From: chang Date: Fri, 1 Feb 2013 11:30:44 +0000 (+0900) Subject: backport the latest dvb frontends. X-Git-Url: http://code.vuplus.com/gitweb/?p=vuplus_openvuplus;a=commitdiff_plain;h=38f345c805ffd15117df99b912ebb7ef4fe537af backport the latest dvb frontends. --- diff --git a/meta-bsp/vusolo2/recipes/linux/linux-vuplus-3.3.6/dvb_backport.patch b/meta-bsp/vusolo2/recipes/linux/linux-vuplus-3.3.6/dvb_backport.patch new file mode 100644 index 0000000..5d82aed --- /dev/null +++ b/meta-bsp/vusolo2/recipes/linux/linux-vuplus-3.3.6/dvb_backport.patch @@ -0,0 +1,1120953 @@ +diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c +index e9c1893..2477aba 100644 +--- a/drivers/i2c/i2c-core.c ++++ b/drivers/i2c/i2c-core.c +@@ -1292,6 +1292,37 @@ module_exit(i2c_exit); + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ ++/** ++ * __i2c_transfer - unlocked flavor of i2c_transfer ++ * @adap: Handle to I2C bus ++ * @msgs: One or more messages to execute before STOP is issued to ++ * terminate the operation; each message begins with a START. ++ * @num: Number of messages to be executed. ++ * ++ * Returns negative errno, else the number of messages executed. ++ * ++ * Adapter lock must be held when calling this function. No debug logging ++ * takes place. adap->algo->master_xfer existence isn't checked. ++ */ ++int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ++{ ++ unsigned long orig_jiffies; ++ int ret, try; ++ ++ /* Retry automatically on arbitration loss */ ++ orig_jiffies = jiffies; ++ for (ret = 0, try = 0; try <= adap->retries; try++) { ++ ret = adap->algo->master_xfer(adap, msgs, num); ++ if (ret != -EAGAIN) ++ break; ++ if (time_after(jiffies, orig_jiffies + adap->timeout)) ++ break; ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(__i2c_transfer); ++ + + /** + * i2c_transfer - execute a single or combined I2C message +diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig +index 9575db4..84d85b9 100644 +--- a/drivers/media/Kconfig ++++ b/drivers/media/Kconfig +@@ -6,20 +6,82 @@ menuconfig MEDIA_SUPPORT + tristate "Multimedia support" + depends on HAS_IOMEM + help +- If you want to use Video for Linux, DVB for Linux, or DAB adapters, ++ If you want to use Webcams, Video grabber devices and/or TV devices + enable this option and other options below. ++ Additional info and docs are available on the web at ++ + + if MEDIA_SUPPORT + + comment "Multimedia core support" + + # ++# Multimedia support - automatically enable V4L2 and DVB core ++# ++config MEDIA_CAMERA_SUPPORT ++ bool "Cameras/video grabbers support" ++ ---help--- ++ Enable support for webcams and video grabbers. ++ ++ Say Y when you have a webcam or a video capture grabber board. ++ ++config MEDIA_ANALOG_TV_SUPPORT ++ bool "Analog TV support" ++ ---help--- ++ Enable analog TV support. ++ ++ Say Y when you have a TV board with analog support or with a ++ hybrid analog/digital TV chipset. ++ ++ Note: There are several DVB cards that are based on chips that ++ support both analog and digital TV. Disabling this option ++ will disable support for them. ++ ++config MEDIA_DIGITAL_TV_SUPPORT ++ bool "Digital TV support" ++ ---help--- ++ Enable digital TV support. ++ ++ Say Y when you have a board with digital support or a board with ++ hybrid digital TV and analog TV. ++ ++config MEDIA_RADIO_SUPPORT ++ bool "AM/FM radio receivers/transmitters support" ++ ---help--- ++ Enable AM/FM radio support. ++ ++ Additional info and docs are available on the web at ++ ++ ++ Say Y when you have a board with radio support. ++ ++ Note: There are several TV cards that are based on chips that ++ support radio reception. Disabling this option will ++ disable support for them. ++ ++config MEDIA_RC_SUPPORT ++ bool "Remote Controller support" ++ depends on INPUT ++ ---help--- ++ Enable support for Remote Controllers on Linux. This is ++ needed in order to support several video capture adapters, ++ standalone IR receivers/transmitters, and RF receivers. ++ ++ Enable this option if you have a video capture board even ++ if you don't need IR, as otherwise, you may not be able to ++ compile the driver for your adapter. ++ ++ Say Y when you have a TV or an IR device. ++ ++# + # Media controller ++# Selectable only for webcam/grabbers, as other drivers don't use it + # + + config MEDIA_CONTROLLER + bool "Media Controller API (EXPERIMENTAL)" + depends on EXPERIMENTAL ++ depends on MEDIA_CAMERA_SUPPORT + ---help--- + Enable the media controller API used to query media devices internal + topology and configure it dynamically. +@@ -27,31 +89,15 @@ config MEDIA_CONTROLLER + This API is mostly used by camera interfaces in embedded platforms. + + # +-# V4L core and enabled API's ++# Video4Linux support ++# Only enables if one of the V4L2 types (ATV, webcam, radio) is selected + # + + config VIDEO_DEV +- tristate "Video For Linux" +- ---help--- +- V4L core support for video capture and overlay devices, webcams and +- AM/FM radio cards. +- +- This kernel includes support for the new Video for Linux Two API, +- (V4L2). +- +- Additional info and docs are available on the web at +- +- +- Documentation for V4L2 is also available on the web at +- . +- +- To compile this driver as a module, choose M here: the +- module will be called videodev. +- +-config VIDEO_V4L2_COMMON + tristate +- depends on (I2C || I2C=n) && VIDEO_DEV +- default (I2C || I2C=n) && VIDEO_DEV ++ depends on MEDIA_SUPPORT ++ depends on MEDIA_CAMERA_SUPPORT || MEDIA_ANALOG_TV_SUPPORT || MEDIA_RADIO_SUPPORT ++ default y + + config VIDEO_V4L2_SUBDEV_API + bool "V4L2 sub-device userspace API (EXPERIMENTAL)" +@@ -62,27 +108,19 @@ config VIDEO_V4L2_SUBDEV_API + + This API is mostly used by camera interfaces in embedded platforms. + ++source "drivers/media/v4l2-core/Kconfig" ++ + # + # DVB Core ++# Only enables if one of DTV is selected + # + + config DVB_CORE +- tristate "DVB for Linux" ++ tristate ++ depends on MEDIA_SUPPORT ++ depends on MEDIA_DIGITAL_TV_SUPPORT ++ default y + select CRC32 +- help +- DVB core utility functions for device handling, software fallbacks etc. +- +- Enable this if you own a DVB/ATSC adapter and want to use it or if +- you compile Linux for a digital SetTopBox. +- +- Say Y when you have a DVB or an ATSC card and want to use it. +- +- API specs and user tools are available from . +- +- Please report problems regarding this support to the LinuxDVB +- mailing list. +- +- If unsure say N. + + config DVB_NET + bool "DVB Network Support" +@@ -97,33 +135,63 @@ config DVB_NET + You may want to disable the network support on embedded devices. If + unsure say Y. + +-config VIDEO_MEDIA +- tristate +- default (DVB_CORE && (VIDEO_DEV = n)) || (VIDEO_DEV && (DVB_CORE = n)) || (DVB_CORE && VIDEO_DEV) ++# This Kconfig option is used by both PCI and USB drivers ++config TTPCI_EEPROM ++ tristate ++ depends on I2C ++ default n + +-comment "Multimedia drivers" ++source "drivers/media/dvb-core/Kconfig" + +-source "drivers/media/common/Kconfig" ++comment "Media drivers" + source "drivers/media/rc/Kconfig" + + # +-# Tuner drivers for DVB and V4L ++# V4L platform/mem2mem drivers + # + +-source "drivers/media/common/tuners/Kconfig" ++source "drivers/media/usb/Kconfig" ++source "drivers/media/pci/Kconfig" ++source "drivers/media/platform/Kconfig" ++source "drivers/media/mmc/Kconfig" ++source "drivers/media/parport/Kconfig" ++source "drivers/media/radio/Kconfig" + +-# +-# Video/Radio/Hybrid adapters +-# ++comment "Supported FireWire (IEEE 1394) Adapters" ++ depends on DVB_CORE && FIREWIRE ++source "drivers/media/firewire/Kconfig" + +-source "drivers/media/video/Kconfig" ++# Common driver options ++source "drivers/media/common/Kconfig" + +-source "drivers/media/radio/Kconfig" ++comment "Media ancillary drivers (tuners, sensors, i2c, frontends)" + + # +-# DVB adapters ++# Ancillary drivers (tuners, i2c, frontends) + # + +-source "drivers/media/dvb/Kconfig" ++config MEDIA_SUBDRV_AUTOSELECT ++ bool "Autoselect ancillary drivers (tuners, sensors, i2c, frontends)" ++ depends on MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT || MEDIA_CAMERA_SUPPORT ++ default y ++ help ++ By default, a media driver auto-selects all possible ancillary ++ devices such as tuners, sensors, video encoders/decoders and ++ frontends, that are used by any of the supported devices. ++ ++ This is generally the right thing to do, except when there ++ are strict constraints with regards to the kernel size, ++ like on embedded systems. ++ ++ Use this option with care, as deselecting ancillary drivers which ++ are, in fact, necessary will result in the lack of the needed ++ functionality for your device (it may not tune or may not have ++ the needed demodulators). ++ ++ If unsure say Y. ++ ++source "drivers/media/i2c/Kconfig" ++source "drivers/media/tuners/Kconfig" ++source "drivers/media/dvb-frontends/Kconfig" + + endif # MEDIA_SUPPORT +diff --git a/drivers/media/Makefile b/drivers/media/Makefile +index 64755c9..620f275 100644 +--- a/drivers/media/Makefile ++++ b/drivers/media/Makefile +@@ -4,11 +4,30 @@ + + media-objs := media-device.o media-devnode.o media-entity.o + ++# ++# I2C drivers should come before other drivers, otherwise they'll fail ++# when compiled as builtin drivers ++# ++obj-y += i2c/ tuners/ ++obj-$(CONFIG_DVB_CORE) += dvb-frontends/ ++ ++# ++# Now, let's link-in the media core ++# + ifeq ($(CONFIG_MEDIA_CONTROLLER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += media.o + endif + +-obj-y += common/ rc/ video/ ++obj-$(CONFIG_VIDEO_DEV) += v4l2-core/ ++obj-$(CONFIG_DVB_CORE) += dvb-core/ + ++# There are both core and drivers at RC subtree - merge before drivers ++obj-y += rc/ ++ ++# ++# Finally, merge the drivers that require the core ++# ++ ++obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/ + obj-$(CONFIG_VIDEO_DEV) += radio/ +-obj-$(CONFIG_DVB_CORE) += dvb/ ++ +diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig +index 769c6f8..d2a436c 100644 +--- a/drivers/media/common/Kconfig ++++ b/drivers/media/common/Kconfig +@@ -1,9 +1,10 @@ +-config VIDEO_SAA7146 +- tristate +- depends on I2C && PCI ++# Used by common drivers, when they need to ask questions ++config MEDIA_COMMON_OPTIONS ++ bool + +-config VIDEO_SAA7146_VV +- tristate +- depends on VIDEO_V4L2 +- select VIDEOBUF_DMA_SG +- select VIDEO_SAA7146 ++comment "common driver options" ++ depends on MEDIA_COMMON_OPTIONS ++ ++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 +index e3ec963..b8e2e3a 100644 +--- a/drivers/media/common/Makefile ++++ b/drivers/media/common/Makefile +@@ -1,6 +1 @@ +-saa7146-objs := saa7146_i2c.o saa7146_core.o +-saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o +- +-obj-y += tuners/ +-obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o +-obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o ++obj-y += b2c2/ saa7146/ siano/ +diff --git a/drivers/media/common/b2c2/Kconfig b/drivers/media/common/b2c2/Kconfig +new file mode 100644 +index 0000000..a8c6cdf +--- /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 0000000..24993a5 +--- /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 0000000..437912e +--- /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 ++#include ++#include ++ ++#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 0000000..a25373a +--- /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 0000000..850a6c6 +--- /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 ++#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)) { ++ 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)) { ++ 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 request_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 0000000..77e4547 +--- /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 0000000..965d5eb +--- /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 0000000..f06f3a9 +--- /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 0000000..dc4528d +--- /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 0000000..f2199e4 +--- /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 0000000..412c5da +--- /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 ++ * 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 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 0000000..897b10c +--- /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 0000000..8f64bdb +--- /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 0000000..c75830d +--- /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/saa7146/Kconfig b/drivers/media/common/saa7146/Kconfig +new file mode 100644 +index 0000000..769c6f8 +--- /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 0000000..3219b00 +--- /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 0000000..bb6ee51 +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_core.c +@@ -0,0 +1,592 @@ ++/* ++ saa7146.o - driver for generic saa7146-based hardware ++ ++ Copyright (C) 1998-2003 Michael Hunold ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++ ++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_err("%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; ++ } ++ ++ 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 | IRQF_DISABLED, ++ 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_alloc_consistent(pci, SAA7146_RPS_MEM, ++ &dev->d_rps0.dma_handle); ++ if (!dev->d_rps0.cpu_addr) ++ goto err_free_irq; ++ memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM); ++ ++ dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, ++ &dev->d_rps1.dma_handle); ++ if (!dev->d_rps1.cpu_addr) ++ goto err_free_rps0; ++ memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM); ++ ++ dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, ++ &dev->d_i2c.dma_handle); ++ if (!dev->d_i2c.cpu_addr) ++ goto err_free_rps1; ++ memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM); ++ ++ /* the rest + print status message */ ++ ++ /* create a nice device name */ ++ sprintf(dev->name, "saa7146 (%d)", saa7146_num); ++ ++ 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); ++ /* Zero the PCI drvdata after use. */ ++ pci_set_drvdata(pdev, NULL); ++ ++ /* 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 "); ++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 0000000..eda01bc +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_fops.c +@@ -0,0 +1,663 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++ ++/****************************************************************************/ ++/* 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(); ++ return 0; ++ } ++ ++ 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(); ++ return 0; ++ } ++} ++ ++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(); ++ return -EINVAL; ++ } ++} ++ ++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_alloc_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; ++ } ++ memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); ++ ++ saa7146_video_uops.init(dev,vv); ++ if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) ++ saa7146_vbi_uops.init(dev,vv); ++ ++ fmt = &vv->ov_fb.fmt; ++ fmt->width = vv->standard->h_max_out; ++ fmt->height = vv->standard->v_max_out; ++ fmt->pixelformat = V4L2_PIX_FMT_RGB565; ++ fmt->bytesperline = 2 * fmt->width; ++ fmt->sizeimage = fmt->bytesperline * fmt->height; ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ 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 **vid, struct saa7146_dev* dev, ++ char *name, int type) ++{ ++ struct video_device *vfd; ++ int err; ++ int i; ++ ++ DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); ++ ++ // released by vfd->release ++ vfd = video_device_alloc(); ++ if (vfd == NULL) ++ return -ENOMEM; ++ ++ 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; ++ vfd->lock = &dev->v4l2_lock; ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ vfd->tvnorms = 0; ++ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); ++ for (i = 0; i < dev->ext_vv_data->num_stds; i++) ++ vfd->tvnorms |= dev->ext_vv_data->stds[i].id; ++ strlcpy(vfd->name, name, sizeof(vfd->name)); ++ video_set_drvdata(vfd, dev); ++ ++ err = video_register_device(vfd, type, -1); ++ if (err < 0) { ++ ERR("cannot register v4l2 device. skipping.\n"); ++ video_device_release(vfd); ++ return err; ++ } ++ ++ pr_info("%s: registered device %s [v4l2]\n", ++ dev->name, video_device_node_name(vfd)); ++ ++ *vid = vfd; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(saa7146_register_device); ++ ++int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev) ++{ ++ DEB_EE("dev:%p\n", dev); ++ ++ video_unregister_device(*vid); ++ *vid = NULL; ++ ++ 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 "); ++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 0000000..be746d1 +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_hlp.c +@@ -0,0 +1,1048 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++ ++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 0000000..2202719 +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_i2c.c +@@ -0,0 +1,423 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++ ++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 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 0000000..1e71e37 +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_vbi.c +@@ -0,0 +1,498 @@ ++#include ++ ++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); ++ 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); ++ 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 0000000..4143d61 +--- /dev/null ++++ b/drivers/media/common/saa7146/saa7146_video.c +@@ -0,0 +1,1332 @@ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++ ++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; ++} ++ ++static int vidioc_g_chip_ident(struct file *file, void *__fh, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct saa7146_fh *fh = __fh; ++ struct saa7146_dev *dev = fh->dev; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type == V4L2_CHIP_MATCH_HOST) { ++ if (v4l2_chip_match_host(&chip->match)) ++ chip->ident = V4L2_IDENT_SAA7146; ++ return 0; ++ } ++ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && ++ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ return v4l2_device_call_until_err(&dev->v4l2_dev, 0, ++ core, g_chip_ident, chip); ++} ++ ++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_g_chip_ident = vidioc_g_chip_ident, ++ ++ .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_g_chip_ident = vidioc_g_chip_ident, ++ ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_g_std = vidioc_g_std, ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/*********************************************************************************/ ++/* buffer handling functions */ ++ ++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/saa7146_core.c b/drivers/media/common/saa7146_core.c +deleted file mode 100644 +index d6b1cf6..0000000 +--- a/drivers/media/common/saa7146_core.c ++++ /dev/null +@@ -1,600 +0,0 @@ +-/* +- saa7146.o - driver for generic saa7146-based hardware +- +- Copyright (C) 1998-2003 Michael Hunold +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +- +-LIST_HEAD(saa7146_devices); +-DEFINE_MUTEX(saa7146_devices_lock); +- +-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_err("%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; +- } +- +- 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 | IRQF_DISABLED, +- 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_alloc_consistent(pci, SAA7146_RPS_MEM, +- &dev->d_rps0.dma_handle); +- if (!dev->d_rps0.cpu_addr) +- goto err_free_irq; +- memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM); +- +- dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, +- &dev->d_rps1.dma_handle); +- if (!dev->d_rps1.cpu_addr) +- goto err_free_rps0; +- memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM); +- +- dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM, +- &dev->d_i2c.dma_handle); +- if (!dev->d_i2c.cpu_addr) +- goto err_free_rps1; +- memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM); +- +- /* the rest + print status message */ +- +- /* create a nice device name */ +- sprintf(dev->name, "saa7146 (%d)", saa7146_num); +- +- 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); +- +- INIT_LIST_HEAD(&dev->item); +- list_add_tail(&dev->item,&saa7146_devices); +- 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); +- /* Zero the PCI drvdata after use. */ +- pci_set_drvdata(pdev, NULL); +- +- /* 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); +- list_del(&dev->item); +- 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); +-EXPORT_SYMBOL_GPL(saa7146_devices); +-EXPORT_SYMBOL_GPL(saa7146_devices_lock); +- +-MODULE_AUTHOR("Michael Hunold "); +-MODULE_DESCRIPTION("driver for generic saa7146-based hardware"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c +deleted file mode 100644 +index 71f8e01..0000000 +--- a/drivers/media/common/saa7146_fops.c ++++ /dev/null +@@ -1,562 +0,0 @@ +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +- +-/****************************************************************************/ +-/* 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; +- do_gettimeofday(&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; +- +- enum v4l2_buf_type type; +- +- DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); +- +- if (mutex_lock_interruptible(&saa7146_devices_lock)) +- return -ERESTARTSYS; +- +- DEB_D("using: %p\n", dev); +- +- type = vdev->vfl_type == VFL_TYPE_GRABBER +- ? V4L2_BUF_TYPE_VIDEO_CAPTURE +- : V4L2_BUF_TYPE_VBI_CAPTURE; +- +- /* 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; +- } +- +- file->private_data = fh; +- fh->dev = dev; +- fh->type = type; +- +- if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { +- 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; +-out: +- if (fh && result != 0) { +- kfree(fh); +- file->private_data = NULL; +- } +- mutex_unlock(&saa7146_devices_lock); +- return result; +-} +- +-static int fops_release(struct file *file) +-{ +- struct saa7146_fh *fh = file->private_data; +- struct saa7146_dev *dev = fh->dev; +- +- DEB_EE("file:%p\n", file); +- +- if (mutex_lock_interruptible(&saa7146_devices_lock)) +- return -ERESTARTSYS; +- +- if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { +- 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); +- } +- +- module_put(dev->ext->module); +- file->private_data = NULL; +- kfree(fh); +- +- mutex_unlock(&saa7146_devices_lock); +- +- return 0; +-} +- +-static int fops_mmap(struct file *file, struct vm_area_struct * vma) +-{ +- struct saa7146_fh *fh = file->private_data; +- struct videobuf_queue *q; +- +- switch (fh->type) { +- case V4L2_BUF_TYPE_VIDEO_CAPTURE: { +- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", +- file, vma); +- q = &fh->video_q; +- break; +- } +- case V4L2_BUF_TYPE_VBI_CAPTURE: { +- DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", +- file, vma); +- q = &fh->vbi_q; +- break; +- } +- default: +- BUG(); +- return 0; +- } +- +- return videobuf_mmap_mapper(q,vma); +-} +- +-static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) +-{ +- struct saa7146_fh *fh = file->private_data; +- struct videobuf_buffer *buf = NULL; +- struct videobuf_queue *q; +- +- DEB_EE("file:%p, poll:%p\n", file, wait); +- +- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { +- if( 0 == fh->vbi_q.streaming ) +- return 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 POLLERR; +- } +- +- poll_wait(file, &buf->done, wait); +- if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { +- DEB_D("poll succeeded!\n"); +- return POLLIN|POLLRDNORM; +- } +- +- DEB_D("nothing to poll for, buf->state:%d\n", buf->state); +- return 0; +-} +- +-static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +-{ +- struct saa7146_fh *fh = file->private_data; +- +- switch (fh->type) { +- case V4L2_BUF_TYPE_VIDEO_CAPTURE: +-/* +- DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", +- file, data, (unsigned long)count); +-*/ +- return saa7146_video_uops.read(file,data,count,ppos); +- case V4L2_BUF_TYPE_VBI_CAPTURE: +-/* +- 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) +- return saa7146_vbi_uops.read(file,data,count,ppos); +- return -EINVAL; +- default: +- BUG(); +- return 0; +- } +-} +- +-static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +-{ +- struct saa7146_fh *fh = file->private_data; +- +- switch (fh->type) { +- case V4L2_BUF_TYPE_VIDEO_CAPTURE: +- return -EINVAL; +- case V4L2_BUF_TYPE_VBI_CAPTURE: +- if (fh->dev->ext_vv_data->vbi_fops.write) +- return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); +- else +- return -EINVAL; +- default: +- BUG(); +- return -EINVAL; +- } +-} +- +-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); +- } +-} +- +-int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) +-{ +- struct saa7146_vv *vv; +- int err; +- +- err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); +- if (err) +- return err; +- +- vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); +- if (vv == NULL) { +- ERR("out of memory. aborting.\n"); +- return -ENOMEM; +- } +- ext_vv->ops = saa7146_video_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_alloc_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); +- return -1; +- } +- memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); +- +- saa7146_video_uops.init(dev,vv); +- if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) +- saa7146_vbi_uops.init(dev,vv); +- +- 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); +- 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 **vid, struct saa7146_dev* dev, +- char *name, int type) +-{ +- struct video_device *vfd; +- int err; +- int i; +- +- DEB_EE("dev:%p, name:'%s', type:%d\n", dev, name, type); +- +- // released by vfd->release +- vfd = video_device_alloc(); +- if (vfd == NULL) +- return -ENOMEM; +- +- vfd->fops = &video_fops; +- vfd->ioctl_ops = &dev->ext_vv_data->ops; +- vfd->release = video_device_release; +- vfd->lock = &dev->v4l2_lock; +- 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"); +- video_device_release(vfd); +- return err; +- } +- +- pr_info("%s: registered device %s [v4l2]\n", +- dev->name, video_device_node_name(vfd)); +- +- *vid = vfd; +- return 0; +-} +-EXPORT_SYMBOL_GPL(saa7146_register_device); +- +-int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev) +-{ +- DEB_EE("dev:%p\n", dev); +- +- video_unregister_device(*vid); +- *vid = NULL; +- +- 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 "); +-MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c +deleted file mode 100644 +index bc1f545..0000000 +--- a/drivers/media/common/saa7146_hlp.c ++++ /dev/null +@@ -1,1047 +0,0 @@ +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +-#include +- +-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 = fh->ov.win.w.width; +- int height = fh->ov.win.w.height; +- int clipcount = fh->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] = fh->ov.clips[i].c.left; +- y[i] = fh->ov.clips[i].c.top; +- w[i] = fh->ov.clips[i].c.width; +- h[i] = fh->ov.clips[i].c.height; +- +- 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; +- enum v4l2_field field = fh->ov.win.field; +- struct saa7146_video_dma vdma2; +- u32 clip_format; +- u32 arbtr_ctrl; +- +- /* check clipcount, disable clipping if clipcount == 0*/ +- if( fh->ov.nclips == 0 ) { +- 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, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field); +- saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat); +- saa7146_set_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_i2c.c b/drivers/media/common/saa7146_i2c.c +deleted file mode 100644 +index 2202719..0000000 +--- a/drivers/media/common/saa7146_i2c.c ++++ /dev/null +@@ -1,423 +0,0 @@ +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +- +-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 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_vbi.c b/drivers/media/common/saa7146_vbi.c +deleted file mode 100644 +index b2e7183..0000000 +--- a/drivers/media/common/saa7146_vbi.c ++++ /dev/null +@@ -1,512 +0,0 @@ +-#include +- +-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); +- 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); +- 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_q.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_q,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_q.curr) { +- saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); +- } +- +- videobuf_queue_cancel(&fh->vbi_q); +- +- vv->vbi_streaming = NULL; +- +- del_timer(&vv->vbi_q.timeout); +- del_timer(&fh->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_q.queue); +- +- init_timer(&vv->vbi_q.timeout); +- vv->vbi_q.timeout.function = saa7146_buffer_timeout; +- vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q); +- vv->vbi_q.dev = dev; +- +- init_waitqueue_head(&vv->vbi_wq); +-} +- +-static int vbi_open(struct saa7146_dev *dev, struct file *file) +-{ +- struct saa7146_fh *fh = file->private_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)); +- +- memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt)); +- +- fh->vbi_fmt.sampling_rate = 27000000; +- fh->vbi_fmt.offset = 248; /* todo */ +- fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture; +- fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY; +- +- /* fixme: this only works for PAL */ +- fh->vbi_fmt.start[0] = 5; +- fh->vbi_fmt.count[0] = 16; +- fh->vbi_fmt.start[1] = 312; +- fh->vbi_fmt.count[1] = 16; +- +- videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, +- &dev->pci->dev, &dev->slock, +- V4L2_BUF_TYPE_VBI_CAPTURE, +- V4L2_FIELD_SEQ_TB, // FIXME: does this really work? +- sizeof(struct saa7146_buf), +- file, &dev->v4l2_lock); +- +- init_timer(&fh->vbi_read_timeout); +- fh->vbi_read_timeout.function = vbi_read_timeout; +- fh->vbi_read_timeout.data = (unsigned long)file; +- +- /* 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_q.curr) { +- DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr); +- /* this must be += 2, one count for each field */ +- vv->vbi_fieldcount+=2; +- vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount; +- saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); +- } else { +- DEB_VBI("dev:%p\n", dev); +- } +- saa7146_buffer_next(dev,&vv->vbi_q,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(&fh->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_video.c b/drivers/media/common/saa7146_video.c +deleted file mode 100644 +index ce30533..0000000 +--- a/drivers/media/common/saa7146_video.c ++++ /dev/null +@@ -1,1435 +0,0 @@ +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +-#include +- +-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 informations */ +- if( NULL == fh->ov.fh ) { +- 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 = fh->ov.win; +- err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); +- if (0 != err) { +- saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); +- return -EBUSY; +- } +- fh->ov.win = fmt.fmt.win; +- vv->ov_data = &fh->ov; +- +- DEB_D("%dx%d+%d+%d %s field=%s\n", +- fh->ov.win.w.width, fh->ov.win.w.height, +- fh->ov.win.w.left, fh->ov.win.w.top, +- vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]); +- +- 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); +- +-/********************************************************************************/ +-/* device controls */ +- +-static struct v4l2_queryctrl controls[] = { +- { +- .id = V4L2_CID_BRIGHTNESS, +- .name = "Brightness", +- .minimum = 0, +- .maximum = 255, +- .step = 1, +- .default_value = 128, +- .type = V4L2_CTRL_TYPE_INTEGER, +- .flags = V4L2_CTRL_FLAG_SLIDER, +- },{ +- .id = V4L2_CID_CONTRAST, +- .name = "Contrast", +- .minimum = 0, +- .maximum = 127, +- .step = 1, +- .default_value = 64, +- .type = V4L2_CTRL_TYPE_INTEGER, +- .flags = V4L2_CTRL_FLAG_SLIDER, +- },{ +- .id = V4L2_CID_SATURATION, +- .name = "Saturation", +- .minimum = 0, +- .maximum = 127, +- .step = 1, +- .default_value = 64, +- .type = V4L2_CTRL_TYPE_INTEGER, +- .flags = V4L2_CTRL_FLAG_SLIDER, +- },{ +- .id = V4L2_CID_VFLIP, +- .name = "Vertical Flip", +- .minimum = 0, +- .maximum = 1, +- .type = V4L2_CTRL_TYPE_BOOLEAN, +- },{ +- .id = V4L2_CID_HFLIP, +- .name = "Horizontal Flip", +- .minimum = 0, +- .maximum = 1, +- .type = V4L2_CTRL_TYPE_BOOLEAN, +- }, +-}; +-static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); +- +-#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0) +- +-static struct v4l2_queryctrl* ctrl_by_id(int id) +-{ +- int i; +- +- for (i = 0; i < NUM_CONTROLS; i++) +- if (controls[i].id == id) +- return controls+i; +- return NULL; +-} +- +-/********************************************************************************/ +-/* common pagetable functions */ +- +-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,fh->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,fh->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 saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- +- strcpy((char *)cap->driver, "saa7146 v4l2"); +- strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); +- sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); +- cap->version = SAA7146_VERSION_CODE; +- cap->capabilities = +- V4L2_CAP_VIDEO_CAPTURE | +- V4L2_CAP_VIDEO_OVERLAY | +- V4L2_CAP_READWRITE | +- V4L2_CAP_STREAMING; +- cap->capabilities |= dev->ext_vv_data->capabilities; +- 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; +- return 0; +-} +- +-static int vidioc_s_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; +- 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; +-} +- +-static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +-{ +- const struct v4l2_queryctrl *ctrl; +- +- if ((c->id < V4L2_CID_BASE || +- c->id >= V4L2_CID_LASTP1) && +- (c->id < V4L2_CID_PRIVATE_BASE || +- c->id >= V4L2_CID_PRIVATE_LASTP1)) +- return -EINVAL; +- +- ctrl = ctrl_by_id(c->id); +- if (ctrl == NULL) +- return -EINVAL; +- +- DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id); +- *c = *ctrl; +- return 0; +-} +- +-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct saa7146_vv *vv = dev->vv_data; +- const struct v4l2_queryctrl *ctrl; +- u32 value = 0; +- +- ctrl = ctrl_by_id(c->id); +- if (NULL == ctrl) +- return -EINVAL; +- switch (c->id) { +- case V4L2_CID_BRIGHTNESS: +- value = saa7146_read(dev, BCS_CTRL); +- c->value = 0xff & (value >> 24); +- DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value); +- break; +- case V4L2_CID_CONTRAST: +- value = saa7146_read(dev, BCS_CTRL); +- c->value = 0x7f & (value >> 16); +- DEB_D("V4L2_CID_CONTRAST: %d\n", c->value); +- break; +- case V4L2_CID_SATURATION: +- value = saa7146_read(dev, BCS_CTRL); +- c->value = 0x7f & (value >> 0); +- DEB_D("V4L2_CID_SATURATION: %d\n", c->value); +- break; +- case V4L2_CID_VFLIP: +- c->value = vv->vflip; +- DEB_D("V4L2_CID_VFLIP: %d\n", c->value); +- break; +- case V4L2_CID_HFLIP: +- c->value = vv->hflip; +- DEB_D("V4L2_CID_HFLIP: %d\n", c->value); +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct saa7146_vv *vv = dev->vv_data; +- const struct v4l2_queryctrl *ctrl; +- +- ctrl = ctrl_by_id(c->id); +- if (NULL == ctrl) { +- DEB_D("unknown control %d\n", c->id); +- return -EINVAL; +- } +- +- switch (ctrl->type) { +- case V4L2_CTRL_TYPE_BOOLEAN: +- case V4L2_CTRL_TYPE_MENU: +- case V4L2_CTRL_TYPE_INTEGER: +- if (c->value < ctrl->minimum) +- c->value = ctrl->minimum; +- if (c->value > ctrl->maximum) +- c->value = ctrl->maximum; +- break; +- default: +- /* nothing */; +- } +- +- switch (c->id) { +- case V4L2_CID_BRIGHTNESS: { +- u32 value = saa7146_read(dev, BCS_CTRL); +- value &= 0x00ffffff; +- value |= (c->value << 24); +- saa7146_write(dev, BCS_CTRL, value); +- saa7146_write(dev, MC2, MASK_22 | MASK_06); +- break; +- } +- case V4L2_CID_CONTRAST: { +- u32 value = saa7146_read(dev, BCS_CTRL); +- value &= 0xff00ffff; +- value |= (c->value << 16); +- saa7146_write(dev, BCS_CTRL, value); +- saa7146_write(dev, MC2, MASK_22 | MASK_06); +- break; +- } +- case V4L2_CID_SATURATION: { +- u32 value = saa7146_read(dev, BCS_CTRL); +- value &= 0xffffff00; +- value |= (c->value << 0); +- saa7146_write(dev, BCS_CTRL, value); +- saa7146_write(dev, MC2, MASK_22 | MASK_06); +- break; +- } +- case V4L2_CID_HFLIP: +- /* fixme: we can support changing VFLIP and HFLIP here... */ +- if (IS_CAPTURE_ACTIVE(fh) != 0) { +- DEB_D("V4L2_CID_HFLIP while active capture\n"); +- return -EBUSY; +- } +- vv->hflip = c->value; +- break; +- case V4L2_CID_VFLIP: +- if (IS_CAPTURE_ACTIVE(fh) != 0) { +- DEB_D("V4L2_CID_VFLIP while active capture\n"); +- return -EBUSY; +- } +- vv->vflip = c->value; +- break; +- default: +- return -EINVAL; +- } +- +- if (IS_OVERLAY_ACTIVE(fh) != 0) { +- 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; +- +- 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) +-{ +- f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt; +- return 0; +-} +- +-static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) +-{ +- f->fmt.win = ((struct saa7146_fh *)fh)->ov.win; +- return 0; +-} +- +-static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) +-{ +- f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt; +- 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; +- 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; +- fh->video_fmt = f->fmt.pix; +- DEB_EE("set to pixelformat '%4.4s'\n", +- (char *)&fh->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; +- fh->ov.win = f->fmt.win; +- fh->ov.nclips = f->fmt.win.clipcount; +- if (fh->ov.nclips > 16) +- fh->ov.nclips = 16; +- if (copy_from_user(fh->ov.clips, f->fmt.win.clips, +- sizeof(struct v4l2_clip) * fh->ov.nclips)) { +- return -EFAULT; +- } +- +- /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ +- fh->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; +-} +- +-static int vidioc_g_chip_ident(struct file *file, void *__fh, +- struct v4l2_dbg_chip_ident *chip) +-{ +- struct saa7146_fh *fh = __fh; +- struct saa7146_dev *dev = fh->dev; +- +- chip->ident = V4L2_IDENT_NONE; +- chip->revision = 0; +- if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) { +- chip->ident = V4L2_IDENT_SAA7146; +- return 0; +- } +- return v4l2_device_call_until_err(&dev->v4l2_dev, 0, +- core, g_chip_ident, chip); +-} +- +-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_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, +- .vidioc_g_chip_ident = vidioc_g_chip_ident, +- +- .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_queryctrl = vidioc_queryctrl, +- .vidioc_g_ctrl = vidioc_g_ctrl, +- .vidioc_s_ctrl = vidioc_s_ctrl, +- .vidioc_streamon = vidioc_streamon, +- .vidioc_streamoff = vidioc_streamoff, +- .vidioc_g_parm = vidioc_g_parm, +-}; +- +-/*********************************************************************************/ +-/* 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_q.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 (fh->video_fmt.width < 48 || +- fh->video_fmt.height < 32 || +- fh->video_fmt.width > vv->standard->h_max_out || +- fh->video_fmt.height > vv->standard->v_max_out) { +- DEB_D("w (%d) / h (%d) out of bounds\n", +- fh->video_fmt.width, fh->video_fmt.height); +- return -EINVAL; +- } +- +- size = fh->video_fmt.sizeimage; +- if (0 != buf->vb.baddr && buf->vb.bsize < size) { +- DEB_D("size mismatch\n"); +- return -EINVAL; +- } +- +- DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", +- fh->video_fmt.width, fh->video_fmt.height, +- size, v4l2_field_names[fh->video_fmt.field]); +- if (buf->vb.width != fh->video_fmt.width || +- buf->vb.bytesperline != fh->video_fmt.bytesperline || +- buf->vb.height != fh->video_fmt.height || +- buf->vb.size != size || +- buf->vb.field != field || +- buf->vb.field != fh->video_fmt.field || +- buf->fmt != &fh->video_fmt) { +- saa7146_dma_free(dev,q,buf); +- } +- +- if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { +- struct saa7146_format *sfmt; +- +- buf->vb.bytesperline = fh->video_fmt.bytesperline; +- buf->vb.width = fh->video_fmt.width; +- buf->vb.height = fh->video_fmt.height; +- buf->vb.size = size; +- buf->vb.field = field; +- buf->fmt = &fh->video_fmt; +- buf->vb.field = fh->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; +- +- if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) +- *count = MAX_SAA7146_CAPTURE_BUFFERS; +- +- *size = fh->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_q,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_q.queue); +- +- init_timer(&vv->video_q.timeout); +- vv->video_q.timeout.function = saa7146_buffer_timeout; +- vv->video_q.timeout.data = (unsigned long)(&vv->video_q); +- vv->video_q.dev = dev; +- +- /* 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; +- struct saa7146_format *sfmt; +- +- fh->video_fmt.width = 384; +- fh->video_fmt.height = 288; +- fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; +- fh->video_fmt.bytesperline = 0; +- fh->video_fmt.field = V4L2_FIELD_ANY; +- sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); +- fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; +- +- videobuf_queue_sg_init(&fh->video_q, &video_qops, +- &dev->pci->dev, &dev->slock, +- 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_q; +- +- 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 0000000..68f0f60 +--- /dev/null ++++ b/drivers/media/common/siano/Kconfig +@@ -0,0 +1,19 @@ ++# ++# 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. +diff --git a/drivers/media/common/siano/Makefile b/drivers/media/common/siano/Makefile +new file mode 100644 +index 0000000..81b1e98 +--- /dev/null ++++ b/drivers/media/common/siano/Makefile +@@ -0,0 +1,11 @@ ++smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o ++ ++obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o ++ ++ifeq ($(CONFIG_SMS_SIANO_RC),y) ++ smsmdtv-objs += smsir.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 0000000..680c781 +--- /dev/null ++++ b/drivers/media/common/siano/sms-cards.c +@@ -0,0 +1,311 @@ ++/* ++ * Card-specific functions for the Siano SMS1xxx USB dongle ++ * ++ * Copyright (c) 2008 Michael Krufky ++ * ++ * 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 ++ ++static int sms_dbg; ++module_param_named(cards_dbg, sms_dbg, int, 0644); ++MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); ++ ++static struct sms_board sms_boards[] = { ++ [SMS_BOARD_UNKNOWN] = { ++ .name = "Unknown board", ++ }, ++ [SMS1XXX_BOARD_SIANO_STELLAR] = { ++ .name = "Siano Stellar Digital Receiver", ++ .type = SMS_STELLAR, ++ }, ++ [SMS1XXX_BOARD_SIANO_NOVA_A] = { ++ .name = "Siano Nova A Digital Receiver", ++ .type = SMS_NOVA_A0, ++ }, ++ [SMS1XXX_BOARD_SIANO_NOVA_B] = { ++ .name = "Siano Nova B Digital Receiver", ++ .type = SMS_NOVA_B0, ++ }, ++ [SMS1XXX_BOARD_SIANO_VEGA] = { ++ .name = "Siano Vega Digital Receiver", ++ .type = SMS_VEGA, ++ }, ++ [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { ++ .name = "Hauppauge Catamount", ++ .type = SMS_STELLAR, ++ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", ++ }, ++ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { ++ .name = "Hauppauge Okemo-A", ++ .type = SMS_NOVA_A0, ++ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", ++ }, ++ [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { ++ .name = "Hauppauge Okemo-B", ++ .type = SMS_NOVA_B0, ++ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", ++ }, ++ [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { ++ .name = "Hauppauge WinTV MiniStick", ++ .type = SMS_NOVA_B0, ++ .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", ++ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", ++ .rc_codes = RC_MAP_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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", ++ .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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", ++ .lna_ctrl = -1, ++ }, ++ [SMS1XXX_BOARD_SIANO_NICE] = { ++ /* 11 */ ++ .name = "Siano Nice Digital Receiver", ++ .type = SMS_NOVA_B0, ++ }, ++ [SMS1XXX_BOARD_SIANO_VENICE] = { ++ /* 12 */ ++ .name = "Siano Venice Digital Receiver", ++ .type = SMS_VEGA, ++ }, ++}; ++ ++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_gpio_config *pGpioConfig) { ++ pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; ++ pGpioConfig->InputCharacteristics = ++ SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; ++ pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; ++ pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; ++ pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; ++} ++ ++int sms_board_event(struct smscore_device_t *coredev, ++ enum SMS_BOARD_EVENTS gevent) { ++ struct smscore_gpio_config MyGpioConfig; ++ ++ sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); ++ ++ 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: ++ sms_err("Unknown SMS board event"); ++ 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_OUTPUTSLEWRATE_FAST, ++ .outputdriving = SMS_GPIO_OUTPUTDRIVING_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); ++ ++ sms_debug("%s: LNA %s", __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) ++{ ++ switch (id) { ++ case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: ++ case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: ++ case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: ++ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: ++ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: ++ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: ++ request_module("smsdvb"); ++ break; ++ default: ++ /* do nothing */ ++ break; ++ } ++ 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 0000000..d8cdf75 +--- /dev/null ++++ b/drivers/media/common/siano/sms-cards.h +@@ -0,0 +1,123 @@ ++/* ++ * Card-specific functions for the Siano SMS1xxx USB dongle ++ * ++ * Copyright (c) 2008 Michael Krufky ++ * ++ * 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 ++#include "smscoreapi.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 ++ ++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; ++}; ++ ++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 0000000..1842e64 +--- /dev/null ++++ b/drivers/media/common/siano/smscoreapi.c +@@ -0,0 +1,1637 @@ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "smscoreapi.h" ++#include "sms-cards.h" ++#include "smsir.h" ++#include "smsendian.h" ++ ++static int sms_dbg; ++module_param_named(debug, sms_dbg, int, 0644); ++MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); ++ ++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; ++}; ++ ++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 = 4; ++ ++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 ++ sms_err("failed to create smscore_registry."); ++ 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 ++ sms_err("No registry found."); ++ ++ 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 ++ sms_err("No registry found."); ++ ++ return -1; ++} ++ ++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 ++ sms_err("No registry found."); ++} ++ ++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 ++ sms_err("No registry found."); ++} ++ ++ ++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(¬ifyee->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(¬ifyee->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 = ++ kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); ++ if (!cb) { ++ sms_info("kmalloc(...) failed"); ++ 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) ++{ ++ struct smscore_device_t *dev; ++ u8 *buffer; ++ ++ dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); ++ if (!dev) { ++ sms_info("kzalloc(...) failed"); ++ return -ENOMEM; ++ } ++ ++ /* 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->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 = ++ smscore_createbuffer(buffer, dev->common_buffer, ++ dev->common_buffer_phys); ++ if (!cb) { ++ smscore_unregister_device(dev); ++ return -ENOMEM; ++ } ++ ++ smscore_putbuffer(dev, cb); ++ } ++ ++ sms_info("allocated %d buffers", dev->num_buffers); ++ ++ dev->mode = DEVICE_MODE_NONE; ++ 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; ++ ++ sms_info("device %p created", 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 = coredev->sendrequest_handler(coredev->context, buffer, size); ++ if (rc < 0) { ++ sms_info("sendrequest returned error %d", 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 */ ++ sms_info("IR loading"); ++ rc = sms_ir_init(coredev); ++ ++ if (rc != 0) ++ sms_err("Error initialization DTV IR sub-module"); ++ else { ++ buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + ++ SMS_DMA_ALIGNMENT, ++ GFP_KERNEL | GFP_DMA); ++ if (buffer) { ++ struct SmsMsgData_ST2 *msg = ++ (struct SmsMsgData_ST2 *) ++ SMS_ALIGN_ADDRESS(buffer); ++ ++ SMS_INIT_MSG(&msg->xMsgHeader, ++ MSG_SMS_START_IR_REQ, ++ sizeof(struct SmsMsgData_ST2)); ++ msg->msgData[0] = coredev->ir.controller; ++ msg->msgData[1] = coredev->ir.timeout; ++ ++ smsendian_handle_tx_message( ++ (struct SmsMsgHdr_ST2 *)msg); ++ rc = smscore_sendrequest_and_wait(coredev, msg, ++ msg->xMsgHeader. msgLength, ++ &coredev->ir_init_done); ++ ++ kfree(buffer); ++ } else ++ sms_err ++ ("Sending IR initialization message failed"); ++ } ++ } else ++ sms_info("IR port has not been detected"); ++ ++ 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 = smscore_set_device_mode( ++ coredev, smscore_registry_getmode(coredev->devpath)); ++ if (rc < 0) { ++ sms_info("set device mode faile , rc %d", rc); ++ return rc; ++ } ++ ++ kmutex_lock(&g_smscore_deviceslock); ++ ++ rc = smscore_notify_callbacks(coredev, coredev->device, 1); ++ smscore_init_ir(coredev); ++ ++ sms_info("device %p started, rc %d", 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 SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; ++ struct SmsMsgHdr_ST *msg; ++ u32 mem_address; ++ u8 *payload = firmware->Payload; ++ int rc = 0; ++ firmware->StartAddress = le32_to_cpu(firmware->StartAddress); ++ firmware->Length = le32_to_cpu(firmware->Length); ++ ++ mem_address = firmware->StartAddress; ++ ++ sms_info("loading FW to addr 0x%x size %d", ++ 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) { ++ sms_debug("sending reload command."); ++ SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, ++ sizeof(struct SmsMsgHdr_ST)); ++ rc = smscore_sendrequest_and_wait(coredev, msg, ++ msg->msgLength, ++ &coredev->reload_start_done); ++ mem_address = *(u32 *) &payload[20]; ++ } ++ ++ while (size && rc >= 0) { ++ struct SmsDataDownload_ST *DataMsg = ++ (struct SmsDataDownload_ST *) msg; ++ int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); ++ ++ SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, ++ (u16)(sizeof(struct SmsMsgHdr_ST) + ++ sizeof(u32) + payload_size)); ++ ++ DataMsg->MemAddr = mem_address; ++ memcpy(DataMsg->Payload, payload, payload_size); ++ ++ if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && ++ (coredev->mode == DEVICE_MODE_NONE)) ++ rc = coredev->sendrequest_handler( ++ coredev->context, DataMsg, ++ DataMsg->xMsgHeader.msgLength); ++ else ++ rc = smscore_sendrequest_and_wait( ++ coredev, DataMsg, ++ DataMsg->xMsgHeader.msgLength, ++ &coredev->data_download_done); ++ ++ payload += payload_size; ++ size -= payload_size; ++ mem_address += payload_size; ++ } ++ ++ if (rc >= 0) { ++ if (coredev->mode == DEVICE_MODE_NONE) { ++ struct SmsMsgData_ST *TriggerMsg = ++ (struct SmsMsgData_ST *) msg; ++ ++ SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, ++ sizeof(struct SmsMsgHdr_ST) + ++ sizeof(u32) * 5); ++ ++ TriggerMsg->msgData[0] = firmware->StartAddress; ++ /* Entry point */ ++ TriggerMsg->msgData[1] = 5; /* Priority */ ++ TriggerMsg->msgData[2] = 0x200; /* Stack size */ ++ TriggerMsg->msgData[3] = 0; /* Parameter */ ++ TriggerMsg->msgData[4] = 4; /* Task ID */ ++ ++ if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { ++ rc = coredev->sendrequest_handler( ++ coredev->context, TriggerMsg, ++ TriggerMsg->xMsgHeader.msgLength); ++ msleep(100); ++ } else ++ rc = smscore_sendrequest_and_wait( ++ coredev, TriggerMsg, ++ TriggerMsg->xMsgHeader.msgLength, ++ &coredev->trigger_done); ++ } else { ++ SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, ++ sizeof(struct SmsMsgHdr_ST)); ++ ++ rc = coredev->sendrequest_handler(coredev->context, ++ msg, msg->msgLength); ++ } ++ msleep(500); ++ } ++ ++ sms_debug("rc=%d, postload=%p ", rc, ++ coredev->postload_handler); ++ ++ kfree(msg); ++ ++ return ((rc >= 0) && coredev->postload_handler) ? ++ coredev->postload_handler(coredev->context) : ++ rc; ++} ++ ++/** ++ * 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, ++ char *filename, ++ loadfirmware_t loadfirmware_handler) ++{ ++ int rc = -ENOENT; ++ const struct firmware *fw; ++ u8 *fw_buffer; ++ ++ if (loadfirmware_handler == NULL && !(coredev->device_flags & ++ SMS_DEVICE_FAMILY2)) ++ return -EINVAL; ++ ++ rc = request_firmware(&fw, filename, coredev->device); ++ if (rc < 0) { ++ sms_info("failed to open \"%s\"", filename); ++ return rc; ++ } ++ sms_info("read FW %s, size=%zd", filename, fw->size); ++ fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), ++ GFP_KERNEL | GFP_DMA); ++ if (fw_buffer) { ++ memcpy(fw_buffer, fw->data, fw->size); ++ ++ rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? ++ smscore_load_firmware_family2(coredev, ++ fw_buffer, ++ fw->size) : ++ loadfirmware_handler(coredev->context, ++ fw_buffer, fw->size); ++ ++ kfree(fw_buffer); ++ } else { ++ sms_info("failed to allocate firmware buffer"); ++ rc = -ENOMEM; ++ } ++ ++ 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) { ++ sms_info("exiting although " ++ "not all buffers released."); ++ break; ++ } ++ ++ sms_info("waiting for %d buffer(s)", ++ coredev->num_buffers - num_buffers); ++ msleep(100); ++ } ++ ++ sms_info("freed %d buffers", num_buffers); ++ ++ if (coredev->common_buffer) ++ dma_free_coherent(NULL, coredev->common_buffer_size, ++ coredev->common_buffer, coredev->common_buffer_phys); ++ ++ if (coredev->fw_buf != NULL) ++ kfree(coredev->fw_buf); ++ ++ list_del(&coredev->entry); ++ kfree(coredev); ++ ++ kmutex_unlock(&g_smscore_deviceslock); ++ ++ sms_info("device %p destroyed", coredev); ++} ++EXPORT_SYMBOL_GPL(smscore_unregister_device); ++ ++static int smscore_detect_mode(struct smscore_device_t *coredev) ++{ ++ void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, ++ GFP_KERNEL | GFP_DMA); ++ struct SmsMsgHdr_ST *msg = ++ (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); ++ int rc; ++ ++ if (!buffer) ++ return -ENOMEM; ++ ++ SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, ++ sizeof(struct SmsMsgHdr_ST)); ++ ++ rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, ++ &coredev->version_ex_done); ++ if (rc == -ETIME) { ++ sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); ++ ++ if (wait_for_completion_timeout(&coredev->resume_done, ++ msecs_to_jiffies(5000))) { ++ rc = smscore_sendrequest_and_wait( ++ coredev, msg, msg->msgLength, ++ &coredev->version_ex_done); ++ if (rc < 0) ++ sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " ++ "second try, rc %d", rc); ++ } else ++ rc = -ETIME; ++ } ++ ++ kfree(buffer); ++ ++ return rc; ++} ++ ++static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { ++ /*Stellar NOVA A0 Nova B0 VEGA*/ ++ /*DVBT*/ ++ {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, ++ /*DVBH*/ ++ {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, ++ /*TDMB*/ ++ {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, ++ /*DABIP*/ ++ {"none", "none", "none", "none"}, ++ /*BDA*/ ++ {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, ++ /*ISDBT*/ ++ {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, ++ /*ISDBTBDA*/ ++ {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, ++ /*CMMB*/ ++ {"none", "none", "none", "cmmb_vega_12mhz.inp"} ++}; ++ ++static inline char *sms_get_fw_name(struct smscore_device_t *coredev, ++ int mode, enum sms_device_type_st type) ++{ ++ char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; ++ return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; ++} ++ ++/** ++ * 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) ++{ ++ void *buffer; ++ int rc = 0; ++ enum sms_device_type_st type; ++ ++ sms_debug("set device mode to %d", mode); ++ if (coredev->device_flags & SMS_DEVICE_FAMILY2) { ++ if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { ++ sms_err("invalid mode specified %d", 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) { ++ sms_err("mode detect failed %d", rc); ++ return rc; ++ } ++ } ++ ++ if (coredev->mode == mode) { ++ sms_info("device mode %d already set", mode); ++ return 0; ++ } ++ ++ if (!(coredev->modes_supported & (1 << mode))) { ++ char *fw_filename; ++ ++ type = smscore_registry_gettype(coredev->devpath); ++ fw_filename = sms_get_fw_name(coredev, mode, type); ++ ++ rc = smscore_load_firmware_from_file(coredev, ++ fw_filename, NULL); ++ if (rc < 0) { ++ sms_warn("error %d loading firmware: %s, " ++ "trying again with default firmware", ++ rc, fw_filename); ++ ++ /* try again with the default firmware */ ++ fw_filename = smscore_fw_lkup[mode][type]; ++ rc = smscore_load_firmware_from_file(coredev, ++ fw_filename, NULL); ++ ++ if (rc < 0) { ++ sms_warn("error %d loading " ++ "firmware: %s", rc, ++ fw_filename); ++ return rc; ++ } ++ } ++ sms_log("firmware download success: %s", fw_filename); ++ } else ++ sms_info("mode %d supported by running " ++ "firmware", mode); ++ ++ buffer = kmalloc(sizeof(struct SmsMsgData_ST) + ++ SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); ++ if (buffer) { ++ struct SmsMsgData_ST *msg = ++ (struct SmsMsgData_ST *) ++ SMS_ALIGN_ADDRESS(buffer); ++ ++ SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, ++ sizeof(struct SmsMsgData_ST)); ++ msg->msgData[0] = mode; ++ ++ rc = smscore_sendrequest_and_wait( ++ coredev, msg, msg->xMsgHeader.msgLength, ++ &coredev->init_device_done); ++ ++ kfree(buffer); ++ } else { ++ sms_err("Could not allocate buffer for " ++ "init device message."); ++ rc = -ENOMEM; ++ } ++ } else { ++ if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { ++ sms_err("invalid mode specified %d", 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) { ++ coredev->mode = mode; ++ coredev->device_flags &= ~SMS_DEVICE_NOT_READY; ++ } ++ ++ if (rc < 0) ++ sms_err("return error code %d.", rc); ++ 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((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) { ++ sms_debug("\ndata rate %d bytes/secs", ++ (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->msgType == MSG_SMS_HO_PER_SLICES_IND) || ++ (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { ++ if (coredev->mode == DEVICE_MODE_DVBT_BDA) ++ phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; ++ } ++ ++ ++ client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); ++ ++ /* 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->msgType) { ++ case MSG_SMS_GET_VERSION_EX_RES: ++ { ++ struct SmsVersionRes_ST *ver = ++ (struct SmsVersionRes_ST *) phdr; ++ sms_debug("MSG_SMS_GET_VERSION_EX_RES " ++ "id %d prots 0x%x ver %d.%d", ++ ver->FirmwareId, ver->SupportedProtocols, ++ ver->RomVersionMajor, ver->RomVersionMinor); ++ ++ coredev->mode = ver->FirmwareId == 255 ? ++ DEVICE_MODE_NONE : ver->FirmwareId; ++ coredev->modes_supported = ver->SupportedProtocols; ++ ++ complete(&coredev->version_ex_done); ++ break; ++ } ++ case MSG_SMS_INIT_DEVICE_RES: ++ sms_debug("MSG_SMS_INIT_DEVICE_RES"); ++ complete(&coredev->init_device_done); ++ break; ++ case MSG_SW_RELOAD_START_RES: ++ sms_debug("MSG_SW_RELOAD_START_RES"); ++ complete(&coredev->reload_start_done); ++ break; ++ case MSG_SMS_DATA_DOWNLOAD_RES: ++ complete(&coredev->data_download_done); ++ break; ++ case MSG_SW_RELOAD_EXEC_RES: ++ sms_debug("MSG_SW_RELOAD_EXEC_RES"); ++ break; ++ case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: ++ sms_debug("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: ++ sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); ++ complete(&coredev->gpio_configuration_done); ++ break; ++ case MSG_SMS_GPIO_SET_LEVEL_RES: ++ sms_debug("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]; ++ sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", ++ 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 SmsMsgHdr_ST)), ++ (int)phdr->msgLength ++ - sizeof(struct SmsMsgHdr_ST)); ++ break; ++ ++ default: ++ 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) { ++ sms_err("bad parameter."); ++ return -EINVAL; ++ } ++ registered_client = smscore_find_client(coredev, data_type, id); ++ if (registered_client == client) ++ return 0; ++ ++ if (registered_client) { ++ sms_err("The msg ID already registered to another client."); ++ return -EEXIST; ++ } ++ listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); ++ if (!listentry) { ++ sms_err("Can't allocate memory for client id."); ++ 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)) { ++ sms_err("Client already exist."); ++ return -EEXIST; ++ } ++ ++ newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); ++ if (!newclient) { ++ sms_err("Failed to allocate memory for client."); ++ 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; ++ sms_debug("%p %d %d", 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); ++ } ++ ++ sms_info("%p", 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; ++ int rc; ++ ++ if (client == NULL) { ++ sms_err("Got NULL client"); ++ return -EINVAL; ++ } ++ ++ coredev = client->coredev; ++ ++ /* check that no other channel with same id exists */ ++ if (coredev == NULL) { ++ sms_err("Got NULL coredev"); ++ return -EINVAL; ++ } ++ ++ rc = smscore_validate_client(client->coredev, client, 0, ++ phdr->msgSrcId); ++ 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 SmsMsgHdr_ST hdr; ++ u32 data[6]; ++ } msg; ++ ++ if (coredev->device_flags & SMS_DEVICE_FAMILY2) { ++ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ msg.hdr.msgDstId = HIF_TASK; ++ msg.hdr.msgFlags = 0; ++ msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; ++ msg.hdr.msgLength = 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_16mA: ++ msg.data[3] = 7; /* Nova - 16mA */ ++ break; ++ case SMS_GPIO_OUTPUTDRIVING_12mA: ++ msg.data[3] = 5; /* Nova - 11mA */ ++ break; ++ case SMS_GPIO_OUTPUTDRIVING_8mA: ++ msg.data[3] = 3; /* Nova - 7mA */ ++ break; ++ case SMS_GPIO_OUTPUTDRIVING_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 SmsMsgHdr_ST hdr; ++ u32 data[3]; ++ } msg; ++ ++ if (pin > MAX_GPIO_PIN_NUMBER) ++ return -EINVAL; ++ ++ msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ msg.hdr.msgDstId = HIF_TASK; ++ msg.hdr.msgFlags = 0; ++ msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; ++ msg.hdr.msgLength = 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 GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, ++ u32 *pGroupNum, u32 *pGroupCfg) { ++ ++ *pGroupCfg = 1; ++ ++ if (PinNum <= 1) { ++ *pTranslatedPinNum = 0; ++ *pGroupNum = 9; ++ *pGroupCfg = 2; ++ } else if (PinNum >= 2 && PinNum <= 6) { ++ *pTranslatedPinNum = 2; ++ *pGroupNum = 0; ++ *pGroupCfg = 2; ++ } else if (PinNum >= 7 && PinNum <= 11) { ++ *pTranslatedPinNum = 7; ++ *pGroupNum = 1; ++ } else if (PinNum >= 12 && PinNum <= 15) { ++ *pTranslatedPinNum = 12; ++ *pGroupNum = 2; ++ *pGroupCfg = 3; ++ } else if (PinNum == 16) { ++ *pTranslatedPinNum = 16; ++ *pGroupNum = 23; ++ } else if (PinNum >= 17 && PinNum <= 24) { ++ *pTranslatedPinNum = 17; ++ *pGroupNum = 3; ++ } else if (PinNum == 25) { ++ *pTranslatedPinNum = 25; ++ *pGroupNum = 6; ++ } else if (PinNum >= 26 && PinNum <= 28) { ++ *pTranslatedPinNum = 26; ++ *pGroupNum = 4; ++ } else if (PinNum == 29) { ++ *pTranslatedPinNum = 29; ++ *pGroupNum = 5; ++ *pGroupCfg = 2; ++ } else if (PinNum == 30) { ++ *pTranslatedPinNum = 30; ++ *pGroupNum = 8; ++ } else if (PinNum == 31) { ++ *pTranslatedPinNum = 31; ++ *pGroupNum = 17; ++ } else ++ return -1; ++ ++ *pGroupCfg <<= 24; ++ ++ return 0; ++} ++ ++int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, ++ struct smscore_gpio_config *pGpioConfig) { ++ ++ u32 totalLen; ++ u32 TranslatedPinNum = 0; ++ u32 GroupNum = 0; ++ u32 ElectricChar; ++ u32 groupCfg; ++ void *buffer; ++ int rc; ++ ++ struct SetGpioMsg { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 msgData[6]; ++ } *pMsg; ++ ++ ++ if (PinNum > MAX_GPIO_PIN_NUMBER) ++ return -EINVAL; ++ ++ if (pGpioConfig == NULL) ++ return -EINVAL; ++ ++ totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); ++ ++ buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, ++ GFP_KERNEL | GFP_DMA); ++ if (!buffer) ++ return -ENOMEM; ++ ++ pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); ++ ++ pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ pMsg->xMsgHeader.msgDstId = HIF_TASK; ++ pMsg->xMsgHeader.msgFlags = 0; ++ pMsg->xMsgHeader.msgLength = (u16) totalLen; ++ pMsg->msgData[0] = PinNum; ++ ++ if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { ++ pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; ++ if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, ++ &groupCfg) != 0) { ++ rc = -EINVAL; ++ goto free; ++ } ++ ++ pMsg->msgData[1] = TranslatedPinNum; ++ pMsg->msgData[2] = GroupNum; ++ ElectricChar = (pGpioConfig->PullUpDown) ++ | (pGpioConfig->InputCharacteristics << 2) ++ | (pGpioConfig->OutputSlewRate << 3) ++ | (pGpioConfig->OutputDriving << 4); ++ pMsg->msgData[3] = ElectricChar; ++ pMsg->msgData[4] = pGpioConfig->Direction; ++ pMsg->msgData[5] = groupCfg; ++ } else { ++ pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; ++ pMsg->msgData[1] = pGpioConfig->PullUpDown; ++ pMsg->msgData[2] = pGpioConfig->OutputSlewRate; ++ pMsg->msgData[3] = pGpioConfig->OutputDriving; ++ pMsg->msgData[4] = pGpioConfig->Direction; ++ pMsg->msgData[5] = 0; ++ } ++ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); ++ rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, ++ &coredev->gpio_configuration_done); ++ ++ if (rc != 0) { ++ if (rc == -ETIME) ++ sms_err("smscore_gpio_configure timeout"); ++ else ++ sms_err("smscore_gpio_configure error"); ++ } ++free: ++ kfree(buffer); ++ ++ return rc; ++} ++ ++int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, ++ u8 NewLevel) { ++ ++ u32 totalLen; ++ int rc; ++ void *buffer; ++ ++ struct SetGpioMsg { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 msgData[3]; /* keep it 3 ! */ ++ } *pMsg; ++ ++ if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) ++ return -EINVAL; ++ ++ totalLen = sizeof(struct SmsMsgHdr_ST) + ++ (3 * sizeof(u32)); /* keep it 3 ! */ ++ ++ buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, ++ GFP_KERNEL | GFP_DMA); ++ if (!buffer) ++ return -ENOMEM; ++ ++ pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); ++ ++ pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ pMsg->xMsgHeader.msgDstId = HIF_TASK; ++ pMsg->xMsgHeader.msgFlags = 0; ++ pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; ++ pMsg->xMsgHeader.msgLength = (u16) totalLen; ++ pMsg->msgData[0] = PinNum; ++ pMsg->msgData[1] = NewLevel; ++ ++ /* Send message to SMS */ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); ++ rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, ++ &coredev->gpio_set_level_done); ++ ++ if (rc != 0) { ++ if (rc == -ETIME) ++ sms_err("smscore_gpio_set_level timeout"); ++ else ++ sms_err("smscore_gpio_set_level error"); ++ } ++ kfree(buffer); ++ ++ return rc; ++} ++ ++int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, ++ u8 *level) { ++ ++ u32 totalLen; ++ int rc; ++ void *buffer; ++ ++ struct SetGpioMsg { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 msgData[2]; ++ } *pMsg; ++ ++ ++ if (PinNum > MAX_GPIO_PIN_NUMBER) ++ return -EINVAL; ++ ++ totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); ++ ++ buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, ++ GFP_KERNEL | GFP_DMA); ++ if (!buffer) ++ return -ENOMEM; ++ ++ pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); ++ ++ pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ pMsg->xMsgHeader.msgDstId = HIF_TASK; ++ pMsg->xMsgHeader.msgFlags = 0; ++ pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; ++ pMsg->xMsgHeader.msgLength = (u16) totalLen; ++ pMsg->msgData[0] = PinNum; ++ pMsg->msgData[1] = 0; ++ ++ /* Send message to SMS */ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); ++ rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, ++ &coredev->gpio_get_level_done); ++ ++ if (rc != 0) { ++ if (rc == -ETIME) ++ sms_err("smscore_gpio_get_level timeout"); ++ else ++ sms_err("smscore_gpio_get_level error"); ++ } ++ 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) ++{ ++ int rc = 0; ++ ++ 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 rc; ++} ++ ++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(¬ifyee->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); ++ ++ sms_debug(""); ++} ++ ++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"); +diff --git a/drivers/media/common/siano/smscoreapi.h b/drivers/media/common/siano/smscoreapi.h +new file mode 100644 +index 0000000..c592ae0 +--- /dev/null ++++ b/drivers/media/common/siano/smscoreapi.h +@@ -0,0 +1,775 @@ ++/**************************************************************** ++ ++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 . ++ ++****************************************************************/ ++ ++#ifndef __SMS_CORE_API_H__ ++#define __SMS_CORE_API_H__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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_) ++ ++#ifndef min ++#define min(a, b) (((a) < (b)) ? (a) : (b)) ++#endif ++ ++#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_FAMILY2 1 ++#define SMS_ROM_NO_RESPONSE 2 ++#define SMS_DEVICE_NOT_READY 0x8000000 ++ ++enum sms_device_type_st { ++ SMS_STELLAR = 0, ++ SMS_NOVA_A0, ++ SMS_NOVA_B0, ++ SMS_VEGA, ++ SMS_NUM_OF_DEVICE_TYPES ++}; ++ ++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 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; ++ ++ /* Infrared (IR) */ ++ struct ir_t ir; ++ ++ int led_state; ++}; ++ ++/* GPIO definitions for antenna frequency domain control (SMS8021) */ ++#define SMS_ANTENNA_GPIO_0 1 ++#define SMS_ANTENNA_GPIO_1 0 ++ ++#define BW_8_MHZ 0 ++#define BW_7_MHZ 1 ++#define BW_6_MHZ 2 ++#define BW_5_MHZ 3 ++#define BW_ISDBT_1SEG 4 ++#define BW_ISDBT_3SEG 5 ++ ++#define MSG_HDR_FLAG_SPLIT_MSG 4 ++ ++#define MAX_GPIO_PIN_NUMBER 31 ++ ++#define HIF_TASK 11 ++#define SMS_HOST_LIB 150 ++#define DVBT_BDA_CONTROL_MSG_ID 201 ++ ++#define SMS_MAX_PAYLOAD_SIZE 240 ++#define SMS_TUNE_TIMEOUT 500 ++ ++#define MSG_SMS_GPIO_CONFIG_REQ 507 ++#define MSG_SMS_GPIO_CONFIG_RES 508 ++#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 ++#define MSG_SMS_GPIO_SET_LEVEL_RES 510 ++#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 ++#define MSG_SMS_GPIO_GET_LEVEL_RES 512 ++#define MSG_SMS_RF_TUNE_REQ 561 ++#define MSG_SMS_RF_TUNE_RES 562 ++#define MSG_SMS_INIT_DEVICE_REQ 578 ++#define MSG_SMS_INIT_DEVICE_RES 579 ++#define MSG_SMS_ADD_PID_FILTER_REQ 601 ++#define MSG_SMS_ADD_PID_FILTER_RES 602 ++#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 ++#define MSG_SMS_REMOVE_PID_FILTER_RES 604 ++#define MSG_SMS_DAB_CHANNEL 607 ++#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 ++#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 ++#define MSG_SMS_GET_STATISTICS_RES 616 ++#define MSG_SMS_GET_STATISTICS_REQ 615 ++#define MSG_SMS_HO_PER_SLICES_IND 630 ++#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 ++#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 ++#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 ++#define MSG_SMS_DATA_DOWNLOAD_REQ 660 ++#define MSG_SMS_DATA_DOWNLOAD_RES 661 ++#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 ++#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 ++#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 ++#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 ++#define MSG_SMS_GET_VERSION_EX_REQ 668 ++#define MSG_SMS_GET_VERSION_EX_RES 669 ++#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 ++#define MSG_SMS_I2C_SET_FREQ_REQ 685 ++#define MSG_SMS_GENERIC_I2C_REQ 687 ++#define MSG_SMS_GENERIC_I2C_RES 688 ++#define MSG_SMS_DVBT_BDA_DATA 693 ++#define MSG_SW_RELOAD_REQ 697 ++#define MSG_SMS_DATA_MSG 699 ++#define MSG_SW_RELOAD_START_REQ 702 ++#define MSG_SW_RELOAD_START_RES 703 ++#define MSG_SW_RELOAD_EXEC_REQ 704 ++#define MSG_SW_RELOAD_EXEC_RES 705 ++#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 ++#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 ++#define MSG_SMS_GPIO_CONFIG_EX_RES 713 ++#define MSG_SMS_ISDBT_TUNE_REQ 776 ++#define MSG_SMS_ISDBT_TUNE_RES 777 ++#define MSG_SMS_TRANSMISSION_IND 782 ++#define MSG_SMS_START_IR_REQ 800 ++#define MSG_SMS_START_IR_RES 801 ++#define MSG_SMS_IR_SAMPLES_IND 802 ++#define MSG_SMS_SIGNAL_DETECTED_IND 827 ++#define MSG_SMS_NO_SIGNAL_IND 828 ++ ++#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ ++ (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ ++ (ptr)->msgLength = len; (ptr)->msgFlags = 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_MAX, ++}; ++ ++struct SmsMsgHdr_ST { ++ u16 msgType; ++ u8 msgSrcId; ++ u8 msgDstId; ++ u16 msgLength; /* Length of entire message, including header */ ++ u16 msgFlags; ++}; ++ ++struct SmsMsgData_ST { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 msgData[1]; ++}; ++ ++struct SmsMsgData_ST2 { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 msgData[2]; ++}; ++ ++struct SmsDataDownload_ST { ++ struct SmsMsgHdr_ST xMsgHeader; ++ u32 MemAddr; ++ u8 Payload[SMS_MAX_PAYLOAD_SIZE]; ++}; ++ ++struct SmsVersionRes_ST { ++ struct SmsMsgHdr_ST xMsgHeader; ++ ++ u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ ++ u8 Step; /* 0 - Step A */ ++ u8 MetalFix; /* 0 - Metal 0 */ ++ ++ /* FirmwareId 0xFF if ROM, otherwise the ++ * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ ++ u8 FirmwareId; ++ /* SupportedProtocols Bitwise OR combination of ++ * supported protocols */ ++ u8 SupportedProtocols; ++ ++ u8 VersionMajor; ++ u8 VersionMinor; ++ u8 VersionPatch; ++ u8 VersionFieldPatch; ++ ++ u8 RomVersionMajor; ++ u8 RomVersionMinor; ++ u8 RomVersionPatch; ++ u8 RomVersionFieldPatch; ++ ++ u8 TextLabel[34]; ++}; ++ ++struct SmsFirmware_ST { ++ u32 CheckSum; ++ u32 Length; ++ u32 StartAddress; ++ u8 Payload[1]; ++}; ++ ++/* Statistics information returned as response for ++ * SmsHostApiGetStatistics_Req */ ++struct SMSHOSTLIB_STATISTICS_ST { ++ u32 Reserved; /* Reserved */ ++ ++ /* Common parameters */ ++ u32 IsRfLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsExternalLNAOn; /* 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 InBandPwr; /* In band power in dBM */ ++ s32 CarrierOffset; /* Carrier Offset in bin/1024 */ ++ ++ /* Transmission parameters */ ++ u32 Frequency; /* Frequency in Hz */ ++ u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ ++ u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, ++ for DVB-T/H FFT mode carriers in Kilos */ ++ u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, ++ valid only for DVB-T/H */ ++ u32 GuardInterval; /* Guard Interval from ++ SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ ++ u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, ++ valid only for DVB-T/H */ ++ u32 LPCodeRate; /* 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 BurstSize; /* Current burst size in bytes, ++ valid only for DVB-H */ ++ u32 BurstDuration; /* Current burst duration in mSec, ++ valid only for DVB-H */ ++ u32 BurstCycleTime; /* Current burst cycle time in mSec, ++ valid only for DVB-H */ ++ u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, ++ as calculated by demodulator, valid only for DVB-H */ ++ u32 NumOfRows; /* Number of rows in MPE table, ++ valid only for DVB-H */ ++ u32 NumOfPaddCols; /* Number of padding columns in MPE table, ++ valid only for DVB-H */ ++ u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, ++ valid only for DVB-H */ ++ u32 ErrorTSPackets; /* Number of erroneous ++ transport-stream packets */ ++ u32 TotalTSPackets; /* Total number of transport-stream packets */ ++ u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include ++ errors after MPE RS decoding */ ++ u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors ++ after MPE RS decoding */ ++ u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were ++ corrected by MPE RS decoding */ ++ /* Common params */ ++ u32 BERErrorCount; /* Number of errornous SYNC bits. */ ++ u32 BERBitCount; /* Total number of SYNC bits. */ ++ ++ /* Interface information */ ++ u32 SmsToHostTxErrors; /* Total number of transmission errors. */ ++ ++ /* DAB/T-DMB */ ++ u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ ++ ++ /* DVB-H TPS parameters */ ++ u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; ++ if set to 0xFFFFFFFF cell_id not yet recovered */ ++ u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - ++ Time Slicing indicator, bit 0 - MPE-FEC indicator */ ++ u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - ++ Time Slicing indicator, bit 0 - MPE-FEC indicator */ ++ ++ u32 NumMPEReceived; /* DVB-H, Num MPE section received */ ++ ++ u32 ReservedFields[10]; /* Reserved */ ++}; ++ ++struct SmsMsgStatisticsInfo_ST { ++ u32 RequestResult; ++ ++ struct SMSHOSTLIB_STATISTICS_ST Stat; ++ ++ /* Split the calc of the SNR in DAB */ ++ u32 Signal; /* dB */ ++ u32 Noise; /* dB */ ++ ++}; ++ ++struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { ++ /* Per-layer information */ ++ u32 CodeRate; /* 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 BERErrorCount; /* Post Viterbi Error Bits Count */ ++ u32 BERBitCount; /* Post Viterbi Total Bits Count */ ++ u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ ++ u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ ++ u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ ++ u32 TotalTSPackets; /* Total number of transport-stream packets */ ++ u32 TILdepthI; /* Time interleaver depth I parameter, ++ * 255 means layer does not exist */ ++ u32 NumberOfSegments; /* Number of segments in layer A, ++ * 255 means layer does not exist */ ++ u32 TMCCErrors; /* TMCC errors */ ++}; ++ ++struct SMSHOSTLIB_STATISTICS_ISDBT_ST { ++ u32 StatisticsType; /* 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 FullSize; /* Total size of the structure returned by the modem. ++ * If the size requested by the host is smaller than ++ * FullSize, the struct will be truncated */ ++ ++ /* Common parameters */ ++ u32 IsRfLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ ++ ++ /* Reception quality */ ++ s32 SNR; /* dB */ ++ s32 RSSI; /* dBm */ ++ s32 InBandPwr; /* In band power in dBM */ ++ s32 CarrierOffset; /* Carrier Offset in Hz */ ++ ++ /* Transmission parameters */ ++ u32 Frequency; /* Frequency in Hz */ ++ u32 Bandwidth; /* Bandwidth in MHz */ ++ u32 TransmissionMode; /* ISDB-T transmission mode */ ++ u32 ModemState; /* 0 - Acquisition, 1 - Locked */ ++ u32 GuardInterval; /* Guard Interval, 1 divided by value */ ++ u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ ++ u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ ++ u32 NumOfLayers; /* Number of ISDB-T layers in the network */ ++ ++ /* Per-layer information */ ++ /* Layers A, B and C */ ++ struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; ++ /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ ++ ++ /* Interface information */ ++ u32 SmsToHostTxErrors; /* Total number of transmission errors. */ ++}; ++ ++struct PID_STATISTICS_DATA_S { ++ 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 PID_DATA_S { ++ u32 pid; ++ u32 num_rows; ++ struct PID_STATISTICS_DATA_S 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.TransmissionMode == 0) \ ++ _stat.TransmissionMode = 2; \ ++ else if (_stat.TransmissionMode == 1) \ ++ _stat.TransmissionMode = 8; \ ++ else \ ++ _stat.TransmissionMode = 4; ++ ++struct TRANSMISSION_STATISTICS_S { ++ u32 Frequency; /* Frequency in Hz */ ++ u32 Bandwidth; /* Bandwidth in MHz */ ++ u32 TransmissionMode; /* FFT mode carriers in Kilos */ ++ u32 GuardInterval; /* Guard Interval from ++ SMSHOSTLIB_GUARD_INTERVALS_ET */ ++ u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ ++ u32 LPCodeRate; /* 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 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; ++ if set to 0xFFFFFFFF cell_id not yet recovered */ ++ u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - ++ Time Slicing indicator, bit 0 - MPE-FEC indicator */ ++ u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - ++ Time Slicing indicator, bit 0 - MPE-FEC indicator */ ++ u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ ++}; ++ ++struct RECEPTION_STATISTICS_S { ++ u32 IsRfLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ ++ u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ ++ ++ u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ ++ s32 SNR; /* dB */ ++ u32 BER; /* Post Viterbi BER [1E-5] */ ++ u32 BERErrorCount; /* Number of erronous SYNC bits. */ ++ u32 BERBitCount; /* 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 InBandPwr; /* In band power in dBM */ ++ s32 CarrierOffset; /* Carrier Offset in bin/1024 */ ++ u32 ErrorTSPackets; /* Number of erroneous ++ transport-stream packets */ ++ u32 TotalTSPackets; /* Total number of transport-stream packets */ ++ ++ s32 MRC_SNR; /* dB */ ++ s32 MRC_RSSI; /* dBm */ ++ s32 MRC_InBandPwr; /* In band power in dBM */ ++}; ++ ++ ++/* Statistics information returned as response for ++ * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ ++struct SMSHOSTLIB_STATISTICS_DVB_S { ++ /* Reception */ ++ struct RECEPTION_STATISTICS_S ReceptionData; ++ ++ /* Transmission parameters */ ++ struct TRANSMISSION_STATISTICS_S TransmissionData; ++ ++ /* Burst parameters, valid only for DVB-H */ ++#define SRVM_MAX_PID_FILTERS 8 ++ struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; ++}; ++ ++struct SRVM_SIGNAL_STATUS_S { ++ u32 result; ++ u32 snr; ++ u32 tsPackets; ++ u32 etsPackets; ++ u32 constellation; ++ u32 hpCode; ++ u32 tpsSrvIndLP; ++ u32 tpsSrvIndHP; ++ u32 cellId; ++ u32 reason; ++ ++ s32 inBandPower; ++ u32 requestId; ++}; ++ ++struct SMSHOSTLIB_I2C_REQ_ST { ++ u32 DeviceAddress; /* I2c device address */ ++ u32 WriteCount; /* number of bytes to write */ ++ u32 ReadCount; /* number of bytes to read */ ++ u8 Data[1]; ++}; ++ ++struct SMSHOSTLIB_I2C_RES_ST { ++ u32 Status; /* non-zero value in case of failure */ ++ u32 ReadCount; /* 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; ++ ++#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 ++#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 ++ u8 outputslewrate; ++ ++#define SMS_GPIO_OUTPUTDRIVING_4mA 0 ++#define SMS_GPIO_OUTPUTDRIVING_8mA 1 ++#define SMS_GPIO_OUTPUTDRIVING_12mA 2 ++#define SMS_GPIO_OUTPUTDRIVING_16mA 3 ++ u8 outputdriving; ++}; ++ ++struct smscore_gpio_config { ++#define SMS_GPIO_DIRECTION_INPUT 0 ++#define SMS_GPIO_DIRECTION_OUTPUT 1 ++ u8 Direction; ++ ++#define SMS_GPIO_PULL_UP_DOWN_NONE 0 ++#define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 ++#define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 ++#define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 ++ u8 PullUpDown; ++ ++#define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 ++#define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 ++ u8 InputCharacteristics; ++ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ ++ ++ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ ++#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ ++ u8 OutputSlewRate; ++ ++#define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ ++ ++#define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ ++#define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ ++ u8 OutputDriving; ++}; ++ ++extern void smscore_registry_setmode(char *devpath, int mode); ++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); ++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_get_fw_filename(struct smscore_device_t *coredev, ++ int mode, char *filename); ++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 PinNum, ++ struct smscore_gpio_config *pGpioConfig); ++extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, ++ u8 NewLevel); ++extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, ++ 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); ++ ++ ++/* ------------------------------------------------------------------------ */ ++ ++#define DBG_INFO 1 ++#define DBG_ADV 2 ++ ++#define sms_printk(kern, fmt, arg...) \ ++ printk(kern "%s: " fmt "\n", __func__, ##arg) ++ ++#define dprintk(kern, lvl, fmt, arg...) do {\ ++ if (sms_dbg & lvl) \ ++ sms_printk(kern, fmt, ##arg); } while (0) ++ ++#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) ++#define sms_err(fmt, arg...) \ ++ sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) ++#define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) ++#define sms_info(fmt, arg...) \ ++ dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) ++#define sms_debug(fmt, arg...) \ ++ dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) ++ ++ ++#endif /* __SMS_CORE_API_H__ */ +diff --git a/drivers/media/common/siano/smsdvb.c b/drivers/media/common/siano/smsdvb.c +new file mode 100644 +index 0000000..aa77e54 +--- /dev/null ++++ b/drivers/media/common/siano/smsdvb.c +@@ -0,0 +1,1078 @@ ++/**************************************************************** ++ ++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 . ++ ++****************************************************************/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++ ++#include "smscoreapi.h" ++#include "smsendian.h" ++#include "sms-cards.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++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 SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; ++ int event_fe_state; ++ int event_unc_state; ++}; ++ ++static struct list_head g_smsdvb_clients; ++static struct mutex g_smsdvb_clientslock; ++ ++static int sms_dbg; ++module_param_named(debug, sms_dbg, int, 0644); ++MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); ++ ++/* 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: ++ sms_debug("DVB3_EVENT_INIT"); ++ sms_board_event(coredev, BOARD_EVENT_BIND); ++ break; ++ case DVB3_EVENT_SLEEP: ++ sms_debug("DVB3_EVENT_SLEEP"); ++ sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); ++ break; ++ case DVB3_EVENT_HOTPLUG: ++ sms_debug("DVB3_EVENT_HOTPLUG"); ++ 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; ++ sms_debug("DVB3_EVENT_FE_LOCK"); ++ 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; ++ sms_debug("DVB3_EVENT_FE_UNLOCK"); ++ 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; ++ sms_debug("DVB3_EVENT_UNC_OK"); ++ 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; ++ sms_debug("DVB3_EVENT_UNC_ERR"); ++ sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); ++ } ++ break; ++ ++ default: ++ sms_err("Unknown dvb3 api event"); ++ break; ++ } ++} ++ ++ ++static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, ++ struct SMSHOSTLIB_STATISTICS_ST *p) ++{ ++ if (sms_dbg & 2) { ++ printk(KERN_DEBUG "Reserved = %d", p->Reserved); ++ printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); ++ printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); ++ printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); ++ printk(KERN_DEBUG "SNR = %d", p->SNR); ++ printk(KERN_DEBUG "BER = %d", p->BER); ++ printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); ++ printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); ++ printk(KERN_DEBUG "MFER = %d", p->MFER); ++ printk(KERN_DEBUG "RSSI = %d", p->RSSI); ++ printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); ++ printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); ++ printk(KERN_DEBUG "Frequency = %d", p->Frequency); ++ printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); ++ printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); ++ printk(KERN_DEBUG "ModemState = %d", p->ModemState); ++ printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); ++ printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); ++ printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); ++ printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); ++ printk(KERN_DEBUG "Constellation = %d", p->Constellation); ++ printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); ++ printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); ++ printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); ++ printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); ++ printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); ++ printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); ++ printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); ++ printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); ++ printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); ++ printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); ++ printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); ++ printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); ++ printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); ++ printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); ++ printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); ++ printk(KERN_DEBUG "PreBER = %d", p->PreBER); ++ printk(KERN_DEBUG "CellId = %d", p->CellId); ++ printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); ++ printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); ++ printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); ++ } ++ ++ pReceptionData->IsDemodLocked = p->IsDemodLocked; ++ ++ pReceptionData->SNR = p->SNR; ++ pReceptionData->BER = p->BER; ++ pReceptionData->BERErrorCount = p->BERErrorCount; ++ pReceptionData->InBandPwr = p->InBandPwr; ++ pReceptionData->ErrorTSPackets = p->ErrorTSPackets; ++}; ++ ++ ++static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, ++ struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) ++{ ++ int i; ++ ++ if (sms_dbg & 2) { ++ printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); ++ printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); ++ printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); ++ printk(KERN_DEBUG "SNR = %d", p->SNR); ++ printk(KERN_DEBUG "RSSI = %d", p->RSSI); ++ printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); ++ printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); ++ printk(KERN_DEBUG "Frequency = %d", p->Frequency); ++ printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); ++ printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); ++ printk(KERN_DEBUG "ModemState = %d", p->ModemState); ++ printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); ++ printk(KERN_DEBUG "SystemType = %d", p->SystemType); ++ printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); ++ printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); ++ printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); ++ ++ for (i = 0; i < 3; i++) { ++ printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); ++ printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); ++ printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); ++ printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); ++ printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); ++ printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); ++ printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); ++ printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); ++ printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); ++ printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); ++ printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); ++ printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); ++ } ++ } ++ ++ pReceptionData->IsDemodLocked = p->IsDemodLocked; ++ ++ pReceptionData->SNR = p->SNR; ++ pReceptionData->InBandPwr = p->InBandPwr; ++ ++ pReceptionData->ErrorTSPackets = 0; ++ pReceptionData->BER = 0; ++ pReceptionData->BERErrorCount = 0; ++ for (i = 0; i < 3; i++) { ++ pReceptionData->BER += p->LayerInfo[i].BER; ++ pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; ++ pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; ++ } ++} ++ ++static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) ++{ ++ struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; ++ struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) ++ + cb->offset); ++ u32 *pMsgData = (u32 *) phdr + 1; ++ /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ ++ bool is_status_update = false; ++ ++ smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); ++ ++ switch (phdr->msgType) { ++ case MSG_SMS_DVBT_BDA_DATA: ++ dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), ++ cb->size - sizeof(struct SmsMsgHdr_ST)); ++ break; ++ ++ case MSG_SMS_RF_TUNE_RES: ++ case MSG_SMS_ISDBT_TUNE_RES: ++ complete(&client->tune_done); ++ break; ++ ++ case MSG_SMS_SIGNAL_DETECTED_IND: ++ sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); ++ client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; ++ is_status_update = true; ++ break; ++ ++ case MSG_SMS_NO_SIGNAL_IND: ++ sms_info("MSG_SMS_NO_SIGNAL_IND"); ++ client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; ++ is_status_update = true; ++ break; ++ ++ case MSG_SMS_TRANSMISSION_IND: { ++ sms_info("MSG_SMS_TRANSMISSION_IND"); ++ ++ pMsgData++; ++ memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, ++ sizeof(struct TRANSMISSION_STATISTICS_S)); ++ ++ /* Mo need to correct guard interval ++ * (as opposed to old statistics message). ++ */ ++ CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); ++ CORRECT_STAT_TRANSMISSON_MODE( ++ client->sms_stat_dvb.TransmissionData); ++ is_status_update = true; ++ break; ++ } ++ case MSG_SMS_HO_PER_SLICES_IND: { ++ struct RECEPTION_STATISTICS_S *pReceptionData = ++ &client->sms_stat_dvb.ReceptionData; ++ struct SRVM_SIGNAL_STATUS_S SignalStatusData; ++ ++ /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ ++ pMsgData++; ++ SignalStatusData.result = pMsgData[0]; ++ SignalStatusData.snr = pMsgData[1]; ++ SignalStatusData.inBandPower = (s32) pMsgData[2]; ++ SignalStatusData.tsPackets = pMsgData[3]; ++ SignalStatusData.etsPackets = pMsgData[4]; ++ SignalStatusData.constellation = pMsgData[5]; ++ SignalStatusData.hpCode = pMsgData[6]; ++ SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; ++ SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; ++ SignalStatusData.cellId = pMsgData[9] & 0xFFFF; ++ SignalStatusData.reason = pMsgData[10]; ++ SignalStatusData.requestId = pMsgData[11]; ++ pReceptionData->IsRfLocked = pMsgData[16]; ++ pReceptionData->IsDemodLocked = pMsgData[17]; ++ pReceptionData->ModemState = pMsgData[12]; ++ pReceptionData->SNR = pMsgData[1]; ++ pReceptionData->BER = pMsgData[13]; ++ pReceptionData->RSSI = pMsgData[14]; ++ CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); ++ ++ pReceptionData->InBandPwr = (s32) pMsgData[2]; ++ pReceptionData->CarrierOffset = (s32) pMsgData[15]; ++ pReceptionData->TotalTSPackets = pMsgData[3]; ++ pReceptionData->ErrorTSPackets = pMsgData[4]; ++ ++ /* TS PER */ ++ if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) ++ > 0) { ++ pReceptionData->TS_PER = (SignalStatusData.etsPackets ++ * 100) / (SignalStatusData.tsPackets ++ + SignalStatusData.etsPackets); ++ } else { ++ pReceptionData->TS_PER = 0; ++ } ++ ++ pReceptionData->BERBitCount = pMsgData[18]; ++ pReceptionData->BERErrorCount = pMsgData[19]; ++ ++ pReceptionData->MRC_SNR = pMsgData[20]; ++ pReceptionData->MRC_InBandPwr = pMsgData[21]; ++ pReceptionData->MRC_RSSI = pMsgData[22]; ++ ++ is_status_update = true; ++ break; ++ } ++ case MSG_SMS_GET_STATISTICS_RES: { ++ union { ++ struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; ++ struct SmsMsgStatisticsInfo_ST dvb; ++ } *p = (void *) (phdr + 1); ++ struct RECEPTION_STATISTICS_S *pReceptionData = ++ &client->sms_stat_dvb.ReceptionData; ++ ++ sms_info("MSG_SMS_GET_STATISTICS_RES"); ++ ++ is_status_update = true; ++ ++ switch (smscore_get_device_mode(client->coredev)) { ++ case DEVICE_MODE_ISDBT: ++ case DEVICE_MODE_ISDBT_BDA: ++ smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); ++ break; ++ default: ++ smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); ++ } ++ if (!pReceptionData->IsDemodLocked) { ++ pReceptionData->SNR = 0; ++ pReceptionData->BER = 0; ++ pReceptionData->BERErrorCount = 0; ++ pReceptionData->InBandPwr = 0; ++ pReceptionData->ErrorTSPackets = 0; ++ } ++ ++ complete(&client->tune_done); ++ break; ++ } ++ default: ++ sms_info("Unhandled message %d", phdr->msgType); ++ ++ } ++ smscore_putbuffer(client->coredev, cb); ++ ++ if (is_status_update) { ++ if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { ++ client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER ++ | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); ++ if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets ++ == 0) ++ sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); ++ else ++ sms_board_dvb3_event(client, ++ DVB3_EVENT_UNC_ERR); ++ ++ } else { ++ if (client->sms_stat_dvb.ReceptionData.IsRfLocked) ++ client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ else ++ client->fe_status = 0; ++ sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); ++ } ++ } ++ ++ return 0; ++} ++ ++static void smsdvb_unregister_client(struct smsdvb_client_t *client) ++{ ++ /* must be called under clientslock */ ++ ++ list_del(&client->entry); ++ ++ smscore_unregister_client(client->smsclient); ++ dvb_unregister_frontend(&client->frontend); ++ dvb_dmxdev_release(&client->dmxdev); ++ dvb_dmx_release(&client->demux); ++ 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 SmsMsgData_ST PidMsg; ++ ++ sms_debug("add pid %d(%x)", ++ feed->pid, feed->pid); ++ ++ PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ PidMsg.xMsgHeader.msgDstId = HIF_TASK; ++ PidMsg.xMsgHeader.msgFlags = 0; ++ PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; ++ PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); ++ PidMsg.msgData[0] = feed->pid; ++ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); ++ return smsclient_sendrequest(client->smsclient, ++ &PidMsg, sizeof(PidMsg)); ++} ++ ++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 SmsMsgData_ST PidMsg; ++ ++ sms_debug("remove pid %d(%x)", ++ feed->pid, feed->pid); ++ ++ PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ PidMsg.xMsgHeader.msgDstId = HIF_TASK; ++ PidMsg.xMsgHeader.msgFlags = 0; ++ PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; ++ PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); ++ PidMsg.msgData[0] = feed->pid; ++ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); ++ return smsclient_sendrequest(client->smsclient, ++ &PidMsg, sizeof(PidMsg)); ++} ++ ++static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, ++ void *buffer, size_t size, ++ struct completion *completion) ++{ ++ int rc; ++ ++ smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); ++ 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 SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, ++ DVBT_BDA_CONTROL_MSG_ID, ++ HIF_TASK, ++ sizeof(struct SmsMsgHdr_ST), 0 }; ++ ++ rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), ++ &client->tune_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, ++ (client->sms_stat_dvb.ReceptionData.BER ++ == 0) ? SMS_LED_HI : SMS_LED_LO); ++ else ++ return sms_board_led_feedback(client->coredev, SMS_LED_OFF); ++} ++ ++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->sms_stat_dvb.ReceptionData.BER; ++ ++ led_feedback(client); ++ ++ return rc; ++} ++ ++static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ int rc; ++ ++ struct smsdvb_client_t *client; ++ client = container_of(fe, struct smsdvb_client_t, frontend); ++ ++ rc = smsdvb_send_statistics_request(client); ++ ++ if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) ++ *strength = 0; ++ else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) ++ *strength = 100; ++ else ++ *strength = ++ (client->sms_stat_dvb.ReceptionData.InBandPwr ++ + 95) * 3 / 2; ++ ++ led_feedback(client); ++ ++ return rc; ++} ++ ++static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ int rc; ++ struct smsdvb_client_t *client; ++ client = container_of(fe, struct smsdvb_client_t, frontend); ++ ++ rc = smsdvb_send_statistics_request(client); ++ ++ *snr = client->sms_stat_dvb.ReceptionData.SNR; ++ ++ led_feedback(client); ++ ++ return rc; ++} ++ ++static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ int rc; ++ struct smsdvb_client_t *client; ++ client = container_of(fe, struct smsdvb_client_t, frontend); ++ ++ rc = smsdvb_send_statistics_request(client); ++ ++ *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; ++ ++ led_feedback(client); ++ ++ return rc; ++} ++ ++static int smsdvb_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ sms_debug(""); ++ ++ 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 SmsMsgHdr_ST Msg; ++ u32 Data[3]; ++ } Msg; ++ ++ int ret; ++ ++ client->fe_status = FE_HAS_SIGNAL; ++ client->event_fe_state = -1; ++ client->event_unc_state = -1; ++ fe->dtv_property_cache.delivery_system = SYS_DVBT; ++ ++ Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ Msg.Msg.msgDstId = HIF_TASK; ++ Msg.Msg.msgFlags = 0; ++ Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; ++ Msg.Msg.msgLength = sizeof(Msg); ++ Msg.Data[0] = c->frequency; ++ Msg.Data[2] = 12000000; ++ ++ sms_info("%s: freq %d band %d", __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); ++ ++ struct { ++ struct SmsMsgHdr_ST Msg; ++ u32 Data[4]; ++ } Msg; ++ ++ fe->dtv_property_cache.delivery_system = SYS_ISDBT; ++ ++ Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; ++ Msg.Msg.msgDstId = HIF_TASK; ++ Msg.Msg.msgFlags = 0; ++ Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; ++ Msg.Msg.msgLength = sizeof(Msg); ++ ++ if (c->isdbt_sb_segment_idx == -1) ++ c->isdbt_sb_segment_idx = 0; ++ ++ switch (c->isdbt_sb_segment_count) { ++ case 3: ++ Msg.Data[1] = BW_ISDBT_3SEG; ++ break; ++ case 1: ++ Msg.Data[1] = BW_ISDBT_1SEG; ++ break; ++ case 0: /* AUTO */ ++ switch (c->bandwidth_hz / 1000000) { ++ case 8: ++ case 7: ++ c->isdbt_sb_segment_count = 3; ++ Msg.Data[1] = BW_ISDBT_3SEG; ++ break; ++ case 6: ++ c->isdbt_sb_segment_count = 1; ++ Msg.Data[1] = BW_ISDBT_1SEG; ++ break; ++ default: /* Assumes 6 MHZ bw */ ++ c->isdbt_sb_segment_count = 1; ++ c->bandwidth_hz = 6000; ++ Msg.Data[1] = BW_ISDBT_1SEG; ++ break; ++ } ++ break; ++ default: ++ sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); ++ return -EINVAL; ++ } ++ ++ Msg.Data[0] = c->frequency; ++ Msg.Data[2] = 12000000; ++ Msg.Data[3] = c->isdbt_sb_segment_idx; ++ ++ sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, ++ c->frequency, c->isdbt_sb_segment_count, ++ c->isdbt_sb_segment_idx); ++ ++ return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), ++ &client->tune_done); ++} ++ ++static int smsdvb_set_frontend(struct dvb_frontend *fe) ++{ ++ struct smsdvb_client_t *client = ++ container_of(fe, struct smsdvb_client_t, frontend); ++ struct smscore_device_t *coredev = client->coredev; ++ ++ 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; ++ } ++} ++ ++static int smsdvb_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct smsdvb_client_t *client = ++ container_of(fe, struct smsdvb_client_t, frontend); ++ struct smscore_device_t *coredev = client->coredev; ++ struct TRANSMISSION_STATISTICS_S *td = ++ &client->sms_stat_dvb.TransmissionData; ++ ++ switch (smscore_get_device_mode(coredev)) { ++ case DEVICE_MODE_DVBT: ++ case DEVICE_MODE_DVBT_BDA: ++ fep->frequency = td->Frequency; ++ ++ switch (td->Bandwidth) { ++ case 6: ++ fep->bandwidth_hz = 6000000; ++ break; ++ case 7: ++ fep->bandwidth_hz = 7000000; ++ break; ++ case 8: ++ fep->bandwidth_hz = 8000000; ++ break; ++ } ++ ++ switch (td->TransmissionMode) { ++ case 2: ++ fep->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 8: ++ fep->transmission_mode = TRANSMISSION_MODE_8K; ++ } ++ ++ switch (td->GuardInterval) { ++ case 0: ++ fep->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ fep->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ fep->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ fep->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch (td->CodeRate) { ++ case 0: ++ fep->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ fep->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ fep->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ fep->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ fep->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ switch (td->LPCodeRate) { ++ case 0: ++ fep->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ fep->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ fep->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ fep->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ fep->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ switch (td->Constellation) { ++ case 0: ++ fep->modulation = QPSK; ++ break; ++ case 1: ++ fep->modulation = QAM_16; ++ break; ++ case 2: ++ fep->modulation = QAM_64; ++ break; ++ } ++ ++ switch (td->Hierarchy) { ++ case 0: ++ fep->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ fep->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ fep->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ fep->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ fep->inversion = INVERSION_AUTO; ++ break; ++ case DEVICE_MODE_ISDBT: ++ case DEVICE_MODE_ISDBT_BDA: ++ fep->frequency = td->Frequency; ++ fep->bandwidth_hz = 6000000; ++ /* todo: retrive the other parameters */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ 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) { ++ sms_err("kmalloc() failed"); ++ 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) { ++ sms_err("dvb_register_adapter() failed %d", rc); ++ goto adapter_error; ++ } ++ ++ /* 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) { ++ sms_err("dvb_dmx_init failed %d", 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) { ++ sms_err("dvb_dmxdev_init failed %d", 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) { ++ sms_err("frontend registration failed %d", 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, ¶ms, &client->smsclient); ++ if (rc < 0) { ++ sms_err("smscore_register_client() failed %d", rc); ++ goto client_error; ++ } ++ ++ client->coredev = coredev; ++ ++ init_completion(&client->tune_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_info("success"); ++ sms_board_setup(coredev); ++ ++ 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: ++ 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); ++ ++ rc = smscore_register_hotplug(smsdvb_hotplug); ++ ++ sms_debug(""); ++ ++ 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); ++ ++ 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/smsendian.c b/drivers/media/common/siano/smsendian.c +new file mode 100644 +index 0000000..e2657c2 +--- /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 . ++ ++ ****************************************************************/ ++ ++#include ++#include ++ ++#include "smsendian.h" ++#include "smscoreapi.h" ++ ++void smsendian_handle_tx_message(void *buffer) ++{ ++#ifdef __BIG_ENDIAN ++ struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; ++ int i; ++ int msgWords; ++ ++ switch (msg->xMsgHeader.msgType) { ++ case MSG_SMS_DATA_DOWNLOAD_REQ: ++ { ++ msg->msgData[0] = le32_to_cpu(msg->msgData[0]); ++ break; ++ } ++ ++ default: ++ msgWords = (msg->xMsgHeader.msgLength - ++ sizeof(struct SmsMsgHdr_ST))/4; ++ ++ for (i = 0; i < msgWords; i++) ++ msg->msgData[i] = le32_to_cpu(msg->msgData[i]); ++ ++ break; ++ } ++#endif /* __BIG_ENDIAN */ ++} ++EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); ++ ++void smsendian_handle_rx_message(void *buffer) ++{ ++#ifdef __BIG_ENDIAN ++ struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; ++ int i; ++ int msgWords; ++ ++ switch (msg->xMsgHeader.msgType) { ++ case MSG_SMS_GET_VERSION_EX_RES: ++ { ++ struct SmsVersionRes_ST *ver = ++ (struct SmsVersionRes_ST *) msg; ++ ver->ChipModel = le16_to_cpu(ver->ChipModel); ++ break; ++ } ++ ++ case MSG_SMS_DVBT_BDA_DATA: ++ case MSG_SMS_DAB_CHANNEL: ++ case MSG_SMS_DATA_MSG: ++ { ++ break; ++ } ++ ++ default: ++ { ++ msgWords = (msg->xMsgHeader.msgLength - ++ sizeof(struct SmsMsgHdr_ST))/4; ++ ++ for (i = 0; i < msgWords; i++) ++ msg->msgData[i] = le32_to_cpu(msg->msgData[i]); ++ ++ break; ++ } ++ } ++#endif /* __BIG_ENDIAN */ ++} ++EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); ++ ++void smsendian_handle_message_header(void *msg) ++{ ++#ifdef __BIG_ENDIAN ++ struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; ++ ++ phdr->msgType = le16_to_cpu(phdr->msgType); ++ phdr->msgLength = le16_to_cpu(phdr->msgLength); ++ phdr->msgFlags = le16_to_cpu(phdr->msgFlags); ++#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 0000000..1624d6f +--- /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 . ++ ++****************************************************************/ ++ ++#ifndef __SMS_ENDIAN_H__ ++#define __SMS_ENDIAN_H__ ++ ++#include ++ ++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 0000000..b8c5cad +--- /dev/null ++++ b/drivers/media/common/siano/smsir.c +@@ -0,0 +1,114 @@ ++/**************************************************************** ++ ++ 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 . ++ ++ ****************************************************************/ ++ ++ ++#include ++#include ++ ++#include "smscoreapi.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; ++ ++ sms_log("Allocating rc device"); ++ dev = rc_allocate_device(); ++ if (!dev) { ++ sms_err("Not enough memory"); ++ return -ENOMEM; ++ } ++ ++ coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ ++ coredev->ir.timeout = IR_DEFAULT_TIMEOUT; ++ sms_log("IR port %d, timeout %d ms", ++ 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_protos = RC_BIT_ALL; ++ dev->map_name = sms_get_board(board_id)->rc_codes; ++ dev->driver_name = MODULE_NAME; ++ ++ sms_log("Input device (IR) %s is set for key events", dev->input_name); ++ ++ err = rc_register_device(dev); ++ if (err < 0) { ++ sms_err("Failed to register device"); ++ rc_free_device(dev); ++ return err; ++ } ++ ++ coredev->ir.dev = dev; ++ return 0; ++} ++ ++void sms_ir_exit(struct smscore_device_t *coredev) ++{ ++ if (coredev->ir.dev) ++ rc_unregister_device(coredev->ir.dev); ++ ++ sms_log(""); ++} +diff --git a/drivers/media/common/siano/smsir.h b/drivers/media/common/siano/smsir.h +new file mode 100644 +index 0000000..69b59b9 +--- /dev/null ++++ b/drivers/media/common/siano/smsir.h +@@ -0,0 +1,64 @@ ++/**************************************************************** ++ ++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 . ++ ++****************************************************************/ ++ ++#ifndef __SMS_IR_H__ ++#define __SMS_IR_H__ ++ ++#include ++#include ++ ++#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; ++ u64 protocol; ++ ++ 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/tuners/Kconfig b/drivers/media/common/tuners/Kconfig +deleted file mode 100644 +index 4a6d5ce..0000000 +--- a/drivers/media/common/tuners/Kconfig ++++ /dev/null +@@ -1,214 +0,0 @@ +-config MEDIA_ATTACH +- bool "Load and attach frontend and tuner driver modules as needed" +- depends on VIDEO_MEDIA +- depends on MODULES +- help +- Remove the static dependency of DVB card drivers on all +- frontend modules for all possible card variants. Instead, +- allow the card drivers to only load the frontend modules +- they require. +- +- Also, tuner module will automatically load a tuner driver +- when needed, for analog mode. +- +- This saves several KBytes of memory. +- +- Note: You will need module-init-tools v3.2 or later for this feature. +- +- If unsure say Y. +- +-config MEDIA_TUNER +- tristate +- default VIDEO_MEDIA && I2C +- depends on VIDEO_MEDIA && I2C +- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE && EXPERIMENTAL +- select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE +- +-config MEDIA_TUNER_CUSTOMISE +- bool "Customize analog and hybrid tuner modules to build" +- depends on MEDIA_TUNER +- default y if EXPERT +- help +- This allows the user to deselect tuner drivers unnecessary +- for their hardware from the build. Use this option with care +- as deselecting tuner drivers which are in fact necessary will +- result in V4L/DVB devices which cannot be tuned due to lack of +- driver support +- +- If unsure say N. +- +-menu "Customize TV tuners" +- visible if MEDIA_TUNER_CUSTOMISE +- +-config MEDIA_TUNER_SIMPLE +- tristate "Simple tuner support" +- depends on VIDEO_MEDIA && I2C +- select MEDIA_TUNER_TDA9887 +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for various simple tuners. +- +-config MEDIA_TUNER_TDA8290 +- tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo" +- depends on VIDEO_MEDIA && I2C +- select MEDIA_TUNER_TDA827X +- select MEDIA_TUNER_TDA18271 +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for Philips TDA8290+8275(a) tuner. +- +-config MEDIA_TUNER_TDA827X +- tristate "Philips TDA827X silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A DVB-T silicon tuner module. Say Y when you want to support this tuner. +- +-config MEDIA_TUNER_TDA18271 +- tristate "NXP TDA18271 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A silicon tuner module. Say Y when you want to support this tuner. +- +-config MEDIA_TUNER_TDA9887 +- tristate "TDA 9885/6/7 analog IF demodulator" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for Philips TDA9885/6/7 +- analog IF demodulator. +- +-config MEDIA_TUNER_TEA5761 +- tristate "TEA 5761 radio tuner (EXPERIMENTAL)" +- depends on VIDEO_MEDIA && I2C +- depends on EXPERIMENTAL +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for the Philips TEA5761 radio tuner. +- +-config MEDIA_TUNER_TEA5767 +- tristate "TEA 5767 radio tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for the Philips TEA5767 radio tuner. +- +-config MEDIA_TUNER_MT20XX +- tristate "Microtune 2032 / 2050 tuners" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for the MT2032 / MT2050 tuner. +- +-config MEDIA_TUNER_MT2060 +- tristate "Microtune MT2060 silicon IF tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon IF tuner MT2060 from Microtune. +- +-config MEDIA_TUNER_MT2063 +- tristate "Microtune MT2063 silicon IF tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon IF tuner MT2063 from Microtune. +- +-config MEDIA_TUNER_MT2266 +- tristate "Microtune MT2266 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon baseband tuner MT2266 from Microtune. +- +-config MEDIA_TUNER_MT2131 +- tristate "Microtune MT2131 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon baseband tuner MT2131 from Microtune. +- +-config MEDIA_TUNER_QT1010 +- tristate "Quantek QT1010 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner QT1010 from Quantek. +- +-config MEDIA_TUNER_XC2028 +- tristate "XCeive xc2028/xc3028 tuners" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to include support for the xc2028/xc3028 tuners. +- +-config MEDIA_TUNER_XC5000 +- tristate "Xceive XC5000 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner XC5000 from Xceive. +- This device is only used inside a SiP called together with a +- demodulator for now. +- +-config MEDIA_TUNER_XC4000 +- tristate "Xceive XC4000 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner XC4000 from Xceive. +- This device is only used inside a SiP called together with a +- demodulator for now. +- +-config MEDIA_TUNER_MXL5005S +- tristate "MaxLinear MSL5005S silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner MXL5005S from MaxLinear. +- +-config MEDIA_TUNER_MXL5007T +- tristate "MaxLinear MxL5007T silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner MxL5007T from MaxLinear. +- +-config MEDIA_TUNER_MC44S803 +- tristate "Freescale MC44S803 Low Power CMOS Broadband tuners" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Freescale MC44S803 based tuners +- +-config MEDIA_TUNER_MAX2165 +- tristate "Maxim MAX2165 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- A driver for the silicon tuner MAX2165 from Maxim. +- +-config MEDIA_TUNER_TDA18218 +- tristate "NXP TDA18218 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- NXP TDA18218 silicon tuner driver. +- +-config MEDIA_TUNER_TDA18212 +- tristate "NXP TDA18212 silicon tuner" +- depends on VIDEO_MEDIA && I2C +- default m if MEDIA_TUNER_CUSTOMISE +- help +- NXP TDA18212 silicon tuner driver. +- +-endmenu +diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile +deleted file mode 100644 +index 8295854..0000000 +--- a/drivers/media/common/tuners/Makefile ++++ /dev/null +@@ -1,33 +0,0 @@ +-# +-# Makefile for common V4L/DVB tuners +-# +- +-tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o +- +-obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o +-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o +-# tuner-types will be merged into tuner-simple, in the future +-obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o +-obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o +-obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o +-obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o +-obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o +-obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o +-obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o +-obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o +-obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o +-obj-$(CONFIG_MEDIA_TUNER_XC4000) += xc4000.o +-obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o +-obj-$(CONFIG_MEDIA_TUNER_MT2063) += mt2063.o +-obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o +-obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o +-obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o +-obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o +-obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o +-obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +-obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o +-obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o +-obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core +-ccflags-y += -Idrivers/media/dvb/frontends +diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c +deleted file mode 100644 +index cb2c98f..0000000 +--- a/drivers/media/common/tuners/max2165.c ++++ /dev/null +@@ -1,430 +0,0 @@ +-/* +- * Driver for Maxim MAX2165 silicon tuner +- * +- * Copyright (c) 2009 David T. L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "max2165.h" +-#include "max2165_priv.h" +-#include "tuner-i2c.h" +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "max2165: " args); \ +- } while (0) +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; +- +- msg.addr = priv->config->i2c_address; +- +- if (debug >= 2) +- dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); +- +- ret = i2c_transfer(priv->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", +- __func__, reg, data, ret); +- +- return (ret != 1) ? -EIO : 0; +-} +- +-static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +-{ +- int ret; +- u8 dev_addr = priv->config->i2c_address; +- +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret != 2) { +- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); +- return -EIO; +- } +- +- *p_data = b1[0]; +- if (debug >= 2) +- dprintk("%s: reg=0x%02X, data=0x%02X\n", +- __func__, reg, b1[0]); +- return 0; +-} +- +-static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, +- u8 mask, u8 data) +-{ +- int ret; +- u8 v; +- +- data &= mask; +- ret = max2165_read_reg(priv, reg, &v); +- if (ret != 0) +- return ret; +- v &= ~mask; +- v |= data; +- ret = max2165_write_reg(priv, reg, v); +- +- return ret; +-} +- +-static int max2165_read_rom_table(struct max2165_priv *priv) +-{ +- u8 dat[3]; +- int i; +- +- for (i = 0; i < 3; i++) { +- max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); +- max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); +- } +- +- priv->tf_ntch_low_cfg = dat[0] >> 4; +- priv->tf_ntch_hi_cfg = dat[0] & 0x0F; +- priv->tf_balun_low_ref = dat[1] & 0x0F; +- priv->tf_balun_hi_ref = dat[1] >> 4; +- priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; +- priv->bb_filter_8mhz_cfg = dat[2] >> 4; +- +- dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); +- dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); +- dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); +- dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); +- dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); +- dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); +- +- return 0; +-} +- +-static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +-{ +- u8 v; +- +- v = (osc / 2); +- if (v == 2) +- v = 0x7; +- else +- v -= 8; +- +- max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); +- +- return 0; +-} +- +-static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +-{ +- u8 val; +- +- if (bw == 8000000) +- val = priv->bb_filter_8mhz_cfg; +- else +- val = priv->bb_filter_7mhz_cfg; +- +- max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); +- +- return 0; +-} +- +-int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +-{ +- u32 remainder; +- u32 q, f = 0; +- int i; +- +- if (0 == divisor) +- return -1; +- +- q = dividend / divisor; +- remainder = dividend - q * divisor; +- +- for (i = 0; i < 31; i++) { +- remainder <<= 1; +- if (remainder >= divisor) { +- f += 1; +- remainder -= divisor; +- } +- f <<= 1; +- } +- +- *quotient = q; +- *fraction = f; +- +- return 0; +-} +- +-static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +-{ +- u8 tf; +- u8 tf_ntch; +- u32 t; +- u32 quotient, fraction; +- +- /* Set PLL divider according to RF frequency */ +- fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, +- "ient, &fraction); +- +- /* 20-bit fraction */ +- fraction >>= 12; +- +- max2165_write_reg(priv, REG_NDIV_INT, quotient); +- max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); +- max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); +- max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); +- +- /* Norch Filter */ +- tf_ntch = (freq < 725000000) ? +- priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; +- +- /* Tracking filter balun */ +- t = priv->tf_balun_low_ref; +- t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) +- * (freq / 1000 - 470000) / (780000 - 470000); +- +- tf = t; +- dprintk("tf = %X\n", tf); +- tf |= tf_ntch << 4; +- +- max2165_write_reg(priv, REG_TRACK_FILTER, tf); +- +- return 0; +-} +- +-static void max2165_debug_status(struct max2165_priv *priv) +-{ +- u8 status, autotune; +- u8 auto_vco_success, auto_vco_active; +- u8 pll_locked; +- u8 dc_offset_low, dc_offset_hi; +- u8 signal_lv_over_threshold; +- u8 vco, vco_sub_band, adc; +- +- max2165_read_reg(priv, REG_STATUS, &status); +- max2165_read_reg(priv, REG_AUTOTUNE, &autotune); +- +- auto_vco_success = (status >> 6) & 0x01; +- auto_vco_active = (status >> 5) & 0x01; +- pll_locked = (status >> 4) & 0x01; +- dc_offset_low = (status >> 3) & 0x01; +- dc_offset_hi = (status >> 2) & 0x01; +- signal_lv_over_threshold = status & 0x01; +- +- vco = autotune >> 6; +- vco_sub_band = (autotune >> 3) & 0x7; +- adc = autotune & 0x7; +- +- dprintk("auto VCO active: %d, auto VCO success: %d\n", +- auto_vco_active, auto_vco_success); +- dprintk("PLL locked: %d\n", pll_locked); +- dprintk("DC offset low: %d, DC offset high: %d\n", +- dc_offset_low, dc_offset_hi); +- dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); +- dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +-} +- +-static int max2165_set_params(struct dvb_frontend *fe) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret; +- +- switch (c->bandwidth_hz) { +- case 7000000: +- case 8000000: +- priv->frequency = c->frequency; +- break; +- default: +- printk(KERN_INFO "MAX2165: bandwidth %d Hz not supported.\n", +- c->bandwidth_hz); +- return -EINVAL; +- } +- +- dprintk("%s() frequency=%d\n", __func__, c->frequency); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- max2165_set_bandwidth(priv, c->bandwidth_hz); +- ret = max2165_set_rf(priv, priv->frequency); +- mdelay(50); +- max2165_debug_status(priv); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret != 0) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- dprintk("%s()\n", __func__); +- *freq = priv->frequency; +- return 0; +-} +- +-static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- dprintk("%s()\n", __func__); +- +- *bw = priv->bandwidth; +- return 0; +-} +- +-static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- u16 lock_status = 0; +- +- dprintk("%s()\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- max2165_debug_status(priv); +- *status = lock_status; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int max2165_sleep(struct dvb_frontend *fe) +-{ +- dprintk("%s()\n", __func__); +- return 0; +-} +- +-static int max2165_init(struct dvb_frontend *fe) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- dprintk("%s()\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- /* Setup initial values */ +- /* Fractional Mode on */ +- max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); +- /* LNA on */ +- max2165_write_reg(priv, REG_LNA, 0x01); +- max2165_write_reg(priv, REG_PLL_CFG, 0x7A); +- max2165_write_reg(priv, REG_TEST, 0x08); +- max2165_write_reg(priv, REG_SHUTDOWN, 0x40); +- max2165_write_reg(priv, REG_VCO_CTRL, 0x84); +- max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); +- max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); +- max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); +- max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); +- +- max2165_set_osc(priv, priv->config->osc_clk); +- +- max2165_read_rom_table(priv); +- +- max2165_set_bandwidth(priv, 8000000); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int max2165_release(struct dvb_frontend *fe) +-{ +- struct max2165_priv *priv = fe->tuner_priv; +- dprintk("%s()\n", __func__); +- +- kfree(priv); +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static const struct dvb_tuner_ops max2165_tuner_ops = { +- .info = { +- .name = "Maxim MAX2165", +- .frequency_min = 470000000, +- .frequency_max = 780000000, +- .frequency_step = 50000, +- }, +- +- .release = max2165_release, +- .init = max2165_init, +- .sleep = max2165_sleep, +- +- .set_params = max2165_set_params, +- .set_analog_params = NULL, +- .get_frequency = max2165_get_frequency, +- .get_bandwidth = max2165_get_bandwidth, +- .get_status = max2165_get_status +-}; +- +-struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct max2165_config *cfg) +-{ +- struct max2165_priv *priv = NULL; +- +- dprintk("%s(%d-%04x)\n", __func__, +- i2c ? i2c_adapter_id(i2c) : -1, +- cfg ? cfg->i2c_address : -1); +- +- priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- priv->config = cfg; +- priv->i2c = i2c; +- fe->tuner_priv = priv; +- +- max2165_init(fe); +- max2165_debug_status(priv); +- +- return fe; +-} +-EXPORT_SYMBOL(max2165_attach); +- +-MODULE_AUTHOR("David T. L. Wong "); +-MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h +deleted file mode 100644 +index c063c36..0000000 +--- a/drivers/media/common/tuners/max2165.h ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- * Driver for Maxim MAX2165 silicon tuner +- * +- * Copyright (c) 2009 David T. L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MAX2165_H__ +-#define __MAX2165_H__ +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct max2165_config { +- u8 i2c_address; +- u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ +- (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct max2165_config *cfg); +-#else +-static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct max2165_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h +deleted file mode 100644 +index 91bbe02..0000000 +--- a/drivers/media/common/tuners/max2165_priv.h ++++ /dev/null +@@ -1,60 +0,0 @@ +-/* +- * Driver for Maxim MAX2165 silicon tuner +- * +- * Copyright (c) 2009 David T. L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MAX2165_PRIV_H__ +-#define __MAX2165_PRIV_H__ +- +-#define REG_NDIV_INT 0x00 +-#define REG_NDIV_FRAC2 0x01 +-#define REG_NDIV_FRAC1 0x02 +-#define REG_NDIV_FRAC0 0x03 +-#define REG_TRACK_FILTER 0x04 +-#define REG_LNA 0x05 +-#define REG_PLL_CFG 0x06 +-#define REG_TEST 0x07 +-#define REG_SHUTDOWN 0x08 +-#define REG_VCO_CTRL 0x09 +-#define REG_BASEBAND_CTRL 0x0A +-#define REG_DC_OFFSET_CTRL 0x0B +-#define REG_DC_OFFSET_DAC 0x0C +-#define REG_ROM_TABLE_ADDR 0x0D +- +-/* Read Only Registers */ +-#define REG_ROM_TABLE_DATA 0x10 +-#define REG_STATUS 0x11 +-#define REG_AUTOTUNE 0x12 +- +-struct max2165_priv { +- struct max2165_config *config; +- struct i2c_adapter *i2c; +- +- u32 frequency; +- u32 bandwidth; +- +- u8 tf_ntch_low_cfg; +- u8 tf_ntch_hi_cfg; +- u8 tf_balun_low_ref; +- u8 tf_balun_hi_ref; +- u8 bb_filter_7mhz_cfg; +- u8 bb_filter_8mhz_cfg; +-}; +- +-#endif +diff --git a/drivers/media/common/tuners/mc44s803.c b/drivers/media/common/tuners/mc44s803.c +deleted file mode 100644 +index 5ddce7e..0000000 +--- a/drivers/media/common/tuners/mc44s803.c ++++ /dev/null +@@ -1,372 +0,0 @@ +-/* +- * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner +- * +- * Copyright (c) 2009 Jochen Friedrich +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "mc44s803.h" +-#include "mc44s803_priv.h" +- +-#define mc_printk(level, format, arg...) \ +- printk(level "mc44s803: " format , ## arg) +- +-/* Writes a single register */ +-static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val) +-{ +- u8 buf[3]; +- struct i2c_msg msg = { +- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3 +- }; +- +- buf[0] = (val & 0xff0000) >> 16; +- buf[1] = (val & 0xff00) >> 8; +- buf[2] = (val & 0xff); +- +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- mc_printk(KERN_WARNING, "I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* Reads a single register */ +-static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val) +-{ +- u32 wval; +- u8 buf[3]; +- int ret; +- struct i2c_msg msg[] = { +- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, +- .buf = buf, .len = 3 }, +- }; +- +- wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) | +- MC44S803_REG_SM(reg, MC44S803_D); +- +- ret = mc44s803_writereg(priv, wval); +- if (ret) +- return ret; +- +- if (i2c_transfer(priv->i2c, msg, 1) != 1) { +- mc_printk(KERN_WARNING, "I2C read failed\n"); +- return -EREMOTEIO; +- } +- +- *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; +- +- return 0; +-} +- +-static int mc44s803_release(struct dvb_frontend *fe) +-{ +- struct mc44s803_priv *priv = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(priv); +- +- return 0; +-} +- +-static int mc44s803_init(struct dvb_frontend *fe) +-{ +- struct mc44s803_priv *priv = fe->tuner_priv; +- u32 val; +- int err; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +-/* Reset chip */ +- val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_RS); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +-/* Power Up and Start Osc */ +- +- val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | +- MC44S803_REG_SM(0xC0, MC44S803_REFOSC) | +- MC44S803_REG_SM(1, MC44S803_OSCSEL); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) | +- MC44S803_REG_SM(0x200, MC44S803_POWER); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- msleep(10); +- +- val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) | +- MC44S803_REG_SM(0x40, MC44S803_REFOSC) | +- MC44S803_REG_SM(1, MC44S803_OSCSEL); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- msleep(20); +- +-/* Setup Mixer */ +- +- val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_TRI_STATE) | +- MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +-/* Setup Cirquit Adjust */ +- +- val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_G1) | +- MC44S803_REG_SM(1, MC44S803_G3) | +- MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | +- MC44S803_REG_SM(1, MC44S803_G6) | +- MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | +- MC44S803_REG_SM(0x3, MC44S803_LP) | +- MC44S803_REG_SM(1, MC44S803_CLRF) | +- MC44S803_REG_SM(1, MC44S803_CLIF); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_G1) | +- MC44S803_REG_SM(1, MC44S803_G3) | +- MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) | +- MC44S803_REG_SM(1, MC44S803_G6) | +- MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) | +- MC44S803_REG_SM(0x3, MC44S803_LP); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +-/* Setup Digtune */ +- +- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | +- MC44S803_REG_SM(3, MC44S803_XOD); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +-/* Setup AGC */ +- +- val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_AT1) | +- MC44S803_REG_SM(1, MC44S803_AT2) | +- MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) | +- MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) | +- MC44S803_REG_SM(1, MC44S803_LNA0); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- return 0; +- +-exit: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- mc_printk(KERN_WARNING, "I/O Error\n"); +- return err; +-} +- +-static int mc44s803_set_params(struct dvb_frontend *fe) +-{ +- struct mc44s803_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 r1, r2, n1, n2, lo1, lo2, freq, val; +- int err; +- +- priv->frequency = c->frequency; +- +- r1 = MC44S803_OSC / 1000000; +- r2 = MC44S803_OSC / 100000; +- +- n1 = (c->frequency + MC44S803_IF1 + 500000) / 1000000; +- freq = MC44S803_OSC / r1 * n1; +- lo1 = ((60 * n1) + (r1 / 2)) / r1; +- freq = freq - c->frequency; +- +- n2 = (freq - MC44S803_IF2 + 50000) / 100000; +- lo2 = ((60 * n2) + (r2 / 2)) / r2; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) | +- MC44S803_REG_SM(r1-1, MC44S803_R1) | +- MC44S803_REG_SM(r2-1, MC44S803_R2) | +- MC44S803_REG_SM(1, MC44S803_REFBUF_EN); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) | +- MC44S803_REG_SM(n1-2, MC44S803_LO1); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) | +- MC44S803_REG_SM(n2-2, MC44S803_LO2); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | +- MC44S803_REG_SM(1, MC44S803_DA) | +- MC44S803_REG_SM(lo1, MC44S803_LO_REF) | +- MC44S803_REG_SM(1, MC44S803_AT); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) | +- MC44S803_REG_SM(2, MC44S803_DA) | +- MC44S803_REG_SM(lo2, MC44S803_LO_REF) | +- MC44S803_REG_SM(1, MC44S803_AT); +- +- err = mc44s803_writereg(priv, val); +- if (err) +- goto exit; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +- +-exit: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- mc_printk(KERN_WARNING, "I/O Error\n"); +- return err; +-} +- +-static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mc44s803_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static const struct dvb_tuner_ops mc44s803_tuner_ops = { +- .info = { +- .name = "Freescale MC44S803", +- .frequency_min = 48000000, +- .frequency_max = 1000000000, +- .frequency_step = 100000, +- }, +- +- .release = mc44s803_release, +- .init = mc44s803_init, +- .set_params = mc44s803_set_params, +- .get_frequency = mc44s803_get_frequency +-}; +- +-/* This functions tries to identify a MC44S803 tuner by reading the ID +- register. This is hasty. */ +-struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct mc44s803_config *cfg) +-{ +- struct mc44s803_priv *priv; +- u32 reg; +- u8 id; +- int ret; +- +- reg = 0; +- +- priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- priv->fe = fe; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = mc44s803_readreg(priv, MC44S803_REG_ID, ®); +- if (ret) +- goto error; +- +- id = MC44S803_REG_MS(reg, MC44S803_ID); +- +- if (id != 0x14) { +- mc_printk(KERN_ERR, "unsupported ID " +- "(%x should be 0x14)\n", id); +- goto error; +- } +- +- mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id); +- memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return fe; +- +-error: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- kfree(priv); +- return NULL; +-} +-EXPORT_SYMBOL(mc44s803_attach); +- +-MODULE_AUTHOR("Jochen Friedrich"); +-MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/mc44s803.h b/drivers/media/common/tuners/mc44s803.h +deleted file mode 100644 +index 34f3892..0000000 +--- a/drivers/media/common/tuners/mc44s803.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner +- * +- * Copyright (c) 2009 Jochen Friedrich +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef MC44S803_H +-#define MC44S803_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct mc44s803_config { +- u8 i2c_address; +- u8 dig_out; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MC44S803) || \ +- (defined(CONFIG_MEDIA_TUNER_MC44S803_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct mc44s803_config *cfg); +-#else +-static inline struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct mc44s803_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_MEDIA_TUNER_MC44S803 */ +- +-#endif +diff --git a/drivers/media/common/tuners/mc44s803_priv.h b/drivers/media/common/tuners/mc44s803_priv.h +deleted file mode 100644 +index 14a9278..0000000 +--- a/drivers/media/common/tuners/mc44s803_priv.h ++++ /dev/null +@@ -1,208 +0,0 @@ +-/* +- * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner +- * +- * Copyright (c) 2009 Jochen Friedrich +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef MC44S803_PRIV_H +-#define MC44S803_PRIV_H +- +-/* This driver is based on the information available in the datasheet +- http://www.freescale.com/files/rf_if/doc/data_sheet/MC44S803.pdf +- +- SPI or I2C Address : 0xc0-0xc6 +- +- Reg.No | Function +- ------------------------------------------- +- 00 | Power Down +- 01 | Reference Oszillator +- 02 | Reference Dividers +- 03 | Mixer and Reference Buffer +- 04 | Reset/Serial Out +- 05 | LO 1 +- 06 | LO 2 +- 07 | Circuit Adjust +- 08 | Test +- 09 | Digital Tune +- 0A | LNA AGC +- 0B | Data Register Address +- 0C | Regulator Test +- 0D | VCO Test +- 0E | LNA Gain/Input Power +- 0F | ID Bits +- +-*/ +- +-#define MC44S803_OSC 26000000 /* 26 MHz */ +-#define MC44S803_IF1 1086000000 /* 1086 MHz */ +-#define MC44S803_IF2 36125000 /* 36.125 MHz */ +- +-#define MC44S803_REG_POWER 0 +-#define MC44S803_REG_REFOSC 1 +-#define MC44S803_REG_REFDIV 2 +-#define MC44S803_REG_MIXER 3 +-#define MC44S803_REG_RESET 4 +-#define MC44S803_REG_LO1 5 +-#define MC44S803_REG_LO2 6 +-#define MC44S803_REG_CIRCADJ 7 +-#define MC44S803_REG_TEST 8 +-#define MC44S803_REG_DIGTUNE 9 +-#define MC44S803_REG_LNAAGC 0x0A +-#define MC44S803_REG_DATAREG 0x0B +-#define MC44S803_REG_REGTEST 0x0C +-#define MC44S803_REG_VCOTEST 0x0D +-#define MC44S803_REG_LNAGAIN 0x0E +-#define MC44S803_REG_ID 0x0F +- +-/* Register definitions */ +-#define MC44S803_ADDR 0x0F +-#define MC44S803_ADDR_S 0 +-/* REG_POWER */ +-#define MC44S803_POWER 0xFFFFF0 +-#define MC44S803_POWER_S 4 +-/* REG_REFOSC */ +-#define MC44S803_REFOSC 0x1FF0 +-#define MC44S803_REFOSC_S 4 +-#define MC44S803_OSCSEL 0x2000 +-#define MC44S803_OSCSEL_S 13 +-/* REG_REFDIV */ +-#define MC44S803_R2 0x1FF0 +-#define MC44S803_R2_S 4 +-#define MC44S803_REFBUF_EN 0x2000 +-#define MC44S803_REFBUF_EN_S 13 +-#define MC44S803_R1 0x7C000 +-#define MC44S803_R1_S 14 +-/* REG_MIXER */ +-#define MC44S803_R3 0x70 +-#define MC44S803_R3_S 4 +-#define MC44S803_MUX3 0x80 +-#define MC44S803_MUX3_S 7 +-#define MC44S803_MUX4 0x100 +-#define MC44S803_MUX4_S 8 +-#define MC44S803_OSC_SCR 0x200 +-#define MC44S803_OSC_SCR_S 9 +-#define MC44S803_TRI_STATE 0x400 +-#define MC44S803_TRI_STATE_S 10 +-#define MC44S803_BUF_GAIN 0x800 +-#define MC44S803_BUF_GAIN_S 11 +-#define MC44S803_BUF_IO 0x1000 +-#define MC44S803_BUF_IO_S 12 +-#define MC44S803_MIXER_RES 0xFE000 +-#define MC44S803_MIXER_RES_S 13 +-/* REG_RESET */ +-#define MC44S803_RS 0x10 +-#define MC44S803_RS_S 4 +-#define MC44S803_SO 0x20 +-#define MC44S803_SO_S 5 +-/* REG_LO1 */ +-#define MC44S803_LO1 0xFFF0 +-#define MC44S803_LO1_S 4 +-/* REG_LO2 */ +-#define MC44S803_LO2 0x7FFF0 +-#define MC44S803_LO2_S 4 +-/* REG_CIRCADJ */ +-#define MC44S803_G1 0x20 +-#define MC44S803_G1_S 5 +-#define MC44S803_G3 0x80 +-#define MC44S803_G3_S 7 +-#define MC44S803_CIRCADJ_RES 0x300 +-#define MC44S803_CIRCADJ_RES_S 8 +-#define MC44S803_G6 0x400 +-#define MC44S803_G6_S 10 +-#define MC44S803_G7 0x800 +-#define MC44S803_G7_S 11 +-#define MC44S803_S1 0x1000 +-#define MC44S803_S1_S 12 +-#define MC44S803_LP 0x7E000 +-#define MC44S803_LP_S 13 +-#define MC44S803_CLRF 0x80000 +-#define MC44S803_CLRF_S 19 +-#define MC44S803_CLIF 0x100000 +-#define MC44S803_CLIF_S 20 +-/* REG_TEST */ +-/* REG_DIGTUNE */ +-#define MC44S803_DA 0xF0 +-#define MC44S803_DA_S 4 +-#define MC44S803_XOD 0x300 +-#define MC44S803_XOD_S 8 +-#define MC44S803_RST 0x10000 +-#define MC44S803_RST_S 16 +-#define MC44S803_LO_REF 0x1FFF00 +-#define MC44S803_LO_REF_S 8 +-#define MC44S803_AT 0x200000 +-#define MC44S803_AT_S 21 +-#define MC44S803_MT 0x400000 +-#define MC44S803_MT_S 22 +-/* REG_LNAAGC */ +-#define MC44S803_G 0x3F0 +-#define MC44S803_G_S 4 +-#define MC44S803_AT1 0x400 +-#define MC44S803_AT1_S 10 +-#define MC44S803_AT2 0x800 +-#define MC44S803_AT2_S 11 +-#define MC44S803_HL_GR_EN 0x8000 +-#define MC44S803_HL_GR_EN_S 15 +-#define MC44S803_AGC_AN_DIG 0x10000 +-#define MC44S803_AGC_AN_DIG_S 16 +-#define MC44S803_ATTEN_EN 0x20000 +-#define MC44S803_ATTEN_EN_S 17 +-#define MC44S803_AGC_READ_EN 0x40000 +-#define MC44S803_AGC_READ_EN_S 18 +-#define MC44S803_LNA0 0x80000 +-#define MC44S803_LNA0_S 19 +-#define MC44S803_AGC_SEL 0x100000 +-#define MC44S803_AGC_SEL_S 20 +-#define MC44S803_AT0 0x200000 +-#define MC44S803_AT0_S 21 +-#define MC44S803_B 0xC00000 +-#define MC44S803_B_S 22 +-/* REG_DATAREG */ +-#define MC44S803_D 0xF0 +-#define MC44S803_D_S 4 +-/* REG_REGTEST */ +-/* REG_VCOTEST */ +-/* REG_LNAGAIN */ +-#define MC44S803_IF_PWR 0x700 +-#define MC44S803_IF_PWR_S 8 +-#define MC44S803_RF_PWR 0x3800 +-#define MC44S803_RF_PWR_S 11 +-#define MC44S803_LNA_GAIN 0xFC000 +-#define MC44S803_LNA_GAIN_S 14 +-/* REG_ID */ +-#define MC44S803_ID 0x3E00 +-#define MC44S803_ID_S 9 +- +-/* Some macros to read/write fields */ +- +-/* First shift, then mask */ +-#define MC44S803_REG_SM(_val, _reg) \ +- (((_val) << _reg##_S) & (_reg)) +- +-/* First mask, then shift */ +-#define MC44S803_REG_MS(_val, _reg) \ +- (((_val) & (_reg)) >> _reg##_S) +- +-struct mc44s803_priv { +- struct mc44s803_config *cfg; +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe; +- +- u32 frequency; +-}; +- +-#endif +diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/common/tuners/mt2060.c +deleted file mode 100644 +index 13381de..0000000 +--- a/drivers/media/common/tuners/mt2060.c ++++ /dev/null +@@ -1,403 +0,0 @@ +-/* +- * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" +- * +- * Copyright (c) 2006 Olivier DANET +- * +- * 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.= +- */ +- +-/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "mt2060.h" +-#include "mt2060_priv.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0) +- +-// Reads a single register +-static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val) +-{ +- struct i2c_msg msg[2] = { +- { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, +- }; +- +- if (i2c_transfer(priv->i2c, msg, 2) != 2) { +- printk(KERN_WARNING "mt2060 I2C read failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Writes a single register +-static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val) +-{ +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { +- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 +- }; +- +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mt2060 I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Writes a set of consecutive registers +-static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len) +-{ +- struct i2c_msg msg = { +- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len +- }; +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Initialisation sequences +-// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49 +-static u8 mt2060_config1[] = { +- REG_LO1C1, +- 0x3F, 0x74, 0x00, 0x08, 0x93 +-}; +- +-// FMCG=2, GP2=0, GP1=0 +-static u8 mt2060_config2[] = { +- REG_MISC_CTRL, +- 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42 +-}; +- +-// VGAG=3, V1CSE=1 +- +-#ifdef MT2060_SPURCHECK +-/* The function below calculates the frequency offset between the output frequency if2 +- and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */ +-static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2) +-{ +- int I,J; +- int dia,diamin,diff; +- diamin=1000000; +- for (I = 1; I < 10; I++) { +- J = ((2*I*lo1)/lo2+1)/2; +- diff = I*(int)lo1-J*(int)lo2; +- if (diff < 0) diff=-diff; +- dia = (diff-(int)if2); +- if (dia < 0) dia=-dia; +- if (diamin > dia) diamin=dia; +- } +- return diamin; +-} +- +-#define BANDWIDTH 4000 // kHz +- +-/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */ +-static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2) +-{ +- u32 Spur,Sp1,Sp2; +- int I,J; +- I=0; +- J=1000; +- +- Spur=mt2060_spurcalc(lo1,lo2,if2); +- if (Spur < BANDWIDTH) { +- /* Potential spurs detected */ +- dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)", +- (int)lo1,(int)lo2); +- I=1000; +- Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2); +- Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2); +- +- if (Sp1 < Sp2) { +- J=-J; I=-I; Spur=Sp2; +- } else +- Spur=Sp1; +- +- while (Spur < BANDWIDTH) { +- I += J; +- Spur = mt2060_spurcalc(lo1+I,lo2+I,if2); +- } +- dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)", +- (int)(lo1+I),(int)(lo2+I)); +- } +- return I; +-} +-#endif +- +-#define IF2 36150 // IF2 frequency = 36.150 MHz +-#define FREF 16000 // Quartz oscillator 16 MHz +- +-static int mt2060_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct mt2060_priv *priv; +- int ret=0; +- int i=0; +- u32 freq; +- u8 lnaband; +- u32 f_lo1,f_lo2; +- u32 div1,num1,div2,num2; +- u8 b[8]; +- u32 if1; +- +- priv = fe->tuner_priv; +- +- if1 = priv->if1_freq; +- b[0] = REG_LO1B1; +- b[1] = 0xFF; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- mt2060_writeregs(priv,b,2); +- +- freq = c->frequency / 1000; /* Hz -> kHz */ +- +- f_lo1 = freq + if1 * 1000; +- f_lo1 = (f_lo1 / 250) * 250; +- f_lo2 = f_lo1 - freq - IF2; +- // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise +- f_lo2 = ((f_lo2 + 25) / 50) * 50; +- priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000, +- +-#ifdef MT2060_SPURCHECK +- // LO-related spurs detection and correction +- num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2); +- f_lo1 += num1; +- f_lo2 += num1; +-#endif +- //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 ) +- num1 = f_lo1 / (FREF / 64); +- div1 = num1 / 64; +- num1 &= 0x3f; +- +- // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) +- num2 = f_lo2 * 64 / (FREF / 128); +- div2 = num2 / 8192; +- num2 &= 0x1fff; +- +- if (freq <= 95000) lnaband = 0xB0; else +- if (freq <= 180000) lnaband = 0xA0; else +- if (freq <= 260000) lnaband = 0x90; else +- if (freq <= 335000) lnaband = 0x80; else +- if (freq <= 425000) lnaband = 0x70; else +- if (freq <= 480000) lnaband = 0x60; else +- if (freq <= 570000) lnaband = 0x50; else +- if (freq <= 645000) lnaband = 0x40; else +- if (freq <= 730000) lnaband = 0x30; else +- if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10; +- +- b[0] = REG_LO1C1; +- b[1] = lnaband | ((num1 >>2) & 0x0F); +- b[2] = div1; +- b[3] = (num2 & 0x0F) | ((num1 & 3) << 4); +- b[4] = num2 >> 4; +- b[5] = ((num2 >>12) & 1) | (div2 << 1); +- +- dprintk("IF1: %dMHz",(int)if1); +- dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2); +- dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2); +- dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]); +- +- mt2060_writeregs(priv,b,6); +- +- //Waits for pll lock or timeout +- i = 0; +- do { +- mt2060_readreg(priv,REG_LO_STATUS,b); +- if ((b[0] & 0x88)==0x88) +- break; +- msleep(4); +- i++; +- } while (i<10); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-static void mt2060_calibrate(struct mt2060_priv *priv) +-{ +- u8 b = 0; +- int i = 0; +- +- if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1))) +- return; +- if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2))) +- return; +- +- /* initialize the clock output */ +- mt2060_writereg(priv, REG_VGAG, (priv->cfg->clock_out << 6) | 0x30); +- +- do { +- b |= (1 << 6); // FM1SS; +- mt2060_writereg(priv, REG_LO2C1,b); +- msleep(20); +- +- if (i == 0) { +- b |= (1 << 7); // FM1CA; +- mt2060_writereg(priv, REG_LO2C1,b); +- b &= ~(1 << 7); // FM1CA; +- msleep(20); +- } +- +- b &= ~(1 << 6); // FM1SS +- mt2060_writereg(priv, REG_LO2C1,b); +- +- msleep(20); +- i++; +- } while (i < 9); +- +- i = 0; +- while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0) +- msleep(20); +- +- if (i <= 10) { +- mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :) +- dprintk("calibration was successful: %d", (int)priv->fmfreq); +- } else +- dprintk("FMCAL timed out"); +-} +- +-static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mt2060_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int mt2060_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- *frequency = IF2 * 1000; +- return 0; +-} +- +-static int mt2060_init(struct dvb_frontend *fe) +-{ +- struct mt2060_priv *priv = fe->tuner_priv; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = mt2060_writereg(priv, REG_VGAG, +- (priv->cfg->clock_out << 6) | 0x33); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-static int mt2060_sleep(struct dvb_frontend *fe) +-{ +- struct mt2060_priv *priv = fe->tuner_priv; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = mt2060_writereg(priv, REG_VGAG, +- (priv->cfg->clock_out << 6) | 0x30); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-static int mt2060_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops mt2060_tuner_ops = { +- .info = { +- .name = "Microtune MT2060", +- .frequency_min = 48000000, +- .frequency_max = 860000000, +- .frequency_step = 50000, +- }, +- +- .release = mt2060_release, +- +- .init = mt2060_init, +- .sleep = mt2060_sleep, +- +- .set_params = mt2060_set_params, +- .get_frequency = mt2060_get_frequency, +- .get_if_frequency = mt2060_get_if_frequency, +-}; +- +-/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ +-struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) +-{ +- struct mt2060_priv *priv = NULL; +- u8 id = 0; +- +- priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- priv->if1_freq = if1; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) { +- kfree(priv); +- return NULL; +- } +- +- if (id != PART_REV) { +- kfree(priv); +- return NULL; +- } +- printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1); +- memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- +- mt2060_calibrate(priv); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return fe; +-} +-EXPORT_SYMBOL(mt2060_attach); +- +-MODULE_AUTHOR("Olivier DANET"); +-MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/mt2060.h b/drivers/media/common/tuners/mt2060.h +deleted file mode 100644 +index cb60caf..0000000 +--- a/drivers/media/common/tuners/mt2060.h ++++ /dev/null +@@ -1,43 +0,0 @@ +-/* +- * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" +- * +- * Copyright (c) 2006 Olivier DANET +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef MT2060_H +-#define MT2060_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct mt2060_config { +- u8 i2c_address; +- u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE)) +-extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1); +-#else +-static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_MEDIA_TUNER_MT2060 +- +-#endif +diff --git a/drivers/media/common/tuners/mt2060_priv.h b/drivers/media/common/tuners/mt2060_priv.h +deleted file mode 100644 +index 2b60de6..0000000 +--- a/drivers/media/common/tuners/mt2060_priv.h ++++ /dev/null +@@ -1,104 +0,0 @@ +-/* +- * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" +- * +- * Copyright (c) 2006 Olivier DANET +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef MT2060_PRIV_H +-#define MT2060_PRIV_H +- +-// Uncomment the #define below to enable spurs checking. The results where quite unconvincing. +-// #define MT2060_SPURCHECK +- +-/* This driver is based on the information available in the datasheet of the +- "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map : +- +- I2C Address : 0x60 +- +- Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults ) +- -------------------------------------------------------------------------------- +- 00 | [ PART ] | [ REV ] | R = 0x63 +- 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F +- 02 | [ DIV1 ] | RW = 0x74 +- 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00 +- 04 | NUM2(11:4) ] | RW = 0x08 +- 05 | [ DIV2 ] |NUM2(12)| RW = 0x93 +- 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R +- 07 | [ FMF ] | R +- 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R +- 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20 +- 0A | ?? +- 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30 +- 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF +- 0D | 1 | 0 | [ V1CS ] | RW = 0xB0 +- 0E | ?? +- 0F | ?? +- 10 | ?? +- 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42 +- +- PART : Part code : 6 for MT2060 +- REV : Revision code : 3 for current revision +- LNABAND : Input frequency range : ( See code for details ) +- NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details ) +- FM1CA : Calibration Start Bit +- FM1SS : Calibration Single Step bit +- L1LK : LO1 Lock Detect +- TAD1 : Tune Line ADC ( ? ) +- L2LK : LO2 Lock Detect +- TAD2 : Tune Line ADC ( ? ) +- FMF : Estimated first IF Center frequency Offset ( ? ) +- FM1CAL : Calibration done bit +- TEMP : On chip temperature sensor +- FMCG : Mixer 1 Cap Gain ( ? ) +- GP01 / GP02 : Programmable digital outputs. Unconnected pins ? +- V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? ) +- V1CS : LO1 Capacitor Selection Value ( ? ) +- LOTO : LO Timeout ( ? ) +- VGAG : Tuner Output gain +-*/ +- +-#define I2C_ADDRESS 0x60 +- +-#define REG_PART_REV 0 +-#define REG_LO1C1 1 +-#define REG_LO1C2 2 +-#define REG_LO2C1 3 +-#define REG_LO2C2 4 +-#define REG_LO2C3 5 +-#define REG_LO_STATUS 6 +-#define REG_FM_FREQ 7 +-#define REG_MISC_STAT 8 +-#define REG_MISC_CTRL 9 +-#define REG_RESERVED_A 0x0A +-#define REG_VGAG 0x0B +-#define REG_LO1B1 0x0C +-#define REG_LO1B2 0x0D +-#define REG_LOTO 0x11 +- +-#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips +- +-struct mt2060_priv { +- struct mt2060_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 frequency; +- u16 if1_freq; +- u8 fmfreq; +-}; +- +-#endif +diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/common/tuners/mt2063.c +deleted file mode 100644 +index c89af3c..0000000 +--- a/drivers/media/common/tuners/mt2063.c ++++ /dev/null +@@ -1,2307 +0,0 @@ +-/* +- * Driver for mt2063 Micronas tuner +- * +- * Copyright (c) 2011 Mauro Carvalho Chehab +- * +- * This driver came from a driver originally written by: +- * Henry Wang +- * Made publicly available by Terratec, at: +- * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz +- * The original driver's license is GPL, as declared with MODULE_LICENSE() +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation under version 2 of the License. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "mt2063.h" +- +-static unsigned int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Set Verbosity level"); +- +-#define dprintk(level, fmt, arg...) do { \ +-if (debug >= level) \ +- printk(KERN_DEBUG "mt2063 %s: " fmt, __func__, ## arg); \ +-} while (0) +- +- +-/* positive error codes used internally */ +- +-/* Info: Unavoidable LO-related spur may be present in the output */ +-#define MT2063_SPUR_PRESENT_ERR (0x00800000) +- +-/* Info: Mask of bits used for # of LO-related spurs that were avoided during tuning */ +-#define MT2063_SPUR_CNT_MASK (0x001f0000) +-#define MT2063_SPUR_SHIFT (16) +- +-/* Info: Upconverter frequency is out of range (may be reason for MT_UPC_UNLOCK) */ +-#define MT2063_UPC_RANGE (0x04000000) +- +-/* Info: Downconverter frequency is out of range (may be reason for MT_DPC_UNLOCK) */ +-#define MT2063_DNC_RANGE (0x08000000) +- +-/* +- * Constant defining the version of the following structure +- * and therefore the API for this code. +- * +- * When compiling the tuner driver, the preprocessor will +- * check against this version number to make sure that +- * it matches the version that the tuner driver knows about. +- */ +- +-/* DECT Frequency Avoidance */ +-#define MT2063_DECT_AVOID_US_FREQS 0x00000001 +- +-#define MT2063_DECT_AVOID_EURO_FREQS 0x00000002 +- +-#define MT2063_EXCLUDE_US_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_US_FREQS) != 0) +- +-#define MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(s) (((s) & MT2063_DECT_AVOID_EURO_FREQS) != 0) +- +-enum MT2063_DECT_Avoid_Type { +- MT2063_NO_DECT_AVOIDANCE = 0, /* Do not create DECT exclusion zones. */ +- MT2063_AVOID_US_DECT = MT2063_DECT_AVOID_US_FREQS, /* Avoid US DECT frequencies. */ +- MT2063_AVOID_EURO_DECT = MT2063_DECT_AVOID_EURO_FREQS, /* Avoid European DECT frequencies. */ +- MT2063_AVOID_BOTH /* Avoid both regions. Not typically used. */ +-}; +- +-#define MT2063_MAX_ZONES 48 +- +-struct MT2063_ExclZone_t { +- u32 min_; +- u32 max_; +- struct MT2063_ExclZone_t *next_; +-}; +- +-/* +- * Structure of data needed for Spur Avoidance +- */ +-struct MT2063_AvoidSpursData_t { +- u32 f_ref; +- u32 f_in; +- u32 f_LO1; +- u32 f_if1_Center; +- u32 f_if1_Request; +- u32 f_if1_bw; +- u32 f_LO2; +- u32 f_out; +- u32 f_out_bw; +- u32 f_LO1_Step; +- u32 f_LO2_Step; +- u32 f_LO1_FracN_Avoid; +- u32 f_LO2_FracN_Avoid; +- u32 f_zif_bw; +- u32 f_min_LO_Separation; +- u32 maxH1; +- u32 maxH2; +- enum MT2063_DECT_Avoid_Type avoidDECT; +- u32 bSpurPresent; +- u32 bSpurAvoided; +- u32 nSpursFound; +- u32 nZones; +- struct MT2063_ExclZone_t *freeZones; +- struct MT2063_ExclZone_t *usedZones; +- struct MT2063_ExclZone_t MT2063_ExclZones[MT2063_MAX_ZONES]; +-}; +- +-/* +- * Parameter for function MT2063_SetPowerMask that specifies the power down +- * of various sections of the MT2063. +- */ +-enum MT2063_Mask_Bits { +- MT2063_REG_SD = 0x0040, /* Shutdown regulator */ +- MT2063_SRO_SD = 0x0020, /* Shutdown SRO */ +- MT2063_AFC_SD = 0x0010, /* Shutdown AFC A/D */ +- MT2063_PD_SD = 0x0002, /* Enable power detector shutdown */ +- MT2063_PDADC_SD = 0x0001, /* Enable power detector A/D shutdown */ +- MT2063_VCO_SD = 0x8000, /* Enable VCO shutdown */ +- MT2063_LTX_SD = 0x4000, /* Enable LTX shutdown */ +- MT2063_LT1_SD = 0x2000, /* Enable LT1 shutdown */ +- MT2063_LNA_SD = 0x1000, /* Enable LNA shutdown */ +- MT2063_UPC_SD = 0x0800, /* Enable upconverter shutdown */ +- MT2063_DNC_SD = 0x0400, /* Enable downconverter shutdown */ +- MT2063_VGA_SD = 0x0200, /* Enable VGA shutdown */ +- MT2063_AMP_SD = 0x0100, /* Enable AMP shutdown */ +- MT2063_ALL_SD = 0xFF73, /* All shutdown bits for this tuner */ +- MT2063_NONE_SD = 0x0000 /* No shutdown bits */ +-}; +- +-/* +- * Possible values for MT2063_DNC_OUTPUT +- */ +-enum MT2063_DNC_Output_Enable { +- MT2063_DNC_NONE = 0, +- MT2063_DNC_1, +- MT2063_DNC_2, +- MT2063_DNC_BOTH +-}; +- +-/* +- * Two-wire serial bus subaddresses of the tuner registers. +- * Also known as the tuner's register addresses. +- */ +-enum MT2063_Register_Offsets { +- MT2063_REG_PART_REV = 0, /* 0x00: Part/Rev Code */ +- MT2063_REG_LO1CQ_1, /* 0x01: LO1C Queued Byte 1 */ +- MT2063_REG_LO1CQ_2, /* 0x02: LO1C Queued Byte 2 */ +- MT2063_REG_LO2CQ_1, /* 0x03: LO2C Queued Byte 1 */ +- MT2063_REG_LO2CQ_2, /* 0x04: LO2C Queued Byte 2 */ +- MT2063_REG_LO2CQ_3, /* 0x05: LO2C Queued Byte 3 */ +- MT2063_REG_RSVD_06, /* 0x06: Reserved */ +- MT2063_REG_LO_STATUS, /* 0x07: LO Status */ +- MT2063_REG_FIFFC, /* 0x08: FIFF Center */ +- MT2063_REG_CLEARTUNE, /* 0x09: ClearTune Filter */ +- MT2063_REG_ADC_OUT, /* 0x0A: ADC_OUT */ +- MT2063_REG_LO1C_1, /* 0x0B: LO1C Byte 1 */ +- MT2063_REG_LO1C_2, /* 0x0C: LO1C Byte 2 */ +- MT2063_REG_LO2C_1, /* 0x0D: LO2C Byte 1 */ +- MT2063_REG_LO2C_2, /* 0x0E: LO2C Byte 2 */ +- MT2063_REG_LO2C_3, /* 0x0F: LO2C Byte 3 */ +- MT2063_REG_RSVD_10, /* 0x10: Reserved */ +- MT2063_REG_PWR_1, /* 0x11: PWR Byte 1 */ +- MT2063_REG_PWR_2, /* 0x12: PWR Byte 2 */ +- MT2063_REG_TEMP_STATUS, /* 0x13: Temp Status */ +- MT2063_REG_XO_STATUS, /* 0x14: Crystal Status */ +- MT2063_REG_RF_STATUS, /* 0x15: RF Attn Status */ +- MT2063_REG_FIF_STATUS, /* 0x16: FIF Attn Status */ +- MT2063_REG_LNA_OV, /* 0x17: LNA Attn Override */ +- MT2063_REG_RF_OV, /* 0x18: RF Attn Override */ +- MT2063_REG_FIF_OV, /* 0x19: FIF Attn Override */ +- MT2063_REG_LNA_TGT, /* 0x1A: Reserved */ +- MT2063_REG_PD1_TGT, /* 0x1B: Pwr Det 1 Target */ +- MT2063_REG_PD2_TGT, /* 0x1C: Pwr Det 2 Target */ +- MT2063_REG_RSVD_1D, /* 0x1D: Reserved */ +- MT2063_REG_RSVD_1E, /* 0x1E: Reserved */ +- MT2063_REG_RSVD_1F, /* 0x1F: Reserved */ +- MT2063_REG_RSVD_20, /* 0x20: Reserved */ +- MT2063_REG_BYP_CTRL, /* 0x21: Bypass Control */ +- MT2063_REG_RSVD_22, /* 0x22: Reserved */ +- MT2063_REG_RSVD_23, /* 0x23: Reserved */ +- MT2063_REG_RSVD_24, /* 0x24: Reserved */ +- MT2063_REG_RSVD_25, /* 0x25: Reserved */ +- MT2063_REG_RSVD_26, /* 0x26: Reserved */ +- MT2063_REG_RSVD_27, /* 0x27: Reserved */ +- MT2063_REG_FIFF_CTRL, /* 0x28: FIFF Control */ +- MT2063_REG_FIFF_OFFSET, /* 0x29: FIFF Offset */ +- MT2063_REG_CTUNE_CTRL, /* 0x2A: Reserved */ +- MT2063_REG_CTUNE_OV, /* 0x2B: Reserved */ +- MT2063_REG_CTRL_2C, /* 0x2C: Reserved */ +- MT2063_REG_FIFF_CTRL2, /* 0x2D: Fiff Control */ +- MT2063_REG_RSVD_2E, /* 0x2E: Reserved */ +- MT2063_REG_DNC_GAIN, /* 0x2F: DNC Control */ +- MT2063_REG_VGA_GAIN, /* 0x30: VGA Gain Ctrl */ +- MT2063_REG_RSVD_31, /* 0x31: Reserved */ +- MT2063_REG_TEMP_SEL, /* 0x32: Temperature Selection */ +- MT2063_REG_RSVD_33, /* 0x33: Reserved */ +- MT2063_REG_RSVD_34, /* 0x34: Reserved */ +- MT2063_REG_RSVD_35, /* 0x35: Reserved */ +- MT2063_REG_RSVD_36, /* 0x36: Reserved */ +- MT2063_REG_RSVD_37, /* 0x37: Reserved */ +- MT2063_REG_RSVD_38, /* 0x38: Reserved */ +- MT2063_REG_RSVD_39, /* 0x39: Reserved */ +- MT2063_REG_RSVD_3A, /* 0x3A: Reserved */ +- MT2063_REG_RSVD_3B, /* 0x3B: Reserved */ +- MT2063_REG_RSVD_3C, /* 0x3C: Reserved */ +- MT2063_REG_END_REGS +-}; +- +-struct mt2063_state { +- struct i2c_adapter *i2c; +- +- bool init; +- +- const struct mt2063_config *config; +- struct dvb_tuner_ops ops; +- struct dvb_frontend *frontend; +- struct tuner_state status; +- +- u32 frequency; +- u32 srate; +- u32 bandwidth; +- u32 reference; +- +- u32 tuner_id; +- struct MT2063_AvoidSpursData_t AS_Data; +- u32 f_IF1_actual; +- u32 rcvr_mode; +- u32 ctfilt_sw; +- u32 CTFiltMax[31]; +- u32 num_regs; +- u8 reg[MT2063_REG_END_REGS]; +-}; +- +-/* +- * mt2063_write - Write data into the I2C bus +- */ +-static u32 mt2063_write(struct mt2063_state *state, u8 reg, u8 *data, u32 len) +-{ +- struct dvb_frontend *fe = state->frontend; +- int ret; +- u8 buf[60]; +- struct i2c_msg msg = { +- .addr = state->config->tuner_address, +- .flags = 0, +- .buf = buf, +- .len = len + 1 +- }; +- +- dprintk(2, "\n"); +- +- msg.buf[0] = reg; +- memcpy(msg.buf + 1, data, len); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret < 0) +- printk(KERN_ERR "%s error ret=%d\n", __func__, ret); +- +- return ret; +-} +- +-/* +- * mt2063_write - Write register data into the I2C bus, caching the value +- */ +-static u32 mt2063_setreg(struct mt2063_state *state, u8 reg, u8 val) +-{ +- u32 status; +- +- dprintk(2, "\n"); +- +- if (reg >= MT2063_REG_END_REGS) +- return -ERANGE; +- +- status = mt2063_write(state, reg, &val, 1); +- if (status < 0) +- return status; +- +- state->reg[reg] = val; +- +- return 0; +-} +- +-/* +- * mt2063_read - Read data from the I2C bus +- */ +-static u32 mt2063_read(struct mt2063_state *state, +- u8 subAddress, u8 *pData, u32 cnt) +-{ +- u32 status = 0; /* Status to be returned */ +- struct dvb_frontend *fe = state->frontend; +- u32 i = 0; +- +- dprintk(2, "addr 0x%02x, cnt %d\n", subAddress, cnt); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- for (i = 0; i < cnt; i++) { +- u8 b0[] = { subAddress + i }; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->tuner_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = state->config->tuner_address, +- .flags = I2C_M_RD, +- .buf = pData + i, +- .len = 1 +- } +- }; +- +- status = i2c_transfer(state->i2c, msg, 2); +- dprintk(2, "addr 0x%02x, ret = %d, val = 0x%02x\n", +- subAddress + i, status, *(pData + i)); +- if (status < 0) +- break; +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (status < 0) +- printk(KERN_ERR "Can't read from address 0x%02x,\n", +- subAddress + i); +- +- return status; +-} +- +-/* +- * FIXME: Is this really needed? +- */ +-static int MT2063_Sleep(struct dvb_frontend *fe) +-{ +- /* +- * ToDo: Add code here to implement a OS blocking +- */ +- msleep(10); +- +- return 0; +-} +- +-/* +- * Microtune spur avoidance +- */ +- +-/* Implement ceiling, floor functions. */ +-#define ceil(n, d) (((n) < 0) ? (-((-(n))/(d))) : (n)/(d) + ((n)%(d) != 0)) +-#define floor(n, d) (((n) < 0) ? (-((-(n))/(d))) - ((n)%(d) != 0) : (n)/(d)) +- +-struct MT2063_FIFZone_t { +- s32 min_; +- s32 max_; +-}; +- +-static struct MT2063_ExclZone_t *InsertNode(struct MT2063_AvoidSpursData_t +- *pAS_Info, +- struct MT2063_ExclZone_t *pPrevNode) +-{ +- struct MT2063_ExclZone_t *pNode; +- +- dprintk(2, "\n"); +- +- /* Check for a node in the free list */ +- if (pAS_Info->freeZones != NULL) { +- /* Use one from the free list */ +- pNode = pAS_Info->freeZones; +- pAS_Info->freeZones = pNode->next_; +- } else { +- /* Grab a node from the array */ +- pNode = &pAS_Info->MT2063_ExclZones[pAS_Info->nZones]; +- } +- +- if (pPrevNode != NULL) { +- pNode->next_ = pPrevNode->next_; +- pPrevNode->next_ = pNode; +- } else { /* insert at the beginning of the list */ +- +- pNode->next_ = pAS_Info->usedZones; +- pAS_Info->usedZones = pNode; +- } +- +- pAS_Info->nZones++; +- return pNode; +-} +- +-static struct MT2063_ExclZone_t *RemoveNode(struct MT2063_AvoidSpursData_t +- *pAS_Info, +- struct MT2063_ExclZone_t *pPrevNode, +- struct MT2063_ExclZone_t +- *pNodeToRemove) +-{ +- struct MT2063_ExclZone_t *pNext = pNodeToRemove->next_; +- +- dprintk(2, "\n"); +- +- /* Make previous node point to the subsequent node */ +- if (pPrevNode != NULL) +- pPrevNode->next_ = pNext; +- +- /* Add pNodeToRemove to the beginning of the freeZones */ +- pNodeToRemove->next_ = pAS_Info->freeZones; +- pAS_Info->freeZones = pNodeToRemove; +- +- /* Decrement node count */ +- pAS_Info->nZones--; +- +- return pNext; +-} +- +-/* +- * MT_AddExclZone() +- * +- * Add (and merge) an exclusion zone into the list. +- * If the range (f_min, f_max) is totally outside the +- * 1st IF BW, ignore the entry. +- * If the range (f_min, f_max) is negative, ignore the entry. +- */ +-static void MT2063_AddExclZone(struct MT2063_AvoidSpursData_t *pAS_Info, +- u32 f_min, u32 f_max) +-{ +- struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; +- struct MT2063_ExclZone_t *pPrev = NULL; +- struct MT2063_ExclZone_t *pNext = NULL; +- +- dprintk(2, "\n"); +- +- /* Check to see if this overlaps the 1st IF filter */ +- if ((f_max > (pAS_Info->f_if1_Center - (pAS_Info->f_if1_bw / 2))) +- && (f_min < (pAS_Info->f_if1_Center + (pAS_Info->f_if1_bw / 2))) +- && (f_min < f_max)) { +- /* +- * 1 2 3 4 5 6 +- * +- * New entry: |---| |--| |--| |-| |---| |--| +- * or or or or or +- * Existing: |--| |--| |--| |---| |-| |--| +- */ +- +- /* Check for our place in the list */ +- while ((pNode != NULL) && (pNode->max_ < f_min)) { +- pPrev = pNode; +- pNode = pNode->next_; +- } +- +- if ((pNode != NULL) && (pNode->min_ < f_max)) { +- /* Combine me with pNode */ +- if (f_min < pNode->min_) +- pNode->min_ = f_min; +- if (f_max > pNode->max_) +- pNode->max_ = f_max; +- } else { +- pNode = InsertNode(pAS_Info, pPrev); +- pNode->min_ = f_min; +- pNode->max_ = f_max; +- } +- +- /* Look for merging possibilities */ +- pNext = pNode->next_; +- while ((pNext != NULL) && (pNext->min_ < pNode->max_)) { +- if (pNext->max_ > pNode->max_) +- pNode->max_ = pNext->max_; +- /* Remove pNext, return ptr to pNext->next */ +- pNext = RemoveNode(pAS_Info, pNode, pNext); +- } +- } +-} +- +-/* +- * Reset all exclusion zones. +- * Add zones to protect the PLL FracN regions near zero +- */ +-static void MT2063_ResetExclZones(struct MT2063_AvoidSpursData_t *pAS_Info) +-{ +- u32 center; +- +- dprintk(2, "\n"); +- +- pAS_Info->nZones = 0; /* this clears the used list */ +- pAS_Info->usedZones = NULL; /* reset ptr */ +- pAS_Info->freeZones = NULL; /* reset ptr */ +- +- center = +- pAS_Info->f_ref * +- ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 + +- pAS_Info->f_in) / pAS_Info->f_ref) - pAS_Info->f_in; +- while (center < +- pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + +- pAS_Info->f_LO1_FracN_Avoid) { +- /* Exclude LO1 FracN */ +- MT2063_AddExclZone(pAS_Info, +- center - pAS_Info->f_LO1_FracN_Avoid, +- center - 1); +- MT2063_AddExclZone(pAS_Info, center + 1, +- center + pAS_Info->f_LO1_FracN_Avoid); +- center += pAS_Info->f_ref; +- } +- +- center = +- pAS_Info->f_ref * +- ((pAS_Info->f_if1_Center - pAS_Info->f_if1_bw / 2 - +- pAS_Info->f_out) / pAS_Info->f_ref) + pAS_Info->f_out; +- while (center < +- pAS_Info->f_if1_Center + pAS_Info->f_if1_bw / 2 + +- pAS_Info->f_LO2_FracN_Avoid) { +- /* Exclude LO2 FracN */ +- MT2063_AddExclZone(pAS_Info, +- center - pAS_Info->f_LO2_FracN_Avoid, +- center - 1); +- MT2063_AddExclZone(pAS_Info, center + 1, +- center + pAS_Info->f_LO2_FracN_Avoid); +- center += pAS_Info->f_ref; +- } +- +- if (MT2063_EXCLUDE_US_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { +- /* Exclude LO1 values that conflict with DECT channels */ +- MT2063_AddExclZone(pAS_Info, 1920836000 - pAS_Info->f_in, 1922236000 - pAS_Info->f_in); /* Ctr = 1921.536 */ +- MT2063_AddExclZone(pAS_Info, 1922564000 - pAS_Info->f_in, 1923964000 - pAS_Info->f_in); /* Ctr = 1923.264 */ +- MT2063_AddExclZone(pAS_Info, 1924292000 - pAS_Info->f_in, 1925692000 - pAS_Info->f_in); /* Ctr = 1924.992 */ +- MT2063_AddExclZone(pAS_Info, 1926020000 - pAS_Info->f_in, 1927420000 - pAS_Info->f_in); /* Ctr = 1926.720 */ +- MT2063_AddExclZone(pAS_Info, 1927748000 - pAS_Info->f_in, 1929148000 - pAS_Info->f_in); /* Ctr = 1928.448 */ +- } +- +- if (MT2063_EXCLUDE_EURO_DECT_FREQUENCIES(pAS_Info->avoidDECT)) { +- MT2063_AddExclZone(pAS_Info, 1896644000 - pAS_Info->f_in, 1898044000 - pAS_Info->f_in); /* Ctr = 1897.344 */ +- MT2063_AddExclZone(pAS_Info, 1894916000 - pAS_Info->f_in, 1896316000 - pAS_Info->f_in); /* Ctr = 1895.616 */ +- MT2063_AddExclZone(pAS_Info, 1893188000 - pAS_Info->f_in, 1894588000 - pAS_Info->f_in); /* Ctr = 1893.888 */ +- MT2063_AddExclZone(pAS_Info, 1891460000 - pAS_Info->f_in, 1892860000 - pAS_Info->f_in); /* Ctr = 1892.16 */ +- MT2063_AddExclZone(pAS_Info, 1889732000 - pAS_Info->f_in, 1891132000 - pAS_Info->f_in); /* Ctr = 1890.432 */ +- MT2063_AddExclZone(pAS_Info, 1888004000 - pAS_Info->f_in, 1889404000 - pAS_Info->f_in); /* Ctr = 1888.704 */ +- MT2063_AddExclZone(pAS_Info, 1886276000 - pAS_Info->f_in, 1887676000 - pAS_Info->f_in); /* Ctr = 1886.976 */ +- MT2063_AddExclZone(pAS_Info, 1884548000 - pAS_Info->f_in, 1885948000 - pAS_Info->f_in); /* Ctr = 1885.248 */ +- MT2063_AddExclZone(pAS_Info, 1882820000 - pAS_Info->f_in, 1884220000 - pAS_Info->f_in); /* Ctr = 1883.52 */ +- MT2063_AddExclZone(pAS_Info, 1881092000 - pAS_Info->f_in, 1882492000 - pAS_Info->f_in); /* Ctr = 1881.792 */ +- } +-} +- +-/* +- * MT_ChooseFirstIF - Choose the best available 1st IF +- * If f_Desired is not excluded, choose that first. +- * Otherwise, return the value closest to f_Center that is +- * not excluded +- */ +-static u32 MT2063_ChooseFirstIF(struct MT2063_AvoidSpursData_t *pAS_Info) +-{ +- /* +- * Update "f_Desired" to be the nearest "combinational-multiple" of +- * "f_LO1_Step". +- * The resulting number, F_LO1 must be a multiple of f_LO1_Step. +- * And F_LO1 is the arithmetic sum of f_in + f_Center. +- * Neither f_in, nor f_Center must be a multiple of f_LO1_Step. +- * However, the sum must be. +- */ +- const u32 f_Desired = +- pAS_Info->f_LO1_Step * +- ((pAS_Info->f_if1_Request + pAS_Info->f_in + +- pAS_Info->f_LO1_Step / 2) / pAS_Info->f_LO1_Step) - +- pAS_Info->f_in; +- const u32 f_Step = +- (pAS_Info->f_LO1_Step > +- pAS_Info->f_LO2_Step) ? pAS_Info->f_LO1_Step : pAS_Info-> +- f_LO2_Step; +- u32 f_Center; +- s32 i; +- s32 j = 0; +- u32 bDesiredExcluded = 0; +- u32 bZeroExcluded = 0; +- s32 tmpMin, tmpMax; +- s32 bestDiff; +- struct MT2063_ExclZone_t *pNode = pAS_Info->usedZones; +- struct MT2063_FIFZone_t zones[MT2063_MAX_ZONES]; +- +- dprintk(2, "\n"); +- +- if (pAS_Info->nZones == 0) +- return f_Desired; +- +- /* +- * f_Center needs to be an integer multiple of f_Step away +- * from f_Desired +- */ +- if (pAS_Info->f_if1_Center > f_Desired) +- f_Center = +- f_Desired + +- f_Step * +- ((pAS_Info->f_if1_Center - f_Desired + +- f_Step / 2) / f_Step); +- else +- f_Center = +- f_Desired - +- f_Step * +- ((f_Desired - pAS_Info->f_if1_Center + +- f_Step / 2) / f_Step); +- +- /* +- * Take MT_ExclZones, center around f_Center and change the +- * resolution to f_Step +- */ +- while (pNode != NULL) { +- /* floor function */ +- tmpMin = +- floor((s32) (pNode->min_ - f_Center), (s32) f_Step); +- +- /* ceil function */ +- tmpMax = +- ceil((s32) (pNode->max_ - f_Center), (s32) f_Step); +- +- if ((pNode->min_ < f_Desired) && (pNode->max_ > f_Desired)) +- bDesiredExcluded = 1; +- +- if ((tmpMin < 0) && (tmpMax > 0)) +- bZeroExcluded = 1; +- +- /* See if this zone overlaps the previous */ +- if ((j > 0) && (tmpMin < zones[j - 1].max_)) +- zones[j - 1].max_ = tmpMax; +- else { +- /* Add new zone */ +- zones[j].min_ = tmpMin; +- zones[j].max_ = tmpMax; +- j++; +- } +- pNode = pNode->next_; +- } +- +- /* +- * If the desired is okay, return with it +- */ +- if (bDesiredExcluded == 0) +- return f_Desired; +- +- /* +- * If the desired is excluded and the center is okay, return with it +- */ +- if (bZeroExcluded == 0) +- return f_Center; +- +- /* Find the value closest to 0 (f_Center) */ +- bestDiff = zones[0].min_; +- for (i = 0; i < j; i++) { +- if (abs(zones[i].min_) < abs(bestDiff)) +- bestDiff = zones[i].min_; +- if (abs(zones[i].max_) < abs(bestDiff)) +- bestDiff = zones[i].max_; +- } +- +- if (bestDiff < 0) +- return f_Center - ((u32) (-bestDiff) * f_Step); +- +- return f_Center + (bestDiff * f_Step); +-} +- +-/** +- * gcd() - Uses Euclid's algorithm +- * +- * @u, @v: Unsigned values whose GCD is desired. +- * +- * Returns THE greatest common divisor of u and v, if either value is 0, +- * the other value is returned as the result. +- */ +-static u32 MT2063_gcd(u32 u, u32 v) +-{ +- u32 r; +- +- while (v != 0) { +- r = u % v; +- u = v; +- v = r; +- } +- +- return u; +-} +- +-/** +- * IsSpurInBand() - Checks to see if a spur will be present within the IF's +- * bandwidth. (fIFOut +/- fIFBW, -fIFOut +/- fIFBW) +- * +- * ma mb mc md +- * <--+-+-+-------------------+-------------------+-+-+--> +- * | ^ 0 ^ | +- * ^ b=-fIFOut+fIFBW/2 -b=+fIFOut-fIFBW/2 ^ +- * a=-fIFOut-fIFBW/2 -a=+fIFOut+fIFBW/2 +- * +- * Note that some equations are doubled to prevent round-off +- * problems when calculating fIFBW/2 +- * +- * @pAS_Info: Avoid Spurs information block +- * @fm: If spur, amount f_IF1 has to move negative +- * @fp: If spur, amount f_IF1 has to move positive +- * +- * Returns 1 if an LO spur would be present, otherwise 0. +- */ +-static u32 IsSpurInBand(struct MT2063_AvoidSpursData_t *pAS_Info, +- u32 *fm, u32 * fp) +-{ +- /* +- ** Calculate LO frequency settings. +- */ +- u32 n, n0; +- const u32 f_LO1 = pAS_Info->f_LO1; +- const u32 f_LO2 = pAS_Info->f_LO2; +- const u32 d = pAS_Info->f_out + pAS_Info->f_out_bw / 2; +- const u32 c = d - pAS_Info->f_out_bw; +- const u32 f = pAS_Info->f_zif_bw / 2; +- const u32 f_Scale = (f_LO1 / (UINT_MAX / 2 / pAS_Info->maxH1)) + 1; +- s32 f_nsLO1, f_nsLO2; +- s32 f_Spur; +- u32 ma, mb, mc, md, me, mf; +- u32 lo_gcd, gd_Scale, gc_Scale, gf_Scale, hgds, hgfs, hgcs; +- +- dprintk(2, "\n"); +- +- *fm = 0; +- +- /* +- ** For each edge (d, c & f), calculate a scale, based on the gcd +- ** of f_LO1, f_LO2 and the edge value. Use the larger of this +- ** gcd-based scale factor or f_Scale. +- */ +- lo_gcd = MT2063_gcd(f_LO1, f_LO2); +- gd_Scale = max((u32) MT2063_gcd(lo_gcd, d), f_Scale); +- hgds = gd_Scale / 2; +- gc_Scale = max((u32) MT2063_gcd(lo_gcd, c), f_Scale); +- hgcs = gc_Scale / 2; +- gf_Scale = max((u32) MT2063_gcd(lo_gcd, f), f_Scale); +- hgfs = gf_Scale / 2; +- +- n0 = DIV_ROUND_UP(f_LO2 - d, f_LO1 - f_LO2); +- +- /* Check out all multiples of LO1 from n0 to m_maxLOSpurHarmonic */ +- for (n = n0; n <= pAS_Info->maxH1; ++n) { +- md = (n * ((f_LO1 + hgds) / gd_Scale) - +- ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); +- +- /* If # fLO2 harmonics > m_maxLOSpurHarmonic, then no spurs present */ +- if (md >= pAS_Info->maxH1) +- break; +- +- ma = (n * ((f_LO1 + hgds) / gd_Scale) + +- ((d + hgds) / gd_Scale)) / ((f_LO2 + hgds) / gd_Scale); +- +- /* If no spurs between +/- (f_out + f_IFBW/2), then try next harmonic */ +- if (md == ma) +- continue; +- +- mc = (n * ((f_LO1 + hgcs) / gc_Scale) - +- ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); +- if (mc != md) { +- f_nsLO1 = (s32) (n * (f_LO1 / gc_Scale)); +- f_nsLO2 = (s32) (mc * (f_LO2 / gc_Scale)); +- f_Spur = +- (gc_Scale * (f_nsLO1 - f_nsLO2)) + +- n * (f_LO1 % gc_Scale) - mc * (f_LO2 % gc_Scale); +- +- *fp = ((f_Spur - (s32) c) / (mc - n)) + 1; +- *fm = (((s32) d - f_Spur) / (mc - n)) + 1; +- return 1; +- } +- +- /* Location of Zero-IF-spur to be checked */ +- me = (n * ((f_LO1 + hgfs) / gf_Scale) + +- ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); +- mf = (n * ((f_LO1 + hgfs) / gf_Scale) - +- ((f + hgfs) / gf_Scale)) / ((f_LO2 + hgfs) / gf_Scale); +- if (me != mf) { +- f_nsLO1 = n * (f_LO1 / gf_Scale); +- f_nsLO2 = me * (f_LO2 / gf_Scale); +- f_Spur = +- (gf_Scale * (f_nsLO1 - f_nsLO2)) + +- n * (f_LO1 % gf_Scale) - me * (f_LO2 % gf_Scale); +- +- *fp = ((f_Spur + (s32) f) / (me - n)) + 1; +- *fm = (((s32) f - f_Spur) / (me - n)) + 1; +- return 1; +- } +- +- mb = (n * ((f_LO1 + hgcs) / gc_Scale) + +- ((c + hgcs) / gc_Scale)) / ((f_LO2 + hgcs) / gc_Scale); +- if (ma != mb) { +- f_nsLO1 = n * (f_LO1 / gc_Scale); +- f_nsLO2 = ma * (f_LO2 / gc_Scale); +- f_Spur = +- (gc_Scale * (f_nsLO1 - f_nsLO2)) + +- n * (f_LO1 % gc_Scale) - ma * (f_LO2 % gc_Scale); +- +- *fp = (((s32) d + f_Spur) / (ma - n)) + 1; +- *fm = (-(f_Spur + (s32) c) / (ma - n)) + 1; +- return 1; +- } +- } +- +- /* No spurs found */ +- return 0; +-} +- +-/* +- * MT_AvoidSpurs() - Main entry point to avoid spurs. +- * Checks for existing spurs in present LO1, LO2 freqs +- * and if present, chooses spur-free LO1, LO2 combination +- * that tunes the same input/output frequencies. +- */ +-static u32 MT2063_AvoidSpurs(struct MT2063_AvoidSpursData_t *pAS_Info) +-{ +- u32 status = 0; +- u32 fm, fp; /* restricted range on LO's */ +- pAS_Info->bSpurAvoided = 0; +- pAS_Info->nSpursFound = 0; +- +- dprintk(2, "\n"); +- +- if (pAS_Info->maxH1 == 0) +- return 0; +- +- /* +- * Avoid LO Generated Spurs +- * +- * Make sure that have no LO-related spurs within the IF output +- * bandwidth. +- * +- * If there is an LO spur in this band, start at the current IF1 frequency +- * and work out until we find a spur-free frequency or run up against the +- * 1st IF SAW band edge. Use temporary copies of fLO1 and fLO2 so that they +- * will be unchanged if a spur-free setting is not found. +- */ +- pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); +- if (pAS_Info->bSpurPresent) { +- u32 zfIF1 = pAS_Info->f_LO1 - pAS_Info->f_in; /* current attempt at a 1st IF */ +- u32 zfLO1 = pAS_Info->f_LO1; /* current attempt at an LO1 freq */ +- u32 zfLO2 = pAS_Info->f_LO2; /* current attempt at an LO2 freq */ +- u32 delta_IF1; +- u32 new_IF1; +- +- /* +- ** Spur was found, attempt to find a spur-free 1st IF +- */ +- do { +- pAS_Info->nSpursFound++; +- +- /* Raise f_IF1_upper, if needed */ +- MT2063_AddExclZone(pAS_Info, zfIF1 - fm, zfIF1 + fp); +- +- /* Choose next IF1 that is closest to f_IF1_CENTER */ +- new_IF1 = MT2063_ChooseFirstIF(pAS_Info); +- +- if (new_IF1 > zfIF1) { +- pAS_Info->f_LO1 += (new_IF1 - zfIF1); +- pAS_Info->f_LO2 += (new_IF1 - zfIF1); +- } else { +- pAS_Info->f_LO1 -= (zfIF1 - new_IF1); +- pAS_Info->f_LO2 -= (zfIF1 - new_IF1); +- } +- zfIF1 = new_IF1; +- +- if (zfIF1 > pAS_Info->f_if1_Center) +- delta_IF1 = zfIF1 - pAS_Info->f_if1_Center; +- else +- delta_IF1 = pAS_Info->f_if1_Center - zfIF1; +- +- pAS_Info->bSpurPresent = IsSpurInBand(pAS_Info, &fm, &fp); +- /* +- * Continue while the new 1st IF is still within the 1st IF bandwidth +- * and there is a spur in the band (again) +- */ +- } while ((2 * delta_IF1 + pAS_Info->f_out_bw <= pAS_Info->f_if1_bw) && pAS_Info->bSpurPresent); +- +- /* +- * Use the LO-spur free values found. If the search went all +- * the way to the 1st IF band edge and always found spurs, just +- * leave the original choice. It's as "good" as any other. +- */ +- if (pAS_Info->bSpurPresent == 1) { +- status |= MT2063_SPUR_PRESENT_ERR; +- pAS_Info->f_LO1 = zfLO1; +- pAS_Info->f_LO2 = zfLO2; +- } else +- pAS_Info->bSpurAvoided = 1; +- } +- +- status |= +- ((pAS_Info-> +- nSpursFound << MT2063_SPUR_SHIFT) & MT2063_SPUR_CNT_MASK); +- +- return status; +-} +- +-/* +- * Constants used by the tuning algorithm +- */ +-#define MT2063_REF_FREQ (16000000UL) /* Reference oscillator Frequency (in Hz) */ +-#define MT2063_IF1_BW (22000000UL) /* The IF1 filter bandwidth (in Hz) */ +-#define MT2063_TUNE_STEP_SIZE (50000UL) /* Tune in steps of 50 kHz */ +-#define MT2063_SPUR_STEP_HZ (250000UL) /* Step size (in Hz) to move IF1 when avoiding spurs */ +-#define MT2063_ZIF_BW (2000000UL) /* Zero-IF spur-free bandwidth (in Hz) */ +-#define MT2063_MAX_HARMONICS_1 (15UL) /* Highest intra-tuner LO Spur Harmonic to be avoided */ +-#define MT2063_MAX_HARMONICS_2 (5UL) /* Highest inter-tuner LO Spur Harmonic to be avoided */ +-#define MT2063_MIN_LO_SEP (1000000UL) /* Minimum inter-tuner LO frequency separation */ +-#define MT2063_LO1_FRACN_AVOID (0UL) /* LO1 FracN numerator avoid region (in Hz) */ +-#define MT2063_LO2_FRACN_AVOID (199999UL) /* LO2 FracN numerator avoid region (in Hz) */ +-#define MT2063_MIN_FIN_FREQ (44000000UL) /* Minimum input frequency (in Hz) */ +-#define MT2063_MAX_FIN_FREQ (1100000000UL) /* Maximum input frequency (in Hz) */ +-#define MT2063_MIN_FOUT_FREQ (36000000UL) /* Minimum output frequency (in Hz) */ +-#define MT2063_MAX_FOUT_FREQ (57000000UL) /* Maximum output frequency (in Hz) */ +-#define MT2063_MIN_DNC_FREQ (1293000000UL) /* Minimum LO2 frequency (in Hz) */ +-#define MT2063_MAX_DNC_FREQ (1614000000UL) /* Maximum LO2 frequency (in Hz) */ +-#define MT2063_MIN_UPC_FREQ (1396000000UL) /* Minimum LO1 frequency (in Hz) */ +-#define MT2063_MAX_UPC_FREQ (2750000000UL) /* Maximum LO1 frequency (in Hz) */ +- +-/* +- * Define the supported Part/Rev codes for the MT2063 +- */ +-#define MT2063_B0 (0x9B) +-#define MT2063_B1 (0x9C) +-#define MT2063_B2 (0x9D) +-#define MT2063_B3 (0x9E) +- +-/** +- * mt2063_lockStatus - Checks to see if LO1 and LO2 are locked +- * +- * @state: struct mt2063_state pointer +- * +- * This function returns 0, if no lock, 1 if locked and a value < 1 if error +- */ +-static unsigned int mt2063_lockStatus(struct mt2063_state *state) +-{ +- const u32 nMaxWait = 100; /* wait a maximum of 100 msec */ +- const u32 nPollRate = 2; /* poll status bits every 2 ms */ +- const u32 nMaxLoops = nMaxWait / nPollRate; +- const u8 LO1LK = 0x80; +- u8 LO2LK = 0x08; +- u32 status; +- u32 nDelays = 0; +- +- dprintk(2, "\n"); +- +- /* LO2 Lock bit was in a different place for B0 version */ +- if (state->tuner_id == MT2063_B0) +- LO2LK = 0x40; +- +- do { +- status = mt2063_read(state, MT2063_REG_LO_STATUS, +- &state->reg[MT2063_REG_LO_STATUS], 1); +- +- if (status < 0) +- return status; +- +- if ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) == +- (LO1LK | LO2LK)) { +- return TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO; +- } +- msleep(nPollRate); /* Wait between retries */ +- } while (++nDelays < nMaxLoops); +- +- /* +- * Got no lock or partial lock +- */ +- return 0; +-} +- +-/* +- * Constants for setting receiver modes. +- * (6 modes defined at this time, enumerated by mt2063_delivery_sys) +- * (DNC1GC & DNC2GC are the values, which are used, when the specific +- * DNC Output is selected, the other is always off) +- * +- * enum mt2063_delivery_sys +- * -------------+---------------------------------------------- +- * Mode 0 : | MT2063_CABLE_QAM +- * Mode 1 : | MT2063_CABLE_ANALOG +- * Mode 2 : | MT2063_OFFAIR_COFDM +- * Mode 3 : | MT2063_OFFAIR_COFDM_SAWLESS +- * Mode 4 : | MT2063_OFFAIR_ANALOG +- * Mode 5 : | MT2063_OFFAIR_8VSB +- * --------------+---------------------------------------------- +- * +- * |<---------- Mode -------------->| +- * Reg Field | 0 | 1 | 2 | 3 | 4 | 5 | +- * ------------+-----+-----+-----+-----+-----+-----+ +- * RFAGCen | OFF | OFF | OFF | OFF | OFF | OFF +- * LNARin | 0 | 0 | 3 | 3 | 3 | 3 +- * FIFFQen | 1 | 1 | 1 | 1 | 1 | 1 +- * FIFFq | 0 | 0 | 0 | 0 | 0 | 0 +- * DNC1gc | 0 | 0 | 0 | 0 | 0 | 0 +- * DNC2gc | 0 | 0 | 0 | 0 | 0 | 0 +- * GCU Auto | 1 | 1 | 1 | 1 | 1 | 1 +- * LNA max Atn | 31 | 31 | 31 | 31 | 31 | 31 +- * LNA Target | 44 | 43 | 43 | 43 | 43 | 43 +- * ign RF Ovl | 0 | 0 | 0 | 0 | 0 | 0 +- * RF max Atn | 31 | 31 | 31 | 31 | 31 | 31 +- * PD1 Target | 36 | 36 | 38 | 38 | 36 | 38 +- * ign FIF Ovl | 0 | 0 | 0 | 0 | 0 | 0 +- * FIF max Atn | 5 | 5 | 5 | 5 | 5 | 5 +- * PD2 Target | 40 | 33 | 42 | 42 | 33 | 42 +- */ +- +-enum mt2063_delivery_sys { +- MT2063_CABLE_QAM = 0, +- MT2063_CABLE_ANALOG, +- MT2063_OFFAIR_COFDM, +- MT2063_OFFAIR_COFDM_SAWLESS, +- MT2063_OFFAIR_ANALOG, +- MT2063_OFFAIR_8VSB, +- MT2063_NUM_RCVR_MODES +-}; +- +-static const char *mt2063_mode_name[] = { +- [MT2063_CABLE_QAM] = "digital cable", +- [MT2063_CABLE_ANALOG] = "analog cable", +- [MT2063_OFFAIR_COFDM] = "digital offair", +- [MT2063_OFFAIR_COFDM_SAWLESS] = "digital offair without SAW", +- [MT2063_OFFAIR_ANALOG] = "analog offair", +- [MT2063_OFFAIR_8VSB] = "analog offair 8vsb", +-}; +- +-static const u8 RFAGCEN[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 LNARIN[] = { 0, 0, 3, 3, 3, 3 }; +-static const u8 FIFFQEN[] = { 1, 1, 1, 1, 1, 1 }; +-static const u8 FIFFQ[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 DNC1GC[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 DNC2GC[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 ACLNAMAX[] = { 31, 31, 31, 31, 31, 31 }; +-static const u8 LNATGT[] = { 44, 43, 43, 43, 43, 43 }; +-static const u8 RFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 ACRFMAX[] = { 31, 31, 31, 31, 31, 31 }; +-static const u8 PD1TGT[] = { 36, 36, 38, 38, 36, 38 }; +-static const u8 FIFOVDIS[] = { 0, 0, 0, 0, 0, 0 }; +-static const u8 ACFIFMAX[] = { 29, 29, 29, 29, 29, 29 }; +-static const u8 PD2TGT[] = { 40, 33, 38, 42, 30, 38 }; +- +-/* +- * mt2063_set_dnc_output_enable() +- */ +-static u32 mt2063_get_dnc_output_enable(struct mt2063_state *state, +- enum MT2063_DNC_Output_Enable *pValue) +-{ +- dprintk(2, "\n"); +- +- if ((state->reg[MT2063_REG_DNC_GAIN] & 0x03) == 0x03) { /* if DNC1 is off */ +- if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ +- *pValue = MT2063_DNC_NONE; +- else +- *pValue = MT2063_DNC_2; +- } else { /* DNC1 is on */ +- if ((state->reg[MT2063_REG_VGA_GAIN] & 0x03) == 0x03) /* if DNC2 is off */ +- *pValue = MT2063_DNC_1; +- else +- *pValue = MT2063_DNC_BOTH; +- } +- return 0; +-} +- +-/* +- * mt2063_set_dnc_output_enable() +- */ +-static u32 mt2063_set_dnc_output_enable(struct mt2063_state *state, +- enum MT2063_DNC_Output_Enable nValue) +-{ +- u32 status = 0; /* Status to be returned */ +- u8 val = 0; +- +- dprintk(2, "\n"); +- +- /* selects, which DNC output is used */ +- switch (nValue) { +- case MT2063_DNC_NONE: +- val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ +- if (state->reg[MT2063_REG_DNC_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_DNC_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ +- if (state->reg[MT2063_REG_VGA_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_VGA_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ +- if (state->reg[MT2063_REG_RSVD_20] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_RSVD_20, +- val); +- +- break; +- case MT2063_DNC_1: +- val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ +- if (state->reg[MT2063_REG_DNC_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_DNC_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | 0x03; /* Set DNC2GC=3 */ +- if (state->reg[MT2063_REG_VGA_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_VGA_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_RSVD_20] & ~0x40); /* Set PD2MUX=0 */ +- if (state->reg[MT2063_REG_RSVD_20] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_RSVD_20, +- val); +- +- break; +- case MT2063_DNC_2: +- val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | 0x03; /* Set DNC1GC=3 */ +- if (state->reg[MT2063_REG_DNC_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_DNC_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ +- if (state->reg[MT2063_REG_VGA_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_VGA_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ +- if (state->reg[MT2063_REG_RSVD_20] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_RSVD_20, +- val); +- +- break; +- case MT2063_DNC_BOTH: +- val = (state->reg[MT2063_REG_DNC_GAIN] & 0xFC) | (DNC1GC[state->rcvr_mode] & 0x03); /* Set DNC1GC=x */ +- if (state->reg[MT2063_REG_DNC_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_DNC_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_VGA_GAIN] & 0xFC) | (DNC2GC[state->rcvr_mode] & 0x03); /* Set DNC2GC=x */ +- if (state->reg[MT2063_REG_VGA_GAIN] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_VGA_GAIN, +- val); +- +- val = (state->reg[MT2063_REG_RSVD_20] | 0x40); /* Set PD2MUX=1 */ +- if (state->reg[MT2063_REG_RSVD_20] != +- val) +- status |= +- mt2063_setreg(state, +- MT2063_REG_RSVD_20, +- val); +- +- break; +- default: +- break; +- } +- +- return status; +-} +- +-/* +- * MT2063_SetReceiverMode() - Set the MT2063 receiver mode, according with +- * the selected enum mt2063_delivery_sys type. +- * +- * (DNC1GC & DNC2GC are the values, which are used, when the specific +- * DNC Output is selected, the other is always off) +- * +- * @state: ptr to mt2063_state structure +- * @Mode: desired reciever delivery system +- * +- * Note: Register cache must be valid for it to work +- */ +- +-static u32 MT2063_SetReceiverMode(struct mt2063_state *state, +- enum mt2063_delivery_sys Mode) +-{ +- u32 status = 0; /* Status to be returned */ +- u8 val; +- u32 longval; +- +- dprintk(2, "\n"); +- +- if (Mode >= MT2063_NUM_RCVR_MODES) +- status = -ERANGE; +- +- /* RFAGCen */ +- if (status >= 0) { +- val = +- (state-> +- reg[MT2063_REG_PD1_TGT] & (u8) ~0x40) | (RFAGCEN[Mode] +- ? 0x40 : +- 0x00); +- if (state->reg[MT2063_REG_PD1_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); +- } +- +- /* LNARin */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_CTRL_2C] & (u8) ~0x03) | +- (LNARIN[Mode] & 0x03); +- if (state->reg[MT2063_REG_CTRL_2C] != val) +- status |= mt2063_setreg(state, MT2063_REG_CTRL_2C, val); +- } +- +- /* FIFFQEN and FIFFQ */ +- if (status >= 0) { +- val = +- (state-> +- reg[MT2063_REG_FIFF_CTRL2] & (u8) ~0xF0) | +- (FIFFQEN[Mode] << 7) | (FIFFQ[Mode] << 4); +- if (state->reg[MT2063_REG_FIFF_CTRL2] != val) { +- status |= +- mt2063_setreg(state, MT2063_REG_FIFF_CTRL2, val); +- /* trigger FIFF calibration, needed after changing FIFFQ */ +- val = +- (state->reg[MT2063_REG_FIFF_CTRL] | (u8) 0x01); +- status |= +- mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); +- val = +- (state-> +- reg[MT2063_REG_FIFF_CTRL] & (u8) ~0x01); +- status |= +- mt2063_setreg(state, MT2063_REG_FIFF_CTRL, val); +- } +- } +- +- /* DNC1GC & DNC2GC */ +- status |= mt2063_get_dnc_output_enable(state, &longval); +- status |= mt2063_set_dnc_output_enable(state, longval); +- +- /* acLNAmax */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_LNA_OV] & (u8) ~0x1F) | +- (ACLNAMAX[Mode] & 0x1F); +- if (state->reg[MT2063_REG_LNA_OV] != val) +- status |= mt2063_setreg(state, MT2063_REG_LNA_OV, val); +- } +- +- /* LNATGT */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x3F) | +- (LNATGT[Mode] & 0x3F); +- if (state->reg[MT2063_REG_LNA_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); +- } +- +- /* ACRF */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_RF_OV] & (u8) ~0x1F) | +- (ACRFMAX[Mode] & 0x1F); +- if (state->reg[MT2063_REG_RF_OV] != val) +- status |= mt2063_setreg(state, MT2063_REG_RF_OV, val); +- } +- +- /* PD1TGT */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x3F) | +- (PD1TGT[Mode] & 0x3F); +- if (state->reg[MT2063_REG_PD1_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); +- } +- +- /* FIFATN */ +- if (status >= 0) { +- u8 val = ACFIFMAX[Mode]; +- if (state->reg[MT2063_REG_PART_REV] != MT2063_B3 && val > 5) +- val = 5; +- val = (state->reg[MT2063_REG_FIF_OV] & (u8) ~0x1F) | +- (val & 0x1F); +- if (state->reg[MT2063_REG_FIF_OV] != val) +- status |= mt2063_setreg(state, MT2063_REG_FIF_OV, val); +- } +- +- /* PD2TGT */ +- if (status >= 0) { +- u8 val = (state->reg[MT2063_REG_PD2_TGT] & (u8) ~0x3F) | +- (PD2TGT[Mode] & 0x3F); +- if (state->reg[MT2063_REG_PD2_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_PD2_TGT, val); +- } +- +- /* Ignore ATN Overload */ +- if (status >= 0) { +- val = (state->reg[MT2063_REG_LNA_TGT] & (u8) ~0x80) | +- (RFOVDIS[Mode] ? 0x80 : 0x00); +- if (state->reg[MT2063_REG_LNA_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_LNA_TGT, val); +- } +- +- /* Ignore FIF Overload */ +- if (status >= 0) { +- val = (state->reg[MT2063_REG_PD1_TGT] & (u8) ~0x80) | +- (FIFOVDIS[Mode] ? 0x80 : 0x00); +- if (state->reg[MT2063_REG_PD1_TGT] != val) +- status |= mt2063_setreg(state, MT2063_REG_PD1_TGT, val); +- } +- +- if (status >= 0) { +- state->rcvr_mode = Mode; +- dprintk(1, "mt2063 mode changed to %s\n", +- mt2063_mode_name[state->rcvr_mode]); +- } +- +- return status; +-} +- +-/* +- * MT2063_ClearPowerMaskBits () - Clears the power-down mask bits for various +- * sections of the MT2063 +- * +- * @Bits: Mask bits to be cleared. +- * +- * See definition of MT2063_Mask_Bits type for description +- * of each of the power bits. +- */ +-static u32 MT2063_ClearPowerMaskBits(struct mt2063_state *state, +- enum MT2063_Mask_Bits Bits) +-{ +- u32 status = 0; +- +- dprintk(2, "\n"); +- Bits = (enum MT2063_Mask_Bits)(Bits & MT2063_ALL_SD); /* Only valid bits for this tuner */ +- if ((Bits & 0xFF00) != 0) { +- state->reg[MT2063_REG_PWR_2] &= ~(u8) (Bits >> 8); +- status |= +- mt2063_write(state, +- MT2063_REG_PWR_2, +- &state->reg[MT2063_REG_PWR_2], 1); +- } +- if ((Bits & 0xFF) != 0) { +- state->reg[MT2063_REG_PWR_1] &= ~(u8) (Bits & 0xFF); +- status |= +- mt2063_write(state, +- MT2063_REG_PWR_1, +- &state->reg[MT2063_REG_PWR_1], 1); +- } +- +- return status; +-} +- +-/* +- * MT2063_SoftwareShutdown() - Enables or disables software shutdown function. +- * When Shutdown is 1, any section whose power +- * mask is set will be shutdown. +- */ +-static u32 MT2063_SoftwareShutdown(struct mt2063_state *state, u8 Shutdown) +-{ +- u32 status; +- +- dprintk(2, "\n"); +- if (Shutdown == 1) +- state->reg[MT2063_REG_PWR_1] |= 0x04; +- else +- state->reg[MT2063_REG_PWR_1] &= ~0x04; +- +- status = mt2063_write(state, +- MT2063_REG_PWR_1, +- &state->reg[MT2063_REG_PWR_1], 1); +- +- if (Shutdown != 1) { +- state->reg[MT2063_REG_BYP_CTRL] = +- (state->reg[MT2063_REG_BYP_CTRL] & 0x9F) | 0x40; +- status |= +- mt2063_write(state, +- MT2063_REG_BYP_CTRL, +- &state->reg[MT2063_REG_BYP_CTRL], +- 1); +- state->reg[MT2063_REG_BYP_CTRL] = +- (state->reg[MT2063_REG_BYP_CTRL] & 0x9F); +- status |= +- mt2063_write(state, +- MT2063_REG_BYP_CTRL, +- &state->reg[MT2063_REG_BYP_CTRL], +- 1); +- } +- +- return status; +-} +- +-static u32 MT2063_Round_fLO(u32 f_LO, u32 f_LO_Step, u32 f_ref) +-{ +- return f_ref * (f_LO / f_ref) +- + f_LO_Step * (((f_LO % f_ref) + (f_LO_Step / 2)) / f_LO_Step); +-} +- +-/** +- * fLO_FractionalTerm() - Calculates the portion contributed by FracN / denom. +- * This function preserves maximum precision without +- * risk of overflow. It accurately calculates +- * f_ref * num / denom to within 1 HZ with fixed math. +- * +- * @num : Fractional portion of the multiplier +- * @denom: denominator portion of the ratio +- * @f_Ref: SRO frequency. +- * +- * This calculation handles f_ref as two separate 14-bit fields. +- * Therefore, a maximum value of 2^28-1 may safely be used for f_ref. +- * This is the genesis of the magic number "14" and the magic mask value of +- * 0x03FFF. +- * +- * This routine successfully handles denom values up to and including 2^18. +- * Returns: f_ref * num / denom +- */ +-static u32 MT2063_fLO_FractionalTerm(u32 f_ref, u32 num, u32 denom) +-{ +- u32 t1 = (f_ref >> 14) * num; +- u32 term1 = t1 / denom; +- u32 loss = t1 % denom; +- u32 term2 = +- (((f_ref & 0x00003FFF) * num + (loss << 14)) + (denom / 2)) / denom; +- return (term1 << 14) + term2; +-} +- +-/* +- * CalcLO1Mult()- Calculates Integer divider value and the numerator +- * value for a FracN PLL. +- * +- * This function assumes that the f_LO and f_Ref are +- * evenly divisible by f_LO_Step. +- * +- * @Div: OUTPUT: Whole number portion of the multiplier +- * @FracN: OUTPUT: Fractional portion of the multiplier +- * @f_LO: desired LO frequency. +- * @f_LO_Step: Minimum step size for the LO (in Hz). +- * @f_Ref: SRO frequency. +- * @f_Avoid: Range of PLL frequencies to avoid near integer multiples +- * of f_Ref (in Hz). +- * +- * Returns: Recalculated LO frequency. +- */ +-static u32 MT2063_CalcLO1Mult(u32 *Div, +- u32 *FracN, +- u32 f_LO, +- u32 f_LO_Step, u32 f_Ref) +-{ +- /* Calculate the whole number portion of the divider */ +- *Div = f_LO / f_Ref; +- +- /* Calculate the numerator value (round to nearest f_LO_Step) */ +- *FracN = +- (64 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + +- (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); +- +- return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, 64); +-} +- +-/** +- * CalcLO2Mult() - Calculates Integer divider value and the numerator +- * value for a FracN PLL. +- * +- * This function assumes that the f_LO and f_Ref are +- * evenly divisible by f_LO_Step. +- * +- * @Div: OUTPUT: Whole number portion of the multiplier +- * @FracN: OUTPUT: Fractional portion of the multiplier +- * @f_LO: desired LO frequency. +- * @f_LO_Step: Minimum step size for the LO (in Hz). +- * @f_Ref: SRO frequency. +- * @f_Avoid: Range of PLL frequencies to avoid near +- * integer multiples of f_Ref (in Hz). +- * +- * Returns: Recalculated LO frequency. +- */ +-static u32 MT2063_CalcLO2Mult(u32 *Div, +- u32 *FracN, +- u32 f_LO, +- u32 f_LO_Step, u32 f_Ref) +-{ +- /* Calculate the whole number portion of the divider */ +- *Div = f_LO / f_Ref; +- +- /* Calculate the numerator value (round to nearest f_LO_Step) */ +- *FracN = +- (8191 * (((f_LO % f_Ref) + (f_LO_Step / 2)) / f_LO_Step) + +- (f_Ref / f_LO_Step / 2)) / (f_Ref / f_LO_Step); +- +- return (f_Ref * (*Div)) + MT2063_fLO_FractionalTerm(f_Ref, *FracN, +- 8191); +-} +- +-/* +- * FindClearTuneFilter() - Calculate the corrrect ClearTune filter to be +- * used for a given input frequency. +- * +- * @state: ptr to tuner data structure +- * @f_in: RF input center frequency (in Hz). +- * +- * Returns: ClearTune filter number (0-31) +- */ +-static u32 FindClearTuneFilter(struct mt2063_state *state, u32 f_in) +-{ +- u32 RFBand; +- u32 idx; /* index loop */ +- +- /* +- ** Find RF Band setting +- */ +- RFBand = 31; /* def when f_in > all */ +- for (idx = 0; idx < 31; ++idx) { +- if (state->CTFiltMax[idx] >= f_in) { +- RFBand = idx; +- break; +- } +- } +- return RFBand; +-} +- +-/* +- * MT2063_Tune() - Change the tuner's tuned frequency to RFin. +- */ +-static u32 MT2063_Tune(struct mt2063_state *state, u32 f_in) +-{ /* RF input center frequency */ +- +- u32 status = 0; +- u32 LO1; /* 1st LO register value */ +- u32 Num1; /* Numerator for LO1 reg. value */ +- u32 f_IF1; /* 1st IF requested */ +- u32 LO2; /* 2nd LO register value */ +- u32 Num2; /* Numerator for LO2 reg. value */ +- u32 ofLO1, ofLO2; /* last time's LO frequencies */ +- u8 fiffc = 0x80; /* FIFF center freq from tuner */ +- u32 fiffof; /* Offset from FIFF center freq */ +- const u8 LO1LK = 0x80; /* Mask for LO1 Lock bit */ +- u8 LO2LK = 0x08; /* Mask for LO2 Lock bit */ +- u8 val; +- u32 RFBand; +- +- dprintk(2, "\n"); +- /* Check the input and output frequency ranges */ +- if ((f_in < MT2063_MIN_FIN_FREQ) || (f_in > MT2063_MAX_FIN_FREQ)) +- return -EINVAL; +- +- if ((state->AS_Data.f_out < MT2063_MIN_FOUT_FREQ) +- || (state->AS_Data.f_out > MT2063_MAX_FOUT_FREQ)) +- return -EINVAL; +- +- /* +- * Save original LO1 and LO2 register values +- */ +- ofLO1 = state->AS_Data.f_LO1; +- ofLO2 = state->AS_Data.f_LO2; +- +- /* +- * Find and set RF Band setting +- */ +- if (state->ctfilt_sw == 1) { +- val = (state->reg[MT2063_REG_CTUNE_CTRL] | 0x08); +- if (state->reg[MT2063_REG_CTUNE_CTRL] != val) { +- status |= +- mt2063_setreg(state, MT2063_REG_CTUNE_CTRL, val); +- } +- val = state->reg[MT2063_REG_CTUNE_OV]; +- RFBand = FindClearTuneFilter(state, f_in); +- state->reg[MT2063_REG_CTUNE_OV] = +- (u8) ((state->reg[MT2063_REG_CTUNE_OV] & ~0x1F) +- | RFBand); +- if (state->reg[MT2063_REG_CTUNE_OV] != val) { +- status |= +- mt2063_setreg(state, MT2063_REG_CTUNE_OV, val); +- } +- } +- +- /* +- * Read the FIFF Center Frequency from the tuner +- */ +- if (status >= 0) { +- status |= +- mt2063_read(state, +- MT2063_REG_FIFFC, +- &state->reg[MT2063_REG_FIFFC], 1); +- fiffc = state->reg[MT2063_REG_FIFFC]; +- } +- /* +- * Assign in the requested values +- */ +- state->AS_Data.f_in = f_in; +- /* Request a 1st IF such that LO1 is on a step size */ +- state->AS_Data.f_if1_Request = +- MT2063_Round_fLO(state->AS_Data.f_if1_Request + f_in, +- state->AS_Data.f_LO1_Step, +- state->AS_Data.f_ref) - f_in; +- +- /* +- * Calculate frequency settings. f_IF1_FREQ + f_in is the +- * desired LO1 frequency +- */ +- MT2063_ResetExclZones(&state->AS_Data); +- +- f_IF1 = MT2063_ChooseFirstIF(&state->AS_Data); +- +- state->AS_Data.f_LO1 = +- MT2063_Round_fLO(f_IF1 + f_in, state->AS_Data.f_LO1_Step, +- state->AS_Data.f_ref); +- +- state->AS_Data.f_LO2 = +- MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, +- state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); +- +- /* +- * Check for any LO spurs in the output bandwidth and adjust +- * the LO settings to avoid them if needed +- */ +- status |= MT2063_AvoidSpurs(&state->AS_Data); +- /* +- * MT_AvoidSpurs spurs may have changed the LO1 & LO2 values. +- * Recalculate the LO frequencies and the values to be placed +- * in the tuning registers. +- */ +- state->AS_Data.f_LO1 = +- MT2063_CalcLO1Mult(&LO1, &Num1, state->AS_Data.f_LO1, +- state->AS_Data.f_LO1_Step, state->AS_Data.f_ref); +- state->AS_Data.f_LO2 = +- MT2063_Round_fLO(state->AS_Data.f_LO1 - state->AS_Data.f_out - f_in, +- state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); +- state->AS_Data.f_LO2 = +- MT2063_CalcLO2Mult(&LO2, &Num2, state->AS_Data.f_LO2, +- state->AS_Data.f_LO2_Step, state->AS_Data.f_ref); +- +- /* +- * Check the upconverter and downconverter frequency ranges +- */ +- if ((state->AS_Data.f_LO1 < MT2063_MIN_UPC_FREQ) +- || (state->AS_Data.f_LO1 > MT2063_MAX_UPC_FREQ)) +- status |= MT2063_UPC_RANGE; +- if ((state->AS_Data.f_LO2 < MT2063_MIN_DNC_FREQ) +- || (state->AS_Data.f_LO2 > MT2063_MAX_DNC_FREQ)) +- status |= MT2063_DNC_RANGE; +- /* LO2 Lock bit was in a different place for B0 version */ +- if (state->tuner_id == MT2063_B0) +- LO2LK = 0x40; +- +- /* +- * If we have the same LO frequencies and we're already locked, +- * then skip re-programming the LO registers. +- */ +- if ((ofLO1 != state->AS_Data.f_LO1) +- || (ofLO2 != state->AS_Data.f_LO2) +- || ((state->reg[MT2063_REG_LO_STATUS] & (LO1LK | LO2LK)) != +- (LO1LK | LO2LK))) { +- /* +- * Calculate the FIFFOF register value +- * +- * IF1_Actual +- * FIFFOF = ------------ - 8 * FIFFC - 4992 +- * f_ref/64 +- */ +- fiffof = +- (state->AS_Data.f_LO1 - +- f_in) / (state->AS_Data.f_ref / 64) - 8 * (u32) fiffc - +- 4992; +- if (fiffof > 0xFF) +- fiffof = 0xFF; +- +- /* +- * Place all of the calculated values into the local tuner +- * register fields. +- */ +- if (status >= 0) { +- state->reg[MT2063_REG_LO1CQ_1] = (u8) (LO1 & 0xFF); /* DIV1q */ +- state->reg[MT2063_REG_LO1CQ_2] = (u8) (Num1 & 0x3F); /* NUM1q */ +- state->reg[MT2063_REG_LO2CQ_1] = (u8) (((LO2 & 0x7F) << 1) /* DIV2q */ +- |(Num2 >> 12)); /* NUM2q (hi) */ +- state->reg[MT2063_REG_LO2CQ_2] = (u8) ((Num2 & 0x0FF0) >> 4); /* NUM2q (mid) */ +- state->reg[MT2063_REG_LO2CQ_3] = (u8) (0xE0 | (Num2 & 0x000F)); /* NUM2q (lo) */ +- +- /* +- * Now write out the computed register values +- * IMPORTANT: There is a required order for writing +- * (0x05 must follow all the others). +- */ +- status |= mt2063_write(state, MT2063_REG_LO1CQ_1, &state->reg[MT2063_REG_LO1CQ_1], 5); /* 0x01 - 0x05 */ +- if (state->tuner_id == MT2063_B0) { +- /* Re-write the one-shot bits to trigger the tune operation */ +- status |= mt2063_write(state, MT2063_REG_LO2CQ_3, &state->reg[MT2063_REG_LO2CQ_3], 1); /* 0x05 */ +- } +- /* Write out the FIFF offset only if it's changing */ +- if (state->reg[MT2063_REG_FIFF_OFFSET] != +- (u8) fiffof) { +- state->reg[MT2063_REG_FIFF_OFFSET] = +- (u8) fiffof; +- status |= +- mt2063_write(state, +- MT2063_REG_FIFF_OFFSET, +- &state-> +- reg[MT2063_REG_FIFF_OFFSET], +- 1); +- } +- } +- +- /* +- * Check for LO's locking +- */ +- +- if (status < 0) +- return status; +- +- status = mt2063_lockStatus(state); +- if (status < 0) +- return status; +- if (!status) +- return -EINVAL; /* Couldn't lock */ +- +- /* +- * If we locked OK, assign calculated data to mt2063_state structure +- */ +- state->f_IF1_actual = state->AS_Data.f_LO1 - f_in; +- } +- +- return status; +-} +- +-static const u8 MT2063B0_defaults[] = { +- /* Reg, Value */ +- 0x19, 0x05, +- 0x1B, 0x1D, +- 0x1C, 0x1F, +- 0x1D, 0x0F, +- 0x1E, 0x3F, +- 0x1F, 0x0F, +- 0x20, 0x3F, +- 0x22, 0x21, +- 0x23, 0x3F, +- 0x24, 0x20, +- 0x25, 0x3F, +- 0x27, 0xEE, +- 0x2C, 0x27, /* bit at 0x20 is cleared below */ +- 0x30, 0x03, +- 0x2C, 0x07, /* bit at 0x20 is cleared here */ +- 0x2D, 0x87, +- 0x2E, 0xAA, +- 0x28, 0xE1, /* Set the FIFCrst bit here */ +- 0x28, 0xE0, /* Clear the FIFCrst bit here */ +- 0x00 +-}; +- +-/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ +-static const u8 MT2063B1_defaults[] = { +- /* Reg, Value */ +- 0x05, 0xF0, +- 0x11, 0x10, /* New Enable AFCsd */ +- 0x19, 0x05, +- 0x1A, 0x6C, +- 0x1B, 0x24, +- 0x1C, 0x28, +- 0x1D, 0x8F, +- 0x1E, 0x14, +- 0x1F, 0x8F, +- 0x20, 0x57, +- 0x22, 0x21, /* New - ver 1.03 */ +- 0x23, 0x3C, /* New - ver 1.10 */ +- 0x24, 0x20, /* New - ver 1.03 */ +- 0x2C, 0x24, /* bit at 0x20 is cleared below */ +- 0x2D, 0x87, /* FIFFQ=0 */ +- 0x2F, 0xF3, +- 0x30, 0x0C, /* New - ver 1.11 */ +- 0x31, 0x1B, /* New - ver 1.11 */ +- 0x2C, 0x04, /* bit at 0x20 is cleared here */ +- 0x28, 0xE1, /* Set the FIFCrst bit here */ +- 0x28, 0xE0, /* Clear the FIFCrst bit here */ +- 0x00 +-}; +- +-/* writing 0x05 0xf0 sw-resets all registers, so we write only needed changes */ +-static const u8 MT2063B3_defaults[] = { +- /* Reg, Value */ +- 0x05, 0xF0, +- 0x19, 0x3D, +- 0x2C, 0x24, /* bit at 0x20 is cleared below */ +- 0x2C, 0x04, /* bit at 0x20 is cleared here */ +- 0x28, 0xE1, /* Set the FIFCrst bit here */ +- 0x28, 0xE0, /* Clear the FIFCrst bit here */ +- 0x00 +-}; +- +-static int mt2063_init(struct dvb_frontend *fe) +-{ +- u32 status; +- struct mt2063_state *state = fe->tuner_priv; +- u8 all_resets = 0xF0; /* reset/load bits */ +- const u8 *def = NULL; +- char *step; +- u32 FCRUN; +- s32 maxReads; +- u32 fcu_osc; +- u32 i; +- +- dprintk(2, "\n"); +- +- state->rcvr_mode = MT2063_CABLE_QAM; +- +- /* Read the Part/Rev code from the tuner */ +- status = mt2063_read(state, MT2063_REG_PART_REV, +- &state->reg[MT2063_REG_PART_REV], 1); +- if (status < 0) { +- printk(KERN_ERR "Can't read mt2063 part ID\n"); +- return status; +- } +- +- /* Check the part/rev code */ +- switch (state->reg[MT2063_REG_PART_REV]) { +- case MT2063_B0: +- step = "B0"; +- break; +- case MT2063_B1: +- step = "B1"; +- break; +- case MT2063_B2: +- step = "B2"; +- break; +- case MT2063_B3: +- step = "B3"; +- break; +- default: +- printk(KERN_ERR "mt2063: Unknown mt2063 device ID (0x%02x)\n", +- state->reg[MT2063_REG_PART_REV]); +- return -ENODEV; /* Wrong tuner Part/Rev code */ +- } +- +- /* Check the 2nd byte of the Part/Rev code from the tuner */ +- status = mt2063_read(state, MT2063_REG_RSVD_3B, +- &state->reg[MT2063_REG_RSVD_3B], 1); +- +- /* b7 != 0 ==> NOT MT2063 */ +- if (status < 0 || ((state->reg[MT2063_REG_RSVD_3B] & 0x80) != 0x00)) { +- printk(KERN_ERR "mt2063: Unknown part ID (0x%02x%02x)\n", +- state->reg[MT2063_REG_PART_REV], +- state->reg[MT2063_REG_RSVD_3B]); +- return -ENODEV; /* Wrong tuner Part/Rev code */ +- } +- +- printk(KERN_INFO "mt2063: detected a mt2063 %s\n", step); +- +- /* Reset the tuner */ +- status = mt2063_write(state, MT2063_REG_LO2CQ_3, &all_resets, 1); +- if (status < 0) +- return status; +- +- /* change all of the default values that vary from the HW reset values */ +- /* def = (state->reg[PART_REV] == MT2063_B0) ? MT2063B0_defaults : MT2063B1_defaults; */ +- switch (state->reg[MT2063_REG_PART_REV]) { +- case MT2063_B3: +- def = MT2063B3_defaults; +- break; +- +- case MT2063_B1: +- def = MT2063B1_defaults; +- break; +- +- case MT2063_B0: +- def = MT2063B0_defaults; +- break; +- +- default: +- return -ENODEV; +- break; +- } +- +- while (status >= 0 && *def) { +- u8 reg = *def++; +- u8 val = *def++; +- status = mt2063_write(state, reg, &val, 1); +- } +- if (status < 0) +- return status; +- +- /* Wait for FIFF location to complete. */ +- FCRUN = 1; +- maxReads = 10; +- while (status >= 0 && (FCRUN != 0) && (maxReads-- > 0)) { +- msleep(2); +- status = mt2063_read(state, +- MT2063_REG_XO_STATUS, +- &state-> +- reg[MT2063_REG_XO_STATUS], 1); +- FCRUN = (state->reg[MT2063_REG_XO_STATUS] & 0x40) >> 6; +- } +- +- if (FCRUN != 0 || status < 0) +- return -ENODEV; +- +- status = mt2063_read(state, +- MT2063_REG_FIFFC, +- &state->reg[MT2063_REG_FIFFC], 1); +- if (status < 0) +- return status; +- +- /* Read back all the registers from the tuner */ +- status = mt2063_read(state, +- MT2063_REG_PART_REV, +- state->reg, MT2063_REG_END_REGS); +- if (status < 0) +- return status; +- +- /* Initialize the tuner state. */ +- state->tuner_id = state->reg[MT2063_REG_PART_REV]; +- state->AS_Data.f_ref = MT2063_REF_FREQ; +- state->AS_Data.f_if1_Center = (state->AS_Data.f_ref / 8) * +- ((u32) state->reg[MT2063_REG_FIFFC] + 640); +- state->AS_Data.f_if1_bw = MT2063_IF1_BW; +- state->AS_Data.f_out = 43750000UL; +- state->AS_Data.f_out_bw = 6750000UL; +- state->AS_Data.f_zif_bw = MT2063_ZIF_BW; +- state->AS_Data.f_LO1_Step = state->AS_Data.f_ref / 64; +- state->AS_Data.f_LO2_Step = MT2063_TUNE_STEP_SIZE; +- state->AS_Data.maxH1 = MT2063_MAX_HARMONICS_1; +- state->AS_Data.maxH2 = MT2063_MAX_HARMONICS_2; +- state->AS_Data.f_min_LO_Separation = MT2063_MIN_LO_SEP; +- state->AS_Data.f_if1_Request = state->AS_Data.f_if1_Center; +- state->AS_Data.f_LO1 = 2181000000UL; +- state->AS_Data.f_LO2 = 1486249786UL; +- state->f_IF1_actual = state->AS_Data.f_if1_Center; +- state->AS_Data.f_in = state->AS_Data.f_LO1 - state->f_IF1_actual; +- state->AS_Data.f_LO1_FracN_Avoid = MT2063_LO1_FRACN_AVOID; +- state->AS_Data.f_LO2_FracN_Avoid = MT2063_LO2_FRACN_AVOID; +- state->num_regs = MT2063_REG_END_REGS; +- state->AS_Data.avoidDECT = MT2063_AVOID_BOTH; +- state->ctfilt_sw = 0; +- +- state->CTFiltMax[0] = 69230000; +- state->CTFiltMax[1] = 105770000; +- state->CTFiltMax[2] = 140350000; +- state->CTFiltMax[3] = 177110000; +- state->CTFiltMax[4] = 212860000; +- state->CTFiltMax[5] = 241130000; +- state->CTFiltMax[6] = 274370000; +- state->CTFiltMax[7] = 309820000; +- state->CTFiltMax[8] = 342450000; +- state->CTFiltMax[9] = 378870000; +- state->CTFiltMax[10] = 416210000; +- state->CTFiltMax[11] = 456500000; +- state->CTFiltMax[12] = 495790000; +- state->CTFiltMax[13] = 534530000; +- state->CTFiltMax[14] = 572610000; +- state->CTFiltMax[15] = 598970000; +- state->CTFiltMax[16] = 635910000; +- state->CTFiltMax[17] = 672130000; +- state->CTFiltMax[18] = 714840000; +- state->CTFiltMax[19] = 739660000; +- state->CTFiltMax[20] = 770410000; +- state->CTFiltMax[21] = 814660000; +- state->CTFiltMax[22] = 846950000; +- state->CTFiltMax[23] = 867820000; +- state->CTFiltMax[24] = 915980000; +- state->CTFiltMax[25] = 947450000; +- state->CTFiltMax[26] = 983110000; +- state->CTFiltMax[27] = 1021630000; +- state->CTFiltMax[28] = 1061870000; +- state->CTFiltMax[29] = 1098330000; +- state->CTFiltMax[30] = 1138990000; +- +- /* +- ** Fetch the FCU osc value and use it and the fRef value to +- ** scale all of the Band Max values +- */ +- +- state->reg[MT2063_REG_CTUNE_CTRL] = 0x0A; +- status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, +- &state->reg[MT2063_REG_CTUNE_CTRL], 1); +- if (status < 0) +- return status; +- +- /* Read the ClearTune filter calibration value */ +- status = mt2063_read(state, MT2063_REG_FIFFC, +- &state->reg[MT2063_REG_FIFFC], 1); +- if (status < 0) +- return status; +- +- fcu_osc = state->reg[MT2063_REG_FIFFC]; +- +- state->reg[MT2063_REG_CTUNE_CTRL] = 0x00; +- status = mt2063_write(state, MT2063_REG_CTUNE_CTRL, +- &state->reg[MT2063_REG_CTUNE_CTRL], 1); +- if (status < 0) +- return status; +- +- /* Adjust each of the values in the ClearTune filter cross-over table */ +- for (i = 0; i < 31; i++) +- state->CTFiltMax[i] = (state->CTFiltMax[i] / 768) * (fcu_osc + 640); +- +- status = MT2063_SoftwareShutdown(state, 1); +- if (status < 0) +- return status; +- status = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); +- if (status < 0) +- return status; +- +- state->init = true; +- +- return 0; +-} +- +-static int mt2063_get_status(struct dvb_frontend *fe, u32 *tuner_status) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- int status; +- +- dprintk(2, "\n"); +- +- if (!state->init) +- return -ENODEV; +- +- *tuner_status = 0; +- status = mt2063_lockStatus(state); +- if (status < 0) +- return status; +- if (status) +- *tuner_status = TUNER_STATUS_LOCKED; +- +- dprintk(1, "Tuner status: %d", *tuner_status); +- +- return 0; +-} +- +-static int mt2063_release(struct dvb_frontend *fe) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- +- dprintk(2, "\n"); +- +- fe->tuner_priv = NULL; +- kfree(state); +- +- return 0; +-} +- +-static int mt2063_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- s32 pict_car; +- s32 pict2chanb_vsb; +- s32 ch_bw; +- s32 if_mid; +- s32 rcvr_mode; +- int status; +- +- dprintk(2, "\n"); +- +- if (!state->init) { +- status = mt2063_init(fe); +- if (status < 0) +- return status; +- } +- +- switch (params->mode) { +- case V4L2_TUNER_RADIO: +- pict_car = 38900000; +- ch_bw = 8000000; +- pict2chanb_vsb = -(ch_bw / 2); +- rcvr_mode = MT2063_OFFAIR_ANALOG; +- break; +- case V4L2_TUNER_ANALOG_TV: +- rcvr_mode = MT2063_CABLE_ANALOG; +- if (params->std & ~V4L2_STD_MN) { +- pict_car = 38900000; +- ch_bw = 6000000; +- pict2chanb_vsb = -1250000; +- } else if (params->std & V4L2_STD_PAL_G) { +- pict_car = 38900000; +- ch_bw = 7000000; +- pict2chanb_vsb = -1250000; +- } else { /* PAL/SECAM standards */ +- pict_car = 38900000; +- ch_bw = 8000000; +- pict2chanb_vsb = -1250000; +- } +- break; +- default: +- return -EINVAL; +- } +- if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); +- +- state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ +- state->AS_Data.f_out = if_mid; +- state->AS_Data.f_out_bw = ch_bw + 750000; +- status = MT2063_SetReceiverMode(state, rcvr_mode); +- if (status < 0) +- return status; +- +- dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", +- params->frequency, ch_bw, pict2chanb_vsb); +- +- status = MT2063_Tune(state, (params->frequency + (pict2chanb_vsb + (ch_bw / 2)))); +- if (status < 0) +- return status; +- +- state->frequency = params->frequency; +- return 0; +-} +- +-/* +- * As defined on EN 300 429, the DVB-C roll-off factor is 0.15. +- * So, the amount of the needed bandwith is given by: +- * Bw = Symbol_rate * (1 + 0.15) +- * As such, the maximum symbol rate supported by 6 MHz is given by: +- * max_symbol_rate = 6 MHz / 1.15 = 5217391 Bauds +- */ +-#define MAX_SYMBOL_RATE_6MHz 5217391 +- +-static int mt2063_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct mt2063_state *state = fe->tuner_priv; +- int status; +- s32 pict_car; +- s32 pict2chanb_vsb; +- s32 ch_bw; +- s32 if_mid; +- s32 rcvr_mode; +- +- if (!state->init) { +- status = mt2063_init(fe); +- if (status < 0) +- return status; +- } +- +- dprintk(2, "\n"); +- +- if (c->bandwidth_hz == 0) +- return -EINVAL; +- if (c->bandwidth_hz <= 6000000) +- ch_bw = 6000000; +- else if (c->bandwidth_hz <= 7000000) +- ch_bw = 7000000; +- else +- ch_bw = 8000000; +- +- switch (c->delivery_system) { +- case SYS_DVBT: +- rcvr_mode = MT2063_OFFAIR_COFDM; +- pict_car = 36125000; +- pict2chanb_vsb = -(ch_bw / 2); +- break; +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- rcvr_mode = MT2063_CABLE_QAM; +- pict_car = 36125000; +- pict2chanb_vsb = -(ch_bw / 2); +- break; +- default: +- return -EINVAL; +- } +- if_mid = pict_car - (pict2chanb_vsb + (ch_bw / 2)); +- +- state->AS_Data.f_LO2_Step = 125000; /* FIXME: probably 5000 for FM */ +- state->AS_Data.f_out = if_mid; +- state->AS_Data.f_out_bw = ch_bw + 750000; +- status = MT2063_SetReceiverMode(state, rcvr_mode); +- if (status < 0) +- return status; +- +- dprintk(1, "Tuning to frequency: %d, bandwidth %d, foffset %d\n", +- c->frequency, ch_bw, pict2chanb_vsb); +- +- status = MT2063_Tune(state, (c->frequency + (pict2chanb_vsb + (ch_bw / 2)))); +- +- if (status < 0) +- return status; +- +- state->frequency = c->frequency; +- return 0; +-} +- +-static int mt2063_get_if_frequency(struct dvb_frontend *fe, u32 *freq) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- +- dprintk(2, "\n"); +- +- if (!state->init) +- return -ENODEV; +- +- *freq = state->AS_Data.f_out; +- +- dprintk(1, "IF frequency: %d\n", *freq); +- +- return 0; +-} +- +-static int mt2063_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- +- dprintk(2, "\n"); +- +- if (!state->init) +- return -ENODEV; +- +- *bw = state->AS_Data.f_out_bw - 750000; +- +- dprintk(1, "bandwidth: %d\n", *bw); +- +- return 0; +-} +- +-static struct dvb_tuner_ops mt2063_ops = { +- .info = { +- .name = "MT2063 Silicon Tuner", +- .frequency_min = 45000000, +- .frequency_max = 850000000, +- .frequency_step = 0, +- }, +- +- .init = mt2063_init, +- .sleep = MT2063_Sleep, +- .get_status = mt2063_get_status, +- .set_analog_params = mt2063_set_analog_params, +- .set_params = mt2063_set_params, +- .get_if_frequency = mt2063_get_if_frequency, +- .get_bandwidth = mt2063_get_bandwidth, +- .release = mt2063_release, +-}; +- +-struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, +- struct mt2063_config *config, +- struct i2c_adapter *i2c) +-{ +- struct mt2063_state *state = NULL; +- +- dprintk(2, "\n"); +- +- state = kzalloc(sizeof(struct mt2063_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->config = config; +- state->i2c = i2c; +- state->frontend = fe; +- state->reference = config->refclock / 1000; /* kHz */ +- fe->tuner_priv = state; +- fe->ops.tuner_ops = mt2063_ops; +- +- printk(KERN_INFO "%s: Attaching MT2063\n", __func__); +- return fe; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL_GPL(mt2063_attach); +- +-/* +- * Ancillary routines visible outside mt2063 +- * FIXME: Remove them in favor of using standard tuner callbacks +- */ +-unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- int err = 0; +- +- dprintk(2, "\n"); +- +- err = MT2063_SoftwareShutdown(state, 1); +- if (err < 0) +- printk(KERN_ERR "%s: Couldn't shutdown\n", __func__); +- +- return err; +-} +-EXPORT_SYMBOL_GPL(tuner_MT2063_SoftwareShutdown); +- +-unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe) +-{ +- struct mt2063_state *state = fe->tuner_priv; +- int err = 0; +- +- dprintk(2, "\n"); +- +- err = MT2063_ClearPowerMaskBits(state, MT2063_ALL_SD); +- if (err < 0) +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- +- return err; +-} +-EXPORT_SYMBOL_GPL(tuner_MT2063_ClearPowerMaskBits); +- +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_DESCRIPTION("MT2063 Silicon tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h +deleted file mode 100644 +index 62d0e8e..0000000 +--- a/drivers/media/common/tuners/mt2063.h ++++ /dev/null +@@ -1,36 +0,0 @@ +-#ifndef __MT2063_H__ +-#define __MT2063_H__ +- +-#include "dvb_frontend.h" +- +-struct mt2063_config { +- u8 tuner_address; +- u32 refclock; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MT2063) || (defined(CONFIG_MEDIA_TUNER_MT2063_MODULE) && defined(MODULE)) +-struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, +- struct mt2063_config *config, +- struct i2c_adapter *i2c); +- +-#else +- +-static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe, +- struct mt2063_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-int mt2063_setTune(struct dvb_frontend *fe, u32 f_in, +- u32 bw_in, +- enum MTTune_atv_standard tv_type); +- +-/* FIXME: Should use the standard DVB attachment interfaces */ +-unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe); +-unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe); +- +-#endif /* CONFIG_DVB_MT2063 */ +- +-#endif /* __MT2063_H__ */ +diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/common/tuners/mt20xx.c +deleted file mode 100644 +index 0e74e97..0000000 +--- a/drivers/media/common/tuners/mt20xx.c ++++ /dev/null +@@ -1,670 +0,0 @@ +-/* +- * i2c tv tuner chip device driver +- * controls microtune tuners, mt2032 + mt2050 at the moment. +- * +- * This "mt20xx" module was split apart from the original "tuner" module. +- */ +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "mt20xx.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-/* ---------------------------------------------------------------------- */ +- +-static unsigned int optimize_vco = 1; +-module_param(optimize_vco, int, 0644); +- +-static unsigned int tv_antenna = 1; +-module_param(tv_antenna, int, 0644); +- +-static unsigned int radio_antenna; +-module_param(radio_antenna, int, 0644); +- +-/* ---------------------------------------------------------------------- */ +- +-#define MT2032 0x04 +-#define MT2030 0x06 +-#define MT2040 0x07 +-#define MT2050 0x42 +- +-static char *microtune_part[] = { +- [ MT2030 ] = "MT2030", +- [ MT2032 ] = "MT2032", +- [ MT2040 ] = "MT2040", +- [ MT2050 ] = "MT2050", +-}; +- +-struct microtune_priv { +- struct tuner_i2c_props i2c_props; +- +- unsigned int xogc; +- //unsigned int radio_if2; +- +- u32 frequency; +-}; +- +-static int microtune_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int microtune_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-// IsSpurInBand()? +-static int mt2032_spurcheck(struct dvb_frontend *fe, +- int f1, int f2, int spectrum_from,int spectrum_to) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int n1=1,n2,f; +- +- f1=f1/1000; //scale to kHz to avoid 32bit overflows +- f2=f2/1000; +- spectrum_from/=1000; +- spectrum_to/=1000; +- +- tuner_dbg("spurcheck f1=%d f2=%d from=%d to=%d\n", +- f1,f2,spectrum_from,spectrum_to); +- +- do { +- n2=-n1; +- f=n1*(f1-f2); +- do { +- n2--; +- f=f-f2; +- tuner_dbg("spurtest n1=%d n2=%d ftest=%d\n",n1,n2,f); +- +- if( (f>spectrum_from) && (f(f2-spectrum_to)) || (n2>-5)); +- n1++; +- } while (n1<5); +- +- return 1; +-} +- +-static int mt2032_compute_freq(struct dvb_frontend *fe, +- unsigned int rfin, +- unsigned int if1, unsigned int if2, +- unsigned int spectrum_from, +- unsigned int spectrum_to, +- unsigned char *buf, +- int *ret_sel, +- unsigned int xogc) //all in Hz +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned int fref,lo1,lo1n,lo1a,s,sel,lo1freq, desired_lo1, +- desired_lo2,lo2,lo2n,lo2a,lo2num,lo2freq; +- +- fref= 5250 *1000; //5.25MHz +- desired_lo1=rfin+if1; +- +- lo1=(2*(desired_lo1/1000)+(fref/1000)) / (2*fref/1000); +- lo1n=lo1/8; +- lo1a=lo1-(lo1n*8); +- +- s=rfin/1000/1000+1090; +- +- if(optimize_vco) { +- if(s>1890) sel=0; +- else if(s>1720) sel=1; +- else if(s>1530) sel=2; +- else if(s>1370) sel=3; +- else sel=4; // >1090 +- } +- else { +- if(s>1790) sel=0; // <1958 +- else if(s>1617) sel=1; +- else if(s>1449) sel=2; +- else if(s>1291) sel=3; +- else sel=4; // >1090 +- } +- *ret_sel=sel; +- +- lo1freq=(lo1a+8*lo1n)*fref; +- +- tuner_dbg("mt2032: rfin=%d lo1=%d lo1n=%d lo1a=%d sel=%d, lo1freq=%d\n", +- rfin,lo1,lo1n,lo1a,sel,lo1freq); +- +- desired_lo2=lo1freq-rfin-if2; +- lo2=(desired_lo2)/fref; +- lo2n=lo2/8; +- lo2a=lo2-(lo2n*8); +- lo2num=((desired_lo2/1000)%(fref/1000))* 3780/(fref/1000); //scale to fit in 32bit arith +- lo2freq=(lo2a+8*lo2n)*fref + lo2num*(fref/1000)/3780*1000; +- +- tuner_dbg("mt2032: rfin=%d lo2=%d lo2n=%d lo2a=%d num=%d lo2freq=%d\n", +- rfin,lo2,lo2n,lo2a,lo2num,lo2freq); +- +- if (lo1a > 7 || lo1n < 17 || lo1n > 48 || lo2a > 7 || lo2n < 17 || +- lo2n > 30) { +- tuner_info("mt2032: frequency parameters out of range: %d %d %d %d\n", +- lo1a, lo1n, lo2a,lo2n); +- return(-1); +- } +- +- mt2032_spurcheck(fe, lo1freq, desired_lo2, spectrum_from, spectrum_to); +- // should recalculate lo1 (one step up/down) +- +- // set up MT2032 register map for transfer over i2c +- buf[0]=lo1n-1; +- buf[1]=lo1a | (sel<<4); +- buf[2]=0x86; // LOGC +- buf[3]=0x0f; //reserved +- buf[4]=0x1f; +- buf[5]=(lo2n-1) | (lo2a<<5); +- if(rfin >400*1000*1000) +- buf[6]=0xe4; +- else +- buf[6]=0xf4; // set PKEN per rev 1.2 +- buf[7]=8+xogc; +- buf[8]=0xc3; //reserved +- buf[9]=0x4e; //reserved +- buf[10]=0xec; //reserved +- buf[11]=(lo2num&0xff); +- buf[12]=(lo2num>>8) |0x80; // Lo2RST +- +- return 0; +-} +- +-static int mt2032_check_lo_lock(struct dvb_frontend *fe) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int try,lock=0; +- unsigned char buf[2]; +- +- for(try=0;try<10;try++) { +- buf[0]=0x0e; +- tuner_i2c_xfer_send(&priv->i2c_props,buf,1); +- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); +- tuner_dbg("mt2032 Reg.E=0x%02x\n",buf[0]); +- lock=buf[0] &0x06; +- +- if (lock==6) +- break; +- +- tuner_dbg("mt2032: pll wait 1ms for lock (0x%2x)\n",buf[0]); +- udelay(1000); +- } +- return lock; +-} +- +-static int mt2032_optimize_vco(struct dvb_frontend *fe,int sel,int lock) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned char buf[2]; +- int tad1; +- +- buf[0]=0x0f; +- tuner_i2c_xfer_send(&priv->i2c_props,buf,1); +- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); +- tuner_dbg("mt2032 Reg.F=0x%02x\n",buf[0]); +- tad1=buf[0]&0x07; +- +- if(tad1 ==0) return lock; +- if(tad1 ==1) return lock; +- +- if(tad1==2) { +- if(sel==0) +- return lock; +- else sel--; +- } +- else { +- if(sel<4) +- sel++; +- else +- return lock; +- } +- +- tuner_dbg("mt2032 optimize_vco: sel=%d\n",sel); +- +- buf[0]=0x0f; +- buf[1]=sel; +- tuner_i2c_xfer_send(&priv->i2c_props,buf,2); +- lock=mt2032_check_lo_lock(fe); +- return lock; +-} +- +- +-static void mt2032_set_if_freq(struct dvb_frontend *fe, unsigned int rfin, +- unsigned int if1, unsigned int if2, +- unsigned int from, unsigned int to) +-{ +- unsigned char buf[21]; +- int lint_try,ret,sel,lock=0; +- struct microtune_priv *priv = fe->tuner_priv; +- +- tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n", +- rfin,if1,if2,from,to); +- +- buf[0]=0; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,1); +- tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); +- +- buf[0]=0; +- ret=mt2032_compute_freq(fe,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc); +- if (ret<0) +- return; +- +- // send only the relevant registers per Rev. 1.2 +- buf[0]=0; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,4); +- buf[5]=5; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,4); +- buf[11]=11; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+11,3); +- if(ret!=3) +- tuner_warn("i2c i/o error: rc == %d (should be 3)\n",ret); +- +- // wait for PLLs to lock (per manual), retry LINT if not. +- for(lint_try=0; lint_try<2; lint_try++) { +- lock=mt2032_check_lo_lock(fe); +- +- if(optimize_vco) +- lock=mt2032_optimize_vco(fe,sel,lock); +- if(lock==6) break; +- +- tuner_dbg("mt2032: re-init PLLs by LINT\n"); +- buf[0]=7; +- buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs +- tuner_i2c_xfer_send(&priv->i2c_props,buf,2); +- mdelay(10); +- buf[1]=8+priv->xogc; +- tuner_i2c_xfer_send(&priv->i2c_props,buf,2); +- } +- +- if (lock!=6) +- tuner_warn("MT2032 Fatal Error: PLLs didn't lock.\n"); +- +- buf[0]=2; +- buf[1]=0x20; // LOGC for optimal phase noise +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); +- if (ret!=2) +- tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); +-} +- +- +-static int mt2032_set_tv_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- int if2,from,to; +- +- // signal bandwidth and picture carrier +- if (params->std & V4L2_STD_525_60) { +- // NTSC +- from = 40750*1000; +- to = 46750*1000; +- if2 = 45750*1000; +- } else { +- // PAL +- from = 32900*1000; +- to = 39900*1000; +- if2 = 38900*1000; +- } +- +- mt2032_set_if_freq(fe, params->frequency*62500, +- 1090*1000*1000, if2, from, to); +- +- return 0; +-} +- +-static int mt2032_set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int if2; +- +- if (params->std & V4L2_STD_525_60) { +- tuner_dbg("pinnacle ntsc\n"); +- if2 = 41300 * 1000; +- } else { +- tuner_dbg("pinnacle pal\n"); +- if2 = 33300 * 1000; +- } +- +- // per Manual for FM tuning: first if center freq. 1085 MHz +- mt2032_set_if_freq(fe, params->frequency * 125 / 2, +- 1085*1000*1000,if2,if2,if2); +- +- return 0; +-} +- +-static int mt2032_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int ret = -EINVAL; +- +- switch (params->mode) { +- case V4L2_TUNER_RADIO: +- ret = mt2032_set_radio_freq(fe, params); +- priv->frequency = params->frequency * 125 / 2; +- break; +- case V4L2_TUNER_ANALOG_TV: +- case V4L2_TUNER_DIGITAL_TV: +- ret = mt2032_set_tv_freq(fe, params); +- priv->frequency = params->frequency * 62500; +- break; +- } +- +- return ret; +-} +- +-static struct dvb_tuner_ops mt2032_tuner_ops = { +- .set_analog_params = mt2032_set_params, +- .release = microtune_release, +- .get_frequency = microtune_get_frequency, +-}; +- +-// Initialization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 +-static int mt2032_init(struct dvb_frontend *fe) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned char buf[21]; +- int ret,xogc,xok=0; +- +- // Initialize Registers per spec. +- buf[1]=2; // Index to register 2 +- buf[2]=0xff; +- buf[3]=0x0f; +- buf[4]=0x1f; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+1,4); +- +- buf[5]=6; // Index register 6 +- buf[6]=0xe4; +- buf[7]=0x8f; +- buf[8]=0xc3; +- buf[9]=0x4e; +- buf[10]=0xec; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+5,6); +- +- buf[12]=13; // Index register 13 +- buf[13]=0x32; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf+12,2); +- +- // Adjust XOGC (register 7), wait for XOK +- xogc=7; +- do { +- tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); +- mdelay(10); +- buf[0]=0x0e; +- tuner_i2c_xfer_send(&priv->i2c_props,buf,1); +- tuner_i2c_xfer_recv(&priv->i2c_props,buf,1); +- xok=buf[0]&0x01; +- tuner_dbg("mt2032: xok = 0x%02x\n",xok); +- if (xok == 1) break; +- +- xogc--; +- tuner_dbg("mt2032: xogc = 0x%02x\n",xogc&0x07); +- if (xogc == 3) { +- xogc=4; // min. 4 per spec +- break; +- } +- buf[0]=0x07; +- buf[1]=0x88 + xogc; +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,2); +- if (ret!=2) +- tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); +- } while (xok != 1 ); +- priv->xogc=xogc; +- +- memcpy(&fe->ops.tuner_ops, &mt2032_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- return(1); +-} +- +-static void mt2050_set_antenna(struct dvb_frontend *fe, unsigned char antenna) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned char buf[2]; +- +- buf[0] = 6; +- buf[1] = antenna ? 0x11 : 0x10; +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +- tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); +-} +- +-static void mt2050_set_if_freq(struct dvb_frontend *fe,unsigned int freq, unsigned int if2) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned int if1=1218*1000*1000; +- unsigned int f_lo1,f_lo2,lo1,lo2,f_lo1_modulo,f_lo2_modulo,num1,num2,div1a,div1b,div2a,div2b; +- int ret; +- unsigned char buf[6]; +- +- tuner_dbg("mt2050_set_if_freq freq=%d if1=%d if2=%d\n", +- freq,if1,if2); +- +- f_lo1=freq+if1; +- f_lo1=(f_lo1/1000000)*1000000; +- +- f_lo2=f_lo1-freq-if2; +- f_lo2=(f_lo2/50000)*50000; +- +- lo1=f_lo1/4000000; +- lo2=f_lo2/4000000; +- +- f_lo1_modulo= f_lo1-(lo1*4000000); +- f_lo2_modulo= f_lo2-(lo2*4000000); +- +- num1=4*f_lo1_modulo/4000000; +- num2=4096*(f_lo2_modulo/1000)/4000; +- +- // todo spurchecks +- +- div1a=(lo1/12)-1; +- div1b=lo1-(div1a+1)*12; +- +- div2a=(lo2/8)-1; +- div2b=lo2-(div2a+1)*8; +- +- if (debug > 1) { +- tuner_dbg("lo1 lo2 = %d %d\n", lo1, lo2); +- tuner_dbg("num1 num2 div1a div1b div2a div2b= %x %x %x %x %x %x\n", +- num1,num2,div1a,div1b,div2a,div2b); +- } +- +- buf[0]=1; +- buf[1]= 4*div1b + num1; +- if(freq<275*1000*1000) buf[1] = buf[1]|0x80; +- +- buf[2]=div1a; +- buf[3]=32*div2b + num2/256; +- buf[4]=num2-(num2/256)*256; +- buf[5]=div2a; +- if(num2!=0) buf[5]=buf[5]|0x40; +- +- if (debug > 1) { +- int i; +- tuner_dbg("bufs is: "); +- for(i=0;i<6;i++) +- printk("%x ",buf[i]); +- printk("\n"); +- } +- +- ret=tuner_i2c_xfer_send(&priv->i2c_props,buf,6); +- if (ret!=6) +- tuner_warn("i2c i/o error: rc == %d (should be 6)\n",ret); +-} +- +-static int mt2050_set_tv_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- unsigned int if2; +- +- if (params->std & V4L2_STD_525_60) { +- // NTSC +- if2 = 45750*1000; +- } else { +- // PAL +- if2 = 38900*1000; +- } +- if (V4L2_TUNER_DIGITAL_TV == params->mode) { +- // DVB (pinnacle 300i) +- if2 = 36150*1000; +- } +- mt2050_set_if_freq(fe, params->frequency*62500, if2); +- mt2050_set_antenna(fe, tv_antenna); +- +- return 0; +-} +- +-static int mt2050_set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int if2; +- +- if (params->std & V4L2_STD_525_60) { +- tuner_dbg("pinnacle ntsc\n"); +- if2 = 41300 * 1000; +- } else { +- tuner_dbg("pinnacle pal\n"); +- if2 = 33300 * 1000; +- } +- +- mt2050_set_if_freq(fe, params->frequency * 125 / 2, if2); +- mt2050_set_antenna(fe, radio_antenna); +- +- return 0; +-} +- +-static int mt2050_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- int ret = -EINVAL; +- +- switch (params->mode) { +- case V4L2_TUNER_RADIO: +- ret = mt2050_set_radio_freq(fe, params); +- priv->frequency = params->frequency * 125 / 2; +- break; +- case V4L2_TUNER_ANALOG_TV: +- case V4L2_TUNER_DIGITAL_TV: +- ret = mt2050_set_tv_freq(fe, params); +- priv->frequency = params->frequency * 62500; +- break; +- } +- +- return ret; +-} +- +-static struct dvb_tuner_ops mt2050_tuner_ops = { +- .set_analog_params = mt2050_set_params, +- .release = microtune_release, +- .get_frequency = microtune_get_frequency, +-}; +- +-static int mt2050_init(struct dvb_frontend *fe) +-{ +- struct microtune_priv *priv = fe->tuner_priv; +- unsigned char buf[2]; +- +- buf[0] = 6; +- buf[1] = 0x10; +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* power */ +- +- buf[0] = 0x0f; +- buf[1] = 0x0f; +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); /* m1lo */ +- +- buf[0] = 0x0d; +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 1); +- tuner_i2c_xfer_recv(&priv->i2c_props, buf, 1); +- +- tuner_dbg("mt2050: sro is %x\n", buf[0]); +- +- memcpy(&fe->ops.tuner_ops, &mt2050_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- return 0; +-} +- +-struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- struct microtune_priv *priv = NULL; +- char *name; +- unsigned char buf[21]; +- int company_code; +- +- priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- fe->tuner_priv = priv; +- +- priv->i2c_props.addr = i2c_addr; +- priv->i2c_props.adap = i2c_adap; +- priv->i2c_props.name = "mt20xx"; +- +- //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ +- +- memset(buf,0,sizeof(buf)); +- +- name = "unknown"; +- +- tuner_i2c_xfer_send(&priv->i2c_props,buf,1); +- tuner_i2c_xfer_recv(&priv->i2c_props,buf,21); +- if (debug) { +- int i; +- tuner_dbg("MT20xx hexdump:"); +- for(i=0;i<21;i++) { +- printk(" %02x",buf[i]); +- if(((i+1)%8)==0) printk(" "); +- } +- printk("\n"); +- } +- company_code = buf[0x11] << 8 | buf[0x12]; +- tuner_info("microtune: companycode=%04x part=%02x rev=%02x\n", +- company_code,buf[0x13],buf[0x14]); +- +- +- if (buf[0x13] < ARRAY_SIZE(microtune_part) && +- NULL != microtune_part[buf[0x13]]) +- name = microtune_part[buf[0x13]]; +- switch (buf[0x13]) { +- case MT2032: +- mt2032_init(fe); +- break; +- case MT2050: +- mt2050_init(fe); +- break; +- default: +- tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n", +- name); +- return NULL; +- } +- +- strlcpy(fe->ops.tuner_ops.info.name, name, +- sizeof(fe->ops.tuner_ops.info.name)); +- tuner_info("microtune %s found, OK\n",name); +- return fe; +-} +- +-EXPORT_SYMBOL_GPL(microtune_attach); +- +-MODULE_DESCRIPTION("Microtune tuner driver"); +-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/mt20xx.h b/drivers/media/common/tuners/mt20xx.h +deleted file mode 100644 +index 259553a..0000000 +--- a/drivers/media/common/tuners/mt20xx.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MT20XX_H__ +-#define __MT20XX_H__ +- +-#include +-#include "dvb_frontend.h" +- +-#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr); +-#else +-static inline struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __MT20XX_H__ */ +diff --git a/drivers/media/common/tuners/mt2131.c b/drivers/media/common/tuners/mt2131.c +deleted file mode 100644 +index f83b0c1..0000000 +--- a/drivers/media/common/tuners/mt2131.c ++++ /dev/null +@@ -1,301 +0,0 @@ +-/* +- * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2006 Steven Toth +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "mt2131.h" +-#include "mt2131_priv.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define dprintk(level,fmt, arg...) if (debug >= level) \ +- printk(KERN_INFO "%s: " fmt, "mt2131", ## arg) +- +-static u8 mt2131_config1[] = { +- 0x01, +- 0x50, 0x00, 0x50, 0x80, 0x00, 0x49, 0xfa, 0x88, +- 0x08, 0x77, 0x41, 0x04, 0x00, 0x00, 0x00, 0x32, +- 0x7f, 0xda, 0x4c, 0x00, 0x10, 0xaa, 0x78, 0x80, +- 0xff, 0x68, 0xa0, 0xff, 0xdd, 0x00, 0x00 +-}; +- +-static u8 mt2131_config2[] = { +- 0x10, +- 0x7f, 0xc8, 0x0a, 0x5f, 0x00, 0x04 +-}; +- +-static int mt2131_readreg(struct mt2131_priv *priv, u8 reg, u8 *val) +-{ +- struct i2c_msg msg[2] = { +- { .addr = priv->cfg->i2c_address, .flags = 0, +- .buf = ®, .len = 1 }, +- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, +- .buf = val, .len = 1 }, +- }; +- +- if (i2c_transfer(priv->i2c, msg, 2) != 2) { +- printk(KERN_WARNING "mt2131 I2C read failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mt2131_writereg(struct mt2131_priv *priv, u8 reg, u8 val) +-{ +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { .addr = priv->cfg->i2c_address, .flags = 0, +- .buf = buf, .len = 2 }; +- +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mt2131 I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mt2131_writeregs(struct mt2131_priv *priv,u8 *buf, u8 len) +-{ +- struct i2c_msg msg = { .addr = priv->cfg->i2c_address, +- .flags = 0, .buf = buf, .len = len }; +- +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mt2131 I2C write failed (len=%i)\n", +- (int)len); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mt2131_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct mt2131_priv *priv; +- int ret=0, i; +- u32 freq; +- u8 if_band_center; +- u32 f_lo1, f_lo2; +- u32 div1, num1, div2, num2; +- u8 b[8]; +- u8 lockval = 0; +- +- priv = fe->tuner_priv; +- +- freq = c->frequency / 1000; /* Hz -> kHz */ +- dprintk(1, "%s() freq=%d\n", __func__, freq); +- +- f_lo1 = freq + MT2131_IF1 * 1000; +- f_lo1 = (f_lo1 / 250) * 250; +- f_lo2 = f_lo1 - freq - MT2131_IF2; +- +- priv->frequency = (f_lo1 - f_lo2 - MT2131_IF2) * 1000; +- +- /* Frequency LO1 = 16MHz * (DIV1 + NUM1/8192 ) */ +- num1 = f_lo1 * 64 / (MT2131_FREF / 128); +- div1 = num1 / 8192; +- num1 &= 0x1fff; +- +- /* Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 ) */ +- num2 = f_lo2 * 64 / (MT2131_FREF / 128); +- div2 = num2 / 8192; +- num2 &= 0x1fff; +- +- if (freq <= 82500) if_band_center = 0x00; else +- if (freq <= 137500) if_band_center = 0x01; else +- if (freq <= 192500) if_band_center = 0x02; else +- if (freq <= 247500) if_band_center = 0x03; else +- if (freq <= 302500) if_band_center = 0x04; else +- if (freq <= 357500) if_band_center = 0x05; else +- if (freq <= 412500) if_band_center = 0x06; else +- if (freq <= 467500) if_band_center = 0x07; else +- if (freq <= 522500) if_band_center = 0x08; else +- if (freq <= 577500) if_band_center = 0x09; else +- if (freq <= 632500) if_band_center = 0x0A; else +- if (freq <= 687500) if_band_center = 0x0B; else +- if (freq <= 742500) if_band_center = 0x0C; else +- if (freq <= 797500) if_band_center = 0x0D; else +- if (freq <= 852500) if_band_center = 0x0E; else +- if (freq <= 907500) if_band_center = 0x0F; else +- if (freq <= 962500) if_band_center = 0x10; else +- if (freq <= 1017500) if_band_center = 0x11; else +- if (freq <= 1072500) if_band_center = 0x12; else if_band_center = 0x13; +- +- b[0] = 1; +- b[1] = (num1 >> 5) & 0xFF; +- b[2] = (num1 & 0x1F); +- b[3] = div1; +- b[4] = (num2 >> 5) & 0xFF; +- b[5] = num2 & 0x1F; +- b[6] = div2; +- +- dprintk(1, "IF1: %dMHz IF2: %dMHz\n", MT2131_IF1, MT2131_IF2); +- dprintk(1, "PLL freq=%dkHz band=%d\n", (int)freq, (int)if_band_center); +- dprintk(1, "PLL f_lo1=%dkHz f_lo2=%dkHz\n", (int)f_lo1, (int)f_lo2); +- dprintk(1, "PLL div1=%d num1=%d div2=%d num2=%d\n", +- (int)div1, (int)num1, (int)div2, (int)num2); +- dprintk(1, "PLL [1..6]: %2x %2x %2x %2x %2x %2x\n", +- (int)b[1], (int)b[2], (int)b[3], (int)b[4], (int)b[5], +- (int)b[6]); +- +- ret = mt2131_writeregs(priv,b,7); +- if (ret < 0) +- return ret; +- +- mt2131_writereg(priv, 0x0b, if_band_center); +- +- /* Wait for lock */ +- i = 0; +- do { +- mt2131_readreg(priv, 0x08, &lockval); +- if ((lockval & 0x88) == 0x88) +- break; +- msleep(4); +- i++; +- } while (i < 10); +- +- return ret; +-} +- +-static int mt2131_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mt2131_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- *frequency = priv->frequency; +- return 0; +-} +- +-static int mt2131_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct mt2131_priv *priv = fe->tuner_priv; +- u8 lock_status = 0; +- u8 afc_status = 0; +- +- *status = 0; +- +- mt2131_readreg(priv, 0x08, &lock_status); +- if ((lock_status & 0x88) == 0x88) +- *status = TUNER_STATUS_LOCKED; +- +- mt2131_readreg(priv, 0x09, &afc_status); +- dprintk(1, "%s() - LO Status = 0x%x, AFC Status = 0x%x\n", +- __func__, lock_status, afc_status); +- +- return 0; +-} +- +-static int mt2131_init(struct dvb_frontend *fe) +-{ +- struct mt2131_priv *priv = fe->tuner_priv; +- int ret; +- dprintk(1, "%s()\n", __func__); +- +- if ((ret = mt2131_writeregs(priv, mt2131_config1, +- sizeof(mt2131_config1))) < 0) +- return ret; +- +- mt2131_writereg(priv, 0x0b, 0x09); +- mt2131_writereg(priv, 0x15, 0x47); +- mt2131_writereg(priv, 0x07, 0xf2); +- mt2131_writereg(priv, 0x0b, 0x01); +- +- if ((ret = mt2131_writeregs(priv, mt2131_config2, +- sizeof(mt2131_config2))) < 0) +- return ret; +- +- return ret; +-} +- +-static int mt2131_release(struct dvb_frontend *fe) +-{ +- dprintk(1, "%s()\n", __func__); +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops mt2131_tuner_ops = { +- .info = { +- .name = "Microtune MT2131", +- .frequency_min = 48000000, +- .frequency_max = 860000000, +- .frequency_step = 50000, +- }, +- +- .release = mt2131_release, +- .init = mt2131_init, +- +- .set_params = mt2131_set_params, +- .get_frequency = mt2131_get_frequency, +- .get_status = mt2131_get_status +-}; +- +-struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mt2131_config *cfg, u16 if1) +-{ +- struct mt2131_priv *priv = NULL; +- u8 id = 0; +- +- dprintk(1, "%s()\n", __func__); +- +- priv = kzalloc(sizeof(struct mt2131_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- +- if (mt2131_readreg(priv, 0, &id) != 0) { +- kfree(priv); +- return NULL; +- } +- if ( (id != 0x3E) && (id != 0x3F) ) { +- printk(KERN_ERR "MT2131: Device not found at addr 0x%02x\n", +- cfg->i2c_address); +- kfree(priv); +- return NULL; +- } +- +- printk(KERN_INFO "MT2131: successfully identified at address 0x%02x\n", +- cfg->i2c_address); +- memcpy(&fe->ops.tuner_ops, &mt2131_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- return fe; +-} +-EXPORT_SYMBOL(mt2131_attach); +- +-MODULE_AUTHOR("Steven Toth"); +-MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/common/tuners/mt2131.h b/drivers/media/common/tuners/mt2131.h +deleted file mode 100644 +index 6632de6..0000000 +--- a/drivers/media/common/tuners/mt2131.h ++++ /dev/null +@@ -1,54 +0,0 @@ +-/* +- * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2006 Steven Toth +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MT2131_H__ +-#define __MT2131_H__ +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct mt2131_config { +- u8 i2c_address; +- u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mt2131_config *cfg, +- u16 if1); +-#else +-static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mt2131_config *cfg, +- u16 if1) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_MEDIA_TUNER_MT2131 */ +- +-#endif /* __MT2131_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/common/tuners/mt2131_priv.h b/drivers/media/common/tuners/mt2131_priv.h +deleted file mode 100644 +index 62aeedf..0000000 +--- a/drivers/media/common/tuners/mt2131_priv.h ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- * Driver for Microtune MT2131 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2006 Steven Toth +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MT2131_PRIV_H__ +-#define __MT2131_PRIV_H__ +- +-/* Regs */ +-#define MT2131_PWR 0x07 +-#define MT2131_UPC_1 0x0b +-#define MT2131_AGC_RL 0x10 +-#define MT2131_MISC_2 0x15 +- +-/* frequency values in KHz */ +-#define MT2131_IF1 1220 +-#define MT2131_IF2 44000 +-#define MT2131_FREF 16000 +- +-struct mt2131_priv { +- struct mt2131_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 frequency; +-}; +- +-#endif /* __MT2131_PRIV_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/common/tuners/mt2266.c b/drivers/media/common/tuners/mt2266.c +deleted file mode 100644 +index bca4d75..0000000 +--- a/drivers/media/common/tuners/mt2266.c ++++ /dev/null +@@ -1,353 +0,0 @@ +-/* +- * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" +- * +- * Copyright (c) 2007 Olivier DANET +- * +- * 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. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "mt2266.h" +- +-#define I2C_ADDRESS 0x60 +- +-#define REG_PART_REV 0 +-#define REG_TUNE 1 +-#define REG_BAND 6 +-#define REG_BANDWIDTH 8 +-#define REG_LOCK 0x12 +- +-#define PART_REV 0x85 +- +-struct mt2266_priv { +- struct mt2266_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 frequency; +- u32 bandwidth; +- u8 band; +-}; +- +-#define MT2266_VHF 1 +-#define MT2266_UHF 0 +- +-/* Here, frequencies are expressed in kiloHertz to avoid 32 bits overflows */ +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2266: " args); printk("\n"); }} while (0) +- +-// Reads a single register +-static int mt2266_readreg(struct mt2266_priv *priv, u8 reg, u8 *val) +-{ +- struct i2c_msg msg[2] = { +- { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, +- }; +- if (i2c_transfer(priv->i2c, msg, 2) != 2) { +- printk(KERN_WARNING "MT2266 I2C read failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Writes a single register +-static int mt2266_writereg(struct mt2266_priv *priv, u8 reg, u8 val) +-{ +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { +- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 +- }; +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "MT2266 I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Writes a set of consecutive registers +-static int mt2266_writeregs(struct mt2266_priv *priv,u8 *buf, u8 len) +-{ +- struct i2c_msg msg = { +- .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len +- }; +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "MT2266 I2C write failed (len=%i)\n",(int)len); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-// Initialisation sequences +-static u8 mt2266_init1[] = { REG_TUNE, 0x00, 0x00, 0x28, +- 0x00, 0x52, 0x99, 0x3f }; +- +-static u8 mt2266_init2[] = { +- 0x17, 0x6d, 0x71, 0x61, 0xc0, 0xbf, 0xff, 0xdc, 0x00, 0x0a, 0xd4, +- 0x03, 0x64, 0x64, 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14, +- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f, 0x5e, 0x3f, 0xff, 0xff, +- 0xff, 0x00, 0x77, 0x0f, 0x2d +-}; +- +-static u8 mt2266_init_8mhz[] = { REG_BANDWIDTH, 0x22, 0x22, 0x22, 0x22, +- 0x22, 0x22, 0x22, 0x22 }; +- +-static u8 mt2266_init_7mhz[] = { REG_BANDWIDTH, 0x32, 0x32, 0x32, 0x32, +- 0x32, 0x32, 0x32, 0x32 }; +- +-static u8 mt2266_init_6mhz[] = { REG_BANDWIDTH, 0xa7, 0xa7, 0xa7, 0xa7, +- 0xa7, 0xa7, 0xa7, 0xa7 }; +- +-static u8 mt2266_uhf[] = { 0x1d, 0xdc, 0x00, 0x0a, 0xd4, 0x03, 0x64, 0x64, +- 0x64, 0x64, 0x22, 0xaa, 0xf2, 0x1e, 0x80, 0x14 }; +- +-static u8 mt2266_vhf[] = { 0x1d, 0xfe, 0x00, 0x00, 0xb4, 0x03, 0xa5, 0xa5, +- 0xa5, 0xa5, 0x82, 0xaa, 0xf1, 0x17, 0x80, 0x1f }; +- +-#define FREF 30000 // Quartz oscillator 30 MHz +- +-static int mt2266_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct mt2266_priv *priv; +- int ret=0; +- u32 freq; +- u32 tune; +- u8 lnaband; +- u8 b[10]; +- int i; +- u8 band; +- +- priv = fe->tuner_priv; +- +- freq = priv->frequency / 1000; /* Hz -> kHz */ +- if (freq < 470000 && freq > 230000) +- return -EINVAL; /* Gap between VHF and UHF bands */ +- +- priv->frequency = c->frequency; +- tune = 2 * freq * (8192/16) / (FREF/16); +- band = (freq < 300000) ? MT2266_VHF : MT2266_UHF; +- if (band == MT2266_VHF) +- tune *= 2; +- +- switch (c->bandwidth_hz) { +- case 6000000: +- mt2266_writeregs(priv, mt2266_init_6mhz, +- sizeof(mt2266_init_6mhz)); +- break; +- case 8000000: +- mt2266_writeregs(priv, mt2266_init_8mhz, +- sizeof(mt2266_init_8mhz)); +- break; +- case 7000000: +- default: +- mt2266_writeregs(priv, mt2266_init_7mhz, +- sizeof(mt2266_init_7mhz)); +- break; +- } +- priv->bandwidth = c->bandwidth_hz; +- +- if (band == MT2266_VHF && priv->band == MT2266_UHF) { +- dprintk("Switch from UHF to VHF"); +- mt2266_writereg(priv, 0x05, 0x04); +- mt2266_writereg(priv, 0x19, 0x61); +- mt2266_writeregs(priv, mt2266_vhf, sizeof(mt2266_vhf)); +- } else if (band == MT2266_UHF && priv->band == MT2266_VHF) { +- dprintk("Switch from VHF to UHF"); +- mt2266_writereg(priv, 0x05, 0x52); +- mt2266_writereg(priv, 0x19, 0x61); +- mt2266_writeregs(priv, mt2266_uhf, sizeof(mt2266_uhf)); +- } +- msleep(10); +- +- if (freq <= 495000) +- lnaband = 0xEE; +- else if (freq <= 525000) +- lnaband = 0xDD; +- else if (freq <= 550000) +- lnaband = 0xCC; +- else if (freq <= 580000) +- lnaband = 0xBB; +- else if (freq <= 605000) +- lnaband = 0xAA; +- else if (freq <= 630000) +- lnaband = 0x99; +- else if (freq <= 655000) +- lnaband = 0x88; +- else if (freq <= 685000) +- lnaband = 0x77; +- else if (freq <= 710000) +- lnaband = 0x66; +- else if (freq <= 735000) +- lnaband = 0x55; +- else if (freq <= 765000) +- lnaband = 0x44; +- else if (freq <= 802000) +- lnaband = 0x33; +- else if (freq <= 840000) +- lnaband = 0x22; +- else +- lnaband = 0x11; +- +- b[0] = REG_TUNE; +- b[1] = (tune >> 8) & 0x1F; +- b[2] = tune & 0xFF; +- b[3] = tune >> 13; +- mt2266_writeregs(priv,b,4); +- +- dprintk("set_parms: tune=%d band=%d %s", +- (int) tune, (int) lnaband, +- (band == MT2266_UHF) ? "UHF" : "VHF"); +- dprintk("set_parms: [1..3]: %2x %2x %2x", +- (int) b[1], (int) b[2], (int)b[3]); +- +- if (band == MT2266_UHF) { +- b[0] = 0x05; +- b[1] = (priv->band == MT2266_VHF) ? 0x52 : 0x62; +- b[2] = lnaband; +- mt2266_writeregs(priv, b, 3); +- } +- +- /* Wait for pll lock or timeout */ +- i = 0; +- do { +- mt2266_readreg(priv,REG_LOCK,b); +- if (b[0] & 0x40) +- break; +- msleep(10); +- i++; +- } while (i<10); +- dprintk("Lock when i=%i",(int)i); +- +- if (band == MT2266_UHF && priv->band == MT2266_VHF) +- mt2266_writereg(priv, 0x05, 0x62); +- +- priv->band = band; +- +- return ret; +-} +- +-static void mt2266_calibrate(struct mt2266_priv *priv) +-{ +- mt2266_writereg(priv, 0x11, 0x03); +- mt2266_writereg(priv, 0x11, 0x01); +- mt2266_writeregs(priv, mt2266_init1, sizeof(mt2266_init1)); +- mt2266_writeregs(priv, mt2266_init2, sizeof(mt2266_init2)); +- mt2266_writereg(priv, 0x33, 0x5e); +- mt2266_writereg(priv, 0x10, 0x10); +- mt2266_writereg(priv, 0x10, 0x00); +- mt2266_writeregs(priv, mt2266_init_8mhz, sizeof(mt2266_init_8mhz)); +- msleep(25); +- mt2266_writereg(priv, 0x17, 0x6d); +- mt2266_writereg(priv, 0x1c, 0x00); +- msleep(75); +- mt2266_writereg(priv, 0x17, 0x6d); +- mt2266_writereg(priv, 0x1c, 0xff); +-} +- +-static int mt2266_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mt2266_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int mt2266_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct mt2266_priv *priv = fe->tuner_priv; +- *bandwidth = priv->bandwidth; +- return 0; +-} +- +-static int mt2266_init(struct dvb_frontend *fe) +-{ +- int ret; +- struct mt2266_priv *priv = fe->tuner_priv; +- ret = mt2266_writereg(priv, 0x17, 0x6d); +- if (ret < 0) +- return ret; +- ret = mt2266_writereg(priv, 0x1c, 0xff); +- if (ret < 0) +- return ret; +- return 0; +-} +- +-static int mt2266_sleep(struct dvb_frontend *fe) +-{ +- struct mt2266_priv *priv = fe->tuner_priv; +- mt2266_writereg(priv, 0x17, 0x6d); +- mt2266_writereg(priv, 0x1c, 0x00); +- return 0; +-} +- +-static int mt2266_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops mt2266_tuner_ops = { +- .info = { +- .name = "Microtune MT2266", +- .frequency_min = 174000000, +- .frequency_max = 862000000, +- .frequency_step = 50000, +- }, +- .release = mt2266_release, +- .init = mt2266_init, +- .sleep = mt2266_sleep, +- .set_params = mt2266_set_params, +- .get_frequency = mt2266_get_frequency, +- .get_bandwidth = mt2266_get_bandwidth +-}; +- +-struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) +-{ +- struct mt2266_priv *priv = NULL; +- u8 id = 0; +- +- priv = kzalloc(sizeof(struct mt2266_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- priv->band = MT2266_UHF; +- +- if (mt2266_readreg(priv, 0, &id)) { +- kfree(priv); +- return NULL; +- } +- if (id != PART_REV) { +- kfree(priv); +- return NULL; +- } +- printk(KERN_INFO "MT2266: successfully identified\n"); +- memcpy(&fe->ops.tuner_ops, &mt2266_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- mt2266_calibrate(priv); +- return fe; +-} +-EXPORT_SYMBOL(mt2266_attach); +- +-MODULE_AUTHOR("Olivier DANET"); +-MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/mt2266.h b/drivers/media/common/tuners/mt2266.h +deleted file mode 100644 +index 4d08388..0000000 +--- a/drivers/media/common/tuners/mt2266.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-/* +- * Driver for Microtune MT2266 "Direct conversion low power broadband tuner" +- * +- * Copyright (c) 2007 Olivier DANET +- * +- * 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. +- */ +- +-#ifndef MT2266_H +-#define MT2266_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct mt2266_config { +- u8 i2c_address; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE)) +-extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg); +-#else +-static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_MEDIA_TUNER_MT2266 +- +-#endif +diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c +deleted file mode 100644 +index 6133315..0000000 +--- a/drivers/media/common/tuners/mxl5005s.c ++++ /dev/null +@@ -1,4109 +0,0 @@ +-/* +- MaxLinear MXL5005S VSB/QAM/DVBT tuner driver +- +- Copyright (C) 2008 MaxLinear +- Copyright (C) 2006 Steven Toth +- Functions: +- mxl5005s_reset() +- mxl5005s_writereg() +- mxl5005s_writeregs() +- mxl5005s_init() +- mxl5005s_reconfigure() +- mxl5005s_AssignTunerMode() +- mxl5005s_set_params() +- mxl5005s_get_frequency() +- mxl5005s_get_bandwidth() +- mxl5005s_release() +- mxl5005s_attach() +- +- Copyright (C) 2008 Realtek +- Copyright (C) 2008 Jan Hoogenraad +- Functions: +- mxl5005s_SetRfFreqHz() +- +- 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. +- +-*/ +- +-/* +- History of this driver (Steven Toth): +- I was given a public release of a linux driver that included +- support for the MaxLinear MXL5005S silicon tuner. Analysis of +- the tuner driver showed clearly three things. +- +- 1. The tuner driver didn't support the LinuxTV tuner API +- so the code Realtek added had to be removed. +- +- 2. A significant amount of the driver is reference driver code +- from MaxLinear, I felt it was important to identify and +- preserve this. +- +- 3. New code has to be added to interface correctly with the +- LinuxTV API, as a regular kernel module. +- +- Other than the reference driver enum's, I've clearly marked +- sections of the code and retained the copyright of the +- respective owners. +-*/ +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "mxl5005s.h" +- +-static int debug; +- +-#define dprintk(level, arg...) do { \ +- if (level <= debug) \ +- printk(arg); \ +- } while (0) +- +-#define TUNER_REGS_NUM 104 +-#define INITCTRL_NUM 40 +- +-#ifdef _MXL_PRODUCTION +-#define CHCTRL_NUM 39 +-#else +-#define CHCTRL_NUM 36 +-#endif +- +-#define MXLCTRL_NUM 189 +-#define MASTER_CONTROL_ADDR 9 +- +-/* Enumeration of Master Control Register State */ +-enum master_control_state { +- MC_LOAD_START = 1, +- MC_POWER_DOWN, +- MC_SYNTH_RESET, +- MC_SEQ_OFF +-}; +- +-/* Enumeration of MXL5005 Tuner Modulation Type */ +-enum { +- MXL_DEFAULT_MODULATION = 0, +- MXL_DVBT, +- MXL_ATSC, +- MXL_QAM, +- MXL_ANALOG_CABLE, +- MXL_ANALOG_OTA +-}; +- +-/* MXL5005 Tuner Register Struct */ +-struct TunerReg { +- u16 Reg_Num; /* Tuner Register Address */ +- u16 Reg_Val; /* Current sw programmed value waiting to be written */ +-}; +- +-enum { +- /* Initialization Control Names */ +- DN_IQTN_AMP_CUT = 1, /* 1 */ +- BB_MODE, /* 2 */ +- BB_BUF, /* 3 */ +- BB_BUF_OA, /* 4 */ +- BB_ALPF_BANDSELECT, /* 5 */ +- BB_IQSWAP, /* 6 */ +- BB_DLPF_BANDSEL, /* 7 */ +- RFSYN_CHP_GAIN, /* 8 */ +- RFSYN_EN_CHP_HIGAIN, /* 9 */ +- AGC_IF, /* 10 */ +- AGC_RF, /* 11 */ +- IF_DIVVAL, /* 12 */ +- IF_VCO_BIAS, /* 13 */ +- CHCAL_INT_MOD_IF, /* 14 */ +- CHCAL_FRAC_MOD_IF, /* 15 */ +- DRV_RES_SEL, /* 16 */ +- I_DRIVER, /* 17 */ +- EN_AAF, /* 18 */ +- EN_3P, /* 19 */ +- EN_AUX_3P, /* 20 */ +- SEL_AAF_BAND, /* 21 */ +- SEQ_ENCLK16_CLK_OUT, /* 22 */ +- SEQ_SEL4_16B, /* 23 */ +- XTAL_CAPSELECT, /* 24 */ +- IF_SEL_DBL, /* 25 */ +- RFSYN_R_DIV, /* 26 */ +- SEQ_EXTSYNTHCALIF, /* 27 */ +- SEQ_EXTDCCAL, /* 28 */ +- AGC_EN_RSSI, /* 29 */ +- RFA_ENCLKRFAGC, /* 30 */ +- RFA_RSSI_REFH, /* 31 */ +- RFA_RSSI_REF, /* 32 */ +- RFA_RSSI_REFL, /* 33 */ +- RFA_FLR, /* 34 */ +- RFA_CEIL, /* 35 */ +- SEQ_EXTIQFSMPULSE, /* 36 */ +- OVERRIDE_1, /* 37 */ +- BB_INITSTATE_DLPF_TUNE, /* 38 */ +- TG_R_DIV, /* 39 */ +- EN_CHP_LIN_B, /* 40 */ +- +- /* Channel Change Control Names */ +- DN_POLY = 51, /* 51 */ +- DN_RFGAIN, /* 52 */ +- DN_CAP_RFLPF, /* 53 */ +- DN_EN_VHFUHFBAR, /* 54 */ +- DN_GAIN_ADJUST, /* 55 */ +- DN_IQTNBUF_AMP, /* 56 */ +- DN_IQTNGNBFBIAS_BST, /* 57 */ +- RFSYN_EN_OUTMUX, /* 58 */ +- RFSYN_SEL_VCO_OUT, /* 59 */ +- RFSYN_SEL_VCO_HI, /* 60 */ +- RFSYN_SEL_DIVM, /* 61 */ +- RFSYN_RF_DIV_BIAS, /* 62 */ +- DN_SEL_FREQ, /* 63 */ +- RFSYN_VCO_BIAS, /* 64 */ +- CHCAL_INT_MOD_RF, /* 65 */ +- CHCAL_FRAC_MOD_RF, /* 66 */ +- RFSYN_LPF_R, /* 67 */ +- CHCAL_EN_INT_RF, /* 68 */ +- TG_LO_DIVVAL, /* 69 */ +- TG_LO_SELVAL, /* 70 */ +- TG_DIV_VAL, /* 71 */ +- TG_VCO_BIAS, /* 72 */ +- SEQ_EXTPOWERUP, /* 73 */ +- OVERRIDE_2, /* 74 */ +- OVERRIDE_3, /* 75 */ +- OVERRIDE_4, /* 76 */ +- SEQ_FSM_PULSE, /* 77 */ +- GPIO_4B, /* 78 */ +- GPIO_3B, /* 79 */ +- GPIO_4, /* 80 */ +- GPIO_3, /* 81 */ +- GPIO_1B, /* 82 */ +- DAC_A_ENABLE, /* 83 */ +- DAC_B_ENABLE, /* 84 */ +- DAC_DIN_A, /* 85 */ +- DAC_DIN_B, /* 86 */ +-#ifdef _MXL_PRODUCTION +- RFSYN_EN_DIV, /* 87 */ +- RFSYN_DIVM, /* 88 */ +- DN_BYPASS_AGC_I2C /* 89 */ +-#endif +-}; +- +-/* +- * The following context is source code provided by MaxLinear. +- * MaxLinear source code - Common_MXL.h (?) +- */ +- +-/* Constants */ +-#define MXL5005S_REG_WRITING_TABLE_LEN_MAX 104 +-#define MXL5005S_LATCH_BYTE 0xfe +- +-/* Register address, MSB, and LSB */ +-#define MXL5005S_BB_IQSWAP_ADDR 59 +-#define MXL5005S_BB_IQSWAP_MSB 0 +-#define MXL5005S_BB_IQSWAP_LSB 0 +- +-#define MXL5005S_BB_DLPF_BANDSEL_ADDR 53 +-#define MXL5005S_BB_DLPF_BANDSEL_MSB 4 +-#define MXL5005S_BB_DLPF_BANDSEL_LSB 3 +- +-/* Standard modes */ +-enum { +- MXL5005S_STANDARD_DVBT, +- MXL5005S_STANDARD_ATSC, +-}; +-#define MXL5005S_STANDARD_MODE_NUM 2 +- +-/* Bandwidth modes */ +-enum { +- MXL5005S_BANDWIDTH_6MHZ = 6000000, +- MXL5005S_BANDWIDTH_7MHZ = 7000000, +- MXL5005S_BANDWIDTH_8MHZ = 8000000, +-}; +-#define MXL5005S_BANDWIDTH_MODE_NUM 3 +- +-/* MXL5005 Tuner Control Struct */ +-struct TunerControl { +- u16 Ctrl_Num; /* Control Number */ +- u16 size; /* Number of bits to represent Value */ +- u16 addr[25]; /* Array of Tuner Register Address for each bit pos */ +- u16 bit[25]; /* Array of bit pos in Reg Addr for each bit pos */ +- u16 val[25]; /* Binary representation of Value */ +-}; +- +-/* MXL5005 Tuner Struct */ +-struct mxl5005s_state { +- u8 Mode; /* 0: Analog Mode ; 1: Digital Mode */ +- u8 IF_Mode; /* for Analog Mode, 0: zero IF; 1: low IF */ +- u32 Chan_Bandwidth; /* filter channel bandwidth (6, 7, 8) */ +- u32 IF_OUT; /* Desired IF Out Frequency */ +- u16 IF_OUT_LOAD; /* IF Out Load Resistor (200/300 Ohms) */ +- u32 RF_IN; /* RF Input Frequency */ +- u32 Fxtal; /* XTAL Frequency */ +- u8 AGC_Mode; /* AGC Mode 0: Dual AGC; 1: Single AGC */ +- u16 TOP; /* Value: take over point */ +- u8 CLOCK_OUT; /* 0: turn off clk out; 1: turn on clock out */ +- u8 DIV_OUT; /* 4MHz or 16MHz */ +- u8 CAPSELECT; /* 0: disable On-Chip pulling cap; 1: enable */ +- u8 EN_RSSI; /* 0: disable RSSI; 1: enable RSSI */ +- +- /* Modulation Type; */ +- /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ +- u8 Mod_Type; +- +- /* Tracking Filter Type */ +- /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ +- u8 TF_Type; +- +- /* Calculated Settings */ +- u32 RF_LO; /* Synth RF LO Frequency */ +- u32 IF_LO; /* Synth IF LO Frequency */ +- u32 TG_LO; /* Synth TG_LO Frequency */ +- +- /* Pointers to ControlName Arrays */ +- u16 Init_Ctrl_Num; /* Number of INIT Control Names */ +- struct TunerControl +- Init_Ctrl[INITCTRL_NUM]; /* INIT Control Names Array Pointer */ +- +- u16 CH_Ctrl_Num; /* Number of CH Control Names */ +- struct TunerControl +- CH_Ctrl[CHCTRL_NUM]; /* CH Control Name Array Pointer */ +- +- u16 MXL_Ctrl_Num; /* Number of MXL Control Names */ +- struct TunerControl +- MXL_Ctrl[MXLCTRL_NUM]; /* MXL Control Name Array Pointer */ +- +- /* Pointer to Tuner Register Array */ +- u16 TunerRegs_Num; /* Number of Tuner Registers */ +- struct TunerReg +- TunerRegs[TUNER_REGS_NUM]; /* Tuner Register Array Pointer */ +- +- /* Linux driver framework specific */ +- struct mxl5005s_config *config; +- struct dvb_frontend *frontend; +- struct i2c_adapter *i2c; +- +- /* Cache values */ +- u32 current_mode; +- +-}; +- +-static u16 MXL_GetMasterControl(u8 *MasterReg, int state); +-static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value); +-static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value); +-static void MXL_RegWriteBit(struct dvb_frontend *fe, u8 address, u8 bit, +- u8 bitVal); +-static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, +- u8 *RegVal, int *count); +-static u32 MXL_Ceiling(u32 value, u32 resolution); +-static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal); +-static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, +- u32 value, u16 controlGroup); +-static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val); +-static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, +- u8 *RegVal, int *count); +-static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq); +-static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe); +-static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe); +-static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, +- u8 *RegVal, int *count); +-static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, +- u8 *datatable, u8 len); +-static u16 MXL_IFSynthInit(struct dvb_frontend *fe); +-static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, +- u32 bandwidth); +-static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, +- u32 bandwidth); +- +-/* ---------------------------------------------------------------- +- * Begin: Custom code salvaged from the Realtek driver. +- * Copyright (C) 2008 Realtek +- * Copyright (C) 2008 Jan Hoogenraad +- * This code is placed under the terms of the GNU General Public License +- * +- * Released by Realtek under GPLv2. +- * Thanks to Realtek for a lot of support we received ! +- * +- * Revision: 080314 - original version +- */ +- +-static int mxl5005s_SetRfFreqHz(struct dvb_frontend *fe, unsigned long RfFreqHz) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- unsigned char AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; +- unsigned char ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; +- int TableLen; +- +- u32 IfDivval = 0; +- unsigned char MasterControlByte; +- +- dprintk(1, "%s() freq=%ld\n", __func__, RfFreqHz); +- +- /* Set MxL5005S tuner RF frequency according to example code. */ +- +- /* Tuner RF frequency setting stage 0 */ +- MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); +- AddrTable[0] = MASTER_CONTROL_ADDR; +- ByteTable[0] |= state->config->AgcMasterByte; +- +- mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); +- +- /* Tuner RF frequency setting stage 1 */ +- MXL_TuneRF(fe, RfFreqHz); +- +- MXL_ControlRead(fe, IF_DIVVAL, &IfDivval); +- +- MXL_ControlWrite(fe, SEQ_FSM_PULSE, 0); +- MXL_ControlWrite(fe, SEQ_EXTPOWERUP, 1); +- MXL_ControlWrite(fe, IF_DIVVAL, 8); +- MXL_GetCHRegister(fe, AddrTable, ByteTable, &TableLen); +- +- MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); +- AddrTable[TableLen] = MASTER_CONTROL_ADDR ; +- ByteTable[TableLen] = MasterControlByte | +- state->config->AgcMasterByte; +- TableLen += 1; +- +- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); +- +- /* Wait 30 ms. */ +- msleep(150); +- +- /* Tuner RF frequency setting stage 2 */ +- MXL_ControlWrite(fe, SEQ_FSM_PULSE, 1); +- MXL_ControlWrite(fe, IF_DIVVAL, IfDivval); +- MXL_GetCHRegister_ZeroIF(fe, AddrTable, ByteTable, &TableLen); +- +- MXL_GetMasterControl(&MasterControlByte, MC_LOAD_START); +- AddrTable[TableLen] = MASTER_CONTROL_ADDR ; +- ByteTable[TableLen] = MasterControlByte | +- state->config->AgcMasterByte ; +- TableLen += 1; +- +- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); +- +- msleep(100); +- +- return 0; +-} +-/* End: Custom code taken from the Realtek driver */ +- +-/* ---------------------------------------------------------------- +- * Begin: Reference driver code found in the Realtek driver. +- * Copyright (C) 2008 MaxLinear +- */ +-static u16 MXL5005_RegisterInit(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- state->TunerRegs_Num = TUNER_REGS_NUM ; +- +- state->TunerRegs[0].Reg_Num = 9 ; +- state->TunerRegs[0].Reg_Val = 0x40 ; +- +- state->TunerRegs[1].Reg_Num = 11 ; +- state->TunerRegs[1].Reg_Val = 0x19 ; +- +- state->TunerRegs[2].Reg_Num = 12 ; +- state->TunerRegs[2].Reg_Val = 0x60 ; +- +- state->TunerRegs[3].Reg_Num = 13 ; +- state->TunerRegs[3].Reg_Val = 0x00 ; +- +- state->TunerRegs[4].Reg_Num = 14 ; +- state->TunerRegs[4].Reg_Val = 0x00 ; +- +- state->TunerRegs[5].Reg_Num = 15 ; +- state->TunerRegs[5].Reg_Val = 0xC0 ; +- +- state->TunerRegs[6].Reg_Num = 16 ; +- state->TunerRegs[6].Reg_Val = 0x00 ; +- +- state->TunerRegs[7].Reg_Num = 17 ; +- state->TunerRegs[7].Reg_Val = 0x00 ; +- +- state->TunerRegs[8].Reg_Num = 18 ; +- state->TunerRegs[8].Reg_Val = 0x00 ; +- +- state->TunerRegs[9].Reg_Num = 19 ; +- state->TunerRegs[9].Reg_Val = 0x34 ; +- +- state->TunerRegs[10].Reg_Num = 21 ; +- state->TunerRegs[10].Reg_Val = 0x00 ; +- +- state->TunerRegs[11].Reg_Num = 22 ; +- state->TunerRegs[11].Reg_Val = 0x6B ; +- +- state->TunerRegs[12].Reg_Num = 23 ; +- state->TunerRegs[12].Reg_Val = 0x35 ; +- +- state->TunerRegs[13].Reg_Num = 24 ; +- state->TunerRegs[13].Reg_Val = 0x70 ; +- +- state->TunerRegs[14].Reg_Num = 25 ; +- state->TunerRegs[14].Reg_Val = 0x3E ; +- +- state->TunerRegs[15].Reg_Num = 26 ; +- state->TunerRegs[15].Reg_Val = 0x82 ; +- +- state->TunerRegs[16].Reg_Num = 31 ; +- state->TunerRegs[16].Reg_Val = 0x00 ; +- +- state->TunerRegs[17].Reg_Num = 32 ; +- state->TunerRegs[17].Reg_Val = 0x40 ; +- +- state->TunerRegs[18].Reg_Num = 33 ; +- state->TunerRegs[18].Reg_Val = 0x53 ; +- +- state->TunerRegs[19].Reg_Num = 34 ; +- state->TunerRegs[19].Reg_Val = 0x81 ; +- +- state->TunerRegs[20].Reg_Num = 35 ; +- state->TunerRegs[20].Reg_Val = 0xC9 ; +- +- state->TunerRegs[21].Reg_Num = 36 ; +- state->TunerRegs[21].Reg_Val = 0x01 ; +- +- state->TunerRegs[22].Reg_Num = 37 ; +- state->TunerRegs[22].Reg_Val = 0x00 ; +- +- state->TunerRegs[23].Reg_Num = 41 ; +- state->TunerRegs[23].Reg_Val = 0x00 ; +- +- state->TunerRegs[24].Reg_Num = 42 ; +- state->TunerRegs[24].Reg_Val = 0xF8 ; +- +- state->TunerRegs[25].Reg_Num = 43 ; +- state->TunerRegs[25].Reg_Val = 0x43 ; +- +- state->TunerRegs[26].Reg_Num = 44 ; +- state->TunerRegs[26].Reg_Val = 0x20 ; +- +- state->TunerRegs[27].Reg_Num = 45 ; +- state->TunerRegs[27].Reg_Val = 0x80 ; +- +- state->TunerRegs[28].Reg_Num = 46 ; +- state->TunerRegs[28].Reg_Val = 0x88 ; +- +- state->TunerRegs[29].Reg_Num = 47 ; +- state->TunerRegs[29].Reg_Val = 0x86 ; +- +- state->TunerRegs[30].Reg_Num = 48 ; +- state->TunerRegs[30].Reg_Val = 0x00 ; +- +- state->TunerRegs[31].Reg_Num = 49 ; +- state->TunerRegs[31].Reg_Val = 0x00 ; +- +- state->TunerRegs[32].Reg_Num = 53 ; +- state->TunerRegs[32].Reg_Val = 0x94 ; +- +- state->TunerRegs[33].Reg_Num = 54 ; +- state->TunerRegs[33].Reg_Val = 0xFA ; +- +- state->TunerRegs[34].Reg_Num = 55 ; +- state->TunerRegs[34].Reg_Val = 0x92 ; +- +- state->TunerRegs[35].Reg_Num = 56 ; +- state->TunerRegs[35].Reg_Val = 0x80 ; +- +- state->TunerRegs[36].Reg_Num = 57 ; +- state->TunerRegs[36].Reg_Val = 0x41 ; +- +- state->TunerRegs[37].Reg_Num = 58 ; +- state->TunerRegs[37].Reg_Val = 0xDB ; +- +- state->TunerRegs[38].Reg_Num = 59 ; +- state->TunerRegs[38].Reg_Val = 0x00 ; +- +- state->TunerRegs[39].Reg_Num = 60 ; +- state->TunerRegs[39].Reg_Val = 0x00 ; +- +- state->TunerRegs[40].Reg_Num = 61 ; +- state->TunerRegs[40].Reg_Val = 0x00 ; +- +- state->TunerRegs[41].Reg_Num = 62 ; +- state->TunerRegs[41].Reg_Val = 0x00 ; +- +- state->TunerRegs[42].Reg_Num = 65 ; +- state->TunerRegs[42].Reg_Val = 0xF8 ; +- +- state->TunerRegs[43].Reg_Num = 66 ; +- state->TunerRegs[43].Reg_Val = 0xE4 ; +- +- state->TunerRegs[44].Reg_Num = 67 ; +- state->TunerRegs[44].Reg_Val = 0x90 ; +- +- state->TunerRegs[45].Reg_Num = 68 ; +- state->TunerRegs[45].Reg_Val = 0xC0 ; +- +- state->TunerRegs[46].Reg_Num = 69 ; +- state->TunerRegs[46].Reg_Val = 0x01 ; +- +- state->TunerRegs[47].Reg_Num = 70 ; +- state->TunerRegs[47].Reg_Val = 0x50 ; +- +- state->TunerRegs[48].Reg_Num = 71 ; +- state->TunerRegs[48].Reg_Val = 0x06 ; +- +- state->TunerRegs[49].Reg_Num = 72 ; +- state->TunerRegs[49].Reg_Val = 0x00 ; +- +- state->TunerRegs[50].Reg_Num = 73 ; +- state->TunerRegs[50].Reg_Val = 0x20 ; +- +- state->TunerRegs[51].Reg_Num = 76 ; +- state->TunerRegs[51].Reg_Val = 0xBB ; +- +- state->TunerRegs[52].Reg_Num = 77 ; +- state->TunerRegs[52].Reg_Val = 0x13 ; +- +- state->TunerRegs[53].Reg_Num = 81 ; +- state->TunerRegs[53].Reg_Val = 0x04 ; +- +- state->TunerRegs[54].Reg_Num = 82 ; +- state->TunerRegs[54].Reg_Val = 0x75 ; +- +- state->TunerRegs[55].Reg_Num = 83 ; +- state->TunerRegs[55].Reg_Val = 0x00 ; +- +- state->TunerRegs[56].Reg_Num = 84 ; +- state->TunerRegs[56].Reg_Val = 0x00 ; +- +- state->TunerRegs[57].Reg_Num = 85 ; +- state->TunerRegs[57].Reg_Val = 0x00 ; +- +- state->TunerRegs[58].Reg_Num = 91 ; +- state->TunerRegs[58].Reg_Val = 0x70 ; +- +- state->TunerRegs[59].Reg_Num = 92 ; +- state->TunerRegs[59].Reg_Val = 0x00 ; +- +- state->TunerRegs[60].Reg_Num = 93 ; +- state->TunerRegs[60].Reg_Val = 0x00 ; +- +- state->TunerRegs[61].Reg_Num = 94 ; +- state->TunerRegs[61].Reg_Val = 0x00 ; +- +- state->TunerRegs[62].Reg_Num = 95 ; +- state->TunerRegs[62].Reg_Val = 0x0C ; +- +- state->TunerRegs[63].Reg_Num = 96 ; +- state->TunerRegs[63].Reg_Val = 0x00 ; +- +- state->TunerRegs[64].Reg_Num = 97 ; +- state->TunerRegs[64].Reg_Val = 0x00 ; +- +- state->TunerRegs[65].Reg_Num = 98 ; +- state->TunerRegs[65].Reg_Val = 0xE2 ; +- +- state->TunerRegs[66].Reg_Num = 99 ; +- state->TunerRegs[66].Reg_Val = 0x00 ; +- +- state->TunerRegs[67].Reg_Num = 100 ; +- state->TunerRegs[67].Reg_Val = 0x00 ; +- +- state->TunerRegs[68].Reg_Num = 101 ; +- state->TunerRegs[68].Reg_Val = 0x12 ; +- +- state->TunerRegs[69].Reg_Num = 102 ; +- state->TunerRegs[69].Reg_Val = 0x80 ; +- +- state->TunerRegs[70].Reg_Num = 103 ; +- state->TunerRegs[70].Reg_Val = 0x32 ; +- +- state->TunerRegs[71].Reg_Num = 104 ; +- state->TunerRegs[71].Reg_Val = 0xB4 ; +- +- state->TunerRegs[72].Reg_Num = 105 ; +- state->TunerRegs[72].Reg_Val = 0x60 ; +- +- state->TunerRegs[73].Reg_Num = 106 ; +- state->TunerRegs[73].Reg_Val = 0x83 ; +- +- state->TunerRegs[74].Reg_Num = 107 ; +- state->TunerRegs[74].Reg_Val = 0x84 ; +- +- state->TunerRegs[75].Reg_Num = 108 ; +- state->TunerRegs[75].Reg_Val = 0x9C ; +- +- state->TunerRegs[76].Reg_Num = 109 ; +- state->TunerRegs[76].Reg_Val = 0x02 ; +- +- state->TunerRegs[77].Reg_Num = 110 ; +- state->TunerRegs[77].Reg_Val = 0x81 ; +- +- state->TunerRegs[78].Reg_Num = 111 ; +- state->TunerRegs[78].Reg_Val = 0xC0 ; +- +- state->TunerRegs[79].Reg_Num = 112 ; +- state->TunerRegs[79].Reg_Val = 0x10 ; +- +- state->TunerRegs[80].Reg_Num = 131 ; +- state->TunerRegs[80].Reg_Val = 0x8A ; +- +- state->TunerRegs[81].Reg_Num = 132 ; +- state->TunerRegs[81].Reg_Val = 0x10 ; +- +- state->TunerRegs[82].Reg_Num = 133 ; +- state->TunerRegs[82].Reg_Val = 0x24 ; +- +- state->TunerRegs[83].Reg_Num = 134 ; +- state->TunerRegs[83].Reg_Val = 0x00 ; +- +- state->TunerRegs[84].Reg_Num = 135 ; +- state->TunerRegs[84].Reg_Val = 0x00 ; +- +- state->TunerRegs[85].Reg_Num = 136 ; +- state->TunerRegs[85].Reg_Val = 0x7E ; +- +- state->TunerRegs[86].Reg_Num = 137 ; +- state->TunerRegs[86].Reg_Val = 0x40 ; +- +- state->TunerRegs[87].Reg_Num = 138 ; +- state->TunerRegs[87].Reg_Val = 0x38 ; +- +- state->TunerRegs[88].Reg_Num = 146 ; +- state->TunerRegs[88].Reg_Val = 0xF6 ; +- +- state->TunerRegs[89].Reg_Num = 147 ; +- state->TunerRegs[89].Reg_Val = 0x1A ; +- +- state->TunerRegs[90].Reg_Num = 148 ; +- state->TunerRegs[90].Reg_Val = 0x62 ; +- +- state->TunerRegs[91].Reg_Num = 149 ; +- state->TunerRegs[91].Reg_Val = 0x33 ; +- +- state->TunerRegs[92].Reg_Num = 150 ; +- state->TunerRegs[92].Reg_Val = 0x80 ; +- +- state->TunerRegs[93].Reg_Num = 156 ; +- state->TunerRegs[93].Reg_Val = 0x56 ; +- +- state->TunerRegs[94].Reg_Num = 157 ; +- state->TunerRegs[94].Reg_Val = 0x17 ; +- +- state->TunerRegs[95].Reg_Num = 158 ; +- state->TunerRegs[95].Reg_Val = 0xA9 ; +- +- state->TunerRegs[96].Reg_Num = 159 ; +- state->TunerRegs[96].Reg_Val = 0x00 ; +- +- state->TunerRegs[97].Reg_Num = 160 ; +- state->TunerRegs[97].Reg_Val = 0x00 ; +- +- state->TunerRegs[98].Reg_Num = 161 ; +- state->TunerRegs[98].Reg_Val = 0x00 ; +- +- state->TunerRegs[99].Reg_Num = 162 ; +- state->TunerRegs[99].Reg_Val = 0x40 ; +- +- state->TunerRegs[100].Reg_Num = 166 ; +- state->TunerRegs[100].Reg_Val = 0xAE ; +- +- state->TunerRegs[101].Reg_Num = 167 ; +- state->TunerRegs[101].Reg_Val = 0x1B ; +- +- state->TunerRegs[102].Reg_Num = 168 ; +- state->TunerRegs[102].Reg_Val = 0xF2 ; +- +- state->TunerRegs[103].Reg_Num = 195 ; +- state->TunerRegs[103].Reg_Val = 0x00 ; +- +- return 0 ; +-} +- +-static u16 MXL5005_ControlInit(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- state->Init_Ctrl_Num = INITCTRL_NUM; +- +- state->Init_Ctrl[0].Ctrl_Num = DN_IQTN_AMP_CUT ; +- state->Init_Ctrl[0].size = 1 ; +- state->Init_Ctrl[0].addr[0] = 73; +- state->Init_Ctrl[0].bit[0] = 7; +- state->Init_Ctrl[0].val[0] = 0; +- +- state->Init_Ctrl[1].Ctrl_Num = BB_MODE ; +- state->Init_Ctrl[1].size = 1 ; +- state->Init_Ctrl[1].addr[0] = 53; +- state->Init_Ctrl[1].bit[0] = 2; +- state->Init_Ctrl[1].val[0] = 1; +- +- state->Init_Ctrl[2].Ctrl_Num = BB_BUF ; +- state->Init_Ctrl[2].size = 2 ; +- state->Init_Ctrl[2].addr[0] = 53; +- state->Init_Ctrl[2].bit[0] = 1; +- state->Init_Ctrl[2].val[0] = 0; +- state->Init_Ctrl[2].addr[1] = 57; +- state->Init_Ctrl[2].bit[1] = 0; +- state->Init_Ctrl[2].val[1] = 1; +- +- state->Init_Ctrl[3].Ctrl_Num = BB_BUF_OA ; +- state->Init_Ctrl[3].size = 1 ; +- state->Init_Ctrl[3].addr[0] = 53; +- state->Init_Ctrl[3].bit[0] = 0; +- state->Init_Ctrl[3].val[0] = 0; +- +- state->Init_Ctrl[4].Ctrl_Num = BB_ALPF_BANDSELECT ; +- state->Init_Ctrl[4].size = 3 ; +- state->Init_Ctrl[4].addr[0] = 53; +- state->Init_Ctrl[4].bit[0] = 5; +- state->Init_Ctrl[4].val[0] = 0; +- state->Init_Ctrl[4].addr[1] = 53; +- state->Init_Ctrl[4].bit[1] = 6; +- state->Init_Ctrl[4].val[1] = 0; +- state->Init_Ctrl[4].addr[2] = 53; +- state->Init_Ctrl[4].bit[2] = 7; +- state->Init_Ctrl[4].val[2] = 1; +- +- state->Init_Ctrl[5].Ctrl_Num = BB_IQSWAP ; +- state->Init_Ctrl[5].size = 1 ; +- state->Init_Ctrl[5].addr[0] = 59; +- state->Init_Ctrl[5].bit[0] = 0; +- state->Init_Ctrl[5].val[0] = 0; +- +- state->Init_Ctrl[6].Ctrl_Num = BB_DLPF_BANDSEL ; +- state->Init_Ctrl[6].size = 2 ; +- state->Init_Ctrl[6].addr[0] = 53; +- state->Init_Ctrl[6].bit[0] = 3; +- state->Init_Ctrl[6].val[0] = 0; +- state->Init_Ctrl[6].addr[1] = 53; +- state->Init_Ctrl[6].bit[1] = 4; +- state->Init_Ctrl[6].val[1] = 1; +- +- state->Init_Ctrl[7].Ctrl_Num = RFSYN_CHP_GAIN ; +- state->Init_Ctrl[7].size = 4 ; +- state->Init_Ctrl[7].addr[0] = 22; +- state->Init_Ctrl[7].bit[0] = 4; +- state->Init_Ctrl[7].val[0] = 0; +- state->Init_Ctrl[7].addr[1] = 22; +- state->Init_Ctrl[7].bit[1] = 5; +- state->Init_Ctrl[7].val[1] = 1; +- state->Init_Ctrl[7].addr[2] = 22; +- state->Init_Ctrl[7].bit[2] = 6; +- state->Init_Ctrl[7].val[2] = 1; +- state->Init_Ctrl[7].addr[3] = 22; +- state->Init_Ctrl[7].bit[3] = 7; +- state->Init_Ctrl[7].val[3] = 0; +- +- state->Init_Ctrl[8].Ctrl_Num = RFSYN_EN_CHP_HIGAIN ; +- state->Init_Ctrl[8].size = 1 ; +- state->Init_Ctrl[8].addr[0] = 22; +- state->Init_Ctrl[8].bit[0] = 2; +- state->Init_Ctrl[8].val[0] = 0; +- +- state->Init_Ctrl[9].Ctrl_Num = AGC_IF ; +- state->Init_Ctrl[9].size = 4 ; +- state->Init_Ctrl[9].addr[0] = 76; +- state->Init_Ctrl[9].bit[0] = 0; +- state->Init_Ctrl[9].val[0] = 1; +- state->Init_Ctrl[9].addr[1] = 76; +- state->Init_Ctrl[9].bit[1] = 1; +- state->Init_Ctrl[9].val[1] = 1; +- state->Init_Ctrl[9].addr[2] = 76; +- state->Init_Ctrl[9].bit[2] = 2; +- state->Init_Ctrl[9].val[2] = 0; +- state->Init_Ctrl[9].addr[3] = 76; +- state->Init_Ctrl[9].bit[3] = 3; +- state->Init_Ctrl[9].val[3] = 1; +- +- state->Init_Ctrl[10].Ctrl_Num = AGC_RF ; +- state->Init_Ctrl[10].size = 4 ; +- state->Init_Ctrl[10].addr[0] = 76; +- state->Init_Ctrl[10].bit[0] = 4; +- state->Init_Ctrl[10].val[0] = 1; +- state->Init_Ctrl[10].addr[1] = 76; +- state->Init_Ctrl[10].bit[1] = 5; +- state->Init_Ctrl[10].val[1] = 1; +- state->Init_Ctrl[10].addr[2] = 76; +- state->Init_Ctrl[10].bit[2] = 6; +- state->Init_Ctrl[10].val[2] = 0; +- state->Init_Ctrl[10].addr[3] = 76; +- state->Init_Ctrl[10].bit[3] = 7; +- state->Init_Ctrl[10].val[3] = 1; +- +- state->Init_Ctrl[11].Ctrl_Num = IF_DIVVAL ; +- state->Init_Ctrl[11].size = 5 ; +- state->Init_Ctrl[11].addr[0] = 43; +- state->Init_Ctrl[11].bit[0] = 3; +- state->Init_Ctrl[11].val[0] = 0; +- state->Init_Ctrl[11].addr[1] = 43; +- state->Init_Ctrl[11].bit[1] = 4; +- state->Init_Ctrl[11].val[1] = 0; +- state->Init_Ctrl[11].addr[2] = 43; +- state->Init_Ctrl[11].bit[2] = 5; +- state->Init_Ctrl[11].val[2] = 0; +- state->Init_Ctrl[11].addr[3] = 43; +- state->Init_Ctrl[11].bit[3] = 6; +- state->Init_Ctrl[11].val[3] = 1; +- state->Init_Ctrl[11].addr[4] = 43; +- state->Init_Ctrl[11].bit[4] = 7; +- state->Init_Ctrl[11].val[4] = 0; +- +- state->Init_Ctrl[12].Ctrl_Num = IF_VCO_BIAS ; +- state->Init_Ctrl[12].size = 6 ; +- state->Init_Ctrl[12].addr[0] = 44; +- state->Init_Ctrl[12].bit[0] = 2; +- state->Init_Ctrl[12].val[0] = 0; +- state->Init_Ctrl[12].addr[1] = 44; +- state->Init_Ctrl[12].bit[1] = 3; +- state->Init_Ctrl[12].val[1] = 0; +- state->Init_Ctrl[12].addr[2] = 44; +- state->Init_Ctrl[12].bit[2] = 4; +- state->Init_Ctrl[12].val[2] = 0; +- state->Init_Ctrl[12].addr[3] = 44; +- state->Init_Ctrl[12].bit[3] = 5; +- state->Init_Ctrl[12].val[3] = 1; +- state->Init_Ctrl[12].addr[4] = 44; +- state->Init_Ctrl[12].bit[4] = 6; +- state->Init_Ctrl[12].val[4] = 0; +- state->Init_Ctrl[12].addr[5] = 44; +- state->Init_Ctrl[12].bit[5] = 7; +- state->Init_Ctrl[12].val[5] = 0; +- +- state->Init_Ctrl[13].Ctrl_Num = CHCAL_INT_MOD_IF ; +- state->Init_Ctrl[13].size = 7 ; +- state->Init_Ctrl[13].addr[0] = 11; +- state->Init_Ctrl[13].bit[0] = 0; +- state->Init_Ctrl[13].val[0] = 1; +- state->Init_Ctrl[13].addr[1] = 11; +- state->Init_Ctrl[13].bit[1] = 1; +- state->Init_Ctrl[13].val[1] = 0; +- state->Init_Ctrl[13].addr[2] = 11; +- state->Init_Ctrl[13].bit[2] = 2; +- state->Init_Ctrl[13].val[2] = 0; +- state->Init_Ctrl[13].addr[3] = 11; +- state->Init_Ctrl[13].bit[3] = 3; +- state->Init_Ctrl[13].val[3] = 1; +- state->Init_Ctrl[13].addr[4] = 11; +- state->Init_Ctrl[13].bit[4] = 4; +- state->Init_Ctrl[13].val[4] = 1; +- state->Init_Ctrl[13].addr[5] = 11; +- state->Init_Ctrl[13].bit[5] = 5; +- state->Init_Ctrl[13].val[5] = 0; +- state->Init_Ctrl[13].addr[6] = 11; +- state->Init_Ctrl[13].bit[6] = 6; +- state->Init_Ctrl[13].val[6] = 0; +- +- state->Init_Ctrl[14].Ctrl_Num = CHCAL_FRAC_MOD_IF ; +- state->Init_Ctrl[14].size = 16 ; +- state->Init_Ctrl[14].addr[0] = 13; +- state->Init_Ctrl[14].bit[0] = 0; +- state->Init_Ctrl[14].val[0] = 0; +- state->Init_Ctrl[14].addr[1] = 13; +- state->Init_Ctrl[14].bit[1] = 1; +- state->Init_Ctrl[14].val[1] = 0; +- state->Init_Ctrl[14].addr[2] = 13; +- state->Init_Ctrl[14].bit[2] = 2; +- state->Init_Ctrl[14].val[2] = 0; +- state->Init_Ctrl[14].addr[3] = 13; +- state->Init_Ctrl[14].bit[3] = 3; +- state->Init_Ctrl[14].val[3] = 0; +- state->Init_Ctrl[14].addr[4] = 13; +- state->Init_Ctrl[14].bit[4] = 4; +- state->Init_Ctrl[14].val[4] = 0; +- state->Init_Ctrl[14].addr[5] = 13; +- state->Init_Ctrl[14].bit[5] = 5; +- state->Init_Ctrl[14].val[5] = 0; +- state->Init_Ctrl[14].addr[6] = 13; +- state->Init_Ctrl[14].bit[6] = 6; +- state->Init_Ctrl[14].val[6] = 0; +- state->Init_Ctrl[14].addr[7] = 13; +- state->Init_Ctrl[14].bit[7] = 7; +- state->Init_Ctrl[14].val[7] = 0; +- state->Init_Ctrl[14].addr[8] = 12; +- state->Init_Ctrl[14].bit[8] = 0; +- state->Init_Ctrl[14].val[8] = 0; +- state->Init_Ctrl[14].addr[9] = 12; +- state->Init_Ctrl[14].bit[9] = 1; +- state->Init_Ctrl[14].val[9] = 0; +- state->Init_Ctrl[14].addr[10] = 12; +- state->Init_Ctrl[14].bit[10] = 2; +- state->Init_Ctrl[14].val[10] = 0; +- state->Init_Ctrl[14].addr[11] = 12; +- state->Init_Ctrl[14].bit[11] = 3; +- state->Init_Ctrl[14].val[11] = 0; +- state->Init_Ctrl[14].addr[12] = 12; +- state->Init_Ctrl[14].bit[12] = 4; +- state->Init_Ctrl[14].val[12] = 0; +- state->Init_Ctrl[14].addr[13] = 12; +- state->Init_Ctrl[14].bit[13] = 5; +- state->Init_Ctrl[14].val[13] = 1; +- state->Init_Ctrl[14].addr[14] = 12; +- state->Init_Ctrl[14].bit[14] = 6; +- state->Init_Ctrl[14].val[14] = 1; +- state->Init_Ctrl[14].addr[15] = 12; +- state->Init_Ctrl[14].bit[15] = 7; +- state->Init_Ctrl[14].val[15] = 0; +- +- state->Init_Ctrl[15].Ctrl_Num = DRV_RES_SEL ; +- state->Init_Ctrl[15].size = 3 ; +- state->Init_Ctrl[15].addr[0] = 147; +- state->Init_Ctrl[15].bit[0] = 2; +- state->Init_Ctrl[15].val[0] = 0; +- state->Init_Ctrl[15].addr[1] = 147; +- state->Init_Ctrl[15].bit[1] = 3; +- state->Init_Ctrl[15].val[1] = 1; +- state->Init_Ctrl[15].addr[2] = 147; +- state->Init_Ctrl[15].bit[2] = 4; +- state->Init_Ctrl[15].val[2] = 1; +- +- state->Init_Ctrl[16].Ctrl_Num = I_DRIVER ; +- state->Init_Ctrl[16].size = 2 ; +- state->Init_Ctrl[16].addr[0] = 147; +- state->Init_Ctrl[16].bit[0] = 0; +- state->Init_Ctrl[16].val[0] = 0; +- state->Init_Ctrl[16].addr[1] = 147; +- state->Init_Ctrl[16].bit[1] = 1; +- state->Init_Ctrl[16].val[1] = 1; +- +- state->Init_Ctrl[17].Ctrl_Num = EN_AAF ; +- state->Init_Ctrl[17].size = 1 ; +- state->Init_Ctrl[17].addr[0] = 147; +- state->Init_Ctrl[17].bit[0] = 7; +- state->Init_Ctrl[17].val[0] = 0; +- +- state->Init_Ctrl[18].Ctrl_Num = EN_3P ; +- state->Init_Ctrl[18].size = 1 ; +- state->Init_Ctrl[18].addr[0] = 147; +- state->Init_Ctrl[18].bit[0] = 6; +- state->Init_Ctrl[18].val[0] = 0; +- +- state->Init_Ctrl[19].Ctrl_Num = EN_AUX_3P ; +- state->Init_Ctrl[19].size = 1 ; +- state->Init_Ctrl[19].addr[0] = 156; +- state->Init_Ctrl[19].bit[0] = 0; +- state->Init_Ctrl[19].val[0] = 0; +- +- state->Init_Ctrl[20].Ctrl_Num = SEL_AAF_BAND ; +- state->Init_Ctrl[20].size = 1 ; +- state->Init_Ctrl[20].addr[0] = 147; +- state->Init_Ctrl[20].bit[0] = 5; +- state->Init_Ctrl[20].val[0] = 0; +- +- state->Init_Ctrl[21].Ctrl_Num = SEQ_ENCLK16_CLK_OUT ; +- state->Init_Ctrl[21].size = 1 ; +- state->Init_Ctrl[21].addr[0] = 137; +- state->Init_Ctrl[21].bit[0] = 4; +- state->Init_Ctrl[21].val[0] = 0; +- +- state->Init_Ctrl[22].Ctrl_Num = SEQ_SEL4_16B ; +- state->Init_Ctrl[22].size = 1 ; +- state->Init_Ctrl[22].addr[0] = 137; +- state->Init_Ctrl[22].bit[0] = 7; +- state->Init_Ctrl[22].val[0] = 0; +- +- state->Init_Ctrl[23].Ctrl_Num = XTAL_CAPSELECT ; +- state->Init_Ctrl[23].size = 1 ; +- state->Init_Ctrl[23].addr[0] = 91; +- state->Init_Ctrl[23].bit[0] = 5; +- state->Init_Ctrl[23].val[0] = 1; +- +- state->Init_Ctrl[24].Ctrl_Num = IF_SEL_DBL ; +- state->Init_Ctrl[24].size = 1 ; +- state->Init_Ctrl[24].addr[0] = 43; +- state->Init_Ctrl[24].bit[0] = 0; +- state->Init_Ctrl[24].val[0] = 1; +- +- state->Init_Ctrl[25].Ctrl_Num = RFSYN_R_DIV ; +- state->Init_Ctrl[25].size = 2 ; +- state->Init_Ctrl[25].addr[0] = 22; +- state->Init_Ctrl[25].bit[0] = 0; +- state->Init_Ctrl[25].val[0] = 1; +- state->Init_Ctrl[25].addr[1] = 22; +- state->Init_Ctrl[25].bit[1] = 1; +- state->Init_Ctrl[25].val[1] = 1; +- +- state->Init_Ctrl[26].Ctrl_Num = SEQ_EXTSYNTHCALIF ; +- state->Init_Ctrl[26].size = 1 ; +- state->Init_Ctrl[26].addr[0] = 134; +- state->Init_Ctrl[26].bit[0] = 2; +- state->Init_Ctrl[26].val[0] = 0; +- +- state->Init_Ctrl[27].Ctrl_Num = SEQ_EXTDCCAL ; +- state->Init_Ctrl[27].size = 1 ; +- state->Init_Ctrl[27].addr[0] = 137; +- state->Init_Ctrl[27].bit[0] = 3; +- state->Init_Ctrl[27].val[0] = 0; +- +- state->Init_Ctrl[28].Ctrl_Num = AGC_EN_RSSI ; +- state->Init_Ctrl[28].size = 1 ; +- state->Init_Ctrl[28].addr[0] = 77; +- state->Init_Ctrl[28].bit[0] = 7; +- state->Init_Ctrl[28].val[0] = 0; +- +- state->Init_Ctrl[29].Ctrl_Num = RFA_ENCLKRFAGC ; +- state->Init_Ctrl[29].size = 1 ; +- state->Init_Ctrl[29].addr[0] = 166; +- state->Init_Ctrl[29].bit[0] = 7; +- state->Init_Ctrl[29].val[0] = 1; +- +- state->Init_Ctrl[30].Ctrl_Num = RFA_RSSI_REFH ; +- state->Init_Ctrl[30].size = 3 ; +- state->Init_Ctrl[30].addr[0] = 166; +- state->Init_Ctrl[30].bit[0] = 0; +- state->Init_Ctrl[30].val[0] = 0; +- state->Init_Ctrl[30].addr[1] = 166; +- state->Init_Ctrl[30].bit[1] = 1; +- state->Init_Ctrl[30].val[1] = 1; +- state->Init_Ctrl[30].addr[2] = 166; +- state->Init_Ctrl[30].bit[2] = 2; +- state->Init_Ctrl[30].val[2] = 1; +- +- state->Init_Ctrl[31].Ctrl_Num = RFA_RSSI_REF ; +- state->Init_Ctrl[31].size = 3 ; +- state->Init_Ctrl[31].addr[0] = 166; +- state->Init_Ctrl[31].bit[0] = 3; +- state->Init_Ctrl[31].val[0] = 1; +- state->Init_Ctrl[31].addr[1] = 166; +- state->Init_Ctrl[31].bit[1] = 4; +- state->Init_Ctrl[31].val[1] = 0; +- state->Init_Ctrl[31].addr[2] = 166; +- state->Init_Ctrl[31].bit[2] = 5; +- state->Init_Ctrl[31].val[2] = 1; +- +- state->Init_Ctrl[32].Ctrl_Num = RFA_RSSI_REFL ; +- state->Init_Ctrl[32].size = 3 ; +- state->Init_Ctrl[32].addr[0] = 167; +- state->Init_Ctrl[32].bit[0] = 0; +- state->Init_Ctrl[32].val[0] = 1; +- state->Init_Ctrl[32].addr[1] = 167; +- state->Init_Ctrl[32].bit[1] = 1; +- state->Init_Ctrl[32].val[1] = 1; +- state->Init_Ctrl[32].addr[2] = 167; +- state->Init_Ctrl[32].bit[2] = 2; +- state->Init_Ctrl[32].val[2] = 0; +- +- state->Init_Ctrl[33].Ctrl_Num = RFA_FLR ; +- state->Init_Ctrl[33].size = 4 ; +- state->Init_Ctrl[33].addr[0] = 168; +- state->Init_Ctrl[33].bit[0] = 0; +- state->Init_Ctrl[33].val[0] = 0; +- state->Init_Ctrl[33].addr[1] = 168; +- state->Init_Ctrl[33].bit[1] = 1; +- state->Init_Ctrl[33].val[1] = 1; +- state->Init_Ctrl[33].addr[2] = 168; +- state->Init_Ctrl[33].bit[2] = 2; +- state->Init_Ctrl[33].val[2] = 0; +- state->Init_Ctrl[33].addr[3] = 168; +- state->Init_Ctrl[33].bit[3] = 3; +- state->Init_Ctrl[33].val[3] = 0; +- +- state->Init_Ctrl[34].Ctrl_Num = RFA_CEIL ; +- state->Init_Ctrl[34].size = 4 ; +- state->Init_Ctrl[34].addr[0] = 168; +- state->Init_Ctrl[34].bit[0] = 4; +- state->Init_Ctrl[34].val[0] = 1; +- state->Init_Ctrl[34].addr[1] = 168; +- state->Init_Ctrl[34].bit[1] = 5; +- state->Init_Ctrl[34].val[1] = 1; +- state->Init_Ctrl[34].addr[2] = 168; +- state->Init_Ctrl[34].bit[2] = 6; +- state->Init_Ctrl[34].val[2] = 1; +- state->Init_Ctrl[34].addr[3] = 168; +- state->Init_Ctrl[34].bit[3] = 7; +- state->Init_Ctrl[34].val[3] = 1; +- +- state->Init_Ctrl[35].Ctrl_Num = SEQ_EXTIQFSMPULSE ; +- state->Init_Ctrl[35].size = 1 ; +- state->Init_Ctrl[35].addr[0] = 135; +- state->Init_Ctrl[35].bit[0] = 0; +- state->Init_Ctrl[35].val[0] = 0; +- +- state->Init_Ctrl[36].Ctrl_Num = OVERRIDE_1 ; +- state->Init_Ctrl[36].size = 1 ; +- state->Init_Ctrl[36].addr[0] = 56; +- state->Init_Ctrl[36].bit[0] = 3; +- state->Init_Ctrl[36].val[0] = 0; +- +- state->Init_Ctrl[37].Ctrl_Num = BB_INITSTATE_DLPF_TUNE ; +- state->Init_Ctrl[37].size = 7 ; +- state->Init_Ctrl[37].addr[0] = 59; +- state->Init_Ctrl[37].bit[0] = 1; +- state->Init_Ctrl[37].val[0] = 0; +- state->Init_Ctrl[37].addr[1] = 59; +- state->Init_Ctrl[37].bit[1] = 2; +- state->Init_Ctrl[37].val[1] = 0; +- state->Init_Ctrl[37].addr[2] = 59; +- state->Init_Ctrl[37].bit[2] = 3; +- state->Init_Ctrl[37].val[2] = 0; +- state->Init_Ctrl[37].addr[3] = 59; +- state->Init_Ctrl[37].bit[3] = 4; +- state->Init_Ctrl[37].val[3] = 0; +- state->Init_Ctrl[37].addr[4] = 59; +- state->Init_Ctrl[37].bit[4] = 5; +- state->Init_Ctrl[37].val[4] = 0; +- state->Init_Ctrl[37].addr[5] = 59; +- state->Init_Ctrl[37].bit[5] = 6; +- state->Init_Ctrl[37].val[5] = 0; +- state->Init_Ctrl[37].addr[6] = 59; +- state->Init_Ctrl[37].bit[6] = 7; +- state->Init_Ctrl[37].val[6] = 0; +- +- state->Init_Ctrl[38].Ctrl_Num = TG_R_DIV ; +- state->Init_Ctrl[38].size = 6 ; +- state->Init_Ctrl[38].addr[0] = 32; +- state->Init_Ctrl[38].bit[0] = 2; +- state->Init_Ctrl[38].val[0] = 0; +- state->Init_Ctrl[38].addr[1] = 32; +- state->Init_Ctrl[38].bit[1] = 3; +- state->Init_Ctrl[38].val[1] = 0; +- state->Init_Ctrl[38].addr[2] = 32; +- state->Init_Ctrl[38].bit[2] = 4; +- state->Init_Ctrl[38].val[2] = 0; +- state->Init_Ctrl[38].addr[3] = 32; +- state->Init_Ctrl[38].bit[3] = 5; +- state->Init_Ctrl[38].val[3] = 0; +- state->Init_Ctrl[38].addr[4] = 32; +- state->Init_Ctrl[38].bit[4] = 6; +- state->Init_Ctrl[38].val[4] = 1; +- state->Init_Ctrl[38].addr[5] = 32; +- state->Init_Ctrl[38].bit[5] = 7; +- state->Init_Ctrl[38].val[5] = 0; +- +- state->Init_Ctrl[39].Ctrl_Num = EN_CHP_LIN_B ; +- state->Init_Ctrl[39].size = 1 ; +- state->Init_Ctrl[39].addr[0] = 25; +- state->Init_Ctrl[39].bit[0] = 3; +- state->Init_Ctrl[39].val[0] = 1; +- +- +- state->CH_Ctrl_Num = CHCTRL_NUM ; +- +- state->CH_Ctrl[0].Ctrl_Num = DN_POLY ; +- state->CH_Ctrl[0].size = 2 ; +- state->CH_Ctrl[0].addr[0] = 68; +- state->CH_Ctrl[0].bit[0] = 6; +- state->CH_Ctrl[0].val[0] = 1; +- state->CH_Ctrl[0].addr[1] = 68; +- state->CH_Ctrl[0].bit[1] = 7; +- state->CH_Ctrl[0].val[1] = 1; +- +- state->CH_Ctrl[1].Ctrl_Num = DN_RFGAIN ; +- state->CH_Ctrl[1].size = 2 ; +- state->CH_Ctrl[1].addr[0] = 70; +- state->CH_Ctrl[1].bit[0] = 6; +- state->CH_Ctrl[1].val[0] = 1; +- state->CH_Ctrl[1].addr[1] = 70; +- state->CH_Ctrl[1].bit[1] = 7; +- state->CH_Ctrl[1].val[1] = 0; +- +- state->CH_Ctrl[2].Ctrl_Num = DN_CAP_RFLPF ; +- state->CH_Ctrl[2].size = 9 ; +- state->CH_Ctrl[2].addr[0] = 69; +- state->CH_Ctrl[2].bit[0] = 5; +- state->CH_Ctrl[2].val[0] = 0; +- state->CH_Ctrl[2].addr[1] = 69; +- state->CH_Ctrl[2].bit[1] = 6; +- state->CH_Ctrl[2].val[1] = 0; +- state->CH_Ctrl[2].addr[2] = 69; +- state->CH_Ctrl[2].bit[2] = 7; +- state->CH_Ctrl[2].val[2] = 0; +- state->CH_Ctrl[2].addr[3] = 68; +- state->CH_Ctrl[2].bit[3] = 0; +- state->CH_Ctrl[2].val[3] = 0; +- state->CH_Ctrl[2].addr[4] = 68; +- state->CH_Ctrl[2].bit[4] = 1; +- state->CH_Ctrl[2].val[4] = 0; +- state->CH_Ctrl[2].addr[5] = 68; +- state->CH_Ctrl[2].bit[5] = 2; +- state->CH_Ctrl[2].val[5] = 0; +- state->CH_Ctrl[2].addr[6] = 68; +- state->CH_Ctrl[2].bit[6] = 3; +- state->CH_Ctrl[2].val[6] = 0; +- state->CH_Ctrl[2].addr[7] = 68; +- state->CH_Ctrl[2].bit[7] = 4; +- state->CH_Ctrl[2].val[7] = 0; +- state->CH_Ctrl[2].addr[8] = 68; +- state->CH_Ctrl[2].bit[8] = 5; +- state->CH_Ctrl[2].val[8] = 0; +- +- state->CH_Ctrl[3].Ctrl_Num = DN_EN_VHFUHFBAR ; +- state->CH_Ctrl[3].size = 1 ; +- state->CH_Ctrl[3].addr[0] = 70; +- state->CH_Ctrl[3].bit[0] = 5; +- state->CH_Ctrl[3].val[0] = 0; +- +- state->CH_Ctrl[4].Ctrl_Num = DN_GAIN_ADJUST ; +- state->CH_Ctrl[4].size = 3 ; +- state->CH_Ctrl[4].addr[0] = 73; +- state->CH_Ctrl[4].bit[0] = 4; +- state->CH_Ctrl[4].val[0] = 0; +- state->CH_Ctrl[4].addr[1] = 73; +- state->CH_Ctrl[4].bit[1] = 5; +- state->CH_Ctrl[4].val[1] = 1; +- state->CH_Ctrl[4].addr[2] = 73; +- state->CH_Ctrl[4].bit[2] = 6; +- state->CH_Ctrl[4].val[2] = 0; +- +- state->CH_Ctrl[5].Ctrl_Num = DN_IQTNBUF_AMP ; +- state->CH_Ctrl[5].size = 4 ; +- state->CH_Ctrl[5].addr[0] = 70; +- state->CH_Ctrl[5].bit[0] = 0; +- state->CH_Ctrl[5].val[0] = 0; +- state->CH_Ctrl[5].addr[1] = 70; +- state->CH_Ctrl[5].bit[1] = 1; +- state->CH_Ctrl[5].val[1] = 0; +- state->CH_Ctrl[5].addr[2] = 70; +- state->CH_Ctrl[5].bit[2] = 2; +- state->CH_Ctrl[5].val[2] = 0; +- state->CH_Ctrl[5].addr[3] = 70; +- state->CH_Ctrl[5].bit[3] = 3; +- state->CH_Ctrl[5].val[3] = 0; +- +- state->CH_Ctrl[6].Ctrl_Num = DN_IQTNGNBFBIAS_BST ; +- state->CH_Ctrl[6].size = 1 ; +- state->CH_Ctrl[6].addr[0] = 70; +- state->CH_Ctrl[6].bit[0] = 4; +- state->CH_Ctrl[6].val[0] = 1; +- +- state->CH_Ctrl[7].Ctrl_Num = RFSYN_EN_OUTMUX ; +- state->CH_Ctrl[7].size = 1 ; +- state->CH_Ctrl[7].addr[0] = 111; +- state->CH_Ctrl[7].bit[0] = 4; +- state->CH_Ctrl[7].val[0] = 0; +- +- state->CH_Ctrl[8].Ctrl_Num = RFSYN_SEL_VCO_OUT ; +- state->CH_Ctrl[8].size = 1 ; +- state->CH_Ctrl[8].addr[0] = 111; +- state->CH_Ctrl[8].bit[0] = 7; +- state->CH_Ctrl[8].val[0] = 1; +- +- state->CH_Ctrl[9].Ctrl_Num = RFSYN_SEL_VCO_HI ; +- state->CH_Ctrl[9].size = 1 ; +- state->CH_Ctrl[9].addr[0] = 111; +- state->CH_Ctrl[9].bit[0] = 6; +- state->CH_Ctrl[9].val[0] = 1; +- +- state->CH_Ctrl[10].Ctrl_Num = RFSYN_SEL_DIVM ; +- state->CH_Ctrl[10].size = 1 ; +- state->CH_Ctrl[10].addr[0] = 111; +- state->CH_Ctrl[10].bit[0] = 5; +- state->CH_Ctrl[10].val[0] = 0; +- +- state->CH_Ctrl[11].Ctrl_Num = RFSYN_RF_DIV_BIAS ; +- state->CH_Ctrl[11].size = 2 ; +- state->CH_Ctrl[11].addr[0] = 110; +- state->CH_Ctrl[11].bit[0] = 0; +- state->CH_Ctrl[11].val[0] = 1; +- state->CH_Ctrl[11].addr[1] = 110; +- state->CH_Ctrl[11].bit[1] = 1; +- state->CH_Ctrl[11].val[1] = 0; +- +- state->CH_Ctrl[12].Ctrl_Num = DN_SEL_FREQ ; +- state->CH_Ctrl[12].size = 3 ; +- state->CH_Ctrl[12].addr[0] = 69; +- state->CH_Ctrl[12].bit[0] = 2; +- state->CH_Ctrl[12].val[0] = 0; +- state->CH_Ctrl[12].addr[1] = 69; +- state->CH_Ctrl[12].bit[1] = 3; +- state->CH_Ctrl[12].val[1] = 0; +- state->CH_Ctrl[12].addr[2] = 69; +- state->CH_Ctrl[12].bit[2] = 4; +- state->CH_Ctrl[12].val[2] = 0; +- +- state->CH_Ctrl[13].Ctrl_Num = RFSYN_VCO_BIAS ; +- state->CH_Ctrl[13].size = 6 ; +- state->CH_Ctrl[13].addr[0] = 110; +- state->CH_Ctrl[13].bit[0] = 2; +- state->CH_Ctrl[13].val[0] = 0; +- state->CH_Ctrl[13].addr[1] = 110; +- state->CH_Ctrl[13].bit[1] = 3; +- state->CH_Ctrl[13].val[1] = 0; +- state->CH_Ctrl[13].addr[2] = 110; +- state->CH_Ctrl[13].bit[2] = 4; +- state->CH_Ctrl[13].val[2] = 0; +- state->CH_Ctrl[13].addr[3] = 110; +- state->CH_Ctrl[13].bit[3] = 5; +- state->CH_Ctrl[13].val[3] = 0; +- state->CH_Ctrl[13].addr[4] = 110; +- state->CH_Ctrl[13].bit[4] = 6; +- state->CH_Ctrl[13].val[4] = 0; +- state->CH_Ctrl[13].addr[5] = 110; +- state->CH_Ctrl[13].bit[5] = 7; +- state->CH_Ctrl[13].val[5] = 1; +- +- state->CH_Ctrl[14].Ctrl_Num = CHCAL_INT_MOD_RF ; +- state->CH_Ctrl[14].size = 7 ; +- state->CH_Ctrl[14].addr[0] = 14; +- state->CH_Ctrl[14].bit[0] = 0; +- state->CH_Ctrl[14].val[0] = 0; +- state->CH_Ctrl[14].addr[1] = 14; +- state->CH_Ctrl[14].bit[1] = 1; +- state->CH_Ctrl[14].val[1] = 0; +- state->CH_Ctrl[14].addr[2] = 14; +- state->CH_Ctrl[14].bit[2] = 2; +- state->CH_Ctrl[14].val[2] = 0; +- state->CH_Ctrl[14].addr[3] = 14; +- state->CH_Ctrl[14].bit[3] = 3; +- state->CH_Ctrl[14].val[3] = 0; +- state->CH_Ctrl[14].addr[4] = 14; +- state->CH_Ctrl[14].bit[4] = 4; +- state->CH_Ctrl[14].val[4] = 0; +- state->CH_Ctrl[14].addr[5] = 14; +- state->CH_Ctrl[14].bit[5] = 5; +- state->CH_Ctrl[14].val[5] = 0; +- state->CH_Ctrl[14].addr[6] = 14; +- state->CH_Ctrl[14].bit[6] = 6; +- state->CH_Ctrl[14].val[6] = 0; +- +- state->CH_Ctrl[15].Ctrl_Num = CHCAL_FRAC_MOD_RF ; +- state->CH_Ctrl[15].size = 18 ; +- state->CH_Ctrl[15].addr[0] = 17; +- state->CH_Ctrl[15].bit[0] = 6; +- state->CH_Ctrl[15].val[0] = 0; +- state->CH_Ctrl[15].addr[1] = 17; +- state->CH_Ctrl[15].bit[1] = 7; +- state->CH_Ctrl[15].val[1] = 0; +- state->CH_Ctrl[15].addr[2] = 16; +- state->CH_Ctrl[15].bit[2] = 0; +- state->CH_Ctrl[15].val[2] = 0; +- state->CH_Ctrl[15].addr[3] = 16; +- state->CH_Ctrl[15].bit[3] = 1; +- state->CH_Ctrl[15].val[3] = 0; +- state->CH_Ctrl[15].addr[4] = 16; +- state->CH_Ctrl[15].bit[4] = 2; +- state->CH_Ctrl[15].val[4] = 0; +- state->CH_Ctrl[15].addr[5] = 16; +- state->CH_Ctrl[15].bit[5] = 3; +- state->CH_Ctrl[15].val[5] = 0; +- state->CH_Ctrl[15].addr[6] = 16; +- state->CH_Ctrl[15].bit[6] = 4; +- state->CH_Ctrl[15].val[6] = 0; +- state->CH_Ctrl[15].addr[7] = 16; +- state->CH_Ctrl[15].bit[7] = 5; +- state->CH_Ctrl[15].val[7] = 0; +- state->CH_Ctrl[15].addr[8] = 16; +- state->CH_Ctrl[15].bit[8] = 6; +- state->CH_Ctrl[15].val[8] = 0; +- state->CH_Ctrl[15].addr[9] = 16; +- state->CH_Ctrl[15].bit[9] = 7; +- state->CH_Ctrl[15].val[9] = 0; +- state->CH_Ctrl[15].addr[10] = 15; +- state->CH_Ctrl[15].bit[10] = 0; +- state->CH_Ctrl[15].val[10] = 0; +- state->CH_Ctrl[15].addr[11] = 15; +- state->CH_Ctrl[15].bit[11] = 1; +- state->CH_Ctrl[15].val[11] = 0; +- state->CH_Ctrl[15].addr[12] = 15; +- state->CH_Ctrl[15].bit[12] = 2; +- state->CH_Ctrl[15].val[12] = 0; +- state->CH_Ctrl[15].addr[13] = 15; +- state->CH_Ctrl[15].bit[13] = 3; +- state->CH_Ctrl[15].val[13] = 0; +- state->CH_Ctrl[15].addr[14] = 15; +- state->CH_Ctrl[15].bit[14] = 4; +- state->CH_Ctrl[15].val[14] = 0; +- state->CH_Ctrl[15].addr[15] = 15; +- state->CH_Ctrl[15].bit[15] = 5; +- state->CH_Ctrl[15].val[15] = 0; +- state->CH_Ctrl[15].addr[16] = 15; +- state->CH_Ctrl[15].bit[16] = 6; +- state->CH_Ctrl[15].val[16] = 1; +- state->CH_Ctrl[15].addr[17] = 15; +- state->CH_Ctrl[15].bit[17] = 7; +- state->CH_Ctrl[15].val[17] = 1; +- +- state->CH_Ctrl[16].Ctrl_Num = RFSYN_LPF_R ; +- state->CH_Ctrl[16].size = 5 ; +- state->CH_Ctrl[16].addr[0] = 112; +- state->CH_Ctrl[16].bit[0] = 0; +- state->CH_Ctrl[16].val[0] = 0; +- state->CH_Ctrl[16].addr[1] = 112; +- state->CH_Ctrl[16].bit[1] = 1; +- state->CH_Ctrl[16].val[1] = 0; +- state->CH_Ctrl[16].addr[2] = 112; +- state->CH_Ctrl[16].bit[2] = 2; +- state->CH_Ctrl[16].val[2] = 0; +- state->CH_Ctrl[16].addr[3] = 112; +- state->CH_Ctrl[16].bit[3] = 3; +- state->CH_Ctrl[16].val[3] = 0; +- state->CH_Ctrl[16].addr[4] = 112; +- state->CH_Ctrl[16].bit[4] = 4; +- state->CH_Ctrl[16].val[4] = 1; +- +- state->CH_Ctrl[17].Ctrl_Num = CHCAL_EN_INT_RF ; +- state->CH_Ctrl[17].size = 1 ; +- state->CH_Ctrl[17].addr[0] = 14; +- state->CH_Ctrl[17].bit[0] = 7; +- state->CH_Ctrl[17].val[0] = 0; +- +- state->CH_Ctrl[18].Ctrl_Num = TG_LO_DIVVAL ; +- state->CH_Ctrl[18].size = 4 ; +- state->CH_Ctrl[18].addr[0] = 107; +- state->CH_Ctrl[18].bit[0] = 3; +- state->CH_Ctrl[18].val[0] = 0; +- state->CH_Ctrl[18].addr[1] = 107; +- state->CH_Ctrl[18].bit[1] = 4; +- state->CH_Ctrl[18].val[1] = 0; +- state->CH_Ctrl[18].addr[2] = 107; +- state->CH_Ctrl[18].bit[2] = 5; +- state->CH_Ctrl[18].val[2] = 0; +- state->CH_Ctrl[18].addr[3] = 107; +- state->CH_Ctrl[18].bit[3] = 6; +- state->CH_Ctrl[18].val[3] = 0; +- +- state->CH_Ctrl[19].Ctrl_Num = TG_LO_SELVAL ; +- state->CH_Ctrl[19].size = 3 ; +- state->CH_Ctrl[19].addr[0] = 107; +- state->CH_Ctrl[19].bit[0] = 7; +- state->CH_Ctrl[19].val[0] = 1; +- state->CH_Ctrl[19].addr[1] = 106; +- state->CH_Ctrl[19].bit[1] = 0; +- state->CH_Ctrl[19].val[1] = 1; +- state->CH_Ctrl[19].addr[2] = 106; +- state->CH_Ctrl[19].bit[2] = 1; +- state->CH_Ctrl[19].val[2] = 1; +- +- state->CH_Ctrl[20].Ctrl_Num = TG_DIV_VAL ; +- state->CH_Ctrl[20].size = 11 ; +- state->CH_Ctrl[20].addr[0] = 109; +- state->CH_Ctrl[20].bit[0] = 2; +- state->CH_Ctrl[20].val[0] = 0; +- state->CH_Ctrl[20].addr[1] = 109; +- state->CH_Ctrl[20].bit[1] = 3; +- state->CH_Ctrl[20].val[1] = 0; +- state->CH_Ctrl[20].addr[2] = 109; +- state->CH_Ctrl[20].bit[2] = 4; +- state->CH_Ctrl[20].val[2] = 0; +- state->CH_Ctrl[20].addr[3] = 109; +- state->CH_Ctrl[20].bit[3] = 5; +- state->CH_Ctrl[20].val[3] = 0; +- state->CH_Ctrl[20].addr[4] = 109; +- state->CH_Ctrl[20].bit[4] = 6; +- state->CH_Ctrl[20].val[4] = 0; +- state->CH_Ctrl[20].addr[5] = 109; +- state->CH_Ctrl[20].bit[5] = 7; +- state->CH_Ctrl[20].val[5] = 0; +- state->CH_Ctrl[20].addr[6] = 108; +- state->CH_Ctrl[20].bit[6] = 0; +- state->CH_Ctrl[20].val[6] = 0; +- state->CH_Ctrl[20].addr[7] = 108; +- state->CH_Ctrl[20].bit[7] = 1; +- state->CH_Ctrl[20].val[7] = 0; +- state->CH_Ctrl[20].addr[8] = 108; +- state->CH_Ctrl[20].bit[8] = 2; +- state->CH_Ctrl[20].val[8] = 1; +- state->CH_Ctrl[20].addr[9] = 108; +- state->CH_Ctrl[20].bit[9] = 3; +- state->CH_Ctrl[20].val[9] = 1; +- state->CH_Ctrl[20].addr[10] = 108; +- state->CH_Ctrl[20].bit[10] = 4; +- state->CH_Ctrl[20].val[10] = 1; +- +- state->CH_Ctrl[21].Ctrl_Num = TG_VCO_BIAS ; +- state->CH_Ctrl[21].size = 6 ; +- state->CH_Ctrl[21].addr[0] = 106; +- state->CH_Ctrl[21].bit[0] = 2; +- state->CH_Ctrl[21].val[0] = 0; +- state->CH_Ctrl[21].addr[1] = 106; +- state->CH_Ctrl[21].bit[1] = 3; +- state->CH_Ctrl[21].val[1] = 0; +- state->CH_Ctrl[21].addr[2] = 106; +- state->CH_Ctrl[21].bit[2] = 4; +- state->CH_Ctrl[21].val[2] = 0; +- state->CH_Ctrl[21].addr[3] = 106; +- state->CH_Ctrl[21].bit[3] = 5; +- state->CH_Ctrl[21].val[3] = 0; +- state->CH_Ctrl[21].addr[4] = 106; +- state->CH_Ctrl[21].bit[4] = 6; +- state->CH_Ctrl[21].val[4] = 0; +- state->CH_Ctrl[21].addr[5] = 106; +- state->CH_Ctrl[21].bit[5] = 7; +- state->CH_Ctrl[21].val[5] = 1; +- +- state->CH_Ctrl[22].Ctrl_Num = SEQ_EXTPOWERUP ; +- state->CH_Ctrl[22].size = 1 ; +- state->CH_Ctrl[22].addr[0] = 138; +- state->CH_Ctrl[22].bit[0] = 4; +- state->CH_Ctrl[22].val[0] = 1; +- +- state->CH_Ctrl[23].Ctrl_Num = OVERRIDE_2 ; +- state->CH_Ctrl[23].size = 1 ; +- state->CH_Ctrl[23].addr[0] = 17; +- state->CH_Ctrl[23].bit[0] = 5; +- state->CH_Ctrl[23].val[0] = 0; +- +- state->CH_Ctrl[24].Ctrl_Num = OVERRIDE_3 ; +- state->CH_Ctrl[24].size = 1 ; +- state->CH_Ctrl[24].addr[0] = 111; +- state->CH_Ctrl[24].bit[0] = 3; +- state->CH_Ctrl[24].val[0] = 0; +- +- state->CH_Ctrl[25].Ctrl_Num = OVERRIDE_4 ; +- state->CH_Ctrl[25].size = 1 ; +- state->CH_Ctrl[25].addr[0] = 112; +- state->CH_Ctrl[25].bit[0] = 7; +- state->CH_Ctrl[25].val[0] = 0; +- +- state->CH_Ctrl[26].Ctrl_Num = SEQ_FSM_PULSE ; +- state->CH_Ctrl[26].size = 1 ; +- state->CH_Ctrl[26].addr[0] = 136; +- state->CH_Ctrl[26].bit[0] = 7; +- state->CH_Ctrl[26].val[0] = 0; +- +- state->CH_Ctrl[27].Ctrl_Num = GPIO_4B ; +- state->CH_Ctrl[27].size = 1 ; +- state->CH_Ctrl[27].addr[0] = 149; +- state->CH_Ctrl[27].bit[0] = 7; +- state->CH_Ctrl[27].val[0] = 0; +- +- state->CH_Ctrl[28].Ctrl_Num = GPIO_3B ; +- state->CH_Ctrl[28].size = 1 ; +- state->CH_Ctrl[28].addr[0] = 149; +- state->CH_Ctrl[28].bit[0] = 6; +- state->CH_Ctrl[28].val[0] = 0; +- +- state->CH_Ctrl[29].Ctrl_Num = GPIO_4 ; +- state->CH_Ctrl[29].size = 1 ; +- state->CH_Ctrl[29].addr[0] = 149; +- state->CH_Ctrl[29].bit[0] = 5; +- state->CH_Ctrl[29].val[0] = 1; +- +- state->CH_Ctrl[30].Ctrl_Num = GPIO_3 ; +- state->CH_Ctrl[30].size = 1 ; +- state->CH_Ctrl[30].addr[0] = 149; +- state->CH_Ctrl[30].bit[0] = 4; +- state->CH_Ctrl[30].val[0] = 1; +- +- state->CH_Ctrl[31].Ctrl_Num = GPIO_1B ; +- state->CH_Ctrl[31].size = 1 ; +- state->CH_Ctrl[31].addr[0] = 149; +- state->CH_Ctrl[31].bit[0] = 3; +- state->CH_Ctrl[31].val[0] = 0; +- +- state->CH_Ctrl[32].Ctrl_Num = DAC_A_ENABLE ; +- state->CH_Ctrl[32].size = 1 ; +- state->CH_Ctrl[32].addr[0] = 93; +- state->CH_Ctrl[32].bit[0] = 1; +- state->CH_Ctrl[32].val[0] = 0; +- +- state->CH_Ctrl[33].Ctrl_Num = DAC_B_ENABLE ; +- state->CH_Ctrl[33].size = 1 ; +- state->CH_Ctrl[33].addr[0] = 93; +- state->CH_Ctrl[33].bit[0] = 0; +- state->CH_Ctrl[33].val[0] = 0; +- +- state->CH_Ctrl[34].Ctrl_Num = DAC_DIN_A ; +- state->CH_Ctrl[34].size = 6 ; +- state->CH_Ctrl[34].addr[0] = 92; +- state->CH_Ctrl[34].bit[0] = 2; +- state->CH_Ctrl[34].val[0] = 0; +- state->CH_Ctrl[34].addr[1] = 92; +- state->CH_Ctrl[34].bit[1] = 3; +- state->CH_Ctrl[34].val[1] = 0; +- state->CH_Ctrl[34].addr[2] = 92; +- state->CH_Ctrl[34].bit[2] = 4; +- state->CH_Ctrl[34].val[2] = 0; +- state->CH_Ctrl[34].addr[3] = 92; +- state->CH_Ctrl[34].bit[3] = 5; +- state->CH_Ctrl[34].val[3] = 0; +- state->CH_Ctrl[34].addr[4] = 92; +- state->CH_Ctrl[34].bit[4] = 6; +- state->CH_Ctrl[34].val[4] = 0; +- state->CH_Ctrl[34].addr[5] = 92; +- state->CH_Ctrl[34].bit[5] = 7; +- state->CH_Ctrl[34].val[5] = 0; +- +- state->CH_Ctrl[35].Ctrl_Num = DAC_DIN_B ; +- state->CH_Ctrl[35].size = 6 ; +- state->CH_Ctrl[35].addr[0] = 93; +- state->CH_Ctrl[35].bit[0] = 2; +- state->CH_Ctrl[35].val[0] = 0; +- state->CH_Ctrl[35].addr[1] = 93; +- state->CH_Ctrl[35].bit[1] = 3; +- state->CH_Ctrl[35].val[1] = 0; +- state->CH_Ctrl[35].addr[2] = 93; +- state->CH_Ctrl[35].bit[2] = 4; +- state->CH_Ctrl[35].val[2] = 0; +- state->CH_Ctrl[35].addr[3] = 93; +- state->CH_Ctrl[35].bit[3] = 5; +- state->CH_Ctrl[35].val[3] = 0; +- state->CH_Ctrl[35].addr[4] = 93; +- state->CH_Ctrl[35].bit[4] = 6; +- state->CH_Ctrl[35].val[4] = 0; +- state->CH_Ctrl[35].addr[5] = 93; +- state->CH_Ctrl[35].bit[5] = 7; +- state->CH_Ctrl[35].val[5] = 0; +- +-#ifdef _MXL_PRODUCTION +- state->CH_Ctrl[36].Ctrl_Num = RFSYN_EN_DIV ; +- state->CH_Ctrl[36].size = 1 ; +- state->CH_Ctrl[36].addr[0] = 109; +- state->CH_Ctrl[36].bit[0] = 1; +- state->CH_Ctrl[36].val[0] = 1; +- +- state->CH_Ctrl[37].Ctrl_Num = RFSYN_DIVM ; +- state->CH_Ctrl[37].size = 2 ; +- state->CH_Ctrl[37].addr[0] = 112; +- state->CH_Ctrl[37].bit[0] = 5; +- state->CH_Ctrl[37].val[0] = 0; +- state->CH_Ctrl[37].addr[1] = 112; +- state->CH_Ctrl[37].bit[1] = 6; +- state->CH_Ctrl[37].val[1] = 0; +- +- state->CH_Ctrl[38].Ctrl_Num = DN_BYPASS_AGC_I2C ; +- state->CH_Ctrl[38].size = 1 ; +- state->CH_Ctrl[38].addr[0] = 65; +- state->CH_Ctrl[38].bit[0] = 1; +- state->CH_Ctrl[38].val[0] = 0; +-#endif +- +- return 0 ; +-} +- +-static void InitTunerControls(struct dvb_frontend *fe) +-{ +- MXL5005_RegisterInit(fe); +- MXL5005_ControlInit(fe); +-#ifdef _MXL_INTERNAL +- MXL5005_MXLControlInit(fe); +-#endif +-} +- +-static u16 MXL5005_TunerConfig(struct dvb_frontend *fe, +- u8 Mode, /* 0: Analog Mode ; 1: Digital Mode */ +- u8 IF_mode, /* for Analog Mode, 0: zero IF; 1: low IF */ +- u32 Bandwidth, /* filter channel bandwidth (6, 7, 8) */ +- u32 IF_out, /* Desired IF Out Frequency */ +- u32 Fxtal, /* XTAL Frequency */ +- u8 AGC_Mode, /* AGC Mode - Dual AGC: 0, Single AGC: 1 */ +- u16 TOP, /* 0: Dual AGC; Value: take over point */ +- u16 IF_OUT_LOAD, /* IF Out Load Resistor (200 / 300 Ohms) */ +- u8 CLOCK_OUT, /* 0: turn off clk out; 1: turn on clock out */ +- u8 DIV_OUT, /* 0: Div-1; 1: Div-4 */ +- u8 CAPSELECT, /* 0: disable On-Chip pulling cap; 1: enable */ +- u8 EN_RSSI, /* 0: disable RSSI; 1: enable RSSI */ +- +- /* Modulation Type; */ +- /* 0 - Default; 1 - DVB-T; 2 - ATSC; 3 - QAM; 4 - Analog Cable */ +- u8 Mod_Type, +- +- /* Tracking Filter */ +- /* 0 - Default; 1 - Off; 2 - Type C; 3 - Type C-H */ +- u8 TF_Type +- ) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0; +- +- state->Mode = Mode; +- state->IF_Mode = IF_mode; +- state->Chan_Bandwidth = Bandwidth; +- state->IF_OUT = IF_out; +- state->Fxtal = Fxtal; +- state->AGC_Mode = AGC_Mode; +- state->TOP = TOP; +- state->IF_OUT_LOAD = IF_OUT_LOAD; +- state->CLOCK_OUT = CLOCK_OUT; +- state->DIV_OUT = DIV_OUT; +- state->CAPSELECT = CAPSELECT; +- state->EN_RSSI = EN_RSSI; +- state->Mod_Type = Mod_Type; +- state->TF_Type = TF_Type; +- +- /* Initialize all the controls and registers */ +- InitTunerControls(fe); +- +- /* Synthesizer LO frequency calculation */ +- MXL_SynthIFLO_Calc(fe); +- +- return status; +-} +- +-static void MXL_SynthIFLO_Calc(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- if (state->Mode == 1) /* Digital Mode */ +- state->IF_LO = state->IF_OUT; +- else /* Analog Mode */ { +- if (state->IF_Mode == 0) /* Analog Zero IF mode */ +- state->IF_LO = state->IF_OUT + 400000; +- else /* Analog Low IF mode */ +- state->IF_LO = state->IF_OUT + state->Chan_Bandwidth/2; +- } +-} +- +-static void MXL_SynthRFTGLO_Calc(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- +- if (state->Mode == 1) /* Digital Mode */ { +- /* remove 20.48MHz setting for 2.6.10 */ +- state->RF_LO = state->RF_IN; +- /* change for 2.6.6 */ +- state->TG_LO = state->RF_IN - 750000; +- } else /* Analog Mode */ { +- if (state->IF_Mode == 0) /* Analog Zero IF mode */ { +- state->RF_LO = state->RF_IN - 400000; +- state->TG_LO = state->RF_IN - 1750000; +- } else /* Analog Low IF mode */ { +- state->RF_LO = state->RF_IN - state->Chan_Bandwidth/2; +- state->TG_LO = state->RF_IN - +- state->Chan_Bandwidth + 500000; +- } +- } +-} +- +-static u16 MXL_OverwriteICDefault(struct dvb_frontend *fe) +-{ +- u16 status = 0; +- +- status += MXL_ControlWrite(fe, OVERRIDE_1, 1); +- status += MXL_ControlWrite(fe, OVERRIDE_2, 1); +- status += MXL_ControlWrite(fe, OVERRIDE_3, 1); +- status += MXL_ControlWrite(fe, OVERRIDE_4, 1); +- +- return status; +-} +- +-static u16 MXL_BlockInit(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0; +- +- status += MXL_OverwriteICDefault(fe); +- +- /* Downconverter Control Dig Ana */ +- status += MXL_ControlWrite(fe, DN_IQTN_AMP_CUT, state->Mode ? 1 : 0); +- +- /* Filter Control Dig Ana */ +- status += MXL_ControlWrite(fe, BB_MODE, state->Mode ? 0 : 1); +- status += MXL_ControlWrite(fe, BB_BUF, state->Mode ? 3 : 2); +- status += MXL_ControlWrite(fe, BB_BUF_OA, state->Mode ? 1 : 0); +- status += MXL_ControlWrite(fe, BB_IQSWAP, state->Mode ? 0 : 1); +- status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 0); +- +- /* Initialize Low-Pass Filter */ +- if (state->Mode) { /* Digital Mode */ +- switch (state->Chan_Bandwidth) { +- case 8000000: +- status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 0); +- break; +- case 7000000: +- status += MXL_ControlWrite(fe, BB_DLPF_BANDSEL, 2); +- break; +- case 6000000: +- status += MXL_ControlWrite(fe, +- BB_DLPF_BANDSEL, 3); +- break; +- } +- } else { /* Analog Mode */ +- switch (state->Chan_Bandwidth) { +- case 8000000: /* Low Zero */ +- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, +- (state->IF_Mode ? 0 : 3)); +- break; +- case 7000000: +- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, +- (state->IF_Mode ? 1 : 4)); +- break; +- case 6000000: +- status += MXL_ControlWrite(fe, BB_ALPF_BANDSELECT, +- (state->IF_Mode ? 2 : 5)); +- break; +- } +- } +- +- /* Charge Pump Control Dig Ana */ +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, state->Mode ? 5 : 8); +- status += MXL_ControlWrite(fe, +- RFSYN_EN_CHP_HIGAIN, state->Mode ? 1 : 1); +- status += MXL_ControlWrite(fe, EN_CHP_LIN_B, state->Mode ? 0 : 0); +- +- /* AGC TOP Control */ +- if (state->AGC_Mode == 0) /* Dual AGC */ { +- status += MXL_ControlWrite(fe, AGC_IF, 15); +- status += MXL_ControlWrite(fe, AGC_RF, 15); +- } else /* Single AGC Mode Dig Ana */ +- status += MXL_ControlWrite(fe, AGC_RF, state->Mode ? 15 : 12); +- +- if (state->TOP == 55) /* TOP == 5.5 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x0); +- +- if (state->TOP == 72) /* TOP == 7.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x1); +- +- if (state->TOP == 92) /* TOP == 9.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x2); +- +- if (state->TOP == 110) /* TOP == 11.0 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x3); +- +- if (state->TOP == 129) /* TOP == 12.9 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x4); +- +- if (state->TOP == 147) /* TOP == 14.7 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x5); +- +- if (state->TOP == 168) /* TOP == 16.8 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x6); +- +- if (state->TOP == 194) /* TOP == 19.4 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x7); +- +- if (state->TOP == 212) /* TOP == 21.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0x9); +- +- if (state->TOP == 232) /* TOP == 23.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xA); +- +- if (state->TOP == 252) /* TOP == 25.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xB); +- +- if (state->TOP == 271) /* TOP == 27.1 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xC); +- +- if (state->TOP == 292) /* TOP == 29.2 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xD); +- +- if (state->TOP == 317) /* TOP == 31.7 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xE); +- +- if (state->TOP == 349) /* TOP == 34.9 */ +- status += MXL_ControlWrite(fe, AGC_IF, 0xF); +- +- /* IF Synthesizer Control */ +- status += MXL_IFSynthInit(fe); +- +- /* IF UpConverter Control */ +- if (state->IF_OUT_LOAD == 200) { +- status += MXL_ControlWrite(fe, DRV_RES_SEL, 6); +- status += MXL_ControlWrite(fe, I_DRIVER, 2); +- } +- if (state->IF_OUT_LOAD == 300) { +- status += MXL_ControlWrite(fe, DRV_RES_SEL, 4); +- status += MXL_ControlWrite(fe, I_DRIVER, 1); +- } +- +- /* Anti-Alias Filtering Control +- * initialise Anti-Aliasing Filter +- */ +- if (state->Mode) { /* Digital Mode */ +- if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 6280000UL) { +- status += MXL_ControlWrite(fe, EN_AAF, 1); +- status += MXL_ControlWrite(fe, EN_3P, 1); +- status += MXL_ControlWrite(fe, EN_AUX_3P, 1); +- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); +- } +- if ((state->IF_OUT == 36125000UL) || +- (state->IF_OUT == 36150000UL)) { +- status += MXL_ControlWrite(fe, EN_AAF, 1); +- status += MXL_ControlWrite(fe, EN_3P, 1); +- status += MXL_ControlWrite(fe, EN_AUX_3P, 1); +- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); +- } +- if (state->IF_OUT > 36150000UL) { +- status += MXL_ControlWrite(fe, EN_AAF, 0); +- status += MXL_ControlWrite(fe, EN_3P, 1); +- status += MXL_ControlWrite(fe, EN_AUX_3P, 1); +- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 1); +- } +- } else { /* Analog Mode */ +- if (state->IF_OUT >= 4000000UL && state->IF_OUT <= 5000000UL) { +- status += MXL_ControlWrite(fe, EN_AAF, 1); +- status += MXL_ControlWrite(fe, EN_3P, 1); +- status += MXL_ControlWrite(fe, EN_AUX_3P, 1); +- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); +- } +- if (state->IF_OUT > 5000000UL) { +- status += MXL_ControlWrite(fe, EN_AAF, 0); +- status += MXL_ControlWrite(fe, EN_3P, 0); +- status += MXL_ControlWrite(fe, EN_AUX_3P, 0); +- status += MXL_ControlWrite(fe, SEL_AAF_BAND, 0); +- } +- } +- +- /* Demod Clock Out */ +- if (state->CLOCK_OUT) +- status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 1); +- else +- status += MXL_ControlWrite(fe, SEQ_ENCLK16_CLK_OUT, 0); +- +- if (state->DIV_OUT == 1) +- status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 1); +- if (state->DIV_OUT == 0) +- status += MXL_ControlWrite(fe, SEQ_SEL4_16B, 0); +- +- /* Crystal Control */ +- if (state->CAPSELECT) +- status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 1); +- else +- status += MXL_ControlWrite(fe, XTAL_CAPSELECT, 0); +- +- if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) +- status += MXL_ControlWrite(fe, IF_SEL_DBL, 1); +- if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) +- status += MXL_ControlWrite(fe, IF_SEL_DBL, 0); +- +- if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) +- status += MXL_ControlWrite(fe, RFSYN_R_DIV, 3); +- if (state->Fxtal > 22000000UL && state->Fxtal <= 32000000UL) +- status += MXL_ControlWrite(fe, RFSYN_R_DIV, 0); +- +- /* Misc Controls */ +- if (state->Mode == 0 && state->IF_Mode == 1) /* Analog LowIF mode */ +- status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 0); +- else +- status += MXL_ControlWrite(fe, SEQ_EXTIQFSMPULSE, 1); +- +- /* status += MXL_ControlRead(fe, IF_DIVVAL, &IF_DIVVAL_Val); */ +- +- /* Set TG_R_DIV */ +- status += MXL_ControlWrite(fe, TG_R_DIV, +- MXL_Ceiling(state->Fxtal, 1000000)); +- +- /* Apply Default value to BB_INITSTATE_DLPF_TUNE */ +- +- /* RSSI Control */ +- if (state->EN_RSSI) { +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 3); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); +- +- /* TOP point */ +- status += MXL_ControlWrite(fe, RFA_FLR, 0); +- status += MXL_ControlWrite(fe, RFA_CEIL, 12); +- } +- +- /* Modulation type bit settings +- * Override the control values preset +- */ +- if (state->Mod_Type == MXL_DVBT) /* DVB-T Mode */ { +- state->AGC_Mode = 1; /* Single AGC Mode */ +- +- /* Enable RSSI */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); +- +- /* TOP point */ +- status += MXL_ControlWrite(fe, RFA_FLR, 2); +- status += MXL_ControlWrite(fe, RFA_CEIL, 13); +- if (state->IF_OUT <= 6280000UL) /* Low IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 0); +- else /* High IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +- +- } +- if (state->Mod_Type == MXL_ATSC) /* ATSC Mode */ { +- state->AGC_Mode = 1; /* Single AGC Mode */ +- +- /* Enable RSSI */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 2); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 4); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 1); +- +- /* TOP point */ +- status += MXL_ControlWrite(fe, RFA_FLR, 2); +- status += MXL_ControlWrite(fe, RFA_CEIL, 13); +- status += MXL_ControlWrite(fe, BB_INITSTATE_DLPF_TUNE, 1); +- /* Low Zero */ +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); +- +- if (state->IF_OUT <= 6280000UL) /* Low IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 0); +- else /* High IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +- } +- if (state->Mod_Type == MXL_QAM) /* QAM Mode */ { +- state->Mode = MXL_DIGITAL_MODE; +- +- /* state->AGC_Mode = 1; */ /* Single AGC Mode */ +- +- /* Disable RSSI */ /* change here for v2.6.5 */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); +- /* change here for v2.6.5 */ +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); +- +- if (state->IF_OUT <= 6280000UL) /* Low IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 0); +- else /* High IF */ +- status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); +- +- } +- if (state->Mod_Type == MXL_ANALOG_CABLE) { +- /* Analog Cable Mode */ +- /* state->Mode = MXL_DIGITAL_MODE; */ +- +- state->AGC_Mode = 1; /* Single AGC Mode */ +- +- /* Disable RSSI */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- /* change for 2.6.3 */ +- status += MXL_ControlWrite(fe, AGC_IF, 1); +- status += MXL_ControlWrite(fe, AGC_RF, 15); +- status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +- } +- +- if (state->Mod_Type == MXL_ANALOG_OTA) { +- /* Analog OTA Terrestrial mode add for 2.6.7 */ +- /* state->Mode = MXL_ANALOG_MODE; */ +- +- /* Enable RSSI */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); +- status += MXL_ControlWrite(fe, BB_IQSWAP, 1); +- } +- +- /* RSSI disable */ +- if (state->EN_RSSI == 0) { +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- } +- +- return status; +-} +- +-static u16 MXL_IFSynthInit(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0 ; +- u32 Fref = 0 ; +- u32 Kdbl, intModVal ; +- u32 fracModVal ; +- Kdbl = 2 ; +- +- if (state->Fxtal >= 12000000UL && state->Fxtal <= 16000000UL) +- Kdbl = 2 ; +- if (state->Fxtal > 16000000UL && state->Fxtal <= 32000000UL) +- Kdbl = 1 ; +- +- /* IF Synthesizer Control */ +- if (state->Mode == 0 && state->IF_Mode == 1) /* Analog Low IF mode */ { +- if (state->IF_LO == 41000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 328000000UL ; +- } +- if (state->IF_LO == 47000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 376000000UL ; +- } +- if (state->IF_LO == 54000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 324000000UL ; +- } +- if (state->IF_LO == 60000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 39250000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 314000000UL ; +- } +- if (state->IF_LO == 39650000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 317200000UL ; +- } +- if (state->IF_LO == 40150000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 321200000UL ; +- } +- if (state->IF_LO == 40650000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 325200000UL ; +- } +- } +- +- if (state->Mode || (state->Mode == 0 && state->IF_Mode == 0)) { +- if (state->IF_LO == 57000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 342000000UL ; +- } +- if (state->IF_LO == 44000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 352000000UL ; +- } +- if (state->IF_LO == 43750000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 350000000UL ; +- } +- if (state->IF_LO == 36650000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 366500000UL ; +- } +- if (state->IF_LO == 36150000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 361500000UL ; +- } +- if (state->IF_LO == 36000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 35250000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 352500000UL ; +- } +- if (state->IF_LO == 34750000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 347500000UL ; +- } +- if (state->IF_LO == 6280000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 376800000UL ; +- } +- if (state->IF_LO == 5000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 4500000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 4570000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 365600000UL ; +- } +- if (state->IF_LO == 4000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 57400000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x10); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 344400000UL ; +- } +- if (state->IF_LO == 44400000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 355200000UL ; +- } +- if (state->IF_LO == 44150000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x08); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 353200000UL ; +- } +- if (state->IF_LO == 37050000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 370500000UL ; +- } +- if (state->IF_LO == 36550000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 365500000UL ; +- } +- if (state->IF_LO == 36125000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x04); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 361250000UL ; +- } +- if (state->IF_LO == 6000000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 360000000UL ; +- } +- if (state->IF_LO == 5400000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 324000000UL ; +- } +- if (state->IF_LO == 5380000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x07); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x0C); +- Fref = 322800000UL ; +- } +- if (state->IF_LO == 5200000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 374400000UL ; +- } +- if (state->IF_LO == 4900000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x09); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 352800000UL ; +- } +- if (state->IF_LO == 4400000UL) { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x06); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 352000000UL ; +- } +- if (state->IF_LO == 4063000UL) /* add for 2.6.8 */ { +- status += MXL_ControlWrite(fe, IF_DIVVAL, 0x05); +- status += MXL_ControlWrite(fe, IF_VCO_BIAS, 0x08); +- Fref = 365670000UL ; +- } +- } +- /* CHCAL_INT_MOD_IF */ +- /* CHCAL_FRAC_MOD_IF */ +- intModVal = Fref / (state->Fxtal * Kdbl/2); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_IF, intModVal); +- +- fracModVal = (2<<15)*(Fref/1000 - (state->Fxtal/1000 * Kdbl/2) * +- intModVal); +- +- fracModVal = fracModVal / ((state->Fxtal * Kdbl/2)/1000); +- status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_IF, fracModVal); +- +- return status ; +-} +- +-static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0; +- u32 divider_val, E3, E4, E5, E5A; +- u32 Fmax, Fmin, FmaxBin, FminBin; +- u32 Kdbl_RF = 2; +- u32 tg_divval; +- u32 tg_lo; +- +- u32 Fref_TG; +- u32 Fvco; +- +- state->RF_IN = RF_Freq; +- +- MXL_SynthRFTGLO_Calc(fe); +- +- if (state->Fxtal >= 12000000UL && state->Fxtal <= 22000000UL) +- Kdbl_RF = 2; +- if (state->Fxtal > 22000000 && state->Fxtal <= 32000000) +- Kdbl_RF = 1; +- +- /* Downconverter Controls +- * Look-Up Table Implementation for: +- * DN_POLY +- * DN_RFGAIN +- * DN_CAP_RFLPF +- * DN_EN_VHFUHFBAR +- * DN_GAIN_ADJUST +- * Change the boundary reference from RF_IN to RF_LO +- */ +- if (state->RF_LO < 40000000UL) +- return -1; +- +- if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 2); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 3); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 423); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); +- } +- if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 3); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 222); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 1); +- } +- if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 3); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 147); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); +- } +- if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 3); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 9); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 2); +- } +- if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 3); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 1); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); +- } +- if (state->RF_LO > 300000000UL && state->RF_LO <= 650000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 1); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); +- } +- if (state->RF_LO > 650000000UL && state->RF_LO <= 900000000UL) { +- status += MXL_ControlWrite(fe, DN_POLY, 3); +- status += MXL_ControlWrite(fe, DN_RFGAIN, 2); +- status += MXL_ControlWrite(fe, DN_CAP_RFLPF, 0); +- status += MXL_ControlWrite(fe, DN_EN_VHFUHFBAR, 0); +- status += MXL_ControlWrite(fe, DN_GAIN_ADJUST, 3); +- } +- if (state->RF_LO > 900000000UL) +- return -1; +- +- /* DN_IQTNBUF_AMP */ +- /* DN_IQTNGNBFBIAS_BST */ +- if (state->RF_LO >= 40000000UL && state->RF_LO <= 75000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 75000000UL && state->RF_LO <= 100000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 100000000UL && state->RF_LO <= 150000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 150000000UL && state->RF_LO <= 200000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 200000000UL && state->RF_LO <= 300000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 300000000UL && state->RF_LO <= 400000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 400000000UL && state->RF_LO <= 450000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 450000000UL && state->RF_LO <= 500000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 500000000UL && state->RF_LO <= 550000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 550000000UL && state->RF_LO <= 600000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 600000000UL && state->RF_LO <= 650000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 650000000UL && state->RF_LO <= 700000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 700000000UL && state->RF_LO <= 750000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 750000000UL && state->RF_LO <= 800000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 1); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 0); +- } +- if (state->RF_LO > 800000000UL && state->RF_LO <= 850000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); +- } +- if (state->RF_LO > 850000000UL && state->RF_LO <= 900000000UL) { +- status += MXL_ControlWrite(fe, DN_IQTNBUF_AMP, 10); +- status += MXL_ControlWrite(fe, DN_IQTNGNBFBIAS_BST, 1); +- } +- +- /* +- * Set RF Synth and LO Path Control +- * +- * Look-Up table implementation for: +- * RFSYN_EN_OUTMUX +- * RFSYN_SEL_VCO_OUT +- * RFSYN_SEL_VCO_HI +- * RFSYN_SEL_DIVM +- * RFSYN_RF_DIV_BIAS +- * DN_SEL_FREQ +- * +- * Set divider_val, Fmax, Fmix to use in Equations +- */ +- FminBin = 28000000UL ; +- FmaxBin = 42500000UL ; +- if (state->RF_LO >= 40000000UL && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); +- divider_val = 64 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 42500000UL ; +- FmaxBin = 56000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); +- divider_val = 64 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 56000000UL ; +- FmaxBin = 85000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); +- divider_val = 32 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 85000000UL ; +- FmaxBin = 112000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 1); +- divider_val = 32 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 112000000UL ; +- FmaxBin = 170000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); +- divider_val = 16 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 170000000UL ; +- FmaxBin = 225000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 2); +- divider_val = 16 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 225000000UL ; +- FmaxBin = 300000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 4); +- divider_val = 8 ; +- Fmax = 340000000UL ; +- Fmin = FminBin ; +- } +- FminBin = 300000000UL ; +- FmaxBin = 340000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- divider_val = 8 ; +- Fmax = FmaxBin ; +- Fmin = 225000000UL ; +- } +- FminBin = 340000000UL ; +- FmaxBin = 450000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 2); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- divider_val = 8 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 450000000UL ; +- FmaxBin = 680000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- divider_val = 4 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 680000000UL ; +- FmaxBin = 900000000UL ; +- if (state->RF_LO > FminBin && state->RF_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- divider_val = 4 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- +- /* CHCAL_INT_MOD_RF +- * CHCAL_FRAC_MOD_RF +- * RFSYN_LPF_R +- * CHCAL_EN_INT_RF +- */ +- /* Equation E3 RFSYN_VCO_BIAS */ +- E3 = (((Fmax-state->RF_LO)/1000)*32)/((Fmax-Fmin)/1000) + 8 ; +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, E3); +- +- /* Equation E4 CHCAL_INT_MOD_RF */ +- E4 = (state->RF_LO*divider_val/1000)/(2*state->Fxtal*Kdbl_RF/1000); +- MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, E4); +- +- /* Equation E5 CHCAL_FRAC_MOD_RF CHCAL_EN_INT_RF */ +- E5 = ((2<<17)*(state->RF_LO/10000*divider_val - +- (E4*(2*state->Fxtal*Kdbl_RF)/10000))) / +- (2*state->Fxtal*Kdbl_RF/10000); +- +- status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); +- +- /* Equation E5A RFSYN_LPF_R */ +- E5A = (((Fmax - state->RF_LO)/1000)*4/((Fmax-Fmin)/1000)) + 1 ; +- status += MXL_ControlWrite(fe, RFSYN_LPF_R, E5A); +- +- /* Euqation E5B CHCAL_EN_INIT_RF */ +- status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, ((E5 == 0) ? 1 : 0)); +- /*if (E5 == 0) +- * status += MXL_ControlWrite(fe, CHCAL_EN_INT_RF, 1); +- *else +- * status += MXL_ControlWrite(fe, CHCAL_FRAC_MOD_RF, E5); +- */ +- +- /* +- * Set TG Synth +- * +- * Look-Up table implementation for: +- * TG_LO_DIVVAL +- * TG_LO_SELVAL +- * +- * Set divider_val, Fmax, Fmix to use in Equations +- */ +- if (state->TG_LO < 33000000UL) +- return -1; +- +- FminBin = 33000000UL ; +- FmaxBin = 50000000UL ; +- if (state->TG_LO >= FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x6); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); +- divider_val = 36 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 50000000UL ; +- FmaxBin = 67000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x1); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x0); +- divider_val = 24 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 67000000UL ; +- FmaxBin = 100000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0xC); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); +- divider_val = 18 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 100000000UL ; +- FmaxBin = 150000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); +- divider_val = 12 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 150000000UL ; +- FmaxBin = 200000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x2); +- divider_val = 8 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 200000000UL ; +- FmaxBin = 300000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); +- divider_val = 6 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 300000000UL ; +- FmaxBin = 400000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x3); +- divider_val = 4 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 400000000UL ; +- FmaxBin = 600000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x8); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); +- divider_val = 3 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- FminBin = 600000000UL ; +- FmaxBin = 900000000UL ; +- if (state->TG_LO > FminBin && state->TG_LO <= FmaxBin) { +- status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); +- status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); +- divider_val = 2 ; +- Fmax = FmaxBin ; +- Fmin = FminBin ; +- } +- +- /* TG_DIV_VAL */ +- tg_divval = (state->TG_LO*divider_val/100000) * +- (MXL_Ceiling(state->Fxtal, 1000000) * 100) / +- (state->Fxtal/1000); +- +- status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval); +- +- if (state->TG_LO > 600000000UL) +- status += MXL_ControlWrite(fe, TG_DIV_VAL, tg_divval + 1); +- +- Fmax = 1800000000UL ; +- Fmin = 1200000000UL ; +- +- /* prevent overflow of 32 bit unsigned integer, use +- * following equation. Edit for v2.6.4 +- */ +- /* Fref_TF = Fref_TG * 1000 */ +- Fref_TG = (state->Fxtal/1000) / MXL_Ceiling(state->Fxtal, 1000000); +- +- /* Fvco = Fvco/10 */ +- Fvco = (state->TG_LO/10000) * divider_val * Fref_TG; +- +- tg_lo = (((Fmax/10 - Fvco)/100)*32) / ((Fmax-Fmin)/1000)+8; +- +- /* below equation is same as above but much harder to debug. +- * +- * static u32 MXL_GetXtalInt(u32 Xtal_Freq) +- * { +- * if ((Xtal_Freq % 1000000) == 0) +- * return (Xtal_Freq / 10000); +- * else +- * return (((Xtal_Freq / 1000000) + 1)*100); +- * } +- * +- * u32 Xtal_Int = MXL_GetXtalInt(state->Fxtal); +- * tg_lo = ( ((Fmax/10000 * Xtal_Int)/100) - +- * ((state->TG_LO/10000)*divider_val * +- * (state->Fxtal/10000)/100) )*32/((Fmax-Fmin)/10000 * +- * Xtal_Int/100) + 8; +- */ +- +- status += MXL_ControlWrite(fe, TG_VCO_BIAS , tg_lo); +- +- /* add for 2.6.5 Special setting for QAM */ +- if (state->Mod_Type == MXL_QAM) { +- if (state->config->qam_gain != 0) +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, +- state->config->qam_gain); +- else if (state->RF_IN < 680000000) +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); +- else +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); +- } +- +- /* Off Chip Tracking Filter Control */ +- if (state->TF_Type == MXL_TF_OFF) { +- /* Tracking Filter Off State; turn off all the banks */ +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 3, 1); /* Bank1 Off */ +- status += MXL_SetGPIO(fe, 1, 1); /* Bank2 Off */ +- status += MXL_SetGPIO(fe, 4, 1); /* Bank3 Off */ +- } +- +- if (state->TF_Type == MXL_TF_C) /* Tracking Filter type C */ { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_A, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- } +- if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- } +- if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- } +- if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 0); +- } +- if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 29); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 0); +- } +- if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 0); +- } +- if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 16); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- } +- if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 7); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- } +- if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_C_H) { +- +- /* Tracking Filter type C-H for Hauppauge only */ +- status += MXL_ControlWrite(fe, DAC_DIN_A, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 150000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- } +- if (state->RF_IN >= 150000000 && state->RF_IN < 280000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- } +- if (state->RF_IN >= 280000000 && state->RF_IN < 360000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- } +- if (state->RF_IN >= 360000000 && state->RF_IN < 560000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- } +- if (state->RF_IN >= 560000000 && state->RF_IN < 580000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- } +- if (state->RF_IN >= 580000000 && state->RF_IN < 630000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- } +- if (state->RF_IN >= 630000000 && state->RF_IN < 700000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- } +- if (state->RF_IN >= 700000000 && state->RF_IN < 760000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- } +- if (state->RF_IN >= 760000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_D) { /* Tracking Filter type D */ +- +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_D_L) { +- +- /* Tracking Filter type D-L for Lumanate ONLY change 2.6.3 */ +- status += MXL_ControlWrite(fe, DAC_DIN_A, 0); +- +- /* if UHF and terrestrial => Turn off Tracking Filter */ +- if (state->RF_IN >= 471000000 && +- (state->RF_IN - 471000000)%6000000 != 0) { +- /* Turn off all the banks */ +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_ControlWrite(fe, AGC_IF, 10); +- } else { +- /* if VHF or cable => Turn on Tracking Filter */ +- if (state->RF_IN >= 43000000 && +- state->RF_IN < 140000000) { +- +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 140000000 && +- state->RF_IN < 240000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 240000000 && +- state->RF_IN < 340000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 340000000 && +- state->RF_IN < 430000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 430000000 && +- state->RF_IN < 470000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 470000000 && +- state->RF_IN < 570000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 570000000 && +- state->RF_IN < 620000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 620000000 && +- state->RF_IN < 760000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 760000000 && +- state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_A_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- } +- +- if (state->TF_Type == MXL_TF_E) /* Tracking Filter type E */ { +- +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 250000000 && state->RF_IN < 310000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 310000000 && state->RF_IN < 360000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 360000000 && state->RF_IN < 470000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 640000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_F) { +- +- /* Tracking Filter type F */ +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 160000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 160000000 && state->RF_IN < 210000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 210000000 && state->RF_IN < 300000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 300000000 && state->RF_IN < 390000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 390000000 && state->RF_IN < 515000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 515000000 && state->RF_IN < 650000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 650000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_E_2) { +- +- /* Tracking Filter type E_2 */ +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_G) { +- +- /* Tracking Filter type G add for v2.6.8 */ +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- if (state->RF_IN >= 50000000 && state->RF_IN < 190000000) { +- +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 190000000 && state->RF_IN < 280000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 280000000 && state->RF_IN < 350000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 400000000 && state->RF_IN < 470000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 470000000 && state->RF_IN < 640000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 640000000 && state->RF_IN < 820000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 820000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- +- if (state->TF_Type == MXL_TF_E_NA) { +- +- /* Tracking Filter type E-NA for Empia ONLY change for 2.6.8 */ +- status += MXL_ControlWrite(fe, DAC_DIN_B, 0); +- +- /* if UHF and terrestrial=> Turn off Tracking Filter */ +- if (state->RF_IN >= 471000000 && +- (state->RF_IN - 471000000)%6000000 != 0) { +- +- /* Turn off all the banks */ +- status += MXL_SetGPIO(fe, 3, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- +- /* 2.6.12 Turn on RSSI */ +- status += MXL_ControlWrite(fe, SEQ_EXTSYNTHCALIF, 1); +- status += MXL_ControlWrite(fe, SEQ_EXTDCCAL, 1); +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 1); +- status += MXL_ControlWrite(fe, RFA_ENCLKRFAGC, 1); +- +- /* RSSI reference point */ +- status += MXL_ControlWrite(fe, RFA_RSSI_REFH, 5); +- status += MXL_ControlWrite(fe, RFA_RSSI_REF, 3); +- status += MXL_ControlWrite(fe, RFA_RSSI_REFL, 2); +- +- /* following parameter is from analog OTA mode, +- * can be change to seek better performance */ +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); +- } else { +- /* if VHF or Cable => Turn on Tracking Filter */ +- +- /* 2.6.12 Turn off RSSI */ +- status += MXL_ControlWrite(fe, AGC_EN_RSSI, 0); +- +- /* change back from above condition */ +- status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 5); +- +- +- if (state->RF_IN >= 43000000 && state->RF_IN < 174000000) { +- +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 174000000 && state->RF_IN < 250000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 0); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 250000000 && state->RF_IN < 350000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- if (state->RF_IN >= 350000000 && state->RF_IN < 400000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 0); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 400000000 && state->RF_IN < 570000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 0); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 570000000 && state->RF_IN < 770000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 0); +- } +- if (state->RF_IN >= 770000000 && state->RF_IN <= 900000000) { +- status += MXL_ControlWrite(fe, DAC_B_ENABLE, 1); +- status += MXL_SetGPIO(fe, 4, 1); +- status += MXL_SetGPIO(fe, 1, 1); +- status += MXL_SetGPIO(fe, 3, 1); +- } +- } +- } +- return status ; +-} +- +-static u16 MXL_SetGPIO(struct dvb_frontend *fe, u8 GPIO_Num, u8 GPIO_Val) +-{ +- u16 status = 0; +- +- if (GPIO_Num == 1) +- status += MXL_ControlWrite(fe, GPIO_1B, GPIO_Val ? 0 : 1); +- +- /* GPIO2 is not available */ +- +- if (GPIO_Num == 3) { +- if (GPIO_Val == 1) { +- status += MXL_ControlWrite(fe, GPIO_3, 0); +- status += MXL_ControlWrite(fe, GPIO_3B, 0); +- } +- if (GPIO_Val == 0) { +- status += MXL_ControlWrite(fe, GPIO_3, 1); +- status += MXL_ControlWrite(fe, GPIO_3B, 1); +- } +- if (GPIO_Val == 3) { /* tri-state */ +- status += MXL_ControlWrite(fe, GPIO_3, 0); +- status += MXL_ControlWrite(fe, GPIO_3B, 1); +- } +- } +- if (GPIO_Num == 4) { +- if (GPIO_Val == 1) { +- status += MXL_ControlWrite(fe, GPIO_4, 0); +- status += MXL_ControlWrite(fe, GPIO_4B, 0); +- } +- if (GPIO_Val == 0) { +- status += MXL_ControlWrite(fe, GPIO_4, 1); +- status += MXL_ControlWrite(fe, GPIO_4B, 1); +- } +- if (GPIO_Val == 3) { /* tri-state */ +- status += MXL_ControlWrite(fe, GPIO_4, 0); +- status += MXL_ControlWrite(fe, GPIO_4B, 1); +- } +- } +- +- return status; +-} +- +-static u16 MXL_ControlWrite(struct dvb_frontend *fe, u16 ControlNum, u32 value) +-{ +- u16 status = 0; +- +- /* Will write ALL Matching Control Name */ +- /* Write Matching INIT Control */ +- status += MXL_ControlWrite_Group(fe, ControlNum, value, 1); +- /* Write Matching CH Control */ +- status += MXL_ControlWrite_Group(fe, ControlNum, value, 2); +-#ifdef _MXL_INTERNAL +- /* Write Matching MXL Control */ +- status += MXL_ControlWrite_Group(fe, ControlNum, value, 3); +-#endif +- return status; +-} +- +-static u16 MXL_ControlWrite_Group(struct dvb_frontend *fe, u16 controlNum, +- u32 value, u16 controlGroup) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 i, j, k; +- u32 highLimit; +- u32 ctrlVal; +- +- if (controlGroup == 1) /* Initial Control */ { +- +- for (i = 0; i < state->Init_Ctrl_Num; i++) { +- +- if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { +- +- highLimit = 1 << state->Init_Ctrl[i].size; +- if (value < highLimit) { +- for (j = 0; j < state->Init_Ctrl[i].size; j++) { +- state->Init_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); +- MXL_RegWriteBit(fe, (u8)(state->Init_Ctrl[i].addr[j]), +- (u8)(state->Init_Ctrl[i].bit[j]), +- (u8)((value>>j) & 0x01)); +- } +- ctrlVal = 0; +- for (k = 0; k < state->Init_Ctrl[i].size; k++) +- ctrlVal += state->Init_Ctrl[i].val[k] * (1 << k); +- } else +- return -1; +- } +- } +- } +- if (controlGroup == 2) /* Chan change Control */ { +- +- for (i = 0; i < state->CH_Ctrl_Num; i++) { +- +- if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { +- +- highLimit = 1 << state->CH_Ctrl[i].size; +- if (value < highLimit) { +- for (j = 0; j < state->CH_Ctrl[i].size; j++) { +- state->CH_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); +- MXL_RegWriteBit(fe, (u8)(state->CH_Ctrl[i].addr[j]), +- (u8)(state->CH_Ctrl[i].bit[j]), +- (u8)((value>>j) & 0x01)); +- } +- ctrlVal = 0; +- for (k = 0; k < state->CH_Ctrl[i].size; k++) +- ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); +- } else +- return -1; +- } +- } +- } +-#ifdef _MXL_INTERNAL +- if (controlGroup == 3) /* Maxlinear Control */ { +- +- for (i = 0; i < state->MXL_Ctrl_Num; i++) { +- +- if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { +- +- highLimit = (1 << state->MXL_Ctrl[i].size); +- if (value < highLimit) { +- for (j = 0; j < state->MXL_Ctrl[i].size; j++) { +- state->MXL_Ctrl[i].val[j] = (u8)((value >> j) & 0x01); +- MXL_RegWriteBit(fe, (u8)(state->MXL_Ctrl[i].addr[j]), +- (u8)(state->MXL_Ctrl[i].bit[j]), +- (u8)((value>>j) & 0x01)); +- } +- ctrlVal = 0; +- for (k = 0; k < state->MXL_Ctrl[i].size; k++) +- ctrlVal += state-> +- MXL_Ctrl[i].val[k] * +- (1 << k); +- } else +- return -1; +- } +- } +- } +-#endif +- return 0 ; /* successful return */ +-} +- +-static u16 MXL_RegRead(struct dvb_frontend *fe, u8 RegNum, u8 *RegVal) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- int i ; +- +- for (i = 0; i < 104; i++) { +- if (RegNum == state->TunerRegs[i].Reg_Num) { +- *RegVal = (u8)(state->TunerRegs[i].Reg_Val); +- return 0; +- } +- } +- +- return 1; +-} +- +-static u16 MXL_ControlRead(struct dvb_frontend *fe, u16 controlNum, u32 *value) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u32 ctrlVal ; +- u16 i, k ; +- +- for (i = 0; i < state->Init_Ctrl_Num ; i++) { +- +- if (controlNum == state->Init_Ctrl[i].Ctrl_Num) { +- +- ctrlVal = 0; +- for (k = 0; k < state->Init_Ctrl[i].size; k++) +- ctrlVal += state->Init_Ctrl[i].val[k] * (1<CH_Ctrl_Num ; i++) { +- +- if (controlNum == state->CH_Ctrl[i].Ctrl_Num) { +- +- ctrlVal = 0; +- for (k = 0; k < state->CH_Ctrl[i].size; k++) +- ctrlVal += state->CH_Ctrl[i].val[k] * (1 << k); +- *value = ctrlVal; +- return 0; +- +- } +- } +- +-#ifdef _MXL_INTERNAL +- for (i = 0; i < state->MXL_Ctrl_Num ; i++) { +- +- if (controlNum == state->MXL_Ctrl[i].Ctrl_Num) { +- +- ctrlVal = 0; +- for (k = 0; k < state->MXL_Ctrl[i].size; k++) +- ctrlVal += state->MXL_Ctrl[i].val[k] * (1<tuner_priv; +- int i ; +- +- const u8 AND_MAP[8] = { +- 0xFE, 0xFD, 0xFB, 0xF7, +- 0xEF, 0xDF, 0xBF, 0x7F } ; +- +- const u8 OR_MAP[8] = { +- 0x01, 0x02, 0x04, 0x08, +- 0x10, 0x20, 0x40, 0x80 } ; +- +- for (i = 0; i < state->TunerRegs_Num; i++) { +- if (state->TunerRegs[i].Reg_Num == address) { +- if (bitVal) +- state->TunerRegs[i].Reg_Val |= OR_MAP[bit]; +- else +- state->TunerRegs[i].Reg_Val &= AND_MAP[bit]; +- break ; +- } +- } +-} +- +-static u32 MXL_Ceiling(u32 value, u32 resolution) +-{ +- return value / resolution + (value % resolution > 0 ? 1 : 0); +-} +- +-/* Retrieve the Initialzation Registers */ +-static u16 MXL_GetInitRegister(struct dvb_frontend *fe, u8 *RegNum, +- u8 *RegVal, int *count) +-{ +- u16 status = 0; +- int i ; +- +- u8 RegAddr[] = { +- 11, 12, 13, 22, 32, 43, 44, 53, 56, 59, 73, +- 76, 77, 91, 134, 135, 137, 147, +- 156, 166, 167, 168, 25 }; +- +- *count = ARRAY_SIZE(RegAddr); +- +- status += MXL_BlockInit(fe); +- +- for (i = 0 ; i < *count; i++) { +- RegNum[i] = RegAddr[i]; +- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); +- } +- +- return status; +-} +- +-static u16 MXL_GetCHRegister(struct dvb_frontend *fe, u8 *RegNum, u8 *RegVal, +- int *count) +-{ +- u16 status = 0; +- int i ; +- +-/* add 77, 166, 167, 168 register for 2.6.12 */ +-#ifdef _MXL_PRODUCTION +- u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 65, 68, 69, 70, 73, 92, 93, 106, +- 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; +-#else +- u8 RegAddr[] = {14, 15, 16, 17, 22, 43, 68, 69, 70, 73, 92, 93, 106, +- 107, 108, 109, 110, 111, 112, 136, 138, 149, 77, 166, 167, 168 } ; +- /* +- u8 RegAddr[171]; +- for (i = 0; i <= 170; i++) +- RegAddr[i] = i; +- */ +-#endif +- +- *count = ARRAY_SIZE(RegAddr); +- +- for (i = 0 ; i < *count; i++) { +- RegNum[i] = RegAddr[i]; +- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); +- } +- +- return status; +-} +- +-static u16 MXL_GetCHRegister_ZeroIF(struct dvb_frontend *fe, u8 *RegNum, +- u8 *RegVal, int *count) +-{ +- u16 status = 0; +- int i; +- +- u8 RegAddr[] = {43, 136}; +- +- *count = ARRAY_SIZE(RegAddr); +- +- for (i = 0; i < *count; i++) { +- RegNum[i] = RegAddr[i]; +- status += MXL_RegRead(fe, RegNum[i], &RegVal[i]); +- } +- +- return status; +-} +- +-static u16 MXL_GetMasterControl(u8 *MasterReg, int state) +-{ +- if (state == 1) /* Load_Start */ +- *MasterReg = 0xF3; +- if (state == 2) /* Power_Down */ +- *MasterReg = 0x41; +- if (state == 3) /* Synth_Reset */ +- *MasterReg = 0xB1; +- if (state == 4) /* Seq_Off */ +- *MasterReg = 0xF1; +- +- return 0; +-} +- +-#ifdef _MXL_PRODUCTION +-static u16 MXL_VCORange_Test(struct dvb_frontend *fe, int VCO_Range) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0 ; +- +- if (VCO_Range == 1) { +- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- if (state->Mode == 0 && state->IF_Mode == 1) { +- /* Analog Low IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 180224); +- } +- if (state->Mode == 0 && state->IF_Mode == 0) { +- /* Analog Zero IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 222822); +- } +- if (state->Mode == 1) /* Digital Mode */ { +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 56); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 229376); +- } +- } +- +- if (VCO_Range == 2) { +- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); +- if (state->Mode == 0 && state->IF_Mode == 1) { +- /* Analog Low IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 206438); +- } +- if (state->Mode == 0 && state->IF_Mode == 0) { +- /* Analog Zero IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 206438); +- } +- if (state->Mode == 1) /* Digital Mode */ { +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 1); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 41); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 16384); +- } +- } +- +- if (VCO_Range == 3) { +- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); +- if (state->Mode == 0 && state->IF_Mode == 1) { +- /* Analog Low IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 173670); +- } +- if (state->Mode == 0 && state->IF_Mode == 0) { +- /* Analog Zero IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 44); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 173670); +- } +- if (state->Mode == 1) /* Digital Mode */ { +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 8); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 42); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 245760); +- } +- } +- +- if (VCO_Range == 4) { +- status += MXL_ControlWrite(fe, RFSYN_EN_DIV, 1); +- status += MXL_ControlWrite(fe, RFSYN_EN_OUTMUX, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_DIVM, 0); +- status += MXL_ControlWrite(fe, RFSYN_DIVM, 1); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_OUT, 1); +- status += MXL_ControlWrite(fe, RFSYN_RF_DIV_BIAS, 1); +- status += MXL_ControlWrite(fe, DN_SEL_FREQ, 0); +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); +- if (state->Mode == 0 && state->IF_Mode == 1) { +- /* Analog Low IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 206438); +- } +- if (state->Mode == 0 && state->IF_Mode == 0) { +- /* Analog Zero IF Mode */ +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 206438); +- } +- if (state->Mode == 1) /* Digital Mode */ { +- status += MXL_ControlWrite(fe, RFSYN_SEL_VCO_HI, 0); +- status += MXL_ControlWrite(fe, RFSYN_VCO_BIAS, 40); +- status += MXL_ControlWrite(fe, CHCAL_INT_MOD_RF, 27); +- status += MXL_ControlWrite(fe, +- CHCAL_FRAC_MOD_RF, 212992); +- } +- } +- +- return status; +-} +- +-static u16 MXL_Hystersis_Test(struct dvb_frontend *fe, int Hystersis) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u16 status = 0; +- +- if (Hystersis == 1) +- status += MXL_ControlWrite(fe, DN_BYPASS_AGC_I2C, 1); +- +- return status; +-} +-#endif +-/* End: Reference driver code found in the Realtek driver that +- * is copyright MaxLinear */ +- +-/* ---------------------------------------------------------------- +- * Begin: Everything after here is new code to adapt the +- * proprietary Realtek driver into a Linux API tuner. +- * Copyright (C) 2008 Steven Toth +- */ +-static int mxl5005s_reset(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- int ret = 0; +- +- u8 buf[2] = { 0xff, 0x00 }; +- struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, +- .buf = buf, .len = 2 }; +- +- dprintk(2, "%s()\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mxl5005s I2C reset failed\n"); +- ret = -EREMOTEIO; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-/* Write a single byte to a single reg, latch the value if required by +- * following the transaction with the latch byte. +- */ +-static int mxl5005s_writereg(struct dvb_frontend *fe, u8 reg, u8 val, int latch) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- u8 buf[3] = { reg, val, MXL5005S_LATCH_BYTE }; +- struct i2c_msg msg = { .addr = state->config->i2c_address, .flags = 0, +- .buf = buf, .len = 3 }; +- +- if (latch == 0) +- msg.len = 2; +- +- dprintk(2, "%s(0x%x, 0x%x, 0x%x)\n", __func__, reg, val, msg.addr); +- +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "mxl5005s I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mxl5005s_writeregs(struct dvb_frontend *fe, u8 *addrtable, +- u8 *datatable, u8 len) +-{ +- int ret = 0, i; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- for (i = 0 ; i < len-1; i++) { +- ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 0); +- if (ret < 0) +- break; +- } +- +- ret = mxl5005s_writereg(fe, addrtable[i], datatable[i], 1); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-static int mxl5005s_init(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- +- dprintk(1, "%s()\n", __func__); +- state->current_mode = MXL_QAM; +- return mxl5005s_reconfigure(fe, MXL_QAM, MXL5005S_BANDWIDTH_6MHZ); +-} +- +-static int mxl5005s_reconfigure(struct dvb_frontend *fe, u32 mod_type, +- u32 bandwidth) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- +- u8 AddrTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; +- u8 ByteTable[MXL5005S_REG_WRITING_TABLE_LEN_MAX]; +- int TableLen; +- +- dprintk(1, "%s(type=%d, bw=%d)\n", __func__, mod_type, bandwidth); +- +- mxl5005s_reset(fe); +- +- /* Tuner initialization stage 0 */ +- MXL_GetMasterControl(ByteTable, MC_SYNTH_RESET); +- AddrTable[0] = MASTER_CONTROL_ADDR; +- ByteTable[0] |= state->config->AgcMasterByte; +- +- mxl5005s_writeregs(fe, AddrTable, ByteTable, 1); +- +- mxl5005s_AssignTunerMode(fe, mod_type, bandwidth); +- +- /* Tuner initialization stage 1 */ +- MXL_GetInitRegister(fe, AddrTable, ByteTable, &TableLen); +- +- mxl5005s_writeregs(fe, AddrTable, ByteTable, TableLen); +- +- return 0; +-} +- +-static int mxl5005s_AssignTunerMode(struct dvb_frontend *fe, u32 mod_type, +- u32 bandwidth) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- struct mxl5005s_config *c = state->config; +- +- InitTunerControls(fe); +- +- /* Set MxL5005S parameters. */ +- MXL5005_TunerConfig( +- fe, +- c->mod_mode, +- c->if_mode, +- bandwidth, +- c->if_freq, +- c->xtal_freq, +- c->agc_mode, +- c->top, +- c->output_load, +- c->clock_out, +- c->div_out, +- c->cap_select, +- c->rssi_enable, +- mod_type, +- c->tracking_filter); +- +- return 0; +-} +- +-static int mxl5005s_set_params(struct dvb_frontend *fe) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- u32 req_mode, req_bw = 0; +- int ret; +- +- dprintk(1, "%s()\n", __func__); +- +- switch (delsys) { +- case SYS_ATSC: +- req_mode = MXL_ATSC; +- req_bw = MXL5005S_BANDWIDTH_6MHZ; +- break; +- case SYS_DVBC_ANNEX_B: +- req_mode = MXL_QAM; +- req_bw = MXL5005S_BANDWIDTH_6MHZ; +- break; +- default: /* Assume DVB-T */ +- req_mode = MXL_DVBT; +- switch (bw) { +- case 6000000: +- req_bw = MXL5005S_BANDWIDTH_6MHZ; +- break; +- case 7000000: +- req_bw = MXL5005S_BANDWIDTH_7MHZ; +- break; +- case 8000000: +- case 0: +- req_bw = MXL5005S_BANDWIDTH_8MHZ; +- break; +- default: +- return -EINVAL; +- } +- } +- +- /* Change tuner for new modulation type if reqd */ +- if (req_mode != state->current_mode || +- req_bw != state->Chan_Bandwidth) { +- state->current_mode = req_mode; +- ret = mxl5005s_reconfigure(fe, req_mode, req_bw); +- +- } else +- ret = 0; +- +- if (ret == 0) { +- dprintk(1, "%s() freq=%d\n", __func__, c->frequency); +- ret = mxl5005s_SetRfFreqHz(fe, c->frequency); +- } +- +- return ret; +-} +- +-static int mxl5005s_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- +- *frequency = state->RF_IN; +- +- return 0; +-} +- +-static int mxl5005s_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct mxl5005s_state *state = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- +- *bandwidth = state->Chan_Bandwidth; +- +- return 0; +-} +- +-static int mxl5005s_release(struct dvb_frontend *fe) +-{ +- dprintk(1, "%s()\n", __func__); +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops mxl5005s_tuner_ops = { +- .info = { +- .name = "MaxLinear MXL5005S", +- .frequency_min = 48000000, +- .frequency_max = 860000000, +- .frequency_step = 50000, +- }, +- +- .release = mxl5005s_release, +- .init = mxl5005s_init, +- +- .set_params = mxl5005s_set_params, +- .get_frequency = mxl5005s_get_frequency, +- .get_bandwidth = mxl5005s_get_bandwidth, +-}; +- +-struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mxl5005s_config *config) +-{ +- struct mxl5005s_state *state = NULL; +- dprintk(1, "%s()\n", __func__); +- +- state = kzalloc(sizeof(struct mxl5005s_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- state->frontend = fe; +- state->config = config; +- state->i2c = i2c; +- +- printk(KERN_INFO "MXL5005S: Attached at address 0x%02x\n", +- config->i2c_address); +- +- memcpy(&fe->ops.tuner_ops, &mxl5005s_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = state; +- return fe; +-} +-EXPORT_SYMBOL(mxl5005s_attach); +- +-MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h +deleted file mode 100644 +index fc8a1ff..0000000 +--- a/drivers/media/common/tuners/mxl5005s.h ++++ /dev/null +@@ -1,135 +0,0 @@ +-/* +- MaxLinear MXL5005S VSB/QAM/DVBT tuner driver +- +- Copyright (C) 2008 MaxLinear +- Copyright (C) 2008 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef __MXL5005S_H +-#define __MXL5005S_H +- +-#include +-#include "dvb_frontend.h" +- +-struct mxl5005s_config { +- +- /* 7 bit i2c address */ +- u8 i2c_address; +- +-#define IF_FREQ_4570000HZ 4570000 +-#define IF_FREQ_4571429HZ 4571429 +-#define IF_FREQ_5380000HZ 5380000 +-#define IF_FREQ_36000000HZ 36000000 +-#define IF_FREQ_36125000HZ 36125000 +-#define IF_FREQ_36166667HZ 36166667 +-#define IF_FREQ_44000000HZ 44000000 +- u32 if_freq; +- +-#define CRYSTAL_FREQ_4000000HZ 4000000 +-#define CRYSTAL_FREQ_16000000HZ 16000000 +-#define CRYSTAL_FREQ_25000000HZ 25000000 +-#define CRYSTAL_FREQ_28800000HZ 28800000 +- u32 xtal_freq; +- +-#define MXL_DUAL_AGC 0 +-#define MXL_SINGLE_AGC 1 +- u8 agc_mode; +- +-#define MXL_TF_DEFAULT 0 +-#define MXL_TF_OFF 1 +-#define MXL_TF_C 2 +-#define MXL_TF_C_H 3 +-#define MXL_TF_D 4 +-#define MXL_TF_D_L 5 +-#define MXL_TF_E 6 +-#define MXL_TF_F 7 +-#define MXL_TF_E_2 8 +-#define MXL_TF_E_NA 9 +-#define MXL_TF_G 10 +- u8 tracking_filter; +- +-#define MXL_RSSI_DISABLE 0 +-#define MXL_RSSI_ENABLE 1 +- u8 rssi_enable; +- +-#define MXL_CAP_SEL_DISABLE 0 +-#define MXL_CAP_SEL_ENABLE 1 +- u8 cap_select; +- +-#define MXL_DIV_OUT_1 0 +-#define MXL_DIV_OUT_4 1 +- u8 div_out; +- +-#define MXL_CLOCK_OUT_DISABLE 0 +-#define MXL_CLOCK_OUT_ENABLE 1 +- u8 clock_out; +- +-#define MXL5005S_IF_OUTPUT_LOAD_200_OHM 200 +-#define MXL5005S_IF_OUTPUT_LOAD_300_OHM 300 +- u32 output_load; +- +-#define MXL5005S_TOP_5P5 55 +-#define MXL5005S_TOP_7P2 72 +-#define MXL5005S_TOP_9P2 92 +-#define MXL5005S_TOP_11P0 110 +-#define MXL5005S_TOP_12P9 129 +-#define MXL5005S_TOP_14P7 147 +-#define MXL5005S_TOP_16P8 168 +-#define MXL5005S_TOP_19P4 194 +-#define MXL5005S_TOP_21P2 212 +-#define MXL5005S_TOP_23P2 232 +-#define MXL5005S_TOP_25P2 252 +-#define MXL5005S_TOP_27P1 271 +-#define MXL5005S_TOP_29P2 292 +-#define MXL5005S_TOP_31P7 317 +-#define MXL5005S_TOP_34P9 349 +- u32 top; +- +-#define MXL_ANALOG_MODE 0 +-#define MXL_DIGITAL_MODE 1 +- u8 mod_mode; +- +-#define MXL_ZERO_IF 0 +-#define MXL_LOW_IF 1 +- u8 if_mode; +- +- /* Some boards need to override the built-in logic for determining +- the gain when in QAM mode (the HVR-1600 is one such case) */ +- u8 qam_gain; +- +- /* Stuff I don't know what to do with */ +- u8 AgcMasterByte; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MXL5005S) || \ +- (defined(CONFIG_MEDIA_TUNER_MXL5005S_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mxl5005s_config *config); +-#else +-static inline struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct mxl5005s_config *config) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_TUNER_MXL5005S */ +- +-#endif /* __MXL5005S_H */ +- +diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c +deleted file mode 100644 +index 69e453e..0000000 +--- a/drivers/media/common/tuners/mxl5007t.c ++++ /dev/null +@@ -1,928 +0,0 @@ +-/* +- * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner +- * +- * Copyright (C) 2008, 2009 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "mxl5007t.h" +- +-static DEFINE_MUTEX(mxl5007t_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-static int mxl5007t_debug; +-module_param_named(debug, mxl5007t_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level"); +- +-/* ------------------------------------------------------------------------- */ +- +-#define mxl_printk(kern, fmt, arg...) \ +- printk(kern "%s: " fmt "\n", __func__, ##arg) +- +-#define mxl_err(fmt, arg...) \ +- mxl_printk(KERN_ERR, "%d: " fmt, __LINE__, ##arg) +- +-#define mxl_warn(fmt, arg...) \ +- mxl_printk(KERN_WARNING, fmt, ##arg) +- +-#define mxl_info(fmt, arg...) \ +- mxl_printk(KERN_INFO, fmt, ##arg) +- +-#define mxl_debug(fmt, arg...) \ +-({ \ +- if (mxl5007t_debug) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg); \ +-}) +- +-#define mxl_fail(ret) \ +-({ \ +- int __ret; \ +- __ret = (ret < 0); \ +- if (__ret) \ +- mxl_printk(KERN_ERR, "error %d on line %d", \ +- ret, __LINE__); \ +- __ret; \ +-}) +- +-/* ------------------------------------------------------------------------- */ +- +-#define MHz 1000000 +- +-enum mxl5007t_mode { +- MxL_MODE_ISDBT = 0, +- MxL_MODE_DVBT = 1, +- MxL_MODE_ATSC = 2, +- MxL_MODE_CABLE = 0x10, +-}; +- +-enum mxl5007t_chip_version { +- MxL_UNKNOWN_ID = 0x00, +- MxL_5007_V1_F1 = 0x11, +- MxL_5007_V1_F2 = 0x12, +- MxL_5007_V4 = 0x14, +- MxL_5007_V2_100_F1 = 0x21, +- MxL_5007_V2_100_F2 = 0x22, +- MxL_5007_V2_200_F1 = 0x23, +- MxL_5007_V2_200_F2 = 0x24, +-}; +- +-struct reg_pair_t { +- u8 reg; +- u8 val; +-}; +- +-/* ------------------------------------------------------------------------- */ +- +-static struct reg_pair_t init_tab[] = { +- { 0x02, 0x06 }, +- { 0x03, 0x48 }, +- { 0x05, 0x04 }, +- { 0x06, 0x10 }, +- { 0x2e, 0x15 }, /* OVERRIDE */ +- { 0x30, 0x10 }, /* OVERRIDE */ +- { 0x45, 0x58 }, /* OVERRIDE */ +- { 0x48, 0x19 }, /* OVERRIDE */ +- { 0x52, 0x03 }, /* OVERRIDE */ +- { 0x53, 0x44 }, /* OVERRIDE */ +- { 0x6a, 0x4b }, /* OVERRIDE */ +- { 0x76, 0x00 }, /* OVERRIDE */ +- { 0x78, 0x18 }, /* OVERRIDE */ +- { 0x7a, 0x17 }, /* OVERRIDE */ +- { 0x85, 0x06 }, /* OVERRIDE */ +- { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ +- { 0, 0 } +-}; +- +-static struct reg_pair_t init_tab_cable[] = { +- { 0x02, 0x06 }, +- { 0x03, 0x48 }, +- { 0x05, 0x04 }, +- { 0x06, 0x10 }, +- { 0x09, 0x3f }, +- { 0x0a, 0x3f }, +- { 0x0b, 0x3f }, +- { 0x2e, 0x15 }, /* OVERRIDE */ +- { 0x30, 0x10 }, /* OVERRIDE */ +- { 0x45, 0x58 }, /* OVERRIDE */ +- { 0x48, 0x19 }, /* OVERRIDE */ +- { 0x52, 0x03 }, /* OVERRIDE */ +- { 0x53, 0x44 }, /* OVERRIDE */ +- { 0x6a, 0x4b }, /* OVERRIDE */ +- { 0x76, 0x00 }, /* OVERRIDE */ +- { 0x78, 0x18 }, /* OVERRIDE */ +- { 0x7a, 0x17 }, /* OVERRIDE */ +- { 0x85, 0x06 }, /* OVERRIDE */ +- { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */ +- { 0, 0 } +-}; +- +-/* ------------------------------------------------------------------------- */ +- +-static struct reg_pair_t reg_pair_rftune[] = { +- { 0x0f, 0x00 }, /* abort tune */ +- { 0x0c, 0x15 }, +- { 0x0d, 0x40 }, +- { 0x0e, 0x0e }, +- { 0x1f, 0x87 }, /* OVERRIDE */ +- { 0x20, 0x1f }, /* OVERRIDE */ +- { 0x21, 0x87 }, /* OVERRIDE */ +- { 0x22, 0x1f }, /* OVERRIDE */ +- { 0x80, 0x01 }, /* freq dependent */ +- { 0x0f, 0x01 }, /* start tune */ +- { 0, 0 } +-}; +- +-/* ------------------------------------------------------------------------- */ +- +-struct mxl5007t_state { +- struct list_head hybrid_tuner_instance_list; +- struct tuner_i2c_props i2c_props; +- +- struct mutex lock; +- +- struct mxl5007t_config *config; +- +- enum mxl5007t_chip_version chip_id; +- +- struct reg_pair_t tab_init[ARRAY_SIZE(init_tab)]; +- struct reg_pair_t tab_init_cable[ARRAY_SIZE(init_tab_cable)]; +- struct reg_pair_t tab_rftune[ARRAY_SIZE(reg_pair_rftune)]; +- +- enum mxl5007t_if_freq if_freq; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-/* ------------------------------------------------------------------------- */ +- +-/* called by _init and _rftun to manipulate the register arrays */ +- +-static void set_reg_bits(struct reg_pair_t *reg_pair, u8 reg, u8 mask, u8 val) +-{ +- unsigned int i = 0; +- +- while (reg_pair[i].reg || reg_pair[i].val) { +- if (reg_pair[i].reg == reg) { +- reg_pair[i].val &= ~mask; +- reg_pair[i].val |= val; +- } +- i++; +- +- } +- return; +-} +- +-static void copy_reg_bits(struct reg_pair_t *reg_pair1, +- struct reg_pair_t *reg_pair2) +-{ +- unsigned int i, j; +- +- i = j = 0; +- +- while (reg_pair1[i].reg || reg_pair1[i].val) { +- while (reg_pair2[j].reg || reg_pair2[j].val) { +- if (reg_pair1[i].reg != reg_pair2[j].reg) { +- j++; +- continue; +- } +- reg_pair2[j].val = reg_pair1[i].val; +- break; +- } +- i++; +- } +- return; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static void mxl5007t_set_mode_bits(struct mxl5007t_state *state, +- enum mxl5007t_mode mode, +- s32 if_diff_out_level) +-{ +- switch (mode) { +- case MxL_MODE_ATSC: +- set_reg_bits(state->tab_init, 0x06, 0x1f, 0x12); +- break; +- case MxL_MODE_DVBT: +- set_reg_bits(state->tab_init, 0x06, 0x1f, 0x11); +- break; +- case MxL_MODE_ISDBT: +- set_reg_bits(state->tab_init, 0x06, 0x1f, 0x10); +- break; +- case MxL_MODE_CABLE: +- set_reg_bits(state->tab_init_cable, 0x09, 0xff, 0xc1); +- set_reg_bits(state->tab_init_cable, 0x0a, 0xff, +- 8 - if_diff_out_level); +- set_reg_bits(state->tab_init_cable, 0x0b, 0xff, 0x17); +- break; +- default: +- mxl_fail(-EINVAL); +- } +- return; +-} +- +-static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state, +- enum mxl5007t_if_freq if_freq, +- int invert_if) +-{ +- u8 val; +- +- switch (if_freq) { +- case MxL_IF_4_MHZ: +- val = 0x00; +- break; +- case MxL_IF_4_5_MHZ: +- val = 0x02; +- break; +- case MxL_IF_4_57_MHZ: +- val = 0x03; +- break; +- case MxL_IF_5_MHZ: +- val = 0x04; +- break; +- case MxL_IF_5_38_MHZ: +- val = 0x05; +- break; +- case MxL_IF_6_MHZ: +- val = 0x06; +- break; +- case MxL_IF_6_28_MHZ: +- val = 0x07; +- break; +- case MxL_IF_9_1915_MHZ: +- val = 0x08; +- break; +- case MxL_IF_35_25_MHZ: +- val = 0x09; +- break; +- case MxL_IF_36_15_MHZ: +- val = 0x0a; +- break; +- case MxL_IF_44_MHZ: +- val = 0x0b; +- break; +- default: +- mxl_fail(-EINVAL); +- return; +- } +- set_reg_bits(state->tab_init, 0x02, 0x0f, val); +- +- /* set inverted IF or normal IF */ +- set_reg_bits(state->tab_init, 0x02, 0x10, invert_if ? 0x10 : 0x00); +- +- state->if_freq = if_freq; +- +- return; +-} +- +-static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state, +- enum mxl5007t_xtal_freq xtal_freq) +-{ +- switch (xtal_freq) { +- case MxL_XTAL_16_MHZ: +- /* select xtal freq & ref freq */ +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x00); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x00); +- break; +- case MxL_XTAL_20_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x10); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x01); +- break; +- case MxL_XTAL_20_25_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x20); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x02); +- break; +- case MxL_XTAL_20_48_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x30); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x03); +- break; +- case MxL_XTAL_24_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x40); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x04); +- break; +- case MxL_XTAL_25_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x50); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x05); +- break; +- case MxL_XTAL_25_14_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x60); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x06); +- break; +- case MxL_XTAL_27_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x70); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x07); +- break; +- case MxL_XTAL_28_8_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x80); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x08); +- break; +- case MxL_XTAL_32_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0x90); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x09); +- break; +- case MxL_XTAL_40_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0xa0); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0a); +- break; +- case MxL_XTAL_44_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0xb0); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0b); +- break; +- case MxL_XTAL_48_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0xc0); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0c); +- break; +- case MxL_XTAL_49_3811_MHZ: +- set_reg_bits(state->tab_init, 0x03, 0xf0, 0xd0); +- set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0d); +- break; +- default: +- mxl_fail(-EINVAL); +- return; +- } +- +- return; +-} +- +-static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state, +- enum mxl5007t_mode mode) +-{ +- struct mxl5007t_config *cfg = state->config; +- +- memcpy(&state->tab_init, &init_tab, sizeof(init_tab)); +- memcpy(&state->tab_init_cable, &init_tab_cable, sizeof(init_tab_cable)); +- +- mxl5007t_set_mode_bits(state, mode, cfg->if_diff_out_level); +- mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if); +- mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz); +- +- set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable); +- set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3); +- set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp); +- +- if (mode >= MxL_MODE_CABLE) { +- copy_reg_bits(state->tab_init, state->tab_init_cable); +- return state->tab_init_cable; +- } else +- return state->tab_init; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-enum mxl5007t_bw_mhz { +- MxL_BW_6MHz = 6, +- MxL_BW_7MHz = 7, +- MxL_BW_8MHz = 8, +-}; +- +-static void mxl5007t_set_bw_bits(struct mxl5007t_state *state, +- enum mxl5007t_bw_mhz bw) +-{ +- u8 val; +- +- switch (bw) { +- case MxL_BW_6MHz: +- val = 0x15; /* set DIG_MODEINDEX, DIG_MODEINDEX_A, +- * and DIG_MODEINDEX_CSF */ +- break; +- case MxL_BW_7MHz: +- val = 0x2a; +- break; +- case MxL_BW_8MHz: +- val = 0x3f; +- break; +- default: +- mxl_fail(-EINVAL); +- return; +- } +- set_reg_bits(state->tab_rftune, 0x0c, 0x3f, val); +- +- return; +-} +- +-static struct +-reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state, +- u32 rf_freq, enum mxl5007t_bw_mhz bw) +-{ +- u32 dig_rf_freq = 0; +- u32 temp; +- u32 frac_divider = 1000000; +- unsigned int i; +- +- memcpy(&state->tab_rftune, ®_pair_rftune, sizeof(reg_pair_rftune)); +- +- mxl5007t_set_bw_bits(state, bw); +- +- /* Convert RF frequency into 16 bits => +- * 10 bit integer (MHz) + 6 bit fraction */ +- dig_rf_freq = rf_freq / MHz; +- +- temp = rf_freq % MHz; +- +- for (i = 0; i < 6; i++) { +- dig_rf_freq <<= 1; +- frac_divider /= 2; +- if (temp > frac_divider) { +- temp -= frac_divider; +- dig_rf_freq++; +- } +- } +- +- /* add to have shift center point by 7.8124 kHz */ +- if (temp > 7812) +- dig_rf_freq++; +- +- set_reg_bits(state->tab_rftune, 0x0d, 0xff, (u8) dig_rf_freq); +- set_reg_bits(state->tab_rftune, 0x0e, 0xff, (u8) (dig_rf_freq >> 8)); +- +- if (rf_freq >= 333000000) +- set_reg_bits(state->tab_rftune, 0x80, 0x40, 0x40); +- +- return state->tab_rftune; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_write_reg(struct mxl5007t_state *state, u8 reg, u8 val) +-{ +- u8 buf[] = { reg, val }; +- struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0, +- .buf = buf, .len = 2 }; +- int ret; +- +- ret = i2c_transfer(state->i2c_props.adap, &msg, 1); +- if (ret != 1) { +- mxl_err("failed!"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mxl5007t_write_regs(struct mxl5007t_state *state, +- struct reg_pair_t *reg_pair) +-{ +- unsigned int i = 0; +- int ret = 0; +- +- while ((ret == 0) && (reg_pair[i].reg || reg_pair[i].val)) { +- ret = mxl5007t_write_reg(state, +- reg_pair[i].reg, reg_pair[i].val); +- i++; +- } +- return ret; +-} +- +-static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val) +-{ +- u8 buf[2] = { 0xfb, reg }; +- struct i2c_msg msg[] = { +- { .addr = state->i2c_props.addr, .flags = 0, +- .buf = buf, .len = 2 }, +- { .addr = state->i2c_props.addr, .flags = I2C_M_RD, +- .buf = val, .len = 1 }, +- }; +- int ret; +- +- ret = i2c_transfer(state->i2c_props.adap, msg, 2); +- if (ret != 2) { +- mxl_err("failed!"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mxl5007t_soft_reset(struct mxl5007t_state *state) +-{ +- u8 d = 0xff; +- struct i2c_msg msg = { +- .addr = state->i2c_props.addr, .flags = 0, +- .buf = &d, .len = 1 +- }; +- int ret = i2c_transfer(state->i2c_props.adap, &msg, 1); +- +- if (ret != 1) { +- mxl_err("failed!"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int mxl5007t_tuner_init(struct mxl5007t_state *state, +- enum mxl5007t_mode mode) +-{ +- struct reg_pair_t *init_regs; +- int ret; +- +- ret = mxl5007t_soft_reset(state); +- if (mxl_fail(ret)) +- goto fail; +- +- /* calculate initialization reg array */ +- init_regs = mxl5007t_calc_init_regs(state, mode); +- +- ret = mxl5007t_write_regs(state, init_regs); +- if (mxl_fail(ret)) +- goto fail; +- mdelay(1); +-fail: +- return ret; +-} +- +-static int mxl5007t_tuner_rf_tune(struct mxl5007t_state *state, u32 rf_freq_hz, +- enum mxl5007t_bw_mhz bw) +-{ +- struct reg_pair_t *rf_tune_regs; +- int ret; +- +- /* calculate channel change reg array */ +- rf_tune_regs = mxl5007t_calc_rf_tune_regs(state, rf_freq_hz, bw); +- +- ret = mxl5007t_write_regs(state, rf_tune_regs); +- if (mxl_fail(ret)) +- goto fail; +- msleep(3); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_synth_lock_status(struct mxl5007t_state *state, +- int *rf_locked, int *ref_locked) +-{ +- u8 d; +- int ret; +- +- *rf_locked = 0; +- *ref_locked = 0; +- +- ret = mxl5007t_read_reg(state, 0xd8, &d); +- if (mxl_fail(ret)) +- goto fail; +- +- if ((d & 0x0c) == 0x0c) +- *rf_locked = 1; +- +- if ((d & 0x03) == 0x03) +- *ref_locked = 1; +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- int rf_locked, ref_locked, ret; +- +- *status = 0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = mxl5007t_synth_lock_status(state, &rf_locked, &ref_locked); +- if (mxl_fail(ret)) +- goto fail; +- mxl_debug("%s%s", rf_locked ? "rf locked " : "", +- ref_locked ? "ref locked" : ""); +- +- if ((rf_locked) || (ref_locked)) +- *status |= TUNER_STATUS_LOCKED; +-fail: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- struct mxl5007t_state *state = fe->tuner_priv; +- enum mxl5007t_bw_mhz bw; +- enum mxl5007t_mode mode; +- int ret; +- u32 freq = c->frequency; +- +- switch (delsys) { +- case SYS_ATSC: +- mode = MxL_MODE_ATSC; +- bw = MxL_BW_6MHz; +- break; +- case SYS_DVBC_ANNEX_B: +- mode = MxL_MODE_CABLE; +- bw = MxL_BW_6MHz; +- break; +- case SYS_DVBT: +- case SYS_DVBT2: +- mode = MxL_MODE_DVBT; +- switch (c->bandwidth_hz) { +- case 6000000: +- bw = MxL_BW_6MHz; +- break; +- case 7000000: +- bw = MxL_BW_7MHz; +- break; +- case 8000000: +- bw = MxL_BW_8MHz; +- break; +- default: +- return -EINVAL; +- } +- break; +- default: +- mxl_err("modulation type not supported!"); +- return -EINVAL; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- mutex_lock(&state->lock); +- +- ret = mxl5007t_tuner_init(state, mode); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl5007t_tuner_rf_tune(state, freq, bw); +- if (mxl_fail(ret)) +- goto fail; +- +- state->frequency = freq; +- state->bandwidth = c->bandwidth_hz; +-fail: +- mutex_unlock(&state->lock); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_init(struct dvb_frontend *fe) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- /* wake from standby */ +- ret = mxl5007t_write_reg(state, 0x01, 0x01); +- mxl_fail(ret); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-static int mxl5007t_sleep(struct dvb_frontend *fe) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- /* enter standby mode */ +- ret = mxl5007t_write_reg(state, 0x01, 0x00); +- mxl_fail(ret); +- ret = mxl5007t_write_reg(state, 0x0f, 0x00); +- mxl_fail(ret); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl5007t_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- *frequency = state->frequency; +- return 0; +-} +- +-static int mxl5007t_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- *bandwidth = state->bandwidth; +- return 0; +-} +- +-static int mxl5007t_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- +- *frequency = 0; +- +- switch (state->if_freq) { +- case MxL_IF_4_MHZ: +- *frequency = 4000000; +- break; +- case MxL_IF_4_5_MHZ: +- *frequency = 4500000; +- break; +- case MxL_IF_4_57_MHZ: +- *frequency = 4570000; +- break; +- case MxL_IF_5_MHZ: +- *frequency = 5000000; +- break; +- case MxL_IF_5_38_MHZ: +- *frequency = 5380000; +- break; +- case MxL_IF_6_MHZ: +- *frequency = 6000000; +- break; +- case MxL_IF_6_28_MHZ: +- *frequency = 6280000; +- break; +- case MxL_IF_9_1915_MHZ: +- *frequency = 9191500; +- break; +- case MxL_IF_35_25_MHZ: +- *frequency = 35250000; +- break; +- case MxL_IF_36_15_MHZ: +- *frequency = 36150000; +- break; +- case MxL_IF_44_MHZ: +- *frequency = 44000000; +- break; +- } +- return 0; +-} +- +-static int mxl5007t_release(struct dvb_frontend *fe) +-{ +- struct mxl5007t_state *state = fe->tuner_priv; +- +- mutex_lock(&mxl5007t_list_mutex); +- +- if (state) +- hybrid_tuner_release_state(state); +- +- mutex_unlock(&mxl5007t_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static struct dvb_tuner_ops mxl5007t_tuner_ops = { +- .info = { +- .name = "MaxLinear MxL5007T", +- }, +- .init = mxl5007t_init, +- .sleep = mxl5007t_sleep, +- .set_params = mxl5007t_set_params, +- .get_status = mxl5007t_get_status, +- .get_frequency = mxl5007t_get_frequency, +- .get_bandwidth = mxl5007t_get_bandwidth, +- .release = mxl5007t_release, +- .get_if_frequency = mxl5007t_get_if_frequency, +-}; +- +-static int mxl5007t_get_chip_id(struct mxl5007t_state *state) +-{ +- char *name; +- int ret; +- u8 id; +- +- ret = mxl5007t_read_reg(state, 0xd9, &id); +- if (mxl_fail(ret)) +- goto fail; +- +- switch (id) { +- case MxL_5007_V1_F1: +- name = "MxL5007.v1.f1"; +- break; +- case MxL_5007_V1_F2: +- name = "MxL5007.v1.f2"; +- break; +- case MxL_5007_V2_100_F1: +- name = "MxL5007.v2.100.f1"; +- break; +- case MxL_5007_V2_100_F2: +- name = "MxL5007.v2.100.f2"; +- break; +- case MxL_5007_V2_200_F1: +- name = "MxL5007.v2.200.f1"; +- break; +- case MxL_5007_V2_200_F2: +- name = "MxL5007.v2.200.f2"; +- break; +- case MxL_5007_V4: +- name = "MxL5007T.v4"; +- break; +- default: +- name = "MxL5007T"; +- printk(KERN_WARNING "%s: unknown rev (%02x)\n", __func__, id); +- id = MxL_UNKNOWN_ID; +- } +- state->chip_id = id; +- mxl_info("%s detected @ %d-%04x", name, +- i2c_adapter_id(state->i2c_props.adap), +- state->i2c_props.addr); +- return 0; +-fail: +- mxl_warn("unable to identify device @ %d-%04x", +- i2c_adapter_id(state->i2c_props.adap), +- state->i2c_props.addr); +- +- state->chip_id = MxL_UNKNOWN_ID; +- return ret; +-} +- +-struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 addr, +- struct mxl5007t_config *cfg) +-{ +- struct mxl5007t_state *state = NULL; +- int instance, ret; +- +- mutex_lock(&mxl5007t_list_mutex); +- instance = hybrid_tuner_request_state(struct mxl5007t_state, state, +- hybrid_tuner_instance_list, +- i2c, addr, "mxl5007t"); +- switch (instance) { +- case 0: +- goto fail; +- case 1: +- /* new tuner instance */ +- state->config = cfg; +- +- mutex_init(&state->lock); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = mxl5007t_get_chip_id(state); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- /* check return value of mxl5007t_get_chip_id */ +- if (mxl_fail(ret)) +- goto fail; +- break; +- default: +- /* existing tuner instance */ +- break; +- } +- fe->tuner_priv = state; +- mutex_unlock(&mxl5007t_list_mutex); +- +- memcpy(&fe->ops.tuner_ops, &mxl5007t_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- return fe; +-fail: +- mutex_unlock(&mxl5007t_list_mutex); +- +- mxl5007t_release(fe); +- return NULL; +-} +-EXPORT_SYMBOL_GPL(mxl5007t_attach); +-MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.2"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/mxl5007t.h b/drivers/media/common/tuners/mxl5007t.h +deleted file mode 100644 +index aa3eea0..0000000 +--- a/drivers/media/common/tuners/mxl5007t.h ++++ /dev/null +@@ -1,104 +0,0 @@ +-/* +- * mxl5007t.h - driver for the MaxLinear MxL5007T silicon tuner +- * +- * Copyright (C) 2008 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MXL5007T_H__ +-#define __MXL5007T_H__ +- +-#include "dvb_frontend.h" +- +-/* ------------------------------------------------------------------------- */ +- +-enum mxl5007t_if_freq { +- MxL_IF_4_MHZ, /* 4000000 */ +- MxL_IF_4_5_MHZ, /* 4500000 */ +- MxL_IF_4_57_MHZ, /* 4570000 */ +- MxL_IF_5_MHZ, /* 5000000 */ +- MxL_IF_5_38_MHZ, /* 5380000 */ +- MxL_IF_6_MHZ, /* 6000000 */ +- MxL_IF_6_28_MHZ, /* 6280000 */ +- MxL_IF_9_1915_MHZ, /* 9191500 */ +- MxL_IF_35_25_MHZ, /* 35250000 */ +- MxL_IF_36_15_MHZ, /* 36150000 */ +- MxL_IF_44_MHZ, /* 44000000 */ +-}; +- +-enum mxl5007t_xtal_freq { +- MxL_XTAL_16_MHZ, /* 16000000 */ +- MxL_XTAL_20_MHZ, /* 20000000 */ +- MxL_XTAL_20_25_MHZ, /* 20250000 */ +- MxL_XTAL_20_48_MHZ, /* 20480000 */ +- MxL_XTAL_24_MHZ, /* 24000000 */ +- MxL_XTAL_25_MHZ, /* 25000000 */ +- MxL_XTAL_25_14_MHZ, /* 25140000 */ +- MxL_XTAL_27_MHZ, /* 27000000 */ +- MxL_XTAL_28_8_MHZ, /* 28800000 */ +- MxL_XTAL_32_MHZ, /* 32000000 */ +- MxL_XTAL_40_MHZ, /* 40000000 */ +- MxL_XTAL_44_MHZ, /* 44000000 */ +- MxL_XTAL_48_MHZ, /* 48000000 */ +- MxL_XTAL_49_3811_MHZ, /* 49381100 */ +-}; +- +-enum mxl5007t_clkout_amp { +- MxL_CLKOUT_AMP_0_94V = 0, +- MxL_CLKOUT_AMP_0_53V = 1, +- MxL_CLKOUT_AMP_0_37V = 2, +- MxL_CLKOUT_AMP_0_28V = 3, +- MxL_CLKOUT_AMP_0_23V = 4, +- MxL_CLKOUT_AMP_0_20V = 5, +- MxL_CLKOUT_AMP_0_17V = 6, +- MxL_CLKOUT_AMP_0_15V = 7, +-}; +- +-struct mxl5007t_config { +- s32 if_diff_out_level; +- enum mxl5007t_clkout_amp clk_out_amp; +- enum mxl5007t_xtal_freq xtal_freq_hz; +- enum mxl5007t_if_freq if_freq_hz; +- unsigned int invert_if:1; +- unsigned int loop_thru_enable:1; +- unsigned int clk_out_enable:1; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_MXL5007T) || (defined(CONFIG_MEDIA_TUNER_MXL5007T_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 addr, +- struct mxl5007t_config *cfg); +-#else +-static inline struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- u8 addr, +- struct mxl5007t_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __MXL5007T_H__ */ +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +- +diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c +deleted file mode 100644 +index 2d79b1f..0000000 +--- a/drivers/media/common/tuners/qt1010.c ++++ /dev/null +@@ -1,480 +0,0 @@ +-/* +- * Driver for Quantek QT1010 silicon tuner +- * +- * Copyright (C) 2006 Antti Palosaari +- * Aapo Tahkola +- * +- * 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 "qt1010.h" +-#include "qt1010_priv.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "QT1010: " args); \ +- } while (0) +- +-/* read single register */ +-static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) +-{ +- struct i2c_msg msg[2] = { +- { .addr = priv->cfg->i2c_address, +- .flags = 0, .buf = ®, .len = 1 }, +- { .addr = priv->cfg->i2c_address, +- .flags = I2C_M_RD, .buf = val, .len = 1 }, +- }; +- +- if (i2c_transfer(priv->i2c, msg, 2) != 2) { +- printk(KERN_WARNING "qt1010 I2C read failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* write single register */ +-static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) +-{ +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { .addr = priv->cfg->i2c_address, +- .flags = 0, .buf = buf, .len = 2 }; +- +- if (i2c_transfer(priv->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "qt1010 I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* dump all registers */ +-static void qt1010_dump_regs(struct qt1010_priv *priv) +-{ +- u8 reg, val; +- +- for (reg = 0; ; reg++) { +- if (reg % 16 == 0) { +- if (reg) +- printk(KERN_CONT "\n"); +- printk(KERN_DEBUG "%02x:", reg); +- } +- if (qt1010_readreg(priv, reg, &val) == 0) +- printk(KERN_CONT " %02x", val); +- else +- printk(KERN_CONT " --"); +- if (reg == 0x2f) +- break; +- } +- printk(KERN_CONT "\n"); +-} +- +-static int qt1010_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct qt1010_priv *priv; +- int err; +- u32 freq, div, mod1, mod2; +- u8 i, tmpval, reg05; +- qt1010_i2c_oper_t rd[48] = { +- { QT1010_WR, 0x01, 0x80 }, +- { QT1010_WR, 0x02, 0x3f }, +- { QT1010_WR, 0x05, 0xff }, /* 02 c write */ +- { QT1010_WR, 0x06, 0x44 }, +- { QT1010_WR, 0x07, 0xff }, /* 04 c write */ +- { QT1010_WR, 0x08, 0x08 }, +- { QT1010_WR, 0x09, 0xff }, /* 06 c write */ +- { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ +- { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ +- { QT1010_WR, 0x0c, 0xe1 }, +- { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ +- { QT1010_WR, 0x1b, 0x00 }, +- { QT1010_WR, 0x1c, 0x89 }, +- { QT1010_WR, 0x11, 0xff }, /* 13 c write */ +- { QT1010_WR, 0x12, 0xff }, /* 14 c write */ +- { QT1010_WR, 0x22, 0xff }, /* 15 c write */ +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x1e, 0xd0 }, +- { QT1010_RD, 0x22, 0xff }, /* 16 c read */ +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_RD, 0x05, 0xff }, /* 20 c read */ +- { QT1010_RD, 0x22, 0xff }, /* 21 c read */ +- { QT1010_WR, 0x23, 0xd0 }, +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x1e, 0xe0 }, +- { QT1010_RD, 0x23, 0xff }, /* 25 c read */ +- { QT1010_RD, 0x23, 0xff }, /* 26 c read */ +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x24, 0xd0 }, +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x1e, 0xf0 }, +- { QT1010_RD, 0x24, 0xff }, /* 31 c read */ +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x14, 0x7f }, +- { QT1010_WR, 0x15, 0x7f }, +- { QT1010_WR, 0x05, 0xff }, /* 35 c write */ +- { QT1010_WR, 0x06, 0x00 }, +- { QT1010_WR, 0x15, 0x1f }, +- { QT1010_WR, 0x16, 0xff }, +- { QT1010_WR, 0x18, 0xff }, +- { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ +- { QT1010_WR, 0x20, 0xff }, /* 41 c write */ +- { QT1010_WR, 0x21, 0x53 }, +- { QT1010_WR, 0x25, 0xff }, /* 43 c write */ +- { QT1010_WR, 0x26, 0x15 }, +- { QT1010_WR, 0x00, 0xff }, /* 45 c write */ +- { QT1010_WR, 0x02, 0x00 }, +- { QT1010_WR, 0x01, 0x00 } +- }; +- +-#define FREQ1 32000000 /* 32 MHz */ +-#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ +- +- priv = fe->tuner_priv; +- freq = c->frequency; +- div = (freq + QT1010_OFFSET) / QT1010_STEP; +- freq = (div * QT1010_STEP) - QT1010_OFFSET; +- mod1 = (freq + QT1010_OFFSET) % FREQ1; +- mod2 = (freq + QT1010_OFFSET) % FREQ2; +- priv->frequency = freq; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- /* reg 05 base value */ +- if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ +- else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ +- else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ +- else reg05 = 0x74; +- +- /* 0x5 */ +- rd[2].val = reg05; +- +- /* 07 - set frequency: 32 MHz scale */ +- rd[4].val = (freq + QT1010_OFFSET) / FREQ1; +- +- /* 09 - changes every 8/24 MHz */ +- if (mod1 < 8000000) rd[6].val = 0x1d; +- else rd[6].val = 0x1c; +- +- /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ +- if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ +- else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ +- else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ +- else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ +- else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ +- else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ +- else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ +- else rd[7].val = 0x0a; /* +28 MHz */ +- +- /* 0b - changes every 2/2 MHz */ +- if (mod2 < 2000000) rd[8].val = 0x45; +- else rd[8].val = 0x44; +- +- /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ +- tmpval = 0x78; /* byte, overflows intentionally */ +- rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); +- +- /* 11 */ +- rd[13].val = 0xfd; /* TODO: correct value calculation */ +- +- /* 12 */ +- rd[14].val = 0x91; /* TODO: correct value calculation */ +- +- /* 22 */ +- if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ +- else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ +- else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ +- else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ +- else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ +- else rd[15].val = 0xd0; +- +- /* 05 */ +- rd[35].val = (reg05 & 0xf0); +- +- /* 1f */ +- if (mod1 < 8000000) tmpval = 0x00; +- else if (mod1 < 12000000) tmpval = 0x01; +- else if (mod1 < 16000000) tmpval = 0x02; +- else if (mod1 < 24000000) tmpval = 0x03; +- else if (mod1 < 28000000) tmpval = 0x04; +- else tmpval = 0x05; +- rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); +- +- /* 20 */ +- if (mod1 < 8000000) tmpval = 0x00; +- else if (mod1 < 12000000) tmpval = 0x01; +- else if (mod1 < 20000000) tmpval = 0x02; +- else if (mod1 < 24000000) tmpval = 0x03; +- else if (mod1 < 28000000) tmpval = 0x04; +- else tmpval = 0x05; +- rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); +- +- /* 25 */ +- rd[43].val = priv->reg25_init_val; +- +- /* 00 */ +- rd[45].val = 0x92; /* TODO: correct value calculation */ +- +- dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ +- "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ +- "20:%02x 25:%02x 00:%02x", \ +- freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \ +- rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \ +- rd[40].val, rd[41].val, rd[43].val, rd[45].val); +- +- for (i = 0; i < ARRAY_SIZE(rd); i++) { +- if (rd[i].oper == QT1010_WR) { +- err = qt1010_writereg(priv, rd[i].reg, rd[i].val); +- } else { /* read is required to proper locking */ +- err = qt1010_readreg(priv, rd[i].reg, &tmpval); +- } +- if (err) return err; +- } +- +- if (debug) +- qt1010_dump_regs(priv); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return 0; +-} +- +-static int qt1010_init_meas1(struct qt1010_priv *priv, +- u8 oper, u8 reg, u8 reg_init_val, u8 *retval) +-{ +- u8 i, val1, val2; +- int err; +- +- qt1010_i2c_oper_t i2c_data[] = { +- { QT1010_WR, reg, reg_init_val }, +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x1e, oper }, +- { QT1010_RD, reg, 0xff } +- }; +- +- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { +- if (i2c_data[i].oper == QT1010_WR) { +- err = qt1010_writereg(priv, i2c_data[i].reg, +- i2c_data[i].val); +- } else { +- err = qt1010_readreg(priv, i2c_data[i].reg, &val2); +- } +- if (err) return err; +- } +- +- do { +- val1 = val2; +- err = qt1010_readreg(priv, reg, &val2); +- if (err) return err; +- dprintk("compare reg:%02x %02x %02x", reg, val1, val2); +- } while (val1 != val2); +- *retval = val1; +- +- return qt1010_writereg(priv, 0x1e, 0x00); +-} +- +-static u8 qt1010_init_meas2(struct qt1010_priv *priv, +- u8 reg_init_val, u8 *retval) +-{ +- u8 i, val; +- int err; +- qt1010_i2c_oper_t i2c_data[] = { +- { QT1010_WR, 0x07, reg_init_val }, +- { QT1010_WR, 0x22, 0xd0 }, +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x1e, 0xd0 }, +- { QT1010_RD, 0x22, 0xff }, +- { QT1010_WR, 0x1e, 0x00 }, +- { QT1010_WR, 0x22, 0xff } +- }; +- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { +- if (i2c_data[i].oper == QT1010_WR) { +- err = qt1010_writereg(priv, i2c_data[i].reg, +- i2c_data[i].val); +- } else { +- err = qt1010_readreg(priv, i2c_data[i].reg, &val); +- } +- if (err) return err; +- } +- *retval = val; +- return 0; +-} +- +-static int qt1010_init(struct dvb_frontend *fe) +-{ +- struct qt1010_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int err = 0; +- u8 i, tmpval, *valptr = NULL; +- +- qt1010_i2c_oper_t i2c_data[] = { +- { QT1010_WR, 0x01, 0x80 }, +- { QT1010_WR, 0x0d, 0x84 }, +- { QT1010_WR, 0x0e, 0xb7 }, +- { QT1010_WR, 0x2a, 0x23 }, +- { QT1010_WR, 0x2c, 0xdc }, +- { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ +- { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ +- { QT1010_WR, 0x2b, 0x70 }, +- { QT1010_WR, 0x2a, 0x23 }, +- { QT1010_M1, 0x26, 0x08 }, +- { QT1010_M1, 0x82, 0xff }, +- { QT1010_WR, 0x05, 0x14 }, +- { QT1010_WR, 0x06, 0x44 }, +- { QT1010_WR, 0x07, 0x28 }, +- { QT1010_WR, 0x08, 0x0b }, +- { QT1010_WR, 0x11, 0xfd }, +- { QT1010_M1, 0x22, 0x0d }, +- { QT1010_M1, 0xd0, 0xff }, +- { QT1010_WR, 0x06, 0x40 }, +- { QT1010_WR, 0x16, 0xf0 }, +- { QT1010_WR, 0x02, 0x38 }, +- { QT1010_WR, 0x03, 0x18 }, +- { QT1010_WR, 0x20, 0xe0 }, +- { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ +- { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ +- { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ +- { QT1010_WR, 0x03, 0x19 }, +- { QT1010_WR, 0x02, 0x3f }, +- { QT1010_WR, 0x21, 0x53 }, +- { QT1010_RD, 0x21, 0xff }, +- { QT1010_WR, 0x11, 0xfd }, +- { QT1010_WR, 0x05, 0x34 }, +- { QT1010_WR, 0x06, 0x44 }, +- { QT1010_WR, 0x08, 0x08 } +- }; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { +- switch (i2c_data[i].oper) { +- case QT1010_WR: +- err = qt1010_writereg(priv, i2c_data[i].reg, +- i2c_data[i].val); +- break; +- case QT1010_RD: +- if (i2c_data[i].val == 0x20) +- valptr = &priv->reg20_init_val; +- else +- valptr = &tmpval; +- err = qt1010_readreg(priv, i2c_data[i].reg, valptr); +- break; +- case QT1010_M1: +- if (i2c_data[i].val == 0x25) +- valptr = &priv->reg25_init_val; +- else if (i2c_data[i].val == 0x1f) +- valptr = &priv->reg1f_init_val; +- else +- valptr = &tmpval; +- err = qt1010_init_meas1(priv, i2c_data[i+1].reg, +- i2c_data[i].reg, +- i2c_data[i].val, valptr); +- i++; +- break; +- } +- if (err) return err; +- } +- +- for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ +- if ((err = qt1010_init_meas2(priv, i, &tmpval))) +- return err; +- +- c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */ +- /* MSI Megasky 580 GL861 533000000 */ +- return qt1010_set_params(fe); +-} +- +-static int qt1010_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct qt1010_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- *frequency = 36125000; +- return 0; +-} +- +-static const struct dvb_tuner_ops qt1010_tuner_ops = { +- .info = { +- .name = "Quantek QT1010", +- .frequency_min = QT1010_MIN_FREQ, +- .frequency_max = QT1010_MAX_FREQ, +- .frequency_step = QT1010_STEP, +- }, +- +- .release = qt1010_release, +- .init = qt1010_init, +- /* TODO: implement sleep */ +- +- .set_params = qt1010_set_params, +- .get_frequency = qt1010_get_frequency, +- .get_if_frequency = qt1010_get_if_frequency, +-}; +- +-struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct qt1010_config *cfg) +-{ +- struct qt1010_priv *priv = NULL; +- u8 id; +- +- priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- +- /* Try to detect tuner chip. Probably this is not correct register. */ +- if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { +- kfree(priv); +- return NULL; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- printk(KERN_INFO "Quantek QT1010 successfully identified.\n"); +- memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- return fe; +-} +-EXPORT_SYMBOL(qt1010_attach); +- +-MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_AUTHOR("Aapo Tahkola "); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/qt1010.h b/drivers/media/common/tuners/qt1010.h +deleted file mode 100644 +index 807fb7b..0000000 +--- a/drivers/media/common/tuners/qt1010.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * Driver for Quantek QT1010 silicon tuner +- * +- * Copyright (C) 2006 Antti Palosaari +- * Aapo Tahkola +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef QT1010_H +-#define QT1010_H +- +-#include "dvb_frontend.h" +- +-struct qt1010_config { +- u8 i2c_address; +-}; +- +-/** +- * Attach a qt1010 tuner to the supplied frontend structure. +- * +- * @param fe frontend to attach to +- * @param i2c i2c adapter to use +- * @param cfg tuner hw based configuration +- * @return fe pointer on success, NULL on failure +- */ +-#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct qt1010_config *cfg); +-#else +-static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct qt1010_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_MEDIA_TUNER_QT1010 +- +-#endif +diff --git a/drivers/media/common/tuners/qt1010_priv.h b/drivers/media/common/tuners/qt1010_priv.h +deleted file mode 100644 +index 2c42d3f..0000000 +--- a/drivers/media/common/tuners/qt1010_priv.h ++++ /dev/null +@@ -1,104 +0,0 @@ +-/* +- * Driver for Quantek QT1010 silicon tuner +- * +- * Copyright (C) 2006 Antti Palosaari +- * Aapo Tahkola +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef QT1010_PRIV_H +-#define QT1010_PRIV_H +- +-/* +-reg def meaning +-=== === ======= +-00 00 ? +-01 a0 ? operation start/stop; start=80, stop=00 +-02 00 ? +-03 19 ? +-04 00 ? +-05 00 ? maybe band selection +-06 00 ? +-07 2b set frequency: 32 MHz scale, n*32 MHz +-08 0b ? +-09 10 ? changes every 8/24 MHz; values 1d/1c +-0a 08 set frequency: 4 MHz scale, n*4 MHz +-0b 41 ? changes every 2/2 MHz; values 45/45 +-0c e1 ? +-0d 94 ? +-0e b6 ? +-0f 2c ? +-10 10 ? +-11 f1 ? maybe device specified adjustment +-12 11 ? maybe device specified adjustment +-13 3f ? +-14 1f ? +-15 3f ? +-16 ff ? +-17 ff ? +-18 f7 ? +-19 80 ? +-1a d0 set frequency: 125 kHz scale, n*125 kHz +-1b 00 ? +-1c 89 ? +-1d 00 ? +-1e 00 ? looks like operation register; write cmd here, read result from 1f-26 +-1f 20 ? chip initialization +-20 e0 ? chip initialization +-21 20 ? +-22 d0 ? +-23 d0 ? +-24 d0 ? +-25 40 ? chip initialization +-26 08 ? +-27 29 ? +-28 55 ? +-29 39 ? +-2a 13 ? +-2b 01 ? +-2c ea ? +-2d 00 ? +-2e 00 ? not used? +-2f 00 ? not used? +-*/ +- +-#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers, +- hw could be more precise but we don't +- know how to use */ +-#define QT1010_MIN_FREQ 48000000 /* 48 MHz */ +-#define QT1010_MAX_FREQ 860000000 /* 860 MHz */ +-#define QT1010_OFFSET 1246000000 /* 1246 MHz */ +- +-#define QT1010_WR 0 +-#define QT1010_RD 1 +-#define QT1010_M1 3 +- +-typedef struct { +- u8 oper, reg, val; +-} qt1010_i2c_oper_t; +- +-struct qt1010_priv { +- struct qt1010_config *cfg; +- struct i2c_adapter *i2c; +- +- u8 reg1f_init_val; +- u8 reg20_init_val; +- u8 reg25_init_val; +- +- u32 frequency; +-}; +- +-#endif +diff --git a/drivers/media/common/tuners/tda18212.c b/drivers/media/common/tuners/tda18212.c +deleted file mode 100644 +index 602c2e3..0000000 +--- a/drivers/media/common/tuners/tda18212.c ++++ /dev/null +@@ -1,326 +0,0 @@ +-/* +- * NXP TDA18212HN silicon tuner driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include "tda18212.h" +- +-struct tda18212_priv { +- struct tda18212_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 if_frequency; +-}; +- +-#define dbg(fmt, arg...) \ +-do { \ +- if (debug) \ +- pr_info("%s: " fmt, __func__, ##arg); \ +-} while (0) +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-/* write multiple registers */ +-static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val, +- int len) +-{ +- int ret; +- u8 buf[len+1]; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->cfg->i2c_address, +- .flags = 0, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- buf[0] = reg; +- memcpy(&buf[1], val, len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- pr_warn("i2c wr failed ret:%d reg:%02x len:%d\n", +- ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* read multiple registers */ +-static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val, +- int len) +-{ +- int ret; +- u8 buf[len]; +- struct i2c_msg msg[2] = { +- { +- .addr = priv->cfg->i2c_address, +- .flags = 0, +- .len = 1, +- .buf = ®, +- }, { +- .addr = priv->cfg->i2c_address, +- .flags = I2C_M_RD, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- memcpy(val, buf, len); +- ret = 0; +- } else { +- pr_warn("i2c rd failed ret:%d reg:%02x len:%d\n", +- ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* write single register */ +-static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val) +-{ +- return tda18212_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +-static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val) +-{ +- return tda18212_rd_regs(priv, reg, val, 1); +-} +- +-#if 0 /* keep, useful when developing driver */ +-static void tda18212_dump_regs(struct tda18212_priv *priv) +-{ +- int i; +- u8 buf[256]; +- +- #define TDA18212_RD_LEN 32 +- for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN) +- tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN); +- +- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf, +- sizeof(buf), true); +- +- return; +-} +-#endif +- +-static int tda18212_set_params(struct dvb_frontend *fe) +-{ +- struct tda18212_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i; +- u32 if_khz; +- u8 buf[9]; +- #define DVBT_6 0 +- #define DVBT_7 1 +- #define DVBT_8 2 +- #define DVBT2_6 3 +- #define DVBT2_7 4 +- #define DVBT2_8 5 +- #define DVBC_6 6 +- #define DVBC_8 7 +- static const u8 bw_params[][3] = { +- /* reg: 0f 13 23 */ +- [DVBT_6] = { 0xb3, 0x20, 0x03 }, +- [DVBT_7] = { 0xb3, 0x31, 0x01 }, +- [DVBT_8] = { 0xb3, 0x22, 0x01 }, +- [DVBT2_6] = { 0xbc, 0x20, 0x03 }, +- [DVBT2_7] = { 0xbc, 0x72, 0x03 }, +- [DVBT2_8] = { 0xbc, 0x22, 0x01 }, +- [DVBC_6] = { 0x92, 0x50, 0x03 }, +- [DVBC_8] = { 0x92, 0x53, 0x03 }, +- }; +- +- dbg("delsys=%d RF=%d BW=%d\n", +- c->delivery_system, c->frequency, c->bandwidth_hz); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- switch (c->delivery_system) { +- case SYS_DVBT: +- switch (c->bandwidth_hz) { +- case 6000000: +- if_khz = priv->cfg->if_dvbt_6; +- i = DVBT_6; +- break; +- case 7000000: +- if_khz = priv->cfg->if_dvbt_7; +- i = DVBT_7; +- break; +- case 8000000: +- if_khz = priv->cfg->if_dvbt_8; +- i = DVBT_8; +- break; +- default: +- ret = -EINVAL; +- goto error; +- } +- break; +- case SYS_DVBT2: +- switch (c->bandwidth_hz) { +- case 6000000: +- if_khz = priv->cfg->if_dvbt2_6; +- i = DVBT2_6; +- break; +- case 7000000: +- if_khz = priv->cfg->if_dvbt2_7; +- i = DVBT2_7; +- break; +- case 8000000: +- if_khz = priv->cfg->if_dvbt2_8; +- i = DVBT2_8; +- break; +- default: +- ret = -EINVAL; +- goto error; +- } +- break; +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- if_khz = priv->cfg->if_dvbc; +- i = DVBC_8; +- break; +- default: +- ret = -EINVAL; +- goto error; +- } +- +- ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]); +- if (ret) +- goto error; +- +- ret = tda18212_wr_reg(priv, 0x06, 0x00); +- if (ret) +- goto error; +- +- ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]); +- if (ret) +- goto error; +- +- buf[0] = 0x02; +- buf[1] = bw_params[i][1]; +- buf[2] = 0x03; /* default value */ +- buf[3] = DIV_ROUND_CLOSEST(if_khz, 50); +- buf[4] = ((c->frequency / 1000) >> 16) & 0xff; +- buf[5] = ((c->frequency / 1000) >> 8) & 0xff; +- buf[6] = ((c->frequency / 1000) >> 0) & 0xff; +- buf[7] = 0xc1; +- buf[8] = 0x01; +- ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- /* actual IF rounded as it is on register */ +- priv->if_frequency = buf[3] * 50 * 1000; +- +-exit: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- return ret; +- +-error: +- dbg("failed:%d\n", ret); +- goto exit; +-} +- +-static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda18212_priv *priv = fe->tuner_priv; +- +- *frequency = priv->if_frequency; +- +- return 0; +-} +- +-static int tda18212_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops tda18212_tuner_ops = { +- .info = { +- .name = "NXP TDA18212", +- +- .frequency_min = 48000000, +- .frequency_max = 864000000, +- .frequency_step = 1000, +- }, +- +- .release = tda18212_release, +- +- .set_params = tda18212_set_params, +- .get_if_frequency = tda18212_get_if_frequency, +-}; +- +-struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18212_config *cfg) +-{ +- struct tda18212_priv *priv = NULL; +- int ret; +- u8 val; +- +- priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- fe->tuner_priv = priv; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- /* check if the tuner is there */ +- ret = tda18212_rd_reg(priv, 0x00, &val); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- dbg("ret:%d chip ID:%02x\n", ret, val); +- if (ret || val != 0xc7) { +- kfree(priv); +- return NULL; +- } +- +- pr_info("NXP TDA18212HN successfully identified\n"); +- +- memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- return fe; +-} +-EXPORT_SYMBOL(tda18212_attach); +- +-MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver"); +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tda18212.h b/drivers/media/common/tuners/tda18212.h +deleted file mode 100644 +index 9bd5da4..0000000 +--- a/drivers/media/common/tuners/tda18212.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* +- * NXP TDA18212HN silicon tuner driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#ifndef TDA18212_H +-#define TDA18212_H +- +-#include "dvb_frontend.h" +- +-struct tda18212_config { +- u8 i2c_address; +- +- u16 if_dvbt_6; +- u16 if_dvbt_7; +- u16 if_dvbt_8; +- u16 if_dvbt2_5; +- u16 if_dvbt2_6; +- u16 if_dvbt2_7; +- u16 if_dvbt2_8; +- u16 if_dvbc; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_TDA18212) || \ +- (defined(CONFIG_MEDIA_TUNER_TDA18212_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18212_config *cfg); +-#else +-static inline struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18212_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/common/tuners/tda18218.c b/drivers/media/common/tuners/tda18218.c +deleted file mode 100644 +index dfb3a83..0000000 +--- a/drivers/media/common/tuners/tda18218.c ++++ /dev/null +@@ -1,342 +0,0 @@ +-/* +- * NXP TDA18218HN silicon tuner driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "tda18218.h" +-#include "tda18218_priv.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-/* write multiple registers */ +-static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) +-{ +- int ret = 0; +- u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->cfg->i2c_address, +- .flags = 0, +- .buf = buf, +- } +- }; +- +- msg_len_max = priv->cfg->i2c_wr_max - 1; +- quotient = len / msg_len_max; +- remainder = len % msg_len_max; +- msg_len = msg_len_max; +- for (i = 0; (i <= quotient && remainder); i++) { +- if (i == quotient) /* set len of the last msg */ +- msg_len = remainder; +- +- msg[0].len = msg_len + 1; +- buf[0] = reg + i * msg_len_max; +- memcpy(&buf[1], &val[i * msg_len_max], msg_len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret != 1) +- break; +- } +- +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* read multiple registers */ +-static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len) +-{ +- int ret; +- u8 buf[reg+len]; /* we must start read always from reg 0x00 */ +- struct i2c_msg msg[2] = { +- { +- .addr = priv->cfg->i2c_address, +- .flags = 0, +- .len = 1, +- .buf = "\x00", +- }, { +- .addr = priv->cfg->i2c_address, +- .flags = I2C_M_RD, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- memcpy(val, &buf[reg], len); +- ret = 0; +- } else { +- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* write single register */ +-static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val) +-{ +- return tda18218_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +- +-static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val) +-{ +- return tda18218_rd_regs(priv, reg, val, 1); +-} +- +-static int tda18218_set_params(struct dvb_frontend *fe) +-{ +- struct tda18218_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 bw = c->bandwidth_hz; +- int ret; +- u8 buf[3], i, BP_Filter, LP_Fc; +- u32 LO_Frac; +- /* TODO: find out correct AGC algorithm */ +- u8 agc[][2] = { +- { R20_AGC11, 0x60 }, +- { R23_AGC21, 0x02 }, +- { R20_AGC11, 0xa0 }, +- { R23_AGC21, 0x09 }, +- { R20_AGC11, 0xe0 }, +- { R23_AGC21, 0x0c }, +- { R20_AGC11, 0x40 }, +- { R23_AGC21, 0x01 }, +- { R20_AGC11, 0x80 }, +- { R23_AGC21, 0x08 }, +- { R20_AGC11, 0xc0 }, +- { R23_AGC21, 0x0b }, +- { R24_AGC22, 0x1c }, +- { R24_AGC22, 0x0c }, +- }; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- /* low-pass filter cut-off frequency */ +- if (bw <= 6000000) { +- LP_Fc = 0; +- priv->if_frequency = 3000000; +- } else if (bw <= 7000000) { +- LP_Fc = 1; +- priv->if_frequency = 3500000; +- } else { +- LP_Fc = 2; +- priv->if_frequency = 4000000; +- } +- +- LO_Frac = c->frequency + priv->if_frequency; +- +- /* band-pass filter */ +- if (LO_Frac < 188000000) +- BP_Filter = 3; +- else if (LO_Frac < 253000000) +- BP_Filter = 4; +- else if (LO_Frac < 343000000) +- BP_Filter = 5; +- else +- BP_Filter = 6; +- +- buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */ +- buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */ +- buf[2] = priv->regs[R1C_AGC2B]; +- ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3); +- if (ret) +- goto error; +- +- buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */ +- buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */ +- buf[2] = (LO_Frac / 1000) << 4 | +- (priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */ +- ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3); +- if (ret) +- goto error; +- +- buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */ +- ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); +- if (ret) +- goto error; +- +- buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */ +- ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1); +- if (ret) +- goto error; +- +- /* trigger AGC */ +- for (i = 0; i < ARRAY_SIZE(agc); i++) { +- ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]); +- if (ret) +- goto error; +- } +- +-error: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- if (ret) +- dbg("%s: failed ret:%d", __func__, ret); +- +- return ret; +-} +- +-static int tda18218_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda18218_priv *priv = fe->tuner_priv; +- *frequency = priv->if_frequency; +- dbg("%s: if=%d", __func__, *frequency); +- return 0; +-} +- +-static int tda18218_sleep(struct dvb_frontend *fe) +-{ +- struct tda18218_priv *priv = fe->tuner_priv; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- /* standby */ +- ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- if (ret) +- dbg("%s: failed ret:%d", __func__, ret); +- +- return ret; +-} +- +-static int tda18218_init(struct dvb_frontend *fe) +-{ +- struct tda18218_priv *priv = fe->tuner_priv; +- int ret; +- +- /* TODO: calibrations */ +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- if (ret) +- dbg("%s: failed ret:%d", __func__, ret); +- +- return ret; +-} +- +-static int tda18218_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops tda18218_tuner_ops = { +- .info = { +- .name = "NXP TDA18218", +- +- .frequency_min = 174000000, +- .frequency_max = 864000000, +- .frequency_step = 1000, +- }, +- +- .release = tda18218_release, +- .init = tda18218_init, +- .sleep = tda18218_sleep, +- +- .set_params = tda18218_set_params, +- +- .get_if_frequency = tda18218_get_if_frequency, +-}; +- +-struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18218_config *cfg) +-{ +- struct tda18218_priv *priv = NULL; +- u8 val; +- int ret; +- /* chip default registers values */ +- static u8 def_regs[] = { +- 0xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40, +- 0x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00, +- 0x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01, +- 0x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9, +- 0xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00, +- 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6 +- }; +- +- priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->cfg = cfg; +- priv->i2c = i2c; +- fe->tuner_priv = priv; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ +- +- /* check if the tuner is there */ +- ret = tda18218_rd_reg(priv, R00_ID, &val); +- dbg("%s: ret:%d chip ID:%02x", __func__, ret, val); +- if (ret || val != def_regs[R00_ID]) { +- kfree(priv); +- return NULL; +- } +- +- info("NXP TDA18218HN successfully identified."); +- +- memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- memcpy(priv->regs, def_regs, sizeof(def_regs)); +- +- /* loop-through enabled chip default register values */ +- if (priv->cfg->loop_through) { +- priv->regs[R17_PD1] = 0xb0; +- priv->regs[R18_PD2] = 0x59; +- } +- +- /* standby */ +- ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0)); +- if (ret) +- dbg("%s: failed ret:%d", __func__, ret); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +- +- return fe; +-} +-EXPORT_SYMBOL(tda18218_attach); +- +-MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver"); +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tda18218.h b/drivers/media/common/tuners/tda18218.h +deleted file mode 100644 +index b4180d1..0000000 +--- a/drivers/media/common/tuners/tda18218.h ++++ /dev/null +@@ -1,45 +0,0 @@ +-/* +- * NXP TDA18218HN silicon tuner driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef TDA18218_H +-#define TDA18218_H +- +-#include "dvb_frontend.h" +- +-struct tda18218_config { +- u8 i2c_address; +- u8 i2c_wr_max; +- u8 loop_through:1; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_TDA18218) || \ +- (defined(CONFIG_MEDIA_TUNER_TDA18218_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18218_config *cfg); +-#else +-static inline struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, struct tda18218_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/common/tuners/tda18218_priv.h b/drivers/media/common/tuners/tda18218_priv.h +deleted file mode 100644 +index dc52b72..0000000 +--- a/drivers/media/common/tuners/tda18218_priv.h ++++ /dev/null +@@ -1,108 +0,0 @@ +-/* +- * NXP TDA18218HN silicon tuner driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef TDA18218_PRIV_H +-#define TDA18218_PRIV_H +- +-#define LOG_PREFIX "tda18218" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (debug) \ +- printk(KERN_DEBUG LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-#define R00_ID 0x00 /* ID byte */ +-#define R01_R1 0x01 /* Read byte 1 */ +-#define R02_R2 0x02 /* Read byte 2 */ +-#define R03_R3 0x03 /* Read byte 3 */ +-#define R04_R4 0x04 /* Read byte 4 */ +-#define R05_R5 0x05 /* Read byte 5 */ +-#define R06_R6 0x06 /* Read byte 6 */ +-#define R07_MD1 0x07 /* Main divider byte 1 */ +-#define R08_PSM1 0x08 /* PSM byte 1 */ +-#define R09_MD2 0x09 /* Main divider byte 2 */ +-#define R0A_MD3 0x0a /* Main divider byte 1 */ +-#define R0B_MD4 0x0b /* Main divider byte 4 */ +-#define R0C_MD5 0x0c /* Main divider byte 5 */ +-#define R0D_MD6 0x0d /* Main divider byte 6 */ +-#define R0E_MD7 0x0e /* Main divider byte 7 */ +-#define R0F_MD8 0x0f /* Main divider byte 8 */ +-#define R10_CD1 0x10 /* Call divider byte 1 */ +-#define R11_CD2 0x11 /* Call divider byte 2 */ +-#define R12_CD3 0x12 /* Call divider byte 3 */ +-#define R13_CD4 0x13 /* Call divider byte 4 */ +-#define R14_CD5 0x14 /* Call divider byte 5 */ +-#define R15_CD6 0x15 /* Call divider byte 6 */ +-#define R16_CD7 0x16 /* Call divider byte 7 */ +-#define R17_PD1 0x17 /* Power-down byte 1 */ +-#define R18_PD2 0x18 /* Power-down byte 2 */ +-#define R19_XTOUT 0x19 /* XTOUT byte */ +-#define R1A_IF1 0x1a /* IF byte 1 */ +-#define R1B_IF2 0x1b /* IF byte 2 */ +-#define R1C_AGC2B 0x1c /* AGC2b byte */ +-#define R1D_PSM2 0x1d /* PSM byte 2 */ +-#define R1E_PSM3 0x1e /* PSM byte 3 */ +-#define R1F_PSM4 0x1f /* PSM byte 4 */ +-#define R20_AGC11 0x20 /* AGC1 byte 1 */ +-#define R21_AGC12 0x21 /* AGC1 byte 2 */ +-#define R22_AGC13 0x22 /* AGC1 byte 3 */ +-#define R23_AGC21 0x23 /* AGC2 byte 1 */ +-#define R24_AGC22 0x24 /* AGC2 byte 2 */ +-#define R25_AAGC 0x25 /* Analog AGC byte */ +-#define R26_RC 0x26 /* RC byte */ +-#define R27_RSSI 0x27 /* RSSI byte */ +-#define R28_IRCAL1 0x28 /* IR CAL byte 1 */ +-#define R29_IRCAL2 0x29 /* IR CAL byte 2 */ +-#define R2A_IRCAL3 0x2a /* IR CAL byte 3 */ +-#define R2B_IRCAL4 0x2b /* IR CAL byte 4 */ +-#define R2C_RFCAL1 0x2c /* RF CAL byte 1 */ +-#define R2D_RFCAL2 0x2d /* RF CAL byte 2 */ +-#define R2E_RFCAL3 0x2e /* RF CAL byte 3 */ +-#define R2F_RFCAL4 0x2f /* RF CAL byte 4 */ +-#define R30_RFCAL5 0x30 /* RF CAL byte 5 */ +-#define R31_RFCAL6 0x31 /* RF CAL byte 6 */ +-#define R32_RFCAL7 0x32 /* RF CAL byte 7 */ +-#define R33_RFCAL8 0x33 /* RF CAL byte 8 */ +-#define R34_RFCAL9 0x34 /* RF CAL byte 9 */ +-#define R35_RFCAL10 0x35 /* RF CAL byte 10 */ +-#define R36_RFCALRAM1 0x36 /* RF CAL RAM byte 1 */ +-#define R37_RFCALRAM2 0x37 /* RF CAL RAM byte 2 */ +-#define R38_MARGIN 0x38 /* Margin byte */ +-#define R39_FMAX1 0x39 /* Fmax byte 1 */ +-#define R3A_FMAX2 0x3a /* Fmax byte 2 */ +- +-#define TDA18218_NUM_REGS 59 +- +-struct tda18218_priv { +- struct tda18218_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 if_frequency; +- +- u8 regs[TDA18218_NUM_REGS]; +-}; +- +-#endif +diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c +deleted file mode 100644 +index 39c6457..0000000 +--- a/drivers/media/common/tuners/tda18271-common.c ++++ /dev/null +@@ -1,703 +0,0 @@ +-/* +- tda18271-common.c - driver for the Philips / NXP TDA18271 silicon tuner +- +- Copyright (C) 2007, 2008 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include "tda18271-priv.h" +- +-static int tda18271_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- enum tda18271_i2c_gate gate; +- int ret = 0; +- +- switch (priv->gate) { +- case TDA18271_GATE_DIGITAL: +- case TDA18271_GATE_ANALOG: +- gate = priv->gate; +- break; +- case TDA18271_GATE_AUTO: +- default: +- switch (priv->mode) { +- case TDA18271_DIGITAL: +- gate = TDA18271_GATE_DIGITAL; +- break; +- case TDA18271_ANALOG: +- default: +- gate = TDA18271_GATE_ANALOG; +- break; +- } +- } +- +- switch (gate) { +- case TDA18271_GATE_ANALOG: +- if (fe->ops.analog_ops.i2c_gate_ctrl) +- ret = fe->ops.analog_ops.i2c_gate_ctrl(fe, enable); +- break; +- case TDA18271_GATE_DIGITAL: +- if (fe->ops.i2c_gate_ctrl) +- ret = fe->ops.i2c_gate_ctrl(fe, enable); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- +- return ret; +-}; +- +-/*---------------------------------------------------------------------*/ +- +-static void tda18271_dump_regs(struct dvb_frontend *fe, int extended) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- +- tda_reg("=== TDA18271 REG DUMP ===\n"); +- tda_reg("ID_BYTE = 0x%02x\n", 0xff & regs[R_ID]); +- tda_reg("THERMO_BYTE = 0x%02x\n", 0xff & regs[R_TM]); +- tda_reg("POWER_LEVEL_BYTE = 0x%02x\n", 0xff & regs[R_PL]); +- tda_reg("EASY_PROG_BYTE_1 = 0x%02x\n", 0xff & regs[R_EP1]); +- tda_reg("EASY_PROG_BYTE_2 = 0x%02x\n", 0xff & regs[R_EP2]); +- tda_reg("EASY_PROG_BYTE_3 = 0x%02x\n", 0xff & regs[R_EP3]); +- tda_reg("EASY_PROG_BYTE_4 = 0x%02x\n", 0xff & regs[R_EP4]); +- tda_reg("EASY_PROG_BYTE_5 = 0x%02x\n", 0xff & regs[R_EP5]); +- tda_reg("CAL_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_CPD]); +- tda_reg("CAL_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_CD1]); +- tda_reg("CAL_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_CD2]); +- tda_reg("CAL_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_CD3]); +- tda_reg("MAIN_POST_DIV_BYTE = 0x%02x\n", 0xff & regs[R_MPD]); +- tda_reg("MAIN_DIV_BYTE_1 = 0x%02x\n", 0xff & regs[R_MD1]); +- tda_reg("MAIN_DIV_BYTE_2 = 0x%02x\n", 0xff & regs[R_MD2]); +- tda_reg("MAIN_DIV_BYTE_3 = 0x%02x\n", 0xff & regs[R_MD3]); +- +- /* only dump extended regs if DBG_ADV is set */ +- if (!(tda18271_debug & DBG_ADV)) +- return; +- +- /* W indicates write-only registers. +- * Register dump for write-only registers shows last value written. */ +- +- tda_reg("EXTENDED_BYTE_1 = 0x%02x\n", 0xff & regs[R_EB1]); +- tda_reg("EXTENDED_BYTE_2 = 0x%02x\n", 0xff & regs[R_EB2]); +- tda_reg("EXTENDED_BYTE_3 = 0x%02x\n", 0xff & regs[R_EB3]); +- tda_reg("EXTENDED_BYTE_4 = 0x%02x\n", 0xff & regs[R_EB4]); +- tda_reg("EXTENDED_BYTE_5 = 0x%02x\n", 0xff & regs[R_EB5]); +- tda_reg("EXTENDED_BYTE_6 = 0x%02x\n", 0xff & regs[R_EB6]); +- tda_reg("EXTENDED_BYTE_7 = 0x%02x\n", 0xff & regs[R_EB7]); +- tda_reg("EXTENDED_BYTE_8 = 0x%02x\n", 0xff & regs[R_EB8]); +- tda_reg("EXTENDED_BYTE_9 W = 0x%02x\n", 0xff & regs[R_EB9]); +- tda_reg("EXTENDED_BYTE_10 = 0x%02x\n", 0xff & regs[R_EB10]); +- tda_reg("EXTENDED_BYTE_11 = 0x%02x\n", 0xff & regs[R_EB11]); +- tda_reg("EXTENDED_BYTE_12 = 0x%02x\n", 0xff & regs[R_EB12]); +- tda_reg("EXTENDED_BYTE_13 = 0x%02x\n", 0xff & regs[R_EB13]); +- tda_reg("EXTENDED_BYTE_14 = 0x%02x\n", 0xff & regs[R_EB14]); +- tda_reg("EXTENDED_BYTE_15 = 0x%02x\n", 0xff & regs[R_EB15]); +- tda_reg("EXTENDED_BYTE_16 W = 0x%02x\n", 0xff & regs[R_EB16]); +- tda_reg("EXTENDED_BYTE_17 W = 0x%02x\n", 0xff & regs[R_EB17]); +- tda_reg("EXTENDED_BYTE_18 = 0x%02x\n", 0xff & regs[R_EB18]); +- tda_reg("EXTENDED_BYTE_19 W = 0x%02x\n", 0xff & regs[R_EB19]); +- tda_reg("EXTENDED_BYTE_20 W = 0x%02x\n", 0xff & regs[R_EB20]); +- tda_reg("EXTENDED_BYTE_21 = 0x%02x\n", 0xff & regs[R_EB21]); +- tda_reg("EXTENDED_BYTE_22 = 0x%02x\n", 0xff & regs[R_EB22]); +- tda_reg("EXTENDED_BYTE_23 = 0x%02x\n", 0xff & regs[R_EB23]); +-} +- +-int tda18271_read_regs(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- unsigned char buf = 0x00; +- int ret; +- struct i2c_msg msg[] = { +- { .addr = priv->i2c_props.addr, .flags = 0, +- .buf = &buf, .len = 1 }, +- { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, +- .buf = regs, .len = 16 } +- }; +- +- tda18271_i2c_gate_ctrl(fe, 1); +- +- /* read all registers */ +- ret = i2c_transfer(priv->i2c_props.adap, msg, 2); +- +- tda18271_i2c_gate_ctrl(fe, 0); +- +- if (ret != 2) +- tda_err("ERROR: i2c_transfer returned: %d\n", ret); +- +- if (tda18271_debug & DBG_REG) +- tda18271_dump_regs(fe, 0); +- +- return (ret == 2 ? 0 : ret); +-} +- +-int tda18271_read_extended(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- unsigned char regdump[TDA18271_NUM_REGS]; +- unsigned char buf = 0x00; +- int ret, i; +- struct i2c_msg msg[] = { +- { .addr = priv->i2c_props.addr, .flags = 0, +- .buf = &buf, .len = 1 }, +- { .addr = priv->i2c_props.addr, .flags = I2C_M_RD, +- .buf = regdump, .len = TDA18271_NUM_REGS } +- }; +- +- tda18271_i2c_gate_ctrl(fe, 1); +- +- /* read all registers */ +- ret = i2c_transfer(priv->i2c_props.adap, msg, 2); +- +- tda18271_i2c_gate_ctrl(fe, 0); +- +- if (ret != 2) +- tda_err("ERROR: i2c_transfer returned: %d\n", ret); +- +- for (i = 0; i < TDA18271_NUM_REGS; i++) { +- /* don't update write-only registers */ +- if ((i != R_EB9) && +- (i != R_EB16) && +- (i != R_EB17) && +- (i != R_EB19) && +- (i != R_EB20)) +- regs[i] = regdump[i]; +- } +- +- if (tda18271_debug & DBG_REG) +- tda18271_dump_regs(fe, 1); +- +- return (ret == 2 ? 0 : ret); +-} +- +-int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- unsigned char buf[TDA18271_NUM_REGS + 1]; +- struct i2c_msg msg = { .addr = priv->i2c_props.addr, .flags = 0, +- .buf = buf }; +- int i, ret = 1, max; +- +- BUG_ON((len == 0) || (idx + len > sizeof(buf))); +- +- +- switch (priv->small_i2c) { +- case TDA18271_03_BYTE_CHUNK_INIT: +- max = 3; +- break; +- case TDA18271_08_BYTE_CHUNK_INIT: +- max = 8; +- break; +- case TDA18271_16_BYTE_CHUNK_INIT: +- max = 16; +- break; +- case TDA18271_39_BYTE_CHUNK_INIT: +- default: +- max = 39; +- } +- +- tda18271_i2c_gate_ctrl(fe, 1); +- while (len) { +- if (max > len) +- max = len; +- +- buf[0] = idx; +- for (i = 1; i <= max; i++) +- buf[i] = regs[idx - 1 + i]; +- +- msg.len = max + 1; +- +- /* write registers */ +- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); +- if (ret != 1) +- break; +- +- idx += max; +- len -= max; +- } +- tda18271_i2c_gate_ctrl(fe, 0); +- +- if (ret != 1) +- tda_err("ERROR: idx = 0x%x, len = %d, " +- "i2c_transfer returned: %d\n", idx, max, ret); +- +- return (ret == 1 ? 0 : ret); +-} +- +-/*---------------------------------------------------------------------*/ +- +-int tda18271_charge_pump_source(struct dvb_frontend *fe, +- enum tda18271_pll pll, int force) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- +- int r_cp = (pll == TDA18271_CAL_PLL) ? R_EB7 : R_EB4; +- +- regs[r_cp] &= ~0x20; +- regs[r_cp] |= ((force & 1) << 5); +- +- return tda18271_write_regs(fe, r_cp, 1); +-} +- +-int tda18271_init_regs(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- +- tda_dbg("initializing registers for device @ %d-%04x\n", +- i2c_adapter_id(priv->i2c_props.adap), +- priv->i2c_props.addr); +- +- /* initialize registers */ +- switch (priv->id) { +- case TDA18271HDC1: +- regs[R_ID] = 0x83; +- break; +- case TDA18271HDC2: +- regs[R_ID] = 0x84; +- break; +- }; +- +- regs[R_TM] = 0x08; +- regs[R_PL] = 0x80; +- regs[R_EP1] = 0xc6; +- regs[R_EP2] = 0xdf; +- regs[R_EP3] = 0x16; +- regs[R_EP4] = 0x60; +- regs[R_EP5] = 0x80; +- regs[R_CPD] = 0x80; +- regs[R_CD1] = 0x00; +- regs[R_CD2] = 0x00; +- regs[R_CD3] = 0x00; +- regs[R_MPD] = 0x00; +- regs[R_MD1] = 0x00; +- regs[R_MD2] = 0x00; +- regs[R_MD3] = 0x00; +- +- switch (priv->id) { +- case TDA18271HDC1: +- regs[R_EB1] = 0xff; +- break; +- case TDA18271HDC2: +- regs[R_EB1] = 0xfc; +- break; +- }; +- +- regs[R_EB2] = 0x01; +- regs[R_EB3] = 0x84; +- regs[R_EB4] = 0x41; +- regs[R_EB5] = 0x01; +- regs[R_EB6] = 0x84; +- regs[R_EB7] = 0x40; +- regs[R_EB8] = 0x07; +- regs[R_EB9] = 0x00; +- regs[R_EB10] = 0x00; +- regs[R_EB11] = 0x96; +- +- switch (priv->id) { +- case TDA18271HDC1: +- regs[R_EB12] = 0x0f; +- break; +- case TDA18271HDC2: +- regs[R_EB12] = 0x33; +- break; +- }; +- +- regs[R_EB13] = 0xc1; +- regs[R_EB14] = 0x00; +- regs[R_EB15] = 0x8f; +- regs[R_EB16] = 0x00; +- regs[R_EB17] = 0x00; +- +- switch (priv->id) { +- case TDA18271HDC1: +- regs[R_EB18] = 0x00; +- break; +- case TDA18271HDC2: +- regs[R_EB18] = 0x8c; +- break; +- }; +- +- regs[R_EB19] = 0x00; +- regs[R_EB20] = 0x20; +- +- switch (priv->id) { +- case TDA18271HDC1: +- regs[R_EB21] = 0x33; +- break; +- case TDA18271HDC2: +- regs[R_EB21] = 0xb3; +- break; +- }; +- +- regs[R_EB22] = 0x48; +- regs[R_EB23] = 0xb0; +- +- tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); +- +- /* setup agc1 gain */ +- regs[R_EB17] = 0x00; +- tda18271_write_regs(fe, R_EB17, 1); +- regs[R_EB17] = 0x03; +- tda18271_write_regs(fe, R_EB17, 1); +- regs[R_EB17] = 0x43; +- tda18271_write_regs(fe, R_EB17, 1); +- regs[R_EB17] = 0x4c; +- tda18271_write_regs(fe, R_EB17, 1); +- +- /* setup agc2 gain */ +- if ((priv->id) == TDA18271HDC1) { +- regs[R_EB20] = 0xa0; +- tda18271_write_regs(fe, R_EB20, 1); +- regs[R_EB20] = 0xa7; +- tda18271_write_regs(fe, R_EB20, 1); +- regs[R_EB20] = 0xe7; +- tda18271_write_regs(fe, R_EB20, 1); +- regs[R_EB20] = 0xec; +- tda18271_write_regs(fe, R_EB20, 1); +- } +- +- /* image rejection calibration */ +- +- /* low-band */ +- regs[R_EP3] = 0x1f; +- regs[R_EP4] = 0x66; +- regs[R_EP5] = 0x81; +- regs[R_CPD] = 0xcc; +- regs[R_CD1] = 0x6c; +- regs[R_CD2] = 0x00; +- regs[R_CD3] = 0x00; +- regs[R_MPD] = 0xcd; +- regs[R_MD1] = 0x77; +- regs[R_MD2] = 0x08; +- regs[R_MD3] = 0x00; +- +- tda18271_write_regs(fe, R_EP3, 11); +- +- if ((priv->id) == TDA18271HDC2) { +- /* main pll cp source on */ +- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); +- msleep(1); +- +- /* main pll cp source off */ +- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); +- } +- +- msleep(5); /* pll locking */ +- +- /* launch detector */ +- tda18271_write_regs(fe, R_EP1, 1); +- msleep(5); /* wanted low measurement */ +- +- regs[R_EP5] = 0x85; +- regs[R_CPD] = 0xcb; +- regs[R_CD1] = 0x66; +- regs[R_CD2] = 0x70; +- +- tda18271_write_regs(fe, R_EP3, 7); +- msleep(5); /* pll locking */ +- +- /* launch optimization algorithm */ +- tda18271_write_regs(fe, R_EP2, 1); +- msleep(30); /* image low optimization completion */ +- +- /* mid-band */ +- regs[R_EP5] = 0x82; +- regs[R_CPD] = 0xa8; +- regs[R_CD2] = 0x00; +- regs[R_MPD] = 0xa9; +- regs[R_MD1] = 0x73; +- regs[R_MD2] = 0x1a; +- +- tda18271_write_regs(fe, R_EP3, 11); +- msleep(5); /* pll locking */ +- +- /* launch detector */ +- tda18271_write_regs(fe, R_EP1, 1); +- msleep(5); /* wanted mid measurement */ +- +- regs[R_EP5] = 0x86; +- regs[R_CPD] = 0xa8; +- regs[R_CD1] = 0x66; +- regs[R_CD2] = 0xa0; +- +- tda18271_write_regs(fe, R_EP3, 7); +- msleep(5); /* pll locking */ +- +- /* launch optimization algorithm */ +- tda18271_write_regs(fe, R_EP2, 1); +- msleep(30); /* image mid optimization completion */ +- +- /* high-band */ +- regs[R_EP5] = 0x83; +- regs[R_CPD] = 0x98; +- regs[R_CD1] = 0x65; +- regs[R_CD2] = 0x00; +- regs[R_MPD] = 0x99; +- regs[R_MD1] = 0x71; +- regs[R_MD2] = 0xcd; +- +- tda18271_write_regs(fe, R_EP3, 11); +- msleep(5); /* pll locking */ +- +- /* launch detector */ +- tda18271_write_regs(fe, R_EP1, 1); +- msleep(5); /* wanted high measurement */ +- +- regs[R_EP5] = 0x87; +- regs[R_CD1] = 0x65; +- regs[R_CD2] = 0x50; +- +- tda18271_write_regs(fe, R_EP3, 7); +- msleep(5); /* pll locking */ +- +- /* launch optimization algorithm */ +- tda18271_write_regs(fe, R_EP2, 1); +- msleep(30); /* image high optimization completion */ +- +- /* return to normal mode */ +- regs[R_EP4] = 0x64; +- tda18271_write_regs(fe, R_EP4, 1); +- +- /* synchronize */ +- tda18271_write_regs(fe, R_EP1, 1); +- +- return 0; +-} +- +-/*---------------------------------------------------------------------*/ +- +-/* +- * Standby modes, EP3 [7:5] +- * +- * | SM || SM_LT || SM_XT || mode description +- * |=====\\=======\\=======\\=================================== +- * | 0 || 0 || 0 || normal mode +- * |-----||-------||-------||----------------------------------- +- * | || || || standby mode w/ slave tuner output +- * | 1 || 0 || 0 || & loop thru & xtal oscillator on +- * |-----||-------||-------||----------------------------------- +- * | 1 || 1 || 0 || standby mode w/ xtal oscillator on +- * |-----||-------||-------||----------------------------------- +- * | 1 || 1 || 1 || power off +- * +- */ +- +-int tda18271_set_standby_mode(struct dvb_frontend *fe, +- int sm, int sm_lt, int sm_xt) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- +- if (tda18271_debug & DBG_ADV) +- tda_dbg("sm = %d, sm_lt = %d, sm_xt = %d\n", sm, sm_lt, sm_xt); +- +- regs[R_EP3] &= ~0xe0; /* clear sm, sm_lt, sm_xt */ +- regs[R_EP3] |= (sm ? (1 << 7) : 0) | +- (sm_lt ? (1 << 6) : 0) | +- (sm_xt ? (1 << 5) : 0); +- +- return tda18271_write_regs(fe, R_EP3, 1); +-} +- +-/*---------------------------------------------------------------------*/ +- +-int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq) +-{ +- /* sets main post divider & divider bytes, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 d, pd; +- u32 div; +- +- int ret = tda18271_lookup_pll_map(fe, MAIN_PLL, &freq, &pd, &d); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_MPD] = (0x7f & pd); +- +- div = ((d * (freq / 1000)) << 7) / 125; +- +- regs[R_MD1] = 0x7f & (div >> 16); +- regs[R_MD2] = 0xff & (div >> 8); +- regs[R_MD3] = 0xff & div; +-fail: +- return ret; +-} +- +-int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq) +-{ +- /* sets cal post divider & divider bytes, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 d, pd; +- u32 div; +- +- int ret = tda18271_lookup_pll_map(fe, CAL_PLL, &freq, &pd, &d); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_CPD] = pd; +- +- div = ((d * (freq / 1000)) << 7) / 125; +- +- regs[R_CD1] = 0x7f & (div >> 16); +- regs[R_CD2] = 0xff & (div >> 8); +- regs[R_CD3] = 0xff & div; +-fail: +- return ret; +-} +- +-/*---------------------------------------------------------------------*/ +- +-int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets bp filter bits, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, BP_FILTER, freq, &val); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EP1] &= ~0x07; /* clear bp filter bits */ +- regs[R_EP1] |= (0x07 & val); +-fail: +- return ret; +-} +- +-int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets K & M bits, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, RF_CAL_KMCO, freq, &val); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EB13] &= ~0x7c; /* clear k & m bits */ +- regs[R_EB13] |= (0x7c & val); +-fail: +- return ret; +-} +- +-int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets rf band bits, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, RF_BAND, freq, &val); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EP2] &= ~0xe0; /* clear rf band bits */ +- regs[R_EP2] |= (0xe0 & (val << 5)); +-fail: +- return ret; +-} +- +-int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets gain taper bits, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, GAIN_TAPER, freq, &val); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EP2] &= ~0x1f; /* clear gain taper bits */ +- regs[R_EP2] |= (0x1f & val); +-fail: +- return ret; +-} +- +-int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets IR Meas bits, but does not write them */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, IR_MEASURE, freq, &val); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EP5] &= ~0x07; +- regs[R_EP5] |= (0x07 & val); +-fail: +- return ret; +-} +- +-int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq) +-{ +- /* sets rf cal byte (RFC_Cprog), but does not write it */ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u8 val; +- +- int ret = tda18271_lookup_map(fe, RF_CAL, freq, &val); +- /* The TDA18271HD/C1 rf_cal map lookup is expected to go out of range +- * for frequencies above 61.1 MHz. In these cases, the internal RF +- * tracking filters calibration mechanism is used. +- * +- * There is no need to warn the user about this. +- */ +- if (ret < 0) +- goto fail; +- +- regs[R_EB14] = val; +-fail: +- return ret; +-} +- +-int _tda_printk(struct tda18271_priv *state, const char *level, +- const char *func, const char *fmt, ...) +-{ +- struct va_format vaf; +- va_list args; +- int rtn; +- +- va_start(args, fmt); +- +- vaf.fmt = fmt; +- vaf.va = &args; +- +- if (state) +- rtn = printk("%s%s: [%d-%04x|%c] %pV", +- level, func, i2c_adapter_id(state->i2c_props.adap), +- state->i2c_props.addr, +- (state->role == TDA18271_MASTER) ? 'M' : 'S', +- &vaf); +- else +- rtn = printk("%s%s: %pV", level, func, &vaf); +- +- va_end(args); +- +- return rtn; +-} +diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c +deleted file mode 100644 +index 2e67f44..0000000 +--- a/drivers/media/common/tuners/tda18271-fe.c ++++ /dev/null +@@ -1,1345 +0,0 @@ +-/* +- tda18271-fe.c - driver for the Philips / NXP TDA18271 silicon tuner +- +- Copyright (C) 2007, 2008 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include "tda18271-priv.h" +- +-int tda18271_debug; +-module_param_named(debug, tda18271_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level " +- "(info=1, map=2, reg=4, adv=8, cal=16 (or-able))"); +- +-static int tda18271_cal_on_startup = -1; +-module_param_named(cal, tda18271_cal_on_startup, int, 0644); +-MODULE_PARM_DESC(cal, "perform RF tracking filter calibration on startup"); +- +-static DEFINE_MUTEX(tda18271_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-/*---------------------------------------------------------------------*/ +- +-static int tda18271_toggle_output(struct dvb_frontend *fe, int standby) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- +- int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0, +- priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0, +- priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0); +- +- if (tda_fail(ret)) +- goto fail; +- +- tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n", +- standby ? "standby" : "active", +- priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on", +- priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on"); +-fail: +- return ret; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static inline int charge_pump_source(struct dvb_frontend *fe, int force) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- return tda18271_charge_pump_source(fe, +- (priv->role == TDA18271_SLAVE) ? +- TDA18271_CAL_PLL : +- TDA18271_MAIN_PLL, force); +-} +- +-static inline void tda18271_set_if_notch(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- +- switch (priv->mode) { +- case TDA18271_ANALOG: +- regs[R_MPD] &= ~0x80; /* IF notch = 0 */ +- break; +- case TDA18271_DIGITAL: +- regs[R_MPD] |= 0x80; /* IF notch = 1 */ +- break; +- } +-} +- +-static int tda18271_channel_configuration(struct dvb_frontend *fe, +- struct tda18271_std_map_item *map, +- u32 freq, u32 bw) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- u32 N; +- +- /* update TV broadcast parameters */ +- +- /* set standard */ +- regs[R_EP3] &= ~0x1f; /* clear std bits */ +- regs[R_EP3] |= (map->agc_mode << 3) | map->std; +- +- if (priv->id == TDA18271HDC2) { +- /* set rfagc to high speed mode */ +- regs[R_EP3] &= ~0x04; +- } +- +- /* set cal mode to normal */ +- regs[R_EP4] &= ~0x03; +- +- /* update IF output level */ +- regs[R_EP4] &= ~0x1c; /* clear if level bits */ +- regs[R_EP4] |= (map->if_lvl << 2); +- +- /* update FM_RFn */ +- regs[R_EP4] &= ~0x80; +- regs[R_EP4] |= map->fm_rfn << 7; +- +- /* update rf top / if top */ +- regs[R_EB22] = 0x00; +- regs[R_EB22] |= map->rfagc_top; +- ret = tda18271_write_regs(fe, R_EB22, 1); +- if (tda_fail(ret)) +- goto fail; +- +- /* --------------------------------------------------------------- */ +- +- /* disable Power Level Indicator */ +- regs[R_EP1] |= 0x40; +- +- /* make sure thermometer is off */ +- regs[R_TM] &= ~0x10; +- +- /* frequency dependent parameters */ +- +- tda18271_calc_ir_measure(fe, &freq); +- +- tda18271_calc_bp_filter(fe, &freq); +- +- tda18271_calc_rf_band(fe, &freq); +- +- tda18271_calc_gain_taper(fe, &freq); +- +- /* --------------------------------------------------------------- */ +- +- /* dual tuner and agc1 extra configuration */ +- +- switch (priv->role) { +- case TDA18271_MASTER: +- regs[R_EB1] |= 0x04; /* main vco */ +- break; +- case TDA18271_SLAVE: +- regs[R_EB1] &= ~0x04; /* cal vco */ +- break; +- } +- +- /* agc1 always active */ +- regs[R_EB1] &= ~0x02; +- +- /* agc1 has priority on agc2 */ +- regs[R_EB1] &= ~0x01; +- +- ret = tda18271_write_regs(fe, R_EB1, 1); +- if (tda_fail(ret)) +- goto fail; +- +- /* --------------------------------------------------------------- */ +- +- N = map->if_freq * 1000 + freq; +- +- switch (priv->role) { +- case TDA18271_MASTER: +- tda18271_calc_main_pll(fe, N); +- tda18271_set_if_notch(fe); +- tda18271_write_regs(fe, R_MPD, 4); +- break; +- case TDA18271_SLAVE: +- tda18271_calc_cal_pll(fe, N); +- tda18271_write_regs(fe, R_CPD, 4); +- +- regs[R_MPD] = regs[R_CPD] & 0x7f; +- tda18271_set_if_notch(fe); +- tda18271_write_regs(fe, R_MPD, 1); +- break; +- } +- +- ret = tda18271_write_regs(fe, R_TM, 7); +- if (tda_fail(ret)) +- goto fail; +- +- /* force charge pump source */ +- charge_pump_source(fe, 1); +- +- msleep(1); +- +- /* return pll to normal operation */ +- charge_pump_source(fe, 0); +- +- msleep(20); +- +- if (priv->id == TDA18271HDC2) { +- /* set rfagc to normal speed mode */ +- if (map->fm_rfn) +- regs[R_EP3] &= ~0x04; +- else +- regs[R_EP3] |= 0x04; +- ret = tda18271_write_regs(fe, R_EP3, 1); +- } +-fail: +- return ret; +-} +- +-static int tda18271_read_thermometer(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int tm; +- +- /* switch thermometer on */ +- regs[R_TM] |= 0x10; +- tda18271_write_regs(fe, R_TM, 1); +- +- /* read thermometer info */ +- tda18271_read_regs(fe); +- +- if ((((regs[R_TM] & 0x0f) == 0x00) && ((regs[R_TM] & 0x20) == 0x20)) || +- (((regs[R_TM] & 0x0f) == 0x08) && ((regs[R_TM] & 0x20) == 0x00))) { +- +- if ((regs[R_TM] & 0x20) == 0x20) +- regs[R_TM] &= ~0x20; +- else +- regs[R_TM] |= 0x20; +- +- tda18271_write_regs(fe, R_TM, 1); +- +- msleep(10); /* temperature sensing */ +- +- /* read thermometer info */ +- tda18271_read_regs(fe); +- } +- +- tm = tda18271_lookup_thermometer(fe); +- +- /* switch thermometer off */ +- regs[R_TM] &= ~0x10; +- tda18271_write_regs(fe, R_TM, 1); +- +- /* set CAL mode to normal */ +- regs[R_EP4] &= ~0x03; +- tda18271_write_regs(fe, R_EP4, 1); +- +- return tm; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, +- u32 freq) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; +- unsigned char *regs = priv->tda18271_regs; +- int i, ret; +- u8 tm_current, dc_over_dt, rf_tab; +- s32 rfcal_comp, approx; +- +- /* power up */ +- ret = tda18271_set_standby_mode(fe, 0, 0, 0); +- if (tda_fail(ret)) +- goto fail; +- +- /* read die current temperature */ +- tm_current = tda18271_read_thermometer(fe); +- +- /* frequency dependent parameters */ +- +- tda18271_calc_rf_cal(fe, &freq); +- rf_tab = regs[R_EB14]; +- +- i = tda18271_lookup_rf_band(fe, &freq, NULL); +- if (tda_fail(i)) +- return i; +- +- if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { +- approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + +- map[i].rf_b1 + rf_tab; +- } else { +- approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + +- map[i].rf_b2 + rf_tab; +- } +- +- if (approx < 0) +- approx = 0; +- if (approx > 255) +- approx = 255; +- +- tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); +- +- /* calculate temperature compensation */ +- rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; +- +- regs[R_EB14] = (unsigned char)(approx + rfcal_comp); +- ret = tda18271_write_regs(fe, R_EB14, 1); +-fail: +- return ret; +-} +- +-static int tda18271_por(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- +- /* power up detector 1 */ +- regs[R_EB12] &= ~0x20; +- ret = tda18271_write_regs(fe, R_EB12, 1); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EB18] &= ~0x80; /* turn agc1 loop on */ +- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ +- ret = tda18271_write_regs(fe, R_EB18, 1); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EB21] |= 0x03; /* set agc2_gain to -6 dB */ +- +- /* POR mode */ +- ret = tda18271_set_standby_mode(fe, 1, 0, 0); +- if (tda_fail(ret)) +- goto fail; +- +- /* disable 1.5 MHz low pass filter */ +- regs[R_EB23] &= ~0x04; /* forcelp_fc2_en = 0 */ +- regs[R_EB23] &= ~0x02; /* XXX: lp_fc[2] = 0 */ +- ret = tda18271_write_regs(fe, R_EB21, 3); +-fail: +- return ret; +-} +- +-static int tda18271_calibrate_rf(struct dvb_frontend *fe, u32 freq) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- u32 N; +- +- /* set CAL mode to normal */ +- regs[R_EP4] &= ~0x03; +- tda18271_write_regs(fe, R_EP4, 1); +- +- /* switch off agc1 */ +- regs[R_EP3] |= 0x40; /* sm_lt = 1 */ +- +- regs[R_EB18] |= 0x03; /* set agc1_gain to 15 dB */ +- tda18271_write_regs(fe, R_EB18, 1); +- +- /* frequency dependent parameters */ +- +- tda18271_calc_bp_filter(fe, &freq); +- tda18271_calc_gain_taper(fe, &freq); +- tda18271_calc_rf_band(fe, &freq); +- tda18271_calc_km(fe, &freq); +- +- tda18271_write_regs(fe, R_EP1, 3); +- tda18271_write_regs(fe, R_EB13, 1); +- +- /* main pll charge pump source */ +- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 1); +- +- /* cal pll charge pump source */ +- tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 1); +- +- /* force dcdc converter to 0 V */ +- regs[R_EB14] = 0x00; +- tda18271_write_regs(fe, R_EB14, 1); +- +- /* disable plls lock */ +- regs[R_EB20] &= ~0x20; +- tda18271_write_regs(fe, R_EB20, 1); +- +- /* set CAL mode to RF tracking filter calibration */ +- regs[R_EP4] |= 0x03; +- tda18271_write_regs(fe, R_EP4, 2); +- +- /* --------------------------------------------------------------- */ +- +- /* set the internal calibration signal */ +- N = freq; +- +- tda18271_calc_cal_pll(fe, N); +- tda18271_write_regs(fe, R_CPD, 4); +- +- /* downconvert internal calibration */ +- N += 1000000; +- +- tda18271_calc_main_pll(fe, N); +- tda18271_write_regs(fe, R_MPD, 4); +- +- msleep(5); +- +- tda18271_write_regs(fe, R_EP2, 1); +- tda18271_write_regs(fe, R_EP1, 1); +- tda18271_write_regs(fe, R_EP2, 1); +- tda18271_write_regs(fe, R_EP1, 1); +- +- /* --------------------------------------------------------------- */ +- +- /* normal operation for the main pll */ +- tda18271_charge_pump_source(fe, TDA18271_MAIN_PLL, 0); +- +- /* normal operation for the cal pll */ +- tda18271_charge_pump_source(fe, TDA18271_CAL_PLL, 0); +- +- msleep(10); /* plls locking */ +- +- /* launch the rf tracking filters calibration */ +- regs[R_EB20] |= 0x20; +- tda18271_write_regs(fe, R_EB20, 1); +- +- msleep(60); /* calibration */ +- +- /* --------------------------------------------------------------- */ +- +- /* set CAL mode to normal */ +- regs[R_EP4] &= ~0x03; +- +- /* switch on agc1 */ +- regs[R_EP3] &= ~0x40; /* sm_lt = 0 */ +- +- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ +- tda18271_write_regs(fe, R_EB18, 1); +- +- tda18271_write_regs(fe, R_EP3, 2); +- +- /* synchronization */ +- tda18271_write_regs(fe, R_EP1, 1); +- +- /* get calibration result */ +- tda18271_read_extended(fe); +- +- return regs[R_EB14]; +-} +- +-static int tda18271_powerscan(struct dvb_frontend *fe, +- u32 *freq_in, u32 *freq_out) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int sgn, bcal, count, wait, ret; +- u8 cid_target; +- u16 count_limit; +- u32 freq; +- +- freq = *freq_in; +- +- tda18271_calc_rf_band(fe, &freq); +- tda18271_calc_rf_cal(fe, &freq); +- tda18271_calc_gain_taper(fe, &freq); +- tda18271_lookup_cid_target(fe, &freq, &cid_target, &count_limit); +- +- tda18271_write_regs(fe, R_EP2, 1); +- tda18271_write_regs(fe, R_EB14, 1); +- +- /* downconvert frequency */ +- freq += 1000000; +- +- tda18271_calc_main_pll(fe, freq); +- tda18271_write_regs(fe, R_MPD, 4); +- +- msleep(5); /* pll locking */ +- +- /* detection mode */ +- regs[R_EP4] &= ~0x03; +- regs[R_EP4] |= 0x01; +- tda18271_write_regs(fe, R_EP4, 1); +- +- /* launch power detection measurement */ +- tda18271_write_regs(fe, R_EP2, 1); +- +- /* read power detection info, stored in EB10 */ +- ret = tda18271_read_extended(fe); +- if (tda_fail(ret)) +- return ret; +- +- /* algorithm initialization */ +- sgn = 1; +- *freq_out = *freq_in; +- bcal = 0; +- count = 0; +- wait = false; +- +- while ((regs[R_EB10] & 0x3f) < cid_target) { +- /* downconvert updated freq to 1 MHz */ +- freq = *freq_in + (sgn * count) + 1000000; +- +- tda18271_calc_main_pll(fe, freq); +- tda18271_write_regs(fe, R_MPD, 4); +- +- if (wait) { +- msleep(5); /* pll locking */ +- wait = false; +- } else +- udelay(100); /* pll locking */ +- +- /* launch power detection measurement */ +- tda18271_write_regs(fe, R_EP2, 1); +- +- /* read power detection info, stored in EB10 */ +- ret = tda18271_read_extended(fe); +- if (tda_fail(ret)) +- return ret; +- +- count += 200; +- +- if (count <= count_limit) +- continue; +- +- if (sgn <= 0) +- break; +- +- sgn = -1 * sgn; +- count = 200; +- wait = true; +- } +- +- if ((regs[R_EB10] & 0x3f) >= cid_target) { +- bcal = 1; +- *freq_out = freq - 1000000; +- } else +- bcal = 0; +- +- tda_cal("bcal = %d, freq_in = %d, freq_out = %d (freq = %d)\n", +- bcal, *freq_in, *freq_out, freq); +- +- return bcal; +-} +- +-static int tda18271_powerscan_init(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- +- /* set standard to digital */ +- regs[R_EP3] &= ~0x1f; /* clear std bits */ +- regs[R_EP3] |= 0x12; +- +- /* set cal mode to normal */ +- regs[R_EP4] &= ~0x03; +- +- /* update IF output level */ +- regs[R_EP4] &= ~0x1c; /* clear if level bits */ +- +- ret = tda18271_write_regs(fe, R_EP3, 2); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EB18] &= ~0x03; /* set agc1_gain to 6 dB */ +- ret = tda18271_write_regs(fe, R_EB18, 1); +- if (tda_fail(ret)) +- goto fail; +- +- regs[R_EB21] &= ~0x03; /* set agc2_gain to -15 dB */ +- +- /* 1.5 MHz low pass filter */ +- regs[R_EB23] |= 0x04; /* forcelp_fc2_en = 1 */ +- regs[R_EB23] |= 0x02; /* lp_fc[2] = 1 */ +- +- ret = tda18271_write_regs(fe, R_EB21, 3); +-fail: +- return ret; +-} +- +-static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; +- unsigned char *regs = priv->tda18271_regs; +- int bcal, rf, i; +- s32 divisor, dividend; +-#define RF1 0 +-#define RF2 1 +-#define RF3 2 +- u32 rf_default[3]; +- u32 rf_freq[3]; +- s32 prog_cal[3]; +- s32 prog_tab[3]; +- +- i = tda18271_lookup_rf_band(fe, &freq, NULL); +- +- if (tda_fail(i)) +- return i; +- +- rf_default[RF1] = 1000 * map[i].rf1_def; +- rf_default[RF2] = 1000 * map[i].rf2_def; +- rf_default[RF3] = 1000 * map[i].rf3_def; +- +- for (rf = RF1; rf <= RF3; rf++) { +- if (0 == rf_default[rf]) +- return 0; +- tda_cal("freq = %d, rf = %d\n", freq, rf); +- +- /* look for optimized calibration frequency */ +- bcal = tda18271_powerscan(fe, &rf_default[rf], &rf_freq[rf]); +- if (tda_fail(bcal)) +- return bcal; +- +- tda18271_calc_rf_cal(fe, &rf_freq[rf]); +- prog_tab[rf] = (s32)regs[R_EB14]; +- +- if (1 == bcal) +- prog_cal[rf] = +- (s32)tda18271_calibrate_rf(fe, rf_freq[rf]); +- else +- prog_cal[rf] = prog_tab[rf]; +- +- switch (rf) { +- case RF1: +- map[i].rf_a1 = 0; +- map[i].rf_b1 = (prog_cal[RF1] - prog_tab[RF1]); +- map[i].rf1 = rf_freq[RF1] / 1000; +- break; +- case RF2: +- dividend = (prog_cal[RF2] - prog_tab[RF2] - +- prog_cal[RF1] + prog_tab[RF1]); +- divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; +- map[i].rf_a1 = (dividend / divisor); +- map[i].rf2 = rf_freq[RF2] / 1000; +- break; +- case RF3: +- dividend = (prog_cal[RF3] - prog_tab[RF3] - +- prog_cal[RF2] + prog_tab[RF2]); +- divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; +- map[i].rf_a2 = (dividend / divisor); +- map[i].rf_b2 = (prog_cal[RF2] - prog_tab[RF2]); +- map[i].rf3 = rf_freq[RF3] / 1000; +- break; +- default: +- BUG(); +- } +- } +- +- return 0; +-} +- +-static int tda18271_calc_rf_filter_curve(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned int i; +- int ret; +- +- tda_info("tda18271: performing RF tracking filter calibration\n"); +- +- /* wait for die temperature stabilization */ +- msleep(200); +- +- ret = tda18271_powerscan_init(fe); +- if (tda_fail(ret)) +- goto fail; +- +- /* rf band calibration */ +- for (i = 0; priv->rf_cal_state[i].rfmax != 0; i++) { +- ret = +- tda18271_rf_tracking_filters_init(fe, 1000 * +- priv->rf_cal_state[i].rfmax); +- if (tda_fail(ret)) +- goto fail; +- } +- +- priv->tm_rfcal = tda18271_read_thermometer(fe); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda18271c2_rf_cal_init(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- +- /* test RF_CAL_OK to see if we need init */ +- if ((regs[R_EP1] & 0x10) == 0) +- priv->cal_initialized = false; +- +- if (priv->cal_initialized) +- return 0; +- +- ret = tda18271_calc_rf_filter_curve(fe); +- if (tda_fail(ret)) +- goto fail; +- +- ret = tda18271_por(fe); +- if (tda_fail(ret)) +- goto fail; +- +- tda_info("tda18271: RF tracking filter calibration complete\n"); +- +- priv->cal_initialized = true; +- goto end; +-fail: +- tda_info("tda18271: RF tracking filter calibration failed!\n"); +-end: +- return ret; +-} +- +-static int tda18271c1_rf_tracking_filter_calibration(struct dvb_frontend *fe, +- u32 freq, u32 bw) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- u32 N = 0; +- +- /* calculate bp filter */ +- tda18271_calc_bp_filter(fe, &freq); +- tda18271_write_regs(fe, R_EP1, 1); +- +- regs[R_EB4] &= 0x07; +- regs[R_EB4] |= 0x60; +- tda18271_write_regs(fe, R_EB4, 1); +- +- regs[R_EB7] = 0x60; +- tda18271_write_regs(fe, R_EB7, 1); +- +- regs[R_EB14] = 0x00; +- tda18271_write_regs(fe, R_EB14, 1); +- +- regs[R_EB20] = 0xcc; +- tda18271_write_regs(fe, R_EB20, 1); +- +- /* set cal mode to RF tracking filter calibration */ +- regs[R_EP4] |= 0x03; +- +- /* calculate cal pll */ +- +- switch (priv->mode) { +- case TDA18271_ANALOG: +- N = freq - 1250000; +- break; +- case TDA18271_DIGITAL: +- N = freq + bw / 2; +- break; +- } +- +- tda18271_calc_cal_pll(fe, N); +- +- /* calculate main pll */ +- +- switch (priv->mode) { +- case TDA18271_ANALOG: +- N = freq - 250000; +- break; +- case TDA18271_DIGITAL: +- N = freq + bw / 2 + 1000000; +- break; +- } +- +- tda18271_calc_main_pll(fe, N); +- +- ret = tda18271_write_regs(fe, R_EP3, 11); +- if (tda_fail(ret)) +- return ret; +- +- msleep(5); /* RF tracking filter calibration initialization */ +- +- /* search for K,M,CO for RF calibration */ +- tda18271_calc_km(fe, &freq); +- tda18271_write_regs(fe, R_EB13, 1); +- +- /* search for rf band */ +- tda18271_calc_rf_band(fe, &freq); +- +- /* search for gain taper */ +- tda18271_calc_gain_taper(fe, &freq); +- +- tda18271_write_regs(fe, R_EP2, 1); +- tda18271_write_regs(fe, R_EP1, 1); +- tda18271_write_regs(fe, R_EP2, 1); +- tda18271_write_regs(fe, R_EP1, 1); +- +- regs[R_EB4] &= 0x07; +- regs[R_EB4] |= 0x40; +- tda18271_write_regs(fe, R_EB4, 1); +- +- regs[R_EB7] = 0x40; +- tda18271_write_regs(fe, R_EB7, 1); +- msleep(10); /* pll locking */ +- +- regs[R_EB20] = 0xec; +- tda18271_write_regs(fe, R_EB20, 1); +- msleep(60); /* RF tracking filter calibration completion */ +- +- regs[R_EP4] &= ~0x03; /* set cal mode to normal */ +- tda18271_write_regs(fe, R_EP4, 1); +- +- tda18271_write_regs(fe, R_EP1, 1); +- +- /* RF tracking filter correction for VHF_Low band */ +- if (0 == tda18271_calc_rf_cal(fe, &freq)) +- tda18271_write_regs(fe, R_EB14, 1); +- +- return 0; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda18271_ir_cal_init(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int ret; +- +- ret = tda18271_read_regs(fe); +- if (tda_fail(ret)) +- goto fail; +- +- /* test IR_CAL_OK to see if we need init */ +- if ((regs[R_EP1] & 0x08) == 0) +- ret = tda18271_init_regs(fe); +-fail: +- return ret; +-} +- +-static int tda18271_init(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int ret; +- +- mutex_lock(&priv->lock); +- +- /* full power up */ +- ret = tda18271_set_standby_mode(fe, 0, 0, 0); +- if (tda_fail(ret)) +- goto fail; +- +- /* initialization */ +- ret = tda18271_ir_cal_init(fe); +- if (tda_fail(ret)) +- goto fail; +- +- if (priv->id == TDA18271HDC2) +- tda18271c2_rf_cal_init(fe); +-fail: +- mutex_unlock(&priv->lock); +- +- return ret; +-} +- +-static int tda18271_sleep(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int ret; +- +- mutex_lock(&priv->lock); +- +- /* enter standby mode, with required output features enabled */ +- ret = tda18271_toggle_output(fe, 1); +- +- mutex_unlock(&priv->lock); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda18271_agc(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int ret = 0; +- +- switch (priv->config) { +- case 0: +- /* no external agc configuration required */ +- if (tda18271_debug & DBG_ADV) +- tda_dbg("no agc configuration provided\n"); +- break; +- case 3: +- /* switch with GPIO of saa713x */ +- tda_dbg("invoking callback\n"); +- if (fe->callback) +- ret = fe->callback(priv->i2c_props.adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, +- TDA18271_CALLBACK_CMD_AGC_ENABLE, +- priv->mode); +- break; +- case 1: +- case 2: +- default: +- /* n/a - currently not supported */ +- tda_err("unsupported configuration: %d\n", priv->config); +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int tda18271_tune(struct dvb_frontend *fe, +- struct tda18271_std_map_item *map, u32 freq, u32 bw) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int ret; +- +- tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n", +- freq, map->if_freq, bw, map->agc_mode, map->std); +- +- ret = tda18271_agc(fe); +- if (tda_fail(ret)) +- tda_warn("failed to configure agc\n"); +- +- ret = tda18271_init(fe); +- if (tda_fail(ret)) +- goto fail; +- +- mutex_lock(&priv->lock); +- +- switch (priv->id) { +- case TDA18271HDC1: +- tda18271c1_rf_tracking_filter_calibration(fe, freq, bw); +- break; +- case TDA18271HDC2: +- tda18271c2_rf_tracking_filters_correction(fe, freq); +- break; +- } +- ret = tda18271_channel_configuration(fe, map, freq, bw); +- +- mutex_unlock(&priv->lock); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda18271_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- u32 freq = c->frequency; +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_std_map *std_map = &priv->std; +- struct tda18271_std_map_item *map; +- int ret; +- +- priv->mode = TDA18271_DIGITAL; +- +- switch (delsys) { +- case SYS_ATSC: +- map = &std_map->atsc_6; +- bw = 6000000; +- break; +- case SYS_ISDBT: +- case SYS_DVBT: +- case SYS_DVBT2: +- if (bw <= 6000000) { +- map = &std_map->dvbt_6; +- } else if (bw <= 7000000) { +- map = &std_map->dvbt_7; +- } else { +- map = &std_map->dvbt_8; +- } +- break; +- case SYS_DVBC_ANNEX_B: +- bw = 6000000; +- /* falltrough */ +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- if (bw <= 6000000) { +- map = &std_map->qam_6; +- } else if (bw <= 7000000) { +- map = &std_map->qam_7; +- } else { +- map = &std_map->qam_8; +- } +- break; +- default: +- tda_warn("modulation type not supported!\n"); +- return -EINVAL; +- } +- +- /* When tuning digital, the analog demod must be tri-stated */ +- if (fe->ops.analog_ops.standby) +- fe->ops.analog_ops.standby(fe); +- +- ret = tda18271_tune(fe, map, freq, bw); +- +- if (tda_fail(ret)) +- goto fail; +- +- priv->if_freq = map->if_freq; +- priv->frequency = freq; +- priv->bandwidth = bw; +-fail: +- return ret; +-} +- +-static int tda18271_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_std_map *std_map = &priv->std; +- struct tda18271_std_map_item *map; +- char *mode; +- int ret; +- u32 freq = params->frequency * 125 * +- ((params->mode == V4L2_TUNER_RADIO) ? 1 : 1000) / 2; +- +- priv->mode = TDA18271_ANALOG; +- +- if (params->mode == V4L2_TUNER_RADIO) { +- map = &std_map->fm_radio; +- mode = "fm"; +- } else if (params->std & V4L2_STD_MN) { +- map = &std_map->atv_mn; +- mode = "MN"; +- } else if (params->std & V4L2_STD_B) { +- map = &std_map->atv_b; +- mode = "B"; +- } else if (params->std & V4L2_STD_GH) { +- map = &std_map->atv_gh; +- mode = "GH"; +- } else if (params->std & V4L2_STD_PAL_I) { +- map = &std_map->atv_i; +- mode = "I"; +- } else if (params->std & V4L2_STD_DK) { +- map = &std_map->atv_dk; +- mode = "DK"; +- } else if (params->std & V4L2_STD_SECAM_L) { +- map = &std_map->atv_l; +- mode = "L"; +- } else if (params->std & V4L2_STD_SECAM_LC) { +- map = &std_map->atv_lc; +- mode = "L'"; +- } else { +- map = &std_map->atv_i; +- mode = "xx"; +- } +- +- tda_dbg("setting tda18271 to system %s\n", mode); +- +- ret = tda18271_tune(fe, map, freq, 0); +- +- if (tda_fail(ret)) +- goto fail; +- +- priv->if_freq = map->if_freq; +- priv->frequency = freq; +- priv->bandwidth = 0; +-fail: +- return ret; +-} +- +-static int tda18271_release(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- +- mutex_lock(&tda18271_list_mutex); +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&tda18271_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int tda18271_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int tda18271_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- *bandwidth = priv->bandwidth; +- return 0; +-} +- +-static int tda18271_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- *frequency = (u32)priv->if_freq * 1000; +- return 0; +-} +- +-/* ------------------------------------------------------------------ */ +- +-#define tda18271_update_std(std_cfg, name) do { \ +- if (map->std_cfg.if_freq + \ +- map->std_cfg.agc_mode + map->std_cfg.std + \ +- map->std_cfg.if_lvl + map->std_cfg.rfagc_top > 0) { \ +- tda_dbg("Using custom std config for %s\n", name); \ +- memcpy(&std->std_cfg, &map->std_cfg, \ +- sizeof(struct tda18271_std_map_item)); \ +- } } while (0) +- +-#define tda18271_dump_std_item(std_cfg, name) do { \ +- tda_dbg("(%s) if_freq = %d, agc_mode = %d, std = %d, " \ +- "if_lvl = %d, rfagc_top = 0x%02x\n", \ +- name, std->std_cfg.if_freq, \ +- std->std_cfg.agc_mode, std->std_cfg.std, \ +- std->std_cfg.if_lvl, std->std_cfg.rfagc_top); \ +- } while (0) +- +-static int tda18271_dump_std_map(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_std_map *std = &priv->std; +- +- tda_dbg("========== STANDARD MAP SETTINGS ==========\n"); +- tda18271_dump_std_item(fm_radio, " fm "); +- tda18271_dump_std_item(atv_b, "atv b "); +- tda18271_dump_std_item(atv_dk, "atv dk"); +- tda18271_dump_std_item(atv_gh, "atv gh"); +- tda18271_dump_std_item(atv_i, "atv i "); +- tda18271_dump_std_item(atv_l, "atv l "); +- tda18271_dump_std_item(atv_lc, "atv l'"); +- tda18271_dump_std_item(atv_mn, "atv mn"); +- tda18271_dump_std_item(atsc_6, "atsc 6"); +- tda18271_dump_std_item(dvbt_6, "dvbt 6"); +- tda18271_dump_std_item(dvbt_7, "dvbt 7"); +- tda18271_dump_std_item(dvbt_8, "dvbt 8"); +- tda18271_dump_std_item(qam_6, "qam 6 "); +- tda18271_dump_std_item(qam_8, "qam 8 "); +- +- return 0; +-} +- +-static int tda18271_update_std_map(struct dvb_frontend *fe, +- struct tda18271_std_map *map) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_std_map *std = &priv->std; +- +- if (!map) +- return -EINVAL; +- +- tda18271_update_std(fm_radio, "fm"); +- tda18271_update_std(atv_b, "atv b"); +- tda18271_update_std(atv_dk, "atv dk"); +- tda18271_update_std(atv_gh, "atv gh"); +- tda18271_update_std(atv_i, "atv i"); +- tda18271_update_std(atv_l, "atv l"); +- tda18271_update_std(atv_lc, "atv l'"); +- tda18271_update_std(atv_mn, "atv mn"); +- tda18271_update_std(atsc_6, "atsc 6"); +- tda18271_update_std(dvbt_6, "dvbt 6"); +- tda18271_update_std(dvbt_7, "dvbt 7"); +- tda18271_update_std(dvbt_8, "dvbt 8"); +- tda18271_update_std(qam_6, "qam 6"); +- tda18271_update_std(qam_8, "qam 8"); +- +- return 0; +-} +- +-static int tda18271_get_id(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- char *name; +- +- mutex_lock(&priv->lock); +- tda18271_read_regs(fe); +- mutex_unlock(&priv->lock); +- +- switch (regs[R_ID] & 0x7f) { +- case 3: +- name = "TDA18271HD/C1"; +- priv->id = TDA18271HDC1; +- break; +- case 4: +- name = "TDA18271HD/C2"; +- priv->id = TDA18271HDC2; +- break; +- default: +- tda_info("Unknown device (%i) detected @ %d-%04x, device not supported.\n", +- regs[R_ID], i2c_adapter_id(priv->i2c_props.adap), +- priv->i2c_props.addr); +- return -EINVAL; +- } +- +- tda_info("%s detected @ %d-%04x\n", name, +- i2c_adapter_id(priv->i2c_props.adap), priv->i2c_props.addr); +- +- return 0; +-} +- +-static int tda18271_setup_configuration(struct dvb_frontend *fe, +- struct tda18271_config *cfg) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- +- priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; +- priv->role = (cfg) ? cfg->role : TDA18271_MASTER; +- priv->config = (cfg) ? cfg->config : 0; +- priv->small_i2c = (cfg) ? +- cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; +- priv->output_opt = (cfg) ? +- cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; +- +- return 0; +-} +- +-static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +-{ +- /* tda18271_cal_on_startup == -1 when cal module option is unset */ +- return ((tda18271_cal_on_startup == -1) ? +- /* honor configuration setting */ +- ((cfg) && (cfg->rf_cal_on_startup)) : +- /* module option overrides configuration setting */ +- (tda18271_cal_on_startup)) ? 1 : 0; +-} +- +-static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +-{ +- struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; +- +- tda18271_setup_configuration(fe, cfg); +- +- if (tda18271_need_cal_on_startup(cfg)) +- tda18271_init(fe); +- +- /* override default std map with values in config struct */ +- if ((cfg) && (cfg->std_map)) +- tda18271_update_std_map(fe, cfg->std_map); +- +- return 0; +-} +- +-static const struct dvb_tuner_ops tda18271_tuner_ops = { +- .info = { +- .name = "NXP TDA18271HD", +- .frequency_min = 45000000, +- .frequency_max = 864000000, +- .frequency_step = 62500 +- }, +- .init = tda18271_init, +- .sleep = tda18271_sleep, +- .set_params = tda18271_set_params, +- .set_analog_params = tda18271_set_analog_params, +- .release = tda18271_release, +- .set_config = tda18271_set_config, +- .get_frequency = tda18271_get_frequency, +- .get_bandwidth = tda18271_get_bandwidth, +- .get_if_frequency = tda18271_get_if_frequency, +-}; +- +-struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, +- struct i2c_adapter *i2c, +- struct tda18271_config *cfg) +-{ +- struct tda18271_priv *priv = NULL; +- int instance, ret; +- +- mutex_lock(&tda18271_list_mutex); +- +- instance = hybrid_tuner_request_state(struct tda18271_priv, priv, +- hybrid_tuner_instance_list, +- i2c, addr, "tda18271"); +- switch (instance) { +- case 0: +- goto fail; +- case 1: +- /* new tuner instance */ +- fe->tuner_priv = priv; +- +- tda18271_setup_configuration(fe, cfg); +- +- priv->cal_initialized = false; +- mutex_init(&priv->lock); +- +- ret = tda18271_get_id(fe); +- if (tda_fail(ret)) +- goto fail; +- +- ret = tda18271_assign_map_layout(fe); +- if (tda_fail(ret)) +- goto fail; +- +- mutex_lock(&priv->lock); +- tda18271_init_regs(fe); +- +- if ((tda18271_need_cal_on_startup(cfg)) && +- (priv->id == TDA18271HDC2)) +- tda18271c2_rf_cal_init(fe); +- +- mutex_unlock(&priv->lock); +- break; +- default: +- /* existing tuner instance */ +- fe->tuner_priv = priv; +- +- /* allow dvb driver to override configuration settings */ +- if (cfg) { +- if (cfg->gate != TDA18271_GATE_ANALOG) +- priv->gate = cfg->gate; +- if (cfg->role) +- priv->role = cfg->role; +- if (cfg->config) +- priv->config = cfg->config; +- if (cfg->small_i2c) +- priv->small_i2c = cfg->small_i2c; +- if (cfg->output_opt) +- priv->output_opt = cfg->output_opt; +- if (cfg->std_map) +- tda18271_update_std_map(fe, cfg->std_map); +- } +- if (tda18271_need_cal_on_startup(cfg)) +- tda18271_init(fe); +- break; +- } +- +- /* override default std map with values in config struct */ +- if ((cfg) && (cfg->std_map)) +- tda18271_update_std_map(fe, cfg->std_map); +- +- mutex_unlock(&tda18271_list_mutex); +- +- memcpy(&fe->ops.tuner_ops, &tda18271_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- if (tda18271_debug & (DBG_MAP | DBG_ADV)) +- tda18271_dump_std_map(fe); +- +- return fe; +-fail: +- mutex_unlock(&tda18271_list_mutex); +- +- tda18271_release(fe); +- return NULL; +-} +-EXPORT_SYMBOL_GPL(tda18271_attach); +-MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.4"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c +deleted file mode 100644 +index fb881c6..0000000 +--- a/drivers/media/common/tuners/tda18271-maps.c ++++ /dev/null +@@ -1,1317 +0,0 @@ +-/* +- tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner +- +- Copyright (C) 2007, 2008 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include "tda18271-priv.h" +- +-struct tda18271_pll_map { +- u32 lomax; +- u8 pd; /* post div */ +- u8 d; /* div */ +-}; +- +-struct tda18271_map { +- u32 rfmax; +- u8 val; +-}; +- +-/*---------------------------------------------------------------------*/ +- +-static struct tda18271_pll_map tda18271c1_main_pll[] = { +- { .lomax = 32000, .pd = 0x5f, .d = 0xf0 }, +- { .lomax = 35000, .pd = 0x5e, .d = 0xe0 }, +- { .lomax = 37000, .pd = 0x5d, .d = 0xd0 }, +- { .lomax = 41000, .pd = 0x5c, .d = 0xc0 }, +- { .lomax = 44000, .pd = 0x5b, .d = 0xb0 }, +- { .lomax = 49000, .pd = 0x5a, .d = 0xa0 }, +- { .lomax = 54000, .pd = 0x59, .d = 0x90 }, +- { .lomax = 61000, .pd = 0x58, .d = 0x80 }, +- { .lomax = 65000, .pd = 0x4f, .d = 0x78 }, +- { .lomax = 70000, .pd = 0x4e, .d = 0x70 }, +- { .lomax = 75000, .pd = 0x4d, .d = 0x68 }, +- { .lomax = 82000, .pd = 0x4c, .d = 0x60 }, +- { .lomax = 89000, .pd = 0x4b, .d = 0x58 }, +- { .lomax = 98000, .pd = 0x4a, .d = 0x50 }, +- { .lomax = 109000, .pd = 0x49, .d = 0x48 }, +- { .lomax = 123000, .pd = 0x48, .d = 0x40 }, +- { .lomax = 131000, .pd = 0x3f, .d = 0x3c }, +- { .lomax = 141000, .pd = 0x3e, .d = 0x38 }, +- { .lomax = 151000, .pd = 0x3d, .d = 0x34 }, +- { .lomax = 164000, .pd = 0x3c, .d = 0x30 }, +- { .lomax = 179000, .pd = 0x3b, .d = 0x2c }, +- { .lomax = 197000, .pd = 0x3a, .d = 0x28 }, +- { .lomax = 219000, .pd = 0x39, .d = 0x24 }, +- { .lomax = 246000, .pd = 0x38, .d = 0x20 }, +- { .lomax = 263000, .pd = 0x2f, .d = 0x1e }, +- { .lomax = 282000, .pd = 0x2e, .d = 0x1c }, +- { .lomax = 303000, .pd = 0x2d, .d = 0x1a }, +- { .lomax = 329000, .pd = 0x2c, .d = 0x18 }, +- { .lomax = 359000, .pd = 0x2b, .d = 0x16 }, +- { .lomax = 395000, .pd = 0x2a, .d = 0x14 }, +- { .lomax = 438000, .pd = 0x29, .d = 0x12 }, +- { .lomax = 493000, .pd = 0x28, .d = 0x10 }, +- { .lomax = 526000, .pd = 0x1f, .d = 0x0f }, +- { .lomax = 564000, .pd = 0x1e, .d = 0x0e }, +- { .lomax = 607000, .pd = 0x1d, .d = 0x0d }, +- { .lomax = 658000, .pd = 0x1c, .d = 0x0c }, +- { .lomax = 718000, .pd = 0x1b, .d = 0x0b }, +- { .lomax = 790000, .pd = 0x1a, .d = 0x0a }, +- { .lomax = 877000, .pd = 0x19, .d = 0x09 }, +- { .lomax = 987000, .pd = 0x18, .d = 0x08 }, +- { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +-}; +- +-static struct tda18271_pll_map tda18271c2_main_pll[] = { +- { .lomax = 33125, .pd = 0x57, .d = 0xf0 }, +- { .lomax = 35500, .pd = 0x56, .d = 0xe0 }, +- { .lomax = 38188, .pd = 0x55, .d = 0xd0 }, +- { .lomax = 41375, .pd = 0x54, .d = 0xc0 }, +- { .lomax = 45125, .pd = 0x53, .d = 0xb0 }, +- { .lomax = 49688, .pd = 0x52, .d = 0xa0 }, +- { .lomax = 55188, .pd = 0x51, .d = 0x90 }, +- { .lomax = 62125, .pd = 0x50, .d = 0x80 }, +- { .lomax = 66250, .pd = 0x47, .d = 0x78 }, +- { .lomax = 71000, .pd = 0x46, .d = 0x70 }, +- { .lomax = 76375, .pd = 0x45, .d = 0x68 }, +- { .lomax = 82750, .pd = 0x44, .d = 0x60 }, +- { .lomax = 90250, .pd = 0x43, .d = 0x58 }, +- { .lomax = 99375, .pd = 0x42, .d = 0x50 }, +- { .lomax = 110375, .pd = 0x41, .d = 0x48 }, +- { .lomax = 124250, .pd = 0x40, .d = 0x40 }, +- { .lomax = 132500, .pd = 0x37, .d = 0x3c }, +- { .lomax = 142000, .pd = 0x36, .d = 0x38 }, +- { .lomax = 152750, .pd = 0x35, .d = 0x34 }, +- { .lomax = 165500, .pd = 0x34, .d = 0x30 }, +- { .lomax = 180500, .pd = 0x33, .d = 0x2c }, +- { .lomax = 198750, .pd = 0x32, .d = 0x28 }, +- { .lomax = 220750, .pd = 0x31, .d = 0x24 }, +- { .lomax = 248500, .pd = 0x30, .d = 0x20 }, +- { .lomax = 265000, .pd = 0x27, .d = 0x1e }, +- { .lomax = 284000, .pd = 0x26, .d = 0x1c }, +- { .lomax = 305500, .pd = 0x25, .d = 0x1a }, +- { .lomax = 331000, .pd = 0x24, .d = 0x18 }, +- { .lomax = 361000, .pd = 0x23, .d = 0x16 }, +- { .lomax = 397500, .pd = 0x22, .d = 0x14 }, +- { .lomax = 441500, .pd = 0x21, .d = 0x12 }, +- { .lomax = 497000, .pd = 0x20, .d = 0x10 }, +- { .lomax = 530000, .pd = 0x17, .d = 0x0f }, +- { .lomax = 568000, .pd = 0x16, .d = 0x0e }, +- { .lomax = 611000, .pd = 0x15, .d = 0x0d }, +- { .lomax = 662000, .pd = 0x14, .d = 0x0c }, +- { .lomax = 722000, .pd = 0x13, .d = 0x0b }, +- { .lomax = 795000, .pd = 0x12, .d = 0x0a }, +- { .lomax = 883000, .pd = 0x11, .d = 0x09 }, +- { .lomax = 994000, .pd = 0x10, .d = 0x08 }, +- { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +-}; +- +-static struct tda18271_pll_map tda18271c1_cal_pll[] = { +- { .lomax = 33000, .pd = 0xdd, .d = 0xd0 }, +- { .lomax = 36000, .pd = 0xdc, .d = 0xc0 }, +- { .lomax = 40000, .pd = 0xdb, .d = 0xb0 }, +- { .lomax = 44000, .pd = 0xda, .d = 0xa0 }, +- { .lomax = 49000, .pd = 0xd9, .d = 0x90 }, +- { .lomax = 55000, .pd = 0xd8, .d = 0x80 }, +- { .lomax = 63000, .pd = 0xd3, .d = 0x70 }, +- { .lomax = 67000, .pd = 0xcd, .d = 0x68 }, +- { .lomax = 73000, .pd = 0xcc, .d = 0x60 }, +- { .lomax = 80000, .pd = 0xcb, .d = 0x58 }, +- { .lomax = 88000, .pd = 0xca, .d = 0x50 }, +- { .lomax = 98000, .pd = 0xc9, .d = 0x48 }, +- { .lomax = 110000, .pd = 0xc8, .d = 0x40 }, +- { .lomax = 126000, .pd = 0xc3, .d = 0x38 }, +- { .lomax = 135000, .pd = 0xbd, .d = 0x34 }, +- { .lomax = 147000, .pd = 0xbc, .d = 0x30 }, +- { .lomax = 160000, .pd = 0xbb, .d = 0x2c }, +- { .lomax = 176000, .pd = 0xba, .d = 0x28 }, +- { .lomax = 196000, .pd = 0xb9, .d = 0x24 }, +- { .lomax = 220000, .pd = 0xb8, .d = 0x20 }, +- { .lomax = 252000, .pd = 0xb3, .d = 0x1c }, +- { .lomax = 271000, .pd = 0xad, .d = 0x1a }, +- { .lomax = 294000, .pd = 0xac, .d = 0x18 }, +- { .lomax = 321000, .pd = 0xab, .d = 0x16 }, +- { .lomax = 353000, .pd = 0xaa, .d = 0x14 }, +- { .lomax = 392000, .pd = 0xa9, .d = 0x12 }, +- { .lomax = 441000, .pd = 0xa8, .d = 0x10 }, +- { .lomax = 505000, .pd = 0xa3, .d = 0x0e }, +- { .lomax = 543000, .pd = 0x9d, .d = 0x0d }, +- { .lomax = 589000, .pd = 0x9c, .d = 0x0c }, +- { .lomax = 642000, .pd = 0x9b, .d = 0x0b }, +- { .lomax = 707000, .pd = 0x9a, .d = 0x0a }, +- { .lomax = 785000, .pd = 0x99, .d = 0x09 }, +- { .lomax = 883000, .pd = 0x98, .d = 0x08 }, +- { .lomax = 1010000, .pd = 0x93, .d = 0x07 }, +- { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +-}; +- +-static struct tda18271_pll_map tda18271c2_cal_pll[] = { +- { .lomax = 33813, .pd = 0xdd, .d = 0xd0 }, +- { .lomax = 36625, .pd = 0xdc, .d = 0xc0 }, +- { .lomax = 39938, .pd = 0xdb, .d = 0xb0 }, +- { .lomax = 43938, .pd = 0xda, .d = 0xa0 }, +- { .lomax = 48813, .pd = 0xd9, .d = 0x90 }, +- { .lomax = 54938, .pd = 0xd8, .d = 0x80 }, +- { .lomax = 62813, .pd = 0xd3, .d = 0x70 }, +- { .lomax = 67625, .pd = 0xcd, .d = 0x68 }, +- { .lomax = 73250, .pd = 0xcc, .d = 0x60 }, +- { .lomax = 79875, .pd = 0xcb, .d = 0x58 }, +- { .lomax = 87875, .pd = 0xca, .d = 0x50 }, +- { .lomax = 97625, .pd = 0xc9, .d = 0x48 }, +- { .lomax = 109875, .pd = 0xc8, .d = 0x40 }, +- { .lomax = 125625, .pd = 0xc3, .d = 0x38 }, +- { .lomax = 135250, .pd = 0xbd, .d = 0x34 }, +- { .lomax = 146500, .pd = 0xbc, .d = 0x30 }, +- { .lomax = 159750, .pd = 0xbb, .d = 0x2c }, +- { .lomax = 175750, .pd = 0xba, .d = 0x28 }, +- { .lomax = 195250, .pd = 0xb9, .d = 0x24 }, +- { .lomax = 219750, .pd = 0xb8, .d = 0x20 }, +- { .lomax = 251250, .pd = 0xb3, .d = 0x1c }, +- { .lomax = 270500, .pd = 0xad, .d = 0x1a }, +- { .lomax = 293000, .pd = 0xac, .d = 0x18 }, +- { .lomax = 319500, .pd = 0xab, .d = 0x16 }, +- { .lomax = 351500, .pd = 0xaa, .d = 0x14 }, +- { .lomax = 390500, .pd = 0xa9, .d = 0x12 }, +- { .lomax = 439500, .pd = 0xa8, .d = 0x10 }, +- { .lomax = 502500, .pd = 0xa3, .d = 0x0e }, +- { .lomax = 541000, .pd = 0x9d, .d = 0x0d }, +- { .lomax = 586000, .pd = 0x9c, .d = 0x0c }, +- { .lomax = 639000, .pd = 0x9b, .d = 0x0b }, +- { .lomax = 703000, .pd = 0x9a, .d = 0x0a }, +- { .lomax = 781000, .pd = 0x99, .d = 0x09 }, +- { .lomax = 879000, .pd = 0x98, .d = 0x08 }, +- { .lomax = 0, .pd = 0x00, .d = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271_bp_filter[] = { +- { .rfmax = 62000, .val = 0x00 }, +- { .rfmax = 84000, .val = 0x01 }, +- { .rfmax = 100000, .val = 0x02 }, +- { .rfmax = 140000, .val = 0x03 }, +- { .rfmax = 170000, .val = 0x04 }, +- { .rfmax = 180000, .val = 0x05 }, +- { .rfmax = 865000, .val = 0x06 }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271c1_km[] = { +- { .rfmax = 61100, .val = 0x74 }, +- { .rfmax = 350000, .val = 0x40 }, +- { .rfmax = 720000, .val = 0x30 }, +- { .rfmax = 865000, .val = 0x40 }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271c2_km[] = { +- { .rfmax = 47900, .val = 0x38 }, +- { .rfmax = 61100, .val = 0x44 }, +- { .rfmax = 350000, .val = 0x30 }, +- { .rfmax = 720000, .val = 0x24 }, +- { .rfmax = 865000, .val = 0x3c }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271_rf_band[] = { +- { .rfmax = 47900, .val = 0x00 }, +- { .rfmax = 61100, .val = 0x01 }, +- { .rfmax = 152600, .val = 0x02 }, +- { .rfmax = 164700, .val = 0x03 }, +- { .rfmax = 203500, .val = 0x04 }, +- { .rfmax = 457800, .val = 0x05 }, +- { .rfmax = 865000, .val = 0x06 }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271_gain_taper[] = { +- { .rfmax = 45400, .val = 0x1f }, +- { .rfmax = 45800, .val = 0x1e }, +- { .rfmax = 46200, .val = 0x1d }, +- { .rfmax = 46700, .val = 0x1c }, +- { .rfmax = 47100, .val = 0x1b }, +- { .rfmax = 47500, .val = 0x1a }, +- { .rfmax = 47900, .val = 0x19 }, +- { .rfmax = 49600, .val = 0x17 }, +- { .rfmax = 51200, .val = 0x16 }, +- { .rfmax = 52900, .val = 0x15 }, +- { .rfmax = 54500, .val = 0x14 }, +- { .rfmax = 56200, .val = 0x13 }, +- { .rfmax = 57800, .val = 0x12 }, +- { .rfmax = 59500, .val = 0x11 }, +- { .rfmax = 61100, .val = 0x10 }, +- { .rfmax = 67600, .val = 0x0d }, +- { .rfmax = 74200, .val = 0x0c }, +- { .rfmax = 80700, .val = 0x0b }, +- { .rfmax = 87200, .val = 0x0a }, +- { .rfmax = 93800, .val = 0x09 }, +- { .rfmax = 100300, .val = 0x08 }, +- { .rfmax = 106900, .val = 0x07 }, +- { .rfmax = 113400, .val = 0x06 }, +- { .rfmax = 119900, .val = 0x05 }, +- { .rfmax = 126500, .val = 0x04 }, +- { .rfmax = 133000, .val = 0x03 }, +- { .rfmax = 139500, .val = 0x02 }, +- { .rfmax = 146100, .val = 0x01 }, +- { .rfmax = 152600, .val = 0x00 }, +- { .rfmax = 154300, .val = 0x1f }, +- { .rfmax = 156100, .val = 0x1e }, +- { .rfmax = 157800, .val = 0x1d }, +- { .rfmax = 159500, .val = 0x1c }, +- { .rfmax = 161200, .val = 0x1b }, +- { .rfmax = 163000, .val = 0x1a }, +- { .rfmax = 164700, .val = 0x19 }, +- { .rfmax = 170200, .val = 0x17 }, +- { .rfmax = 175800, .val = 0x16 }, +- { .rfmax = 181300, .val = 0x15 }, +- { .rfmax = 186900, .val = 0x14 }, +- { .rfmax = 192400, .val = 0x13 }, +- { .rfmax = 198000, .val = 0x12 }, +- { .rfmax = 203500, .val = 0x11 }, +- { .rfmax = 216200, .val = 0x14 }, +- { .rfmax = 228900, .val = 0x13 }, +- { .rfmax = 241600, .val = 0x12 }, +- { .rfmax = 254400, .val = 0x11 }, +- { .rfmax = 267100, .val = 0x10 }, +- { .rfmax = 279800, .val = 0x0f }, +- { .rfmax = 292500, .val = 0x0e }, +- { .rfmax = 305200, .val = 0x0d }, +- { .rfmax = 317900, .val = 0x0c }, +- { .rfmax = 330700, .val = 0x0b }, +- { .rfmax = 343400, .val = 0x0a }, +- { .rfmax = 356100, .val = 0x09 }, +- { .rfmax = 368800, .val = 0x08 }, +- { .rfmax = 381500, .val = 0x07 }, +- { .rfmax = 394200, .val = 0x06 }, +- { .rfmax = 406900, .val = 0x05 }, +- { .rfmax = 419700, .val = 0x04 }, +- { .rfmax = 432400, .val = 0x03 }, +- { .rfmax = 445100, .val = 0x02 }, +- { .rfmax = 457800, .val = 0x01 }, +- { .rfmax = 476300, .val = 0x19 }, +- { .rfmax = 494800, .val = 0x18 }, +- { .rfmax = 513300, .val = 0x17 }, +- { .rfmax = 531800, .val = 0x16 }, +- { .rfmax = 550300, .val = 0x15 }, +- { .rfmax = 568900, .val = 0x14 }, +- { .rfmax = 587400, .val = 0x13 }, +- { .rfmax = 605900, .val = 0x12 }, +- { .rfmax = 624400, .val = 0x11 }, +- { .rfmax = 642900, .val = 0x10 }, +- { .rfmax = 661400, .val = 0x0f }, +- { .rfmax = 679900, .val = 0x0e }, +- { .rfmax = 698400, .val = 0x0d }, +- { .rfmax = 716900, .val = 0x0c }, +- { .rfmax = 735400, .val = 0x0b }, +- { .rfmax = 753900, .val = 0x0a }, +- { .rfmax = 772500, .val = 0x09 }, +- { .rfmax = 791000, .val = 0x08 }, +- { .rfmax = 809500, .val = 0x07 }, +- { .rfmax = 828000, .val = 0x06 }, +- { .rfmax = 846500, .val = 0x05 }, +- { .rfmax = 865000, .val = 0x04 }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271c1_rf_cal[] = { +- { .rfmax = 41000, .val = 0x1e }, +- { .rfmax = 43000, .val = 0x30 }, +- { .rfmax = 45000, .val = 0x43 }, +- { .rfmax = 46000, .val = 0x4d }, +- { .rfmax = 47000, .val = 0x54 }, +- { .rfmax = 47900, .val = 0x64 }, +- { .rfmax = 49100, .val = 0x20 }, +- { .rfmax = 50000, .val = 0x22 }, +- { .rfmax = 51000, .val = 0x2a }, +- { .rfmax = 53000, .val = 0x32 }, +- { .rfmax = 55000, .val = 0x35 }, +- { .rfmax = 56000, .val = 0x3c }, +- { .rfmax = 57000, .val = 0x3f }, +- { .rfmax = 58000, .val = 0x48 }, +- { .rfmax = 59000, .val = 0x4d }, +- { .rfmax = 60000, .val = 0x58 }, +- { .rfmax = 61100, .val = 0x5f }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271c2_rf_cal[] = { +- { .rfmax = 41000, .val = 0x0f }, +- { .rfmax = 43000, .val = 0x1c }, +- { .rfmax = 45000, .val = 0x2f }, +- { .rfmax = 46000, .val = 0x39 }, +- { .rfmax = 47000, .val = 0x40 }, +- { .rfmax = 47900, .val = 0x50 }, +- { .rfmax = 49100, .val = 0x16 }, +- { .rfmax = 50000, .val = 0x18 }, +- { .rfmax = 51000, .val = 0x20 }, +- { .rfmax = 53000, .val = 0x28 }, +- { .rfmax = 55000, .val = 0x2b }, +- { .rfmax = 56000, .val = 0x32 }, +- { .rfmax = 57000, .val = 0x35 }, +- { .rfmax = 58000, .val = 0x3e }, +- { .rfmax = 59000, .val = 0x43 }, +- { .rfmax = 60000, .val = 0x4e }, +- { .rfmax = 61100, .val = 0x55 }, +- { .rfmax = 63000, .val = 0x0f }, +- { .rfmax = 64000, .val = 0x11 }, +- { .rfmax = 65000, .val = 0x12 }, +- { .rfmax = 66000, .val = 0x15 }, +- { .rfmax = 67000, .val = 0x16 }, +- { .rfmax = 68000, .val = 0x17 }, +- { .rfmax = 70000, .val = 0x19 }, +- { .rfmax = 71000, .val = 0x1c }, +- { .rfmax = 72000, .val = 0x1d }, +- { .rfmax = 73000, .val = 0x1f }, +- { .rfmax = 74000, .val = 0x20 }, +- { .rfmax = 75000, .val = 0x21 }, +- { .rfmax = 76000, .val = 0x24 }, +- { .rfmax = 77000, .val = 0x25 }, +- { .rfmax = 78000, .val = 0x27 }, +- { .rfmax = 80000, .val = 0x28 }, +- { .rfmax = 81000, .val = 0x29 }, +- { .rfmax = 82000, .val = 0x2d }, +- { .rfmax = 83000, .val = 0x2e }, +- { .rfmax = 84000, .val = 0x2f }, +- { .rfmax = 85000, .val = 0x31 }, +- { .rfmax = 86000, .val = 0x33 }, +- { .rfmax = 87000, .val = 0x34 }, +- { .rfmax = 88000, .val = 0x35 }, +- { .rfmax = 89000, .val = 0x37 }, +- { .rfmax = 90000, .val = 0x38 }, +- { .rfmax = 91000, .val = 0x39 }, +- { .rfmax = 93000, .val = 0x3c }, +- { .rfmax = 94000, .val = 0x3e }, +- { .rfmax = 95000, .val = 0x3f }, +- { .rfmax = 96000, .val = 0x40 }, +- { .rfmax = 97000, .val = 0x42 }, +- { .rfmax = 99000, .val = 0x45 }, +- { .rfmax = 100000, .val = 0x46 }, +- { .rfmax = 102000, .val = 0x48 }, +- { .rfmax = 103000, .val = 0x4a }, +- { .rfmax = 105000, .val = 0x4d }, +- { .rfmax = 106000, .val = 0x4e }, +- { .rfmax = 107000, .val = 0x50 }, +- { .rfmax = 108000, .val = 0x51 }, +- { .rfmax = 110000, .val = 0x54 }, +- { .rfmax = 111000, .val = 0x56 }, +- { .rfmax = 112000, .val = 0x57 }, +- { .rfmax = 113000, .val = 0x58 }, +- { .rfmax = 114000, .val = 0x59 }, +- { .rfmax = 115000, .val = 0x5c }, +- { .rfmax = 116000, .val = 0x5d }, +- { .rfmax = 117000, .val = 0x5f }, +- { .rfmax = 119000, .val = 0x60 }, +- { .rfmax = 120000, .val = 0x64 }, +- { .rfmax = 121000, .val = 0x65 }, +- { .rfmax = 122000, .val = 0x66 }, +- { .rfmax = 123000, .val = 0x68 }, +- { .rfmax = 124000, .val = 0x69 }, +- { .rfmax = 125000, .val = 0x6c }, +- { .rfmax = 126000, .val = 0x6d }, +- { .rfmax = 127000, .val = 0x6e }, +- { .rfmax = 128000, .val = 0x70 }, +- { .rfmax = 129000, .val = 0x71 }, +- { .rfmax = 130000, .val = 0x75 }, +- { .rfmax = 131000, .val = 0x77 }, +- { .rfmax = 132000, .val = 0x78 }, +- { .rfmax = 133000, .val = 0x7b }, +- { .rfmax = 134000, .val = 0x7e }, +- { .rfmax = 135000, .val = 0x81 }, +- { .rfmax = 136000, .val = 0x82 }, +- { .rfmax = 137000, .val = 0x87 }, +- { .rfmax = 138000, .val = 0x88 }, +- { .rfmax = 139000, .val = 0x8d }, +- { .rfmax = 140000, .val = 0x8e }, +- { .rfmax = 141000, .val = 0x91 }, +- { .rfmax = 142000, .val = 0x95 }, +- { .rfmax = 143000, .val = 0x9a }, +- { .rfmax = 144000, .val = 0x9d }, +- { .rfmax = 145000, .val = 0xa1 }, +- { .rfmax = 146000, .val = 0xa2 }, +- { .rfmax = 147000, .val = 0xa4 }, +- { .rfmax = 148000, .val = 0xa9 }, +- { .rfmax = 149000, .val = 0xae }, +- { .rfmax = 150000, .val = 0xb0 }, +- { .rfmax = 151000, .val = 0xb1 }, +- { .rfmax = 152000, .val = 0xb7 }, +- { .rfmax = 152600, .val = 0xbd }, +- { .rfmax = 154000, .val = 0x20 }, +- { .rfmax = 155000, .val = 0x22 }, +- { .rfmax = 156000, .val = 0x24 }, +- { .rfmax = 157000, .val = 0x25 }, +- { .rfmax = 158000, .val = 0x27 }, +- { .rfmax = 159000, .val = 0x29 }, +- { .rfmax = 160000, .val = 0x2c }, +- { .rfmax = 161000, .val = 0x2d }, +- { .rfmax = 163000, .val = 0x2e }, +- { .rfmax = 164000, .val = 0x2f }, +- { .rfmax = 164700, .val = 0x30 }, +- { .rfmax = 166000, .val = 0x11 }, +- { .rfmax = 167000, .val = 0x12 }, +- { .rfmax = 168000, .val = 0x13 }, +- { .rfmax = 169000, .val = 0x14 }, +- { .rfmax = 170000, .val = 0x15 }, +- { .rfmax = 172000, .val = 0x16 }, +- { .rfmax = 173000, .val = 0x17 }, +- { .rfmax = 174000, .val = 0x18 }, +- { .rfmax = 175000, .val = 0x1a }, +- { .rfmax = 176000, .val = 0x1b }, +- { .rfmax = 178000, .val = 0x1d }, +- { .rfmax = 179000, .val = 0x1e }, +- { .rfmax = 180000, .val = 0x1f }, +- { .rfmax = 181000, .val = 0x20 }, +- { .rfmax = 182000, .val = 0x21 }, +- { .rfmax = 183000, .val = 0x22 }, +- { .rfmax = 184000, .val = 0x24 }, +- { .rfmax = 185000, .val = 0x25 }, +- { .rfmax = 186000, .val = 0x26 }, +- { .rfmax = 187000, .val = 0x27 }, +- { .rfmax = 188000, .val = 0x29 }, +- { .rfmax = 189000, .val = 0x2a }, +- { .rfmax = 190000, .val = 0x2c }, +- { .rfmax = 191000, .val = 0x2d }, +- { .rfmax = 192000, .val = 0x2e }, +- { .rfmax = 193000, .val = 0x2f }, +- { .rfmax = 194000, .val = 0x30 }, +- { .rfmax = 195000, .val = 0x33 }, +- { .rfmax = 196000, .val = 0x35 }, +- { .rfmax = 198000, .val = 0x36 }, +- { .rfmax = 200000, .val = 0x38 }, +- { .rfmax = 201000, .val = 0x3c }, +- { .rfmax = 202000, .val = 0x3d }, +- { .rfmax = 203500, .val = 0x3e }, +- { .rfmax = 206000, .val = 0x0e }, +- { .rfmax = 208000, .val = 0x0f }, +- { .rfmax = 212000, .val = 0x10 }, +- { .rfmax = 216000, .val = 0x11 }, +- { .rfmax = 217000, .val = 0x12 }, +- { .rfmax = 218000, .val = 0x13 }, +- { .rfmax = 220000, .val = 0x14 }, +- { .rfmax = 222000, .val = 0x15 }, +- { .rfmax = 225000, .val = 0x16 }, +- { .rfmax = 228000, .val = 0x17 }, +- { .rfmax = 231000, .val = 0x18 }, +- { .rfmax = 234000, .val = 0x19 }, +- { .rfmax = 235000, .val = 0x1a }, +- { .rfmax = 236000, .val = 0x1b }, +- { .rfmax = 237000, .val = 0x1c }, +- { .rfmax = 240000, .val = 0x1d }, +- { .rfmax = 242000, .val = 0x1e }, +- { .rfmax = 244000, .val = 0x1f }, +- { .rfmax = 247000, .val = 0x20 }, +- { .rfmax = 249000, .val = 0x21 }, +- { .rfmax = 252000, .val = 0x22 }, +- { .rfmax = 253000, .val = 0x23 }, +- { .rfmax = 254000, .val = 0x24 }, +- { .rfmax = 256000, .val = 0x25 }, +- { .rfmax = 259000, .val = 0x26 }, +- { .rfmax = 262000, .val = 0x27 }, +- { .rfmax = 264000, .val = 0x28 }, +- { .rfmax = 267000, .val = 0x29 }, +- { .rfmax = 269000, .val = 0x2a }, +- { .rfmax = 271000, .val = 0x2b }, +- { .rfmax = 273000, .val = 0x2c }, +- { .rfmax = 275000, .val = 0x2d }, +- { .rfmax = 277000, .val = 0x2e }, +- { .rfmax = 279000, .val = 0x2f }, +- { .rfmax = 282000, .val = 0x30 }, +- { .rfmax = 284000, .val = 0x31 }, +- { .rfmax = 286000, .val = 0x32 }, +- { .rfmax = 287000, .val = 0x33 }, +- { .rfmax = 290000, .val = 0x34 }, +- { .rfmax = 293000, .val = 0x35 }, +- { .rfmax = 295000, .val = 0x36 }, +- { .rfmax = 297000, .val = 0x37 }, +- { .rfmax = 300000, .val = 0x38 }, +- { .rfmax = 303000, .val = 0x39 }, +- { .rfmax = 305000, .val = 0x3a }, +- { .rfmax = 306000, .val = 0x3b }, +- { .rfmax = 307000, .val = 0x3c }, +- { .rfmax = 310000, .val = 0x3d }, +- { .rfmax = 312000, .val = 0x3e }, +- { .rfmax = 315000, .val = 0x3f }, +- { .rfmax = 318000, .val = 0x40 }, +- { .rfmax = 320000, .val = 0x41 }, +- { .rfmax = 323000, .val = 0x42 }, +- { .rfmax = 324000, .val = 0x43 }, +- { .rfmax = 325000, .val = 0x44 }, +- { .rfmax = 327000, .val = 0x45 }, +- { .rfmax = 331000, .val = 0x46 }, +- { .rfmax = 334000, .val = 0x47 }, +- { .rfmax = 337000, .val = 0x48 }, +- { .rfmax = 339000, .val = 0x49 }, +- { .rfmax = 340000, .val = 0x4a }, +- { .rfmax = 341000, .val = 0x4b }, +- { .rfmax = 343000, .val = 0x4c }, +- { .rfmax = 345000, .val = 0x4d }, +- { .rfmax = 349000, .val = 0x4e }, +- { .rfmax = 352000, .val = 0x4f }, +- { .rfmax = 353000, .val = 0x50 }, +- { .rfmax = 355000, .val = 0x51 }, +- { .rfmax = 357000, .val = 0x52 }, +- { .rfmax = 359000, .val = 0x53 }, +- { .rfmax = 361000, .val = 0x54 }, +- { .rfmax = 362000, .val = 0x55 }, +- { .rfmax = 364000, .val = 0x56 }, +- { .rfmax = 368000, .val = 0x57 }, +- { .rfmax = 370000, .val = 0x58 }, +- { .rfmax = 372000, .val = 0x59 }, +- { .rfmax = 375000, .val = 0x5a }, +- { .rfmax = 376000, .val = 0x5b }, +- { .rfmax = 377000, .val = 0x5c }, +- { .rfmax = 379000, .val = 0x5d }, +- { .rfmax = 382000, .val = 0x5e }, +- { .rfmax = 384000, .val = 0x5f }, +- { .rfmax = 385000, .val = 0x60 }, +- { .rfmax = 386000, .val = 0x61 }, +- { .rfmax = 388000, .val = 0x62 }, +- { .rfmax = 390000, .val = 0x63 }, +- { .rfmax = 393000, .val = 0x64 }, +- { .rfmax = 394000, .val = 0x65 }, +- { .rfmax = 396000, .val = 0x66 }, +- { .rfmax = 397000, .val = 0x67 }, +- { .rfmax = 398000, .val = 0x68 }, +- { .rfmax = 400000, .val = 0x69 }, +- { .rfmax = 402000, .val = 0x6a }, +- { .rfmax = 403000, .val = 0x6b }, +- { .rfmax = 407000, .val = 0x6c }, +- { .rfmax = 408000, .val = 0x6d }, +- { .rfmax = 409000, .val = 0x6e }, +- { .rfmax = 410000, .val = 0x6f }, +- { .rfmax = 411000, .val = 0x70 }, +- { .rfmax = 412000, .val = 0x71 }, +- { .rfmax = 413000, .val = 0x72 }, +- { .rfmax = 414000, .val = 0x73 }, +- { .rfmax = 417000, .val = 0x74 }, +- { .rfmax = 418000, .val = 0x75 }, +- { .rfmax = 420000, .val = 0x76 }, +- { .rfmax = 422000, .val = 0x77 }, +- { .rfmax = 423000, .val = 0x78 }, +- { .rfmax = 424000, .val = 0x79 }, +- { .rfmax = 427000, .val = 0x7a }, +- { .rfmax = 428000, .val = 0x7b }, +- { .rfmax = 429000, .val = 0x7d }, +- { .rfmax = 432000, .val = 0x7f }, +- { .rfmax = 434000, .val = 0x80 }, +- { .rfmax = 435000, .val = 0x81 }, +- { .rfmax = 436000, .val = 0x83 }, +- { .rfmax = 437000, .val = 0x84 }, +- { .rfmax = 438000, .val = 0x85 }, +- { .rfmax = 439000, .val = 0x86 }, +- { .rfmax = 440000, .val = 0x87 }, +- { .rfmax = 441000, .val = 0x88 }, +- { .rfmax = 442000, .val = 0x89 }, +- { .rfmax = 445000, .val = 0x8a }, +- { .rfmax = 446000, .val = 0x8b }, +- { .rfmax = 447000, .val = 0x8c }, +- { .rfmax = 448000, .val = 0x8e }, +- { .rfmax = 449000, .val = 0x8f }, +- { .rfmax = 450000, .val = 0x90 }, +- { .rfmax = 452000, .val = 0x91 }, +- { .rfmax = 453000, .val = 0x93 }, +- { .rfmax = 454000, .val = 0x94 }, +- { .rfmax = 456000, .val = 0x96 }, +- { .rfmax = 457800, .val = 0x98 }, +- { .rfmax = 461000, .val = 0x11 }, +- { .rfmax = 468000, .val = 0x12 }, +- { .rfmax = 472000, .val = 0x13 }, +- { .rfmax = 473000, .val = 0x14 }, +- { .rfmax = 474000, .val = 0x15 }, +- { .rfmax = 481000, .val = 0x16 }, +- { .rfmax = 486000, .val = 0x17 }, +- { .rfmax = 491000, .val = 0x18 }, +- { .rfmax = 498000, .val = 0x19 }, +- { .rfmax = 499000, .val = 0x1a }, +- { .rfmax = 501000, .val = 0x1b }, +- { .rfmax = 506000, .val = 0x1c }, +- { .rfmax = 511000, .val = 0x1d }, +- { .rfmax = 516000, .val = 0x1e }, +- { .rfmax = 520000, .val = 0x1f }, +- { .rfmax = 521000, .val = 0x20 }, +- { .rfmax = 525000, .val = 0x21 }, +- { .rfmax = 529000, .val = 0x22 }, +- { .rfmax = 533000, .val = 0x23 }, +- { .rfmax = 539000, .val = 0x24 }, +- { .rfmax = 541000, .val = 0x25 }, +- { .rfmax = 547000, .val = 0x26 }, +- { .rfmax = 549000, .val = 0x27 }, +- { .rfmax = 551000, .val = 0x28 }, +- { .rfmax = 556000, .val = 0x29 }, +- { .rfmax = 561000, .val = 0x2a }, +- { .rfmax = 563000, .val = 0x2b }, +- { .rfmax = 565000, .val = 0x2c }, +- { .rfmax = 569000, .val = 0x2d }, +- { .rfmax = 571000, .val = 0x2e }, +- { .rfmax = 577000, .val = 0x2f }, +- { .rfmax = 580000, .val = 0x30 }, +- { .rfmax = 582000, .val = 0x31 }, +- { .rfmax = 584000, .val = 0x32 }, +- { .rfmax = 588000, .val = 0x33 }, +- { .rfmax = 591000, .val = 0x34 }, +- { .rfmax = 596000, .val = 0x35 }, +- { .rfmax = 598000, .val = 0x36 }, +- { .rfmax = 603000, .val = 0x37 }, +- { .rfmax = 604000, .val = 0x38 }, +- { .rfmax = 606000, .val = 0x39 }, +- { .rfmax = 612000, .val = 0x3a }, +- { .rfmax = 615000, .val = 0x3b }, +- { .rfmax = 617000, .val = 0x3c }, +- { .rfmax = 621000, .val = 0x3d }, +- { .rfmax = 622000, .val = 0x3e }, +- { .rfmax = 625000, .val = 0x3f }, +- { .rfmax = 632000, .val = 0x40 }, +- { .rfmax = 633000, .val = 0x41 }, +- { .rfmax = 634000, .val = 0x42 }, +- { .rfmax = 642000, .val = 0x43 }, +- { .rfmax = 643000, .val = 0x44 }, +- { .rfmax = 647000, .val = 0x45 }, +- { .rfmax = 650000, .val = 0x46 }, +- { .rfmax = 652000, .val = 0x47 }, +- { .rfmax = 657000, .val = 0x48 }, +- { .rfmax = 661000, .val = 0x49 }, +- { .rfmax = 662000, .val = 0x4a }, +- { .rfmax = 665000, .val = 0x4b }, +- { .rfmax = 667000, .val = 0x4c }, +- { .rfmax = 670000, .val = 0x4d }, +- { .rfmax = 673000, .val = 0x4e }, +- { .rfmax = 676000, .val = 0x4f }, +- { .rfmax = 677000, .val = 0x50 }, +- { .rfmax = 681000, .val = 0x51 }, +- { .rfmax = 683000, .val = 0x52 }, +- { .rfmax = 686000, .val = 0x53 }, +- { .rfmax = 688000, .val = 0x54 }, +- { .rfmax = 689000, .val = 0x55 }, +- { .rfmax = 691000, .val = 0x56 }, +- { .rfmax = 695000, .val = 0x57 }, +- { .rfmax = 698000, .val = 0x58 }, +- { .rfmax = 703000, .val = 0x59 }, +- { .rfmax = 704000, .val = 0x5a }, +- { .rfmax = 705000, .val = 0x5b }, +- { .rfmax = 707000, .val = 0x5c }, +- { .rfmax = 710000, .val = 0x5d }, +- { .rfmax = 712000, .val = 0x5e }, +- { .rfmax = 717000, .val = 0x5f }, +- { .rfmax = 718000, .val = 0x60 }, +- { .rfmax = 721000, .val = 0x61 }, +- { .rfmax = 722000, .val = 0x62 }, +- { .rfmax = 723000, .val = 0x63 }, +- { .rfmax = 725000, .val = 0x64 }, +- { .rfmax = 727000, .val = 0x65 }, +- { .rfmax = 730000, .val = 0x66 }, +- { .rfmax = 732000, .val = 0x67 }, +- { .rfmax = 735000, .val = 0x68 }, +- { .rfmax = 740000, .val = 0x69 }, +- { .rfmax = 741000, .val = 0x6a }, +- { .rfmax = 742000, .val = 0x6b }, +- { .rfmax = 743000, .val = 0x6c }, +- { .rfmax = 745000, .val = 0x6d }, +- { .rfmax = 747000, .val = 0x6e }, +- { .rfmax = 748000, .val = 0x6f }, +- { .rfmax = 750000, .val = 0x70 }, +- { .rfmax = 752000, .val = 0x71 }, +- { .rfmax = 754000, .val = 0x72 }, +- { .rfmax = 757000, .val = 0x73 }, +- { .rfmax = 758000, .val = 0x74 }, +- { .rfmax = 760000, .val = 0x75 }, +- { .rfmax = 763000, .val = 0x76 }, +- { .rfmax = 764000, .val = 0x77 }, +- { .rfmax = 766000, .val = 0x78 }, +- { .rfmax = 767000, .val = 0x79 }, +- { .rfmax = 768000, .val = 0x7a }, +- { .rfmax = 773000, .val = 0x7b }, +- { .rfmax = 774000, .val = 0x7c }, +- { .rfmax = 776000, .val = 0x7d }, +- { .rfmax = 777000, .val = 0x7e }, +- { .rfmax = 778000, .val = 0x7f }, +- { .rfmax = 779000, .val = 0x80 }, +- { .rfmax = 781000, .val = 0x81 }, +- { .rfmax = 783000, .val = 0x82 }, +- { .rfmax = 784000, .val = 0x83 }, +- { .rfmax = 785000, .val = 0x84 }, +- { .rfmax = 786000, .val = 0x85 }, +- { .rfmax = 793000, .val = 0x86 }, +- { .rfmax = 794000, .val = 0x87 }, +- { .rfmax = 795000, .val = 0x88 }, +- { .rfmax = 797000, .val = 0x89 }, +- { .rfmax = 799000, .val = 0x8a }, +- { .rfmax = 801000, .val = 0x8b }, +- { .rfmax = 802000, .val = 0x8c }, +- { .rfmax = 803000, .val = 0x8d }, +- { .rfmax = 804000, .val = 0x8e }, +- { .rfmax = 810000, .val = 0x90 }, +- { .rfmax = 811000, .val = 0x91 }, +- { .rfmax = 812000, .val = 0x92 }, +- { .rfmax = 814000, .val = 0x93 }, +- { .rfmax = 816000, .val = 0x94 }, +- { .rfmax = 817000, .val = 0x96 }, +- { .rfmax = 818000, .val = 0x97 }, +- { .rfmax = 820000, .val = 0x98 }, +- { .rfmax = 821000, .val = 0x99 }, +- { .rfmax = 822000, .val = 0x9a }, +- { .rfmax = 828000, .val = 0x9b }, +- { .rfmax = 829000, .val = 0x9d }, +- { .rfmax = 830000, .val = 0x9f }, +- { .rfmax = 831000, .val = 0xa0 }, +- { .rfmax = 833000, .val = 0xa1 }, +- { .rfmax = 835000, .val = 0xa2 }, +- { .rfmax = 836000, .val = 0xa3 }, +- { .rfmax = 837000, .val = 0xa4 }, +- { .rfmax = 838000, .val = 0xa6 }, +- { .rfmax = 840000, .val = 0xa8 }, +- { .rfmax = 842000, .val = 0xa9 }, +- { .rfmax = 845000, .val = 0xaa }, +- { .rfmax = 846000, .val = 0xab }, +- { .rfmax = 847000, .val = 0xad }, +- { .rfmax = 848000, .val = 0xae }, +- { .rfmax = 852000, .val = 0xaf }, +- { .rfmax = 853000, .val = 0xb0 }, +- { .rfmax = 858000, .val = 0xb1 }, +- { .rfmax = 860000, .val = 0xb2 }, +- { .rfmax = 861000, .val = 0xb3 }, +- { .rfmax = 862000, .val = 0xb4 }, +- { .rfmax = 863000, .val = 0xb6 }, +- { .rfmax = 864000, .val = 0xb8 }, +- { .rfmax = 865000, .val = 0xb9 }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-static struct tda18271_map tda18271_ir_measure[] = { +- { .rfmax = 30000, .val = 4 }, +- { .rfmax = 200000, .val = 5 }, +- { .rfmax = 600000, .val = 6 }, +- { .rfmax = 865000, .val = 7 }, +- { .rfmax = 0, .val = 0 }, /* end */ +-}; +- +-static struct tda18271_map tda18271_rf_cal_dc_over_dt[] = { +- { .rfmax = 47900, .val = 0x00 }, +- { .rfmax = 55000, .val = 0x00 }, +- { .rfmax = 61100, .val = 0x0a }, +- { .rfmax = 64000, .val = 0x0a }, +- { .rfmax = 82000, .val = 0x14 }, +- { .rfmax = 84000, .val = 0x19 }, +- { .rfmax = 119000, .val = 0x1c }, +- { .rfmax = 124000, .val = 0x20 }, +- { .rfmax = 129000, .val = 0x2a }, +- { .rfmax = 134000, .val = 0x32 }, +- { .rfmax = 139000, .val = 0x39 }, +- { .rfmax = 144000, .val = 0x3e }, +- { .rfmax = 149000, .val = 0x3f }, +- { .rfmax = 152600, .val = 0x40 }, +- { .rfmax = 154000, .val = 0x40 }, +- { .rfmax = 164700, .val = 0x41 }, +- { .rfmax = 203500, .val = 0x32 }, +- { .rfmax = 353000, .val = 0x19 }, +- { .rfmax = 356000, .val = 0x1a }, +- { .rfmax = 359000, .val = 0x1b }, +- { .rfmax = 363000, .val = 0x1c }, +- { .rfmax = 366000, .val = 0x1d }, +- { .rfmax = 369000, .val = 0x1e }, +- { .rfmax = 373000, .val = 0x1f }, +- { .rfmax = 376000, .val = 0x20 }, +- { .rfmax = 379000, .val = 0x21 }, +- { .rfmax = 383000, .val = 0x22 }, +- { .rfmax = 386000, .val = 0x23 }, +- { .rfmax = 389000, .val = 0x24 }, +- { .rfmax = 393000, .val = 0x25 }, +- { .rfmax = 396000, .val = 0x26 }, +- { .rfmax = 399000, .val = 0x27 }, +- { .rfmax = 402000, .val = 0x28 }, +- { .rfmax = 404000, .val = 0x29 }, +- { .rfmax = 407000, .val = 0x2a }, +- { .rfmax = 409000, .val = 0x2b }, +- { .rfmax = 412000, .val = 0x2c }, +- { .rfmax = 414000, .val = 0x2d }, +- { .rfmax = 417000, .val = 0x2e }, +- { .rfmax = 419000, .val = 0x2f }, +- { .rfmax = 422000, .val = 0x30 }, +- { .rfmax = 424000, .val = 0x31 }, +- { .rfmax = 427000, .val = 0x32 }, +- { .rfmax = 429000, .val = 0x33 }, +- { .rfmax = 432000, .val = 0x34 }, +- { .rfmax = 434000, .val = 0x35 }, +- { .rfmax = 437000, .val = 0x36 }, +- { .rfmax = 439000, .val = 0x37 }, +- { .rfmax = 442000, .val = 0x38 }, +- { .rfmax = 444000, .val = 0x39 }, +- { .rfmax = 447000, .val = 0x3a }, +- { .rfmax = 449000, .val = 0x3b }, +- { .rfmax = 457800, .val = 0x3c }, +- { .rfmax = 465000, .val = 0x0f }, +- { .rfmax = 477000, .val = 0x12 }, +- { .rfmax = 483000, .val = 0x14 }, +- { .rfmax = 502000, .val = 0x19 }, +- { .rfmax = 508000, .val = 0x1b }, +- { .rfmax = 519000, .val = 0x1c }, +- { .rfmax = 522000, .val = 0x1d }, +- { .rfmax = 524000, .val = 0x1e }, +- { .rfmax = 534000, .val = 0x1f }, +- { .rfmax = 549000, .val = 0x20 }, +- { .rfmax = 554000, .val = 0x22 }, +- { .rfmax = 584000, .val = 0x24 }, +- { .rfmax = 589000, .val = 0x26 }, +- { .rfmax = 658000, .val = 0x27 }, +- { .rfmax = 664000, .val = 0x2c }, +- { .rfmax = 669000, .val = 0x2d }, +- { .rfmax = 699000, .val = 0x2e }, +- { .rfmax = 704000, .val = 0x30 }, +- { .rfmax = 709000, .val = 0x31 }, +- { .rfmax = 714000, .val = 0x32 }, +- { .rfmax = 724000, .val = 0x33 }, +- { .rfmax = 729000, .val = 0x36 }, +- { .rfmax = 739000, .val = 0x38 }, +- { .rfmax = 744000, .val = 0x39 }, +- { .rfmax = 749000, .val = 0x3b }, +- { .rfmax = 754000, .val = 0x3c }, +- { .rfmax = 759000, .val = 0x3d }, +- { .rfmax = 764000, .val = 0x3e }, +- { .rfmax = 769000, .val = 0x3f }, +- { .rfmax = 774000, .val = 0x40 }, +- { .rfmax = 779000, .val = 0x41 }, +- { .rfmax = 784000, .val = 0x43 }, +- { .rfmax = 789000, .val = 0x46 }, +- { .rfmax = 794000, .val = 0x48 }, +- { .rfmax = 799000, .val = 0x4b }, +- { .rfmax = 804000, .val = 0x4f }, +- { .rfmax = 809000, .val = 0x54 }, +- { .rfmax = 814000, .val = 0x59 }, +- { .rfmax = 819000, .val = 0x5d }, +- { .rfmax = 824000, .val = 0x61 }, +- { .rfmax = 829000, .val = 0x68 }, +- { .rfmax = 834000, .val = 0x6e }, +- { .rfmax = 839000, .val = 0x75 }, +- { .rfmax = 844000, .val = 0x7e }, +- { .rfmax = 849000, .val = 0x82 }, +- { .rfmax = 854000, .val = 0x84 }, +- { .rfmax = 859000, .val = 0x8f }, +- { .rfmax = 865000, .val = 0x9a }, +- { .rfmax = 0, .val = 0x00 }, /* end */ +-}; +- +-/*---------------------------------------------------------------------*/ +- +-struct tda18271_thermo_map { +- u8 d; +- u8 r0; +- u8 r1; +-}; +- +-static struct tda18271_thermo_map tda18271_thermometer[] = { +- { .d = 0x00, .r0 = 60, .r1 = 92 }, +- { .d = 0x01, .r0 = 62, .r1 = 94 }, +- { .d = 0x02, .r0 = 66, .r1 = 98 }, +- { .d = 0x03, .r0 = 64, .r1 = 96 }, +- { .d = 0x04, .r0 = 74, .r1 = 106 }, +- { .d = 0x05, .r0 = 72, .r1 = 104 }, +- { .d = 0x06, .r0 = 68, .r1 = 100 }, +- { .d = 0x07, .r0 = 70, .r1 = 102 }, +- { .d = 0x08, .r0 = 90, .r1 = 122 }, +- { .d = 0x09, .r0 = 88, .r1 = 120 }, +- { .d = 0x0a, .r0 = 84, .r1 = 116 }, +- { .d = 0x0b, .r0 = 86, .r1 = 118 }, +- { .d = 0x0c, .r0 = 76, .r1 = 108 }, +- { .d = 0x0d, .r0 = 78, .r1 = 110 }, +- { .d = 0x0e, .r0 = 82, .r1 = 114 }, +- { .d = 0x0f, .r0 = 80, .r1 = 112 }, +- { .d = 0x00, .r0 = 0, .r1 = 0 }, /* end */ +-}; +- +-int tda18271_lookup_thermometer(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- unsigned char *regs = priv->tda18271_regs; +- int val, i = 0; +- +- while (tda18271_thermometer[i].d < (regs[R_TM] & 0x0f)) { +- if (tda18271_thermometer[i + 1].d == 0) +- break; +- i++; +- } +- +- if ((regs[R_TM] & 0x20) == 0x20) +- val = tda18271_thermometer[i].r1; +- else +- val = tda18271_thermometer[i].r0; +- +- tda_map("(%d) tm = %d\n", i, val); +- +- return val; +-} +- +-/*---------------------------------------------------------------------*/ +- +-struct tda18271_cid_target_map { +- u32 rfmax; +- u8 target; +- u16 limit; +-}; +- +-static struct tda18271_cid_target_map tda18271_cid_target[] = { +- { .rfmax = 46000, .target = 0x04, .limit = 1800 }, +- { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, +- { .rfmax = 70100, .target = 0x01, .limit = 4000 }, +- { .rfmax = 136800, .target = 0x18, .limit = 4000 }, +- { .rfmax = 156700, .target = 0x18, .limit = 4000 }, +- { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, +- { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, +- { .rfmax = 345000, .target = 0x18, .limit = 4000 }, +- { .rfmax = 426000, .target = 0x0e, .limit = 4000 }, +- { .rfmax = 489500, .target = 0x1e, .limit = 4000 }, +- { .rfmax = 697500, .target = 0x32, .limit = 4000 }, +- { .rfmax = 842000, .target = 0x3a, .limit = 4000 }, +- { .rfmax = 0, .target = 0x00, .limit = 0 }, /* end */ +-}; +- +-int tda18271_lookup_cid_target(struct dvb_frontend *fe, +- u32 *freq, u8 *cid_target, u16 *count_limit) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int i = 0; +- +- while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { +- if (tda18271_cid_target[i + 1].rfmax == 0) +- break; +- i++; +- } +- *cid_target = tda18271_cid_target[i].target; +- *count_limit = tda18271_cid_target[i].limit; +- +- tda_map("(%d) cid_target = %02x, count_limit = %d\n", i, +- tda18271_cid_target[i].target, tda18271_cid_target[i].limit); +- +- return 0; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static struct tda18271_rf_tracking_filter_cal tda18271_rf_band_template[] = { +- { .rfmax = 47900, .rfband = 0x00, +- .rf1_def = 46000, .rf2_def = 0, .rf3_def = 0 }, +- { .rfmax = 61100, .rfband = 0x01, +- .rf1_def = 52200, .rf2_def = 0, .rf3_def = 0 }, +- { .rfmax = 152600, .rfband = 0x02, +- .rf1_def = 70100, .rf2_def = 136800, .rf3_def = 0 }, +- { .rfmax = 164700, .rfband = 0x03, +- .rf1_def = 156700, .rf2_def = 0, .rf3_def = 0 }, +- { .rfmax = 203500, .rfband = 0x04, +- .rf1_def = 186250, .rf2_def = 0, .rf3_def = 0 }, +- { .rfmax = 457800, .rfband = 0x05, +- .rf1_def = 230000, .rf2_def = 345000, .rf3_def = 426000 }, +- { .rfmax = 865000, .rfband = 0x06, +- .rf1_def = 489500, .rf2_def = 697500, .rf3_def = 842000 }, +- { .rfmax = 0, .rfband = 0x00, +- .rf1_def = 0, .rf2_def = 0, .rf3_def = 0 }, /* end */ +-}; +- +-int tda18271_lookup_rf_band(struct dvb_frontend *fe, u32 *freq, u8 *rf_band) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; +- int i = 0; +- +- while ((map[i].rfmax * 1000) < *freq) { +- if (tda18271_debug & DBG_ADV) +- tda_map("(%d) rfmax = %d < freq = %d, " +- "rf1_def = %d, rf2_def = %d, rf3_def = %d, " +- "rf1 = %d, rf2 = %d, rf3 = %d, " +- "rf_a1 = %d, rf_a2 = %d, " +- "rf_b1 = %d, rf_b2 = %d\n", +- i, map[i].rfmax * 1000, *freq, +- map[i].rf1_def, map[i].rf2_def, map[i].rf3_def, +- map[i].rf1, map[i].rf2, map[i].rf3, +- map[i].rf_a1, map[i].rf_a2, +- map[i].rf_b1, map[i].rf_b2); +- if (map[i].rfmax == 0) +- return -EINVAL; +- i++; +- } +- if (rf_band) +- *rf_band = map[i].rfband; +- +- tda_map("(%d) rf_band = %02x\n", i, map[i].rfband); +- +- return i; +-} +- +-/*---------------------------------------------------------------------*/ +- +-struct tda18271_map_layout { +- struct tda18271_pll_map *main_pll; +- struct tda18271_pll_map *cal_pll; +- +- struct tda18271_map *rf_cal; +- struct tda18271_map *rf_cal_kmco; +- struct tda18271_map *rf_cal_dc_over_dt; +- +- struct tda18271_map *bp_filter; +- struct tda18271_map *rf_band; +- struct tda18271_map *gain_taper; +- struct tda18271_map *ir_measure; +-}; +- +-/*---------------------------------------------------------------------*/ +- +-int tda18271_lookup_pll_map(struct dvb_frontend *fe, +- enum tda18271_map_type map_type, +- u32 *freq, u8 *post_div, u8 *div) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_pll_map *map = NULL; +- unsigned int i = 0; +- char *map_name; +- int ret = 0; +- +- BUG_ON(!priv->maps); +- +- switch (map_type) { +- case MAIN_PLL: +- map = priv->maps->main_pll; +- map_name = "main_pll"; +- break; +- case CAL_PLL: +- map = priv->maps->cal_pll; +- map_name = "cal_pll"; +- break; +- default: +- /* we should never get here */ +- map_name = "undefined"; +- break; +- } +- +- if (!map) { +- tda_warn("%s map is not set!\n", map_name); +- ret = -EINVAL; +- goto fail; +- } +- +- while ((map[i].lomax * 1000) < *freq) { +- if (map[i + 1].lomax == 0) { +- tda_map("%s: frequency (%d) out of range\n", +- map_name, *freq); +- ret = -ERANGE; +- break; +- } +- i++; +- } +- *post_div = map[i].pd; +- *div = map[i].d; +- +- tda_map("(%d) %s: post div = 0x%02x, div = 0x%02x\n", +- i, map_name, *post_div, *div); +-fail: +- return ret; +-} +- +-int tda18271_lookup_map(struct dvb_frontend *fe, +- enum tda18271_map_type map_type, +- u32 *freq, u8 *val) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- struct tda18271_map *map = NULL; +- unsigned int i = 0; +- char *map_name; +- int ret = 0; +- +- BUG_ON(!priv->maps); +- +- switch (map_type) { +- case BP_FILTER: +- map = priv->maps->bp_filter; +- map_name = "bp_filter"; +- break; +- case RF_CAL_KMCO: +- map = priv->maps->rf_cal_kmco; +- map_name = "km"; +- break; +- case RF_BAND: +- map = priv->maps->rf_band; +- map_name = "rf_band"; +- break; +- case GAIN_TAPER: +- map = priv->maps->gain_taper; +- map_name = "gain_taper"; +- break; +- case RF_CAL: +- map = priv->maps->rf_cal; +- map_name = "rf_cal"; +- break; +- case IR_MEASURE: +- map = priv->maps->ir_measure; +- map_name = "ir_measure"; +- break; +- case RF_CAL_DC_OVER_DT: +- map = priv->maps->rf_cal_dc_over_dt; +- map_name = "rf_cal_dc_over_dt"; +- break; +- default: +- /* we should never get here */ +- map_name = "undefined"; +- break; +- } +- +- if (!map) { +- tda_warn("%s map is not set!\n", map_name); +- ret = -EINVAL; +- goto fail; +- } +- +- while ((map[i].rfmax * 1000) < *freq) { +- if (map[i + 1].rfmax == 0) { +- tda_map("%s: frequency (%d) out of range\n", +- map_name, *freq); +- ret = -ERANGE; +- break; +- } +- i++; +- } +- *val = map[i].val; +- +- tda_map("(%d) %s: 0x%02x\n", i, map_name, *val); +-fail: +- return ret; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static struct tda18271_std_map tda18271c1_std_map = { +- .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ +- .atv_b = { .if_freq = 6750, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_dk = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ +- .atv_gh = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ +- .atv_i = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ +- .atv_l = { .if_freq = 7750, .fm_rfn = 0, .agc_mode = 1, .std = 7, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ +- .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 7, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0f */ +- .atv_mn = { .if_freq = 5750, .fm_rfn = 0, .agc_mode = 1, .std = 5, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ +- .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ +- .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ +- .dvbt_7 = { .if_freq = 3800, .fm_rfn = 0, .agc_mode = 3, .std = 5, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ +- .dvbt_8 = { .if_freq = 4300, .fm_rfn = 0, .agc_mode = 3, .std = 6, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ +- .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ +- .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ +- .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ +-}; +- +-static struct tda18271_std_map tda18271c2_std_map = { +- .fm_radio = { .if_freq = 1250, .fm_rfn = 1, .agc_mode = 3, .std = 0, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x18 */ +- .atv_b = { .if_freq = 6000, .fm_rfn = 0, .agc_mode = 1, .std = 5, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0d */ +- .atv_dk = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_gh = { .if_freq = 7100, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_i = { .if_freq = 7250, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_l = { .if_freq = 6900, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_lc = { .if_freq = 1250, .fm_rfn = 0, .agc_mode = 1, .std = 6, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0e */ +- .atv_mn = { .if_freq = 5400, .fm_rfn = 0, .agc_mode = 1, .std = 4, +- .if_lvl = 0, .rfagc_top = 0x2c, }, /* EP3[4:0] 0x0c */ +- .atsc_6 = { .if_freq = 3250, .fm_rfn = 0, .agc_mode = 3, .std = 4, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ +- .dvbt_6 = { .if_freq = 3300, .fm_rfn = 0, .agc_mode = 3, .std = 4, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ +- .dvbt_7 = { .if_freq = 3500, .fm_rfn = 0, .agc_mode = 3, .std = 4, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1c */ +- .dvbt_8 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ +- .qam_6 = { .if_freq = 4000, .fm_rfn = 0, .agc_mode = 3, .std = 5, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1d */ +- .qam_7 = { .if_freq = 4500, .fm_rfn = 0, .agc_mode = 3, .std = 6, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1e */ +- .qam_8 = { .if_freq = 5000, .fm_rfn = 0, .agc_mode = 3, .std = 7, +- .if_lvl = 1, .rfagc_top = 0x37, }, /* EP3[4:0] 0x1f */ +-}; +- +-/*---------------------------------------------------------------------*/ +- +-static struct tda18271_map_layout tda18271c1_map_layout = { +- .main_pll = tda18271c1_main_pll, +- .cal_pll = tda18271c1_cal_pll, +- +- .rf_cal = tda18271c1_rf_cal, +- .rf_cal_kmco = tda18271c1_km, +- +- .bp_filter = tda18271_bp_filter, +- .rf_band = tda18271_rf_band, +- .gain_taper = tda18271_gain_taper, +- .ir_measure = tda18271_ir_measure, +-}; +- +-static struct tda18271_map_layout tda18271c2_map_layout = { +- .main_pll = tda18271c2_main_pll, +- .cal_pll = tda18271c2_cal_pll, +- +- .rf_cal = tda18271c2_rf_cal, +- .rf_cal_kmco = tda18271c2_km, +- +- .rf_cal_dc_over_dt = tda18271_rf_cal_dc_over_dt, +- +- .bp_filter = tda18271_bp_filter, +- .rf_band = tda18271_rf_band, +- .gain_taper = tda18271_gain_taper, +- .ir_measure = tda18271_ir_measure, +-}; +- +-int tda18271_assign_map_layout(struct dvb_frontend *fe) +-{ +- struct tda18271_priv *priv = fe->tuner_priv; +- int ret = 0; +- +- switch (priv->id) { +- case TDA18271HDC1: +- priv->maps = &tda18271c1_map_layout; +- memcpy(&priv->std, &tda18271c1_std_map, +- sizeof(struct tda18271_std_map)); +- break; +- case TDA18271HDC2: +- priv->maps = &tda18271c2_map_layout; +- memcpy(&priv->std, &tda18271c2_std_map, +- sizeof(struct tda18271_std_map)); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- memcpy(priv->rf_cal_state, &tda18271_rf_band_template, +- sizeof(tda18271_rf_band_template)); +- +- return ret; +-} +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h +deleted file mode 100644 +index 454c152..0000000 +--- a/drivers/media/common/tuners/tda18271-priv.h ++++ /dev/null +@@ -1,236 +0,0 @@ +-/* +- tda18271-priv.h - private header for the NXP TDA18271 silicon tuner +- +- Copyright (C) 2007, 2008 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA18271_PRIV_H__ +-#define __TDA18271_PRIV_H__ +- +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tda18271.h" +- +-#define R_ID 0x00 /* ID byte */ +-#define R_TM 0x01 /* Thermo byte */ +-#define R_PL 0x02 /* Power level byte */ +-#define R_EP1 0x03 /* Easy Prog byte 1 */ +-#define R_EP2 0x04 /* Easy Prog byte 2 */ +-#define R_EP3 0x05 /* Easy Prog byte 3 */ +-#define R_EP4 0x06 /* Easy Prog byte 4 */ +-#define R_EP5 0x07 /* Easy Prog byte 5 */ +-#define R_CPD 0x08 /* Cal Post-Divider byte */ +-#define R_CD1 0x09 /* Cal Divider byte 1 */ +-#define R_CD2 0x0a /* Cal Divider byte 2 */ +-#define R_CD3 0x0b /* Cal Divider byte 3 */ +-#define R_MPD 0x0c /* Main Post-Divider byte */ +-#define R_MD1 0x0d /* Main Divider byte 1 */ +-#define R_MD2 0x0e /* Main Divider byte 2 */ +-#define R_MD3 0x0f /* Main Divider byte 3 */ +-#define R_EB1 0x10 /* Extended byte 1 */ +-#define R_EB2 0x11 /* Extended byte 2 */ +-#define R_EB3 0x12 /* Extended byte 3 */ +-#define R_EB4 0x13 /* Extended byte 4 */ +-#define R_EB5 0x14 /* Extended byte 5 */ +-#define R_EB6 0x15 /* Extended byte 6 */ +-#define R_EB7 0x16 /* Extended byte 7 */ +-#define R_EB8 0x17 /* Extended byte 8 */ +-#define R_EB9 0x18 /* Extended byte 9 */ +-#define R_EB10 0x19 /* Extended byte 10 */ +-#define R_EB11 0x1a /* Extended byte 11 */ +-#define R_EB12 0x1b /* Extended byte 12 */ +-#define R_EB13 0x1c /* Extended byte 13 */ +-#define R_EB14 0x1d /* Extended byte 14 */ +-#define R_EB15 0x1e /* Extended byte 15 */ +-#define R_EB16 0x1f /* Extended byte 16 */ +-#define R_EB17 0x20 /* Extended byte 17 */ +-#define R_EB18 0x21 /* Extended byte 18 */ +-#define R_EB19 0x22 /* Extended byte 19 */ +-#define R_EB20 0x23 /* Extended byte 20 */ +-#define R_EB21 0x24 /* Extended byte 21 */ +-#define R_EB22 0x25 /* Extended byte 22 */ +-#define R_EB23 0x26 /* Extended byte 23 */ +- +-#define TDA18271_NUM_REGS 39 +- +-/*---------------------------------------------------------------------*/ +- +-struct tda18271_rf_tracking_filter_cal { +- u32 rfmax; +- u8 rfband; +- u32 rf1_def; +- u32 rf2_def; +- u32 rf3_def; +- u32 rf1; +- u32 rf2; +- u32 rf3; +- s32 rf_a1; +- s32 rf_b1; +- s32 rf_a2; +- s32 rf_b2; +-}; +- +-enum tda18271_pll { +- TDA18271_MAIN_PLL, +- TDA18271_CAL_PLL, +-}; +- +-struct tda18271_map_layout; +- +-enum tda18271_ver { +- TDA18271HDC1, +- TDA18271HDC2, +-}; +- +-struct tda18271_priv { +- unsigned char tda18271_regs[TDA18271_NUM_REGS]; +- +- struct list_head hybrid_tuner_instance_list; +- struct tuner_i2c_props i2c_props; +- +- enum tda18271_mode mode; +- enum tda18271_role role; +- enum tda18271_i2c_gate gate; +- enum tda18271_ver id; +- enum tda18271_output_options output_opt; +- enum tda18271_small_i2c small_i2c; +- +- unsigned int config; /* interface to saa713x / tda829x */ +- unsigned int cal_initialized:1; +- +- u8 tm_rfcal; +- +- struct tda18271_map_layout *maps; +- struct tda18271_std_map std; +- struct tda18271_rf_tracking_filter_cal rf_cal_state[8]; +- +- struct mutex lock; +- +- u16 if_freq; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-/*---------------------------------------------------------------------*/ +- +-extern int tda18271_debug; +- +-#define DBG_INFO 1 +-#define DBG_MAP 2 +-#define DBG_REG 4 +-#define DBG_ADV 8 +-#define DBG_CAL 16 +- +-__attribute__((format(printf, 4, 5))) +-int _tda_printk(struct tda18271_priv *state, const char *level, +- const char *func, const char *fmt, ...); +- +-#define tda_printk(st, lvl, fmt, arg...) \ +- _tda_printk(st, lvl, __func__, fmt, ##arg) +- +-#define tda_dprintk(st, lvl, fmt, arg...) \ +-do { \ +- if (tda18271_debug & lvl) \ +- tda_printk(st, KERN_DEBUG, fmt, ##arg); \ +-} while (0) +- +-#define tda_info(fmt, arg...) pr_info(fmt, ##arg) +-#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +-#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +-#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +-#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +-#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +-#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) +- +-#define tda_fail(ret) \ +-({ \ +- int __ret; \ +- __ret = (ret < 0); \ +- if (__ret) \ +- tda_printk(priv, KERN_ERR, \ +- "error %d on line %d\n", ret, __LINE__); \ +- __ret; \ +-}) +- +-/*---------------------------------------------------------------------*/ +- +-enum tda18271_map_type { +- /* tda18271_pll_map */ +- MAIN_PLL, +- CAL_PLL, +- /* tda18271_map */ +- RF_CAL, +- RF_CAL_KMCO, +- RF_CAL_DC_OVER_DT, +- BP_FILTER, +- RF_BAND, +- GAIN_TAPER, +- IR_MEASURE, +-}; +- +-extern int tda18271_lookup_pll_map(struct dvb_frontend *fe, +- enum tda18271_map_type map_type, +- u32 *freq, u8 *post_div, u8 *div); +-extern int tda18271_lookup_map(struct dvb_frontend *fe, +- enum tda18271_map_type map_type, +- u32 *freq, u8 *val); +- +-extern int tda18271_lookup_thermometer(struct dvb_frontend *fe); +- +-extern int tda18271_lookup_rf_band(struct dvb_frontend *fe, +- u32 *freq, u8 *rf_band); +- +-extern int tda18271_lookup_cid_target(struct dvb_frontend *fe, +- u32 *freq, u8 *cid_target, +- u16 *count_limit); +- +-extern int tda18271_assign_map_layout(struct dvb_frontend *fe); +- +-/*---------------------------------------------------------------------*/ +- +-extern int tda18271_read_regs(struct dvb_frontend *fe); +-extern int tda18271_read_extended(struct dvb_frontend *fe); +-extern int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len); +-extern int tda18271_init_regs(struct dvb_frontend *fe); +- +-extern int tda18271_charge_pump_source(struct dvb_frontend *fe, +- enum tda18271_pll pll, int force); +-extern int tda18271_set_standby_mode(struct dvb_frontend *fe, +- int sm, int sm_lt, int sm_xt); +- +-extern int tda18271_calc_main_pll(struct dvb_frontend *fe, u32 freq); +-extern int tda18271_calc_cal_pll(struct dvb_frontend *fe, u32 freq); +- +-extern int tda18271_calc_bp_filter(struct dvb_frontend *fe, u32 *freq); +-extern int tda18271_calc_km(struct dvb_frontend *fe, u32 *freq); +-extern int tda18271_calc_rf_band(struct dvb_frontend *fe, u32 *freq); +-extern int tda18271_calc_gain_taper(struct dvb_frontend *fe, u32 *freq); +-extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); +-extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); +- +-#endif /* __TDA18271_PRIV_H__ */ +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h +deleted file mode 100644 +index 640bae4..0000000 +--- a/drivers/media/common/tuners/tda18271.h ++++ /dev/null +@@ -1,134 +0,0 @@ +-/* +- tda18271.h - header for the Philips / NXP TDA18271 silicon tuner +- +- Copyright (C) 2007, 2008 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA18271_H__ +-#define __TDA18271_H__ +- +-#include +-#include "dvb_frontend.h" +- +-struct tda18271_std_map_item { +- u16 if_freq; +- +- /* EP3[4:3] */ +- unsigned int agc_mode:2; +- /* EP3[2:0] */ +- unsigned int std:3; +- /* EP4[7] */ +- unsigned int fm_rfn:1; +- /* EP4[4:2] */ +- unsigned int if_lvl:3; +- /* EB22[6:0] */ +- unsigned int rfagc_top:7; +-}; +- +-struct tda18271_std_map { +- struct tda18271_std_map_item fm_radio; +- struct tda18271_std_map_item atv_b; +- struct tda18271_std_map_item atv_dk; +- struct tda18271_std_map_item atv_gh; +- struct tda18271_std_map_item atv_i; +- struct tda18271_std_map_item atv_l; +- struct tda18271_std_map_item atv_lc; +- struct tda18271_std_map_item atv_mn; +- struct tda18271_std_map_item atsc_6; +- struct tda18271_std_map_item dvbt_6; +- struct tda18271_std_map_item dvbt_7; +- struct tda18271_std_map_item dvbt_8; +- struct tda18271_std_map_item qam_6; +- struct tda18271_std_map_item qam_7; +- struct tda18271_std_map_item qam_8; +-}; +- +-enum tda18271_role { +- TDA18271_MASTER = 0, +- TDA18271_SLAVE, +-}; +- +-enum tda18271_i2c_gate { +- TDA18271_GATE_AUTO = 0, +- TDA18271_GATE_ANALOG, +- TDA18271_GATE_DIGITAL, +-}; +- +-enum tda18271_output_options { +- /* slave tuner output & loop thru & xtal oscillator always on */ +- TDA18271_OUTPUT_LT_XT_ON = 0, +- +- /* slave tuner output loop thru off */ +- TDA18271_OUTPUT_LT_OFF = 1, +- +- /* xtal oscillator off */ +- TDA18271_OUTPUT_XT_OFF = 2, +-}; +- +-enum tda18271_small_i2c { +- TDA18271_39_BYTE_CHUNK_INIT = 0, +- TDA18271_16_BYTE_CHUNK_INIT = 16, +- TDA18271_08_BYTE_CHUNK_INIT = 8, +- TDA18271_03_BYTE_CHUNK_INIT = 3, +-}; +- +-struct tda18271_config { +- /* override default if freq / std settings (optional) */ +- struct tda18271_std_map *std_map; +- +- /* master / slave tuner: master uses main pll, slave uses cal pll */ +- enum tda18271_role role; +- +- /* use i2c gate provided by analog or digital demod */ +- enum tda18271_i2c_gate gate; +- +- /* output options that can be disabled */ +- enum tda18271_output_options output_opt; +- +- /* some i2c providers can't write all 39 registers at once */ +- enum tda18271_small_i2c small_i2c; +- +- /* force rf tracking filter calibration on startup */ +- unsigned int rf_cal_on_startup:1; +- +- /* interface to saa713x / tda829x */ +- unsigned int config; +-}; +- +-#define TDA18271_CALLBACK_CMD_AGC_ENABLE 0 +- +-enum tda18271_mode { +- TDA18271_ANALOG = 0, +- TDA18271_DIGITAL, +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, +- struct i2c_adapter *i2c, +- struct tda18271_config *cfg); +-#else +-static inline struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, +- u8 addr, +- struct i2c_adapter *i2c, +- struct tda18271_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TDA18271_H__ */ +diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c +deleted file mode 100644 +index a0d1762..0000000 +--- a/drivers/media/common/tuners/tda827x.c ++++ /dev/null +@@ -1,917 +0,0 @@ +-/* +- * +- * (c) 2005 Hartmut Hackmann +- * (c) 2007 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "tda827x.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "tda827x: " args); \ +- } while (0) +- +-struct tda827x_priv { +- int i2c_addr; +- struct i2c_adapter *i2c_adap; +- struct tda827x_config *cfg; +- +- unsigned int sgIF; +- unsigned char lpsel; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-static void tda827x_set_std(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- char *mode; +- +- priv->lpsel = 0; +- if (params->std & V4L2_STD_MN) { +- priv->sgIF = 92; +- priv->lpsel = 1; +- mode = "MN"; +- } else if (params->std & V4L2_STD_B) { +- priv->sgIF = 108; +- mode = "B"; +- } else if (params->std & V4L2_STD_GH) { +- priv->sgIF = 124; +- mode = "GH"; +- } else if (params->std & V4L2_STD_PAL_I) { +- priv->sgIF = 124; +- mode = "I"; +- } else if (params->std & V4L2_STD_DK) { +- priv->sgIF = 124; +- mode = "DK"; +- } else if (params->std & V4L2_STD_SECAM_L) { +- priv->sgIF = 124; +- mode = "L"; +- } else if (params->std & V4L2_STD_SECAM_LC) { +- priv->sgIF = 20; +- mode = "LC"; +- } else { +- priv->sgIF = 124; +- mode = "xx"; +- } +- +- if (params->mode == V4L2_TUNER_RADIO) { +- priv->sgIF = 88; /* if frequency is 5.5 MHz */ +- dprintk("setting tda827x to radio FM\n"); +- } else +- dprintk("setting tda827x to system %s\n", mode); +-} +- +- +-/* ------------------------------------------------------------------ */ +- +-struct tda827x_data { +- u32 lomax; +- u8 spd; +- u8 bs; +- u8 bp; +- u8 cp; +- u8 gc3; +- u8 div1p5; +-}; +- +-static const struct tda827x_data tda827x_table[] = { +- { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, +- { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, +- { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, +- { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, +- { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, +- { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, +- { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, +- { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} +-}; +- +-static int tuner_transfer(struct dvb_frontend *fe, +- struct i2c_msg *msg, +- const int size) +-{ +- int rc; +- struct tda827x_priv *priv = fe->tuner_priv; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- rc = i2c_transfer(priv->i2c_adap, msg, size); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (rc >= 0 && rc != size) +- return -EIO; +- +- return rc; +-} +- +-static int tda827xo_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct tda827x_priv *priv = fe->tuner_priv; +- u8 buf[14]; +- int rc; +- +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = buf, .len = sizeof(buf) }; +- int i, tuner_freq, if_freq; +- u32 N; +- +- dprintk("%s:\n", __func__); +- if (c->bandwidth_hz == 0) { +- if_freq = 5000000; +- } else if (c->bandwidth_hz <= 6000000) { +- if_freq = 4000000; +- } else if (c->bandwidth_hz <= 7000000) { +- if_freq = 4500000; +- } else { /* 8 MHz */ +- if_freq = 5000000; +- } +- tuner_freq = c->frequency; +- +- i = 0; +- while (tda827x_table[i].lomax < tuner_freq) { +- if (tda827x_table[i + 1].lomax == 0) +- break; +- i++; +- } +- +- tuner_freq += if_freq; +- +- N = ((tuner_freq + 125000) / 250000) << (tda827x_table[i].spd + 2); +- buf[0] = 0; +- buf[1] = (N>>8) | 0x40; +- buf[2] = N & 0xff; +- buf[3] = 0; +- buf[4] = 0x52; +- buf[5] = (tda827x_table[i].spd << 6) + (tda827x_table[i].div1p5 << 5) + +- (tda827x_table[i].bs << 3) + +- tda827x_table[i].bp; +- buf[6] = (tda827x_table[i].gc3 << 4) + 0x8f; +- buf[7] = 0xbf; +- buf[8] = 0x2a; +- buf[9] = 0x05; +- buf[10] = 0xff; +- buf[11] = 0x00; +- buf[12] = 0x00; +- buf[13] = 0x40; +- +- msg.len = 14; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- msleep(500); +- /* correct CP value */ +- buf[0] = 0x30; +- buf[1] = 0x50 + tda827x_table[i].cp; +- msg.len = 2; +- +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- priv->frequency = c->frequency; +- priv->bandwidth = c->bandwidth_hz; +- +- return 0; +- +-err: +- printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", +- __func__, priv->i2c_addr << 1); +- return rc; +-} +- +-static int tda827xo_sleep(struct dvb_frontend *fe) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- static u8 buf[] = { 0x30, 0xd0 }; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = buf, .len = sizeof(buf) }; +- +- dprintk("%s:\n", __func__); +- tuner_transfer(fe, &msg, 1); +- +- if (priv->cfg && priv->cfg->sleep) +- priv->cfg->sleep(fe); +- +- return 0; +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda827xo_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- unsigned char tuner_reg[8]; +- unsigned char reg2[2]; +- u32 N; +- int i; +- struct tda827x_priv *priv = fe->tuner_priv; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0 }; +- unsigned int freq = params->frequency; +- +- tda827x_set_std(fe, params); +- +- if (params->mode == V4L2_TUNER_RADIO) +- freq = freq / 1000; +- +- N = freq + priv->sgIF; +- +- i = 0; +- while (tda827x_table[i].lomax < N * 62500) { +- if (tda827x_table[i + 1].lomax == 0) +- break; +- i++; +- } +- +- N = N << tda827x_table[i].spd; +- +- tuner_reg[0] = 0; +- tuner_reg[1] = (unsigned char)(N>>8); +- tuner_reg[2] = (unsigned char) N; +- tuner_reg[3] = 0x40; +- tuner_reg[4] = 0x52 + (priv->lpsel << 5); +- tuner_reg[5] = (tda827x_table[i].spd << 6) + +- (tda827x_table[i].div1p5 << 5) + +- (tda827x_table[i].bs << 3) + tda827x_table[i].bp; +- tuner_reg[6] = 0x8f + (tda827x_table[i].gc3 << 4); +- tuner_reg[7] = 0x8f; +- +- msg.buf = tuner_reg; +- msg.len = 8; +- tuner_transfer(fe, &msg, 1); +- +- msg.buf = reg2; +- msg.len = 2; +- reg2[0] = 0x80; +- reg2[1] = 0; +- tuner_transfer(fe, &msg, 1); +- +- reg2[0] = 0x60; +- reg2[1] = 0xbf; +- tuner_transfer(fe, &msg, 1); +- +- reg2[0] = 0x30; +- reg2[1] = tuner_reg[4] + 0x80; +- tuner_transfer(fe, &msg, 1); +- +- msleep(1); +- reg2[0] = 0x30; +- reg2[1] = tuner_reg[4] + 4; +- tuner_transfer(fe, &msg, 1); +- +- msleep(1); +- reg2[0] = 0x30; +- reg2[1] = tuner_reg[4]; +- tuner_transfer(fe, &msg, 1); +- +- msleep(550); +- reg2[0] = 0x30; +- reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp; +- tuner_transfer(fe, &msg, 1); +- +- reg2[0] = 0x60; +- reg2[1] = 0x3f; +- tuner_transfer(fe, &msg, 1); +- +- reg2[0] = 0x80; +- reg2[1] = 0x08; /* Vsync en */ +- tuner_transfer(fe, &msg, 1); +- +- priv->frequency = params->frequency; +- +- return 0; +-} +- +-static void tda827xo_agcf(struct dvb_frontend *fe) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- unsigned char data[] = { 0x80, 0x0c }; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = data, .len = 2}; +- +- tuner_transfer(fe, &msg, 1); +-} +- +-/* ------------------------------------------------------------------ */ +- +-struct tda827xa_data { +- u32 lomax; +- u8 svco; +- u8 spd; +- u8 scr; +- u8 sbs; +- u8 gc3; +-}; +- +-static struct tda827xa_data tda827xa_dvbt[] = { +- { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, +- { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, +- { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, +- { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, +- { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, +- { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, +- { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +-}; +- +-static struct tda827xa_data tda827xa_dvbc[] = { +- { .lomax = 50125000, .svco = 2, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, +- { .lomax = 58500000, .svco = 3, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3}, +- { .lomax = 69250000, .svco = 0, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, +- { .lomax = 83625000, .svco = 1, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, +- { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3}, +- { .lomax = 100250000, .svco = 2, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, +- { .lomax = 117000000, .svco = 3, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1}, +- { .lomax = 138500000, .svco = 0, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, +- { .lomax = 167250000, .svco = 1, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, +- { .lomax = 187000000, .svco = 2, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1}, +- { .lomax = 200500000, .svco = 2, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 1}, +- { .lomax = 234000000, .svco = 3, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 3}, +- { .lomax = 277000000, .svco = 0, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 3}, +- { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 1}, +- { .lomax = 334500000, .svco = 1, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, +- { .lomax = 401000000, .svco = 2, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3}, +- { .lomax = 468000000, .svco = 3, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 1}, +- { .lomax = 535000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, +- { .lomax = 554000000, .svco = 0, .spd = 0, .scr = 2, .sbs = 3, .gc3 = 1}, +- { .lomax = 638000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, +- { .lomax = 669000000, .svco = 1, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, +- { .lomax = 720000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, +- { .lomax = 802000000, .svco = 2, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, +- { .lomax = 835000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, +- { .lomax = 885000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1}, +- { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1}, +- { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +-}; +- +-static struct tda827xa_data tda827xa_analog[] = { +- { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 3}, +- { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, +- { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, +- { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 3}, +- { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, +- { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 3}, +- { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 3}, +- { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, +- { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, +- { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 3}, +- { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, +- { .lomax = 554000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, +- { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, +- { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, +- { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, +- { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +-}; +- +-static int tda827xa_sleep(struct dvb_frontend *fe) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- static u8 buf[] = { 0x30, 0x90 }; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = buf, .len = sizeof(buf) }; +- +- dprintk("%s:\n", __func__); +- +- tuner_transfer(fe, &msg, 1); +- +- if (priv->cfg && priv->cfg->sleep) +- priv->cfg->sleep(fe); +- +- return 0; +-} +- +-static void tda827xa_lna_gain(struct dvb_frontend *fe, int high, +- struct analog_parameters *params) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- unsigned char buf[] = {0x22, 0x01}; +- int arg; +- int gp_func; +- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = sizeof(buf) }; +- +- if (NULL == priv->cfg) { +- dprintk("tda827x_config not defined, cannot set LNA gain!\n"); +- return; +- } +- msg.addr = priv->cfg->switch_addr; +- if (priv->cfg->config) { +- if (high) +- dprintk("setting LNA to high gain\n"); +- else +- dprintk("setting LNA to low gain\n"); +- } +- switch (priv->cfg->config) { +- case 0: /* no LNA */ +- break; +- case 1: /* switch is GPIO 0 of tda8290 */ +- case 2: +- if (params == NULL) { +- gp_func = 0; +- arg = 0; +- } else { +- /* turn Vsync on */ +- gp_func = 1; +- if (params->std & V4L2_STD_MN) +- arg = 1; +- else +- arg = 0; +- } +- if (fe->callback) +- fe->callback(priv->i2c_adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, +- gp_func, arg); +- buf[1] = high ? 0 : 1; +- if (priv->cfg->config == 2) +- buf[1] = high ? 1 : 0; +- tuner_transfer(fe, &msg, 1); +- break; +- case 3: /* switch with GPIO of saa713x */ +- if (fe->callback) +- fe->callback(priv->i2c_adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, 0, high); +- break; +- } +-} +- +-static int tda827xa_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct tda827x_priv *priv = fe->tuner_priv; +- struct tda827xa_data *frequency_map = tda827xa_dvbt; +- u8 buf[11]; +- +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = buf, .len = sizeof(buf) }; +- +- int i, tuner_freq, if_freq, rc; +- u32 N; +- +- dprintk("%s:\n", __func__); +- +- tda827xa_lna_gain(fe, 1, NULL); +- msleep(20); +- +- if (c->bandwidth_hz == 0) { +- if_freq = 5000000; +- } else if (c->bandwidth_hz <= 6000000) { +- if_freq = 4000000; +- } else if (c->bandwidth_hz <= 7000000) { +- if_freq = 4500000; +- } else { /* 8 MHz */ +- if_freq = 5000000; +- } +- tuner_freq = c->frequency; +- +- switch (c->delivery_system) { +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- dprintk("%s select tda827xa_dvbc\n", __func__); +- frequency_map = tda827xa_dvbc; +- break; +- default: +- break; +- } +- +- i = 0; +- while (frequency_map[i].lomax < tuner_freq) { +- if (frequency_map[i + 1].lomax == 0) +- break; +- i++; +- } +- +- tuner_freq += if_freq; +- +- N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd; +- buf[0] = 0; // subaddress +- buf[1] = N >> 8; +- buf[2] = N & 0xff; +- buf[3] = 0; +- buf[4] = 0x16; +- buf[5] = (frequency_map[i].spd << 5) + (frequency_map[i].svco << 3) + +- frequency_map[i].sbs; +- buf[6] = 0x4b + (frequency_map[i].gc3 << 4); +- buf[7] = 0x1c; +- buf[8] = 0x06; +- buf[9] = 0x24; +- buf[10] = 0x00; +- msg.len = 11; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- buf[0] = 0x90; +- buf[1] = 0xff; +- buf[2] = 0x60; +- buf[3] = 0x00; +- buf[4] = 0x59; // lpsel, for 6MHz + 2 +- msg.len = 5; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- buf[0] = 0xa0; +- buf[1] = 0x40; +- msg.len = 2; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- msleep(11); +- msg.flags = I2C_M_RD; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- msg.flags = 0; +- +- buf[1] >>= 4; +- dprintk("tda8275a AGC2 gain is: %d\n", buf[1]); +- if ((buf[1]) < 2) { +- tda827xa_lna_gain(fe, 0, NULL); +- buf[0] = 0x60; +- buf[1] = 0x0c; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- } +- +- buf[0] = 0xc0; +- buf[1] = 0x99; // lpsel, for 6MHz + 2 +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- buf[0] = 0x60; +- buf[1] = 0x3c; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- /* correct CP value */ +- buf[0] = 0x30; +- buf[1] = 0x10 + frequency_map[i].scr; +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- msleep(163); +- buf[0] = 0xc0; +- buf[1] = 0x39; // lpsel, for 6MHz + 2 +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- msleep(3); +- /* freeze AGC1 */ +- buf[0] = 0x50; +- buf[1] = 0x4f + (frequency_map[i].gc3 << 4); +- rc = tuner_transfer(fe, &msg, 1); +- if (rc < 0) +- goto err; +- +- priv->frequency = c->frequency; +- priv->bandwidth = c->bandwidth_hz; +- +- return 0; +- +-err: +- printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n", +- __func__, priv->i2c_addr << 1); +- return rc; +-} +- +- +-static int tda827xa_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- unsigned char tuner_reg[11]; +- u32 N; +- int i; +- struct tda827x_priv *priv = fe->tuner_priv; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, +- .buf = tuner_reg, .len = sizeof(tuner_reg) }; +- unsigned int freq = params->frequency; +- +- tda827x_set_std(fe, params); +- +- tda827xa_lna_gain(fe, 1, params); +- msleep(10); +- +- if (params->mode == V4L2_TUNER_RADIO) +- freq = freq / 1000; +- +- N = freq + priv->sgIF; +- +- i = 0; +- while (tda827xa_analog[i].lomax < N * 62500) { +- if (tda827xa_analog[i + 1].lomax == 0) +- break; +- i++; +- } +- +- N = N << tda827xa_analog[i].spd; +- +- tuner_reg[0] = 0; +- tuner_reg[1] = (unsigned char)(N>>8); +- tuner_reg[2] = (unsigned char) N; +- tuner_reg[3] = 0; +- tuner_reg[4] = 0x16; +- tuner_reg[5] = (tda827xa_analog[i].spd << 5) + +- (tda827xa_analog[i].svco << 3) + +- tda827xa_analog[i].sbs; +- tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); +- tuner_reg[7] = 0x1c; +- tuner_reg[8] = 4; +- tuner_reg[9] = 0x20; +- tuner_reg[10] = 0x00; +- msg.len = 11; +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0x90; +- tuner_reg[1] = 0xff; +- tuner_reg[2] = 0xe0; +- tuner_reg[3] = 0; +- tuner_reg[4] = 0x99 + (priv->lpsel << 1); +- msg.len = 5; +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0xa0; +- tuner_reg[1] = 0xc0; +- msg.len = 2; +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0x30; +- tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; +- tuner_transfer(fe, &msg, 1); +- +- msg.flags = I2C_M_RD; +- tuner_transfer(fe, &msg, 1); +- msg.flags = 0; +- tuner_reg[1] >>= 4; +- dprintk("AGC2 gain is: %d\n", tuner_reg[1]); +- if (tuner_reg[1] < 1) +- tda827xa_lna_gain(fe, 0, params); +- +- msleep(100); +- tuner_reg[0] = 0x60; +- tuner_reg[1] = 0x3c; +- tuner_transfer(fe, &msg, 1); +- +- msleep(163); +- tuner_reg[0] = 0x50; +- tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0x80; +- tuner_reg[1] = 0x28; +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0xb0; +- tuner_reg[1] = 0x01; +- tuner_transfer(fe, &msg, 1); +- +- tuner_reg[0] = 0xc0; +- tuner_reg[1] = 0x19 + (priv->lpsel << 1); +- tuner_transfer(fe, &msg, 1); +- +- priv->frequency = params->frequency; +- +- return 0; +-} +- +-static void tda827xa_agcf(struct dvb_frontend *fe) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- unsigned char data[] = {0x80, 0x2c}; +- struct i2c_msg msg = {.addr = priv->i2c_addr, .flags = 0, +- .buf = data, .len = 2}; +- tuner_transfer(fe, &msg, 1); +-} +- +-/* ------------------------------------------------------------------ */ +- +-static int tda827x_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- *bandwidth = priv->bandwidth; +- return 0; +-} +- +-static int tda827x_init(struct dvb_frontend *fe) +-{ +- struct tda827x_priv *priv = fe->tuner_priv; +- dprintk("%s:\n", __func__); +- if (priv->cfg && priv->cfg->init) +- priv->cfg->init(fe); +- +- return 0; +-} +- +-static int tda827x_probe_version(struct dvb_frontend *fe); +- +-static int tda827x_initial_init(struct dvb_frontend *fe) +-{ +- int ret; +- ret = tda827x_probe_version(fe); +- if (ret) +- return ret; +- return fe->ops.tuner_ops.init(fe); +-} +- +-static int tda827x_initial_sleep(struct dvb_frontend *fe) +-{ +- int ret; +- ret = tda827x_probe_version(fe); +- if (ret) +- return ret; +- return fe->ops.tuner_ops.sleep(fe); +-} +- +-static struct dvb_tuner_ops tda827xo_tuner_ops = { +- .info = { +- .name = "Philips TDA827X", +- .frequency_min = 55000000, +- .frequency_max = 860000000, +- .frequency_step = 250000 +- }, +- .release = tda827x_release, +- .init = tda827x_initial_init, +- .sleep = tda827x_initial_sleep, +- .set_params = tda827xo_set_params, +- .set_analog_params = tda827xo_set_analog_params, +- .get_frequency = tda827x_get_frequency, +- .get_bandwidth = tda827x_get_bandwidth, +-}; +- +-static struct dvb_tuner_ops tda827xa_tuner_ops = { +- .info = { +- .name = "Philips TDA827XA", +- .frequency_min = 44000000, +- .frequency_max = 906000000, +- .frequency_step = 62500 +- }, +- .release = tda827x_release, +- .init = tda827x_init, +- .sleep = tda827xa_sleep, +- .set_params = tda827xa_set_params, +- .set_analog_params = tda827xa_set_analog_params, +- .get_frequency = tda827x_get_frequency, +- .get_bandwidth = tda827x_get_bandwidth, +-}; +- +-static int tda827x_probe_version(struct dvb_frontend *fe) +-{ +- u8 data; +- int rc; +- struct tda827x_priv *priv = fe->tuner_priv; +- struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD, +- .buf = &data, .len = 1 }; +- +- rc = tuner_transfer(fe, &msg, 1); +- +- if (rc < 0) { +- printk("%s: could not read from tuner at addr: 0x%02x\n", +- __func__, msg.addr << 1); +- return rc; +- } +- if ((data & 0x3c) == 0) { +- dprintk("tda827x tuner found\n"); +- fe->ops.tuner_ops.init = tda827x_init; +- fe->ops.tuner_ops.sleep = tda827xo_sleep; +- if (priv->cfg) +- priv->cfg->agcf = tda827xo_agcf; +- } else { +- dprintk("tda827xa tuner found\n"); +- memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops)); +- if (priv->cfg) +- priv->cfg->agcf = tda827xa_agcf; +- } +- return 0; +-} +- +-struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr, +- struct i2c_adapter *i2c, +- struct tda827x_config *cfg) +-{ +- struct tda827x_priv *priv = NULL; +- +- dprintk("%s:\n", __func__); +- priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->i2c_addr = addr; +- priv->i2c_adap = i2c; +- priv->cfg = cfg; +- memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops)); +- fe->tuner_priv = priv; +- +- dprintk("type set to %s\n", fe->ops.tuner_ops.info.name); +- +- return fe; +-} +-EXPORT_SYMBOL_GPL(tda827x_attach); +- +-MODULE_DESCRIPTION("DVB TDA827x driver"); +-MODULE_AUTHOR("Hartmut Hackmann "); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda827x.h b/drivers/media/common/tuners/tda827x.h +deleted file mode 100644 +index 7d72ce0..0000000 +--- a/drivers/media/common/tuners/tda827x.h ++++ /dev/null +@@ -1,68 +0,0 @@ +- /* +- DVB Driver for Philips tda827x / tda827xa Silicon tuners +- +- (c) 2005 Hartmut Hackmann +- (c) 2007 Michael Krufky +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#ifndef __DVB_TDA827X_H__ +-#define __DVB_TDA827X_H__ +- +-#include +-#include "dvb_frontend.h" +- +-struct tda827x_config +-{ +- /* saa7134 - provided callbacks */ +- int (*init) (struct dvb_frontend *fe); +- int (*sleep) (struct dvb_frontend *fe); +- +- /* interface to tda829x driver */ +- unsigned int config; +- int switch_addr; +- +- void (*agcf)(struct dvb_frontend *fe); +-}; +- +- +-/** +- * Attach a tda827x tuner to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param addr i2c address of the tuner. +- * @param i2c i2c adapter to use. +- * @param cfg optional callback function pointers. +- * @return FE pointer on success, NULL on failure. +- */ +-#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, +- struct i2c_adapter *i2c, +- struct tda827x_config *cfg); +-#else +-static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, +- int addr, +- struct i2c_adapter *i2c, +- struct tda827x_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_MEDIA_TUNER_TDA827X +- +-#endif // __DVB_TDA827X_H__ +diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c +deleted file mode 100644 +index 8c48521..0000000 +--- a/drivers/media/common/tuners/tda8290.c ++++ /dev/null +@@ -1,874 +0,0 @@ +-/* +- +- i2c tv tuner chip device driver +- controls the philips tda8290+75 tuner chip combo. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- This "tda8290" module was split apart from the original "tuner" module. +-*/ +- +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tda8290.h" +-#include "tda827x.h" +-#include "tda18271.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-static int deemphasis_50; +-module_param(deemphasis_50, int, 0644); +-MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); +- +-/* ---------------------------------------------------------------------- */ +- +-struct tda8290_priv { +- struct tuner_i2c_props i2c_props; +- +- unsigned char tda8290_easy_mode; +- +- unsigned char tda827x_addr; +- +- unsigned char ver; +-#define TDA8290 1 +-#define TDA8295 2 +-#define TDA8275 4 +-#define TDA8275A 8 +-#define TDA18271 16 +- +- struct tda827x_config cfg; +-}; +- +-/*---------------------------------------------------------------------*/ +- +-static int tda8290_i2c_bridge(struct dvb_frontend *fe, int close) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char enable[2] = { 0x21, 0xC0 }; +- unsigned char disable[2] = { 0x21, 0x00 }; +- unsigned char *msg; +- +- if (close) { +- msg = enable; +- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); +- /* let the bridge stabilize */ +- msleep(20); +- } else { +- msg = disable; +- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); +- } +- +- return 0; +-} +- +-static int tda8295_i2c_bridge(struct dvb_frontend *fe, int close) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char enable[2] = { 0x45, 0xc1 }; +- unsigned char disable[2] = { 0x46, 0x00 }; +- unsigned char buf[3] = { 0x45, 0x01, 0x00 }; +- unsigned char *msg; +- +- if (close) { +- msg = enable; +- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); +- /* let the bridge stabilize */ +- msleep(20); +- } else { +- msg = disable; +- tuner_i2c_xfer_send_recv(&priv->i2c_props, msg, 1, &msg[1], 1); +- +- buf[2] = msg[1]; +- buf[2] &= ~0x04; +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 3); +- msleep(5); +- +- msg[1] |= 0x04; +- tuner_i2c_xfer_send(&priv->i2c_props, msg, 2); +- } +- +- return 0; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void set_audio(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- char* mode; +- +- if (params->std & V4L2_STD_MN) { +- priv->tda8290_easy_mode = 0x01; +- mode = "MN"; +- } else if (params->std & V4L2_STD_B) { +- priv->tda8290_easy_mode = 0x02; +- mode = "B"; +- } else if (params->std & V4L2_STD_GH) { +- priv->tda8290_easy_mode = 0x04; +- mode = "GH"; +- } else if (params->std & V4L2_STD_PAL_I) { +- priv->tda8290_easy_mode = 0x08; +- mode = "I"; +- } else if (params->std & V4L2_STD_DK) { +- priv->tda8290_easy_mode = 0x10; +- mode = "DK"; +- } else if (params->std & V4L2_STD_SECAM_L) { +- priv->tda8290_easy_mode = 0x20; +- mode = "L"; +- } else if (params->std & V4L2_STD_SECAM_LC) { +- priv->tda8290_easy_mode = 0x40; +- mode = "LC"; +- } else { +- priv->tda8290_easy_mode = 0x10; +- mode = "xx"; +- } +- +- if (params->mode == V4L2_TUNER_RADIO) { +- /* Set TDA8295 to FM radio; Start TDA8290 with MN values */ +- priv->tda8290_easy_mode = (priv->ver & TDA8295) ? 0x80 : 0x01; +- tuner_dbg("setting to radio FM\n"); +- } else { +- tuner_dbg("setting tda829x to system %s\n", mode); +- } +-} +- +-static struct { +- unsigned char seq[2]; +-} fm_mode[] = { +- { { 0x01, 0x81} }, /* Put device into expert mode */ +- { { 0x03, 0x48} }, /* Disable NOTCH and VIDEO filters */ +- { { 0x04, 0x04} }, /* Disable color carrier filter (SSIF) */ +- { { 0x05, 0x04} }, /* ADC headroom */ +- { { 0x06, 0x10} }, /* group delay flat */ +- +- { { 0x07, 0x00} }, /* use the same radio DTO values as a tda8295 */ +- { { 0x08, 0x00} }, +- { { 0x09, 0x80} }, +- { { 0x0a, 0xda} }, +- { { 0x0b, 0x4b} }, +- { { 0x0c, 0x68} }, +- +- { { 0x0d, 0x00} }, /* PLL off, no video carrier detect */ +- { { 0x14, 0x00} }, /* disable auto mute if no video */ +-}; +- +-static void tda8290_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char soft_reset[] = { 0x00, 0x00 }; +- unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode }; +- unsigned char expert_mode[] = { 0x01, 0x80 }; +- unsigned char agc_out_on[] = { 0x02, 0x00 }; +- unsigned char gainset_off[] = { 0x28, 0x14 }; +- unsigned char if_agc_spd[] = { 0x0f, 0x88 }; +- unsigned char adc_head_6[] = { 0x05, 0x04 }; +- unsigned char adc_head_9[] = { 0x05, 0x02 }; +- unsigned char adc_head_12[] = { 0x05, 0x01 }; +- unsigned char pll_bw_nom[] = { 0x0d, 0x47 }; +- unsigned char pll_bw_low[] = { 0x0d, 0x27 }; +- unsigned char gainset_2[] = { 0x28, 0x64 }; +- unsigned char agc_rst_on[] = { 0x0e, 0x0b }; +- unsigned char agc_rst_off[] = { 0x0e, 0x09 }; +- unsigned char if_agc_set[] = { 0x0f, 0x81 }; +- unsigned char addr_adc_sat = 0x1a; +- unsigned char addr_agc_stat = 0x1d; +- unsigned char addr_pll_stat = 0x1b; +- unsigned char adc_sat, agc_stat, +- pll_stat; +- int i; +- +- set_audio(fe, params); +- +- if (priv->cfg.config) +- tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config); +- tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); +- msleep(1); +- +- if (params->mode == V4L2_TUNER_RADIO) { +- unsigned char deemphasis[] = { 0x13, 1 }; +- +- /* FIXME: allow using a different deemphasis */ +- +- if (deemphasis_50) +- deemphasis[1] = 2; +- +- for (i = 0; i < ARRAY_SIZE(fm_mode); i++) +- tuner_i2c_xfer_send(&priv->i2c_props, fm_mode[i].seq, 2); +- +- tuner_i2c_xfer_send(&priv->i2c_props, deemphasis, 2); +- } else { +- expert_mode[1] = priv->tda8290_easy_mode + 0x80; +- tuner_i2c_xfer_send(&priv->i2c_props, expert_mode, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, gainset_off, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_spd, 2); +- if (priv->tda8290_easy_mode & 0x60) +- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_9, 2); +- else +- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_6, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_nom, 2); +- } +- +- +- tda8290_i2c_bridge(fe, 1); +- +- if (fe->ops.tuner_ops.set_analog_params) +- fe->ops.tuner_ops.set_analog_params(fe, params); +- +- for (i = 0; i < 3; i++) { +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_pll_stat, 1, &pll_stat, 1); +- if (pll_stat & 0x80) { +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_adc_sat, 1, +- &adc_sat, 1); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_agc_stat, 1, +- &agc_stat, 1); +- tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); +- break; +- } else { +- tuner_dbg("tda8290 not locked, no signal?\n"); +- msleep(100); +- } +- } +- /* adjust headroom resp. gain */ +- if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { +- tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", +- agc_stat, adc_sat, pll_stat & 0x80); +- tuner_i2c_xfer_send(&priv->i2c_props, gainset_2, 2); +- msleep(100); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_agc_stat, 1, &agc_stat, 1); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_pll_stat, 1, &pll_stat, 1); +- if ((agc_stat > 115) || !(pll_stat & 0x80)) { +- tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n", +- agc_stat, pll_stat & 0x80); +- if (priv->cfg.agcf) +- priv->cfg.agcf(fe); +- msleep(100); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_agc_stat, 1, +- &agc_stat, 1); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_pll_stat, 1, +- &pll_stat, 1); +- if((agc_stat > 115) || !(pll_stat & 0x80)) { +- tuner_dbg("adjust gain, step 3. Agc: %d\n", agc_stat); +- tuner_i2c_xfer_send(&priv->i2c_props, adc_head_12, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, pll_bw_low, 2); +- msleep(100); +- } +- } +- } +- +- /* l/ l' deadlock? */ +- if(priv->tda8290_easy_mode & 0x60) { +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_adc_sat, 1, +- &adc_sat, 1); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &addr_pll_stat, 1, +- &pll_stat, 1); +- if ((adc_sat > 20) || !(pll_stat & 0x80)) { +- tuner_dbg("trying to resolve SECAM L deadlock\n"); +- tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_on, 2); +- msleep(40); +- tuner_i2c_xfer_send(&priv->i2c_props, agc_rst_off, 2); +- } +- } +- +- tda8290_i2c_bridge(fe, 0); +- tuner_i2c_xfer_send(&priv->i2c_props, if_agc_set, 2); +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void tda8295_power(struct dvb_frontend *fe, int enable) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char buf[] = { 0x30, 0x00 }; /* clb_stdbt */ +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); +- +- if (enable) +- buf[1] = 0x01; +- else +- buf[1] = 0x03; +- +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +-} +- +-static void tda8295_set_easy_mode(struct dvb_frontend *fe, int enable) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char buf[] = { 0x01, 0x00 }; +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); +- +- if (enable) +- buf[1] = 0x01; /* rising edge sets regs 0x02 - 0x23 */ +- else +- buf[1] = 0x00; /* reset active bit */ +- +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +-} +- +-static void tda8295_set_video_std(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char buf[] = { 0x00, priv->tda8290_easy_mode }; +- +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +- +- tda8295_set_easy_mode(fe, 1); +- msleep(20); +- tda8295_set_easy_mode(fe, 0); +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void tda8295_agc1_out(struct dvb_frontend *fe, int enable) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char buf[] = { 0x02, 0x00 }; /* DIV_FUNC */ +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, &buf[0], 1, &buf[1], 1); +- +- if (enable) +- buf[1] &= ~0x40; +- else +- buf[1] |= 0x40; +- +- tuner_i2c_xfer_send(&priv->i2c_props, buf, 2); +-} +- +-static void tda8295_agc2_out(struct dvb_frontend *fe, int enable) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char set_gpio_cf[] = { 0x44, 0x00 }; +- unsigned char set_gpio_val[] = { 0x46, 0x00 }; +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &set_gpio_cf[0], 1, &set_gpio_cf[1], 1); +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &set_gpio_val[0], 1, &set_gpio_val[1], 1); +- +- set_gpio_cf[1] &= 0xf0; /* clear GPIO_0 bits 3-0 */ +- +- if (enable) { +- set_gpio_cf[1] |= 0x01; /* config GPIO_0 as Open Drain Out */ +- set_gpio_val[1] &= 0xfe; /* set GPIO_0 pin low */ +- } +- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_cf, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_val, 2); +-} +- +-static int tda8295_has_signal(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char hvpll_stat = 0x26; +- unsigned char ret; +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, &hvpll_stat, 1, &ret, 1); +- return (ret & 0x01) ? 65535 : 0; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void tda8295_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char blanking_mode[] = { 0x1d, 0x00 }; +- +- set_audio(fe, params); +- +- tuner_dbg("%s: freq = %d\n", __func__, params->frequency); +- +- tda8295_power(fe, 1); +- tda8295_agc1_out(fe, 1); +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- &blanking_mode[0], 1, &blanking_mode[1], 1); +- +- tda8295_set_video_std(fe); +- +- blanking_mode[1] = 0x03; +- tuner_i2c_xfer_send(&priv->i2c_props, blanking_mode, 2); +- msleep(20); +- +- tda8295_i2c_bridge(fe, 1); +- +- if (fe->ops.tuner_ops.set_analog_params) +- fe->ops.tuner_ops.set_analog_params(fe, params); +- +- if (priv->cfg.agcf) +- priv->cfg.agcf(fe); +- +- if (tda8295_has_signal(fe)) +- tuner_dbg("tda8295 is locked\n"); +- else +- tuner_dbg("tda8295 not locked, no signal?\n"); +- +- tda8295_i2c_bridge(fe, 0); +-} +- +-/*---------------------------------------------------------------------*/ +- +-static int tda8290_has_signal(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char i2c_get_afc[1] = { 0x1B }; +- unsigned char afc = 0; +- +- tuner_i2c_xfer_send_recv(&priv->i2c_props, +- i2c_get_afc, ARRAY_SIZE(i2c_get_afc), &afc, 1); +- return (afc & 0x80)? 65535:0; +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void tda8290_standby(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char cb1[] = { 0x30, 0xD0 }; +- unsigned char tda8290_standby[] = { 0x00, 0x02 }; +- unsigned char tda8290_agc_tri[] = { 0x02, 0x20 }; +- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; +- +- tda8290_i2c_bridge(fe, 1); +- if (priv->ver & TDA8275A) +- cb1[1] = 0x90; +- i2c_transfer(priv->i2c_props.adap, &msg, 1); +- tda8290_i2c_bridge(fe, 0); +- tuner_i2c_xfer_send(&priv->i2c_props, tda8290_agc_tri, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, tda8290_standby, 2); +-} +- +-static void tda8295_standby(struct dvb_frontend *fe) +-{ +- tda8295_agc1_out(fe, 0); /* Put AGC in tri-state */ +- +- tda8295_power(fe, 0); +-} +- +-static void tda8290_init_if(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- unsigned char set_VS[] = { 0x30, 0x6F }; +- unsigned char set_GP00_CF[] = { 0x20, 0x01 }; +- unsigned char set_GP01_CF[] = { 0x20, 0x0B }; +- +- if ((priv->cfg.config == 1) || (priv->cfg.config == 2)) +- tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2); +- else +- tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_VS, 2); +-} +- +-static void tda8295_init_if(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- static unsigned char set_adc_ctl[] = { 0x33, 0x14 }; +- static unsigned char set_adc_ctl2[] = { 0x34, 0x00 }; +- static unsigned char set_pll_reg6[] = { 0x3e, 0x63 }; +- static unsigned char set_pll_reg0[] = { 0x38, 0x23 }; +- static unsigned char set_pll_reg7[] = { 0x3f, 0x01 }; +- static unsigned char set_pll_reg10[] = { 0x42, 0x61 }; +- static unsigned char set_gpio_reg0[] = { 0x44, 0x0b }; +- +- tda8295_power(fe, 1); +- +- tda8295_set_easy_mode(fe, 0); +- tda8295_set_video_std(fe); +- +- tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_adc_ctl2, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg6, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg0, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg7, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_pll_reg10, 2); +- tuner_i2c_xfer_send(&priv->i2c_props, set_gpio_reg0, 2); +- +- tda8295_agc1_out(fe, 0); +- tda8295_agc2_out(fe, 0); +-} +- +-static void tda8290_init_tuner(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf, +- 0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 }; +- unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b, +- 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b }; +- struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, +- .buf=tda8275_init, .len = 14}; +- if (priv->ver & TDA8275A) +- msg.buf = tda8275a_init; +- +- tda8290_i2c_bridge(fe, 1); +- i2c_transfer(priv->i2c_props.adap, &msg, 1); +- tda8290_i2c_bridge(fe, 0); +-} +- +-/*---------------------------------------------------------------------*/ +- +-static void tda829x_release(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- +- /* only try to release the tuner if we've +- * attached it from within this module */ +- if (priv->ver & (TDA18271 | TDA8275 | TDA8275A)) +- if (fe->ops.tuner_ops.release) +- fe->ops.tuner_ops.release(fe); +- +- kfree(fe->analog_demod_priv); +- fe->analog_demod_priv = NULL; +-} +- +-static struct tda18271_config tda829x_tda18271_config = { +- .gate = TDA18271_GATE_ANALOG, +-}; +- +-static int tda829x_find_tuner(struct dvb_frontend *fe) +-{ +- struct tda8290_priv *priv = fe->analog_demod_priv; +- struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; +- int i, ret, tuners_found; +- u32 tuner_addrs; +- u8 data; +- struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 }; +- +- if (!analog_ops->i2c_gate_ctrl) { +- printk(KERN_ERR "tda8290: no gate control were provided!\n"); +- +- return -EINVAL; +- } +- +- analog_ops->i2c_gate_ctrl(fe, 1); +- +- /* probe for tuner chip */ +- tuners_found = 0; +- tuner_addrs = 0; +- for (i = 0x60; i <= 0x63; i++) { +- msg.addr = i; +- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); +- if (ret == 1) { +- tuners_found++; +- tuner_addrs = (tuner_addrs << 8) + i; +- } +- } +- /* if there is more than one tuner, we expect the right one is +- behind the bridge and we choose the highest address that doesn't +- give a response now +- */ +- +- analog_ops->i2c_gate_ctrl(fe, 0); +- +- if (tuners_found > 1) +- for (i = 0; i < tuners_found; i++) { +- msg.addr = tuner_addrs & 0xff; +- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); +- if (ret == 1) +- tuner_addrs = tuner_addrs >> 8; +- else +- break; +- } +- +- if (tuner_addrs == 0) { +- tuner_addrs = 0x60; +- tuner_info("could not clearly identify tuner address, " +- "defaulting to %x\n", tuner_addrs); +- } else { +- tuner_addrs = tuner_addrs & 0xff; +- tuner_info("setting tuner address to %x\n", tuner_addrs); +- } +- priv->tda827x_addr = tuner_addrs; +- msg.addr = tuner_addrs; +- +- analog_ops->i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer(priv->i2c_props.adap, &msg, 1); +- +- if (ret != 1) { +- tuner_warn("tuner access failed!\n"); +- analog_ops->i2c_gate_ctrl(fe, 0); +- return -EREMOTEIO; +- } +- +- if ((data == 0x83) || (data == 0x84)) { +- priv->ver |= TDA18271; +- tda829x_tda18271_config.config = priv->cfg.config; +- dvb_attach(tda18271_attach, fe, priv->tda827x_addr, +- priv->i2c_props.adap, &tda829x_tda18271_config); +- } else { +- if ((data & 0x3c) == 0) +- priv->ver |= TDA8275; +- else +- priv->ver |= TDA8275A; +- +- dvb_attach(tda827x_attach, fe, priv->tda827x_addr, +- priv->i2c_props.adap, &priv->cfg); +- priv->cfg.switch_addr = priv->i2c_props.addr; +- } +- if (fe->ops.tuner_ops.init) +- fe->ops.tuner_ops.init(fe); +- +- if (fe->ops.tuner_ops.sleep) +- fe->ops.tuner_ops.sleep(fe); +- +- analog_ops->i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int tda8290_probe(struct tuner_i2c_props *i2c_props) +-{ +-#define TDA8290_ID 0x89 +- u8 reg = 0x1f, id; +- struct i2c_msg msg_read[] = { +- { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, +- { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, +- }; +- +- /* detect tda8290 */ +- if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { +- printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", +- __func__, reg); +- return -ENODEV; +- } +- +- if (id == TDA8290_ID) { +- if (debug) +- printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n", +- __func__, i2c_adapter_id(i2c_props->adap), +- i2c_props->addr); +- return 0; +- } +- return -ENODEV; +-} +- +-static int tda8295_probe(struct tuner_i2c_props *i2c_props) +-{ +-#define TDA8295_ID 0x8a +-#define TDA8295C2_ID 0x8b +- u8 reg = 0x2f, id; +- struct i2c_msg msg_read[] = { +- { .addr = i2c_props->addr, .flags = 0, .len = 1, .buf = ® }, +- { .addr = i2c_props->addr, .flags = I2C_M_RD, .len = 1, .buf = &id }, +- }; +- +- /* detect tda8295 */ +- if (i2c_transfer(i2c_props->adap, msg_read, 2) != 2) { +- printk(KERN_WARNING "%s: couldn't read register 0x%02x\n", +- __func__, reg); +- return -ENODEV; +- } +- +- if ((id & 0xfe) == TDA8295_ID) { +- if (debug) +- printk(KERN_DEBUG "%s: %s detected @ %d-%04x\n", +- __func__, (id == TDA8295_ID) ? +- "tda8295c1" : "tda8295c2", +- i2c_adapter_id(i2c_props->adap), +- i2c_props->addr); +- return 0; +- } +- +- return -ENODEV; +-} +- +-static struct analog_demod_ops tda8290_ops = { +- .set_params = tda8290_set_params, +- .has_signal = tda8290_has_signal, +- .standby = tda8290_standby, +- .release = tda829x_release, +- .i2c_gate_ctrl = tda8290_i2c_bridge, +-}; +- +-static struct analog_demod_ops tda8295_ops = { +- .set_params = tda8295_set_params, +- .has_signal = tda8295_has_signal, +- .standby = tda8295_standby, +- .release = tda829x_release, +- .i2c_gate_ctrl = tda8295_i2c_bridge, +-}; +- +-struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, u8 i2c_addr, +- struct tda829x_config *cfg) +-{ +- struct tda8290_priv *priv = NULL; +- char *name; +- +- priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- fe->analog_demod_priv = priv; +- +- priv->i2c_props.addr = i2c_addr; +- priv->i2c_props.adap = i2c_adap; +- priv->i2c_props.name = "tda829x"; +- if (cfg) +- priv->cfg.config = cfg->lna_cfg; +- +- if (tda8290_probe(&priv->i2c_props) == 0) { +- priv->ver = TDA8290; +- memcpy(&fe->ops.analog_ops, &tda8290_ops, +- sizeof(struct analog_demod_ops)); +- } +- +- if (tda8295_probe(&priv->i2c_props) == 0) { +- priv->ver = TDA8295; +- memcpy(&fe->ops.analog_ops, &tda8295_ops, +- sizeof(struct analog_demod_ops)); +- } +- +- if (!(cfg) || (TDA829X_PROBE_TUNER == cfg->probe_tuner)) { +- tda8295_power(fe, 1); +- if (tda829x_find_tuner(fe) < 0) +- goto fail; +- } +- +- switch (priv->ver) { +- case TDA8290: +- name = "tda8290"; +- break; +- case TDA8295: +- name = "tda8295"; +- break; +- case TDA8290 | TDA8275: +- name = "tda8290+75"; +- break; +- case TDA8295 | TDA8275: +- name = "tda8295+75"; +- break; +- case TDA8290 | TDA8275A: +- name = "tda8290+75a"; +- break; +- case TDA8295 | TDA8275A: +- name = "tda8295+75a"; +- break; +- case TDA8290 | TDA18271: +- name = "tda8290+18271"; +- break; +- case TDA8295 | TDA18271: +- name = "tda8295+18271"; +- break; +- default: +- goto fail; +- } +- tuner_info("type set to %s\n", name); +- +- fe->ops.analog_ops.info.name = name; +- +- if (priv->ver & TDA8290) { +- if (priv->ver & (TDA8275 | TDA8275A)) +- tda8290_init_tuner(fe); +- tda8290_init_if(fe); +- } else if (priv->ver & TDA8295) +- tda8295_init_if(fe); +- +- return fe; +- +-fail: +- memset(&fe->ops.analog_ops, 0, sizeof(struct analog_demod_ops)); +- +- tda829x_release(fe); +- return NULL; +-} +-EXPORT_SYMBOL_GPL(tda829x_attach); +- +-int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) +-{ +- struct tuner_i2c_props i2c_props = { +- .adap = i2c_adap, +- .addr = i2c_addr, +- }; +- +- unsigned char soft_reset[] = { 0x00, 0x00 }; +- unsigned char easy_mode_b[] = { 0x01, 0x02 }; +- unsigned char easy_mode_g[] = { 0x01, 0x04 }; +- unsigned char restore_9886[] = { 0x00, 0xd6, 0x30 }; +- unsigned char addr_dto_lsb = 0x07; +- unsigned char data; +-#define PROBE_BUFFER_SIZE 8 +- unsigned char buf[PROBE_BUFFER_SIZE]; +- int i; +- +- /* rule out tda9887, which would return the same byte repeatedly */ +- tuner_i2c_xfer_send_recv(&i2c_props, +- soft_reset, 1, buf, PROBE_BUFFER_SIZE); +- for (i = 1; i < PROBE_BUFFER_SIZE; i++) { +- if (buf[i] != buf[0]) +- break; +- } +- +- /* all bytes are equal, not a tda829x - probably a tda9887 */ +- if (i == PROBE_BUFFER_SIZE) +- return -ENODEV; +- +- if ((tda8290_probe(&i2c_props) == 0) || +- (tda8295_probe(&i2c_props) == 0)) +- return 0; +- +- /* fall back to old probing method */ +- tuner_i2c_xfer_send(&i2c_props, easy_mode_b, 2); +- tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); +- tuner_i2c_xfer_send_recv(&i2c_props, &addr_dto_lsb, 1, &data, 1); +- if (data == 0) { +- tuner_i2c_xfer_send(&i2c_props, easy_mode_g, 2); +- tuner_i2c_xfer_send(&i2c_props, soft_reset, 2); +- tuner_i2c_xfer_send_recv(&i2c_props, +- &addr_dto_lsb, 1, &data, 1); +- if (data == 0x7b) { +- return 0; +- } +- } +- tuner_i2c_xfer_send(&i2c_props, restore_9886, 3); +- return -ENODEV; +-} +-EXPORT_SYMBOL_GPL(tda829x_probe); +- +-MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver"); +-MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda8290.h b/drivers/media/common/tuners/tda8290.h +deleted file mode 100644 +index 7e288b2..0000000 +--- a/drivers/media/common/tuners/tda8290.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA8290_H__ +-#define __TDA8290_H__ +- +-#include +-#include "dvb_frontend.h" +- +-struct tda829x_config { +- unsigned int lna_cfg; +- +- unsigned int probe_tuner:1; +-#define TDA829X_PROBE_TUNER 0 +-#define TDA829X_DONT_PROBE 1 +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE)) +-extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr); +- +-extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- struct tda829x_config *cfg); +-#else +-static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -EINVAL; +-} +- +-static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- struct tda829x_config *cfg) +-{ +- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", +- __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TDA8290_H__ */ +diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c +deleted file mode 100644 +index cdb645d..0000000 +--- a/drivers/media/common/tuners/tda9887.c ++++ /dev/null +@@ -1,717 +0,0 @@ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tda9887.h" +- +- +-/* Chips: +- TDA9885 (PAL, NTSC) +- TDA9886 (PAL, SECAM, NTSC) +- TDA9887 (PAL, SECAM, NTSC, FM Radio) +- +- Used as part of several tuners +-*/ +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-static DEFINE_MUTEX(tda9887_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-struct tda9887_priv { +- struct tuner_i2c_props i2c_props; +- struct list_head hybrid_tuner_instance_list; +- +- unsigned char data[4]; +- unsigned int config; +- unsigned int mode; +- unsigned int audmode; +- v4l2_std_id std; +- +- bool standby; +-}; +- +-/* ---------------------------------------------------------------------- */ +- +-#define UNSET (-1U) +- +-struct tvnorm { +- v4l2_std_id std; +- char *name; +- unsigned char b; +- unsigned char c; +- unsigned char e; +-}; +- +-/* ---------------------------------------------------------------------- */ +- +-// +-// TDA defines +-// +- +-//// first reg (b) +-#define cVideoTrapBypassOFF 0x00 // bit b0 +-#define cVideoTrapBypassON 0x01 // bit b0 +- +-#define cAutoMuteFmInactive 0x00 // bit b1 +-#define cAutoMuteFmActive 0x02 // bit b1 +- +-#define cIntercarrier 0x00 // bit b2 +-#define cQSS 0x04 // bit b2 +- +-#define cPositiveAmTV 0x00 // bit b3:4 +-#define cFmRadio 0x08 // bit b3:4 +-#define cNegativeFmTV 0x10 // bit b3:4 +- +- +-#define cForcedMuteAudioON 0x20 // bit b5 +-#define cForcedMuteAudioOFF 0x00 // bit b5 +- +-#define cOutputPort1Active 0x00 // bit b6 +-#define cOutputPort1Inactive 0x40 // bit b6 +- +-#define cOutputPort2Active 0x00 // bit b7 +-#define cOutputPort2Inactive 0x80 // bit b7 +- +- +-//// second reg (c) +-#define cDeemphasisOFF 0x00 // bit c5 +-#define cDeemphasisON 0x20 // bit c5 +- +-#define cDeemphasis75 0x00 // bit c6 +-#define cDeemphasis50 0x40 // bit c6 +- +-#define cAudioGain0 0x00 // bit c7 +-#define cAudioGain6 0x80 // bit c7 +- +-#define cTopMask 0x1f // bit c0:4 +-#define cTopDefault 0x10 // bit c0:4 +- +-//// third reg (e) +-#define cAudioIF_4_5 0x00 // bit e0:1 +-#define cAudioIF_5_5 0x01 // bit e0:1 +-#define cAudioIF_6_0 0x02 // bit e0:1 +-#define cAudioIF_6_5 0x03 // bit e0:1 +- +- +-#define cVideoIFMask 0x1c // bit e2:4 +-/* Video IF selection in TV Mode (bit B3=0) */ +-#define cVideoIF_58_75 0x00 // bit e2:4 +-#define cVideoIF_45_75 0x04 // bit e2:4 +-#define cVideoIF_38_90 0x08 // bit e2:4 +-#define cVideoIF_38_00 0x0C // bit e2:4 +-#define cVideoIF_33_90 0x10 // bit e2:4 +-#define cVideoIF_33_40 0x14 // bit e2:4 +-#define cRadioIF_45_75 0x18 // bit e2:4 +-#define cRadioIF_38_90 0x1C // bit e2:4 +- +-/* IF1 selection in Radio Mode (bit B3=1) */ +-#define cRadioIF_33_30 0x00 // bit e2,4 (also 0x10,0x14) +-#define cRadioIF_41_30 0x04 // bit e2,4 +- +-/* Output of AFC pin in radio mode when bit E7=1 */ +-#define cRadioAGC_SIF 0x00 // bit e3 +-#define cRadioAGC_FM 0x08 // bit e3 +- +-#define cTunerGainNormal 0x00 // bit e5 +-#define cTunerGainLow 0x20 // bit e5 +- +-#define cGating_18 0x00 // bit e6 +-#define cGating_36 0x40 // bit e6 +- +-#define cAgcOutON 0x80 // bit e7 +-#define cAgcOutOFF 0x00 // bit e7 +- +-/* ---------------------------------------------------------------------- */ +- +-static struct tvnorm tvnorms[] = { +- { +- .std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H | V4L2_STD_PAL_N, +- .name = "PAL-BGHN", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis50 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_5_5 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_PAL_I, +- .name = "PAL-I", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis50 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_6_0 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_PAL_DK, +- .name = "PAL-DK", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis50 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_6_5 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_PAL_M | V4L2_STD_PAL_Nc, +- .name = "PAL-M/Nc", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis75 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_4_5 | +- cVideoIF_45_75 ), +- },{ +- .std = V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, +- .name = "SECAM-BGH", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cTopDefault), +- .e = ( cAudioIF_5_5 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_SECAM_L, +- .name = "SECAM-L", +- .b = ( cPositiveAmTV | +- cQSS ), +- .c = ( cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_6_5 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_SECAM_LC, +- .name = "SECAM-L'", +- .b = ( cOutputPort2Inactive | +- cPositiveAmTV | +- cQSS ), +- .c = ( cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_6_5 | +- cVideoIF_33_90 ), +- },{ +- .std = V4L2_STD_SECAM_DK, +- .name = "SECAM-DK", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis50 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_6_5 | +- cVideoIF_38_90 ), +- },{ +- .std = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, +- .name = "NTSC-M", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis75 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_4_5 | +- cVideoIF_45_75 ), +- },{ +- .std = V4L2_STD_NTSC_M_JP, +- .name = "NTSC-M-JP", +- .b = ( cNegativeFmTV | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis50 | +- cTopDefault), +- .e = ( cGating_36 | +- cAudioIF_4_5 | +- cVideoIF_58_75 ), +- } +-}; +- +-static struct tvnorm radio_stereo = { +- .name = "Radio Stereo", +- .b = ( cFmRadio | +- cQSS ), +- .c = ( cDeemphasisOFF | +- cAudioGain6 | +- cTopDefault), +- .e = ( cTunerGainLow | +- cAudioIF_5_5 | +- cRadioIF_38_90 ), +-}; +- +-static struct tvnorm radio_mono = { +- .name = "Radio Mono", +- .b = ( cFmRadio | +- cQSS ), +- .c = ( cDeemphasisON | +- cDeemphasis75 | +- cTopDefault), +- .e = ( cTunerGainLow | +- cAudioIF_5_5 | +- cRadioIF_38_90 ), +-}; +- +-/* ---------------------------------------------------------------------- */ +- +-static void dump_read_message(struct dvb_frontend *fe, unsigned char *buf) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- static char *afc[16] = { +- "- 12.5 kHz", +- "- 37.5 kHz", +- "- 62.5 kHz", +- "- 87.5 kHz", +- "-112.5 kHz", +- "-137.5 kHz", +- "-162.5 kHz", +- "-187.5 kHz [min]", +- "+187.5 kHz [max]", +- "+162.5 kHz", +- "+137.5 kHz", +- "+112.5 kHz", +- "+ 87.5 kHz", +- "+ 62.5 kHz", +- "+ 37.5 kHz", +- "+ 12.5 kHz", +- }; +- tuner_info("read: 0x%2x\n", buf[0]); +- tuner_info(" after power on : %s\n", (buf[0] & 0x01) ? "yes" : "no"); +- tuner_info(" afc : %s\n", afc[(buf[0] >> 1) & 0x0f]); +- tuner_info(" fmif level : %s\n", (buf[0] & 0x20) ? "high" : "low"); +- tuner_info(" afc window : %s\n", (buf[0] & 0x40) ? "in" : "out"); +- tuner_info(" vfi level : %s\n", (buf[0] & 0x80) ? "high" : "low"); +-} +- +-static void dump_write_message(struct dvb_frontend *fe, unsigned char *buf) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- static char *sound[4] = { +- "AM/TV", +- "FM/radio", +- "FM/TV", +- "FM/radio" +- }; +- static char *adjust[32] = { +- "-16", "-15", "-14", "-13", "-12", "-11", "-10", "-9", +- "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", +- "0", "+1", "+2", "+3", "+4", "+5", "+6", "+7", +- "+8", "+9", "+10", "+11", "+12", "+13", "+14", "+15" +- }; +- static char *deemph[4] = { +- "no", "no", "75", "50" +- }; +- static char *carrier[4] = { +- "4.5 MHz", +- "5.5 MHz", +- "6.0 MHz", +- "6.5 MHz / AM" +- }; +- static char *vif[8] = { +- "58.75 MHz", +- "45.75 MHz", +- "38.9 MHz", +- "38.0 MHz", +- "33.9 MHz", +- "33.4 MHz", +- "45.75 MHz + pin13", +- "38.9 MHz + pin13", +- }; +- static char *rif[4] = { +- "44 MHz", +- "52 MHz", +- "52 MHz", +- "44 MHz", +- }; +- +- tuner_info("write: byte B 0x%02x\n", buf[1]); +- tuner_info(" B0 video mode : %s\n", +- (buf[1] & 0x01) ? "video trap" : "sound trap"); +- tuner_info(" B1 auto mute fm : %s\n", +- (buf[1] & 0x02) ? "yes" : "no"); +- tuner_info(" B2 carrier mode : %s\n", +- (buf[1] & 0x04) ? "QSS" : "Intercarrier"); +- tuner_info(" B3-4 tv sound/radio : %s\n", +- sound[(buf[1] & 0x18) >> 3]); +- tuner_info(" B5 force mute audio: %s\n", +- (buf[1] & 0x20) ? "yes" : "no"); +- tuner_info(" B6 output port 1 : %s\n", +- (buf[1] & 0x40) ? "high (inactive)" : "low (active)"); +- tuner_info(" B7 output port 2 : %s\n", +- (buf[1] & 0x80) ? "high (inactive)" : "low (active)"); +- +- tuner_info("write: byte C 0x%02x\n", buf[2]); +- tuner_info(" C0-4 top adjustment : %s dB\n", +- adjust[buf[2] & 0x1f]); +- tuner_info(" C5-6 de-emphasis : %s\n", +- deemph[(buf[2] & 0x60) >> 5]); +- tuner_info(" C7 audio gain : %s\n", +- (buf[2] & 0x80) ? "-6" : "0"); +- +- tuner_info("write: byte E 0x%02x\n", buf[3]); +- tuner_info(" E0-1 sound carrier : %s\n", +- carrier[(buf[3] & 0x03)]); +- tuner_info(" E6 l pll gating : %s\n", +- (buf[3] & 0x40) ? "36" : "13"); +- +- if (buf[1] & 0x08) { +- /* radio */ +- tuner_info(" E2-4 video if : %s\n", +- rif[(buf[3] & 0x0c) >> 2]); +- tuner_info(" E7 vif agc output : %s\n", +- (buf[3] & 0x80) +- ? ((buf[3] & 0x10) ? "fm-agc radio" : +- "sif-agc radio") +- : "fm radio carrier afc"); +- } else { +- /* video */ +- tuner_info(" E2-4 video if : %s\n", +- vif[(buf[3] & 0x1c) >> 2]); +- tuner_info(" E5 tuner gain : %s\n", +- (buf[3] & 0x80) +- ? ((buf[3] & 0x20) ? "external" : "normal") +- : ((buf[3] & 0x20) ? "minimum" : "normal")); +- tuner_info(" E7 vif agc output : %s\n", +- (buf[3] & 0x80) ? ((buf[3] & 0x20) +- ? "pin3 port, pin22 vif agc out" +- : "pin22 port, pin3 vif acg ext in") +- : "pin3+pin22 port"); +- } +- tuner_info("--\n"); +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static int tda9887_set_tvnorm(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- struct tvnorm *norm = NULL; +- char *buf = priv->data; +- int i; +- +- if (priv->mode == V4L2_TUNER_RADIO) { +- if (priv->audmode == V4L2_TUNER_MODE_MONO) +- norm = &radio_mono; +- else +- norm = &radio_stereo; +- } else { +- for (i = 0; i < ARRAY_SIZE(tvnorms); i++) { +- if (tvnorms[i].std & priv->std) { +- norm = tvnorms+i; +- break; +- } +- } +- } +- if (NULL == norm) { +- tuner_dbg("Unsupported tvnorm entry - audio muted\n"); +- return -1; +- } +- +- tuner_dbg("configure for: %s\n", norm->name); +- buf[1] = norm->b; +- buf[2] = norm->c; +- buf[3] = norm->e; +- return 0; +-} +- +-static unsigned int port1 = UNSET; +-static unsigned int port2 = UNSET; +-static unsigned int qss = UNSET; +-static unsigned int adjust = UNSET; +- +-module_param(port1, int, 0644); +-module_param(port2, int, 0644); +-module_param(qss, int, 0644); +-module_param(adjust, int, 0644); +- +-static int tda9887_set_insmod(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- char *buf = priv->data; +- +- if (UNSET != port1) { +- if (port1) +- buf[1] |= cOutputPort1Inactive; +- else +- buf[1] &= ~cOutputPort1Inactive; +- } +- if (UNSET != port2) { +- if (port2) +- buf[1] |= cOutputPort2Inactive; +- else +- buf[1] &= ~cOutputPort2Inactive; +- } +- +- if (UNSET != qss) { +- if (qss) +- buf[1] |= cQSS; +- else +- buf[1] &= ~cQSS; +- } +- +- if (adjust < 0x20) { +- buf[2] &= ~cTopMask; +- buf[2] |= adjust; +- } +- return 0; +-} +- +-static int tda9887_do_config(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- char *buf = priv->data; +- +- if (priv->config & TDA9887_PORT1_ACTIVE) +- buf[1] &= ~cOutputPort1Inactive; +- if (priv->config & TDA9887_PORT1_INACTIVE) +- buf[1] |= cOutputPort1Inactive; +- if (priv->config & TDA9887_PORT2_ACTIVE) +- buf[1] &= ~cOutputPort2Inactive; +- if (priv->config & TDA9887_PORT2_INACTIVE) +- buf[1] |= cOutputPort2Inactive; +- +- if (priv->config & TDA9887_QSS) +- buf[1] |= cQSS; +- if (priv->config & TDA9887_INTERCARRIER) +- buf[1] &= ~cQSS; +- +- if (priv->config & TDA9887_AUTOMUTE) +- buf[1] |= cAutoMuteFmActive; +- if (priv->config & TDA9887_DEEMPHASIS_MASK) { +- buf[2] &= ~0x60; +- switch (priv->config & TDA9887_DEEMPHASIS_MASK) { +- case TDA9887_DEEMPHASIS_NONE: +- buf[2] |= cDeemphasisOFF; +- break; +- case TDA9887_DEEMPHASIS_50: +- buf[2] |= cDeemphasisON | cDeemphasis50; +- break; +- case TDA9887_DEEMPHASIS_75: +- buf[2] |= cDeemphasisON | cDeemphasis75; +- break; +- } +- } +- if (priv->config & TDA9887_TOP_SET) { +- buf[2] &= ~cTopMask; +- buf[2] |= (priv->config >> 8) & cTopMask; +- } +- if ((priv->config & TDA9887_INTERCARRIER_NTSC) && +- (priv->std & V4L2_STD_NTSC)) +- buf[1] &= ~cQSS; +- if (priv->config & TDA9887_GATING_18) +- buf[3] &= ~cGating_36; +- +- if (priv->mode == V4L2_TUNER_RADIO) { +- if (priv->config & TDA9887_RIF_41_3) { +- buf[3] &= ~cVideoIFMask; +- buf[3] |= cRadioIF_41_30; +- } +- if (priv->config & TDA9887_GAIN_NORMAL) +- buf[3] &= ~cTunerGainLow; +- } +- +- return 0; +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static int tda9887_status(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- unsigned char buf[1]; +- int rc; +- +- memset(buf,0,sizeof(buf)); +- if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,buf,1))) +- tuner_info("i2c i/o error: rc == %d (should be 1)\n", rc); +- dump_read_message(fe, buf); +- return 0; +-} +- +-static void tda9887_configure(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- int rc; +- +- memset(priv->data,0,sizeof(priv->data)); +- tda9887_set_tvnorm(fe); +- +- /* A note on the port settings: +- These settings tend to depend on the specifics of the board. +- By default they are set to inactive (bit value 1) by this driver, +- overwriting any changes made by the tvnorm. This means that it +- is the responsibility of the module using the tda9887 to set +- these values in case of changes in the tvnorm. +- In many cases port 2 should be made active (0) when selecting +- SECAM-L, and port 2 should remain inactive (1) for SECAM-L'. +- +- For the other standards the tda9887 application note says that +- the ports should be set to active (0), but, again, that may +- differ depending on the precise hardware configuration. +- */ +- priv->data[1] |= cOutputPort1Inactive; +- priv->data[1] |= cOutputPort2Inactive; +- +- tda9887_do_config(fe); +- tda9887_set_insmod(fe); +- +- if (priv->standby) +- priv->data[1] |= cForcedMuteAudioON; +- +- tuner_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", +- priv->data[1], priv->data[2], priv->data[3]); +- if (debug > 1) +- dump_write_message(fe, priv->data); +- +- if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,priv->data,4))) +- tuner_info("i2c i/o error: rc == %d (should be 4)\n", rc); +- +- if (debug > 2) { +- msleep_interruptible(1000); +- tda9887_status(fe); +- } +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static void tda9887_tuner_status(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- tuner_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", +- priv->data[1], priv->data[2], priv->data[3]); +-} +- +-static int tda9887_get_afc(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- static int AFC_BITS_2_kHz[] = { +- -12500, -37500, -62500, -97500, +- -112500, -137500, -162500, -187500, +- 187500, 162500, 137500, 112500, +- 97500 , 62500, 37500 , 12500 +- }; +- int afc=0; +- __u8 reg = 0; +- +- if (1 == tuner_i2c_xfer_recv(&priv->i2c_props,®,1)) +- afc = AFC_BITS_2_kHz[(reg>>1)&0x0f]; +- +- return afc; +-} +- +-static void tda9887_standby(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- priv->standby = true; +- +- tda9887_configure(fe); +-} +- +-static void tda9887_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- priv->standby = false; +- priv->mode = params->mode; +- priv->audmode = params->audmode; +- priv->std = params->std; +- tda9887_configure(fe); +-} +- +-static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- priv->config = *(unsigned int *)priv_cfg; +- tda9887_configure(fe); +- +- return 0; +-} +- +-static void tda9887_release(struct dvb_frontend *fe) +-{ +- struct tda9887_priv *priv = fe->analog_demod_priv; +- +- mutex_lock(&tda9887_list_mutex); +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&tda9887_list_mutex); +- +- fe->analog_demod_priv = NULL; +-} +- +-static struct analog_demod_ops tda9887_ops = { +- .info = { +- .name = "tda9887", +- }, +- .set_params = tda9887_set_params, +- .standby = tda9887_standby, +- .tuner_status = tda9887_tuner_status, +- .get_afc = tda9887_get_afc, +- .release = tda9887_release, +- .set_config = tda9887_set_config, +-}; +- +-struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr) +-{ +- struct tda9887_priv *priv = NULL; +- int instance; +- +- mutex_lock(&tda9887_list_mutex); +- +- instance = hybrid_tuner_request_state(struct tda9887_priv, priv, +- hybrid_tuner_instance_list, +- i2c_adap, i2c_addr, "tda9887"); +- switch (instance) { +- case 0: +- mutex_unlock(&tda9887_list_mutex); +- return NULL; +- case 1: +- fe->analog_demod_priv = priv; +- priv->standby = true; +- tuner_info("tda988[5/6/7] found\n"); +- break; +- default: +- fe->analog_demod_priv = priv; +- break; +- } +- +- mutex_unlock(&tda9887_list_mutex); +- +- memcpy(&fe->ops.analog_ops, &tda9887_ops, +- sizeof(struct analog_demod_ops)); +- +- return fe; +-} +-EXPORT_SYMBOL_GPL(tda9887_attach); +- +-MODULE_LICENSE("GPL"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tda9887.h b/drivers/media/common/tuners/tda9887.h +deleted file mode 100644 +index acc419e..0000000 +--- a/drivers/media/common/tuners/tda9887.h ++++ /dev/null +@@ -1,38 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA9887_H__ +-#define __TDA9887_H__ +- +-#include +-#include "dvb_frontend.h" +- +-/* ------------------------------------------------------------------------ */ +-#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr); +-#else +-static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TDA9887_H__ */ +diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/common/tuners/tea5761.c +deleted file mode 100644 +index bf78cb9..0000000 +--- a/drivers/media/common/tuners/tea5761.c ++++ /dev/null +@@ -1,348 +0,0 @@ +-/* +- * For Philips TEA5761 FM Chip +- * I2C address is allways 0x20 (0x10 at 7-bit mode). +- * +- * Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org) +- * This code is placed under the terms of the GNUv2 General Public License +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tea5761.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-struct tea5761_priv { +- struct tuner_i2c_props i2c_props; +- +- u32 frequency; +- bool standby; +-}; +- +-/*****************************************************************************/ +- +-/*************************** +- * TEA5761HN I2C registers * +- ***************************/ +- +-/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */ +- +- /* first byte for reading */ +-#define TEA5761_INTREG_IFFLAG 0x10 +-#define TEA5761_INTREG_LEVFLAG 0x8 +-#define TEA5761_INTREG_FRRFLAG 0x2 +-#define TEA5761_INTREG_BLFLAG 0x1 +- +- /* second byte for reading / byte for writing */ +-#define TEA5761_INTREG_IFMSK 0x10 +-#define TEA5761_INTREG_LEVMSK 0x8 +-#define TEA5761_INTREG_FRMSK 0x2 +-#define TEA5761_INTREG_BLMSK 0x1 +- +-/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */ +- +- /* First byte */ +-#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */ +-#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */ +- +- /* Bits 0-5 for divider MSB */ +- +- /* Second byte */ +- /* Bits 0-7 for divider LSB */ +- +-/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */ +- +- /* first byte */ +- +-#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */ +-#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */ +-#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */ +-#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */ +-#define TEA5761_TNCTRL_AFM 0x04 +-#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */ +-#define TEA5761_TNCTRL_SNC 0x01 +- +- /* second byte */ +- +-#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */ +-#define TEA5761_TNCTRL_SSL_1 0x40 +-#define TEA5761_TNCTRL_SSL_0 0x20 +-#define TEA5761_TNCTRL_HLSI 0x10 +-#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */ +-#define TEA5761_TNCTRL_SWP 0x04 +-#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */ +-#define TEA5761_TNCTRL_AHLSI 0x01 +- +-/* FRQCHECK - Read: bytes 6 and 7 */ +- /* First byte */ +- +- /* Bits 0-5 for divider MSB */ +- +- /* Second byte */ +- /* Bits 0-7 for divider LSB */ +- +-/* TUNCHECK - Read: bytes 8 and 9 */ +- +- /* First byte */ +-#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */ +-#define TEA5761_TUNCHECK_TUNTO 0x01 +- +- /* Second byte */ +-#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */ +-#define TEA5761_TUNCHECK_LD 0x08 +-#define TEA5761_TUNCHECK_STEREO 0x04 +- +-/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */ +- +- /* All zero = no test mode */ +- +-/* MANID - Read: bytes 12 and 13 */ +- +- /* First byte - should be 0x10 */ +-#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */ +-#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */ +- +- /* Second byte - Should be 0x2b */ +- +-#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */ +-#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */ +- +-/* Chip ID - Read: bytes 14 and 15 */ +- +- /* First byte - should be 0x57 */ +- +- /* Second byte - should be 0x61 */ +- +-/*****************************************************************************/ +- +-#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */ +-static void tea5761_status_dump(unsigned char *buffer) +-{ +- unsigned int div, frq; +- +- div = ((buffer[2] & 0x3f) << 8) | buffer[3]; +- +- frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */ +- +- printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n", +- frq / 1000, frq % 1000, div); +-} +- +-/* Freq should be specifyed at 62.5 Hz */ +-static int __set_radio_freq(struct dvb_frontend *fe, +- unsigned int freq, +- bool mono) +-{ +- struct tea5761_priv *priv = fe->tuner_priv; +- unsigned int frq = freq; +- unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 }; +- unsigned div; +- int rc; +- +- tuner_dbg("radio freq counter %d\n", frq); +- +- if (priv->standby) { +- tuner_dbg("TEA5761 set to standby mode\n"); +- buffer[5] |= TEA5761_TNCTRL_MU; +- } else { +- buffer[4] |= TEA5761_TNCTRL_PUPD_0; +- } +- +- +- if (mono) { +- tuner_dbg("TEA5761 set to mono\n"); +- buffer[5] |= TEA5761_TNCTRL_MST; +- } else { +- tuner_dbg("TEA5761 set to stereo\n"); +- } +- +- div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15; +- buffer[1] = (div >> 8) & 0x3f; +- buffer[2] = div & 0xff; +- +- if (debug) +- tea5761_status_dump(buffer); +- +- if (7 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 7))) +- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +- +- priv->frequency = frq * 125 / 2; +- +- return 0; +-} +- +-static int set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tea5761_priv *priv = fe->analog_demod_priv; +- +- priv->standby = false; +- +- return __set_radio_freq(fe, params->frequency, +- params->audmode == V4L2_TUNER_MODE_MONO); +-} +- +-static int set_radio_sleep(struct dvb_frontend *fe) +-{ +- struct tea5761_priv *priv = fe->analog_demod_priv; +- +- priv->standby = true; +- +- return __set_radio_freq(fe, priv->frequency, false); +-} +- +-static int tea5761_read_status(struct dvb_frontend *fe, char *buffer) +-{ +- struct tea5761_priv *priv = fe->tuner_priv; +- int rc; +- +- memset(buffer, 0, 16); +- if (16 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 16))) { +- tuner_warn("i2c i/o error: rc == %d (should be 16)\n", rc); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static inline int tea5761_signal(struct dvb_frontend *fe, const char *buffer) +-{ +- struct tea5761_priv *priv = fe->tuner_priv; +- +- int signal = ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4)); +- +- tuner_dbg("Signal strength: %d\n", signal); +- +- return signal; +-} +- +-static inline int tea5761_stereo(struct dvb_frontend *fe, const char *buffer) +-{ +- struct tea5761_priv *priv = fe->tuner_priv; +- +- int stereo = buffer[9] & TEA5761_TUNCHECK_STEREO; +- +- tuner_dbg("Radio ST GET = %02x\n", stereo); +- +- return (stereo ? V4L2_TUNER_SUB_STEREO : 0); +-} +- +-static int tea5761_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- unsigned char buffer[16]; +- +- *status = 0; +- +- if (0 == tea5761_read_status(fe, buffer)) { +- if (tea5761_signal(fe, buffer)) +- *status = TUNER_STATUS_LOCKED; +- if (tea5761_stereo(fe, buffer)) +- *status |= TUNER_STATUS_STEREO; +- } +- +- return 0; +-} +- +-static int tea5761_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- unsigned char buffer[16]; +- +- *strength = 0; +- +- if (0 == tea5761_read_status(fe, buffer)) +- *strength = tea5761_signal(fe, buffer); +- +- return 0; +-} +- +-int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) +-{ +- unsigned char buffer[16]; +- int rc; +- struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; +- +- if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) { +- printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc); +- return -EINVAL; +- } +- +- if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) { +- printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x." +- " It is not a TEA5761\n", +- buffer[13], buffer[14], buffer[15]); +- return -EINVAL; +- } +- printk(KERN_WARNING "tea5761: TEA%02x%02x detected. " +- "Manufacturer ID= 0x%02x\n", +- buffer[14], buffer[15], buffer[13]); +- +- return 0; +-} +- +-static int tea5761_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int tea5761_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tea5761_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static struct dvb_tuner_ops tea5761_tuner_ops = { +- .info = { +- .name = "tea5761", // Philips TEA5761HN FM Radio +- }, +- .set_analog_params = set_radio_freq, +- .sleep = set_radio_sleep, +- .release = tea5761_release, +- .get_frequency = tea5761_get_frequency, +- .get_status = tea5761_get_status, +- .get_rf_strength = tea5761_get_rf_strength, +-}; +- +-struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- struct tea5761_priv *priv = NULL; +- +- if (tea5761_autodetection(i2c_adap, i2c_addr) != 0) +- return NULL; +- +- priv = kzalloc(sizeof(struct tea5761_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- fe->tuner_priv = priv; +- +- priv->i2c_props.addr = i2c_addr; +- priv->i2c_props.adap = i2c_adap; +- priv->i2c_props.name = "tea5761"; +- +- memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- tuner_info("type set to %s\n", "Philips TEA5761HN FM Radio"); +- +- return fe; +-} +- +- +-EXPORT_SYMBOL_GPL(tea5761_attach); +-EXPORT_SYMBOL_GPL(tea5761_autodetection); +- +-MODULE_DESCRIPTION("Philips TEA5761 FM tuner driver"); +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tea5761.h b/drivers/media/common/tuners/tea5761.h +deleted file mode 100644 +index 2e2ff82..0000000 +--- a/drivers/media/common/tuners/tea5761.h ++++ /dev/null +@@ -1,47 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TEA5761_H__ +-#define __TEA5761_H__ +- +-#include +-#include "dvb_frontend.h" +- +-#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) +-extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); +- +-extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr); +-#else +-static inline int tea5761_autodetection(struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", +- __func__); +- return -EINVAL; +-} +- +-static inline struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TEA5761_H__ */ +diff --git a/drivers/media/common/tuners/tea5767.c b/drivers/media/common/tuners/tea5767.c +deleted file mode 100644 +index 36e85d8..0000000 +--- a/drivers/media/common/tuners/tea5767.c ++++ /dev/null +@@ -1,475 +0,0 @@ +-/* +- * For Philips TEA5767 FM Chip used on some TV Cards like Prolink Pixelview +- * I2C address is allways 0xC0. +- * +- * +- * Copyright (c) 2005 Mauro Carvalho Chehab (mchehab@infradead.org) +- * This code is placed under the terms of the GNU General Public License +- * +- * tea5767 autodetection thanks to Torsten Seeboth and Atsushi Nakagawa +- * from their contributions on DScaler. +- */ +- +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tea5767.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-/*****************************************************************************/ +- +-struct tea5767_priv { +- struct tuner_i2c_props i2c_props; +- u32 frequency; +- struct tea5767_ctrl ctrl; +-}; +- +-/*****************************************************************************/ +- +-/****************************** +- * Write mode register values * +- ******************************/ +- +-/* First register */ +-#define TEA5767_MUTE 0x80 /* Mutes output */ +-#define TEA5767_SEARCH 0x40 /* Activates station search */ +-/* Bits 0-5 for divider MSB */ +- +-/* Second register */ +-/* Bits 0-7 for divider LSB */ +- +-/* Third register */ +- +-/* Station search from botton to up */ +-#define TEA5767_SEARCH_UP 0x80 +- +-/* Searches with ADC output = 10 */ +-#define TEA5767_SRCH_HIGH_LVL 0x60 +- +-/* Searches with ADC output = 10 */ +-#define TEA5767_SRCH_MID_LVL 0x40 +- +-/* Searches with ADC output = 5 */ +-#define TEA5767_SRCH_LOW_LVL 0x20 +- +-/* if on, div=4*(Frf+Fif)/Fref otherwise, div=4*(Frf-Fif)/Freq) */ +-#define TEA5767_HIGH_LO_INJECT 0x10 +- +-/* Disable stereo */ +-#define TEA5767_MONO 0x08 +- +-/* Disable right channel and turns to mono */ +-#define TEA5767_MUTE_RIGHT 0x04 +- +-/* Disable left channel and turns to mono */ +-#define TEA5767_MUTE_LEFT 0x02 +- +-#define TEA5767_PORT1_HIGH 0x01 +- +-/* Fourth register */ +-#define TEA5767_PORT2_HIGH 0x80 +-/* Chips stops working. Only I2C bus remains on */ +-#define TEA5767_STDBY 0x40 +- +-/* Japan freq (76-108 MHz. If disabled, 87.5-108 MHz */ +-#define TEA5767_JAPAN_BAND 0x20 +- +-/* Unselected means 32.768 KHz freq as reference. Otherwise Xtal at 13 MHz */ +-#define TEA5767_XTAL_32768 0x10 +- +-/* Cuts weak signals */ +-#define TEA5767_SOFT_MUTE 0x08 +- +-/* Activates high cut control */ +-#define TEA5767_HIGH_CUT_CTRL 0x04 +- +-/* Activates stereo noise control */ +-#define TEA5767_ST_NOISE_CTL 0x02 +- +-/* If activate PORT 1 indicates SEARCH or else it is used as PORT1 */ +-#define TEA5767_SRCH_IND 0x01 +- +-/* Fifth register */ +- +-/* By activating, it will use Xtal at 13 MHz as reference for divider */ +-#define TEA5767_PLLREF_ENABLE 0x80 +- +-/* By activating, deemphasis=50, or else, deemphasis of 50us */ +-#define TEA5767_DEEMPH_75 0X40 +- +-/***************************** +- * Read mode register values * +- *****************************/ +- +-/* First register */ +-#define TEA5767_READY_FLAG_MASK 0x80 +-#define TEA5767_BAND_LIMIT_MASK 0X40 +-/* Bits 0-5 for divider MSB after search or preset */ +- +-/* Second register */ +-/* Bits 0-7 for divider LSB after search or preset */ +- +-/* Third register */ +-#define TEA5767_STEREO_MASK 0x80 +-#define TEA5767_IF_CNTR_MASK 0x7f +- +-/* Fourth register */ +-#define TEA5767_ADC_LEVEL_MASK 0xf0 +- +-/* should be 0 */ +-#define TEA5767_CHIP_ID_MASK 0x0f +- +-/* Fifth register */ +-/* Reserved for future extensions */ +-#define TEA5767_RESERVED_MASK 0xff +- +-/*****************************************************************************/ +- +-static void tea5767_status_dump(struct tea5767_priv *priv, +- unsigned char *buffer) +-{ +- unsigned int div, frq; +- +- if (TEA5767_READY_FLAG_MASK & buffer[0]) +- tuner_info("Ready Flag ON\n"); +- else +- tuner_info("Ready Flag OFF\n"); +- +- if (TEA5767_BAND_LIMIT_MASK & buffer[0]) +- tuner_info("Tuner at band limit\n"); +- else +- tuner_info("Tuner not at band limit\n"); +- +- div = ((buffer[0] & 0x3f) << 8) | buffer[1]; +- +- switch (priv->ctrl.xtal_freq) { +- case TEA5767_HIGH_LO_13MHz: +- frq = (div * 50000 - 700000 - 225000) / 4; /* Freq in KHz */ +- break; +- case TEA5767_LOW_LO_13MHz: +- frq = (div * 50000 + 700000 + 225000) / 4; /* Freq in KHz */ +- break; +- case TEA5767_LOW_LO_32768: +- frq = (div * 32768 + 700000 + 225000) / 4; /* Freq in KHz */ +- break; +- case TEA5767_HIGH_LO_32768: +- default: +- frq = (div * 32768 - 700000 - 225000) / 4; /* Freq in KHz */ +- break; +- } +- buffer[0] = (div >> 8) & 0x3f; +- buffer[1] = div & 0xff; +- +- tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", +- frq / 1000, frq % 1000, div); +- +- if (TEA5767_STEREO_MASK & buffer[2]) +- tuner_info("Stereo\n"); +- else +- tuner_info("Mono\n"); +- +- tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); +- +- tuner_info("ADC Level = %d\n", +- (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); +- +- tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); +- +- tuner_info("Reserved = 0x%02x\n", +- (buffer[4] & TEA5767_RESERVED_MASK)); +-} +- +-/* Freq should be specifyed at 62.5 Hz */ +-static int set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- unsigned int frq = params->frequency; +- unsigned char buffer[5]; +- unsigned div; +- int rc; +- +- tuner_dbg("radio freq = %d.%03d MHz\n", frq/16000,(frq/16)%1000); +- +- buffer[2] = 0; +- +- if (priv->ctrl.port1) +- buffer[2] |= TEA5767_PORT1_HIGH; +- +- if (params->audmode == V4L2_TUNER_MODE_MONO) { +- tuner_dbg("TEA5767 set to mono\n"); +- buffer[2] |= TEA5767_MONO; +- } else { +- tuner_dbg("TEA5767 set to stereo\n"); +- } +- +- +- buffer[3] = 0; +- +- if (priv->ctrl.port2) +- buffer[3] |= TEA5767_PORT2_HIGH; +- +- if (priv->ctrl.high_cut) +- buffer[3] |= TEA5767_HIGH_CUT_CTRL; +- +- if (priv->ctrl.st_noise) +- buffer[3] |= TEA5767_ST_NOISE_CTL; +- +- if (priv->ctrl.soft_mute) +- buffer[3] |= TEA5767_SOFT_MUTE; +- +- if (priv->ctrl.japan_band) +- buffer[3] |= TEA5767_JAPAN_BAND; +- +- buffer[4] = 0; +- +- if (priv->ctrl.deemph_75) +- buffer[4] |= TEA5767_DEEMPH_75; +- +- if (priv->ctrl.pllref) +- buffer[4] |= TEA5767_PLLREF_ENABLE; +- +- +- /* Rounds freq to next decimal value - for 62.5 KHz step */ +- /* frq = 20*(frq/16)+radio_frq[frq%16]; */ +- +- switch (priv->ctrl.xtal_freq) { +- case TEA5767_HIGH_LO_13MHz: +- tuner_dbg("radio HIGH LO inject xtal @ 13 MHz\n"); +- buffer[2] |= TEA5767_HIGH_LO_INJECT; +- div = (frq * (4000 / 16) + 700000 + 225000 + 25000) / 50000; +- break; +- case TEA5767_LOW_LO_13MHz: +- tuner_dbg("radio LOW LO inject xtal @ 13 MHz\n"); +- +- div = (frq * (4000 / 16) - 700000 - 225000 + 25000) / 50000; +- break; +- case TEA5767_LOW_LO_32768: +- tuner_dbg("radio LOW LO inject xtal @ 32,768 MHz\n"); +- buffer[3] |= TEA5767_XTAL_32768; +- /* const 700=4000*175 Khz - to adjust freq to right value */ +- div = ((frq * (4000 / 16) - 700000 - 225000) + 16384) >> 15; +- break; +- case TEA5767_HIGH_LO_32768: +- default: +- tuner_dbg("radio HIGH LO inject xtal @ 32,768 MHz\n"); +- +- buffer[2] |= TEA5767_HIGH_LO_INJECT; +- buffer[3] |= TEA5767_XTAL_32768; +- div = ((frq * (4000 / 16) + 700000 + 225000) + 16384) >> 15; +- break; +- } +- buffer[0] = (div >> 8) & 0x3f; +- buffer[1] = div & 0xff; +- +- if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) +- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +- +- if (debug) { +- if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) +- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +- else +- tea5767_status_dump(priv, buffer); +- } +- +- priv->frequency = frq * 125 / 2; +- +- return 0; +-} +- +-static int tea5767_read_status(struct dvb_frontend *fe, char *buffer) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- int rc; +- +- memset(buffer, 0, 5); +- if (5 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props, buffer, 5))) { +- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static inline int tea5767_signal(struct dvb_frontend *fe, const char *buffer) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- +- int signal = ((buffer[3] & TEA5767_ADC_LEVEL_MASK) << 8); +- +- tuner_dbg("Signal strength: %d\n", signal); +- +- return signal; +-} +- +-static inline int tea5767_stereo(struct dvb_frontend *fe, const char *buffer) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- +- int stereo = buffer[2] & TEA5767_STEREO_MASK; +- +- tuner_dbg("Radio ST GET = %02x\n", stereo); +- +- return (stereo ? V4L2_TUNER_SUB_STEREO : 0); +-} +- +-static int tea5767_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- unsigned char buffer[5]; +- +- *status = 0; +- +- if (0 == tea5767_read_status(fe, buffer)) { +- if (tea5767_signal(fe, buffer)) +- *status = TUNER_STATUS_LOCKED; +- if (tea5767_stereo(fe, buffer)) +- *status |= TUNER_STATUS_STEREO; +- } +- +- return 0; +-} +- +-static int tea5767_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- unsigned char buffer[5]; +- +- *strength = 0; +- +- if (0 == tea5767_read_status(fe, buffer)) +- *strength = tea5767_signal(fe, buffer); +- +- return 0; +-} +- +-static int tea5767_standby(struct dvb_frontend *fe) +-{ +- unsigned char buffer[5]; +- struct tea5767_priv *priv = fe->tuner_priv; +- unsigned div, rc; +- +- div = (87500 * 4 + 700 + 225 + 25) / 50; /* Set frequency to 87.5 MHz */ +- buffer[0] = (div >> 8) & 0x3f; +- buffer[1] = div & 0xff; +- buffer[2] = TEA5767_PORT1_HIGH; +- buffer[3] = TEA5767_PORT2_HIGH | TEA5767_HIGH_CUT_CTRL | +- TEA5767_ST_NOISE_CTL | TEA5767_JAPAN_BAND | TEA5767_STDBY; +- buffer[4] = 0; +- +- if (5 != (rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 5))) +- tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +- +- return 0; +-} +- +-int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) +-{ +- struct tuner_i2c_props i2c = { .adap = i2c_adap, .addr = i2c_addr }; +- unsigned char buffer[7] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +- int rc; +- +- if ((rc = tuner_i2c_xfer_recv(&i2c, buffer, 7))< 5) { +- printk(KERN_WARNING "It is not a TEA5767. Received %i bytes.\n", rc); +- return -EINVAL; +- } +- +- /* If all bytes are the same then it's a TV tuner and not a tea5767 */ +- if (buffer[0] == buffer[1] && buffer[0] == buffer[2] && +- buffer[0] == buffer[3] && buffer[0] == buffer[4]) { +- printk(KERN_WARNING "All bytes are equal. It is not a TEA5767\n"); +- return -EINVAL; +- } +- +- /* Status bytes: +- * Byte 4: bit 3:1 : CI (Chip Identification) == 0 +- * bit 0 : internally set to 0 +- * Byte 5: bit 7:0 : == 0 +- */ +- if (((buffer[3] & 0x0f) != 0x00) || (buffer[4] != 0x00)) { +- printk(KERN_WARNING "Chip ID is not zero. It is not a TEA5767\n"); +- return -EINVAL; +- } +- +- +- return 0; +-} +- +-static int tea5767_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int tea5767_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- +- return 0; +-} +- +-static int tea5767_set_config (struct dvb_frontend *fe, void *priv_cfg) +-{ +- struct tea5767_priv *priv = fe->tuner_priv; +- +- memcpy(&priv->ctrl, priv_cfg, sizeof(priv->ctrl)); +- +- return 0; +-} +- +-static struct dvb_tuner_ops tea5767_tuner_ops = { +- .info = { +- .name = "tea5767", // Philips TEA5767HN FM Radio +- }, +- +- .set_analog_params = set_radio_freq, +- .set_config = tea5767_set_config, +- .sleep = tea5767_standby, +- .release = tea5767_release, +- .get_frequency = tea5767_get_frequency, +- .get_status = tea5767_get_status, +- .get_rf_strength = tea5767_get_rf_strength, +-}; +- +-struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- struct tea5767_priv *priv = NULL; +- +- priv = kzalloc(sizeof(struct tea5767_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- fe->tuner_priv = priv; +- +- priv->i2c_props.addr = i2c_addr; +- priv->i2c_props.adap = i2c_adap; +- priv->i2c_props.name = "tea5767"; +- +- priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; +- priv->ctrl.port1 = 1; +- priv->ctrl.port2 = 1; +- priv->ctrl.high_cut = 1; +- priv->ctrl.st_noise = 1; +- priv->ctrl.japan_band = 1; +- +- memcpy(&fe->ops.tuner_ops, &tea5767_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- tuner_info("type set to %s\n", "Philips TEA5767HN FM Radio"); +- +- return fe; +-} +- +-EXPORT_SYMBOL_GPL(tea5767_attach); +-EXPORT_SYMBOL_GPL(tea5767_autodetection); +- +-MODULE_DESCRIPTION("Philips TEA5767 FM tuner driver"); +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tea5767.h b/drivers/media/common/tuners/tea5767.h +deleted file mode 100644 +index d30ab1b..0000000 +--- a/drivers/media/common/tuners/tea5767.h ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TEA5767_H__ +-#define __TEA5767_H__ +- +-#include +-#include "dvb_frontend.h" +- +-enum tea5767_xtal { +- TEA5767_LOW_LO_32768 = 0, +- TEA5767_HIGH_LO_32768 = 1, +- TEA5767_LOW_LO_13MHz = 2, +- TEA5767_HIGH_LO_13MHz = 3, +-}; +- +-struct tea5767_ctrl { +- unsigned int port1:1; +- unsigned int port2:1; +- unsigned int high_cut:1; +- unsigned int st_noise:1; +- unsigned int soft_mute:1; +- unsigned int japan_band:1; +- unsigned int deemph_75:1; +- unsigned int pllref:1; +- enum tea5767_xtal xtal_freq; +-}; +- +-#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE)) +-extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr); +- +-extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr); +-#else +-static inline int tea5767_autodetection(struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", +- __func__); +- return -EINVAL; +-} +- +-static inline struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, +- struct i2c_adapter* i2c_adap, +- u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TEA5767_H__ */ +diff --git a/drivers/media/common/tuners/tuner-i2c.h b/drivers/media/common/tuners/tuner-i2c.h +deleted file mode 100644 +index 18f0056..0000000 +--- a/drivers/media/common/tuners/tuner-i2c.h ++++ /dev/null +@@ -1,182 +0,0 @@ +-/* +- tuner-i2c.h - i2c interface for different tuners +- +- Copyright (C) 2007 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 as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TUNER_I2C_H__ +-#define __TUNER_I2C_H__ +- +-#include +-#include +- +-struct tuner_i2c_props { +- u8 addr; +- struct i2c_adapter *adap; +- +- /* used for tuner instance management */ +- int count; +- char *name; +-}; +- +-static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len) +-{ +- struct i2c_msg msg = { .addr = props->addr, .flags = 0, +- .buf = buf, .len = len }; +- int ret = i2c_transfer(props->adap, &msg, 1); +- +- return (ret == 1) ? len : ret; +-} +- +-static inline int tuner_i2c_xfer_recv(struct tuner_i2c_props *props, char *buf, int len) +-{ +- struct i2c_msg msg = { .addr = props->addr, .flags = I2C_M_RD, +- .buf = buf, .len = len }; +- int ret = i2c_transfer(props->adap, &msg, 1); +- +- return (ret == 1) ? len : ret; +-} +- +-static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, +- char *obuf, int olen, +- char *ibuf, int ilen) +-{ +- struct i2c_msg msg[2] = { { .addr = props->addr, .flags = 0, +- .buf = obuf, .len = olen }, +- { .addr = props->addr, .flags = I2C_M_RD, +- .buf = ibuf, .len = ilen } }; +- int ret = i2c_transfer(props->adap, msg, 2); +- +- return (ret == 2) ? ilen : ret; +-} +- +-/* Callers must declare as a global for the module: +- * +- * static LIST_HEAD(hybrid_tuner_instance_list); +- * +- * hybrid_tuner_instance_list should be the third argument +- * passed into hybrid_tuner_request_state(). +- * +- * state structure must contain the following: +- * +- * struct list_head hybrid_tuner_instance_list; +- * struct tuner_i2c_props i2c_props; +- * +- * hybrid_tuner_instance_list (both within state structure and globally) +- * is only required if the driver is using hybrid_tuner_request_state +- * and hybrid_tuner_release_state to manage state sharing between +- * multiple instances of hybrid tuners. +- */ +- +-#define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ +- printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ +- i2cprops.adap ? \ +- i2c_adapter_id(i2cprops.adap) : -1, \ +- i2cprops.addr, ##arg); \ +- } while (0) +- +-/* TO DO: convert all callers of these macros to pass in +- * struct tuner_i2c_props, then remove the macro wrappers */ +- +-#define __tuner_warn(i2cprops, fmt, arg...) do { \ +- tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \ +- } while (0) +- +-#define __tuner_info(i2cprops, fmt, arg...) do { \ +- tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \ +- } while (0) +- +-#define __tuner_err(i2cprops, fmt, arg...) do { \ +- tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \ +- } while (0) +- +-#define __tuner_dbg(i2cprops, fmt, arg...) do { \ +- if ((debug)) \ +- tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \ +- } while (0) +- +-#define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg) +-#define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg) +-#define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg) +-#define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg) +- +-/****************************************************************************/ +- +-/* The return value of hybrid_tuner_request_state indicates the number of +- * instances using this tuner object. +- * +- * 0 - no instances, indicates an error - kzalloc must have failed +- * +- * 1 - one instance, indicates that the tuner object was created successfully +- * +- * 2 (or more) instances, indicates that an existing tuner object was found +- */ +- +-#define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\ +-({ \ +- int __ret = 0; \ +- list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ +- if (((i2cadap) && (state->i2c_props.adap)) && \ +- ((i2c_adapter_id(state->i2c_props.adap) == \ +- i2c_adapter_id(i2cadap)) && \ +- (i2caddr == state->i2c_props.addr))) { \ +- __tuner_info(state->i2c_props, \ +- "attaching existing instance\n"); \ +- state->i2c_props.count++; \ +- __ret = state->i2c_props.count; \ +- break; \ +- } \ +- } \ +- if (0 == __ret) { \ +- state = kzalloc(sizeof(type), GFP_KERNEL); \ +- if (NULL == state) \ +- goto __fail; \ +- state->i2c_props.addr = i2caddr; \ +- state->i2c_props.adap = i2cadap; \ +- state->i2c_props.name = devname; \ +- __tuner_info(state->i2c_props, \ +- "creating new instance\n"); \ +- list_add_tail(&state->hybrid_tuner_instance_list, &list);\ +- state->i2c_props.count++; \ +- __ret = state->i2c_props.count; \ +- } \ +-__fail: \ +- __ret; \ +-}) +- +-#define hybrid_tuner_release_state(state) \ +-({ \ +- int __ret; \ +- state->i2c_props.count--; \ +- __ret = state->i2c_props.count; \ +- if (!state->i2c_props.count) { \ +- __tuner_info(state->i2c_props, "destroying instance\n");\ +- list_del(&state->hybrid_tuner_instance_list); \ +- kfree(state); \ +- } \ +- __ret; \ +-}) +- +-#define hybrid_tuner_report_instance_count(state) \ +-({ \ +- int __ret = 0; \ +- if (state) \ +- __ret = state->i2c_props.count; \ +- __ret; \ +-}) +- +-#endif /* __TUNER_I2C_H__ */ +diff --git a/drivers/media/common/tuners/tuner-simple.c b/drivers/media/common/tuners/tuner-simple.c +deleted file mode 100644 +index 39e7e58..0000000 +--- a/drivers/media/common/tuners/tuner-simple.c ++++ /dev/null +@@ -1,1155 +0,0 @@ +-/* +- * i2c tv tuner chip device driver +- * controls all those simple 4-control-bytes style tuners. +- * +- * This "tuner-simple" module was split apart from the original "tuner" module. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tuner-simple.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-#define TUNER_SIMPLE_MAX 64 +-static unsigned int simple_devcount; +- +-static int offset; +-module_param(offset, int, 0664); +-MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner"); +- +-static unsigned int atv_input[TUNER_SIMPLE_MAX] = \ +- { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +-static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \ +- { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +-module_param_array(atv_input, int, NULL, 0644); +-module_param_array(dtv_input, int, NULL, 0644); +-MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect"); +-MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect"); +- +-/* ---------------------------------------------------------------------- */ +- +-/* tv standard selection for Temic 4046 FM5 +- this value takes the low bits of control byte 2 +- from datasheet Rev.01, Feb.00 +- standard BG I L L2 D +- picture IF 38.9 38.9 38.9 33.95 38.9 +- sound 1 33.4 32.9 32.4 40.45 32.4 +- sound 2 33.16 +- NICAM 33.05 32.348 33.05 33.05 +- */ +-#define TEMIC_SET_PAL_I 0x05 +-#define TEMIC_SET_PAL_DK 0x09 +-#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */ +-#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */ +-#define TEMIC_SET_PAL_BG 0x0c +- +-/* tv tuner system standard selection for Philips FQ1216ME +- this value takes the low bits of control byte 2 +- from datasheet "1999 Nov 16" (supersedes "1999 Mar 23") +- standard BG DK I L L` +- picture carrier 38.90 38.90 38.90 38.90 33.95 +- colour 34.47 34.47 34.47 34.47 38.38 +- sound 1 33.40 32.40 32.90 32.40 40.45 +- sound 2 33.16 - - - - +- NICAM 33.05 33.05 32.35 33.05 39.80 +- */ +-#define PHILIPS_SET_PAL_I 0x01 /* Bit 2 always zero !*/ +-#define PHILIPS_SET_PAL_BGDK 0x09 +-#define PHILIPS_SET_PAL_L2 0x0a +-#define PHILIPS_SET_PAL_L 0x0b +- +-/* system switching for Philips FI1216MF MK2 +- from datasheet "1996 Jul 09", +- standard BG L L' +- picture carrier 38.90 38.90 33.95 +- colour 34.47 34.37 38.38 +- sound 1 33.40 32.40 40.45 +- sound 2 33.16 - - +- NICAM 33.05 33.05 39.80 +- */ +-#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ +-#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */ +-#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */ +- +-/* Control byte */ +- +-#define TUNER_RATIO_MASK 0x06 /* Bit cb1:cb2 */ +-#define TUNER_RATIO_SELECT_50 0x00 +-#define TUNER_RATIO_SELECT_32 0x02 +-#define TUNER_RATIO_SELECT_166 0x04 +-#define TUNER_RATIO_SELECT_62 0x06 +- +-#define TUNER_CHARGE_PUMP 0x40 /* Bit cb6 */ +- +-/* Status byte */ +- +-#define TUNER_POR 0x80 +-#define TUNER_FL 0x40 +-#define TUNER_MODE 0x38 +-#define TUNER_AFC 0x07 +-#define TUNER_SIGNAL 0x07 +-#define TUNER_STEREO 0x10 +- +-#define TUNER_PLL_LOCKED 0x40 +-#define TUNER_STEREO_MK3 0x04 +- +-static DEFINE_MUTEX(tuner_simple_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-struct tuner_simple_priv { +- unsigned int nr; +- u16 last_div; +- +- struct tuner_i2c_props i2c_props; +- struct list_head hybrid_tuner_instance_list; +- +- unsigned int type; +- struct tunertype *tun; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-/* ---------------------------------------------------------------------- */ +- +-static int tuner_read_status(struct dvb_frontend *fe) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- unsigned char byte; +- +- if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1)) +- return 0; +- +- return byte; +-} +- +-static inline int tuner_signal(const int status) +-{ +- return (status & TUNER_SIGNAL) << 13; +-} +- +-static inline int tuner_stereo(const int type, const int status) +-{ +- switch (type) { +- case TUNER_PHILIPS_FM1216ME_MK3: +- case TUNER_PHILIPS_FM1236_MK3: +- case TUNER_PHILIPS_FM1256_IH3: +- case TUNER_LG_NTSC_TAPE: +- case TUNER_TCL_MF02GIP_5N: +- return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); +- case TUNER_PHILIPS_FM1216MK5: +- return status | TUNER_STEREO; +- default: +- return status & TUNER_STEREO; +- } +-} +- +-static inline int tuner_islocked(const int status) +-{ +- return (status & TUNER_FL); +-} +- +-static inline int tuner_afcstatus(const int status) +-{ +- return (status & TUNER_AFC) - 2; +-} +- +- +-static int simple_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int tuner_status; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- tuner_status = tuner_read_status(fe); +- +- *status = 0; +- +- if (tuner_islocked(tuner_status)) +- *status = TUNER_STATUS_LOCKED; +- if (tuner_stereo(priv->type, tuner_status)) +- *status |= TUNER_STATUS_STEREO; +- +- tuner_dbg("AFC Status: %d\n", tuner_afcstatus(tuner_status)); +- +- return 0; +-} +- +-static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int signal; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- signal = tuner_signal(tuner_read_status(fe)); +- +- *strength = signal; +- +- tuner_dbg("Signal strength: %d\n", signal); +- +- return 0; +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static inline char *tuner_param_name(enum param_type type) +-{ +- char *name; +- +- switch (type) { +- case TUNER_PARAM_TYPE_RADIO: +- name = "radio"; +- break; +- case TUNER_PARAM_TYPE_PAL: +- name = "pal"; +- break; +- case TUNER_PARAM_TYPE_SECAM: +- name = "secam"; +- break; +- case TUNER_PARAM_TYPE_NTSC: +- name = "ntsc"; +- break; +- case TUNER_PARAM_TYPE_DIGITAL: +- name = "digital"; +- break; +- default: +- name = "unknown"; +- break; +- } +- return name; +-} +- +-static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, +- enum param_type desired_type) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- struct tunertype *tun = priv->tun; +- int i; +- +- for (i = 0; i < tun->count; i++) +- if (desired_type == tun->params[i].type) +- break; +- +- /* use default tuner params if desired_type not available */ +- if (i == tun->count) { +- tuner_dbg("desired params (%s) undefined for tuner %d\n", +- tuner_param_name(desired_type), priv->type); +- i = 0; +- } +- +- tuner_dbg("using tuner params #%d (%s)\n", i, +- tuner_param_name(tun->params[i].type)); +- +- return &tun->params[i]; +-} +- +-static int simple_config_lookup(struct dvb_frontend *fe, +- struct tuner_params *t_params, +- unsigned *frequency, u8 *config, u8 *cb) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int i; +- +- for (i = 0; i < t_params->count; i++) { +- if (*frequency > t_params->ranges[i].limit) +- continue; +- break; +- } +- if (i == t_params->count) { +- tuner_dbg("frequency out of range (%d > %d)\n", +- *frequency, t_params->ranges[i - 1].limit); +- *frequency = t_params->ranges[--i].limit; +- } +- *config = t_params->ranges[i].config; +- *cb = t_params->ranges[i].cb; +- +- tuner_dbg("freq = %d.%02d (%d), range = %d, " +- "config = 0x%02x, cb = 0x%02x\n", +- *frequency / 16, *frequency % 16 * 100 / 16, *frequency, +- i, *config, *cb); +- +- return i; +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static void simple_set_rf_input(struct dvb_frontend *fe, +- u8 *config, u8 *cb, unsigned int rf) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- switch (priv->type) { +- case TUNER_PHILIPS_TUV1236D: +- switch (rf) { +- case 1: +- *cb |= 0x08; +- break; +- default: +- *cb &= ~0x08; +- break; +- } +- break; +- case TUNER_PHILIPS_FCV1236D: +- switch (rf) { +- case 1: +- *cb |= 0x01; +- break; +- default: +- *cb &= ~0x01; +- break; +- } +- break; +- default: +- break; +- } +-} +- +-static int simple_std_setup(struct dvb_frontend *fe, +- struct analog_parameters *params, +- u8 *config, u8 *cb) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int rc; +- +- /* tv norm specific stuff for multi-norm tuners */ +- switch (priv->type) { +- case TUNER_PHILIPS_SECAM: /* FI1216MF */ +- /* 0x01 -> ??? no change ??? */ +- /* 0x02 -> PAL BDGHI / SECAM L */ +- /* 0x04 -> ??? PAL others / SECAM others ??? */ +- *cb &= ~0x03; +- if (params->std & V4L2_STD_SECAM_L) +- /* also valid for V4L2_STD_SECAM */ +- *cb |= PHILIPS_MF_SET_STD_L; +- else if (params->std & V4L2_STD_SECAM_LC) +- *cb |= PHILIPS_MF_SET_STD_LC; +- else /* V4L2_STD_B|V4L2_STD_GH */ +- *cb |= PHILIPS_MF_SET_STD_BG; +- break; +- +- case TUNER_TEMIC_4046FM5: +- *cb &= ~0x0f; +- +- if (params->std & V4L2_STD_PAL_BG) { +- *cb |= TEMIC_SET_PAL_BG; +- +- } else if (params->std & V4L2_STD_PAL_I) { +- *cb |= TEMIC_SET_PAL_I; +- +- } else if (params->std & V4L2_STD_PAL_DK) { +- *cb |= TEMIC_SET_PAL_DK; +- +- } else if (params->std & V4L2_STD_SECAM_L) { +- *cb |= TEMIC_SET_PAL_L; +- +- } +- break; +- +- case TUNER_PHILIPS_FQ1216ME: +- *cb &= ~0x0f; +- +- if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) { +- *cb |= PHILIPS_SET_PAL_BGDK; +- +- } else if (params->std & V4L2_STD_PAL_I) { +- *cb |= PHILIPS_SET_PAL_I; +- +- } else if (params->std & V4L2_STD_SECAM_L) { +- *cb |= PHILIPS_SET_PAL_L; +- +- } +- break; +- +- case TUNER_PHILIPS_FCV1236D: +- /* 0x00 -> ATSC antenna input 1 */ +- /* 0x01 -> ATSC antenna input 2 */ +- /* 0x02 -> NTSC antenna input 1 */ +- /* 0x03 -> NTSC antenna input 2 */ +- *cb &= ~0x03; +- if (!(params->std & V4L2_STD_ATSC)) +- *cb |= 2; +- break; +- +- case TUNER_MICROTUNE_4042FI5: +- /* Set the charge pump for fast tuning */ +- *config |= TUNER_CHARGE_PUMP; +- break; +- +- case TUNER_PHILIPS_TUV1236D: +- { +- struct tuner_i2c_props i2c = priv->i2c_props; +- /* 0x40 -> ATSC antenna input 1 */ +- /* 0x48 -> ATSC antenna input 2 */ +- /* 0x00 -> NTSC antenna input 1 */ +- /* 0x08 -> NTSC antenna input 2 */ +- u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00}; +- *cb &= ~0x40; +- if (params->std & V4L2_STD_ATSC) { +- *cb |= 0x40; +- buffer[1] = 0x04; +- } +- /* set to the correct mode (analog or digital) */ +- i2c.addr = 0x0a; +- rc = tuner_i2c_xfer_send(&i2c, &buffer[0], 2); +- if (2 != rc) +- tuner_warn("i2c i/o error: rc == %d " +- "(should be 2)\n", rc); +- rc = tuner_i2c_xfer_send(&i2c, &buffer[2], 2); +- if (2 != rc) +- tuner_warn("i2c i/o error: rc == %d " +- "(should be 2)\n", rc); +- break; +- } +- } +- if (atv_input[priv->nr]) +- simple_set_rf_input(fe, config, cb, atv_input[priv->nr]); +- +- return 0; +-} +- +-static int simple_set_aux_byte(struct dvb_frontend *fe, u8 config, u8 aux) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int rc; +- u8 buffer[2]; +- +- buffer[0] = (config & ~0x38) | 0x18; +- buffer[1] = aux; +- +- tuner_dbg("setting aux byte: 0x%02x 0x%02x\n", buffer[0], buffer[1]); +- +- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2); +- if (2 != rc) +- tuner_warn("i2c i/o error: rc == %d (should be 2)\n", rc); +- +- return rc == 2 ? 0 : rc; +-} +- +-static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer, +- u16 div, u8 config, u8 cb) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int rc; +- +- switch (priv->type) { +- case TUNER_LG_TDVS_H06XF: +- simple_set_aux_byte(fe, config, 0x20); +- break; +- case TUNER_PHILIPS_FQ1216LME_MK3: +- simple_set_aux_byte(fe, config, 0x60); /* External AGC */ +- break; +- case TUNER_MICROTUNE_4042FI5: +- { +- /* FIXME - this may also work for other tuners */ +- unsigned long timeout = jiffies + msecs_to_jiffies(1); +- u8 status_byte = 0; +- +- /* Wait until the PLL locks */ +- for (;;) { +- if (time_after(jiffies, timeout)) +- return 0; +- rc = tuner_i2c_xfer_recv(&priv->i2c_props, +- &status_byte, 1); +- if (1 != rc) { +- tuner_warn("i2c i/o read error: rc == %d " +- "(should be 1)\n", rc); +- break; +- } +- if (status_byte & TUNER_PLL_LOCKED) +- break; +- udelay(10); +- } +- +- /* Set the charge pump for optimized phase noise figure */ +- config &= ~TUNER_CHARGE_PUMP; +- buffer[0] = (div>>8) & 0x7f; +- buffer[1] = div & 0xff; +- buffer[2] = config; +- buffer[3] = cb; +- tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", +- buffer[0], buffer[1], buffer[2], buffer[3]); +- +- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); +- if (4 != rc) +- tuner_warn("i2c i/o error: rc == %d " +- "(should be 4)\n", rc); +- break; +- } +- } +- +- return 0; +-} +- +-static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- switch (priv->type) { +- case TUNER_TENA_9533_DI: +- case TUNER_YMEC_TVF_5533MF: +- tuner_dbg("This tuner doesn't have FM. " +- "Most cards have a TEA5767 for FM\n"); +- return 0; +- case TUNER_PHILIPS_FM1216ME_MK3: +- case TUNER_PHILIPS_FM1236_MK3: +- case TUNER_PHILIPS_FMD1216ME_MK3: +- case TUNER_PHILIPS_FMD1216MEX_MK3: +- case TUNER_LG_NTSC_TAPE: +- case TUNER_PHILIPS_FM1256_IH3: +- case TUNER_TCL_MF02GIP_5N: +- buffer[3] = 0x19; +- break; +- case TUNER_PHILIPS_FM1216MK5: +- buffer[2] = 0x88; +- buffer[3] = 0x09; +- break; +- case TUNER_TNF_5335MF: +- buffer[3] = 0x11; +- break; +- case TUNER_LG_PAL_FM: +- buffer[3] = 0xa5; +- break; +- case TUNER_THOMSON_DTT761X: +- buffer[3] = 0x39; +- break; +- case TUNER_PHILIPS_FQ1216LME_MK3: +- case TUNER_PHILIPS_FQ1236_MK5: +- tuner_err("This tuner doesn't have FM\n"); +- /* Set the low band for sanity, since it covers 88-108 MHz */ +- buffer[3] = 0x01; +- break; +- case TUNER_MICROTUNE_4049FM5: +- default: +- buffer[3] = 0xa4; +- break; +- } +- +- return 0; +-} +- +-/* ---------------------------------------------------------------------- */ +- +-static int simple_set_tv_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- u8 config, cb; +- u16 div; +- u8 buffer[4]; +- int rc, IFPCoff, i; +- enum param_type desired_type; +- struct tuner_params *t_params; +- +- /* IFPCoff = Video Intermediate Frequency - Vif: +- 940 =16*58.75 NTSC/J (Japan) +- 732 =16*45.75 M/N STD +- 704 =16*44 ATSC (at DVB code) +- 632 =16*39.50 I U.K. +- 622.4=16*38.90 B/G D/K I, L STD +- 592 =16*37.00 D China +- 590 =16.36.875 B Australia +- 543.2=16*33.95 L' STD +- 171.2=16*10.70 FM Radio (at set_radio_freq) +- */ +- +- if (params->std == V4L2_STD_NTSC_M_JP) { +- IFPCoff = 940; +- desired_type = TUNER_PARAM_TYPE_NTSC; +- } else if ((params->std & V4L2_STD_MN) && +- !(params->std & ~V4L2_STD_MN)) { +- IFPCoff = 732; +- desired_type = TUNER_PARAM_TYPE_NTSC; +- } else if (params->std == V4L2_STD_SECAM_LC) { +- IFPCoff = 543; +- desired_type = TUNER_PARAM_TYPE_SECAM; +- } else { +- IFPCoff = 623; +- desired_type = TUNER_PARAM_TYPE_PAL; +- } +- +- t_params = simple_tuner_params(fe, desired_type); +- +- i = simple_config_lookup(fe, t_params, ¶ms->frequency, +- &config, &cb); +- +- div = params->frequency + IFPCoff + offset; +- +- tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, " +- "Offset=%d.%02d MHz, div=%0d\n", +- params->frequency / 16, params->frequency % 16 * 100 / 16, +- IFPCoff / 16, IFPCoff % 16 * 100 / 16, +- offset / 16, offset % 16 * 100 / 16, div); +- +- /* tv norm specific stuff for multi-norm tuners */ +- simple_std_setup(fe, params, &config, &cb); +- +- if (t_params->cb_first_if_lower_freq && div < priv->last_div) { +- buffer[0] = config; +- buffer[1] = cb; +- buffer[2] = (div>>8) & 0x7f; +- buffer[3] = div & 0xff; +- } else { +- buffer[0] = (div>>8) & 0x7f; +- buffer[1] = div & 0xff; +- buffer[2] = config; +- buffer[3] = cb; +- } +- priv->last_div = div; +- if (t_params->has_tda9887) { +- struct v4l2_priv_tun_config tda9887_cfg; +- int tda_config = 0; +- int is_secam_l = (params->std & (V4L2_STD_SECAM_L | +- V4L2_STD_SECAM_LC)) && +- !(params->std & ~(V4L2_STD_SECAM_L | +- V4L2_STD_SECAM_LC)); +- +- tda9887_cfg.tuner = TUNER_TDA9887; +- tda9887_cfg.priv = &tda_config; +- +- if (params->std == V4L2_STD_SECAM_LC) { +- if (t_params->port1_active ^ t_params->port1_invert_for_secam_lc) +- tda_config |= TDA9887_PORT1_ACTIVE; +- if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc) +- tda_config |= TDA9887_PORT2_ACTIVE; +- } else { +- if (t_params->port1_active) +- tda_config |= TDA9887_PORT1_ACTIVE; +- if (t_params->port2_active) +- tda_config |= TDA9887_PORT2_ACTIVE; +- } +- if (t_params->intercarrier_mode) +- tda_config |= TDA9887_INTERCARRIER; +- if (is_secam_l) { +- if (i == 0 && t_params->default_top_secam_low) +- tda_config |= TDA9887_TOP(t_params->default_top_secam_low); +- else if (i == 1 && t_params->default_top_secam_mid) +- tda_config |= TDA9887_TOP(t_params->default_top_secam_mid); +- else if (t_params->default_top_secam_high) +- tda_config |= TDA9887_TOP(t_params->default_top_secam_high); +- } else { +- if (i == 0 && t_params->default_top_low) +- tda_config |= TDA9887_TOP(t_params->default_top_low); +- else if (i == 1 && t_params->default_top_mid) +- tda_config |= TDA9887_TOP(t_params->default_top_mid); +- else if (t_params->default_top_high) +- tda_config |= TDA9887_TOP(t_params->default_top_high); +- } +- if (t_params->default_pll_gating_18) +- tda_config |= TDA9887_GATING_18; +- i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, +- &tda9887_cfg); +- } +- tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", +- buffer[0], buffer[1], buffer[2], buffer[3]); +- +- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); +- if (4 != rc) +- tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); +- +- simple_post_tune(fe, &buffer[0], div, config, cb); +- +- return 0; +-} +- +-static int simple_set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tunertype *tun; +- struct tuner_simple_priv *priv = fe->tuner_priv; +- u8 buffer[4]; +- u16 div; +- int rc, j; +- struct tuner_params *t_params; +- unsigned int freq = params->frequency; +- +- tun = priv->tun; +- +- for (j = tun->count-1; j > 0; j--) +- if (tun->params[j].type == TUNER_PARAM_TYPE_RADIO) +- break; +- /* default t_params (j=0) will be used if desired type wasn't found */ +- t_params = &tun->params[j]; +- +- /* Select Radio 1st IF used */ +- switch (t_params->radio_if) { +- case 0: /* 10.7 MHz */ +- freq += (unsigned int)(10.7*16000); +- break; +- case 1: /* 33.3 MHz */ +- freq += (unsigned int)(33.3*16000); +- break; +- case 2: /* 41.3 MHz */ +- freq += (unsigned int)(41.3*16000); +- break; +- default: +- tuner_warn("Unsupported radio_if value %d\n", +- t_params->radio_if); +- return 0; +- } +- +- buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) | +- TUNER_RATIO_SELECT_50; /* 50 kHz step */ +- +- /* Bandswitch byte */ +- simple_radio_bandswitch(fe, &buffer[0]); +- +- /* Convert from 1/16 kHz V4L steps to 1/20 MHz (=50 kHz) PLL steps +- freq * (1 Mhz / 16000 V4L steps) * (20 PLL steps / 1 MHz) = +- freq * (1/800) */ +- div = (freq + 400) / 800; +- +- if (t_params->cb_first_if_lower_freq && div < priv->last_div) { +- buffer[0] = buffer[2]; +- buffer[1] = buffer[3]; +- buffer[2] = (div>>8) & 0x7f; +- buffer[3] = div & 0xff; +- } else { +- buffer[0] = (div>>8) & 0x7f; +- buffer[1] = div & 0xff; +- } +- +- tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n", +- buffer[0], buffer[1], buffer[2], buffer[3]); +- priv->last_div = div; +- +- if (t_params->has_tda9887) { +- int config = 0; +- struct v4l2_priv_tun_config tda9887_cfg; +- +- tda9887_cfg.tuner = TUNER_TDA9887; +- tda9887_cfg.priv = &config; +- +- if (t_params->port1_active && +- !t_params->port1_fm_high_sensitivity) +- config |= TDA9887_PORT1_ACTIVE; +- if (t_params->port2_active && +- !t_params->port2_fm_high_sensitivity) +- config |= TDA9887_PORT2_ACTIVE; +- if (t_params->intercarrier_mode) +- config |= TDA9887_INTERCARRIER; +-/* if (t_params->port1_set_for_fm_mono) +- config &= ~TDA9887_PORT1_ACTIVE;*/ +- if (t_params->fm_gain_normal) +- config |= TDA9887_GAIN_NORMAL; +- if (t_params->radio_if == 2) +- config |= TDA9887_RIF_41_3; +- i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, +- &tda9887_cfg); +- } +- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); +- if (4 != rc) +- tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); +- +- /* Write AUX byte */ +- switch (priv->type) { +- case TUNER_PHILIPS_FM1216ME_MK3: +- buffer[2] = 0x98; +- buffer[3] = 0x20; /* set TOP AGC */ +- rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); +- if (4 != rc) +- tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); +- break; +- } +- +- return 0; +-} +- +-static int simple_set_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- int ret = -EINVAL; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- switch (params->mode) { +- case V4L2_TUNER_RADIO: +- ret = simple_set_radio_freq(fe, params); +- priv->frequency = params->frequency * 125 / 2; +- break; +- case V4L2_TUNER_ANALOG_TV: +- case V4L2_TUNER_DIGITAL_TV: +- ret = simple_set_tv_freq(fe, params); +- priv->frequency = params->frequency * 62500; +- break; +- } +- priv->bandwidth = 0; +- +- return ret; +-} +- +-static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf, +- const u32 delsys, +- const u32 frequency, +- const u32 bandwidth) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- switch (priv->type) { +- case TUNER_PHILIPS_FMD1216ME_MK3: +- case TUNER_PHILIPS_FMD1216MEX_MK3: +- if (bandwidth == 8000000 && +- frequency >= 158870000) +- buf[3] |= 0x08; +- break; +- case TUNER_PHILIPS_TD1316: +- /* determine band */ +- buf[3] |= (frequency < 161000000) ? 1 : +- (frequency < 444000000) ? 2 : 4; +- +- /* setup PLL filter */ +- if (bandwidth == 8000000) +- buf[3] |= 1 << 3; +- break; +- case TUNER_PHILIPS_TUV1236D: +- case TUNER_PHILIPS_FCV1236D: +- { +- unsigned int new_rf; +- +- if (dtv_input[priv->nr]) +- new_rf = dtv_input[priv->nr]; +- else +- switch (delsys) { +- case SYS_DVBC_ANNEX_B: +- new_rf = 1; +- break; +- case SYS_ATSC: +- default: +- new_rf = 0; +- break; +- } +- simple_set_rf_input(fe, &buf[2], &buf[3], new_rf); +- break; +- } +- default: +- break; +- } +-} +- +-static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, +- const u32 delsys, +- const u32 freq, +- const u32 bw) +-{ +- /* This function returns the tuned frequency on success, 0 on error */ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- struct tunertype *tun = priv->tun; +- static struct tuner_params *t_params; +- u8 config, cb; +- u32 div; +- int ret; +- u32 frequency = freq / 62500; +- +- if (!tun->stepsize) { +- /* tuner-core was loaded before the digital tuner was +- * configured and somehow picked the wrong tuner type */ +- tuner_err("attempt to treat tuner %d (%s) as digital tuner " +- "without stepsize defined.\n", +- priv->type, priv->tun->name); +- return 0; /* failure */ +- } +- +- t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); +- ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); +- if (ret < 0) +- return 0; /* failure */ +- +- div = ((frequency + t_params->iffreq) * 62500 + offset + +- tun->stepsize/2) / tun->stepsize; +- +- buf[0] = div >> 8; +- buf[1] = div & 0xff; +- buf[2] = config; +- buf[3] = cb; +- +- simple_set_dvb(fe, buf, delsys, freq, bw); +- +- tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", +- tun->name, div, buf[0], buf[1], buf[2], buf[3]); +- +- /* calculate the frequency we set it to */ +- return (div * tun->stepsize) - t_params->iffreq; +-} +- +-static int simple_dvb_calc_regs(struct dvb_frontend *fe, +- u8 *buf, int buf_len) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- struct tuner_simple_priv *priv = fe->tuner_priv; +- u32 frequency; +- +- if (buf_len < 5) +- return -EINVAL; +- +- frequency = simple_dvb_configure(fe, buf+1, delsys, c->frequency, bw); +- if (frequency == 0) +- return -EINVAL; +- +- buf[0] = priv->i2c_props.addr; +- +- priv->frequency = frequency; +- priv->bandwidth = c->bandwidth_hz; +- +- return 5; +-} +- +-static int simple_dvb_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- u32 freq = c->frequency; +- struct tuner_simple_priv *priv = fe->tuner_priv; +- u32 frequency; +- u32 prev_freq, prev_bw; +- int ret; +- u8 buf[5]; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- prev_freq = priv->frequency; +- prev_bw = priv->bandwidth; +- +- frequency = simple_dvb_configure(fe, buf+1, delsys, freq, bw); +- if (frequency == 0) +- return -EINVAL; +- +- buf[0] = priv->i2c_props.addr; +- +- priv->frequency = frequency; +- priv->bandwidth = bw; +- +- /* put analog demod in standby when tuning digital */ +- if (fe->ops.analog_ops.standby) +- fe->ops.analog_ops.standby(fe); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- /* buf[0] contains the i2c address, but * +- * we already have it in i2c_props.addr */ +- ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4); +- if (ret != 4) +- goto fail; +- +- return 0; +-fail: +- /* calc_regs sets frequency and bandwidth. if we failed, unset them */ +- priv->frequency = prev_freq; +- priv->bandwidth = prev_bw; +- +- return ret; +-} +- +-static int simple_init(struct dvb_frontend *fe) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- if (priv->tun->initdata) { +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = tuner_i2c_xfer_send(&priv->i2c_props, +- priv->tun->initdata + 1, +- priv->tun->initdata[0]); +- if (ret != priv->tun->initdata[0]) +- return ret; +- } +- +- return 0; +-} +- +-static int simple_sleep(struct dvb_frontend *fe) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- if (priv->tun->sleepdata) { +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = tuner_i2c_xfer_send(&priv->i2c_props, +- priv->tun->sleepdata + 1, +- priv->tun->sleepdata[0]); +- if (ret != priv->tun->sleepdata[0]) +- return ret; +- } +- +- return 0; +-} +- +-static int simple_release(struct dvb_frontend *fe) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- +- mutex_lock(&tuner_simple_list_mutex); +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&tuner_simple_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct tuner_simple_priv *priv = fe->tuner_priv; +- *bandwidth = priv->bandwidth; +- return 0; +-} +- +-static struct dvb_tuner_ops simple_tuner_ops = { +- .init = simple_init, +- .sleep = simple_sleep, +- .set_analog_params = simple_set_params, +- .set_params = simple_dvb_set_params, +- .calc_regs = simple_dvb_calc_regs, +- .release = simple_release, +- .get_frequency = simple_get_frequency, +- .get_bandwidth = simple_get_bandwidth, +- .get_status = simple_get_status, +- .get_rf_strength = simple_get_rf_strength, +-}; +- +-struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- unsigned int type) +-{ +- struct tuner_simple_priv *priv = NULL; +- int instance; +- +- if (type >= tuner_count) { +- printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", +- __func__, type, tuner_count-1); +- return NULL; +- } +- +- /* If i2c_adap is set, check that the tuner is at the correct address. +- * Otherwise, if i2c_adap is NULL, the tuner will be programmed directly +- * by the digital demod via calc_regs. +- */ +- if (i2c_adap != NULL) { +- u8 b[1]; +- struct i2c_msg msg = { +- .addr = i2c_addr, .flags = I2C_M_RD, +- .buf = b, .len = 1, +- }; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (1 != i2c_transfer(i2c_adap, &msg, 1)) +- printk(KERN_WARNING "tuner-simple %d-%04x: " +- "unable to probe %s, proceeding anyway.", +- i2c_adapter_id(i2c_adap), i2c_addr, +- tuners[type].name); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- mutex_lock(&tuner_simple_list_mutex); +- +- instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv, +- hybrid_tuner_instance_list, +- i2c_adap, i2c_addr, +- "tuner-simple"); +- switch (instance) { +- case 0: +- mutex_unlock(&tuner_simple_list_mutex); +- return NULL; +- case 1: +- fe->tuner_priv = priv; +- +- priv->type = type; +- priv->tun = &tuners[type]; +- priv->nr = simple_devcount++; +- break; +- default: +- fe->tuner_priv = priv; +- break; +- } +- +- mutex_unlock(&tuner_simple_list_mutex); +- +- memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- if (type != priv->type) +- tuner_warn("couldn't set type to %d. Using %d (%s) instead\n", +- type, priv->type, priv->tun->name); +- else +- tuner_info("type set to %d (%s)\n", +- priv->type, priv->tun->name); +- +- if ((debug) || ((atv_input[priv->nr] > 0) || +- (dtv_input[priv->nr] > 0))) { +- if (0 == atv_input[priv->nr]) +- tuner_info("tuner %d atv rf input will be " +- "autoselected\n", priv->nr); +- else +- tuner_info("tuner %d atv rf input will be " +- "set to input %d (insmod option)\n", +- priv->nr, atv_input[priv->nr]); +- if (0 == dtv_input[priv->nr]) +- tuner_info("tuner %d dtv rf input will be " +- "autoselected\n", priv->nr); +- else +- tuner_info("tuner %d dtv rf input will be " +- "set to input %d (insmod option)\n", +- priv->nr, dtv_input[priv->nr]); +- } +- +- strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, +- sizeof(fe->ops.tuner_ops.info.name)); +- +- return fe; +-} +-EXPORT_SYMBOL_GPL(simple_tuner_attach); +- +-MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); +-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/common/tuners/tuner-simple.h b/drivers/media/common/tuners/tuner-simple.h +deleted file mode 100644 +index 381fa5d..0000000 +--- a/drivers/media/common/tuners/tuner-simple.h ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TUNER_SIMPLE_H__ +-#define __TUNER_SIMPLE_H__ +- +-#include +-#include "dvb_frontend.h" +- +-#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- unsigned int type); +-#else +-static inline struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- unsigned int type) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TUNER_SIMPLE_H__ */ +diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c +deleted file mode 100644 +index e13683b..0000000 +--- a/drivers/media/common/tuners/tuner-types.c ++++ /dev/null +@@ -1,1879 +0,0 @@ +-/* +- * +- * i2c tv tuner chip device type database. +- * +- */ +- +-#include +-#include +-#include +-#include +- +-/* ---------------------------------------------------------------------- */ +- +-/* +- * The floats in the tuner struct are computed at compile time +- * by gcc and cast back to integers. Thus we don't violate the +- * "no float in kernel" rule. +- * +- * A tuner_range may be referenced by multiple tuner_params structs. +- * There are many duplicates in here. Reusing tuner_range structs, +- * rather than defining new ones for each tuner, will cut down on +- * memory usage, and is preferred when possible. +- * +- * Each tuner_params array may contain one or more elements, one +- * for each video standard. +- * +- * FIXME: tuner_params struct contains an element, tda988x. We must +- * set this for all tuners that contain a tda988x chip, and then we +- * can remove this setting from the various card structs. +- * +- * FIXME: Right now, all tuners are using the first tuner_params[] +- * array element for analog mode. In the future, we will be merging +- * similar tuner definitions together, such that each tuner definition +- * will have a tuner_params struct for each available video standard. +- * At that point, the tuner_params[] array element will be chosen +- * based on the video standard in use. +- */ +- +-/* The following was taken from dvb-pll.c: */ +- +-/* Set AGC TOP value to 103 dBuV: +- * 0x80 = Control Byte +- * 0x40 = 250 uA charge pump (irrelevant) +- * 0x18 = Aux Byte to follow +- * 0x06 = 64.5 kHz divider (irrelevant) +- * 0x01 = Disable Vt (aka sleep) +- * +- * 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) +- * 0x50 = AGC Take over point = 103 dBuV +- */ +-static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; +- +-/* 0x04 = 166.67 kHz divider +- * +- * 0x80 = AGC Time constant 50ms Iagc = 9 uA +- * 0x20 = AGC Take over point = 112 dBuV +- */ +-static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 }; +- +-/* 0-9 */ +-/* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */ +- +-static struct tuner_range tuner_temic_pal_ranges[] = { +- { 16 * 140.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, +- { 16 * 999.99 , 0x8e, 0x01, }, +-}; +- +-static struct tuner_params tuner_temic_pal_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_PAL_I - Philips PAL_I ------------ */ +- +-static struct tuner_range tuner_philips_pal_i_ranges[] = { +- { 16 * 140.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_philips_pal_i_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_pal_i_ranges, +- .count = ARRAY_SIZE(tuner_philips_pal_i_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_NTSC - Philips NTSC ------------ */ +- +-static struct tuner_range tuner_philips_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 451.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_philips_ntsc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_philips_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_philips_ntsc_ranges), +- .cb_first_if_lower_freq = 1, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_SECAM - Philips SECAM ------------ */ +- +-static struct tuner_range tuner_philips_secam_ranges[] = { +- { 16 * 168.25 /*MHz*/, 0x8e, 0xa7, }, +- { 16 * 447.25 /*MHz*/, 0x8e, 0x97, }, +- { 16 * 999.99 , 0x8e, 0x37, }, +-}; +- +-static struct tuner_params tuner_philips_secam_params[] = { +- { +- .type = TUNER_PARAM_TYPE_SECAM, +- .ranges = tuner_philips_secam_ranges, +- .count = ARRAY_SIZE(tuner_philips_secam_ranges), +- .cb_first_if_lower_freq = 1, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_PAL - Philips PAL ------------ */ +- +-static struct tuner_range tuner_philips_pal_ranges[] = { +- { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 447.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_philips_pal_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_pal_ranges), +- .cb_first_if_lower_freq = 1, +- }, +-}; +- +-/* ------------ TUNER_TEMIC_NTSC - TEMIC NTSC ------------ */ +- +-static struct tuner_range tuner_temic_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0x04, }, +- { 16 * 999.99 , 0x8e, 0x01, }, +-}; +- +-static struct tuner_params tuner_temic_ntsc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_temic_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_temic_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_PAL_I - TEMIC PAL_I ------------ */ +- +-static struct tuner_range tuner_temic_pal_i_ranges[] = { +- { 16 * 170.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 450.00 /*MHz*/, 0x8e, 0x04, }, +- { 16 * 999.99 , 0x8e, 0x01, }, +-}; +- +-static struct tuner_params tuner_temic_pal_i_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_pal_i_ranges, +- .count = ARRAY_SIZE(tuner_temic_pal_i_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4036FY5_NTSC - TEMIC NTSC ------------ */ +- +-static struct tuner_range tuner_temic_4036fy5_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_4036fy5_ntsc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_temic_4036fy5_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_ALPS_TSBH1_NTSC - TEMIC NTSC ------------ */ +- +-static struct tuner_range tuner_alps_tsb_1_ranges[] = { +- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 385.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_alps_tsbh1_ntsc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_alps_tsb_1_ranges, +- .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), +- }, +-}; +- +-/* 10-19 */ +-/* ------------ TUNER_ALPS_TSBE1_PAL - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_alps_tsb_1_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_alps_tsb_1_ranges, +- .count = ARRAY_SIZE(tuner_alps_tsb_1_ranges), +- }, +-}; +- +-/* ------------ TUNER_ALPS_TSBB5_PAL_I - Alps PAL_I ------------ */ +- +-static struct tuner_range tuner_alps_tsb_5_pal_ranges[] = { +- { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 351.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_alps_tsbb5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_alps_tsb_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_ALPS_TSBE5_PAL - Alps PAL ------------ */ +- +-static struct tuner_params tuner_alps_tsbe5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_alps_tsb_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_ALPS_TSBC5_PAL - Alps PAL ------------ */ +- +-static struct tuner_params tuner_alps_tsbc5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_alps_tsb_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_alps_tsb_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4006FH5_PAL - TEMIC PAL ------------ */ +- +-static struct tuner_range tuner_lg_pal_ranges[] = { +- { 16 * 170.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 450.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_4006fh5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_ALPS_TSHC6_NTSC - Alps NTSC ------------ */ +- +-static struct tuner_range tuner_alps_tshc6_ntsc_ranges[] = { +- { 16 * 137.25 /*MHz*/, 0x8e, 0x14, }, +- { 16 * 385.25 /*MHz*/, 0x8e, 0x12, }, +- { 16 * 999.99 , 0x8e, 0x11, }, +-}; +- +-static struct tuner_params tuner_alps_tshc6_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_alps_tshc6_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_alps_tshc6_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_PAL_DK - TEMIC PAL ------------ */ +- +-static struct tuner_range tuner_temic_pal_dk_ranges[] = { +- { 16 * 168.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 456.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_pal_dk_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_pal_dk_ranges, +- .count = ARRAY_SIZE(tuner_temic_pal_dk_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_NTSC_M - Philips NTSC ------------ */ +- +-static struct tuner_range tuner_philips_ntsc_m_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_philips_ntsc_m_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_philips_ntsc_m_ranges, +- .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4066FY5_PAL_I - TEMIC PAL_I ------------ */ +- +-static struct tuner_range tuner_temic_40x6f_5_pal_ranges[] = { +- { 16 * 169.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_4066fy5_pal_i_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_40x6f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4006FN5_MULTI_PAL - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_temic_4006fn5_multi_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_40x6f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), +- }, +-}; +- +-/* 20-29 */ +-/* ------------ TUNER_TEMIC_4009FR5_PAL - TEMIC PAL ------------ */ +- +-static struct tuner_range tuner_temic_4009f_5_pal_ranges[] = { +- { 16 * 141.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 464.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_4009f_5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_4009f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4039FR5_NTSC - TEMIC NTSC ------------ */ +- +-static struct tuner_range tuner_temic_4x3x_f_5_ntsc_ranges[] = { +- { 16 * 158.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_temic_4039fr5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4046FM5 - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_temic_4046fm5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_40x6f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_40x6f_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_PAL_DK - Philips PAL ------------ */ +- +-static struct tuner_params tuner_philips_pal_dk_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FQ1216ME - Philips PAL ------------ */ +- +-static struct tuner_params tuner_philips_fq1216me_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_invert_for_secam_lc = 1, +- }, +-}; +- +-/* ------------ TUNER_LG_PAL_I_FM - LGINNOTEK PAL_I ------------ */ +- +-static struct tuner_params tuner_lg_pal_i_fm_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_PAL_I - LGINNOTEK PAL_I ------------ */ +- +-static struct tuner_params tuner_lg_pal_i_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_NTSC_FM - LGINNOTEK NTSC ------------ */ +- +-static struct tuner_range tuner_lg_ntsc_fm_ranges[] = { +- { 16 * 210.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 497.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_lg_ntsc_fm_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_lg_ntsc_fm_ranges, +- .count = ARRAY_SIZE(tuner_lg_ntsc_fm_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_PAL_FM - LGINNOTEK PAL ------------ */ +- +-static struct tuner_params tuner_lg_pal_fm_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_PAL - LGINNOTEK PAL ------------ */ +- +-static struct tuner_params tuner_lg_pal_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_pal_ranges, +- .count = ARRAY_SIZE(tuner_lg_pal_ranges), +- }, +-}; +- +-/* 30-39 */ +-/* ------------ TUNER_TEMIC_4009FN5_MULTI_PAL_FM - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_temic_4009_fn5_multi_pal_fm_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_4009f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_SHARP_2U5JF5540_NTSC - SHARP NTSC ------------ */ +- +-static struct tuner_range tuner_sharp_2u5jf5540_ntsc_ranges[] = { +- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 317.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_sharp_2u5jf5540_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_sharp_2u5jf5540_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_Samsung_PAL_TCPM9091PD27 - Samsung PAL ------------ */ +- +-static struct tuner_range tuner_samsung_pal_tcpm9091pd27_ranges[] = { +- { 16 * 169 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 464 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_samsung_pal_tcpm9091pd27_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_samsung_pal_tcpm9091pd27_ranges, +- .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4106FH5 - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_temic_4106fh5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_4009f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4012FY5 - TEMIC PAL ------------ */ +- +-static struct tuner_params tuner_temic_4012fy5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TEMIC_4136FY5 - TEMIC NTSC ------------ */ +- +-static struct tuner_params tuner_temic_4136_fy5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_temic_4x3x_f_5_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_temic_4x3x_f_5_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_PAL_NEW_TAPC - LGINNOTEK PAL ------------ */ +- +-static struct tuner_range tuner_lg_new_tapc_ranges[] = { +- { 16 * 170.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 450.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_lg_pal_new_tapc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_new_tapc_ranges, +- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FM1216ME_MK3 - Philips PAL ------------ */ +- +-static struct tuner_range tuner_fm1216me_mk3_pal_ranges[] = { +- { 16 * 158.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_fm1216me_mk3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_fm1216me_mk3_pal_ranges, +- .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), +- .cb_first_if_lower_freq = 1, +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_invert_for_secam_lc = 1, +- .port1_fm_high_sensitivity = 1, +- .default_top_mid = -2, +- .default_top_secam_mid = -2, +- .default_top_secam_high = -2, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FM1216MK5 - Philips PAL ------------ */ +- +-static struct tuner_range tuner_fm1216mk5_pal_ranges[] = { +- { 16 * 158.00 /*MHz*/, 0xce, 0x01, }, +- { 16 * 441.00 /*MHz*/, 0xce, 0x02, }, +- { 16 * 864.00 , 0xce, 0x04, }, +-}; +- +-static struct tuner_params tuner_fm1216mk5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_fm1216mk5_pal_ranges, +- .count = ARRAY_SIZE(tuner_fm1216mk5_pal_ranges), +- .cb_first_if_lower_freq = 1, +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_invert_for_secam_lc = 1, +- .port1_fm_high_sensitivity = 1, +- .default_top_mid = -2, +- .default_top_secam_mid = -2, +- .default_top_secam_high = -2, +- }, +-}; +- +-/* ------------ TUNER_LG_NTSC_NEW_TAPC - LGINNOTEK NTSC ------------ */ +- +-static struct tuner_params tuner_lg_ntsc_new_tapc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_lg_new_tapc_ranges, +- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), +- }, +-}; +- +-/* 40-49 */ +-/* ------------ TUNER_HITACHI_NTSC - HITACHI NTSC ------------ */ +- +-static struct tuner_params tuner_hitachi_ntsc_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_lg_new_tapc_ranges, +- .count = ARRAY_SIZE(tuner_lg_new_tapc_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_PAL_MK - Philips PAL ------------ */ +- +-static struct tuner_range tuner_philips_pal_mk_pal_ranges[] = { +- { 16 * 140.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0xc2, }, +- { 16 * 999.99 , 0x8e, 0xcf, }, +-}; +- +-static struct tuner_params tuner_philips_pal_mk_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_pal_mk_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_pal_mk_pal_ranges), +- }, +-}; +- +-/* ---- TUNER_PHILIPS_FCV1236D - Philips FCV1236D (ATSC/NTSC) ---- */ +- +-static struct tuner_range tuner_philips_fcv1236d_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0x8e, 0xa2, }, +- { 16 * 451.25 /*MHz*/, 0x8e, 0x92, }, +- { 16 * 999.99 , 0x8e, 0x32, }, +-}; +- +-static struct tuner_range tuner_philips_fcv1236d_atsc_ranges[] = { +- { 16 * 159.00 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_philips_fcv1236d_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_philips_fcv1236d_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_philips_fcv1236d_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_philips_fcv1236d_atsc_ranges, +- .count = ARRAY_SIZE(tuner_philips_fcv1236d_atsc_ranges), +- .iffreq = 16 * 44.00, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FM1236_MK3 - Philips NTSC ------------ */ +- +-static struct tuner_range tuner_fm1236_mk3_ntsc_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 442.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_fm1236_mk3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_fm1236_mk3_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), +- .cb_first_if_lower_freq = 1, +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port1_fm_high_sensitivity = 1, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_4IN1 - Philips NTSC ------------ */ +- +-static struct tuner_params tuner_philips_4in1_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_fm1236_mk3_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_MICROTUNE_4049FM5 - Microtune PAL ------------ */ +- +-static struct tuner_params tuner_microtune_4049_fm5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_temic_4009f_5_pal_ranges, +- .count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges), +- .has_tda9887 = 1, +- .port1_invert_for_secam_lc = 1, +- .default_pll_gating_18 = 1, +- .fm_gain_normal=1, +- .radio_if = 1, /* 33.3 MHz */ +- }, +-}; +- +-/* ------------ TUNER_PANASONIC_VP27 - Panasonic NTSC ------------ */ +- +-static struct tuner_range tuner_panasonic_vp27_ntsc_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, +- { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, +- { 16 * 999.99 , 0xce, 0x08, }, +-}; +- +-static struct tuner_params tuner_panasonic_vp27_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_panasonic_vp27_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_panasonic_vp27_ntsc_ranges), +- .has_tda9887 = 1, +- .intercarrier_mode = 1, +- .default_top_low = -3, +- .default_top_mid = -3, +- .default_top_high = -3, +- }, +-}; +- +-/* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */ +- +-static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = { +- { 16 * 161.25 /*MHz*/, 0x8e, 0xa0, }, +- { 16 * 463.25 /*MHz*/, 0x8e, 0x90, }, +- { 16 * 999.99 , 0x8e, 0x30, }, +-}; +- +-static struct tuner_params tuner_tnf_8831bgff_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_tnf_8831bgff_pal_ranges, +- .count = ARRAY_SIZE(tuner_tnf_8831bgff_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_MICROTUNE_4042FI5 - Microtune NTSC ------------ */ +- +-static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = { +- { 16 * 162.00 /*MHz*/, 0x8e, 0xa2, }, +- { 16 * 457.00 /*MHz*/, 0x8e, 0x94, }, +- { 16 * 999.99 , 0x8e, 0x31, }, +-}; +- +-static struct tuner_range tuner_microtune_4042fi5_atsc_ranges[] = { +- { 16 * 162.00 /*MHz*/, 0x8e, 0xa1, }, +- { 16 * 457.00 /*MHz*/, 0x8e, 0x91, }, +- { 16 * 999.99 , 0x8e, 0x31, }, +-}; +- +-static struct tuner_params tuner_microtune_4042fi5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_microtune_4042fi5_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_microtune_4042fi5_atsc_ranges, +- .count = ARRAY_SIZE(tuner_microtune_4042fi5_atsc_ranges), +- .iffreq = 16 * 44.00 /*MHz*/, +- }, +-}; +- +-/* 50-59 */ +-/* ------------ TUNER_TCL_2002N - TCL NTSC ------------ */ +- +-static struct tuner_range tuner_tcl_2002n_ntsc_ranges[] = { +- { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_tcl_2002n_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tcl_2002n_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tcl_2002n_ntsc_ranges), +- .cb_first_if_lower_freq = 1, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FM1256_IH3 - Philips PAL ------------ */ +- +-static struct tuner_params tuner_philips_fm1256_ih3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_fm1236_mk3_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), +- .radio_if = 1, /* 33.3 MHz */ +- }, +-}; +- +-/* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */ +- +-/* single range used for both ntsc and atsc */ +-static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0x8e, 0x39, }, +- { 16 * 454.00 /*MHz*/, 0x8e, 0x3a, }, +- { 16 * 999.99 , 0x8e, 0x3c, }, +-}; +- +-static struct tuner_params tuner_thomson_dtt7610_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_thomson_dtt7610_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_thomson_dtt7610_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), +- .iffreq = 16 * 44.00 /*MHz*/, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */ +- +-static struct tuner_range tuner_philips_fq1286_ntsc_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0x8e, 0x41, }, +- { 16 * 454.00 /*MHz*/, 0x8e, 0x42, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_philips_fq1286_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_philips_fq1286_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_philips_fq1286_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_TCL_2002MB - TCL PAL ------------ */ +- +-static struct tuner_range tuner_tcl_2002mb_pal_ranges[] = { +- { 16 * 170.00 /*MHz*/, 0xce, 0x01, }, +- { 16 * 450.00 /*MHz*/, 0xce, 0x02, }, +- { 16 * 999.99 , 0xce, 0x08, }, +-}; +- +-static struct tuner_params tuner_tcl_2002mb_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_tcl_2002mb_pal_ranges, +- .count = ARRAY_SIZE(tuner_tcl_2002mb_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FQ1216AME_MK4 - Philips PAL ------------ */ +- +-static struct tuner_range tuner_philips_fq12_6a___mk4_pal_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0xce, 0x01, }, +- { 16 * 442.00 /*MHz*/, 0xce, 0x02, }, +- { 16 * 999.99 , 0xce, 0x04, }, +-}; +- +-static struct tuner_params tuner_philips_fq1216ame_mk4_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_fq12_6a___mk4_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_fq12_6a___mk4_pal_ranges), +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_invert_for_secam_lc = 1, +- .default_top_mid = -2, +- .default_top_secam_low = -2, +- .default_top_secam_mid = -2, +- .default_top_secam_high = -2, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FQ1236A_MK4 - Philips NTSC ------------ */ +- +-static struct tuner_params tuner_philips_fq1236a_mk4_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_fm1236_mk3_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_YMEC_TVF_8531MF - Philips NTSC ------------ */ +- +-static struct tuner_params tuner_ymec_tvf_8531mf_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_philips_ntsc_m_ranges, +- .count = ARRAY_SIZE(tuner_philips_ntsc_m_ranges), +- }, +-}; +- +-/* ------------ TUNER_YMEC_TVF_5533MF - Philips NTSC ------------ */ +- +-static struct tuner_range tuner_ymec_tvf_5533mf_ntsc_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 454.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_ymec_tvf_5533mf_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_ymec_tvf_5533mf_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_ntsc_ranges), +- }, +-}; +- +-/* 60-69 */ +-/* ------------ TUNER_THOMSON_DTT761X - THOMSON ATSC ------------ */ +-/* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ +- +-static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = { +- { 16 * 145.25 /*MHz*/, 0x8e, 0x39, }, +- { 16 * 415.25 /*MHz*/, 0x8e, 0x3a, }, +- { 16 * 999.99 , 0x8e, 0x3c, }, +-}; +- +-static struct tuner_range tuner_thomson_dtt761x_atsc_ranges[] = { +- { 16 * 147.00 /*MHz*/, 0x8e, 0x39, }, +- { 16 * 417.00 /*MHz*/, 0x8e, 0x3a, }, +- { 16 * 999.99 , 0x8e, 0x3c, }, +-}; +- +-static struct tuner_params tuner_thomson_dtt761x_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_thomson_dtt761x_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_thomson_dtt761x_ntsc_ranges), +- .has_tda9887 = 1, +- .fm_gain_normal = 1, +- .radio_if = 2, /* 41.3 MHz */ +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_thomson_dtt761x_atsc_ranges, +- .count = ARRAY_SIZE(tuner_thomson_dtt761x_atsc_ranges), +- .iffreq = 16 * 44.00, /*MHz*/ +- }, +-}; +- +-/* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */ +- +-static struct tuner_range tuner_tena_9533_di_pal_ranges[] = { +- { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_tena_9533_di_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_tena_9533_di_pal_ranges, +- .count = ARRAY_SIZE(tuner_tena_9533_di_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_TENA_TNF_5337 - Tena tnf5337MFD STD M/N ------------ */ +- +-static struct tuner_range tuner_tena_tnf_5337_ntsc_ranges[] = { +- { 16 * 166.25 /*MHz*/, 0x86, 0x01, }, +- { 16 * 466.25 /*MHz*/, 0x86, 0x02, }, +- { 16 * 999.99 , 0x86, 0x08, }, +-}; +- +-static struct tuner_params tuner_tena_tnf_5337_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tena_tnf_5337_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tena_tnf_5337_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FMD1216ME(X)_MK3 - Philips PAL ------------ */ +- +-static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0x86, 0x51, }, +- { 16 * 442.00 /*MHz*/, 0x86, 0x52, }, +- { 16 * 999.99 , 0x86, 0x54, }, +-}; +- +-static struct tuner_range tuner_philips_fmd1216me_mk3_dvb_ranges[] = { +- { 16 * 143.87 /*MHz*/, 0xbc, 0x41 }, +- { 16 * 158.87 /*MHz*/, 0xf4, 0x41 }, +- { 16 * 329.87 /*MHz*/, 0xbc, 0x42 }, +- { 16 * 441.87 /*MHz*/, 0xf4, 0x42 }, +- { 16 * 625.87 /*MHz*/, 0xbc, 0x44 }, +- { 16 * 803.87 /*MHz*/, 0xf4, 0x44 }, +- { 16 * 999.99 , 0xfc, 0x44 }, +-}; +- +-static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_fm_high_sensitivity = 1, +- .port2_invert_for_secam_lc = 1, +- .port1_set_for_fm_mono = 1, +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, +- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), +- .iffreq = 16 * 36.125, /*MHz*/ +- }, +-}; +- +-static struct tuner_params tuner_philips_fmd1216mex_mk3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_fmd1216me_mk3_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_pal_ranges), +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_fm_high_sensitivity = 1, +- .port2_invert_for_secam_lc = 1, +- .port1_set_for_fm_mono = 1, +- .radio_if = 1, +- .fm_gain_normal = 1, +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, +- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), +- .iffreq = 16 * 36.125, /*MHz*/ +- }, +-}; +- +-/* ------ TUNER_LG_TDVS_H06XF - LG INNOTEK / INFINEON ATSC ----- */ +- +-static struct tuner_range tuner_tua6034_ntsc_ranges[] = { +- { 16 * 165.00 /*MHz*/, 0x8e, 0x01 }, +- { 16 * 450.00 /*MHz*/, 0x8e, 0x02 }, +- { 16 * 999.99 , 0x8e, 0x04 }, +-}; +- +-static struct tuner_range tuner_tua6034_atsc_ranges[] = { +- { 16 * 165.00 /*MHz*/, 0xce, 0x01 }, +- { 16 * 450.00 /*MHz*/, 0xce, 0x02 }, +- { 16 * 999.99 , 0xce, 0x04 }, +-}; +- +-static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tua6034_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_tua6034_atsc_ranges, +- .count = ARRAY_SIZE(tuner_tua6034_atsc_ranges), +- .iffreq = 16 * 44.00, +- }, +-}; +- +-/* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */ +- +-static struct tuner_range tuner_ymec_tvf66t5_b_dff_pal_ranges[] = { +- { 16 * 160.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 464.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_ymec_tvf66t5_b_dff_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_ymec_tvf66t5_b_dff_pal_ranges, +- .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_pal_ranges), +- }, +-}; +- +-/* ------------ TUNER_LG_NTSC_TALN_MINI - LGINNOTEK NTSC ------------ */ +- +-static struct tuner_range tuner_lg_taln_ntsc_ranges[] = { +- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 373.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_range tuner_lg_taln_pal_secam_ranges[] = { +- { 16 * 150.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 425.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_lg_taln_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_lg_taln_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_lg_taln_ntsc_ranges), +- },{ +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_lg_taln_pal_secam_ranges, +- .count = ARRAY_SIZE(tuner_lg_taln_pal_secam_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_TD1316 - Philips PAL ------------ */ +- +-static struct tuner_range tuner_philips_td1316_pal_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0xc8, 0xa1, }, +- { 16 * 442.00 /*MHz*/, 0xc8, 0xa2, }, +- { 16 * 999.99 , 0xc8, 0xa4, }, +-}; +- +-static struct tuner_range tuner_philips_td1316_dvb_ranges[] = { +- { 16 * 93.834 /*MHz*/, 0xca, 0x60, }, +- { 16 * 123.834 /*MHz*/, 0xca, 0xa0, }, +- { 16 * 163.834 /*MHz*/, 0xca, 0xc0, }, +- { 16 * 253.834 /*MHz*/, 0xca, 0x60, }, +- { 16 * 383.834 /*MHz*/, 0xca, 0xa0, }, +- { 16 * 443.834 /*MHz*/, 0xca, 0xc0, }, +- { 16 * 583.834 /*MHz*/, 0xca, 0x60, }, +- { 16 * 793.834 /*MHz*/, 0xca, 0xa0, }, +- { 16 * 999.999 , 0xca, 0xe0, }, +-}; +- +-static struct tuner_params tuner_philips_td1316_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_philips_td1316_pal_ranges, +- .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_philips_td1316_dvb_ranges, +- .count = ARRAY_SIZE(tuner_philips_td1316_dvb_ranges), +- .iffreq = 16 * 36.166667 /*MHz*/, +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */ +- +-static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0xce, 0x01, }, +- { 16 * 454.00 /*MHz*/, 0xce, 0x02, }, +- { 16 * 999.99 , 0xce, 0x04, }, +-}; +- +-static struct tuner_range tuner_tuv1236d_atsc_ranges[] = { +- { 16 * 157.25 /*MHz*/, 0xc6, 0x41, }, +- { 16 * 454.00 /*MHz*/, 0xc6, 0x42, }, +- { 16 * 999.99 , 0xc6, 0x44, }, +-}; +- +-static struct tuner_params tuner_tuv1236d_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tuv1236d_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_tuv1236d_atsc_ranges, +- .count = ARRAY_SIZE(tuner_tuv1236d_atsc_ranges), +- .iffreq = 16 * 44.00, +- }, +-}; +- +-/* ------------ TUNER_TNF_xxx5 - Texas Instruments--------- */ +-/* This is known to work with Tenna TVF58t5-MFF and TVF5835 MFF +- * but it is expected to work also with other Tenna/Ymec +- * models based on TI SN 761677 chip on both PAL and NTSC +- */ +- +-static struct tuner_range tuner_tnf_5335_d_if_pal_ranges[] = { +- { 16 * 168.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 471.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_range tuner_tnf_5335mf_ntsc_ranges[] = { +- { 16 * 169.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 469.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x08, }, +-}; +- +-static struct tuner_params tuner_tnf_5335mf_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tnf_5335mf_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tnf_5335mf_ntsc_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_tnf_5335_d_if_pal_ranges, +- .count = ARRAY_SIZE(tuner_tnf_5335_d_if_pal_ranges), +- }, +-}; +- +-/* 70-79 */ +-/* ------------ TUNER_SAMSUNG_TCPN_2121P30A - Samsung NTSC ------------ */ +- +-/* '+ 4' turns on the Low Noise Amplifier */ +-static struct tuner_range tuner_samsung_tcpn_2121p30a_ntsc_ranges[] = { +- { 16 * 130.00 /*MHz*/, 0xce, 0x01 + 4, }, +- { 16 * 364.50 /*MHz*/, 0xce, 0x02 + 4, }, +- { 16 * 999.99 , 0xce, 0x08 + 4, }, +-}; +- +-static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_samsung_tcpn_2121p30a_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_ntsc_ranges), +- }, +-}; +- +-/* ------------ TUNER_THOMSON_FE6600 - DViCO Hybrid PAL ------------ */ +- +-static struct tuner_range tuner_thomson_fe6600_pal_ranges[] = { +- { 16 * 160.00 /*MHz*/, 0xfe, 0x11, }, +- { 16 * 442.00 /*MHz*/, 0xf6, 0x12, }, +- { 16 * 999.99 , 0xf6, 0x18, }, +-}; +- +-static struct tuner_range tuner_thomson_fe6600_dvb_ranges[] = { +- { 16 * 250.00 /*MHz*/, 0xb4, 0x12, }, +- { 16 * 455.00 /*MHz*/, 0xfe, 0x11, }, +- { 16 * 775.50 /*MHz*/, 0xbc, 0x18, }, +- { 16 * 999.99 , 0xf4, 0x18, }, +-}; +- +-static struct tuner_params tuner_thomson_fe6600_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_thomson_fe6600_pal_ranges, +- .count = ARRAY_SIZE(tuner_thomson_fe6600_pal_ranges), +- }, +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_thomson_fe6600_dvb_ranges, +- .count = ARRAY_SIZE(tuner_thomson_fe6600_dvb_ranges), +- .iffreq = 16 * 36.125 /*MHz*/, +- }, +-}; +- +-/* ------------ TUNER_SAMSUNG_TCPG_6121P30A - Samsung PAL ------------ */ +- +-/* '+ 4' turns on the Low Noise Amplifier */ +-static struct tuner_range tuner_samsung_tcpg_6121p30a_pal_ranges[] = { +- { 16 * 146.25 /*MHz*/, 0xce, 0x01 + 4, }, +- { 16 * 428.50 /*MHz*/, 0xce, 0x02 + 4, }, +- { 16 * 999.99 , 0xce, 0x08 + 4, }, +-}; +- +-static struct tuner_params tuner_samsung_tcpg_6121p30a_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_samsung_tcpg_6121p30a_pal_ranges, +- .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_pal_ranges), +- .has_tda9887 = 1, +- .port1_active = 1, +- .port2_active = 1, +- .port2_invert_for_secam_lc = 1, +- }, +-}; +- +-/* ------------ TUNER_TCL_MF02GIP-5N-E - TCL MF02GIP-5N ------------ */ +- +-static struct tuner_range tuner_tcl_mf02gip_5n_ntsc_ranges[] = { +- { 16 * 172.00 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 448.00 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_tcl_mf02gip_5n_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_tcl_mf02gip_5n_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_ntsc_ranges), +- .cb_first_if_lower_freq = 1, +- }, +-}; +- +-/* 80-89 */ +-/* --------- TUNER_PHILIPS_FQ1216LME_MK3 -- active loopthrough, no FM ------- */ +- +-static struct tuner_params tuner_fq1216lme_mk3_params[] = { +- { +- .type = TUNER_PARAM_TYPE_PAL, +- .ranges = tuner_fm1216me_mk3_pal_ranges, +- .count = ARRAY_SIZE(tuner_fm1216me_mk3_pal_ranges), +- .cb_first_if_lower_freq = 1, /* not specified, but safe to do */ +- .has_tda9887 = 1, /* TDA9886 */ +- .port1_active = 1, +- .port2_active = 1, +- .port2_invert_for_secam_lc = 1, +- .default_top_low = 4, +- .default_top_mid = 4, +- .default_top_high = 4, +- .default_top_secam_low = 4, +- .default_top_secam_mid = 4, +- .default_top_secam_high = 4, +- }, +-}; +- +-/* ----- TUNER_PARTSNIC_PTI_5NF05 - Partsnic (Daewoo) PTI-5NF05 NTSC ----- */ +- +-static struct tuner_range tuner_partsnic_pti_5nf05_ranges[] = { +- /* The datasheet specified channel ranges and the bandswitch byte */ +- /* The control byte value of 0x8e is just a guess */ +- { 16 * 133.25 /*MHz*/, 0x8e, 0x01, }, /* Channels 2 - B */ +- { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, /* Channels C - W+11 */ +- { 16 * 999.99 , 0x8e, 0x08, }, /* Channels W+12 - 69 */ +-}; +- +-static struct tuner_params tuner_partsnic_pti_5nf05_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_partsnic_pti_5nf05_ranges, +- .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_ranges), +- .cb_first_if_lower_freq = 1, /* not specified but safe to do */ +- }, +-}; +- +-/* --------- TUNER_PHILIPS_CU1216L - DVB-C NIM ------------------------- */ +- +-static struct tuner_range tuner_cu1216l_ranges[] = { +- { 16 * 160.25 /*MHz*/, 0xce, 0x01 }, +- { 16 * 444.25 /*MHz*/, 0xce, 0x02 }, +- { 16 * 999.99 , 0xce, 0x04 }, +-}; +- +-static struct tuner_params tuner_philips_cu1216l_params[] = { +- { +- .type = TUNER_PARAM_TYPE_DIGITAL, +- .ranges = tuner_cu1216l_ranges, +- .count = ARRAY_SIZE(tuner_cu1216l_ranges), +- .iffreq = 16 * 36.125, /*MHz*/ +- }, +-}; +- +-/* ---------------------- TUNER_SONY_BTF_PXN01Z ------------------------ */ +- +-static struct tuner_range tuner_sony_btf_pxn01z_ranges[] = { +- { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, +- { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, +- { 16 * 999.99 , 0x8e, 0x04, }, +-}; +- +-static struct tuner_params tuner_sony_btf_pxn01z_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_sony_btf_pxn01z_ranges, +- .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_ranges), +- }, +-}; +- +-/* ------------ TUNER_PHILIPS_FQ1236_MK5 - Philips NTSC ------------ */ +- +-static struct tuner_params tuner_philips_fq1236_mk5_params[] = { +- { +- .type = TUNER_PARAM_TYPE_NTSC, +- .ranges = tuner_fm1236_mk3_ntsc_ranges, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), +- .has_tda9887 = 1, /* TDA9885, no FM radio */ +- }, +-}; +- +-/* --------------------------------------------------------------------- */ +- +-struct tunertype tuners[] = { +- /* 0-9 */ +- [TUNER_TEMIC_PAL] = { /* TEMIC PAL */ +- .name = "Temic PAL (4002 FH5)", +- .params = tuner_temic_pal_params, +- .count = ARRAY_SIZE(tuner_temic_pal_params), +- }, +- [TUNER_PHILIPS_PAL_I] = { /* Philips PAL_I */ +- .name = "Philips PAL_I (FI1246 and compatibles)", +- .params = tuner_philips_pal_i_params, +- .count = ARRAY_SIZE(tuner_philips_pal_i_params), +- }, +- [TUNER_PHILIPS_NTSC] = { /* Philips NTSC */ +- .name = "Philips NTSC (FI1236,FM1236 and compatibles)", +- .params = tuner_philips_ntsc_params, +- .count = ARRAY_SIZE(tuner_philips_ntsc_params), +- }, +- [TUNER_PHILIPS_SECAM] = { /* Philips SECAM */ +- .name = "Philips (SECAM+PAL_BG) (FI1216MF, FM1216MF, FR1216MF)", +- .params = tuner_philips_secam_params, +- .count = ARRAY_SIZE(tuner_philips_secam_params), +- }, +- [TUNER_ABSENT] = { /* Tuner Absent */ +- .name = "NoTuner", +- }, +- [TUNER_PHILIPS_PAL] = { /* Philips PAL */ +- .name = "Philips PAL_BG (FI1216 and compatibles)", +- .params = tuner_philips_pal_params, +- .count = ARRAY_SIZE(tuner_philips_pal_params), +- }, +- [TUNER_TEMIC_NTSC] = { /* TEMIC NTSC */ +- .name = "Temic NTSC (4032 FY5)", +- .params = tuner_temic_ntsc_params, +- .count = ARRAY_SIZE(tuner_temic_ntsc_params), +- }, +- [TUNER_TEMIC_PAL_I] = { /* TEMIC PAL_I */ +- .name = "Temic PAL_I (4062 FY5)", +- .params = tuner_temic_pal_i_params, +- .count = ARRAY_SIZE(tuner_temic_pal_i_params), +- }, +- [TUNER_TEMIC_4036FY5_NTSC] = { /* TEMIC NTSC */ +- .name = "Temic NTSC (4036 FY5)", +- .params = tuner_temic_4036fy5_ntsc_params, +- .count = ARRAY_SIZE(tuner_temic_4036fy5_ntsc_params), +- }, +- [TUNER_ALPS_TSBH1_NTSC] = { /* TEMIC NTSC */ +- .name = "Alps HSBH1", +- .params = tuner_alps_tsbh1_ntsc_params, +- .count = ARRAY_SIZE(tuner_alps_tsbh1_ntsc_params), +- }, +- +- /* 10-19 */ +- [TUNER_ALPS_TSBE1_PAL] = { /* TEMIC PAL */ +- .name = "Alps TSBE1", +- .params = tuner_alps_tsb_1_params, +- .count = ARRAY_SIZE(tuner_alps_tsb_1_params), +- }, +- [TUNER_ALPS_TSBB5_PAL_I] = { /* Alps PAL_I */ +- .name = "Alps TSBB5", +- .params = tuner_alps_tsbb5_params, +- .count = ARRAY_SIZE(tuner_alps_tsbb5_params), +- }, +- [TUNER_ALPS_TSBE5_PAL] = { /* Alps PAL */ +- .name = "Alps TSBE5", +- .params = tuner_alps_tsbe5_params, +- .count = ARRAY_SIZE(tuner_alps_tsbe5_params), +- }, +- [TUNER_ALPS_TSBC5_PAL] = { /* Alps PAL */ +- .name = "Alps TSBC5", +- .params = tuner_alps_tsbc5_params, +- .count = ARRAY_SIZE(tuner_alps_tsbc5_params), +- }, +- [TUNER_TEMIC_4006FH5_PAL] = { /* TEMIC PAL */ +- .name = "Temic PAL_BG (4006FH5)", +- .params = tuner_temic_4006fh5_params, +- .count = ARRAY_SIZE(tuner_temic_4006fh5_params), +- }, +- [TUNER_ALPS_TSHC6_NTSC] = { /* Alps NTSC */ +- .name = "Alps TSCH6", +- .params = tuner_alps_tshc6_params, +- .count = ARRAY_SIZE(tuner_alps_tshc6_params), +- }, +- [TUNER_TEMIC_PAL_DK] = { /* TEMIC PAL */ +- .name = "Temic PAL_DK (4016 FY5)", +- .params = tuner_temic_pal_dk_params, +- .count = ARRAY_SIZE(tuner_temic_pal_dk_params), +- }, +- [TUNER_PHILIPS_NTSC_M] = { /* Philips NTSC */ +- .name = "Philips NTSC_M (MK2)", +- .params = tuner_philips_ntsc_m_params, +- .count = ARRAY_SIZE(tuner_philips_ntsc_m_params), +- }, +- [TUNER_TEMIC_4066FY5_PAL_I] = { /* TEMIC PAL_I */ +- .name = "Temic PAL_I (4066 FY5)", +- .params = tuner_temic_4066fy5_pal_i_params, +- .count = ARRAY_SIZE(tuner_temic_4066fy5_pal_i_params), +- }, +- [TUNER_TEMIC_4006FN5_MULTI_PAL] = { /* TEMIC PAL */ +- .name = "Temic PAL* auto (4006 FN5)", +- .params = tuner_temic_4006fn5_multi_params, +- .count = ARRAY_SIZE(tuner_temic_4006fn5_multi_params), +- }, +- +- /* 20-29 */ +- [TUNER_TEMIC_4009FR5_PAL] = { /* TEMIC PAL */ +- .name = "Temic PAL_BG (4009 FR5) or PAL_I (4069 FR5)", +- .params = tuner_temic_4009f_5_params, +- .count = ARRAY_SIZE(tuner_temic_4009f_5_params), +- }, +- [TUNER_TEMIC_4039FR5_NTSC] = { /* TEMIC NTSC */ +- .name = "Temic NTSC (4039 FR5)", +- .params = tuner_temic_4039fr5_params, +- .count = ARRAY_SIZE(tuner_temic_4039fr5_params), +- }, +- [TUNER_TEMIC_4046FM5] = { /* TEMIC PAL */ +- .name = "Temic PAL/SECAM multi (4046 FM5)", +- .params = tuner_temic_4046fm5_params, +- .count = ARRAY_SIZE(tuner_temic_4046fm5_params), +- }, +- [TUNER_PHILIPS_PAL_DK] = { /* Philips PAL */ +- .name = "Philips PAL_DK (FI1256 and compatibles)", +- .params = tuner_philips_pal_dk_params, +- .count = ARRAY_SIZE(tuner_philips_pal_dk_params), +- }, +- [TUNER_PHILIPS_FQ1216ME] = { /* Philips PAL */ +- .name = "Philips PAL/SECAM multi (FQ1216ME)", +- .params = tuner_philips_fq1216me_params, +- .count = ARRAY_SIZE(tuner_philips_fq1216me_params), +- }, +- [TUNER_LG_PAL_I_FM] = { /* LGINNOTEK PAL_I */ +- .name = "LG PAL_I+FM (TAPC-I001D)", +- .params = tuner_lg_pal_i_fm_params, +- .count = ARRAY_SIZE(tuner_lg_pal_i_fm_params), +- }, +- [TUNER_LG_PAL_I] = { /* LGINNOTEK PAL_I */ +- .name = "LG PAL_I (TAPC-I701D)", +- .params = tuner_lg_pal_i_params, +- .count = ARRAY_SIZE(tuner_lg_pal_i_params), +- }, +- [TUNER_LG_NTSC_FM] = { /* LGINNOTEK NTSC */ +- .name = "LG NTSC+FM (TPI8NSR01F)", +- .params = tuner_lg_ntsc_fm_params, +- .count = ARRAY_SIZE(tuner_lg_ntsc_fm_params), +- }, +- [TUNER_LG_PAL_FM] = { /* LGINNOTEK PAL */ +- .name = "LG PAL_BG+FM (TPI8PSB01D)", +- .params = tuner_lg_pal_fm_params, +- .count = ARRAY_SIZE(tuner_lg_pal_fm_params), +- }, +- [TUNER_LG_PAL] = { /* LGINNOTEK PAL */ +- .name = "LG PAL_BG (TPI8PSB11D)", +- .params = tuner_lg_pal_params, +- .count = ARRAY_SIZE(tuner_lg_pal_params), +- }, +- +- /* 30-39 */ +- [TUNER_TEMIC_4009FN5_MULTI_PAL_FM] = { /* TEMIC PAL */ +- .name = "Temic PAL* auto + FM (4009 FN5)", +- .params = tuner_temic_4009_fn5_multi_pal_fm_params, +- .count = ARRAY_SIZE(tuner_temic_4009_fn5_multi_pal_fm_params), +- }, +- [TUNER_SHARP_2U5JF5540_NTSC] = { /* SHARP NTSC */ +- .name = "SHARP NTSC_JP (2U5JF5540)", +- .params = tuner_sharp_2u5jf5540_params, +- .count = ARRAY_SIZE(tuner_sharp_2u5jf5540_params), +- }, +- [TUNER_Samsung_PAL_TCPM9091PD27] = { /* Samsung PAL */ +- .name = "Samsung PAL TCPM9091PD27", +- .params = tuner_samsung_pal_tcpm9091pd27_params, +- .count = ARRAY_SIZE(tuner_samsung_pal_tcpm9091pd27_params), +- }, +- [TUNER_MT2032] = { /* Microtune PAL|NTSC */ +- .name = "MT20xx universal", +- /* see mt20xx.c for details */ }, +- [TUNER_TEMIC_4106FH5] = { /* TEMIC PAL */ +- .name = "Temic PAL_BG (4106 FH5)", +- .params = tuner_temic_4106fh5_params, +- .count = ARRAY_SIZE(tuner_temic_4106fh5_params), +- }, +- [TUNER_TEMIC_4012FY5] = { /* TEMIC PAL */ +- .name = "Temic PAL_DK/SECAM_L (4012 FY5)", +- .params = tuner_temic_4012fy5_params, +- .count = ARRAY_SIZE(tuner_temic_4012fy5_params), +- }, +- [TUNER_TEMIC_4136FY5] = { /* TEMIC NTSC */ +- .name = "Temic NTSC (4136 FY5)", +- .params = tuner_temic_4136_fy5_params, +- .count = ARRAY_SIZE(tuner_temic_4136_fy5_params), +- }, +- [TUNER_LG_PAL_NEW_TAPC] = { /* LGINNOTEK PAL */ +- .name = "LG PAL (newer TAPC series)", +- .params = tuner_lg_pal_new_tapc_params, +- .count = ARRAY_SIZE(tuner_lg_pal_new_tapc_params), +- }, +- [TUNER_PHILIPS_FM1216ME_MK3] = { /* Philips PAL */ +- .name = "Philips PAL/SECAM multi (FM1216ME MK3)", +- .params = tuner_fm1216me_mk3_params, +- .count = ARRAY_SIZE(tuner_fm1216me_mk3_params), +- }, +- [TUNER_LG_NTSC_NEW_TAPC] = { /* LGINNOTEK NTSC */ +- .name = "LG NTSC (newer TAPC series)", +- .params = tuner_lg_ntsc_new_tapc_params, +- .count = ARRAY_SIZE(tuner_lg_ntsc_new_tapc_params), +- }, +- +- /* 40-49 */ +- [TUNER_HITACHI_NTSC] = { /* HITACHI NTSC */ +- .name = "HITACHI V7-J180AT", +- .params = tuner_hitachi_ntsc_params, +- .count = ARRAY_SIZE(tuner_hitachi_ntsc_params), +- }, +- [TUNER_PHILIPS_PAL_MK] = { /* Philips PAL */ +- .name = "Philips PAL_MK (FI1216 MK)", +- .params = tuner_philips_pal_mk_params, +- .count = ARRAY_SIZE(tuner_philips_pal_mk_params), +- }, +- [TUNER_PHILIPS_FCV1236D] = { /* Philips ATSC */ +- .name = "Philips FCV1236D ATSC/NTSC dual in", +- .params = tuner_philips_fcv1236d_params, +- .count = ARRAY_SIZE(tuner_philips_fcv1236d_params), +- .min = 16 * 53.00, +- .max = 16 * 803.00, +- .stepsize = 62500, +- }, +- [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ +- .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", +- .params = tuner_fm1236_mk3_params, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_params), +- }, +- [TUNER_PHILIPS_4IN1] = { /* Philips NTSC */ +- .name = "Philips 4 in 1 (ATI TV Wonder Pro/Conexant)", +- .params = tuner_philips_4in1_params, +- .count = ARRAY_SIZE(tuner_philips_4in1_params), +- }, +- [TUNER_MICROTUNE_4049FM5] = { /* Microtune PAL */ +- .name = "Microtune 4049 FM5", +- .params = tuner_microtune_4049_fm5_params, +- .count = ARRAY_SIZE(tuner_microtune_4049_fm5_params), +- }, +- [TUNER_PANASONIC_VP27] = { /* Panasonic NTSC */ +- .name = "Panasonic VP27s/ENGE4324D", +- .params = tuner_panasonic_vp27_params, +- .count = ARRAY_SIZE(tuner_panasonic_vp27_params), +- }, +- [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */ +- .name = "LG NTSC (TAPE series)", +- .params = tuner_fm1236_mk3_params, +- .count = ARRAY_SIZE(tuner_fm1236_mk3_params), +- }, +- [TUNER_TNF_8831BGFF] = { /* Philips PAL */ +- .name = "Tenna TNF 8831 BGFF)", +- .params = tuner_tnf_8831bgff_params, +- .count = ARRAY_SIZE(tuner_tnf_8831bgff_params), +- }, +- [TUNER_MICROTUNE_4042FI5] = { /* Microtune NTSC */ +- .name = "Microtune 4042 FI5 ATSC/NTSC dual in", +- .params = tuner_microtune_4042fi5_params, +- .count = ARRAY_SIZE(tuner_microtune_4042fi5_params), +- .min = 16 * 57.00, +- .max = 16 * 858.00, +- .stepsize = 62500, +- }, +- +- /* 50-59 */ +- [TUNER_TCL_2002N] = { /* TCL NTSC */ +- .name = "TCL 2002N", +- .params = tuner_tcl_2002n_params, +- .count = ARRAY_SIZE(tuner_tcl_2002n_params), +- }, +- [TUNER_PHILIPS_FM1256_IH3] = { /* Philips PAL */ +- .name = "Philips PAL/SECAM_D (FM 1256 I-H3)", +- .params = tuner_philips_fm1256_ih3_params, +- .count = ARRAY_SIZE(tuner_philips_fm1256_ih3_params), +- }, +- [TUNER_THOMSON_DTT7610] = { /* THOMSON ATSC */ +- .name = "Thomson DTT 7610 (ATSC/NTSC)", +- .params = tuner_thomson_dtt7610_params, +- .count = ARRAY_SIZE(tuner_thomson_dtt7610_params), +- .min = 16 * 44.00, +- .max = 16 * 958.00, +- .stepsize = 62500, +- }, +- [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */ +- .name = "Philips FQ1286", +- .params = tuner_philips_fq1286_params, +- .count = ARRAY_SIZE(tuner_philips_fq1286_params), +- }, +- [TUNER_PHILIPS_TDA8290] = { /* Philips PAL|NTSC */ +- .name = "Philips/NXP TDA 8290/8295 + 8275/8275A/18271", +- /* see tda8290.c for details */ }, +- [TUNER_TCL_2002MB] = { /* TCL PAL */ +- .name = "TCL 2002MB", +- .params = tuner_tcl_2002mb_params, +- .count = ARRAY_SIZE(tuner_tcl_2002mb_params), +- }, +- [TUNER_PHILIPS_FQ1216AME_MK4] = { /* Philips PAL */ +- .name = "Philips PAL/SECAM multi (FQ1216AME MK4)", +- .params = tuner_philips_fq1216ame_mk4_params, +- .count = ARRAY_SIZE(tuner_philips_fq1216ame_mk4_params), +- }, +- [TUNER_PHILIPS_FQ1236A_MK4] = { /* Philips NTSC */ +- .name = "Philips FQ1236A MK4", +- .params = tuner_philips_fq1236a_mk4_params, +- .count = ARRAY_SIZE(tuner_philips_fq1236a_mk4_params), +- }, +- [TUNER_YMEC_TVF_8531MF] = { /* Philips NTSC */ +- .name = "Ymec TVision TVF-8531MF/8831MF/8731MF", +- .params = tuner_ymec_tvf_8531mf_params, +- .count = ARRAY_SIZE(tuner_ymec_tvf_8531mf_params), +- }, +- [TUNER_YMEC_TVF_5533MF] = { /* Philips NTSC */ +- .name = "Ymec TVision TVF-5533MF", +- .params = tuner_ymec_tvf_5533mf_params, +- .count = ARRAY_SIZE(tuner_ymec_tvf_5533mf_params), +- }, +- +- /* 60-69 */ +- [TUNER_THOMSON_DTT761X] = { /* THOMSON ATSC */ +- /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ +- .name = "Thomson DTT 761X (ATSC/NTSC)", +- .params = tuner_thomson_dtt761x_params, +- .count = ARRAY_SIZE(tuner_thomson_dtt761x_params), +- .min = 16 * 57.00, +- .max = 16 * 863.00, +- .stepsize = 62500, +- .initdata = tua603x_agc103, +- }, +- [TUNER_TENA_9533_DI] = { /* Philips PAL */ +- .name = "Tena TNF9533-D/IF/TNF9533-B/DF", +- .params = tuner_tena_9533_di_params, +- .count = ARRAY_SIZE(tuner_tena_9533_di_params), +- }, +- [TUNER_TEA5767] = { /* Philips RADIO */ +- .name = "Philips TEA5767HN FM Radio", +- /* see tea5767.c for details */ +- }, +- [TUNER_PHILIPS_FMD1216ME_MK3] = { /* Philips PAL */ +- .name = "Philips FMD1216ME MK3 Hybrid Tuner", +- .params = tuner_philips_fmd1216me_mk3_params, +- .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), +- .min = 16 * 50.87, +- .max = 16 * 858.00, +- .stepsize = 166667, +- .initdata = tua603x_agc112, +- .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, +- }, +- [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */ +- .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ +- .params = tuner_lg_tdvs_h06xf_params, +- .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params), +- .min = 16 * 54.00, +- .max = 16 * 863.00, +- .stepsize = 62500, +- .initdata = tua603x_agc103, +- }, +- [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ +- .name = "Ymec TVF66T5-B/DFF", +- .params = tuner_ymec_tvf66t5_b_dff_params, +- .count = ARRAY_SIZE(tuner_ymec_tvf66t5_b_dff_params), +- }, +- [TUNER_LG_TALN] = { /* LGINNOTEK NTSC / PAL / SECAM */ +- .name = "LG TALN series", +- .params = tuner_lg_taln_params, +- .count = ARRAY_SIZE(tuner_lg_taln_params), +- }, +- [TUNER_PHILIPS_TD1316] = { /* Philips PAL */ +- .name = "Philips TD1316 Hybrid Tuner", +- .params = tuner_philips_td1316_params, +- .count = ARRAY_SIZE(tuner_philips_td1316_params), +- .min = 16 * 87.00, +- .max = 16 * 895.00, +- .stepsize = 166667, +- }, +- [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */ +- .name = "Philips TUV1236D ATSC/NTSC dual in", +- .params = tuner_tuv1236d_params, +- .count = ARRAY_SIZE(tuner_tuv1236d_params), +- .min = 16 * 54.00, +- .max = 16 * 864.00, +- .stepsize = 62500, +- }, +- [TUNER_TNF_5335MF] = { /* Tenna PAL/NTSC */ +- .name = "Tena TNF 5335 and similar models", +- .params = tuner_tnf_5335mf_params, +- .count = ARRAY_SIZE(tuner_tnf_5335mf_params), +- }, +- +- /* 70-79 */ +- [TUNER_SAMSUNG_TCPN_2121P30A] = { /* Samsung NTSC */ +- .name = "Samsung TCPN 2121P30A", +- .params = tuner_samsung_tcpn_2121p30a_params, +- .count = ARRAY_SIZE(tuner_samsung_tcpn_2121p30a_params), +- }, +- [TUNER_XC2028] = { /* Xceive 2028 */ +- .name = "Xceive xc2028/xc3028 tuner", +- /* see tuner-xc2028.c for details */ +- }, +- [TUNER_THOMSON_FE6600] = { /* Thomson PAL / DVB-T */ +- .name = "Thomson FE6600", +- .params = tuner_thomson_fe6600_params, +- .count = ARRAY_SIZE(tuner_thomson_fe6600_params), +- .min = 16 * 44.25, +- .max = 16 * 858.00, +- .stepsize = 166667, +- }, +- [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ +- .name = "Samsung TCPG 6121P30A", +- .params = tuner_samsung_tcpg_6121p30a_params, +- .count = ARRAY_SIZE(tuner_samsung_tcpg_6121p30a_params), +- }, +- [TUNER_TDA9887] = { /* Philips TDA 9887 IF PLL Demodulator. +- This chip is part of some modern tuners */ +- .name = "Philips TDA988[5,6,7] IF PLL Demodulator", +- /* see tda9887.c for details */ +- }, +- [TUNER_TEA5761] = { /* Philips RADIO */ +- .name = "Philips TEA5761 FM Radio", +- /* see tea5767.c for details */ +- }, +- [TUNER_XC5000] = { /* Xceive 5000 */ +- .name = "Xceive 5000 tuner", +- /* see xc5000.c for details */ +- }, +- [TUNER_XC4000] = { /* Xceive 4000 */ +- .name = "Xceive 4000 tuner", +- /* see xc4000.c for details */ +- }, +- [TUNER_TCL_MF02GIP_5N] = { /* TCL tuner MF02GIP-5N-E */ +- .name = "TCL tuner MF02GIP-5N-E", +- .params = tuner_tcl_mf02gip_5n_params, +- .count = ARRAY_SIZE(tuner_tcl_mf02gip_5n_params), +- }, +- [TUNER_PHILIPS_FMD1216MEX_MK3] = { /* Philips PAL */ +- .name = "Philips FMD1216MEX MK3 Hybrid Tuner", +- .params = tuner_philips_fmd1216mex_mk3_params, +- .count = ARRAY_SIZE(tuner_philips_fmd1216mex_mk3_params), +- .min = 16 * 50.87, +- .max = 16 * 858.00, +- .stepsize = 166667, +- .initdata = tua603x_agc112, +- .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, +- }, +- [TUNER_PHILIPS_FM1216MK5] = { /* Philips PAL */ +- .name = "Philips PAL/SECAM multi (FM1216 MK5)", +- .params = tuner_fm1216mk5_params, +- .count = ARRAY_SIZE(tuner_fm1216mk5_params), +- }, +- +- /* 80-89 */ +- [TUNER_PHILIPS_FQ1216LME_MK3] = { /* PAL/SECAM, Loop-thru, no FM */ +- .name = "Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough", +- .params = tuner_fq1216lme_mk3_params, +- .count = ARRAY_SIZE(tuner_fq1216lme_mk3_params), +- }, +- +- [TUNER_PARTSNIC_PTI_5NF05] = { +- .name = "Partsnic (Daewoo) PTI-5NF05", +- .params = tuner_partsnic_pti_5nf05_params, +- .count = ARRAY_SIZE(tuner_partsnic_pti_5nf05_params), +- }, +- [TUNER_PHILIPS_CU1216L] = { +- .name = "Philips CU1216L", +- .params = tuner_philips_cu1216l_params, +- .count = ARRAY_SIZE(tuner_philips_cu1216l_params), +- .stepsize = 62500, +- }, +- [TUNER_NXP_TDA18271] = { +- .name = "NXP TDA18271", +- /* see tda18271-fe.c for details */ +- }, +- [TUNER_SONY_BTF_PXN01Z] = { +- .name = "Sony BTF-Pxn01Z", +- .params = tuner_sony_btf_pxn01z_params, +- .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_params), +- }, +- [TUNER_PHILIPS_FQ1236_MK5] = { /* NTSC, TDA9885, no FM radio */ +- .name = "Philips FQ1236 MK5", +- .params = tuner_philips_fq1236_mk5_params, +- .count = ARRAY_SIZE(tuner_philips_fq1236_mk5_params), +- }, +- [TUNER_TENA_TNF_5337] = { /* Tena 5337 MFD */ +- .name = "Tena TNF5337 MFD", +- .params = tuner_tena_tnf_5337_params, +- .count = ARRAY_SIZE(tuner_tena_tnf_5337_params), +- }, +-}; +-EXPORT_SYMBOL(tuners); +- +-unsigned const int tuner_count = ARRAY_SIZE(tuners); +-EXPORT_SYMBOL(tuner_count); +- +-MODULE_DESCRIPTION("Simple tuner device type database"); +-MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tuner-xc2028-types.h b/drivers/media/common/tuners/tuner-xc2028-types.h +deleted file mode 100644 +index 74dc46a..0000000 +--- a/drivers/media/common/tuners/tuner-xc2028-types.h ++++ /dev/null +@@ -1,141 +0,0 @@ +-/* tuner-xc2028_types +- * +- * This file includes internal tipes to be used inside tuner-xc2028. +- * Shouldn't be included outside tuner-xc2028 +- * +- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) +- * This code is placed under the terms of the GNU General Public License v2 +- */ +- +-/* xc3028 firmware types */ +- +-/* BASE firmware should be loaded before any other firmware */ +-#define BASE (1<<0) +-#define BASE_TYPES (BASE|F8MHZ|MTS|FM|INPUT1|INPUT2|INIT1) +- +-/* F8MHZ marks BASE firmwares for 8 MHz Bandwidth */ +-#define F8MHZ (1<<1) +- +-/* Multichannel Television Sound (MTS) +- Those firmwares are capable of using xc2038 DSP to decode audio and +- produce a baseband audio output on some pins of the chip. +- There are MTS firmwares for the most used video standards. It should be +- required to use MTS firmwares, depending on the way audio is routed into +- the bridge chip +- */ +-#define MTS (1<<2) +- +-/* FIXME: I have no idea what's the difference between +- D2620 and D2633 firmwares +- */ +-#define D2620 (1<<3) +-#define D2633 (1<<4) +- +-/* DTV firmwares for 6, 7 and 8 MHz +- DTV6 - 6MHz - ATSC/DVB-C/DVB-T/ISDB-T/DOCSIS +- DTV8 - 8MHz - DVB-C/DVB-T +- */ +-#define DTV6 (1 << 5) +-#define QAM (1 << 6) +-#define DTV7 (1<<7) +-#define DTV78 (1<<8) +-#define DTV8 (1<<9) +- +-#define DTV_TYPES (D2620|D2633|DTV6|QAM|DTV7|DTV78|DTV8|ATSC) +- +-/* There's a FM | BASE firmware + FM specific firmware (std=0) */ +-#define FM (1<<10) +- +-#define STD_SPECIFIC_TYPES (MTS|FM|LCD|NOGD) +- +-/* Applies only for FM firmware +- Makes it use RF input 1 (pin #2) instead of input 2 (pin #4) +- */ +-#define INPUT1 (1<<11) +- +- +-/* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M) +- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) +- There are variants both with and without NOGD +- Those firmwares produce better result with LCD displays +- */ +-#define LCD (1<<12) +- +-/* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M) +- and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) +- The NOGD firmwares don't have group delay compensation filter +- */ +-#define NOGD (1<<13) +- +-/* Old firmwares were broken into init0 and init1 */ +-#define INIT1 (1<<14) +- +-/* SCODE firmware selects particular behaviours */ +-#define MONO (1 << 15) +-#define ATSC (1 << 16) +-#define IF (1 << 17) +-#define LG60 (1 << 18) +-#define ATI638 (1 << 19) +-#define OREN538 (1 << 20) +-#define OREN36 (1 << 21) +-#define TOYOTA388 (1 << 22) +-#define TOYOTA794 (1 << 23) +-#define DIBCOM52 (1 << 24) +-#define ZARLINK456 (1 << 25) +-#define CHINA (1 << 26) +-#define F6MHZ (1 << 27) +-#define INPUT2 (1 << 28) +-#define SCODE (1 << 29) +- +-/* This flag identifies that the scode table has a new format */ +-#define HAS_IF (1 << 30) +- +-/* There are different scode tables for MTS and non-MTS. +- The MTS firmwares support mono only +- */ +-#define SCODE_TYPES (SCODE | MTS) +- +- +-/* Newer types not defined on videodev2.h. +- The original idea were to move all those types to videodev2.h, but +- it seemed overkill, since, with the exception of SECAM/K3, the other +- types seem to be autodetected. +- It is not clear where secam/k3 is used, nor we have a feedback of this +- working or being autodetected by the standard secam firmware. +- */ +- +-#define V4L2_STD_SECAM_K3 (0x04000000) +- +-/* Audio types */ +- +-#define V4L2_STD_A2_A (1LL<<32) +-#define V4L2_STD_A2_B (1LL<<33) +-#define V4L2_STD_NICAM_A (1LL<<34) +-#define V4L2_STD_NICAM_B (1LL<<35) +-#define V4L2_STD_AM (1LL<<36) +-#define V4L2_STD_BTSC (1LL<<37) +-#define V4L2_STD_EIAJ (1LL<<38) +- +-#define V4L2_STD_A2 (V4L2_STD_A2_A | V4L2_STD_A2_B) +-#define V4L2_STD_NICAM (V4L2_STD_NICAM_A | V4L2_STD_NICAM_B) +- +-/* To preserve backward compatibilty, +- (std & V4L2_STD_AUDIO) = 0 means that ALL audio stds are supported +- */ +- +-#define V4L2_STD_AUDIO (V4L2_STD_A2 | \ +- V4L2_STD_NICAM | \ +- V4L2_STD_AM | \ +- V4L2_STD_BTSC | \ +- V4L2_STD_EIAJ) +- +-/* Used standards with audio restrictions */ +- +-#define V4L2_STD_PAL_BG_A2_A (V4L2_STD_PAL_BG | V4L2_STD_A2_A) +-#define V4L2_STD_PAL_BG_A2_B (V4L2_STD_PAL_BG | V4L2_STD_A2_B) +-#define V4L2_STD_PAL_BG_NICAM_A (V4L2_STD_PAL_BG | V4L2_STD_NICAM_A) +-#define V4L2_STD_PAL_BG_NICAM_B (V4L2_STD_PAL_BG | V4L2_STD_NICAM_B) +-#define V4L2_STD_PAL_DK_A2 (V4L2_STD_PAL_DK | V4L2_STD_A2) +-#define V4L2_STD_PAL_DK_NICAM (V4L2_STD_PAL_DK | V4L2_STD_NICAM) +-#define V4L2_STD_SECAM_L_NICAM (V4L2_STD_SECAM_L | V4L2_STD_NICAM) +-#define V4L2_STD_SECAM_L_AM (V4L2_STD_SECAM_L | V4L2_STD_AM) +diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c +deleted file mode 100644 +index b5ee3eb..0000000 +--- a/drivers/media/common/tuners/tuner-xc2028.c ++++ /dev/null +@@ -1,1377 +0,0 @@ +-/* tuner-xc2028 +- * +- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) +- * +- * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) +- * - frontend interface +- * +- * This code is placed under the terms of the GNU General Public License v2 +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "tuner-i2c.h" +-#include "tuner-xc2028.h" +-#include "tuner-xc2028-types.h" +- +-#include +-#include "dvb_frontend.h" +- +-/* Registers (Write-only) */ +-#define XREG_INIT 0x00 +-#define XREG_RF_FREQ 0x02 +-#define XREG_POWER_DOWN 0x08 +- +-/* Registers (Read-only) */ +-#define XREG_FREQ_ERROR 0x01 +-#define XREG_LOCK 0x02 +-#define XREG_VERSION 0x04 +-#define XREG_PRODUCT_ID 0x08 +-#define XREG_HSYNC_FREQ 0x10 +-#define XREG_FRAME_LINES 0x20 +-#define XREG_SNR 0x40 +- +-#define XREG_ADC_ENV 0x0100 +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-static int no_poweroff; +-module_param(no_poweroff, int, 0644); +-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n" +- "1 keep device energized and with tuner ready all the times.\n" +- " Faster, but consumes more power and keeps the device hotter\n"); +- +-static char audio_std[8]; +-module_param_string(audio_std, audio_std, sizeof(audio_std), 0); +-MODULE_PARM_DESC(audio_std, +- "Audio standard. XC3028 audio decoder explicitly " +- "needs to know what audio\n" +- "standard is needed for some video standards with audio A2 or NICAM.\n" +- "The valid values are:\n" +- "A2\n" +- "A2/A\n" +- "A2/B\n" +- "NICAM\n" +- "NICAM/A\n" +- "NICAM/B\n"); +- +-static char firmware_name[30]; +-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); +-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " +- "default firmware name\n"); +- +-static LIST_HEAD(hybrid_tuner_instance_list); +-static DEFINE_MUTEX(xc2028_list_mutex); +- +-/* struct for storing firmware table */ +-struct firmware_description { +- unsigned int type; +- v4l2_std_id id; +- __u16 int_freq; +- unsigned char *ptr; +- unsigned int size; +-}; +- +-struct firmware_properties { +- unsigned int type; +- v4l2_std_id id; +- v4l2_std_id std_req; +- __u16 int_freq; +- unsigned int scode_table; +- int scode_nr; +-}; +- +-struct xc2028_data { +- struct list_head hybrid_tuner_instance_list; +- struct tuner_i2c_props i2c_props; +- __u32 frequency; +- +- struct firmware_description *firm; +- int firm_size; +- __u16 firm_version; +- +- __u16 hwmodel; +- __u16 hwvers; +- +- struct xc2028_ctrl ctrl; +- +- struct firmware_properties cur_fw; +- +- struct mutex lock; +-}; +- +-#define i2c_send(priv, buf, size) ({ \ +- int _rc; \ +- _rc = tuner_i2c_xfer_send(&priv->i2c_props, buf, size); \ +- if (size != _rc) \ +- tuner_info("i2c output error: rc = %d (should be %d)\n",\ +- _rc, (int)size); \ +- if (priv->ctrl.msleep) \ +- msleep(priv->ctrl.msleep); \ +- _rc; \ +-}) +- +-#define i2c_rcv(priv, buf, size) ({ \ +- int _rc; \ +- _rc = tuner_i2c_xfer_recv(&priv->i2c_props, buf, size); \ +- if (size != _rc) \ +- tuner_err("i2c input error: rc = %d (should be %d)\n", \ +- _rc, (int)size); \ +- _rc; \ +-}) +- +-#define i2c_send_recv(priv, obuf, osize, ibuf, isize) ({ \ +- int _rc; \ +- _rc = tuner_i2c_xfer_send_recv(&priv->i2c_props, obuf, osize, \ +- ibuf, isize); \ +- if (isize != _rc) \ +- tuner_err("i2c input error: rc = %d (should be %d)\n", \ +- _rc, (int)isize); \ +- if (priv->ctrl.msleep) \ +- msleep(priv->ctrl.msleep); \ +- _rc; \ +-}) +- +-#define send_seq(priv, data...) ({ \ +- static u8 _val[] = data; \ +- int _rc; \ +- if (sizeof(_val) != \ +- (_rc = tuner_i2c_xfer_send(&priv->i2c_props, \ +- _val, sizeof(_val)))) { \ +- tuner_err("Error on line %d: %d\n", __LINE__, _rc); \ +- } else if (priv->ctrl.msleep) \ +- msleep(priv->ctrl.msleep); \ +- _rc; \ +-}) +- +-static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) +-{ +- unsigned char buf[2]; +- unsigned char ibuf[2]; +- +- tuner_dbg("%s %04x called\n", __func__, reg); +- +- buf[0] = reg >> 8; +- buf[1] = (unsigned char) reg; +- +- if (i2c_send_recv(priv, buf, 2, ibuf, 2) != 2) +- return -EIO; +- +- *val = (ibuf[1]) | (ibuf[0] << 8); +- return 0; +-} +- +-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) +-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) +-{ +- if (type & BASE) +- printk("BASE "); +- if (type & INIT1) +- printk("INIT1 "); +- if (type & F8MHZ) +- printk("F8MHZ "); +- if (type & MTS) +- printk("MTS "); +- if (type & D2620) +- printk("D2620 "); +- if (type & D2633) +- printk("D2633 "); +- if (type & DTV6) +- printk("DTV6 "); +- if (type & QAM) +- printk("QAM "); +- if (type & DTV7) +- printk("DTV7 "); +- if (type & DTV78) +- printk("DTV78 "); +- if (type & DTV8) +- printk("DTV8 "); +- if (type & FM) +- printk("FM "); +- if (type & INPUT1) +- printk("INPUT1 "); +- if (type & LCD) +- printk("LCD "); +- if (type & NOGD) +- printk("NOGD "); +- if (type & MONO) +- printk("MONO "); +- if (type & ATSC) +- printk("ATSC "); +- if (type & IF) +- printk("IF "); +- if (type & LG60) +- printk("LG60 "); +- if (type & ATI638) +- printk("ATI638 "); +- if (type & OREN538) +- printk("OREN538 "); +- if (type & OREN36) +- printk("OREN36 "); +- if (type & TOYOTA388) +- printk("TOYOTA388 "); +- if (type & TOYOTA794) +- printk("TOYOTA794 "); +- if (type & DIBCOM52) +- printk("DIBCOM52 "); +- if (type & ZARLINK456) +- printk("ZARLINK456 "); +- if (type & CHINA) +- printk("CHINA "); +- if (type & F6MHZ) +- printk("F6MHZ "); +- if (type & INPUT2) +- printk("INPUT2 "); +- if (type & SCODE) +- printk("SCODE "); +- if (type & HAS_IF) +- printk("HAS_IF_%d ", int_freq); +-} +- +-static v4l2_std_id parse_audio_std_option(void) +-{ +- if (strcasecmp(audio_std, "A2") == 0) +- return V4L2_STD_A2; +- if (strcasecmp(audio_std, "A2/A") == 0) +- return V4L2_STD_A2_A; +- if (strcasecmp(audio_std, "A2/B") == 0) +- return V4L2_STD_A2_B; +- if (strcasecmp(audio_std, "NICAM") == 0) +- return V4L2_STD_NICAM; +- if (strcasecmp(audio_std, "NICAM/A") == 0) +- return V4L2_STD_NICAM_A; +- if (strcasecmp(audio_std, "NICAM/B") == 0) +- return V4L2_STD_NICAM_B; +- +- return 0; +-} +- +-static void free_firmware(struct xc2028_data *priv) +-{ +- int i; +- tuner_dbg("%s called\n", __func__); +- +- if (!priv->firm) +- return; +- +- for (i = 0; i < priv->firm_size; i++) +- kfree(priv->firm[i].ptr); +- +- kfree(priv->firm); +- +- priv->firm = NULL; +- priv->firm_size = 0; +- +- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +-} +- +-static int load_all_firmwares(struct dvb_frontend *fe) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- const struct firmware *fw = NULL; +- const unsigned char *p, *endp; +- int rc = 0; +- int n, n_array; +- char name[33]; +- char *fname; +- +- tuner_dbg("%s called\n", __func__); +- +- if (!firmware_name[0]) +- fname = priv->ctrl.fname; +- else +- fname = firmware_name; +- +- tuner_dbg("Reading firmware %s\n", fname); +- rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); +- if (rc < 0) { +- if (rc == -ENOENT) +- tuner_err("Error: firmware %s not found.\n", +- fname); +- else +- tuner_err("Error %d while requesting firmware %s \n", +- rc, fname); +- +- return rc; +- } +- p = fw->data; +- endp = p + fw->size; +- +- if (fw->size < sizeof(name) - 1 + 2 + 2) { +- tuner_err("Error: firmware file %s has invalid size!\n", +- fname); +- goto corrupt; +- } +- +- memcpy(name, p, sizeof(name) - 1); +- name[sizeof(name) - 1] = 0; +- p += sizeof(name) - 1; +- +- priv->firm_version = get_unaligned_le16(p); +- p += 2; +- +- n_array = get_unaligned_le16(p); +- p += 2; +- +- tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", +- n_array, fname, name, +- priv->firm_version >> 8, priv->firm_version & 0xff); +- +- priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL); +- if (priv->firm == NULL) { +- tuner_err("Not enough memory to load firmware file.\n"); +- rc = -ENOMEM; +- goto err; +- } +- priv->firm_size = n_array; +- +- n = -1; +- while (p < endp) { +- __u32 type, size; +- v4l2_std_id id; +- __u16 int_freq = 0; +- +- n++; +- if (n >= n_array) { +- tuner_err("More firmware images in file than " +- "were expected!\n"); +- goto corrupt; +- } +- +- /* Checks if there's enough bytes to read */ +- if (endp - p < sizeof(type) + sizeof(id) + sizeof(size)) +- goto header; +- +- type = get_unaligned_le32(p); +- p += sizeof(type); +- +- id = get_unaligned_le64(p); +- p += sizeof(id); +- +- if (type & HAS_IF) { +- int_freq = get_unaligned_le16(p); +- p += sizeof(int_freq); +- if (endp - p < sizeof(size)) +- goto header; +- } +- +- size = get_unaligned_le32(p); +- p += sizeof(size); +- +- if (!size || size > endp - p) { +- tuner_err("Firmware type "); +- dump_firm_type(type); +- printk("(%x), id %llx is corrupted " +- "(size=%d, expected %d)\n", +- type, (unsigned long long)id, +- (unsigned)(endp - p), size); +- goto corrupt; +- } +- +- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL); +- if (priv->firm[n].ptr == NULL) { +- tuner_err("Not enough memory to load firmware file.\n"); +- rc = -ENOMEM; +- goto err; +- } +- tuner_dbg("Reading firmware type "); +- if (debug) { +- dump_firm_type_and_int_freq(type, int_freq); +- printk("(%x), id %llx, size=%d.\n", +- type, (unsigned long long)id, size); +- } +- +- memcpy(priv->firm[n].ptr, p, size); +- priv->firm[n].type = type; +- priv->firm[n].id = id; +- priv->firm[n].size = size; +- priv->firm[n].int_freq = int_freq; +- +- p += size; +- } +- +- if (n + 1 != priv->firm_size) { +- tuner_err("Firmware file is incomplete!\n"); +- goto corrupt; +- } +- +- goto done; +- +-header: +- tuner_err("Firmware header is incomplete!\n"); +-corrupt: +- rc = -EINVAL; +- tuner_err("Error: firmware file is corrupted!\n"); +- +-err: +- tuner_info("Releasing partially loaded firmware file.\n"); +- free_firmware(priv); +- +-done: +- release_firmware(fw); +- if (rc == 0) +- tuner_dbg("Firmware files loaded.\n"); +- +- return rc; +-} +- +-static int seek_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- int i, best_i = -1, best_nr_matches = 0; +- unsigned int type_mask = 0; +- +- tuner_dbg("%s called, want type=", __func__); +- if (debug) { +- dump_firm_type(type); +- printk("(%x), id %016llx.\n", type, (unsigned long long)*id); +- } +- +- if (!priv->firm) { +- tuner_err("Error! firmware not loaded\n"); +- return -EINVAL; +- } +- +- if (((type & ~SCODE) == 0) && (*id == 0)) +- *id = V4L2_STD_PAL; +- +- if (type & BASE) +- type_mask = BASE_TYPES; +- else if (type & SCODE) { +- type &= SCODE_TYPES; +- type_mask = SCODE_TYPES & ~HAS_IF; +- } else if (type & DTV_TYPES) +- type_mask = DTV_TYPES; +- else if (type & STD_SPECIFIC_TYPES) +- type_mask = STD_SPECIFIC_TYPES; +- +- type &= type_mask; +- +- if (!(type & SCODE)) +- type_mask = ~0; +- +- /* Seek for exact match */ +- for (i = 0; i < priv->firm_size; i++) { +- if ((type == (priv->firm[i].type & type_mask)) && +- (*id == priv->firm[i].id)) +- goto found; +- } +- +- /* Seek for generic video standard match */ +- for (i = 0; i < priv->firm_size; i++) { +- v4l2_std_id match_mask; +- int nr_matches; +- +- if (type != (priv->firm[i].type & type_mask)) +- continue; +- +- match_mask = *id & priv->firm[i].id; +- if (!match_mask) +- continue; +- +- if ((*id & match_mask) == *id) +- goto found; /* Supports all the requested standards */ +- +- nr_matches = hweight64(match_mask); +- if (nr_matches > best_nr_matches) { +- best_nr_matches = nr_matches; +- best_i = i; +- } +- } +- +- if (best_nr_matches > 0) { +- tuner_dbg("Selecting best matching firmware (%d bits) for " +- "type=", best_nr_matches); +- dump_firm_type(type); +- printk("(%x), id %016llx:\n", type, (unsigned long long)*id); +- i = best_i; +- goto found; +- } +- +- /*FIXME: Would make sense to seek for type "hint" match ? */ +- +- i = -ENOENT; +- goto ret; +- +-found: +- *id = priv->firm[i].id; +- +-ret: +- tuner_dbg("%s firmware for type=", (i < 0) ? "Can't find" : "Found"); +- if (debug) { +- dump_firm_type(type); +- printk("(%x), id %016llx.\n", type, (unsigned long long)*id); +- } +- return i; +-} +- +-static inline int do_tuner_callback(struct dvb_frontend *fe, int cmd, int arg) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- +- /* analog side (tuner-core) uses i2c_adap->algo_data. +- * digital side is not guaranteed to have algo_data defined. +- * +- * digital side will always have fe->dvb defined. +- * analog side (tuner-core) doesn't (yet) define fe->dvb. +- */ +- +- return (!fe->callback) ? -EINVAL : +- fe->callback(((fe->dvb) && (fe->dvb->priv)) ? +- fe->dvb->priv : priv->i2c_props.adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, cmd, arg); +-} +- +-static int load_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- int pos, rc; +- unsigned char *p, *endp, buf[priv->ctrl.max_len]; +- +- tuner_dbg("%s called\n", __func__); +- +- pos = seek_firmware(fe, type, id); +- if (pos < 0) +- return pos; +- +- tuner_info("Loading firmware for type="); +- dump_firm_type(priv->firm[pos].type); +- printk("(%x), id %016llx.\n", priv->firm[pos].type, +- (unsigned long long)*id); +- +- p = priv->firm[pos].ptr; +- endp = p + priv->firm[pos].size; +- +- while (p < endp) { +- __u16 size; +- +- /* Checks if there's enough bytes to read */ +- if (p + sizeof(size) > endp) { +- tuner_err("Firmware chunk size is wrong\n"); +- return -EINVAL; +- } +- +- size = le16_to_cpu(*(__u16 *) p); +- p += sizeof(size); +- +- if (size == 0xffff) +- return 0; +- +- if (!size) { +- /* Special callback command received */ +- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0); +- if (rc < 0) { +- tuner_err("Error at RESET code %d\n", +- (*p) & 0x7f); +- return -EINVAL; +- } +- continue; +- } +- if (size >= 0xff00) { +- switch (size) { +- case 0xff00: +- rc = do_tuner_callback(fe, XC2028_RESET_CLK, 0); +- if (rc < 0) { +- tuner_err("Error at RESET code %d\n", +- (*p) & 0x7f); +- return -EINVAL; +- } +- break; +- default: +- tuner_info("Invalid RESET code %d\n", +- size & 0x7f); +- return -EINVAL; +- +- } +- continue; +- } +- +- /* Checks for a sleep command */ +- if (size & 0x8000) { +- msleep(size & 0x7fff); +- continue; +- } +- +- if ((size + p > endp)) { +- tuner_err("missing bytes: need %d, have %d\n", +- size, (int)(endp - p)); +- return -EINVAL; +- } +- +- buf[0] = *p; +- p++; +- size--; +- +- /* Sends message chunks */ +- while (size > 0) { +- int len = (size < priv->ctrl.max_len - 1) ? +- size : priv->ctrl.max_len - 1; +- +- memcpy(buf + 1, p, len); +- +- rc = i2c_send(priv, buf, len + 1); +- if (rc < 0) { +- tuner_err("%d returned from send\n", rc); +- return -EINVAL; +- } +- +- p += len; +- size -= len; +- } +- +- /* silently fail if the frontend doesn't support I2C flush */ +- rc = do_tuner_callback(fe, XC2028_I2C_FLUSH, 0); +- if ((rc < 0) && (rc != -EINVAL)) { +- tuner_err("error executing flush: %d\n", rc); +- return rc; +- } +- } +- return 0; +-} +- +-static int load_scode(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id, __u16 int_freq, int scode) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- int pos, rc; +- unsigned char *p; +- +- tuner_dbg("%s called\n", __func__); +- +- if (!int_freq) { +- pos = seek_firmware(fe, type, id); +- if (pos < 0) +- return pos; +- } else { +- for (pos = 0; pos < priv->firm_size; pos++) { +- if ((priv->firm[pos].int_freq == int_freq) && +- (priv->firm[pos].type & HAS_IF)) +- break; +- } +- if (pos == priv->firm_size) +- return -ENOENT; +- } +- +- p = priv->firm[pos].ptr; +- +- if (priv->firm[pos].type & HAS_IF) { +- if (priv->firm[pos].size != 12 * 16 || scode >= 16) +- return -EINVAL; +- p += 12 * scode; +- } else { +- /* 16 SCODE entries per file; each SCODE entry is 12 bytes and +- * has a 2-byte size header in the firmware format. */ +- if (priv->firm[pos].size != 14 * 16 || scode >= 16 || +- le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12) +- return -EINVAL; +- p += 14 * scode + 2; +- } +- +- tuner_info("Loading SCODE for type="); +- dump_firm_type_and_int_freq(priv->firm[pos].type, +- priv->firm[pos].int_freq); +- printk("(%x), id %016llx.\n", priv->firm[pos].type, +- (unsigned long long)*id); +- +- if (priv->firm_version < 0x0202) +- rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00}); +- else +- rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00}); +- if (rc < 0) +- return -EIO; +- +- rc = i2c_send(priv, p, 12); +- if (rc < 0) +- return -EIO; +- +- rc = send_seq(priv, {0x00, 0x8c}); +- if (rc < 0) +- return -EIO; +- +- return 0; +-} +- +-static int check_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id std, __u16 int_freq) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- struct firmware_properties new_fw; +- int rc = 0, retry_count = 0; +- u16 version, hwmodel; +- v4l2_std_id std0; +- +- tuner_dbg("%s called\n", __func__); +- +- if (!priv->firm) { +- if (!priv->ctrl.fname) { +- tuner_info("xc2028/3028 firmware name not set!\n"); +- return -EINVAL; +- } +- +- rc = load_all_firmwares(fe); +- if (rc < 0) +- return rc; +- } +- +- if (priv->ctrl.mts && !(type & FM)) +- type |= MTS; +- +-retry: +- new_fw.type = type; +- new_fw.id = std; +- new_fw.std_req = std; +- new_fw.scode_table = SCODE | priv->ctrl.scode_table; +- new_fw.scode_nr = 0; +- new_fw.int_freq = int_freq; +- +- tuner_dbg("checking firmware, user requested type="); +- if (debug) { +- dump_firm_type(new_fw.type); +- printk("(%x), id %016llx, ", new_fw.type, +- (unsigned long long)new_fw.std_req); +- if (!int_freq) { +- printk("scode_tbl "); +- dump_firm_type(priv->ctrl.scode_table); +- printk("(%x), ", priv->ctrl.scode_table); +- } else +- printk("int_freq %d, ", new_fw.int_freq); +- printk("scode_nr %d\n", new_fw.scode_nr); +- } +- +- /* No need to reload base firmware if it matches */ +- if (((BASE | new_fw.type) & BASE_TYPES) == +- (priv->cur_fw.type & BASE_TYPES)) { +- tuner_dbg("BASE firmware not changed.\n"); +- goto skip_base; +- } +- +- /* Updating BASE - forget about all currently loaded firmware */ +- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +- +- /* Reset is needed before loading firmware */ +- rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0); +- if (rc < 0) +- goto fail; +- +- /* BASE firmwares are all std0 */ +- std0 = 0; +- rc = load_firmware(fe, BASE | new_fw.type, &std0); +- if (rc < 0) { +- tuner_err("Error %d while loading base firmware\n", +- rc); +- goto fail; +- } +- +- /* Load INIT1, if needed */ +- tuner_dbg("Load init1 firmware, if exists\n"); +- +- rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0); +- if (rc == -ENOENT) +- rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ, +- &std0); +- if (rc < 0 && rc != -ENOENT) { +- tuner_err("Error %d while loading init1 firmware\n", +- rc); +- goto fail; +- } +- +-skip_base: +- /* +- * No need to reload standard specific firmware if base firmware +- * was not reloaded and requested video standards have not changed. +- */ +- if (priv->cur_fw.type == (BASE | new_fw.type) && +- priv->cur_fw.std_req == std) { +- tuner_dbg("Std-specific firmware already loaded.\n"); +- goto skip_std_specific; +- } +- +- /* Reloading std-specific firmware forces a SCODE update */ +- priv->cur_fw.scode_table = 0; +- +- rc = load_firmware(fe, new_fw.type, &new_fw.id); +- if (rc == -ENOENT) +- rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id); +- +- if (rc < 0) +- goto fail; +- +-skip_std_specific: +- if (priv->cur_fw.scode_table == new_fw.scode_table && +- priv->cur_fw.scode_nr == new_fw.scode_nr) { +- tuner_dbg("SCODE firmware already loaded.\n"); +- goto check_device; +- } +- +- if (new_fw.type & FM) +- goto check_device; +- +- /* Load SCODE firmware, if exists */ +- tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr); +- +- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, +- new_fw.int_freq, new_fw.scode_nr); +- +-check_device: +- if (xc2028_get_reg(priv, 0x0004, &version) < 0 || +- xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) { +- tuner_err("Unable to read tuner registers.\n"); +- goto fail; +- } +- +- tuner_dbg("Device is Xceive %d version %d.%d, " +- "firmware version %d.%d\n", +- hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, +- (version & 0xf0) >> 4, version & 0xf); +- +- +- if (priv->ctrl.read_not_reliable) +- goto read_not_reliable; +- +- /* Check firmware version against what we downloaded. */ +- if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { +- if (!priv->ctrl.read_not_reliable) { +- tuner_err("Incorrect readback of firmware version.\n"); +- goto fail; +- } else { +- tuner_err("Returned an incorrect version. However, " +- "read is not reliable enough. Ignoring it.\n"); +- hwmodel = 3028; +- } +- } +- +- /* Check that the tuner hardware model remains consistent over time. */ +- if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) { +- priv->hwmodel = hwmodel; +- priv->hwvers = version & 0xff00; +- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || +- priv->hwvers != (version & 0xff00)) { +- tuner_err("Read invalid device hardware information - tuner " +- "hung?\n"); +- goto fail; +- } +- +-read_not_reliable: +- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); +- +- /* +- * By setting BASE in cur_fw.type only after successfully loading all +- * firmwares, we can: +- * 1. Identify that BASE firmware with type=0 has been loaded; +- * 2. Tell whether BASE firmware was just changed the next time through. +- */ +- priv->cur_fw.type |= BASE; +- +- return 0; +- +-fail: +- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +- if (retry_count < 8) { +- msleep(50); +- retry_count++; +- tuner_dbg("Retrying firmware load\n"); +- goto retry; +- } +- +- if (rc == -ENOENT) +- rc = -EINVAL; +- return rc; +-} +- +-static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- u16 frq_lock, signal = 0; +- int rc; +- +- tuner_dbg("%s called\n", __func__); +- +- mutex_lock(&priv->lock); +- +- /* Sync Lock Indicator */ +- rc = xc2028_get_reg(priv, XREG_LOCK, &frq_lock); +- if (rc < 0) +- goto ret; +- +- /* Frequency is locked */ +- if (frq_lock == 1) +- signal = 1 << 11; +- +- /* Get SNR of the video signal */ +- rc = xc2028_get_reg(priv, XREG_SNR, &signal); +- if (rc < 0) +- goto ret; +- +- /* Use both frq_lock and signal to generate the result */ +- signal = signal || ((signal & 0x07) << 12); +- +-ret: +- mutex_unlock(&priv->lock); +- +- *strength = signal; +- +- tuner_dbg("signal strength is %d\n", signal); +- +- return rc; +-} +- +-#define DIV 15625 +- +-static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, +- enum v4l2_tuner_type new_type, +- unsigned int type, +- v4l2_std_id std, +- u16 int_freq) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- int rc = -EINVAL; +- unsigned char buf[4]; +- u32 div, offset = 0; +- +- tuner_dbg("%s called\n", __func__); +- +- mutex_lock(&priv->lock); +- +- tuner_dbg("should set frequency %d kHz\n", freq / 1000); +- +- if (check_firmware(fe, type, std, int_freq) < 0) +- goto ret; +- +- /* On some cases xc2028 can disable video output, if +- * very weak signals are received. By sending a soft +- * reset, this is re-enabled. So, it is better to always +- * send a soft reset before changing channels, to be sure +- * that xc2028 will be in a safe state. +- * Maybe this might also be needed for DTV. +- */ +- switch (new_type) { +- case V4L2_TUNER_ANALOG_TV: +- rc = send_seq(priv, {0x00, 0x00}); +- +- /* Analog mode requires offset = 0 */ +- break; +- case V4L2_TUNER_RADIO: +- /* Radio mode requires offset = 0 */ +- break; +- case V4L2_TUNER_DIGITAL_TV: +- /* +- * Digital modes require an offset to adjust to the +- * proper frequency. The offset depends on what +- * firmware version is used. +- */ +- +- /* +- * Adjust to the center frequency. This is calculated by the +- * formula: offset = 1.25MHz - BW/2 +- * For DTV 7/8, the firmware uses BW = 8000, so it needs a +- * further adjustment to get the frequency center on VHF +- */ +- +- /* +- * The firmware DTV78 used to work fine in UHF band (8 MHz +- * bandwidth) but not at all in VHF band (7 MHz bandwidth). +- * The real problem was connected to the formula used to +- * calculate the center frequency offset in VHF band. +- * In fact, removing the 500KHz adjustment fixed the problem. +- * This is coherent to what was implemented for the DTV7 +- * firmware. +- * In the end, now the center frequency is the same for all 3 +- * firmwares (DTV7, DTV8, DTV78) and doesn't depend on channel +- * bandwidth. +- */ +- +- if (priv->cur_fw.type & DTV6) +- offset = 1750000; +- else /* DTV7 or DTV8 or DTV78 */ +- offset = 2750000; +- +- /* +- * xc3028 additional "magic" +- * Depending on the firmware version, it needs some adjustments +- * to properly centralize the frequency. This seems to be +- * needed to compensate the SCODE table adjustments made by +- * newer firmwares +- */ +- +- /* +- * The proper adjustment would be to do it at s-code table. +- * However, this didn't work, as reported by +- * Robert Lowery +- */ +- +-#if 0 +- /* +- * Still need tests for XC3028L (firmware 3.2 or upper) +- * So, for now, let's just comment the per-firmware +- * version of this change. Reports with xc3028l working +- * with and without the lines bellow are welcome +- */ +- +- if (priv->firm_version < 0x0302) { +- if (priv->cur_fw.type & DTV7) +- offset += 500000; +- } else { +- if (priv->cur_fw.type & DTV7) +- offset -= 300000; +- else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */ +- offset += 200000; +- } +-#endif +- } +- +- div = (freq - offset + DIV / 2) / DIV; +- +- /* CMD= Set frequency */ +- if (priv->firm_version < 0x0202) +- rc = send_seq(priv, {0x00, XREG_RF_FREQ, 0x00, 0x00}); +- else +- rc = send_seq(priv, {0x80, XREG_RF_FREQ, 0x00, 0x00}); +- if (rc < 0) +- goto ret; +- +- /* Return code shouldn't be checked. +- The reset CLK is needed only with tm6000. +- Driver should work fine even if this fails. +- */ +- if (priv->ctrl.msleep) +- msleep(priv->ctrl.msleep); +- do_tuner_callback(fe, XC2028_RESET_CLK, 1); +- +- msleep(10); +- +- buf[0] = 0xff & (div >> 24); +- buf[1] = 0xff & (div >> 16); +- buf[2] = 0xff & (div >> 8); +- buf[3] = 0xff & (div); +- +- rc = i2c_send(priv, buf, sizeof(buf)); +- if (rc < 0) +- goto ret; +- msleep(100); +- +- priv->frequency = freq; +- +- tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n", +- buf[0], buf[1], buf[2], buf[3], +- freq / 1000000, (freq % 1000000) / 1000); +- +- rc = 0; +- +-ret: +- mutex_unlock(&priv->lock); +- +- return rc; +-} +- +-static int xc2028_set_analog_freq(struct dvb_frontend *fe, +- struct analog_parameters *p) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- unsigned int type=0; +- +- tuner_dbg("%s called\n", __func__); +- +- if (p->mode == V4L2_TUNER_RADIO) { +- type |= FM; +- if (priv->ctrl.input1) +- type |= INPUT1; +- return generic_set_freq(fe, (625l * p->frequency) / 10, +- V4L2_TUNER_RADIO, type, 0, 0); +- } +- +- /* if std is not defined, choose one */ +- if (!p->std) +- p->std = V4L2_STD_MN; +- +- /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */ +- if (!(p->std & V4L2_STD_MN)) +- type |= F8MHZ; +- +- /* Add audio hack to std mask */ +- p->std |= parse_audio_std_option(); +- +- return generic_set_freq(fe, 62500l * p->frequency, +- V4L2_TUNER_ANALOG_TV, type, p->std, 0); +-} +- +-static int xc2028_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- struct xc2028_data *priv = fe->tuner_priv; +- unsigned int type=0; +- u16 demod = 0; +- +- tuner_dbg("%s called\n", __func__); +- +- switch (delsys) { +- case SYS_DVBT: +- case SYS_DVBT2: +- /* +- * The only countries with 6MHz seem to be Taiwan/Uruguay. +- * Both seem to require QAM firmware for OFDM decoding +- * Tested in Taiwan by Terry Wu +- */ +- if (bw <= 6000000) +- type |= QAM; +- +- switch (priv->ctrl.type) { +- case XC2028_D2633: +- type |= D2633; +- break; +- case XC2028_D2620: +- type |= D2620; +- break; +- case XC2028_AUTO: +- default: +- /* Zarlink seems to need D2633 */ +- if (priv->ctrl.demod == XC3028_FE_ZARLINK456) +- type |= D2633; +- else +- type |= D2620; +- } +- break; +- case SYS_ATSC: +- /* The only ATSC firmware (at least on v2.7) is D2633 */ +- type |= ATSC | D2633; +- break; +- /* DVB-S and pure QAM (FE_QAM) are not supported */ +- default: +- return -EINVAL; +- } +- +- if (bw <= 6000000) { +- type |= DTV6; +- priv->ctrl.vhfbw7 = 0; +- priv->ctrl.uhfbw8 = 0; +- } else if (bw <= 7000000) { +- if (c->frequency < 470000000) +- priv->ctrl.vhfbw7 = 1; +- else +- priv->ctrl.uhfbw8 = 0; +- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7; +- type |= F8MHZ; +- } else { +- if (c->frequency < 470000000) +- priv->ctrl.vhfbw7 = 0; +- else +- priv->ctrl.uhfbw8 = 1; +- type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8; +- type |= F8MHZ; +- } +- +- /* All S-code tables need a 200kHz shift */ +- if (priv->ctrl.demod) { +- demod = priv->ctrl.demod; +- +- /* +- * Newer firmwares require a 200 kHz offset only for ATSC +- */ +- if (type == ATSC || priv->firm_version < 0x0302) +- demod += 200; +- /* +- * The DTV7 S-code table needs a 700 kHz shift. +- * +- * DTV7 is only used in Australia. Germany or Italy may also +- * use this firmware after initialization, but a tune to a UHF +- * channel should then cause DTV78 to be used. +- * +- * Unfortunately, on real-field tests, the s-code offset +- * didn't work as expected, as reported by +- * Robert Lowery +- */ +- } +- +- return generic_set_freq(fe, c->frequency, +- V4L2_TUNER_DIGITAL_TV, type, 0, demod); +-} +- +-static int xc2028_sleep(struct dvb_frontend *fe) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- int rc = 0; +- +- /* Avoid firmware reload on slow devices or if PM disabled */ +- if (no_poweroff || priv->ctrl.disable_power_mgmt) +- return 0; +- +- tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); +- if (debug > 1) { +- tuner_dbg("Printing sleep stack trace:\n"); +- dump_stack(); +- } +- +- mutex_lock(&priv->lock); +- +- if (priv->firm_version < 0x0202) +- rc = send_seq(priv, {0x00, XREG_POWER_DOWN, 0x00, 0x00}); +- else +- rc = send_seq(priv, {0x80, XREG_POWER_DOWN, 0x00, 0x00}); +- +- priv->cur_fw.type = 0; /* need firmware reload */ +- +- mutex_unlock(&priv->lock); +- +- return rc; +-} +- +-static int xc2028_dvb_release(struct dvb_frontend *fe) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- +- tuner_dbg("%s called\n", __func__); +- +- mutex_lock(&xc2028_list_mutex); +- +- /* only perform final cleanup if this is the last instance */ +- if (hybrid_tuner_report_instance_count(priv) == 1) { +- kfree(priv->ctrl.fname); +- free_firmware(priv); +- } +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&xc2028_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- +- tuner_dbg("%s called\n", __func__); +- +- *frequency = priv->frequency; +- +- return 0; +-} +- +-static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) +-{ +- struct xc2028_data *priv = fe->tuner_priv; +- struct xc2028_ctrl *p = priv_cfg; +- int rc = 0; +- +- tuner_dbg("%s called\n", __func__); +- +- mutex_lock(&priv->lock); +- +- memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); +- if (priv->ctrl.max_len < 9) +- priv->ctrl.max_len = 13; +- +- if (p->fname) { +- if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { +- kfree(priv->ctrl.fname); +- free_firmware(priv); +- } +- +- priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); +- if (priv->ctrl.fname == NULL) +- rc = -ENOMEM; +- } +- +- mutex_unlock(&priv->lock); +- +- return rc; +-} +- +-static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { +- .info = { +- .name = "Xceive XC3028", +- .frequency_min = 42000000, +- .frequency_max = 864000000, +- .frequency_step = 50000, +- }, +- +- .set_config = xc2028_set_config, +- .set_analog_params = xc2028_set_analog_freq, +- .release = xc2028_dvb_release, +- .get_frequency = xc2028_get_frequency, +- .get_rf_strength = xc2028_signal, +- .set_params = xc2028_set_params, +- .sleep = xc2028_sleep, +-}; +- +-struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, +- struct xc2028_config *cfg) +-{ +- struct xc2028_data *priv; +- int instance; +- +- if (debug) +- printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); +- +- if (NULL == cfg) +- return NULL; +- +- if (!fe) { +- printk(KERN_ERR "xc2028: No frontend!\n"); +- return NULL; +- } +- +- mutex_lock(&xc2028_list_mutex); +- +- instance = hybrid_tuner_request_state(struct xc2028_data, priv, +- hybrid_tuner_instance_list, +- cfg->i2c_adap, cfg->i2c_addr, +- "xc2028"); +- switch (instance) { +- case 0: +- /* memory allocation failure */ +- goto fail; +- break; +- case 1: +- /* new tuner instance */ +- priv->ctrl.max_len = 13; +- +- mutex_init(&priv->lock); +- +- fe->tuner_priv = priv; +- break; +- case 2: +- /* existing tuner instance */ +- fe->tuner_priv = priv; +- break; +- } +- +- memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, +- sizeof(xc2028_dvb_tuner_ops)); +- +- tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner"); +- +- if (cfg->ctrl) +- xc2028_set_config(fe, cfg->ctrl); +- +- mutex_unlock(&xc2028_list_mutex); +- +- return fe; +-fail: +- mutex_unlock(&xc2028_list_mutex); +- +- xc2028_dvb_release(fe); +- return NULL; +-} +- +-EXPORT_SYMBOL(xc2028_attach); +- +-MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver"); +-MODULE_AUTHOR("Michel Ludwig "); +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h +deleted file mode 100644 +index 9ebfb2d..0000000 +--- a/drivers/media/common/tuners/tuner-xc2028.h ++++ /dev/null +@@ -1,72 +0,0 @@ +-/* tuner-xc2028 +- * +- * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) +- * This code is placed under the terms of the GNU General Public License v2 +- */ +- +-#ifndef __TUNER_XC2028_H__ +-#define __TUNER_XC2028_H__ +- +-#include "dvb_frontend.h" +- +-#define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw" +-#define XC3028L_DEFAULT_FIRMWARE "xc3028L-v36.fw" +- +-/* Dmoduler IF (kHz) */ +-#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */ +-#define XC3028_FE_LG60 6000 +-#define XC3028_FE_ATI638 6380 +-#define XC3028_FE_OREN538 5380 +-#define XC3028_FE_OREN36 3600 +-#define XC3028_FE_TOYOTA388 3880 +-#define XC3028_FE_TOYOTA794 7940 +-#define XC3028_FE_DIBCOM52 5200 +-#define XC3028_FE_ZARLINK456 4560 +-#define XC3028_FE_CHINA 5200 +- +-enum firmware_type { +- XC2028_AUTO = 0, /* By default, auto-detects */ +- XC2028_D2633, +- XC2028_D2620, +-}; +- +-struct xc2028_ctrl { +- char *fname; +- int max_len; +- int msleep; +- unsigned int scode_table; +- unsigned int mts :1; +- unsigned int input1:1; +- unsigned int vhfbw7:1; +- unsigned int uhfbw8:1; +- unsigned int disable_power_mgmt:1; +- unsigned int read_not_reliable:1; +- unsigned int demod; +- enum firmware_type type:2; +-}; +- +-struct xc2028_config { +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +- struct xc2028_ctrl *ctrl; +-}; +- +-/* xc2028 commands for callback */ +-#define XC2028_TUNER_RESET 0 +-#define XC2028_RESET_CLK 1 +-#define XC2028_I2C_FLUSH 2 +- +-#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, +- struct xc2028_config *cfg); +-#else +-static inline struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, +- struct xc2028_config *cfg) +-{ +- printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", +- __func__); +- return NULL; +-} +-#endif +- +-#endif /* __TUNER_XC2028_H__ */ +diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c +deleted file mode 100644 +index 6839711..0000000 +--- a/drivers/media/common/tuners/xc4000.c ++++ /dev/null +@@ -1,1758 +0,0 @@ +-/* +- * Driver for Xceive XC4000 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2007 Xceive Corporation +- * Copyright (c) 2007 Steven Toth +- * Copyright (c) 2009 Devin Heitmueller +- * Copyright (c) 2009 Davide Ferri +- * Copyright (c) 2010 Istvan Varga +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "xc4000.h" +-#include "tuner-i2c.h" +-#include "tuner-xc2028-types.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Debugging level (0 to 2, default: 0 (off))."); +- +-static int no_poweroff; +-module_param(no_poweroff, int, 0644); +-MODULE_PARM_DESC(no_poweroff, "Power management (1: disabled, 2: enabled, " +- "0 (default): use device-specific default mode)."); +- +-static int audio_std; +-module_param(audio_std, int, 0644); +-MODULE_PARM_DESC(audio_std, "Audio standard. XC4000 audio decoder explicitly " +- "needs to know what audio standard is needed for some video standards " +- "with audio A2 or NICAM. The valid settings are a sum of:\n" +- " 1: use NICAM/B or A2/B instead of NICAM/A or A2/A\n" +- " 2: use A2 instead of NICAM or BTSC\n" +- " 4: use SECAM/K3 instead of K1\n" +- " 8: use PAL-D/K audio for SECAM-D/K\n" +- "16: use FM radio input 1 instead of input 2\n" +- "32: use mono audio (the lower three bits are ignored)"); +- +-static char firmware_name[30]; +-module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); +-MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " +- "default firmware name."); +- +-static DEFINE_MUTEX(xc4000_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-#define dprintk(level, fmt, arg...) if (debug >= level) \ +- printk(KERN_INFO "%s: " fmt, "xc4000", ## arg) +- +-/* struct for storing firmware table */ +-struct firmware_description { +- unsigned int type; +- v4l2_std_id id; +- __u16 int_freq; +- unsigned char *ptr; +- unsigned int size; +-}; +- +-struct firmware_properties { +- unsigned int type; +- v4l2_std_id id; +- v4l2_std_id std_req; +- __u16 int_freq; +- unsigned int scode_table; +- int scode_nr; +-}; +- +-struct xc4000_priv { +- struct tuner_i2c_props i2c_props; +- struct list_head hybrid_tuner_instance_list; +- struct firmware_description *firm; +- int firm_size; +- u32 if_khz; +- u32 freq_hz; +- u32 bandwidth; +- u8 video_standard; +- u8 rf_mode; +- u8 default_pm; +- u8 dvb_amplitude; +- u8 set_smoothedcvbs; +- u8 ignore_i2c_write_errors; +- __u16 firm_version; +- struct firmware_properties cur_fw; +- __u16 hwmodel; +- __u16 hwvers; +- struct mutex lock; +-}; +- +-#define XC4000_AUDIO_STD_B 1 +-#define XC4000_AUDIO_STD_A2 2 +-#define XC4000_AUDIO_STD_K3 4 +-#define XC4000_AUDIO_STD_L 8 +-#define XC4000_AUDIO_STD_INPUT1 16 +-#define XC4000_AUDIO_STD_MONO 32 +- +-#define XC4000_DEFAULT_FIRMWARE "dvb-fe-xc4000-1.4.fw" +- +-/* Misc Defines */ +-#define MAX_TV_STANDARD 24 +-#define XC_MAX_I2C_WRITE_LENGTH 64 +-#define XC_POWERED_DOWN 0x80000000U +- +-/* Signal Types */ +-#define XC_RF_MODE_AIR 0 +-#define XC_RF_MODE_CABLE 1 +- +-/* Product id */ +-#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000 +-#define XC_PRODUCT_ID_XC4000 0x0FA0 +-#define XC_PRODUCT_ID_XC4100 0x1004 +- +-/* Registers (Write-only) */ +-#define XREG_INIT 0x00 +-#define XREG_VIDEO_MODE 0x01 +-#define XREG_AUDIO_MODE 0x02 +-#define XREG_RF_FREQ 0x03 +-#define XREG_D_CODE 0x04 +-#define XREG_DIRECTSITTING_MODE 0x05 +-#define XREG_SEEK_MODE 0x06 +-#define XREG_POWER_DOWN 0x08 +-#define XREG_SIGNALSOURCE 0x0A +-#define XREG_SMOOTHEDCVBS 0x0E +-#define XREG_AMPLITUDE 0x10 +- +-/* Registers (Read-only) */ +-#define XREG_ADC_ENV 0x00 +-#define XREG_QUALITY 0x01 +-#define XREG_FRAME_LINES 0x02 +-#define XREG_HSYNC_FREQ 0x03 +-#define XREG_LOCK 0x04 +-#define XREG_FREQ_ERROR 0x05 +-#define XREG_SNR 0x06 +-#define XREG_VERSION 0x07 +-#define XREG_PRODUCT_ID 0x08 +-#define XREG_SIGNAL_LEVEL 0x0A +-#define XREG_NOISE_LEVEL 0x0B +- +-/* +- Basic firmware description. This will remain with +- the driver for documentation purposes. +- +- This represents an I2C firmware file encoded as a +- string of unsigned char. Format is as follows: +- +- char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB +- char[1 ]=len0_LSB -> length of first write transaction +- char[2 ]=data0 -> first byte to be sent +- char[3 ]=data1 +- char[4 ]=data2 +- char[ ]=... +- char[M ]=dataN -> last byte to be sent +- char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB +- char[M+2]=len1_LSB -> length of second write transaction +- char[M+3]=data0 +- char[M+4]=data1 +- ... +- etc. +- +- The [len] value should be interpreted as follows: +- +- len= len_MSB _ len_LSB +- len=1111_1111_1111_1111 : End of I2C_SEQUENCE +- len=0000_0000_0000_0000 : Reset command: Do hardware reset +- len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767) +- len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms +- +- For the RESET and WAIT commands, the two following bytes will contain +- immediately the length of the following transaction. +-*/ +- +-struct XC_TV_STANDARD { +- const char *Name; +- u16 audio_mode; +- u16 video_mode; +- u16 int_freq; +-}; +- +-/* Tuner standards */ +-#define XC4000_MN_NTSC_PAL_BTSC 0 +-#define XC4000_MN_NTSC_PAL_A2 1 +-#define XC4000_MN_NTSC_PAL_EIAJ 2 +-#define XC4000_MN_NTSC_PAL_Mono 3 +-#define XC4000_BG_PAL_A2 4 +-#define XC4000_BG_PAL_NICAM 5 +-#define XC4000_BG_PAL_MONO 6 +-#define XC4000_I_PAL_NICAM 7 +-#define XC4000_I_PAL_NICAM_MONO 8 +-#define XC4000_DK_PAL_A2 9 +-#define XC4000_DK_PAL_NICAM 10 +-#define XC4000_DK_PAL_MONO 11 +-#define XC4000_DK_SECAM_A2DK1 12 +-#define XC4000_DK_SECAM_A2LDK3 13 +-#define XC4000_DK_SECAM_A2MONO 14 +-#define XC4000_DK_SECAM_NICAM 15 +-#define XC4000_L_SECAM_NICAM 16 +-#define XC4000_LC_SECAM_NICAM 17 +-#define XC4000_DTV6 18 +-#define XC4000_DTV8 19 +-#define XC4000_DTV7_8 20 +-#define XC4000_DTV7 21 +-#define XC4000_FM_Radio_INPUT2 22 +-#define XC4000_FM_Radio_INPUT1 23 +- +-static struct XC_TV_STANDARD xc4000_standard[MAX_TV_STANDARD] = { +- {"M/N-NTSC/PAL-BTSC", 0x0000, 0x80A0, 4500}, +- {"M/N-NTSC/PAL-A2", 0x0000, 0x80A0, 4600}, +- {"M/N-NTSC/PAL-EIAJ", 0x0040, 0x80A0, 4500}, +- {"M/N-NTSC/PAL-Mono", 0x0078, 0x80A0, 4500}, +- {"B/G-PAL-A2", 0x0000, 0x8159, 5640}, +- {"B/G-PAL-NICAM", 0x0004, 0x8159, 5740}, +- {"B/G-PAL-MONO", 0x0078, 0x8159, 5500}, +- {"I-PAL-NICAM", 0x0080, 0x8049, 6240}, +- {"I-PAL-NICAM-MONO", 0x0078, 0x8049, 6000}, +- {"D/K-PAL-A2", 0x0000, 0x8049, 6380}, +- {"D/K-PAL-NICAM", 0x0080, 0x8049, 6200}, +- {"D/K-PAL-MONO", 0x0078, 0x8049, 6500}, +- {"D/K-SECAM-A2 DK1", 0x0000, 0x8049, 6340}, +- {"D/K-SECAM-A2 L/DK3", 0x0000, 0x8049, 6000}, +- {"D/K-SECAM-A2 MONO", 0x0078, 0x8049, 6500}, +- {"D/K-SECAM-NICAM", 0x0080, 0x8049, 6200}, +- {"L-SECAM-NICAM", 0x8080, 0x0009, 6200}, +- {"L'-SECAM-NICAM", 0x8080, 0x4009, 6200}, +- {"DTV6", 0x00C0, 0x8002, 0}, +- {"DTV8", 0x00C0, 0x800B, 0}, +- {"DTV7/8", 0x00C0, 0x801B, 0}, +- {"DTV7", 0x00C0, 0x8007, 0}, +- {"FM Radio-INPUT2", 0x0008, 0x9800, 10700}, +- {"FM Radio-INPUT1", 0x0008, 0x9000, 10700} +-}; +- +-static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val); +-static int xc4000_tuner_reset(struct dvb_frontend *fe); +-static void xc_debug_dump(struct xc4000_priv *priv); +- +-static int xc_send_i2c_data(struct xc4000_priv *priv, u8 *buf, int len) +-{ +- struct i2c_msg msg = { .addr = priv->i2c_props.addr, +- .flags = 0, .buf = buf, .len = len }; +- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { +- if (priv->ignore_i2c_write_errors == 0) { +- printk(KERN_ERR "xc4000: I2C write failed (len=%i)\n", +- len); +- if (len == 4) { +- printk(KERN_ERR "bytes %02x %02x %02x %02x\n", buf[0], +- buf[1], buf[2], buf[3]); +- } +- return -EREMOTEIO; +- } +- } +- return 0; +-} +- +-static int xc4000_tuner_reset(struct dvb_frontend *fe) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- int ret; +- +- dprintk(1, "%s()\n", __func__); +- +- if (fe->callback) { +- ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ? +- fe->dvb->priv : +- priv->i2c_props.adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, +- XC4000_TUNER_RESET, 0); +- if (ret) { +- printk(KERN_ERR "xc4000: reset failed\n"); +- return -EREMOTEIO; +- } +- } else { +- printk(KERN_ERR "xc4000: no tuner reset callback function, " +- "fatal\n"); +- return -EINVAL; +- } +- return 0; +-} +- +-static int xc_write_reg(struct xc4000_priv *priv, u16 regAddr, u16 i2cData) +-{ +- u8 buf[4]; +- int result; +- +- buf[0] = (regAddr >> 8) & 0xFF; +- buf[1] = regAddr & 0xFF; +- buf[2] = (i2cData >> 8) & 0xFF; +- buf[3] = i2cData & 0xFF; +- result = xc_send_i2c_data(priv, buf, 4); +- +- return result; +-} +- +-static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- +- int i, nbytes_to_send, result; +- unsigned int len, pos, index; +- u8 buf[XC_MAX_I2C_WRITE_LENGTH]; +- +- index = 0; +- while ((i2c_sequence[index] != 0xFF) || +- (i2c_sequence[index + 1] != 0xFF)) { +- len = i2c_sequence[index] * 256 + i2c_sequence[index+1]; +- if (len == 0x0000) { +- /* RESET command */ +- /* NOTE: this is ignored, as the reset callback was */ +- /* already called by check_firmware() */ +- index += 2; +- } else if (len & 0x8000) { +- /* WAIT command */ +- msleep(len & 0x7FFF); +- index += 2; +- } else { +- /* Send i2c data whilst ensuring individual transactions +- * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes. +- */ +- index += 2; +- buf[0] = i2c_sequence[index]; +- buf[1] = i2c_sequence[index + 1]; +- pos = 2; +- while (pos < len) { +- if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2) +- nbytes_to_send = +- XC_MAX_I2C_WRITE_LENGTH; +- else +- nbytes_to_send = (len - pos + 2); +- for (i = 2; i < nbytes_to_send; i++) { +- buf[i] = i2c_sequence[index + pos + +- i - 2]; +- } +- result = xc_send_i2c_data(priv, buf, +- nbytes_to_send); +- +- if (result != 0) +- return result; +- +- pos += nbytes_to_send - 2; +- } +- index += len; +- } +- } +- return 0; +-} +- +-static int xc_set_tv_standard(struct xc4000_priv *priv, +- u16 video_mode, u16 audio_mode) +-{ +- int ret; +- dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, video_mode, audio_mode); +- dprintk(1, "%s() Standard = %s\n", +- __func__, +- xc4000_standard[priv->video_standard].Name); +- +- /* Don't complain when the request fails because of i2c stretching */ +- priv->ignore_i2c_write_errors = 1; +- +- ret = xc_write_reg(priv, XREG_VIDEO_MODE, video_mode); +- if (ret == 0) +- ret = xc_write_reg(priv, XREG_AUDIO_MODE, audio_mode); +- +- priv->ignore_i2c_write_errors = 0; +- +- return ret; +-} +- +-static int xc_set_signal_source(struct xc4000_priv *priv, u16 rf_mode) +-{ +- dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode, +- rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE"); +- +- if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) { +- rf_mode = XC_RF_MODE_CABLE; +- printk(KERN_ERR +- "%s(), Invalid mode, defaulting to CABLE", +- __func__); +- } +- return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode); +-} +- +-static const struct dvb_tuner_ops xc4000_tuner_ops; +- +-static int xc_set_rf_frequency(struct xc4000_priv *priv, u32 freq_hz) +-{ +- u16 freq_code; +- +- dprintk(1, "%s(%u)\n", __func__, freq_hz); +- +- if ((freq_hz > xc4000_tuner_ops.info.frequency_max) || +- (freq_hz < xc4000_tuner_ops.info.frequency_min)) +- return -EINVAL; +- +- freq_code = (u16)(freq_hz / 15625); +- +- /* WAS: Starting in firmware version 1.1.44, Xceive recommends using the +- FINERFREQ for all normal tuning (the doc indicates reg 0x03 should +- only be used for fast scanning for channel lock) */ +- /* WAS: XREG_FINERFREQ */ +- return xc_write_reg(priv, XREG_RF_FREQ, freq_code); +-} +- +-static int xc_get_adc_envelope(struct xc4000_priv *priv, u16 *adc_envelope) +-{ +- return xc4000_readreg(priv, XREG_ADC_ENV, adc_envelope); +-} +- +-static int xc_get_frequency_error(struct xc4000_priv *priv, u32 *freq_error_hz) +-{ +- int result; +- u16 regData; +- u32 tmp; +- +- result = xc4000_readreg(priv, XREG_FREQ_ERROR, ®Data); +- if (result != 0) +- return result; +- +- tmp = (u32)regData & 0xFFFFU; +- tmp = (tmp < 0x8000U ? tmp : 0x10000U - tmp); +- (*freq_error_hz) = tmp * 15625; +- return result; +-} +- +-static int xc_get_lock_status(struct xc4000_priv *priv, u16 *lock_status) +-{ +- return xc4000_readreg(priv, XREG_LOCK, lock_status); +-} +- +-static int xc_get_version(struct xc4000_priv *priv, +- u8 *hw_majorversion, u8 *hw_minorversion, +- u8 *fw_majorversion, u8 *fw_minorversion) +-{ +- u16 data; +- int result; +- +- result = xc4000_readreg(priv, XREG_VERSION, &data); +- if (result != 0) +- return result; +- +- (*hw_majorversion) = (data >> 12) & 0x0F; +- (*hw_minorversion) = (data >> 8) & 0x0F; +- (*fw_majorversion) = (data >> 4) & 0x0F; +- (*fw_minorversion) = data & 0x0F; +- +- return 0; +-} +- +-static int xc_get_hsync_freq(struct xc4000_priv *priv, u32 *hsync_freq_hz) +-{ +- u16 regData; +- int result; +- +- result = xc4000_readreg(priv, XREG_HSYNC_FREQ, ®Data); +- if (result != 0) +- return result; +- +- (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; +- return result; +-} +- +-static int xc_get_frame_lines(struct xc4000_priv *priv, u16 *frame_lines) +-{ +- return xc4000_readreg(priv, XREG_FRAME_LINES, frame_lines); +-} +- +-static int xc_get_quality(struct xc4000_priv *priv, u16 *quality) +-{ +- return xc4000_readreg(priv, XREG_QUALITY, quality); +-} +- +-static int xc_get_signal_level(struct xc4000_priv *priv, u16 *signal) +-{ +- return xc4000_readreg(priv, XREG_SIGNAL_LEVEL, signal); +-} +- +-static int xc_get_noise_level(struct xc4000_priv *priv, u16 *noise) +-{ +- return xc4000_readreg(priv, XREG_NOISE_LEVEL, noise); +-} +- +-static u16 xc_wait_for_lock(struct xc4000_priv *priv) +-{ +- u16 lock_state = 0; +- int watchdog_count = 40; +- +- while ((lock_state == 0) && (watchdog_count > 0)) { +- xc_get_lock_status(priv, &lock_state); +- if (lock_state != 1) { +- msleep(5); +- watchdog_count--; +- } +- } +- return lock_state; +-} +- +-static int xc_tune_channel(struct xc4000_priv *priv, u32 freq_hz) +-{ +- int found = 1; +- int result; +- +- dprintk(1, "%s(%u)\n", __func__, freq_hz); +- +- /* Don't complain when the request fails because of i2c stretching */ +- priv->ignore_i2c_write_errors = 1; +- result = xc_set_rf_frequency(priv, freq_hz); +- priv->ignore_i2c_write_errors = 0; +- +- if (result != 0) +- return 0; +- +- /* wait for lock only in analog TV mode */ +- if ((priv->cur_fw.type & (FM | DTV6 | DTV7 | DTV78 | DTV8)) == 0) { +- if (xc_wait_for_lock(priv) != 1) +- found = 0; +- } +- +- /* Wait for stats to stabilize. +- * Frame Lines needs two frame times after initial lock +- * before it is valid. +- */ +- msleep(debug ? 100 : 10); +- +- if (debug) +- xc_debug_dump(priv); +- +- return found; +-} +- +-static int xc4000_readreg(struct xc4000_priv *priv, u16 reg, u16 *val) +-{ +- u8 buf[2] = { reg >> 8, reg & 0xff }; +- u8 bval[2] = { 0, 0 }; +- struct i2c_msg msg[2] = { +- { .addr = priv->i2c_props.addr, +- .flags = 0, .buf = &buf[0], .len = 2 }, +- { .addr = priv->i2c_props.addr, +- .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, +- }; +- +- if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { +- printk(KERN_ERR "xc4000: I2C read failed\n"); +- return -EREMOTEIO; +- } +- +- *val = (bval[0] << 8) | bval[1]; +- return 0; +-} +- +-#define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) +-static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) +-{ +- if (type & BASE) +- printk(KERN_CONT "BASE "); +- if (type & INIT1) +- printk(KERN_CONT "INIT1 "); +- if (type & F8MHZ) +- printk(KERN_CONT "F8MHZ "); +- if (type & MTS) +- printk(KERN_CONT "MTS "); +- if (type & D2620) +- printk(KERN_CONT "D2620 "); +- if (type & D2633) +- printk(KERN_CONT "D2633 "); +- if (type & DTV6) +- printk(KERN_CONT "DTV6 "); +- if (type & QAM) +- printk(KERN_CONT "QAM "); +- if (type & DTV7) +- printk(KERN_CONT "DTV7 "); +- if (type & DTV78) +- printk(KERN_CONT "DTV78 "); +- if (type & DTV8) +- printk(KERN_CONT "DTV8 "); +- if (type & FM) +- printk(KERN_CONT "FM "); +- if (type & INPUT1) +- printk(KERN_CONT "INPUT1 "); +- if (type & LCD) +- printk(KERN_CONT "LCD "); +- if (type & NOGD) +- printk(KERN_CONT "NOGD "); +- if (type & MONO) +- printk(KERN_CONT "MONO "); +- if (type & ATSC) +- printk(KERN_CONT "ATSC "); +- if (type & IF) +- printk(KERN_CONT "IF "); +- if (type & LG60) +- printk(KERN_CONT "LG60 "); +- if (type & ATI638) +- printk(KERN_CONT "ATI638 "); +- if (type & OREN538) +- printk(KERN_CONT "OREN538 "); +- if (type & OREN36) +- printk(KERN_CONT "OREN36 "); +- if (type & TOYOTA388) +- printk(KERN_CONT "TOYOTA388 "); +- if (type & TOYOTA794) +- printk(KERN_CONT "TOYOTA794 "); +- if (type & DIBCOM52) +- printk(KERN_CONT "DIBCOM52 "); +- if (type & ZARLINK456) +- printk(KERN_CONT "ZARLINK456 "); +- if (type & CHINA) +- printk(KERN_CONT "CHINA "); +- if (type & F6MHZ) +- printk(KERN_CONT "F6MHZ "); +- if (type & INPUT2) +- printk(KERN_CONT "INPUT2 "); +- if (type & SCODE) +- printk(KERN_CONT "SCODE "); +- if (type & HAS_IF) +- printk(KERN_CONT "HAS_IF_%d ", int_freq); +-} +- +-static int seek_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- int i, best_i = -1; +- unsigned int best_nr_diffs = 255U; +- +- if (!priv->firm) { +- printk(KERN_ERR "Error! firmware not loaded\n"); +- return -EINVAL; +- } +- +- if (((type & ~SCODE) == 0) && (*id == 0)) +- *id = V4L2_STD_PAL; +- +- /* Seek for generic video standard match */ +- for (i = 0; i < priv->firm_size; i++) { +- v4l2_std_id id_diff_mask = +- (priv->firm[i].id ^ (*id)) & (*id); +- unsigned int type_diff_mask = +- (priv->firm[i].type ^ type) +- & (BASE_TYPES | DTV_TYPES | LCD | NOGD | MONO | SCODE); +- unsigned int nr_diffs; +- +- if (type_diff_mask +- & (BASE | INIT1 | FM | DTV6 | DTV7 | DTV78 | DTV8 | SCODE)) +- continue; +- +- nr_diffs = hweight64(id_diff_mask) + hweight32(type_diff_mask); +- if (!nr_diffs) /* Supports all the requested standards */ +- goto found; +- +- if (nr_diffs < best_nr_diffs) { +- best_nr_diffs = nr_diffs; +- best_i = i; +- } +- } +- +- /* FIXME: Would make sense to seek for type "hint" match ? */ +- if (best_i < 0) { +- i = -ENOENT; +- goto ret; +- } +- +- if (best_nr_diffs > 0U) { +- printk(KERN_WARNING +- "Selecting best matching firmware (%u bits differ) for " +- "type=(%x), id %016llx:\n", +- best_nr_diffs, type, (unsigned long long)*id); +- i = best_i; +- } +- +-found: +- *id = priv->firm[i].id; +- +-ret: +- if (debug) { +- printk(KERN_DEBUG "%s firmware for type=", +- (i < 0) ? "Can't find" : "Found"); +- dump_firm_type(type); +- printk(KERN_DEBUG "(%x), id %016llx.\n", type, (unsigned long long)*id); +- } +- return i; +-} +- +-static int load_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- int pos, rc; +- unsigned char *p; +- +- pos = seek_firmware(fe, type, id); +- if (pos < 0) +- return pos; +- +- p = priv->firm[pos].ptr; +- +- /* Don't complain when the request fails because of i2c stretching */ +- priv->ignore_i2c_write_errors = 1; +- +- rc = xc_load_i2c_sequence(fe, p); +- +- priv->ignore_i2c_write_errors = 0; +- +- return rc; +-} +- +-static int xc4000_fwupload(struct dvb_frontend *fe) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- const struct firmware *fw = NULL; +- const unsigned char *p, *endp; +- int rc = 0; +- int n, n_array; +- char name[33]; +- const char *fname; +- +- if (firmware_name[0] != '\0') +- fname = firmware_name; +- else +- fname = XC4000_DEFAULT_FIRMWARE; +- +- dprintk(1, "Reading firmware %s\n", fname); +- rc = request_firmware(&fw, fname, priv->i2c_props.adap->dev.parent); +- if (rc < 0) { +- if (rc == -ENOENT) +- printk(KERN_ERR "Error: firmware %s not found.\n", fname); +- else +- printk(KERN_ERR "Error %d while requesting firmware %s\n", +- rc, fname); +- +- return rc; +- } +- p = fw->data; +- endp = p + fw->size; +- +- if (fw->size < sizeof(name) - 1 + 2 + 2) { +- printk(KERN_ERR "Error: firmware file %s has invalid size!\n", +- fname); +- goto corrupt; +- } +- +- memcpy(name, p, sizeof(name) - 1); +- name[sizeof(name) - 1] = '\0'; +- p += sizeof(name) - 1; +- +- priv->firm_version = get_unaligned_le16(p); +- p += 2; +- +- n_array = get_unaligned_le16(p); +- p += 2; +- +- dprintk(1, "Loading %d firmware images from %s, type: %s, ver %d.%d\n", +- n_array, fname, name, +- priv->firm_version >> 8, priv->firm_version & 0xff); +- +- priv->firm = kcalloc(n_array, sizeof(*priv->firm), GFP_KERNEL); +- if (priv->firm == NULL) { +- printk(KERN_ERR "Not enough memory to load firmware file.\n"); +- rc = -ENOMEM; +- goto done; +- } +- priv->firm_size = n_array; +- +- n = -1; +- while (p < endp) { +- __u32 type, size; +- v4l2_std_id id; +- __u16 int_freq = 0; +- +- n++; +- if (n >= n_array) { +- printk(KERN_ERR "More firmware images in file than " +- "were expected!\n"); +- goto corrupt; +- } +- +- /* Checks if there's enough bytes to read */ +- if (endp - p < sizeof(type) + sizeof(id) + sizeof(size)) +- goto header; +- +- type = get_unaligned_le32(p); +- p += sizeof(type); +- +- id = get_unaligned_le64(p); +- p += sizeof(id); +- +- if (type & HAS_IF) { +- int_freq = get_unaligned_le16(p); +- p += sizeof(int_freq); +- if (endp - p < sizeof(size)) +- goto header; +- } +- +- size = get_unaligned_le32(p); +- p += sizeof(size); +- +- if (!size || size > endp - p) { +- printk(KERN_ERR "Firmware type (%x), id %llx is corrupted (size=%d, expected %d)\n", +- type, (unsigned long long)id, +- (unsigned)(endp - p), size); +- goto corrupt; +- } +- +- priv->firm[n].ptr = kzalloc(size, GFP_KERNEL); +- if (priv->firm[n].ptr == NULL) { +- printk(KERN_ERR "Not enough memory to load firmware file.\n"); +- rc = -ENOMEM; +- goto done; +- } +- +- if (debug) { +- printk(KERN_DEBUG "Reading firmware type "); +- dump_firm_type_and_int_freq(type, int_freq); +- printk(KERN_DEBUG "(%x), id %llx, size=%d.\n", +- type, (unsigned long long)id, size); +- } +- +- memcpy(priv->firm[n].ptr, p, size); +- priv->firm[n].type = type; +- priv->firm[n].id = id; +- priv->firm[n].size = size; +- priv->firm[n].int_freq = int_freq; +- +- p += size; +- } +- +- if (n + 1 != priv->firm_size) { +- printk(KERN_ERR "Firmware file is incomplete!\n"); +- goto corrupt; +- } +- +- goto done; +- +-header: +- printk(KERN_ERR "Firmware header is incomplete!\n"); +-corrupt: +- rc = -EINVAL; +- printk(KERN_ERR "Error: firmware file is corrupted!\n"); +- +-done: +- release_firmware(fw); +- if (rc == 0) +- dprintk(1, "Firmware files loaded.\n"); +- +- return rc; +-} +- +-static int load_scode(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id *id, __u16 int_freq, int scode) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- int pos, rc; +- unsigned char *p; +- u8 scode_buf[13]; +- u8 indirect_mode[5]; +- +- dprintk(1, "%s called int_freq=%d\n", __func__, int_freq); +- +- if (!int_freq) { +- pos = seek_firmware(fe, type, id); +- if (pos < 0) +- return pos; +- } else { +- for (pos = 0; pos < priv->firm_size; pos++) { +- if ((priv->firm[pos].int_freq == int_freq) && +- (priv->firm[pos].type & HAS_IF)) +- break; +- } +- if (pos == priv->firm_size) +- return -ENOENT; +- } +- +- p = priv->firm[pos].ptr; +- +- if (priv->firm[pos].size != 12 * 16 || scode >= 16) +- return -EINVAL; +- p += 12 * scode; +- +- if (debug) { +- tuner_info("Loading SCODE for type="); +- dump_firm_type_and_int_freq(priv->firm[pos].type, +- priv->firm[pos].int_freq); +- printk(KERN_CONT "(%x), id %016llx.\n", priv->firm[pos].type, +- (unsigned long long)*id); +- } +- +- scode_buf[0] = 0x00; +- memcpy(&scode_buf[1], p, 12); +- +- /* Enter direct-mode */ +- rc = xc_write_reg(priv, XREG_DIRECTSITTING_MODE, 0); +- if (rc < 0) { +- printk(KERN_ERR "failed to put device into direct mode!\n"); +- return -EIO; +- } +- +- rc = xc_send_i2c_data(priv, scode_buf, 13); +- if (rc != 0) { +- /* Even if the send failed, make sure we set back to indirect +- mode */ +- printk(KERN_ERR "Failed to set scode %d\n", rc); +- } +- +- /* Switch back to indirect-mode */ +- memset(indirect_mode, 0, sizeof(indirect_mode)); +- indirect_mode[4] = 0x88; +- xc_send_i2c_data(priv, indirect_mode, sizeof(indirect_mode)); +- msleep(10); +- +- return 0; +-} +- +-static int check_firmware(struct dvb_frontend *fe, unsigned int type, +- v4l2_std_id std, __u16 int_freq) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- struct firmware_properties new_fw; +- int rc = 0, is_retry = 0; +- u16 hwmodel; +- v4l2_std_id std0; +- u8 hw_major, hw_minor, fw_major, fw_minor; +- +- dprintk(1, "%s called\n", __func__); +- +- if (!priv->firm) { +- rc = xc4000_fwupload(fe); +- if (rc < 0) +- return rc; +- } +- +-retry: +- new_fw.type = type; +- new_fw.id = std; +- new_fw.std_req = std; +- new_fw.scode_table = SCODE; +- new_fw.scode_nr = 0; +- new_fw.int_freq = int_freq; +- +- dprintk(1, "checking firmware, user requested type="); +- if (debug) { +- dump_firm_type(new_fw.type); +- printk(KERN_CONT "(%x), id %016llx, ", new_fw.type, +- (unsigned long long)new_fw.std_req); +- if (!int_freq) +- printk(KERN_CONT "scode_tbl "); +- else +- printk(KERN_CONT "int_freq %d, ", new_fw.int_freq); +- printk(KERN_CONT "scode_nr %d\n", new_fw.scode_nr); +- } +- +- /* No need to reload base firmware if it matches */ +- if (priv->cur_fw.type & BASE) { +- dprintk(1, "BASE firmware not changed.\n"); +- goto skip_base; +- } +- +- /* Updating BASE - forget about all currently loaded firmware */ +- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +- +- /* Reset is needed before loading firmware */ +- rc = xc4000_tuner_reset(fe); +- if (rc < 0) +- goto fail; +- +- /* BASE firmwares are all std0 */ +- std0 = 0; +- rc = load_firmware(fe, BASE, &std0); +- if (rc < 0) { +- printk(KERN_ERR "Error %d while loading base firmware\n", rc); +- goto fail; +- } +- +- /* Load INIT1, if needed */ +- dprintk(1, "Load init1 firmware, if exists\n"); +- +- rc = load_firmware(fe, BASE | INIT1, &std0); +- if (rc == -ENOENT) +- rc = load_firmware(fe, BASE | INIT1, &std0); +- if (rc < 0 && rc != -ENOENT) { +- tuner_err("Error %d while loading init1 firmware\n", +- rc); +- goto fail; +- } +- +-skip_base: +- /* +- * No need to reload standard specific firmware if base firmware +- * was not reloaded and requested video standards have not changed. +- */ +- if (priv->cur_fw.type == (BASE | new_fw.type) && +- priv->cur_fw.std_req == std) { +- dprintk(1, "Std-specific firmware already loaded.\n"); +- goto skip_std_specific; +- } +- +- /* Reloading std-specific firmware forces a SCODE update */ +- priv->cur_fw.scode_table = 0; +- +- /* Load the standard firmware */ +- rc = load_firmware(fe, new_fw.type, &new_fw.id); +- +- if (rc < 0) +- goto fail; +- +-skip_std_specific: +- if (priv->cur_fw.scode_table == new_fw.scode_table && +- priv->cur_fw.scode_nr == new_fw.scode_nr) { +- dprintk(1, "SCODE firmware already loaded.\n"); +- goto check_device; +- } +- +- /* Load SCODE firmware, if exists */ +- rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, +- new_fw.int_freq, new_fw.scode_nr); +- if (rc != 0) +- dprintk(1, "load scode failed %d\n", rc); +- +-check_device: +- rc = xc4000_readreg(priv, XREG_PRODUCT_ID, &hwmodel); +- +- if (xc_get_version(priv, &hw_major, &hw_minor, &fw_major, +- &fw_minor) != 0) { +- printk(KERN_ERR "Unable to read tuner registers.\n"); +- goto fail; +- } +- +- dprintk(1, "Device is Xceive %d version %d.%d, " +- "firmware version %d.%d\n", +- hwmodel, hw_major, hw_minor, fw_major, fw_minor); +- +- /* Check firmware version against what we downloaded. */ +- if (priv->firm_version != ((fw_major << 8) | fw_minor)) { +- printk(KERN_WARNING +- "Incorrect readback of firmware version %d.%d.\n", +- fw_major, fw_minor); +- goto fail; +- } +- +- /* Check that the tuner hardware model remains consistent over time. */ +- if (priv->hwmodel == 0 && +- (hwmodel == XC_PRODUCT_ID_XC4000 || +- hwmodel == XC_PRODUCT_ID_XC4100)) { +- priv->hwmodel = hwmodel; +- priv->hwvers = (hw_major << 8) | hw_minor; +- } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || +- priv->hwvers != ((hw_major << 8) | hw_minor)) { +- printk(KERN_WARNING +- "Read invalid device hardware information - tuner " +- "hung?\n"); +- goto fail; +- } +- +- memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); +- +- /* +- * By setting BASE in cur_fw.type only after successfully loading all +- * firmwares, we can: +- * 1. Identify that BASE firmware with type=0 has been loaded; +- * 2. Tell whether BASE firmware was just changed the next time through. +- */ +- priv->cur_fw.type |= BASE; +- +- return 0; +- +-fail: +- memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); +- if (!is_retry) { +- msleep(50); +- is_retry = 1; +- dprintk(1, "Retrying firmware load\n"); +- goto retry; +- } +- +- if (rc == -ENOENT) +- rc = -EINVAL; +- return rc; +-} +- +-static void xc_debug_dump(struct xc4000_priv *priv) +-{ +- u16 adc_envelope; +- u32 freq_error_hz = 0; +- u16 lock_status; +- u32 hsync_freq_hz = 0; +- u16 frame_lines; +- u16 quality; +- u16 signal = 0; +- u16 noise = 0; +- u8 hw_majorversion = 0, hw_minorversion = 0; +- u8 fw_majorversion = 0, fw_minorversion = 0; +- +- xc_get_adc_envelope(priv, &adc_envelope); +- dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope); +- +- xc_get_frequency_error(priv, &freq_error_hz); +- dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz); +- +- xc_get_lock_status(priv, &lock_status); +- dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n", +- lock_status); +- +- xc_get_version(priv, &hw_majorversion, &hw_minorversion, +- &fw_majorversion, &fw_minorversion); +- dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x\n", +- hw_majorversion, hw_minorversion, +- fw_majorversion, fw_minorversion); +- +- if (priv->video_standard < XC4000_DTV6) { +- xc_get_hsync_freq(priv, &hsync_freq_hz); +- dprintk(1, "*** Horizontal sync frequency = %d Hz\n", +- hsync_freq_hz); +- +- xc_get_frame_lines(priv, &frame_lines); +- dprintk(1, "*** Frame lines = %d\n", frame_lines); +- } +- +- xc_get_quality(priv, &quality); +- dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality); +- +- xc_get_signal_level(priv, &signal); +- dprintk(1, "*** Signal level = -%ddB (%d)\n", signal >> 8, signal); +- +- xc_get_noise_level(priv, &noise); +- dprintk(1, "*** Noise level = %ddB (%d)\n", noise >> 8, noise); +-} +- +-static int xc4000_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- u32 bw = c->bandwidth_hz; +- struct xc4000_priv *priv = fe->tuner_priv; +- unsigned int type; +- int ret = -EREMOTEIO; +- +- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency); +- +- mutex_lock(&priv->lock); +- +- switch (delsys) { +- case SYS_ATSC: +- dprintk(1, "%s() VSB modulation\n", __func__); +- priv->rf_mode = XC_RF_MODE_AIR; +- priv->freq_hz = c->frequency - 1750000; +- priv->video_standard = XC4000_DTV6; +- type = DTV6; +- break; +- case SYS_DVBC_ANNEX_B: +- dprintk(1, "%s() QAM modulation\n", __func__); +- priv->rf_mode = XC_RF_MODE_CABLE; +- priv->freq_hz = c->frequency - 1750000; +- priv->video_standard = XC4000_DTV6; +- type = DTV6; +- break; +- case SYS_DVBT: +- case SYS_DVBT2: +- dprintk(1, "%s() OFDM\n", __func__); +- if (bw == 0) { +- if (c->frequency < 400000000) { +- priv->freq_hz = c->frequency - 2250000; +- } else { +- priv->freq_hz = c->frequency - 2750000; +- } +- priv->video_standard = XC4000_DTV7_8; +- type = DTV78; +- } else if (bw <= 6000000) { +- priv->video_standard = XC4000_DTV6; +- priv->freq_hz = c->frequency - 1750000; +- type = DTV6; +- } else if (bw <= 7000000) { +- priv->video_standard = XC4000_DTV7; +- priv->freq_hz = c->frequency - 2250000; +- type = DTV7; +- } else { +- priv->video_standard = XC4000_DTV8; +- priv->freq_hz = c->frequency - 2750000; +- type = DTV8; +- } +- priv->rf_mode = XC_RF_MODE_AIR; +- break; +- default: +- printk(KERN_ERR "xc4000 delivery system not supported!\n"); +- ret = -EINVAL; +- goto fail; +- } +- +- dprintk(1, "%s() frequency=%d (compensated)\n", +- __func__, priv->freq_hz); +- +- /* Make sure the correct firmware type is loaded */ +- if (check_firmware(fe, type, 0, priv->if_khz) != 0) +- goto fail; +- +- priv->bandwidth = c->bandwidth_hz; +- +- ret = xc_set_signal_source(priv, priv->rf_mode); +- if (ret != 0) { +- printk(KERN_ERR "xc4000: xc_set_signal_source(%d) failed\n", +- priv->rf_mode); +- goto fail; +- } else { +- u16 video_mode, audio_mode; +- video_mode = xc4000_standard[priv->video_standard].video_mode; +- audio_mode = xc4000_standard[priv->video_standard].audio_mode; +- if (type == DTV6 && priv->firm_version != 0x0102) +- video_mode |= 0x0001; +- ret = xc_set_tv_standard(priv, video_mode, audio_mode); +- if (ret != 0) { +- printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); +- /* DJH - do not return when it fails... */ +- /* goto fail; */ +- } +- } +- +- if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) +- ret = 0; +- if (priv->dvb_amplitude != 0) { +- if (xc_write_reg(priv, XREG_AMPLITUDE, +- (priv->firm_version != 0x0102 || +- priv->dvb_amplitude != 134 ? +- priv->dvb_amplitude : 132)) != 0) +- ret = -EREMOTEIO; +- } +- if (priv->set_smoothedcvbs != 0) { +- if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) +- ret = -EREMOTEIO; +- } +- if (ret != 0) { +- printk(KERN_ERR "xc4000: setting registers failed\n"); +- /* goto fail; */ +- } +- +- xc_tune_channel(priv, priv->freq_hz); +- +- ret = 0; +- +-fail: +- mutex_unlock(&priv->lock); +- +- return ret; +-} +- +-static int xc4000_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- unsigned int type = 0; +- int ret = -EREMOTEIO; +- +- if (params->mode == V4L2_TUNER_RADIO) { +- dprintk(1, "%s() frequency=%d (in units of 62.5Hz)\n", +- __func__, params->frequency); +- +- mutex_lock(&priv->lock); +- +- params->std = 0; +- priv->freq_hz = params->frequency * 125L / 2; +- +- if (audio_std & XC4000_AUDIO_STD_INPUT1) { +- priv->video_standard = XC4000_FM_Radio_INPUT1; +- type = FM | INPUT1; +- } else { +- priv->video_standard = XC4000_FM_Radio_INPUT2; +- type = FM | INPUT2; +- } +- +- goto tune_channel; +- } +- +- dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", +- __func__, params->frequency); +- +- mutex_lock(&priv->lock); +- +- /* params->frequency is in units of 62.5khz */ +- priv->freq_hz = params->frequency * 62500; +- +- params->std &= V4L2_STD_ALL; +- /* if std is not defined, choose one */ +- if (!params->std) +- params->std = V4L2_STD_PAL_BG; +- +- if (audio_std & XC4000_AUDIO_STD_MONO) +- type = MONO; +- +- if (params->std & V4L2_STD_MN) { +- params->std = V4L2_STD_MN; +- if (audio_std & XC4000_AUDIO_STD_MONO) { +- priv->video_standard = XC4000_MN_NTSC_PAL_Mono; +- } else if (audio_std & XC4000_AUDIO_STD_A2) { +- params->std |= V4L2_STD_A2; +- priv->video_standard = XC4000_MN_NTSC_PAL_A2; +- } else { +- params->std |= V4L2_STD_BTSC; +- priv->video_standard = XC4000_MN_NTSC_PAL_BTSC; +- } +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_BG) { +- params->std = V4L2_STD_PAL_BG; +- if (audio_std & XC4000_AUDIO_STD_MONO) { +- priv->video_standard = XC4000_BG_PAL_MONO; +- } else if (!(audio_std & XC4000_AUDIO_STD_A2)) { +- if (!(audio_std & XC4000_AUDIO_STD_B)) { +- params->std |= V4L2_STD_NICAM_A; +- priv->video_standard = XC4000_BG_PAL_NICAM; +- } else { +- params->std |= V4L2_STD_NICAM_B; +- priv->video_standard = XC4000_BG_PAL_NICAM; +- } +- } else { +- if (!(audio_std & XC4000_AUDIO_STD_B)) { +- params->std |= V4L2_STD_A2_A; +- priv->video_standard = XC4000_BG_PAL_A2; +- } else { +- params->std |= V4L2_STD_A2_B; +- priv->video_standard = XC4000_BG_PAL_A2; +- } +- } +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_I) { +- /* default to NICAM audio standard */ +- params->std = V4L2_STD_PAL_I | V4L2_STD_NICAM; +- if (audio_std & XC4000_AUDIO_STD_MONO) +- priv->video_standard = XC4000_I_PAL_NICAM_MONO; +- else +- priv->video_standard = XC4000_I_PAL_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_DK) { +- params->std = V4L2_STD_PAL_DK; +- if (audio_std & XC4000_AUDIO_STD_MONO) { +- priv->video_standard = XC4000_DK_PAL_MONO; +- } else if (audio_std & XC4000_AUDIO_STD_A2) { +- params->std |= V4L2_STD_A2; +- priv->video_standard = XC4000_DK_PAL_A2; +- } else { +- params->std |= V4L2_STD_NICAM; +- priv->video_standard = XC4000_DK_PAL_NICAM; +- } +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_DK) { +- /* default to A2 audio standard */ +- params->std = V4L2_STD_SECAM_DK | V4L2_STD_A2; +- if (audio_std & XC4000_AUDIO_STD_L) { +- type = 0; +- priv->video_standard = XC4000_DK_SECAM_NICAM; +- } else if (audio_std & XC4000_AUDIO_STD_MONO) { +- priv->video_standard = XC4000_DK_SECAM_A2MONO; +- } else if (audio_std & XC4000_AUDIO_STD_K3) { +- params->std |= V4L2_STD_SECAM_K3; +- priv->video_standard = XC4000_DK_SECAM_A2LDK3; +- } else { +- priv->video_standard = XC4000_DK_SECAM_A2DK1; +- } +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_L) { +- /* default to NICAM audio standard */ +- type = 0; +- params->std = V4L2_STD_SECAM_L | V4L2_STD_NICAM; +- priv->video_standard = XC4000_L_SECAM_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_LC) { +- /* default to NICAM audio standard */ +- type = 0; +- params->std = V4L2_STD_SECAM_LC | V4L2_STD_NICAM; +- priv->video_standard = XC4000_LC_SECAM_NICAM; +- goto tune_channel; +- } +- +-tune_channel: +- /* FIXME: it could be air. */ +- priv->rf_mode = XC_RF_MODE_CABLE; +- +- if (check_firmware(fe, type, params->std, +- xc4000_standard[priv->video_standard].int_freq) != 0) +- goto fail; +- +- ret = xc_set_signal_source(priv, priv->rf_mode); +- if (ret != 0) { +- printk(KERN_ERR +- "xc4000: xc_set_signal_source(%d) failed\n", +- priv->rf_mode); +- goto fail; +- } else { +- u16 video_mode, audio_mode; +- video_mode = xc4000_standard[priv->video_standard].video_mode; +- audio_mode = xc4000_standard[priv->video_standard].audio_mode; +- if (priv->video_standard < XC4000_BG_PAL_A2) { +- if (type & NOGD) +- video_mode &= 0xFF7F; +- } else if (priv->video_standard < XC4000_I_PAL_NICAM) { +- if (priv->firm_version == 0x0102) +- video_mode &= 0xFEFF; +- if (audio_std & XC4000_AUDIO_STD_B) +- video_mode |= 0x0080; +- } +- ret = xc_set_tv_standard(priv, video_mode, audio_mode); +- if (ret != 0) { +- printk(KERN_ERR "xc4000: xc_set_tv_standard failed\n"); +- goto fail; +- } +- } +- +- if (xc_write_reg(priv, XREG_D_CODE, 0) == 0) +- ret = 0; +- if (xc_write_reg(priv, XREG_AMPLITUDE, 1) != 0) +- ret = -EREMOTEIO; +- if (priv->set_smoothedcvbs != 0) { +- if (xc_write_reg(priv, XREG_SMOOTHEDCVBS, 1) != 0) +- ret = -EREMOTEIO; +- } +- if (ret != 0) { +- printk(KERN_ERR "xc4000: setting registers failed\n"); +- goto fail; +- } +- +- xc_tune_channel(priv, priv->freq_hz); +- +- ret = 0; +- +-fail: +- mutex_unlock(&priv->lock); +- +- return ret; +-} +- +-static int xc4000_get_signal(struct dvb_frontend *fe, u16 *strength) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- u16 value = 0; +- int rc; +- +- mutex_lock(&priv->lock); +- rc = xc4000_readreg(priv, XREG_SIGNAL_LEVEL, &value); +- mutex_unlock(&priv->lock); +- +- if (rc < 0) +- goto ret; +- +- /* Informations from real testing of DVB-T and radio part, +- coeficient for one dB is 0xff. +- */ +- tuner_dbg("Signal strength: -%ddB (%05d)\n", value >> 8, value); +- +- /* all known digital modes */ +- if ((priv->video_standard == XC4000_DTV6) || +- (priv->video_standard == XC4000_DTV7) || +- (priv->video_standard == XC4000_DTV7_8) || +- (priv->video_standard == XC4000_DTV8)) +- goto digital; +- +- /* Analog mode has NOISE LEVEL important, signal +- depends only on gain of antenna and amplifiers, +- but it doesn't tell anything about real quality +- of reception. +- */ +- mutex_lock(&priv->lock); +- rc = xc4000_readreg(priv, XREG_NOISE_LEVEL, &value); +- mutex_unlock(&priv->lock); +- +- tuner_dbg("Noise level: %ddB (%05d)\n", value >> 8, value); +- +- /* highest noise level: 32dB */ +- if (value >= 0x2000) { +- value = 0; +- } else { +- value = ~value << 3; +- } +- +- goto ret; +- +- /* Digital mode has SIGNAL LEVEL important and real +- noise level is stored in demodulator registers. +- */ +-digital: +- /* best signal: -50dB */ +- if (value <= 0x3200) { +- value = 0xffff; +- /* minimum: -114dB - should be 0x7200 but real zero is 0x713A */ +- } else if (value >= 0x713A) { +- value = 0; +- } else { +- value = ~(value - 0x3200) << 2; +- } +- +-ret: +- *strength = value; +- +- return rc; +-} +- +-static int xc4000_get_frequency(struct dvb_frontend *fe, u32 *freq) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- +- *freq = priv->freq_hz; +- +- if (debug) { +- mutex_lock(&priv->lock); +- if ((priv->cur_fw.type +- & (BASE | FM | DTV6 | DTV7 | DTV78 | DTV8)) == BASE) { +- u16 snr = 0; +- if (xc4000_readreg(priv, XREG_SNR, &snr) == 0) { +- mutex_unlock(&priv->lock); +- dprintk(1, "%s() freq = %u, SNR = %d\n", +- __func__, *freq, snr); +- return 0; +- } +- } +- mutex_unlock(&priv->lock); +- } +- +- dprintk(1, "%s()\n", __func__); +- +- return 0; +-} +- +-static int xc4000_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- +- *bw = priv->bandwidth; +- return 0; +-} +- +-static int xc4000_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- u16 lock_status = 0; +- +- mutex_lock(&priv->lock); +- +- if (priv->cur_fw.type & BASE) +- xc_get_lock_status(priv, &lock_status); +- +- *status = (lock_status == 1 ? +- TUNER_STATUS_LOCKED | TUNER_STATUS_STEREO : 0); +- if (priv->cur_fw.type & (DTV6 | DTV7 | DTV78 | DTV8)) +- *status &= (~TUNER_STATUS_STEREO); +- +- mutex_unlock(&priv->lock); +- +- dprintk(2, "%s() lock_status = %d\n", __func__, lock_status); +- +- return 0; +-} +- +-static int xc4000_sleep(struct dvb_frontend *fe) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- int ret = 0; +- +- dprintk(1, "%s()\n", __func__); +- +- mutex_lock(&priv->lock); +- +- /* Avoid firmware reload on slow devices */ +- if ((no_poweroff == 2 || +- (no_poweroff == 0 && priv->default_pm != 0)) && +- (priv->cur_fw.type & BASE) != 0) { +- /* force reset and firmware reload */ +- priv->cur_fw.type = XC_POWERED_DOWN; +- +- if (xc_write_reg(priv, XREG_POWER_DOWN, 0) != 0) { +- printk(KERN_ERR +- "xc4000: %s() unable to shutdown tuner\n", +- __func__); +- ret = -EREMOTEIO; +- } +- msleep(20); +- } +- +- mutex_unlock(&priv->lock); +- +- return ret; +-} +- +-static int xc4000_init(struct dvb_frontend *fe) +-{ +- dprintk(1, "%s()\n", __func__); +- +- return 0; +-} +- +-static int xc4000_release(struct dvb_frontend *fe) +-{ +- struct xc4000_priv *priv = fe->tuner_priv; +- +- dprintk(1, "%s()\n", __func__); +- +- mutex_lock(&xc4000_list_mutex); +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&xc4000_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static const struct dvb_tuner_ops xc4000_tuner_ops = { +- .info = { +- .name = "Xceive XC4000", +- .frequency_min = 1000000, +- .frequency_max = 1023000000, +- .frequency_step = 50000, +- }, +- +- .release = xc4000_release, +- .init = xc4000_init, +- .sleep = xc4000_sleep, +- +- .set_params = xc4000_set_params, +- .set_analog_params = xc4000_set_analog_params, +- .get_frequency = xc4000_get_frequency, +- .get_rf_strength = xc4000_get_signal, +- .get_bandwidth = xc4000_get_bandwidth, +- .get_status = xc4000_get_status +-}; +- +-struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct xc4000_config *cfg) +-{ +- struct xc4000_priv *priv = NULL; +- int instance; +- u16 id = 0; +- +- dprintk(1, "%s(%d-%04x)\n", __func__, +- i2c ? i2c_adapter_id(i2c) : -1, +- cfg ? cfg->i2c_address : -1); +- +- mutex_lock(&xc4000_list_mutex); +- +- instance = hybrid_tuner_request_state(struct xc4000_priv, priv, +- hybrid_tuner_instance_list, +- i2c, cfg->i2c_address, "xc4000"); +- switch (instance) { +- case 0: +- goto fail; +- break; +- case 1: +- /* new tuner instance */ +- priv->bandwidth = 6000000; +- /* set default configuration */ +- priv->if_khz = 4560; +- priv->default_pm = 0; +- priv->dvb_amplitude = 134; +- priv->set_smoothedcvbs = 1; +- mutex_init(&priv->lock); +- fe->tuner_priv = priv; +- break; +- default: +- /* existing tuner instance */ +- fe->tuner_priv = priv; +- break; +- } +- +- if (cfg->if_khz != 0) { +- /* copy configuration if provided by the caller */ +- priv->if_khz = cfg->if_khz; +- priv->default_pm = cfg->default_pm; +- priv->dvb_amplitude = cfg->dvb_amplitude; +- priv->set_smoothedcvbs = cfg->set_smoothedcvbs; +- } +- +- /* Check if firmware has been loaded. It is possible that another +- instance of the driver has loaded the firmware. +- */ +- +- if (instance == 1) { +- if (xc4000_readreg(priv, XREG_PRODUCT_ID, &id) != 0) +- goto fail; +- } else { +- id = ((priv->cur_fw.type & BASE) != 0 ? +- priv->hwmodel : XC_PRODUCT_ID_FW_NOT_LOADED); +- } +- +- switch (id) { +- case XC_PRODUCT_ID_XC4000: +- case XC_PRODUCT_ID_XC4100: +- printk(KERN_INFO +- "xc4000: Successfully identified at address 0x%02x\n", +- cfg->i2c_address); +- printk(KERN_INFO +- "xc4000: Firmware has been loaded previously\n"); +- break; +- case XC_PRODUCT_ID_FW_NOT_LOADED: +- printk(KERN_INFO +- "xc4000: Successfully identified at address 0x%02x\n", +- cfg->i2c_address); +- printk(KERN_INFO +- "xc4000: Firmware has not been loaded previously\n"); +- break; +- default: +- printk(KERN_ERR +- "xc4000: Device not found at addr 0x%02x (0x%x)\n", +- cfg->i2c_address, id); +- goto fail; +- } +- +- mutex_unlock(&xc4000_list_mutex); +- +- memcpy(&fe->ops.tuner_ops, &xc4000_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- if (instance == 1) { +- int ret; +- mutex_lock(&priv->lock); +- ret = xc4000_fwupload(fe); +- mutex_unlock(&priv->lock); +- if (ret != 0) +- goto fail2; +- } +- +- return fe; +-fail: +- mutex_unlock(&xc4000_list_mutex); +-fail2: +- xc4000_release(fe); +- return NULL; +-} +-EXPORT_SYMBOL(xc4000_attach); +- +-MODULE_AUTHOR("Steven Toth, Davide Ferri"); +-MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/xc4000.h b/drivers/media/common/tuners/xc4000.h +deleted file mode 100644 +index e6a44d1..0000000 +--- a/drivers/media/common/tuners/xc4000.h ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * Driver for Xceive XC4000 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2007 Steven Toth +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __XC4000_H__ +-#define __XC4000_H__ +- +-#include +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct xc4000_config { +- u8 i2c_address; +- /* if non-zero, power management is enabled by default */ +- u8 default_pm; +- /* value to be written to XREG_AMPLITUDE in DVB-T mode (0: no write) */ +- u8 dvb_amplitude; +- /* if non-zero, register 0x0E is set to filter analog TV video output */ +- u8 set_smoothedcvbs; +- /* IF for DVB-T */ +- u32 if_khz; +-}; +- +-/* xc4000 callback command */ +-#define XC4000_TUNER_RESET 0 +- +-/* For each bridge framework, when it attaches either analog or digital, +- * it has to store a reference back to its _core equivalent structure, +- * so that it can service the hardware by steering gpio's etc. +- * Each bridge implementation is different so cast devptr accordingly. +- * The xc4000 driver cares not for this value, other than ensuring +- * it's passed back to a bridge during tuner_callback(). +- */ +- +-#if defined(CONFIG_MEDIA_TUNER_XC4000) || (defined(CONFIG_MEDIA_TUNER_XC4000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct xc4000_config *cfg); +-#else +-static inline struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- struct xc4000_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c +deleted file mode 100644 +index 296df05..0000000 +--- a/drivers/media/common/tuners/xc5000.c ++++ /dev/null +@@ -1,1186 +0,0 @@ +-/* +- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2007 Xceive Corporation +- * Copyright (c) 2007 Steven Toth +- * Copyright (c) 2009 Devin Heitmueller +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "xc5000.h" +-#include "tuner-i2c.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-static int no_poweroff; +-module_param(no_poweroff, int, 0644); +-MODULE_PARM_DESC(no_poweroff, "0 (default) powers device off when not used.\n" +- "\t\t1 keep device energized and with tuner ready all the times.\n" +- "\t\tFaster, but consumes more power and keeps the device hotter"); +- +-static DEFINE_MUTEX(xc5000_list_mutex); +-static LIST_HEAD(hybrid_tuner_instance_list); +- +-#define dprintk(level, fmt, arg...) if (debug >= level) \ +- printk(KERN_INFO "%s: " fmt, "xc5000", ## arg) +- +-#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.6.114.fw" +-#define XC5000_DEFAULT_FIRMWARE_SIZE 12401 +- +-struct xc5000_priv { +- struct tuner_i2c_props i2c_props; +- struct list_head hybrid_tuner_instance_list; +- +- u32 if_khz; +- u32 freq_hz; +- u32 bandwidth; +- u8 video_standard; +- u8 rf_mode; +- u8 radio_input; +-}; +- +-/* Misc Defines */ +-#define MAX_TV_STANDARD 24 +-#define XC_MAX_I2C_WRITE_LENGTH 64 +- +-/* Signal Types */ +-#define XC_RF_MODE_AIR 0 +-#define XC_RF_MODE_CABLE 1 +- +-/* Result codes */ +-#define XC_RESULT_SUCCESS 0 +-#define XC_RESULT_RESET_FAILURE 1 +-#define XC_RESULT_I2C_WRITE_FAILURE 2 +-#define XC_RESULT_I2C_READ_FAILURE 3 +-#define XC_RESULT_OUT_OF_RANGE 5 +- +-/* Product id */ +-#define XC_PRODUCT_ID_FW_NOT_LOADED 0x2000 +-#define XC_PRODUCT_ID_FW_LOADED 0x1388 +- +-/* Registers */ +-#define XREG_INIT 0x00 +-#define XREG_VIDEO_MODE 0x01 +-#define XREG_AUDIO_MODE 0x02 +-#define XREG_RF_FREQ 0x03 +-#define XREG_D_CODE 0x04 +-#define XREG_IF_OUT 0x05 +-#define XREG_SEEK_MODE 0x07 +-#define XREG_POWER_DOWN 0x0A /* Obsolete */ +-/* Set the output amplitude - SIF for analog, DTVP/DTVN for digital */ +-#define XREG_OUTPUT_AMP 0x0B +-#define XREG_SIGNALSOURCE 0x0D /* 0=Air, 1=Cable */ +-#define XREG_SMOOTHEDCVBS 0x0E +-#define XREG_XTALFREQ 0x0F +-#define XREG_FINERFREQ 0x10 +-#define XREG_DDIMODE 0x11 +- +-#define XREG_ADC_ENV 0x00 +-#define XREG_QUALITY 0x01 +-#define XREG_FRAME_LINES 0x02 +-#define XREG_HSYNC_FREQ 0x03 +-#define XREG_LOCK 0x04 +-#define XREG_FREQ_ERROR 0x05 +-#define XREG_SNR 0x06 +-#define XREG_VERSION 0x07 +-#define XREG_PRODUCT_ID 0x08 +-#define XREG_BUSY 0x09 +-#define XREG_BUILD 0x0D +- +-/* +- Basic firmware description. This will remain with +- the driver for documentation purposes. +- +- This represents an I2C firmware file encoded as a +- string of unsigned char. Format is as follows: +- +- char[0 ]=len0_MSB -> len = len_MSB * 256 + len_LSB +- char[1 ]=len0_LSB -> length of first write transaction +- char[2 ]=data0 -> first byte to be sent +- char[3 ]=data1 +- char[4 ]=data2 +- char[ ]=... +- char[M ]=dataN -> last byte to be sent +- char[M+1]=len1_MSB -> len = len_MSB * 256 + len_LSB +- char[M+2]=len1_LSB -> length of second write transaction +- char[M+3]=data0 +- char[M+4]=data1 +- ... +- etc. +- +- The [len] value should be interpreted as follows: +- +- len= len_MSB _ len_LSB +- len=1111_1111_1111_1111 : End of I2C_SEQUENCE +- len=0000_0000_0000_0000 : Reset command: Do hardware reset +- len=0NNN_NNNN_NNNN_NNNN : Normal transaction: number of bytes = {1:32767) +- len=1WWW_WWWW_WWWW_WWWW : Wait command: wait for {1:32767} ms +- +- For the RESET and WAIT commands, the two following bytes will contain +- immediately the length of the following transaction. +- +-*/ +-struct XC_TV_STANDARD { +- char *Name; +- u16 AudioMode; +- u16 VideoMode; +-}; +- +-/* Tuner standards */ +-#define MN_NTSC_PAL_BTSC 0 +-#define MN_NTSC_PAL_A2 1 +-#define MN_NTSC_PAL_EIAJ 2 +-#define MN_NTSC_PAL_Mono 3 +-#define BG_PAL_A2 4 +-#define BG_PAL_NICAM 5 +-#define BG_PAL_MONO 6 +-#define I_PAL_NICAM 7 +-#define I_PAL_NICAM_MONO 8 +-#define DK_PAL_A2 9 +-#define DK_PAL_NICAM 10 +-#define DK_PAL_MONO 11 +-#define DK_SECAM_A2DK1 12 +-#define DK_SECAM_A2LDK3 13 +-#define DK_SECAM_A2MONO 14 +-#define L_SECAM_NICAM 15 +-#define LC_SECAM_NICAM 16 +-#define DTV6 17 +-#define DTV8 18 +-#define DTV7_8 19 +-#define DTV7 20 +-#define FM_Radio_INPUT2 21 +-#define FM_Radio_INPUT1 22 +-#define FM_Radio_INPUT1_MONO 23 +- +-static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = { +- {"M/N-NTSC/PAL-BTSC", 0x0400, 0x8020}, +- {"M/N-NTSC/PAL-A2", 0x0600, 0x8020}, +- {"M/N-NTSC/PAL-EIAJ", 0x0440, 0x8020}, +- {"M/N-NTSC/PAL-Mono", 0x0478, 0x8020}, +- {"B/G-PAL-A2", 0x0A00, 0x8049}, +- {"B/G-PAL-NICAM", 0x0C04, 0x8049}, +- {"B/G-PAL-MONO", 0x0878, 0x8059}, +- {"I-PAL-NICAM", 0x1080, 0x8009}, +- {"I-PAL-NICAM-MONO", 0x0E78, 0x8009}, +- {"D/K-PAL-A2", 0x1600, 0x8009}, +- {"D/K-PAL-NICAM", 0x0E80, 0x8009}, +- {"D/K-PAL-MONO", 0x1478, 0x8009}, +- {"D/K-SECAM-A2 DK1", 0x1200, 0x8009}, +- {"D/K-SECAM-A2 L/DK3", 0x0E00, 0x8009}, +- {"D/K-SECAM-A2 MONO", 0x1478, 0x8009}, +- {"L-SECAM-NICAM", 0x8E82, 0x0009}, +- {"L'-SECAM-NICAM", 0x8E82, 0x4009}, +- {"DTV6", 0x00C0, 0x8002}, +- {"DTV8", 0x00C0, 0x800B}, +- {"DTV7/8", 0x00C0, 0x801B}, +- {"DTV7", 0x00C0, 0x8007}, +- {"FM Radio-INPUT2", 0x9802, 0x9002}, +- {"FM Radio-INPUT1", 0x0208, 0x9002}, +- {"FM Radio-INPUT1_MONO", 0x0278, 0x9002} +-}; +- +-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe); +-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe); +-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val); +-static int xc5000_TunerReset(struct dvb_frontend *fe); +- +-static int xc_send_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) +-{ +- struct i2c_msg msg = { .addr = priv->i2c_props.addr, +- .flags = 0, .buf = buf, .len = len }; +- +- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { +- printk(KERN_ERR "xc5000: I2C write failed (len=%i)\n", len); +- return XC_RESULT_I2C_WRITE_FAILURE; +- } +- return XC_RESULT_SUCCESS; +-} +- +-#if 0 +-/* This routine is never used because the only time we read data from the +- i2c bus is when we read registers, and we want that to be an atomic i2c +- transaction in case we are on a multi-master bus */ +-static int xc_read_i2c_data(struct xc5000_priv *priv, u8 *buf, int len) +-{ +- struct i2c_msg msg = { .addr = priv->i2c_props.addr, +- .flags = I2C_M_RD, .buf = buf, .len = len }; +- +- if (i2c_transfer(priv->i2c_props.adap, &msg, 1) != 1) { +- printk(KERN_ERR "xc5000 I2C read failed (len=%i)\n", len); +- return -EREMOTEIO; +- } +- return 0; +-} +-#endif +- +-static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val) +-{ +- u8 buf[2] = { reg >> 8, reg & 0xff }; +- u8 bval[2] = { 0, 0 }; +- struct i2c_msg msg[2] = { +- { .addr = priv->i2c_props.addr, +- .flags = 0, .buf = &buf[0], .len = 2 }, +- { .addr = priv->i2c_props.addr, +- .flags = I2C_M_RD, .buf = &bval[0], .len = 2 }, +- }; +- +- if (i2c_transfer(priv->i2c_props.adap, msg, 2) != 2) { +- printk(KERN_WARNING "xc5000: I2C read failed\n"); +- return -EREMOTEIO; +- } +- +- *val = (bval[0] << 8) | bval[1]; +- return XC_RESULT_SUCCESS; +-} +- +-static void xc_wait(int wait_ms) +-{ +- msleep(wait_ms); +-} +- +-static int xc5000_TunerReset(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret; +- +- dprintk(1, "%s()\n", __func__); +- +- if (fe->callback) { +- ret = fe->callback(((fe->dvb) && (fe->dvb->priv)) ? +- fe->dvb->priv : +- priv->i2c_props.adap->algo_data, +- DVB_FRONTEND_COMPONENT_TUNER, +- XC5000_TUNER_RESET, 0); +- if (ret) { +- printk(KERN_ERR "xc5000: reset failed\n"); +- return XC_RESULT_RESET_FAILURE; +- } +- } else { +- printk(KERN_ERR "xc5000: no tuner reset callback function, fatal\n"); +- return XC_RESULT_RESET_FAILURE; +- } +- return XC_RESULT_SUCCESS; +-} +- +-static int xc_write_reg(struct xc5000_priv *priv, u16 regAddr, u16 i2cData) +-{ +- u8 buf[4]; +- int WatchDogTimer = 100; +- int result; +- +- buf[0] = (regAddr >> 8) & 0xFF; +- buf[1] = regAddr & 0xFF; +- buf[2] = (i2cData >> 8) & 0xFF; +- buf[3] = i2cData & 0xFF; +- result = xc_send_i2c_data(priv, buf, 4); +- if (result == XC_RESULT_SUCCESS) { +- /* wait for busy flag to clear */ +- while ((WatchDogTimer > 0) && (result == XC_RESULT_SUCCESS)) { +- result = xc5000_readreg(priv, XREG_BUSY, (u16 *)buf); +- if (result == XC_RESULT_SUCCESS) { +- if ((buf[0] == 0) && (buf[1] == 0)) { +- /* busy flag cleared */ +- break; +- } else { +- xc_wait(5); /* wait 5 ms */ +- WatchDogTimer--; +- } +- } +- } +- } +- if (WatchDogTimer < 0) +- result = XC_RESULT_I2C_WRITE_FAILURE; +- +- return result; +-} +- +-static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- +- int i, nbytes_to_send, result; +- unsigned int len, pos, index; +- u8 buf[XC_MAX_I2C_WRITE_LENGTH]; +- +- index = 0; +- while ((i2c_sequence[index] != 0xFF) || +- (i2c_sequence[index + 1] != 0xFF)) { +- len = i2c_sequence[index] * 256 + i2c_sequence[index+1]; +- if (len == 0x0000) { +- /* RESET command */ +- result = xc5000_TunerReset(fe); +- index += 2; +- if (result != XC_RESULT_SUCCESS) +- return result; +- } else if (len & 0x8000) { +- /* WAIT command */ +- xc_wait(len & 0x7FFF); +- index += 2; +- } else { +- /* Send i2c data whilst ensuring individual transactions +- * do not exceed XC_MAX_I2C_WRITE_LENGTH bytes. +- */ +- index += 2; +- buf[0] = i2c_sequence[index]; +- buf[1] = i2c_sequence[index + 1]; +- pos = 2; +- while (pos < len) { +- if ((len - pos) > XC_MAX_I2C_WRITE_LENGTH - 2) +- nbytes_to_send = +- XC_MAX_I2C_WRITE_LENGTH; +- else +- nbytes_to_send = (len - pos + 2); +- for (i = 2; i < nbytes_to_send; i++) { +- buf[i] = i2c_sequence[index + pos + +- i - 2]; +- } +- result = xc_send_i2c_data(priv, buf, +- nbytes_to_send); +- +- if (result != XC_RESULT_SUCCESS) +- return result; +- +- pos += nbytes_to_send - 2; +- } +- index += len; +- } +- } +- return XC_RESULT_SUCCESS; +-} +- +-static int xc_initialize(struct xc5000_priv *priv) +-{ +- dprintk(1, "%s()\n", __func__); +- return xc_write_reg(priv, XREG_INIT, 0); +-} +- +-static int xc_SetTVStandard(struct xc5000_priv *priv, +- u16 VideoMode, u16 AudioMode) +-{ +- int ret; +- dprintk(1, "%s(0x%04x,0x%04x)\n", __func__, VideoMode, AudioMode); +- dprintk(1, "%s() Standard = %s\n", +- __func__, +- XC5000_Standard[priv->video_standard].Name); +- +- ret = xc_write_reg(priv, XREG_VIDEO_MODE, VideoMode); +- if (ret == XC_RESULT_SUCCESS) +- ret = xc_write_reg(priv, XREG_AUDIO_MODE, AudioMode); +- +- return ret; +-} +- +-static int xc_SetSignalSource(struct xc5000_priv *priv, u16 rf_mode) +-{ +- dprintk(1, "%s(%d) Source = %s\n", __func__, rf_mode, +- rf_mode == XC_RF_MODE_AIR ? "ANTENNA" : "CABLE"); +- +- if ((rf_mode != XC_RF_MODE_AIR) && (rf_mode != XC_RF_MODE_CABLE)) { +- rf_mode = XC_RF_MODE_CABLE; +- printk(KERN_ERR +- "%s(), Invalid mode, defaulting to CABLE", +- __func__); +- } +- return xc_write_reg(priv, XREG_SIGNALSOURCE, rf_mode); +-} +- +-static const struct dvb_tuner_ops xc5000_tuner_ops; +- +-static int xc_set_RF_frequency(struct xc5000_priv *priv, u32 freq_hz) +-{ +- u16 freq_code; +- +- dprintk(1, "%s(%u)\n", __func__, freq_hz); +- +- if ((freq_hz > xc5000_tuner_ops.info.frequency_max) || +- (freq_hz < xc5000_tuner_ops.info.frequency_min)) +- return XC_RESULT_OUT_OF_RANGE; +- +- freq_code = (u16)(freq_hz / 15625); +- +- /* Starting in firmware version 1.1.44, Xceive recommends using the +- FINERFREQ for all normal tuning (the doc indicates reg 0x03 should +- only be used for fast scanning for channel lock) */ +- return xc_write_reg(priv, XREG_FINERFREQ, freq_code); +-} +- +- +-static int xc_set_IF_frequency(struct xc5000_priv *priv, u32 freq_khz) +-{ +- u32 freq_code = (freq_khz * 1024)/1000; +- dprintk(1, "%s(freq_khz = %d) freq_code = 0x%x\n", +- __func__, freq_khz, freq_code); +- +- return xc_write_reg(priv, XREG_IF_OUT, freq_code); +-} +- +- +-static int xc_get_ADC_Envelope(struct xc5000_priv *priv, u16 *adc_envelope) +-{ +- return xc5000_readreg(priv, XREG_ADC_ENV, adc_envelope); +-} +- +-static int xc_get_frequency_error(struct xc5000_priv *priv, u32 *freq_error_hz) +-{ +- int result; +- u16 regData; +- u32 tmp; +- +- result = xc5000_readreg(priv, XREG_FREQ_ERROR, ®Data); +- if (result != XC_RESULT_SUCCESS) +- return result; +- +- tmp = (u32)regData; +- (*freq_error_hz) = (tmp * 15625) / 1000; +- return result; +-} +- +-static int xc_get_lock_status(struct xc5000_priv *priv, u16 *lock_status) +-{ +- return xc5000_readreg(priv, XREG_LOCK, lock_status); +-} +- +-static int xc_get_version(struct xc5000_priv *priv, +- u8 *hw_majorversion, u8 *hw_minorversion, +- u8 *fw_majorversion, u8 *fw_minorversion) +-{ +- u16 data; +- int result; +- +- result = xc5000_readreg(priv, XREG_VERSION, &data); +- if (result != XC_RESULT_SUCCESS) +- return result; +- +- (*hw_majorversion) = (data >> 12) & 0x0F; +- (*hw_minorversion) = (data >> 8) & 0x0F; +- (*fw_majorversion) = (data >> 4) & 0x0F; +- (*fw_minorversion) = data & 0x0F; +- +- return 0; +-} +- +-static int xc_get_buildversion(struct xc5000_priv *priv, u16 *buildrev) +-{ +- return xc5000_readreg(priv, XREG_BUILD, buildrev); +-} +- +-static int xc_get_hsync_freq(struct xc5000_priv *priv, u32 *hsync_freq_hz) +-{ +- u16 regData; +- int result; +- +- result = xc5000_readreg(priv, XREG_HSYNC_FREQ, ®Data); +- if (result != XC_RESULT_SUCCESS) +- return result; +- +- (*hsync_freq_hz) = ((regData & 0x0fff) * 763)/100; +- return result; +-} +- +-static int xc_get_frame_lines(struct xc5000_priv *priv, u16 *frame_lines) +-{ +- return xc5000_readreg(priv, XREG_FRAME_LINES, frame_lines); +-} +- +-static int xc_get_quality(struct xc5000_priv *priv, u16 *quality) +-{ +- return xc5000_readreg(priv, XREG_QUALITY, quality); +-} +- +-static u16 WaitForLock(struct xc5000_priv *priv) +-{ +- u16 lockState = 0; +- int watchDogCount = 40; +- +- while ((lockState == 0) && (watchDogCount > 0)) { +- xc_get_lock_status(priv, &lockState); +- if (lockState != 1) { +- xc_wait(5); +- watchDogCount--; +- } +- } +- return lockState; +-} +- +-#define XC_TUNE_ANALOG 0 +-#define XC_TUNE_DIGITAL 1 +-static int xc_tune_channel(struct xc5000_priv *priv, u32 freq_hz, int mode) +-{ +- int found = 0; +- +- dprintk(1, "%s(%u)\n", __func__, freq_hz); +- +- if (xc_set_RF_frequency(priv, freq_hz) != XC_RESULT_SUCCESS) +- return 0; +- +- if (mode == XC_TUNE_ANALOG) { +- if (WaitForLock(priv) == 1) +- found = 1; +- } +- +- return found; +-} +- +- +-static int xc5000_fwupload(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- const struct firmware *fw; +- int ret; +- +- /* request the firmware, this will block and timeout */ +- printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n", +- XC5000_DEFAULT_FIRMWARE); +- +- ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE, +- priv->i2c_props.adap->dev.parent); +- if (ret) { +- printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n"); +- ret = XC_RESULT_RESET_FAILURE; +- goto out; +- } else { +- printk(KERN_DEBUG "xc5000: firmware read %Zu bytes.\n", +- fw->size); +- ret = XC_RESULT_SUCCESS; +- } +- +- if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) { +- printk(KERN_ERR "xc5000: firmware incorrect size\n"); +- ret = XC_RESULT_RESET_FAILURE; +- } else { +- printk(KERN_INFO "xc5000: firmware uploading...\n"); +- ret = xc_load_i2c_sequence(fe, fw->data); +- printk(KERN_INFO "xc5000: firmware upload complete...\n"); +- } +- +-out: +- release_firmware(fw); +- return ret; +-} +- +-static void xc_debug_dump(struct xc5000_priv *priv) +-{ +- u16 adc_envelope; +- u32 freq_error_hz = 0; +- u16 lock_status; +- u32 hsync_freq_hz = 0; +- u16 frame_lines; +- u16 quality; +- u8 hw_majorversion = 0, hw_minorversion = 0; +- u8 fw_majorversion = 0, fw_minorversion = 0; +- u16 fw_buildversion = 0; +- +- /* Wait for stats to stabilize. +- * Frame Lines needs two frame times after initial lock +- * before it is valid. +- */ +- xc_wait(100); +- +- xc_get_ADC_Envelope(priv, &adc_envelope); +- dprintk(1, "*** ADC envelope (0-1023) = %d\n", adc_envelope); +- +- xc_get_frequency_error(priv, &freq_error_hz); +- dprintk(1, "*** Frequency error = %d Hz\n", freq_error_hz); +- +- xc_get_lock_status(priv, &lock_status); +- dprintk(1, "*** Lock status (0-Wait, 1-Locked, 2-No-signal) = %d\n", +- lock_status); +- +- xc_get_version(priv, &hw_majorversion, &hw_minorversion, +- &fw_majorversion, &fw_minorversion); +- xc_get_buildversion(priv, &fw_buildversion); +- dprintk(1, "*** HW: V%02x.%02x, FW: V%02x.%02x.%04x\n", +- hw_majorversion, hw_minorversion, +- fw_majorversion, fw_minorversion, fw_buildversion); +- +- xc_get_hsync_freq(priv, &hsync_freq_hz); +- dprintk(1, "*** Horizontal sync frequency = %d Hz\n", hsync_freq_hz); +- +- xc_get_frame_lines(priv, &frame_lines); +- dprintk(1, "*** Frame lines = %d\n", frame_lines); +- +- xc_get_quality(priv, &quality); +- dprintk(1, "*** Quality (0:<8dB, 7:>56dB) = %d\n", quality); +-} +- +-static int xc5000_set_params(struct dvb_frontend *fe) +-{ +- int ret, b; +- struct xc5000_priv *priv = fe->tuner_priv; +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- u32 freq = fe->dtv_property_cache.frequency; +- u32 delsys = fe->dtv_property_cache.delivery_system; +- +- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { +- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { +- dprintk(1, "Unable to load firmware and init tuner\n"); +- return -EINVAL; +- } +- } +- +- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, freq); +- +- switch (delsys) { +- case SYS_ATSC: +- dprintk(1, "%s() VSB modulation\n", __func__); +- priv->rf_mode = XC_RF_MODE_AIR; +- priv->freq_hz = freq - 1750000; +- priv->video_standard = DTV6; +- break; +- case SYS_DVBC_ANNEX_B: +- dprintk(1, "%s() QAM modulation\n", __func__); +- priv->rf_mode = XC_RF_MODE_CABLE; +- priv->freq_hz = freq - 1750000; +- priv->video_standard = DTV6; +- break; +- case SYS_DVBT: +- case SYS_DVBT2: +- dprintk(1, "%s() OFDM\n", __func__); +- switch (bw) { +- case 6000000: +- priv->video_standard = DTV6; +- priv->freq_hz = freq - 1750000; +- break; +- case 7000000: +- priv->video_standard = DTV7; +- priv->freq_hz = freq - 2250000; +- break; +- case 8000000: +- priv->video_standard = DTV8; +- priv->freq_hz = freq - 2750000; +- break; +- default: +- printk(KERN_ERR "xc5000 bandwidth not set!\n"); +- return -EINVAL; +- } +- priv->rf_mode = XC_RF_MODE_AIR; +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- dprintk(1, "%s() QAM modulation\n", __func__); +- priv->rf_mode = XC_RF_MODE_CABLE; +- if (bw <= 6000000) { +- priv->video_standard = DTV6; +- priv->freq_hz = freq - 1750000; +- b = 6; +- } else if (bw <= 7000000) { +- priv->video_standard = DTV7; +- priv->freq_hz = freq - 2250000; +- b = 7; +- } else { +- priv->video_standard = DTV7_8; +- priv->freq_hz = freq - 2750000; +- b = 8; +- } +- dprintk(1, "%s() Bandwidth %dMHz (%d)\n", __func__, +- b, bw); +- break; +- default: +- printk(KERN_ERR "xc5000: delivery system is not supported!\n"); +- return -EINVAL; +- } +- +- dprintk(1, "%s() frequency=%d (compensated to %d)\n", +- __func__, freq, priv->freq_hz); +- +- ret = xc_SetSignalSource(priv, priv->rf_mode); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR +- "xc5000: xc_SetSignalSource(%d) failed\n", +- priv->rf_mode); +- return -EREMOTEIO; +- } +- +- ret = xc_SetTVStandard(priv, +- XC5000_Standard[priv->video_standard].VideoMode, +- XC5000_Standard[priv->video_standard].AudioMode); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +- return -EREMOTEIO; +- } +- +- ret = xc_set_IF_frequency(priv, priv->if_khz); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR "xc5000: xc_Set_IF_frequency(%d) failed\n", +- priv->if_khz); +- return -EIO; +- } +- +- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x8a); +- +- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_DIGITAL); +- +- if (debug) +- xc_debug_dump(priv); +- +- priv->bandwidth = bw; +- +- return 0; +-} +- +-static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret; +- u16 id; +- +- ret = xc5000_readreg(priv, XREG_PRODUCT_ID, &id); +- if (ret == XC_RESULT_SUCCESS) { +- if (id == XC_PRODUCT_ID_FW_NOT_LOADED) +- ret = XC_RESULT_RESET_FAILURE; +- else +- ret = XC_RESULT_SUCCESS; +- } +- +- dprintk(1, "%s() returns %s id = 0x%x\n", __func__, +- ret == XC_RESULT_SUCCESS ? "True" : "False", id); +- return ret; +-} +- +-static int xc5000_set_tv_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret; +- +- dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", +- __func__, params->frequency); +- +- /* Fix me: it could be air. */ +- priv->rf_mode = params->mode; +- if (params->mode > XC_RF_MODE_CABLE) +- priv->rf_mode = XC_RF_MODE_CABLE; +- +- /* params->frequency is in units of 62.5khz */ +- priv->freq_hz = params->frequency * 62500; +- +- /* FIX ME: Some video standards may have several possible audio +- standards. We simply default to one of them here. +- */ +- if (params->std & V4L2_STD_MN) { +- /* default to BTSC audio standard */ +- priv->video_standard = MN_NTSC_PAL_BTSC; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_BG) { +- /* default to NICAM audio standard */ +- priv->video_standard = BG_PAL_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_I) { +- /* default to NICAM audio standard */ +- priv->video_standard = I_PAL_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_PAL_DK) { +- /* default to NICAM audio standard */ +- priv->video_standard = DK_PAL_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_DK) { +- /* default to A2 DK1 audio standard */ +- priv->video_standard = DK_SECAM_A2DK1; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_L) { +- priv->video_standard = L_SECAM_NICAM; +- goto tune_channel; +- } +- +- if (params->std & V4L2_STD_SECAM_LC) { +- priv->video_standard = LC_SECAM_NICAM; +- goto tune_channel; +- } +- +-tune_channel: +- ret = xc_SetSignalSource(priv, priv->rf_mode); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR +- "xc5000: xc_SetSignalSource(%d) failed\n", +- priv->rf_mode); +- return -EREMOTEIO; +- } +- +- ret = xc_SetTVStandard(priv, +- XC5000_Standard[priv->video_standard].VideoMode, +- XC5000_Standard[priv->video_standard].AudioMode); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +- return -EREMOTEIO; +- } +- +- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09); +- +- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); +- +- if (debug) +- xc_debug_dump(priv); +- +- return 0; +-} +- +-static int xc5000_set_radio_freq(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret = -EINVAL; +- u8 radio_input; +- +- dprintk(1, "%s() frequency=%d (in units of khz)\n", +- __func__, params->frequency); +- +- if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) { +- dprintk(1, "%s() radio input not configured\n", __func__); +- return -EINVAL; +- } +- +- if (priv->radio_input == XC5000_RADIO_FM1) +- radio_input = FM_Radio_INPUT1; +- else if (priv->radio_input == XC5000_RADIO_FM2) +- radio_input = FM_Radio_INPUT2; +- else if (priv->radio_input == XC5000_RADIO_FM1_MONO) +- radio_input = FM_Radio_INPUT1_MONO; +- else { +- dprintk(1, "%s() unknown radio input %d\n", __func__, +- priv->radio_input); +- return -EINVAL; +- } +- +- priv->freq_hz = params->frequency * 125 / 2; +- +- priv->rf_mode = XC_RF_MODE_AIR; +- +- ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, +- XC5000_Standard[radio_input].AudioMode); +- +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); +- return -EREMOTEIO; +- } +- +- ret = xc_SetSignalSource(priv, priv->rf_mode); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR +- "xc5000: xc_SetSignalSource(%d) failed\n", +- priv->rf_mode); +- return -EREMOTEIO; +- } +- +- if ((priv->radio_input == XC5000_RADIO_FM1) || +- (priv->radio_input == XC5000_RADIO_FM2)) +- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x09); +- else if (priv->radio_input == XC5000_RADIO_FM1_MONO) +- xc_write_reg(priv, XREG_OUTPUT_AMP, 0x06); +- +- xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); +- +- return 0; +-} +- +-static int xc5000_set_analog_params(struct dvb_frontend *fe, +- struct analog_parameters *params) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret = -EINVAL; +- +- if (priv->i2c_props.adap == NULL) +- return -EINVAL; +- +- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { +- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { +- dprintk(1, "Unable to load firmware and init tuner\n"); +- return -EINVAL; +- } +- } +- +- switch (params->mode) { +- case V4L2_TUNER_RADIO: +- ret = xc5000_set_radio_freq(fe, params); +- break; +- case V4L2_TUNER_ANALOG_TV: +- case V4L2_TUNER_DIGITAL_TV: +- ret = xc5000_set_tv_freq(fe, params); +- break; +- } +- +- return ret; +-} +- +- +-static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- *freq = priv->freq_hz; +- return 0; +-} +- +-static int xc5000_get_if_frequency(struct dvb_frontend *fe, u32 *freq) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- *freq = priv->if_khz * 1000; +- return 0; +-} +- +-static int xc5000_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- +- *bw = priv->bandwidth; +- return 0; +-} +- +-static int xc5000_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- u16 lock_status = 0; +- +- xc_get_lock_status(priv, &lock_status); +- +- dprintk(1, "%s() lock_status = 0x%08x\n", __func__, lock_status); +- +- *status = lock_status; +- +- return 0; +-} +- +-static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- int ret = 0; +- +- if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { +- ret = xc5000_fwupload(fe); +- if (ret != XC_RESULT_SUCCESS) +- return ret; +- } +- +- /* Start the tuner self-calibration process */ +- ret |= xc_initialize(priv); +- +- /* Wait for calibration to complete. +- * We could continue but XC5000 will clock stretch subsequent +- * I2C transactions until calibration is complete. This way we +- * don't have to rely on clock stretching working. +- */ +- xc_wait(100); +- +- /* Default to "CABLE" mode */ +- ret |= xc_write_reg(priv, XREG_SIGNALSOURCE, XC_RF_MODE_CABLE); +- +- return ret; +-} +- +-static int xc5000_sleep(struct dvb_frontend *fe) +-{ +- int ret; +- +- dprintk(1, "%s()\n", __func__); +- +- /* Avoid firmware reload on slow devices */ +- if (no_poweroff) +- return 0; +- +- /* According to Xceive technical support, the "powerdown" register +- was removed in newer versions of the firmware. The "supported" +- way to sleep the tuner is to pull the reset pin low for 10ms */ +- ret = xc5000_TunerReset(fe); +- if (ret != XC_RESULT_SUCCESS) { +- printk(KERN_ERR +- "xc5000: %s() unable to shutdown tuner\n", +- __func__); +- return -EREMOTEIO; +- } else +- return XC_RESULT_SUCCESS; +-} +- +-static int xc5000_init(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- dprintk(1, "%s()\n", __func__); +- +- if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { +- printk(KERN_ERR "xc5000: Unable to initialise tuner\n"); +- return -EREMOTEIO; +- } +- +- if (debug) +- xc_debug_dump(priv); +- +- return 0; +-} +- +-static int xc5000_release(struct dvb_frontend *fe) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- +- dprintk(1, "%s()\n", __func__); +- +- mutex_lock(&xc5000_list_mutex); +- +- if (priv) +- hybrid_tuner_release_state(priv); +- +- mutex_unlock(&xc5000_list_mutex); +- +- fe->tuner_priv = NULL; +- +- return 0; +-} +- +-static int xc5000_set_config(struct dvb_frontend *fe, void *priv_cfg) +-{ +- struct xc5000_priv *priv = fe->tuner_priv; +- struct xc5000_config *p = priv_cfg; +- +- dprintk(1, "%s()\n", __func__); +- +- if (p->if_khz) +- priv->if_khz = p->if_khz; +- +- if (p->radio_input) +- priv->radio_input = p->radio_input; +- +- return 0; +-} +- +- +-static const struct dvb_tuner_ops xc5000_tuner_ops = { +- .info = { +- .name = "Xceive XC5000", +- .frequency_min = 1000000, +- .frequency_max = 1023000000, +- .frequency_step = 50000, +- }, +- +- .release = xc5000_release, +- .init = xc5000_init, +- .sleep = xc5000_sleep, +- +- .set_config = xc5000_set_config, +- .set_params = xc5000_set_params, +- .set_analog_params = xc5000_set_analog_params, +- .get_frequency = xc5000_get_frequency, +- .get_if_frequency = xc5000_get_if_frequency, +- .get_bandwidth = xc5000_get_bandwidth, +- .get_status = xc5000_get_status +-}; +- +-struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct xc5000_config *cfg) +-{ +- struct xc5000_priv *priv = NULL; +- int instance; +- u16 id = 0; +- +- dprintk(1, "%s(%d-%04x)\n", __func__, +- i2c ? i2c_adapter_id(i2c) : -1, +- cfg ? cfg->i2c_address : -1); +- +- mutex_lock(&xc5000_list_mutex); +- +- instance = hybrid_tuner_request_state(struct xc5000_priv, priv, +- hybrid_tuner_instance_list, +- i2c, cfg->i2c_address, "xc5000"); +- switch (instance) { +- case 0: +- goto fail; +- break; +- case 1: +- /* new tuner instance */ +- priv->bandwidth = 6000000; +- fe->tuner_priv = priv; +- break; +- default: +- /* existing tuner instance */ +- fe->tuner_priv = priv; +- break; +- } +- +- if (priv->if_khz == 0) { +- /* If the IF hasn't been set yet, use the value provided by +- the caller (occurs in hybrid devices where the analog +- call to xc5000_attach occurs before the digital side) */ +- priv->if_khz = cfg->if_khz; +- } +- +- if (priv->radio_input == 0) +- priv->radio_input = cfg->radio_input; +- +- /* Check if firmware has been loaded. It is possible that another +- instance of the driver has loaded the firmware. +- */ +- if (xc5000_readreg(priv, XREG_PRODUCT_ID, &id) != XC_RESULT_SUCCESS) +- goto fail; +- +- switch (id) { +- case XC_PRODUCT_ID_FW_LOADED: +- printk(KERN_INFO +- "xc5000: Successfully identified at address 0x%02x\n", +- cfg->i2c_address); +- printk(KERN_INFO +- "xc5000: Firmware has been loaded previously\n"); +- break; +- case XC_PRODUCT_ID_FW_NOT_LOADED: +- printk(KERN_INFO +- "xc5000: Successfully identified at address 0x%02x\n", +- cfg->i2c_address); +- printk(KERN_INFO +- "xc5000: Firmware has not been loaded previously\n"); +- break; +- default: +- printk(KERN_ERR +- "xc5000: Device not found at addr 0x%02x (0x%x)\n", +- cfg->i2c_address, id); +- goto fail; +- } +- +- mutex_unlock(&xc5000_list_mutex); +- +- memcpy(&fe->ops.tuner_ops, &xc5000_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- return fe; +-fail: +- mutex_unlock(&xc5000_list_mutex); +- +- xc5000_release(fe); +- return NULL; +-} +-EXPORT_SYMBOL(xc5000_attach); +- +-MODULE_AUTHOR("Steven Toth"); +-MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h +deleted file mode 100644 +index e295745..0000000 +--- a/drivers/media/common/tuners/xc5000.h ++++ /dev/null +@@ -1,68 +0,0 @@ +-/* +- * Driver for Xceive XC5000 "QAM/8VSB single chip tuner" +- * +- * Copyright (c) 2007 Steven Toth +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __XC5000_H__ +-#define __XC5000_H__ +- +-#include +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct xc5000_config { +- u8 i2c_address; +- u32 if_khz; +- u8 radio_input; +-}; +- +-/* xc5000 callback command */ +-#define XC5000_TUNER_RESET 0 +- +-/* Possible Radio inputs */ +-#define XC5000_RADIO_NOT_CONFIGURED 0 +-#define XC5000_RADIO_FM1 1 +-#define XC5000_RADIO_FM2 2 +-#define XC5000_RADIO_FM1_MONO 3 +- +-/* For each bridge framework, when it attaches either analog or digital, +- * it has to store a reference back to its _core equivalent structure, +- * so that it can service the hardware by steering gpio's etc. +- * Each bridge implementation is different so cast devptr accordingly. +- * The xc5000 driver cares not for this value, other than ensuring +- * it's passed back to a bridge during tuner_callback(). +- */ +- +-#if defined(CONFIG_MEDIA_TUNER_XC5000) || \ +- (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct xc5000_config *cfg); +-#else +-static inline struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct xc5000_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb-core/Kconfig b/drivers/media/dvb-core/Kconfig +new file mode 100644 +index 0000000..fa7a249 +--- /dev/null ++++ b/drivers/media/dvb-core/Kconfig +@@ -0,0 +1,29 @@ ++# ++# DVB device configuration ++# ++ ++config DVB_MAX_ADAPTERS ++ int "maximum number of DVB/ATSC adapters" ++ depends on DVB_CORE ++ default 8 ++ range 1 255 ++ help ++ Maximum number of DVB/ATSC adapters. Increasing this number ++ increases the memory consumption of the DVB subsystem even ++ if a much lower number of DVB/ATSC adapters is present. ++ Only values in the range 4-32 are tested. ++ ++ If you are unsure about this, use the default value 8 ++ ++config DVB_DYNAMIC_MINORS ++ bool "Dynamic DVB minor allocation" ++ depends on DVB_CORE ++ default n ++ help ++ If you say Y here, the DVB subsystem will use dynamic minor ++ allocation for any device that uses the DVB major number. ++ This means that you can have more than 4 of a single type ++ of device (like demuxes and frontends) per adapter, but udev ++ will be required to manage the device nodes. ++ ++ If you are unsure about this, say N here. +diff --git a/drivers/media/dvb-core/Makefile b/drivers/media/dvb-core/Makefile +new file mode 100644 +index 0000000..8f22bcd +--- /dev/null ++++ b/drivers/media/dvb-core/Makefile +@@ -0,0 +1,11 @@ ++# ++# Makefile for the kernel DVB device drivers. ++# ++ ++dvb-net-$(CONFIG_DVB_NET) := dvb_net.o ++ ++dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ ++ dvb_ca_en50221.o dvb_frontend.o \ ++ $(dvb-net-y) dvb_ringbuffer.o dvb_math.o ++ ++obj-$(CONFIG_DVB_CORE) += dvb-core.o +diff --git a/drivers/media/dvb-core/demux.h b/drivers/media/dvb-core/demux.h +new file mode 100644 +index 0000000..eb91fd8 +--- /dev/null ++++ b/drivers/media/dvb-core/demux.h +@@ -0,0 +1,280 @@ ++/* ++ * demux.h ++ * ++ * Copyright (c) 2002 Convergence GmbH ++ * ++ * based on code: ++ * Copyright (c) 2000 Nokia Research Center ++ * Tampere, FINLAND ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef __DEMUX_H ++#define __DEMUX_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++/*--------------------------------------------------------------------------*/ ++/* Common definitions */ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter. ++ */ ++ ++#ifndef DMX_MAX_FILTER_SIZE ++#define DMX_MAX_FILTER_SIZE 18 ++#endif ++ ++/* ++ * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter. ++ */ ++ ++#ifndef DMX_MAX_SECTION_SIZE ++#define DMX_MAX_SECTION_SIZE 4096 ++#endif ++#ifndef DMX_MAX_SECFEED_SIZE ++#define DMX_MAX_SECFEED_SIZE (DMX_MAX_SECTION_SIZE + 188) ++#endif ++ ++ ++/* ++ * enum dmx_success: Success codes for the Demux Callback API. ++ */ ++ ++enum dmx_success { ++ DMX_OK = 0, /* Received Ok */ ++ DMX_LENGTH_ERROR, /* Incorrect length */ ++ DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */ ++ DMX_CRC_ERROR, /* Incorrect CRC */ ++ DMX_FRAME_ERROR, /* Frame alignment error */ ++ DMX_FIFO_ERROR, /* Receiver FIFO overrun */ ++ DMX_MISSED_ERROR /* Receiver missed packet */ ++} ; ++ ++/*--------------------------------------------------------------------------*/ ++/* TS packet reception */ ++/*--------------------------------------------------------------------------*/ ++ ++/* TS filter type for set() */ ++ ++#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */ ++#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS ++ payload (<=184 bytes per packet) to callback */ ++#define TS_DECODER 4 /* send stream to built-in decoder (if present) */ ++#define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to ++ the demux device, not to the dvr device */ ++ ++/* PES type for filters which write to built-in decoder */ ++/* these should be kept identical to the types in dmx.h */ ++ ++enum dmx_ts_pes ++{ /* also send packets to decoder (if it exists) */ ++ DMX_TS_PES_AUDIO0, ++ DMX_TS_PES_VIDEO0, ++ DMX_TS_PES_TELETEXT0, ++ DMX_TS_PES_SUBTITLE0, ++ DMX_TS_PES_PCR0, ++ ++ DMX_TS_PES_AUDIO1, ++ DMX_TS_PES_VIDEO1, ++ DMX_TS_PES_TELETEXT1, ++ DMX_TS_PES_SUBTITLE1, ++ DMX_TS_PES_PCR1, ++ ++ DMX_TS_PES_AUDIO2, ++ DMX_TS_PES_VIDEO2, ++ DMX_TS_PES_TELETEXT2, ++ DMX_TS_PES_SUBTITLE2, ++ DMX_TS_PES_PCR2, ++ ++ DMX_TS_PES_AUDIO3, ++ DMX_TS_PES_VIDEO3, ++ DMX_TS_PES_TELETEXT3, ++ DMX_TS_PES_SUBTITLE3, ++ DMX_TS_PES_PCR3, ++ ++ DMX_TS_PES_OTHER ++}; ++ ++#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0 ++#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0 ++#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0 ++#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0 ++#define DMX_TS_PES_PCR DMX_TS_PES_PCR0 ++ ++ ++struct dmx_ts_feed { ++ int is_filtering; /* Set to non-zero when filtering in progress */ ++ struct dmx_demux *parent; /* Back-pointer */ ++ void *priv; /* Pointer to private data of the API client */ ++ int (*set) (struct dmx_ts_feed *feed, ++ u16 pid, ++ int type, ++ enum dmx_ts_pes pes_type, ++ size_t circular_buffer_size, ++ struct timespec timeout); ++ int (*start_filtering) (struct dmx_ts_feed* feed); ++ int (*stop_filtering) (struct dmx_ts_feed* feed); ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* Section reception */ ++/*--------------------------------------------------------------------------*/ ++ ++struct dmx_section_filter { ++ u8 filter_value [DMX_MAX_FILTER_SIZE]; ++ u8 filter_mask [DMX_MAX_FILTER_SIZE]; ++ u8 filter_mode [DMX_MAX_FILTER_SIZE]; ++ struct dmx_section_feed* parent; /* Back-pointer */ ++ void* priv; /* Pointer to private data of the API client */ ++}; ++ ++struct dmx_section_feed { ++ int is_filtering; /* Set to non-zero when filtering in progress */ ++ struct dmx_demux* parent; /* Back-pointer */ ++ void* priv; /* Pointer to private data of the API client */ ++ ++ int check_crc; ++ u32 crc_val; ++ ++ u8 *secbuf; ++ u8 secbuf_base[DMX_MAX_SECFEED_SIZE]; ++ u16 secbufp, seclen, tsfeedp; ++ ++ int (*set) (struct dmx_section_feed* feed, ++ u16 pid, ++ size_t circular_buffer_size, ++ int check_crc); ++ int (*allocate_filter) (struct dmx_section_feed* feed, ++ struct dmx_section_filter** filter); ++ int (*release_filter) (struct dmx_section_feed* feed, ++ struct dmx_section_filter* filter); ++ int (*start_filtering) (struct dmx_section_feed* feed); ++ int (*stop_filtering) (struct dmx_section_feed* feed); ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* Callback functions */ ++/*--------------------------------------------------------------------------*/ ++ ++typedef int (*dmx_ts_cb) ( const u8 * buffer1, ++ size_t buffer1_length, ++ const u8 * buffer2, ++ size_t buffer2_length, ++ struct dmx_ts_feed* source, ++ enum dmx_success success); ++ ++typedef int (*dmx_section_cb) ( const u8 * buffer1, ++ size_t buffer1_len, ++ const u8 * buffer2, ++ size_t buffer2_len, ++ struct dmx_section_filter * source, ++ enum dmx_success success); ++ ++/*--------------------------------------------------------------------------*/ ++/* DVB Front-End */ ++/*--------------------------------------------------------------------------*/ ++ ++enum dmx_frontend_source { ++ DMX_MEMORY_FE, ++ DMX_FRONTEND_0, ++ DMX_FRONTEND_1, ++ DMX_FRONTEND_2, ++ DMX_FRONTEND_3, ++ DMX_STREAM_0, /* external stream input, e.g. LVDS */ ++ DMX_STREAM_1, ++ DMX_STREAM_2, ++ DMX_STREAM_3 ++}; ++ ++struct dmx_frontend { ++ struct list_head connectivity_list; /* List of front-ends that can ++ be connected to a particular ++ demux */ ++ enum dmx_frontend_source source; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* MPEG-2 TS Demux */ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * Flags OR'ed in the capabilities field of struct dmx_demux. ++ */ ++ ++#define DMX_TS_FILTERING 1 ++#define DMX_PES_FILTERING 2 ++#define DMX_SECTION_FILTERING 4 ++#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */ ++#define DMX_CRC_CHECKING 16 ++#define DMX_TS_DESCRAMBLING 32 ++ ++/* ++ * Demux resource type identifier. ++*/ ++ ++/* ++ * DMX_FE_ENTRY(): Casts elements in the list of registered ++ * front-ends from the generic type struct list_head ++ * to the type * struct dmx_frontend ++ *. ++*/ ++ ++#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list) ++ ++struct dmx_demux { ++ u32 capabilities; /* Bitfield of capability flags */ ++ struct dmx_frontend* frontend; /* Front-end connected to the demux */ ++ void* priv; /* Pointer to private data of the API client */ ++ int (*open) (struct dmx_demux* demux); ++ int (*close) (struct dmx_demux* demux); ++ int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count); ++ int (*allocate_ts_feed) (struct dmx_demux* demux, ++ struct dmx_ts_feed** feed, ++ dmx_ts_cb callback); ++ int (*release_ts_feed) (struct dmx_demux* demux, ++ struct dmx_ts_feed* feed); ++ int (*allocate_section_feed) (struct dmx_demux* demux, ++ struct dmx_section_feed** feed, ++ dmx_section_cb callback); ++ int (*release_section_feed) (struct dmx_demux* demux, ++ struct dmx_section_feed* feed); ++ int (*add_frontend) (struct dmx_demux* demux, ++ struct dmx_frontend* frontend); ++ int (*remove_frontend) (struct dmx_demux* demux, ++ struct dmx_frontend* frontend); ++ struct list_head* (*get_frontends) (struct dmx_demux* demux); ++ int (*connect_frontend) (struct dmx_demux* demux, ++ struct dmx_frontend* frontend); ++ int (*disconnect_frontend) (struct dmx_demux* demux); ++ ++ int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids); ++ ++ int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps); ++ ++ int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src); ++ ++ int (*get_stc) (struct dmx_demux* demux, unsigned int num, ++ u64 *stc, unsigned int *base); ++}; ++ ++#endif /* #ifndef __DEMUX_H */ +diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c +new file mode 100644 +index 0000000..caca6e5 +--- /dev/null ++++ b/drivers/media/dvb-core/dmxdev.c +@@ -0,0 +1,1299 @@ ++/* ++ * dmxdev.c - DVB demultiplexer device ++ * ++ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dmxdev.h" ++ ++static int debug; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); ++ ++#define dprintk if (debug) printk ++ ++static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf, ++ const u8 *src, size_t len) ++{ ++ ssize_t free; ++ ++ if (!len) ++ return 0; ++ if (!buf->data) ++ return 0; ++ ++ free = dvb_ringbuffer_free(buf); ++ if (len > free) { ++ dprintk("dmxdev: buffer overflow\n"); ++ return -EOVERFLOW; ++ } ++ ++ return dvb_ringbuffer_write(buf, src, len); ++} ++ ++static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, ++ int non_blocking, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ size_t todo; ++ ssize_t avail; ++ ssize_t ret = 0; ++ ++ if (!src->data) ++ return 0; ++ ++ if (src->error) { ++ ret = src->error; ++ dvb_ringbuffer_flush(src); ++ return ret; ++ } ++ ++ for (todo = count; todo > 0; todo -= ret) { ++ if (non_blocking && dvb_ringbuffer_empty(src)) { ++ ret = -EWOULDBLOCK; ++ break; ++ } ++ ++ ++ ret = wait_event_interruptible(src->queue, ++ !dvb_ringbuffer_empty(src) || ++ (src->error != 0) || ++ (src->do_wait != 1)); ++ if (src->do_wait != 1) ++ ret = -EINTR; ++ ++ if (ret < 0) ++ break; ++ ++ if (src->error) { ++ ret = src->error; ++ dvb_ringbuffer_flush(src); ++ break; ++ } ++ ++ avail = dvb_ringbuffer_avail(src); ++ if (avail > todo) ++ avail = todo; ++ ++ ret = dvb_ringbuffer_read_user(src, buf, avail); ++ if (ret < 0) ++ break; ++ ++ buf += ret; ++ } ++ ++ return (count - todo) ? (count - todo) : ret; ++} ++ ++static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type) ++{ ++ struct list_head *head, *pos; ++ ++ head = demux->get_frontends(demux); ++ if (!head) ++ return NULL; ++ list_for_each(pos, head) ++ if (DMX_FE_ENTRY(pos)->source == type) ++ return DMX_FE_ENTRY(pos); ++ ++ return NULL; ++} ++ ++static int dvb_dvr_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ struct dmx_frontend *front; ++ ++ dprintk("function : %s\n", __func__); ++ ++ if (mutex_lock_interruptible(&dmxdev->mutex)) ++ return -ERESTARTSYS; ++ ++ if (dmxdev->exit) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ENODEV; ++ } ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDWR) { ++ if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ void *mem; ++ if (!dvbdev->readers) { ++ mutex_unlock(&dmxdev->mutex); ++ return -EBUSY; ++ } ++ mem = vmalloc(DVR_BUFFER_SIZE); ++ if (!mem) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ENOMEM; ++ } ++ dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); ++ dvbdev->readers--; ++ } ++ ++ if ((file->f_flags & O_ACCMODE) == O_WRONLY) { ++ dmxdev->dvr_orig_fe = dmxdev->demux->frontend; ++ ++ if (!dmxdev->demux->write) { ++ mutex_unlock(&dmxdev->mutex); ++ return -EOPNOTSUPP; ++ } ++ ++ front = get_fe(dmxdev->demux, DMX_MEMORY_FE); ++ ++ if (!front) { ++ mutex_unlock(&dmxdev->mutex); ++ return -EINVAL; ++ } ++ dmxdev->demux->disconnect_frontend(dmxdev->demux); ++ dmxdev->demux->connect_frontend(dmxdev->demux, front); ++ } ++ dvbdev->users++; ++ mutex_unlock(&dmxdev->mutex); ++ return 0; ++} ++ ++static int dvb_dvr_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ ++ mutex_lock(&dmxdev->mutex); ++ ++ if ((file->f_flags & O_ACCMODE) == O_WRONLY) { ++ dmxdev->demux->disconnect_frontend(dmxdev->demux); ++ dmxdev->demux->connect_frontend(dmxdev->demux, ++ dmxdev->dvr_orig_fe); ++ } ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ dvbdev->readers++; ++ if (dmxdev->dvr_buffer.data) { ++ void *mem = dmxdev->dvr_buffer.data; ++ mb(); ++ spin_lock_irq(&dmxdev->lock); ++ dmxdev->dvr_buffer.data = NULL; ++ spin_unlock_irq(&dmxdev->lock); ++ vfree(mem); ++ } ++ } ++ /* TODO */ ++ dvbdev->users--; ++ if (dvbdev->users == 1 && dmxdev->exit == 1) { ++ fops_put(file->f_op); ++ file->f_op = NULL; ++ mutex_unlock(&dmxdev->mutex); ++ wake_up(&dvbdev->wait_queue); ++ } else ++ mutex_unlock(&dmxdev->mutex); ++ ++ return 0; ++} ++ ++static ssize_t dvb_dvr_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ int ret; ++ ++ if (!dmxdev->demux->write) ++ return -EOPNOTSUPP; ++ if ((file->f_flags & O_ACCMODE) != O_WRONLY) ++ return -EINVAL; ++ if (mutex_lock_interruptible(&dmxdev->mutex)) ++ return -ERESTARTSYS; ++ ++ if (dmxdev->exit) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ENODEV; ++ } ++ ret = dmxdev->demux->write(dmxdev->demux, buf, count); ++ mutex_unlock(&dmxdev->mutex); ++ return ret; ++} ++ ++static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ ++ if (dmxdev->exit) ++ return -ENODEV; ++ ++ return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, ++ file->f_flags & O_NONBLOCK, ++ buf, count, ppos); ++} ++ ++static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, ++ unsigned long size) ++{ ++ struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer; ++ void *newmem; ++ void *oldmem; ++ ++ dprintk("function : %s\n", __func__); ++ ++ if (buf->size == size) ++ return 0; ++ if (!size) ++ return -EINVAL; ++ ++ newmem = vmalloc(size); ++ if (!newmem) ++ return -ENOMEM; ++ ++ oldmem = buf->data; ++ ++ spin_lock_irq(&dmxdev->lock); ++ buf->data = newmem; ++ buf->size = size; ++ ++ /* reset and not flush in case the buffer shrinks */ ++ dvb_ringbuffer_reset(buf); ++ spin_unlock_irq(&dmxdev->lock); ++ ++ vfree(oldmem); ++ ++ return 0; ++} ++ ++static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter ++ *dmxdevfilter, int state) ++{ ++ spin_lock_irq(&dmxdevfilter->dev->lock); ++ dmxdevfilter->state = state; ++ spin_unlock_irq(&dmxdevfilter->dev->lock); ++} ++ ++static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, ++ unsigned long size) ++{ ++ struct dvb_ringbuffer *buf = &dmxdevfilter->buffer; ++ void *newmem; ++ void *oldmem; ++ ++ if (buf->size == size) ++ return 0; ++ if (!size) ++ return -EINVAL; ++ if (dmxdevfilter->state >= DMXDEV_STATE_GO) ++ return -EBUSY; ++ ++ newmem = vmalloc(size); ++ if (!newmem) ++ return -ENOMEM; ++ ++ oldmem = buf->data; ++ ++ spin_lock_irq(&dmxdevfilter->dev->lock); ++ buf->data = newmem; ++ buf->size = size; ++ ++ /* reset and not flush in case the buffer shrinks */ ++ dvb_ringbuffer_reset(buf); ++ spin_unlock_irq(&dmxdevfilter->dev->lock); ++ ++ vfree(oldmem); ++ ++ return 0; ++} ++ ++static void dvb_dmxdev_filter_timeout(unsigned long data) ++{ ++ struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data; ++ ++ dmxdevfilter->buffer.error = -ETIMEDOUT; ++ spin_lock_irq(&dmxdevfilter->dev->lock); ++ dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT; ++ spin_unlock_irq(&dmxdevfilter->dev->lock); ++ wake_up(&dmxdevfilter->buffer.queue); ++} ++ ++static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter) ++{ ++ struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec; ++ ++ del_timer(&dmxdevfilter->timer); ++ if (para->timeout) { ++ dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout; ++ dmxdevfilter->timer.data = (unsigned long)dmxdevfilter; ++ dmxdevfilter->timer.expires = ++ jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000; ++ add_timer(&dmxdevfilter->timer); ++ } ++} ++ ++static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, ++ const u8 *buffer2, size_t buffer2_len, ++ struct dmx_section_filter *filter, ++ enum dmx_success success) ++{ ++ struct dmxdev_filter *dmxdevfilter = filter->priv; ++ int ret; ++ ++ if (dmxdevfilter->buffer.error) { ++ wake_up(&dmxdevfilter->buffer.queue); ++ return 0; ++ } ++ spin_lock(&dmxdevfilter->dev->lock); ++ if (dmxdevfilter->state != DMXDEV_STATE_GO) { ++ spin_unlock(&dmxdevfilter->dev->lock); ++ return 0; ++ } ++ del_timer(&dmxdevfilter->timer); ++ dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n", ++ buffer1[0], buffer1[1], ++ buffer1[2], buffer1[3], buffer1[4], buffer1[5]); ++ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, ++ buffer1_len); ++ if (ret == buffer1_len) { ++ ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, ++ buffer2_len); ++ } ++ if (ret < 0) { ++ dvb_ringbuffer_flush(&dmxdevfilter->buffer); ++ dmxdevfilter->buffer.error = ret; ++ } ++ if (dmxdevfilter->params.sec.flags & DMX_ONESHOT) ++ dmxdevfilter->state = DMXDEV_STATE_DONE; ++ spin_unlock(&dmxdevfilter->dev->lock); ++ wake_up(&dmxdevfilter->buffer.queue); ++ return 0; ++} ++ ++static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, ++ const u8 *buffer2, size_t buffer2_len, ++ struct dmx_ts_feed *feed, ++ enum dmx_success success) ++{ ++ struct dmxdev_filter *dmxdevfilter = feed->priv; ++ struct dvb_ringbuffer *buffer; ++ int ret; ++ ++ spin_lock(&dmxdevfilter->dev->lock); ++ if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) { ++ spin_unlock(&dmxdevfilter->dev->lock); ++ return 0; ++ } ++ ++ if (dmxdevfilter->params.pes.output == DMX_OUT_TAP ++ || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) ++ buffer = &dmxdevfilter->buffer; ++ else ++ buffer = &dmxdevfilter->dev->dvr_buffer; ++ if (buffer->error) { ++ spin_unlock(&dmxdevfilter->dev->lock); ++ wake_up(&buffer->queue); ++ return 0; ++ } ++ ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len); ++ if (ret == buffer1_len) ++ ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len); ++ if (ret < 0) { ++ dvb_ringbuffer_flush(buffer); ++ buffer->error = ret; ++ } ++ spin_unlock(&dmxdevfilter->dev->lock); ++ wake_up(&buffer->queue); ++ return 0; ++} ++ ++/* stop feed but only mark the specified filter as stopped (state set) */ ++static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) ++{ ++ struct dmxdev_feed *feed; ++ ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); ++ ++ switch (dmxdevfilter->type) { ++ case DMXDEV_TYPE_SEC: ++ del_timer(&dmxdevfilter->timer); ++ dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); ++ break; ++ case DMXDEV_TYPE_PES: ++ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) ++ feed->ts->stop_filtering(feed->ts); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* start feed associated with the specified filter */ ++static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) ++{ ++ struct dmxdev_feed *feed; ++ int ret; ++ ++ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); ++ ++ switch (filter->type) { ++ case DMXDEV_TYPE_SEC: ++ return filter->feed.sec->start_filtering(filter->feed.sec); ++ case DMXDEV_TYPE_PES: ++ list_for_each_entry(feed, &filter->feed.ts, next) { ++ ret = feed->ts->start_filtering(feed->ts); ++ if (ret < 0) { ++ dvb_dmxdev_feed_stop(filter); ++ return ret; ++ } ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* restart section feed if it has filters left associated with it, ++ otherwise release the feed */ ++static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) ++{ ++ int i; ++ struct dmxdev *dmxdev = filter->dev; ++ u16 pid = filter->params.sec.pid; ++ ++ for (i = 0; i < dmxdev->filternum; i++) ++ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO && ++ dmxdev->filter[i].type == DMXDEV_TYPE_SEC && ++ dmxdev->filter[i].params.sec.pid == pid) { ++ dvb_dmxdev_feed_start(&dmxdev->filter[i]); ++ return 0; ++ } ++ ++ filter->dev->demux->release_section_feed(dmxdev->demux, ++ filter->feed.sec); ++ ++ return 0; ++} ++ ++static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) ++{ ++ struct dmxdev_feed *feed; ++ struct dmx_demux *demux; ++ ++ if (dmxdevfilter->state < DMXDEV_STATE_GO) ++ return 0; ++ ++ switch (dmxdevfilter->type) { ++ case DMXDEV_TYPE_SEC: ++ if (!dmxdevfilter->feed.sec) ++ break; ++ dvb_dmxdev_feed_stop(dmxdevfilter); ++ if (dmxdevfilter->filter.sec) ++ dmxdevfilter->feed.sec-> ++ release_filter(dmxdevfilter->feed.sec, ++ dmxdevfilter->filter.sec); ++ dvb_dmxdev_feed_restart(dmxdevfilter); ++ dmxdevfilter->feed.sec = NULL; ++ break; ++ case DMXDEV_TYPE_PES: ++ dvb_dmxdev_feed_stop(dmxdevfilter); ++ demux = dmxdevfilter->dev->demux; ++ list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) { ++ demux->release_ts_feed(demux, feed->ts); ++ feed->ts = NULL; ++ } ++ break; ++ default: ++ if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) ++ return 0; ++ return -EINVAL; ++ } ++ ++ dvb_ringbuffer_flush(&dmxdevfilter->buffer); ++ return 0; ++} ++ ++static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter) ++{ ++ struct dmxdev_feed *feed, *tmp; ++ ++ /* delete all PIDs */ ++ list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) { ++ list_del(&feed->next); ++ kfree(feed); ++ } ++ ++ BUG_ON(!list_empty(&dmxdevfilter->feed.ts)); ++} ++ ++static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) ++{ ++ if (dmxdevfilter->state < DMXDEV_STATE_SET) ++ return 0; ++ ++ if (dmxdevfilter->type == DMXDEV_TYPE_PES) ++ dvb_dmxdev_delete_pids(dmxdevfilter); ++ ++ dmxdevfilter->type = DMXDEV_TYPE_NONE; ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); ++ return 0; ++} ++ ++static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, ++ struct dmxdev_filter *filter, ++ struct dmxdev_feed *feed) ++{ ++ struct timespec timeout = { 0 }; ++ struct dmx_pes_filter_params *para = &filter->params.pes; ++ dmx_output_t otype; ++ int ret; ++ int ts_type; ++ dmx_pes_type_t ts_pes; ++ struct dmx_ts_feed *tsfeed; ++ ++ feed->ts = NULL; ++ otype = para->output; ++ ++ ts_pes = para->pes_type; ++ ++ if (ts_pes < DMX_PES_OTHER) ++ ts_type = TS_DECODER; ++ else ++ ts_type = 0; ++ ++ if (otype == DMX_OUT_TS_TAP) ++ ts_type |= TS_PACKET; ++ else if (otype == DMX_OUT_TSDEMUX_TAP) ++ ts_type |= TS_PACKET | TS_DEMUX; ++ else if (otype == DMX_OUT_TAP) ++ ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; ++ ++ ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts, ++ dvb_dmxdev_ts_callback); ++ if (ret < 0) ++ return ret; ++ ++ tsfeed = feed->ts; ++ tsfeed->priv = filter; ++ ++ ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout); ++ if (ret < 0) { ++ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); ++ return ret; ++ } ++ ++ ret = tsfeed->start_filtering(tsfeed); ++ if (ret < 0) { ++ dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) ++{ ++ struct dmxdev *dmxdev = filter->dev; ++ struct dmxdev_feed *feed; ++ void *mem; ++ int ret, i; ++ ++ if (filter->state < DMXDEV_STATE_SET) ++ return -EINVAL; ++ ++ if (filter->state >= DMXDEV_STATE_GO) ++ dvb_dmxdev_filter_stop(filter); ++ ++ if (!filter->buffer.data) { ++ mem = vmalloc(filter->buffer.size); ++ if (!mem) ++ return -ENOMEM; ++ spin_lock_irq(&filter->dev->lock); ++ filter->buffer.data = mem; ++ spin_unlock_irq(&filter->dev->lock); ++ } ++ ++ dvb_ringbuffer_flush(&filter->buffer); ++ ++ switch (filter->type) { ++ case DMXDEV_TYPE_SEC: ++ { ++ struct dmx_sct_filter_params *para = &filter->params.sec; ++ struct dmx_section_filter **secfilter = &filter->filter.sec; ++ struct dmx_section_feed **secfeed = &filter->feed.sec; ++ ++ *secfilter = NULL; ++ *secfeed = NULL; ++ ++ ++ /* find active filter/feed with same PID */ ++ for (i = 0; i < dmxdev->filternum; i++) { ++ if (dmxdev->filter[i].state >= DMXDEV_STATE_GO && ++ dmxdev->filter[i].type == DMXDEV_TYPE_SEC && ++ dmxdev->filter[i].params.sec.pid == para->pid) { ++ *secfeed = dmxdev->filter[i].feed.sec; ++ break; ++ } ++ } ++ ++ /* if no feed found, try to allocate new one */ ++ if (!*secfeed) { ++ ret = dmxdev->demux->allocate_section_feed(dmxdev->demux, ++ secfeed, ++ dvb_dmxdev_section_callback); ++ if (ret < 0) { ++ printk("DVB (%s): could not alloc feed\n", ++ __func__); ++ return ret; ++ } ++ ++ ret = (*secfeed)->set(*secfeed, para->pid, 32768, ++ (para->flags & DMX_CHECK_CRC) ? 1 : 0); ++ if (ret < 0) { ++ printk("DVB (%s): could not set feed\n", ++ __func__); ++ dvb_dmxdev_feed_restart(filter); ++ return ret; ++ } ++ } else { ++ dvb_dmxdev_feed_stop(filter); ++ } ++ ++ ret = (*secfeed)->allocate_filter(*secfeed, secfilter); ++ if (ret < 0) { ++ dvb_dmxdev_feed_restart(filter); ++ filter->feed.sec->start_filtering(*secfeed); ++ dprintk("could not get filter\n"); ++ return ret; ++ } ++ ++ (*secfilter)->priv = filter; ++ ++ memcpy(&((*secfilter)->filter_value[3]), ++ &(para->filter.filter[1]), DMX_FILTER_SIZE - 1); ++ memcpy(&(*secfilter)->filter_mask[3], ++ ¶->filter.mask[1], DMX_FILTER_SIZE - 1); ++ memcpy(&(*secfilter)->filter_mode[3], ++ ¶->filter.mode[1], DMX_FILTER_SIZE - 1); ++ ++ (*secfilter)->filter_value[0] = para->filter.filter[0]; ++ (*secfilter)->filter_mask[0] = para->filter.mask[0]; ++ (*secfilter)->filter_mode[0] = para->filter.mode[0]; ++ (*secfilter)->filter_mask[1] = 0; ++ (*secfilter)->filter_mask[2] = 0; ++ ++ filter->todo = 0; ++ ++ ret = filter->feed.sec->start_filtering(filter->feed.sec); ++ if (ret < 0) ++ return ret; ++ ++ dvb_dmxdev_filter_timer(filter); ++ break; ++ } ++ case DMXDEV_TYPE_PES: ++ list_for_each_entry(feed, &filter->feed.ts, next) { ++ ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); ++ if (ret < 0) { ++ dvb_dmxdev_filter_stop(filter); ++ return ret; ++ } ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); ++ return 0; ++} ++ ++static int dvb_demux_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ int i; ++ struct dmxdev_filter *dmxdevfilter; ++ ++ if (!dmxdev->filter) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&dmxdev->mutex)) ++ return -ERESTARTSYS; ++ ++ for (i = 0; i < dmxdev->filternum; i++) ++ if (dmxdev->filter[i].state == DMXDEV_STATE_FREE) ++ break; ++ ++ if (i == dmxdev->filternum) { ++ mutex_unlock(&dmxdev->mutex); ++ return -EMFILE; ++ } ++ ++ dmxdevfilter = &dmxdev->filter[i]; ++ mutex_init(&dmxdevfilter->mutex); ++ file->private_data = dmxdevfilter; ++ ++ dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); ++ dmxdevfilter->type = DMXDEV_TYPE_NONE; ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); ++ init_timer(&dmxdevfilter->timer); ++ ++ dvbdev->users++; ++ ++ mutex_unlock(&dmxdev->mutex); ++ return 0; ++} ++ ++static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, ++ struct dmxdev_filter *dmxdevfilter) ++{ ++ mutex_lock(&dmxdev->mutex); ++ mutex_lock(&dmxdevfilter->mutex); ++ ++ dvb_dmxdev_filter_stop(dmxdevfilter); ++ dvb_dmxdev_filter_reset(dmxdevfilter); ++ ++ if (dmxdevfilter->buffer.data) { ++ void *mem = dmxdevfilter->buffer.data; ++ ++ spin_lock_irq(&dmxdev->lock); ++ dmxdevfilter->buffer.data = NULL; ++ spin_unlock_irq(&dmxdev->lock); ++ vfree(mem); ++ } ++ ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE); ++ wake_up(&dmxdevfilter->buffer.queue); ++ mutex_unlock(&dmxdevfilter->mutex); ++ mutex_unlock(&dmxdev->mutex); ++ return 0; ++} ++ ++static inline void invert_mode(dmx_filter_t *filter) ++{ ++ int i; ++ ++ for (i = 0; i < DMX_FILTER_SIZE; i++) ++ filter->mode[i] ^= 0xff; ++} ++ ++static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, ++ struct dmxdev_filter *filter, u16 pid) ++{ ++ struct dmxdev_feed *feed; ++ ++ if ((filter->type != DMXDEV_TYPE_PES) || ++ (filter->state < DMXDEV_STATE_SET)) ++ return -EINVAL; ++ ++ /* only TS packet filters may have multiple PIDs */ ++ if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) && ++ (!list_empty(&filter->feed.ts))) ++ return -EINVAL; ++ ++ feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL); ++ if (feed == NULL) ++ return -ENOMEM; ++ ++ feed->pid = pid; ++ list_add(&feed->next, &filter->feed.ts); ++ ++ if (filter->state >= DMXDEV_STATE_GO) ++ return dvb_dmxdev_start_feed(dmxdev, filter, feed); ++ ++ return 0; ++} ++ ++static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev, ++ struct dmxdev_filter *filter, u16 pid) ++{ ++ struct dmxdev_feed *feed, *tmp; ++ ++ if ((filter->type != DMXDEV_TYPE_PES) || ++ (filter->state < DMXDEV_STATE_SET)) ++ return -EINVAL; ++ ++ list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) { ++ if ((feed->pid == pid) && (feed->ts != NULL)) { ++ feed->ts->stop_filtering(feed->ts); ++ filter->dev->demux->release_ts_feed(filter->dev->demux, ++ feed->ts); ++ list_del(&feed->next); ++ kfree(feed); ++ } ++ } ++ ++ return 0; ++} ++ ++static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, ++ struct dmxdev_filter *dmxdevfilter, ++ struct dmx_sct_filter_params *params) ++{ ++ dprintk("function : %s\n", __func__); ++ ++ dvb_dmxdev_filter_stop(dmxdevfilter); ++ ++ dmxdevfilter->type = DMXDEV_TYPE_SEC; ++ memcpy(&dmxdevfilter->params.sec, ++ params, sizeof(struct dmx_sct_filter_params)); ++ invert_mode(&dmxdevfilter->params.sec.filter); ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); ++ ++ if (params->flags & DMX_IMMEDIATE_START) ++ return dvb_dmxdev_filter_start(dmxdevfilter); ++ ++ return 0; ++} ++ ++static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, ++ struct dmxdev_filter *dmxdevfilter, ++ struct dmx_pes_filter_params *params) ++{ ++ int ret; ++ ++ dvb_dmxdev_filter_stop(dmxdevfilter); ++ dvb_dmxdev_filter_reset(dmxdevfilter); ++ ++ if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) ++ return -EINVAL; ++ ++ dmxdevfilter->type = DMXDEV_TYPE_PES; ++ memcpy(&dmxdevfilter->params, params, ++ sizeof(struct dmx_pes_filter_params)); ++ INIT_LIST_HEAD(&dmxdevfilter->feed.ts); ++ ++ dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); ++ ++ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, ++ dmxdevfilter->params.pes.pid); ++ if (ret < 0) ++ return ret; ++ ++ if (params->flags & DMX_IMMEDIATE_START) ++ return dvb_dmxdev_filter_start(dmxdevfilter); ++ ++ return 0; ++} ++ ++static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, ++ struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int result, hcount; ++ int done = 0; ++ ++ if (dfil->todo <= 0) { ++ hcount = 3 + dfil->todo; ++ if (hcount > count) ++ hcount = count; ++ result = dvb_dmxdev_buffer_read(&dfil->buffer, ++ file->f_flags & O_NONBLOCK, ++ buf, hcount, ppos); ++ if (result < 0) { ++ dfil->todo = 0; ++ return result; ++ } ++ if (copy_from_user(dfil->secheader - dfil->todo, buf, result)) ++ return -EFAULT; ++ buf += result; ++ done = result; ++ count -= result; ++ dfil->todo -= result; ++ if (dfil->todo > -3) ++ return done; ++ dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff; ++ if (!count) ++ return done; ++ } ++ if (count > dfil->todo) ++ count = dfil->todo; ++ result = dvb_dmxdev_buffer_read(&dfil->buffer, ++ file->f_flags & O_NONBLOCK, ++ buf, count, ppos); ++ if (result < 0) ++ return result; ++ dfil->todo -= result; ++ return (result + done); ++} ++ ++static ssize_t ++dvb_demux_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct dmxdev_filter *dmxdevfilter = file->private_data; ++ int ret; ++ ++ if (mutex_lock_interruptible(&dmxdevfilter->mutex)) ++ return -ERESTARTSYS; ++ ++ if (dmxdevfilter->type == DMXDEV_TYPE_SEC) ++ ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos); ++ else ++ ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer, ++ file->f_flags & O_NONBLOCK, ++ buf, count, ppos); ++ ++ mutex_unlock(&dmxdevfilter->mutex); ++ return ret; ++} ++ ++static int dvb_demux_lock_filter(struct dmxdev_filter *dmxdevfilter) ++{ ++ int ret; ++ ++ dmxdevfilter->buffer.do_wait = 0; ++ ++ if (waitqueue_active(&dmxdevfilter->buffer.queue)) ++ wake_up(&dmxdevfilter->buffer.queue); ++ ++ ret = mutex_lock_interruptible(&dmxdevfilter->mutex); ++ ++ dmxdevfilter->buffer.do_wait = 1; ++ ++ return ret; ++} ++ ++static int dvb_demux_do_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dmxdev_filter *dmxdevfilter = file->private_data; ++ struct dmxdev *dmxdev = dmxdevfilter->dev; ++ unsigned long arg = (unsigned long)parg; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&dmxdev->mutex)) ++ return -ERESTARTSYS; ++ ++ switch (cmd) { ++ case DMX_START: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ERESTARTSYS; ++ } ++ if (dmxdevfilter->state < DMXDEV_STATE_SET) ++ ret = -EINVAL; ++ else ++ ret = dvb_dmxdev_filter_start(dmxdevfilter); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_STOP: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ERESTARTSYS; ++ } ++ ret = dvb_dmxdev_filter_stop(dmxdevfilter); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_SET_FILTER: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ERESTARTSYS; ++ } ++ ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_SET_PES_FILTER: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ERESTARTSYS; ++ } ++ ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_SET_BUFFER_SIZE: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ mutex_unlock(&dmxdev->mutex); ++ return -ERESTARTSYS; ++ } ++ ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_GET_PES_PIDS: ++ if (!dmxdev->demux->get_pes_pids) { ++ ret = -EINVAL; ++ break; ++ } ++ dmxdev->demux->get_pes_pids(dmxdev->demux, parg); ++ break; ++ ++ case DMX_GET_CAPS: ++ if (!dmxdev->demux->get_caps) { ++ ret = -EINVAL; ++ break; ++ } ++ ret = dmxdev->demux->get_caps(dmxdev->demux, parg); ++ break; ++ ++ case DMX_SET_SOURCE: ++ if (!dmxdev->demux->set_source) { ++ ret = -EINVAL; ++ break; ++ } ++ ret = dmxdev->demux->set_source(dmxdev->demux, parg); ++ break; ++ ++ case DMX_GET_STC: ++ if (!dmxdev->demux->get_stc) { ++ ret = -EINVAL; ++ break; ++ } ++ ret = dmxdev->demux->get_stc(dmxdev->demux, ++ ((struct dmx_stc *)parg)->num, ++ &((struct dmx_stc *)parg)->stc, ++ &((struct dmx_stc *)parg)->base); ++ break; ++ ++ case DMX_ADD_PID: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ case DMX_REMOVE_PID: ++ if (dvb_demux_lock_filter(dmxdevfilter)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg); ++ mutex_unlock(&dmxdevfilter->mutex); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ mutex_unlock(&dmxdev->mutex); ++ return ret; ++} ++ ++static long dvb_demux_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl); ++} ++ ++static unsigned int dvb_demux_poll(struct file *file, poll_table *wait) ++{ ++ struct dmxdev_filter *dmxdevfilter = file->private_data; ++ unsigned int mask = 0; ++ ++ if (!dmxdevfilter) ++ return -EINVAL; ++ ++ poll_wait(file, &dmxdevfilter->buffer.queue, wait); ++ ++ if (dmxdevfilter->state != DMXDEV_STATE_GO && ++ dmxdevfilter->state != DMXDEV_STATE_DONE && ++ dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) ++ return 0; ++ ++ if (dmxdevfilter->buffer.error) ++ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); ++ ++ if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer)) ++ mask |= (POLLIN | POLLRDNORM | POLLPRI); ++ ++ return mask; ++} ++ ++static int dvb_demux_release(struct inode *inode, struct file *file) ++{ ++ struct dmxdev_filter *dmxdevfilter = file->private_data; ++ struct dmxdev *dmxdev = dmxdevfilter->dev; ++ ++ int ret; ++ ++ ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); ++ ++ mutex_lock(&dmxdev->mutex); ++ dmxdev->dvbdev->users--; ++ if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) { ++ fops_put(file->f_op); ++ file->f_op = NULL; ++ mutex_unlock(&dmxdev->mutex); ++ wake_up(&dmxdev->dvbdev->wait_queue); ++ } else ++ mutex_unlock(&dmxdev->mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations dvb_demux_fops = { ++ .owner = THIS_MODULE, ++ .read = dvb_demux_read, ++ .unlocked_ioctl = dvb_demux_ioctl, ++ .open = dvb_demux_open, ++ .release = dvb_demux_release, ++ .poll = dvb_demux_poll, ++ .llseek = default_llseek, ++}; ++ ++static struct dvb_device dvbdev_demux = { ++ .priv = NULL, ++ .users = 1, ++ .writers = 1, ++ .fops = &dvb_demux_fops ++}; ++ ++static int dvb_dvr_do_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ unsigned long arg = (unsigned long)parg; ++ int ret; ++ ++ if (mutex_lock_interruptible(&dmxdev->mutex)) ++ return -ERESTARTSYS; ++ ++ switch (cmd) { ++ case DMX_SET_BUFFER_SIZE: ++ ret = dvb_dvr_set_buffer_size(dmxdev, arg); ++ break; ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ mutex_unlock(&dmxdev->mutex); ++ return ret; ++} ++ ++static long dvb_dvr_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl); ++} ++ ++static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dmxdev *dmxdev = dvbdev->priv; ++ unsigned int mask = 0; ++ ++ dprintk("function : %s\n", __func__); ++ ++ poll_wait(file, &dmxdev->dvr_buffer.queue, wait); ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ if (dmxdev->dvr_buffer.error) ++ mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); ++ ++ if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer)) ++ mask |= (POLLIN | POLLRDNORM | POLLPRI); ++ } else ++ mask |= (POLLOUT | POLLWRNORM | POLLPRI); ++ ++ return mask; ++} ++ ++static const struct file_operations dvb_dvr_fops = { ++ .owner = THIS_MODULE, ++ .read = dvb_dvr_read, ++ .write = dvb_dvr_write, ++ .unlocked_ioctl = dvb_dvr_ioctl, ++ .open = dvb_dvr_open, ++ .release = dvb_dvr_release, ++ .poll = dvb_dvr_poll, ++ .llseek = default_llseek, ++}; ++ ++static struct dvb_device dvbdev_dvr = { ++ .priv = NULL, ++ .readers = 1, ++ .users = 1, ++ .fops = &dvb_dvr_fops ++}; ++ ++int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) ++{ ++ int i; ++ ++ if (dmxdev->demux->open(dmxdev->demux) < 0) ++ return -EUSERS; ++ ++ dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter)); ++ if (!dmxdev->filter) ++ return -ENOMEM; ++ ++ mutex_init(&dmxdev->mutex); ++ spin_lock_init(&dmxdev->lock); ++ for (i = 0; i < dmxdev->filternum; i++) { ++ dmxdev->filter[i].dev = dmxdev; ++ dmxdev->filter[i].buffer.data = NULL; ++ dvb_dmxdev_filter_state_set(&dmxdev->filter[i], ++ DMXDEV_STATE_FREE); ++ } ++ ++ dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, ++ DVB_DEVICE_DEMUX); ++ dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, ++ dmxdev, DVB_DEVICE_DVR); ++ ++ dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(dvb_dmxdev_init); ++ ++void dvb_dmxdev_release(struct dmxdev *dmxdev) ++{ ++ dmxdev->exit=1; ++ if (dmxdev->dvbdev->users > 1) { ++ wait_event(dmxdev->dvbdev->wait_queue, ++ dmxdev->dvbdev->users==1); ++ } ++ if (dmxdev->dvr_dvbdev->users > 1) { ++ wait_event(dmxdev->dvr_dvbdev->wait_queue, ++ dmxdev->dvr_dvbdev->users==1); ++ } ++ ++ dvb_unregister_device(dmxdev->dvbdev); ++ dvb_unregister_device(dmxdev->dvr_dvbdev); ++ ++ vfree(dmxdev->filter); ++ dmxdev->filter = NULL; ++ dmxdev->demux->close(dmxdev->demux); ++} ++ ++EXPORT_SYMBOL(dvb_dmxdev_release); +diff --git a/drivers/media/dvb-core/dmxdev.h b/drivers/media/dvb-core/dmxdev.h +new file mode 100644 +index 0000000..02ebe28 +--- /dev/null ++++ b/drivers/media/dvb-core/dmxdev.h +@@ -0,0 +1,118 @@ ++/* ++ * dmxdev.h ++ * ++ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _DMXDEV_H_ ++#define _DMXDEV_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dvbdev.h" ++#include "demux.h" ++#include "dvb_ringbuffer.h" ++ ++enum dmxdev_type { ++ DMXDEV_TYPE_NONE, ++ DMXDEV_TYPE_SEC, ++ DMXDEV_TYPE_PES, ++}; ++ ++enum dmxdev_state { ++ DMXDEV_STATE_FREE, ++ DMXDEV_STATE_ALLOCATED, ++ DMXDEV_STATE_SET, ++ DMXDEV_STATE_GO, ++ DMXDEV_STATE_DONE, ++ DMXDEV_STATE_TIMEDOUT ++}; ++ ++struct dmxdev_feed { ++ u16 pid; ++ struct dmx_ts_feed *ts; ++ struct list_head next; ++}; ++ ++struct dmxdev_filter { ++ union { ++ struct dmx_section_filter *sec; ++ } filter; ++ ++ union { ++ /* list of TS and PES feeds (struct dmxdev_feed) */ ++ struct list_head ts; ++ struct dmx_section_feed *sec; ++ } feed; ++ ++ union { ++ struct dmx_sct_filter_params sec; ++ struct dmx_pes_filter_params pes; ++ } params; ++ ++ enum dmxdev_type type; ++ enum dmxdev_state state; ++ struct dmxdev *dev; ++ struct dvb_ringbuffer buffer; ++ ++ struct mutex mutex; ++ ++ /* only for sections */ ++ struct timer_list timer; ++ int todo; ++ u8 secheader[3]; ++}; ++ ++ ++struct dmxdev { ++ struct dvb_device *dvbdev; ++ struct dvb_device *dvr_dvbdev; ++ ++ struct dmxdev_filter *filter; ++ struct dmx_demux *demux; ++ ++ int filternum; ++ int capabilities; ++ ++ unsigned int exit:1; ++#define DMXDEV_CAP_DUPLEX 1 ++ struct dmx_frontend *dvr_orig_fe; ++ ++ struct dvb_ringbuffer dvr_buffer; ++#define DVR_BUFFER_SIZE (10*188*1024) ++ ++ struct mutex mutex; ++ spinlock_t lock; ++}; ++ ++ ++int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *); ++void dvb_dmxdev_release(struct dmxdev *dmxdev); ++ ++#endif /* _DMXDEV_H_ */ +diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h +new file mode 100644 +index 0000000..7e1597d +--- /dev/null ++++ b/drivers/media/dvb-core/dvb-usb-ids.h +@@ -0,0 +1,370 @@ ++/* dvb-usb-ids.h is part of the DVB USB library. ++ * ++ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see ++ * dvb-usb-init.c for copyright information. ++ * ++ * a header file containing define's for the USB device supported by the ++ * various drivers. ++ */ ++#ifndef _DVB_USB_IDS_H_ ++#define _DVB_USB_IDS_H_ ++ ++/* Vendor IDs */ ++#define USB_VID_ADSTECH 0x06e1 ++#define USB_VID_AFATECH 0x15a4 ++#define USB_VID_ALCOR_MICRO 0x058f ++#define USB_VID_ALINK 0x05e3 ++#define USB_VID_AMT 0x1c73 ++#define USB_VID_ANCHOR 0x0547 ++#define USB_VID_ANSONIC 0x10b9 ++#define USB_VID_ANUBIS_ELECTRONIC 0x10fd ++#define USB_VID_ASUS 0x0b05 ++#define USB_VID_AVERMEDIA 0x07ca ++#define USB_VID_COMPRO 0x185b ++#define USB_VID_COMPRO_UNK 0x145f ++#define USB_VID_CONEXANT 0x0572 ++#define USB_VID_CYPRESS 0x04b4 ++#define USB_VID_DEXATEK 0x1d19 ++#define USB_VID_DIBCOM 0x10b8 ++#define USB_VID_DPOSH 0x1498 ++#define USB_VID_DVICO 0x0fe9 ++#define USB_VID_E3C 0x18b4 ++#define USB_VID_ELGATO 0x0fd9 ++#define USB_VID_EMPIA 0xeb1a ++#define USB_VID_GENPIX 0x09c0 ++#define USB_VID_GRANDTEC 0x5032 ++#define USB_VID_GTEK 0x1f4d ++#define USB_VID_HANFTEK 0x15f4 ++#define USB_VID_HAUPPAUGE 0x2040 ++#define USB_VID_HYPER_PALTEK 0x1025 ++#define USB_VID_INTEL 0x8086 ++#define USB_VID_ITETECH 0x048d ++#define USB_VID_KWORLD 0xeb2a ++#define USB_VID_KWORLD_2 0x1b80 ++#define USB_VID_KYE 0x0458 ++#define USB_VID_LEADTEK 0x0413 ++#define USB_VID_LITEON 0x04ca ++#define USB_VID_MEDION 0x1660 ++#define USB_VID_MIGLIA 0x18f3 ++#define USB_VID_MSI 0x0db0 ++#define USB_VID_MSI_2 0x1462 ++#define USB_VID_OPERA1 0x695c ++#define USB_VID_PINNACLE 0x2304 ++#define USB_VID_PCTV 0x2013 ++#define USB_VID_PIXELVIEW 0x1554 ++#define USB_VID_REALTEK 0x0bda ++#define USB_VID_TECHNOTREND 0x0b48 ++#define USB_VID_TERRATEC 0x0ccd ++#define USB_VID_TELESTAR 0x10b9 ++#define USB_VID_VISIONPLUS 0x13d3 ++#define USB_VID_SONY 0x1415 ++#define USB_VID_TWINHAN 0x1822 ++#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 ++#define USB_VID_UNIWILL 0x1584 ++#define USB_VID_WIDEVIEW 0x14aa ++#define USB_VID_GIGABYTE 0x1044 ++#define USB_VID_YUAN 0x1164 ++#define USB_VID_XTENSIONS 0x1ae7 ++#define USB_VID_HUMAX_COEX 0x10b9 ++#define USB_VID_774 0x7a69 ++#define USB_VID_EVOLUTEPC 0x1e59 ++#define USB_VID_AZUREWAVE 0x13d3 ++#define USB_VID_TECHNISAT 0x14f7 ++ ++/* Product IDs */ ++#define USB_PID_ADSTECH_USB2_COLD 0xa333 ++#define USB_PID_ADSTECH_USB2_WARM 0xa334 ++#define USB_PID_AFATECH_AF9005 0x9020 ++#define USB_PID_AFATECH_AF9015_9015 0x9015 ++#define USB_PID_AFATECH_AF9015_9016 0x9016 ++#define USB_PID_AFATECH_AF9035_1000 0x1000 ++#define USB_PID_AFATECH_AF9035_1001 0x1001 ++#define USB_PID_AFATECH_AF9035_1002 0x1002 ++#define USB_PID_AFATECH_AF9035_1003 0x1003 ++#define USB_PID_AFATECH_AF9035_9035 0x9035 ++#define USB_PID_TREKSTOR_DVBT 0x901b ++#define USB_PID_TREKSTOR_TERRES_2_0 0xC803 ++#define USB_VID_ALINK_DTU 0xf170 ++#define USB_PID_ANSONIC_DVBT_USB 0x6000 ++#define USB_PID_ANYSEE 0x861f ++#define USB_PID_AZUREWAVE_AD_TU700 0x3237 ++#define USB_PID_AZUREWAVE_6007 0x0ccd ++#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 ++#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 ++#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 ++#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801 ++#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 ++#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 ++#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c ++#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d ++#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78 ++#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80 ++#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397 ++#define USB_PID_CONEXANT_D680_DMB 0x86d6 ++#define USB_PID_CREATIX_CTX1921 0x1921 ++#define USB_PID_DELOCK_USB2_DVBT 0xb803 ++#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 ++#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 ++#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 ++#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 ++#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 ++#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 ++#define USB_PID_DIBCOM_STK7700P 0x1e14 ++#define USB_PID_DIBCOM_STK7700P_PC 0x1e78 ++#define USB_PID_DIBCOM_STK7700D 0x1ef0 ++#define USB_PID_DIBCOM_STK7700_U7000 0x7001 ++#define USB_PID_DIBCOM_STK7070P 0x1ebc ++#define USB_PID_DIBCOM_STK7070PD 0x1ebe ++#define USB_PID_DIBCOM_STK807XP 0x1f90 ++#define USB_PID_DIBCOM_STK807XPVR 0x1f98 ++#define USB_PID_DIBCOM_STK8096GP 0x1fa0 ++#define USB_PID_DIBCOM_NIM8096MD 0x1fa8 ++#define USB_PID_DIBCOM_TFE8096P 0x1f9C ++#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 ++#define USB_PID_DIBCOM_STK7770P 0x1e80 ++#define USB_PID_DIBCOM_NIM7090 0x1bb2 ++#define USB_PID_DIBCOM_TFE7090PVR 0x1bb4 ++#define USB_PID_DIBCOM_TFE7090E 0x1bb7 ++#define USB_PID_DIBCOM_TFE7790E 0x1e6e ++#define USB_PID_DIBCOM_NIM9090M 0x2383 ++#define USB_PID_DIBCOM_NIM9090MD 0x2384 ++#define USB_PID_DPOSH_M9206_COLD 0x9206 ++#define USB_PID_DPOSH_M9206_WARM 0xa090 ++#define USB_PID_E3C_EC168 0x1689 ++#define USB_PID_E3C_EC168_2 0xfffa ++#define USB_PID_E3C_EC168_3 0xfffb ++#define USB_PID_E3C_EC168_4 0x1001 ++#define USB_PID_E3C_EC168_5 0x1002 ++#define USB_PID_FREECOM_DVBT 0x0160 ++#define USB_PID_FREECOM_DVBT_2 0x0161 ++#define USB_PID_UNIWILL_STK7700P 0x6003 ++#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 ++#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 ++#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 ++#define USB_PID_INTEL_CE9500 0x9500 ++#define USB_PID_ITETECH_IT9135 0x9135 ++#define USB_PID_ITETECH_IT9135_9005 0x9005 ++#define USB_PID_ITETECH_IT9135_9006 0x9006 ++#define USB_PID_KWORLD_399U 0xe399 ++#define USB_PID_KWORLD_399U_2 0xe400 ++#define USB_PID_KWORLD_395U 0xe396 ++#define USB_PID_KWORLD_395U_2 0xe39b ++#define USB_PID_KWORLD_395U_3 0xe395 ++#define USB_PID_KWORLD_395U_4 0xe39a ++#define USB_PID_KWORLD_MC810 0xc810 ++#define USB_PID_KWORLD_PC160_2T 0xc160 ++#define USB_PID_KWORLD_PC160_T 0xc161 ++#define USB_PID_KWORLD_UB383_T 0xe383 ++#define USB_PID_KWORLD_UB499_2T_T09 0xe409 ++#define USB_PID_KWORLD_VSTREAM_COLD 0x17de ++#define USB_PID_KWORLD_VSTREAM_WARM 0x17df ++#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 ++#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 ++#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 ++#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 ++#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 ++#define USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1 0x00a9 ++#define USB_PID_TWINHAN_VP7041_COLD 0x3201 ++#define USB_PID_TWINHAN_VP7041_WARM 0x3202 ++#define USB_PID_TWINHAN_VP7020_COLD 0x3203 ++#define USB_PID_TWINHAN_VP7020_WARM 0x3204 ++#define USB_PID_TWINHAN_VP7045_COLD 0x3205 ++#define USB_PID_TWINHAN_VP7045_WARM 0x3206 ++#define USB_PID_TWINHAN_VP7021_COLD 0x3207 ++#define USB_PID_TWINHAN_VP7021_WARM 0x3208 ++#define USB_PID_TWINHAN_VP7049 0x3219 ++#define USB_PID_TINYTWIN 0x3226 ++#define USB_PID_TINYTWIN_2 0xe402 ++#define USB_PID_TINYTWIN_3 0x9016 ++#define USB_PID_DNTV_TINYUSB2_COLD 0x3223 ++#define USB_PID_DNTV_TINYUSB2_WARM 0x3224 ++#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 ++#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 ++#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 ++#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 ++#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 ++#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 ++#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a ++#define USB_PID_ARTEC_T14_COLD 0x810b ++#define USB_PID_ARTEC_T14_WARM 0x810c ++#define USB_PID_ARTEC_T14BR 0x810f ++#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 ++#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 ++#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e ++#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f ++#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 ++#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 ++#define USB_PID_DTT200U_COLD 0x0201 ++#define USB_PID_DTT200U_WARM 0x0301 ++#define USB_PID_WT220U_ZAP250_COLD 0x0220 ++#define USB_PID_WT220U_COLD 0x0222 ++#define USB_PID_WT220U_WARM 0x0221 ++#define USB_PID_WT220U_FC_COLD 0x0225 ++#define USB_PID_WT220U_FC_WARM 0x0226 ++#define USB_PID_WT220U_ZL0353_COLD 0x022a ++#define USB_PID_WT220U_ZL0353_WARM 0x022b ++#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 ++#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 ++#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941 ++#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 ++#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400 ++#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 ++#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 ++#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070 ++#define USB_PID_HAUPPAUGE_MYTV_T 0x7080 ++#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580 ++#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200 ++#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200 ++#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210 ++#define USB_PID_AVERMEDIA_EXPRESS 0xb568 ++#define USB_PID_AVERMEDIA_VOLAR 0xa807 ++#define USB_PID_AVERMEDIA_VOLAR_2 0xb808 ++#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868 ++#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228 ++#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039 ++#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039 ++#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 ++#define USB_PID_AVERMEDIA_VOLAR_X 0xa815 ++#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150 ++#define USB_PID_AVERMEDIA_A309 0xa309 ++#define USB_PID_AVERMEDIA_A310 0xa310 ++#define USB_PID_AVERMEDIA_A850 0x850a ++#define USB_PID_AVERMEDIA_A850T 0x850b ++#define USB_PID_AVERMEDIA_A805 0xa805 ++#define USB_PID_AVERMEDIA_A815M 0x815a ++#define USB_PID_AVERMEDIA_A835 0xa835 ++#define USB_PID_AVERMEDIA_B835 0xb835 ++#define USB_PID_AVERMEDIA_A835B_1835 0x1835 ++#define USB_PID_AVERMEDIA_A835B_2835 0x2835 ++#define USB_PID_AVERMEDIA_A835B_3835 0x3835 ++#define USB_PID_AVERMEDIA_A835B_4835 0x4835 ++#define USB_PID_AVERMEDIA_1867 0x1867 ++#define USB_PID_AVERMEDIA_A867 0xa867 ++#define USB_PID_AVERMEDIA_TWINSTAR 0x0825 ++#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 ++#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d ++#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a ++#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 ++#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 ++#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060 ++#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062 ++#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078 ++#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab ++#define USB_PID_TERRATEC_H7 0x10b4 ++#define USB_PID_TERRATEC_H7_2 0x10a3 ++#define USB_PID_TERRATEC_T3 0x10a0 ++#define USB_PID_TERRATEC_T5 0x10a1 ++#define USB_PID_NOXON_DAB_STICK 0x00b3 ++#define USB_PID_NOXON_DAB_STICK_REV2 0x00e0 ++#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e ++#define USB_PID_PINNACLE_PCTV2000E 0x022c ++#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228 ++#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229 ++#define USB_PID_PINNACLE_PCTV71E 0x022b ++#define USB_PID_PINNACLE_PCTV72E 0x0236 ++#define USB_PID_PINNACLE_PCTV73E 0x0237 ++#define USB_PID_PINNACLE_PCTV310E 0x3211 ++#define USB_PID_PINNACLE_PCTV801E 0x023a ++#define USB_PID_PINNACLE_PCTV801E_SE 0x023b ++#define USB_PID_PINNACLE_PCTV340E 0x023d ++#define USB_PID_PINNACLE_PCTV340E_SE 0x023e ++#define USB_PID_PINNACLE_PCTV73A 0x0243 ++#define USB_PID_PINNACLE_PCTV73ESE 0x0245 ++#define USB_PID_PINNACLE_PCTV74E 0x0246 ++#define USB_PID_PINNACLE_PCTV282E 0x0248 ++#define USB_PID_PIXELVIEW_SBTVD 0x5010 ++#define USB_PID_PCTV_200E 0x020e ++#define USB_PID_PCTV_400E 0x020f ++#define USB_PID_PCTV_450E 0x0222 ++#define USB_PID_PCTV_452E 0x021f ++#define USB_PID_REALTEK_RTL2831U 0x2831 ++#define USB_PID_REALTEK_RTL2832U 0x2832 ++#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007 ++#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a ++#define USB_PID_NEBULA_DIGITV 0x0201 ++#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 ++#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 ++#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501 ++#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00 ++#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01 ++#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10 ++#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78 ++#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98 ++#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70 ++#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71 ++#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 ++#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 ++#define USB_PID_MEDION_MD95700 0x0932 ++#define USB_PID_MSI_MEGASKY580 0x5580 ++#define USB_PID_MSI_MEGASKY580_55801 0x5581 ++#define USB_PID_KYE_DVB_T_COLD 0x701e ++#define USB_PID_KYE_DVB_T_WARM 0x701f ++#define USB_PID_LITEON_DVB_T_COLD 0xf000 ++#define USB_PID_LITEON_DVB_T_WARM 0xf001 ++#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360 ++#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361 ++#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6 ++#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 ++#define USB_PID_WINFAST_DTV2000DS 0x6a04 ++#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 ++#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 ++#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 ++#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6 ++#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01 ++#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029 ++#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200 ++#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201 ++#define USB_PID_GENPIX_8PSK_REV_2 0x0202 ++#define USB_PID_GENPIX_SKYWALKER_1 0x0203 ++#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204 ++#define USB_PID_GENPIX_SKYWALKER_2 0x0206 ++#define USB_PID_SIGMATEK_DVB_110 0x6610 ++#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 ++#define USB_PID_MSI_DIGIVOX_DUO 0x8801 ++#define USB_PID_OPERA1_COLD 0x2830 ++#define USB_PID_OPERA1_WARM 0x3829 ++#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514 ++#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513 ++#define USB_PID_GIGABYTE_U7000 0x7001 ++#define USB_PID_GIGABYTE_U8000 0x7002 ++#define USB_PID_ASUS_U3000 0x171f ++#define USB_PID_ASUS_U3000H 0x1736 ++#define USB_PID_ASUS_U3100 0x173f ++#define USB_PID_ASUS_U3100MINI_PLUS 0x1779 ++#define USB_PID_YUAN_EC372S 0x1edc ++#define USB_PID_YUAN_STK7700PH 0x1f08 ++#define USB_PID_YUAN_PD378S 0x2edc ++#define USB_PID_YUAN_MC770 0x0871 ++#define USB_PID_YUAN_STK7700D 0x1efc ++#define USB_PID_YUAN_STK7700D_2 0x1e8c ++#define USB_PID_DW2102 0x2102 ++#define USB_PID_XTENSIONS_XD_380 0x0381 ++#define USB_PID_TELESTAR_STARSTICK_2 0x8000 ++#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 ++#define USB_PID_SONY_PLAYTV 0x0003 ++#define USB_PID_MYGICA_D689 0xd811 ++#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 ++#define USB_PID_ELGATO_EYETV_DTT 0x0021 ++#define USB_PID_ELGATO_EYETV_DTT_2 0x003f ++#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 ++#define USB_PID_ELGATO_EYETV_SAT 0x002a ++#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 ++#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 ++#define USB_PID_FRIIO_WHITE 0x0001 ++#define USB_PID_TVWAY_PLUS 0x0002 ++#define USB_PID_SVEON_STV20 0xe39d ++#define USB_PID_SVEON_STV22 0xe401 ++#define USB_PID_SVEON_STV22_IT9137 0xe411 ++#define USB_PID_AZUREWAVE_AZ6027 0x3275 ++#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4 ++#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac ++#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 ++#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 ++#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004 ++#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 ++#endif +diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c +new file mode 100644 +index 0000000..9be65a3 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_ca_en50221.c +@@ -0,0 +1,1753 @@ ++/* ++ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces ++ * ++ * Copyright (C) 2004 Andrew de Quincey ++ * ++ * Parts of this file were based on sources as follows: ++ * ++ * Copyright (C) 2003 Ralph Metzler ++ * ++ * based on code: ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_ca_en50221.h" ++#include "dvb_ringbuffer.h" ++ ++static int dvb_ca_en50221_debug; ++ ++module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644); ++MODULE_PARM_DESC(cam_debug, "enable verbose debug messages"); ++ ++#define dprintk if (dvb_ca_en50221_debug) printk ++ ++#define INIT_TIMEOUT_SECS 10 ++ ++#define HOST_LINK_BUF_SIZE 0x200 ++ ++#define RX_BUFFER_SIZE 65535 ++ ++#define MAX_RX_PACKETS_PER_ITERATION 10 ++ ++#define CTRLIF_DATA 0 ++#define CTRLIF_COMMAND 1 ++#define CTRLIF_STATUS 1 ++#define CTRLIF_SIZE_LOW 2 ++#define CTRLIF_SIZE_HIGH 3 ++ ++#define CMDREG_HC 1 /* Host control */ ++#define CMDREG_SW 2 /* Size write */ ++#define CMDREG_SR 4 /* Size read */ ++#define CMDREG_RS 8 /* Reset interface */ ++#define CMDREG_FRIE 0x40 /* Enable FR interrupt */ ++#define CMDREG_DAIE 0x80 /* Enable DA interrupt */ ++#define IRQEN (CMDREG_DAIE) ++ ++#define STATUSREG_RE 1 /* read error */ ++#define STATUSREG_WE 2 /* write error */ ++#define STATUSREG_FR 0x40 /* module free */ ++#define STATUSREG_DA 0x80 /* data available */ ++#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */ ++ ++ ++#define DVB_CA_SLOTSTATE_NONE 0 ++#define DVB_CA_SLOTSTATE_UNINITIALISED 1 ++#define DVB_CA_SLOTSTATE_RUNNING 2 ++#define DVB_CA_SLOTSTATE_INVALID 3 ++#define DVB_CA_SLOTSTATE_WAITREADY 4 ++#define DVB_CA_SLOTSTATE_VALIDATE 5 ++#define DVB_CA_SLOTSTATE_WAITFR 6 ++#define DVB_CA_SLOTSTATE_LINKINIT 7 ++ ++ ++/* Information on a CA slot */ ++struct dvb_ca_slot { ++ ++ /* current state of the CAM */ ++ int slot_state; ++ ++ /* mutex used for serializing access to one CI slot */ ++ struct mutex slot_lock; ++ ++ /* Number of CAMCHANGES that have occurred since last processing */ ++ atomic_t camchange_count; ++ ++ /* Type of last CAMCHANGE */ ++ int camchange_type; ++ ++ /* base address of CAM config */ ++ u32 config_base; ++ ++ /* value to write into Config Control register */ ++ u8 config_option; ++ ++ /* if 1, the CAM supports DA IRQs */ ++ u8 da_irq_supported:1; ++ ++ /* size of the buffer to use when talking to the CAM */ ++ int link_buf_size; ++ ++ /* buffer for incoming packets */ ++ struct dvb_ringbuffer rx_buffer; ++ ++ /* timer used during various states of the slot */ ++ unsigned long timeout; ++}; ++ ++/* Private CA-interface information */ ++struct dvb_ca_private { ++ ++ /* pointer back to the public data structure */ ++ struct dvb_ca_en50221 *pub; ++ ++ /* the DVB device */ ++ struct dvb_device *dvbdev; ++ ++ /* Flags describing the interface (DVB_CA_FLAG_*) */ ++ u32 flags; ++ ++ /* number of slots supported by this CA interface */ ++ unsigned int slot_count; ++ ++ /* information on each slot */ ++ struct dvb_ca_slot *slot_info; ++ ++ /* wait queues for read() and write() operations */ ++ wait_queue_head_t wait_queue; ++ ++ /* PID of the monitoring thread */ ++ struct task_struct *thread; ++ ++ /* Flag indicating if the CA device is open */ ++ unsigned int open:1; ++ ++ /* Flag indicating the thread should wake up now */ ++ unsigned int wakeup:1; ++ ++ /* Delay the main thread should use */ ++ unsigned long delay; ++ ++ /* Slot to start looking for data to read from in the next user-space read operation */ ++ int next_read_slot; ++}; ++ ++static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); ++static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); ++static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); ++ ++ ++/** ++ * Safely find needle in haystack. ++ * ++ * @param haystack Buffer to look in. ++ * @param hlen Number of bytes in haystack. ++ * @param needle Buffer to find. ++ * @param nlen Number of bytes in needle. ++ * @return Pointer into haystack needle was found at, or NULL if not found. ++ */ ++static char *findstr(char * haystack, int hlen, char * needle, int nlen) ++{ ++ int i; ++ ++ if (hlen < nlen) ++ return NULL; ++ ++ for (i = 0; i <= hlen - nlen; i++) { ++ if (!strncmp(haystack + i, needle, nlen)) ++ return haystack + i; ++ } ++ ++ return NULL; ++} ++ ++ ++ ++/* ******************************************************************************** */ ++/* EN50221 physical interface functions */ ++ ++ ++/** ++ * Check CAM status. ++ */ ++static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) ++{ ++ int slot_status; ++ int cam_present_now; ++ int cam_changed; ++ ++ /* IRQ mode */ ++ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) { ++ return (atomic_read(&ca->slot_info[slot].camchange_count) != 0); ++ } ++ ++ /* poll mode */ ++ slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open); ++ ++ cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0; ++ cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0; ++ if (!cam_changed) { ++ int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE); ++ cam_changed = (cam_present_now != cam_present_old); ++ } ++ ++ if (cam_changed) { ++ if (!cam_present_now) { ++ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; ++ } else { ++ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED; ++ } ++ atomic_set(&ca->slot_info[slot].camchange_count, 1); ++ } else { ++ if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) && ++ (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) { ++ // move to validate state if reset is completed ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE; ++ } ++ } ++ ++ return cam_changed; ++} ++ ++ ++/** ++ * Wait for flags to become set on the STATUS register on a CAM interface, ++ * checking for errors and timeout. ++ * ++ * @param ca CA instance. ++ * @param slot Slot on interface. ++ * @param waitfor Flags to wait for. ++ * @param timeout_ms Timeout in milliseconds. ++ * ++ * @return 0 on success, nonzero on error. ++ */ ++static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, ++ u8 waitfor, int timeout_hz) ++{ ++ unsigned long timeout; ++ unsigned long start; ++ ++ dprintk("%s\n", __func__); ++ ++ /* loop until timeout elapsed */ ++ start = jiffies; ++ timeout = jiffies + timeout_hz; ++ while (1) { ++ /* read the status and check for error */ ++ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); ++ if (res < 0) ++ return -EIO; ++ ++ /* if we got the flags, it was successful! */ ++ if (res & waitfor) { ++ dprintk("%s succeeded timeout:%lu\n", __func__, jiffies - start); ++ return 0; ++ } ++ ++ /* check for timeout */ ++ if (time_after(jiffies, timeout)) { ++ break; ++ } ++ ++ /* wait for a bit */ ++ msleep(1); ++ } ++ ++ dprintk("%s failed timeout:%lu\n", __func__, jiffies - start); ++ ++ /* if we get here, we've timed out */ ++ return -ETIMEDOUT; ++} ++ ++ ++/** ++ * Initialise the link layer connection to a CAM. ++ * ++ * @param ca CA instance. ++ * @param slot Slot id. ++ * ++ * @return 0 on success, nonzero on failure. ++ */ ++static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) ++{ ++ int ret; ++ int buf_size; ++ u8 buf[2]; ++ ++ dprintk("%s\n", __func__); ++ ++ /* we'll be determining these during this function */ ++ ca->slot_info[slot].da_irq_supported = 0; ++ ++ /* set the host link buffer size temporarily. it will be overwritten with the ++ * real negotiated size later. */ ++ ca->slot_info[slot].link_buf_size = 2; ++ ++ /* read the buffer size from the CAM */ ++ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) ++ return ret; ++ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0) ++ return ret; ++ if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) ++ return -EIO; ++ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) ++ return ret; ++ ++ /* store it, and choose the minimum of our buffer and the CAM's buffer size */ ++ buf_size = (buf[0] << 8) | buf[1]; ++ if (buf_size > HOST_LINK_BUF_SIZE) ++ buf_size = HOST_LINK_BUF_SIZE; ++ ca->slot_info[slot].link_buf_size = buf_size; ++ buf[0] = buf_size >> 8; ++ buf[1] = buf_size & 0xff; ++ dprintk("Chosen link buffer size of %i\n", buf_size); ++ ++ /* write the buffer size to the CAM */ ++ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) ++ return ret; ++ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0) ++ return ret; ++ if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) ++ return -EIO; ++ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) ++ return ret; ++ ++ /* success */ ++ return 0; ++} ++ ++/** ++ * Read a tuple from attribute memory. ++ * ++ * @param ca CA instance. ++ * @param slot Slot id. ++ * @param address Address to read from. Updated. ++ * @param tupleType Tuple id byte. Updated. ++ * @param tupleLength Tuple length. Updated. ++ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. ++ * ++ * @return 0 on success, nonzero on error. ++ */ ++static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, ++ int *address, int *tupleType, int *tupleLength, u8 * tuple) ++{ ++ int i; ++ int _tupleType; ++ int _tupleLength; ++ int _address = *address; ++ ++ /* grab the next tuple length and type */ ++ if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) ++ return _tupleType; ++ if (_tupleType == 0xff) { ++ dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType); ++ *address += 2; ++ *tupleType = _tupleType; ++ *tupleLength = 0; ++ return 0; ++ } ++ if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0) ++ return _tupleLength; ++ _address += 4; ++ ++ dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength); ++ ++ /* read in the whole tuple */ ++ for (i = 0; i < _tupleLength; i++) { ++ tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2)); ++ dprintk(" 0x%02x: 0x%02x %c\n", ++ i, tuple[i] & 0xff, ++ ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); ++ } ++ _address += (_tupleLength * 2); ++ ++ // success ++ *tupleType = _tupleType; ++ *tupleLength = _tupleLength; ++ *address = _address; ++ return 0; ++} ++ ++ ++/** ++ * Parse attribute memory of a CAM module, extracting Config register, and checking ++ * it is a DVB CAM module. ++ * ++ * @param ca CA instance. ++ * @param slot Slot id. ++ * ++ * @return 0 on success, <0 on failure. ++ */ ++static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot) ++{ ++ int address = 0; ++ int tupleLength; ++ int tupleType; ++ u8 tuple[257]; ++ char *dvb_str; ++ int rasz; ++ int status; ++ int got_cftableentry = 0; ++ int end_chain = 0; ++ int i; ++ u16 manfid = 0; ++ u16 devid = 0; ++ ++ ++ // CISTPL_DEVICE_0A ++ if ((status = ++ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) ++ return status; ++ if (tupleType != 0x1D) ++ return -EINVAL; ++ ++ ++ ++ // CISTPL_DEVICE_0C ++ if ((status = ++ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) ++ return status; ++ if (tupleType != 0x1C) ++ return -EINVAL; ++ ++ ++ ++ // CISTPL_VERS_1 ++ if ((status = ++ dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) ++ return status; ++ if (tupleType != 0x15) ++ return -EINVAL; ++ ++ ++ ++ // CISTPL_MANFID ++ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, ++ &tupleLength, tuple)) < 0) ++ return status; ++ if (tupleType != 0x20) ++ return -EINVAL; ++ if (tupleLength != 4) ++ return -EINVAL; ++ manfid = (tuple[1] << 8) | tuple[0]; ++ devid = (tuple[3] << 8) | tuple[2]; ++ ++ ++ ++ // CISTPL_CONFIG ++ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, ++ &tupleLength, tuple)) < 0) ++ return status; ++ if (tupleType != 0x1A) ++ return -EINVAL; ++ if (tupleLength < 3) ++ return -EINVAL; ++ ++ /* extract the configbase */ ++ rasz = tuple[0] & 3; ++ if (tupleLength < (3 + rasz + 14)) ++ return -EINVAL; ++ ca->slot_info[slot].config_base = 0; ++ for (i = 0; i < rasz + 1; i++) { ++ ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i)); ++ } ++ ++ /* check it contains the correct DVB string */ ++ dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8); ++ if (dvb_str == NULL) ++ return -EINVAL; ++ if (tupleLength < ((dvb_str - (char *) tuple) + 12)) ++ return -EINVAL; ++ ++ /* is it a version we support? */ ++ if (strncmp(dvb_str + 8, "1.00", 4)) { ++ printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n", ++ ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]); ++ return -EINVAL; ++ } ++ ++ /* process the CFTABLE_ENTRY tuples, and any after those */ ++ while ((!end_chain) && (address < 0x1000)) { ++ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, ++ &tupleLength, tuple)) < 0) ++ return status; ++ switch (tupleType) { ++ case 0x1B: // CISTPL_CFTABLE_ENTRY ++ if (tupleLength < (2 + 11 + 17)) ++ break; ++ ++ /* if we've already parsed one, just use it */ ++ if (got_cftableentry) ++ break; ++ ++ /* get the config option */ ++ ca->slot_info[slot].config_option = tuple[0] & 0x3f; ++ ++ /* OK, check it contains the correct strings */ ++ if ((findstr((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) || ++ (findstr((char *)tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) ++ break; ++ ++ got_cftableentry = 1; ++ break; ++ ++ case 0x14: // CISTPL_NO_LINK ++ break; ++ ++ case 0xFF: // CISTPL_END ++ end_chain = 1; ++ break; ++ ++ default: /* Unknown tuple type - just skip this tuple and move to the next one */ ++ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, ++ tupleLength); ++ break; ++ } ++ } ++ ++ if ((address > 0x1000) || (!got_cftableentry)) ++ return -EINVAL; ++ ++ dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n", ++ manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); ++ ++ // success! ++ return 0; ++} ++ ++ ++/** ++ * Set CAM's configoption correctly. ++ * ++ * @param ca CA instance. ++ * @param slot Slot containing the CAM. ++ */ ++static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) ++{ ++ int configoption; ++ ++ dprintk("%s\n", __func__); ++ ++ /* set the config option */ ++ ca->pub->write_attribute_mem(ca->pub, slot, ++ ca->slot_info[slot].config_base, ++ ca->slot_info[slot].config_option); ++ ++ /* check it */ ++ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base); ++ dprintk("Set configoption 0x%x, read configoption 0x%x\n", ++ ca->slot_info[slot].config_option, configoption & 0x3f); ++ ++ /* fine! */ ++ return 0; ++ ++} ++ ++ ++/** ++ * This function talks to an EN50221 CAM control interface. It reads a buffer of ++ * data from the CAM. The data can either be stored in a supplied buffer, or ++ * automatically be added to the slot's rx_buffer. ++ * ++ * @param ca CA instance. ++ * @param slot Slot to read from. ++ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL, ++ * the data will be added into the buffering system as a normal fragment. ++ * @param ecount Size of ebuf. Ignored if ebuf is NULL. ++ * ++ * @return Number of bytes read, or < 0 on error ++ */ ++static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount) ++{ ++ int bytes_read; ++ int status; ++ u8 buf[HOST_LINK_BUF_SIZE]; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ /* check if we have space for a link buf in the rx_buffer */ ++ if (ebuf == NULL) { ++ int buf_free; ++ ++ if (ca->slot_info[slot].rx_buffer.data == NULL) { ++ status = -EIO; ++ goto exit; ++ } ++ buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer); ++ ++ if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) { ++ status = -EAGAIN; ++ goto exit; ++ } ++ } ++ ++ /* check if there is data available */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) ++ goto exit; ++ if (!(status & STATUSREG_DA)) { ++ /* no data */ ++ status = 0; ++ goto exit; ++ } ++ ++ /* read the amount of data */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) ++ goto exit; ++ bytes_read = status << 8; ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) ++ goto exit; ++ bytes_read |= status; ++ ++ /* check it will fit */ ++ if (ebuf == NULL) { ++ if (bytes_read > ca->slot_info[slot].link_buf_size) { ++ printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", ++ ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ++ status = -EIO; ++ goto exit; ++ } ++ if (bytes_read < 2) { ++ printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ++ status = -EIO; ++ goto exit; ++ } ++ } else { ++ if (bytes_read > ecount) { ++ printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", ++ ca->dvbdev->adapter->num); ++ status = -EIO; ++ goto exit; ++ } ++ } ++ ++ /* fill the buffer */ ++ for (i = 0; i < bytes_read; i++) { ++ /* read byte and check */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) ++ goto exit; ++ ++ /* OK, store it in the buffer */ ++ buf[i] = status; ++ } ++ ++ /* check for read error (RE should now be 0) */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) ++ goto exit; ++ if (status & STATUSREG_RE) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ++ status = -EIO; ++ goto exit; ++ } ++ ++ /* OK, add it to the receive buffer, or copy into external buffer if supplied */ ++ if (ebuf == NULL) { ++ if (ca->slot_info[slot].rx_buffer.data == NULL) { ++ status = -EIO; ++ goto exit; ++ } ++ dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read); ++ } else { ++ memcpy(ebuf, buf, bytes_read); ++ } ++ ++ dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, ++ buf[0], (buf[1] & 0x80) == 0, bytes_read); ++ ++ /* wake up readers when a last_fragment is received */ ++ if ((buf[1] & 0x80) == 0x00) { ++ wake_up_interruptible(&ca->wait_queue); ++ } ++ status = bytes_read; ++ ++exit: ++ return status; ++} ++ ++ ++/** ++ * This function talks to an EN50221 CAM control interface. It writes a buffer of data ++ * to a CAM. ++ * ++ * @param ca CA instance. ++ * @param slot Slot to write to. ++ * @param ebuf The data in this buffer is treated as a complete link-level packet to ++ * be written. ++ * @param count Size of ebuf. ++ * ++ * @return Number of bytes written, or < 0 on error. ++ */ ++static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write) ++{ ++ int status; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ ++ /* sanity check */ ++ if (bytes_write > ca->slot_info[slot].link_buf_size) ++ return -EINVAL; ++ ++ /* it is possible we are dealing with a single buffer implementation, ++ thus if there is data available for read or if there is even a read ++ already in progress, we do nothing but awake the kernel thread to ++ process the data if necessary. */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) ++ goto exitnowrite; ++ if (status & (STATUSREG_DA | STATUSREG_RE)) { ++ if (status & STATUSREG_DA) ++ dvb_ca_en50221_thread_wakeup(ca); ++ ++ status = -EAGAIN; ++ goto exitnowrite; ++ } ++ ++ /* OK, set HC bit */ ++ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, ++ IRQEN | CMDREG_HC)) != 0) ++ goto exit; ++ ++ /* check if interface is still free */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) ++ goto exit; ++ if (!(status & STATUSREG_FR)) { ++ /* it wasn't free => try again later */ ++ status = -EAGAIN; ++ goto exit; ++ } ++ ++ /* send the amount of data */ ++ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) ++ goto exit; ++ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, ++ bytes_write & 0xff)) != 0) ++ goto exit; ++ ++ /* send the buffer */ ++ for (i = 0; i < bytes_write; i++) { ++ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) ++ goto exit; ++ } ++ ++ /* check for write error (WE should now be 0) */ ++ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) ++ goto exit; ++ if (status & STATUSREG_WE) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ++ status = -EIO; ++ goto exit; ++ } ++ status = bytes_write; ++ ++ dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, ++ buf[0], (buf[1] & 0x80) == 0, bytes_write); ++ ++exit: ++ ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); ++ ++exitnowrite: ++ return status; ++} ++EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); ++ ++ ++ ++/* ******************************************************************************** */ ++/* EN50221 higher level functions */ ++ ++ ++/** ++ * A CAM has been removed => shut it down. ++ * ++ * @param ca CA instance. ++ * @param slot Slot to shut down. ++ */ ++static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot) ++{ ++ dprintk("%s\n", __func__); ++ ++ ca->pub->slot_shutdown(ca->pub, slot); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; ++ ++ /* need to wake up all processes to check if they're now ++ trying to write to a defunct CAM */ ++ wake_up_interruptible(&ca->wait_queue); ++ ++ dprintk("Slot %i shutdown\n", slot); ++ ++ /* success */ ++ return 0; ++} ++EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); ++ ++ ++/** ++ * A CAMCHANGE IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ * @param change_type One of the DVB_CA_CAMCHANGE_* values. ++ */ ++void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type) ++{ ++ struct dvb_ca_private *ca = pubca->private; ++ ++ dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type); ++ ++ switch (change_type) { ++ case DVB_CA_EN50221_CAMCHANGE_REMOVED: ++ case DVB_CA_EN50221_CAMCHANGE_INSERTED: ++ break; ++ ++ default: ++ return; ++ } ++ ++ ca->slot_info[slot].camchange_type = change_type; ++ atomic_inc(&ca->slot_info[slot].camchange_count); ++ dvb_ca_en50221_thread_wakeup(ca); ++} ++EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); ++ ++ ++/** ++ * A CAMREADY IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ */ ++void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) ++{ ++ struct dvb_ca_private *ca = pubca->private; ++ ++ dprintk("CAMREADY IRQ slot:%i\n", slot); ++ ++ if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE; ++ dvb_ca_en50221_thread_wakeup(ca); ++ } ++} ++ ++ ++/** ++ * An FR or DA IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ */ ++void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) ++{ ++ struct dvb_ca_private *ca = pubca->private; ++ int flags; ++ ++ dprintk("FR/DA IRQ slot:%i\n", slot); ++ ++ switch (ca->slot_info[slot].slot_state) { ++ case DVB_CA_SLOTSTATE_LINKINIT: ++ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS); ++ if (flags & STATUSREG_DA) { ++ dprintk("CAM supports DA IRQ\n"); ++ ca->slot_info[slot].da_irq_supported = 1; ++ } ++ break; ++ ++ case DVB_CA_SLOTSTATE_RUNNING: ++ if (ca->open) ++ dvb_ca_en50221_thread_wakeup(ca); ++ break; ++ } ++} ++ ++ ++ ++/* ******************************************************************************** */ ++/* EN50221 thread functions */ ++ ++/** ++ * Wake up the DVB CA thread ++ * ++ * @param ca CA instance. ++ */ ++static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) ++{ ++ ++ dprintk("%s\n", __func__); ++ ++ ca->wakeup = 1; ++ mb(); ++ wake_up_process(ca->thread); ++} ++ ++/** ++ * Update the delay used by the thread. ++ * ++ * @param ca CA instance. ++ */ ++static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) ++{ ++ int delay; ++ int curdelay = 100000000; ++ int slot; ++ ++ /* Beware of too high polling frequency, because one polling ++ * call might take several hundred milliseconds until timeout! ++ */ ++ for (slot = 0; slot < ca->slot_count; slot++) { ++ switch (ca->slot_info[slot].slot_state) { ++ default: ++ case DVB_CA_SLOTSTATE_NONE: ++ delay = HZ * 60; /* 60s */ ++ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) ++ delay = HZ * 5; /* 5s */ ++ break; ++ case DVB_CA_SLOTSTATE_INVALID: ++ delay = HZ * 60; /* 60s */ ++ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) ++ delay = HZ / 10; /* 100ms */ ++ break; ++ ++ case DVB_CA_SLOTSTATE_UNINITIALISED: ++ case DVB_CA_SLOTSTATE_WAITREADY: ++ case DVB_CA_SLOTSTATE_VALIDATE: ++ case DVB_CA_SLOTSTATE_WAITFR: ++ case DVB_CA_SLOTSTATE_LINKINIT: ++ delay = HZ / 10; /* 100ms */ ++ break; ++ ++ case DVB_CA_SLOTSTATE_RUNNING: ++ delay = HZ * 60; /* 60s */ ++ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) ++ delay = HZ / 10; /* 100ms */ ++ if (ca->open) { ++ if ((!ca->slot_info[slot].da_irq_supported) || ++ (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) ++ delay = HZ / 10; /* 100ms */ ++ } ++ break; ++ } ++ ++ if (delay < curdelay) ++ curdelay = delay; ++ } ++ ++ ca->delay = curdelay; ++} ++ ++ ++ ++/** ++ * Kernel thread which monitors CA slots for CAM changes, and performs data transfers. ++ */ ++static int dvb_ca_en50221_thread(void *data) ++{ ++ struct dvb_ca_private *ca = data; ++ int slot; ++ int flags; ++ int status; ++ int pktcount; ++ void *rxbuf; ++ ++ dprintk("%s\n", __func__); ++ ++ /* choose the correct initial delay */ ++ dvb_ca_en50221_thread_update_delay(ca); ++ ++ /* main loop */ ++ while (!kthread_should_stop()) { ++ /* sleep for a bit */ ++ if (!ca->wakeup) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(ca->delay); ++ if (kthread_should_stop()) ++ return 0; ++ } ++ ca->wakeup = 0; ++ ++ /* go through all the slots processing them */ ++ for (slot = 0; slot < ca->slot_count; slot++) { ++ ++ mutex_lock(&ca->slot_info[slot].slot_lock); ++ ++ // check the cam status + deal with CAMCHANGEs ++ while (dvb_ca_en50221_check_camstatus(ca, slot)) { ++ /* clear down an old CI slot if necessary */ ++ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) ++ dvb_ca_en50221_slot_shutdown(ca, slot); ++ ++ /* if a CAM is NOW present, initialise it */ ++ if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED; ++ } ++ ++ /* we've handled one CAMCHANGE */ ++ dvb_ca_en50221_thread_update_delay(ca); ++ atomic_dec(&ca->slot_info[slot].camchange_count); ++ } ++ ++ // CAM state machine ++ switch (ca->slot_info[slot].slot_state) { ++ case DVB_CA_SLOTSTATE_NONE: ++ case DVB_CA_SLOTSTATE_INVALID: ++ // no action needed ++ break; ++ ++ case DVB_CA_SLOTSTATE_UNINITIALISED: ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY; ++ ca->pub->slot_reset(ca->pub, slot); ++ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ); ++ break; ++ ++ case DVB_CA_SLOTSTATE_WAITREADY: ++ if (time_after(jiffies, ca->slot_info[slot].timeout)) { ++ printk("dvb_ca adaptor %d: PC card did not respond :(\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ // no other action needed; will automatically change state when ready ++ break; ++ ++ case DVB_CA_SLOTSTATE_VALIDATE: ++ if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { ++ /* we need this extra check for annoying interfaces like the budget-av */ ++ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && ++ (ca->pub->poll_slot_status)) { ++ status = ca->pub->poll_slot_status(ca->pub, slot, 0); ++ if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ } ++ ++ printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ if (dvb_ca_en50221_set_configoption(ca, slot) != 0) { ++ printk("dvb_ca adapter %d: Unable to initialise CAM :(\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ if (ca->pub->write_cam_control(ca->pub, slot, ++ CTRLIF_COMMAND, CMDREG_RS) != 0) { ++ printk("dvb_ca adapter %d: Unable to reset CAM IF\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ dprintk("DVB CAM validated successfully\n"); ++ ++ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR; ++ ca->wakeup = 1; ++ break; ++ ++ case DVB_CA_SLOTSTATE_WAITFR: ++ if (time_after(jiffies, ca->slot_info[slot].timeout)) { ++ printk("dvb_ca adapter %d: DVB CAM did not respond :(\n", ++ ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ ++ flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); ++ if (flags & STATUSREG_FR) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; ++ ca->wakeup = 1; ++ } ++ break; ++ ++ case DVB_CA_SLOTSTATE_LINKINIT: ++ if (dvb_ca_en50221_link_init(ca, slot) != 0) { ++ /* we need this extra check for annoying interfaces like the budget-av */ ++ if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && ++ (ca->pub->poll_slot_status)) { ++ status = ca->pub->poll_slot_status(ca->pub, slot, 0); ++ if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ } ++ ++ printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ ++ if (ca->slot_info[slot].rx_buffer.data == NULL) { ++ rxbuf = vmalloc(RX_BUFFER_SIZE); ++ if (rxbuf == NULL) { ++ printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; ++ dvb_ca_en50221_thread_update_delay(ca); ++ break; ++ } ++ dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE); ++ } ++ ++ ca->pub->slot_ts_enable(ca->pub, slot); ++ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING; ++ dvb_ca_en50221_thread_update_delay(ca); ++ printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num); ++ break; ++ ++ case DVB_CA_SLOTSTATE_RUNNING: ++ if (!ca->open) ++ break; ++ ++ // poll slots for data ++ pktcount = 0; ++ while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) { ++ if (!ca->open) ++ break; ++ ++ /* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */ ++ if (dvb_ca_en50221_check_camstatus(ca, slot)) { ++ // we dont want to sleep on the next iteration so we can handle the cam change ++ ca->wakeup = 1; ++ break; ++ } ++ ++ /* check if we've hit our limit this time */ ++ if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) { ++ // dont sleep; there is likely to be more data to read ++ ca->wakeup = 1; ++ break; ++ } ++ } ++ break; ++ } ++ ++ mutex_unlock(&ca->slot_info[slot].slot_lock); ++ } ++ } ++ ++ return 0; ++} ++ ++ ++ ++/* ******************************************************************************** */ ++/* EN50221 IO interface functions */ ++ ++/** ++ * Real ioctl implementation. ++ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them. ++ * ++ * @param inode Inode concerned. ++ * @param file File concerned. ++ * @param cmd IOCTL command. ++ * @param arg Associated argument. ++ * ++ * @return 0 on success, <0 on error. ++ */ ++static int dvb_ca_en50221_io_do_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ int err = 0; ++ int slot; ++ ++ dprintk("%s\n", __func__); ++ ++ switch (cmd) { ++ case CA_RESET: ++ for (slot = 0; slot < ca->slot_count; slot++) { ++ mutex_lock(&ca->slot_info[slot].slot_lock); ++ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) { ++ dvb_ca_en50221_slot_shutdown(ca, slot); ++ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) ++ dvb_ca_en50221_camchange_irq(ca->pub, ++ slot, ++ DVB_CA_EN50221_CAMCHANGE_INSERTED); ++ } ++ mutex_unlock(&ca->slot_info[slot].slot_lock); ++ } ++ ca->next_read_slot = 0; ++ dvb_ca_en50221_thread_wakeup(ca); ++ break; ++ ++ case CA_GET_CAP: { ++ struct ca_caps *caps = parg; ++ ++ caps->slot_num = ca->slot_count; ++ caps->slot_type = CA_CI_LINK; ++ caps->descr_num = 0; ++ caps->descr_type = 0; ++ break; ++ } ++ ++ case CA_GET_SLOT_INFO: { ++ struct ca_slot_info *info = parg; ++ ++ if ((info->num > ca->slot_count) || (info->num < 0)) ++ return -EINVAL; ++ ++ info->type = CA_CI_LINK; ++ info->flags = 0; ++ if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) ++ && (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) { ++ info->flags = CA_CI_MODULE_PRESENT; ++ } ++ if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) { ++ info->flags |= CA_CI_MODULE_READY; ++ } ++ break; ++ } ++ ++ default: ++ err = -EINVAL; ++ break; ++ } ++ ++ return err; ++} ++ ++ ++/** ++ * Wrapper for ioctl implementation. ++ * ++ * @param inode Inode concerned. ++ * @param file File concerned. ++ * @param cmd IOCTL command. ++ * @param arg Associated argument. ++ * ++ * @return 0 on success, <0 on error. ++ */ ++static long dvb_ca_en50221_io_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl); ++} ++ ++ ++/** ++ * Implementation of write() syscall. ++ * ++ * @param file File structure. ++ * @param buf Source buffer. ++ * @param count Size of source buffer. ++ * @param ppos Position in file (ignored). ++ * ++ * @return Number of bytes read, or <0 on error. ++ */ ++static ssize_t dvb_ca_en50221_io_write(struct file *file, ++ const char __user * buf, size_t count, loff_t * ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ u8 slot, connection_id; ++ int status; ++ u8 fragbuf[HOST_LINK_BUF_SIZE]; ++ int fragpos = 0; ++ int fraglen; ++ unsigned long timeout; ++ int written; ++ ++ dprintk("%s\n", __func__); ++ ++ /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ ++ if (count < 2) ++ return -EINVAL; ++ ++ /* extract slot & connection id */ ++ if (copy_from_user(&slot, buf, 1)) ++ return -EFAULT; ++ if (copy_from_user(&connection_id, buf + 1, 1)) ++ return -EFAULT; ++ buf += 2; ++ count -= 2; ++ ++ /* check if the slot is actually running */ ++ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) ++ return -EINVAL; ++ ++ /* fragment the packets & store in the buffer */ ++ while (fragpos < count) { ++ fraglen = ca->slot_info[slot].link_buf_size - 2; ++ if (fraglen < 0) ++ break; ++ if (fraglen > HOST_LINK_BUF_SIZE - 2) ++ fraglen = HOST_LINK_BUF_SIZE - 2; ++ if ((count - fragpos) < fraglen) ++ fraglen = count - fragpos; ++ ++ fragbuf[0] = connection_id; ++ fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00; ++ status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen); ++ if (status) { ++ status = -EFAULT; ++ goto exit; ++ } ++ ++ timeout = jiffies + HZ / 2; ++ written = 0; ++ while (!time_after(jiffies, timeout)) { ++ /* check the CAM hasn't been removed/reset in the meantime */ ++ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) { ++ status = -EIO; ++ goto exit; ++ } ++ ++ mutex_lock(&ca->slot_info[slot].slot_lock); ++ status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2); ++ mutex_unlock(&ca->slot_info[slot].slot_lock); ++ if (status == (fraglen + 2)) { ++ written = 1; ++ break; ++ } ++ if (status != -EAGAIN) ++ goto exit; ++ ++ msleep(1); ++ } ++ if (!written) { ++ status = -EIO; ++ goto exit; ++ } ++ ++ fragpos += fraglen; ++ } ++ status = count + 2; ++ ++exit: ++ return status; ++} ++ ++ ++/** ++ * Condition for waking up in dvb_ca_en50221_io_read_condition ++ */ ++static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, ++ int *result, int *_slot) ++{ ++ int slot; ++ int slot_count = 0; ++ int idx; ++ size_t fraglen; ++ int connection_id = -1; ++ int found = 0; ++ u8 hdr[2]; ++ ++ slot = ca->next_read_slot; ++ while ((slot_count < ca->slot_count) && (!found)) { ++ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) ++ goto nextslot; ++ ++ if (ca->slot_info[slot].rx_buffer.data == NULL) { ++ return 0; ++ } ++ ++ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); ++ while (idx != -1) { ++ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); ++ if (connection_id == -1) ++ connection_id = hdr[0]; ++ if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) { ++ *_slot = slot; ++ found = 1; ++ break; ++ } ++ ++ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); ++ } ++ ++nextslot: ++ slot = (slot + 1) % ca->slot_count; ++ slot_count++; ++ } ++ ++ ca->next_read_slot = slot; ++ return found; ++} ++ ++ ++/** ++ * Implementation of read() syscall. ++ * ++ * @param file File structure. ++ * @param buf Destination buffer. ++ * @param count Size of destination buffer. ++ * @param ppos Position in file (ignored). ++ * ++ * @return Number of bytes read, or <0 on error. ++ */ ++static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, ++ size_t count, loff_t * ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ int status; ++ int result = 0; ++ u8 hdr[2]; ++ int slot; ++ int connection_id = -1; ++ size_t idx, idx2; ++ int last_fragment = 0; ++ size_t fraglen; ++ int pktlen; ++ int dispose = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ ++ if (count < 2) ++ return -EINVAL; ++ ++ /* wait for some data */ ++ if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) { ++ ++ /* if we're in nonblocking mode, exit immediately */ ++ if (file->f_flags & O_NONBLOCK) ++ return -EWOULDBLOCK; ++ ++ /* wait for some data */ ++ status = wait_event_interruptible(ca->wait_queue, ++ dvb_ca_en50221_io_read_condition ++ (ca, &result, &slot)); ++ } ++ if ((status < 0) || (result < 0)) { ++ if (result) ++ return result; ++ return status; ++ } ++ ++ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); ++ pktlen = 2; ++ do { ++ if (idx == -1) { ++ printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num); ++ status = -EIO; ++ goto exit; ++ } ++ ++ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); ++ if (connection_id == -1) ++ connection_id = hdr[0]; ++ if (hdr[0] == connection_id) { ++ if (pktlen < count) { ++ if ((pktlen + fraglen - 2) > count) { ++ fraglen = count - pktlen; ++ } else { ++ fraglen -= 2; ++ } ++ ++ if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2, ++ buf + pktlen, fraglen)) < 0) { ++ goto exit; ++ } ++ pktlen += fraglen; ++ } ++ ++ if ((hdr[1] & 0x80) == 0) ++ last_fragment = 1; ++ dispose = 1; ++ } ++ ++ idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); ++ if (dispose) ++ dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx); ++ idx = idx2; ++ dispose = 0; ++ } while (!last_fragment); ++ ++ hdr[0] = slot; ++ hdr[1] = connection_id; ++ status = copy_to_user(buf, hdr, 2); ++ if (status) { ++ status = -EFAULT; ++ goto exit; ++ } ++ status = pktlen; ++ ++exit: ++ return status; ++} ++ ++ ++/** ++ * Implementation of file open syscall. ++ * ++ * @param inode Inode concerned. ++ * @param file File concerned. ++ * ++ * @return 0 on success, <0 on failure. ++ */ ++static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ int err; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ if (!try_module_get(ca->pub->owner)) ++ return -EIO; ++ ++ err = dvb_generic_open(inode, file); ++ if (err < 0) { ++ module_put(ca->pub->owner); ++ return err; ++ } ++ ++ for (i = 0; i < ca->slot_count; i++) { ++ ++ if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) { ++ if (ca->slot_info[i].rx_buffer.data != NULL) { ++ /* it is safe to call this here without locks because ++ * ca->open == 0. Data is not read in this case */ ++ dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer); ++ } ++ } ++ } ++ ++ ca->open = 1; ++ dvb_ca_en50221_thread_update_delay(ca); ++ dvb_ca_en50221_thread_wakeup(ca); ++ ++ return 0; ++} ++ ++ ++/** ++ * Implementation of file close syscall. ++ * ++ * @param inode Inode concerned. ++ * @param file File concerned. ++ * ++ * @return 0 on success, <0 on failure. ++ */ ++static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ int err; ++ ++ dprintk("%s\n", __func__); ++ ++ /* mark the CA device as closed */ ++ ca->open = 0; ++ dvb_ca_en50221_thread_update_delay(ca); ++ ++ err = dvb_generic_release(inode, file); ++ ++ module_put(ca->pub->owner); ++ ++ return err; ++} ++ ++ ++/** ++ * Implementation of poll() syscall. ++ * ++ * @param file File concerned. ++ * @param wait poll wait table. ++ * ++ * @return Standard poll mask. ++ */ ++static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_ca_private *ca = dvbdev->priv; ++ unsigned int mask = 0; ++ int slot; ++ int result = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { ++ mask |= POLLIN; ++ } ++ ++ /* if there is something, return now */ ++ if (mask) ++ return mask; ++ ++ /* wait for something to happen */ ++ poll_wait(file, &ca->wait_queue, wait); ++ ++ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { ++ mask |= POLLIN; ++ } ++ ++ return mask; ++} ++EXPORT_SYMBOL(dvb_ca_en50221_init); ++ ++ ++static const struct file_operations dvb_ca_fops = { ++ .owner = THIS_MODULE, ++ .read = dvb_ca_en50221_io_read, ++ .write = dvb_ca_en50221_io_write, ++ .unlocked_ioctl = dvb_ca_en50221_io_ioctl, ++ .open = dvb_ca_en50221_io_open, ++ .release = dvb_ca_en50221_io_release, ++ .poll = dvb_ca_en50221_io_poll, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_ca = { ++ .priv = NULL, ++ .users = 1, ++ .readers = 1, ++ .writers = 1, ++ .fops = &dvb_ca_fops, ++}; ++ ++ ++/* ******************************************************************************** */ ++/* Initialisation/shutdown functions */ ++ ++ ++/** ++ * Initialise a new DVB CA EN50221 interface device. ++ * ++ * @param dvb_adapter DVB adapter to attach the new CA device to. ++ * @param ca The dvb_ca instance. ++ * @param flags Flags describing the CA device (DVB_CA_FLAG_*). ++ * @param slot_count Number of slots supported. ++ * ++ * @return 0 on success, nonzero on failure ++ */ ++int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, ++ struct dvb_ca_en50221 *pubca, int flags, int slot_count) ++{ ++ int ret; ++ struct dvb_ca_private *ca = NULL; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ if (slot_count < 1) ++ return -EINVAL; ++ ++ /* initialise the system data */ ++ if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ca->pub = pubca; ++ ca->flags = flags; ++ ca->slot_count = slot_count; ++ if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ init_waitqueue_head(&ca->wait_queue); ++ ca->open = 0; ++ ca->wakeup = 0; ++ ca->next_read_slot = 0; ++ pubca->private = ca; ++ ++ /* register the DVB device */ ++ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA); ++ if (ret) ++ goto error; ++ ++ /* now initialise each slot */ ++ for (i = 0; i < slot_count; i++) { ++ memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot)); ++ ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE; ++ atomic_set(&ca->slot_info[i].camchange_count, 0); ++ ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; ++ mutex_init(&ca->slot_info[i].slot_lock); ++ } ++ ++ if (signal_pending(current)) { ++ ret = -EINTR; ++ goto error; ++ } ++ mb(); ++ ++ /* create a kthread for monitoring this CA device */ ++ ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i", ++ ca->dvbdev->adapter->num, ca->dvbdev->id); ++ if (IS_ERR(ca->thread)) { ++ ret = PTR_ERR(ca->thread); ++ printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ++ ret); ++ goto error; ++ } ++ return 0; ++ ++error: ++ if (ca != NULL) { ++ if (ca->dvbdev != NULL) ++ dvb_unregister_device(ca->dvbdev); ++ kfree(ca->slot_info); ++ kfree(ca); ++ } ++ pubca->private = NULL; ++ return ret; ++} ++EXPORT_SYMBOL(dvb_ca_en50221_release); ++ ++ ++ ++/** ++ * Release a DVB CA EN50221 interface device. ++ * ++ * @param ca_dev The dvb_device_t instance for the CA device. ++ * @param ca The associated dvb_ca instance. ++ */ ++void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) ++{ ++ struct dvb_ca_private *ca = pubca->private; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ /* shutdown the thread if there was one */ ++ kthread_stop(ca->thread); ++ ++ for (i = 0; i < ca->slot_count; i++) { ++ dvb_ca_en50221_slot_shutdown(ca, i); ++ vfree(ca->slot_info[i].rx_buffer.data); ++ } ++ kfree(ca->slot_info); ++ dvb_unregister_device(ca->dvbdev); ++ kfree(ca); ++ pubca->private = NULL; ++} +diff --git a/drivers/media/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h +new file mode 100644 +index 0000000..7df2e14 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_ca_en50221.h +@@ -0,0 +1,136 @@ ++/* ++ * dvb_ca.h: generic DVB functions for EN50221 CA interfaces ++ * ++ * Copyright (C) 2004 Andrew de Quincey ++ * ++ * 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. ++ */ ++ ++#ifndef _DVB_CA_EN50221_H_ ++#define _DVB_CA_EN50221_H_ ++ ++#include ++#include ++ ++#include "dvbdev.h" ++ ++#define DVB_CA_EN50221_POLL_CAM_PRESENT 1 ++#define DVB_CA_EN50221_POLL_CAM_CHANGED 2 ++#define DVB_CA_EN50221_POLL_CAM_READY 4 ++ ++#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1 ++#define DVB_CA_EN50221_FLAG_IRQ_FR 2 ++#define DVB_CA_EN50221_FLAG_IRQ_DA 4 ++ ++#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0 ++#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1 ++ ++ ++ ++/* Structure describing a CA interface */ ++struct dvb_ca_en50221 { ++ ++ /* the module owning this structure */ ++ struct module* owner; ++ ++ /* NOTE: the read_*, write_* and poll_slot_status functions will be ++ * called for different slots concurrently and need to use locks where ++ * and if appropriate. There will be no concurrent access to one slot. ++ */ ++ ++ /* functions for accessing attribute memory on the CAM */ ++ int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address); ++ int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value); ++ ++ /* functions for accessing the control interface on the CAM */ ++ int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address); ++ int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value); ++ ++ /* Functions for controlling slots */ ++ int (*slot_reset)(struct dvb_ca_en50221* ca, int slot); ++ int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot); ++ int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot); ++ ++ /* ++ * Poll slot status. ++ * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set ++ */ ++ int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open); ++ ++ /* private data, used by caller */ ++ void* data; ++ ++ /* Opaque data used by the dvb_ca core. Do not modify! */ ++ void* private; ++}; ++ ++ ++ ++ ++/* ******************************************************************************** */ ++/* Functions for reporting IRQ events */ ++ ++/** ++ * A CAMCHANGE IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ * @param change_type One of the DVB_CA_CAMCHANGE_* values ++ */ ++void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type); ++ ++/** ++ * A CAMREADY IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ */ ++void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot); ++ ++/** ++ * An FR or a DA IRQ has occurred. ++ * ++ * @param ca CA instance. ++ * @param slot Slot concerned. ++ */ ++void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot); ++ ++ ++ ++/* ******************************************************************************** */ ++/* Initialisation/shutdown functions */ ++ ++/** ++ * Initialise a new DVB CA device. ++ * ++ * @param dvb_adapter DVB adapter to attach the new CA device to. ++ * @param ca The dvb_ca instance. ++ * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*). ++ * @param slot_count Number of slots supported. ++ * ++ * @return 0 on success, nonzero on failure ++ */ ++extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count); ++ ++/** ++ * Release a DVB CA device. ++ * ++ * @param ca The associated dvb_ca instance. ++ */ ++extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca); ++ ++ ++ ++#endif +diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c +new file mode 100644 +index 0000000..53c7ff6 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_demux.c +@@ -0,0 +1,1309 @@ ++/* ++ * dvb_demux.c - DVB kernel demux API ++ * ++ * Copyright (C) 2000-2001 Ralph Metzler ++ * & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_demux.h" ++ ++#define NOBUFS ++/* ++** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog ++*/ ++// #define DVB_DEMUX_SECTION_LOSS_LOG ++ ++static int dvb_demux_tscheck; ++module_param(dvb_demux_tscheck, int, 0644); ++MODULE_PARM_DESC(dvb_demux_tscheck, ++ "enable transport stream continuity and TEI check"); ++ ++static int dvb_demux_speedcheck; ++module_param(dvb_demux_speedcheck, int, 0644); ++MODULE_PARM_DESC(dvb_demux_speedcheck, ++ "enable transport stream speed check"); ++ ++#define dprintk_tscheck(x...) do { \ ++ if (dvb_demux_tscheck && printk_ratelimit()) \ ++ printk(x); \ ++ } while (0) ++ ++/****************************************************************************** ++ * static inlined helper functions ++ ******************************************************************************/ ++ ++static inline u16 section_length(const u8 *buf) ++{ ++ return 3 + ((buf[1] & 0x0f) << 8) + buf[2]; ++} ++ ++static inline u16 ts_pid(const u8 *buf) ++{ ++ return ((buf[1] & 0x1f) << 8) + buf[2]; ++} ++ ++static inline u8 payload(const u8 *tsp) ++{ ++ if (!(tsp[3] & 0x10)) // no payload? ++ return 0; ++ ++ if (tsp[3] & 0x20) { // adaptation field? ++ if (tsp[4] > 183) // corrupted data? ++ return 0; ++ else ++ return 184 - 1 - tsp[4]; ++ } ++ ++ return 184; ++} ++ ++static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len) ++{ ++ return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len)); ++} ++ ++static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s, ++ size_t len) ++{ ++ memcpy(d, s, len); ++} ++ ++/****************************************************************************** ++ * Software filter functions ++ ******************************************************************************/ ++ ++static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, ++ const u8 *buf) ++{ ++ int count = payload(buf); ++ int p; ++ //int ccok; ++ //u8 cc; ++ ++ if (count == 0) ++ return -1; ++ ++ p = 188 - count; ++ ++ /* ++ cc = buf[3] & 0x0f; ++ ccok = ((feed->cc + 1) & 0x0f) == cc; ++ feed->cc = cc; ++ if (!ccok) ++ printk("missed packet!\n"); ++ */ ++ ++ if (buf[1] & 0x40) // PUSI ? ++ feed->peslen = 0xfffa; ++ ++ feed->peslen += count; ++ ++ return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK); ++} ++ ++static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, ++ struct dvb_demux_filter *f) ++{ ++ u8 neq = 0; ++ int i; ++ ++ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { ++ u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i]; ++ ++ if (f->maskandmode[i] & xor) ++ return 0; ++ ++ neq |= f->maskandnotmode[i] & xor; ++ } ++ ++ if (f->doneq && !neq) ++ return 0; ++ ++ return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, ++ NULL, 0, &f->filter, DMX_OK); ++} ++ ++static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct dvb_demux_filter *f = feed->filter; ++ struct dmx_section_feed *sec = &feed->feed.sec; ++ int section_syntax_indicator; ++ ++ if (!sec->is_filtering) ++ return 0; ++ ++ if (!f) ++ return 0; ++ ++ if (sec->check_crc) { ++ section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0); ++ if (section_syntax_indicator && ++ demux->check_crc32(feed, sec->secbuf, sec->seclen)) ++ return -1; ++ } ++ ++ do { ++ if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0) ++ return -1; ++ } while ((f = f->next) && sec->is_filtering); ++ ++ sec->seclen = 0; ++ ++ return 0; ++} ++ ++static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) ++{ ++ struct dmx_section_feed *sec = &feed->feed.sec; ++ ++#ifdef DVB_DEMUX_SECTION_LOSS_LOG ++ if (sec->secbufp < sec->tsfeedp) { ++ int i, n = sec->tsfeedp - sec->secbufp; ++ ++ /* ++ * Section padding is done with 0xff bytes entirely. ++ * Due to speed reasons, we won't check all of them ++ * but just first and last. ++ */ ++ if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) { ++ printk("dvb_demux.c section ts padding loss: %d/%d\n", ++ n, sec->tsfeedp); ++ printk("dvb_demux.c pad data:"); ++ for (i = 0; i < n; i++) ++ printk(" %02x", sec->secbuf[i]); ++ printk("\n"); ++ } ++ } ++#endif ++ ++ sec->tsfeedp = sec->secbufp = sec->seclen = 0; ++ sec->secbuf = sec->secbuf_base; ++} ++ ++/* ++ * Losless Section Demux 1.4.1 by Emard ++ * Valsecchi Patrick: ++ * - middle of section A (no PUSI) ++ * - end of section A and start of section B ++ * (with PUSI pointing to the start of the second section) ++ * ++ * In this case, without feed->pusi_seen you'll receive a garbage section ++ * consisting of the end of section A. Basically because tsfeedp ++ * is incemented and the use=0 condition is not raised ++ * when the second packet arrives. ++ * ++ * Fix: ++ * when demux is started, let feed->pusi_seen = 0 to ++ * prevent initial feeding of garbage from the end of ++ * previous section. When you for the first time see PUSI=1 ++ * then set feed->pusi_seen = 1 ++ */ ++static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, ++ const u8 *buf, u8 len) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct dmx_section_feed *sec = &feed->feed.sec; ++ u16 limit, seclen, n; ++ ++ if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE) ++ return 0; ++ ++ if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) { ++#ifdef DVB_DEMUX_SECTION_LOSS_LOG ++ printk("dvb_demux.c section buffer full loss: %d/%d\n", ++ sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, ++ DMX_MAX_SECFEED_SIZE); ++#endif ++ len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp; ++ } ++ ++ if (len <= 0) ++ return 0; ++ ++ demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len); ++ sec->tsfeedp += len; ++ ++ /* ++ * Dump all the sections we can find in the data (Emard) ++ */ ++ limit = sec->tsfeedp; ++ if (limit > DMX_MAX_SECFEED_SIZE) ++ return -1; /* internal error should never happen */ ++ ++ /* to be sure always set secbuf */ ++ sec->secbuf = sec->secbuf_base + sec->secbufp; ++ ++ for (n = 0; sec->secbufp + 2 < limit; n++) { ++ seclen = section_length(sec->secbuf); ++ if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE ++ || seclen + sec->secbufp > limit) ++ return 0; ++ sec->seclen = seclen; ++ sec->crc_val = ~0; ++ /* dump [secbuf .. secbuf+seclen) */ ++ if (feed->pusi_seen) ++ dvb_dmx_swfilter_section_feed(feed); ++#ifdef DVB_DEMUX_SECTION_LOSS_LOG ++ else ++ printk("dvb_demux.c pusi not seen, discarding section data\n"); ++#endif ++ sec->secbufp += seclen; /* secbufp and secbuf moving together is */ ++ sec->secbuf += seclen; /* redundant but saves pointer arithmetic */ ++ } ++ ++ return 0; ++} ++ ++static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, ++ const u8 *buf) ++{ ++ u8 p, count; ++ int ccok, dc_i = 0; ++ u8 cc; ++ ++ count = payload(buf); ++ ++ if (count == 0) /* count == 0 if no payload or out of range */ ++ return -1; ++ ++ p = 188 - count; /* payload start */ ++ ++ cc = buf[3] & 0x0f; ++ ccok = ((feed->cc + 1) & 0x0f) == cc; ++ feed->cc = cc; ++ ++ if (buf[3] & 0x20) { ++ /* adaption field present, check for discontinuity_indicator */ ++ if ((buf[4] > 0) && (buf[5] & 0x80)) ++ dc_i = 1; ++ } ++ ++ if (!ccok || dc_i) { ++#ifdef DVB_DEMUX_SECTION_LOSS_LOG ++ printk("dvb_demux.c discontinuity detected %d bytes lost\n", ++ count); ++ /* ++ * those bytes under sume circumstances will again be reported ++ * in the following dvb_dmx_swfilter_section_new ++ */ ++#endif ++ /* ++ * Discontinuity detected. Reset pusi_seen = 0 to ++ * stop feeding of suspicious data until next PUSI=1 arrives ++ */ ++ feed->pusi_seen = 0; ++ dvb_dmx_swfilter_section_new(feed); ++ } ++ ++ if (buf[1] & 0x40) { ++ /* PUSI=1 (is set), section boundary is here */ ++ if (count > 1 && buf[p] < count) { ++ const u8 *before = &buf[p + 1]; ++ u8 before_len = buf[p]; ++ const u8 *after = &before[before_len]; ++ u8 after_len = count - 1 - before_len; ++ ++ dvb_dmx_swfilter_section_copy_dump(feed, before, ++ before_len); ++ /* before start of new section, set pusi_seen = 1 */ ++ feed->pusi_seen = 1; ++ dvb_dmx_swfilter_section_new(feed); ++ dvb_dmx_swfilter_section_copy_dump(feed, after, ++ after_len); ++ } ++#ifdef DVB_DEMUX_SECTION_LOSS_LOG ++ else if (count > 0) ++ printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count); ++#endif ++ } else { ++ /* PUSI=0 (is not set), no section boundary */ ++ dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count); ++ } ++ ++ return 0; ++} ++ ++static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, ++ const u8 *buf) ++{ ++ switch (feed->type) { ++ case DMX_TYPE_TS: ++ if (!feed->feed.ts.is_filtering) ++ break; ++ if (feed->ts_type & TS_PACKET) { ++ if (feed->ts_type & TS_PAYLOAD_ONLY) ++ dvb_dmx_swfilter_payload(feed, buf); ++ else ++ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, ++ DMX_OK); ++ } ++ if (feed->ts_type & TS_DECODER) ++ if (feed->demux->write_to_decoder) ++ feed->demux->write_to_decoder(feed, buf, 188); ++ break; ++ ++ case DMX_TYPE_SEC: ++ if (!feed->feed.sec.is_filtering) ++ break; ++ if (dvb_dmx_swfilter_section_packet(feed, buf) < 0) ++ feed->feed.sec.seclen = feed->feed.sec.secbufp = 0; ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++#define DVR_FEED(f) \ ++ (((f)->type == DMX_TYPE_TS) && \ ++ ((f)->feed.ts.is_filtering) && \ ++ (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET)) ++ ++static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) ++{ ++ struct dvb_demux_feed *feed; ++ u16 pid = ts_pid(buf); ++ int dvr_done = 0; ++ ++ if (dvb_demux_speedcheck) { ++ struct timespec cur_time, delta_time; ++ u64 speed_bytes, speed_timedelta; ++ ++ demux->speed_pkts_cnt++; ++ ++ /* show speed every SPEED_PKTS_INTERVAL packets */ ++ if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { ++ cur_time = current_kernel_time(); ++ ++ if (demux->speed_last_time.tv_sec != 0 && ++ demux->speed_last_time.tv_nsec != 0) { ++ delta_time = timespec_sub(cur_time, ++ demux->speed_last_time); ++ speed_bytes = (u64)demux->speed_pkts_cnt ++ * 188 * 8; ++ /* convert to 1024 basis */ ++ speed_bytes = 1000 * div64_u64(speed_bytes, ++ 1024); ++ speed_timedelta = ++ (u64)timespec_to_ns(&delta_time); ++ speed_timedelta = div64_u64(speed_timedelta, ++ 1000000); /* nsec -> usec */ ++ printk(KERN_INFO "TS speed %llu Kbits/sec \n", ++ div64_u64(speed_bytes, ++ speed_timedelta)); ++ }; ++ ++ demux->speed_last_time = cur_time; ++ demux->speed_pkts_cnt = 0; ++ }; ++ }; ++ ++ if (demux->cnt_storage && dvb_demux_tscheck) { ++ /* check pkt counter */ ++ if (pid < MAX_PID) { ++ if (buf[1] & 0x80) ++ dprintk_tscheck("TEI detected. " ++ "PID=0x%x data1=0x%x\n", ++ pid, buf[1]); ++ ++ if ((buf[3] & 0xf) != demux->cnt_storage[pid]) ++ dprintk_tscheck("TS packet counter mismatch. " ++ "PID=0x%x expected 0x%x " ++ "got 0x%x\n", ++ pid, demux->cnt_storage[pid], ++ buf[3] & 0xf); ++ ++ demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; ++ }; ++ /* end check */ ++ }; ++ ++ list_for_each_entry(feed, &demux->feed_list, list_head) { ++ if ((feed->pid != pid) && (feed->pid != 0x2000)) ++ continue; ++ ++ /* copy each packet only once to the dvr device, even ++ * if a PID is in multiple filters (e.g. video + PCR) */ ++ if ((DVR_FEED(feed)) && (dvr_done++)) ++ continue; ++ ++ if (feed->pid == pid) ++ dvb_dmx_swfilter_packet_type(feed, buf); ++ else if (feed->pid == 0x2000) ++ feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); ++ } ++} ++ ++void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, ++ size_t count) ++{ ++ spin_lock(&demux->lock); ++ ++ while (count--) { ++ if (buf[0] == 0x47) ++ dvb_dmx_swfilter_packet(demux, buf); ++ buf += 188; ++ } ++ ++ spin_unlock(&demux->lock); ++} ++ ++EXPORT_SYMBOL(dvb_dmx_swfilter_packets); ++ ++static inline int find_next_packet(const u8 *buf, int pos, size_t count, ++ const int pktsize) ++{ ++ int start = pos, lost; ++ ++ while (pos < count) { ++ if (buf[pos] == 0x47 || ++ (pktsize == 204 && buf[pos] == 0xB8)) ++ break; ++ pos++; ++ } ++ ++ lost = pos - start; ++ if (lost) { ++ /* This garbage is part of a valid packet? */ ++ int backtrack = pos - pktsize; ++ if (backtrack >= 0 && (buf[backtrack] == 0x47 || ++ (pktsize == 204 && buf[backtrack] == 0xB8))) ++ return backtrack; ++ } ++ ++ return pos; ++} ++ ++/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */ ++static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, ++ size_t count, const int pktsize) ++{ ++ int p = 0, i, j; ++ const u8 *q; ++ ++ spin_lock(&demux->lock); ++ ++ if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */ ++ i = demux->tsbufp; ++ j = pktsize - i; ++ if (count < j) { ++ memcpy(&demux->tsbuf[i], buf, count); ++ demux->tsbufp += count; ++ goto bailout; ++ } ++ memcpy(&demux->tsbuf[i], buf, j); ++ if (demux->tsbuf[0] == 0x47) /* double check */ ++ dvb_dmx_swfilter_packet(demux, demux->tsbuf); ++ demux->tsbufp = 0; ++ p += j; ++ } ++ ++ while (1) { ++ p = find_next_packet(buf, p, count, pktsize); ++ if (p >= count) ++ break; ++ if (count - p < pktsize) ++ break; ++ ++ q = &buf[p]; ++ ++ if (pktsize == 204 && (*q == 0xB8)) { ++ memcpy(demux->tsbuf, q, 188); ++ demux->tsbuf[0] = 0x47; ++ q = demux->tsbuf; ++ } ++ dvb_dmx_swfilter_packet(demux, q); ++ p += pktsize; ++ } ++ ++ i = count - p; ++ if (i) { ++ memcpy(demux->tsbuf, &buf[p], i); ++ demux->tsbufp = i; ++ if (pktsize == 204 && demux->tsbuf[0] == 0xB8) ++ demux->tsbuf[0] = 0x47; ++ } ++ ++bailout: ++ spin_unlock(&demux->lock); ++} ++ ++void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) ++{ ++ _dvb_dmx_swfilter(demux, buf, count, 188); ++} ++EXPORT_SYMBOL(dvb_dmx_swfilter); ++ ++void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) ++{ ++ _dvb_dmx_swfilter(demux, buf, count, 204); ++} ++EXPORT_SYMBOL(dvb_dmx_swfilter_204); ++void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) ++{ ++ spin_lock(&demux->lock); ++ ++ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK); ++ ++ spin_unlock(&demux->lock); ++} ++EXPORT_SYMBOL(dvb_dmx_swfilter_raw); ++ ++ ++static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) ++{ ++ int i; ++ ++ for (i = 0; i < demux->filternum; i++) ++ if (demux->filter[i].state == DMX_STATE_FREE) ++ break; ++ ++ if (i == demux->filternum) ++ return NULL; ++ ++ demux->filter[i].state = DMX_STATE_ALLOCATED; ++ ++ return &demux->filter[i]; ++} ++ ++static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux) ++{ ++ int i; ++ ++ for (i = 0; i < demux->feednum; i++) ++ if (demux->feed[i].state == DMX_STATE_FREE) ++ break; ++ ++ if (i == demux->feednum) ++ return NULL; ++ ++ demux->feed[i].state = DMX_STATE_ALLOCATED; ++ ++ return &demux->feed[i]; ++} ++ ++static int dvb_demux_feed_find(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux_feed *entry; ++ ++ list_for_each_entry(entry, &feed->demux->feed_list, list_head) ++ if (entry == feed) ++ return 1; ++ ++ return 0; ++} ++ ++static void dvb_demux_feed_add(struct dvb_demux_feed *feed) ++{ ++ spin_lock_irq(&feed->demux->lock); ++ if (dvb_demux_feed_find(feed)) { ++ printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n", ++ __func__, feed->type, feed->state, feed->pid); ++ goto out; ++ } ++ ++ list_add(&feed->list_head, &feed->demux->feed_list); ++out: ++ spin_unlock_irq(&feed->demux->lock); ++} ++ ++static void dvb_demux_feed_del(struct dvb_demux_feed *feed) ++{ ++ spin_lock_irq(&feed->demux->lock); ++ if (!(dvb_demux_feed_find(feed))) { ++ printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n", ++ __func__, feed->type, feed->state, feed->pid); ++ goto out; ++ } ++ ++ list_del(&feed->list_head); ++out: ++ spin_unlock_irq(&feed->demux->lock); ++} ++ ++static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, ++ enum dmx_ts_pes pes_type, ++ size_t circular_buffer_size, struct timespec timeout) ++{ ++ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; ++ struct dvb_demux *demux = feed->demux; ++ ++ if (pid > DMX_MAX_PID) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&demux->mutex)) ++ return -ERESTARTSYS; ++ ++ if (ts_type & TS_DECODER) { ++ if (pes_type >= DMX_TS_PES_OTHER) { ++ mutex_unlock(&demux->mutex); ++ return -EINVAL; ++ } ++ ++ if (demux->pesfilter[pes_type] && ++ demux->pesfilter[pes_type] != feed) { ++ mutex_unlock(&demux->mutex); ++ return -EINVAL; ++ } ++ ++ demux->pesfilter[pes_type] = feed; ++ demux->pids[pes_type] = pid; ++ } ++ ++ dvb_demux_feed_add(feed); ++ ++ feed->pid = pid; ++ feed->buffer_size = circular_buffer_size; ++ feed->timeout = timeout; ++ feed->ts_type = ts_type; ++ feed->pes_type = pes_type; ++ ++ if (feed->buffer_size) { ++#ifdef NOBUFS ++ feed->buffer = NULL; ++#else ++ feed->buffer = vmalloc(feed->buffer_size); ++ if (!feed->buffer) { ++ mutex_unlock(&demux->mutex); ++ return -ENOMEM; ++ } ++#endif ++ } ++ ++ feed->state = DMX_STATE_READY; ++ mutex_unlock(&demux->mutex); ++ ++ return 0; ++} ++ ++static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed) ++{ ++ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; ++ struct dvb_demux *demux = feed->demux; ++ int ret; ++ ++ if (mutex_lock_interruptible(&demux->mutex)) ++ return -ERESTARTSYS; ++ ++ if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) { ++ mutex_unlock(&demux->mutex); ++ return -EINVAL; ++ } ++ ++ if (!demux->start_feed) { ++ mutex_unlock(&demux->mutex); ++ return -ENODEV; ++ } ++ ++ if ((ret = demux->start_feed(feed)) < 0) { ++ mutex_unlock(&demux->mutex); ++ return ret; ++ } ++ ++ spin_lock_irq(&demux->lock); ++ ts_feed->is_filtering = 1; ++ feed->state = DMX_STATE_GO; ++ spin_unlock_irq(&demux->lock); ++ mutex_unlock(&demux->mutex); ++ ++ return 0; ++} ++ ++static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed) ++{ ++ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; ++ struct dvb_demux *demux = feed->demux; ++ int ret; ++ ++ mutex_lock(&demux->mutex); ++ ++ if (feed->state < DMX_STATE_GO) { ++ mutex_unlock(&demux->mutex); ++ return -EINVAL; ++ } ++ ++ if (!demux->stop_feed) { ++ mutex_unlock(&demux->mutex); ++ return -ENODEV; ++ } ++ ++ ret = demux->stop_feed(feed); ++ ++ spin_lock_irq(&demux->lock); ++ ts_feed->is_filtering = 0; ++ feed->state = DMX_STATE_ALLOCATED; ++ spin_unlock_irq(&demux->lock); ++ mutex_unlock(&demux->mutex); ++ ++ return ret; ++} ++ ++static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx, ++ struct dmx_ts_feed **ts_feed, ++ dmx_ts_cb callback) ++{ ++ struct dvb_demux *demux = (struct dvb_demux *)dmx; ++ struct dvb_demux_feed *feed; ++ ++ if (mutex_lock_interruptible(&demux->mutex)) ++ return -ERESTARTSYS; ++ ++ if (!(feed = dvb_dmx_feed_alloc(demux))) { ++ mutex_unlock(&demux->mutex); ++ return -EBUSY; ++ } ++ ++ feed->type = DMX_TYPE_TS; ++ feed->cb.ts = callback; ++ feed->demux = demux; ++ feed->pid = 0xffff; ++ feed->peslen = 0xfffa; ++ feed->buffer = NULL; ++ ++ (*ts_feed) = &feed->feed.ts; ++ (*ts_feed)->parent = dmx; ++ (*ts_feed)->priv = NULL; ++ (*ts_feed)->is_filtering = 0; ++ (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering; ++ (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering; ++ (*ts_feed)->set = dmx_ts_feed_set; ++ ++ if (!(feed->filter = dvb_dmx_filter_alloc(demux))) { ++ feed->state = DMX_STATE_FREE; ++ mutex_unlock(&demux->mutex); ++ return -EBUSY; ++ } ++ ++ feed->filter->type = DMX_TYPE_TS; ++ feed->filter->feed = feed; ++ feed->filter->state = DMX_STATE_READY; ++ ++ mutex_unlock(&demux->mutex); ++ ++ return 0; ++} ++ ++static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, ++ struct dmx_ts_feed *ts_feed) ++{ ++ struct dvb_demux *demux = (struct dvb_demux *)dmx; ++ struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; ++ ++ mutex_lock(&demux->mutex); ++ ++ if (feed->state == DMX_STATE_FREE) { ++ mutex_unlock(&demux->mutex); ++ return -EINVAL; ++ } ++#ifndef NOBUFS ++ vfree(feed->buffer); ++ feed->buffer = NULL; ++#endif ++ ++ feed->state = DMX_STATE_FREE; ++ feed->filter->state = DMX_STATE_FREE; ++ ++ dvb_demux_feed_del(feed); ++ ++ feed->pid = 0xffff; ++ ++ if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER) ++ demux->pesfilter[feed->pes_type] = NULL; ++ ++ mutex_unlock(&demux->mutex); ++ return 0; ++} ++ ++/****************************************************************************** ++ * dmx_section_feed API calls ++ ******************************************************************************/ ++ ++static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed, ++ struct dmx_section_filter **filter) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdemux = dvbdmxfeed->demux; ++ struct dvb_demux_filter *dvbdmxfilter; ++ ++ if (mutex_lock_interruptible(&dvbdemux->mutex)) ++ return -ERESTARTSYS; ++ ++ dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux); ++ if (!dvbdmxfilter) { ++ mutex_unlock(&dvbdemux->mutex); ++ return -EBUSY; ++ } ++ ++ spin_lock_irq(&dvbdemux->lock); ++ *filter = &dvbdmxfilter->filter; ++ (*filter)->parent = feed; ++ (*filter)->priv = NULL; ++ dvbdmxfilter->feed = dvbdmxfeed; ++ dvbdmxfilter->type = DMX_TYPE_SEC; ++ dvbdmxfilter->state = DMX_STATE_READY; ++ dvbdmxfilter->next = dvbdmxfeed->filter; ++ dvbdmxfeed->filter = dvbdmxfilter; ++ spin_unlock_irq(&dvbdemux->lock); ++ ++ mutex_unlock(&dvbdemux->mutex); ++ return 0; ++} ++ ++static int dmx_section_feed_set(struct dmx_section_feed *feed, ++ u16 pid, size_t circular_buffer_size, ++ int check_crc) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ ++ if (pid > 0x1fff) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&dvbdmx->mutex)) ++ return -ERESTARTSYS; ++ ++ dvb_demux_feed_add(dvbdmxfeed); ++ ++ dvbdmxfeed->pid = pid; ++ dvbdmxfeed->buffer_size = circular_buffer_size; ++ dvbdmxfeed->feed.sec.check_crc = check_crc; ++ ++#ifdef NOBUFS ++ dvbdmxfeed->buffer = NULL; ++#else ++ dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size); ++ if (!dvbdmxfeed->buffer) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -ENOMEM; ++ } ++#endif ++ ++ dvbdmxfeed->state = DMX_STATE_READY; ++ mutex_unlock(&dvbdmx->mutex); ++ return 0; ++} ++ ++static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ int i; ++ struct dvb_demux_filter *f; ++ struct dmx_section_filter *sf; ++ u8 mask, mode, doneq; ++ ++ if (!(f = dvbdmxfeed->filter)) ++ return; ++ do { ++ sf = &f->filter; ++ doneq = 0; ++ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { ++ mode = sf->filter_mode[i]; ++ mask = sf->filter_mask[i]; ++ f->maskandmode[i] = mask & mode; ++ doneq |= f->maskandnotmode[i] = mask & ~mode; ++ } ++ f->doneq = doneq ? 1 : 0; ++ } while ((f = f->next)); ++} ++ ++static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ int ret; ++ ++ if (mutex_lock_interruptible(&dvbdmx->mutex)) ++ return -ERESTARTSYS; ++ ++ if (feed->is_filtering) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -EBUSY; ++ } ++ ++ if (!dvbdmxfeed->filter) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -EINVAL; ++ } ++ ++ dvbdmxfeed->feed.sec.tsfeedp = 0; ++ dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; ++ dvbdmxfeed->feed.sec.secbufp = 0; ++ dvbdmxfeed->feed.sec.seclen = 0; ++ ++ if (!dvbdmx->start_feed) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -ENODEV; ++ } ++ ++ prepare_secfilters(dvbdmxfeed); ++ ++ if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) { ++ mutex_unlock(&dvbdmx->mutex); ++ return ret; ++ } ++ ++ spin_lock_irq(&dvbdmx->lock); ++ feed->is_filtering = 1; ++ dvbdmxfeed->state = DMX_STATE_GO; ++ spin_unlock_irq(&dvbdmx->lock); ++ ++ mutex_unlock(&dvbdmx->mutex); ++ return 0; ++} ++ ++static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ int ret; ++ ++ mutex_lock(&dvbdmx->mutex); ++ ++ if (!dvbdmx->stop_feed) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -ENODEV; ++ } ++ ++ ret = dvbdmx->stop_feed(dvbdmxfeed); ++ ++ spin_lock_irq(&dvbdmx->lock); ++ dvbdmxfeed->state = DMX_STATE_READY; ++ feed->is_filtering = 0; ++ spin_unlock_irq(&dvbdmx->lock); ++ ++ mutex_unlock(&dvbdmx->mutex); ++ return ret; ++} ++ ++static int dmx_section_feed_release_filter(struct dmx_section_feed *feed, ++ struct dmx_section_filter *filter) ++{ ++ struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f; ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ ++ mutex_lock(&dvbdmx->mutex); ++ ++ if (dvbdmxfilter->feed != dvbdmxfeed) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -EINVAL; ++ } ++ ++ if (feed->is_filtering) ++ feed->stop_filtering(feed); ++ ++ spin_lock_irq(&dvbdmx->lock); ++ f = dvbdmxfeed->filter; ++ ++ if (f == dvbdmxfilter) { ++ dvbdmxfeed->filter = dvbdmxfilter->next; ++ } else { ++ while (f->next != dvbdmxfilter) ++ f = f->next; ++ f->next = f->next->next; ++ } ++ ++ dvbdmxfilter->state = DMX_STATE_FREE; ++ spin_unlock_irq(&dvbdmx->lock); ++ mutex_unlock(&dvbdmx->mutex); ++ return 0; ++} ++ ++static int dvbdmx_allocate_section_feed(struct dmx_demux *demux, ++ struct dmx_section_feed **feed, ++ dmx_section_cb callback) ++{ ++ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; ++ struct dvb_demux_feed *dvbdmxfeed; ++ ++ if (mutex_lock_interruptible(&dvbdmx->mutex)) ++ return -ERESTARTSYS; ++ ++ if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -EBUSY; ++ } ++ ++ dvbdmxfeed->type = DMX_TYPE_SEC; ++ dvbdmxfeed->cb.sec = callback; ++ dvbdmxfeed->demux = dvbdmx; ++ dvbdmxfeed->pid = 0xffff; ++ dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; ++ dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0; ++ dvbdmxfeed->feed.sec.tsfeedp = 0; ++ dvbdmxfeed->filter = NULL; ++ dvbdmxfeed->buffer = NULL; ++ ++ (*feed) = &dvbdmxfeed->feed.sec; ++ (*feed)->is_filtering = 0; ++ (*feed)->parent = demux; ++ (*feed)->priv = NULL; ++ ++ (*feed)->set = dmx_section_feed_set; ++ (*feed)->allocate_filter = dmx_section_feed_allocate_filter; ++ (*feed)->start_filtering = dmx_section_feed_start_filtering; ++ (*feed)->stop_filtering = dmx_section_feed_stop_filtering; ++ (*feed)->release_filter = dmx_section_feed_release_filter; ++ ++ mutex_unlock(&dvbdmx->mutex); ++ return 0; ++} ++ ++static int dvbdmx_release_section_feed(struct dmx_demux *demux, ++ struct dmx_section_feed *feed) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; ++ struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; ++ ++ mutex_lock(&dvbdmx->mutex); ++ ++ if (dvbdmxfeed->state == DMX_STATE_FREE) { ++ mutex_unlock(&dvbdmx->mutex); ++ return -EINVAL; ++ } ++#ifndef NOBUFS ++ vfree(dvbdmxfeed->buffer); ++ dvbdmxfeed->buffer = NULL; ++#endif ++ dvbdmxfeed->state = DMX_STATE_FREE; ++ ++ dvb_demux_feed_del(dvbdmxfeed); ++ ++ dvbdmxfeed->pid = 0xffff; ++ ++ mutex_unlock(&dvbdmx->mutex); ++ return 0; ++} ++ ++/****************************************************************************** ++ * dvb_demux kernel data API calls ++ ******************************************************************************/ ++ ++static int dvbdmx_open(struct dmx_demux *demux) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ if (dvbdemux->users >= MAX_DVB_DEMUX_USERS) ++ return -EUSERS; ++ ++ dvbdemux->users++; ++ return 0; ++} ++ ++static int dvbdmx_close(struct dmx_demux *demux) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ if (dvbdemux->users == 0) ++ return -ENODEV; ++ ++ dvbdemux->users--; ++ //FIXME: release any unneeded resources if users==0 ++ return 0; ++} ++ ++static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ void *p; ++ ++ if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) ++ return -EINVAL; ++ ++ p = memdup_user(buf, count); ++ if (IS_ERR(p)) ++ return PTR_ERR(p); ++ if (mutex_lock_interruptible(&dvbdemux->mutex)) { ++ kfree(p); ++ return -ERESTARTSYS; ++ } ++ dvb_dmx_swfilter(dvbdemux, p, count); ++ kfree(p); ++ mutex_unlock(&dvbdemux->mutex); ++ ++ if (signal_pending(current)) ++ return -EINTR; ++ return count; ++} ++ ++static int dvbdmx_add_frontend(struct dmx_demux *demux, ++ struct dmx_frontend *frontend) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ struct list_head *head = &dvbdemux->frontend_list; ++ ++ list_add(&(frontend->connectivity_list), head); ++ ++ return 0; ++} ++ ++static int dvbdmx_remove_frontend(struct dmx_demux *demux, ++ struct dmx_frontend *frontend) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ struct list_head *pos, *n, *head = &dvbdemux->frontend_list; ++ ++ list_for_each_safe(pos, n, head) { ++ if (DMX_FE_ENTRY(pos) == frontend) { ++ list_del(pos); ++ return 0; ++ } ++ } ++ ++ return -ENODEV; ++} ++ ++static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ if (list_empty(&dvbdemux->frontend_list)) ++ return NULL; ++ ++ return &dvbdemux->frontend_list; ++} ++ ++static int dvbdmx_connect_frontend(struct dmx_demux *demux, ++ struct dmx_frontend *frontend) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ if (demux->frontend) ++ return -EINVAL; ++ ++ mutex_lock(&dvbdemux->mutex); ++ ++ demux->frontend = frontend; ++ mutex_unlock(&dvbdemux->mutex); ++ return 0; ++} ++ ++static int dvbdmx_disconnect_frontend(struct dmx_demux *demux) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ mutex_lock(&dvbdemux->mutex); ++ ++ demux->frontend = NULL; ++ mutex_unlock(&dvbdemux->mutex); ++ return 0; ++} ++ ++static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) ++{ ++ struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; ++ ++ memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); ++ return 0; ++} ++ ++int dvb_dmx_init(struct dvb_demux *dvbdemux) ++{ ++ int i; ++ struct dmx_demux *dmx = &dvbdemux->dmx; ++ ++ dvbdemux->cnt_storage = NULL; ++ dvbdemux->users = 0; ++ dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter)); ++ ++ if (!dvbdemux->filter) ++ return -ENOMEM; ++ ++ dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed)); ++ if (!dvbdemux->feed) { ++ vfree(dvbdemux->filter); ++ dvbdemux->filter = NULL; ++ return -ENOMEM; ++ } ++ for (i = 0; i < dvbdemux->filternum; i++) { ++ dvbdemux->filter[i].state = DMX_STATE_FREE; ++ dvbdemux->filter[i].index = i; ++ } ++ for (i = 0; i < dvbdemux->feednum; i++) { ++ dvbdemux->feed[i].state = DMX_STATE_FREE; ++ dvbdemux->feed[i].index = i; ++ } ++ ++ dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); ++ if (!dvbdemux->cnt_storage) ++ printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); ++ ++ INIT_LIST_HEAD(&dvbdemux->frontend_list); ++ ++ for (i = 0; i < DMX_TS_PES_OTHER; i++) { ++ dvbdemux->pesfilter[i] = NULL; ++ dvbdemux->pids[i] = 0xffff; ++ } ++ ++ INIT_LIST_HEAD(&dvbdemux->feed_list); ++ ++ dvbdemux->playing = 0; ++ dvbdemux->recording = 0; ++ dvbdemux->tsbufp = 0; ++ ++ if (!dvbdemux->check_crc32) ++ dvbdemux->check_crc32 = dvb_dmx_crc32; ++ ++ if (!dvbdemux->memcopy) ++ dvbdemux->memcopy = dvb_dmx_memcopy; ++ ++ dmx->frontend = NULL; ++ dmx->priv = dvbdemux; ++ dmx->open = dvbdmx_open; ++ dmx->close = dvbdmx_close; ++ dmx->write = dvbdmx_write; ++ dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed; ++ dmx->release_ts_feed = dvbdmx_release_ts_feed; ++ dmx->allocate_section_feed = dvbdmx_allocate_section_feed; ++ dmx->release_section_feed = dvbdmx_release_section_feed; ++ ++ dmx->add_frontend = dvbdmx_add_frontend; ++ dmx->remove_frontend = dvbdmx_remove_frontend; ++ dmx->get_frontends = dvbdmx_get_frontends; ++ dmx->connect_frontend = dvbdmx_connect_frontend; ++ dmx->disconnect_frontend = dvbdmx_disconnect_frontend; ++ dmx->get_pes_pids = dvbdmx_get_pes_pids; ++ ++ mutex_init(&dvbdemux->mutex); ++ spin_lock_init(&dvbdemux->lock); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(dvb_dmx_init); ++ ++void dvb_dmx_release(struct dvb_demux *dvbdemux) ++{ ++ vfree(dvbdemux->cnt_storage); ++ vfree(dvbdemux->filter); ++ vfree(dvbdemux->feed); ++} ++ ++EXPORT_SYMBOL(dvb_dmx_release); +diff --git a/drivers/media/dvb-core/dvb_demux.h b/drivers/media/dvb-core/dvb_demux.h +new file mode 100644 +index 0000000..6173263 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_demux.h +@@ -0,0 +1,152 @@ ++/* ++ * dvb_demux.h: DVB kernel demux API ++ * ++ * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _DVB_DEMUX_H_ ++#define _DVB_DEMUX_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "demux.h" ++ ++#define DMX_TYPE_TS 0 ++#define DMX_TYPE_SEC 1 ++#define DMX_TYPE_PES 2 ++ ++#define DMX_STATE_FREE 0 ++#define DMX_STATE_ALLOCATED 1 ++#define DMX_STATE_SET 2 ++#define DMX_STATE_READY 3 ++#define DMX_STATE_GO 4 ++ ++#define DVB_DEMUX_MASK_MAX 18 ++ ++#define MAX_PID 0x1fff ++ ++#define SPEED_PKTS_INTERVAL 50000 ++ ++struct dvb_demux_filter { ++ struct dmx_section_filter filter; ++ u8 maskandmode[DMX_MAX_FILTER_SIZE]; ++ u8 maskandnotmode[DMX_MAX_FILTER_SIZE]; ++ int doneq; ++ ++ struct dvb_demux_filter *next; ++ struct dvb_demux_feed *feed; ++ int index; ++ int state; ++ int type; ++ ++ u16 hw_handle; ++ struct timer_list timer; ++}; ++ ++#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head) ++ ++struct dvb_demux_feed { ++ union { ++ struct dmx_ts_feed ts; ++ struct dmx_section_feed sec; ++ } feed; ++ ++ union { ++ dmx_ts_cb ts; ++ dmx_section_cb sec; ++ } cb; ++ ++ struct dvb_demux *demux; ++ void *priv; ++ int type; ++ int state; ++ u16 pid; ++ u8 *buffer; ++ int buffer_size; ++ ++ struct timespec timeout; ++ struct dvb_demux_filter *filter; ++ ++ int ts_type; ++ enum dmx_ts_pes pes_type; ++ ++ int cc; ++ int pusi_seen; /* prevents feeding of garbage from previous section */ ++ ++ u16 peslen; ++ ++ struct list_head list_head; ++ unsigned int index; /* a unique index for each feed (can be used as hardware pid filter index) */ ++}; ++ ++struct dvb_demux { ++ struct dmx_demux dmx; ++ void *priv; ++ int filternum; ++ int feednum; ++ int (*start_feed)(struct dvb_demux_feed *feed); ++ int (*stop_feed)(struct dvb_demux_feed *feed); ++ int (*write_to_decoder)(struct dvb_demux_feed *feed, ++ const u8 *buf, size_t len); ++ u32 (*check_crc32)(struct dvb_demux_feed *feed, ++ const u8 *buf, size_t len); ++ void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst, ++ const u8 *src, size_t len); ++ ++ int users; ++#define MAX_DVB_DEMUX_USERS 10 ++ struct dvb_demux_filter *filter; ++ struct dvb_demux_feed *feed; ++ ++ struct list_head frontend_list; ++ ++ struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER]; ++ u16 pids[DMX_TS_PES_OTHER]; ++ int playing; ++ int recording; ++ ++#define DMX_MAX_PID 0x2000 ++ struct list_head feed_list; ++ u8 tsbuf[204]; ++ int tsbufp; ++ ++ struct mutex mutex; ++ spinlock_t lock; ++ ++ uint8_t *cnt_storage; /* for TS continuity check */ ++ ++ struct timespec speed_last_time; /* for TS speed check */ ++ uint32_t speed_pkts_cnt; /* for TS speed check */ ++}; ++ ++int dvb_dmx_init(struct dvb_demux *dvbdemux); ++void dvb_dmx_release(struct dvb_demux *dvbdemux); ++void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, ++ size_t count); ++void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count); ++void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, ++ size_t count); ++void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, ++ size_t count); ++ ++ ++#endif /* _DVB_DEMUX_H_ */ +diff --git a/drivers/media/dvb-core/dvb_filter.c b/drivers/media/dvb-core/dvb_filter.c +new file mode 100644 +index 0000000..772003f +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_filter.c +@@ -0,0 +1,603 @@ ++#include ++#include ++#include ++#include "dvb_filter.h" ++ ++#if 0 ++static unsigned int bitrates[3][16] = ++{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, ++ {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, ++ {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}}; ++#endif ++ ++static u32 freq[4] = {480, 441, 320, 0}; ++ ++static unsigned int ac3_bitrates[32] = ++ {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, ++ 0,0,0,0,0,0,0,0,0,0,0,0,0}; ++ ++static u32 ac3_frames[3][32] = ++ {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, ++ 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++ {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, ++ 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++ {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, ++ 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; ++ ++ ++ ++#if 0 ++static void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, ++ void (*pes_write)(u8 *buf, int count, void *data), ++ void *priv) ++{ ++ dvb_filter_ipack_init(pa, IPACKS, pes_write); ++ dvb_filter_ipack_init(pv, IPACKS, pes_write); ++ pa->pid = pida; ++ pv->pid = pidv; ++ pa->data = priv; ++ pv->data = priv; ++} ++#endif ++ ++#if 0 ++static void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188) ++{ ++ u8 off = 0; ++ ++ if (!buf || !p ){ ++ printk("NULL POINTER IDIOT\n"); ++ return; ++ } ++ if (buf[1]&PAY_START) { ++ if (p->plength == MMAX_PLENGTH-6 && p->found>6){ ++ p->plength = p->found-6; ++ p->found = 0; ++ send_ipack(p); ++ dvb_filter_ipack_reset(p); ++ } ++ } ++ if (buf[3] & ADAPT_FIELD) { // adaptation field? ++ off = buf[4] + 1; ++ if (off+4 > 187) return; ++ } ++ dvb_filter_instant_repack(buf+4+off, TS_SIZE-4-off, p); ++} ++#endif ++ ++#if 0 ++/* needs 5 byte input, returns picture coding type*/ ++static int read_picture_header(u8 *headr, struct mpg_picture *pic, int field, int pr) ++{ ++ u8 pct; ++ ++ if (pr) printk( "Pic header: "); ++ pic->temporal_reference[field] = (( headr[0] << 2 ) | ++ (headr[1] & 0x03) )& 0x03ff; ++ if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]); ++ ++ pct = ( headr[1] >> 2 ) & 0x07; ++ pic->picture_coding_type[field] = pct; ++ if (pr) { ++ switch(pct){ ++ case I_FRAME: ++ printk( " I-FRAME"); ++ break; ++ case B_FRAME: ++ printk( " B-FRAME"); ++ break; ++ case P_FRAME: ++ printk( " P-FRAME"); ++ break; ++ } ++ } ++ ++ ++ pic->vinfo.vbv_delay = (( headr[1] >> 5 ) | ( headr[2] << 3) | ++ ( (headr[3] & 0x1F) << 11) ) & 0xffff; ++ ++ if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay); ++ ++ pic->picture_header_parameter = ( headr[3] & 0xe0 ) | ++ ((headr[4] & 0x80) >> 3); ++ ++ if ( pct == B_FRAME ){ ++ pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f; ++ } ++ if (pr) printk( " pic head param: 0x%x", ++ pic->picture_header_parameter); ++ ++ return pct; ++} ++#endif ++ ++#if 0 ++/* needs 4 byte input */ ++static int read_gop_header(u8 *headr, struct mpg_picture *pic, int pr) ++{ ++ if (pr) printk("GOP header: "); ++ ++ pic->time_code = (( headr[0] << 17 ) | ( headr[1] << 9) | ++ ( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff; ++ ++ if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F, ++ ((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F), ++ ((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F)); ++ ++ if ( ( headr[3] & 0x40 ) != 0 ){ ++ pic->closed_gop = 1; ++ } else { ++ pic->closed_gop = 0; ++ } ++ if (pr) printk("closed: %d", pic->closed_gop); ++ ++ if ( ( headr[3] & 0x20 ) != 0 ){ ++ pic->broken_link = 1; ++ } else { ++ pic->broken_link = 0; ++ } ++ if (pr) printk(" broken: %d\n", pic->broken_link); ++ ++ return 0; ++} ++#endif ++ ++#if 0 ++/* needs 8 byte input */ ++static int read_sequence_header(u8 *headr, struct dvb_video_info *vi, int pr) ++{ ++ int sw; ++ int form = -1; ++ ++ if (pr) printk("Reading sequence header\n"); ++ ++ vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4); ++ vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]); ++ ++ sw = (int)((headr[3]&0xF0) >> 4) ; ++ ++ switch( sw ){ ++ case 1: ++ if (pr) ++ printk("Videostream: ASPECT: 1:1"); ++ vi->aspect_ratio = 100; ++ break; ++ case 2: ++ if (pr) ++ printk("Videostream: ASPECT: 4:3"); ++ vi->aspect_ratio = 133; ++ break; ++ case 3: ++ if (pr) ++ printk("Videostream: ASPECT: 16:9"); ++ vi->aspect_ratio = 177; ++ break; ++ case 4: ++ if (pr) ++ printk("Videostream: ASPECT: 2.21:1"); ++ vi->aspect_ratio = 221; ++ break; ++ ++ case 5 ... 15: ++ if (pr) ++ printk("Videostream: ASPECT: reserved"); ++ vi->aspect_ratio = 0; ++ break; ++ ++ default: ++ vi->aspect_ratio = 0; ++ return -1; ++ } ++ ++ if (pr) ++ printk(" Size = %dx%d",vi->horizontal_size,vi->vertical_size); ++ ++ sw = (int)(headr[3]&0x0F); ++ ++ switch ( sw ) { ++ case 1: ++ if (pr) ++ printk(" FRate: 23.976 fps"); ++ vi->framerate = 23976; ++ form = -1; ++ break; ++ case 2: ++ if (pr) ++ printk(" FRate: 24 fps"); ++ vi->framerate = 24000; ++ form = -1; ++ break; ++ case 3: ++ if (pr) ++ printk(" FRate: 25 fps"); ++ vi->framerate = 25000; ++ form = VIDEO_MODE_PAL; ++ break; ++ case 4: ++ if (pr) ++ printk(" FRate: 29.97 fps"); ++ vi->framerate = 29970; ++ form = VIDEO_MODE_NTSC; ++ break; ++ case 5: ++ if (pr) ++ printk(" FRate: 30 fps"); ++ vi->framerate = 30000; ++ form = VIDEO_MODE_NTSC; ++ break; ++ case 6: ++ if (pr) ++ printk(" FRate: 50 fps"); ++ vi->framerate = 50000; ++ form = VIDEO_MODE_PAL; ++ break; ++ case 7: ++ if (pr) ++ printk(" FRate: 60 fps"); ++ vi->framerate = 60000; ++ form = VIDEO_MODE_NTSC; ++ break; ++ } ++ ++ vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03); ++ ++ vi->vbv_buffer_size ++ = (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5); ++ ++ if (pr){ ++ printk(" BRate: %d Mbit/s",4*(vi->bit_rate)/10000); ++ printk(" vbvbuffer %d",16*1024*(vi->vbv_buffer_size)); ++ printk("\n"); ++ } ++ ++ vi->video_format = form; ++ ++ return 0; ++} ++#endif ++ ++ ++#if 0 ++static int get_vinfo(u8 *mbuf, int count, struct dvb_video_info *vi, int pr) ++{ ++ u8 *headr; ++ int found = 0; ++ int c = 0; ++ ++ while (found < 4 && c+4 < count){ ++ u8 *b; ++ ++ b = mbuf+c; ++ if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01 ++ && b[3] == 0xb3) found = 4; ++ else { ++ c++; ++ } ++ } ++ ++ if (! found) return -1; ++ c += 4; ++ if (c+12 >= count) return -1; ++ headr = mbuf+c; ++ if (read_sequence_header(headr, vi, pr) < 0) return -1; ++ vi->off = c-4; ++ return 0; ++} ++#endif ++ ++ ++#if 0 ++static int get_ainfo(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) ++{ ++ u8 *headr; ++ int found = 0; ++ int c = 0; ++ int fr = 0; ++ ++ while (found < 2 && c < count){ ++ u8 b[2]; ++ memcpy( b, mbuf+c, 2); ++ ++ if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8) ++ found = 2; ++ else { ++ c++; ++ } ++ } ++ ++ if (!found) return -1; ++ ++ if (c+3 >= count) return -1; ++ headr = mbuf+c; ++ ++ ai->layer = (headr[1] & 0x06) >> 1; ++ ++ if (pr) ++ printk("Audiostream: Layer: %d", 4-ai->layer); ++ ++ ++ ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000; ++ ++ if (pr){ ++ if (ai->bit_rate == 0) ++ printk(" Bit rate: free"); ++ else if (ai->bit_rate == 0xf) ++ printk(" BRate: reserved"); ++ else ++ printk(" BRate: %d kb/s", ai->bit_rate/1000); ++ } ++ ++ fr = (headr[2] & 0x0c ) >> 2; ++ ai->frequency = freq[fr]*100; ++ if (pr){ ++ if (ai->frequency == 3) ++ printk(" Freq: reserved\n"); ++ else ++ printk(" Freq: %d kHz\n",ai->frequency); ++ ++ } ++ ai->off = c; ++ return 0; ++} ++#endif ++ ++ ++int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) ++{ ++ u8 *headr; ++ int found = 0; ++ int c = 0; ++ u8 frame = 0; ++ int fr = 0; ++ ++ while ( !found && c < count){ ++ u8 *b = mbuf+c; ++ ++ if ( b[0] == 0x0b && b[1] == 0x77 ) ++ found = 1; ++ else { ++ c++; ++ } ++ } ++ ++ if (!found) return -1; ++ if (pr) ++ printk("Audiostream: AC3"); ++ ++ ai->off = c; ++ if (c+5 >= count) return -1; ++ ++ ai->layer = 0; // 0 for AC3 ++ headr = mbuf+c+2; ++ ++ frame = (headr[2]&0x3f); ++ ai->bit_rate = ac3_bitrates[frame >> 1]*1000; ++ ++ if (pr) ++ printk(" BRate: %d kb/s", (int) ai->bit_rate/1000); ++ ++ ai->frequency = (headr[2] & 0xc0 ) >> 6; ++ fr = (headr[2] & 0xc0 ) >> 6; ++ ai->frequency = freq[fr]*100; ++ if (pr) printk (" Freq: %d Hz\n", (int) ai->frequency); ++ ++ ++ ai->framesize = ac3_frames[fr][frame >> 1]; ++ if ((frame & 1) && (fr == 1)) ai->framesize++; ++ ai->framesize = ai->framesize << 1; ++ if (pr) printk (" Framesize %d\n",(int) ai->framesize); ++ ++ ++ return 0; ++} ++EXPORT_SYMBOL(dvb_filter_get_ac3info); ++ ++ ++#if 0 ++static u8 *skip_pes_header(u8 **bufp) ++{ ++ u8 *inbuf = *bufp; ++ u8 *buf = inbuf; ++ u8 *pts = NULL; ++ int skip = 0; ++ ++ static const int mpeg1_skip_table[16] = { ++ 1, 0xffff, 5, 10, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff ++ }; ++ ++ ++ if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */ ++ if (buf[7] & PTS_ONLY) ++ pts = buf+9; ++ else pts = NULL; ++ buf = inbuf + 9 + inbuf[8]; ++ } else { /* mpeg1 */ ++ for (buf = inbuf + 6; *buf == 0xff; buf++) ++ if (buf == inbuf + 6 + 16) { ++ break; ++ } ++ if ((*buf & 0xc0) == 0x40) ++ buf += 2; ++ skip = mpeg1_skip_table [*buf >> 4]; ++ if (skip == 5 || skip == 10) pts = buf; ++ else pts = NULL; ++ ++ buf += mpeg1_skip_table [*buf >> 4]; ++ } ++ ++ *bufp = buf; ++ return pts; ++} ++#endif ++ ++#if 0 ++static void initialize_quant_matrix( u32 *matrix ) ++{ ++ int i; ++ ++ matrix[0] = 0x08101013; ++ matrix[1] = 0x10131616; ++ matrix[2] = 0x16161616; ++ matrix[3] = 0x1a181a1b; ++ matrix[4] = 0x1b1b1a1a; ++ matrix[5] = 0x1a1a1b1b; ++ matrix[6] = 0x1b1d1d1d; ++ matrix[7] = 0x2222221d; ++ matrix[8] = 0x1d1d1b1b; ++ matrix[9] = 0x1d1d2020; ++ matrix[10] = 0x22222526; ++ matrix[11] = 0x25232322; ++ matrix[12] = 0x23262628; ++ matrix[13] = 0x28283030; ++ matrix[14] = 0x2e2e3838; ++ matrix[15] = 0x3a454553; ++ ++ for ( i = 16 ; i < 32 ; i++ ) ++ matrix[i] = 0x10101010; ++} ++#endif ++ ++#if 0 ++static void initialize_mpg_picture(struct mpg_picture *pic) ++{ ++ int i; ++ ++ /* set MPEG1 */ ++ pic->mpeg1_flag = 1; ++ pic->profile_and_level = 0x4A ; /* MP@LL */ ++ pic->progressive_sequence = 1; ++ pic->low_delay = 0; ++ ++ pic->sequence_display_extension_flag = 0; ++ for ( i = 0 ; i < 4 ; i++ ){ ++ pic->frame_centre_horizontal_offset[i] = 0; ++ pic->frame_centre_vertical_offset[i] = 0; ++ } ++ pic->last_frame_centre_horizontal_offset = 0; ++ pic->last_frame_centre_vertical_offset = 0; ++ ++ pic->picture_display_extension_flag[0] = 0; ++ pic->picture_display_extension_flag[1] = 0; ++ pic->sequence_header_flag = 0; ++ pic->gop_flag = 0; ++ pic->sequence_end_flag = 0; ++} ++#endif ++ ++#if 0 ++static void mpg_set_picture_parameter( int32_t field_type, struct mpg_picture *pic ) ++{ ++ int16_t last_h_offset; ++ int16_t last_v_offset; ++ ++ int16_t *p_h_offset; ++ int16_t *p_v_offset; ++ ++ if ( pic->mpeg1_flag ){ ++ pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE; ++ pic->top_field_first = 0; ++ pic->repeat_first_field = 0; ++ pic->progressive_frame = 1; ++ pic->picture_coding_parameter = 0x000010; ++ } ++ ++ /* Reset flag */ ++ pic->picture_display_extension_flag[field_type] = 0; ++ ++ last_h_offset = pic->last_frame_centre_horizontal_offset; ++ last_v_offset = pic->last_frame_centre_vertical_offset; ++ if ( field_type == FIRST_FIELD ){ ++ p_h_offset = pic->frame_centre_horizontal_offset; ++ p_v_offset = pic->frame_centre_vertical_offset; ++ *p_h_offset = last_h_offset; ++ *(p_h_offset + 1) = last_h_offset; ++ *(p_h_offset + 2) = last_h_offset; ++ *p_v_offset = last_v_offset; ++ *(p_v_offset + 1) = last_v_offset; ++ *(p_v_offset + 2) = last_v_offset; ++ } else { ++ pic->frame_centre_horizontal_offset[3] = last_h_offset; ++ pic->frame_centre_vertical_offset[3] = last_v_offset; ++ } ++} ++#endif ++ ++#if 0 ++static void init_mpg_picture( struct mpg_picture *pic, int chan, int32_t field_type) ++{ ++ pic->picture_header = 0; ++ pic->sequence_header_data ++ = ( INIT_HORIZONTAL_SIZE << 20 ) ++ | ( INIT_VERTICAL_SIZE << 8 ) ++ | ( INIT_ASPECT_RATIO << 4 ) ++ | ( INIT_FRAME_RATE ); ++ pic->mpeg1_flag = 0; ++ pic->vinfo.horizontal_size ++ = INIT_DISP_HORIZONTAL_SIZE; ++ pic->vinfo.vertical_size ++ = INIT_DISP_VERTICAL_SIZE; ++ pic->picture_display_extension_flag[field_type] ++ = 0; ++ pic->pts_flag[field_type] = 0; ++ ++ pic->sequence_gop_header = 0; ++ pic->picture_header = 0; ++ pic->sequence_header_flag = 0; ++ pic->gop_flag = 0; ++ pic->sequence_end_flag = 0; ++ pic->sequence_display_extension_flag = 0; ++ pic->last_frame_centre_horizontal_offset = 0; ++ pic->last_frame_centre_vertical_offset = 0; ++ pic->channel = chan; ++} ++#endif ++ ++void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, ++ dvb_filter_pes2ts_cb_t *cb, void *priv) ++{ ++ unsigned char *buf=p2ts->buf; ++ ++ buf[0]=0x47; ++ buf[1]=(pid>>8); ++ buf[2]=pid&0xff; ++ p2ts->cc=0; ++ p2ts->cb=cb; ++ p2ts->priv=priv; ++} ++EXPORT_SYMBOL(dvb_filter_pes2ts_init); ++ ++int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, ++ int len, int payload_start) ++{ ++ unsigned char *buf=p2ts->buf; ++ int ret=0, rest; ++ ++ //len=6+((pes[4]<<8)|pes[5]); ++ ++ if (payload_start) ++ buf[1]|=0x40; ++ else ++ buf[1]&=~0x40; ++ while (len>=184) { ++ buf[3]=0x10|((p2ts->cc++)&0x0f); ++ memcpy(buf+4, pes, 184); ++ if ((ret=p2ts->cb(p2ts->priv, buf))) ++ return ret; ++ len-=184; pes+=184; ++ buf[1]&=~0x40; ++ } ++ if (!len) ++ return 0; ++ buf[3]=0x30|((p2ts->cc++)&0x0f); ++ rest=183-len; ++ if (rest) { ++ buf[5]=0x00; ++ if (rest-1) ++ memset(buf+6, 0xff, rest-1); ++ } ++ buf[4]=rest; ++ memcpy(buf+5+rest, pes, len); ++ return p2ts->cb(p2ts->priv, buf); ++} ++EXPORT_SYMBOL(dvb_filter_pes2ts); +diff --git a/drivers/media/dvb-core/dvb_filter.h b/drivers/media/dvb-core/dvb_filter.h +new file mode 100644 +index 0000000..375e3be +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_filter.h +@@ -0,0 +1,246 @@ ++/* ++ * dvb_filter.h ++ * ++ * Copyright (C) 2003 Convergence GmbH ++ * ++ * 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. ++ */ ++ ++#ifndef _DVB_FILTER_H_ ++#define _DVB_FILTER_H_ ++ ++#include ++ ++#include "demux.h" ++ ++typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *); ++ ++struct dvb_filter_pes2ts { ++ unsigned char buf[188]; ++ unsigned char cc; ++ dvb_filter_pes2ts_cb_t *cb; ++ void *priv; ++}; ++ ++void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, ++ dvb_filter_pes2ts_cb_t *cb, void *priv); ++ ++int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, ++ int len, int payload_start); ++ ++ ++#define PROG_STREAM_MAP 0xBC ++#define PRIVATE_STREAM1 0xBD ++#define PADDING_STREAM 0xBE ++#define PRIVATE_STREAM2 0xBF ++#define AUDIO_STREAM_S 0xC0 ++#define AUDIO_STREAM_E 0xDF ++#define VIDEO_STREAM_S 0xE0 ++#define VIDEO_STREAM_E 0xEF ++#define ECM_STREAM 0xF0 ++#define EMM_STREAM 0xF1 ++#define DSM_CC_STREAM 0xF2 ++#define ISO13522_STREAM 0xF3 ++#define PROG_STREAM_DIR 0xFF ++ ++#define DVB_PICTURE_START 0x00 ++#define DVB_USER_START 0xb2 ++#define DVB_SEQUENCE_HEADER 0xb3 ++#define DVB_SEQUENCE_ERROR 0xb4 ++#define DVB_EXTENSION_START 0xb5 ++#define DVB_SEQUENCE_END 0xb7 ++#define DVB_GOP_START 0xb8 ++#define DVB_EXCEPT_SLICE 0xb0 ++ ++#define SEQUENCE_EXTENSION 0x01 ++#define SEQUENCE_DISPLAY_EXTENSION 0x02 ++#define PICTURE_CODING_EXTENSION 0x08 ++#define QUANT_MATRIX_EXTENSION 0x03 ++#define PICTURE_DISPLAY_EXTENSION 0x07 ++ ++#define I_FRAME 0x01 ++#define B_FRAME 0x02 ++#define P_FRAME 0x03 ++ ++/* Initialize sequence_data */ ++#define INIT_HORIZONTAL_SIZE 720 ++#define INIT_VERTICAL_SIZE 576 ++#define INIT_ASPECT_RATIO 0x02 ++#define INIT_FRAME_RATE 0x03 ++#define INIT_DISP_HORIZONTAL_SIZE 540 ++#define INIT_DISP_VERTICAL_SIZE 576 ++ ++ ++//flags2 ++#define PTS_DTS_FLAGS 0xC0 ++#define ESCR_FLAG 0x20 ++#define ES_RATE_FLAG 0x10 ++#define DSM_TRICK_FLAG 0x08 ++#define ADD_CPY_FLAG 0x04 ++#define PES_CRC_FLAG 0x02 ++#define PES_EXT_FLAG 0x01 ++ ++//pts_dts flags ++#define PTS_ONLY 0x80 ++#define PTS_DTS 0xC0 ++ ++#define TS_SIZE 188 ++#define TRANS_ERROR 0x80 ++#define PAY_START 0x40 ++#define TRANS_PRIO 0x20 ++#define PID_MASK_HI 0x1F ++//flags ++#define TRANS_SCRMBL1 0x80 ++#define TRANS_SCRMBL2 0x40 ++#define ADAPT_FIELD 0x20 ++#define PAYLOAD 0x10 ++#define COUNT_MASK 0x0F ++ ++// adaptation flags ++#define DISCON_IND 0x80 ++#define RAND_ACC_IND 0x40 ++#define ES_PRI_IND 0x20 ++#define PCR_FLAG 0x10 ++#define OPCR_FLAG 0x08 ++#define SPLICE_FLAG 0x04 ++#define TRANS_PRIV 0x02 ++#define ADAP_EXT_FLAG 0x01 ++ ++// adaptation extension flags ++#define LTW_FLAG 0x80 ++#define PIECE_RATE 0x40 ++#define SEAM_SPLICE 0x20 ++ ++ ++#define MAX_PLENGTH 0xFFFF ++#define MMAX_PLENGTH (256*MAX_PLENGTH) ++ ++#ifndef IPACKS ++#define IPACKS 2048 ++#endif ++ ++struct ipack { ++ int size; ++ int found; ++ u8 *buf; ++ u8 cid; ++ u32 plength; ++ u8 plen[2]; ++ u8 flag1; ++ u8 flag2; ++ u8 hlength; ++ u8 pts[5]; ++ u16 *pid; ++ int mpeg; ++ u8 check; ++ int which; ++ int done; ++ void *data; ++ void (*func)(u8 *buf, int size, void *priv); ++ int count; ++ int repack_subids; ++}; ++ ++struct dvb_video_info { ++ u32 horizontal_size; ++ u32 vertical_size; ++ u32 aspect_ratio; ++ u32 framerate; ++ u32 video_format; ++ u32 bit_rate; ++ u32 comp_bit_rate; ++ u32 vbv_buffer_size; ++ s16 vbv_delay; ++ u32 CSPF; ++ u32 off; ++}; ++ ++#define OFF_SIZE 4 ++#define FIRST_FIELD 0 ++#define SECOND_FIELD 1 ++#define VIDEO_FRAME_PICTURE 0x03 ++ ++struct mpg_picture { ++ int channel; ++ struct dvb_video_info vinfo; ++ u32 *sequence_gop_header; ++ u32 *picture_header; ++ s32 time_code; ++ int low_delay; ++ int closed_gop; ++ int broken_link; ++ int sequence_header_flag; ++ int gop_flag; ++ int sequence_end_flag; ++ ++ u8 profile_and_level; ++ s32 picture_coding_parameter; ++ u32 matrix[32]; ++ s8 matrix_change_flag; ++ ++ u8 picture_header_parameter; ++ /* bit 0 - 2: bwd f code ++ bit 3 : fpb vector ++ bit 4 - 6: fwd f code ++ bit 7 : fpf vector */ ++ ++ int mpeg1_flag; ++ int progressive_sequence; ++ int sequence_display_extension_flag; ++ u32 sequence_header_data; ++ s16 last_frame_centre_horizontal_offset; ++ s16 last_frame_centre_vertical_offset; ++ ++ u32 pts[2]; /* [0] 1st field, [1] 2nd field */ ++ int top_field_first; ++ int repeat_first_field; ++ int progressive_frame; ++ int bank; ++ int forward_bank; ++ int backward_bank; ++ int compress; ++ s16 frame_centre_horizontal_offset[OFF_SIZE]; ++ /* [0-2] 1st field, [3] 2nd field */ ++ s16 frame_centre_vertical_offset[OFF_SIZE]; ++ /* [0-2] 1st field, [3] 2nd field */ ++ s16 temporal_reference[2]; ++ /* [0] 1st field, [1] 2nd field */ ++ ++ s8 picture_coding_type[2]; ++ /* [0] 1st field, [1] 2nd field */ ++ s8 picture_structure[2]; ++ /* [0] 1st field, [1] 2nd field */ ++ s8 picture_display_extension_flag[2]; ++ /* [0] 1st field, [1] 2nd field */ ++ /* picture_display_extenion() 0:no 1:exit*/ ++ s8 pts_flag[2]; ++ /* [0] 1st field, [1] 2nd field */ ++}; ++ ++struct dvb_audio_info { ++ int layer; ++ u32 bit_rate; ++ u32 frequency; ++ u32 mode; ++ u32 mode_extension ; ++ u32 emphasis; ++ u32 framesize; ++ u32 off; ++}; ++ ++int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr); ++ ++ ++#endif +diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c +new file mode 100644 +index 0000000..5ad7c81 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_frontend.c +@@ -0,0 +1,2520 @@ ++/* ++ * dvb_frontend.c: DVB frontend tuning interface/thread ++ * ++ * ++ * Copyright (C) 1999-2001 Ralph Metzler ++ * Marcus Metzler ++ * Holger Waechtler ++ * for convergence integrated media GmbH ++ * ++ * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/* Enables DVBv3 compatibility bits at the headers */ ++#define __DVB_CORE__ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "dvbdev.h" ++#include ++ ++static int dvb_frontend_debug; ++static int dvb_shutdown_timeout; ++static int dvb_force_auto_inversion; ++static int dvb_override_tune_delay; ++static int dvb_powerdown_on_sleep = 1; ++static int dvb_mfe_wait_time = 5; ++ ++module_param_named(frontend_debug, dvb_frontend_debug, int, 0644); ++MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off)."); ++module_param(dvb_shutdown_timeout, int, 0644); ++MODULE_PARM_DESC(dvb_shutdown_timeout, "wait seconds after close() before suspending hardware"); ++module_param(dvb_force_auto_inversion, int, 0644); ++MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always"); ++module_param(dvb_override_tune_delay, int, 0644); ++MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt"); ++module_param(dvb_powerdown_on_sleep, int, 0644); ++MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)"); ++module_param(dvb_mfe_wait_time, int, 0644); ++MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open() for multi-frontend to become available (default:5 seconds)"); ++ ++#define dprintk if (dvb_frontend_debug) printk ++ ++#define FESTATE_IDLE 1 ++#define FESTATE_RETUNE 2 ++#define FESTATE_TUNING_FAST 4 ++#define FESTATE_TUNING_SLOW 8 ++#define FESTATE_TUNED 16 ++#define FESTATE_ZIGZAG_FAST 32 ++#define FESTATE_ZIGZAG_SLOW 64 ++#define FESTATE_DISEQC 128 ++#define FESTATE_ERROR 256 ++#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC) ++#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST) ++#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW) ++#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW) ++ ++#define FE_ALGO_HW 1 ++/* ++ * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling. ++ * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune. ++ * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress. ++ * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower. ++ * FESTATE_TUNED. The frontend has successfully locked on. ++ * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it. ++ * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower. ++ * FESTATE_DISEQC. A DISEQC command has just been issued. ++ * FESTATE_WAITFORLOCK. When we're waiting for a lock. ++ * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan. ++ * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan. ++ * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. ++ */ ++ ++#define DVB_FE_NO_EXIT 0 ++#define DVB_FE_NORMAL_EXIT 1 ++#define DVB_FE_DEVICE_REMOVED 2 ++ ++static DEFINE_MUTEX(frontend_mutex); ++ ++struct dvb_frontend_private { ++ ++ /* thread/frontend values */ ++ struct dvb_device *dvbdev; ++ struct dvb_frontend_parameters parameters_out; ++ struct dvb_fe_events events; ++ struct semaphore sem; ++ struct list_head list_head; ++ wait_queue_head_t wait_queue; ++ struct task_struct *thread; ++ unsigned long release_jiffies; ++ unsigned int exit; ++ unsigned int wakeup; ++ fe_status_t status; ++ unsigned long tune_mode_flags; ++ unsigned int delay; ++ unsigned int reinitialise; ++ int tone; ++ int voltage; ++ ++ /* swzigzag values */ ++ unsigned int state; ++ unsigned int bending; ++ int lnb_drift; ++ unsigned int inversion; ++ unsigned int auto_step; ++ unsigned int auto_sub_step; ++ unsigned int started_auto_step; ++ unsigned int min_delay; ++ unsigned int max_drift; ++ unsigned int step_size; ++ int quality; ++ unsigned int check_wrapped; ++ enum dvbfe_search algo_status; ++}; ++ ++static void dvb_frontend_wakeup(struct dvb_frontend *fe); ++static int dtv_get_frontend(struct dvb_frontend *fe, ++ struct dvb_frontend_parameters *p_out); ++static int dtv_property_legacy_params_sync(struct dvb_frontend *fe, ++ struct dvb_frontend_parameters *p); ++ ++static bool has_get_frontend(struct dvb_frontend *fe) ++{ ++ return fe->ops.get_frontend; ++} ++ ++/* ++ * Due to DVBv3 API calls, a delivery system should be mapped into one of ++ * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC), ++ * otherwise, a DVBv3 call will fail. ++ */ ++enum dvbv3_emulation_type { ++ DVBV3_UNKNOWN, ++ DVBV3_QPSK, ++ DVBV3_QAM, ++ DVBV3_OFDM, ++ DVBV3_ATSC, ++}; ++ ++static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system) ++{ ++ switch (delivery_system) { ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ return DVBV3_QAM; ++ case SYS_DVBS: ++ case SYS_DVBS2: ++ case SYS_TURBO: ++ case SYS_ISDBS: ++ case SYS_DSS: ++ return DVBV3_QPSK; ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ case SYS_ISDBT: ++ case SYS_DMBTH: ++ return DVBV3_OFDM; ++ case SYS_ATSC: ++ case SYS_DVBC_ANNEX_B: ++ return DVBV3_ATSC; ++ case SYS_UNDEFINED: ++ case SYS_ISDBC: ++ case SYS_DVBH: ++ case SYS_DAB: ++ case SYS_ATSCMH: ++ default: ++ /* ++ * Doesn't know how to emulate those types and/or ++ * there's no frontend driver from this type yet ++ * with some emulation code, so, we're not sure yet how ++ * to handle them, or they're not compatible with a DVBv3 call. ++ */ ++ return DVBV3_UNKNOWN; ++ } ++} ++ ++static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dvb_fe_events *events = &fepriv->events; ++ struct dvb_frontend_event *e; ++ int wp; ++ ++ dprintk ("%s\n", __func__); ++ ++ if ((status & FE_HAS_LOCK) && has_get_frontend(fe)) ++ dtv_get_frontend(fe, &fepriv->parameters_out); ++ ++ mutex_lock(&events->mtx); ++ ++ wp = (events->eventw + 1) % MAX_EVENT; ++ if (wp == events->eventr) { ++ events->overflow = 1; ++ events->eventr = (events->eventr + 1) % MAX_EVENT; ++ } ++ ++ e = &events->events[events->eventw]; ++ e->status = status; ++ e->parameters = fepriv->parameters_out; ++ ++ events->eventw = wp; ++ ++ mutex_unlock(&events->mtx); ++ ++ wake_up_interruptible (&events->wait_queue); ++} ++ ++static int dvb_frontend_get_event(struct dvb_frontend *fe, ++ struct dvb_frontend_event *event, int flags) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dvb_fe_events *events = &fepriv->events; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (events->overflow) { ++ events->overflow = 0; ++ return -EOVERFLOW; ++ } ++ ++ if (events->eventw == events->eventr) { ++ int ret; ++ ++ if (flags & O_NONBLOCK) ++ return -EWOULDBLOCK; ++ ++ up(&fepriv->sem); ++ ++ ret = wait_event_interruptible (events->wait_queue, ++ events->eventw != events->eventr); ++ ++ if (down_interruptible (&fepriv->sem)) ++ return -ERESTARTSYS; ++ ++ if (ret < 0) ++ return ret; ++ } ++ ++ mutex_lock(&events->mtx); ++ *event = events->events[events->eventr]; ++ events->eventr = (events->eventr + 1) % MAX_EVENT; ++ mutex_unlock(&events->mtx); ++ ++ return 0; ++} ++ ++static void dvb_frontend_clear_events(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dvb_fe_events *events = &fepriv->events; ++ ++ mutex_lock(&events->mtx); ++ events->eventr = events->eventw; ++ mutex_unlock(&events->mtx); ++} ++ ++static void dvb_frontend_init(struct dvb_frontend *fe) ++{ ++ dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n", ++ fe->dvb->num, ++ fe->id, ++ fe->ops.info.name); ++ ++ if (fe->ops.init) ++ fe->ops.init(fe); ++ if (fe->ops.tuner_ops.init) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.init(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++} ++ ++void dvb_frontend_reinitialise(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ fepriv->reinitialise = 1; ++ dvb_frontend_wakeup(fe); ++} ++EXPORT_SYMBOL(dvb_frontend_reinitialise); ++ ++static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked) ++{ ++ int q2; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (locked) ++ (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256; ++ else ++ (fepriv->quality) = (fepriv->quality * 220 + 0) / 256; ++ ++ q2 = fepriv->quality - 128; ++ q2 *= q2; ++ ++ fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128); ++} ++ ++/** ++ * Performs automatic twiddling of frontend parameters. ++ * ++ * @param fe The frontend concerned. ++ * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT ++ * @returns Number of complete iterations that have been performed. ++ */ ++static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped) ++{ ++ int autoinversion; ++ int ready = 0; ++ int fe_set_err = 0; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp; ++ int original_inversion = c->inversion; ++ u32 original_frequency = c->frequency; ++ ++ /* are we using autoinversion? */ ++ autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) && ++ (c->inversion == INVERSION_AUTO)); ++ ++ /* setup parameters correctly */ ++ while(!ready) { ++ /* calculate the lnb_drift */ ++ fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size; ++ ++ /* wrap the auto_step if we've exceeded the maximum drift */ ++ if (fepriv->lnb_drift > fepriv->max_drift) { ++ fepriv->auto_step = 0; ++ fepriv->auto_sub_step = 0; ++ fepriv->lnb_drift = 0; ++ } ++ ++ /* perform inversion and +/- zigzag */ ++ switch(fepriv->auto_sub_step) { ++ case 0: ++ /* try with the current inversion and current drift setting */ ++ ready = 1; ++ break; ++ ++ case 1: ++ if (!autoinversion) break; ++ ++ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF; ++ ready = 1; ++ break; ++ ++ case 2: ++ if (fepriv->lnb_drift == 0) break; ++ ++ fepriv->lnb_drift = -fepriv->lnb_drift; ++ ready = 1; ++ break; ++ ++ case 3: ++ if (fepriv->lnb_drift == 0) break; ++ if (!autoinversion) break; ++ ++ fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF; ++ fepriv->lnb_drift = -fepriv->lnb_drift; ++ ready = 1; ++ break; ++ ++ default: ++ fepriv->auto_step++; ++ fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */ ++ break; ++ } ++ ++ if (!ready) fepriv->auto_sub_step++; ++ } ++ ++ /* if this attempt would hit where we started, indicate a complete ++ * iteration has occurred */ ++ if ((fepriv->auto_step == fepriv->started_auto_step) && ++ (fepriv->auto_sub_step == 0) && check_wrapped) { ++ return 1; ++ } ++ ++ dprintk("%s: drift:%i inversion:%i auto_step:%i " ++ "auto_sub_step:%i started_auto_step:%i\n", ++ __func__, fepriv->lnb_drift, fepriv->inversion, ++ fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step); ++ ++ /* set the frontend itself */ ++ c->frequency += fepriv->lnb_drift; ++ if (autoinversion) ++ c->inversion = fepriv->inversion; ++ tmp = *c; ++ if (fe->ops.set_frontend) ++ fe_set_err = fe->ops.set_frontend(fe); ++ *c = tmp; ++ if (fe_set_err < 0) { ++ fepriv->state = FESTATE_ERROR; ++ return fe_set_err; ++ } ++ ++ c->frequency = original_frequency; ++ c->inversion = original_inversion; ++ ++ fepriv->auto_sub_step++; ++ return 0; ++} ++ ++static void dvb_frontend_swzigzag(struct dvb_frontend *fe) ++{ ++ fe_status_t s = 0; ++ int retval = 0; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp; ++ ++ /* if we've got no parameters, just keep idling */ ++ if (fepriv->state & FESTATE_IDLE) { ++ fepriv->delay = 3*HZ; ++ fepriv->quality = 0; ++ return; ++ } ++ ++ /* in SCAN mode, we just set the frontend when asked and leave it alone */ ++ if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) { ++ if (fepriv->state & FESTATE_RETUNE) { ++ tmp = *c; ++ if (fe->ops.set_frontend) ++ retval = fe->ops.set_frontend(fe); ++ *c = tmp; ++ if (retval < 0) ++ fepriv->state = FESTATE_ERROR; ++ else ++ fepriv->state = FESTATE_TUNED; ++ } ++ fepriv->delay = 3*HZ; ++ fepriv->quality = 0; ++ return; ++ } ++ ++ /* get the frontend status */ ++ if (fepriv->state & FESTATE_RETUNE) { ++ s = 0; ++ } else { ++ if (fe->ops.read_status) ++ fe->ops.read_status(fe, &s); ++ if (s != fepriv->status) { ++ dvb_frontend_add_event(fe, s); ++ fepriv->status = s; ++ } ++ } ++ ++ /* if we're not tuned, and we have a lock, move to the TUNED state */ ++ if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) { ++ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); ++ fepriv->state = FESTATE_TUNED; ++ ++ /* if we're tuned, then we have determined the correct inversion */ ++ if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) && ++ (c->inversion == INVERSION_AUTO)) { ++ c->inversion = fepriv->inversion; ++ } ++ return; ++ } ++ ++ /* if we are tuned already, check we're still locked */ ++ if (fepriv->state & FESTATE_TUNED) { ++ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); ++ ++ /* we're tuned, and the lock is still good... */ ++ if (s & FE_HAS_LOCK) { ++ return; ++ } else { /* if we _WERE_ tuned, but now don't have a lock */ ++ fepriv->state = FESTATE_ZIGZAG_FAST; ++ fepriv->started_auto_step = fepriv->auto_step; ++ fepriv->check_wrapped = 0; ++ } ++ } ++ ++ /* don't actually do anything if we're in the LOSTLOCK state, ++ * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */ ++ if ((fepriv->state & FESTATE_LOSTLOCK) && ++ (fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) { ++ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); ++ return; ++ } ++ ++ /* don't do anything if we're in the DISEQC state, since this ++ * might be someone with a motorized dish controlled by DISEQC. ++ * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */ ++ if (fepriv->state & FESTATE_DISEQC) { ++ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); ++ return; ++ } ++ ++ /* if we're in the RETUNE state, set everything up for a brand ++ * new scan, keeping the current inversion setting, as the next ++ * tune is _very_ likely to require the same */ ++ if (fepriv->state & FESTATE_RETUNE) { ++ fepriv->lnb_drift = 0; ++ fepriv->auto_step = 0; ++ fepriv->auto_sub_step = 0; ++ fepriv->started_auto_step = 0; ++ fepriv->check_wrapped = 0; ++ } ++ ++ /* fast zigzag. */ ++ if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) { ++ fepriv->delay = fepriv->min_delay; ++ ++ /* perform a tune */ ++ retval = dvb_frontend_swzigzag_autotune(fe, ++ fepriv->check_wrapped); ++ if (retval < 0) { ++ return; ++ } else if (retval) { ++ /* OK, if we've run out of trials at the fast speed. ++ * Drop back to slow for the _next_ attempt */ ++ fepriv->state = FESTATE_SEARCHING_SLOW; ++ fepriv->started_auto_step = fepriv->auto_step; ++ return; ++ } ++ fepriv->check_wrapped = 1; ++ ++ /* if we've just retuned, enter the ZIGZAG_FAST state. ++ * This ensures we cannot return from an ++ * FE_SET_FRONTEND ioctl before the first frontend tune ++ * occurs */ ++ if (fepriv->state & FESTATE_RETUNE) { ++ fepriv->state = FESTATE_TUNING_FAST; ++ } ++ } ++ ++ /* slow zigzag */ ++ if (fepriv->state & FESTATE_SEARCHING_SLOW) { ++ dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); ++ ++ /* Note: don't bother checking for wrapping; we stay in this ++ * state until we get a lock */ ++ dvb_frontend_swzigzag_autotune(fe, 0); ++ } ++} ++ ++static int dvb_frontend_is_exiting(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ if (fepriv->exit != DVB_FE_NO_EXIT) ++ return 1; ++ ++ if (fepriv->dvbdev->writers == 1) ++ if (time_after_eq(jiffies, fepriv->release_jiffies + ++ dvb_shutdown_timeout * HZ)) ++ return 1; ++ ++ return 0; ++} ++ ++static int dvb_frontend_should_wakeup(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ if (fepriv->wakeup) { ++ fepriv->wakeup = 0; ++ return 1; ++ } ++ return dvb_frontend_is_exiting(fe); ++} ++ ++static void dvb_frontend_wakeup(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ fepriv->wakeup = 1; ++ wake_up_interruptible(&fepriv->wait_queue); ++} ++ ++static int dvb_frontend_thread(void *data) ++{ ++ struct dvb_frontend *fe = data; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ fe_status_t s; ++ enum dvbfe_algo algo; ++ ++ bool re_tune = false; ++ ++ dprintk("%s\n", __func__); ++ ++ fepriv->check_wrapped = 0; ++ fepriv->quality = 0; ++ fepriv->delay = 3*HZ; ++ fepriv->status = 0; ++ fepriv->wakeup = 0; ++ fepriv->reinitialise = 0; ++ ++ dvb_frontend_init(fe); ++ ++ set_freezable(); ++ while (1) { ++ up(&fepriv->sem); /* is locked when we enter the thread... */ ++restart: ++ wait_event_interruptible_timeout(fepriv->wait_queue, ++ dvb_frontend_should_wakeup(fe) || kthread_should_stop() ++ || freezing(current), ++ fepriv->delay); ++ ++ if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { ++ /* got signal or quitting */ ++ fepriv->exit = DVB_FE_NORMAL_EXIT; ++ break; ++ } ++ ++ if (try_to_freeze()) ++ goto restart; ++ ++ if (down_interruptible(&fepriv->sem)) ++ break; ++ ++ if (fepriv->reinitialise) { ++ dvb_frontend_init(fe); ++ if (fe->ops.set_tone && fepriv->tone != -1) ++ fe->ops.set_tone(fe, fepriv->tone); ++ if (fe->ops.set_voltage && fepriv->voltage != -1) ++ fe->ops.set_voltage(fe, fepriv->voltage); ++ fepriv->reinitialise = 0; ++ } ++ ++ /* do an iteration of the tuning loop */ ++ if (fe->ops.get_frontend_algo) { ++ algo = fe->ops.get_frontend_algo(fe); ++ switch (algo) { ++ case DVBFE_ALGO_HW: ++ dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__); ++ ++ if (fepriv->state & FESTATE_RETUNE) { ++ dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__); ++ re_tune = true; ++ fepriv->state = FESTATE_TUNED; ++ } else { ++ re_tune = false; ++ } ++ ++ if (fe->ops.tune) ++ fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s); ++ ++ if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) { ++ dprintk("%s: state changed, adding current state\n", __func__); ++ dvb_frontend_add_event(fe, s); ++ fepriv->status = s; ++ } ++ break; ++ case DVBFE_ALGO_SW: ++ dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__); ++ dvb_frontend_swzigzag(fe); ++ break; ++ case DVBFE_ALGO_CUSTOM: ++ dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state); ++ if (fepriv->state & FESTATE_RETUNE) { ++ dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__); ++ fepriv->state = FESTATE_TUNED; ++ } ++ /* Case where we are going to search for a carrier ++ * User asked us to retune again for some reason, possibly ++ * requesting a search with a new set of parameters ++ */ ++ if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) { ++ if (fe->ops.search) { ++ fepriv->algo_status = fe->ops.search(fe); ++ /* We did do a search as was requested, the flags are ++ * now unset as well and has the flags wrt to search. ++ */ ++ } else { ++ fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN; ++ } ++ } ++ /* Track the carrier if the search was successful */ ++ if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) { ++ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; ++ fepriv->delay = HZ / 2; ++ } ++ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); ++ fe->ops.read_status(fe, &s); ++ if (s != fepriv->status) { ++ dvb_frontend_add_event(fe, s); /* update event list */ ++ fepriv->status = s; ++ if (!(s & FE_HAS_LOCK)) { ++ fepriv->delay = HZ / 10; ++ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; ++ } else { ++ fepriv->delay = 60 * HZ; ++ } ++ } ++ break; ++ default: ++ dprintk("%s: UNDEFINED ALGO !\n", __func__); ++ break; ++ } ++ } else { ++ dvb_frontend_swzigzag(fe); ++ } ++ } ++ ++ if (dvb_powerdown_on_sleep) { ++ if (fe->ops.set_voltage) ++ fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); ++ if (fe->ops.tuner_ops.sleep) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.sleep(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (fe->ops.sleep) ++ fe->ops.sleep(fe); ++ } ++ ++ fepriv->thread = NULL; ++ if (kthread_should_stop()) ++ fepriv->exit = DVB_FE_DEVICE_REMOVED; ++ else ++ fepriv->exit = DVB_FE_NO_EXIT; ++ mb(); ++ ++ dvb_frontend_wakeup(fe); ++ return 0; ++} ++ ++static void dvb_frontend_stop(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ fepriv->exit = DVB_FE_NORMAL_EXIT; ++ mb(); ++ ++ if (!fepriv->thread) ++ return; ++ ++ kthread_stop(fepriv->thread); ++ ++ sema_init(&fepriv->sem, 1); ++ fepriv->state = FESTATE_IDLE; ++ ++ /* paranoia check in case a signal arrived */ ++ if (fepriv->thread) ++ printk("dvb_frontend_stop: warning: thread %p won't exit\n", ++ fepriv->thread); ++} ++ ++s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime) ++{ ++ return ((curtime.tv_usec < lasttime.tv_usec) ? ++ 1000000 - lasttime.tv_usec + curtime.tv_usec : ++ curtime.tv_usec - lasttime.tv_usec); ++} ++EXPORT_SYMBOL(timeval_usec_diff); ++ ++static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec) ++{ ++ curtime->tv_usec += add_usec; ++ if (curtime->tv_usec >= 1000000) { ++ curtime->tv_usec -= 1000000; ++ curtime->tv_sec++; ++ } ++} ++ ++/* ++ * Sleep until gettimeofday() > waketime + add_usec ++ * This needs to be as precise as possible, but as the delay is ++ * usually between 2ms and 32ms, it is done using a scheduled msleep ++ * followed by usleep (normally a busy-wait loop) for the remainder ++ */ ++void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec) ++{ ++ struct timeval lasttime; ++ s32 delta, newdelta; ++ ++ timeval_usec_add(waketime, add_usec); ++ ++ do_gettimeofday(&lasttime); ++ delta = timeval_usec_diff(lasttime, *waketime); ++ if (delta > 2500) { ++ msleep((delta - 1500) / 1000); ++ do_gettimeofday(&lasttime); ++ newdelta = timeval_usec_diff(lasttime, *waketime); ++ delta = (newdelta > delta) ? 0 : newdelta; ++ } ++ if (delta > 0) ++ udelay(delta); ++} ++EXPORT_SYMBOL(dvb_frontend_sleep_until); ++ ++static int dvb_frontend_start(struct dvb_frontend *fe) ++{ ++ int ret; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct task_struct *fe_thread; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (fepriv->thread) { ++ if (fepriv->exit == DVB_FE_NO_EXIT) ++ return 0; ++ else ++ dvb_frontend_stop (fe); ++ } ++ ++ if (signal_pending(current)) ++ return -EINTR; ++ if (down_interruptible (&fepriv->sem)) ++ return -EINTR; ++ ++ fepriv->state = FESTATE_IDLE; ++ fepriv->exit = DVB_FE_NO_EXIT; ++ fepriv->thread = NULL; ++ mb(); ++ ++ fe_thread = kthread_run(dvb_frontend_thread, fe, ++ "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id); ++ if (IS_ERR(fe_thread)) { ++ ret = PTR_ERR(fe_thread); ++ printk("dvb_frontend_start: failed to start kthread (%d)\n", ret); ++ up(&fepriv->sem); ++ return ret; ++ } ++ fepriv->thread = fe_thread; ++ return 0; ++} ++ ++static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, ++ u32 *freq_min, u32 *freq_max) ++{ ++ *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); ++ ++ if (fe->ops.info.frequency_max == 0) ++ *freq_max = fe->ops.tuner_ops.info.frequency_max; ++ else if (fe->ops.tuner_ops.info.frequency_max == 0) ++ *freq_max = fe->ops.info.frequency_max; ++ else ++ *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max); ++ ++ if (*freq_min == 0 || *freq_max == 0) ++ printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", ++ fe->dvb->num,fe->id); ++} ++ ++static int dvb_frontend_check_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 freq_min; ++ u32 freq_max; ++ ++ /* range check: frequency */ ++ dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max); ++ if ((freq_min && c->frequency < freq_min) || ++ (freq_max && c->frequency > freq_max)) { ++ printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", ++ fe->dvb->num, fe->id, c->frequency, freq_min, freq_max); ++ return -EINVAL; ++ } ++ ++ /* range check: symbol rate */ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ case SYS_DVBS2: ++ case SYS_TURBO: ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ if ((fe->ops.info.symbol_rate_min && ++ c->symbol_rate < fe->ops.info.symbol_rate_min) || ++ (fe->ops.info.symbol_rate_max && ++ c->symbol_rate > fe->ops.info.symbol_rate_max)) { ++ printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", ++ fe->dvb->num, fe->id, c->symbol_rate, ++ fe->ops.info.symbol_rate_min, ++ fe->ops.info.symbol_rate_max); ++ return -EINVAL; ++ } ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int dvb_frontend_clear_cache(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int i; ++ u32 delsys; ++ ++ delsys = c->delivery_system; ++ memset(c, 0, sizeof(struct dtv_frontend_properties)); ++ c->delivery_system = delsys; ++ ++ c->state = DTV_CLEAR; ++ ++ dprintk("%s() Clearing cache for delivery system %d\n", __func__, ++ c->delivery_system); ++ ++ c->transmission_mode = TRANSMISSION_MODE_AUTO; ++ c->bandwidth_hz = 0; /* AUTO */ ++ c->guard_interval = GUARD_INTERVAL_AUTO; ++ c->hierarchy = HIERARCHY_AUTO; ++ c->symbol_rate = 0; ++ c->code_rate_HP = FEC_AUTO; ++ c->code_rate_LP = FEC_AUTO; ++ c->fec_inner = FEC_AUTO; ++ c->rolloff = ROLLOFF_AUTO; ++ c->voltage = SEC_VOLTAGE_OFF; ++ c->sectone = SEC_TONE_OFF; ++ c->pilot = PILOT_AUTO; ++ ++ c->isdbt_partial_reception = 0; ++ c->isdbt_sb_mode = 0; ++ c->isdbt_sb_subchannel = 0; ++ c->isdbt_sb_segment_idx = 0; ++ c->isdbt_sb_segment_count = 0; ++ c->isdbt_layer_enabled = 0; ++ for (i = 0; i < 3; i++) { ++ c->layer[i].fec = FEC_AUTO; ++ c->layer[i].modulation = QAM_AUTO; ++ c->layer[i].interleaving = 0; ++ c->layer[i].segment_count = 0; ++ } ++ ++ c->stream_id = 0; ++ c->dvbt2_plp_id = 0; ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ case SYS_DVBS2: ++ case SYS_TURBO: ++ c->modulation = QPSK; /* implied for DVB-S in legacy API */ ++ c->rolloff = ROLLOFF_35;/* implied for DVB-S */ ++ break; ++ case SYS_ATSC: ++ c->modulation = VSB_8; ++ break; ++ default: ++ c->modulation = QAM_AUTO; ++ break; ++ } ++ ++ return 0; ++} ++ ++#define _DTV_CMD(n, s, b) \ ++[n] = { \ ++ .name = #n, \ ++ .cmd = n, \ ++ .set = s,\ ++ .buffer = b \ ++} ++ ++static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { ++ _DTV_CMD(DTV_TUNE, 1, 0), ++ _DTV_CMD(DTV_CLEAR, 1, 0), ++ ++ /* Set */ ++ _DTV_CMD(DTV_FREQUENCY, 1, 0), ++ _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0), ++ _DTV_CMD(DTV_MODULATION, 1, 0), ++ _DTV_CMD(DTV_INVERSION, 1, 0), ++ _DTV_CMD(DTV_DISEQC_MASTER, 1, 1), ++ _DTV_CMD(DTV_SYMBOL_RATE, 1, 0), ++ _DTV_CMD(DTV_INNER_FEC, 1, 0), ++ _DTV_CMD(DTV_VOLTAGE, 1, 0), ++ _DTV_CMD(DTV_TONE, 1, 0), ++ _DTV_CMD(DTV_PILOT, 1, 0), ++ _DTV_CMD(DTV_ROLLOFF, 1, 0), ++ _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0), ++ _DTV_CMD(DTV_HIERARCHY, 1, 0), ++ _DTV_CMD(DTV_CODE_RATE_HP, 1, 0), ++ _DTV_CMD(DTV_CODE_RATE_LP, 1, 0), ++ _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0), ++ _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0), ++ ++ _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), ++ _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), ++ _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0), ++ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0), ++ _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0), ++ _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0), ++ ++ _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), ++ _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0), ++ ++ /* Get */ ++ _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1), ++ _DTV_CMD(DTV_API_VERSION, 0, 0), ++ _DTV_CMD(DTV_CODE_RATE_HP, 0, 0), ++ _DTV_CMD(DTV_CODE_RATE_LP, 0, 0), ++ _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0), ++ _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0), ++ _DTV_CMD(DTV_HIERARCHY, 0, 0), ++ ++ _DTV_CMD(DTV_ENUM_DELSYS, 0, 0), ++}; ++ ++static void dtv_property_dump(struct dtv_property *tvp) ++{ ++ int i; ++ ++ if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) { ++ printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n", ++ __func__, tvp->cmd); ++ return; ++ } ++ ++ dprintk("%s() tvp.cmd = 0x%08x (%s)\n" ++ ,__func__ ++ ,tvp->cmd ++ ,dtv_cmds[ tvp->cmd ].name); ++ ++ if(dtv_cmds[ tvp->cmd ].buffer) { ++ ++ dprintk("%s() tvp.u.buffer.len = 0x%02x\n" ++ ,__func__ ++ ,tvp->u.buffer.len); ++ ++ for(i = 0; i < tvp->u.buffer.len; i++) ++ dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n" ++ ,__func__ ++ ,i ++ ,tvp->u.buffer.data[i]); ++ ++ } else ++ dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data); ++} ++ ++/* Synchronise the legacy tuning parameters into the cache, so that demodulator ++ * drivers can use a single set_frontend tuning function, regardless of whether ++ * it's being used for the legacy or new API, reducing code and complexity. ++ */ ++static int dtv_property_cache_sync(struct dvb_frontend *fe, ++ struct dtv_frontend_properties *c, ++ const struct dvb_frontend_parameters *p) ++{ ++ c->frequency = p->frequency; ++ c->inversion = p->inversion; ++ ++ switch (dvbv3_type(c->delivery_system)) { ++ case DVBV3_QPSK: ++ dprintk("%s() Preparing QPSK req\n", __func__); ++ c->symbol_rate = p->u.qpsk.symbol_rate; ++ c->fec_inner = p->u.qpsk.fec_inner; ++ break; ++ case DVBV3_QAM: ++ dprintk("%s() Preparing QAM req\n", __func__); ++ c->symbol_rate = p->u.qam.symbol_rate; ++ c->fec_inner = p->u.qam.fec_inner; ++ c->modulation = p->u.qam.modulation; ++ break; ++ case DVBV3_OFDM: ++ dprintk("%s() Preparing OFDM req\n", __func__); ++ switch (p->u.ofdm.bandwidth) { ++ case BANDWIDTH_10_MHZ: ++ c->bandwidth_hz = 10000000; ++ break; ++ case BANDWIDTH_8_MHZ: ++ c->bandwidth_hz = 8000000; ++ break; ++ case BANDWIDTH_7_MHZ: ++ c->bandwidth_hz = 7000000; ++ break; ++ case BANDWIDTH_6_MHZ: ++ c->bandwidth_hz = 6000000; ++ break; ++ case BANDWIDTH_5_MHZ: ++ c->bandwidth_hz = 5000000; ++ break; ++ case BANDWIDTH_1_712_MHZ: ++ c->bandwidth_hz = 1712000; ++ break; ++ case BANDWIDTH_AUTO: ++ c->bandwidth_hz = 0; ++ } ++ ++ c->code_rate_HP = p->u.ofdm.code_rate_HP; ++ c->code_rate_LP = p->u.ofdm.code_rate_LP; ++ c->modulation = p->u.ofdm.constellation; ++ c->transmission_mode = p->u.ofdm.transmission_mode; ++ c->guard_interval = p->u.ofdm.guard_interval; ++ c->hierarchy = p->u.ofdm.hierarchy_information; ++ break; ++ case DVBV3_ATSC: ++ dprintk("%s() Preparing ATSC req\n", __func__); ++ c->modulation = p->u.vsb.modulation; ++ if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) ++ c->delivery_system = SYS_ATSC; ++ else ++ c->delivery_system = SYS_DVBC_ANNEX_B; ++ break; ++ case DVBV3_UNKNOWN: ++ printk(KERN_ERR ++ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", ++ __func__, c->delivery_system); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* Ensure the cached values are set correctly in the frontend ++ * legacy tuning structures, for the advanced tuning API. ++ */ ++static int dtv_property_legacy_params_sync(struct dvb_frontend *fe, ++ struct dvb_frontend_parameters *p) ++{ ++ const struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ p->frequency = c->frequency; ++ p->inversion = c->inversion; ++ ++ switch (dvbv3_type(c->delivery_system)) { ++ case DVBV3_UNKNOWN: ++ printk(KERN_ERR ++ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", ++ __func__, c->delivery_system); ++ return -EINVAL; ++ case DVBV3_QPSK: ++ dprintk("%s() Preparing QPSK req\n", __func__); ++ p->u.qpsk.symbol_rate = c->symbol_rate; ++ p->u.qpsk.fec_inner = c->fec_inner; ++ break; ++ case DVBV3_QAM: ++ dprintk("%s() Preparing QAM req\n", __func__); ++ p->u.qam.symbol_rate = c->symbol_rate; ++ p->u.qam.fec_inner = c->fec_inner; ++ p->u.qam.modulation = c->modulation; ++ break; ++ case DVBV3_OFDM: ++ dprintk("%s() Preparing OFDM req\n", __func__); ++ ++ switch (c->bandwidth_hz) { ++ case 10000000: ++ p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ; ++ break; ++ case 8000000: ++ p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; ++ break; ++ case 7000000: ++ p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; ++ break; ++ case 6000000: ++ p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; ++ break; ++ case 5000000: ++ p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ; ++ break; ++ case 1712000: ++ p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ; ++ break; ++ case 0: ++ default: ++ p->u.ofdm.bandwidth = BANDWIDTH_AUTO; ++ } ++ p->u.ofdm.code_rate_HP = c->code_rate_HP; ++ p->u.ofdm.code_rate_LP = c->code_rate_LP; ++ p->u.ofdm.constellation = c->modulation; ++ p->u.ofdm.transmission_mode = c->transmission_mode; ++ p->u.ofdm.guard_interval = c->guard_interval; ++ p->u.ofdm.hierarchy_information = c->hierarchy; ++ break; ++ case DVBV3_ATSC: ++ dprintk("%s() Preparing VSB req\n", __func__); ++ p->u.vsb.modulation = c->modulation; ++ break; ++ } ++ return 0; ++} ++ ++/** ++ * dtv_get_frontend - calls a callback for retrieving DTV parameters ++ * @fe: struct dvb_frontend pointer ++ * @c: struct dtv_frontend_properties pointer (DVBv5 cache) ++ * @p_out struct dvb_frontend_parameters pointer (DVBv3 FE struct) ++ * ++ * This routine calls either the DVBv3 or DVBv5 get_frontend call. ++ * If c is not null, it will update the DVBv5 cache struct pointed by it. ++ * If p_out is not null, it will update the DVBv3 params pointed by it. ++ */ ++static int dtv_get_frontend(struct dvb_frontend *fe, ++ struct dvb_frontend_parameters *p_out) ++{ ++ int r; ++ ++ if (fe->ops.get_frontend) { ++ r = fe->ops.get_frontend(fe); ++ if (unlikely(r < 0)) ++ return r; ++ if (p_out) ++ dtv_property_legacy_params_sync(fe, p_out); ++ return 0; ++ } ++ ++ /* As everything is in cache, get_frontend fops are always supported */ ++ return 0; ++} ++ ++static int dvb_frontend_ioctl_legacy(struct file *file, ++ unsigned int cmd, void *parg); ++static int dvb_frontend_ioctl_properties(struct file *file, ++ unsigned int cmd, void *parg); ++ ++static int dtv_property_process_get(struct dvb_frontend *fe, ++ const struct dtv_frontend_properties *c, ++ struct dtv_property *tvp, ++ struct file *file) ++{ ++ int r, ncaps; ++ ++ switch(tvp->cmd) { ++ case DTV_ENUM_DELSYS: ++ ncaps = 0; ++ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { ++ tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps]; ++ ncaps++; ++ } ++ tvp->u.buffer.len = ncaps; ++ break; ++ case DTV_FREQUENCY: ++ tvp->u.data = c->frequency; ++ break; ++ case DTV_MODULATION: ++ tvp->u.data = c->modulation; ++ break; ++ case DTV_BANDWIDTH_HZ: ++ tvp->u.data = c->bandwidth_hz; ++ break; ++ case DTV_INVERSION: ++ tvp->u.data = c->inversion; ++ break; ++ case DTV_SYMBOL_RATE: ++ tvp->u.data = c->symbol_rate; ++ break; ++ case DTV_INNER_FEC: ++ tvp->u.data = c->fec_inner; ++ break; ++ case DTV_PILOT: ++ tvp->u.data = c->pilot; ++ break; ++ case DTV_ROLLOFF: ++ tvp->u.data = c->rolloff; ++ break; ++ case DTV_DELIVERY_SYSTEM: ++ tvp->u.data = c->delivery_system; ++ break; ++ case DTV_VOLTAGE: ++ tvp->u.data = c->voltage; ++ break; ++ case DTV_TONE: ++ tvp->u.data = c->sectone; ++ break; ++ case DTV_API_VERSION: ++ tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; ++ break; ++ case DTV_CODE_RATE_HP: ++ tvp->u.data = c->code_rate_HP; ++ break; ++ case DTV_CODE_RATE_LP: ++ tvp->u.data = c->code_rate_LP; ++ break; ++ case DTV_GUARD_INTERVAL: ++ tvp->u.data = c->guard_interval; ++ break; ++ case DTV_TRANSMISSION_MODE: ++ tvp->u.data = c->transmission_mode; ++ break; ++ case DTV_HIERARCHY: ++ tvp->u.data = c->hierarchy; ++ break; ++ ++ /* ISDB-T Support here */ ++ case DTV_ISDBT_PARTIAL_RECEPTION: ++ tvp->u.data = c->isdbt_partial_reception; ++ break; ++ case DTV_ISDBT_SOUND_BROADCASTING: ++ tvp->u.data = c->isdbt_sb_mode; ++ break; ++ case DTV_ISDBT_SB_SUBCHANNEL_ID: ++ tvp->u.data = c->isdbt_sb_subchannel; ++ break; ++ case DTV_ISDBT_SB_SEGMENT_IDX: ++ tvp->u.data = c->isdbt_sb_segment_idx; ++ break; ++ case DTV_ISDBT_SB_SEGMENT_COUNT: ++ tvp->u.data = c->isdbt_sb_segment_count; ++ break; ++ case DTV_ISDBT_LAYER_ENABLED: ++ tvp->u.data = c->isdbt_layer_enabled; ++ break; ++ case DTV_ISDBT_LAYERA_FEC: ++ tvp->u.data = c->layer[0].fec; ++ break; ++ case DTV_ISDBT_LAYERA_MODULATION: ++ tvp->u.data = c->layer[0].modulation; ++ break; ++ case DTV_ISDBT_LAYERA_SEGMENT_COUNT: ++ tvp->u.data = c->layer[0].segment_count; ++ break; ++ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: ++ tvp->u.data = c->layer[0].interleaving; ++ break; ++ case DTV_ISDBT_LAYERB_FEC: ++ tvp->u.data = c->layer[1].fec; ++ break; ++ case DTV_ISDBT_LAYERB_MODULATION: ++ tvp->u.data = c->layer[1].modulation; ++ break; ++ case DTV_ISDBT_LAYERB_SEGMENT_COUNT: ++ tvp->u.data = c->layer[1].segment_count; ++ break; ++ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: ++ tvp->u.data = c->layer[1].interleaving; ++ break; ++ case DTV_ISDBT_LAYERC_FEC: ++ tvp->u.data = c->layer[2].fec; ++ break; ++ case DTV_ISDBT_LAYERC_MODULATION: ++ tvp->u.data = c->layer[2].modulation; ++ break; ++ case DTV_ISDBT_LAYERC_SEGMENT_COUNT: ++ tvp->u.data = c->layer[2].segment_count; ++ break; ++ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: ++ tvp->u.data = c->layer[2].interleaving; ++ break; ++ case DTV_ISDBS_TS_ID: ++ tvp->u.data = c->stream_id; ++ break; ++ case DTV_DVBT2_PLP_ID: ++ tvp->u.data = c->dvbt2_plp_id; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Allow the frontend to override outgoing properties */ ++ if (fe->ops.get_property) { ++ r = fe->ops.get_property(fe, tvp); ++ if (r < 0) ++ return r; ++ } ++ ++ dtv_property_dump(tvp); ++ ++ return 0; ++} ++ ++static int dtv_set_frontend(struct dvb_frontend *fe); ++ ++static bool is_dvbv3_delsys(u32 delsys) ++{ ++ bool status; ++ ++ status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) || ++ (delsys == SYS_DVBS) || (delsys == SYS_ATSC); ++ ++ return status; ++} ++ ++static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) ++{ ++ int ncaps, i; ++ u32 delsys = SYS_UNDEFINED; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ enum dvbv3_emulation_type type; ++ ++ /* ++ * It was reported that some old DVBv5 applications were ++ * filling delivery_system with SYS_UNDEFINED. If this happens, ++ * assume that the application wants to use the first supported ++ * delivery system. ++ */ ++ if (c->delivery_system == SYS_UNDEFINED) ++ c->delivery_system = fe->ops.delsys[0]; ++ ++ if (desired_system == SYS_UNDEFINED) { ++ /* ++ * A DVBv3 call doesn't know what's the desired system. ++ * Also, DVBv3 applications don't know that ops.info->type ++ * could be changed, and they simply dies when it doesn't ++ * match. ++ * So, don't change the current delivery system, as it ++ * may be trying to do the wrong thing, like setting an ++ * ISDB-T frontend as DVB-T. Instead, find the closest ++ * DVBv3 system that matches the delivery system. ++ */ ++ if (is_dvbv3_delsys(c->delivery_system)) { ++ dprintk("%s() Using delivery system to %d\n", ++ __func__, c->delivery_system); ++ return 0; ++ } ++ type = dvbv3_type(c->delivery_system); ++ switch (type) { ++ case DVBV3_QPSK: ++ desired_system = SYS_DVBS; ++ break; ++ case DVBV3_QAM: ++ desired_system = SYS_DVBC_ANNEX_A; ++ break; ++ case DVBV3_ATSC: ++ desired_system = SYS_ATSC; ++ break; ++ case DVBV3_OFDM: ++ desired_system = SYS_DVBT; ++ break; ++ default: ++ dprintk("%s(): This frontend doesn't support DVBv3 calls\n", ++ __func__); ++ return -EINVAL; ++ } ++ /* ++ * Get a delivery system that is compatible with DVBv3 ++ * NOTE: in order for this to work with softwares like Kaffeine that ++ * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to ++ * DVB-S, drivers that support both should put the SYS_DVBS entry ++ * before the SYS_DVBS2, otherwise it won't switch back to DVB-S. ++ * The real fix is that userspace applications should not use DVBv3 ++ * and not trust on calling FE_SET_FRONTEND to switch the delivery ++ * system. ++ */ ++ ncaps = 0; ++ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { ++ if (fe->ops.delsys[ncaps] == desired_system) { ++ delsys = desired_system; ++ break; ++ } ++ ncaps++; ++ } ++ if (delsys == SYS_UNDEFINED) { ++ dprintk("%s() Couldn't find a delivery system that matches %d\n", ++ __func__, desired_system); ++ } ++ } else { ++ /* ++ * This is a DVBv5 call. So, it likely knows the supported ++ * delivery systems. ++ */ ++ ++ /* Check if the desired delivery system is supported */ ++ ncaps = 0; ++ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { ++ if (fe->ops.delsys[ncaps] == desired_system) { ++ c->delivery_system = desired_system; ++ dprintk("%s() Changing delivery system to %d\n", ++ __func__, desired_system); ++ return 0; ++ } ++ ncaps++; ++ } ++ type = dvbv3_type(desired_system); ++ ++ /* ++ * The delivery system is not supported. See if it can be ++ * emulated. ++ * The emulation only works if the desired system is one of the ++ * DVBv3 delivery systems ++ */ ++ if (!is_dvbv3_delsys(desired_system)) { ++ dprintk("%s() can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Get the last non-DVBv3 delivery system that has the same type ++ * of the desired system ++ */ ++ ncaps = 0; ++ while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { ++ if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) && ++ !is_dvbv3_delsys(fe->ops.delsys[ncaps])) ++ delsys = fe->ops.delsys[ncaps]; ++ ncaps++; ++ } ++ /* There's nothing compatible with the desired delivery system */ ++ if (delsys == SYS_UNDEFINED) { ++ dprintk("%s() Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n", ++ __func__); ++ return -EINVAL; ++ } ++ } ++ ++ c->delivery_system = delsys; ++ ++ /* ++ * The DVBv3 or DVBv5 call is requesting a different system. So, ++ * emulation is needed. ++ * ++ * Emulate newer delivery systems like ISDBT, DVBT and DMBTH ++ * for older DVBv5 applications. The emulation will try to use ++ * the auto mode for most things, and will assume that the desired ++ * delivery system is the last one at the ops.delsys[] array ++ */ ++ dprintk("%s() Using delivery system %d emulated as if it were a %d\n", ++ __func__, delsys, desired_system); ++ ++ /* ++ * For now, handles ISDB-T calls. More code may be needed here for the ++ * other emulated stuff ++ */ ++ if (type == DVBV3_OFDM) { ++ if (c->delivery_system == SYS_ISDBT) { ++ dprintk("%s() Using defaults for SYS_ISDBT\n", ++ __func__); ++ if (!c->bandwidth_hz) ++ c->bandwidth_hz = 6000000; ++ ++ c->isdbt_partial_reception = 0; ++ c->isdbt_sb_mode = 0; ++ c->isdbt_sb_subchannel = 0; ++ c->isdbt_sb_segment_idx = 0; ++ c->isdbt_sb_segment_count = 0; ++ c->isdbt_layer_enabled = 0; ++ for (i = 0; i < 3; i++) { ++ c->layer[i].fec = FEC_AUTO; ++ c->layer[i].modulation = QAM_AUTO; ++ c->layer[i].interleaving = 0; ++ c->layer[i].segment_count = 0; ++ } ++ } ++ } ++ dprintk("change delivery system on cache to %d\n", c->delivery_system); ++ ++ return 0; ++} ++ ++static int dtv_property_process_set(struct dvb_frontend *fe, ++ struct dtv_property *tvp, ++ struct file *file) ++{ ++ int r = 0; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ /* Allow the frontend to validate incoming properties */ ++ if (fe->ops.set_property) { ++ r = fe->ops.set_property(fe, tvp); ++ if (r < 0) ++ return r; ++ } ++ ++ switch(tvp->cmd) { ++ case DTV_CLEAR: ++ /* ++ * Reset a cache of data specific to the frontend here. This does ++ * not effect hardware. ++ */ ++ dvb_frontend_clear_cache(fe); ++ break; ++ case DTV_TUNE: ++ /* interpret the cache of data, build either a traditional frontend ++ * tunerequest so we can pass validation in the FE_SET_FRONTEND ++ * ioctl. ++ */ ++ c->state = tvp->cmd; ++ dprintk("%s() Finalised property cache\n", __func__); ++ ++ r = dtv_set_frontend(fe); ++ break; ++ case DTV_FREQUENCY: ++ c->frequency = tvp->u.data; ++ break; ++ case DTV_MODULATION: ++ c->modulation = tvp->u.data; ++ break; ++ case DTV_BANDWIDTH_HZ: ++ c->bandwidth_hz = tvp->u.data; ++ break; ++ case DTV_INVERSION: ++ c->inversion = tvp->u.data; ++ break; ++ case DTV_SYMBOL_RATE: ++ c->symbol_rate = tvp->u.data; ++ break; ++ case DTV_INNER_FEC: ++ c->fec_inner = tvp->u.data; ++ break; ++ case DTV_PILOT: ++ c->pilot = tvp->u.data; ++ break; ++ case DTV_ROLLOFF: ++ c->rolloff = tvp->u.data; ++ break; ++ case DTV_DELIVERY_SYSTEM: ++ r = set_delivery_system(fe, tvp->u.data); ++ break; ++ case DTV_VOLTAGE: ++ c->voltage = tvp->u.data; ++ r = dvb_frontend_ioctl_legacy(file, FE_SET_VOLTAGE, ++ (void *)c->voltage); ++ break; ++ case DTV_TONE: ++ c->sectone = tvp->u.data; ++ r = dvb_frontend_ioctl_legacy(file, FE_SET_TONE, ++ (void *)c->sectone); ++ break; ++ case DTV_CODE_RATE_HP: ++ c->code_rate_HP = tvp->u.data; ++ break; ++ case DTV_CODE_RATE_LP: ++ c->code_rate_LP = tvp->u.data; ++ break; ++ case DTV_GUARD_INTERVAL: ++ c->guard_interval = tvp->u.data; ++ break; ++ case DTV_TRANSMISSION_MODE: ++ c->transmission_mode = tvp->u.data; ++ break; ++ case DTV_HIERARCHY: ++ c->hierarchy = tvp->u.data; ++ break; ++ ++ /* ISDB-T Support here */ ++ case DTV_ISDBT_PARTIAL_RECEPTION: ++ c->isdbt_partial_reception = tvp->u.data; ++ break; ++ case DTV_ISDBT_SOUND_BROADCASTING: ++ c->isdbt_sb_mode = tvp->u.data; ++ break; ++ case DTV_ISDBT_SB_SUBCHANNEL_ID: ++ c->isdbt_sb_subchannel = tvp->u.data; ++ break; ++ case DTV_ISDBT_SB_SEGMENT_IDX: ++ c->isdbt_sb_segment_idx = tvp->u.data; ++ break; ++ case DTV_ISDBT_SB_SEGMENT_COUNT: ++ c->isdbt_sb_segment_count = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYER_ENABLED: ++ c->isdbt_layer_enabled = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERA_FEC: ++ c->layer[0].fec = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERA_MODULATION: ++ c->layer[0].modulation = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERA_SEGMENT_COUNT: ++ c->layer[0].segment_count = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: ++ c->layer[0].interleaving = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERB_FEC: ++ c->layer[1].fec = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERB_MODULATION: ++ c->layer[1].modulation = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERB_SEGMENT_COUNT: ++ c->layer[1].segment_count = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: ++ c->layer[1].interleaving = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERC_FEC: ++ c->layer[2].fec = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERC_MODULATION: ++ c->layer[2].modulation = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERC_SEGMENT_COUNT: ++ c->layer[2].segment_count = tvp->u.data; ++ break; ++ case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: ++ c->layer[2].interleaving = tvp->u.data; ++ break; ++ case DTV_ISDBS_TS_ID: ++ c->stream_id = tvp->u.data; ++ break; ++ case DTV_DVBT2_PLP_ID: ++ c->dvbt2_plp_id = tvp->u.data; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return r; ++} ++ ++static int dvb_frontend_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ int err = -EOPNOTSUPP; ++ ++ dprintk("%s (%d)\n", __func__, _IOC_NR(cmd)); ++ ++ if (fepriv->exit != DVB_FE_NO_EXIT) ++ return -ENODEV; ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY && ++ (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT || ++ cmd == FE_DISEQC_RECV_SLAVE_REPLY)) ++ return -EPERM; ++ ++ if (down_interruptible (&fepriv->sem)) ++ return -ERESTARTSYS; ++ ++ if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) ++ err = dvb_frontend_ioctl_properties(file, cmd, parg); ++ else { ++ c->state = DTV_UNDEFINED; ++ err = dvb_frontend_ioctl_legacy(file, cmd, parg); ++ } ++ ++ up(&fepriv->sem); ++ return err; ++} ++ ++static int dvb_frontend_ioctl_properties(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int err = 0; ++ ++ struct dtv_properties *tvps = NULL; ++ struct dtv_property *tvp = NULL; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ if(cmd == FE_SET_PROPERTY) { ++ tvps = (struct dtv_properties __user *)parg; ++ ++ dprintk("%s() properties.num = %d\n", __func__, tvps->num); ++ dprintk("%s() properties.props = %p\n", __func__, tvps->props); ++ ++ /* Put an arbitrary limit on the number of messages that can ++ * be sent at once */ ++ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) ++ return -EINVAL; ++ ++ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); ++ if (!tvp) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ for (i = 0; i < tvps->num; i++) { ++ err = dtv_property_process_set(fe, tvp + i, file); ++ if (err < 0) ++ goto out; ++ (tvp + i)->result = err; ++ } ++ ++ if (c->state == DTV_TUNE) ++ dprintk("%s() Property cache is full, tuning\n", __func__); ++ ++ } else ++ if(cmd == FE_GET_PROPERTY) { ++ tvps = (struct dtv_properties __user *)parg; ++ ++ dprintk("%s() properties.num = %d\n", __func__, tvps->num); ++ dprintk("%s() properties.props = %p\n", __func__, tvps->props); ++ ++ /* Put an arbitrary limit on the number of messages that can ++ * be sent at once */ ++ if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) ++ return -EINVAL; ++ ++ tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); ++ if (!tvp) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ /* ++ * Fills the cache out struct with the cache contents, plus ++ * the data retrieved from get_frontend, if the frontend ++ * is not idle. Otherwise, returns the cached content ++ */ ++ if (fepriv->state != FESTATE_IDLE) { ++ err = dtv_get_frontend(fe, NULL); ++ if (err < 0) ++ goto out; ++ } ++ for (i = 0; i < tvps->num; i++) { ++ err = dtv_property_process_get(fe, c, tvp + i, file); ++ if (err < 0) ++ goto out; ++ (tvp + i)->result = err; ++ } ++ ++ if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { ++ err = -EFAULT; ++ goto out; ++ } ++ ++ } else ++ err = -EOPNOTSUPP; ++ ++out: ++ kfree(tvp); ++ return err; ++} ++ ++static int dtv_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_frontend_tune_settings fetunesettings; ++ u32 rolloff = 0; ++ ++ if (dvb_frontend_check_parameters(fe) < 0) ++ return -EINVAL; ++ ++ /* ++ * Initialize output parameters to match the values given by ++ * the user. FE_SET_FRONTEND triggers an initial frontend event ++ * with status = 0, which copies output parameters to userspace. ++ */ ++ dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); ++ ++ /* ++ * Be sure that the bandwidth will be filled for all ++ * non-satellite systems, as tuners need to know what ++ * low pass/Nyquist half filter should be applied, in ++ * order to avoid inter-channel noise. ++ * ++ * ISDB-T and DVB-T/T2 already sets bandwidth. ++ * ATSC and DVB-C don't set, so, the core should fill it. ++ * ++ * On DVB-C Annex A and C, the bandwidth is a function of ++ * the roll-off and symbol rate. Annex B defines different ++ * roll-off factors depending on the modulation. Fortunately, ++ * Annex B is only used with 6MHz, so there's no need to ++ * calculate it. ++ * ++ * While not officially supported, a side effect of handling it at ++ * the cache level is that a program could retrieve the bandwidth ++ * via DTV_BANDWIDTH_HZ, which may be useful for test programs. ++ */ ++ switch (c->delivery_system) { ++ case SYS_ATSC: ++ case SYS_DVBC_ANNEX_B: ++ c->bandwidth_hz = 6000000; ++ break; ++ case SYS_DVBC_ANNEX_A: ++ rolloff = 115; ++ break; ++ case SYS_DVBC_ANNEX_C: ++ rolloff = 113; ++ break; ++ default: ++ break; ++ } ++ if (rolloff) ++ c->bandwidth_hz = (c->symbol_rate * rolloff) / 100; ++ ++ /* force auto frequency inversion if requested */ ++ if (dvb_force_auto_inversion) ++ c->inversion = INVERSION_AUTO; ++ ++ /* ++ * without hierarchical coding code_rate_LP is irrelevant, ++ * so we tolerate the otherwise invalid FEC_NONE setting ++ */ ++ if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE) ++ c->code_rate_LP = FEC_AUTO; ++ ++ /* get frontend-specific tuning settings */ ++ memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); ++ if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) { ++ fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000; ++ fepriv->max_drift = fetunesettings.max_drift; ++ fepriv->step_size = fetunesettings.step_size; ++ } else { ++ /* default values */ ++ switch (c->delivery_system) { ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ fepriv->min_delay = HZ / 20; ++ fepriv->step_size = c->symbol_rate / 16000; ++ fepriv->max_drift = c->symbol_rate / 2000; ++ break; ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ case SYS_ISDBT: ++ case SYS_DMBTH: ++ fepriv->min_delay = HZ / 20; ++ fepriv->step_size = fe->ops.info.frequency_stepsize * 2; ++ fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; ++ break; ++ default: ++ /* ++ * FIXME: This sounds wrong! if freqency_stepsize is ++ * defined by the frontend, why not use it??? ++ */ ++ fepriv->min_delay = HZ / 20; ++ fepriv->step_size = 0; /* no zigzag */ ++ fepriv->max_drift = 0; ++ break; ++ } ++ } ++ if (dvb_override_tune_delay > 0) ++ fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000; ++ ++ fepriv->state = FESTATE_RETUNE; ++ ++ /* Request the search algorithm to search */ ++ fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; ++ ++ dvb_frontend_clear_events(fe); ++ dvb_frontend_add_event(fe, 0); ++ dvb_frontend_wakeup(fe); ++ fepriv->status = 0; ++ ++ return 0; ++} ++ ++ ++static int dvb_frontend_ioctl_legacy(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int cb_err, err = -EOPNOTSUPP; ++ ++ if (fe->dvb->fe_ioctl_override) { ++ cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, ++ DVB_FE_IOCTL_PRE); ++ if (cb_err < 0) ++ return cb_err; ++ if (cb_err > 0) ++ return 0; ++ /* fe_ioctl_override returning 0 allows ++ * dvb-core to continue handling the ioctl */ ++ } ++ ++ switch (cmd) { ++ case FE_GET_INFO: { ++ struct dvb_frontend_info* info = parg; ++ ++ memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); ++ dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); ++ ++ /* ++ * Associate the 4 delivery systems supported by DVBv3 ++ * API with their DVBv5 counterpart. For the other standards, ++ * use the closest type, assuming that it would hopefully ++ * work with a DVBv3 application. ++ * It should be noticed that, on multi-frontend devices with ++ * different types (terrestrial and cable, for example), ++ * a pure DVBv3 application won't be able to use all delivery ++ * systems. Yet, changing the DVBv5 cache to the other delivery ++ * system should be enough for making it work. ++ */ ++ switch (dvbv3_type(c->delivery_system)) { ++ case DVBV3_QPSK: ++ info->type = FE_QPSK; ++ break; ++ case DVBV3_ATSC: ++ info->type = FE_ATSC; ++ break; ++ case DVBV3_QAM: ++ info->type = FE_QAM; ++ break; ++ case DVBV3_OFDM: ++ info->type = FE_OFDM; ++ break; ++ default: ++ printk(KERN_ERR ++ "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", ++ __func__, c->delivery_system); ++ fe->ops.info.type = FE_OFDM; ++ } ++ dprintk("current delivery system on cache: %d, V3 type: %d\n", ++ c->delivery_system, fe->ops.info.type); ++ ++ /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't ++ * do it, it is done for it. */ ++ info->caps |= FE_CAN_INVERSION_AUTO; ++ err = 0; ++ break; ++ } ++ ++ case FE_READ_STATUS: { ++ fe_status_t* status = parg; ++ ++ /* if retune was requested but hasn't occurred yet, prevent ++ * that user get signal state from previous tuning */ ++ if (fepriv->state == FESTATE_RETUNE || ++ fepriv->state == FESTATE_ERROR) { ++ err=0; ++ *status = 0; ++ break; ++ } ++ ++ if (fe->ops.read_status) ++ err = fe->ops.read_status(fe, status); ++ break; ++ } ++ case FE_READ_BER: ++ if (fe->ops.read_ber) ++ err = fe->ops.read_ber(fe, (__u32*) parg); ++ break; ++ ++ case FE_READ_SIGNAL_STRENGTH: ++ if (fe->ops.read_signal_strength) ++ err = fe->ops.read_signal_strength(fe, (__u16*) parg); ++ break; ++ ++ case FE_READ_SNR: ++ if (fe->ops.read_snr) ++ err = fe->ops.read_snr(fe, (__u16*) parg); ++ break; ++ ++ case FE_READ_UNCORRECTED_BLOCKS: ++ if (fe->ops.read_ucblocks) ++ err = fe->ops.read_ucblocks(fe, (__u32*) parg); ++ break; ++ ++ ++ case FE_DISEQC_RESET_OVERLOAD: ++ if (fe->ops.diseqc_reset_overload) { ++ err = fe->ops.diseqc_reset_overload(fe); ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_DISEQC_SEND_MASTER_CMD: ++ if (fe->ops.diseqc_send_master_cmd) { ++ err = fe->ops.diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg); ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_DISEQC_SEND_BURST: ++ if (fe->ops.diseqc_send_burst) { ++ err = fe->ops.diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg); ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_SET_TONE: ++ if (fe->ops.set_tone) { ++ err = fe->ops.set_tone(fe, (fe_sec_tone_mode_t) parg); ++ fepriv->tone = (fe_sec_tone_mode_t) parg; ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_SET_VOLTAGE: ++ if (fe->ops.set_voltage) { ++ err = fe->ops.set_voltage(fe, (fe_sec_voltage_t) parg); ++ fepriv->voltage = (fe_sec_voltage_t) parg; ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_DISHNETWORK_SEND_LEGACY_CMD: ++ if (fe->ops.dishnetwork_send_legacy_command) { ++ err = fe->ops.dishnetwork_send_legacy_command(fe, (unsigned long) parg); ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } else if (fe->ops.set_voltage) { ++ /* ++ * NOTE: This is a fallback condition. Some frontends ++ * (stv0299 for instance) take longer than 8msec to ++ * respond to a set_voltage command. Those switches ++ * need custom routines to switch properly. For all ++ * other frontends, the following should work ok. ++ * Dish network legacy switches (as used by Dish500) ++ * are controlled by sending 9-bit command words ++ * spaced 8msec apart. ++ * the actual command word is switch/port dependent ++ * so it is up to the userspace application to send ++ * the right command. ++ * The command must always start with a '0' after ++ * initialization, so parg is 8 bits and does not ++ * include the initialization or start bit ++ */ ++ unsigned long swcmd = ((unsigned long) parg) << 1; ++ struct timeval nexttime; ++ struct timeval tv[10]; ++ int i; ++ u8 last = 1; ++ if (dvb_frontend_debug) ++ printk("%s switch command: 0x%04lx\n", __func__, swcmd); ++ do_gettimeofday(&nexttime); ++ if (dvb_frontend_debug) ++ memcpy(&tv[0], &nexttime, sizeof(struct timeval)); ++ /* before sending a command, initialize by sending ++ * a 32ms 18V to the switch ++ */ ++ fe->ops.set_voltage(fe, SEC_VOLTAGE_18); ++ dvb_frontend_sleep_until(&nexttime, 32000); ++ ++ for (i = 0; i < 9; i++) { ++ if (dvb_frontend_debug) ++ do_gettimeofday(&tv[i + 1]); ++ if ((swcmd & 0x01) != last) { ++ /* set voltage to (last ? 13V : 18V) */ ++ fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); ++ last = (last) ? 0 : 1; ++ } ++ swcmd = swcmd >> 1; ++ if (i != 8) ++ dvb_frontend_sleep_until(&nexttime, 8000); ++ } ++ if (dvb_frontend_debug) { ++ printk("%s(%d): switch delay (should be 32k followed by all 8k\n", ++ __func__, fe->dvb->num); ++ for (i = 1; i < 10; i++) ++ printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); ++ } ++ err = 0; ++ fepriv->state = FESTATE_DISEQC; ++ fepriv->status = 0; ++ } ++ break; ++ ++ case FE_DISEQC_RECV_SLAVE_REPLY: ++ if (fe->ops.diseqc_recv_slave_reply) ++ err = fe->ops.diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg); ++ break; ++ ++ case FE_ENABLE_HIGH_LNB_VOLTAGE: ++ if (fe->ops.enable_high_lnb_voltage) ++ err = fe->ops.enable_high_lnb_voltage(fe, (long) parg); ++ break; ++ ++ case FE_SET_FRONTEND: ++ err = set_delivery_system(fe, SYS_UNDEFINED); ++ if (err) ++ break; ++ ++ err = dtv_property_cache_sync(fe, c, parg); ++ if (err) ++ break; ++ err = dtv_set_frontend(fe); ++ break; ++ case FE_GET_EVENT: ++ err = dvb_frontend_get_event (fe, parg, file->f_flags); ++ break; ++ ++ case FE_GET_FRONTEND: ++ err = dtv_get_frontend(fe, parg); ++ break; ++ ++ case FE_SET_FRONTEND_TUNE_MODE: ++ fepriv->tune_mode_flags = (unsigned long) parg; ++ err = 0; ++ break; ++ }; ++ ++ if (fe->dvb->fe_ioctl_override) { ++ cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, ++ DVB_FE_IOCTL_POST); ++ if (cb_err < 0) ++ return cb_err; ++ } ++ ++ return err; ++} ++ ++ ++static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ poll_wait (file, &fepriv->events.wait_queue, wait); ++ ++ if (fepriv->events.eventw != fepriv->events.eventr) ++ return (POLLIN | POLLRDNORM | POLLPRI); ++ ++ return 0; ++} ++ ++static int dvb_frontend_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ struct dvb_adapter *adapter = fe->dvb; ++ int ret; ++ ++ dprintk ("%s\n", __func__); ++ if (fepriv->exit == DVB_FE_DEVICE_REMOVED) ++ return -ENODEV; ++ ++ if (adapter->mfe_shared) { ++ mutex_lock (&adapter->mfe_lock); ++ ++ if (adapter->mfe_dvbdev == NULL) ++ adapter->mfe_dvbdev = dvbdev; ++ ++ else if (adapter->mfe_dvbdev != dvbdev) { ++ struct dvb_device ++ *mfedev = adapter->mfe_dvbdev; ++ struct dvb_frontend ++ *mfe = mfedev->priv; ++ struct dvb_frontend_private ++ *mfepriv = mfe->frontend_priv; ++ int mferetry = (dvb_mfe_wait_time << 1); ++ ++ mutex_unlock (&adapter->mfe_lock); ++ while (mferetry-- && (mfedev->users != -1 || ++ mfepriv->thread != NULL)) { ++ if(msleep_interruptible(500)) { ++ if(signal_pending(current)) ++ return -EINTR; ++ } ++ } ++ ++ mutex_lock (&adapter->mfe_lock); ++ if(adapter->mfe_dvbdev != dvbdev) { ++ mfedev = adapter->mfe_dvbdev; ++ mfe = mfedev->priv; ++ mfepriv = mfe->frontend_priv; ++ if (mfedev->users != -1 || ++ mfepriv->thread != NULL) { ++ mutex_unlock (&adapter->mfe_lock); ++ return -EBUSY; ++ } ++ adapter->mfe_dvbdev = dvbdev; ++ } ++ } ++ } ++ ++ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) { ++ if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0) ++ goto err0; ++ ++ /* If we took control of the bus, we need to force ++ reinitialization. This is because many ts_bus_ctrl() ++ functions strobe the RESET pin on the demod, and if the ++ frontend thread already exists then the dvb_init() routine ++ won't get called (which is what usually does initial ++ register configuration). */ ++ fepriv->reinitialise = 1; ++ } ++ ++ if ((ret = dvb_generic_open (inode, file)) < 0) ++ goto err1; ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ /* normal tune mode when opened R/W */ ++ fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT; ++ fepriv->tone = -1; ++ fepriv->voltage = -1; ++ ++ ret = dvb_frontend_start (fe); ++ if (ret) ++ goto err2; ++ ++ /* empty event queue */ ++ fepriv->events.eventr = fepriv->events.eventw = 0; ++ } ++ ++ if (adapter->mfe_shared) ++ mutex_unlock (&adapter->mfe_lock); ++ return ret; ++ ++err2: ++ dvb_generic_release(inode, file); ++err1: ++ if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) ++ fe->ops.ts_bus_ctrl(fe, 0); ++err0: ++ if (adapter->mfe_shared) ++ mutex_unlock (&adapter->mfe_lock); ++ return ret; ++} ++ ++static int dvb_frontend_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_frontend *fe = dvbdev->priv; ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ int ret; ++ ++ dprintk ("%s\n", __func__); ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ fepriv->release_jiffies = jiffies; ++ mb(); ++ } ++ ++ ret = dvb_generic_release (inode, file); ++ ++ if (dvbdev->users == -1) { ++ wake_up(&fepriv->wait_queue); ++ if (fepriv->exit != DVB_FE_NO_EXIT) { ++ fops_put(file->f_op); ++ file->f_op = NULL; ++ wake_up(&dvbdev->wait_queue); ++ } ++ if (fe->ops.ts_bus_ctrl) ++ fe->ops.ts_bus_ctrl(fe, 0); ++ } ++ ++ return ret; ++} ++ ++static const struct file_operations dvb_frontend_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .poll = dvb_frontend_poll, ++ .open = dvb_frontend_open, ++ .release = dvb_frontend_release, ++ .llseek = noop_llseek, ++}; ++ ++int dvb_frontend_suspend(struct dvb_frontend *fe) ++{ ++ int ret = 0; ++ ++ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, ++ fe->id); ++ ++ if (fe->ops.tuner_ops.sleep) ++ ret = fe->ops.tuner_ops.sleep(fe); ++ ++ if (fe->ops.sleep) ++ ret = fe->ops.sleep(fe); ++ ++ return ret; ++} ++EXPORT_SYMBOL(dvb_frontend_suspend); ++ ++int dvb_frontend_resume(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ int ret = 0; ++ ++ dev_dbg(fe->dvb->device, "%s: adap=%d fe=%d\n", __func__, fe->dvb->num, ++ fe->id); ++ ++ if (fe->ops.init) ++ ret = fe->ops.init(fe); ++ ++ if (fe->ops.tuner_ops.init) ++ ret = fe->ops.tuner_ops.init(fe); ++ ++ fepriv->state = FESTATE_RETUNE; ++ dvb_frontend_wakeup(fe); ++ ++ return ret; ++} ++EXPORT_SYMBOL(dvb_frontend_resume); ++ ++ ++int dvb_register_frontend(struct dvb_adapter* dvb, ++ struct dvb_frontend* fe) ++{ ++ struct dvb_frontend_private *fepriv; ++ static const struct dvb_device dvbdev_template = { ++ .users = ~0, ++ .writers = 1, ++ .readers = (~0)-1, ++ .fops = &dvb_frontend_fops, ++ .kernel_ioctl = dvb_frontend_ioctl ++ }; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (mutex_lock_interruptible(&frontend_mutex)) ++ return -ERESTARTSYS; ++ ++ fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL); ++ if (fe->frontend_priv == NULL) { ++ mutex_unlock(&frontend_mutex); ++ return -ENOMEM; ++ } ++ fepriv = fe->frontend_priv; ++ ++ sema_init(&fepriv->sem, 1); ++ init_waitqueue_head (&fepriv->wait_queue); ++ init_waitqueue_head (&fepriv->events.wait_queue); ++ mutex_init(&fepriv->events.mtx); ++ fe->dvb = dvb; ++ fepriv->inversion = INVERSION_OFF; ++ ++ printk ("DVB: registering adapter %i frontend %i (%s)...\n", ++ fe->dvb->num, ++ fe->id, ++ fe->ops.info.name); ++ ++ dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, ++ fe, DVB_DEVICE_FRONTEND); ++ ++ /* ++ * Initialize the cache to the proper values according with the ++ * first supported delivery system (ops->delsys[0]) ++ */ ++ ++ fe->dtv_property_cache.delivery_system = fe->ops.delsys[0]; ++ dvb_frontend_clear_cache(fe); ++ ++ mutex_unlock(&frontend_mutex); ++ return 0; ++} ++EXPORT_SYMBOL(dvb_register_frontend); ++ ++int dvb_unregister_frontend(struct dvb_frontend* fe) ++{ ++ struct dvb_frontend_private *fepriv = fe->frontend_priv; ++ dprintk ("%s\n", __func__); ++ ++ mutex_lock(&frontend_mutex); ++ dvb_frontend_stop (fe); ++ mutex_unlock(&frontend_mutex); ++ ++ if (fepriv->dvbdev->users < -1) ++ wait_event(fepriv->dvbdev->wait_queue, ++ fepriv->dvbdev->users==-1); ++ ++ mutex_lock(&frontend_mutex); ++ dvb_unregister_device (fepriv->dvbdev); ++ ++ /* fe is invalid now */ ++ kfree(fepriv); ++ mutex_unlock(&frontend_mutex); ++ return 0; ++} ++EXPORT_SYMBOL(dvb_unregister_frontend); ++ ++#ifdef CONFIG_MEDIA_ATTACH ++void dvb_frontend_detach(struct dvb_frontend* fe) ++{ ++ void *ptr; ++ ++ if (fe->ops.release_sec) { ++ fe->ops.release_sec(fe); ++ symbol_put_addr(fe->ops.release_sec); ++ } ++ if (fe->ops.tuner_ops.release) { ++ fe->ops.tuner_ops.release(fe); ++ symbol_put_addr(fe->ops.tuner_ops.release); ++ } ++ if (fe->ops.analog_ops.release) { ++ fe->ops.analog_ops.release(fe); ++ symbol_put_addr(fe->ops.analog_ops.release); ++ } ++ ptr = (void*)fe->ops.release; ++ if (ptr) { ++ fe->ops.release(fe); ++ symbol_put_addr(ptr); ++ } ++} ++#else ++void dvb_frontend_detach(struct dvb_frontend* fe) ++{ ++ if (fe->ops.release_sec) ++ fe->ops.release_sec(fe); ++ if (fe->ops.tuner_ops.release) ++ fe->ops.tuner_ops.release(fe); ++ if (fe->ops.analog_ops.release) ++ fe->ops.analog_ops.release(fe); ++ if (fe->ops.release) ++ fe->ops.release(fe); ++} ++#endif ++EXPORT_SYMBOL(dvb_frontend_detach); +diff --git a/drivers/media/dvb-core/dvb_frontend.h b/drivers/media/dvb-core/dvb_frontend.h +new file mode 100644 +index 0000000..6019b4f +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_frontend.h +@@ -0,0 +1,406 @@ ++/* ++ * dvb_frontend.h ++ * ++ * Copyright (C) 2001 convergence integrated media GmbH ++ * Copyright (C) 2004 convergence GmbH ++ * ++ * Written by Ralph Metzler ++ * Overhauled by Holger Waechtler ++ * Kernel I2C stuff by Michael Hunold ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _DVB_FRONTEND_H_ ++#define _DVB_FRONTEND_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dvbdev.h" ++ ++/* ++ * Maximum number of Delivery systems per frontend. It ++ * should be smaller or equal to 32 ++ */ ++#define MAX_DELSYS 8 ++ ++struct dvb_frontend_tune_settings { ++ int min_delay_ms; ++ int step_size; ++ int max_drift; ++}; ++ ++struct dvb_frontend; ++ ++struct dvb_tuner_info { ++ char name[128]; ++ ++ u32 frequency_min; ++ u32 frequency_max; ++ u32 frequency_step; ++ ++ u32 bandwidth_min; ++ u32 bandwidth_max; ++ u32 bandwidth_step; ++}; ++ ++struct analog_parameters { ++ unsigned int frequency; ++ unsigned int mode; ++ unsigned int audmode; ++ u64 std; ++}; ++ ++enum dvbfe_modcod { ++ DVBFE_MODCOD_DUMMY_PLFRAME = 0, ++ DVBFE_MODCOD_QPSK_1_4, ++ DVBFE_MODCOD_QPSK_1_3, ++ DVBFE_MODCOD_QPSK_2_5, ++ DVBFE_MODCOD_QPSK_1_2, ++ DVBFE_MODCOD_QPSK_3_5, ++ DVBFE_MODCOD_QPSK_2_3, ++ DVBFE_MODCOD_QPSK_3_4, ++ DVBFE_MODCOD_QPSK_4_5, ++ DVBFE_MODCOD_QPSK_5_6, ++ DVBFE_MODCOD_QPSK_8_9, ++ DVBFE_MODCOD_QPSK_9_10, ++ DVBFE_MODCOD_8PSK_3_5, ++ DVBFE_MODCOD_8PSK_2_3, ++ DVBFE_MODCOD_8PSK_3_4, ++ DVBFE_MODCOD_8PSK_5_6, ++ DVBFE_MODCOD_8PSK_8_9, ++ DVBFE_MODCOD_8PSK_9_10, ++ DVBFE_MODCOD_16APSK_2_3, ++ DVBFE_MODCOD_16APSK_3_4, ++ DVBFE_MODCOD_16APSK_4_5, ++ DVBFE_MODCOD_16APSK_5_6, ++ DVBFE_MODCOD_16APSK_8_9, ++ DVBFE_MODCOD_16APSK_9_10, ++ DVBFE_MODCOD_32APSK_3_4, ++ DVBFE_MODCOD_32APSK_4_5, ++ DVBFE_MODCOD_32APSK_5_6, ++ DVBFE_MODCOD_32APSK_8_9, ++ DVBFE_MODCOD_32APSK_9_10, ++ DVBFE_MODCOD_RESERVED_1, ++ DVBFE_MODCOD_BPSK_1_3, ++ DVBFE_MODCOD_BPSK_1_4, ++ DVBFE_MODCOD_RESERVED_2 ++}; ++ ++enum tuner_param { ++ DVBFE_TUNER_FREQUENCY = (1 << 0), ++ DVBFE_TUNER_TUNERSTEP = (1 << 1), ++ DVBFE_TUNER_IFFREQ = (1 << 2), ++ DVBFE_TUNER_BANDWIDTH = (1 << 3), ++ DVBFE_TUNER_REFCLOCK = (1 << 4), ++ DVBFE_TUNER_IQSENSE = (1 << 5), ++ DVBFE_TUNER_DUMMY = (1 << 31) ++}; ++ ++/* ++ * ALGO_HW: (Hardware Algorithm) ++ * ---------------------------------------------------------------- ++ * Devices that support this algorithm do everything in hardware ++ * and no software support is needed to handle them. ++ * Requesting these devices to LOCK is the only thing required, ++ * device is supposed to do everything in the hardware. ++ * ++ * ALGO_SW: (Software Algorithm) ++ * ---------------------------------------------------------------- ++ * These are dumb devices, that require software to do everything ++ * ++ * ALGO_CUSTOM: (Customizable Agorithm) ++ * ---------------------------------------------------------------- ++ * Devices having this algorithm can be customized to have specific ++ * algorithms in the frontend driver, rather than simply doing a ++ * software zig-zag. In this case the zigzag maybe hardware assisted ++ * or it maybe completely done in hardware. In all cases, usage of ++ * this algorithm, in conjunction with the search and track ++ * callbacks, utilizes the driver specific algorithm. ++ * ++ * ALGO_RECOVERY: (Recovery Algorithm) ++ * ---------------------------------------------------------------- ++ * These devices have AUTO recovery capabilities from LOCK failure ++ */ ++enum dvbfe_algo { ++ DVBFE_ALGO_HW = (1 << 0), ++ DVBFE_ALGO_SW = (1 << 1), ++ DVBFE_ALGO_CUSTOM = (1 << 2), ++ DVBFE_ALGO_RECOVERY = (1 << 31) ++}; ++ ++struct tuner_state { ++ u32 frequency; ++ u32 tunerstep; ++ u32 ifreq; ++ u32 bandwidth; ++ u32 iqsense; ++ u32 refclock; ++}; ++ ++/* ++ * search callback possible return status ++ * ++ * DVBFE_ALGO_SEARCH_SUCCESS ++ * The frontend search algorithm completed and returned successfully ++ * ++ * DVBFE_ALGO_SEARCH_ASLEEP ++ * The frontend search algorithm is sleeping ++ * ++ * DVBFE_ALGO_SEARCH_FAILED ++ * The frontend search for a signal failed ++ * ++ * DVBFE_ALGO_SEARCH_INVALID ++ * The frontend search algorith was probably supplied with invalid ++ * parameters and the search is an invalid one ++ * ++ * DVBFE_ALGO_SEARCH_ERROR ++ * The frontend search algorithm failed due to some error ++ * ++ * DVBFE_ALGO_SEARCH_AGAIN ++ * The frontend search algorithm was requested to search again ++ */ ++enum dvbfe_search { ++ DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0), ++ DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1), ++ DVBFE_ALGO_SEARCH_FAILED = (1 << 2), ++ DVBFE_ALGO_SEARCH_INVALID = (1 << 3), ++ DVBFE_ALGO_SEARCH_AGAIN = (1 << 4), ++ DVBFE_ALGO_SEARCH_ERROR = (1 << 31), ++}; ++ ++ ++struct dvb_tuner_ops { ++ ++ struct dvb_tuner_info info; ++ ++ int (*release)(struct dvb_frontend *fe); ++ int (*init)(struct dvb_frontend *fe); ++ int (*sleep)(struct dvb_frontend *fe); ++ ++ /** This is for simple PLLs - set all parameters in one go. */ ++ int (*set_params)(struct dvb_frontend *fe); ++ int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p); ++ ++ /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ ++ int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len); ++ ++ /** This is to allow setting tuner-specific configs */ ++ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg); ++ ++ int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); ++ int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); ++ int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency); ++ ++#define TUNER_STATUS_LOCKED 1 ++#define TUNER_STATUS_STEREO 2 ++ int (*get_status)(struct dvb_frontend *fe, u32 *status); ++ int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength); ++ ++ /** These are provided separately from set_params in order to facilitate silicon ++ * tuners which require sophisticated tuning loops, controlling each parameter separately. */ ++ int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); ++ int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); ++ ++ /* ++ * These are provided separately from set_params in order to facilitate silicon ++ * tuners which require sophisticated tuning loops, controlling each parameter separately. ++ */ ++ int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); ++ int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); ++}; ++ ++struct analog_demod_info { ++ char *name; ++}; ++ ++struct analog_demod_ops { ++ ++ struct analog_demod_info info; ++ ++ void (*set_params)(struct dvb_frontend *fe, ++ struct analog_parameters *params); ++ int (*has_signal)(struct dvb_frontend *fe); ++ int (*get_afc)(struct dvb_frontend *fe); ++ void (*tuner_status)(struct dvb_frontend *fe); ++ void (*standby)(struct dvb_frontend *fe); ++ void (*release)(struct dvb_frontend *fe); ++ int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable); ++ ++ /** This is to allow setting tuner-specific configuration */ ++ int (*set_config)(struct dvb_frontend *fe, void *priv_cfg); ++}; ++ ++struct dtv_frontend_properties; ++ ++struct dvb_frontend_ops { ++ ++ struct dvb_frontend_info info; ++ ++ u8 delsys[MAX_DELSYS]; ++ ++ void (*release)(struct dvb_frontend* fe); ++ void (*release_sec)(struct dvb_frontend* fe); ++ ++ int (*init)(struct dvb_frontend* fe); ++ int (*sleep)(struct dvb_frontend* fe); ++ ++ int (*write)(struct dvb_frontend* fe, const u8 buf[], int len); ++ ++ /* if this is set, it overrides the default swzigzag */ ++ int (*tune)(struct dvb_frontend* fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status); ++ /* get frontend tuning algorithm from the module */ ++ enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe); ++ ++ /* these two are only used for the swzigzag code */ ++ int (*set_frontend)(struct dvb_frontend *fe); ++ int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings); ++ ++ int (*get_frontend)(struct dvb_frontend *fe); ++ ++ int (*read_status)(struct dvb_frontend* fe, fe_status_t* status); ++ int (*read_ber)(struct dvb_frontend* fe, u32* ber); ++ int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength); ++ int (*read_snr)(struct dvb_frontend* fe, u16* snr); ++ int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks); ++ ++ int (*diseqc_reset_overload)(struct dvb_frontend* fe); ++ int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); ++ int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply); ++ int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); ++ int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); ++ int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); ++ int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); ++ int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); ++ int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); ++ int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire); ++ ++ /* These callbacks are for devices that implement their own ++ * tuning algorithms, rather than a simple swzigzag ++ */ ++ enum dvbfe_search (*search)(struct dvb_frontend *fe); ++ ++ struct dvb_tuner_ops tuner_ops; ++ struct analog_demod_ops analog_ops; ++ ++ int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp); ++ int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp); ++}; ++ ++#ifdef __DVB_CORE__ ++#define MAX_EVENT 8 ++ ++struct dvb_fe_events { ++ struct dvb_frontend_event events[MAX_EVENT]; ++ int eventw; ++ int eventr; ++ int overflow; ++ wait_queue_head_t wait_queue; ++ struct mutex mtx; ++}; ++#endif ++ ++struct dtv_frontend_properties { ++ ++ /* Cache State */ ++ u32 state; ++ ++ u32 frequency; ++ fe_modulation_t modulation; ++ ++ fe_sec_voltage_t voltage; ++ fe_sec_tone_mode_t sectone; ++ fe_spectral_inversion_t inversion; ++ fe_code_rate_t fec_inner; ++ fe_transmit_mode_t transmission_mode; ++ u32 bandwidth_hz; /* 0 = AUTO */ ++ fe_guard_interval_t guard_interval; ++ fe_hierarchy_t hierarchy; ++ u32 symbol_rate; ++ fe_code_rate_t code_rate_HP; ++ fe_code_rate_t code_rate_LP; ++ ++ fe_pilot_t pilot; ++ fe_rolloff_t rolloff; ++ ++ fe_delivery_system_t delivery_system; ++ ++ /* ISDB-T specifics */ ++ u8 isdbt_partial_reception; ++ u8 isdbt_sb_mode; ++ u8 isdbt_sb_subchannel; ++ u32 isdbt_sb_segment_idx; ++ u32 isdbt_sb_segment_count; ++ u8 isdbt_layer_enabled; ++ struct { ++ u8 segment_count; ++ fe_code_rate_t fec; ++ fe_modulation_t modulation; ++ u8 interleaving; ++ } layer[3]; ++ ++ u32 stream_id; ++ ++ /* DVB-T2 specifics */ ++ u32 dvbt2_plp_id; ++}; ++ ++struct dvb_frontend { ++ struct dvb_frontend_ops ops; ++ struct dvb_adapter *dvb; ++ void *demodulator_priv; ++ void *tuner_priv; ++ void *frontend_priv; ++ void *sec_priv; ++ void *analog_demod_priv; ++ struct dtv_frontend_properties dtv_property_cache; ++#define DVB_FRONTEND_COMPONENT_TUNER 0 ++#define DVB_FRONTEND_COMPONENT_DEMOD 1 ++ int (*callback)(void *adapter_priv, int component, int cmd, int arg); ++ int id; ++}; ++ ++extern int dvb_register_frontend(struct dvb_adapter *dvb, ++ struct dvb_frontend *fe); ++ ++extern int dvb_unregister_frontend(struct dvb_frontend *fe); ++ ++extern void dvb_frontend_detach(struct dvb_frontend *fe); ++ ++extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); ++extern int dvb_frontend_suspend(struct dvb_frontend *fe); ++extern int dvb_frontend_resume(struct dvb_frontend *fe); ++ ++ ++extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); ++extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); ++ ++#endif +diff --git a/drivers/media/dvb-core/dvb_math.c b/drivers/media/dvb-core/dvb_math.c +new file mode 100644 +index 0000000..beb7c93 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_math.c +@@ -0,0 +1,145 @@ ++/* ++ * dvb-math provides some complex fixed-point math ++ * operations shared between the dvb related stuff ++ * ++ * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) ++ * ++ * This library 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 Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include "dvb_math.h" ++ ++static const unsigned short logtable[256] = { ++ 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, ++ 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, ++ 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6, ++ 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37, ++ 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f, ++ 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41, ++ 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1, ++ 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142, ++ 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68, ++ 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355, ++ 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c, ++ 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490, ++ 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3, ++ 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507, ++ 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe, ++ 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca, ++ 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c, ++ 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7, ++ 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c, ++ 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c, ++ 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a, ++ 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065, ++ 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730, ++ 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc, ++ 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469, ++ 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9, ++ 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c, ++ 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765, ++ 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83, ++ 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387, ++ 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973, ++ 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47 ++}; ++ ++unsigned int intlog2(u32 value) ++{ ++ /** ++ * returns: log2(value) * 2^24 ++ * wrong result if value = 0 (log2(0) is undefined) ++ */ ++ unsigned int msb; ++ unsigned int logentry; ++ unsigned int significand; ++ unsigned int interpolation; ++ ++ if (unlikely(value == 0)) { ++ WARN_ON(1); ++ return 0; ++ } ++ ++ /* first detect the msb (count begins at 0) */ ++ msb = fls(value) - 1; ++ ++ /** ++ * now we use a logtable after the following method: ++ * ++ * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24 ++ * where x = msb and therefore 1 <= y < 2 ++ * first y is determined by shifting the value left ++ * so that msb is bit 31 ++ * 0x00231f56 -> 0x8C7D5800 ++ * the result is y * 2^31 -> "significand" ++ * then the highest 9 bits are used for a table lookup ++ * the highest bit is discarded because it's always set ++ * the highest nine bits in our example are 100011000 ++ * so we would use the entry 0x18 ++ */ ++ significand = value << (31 - msb); ++ logentry = (significand >> 23) & 0xff; ++ ++ /** ++ * last step we do is interpolation because of the ++ * limitations of the log table the error is that part of ++ * the significand which isn't used for lookup then we ++ * compute the ratio between the error and the next table entry ++ * and interpolate it between the log table entry used and the ++ * next one the biggest error possible is 0x7fffff ++ * (in our example it's 0x7D5800) ++ * needed value for next table entry is 0x800000 ++ * so the interpolation is ++ * (error / 0x800000) * (logtable_next - logtable_current) ++ * in the implementation the division is moved to the end for ++ * better accuracy there is also an overflow correction if ++ * logtable_next is 256 ++ */ ++ interpolation = ((significand & 0x7fffff) * ++ ((logtable[(logentry + 1) & 0xff] - ++ logtable[logentry]) & 0xffff)) >> 15; ++ ++ /* now we return the result */ ++ return ((msb << 24) + (logtable[logentry] << 8) + interpolation); ++} ++EXPORT_SYMBOL(intlog2); ++ ++unsigned int intlog10(u32 value) ++{ ++ /** ++ * returns: log10(value) * 2^24 ++ * wrong result if value = 0 (log10(0) is undefined) ++ */ ++ u64 log; ++ ++ if (unlikely(value == 0)) { ++ WARN_ON(1); ++ return 0; ++ } ++ ++ log = intlog2(value); ++ ++ /** ++ * we use the following method: ++ * log10(x) = log2(x) * log10(2) ++ */ ++ ++ return (log * 646456993) >> 31; ++} ++EXPORT_SYMBOL(intlog10); +diff --git a/drivers/media/dvb-core/dvb_math.h b/drivers/media/dvb-core/dvb_math.h +new file mode 100644 +index 0000000..aecc867 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_math.h +@@ -0,0 +1,58 @@ ++/* ++ * dvb-math provides some complex fixed-point math ++ * operations shared between the dvb related stuff ++ * ++ * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) ++ * ++ * This library 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 Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __DVB_MATH_H ++#define __DVB_MATH_H ++ ++#include ++ ++/** ++ * computes log2 of a value; the result is shifted left by 24 bits ++ * ++ * to use rational values you can use the following method: ++ * intlog2(value) = intlog2(value * 2^x) - x * 2^24 ++ * ++ * example: intlog2(8) will give 3 << 24 = 3 * 2^24 ++ * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 ++ * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 ++ * ++ * @param value The value (must be != 0) ++ * @return log2(value) * 2^24 ++ */ ++extern unsigned int intlog2(u32 value); ++ ++/** ++ * computes log10 of a value; the result is shifted left by 24 bits ++ * ++ * to use rational values you can use the following method: ++ * intlog10(value) = intlog10(value * 10^x) - x * 2^24 ++ * ++ * example: intlog10(1000) will give 3 << 24 = 3 * 2^24 ++ * due to the implementation intlog10(1000) might be not exactly 3 * 2^24 ++ * ++ * look at intlog2 for similar examples ++ * ++ * @param value The value (must be != 0) ++ * @return log10(value) * 2^24 ++ */ ++extern unsigned int intlog10(u32 value); ++ ++#endif +diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c +new file mode 100644 +index 0000000..8766ce8 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_net.c +@@ -0,0 +1,1516 @@ ++/* ++ * dvb_net.c ++ * ++ * Copyright (C) 2001 Convergence integrated media GmbH ++ * Ralph Metzler ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * ULE Decapsulation code: ++ * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH. ++ * and Department of Scientific Computing ++ * Paris Lodron University of Salzburg. ++ * Hilmar Linder ++ * and Wolfram Stering ++ * ++ * ULE Decaps according to RFC 4326. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/* ++ * ULE ChangeLog: ++ * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt ++ * ++ * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt: ++ * ULE Extension header handling. ++ * Bugreports by Moritz Vieth and Hanno Tersteegen, ++ * Fraunhofer Institute for Open Communication Systems ++ * Competence Center for Advanced Satellite Communications. ++ * Bugfixes and robustness improvements. ++ * Filtering on dest MAC addresses, if present (D-Bit = 0) ++ * ULE_DEBUG compile-time option. ++ * Apr 2006: cp v3: Bugfixes and compliency with RFC 4326 (ULE) by ++ * Christian Praehauser , ++ * Paris Lodron University of Salzburg. ++ */ ++ ++/* ++ * FIXME / TODO (dvb_net.c): ++ * ++ * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_demux.h" ++#include "dvb_net.h" ++ ++static int dvb_net_debug; ++module_param(dvb_net_debug, int, 0444); ++MODULE_PARM_DESC(dvb_net_debug, "enable debug messages"); ++ ++#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0) ++ ++ ++static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) ++{ ++ unsigned int j; ++ for (j = 0; j < cnt; j++) ++ c = crc32_be( c, iov[j].iov_base, iov[j].iov_len ); ++ return c; ++} ++ ++ ++#define DVB_NET_MULTICAST_MAX 10 ++ ++#undef ULE_DEBUG ++ ++#ifdef ULE_DEBUG ++ ++#define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" ++#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5] ++ ++#define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) ++ ++static void hexdump( const unsigned char *buf, unsigned short len ) ++{ ++ char str[80], octet[10]; ++ int ofs, i, l; ++ ++ for (ofs = 0; ofs < len; ofs += 16) { ++ sprintf( str, "%03d: ", ofs ); ++ ++ for (i = 0; i < 16; i++) { ++ if ((i + ofs) < len) ++ sprintf( octet, "%02x ", buf[ofs + i] ); ++ else ++ strcpy( octet, " " ); ++ ++ strcat( str, octet ); ++ } ++ strcat( str, " " ); ++ l = strlen( str ); ++ ++ for (i = 0; (i < 16) && ((i + ofs) < len); i++) ++ str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.'; ++ ++ str[l] = '\0'; ++ printk( KERN_WARNING "%s\n", str ); ++ } ++} ++ ++#endif ++ ++struct dvb_net_priv { ++ int in_use; ++ u16 pid; ++ struct net_device *net; ++ struct dvb_net *host; ++ struct dmx_demux *demux; ++ struct dmx_section_feed *secfeed; ++ struct dmx_section_filter *secfilter; ++ struct dmx_ts_feed *tsfeed; ++ int multi_num; ++ struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX]; ++ unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6]; ++ int rx_mode; ++#define RX_MODE_UNI 0 ++#define RX_MODE_MULTI 1 ++#define RX_MODE_ALL_MULTI 2 ++#define RX_MODE_PROMISC 3 ++ struct work_struct set_multicast_list_wq; ++ struct work_struct restart_net_feed_wq; ++ unsigned char feedtype; /* Either FEED_TYPE_ or FEED_TYPE_ULE */ ++ int need_pusi; /* Set to 1, if synchronization on PUSI required. */ ++ unsigned char tscc; /* TS continuity counter after sync on PUSI. */ ++ struct sk_buff *ule_skb; /* ULE SNDU decodes into this buffer. */ ++ unsigned char *ule_next_hdr; /* Pointer into skb to next ULE extension header. */ ++ unsigned short ule_sndu_len; /* ULE SNDU length in bytes, w/o D-Bit. */ ++ unsigned short ule_sndu_type; /* ULE SNDU type field, complete. */ ++ unsigned char ule_sndu_type_1; /* ULE SNDU type field, if split across 2 TS cells. */ ++ unsigned char ule_dbit; /* Whether the DestMAC address present ++ * or not (bit is set). */ ++ unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */ ++ int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */ ++ unsigned long ts_count; /* Current ts cell counter. */ ++ struct mutex mutex; ++}; ++ ++ ++/** ++ * Determine the packet's protocol ID. The rule here is that we ++ * assume 802.3 if the type field is short enough to be a length. ++ * This is normal practice and works for any 'now in use' protocol. ++ * ++ * stolen from eth.c out of the linux kernel, hacked for dvb-device ++ * by Michael Holzt ++ */ ++static __be16 dvb_net_eth_type_trans(struct sk_buff *skb, ++ struct net_device *dev) ++{ ++ struct ethhdr *eth; ++ unsigned char *rawp; ++ ++ skb_reset_mac_header(skb); ++ skb_pull(skb,dev->hard_header_len); ++ eth = eth_hdr(skb); ++ ++ if (*eth->h_dest & 1) { ++ if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) ++ skb->pkt_type=PACKET_BROADCAST; ++ else ++ skb->pkt_type=PACKET_MULTICAST; ++ } ++ ++ if (ntohs(eth->h_proto) >= 1536) ++ return eth->h_proto; ++ ++ rawp = skb->data; ++ ++ /** ++ * This is a magic hack to spot IPX packets. Older Novell breaks ++ * the protocol design and runs IPX over 802.3 without an 802.2 LLC ++ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This ++ * won't work for fault tolerant netware but does for the rest. ++ */ ++ if (*(unsigned short *)rawp == 0xFFFF) ++ return htons(ETH_P_802_3); ++ ++ /** ++ * Real 802.2 LLC ++ */ ++ return htons(ETH_P_802_2); ++} ++ ++#define TS_SZ 188 ++#define TS_SYNC 0x47 ++#define TS_TEI 0x80 ++#define TS_SC 0xC0 ++#define TS_PUSI 0x40 ++#define TS_AF_A 0x20 ++#define TS_AF_D 0x10 ++ ++/* ULE Extension Header handlers. */ ++ ++#define ULE_TEST 0 ++#define ULE_BRIDGED 1 ++ ++#define ULE_OPTEXTHDR_PADDING 0 ++ ++static int ule_test_sndu( struct dvb_net_priv *p ) ++{ ++ return -1; ++} ++ ++static int ule_bridged_sndu( struct dvb_net_priv *p ) ++{ ++ struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr; ++ if(ntohs(hdr->h_proto) < 1536) { ++ int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data); ++ /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */ ++ if(framelen != ntohs(hdr->h_proto)) { ++ return -1; ++ } ++ } ++ /* Note: ++ * From RFC4326: ++ * "A bridged SNDU is a Mandatory Extension Header of Type 1. ++ * It must be the final (or only) extension header specified in the header chain of a SNDU." ++ * The 'ule_bridged' flag will cause the extension header processing loop to terminate. ++ */ ++ p->ule_bridged = 1; ++ return 0; ++} ++ ++static int ule_exthdr_padding(struct dvb_net_priv *p) ++{ ++ return 0; ++} ++ ++/** Handle ULE extension headers. ++ * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding. ++ * Returns: >= 0: nr. of bytes consumed by next extension header ++ * -1: Mandatory extension header that is not recognized or TEST SNDU; discard. ++ */ ++static int handle_one_ule_extension( struct dvb_net_priv *p ) ++{ ++ /* Table of mandatory extension header handlers. The header type is the index. */ ++ static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) = ++ { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, }; ++ ++ /* Table of optional extension header handlers. The header type is the index. */ ++ static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = ++ { [0] = ule_exthdr_padding, [1] = NULL, }; ++ ++ int ext_len = 0; ++ unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8; ++ unsigned char htype = p->ule_sndu_type & 0x00FF; ++ ++ /* Discriminate mandatory and optional extension headers. */ ++ if (hlen == 0) { ++ /* Mandatory extension header */ ++ if (ule_mandatory_ext_handlers[htype]) { ++ ext_len = ule_mandatory_ext_handlers[htype]( p ); ++ if(ext_len >= 0) { ++ p->ule_next_hdr += ext_len; ++ if (!p->ule_bridged) { ++ p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr); ++ p->ule_next_hdr += 2; ++ } else { ++ p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN))); ++ /* This assures the extension handling loop will terminate. */ ++ } ++ } ++ // else: extension handler failed or SNDU should be discarded ++ } else ++ ext_len = -1; /* SNDU has to be discarded. */ ++ } else { ++ /* Optional extension header. Calculate the length. */ ++ ext_len = hlen << 1; ++ /* Process the optional extension header according to its type. */ ++ if (ule_optional_ext_handlers[htype]) ++ (void)ule_optional_ext_handlers[htype]( p ); ++ p->ule_next_hdr += ext_len; ++ p->ule_sndu_type = ntohs( *(__be16 *)(p->ule_next_hdr-2) ); ++ /* ++ * note: the length of the next header type is included in the ++ * length of THIS optional extension header ++ */ ++ } ++ ++ return ext_len; ++} ++ ++static int handle_ule_extensions( struct dvb_net_priv *p ) ++{ ++ int total_ext_len = 0, l; ++ ++ p->ule_next_hdr = p->ule_skb->data; ++ do { ++ l = handle_one_ule_extension( p ); ++ if (l < 0) ++ return l; /* Stop extension header processing and discard SNDU. */ ++ total_ext_len += l; ++#ifdef ULE_DEBUG ++ dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, " ++ "l=%i, total_ext_len=%i\n", p->ule_next_hdr, ++ (int) p->ule_sndu_type, l, total_ext_len); ++#endif ++ ++ } while (p->ule_sndu_type < 1536); ++ ++ return total_ext_len; ++} ++ ++ ++/** Prepare for a new ULE SNDU: reset the decoder state. */ ++static inline void reset_ule( struct dvb_net_priv *p ) ++{ ++ p->ule_skb = NULL; ++ p->ule_next_hdr = NULL; ++ p->ule_sndu_len = 0; ++ p->ule_sndu_type = 0; ++ p->ule_sndu_type_1 = 0; ++ p->ule_sndu_remain = 0; ++ p->ule_dbit = 0xFF; ++ p->ule_bridged = 0; ++} ++ ++/** ++ * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of ++ * TS cells of a single PID. ++ */ ++static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ unsigned long skipped = 0L; ++ const u8 *ts, *ts_end, *from_where = NULL; ++ u8 ts_remain = 0, how_much = 0, new_ts = 1; ++ struct ethhdr *ethh = NULL; ++ bool error = false; ++ ++#ifdef ULE_DEBUG ++ /* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */ ++ static unsigned char ule_hist[100*TS_SZ]; ++ static unsigned char *ule_where = ule_hist, ule_dump; ++#endif ++ ++ /* For all TS cells in current buffer. ++ * Appearently, we are called for every single TS cell. ++ */ ++ for (ts = buf, ts_end = buf + buf_len; ts < ts_end; /* no default incr. */ ) { ++ ++ if (new_ts) { ++ /* We are about to process a new TS cell. */ ++ ++#ifdef ULE_DEBUG ++ if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist; ++ memcpy( ule_where, ts, TS_SZ ); ++ if (ule_dump) { ++ hexdump( ule_where, TS_SZ ); ++ ule_dump = 0; ++ } ++ ule_where += TS_SZ; ++#endif ++ ++ /* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */ ++ if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) { ++ printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n", ++ priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6); ++ ++ /* Drop partly decoded SNDU, reset state, resync on PUSI. */ ++ if (priv->ule_skb) { ++ dev_kfree_skb( priv->ule_skb ); ++ /* Prepare for next SNDU. */ ++ dev->stats.rx_errors++; ++ dev->stats.rx_frame_errors++; ++ } ++ reset_ule(priv); ++ priv->need_pusi = 1; ++ ++ /* Continue with next TS cell. */ ++ ts += TS_SZ; ++ priv->ts_count++; ++ continue; ++ } ++ ++ ts_remain = 184; ++ from_where = ts + 4; ++ } ++ /* Synchronize on PUSI, if required. */ ++ if (priv->need_pusi) { ++ if (ts[1] & TS_PUSI) { ++ /* Find beginning of first ULE SNDU in current TS cell. */ ++ /* Synchronize continuity counter. */ ++ priv->tscc = ts[3] & 0x0F; ++ /* There is a pointer field here. */ ++ if (ts[4] > ts_remain) { ++ printk(KERN_ERR "%lu: Invalid ULE packet " ++ "(pointer field %d)\n", priv->ts_count, ts[4]); ++ ts += TS_SZ; ++ priv->ts_count++; ++ continue; ++ } ++ /* Skip to destination of pointer field. */ ++ from_where = &ts[5] + ts[4]; ++ ts_remain -= 1 + ts[4]; ++ skipped = 0; ++ } else { ++ skipped++; ++ ts += TS_SZ; ++ priv->ts_count++; ++ continue; ++ } ++ } ++ ++ if (new_ts) { ++ /* Check continuity counter. */ ++ if ((ts[3] & 0x0F) == priv->tscc) ++ priv->tscc = (priv->tscc + 1) & 0x0F; ++ else { ++ /* TS discontinuity handling: */ ++ printk(KERN_WARNING "%lu: TS discontinuity: got %#x, " ++ "expected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); ++ /* Drop partly decoded SNDU, reset state, resync on PUSI. */ ++ if (priv->ule_skb) { ++ dev_kfree_skb( priv->ule_skb ); ++ /* Prepare for next SNDU. */ ++ // reset_ule(priv); moved to below. ++ dev->stats.rx_errors++; ++ dev->stats.rx_frame_errors++; ++ } ++ reset_ule(priv); ++ /* skip to next PUSI. */ ++ priv->need_pusi = 1; ++ continue; ++ } ++ /* If we still have an incomplete payload, but PUSI is ++ * set; some TS cells are missing. ++ * This is only possible here, if we missed exactly 16 TS ++ * cells (continuity counter wrap). */ ++ if (ts[1] & TS_PUSI) { ++ if (! priv->need_pusi) { ++ if (!(*from_where < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) { ++ /* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */ ++ printk(KERN_WARNING "%lu: Invalid pointer " ++ "field: %u.\n", priv->ts_count, *from_where); ++ ++ /* Drop partly decoded SNDU, reset state, resync on PUSI. */ ++ if (priv->ule_skb) { ++ error = true; ++ dev_kfree_skb(priv->ule_skb); ++ } ++ ++ if (error || priv->ule_sndu_remain) { ++ dev->stats.rx_errors++; ++ dev->stats.rx_frame_errors++; ++ error = false; ++ } ++ ++ reset_ule(priv); ++ priv->need_pusi = 1; ++ continue; ++ } ++ /* Skip pointer field (we're processing a ++ * packed payload). */ ++ from_where += 1; ++ ts_remain -= 1; ++ } else ++ priv->need_pusi = 0; ++ ++ if (priv->ule_sndu_remain > 183) { ++ /* Current SNDU lacks more data than there could be available in the ++ * current TS cell. */ ++ dev->stats.rx_errors++; ++ dev->stats.rx_length_errors++; ++ printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " ++ "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", ++ priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); ++ dev_kfree_skb(priv->ule_skb); ++ /* Prepare for next SNDU. */ ++ reset_ule(priv); ++ /* Resync: go to where pointer field points to: start of next ULE SNDU. */ ++ from_where += ts[4]; ++ ts_remain -= ts[4]; ++ } ++ } ++ } ++ ++ /* Check if new payload needs to be started. */ ++ if (priv->ule_skb == NULL) { ++ /* Start a new payload with skb. ++ * Find ULE header. It is only guaranteed that the ++ * length field (2 bytes) is contained in the current ++ * TS. ++ * Check ts_remain has to be >= 2 here. */ ++ if (ts_remain < 2) { ++ printk(KERN_WARNING "Invalid payload packing: only %d " ++ "bytes left in TS. Resyncing.\n", ts_remain); ++ priv->ule_sndu_len = 0; ++ priv->need_pusi = 1; ++ ts += TS_SZ; ++ continue; ++ } ++ ++ if (! priv->ule_sndu_len) { ++ /* Got at least two bytes, thus extrace the SNDU length. */ ++ priv->ule_sndu_len = from_where[0] << 8 | from_where[1]; ++ if (priv->ule_sndu_len & 0x8000) { ++ /* D-Bit is set: no dest mac present. */ ++ priv->ule_sndu_len &= 0x7FFF; ++ priv->ule_dbit = 1; ++ } else ++ priv->ule_dbit = 0; ++ ++ if (priv->ule_sndu_len < 5) { ++ printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " ++ "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); ++ dev->stats.rx_errors++; ++ dev->stats.rx_length_errors++; ++ priv->ule_sndu_len = 0; ++ priv->need_pusi = 1; ++ new_ts = 1; ++ ts += TS_SZ; ++ priv->ts_count++; ++ continue; ++ } ++ ts_remain -= 2; /* consume the 2 bytes SNDU length. */ ++ from_where += 2; ++ } ++ ++ priv->ule_sndu_remain = priv->ule_sndu_len + 2; ++ /* ++ * State of current TS: ++ * ts_remain (remaining bytes in the current TS cell) ++ * 0 ule_type is not available now, we need the next TS cell ++ * 1 the first byte of the ule_type is present ++ * >=2 full ULE header present, maybe some payload data as well. ++ */ ++ switch (ts_remain) { ++ case 1: ++ priv->ule_sndu_remain--; ++ priv->ule_sndu_type = from_where[0] << 8; ++ priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */ ++ ts_remain -= 1; from_where += 1; ++ /* Continue w/ next TS. */ ++ case 0: ++ new_ts = 1; ++ ts += TS_SZ; ++ priv->ts_count++; ++ continue; ++ ++ default: /* complete ULE header is present in current TS. */ ++ /* Extract ULE type field. */ ++ if (priv->ule_sndu_type_1) { ++ priv->ule_sndu_type_1 = 0; ++ priv->ule_sndu_type |= from_where[0]; ++ from_where += 1; /* points to payload start. */ ++ ts_remain -= 1; ++ } else { ++ /* Complete type is present in new TS. */ ++ priv->ule_sndu_type = from_where[0] << 8 | from_where[1]; ++ from_where += 2; /* points to payload start. */ ++ ts_remain -= 2; ++ } ++ break; ++ } ++ ++ /* Allocate the skb (decoder target buffer) with the correct size, as follows: ++ * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */ ++ priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN ); ++ if (priv->ule_skb == NULL) { ++ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", ++ dev->name); ++ dev->stats.rx_dropped++; ++ return; ++ } ++ ++ /* This includes the CRC32 _and_ dest mac, if !dbit. */ ++ priv->ule_sndu_remain = priv->ule_sndu_len; ++ priv->ule_skb->dev = dev; ++ /* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */ ++ skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN ); ++ } ++ ++ /* Copy data into our current skb. */ ++ how_much = min(priv->ule_sndu_remain, (int)ts_remain); ++ memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much); ++ priv->ule_sndu_remain -= how_much; ++ ts_remain -= how_much; ++ from_where += how_much; ++ ++ /* Check for complete payload. */ ++ if (priv->ule_sndu_remain <= 0) { ++ /* Check CRC32, we've got it in our skb already. */ ++ __be16 ulen = htons(priv->ule_sndu_len); ++ __be16 utype = htons(priv->ule_sndu_type); ++ const u8 *tail; ++ struct kvec iov[3] = { ++ { &ulen, sizeof ulen }, ++ { &utype, sizeof utype }, ++ { priv->ule_skb->data, priv->ule_skb->len - 4 } ++ }; ++ u32 ule_crc = ~0L, expected_crc; ++ if (priv->ule_dbit) { ++ /* Set D-bit for CRC32 verification, ++ * if it was set originally. */ ++ ulen |= htons(0x8000); ++ } ++ ++ ule_crc = iov_crc32(ule_crc, iov, 3); ++ tail = skb_tail_pointer(priv->ule_skb); ++ expected_crc = *(tail - 4) << 24 | ++ *(tail - 3) << 16 | ++ *(tail - 2) << 8 | ++ *(tail - 1); ++ if (ule_crc != expected_crc) { ++ printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n", ++ priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0); ++ ++#ifdef ULE_DEBUG ++ hexdump( iov[0].iov_base, iov[0].iov_len ); ++ hexdump( iov[1].iov_base, iov[1].iov_len ); ++ hexdump( iov[2].iov_base, iov[2].iov_len ); ++ ++ if (ule_where == ule_hist) { ++ hexdump( &ule_hist[98*TS_SZ], TS_SZ ); ++ hexdump( &ule_hist[99*TS_SZ], TS_SZ ); ++ } else if (ule_where == &ule_hist[TS_SZ]) { ++ hexdump( &ule_hist[99*TS_SZ], TS_SZ ); ++ hexdump( ule_hist, TS_SZ ); ++ } else { ++ hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ ); ++ hexdump( ule_where - TS_SZ, TS_SZ ); ++ } ++ ule_dump = 1; ++#endif ++ ++ dev->stats.rx_errors++; ++ dev->stats.rx_crc_errors++; ++ dev_kfree_skb(priv->ule_skb); ++ } else { ++ /* CRC32 verified OK. */ ++ u8 dest_addr[ETH_ALEN]; ++ static const u8 bc_addr[ETH_ALEN] = ++ { [ 0 ... ETH_ALEN-1] = 0xff }; ++ ++ /* CRC32 was OK. Remove it from skb. */ ++ priv->ule_skb->tail -= 4; ++ priv->ule_skb->len -= 4; ++ ++ if (!priv->ule_dbit) { ++ /* ++ * The destination MAC address is the ++ * next data in the skb. It comes ++ * before any extension headers. ++ * ++ * Check if the payload of this SNDU ++ * should be passed up the stack. ++ */ ++ register int drop = 0; ++ if (priv->rx_mode != RX_MODE_PROMISC) { ++ if (priv->ule_skb->data[0] & 0x01) { ++ /* multicast or broadcast */ ++ if (memcmp(priv->ule_skb->data, bc_addr, ETH_ALEN)) { ++ /* multicast */ ++ if (priv->rx_mode == RX_MODE_MULTI) { ++ int i; ++ for(i = 0; i < priv->multi_num && memcmp(priv->ule_skb->data, priv->multi_macs[i], ETH_ALEN); i++) ++ ; ++ if (i == priv->multi_num) ++ drop = 1; ++ } else if (priv->rx_mode != RX_MODE_ALL_MULTI) ++ drop = 1; /* no broadcast; */ ++ /* else: all multicast mode: accept all multicast packets */ ++ } ++ /* else: broadcast */ ++ } ++ else if (memcmp(priv->ule_skb->data, dev->dev_addr, ETH_ALEN)) ++ drop = 1; ++ /* else: destination address matches the MAC address of our receiver device */ ++ } ++ /* else: promiscuous mode; pass everything up the stack */ ++ ++ if (drop) { ++#ifdef ULE_DEBUG ++ dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n", ++ MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr)); ++#endif ++ dev_kfree_skb(priv->ule_skb); ++ goto sndu_done; ++ } ++ else ++ { ++ skb_copy_from_linear_data(priv->ule_skb, ++ dest_addr, ++ ETH_ALEN); ++ skb_pull(priv->ule_skb, ETH_ALEN); ++ } ++ } ++ ++ /* Handle ULE Extension Headers. */ ++ if (priv->ule_sndu_type < 1536) { ++ /* There is an extension header. Handle it accordingly. */ ++ int l = handle_ule_extensions(priv); ++ if (l < 0) { ++ /* Mandatory extension header unknown or TEST SNDU. Drop it. */ ++ // printk( KERN_WARNING "Dropping SNDU, extension headers.\n" ); ++ dev_kfree_skb(priv->ule_skb); ++ goto sndu_done; ++ } ++ skb_pull(priv->ule_skb, l); ++ } ++ ++ /* ++ * Construct/assure correct ethernet header. ++ * Note: in bridged mode (priv->ule_bridged != ++ * 0) we already have the (original) ethernet ++ * header at the start of the payload (after ++ * optional dest. address and any extension ++ * headers). ++ */ ++ ++ if (!priv->ule_bridged) { ++ skb_push(priv->ule_skb, ETH_HLEN); ++ ethh = (struct ethhdr *)priv->ule_skb->data; ++ if (!priv->ule_dbit) { ++ /* dest_addr buffer is only valid if priv->ule_dbit == 0 */ ++ memcpy(ethh->h_dest, dest_addr, ETH_ALEN); ++ memset(ethh->h_source, 0, ETH_ALEN); ++ } ++ else /* zeroize source and dest */ ++ memset( ethh, 0, ETH_ALEN*2 ); ++ ++ ethh->h_proto = htons(priv->ule_sndu_type); ++ } ++ /* else: skb is in correct state; nothing to do. */ ++ priv->ule_bridged = 0; ++ ++ /* Stuff into kernel's protocol stack. */ ++ priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev); ++ /* If D-bit is set (i.e. destination MAC address not present), ++ * receive the packet anyhow. */ ++ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) ++ priv->ule_skb->pkt_type = PACKET_HOST; */ ++ dev->stats.rx_packets++; ++ dev->stats.rx_bytes += priv->ule_skb->len; ++ netif_rx(priv->ule_skb); ++ } ++ sndu_done: ++ /* Prepare for next SNDU. */ ++ reset_ule(priv); ++ } ++ ++ /* More data in current TS (look at the bytes following the CRC32)? */ ++ if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) { ++ /* Next ULE SNDU starts right there. */ ++ new_ts = 0; ++ priv->ule_skb = NULL; ++ priv->ule_sndu_type_1 = 0; ++ priv->ule_sndu_len = 0; ++ // printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n", ++ // *(from_where + 0), *(from_where + 1), ++ // *(from_where + 2), *(from_where + 3)); ++ // printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0); ++ // hexdump(ts, 188); ++ } else { ++ new_ts = 1; ++ ts += TS_SZ; ++ priv->ts_count++; ++ if (priv->ule_skb == NULL) { ++ priv->need_pusi = 1; ++ priv->ule_sndu_type_1 = 0; ++ priv->ule_sndu_len = 0; ++ } ++ } ++ } /* for all available TS cells */ ++} ++ ++static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len, ++ const u8 *buffer2, size_t buffer2_len, ++ struct dmx_ts_feed *feed, enum dmx_success success) ++{ ++ struct net_device *dev = feed->priv; ++ ++ if (buffer2) ++ printk(KERN_WARNING "buffer2 not NULL: %p.\n", buffer2); ++ if (buffer1_len > 32768) ++ printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len); ++ /* printk("TS callback: %u bytes, %u TS cells @ %p.\n", ++ buffer1_len, buffer1_len / TS_SZ, buffer1); */ ++ dvb_net_ule(dev, buffer1, buffer1_len); ++ return 0; ++} ++ ++ ++static void dvb_net_sec(struct net_device *dev, ++ const u8 *pkt, int pkt_len) ++{ ++ u8 *eth; ++ struct sk_buff *skb; ++ struct net_device_stats *stats = &dev->stats; ++ int snap = 0; ++ ++ /* note: pkt_len includes a 32bit checksum */ ++ if (pkt_len < 16) { ++ printk("%s: IP/MPE packet length = %d too small.\n", ++ dev->name, pkt_len); ++ stats->rx_errors++; ++ stats->rx_length_errors++; ++ return; ++ } ++/* it seems some ISPs manage to screw up here, so we have to ++ * relax the error checks... */ ++#if 0 ++ if ((pkt[5] & 0xfd) != 0xc1) { ++ /* drop scrambled or broken packets */ ++#else ++ if ((pkt[5] & 0x3c) != 0x00) { ++ /* drop scrambled */ ++#endif ++ stats->rx_errors++; ++ stats->rx_crc_errors++; ++ return; ++ } ++ if (pkt[5] & 0x02) { ++ /* handle LLC/SNAP, see rfc-1042 */ ++ if (pkt_len < 24 || memcmp(&pkt[12], "\xaa\xaa\x03\0\0\0", 6)) { ++ stats->rx_dropped++; ++ return; ++ } ++ snap = 8; ++ } ++ if (pkt[7]) { ++ /* FIXME: assemble datagram from multiple sections */ ++ stats->rx_errors++; ++ stats->rx_frame_errors++; ++ return; ++ } ++ ++ /* we have 14 byte ethernet header (ip header follows); ++ * 12 byte MPE header; 4 byte checksum; + 2 byte alignment, 8 byte LLC/SNAP ++ */ ++ if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) { ++ //printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); ++ stats->rx_dropped++; ++ return; ++ } ++ skb_reserve(skb, 2); /* longword align L3 header */ ++ skb->dev = dev; ++ ++ /* copy L3 payload */ ++ eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14 - snap); ++ memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap); ++ ++ /* create ethernet header: */ ++ eth[0]=pkt[0x0b]; ++ eth[1]=pkt[0x0a]; ++ eth[2]=pkt[0x09]; ++ eth[3]=pkt[0x08]; ++ eth[4]=pkt[0x04]; ++ eth[5]=pkt[0x03]; ++ ++ eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0; ++ ++ if (snap) { ++ eth[12] = pkt[18]; ++ eth[13] = pkt[19]; ++ } else { ++ /* protocol numbers are from rfc-1700 or ++ * http://www.iana.org/assignments/ethernet-numbers ++ */ ++ if (pkt[12] >> 4 == 6) { /* version field from IP header */ ++ eth[12] = 0x86; /* IPv6 */ ++ eth[13] = 0xdd; ++ } else { ++ eth[12] = 0x08; /* IPv4 */ ++ eth[13] = 0x00; ++ } ++ } ++ ++ skb->protocol = dvb_net_eth_type_trans(skb, dev); ++ ++ stats->rx_packets++; ++ stats->rx_bytes+=skb->len; ++ netif_rx(skb); ++} ++ ++static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, ++ const u8 *buffer2, size_t buffer2_len, ++ struct dmx_section_filter *filter, ++ enum dmx_success success) ++{ ++ struct net_device *dev = filter->priv; ++ ++ /** ++ * we rely on the DVB API definition where exactly one complete ++ * section is delivered in buffer1 ++ */ ++ dvb_net_sec (dev, buffer1, buffer1_len); ++ return 0; ++} ++ ++static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev) ++{ ++ dev_kfree_skb(skb); ++ return NETDEV_TX_OK; ++} ++ ++static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; ++static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00}; ++static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00}; ++static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; ++ ++static int dvb_net_filter_sec_set(struct net_device *dev, ++ struct dmx_section_filter **secfilter, ++ u8 *mac, u8 *mac_mask) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ int ret; ++ ++ *secfilter=NULL; ++ ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter); ++ if (ret<0) { ++ printk("%s: could not get filter\n", dev->name); ++ return ret; ++ } ++ ++ (*secfilter)->priv=(void *) dev; ++ ++ memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE); ++ memset((*secfilter)->filter_mask, 0x00, DMX_MAX_FILTER_SIZE); ++ memset((*secfilter)->filter_mode, 0xff, DMX_MAX_FILTER_SIZE); ++ ++ (*secfilter)->filter_value[0]=0x3e; ++ (*secfilter)->filter_value[3]=mac[5]; ++ (*secfilter)->filter_value[4]=mac[4]; ++ (*secfilter)->filter_value[8]=mac[3]; ++ (*secfilter)->filter_value[9]=mac[2]; ++ (*secfilter)->filter_value[10]=mac[1]; ++ (*secfilter)->filter_value[11]=mac[0]; ++ ++ (*secfilter)->filter_mask[0] = 0xff; ++ (*secfilter)->filter_mask[3] = mac_mask[5]; ++ (*secfilter)->filter_mask[4] = mac_mask[4]; ++ (*secfilter)->filter_mask[8] = mac_mask[3]; ++ (*secfilter)->filter_mask[9] = mac_mask[2]; ++ (*secfilter)->filter_mask[10] = mac_mask[1]; ++ (*secfilter)->filter_mask[11]=mac_mask[0]; ++ ++ dprintk("%s: filter mac=%pM\n", dev->name, mac); ++ dprintk("%s: filter mask=%pM\n", dev->name, mac_mask); ++ ++ return 0; ++} ++ ++static int dvb_net_feed_start(struct net_device *dev) ++{ ++ int ret = 0, i; ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ struct dmx_demux *demux = priv->demux; ++ unsigned char *mac = (unsigned char *) dev->dev_addr; ++ ++ dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode); ++ mutex_lock(&priv->mutex); ++ if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0]) ++ printk("%s: BUG %d\n", __func__, __LINE__); ++ ++ priv->secfeed=NULL; ++ priv->secfilter=NULL; ++ priv->tsfeed = NULL; ++ ++ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { ++ dprintk("%s: alloc secfeed\n", __func__); ++ ret=demux->allocate_section_feed(demux, &priv->secfeed, ++ dvb_net_sec_callback); ++ if (ret<0) { ++ printk("%s: could not allocate section feed\n", dev->name); ++ goto error; ++ } ++ ++ ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 1); ++ ++ if (ret<0) { ++ printk("%s: could not set section feed\n", dev->name); ++ priv->demux->release_section_feed(priv->demux, priv->secfeed); ++ priv->secfeed=NULL; ++ goto error; ++ } ++ ++ if (priv->rx_mode != RX_MODE_PROMISC) { ++ dprintk("%s: set secfilter\n", __func__); ++ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal); ++ } ++ ++ switch (priv->rx_mode) { ++ case RX_MODE_MULTI: ++ for (i = 0; i < priv->multi_num; i++) { ++ dprintk("%s: set multi_secfilter[%d]\n", __func__, i); ++ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i], ++ priv->multi_macs[i], mask_normal); ++ } ++ break; ++ case RX_MODE_ALL_MULTI: ++ priv->multi_num=1; ++ dprintk("%s: set multi_secfilter[0]\n", __func__); ++ dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0], ++ mac_allmulti, mask_allmulti); ++ break; ++ case RX_MODE_PROMISC: ++ priv->multi_num=0; ++ dprintk("%s: set secfilter\n", __func__); ++ dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc); ++ break; ++ } ++ ++ dprintk("%s: start filtering\n", __func__); ++ priv->secfeed->start_filtering(priv->secfeed); ++ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { ++ struct timespec timeout = { 0, 10000000 }; // 10 msec ++ ++ /* we have payloads encapsulated in TS */ ++ dprintk("%s: alloc tsfeed\n", __func__); ++ ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback); ++ if (ret < 0) { ++ printk("%s: could not allocate ts feed\n", dev->name); ++ goto error; ++ } ++ ++ /* Set netdevice pointer for ts decaps callback. */ ++ priv->tsfeed->priv = (void *)dev; ++ ret = priv->tsfeed->set(priv->tsfeed, ++ priv->pid, /* pid */ ++ TS_PACKET, /* type */ ++ DMX_TS_PES_OTHER, /* pes type */ ++ 32768, /* circular buffer size */ ++ timeout /* timeout */ ++ ); ++ ++ if (ret < 0) { ++ printk("%s: could not set ts feed\n", dev->name); ++ priv->demux->release_ts_feed(priv->demux, priv->tsfeed); ++ priv->tsfeed = NULL; ++ goto error; ++ } ++ ++ dprintk("%s: start filtering\n", __func__); ++ priv->tsfeed->start_filtering(priv->tsfeed); ++ } else ++ ret = -EINVAL; ++ ++error: ++ mutex_unlock(&priv->mutex); ++ return ret; ++} ++ ++static int dvb_net_feed_stop(struct net_device *dev) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ int i, ret = 0; ++ ++ dprintk("%s\n", __func__); ++ mutex_lock(&priv->mutex); ++ if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { ++ if (priv->secfeed) { ++ if (priv->secfeed->is_filtering) { ++ dprintk("%s: stop secfeed\n", __func__); ++ priv->secfeed->stop_filtering(priv->secfeed); ++ } ++ ++ if (priv->secfilter) { ++ dprintk("%s: release secfilter\n", __func__); ++ priv->secfeed->release_filter(priv->secfeed, ++ priv->secfilter); ++ priv->secfilter=NULL; ++ } ++ ++ for (i=0; imulti_num; i++) { ++ if (priv->multi_secfilter[i]) { ++ dprintk("%s: release multi_filter[%d]\n", ++ __func__, i); ++ priv->secfeed->release_filter(priv->secfeed, ++ priv->multi_secfilter[i]); ++ priv->multi_secfilter[i] = NULL; ++ } ++ } ++ ++ priv->demux->release_section_feed(priv->demux, priv->secfeed); ++ priv->secfeed = NULL; ++ } else ++ printk("%s: no feed to stop\n", dev->name); ++ } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { ++ if (priv->tsfeed) { ++ if (priv->tsfeed->is_filtering) { ++ dprintk("%s: stop tsfeed\n", __func__); ++ priv->tsfeed->stop_filtering(priv->tsfeed); ++ } ++ priv->demux->release_ts_feed(priv->demux, priv->tsfeed); ++ priv->tsfeed = NULL; ++ } ++ else ++ printk("%s: no ts feed to stop\n", dev->name); ++ } else ++ ret = -EINVAL; ++ mutex_unlock(&priv->mutex); ++ return ret; ++} ++ ++ ++static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ ++ if (priv->multi_num == DVB_NET_MULTICAST_MAX) ++ return -ENOMEM; ++ ++ memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN); ++ ++ priv->multi_num++; ++ return 0; ++} ++ ++ ++static void wq_set_multicast_list (struct work_struct *work) ++{ ++ struct dvb_net_priv *priv = ++ container_of(work, struct dvb_net_priv, set_multicast_list_wq); ++ struct net_device *dev = priv->net; ++ ++ dvb_net_feed_stop(dev); ++ priv->rx_mode = RX_MODE_UNI; ++ netif_addr_lock_bh(dev); ++ ++ if (dev->flags & IFF_PROMISC) { ++ dprintk("%s: promiscuous mode\n", dev->name); ++ priv->rx_mode = RX_MODE_PROMISC; ++ } else if ((dev->flags & IFF_ALLMULTI)) { ++ dprintk("%s: allmulti mode\n", dev->name); ++ priv->rx_mode = RX_MODE_ALL_MULTI; ++ } else if (!netdev_mc_empty(dev)) { ++ struct netdev_hw_addr *ha; ++ ++ dprintk("%s: set_mc_list, %d entries\n", ++ dev->name, netdev_mc_count(dev)); ++ ++ priv->rx_mode = RX_MODE_MULTI; ++ priv->multi_num = 0; ++ ++ netdev_for_each_mc_addr(ha, dev) ++ dvb_set_mc_filter(dev, ha->addr); ++ } ++ ++ netif_addr_unlock_bh(dev); ++ dvb_net_feed_start(dev); ++} ++ ++ ++static void dvb_net_set_multicast_list (struct net_device *dev) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ schedule_work(&priv->set_multicast_list_wq); ++} ++ ++ ++static void wq_restart_net_feed (struct work_struct *work) ++{ ++ struct dvb_net_priv *priv = ++ container_of(work, struct dvb_net_priv, restart_net_feed_wq); ++ struct net_device *dev = priv->net; ++ ++ if (netif_running(dev)) { ++ dvb_net_feed_stop(dev); ++ dvb_net_feed_start(dev); ++ } ++} ++ ++ ++static int dvb_net_set_mac (struct net_device *dev, void *p) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ struct sockaddr *addr=p; ++ ++ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); ++ ++ if (netif_running(dev)) ++ schedule_work(&priv->restart_net_feed_wq); ++ ++ return 0; ++} ++ ++ ++static int dvb_net_open(struct net_device *dev) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ ++ priv->in_use++; ++ dvb_net_feed_start(dev); ++ return 0; ++} ++ ++ ++static int dvb_net_stop(struct net_device *dev) ++{ ++ struct dvb_net_priv *priv = netdev_priv(dev); ++ ++ priv->in_use--; ++ return dvb_net_feed_stop(dev); ++} ++ ++static const struct header_ops dvb_header_ops = { ++ .create = eth_header, ++ .parse = eth_header_parse, ++ .rebuild = eth_rebuild_header, ++}; ++ ++ ++static const struct net_device_ops dvb_netdev_ops = { ++ .ndo_open = dvb_net_open, ++ .ndo_stop = dvb_net_stop, ++ .ndo_start_xmit = dvb_net_tx, ++ .ndo_set_rx_mode = dvb_net_set_multicast_list, ++ .ndo_set_mac_address = dvb_net_set_mac, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static void dvb_net_setup(struct net_device *dev) ++{ ++ ether_setup(dev); ++ ++ dev->header_ops = &dvb_header_ops; ++ dev->netdev_ops = &dvb_netdev_ops; ++ dev->mtu = 4096; ++ ++ dev->flags |= IFF_NOARP; ++} ++ ++static int get_if(struct dvb_net *dvbnet) ++{ ++ int i; ++ ++ for (i=0; istate[i]) ++ break; ++ ++ if (i == DVB_NET_DEVICES_MAX) ++ return -1; ++ ++ dvbnet->state[i]=1; ++ return i; ++} ++ ++static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype) ++{ ++ struct net_device *net; ++ struct dvb_net_priv *priv; ++ int result; ++ int if_num; ++ ++ if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE) ++ return -EINVAL; ++ if ((if_num = get_if(dvbnet)) < 0) ++ return -EINVAL; ++ ++ net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup); ++ if (!net) ++ return -ENOMEM; ++ ++ if (dvbnet->dvbdev->id) ++ snprintf(net->name, IFNAMSIZ, "dvb%d%u%d", ++ dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num); ++ else ++ /* compatibility fix to keep dvb0_0 format */ ++ snprintf(net->name, IFNAMSIZ, "dvb%d_%d", ++ dvbnet->dvbdev->adapter->num, if_num); ++ ++ net->addr_len = 6; ++ memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6); ++ ++ dvbnet->device[if_num] = net; ++ ++ priv = netdev_priv(net); ++ priv->net = net; ++ priv->demux = dvbnet->demux; ++ priv->pid = pid; ++ priv->rx_mode = RX_MODE_UNI; ++ priv->need_pusi = 1; ++ priv->tscc = 0; ++ priv->feedtype = feedtype; ++ reset_ule(priv); ++ ++ INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list); ++ INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed); ++ mutex_init(&priv->mutex); ++ ++ net->base_addr = pid; ++ ++ if ((result = register_netdev(net)) < 0) { ++ dvbnet->device[if_num] = NULL; ++ free_netdev(net); ++ return result; ++ } ++ printk("dvb_net: created network interface %s\n", net->name); ++ ++ return if_num; ++} ++ ++static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num) ++{ ++ struct net_device *net = dvbnet->device[num]; ++ struct dvb_net_priv *priv; ++ ++ if (!dvbnet->state[num]) ++ return -EINVAL; ++ priv = netdev_priv(net); ++ if (priv->in_use) ++ return -EBUSY; ++ ++ dvb_net_stop(net); ++ flush_work_sync(&priv->set_multicast_list_wq); ++ flush_work_sync(&priv->restart_net_feed_wq); ++ printk("dvb_net: removed network interface %s\n", net->name); ++ unregister_netdev(net); ++ dvbnet->state[num]=0; ++ dvbnet->device[num] = NULL; ++ free_netdev(net); ++ ++ return 0; ++} ++ ++static int dvb_net_do_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_net *dvbnet = dvbdev->priv; ++ ++ if (((file->f_flags&O_ACCMODE)==O_RDONLY)) ++ return -EPERM; ++ ++ switch (cmd) { ++ case NET_ADD_IF: ++ { ++ struct dvb_net_if *dvbnetif = parg; ++ int result; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!try_module_get(dvbdev->adapter->module)) ++ return -EPERM; ++ ++ result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); ++ if (result<0) { ++ module_put(dvbdev->adapter->module); ++ return result; ++ } ++ dvbnetif->if_num=result; ++ break; ++ } ++ case NET_GET_IF: ++ { ++ struct net_device *netdev; ++ struct dvb_net_priv *priv_data; ++ struct dvb_net_if *dvbnetif = parg; ++ ++ if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || ++ !dvbnet->state[dvbnetif->if_num]) ++ return -EINVAL; ++ ++ netdev = dvbnet->device[dvbnetif->if_num]; ++ ++ priv_data = netdev_priv(netdev); ++ dvbnetif->pid=priv_data->pid; ++ dvbnetif->feedtype=priv_data->feedtype; ++ break; ++ } ++ case NET_REMOVE_IF: ++ { ++ int ret; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) ++ return -EINVAL; ++ ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); ++ if (!ret) ++ module_put(dvbdev->adapter->module); ++ return ret; ++ } ++ ++ /* binary compatibility cruft */ ++ case __NET_ADD_IF_OLD: ++ { ++ struct __dvb_net_if_old *dvbnetif = parg; ++ int result; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!try_module_get(dvbdev->adapter->module)) ++ return -EPERM; ++ ++ result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); ++ if (result<0) { ++ module_put(dvbdev->adapter->module); ++ return result; ++ } ++ dvbnetif->if_num=result; ++ break; ++ } ++ case __NET_GET_IF_OLD: ++ { ++ struct net_device *netdev; ++ struct dvb_net_priv *priv_data; ++ struct __dvb_net_if_old *dvbnetif = parg; ++ ++ if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || ++ !dvbnet->state[dvbnetif->if_num]) ++ return -EINVAL; ++ ++ netdev = dvbnet->device[dvbnetif->if_num]; ++ ++ priv_data = netdev_priv(netdev); ++ dvbnetif->pid=priv_data->pid; ++ break; ++ } ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} ++ ++static long dvb_net_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl); ++} ++ ++static int dvb_net_close(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct dvb_net *dvbnet = dvbdev->priv; ++ ++ dvb_generic_release(inode, file); ++ ++ if(dvbdev->users == 1 && dvbnet->exit == 1) { ++ fops_put(file->f_op); ++ file->f_op = NULL; ++ wake_up(&dvbdev->wait_queue); ++ } ++ return 0; ++} ++ ++ ++static const struct file_operations dvb_net_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = dvb_net_ioctl, ++ .open = dvb_generic_open, ++ .release = dvb_net_close, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_net = { ++ .priv = NULL, ++ .users = 1, ++ .writers = 1, ++ .fops = &dvb_net_fops, ++}; ++ ++ ++void dvb_net_release (struct dvb_net *dvbnet) ++{ ++ int i; ++ ++ dvbnet->exit = 1; ++ if (dvbnet->dvbdev->users < 1) ++ wait_event(dvbnet->dvbdev->wait_queue, ++ dvbnet->dvbdev->users==1); ++ ++ dvb_unregister_device(dvbnet->dvbdev); ++ ++ for (i=0; istate[i]) ++ continue; ++ dvb_net_remove_if(dvbnet, i); ++ } ++} ++EXPORT_SYMBOL(dvb_net_release); ++ ++ ++int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, ++ struct dmx_demux *dmx) ++{ ++ int i; ++ ++ dvbnet->demux = dmx; ++ ++ for (i=0; istate[i] = 0; ++ ++ return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, ++ dvbnet, DVB_DEVICE_NET); ++} ++EXPORT_SYMBOL(dvb_net_init); +diff --git a/drivers/media/dvb-core/dvb_net.h b/drivers/media/dvb-core/dvb_net.h +new file mode 100644 +index 0000000..1e53acd +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_net.h +@@ -0,0 +1,66 @@ ++/* ++ * dvb_net.h ++ * ++ * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef _DVB_NET_H_ ++#define _DVB_NET_H_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvbdev.h" ++ ++#define DVB_NET_DEVICES_MAX 10 ++ ++#ifdef CONFIG_DVB_NET ++ ++struct dvb_net { ++ struct dvb_device *dvbdev; ++ struct net_device *device[DVB_NET_DEVICES_MAX]; ++ int state[DVB_NET_DEVICES_MAX]; ++ unsigned int exit:1; ++ struct dmx_demux *demux; ++}; ++ ++void dvb_net_release(struct dvb_net *); ++int dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *); ++ ++#else ++ ++struct dvb_net { ++ struct dvb_device *dvbdev; ++}; ++ ++static inline void dvb_net_release(struct dvb_net *dvbnet) ++{ ++} ++ ++static inline int dvb_net_init(struct dvb_adapter *adap, ++ struct dvb_net *dvbnet, struct dmx_demux *dmx) ++{ ++ return 0; ++} ++ ++#endif /* ifdef CONFIG_DVB_NET */ ++ ++#endif +diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c +new file mode 100644 +index 0000000..9f744c2 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_ringbuffer.c +@@ -0,0 +1,300 @@ ++/* ++ * ++ * dvb_ringbuffer.c: ring buffer implementation for the dvb driver ++ * ++ * Copyright (C) 2003 Oliver Endriss ++ * Copyright (C) 2004 Andrew de Quincey ++ * ++ * based on code originally found in av7110.c & dvb_ci.c: ++ * Copyright (C) 1999-2003 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * 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 Lesser 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_ringbuffer.h" ++ ++#define PKT_READY 0 ++#define PKT_DISPOSED 1 ++ ++ ++void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) ++{ ++ rbuf->pread=rbuf->pwrite=0; ++ rbuf->data=data; ++ rbuf->size=len; ++ rbuf->error=0; ++ rbuf->do_wait=1; ++ ++ init_waitqueue_head(&rbuf->queue); ++ ++ spin_lock_init(&(rbuf->lock)); ++} ++ ++ ++ ++int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) ++{ ++ return (rbuf->pread==rbuf->pwrite); ++} ++ ++ ++ ++ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) ++{ ++ ssize_t free; ++ ++ free = rbuf->pread - rbuf->pwrite; ++ if (free <= 0) ++ free += rbuf->size; ++ return free-1; ++} ++ ++ ++ ++ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) ++{ ++ ssize_t avail; ++ ++ avail = rbuf->pwrite - rbuf->pread; ++ if (avail < 0) ++ avail += rbuf->size; ++ return avail; ++} ++ ++ ++ ++void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) ++{ ++ rbuf->pread = rbuf->pwrite; ++ rbuf->error = 0; ++} ++EXPORT_SYMBOL(dvb_ringbuffer_flush); ++ ++void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) ++{ ++ rbuf->pread = rbuf->pwrite = 0; ++ rbuf->error = 0; ++} ++ ++void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rbuf->lock, flags); ++ dvb_ringbuffer_flush(rbuf); ++ spin_unlock_irqrestore(&rbuf->lock, flags); ++ ++ wake_up(&rbuf->queue); ++} ++ ++ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) ++{ ++ size_t todo = len; ++ size_t split; ++ ++ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; ++ if (split > 0) { ++ if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) ++ return -EFAULT; ++ buf += split; ++ todo -= split; ++ rbuf->pread = 0; ++ } ++ if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) ++ return -EFAULT; ++ ++ rbuf->pread = (rbuf->pread + todo) % rbuf->size; ++ ++ return len; ++} ++ ++void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) ++{ ++ size_t todo = len; ++ size_t split; ++ ++ split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; ++ if (split > 0) { ++ memcpy(buf, rbuf->data+rbuf->pread, split); ++ buf += split; ++ todo -= split; ++ rbuf->pread = 0; ++ } ++ memcpy(buf, rbuf->data+rbuf->pread, todo); ++ ++ rbuf->pread = (rbuf->pread + todo) % rbuf->size; ++} ++ ++ ++ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) ++{ ++ size_t todo = len; ++ size_t split; ++ ++ split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; ++ ++ if (split > 0) { ++ memcpy(rbuf->data+rbuf->pwrite, buf, split); ++ buf += split; ++ todo -= split; ++ rbuf->pwrite = 0; ++ } ++ memcpy(rbuf->data+rbuf->pwrite, buf, todo); ++ rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; ++ ++ return len; ++} ++ ++ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) ++{ ++ int status; ++ ssize_t oldpwrite = rbuf->pwrite; ++ ++ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); ++ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); ++ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); ++ status = dvb_ringbuffer_write(rbuf, buf, len); ++ ++ if (status < 0) rbuf->pwrite = oldpwrite; ++ return status; ++} ++ ++ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, ++ int offset, u8 __user *buf, size_t len) ++{ ++ size_t todo; ++ size_t split; ++ size_t pktlen; ++ ++ pktlen = rbuf->data[idx] << 8; ++ pktlen |= rbuf->data[(idx + 1) % rbuf->size]; ++ if (offset > pktlen) return -EINVAL; ++ if ((offset + len) > pktlen) len = pktlen - offset; ++ ++ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; ++ todo = len; ++ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; ++ if (split > 0) { ++ if (copy_to_user(buf, rbuf->data+idx, split)) ++ return -EFAULT; ++ buf += split; ++ todo -= split; ++ idx = 0; ++ } ++ if (copy_to_user(buf, rbuf->data+idx, todo)) ++ return -EFAULT; ++ ++ return len; ++} ++ ++ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, ++ int offset, u8* buf, size_t len) ++{ ++ size_t todo; ++ size_t split; ++ size_t pktlen; ++ ++ pktlen = rbuf->data[idx] << 8; ++ pktlen |= rbuf->data[(idx + 1) % rbuf->size]; ++ if (offset > pktlen) return -EINVAL; ++ if ((offset + len) > pktlen) len = pktlen - offset; ++ ++ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; ++ todo = len; ++ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; ++ if (split > 0) { ++ memcpy(buf, rbuf->data+idx, split); ++ buf += split; ++ todo -= split; ++ idx = 0; ++ } ++ memcpy(buf, rbuf->data+idx, todo); ++ return len; ++} ++ ++void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) ++{ ++ size_t pktlen; ++ ++ rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; ++ ++ // clean up disposed packets ++ while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { ++ if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { ++ pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; ++ pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); ++ DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); ++ } else { ++ // first packet is not disposed, so we stop cleaning now ++ break; ++ } ++ } ++} ++ ++ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) ++{ ++ int consumed; ++ int curpktlen; ++ int curpktstatus; ++ ++ if (idx == -1) { ++ idx = rbuf->pread; ++ } else { ++ curpktlen = rbuf->data[idx] << 8; ++ curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; ++ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; ++ } ++ ++ consumed = (idx - rbuf->pread) % rbuf->size; ++ ++ while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { ++ ++ curpktlen = rbuf->data[idx] << 8; ++ curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; ++ curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; ++ ++ if (curpktstatus == PKT_READY) { ++ *pktlen = curpktlen; ++ return idx; ++ } ++ ++ consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; ++ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; ++ } ++ ++ // no packets available ++ return -1; ++} ++ ++ ++ ++EXPORT_SYMBOL(dvb_ringbuffer_init); ++EXPORT_SYMBOL(dvb_ringbuffer_empty); ++EXPORT_SYMBOL(dvb_ringbuffer_free); ++EXPORT_SYMBOL(dvb_ringbuffer_avail); ++EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); ++EXPORT_SYMBOL(dvb_ringbuffer_read_user); ++EXPORT_SYMBOL(dvb_ringbuffer_read); ++EXPORT_SYMBOL(dvb_ringbuffer_write); +diff --git a/drivers/media/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb-core/dvb_ringbuffer.h +new file mode 100644 +index 0000000..6951dd3 +--- /dev/null ++++ b/drivers/media/dvb-core/dvb_ringbuffer.h +@@ -0,0 +1,187 @@ ++/* ++ * ++ * dvb_ringbuffer.h: ring buffer implementation for the dvb driver ++ * ++ * Copyright (C) 2003 Oliver Endriss ++ * Copyright (C) 2004 Andrew de Quincey ++ * ++ * based on code originally found in av7110.c & dvb_ci.c: ++ * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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 Lesser 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. ++ */ ++ ++#ifndef _DVB_RINGBUFFER_H_ ++#define _DVB_RINGBUFFER_H_ ++ ++#include ++#include ++ ++struct dvb_ringbuffer { ++ u8 *data; ++ ssize_t size; ++ ssize_t pread; ++ ssize_t pwrite; ++ int error; ++ ++ wait_queue_head_t queue; ++ spinlock_t lock; ++ int do_wait; ++}; ++ ++#define DVB_RINGBUFFER_PKTHDRSIZE 3 ++ ++ ++/* ++** Notes: ++** ------ ++** (1) For performance reasons read and write routines don't check buffer sizes ++** and/or number of bytes free/available. This has to be done before these ++** routines are called. For example: ++** ++** *** write bytes *** ++** free = dvb_ringbuffer_free(rbuf); ++** if (free >= buflen) ++** count = dvb_ringbuffer_write(rbuf, buffer, buflen); ++** else ++** ... ++** ++** *** read min. 1000, max. bytes *** ++** avail = dvb_ringbuffer_avail(rbuf); ++** if (avail >= 1000) ++** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); ++** else ++** ... ++** ++** (2) If there is exactly one reader and one writer, there is no need ++** to lock read or write operations. ++** Two or more readers must be locked against each other. ++** Flushing the buffer counts as a read operation. ++** Resetting the buffer counts as a read and write operation. ++** Two or more writers must be locked against each other. ++*/ ++ ++/* initialize ring buffer, lock and queue */ ++extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len); ++ ++/* test whether buffer is empty */ ++extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf); ++ ++/* return the number of free bytes in the buffer */ ++extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf); ++ ++/* return the number of bytes waiting in the buffer */ ++extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); ++ ++ ++/* ++** Reset the read and write pointers to zero and flush the buffer ++** This counts as a read and write operation ++*/ ++extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); ++ ++ ++/* read routines & macros */ ++/* ---------------------- */ ++/* flush buffer */ ++extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf); ++ ++/* flush buffer protected by spinlock and wake-up waiting task(s) */ ++extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); ++ ++/* peek at byte in the buffer */ ++#define DVB_RINGBUFFER_PEEK(rbuf,offs) \ ++ (rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size] ++ ++/* advance read ptr by bytes */ ++#define DVB_RINGBUFFER_SKIP(rbuf,num) \ ++ (rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size ++ ++/* ++** read bytes from ring buffer into ++** specifies whether resides in user space ++** returns number of bytes transferred or -EFAULT ++*/ ++extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, ++ u8 __user *buf, size_t len); ++extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, ++ u8 *buf, size_t len); ++ ++ ++/* write routines & macros */ ++/* ----------------------- */ ++/* write single byte to ring buffer */ ++#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte) \ ++ { (rbuf)->data[(rbuf)->pwrite]=(byte); \ ++ (rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; } ++/* ++** write bytes to ring buffer ++** specifies whether resides in user space ++** returns number of bytes transferred or -EFAULT ++*/ ++extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, ++ size_t len); ++ ++ ++/** ++ * Write a packet into the ringbuffer. ++ * ++ * Ringbuffer to write to. ++ * Buffer to write. ++ * Length of buffer (currently limited to 65535 bytes max). ++ * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. ++ */ ++extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, ++ size_t len); ++ ++/** ++ * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this ++ * does NOT update the read pointer in the ringbuffer. You must use ++ * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required. ++ * ++ * Ringbuffer concerned. ++ * Packet index as returned by dvb_ringbuffer_pkt_next(). ++ * Offset into packet to read from. ++ * Destination buffer for data. ++ * Size of destination buffer. ++ * Set to 1 if is in userspace. ++ * returns Number of bytes read, or -EFAULT. ++ */ ++extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, ++ int offset, u8 __user *buf, size_t len); ++extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, ++ int offset, u8 *buf, size_t len); ++ ++/** ++ * Dispose of a packet in the ring buffer. ++ * ++ * Ring buffer concerned. ++ * Packet index as returned by dvb_ringbuffer_pkt_next(). ++ */ ++extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx); ++ ++/** ++ * Get the index of the next packet in a ringbuffer. ++ * ++ * Ringbuffer concerned. ++ * Previous packet index, or -1 to return the first packet index. ++ * On success, will be updated to contain the length of the packet in bytes. ++ * returns Packet index (if >=0), or -1 if no packets available. ++ */ ++extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen); ++ ++ ++#endif /* _DVB_RINGBUFFER_H_ */ +diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c +new file mode 100644 +index 0000000..00a6732 +--- /dev/null ++++ b/drivers/media/dvb-core/dvbdev.c +@@ -0,0 +1,506 @@ ++/* ++ * dvbdev.c ++ * ++ * Copyright (C) 2000 Ralph Metzler ++ * & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvbdev.h" ++ ++static DEFINE_MUTEX(dvbdev_mutex); ++static int dvbdev_debug; ++ ++module_param(dvbdev_debug, int, 0644); ++MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off)."); ++ ++#define dprintk if (dvbdev_debug) printk ++ ++static LIST_HEAD(dvb_adapter_list); ++static DEFINE_MUTEX(dvbdev_register_lock); ++ ++static const char * const dnames[] = { ++ "video", "audio", "sec", "frontend", "demux", "dvr", "ca", ++ "net", "osd" ++}; ++ ++#ifdef CONFIG_DVB_DYNAMIC_MINORS ++#define MAX_DVB_MINORS 256 ++#define DVB_MAX_IDS MAX_DVB_MINORS ++#else ++#define DVB_MAX_IDS 4 ++#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) ++#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64) ++#endif ++ ++static struct class *dvb_class; ++ ++static struct dvb_device *dvb_minors[MAX_DVB_MINORS]; ++static DECLARE_RWSEM(minor_rwsem); ++ ++static int dvb_device_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev; ++ ++ mutex_lock(&dvbdev_mutex); ++ down_read(&minor_rwsem); ++ dvbdev = dvb_minors[iminor(inode)]; ++ ++ if (dvbdev && dvbdev->fops) { ++ int err = 0; ++ const struct file_operations *old_fops; ++ ++ file->private_data = dvbdev; ++ old_fops = file->f_op; ++ file->f_op = fops_get(dvbdev->fops); ++ if (file->f_op == NULL) { ++ file->f_op = old_fops; ++ goto fail; ++ } ++ if(file->f_op->open) ++ err = file->f_op->open(inode,file); ++ if (err) { ++ fops_put(file->f_op); ++ file->f_op = fops_get(old_fops); ++ } ++ fops_put(old_fops); ++ up_read(&minor_rwsem); ++ mutex_unlock(&dvbdev_mutex); ++ return err; ++ } ++fail: ++ up_read(&minor_rwsem); ++ mutex_unlock(&dvbdev_mutex); ++ return -ENODEV; ++} ++ ++ ++static const struct file_operations dvb_device_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = dvb_device_open, ++ .llseek = noop_llseek, ++}; ++ ++static struct cdev dvb_device_cdev; ++ ++int dvb_generic_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ ++ if (!dvbdev) ++ return -ENODEV; ++ ++ if (!dvbdev->users) ++ return -EBUSY; ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ if (!dvbdev->readers) ++ return -EBUSY; ++ dvbdev->readers--; ++ } else { ++ if (!dvbdev->writers) ++ return -EBUSY; ++ dvbdev->writers--; ++ } ++ ++ dvbdev->users--; ++ return 0; ++} ++EXPORT_SYMBOL(dvb_generic_open); ++ ++ ++int dvb_generic_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ ++ if (!dvbdev) ++ return -ENODEV; ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ dvbdev->readers++; ++ } else { ++ dvbdev->writers++; ++ } ++ ++ dvbdev->users++; ++ return 0; ++} ++EXPORT_SYMBOL(dvb_generic_release); ++ ++ ++long dvb_generic_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ ++ if (!dvbdev) ++ return -ENODEV; ++ ++ if (!dvbdev->kernel_ioctl) ++ return -EINVAL; ++ ++ return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl); ++} ++EXPORT_SYMBOL(dvb_generic_ioctl); ++ ++ ++static int dvbdev_get_free_id (struct dvb_adapter *adap, int type) ++{ ++ u32 id = 0; ++ ++ while (id < DVB_MAX_IDS) { ++ struct dvb_device *dev; ++ list_for_each_entry(dev, &adap->device_list, list_head) ++ if (dev->type == type && dev->id == id) ++ goto skip; ++ return id; ++skip: ++ id++; ++ } ++ return -ENFILE; ++} ++ ++ ++int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, ++ const struct dvb_device *template, void *priv, int type) ++{ ++ struct dvb_device *dvbdev; ++ struct file_operations *dvbdevfops; ++ struct device *clsdev; ++ int minor; ++ int id; ++ ++ mutex_lock(&dvbdev_register_lock); ++ ++ if ((id = dvbdev_get_free_id (adap, type)) < 0){ ++ mutex_unlock(&dvbdev_register_lock); ++ *pdvbdev = NULL; ++ printk(KERN_ERR "%s: couldn't find free device id\n", __func__); ++ return -ENFILE; ++ } ++ ++ *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); ++ ++ if (!dvbdev){ ++ mutex_unlock(&dvbdev_register_lock); ++ return -ENOMEM; ++ } ++ ++ dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); ++ ++ if (!dvbdevfops){ ++ kfree (dvbdev); ++ mutex_unlock(&dvbdev_register_lock); ++ return -ENOMEM; ++ } ++ ++ memcpy(dvbdev, template, sizeof(struct dvb_device)); ++ dvbdev->type = type; ++ dvbdev->id = id; ++ dvbdev->adapter = adap; ++ dvbdev->priv = priv; ++ dvbdev->fops = dvbdevfops; ++ init_waitqueue_head (&dvbdev->wait_queue); ++ ++ memcpy(dvbdevfops, template->fops, sizeof(struct file_operations)); ++ dvbdevfops->owner = adap->module; ++ ++ list_add_tail (&dvbdev->list_head, &adap->device_list); ++ ++ down_write(&minor_rwsem); ++#ifdef CONFIG_DVB_DYNAMIC_MINORS ++ for (minor = 0; minor < MAX_DVB_MINORS; minor++) ++ if (dvb_minors[minor] == NULL) ++ break; ++ ++ if (minor == MAX_DVB_MINORS) { ++ kfree(dvbdevfops); ++ kfree(dvbdev); ++ mutex_unlock(&dvbdev_register_lock); ++ return -EINVAL; ++ } ++#else ++ minor = nums2minor(adap->num, type, id); ++#endif ++ ++ dvbdev->minor = minor; ++ dvb_minors[minor] = dvbdev; ++ up_write(&minor_rwsem); ++ ++ mutex_unlock(&dvbdev_register_lock); ++ ++ clsdev = device_create(dvb_class, adap->device, ++ MKDEV(DVB_MAJOR, minor), ++ dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); ++ if (IS_ERR(clsdev)) { ++ printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n", ++ __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); ++ return PTR_ERR(clsdev); ++ } ++ ++ dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", ++ adap->num, dnames[type], id, minor, minor); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dvb_register_device); ++ ++ ++void dvb_unregister_device(struct dvb_device *dvbdev) ++{ ++ if (!dvbdev) ++ return; ++ ++ down_write(&minor_rwsem); ++ dvb_minors[dvbdev->minor] = NULL; ++ up_write(&minor_rwsem); ++ ++ device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); ++ ++ list_del (&dvbdev->list_head); ++ kfree (dvbdev->fops); ++ kfree (dvbdev); ++} ++EXPORT_SYMBOL(dvb_unregister_device); ++ ++static int dvbdev_check_free_adapter_num(int num) ++{ ++ struct list_head *entry; ++ list_for_each(entry, &dvb_adapter_list) { ++ struct dvb_adapter *adap; ++ adap = list_entry(entry, struct dvb_adapter, list_head); ++ if (adap->num == num) ++ return 0; ++ } ++ return 1; ++} ++ ++static int dvbdev_get_free_adapter_num (void) ++{ ++ int num = 0; ++ ++ while (num < DVB_MAX_ADAPTERS) { ++ if (dvbdev_check_free_adapter_num(num)) ++ return num; ++ num++; ++ } ++ ++ return -ENFILE; ++} ++ ++ ++int dvb_register_adapter(struct dvb_adapter *adap, const char *name, ++ struct module *module, struct device *device, ++ short *adapter_nums) ++{ ++ int i, num; ++ ++ mutex_lock(&dvbdev_register_lock); ++ ++ for (i = 0; i < DVB_MAX_ADAPTERS; ++i) { ++ num = adapter_nums[i]; ++ if (num >= 0 && num < DVB_MAX_ADAPTERS) { ++ /* use the one the driver asked for */ ++ if (dvbdev_check_free_adapter_num(num)) ++ break; ++ } else { ++ num = dvbdev_get_free_adapter_num(); ++ break; ++ } ++ num = -1; ++ } ++ ++ if (num < 0) { ++ mutex_unlock(&dvbdev_register_lock); ++ return -ENFILE; ++ } ++ ++ memset (adap, 0, sizeof(struct dvb_adapter)); ++ INIT_LIST_HEAD (&adap->device_list); ++ ++ printk(KERN_INFO "DVB: registering new adapter (%s)\n", name); ++ ++ adap->num = num; ++ adap->name = name; ++ adap->module = module; ++ adap->device = device; ++ adap->mfe_shared = 0; ++ adap->mfe_dvbdev = NULL; ++ mutex_init (&adap->mfe_lock); ++ ++ list_add_tail (&adap->list_head, &dvb_adapter_list); ++ ++ mutex_unlock(&dvbdev_register_lock); ++ ++ return num; ++} ++EXPORT_SYMBOL(dvb_register_adapter); ++ ++ ++int dvb_unregister_adapter(struct dvb_adapter *adap) ++{ ++ mutex_lock(&dvbdev_register_lock); ++ list_del (&adap->list_head); ++ mutex_unlock(&dvbdev_register_lock); ++ return 0; ++} ++EXPORT_SYMBOL(dvb_unregister_adapter); ++ ++/* if the miracle happens and "generic_usercopy()" is included into ++ the kernel, then this can vanish. please don't make the mistake and ++ define this as video_usercopy(). this will introduce a dependecy ++ to the v4l "videodev.o" module, which is unnecessary for some ++ cards (ie. the budget dvb-cards don't need the v4l module...) */ ++int dvb_usercopy(struct file *file, ++ unsigned int cmd, unsigned long arg, ++ int (*func)(struct file *file, ++ unsigned int cmd, void *arg)) ++{ ++ char sbuf[128]; ++ void *mbuf = NULL; ++ void *parg = NULL; ++ int err = -EINVAL; ++ ++ /* Copy arguments into temp kernel buffer */ ++ switch (_IOC_DIR(cmd)) { ++ case _IOC_NONE: ++ /* ++ * For this command, the pointer is actually an integer ++ * argument. ++ */ ++ parg = (void *) arg; ++ break; ++ case _IOC_READ: /* some v4l ioctls are marked wrong ... */ ++ case _IOC_WRITE: ++ case (_IOC_WRITE | _IOC_READ): ++ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { ++ parg = sbuf; ++ } else { ++ /* too big to allocate from stack */ ++ mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); ++ if (NULL == mbuf) ++ return -ENOMEM; ++ parg = mbuf; ++ } ++ ++ err = -EFAULT; ++ if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) ++ goto out; ++ break; ++ } ++ ++ /* call driver */ ++ mutex_lock(&dvbdev_mutex); ++ if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) ++ err = -EINVAL; ++ mutex_unlock(&dvbdev_mutex); ++ ++ if (err < 0) ++ goto out; ++ ++ /* Copy results into user buffer */ ++ switch (_IOC_DIR(cmd)) ++ { ++ case _IOC_READ: ++ case (_IOC_WRITE | _IOC_READ): ++ if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) ++ err = -EFAULT; ++ break; ++ } ++ ++out: ++ kfree(mbuf); ++ return err; ++} ++ ++static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct dvb_device *dvbdev = dev_get_drvdata(dev); ++ ++ add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num); ++ add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]); ++ add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id); ++ return 0; ++} ++ ++static char *dvb_devnode(struct device *dev, umode_t *mode) ++{ ++ struct dvb_device *dvbdev = dev_get_drvdata(dev); ++ ++ return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d", ++ dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id); ++} ++ ++ ++static int __init init_dvbdev(void) ++{ ++ int retval; ++ dev_t dev = MKDEV(DVB_MAJOR, 0); ++ ++ if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) { ++ printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR); ++ return retval; ++ } ++ ++ cdev_init(&dvb_device_cdev, &dvb_device_fops); ++ if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) { ++ printk(KERN_ERR "dvb-core: unable register character device\n"); ++ goto error; ++ } ++ ++ dvb_class = class_create(THIS_MODULE, "dvb"); ++ if (IS_ERR(dvb_class)) { ++ retval = PTR_ERR(dvb_class); ++ goto error; ++ } ++ dvb_class->dev_uevent = dvb_uevent; ++ dvb_class->devnode = dvb_devnode; ++ return 0; ++ ++error: ++ cdev_del(&dvb_device_cdev); ++ unregister_chrdev_region(dev, MAX_DVB_MINORS); ++ return retval; ++} ++ ++ ++static void __exit exit_dvbdev(void) ++{ ++ class_destroy(dvb_class); ++ cdev_del(&dvb_device_cdev); ++ unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); ++} ++ ++subsys_initcall(init_dvbdev); ++module_exit(exit_dvbdev); ++ ++MODULE_DESCRIPTION("DVB Core Driver"); ++MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h +new file mode 100644 +index 0000000..fcc6ae9 +--- /dev/null ++++ b/drivers/media/dvb-core/dvbdev.h +@@ -0,0 +1,172 @@ ++/* ++ * dvbdev.h ++ * ++ * Copyright (C) 2000 Ralph Metzler & Marcus Metzler ++ * for convergence integrated media GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Lesser 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. ++ * ++ */ ++ ++#ifndef _DVBDEV_H_ ++#define _DVBDEV_H_ ++ ++#include ++#include ++#include ++#include ++ ++#define DVB_MAJOR 212 ++ ++#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0 ++ #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS ++#else ++ #define DVB_MAX_ADAPTERS 8 ++#endif ++ ++#define DVB_UNSET (-1) ++ ++#define DVB_DEVICE_VIDEO 0 ++#define DVB_DEVICE_AUDIO 1 ++#define DVB_DEVICE_SEC 2 ++#define DVB_DEVICE_FRONTEND 3 ++#define DVB_DEVICE_DEMUX 4 ++#define DVB_DEVICE_DVR 5 ++#define DVB_DEVICE_CA 6 ++#define DVB_DEVICE_NET 7 ++#define DVB_DEVICE_OSD 8 ++ ++#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \ ++ static short adapter_nr[] = \ ++ {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \ ++ module_param_array(adapter_nr, short, NULL, 0444); \ ++ MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers") ++ ++struct dvb_frontend; ++ ++struct dvb_adapter { ++ int num; ++ struct list_head list_head; ++ struct list_head device_list; ++ const char *name; ++ u8 proposed_mac [6]; ++ void* priv; ++ ++ struct device *device; ++ ++ struct module *module; ++ ++ int mfe_shared; /* indicates mutually exclusive frontends */ ++ struct dvb_device *mfe_dvbdev; /* frontend device in use */ ++ struct mutex mfe_lock; /* access lock for thread creation */ ++ ++ /* Allow the adapter/bridge driver to perform an action before and/or ++ * after the core handles an ioctl: ++ * ++ * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled. ++ * DVB_FE_IOCTL_POST indicates that the ioctl has been handled. ++ * ++ * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg: ++ * ++ * return 0 to allow dvb-core to handle the ioctl. ++ * return a positive int to prevent dvb-core from handling the ioctl, ++ * and exit without error. ++ * return a negative int to prevent dvb-core from handling the ioctl, ++ * and return that value as an error. ++ * ++ * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg: ++ * ++ * return 0 to allow the dvb_frontend ioctl handler to exit normally. ++ * return a negative int to cause the dvb_frontend ioctl handler to ++ * return that value as an error. ++ */ ++#define DVB_FE_IOCTL_PRE 0 ++#define DVB_FE_IOCTL_POST 1 ++ int (*fe_ioctl_override)(struct dvb_frontend *fe, ++ unsigned int cmd, void *parg, ++ unsigned int stage); ++}; ++ ++ ++struct dvb_device { ++ struct list_head list_head; ++ const struct file_operations *fops; ++ struct dvb_adapter *adapter; ++ int type; ++ int minor; ++ u32 id; ++ ++ /* in theory, 'users' can vanish now, ++ but I don't want to change too much now... */ ++ int readers; ++ int writers; ++ int users; ++ ++ wait_queue_head_t wait_queue; ++ /* don't really need those !? -- FIXME: use video_usercopy */ ++ int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg); ++ ++ void *priv; ++}; ++ ++ ++extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name, ++ struct module *module, struct device *device, ++ short *adapter_nums); ++extern int dvb_unregister_adapter (struct dvb_adapter *adap); ++ ++extern int dvb_register_device (struct dvb_adapter *adap, ++ struct dvb_device **pdvbdev, ++ const struct dvb_device *template, ++ void *priv, ++ int type); ++ ++extern void dvb_unregister_device (struct dvb_device *dvbdev); ++ ++extern int dvb_generic_open (struct inode *inode, struct file *file); ++extern int dvb_generic_release (struct inode *inode, struct file *file); ++extern long dvb_generic_ioctl (struct file *file, ++ unsigned int cmd, unsigned long arg); ++ ++/* we don't mess with video_usercopy() any more, ++we simply define out own dvb_usercopy(), which will hopefully become ++generic_usercopy() someday... */ ++ ++extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, ++ int (*func)(struct file *file, unsigned int cmd, void *arg)); ++ ++/** generic DVB attach function. */ ++#ifdef CONFIG_MEDIA_ATTACH ++#define dvb_attach(FUNCTION, ARGS...) ({ \ ++ void *__r = NULL; \ ++ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ ++ if (__a) { \ ++ __r = (void *) __a(ARGS); \ ++ if (__r == NULL) \ ++ symbol_put(FUNCTION); \ ++ } else { \ ++ printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \ ++ } \ ++ __r; \ ++}) ++ ++#else ++#define dvb_attach(FUNCTION, ARGS...) ({ \ ++ FUNCTION(ARGS); \ ++}) ++ ++#endif ++ ++#endif /* #ifndef _DVBDEV_H_ */ +diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig +new file mode 100644 +index 0000000..6f809a7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/Kconfig +@@ -0,0 +1,748 @@ ++menu "Customise DVB Frontends" ++ visible if !MEDIA_SUBDRV_AUTOSELECT ++ ++comment "Multistandard (satellite) frontends" ++ depends on DVB_CORE ++ ++config DVB_STB0899 ++ tristate "STB0899 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want ++ to support this demodulator based frontends ++ ++config DVB_STB6100 ++ tristate "STB6100 based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A Silicon tuner from ST used in conjunction with the STB0899 ++ demodulator. Say Y when you want to support this tuner. ++ ++config DVB_STV090x ++ tristate "STV0900/STV0903(A/B) based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. ++ Say Y when you want to support these frontends. ++ ++config DVB_STV6110x ++ tristate "STV6110/(A) based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A Silicon tuner that supports DVB-S and DVB-S2 modes ++ ++comment "Multistandard (cable + terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_DRXK ++ tristate "Micronas DRXK based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Micronas DRX-K DVB-C/T demodulator. ++ ++ Say Y when you want to support this frontend. ++ ++config DVB_TDA18271C2DD ++ tristate "NXP TDA18271C2 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ NXP TDA18271 silicon tuner. ++ ++ Say Y when you want to support this tuner. ++ ++comment "DVB-S (satellite) frontends" ++ depends on DVB_CORE ++ ++config DVB_CX24110 ++ tristate "Conexant CX24110 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CX24123 ++ tristate "Conexant CX24123 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MT312 ++ tristate "Zarlink VP310/MT312/ZL10313 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10036 ++ tristate "Zarlink ZL10036 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10039 ++ tristate "Zarlink ZL10039 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_S5H1420 ++ tristate "Samsung S5H1420 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV0288 ++ tristate "ST STV0288 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STB6000 ++ tristate "ST STB6000 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_STV0299 ++ tristate "ST STV0299 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV6110 ++ tristate "ST STV6110 silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_STV0900 ++ tristate "ST STV0900 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 demodulator. Say Y when you want to support this frontend. ++ ++config DVB_TDA8083 ++ tristate "Philips TDA8083 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10086 ++ tristate "Philips TDA10086 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA8261 ++ tristate "Philips TDA8261 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_VES1X93 ++ tristate "VLSI VES1893 or VES1993 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TUNER_ITD1000 ++ tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TUNER_CX24113 ++ tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++ ++config DVB_TDA826X ++ tristate "Philips TDA826X silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S silicon tuner module. Say Y when you want to support this tuner. ++ ++config DVB_TUA6100 ++ tristate "Infineon TUA6100 PLL" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S PLL chip. ++ ++config DVB_CX24116 ++ tristate "Conexant CX24116 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_SI21XX ++ tristate "Silicon Labs SI21XX based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TS2020 ++ tristate "Montage Tehnology TS2020 based tuners" ++ depends on DVB_CORE && I2C ++ default m if DVB_FE_CUSTOMISE ++ help ++ A DVB-S/S2 silicon tuner. Say Y when you want to support this tuner. ++ ++config DVB_DS3000 ++ tristate "Montage Tehnology DS3000 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/S2 tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MB86A16 ++ tristate "Fujitsu MB86A16 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S/DSS Direct Conversion reveiver. ++ Say Y when you want to support this frontend. ++ ++config DVB_TDA10071 ++ tristate "NXP TDA10071" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++comment "DVB-T (terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_SP8870 ++ tristate "Spase sp8870 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware sp8870" to ++ download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_SP887X ++ tristate "Spase sp887x based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware sp887x" to ++ download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_CX22700 ++ tristate "Conexant CX22700 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CX22702 ++ tristate "Conexant cx22702 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_S5H1432 ++ tristate "Samsung s5h1432 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_DRXD ++ tristate "Micronas DRXD driver" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ Note: this driver was based on vendor driver reference code (released ++ under the GPL) as opposed to the existing drx397xd driver, which ++ was written via reverse engineering. ++ ++config DVB_L64781 ++ tristate "LSI L64781" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA1004X ++ tristate "Philips TDA10045H/TDA10046H based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware tda10045", ++ "/Documentation/dvb/get_dvb_firmware tda10046" to ++ download/extract them, and then copy them to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_NXT6000 ++ tristate "NxtWave Communications NXT6000 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_MT352 ++ tristate "Zarlink MT352 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ZL10353 ++ tristate "Zarlink ZL10353 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_DIB3000MB ++ tristate "DiBcom 3000M-B" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB3000MC ++ tristate "DiBcom 3000P/M-C" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB7000M ++ tristate "DiBcom 7000MA/MB/PA/PB/MC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB7000P ++ tristate "DiBcom 7000PC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_DIB9000 ++ tristate "DiBcom 9000" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Designed for mobile usage. Say Y when you want ++ to support this frontend. ++ ++config DVB_TDA10048 ++ tristate "Philips TDA10048HN based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. Say Y when you want to support this frontend. ++ ++config DVB_AF9013 ++ tristate "Afatech AF9013 demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_EC100 ++ tristate "E3C EC100" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_HD29L2 ++ tristate "HDIC HD29L2" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_STV0367 ++ tristate "ST STV0367 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T/C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_CXD2820R ++ tristate "Sony CXD2820R" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_RTL2830 ++ tristate "Realtek RTL2830 DVB-T" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++config DVB_RTL2832 ++ tristate "Realtek RTL2832 DVB-T" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y when you want to support this frontend. ++ ++comment "DVB-C (cable) frontends" ++ depends on DVB_CORE ++ ++config DVB_VES1820 ++ tristate "VLSI VES1820 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10021 ++ tristate "Philips TDA10021 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA10023 ++ tristate "Philips TDA10023 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++config DVB_STV0297 ++ tristate "ST STV0297 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-C tuner module. Say Y when you want to support this frontend. ++ ++comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" ++ depends on DVB_CORE ++ ++config DVB_NXT200X ++ tristate "NxtWave Communications NXT2002/NXT2004 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware nxt2002" and ++ "/Documentation/dvb/get_dvb_firmware nxt2004" to ++ download/extract them, and then copy them to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_OR51211 ++ tristate "Oren OR51211 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB tuner module. Say Y when you want to support this frontend. ++ ++ This driver needs external firmware. Please use the command ++ "/Documentation/dvb/get_dvb_firmware or51211" to ++ download it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++config DVB_OR51132 ++ tristate "Oren OR51132 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++ This driver needs external firmware. Please use the commands ++ "/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or ++ "/Documentation/dvb/get_dvb_firmware or51132_qam" to ++ download firmwares for 8VSB and QAM64/256, respectively. Copy them to ++ /usr/lib/hotplug/firmware or /lib/firmware (depending on ++ configuration of firmware hotplug). ++ ++config DVB_BCM3510 ++ tristate "Broadcom BCM3510" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to ++ support this frontend. ++ ++config DVB_LGDT330X ++ tristate "LG Electronics LGDT3302/LGDT3303 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_LGDT3305 ++ tristate "LG Electronics LGDT3304 and LGDT3305 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_LG2160 ++ tristate "LG Electronics LG216x based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC/MH demodulator module. Say Y when you want ++ to support this frontend. ++ ++config DVB_S5H1409 ++ tristate "Samsung S5H1409 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++config DVB_AU8522 ++ depends on I2C ++ tristate ++ ++config DVB_AU8522_DTV ++ tristate "Auvitek AU8522 based DTV demod" ++ depends on DVB_CORE && I2C ++ select DVB_AU8522 ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when ++ you want to enable DTV demodulation support for this frontend. ++ ++config DVB_AU8522_V4L ++ tristate "Auvitek AU8522 based ATV demod" ++ depends on VIDEO_V4L2 && I2C ++ select DVB_AU8522 ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when ++ you want to enable ATV demodulation support for this frontend. ++ ++config DVB_S5H1411 ++ tristate "Samsung S5H1411 based" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want ++ to support this frontend. ++ ++comment "ISDB-T (terrestrial) frontends" ++ depends on DVB_CORE ++ ++config DVB_S921 ++ tristate "Sharp S921 frontend" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_DIB8000 ++ tristate "DiBcom 8000MB/MC" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. ++ Say Y when you want to support this frontend. ++ ++config DVB_MB86A20S ++ tristate "Fujitsu mb86a20s" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. ++ Say Y when you want to support this frontend. ++ ++comment "Digital terrestrial only tuners/PLL" ++ depends on DVB_CORE ++ ++config DVB_PLL ++ tristate "Generic I2C PLL based tuners" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ This module drives a number of tuners based on PLL chips with a ++ common I2C interface. Say Y when you want to support these tuners. ++ ++config DVB_TUNER_DIB0070 ++ tristate "DiBcom DiB0070 silicon base-band tuner" ++ depends on I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for the silicon baseband tuner DiB0070 from DiBcom. ++ This device is only used inside a SiP called together with a ++ demodulator for now. ++ ++config DVB_TUNER_DIB0090 ++ tristate "DiBcom DiB0090 silicon base-band tuner" ++ depends on I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A driver for the silicon baseband tuner DiB0090 from DiBcom. ++ This device is only used inside a SiP called together with a ++ demodulator for now. ++ ++comment "SEC control devices for DVB-S" ++ depends on DVB_CORE ++ ++config DVB_LNBP21 ++ tristate "LNBP21/LNBH24 SEC controllers" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chips. ++ ++config DVB_LNBP22 ++ tristate "LNBP22 SEC controllers" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ LNB power supply and control voltage ++ regulator chip with step-up converter ++ and I2C interface. ++ Say Y when you want to support this chip. ++ ++config DVB_ISL6405 ++ tristate "ISL6405 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chip. ++ ++config DVB_ISL6421 ++ tristate "ISL6421 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ An SEC control chip. ++ ++config DVB_ISL6423 ++ tristate "ISL6423 SEC controller" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A SEC controller chip from Intersil ++ ++config DVB_A8293 ++ tristate "Allegro A8293" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++config DVB_LGS8GL5 ++ tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_LGS8GXX ++ tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" ++ depends on DVB_CORE && I2C ++ select FW_LOADER ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_ATBM8830 ++ tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DMB-TH tuner module. Say Y when you want to support this frontend. ++ ++config DVB_TDA665x ++ tristate "TDA665x tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for tuner modules based on Philips TDA6650/TDA6651 chips. ++ Say Y when you want to support this chip. ++ ++ Currently supported tuners: ++ * Panasonic ENV57H12D5 (ET-50DT) ++ ++config DVB_IX2505V ++ tristate "Sharp IX2505V silicon tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. Say Y when you want to support this frontend. ++ ++config DVB_IT913X_FE ++ tristate "it913x frontend and it9137 tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-T tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_M88RS2000 ++ tristate "M88RS2000 DVB-S demodulator and tuner" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ help ++ A DVB-S tuner module. ++ Say Y when you want to support this frontend. ++ ++config DVB_AF9033 ++ tristate "Afatech AF9033 DVB-T demodulator" ++ depends on DVB_CORE && I2C ++ default m if !MEDIA_SUBDRV_AUTOSELECT ++ ++comment "Tools to develop new frontends" ++ ++config DVB_DUMMY_FE ++ tristate "Dummy frontend driver" ++ default n ++endmenu +diff --git a/drivers/media/dvb-frontends/Makefile b/drivers/media/dvb-frontends/Makefile +new file mode 100644 +index 0000000..cebc0fa +--- /dev/null ++++ b/drivers/media/dvb-frontends/Makefile +@@ -0,0 +1,106 @@ ++# ++# Makefile for the kernel DVB frontend device drivers. ++# ++ ++ccflags-y += -I$(srctree)/drivers/media/dvb-core/ ++ccflags-y += -I$(srctree)/drivers/media/tuners/ ++ ++stb0899-objs := stb0899_drv.o stb0899_algo.o ++stv0900-objs := stv0900_core.o stv0900_sw.o ++drxd-objs := drxd_firm.o drxd_hard.o ++cxd2820r-objs := cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o ++drxk-objs := drxk_hard.o ++ ++obj-$(CONFIG_DVB_PLL) += dvb-pll.o ++obj-$(CONFIG_DVB_STV0299) += stv0299.o ++obj-$(CONFIG_DVB_STB0899) += stb0899.o ++obj-$(CONFIG_DVB_STB6100) += stb6100.o ++obj-$(CONFIG_DVB_SP8870) += sp8870.o ++obj-$(CONFIG_DVB_CX22700) += cx22700.o ++obj-$(CONFIG_DVB_S5H1432) += s5h1432.o ++obj-$(CONFIG_DVB_CX24110) += cx24110.o ++obj-$(CONFIG_DVB_TDA8083) += tda8083.o ++obj-$(CONFIG_DVB_L64781) += l64781.o ++obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o ++obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o ++obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o ++obj-$(CONFIG_DVB_MT312) += mt312.o ++obj-$(CONFIG_DVB_VES1820) += ves1820.o ++obj-$(CONFIG_DVB_VES1X93) += ves1x93.o ++obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o ++obj-$(CONFIG_DVB_SP887X) += sp887x.o ++obj-$(CONFIG_DVB_NXT6000) += nxt6000.o ++obj-$(CONFIG_DVB_MT352) += mt352.o ++obj-$(CONFIG_DVB_ZL10036) += zl10036.o ++obj-$(CONFIG_DVB_ZL10039) += zl10039.o ++obj-$(CONFIG_DVB_ZL10353) += zl10353.o ++obj-$(CONFIG_DVB_CX22702) += cx22702.o ++obj-$(CONFIG_DVB_DRXD) += drxd.o ++obj-$(CONFIG_DVB_TDA10021) += tda10021.o ++obj-$(CONFIG_DVB_TDA10023) += tda10023.o ++obj-$(CONFIG_DVB_STV0297) += stv0297.o ++obj-$(CONFIG_DVB_NXT200X) += nxt200x.o ++obj-$(CONFIG_DVB_OR51211) += or51211.o ++obj-$(CONFIG_DVB_OR51132) += or51132.o ++obj-$(CONFIG_DVB_BCM3510) += bcm3510.o ++obj-$(CONFIG_DVB_S5H1420) += s5h1420.o ++obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o ++obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o ++obj-$(CONFIG_DVB_LG2160) += lg2160.o ++obj-$(CONFIG_DVB_CX24123) += cx24123.o ++obj-$(CONFIG_DVB_LNBP21) += lnbp21.o ++obj-$(CONFIG_DVB_LNBP22) += lnbp22.o ++obj-$(CONFIG_DVB_ISL6405) += isl6405.o ++obj-$(CONFIG_DVB_ISL6421) += isl6421.o ++obj-$(CONFIG_DVB_TDA10086) += tda10086.o ++obj-$(CONFIG_DVB_TDA826X) += tda826x.o ++obj-$(CONFIG_DVB_TDA8261) += tda8261.o ++obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o ++obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o ++obj-$(CONFIG_DVB_TUA6100) += tua6100.o ++obj-$(CONFIG_DVB_S5H1409) += s5h1409.o ++obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o ++obj-$(CONFIG_DVB_AU8522) += au8522_common.o ++obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o ++obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o ++obj-$(CONFIG_DVB_TDA10048) += tda10048.o ++obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o ++obj-$(CONFIG_DVB_S5H1411) += s5h1411.o ++obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o ++obj-$(CONFIG_DVB_TDA665x) += tda665x.o ++obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o ++obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o ++obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o ++obj-$(CONFIG_DVB_AF9013) += af9013.o ++obj-$(CONFIG_DVB_CX24116) += cx24116.o ++obj-$(CONFIG_DVB_SI21XX) += si21xx.o ++obj-$(CONFIG_DVB_STV0288) += stv0288.o ++obj-$(CONFIG_DVB_STB6000) += stb6000.o ++obj-$(CONFIG_DVB_S921) += s921.o ++obj-$(CONFIG_DVB_STV6110) += stv6110.o ++obj-$(CONFIG_DVB_STV0900) += stv0900.o ++obj-$(CONFIG_DVB_STV090x) += stv090x.o ++obj-$(CONFIG_DVB_STV6110x) += stv6110x.o ++obj-$(CONFIG_DVB_ISL6423) += isl6423.o ++obj-$(CONFIG_DVB_EC100) += ec100.o ++obj-$(CONFIG_DVB_HD29L2) += hd29l2.o ++obj-$(CONFIG_DVB_DS3000) += ds3000.o ++obj-$(CONFIG_DVB_TS2020) += ts2020.o ++obj-$(CONFIG_DVB_MB86A16) += mb86a16.o ++obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o ++obj-$(CONFIG_DVB_IX2505V) += ix2505v.o ++obj-$(CONFIG_DVB_STV0367) += stv0367.o ++obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o ++obj-$(CONFIG_DVB_DRXK) += drxk.o ++obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o ++obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o ++obj-$(CONFIG_DVB_A8293) += a8293.o ++obj-$(CONFIG_DVB_TDA10071) += tda10071.o ++obj-$(CONFIG_DVB_RTL2830) += rtl2830.o ++obj-$(CONFIG_DVB_RTL2832) += rtl2832.o ++obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o ++obj-$(CONFIG_DVB_AF9033) += af9033.o ++ +diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c +new file mode 100644 +index 0000000..74fbb5d +--- /dev/null ++++ b/drivers/media/dvb-frontends/a8293.c +@@ -0,0 +1,167 @@ ++/* ++ * Allegro A8293 SEC driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "dvb_frontend.h" ++#include "a8293.h" ++ ++struct a8293_priv { ++ struct i2c_adapter *i2c; ++ const struct a8293_config *cfg; ++ u8 reg[2]; ++}; ++ ++static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) ++{ ++ int ret; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->cfg->i2c_addr, ++ .len = len, ++ .buf = val, ++ } ++ }; ++ ++ if (rd) ++ msg[0].flags = I2C_M_RD; ++ else ++ msg[0].flags = 0; ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c failed=%d rd=%d\n", ++ KBUILD_MODNAME, ret, rd); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++static int a8293_wr(struct a8293_priv *priv, u8 *val, int len) ++{ ++ return a8293_i2c(priv, val, len, 0); ++} ++ ++static int a8293_rd(struct a8293_priv *priv, u8 *val, int len) ++{ ++ return a8293_i2c(priv, val, len, 1); ++} ++ ++static int a8293_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t fe_sec_voltage) ++{ ++ struct a8293_priv *priv = fe->sec_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: fe_sec_voltage=%d\n", __func__, ++ fe_sec_voltage); ++ ++ switch (fe_sec_voltage) { ++ case SEC_VOLTAGE_OFF: ++ /* ENB=0 */ ++ priv->reg[0] = 0x10; ++ break; ++ case SEC_VOLTAGE_13: ++ /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ ++ priv->reg[0] = 0x31; ++ break; ++ case SEC_VOLTAGE_18: ++ /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ ++ priv->reg[0] = 0x38; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = a8293_wr(priv, &priv->reg[0], 1); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static void a8293_release_sec(struct dvb_frontend *fe) ++{ ++ a8293_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, const struct a8293_config *cfg) ++{ ++ int ret; ++ struct a8293_priv *priv = NULL; ++ u8 buf[2]; ++ ++ /* allocate memory for the internal priv */ ++ priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL); ++ if (priv == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* setup the priv */ ++ priv->i2c = i2c; ++ priv->cfg = cfg; ++ fe->sec_priv = priv; ++ ++ /* check if the SEC is there */ ++ ret = a8293_rd(priv, buf, 2); ++ if (ret) ++ goto err; ++ ++ /* ENB=0 */ ++ priv->reg[0] = 0x10; ++ ret = a8293_wr(priv, &priv->reg[0], 1); ++ if (ret) ++ goto err; ++ ++ /* TMODE=0, TGATE=1 */ ++ priv->reg[1] = 0x82; ++ ret = a8293_wr(priv, &priv->reg[1], 1); ++ if (ret) ++ goto err; ++ ++ fe->ops.release_sec = a8293_release_sec; ++ ++ /* override frontend ops */ ++ fe->ops.set_voltage = a8293_set_voltage; ++ ++ dev_info(&priv->i2c->dev, "%s: Allegro A8293 SEC attached\n", ++ KBUILD_MODNAME); ++ ++ return fe; ++err: ++ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(a8293_attach); ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Allegro A8293 SEC driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/a8293.h b/drivers/media/dvb-frontends/a8293.h +new file mode 100644 +index 0000000..ed29e55 +--- /dev/null ++++ b/drivers/media/dvb-frontends/a8293.h +@@ -0,0 +1,41 @@ ++/* ++ * Allegro A8293 SEC driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef A8293_H ++#define A8293_H ++ ++struct a8293_config { ++ u8 i2c_addr; ++}; ++ ++#if defined(CONFIG_DVB_A8293) || \ ++ (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, const struct a8293_config *cfg); ++#else ++static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, const struct a8293_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* A8293_H */ +diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c +new file mode 100644 +index 0000000..a204f28 +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9013.c +@@ -0,0 +1,1548 @@ ++/* ++ * Afatech AF9013 demodulator driver ++ * ++ * Copyright (C) 2007 Antti Palosaari ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * Thanks to Afatech who kindly provided information. ++ * ++ * 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 "af9013_priv.h" ++ ++struct af9013_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct af9013_config config; ++ ++ /* tuner/demod RF and IF AGC limits used for signal strength calc */ ++ u8 signal_strength_en, rf_50, rf_80, if_50, if_80; ++ u16 signal_strength; ++ u32 ber; ++ u32 ucblocks; ++ u16 snr; ++ u32 bandwidth_hz; ++ fe_status_t fe_status; ++ unsigned long set_frontend_jiffies; ++ unsigned long read_status_jiffies; ++ bool first_tune; ++ bool i2c_gate_state; ++ unsigned int statistics_step:3; ++ struct delayed_work statistics_work; ++}; ++ ++/* write multiple registers */ ++static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg, ++ const u8 *val, int len) ++{ ++ int ret; ++ u8 buf[3+len]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->config.i2c_addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = (reg >> 8) & 0xff; ++ buf[1] = (reg >> 0) & 0xff; ++ buf[2] = mbox; ++ memcpy(&buf[3], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%04x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* read multiple registers */ ++static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg, ++ u8 *val, int len) ++{ ++ int ret; ++ u8 buf[3]; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = priv->config.i2c_addr, ++ .flags = 0, ++ .len = 3, ++ .buf = buf, ++ }, { ++ .addr = priv->config.i2c_addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = val, ++ } ++ }; ++ ++ buf[0] = (reg >> 8) & 0xff; ++ buf[1] = (reg >> 0) & 0xff; ++ buf[2] = mbox; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%04x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* write multiple registers */ ++static int af9013_wr_regs(struct af9013_state *priv, u16 reg, const u8 *val, ++ int len) ++{ ++ int ret, i; ++ u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(1 << 0); ++ ++ if ((priv->config.ts_mode == AF9013_TS_USB) && ++ ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) { ++ mbox |= ((len - 1) << 2); ++ ret = af9013_wr_regs_i2c(priv, mbox, reg, val, len); ++ } else { ++ for (i = 0; i < len; i++) { ++ ret = af9013_wr_regs_i2c(priv, mbox, reg+i, val+i, 1); ++ if (ret) ++ goto err; ++ } ++ } ++ ++err: ++ return 0; ++} ++ ++/* read multiple registers */ ++static int af9013_rd_regs(struct af9013_state *priv, u16 reg, u8 *val, int len) ++{ ++ int ret, i; ++ u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(0 << 0); ++ ++ if ((priv->config.ts_mode == AF9013_TS_USB) && ++ ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) { ++ mbox |= ((len - 1) << 2); ++ ret = af9013_rd_regs_i2c(priv, mbox, reg, val, len); ++ } else { ++ for (i = 0; i < len; i++) { ++ ret = af9013_rd_regs_i2c(priv, mbox, reg+i, val+i, 1); ++ if (ret) ++ goto err; ++ } ++ } ++ ++err: ++ return 0; ++} ++ ++/* write single register */ ++static int af9013_wr_reg(struct af9013_state *priv, u16 reg, u8 val) ++{ ++ return af9013_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register */ ++static int af9013_rd_reg(struct af9013_state *priv, u16 reg, u8 *val) ++{ ++ return af9013_rd_regs(priv, reg, val, 1); ++} ++ ++static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val, ++ u8 len) ++{ ++ u8 mbox = (1 << 7)|(1 << 6)|((len - 1) << 2)|(1 << 1)|(1 << 0); ++ return af9013_wr_regs_i2c(state, mbox, reg, val, len); ++} ++ ++static int af9013_wr_reg_bits(struct af9013_state *state, u16 reg, int pos, ++ int len, u8 val) ++{ ++ int ret; ++ u8 tmp, mask; ++ ++ /* no need for read if whole reg is written */ ++ if (len != 8) { ++ ret = af9013_rd_reg(state, reg, &tmp); ++ if (ret) ++ return ret; ++ ++ mask = (0xff >> (8 - len)) << pos; ++ val <<= pos; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return af9013_wr_reg(state, reg, val); ++} ++ ++static int af9013_rd_reg_bits(struct af9013_state *state, u16 reg, int pos, ++ int len, u8 *val) ++{ ++ int ret; ++ u8 tmp; ++ ++ ret = af9013_rd_reg(state, reg, &tmp); ++ if (ret) ++ return ret; ++ ++ *val = (tmp >> pos); ++ *val &= (0xff >> (8 - len)); ++ ++ return 0; ++} ++ ++static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) ++{ ++ int ret; ++ u8 pos; ++ u16 addr; ++ ++ dev_dbg(&state->i2c->dev, "%s: gpio=%d gpioval=%02x\n", ++ __func__, gpio, gpioval); ++ ++ /* ++ * GPIO0 & GPIO1 0xd735 ++ * GPIO2 & GPIO3 0xd736 ++ */ ++ ++ switch (gpio) { ++ case 0: ++ case 1: ++ addr = 0xd735; ++ break; ++ case 2: ++ case 3: ++ addr = 0xd736; ++ break; ++ ++ default: ++ dev_err(&state->i2c->dev, "%s: invalid gpio=%d\n", ++ KBUILD_MODNAME, gpio); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ switch (gpio) { ++ case 0: ++ case 2: ++ pos = 0; ++ break; ++ case 1: ++ case 3: ++ default: ++ pos = 4; ++ break; ++ } ++ ++ ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static u32 af9013_div(struct af9013_state *state, u32 a, u32 b, u32 x) ++{ ++ u32 r = 0, c = 0, i; ++ ++ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x); ++ ++ if (a > b) { ++ c = a / b; ++ a = a - c * b; ++ } ++ ++ for (i = 0; i < x; i++) { ++ if (a >= b) { ++ r += 1; ++ a -= b; ++ } ++ a <<= 1; ++ r <<= 1; ++ } ++ r = (c << (u32)x) + r; ++ ++ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n", ++ __func__, a, b, x, r, r); ++ ++ return r; ++} ++ ++static int af9013_power_ctrl(struct af9013_state *state, u8 onoff) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ dev_dbg(&state->i2c->dev, "%s: onoff=%d\n", __func__, onoff); ++ ++ /* enable reset */ ++ ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* start reset mechanism */ ++ ret = af9013_wr_reg(state, 0xaeff, 1); ++ if (ret) ++ goto err; ++ ++ /* wait reset performs */ ++ for (i = 0; i < 150; i++) { ++ ret = af9013_rd_reg_bits(state, 0xd417, 1, 1, &tmp); ++ if (ret) ++ goto err; ++ ++ if (tmp) ++ break; /* reset done */ ++ ++ usleep_range(5000, 25000); ++ } ++ ++ if (!tmp) ++ return -ETIMEDOUT; ++ ++ if (onoff) { ++ /* clear reset */ ++ ret = af9013_wr_reg_bits(state, 0xd417, 1, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* disable reset */ ++ ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 0); ++ ++ /* power on */ ++ ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 0); ++ } else { ++ /* power off */ ++ ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 1); ++ } ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* reset and start BER counter */ ++ ret = af9013_wr_reg_bits(state, 0xd391, 4, 1, 1); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[5]; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* check if error bit count is ready */ ++ ret = af9013_rd_reg_bits(state, 0xd391, 4, 1, &buf[0]); ++ if (ret) ++ goto err; ++ ++ if (!buf[0]) { ++ dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__); ++ return 0; ++ } ++ ++ ret = af9013_rd_regs(state, 0xd387, buf, 5); ++ if (ret) ++ goto err; ++ ++ state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0]; ++ state->ucblocks += (buf[4] << 8) | buf[3]; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_statistics_snr_start(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* start SNR meas */ ++ ret = af9013_wr_reg_bits(state, 0xd2e1, 3, 1, 1); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_statistics_snr_result(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret, i, len; ++ u8 buf[3], tmp; ++ u32 snr_val; ++ const struct af9013_snr *uninitialized_var(snr_lut); ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* check if SNR ready */ ++ ret = af9013_rd_reg_bits(state, 0xd2e1, 3, 1, &tmp); ++ if (ret) ++ goto err; ++ ++ if (!tmp) { ++ dev_dbg(&state->i2c->dev, "%s: not ready\n", __func__); ++ return 0; ++ } ++ ++ /* read value */ ++ ret = af9013_rd_regs(state, 0xd2e3, buf, 3); ++ if (ret) ++ goto err; ++ ++ snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; ++ ++ /* read current modulation */ ++ ret = af9013_rd_reg(state, 0xd3c1, &tmp); ++ if (ret) ++ goto err; ++ ++ switch ((tmp >> 6) & 3) { ++ case 0: ++ len = ARRAY_SIZE(qpsk_snr_lut); ++ snr_lut = qpsk_snr_lut; ++ break; ++ case 1: ++ len = ARRAY_SIZE(qam16_snr_lut); ++ snr_lut = qam16_snr_lut; ++ break; ++ case 2: ++ len = ARRAY_SIZE(qam64_snr_lut); ++ snr_lut = qam64_snr_lut; ++ break; ++ default: ++ goto err; ++ break; ++ } ++ ++ for (i = 0; i < len; i++) { ++ tmp = snr_lut[i].snr; ++ ++ if (snr_val < snr_lut[i].val) ++ break; ++ } ++ state->snr = tmp * 10; /* dB/10 */ ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_statistics_signal_strength(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret = 0; ++ u8 buf[2], rf_gain, if_gain; ++ int signal_strength; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ if (!state->signal_strength_en) ++ return 0; ++ ++ ret = af9013_rd_regs(state, 0xd07c, buf, 2); ++ if (ret) ++ goto err; ++ ++ rf_gain = buf[0]; ++ if_gain = buf[1]; ++ ++ signal_strength = (0xffff / \ ++ (9 * (state->rf_50 + state->if_50) - \ ++ 11 * (state->rf_80 + state->if_80))) * \ ++ (10 * (rf_gain + if_gain) - \ ++ 11 * (state->rf_80 + state->if_80)); ++ if (signal_strength < 0) ++ signal_strength = 0; ++ else if (signal_strength > 0xffff) ++ signal_strength = 0xffff; ++ ++ state->signal_strength = signal_strength; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static void af9013_statistics_work(struct work_struct *work) ++{ ++ struct af9013_state *state = container_of(work, ++ struct af9013_state, statistics_work.work); ++ unsigned int next_msec; ++ ++ /* update only signal strength when demod is not locked */ ++ if (!(state->fe_status & FE_HAS_LOCK)) { ++ state->statistics_step = 0; ++ state->ber = 0; ++ state->snr = 0; ++ } ++ ++ switch (state->statistics_step) { ++ default: ++ state->statistics_step = 0; ++ case 0: ++ af9013_statistics_signal_strength(&state->fe); ++ state->statistics_step++; ++ next_msec = 300; ++ break; ++ case 1: ++ af9013_statistics_snr_start(&state->fe); ++ state->statistics_step++; ++ next_msec = 200; ++ break; ++ case 2: ++ af9013_statistics_ber_unc_start(&state->fe); ++ state->statistics_step++; ++ next_msec = 1000; ++ break; ++ case 3: ++ af9013_statistics_snr_result(&state->fe); ++ state->statistics_step++; ++ next_msec = 400; ++ break; ++ case 4: ++ af9013_statistics_ber_unc_result(&state->fe); ++ state->statistics_step++; ++ next_msec = 100; ++ break; ++ } ++ ++ schedule_delayed_work(&state->statistics_work, ++ msecs_to_jiffies(next_msec)); ++} ++ ++static int af9013_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 800; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ ++ return 0; ++} ++ ++static int af9013_set_frontend(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i, sampling_freq; ++ bool auto_mode, spec_inv; ++ u8 buf[6]; ++ u32 if_frequency, freq_cw; ++ ++ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", ++ __func__, c->frequency, c->bandwidth_hz); ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* program CFOE coefficients */ ++ if (c->bandwidth_hz != state->bandwidth_hz) { ++ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { ++ if (coeff_lut[i].clock == state->config.clock && ++ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { ++ break; ++ } ++ } ++ ++ ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val, ++ sizeof(coeff_lut[i].val)); ++ } ++ ++ /* program frequency control */ ++ if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) { ++ /* get used IF frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) ++ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); ++ else ++ if_frequency = state->config.if_frequency; ++ ++ dev_dbg(&state->i2c->dev, "%s: if_frequency=%d\n", ++ __func__, if_frequency); ++ ++ sampling_freq = if_frequency; ++ ++ while (sampling_freq > (state->config.clock / 2)) ++ sampling_freq -= state->config.clock; ++ ++ if (sampling_freq < 0) { ++ sampling_freq *= -1; ++ spec_inv = state->config.spec_inv; ++ } else { ++ spec_inv = !state->config.spec_inv; ++ } ++ ++ freq_cw = af9013_div(state, sampling_freq, state->config.clock, ++ 23); ++ ++ if (spec_inv) ++ freq_cw = 0x800000 - freq_cw; ++ ++ buf[0] = (freq_cw >> 0) & 0xff; ++ buf[1] = (freq_cw >> 8) & 0xff; ++ buf[2] = (freq_cw >> 16) & 0x7f; ++ ++ freq_cw = 0x800000 - freq_cw; ++ ++ buf[3] = (freq_cw >> 0) & 0xff; ++ buf[4] = (freq_cw >> 8) & 0xff; ++ buf[5] = (freq_cw >> 16) & 0x7f; ++ ++ ret = af9013_wr_regs(state, 0xd140, buf, 3); ++ if (ret) ++ goto err; ++ ++ ret = af9013_wr_regs(state, 0x9be7, buf, 6); ++ if (ret) ++ goto err; ++ } ++ ++ /* clear TPS lock flag */ ++ ret = af9013_wr_reg_bits(state, 0xd330, 3, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* clear MPEG2 lock flag */ ++ ret = af9013_wr_reg_bits(state, 0xd507, 6, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* empty channel function */ ++ ret = af9013_wr_reg_bits(state, 0x9bfe, 0, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* empty DVB-T channel function */ ++ ret = af9013_wr_reg_bits(state, 0x9bc2, 0, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* transmission parameters */ ++ auto_mode = false; ++ memset(buf, 0, 3); ++ ++ switch (c->transmission_mode) { ++ case TRANSMISSION_MODE_AUTO: ++ auto_mode = 1; ++ break; ++ case TRANSMISSION_MODE_2K: ++ break; ++ case TRANSMISSION_MODE_8K: ++ buf[0] |= (1 << 0); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid transmission_mode\n", ++ __func__); ++ auto_mode = 1; ++ } ++ ++ switch (c->guard_interval) { ++ case GUARD_INTERVAL_AUTO: ++ auto_mode = 1; ++ break; ++ case GUARD_INTERVAL_1_32: ++ break; ++ case GUARD_INTERVAL_1_16: ++ buf[0] |= (1 << 2); ++ break; ++ case GUARD_INTERVAL_1_8: ++ buf[0] |= (2 << 2); ++ break; ++ case GUARD_INTERVAL_1_4: ++ buf[0] |= (3 << 2); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid guard_interval\n", ++ __func__); ++ auto_mode = 1; ++ } ++ ++ switch (c->hierarchy) { ++ case HIERARCHY_AUTO: ++ auto_mode = 1; ++ break; ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ buf[0] |= (1 << 4); ++ break; ++ case HIERARCHY_2: ++ buf[0] |= (2 << 4); ++ break; ++ case HIERARCHY_4: ++ buf[0] |= (3 << 4); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid hierarchy\n", __func__); ++ auto_mode = 1; ++ } ++ ++ switch (c->modulation) { ++ case QAM_AUTO: ++ auto_mode = 1; ++ break; ++ case QPSK: ++ break; ++ case QAM_16: ++ buf[1] |= (1 << 6); ++ break; ++ case QAM_64: ++ buf[1] |= (2 << 6); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid modulation\n", __func__); ++ auto_mode = 1; ++ } ++ ++ /* Use HP. How and which case we can switch to LP? */ ++ buf[1] |= (1 << 4); ++ ++ switch (c->code_rate_HP) { ++ case FEC_AUTO: ++ auto_mode = 1; ++ break; ++ case FEC_1_2: ++ break; ++ case FEC_2_3: ++ buf[2] |= (1 << 0); ++ break; ++ case FEC_3_4: ++ buf[2] |= (2 << 0); ++ break; ++ case FEC_5_6: ++ buf[2] |= (3 << 0); ++ break; ++ case FEC_7_8: ++ buf[2] |= (4 << 0); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid code_rate_HP\n", ++ __func__); ++ auto_mode = 1; ++ } ++ ++ switch (c->code_rate_LP) { ++ case FEC_AUTO: ++ auto_mode = 1; ++ break; ++ case FEC_1_2: ++ break; ++ case FEC_2_3: ++ buf[2] |= (1 << 3); ++ break; ++ case FEC_3_4: ++ buf[2] |= (2 << 3); ++ break; ++ case FEC_5_6: ++ buf[2] |= (3 << 3); ++ break; ++ case FEC_7_8: ++ buf[2] |= (4 << 3); ++ break; ++ case FEC_NONE: ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid code_rate_LP\n", ++ __func__); ++ auto_mode = 1; ++ } ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ break; ++ case 7000000: ++ buf[1] |= (1 << 2); ++ break; ++ case 8000000: ++ buf[1] |= (2 << 2); ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n", ++ __func__); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = af9013_wr_regs(state, 0xd3c0, buf, 3); ++ if (ret) ++ goto err; ++ ++ if (auto_mode) { ++ /* clear easy mode flag */ ++ ret = af9013_wr_reg(state, 0xaefd, 0); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&state->i2c->dev, "%s: auto params\n", __func__); ++ } else { ++ /* set easy mode flag */ ++ ret = af9013_wr_reg(state, 0xaefd, 1); ++ if (ret) ++ goto err; ++ ++ ret = af9013_wr_reg(state, 0xaefe, 0); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&state->i2c->dev, "%s: manual params\n", __func__); ++ } ++ ++ /* tune */ ++ ret = af9013_wr_reg(state, 0xffff, 0); ++ if (ret) ++ goto err; ++ ++ state->bandwidth_hz = c->bandwidth_hz; ++ state->set_frontend_jiffies = jiffies; ++ state->first_tune = false; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[3]; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ ret = af9013_rd_regs(state, 0xd3c0, buf, 3); ++ if (ret) ++ goto err; ++ ++ switch ((buf[1] >> 6) & 3) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ } ++ ++ switch ((buf[0] >> 0) & 3) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ } ++ ++ switch ((buf[0] >> 2) & 3) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((buf[0] >> 4) & 7) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ switch ((buf[2] >> 0) & 7) { ++ case 0: ++ c->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ switch ((buf[2] >> 3) & 7) { ++ case 0: ++ c->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ switch ((buf[1] >> 2) & 3) { ++ case 0: ++ c->bandwidth_hz = 6000000; ++ break; ++ case 1: ++ c->bandwidth_hz = 7000000; ++ break; ++ case 2: ++ c->bandwidth_hz = 8000000; ++ break; ++ } ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ ++ /* ++ * Return status from the cache if it is younger than 2000ms with the ++ * exception of last tune is done during 4000ms. ++ */ ++ if (time_is_after_jiffies( ++ state->read_status_jiffies + msecs_to_jiffies(2000)) && ++ time_is_before_jiffies( ++ state->set_frontend_jiffies + msecs_to_jiffies(4000)) ++ ) { ++ *status = state->fe_status; ++ return 0; ++ } else { ++ *status = 0; ++ } ++ ++ /* MPEG2 lock */ ++ ret = af9013_rd_reg_bits(state, 0xd507, 6, 1, &tmp); ++ if (ret) ++ goto err; ++ ++ if (tmp) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | ++ FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ if (!*status) { ++ /* TPS lock */ ++ ret = af9013_rd_reg_bits(state, 0xd330, 3, 1, &tmp); ++ if (ret) ++ goto err; ++ ++ if (tmp) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI; ++ } ++ ++ state->fe_status = *status; ++ state->read_status_jiffies = jiffies; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ *snr = state->snr; ++ return 0; ++} ++ ++static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ *strength = state->signal_strength; ++ return 0; ++} ++ ++static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ *ber = state->ber; ++ return 0; ++} ++ ++static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ *ucblocks = state->ucblocks; ++ return 0; ++} ++ ++static int af9013_init(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret, i, len; ++ u8 buf[3], tmp; ++ u32 adc_cw; ++ const struct af9013_reg_bit *init; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* power on */ ++ ret = af9013_power_ctrl(state, 1); ++ if (ret) ++ goto err; ++ ++ /* enable ADC */ ++ ret = af9013_wr_reg(state, 0xd73a, 0xa4); ++ if (ret) ++ goto err; ++ ++ /* write API version to firmware */ ++ ret = af9013_wr_regs(state, 0x9bf2, state->config.api_version, 4); ++ if (ret) ++ goto err; ++ ++ /* program ADC control */ ++ switch (state->config.clock) { ++ case 28800000: /* 28.800 MHz */ ++ tmp = 0; ++ break; ++ case 20480000: /* 20.480 MHz */ ++ tmp = 1; ++ break; ++ case 28000000: /* 28.000 MHz */ ++ tmp = 2; ++ break; ++ case 25000000: /* 25.000 MHz */ ++ tmp = 3; ++ break; ++ default: ++ dev_err(&state->i2c->dev, "%s: invalid clock\n", ++ KBUILD_MODNAME); ++ return -EINVAL; ++ } ++ ++ adc_cw = af9013_div(state, state->config.clock, 1000000ul, 19); ++ buf[0] = (adc_cw >> 0) & 0xff; ++ buf[1] = (adc_cw >> 8) & 0xff; ++ buf[2] = (adc_cw >> 16) & 0xff; ++ ++ ret = af9013_wr_regs(state, 0xd180, buf, 3); ++ if (ret) ++ goto err; ++ ++ ret = af9013_wr_reg_bits(state, 0x9bd2, 0, 4, tmp); ++ if (ret) ++ goto err; ++ ++ /* set I2C master clock */ ++ ret = af9013_wr_reg(state, 0xd416, 0x14); ++ if (ret) ++ goto err; ++ ++ /* set 16 embx */ ++ ret = af9013_wr_reg_bits(state, 0xd700, 1, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* set no trigger */ ++ ret = af9013_wr_reg_bits(state, 0xd700, 2, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* set read-update bit for constellation */ ++ ret = af9013_wr_reg_bits(state, 0xd371, 1, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* settings for mp2if */ ++ if (state->config.ts_mode == AF9013_TS_USB) { ++ /* AF9015 split PSB to 1.5k + 0.5k */ ++ ret = af9013_wr_reg_bits(state, 0xd50b, 2, 1, 1); ++ if (ret) ++ goto err; ++ } else { ++ /* AF9013 change the output bit to data7 */ ++ ret = af9013_wr_reg_bits(state, 0xd500, 3, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* AF9013 set mpeg to full speed */ ++ ret = af9013_wr_reg_bits(state, 0xd502, 4, 1, 1); ++ if (ret) ++ goto err; ++ } ++ ++ ret = af9013_wr_reg_bits(state, 0xd520, 4, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* load OFSM settings */ ++ dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__); ++ len = ARRAY_SIZE(ofsm_init); ++ init = ofsm_init; ++ for (i = 0; i < len; i++) { ++ ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos, ++ init[i].len, init[i].val); ++ if (ret) ++ goto err; ++ } ++ ++ /* load tuner specific settings */ ++ dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n", ++ __func__); ++ switch (state->config.tuner) { ++ case AF9013_TUNER_MXL5003D: ++ len = ARRAY_SIZE(tuner_init_mxl5003d); ++ init = tuner_init_mxl5003d; ++ break; ++ case AF9013_TUNER_MXL5005D: ++ case AF9013_TUNER_MXL5005R: ++ case AF9013_TUNER_MXL5007T: ++ len = ARRAY_SIZE(tuner_init_mxl5005); ++ init = tuner_init_mxl5005; ++ break; ++ case AF9013_TUNER_ENV77H11D5: ++ len = ARRAY_SIZE(tuner_init_env77h11d5); ++ init = tuner_init_env77h11d5; ++ break; ++ case AF9013_TUNER_MT2060: ++ len = ARRAY_SIZE(tuner_init_mt2060); ++ init = tuner_init_mt2060; ++ break; ++ case AF9013_TUNER_MC44S803: ++ len = ARRAY_SIZE(tuner_init_mc44s803); ++ init = tuner_init_mc44s803; ++ break; ++ case AF9013_TUNER_QT1010: ++ case AF9013_TUNER_QT1010A: ++ len = ARRAY_SIZE(tuner_init_qt1010); ++ init = tuner_init_qt1010; ++ break; ++ case AF9013_TUNER_MT2060_2: ++ len = ARRAY_SIZE(tuner_init_mt2060_2); ++ init = tuner_init_mt2060_2; ++ break; ++ case AF9013_TUNER_TDA18271: ++ case AF9013_TUNER_TDA18218: ++ len = ARRAY_SIZE(tuner_init_tda18271); ++ init = tuner_init_tda18271; ++ break; ++ case AF9013_TUNER_UNKNOWN: ++ default: ++ len = ARRAY_SIZE(tuner_init_unknown); ++ init = tuner_init_unknown; ++ break; ++ } ++ ++ for (i = 0; i < len; i++) { ++ ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos, ++ init[i].len, init[i].val); ++ if (ret) ++ goto err; ++ } ++ ++ /* TS mode */ ++ ret = af9013_wr_reg_bits(state, 0xd500, 1, 2, state->config.ts_mode); ++ if (ret) ++ goto err; ++ ++ /* enable lock led */ ++ ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 1); ++ if (ret) ++ goto err; ++ ++ /* check if we support signal strength */ ++ if (!state->signal_strength_en) { ++ ret = af9013_rd_reg_bits(state, 0x9bee, 0, 1, ++ &state->signal_strength_en); ++ if (ret) ++ goto err; ++ } ++ ++ /* read values needed for signal strength calculation */ ++ if (state->signal_strength_en && !state->rf_50) { ++ ret = af9013_rd_reg(state, 0x9bbd, &state->rf_50); ++ if (ret) ++ goto err; ++ ++ ret = af9013_rd_reg(state, 0x9bd0, &state->rf_80); ++ if (ret) ++ goto err; ++ ++ ret = af9013_rd_reg(state, 0x9be2, &state->if_50); ++ if (ret) ++ goto err; ++ ++ ret = af9013_rd_reg(state, 0x9be4, &state->if_80); ++ if (ret) ++ goto err; ++ } ++ ++ /* SNR */ ++ ret = af9013_wr_reg(state, 0xd2e2, 1); ++ if (ret) ++ goto err; ++ ++ /* BER / UCB */ ++ buf[0] = (10000 >> 0) & 0xff; ++ buf[1] = (10000 >> 8) & 0xff; ++ ret = af9013_wr_regs(state, 0xd385, buf, 2); ++ if (ret) ++ goto err; ++ ++ /* enable FEC monitor */ ++ ret = af9013_wr_reg_bits(state, 0xd392, 1, 1, 1); ++ if (ret) ++ goto err; ++ ++ state->first_tune = true; ++ schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400)); ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_sleep(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* stop statistics polling */ ++ cancel_delayed_work_sync(&state->statistics_work); ++ ++ /* disable lock led */ ++ ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 0); ++ if (ret) ++ goto err; ++ ++ /* power off */ ++ ret = af9013_power_ctrl(state, 0); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ int ret; ++ struct af9013_state *state = fe->demodulator_priv; ++ ++ dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable); ++ ++ /* gate already open or close */ ++ if (state->i2c_gate_state == enable) ++ return 0; ++ ++ if (state->config.ts_mode == AF9013_TS_USB) ++ ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable); ++ else ++ ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable); ++ if (ret) ++ goto err; ++ ++ state->i2c_gate_state = enable; ++ ++ return ret; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static void af9013_release(struct dvb_frontend *fe) ++{ ++ struct af9013_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops af9013_ops; ++ ++static int af9013_download_firmware(struct af9013_state *state) ++{ ++ int i, len, remaining, ret; ++ const struct firmware *fw; ++ u16 checksum = 0; ++ u8 val; ++ u8 fw_params[4]; ++ u8 *fw_file = AF9013_FIRMWARE; ++ ++ msleep(100); ++ /* check whether firmware is already running */ ++ ret = af9013_rd_reg(state, 0x98be, &val); ++ if (ret) ++ goto err; ++ else ++ dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n", ++ __func__, val); ++ ++ if (val == 0x0c) /* fw is running, no need for download */ ++ goto exit; ++ ++ dev_info(&state->i2c->dev, "%s: found a '%s' in cold state, will try " \ ++ "to load a firmware\n", ++ KBUILD_MODNAME, af9013_ops.info.name); ++ ++ /* request the firmware, this will block and timeout */ ++ ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); ++ if (ret) { ++ dev_info(&state->i2c->dev, "%s: did not find the firmware " \ ++ "file. (%s) Please see linux/Documentation/dvb/ for " \ ++ "more details on firmware-problems. (%d)\n", ++ KBUILD_MODNAME, fw_file, ret); ++ goto err; ++ } ++ ++ dev_info(&state->i2c->dev, "%s: downloading firmware from file '%s'\n", ++ KBUILD_MODNAME, fw_file); ++ ++ /* calc checksum */ ++ for (i = 0; i < fw->size; i++) ++ checksum += fw->data[i]; ++ ++ fw_params[0] = checksum >> 8; ++ fw_params[1] = checksum & 0xff; ++ fw_params[2] = fw->size >> 8; ++ fw_params[3] = fw->size & 0xff; ++ ++ /* write fw checksum & size */ ++ ret = af9013_write_ofsm_regs(state, 0x50fc, ++ fw_params, sizeof(fw_params)); ++ if (ret) ++ goto err_release; ++ ++ #define FW_ADDR 0x5100 /* firmware start address */ ++ #define LEN_MAX 16 /* max packet size */ ++ for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { ++ len = remaining; ++ if (len > LEN_MAX) ++ len = LEN_MAX; ++ ++ ret = af9013_write_ofsm_regs(state, ++ FW_ADDR + fw->size - remaining, ++ (u8 *) &fw->data[fw->size - remaining], len); ++ if (ret) { ++ dev_err(&state->i2c->dev, ++ "%s: firmware download failed=%d\n", ++ KBUILD_MODNAME, ret); ++ goto err_release; ++ } ++ } ++ ++ /* request boot firmware */ ++ ret = af9013_wr_reg(state, 0xe205, 1); ++ if (ret) ++ goto err_release; ++ ++ for (i = 0; i < 15; i++) { ++ msleep(100); ++ ++ /* check firmware status */ ++ ret = af9013_rd_reg(state, 0x98be, &val); ++ if (ret) ++ goto err_release; ++ ++ dev_dbg(&state->i2c->dev, "%s: firmware status=%02x\n", ++ __func__, val); ++ ++ if (val == 0x0c || val == 0x04) /* success or fail */ ++ break; ++ } ++ ++ if (val == 0x04) { ++ dev_err(&state->i2c->dev, "%s: firmware did not run\n", ++ KBUILD_MODNAME); ++ ret = -ENODEV; ++ } else if (val != 0x0c) { ++ dev_err(&state->i2c->dev, "%s: firmware boot timeout\n", ++ KBUILD_MODNAME); ++ ret = -ENODEV; ++ } ++ ++err_release: ++ release_firmware(fw); ++err: ++exit: ++ if (!ret) ++ dev_info(&state->i2c->dev, "%s: found a '%s' in warm state\n", ++ KBUILD_MODNAME, af9013_ops.info.name); ++ return ret; ++} ++ ++struct dvb_frontend *af9013_attach(const struct af9013_config *config, ++ struct i2c_adapter *i2c) ++{ ++ int ret; ++ struct af9013_state *state = NULL; ++ u8 buf[4], i; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL); ++ if (state == NULL) ++ goto err; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->config, config, sizeof(struct af9013_config)); ++ ++ /* download firmware */ ++ if (state->config.ts_mode != AF9013_TS_USB) { ++ ret = af9013_download_firmware(state); ++ if (ret) ++ goto err; ++ } ++ ++ /* firmware version */ ++ ret = af9013_rd_regs(state, 0x5103, buf, 4); ++ if (ret) ++ goto err; ++ ++ dev_info(&state->i2c->dev, "%s: firmware version %d.%d.%d.%d\n", ++ KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]); ++ ++ /* set GPIOs */ ++ for (i = 0; i < sizeof(state->config.gpio); i++) { ++ ret = af9013_set_gpio(state, i, state->config.gpio[i]); ++ if (ret) ++ goto err; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->fe.ops, &af9013_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->fe.demodulator_priv = state; ++ ++ INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work); ++ ++ return &state->fe; ++err: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(af9013_attach); ++ ++static struct dvb_frontend_ops af9013_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Afatech AF9013", ++ .frequency_min = 174000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 250000, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = af9013_release, ++ ++ .init = af9013_init, ++ .sleep = af9013_sleep, ++ ++ .get_tune_settings = af9013_get_tune_settings, ++ .set_frontend = af9013_set_frontend, ++ .get_frontend = af9013_get_frontend, ++ ++ .read_status = af9013_read_status, ++ .read_snr = af9013_read_snr, ++ .read_signal_strength = af9013_read_signal_strength, ++ .read_ber = af9013_read_ber, ++ .read_ucblocks = af9013_read_ucblocks, ++ ++ .i2c_gate_ctrl = af9013_i2c_gate_ctrl, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(AF9013_FIRMWARE); +diff --git a/drivers/media/dvb-frontends/af9013.h b/drivers/media/dvb-frontends/af9013.h +new file mode 100644 +index 0000000..dc837d9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9013.h +@@ -0,0 +1,118 @@ ++/* ++ * Afatech AF9013 demodulator driver ++ * ++ * Copyright (C) 2007 Antti Palosaari ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * Thanks to Afatech who kindly provided information. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef AF9013_H ++#define AF9013_H ++ ++#include ++ ++/* AF9013/5 GPIOs (mostly guessed) ++ demod#1-gpio#0 - set demod#2 i2c-addr for dual devices ++ demod#1-gpio#1 - xtal setting (?) ++ demod#1-gpio#3 - tuner#1 ++ demod#2-gpio#0 - tuner#2 ++ demod#2-gpio#1 - xtal setting (?) ++*/ ++ ++struct af9013_config { ++ /* ++ * I2C address ++ */ ++ u8 i2c_addr; ++ ++ /* ++ * clock ++ * 20480000, 25000000, 28000000, 28800000 ++ */ ++ u32 clock; ++ ++ /* ++ * tuner ++ */ ++#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */ ++#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */ ++#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */ ++#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */ ++#define AF9013_TUNER_MT2060 130 /* Microtune */ ++#define AF9013_TUNER_MC44S803 133 /* Freescale */ ++#define AF9013_TUNER_QT1010 134 /* Quantek */ ++#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */ ++#define AF9013_TUNER_MT2060_2 147 /* Microtune */ ++#define AF9013_TUNER_TDA18271 156 /* NXP */ ++#define AF9013_TUNER_QT1010A 162 /* Quantek */ ++#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */ ++#define AF9013_TUNER_TDA18218 179 /* NXP */ ++ u8 tuner; ++ ++ /* ++ * IF frequency ++ */ ++ u32 if_frequency; ++ ++ /* ++ * TS settings ++ */ ++#define AF9013_TS_USB 0 ++#define AF9013_TS_PARALLEL 1 ++#define AF9013_TS_SERIAL 2 ++ u8 ts_mode:2; ++ ++ /* ++ * input spectrum inversion ++ */ ++ bool spec_inv; ++ ++ /* ++ * firmware API version ++ */ ++ u8 api_version[4]; ++ ++ /* ++ * GPIOs ++ */ ++#define AF9013_GPIO_ON (1 << 0) ++#define AF9013_GPIO_EN (1 << 1) ++#define AF9013_GPIO_O (1 << 2) ++#define AF9013_GPIO_I (1 << 3) ++#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN) ++#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) ++#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN) ++#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) ++ u8 gpio[4]; ++}; ++ ++#if defined(CONFIG_DVB_AF9013) || \ ++ (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *af9013_attach( ++const struct af9013_config *config, struct i2c_adapter *i2c) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_AF9013 */ ++ ++#endif /* AF9013_H */ +diff --git a/drivers/media/dvb-frontends/af9013_priv.h b/drivers/media/dvb-frontends/af9013_priv.h +new file mode 100644 +index 0000000..8b9392c +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9013_priv.h +@@ -0,0 +1,909 @@ ++/* ++ * Afatech AF9013 demodulator driver ++ * ++ * Copyright (C) 2007 Antti Palosaari ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * Thanks to Afatech who kindly provided information. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef AF9013_PRIV_H ++#define AF9013_PRIV_H ++ ++#include "dvb_frontend.h" ++#include "af9013.h" ++#include ++ ++#define AF9013_FIRMWARE "dvb-fe-af9013.fw" ++ ++struct af9013_reg_bit { ++ u16 addr; ++ u8 pos:4; ++ u8 len:4; ++ u8 val; ++}; ++ ++struct af9013_snr { ++ u32 val; ++ u8 snr; ++}; ++ ++struct af9013_coeff { ++ u32 clock; ++ u32 bandwidth_hz; ++ u8 val[24]; ++}; ++ ++/* pre-calculated coeff lookup table */ ++static const struct af9013_coeff coeff_lut[] = { ++ /* 28.800 MHz */ ++ { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14, ++ 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a, ++ 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } }, ++ { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71, ++ 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38, ++ 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } }, ++ { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf, ++ 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7, ++ 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } }, ++ /* 20.480 MHz */ ++ { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24, ++ 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92, ++ 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } }, ++ { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40, ++ 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00, ++ 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } }, ++ { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b, ++ 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d, ++ 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } }, ++ /* 28.000 MHz */ ++ { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39, ++ 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f, ++ 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } }, ++ { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92, ++ 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49, ++ 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } }, ++ { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb, ++ 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63, ++ 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } }, ++ /* 25.000 MHz */ ++ { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9, ++ 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e, ++ 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } }, ++ { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e, ++ 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7, ++ 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } }, ++ { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63, ++ 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f, ++ 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } }, ++}; ++ ++/* QPSK SNR lookup table */ ++static const struct af9013_snr qpsk_snr_lut[] = { ++ { 0x000000, 0 }, ++ { 0x0b4771, 0 }, ++ { 0x0c1aed, 1 }, ++ { 0x0d0d27, 2 }, ++ { 0x0e4d19, 3 }, ++ { 0x0e5da8, 4 }, ++ { 0x107097, 5 }, ++ { 0x116975, 6 }, ++ { 0x1252d9, 7 }, ++ { 0x131fa4, 8 }, ++ { 0x13d5e1, 9 }, ++ { 0x148e53, 10 }, ++ { 0x15358b, 11 }, ++ { 0x15dd29, 12 }, ++ { 0x168112, 13 }, ++ { 0x170b61, 14 }, ++ { 0xffffff, 15 }, ++}; ++ ++/* QAM16 SNR lookup table */ ++static const struct af9013_snr qam16_snr_lut[] = { ++ { 0x000000, 0 }, ++ { 0x05eb62, 5 }, ++ { 0x05fecf, 6 }, ++ { 0x060b80, 7 }, ++ { 0x062501, 8 }, ++ { 0x064865, 9 }, ++ { 0x069604, 10 }, ++ { 0x06f356, 11 }, ++ { 0x07706a, 12 }, ++ { 0x0804d3, 13 }, ++ { 0x089d1a, 14 }, ++ { 0x093e3d, 15 }, ++ { 0x09e35d, 16 }, ++ { 0x0a7c3c, 17 }, ++ { 0x0afaf8, 18 }, ++ { 0x0b719d, 19 }, ++ { 0xffffff, 20 }, ++}; ++ ++/* QAM64 SNR lookup table */ ++static const struct af9013_snr qam64_snr_lut[] = { ++ { 0x000000, 0 }, ++ { 0x03109b, 12 }, ++ { 0x0310d4, 13 }, ++ { 0x031920, 14 }, ++ { 0x0322d0, 15 }, ++ { 0x0339fc, 16 }, ++ { 0x0364a1, 17 }, ++ { 0x038bcc, 18 }, ++ { 0x03c7d3, 19 }, ++ { 0x0408cc, 20 }, ++ { 0x043bed, 21 }, ++ { 0x048061, 22 }, ++ { 0x04be95, 23 }, ++ { 0x04fa7d, 24 }, ++ { 0x052405, 25 }, ++ { 0x05570d, 26 }, ++ { 0xffffff, 27 }, ++}; ++ ++static const struct af9013_reg_bit ofsm_init[] = { ++ { 0xd73a, 0, 8, 0xa1 }, ++ { 0xd73b, 0, 8, 0x1f }, ++ { 0xd73c, 4, 4, 0x0a }, ++ { 0xd732, 3, 1, 0x00 }, ++ { 0xd731, 4, 2, 0x03 }, ++ { 0xd73d, 7, 1, 0x01 }, ++ { 0xd740, 0, 1, 0x00 }, ++ { 0xd740, 1, 1, 0x00 }, ++ { 0xd740, 2, 1, 0x00 }, ++ { 0xd740, 3, 1, 0x01 }, ++ { 0xd3c1, 4, 1, 0x01 }, ++ { 0x9124, 0, 8, 0x58 }, ++ { 0x9125, 0, 2, 0x02 }, ++ { 0xd3a2, 0, 8, 0x00 }, ++ { 0xd3a3, 0, 8, 0x04 }, ++ { 0xd305, 0, 8, 0x32 }, ++ { 0xd306, 0, 8, 0x10 }, ++ { 0xd304, 0, 8, 0x04 }, ++ { 0x9112, 0, 1, 0x01 }, ++ { 0x911d, 0, 1, 0x01 }, ++ { 0x911a, 0, 1, 0x01 }, ++ { 0x911b, 0, 1, 0x01 }, ++ { 0x9bce, 0, 4, 0x02 }, ++ { 0x9116, 0, 1, 0x01 }, ++ { 0x9122, 0, 8, 0xd0 }, ++ { 0xd2e0, 0, 8, 0xd0 }, ++ { 0xd2e9, 0, 4, 0x0d }, ++ { 0xd38c, 0, 8, 0xfc }, ++ { 0xd38d, 0, 8, 0x00 }, ++ { 0xd38e, 0, 8, 0x7e }, ++ { 0xd38f, 0, 8, 0x00 }, ++ { 0xd390, 0, 8, 0x2f }, ++ { 0xd145, 4, 1, 0x01 }, ++ { 0xd1a9, 4, 1, 0x01 }, ++ { 0xd158, 5, 3, 0x01 }, ++ { 0xd159, 0, 6, 0x06 }, ++ { 0xd167, 0, 8, 0x00 }, ++ { 0xd168, 0, 4, 0x07 }, ++ { 0xd1c3, 5, 3, 0x00 }, ++ { 0xd1c4, 0, 6, 0x00 }, ++ { 0xd1c5, 0, 7, 0x10 }, ++ { 0xd1c6, 0, 3, 0x02 }, ++ { 0xd080, 2, 5, 0x03 }, ++ { 0xd081, 4, 4, 0x09 }, ++ { 0xd098, 4, 4, 0x0f }, ++ { 0xd098, 0, 4, 0x03 }, ++ { 0xdbc0, 4, 1, 0x01 }, ++ { 0xdbc7, 0, 8, 0x08 }, ++ { 0xdbc8, 4, 4, 0x00 }, ++ { 0xdbc9, 0, 5, 0x01 }, ++ { 0xd280, 0, 8, 0xe0 }, ++ { 0xd281, 0, 8, 0xff }, ++ { 0xd282, 0, 8, 0xff }, ++ { 0xd283, 0, 8, 0xc3 }, ++ { 0xd284, 0, 8, 0xff }, ++ { 0xd285, 0, 4, 0x01 }, ++ { 0xd0f0, 0, 7, 0x1a }, ++ { 0xd0f1, 4, 1, 0x01 }, ++ { 0xd0f2, 0, 8, 0x0c }, ++ { 0xd101, 5, 3, 0x06 }, ++ { 0xd103, 0, 4, 0x08 }, ++ { 0xd0f8, 0, 7, 0x20 }, ++ { 0xd111, 5, 1, 0x00 }, ++ { 0xd111, 6, 1, 0x00 }, ++ { 0x910b, 0, 8, 0x0a }, ++ { 0x9115, 0, 8, 0x02 }, ++ { 0x910c, 0, 8, 0x02 }, ++ { 0x910d, 0, 8, 0x08 }, ++ { 0x910e, 0, 8, 0x0a }, ++ { 0x9bf6, 0, 8, 0x06 }, ++ { 0x9bf8, 0, 8, 0x02 }, ++ { 0x9bf7, 0, 8, 0x05 }, ++ { 0x9bf9, 0, 8, 0x0f }, ++ { 0x9bfc, 0, 8, 0x13 }, ++ { 0x9bd3, 0, 8, 0xff }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++}; ++ ++/* Panasonic ENV77H11D5 tuner init ++ AF9013_TUNER_ENV77H11D5 = 129 */ ++static const struct af9013_reg_bit tuner_init_env77h11d5[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x03 }, ++ { 0x9bbe, 0, 8, 0x01 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x00 }, ++ { 0x9be3, 0, 8, 0x00 }, ++ { 0xd015, 0, 8, 0x50 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0xdf }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x44 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0xeb }, ++ { 0xd00d, 0, 2, 0x02 }, ++ { 0xd00a, 0, 8, 0xf4 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bba, 0, 8, 0xf9 }, ++ { 0x9bc3, 0, 8, 0xdf }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0xeb }, ++ { 0x9bc6, 0, 8, 0x02 }, ++ { 0x9bc9, 0, 8, 0x52 }, ++ { 0xd011, 0, 8, 0x3c }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0xf7 }, ++ { 0xd014, 0, 2, 0x02 }, ++ { 0xd040, 0, 8, 0x0b }, ++ { 0xd041, 0, 2, 0x02 }, ++ { 0xd042, 0, 8, 0x4d }, ++ { 0xd043, 0, 2, 0x00 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++}; ++ ++/* Microtune MT2060 tuner init ++ AF9013_TUNER_MT2060 = 130 */ ++static const struct af9013_reg_bit tuner_init_mt2060[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x07 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x00 }, ++ { 0x9be3, 0, 8, 0x00 }, ++ { 0x9bbe, 0, 1, 0x00 }, ++ { 0x9bcc, 0, 1, 0x00 }, ++ { 0x9bb9, 0, 8, 0x75 }, ++ { 0x9bcd, 0, 8, 0x24 }, ++ { 0x9bff, 0, 8, 0x30 }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0x0f }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x32 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0x36 }, ++ { 0xd00d, 0, 2, 0x03 }, ++ { 0xd00a, 0, 8, 0x35 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x07 }, ++ { 0x9bc8, 0, 8, 0x90 }, ++ { 0x9bc3, 0, 8, 0x0f }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x36 }, ++ { 0x9bc6, 0, 8, 0x03 }, ++ { 0x9bba, 0, 8, 0xc9 }, ++ { 0x9bc9, 0, 8, 0x79 }, ++ { 0xd011, 0, 8, 0x10 }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0x45 }, ++ { 0xd014, 0, 2, 0x03 }, ++ { 0xd040, 0, 8, 0x98 }, ++ { 0xd041, 0, 2, 0x00 }, ++ { 0xd042, 0, 8, 0xcf }, ++ { 0xd043, 0, 2, 0x03 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++ { 0x9bd0, 0, 8, 0xcc }, ++ { 0x9be4, 0, 8, 0xa0 }, ++ { 0x9bbd, 0, 8, 0x8e }, ++ { 0x9be2, 0, 8, 0x4d }, ++ { 0x9bee, 0, 1, 0x01 }, ++}; ++ ++/* Microtune MT2060 tuner init ++ AF9013_TUNER_MT2060_2 = 147 */ ++static const struct af9013_reg_bit tuner_init_mt2060_2[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x06 }, ++ { 0x9bbe, 0, 8, 0x01 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0x0f }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x32 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0x36 }, ++ { 0xd00d, 0, 2, 0x03 }, ++ { 0xd00a, 0, 8, 0x35 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x07 }, ++ { 0x9bc8, 0, 8, 0x90 }, ++ { 0x9bc3, 0, 8, 0x0f }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x36 }, ++ { 0x9bc6, 0, 8, 0x03 }, ++ { 0x9bba, 0, 8, 0xc9 }, ++ { 0x9bc9, 0, 8, 0x79 }, ++ { 0xd011, 0, 8, 0x10 }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0x45 }, ++ { 0xd014, 0, 2, 0x03 }, ++ { 0xd040, 0, 8, 0x98 }, ++ { 0xd041, 0, 2, 0x00 }, ++ { 0xd042, 0, 8, 0xcf }, ++ { 0xd043, 0, 2, 0x03 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 8, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x96 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0xd045, 7, 1, 0x00 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++}; ++ ++/* MaxLinear MXL5003 tuner init ++ AF9013_TUNER_MXL5003D = 3 */ ++static const struct af9013_reg_bit tuner_init_mxl5003d[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x09 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x00 }, ++ { 0x9be3, 0, 8, 0x00 }, ++ { 0x9bfc, 0, 8, 0x0f }, ++ { 0x9bf6, 0, 8, 0x01 }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0xd015, 0, 8, 0x33 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x40 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0x0f }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x6c }, ++ { 0xd007, 0, 2, 0x00 }, ++ { 0xd00c, 0, 8, 0x3d }, ++ { 0xd00d, 0, 2, 0x00 }, ++ { 0xd00a, 0, 8, 0x45 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x07 }, ++ { 0x9bc8, 0, 8, 0x52 }, ++ { 0x9bc3, 0, 8, 0x0f }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x3d }, ++ { 0x9bc6, 0, 8, 0x00 }, ++ { 0x9bba, 0, 8, 0xa2 }, ++ { 0x9bc9, 0, 8, 0xa0 }, ++ { 0xd011, 0, 8, 0x56 }, ++ { 0xd012, 0, 2, 0x00 }, ++ { 0xd013, 0, 8, 0x50 }, ++ { 0xd014, 0, 2, 0x00 }, ++ { 0xd040, 0, 8, 0x56 }, ++ { 0xd041, 0, 2, 0x00 }, ++ { 0xd042, 0, 8, 0x50 }, ++ { 0xd043, 0, 2, 0x00 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 8, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++}; ++ ++/* MaxLinear MXL5005S & MXL5007T tuner init ++ AF9013_TUNER_MXL5005D = 13 ++ AF9013_TUNER_MXL5005R = 30 ++ AF9013_TUNER_MXL5007T = 177 */ ++static const struct af9013_reg_bit tuner_init_mxl5005[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x07 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x01 }, ++ { 0x9be3, 0, 8, 0x01 }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++ { 0x9bb9, 0, 8, 0x00 }, ++ { 0x9bcd, 0, 8, 0x28 }, ++ { 0x9bff, 0, 8, 0x24 }, ++ { 0xd015, 0, 8, 0x40 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x40 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0x0f }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x73 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0xfa }, ++ { 0xd00d, 0, 2, 0x01 }, ++ { 0xd00a, 0, 8, 0xff }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x23 }, ++ { 0x9bc8, 0, 8, 0x55 }, ++ { 0x9bc3, 0, 8, 0x01 }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0xfa }, ++ { 0x9bc6, 0, 8, 0x01 }, ++ { 0x9bba, 0, 8, 0xff }, ++ { 0x9bc9, 0, 8, 0xff }, ++ { 0x9bd3, 0, 8, 0x95 }, ++ { 0xd011, 0, 8, 0x70 }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0xfb }, ++ { 0xd014, 0, 2, 0x01 }, ++ { 0xd040, 0, 8, 0x70 }, ++ { 0xd041, 0, 2, 0x01 }, ++ { 0xd042, 0, 8, 0xfb }, ++ { 0xd043, 0, 2, 0x01 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++ { 0x9bd0, 0, 8, 0x93 }, ++ { 0x9be4, 0, 8, 0xfe }, ++ { 0x9bbd, 0, 8, 0x63 }, ++ { 0x9be2, 0, 8, 0xfe }, ++ { 0x9bee, 0, 1, 0x01 }, ++}; ++ ++/* Quantek QT1010 tuner init ++ AF9013_TUNER_QT1010 = 134 ++ AF9013_TUNER_QT1010A = 162 */ ++static const struct af9013_reg_bit tuner_init_qt1010[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x09 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x01 }, ++ { 0x9be3, 0, 8, 0x01 }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++ { 0x9bb9, 0, 8, 0x00 }, ++ { 0x9bcd, 0, 8, 0x28 }, ++ { 0x9bff, 0, 8, 0x20 }, ++ { 0xd008, 0, 8, 0x0f }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x99 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0x0f }, ++ { 0xd00d, 0, 2, 0x02 }, ++ { 0xd00a, 0, 8, 0x50 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x00 }, ++ { 0x9bc8, 0, 8, 0x00 }, ++ { 0x9bc3, 0, 8, 0x0f }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x0f }, ++ { 0x9bc6, 0, 8, 0x02 }, ++ { 0x9bba, 0, 8, 0xc5 }, ++ { 0x9bc9, 0, 8, 0xff }, ++ { 0xd011, 0, 8, 0x58 }, ++ { 0xd012, 0, 2, 0x02 }, ++ { 0xd013, 0, 8, 0x89 }, ++ { 0xd014, 0, 2, 0x01 }, ++ { 0xd040, 0, 8, 0x58 }, ++ { 0xd041, 0, 2, 0x02 }, ++ { 0xd042, 0, 8, 0x89 }, ++ { 0xd043, 0, 2, 0x01 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++ { 0x9bd0, 0, 8, 0xcd }, ++ { 0x9be4, 0, 8, 0xbb }, ++ { 0x9bbd, 0, 8, 0x93 }, ++ { 0x9be2, 0, 8, 0x80 }, ++ { 0x9bee, 0, 1, 0x01 }, ++}; ++ ++/* Freescale MC44S803 tuner init ++ AF9013_TUNER_MC44S803 = 133 */ ++static const struct af9013_reg_bit tuner_init_mc44s803[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x06 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x00 }, ++ { 0x9be3, 0, 8, 0x00 }, ++ { 0x9bf6, 0, 8, 0x01 }, ++ { 0x9bf8, 0, 8, 0x02 }, ++ { 0x9bf9, 0, 8, 0x02 }, ++ { 0x9bfc, 0, 8, 0x1f }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++ { 0x9bb9, 0, 8, 0x00 }, ++ { 0x9bcd, 0, 8, 0x24 }, ++ { 0x9bff, 0, 8, 0x24 }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0x01 }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x7b }, ++ { 0xd007, 0, 2, 0x00 }, ++ { 0xd00c, 0, 8, 0x7c }, ++ { 0xd00d, 0, 2, 0x02 }, ++ { 0xd00a, 0, 8, 0xfe }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bc7, 0, 8, 0x08 }, ++ { 0x9bc8, 0, 8, 0x9a }, ++ { 0x9bc3, 0, 8, 0x01 }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x7c }, ++ { 0x9bc6, 0, 8, 0x02 }, ++ { 0x9bba, 0, 8, 0xfc }, ++ { 0x9bc9, 0, 8, 0xaa }, ++ { 0xd011, 0, 8, 0x6b }, ++ { 0xd012, 0, 2, 0x00 }, ++ { 0xd013, 0, 8, 0x88 }, ++ { 0xd014, 0, 2, 0x02 }, ++ { 0xd040, 0, 8, 0x6b }, ++ { 0xd041, 0, 2, 0x00 }, ++ { 0xd042, 0, 8, 0x7c }, ++ { 0xd043, 0, 2, 0x02 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++ { 0x9bd0, 0, 8, 0x9e }, ++ { 0x9be4, 0, 8, 0xff }, ++ { 0x9bbd, 0, 8, 0x9e }, ++ { 0x9be2, 0, 8, 0x25 }, ++ { 0x9bee, 0, 1, 0x01 }, ++ { 0xd73b, 3, 1, 0x00 }, ++}; ++ ++/* unknown, probably for tin can tuner, tuner init ++ AF9013_TUNER_UNKNOWN = 140 */ ++static const struct af9013_reg_bit tuner_init_unknown[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x02 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x01 }, ++ { 0x9be3, 0, 8, 0x01 }, ++ { 0xd1a0, 1, 1, 0x00 }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++ { 0x9bb9, 0, 8, 0x00 }, ++ { 0x9bcd, 0, 8, 0x18 }, ++ { 0x9bff, 0, 8, 0x2c }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0xdf }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x44 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0x00 }, ++ { 0xd00d, 0, 2, 0x02 }, ++ { 0xd00a, 0, 8, 0xf6 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bba, 0, 8, 0xf9 }, ++ { 0x9bc8, 0, 8, 0xaa }, ++ { 0x9bc3, 0, 8, 0xdf }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x00 }, ++ { 0x9bc6, 0, 8, 0x02 }, ++ { 0x9bc9, 0, 8, 0xf0 }, ++ { 0xd011, 0, 8, 0x3c }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0xf7 }, ++ { 0xd014, 0, 2, 0x02 }, ++ { 0xd040, 0, 8, 0x0b }, ++ { 0xd041, 0, 2, 0x02 }, ++ { 0xd042, 0, 8, 0x4d }, ++ { 0xd043, 0, 2, 0x00 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++}; ++ ++/* NXP TDA18271 & TDA18218 tuner init ++ AF9013_TUNER_TDA18271 = 156 ++ AF9013_TUNER_TDA18218 = 179 */ ++static const struct af9013_reg_bit tuner_init_tda18271[] = { ++ { 0x9bd5, 0, 8, 0x01 }, ++ { 0x9bd6, 0, 8, 0x04 }, ++ { 0xd1a0, 1, 1, 0x01 }, ++ { 0xd000, 0, 1, 0x01 }, ++ { 0xd000, 1, 1, 0x00 }, ++ { 0xd001, 1, 1, 0x01 }, ++ { 0xd001, 0, 1, 0x00 }, ++ { 0xd001, 5, 1, 0x00 }, ++ { 0xd002, 0, 5, 0x19 }, ++ { 0xd003, 0, 5, 0x1a }, ++ { 0xd004, 0, 5, 0x19 }, ++ { 0xd005, 0, 5, 0x1a }, ++ { 0xd00e, 0, 5, 0x10 }, ++ { 0xd00f, 0, 3, 0x04 }, ++ { 0xd00f, 3, 3, 0x05 }, ++ { 0xd010, 0, 3, 0x04 }, ++ { 0xd010, 3, 3, 0x05 }, ++ { 0xd016, 4, 4, 0x03 }, ++ { 0xd01f, 0, 6, 0x0a }, ++ { 0xd020, 0, 6, 0x0a }, ++ { 0x9bda, 0, 8, 0x01 }, ++ { 0x9be3, 0, 8, 0x01 }, ++ { 0xd1a0, 1, 1, 0x00 }, ++ { 0x9bbe, 0, 1, 0x01 }, ++ { 0x9bcc, 0, 1, 0x01 }, ++ { 0x9bb9, 0, 8, 0x00 }, ++ { 0x9bcd, 0, 8, 0x18 }, ++ { 0x9bff, 0, 8, 0x2c }, ++ { 0xd015, 0, 8, 0x46 }, ++ { 0xd016, 0, 1, 0x00 }, ++ { 0xd044, 0, 8, 0x46 }, ++ { 0xd045, 0, 1, 0x00 }, ++ { 0xd008, 0, 8, 0xdf }, ++ { 0xd009, 0, 2, 0x02 }, ++ { 0xd006, 0, 8, 0x44 }, ++ { 0xd007, 0, 2, 0x01 }, ++ { 0xd00c, 0, 8, 0x00 }, ++ { 0xd00d, 0, 2, 0x02 }, ++ { 0xd00a, 0, 8, 0xf6 }, ++ { 0xd00b, 0, 2, 0x01 }, ++ { 0x9bba, 0, 8, 0xf9 }, ++ { 0x9bc8, 0, 8, 0xaa }, ++ { 0x9bc3, 0, 8, 0xdf }, ++ { 0x9bc4, 0, 8, 0x02 }, ++ { 0x9bc5, 0, 8, 0x00 }, ++ { 0x9bc6, 0, 8, 0x02 }, ++ { 0x9bc9, 0, 8, 0xf0 }, ++ { 0xd011, 0, 8, 0x3c }, ++ { 0xd012, 0, 2, 0x01 }, ++ { 0xd013, 0, 8, 0xf7 }, ++ { 0xd014, 0, 2, 0x02 }, ++ { 0xd040, 0, 8, 0x0b }, ++ { 0xd041, 0, 2, 0x02 }, ++ { 0xd042, 0, 8, 0x4d }, ++ { 0xd043, 0, 2, 0x00 }, ++ { 0xd045, 1, 1, 0x00 }, ++ { 0x9bcf, 0, 1, 0x01 }, ++ { 0xd045, 2, 1, 0x01 }, ++ { 0xd04f, 0, 8, 0x9a }, ++ { 0xd050, 0, 1, 0x01 }, ++ { 0xd051, 0, 8, 0x5a }, ++ { 0xd052, 0, 1, 0x01 }, ++ { 0xd053, 0, 8, 0x50 }, ++ { 0xd054, 0, 8, 0x46 }, ++ { 0x9bd7, 0, 8, 0x0a }, ++ { 0x9bd8, 0, 8, 0x14 }, ++ { 0x9bd9, 0, 8, 0x08 }, ++ { 0x9bd0, 0, 8, 0xa8 }, ++ { 0x9be4, 0, 8, 0x7f }, ++ { 0x9bbd, 0, 8, 0xa8 }, ++ { 0x9be2, 0, 8, 0x20 }, ++ { 0x9bee, 0, 1, 0x01 }, ++}; ++ ++#endif /* AF9013_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c +new file mode 100644 +index 0000000..c9cad98 +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9033.c +@@ -0,0 +1,1016 @@ ++/* ++ * Afatech AF9033 demodulator driver ++ * ++ * Copyright (C) 2009 Antti Palosaari ++ * Copyright (C) 2012 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "af9033_priv.h" ++ ++struct af9033_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct af9033_config cfg; ++ ++ u32 bandwidth_hz; ++ bool ts_mode_parallel; ++ bool ts_mode_serial; ++ ++ u32 ber; ++ u32 ucb; ++ unsigned long last_stat_check; ++}; ++ ++/* write multiple registers */ ++static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, ++ int len) ++{ ++ int ret; ++ u8 buf[3 + len]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = state->cfg.i2c_addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = (reg >> 16) & 0xff; ++ buf[1] = (reg >> 8) & 0xff; ++ buf[2] = (reg >> 0) & 0xff; ++ memcpy(&buf[3], val, len); ++ ++ ret = i2c_transfer(state->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%06x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++/* read multiple registers */ ++static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) ++{ ++ int ret; ++ u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff, ++ (reg >> 0) & 0xff }; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = state->cfg.i2c_addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf ++ }, { ++ .addr = state->cfg.i2c_addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = val ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%06x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++ ++/* write single register */ ++static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val) ++{ ++ return af9033_wr_regs(state, reg, &val, 1); ++} ++ ++/* read single register */ ++static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val) ++{ ++ return af9033_rd_regs(state, reg, val, 1); ++} ++ ++/* write single register with mask */ ++static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, ++ u8 mask) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = af9033_rd_regs(state, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return af9033_wr_regs(state, reg, &val, 1); ++} ++ ++/* read single register with mask */ ++static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, ++ u8 mask) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ ret = af9033_rd_regs(state, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ tmp &= mask; ++ ++ /* find position of the first bit */ ++ for (i = 0; i < 8; i++) { ++ if ((mask >> i) & 0x01) ++ break; ++ } ++ *val = tmp >> i; ++ ++ return 0; ++} ++ ++static u32 af9033_div(struct af9033_state *state, u32 a, u32 b, u32 x) ++{ ++ u32 r = 0, c = 0, i; ++ ++ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d\n", __func__, a, b, x); ++ ++ if (a > b) { ++ c = a / b; ++ a = a - c * b; ++ } ++ ++ for (i = 0; i < x; i++) { ++ if (a >= b) { ++ r += 1; ++ a -= b; ++ } ++ a <<= 1; ++ r <<= 1; ++ } ++ r = (c << (u32)x) + r; ++ ++ dev_dbg(&state->i2c->dev, "%s: a=%d b=%d x=%d r=%d r=%x\n", ++ __func__, a, b, x, r, r); ++ ++ return r; ++} ++ ++static void af9033_release(struct dvb_frontend *fe) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ ++ kfree(state); ++} ++ ++static int af9033_init(struct dvb_frontend *fe) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret, i, len; ++ const struct reg_val *init; ++ u8 buf[4]; ++ u32 adc_cw, clock_cw; ++ struct reg_val_mask tab[] = { ++ { 0x80fb24, 0x00, 0x08 }, ++ { 0x80004c, 0x00, 0xff }, ++ { 0x00f641, state->cfg.tuner, 0xff }, ++ { 0x80f5ca, 0x01, 0x01 }, ++ { 0x80f715, 0x01, 0x01 }, ++ { 0x00f41f, 0x04, 0x04 }, ++ { 0x00f41a, 0x01, 0x01 }, ++ { 0x80f731, 0x00, 0x01 }, ++ { 0x00d91e, 0x00, 0x01 }, ++ { 0x00d919, 0x00, 0x01 }, ++ { 0x80f732, 0x00, 0x01 }, ++ { 0x00d91f, 0x00, 0x01 }, ++ { 0x00d91a, 0x00, 0x01 }, ++ { 0x80f730, 0x00, 0x01 }, ++ { 0x80f778, 0x00, 0xff }, ++ { 0x80f73c, 0x01, 0x01 }, ++ { 0x80f776, 0x00, 0x01 }, ++ { 0x00d8fd, 0x01, 0xff }, ++ { 0x00d830, 0x01, 0xff }, ++ { 0x00d831, 0x00, 0xff }, ++ { 0x00d832, 0x00, 0xff }, ++ { 0x80f985, state->ts_mode_serial, 0x01 }, ++ { 0x80f986, state->ts_mode_parallel, 0x01 }, ++ { 0x00d827, 0x00, 0xff }, ++ { 0x00d829, 0x00, 0xff }, ++ }; ++ ++ /* program clock control */ ++ clock_cw = af9033_div(state, state->cfg.clock, 1000000ul, 19ul); ++ buf[0] = (clock_cw >> 0) & 0xff; ++ buf[1] = (clock_cw >> 8) & 0xff; ++ buf[2] = (clock_cw >> 16) & 0xff; ++ buf[3] = (clock_cw >> 24) & 0xff; ++ ++ dev_dbg(&state->i2c->dev, "%s: clock=%d clock_cw=%08x\n", ++ __func__, state->cfg.clock, clock_cw); ++ ++ ret = af9033_wr_regs(state, 0x800025, buf, 4); ++ if (ret < 0) ++ goto err; ++ ++ /* program ADC control */ ++ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { ++ if (clock_adc_lut[i].clock == state->cfg.clock) ++ break; ++ } ++ ++ adc_cw = af9033_div(state, clock_adc_lut[i].adc, 1000000ul, 19ul); ++ buf[0] = (adc_cw >> 0) & 0xff; ++ buf[1] = (adc_cw >> 8) & 0xff; ++ buf[2] = (adc_cw >> 16) & 0xff; ++ ++ dev_dbg(&state->i2c->dev, "%s: adc=%d adc_cw=%06x\n", ++ __func__, clock_adc_lut[i].adc, adc_cw); ++ ++ ret = af9033_wr_regs(state, 0x80f1cd, buf, 3); ++ if (ret < 0) ++ goto err; ++ ++ /* program register table */ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret < 0) ++ goto err; ++ } ++ ++ /* settings for TS interface */ ++ if (state->cfg.ts_mode == AF9033_TS_MODE_USB) { ++ ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01); ++ if (ret < 0) ++ goto err; ++ } else { ++ ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ } ++ ++ /* load OFSM settings */ ++ dev_dbg(&state->i2c->dev, "%s: load ofsm settings\n", __func__); ++ len = ARRAY_SIZE(ofsm_init); ++ init = ofsm_init; ++ for (i = 0; i < len; i++) { ++ ret = af9033_wr_reg(state, init[i].reg, init[i].val); ++ if (ret < 0) ++ goto err; ++ } ++ ++ /* load tuner specific settings */ ++ dev_dbg(&state->i2c->dev, "%s: load tuner specific settings\n", ++ __func__); ++ switch (state->cfg.tuner) { ++ case AF9033_TUNER_TUA9001: ++ len = ARRAY_SIZE(tuner_init_tua9001); ++ init = tuner_init_tua9001; ++ break; ++ case AF9033_TUNER_FC0011: ++ len = ARRAY_SIZE(tuner_init_fc0011); ++ init = tuner_init_fc0011; ++ break; ++ case AF9033_TUNER_MXL5007T: ++ len = ARRAY_SIZE(tuner_init_mxl5007t); ++ init = tuner_init_mxl5007t; ++ break; ++ case AF9033_TUNER_TDA18218: ++ len = ARRAY_SIZE(tuner_init_tda18218); ++ init = tuner_init_tda18218; ++ break; ++ case AF9033_TUNER_FC2580: ++ len = ARRAY_SIZE(tuner_init_fc2580); ++ init = tuner_init_fc2580; ++ break; ++ case AF9033_TUNER_FC0012: ++ len = ARRAY_SIZE(tuner_init_fc0012); ++ init = tuner_init_fc0012; ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: unsupported tuner ID=%d\n", ++ __func__, state->cfg.tuner); ++ ret = -ENODEV; ++ goto err; ++ } ++ ++ for (i = 0; i < len; i++) { ++ ret = af9033_wr_reg(state, init[i].reg, init[i].val); ++ if (ret < 0) ++ goto err; ++ } ++ ++ if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { ++ ret = af9033_wr_reg_mask(state, 0x00d91c, 0x01, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x00d916, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ } ++ ++ state->bandwidth_hz = 0; /* force to program all parameters */ ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_sleep(struct dvb_frontend *fe) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret, i; ++ u8 tmp; ++ ++ ret = af9033_wr_reg(state, 0x80004c, 1); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg(state, 0x800000, 0); ++ if (ret < 0) ++ goto err; ++ ++ for (i = 100, tmp = 1; i && tmp; i--) { ++ ret = af9033_rd_reg(state, 0x80004c, &tmp); ++ if (ret < 0) ++ goto err; ++ ++ usleep_range(200, 10000); ++ } ++ ++ dev_dbg(&state->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) { ++ ret = -ETIMEDOUT; ++ goto err; ++ } ++ ++ ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08); ++ if (ret < 0) ++ goto err; ++ ++ /* prevent current leak (?) */ ++ if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { ++ /* enable parallel TS */ ++ ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01); ++ if (ret < 0) ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 800; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ ++ return 0; ++} ++ ++static int af9033_set_frontend(struct dvb_frontend *fe) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i, spec_inv, sampling_freq; ++ u8 tmp, buf[3], bandwidth_reg_val; ++ u32 if_frequency, freq_cw, adc_freq; ++ ++ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", ++ __func__, c->frequency, c->bandwidth_hz); ++ ++ /* check bandwidth */ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ bandwidth_reg_val = 0x00; ++ break; ++ case 7000000: ++ bandwidth_reg_val = 0x01; ++ break; ++ case 8000000: ++ bandwidth_reg_val = 0x02; ++ break; ++ default: ++ dev_dbg(&state->i2c->dev, "%s: invalid bandwidth_hz\n", ++ __func__); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* program CFOE coefficients */ ++ if (c->bandwidth_hz != state->bandwidth_hz) { ++ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { ++ if (coeff_lut[i].clock == state->cfg.clock && ++ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { ++ break; ++ } ++ } ++ ret = af9033_wr_regs(state, 0x800001, ++ coeff_lut[i].val, sizeof(coeff_lut[i].val)); ++ } ++ ++ /* program frequency control */ ++ if (c->bandwidth_hz != state->bandwidth_hz) { ++ spec_inv = state->cfg.spec_inv ? -1 : 1; ++ ++ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { ++ if (clock_adc_lut[i].clock == state->cfg.clock) ++ break; ++ } ++ adc_freq = clock_adc_lut[i].adc; ++ ++ /* get used IF frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) ++ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); ++ else ++ if_frequency = 0; ++ ++ sampling_freq = if_frequency; ++ ++ while (sampling_freq > (adc_freq / 2)) ++ sampling_freq -= adc_freq; ++ ++ if (sampling_freq >= 0) ++ spec_inv *= -1; ++ else ++ sampling_freq *= -1; ++ ++ freq_cw = af9033_div(state, sampling_freq, adc_freq, 23ul); ++ ++ if (spec_inv == -1) ++ freq_cw = 0x800000 - freq_cw; ++ ++ /* get adc multiplies */ ++ ret = af9033_rd_reg(state, 0x800045, &tmp); ++ if (ret < 0) ++ goto err; ++ ++ if (tmp == 1) ++ freq_cw /= 2; ++ ++ buf[0] = (freq_cw >> 0) & 0xff; ++ buf[1] = (freq_cw >> 8) & 0xff; ++ buf[2] = (freq_cw >> 16) & 0x7f; ++ ret = af9033_wr_regs(state, 0x800029, buf, 3); ++ if (ret < 0) ++ goto err; ++ ++ state->bandwidth_hz = c->bandwidth_hz; ++ } ++ ++ ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg(state, 0x800040, 0x00); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg(state, 0x800047, 0x00); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ if (c->frequency <= 230000000) ++ tmp = 0x00; /* VHF */ ++ else ++ tmp = 0x01; /* UHF */ ++ ++ ret = af9033_wr_reg(state, 0x80004b, tmp); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg(state, 0x800000, 0x00); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_get_frontend(struct dvb_frontend *fe) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[8]; ++ ++ dev_dbg(&state->i2c->dev, "%s:\n", __func__); ++ ++ /* read all needed registers */ ++ ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); ++ if (ret < 0) ++ goto err; ++ ++ switch ((buf[0] >> 0) & 3) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ ++ switch ((buf[1] >> 0) & 3) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((buf[2] >> 0) & 7) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ switch ((buf[3] >> 0) & 3) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ } ++ ++ switch ((buf[4] >> 0) & 3) { ++ case 0: ++ c->bandwidth_hz = 6000000; ++ break; ++ case 1: ++ c->bandwidth_hz = 7000000; ++ break; ++ case 2: ++ c->bandwidth_hz = 8000000; ++ break; ++ } ++ ++ switch ((buf[6] >> 0) & 7) { ++ case 0: ++ c->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_HP = FEC_7_8; ++ break; ++ case 5: ++ c->code_rate_HP = FEC_NONE; ++ break; ++ } ++ ++ switch ((buf[7] >> 0) & 7) { ++ case 0: ++ c->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_LP = FEC_7_8; ++ break; ++ case 5: ++ c->code_rate_LP = FEC_NONE; ++ break; ++ } ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ ++ *status = 0; ++ ++ /* radio channel status, 0=no result, 1=has signal, 2=no signal */ ++ ret = af9033_rd_reg(state, 0x800047, &tmp); ++ if (ret < 0) ++ goto err; ++ ++ /* has signal */ ++ if (tmp == 0x01) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (tmp != 0x02) { ++ /* TPS lock */ ++ ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ if (tmp) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI; ++ ++ /* full lock */ ++ ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ if (tmp) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | ++ FE_HAS_LOCK; ++ } ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret, i, len; ++ u8 buf[3], tmp; ++ u32 snr_val; ++ const struct val_snr *uninitialized_var(snr_lut); ++ ++ /* read value */ ++ ret = af9033_rd_regs(state, 0x80002c, buf, 3); ++ if (ret < 0) ++ goto err; ++ ++ snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; ++ ++ /* read current modulation */ ++ ret = af9033_rd_reg(state, 0x80f903, &tmp); ++ if (ret < 0) ++ goto err; ++ ++ switch ((tmp >> 0) & 3) { ++ case 0: ++ len = ARRAY_SIZE(qpsk_snr_lut); ++ snr_lut = qpsk_snr_lut; ++ break; ++ case 1: ++ len = ARRAY_SIZE(qam16_snr_lut); ++ snr_lut = qam16_snr_lut; ++ break; ++ case 2: ++ len = ARRAY_SIZE(qam64_snr_lut); ++ snr_lut = qam64_snr_lut; ++ break; ++ default: ++ goto err; ++ } ++ ++ for (i = 0; i < len; i++) { ++ tmp = snr_lut[i].snr; ++ ++ if (snr_val < snr_lut[i].val) ++ break; ++ } ++ ++ *snr = tmp * 10; /* dB/10 */ ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret; ++ u8 strength2; ++ ++ /* read signal strength of 0-100 scale */ ++ ret = af9033_rd_reg(state, 0x800048, &strength2); ++ if (ret < 0) ++ goto err; ++ ++ /* scale value to 0x0000-0xffff */ ++ *strength = strength2 * 0xffff / 100; ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_update_ch_stat(struct af9033_state *state) ++{ ++ int ret = 0; ++ u32 err_cnt, bit_cnt; ++ u16 abort_cnt; ++ u8 buf[7]; ++ ++ /* only update data every half second */ ++ if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) { ++ ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf)); ++ if (ret < 0) ++ goto err; ++ /* in 8 byte packets? */ ++ abort_cnt = (buf[1] << 8) + buf[0]; ++ /* in bits */ ++ err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2]; ++ /* in 8 byte packets? always(?) 0x2710 = 10000 */ ++ bit_cnt = (buf[6] << 8) + buf[5]; ++ ++ if (bit_cnt < abort_cnt) { ++ abort_cnt = 1000; ++ state->ber = 0xffffffff; ++ } else { ++ /* 8 byte packets, that have not been rejected already */ ++ bit_cnt -= (u32)abort_cnt; ++ if (bit_cnt == 0) { ++ state->ber = 0xffffffff; ++ } else { ++ err_cnt -= (u32)abort_cnt * 8 * 8; ++ bit_cnt *= 8 * 8; ++ state->ber = err_cnt * (0xffffffff / bit_cnt); ++ } ++ } ++ state->ucb += abort_cnt; ++ state->last_stat_check = jiffies; ++ } ++ ++ return 0; ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret; ++ ++ ret = af9033_update_ch_stat(state); ++ if (ret < 0) ++ return ret; ++ ++ *ber = state->ber; ++ ++ return 0; ++} ++ ++static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret; ++ ++ ret = af9033_update_ch_stat(state); ++ if (ret < 0) ++ return ret; ++ ++ *ucblocks = state->ucb; ++ ++ return 0; ++} ++ ++static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct af9033_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&state->i2c->dev, "%s: enable=%d\n", __func__, enable); ++ ++ ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01); ++ if (ret < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ ++ return ret; ++} ++ ++static struct dvb_frontend_ops af9033_ops; ++ ++struct dvb_frontend *af9033_attach(const struct af9033_config *config, ++ struct i2c_adapter *i2c) ++{ ++ int ret; ++ struct af9033_state *state; ++ u8 buf[8]; ++ ++ dev_dbg(&i2c->dev, "%s:\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL); ++ if (state == NULL) ++ goto err; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->cfg, config, sizeof(struct af9033_config)); ++ ++ if (state->cfg.clock != 12000000) { ++ dev_err(&state->i2c->dev, "%s: af9033: unsupported clock=%d, " \ ++ "only 12000000 Hz is supported currently\n", ++ KBUILD_MODNAME, state->cfg.clock); ++ goto err; ++ } ++ ++ /* firmware version */ ++ ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_rd_regs(state, 0x804191, &buf[4], 4); ++ if (ret < 0) ++ goto err; ++ ++ dev_info(&state->i2c->dev, "%s: firmware version: LINK=%d.%d.%d.%d " \ ++ "OFDM=%d.%d.%d.%d\n", KBUILD_MODNAME, buf[0], buf[1], ++ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); ++ ++ /* sleep */ ++ ret = af9033_wr_reg(state, 0x80004c, 1); ++ if (ret < 0) ++ goto err; ++ ++ ret = af9033_wr_reg(state, 0x800000, 0); ++ if (ret < 0) ++ goto err; ++ ++ /* configure internal TS mode */ ++ switch (state->cfg.ts_mode) { ++ case AF9033_TS_MODE_PARALLEL: ++ state->ts_mode_parallel = true; ++ break; ++ case AF9033_TS_MODE_SERIAL: ++ state->ts_mode_serial = true; ++ break; ++ case AF9033_TS_MODE_USB: ++ /* usb mode for AF9035 */ ++ default: ++ break; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); ++ state->fe.demodulator_priv = state; ++ ++ return &state->fe; ++ ++err: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(af9033_attach); ++ ++static struct dvb_frontend_ops af9033_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Afatech AF9033 (DVB-T)", ++ .frequency_min = 174000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 250000, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = af9033_release, ++ ++ .init = af9033_init, ++ .sleep = af9033_sleep, ++ ++ .get_tune_settings = af9033_get_tune_settings, ++ .set_frontend = af9033_set_frontend, ++ .get_frontend = af9033_get_frontend, ++ ++ .read_status = af9033_read_status, ++ .read_snr = af9033_read_snr, ++ .read_signal_strength = af9033_read_signal_strength, ++ .read_ber = af9033_read_ber, ++ .read_ucblocks = af9033_read_ucblocks, ++ ++ .i2c_gate_ctrl = af9033_i2c_gate_ctrl, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/af9033.h b/drivers/media/dvb-frontends/af9033.h +new file mode 100644 +index 0000000..82bd8c1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9033.h +@@ -0,0 +1,77 @@ ++/* ++ * Afatech AF9033 demodulator driver ++ * ++ * Copyright (C) 2009 Antti Palosaari ++ * Copyright (C) 2012 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef AF9033_H ++#define AF9033_H ++ ++struct af9033_config { ++ /* ++ * I2C address ++ */ ++ u8 i2c_addr; ++ ++ /* ++ * clock Hz ++ * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, ++ * 30000000, 36000000, 20480000, 16384000 ++ */ ++ u32 clock; ++ ++ /* ++ * tuner ++ */ ++#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ ++#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ ++#define AF9033_TUNER_FC0012 0x2e /* Fitipower FC0012 */ ++#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ ++#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ ++#define AF9033_TUNER_FC2580 0x32 /* FCI FC2580 */ ++ u8 tuner; ++ ++ /* ++ * TS settings ++ */ ++#define AF9033_TS_MODE_USB 0 ++#define AF9033_TS_MODE_PARALLEL 1 ++#define AF9033_TS_MODE_SERIAL 2 ++ u8 ts_mode:2; ++ ++ /* ++ * input spectrum inversion ++ */ ++ bool spec_inv; ++}; ++ ++ ++#if defined(CONFIG_DVB_AF9033) || \ ++ (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *af9033_attach(const struct af9033_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *af9033_attach( ++ const struct af9033_config *config, struct i2c_adapter *i2c) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* AF9033_H */ +diff --git a/drivers/media/dvb-frontends/af9033_priv.h b/drivers/media/dvb-frontends/af9033_priv.h +new file mode 100644 +index 0000000..e9bd782 +--- /dev/null ++++ b/drivers/media/dvb-frontends/af9033_priv.h +@@ -0,0 +1,551 @@ ++/* ++ * Afatech AF9033 demodulator driver ++ * ++ * Copyright (C) 2009 Antti Palosaari ++ * Copyright (C) 2012 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef AF9033_PRIV_H ++#define AF9033_PRIV_H ++ ++#include "dvb_frontend.h" ++#include "af9033.h" ++ ++struct reg_val { ++ u32 reg; ++ u8 val; ++}; ++ ++struct reg_val_mask { ++ u32 reg; ++ u8 val; ++ u8 mask; ++}; ++ ++struct coeff { ++ u32 clock; ++ u32 bandwidth_hz; ++ u8 val[36]; ++}; ++ ++struct clock_adc { ++ u32 clock; ++ u32 adc; ++}; ++ ++struct val_snr { ++ u32 val; ++ u8 snr; ++}; ++ ++/* Xtal clock vs. ADC clock lookup table */ ++static const struct clock_adc clock_adc_lut[] = { ++ { 16384000, 20480000 }, ++ { 20480000, 20480000 }, ++ { 36000000, 20250000 }, ++ { 30000000, 20156250 }, ++ { 26000000, 20583333 }, ++ { 28000000, 20416667 }, ++ { 32000000, 20500000 }, ++ { 34000000, 20187500 }, ++ { 24000000, 20500000 }, ++ { 22000000, 20625000 }, ++ { 12000000, 20250000 }, ++}; ++ ++/* pre-calculated coeff lookup table */ ++static const struct coeff coeff_lut[] = { ++ /* 12.000 MHz */ ++ { 12000000, 8000000, { ++ 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, ++ 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5, ++ 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, ++ 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 } ++ }, ++ { 12000000, 7000000, { ++ 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, ++ 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b, ++ 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, ++ 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 } ++ }, ++ { 12000000, 6000000, { ++ 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, ++ 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60, ++ 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, ++ 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 } ++ }, ++}; ++ ++/* QPSK SNR lookup table */ ++static const struct val_snr qpsk_snr_lut[] = { ++ { 0x0b4771, 0 }, ++ { 0x0c1aed, 1 }, ++ { 0x0d0d27, 2 }, ++ { 0x0e4d19, 3 }, ++ { 0x0e5da8, 4 }, ++ { 0x107097, 5 }, ++ { 0x116975, 6 }, ++ { 0x1252d9, 7 }, ++ { 0x131fa4, 8 }, ++ { 0x13d5e1, 9 }, ++ { 0x148e53, 10 }, ++ { 0x15358b, 11 }, ++ { 0x15dd29, 12 }, ++ { 0x168112, 13 }, ++ { 0x170b61, 14 }, ++ { 0x17a532, 15 }, ++ { 0x180f94, 16 }, ++ { 0x186ed2, 17 }, ++ { 0x18b271, 18 }, ++ { 0x18e118, 19 }, ++ { 0x18ff4b, 20 }, ++ { 0x190af1, 21 }, ++ { 0x191451, 22 }, ++ { 0xffffff, 23 }, ++}; ++ ++/* QAM16 SNR lookup table */ ++static const struct val_snr qam16_snr_lut[] = { ++ { 0x04f0d5, 0 }, ++ { 0x05387a, 1 }, ++ { 0x0573a4, 2 }, ++ { 0x05a99e, 3 }, ++ { 0x05cc80, 4 }, ++ { 0x05eb62, 5 }, ++ { 0x05fecf, 6 }, ++ { 0x060b80, 7 }, ++ { 0x062501, 8 }, ++ { 0x064865, 9 }, ++ { 0x069604, 10 }, ++ { 0x06f356, 11 }, ++ { 0x07706a, 12 }, ++ { 0x0804d3, 13 }, ++ { 0x089d1a, 14 }, ++ { 0x093e3d, 15 }, ++ { 0x09e35d, 16 }, ++ { 0x0a7c3c, 17 }, ++ { 0x0afaf8, 18 }, ++ { 0x0b719d, 19 }, ++ { 0x0bda6a, 20 }, ++ { 0x0c0c75, 21 }, ++ { 0x0c3f7d, 22 }, ++ { 0x0c5e62, 23 }, ++ { 0x0c6c31, 24 }, ++ { 0x0c7925, 25 }, ++ { 0xffffff, 26 }, ++}; ++ ++/* QAM64 SNR lookup table */ ++static const struct val_snr qam64_snr_lut[] = { ++ { 0x0256d0, 0 }, ++ { 0x027a65, 1 }, ++ { 0x029873, 2 }, ++ { 0x02b7fe, 3 }, ++ { 0x02cf1e, 4 }, ++ { 0x02e234, 5 }, ++ { 0x02f409, 6 }, ++ { 0x030046, 7 }, ++ { 0x030844, 8 }, ++ { 0x030a02, 9 }, ++ { 0x030cde, 10 }, ++ { 0x031031, 11 }, ++ { 0x03144c, 12 }, ++ { 0x0315dd, 13 }, ++ { 0x031920, 14 }, ++ { 0x0322d0, 15 }, ++ { 0x0339fc, 16 }, ++ { 0x0364a1, 17 }, ++ { 0x038bcc, 18 }, ++ { 0x03c7d3, 19 }, ++ { 0x0408cc, 20 }, ++ { 0x043bed, 21 }, ++ { 0x048061, 22 }, ++ { 0x04be95, 23 }, ++ { 0x04fa7d, 24 }, ++ { 0x052405, 25 }, ++ { 0x05570d, 26 }, ++ { 0x059feb, 27 }, ++ { 0x05bf38, 28 }, ++ { 0xffffff, 29 }, ++}; ++ ++static const struct reg_val ofsm_init[] = { ++ { 0x800051, 0x01 }, ++ { 0x800070, 0x0a }, ++ { 0x80007e, 0x04 }, ++ { 0x800081, 0x0a }, ++ { 0x80008a, 0x01 }, ++ { 0x80008e, 0x01 }, ++ { 0x800092, 0x06 }, ++ { 0x800099, 0x01 }, ++ { 0x80009f, 0xe1 }, ++ { 0x8000a0, 0xcf }, ++ { 0x8000a3, 0x01 }, ++ { 0x8000a5, 0x01 }, ++ { 0x8000a6, 0x01 }, ++ { 0x8000a9, 0x00 }, ++ { 0x8000aa, 0x01 }, ++ { 0x8000b0, 0x01 }, ++ { 0x8000c4, 0x05 }, ++ { 0x8000c8, 0x19 }, ++ { 0x80f000, 0x0f }, ++ { 0x80f016, 0x10 }, ++ { 0x80f017, 0x04 }, ++ { 0x80f018, 0x05 }, ++ { 0x80f019, 0x04 }, ++ { 0x80f01a, 0x05 }, ++ { 0x80f021, 0x03 }, ++ { 0x80f022, 0x0a }, ++ { 0x80f023, 0x0a }, ++ { 0x80f02b, 0x00 }, ++ { 0x80f02c, 0x01 }, ++ { 0x80f064, 0x03 }, ++ { 0x80f065, 0xf9 }, ++ { 0x80f066, 0x03 }, ++ { 0x80f067, 0x01 }, ++ { 0x80f06f, 0xe0 }, ++ { 0x80f070, 0x03 }, ++ { 0x80f072, 0x0f }, ++ { 0x80f073, 0x03 }, ++ { 0x80f078, 0x00 }, ++ { 0x80f087, 0x00 }, ++ { 0x80f09b, 0x3f }, ++ { 0x80f09c, 0x00 }, ++ { 0x80f09d, 0x20 }, ++ { 0x80f09e, 0x00 }, ++ { 0x80f09f, 0x0c }, ++ { 0x80f0a0, 0x00 }, ++ { 0x80f130, 0x04 }, ++ { 0x80f132, 0x04 }, ++ { 0x80f144, 0x1a }, ++ { 0x80f146, 0x00 }, ++ { 0x80f14a, 0x01 }, ++ { 0x80f14c, 0x00 }, ++ { 0x80f14d, 0x00 }, ++ { 0x80f14f, 0x04 }, ++ { 0x80f158, 0x7f }, ++ { 0x80f15a, 0x00 }, ++ { 0x80f15b, 0x08 }, ++ { 0x80f15d, 0x03 }, ++ { 0x80f15e, 0x05 }, ++ { 0x80f163, 0x05 }, ++ { 0x80f166, 0x01 }, ++ { 0x80f167, 0x40 }, ++ { 0x80f168, 0x0f }, ++ { 0x80f17a, 0x00 }, ++ { 0x80f17b, 0x00 }, ++ { 0x80f183, 0x01 }, ++ { 0x80f19d, 0x40 }, ++ { 0x80f1bc, 0x36 }, ++ { 0x80f1bd, 0x00 }, ++ { 0x80f1cb, 0xa0 }, ++ { 0x80f1cc, 0x01 }, ++ { 0x80f204, 0x10 }, ++ { 0x80f214, 0x00 }, ++ { 0x80f40e, 0x0a }, ++ { 0x80f40f, 0x40 }, ++ { 0x80f410, 0x08 }, ++ { 0x80f55f, 0x0a }, ++ { 0x80f561, 0x15 }, ++ { 0x80f562, 0x20 }, ++ { 0x80f5df, 0xfb }, ++ { 0x80f5e0, 0x00 }, ++ { 0x80f5e3, 0x09 }, ++ { 0x80f5e4, 0x01 }, ++ { 0x80f5e5, 0x01 }, ++ { 0x80f5f8, 0x01 }, ++ { 0x80f5fd, 0x01 }, ++ { 0x80f600, 0x05 }, ++ { 0x80f601, 0x08 }, ++ { 0x80f602, 0x0b }, ++ { 0x80f603, 0x0e }, ++ { 0x80f604, 0x11 }, ++ { 0x80f605, 0x14 }, ++ { 0x80f606, 0x17 }, ++ { 0x80f607, 0x1f }, ++ { 0x80f60e, 0x00 }, ++ { 0x80f60f, 0x04 }, ++ { 0x80f610, 0x32 }, ++ { 0x80f611, 0x10 }, ++ { 0x80f707, 0xfc }, ++ { 0x80f708, 0x00 }, ++ { 0x80f709, 0x37 }, ++ { 0x80f70a, 0x00 }, ++ { 0x80f78b, 0x01 }, ++ { 0x80f80f, 0x40 }, ++ { 0x80f810, 0x54 }, ++ { 0x80f811, 0x5a }, ++ { 0x80f905, 0x01 }, ++ { 0x80fb06, 0x03 }, ++ { 0x80fd8b, 0x00 }, ++}; ++ ++/* Infineon TUA 9001 tuner init ++ AF9033_TUNER_TUA9001 = 0x27 */ ++static const struct reg_val tuner_init_tua9001[] = { ++ { 0x800046, 0x27 }, ++ { 0x800057, 0x00 }, ++ { 0x800058, 0x01 }, ++ { 0x80005f, 0x00 }, ++ { 0x800060, 0x00 }, ++ { 0x80006d, 0x00 }, ++ { 0x800071, 0x05 }, ++ { 0x800072, 0x02 }, ++ { 0x800074, 0x01 }, ++ { 0x800075, 0x03 }, ++ { 0x800076, 0x02 }, ++ { 0x800077, 0x00 }, ++ { 0x800078, 0x01 }, ++ { 0x800079, 0x00 }, ++ { 0x80007a, 0x7e }, ++ { 0x80007b, 0x3e }, ++ { 0x800093, 0x00 }, ++ { 0x800094, 0x01 }, ++ { 0x800095, 0x02 }, ++ { 0x800096, 0x01 }, ++ { 0x800098, 0x0a }, ++ { 0x80009b, 0x05 }, ++ { 0x80009c, 0x80 }, ++ { 0x8000b3, 0x00 }, ++ { 0x8000c5, 0x01 }, ++ { 0x8000c6, 0x00 }, ++ { 0x8000c9, 0x5d }, ++ { 0x80f007, 0x00 }, ++ { 0x80f01f, 0x82 }, ++ { 0x80f020, 0x00 }, ++ { 0x80f029, 0x82 }, ++ { 0x80f02a, 0x00 }, ++ { 0x80f047, 0x00 }, ++ { 0x80f054, 0x00 }, ++ { 0x80f055, 0x00 }, ++ { 0x80f077, 0x01 }, ++ { 0x80f1e6, 0x00 }, ++}; ++ ++/* Fitipower fc0011 tuner init ++ AF9033_TUNER_FC0011 = 0x28 */ ++static const struct reg_val tuner_init_fc0011[] = { ++ { 0x800046, 0x28 }, ++ { 0x800057, 0x00 }, ++ { 0x800058, 0x01 }, ++ { 0x80005f, 0x00 }, ++ { 0x800060, 0x00 }, ++ { 0x800068, 0xa5 }, ++ { 0x80006e, 0x01 }, ++ { 0x800071, 0x0a }, ++ { 0x800072, 0x02 }, ++ { 0x800074, 0x01 }, ++ { 0x800079, 0x01 }, ++ { 0x800093, 0x00 }, ++ { 0x800094, 0x00 }, ++ { 0x800095, 0x00 }, ++ { 0x800096, 0x00 }, ++ { 0x80009b, 0x2d }, ++ { 0x80009c, 0x60 }, ++ { 0x80009d, 0x23 }, ++ { 0x8000a4, 0x50 }, ++ { 0x8000ad, 0x50 }, ++ { 0x8000b3, 0x01 }, ++ { 0x8000b7, 0x88 }, ++ { 0x8000b8, 0xa6 }, ++ { 0x8000c5, 0x01 }, ++ { 0x8000c6, 0x01 }, ++ { 0x8000c9, 0x69 }, ++ { 0x80f007, 0x00 }, ++ { 0x80f00a, 0x1b }, ++ { 0x80f00b, 0x1b }, ++ { 0x80f00c, 0x1b }, ++ { 0x80f00d, 0x1b }, ++ { 0x80f00e, 0xff }, ++ { 0x80f00f, 0x01 }, ++ { 0x80f010, 0x00 }, ++ { 0x80f011, 0x02 }, ++ { 0x80f012, 0xff }, ++ { 0x80f013, 0x01 }, ++ { 0x80f014, 0x00 }, ++ { 0x80f015, 0x02 }, ++ { 0x80f01b, 0xef }, ++ { 0x80f01c, 0x01 }, ++ { 0x80f01d, 0x0f }, ++ { 0x80f01e, 0x02 }, ++ { 0x80f01f, 0x6e }, ++ { 0x80f020, 0x00 }, ++ { 0x80f025, 0xde }, ++ { 0x80f026, 0x00 }, ++ { 0x80f027, 0x0a }, ++ { 0x80f028, 0x03 }, ++ { 0x80f029, 0x6e }, ++ { 0x80f02a, 0x00 }, ++ { 0x80f047, 0x00 }, ++ { 0x80f054, 0x00 }, ++ { 0x80f055, 0x00 }, ++ { 0x80f077, 0x01 }, ++ { 0x80f1e6, 0x00 }, ++}; ++ ++/* Fitipower FC0012 tuner init ++ AF9033_TUNER_FC0012 = 0x2e */ ++static const struct reg_val tuner_init_fc0012[] = { ++ { 0x800046, 0x2e }, ++ { 0x800057, 0x00 }, ++ { 0x800058, 0x01 }, ++ { 0x800059, 0x01 }, ++ { 0x80005f, 0x00 }, ++ { 0x800060, 0x00 }, ++ { 0x80006d, 0x00 }, ++ { 0x800071, 0x05 }, ++ { 0x800072, 0x02 }, ++ { 0x800074, 0x01 }, ++ { 0x800075, 0x03 }, ++ { 0x800076, 0x02 }, ++ { 0x800077, 0x01 }, ++ { 0x800078, 0x00 }, ++ { 0x800079, 0x00 }, ++ { 0x80007a, 0x90 }, ++ { 0x80007b, 0x90 }, ++ { 0x800093, 0x00 }, ++ { 0x800094, 0x01 }, ++ { 0x800095, 0x02 }, ++ { 0x800096, 0x01 }, ++ { 0x800098, 0x0a }, ++ { 0x80009b, 0x05 }, ++ { 0x80009c, 0x80 }, ++ { 0x8000b3, 0x00 }, ++ { 0x8000c5, 0x01 }, ++ { 0x8000c6, 0x00 }, ++ { 0x8000c9, 0x5d }, ++ { 0x80f007, 0x00 }, ++ { 0x80f01f, 0xa0 }, ++ { 0x80f020, 0x00 }, ++ { 0x80f029, 0x82 }, ++ { 0x80f02a, 0x00 }, ++ { 0x80f047, 0x00 }, ++ { 0x80f054, 0x00 }, ++ { 0x80f055, 0x00 }, ++ { 0x80f077, 0x01 }, ++ { 0x80f1e6, 0x00 }, ++}; ++ ++/* MaxLinear MxL5007T tuner init ++ AF9033_TUNER_MXL5007T = 0xa0 */ ++static const struct reg_val tuner_init_mxl5007t[] = { ++ { 0x800046, 0x1b }, ++ { 0x800057, 0x01 }, ++ { 0x800058, 0x01 }, ++ { 0x80005f, 0x00 }, ++ { 0x800060, 0x00 }, ++ { 0x800068, 0x96 }, ++ { 0x800071, 0x05 }, ++ { 0x800072, 0x02 }, ++ { 0x800074, 0x01 }, ++ { 0x800079, 0x01 }, ++ { 0x800093, 0x00 }, ++ { 0x800094, 0x00 }, ++ { 0x800095, 0x00 }, ++ { 0x800096, 0x00 }, ++ { 0x8000b3, 0x01 }, ++ { 0x8000c1, 0x01 }, ++ { 0x8000c2, 0x00 }, ++ { 0x80f007, 0x00 }, ++ { 0x80f00c, 0x19 }, ++ { 0x80f00d, 0x1a }, ++ { 0x80f012, 0xda }, ++ { 0x80f013, 0x00 }, ++ { 0x80f014, 0x00 }, ++ { 0x80f015, 0x02 }, ++ { 0x80f01f, 0x82 }, ++ { 0x80f020, 0x00 }, ++ { 0x80f029, 0x82 }, ++ { 0x80f02a, 0x00 }, ++ { 0x80f077, 0x02 }, ++ { 0x80f1e6, 0x00 }, ++}; ++ ++/* NXP TDA 18218HN tuner init ++ AF9033_TUNER_TDA18218 = 0xa1 */ ++static const struct reg_val tuner_init_tda18218[] = { ++ {0x800046, 0xa1}, ++ {0x800057, 0x01}, ++ {0x800058, 0x01}, ++ {0x80005f, 0x00}, ++ {0x800060, 0x00}, ++ {0x800071, 0x05}, ++ {0x800072, 0x02}, ++ {0x800074, 0x01}, ++ {0x800079, 0x01}, ++ {0x800093, 0x00}, ++ {0x800094, 0x00}, ++ {0x800095, 0x00}, ++ {0x800096, 0x00}, ++ {0x8000b3, 0x01}, ++ {0x8000c3, 0x01}, ++ {0x8000c4, 0x00}, ++ {0x80f007, 0x00}, ++ {0x80f00c, 0x19}, ++ {0x80f00d, 0x1a}, ++ {0x80f012, 0xda}, ++ {0x80f013, 0x00}, ++ {0x80f014, 0x00}, ++ {0x80f015, 0x02}, ++ {0x80f01f, 0x82}, ++ {0x80f020, 0x00}, ++ {0x80f029, 0x82}, ++ {0x80f02a, 0x00}, ++ {0x80f077, 0x02}, ++ {0x80f1e6, 0x00}, ++}; ++ ++/* FCI FC2580 tuner init */ ++static const struct reg_val tuner_init_fc2580[] = { ++ { 0x800046, 0x32 }, ++ { 0x800057, 0x01 }, ++ { 0x800058, 0x00 }, ++ { 0x80005f, 0x00 }, ++ { 0x800060, 0x00 }, ++ { 0x800071, 0x05 }, ++ { 0x800072, 0x02 }, ++ { 0x800074, 0x01 }, ++ { 0x800079, 0x01 }, ++ { 0x800093, 0x00 }, ++ { 0x800094, 0x00 }, ++ { 0x800095, 0x00 }, ++ { 0x800096, 0x05 }, ++ { 0x8000b3, 0x01 }, ++ { 0x8000c5, 0x01 }, ++ { 0x8000c6, 0x00 }, ++ { 0x8000d1, 0x01 }, ++ { 0x80f007, 0x00 }, ++ { 0x80f00c, 0x19 }, ++ { 0x80f00d, 0x1a }, ++ { 0x80f00e, 0x00 }, ++ { 0x80f00f, 0x02 }, ++ { 0x80f010, 0x00 }, ++ { 0x80f011, 0x02 }, ++ { 0x80f012, 0x00 }, ++ { 0x80f013, 0x02 }, ++ { 0x80f014, 0x00 }, ++ { 0x80f015, 0x02 }, ++ { 0x80f01f, 0x96 }, ++ { 0x80f020, 0x00 }, ++ { 0x80f029, 0x96 }, ++ { 0x80f02a, 0x00 }, ++ { 0x80f077, 0x01 }, ++ { 0x80f1e6, 0x01 }, ++}; ++ ++#endif /* AF9033_PRIV_H */ ++ +diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c +new file mode 100644 +index 0000000..4e11dc4 +--- /dev/null ++++ b/drivers/media/dvb-frontends/atbm8830.c +@@ -0,0 +1,508 @@ ++/* ++ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator ++ * ATBM8830, ATBM8831 ++ * ++ * Copyright (C) 2009 David T.L. Wong ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include "dvb_frontend.h" ++ ++#include "atbm8830.h" ++#include "atbm8830_priv.h" ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "atbm8830: " args); \ ++ } while (0) ++ ++static int debug; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) ++{ ++ int ret = 0; ++ u8 dev_addr; ++ u8 buf1[] = { reg >> 8, reg & 0xFF }; ++ u8 buf2[] = { data }; ++ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; ++ struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; ++ ++ dev_addr = priv->config->demod_address; ++ msg1.addr = dev_addr; ++ msg2.addr = dev_addr; ++ ++ if (debug >= 2) ++ dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); ++ ++ ret = i2c_transfer(priv->i2c, &msg1, 1); ++ if (ret != 1) ++ return -EIO; ++ ++ ret = i2c_transfer(priv->i2c, &msg2, 1); ++ return (ret != 1) ? -EIO : 0; ++} ++ ++static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) ++{ ++ int ret; ++ u8 dev_addr; ++ ++ u8 buf1[] = { reg >> 8, reg & 0xFF }; ++ u8 buf2[] = { 0 }; ++ struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; ++ struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; ++ ++ dev_addr = priv->config->demod_address; ++ msg1.addr = dev_addr; ++ msg2.addr = dev_addr; ++ ++ ret = i2c_transfer(priv->i2c, &msg1, 1); ++ if (ret != 1) { ++ dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); ++ return -EIO; ++ } ++ ++ ret = i2c_transfer(priv->i2c, &msg2, 1); ++ if (ret != 1) ++ return -EIO; ++ ++ *p_data = buf2[0]; ++ if (debug >= 2) ++ dprintk("%s: reg=0x%04X, data=0x%02X\n", ++ __func__, reg, buf2[0]); ++ ++ return 0; ++} ++ ++/* Lock register latch so that multi-register read is atomic */ ++static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) ++{ ++ return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); ++} ++ ++static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) ++{ ++ u32 val; ++ u64 t; ++ ++ /* 0x100000 * freq / 30.4MHz */ ++ t = (u64)0x100000 * freq; ++ do_div(t, 30400); ++ val = t; ++ ++ atbm8830_write_reg(priv, REG_OSC_CLK, val); ++ atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); ++ atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); ++ ++ return 0; ++} ++ ++static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) ++{ ++ ++ u32 fs = priv->config->osc_clk_freq; ++ u64 t; ++ u32 val; ++ u8 dat; ++ ++ if (freq != 0) { ++ /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ ++ t = (u64) 2 * 31416 * (freq - fs); ++ t <<= 22; ++ do_div(t, fs); ++ do_div(t, 1000); ++ val = t; ++ ++ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); ++ atbm8830_write_reg(priv, REG_IF_FREQ, val); ++ atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); ++ atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); ++ ++ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); ++ dat &= 0xFC; ++ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); ++ } else { ++ /* Zero IF */ ++ atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); ++ ++ atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); ++ dat &= 0xFC; ++ dat |= 0x02; ++ atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); ++ ++ if (priv->config->zif_swap_iq) ++ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); ++ else ++ atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); ++ } ++ ++ return 0; ++} ++ ++static int is_locked(struct atbm_state *priv, u8 *locked) ++{ ++ u8 status; ++ ++ atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); ++ ++ if (locked != NULL) ++ *locked = (status == 1); ++ return 0; ++} ++ ++static int set_agc_config(struct atbm_state *priv, ++ u8 min, u8 max, u8 hold_loop) ++{ ++ /* no effect if both min and max are zero */ ++ if (!min && !max) ++ return 0; ++ ++ atbm8830_write_reg(priv, REG_AGC_MIN, min); ++ atbm8830_write_reg(priv, REG_AGC_MAX, max); ++ atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); ++ ++ return 0; ++} ++ ++static int set_static_channel_mode(struct atbm_state *priv) ++{ ++ int i; ++ ++ for (i = 0; i < 5; i++) ++ atbm8830_write_reg(priv, 0x099B + i, 0x08); ++ ++ atbm8830_write_reg(priv, 0x095B, 0x7F); ++ atbm8830_write_reg(priv, 0x09CB, 0x01); ++ atbm8830_write_reg(priv, 0x09CC, 0x7F); ++ atbm8830_write_reg(priv, 0x09CD, 0x7F); ++ atbm8830_write_reg(priv, 0x0E01, 0x20); ++ ++ /* For single carrier */ ++ atbm8830_write_reg(priv, 0x0B03, 0x0A); ++ atbm8830_write_reg(priv, 0x0935, 0x10); ++ atbm8830_write_reg(priv, 0x0936, 0x08); ++ atbm8830_write_reg(priv, 0x093E, 0x08); ++ atbm8830_write_reg(priv, 0x096E, 0x06); ++ ++ /* frame_count_max0 */ ++ atbm8830_write_reg(priv, 0x0B09, 0x00); ++ /* frame_count_max1 */ ++ atbm8830_write_reg(priv, 0x0B0A, 0x08); ++ ++ return 0; ++} ++ ++static int set_ts_config(struct atbm_state *priv) ++{ ++ const struct atbm8830_config *cfg = priv->config; ++ ++ /*Set parallel/serial ts mode*/ ++ atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); ++ atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); ++ /*Set ts sampling edge*/ ++ atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, ++ cfg->ts_sampling_edge ? 1 : 0); ++ /*Set ts clock freerun*/ ++ atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, ++ cfg->ts_clk_gated ? 0 : 1); ++ ++ return 0; ++} ++ ++static int atbm8830_init(struct dvb_frontend *fe) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ const struct atbm8830_config *cfg = priv->config; ++ ++ /*Set oscillator frequency*/ ++ set_osc_freq(priv, cfg->osc_clk_freq); ++ ++ /*Set IF frequency*/ ++ set_if_freq(priv, cfg->if_freq); ++ ++ /*Set AGC Config*/ ++ set_agc_config(priv, cfg->agc_min, cfg->agc_max, ++ cfg->agc_hold_loop); ++ ++ /*Set static channel mode*/ ++ set_static_channel_mode(priv); ++ ++ set_ts_config(priv); ++ /*Turn off DSP reset*/ ++ atbm8830_write_reg(priv, 0x000A, 0); ++ ++ /*SW version test*/ ++ atbm8830_write_reg(priv, 0x020C, 11); ++ ++ /* Run */ ++ atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); ++ ++ return 0; ++} ++ ++ ++static void atbm8830_release(struct dvb_frontend *fe) ++{ ++ struct atbm_state *state = fe->demodulator_priv; ++ dprintk("%s\n", __func__); ++ ++ kfree(state); ++} ++ ++static int atbm8830_set_fe(struct dvb_frontend *fe) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ int i; ++ u8 locked = 0; ++ dprintk("%s\n", __func__); ++ ++ /* set frequency */ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* start auto lock */ ++ for (i = 0; i < 10; i++) { ++ mdelay(100); ++ dprintk("Try %d\n", i); ++ is_locked(priv, &locked); ++ if (locked != 0) { ++ dprintk("ATBM8830 locked!\n"); ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int atbm8830_get_fe(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ dprintk("%s\n", __func__); ++ ++ /* TODO: get real readings from device */ ++ /* inversion status */ ++ c->inversion = INVERSION_OFF; ++ ++ /* bandwidth */ ++ c->bandwidth_hz = 8000000; ++ ++ c->code_rate_HP = FEC_AUTO; ++ c->code_rate_LP = FEC_AUTO; ++ ++ c->modulation = QAM_AUTO; ++ ++ /* transmission mode */ ++ c->transmission_mode = TRANSMISSION_MODE_AUTO; ++ ++ /* guard interval */ ++ c->guard_interval = GUARD_INTERVAL_AUTO; ++ ++ /* hierarchy */ ++ c->hierarchy = HIERARCHY_NONE; ++ ++ return 0; ++} ++ ++static int atbm8830_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 0; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ u8 locked = 0; ++ u8 agc_locked = 0; ++ ++ dprintk("%s\n", __func__); ++ *fe_status = 0; ++ ++ is_locked(priv, &locked); ++ if (locked) { ++ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); ++ ++ atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); ++ dprintk("AGC Lock: %d\n", agc_locked); ++ ++ return 0; ++} ++ ++static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ u32 frame_err; ++ u8 t; ++ ++ dprintk("%s\n", __func__); ++ ++ atbm8830_reglatch_lock(priv, 1); ++ ++ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); ++ frame_err = t & 0x7F; ++ frame_err <<= 8; ++ atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); ++ frame_err |= t; ++ ++ atbm8830_reglatch_lock(priv, 0); ++ ++ *ber = frame_err * 100 / 32767; ++ ++ dprintk("%s: ber=0x%x\n", __func__, *ber); ++ return 0; ++} ++ ++static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ u32 pwm; ++ u8 t; ++ ++ dprintk("%s\n", __func__); ++ atbm8830_reglatch_lock(priv, 1); ++ ++ atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); ++ pwm = t & 0x03; ++ pwm <<= 8; ++ atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); ++ pwm |= t; ++ ++ atbm8830_reglatch_lock(priv, 0); ++ ++ dprintk("AGC PWM = 0x%02X\n", pwm); ++ pwm = 0x400 - pwm; ++ ++ *signal = pwm * 0x10000 / 0x400; ++ ++ return 0; ++} ++ ++static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ dprintk("%s\n", __func__); ++ *snr = 0; ++ return 0; ++} ++ ++static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ dprintk("%s\n", __func__); ++ *ucblocks = 0; ++ return 0; ++} ++ ++static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct atbm_state *priv = fe->demodulator_priv; ++ ++ return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); ++} ++ ++static struct dvb_frontend_ops atbm8830_ops = { ++ .delsys = { SYS_DTMB }, ++ .info = { ++ .name = "AltoBeam ATBM8830/8831 DMB-TH", ++ .frequency_min = 474000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 10000, ++ .caps = ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO ++ }, ++ ++ .release = atbm8830_release, ++ ++ .init = atbm8830_init, ++ .sleep = NULL, ++ .write = NULL, ++ .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, ++ ++ .set_frontend = atbm8830_set_fe, ++ .get_frontend = atbm8830_get_fe, ++ .get_tune_settings = atbm8830_get_tune_settings, ++ ++ .read_status = atbm8830_read_status, ++ .read_ber = atbm8830_read_ber, ++ .read_signal_strength = atbm8830_read_signal_strength, ++ .read_snr = atbm8830_read_snr, ++ .read_ucblocks = atbm8830_read_ucblocks, ++}; ++ ++struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct atbm_state *priv = NULL; ++ u8 data = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ if (config == NULL || i2c == NULL) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); ++ if (priv == NULL) ++ goto error_out; ++ ++ priv->config = config; ++ priv->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { ++ dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", ++ __func__, priv->config->demod_address); ++ goto error_out; ++ } ++ dprintk("atbm8830 chip id: 0x%02X\n", data); ++ ++ memcpy(&priv->frontend.ops, &atbm8830_ops, ++ sizeof(struct dvb_frontend_ops)); ++ priv->frontend.demodulator_priv = priv; ++ ++ atbm8830_init(&priv->frontend); ++ ++ atbm8830_i2c_gate_ctrl(&priv->frontend, 1); ++ ++ return &priv->frontend; ++ ++error_out: ++ dprintk("%s() error_out\n", __func__); ++ kfree(priv); ++ return NULL; ++ ++} ++EXPORT_SYMBOL(atbm8830_attach); ++ ++MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); ++MODULE_AUTHOR("David T. L. Wong "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/atbm8830.h b/drivers/media/dvb-frontends/atbm8830.h +new file mode 100644 +index 0000000..0242733 +--- /dev/null ++++ b/drivers/media/dvb-frontends/atbm8830.h +@@ -0,0 +1,76 @@ ++/* ++ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator ++ * ATBM8830, ATBM8831 ++ * ++ * Copyright (C) 2009 David T.L. Wong ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ATBM8830_H__ ++#define __ATBM8830_H__ ++ ++#include ++#include ++ ++#define ATBM8830_PROD_8830 0 ++#define ATBM8830_PROD_8831 1 ++ ++struct atbm8830_config { ++ ++ /* product type */ ++ u8 prod; ++ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* parallel or serial transport stream */ ++ u8 serial_ts; ++ ++ /* transport stream clock output only when receiving valid stream */ ++ u8 ts_clk_gated; ++ ++ /* Decoder sample TS data at rising edge of clock */ ++ u8 ts_sampling_edge; ++ ++ /* Oscillator clock frequency */ ++ u32 osc_clk_freq; /* in kHz */ ++ ++ /* IF frequency */ ++ u32 if_freq; /* in kHz */ ++ ++ /* Swap I/Q for zero IF */ ++ u8 zif_swap_iq; ++ ++ /* Tuner AGC settings */ ++ u8 agc_min; ++ u8 agc_max; ++ u8 agc_hold_loop; ++}; ++ ++#if defined(CONFIG_DVB_ATBM8830) || \ ++ (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline ++struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, ++ struct i2c_adapter *i2c) { ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_ATBM8830 */ ++ ++#endif /* __ATBM8830_H__ */ +diff --git a/drivers/media/dvb-frontends/atbm8830_priv.h b/drivers/media/dvb-frontends/atbm8830_priv.h +new file mode 100644 +index 0000000..d460058 +--- /dev/null ++++ b/drivers/media/dvb-frontends/atbm8830_priv.h +@@ -0,0 +1,75 @@ ++/* ++ * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator ++ * ATBM8830, ATBM8831 ++ * ++ * Copyright (C) 2009 David T.L. Wong ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ATBM8830_PRIV_H ++#define __ATBM8830_PRIV_H ++ ++struct atbm_state { ++ struct i2c_adapter *i2c; ++ /* configuration settings */ ++ const struct atbm8830_config *config; ++ struct dvb_frontend frontend; ++}; ++ ++#define REG_CHIP_ID 0x0000 ++#define REG_TUNER_BASEBAND 0x0001 ++#define REG_DEMOD_RUN 0x0004 ++#define REG_DSP_RESET 0x0005 ++#define REG_RAM_RESET 0x0006 ++#define REG_ADC_RESET 0x0007 ++#define REG_TSPORT_RESET 0x0008 ++#define REG_BLKERR_POL 0x000C ++#define REG_I2C_GATE 0x0103 ++#define REG_TS_SAMPLE_EDGE 0x0301 ++#define REG_TS_PKT_LEN_204 0x0302 ++#define REG_TS_PKT_LEN_AUTO 0x0303 ++#define REG_TS_SERIAL 0x0305 ++#define REG_TS_CLK_FREERUN 0x0306 ++#define REG_TS_VALID_MODE 0x0307 ++#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ ++ ++#define REG_TS_ERRBIT_USE 0x030C ++#define REG_LOCK_STATUS 0x030D ++#define REG_ADC_CONFIG 0x0602 ++#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ ++#define REG_DETECTED_PN_MODE 0x082D ++#define REG_READ_LATCH 0x084D ++#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ ++#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ ++#define REG_BYPASS_CCI 0x0A06 ++#define REG_ANALOG_LUMA_DETECTED 0x0A25 ++#define REG_ANALOG_AUDIO_DETECTED 0x0A26 ++#define REG_ANALOG_CHROMA_DETECTED 0x0A39 ++#define REG_FRAME_ERR_CNT 0x0B04 ++#define REG_USE_EXT_ADC 0x0C00 ++#define REG_SWAP_I_Q 0x0C01 ++#define REG_TPS_MANUAL 0x0D01 ++#define REG_TPS_CONFIG 0x0D02 ++#define REG_BYPASS_DEINTERLEAVER 0x0E00 ++#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ ++#define REG_AGC_MIN 0x1020 ++#define REG_AGC_MAX 0x1023 ++#define REG_AGC_LOCK 0x1027 ++#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ ++#define REG_AGC_HOLD_LOOP 0x1031 ++ ++#endif ++ +diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h +new file mode 100644 +index 0000000..565dcf3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/au8522.h +@@ -0,0 +1,98 @@ ++/* ++ Auvitek AU8522 QAM/8VSB demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef __AU8522_H__ ++#define __AU8522_H__ ++ ++#include ++ ++enum au8522_if_freq { ++ AU8522_IF_6MHZ = 0, ++ AU8522_IF_4MHZ, ++ AU8522_IF_3_25MHZ, ++}; ++ ++struct au8522_led_config { ++ u16 vsb8_strong; ++ u16 qam64_strong; ++ u16 qam256_strong; ++ ++ u16 gpio_output; ++ /* unset hi bits, set low bits */ ++ u16 gpio_output_enable; ++ u16 gpio_output_disable; ++ ++ u16 gpio_leds; ++ u8 *led_states; ++ unsigned int num_led_states; ++}; ++ ++struct au8522_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* Return lock status based on tuner lock, or demod lock */ ++#define AU8522_TUNERLOCKING 0 ++#define AU8522_DEMODLOCKING 1 ++ u8 status_mode; ++ ++ struct au8522_led_config *led_cfg; ++ ++ enum au8522_if_freq vsb_if; ++ enum au8522_if_freq qam_if; ++}; ++ ++#if defined(CONFIG_DVB_AU8522) || \ ++ (defined(CONFIG_DVB_AU8522_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline ++struct dvb_frontend *au8522_attach(const struct au8522_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_AU8522 */ ++ ++/* Other modes may need to be added later */ ++enum au8522_video_input { ++ AU8522_COMPOSITE_CH1 = 1, ++ AU8522_COMPOSITE_CH2, ++ AU8522_COMPOSITE_CH3, ++ AU8522_COMPOSITE_CH4, ++ AU8522_COMPOSITE_CH4_SIF, ++ AU8522_SVIDEO_CH13, ++ AU8522_SVIDEO_CH24, ++}; ++ ++enum au8522_audio_input { ++ AU8522_AUDIO_NONE, ++ AU8522_AUDIO_SIF, ++}; ++ ++#endif /* __AU8522_H__ */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ */ +diff --git a/drivers/media/dvb-frontends/au8522_common.c b/drivers/media/dvb-frontends/au8522_common.c +new file mode 100644 +index 0000000..3559ff2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/au8522_common.c +@@ -0,0 +1,277 @@ ++/* ++ Auvitek AU8522 QAM/8VSB demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ Copyright (C) 2008 Devin Heitmueller ++ Copyright (C) 2005-2008 Auvitek International, Ltd. ++ Copyright (C) 2012 Michael Krufky ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include "dvb_frontend.h" ++#include "au8522_priv.h" ++ ++static int debug; ++ ++#define dprintk(arg...)\ ++ do { if (debug)\ ++ printk(arg);\ ++ } while (0) ++ ++/* Despite the name "hybrid_tuner", the framework works just as well for ++ hybrid demodulators as well... */ ++static LIST_HEAD(hybrid_tuner_instance_list); ++static DEFINE_MUTEX(au8522_list_mutex); ++ ++/* 16 bit registers, 8 bit values */ ++int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; ++ ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = buf, .len = 3 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++EXPORT_SYMBOL(au8522_writereg); ++ ++u8 au8522_readreg(struct au8522_state *state, u16 reg) ++{ ++ int ret; ++ u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; ++ u8 b1[] = { 0 }; ++ ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, ++ .buf = b0, .len = 2 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk(KERN_ERR "%s: readreg error (ret == %i)\n", ++ __func__, ret); ++ return b1[0]; ++} ++EXPORT_SYMBOL(au8522_readreg); ++ ++int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (state->operational_mode == AU8522_ANALOG_MODE) { ++ /* We're being asked to manage the gate even though we're ++ not in digital mode. This can occur if we get switched ++ over to analog mode before the dvb_frontend kernel thread ++ has completely shutdown */ ++ return 0; ++ } ++ ++ if (enable) ++ return au8522_writereg(state, 0x106, 1); ++ else ++ return au8522_writereg(state, 0x106, 0); ++} ++EXPORT_SYMBOL(au8522_i2c_gate_ctrl); ++ ++int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (enable) ++ return au8522_writereg(state, 0x106, 1); ++ else ++ return au8522_writereg(state, 0x106, 0); ++} ++EXPORT_SYMBOL(au8522_analog_i2c_gate_ctrl); ++ ++/* Reset the demod hardware and reset all of the configuration registers ++ to a default state. */ ++int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, ++ u8 client_address) ++{ ++ int ret; ++ ++ mutex_lock(&au8522_list_mutex); ++ ret = hybrid_tuner_request_state(struct au8522_state, (*state), ++ hybrid_tuner_instance_list, ++ i2c, client_address, "au8522"); ++ mutex_unlock(&au8522_list_mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL(au8522_get_state); ++ ++void au8522_release_state(struct au8522_state *state) ++{ ++ mutex_lock(&au8522_list_mutex); ++ if (state != NULL) ++ hybrid_tuner_release_state(state); ++ mutex_unlock(&au8522_list_mutex); ++} ++EXPORT_SYMBOL(au8522_release_state); ++ ++static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) ++{ ++ struct au8522_led_config *led_config = state->config->led_cfg; ++ u8 val; ++ ++ /* bail out if we can't control an LED */ ++ if (!led_config || !led_config->gpio_output || ++ !led_config->gpio_output_enable || !led_config->gpio_output_disable) ++ return 0; ++ ++ val = au8522_readreg(state, 0x4000 | ++ (led_config->gpio_output & ~0xc000)); ++ if (onoff) { ++ /* enable GPIO output */ ++ val &= ~((led_config->gpio_output_enable >> 8) & 0xff); ++ val |= (led_config->gpio_output_enable & 0xff); ++ } else { ++ /* disable GPIO output */ ++ val &= ~((led_config->gpio_output_disable >> 8) & 0xff); ++ val |= (led_config->gpio_output_disable & 0xff); ++ } ++ return au8522_writereg(state, 0x8000 | ++ (led_config->gpio_output & ~0xc000), val); ++} ++ ++/* led = 0 | off ++ * led = 1 | signal ok ++ * led = 2 | signal strong ++ * led < 0 | only light led if leds are currently off ++ */ ++int au8522_led_ctrl(struct au8522_state *state, int led) ++{ ++ struct au8522_led_config *led_config = state->config->led_cfg; ++ int i, ret = 0; ++ ++ /* bail out if we can't control an LED */ ++ if (!led_config || !led_config->gpio_leds || ++ !led_config->num_led_states || !led_config->led_states) ++ return 0; ++ ++ if (led < 0) { ++ /* if LED is already lit, then leave it as-is */ ++ if (state->led_state) ++ return 0; ++ else ++ led *= -1; ++ } ++ ++ /* toggle LED if changing state */ ++ if (state->led_state != led) { ++ u8 val; ++ ++ dprintk("%s: %d\n", __func__, led); ++ ++ au8522_led_gpio_enable(state, 1); ++ ++ val = au8522_readreg(state, 0x4000 | ++ (led_config->gpio_leds & ~0xc000)); ++ ++ /* start with all leds off */ ++ for (i = 0; i < led_config->num_led_states; i++) ++ val &= ~led_config->led_states[i]; ++ ++ /* set selected LED state */ ++ if (led < led_config->num_led_states) ++ val |= led_config->led_states[led]; ++ else if (led_config->num_led_states) ++ val |= ++ led_config->led_states[led_config->num_led_states - 1]; ++ ++ ret = au8522_writereg(state, 0x8000 | ++ (led_config->gpio_leds & ~0xc000), val); ++ if (ret < 0) ++ return ret; ++ ++ state->led_state = led; ++ ++ if (led == 0) ++ au8522_led_gpio_enable(state, 0); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(au8522_led_ctrl); ++ ++int au8522_init(struct dvb_frontend *fe) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ dprintk("%s()\n", __func__); ++ ++ state->operational_mode = AU8522_DIGITAL_MODE; ++ ++ /* Clear out any state associated with the digital side of the ++ chip, so that when it gets powered back up it won't think ++ that it is already tuned */ ++ state->current_frequency = 0; ++ ++ au8522_writereg(state, 0xa4, 1 << 5); ++ ++ au8522_i2c_gate_ctrl(fe, 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL(au8522_init); ++ ++int au8522_sleep(struct dvb_frontend *fe) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ dprintk("%s()\n", __func__); ++ ++ /* Only power down if the digital side is currently using the chip */ ++ if (state->operational_mode == AU8522_ANALOG_MODE) { ++ /* We're not in one of the expected power modes, which means ++ that the DVB thread is probably telling us to go to sleep ++ even though the analog frontend has already started using ++ the chip. So ignore the request */ ++ return 0; ++ } ++ ++ /* turn off led */ ++ au8522_led_ctrl(state, 0); ++ ++ /* Power down the chip */ ++ au8522_writereg(state, 0xa4, 1 << 5); ++ ++ state->current_frequency = 0; ++ ++ return 0; ++} ++EXPORT_SYMBOL(au8522_sleep); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c +new file mode 100644 +index 0000000..5243ba6 +--- /dev/null ++++ b/drivers/media/dvb-frontends/au8522_decoder.c +@@ -0,0 +1,839 @@ ++/* ++ * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder ++ * ++ * Copyright (C) 2009 Devin Heitmueller ++ * Copyright (C) 2005-2008 Auvitek International, Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * As published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++/* Developer notes: ++ * ++ * VBI support is not yet working ++ * Enough is implemented here for CVBS and S-Video inputs, but the actual ++ * analog demodulator code isn't implemented (not needed for xc5000 since it ++ * has its own demodulator and outputs CVBS) ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "au8522.h" ++#include "au8522_priv.h" ++ ++MODULE_AUTHOR("Devin Heitmueller"); ++MODULE_LICENSE("GPL"); ++ ++static int au8522_analog_debug; ++ ++ ++module_param_named(analog_debug, au8522_analog_debug, int, 0644); ++ ++MODULE_PARM_DESC(analog_debug, ++ "Analog debugging messages [0=Off (default) 1=On]"); ++ ++struct au8522_register_config { ++ u16 reg_name; ++ u8 reg_val[8]; ++}; ++ ++ ++/* Video Decoder Filter Coefficients ++ The values are as follows from left to right ++ 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13" ++*/ ++static const struct au8522_register_config filter_coef[] = { ++ {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} }, ++ {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} }, ++ {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} }, ++ {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} }, ++ {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} }, ++ {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} }, ++ {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} }, ++ {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} }, ++ {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} }, ++ {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} }, ++ {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} }, ++ {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} }, ++ {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} }, ++ {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} }, ++ {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} }, ++ {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} }, ++ {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} }, ++ {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} }, ++ {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} }, ++ {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} }, ++ {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} }, ++ {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} }, ++ ++}; ++#define NUM_FILTER_COEF (sizeof(filter_coef)\ ++ / sizeof(struct au8522_register_config)) ++ ++ ++/* Registers 0x060b through 0x0652 are the LP Filter coefficients ++ The values are as follows from left to right ++ 0="SIF" 1="ATVRF/ATVRF13" ++ Note: the "ATVRF/ATVRF13" mode has never been tested ++*/ ++static const struct au8522_register_config lpfilter_coef[] = { ++ {0x060b, {0x21, 0x0b} }, ++ {0x060c, {0xad, 0xad} }, ++ {0x060d, {0x70, 0xf0} }, ++ {0x060e, {0xea, 0xe9} }, ++ {0x060f, {0xdd, 0xdd} }, ++ {0x0610, {0x08, 0x64} }, ++ {0x0611, {0x60, 0x60} }, ++ {0x0612, {0xf8, 0xb2} }, ++ {0x0613, {0x01, 0x02} }, ++ {0x0614, {0xe4, 0xb4} }, ++ {0x0615, {0x19, 0x02} }, ++ {0x0616, {0xae, 0x2e} }, ++ {0x0617, {0xee, 0xc5} }, ++ {0x0618, {0x56, 0x56} }, ++ {0x0619, {0x30, 0x58} }, ++ {0x061a, {0xf9, 0xf8} }, ++ {0x061b, {0x24, 0x64} }, ++ {0x061c, {0x07, 0x07} }, ++ {0x061d, {0x30, 0x30} }, ++ {0x061e, {0xa9, 0xed} }, ++ {0x061f, {0x09, 0x0b} }, ++ {0x0620, {0x42, 0xc2} }, ++ {0x0621, {0x1d, 0x2a} }, ++ {0x0622, {0xd6, 0x56} }, ++ {0x0623, {0x95, 0x8b} }, ++ {0x0624, {0x2b, 0x2b} }, ++ {0x0625, {0x30, 0x24} }, ++ {0x0626, {0x3e, 0x3e} }, ++ {0x0627, {0x62, 0xe2} }, ++ {0x0628, {0xe9, 0xf5} }, ++ {0x0629, {0x99, 0x19} }, ++ {0x062a, {0xd4, 0x11} }, ++ {0x062b, {0x03, 0x04} }, ++ {0x062c, {0xb5, 0x85} }, ++ {0x062d, {0x1e, 0x20} }, ++ {0x062e, {0x2a, 0xea} }, ++ {0x062f, {0xd7, 0xd2} }, ++ {0x0630, {0x15, 0x15} }, ++ {0x0631, {0xa3, 0xa9} }, ++ {0x0632, {0x1f, 0x1f} }, ++ {0x0633, {0xf9, 0xd1} }, ++ {0x0634, {0xc0, 0xc3} }, ++ {0x0635, {0x4d, 0x8d} }, ++ {0x0636, {0x21, 0x31} }, ++ {0x0637, {0x83, 0x83} }, ++ {0x0638, {0x08, 0x8c} }, ++ {0x0639, {0x19, 0x19} }, ++ {0x063a, {0x45, 0xa5} }, ++ {0x063b, {0xef, 0xec} }, ++ {0x063c, {0x8a, 0x8a} }, ++ {0x063d, {0xf4, 0xf6} }, ++ {0x063e, {0x8f, 0x8f} }, ++ {0x063f, {0x44, 0x0c} }, ++ {0x0640, {0xef, 0xf0} }, ++ {0x0641, {0x66, 0x66} }, ++ {0x0642, {0xcc, 0xd2} }, ++ {0x0643, {0x41, 0x41} }, ++ {0x0644, {0x63, 0x93} }, ++ {0x0645, {0x8e, 0x8e} }, ++ {0x0646, {0xa2, 0x42} }, ++ {0x0647, {0x7b, 0x7b} }, ++ {0x0648, {0x04, 0x04} }, ++ {0x0649, {0x00, 0x00} }, ++ {0x064a, {0x40, 0x40} }, ++ {0x064b, {0x8c, 0x98} }, ++ {0x064c, {0x00, 0x00} }, ++ {0x064d, {0x63, 0xc3} }, ++ {0x064e, {0x04, 0x04} }, ++ {0x064f, {0x20, 0x20} }, ++ {0x0650, {0x00, 0x00} }, ++ {0x0651, {0x40, 0x40} }, ++ {0x0652, {0x01, 0x01} }, ++}; ++#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\ ++ / sizeof(struct au8522_register_config)) ++ ++static inline struct au8522_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct au8522_state, sd); ++} ++ ++static void setup_vbi(struct au8522_state *state, int aud_input) ++{ ++ int i; ++ ++ /* These are set to zero regardless of what mode we're in */ ++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H, ++ 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H, ++ 0x00); ++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H, ++ 0x00); ++ ++ /* Setup the VBI registers */ ++ for (i = 0x30; i < 0x60; i++) ++ au8522_writereg(state, i, 0x40); ++ ++ /* For some reason, every register is 0x40 except register 0x44 ++ (confirmed via the HVR-950q USB capture) */ ++ au8522_writereg(state, 0x44, 0x60); ++ ++ /* Enable VBI (we always do this regardless of whether the user is ++ viewing closed caption info) */ ++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, ++ AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON); ++ ++} ++ ++static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) ++{ ++ int i; ++ int filter_coef_type; ++ ++ /* Provide reasonable defaults for picture tuning values */ ++ au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07); ++ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed); ++ state->brightness = 0xed - 128; ++ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79); ++ state->contrast = 0x79; ++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); ++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); ++ state->saturation = 0x80; ++ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); ++ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); ++ state->hue = 0x00; ++ ++ /* Other decoder registers */ ++ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); ++ ++ if (input_mode == 0x23) { ++ /* S-Video input mapping */ ++ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04); ++ } else { ++ /* All other modes (CVBS/ATVRF etc.) */ ++ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00); ++ } ++ ++ au8522_writereg(state, AU8522_TVDEC_PGA_REG012H, ++ AU8522_TVDEC_PGA_REG012H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H, ++ AU8522_TVDEC_COMB_MODE_REG015H_CVBS); ++ au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H, ++ AU8522_TVDED_DBG_MODE_REG060H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, ++ AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 | ++ AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 | ++ AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN); ++ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, ++ AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC); ++ au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H, ++ AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H, ++ AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H, ++ AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H, ++ AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H, ++ AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H, ++ AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H, ++ AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH, ++ AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH, ++ AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS); ++ if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || ++ input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { ++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, ++ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO); ++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, ++ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO); ++ } else { ++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, ++ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, ++ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS); ++ } ++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH, ++ AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH, ++ AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H, ++ AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS); ++ au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS); ++ au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H, ++ AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS); ++ au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS); ++ au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H, ++ AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H, ++ AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H, ++ AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH, ++ AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH, ++ AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS); ++ au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H, ++ AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS); ++ au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H, ++ AU8522_TOREGAAGC_REG0E5H_CVBS); ++ au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS); ++ ++ setup_vbi(state, 0); ++ ++ if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || ++ input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { ++ /* Despite what the table says, for the HVR-950q we still need ++ to be in CVBS mode for the S-Video input (reason unknown). */ ++ /* filter_coef_type = 3; */ ++ filter_coef_type = 5; ++ } else { ++ filter_coef_type = 5; ++ } ++ ++ /* Load the Video Decoder Filter Coefficients */ ++ for (i = 0; i < NUM_FILTER_COEF; i++) { ++ au8522_writereg(state, filter_coef[i].reg_name, ++ filter_coef[i].reg_val[filter_coef_type]); ++ } ++ ++ /* It's not clear what these registers are for, but they are always ++ set to the same value regardless of what mode we're in */ ++ au8522_writereg(state, AU8522_REG42EH, 0x87); ++ au8522_writereg(state, AU8522_REG42FH, 0xa2); ++ au8522_writereg(state, AU8522_REG430H, 0xbf); ++ au8522_writereg(state, AU8522_REG431H, 0xcb); ++ au8522_writereg(state, AU8522_REG432H, 0xa1); ++ au8522_writereg(state, AU8522_REG433H, 0x41); ++ au8522_writereg(state, AU8522_REG434H, 0x88); ++ au8522_writereg(state, AU8522_REG435H, 0xc2); ++ au8522_writereg(state, AU8522_REG436H, 0x3c); ++} ++ ++static void au8522_setup_cvbs_mode(struct au8522_state *state) ++{ ++ /* here we're going to try the pre-programmed route */ ++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, ++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); ++ ++ /* PGA in automatic mode */ ++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); ++ ++ /* Enable clamping control */ ++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); ++ ++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, ++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); ++ ++ setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); ++ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); ++} ++ ++static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state) ++{ ++ /* here we're going to try the pre-programmed route */ ++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, ++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); ++ ++ /* It's not clear why we have to have the PGA in automatic mode while ++ enabling clamp control, but it's what Windows does */ ++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); ++ ++ /* Enable clamping control */ ++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e); ++ ++ /* Disable automatic PGA (since the CVBS is coming from the tuner) */ ++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10); ++ ++ /* Set input mode to CVBS on channel 4 with SIF audio input enabled */ ++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, ++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); ++ ++ setup_decoder_defaults(state, ++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); ++ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); ++} ++ ++static void au8522_setup_svideo_mode(struct au8522_state *state) ++{ ++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, ++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO); ++ ++ /* Set input to Y on Channe1, C on Channel 3 */ ++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, ++ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); ++ ++ /* PGA in automatic mode */ ++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); ++ ++ /* Enable clamping control */ ++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); ++ ++ setup_decoder_defaults(state, ++ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); ++ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void disable_audio_input(struct au8522_state *state) ++{ ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); ++ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04); ++ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02); ++ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO); ++} ++ ++/* 0=disable, 1=SIF */ ++static void set_audio_input(struct au8522_state *state, int aud_input) ++{ ++ int i; ++ ++ /* Note that this function needs to be used in conjunction with setting ++ the input routing via register 0x81 */ ++ ++ if (aud_input == AU8522_AUDIO_NONE) { ++ disable_audio_input(state); ++ return; ++ } ++ ++ if (aud_input != AU8522_AUDIO_SIF) { ++ /* The caller asked for a mode we don't currently support */ ++ printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n", ++ aud_input); ++ return; ++ } ++ ++ /* Load the Audio Decoder Filter Coefficients */ ++ for (i = 0; i < NUM_LPFILTER_COEF; i++) { ++ au8522_writereg(state, lpfilter_coef[i].reg_name, ++ lpfilter_coef[i].reg_val[0]); ++ } ++ ++ /* Setup audio */ ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); ++ au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80); ++ au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84); ++ msleep(150); ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00); ++ msleep(1); ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d); ++ msleep(50); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff); ++ msleep(80); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); ++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); ++ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO); ++ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82); ++ msleep(70); ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09); ++ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03); ++ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct au8522_state *state = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ state->brightness = ctrl->value; ++ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, ++ ctrl->value - 128); ++ break; ++ case V4L2_CID_CONTRAST: ++ state->contrast = ctrl->value; ++ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, ++ ctrl->value); ++ break; ++ case V4L2_CID_SATURATION: ++ state->saturation = ctrl->value; ++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, ++ ctrl->value); ++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, ++ ctrl->value); ++ break; ++ case V4L2_CID_HUE: ++ state->hue = ctrl->value; ++ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, ++ ctrl->value >> 8); ++ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, ++ ctrl->value & 0xFF); ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ case V4L2_CID_AUDIO_BALANCE: ++ case V4L2_CID_AUDIO_MUTE: ++ /* Not yet implemented */ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct au8522_state *state = to_state(sd); ++ ++ /* Note that we are using values cached in the state structure instead ++ of reading the registers due to issues with i2c reads not working ++ properly/consistently yet on the HVR-950q */ ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ ctrl->value = state->brightness; ++ break; ++ case V4L2_CID_CONTRAST: ++ ctrl->value = state->contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ ctrl->value = state->saturation; ++ break; ++ case V4L2_CID_HUE: ++ ctrl->value = state->hue; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ case V4L2_CID_AUDIO_BALANCE: ++ case V4L2_CID_AUDIO_MUTE: ++ /* Not yet supported */ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int au8522_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct au8522_state *state = to_state(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = au8522_readreg(state, reg->reg & 0xffff); ++ return 0; ++} ++ ++static int au8522_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct au8522_state *state = to_state(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ au8522_writereg(state, reg->reg, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int au8522_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct au8522_state *state = to_state(sd); ++ ++ if (enable) { ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ 0x01); ++ msleep(1); ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); ++ } else { ++ /* This does not completely power down the device ++ (it only reduces it from around 140ma to 80ma) */ ++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, ++ 1 << 5); ++ } ++ return 0; ++} ++ ++static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) ++{ ++ switch (qc->id) { ++ case V4L2_CID_CONTRAST: ++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, ++ AU8522_TVDEC_CONTRAST_REG00BH_CVBS); ++ case V4L2_CID_BRIGHTNESS: ++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109); ++ case V4L2_CID_SATURATION: ++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); ++ case V4L2_CID_HUE: ++ return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0); ++ default: ++ break; ++ } ++ ++ qc->type = 0; ++ return -EINVAL; ++} ++ ++static int au8522_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ struct au8522_state *state = to_state(sd); ++ ++ state->operational_mode = AU8522_ANALOG_MODE; ++ ++ /* Clear out any state associated with the digital side of the ++ chip, so that when it gets powered back up it won't think ++ that it is already tuned */ ++ state->current_frequency = 0; ++ ++ au8522_writereg(state, 0xa4, 1 << 5); ++ ++ return 0; ++} ++ ++static int au8522_s_video_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct au8522_state *state = to_state(sd); ++ ++ au8522_reset(sd, 0); ++ ++ if (input == AU8522_COMPOSITE_CH1) { ++ au8522_setup_cvbs_mode(state); ++ } else if (input == AU8522_SVIDEO_CH13) { ++ au8522_setup_svideo_mode(state); ++ } else if (input == AU8522_COMPOSITE_CH4_SIF) { ++ au8522_setup_cvbs_tuner_mode(state); ++ } else { ++ printk(KERN_ERR "au8522 mode not currently supported\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int au8522_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct au8522_state *state = to_state(sd); ++ set_audio_input(state, input); ++ return 0; ++} ++ ++static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ int val = 0; ++ struct au8522_state *state = to_state(sd); ++ u8 lock_status; ++ ++ /* Interrogate the decoder to see if we are getting a real signal */ ++ lock_status = au8522_readreg(state, 0x00); ++ if (lock_status == 0xa2) ++ vt->signal = 0xffff; ++ else ++ vt->signal = 0x00; ++ ++ vt->capability |= ++ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | ++ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; ++ ++ val = V4L2_TUNER_SUB_MONO; ++ vt->rxsubchans = val; ++ vt->audmode = V4L2_TUNER_MODE_STEREO; ++ return 0; ++} ++ ++static int au8522_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct au8522_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); ++} ++ ++static int au8522_log_status(struct v4l2_subdev *sd) ++{ ++ /* FIXME: Add some status info here */ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops au8522_core_ops = { ++ .log_status = au8522_log_status, ++ .g_chip_ident = au8522_g_chip_ident, ++ .g_ctrl = au8522_g_ctrl, ++ .s_ctrl = au8522_s_ctrl, ++ .queryctrl = au8522_queryctrl, ++ .reset = au8522_reset, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = au8522_g_register, ++ .s_register = au8522_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = { ++ .g_tuner = au8522_g_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops au8522_audio_ops = { ++ .s_routing = au8522_s_audio_routing, ++}; ++ ++static const struct v4l2_subdev_video_ops au8522_video_ops = { ++ .s_routing = au8522_s_video_routing, ++ .s_stream = au8522_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops au8522_ops = { ++ .core = &au8522_core_ops, ++ .tuner = &au8522_tuner_ops, ++ .audio = &au8522_audio_ops, ++ .video = &au8522_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int au8522_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct au8522_state *state; ++ struct v4l2_subdev *sd; ++ int instance; ++ struct au8522_config *demod_config; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA)) { ++ return -EIO; ++ } ++ ++ /* allocate memory for the internal state */ ++ instance = au8522_get_state(&state, client->adapter, client->addr); ++ switch (instance) { ++ case 0: ++ printk(KERN_ERR "au8522_decoder allocation failed\n"); ++ return -EIO; ++ case 1: ++ /* new demod instance */ ++ printk(KERN_INFO "au8522_decoder creating new instance...\n"); ++ break; ++ default: ++ /* existing demod instance */ ++ printk(KERN_INFO "au8522_decoder attach existing instance.\n"); ++ break; ++ } ++ ++ demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); ++ if (demod_config == NULL) { ++ if (instance == 1) ++ kfree(state); ++ return -ENOMEM; ++ } ++ demod_config->demod_address = 0x8e >> 1; ++ ++ state->config = demod_config; ++ state->i2c = client->adapter; ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &au8522_ops); ++ ++ state->c = client; ++ state->vid_input = AU8522_COMPOSITE_CH1; ++ state->aud_input = AU8522_AUDIO_NONE; ++ state->id = 8522; ++ state->rev = 0; ++ ++ /* Jam open the i2c gate to the tuner */ ++ au8522_writereg(state, 0x106, 1); ++ ++ return 0; ++} ++ ++static int au8522_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ v4l2_device_unregister_subdev(sd); ++ au8522_release_state(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id au8522_id[] = { ++ {"au8522", 0}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, au8522_id); ++ ++static struct i2c_driver au8522_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "au8522", ++ }, ++ .probe = au8522_probe, ++ .remove = au8522_remove, ++ .id_table = au8522_id, ++}; ++ ++module_i2c_driver(au8522_driver); +diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c +new file mode 100644 +index 0000000..a68974f +--- /dev/null ++++ b/drivers/media/dvb-frontends/au8522_dig.c +@@ -0,0 +1,828 @@ ++/* ++ Auvitek AU8522 QAM/8VSB demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "au8522.h" ++#include "au8522_priv.h" ++ ++static int debug; ++ ++#define dprintk(arg...)\ ++ do { if (debug)\ ++ printk(arg);\ ++ } while (0) ++ ++struct mse2snr_tab { ++ u16 val; ++ u16 data; ++}; ++ ++/* VSB SNR lookup table */ ++static struct mse2snr_tab vsb_mse2snr_tab[] = { ++ { 0, 270 }, ++ { 2, 250 }, ++ { 3, 240 }, ++ { 5, 230 }, ++ { 7, 220 }, ++ { 9, 210 }, ++ { 12, 200 }, ++ { 13, 195 }, ++ { 15, 190 }, ++ { 17, 185 }, ++ { 19, 180 }, ++ { 21, 175 }, ++ { 24, 170 }, ++ { 27, 165 }, ++ { 31, 160 }, ++ { 32, 158 }, ++ { 33, 156 }, ++ { 36, 152 }, ++ { 37, 150 }, ++ { 39, 148 }, ++ { 40, 146 }, ++ { 41, 144 }, ++ { 43, 142 }, ++ { 44, 140 }, ++ { 48, 135 }, ++ { 50, 130 }, ++ { 43, 142 }, ++ { 53, 125 }, ++ { 56, 120 }, ++ { 256, 115 }, ++}; ++ ++/* QAM64 SNR lookup table */ ++static struct mse2snr_tab qam64_mse2snr_tab[] = { ++ { 15, 0 }, ++ { 16, 290 }, ++ { 17, 288 }, ++ { 18, 286 }, ++ { 19, 284 }, ++ { 20, 282 }, ++ { 21, 281 }, ++ { 22, 279 }, ++ { 23, 277 }, ++ { 24, 275 }, ++ { 25, 273 }, ++ { 26, 271 }, ++ { 27, 269 }, ++ { 28, 268 }, ++ { 29, 266 }, ++ { 30, 264 }, ++ { 31, 262 }, ++ { 32, 260 }, ++ { 33, 259 }, ++ { 34, 258 }, ++ { 35, 256 }, ++ { 36, 255 }, ++ { 37, 254 }, ++ { 38, 252 }, ++ { 39, 251 }, ++ { 40, 250 }, ++ { 41, 249 }, ++ { 42, 248 }, ++ { 43, 246 }, ++ { 44, 245 }, ++ { 45, 244 }, ++ { 46, 242 }, ++ { 47, 241 }, ++ { 48, 240 }, ++ { 50, 239 }, ++ { 51, 238 }, ++ { 53, 237 }, ++ { 54, 236 }, ++ { 56, 235 }, ++ { 57, 234 }, ++ { 59, 233 }, ++ { 60, 232 }, ++ { 62, 231 }, ++ { 63, 230 }, ++ { 65, 229 }, ++ { 67, 228 }, ++ { 68, 227 }, ++ { 70, 226 }, ++ { 71, 225 }, ++ { 73, 224 }, ++ { 74, 223 }, ++ { 76, 222 }, ++ { 78, 221 }, ++ { 80, 220 }, ++ { 82, 219 }, ++ { 85, 218 }, ++ { 88, 217 }, ++ { 90, 216 }, ++ { 92, 215 }, ++ { 93, 214 }, ++ { 94, 212 }, ++ { 95, 211 }, ++ { 97, 210 }, ++ { 99, 209 }, ++ { 101, 208 }, ++ { 102, 207 }, ++ { 104, 206 }, ++ { 107, 205 }, ++ { 111, 204 }, ++ { 114, 203 }, ++ { 118, 202 }, ++ { 122, 201 }, ++ { 125, 200 }, ++ { 128, 199 }, ++ { 130, 198 }, ++ { 132, 197 }, ++ { 256, 190 }, ++}; ++ ++/* QAM256 SNR lookup table */ ++static struct mse2snr_tab qam256_mse2snr_tab[] = { ++ { 15, 0 }, ++ { 16, 400 }, ++ { 17, 398 }, ++ { 18, 396 }, ++ { 19, 394 }, ++ { 20, 392 }, ++ { 21, 390 }, ++ { 22, 388 }, ++ { 23, 386 }, ++ { 24, 384 }, ++ { 25, 382 }, ++ { 26, 380 }, ++ { 27, 379 }, ++ { 28, 378 }, ++ { 29, 377 }, ++ { 30, 376 }, ++ { 31, 375 }, ++ { 32, 374 }, ++ { 33, 373 }, ++ { 34, 372 }, ++ { 35, 371 }, ++ { 36, 370 }, ++ { 37, 362 }, ++ { 38, 354 }, ++ { 39, 346 }, ++ { 40, 338 }, ++ { 41, 330 }, ++ { 42, 328 }, ++ { 43, 326 }, ++ { 44, 324 }, ++ { 45, 322 }, ++ { 46, 320 }, ++ { 47, 319 }, ++ { 48, 318 }, ++ { 49, 317 }, ++ { 50, 316 }, ++ { 51, 315 }, ++ { 52, 314 }, ++ { 53, 313 }, ++ { 54, 312 }, ++ { 55, 311 }, ++ { 56, 310 }, ++ { 57, 308 }, ++ { 58, 306 }, ++ { 59, 304 }, ++ { 60, 302 }, ++ { 61, 300 }, ++ { 62, 298 }, ++ { 65, 295 }, ++ { 68, 294 }, ++ { 70, 293 }, ++ { 73, 292 }, ++ { 76, 291 }, ++ { 78, 290 }, ++ { 79, 289 }, ++ { 81, 288 }, ++ { 82, 287 }, ++ { 83, 286 }, ++ { 84, 285 }, ++ { 85, 284 }, ++ { 86, 283 }, ++ { 88, 282 }, ++ { 89, 281 }, ++ { 256, 280 }, ++}; ++ ++static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse, ++ u16 *snr) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < sz; i++) { ++ if (mse < tab[i].val) { ++ *snr = tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ dprintk("%s() snr=%d\n", __func__, *snr); ++ return ret; ++} ++ ++static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ u8 r0b5, r0b6, r0b7; ++ char *ifmhz; ++ ++ switch (if_freq) { ++ case AU8522_IF_3_25MHZ: ++ ifmhz = "3.25"; ++ r0b5 = 0x00; ++ r0b6 = 0x3d; ++ r0b7 = 0xa0; ++ break; ++ case AU8522_IF_4MHZ: ++ ifmhz = "4.00"; ++ r0b5 = 0x00; ++ r0b6 = 0x4b; ++ r0b7 = 0xd9; ++ break; ++ case AU8522_IF_6MHZ: ++ ifmhz = "6.00"; ++ r0b5 = 0xfb; ++ r0b6 = 0x8e; ++ r0b7 = 0x39; ++ break; ++ default: ++ dprintk("%s() IF Frequency not supported\n", __func__); ++ return -EINVAL; ++ } ++ dprintk("%s() %s MHz\n", __func__, ifmhz); ++ au8522_writereg(state, 0x80b5, r0b5); ++ au8522_writereg(state, 0x80b6, r0b6); ++ au8522_writereg(state, 0x80b7, r0b7); ++ ++ return 0; ++} ++ ++/* VSB Modulation table */ ++static struct { ++ u16 reg; ++ u16 data; ++} VSB_mod_tab[] = { ++ { 0x8090, 0x84 }, ++ { 0x4092, 0x11 }, ++ { 0x2005, 0x00 }, ++ { 0x8091, 0x80 }, ++ { 0x80a3, 0x0c }, ++ { 0x80a4, 0xe8 }, ++ { 0x8081, 0xc4 }, ++ { 0x80a5, 0x40 }, ++ { 0x80a7, 0x40 }, ++ { 0x80a6, 0x67 }, ++ { 0x8262, 0x20 }, ++ { 0x821c, 0x30 }, ++ { 0x80d8, 0x1a }, ++ { 0x8227, 0xa0 }, ++ { 0x8121, 0xff }, ++ { 0x80a8, 0xf0 }, ++ { 0x80a9, 0x05 }, ++ { 0x80aa, 0x77 }, ++ { 0x80ab, 0xf0 }, ++ { 0x80ac, 0x05 }, ++ { 0x80ad, 0x77 }, ++ { 0x80ae, 0x41 }, ++ { 0x80af, 0x66 }, ++ { 0x821b, 0xcc }, ++ { 0x821d, 0x80 }, ++ { 0x80a4, 0xe8 }, ++ { 0x8231, 0x13 }, ++}; ++ ++/* QAM64 Modulation table */ ++static struct { ++ u16 reg; ++ u16 data; ++} QAM64_mod_tab[] = { ++ { 0x00a3, 0x09 }, ++ { 0x00a4, 0x00 }, ++ { 0x0081, 0xc4 }, ++ { 0x00a5, 0x40 }, ++ { 0x00aa, 0x77 }, ++ { 0x00ad, 0x77 }, ++ { 0x00a6, 0x67 }, ++ { 0x0262, 0x20 }, ++ { 0x021c, 0x30 }, ++ { 0x00b8, 0x3e }, ++ { 0x00b9, 0xf0 }, ++ { 0x00ba, 0x01 }, ++ { 0x00bb, 0x18 }, ++ { 0x00bc, 0x50 }, ++ { 0x00bd, 0x00 }, ++ { 0x00be, 0xea }, ++ { 0x00bf, 0xef }, ++ { 0x00c0, 0xfc }, ++ { 0x00c1, 0xbd }, ++ { 0x00c2, 0x1f }, ++ { 0x00c3, 0xfc }, ++ { 0x00c4, 0xdd }, ++ { 0x00c5, 0xaf }, ++ { 0x00c6, 0x00 }, ++ { 0x00c7, 0x38 }, ++ { 0x00c8, 0x30 }, ++ { 0x00c9, 0x05 }, ++ { 0x00ca, 0x4a }, ++ { 0x00cb, 0xd0 }, ++ { 0x00cc, 0x01 }, ++ { 0x00cd, 0xd9 }, ++ { 0x00ce, 0x6f }, ++ { 0x00cf, 0xf9 }, ++ { 0x00d0, 0x70 }, ++ { 0x00d1, 0xdf }, ++ { 0x00d2, 0xf7 }, ++ { 0x00d3, 0xc2 }, ++ { 0x00d4, 0xdf }, ++ { 0x00d5, 0x02 }, ++ { 0x00d6, 0x9a }, ++ { 0x00d7, 0xd0 }, ++ { 0x0250, 0x0d }, ++ { 0x0251, 0xcd }, ++ { 0x0252, 0xe0 }, ++ { 0x0253, 0x05 }, ++ { 0x0254, 0xa7 }, ++ { 0x0255, 0xff }, ++ { 0x0256, 0xed }, ++ { 0x0257, 0x5b }, ++ { 0x0258, 0xae }, ++ { 0x0259, 0xe6 }, ++ { 0x025a, 0x3d }, ++ { 0x025b, 0x0f }, ++ { 0x025c, 0x0d }, ++ { 0x025d, 0xea }, ++ { 0x025e, 0xf2 }, ++ { 0x025f, 0x51 }, ++ { 0x0260, 0xf5 }, ++ { 0x0261, 0x06 }, ++ { 0x021a, 0x00 }, ++ { 0x0546, 0x40 }, ++ { 0x0210, 0xc7 }, ++ { 0x0211, 0xaa }, ++ { 0x0212, 0xab }, ++ { 0x0213, 0x02 }, ++ { 0x0502, 0x00 }, ++ { 0x0121, 0x04 }, ++ { 0x0122, 0x04 }, ++ { 0x052e, 0x10 }, ++ { 0x00a4, 0xca }, ++ { 0x00a7, 0x40 }, ++ { 0x0526, 0x01 }, ++}; ++ ++/* QAM256 Modulation table */ ++static struct { ++ u16 reg; ++ u16 data; ++} QAM256_mod_tab[] = { ++ { 0x80a3, 0x09 }, ++ { 0x80a4, 0x00 }, ++ { 0x8081, 0xc4 }, ++ { 0x80a5, 0x40 }, ++ { 0x80aa, 0x77 }, ++ { 0x80ad, 0x77 }, ++ { 0x80a6, 0x67 }, ++ { 0x8262, 0x20 }, ++ { 0x821c, 0x30 }, ++ { 0x80b8, 0x3e }, ++ { 0x80b9, 0xf0 }, ++ { 0x80ba, 0x01 }, ++ { 0x80bb, 0x18 }, ++ { 0x80bc, 0x50 }, ++ { 0x80bd, 0x00 }, ++ { 0x80be, 0xea }, ++ { 0x80bf, 0xef }, ++ { 0x80c0, 0xfc }, ++ { 0x80c1, 0xbd }, ++ { 0x80c2, 0x1f }, ++ { 0x80c3, 0xfc }, ++ { 0x80c4, 0xdd }, ++ { 0x80c5, 0xaf }, ++ { 0x80c6, 0x00 }, ++ { 0x80c7, 0x38 }, ++ { 0x80c8, 0x30 }, ++ { 0x80c9, 0x05 }, ++ { 0x80ca, 0x4a }, ++ { 0x80cb, 0xd0 }, ++ { 0x80cc, 0x01 }, ++ { 0x80cd, 0xd9 }, ++ { 0x80ce, 0x6f }, ++ { 0x80cf, 0xf9 }, ++ { 0x80d0, 0x70 }, ++ { 0x80d1, 0xdf }, ++ { 0x80d2, 0xf7 }, ++ { 0x80d3, 0xc2 }, ++ { 0x80d4, 0xdf }, ++ { 0x80d5, 0x02 }, ++ { 0x80d6, 0x9a }, ++ { 0x80d7, 0xd0 }, ++ { 0x8250, 0x0d }, ++ { 0x8251, 0xcd }, ++ { 0x8252, 0xe0 }, ++ { 0x8253, 0x05 }, ++ { 0x8254, 0xa7 }, ++ { 0x8255, 0xff }, ++ { 0x8256, 0xed }, ++ { 0x8257, 0x5b }, ++ { 0x8258, 0xae }, ++ { 0x8259, 0xe6 }, ++ { 0x825a, 0x3d }, ++ { 0x825b, 0x0f }, ++ { 0x825c, 0x0d }, ++ { 0x825d, 0xea }, ++ { 0x825e, 0xf2 }, ++ { 0x825f, 0x51 }, ++ { 0x8260, 0xf5 }, ++ { 0x8261, 0x06 }, ++ { 0x821a, 0x00 }, ++ { 0x8546, 0x40 }, ++ { 0x8210, 0x26 }, ++ { 0x8211, 0xf6 }, ++ { 0x8212, 0x84 }, ++ { 0x8213, 0x02 }, ++ { 0x8502, 0x01 }, ++ { 0x8121, 0x04 }, ++ { 0x8122, 0x04 }, ++ { 0x852e, 0x10 }, ++ { 0x80a4, 0xca }, ++ { 0x80a7, 0x40 }, ++ { 0x8526, 0x01 }, ++}; ++ ++static int au8522_enable_modulation(struct dvb_frontend *fe, ++ fe_modulation_t m) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("%s(0x%08x)\n", __func__, m); ++ ++ switch (m) { ++ case VSB_8: ++ dprintk("%s() VSB_8\n", __func__); ++ for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++) ++ au8522_writereg(state, ++ VSB_mod_tab[i].reg, ++ VSB_mod_tab[i].data); ++ au8522_set_if(fe, state->config->vsb_if); ++ break; ++ case QAM_64: ++ dprintk("%s() QAM 64\n", __func__); ++ for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++) ++ au8522_writereg(state, ++ QAM64_mod_tab[i].reg, ++ QAM64_mod_tab[i].data); ++ au8522_set_if(fe, state->config->qam_if); ++ break; ++ case QAM_256: ++ dprintk("%s() QAM 256\n", __func__); ++ for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++) ++ au8522_writereg(state, ++ QAM256_mod_tab[i].reg, ++ QAM256_mod_tab[i].data); ++ au8522_set_if(fe, state->config->qam_if); ++ break; ++ default: ++ dprintk("%s() Invalid modulation\n", __func__); ++ return -EINVAL; ++ } ++ ++ state->current_modulation = m; ++ ++ return 0; ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++static int au8522_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct au8522_state *state = fe->demodulator_priv; ++ int ret = -EINVAL; ++ ++ dprintk("%s(frequency=%d)\n", __func__, c->frequency); ++ ++ if ((state->current_frequency == c->frequency) && ++ (state->current_modulation == c->modulation)) ++ return 0; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ if (ret < 0) ++ return ret; ++ ++ /* Allow the tuner to settle */ ++ msleep(100); ++ ++ au8522_enable_modulation(fe, c->modulation); ++ ++ state->current_frequency = c->frequency; ++ ++ return 0; ++} ++ ++static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ u8 reg; ++ u32 tuner_status = 0; ++ ++ *status = 0; ++ ++ if (state->current_modulation == VSB_8) { ++ dprintk("%s() Checking VSB_8\n", __func__); ++ reg = au8522_readreg(state, 0x4088); ++ if ((reg & 0x03) == 0x03) ++ *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; ++ } else { ++ dprintk("%s() Checking QAM\n", __func__); ++ reg = au8522_readreg(state, 0x4541); ++ if (reg & 0x80) ++ *status |= FE_HAS_VITERBI; ++ if (reg & 0x20) ++ *status |= FE_HAS_LOCK | FE_HAS_SYNC; ++ } ++ ++ switch (state->config->status_mode) { ++ case AU8522_DEMODLOCKING: ++ dprintk("%s() DEMODLOCKING\n", __func__); ++ if (*status & FE_HAS_VITERBI) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ case AU8522_TUNERLOCKING: ++ /* Get the tuner status */ ++ dprintk("%s() TUNERLOCKING\n", __func__); ++ if (fe->ops.tuner_ops.get_status) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ fe->ops.tuner_ops.get_status(fe, &tuner_status); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (tuner_status) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ } ++ state->fe_status = *status; ++ ++ if (*status & FE_HAS_LOCK) ++ /* turn on LED, if it isn't on already */ ++ au8522_led_ctrl(state, -1); ++ else ++ /* turn off LED */ ++ au8522_led_ctrl(state, 0); ++ ++ dprintk("%s() status 0x%08x\n", __func__, *status); ++ ++ return 0; ++} ++ ++static int au8522_led_status(struct au8522_state *state, const u16 *snr) ++{ ++ struct au8522_led_config *led_config = state->config->led_cfg; ++ int led; ++ u16 strong; ++ ++ /* bail out if we can't control an LED */ ++ if (!led_config) ++ return 0; ++ ++ if (0 == (state->fe_status & FE_HAS_LOCK)) ++ return au8522_led_ctrl(state, 0); ++ else if (state->current_modulation == QAM_256) ++ strong = led_config->qam256_strong; ++ else if (state->current_modulation == QAM_64) ++ strong = led_config->qam64_strong; ++ else /* (state->current_modulation == VSB_8) */ ++ strong = led_config->vsb8_strong; ++ ++ if (*snr >= strong) ++ led = 2; ++ else ++ led = 1; ++ ++ if ((state->led_state) && ++ (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10)) ++ /* snr didn't change enough to bother ++ * changing the color of the led */ ++ return 0; ++ ++ return au8522_led_ctrl(state, led); ++} ++ ++static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ int ret = -EINVAL; ++ ++ dprintk("%s()\n", __func__); ++ ++ if (state->current_modulation == QAM_256) ++ ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, ++ ARRAY_SIZE(qam256_mse2snr_tab), ++ au8522_readreg(state, 0x4522), ++ snr); ++ else if (state->current_modulation == QAM_64) ++ ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, ++ ARRAY_SIZE(qam64_mse2snr_tab), ++ au8522_readreg(state, 0x4522), ++ snr); ++ else /* VSB_8 */ ++ ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, ++ ARRAY_SIZE(vsb_mse2snr_tab), ++ au8522_readreg(state, 0x4311), ++ snr); ++ ++ if (state->config->led_cfg) ++ au8522_led_status(state, snr); ++ ++ return ret; ++} ++ ++static int au8522_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ /* borrowed from lgdt330x.c ++ * ++ * Calculate strength from SNR up to 35dB ++ * Even though the SNR can go higher than 35dB, ++ * there is some comfort factor in having a range of ++ * strong signals that can show at 100% ++ */ ++ u16 snr; ++ u32 tmp; ++ int ret = au8522_read_snr(fe, &snr); ++ ++ *signal_strength = 0; ++ ++ if (0 == ret) { ++ /* The following calculation method was chosen ++ * purely for the sake of code re-use from the ++ * other demod drivers that use this method */ ++ ++ /* Convert from SNR in dB * 10 to 8.24 fixed-point */ ++ tmp = (snr * ((1 << 24) / 10)); ++ ++ /* Convert from 8.24 fixed-point to ++ * scale the range 0 - 35*2^24 into 0 - 65535*/ ++ if (tmp >= 8960 * 0x10000) ++ *signal_strength = 0xffff; ++ else ++ *signal_strength = tmp / 8960; ++ } ++ ++ return ret; ++} ++ ++static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ ++ if (state->current_modulation == VSB_8) ++ *ucblocks = au8522_readreg(state, 0x4087); ++ else ++ *ucblocks = au8522_readreg(state, 0x4543); ++ ++ return 0; ++} ++ ++static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ return au8522_read_ucblocks(fe, ber); ++} ++ ++static int au8522_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct au8522_state *state = fe->demodulator_priv; ++ ++ c->frequency = state->current_frequency; ++ c->modulation = state->current_modulation; ++ ++ return 0; ++} ++ ++static int au8522_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static struct dvb_frontend_ops au8522_ops; ++ ++ ++static void au8522_release(struct dvb_frontend *fe) ++{ ++ struct au8522_state *state = fe->demodulator_priv; ++ au8522_release_state(state); ++} ++ ++struct dvb_frontend *au8522_attach(const struct au8522_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct au8522_state *state = NULL; ++ int instance; ++ ++ /* allocate memory for the internal state */ ++ instance = au8522_get_state(&state, i2c, config->demod_address); ++ switch (instance) { ++ case 0: ++ dprintk("%s state allocation failed\n", __func__); ++ break; ++ case 1: ++ /* new demod instance */ ++ dprintk("%s using new instance\n", __func__); ++ break; ++ default: ++ /* existing demod instance */ ++ dprintk("%s using existing instance\n", __func__); ++ break; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->operational_mode = AU8522_DIGITAL_MODE; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &au8522_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ state->frontend.ops.analog_ops.i2c_gate_ctrl = au8522_analog_i2c_gate_ctrl; ++ ++ if (au8522_init(&state->frontend) != 0) { ++ printk(KERN_ERR "%s: Failed to initialize correctly\n", ++ __func__); ++ goto error; ++ } ++ ++ /* Note: Leaving the I2C gate open here. */ ++ au8522_i2c_gate_ctrl(&state->frontend, 1); ++ ++ return &state->frontend; ++ ++error: ++ au8522_release_state(state); ++ return NULL; ++} ++EXPORT_SYMBOL(au8522_attach); ++ ++static struct dvb_frontend_ops au8522_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Auvitek AU8522 QAM/8VSB Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ ++ .init = au8522_init, ++ .sleep = au8522_sleep, ++ .i2c_gate_ctrl = au8522_i2c_gate_ctrl, ++ .set_frontend = au8522_set_frontend, ++ .get_frontend = au8522_get_frontend, ++ .get_tune_settings = au8522_get_tune_settings, ++ .read_status = au8522_read_status, ++ .read_ber = au8522_read_ber, ++ .read_signal_strength = au8522_read_signal_strength, ++ .read_snr = au8522_read_snr, ++ .read_ucblocks = au8522_read_ucblocks, ++ .release = au8522_release, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/au8522_priv.h b/drivers/media/dvb-frontends/au8522_priv.h +new file mode 100644 +index 0000000..0529699 +--- /dev/null ++++ b/drivers/media/dvb-frontends/au8522_priv.h +@@ -0,0 +1,446 @@ ++/* ++ Auvitek AU8522 QAM/8VSB demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ Copyright (C) 2008 Devin Heitmueller ++ Copyright (C) 2005-2008 Auvitek International, Ltd. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "au8522.h" ++#include "tuner-i2c.h" ++ ++#define AU8522_ANALOG_MODE 0 ++#define AU8522_DIGITAL_MODE 1 ++ ++struct au8522_state { ++ struct i2c_client *c; ++ struct i2c_adapter *i2c; ++ ++ u8 operational_mode; ++ ++ /* Used for sharing of the state between analog and digital mode */ ++ struct tuner_i2c_props i2c_props; ++ struct list_head hybrid_tuner_instance_list; ++ ++ /* configuration settings */ ++ const struct au8522_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ u32 current_frequency; ++ fe_modulation_t current_modulation; ++ ++ u32 fe_status; ++ unsigned int led_state; ++ ++ /* Analog settings */ ++ struct v4l2_subdev sd; ++ v4l2_std_id std; ++ int vid_input; ++ int aud_input; ++ u32 id; ++ u32 rev; ++ u8 brightness; ++ u8 contrast; ++ u8 saturation; ++ s16 hue; ++}; ++ ++/* These are routines shared by both the VSB/QAM demodulator and the analog ++ decoder */ ++int au8522_writereg(struct au8522_state *state, u16 reg, u8 data); ++u8 au8522_readreg(struct au8522_state *state, u16 reg); ++int au8522_init(struct dvb_frontend *fe); ++int au8522_sleep(struct dvb_frontend *fe); ++ ++int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, ++ u8 client_address); ++void au8522_release_state(struct au8522_state *state); ++int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); ++int au8522_analog_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); ++int au8522_led_ctrl(struct au8522_state *state, int led); ++ ++/* REGISTERS */ ++#define AU8522_INPUT_CONTROL_REG081H 0x081 ++#define AU8522_PGA_CONTROL_REG082H 0x082 ++#define AU8522_CLAMPING_CONTROL_REG083H 0x083 ++ ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3 ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5 ++#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6 ++#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7 ++#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8 ++#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9 ++#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA ++#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB ++#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC ++#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD ++#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE ++#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF ++ ++/* Receiver registers */ ++#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0 ++#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1 ++#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2 ++#define AU8522_TOREGAGC1_REG0B3H 0x0B3 ++#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4 ++#define AU8522_FRMREGBBH_REG0B5H 0x0B5 ++#define AU8522_FRMREGBBM_REG0B6H 0x0B6 ++#define AU8522_FRMREGBBL_REG0B7H 0x0B7 ++/* 0xB8 TO 0xD7 are the filter coefficients */ ++#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8 ++#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9 ++#define AU8522_TOREGAGC2_REG0DAH 0x0DA ++#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB ++#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC ++#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD ++#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE ++#define AU8522_TOREGFREQ_REG0DFH 0x0DF ++ ++#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB ++#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC ++#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED ++ ++#define AU8522_CHIP_MODE_REG0FEH 0x0FE ++ ++/* I2C bus control registers */ ++#define AU8522_I2C_CONTROL_REG0_REG090H 0x090 ++#define AU8522_I2C_CONTROL_REG1_REG091H 0x091 ++#define AU8522_I2C_STATUS_REG092H 0x092 ++#define AU8522_I2C_WR_DATA0_REG093H 0x093 ++#define AU8522_I2C_WR_DATA1_REG094H 0x094 ++#define AU8522_I2C_WR_DATA2_REG095H 0x095 ++#define AU8522_I2C_WR_DATA3_REG096H 0x096 ++#define AU8522_I2C_WR_DATA4_REG097H 0x097 ++#define AU8522_I2C_WR_DATA5_REG098H 0x098 ++#define AU8522_I2C_WR_DATA6_REG099H 0x099 ++#define AU8522_I2C_WR_DATA7_REG09AH 0x09A ++#define AU8522_I2C_RD_DATA0_REG09BH 0x09B ++#define AU8522_I2C_RD_DATA1_REG09CH 0x09C ++#define AU8522_I2C_RD_DATA2_REG09DH 0x09D ++#define AU8522_I2C_RD_DATA3_REG09EH 0x09E ++#define AU8522_I2C_RD_DATA4_REG09FH 0x09F ++#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0 ++#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1 ++#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2 ++ ++#define AU8522_ENA_USB_REG101H 0x101 ++ ++#define AU8522_I2S_CTRL_0_REG110H 0x110 ++#define AU8522_I2S_CTRL_1_REG111H 0x111 ++#define AU8522_I2S_CTRL_2_REG112H 0x112 ++ ++#define AU8522_FRMREGFFECONTROL_REG121H 0x121 ++#define AU8522_FRMREGDFECONTROL_REG122H 0x122 ++ ++#define AU8522_CARRFREQOFFSET0_REG201H 0x201 ++#define AU8522_CARRFREQOFFSET1_REG202H 0x202 ++ ++#define AU8522_DECIMATION_GAIN_REG21AH 0x21A ++#define AU8522_FRMREGIFSLP_REG21BH 0x21B ++#define AU8522_FRMREGTHRDL2_REG21CH 0x21C ++#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D ++#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E ++#define AU8522_FRMREGPLLMODE_REG21FH 0x21F ++#define AU8522_FRMREGCSTHRD_REG220H 0x220 ++#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221 ++#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222 ++#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223 ++#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224 ++#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225 ++#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226 ++#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227 ++#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228 ++ ++/* Analog TV Decoder */ ++#define AU8522_TVDEC_STATUS_REG000H 0x000 ++#define AU8522_TVDEC_INT_STATUS_REG001H 0x001 ++#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002 ++#define AU8522_TVDEC_SHARPNESSREG009H 0x009 ++#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A ++#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B ++#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C ++#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D ++#define AU8522_TVDEC_HUE_H_REG00EH 0x00E ++#define AU8522_TVDEC_HUE_L_REG00FH 0x00F ++#define AU8522_TVDEC_INT_MASK_REG010H 0x010 ++#define AU8522_VIDEO_MODE_REG011H 0x011 ++#define AU8522_TVDEC_PGA_REG012H 0x012 ++#define AU8522_TVDEC_COMB_MODE_REG015H 0x015 ++#define AU8522_REG016H 0x016 ++#define AU8522_TVDED_DBG_MODE_REG060H 0x060 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061 ++#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062 ++#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063 ++#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064 ++#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065 ++#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066 ++#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067 ++#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068 ++#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069 ++#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A ++#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B ++#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C ++#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D ++#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E ++#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F ++#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070 ++#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073 ++#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077 ++#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078 ++#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079 ++#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A ++#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B ++#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E ++#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F ++ ++#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4 ++#define AU8522_TOREGAAGC_REG0E5H 0x0E5 ++ ++#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401 ++#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402 ++#define AU8522_FILTER_COEF_R410 0x410 ++#define AU8522_FILTER_COEF_R411 0x411 ++#define AU8522_FILTER_COEF_R412 0x412 ++#define AU8522_FILTER_COEF_R413 0x413 ++#define AU8522_FILTER_COEF_R414 0x414 ++#define AU8522_FILTER_COEF_R415 0x415 ++#define AU8522_FILTER_COEF_R416 0x416 ++#define AU8522_FILTER_COEF_R417 0x417 ++#define AU8522_FILTER_COEF_R418 0x418 ++#define AU8522_FILTER_COEF_R419 0x419 ++#define AU8522_FILTER_COEF_R41A 0x41A ++#define AU8522_FILTER_COEF_R41B 0x41B ++#define AU8522_FILTER_COEF_R41C 0x41C ++#define AU8522_FILTER_COEF_R41D 0x41D ++#define AU8522_FILTER_COEF_R41E 0x41E ++#define AU8522_FILTER_COEF_R41F 0x41F ++#define AU8522_FILTER_COEF_R420 0x420 ++#define AU8522_FILTER_COEF_R421 0x421 ++#define AU8522_FILTER_COEF_R422 0x422 ++#define AU8522_FILTER_COEF_R423 0x423 ++#define AU8522_FILTER_COEF_R424 0x424 ++#define AU8522_FILTER_COEF_R425 0x425 ++#define AU8522_FILTER_COEF_R426 0x426 ++#define AU8522_FILTER_COEF_R427 0x427 ++#define AU8522_FILTER_COEF_R428 0x428 ++#define AU8522_FILTER_COEF_R429 0x429 ++#define AU8522_FILTER_COEF_R42A 0x42A ++#define AU8522_FILTER_COEF_R42B 0x42B ++#define AU8522_FILTER_COEF_R42C 0x42C ++#define AU8522_FILTER_COEF_R42D 0x42D ++ ++/* VBI Control Registers */ ++#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004 ++#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005 ++#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006 ++#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007 ++#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017 ++#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018 ++#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019 ++#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A ++#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B ++#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C ++#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E ++#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F ++#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020 ++#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021 ++#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022 ++#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023 ++ ++#define AU8522_REG071H 0x071 ++#define AU8522_REG072H 0x072 ++#define AU8522_REG074H 0x074 ++#define AU8522_REG075H 0x075 ++ ++/* Digital Demodulator Registers */ ++#define AU8522_FRAME_COUNT0_REG084H 0x084 ++#define AU8522_RS_STATUS_G0_REG085H 0x085 ++#define AU8522_RS_STATUS_B0_REG086H 0x086 ++#define AU8522_RS_STATUS_E_REG087H 0x087 ++#define AU8522_DEMODULATION_STATUS_REG088H 0x088 ++#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6 ++#define AU8522_TSPORT_CONTROL_REG10BH 0x10B ++#define AU8522_TSTHES_REG10CH 0x10C ++#define AU8522_FRMREGDFEKEEP_REG301H 0x301 ++#define AU8522_DFE_AVERAGE_REG302H 0x302 ++#define AU8522_FRMREGEQLERRWIN_REG303H 0x303 ++#define AU8522_FRMREGFFEKEEP_REG304H 0x304 ++#define AU8522_FRMREGDFECONTROL1_REG305H 0x305 ++#define AU8522_FRMREGEQLERRLOW_REG306H 0x306 ++ ++#define AU8522_REG42EH 0x42E ++#define AU8522_REG42FH 0x42F ++#define AU8522_REG430H 0x430 ++#define AU8522_REG431H 0x431 ++#define AU8522_REG432H 0x432 ++#define AU8522_REG433H 0x433 ++#define AU8522_REG434H 0x434 ++#define AU8522_REG435H 0x435 ++#define AU8522_REG436H 0x436 ++ ++/* GPIO Registers */ ++#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0 ++#define AU8522_GPIO_STATUS_REG0E1H 0x0E1 ++#define AU8522_GPIO_DATA_REG0E2H 0x0E2 ++ ++/* Audio Control Registers */ ++#define AU8522_AUDIOAGC_REG0EEH 0x0EE ++#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0 ++#define AU8522_AUDIO_MODE_REG0F1H 0x0F1 ++#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2 ++#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3 ++#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4 ++#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7 ++#define AU8522_REG0F9H 0x0F9 ++ ++#define AU8522_AUDIOAGC2_REG605H 0x605 ++#define AU8522_AUDIOFREQ_REG606H 0x606 ++ ++ ++/**************************************************************/ ++ ++/* Format control 1 */ ++ ++/* VCR Mode 7-6 */ ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_YES 0x80 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_NO 0x40 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_VCR_MODE_AUTO 0x00 ++/* Field len 5-4 */ ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_625 0x20 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_525 0x10 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_FIELD_LEN_AUTO 0x00 ++/* Line len (us) 3-2 */ ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_64_000 0x0b ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_492 0x08 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_LINE_LEN_63_556 0x04 ++/* Subcarrier freq 1-0 */ ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_AUTO 0x03 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_443 0x02 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_MN 0x01 ++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_SUBCARRIER_NTSC_50 0x00 ++ ++/* Format control 2 */ ++#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_AUTODETECT 0x00 ++#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_STD_NTSC 0x01 ++ ++ ++#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4 ++#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4 ++#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4 ++#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4 ++#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68 ++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28 ++/* CH1 AS Y,CH3 AS C */ ++#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23 ++/* CH2 AS Y,CH4 AS C */ ++#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20 ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09 ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09 ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12 ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A ++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02 ++ ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00 ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8 ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD ++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD ++ ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01 ++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01 ++ ++/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */ ++#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79 ++#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80 ++#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80 ++#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00 ++#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00 ++#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F ++#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00 ++#define AU8522_REG016H_CVBS 0x00 ++#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00 ++#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19 ++#define AU8522_REG0F9H_AUDIO 0x20 ++#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7 ++#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A ++#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32 ++#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19 ++#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23 ++#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41 ++#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A ++#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32 ++#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34 ++#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a ++#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05 ++#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15 ++#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E ++#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F ++#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80 ++#define AU8522_REG071H_CVBS 0x18 ++#define AU8522_REG072H_CVBS 0x30 ++#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0 ++#define AU8522_REG074H_CVBS 0x80 ++#define AU8522_REG075H_CVBS 0xF0 ++#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB ++#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04 ++#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00 ++#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00 ++#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE ++#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE ++#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00 ++#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40 ++ ++/* Enables Closed captioning */ ++#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21 +diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c +new file mode 100644 +index 0000000..1b77909 +--- /dev/null ++++ b/drivers/media/dvb-frontends/bcm3510.c +@@ -0,0 +1,856 @@ ++/* ++ * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) ++ * ++ * Copyright (C) 2001-5, B2C2 inc. ++ * ++ * GPL/Linux driver written by Patrick Boettcher ++ * ++ * This driver is "hard-coded" to be used with the 1st generation of ++ * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming ++ * (Panasonic CT10S) is located here, which is actually wrong. Unless there is ++ * another device with a BCM3510, this is no problem. ++ * ++ * The driver works also with QAM64 DVB-C, but had an unreasonable high ++ * UNC. (Tested with the Air2PC ATSC 1st generation) ++ * ++ * You'll need a firmware for this driver in order to get it running. It is ++ * called "dvb-fe-bcm3510-01.fw". ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 675 Mass ++ * Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "bcm3510.h" ++#include "bcm3510_priv.h" ++ ++struct bcm3510_state { ++ ++ struct i2c_adapter* i2c; ++ const struct bcm3510_config* config; ++ struct dvb_frontend frontend; ++ ++ /* demodulator private data */ ++ struct mutex hab_mutex; ++ u8 firmware_loaded:1; ++ ++ unsigned long next_status_check; ++ unsigned long status_check_interval; ++ struct bcm3510_hab_cmd_status1 status1; ++ struct bcm3510_hab_cmd_status2 status2; ++}; ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); ++ ++#define dprintk(level,x...) if (level & debug) printk(x) ++#define dbufout(b,l,m) {\ ++ int i; \ ++ for (i = 0; i < l; i++) \ ++ m("%02x ",b[i]); \ ++} ++#define deb_info(args...) dprintk(0x01,args) ++#define deb_i2c(args...) dprintk(0x02,args) ++#define deb_hab(args...) dprintk(0x04,args) ++ ++/* transfer functions */ ++static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) ++{ ++ u8 b[256]; ++ int err; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; ++ ++ b[0] = reg; ++ memcpy(&b[1],buf,len); ++ ++ deb_i2c("i2c wr %02x: ",reg); ++ dbufout(buf,len,deb_i2c); ++ deb_i2c("\n"); ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ ++ deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", ++ __func__, state->config->demod_address, reg, err); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) ++{ ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } ++ }; ++ int err; ++ ++ memset(buf,0,len); ++ ++ if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { ++ deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", ++ __func__, state->config->demod_address, reg, err); ++ return -EREMOTEIO; ++ } ++ deb_i2c("i2c rd %02x: ",reg); ++ dbufout(buf,len,deb_i2c); ++ deb_i2c("\n"); ++ ++ return 0; ++} ++ ++static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) ++{ ++ return bcm3510_writebytes(state,reg,&v.raw,1); ++} ++ ++static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) ++{ ++ return bcm3510_readbytes(state,reg,&v->raw,1); ++} ++ ++/* Host Access Buffer transfers */ ++static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) ++{ ++ bcm3510_register_value v; ++ int ret,i; ++ ++ v.HABADR_a6.HABADR = 0; ++ if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) ++ return ret; ++ ++ for (i = 0; i < len; i++) { ++ if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) ++ return ret; ++ buf[i] = v.HABDATA_a7; ++ } ++ return 0; ++} ++ ++static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) ++{ ++ bcm3510_register_value v,hab; ++ int ret,i; ++ unsigned long t; ++ ++/* Check if any previous HAB request still needs to be serviced by the ++ * Acquisition Processor before sending new request */ ++ if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) ++ return ret; ++ if (v.HABSTAT_a8.HABR) { ++ deb_info("HAB is running already - clearing it.\n"); ++ v.HABSTAT_a8.HABR = 0; ++ bcm3510_writeB(st,0xa8,v); ++// return -EBUSY; ++ } ++ ++/* Send the start HAB Address (automatically incremented after write of ++ * HABDATA) and write the HAB Data */ ++ hab.HABADR_a6.HABADR = 0; ++ if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) ++ return ret; ++ ++ for (i = 0; i < len; i++) { ++ hab.HABDATA_a7 = buf[i]; ++ if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) ++ return ret; ++ } ++ ++/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to ++ * be written) */ ++ v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; ++ if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) ++ return ret; ++ ++/* Polling method: Wait until the AP finishes processing the HAB request */ ++ t = jiffies + 1*HZ; ++ while (time_before(jiffies, t)) { ++ deb_info("waiting for HAB to complete\n"); ++ msleep(10); ++ if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) ++ return ret; ++ ++ if (!v.HABSTAT_a8.HABR) ++ return 0; ++ } ++ ++ deb_info("send_request execution timed out.\n"); ++ return -ETIMEDOUT; ++} ++ ++static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) ++{ ++ u8 ob[olen+2],ib[ilen+2]; ++ int ret = 0; ++ ++ ob[0] = cmd; ++ ob[1] = msgid; ++ memcpy(&ob[2],obuf,olen); ++ ++ deb_hab("hab snd: "); ++ dbufout(ob,olen+2,deb_hab); ++ deb_hab("\n"); ++ ++ if (mutex_lock_interruptible(&st->hab_mutex) < 0) ++ return -EAGAIN; ++ ++ if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || ++ (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) ++ goto error; ++ ++ deb_hab("hab get: "); ++ dbufout(ib,ilen+2,deb_hab); ++ deb_hab("\n"); ++ ++ memcpy(ibuf,&ib[2],ilen); ++error: ++ mutex_unlock(&st->hab_mutex); ++ return ret; ++} ++ ++#if 0 ++/* not needed, we use a semaphore to prevent HAB races */ ++static int bcm3510_is_ap_ready(struct bcm3510_state *st) ++{ ++ bcm3510_register_value ap,hab; ++ int ret; ++ ++ if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || ++ (ret = bcm3510_readB(st,0xa2,&ap) < 0)) ++ return ret; ++ ++ if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { ++ deb_info("AP is busy\n"); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++#endif ++ ++static int bcm3510_bert_reset(struct bcm3510_state *st) ++{ ++ bcm3510_register_value b; ++ int ret; ++ ++ if ((ret = bcm3510_readB(st,0xfa,&b)) < 0) ++ return ret; ++ ++ b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); ++ b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); ++ b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); ++ b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); ++ ++ /* clear residual bit counter TODO */ ++ return 0; ++} ++ ++static int bcm3510_refresh_state(struct bcm3510_state *st) ++{ ++ if (time_after(jiffies,st->next_status_check)) { ++ bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); ++ bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); ++ st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; ++ } ++ return 0; ++} ++ ++static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ bcm3510_refresh_state(st); ++ ++ *status = 0; ++ if (st->status1.STATUS1.RECEIVER_LOCK) ++ *status |= FE_HAS_LOCK | FE_HAS_SYNC; ++ ++ if (st->status1.STATUS1.FEC_LOCK) ++ *status |= FE_HAS_VITERBI; ++ ++ if (st->status1.STATUS1.OUT_PLL_LOCK) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ ++ if (*status & FE_HAS_LOCK) ++ st->status_check_interval = 1500; ++ else /* more frequently checks if no lock has been achieved yet */ ++ st->status_check_interval = 500; ++ ++ deb_info("real_status: %02x\n",*status); ++ return 0; ++} ++ ++static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ bcm3510_refresh_state(st); ++ ++ *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; ++ return 0; ++} ++ ++static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ bcm3510_refresh_state(st); ++ *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; ++ return 0; ++} ++ ++static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ s32 t; ++ ++ bcm3510_refresh_state(st); ++ t = st->status2.SIGNAL; ++ ++ if (t > 190) ++ t = 190; ++ if (t < 90) ++ t = 90; ++ ++ t -= 90; ++ t = t * 0xff / 100; ++ /* normalize if necessary */ ++ *strength = (t << 8) | t; ++ return 0; ++} ++ ++static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ bcm3510_refresh_state(st); ++ ++ *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); ++ return 0; ++} ++ ++/* tuner frontend programming */ ++static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) ++{ ++ struct bcm3510_hab_cmd_tune c; ++ memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); ++ ++/* I2C Mode disabled, set 16 control / Data pairs */ ++ c.length = 0x10; ++ c.clock_width = 0; ++/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to ++ * logic high (as Configuration) */ ++ c.misc = 0x10; ++/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ ++ c.TUNCTL_state = 0x40; ++ ++/* PRESCALER DIVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ ++ c.ctl_dat[0].ctrl.size = BITS_8; ++ c.ctl_dat[0].data = 0x80 | bc; ++ ++/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ ++ c.ctl_dat[1].ctrl.size = BITS_8; ++ c.ctl_dat[1].data = 4; ++ ++/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ ++ c.ctl_dat[2].ctrl.size = BITS_3; ++ c.ctl_dat[2].data = 0x20; ++ ++/* control CS0 pin, pulse byte ? */ ++ c.ctl_dat[3].ctrl.size = BITS_3; ++ c.ctl_dat[3].ctrl.clk_off = 1; ++ c.ctl_dat[3].ctrl.cs0 = 1; ++ c.ctl_dat[3].data = 0x40; ++ ++/* PGM_S18 to PGM_S11 */ ++ c.ctl_dat[4].ctrl.size = BITS_8; ++ c.ctl_dat[4].data = n >> 3; ++ ++/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ ++ c.ctl_dat[5].ctrl.size = BITS_8; ++ c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); ++ ++/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ ++ c.ctl_dat[6].ctrl.size = BITS_3; ++ c.ctl_dat[6].data = (a << 6) & 0xdf; ++ ++/* control CS0 pin, pulse byte ? */ ++ c.ctl_dat[7].ctrl.size = BITS_3; ++ c.ctl_dat[7].ctrl.clk_off = 1; ++ c.ctl_dat[7].ctrl.cs0 = 1; ++ c.ctl_dat[7].data = 0x40; ++ ++/* PRESCALER DIVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ ++ c.ctl_dat[8].ctrl.size = BITS_8; ++ c.ctl_dat[8].data = 0x80; ++ ++/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ ++ c.ctl_dat[9].ctrl.size = BITS_8; ++ c.ctl_dat[9].data = 0x10; ++ ++/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ ++ c.ctl_dat[10].ctrl.size = BITS_3; ++ c.ctl_dat[10].data = 0x20; ++ ++/* pulse byte */ ++ c.ctl_dat[11].ctrl.size = BITS_3; ++ c.ctl_dat[11].ctrl.clk_off = 1; ++ c.ctl_dat[11].ctrl.cs1 = 1; ++ c.ctl_dat[11].data = 0x40; ++ ++/* PGM_S18 to PGM_S11 */ ++ c.ctl_dat[12].ctrl.size = BITS_8; ++ c.ctl_dat[12].data = 0x2a; ++ ++/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ ++ c.ctl_dat[13].ctrl.size = BITS_8; ++ c.ctl_dat[13].data = 0x8e; ++ ++/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ ++ c.ctl_dat[14].ctrl.size = BITS_3; ++ c.ctl_dat[14].data = 0; ++ ++/* Pulse Byte */ ++ c.ctl_dat[15].ctrl.size = BITS_3; ++ c.ctl_dat[15].ctrl.clk_off = 1; ++ c.ctl_dat[15].ctrl.cs1 = 1; ++ c.ctl_dat[15].data = 0x40; ++ ++ return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); ++} ++ ++static int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) ++{ ++ u8 bc,a; ++ u16 n; ++ s32 YIntercept,Tfvco1; ++ ++ freq /= 1000; ++ ++ deb_info("%dkHz:",freq); ++ /* set Band Switch */ ++ if (freq <= 168000) ++ bc = 0x1c; ++ else if (freq <= 378000) ++ bc = 0x2c; ++ else ++ bc = 0x30; ++ ++ if (freq >= 470000) { ++ freq -= 470001; ++ YIntercept = 18805; ++ } else if (freq >= 90000) { ++ freq -= 90001; ++ YIntercept = 15005; ++ } else if (freq >= 76000){ ++ freq -= 76001; ++ YIntercept = 14865; ++ } else { ++ freq -= 54001; ++ YIntercept = 14645; ++ } ++ ++ Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; ++ ++ n = Tfvco1 >> 6; ++ a = Tfvco1 & 0x3f; ++ ++ deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); ++ if (n >= 16 && n <= 2047) ++ return bcm3510_tuner_cmd(st,bc,n,a); ++ ++ return -EINVAL; ++} ++ ++static int bcm3510_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct bcm3510_state* st = fe->demodulator_priv; ++ struct bcm3510_hab_cmd_ext_acquire cmd; ++ struct bcm3510_hab_cmd_bert_control bert; ++ int ret; ++ ++ memset(&cmd,0,sizeof(cmd)); ++ switch (c->modulation) { ++ case QAM_256: ++ cmd.ACQUIRE0.MODE = 0x1; ++ cmd.ACQUIRE1.SYM_RATE = 0x1; ++ cmd.ACQUIRE1.IF_FREQ = 0x1; ++ break; ++ case QAM_64: ++ cmd.ACQUIRE0.MODE = 0x2; ++ cmd.ACQUIRE1.SYM_RATE = 0x2; ++ cmd.ACQUIRE1.IF_FREQ = 0x1; ++ break; ++#if 0 ++ case QAM_256: ++ cmd.ACQUIRE0.MODE = 0x3; ++ break; ++ case QAM_128: ++ cmd.ACQUIRE0.MODE = 0x4; ++ break; ++ case QAM_64: ++ cmd.ACQUIRE0.MODE = 0x5; ++ break; ++ case QAM_32: ++ cmd.ACQUIRE0.MODE = 0x6; ++ break; ++ case QAM_16: ++ cmd.ACQUIRE0.MODE = 0x7; ++ break; ++#endif ++ case VSB_8: ++ cmd.ACQUIRE0.MODE = 0x8; ++ cmd.ACQUIRE1.SYM_RATE = 0x0; ++ cmd.ACQUIRE1.IF_FREQ = 0x0; ++ break; ++ case VSB_16: ++ cmd.ACQUIRE0.MODE = 0x9; ++ cmd.ACQUIRE1.SYM_RATE = 0x0; ++ cmd.ACQUIRE1.IF_FREQ = 0x0; ++ default: ++ return -EINVAL; ++ } ++ cmd.ACQUIRE0.OFFSET = 0; ++ cmd.ACQUIRE0.NTSCSWEEP = 1; ++ cmd.ACQUIRE0.FA = 1; ++ cmd.ACQUIRE0.BW = 0; ++ ++/* if (enableOffset) { ++ cmd.IF_OFFSET0 = xx; ++ cmd.IF_OFFSET1 = xx; ++ ++ cmd.SYM_OFFSET0 = xx; ++ cmd.SYM_OFFSET1 = xx; ++ if (enableNtscSweep) { ++ cmd.NTSC_OFFSET0; ++ cmd.NTSC_OFFSET1; ++ } ++ } */ ++ bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); ++ ++/* doing it with different MSGIDs, data book and source differs */ ++ bert.BE = 0; ++ bert.unused = 0; ++ bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); ++ bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); ++ ++ bcm3510_bert_reset(st); ++ ++ ret = bcm3510_set_freq(st, c->frequency); ++ if (ret < 0) ++ return ret; ++ ++ memset(&st->status1,0,sizeof(st->status1)); ++ memset(&st->status2,0,sizeof(st->status2)); ++ st->status_check_interval = 500; ++ ++/* Give the AP some time */ ++ msleep(200); ++ ++ return 0; ++} ++ ++static int bcm3510_sleep(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 1000; ++ s->step_size = 0; ++ s->max_drift = 0; ++ return 0; ++} ++ ++static void bcm3510_release(struct dvb_frontend* fe) ++{ ++ struct bcm3510_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++/* firmware download: ++ * firmware file is build up like this: ++ * 16bit addr, 16bit length, 8byte of length ++ */ ++#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" ++ ++static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b, ++ u16 len) ++{ ++ int ret = 0,i; ++ bcm3510_register_value vH, vL,vD; ++ ++ vH.MADRH_a9 = addr >> 8; ++ vL.MADRL_aa = addr; ++ if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; ++ if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; ++ ++ for (i = 0; i < len; i++) { ++ vD.MDATA_ab = b[i]; ++ if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int bcm3510_download_firmware(struct dvb_frontend* fe) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ const struct firmware *fw; ++ u16 addr,len; ++ const u8 *b; ++ int ret,i; ++ ++ deb_info("requesting firmware\n"); ++ if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { ++ err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); ++ return ret; ++ } ++ deb_info("got firmware: %zd\n",fw->size); ++ ++ b = fw->data; ++ for (i = 0; i < fw->size;) { ++ addr = le16_to_cpu( *( (u16 *)&b[i] ) ); ++ len = le16_to_cpu( *( (u16 *)&b[i+2] ) ); ++ deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); ++ if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { ++ err("firmware download failed: %d\n",ret); ++ return ret; ++ } ++ i += 4 + len; ++ } ++ release_firmware(fw); ++ deb_info("firmware download successfully completed\n"); ++ return 0; ++} ++ ++static int bcm3510_check_firmware_version(struct bcm3510_state *st) ++{ ++ struct bcm3510_hab_cmd_get_version_info ver; ++ bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); ++ ++ deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", ++ ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); ++ ++ if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && ++ ver.config_version == BCM3510_DEF_CONFIG_VERSION && ++ ver.demod_version == BCM3510_DEF_DEMOD_VERSION) ++ return 0; ++ ++ deb_info("version check failed\n"); ++ return -ENODEV; ++} ++ ++/* (un)resetting the AP */ ++static int bcm3510_reset(struct bcm3510_state *st) ++{ ++ int ret; ++ unsigned long t; ++ bcm3510_register_value v; ++ ++ bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; ++ if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) ++ return ret; ++ ++ t = jiffies + 3*HZ; ++ while (time_before(jiffies, t)) { ++ msleep(10); ++ if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) ++ return ret; ++ ++ if (v.APSTAT1_a2.RESET) ++ return 0; ++ } ++ deb_info("reset timed out\n"); ++ return -ETIMEDOUT; ++} ++ ++static int bcm3510_clear_reset(struct bcm3510_state *st) ++{ ++ bcm3510_register_value v; ++ int ret; ++ unsigned long t; ++ ++ v.raw = 0; ++ if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) ++ return ret; ++ ++ t = jiffies + 3*HZ; ++ while (time_before(jiffies, t)) { ++ msleep(10); ++ if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) ++ return ret; ++ ++ /* verify that reset is cleared */ ++ if (!v.APSTAT1_a2.RESET) ++ return 0; ++ } ++ deb_info("reset clear timed out\n"); ++ return -ETIMEDOUT; ++} ++ ++static int bcm3510_init_cold(struct bcm3510_state *st) ++{ ++ int ret; ++ bcm3510_register_value v; ++ ++ /* read Acquisation Processor status register and check it is not in RUN mode */ ++ if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) ++ return ret; ++ if (v.APSTAT1_a2.RUN) { ++ deb_info("AP is already running - firmware already loaded.\n"); ++ return 0; ++ } ++ ++ deb_info("reset?\n"); ++ if ((ret = bcm3510_reset(st)) < 0) ++ return ret; ++ ++ deb_info("tristate?\n"); ++ /* tri-state */ ++ v.TSTCTL_2e.CTL = 0; ++ if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) ++ return ret; ++ ++ deb_info("firmware?\n"); ++ if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || ++ (ret = bcm3510_clear_reset(st)) < 0) ++ return ret; ++ ++ /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ ++ ++ return 0; ++} ++ ++static int bcm3510_init(struct dvb_frontend* fe) ++{ ++ struct bcm3510_state* st = fe->demodulator_priv; ++ bcm3510_register_value j; ++ struct bcm3510_hab_cmd_set_agc c; ++ int ret; ++ ++ if ((ret = bcm3510_readB(st,0xca,&j)) < 0) ++ return ret; ++ ++ deb_info("JDEC: %02x\n",j.raw); ++ ++ switch (j.JDEC_ca.JDEC) { ++ case JDEC_WAIT_AT_RAM: ++ deb_info("attempting to download firmware\n"); ++ if ((ret = bcm3510_init_cold(st)) < 0) ++ return ret; ++ case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */ ++ deb_info("firmware is loaded\n"); ++ bcm3510_check_firmware_version(st); ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ memset(&c,0,1); ++ c.SEL = 1; ++ bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); ++ ++ return 0; ++} ++ ++ ++static struct dvb_frontend_ops bcm3510_ops; ++ ++struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct bcm3510_state* state = NULL; ++ int ret; ++ bcm3510_register_value v; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct bcm3510_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ mutex_init(&state->hab_mutex); ++ ++ if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) ++ goto error; ++ ++ deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); ++ ++ if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ ++ (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ ++ goto error; ++ ++ info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); ++ ++ bcm3510_reset(state); ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(bcm3510_attach); ++ ++static struct dvb_frontend_ops bcm3510_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Broadcom BCM3510 VSB/QAM frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 803000000, ++ /* stepsize is just a guess */ ++ .frequency_stepsize = 0, ++ .caps = ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_8VSB | FE_CAN_16VSB | ++ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 ++ }, ++ ++ .release = bcm3510_release, ++ ++ .init = bcm3510_init, ++ .sleep = bcm3510_sleep, ++ ++ .set_frontend = bcm3510_set_frontend, ++ .get_tune_settings = bcm3510_get_tune_settings, ++ ++ .read_status = bcm3510_read_status, ++ .read_ber = bcm3510_read_ber, ++ .read_signal_strength = bcm3510_read_signal_strength, ++ .read_snr = bcm3510_read_snr, ++ .read_ucblocks = bcm3510_read_unc, ++}; ++ ++MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/bcm3510.h b/drivers/media/dvb-frontends/bcm3510.h +new file mode 100644 +index 0000000..f4575c0 +--- /dev/null ++++ b/drivers/media/dvb-frontends/bcm3510.h +@@ -0,0 +1,49 @@ ++/* ++ * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) ++ * ++ * Copyright (C) 2001-5, B2C2 inc. ++ * ++ * GPL/Linux driver written by Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef BCM3510_H ++#define BCM3510_H ++ ++#include ++#include ++ ++struct bcm3510_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* request firmware for device */ ++ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); ++}; ++ ++#if defined(CONFIG_DVB_BCM3510) || (defined(CONFIG_DVB_BCM3510_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_BCM3510 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/bcm3510_priv.h b/drivers/media/dvb-frontends/bcm3510_priv.h +new file mode 100644 +index 0000000..3bb1bc2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/bcm3510_priv.h +@@ -0,0 +1,460 @@ ++/* ++ * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) ++ * ++ * Copyright (C) 2001-5, B2C2 inc. ++ * ++ * GPL/Linux driver written by Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef __BCM3510_PRIV_H__ ++#define __BCM3510_PRIV_H__ ++ ++#define PACKED __attribute__((packed)) ++ ++#undef err ++#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n" , ## arg) ++#undef info ++#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n" , ## arg) ++#undef warn ++#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n" , ## arg) ++ ++ ++#define PANASONIC_FIRST_IF_BASE_IN_KHz 1407500 ++#define BCM3510_SYMBOL_RATE 5381000 ++ ++typedef union { ++ u8 raw; ++ ++ struct { ++ u8 CTL :8; ++ } TSTCTL_2e; ++ ++ u8 LDCERC_4e; ++ u8 LDUERC_4f; ++ u8 LD_BER0_65; ++ u8 LD_BER1_66; ++ u8 LD_BER2_67; ++ u8 LD_BER3_68; ++ ++ struct { ++ u8 RESET :1; ++ u8 IDLE :1; ++ u8 STOP :1; ++ u8 HIRQ0 :1; ++ u8 HIRQ1 :1; ++ u8 na0 :1; ++ u8 HABAV :1; ++ u8 na1 :1; ++ } HCTL1_a0; ++ ++ struct { ++ u8 na0 :1; ++ u8 IDLMSK :1; ++ u8 STMSK :1; ++ u8 I0MSK :1; ++ u8 I1MSK :1; ++ u8 na1 :1; ++ u8 HABMSK :1; ++ u8 na2 :1; ++ } HCTLMSK_a1; ++ ++ struct { ++ u8 RESET :1; ++ u8 IDLE :1; ++ u8 STOP :1; ++ u8 RUN :1; ++ u8 HABAV :1; ++ u8 MEMAV :1; ++ u8 ALDONE :1; ++ u8 REIRQ :1; ++ } APSTAT1_a2; ++ ++ struct { ++ u8 RSTMSK :1; ++ u8 IMSK :1; ++ u8 SMSK :1; ++ u8 RMSK :1; ++ u8 HABMSK :1; ++ u8 MAVMSK :1; ++ u8 ALDMSK :1; ++ u8 REMSK :1; ++ } APMSK1_a3; ++ ++ u8 APSTAT2_a4; ++ u8 APMSK2_a5; ++ ++ struct { ++ u8 HABADR :7; ++ u8 na :1; ++ } HABADR_a6; ++ ++ u8 HABDATA_a7; ++ ++ struct { ++ u8 HABR :1; ++ u8 LDHABR :1; ++ u8 APMSK :1; ++ u8 HMSK :1; ++ u8 LDMSK :1; ++ u8 na :3; ++ } HABSTAT_a8; ++ ++ u8 MADRH_a9; ++ u8 MADRL_aa; ++ u8 MDATA_ab; ++ ++ struct { ++#define JDEC_WAIT_AT_RAM 0x7 ++#define JDEC_EEPROM_LOAD_WAIT 0x4 ++ u8 JDEC :3; ++ u8 na :5; ++ } JDEC_ca; ++ ++ struct { ++ u8 REV :4; ++ u8 LAYER :4; ++ } REVID_e0; ++ ++ struct { ++ u8 unk0 :1; ++ u8 CNTCTL :1; ++ u8 BITCNT :1; ++ u8 unk1 :1; ++ u8 RESYNC :1; ++ u8 unk2 :3; ++ } BERCTL_fa; ++ ++ struct { ++ u8 CSEL0 :1; ++ u8 CLKED0 :1; ++ u8 CSEL1 :1; ++ u8 CLKED1 :1; ++ u8 CLKLEV :1; ++ u8 SPIVAR :1; ++ u8 na :2; ++ } TUNSET_fc; ++ ++ struct { ++ u8 CLK :1; ++ u8 DATA :1; ++ u8 CS0 :1; ++ u8 CS1 :1; ++ u8 AGCSEL :1; ++ u8 na0 :1; ++ u8 TUNSEL :1; ++ u8 na1 :1; ++ } TUNCTL_fd; ++ ++ u8 TUNSEL0_fe; ++ u8 TUNSEL1_ff; ++ ++} bcm3510_register_value; ++ ++/* HAB commands */ ++ ++/* version */ ++#define CMD_GET_VERSION_INFO 0x3D ++#define MSGID_GET_VERSION_INFO 0x15 ++struct bcm3510_hab_cmd_get_version_info { ++ u8 microcode_version; ++ u8 script_version; ++ u8 config_version; ++ u8 demod_version; ++} PACKED; ++ ++#define BCM3510_DEF_MICROCODE_VERSION 0x0E ++#define BCM3510_DEF_SCRIPT_VERSION 0x06 ++#define BCM3510_DEF_CONFIG_VERSION 0x01 ++#define BCM3510_DEF_DEMOD_VERSION 0xB1 ++ ++/* acquire */ ++#define CMD_ACQUIRE 0x38 ++ ++#define MSGID_EXT_TUNER_ACQUIRE 0x0A ++struct bcm3510_hab_cmd_ext_acquire { ++ struct { ++ u8 MODE :4; ++ u8 BW :1; ++ u8 FA :1; ++ u8 NTSCSWEEP :1; ++ u8 OFFSET :1; ++ } PACKED ACQUIRE0; /* control_byte */ ++ ++ struct { ++ u8 IF_FREQ :3; ++ u8 zero0 :1; ++ u8 SYM_RATE :3; ++ u8 zero1 :1; ++ } PACKED ACQUIRE1; /* sym_if */ ++ ++ u8 IF_OFFSET0; /* IF_Offset_10hz */ ++ u8 IF_OFFSET1; ++ u8 SYM_OFFSET0; /* SymbolRateOffset */ ++ u8 SYM_OFFSET1; ++ u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ ++ u8 NTSC_OFFSET1; ++} PACKED; ++ ++#define MSGID_INT_TUNER_ACQUIRE 0x0B ++struct bcm3510_hab_cmd_int_acquire { ++ struct { ++ u8 MODE :4; ++ u8 BW :1; ++ u8 FA :1; ++ u8 NTSCSWEEP :1; ++ u8 OFFSET :1; ++ } PACKED ACQUIRE0; /* control_byte */ ++ ++ struct { ++ u8 IF_FREQ :3; ++ u8 zero0 :1; ++ u8 SYM_RATE :3; ++ u8 zero1 :1; ++ } PACKED ACQUIRE1; /* sym_if */ ++ ++ u8 TUNER_FREQ0; ++ u8 TUNER_FREQ1; ++ u8 TUNER_FREQ2; ++ u8 TUNER_FREQ3; ++ u8 IF_OFFSET0; /* IF_Offset_10hz */ ++ u8 IF_OFFSET1; ++ u8 SYM_OFFSET0; /* SymbolRateOffset */ ++ u8 SYM_OFFSET1; ++ u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ ++ u8 NTSC_OFFSET1; ++} PACKED; ++ ++/* modes */ ++#define BCM3510_QAM16 = 0x01 ++#define BCM3510_QAM32 = 0x02 ++#define BCM3510_QAM64 = 0x03 ++#define BCM3510_QAM128 = 0x04 ++#define BCM3510_QAM256 = 0x05 ++#define BCM3510_8VSB = 0x0B ++#define BCM3510_16VSB = 0x0D ++ ++/* IF_FREQS */ ++#define BCM3510_IF_TERRESTRIAL 0x0 ++#define BCM3510_IF_CABLE 0x1 ++#define BCM3510_IF_USE_CMD 0x7 ++ ++/* SYM_RATE */ ++#define BCM3510_SR_8VSB 0x0 /* 5381119 s/sec */ ++#define BCM3510_SR_256QAM 0x1 /* 5360537 s/sec */ ++#define BCM3510_SR_16QAM 0x2 /* 5056971 s/sec */ ++#define BCM3510_SR_MISC 0x3 /* 5000000 s/sec */ ++#define BCM3510_SR_USE_CMD 0x7 ++ ++/* special symbol rate */ ++#define CMD_SET_VALUE_NOT_LISTED 0x2d ++#define MSGID_SET_SYMBOL_RATE_NOT_LISTED 0x0c ++struct bcm3510_hab_cmd_set_sr_not_listed { ++ u8 HOST_SYM_RATE0; ++ u8 HOST_SYM_RATE1; ++ u8 HOST_SYM_RATE2; ++ u8 HOST_SYM_RATE3; ++} PACKED; ++ ++/* special IF */ ++#define MSGID_SET_IF_FREQ_NOT_LISTED 0x0d ++struct bcm3510_hab_cmd_set_if_freq_not_listed { ++ u8 HOST_IF_FREQ0; ++ u8 HOST_IF_FREQ1; ++ u8 HOST_IF_FREQ2; ++ u8 HOST_IF_FREQ3; ++} PACKED; ++ ++/* auto reacquire */ ++#define CMD_AUTO_PARAM 0x2a ++#define MSGID_AUTO_REACQUIRE 0x0e ++struct bcm3510_hab_cmd_auto_reacquire { ++ u8 ACQ :1; /* on/off*/ ++ u8 unused :7; ++} PACKED; ++ ++#define MSGID_SET_RF_AGC_SEL 0x12 ++struct bcm3510_hab_cmd_set_agc { ++ u8 LVL :1; ++ u8 unused :6; ++ u8 SEL :1; ++} PACKED; ++ ++#define MSGID_SET_AUTO_INVERSION 0x14 ++struct bcm3510_hab_cmd_auto_inversion { ++ u8 AI :1; ++ u8 unused :7; ++} PACKED; ++ ++ ++/* bert control */ ++#define CMD_STATE_CONTROL 0x12 ++#define MSGID_BERT_CONTROL 0x0e ++#define MSGID_BERT_SET 0xfa ++struct bcm3510_hab_cmd_bert_control { ++ u8 BE :1; ++ u8 unused :7; ++} PACKED; ++ ++#define MSGID_TRI_STATE 0x2e ++struct bcm3510_hab_cmd_tri_state { ++ u8 RE :1; /* a/d ram port pins */ ++ u8 PE :1; /* baud clock pin */ ++ u8 AC :1; /* a/d clock pin */ ++ u8 BE :1; /* baud clock pin */ ++ u8 unused :4; ++} PACKED; ++ ++ ++/* tune */ ++#define CMD_TUNE 0x38 ++#define MSGID_TUNE 0x16 ++struct bcm3510_hab_cmd_tune_ctrl_data_pair { ++ struct { ++#define BITS_8 0x07 ++#define BITS_7 0x06 ++#define BITS_6 0x05 ++#define BITS_5 0x04 ++#define BITS_4 0x03 ++#define BITS_3 0x02 ++#define BITS_2 0x01 ++#define BITS_1 0x00 ++ u8 size :3; ++ u8 unk :2; ++ u8 clk_off :1; ++ u8 cs0 :1; ++ u8 cs1 :1; ++ ++ } PACKED ctrl; ++ ++ u8 data; ++} PACKED; ++ ++struct bcm3510_hab_cmd_tune { ++ u8 length; ++ u8 clock_width; ++ u8 misc; ++ u8 TUNCTL_state; ++ ++ struct bcm3510_hab_cmd_tune_ctrl_data_pair ctl_dat[16]; ++} PACKED; ++ ++#define CMD_STATUS 0x38 ++#define MSGID_STATUS1 0x08 ++struct bcm3510_hab_cmd_status1 { ++ struct { ++ u8 EQ_MODE :4; ++ u8 reserved :2; ++ u8 QRE :1; /* if QSE and the spectrum is inversed */ ++ u8 QSE :1; /* automatic spectral inversion */ ++ } PACKED STATUS0; ++ ++ struct { ++ u8 RECEIVER_LOCK :1; ++ u8 FEC_LOCK :1; ++ u8 OUT_PLL_LOCK :1; ++ u8 reserved :5; ++ } PACKED STATUS1; ++ ++ struct { ++ u8 reserved :2; ++ u8 BW :1; ++ u8 NTE :1; /* NTSC filter sweep enabled */ ++ u8 AQI :1; /* currently acquiring */ ++ u8 FA :1; /* fast acquisition */ ++ u8 ARI :1; /* auto reacquire */ ++ u8 TI :1; /* programming the tuner */ ++ } PACKED STATUS2; ++ u8 STATUS3; ++ u8 SNR_EST0; ++ u8 SNR_EST1; ++ u8 TUNER_FREQ0; ++ u8 TUNER_FREQ1; ++ u8 TUNER_FREQ2; ++ u8 TUNER_FREQ3; ++ u8 SYM_RATE0; ++ u8 SYM_RATE1; ++ u8 SYM_RATE2; ++ u8 SYM_RATE3; ++ u8 SYM_OFFSET0; ++ u8 SYM_OFFSET1; ++ u8 SYM_ERROR0; ++ u8 SYM_ERROR1; ++ u8 IF_FREQ0; ++ u8 IF_FREQ1; ++ u8 IF_FREQ2; ++ u8 IF_FREQ3; ++ u8 IF_OFFSET0; ++ u8 IF_OFFSET1; ++ u8 IF_ERROR0; ++ u8 IF_ERROR1; ++ u8 NTSC_FILTER0; ++ u8 NTSC_FILTER1; ++ u8 NTSC_FILTER2; ++ u8 NTSC_FILTER3; ++ u8 NTSC_OFFSET0; ++ u8 NTSC_OFFSET1; ++ u8 NTSC_ERROR0; ++ u8 NTSC_ERROR1; ++ u8 INT_AGC_LEVEL0; ++ u8 INT_AGC_LEVEL1; ++ u8 EXT_AGC_LEVEL0; ++ u8 EXT_AGC_LEVEL1; ++} PACKED; ++ ++#define MSGID_STATUS2 0x14 ++struct bcm3510_hab_cmd_status2 { ++ struct { ++ u8 EQ_MODE :4; ++ u8 reserved :2; ++ u8 QRE :1; ++ u8 QSR :1; ++ } PACKED STATUS0; ++ struct { ++ u8 RL :1; ++ u8 FL :1; ++ u8 OL :1; ++ u8 reserved :5; ++ } PACKED STATUS1; ++ u8 SYMBOL_RATE0; ++ u8 SYMBOL_RATE1; ++ u8 SYMBOL_RATE2; ++ u8 SYMBOL_RATE3; ++ u8 LDCERC0; ++ u8 LDCERC1; ++ u8 LDCERC2; ++ u8 LDCERC3; ++ u8 LDUERC0; ++ u8 LDUERC1; ++ u8 LDUERC2; ++ u8 LDUERC3; ++ u8 LDBER0; ++ u8 LDBER1; ++ u8 LDBER2; ++ u8 LDBER3; ++ struct { ++ u8 MODE_TYPE :4; /* acquire mode 0 */ ++ u8 reservd :4; ++ } MODE_TYPE; ++ u8 SNR_EST0; ++ u8 SNR_EST1; ++ u8 SIGNAL; ++} PACKED; ++ ++#define CMD_SET_RF_BW_NOT_LISTED 0x3f ++#define MSGID_SET_RF_BW_NOT_LISTED 0x11 ++/* TODO */ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/bsbe1-d01a.h b/drivers/media/dvb-frontends/bsbe1-d01a.h +new file mode 100644 +index 0000000..7ed3c42 +--- /dev/null ++++ b/drivers/media/dvb-frontends/bsbe1-d01a.h +@@ -0,0 +1,146 @@ ++/* ++ * bsbe1-d01a.h - ALPS BSBE1-D01A tuner support ++ * ++ * Copyright (C) 2011 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef BSBE1_D01A_H ++#define BSBE1_D01A_H ++ ++#include "stb6000.h" ++#include "stv0288.h" ++ ++static u8 stv0288_bsbe1_d01a_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x20, ++ 0x09, 0x0, ++ 0x0a, 0x4, ++ 0x0b, 0x0, ++ 0x0c, 0x0, ++ 0x0d, 0x0, ++ 0x0e, 0xd4, ++ 0x0f, 0x30, ++ 0x11, 0x80, ++ 0x12, 0x03, ++ 0x13, 0x48, ++ 0x14, 0x84, ++ 0x15, 0x45, ++ 0x16, 0xb7, ++ 0x17, 0x9c, ++ 0x18, 0x0, ++ 0x19, 0xa6, ++ 0x1a, 0x88, ++ 0x1b, 0x8f, ++ 0x1c, 0xf0, ++ 0x20, 0x0b, ++ 0x21, 0x54, ++ 0x22, 0x0, ++ 0x23, 0x0, ++ 0x2b, 0xff, ++ 0x2c, 0xf7, ++ 0x30, 0x0, ++ 0x31, 0x1e, ++ 0x32, 0x14, ++ 0x33, 0x0f, ++ 0x34, 0x09, ++ 0x35, 0x0c, ++ 0x36, 0x05, ++ 0x37, 0x2f, ++ 0x38, 0x16, ++ 0x39, 0xbd, ++ 0x3a, 0x03, ++ 0x3b, 0x13, ++ 0x3c, 0x11, ++ 0x3d, 0x30, ++ 0x40, 0x63, ++ 0x41, 0x04, ++ 0x42, 0x60, ++ 0x43, 0x00, ++ 0x44, 0x00, ++ 0x45, 0x00, ++ 0x46, 0x00, ++ 0x47, 0x00, ++ 0x4a, 0x00, ++ 0x50, 0x10, ++ 0x51, 0x36, ++ 0x52, 0x09, ++ 0x53, 0x94, ++ 0x54, 0x62, ++ 0x55, 0x29, ++ 0x56, 0x64, ++ 0x57, 0x2b, ++ 0x58, 0x54, ++ 0x59, 0x86, ++ 0x5a, 0x0, ++ 0x5b, 0x9b, ++ 0x5c, 0x08, ++ 0x5d, 0x7f, ++ 0x5e, 0x0, ++ 0x5f, 0xff, ++ 0x70, 0x0, ++ 0x71, 0x0, ++ 0x72, 0x0, ++ 0x74, 0x0, ++ 0x75, 0x0, ++ 0x76, 0x0, ++ 0x81, 0x0, ++ 0x82, 0x3f, ++ 0x83, 0x3f, ++ 0x84, 0x0, ++ 0x85, 0x0, ++ 0x88, 0x0, ++ 0x89, 0x0, ++ 0x8a, 0x0, ++ 0x8b, 0x0, ++ 0x8c, 0x0, ++ 0x90, 0x0, ++ 0x91, 0x0, ++ 0x92, 0x0, ++ 0x93, 0x0, ++ 0x94, 0x1c, ++ 0x97, 0x0, ++ 0xa0, 0x48, ++ 0xa1, 0x0, ++ 0xb0, 0xb8, ++ 0xb1, 0x3a, ++ 0xb2, 0x10, ++ 0xb3, 0x82, ++ 0xb4, 0x80, ++ 0xb5, 0x82, ++ 0xb6, 0x82, ++ 0xb7, 0x82, ++ 0xb8, 0x20, ++ 0xb9, 0x0, ++ 0xf0, 0x0, ++ 0xf1, 0x0, ++ 0xf2, 0xc0, ++ 0xff, 0xff, ++}; ++ ++static struct stv0288_config stv0288_bsbe1_d01a_config = { ++ .demod_address = 0x68, ++ .min_delay_ms = 100, ++ .inittab = stv0288_bsbe1_d01a_inittab, ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/bsbe1.h b/drivers/media/dvb-frontends/bsbe1.h +new file mode 100644 +index 0000000..53e4d0d +--- /dev/null ++++ b/drivers/media/dvb-frontends/bsbe1.h +@@ -0,0 +1,106 @@ ++/* ++ * bsbe1.h - ALPS BSBE1 tuner support ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef BSBE1_H ++#define BSBE1_H ++ ++static u8 alps_bsbe1_inittab[] = { ++ 0x01, 0x15, /* XTAL = 4MHz, VCO = 352 MHz */ ++ 0x02, 0x30, /* MCLK = 88 MHz */ ++ 0x03, 0x00, /* ACR output 0 */ ++ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ ++ 0x05, 0x05, /* I2CT = 0, SCLT = 1, SDAT = 1 */ ++ 0x06, 0x00, /* DAC output 0 */ ++ 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ ++ 0x09, 0x00, /* FIFO */ ++ 0x0c, 0x51, /* OP1/OP0 normal, val = 1 (LNB power on) */ ++ 0x0d, 0x82, /* DC offset compensation = on, beta_agc1 = 2 */ ++ 0x0f, 0x92, /* AGC1R */ ++ 0x10, 0x34, /* AGC2O */ ++ 0x11, 0x84, /* TLSR */ ++ 0x12, 0xb9, /* CFD */ ++ 0x15, 0xc9, /* lock detector threshold */ ++ 0x28, 0x00, /* out imp: normal, type: parallel, FEC mode: QPSK */ ++ 0x33, 0xfc, /* RS control */ ++ 0x34, 0x93, /* count viterbi bit errors per 2E18 bytes */ ++ 0xff, 0xff ++}; ++ ++ ++static int alps_bsbe1_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 int alps_bsbe1_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ int ret; ++ u8 data[4]; ++ u32 div; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ struct i2c_adapter *i2c = fe->tuner_priv; ++ ++ if ((p->frequency < 950000) || (p->frequency > 2150000)) ++ return -EINVAL; ++ ++ div = p->frequency / 1000; ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x80 | ((div & 0x18000) >> 10) | 0x1; ++ data[3] = 0xe0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = i2c_transfer(i2c, &msg, 1); ++ return (ret != 1) ? -EIO : 0; ++} ++ ++static struct stv0299_config alps_bsbe1_config = { ++ .demod_address = 0x68, ++ .inittab = alps_bsbe1_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .skip_reinit = 0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = alps_bsbe1_set_symbol_rate, ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/bsru6.h b/drivers/media/dvb-frontends/bsru6.h +new file mode 100644 +index 0000000..c2a578e +--- /dev/null ++++ b/drivers/media/dvb-frontends/bsru6.h +@@ -0,0 +1,143 @@ ++/* ++ * bsru6.h - ALPS BSRU6 tuner support (moved from budget-ci.c) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef BSRU6_H ++#define BSRU6_H ++ ++static u8 alps_bsru6_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ ++ 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ ++ 0x06, 0x40, /* DAC not used, set to high impendance mode */ ++ 0x07, 0x00, /* DAC LSB */ ++ 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ ++ 0x09, 0x00, /* FIFO */ ++ 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ ++ 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ ++ 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ ++ 0x10, 0x3f, // AGC2 0x3d ++ 0x11, 0x84, ++ 0x12, 0xb9, ++ 0x15, 0xc9, // lock detector threshold ++ 0x16, 0x00, ++ 0x17, 0x00, ++ 0x18, 0x00, ++ 0x19, 0x00, ++ 0x1a, 0x00, ++ 0x1f, 0x50, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 ++ 0x29, 0x1e, // 1/2 threshold ++ 0x2a, 0x14, // 2/3 threshold ++ 0x2b, 0x0f, // 3/4 threshold ++ 0x2c, 0x09, // 5/6 threshold ++ 0x2d, 0x05, // 7/8 threshold ++ 0x2e, 0x01, ++ 0x31, 0x1f, // test all FECs ++ 0x32, 0x19, // viterbi and synchro search ++ 0x33, 0xfc, // rs control ++ 0x34, 0x93, // error control ++ 0x0f, 0x52, ++ 0xff, 0xff ++}; ++ ++static int alps_bsru6_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 int alps_bsru6_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ u8 buf[4]; ++ u32 div; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; ++ struct i2c_adapter *i2c = fe->tuner_priv; ++ ++ if ((p->frequency < 950000) || (p->frequency > 2150000)) ++ return -EINVAL; ++ ++ div = (p->frequency + (125 - 1)) / 125; /* round correctly */ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; ++ buf[3] = 0xC4; ++ ++ if (p->frequency > 1530000) ++ buf[3] = 0xc0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(i2c, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct stv0299_config alps_bsru6_config = { ++ .demod_address = 0x68, ++ .inittab = alps_bsru6_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP1, ++ .min_delay_ms = 100, ++ .set_symbol_rate = alps_bsru6_set_symbol_rate, ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c +new file mode 100644 +index 0000000..3d399d9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx22700.c +@@ -0,0 +1,443 @@ ++/* ++ Conexant cx22700 DVB OFDM demodulator driver ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ Holger Waechtler ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "cx22700.h" ++ ++ ++struct cx22700_state { ++ ++ struct i2c_adapter* i2c; ++ ++ const struct cx22700_config* config; ++ ++ struct dvb_frontend frontend; ++}; ++ ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "cx22700: " args); \ ++ } while (0) ++ ++static u8 init_tab [] = { ++ 0x04, 0x10, ++ 0x05, 0x09, ++ 0x06, 0x00, ++ 0x08, 0x04, ++ 0x09, 0x00, ++ 0x0a, 0x01, ++ 0x15, 0x40, ++ 0x16, 0x10, ++ 0x17, 0x87, ++ 0x18, 0x17, ++ 0x1a, 0x10, ++ 0x25, 0x04, ++ 0x2e, 0x00, ++ 0x39, 0x00, ++ 0x3a, 0x04, ++ 0x45, 0x08, ++ 0x46, 0x02, ++ 0x47, 0x05, ++}; ++ ++ ++static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf [] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ ++ dprintk ("%s\n", __func__); ++ ++ ret = i2c_transfer (state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", ++ __func__, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int cx22700_readreg (struct cx22700_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ dprintk ("%s\n", __func__); ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ ++ if (ret != 2) return -EIO; ++ ++ return b1[0]; ++} ++ ++static int cx22700_set_inversion (struct cx22700_state* state, int inversion) ++{ ++ u8 val; ++ ++ dprintk ("%s\n", __func__); ++ ++ switch (inversion) { ++ case INVERSION_AUTO: ++ return -EOPNOTSUPP; ++ case INVERSION_ON: ++ val = cx22700_readreg (state, 0x09); ++ return cx22700_writereg (state, 0x09, val | 0x01); ++ case INVERSION_OFF: ++ val = cx22700_readreg (state, 0x09); ++ return cx22700_writereg (state, 0x09, val & 0xfe); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int cx22700_set_tps(struct cx22700_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ static const u8 qam_tab [4] = { 0, 1, 0, 2 }; ++ static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; ++ u8 val; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) ++ return -EINVAL; ++ ++ if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) ++ return -EINVAL; ++ ++ if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) ++ return -EINVAL; ++ ++ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || ++ p->guard_interval > GUARD_INTERVAL_1_4) ++ return -EINVAL; ++ ++ if (p->transmission_mode != TRANSMISSION_MODE_2K && ++ p->transmission_mode != TRANSMISSION_MODE_8K) ++ return -EINVAL; ++ ++ if (p->modulation != QPSK && ++ p->modulation != QAM_16 && ++ p->modulation != QAM_64) ++ return -EINVAL; ++ ++ if ((int)p->hierarchy < HIERARCHY_NONE || ++ p->hierarchy > HIERARCHY_4) ++ return -EINVAL; ++ ++ if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000) ++ return -EINVAL; ++ ++ if (p->bandwidth_hz == 7000000) ++ cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); ++ else ++ cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); ++ ++ val = qam_tab[p->modulation - QPSK]; ++ val |= p->hierarchy - HIERARCHY_NONE; ++ ++ cx22700_writereg (state, 0x04, val); ++ ++ val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; ++ val |= fec_tab[p->code_rate_LP - FEC_1_2]; ++ ++ cx22700_writereg (state, 0x05, val); ++ ++ val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; ++ val |= p->transmission_mode - TRANSMISSION_MODE_2K; ++ ++ cx22700_writereg (state, 0x06, val); ++ ++ cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ ++ cx22700_writereg (state, 0x08, 0x04); /* restart acquisition */ ++ ++ return 0; ++} ++ ++static int cx22700_get_tps(struct cx22700_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; ++ static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, ++ FEC_5_6, FEC_7_8 }; ++ u8 val; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ ++ return -EAGAIN; ++ ++ val = cx22700_readreg (state, 0x01); ++ ++ if ((val & 0x7) > 4) ++ p->hierarchy = HIERARCHY_AUTO; ++ else ++ p->hierarchy = HIERARCHY_NONE + (val & 0x7); ++ ++ if (((val >> 3) & 0x3) > 2) ++ p->modulation = QAM_AUTO; ++ else ++ p->modulation = qam_tab[(val >> 3) & 0x3]; ++ ++ val = cx22700_readreg (state, 0x02); ++ ++ if (((val >> 3) & 0x07) > 4) ++ p->code_rate_HP = FEC_AUTO; ++ else ++ p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; ++ ++ if ((val & 0x07) > 4) ++ p->code_rate_LP = FEC_AUTO; ++ else ++ p->code_rate_LP = fec_tab[val & 0x07]; ++ ++ val = cx22700_readreg (state, 0x03); ++ ++ p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); ++ p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); ++ ++ return 0; ++} ++ ++static int cx22700_init (struct dvb_frontend* fe) ++ ++{ struct cx22700_state* state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("cx22700_init: init chip\n"); ++ ++ cx22700_writereg (state, 0x00, 0x02); /* soft reset */ ++ cx22700_writereg (state, 0x00, 0x00); ++ ++ msleep(10); ++ ++ for (i=0; idemodulator_priv; ++ ++ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) ++ | (cx22700_readreg (state, 0x0e) << 1); ++ u8 sync = cx22700_readreg (state, 0x07); ++ ++ *status = 0; ++ ++ if (rs_ber < 0xff00) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 0x20) ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 0x10) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 0x10) ++ *status |= FE_HAS_SYNC; ++ ++ if (*status == 0x0f) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ *ber = cx22700_readreg (state, 0x0c) & 0x7f; ++ cx22700_writereg (state, 0x0c, 0x00); ++ ++ return 0; ++} ++ ++static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) ++ | (cx22700_readreg (state, 0x0e) << 1); ++ *signal_strength = ~rs_ber; ++ ++ return 0; ++} ++ ++static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) ++ | (cx22700_readreg (state, 0x0e) << 1); ++ *snr = ~rs_ber; ++ ++ return 0; ++} ++ ++static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ *ucblocks = cx22700_readreg (state, 0x0f); ++ cx22700_writereg (state, 0x0f, 0x00); ++ ++ return 0; ++} ++ ++static int cx22700_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ ++ cx22700_writereg (state, 0x00, 0x00); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ cx22700_set_inversion(state, c->inversion); ++ cx22700_set_tps(state, c); ++ cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ ++ cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ ++ ++ return 0; ++} ++ ++static int cx22700_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx22700_state* state = fe->demodulator_priv; ++ u8 reg09 = cx22700_readreg (state, 0x09); ++ ++ c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; ++ return cx22700_get_tps(state, c); ++} ++ ++static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return cx22700_writereg(state, 0x0a, 0x00); ++ } else { ++ return cx22700_writereg(state, 0x0a, 0x01); ++ } ++} ++ ++static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 150; ++ fesettings->step_size = 166667; ++ fesettings->max_drift = 166667*2; ++ return 0; ++} ++ ++static void cx22700_release(struct dvb_frontend* fe) ++{ ++ struct cx22700_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops cx22700_ops; ++ ++struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct cx22700_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (cx22700_readreg(state, 0x07) < 0) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops cx22700_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Conexant CX22700 DVB-T", ++ .frequency_min = 470000000, ++ .frequency_max = 860000000, ++ .frequency_stepsize = 166667, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | ++ FE_CAN_RECOVER ++ }, ++ ++ .release = cx22700_release, ++ ++ .init = cx22700_init, ++ .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, ++ ++ .set_frontend = cx22700_set_frontend, ++ .get_frontend = cx22700_get_frontend, ++ .get_tune_settings = cx22700_get_tune_settings, ++ ++ .read_status = cx22700_read_status, ++ .read_ber = cx22700_read_ber, ++ .read_signal_strength = cx22700_read_signal_strength, ++ .read_snr = cx22700_read_snr, ++ .read_ucblocks = cx22700_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Holger Waechtler"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(cx22700_attach); +diff --git a/drivers/media/dvb-frontends/cx22700.h b/drivers/media/dvb-frontends/cx22700.h +new file mode 100644 +index 0000000..4757a93 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx22700.h +@@ -0,0 +1,46 @@ ++/* ++ Conexant CX22700 DVB OFDM demodulator driver ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ Holger Waechtler ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef CX22700_H ++#define CX22700_H ++ ++#include ++ ++struct cx22700_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++#if defined(CONFIG_DVB_CX22700) || (defined(CONFIG_DVB_CX22700_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_CX22700 ++ ++#endif // CX22700_H +diff --git a/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c +new file mode 100644 +index 0000000..edc8eaf +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx22702.c +@@ -0,0 +1,653 @@ ++/* ++ Conexant 22702 DVB OFDM demodulator driver ++ ++ based on: ++ Alps TDMB7 DVB OFDM demodulator driver ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ Holger Waechtler ++ ++ Copyright (C) 2004 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "cx22702.h" ++ ++struct cx22702_state { ++ ++ struct i2c_adapter *i2c; ++ ++ /* configuration settings */ ++ const struct cx22702_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ /* previous uncorrected block counter */ ++ u8 prevUCBlocks; ++}; ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++#define dprintk if (debug) printk ++ ++/* Register values to initialise the demod */ ++static const u8 init_tab[] = { ++ 0x00, 0x00, /* Stop acquisition */ ++ 0x0B, 0x06, ++ 0x09, 0x01, ++ 0x0D, 0x41, ++ 0x16, 0x32, ++ 0x20, 0x0A, ++ 0x21, 0x17, ++ 0x24, 0x3e, ++ 0x26, 0xff, ++ 0x27, 0x10, ++ 0x28, 0x00, ++ 0x29, 0x00, ++ 0x2a, 0x10, ++ 0x2b, 0x00, ++ 0x2c, 0x10, ++ 0x2d, 0x00, ++ 0x48, 0xd4, ++ 0x49, 0x56, ++ 0x6b, 0x1e, ++ 0xc8, 0x02, ++ 0xf9, 0x00, ++ 0xfa, 0x00, ++ 0xfb, 0x00, ++ 0xfc, 0x00, ++ 0xfd, 0x00, ++}; ++ ++static int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, .flags = 0, ++ .buf = buf, .len = 2 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (unlikely(ret != 1)) { ++ printk(KERN_ERR ++ "%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", ++ __func__, reg, data, ret); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static u8 cx22702_readreg(struct cx22702_state *state, u8 reg) ++{ ++ int ret; ++ u8 data; ++ ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, ++ .buf = ®, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = &data, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (unlikely(ret != 2)) { ++ printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n", ++ __func__, reg, ret); ++ return 0; ++ } ++ ++ return data; ++} ++ ++static int cx22702_set_inversion(struct cx22702_state *state, int inversion) ++{ ++ u8 val; ++ ++ val = cx22702_readreg(state, 0x0C); ++ switch (inversion) { ++ case INVERSION_AUTO: ++ return -EOPNOTSUPP; ++ case INVERSION_ON: ++ val |= 0x01; ++ break; ++ case INVERSION_OFF: ++ val &= 0xfe; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return cx22702_writereg(state, 0x0C, val); ++} ++ ++/* Retrieve the demod settings */ ++static int cx22702_get_tps(struct cx22702_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u8 val; ++ ++ /* Make sure the TPS regs are valid */ ++ if (!(cx22702_readreg(state, 0x0A) & 0x20)) ++ return -EAGAIN; ++ ++ val = cx22702_readreg(state, 0x01); ++ switch ((val & 0x18) >> 3) { ++ case 0: ++ p->modulation = QPSK; ++ break; ++ case 1: ++ p->modulation = QAM_16; ++ break; ++ case 2: ++ p->modulation = QAM_64; ++ break; ++ } ++ switch (val & 0x07) { ++ case 0: ++ p->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ p->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ p->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ p->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ ++ val = cx22702_readreg(state, 0x02); ++ switch ((val & 0x38) >> 3) { ++ case 0: ++ p->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_HP = FEC_7_8; ++ break; ++ } ++ switch (val & 0x07) { ++ case 0: ++ p->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ val = cx22702_readreg(state, 0x03); ++ switch ((val & 0x0c) >> 2) { ++ case 0: ++ p->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ p->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ p->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ p->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ switch (val & 0x03) { ++ case 0: ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ p->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ val = cx22702_readreg(state, 0x0D); ++ if (enable) ++ val &= 0xfe; ++ else ++ val |= 0x01; ++ return cx22702_writereg(state, 0x0D, val); ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++static int cx22702_set_tps(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ u8 val; ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* set inversion */ ++ cx22702_set_inversion(state, p->inversion); ++ ++ /* set bandwidth */ ++ val = cx22702_readreg(state, 0x0C) & 0xcf; ++ switch (p->bandwidth_hz) { ++ case 6000000: ++ val |= 0x20; ++ break; ++ case 7000000: ++ val |= 0x10; ++ break; ++ case 8000000: ++ break; ++ default: ++ dprintk("%s: invalid bandwidth\n", __func__); ++ return -EINVAL; ++ } ++ cx22702_writereg(state, 0x0C, val); ++ ++ p->code_rate_LP = FEC_AUTO; /* temp hack as manual not working */ ++ ++ /* use auto configuration? */ ++ if ((p->hierarchy == HIERARCHY_AUTO) || ++ (p->modulation == QAM_AUTO) || ++ (p->code_rate_HP == FEC_AUTO) || ++ (p->code_rate_LP == FEC_AUTO) || ++ (p->guard_interval == GUARD_INTERVAL_AUTO) || ++ (p->transmission_mode == TRANSMISSION_MODE_AUTO)) { ++ ++ /* TPS Source - use hardware driven values */ ++ cx22702_writereg(state, 0x06, 0x10); ++ cx22702_writereg(state, 0x07, 0x9); ++ cx22702_writereg(state, 0x08, 0xC1); ++ cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) ++ & 0xfc); ++ cx22702_writereg(state, 0x0C, ++ (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); ++ cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */ ++ dprintk("%s: Autodetecting\n", __func__); ++ return 0; ++ } ++ ++ /* manually programmed values */ ++ switch (p->modulation) { /* mask 0x18 */ ++ case QPSK: ++ val = 0x00; ++ break; ++ case QAM_16: ++ val = 0x08; ++ break; ++ case QAM_64: ++ val = 0x10; ++ break; ++ default: ++ dprintk("%s: invalid modulation\n", __func__); ++ return -EINVAL; ++ } ++ switch (p->hierarchy) { /* mask 0x07 */ ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ val |= 0x01; ++ break; ++ case HIERARCHY_2: ++ val |= 0x02; ++ break; ++ case HIERARCHY_4: ++ val |= 0x03; ++ break; ++ default: ++ dprintk("%s: invalid hierarchy\n", __func__); ++ return -EINVAL; ++ } ++ cx22702_writereg(state, 0x06, val); ++ ++ switch (p->code_rate_HP) { /* mask 0x38 */ ++ case FEC_NONE: ++ case FEC_1_2: ++ val = 0x00; ++ break; ++ case FEC_2_3: ++ val = 0x08; ++ break; ++ case FEC_3_4: ++ val = 0x10; ++ break; ++ case FEC_5_6: ++ val = 0x18; ++ break; ++ case FEC_7_8: ++ val = 0x20; ++ break; ++ default: ++ dprintk("%s: invalid code_rate_HP\n", __func__); ++ return -EINVAL; ++ } ++ switch (p->code_rate_LP) { /* mask 0x07 */ ++ case FEC_NONE: ++ case FEC_1_2: ++ break; ++ case FEC_2_3: ++ val |= 0x01; ++ break; ++ case FEC_3_4: ++ val |= 0x02; ++ break; ++ case FEC_5_6: ++ val |= 0x03; ++ break; ++ case FEC_7_8: ++ val |= 0x04; ++ break; ++ default: ++ dprintk("%s: invalid code_rate_LP\n", __func__); ++ return -EINVAL; ++ } ++ cx22702_writereg(state, 0x07, val); ++ ++ switch (p->guard_interval) { /* mask 0x0c */ ++ case GUARD_INTERVAL_1_32: ++ val = 0x00; ++ break; ++ case GUARD_INTERVAL_1_16: ++ val = 0x04; ++ break; ++ case GUARD_INTERVAL_1_8: ++ val = 0x08; ++ break; ++ case GUARD_INTERVAL_1_4: ++ val = 0x0c; ++ break; ++ default: ++ dprintk("%s: invalid guard_interval\n", __func__); ++ return -EINVAL; ++ } ++ switch (p->transmission_mode) { /* mask 0x03 */ ++ case TRANSMISSION_MODE_2K: ++ break; ++ case TRANSMISSION_MODE_8K: ++ val |= 0x1; ++ break; ++ default: ++ dprintk("%s: invalid transmission_mode\n", __func__); ++ return -EINVAL; ++ } ++ cx22702_writereg(state, 0x08, val); ++ cx22702_writereg(state, 0x0B, ++ (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02); ++ cx22702_writereg(state, 0x0C, ++ (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); ++ ++ /* Begin channel acquisition */ ++ cx22702_writereg(state, 0x00, 0x01); ++ ++ return 0; ++} ++ ++/* Reset the demod hardware and reset all of the configuration registers ++ to a default state. */ ++static int cx22702_init(struct dvb_frontend *fe) ++{ ++ int i; ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ cx22702_writereg(state, 0x00, 0x02); ++ ++ msleep(10); ++ ++ for (i = 0; i < ARRAY_SIZE(init_tab); i += 2) ++ cx22702_writereg(state, init_tab[i], init_tab[i + 1]); ++ ++ cx22702_writereg(state, 0xf8, (state->config->output_mode << 1) ++ & 0x02); ++ ++ cx22702_i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ u8 reg0A; ++ u8 reg23; ++ ++ *status = 0; ++ ++ reg0A = cx22702_readreg(state, 0x0A); ++ reg23 = cx22702_readreg(state, 0x23); ++ ++ dprintk("%s: status demod=0x%02x agc=0x%02x\n" ++ , __func__, reg0A, reg23); ++ ++ if (reg0A & 0x10) { ++ *status |= FE_HAS_LOCK; ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ } ++ ++ if (reg0A & 0x20) ++ *status |= FE_HAS_CARRIER; ++ ++ if (reg23 < 0xf0) ++ *status |= FE_HAS_SIGNAL; ++ ++ return 0; ++} ++ ++static int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ if (cx22702_readreg(state, 0xE4) & 0x02) { ++ /* Realtime statistics */ ++ *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 ++ | (cx22702_readreg(state, 0xDF) & 0x7F); ++ } else { ++ /* Averagtine statistics */ ++ *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 ++ | cx22702_readreg(state, 0xDF); ++ } ++ ++ return 0; ++} ++ ++static int cx22702_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ u8 reg23; ++ ++ /* ++ * Experience suggests that the strength signal register works as ++ * follows: ++ * - In the absence of signal, value is 0xff. ++ * - In the presence of a weak signal, bit 7 is set, not sure what ++ * the lower 7 bits mean. ++ * - In the presence of a strong signal, the register holds a 7-bit ++ * value (bit 7 is cleared), with greater values standing for ++ * weaker signals. ++ */ ++ reg23 = cx22702_readreg(state, 0x23); ++ if (reg23 & 0x80) { ++ *signal_strength = 0; ++ } else { ++ reg23 = ~reg23 & 0x7f; ++ /* Scale to 16 bit */ ++ *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5); ++ } ++ ++ return 0; ++} ++ ++static int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ u16 rs_ber; ++ if (cx22702_readreg(state, 0xE4) & 0x02) { ++ /* Realtime statistics */ ++ rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 ++ | (cx22702_readreg(state, 0xDF) & 0x7F); ++ } else { ++ /* Averagine statistics */ ++ rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8 ++ | cx22702_readreg(state, 0xDF); ++ } ++ *snr = ~rs_ber; ++ ++ return 0; ++} ++ ++static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ u8 _ucblocks; ++ ++ /* RS Uncorrectable Packet Count then reset */ ++ _ucblocks = cx22702_readreg(state, 0xE3); ++ if (state->prevUCBlocks < _ucblocks) ++ *ucblocks = (_ucblocks - state->prevUCBlocks); ++ else ++ *ucblocks = state->prevUCBlocks - _ucblocks; ++ state->prevUCBlocks = _ucblocks; ++ ++ return 0; ++} ++ ++static int cx22702_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx22702_state *state = fe->demodulator_priv; ++ ++ u8 reg0C = cx22702_readreg(state, 0x0C); ++ ++ c->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; ++ return cx22702_get_tps(state, c); ++} ++ ++static int cx22702_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void cx22702_release(struct dvb_frontend *fe) ++{ ++ struct cx22702_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static const struct dvb_frontend_ops cx22702_ops; ++ ++struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct cx22702_state *state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (cx22702_readreg(state, 0x1f) != 0x3) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &cx22702_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(cx22702_attach); ++ ++static const struct dvb_frontend_ops cx22702_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Conexant CX22702 DVB-T", ++ .frequency_min = 177000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER ++ }, ++ ++ .release = cx22702_release, ++ ++ .init = cx22702_init, ++ .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, ++ ++ .set_frontend = cx22702_set_tps, ++ .get_frontend = cx22702_get_frontend, ++ .get_tune_settings = cx22702_get_tune_settings, ++ ++ .read_status = cx22702_read_status, ++ .read_ber = cx22702_read_ber, ++ .read_signal_strength = cx22702_read_signal_strength, ++ .read_snr = cx22702_read_snr, ++ .read_ucblocks = cx22702_read_ucblocks, ++}; ++ ++MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/cx22702.h b/drivers/media/dvb-frontends/cx22702.h +new file mode 100644 +index 0000000..f154e1f +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx22702.h +@@ -0,0 +1,58 @@ ++/* ++ Conexant 22702 DVB OFDM demodulator driver ++ ++ based on: ++ Alps TDMB7 DVB OFDM demodulator driver ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ Holger Waechtler ++ ++ Copyright (C) 2004 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef CX22702_H ++#define CX22702_H ++ ++#include ++ ++struct cx22702_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* serial/parallel output */ ++#define CX22702_PARALLEL_OUTPUT 0 ++#define CX22702_SERIAL_OUTPUT 1 ++ u8 output_mode; ++}; ++ ++#if defined(CONFIG_DVB_CX22702) || (defined(CONFIG_DVB_CX22702_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *cx22702_attach( ++ const struct cx22702_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *cx22702_attach( ++ const struct cx22702_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c +new file mode 100644 +index 0000000..0cd6927 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24110.c +@@ -0,0 +1,666 @@ ++/* ++ cx24110 - Single Chip Satellite Channel Receiver driver module ++ ++ Copyright (C) 2002 Peter Hettkamp based on ++ work ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "cx24110.h" ++ ++ ++struct cx24110_state { ++ ++ struct i2c_adapter* i2c; ++ ++ const struct cx24110_config* config; ++ ++ struct dvb_frontend frontend; ++ ++ u32 lastber; ++ u32 lastbler; ++ u32 lastesn0; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "cx24110: " args); \ ++ } while (0) ++ ++static struct {u8 reg; u8 data;} cx24110_regdata[]= ++ /* Comments beginning with @ denote this value should ++ be the default */ ++ {{0x09,0x01}, /* SoftResetAll */ ++ {0x09,0x00}, /* release reset */ ++ {0x01,0xe8}, /* MSB of code rate 27.5MS/s */ ++ {0x02,0x17}, /* middle byte " */ ++ {0x03,0x29}, /* LSB " */ ++ {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */ ++ {0x06,0xa5}, /* @ PLL 60MHz */ ++ {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */ ++ {0x0a,0x00}, /* @ partial chip disables, do not set */ ++ {0x0b,0x01}, /* set output clock in gapped mode, start signal low ++ active for first byte */ ++ {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */ ++ {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */ ++ {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1 ++ to avoid starting the BER counter. Reset the ++ CRC test bit. Finite counting selected */ ++ {0x15,0xff}, /* @ size of the limited time window for RS BER ++ estimation. It is *256 RS blocks, this ++ gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */ ++ {0x16,0x00}, /* @ enable all RS output ports */ ++ {0x17,0x04}, /* @ time window allowed for the RS to sync */ ++ {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned ++ for automatically */ ++ /* leave the current code rate and normalization ++ registers as they are after reset... */ ++ {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting ++ only once */ ++ {0x23,0x18}, /* @ size of the limited time window for Viterbi BER ++ estimation. It is *65536 channel bits, i.e. ++ approx. 38ms at 27.5MS/s, rate 3/4 */ ++ {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */ ++ /* leave front-end AGC parameters at default values */ ++ /* leave decimation AGC parameters at default values */ ++ {0x35,0x40}, /* disable all interrupts. They are not connected anyway */ ++ {0x36,0xff}, /* clear all interrupt pending flags */ ++ {0x37,0x00}, /* @ fully enable AutoAcqq state machine */ ++ {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */ ++ /* leave the equalizer parameters on their default values */ ++ /* leave the final AGC parameters on their default values */ ++ {0x41,0x00}, /* @ MSB of front-end derotator frequency */ ++ {0x42,0x00}, /* @ middle bytes " */ ++ {0x43,0x00}, /* @ LSB " */ ++ /* leave the carrier tracking loop parameters on default */ ++ /* leave the bit timing loop parameters at default */ ++ {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */ ++ /* the cx24108 data sheet for symbol rates above 15MS/s */ ++ {0x57,0x00}, /* @ Filter sigma delta enabled, positive */ ++ {0x61,0x95}, /* GPIO pins 1-4 have special function */ ++ {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */ ++ {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */ ++ {0x64,0x20}, /* GPIO 6 is input, all others are outputs */ ++ {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */ ++ {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */ ++ {0x73,0x00}, /* @ disable several demod bypasses */ ++ {0x74,0x00}, /* @ " */ ++ {0x75,0x00} /* @ " */ ++ /* the remaining registers are for SEC */ ++ }; ++ ++ ++static int cx24110_writereg (struct cx24110_state* state, int reg, int data) ++{ ++ u8 buf [] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { ++ dprintk ("%s: writereg error (err == %i, reg == 0x%02x," ++ " data == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int cx24110_readreg (struct cx24110_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) return ret; ++ ++ return b1[0]; ++} ++ ++static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion) ++{ ++/* fixme (low): error handling */ ++ ++ switch (inversion) { ++ case INVERSION_OFF: ++ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); ++ /* AcqSpectrInvDis on. No idea why someone should want this */ ++ cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7); ++ /* Initial value 0 at start of acq */ ++ cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef); ++ /* current value 0 */ ++ /* The cx24110 manual tells us this reg is read-only. ++ But what the heck... set it ayways */ ++ break; ++ case INVERSION_ON: ++ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); ++ /* AcqSpectrInvDis on. No idea why someone should want this */ ++ cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08); ++ /* Initial value 1 at start of acq */ ++ cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10); ++ /* current value 1 */ ++ break; ++ case INVERSION_AUTO: ++ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe); ++ /* AcqSpectrInvDis off. Leave initial & current states as is */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec) ++{ ++/* fixme (low): error handling */ ++ ++ static const int rate[]={-1,1,2,3,5,7,-1}; ++ static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1}; ++ static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1}; ++ ++ /* Well, the AutoAcq engine of the cx24106 and 24110 automatically ++ searches all enabled viterbi rates, and can handle non-standard ++ rates as well. */ ++ ++ if (fec>FEC_AUTO) ++ fec=FEC_AUTO; ++ ++ if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */ ++ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf); ++ /* clear AcqVitDis bit */ ++ cx24110_writereg(state,0x18,0xae); ++ /* allow all DVB standard code rates */ ++ cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3); ++ /* set nominal Viterbi rate 3/4 */ ++ cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3); ++ /* set current Viterbi rate 3/4 */ ++ cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06); ++ /* set the puncture registers for code rate 3/4 */ ++ return 0; ++ } else { ++ cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20); ++ /* set AcqVitDis bit */ ++ if(rate[fec]>0) { ++ cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]); ++ /* set nominal Viterbi rate */ ++ cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]); ++ /* set current Viterbi rate */ ++ cx24110_writereg(state,0x1a,g1[fec]); ++ cx24110_writereg(state,0x1b,g2[fec]); ++ /* not sure if this is the right way: I always used AutoAcq mode */ ++ } else ++ return -EOPNOTSUPP; ++/* fixme (low): which is the correct return code? */ ++ } ++ return 0; ++} ++ ++static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) ++{ ++ int i; ++ ++ i=cx24110_readreg(state,0x22)&0x0f; ++ if(!(i&0x08)) { ++ return FEC_1_2 + i - 1; ++ } else { ++/* fixme (low): a special code rate has been selected. In theory, we need to ++ return a denominator value, a numerator value, and a pair of puncture ++ maps to correctly describe this mode. But this should never happen in ++ practice, because it cannot be set by cx24110_get_fec. */ ++ return FEC_NONE; ++ } ++} ++ ++static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) ++{ ++/* fixme (low): add error handling */ ++ u32 ratio; ++ u32 tmp, fclk, BDRI; ++ ++ static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; ++ int i; ++ ++ dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate); ++ if (srate>90999000UL/2) ++ srate=90999000UL/2; ++ if (srate<500000) ++ srate=500000; ++ ++ for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++) ++ ; ++ /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, ++ and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, ++ R06[3:0] PLLphaseDetGain */ ++ tmp=cx24110_readreg(state,0x07)&0xfc; ++ if(srate<90999000UL/4) { /* sample rate 45MHz*/ ++ cx24110_writereg(state,0x07,tmp); ++ cx24110_writereg(state,0x06,0x78); ++ fclk=90999000UL/2; ++ } else if(srate<60666000UL/2) { /* sample rate 60MHz */ ++ cx24110_writereg(state,0x07,tmp|0x1); ++ cx24110_writereg(state,0x06,0xa5); ++ fclk=60666000UL; ++ } else if(srate<80888000UL/2) { /* sample rate 80MHz */ ++ cx24110_writereg(state,0x07,tmp|0x2); ++ cx24110_writereg(state,0x06,0x87); ++ fclk=80888000UL; ++ } else { /* sample rate 90MHz */ ++ cx24110_writereg(state,0x07,tmp|0x3); ++ cx24110_writereg(state,0x06,0x78); ++ fclk=90999000UL; ++ } ++ dprintk("cx24110 debug: fclk %d Hz\n",fclk); ++ /* we need to divide two integers with approx. 27 bits in 32 bit ++ arithmetic giving a 25 bit result */ ++ /* the maximum dividend is 90999000/2, 0x02b6446c, this number is ++ also the most complex divisor. Hence, the dividend has, ++ assuming 32bit unsigned arithmetic, 6 clear bits on top, the ++ divisor 2 unused bits at the bottom. Also, the quotient is ++ always less than 1/2. Borrowed from VES1893.c, of course */ ++ ++ tmp=srate<<6; ++ BDRI=fclk>>2; ++ ratio=(tmp/BDRI); ++ ++ tmp=(tmp%BDRI)<<8; ++ ratio=(ratio<<8)+(tmp/BDRI); ++ ++ tmp=(tmp%BDRI)<<8; ++ ratio=(ratio<<8)+(tmp/BDRI); ++ ++ tmp=(tmp%BDRI)<<1; ++ ratio=(ratio<<1)+(tmp/BDRI); ++ ++ dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]); ++ dprintk("fclk = %d\n", fclk); ++ dprintk("ratio= %08x\n", ratio); ++ ++ cx24110_writereg(state, 0x1, (ratio>>16)&0xff); ++ cx24110_writereg(state, 0x2, (ratio>>8)&0xff); ++ cx24110_writereg(state, 0x3, (ratio)&0xff); ++ ++ return 0; ++ ++} ++ ++static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ if (len != 3) ++ return -EINVAL; ++ ++/* tuner data is 21 bits long, must be left-aligned in data */ ++/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ ++/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ ++ ++ cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ ++ cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ ++ ++ /* if the auto tuner writer is still busy, clear it out */ ++ while (cx24110_readreg(state,0x6d)&0x80) ++ cx24110_writereg(state,0x72,0); ++ ++ /* write the topmost 8 bits */ ++ cx24110_writereg(state,0x72,buf[0]); ++ ++ /* wait for the send to be completed */ ++ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ++ ; ++ ++ /* send another 8 bytes */ ++ cx24110_writereg(state,0x72,buf[1]); ++ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ++ ; ++ ++ /* and the topmost 5 bits of this byte */ ++ cx24110_writereg(state,0x72,buf[2]); ++ while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) ++ ; ++ ++ /* now strobe the enable line once */ ++ cx24110_writereg(state,0x6d,0x32); ++ cx24110_writereg(state,0x6d,0x30); ++ ++ return 0; ++} ++ ++static int cx24110_initfe(struct dvb_frontend* fe) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++/* fixme (low): error handling */ ++ int i; ++ ++ dprintk("%s: init chip\n", __func__); ++ ++ for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { ++ cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); ++ } ++ ++ return 0; ++} ++ ++static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); ++ case SEC_VOLTAGE_18: ++ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); ++ default: ++ return -EINVAL; ++ }; ++} ++ ++static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) ++{ ++ int rv, bit; ++ struct cx24110_state *state = fe->demodulator_priv; ++ unsigned long timeout; ++ ++ if (burst == SEC_MINI_A) ++ bit = 0x00; ++ else if (burst == SEC_MINI_B) ++ bit = 0x08; ++ else ++ return -EINVAL; ++ ++ rv = cx24110_readreg(state, 0x77); ++ if (!(rv & 0x04)) ++ cx24110_writereg(state, 0x77, rv | 0x04); ++ ++ rv = cx24110_readreg(state, 0x76); ++ cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit)); ++ timeout = jiffies + msecs_to_jiffies(100); ++ while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) ++ ; /* wait for LNB ready */ ++ ++ return 0; ++} ++ ++static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd *cmd) ++{ ++ int i, rv; ++ struct cx24110_state *state = fe->demodulator_priv; ++ unsigned long timeout; ++ ++ if (cmd->msg_len < 3 || cmd->msg_len > 6) ++ return -EINVAL; /* not implemented */ ++ ++ for (i = 0; i < cmd->msg_len; i++) ++ cx24110_writereg(state, 0x79 + i, cmd->msg[i]); ++ ++ rv = cx24110_readreg(state, 0x77); ++ if (rv & 0x04) { ++ cx24110_writereg(state, 0x77, rv & ~0x04); ++ msleep(30); /* reportedly fixes switching problems */ ++ } ++ ++ rv = cx24110_readreg(state, 0x76); ++ ++ cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); ++ timeout = jiffies + msecs_to_jiffies(100); ++ while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) ++ ; /* wait for LNB ready */ ++ ++ return 0; ++} ++ ++static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ int sync = cx24110_readreg (state, 0x55); ++ ++ *status = 0; ++ ++ if (sync & 0x10) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 0x08) ++ *status |= FE_HAS_CARRIER; ++ ++ sync = cx24110_readreg (state, 0x08); ++ ++ if (sync & 0x40) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 0x20) ++ *status |= FE_HAS_SYNC; ++ ++ if ((sync & 0x60) == 0x60) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ /* fixme (maybe): value range is 16 bit. Scale? */ ++ if(cx24110_readreg(state,0x24)&0x10) { ++ /* the Viterbi error counter has finished one counting window */ ++ cx24110_writereg(state,0x24,0x04); /* select the ber reg */ ++ state->lastber=cx24110_readreg(state,0x25)| ++ (cx24110_readreg(state,0x26)<<8); ++ cx24110_writereg(state,0x24,0x04); /* start new count window */ ++ cx24110_writereg(state,0x24,0x14); ++ } ++ *ber = state->lastber; ++ ++ return 0; ++} ++ ++static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ ++ u8 signal = cx24110_readreg (state, 0x27)+128; ++ *signal_strength = (signal << 8) | signal; ++ ++ return 0; ++} ++ ++static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ ++ if(cx24110_readreg(state,0x6a)&0x80) { ++ /* the Es/N0 error counter has finished one counting window */ ++ state->lastesn0=cx24110_readreg(state,0x69)| ++ (cx24110_readreg(state,0x68)<<8); ++ cx24110_writereg(state,0x6a,0x84); /* start new count window */ ++ } ++ *snr = state->lastesn0; ++ ++ return 0; ++} ++ ++static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ if(cx24110_readreg(state,0x10)&0x40) { ++ /* the RS error counter has finished one counting window */ ++ cx24110_writereg(state,0x10,0x60); /* select the byer reg */ ++ (void)(cx24110_readreg(state, 0x12) | ++ (cx24110_readreg(state, 0x13) << 8) | ++ (cx24110_readreg(state, 0x14) << 16)); ++ cx24110_writereg(state,0x10,0x70); /* select the bler reg */ ++ state->lastbler=cx24110_readreg(state,0x12)| ++ (cx24110_readreg(state,0x13)<<8)| ++ (cx24110_readreg(state,0x14)<<16); ++ cx24110_writereg(state,0x10,0x20); /* start new count window */ ++ } ++ *ucblocks = state->lastbler; ++ ++ return 0; ++} ++ ++static int cx24110_set_frontend(struct dvb_frontend *fe) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ cx24110_set_inversion(state, p->inversion); ++ cx24110_set_fec(state, p->fec_inner); ++ cx24110_set_symbolrate(state, p->symbol_rate); ++ cx24110_writereg(state,0x04,0x05); /* start acquisition */ ++ ++ return 0; ++} ++ ++static int cx24110_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24110_state *state = fe->demodulator_priv; ++ s32 afc; unsigned sclk; ++ ++/* cannot read back tuner settings (freq). Need to have some private storage */ ++ ++ sclk = cx24110_readreg (state, 0x07) & 0x03; ++/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. ++ * Need 64 bit arithmetic. Is thiss possible in the kernel? */ ++ if (sclk==0) sclk=90999000L/2L; ++ else if (sclk==1) sclk=60666000L; ++ else if (sclk==2) sclk=80888000L; ++ else sclk=90999000L; ++ sclk>>=8; ++ afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ ++ ((sclk*cx24110_readreg (state, 0x45))>>8)+ ++ ((sclk*cx24110_readreg (state, 0x46))>>16); ++ ++ p->frequency += afc; ++ p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? ++ INVERSION_ON : INVERSION_OFF; ++ p->fec_inner = cx24110_get_fec(state); ++ ++ return 0; ++} ++ ++static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct cx24110_state *state = fe->demodulator_priv; ++ ++ return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); ++} ++ ++static void cx24110_release(struct dvb_frontend* fe) ++{ ++ struct cx24110_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops cx24110_ops; ++ ++struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct cx24110_state* state = NULL; ++ int ret; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->lastber = 0; ++ state->lastbler = 0; ++ state->lastesn0 = 0; ++ ++ /* check if the demod is there */ ++ ret = cx24110_readreg(state, 0x00); ++ if ((ret != 0x5a) && (ret != 0x69)) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops cx24110_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Conexant CX24110 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 29500, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .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_RECOVER ++ }, ++ ++ .release = cx24110_release, ++ ++ .init = cx24110_initfe, ++ .write = _cx24110_pll_write, ++ .set_frontend = cx24110_set_frontend, ++ .get_frontend = cx24110_get_frontend, ++ .read_status = cx24110_read_status, ++ .read_ber = cx24110_read_ber, ++ .read_signal_strength = cx24110_read_signal_strength, ++ .read_snr = cx24110_read_snr, ++ .read_ucblocks = cx24110_read_ucblocks, ++ ++ .diseqc_send_master_cmd = cx24110_send_diseqc_msg, ++ .set_tone = cx24110_set_tone, ++ .set_voltage = cx24110_set_voltage, ++ .diseqc_send_burst = cx24110_diseqc_send_burst, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); ++MODULE_AUTHOR("Peter Hettkamp"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(cx24110_attach); +diff --git a/drivers/media/dvb-frontends/cx24110.h b/drivers/media/dvb-frontends/cx24110.h +new file mode 100644 +index 0000000..fdcceee +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24110.h +@@ -0,0 +1,61 @@ ++/* ++ cx24110 - Single Chip Satellite Channel Receiver driver module ++ ++ Copyright (C) 2002 Peter Hettkamp based on ++ work ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef CX24110_H ++#define CX24110_H ++ ++#include ++ ++struct cx24110_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) ++{ ++ u8 buf[] = { ++ (u8)((val >> 24) & 0xff), ++ (u8)((val >> 16) & 0xff), ++ (u8)((val >> 8) & 0xff) ++ }; ++ ++ if (fe->ops.write) ++ return fe->ops.write(fe, buf, 3); ++ return 0; ++} ++ ++#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_CX24110 ++ ++#endif // CX24110_H +diff --git a/drivers/media/dvb-frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c +new file mode 100644 +index 0000000..3883c3b +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24113.c +@@ -0,0 +1,618 @@ ++/* ++ * Driver for Conexant CX24113/CX24128 Tuner (Satellite) ++ * ++ * Copyright (C) 2007-8 Patrick Boettcher ++ * ++ * Developed for BBTI / Technisat ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "cx24113.h" ++ ++static int debug; ++ ++#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) ++#define cx_err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "CX24113: %s: ", __func__); \ ++ printk(args); \ ++ } \ ++ } while (0) ++ ++struct cx24113_state { ++ struct i2c_adapter *i2c; ++ const struct cx24113_config *config; ++ ++#define REV_CX24113 0x23 ++ u8 rev; ++ u8 ver; ++ ++ u8 icp_mode:1; ++ ++#define ICP_LEVEL1 0 ++#define ICP_LEVEL2 1 ++#define ICP_LEVEL3 2 ++#define ICP_LEVEL4 3 ++ u8 icp_man:2; ++ u8 icp_auto_low:2; ++ u8 icp_auto_mlow:2; ++ u8 icp_auto_mhi:2; ++ u8 icp_auto_hi:2; ++ u8 icp_dig; ++ ++#define LNA_MIN_GAIN 0 ++#define LNA_MID_GAIN 1 ++#define LNA_MAX_GAIN 2 ++ u8 lna_gain:2; ++ ++ u8 acp_on:1; ++ ++ u8 vco_mode:2; ++ u8 vco_shift:1; ++#define VCOBANDSEL_6 0x80 ++#define VCOBANDSEL_5 0x01 ++#define VCOBANDSEL_4 0x02 ++#define VCOBANDSEL_3 0x04 ++#define VCOBANDSEL_2 0x08 ++#define VCOBANDSEL_1 0x10 ++ u8 vco_band; ++ ++#define VCODIV4 4 ++#define VCODIV2 2 ++ u8 vcodiv; ++ ++ u8 bs_delay:4; ++ u16 bs_freqcnt:13; ++ u16 bs_rdiv; ++ u8 prescaler_mode:1; ++ ++ u8 rfvga_bias_ctrl; ++ ++ s16 tuner_gain_thres; ++ u8 gain_level; ++ ++ u32 frequency; ++ ++ u8 refdiv; ++ ++ u8 Fwindow_enabled; ++}; ++ ++static int cx24113_writereg(struct cx24113_state *state, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->i2c_addr, ++ .flags = 0, .buf = buf, .len = 2 }; ++ int err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x," ++ " data == 0x%02x)\n", __func__, err, reg, data); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int cx24113_readreg(struct cx24113_state *state, u8 reg) ++{ ++ int ret; ++ u8 b; ++ struct i2c_msg msg[] = { ++ { .addr = state->config->i2c_addr, ++ .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = state->config->i2c_addr, ++ .flags = I2C_M_RD, .buf = &b, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ return b; ++} ++ ++static void cx24113_set_parameters(struct cx24113_state *state) ++{ ++ u8 r; ++ ++ r = cx24113_readreg(state, 0x10) & 0x82; ++ r |= state->icp_mode; ++ r |= state->icp_man << 4; ++ r |= state->icp_dig << 2; ++ r |= state->prescaler_mode << 5; ++ cx24113_writereg(state, 0x10, r); ++ ++ r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) ++ | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); ++ cx24113_writereg(state, 0x11, r); ++ ++ if (state->rev == REV_CX24113) { ++ r = cx24113_readreg(state, 0x20) & 0xec; ++ r |= state->lna_gain; ++ r |= state->rfvga_bias_ctrl << 4; ++ cx24113_writereg(state, 0x20, r); ++ } ++ ++ r = cx24113_readreg(state, 0x12) & 0x03; ++ r |= state->acp_on << 2; ++ r |= state->bs_delay << 4; ++ cx24113_writereg(state, 0x12, r); ++ ++ r = cx24113_readreg(state, 0x18) & 0x40; ++ r |= state->vco_shift; ++ if (state->vco_band == VCOBANDSEL_6) ++ r |= (1 << 7); ++ else ++ r |= (state->vco_band << 1); ++ cx24113_writereg(state, 0x18, r); ++ ++ r = cx24113_readreg(state, 0x14) & 0x20; ++ r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); ++ cx24113_writereg(state, 0x14, r); ++ cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); ++ ++ cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); ++ r = (cx24113_readreg(state, 0x17) & 0x0f) | ++ ((state->bs_rdiv & 0x0f) << 4); ++ cx24113_writereg(state, 0x17, r); ++} ++ ++#define VGA_0 0x00 ++#define VGA_1 0x04 ++#define VGA_2 0x02 ++#define VGA_3 0x06 ++#define VGA_4 0x01 ++#define VGA_5 0x05 ++#define VGA_6 0x03 ++#define VGA_7 0x07 ++ ++#define RFVGA_0 0x00 ++#define RFVGA_1 0x01 ++#define RFVGA_2 0x02 ++#define RFVGA_3 0x03 ++ ++static int cx24113_set_gain_settings(struct cx24113_state *state, ++ s16 power_estimation) ++{ ++ u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, ++ vga = cx24113_readreg(state, 0x1f) & 0x3f, ++ rfvga = cx24113_readreg(state, 0x20) & 0xf3; ++ u8 gain_level = power_estimation >= state->tuner_gain_thres; ++ ++ dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", ++ power_estimation, state->tuner_gain_thres, ++ state->gain_level, gain_level); ++ ++ if (gain_level == state->gain_level) ++ return 0; /* nothing to be done */ ++ ++ ampout |= 0xf; ++ ++ if (gain_level) { ++ rfvga |= RFVGA_0 << 2; ++ vga |= (VGA_7 << 3) | VGA_7; ++ } else { ++ rfvga |= RFVGA_2 << 2; ++ vga |= (VGA_6 << 3) | VGA_2; ++ } ++ state->gain_level = gain_level; ++ ++ cx24113_writereg(state, 0x1d, ampout); ++ cx24113_writereg(state, 0x1f, vga); ++ cx24113_writereg(state, 0x20, rfvga); ++ ++ return 1; /* did something */ ++} ++ ++static int cx24113_set_Fref(struct cx24113_state *state, u8 high) ++{ ++ u8 xtal = cx24113_readreg(state, 0x02); ++ if (state->rev == 0x43 && state->vcodiv == VCODIV4) ++ high = 1; ++ ++ xtal &= ~0x2; ++ if (high) ++ xtal |= high << 1; ++ return cx24113_writereg(state, 0x02, xtal); ++} ++ ++static int cx24113_enable(struct cx24113_state *state, u8 enable) ++{ ++ u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; ++ if (state->rev == REV_CX24113) ++ r21 |= (1 << 1); ++ return cx24113_writereg(state, 0x21, r21); ++} ++ ++static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) ++{ ++ u8 r; ++ ++ if (bandwidth_khz <= 19000) ++ r = 0x03 << 6; ++ else if (bandwidth_khz <= 25000) ++ r = 0x02 << 6; ++ else ++ r = 0x01 << 6; ++ ++ dprintk("bandwidth to be set: %d\n", bandwidth_khz); ++ bandwidth_khz *= 10; ++ bandwidth_khz -= 10000; ++ bandwidth_khz /= 1000; ++ bandwidth_khz += 5; ++ bandwidth_khz /= 10; ++ ++ dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); ++ ++ r |= bandwidth_khz & 0x3f; ++ ++ return cx24113_writereg(state, 0x1e, r); ++} ++ ++static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) ++{ ++ u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); ++ return cx24113_writereg(state, 0x10, r); ++} ++ ++static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) ++{ ++ struct cx24113_state *state = fe->tuner_priv; ++ u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; ++ if (r) ++ *status |= TUNER_STATUS_LOCKED; ++ dprintk("PLL locked: %d\n", r); ++ return 0; ++} ++ ++static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) ++{ ++ if (state->rev == 0x43 && state->vcodiv == VCODIV4) ++ refdiv = 2; ++ return state->refdiv = refdiv; ++} ++ ++static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) ++{ ++ s32 N; ++ s64 F; ++ u64 dividend; ++ u8 R, r; ++ u8 vcodiv; ++ u8 factor; ++ s32 freq_hz = state->frequency * 1000; ++ ++ if (state->config->xtal_khz < 20000) ++ factor = 1; ++ else ++ factor = 2; ++ ++ if (state->rev == REV_CX24113) { ++ if (state->frequency >= 1100000) ++ vcodiv = VCODIV2; ++ else ++ vcodiv = VCODIV4; ++ } else { ++ if (state->frequency >= 1165000) ++ vcodiv = VCODIV2; ++ else ++ vcodiv = VCODIV4; ++ } ++ state->vcodiv = vcodiv; ++ ++ dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); ++ R = 0; ++ do { ++ R = cx24113_set_ref_div(state, R + 1); ++ ++ /* calculate tuner PLL settings: */ ++ N = (freq_hz / 100 * vcodiv) * R; ++ N /= (state->config->xtal_khz) * factor * 2; ++ N += 5; /* For round up. */ ++ N /= 10; ++ N -= 32; ++ } while (N < 6 && R < 3); ++ ++ if (N < 6) { ++ cx_err("strange frequency: N < 6\n"); ++ return; ++ } ++ F = freq_hz; ++ F *= (u64) (R * vcodiv * 262144); ++ dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); ++ /* do_div needs an u64 as first argument */ ++ dividend = F; ++ do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); ++ F = dividend; ++ dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); ++ F -= (N + 32) * 262144; ++ ++ dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); ++ ++ if (state->Fwindow_enabled) { ++ if (F > (262144 / 2 - 1638)) ++ F = 262144 / 2 - 1638; ++ if (F < (-262144 / 2 + 1638)) ++ F = -262144 / 2 + 1638; ++ if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { ++ F = 0; ++ r = cx24113_readreg(state, 0x10); ++ cx24113_writereg(state, 0x10, r | (1 << 6)); ++ } ++ } ++ dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); ++ ++ *n = (u16) N; ++ *f = (s32) F; ++} ++ ++ ++static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) ++{ ++ u8 reg; ++ cx24113_writereg(state, 0x19, (n >> 1) & 0xff); ++ ++ reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); ++ cx24113_writereg(state, 0x1a, reg); ++ ++ cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); ++ ++ reg = cx24113_readreg(state, 0x1c) & 0x1f; ++ cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); ++ ++ cx24113_set_Fref(state, r - 1); ++} ++ ++static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) ++{ ++ u8 r = 1; /* or 2 */ ++ u16 n = 6; ++ s32 f = 0; ++ ++ r = cx24113_readreg(state, 0x14); ++ cx24113_writereg(state, 0x14, r & 0x3f); ++ ++ r = cx24113_readreg(state, 0x10); ++ cx24113_writereg(state, 0x10, r & 0xbf); ++ ++ state->frequency = frequency; ++ ++ dprintk("tuning to frequency: %d\n", frequency); ++ ++ cx24113_calc_pll_nf(state, &n, &f); ++ cx24113_set_nfr(state, n, f, state->refdiv); ++ ++ r = cx24113_readreg(state, 0x18) & 0xbf; ++ if (state->vcodiv != VCODIV2) ++ r |= 1 << 6; ++ cx24113_writereg(state, 0x18, r); ++ ++ /* The need for this sleep is not clear. But helps in some cases */ ++ msleep(5); ++ ++ r = cx24113_readreg(state, 0x1c) & 0xef; ++ cx24113_writereg(state, 0x1c, r | (1 << 4)); ++ return 0; ++} ++ ++static int cx24113_init(struct dvb_frontend *fe) ++{ ++ struct cx24113_state *state = fe->tuner_priv; ++ int ret; ++ ++ state->tuner_gain_thres = -50; ++ state->gain_level = 255; /* to force a gain-setting initialization */ ++ state->icp_mode = 0; ++ ++ if (state->config->xtal_khz < 11000) { ++ state->icp_auto_hi = ICP_LEVEL4; ++ state->icp_auto_mhi = ICP_LEVEL4; ++ state->icp_auto_mlow = ICP_LEVEL3; ++ state->icp_auto_low = ICP_LEVEL3; ++ } else { ++ state->icp_auto_hi = ICP_LEVEL4; ++ state->icp_auto_mhi = ICP_LEVEL4; ++ state->icp_auto_mlow = ICP_LEVEL3; ++ state->icp_auto_low = ICP_LEVEL2; ++ } ++ ++ state->icp_dig = ICP_LEVEL3; ++ state->icp_man = ICP_LEVEL1; ++ state->acp_on = 1; ++ state->vco_mode = 0; ++ state->vco_shift = 0; ++ state->vco_band = VCOBANDSEL_1; ++ state->bs_delay = 8; ++ state->bs_freqcnt = 0x0fff; ++ state->bs_rdiv = 0x0fff; ++ state->prescaler_mode = 0; ++ state->lna_gain = LNA_MAX_GAIN; ++ state->rfvga_bias_ctrl = 1; ++ state->Fwindow_enabled = 1; ++ ++ cx24113_set_Fref(state, 0); ++ cx24113_enable(state, 0x3d); ++ cx24113_set_parameters(state); ++ ++ cx24113_set_gain_settings(state, -30); ++ ++ cx24113_set_bandwidth(state, 18025); ++ cx24113_set_clk_inversion(state, 1); ++ ++ if (state->config->xtal_khz >= 40000) ++ ret = cx24113_writereg(state, 0x02, ++ (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); ++ else ++ ret = cx24113_writereg(state, 0x02, ++ (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); ++ ++ return ret; ++} ++ ++static int cx24113_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx24113_state *state = fe->tuner_priv; ++ /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ ++ u32 roll_off = 675; ++ u32 bw; ++ ++ bw = ((c->symbol_rate/100) * roll_off) / 1000; ++ bw += (10000000/100) + 5; ++ bw /= 10; ++ bw += 1000; ++ cx24113_set_bandwidth(state, bw); ++ ++ cx24113_set_frequency(state, c->frequency); ++ msleep(5); ++ return cx24113_get_status(fe, &bw); ++} ++ ++static s8 cx24113_agc_table[2][10] = { ++ {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, ++ {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, ++}; ++ ++void cx24113_agc_callback(struct dvb_frontend *fe) ++{ ++ struct cx24113_state *state = fe->tuner_priv; ++ s16 s, i; ++ if (!fe->ops.read_signal_strength) ++ return; ++ ++ do { ++ /* this only works with the current CX24123 implementation */ ++ fe->ops.read_signal_strength(fe, (u16 *) &s); ++ s >>= 8; ++ dprintk("signal strength: %d\n", s); ++ for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) ++ if (cx24113_agc_table[state->gain_level][i] > s) ++ break; ++ s = -25 - i*5; ++ } while (cx24113_set_gain_settings(state, s)); ++} ++EXPORT_SYMBOL(cx24113_agc_callback); ++ ++static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct cx24113_state *state = fe->tuner_priv; ++ *frequency = state->frequency; ++ return 0; ++} ++ ++static int cx24113_release(struct dvb_frontend *fe) ++{ ++ struct cx24113_state *state = fe->tuner_priv; ++ dprintk("\n"); ++ fe->tuner_priv = NULL; ++ kfree(state); ++ return 0; ++} ++ ++static const struct dvb_tuner_ops cx24113_tuner_ops = { ++ .info = { ++ .name = "Conexant CX24113", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 125, ++ }, ++ ++ .release = cx24113_release, ++ ++ .init = cx24113_init, ++ ++ .set_params = cx24113_set_params, ++ .get_frequency = cx24113_get_frequency, ++ .get_status = cx24113_get_status, ++}; ++ ++struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, ++ const struct cx24113_config *config, struct i2c_adapter *i2c) ++{ ++ /* allocate memory for the internal state */ ++ struct cx24113_state *state = ++ kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); ++ int rc; ++ if (state == NULL) { ++ cx_err("Unable to kzalloc\n"); ++ goto error; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ cx_info("trying to detect myself\n"); ++ ++ /* making a dummy read, because of some expected troubles ++ * after power on */ ++ cx24113_readreg(state, 0x00); ++ ++ rc = cx24113_readreg(state, 0x00); ++ if (rc < 0) { ++ cx_info("CX24113 not found.\n"); ++ goto error; ++ } ++ state->rev = rc; ++ ++ switch (rc) { ++ case 0x43: ++ cx_info("detected CX24113 variant\n"); ++ break; ++ case REV_CX24113: ++ cx_info("successfully detected\n"); ++ break; ++ default: ++ cx_err("unsupported device id: %x\n", state->rev); ++ goto error; ++ } ++ state->ver = cx24113_readreg(state, 0x01); ++ cx_info("version: %x\n", state->ver); ++ ++ /* create dvb_frontend */ ++ memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = state; ++ return fe; ++ ++error: ++ kfree(state); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(cx24113_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/media/dvb-frontends/cx24113.h b/drivers/media/dvb-frontends/cx24113.h +new file mode 100644 +index 0000000..01eb7b9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24113.h +@@ -0,0 +1,53 @@ ++/* ++ * Driver for Conexant CX24113/CX24128 Tuner (Satellite) ++ * ++ * Copyright (C) 2007-8 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef CX24113_H ++#define CX24113_H ++ ++struct dvb_frontend; ++ ++struct cx24113_config { ++ u8 i2c_addr; /* 0x14 or 0x54 */ ++ ++ u32 xtal_khz; ++}; ++ ++#if defined(CONFIG_DVB_TUNER_CX24113) || \ ++ (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, ++ const struct cx24113_config *config, struct i2c_adapter *i2c); ++ ++extern void cx24113_agc_callback(struct dvb_frontend *fe); ++#else ++static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, ++ const struct cx24113_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline void cx24113_agc_callback(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++#endif ++ ++#endif /* CX24113_H */ +diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c +new file mode 100644 +index 0000000..2916d7c +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24116.c +@@ -0,0 +1,1508 @@ ++/* ++ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver ++ ++ Copyright (C) 2006-2008 Steven Toth ++ Copyright (C) 2006-2007 Georg Acher ++ Copyright (C) 2007-2008 Darron Broad ++ March 2007 ++ Fixed some bugs. ++ Added diseqc support. ++ Added corrected signal strength support. ++ August 2007 ++ Sync with legacy version. ++ Some clean ups. ++ Copyright (C) 2008 Igor Liplianin ++ September, 9th 2008 ++ Fixed locking on high symbol rates (>30000). ++ Implement MPEG initialization parameter. ++ January, 17th 2009 ++ Fill set_voltage with actually control voltage code. ++ Correct set tone to not affect voltage. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "cx24116.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_INFO "cx24116: " args); \ ++ } while (0) ++ ++#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" ++#define CX24116_SEARCH_RANGE_KHZ 5000 ++ ++/* known registers */ ++#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ ++#define CX24116_REG_EXECUTE (0x1f) /* execute command */ ++#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ ++#define CX24116_REG_RESET (0x20) /* reset status > 0 */ ++#define CX24116_REG_SIGNAL (0x9e) /* signal low */ ++#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ ++#define CX24116_REG_QUALITY8 (0xa3) ++#define CX24116_REG_QSTATUS (0xbc) ++#define CX24116_REG_QUALITY0 (0xd5) ++#define CX24116_REG_BER0 (0xc9) ++#define CX24116_REG_BER8 (0xc8) ++#define CX24116_REG_BER16 (0xc7) ++#define CX24116_REG_BER24 (0xc6) ++#define CX24116_REG_UCB0 (0xcb) ++#define CX24116_REG_UCB8 (0xca) ++#define CX24116_REG_CLKDIV (0xf3) ++#define CX24116_REG_RATEDIV (0xf9) ++ ++/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ ++#define CX24116_REG_FECSTATUS (0x9c) ++ ++/* FECSTATUS bits */ ++/* mask to determine configured fec (not tuned) or actual fec (tuned) */ ++#define CX24116_FEC_FECMASK (0x1f) ++ ++/* Select DVB-S demodulator, else DVB-S2 */ ++#define CX24116_FEC_DVBS (0x20) ++#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ ++ ++/* Pilot mode requested when tuning else always reset when tuned */ ++#define CX24116_FEC_PILOT (0x80) ++ ++/* arg buffer size */ ++#define CX24116_ARGLEN (0x1e) ++ ++/* rolloff */ ++#define CX24116_ROLLOFF_020 (0x00) ++#define CX24116_ROLLOFF_025 (0x01) ++#define CX24116_ROLLOFF_035 (0x02) ++ ++/* pilot bit */ ++#define CX24116_PILOT_OFF (0x00) ++#define CX24116_PILOT_ON (0x40) ++ ++/* signal status */ ++#define CX24116_HAS_SIGNAL (0x01) ++#define CX24116_HAS_CARRIER (0x02) ++#define CX24116_HAS_VITERBI (0x04) ++#define CX24116_HAS_SYNCLOCK (0x08) ++#define CX24116_HAS_UNKNOWN1 (0x10) ++#define CX24116_HAS_UNKNOWN2 (0x20) ++#define CX24116_STATUS_MASK (0x0f) ++#define CX24116_SIGNAL_MASK (0xc0) ++ ++#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ ++#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ ++#define CX24116_DISEQC_MESGCACHE (2) /* message cached */ ++ ++/* arg offset for DiSEqC */ ++#define CX24116_DISEQC_BURST (1) ++#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ ++#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ ++#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ ++#define CX24116_DISEQC_MSGLEN (5) ++#define CX24116_DISEQC_MSGOFS (6) ++ ++/* DiSEqC burst */ ++#define CX24116_DISEQC_MINI_A (0) ++#define CX24116_DISEQC_MINI_B (1) ++ ++/* DiSEqC tone burst */ ++static int toneburst = 1; ++module_param(toneburst, int, 0644); ++MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\ ++ "2=MESSAGE CACHE (default:1)"); ++ ++/* SNR measurements */ ++static int esno_snr; ++module_param(esno_snr, int, 0644); ++MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\ ++ "1=ESNO(db * 10) (default:0)"); ++ ++enum cmds { ++ CMD_SET_VCO = 0x10, ++ CMD_TUNEREQUEST = 0x11, ++ CMD_MPEGCONFIG = 0x13, ++ CMD_TUNERINIT = 0x14, ++ CMD_BANDWIDTH = 0x15, ++ CMD_GETAGC = 0x19, ++ CMD_LNBCONFIG = 0x20, ++ CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ ++ CMD_LNBDCLEVEL = 0x22, ++ CMD_SET_TONE = 0x23, ++ CMD_UPDFWVERS = 0x35, ++ CMD_TUNERSLEEP = 0x36, ++ CMD_AGCCONTROL = 0x3b, /* Unknown */ ++}; ++ ++/* The Demod/Tuner can't easily provide these, we cache them */ ++struct cx24116_tuning { ++ u32 frequency; ++ u32 symbol_rate; ++ fe_spectral_inversion_t inversion; ++ fe_code_rate_t fec; ++ ++ fe_delivery_system_t delsys; ++ fe_modulation_t modulation; ++ fe_pilot_t pilot; ++ fe_rolloff_t rolloff; ++ ++ /* Demod values */ ++ u8 fec_val; ++ u8 fec_mask; ++ u8 inversion_val; ++ u8 pilot_val; ++ u8 rolloff_val; ++}; ++ ++/* Basic commands that are sent to the firmware */ ++struct cx24116_cmd { ++ u8 len; ++ u8 args[CX24116_ARGLEN]; ++}; ++ ++struct cx24116_state { ++ struct i2c_adapter *i2c; ++ const struct cx24116_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ struct cx24116_tuning dcur; ++ struct cx24116_tuning dnxt; ++ ++ u8 skip_fw_load; ++ u8 burst; ++ struct cx24116_cmd dsec_cmd; ++}; ++ ++static int cx24116_writereg(struct cx24116_state *state, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ if (debug > 1) ++ printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", ++ __func__, reg, data); ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," ++ " value == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++/* Bulk byte writes to a single I2C address, for 32k firmware load */ ++static int cx24116_writeregN(struct cx24116_state *state, int reg, ++ const u8 *data, u16 len) ++{ ++ int ret = -EREMOTEIO; ++ struct i2c_msg msg; ++ u8 *buf; ++ ++ buf = kmalloc(len + 1, GFP_KERNEL); ++ if (buf == NULL) { ++ printk("Unable to kmalloc\n"); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ *(buf) = reg; ++ memcpy(buf + 1, data, len); ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = len + 1; ++ ++ if (debug > 1) ++ printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n", ++ __func__, reg, len); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", ++ __func__, ret, reg); ++ ret = -EREMOTEIO; ++ } ++ ++error: ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int cx24116_readreg(struct cx24116_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ if (debug > 1) ++ printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n", ++ reg, b1[0]); ++ ++ return b1[0]; ++} ++ ++static int cx24116_set_inversion(struct cx24116_state *state, ++ fe_spectral_inversion_t inversion) ++{ ++ dprintk("%s(%d)\n", __func__, inversion); ++ ++ switch (inversion) { ++ case INVERSION_OFF: ++ state->dnxt.inversion_val = 0x00; ++ break; ++ case INVERSION_ON: ++ state->dnxt.inversion_val = 0x04; ++ break; ++ case INVERSION_AUTO: ++ state->dnxt.inversion_val = 0x0C; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ state->dnxt.inversion = inversion; ++ ++ return 0; ++} ++ ++/* ++ * modfec (modulation and FEC) ++ * =========================== ++ * ++ * MOD FEC mask/val standard ++ * ---- -------- ----------- -------- ++ * QPSK FEC_1_2 0x02 0x02+X DVB-S ++ * QPSK FEC_2_3 0x04 0x02+X DVB-S ++ * QPSK FEC_3_4 0x08 0x02+X DVB-S ++ * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) ++ * QPSK FEC_5_6 0x20 0x02+X DVB-S ++ * QPSK FEC_6_7 0x40 0x02+X DVB-S ++ * QPSK FEC_7_8 0x80 0x02+X DVB-S ++ * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) ++ * QPSK AUTO 0xff 0x02+X DVB-S ++ * ++ * For DVB-S high byte probably represents FEC ++ * and low byte selects the modulator. The high ++ * byte is search range mask. Bit 5 may turn ++ * on DVB-S and remaining bits represent some ++ * kind of calibration (how/what i do not know). ++ * ++ * Eg.(2/3) szap "Zone Horror" ++ * ++ * mask/val = 0x04, 0x20 ++ * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK ++ * ++ * mask/val = 0x04, 0x30 ++ * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK ++ * ++ * After tuning FECSTATUS contains actual FEC ++ * in use numbered 1 through to 8 for 1/2 .. 2/3 etc ++ * ++ * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) ++ * ++ * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 ++ * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 ++ * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 ++ * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 ++ * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 ++ * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 ++ * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 ++ * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 ++ * ++ * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 ++ * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 ++ * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 ++ * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 ++ * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 ++ * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2 ++ * ++ * For DVB-S2 low bytes selects both modulator ++ * and FEC. High byte is meaningless here. To ++ * set pilot, bit 6 (0x40) is set. When inspecting ++ * FECSTATUS bit 7 (0x80) represents the pilot ++ * selection whilst not tuned. When tuned, actual FEC ++ * in use is found in FECSTATUS as per above. Pilot ++ * value is reset. ++ */ ++ ++/* A table of modulation, fec and configuration bytes for the demod. ++ * Not all S2 mmodulation schemes are support and not all rates with ++ * a scheme are support. Especially, no auto detect when in S2 mode. ++ */ ++static struct cx24116_modfec { ++ fe_delivery_system_t delivery_system; ++ fe_modulation_t modulation; ++ fe_code_rate_t fec; ++ u8 mask; /* In DVBS mode this is used to autodetect */ ++ u8 val; /* Passed to the firmware to indicate mode selection */ ++} CX24116_MODFEC_MODES[] = { ++ /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ ++ ++ /*mod fec mask val */ ++ { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, ++ { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ ++ { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ ++ { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ ++ { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ ++ { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ ++ { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ ++ { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ ++ { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ ++ { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, ++ /* NBC-QPSK */ ++ { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, ++ { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, ++ { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, ++ { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, ++ { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, ++ { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, ++ { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, ++ { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, ++ /* 8PSK */ ++ { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, ++ { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, ++ { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, ++ { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, ++ { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, ++ { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, ++ /* ++ * `val' can be found in the FECSTATUS register when tuning. ++ * FECSTATUS will give the actual FEC in use if tuning was successful. ++ */ ++}; ++ ++static int cx24116_lookup_fecmod(struct cx24116_state *state, ++ fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) ++{ ++ int i, ret = -EOPNOTSUPP; ++ ++ dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); ++ ++ for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { ++ if ((d == CX24116_MODFEC_MODES[i].delivery_system) && ++ (m == CX24116_MODFEC_MODES[i].modulation) && ++ (f == CX24116_MODFEC_MODES[i].fec)) { ++ ret = i; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int cx24116_set_fec(struct cx24116_state *state, ++ fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) ++{ ++ int ret = 0; ++ ++ dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); ++ ++ ret = cx24116_lookup_fecmod(state, delsys, mod, fec); ++ ++ if (ret < 0) ++ return ret; ++ ++ state->dnxt.fec = fec; ++ state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; ++ state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; ++ dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, ++ state->dnxt.fec_mask, state->dnxt.fec_val); ++ ++ return 0; ++} ++ ++static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate) ++{ ++ dprintk("%s(%d)\n", __func__, rate); ++ ++ /* check if symbol rate is within limits */ ++ if ((rate > state->frontend.ops.info.symbol_rate_max) || ++ (rate < state->frontend.ops.info.symbol_rate_min)) { ++ dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); ++ return -EOPNOTSUPP; ++ } ++ ++ state->dnxt.symbol_rate = rate; ++ dprintk("%s() symbol_rate = %d\n", __func__, rate); ++ ++ return 0; ++} ++ ++static int cx24116_load_firmware(struct dvb_frontend *fe, ++ const struct firmware *fw); ++ ++static int cx24116_firmware_ondemand(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ const struct firmware *fw; ++ int ret = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ if (cx24116_readreg(state, 0x20) > 0) { ++ ++ if (state->skip_fw_load) ++ return 0; ++ ++ /* Load firmware */ ++ /* request the firmware, this will block until loaded */ ++ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", ++ __func__, CX24116_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", ++ __func__); ++ if (ret) { ++ printk(KERN_ERR "%s: No firmware uploaded " ++ "(timeout or file not found?)\n", __func__); ++ return ret; ++ } ++ ++ /* Make sure we don't recurse back through here ++ * during loading */ ++ state->skip_fw_load = 1; ++ ++ ret = cx24116_load_firmware(fe, fw); ++ if (ret) ++ printk(KERN_ERR "%s: Writing firmware to device failed\n", ++ __func__); ++ ++ release_firmware(fw); ++ ++ printk(KERN_INFO "%s: Firmware upload %s\n", __func__, ++ ret == 0 ? "complete" : "failed"); ++ ++ /* Ensure firmware is always loaded if required */ ++ state->skip_fw_load = 0; ++ } ++ ++ return ret; ++} ++ ++/* Take a basic firmware command structure, format it ++ * and forward it for processing ++ */ ++static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ int i, ret; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* Load the firmware if required */ ++ ret = cx24116_firmware_ondemand(fe); ++ if (ret != 0) { ++ printk(KERN_ERR "%s(): Unable initialise the firmware\n", ++ __func__); ++ return ret; ++ } ++ ++ /* Write the command */ ++ for (i = 0; i < cmd->len ; i++) { ++ dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); ++ cx24116_writereg(state, i, cmd->args[i]); ++ } ++ ++ /* Start execution and wait for cmd to terminate */ ++ cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); ++ while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { ++ msleep(10); ++ if (i++ > 64) { ++ /* Avoid looping forever if the firmware does ++ not respond */ ++ printk(KERN_WARNING "%s() Firmware not responding\n", ++ __func__); ++ return -EREMOTEIO; ++ } ++ } ++ return 0; ++} ++ ++static int cx24116_load_firmware(struct dvb_frontend *fe, ++ const struct firmware *fw) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct cx24116_cmd cmd; ++ int i, ret, len, max, remaining; ++ unsigned char vers[4]; ++ ++ dprintk("%s\n", __func__); ++ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", ++ fw->size, ++ fw->data[0], ++ fw->data[1], ++ fw->data[fw->size-2], ++ fw->data[fw->size-1]); ++ ++ /* Toggle 88x SRST pin to reset demod */ ++ if (state->config->reset_device) ++ state->config->reset_device(fe); ++ ++ /* Begin the firmware load process */ ++ /* Prepare the demod, load the firmware, cleanup after load */ ++ ++ /* Init PLL */ ++ cx24116_writereg(state, 0xE5, 0x00); ++ cx24116_writereg(state, 0xF1, 0x08); ++ cx24116_writereg(state, 0xF2, 0x13); ++ ++ /* Start PLL */ ++ cx24116_writereg(state, 0xe0, 0x03); ++ cx24116_writereg(state, 0xe0, 0x00); ++ ++ /* Unknown */ ++ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); ++ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); ++ ++ /* Unknown */ ++ cx24116_writereg(state, 0xF0, 0x03); ++ cx24116_writereg(state, 0xF4, 0x81); ++ cx24116_writereg(state, 0xF5, 0x00); ++ cx24116_writereg(state, 0xF6, 0x00); ++ ++ /* Split firmware to the max I2C write len and write. ++ * Writes whole firmware as one write when i2c_wr_max is set to 0. */ ++ if (state->config->i2c_wr_max) ++ max = state->config->i2c_wr_max; ++ else ++ max = INT_MAX; /* enough for 32k firmware */ ++ ++ for (remaining = fw->size; remaining > 0; remaining -= max - 1) { ++ len = remaining; ++ if (len > max - 1) ++ len = max - 1; ++ ++ cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining], ++ len); ++ } ++ ++ cx24116_writereg(state, 0xF4, 0x10); ++ cx24116_writereg(state, 0xF0, 0x00); ++ cx24116_writereg(state, 0xF8, 0x06); ++ ++ /* Firmware CMD 10: VCO config */ ++ cmd.args[0x00] = CMD_SET_VCO; ++ cmd.args[0x01] = 0x05; ++ cmd.args[0x02] = 0xdc; ++ cmd.args[0x03] = 0xda; ++ cmd.args[0x04] = 0xae; ++ cmd.args[0x05] = 0xaa; ++ cmd.args[0x06] = 0x04; ++ cmd.args[0x07] = 0x9d; ++ cmd.args[0x08] = 0xfc; ++ cmd.args[0x09] = 0x06; ++ cmd.len = 0x0a; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); ++ ++ /* Firmware CMD 14: Tuner config */ ++ cmd.args[0x00] = CMD_TUNERINIT; ++ cmd.args[0x01] = 0x00; ++ cmd.args[0x02] = 0x00; ++ cmd.len = 0x03; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ cx24116_writereg(state, 0xe5, 0x00); ++ ++ /* Firmware CMD 13: MPEG config */ ++ cmd.args[0x00] = CMD_MPEGCONFIG; ++ cmd.args[0x01] = 0x01; ++ cmd.args[0x02] = 0x75; ++ cmd.args[0x03] = 0x00; ++ if (state->config->mpg_clk_pos_pol) ++ cmd.args[0x04] = state->config->mpg_clk_pos_pol; ++ else ++ cmd.args[0x04] = 0x02; ++ cmd.args[0x05] = 0x00; ++ cmd.len = 0x06; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* Firmware CMD 35: Get firmware version */ ++ cmd.args[0x00] = CMD_UPDFWVERS; ++ cmd.len = 0x02; ++ for (i = 0; i < 4; i++) { ++ cmd.args[0x01] = i; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); ++ } ++ printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, ++ vers[0], vers[1], vers[2], vers[3]); ++ ++ return 0; ++} ++ ++static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ ++ int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & ++ CX24116_STATUS_MASK; ++ ++ dprintk("%s: status = 0x%02x\n", __func__, lock); ++ ++ *status = 0; ++ ++ if (lock & CX24116_HAS_SIGNAL) ++ *status |= FE_HAS_SIGNAL; ++ if (lock & CX24116_HAS_CARRIER) ++ *status |= FE_HAS_CARRIER; ++ if (lock & CX24116_HAS_VITERBI) ++ *status |= FE_HAS_VITERBI; ++ if (lock & CX24116_HAS_SYNCLOCK) ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | ++ (cx24116_readreg(state, CX24116_REG_BER16) << 16) | ++ (cx24116_readreg(state, CX24116_REG_BER8) << 8) | ++ cx24116_readreg(state, CX24116_REG_BER0); ++ ++ return 0; ++} ++ ++/* TODO Determine function and scale appropriately */ ++static int cx24116_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct cx24116_cmd cmd; ++ int ret; ++ u16 sig_reading; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* Firmware CMD 19: Get AGC */ ++ cmd.args[0x00] = CMD_GETAGC; ++ cmd.len = 0x01; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ sig_reading = ++ (cx24116_readreg(state, ++ CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | ++ (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); ++ *signal_strength = 0 - sig_reading; ++ ++ dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", ++ __func__, sig_reading, *signal_strength); ++ ++ return 0; ++} ++ ++/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ ++static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ u8 snr_reading; ++ static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ ++ 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, ++ 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, ++ 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, ++ 0x18000 }; ++ ++ dprintk("%s()\n", __func__); ++ ++ snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); ++ ++ if (snr_reading >= 0xa0 /* 100% */) ++ *snr = 0xffff; ++ else ++ *snr = snr_tab[(snr_reading & 0xf0) >> 4] + ++ (snr_tab[(snr_reading & 0x0f)] >> 4); ++ ++ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, ++ snr_reading, *snr); ++ ++ return 0; ++} ++ ++/* The reelbox patches show the value in the registers represents ++ * ESNO, from 0->30db (values 0->300). We provide this value by ++ * default. ++ */ ++static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | ++ cx24116_readreg(state, CX24116_REG_QUALITY0); ++ ++ dprintk("%s: raw 0x%04x\n", __func__, *snr); ++ ++ return 0; ++} ++ ++static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ if (esno_snr == 1) ++ return cx24116_read_snr_esno(fe, snr); ++ else ++ return cx24116_read_snr_pct(fe, snr); ++} ++ ++static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | ++ cx24116_readreg(state, CX24116_REG_UCB0); ++ ++ return 0; ++} ++ ++/* Overwrite the current tuning params, we are about to tune */ ++static void cx24116_clone_params(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ state->dcur = state->dnxt; ++} ++ ++/* Wait for LNB */ ++static int cx24116_wait_for_lnb(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("%s() qstatus = 0x%02x\n", __func__, ++ cx24116_readreg(state, CX24116_REG_QSTATUS)); ++ ++ /* Wait for up to 300 ms */ ++ for (i = 0; i < 30 ; i++) { ++ if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) ++ return 0; ++ msleep(10); ++ } ++ ++ dprintk("%s(): LNB not ready\n", __func__); ++ ++ return -ETIMEDOUT; /* -EBUSY ? */ ++} ++ ++static int cx24116_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx24116_cmd cmd; ++ int ret; ++ ++ dprintk("%s: %s\n", __func__, ++ voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : ++ voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); ++ ++ /* Wait for LNB ready */ ++ ret = cx24116_wait_for_lnb(fe); ++ if (ret != 0) ++ return ret; ++ ++ /* Wait for voltage/min repeat delay */ ++ msleep(100); ++ ++ cmd.args[0x00] = CMD_LNBDCLEVEL; ++ cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); ++ cmd.len = 0x02; ++ ++ /* Min delay time before DiSEqC send */ ++ msleep(15); ++ ++ return cx24116_cmd_execute(fe, &cmd); ++} ++ ++static int cx24116_set_tone(struct dvb_frontend *fe, ++ fe_sec_tone_mode_t tone) ++{ ++ struct cx24116_cmd cmd; ++ int ret; ++ ++ dprintk("%s(%d)\n", __func__, tone); ++ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { ++ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); ++ return -EINVAL; ++ } ++ ++ /* Wait for LNB ready */ ++ ret = cx24116_wait_for_lnb(fe); ++ if (ret != 0) ++ return ret; ++ ++ /* Min delay time after DiSEqC send */ ++ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ ++ ++ /* Now we set the tone */ ++ cmd.args[0x00] = CMD_SET_TONE; ++ cmd.args[0x01] = 0x00; ++ cmd.args[0x02] = 0x00; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ dprintk("%s: setting tone on\n", __func__); ++ cmd.args[0x03] = 0x01; ++ break; ++ case SEC_TONE_OFF: ++ dprintk("%s: setting tone off\n", __func__); ++ cmd.args[0x03] = 0x00; ++ break; ++ } ++ cmd.len = 0x04; ++ ++ /* Min delay time before DiSEqC send */ ++ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ ++ ++ return cx24116_cmd_execute(fe, &cmd); ++} ++ ++/* Initialise DiSEqC */ ++static int cx24116_diseqc_init(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct cx24116_cmd cmd; ++ int ret; ++ ++ /* Firmware CMD 20: LNB/DiSEqC config */ ++ cmd.args[0x00] = CMD_LNBCONFIG; ++ cmd.args[0x01] = 0x00; ++ cmd.args[0x02] = 0x10; ++ cmd.args[0x03] = 0x00; ++ cmd.args[0x04] = 0x8f; ++ cmd.args[0x05] = 0x28; ++ cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; ++ cmd.args[0x07] = 0x01; ++ cmd.len = 0x08; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* Prepare a DiSEqC command */ ++ state->dsec_cmd.args[0x00] = CMD_LNBSEND; ++ ++ /* DiSEqC burst */ ++ state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; ++ ++ /* Unknown */ ++ state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; ++ state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; ++ /* Continuation flag? */ ++ state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; ++ ++ /* DiSEqC message length */ ++ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; ++ ++ /* Command length */ ++ state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; ++ ++ return 0; ++} ++ ++/* Send DiSEqC message with derived burst (hack) || previous burst */ ++static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *d) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ int i, ret; ++ ++ /* Dump DiSEqC message */ ++ if (debug) { ++ printk(KERN_INFO "cx24116: %s(", __func__); ++ for (i = 0 ; i < d->msg_len ;) { ++ printk(KERN_INFO "0x%02x", d->msg[i]); ++ if (++i < d->msg_len) ++ printk(KERN_INFO ", "); ++ } ++ printk(") toneburst=%d\n", toneburst); ++ } ++ ++ /* Validate length */ ++ if (d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) ++ return -EINVAL; ++ ++ /* DiSEqC message */ ++ for (i = 0; i < d->msg_len; i++) ++ state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; ++ ++ /* DiSEqC message length */ ++ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; ++ ++ /* Command length */ ++ state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + ++ state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; ++ ++ /* DiSEqC toneburst */ ++ if (toneburst == CX24116_DISEQC_MESGCACHE) ++ /* Message is cached */ ++ return 0; ++ ++ else if (toneburst == CX24116_DISEQC_TONEOFF) ++ /* Message is sent without burst */ ++ state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; ++ ++ else if (toneburst == CX24116_DISEQC_TONECACHE) { ++ /* ++ * Message is sent with derived else cached burst ++ * ++ * WRITE PORT GROUP COMMAND 38 ++ * ++ * 0/A/A: E0 10 38 F0..F3 ++ * 1/B/B: E0 10 38 F4..F7 ++ * 2/C/A: E0 10 38 F8..FB ++ * 3/D/B: E0 10 38 FC..FF ++ * ++ * databyte[3]= 8421:8421 ++ * ABCD:WXYZ ++ * CLR :SET ++ * ++ * WX= PORT SELECT 0..3 (X=TONEBURST) ++ * Y = VOLTAGE (0=13V, 1=18V) ++ * Z = BAND (0=LOW, 1=HIGH(22K)) ++ */ ++ if (d->msg_len >= 4 && d->msg[2] == 0x38) ++ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ++ ((d->msg[3] & 4) >> 2); ++ if (debug) ++ dprintk("%s burst=%d\n", __func__, ++ state->dsec_cmd.args[CX24116_DISEQC_BURST]); ++ } ++ ++ /* Wait for LNB ready */ ++ ret = cx24116_wait_for_lnb(fe); ++ if (ret != 0) ++ return ret; ++ ++ /* Wait for voltage/min repeat delay */ ++ msleep(100); ++ ++ /* Command */ ++ ret = cx24116_cmd_execute(fe, &state->dsec_cmd); ++ if (ret != 0) ++ return ret; ++ /* ++ * Wait for send ++ * ++ * Eutelsat spec: ++ * >15ms delay + (XXX determine if FW does this, see set_tone) ++ * 13.5ms per byte + ++ * >15ms delay + ++ * 12.5ms burst + ++ * >15ms delay (XXX determine if FW does this, see set_tone) ++ */ ++ msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + ++ ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60)); ++ ++ return 0; ++} ++ ++/* Send DiSEqC burst */ ++static int cx24116_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst); ++ ++ /* DiSEqC burst */ ++ if (burst == SEC_MINI_A) ++ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ++ CX24116_DISEQC_MINI_A; ++ else if (burst == SEC_MINI_B) ++ state->dsec_cmd.args[CX24116_DISEQC_BURST] = ++ CX24116_DISEQC_MINI_B; ++ else ++ return -EINVAL; ++ ++ /* DiSEqC toneburst */ ++ if (toneburst != CX24116_DISEQC_MESGCACHE) ++ /* Burst is cached */ ++ return 0; ++ ++ /* Burst is to be sent with cached message */ ++ ++ /* Wait for LNB ready */ ++ ret = cx24116_wait_for_lnb(fe); ++ if (ret != 0) ++ return ret; ++ ++ /* Wait for voltage/min repeat delay */ ++ msleep(100); ++ ++ /* Command */ ++ ret = cx24116_cmd_execute(fe, &state->dsec_cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* ++ * Wait for send ++ * ++ * Eutelsat spec: ++ * >15ms delay + (XXX determine if FW does this, see set_tone) ++ * 13.5ms per byte + ++ * >15ms delay + ++ * 12.5ms burst + ++ * >15ms delay (XXX determine if FW does this, see set_tone) ++ */ ++ msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60); ++ ++ return 0; ++} ++ ++static void cx24116_release(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ dprintk("%s\n", __func__); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops cx24116_ops; ++ ++struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct cx24116_state *state = NULL; ++ int ret; ++ ++ dprintk("%s\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error1; ++ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is present */ ++ ret = (cx24116_readreg(state, 0xFF) << 8) | ++ cx24116_readreg(state, 0xFE); ++ if (ret != 0x0501) { ++ printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n"); ++ goto error2; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &cx24116_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error2: kfree(state); ++error1: return NULL; ++} ++EXPORT_SYMBOL(cx24116_attach); ++ ++/* ++ * Initialise or wake up device ++ * ++ * Power config will reset and load initial firmware if required ++ */ ++static int cx24116_initfe(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct cx24116_cmd cmd; ++ int ret; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* Power on */ ++ cx24116_writereg(state, 0xe0, 0); ++ cx24116_writereg(state, 0xe1, 0); ++ cx24116_writereg(state, 0xea, 0); ++ ++ /* Firmware CMD 36: Power config */ ++ cmd.args[0x00] = CMD_TUNERSLEEP; ++ cmd.args[0x01] = 0; ++ cmd.len = 0x02; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ ret = cx24116_diseqc_init(fe); ++ if (ret != 0) ++ return ret; ++ ++ /* HVR-4000 needs this */ ++ return cx24116_set_voltage(fe, SEC_VOLTAGE_13); ++} ++ ++/* ++ * Put device to sleep ++ */ ++static int cx24116_sleep(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct cx24116_cmd cmd; ++ int ret; ++ ++ dprintk("%s()\n", __func__); ++ ++ /* Firmware CMD 36: Power config */ ++ cmd.args[0x00] = CMD_TUNERSLEEP; ++ cmd.args[0x01] = 1; ++ cmd.len = 0x02; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* Power off (Shutdown clocks) */ ++ cx24116_writereg(state, 0xea, 0xff); ++ cx24116_writereg(state, 0xe1, 1); ++ cx24116_writereg(state, 0xe0, 1); ++ ++ return 0; ++} ++ ++/* dvb-core told us to tune, the tv property cache will be complete, ++ * it's safe for is to pull values and use them for tuning purposes. ++ */ ++static int cx24116_set_frontend(struct dvb_frontend *fe) ++{ ++ struct cx24116_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx24116_cmd cmd; ++ fe_status_t tunerstat; ++ int i, status, ret, retune = 1; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ dprintk("%s: DVB-S delivery system selected\n", __func__); ++ ++ /* Only QPSK is supported for DVB-S */ ++ if (c->modulation != QPSK) { ++ dprintk("%s: unsupported modulation selected (%d)\n", ++ __func__, c->modulation); ++ return -EOPNOTSUPP; ++ } ++ ++ /* Pilot doesn't exist in DVB-S, turn bit off */ ++ state->dnxt.pilot_val = CX24116_PILOT_OFF; ++ ++ /* DVB-S only supports 0.35 */ ++ if (c->rolloff != ROLLOFF_35) { ++ dprintk("%s: unsupported rolloff selected (%d)\n", ++ __func__, c->rolloff); ++ return -EOPNOTSUPP; ++ } ++ state->dnxt.rolloff_val = CX24116_ROLLOFF_035; ++ break; ++ ++ case SYS_DVBS2: ++ dprintk("%s: DVB-S2 delivery system selected\n", __func__); ++ ++ /* ++ * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, ++ * but not hardware auto detection ++ */ ++ if (c->modulation != PSK_8 && c->modulation != QPSK) { ++ dprintk("%s: unsupported modulation selected (%d)\n", ++ __func__, c->modulation); ++ return -EOPNOTSUPP; ++ } ++ ++ switch (c->pilot) { ++ case PILOT_AUTO: /* Not supported but emulated */ ++ state->dnxt.pilot_val = (c->modulation == QPSK) ++ ? CX24116_PILOT_OFF : CX24116_PILOT_ON; ++ retune++; ++ break; ++ case PILOT_OFF: ++ state->dnxt.pilot_val = CX24116_PILOT_OFF; ++ break; ++ case PILOT_ON: ++ state->dnxt.pilot_val = CX24116_PILOT_ON; ++ break; ++ default: ++ dprintk("%s: unsupported pilot mode selected (%d)\n", ++ __func__, c->pilot); ++ return -EOPNOTSUPP; ++ } ++ ++ switch (c->rolloff) { ++ case ROLLOFF_20: ++ state->dnxt.rolloff_val = CX24116_ROLLOFF_020; ++ break; ++ case ROLLOFF_25: ++ state->dnxt.rolloff_val = CX24116_ROLLOFF_025; ++ break; ++ case ROLLOFF_35: ++ state->dnxt.rolloff_val = CX24116_ROLLOFF_035; ++ break; ++ case ROLLOFF_AUTO: /* Rolloff must be explicit */ ++ default: ++ dprintk("%s: unsupported rolloff selected (%d)\n", ++ __func__, c->rolloff); ++ return -EOPNOTSUPP; ++ } ++ break; ++ ++ default: ++ dprintk("%s: unsupported delivery system selected (%d)\n", ++ __func__, c->delivery_system); ++ return -EOPNOTSUPP; ++ } ++ state->dnxt.delsys = c->delivery_system; ++ state->dnxt.modulation = c->modulation; ++ state->dnxt.frequency = c->frequency; ++ state->dnxt.pilot = c->pilot; ++ state->dnxt.rolloff = c->rolloff; ++ ++ ret = cx24116_set_inversion(state, c->inversion); ++ if (ret != 0) ++ return ret; ++ ++ /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ ++ ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); ++ if (ret != 0) ++ return ret; ++ ++ ret = cx24116_set_symbolrate(state, c->symbol_rate); ++ if (ret != 0) ++ return ret; ++ ++ /* discard the 'current' tuning parameters and prepare to tune */ ++ cx24116_clone_params(fe); ++ ++ dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); ++ dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); ++ dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); ++ dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, ++ state->dcur.pilot, state->dcur.pilot_val); ++ dprintk("%s: retune = %d\n", __func__, retune); ++ dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__, ++ state->dcur.rolloff, state->dcur.rolloff_val); ++ dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); ++ dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, ++ state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); ++ dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, ++ state->dcur.inversion, state->dcur.inversion_val); ++ ++ /* This is also done in advise/acquire on HVR4000 but not on LITE */ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ /* Set/Reset B/W */ ++ cmd.args[0x00] = CMD_BANDWIDTH; ++ cmd.args[0x01] = 0x01; ++ cmd.len = 0x02; ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ return ret; ++ ++ /* Prepare a tune request */ ++ cmd.args[0x00] = CMD_TUNEREQUEST; ++ ++ /* Frequency */ ++ cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; ++ cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; ++ cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); ++ ++ /* Symbol Rate */ ++ cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; ++ cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); ++ ++ /* Automatic Inversion */ ++ cmd.args[0x06] = state->dcur.inversion_val; ++ ++ /* Modulation / FEC / Pilot */ ++ cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val; ++ ++ cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; ++ cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; ++ cmd.args[0x0a] = 0x00; ++ cmd.args[0x0b] = 0x00; ++ cmd.args[0x0c] = state->dcur.rolloff_val; ++ cmd.args[0x0d] = state->dcur.fec_mask; ++ ++ if (state->dcur.symbol_rate > 30000000) { ++ cmd.args[0x0e] = 0x04; ++ cmd.args[0x0f] = 0x00; ++ cmd.args[0x10] = 0x01; ++ cmd.args[0x11] = 0x77; ++ cmd.args[0x12] = 0x36; ++ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); ++ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); ++ } else { ++ cmd.args[0x0e] = 0x06; ++ cmd.args[0x0f] = 0x00; ++ cmd.args[0x10] = 0x00; ++ cmd.args[0x11] = 0xFA; ++ cmd.args[0x12] = 0x24; ++ cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); ++ cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); ++ } ++ ++ cmd.len = 0x13; ++ ++ /* We need to support pilot and non-pilot tuning in the ++ * driver automatically. This is a workaround for because ++ * the demod does not support autodetect. ++ */ ++ do { ++ /* Reset status register */ ++ status = cx24116_readreg(state, CX24116_REG_SSTATUS) ++ & CX24116_SIGNAL_MASK; ++ cx24116_writereg(state, CX24116_REG_SSTATUS, status); ++ ++ /* Tune */ ++ ret = cx24116_cmd_execute(fe, &cmd); ++ if (ret != 0) ++ break; ++ ++ /* ++ * Wait for up to 500 ms before retrying ++ * ++ * If we are able to tune then generally it occurs within 100ms. ++ * If it takes longer, try a different toneburst setting. ++ */ ++ for (i = 0; i < 50 ; i++) { ++ cx24116_read_status(fe, &tunerstat); ++ status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); ++ if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { ++ dprintk("%s: Tuned\n", __func__); ++ goto tuned; ++ } ++ msleep(10); ++ } ++ ++ dprintk("%s: Not tuned\n", __func__); ++ ++ /* Toggle pilot bit when in auto-pilot */ ++ if (state->dcur.pilot == PILOT_AUTO) ++ cmd.args[0x07] ^= CX24116_PILOT_ON; ++ } while (--retune); ++ ++tuned: /* Set/Reset B/W */ ++ cmd.args[0x00] = CMD_BANDWIDTH; ++ cmd.args[0x01] = 0x00; ++ cmd.len = 0x02; ++ return cx24116_cmd_execute(fe, &cmd); ++} ++ ++static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, ++ unsigned int mode_flags, unsigned int *delay, fe_status_t *status) ++{ ++ /* ++ * It is safe to discard "params" here, as the DVB core will sync ++ * fe->dtv_property_cache with fepriv->parameters_in, where the ++ * DVBv3 params are stored. The only practical usage for it indicate ++ * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is ++ * true. ++ */ ++ ++ *delay = HZ / 5; ++ if (re_tune) { ++ int ret = cx24116_set_frontend(fe); ++ if (ret) ++ return ret; ++ } ++ return cx24116_read_status(fe, status); ++} ++ ++static int cx24116_get_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_HW; ++} ++ ++static struct dvb_frontend_ops cx24116_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2 }, ++ .info = { ++ .name = "Conexant CX24116/CX24118", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_QPSK | FE_CAN_RECOVER ++ }, ++ ++ .release = cx24116_release, ++ ++ .init = cx24116_initfe, ++ .sleep = cx24116_sleep, ++ .read_status = cx24116_read_status, ++ .read_ber = cx24116_read_ber, ++ .read_signal_strength = cx24116_read_signal_strength, ++ .read_snr = cx24116_read_snr, ++ .read_ucblocks = cx24116_read_ucblocks, ++ .set_tone = cx24116_set_tone, ++ .set_voltage = cx24116_set_voltage, ++ .diseqc_send_master_cmd = cx24116_send_diseqc_msg, ++ .diseqc_send_burst = cx24116_diseqc_send_burst, ++ .get_frontend_algo = cx24116_get_algo, ++ .tune = cx24116_tune, ++ ++ .set_frontend = cx24116_set_frontend, ++}; ++ ++MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/media/dvb-frontends/cx24116.h b/drivers/media/dvb-frontends/cx24116.h +new file mode 100644 +index 0000000..7d90ab9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24116.h +@@ -0,0 +1,58 @@ ++/* ++ Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver ++ ++ Copyright (C) 2006 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef CX24116_H ++#define CX24116_H ++ ++#include ++ ++struct cx24116_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* Need to set device param for start_dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++ ++ /* Need to reset device during firmware loading */ ++ int (*reset_device)(struct dvb_frontend *fe); ++ ++ /* Need to set MPEG parameters */ ++ u8 mpg_clk_pos_pol:0x02; ++ ++ /* max bytes I2C provider can write at once */ ++ u16 i2c_wr_max; ++}; ++ ++#if defined(CONFIG_DVB_CX24116) || \ ++ (defined(CONFIG_DVB_CX24116_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *cx24116_attach( ++ const struct cx24116_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *cx24116_attach( ++ const struct cx24116_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* CX24116_H */ +diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c +new file mode 100644 +index 0000000..68c88ab +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24123.c +@@ -0,0 +1,1165 @@ ++/* ++ * Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver ++ * ++ * Copyright (C) 2005 Steven Toth ++ * ++ * Support for KWorld DVB-S 100 by Vadim Catana ++ * ++ * Support for CX24123/CX24113-NIM by Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "cx24123.h" ++ ++#define XTAL 10111000 ++ ++static int force_band; ++module_param(force_band, int, 0644); ++MODULE_PARM_DESC(force_band, "Force a specific band select "\ ++ "(1-9, default:off)."); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define info(args...) do { printk(KERN_INFO "CX24123: " args); } while (0) ++#define err(args...) do { printk(KERN_ERR "CX24123: " args); } while (0) ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "CX24123: %s: ", __func__); \ ++ printk(args); \ ++ } \ ++ } while (0) ++ ++struct cx24123_state { ++ struct i2c_adapter *i2c; ++ const struct cx24123_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ /* Some PLL specifics for tuning */ ++ u32 VCAarg; ++ u32 VGAarg; ++ u32 bandselectarg; ++ u32 pllarg; ++ u32 FILTune; ++ ++ struct i2c_adapter tuner_i2c_adapter; ++ ++ u8 demod_rev; ++ ++ /* The Demod/Tuner can't easily provide these, we cache them */ ++ u32 currentfreq; ++ u32 currentsymbolrate; ++}; ++ ++/* Various tuner defaults need to be established for a given symbol rate Sps */ ++static struct cx24123_AGC_val { ++ u32 symbolrate_low; ++ u32 symbolrate_high; ++ u32 VCAprogdata; ++ u32 VGAprogdata; ++ u32 FILTune; ++} cx24123_AGC_vals[] = ++{ ++ { ++ .symbolrate_low = 1000000, ++ .symbolrate_high = 4999999, ++ /* the specs recommend other values for VGA offsets, ++ but tests show they are wrong */ ++ .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, ++ .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x07, ++ .FILTune = 0x27f /* 0.41 V */ ++ }, ++ { ++ .symbolrate_low = 5000000, ++ .symbolrate_high = 14999999, ++ .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, ++ .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x1f, ++ .FILTune = 0x317 /* 0.90 V */ ++ }, ++ { ++ .symbolrate_low = 15000000, ++ .symbolrate_high = 45000000, ++ .VGAprogdata = (1 << 19) | (0x100 << 9) | 0x180, ++ .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x3f, ++ .FILTune = 0x145 /* 2.70 V */ ++ }, ++}; ++ ++/* ++ * Various tuner defaults need to be established for a given frequency kHz. ++ * fixme: The bounds on the bands do not match the doc in real life. ++ * fixme: Some of them have been moved, other might need adjustment. ++ */ ++static struct cx24123_bandselect_val { ++ u32 freq_low; ++ u32 freq_high; ++ u32 VCOdivider; ++ u32 progdata; ++} cx24123_bandselect_vals[] = ++{ ++ /* band 1 */ ++ { ++ .freq_low = 950000, ++ .freq_high = 1074999, ++ .VCOdivider = 4, ++ .progdata = (0 << 19) | (0 << 9) | 0x40, ++ }, ++ ++ /* band 2 */ ++ { ++ .freq_low = 1075000, ++ .freq_high = 1177999, ++ .VCOdivider = 4, ++ .progdata = (0 << 19) | (0 << 9) | 0x80, ++ }, ++ ++ /* band 3 */ ++ { ++ .freq_low = 1178000, ++ .freq_high = 1295999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x01, ++ }, ++ ++ /* band 4 */ ++ { ++ .freq_low = 1296000, ++ .freq_high = 1431999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x02, ++ }, ++ ++ /* band 5 */ ++ { ++ .freq_low = 1432000, ++ .freq_high = 1575999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x04, ++ }, ++ ++ /* band 6 */ ++ { ++ .freq_low = 1576000, ++ .freq_high = 1717999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x08, ++ }, ++ ++ /* band 7 */ ++ { ++ .freq_low = 1718000, ++ .freq_high = 1855999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x10, ++ }, ++ ++ /* band 8 */ ++ { ++ .freq_low = 1856000, ++ .freq_high = 2035999, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x20, ++ }, ++ ++ /* band 9 */ ++ { ++ .freq_low = 2036000, ++ .freq_high = 2150000, ++ .VCOdivider = 2, ++ .progdata = (0 << 19) | (1 << 9) | 0x40, ++ }, ++}; ++ ++static struct { ++ u8 reg; ++ u8 data; ++} cx24123_regdata[] = ++{ ++ {0x00, 0x03}, /* Reset system */ ++ {0x00, 0x00}, /* Clear reset */ ++ {0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */ ++ {0x04, 0x10}, /* MPEG */ ++ {0x05, 0x04}, /* MPEG */ ++ {0x06, 0x31}, /* MPEG (default) */ ++ {0x0b, 0x00}, /* Freq search start point (default) */ ++ {0x0c, 0x00}, /* Demodulator sample gain (default) */ ++ {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */ ++ {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ ++ {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ ++ {0x10, 0x01}, /* Default search inversion, no repeat (default) */ ++ {0x16, 0x00}, /* Enable reading of frequency */ ++ {0x17, 0x01}, /* Enable EsNO Ready Counter */ ++ {0x1c, 0x80}, /* Enable error counter */ ++ {0x20, 0x00}, /* Tuner burst clock rate = 500KHz */ ++ {0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */ ++ {0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */ ++ {0x29, 0x00}, /* DiSEqC LNB_DC off */ ++ {0x2a, 0xb0}, /* DiSEqC Parameters (default) */ ++ {0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */ ++ {0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ ++ {0x2d, 0x00}, ++ {0x2e, 0x00}, ++ {0x2f, 0x00}, ++ {0x30, 0x00}, ++ {0x31, 0x00}, ++ {0x32, 0x8c}, /* DiSEqC Parameters (default) */ ++ {0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */ ++ {0x34, 0x00}, ++ {0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ ++ {0x36, 0x02}, /* DiSEqC Parameters (default) */ ++ {0x37, 0x3a}, /* DiSEqC Parameters (default) */ ++ {0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */ ++ {0x44, 0x00}, /* Constellation (default) */ ++ {0x45, 0x00}, /* Symbol count (default) */ ++ {0x46, 0x0d}, /* Symbol rate estimator on (default) */ ++ {0x56, 0xc1}, /* Error Counter = Viterbi BER */ ++ {0x57, 0xff}, /* Error Counter Window (default) */ ++ {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */ ++ {0x67, 0x83}, /* Non-DCII symbol clock */ ++}; ++ ++static int cx24123_i2c_writereg(struct cx24123_state *state, ++ u8 i2c_addr, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 ++ }; ++ int err; ++ ++ /* printk(KERN_DEBUG "wr(%02x): %02x %02x\n", i2c_addr, reg, data); */ ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk("%s: writereg error(err == %i, reg == 0x%02x," ++ " data == 0x%02x)\n", __func__, err, reg, data); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg) ++{ ++ int ret; ++ u8 b = 0; ++ struct i2c_msg msg[] = { ++ { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &b, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ err("%s: reg=0x%x (error=%d)\n", __func__, reg, ret); ++ return ret; ++ } ++ ++ /* printk(KERN_DEBUG "rd(%02x): %02x %02x\n", i2c_addr, reg, b); */ ++ ++ return b; ++} ++ ++#define cx24123_readreg(state, reg) \ ++ cx24123_i2c_readreg(state, state->config->demod_address, reg) ++#define cx24123_writereg(state, reg, val) \ ++ cx24123_i2c_writereg(state, state->config->demod_address, reg, val) ++ ++static int cx24123_set_inversion(struct cx24123_state *state, ++ fe_spectral_inversion_t inversion) ++{ ++ u8 nom_reg = cx24123_readreg(state, 0x0e); ++ u8 auto_reg = cx24123_readreg(state, 0x10); ++ ++ switch (inversion) { ++ case INVERSION_OFF: ++ dprintk("inversion off\n"); ++ cx24123_writereg(state, 0x0e, nom_reg & ~0x80); ++ cx24123_writereg(state, 0x10, auto_reg | 0x80); ++ break; ++ case INVERSION_ON: ++ dprintk("inversion on\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x80); ++ cx24123_writereg(state, 0x10, auto_reg | 0x80); ++ break; ++ case INVERSION_AUTO: ++ dprintk("inversion auto\n"); ++ cx24123_writereg(state, 0x10, auto_reg & ~0x80); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int cx24123_get_inversion(struct cx24123_state *state, ++ fe_spectral_inversion_t *inversion) ++{ ++ u8 val; ++ ++ val = cx24123_readreg(state, 0x1b) >> 7; ++ ++ if (val == 0) { ++ dprintk("read inversion off\n"); ++ *inversion = INVERSION_OFF; ++ } else { ++ dprintk("read inversion on\n"); ++ *inversion = INVERSION_ON; ++ } ++ ++ return 0; ++} ++ ++static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec) ++{ ++ u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; ++ ++ if (((int)fec < FEC_NONE) || (fec > FEC_AUTO)) ++ fec = FEC_AUTO; ++ ++ /* Set the soft decision threshold */ ++ if (fec == FEC_1_2) ++ cx24123_writereg(state, 0x43, ++ cx24123_readreg(state, 0x43) | 0x01); ++ else ++ cx24123_writereg(state, 0x43, ++ cx24123_readreg(state, 0x43) & ~0x01); ++ ++ switch (fec) { ++ case FEC_1_2: ++ dprintk("set FEC to 1/2\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x01); ++ cx24123_writereg(state, 0x0f, 0x02); ++ break; ++ case FEC_2_3: ++ dprintk("set FEC to 2/3\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x02); ++ cx24123_writereg(state, 0x0f, 0x04); ++ break; ++ case FEC_3_4: ++ dprintk("set FEC to 3/4\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x03); ++ cx24123_writereg(state, 0x0f, 0x08); ++ break; ++ case FEC_4_5: ++ dprintk("set FEC to 4/5\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x04); ++ cx24123_writereg(state, 0x0f, 0x10); ++ break; ++ case FEC_5_6: ++ dprintk("set FEC to 5/6\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x05); ++ cx24123_writereg(state, 0x0f, 0x20); ++ break; ++ case FEC_6_7: ++ dprintk("set FEC to 6/7\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x06); ++ cx24123_writereg(state, 0x0f, 0x40); ++ break; ++ case FEC_7_8: ++ dprintk("set FEC to 7/8\n"); ++ cx24123_writereg(state, 0x0e, nom_reg | 0x07); ++ cx24123_writereg(state, 0x0f, 0x80); ++ break; ++ case FEC_AUTO: ++ dprintk("set FEC to auto\n"); ++ cx24123_writereg(state, 0x0f, 0xfe); ++ break; ++ default: ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} ++ ++static int cx24123_get_fec(struct cx24123_state *state, fe_code_rate_t *fec) ++{ ++ int ret; ++ ++ ret = cx24123_readreg(state, 0x1b); ++ if (ret < 0) ++ return ret; ++ ret = ret & 0x07; ++ ++ switch (ret) { ++ case 1: ++ *fec = FEC_1_2; ++ break; ++ case 2: ++ *fec = FEC_2_3; ++ break; ++ case 3: ++ *fec = FEC_3_4; ++ break; ++ case 4: ++ *fec = FEC_4_5; ++ break; ++ case 5: ++ *fec = FEC_5_6; ++ break; ++ case 6: ++ *fec = FEC_6_7; ++ break; ++ case 7: ++ *fec = FEC_7_8; ++ break; ++ default: ++ /* this can happen when there's no lock */ ++ *fec = FEC_NONE; ++ } ++ ++ return 0; ++} ++ ++/* Approximation of closest integer of log2(a/b). It actually gives the ++ lowest integer i such that 2^i >= round(a/b) */ ++static u32 cx24123_int_log2(u32 a, u32 b) ++{ ++ u32 exp, nearest = 0; ++ u32 div = a / b; ++ if (a % b >= b / 2) ++ ++div; ++ if (div < (1 << 31)) { ++ for (exp = 1; div > exp; nearest++) ++ exp += exp; ++ } ++ return nearest; ++} ++ ++static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) ++{ ++ u32 tmp, sample_rate, ratio, sample_gain; ++ u8 pll_mult; ++ ++ /* check if symbol rate is within limits */ ++ if ((srate > state->frontend.ops.info.symbol_rate_max) || ++ (srate < state->frontend.ops.info.symbol_rate_min)) ++ return -EOPNOTSUPP; ++ ++ /* choose the sampling rate high enough for the required operation, ++ while optimizing the power consumed by the demodulator */ ++ if (srate < (XTAL*2)/2) ++ pll_mult = 2; ++ else if (srate < (XTAL*3)/2) ++ pll_mult = 3; ++ else if (srate < (XTAL*4)/2) ++ pll_mult = 4; ++ else if (srate < (XTAL*5)/2) ++ pll_mult = 5; ++ else if (srate < (XTAL*6)/2) ++ pll_mult = 6; ++ else if (srate < (XTAL*7)/2) ++ pll_mult = 7; ++ else if (srate < (XTAL*8)/2) ++ pll_mult = 8; ++ else ++ pll_mult = 9; ++ ++ ++ sample_rate = pll_mult * XTAL; ++ ++ /* ++ SYSSymbolRate[21:0] = (srate << 23) / sample_rate ++ ++ We have to use 32 bit unsigned arithmetic without precision loss. ++ The maximum srate is 45000000 or 0x02AEA540. This number has ++ only 6 clear bits on top, hence we can shift it left only 6 bits ++ at a time. Borrowed from cx24110.c ++ */ ++ ++ tmp = srate << 6; ++ ratio = tmp / sample_rate; ++ ++ tmp = (tmp % sample_rate) << 6; ++ ratio = (ratio << 6) + (tmp / sample_rate); ++ ++ tmp = (tmp % sample_rate) << 6; ++ ratio = (ratio << 6) + (tmp / sample_rate); ++ ++ tmp = (tmp % sample_rate) << 5; ++ ratio = (ratio << 5) + (tmp / sample_rate); ++ ++ ++ cx24123_writereg(state, 0x01, pll_mult * 6); ++ ++ cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f); ++ cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff); ++ cx24123_writereg(state, 0x0a, ratio & 0xff); ++ ++ /* also set the demodulator sample gain */ ++ sample_gain = cx24123_int_log2(sample_rate, srate); ++ tmp = cx24123_readreg(state, 0x0c) & ~0xe0; ++ cx24123_writereg(state, 0x0c, tmp | sample_gain << 5); ++ ++ dprintk("srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", ++ srate, ratio, sample_rate, sample_gain); ++ ++ return 0; ++} ++ ++/* ++ * Based on the required frequency and symbolrate, the tuner AGC has ++ * to be configured and the correct band selected. ++ * Calculate those values. ++ */ ++static int cx24123_pll_calculate(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24123_state *state = fe->demodulator_priv; ++ u32 ndiv = 0, adiv = 0, vco_div = 0; ++ int i = 0; ++ int pump = 2; ++ int band = 0; ++ int num_bands = ARRAY_SIZE(cx24123_bandselect_vals); ++ struct cx24123_bandselect_val *bsv = NULL; ++ struct cx24123_AGC_val *agcv = NULL; ++ ++ /* Defaults for low freq, low rate */ ++ state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; ++ state->VGAarg = cx24123_AGC_vals[0].VGAprogdata; ++ state->bandselectarg = cx24123_bandselect_vals[0].progdata; ++ vco_div = cx24123_bandselect_vals[0].VCOdivider; ++ ++ /* For the given symbol rate, determine the VCA, VGA and ++ * FILTUNE programming bits */ ++ for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) { ++ agcv = &cx24123_AGC_vals[i]; ++ if ((agcv->symbolrate_low <= p->symbol_rate) && ++ (agcv->symbolrate_high >= p->symbol_rate)) { ++ state->VCAarg = agcv->VCAprogdata; ++ state->VGAarg = agcv->VGAprogdata; ++ state->FILTune = agcv->FILTune; ++ } ++ } ++ ++ /* determine the band to use */ ++ if (force_band < 1 || force_band > num_bands) { ++ for (i = 0; i < num_bands; i++) { ++ bsv = &cx24123_bandselect_vals[i]; ++ if ((bsv->freq_low <= p->frequency) && ++ (bsv->freq_high >= p->frequency)) ++ band = i; ++ } ++ } else ++ band = force_band - 1; ++ ++ state->bandselectarg = cx24123_bandselect_vals[band].progdata; ++ vco_div = cx24123_bandselect_vals[band].VCOdivider; ++ ++ /* determine the charge pump current */ ++ if (p->frequency < (cx24123_bandselect_vals[band].freq_low + ++ cx24123_bandselect_vals[band].freq_high) / 2) ++ pump = 0x01; ++ else ++ pump = 0x02; ++ ++ /* Determine the N/A dividers for the requested lband freq (in kHz). */ ++ /* Note: the reference divider R=10, frequency is in KHz, ++ * XTAL is in Hz */ ++ ndiv = (((p->frequency * vco_div * 10) / ++ (2 * XTAL / 1000)) / 32) & 0x1ff; ++ adiv = (((p->frequency * vco_div * 10) / ++ (2 * XTAL / 1000)) % 32) & 0x1f; ++ ++ if (adiv == 0 && ndiv > 0) ++ ndiv--; ++ ++ /* control bits 11, refdiv 11, charge pump polarity 1, ++ * charge pump current, ndiv, adiv */ ++ state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | ++ (pump << 14) | (ndiv << 5) | adiv; ++ ++ return 0; ++} ++ ++/* ++ * Tuner data is 21 bits long, must be left-aligned in data. ++ * Tuner cx24109 is written through a dedicated 3wire interface ++ * on the demod chip. ++ */ ++static int cx24123_pll_writereg(struct dvb_frontend *fe, u32 data) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ unsigned long timeout; ++ ++ dprintk("pll writereg called, data=0x%08x\n", data); ++ ++ /* align the 21 bytes into to bit23 boundary */ ++ data = data << 3; ++ ++ /* Reset the demod pll word length to 0x15 bits */ ++ cx24123_writereg(state, 0x21, 0x15); ++ ++ /* write the msb 8 bits, wait for the send to be completed */ ++ timeout = jiffies + msecs_to_jiffies(40); ++ cx24123_writereg(state, 0x22, (data >> 16) & 0xff); ++ while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { ++ if (time_after(jiffies, timeout)) { ++ err("%s: demodulator is not responding, "\ ++ "possibly hung, aborting.\n", __func__); ++ return -EREMOTEIO; ++ } ++ msleep(10); ++ } ++ ++ /* send another 8 bytes, wait for the send to be completed */ ++ timeout = jiffies + msecs_to_jiffies(40); ++ cx24123_writereg(state, 0x22, (data >> 8) & 0xff); ++ while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { ++ if (time_after(jiffies, timeout)) { ++ err("%s: demodulator is not responding, "\ ++ "possibly hung, aborting.\n", __func__); ++ return -EREMOTEIO; ++ } ++ msleep(10); ++ } ++ ++ /* send the lower 5 bits of this byte, padded with 3 LBB, ++ * wait for the send to be completed */ ++ timeout = jiffies + msecs_to_jiffies(40); ++ cx24123_writereg(state, 0x22, (data) & 0xff); ++ while ((cx24123_readreg(state, 0x20) & 0x80)) { ++ if (time_after(jiffies, timeout)) { ++ err("%s: demodulator is not responding," \ ++ "possibly hung, aborting.\n", __func__); ++ return -EREMOTEIO; ++ } ++ msleep(10); ++ } ++ ++ /* Trigger the demod to configure the tuner */ ++ cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2); ++ cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd); ++ ++ return 0; ++} ++ ++static int cx24123_pll_tune(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24123_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("frequency=%i\n", p->frequency); ++ ++ if (cx24123_pll_calculate(fe) != 0) { ++ err("%s: cx24123_pll_calcutate failed\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Write the new VCO/VGA */ ++ cx24123_pll_writereg(fe, state->VCAarg); ++ cx24123_pll_writereg(fe, state->VGAarg); ++ ++ /* Write the new bandselect and pll args */ ++ cx24123_pll_writereg(fe, state->bandselectarg); ++ cx24123_pll_writereg(fe, state->pllarg); ++ ++ /* set the FILTUNE voltage */ ++ val = cx24123_readreg(state, 0x28) & ~0x3; ++ cx24123_writereg(state, 0x27, state->FILTune >> 2); ++ cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); ++ ++ dprintk("pll tune VCA=%d, band=%d, pll=%d\n", state->VCAarg, ++ state->bandselectarg, state->pllarg); ++ ++ return 0; ++} ++ ++ ++/* ++ * 0x23: ++ * [7:7] = BTI enabled ++ * [6:6] = I2C repeater enabled ++ * [5:5] = I2C repeater start ++ * [0:0] = BTI start ++ */ ++ ++/* mode == 1 -> i2c-repeater, 0 -> bti */ ++static int cx24123_repeater_mode(struct cx24123_state *state, u8 mode, u8 start) ++{ ++ u8 r = cx24123_readreg(state, 0x23) & 0x1e; ++ if (mode) ++ r |= (1 << 6) | (start << 5); ++ else ++ r |= (1 << 7) | (start); ++ return cx24123_writereg(state, 0x23, r); ++} ++ ++static int cx24123_initfe(struct dvb_frontend *fe) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("init frontend\n"); ++ ++ /* Configure the demod to a good set of defaults */ ++ for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++) ++ cx24123_writereg(state, cx24123_regdata[i].reg, ++ cx24123_regdata[i].data); ++ ++ /* Set the LNB polarity */ ++ if (state->config->lnb_polarity) ++ cx24123_writereg(state, 0x32, ++ cx24123_readreg(state, 0x32) | 0x02); ++ ++ if (state->config->dont_use_pll) ++ cx24123_repeater_mode(state, 1, 0); ++ ++ return 0; ++} ++ ++static int cx24123_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ val = cx24123_readreg(state, 0x29) & ~0x40; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ dprintk("setting voltage 13V\n"); ++ return cx24123_writereg(state, 0x29, val & 0x7f); ++ case SEC_VOLTAGE_18: ++ dprintk("setting voltage 18V\n"); ++ return cx24123_writereg(state, 0x29, val | 0x80); ++ case SEC_VOLTAGE_OFF: ++ /* already handled in cx88-dvb */ ++ return 0; ++ default: ++ return -EINVAL; ++ }; ++ ++ return 0; ++} ++ ++/* wait for diseqc queue to become ready (or timeout) */ ++static void cx24123_wait_for_diseqc(struct cx24123_state *state) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(200); ++ while (!(cx24123_readreg(state, 0x29) & 0x40)) { ++ if (time_after(jiffies, timeout)) { ++ err("%s: diseqc queue not ready, " \ ++ "command may be lost.\n", __func__); ++ break; ++ } ++ msleep(10); ++ } ++} ++ ++static int cx24123_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ int i, val, tone; ++ ++ dprintk("\n"); ++ ++ /* stop continuous tone if enabled */ ++ tone = cx24123_readreg(state, 0x29); ++ if (tone & 0x10) ++ cx24123_writereg(state, 0x29, tone & ~0x50); ++ ++ /* wait for diseqc queue ready */ ++ cx24123_wait_for_diseqc(state); ++ ++ /* select tone mode */ ++ cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); ++ ++ for (i = 0; i < cmd->msg_len; i++) ++ cx24123_writereg(state, 0x2C + i, cmd->msg[i]); ++ ++ val = cx24123_readreg(state, 0x29); ++ cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | ++ ((cmd->msg_len-3) & 3)); ++ ++ /* wait for diseqc message to finish sending */ ++ cx24123_wait_for_diseqc(state); ++ ++ /* restart continuous tone if enabled */ ++ if (tone & 0x10) ++ cx24123_writereg(state, 0x29, tone & ~0x40); ++ ++ return 0; ++} ++ ++static int cx24123_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ int val, tone; ++ ++ dprintk("\n"); ++ ++ /* stop continuous tone if enabled */ ++ tone = cx24123_readreg(state, 0x29); ++ if (tone & 0x10) ++ cx24123_writereg(state, 0x29, tone & ~0x50); ++ ++ /* wait for diseqc queue ready */ ++ cx24123_wait_for_diseqc(state); ++ ++ /* select tone mode */ ++ cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4); ++ msleep(30); ++ val = cx24123_readreg(state, 0x29); ++ if (burst == SEC_MINI_A) ++ cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); ++ else if (burst == SEC_MINI_B) ++ cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08)); ++ else ++ return -EINVAL; ++ ++ cx24123_wait_for_diseqc(state); ++ cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); ++ ++ /* restart continuous tone if enabled */ ++ if (tone & 0x10) ++ cx24123_writereg(state, 0x29, tone & ~0x40); ++ ++ return 0; ++} ++ ++static int cx24123_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ int sync = cx24123_readreg(state, 0x14); ++ ++ *status = 0; ++ if (state->config->dont_use_pll) { ++ u32 tun_status = 0; ++ if (fe->ops.tuner_ops.get_status) ++ fe->ops.tuner_ops.get_status(fe, &tun_status); ++ if (tun_status & TUNER_STATUS_LOCKED) ++ *status |= FE_HAS_SIGNAL; ++ } else { ++ int lock = cx24123_readreg(state, 0x20); ++ if (lock & 0x01) ++ *status |= FE_HAS_SIGNAL; ++ } ++ ++ if (sync & 0x02) ++ *status |= FE_HAS_CARRIER; /* Phase locked */ ++ if (sync & 0x04) ++ *status |= FE_HAS_VITERBI; ++ ++ /* Reed-Solomon Status */ ++ if (sync & 0x08) ++ *status |= FE_HAS_SYNC; ++ if (sync & 0x80) ++ *status |= FE_HAS_LOCK; /*Full Sync */ ++ ++ return 0; ++} ++ ++/* ++ * Configured to return the measurement of errors in blocks, ++ * because no UCBLOCKS value is available, so this value doubles up ++ * to satisfy both measurements. ++ */ ++static int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ ++ /* The true bit error rate is this value divided by ++ the window size (set as 256 * 255) */ ++ *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | ++ (cx24123_readreg(state, 0x1d) << 8 | ++ cx24123_readreg(state, 0x1e)); ++ ++ dprintk("BER = %d\n", *ber); ++ ++ return 0; ++} ++ ++static int cx24123_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ ++ /* larger = better */ ++ *signal_strength = cx24123_readreg(state, 0x3b) << 8; ++ ++ dprintk("Signal strength = %d\n", *signal_strength); ++ ++ return 0; ++} ++ ++static int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ ++ /* Inverted raw Es/N0 count, totally bogus but better than the ++ BER threshold. */ ++ *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) | ++ (u16)cx24123_readreg(state, 0x19)); ++ ++ dprintk("read S/N index = %d\n", *snr); ++ ++ return 0; ++} ++ ++static int cx24123_set_frontend(struct dvb_frontend *fe) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ dprintk("\n"); ++ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ state->currentfreq = p->frequency; ++ state->currentsymbolrate = p->symbol_rate; ++ ++ cx24123_set_inversion(state, p->inversion); ++ cx24123_set_fec(state, p->fec_inner); ++ cx24123_set_symbolrate(state, p->symbol_rate); ++ ++ if (!state->config->dont_use_pll) ++ cx24123_pll_tune(fe); ++ else if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ else ++ err("it seems I don't have a tuner..."); ++ ++ /* Enable automatic acquisition and reset cycle */ ++ cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07)); ++ cx24123_writereg(state, 0x00, 0x10); ++ cx24123_writereg(state, 0x00, 0); ++ ++ if (state->config->agc_callback) ++ state->config->agc_callback(fe); ++ ++ return 0; ++} ++ ++static int cx24123_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx24123_state *state = fe->demodulator_priv; ++ ++ dprintk("\n"); ++ ++ if (cx24123_get_inversion(state, &p->inversion) != 0) { ++ err("%s: Failed to get inversion status\n", __func__); ++ return -EREMOTEIO; ++ } ++ if (cx24123_get_fec(state, &p->fec_inner) != 0) { ++ err("%s: Failed to get fec status\n", __func__); ++ return -EREMOTEIO; ++ } ++ p->frequency = state->currentfreq; ++ p->symbol_rate = state->currentsymbolrate; ++ ++ return 0; ++} ++ ++static int cx24123_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ /* wait for diseqc queue ready */ ++ cx24123_wait_for_diseqc(state); ++ ++ val = cx24123_readreg(state, 0x29) & ~0x40; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ dprintk("setting tone on\n"); ++ return cx24123_writereg(state, 0x29, val | 0x10); ++ case SEC_TONE_OFF: ++ dprintk("setting tone off\n"); ++ return cx24123_writereg(state, 0x29, val & 0xef); ++ default: ++ err("CASE reached default with tone=%d\n", tone); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int cx24123_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ int retval = 0; ++ ++ if (re_tune) ++ retval = cx24123_set_frontend(fe); ++ ++ if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) ++ cx24123_read_status(fe, status); ++ *delay = HZ/10; ++ ++ return retval; ++} ++ ++static int cx24123_get_algo(struct dvb_frontend *fe) ++{ ++ return 1; /* FE_ALGO_HW */ ++} ++ ++static void cx24123_release(struct dvb_frontend *fe) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ dprintk("\n"); ++ i2c_del_adapter(&state->tuner_i2c_adapter); ++ kfree(state); ++} ++ ++static int cx24123_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct cx24123_state *state = i2c_get_adapdata(i2c_adap); ++ /* this repeater closes after the first stop */ ++ cx24123_repeater_mode(state, 1, 1); ++ return i2c_transfer(state->i2c, msg, num); ++} ++ ++static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm cx24123_tuner_i2c_algo = { ++ .master_xfer = cx24123_tuner_i2c_tuner_xfer, ++ .functionality = cx24123_tuner_i2c_func, ++}; ++ ++struct i2c_adapter * ++ cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ struct cx24123_state *state = fe->demodulator_priv; ++ return &state->tuner_i2c_adapter; ++} ++EXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter); ++ ++static struct dvb_frontend_ops cx24123_ops; ++ ++struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, ++ struct i2c_adapter *i2c) ++{ ++ /* allocate memory for the internal state */ ++ struct cx24123_state *state = ++ kzalloc(sizeof(struct cx24123_state), GFP_KERNEL); ++ ++ dprintk("\n"); ++ if (state == NULL) { ++ err("Unable to kzalloc\n"); ++ goto error; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ state->demod_rev = cx24123_readreg(state, 0x00); ++ switch (state->demod_rev) { ++ case 0xe1: ++ info("detected CX24123C\n"); ++ break; ++ case 0xd1: ++ info("detected CX24123\n"); ++ break; ++ default: ++ err("wrong demod revision: %x\n", state->demod_rev); ++ goto error; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &cx24123_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* create tuner i2c adapter */ ++ if (config->dont_use_pll) ++ cx24123_repeater_mode(state, 1, 0); ++ ++ strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus", ++ sizeof(state->tuner_i2c_adapter.name)); ++ state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo; ++ state->tuner_i2c_adapter.algo_data = NULL; ++ i2c_set_adapdata(&state->tuner_i2c_adapter, state); ++ if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { ++ err("tuner i2c bus could not be initialized\n"); ++ goto error; ++ } ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(cx24123_attach); ++ ++static struct dvb_frontend_ops cx24123_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Conexant CX24123/CX24109", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_RECOVER ++ }, ++ ++ .release = cx24123_release, ++ ++ .init = cx24123_initfe, ++ .set_frontend = cx24123_set_frontend, ++ .get_frontend = cx24123_get_frontend, ++ .read_status = cx24123_read_status, ++ .read_ber = cx24123_read_ber, ++ .read_signal_strength = cx24123_read_signal_strength, ++ .read_snr = cx24123_read_snr, ++ .diseqc_send_master_cmd = cx24123_send_diseqc_msg, ++ .diseqc_send_burst = cx24123_diseqc_send_burst, ++ .set_tone = cx24123_set_tone, ++ .set_voltage = cx24123_set_voltage, ++ .tune = cx24123_tune, ++ .get_frontend_algo = cx24123_get_algo, ++}; ++ ++MODULE_DESCRIPTION("DVB Frontend module for Conexant " \ ++ "CX24123/CX24109/CX24113 hardware"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/media/dvb-frontends/cx24123.h b/drivers/media/dvb-frontends/cx24123.h +new file mode 100644 +index 0000000..51ae866 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cx24123.h +@@ -0,0 +1,61 @@ ++/* ++ Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver ++ ++ Copyright (C) 2005 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef CX24123_H ++#define CX24123_H ++ ++#include ++ ++struct cx24123_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* Need to set device param for start_dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++ ++ /* 0 = LNB voltage normal, 1 = LNB voltage inverted */ ++ int lnb_polarity; ++ ++ /* this device has another tuner */ ++ u8 dont_use_pll; ++ void (*agc_callback) (struct dvb_frontend *); ++}; ++ ++#if defined(CONFIG_DVB_CX24123) || (defined(CONFIG_DVB_CX24123_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, ++ struct i2c_adapter *i2c); ++extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *); ++#else ++static inline struct dvb_frontend *cx24123_attach( ++ const struct cx24123_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static struct i2c_adapter * ++ cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* CX24123_H */ +diff --git a/drivers/media/dvb-frontends/cxd2820r.h b/drivers/media/dvb-frontends/cxd2820r.h +new file mode 100644 +index 0000000..6acc21c +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r.h +@@ -0,0 +1,88 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#ifndef CXD2820R_H ++#define CXD2820R_H ++ ++#include ++ ++#define CXD2820R_GPIO_D (0 << 0) /* disable */ ++#define CXD2820R_GPIO_E (1 << 0) /* enable */ ++#define CXD2820R_GPIO_O (0 << 1) /* output */ ++#define CXD2820R_GPIO_I (1 << 1) /* input */ ++#define CXD2820R_GPIO_L (0 << 2) /* output low */ ++#define CXD2820R_GPIO_H (1 << 2) /* output high */ ++ ++#define CXD2820R_TS_SERIAL 0x08 ++#define CXD2820R_TS_SERIAL_MSB 0x28 ++#define CXD2820R_TS_PARALLEL 0x30 ++#define CXD2820R_TS_PARALLEL_MSB 0x70 ++ ++struct cxd2820r_config { ++ /* Demodulator I2C address. ++ * Driver determines DVB-C slave I2C address automatically from master ++ * address. ++ * Default: none, must set ++ * Values: 0x6c, 0x6d ++ */ ++ u8 i2c_address; ++ ++ /* TS output mode. ++ * Default: none, must set. ++ * Values: ++ */ ++ u8 ts_mode; ++ ++ /* IF AGC polarity. ++ * Default: 0 ++ * Values: 0, 1 ++ */ ++ bool if_agc_polarity; ++ ++ /* Spectrum inversion. ++ * Default: 0 ++ * Values: 0, 1 ++ */ ++ bool spec_inv; ++}; ++ ++ ++#if defined(CONFIG_DVB_CXD2820R) || \ ++ (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *cxd2820r_attach( ++ const struct cxd2820r_config *config, ++ struct i2c_adapter *i2c, ++ int *gpio_chip_base ++); ++#else ++static inline struct dvb_frontend *cxd2820r_attach( ++ const struct cxd2820r_config *config, ++ struct i2c_adapter *i2c, ++ int *gpio_chip_base ++) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif ++ ++#endif /* CXD2820R_H */ +diff --git a/drivers/media/dvb-frontends/cxd2820r_c.c b/drivers/media/dvb-frontends/cxd2820r_c.c +new file mode 100644 +index 0000000..125a440 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r_c.c +@@ -0,0 +1,343 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "cxd2820r_priv.h" ++ ++int cxd2820r_set_frontend_c(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i; ++ u8 buf[2]; ++ u32 if_freq; ++ u16 if_ctl; ++ u64 num; ++ struct reg_val_mask tab[] = { ++ { 0x00080, 0x01, 0xff }, ++ { 0x00081, 0x05, 0xff }, ++ { 0x00085, 0x07, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ ++ { 0x00082, 0x20, 0x60 }, ++ { 0x1016a, 0x48, 0xff }, ++ { 0x100a5, 0x00, 0x01 }, ++ { 0x10020, 0x06, 0x07 }, ++ { 0x10059, 0x50, 0xff }, ++ { 0x10087, 0x0c, 0x3c }, ++ { 0x1008b, 0x07, 0xff }, ++ { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 }, ++ { 0x10070, priv->cfg.ts_mode, 0xff }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s: frequency=%d symbol_rate=%d\n", __func__, ++ c->frequency, c->symbol_rate); ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ if (priv->delivery_system != SYS_DVBC_ANNEX_A) { ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, ++ tab[i].val, tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ } ++ ++ priv->delivery_system = SYS_DVBC_ANNEX_A; ++ priv->ber_running = 0; /* tune stops BER counter */ ++ ++ /* program IF frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) { ++ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); ++ if (ret) ++ goto error; ++ } else ++ if_freq = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); ++ ++ num = if_freq / 1000; /* Hz => kHz */ ++ num *= 0x4000; ++ if_ctl = cxd2820r_div_u64_round_closest(num, 41000); ++ buf[0] = (if_ctl >> 8) & 0x3f; ++ buf[1] = (if_ctl >> 0) & 0xff; ++ ++ ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_get_frontend_c(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[2]; ++ ++ ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2); ++ if (ret) ++ goto error; ++ ++ c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]); ++ ++ ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 0) & 0x07) { ++ case 0: ++ c->modulation = QAM_16; ++ break; ++ case 1: ++ c->modulation = QAM_32; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ case 3: ++ c->modulation = QAM_128; ++ break; ++ case 4: ++ c->modulation = QAM_256; ++ break; ++ } ++ ++ switch ((buf[0] >> 7) & 0x01) { ++ case 0: ++ c->inversion = INVERSION_OFF; ++ break; ++ case 1: ++ c->inversion = INVERSION_ON; ++ break; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[3], start_ber = 0; ++ *ber = 0; ++ ++ if (priv->ber_running) { ++ ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { ++ *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; ++ start_ber = 1; ++ } ++ } else { ++ priv->ber_running = 1; ++ start_ber = 1; ++ } ++ ++ if (start_ber) { ++ /* (re)start BER */ ++ ret = cxd2820r_wr_reg(priv, 0x10079, 0x01); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 tmp; ++ ++ ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ tmp = (buf[0] & 0x03) << 8 | buf[1]; ++ tmp = (~tmp & 0x03ff); ++ ++ if (tmp == 512) ++ /* ~no signal */ ++ tmp = 0; ++ else if (tmp > 350) ++ tmp = 350; ++ ++ /* scale value to 0x0000-0xffff */ ++ *strength = tmp * 0xffff / (350-0); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ unsigned int A, B; ++ /* report SNR in dB * 10 */ ++ ++ ret = cxd2820r_rd_reg(priv, 0x10019, &tmp); ++ if (ret) ++ goto error; ++ ++ if (((tmp >> 0) & 0x03) % 2) { ++ A = 875; ++ B = 650; ++ } else { ++ A = 950; ++ B = 760; ++ } ++ ++ ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp); ++ if (ret) ++ goto error; ++ ++ #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ ++ if (tmp) ++ *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5) ++ / 10; ++ else ++ *snr = 0; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ /* no way to read ? */ ++ return 0; ++} ++ ++int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ *status = 0; ++ ++ ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ if (((buf[0] >> 0) & 0x01) == 1) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC; ++ ++ if (((buf[1] >> 3) & 0x01) == 1) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: lock=%02x %02x\n", __func__, buf[0], ++ buf[1]); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_init_c(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_sleep_c(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret, i; ++ struct reg_val_mask tab[] = { ++ { 0x000ff, 0x1f, 0xff }, ++ { 0x00085, 0x00, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ { 0x00081, 0x00, 0xff }, ++ { 0x00080, 0x00, 0xff }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s\n", __func__); ++ ++ priv->delivery_system = SYS_UNDEFINED; ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 500; ++ s->step_size = 0; /* no zigzag */ ++ s->max_drift = 0; ++ ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c +new file mode 100644 +index 0000000..9b658c1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r_core.c +@@ -0,0 +1,758 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "cxd2820r_priv.h" ++ ++/* write multiple registers */ ++static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, ++ u8 *val, int len) ++{ ++ int ret; ++ u8 buf[len+1]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = i2c, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = reg; ++ memcpy(&buf[1], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* read multiple registers */ ++static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, ++ u8 *val, int len) ++{ ++ int ret; ++ u8 buf[len]; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = i2c, ++ .flags = 0, ++ .len = 1, ++ .buf = ®, ++ }, { ++ .addr = i2c, ++ .flags = I2C_M_RD, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ memcpy(val, buf, len); ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++/* write multiple registers */ ++int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, ++ int len) ++{ ++ int ret; ++ u8 i2c_addr; ++ u8 reg = (reginfo >> 0) & 0xff; ++ u8 bank = (reginfo >> 8) & 0xff; ++ u8 i2c = (reginfo >> 16) & 0x01; ++ ++ /* select I2C */ ++ if (i2c) ++ i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ ++ else ++ i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ ++ ++ /* switch bank if needed */ ++ if (bank != priv->bank[i2c]) { ++ ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); ++ if (ret) ++ return ret; ++ priv->bank[i2c] = bank; ++ } ++ return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len); ++} ++ ++/* read multiple registers */ ++int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, ++ int len) ++{ ++ int ret; ++ u8 i2c_addr; ++ u8 reg = (reginfo >> 0) & 0xff; ++ u8 bank = (reginfo >> 8) & 0xff; ++ u8 i2c = (reginfo >> 16) & 0x01; ++ ++ /* select I2C */ ++ if (i2c) ++ i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ ++ else ++ i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ ++ ++ /* switch bank if needed */ ++ if (bank != priv->bank[i2c]) { ++ ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); ++ if (ret) ++ return ret; ++ priv->bank[i2c] = bank; ++ } ++ return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len); ++} ++ ++/* write single register */ ++int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val) ++{ ++ return cxd2820r_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register */ ++int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val) ++{ ++ return cxd2820r_rd_regs(priv, reg, val, 1); ++} ++ ++/* write single register with mask */ ++int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, ++ u8 mask) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = cxd2820r_rd_reg(priv, reg, &tmp); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return cxd2820r_wr_reg(priv, reg, val); ++} ++ ++int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret, i; ++ u8 tmp0, tmp1; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ /* update GPIOs only when needed */ ++ if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio))) ++ return 0; ++ ++ tmp0 = 0x00; ++ tmp1 = 0x00; ++ for (i = 0; i < sizeof(priv->gpio); i++) { ++ /* enable / disable */ ++ if (gpio[i] & CXD2820R_GPIO_E) ++ tmp0 |= (2 << 6) >> (2 * i); ++ else ++ tmp0 |= (1 << 6) >> (2 * i); ++ ++ /* input / output */ ++ if (gpio[i] & CXD2820R_GPIO_I) ++ tmp1 |= (1 << (3 + i)); ++ else ++ tmp1 |= (0 << (3 + i)); ++ ++ /* high / low */ ++ if (gpio[i] & CXD2820R_GPIO_H) ++ tmp1 |= (1 << (0 + i)); ++ else ++ tmp1 |= (0 << (0 + i)); ++ ++ dev_dbg(&priv->i2c->dev, "%s: gpio i=%d %02x %02x\n", __func__, ++ i, tmp0, tmp1); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: wr gpio=%02x %02x\n", __func__, tmp0, ++ tmp1); ++ ++ /* write bits [7:2] */ ++ ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc); ++ if (ret) ++ goto error; ++ ++ /* write bits [5:0] */ ++ ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f); ++ if (ret) ++ goto error; ++ ++ memcpy(priv->gpio, gpio, sizeof(priv->gpio)); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */ ++u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor) ++{ ++ return div_u64(dividend + (divisor / 2), divisor); ++} ++ ++static int cxd2820r_set_frontend(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_init_t(fe); ++ if (ret < 0) ++ goto err; ++ ret = cxd2820r_set_frontend_t(fe); ++ if (ret < 0) ++ goto err; ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_init_t(fe); ++ if (ret < 0) ++ goto err; ++ ret = cxd2820r_set_frontend_t2(fe); ++ if (ret < 0) ++ goto err; ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_init_c(fe); ++ if (ret < 0) ++ goto err; ++ ret = cxd2820r_set_frontend_c(fe); ++ if (ret < 0) ++ goto err; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: error state=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ret = -EINVAL; ++ break; ++ } ++err: ++ return ret; ++} ++static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_read_status_t(fe, status); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_read_status_t2(fe, status); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_read_status_c(fe, status); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_get_frontend(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ if (priv->delivery_system == SYS_UNDEFINED) ++ return 0; ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_get_frontend_t(fe); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_get_frontend_t2(fe); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_get_frontend_c(fe); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_read_ber_t(fe, ber); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_read_ber_t2(fe, ber); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_read_ber_c(fe, ber); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_read_signal_strength_t(fe, strength); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_read_signal_strength_t2(fe, strength); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_read_signal_strength_c(fe, strength); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_read_snr_t(fe, snr); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_read_snr_t2(fe, snr); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_read_snr_c(fe, snr); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_read_ucblocks_t(fe, ucblocks); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_read_ucblocks_t2(fe, ucblocks); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_read_ucblocks_c(fe, ucblocks); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_init(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int cxd2820r_sleep(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_sleep_t(fe); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_sleep_t2(fe); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_sleep_c(fe); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int cxd2820r_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ switch (fe->dtv_property_cache.delivery_system) { ++ case SYS_DVBT: ++ ret = cxd2820r_get_tune_settings_t(fe, s); ++ break; ++ case SYS_DVBT2: ++ ret = cxd2820r_get_tune_settings_t2(fe, s); ++ break; ++ case SYS_DVBC_ANNEX_A: ++ ret = cxd2820r_get_tune_settings_c(fe, s); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i; ++ fe_status_t status = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delsys=%d\n", __func__, ++ fe->dtv_property_cache.delivery_system); ++ ++ /* switch between DVB-T and DVB-T2 when tune fails */ ++ if (priv->last_tune_failed) { ++ if (priv->delivery_system == SYS_DVBT) { ++ ret = cxd2820r_sleep_t(fe); ++ if (ret) ++ goto error; ++ ++ c->delivery_system = SYS_DVBT2; ++ } else if (priv->delivery_system == SYS_DVBT2) { ++ ret = cxd2820r_sleep_t2(fe); ++ if (ret) ++ goto error; ++ ++ c->delivery_system = SYS_DVBT; ++ } ++ } ++ ++ /* set frontend */ ++ ret = cxd2820r_set_frontend(fe); ++ if (ret) ++ goto error; ++ ++ ++ /* frontend lock wait loop count */ ++ switch (priv->delivery_system) { ++ case SYS_DVBT: ++ case SYS_DVBC_ANNEX_A: ++ i = 20; ++ break; ++ case SYS_DVBT2: ++ i = 40; ++ break; ++ case SYS_UNDEFINED: ++ default: ++ i = 0; ++ break; ++ } ++ ++ /* wait frontend lock */ ++ for (; i > 0; i--) { ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ msleep(50); ++ ret = cxd2820r_read_status(fe, &status); ++ if (ret) ++ goto error; ++ ++ if (status & FE_HAS_LOCK) ++ break; ++ } ++ ++ /* check if we have a valid signal */ ++ if (status & FE_HAS_LOCK) { ++ priv->last_tune_failed = 0; ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ priv->last_tune_failed = 1; ++ return DVBFE_ALGO_SEARCH_AGAIN; ++ } ++ ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static void cxd2820r_release(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int uninitialized_var(ret); /* silence compiler warning */ ++ ++ dev_dbg(&priv->i2c->dev, "%s\n", __func__); ++ ++#ifdef CONFIG_GPIOLIB ++ /* remove GPIOs */ ++ if (priv->gpio_chip.label) { ++ ret = gpiochip_remove(&priv->gpio_chip); ++ if (ret) ++ dev_err(&priv->i2c->dev, "%s: gpiochip_remove() " \ ++ "failed=%d\n", KBUILD_MODNAME, ret); ++ } ++#endif ++ kfree(priv); ++ return; ++} ++ ++static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ ++ dev_dbg(&priv->i2c->dev, "%s: %d\n", __func__, enable); ++ ++ /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */ ++ return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1); ++} ++ ++#ifdef CONFIG_GPIOLIB ++static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr, ++ int val) ++{ ++ struct cxd2820r_priv *priv = ++ container_of(chip, struct cxd2820r_priv, gpio_chip); ++ u8 gpio[GPIO_COUNT]; ++ ++ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); ++ ++ memcpy(gpio, priv->gpio, sizeof(gpio)); ++ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); ++ ++ return cxd2820r_gpio(&priv->fe, gpio); ++} ++ ++static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val) ++{ ++ struct cxd2820r_priv *priv = ++ container_of(chip, struct cxd2820r_priv, gpio_chip); ++ u8 gpio[GPIO_COUNT]; ++ ++ dev_dbg(&priv->i2c->dev, "%s: nr=%d val=%d\n", __func__, nr, val); ++ ++ memcpy(gpio, priv->gpio, sizeof(gpio)); ++ gpio[nr] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | (val << 2); ++ ++ (void) cxd2820r_gpio(&priv->fe, gpio); ++ ++ return; ++} ++ ++static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr) ++{ ++ struct cxd2820r_priv *priv = ++ container_of(chip, struct cxd2820r_priv, gpio_chip); ++ ++ dev_dbg(&priv->i2c->dev, "%s: nr=%d\n", __func__, nr); ++ ++ return (priv->gpio[nr] >> 2) & 0x01; ++} ++#endif ++ ++static const struct dvb_frontend_ops cxd2820r_ops = { ++ .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, ++ /* default: DVB-T/T2 */ ++ .info = { ++ .name = "Sony CXD2820R", ++ ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_MUTE_TS | ++ FE_CAN_2G_MODULATION ++ }, ++ ++ .release = cxd2820r_release, ++ .init = cxd2820r_init, ++ .sleep = cxd2820r_sleep, ++ ++ .get_tune_settings = cxd2820r_get_tune_settings, ++ .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl, ++ ++ .get_frontend = cxd2820r_get_frontend, ++ ++ .get_frontend_algo = cxd2820r_get_frontend_algo, ++ .search = cxd2820r_search, ++ ++ .read_status = cxd2820r_read_status, ++ .read_snr = cxd2820r_read_snr, ++ .read_ber = cxd2820r_read_ber, ++ .read_ucblocks = cxd2820r_read_ucblocks, ++ .read_signal_strength = cxd2820r_read_signal_strength, ++}; ++ ++struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, ++ struct i2c_adapter *i2c, int *gpio_chip_base ++) ++{ ++ struct cxd2820r_priv *priv; ++ int ret; ++ u8 tmp; ++ ++ priv = kzalloc(sizeof(struct cxd2820r_priv), GFP_KERNEL); ++ if (!priv) { ++ ret = -ENOMEM; ++ dev_err(&i2c->dev, "%s: kzalloc() failed\n", ++ KBUILD_MODNAME); ++ goto error; ++ } ++ ++ priv->i2c = i2c; ++ memcpy(&priv->cfg, cfg, sizeof(struct cxd2820r_config)); ++ memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof(struct dvb_frontend_ops)); ++ priv->fe.demodulator_priv = priv; ++ ++ priv->bank[0] = priv->bank[1] = 0xff; ++ ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp); ++ dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, tmp); ++ if (ret || tmp != 0xe1) ++ goto error; ++ ++ if (gpio_chip_base) { ++#ifdef CONFIG_GPIOLIB ++ /* add GPIOs */ ++ priv->gpio_chip.label = KBUILD_MODNAME; ++ priv->gpio_chip.dev = &priv->i2c->dev; ++ priv->gpio_chip.owner = THIS_MODULE; ++ priv->gpio_chip.direction_output = ++ cxd2820r_gpio_direction_output; ++ priv->gpio_chip.set = cxd2820r_gpio_set; ++ priv->gpio_chip.get = cxd2820r_gpio_get; ++ priv->gpio_chip.base = -1; /* dynamic allocation */ ++ priv->gpio_chip.ngpio = GPIO_COUNT; ++ priv->gpio_chip.can_sleep = 1; ++ ret = gpiochip_add(&priv->gpio_chip); ++ if (ret) ++ goto error; ++ ++ dev_dbg(&priv->i2c->dev, "%s: gpio_chip.base=%d\n", __func__, ++ priv->gpio_chip.base); ++ ++ *gpio_chip_base = priv->gpio_chip.base; ++#else ++ /* ++ * Use static GPIO configuration if GPIOLIB is undefined. ++ * This is fallback condition. ++ */ ++ u8 gpio[GPIO_COUNT]; ++ gpio[0] = (*gpio_chip_base >> 0) & 0x07; ++ gpio[1] = (*gpio_chip_base >> 3) & 0x07; ++ gpio[2] = 0; ++ ret = cxd2820r_gpio(&priv->fe, gpio); ++ if (ret) ++ goto error; ++#endif ++ } ++ ++ return &priv->fe; ++error: ++ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(cxd2820r_attach); ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Sony CXD2820R demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/cxd2820r_priv.h b/drivers/media/dvb-frontends/cxd2820r_priv.h +new file mode 100644 +index 0000000..7ff5f60 +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r_priv.h +@@ -0,0 +1,148 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#ifndef CXD2820R_PRIV_H ++#define CXD2820R_PRIV_H ++ ++#include ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "cxd2820r.h" ++#include ++ ++struct reg_val_mask { ++ u32 reg; ++ u8 val; ++ u8 mask; ++}; ++ ++struct cxd2820r_priv { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct cxd2820r_config cfg; ++ ++ bool ber_running; ++ ++ u8 bank[2]; ++#define GPIO_COUNT 3 ++ u8 gpio[GPIO_COUNT]; ++#ifdef CONFIG_GPIOLIB ++ struct gpio_chip gpio_chip; ++#endif ++ ++ fe_delivery_system_t delivery_system; ++ bool last_tune_failed; /* for switch between T and T2 tune */ ++}; ++ ++/* cxd2820r_core.c */ ++ ++extern int cxd2820r_debug; ++ ++int cxd2820r_gpio(struct dvb_frontend *fe, u8 *gpio); ++ ++int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, ++ u8 mask); ++ ++int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, ++ int len); ++ ++u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor); ++ ++int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, ++ int len); ++ ++int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, ++ int len); ++ ++int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val); ++ ++int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val); ++ ++/* cxd2820r_c.c */ ++ ++int cxd2820r_get_frontend_c(struct dvb_frontend *fe); ++ ++int cxd2820r_set_frontend_c(struct dvb_frontend *fe); ++ ++int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status); ++ ++int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber); ++ ++int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength); ++ ++int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr); ++ ++int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks); ++ ++int cxd2820r_init_c(struct dvb_frontend *fe); ++ ++int cxd2820r_sleep_c(struct dvb_frontend *fe); ++ ++int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s); ++ ++/* cxd2820r_t.c */ ++ ++int cxd2820r_get_frontend_t(struct dvb_frontend *fe); ++ ++int cxd2820r_set_frontend_t(struct dvb_frontend *fe); ++ ++int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status); ++ ++int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber); ++ ++int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength); ++ ++int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr); ++ ++int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks); ++ ++int cxd2820r_init_t(struct dvb_frontend *fe); ++ ++int cxd2820r_sleep_t(struct dvb_frontend *fe); ++ ++int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s); ++ ++/* cxd2820r_t2.c */ ++ ++int cxd2820r_get_frontend_t2(struct dvb_frontend *fe); ++ ++int cxd2820r_set_frontend_t2(struct dvb_frontend *fe); ++ ++int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status); ++ ++int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber); ++ ++int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength); ++ ++int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr); ++ ++int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks); ++ ++int cxd2820r_init_t2(struct dvb_frontend *fe); ++ ++int cxd2820r_sleep_t2(struct dvb_frontend *fe); ++ ++int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s); ++ ++#endif /* CXD2820R_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/cxd2820r_t.c b/drivers/media/dvb-frontends/cxd2820r_t.c +new file mode 100644 +index 0000000..fa184ca +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r_t.c +@@ -0,0 +1,449 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "cxd2820r_priv.h" ++ ++int cxd2820r_set_frontend_t(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i, bw_i; ++ u32 if_freq, if_ctl; ++ u64 num; ++ u8 buf[3], bw_param; ++ u8 bw_params1[][5] = { ++ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ ++ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ ++ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ ++ }; ++ u8 bw_params2[][2] = { ++ { 0x1f, 0xdc }, /* 6 MHz */ ++ { 0x12, 0xf8 }, /* 7 MHz */ ++ { 0x01, 0xe0 }, /* 8 MHz */ ++ }; ++ struct reg_val_mask tab[] = { ++ { 0x00080, 0x00, 0xff }, ++ { 0x00081, 0x03, 0xff }, ++ { 0x00085, 0x07, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ ++ { 0x00070, priv->cfg.ts_mode, 0xff }, ++ { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, ++ { 0x000a5, 0x00, 0x01 }, ++ { 0x00082, 0x20, 0x60 }, ++ { 0x000c2, 0xc3, 0xff }, ++ { 0x0016a, 0x50, 0xff }, ++ { 0x00427, 0x41, 0xff }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__, ++ c->frequency, c->bandwidth_hz); ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ bw_i = 0; ++ bw_param = 2; ++ break; ++ case 7000000: ++ bw_i = 1; ++ bw_param = 1; ++ break; ++ case 8000000: ++ bw_i = 2; ++ bw_param = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ if (priv->delivery_system != SYS_DVBT) { ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, ++ tab[i].val, tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ } ++ ++ priv->delivery_system = SYS_DVBT; ++ priv->ber_running = 0; /* tune stops BER counter */ ++ ++ /* program IF frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) { ++ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); ++ if (ret) ++ goto error; ++ } else ++ if_freq = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); ++ ++ num = if_freq / 1000; /* Hz => kHz */ ++ num *= 0x1000000; ++ if_ctl = cxd2820r_div_u64_round_closest(num, 41000); ++ buf[0] = ((if_ctl >> 16) & 0xff); ++ buf[1] = ((if_ctl >> 8) & 0xff); ++ buf[2] = ((if_ctl >> 0) & 0xff); ++ ++ ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[bw_i], 5); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[bw_i], 2); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_get_frontend_t(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[2]; ++ ++ ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 6) & 0x03) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ } ++ ++ switch ((buf[1] >> 1) & 0x03) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ ++ switch ((buf[1] >> 3) & 0x03) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((buf[0] >> 3) & 0x07) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ switch ((buf[0] >> 0) & 0x07) { ++ case 0: ++ c->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ switch ((buf[1] >> 5) & 0x07) { ++ case 0: ++ c->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 0) & 0x01) { ++ case 0: ++ c->inversion = INVERSION_OFF; ++ break; ++ case 1: ++ c->inversion = INVERSION_ON; ++ break; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[3], start_ber = 0; ++ *ber = 0; ++ ++ if (priv->ber_running) { ++ ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { ++ *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; ++ start_ber = 1; ++ } ++ } else { ++ priv->ber_running = 1; ++ start_ber = 1; ++ } ++ ++ if (start_ber) { ++ /* (re)start BER */ ++ ret = cxd2820r_wr_reg(priv, 0x00079, 0x01); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 tmp; ++ ++ ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ tmp = (buf[0] & 0x0f) << 8 | buf[1]; ++ tmp = ~tmp & 0x0fff; ++ ++ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ ++ *strength = tmp * 0xffff / 0x0fff; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 tmp; ++ /* report SNR in dB * 10 */ ++ ++ ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ tmp = (buf[0] & 0x1f) << 8 | buf[1]; ++ #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ ++ if (tmp) ++ *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) ++ / 100); ++ else ++ *snr = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr, ++ tmp); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ /* no way to read ? */ ++ return 0; ++} ++ ++int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[4]; ++ *status = 0; ++ ++ ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]); ++ if (ret) ++ goto error; ++ ++ if ((buf[0] & 0x07) == 6) { ++ ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]); ++ if (ret) ++ goto error; ++ ++ if (((buf[1] >> 3) & 0x01) == 1) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } else { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC; ++ } ++ } else { ++ ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]); ++ if (ret) ++ goto error; ++ ++ if ((buf[2] & 0x0f) >= 4) { ++ ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]); ++ if (ret) ++ goto error; ++ ++ if (((buf[3] >> 4) & 0x01) == 1) ++ *status |= FE_HAS_SIGNAL; ++ } ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: lock=%*ph\n", __func__, 4, buf); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_init_t(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ ++ ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_sleep_t(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret, i; ++ struct reg_val_mask tab[] = { ++ { 0x000ff, 0x1f, 0xff }, ++ { 0x00085, 0x00, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ { 0x00081, 0x00, 0xff }, ++ { 0x00080, 0x00, 0xff }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s\n", __func__); ++ ++ priv->delivery_system = SYS_UNDEFINED; ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 500; ++ s->step_size = fe->ops.info.frequency_stepsize * 2; ++ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; ++ ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/cxd2820r_t2.c b/drivers/media/dvb-frontends/cxd2820r_t2.c +new file mode 100644 +index 0000000..e82d82a +--- /dev/null ++++ b/drivers/media/dvb-frontends/cxd2820r_t2.c +@@ -0,0 +1,423 @@ ++/* ++ * Sony CXD2820R demodulator driver ++ * ++ * Copyright (C) 2010 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++#include "cxd2820r_priv.h" ++ ++int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i, bw_i; ++ u32 if_freq, if_ctl; ++ u64 num; ++ u8 buf[3], bw_param; ++ u8 bw_params1[][5] = { ++ { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */ ++ { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ ++ { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ ++ { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ ++ }; ++ struct reg_val_mask tab[] = { ++ { 0x00080, 0x02, 0xff }, ++ { 0x00081, 0x20, 0xff }, ++ { 0x00085, 0x07, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ { 0x02069, 0x01, 0xff }, ++ ++ { 0x0207f, 0x2a, 0xff }, ++ { 0x02082, 0x0a, 0xff }, ++ { 0x02083, 0x0a, 0xff }, ++ { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 }, ++ { 0x02070, priv->cfg.ts_mode, 0xff }, ++ { 0x020b5, priv->cfg.spec_inv << 4, 0x10 }, ++ { 0x02567, 0x07, 0x0f }, ++ { 0x02569, 0x03, 0x03 }, ++ { 0x02595, 0x1a, 0xff }, ++ { 0x02596, 0x50, 0xff }, ++ { 0x02a8c, 0x00, 0xff }, ++ { 0x02a8d, 0x34, 0xff }, ++ { 0x02a45, 0x06, 0x07 }, ++ { 0x03f10, 0x0d, 0xff }, ++ { 0x03f11, 0x02, 0xff }, ++ { 0x03f12, 0x01, 0xff }, ++ { 0x03f23, 0x2c, 0xff }, ++ { 0x03f51, 0x13, 0xff }, ++ { 0x03f52, 0x01, 0xff }, ++ { 0x03f53, 0x00, 0xff }, ++ { 0x027e6, 0x14, 0xff }, ++ { 0x02786, 0x02, 0x07 }, ++ { 0x02787, 0x40, 0xe0 }, ++ { 0x027ef, 0x10, 0x18 }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", __func__, ++ c->frequency, c->bandwidth_hz); ++ ++ switch (c->bandwidth_hz) { ++ case 5000000: ++ bw_i = 0; ++ bw_param = 3; ++ break; ++ case 6000000: ++ bw_i = 1; ++ bw_param = 2; ++ break; ++ case 7000000: ++ bw_i = 2; ++ bw_param = 1; ++ break; ++ case 8000000: ++ bw_i = 3; ++ bw_param = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ if (priv->delivery_system != SYS_DVBT2) { ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, ++ tab[i].val, tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ } ++ ++ priv->delivery_system = SYS_DVBT2; ++ ++ /* program IF frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) { ++ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); ++ if (ret) ++ goto error; ++ } else ++ if_freq = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d\n", __func__, if_freq); ++ ++ num = if_freq / 1000; /* Hz => kHz */ ++ num *= 0x1000000; ++ if_ctl = cxd2820r_div_u64_round_closest(num, 41000); ++ buf[0] = ((if_ctl >> 16) & 0xff); ++ buf[1] = ((if_ctl >> 8) & 0xff); ++ buf[2] = ((if_ctl >> 0) & 0xff); ++ ++ ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[bw_i], 5); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); ++ if (ret) ++ goto error; ++ ++ ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++ ++} ++ ++int cxd2820r_get_frontend_t2(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[2]; ++ ++ ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 0) & 0x07) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ case 2: ++ c->transmission_mode = TRANSMISSION_MODE_4K; ++ break; ++ case 3: ++ c->transmission_mode = TRANSMISSION_MODE_1K; ++ break; ++ case 4: ++ c->transmission_mode = TRANSMISSION_MODE_16K; ++ break; ++ case 5: ++ c->transmission_mode = TRANSMISSION_MODE_32K; ++ break; ++ } ++ ++ switch ((buf[1] >> 4) & 0x07) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ case 4: ++ c->guard_interval = GUARD_INTERVAL_1_128; ++ break; ++ case 5: ++ c->guard_interval = GUARD_INTERVAL_19_128; ++ break; ++ case 6: ++ c->guard_interval = GUARD_INTERVAL_19_256; ++ break; ++ } ++ ++ ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 0) & 0x07) { ++ case 0: ++ c->fec_inner = FEC_1_2; ++ break; ++ case 1: ++ c->fec_inner = FEC_3_5; ++ break; ++ case 2: ++ c->fec_inner = FEC_2_3; ++ break; ++ case 3: ++ c->fec_inner = FEC_3_4; ++ break; ++ case 4: ++ c->fec_inner = FEC_4_5; ++ break; ++ case 5: ++ c->fec_inner = FEC_5_6; ++ break; ++ } ++ ++ switch ((buf[1] >> 0) & 0x07) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ case 3: ++ c->modulation = QAM_256; ++ break; ++ } ++ ++ ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]); ++ if (ret) ++ goto error; ++ ++ switch ((buf[0] >> 4) & 0x01) { ++ case 0: ++ c->inversion = INVERSION_OFF; ++ break; ++ case 1: ++ c->inversion = INVERSION_ON; ++ break; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[1]; ++ *status = 0; ++ ++ ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]); ++ if (ret) ++ goto error; ++ ++ if ((buf[0] & 0x07) == 6) { ++ if (((buf[0] >> 5) & 0x01) == 1) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } else { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC; ++ } ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: lock=%02x\n", __func__, buf[0]); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[4]; ++ unsigned int errbits; ++ *ber = 0; ++ /* FIXME: correct calculation */ ++ ++ ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ if ((buf[0] >> 4) & 0x01) { ++ errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 | ++ buf[2] << 8 | buf[3]; ++ ++ if (errbits) ++ *ber = errbits * 64 / 16588800; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 tmp; ++ ++ ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ tmp = (buf[0] & 0x0f) << 8 | buf[1]; ++ tmp = ~tmp & 0x0fff; ++ ++ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ ++ *strength = tmp * 0xffff / 0x0fff; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 tmp; ++ /* report SNR in dB * 10 */ ++ ++ ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf)); ++ if (ret) ++ goto error; ++ ++ tmp = (buf[0] & 0x0f) << 8 | buf[1]; ++ #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ ++ if (tmp) ++ *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) ++ / 100); ++ else ++ *snr = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s: dBx10=%d val=%04x\n", __func__, *snr, ++ tmp); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ /* no way to read ? */ ++ return 0; ++} ++ ++int cxd2820r_sleep_t2(struct dvb_frontend *fe) ++{ ++ struct cxd2820r_priv *priv = fe->demodulator_priv; ++ int ret, i; ++ struct reg_val_mask tab[] = { ++ { 0x000ff, 0x1f, 0xff }, ++ { 0x00085, 0x00, 0xff }, ++ { 0x00088, 0x01, 0xff }, ++ { 0x02069, 0x00, 0xff }, ++ { 0x00081, 0x00, 0xff }, ++ { 0x00080, 0x00, 0xff }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ ++ priv->delivery_system = SYS_UNDEFINED; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 1500; ++ s->step_size = fe->ops.info.frequency_stepsize * 2; ++ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; ++ ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c +new file mode 100644 +index 0000000..3b024bf +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib0070.c +@@ -0,0 +1,780 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. ++ * ++ * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * This code is more or less generated from another driver, please ++ * excuse some codingstyle oddities. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "dib0070.h" ++#include "dibx000_common.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "DiB0070: "); \ ++ printk(args); \ ++ printk("\n"); \ ++ } \ ++} while (0) ++ ++#define DIB0070_P1D 0x00 ++#define DIB0070_P1F 0x01 ++#define DIB0070_P1G 0x03 ++#define DIB0070S_P1A 0x02 ++ ++struct dib0070_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend *fe; ++ const struct dib0070_config *cfg; ++ u16 wbd_ff_offset; ++ u8 revision; ++ ++ enum frontend_tune_state tune_state; ++ u32 current_rf; ++ ++ /* for the captrim binary search */ ++ s8 step; ++ u16 adc_diff; ++ ++ s8 captrim; ++ s8 fcaptrim; ++ u16 lo4; ++ ++ const struct dib0070_tuning *current_tune_table_index; ++ const struct dib0070_lna_match *lna_match; ++ ++ u8 wbd_gain_current; ++ u16 wbd_offset_3_3[2]; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[3]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++}; ++ ++static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = reg; ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->cfg->i2c_address; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 1; ++ state->msg[1].addr = state->cfg->i2c_address; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = state->i2c_read_buffer; ++ state->msg[1].len = 2; ++ ++ if (i2c_transfer(state->i2c, state->msg, 2) != 2) { ++ printk(KERN_WARNING "DiB0070 I2C read failed\n"); ++ ret = 0; ++ } else ++ ret = (state->i2c_read_buffer[0] << 8) ++ | state->i2c_read_buffer[1]; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ state->i2c_write_buffer[0] = reg; ++ state->i2c_write_buffer[1] = val >> 8; ++ state->i2c_write_buffer[2] = val & 0xff; ++ ++ memset(state->msg, 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->cfg->i2c_address; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 3; ++ ++ if (i2c_transfer(state->i2c, state->msg, 1) != 1) { ++ printk(KERN_WARNING "DiB0070 I2C write failed\n"); ++ ret = -EREMOTEIO; ++ } else ++ ret = 0; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++#define HARD_RESET(state) do { \ ++ state->cfg->sleep(state->fe, 0); \ ++ if (state->cfg->reset) { \ ++ state->cfg->reset(state->fe,1); msleep(10); \ ++ state->cfg->reset(state->fe,0); msleep(10); \ ++ } \ ++} while (0) ++ ++static int dib0070_set_bandwidth(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; ++ ++ if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000) ++ tmp |= (0 << 14); ++ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000) ++ tmp |= (1 << 14); ++ else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000) ++ tmp |= (2 << 14); ++ else ++ tmp |= (3 << 14); ++ ++ dib0070_write_reg(state, 0x02, tmp); ++ ++ /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ ++ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { ++ u16 value = dib0070_read_reg(state, 0x17); ++ ++ dib0070_write_reg(state, 0x17, value & 0xfffc); ++ tmp = dib0070_read_reg(state, 0x01) & 0x01ff; ++ dib0070_write_reg(state, 0x01, tmp | (60 << 9)); ++ ++ dib0070_write_reg(state, 0x17, value); ++ } ++ return 0; ++} ++ ++static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state) ++{ ++ int8_t step_sign; ++ u16 adc; ++ int ret = 0; ++ ++ if (*tune_state == CT_TUNER_STEP_0) { ++ ++ dib0070_write_reg(state, 0x0f, 0xed10); ++ dib0070_write_reg(state, 0x17, 0x0034); ++ ++ dib0070_write_reg(state, 0x18, 0x0032); ++ state->step = state->captrim = state->fcaptrim = 64; ++ state->adc_diff = 3000; ++ ret = 20; ++ ++ *tune_state = CT_TUNER_STEP_1; ++ } else if (*tune_state == CT_TUNER_STEP_1) { ++ state->step /= 2; ++ dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); ++ ret = 15; ++ ++ *tune_state = CT_TUNER_STEP_2; ++ } else if (*tune_state == CT_TUNER_STEP_2) { ++ ++ adc = dib0070_read_reg(state, 0x19); ++ ++ dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc*(u32)1800/(u32)1024); ++ ++ if (adc >= 400) { ++ adc -= 400; ++ step_sign = -1; ++ } else { ++ adc = 400 - adc; ++ step_sign = 1; ++ } ++ ++ if (adc < state->adc_diff) { ++ dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff); ++ state->adc_diff = adc; ++ state->fcaptrim = state->captrim; ++ ++ ++ ++ } ++ state->captrim += (step_sign * state->step); ++ ++ if (state->step >= 1) ++ *tune_state = CT_TUNER_STEP_1; ++ else ++ *tune_state = CT_TUNER_STEP_3; ++ ++ } else if (*tune_state == CT_TUNER_STEP_3) { ++ dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim); ++ dib0070_write_reg(state, 0x18, 0x07ff); ++ *tune_state = CT_TUNER_STEP_4; ++ } ++ ++ return ret; ++} ++ ++static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); ++ dprintk("CTRL_LO5: 0x%x", lo5); ++ return dib0070_write_reg(state, 0x15, lo5); ++} ++ ++void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ ++ if (open) { ++ dib0070_write_reg(state, 0x1b, 0xff00); ++ dib0070_write_reg(state, 0x1a, 0x0000); ++ } else { ++ dib0070_write_reg(state, 0x1b, 0x4112); ++ if (state->cfg->vga_filter != 0) { ++ dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); ++ dprintk("vga filter register is set to %x", state->cfg->vga_filter); ++ } else ++ dib0070_write_reg(state, 0x1a, 0x0009); ++ } ++} ++ ++EXPORT_SYMBOL(dib0070_ctrl_agc_filter); ++struct dib0070_tuning { ++ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ ++ u8 switch_trim; ++ u8 vco_band; ++ u8 hfdiv; ++ u8 vco_multi; ++ u8 presc; ++ u8 wbdmux; ++ u16 tuner_enable; ++}; ++ ++struct dib0070_lna_match { ++ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ ++ u8 lna_band; ++}; ++ ++static const struct dib0070_tuning dib0070s_tuning_table[] = { ++ { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ ++ { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, ++ { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, ++ { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ ++ { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, ++ { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, ++ { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ ++}; ++ ++static const struct dib0070_tuning dib0070_tuning_table[] = { ++ { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ ++ { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ ++ { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, ++ { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, ++ { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ ++ { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 }, ++ { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, ++ { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ ++}; ++ ++static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { ++ { 180000, 0 }, /* VHF */ ++ { 188000, 1 }, ++ { 196400, 2 }, ++ { 250000, 3 }, ++ { 550000, 0 }, /* UHF */ ++ { 590000, 1 }, ++ { 666000, 3 }, ++ { 864000, 5 }, ++ { 1500000, 0 }, /* LBAND or everything higher than UHF */ ++ { 1600000, 1 }, ++ { 2000000, 3 }, ++ { 0xffffffff, 7 }, ++}; ++ ++static const struct dib0070_lna_match dib0070_lna[] = { ++ { 180000, 0 }, /* VHF */ ++ { 188000, 1 }, ++ { 196400, 2 }, ++ { 250000, 3 }, ++ { 550000, 2 }, /* UHF */ ++ { 650000, 3 }, ++ { 750000, 5 }, ++ { 850000, 6 }, ++ { 864000, 7 }, ++ { 1500000, 0 }, /* LBAND or everything higher than UHF */ ++ { 1600000, 1 }, ++ { 2000000, 3 }, ++ { 0xffffffff, 7 }, ++}; ++ ++#define LPF 100 ++static int dib0070_tune_digital(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ ++ const struct dib0070_tuning *tune; ++ const struct dib0070_lna_match *lna_match; ++ ++ enum frontend_tune_state *tune_state = &state->tune_state; ++ int ret = 10; /* 1ms is the default delay most of the time */ ++ ++ u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000); ++ u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); ++ ++#ifdef CONFIG_SYS_ISDBT ++ if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) ++ if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) ++ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) ++ || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) ++ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) ++ || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) ++ && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) ++ freq += 850; ++#endif ++ if (state->current_rf != freq) { ++ ++ switch (state->revision) { ++ case DIB0070S_P1A: ++ tune = dib0070s_tuning_table; ++ lna_match = dib0070_lna; ++ break; ++ default: ++ tune = dib0070_tuning_table; ++ if (state->cfg->flip_chip) ++ lna_match = dib0070_lna_flip_chip; ++ else ++ lna_match = dib0070_lna; ++ break; ++ } ++ while (freq > tune->max_freq) /* find the right one */ ++ tune++; ++ while (freq > lna_match->max_freq) /* find the right one */ ++ lna_match++; ++ ++ state->current_tune_table_index = tune; ++ state->lna_match = lna_match; ++ } ++ ++ if (*tune_state == CT_TUNER_START) { ++ dprintk("Tuning for Band: %hd (%d kHz)", band, freq); ++ if (state->current_rf != freq) { ++ u8 REFDIV; ++ u32 FBDiv, Rest, FREF, VCOF_kHz; ++ u8 Den; ++ ++ state->current_rf = freq; ++ state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); ++ ++ ++ dib0070_write_reg(state, 0x17, 0x30); ++ ++ ++ VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; ++ ++ switch (band) { ++ case BAND_VHF: ++ REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); ++ break; ++ case BAND_FM: ++ REFDIV = (u8) ((state->cfg->clock_khz) / 1000); ++ break; ++ default: ++ REFDIV = (u8) (state->cfg->clock_khz / 10000); ++ break; ++ } ++ FREF = state->cfg->clock_khz / REFDIV; ++ ++ ++ ++ switch (state->revision) { ++ case DIB0070S_P1A: ++ FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); ++ Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; ++ break; ++ ++ case DIB0070_P1G: ++ case DIB0070_P1F: ++ default: ++ FBDiv = (freq / (FREF / 2)); ++ Rest = 2 * freq - FBDiv * FREF; ++ break; ++ } ++ ++ if (Rest < LPF) ++ Rest = 0; ++ else if (Rest < 2 * LPF) ++ Rest = 2 * LPF; ++ else if (Rest > (FREF - LPF)) { ++ Rest = 0; ++ FBDiv += 1; ++ } else if (Rest > (FREF - 2 * LPF)) ++ Rest = FREF - 2 * LPF; ++ Rest = (Rest * 6528) / (FREF / 10); ++ ++ Den = 1; ++ if (Rest > 0) { ++ state->lo4 |= (1 << 14) | (1 << 12); ++ Den = 255; ++ } ++ ++ ++ dib0070_write_reg(state, 0x11, (u16)FBDiv); ++ dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); ++ dib0070_write_reg(state, 0x13, (u16) Rest); ++ ++ if (state->revision == DIB0070S_P1A) { ++ ++ if (band == BAND_SBAND) { ++ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); ++ dib0070_write_reg(state, 0x1d, 0xFFFF); ++ } else ++ dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); ++ } ++ ++ dib0070_write_reg(state, 0x20, ++ 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); ++ ++ dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); ++ dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); ++ dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); ++ dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); ++ dprintk("VCO = %hd", state->current_tune_table_index->vco_band); ++ dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); ++ ++ *tune_state = CT_TUNER_STEP_0; ++ } else { /* we are already tuned to this frequency - the configuration is correct */ ++ ret = 50; /* wakeup time */ ++ *tune_state = CT_TUNER_STEP_5; ++ } ++ } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { ++ ++ ret = dib0070_captrim(state, tune_state); ++ ++ } else if (*tune_state == CT_TUNER_STEP_4) { ++ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; ++ if (tmp != NULL) { ++ while (freq/1000 > tmp->freq) /* find the right one */ ++ tmp++; ++ dib0070_write_reg(state, 0x0f, ++ (0 << 15) | (1 << 14) | (3 << 12) ++ | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) ++ | (state->current_tune_table_index->wbdmux << 0)); ++ state->wbd_gain_current = tmp->wbd_gain_val; ++ } else { ++ dib0070_write_reg(state, 0x0f, ++ (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index-> ++ wbdmux << 0)); ++ state->wbd_gain_current = 6; ++ } ++ ++ dib0070_write_reg(state, 0x06, 0x3fff); ++ dib0070_write_reg(state, 0x07, ++ (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); ++ dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); ++ dib0070_write_reg(state, 0x0d, 0x0d80); ++ ++ ++ dib0070_write_reg(state, 0x18, 0x07ff); ++ dib0070_write_reg(state, 0x17, 0x0033); ++ ++ ++ *tune_state = CT_TUNER_STEP_5; ++ } else if (*tune_state == CT_TUNER_STEP_5) { ++ dib0070_set_bandwidth(fe); ++ *tune_state = CT_TUNER_STOP; ++ } else { ++ ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ ++ } ++ return ret; ++} ++ ++ ++static int dib0070_tune(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ uint32_t ret; ++ ++ state->tune_state = CT_TUNER_START; ++ ++ do { ++ ret = dib0070_tune_digital(fe); ++ if (ret != FE_CALLBACK_TIME_NEVER) ++ msleep(ret/10); ++ else ++ break; ++ } while (state->tune_state != CT_TUNER_STOP); ++ ++ return 0; ++} ++ ++static int dib0070_wakeup(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ if (state->cfg->sleep) ++ state->cfg->sleep(fe, 0); ++ return 0; ++} ++ ++static int dib0070_sleep(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ if (state->cfg->sleep) ++ state->cfg->sleep(fe, 1); ++ return 0; ++} ++ ++u8 dib0070_get_rf_output(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ return (dib0070_read_reg(state, 0x07) >> 11) & 0x3; ++} ++EXPORT_SYMBOL(dib0070_get_rf_output); ++ ++int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff; ++ if (no > 3) ++ no = 3; ++ if (no < 1) ++ no = 1; ++ return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11)); ++} ++EXPORT_SYMBOL(dib0070_set_rf_output); ++ ++static const u16 dib0070_p1f_defaults[] = ++ ++{ ++ 7, 0x02, ++ 0x0008, ++ 0x0000, ++ 0x0000, ++ 0x0000, ++ 0x0000, ++ 0x0002, ++ 0x0100, ++ ++ 3, 0x0d, ++ 0x0d80, ++ 0x0001, ++ 0x0000, ++ ++ 4, 0x11, ++ 0x0000, ++ 0x0103, ++ 0x0000, ++ 0x0000, ++ ++ 3, 0x16, ++ 0x0004 | 0x0040, ++ 0x0030, ++ 0x07ff, ++ ++ 6, 0x1b, ++ 0x4112, ++ 0xff00, ++ 0xc07f, ++ 0x0000, ++ 0x0180, ++ 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, ++ ++ 0, ++}; ++ ++static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) ++{ ++ u16 tuner_en = dib0070_read_reg(state, 0x20); ++ u16 offset; ++ ++ dib0070_write_reg(state, 0x18, 0x07ff); ++ dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); ++ dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); ++ msleep(9); ++ offset = dib0070_read_reg(state, 0x19); ++ dib0070_write_reg(state, 0x20, tuner_en); ++ return offset; ++} ++ ++static void dib0070_wbd_offset_calibration(struct dib0070_state *state) ++{ ++ u8 gain; ++ for (gain = 6; gain < 8; gain++) { ++ state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); ++ dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); ++ } ++} ++ ++u16 dib0070_wbd_offset(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; ++ u32 freq = fe->dtv_property_cache.frequency/1000; ++ ++ if (tmp != NULL) { ++ while (freq/1000 > tmp->freq) /* find the right one */ ++ tmp++; ++ state->wbd_gain_current = tmp->wbd_gain_val; ++ } else ++ state->wbd_gain_current = 6; ++ ++ return state->wbd_offset_3_3[state->wbd_gain_current - 6]; ++} ++EXPORT_SYMBOL(dib0070_wbd_offset); ++ ++#define pgm_read_word(w) (*w) ++static int dib0070_reset(struct dvb_frontend *fe) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ u16 l, r, *n; ++ ++ HARD_RESET(state); ++ ++ ++#ifndef FORCE_SBAND_TUNER ++ if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) ++ state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; ++ else ++#else ++#warning forcing SBAND ++#endif ++ state->revision = DIB0070S_P1A; ++ ++ /* P1F or not */ ++ dprintk("Revision: %x", state->revision); ++ ++ if (state->revision == DIB0070_P1D) { ++ dprintk("Error: this driver is not to be used meant for P1D or earlier"); ++ return -EINVAL; ++ } ++ ++ n = (u16 *) dib0070_p1f_defaults; ++ l = pgm_read_word(n++); ++ while (l) { ++ r = pgm_read_word(n++); ++ do { ++ dib0070_write_reg(state, (u8)r, pgm_read_word(n++)); ++ r++; ++ } while (--l); ++ l = pgm_read_word(n++); ++ } ++ ++ if (state->cfg->force_crystal_mode != 0) ++ r = state->cfg->force_crystal_mode; ++ else if (state->cfg->clock_khz >= 24000) ++ r = 1; ++ else ++ r = 2; ++ ++ ++ r |= state->cfg->osc_buffer_state << 3; ++ ++ dib0070_write_reg(state, 0x10, r); ++ dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); ++ ++ if (state->cfg->invert_iq) { ++ r = dib0070_read_reg(state, 0x02) & 0xffdf; ++ dib0070_write_reg(state, 0x02, r | (1 << 5)); ++ } ++ ++ if (state->revision == DIB0070S_P1A) ++ dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); ++ else ++ dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); ++ ++ dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); ++ ++ dib0070_wbd_offset_calibration(state); ++ ++ return 0; ++} ++ ++static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct dib0070_state *state = fe->tuner_priv; ++ ++ *frequency = 1000 * state->current_rf; ++ return 0; ++} ++ ++static int dib0070_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static const struct dvb_tuner_ops dib0070_ops = { ++ .info = { ++ .name = "DiBcom DiB0070", ++ .frequency_min = 45000000, ++ .frequency_max = 860000000, ++ .frequency_step = 1000, ++ }, ++ .release = dib0070_release, ++ ++ .init = dib0070_wakeup, ++ .sleep = dib0070_sleep, ++ .set_params = dib0070_tune, ++ ++ .get_frequency = dib0070_get_frequency, ++// .get_bandwidth = dib0070_get_bandwidth ++}; ++ ++struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) ++{ ++ struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ ++ state->cfg = cfg; ++ state->i2c = i2c; ++ state->fe = fe; ++ mutex_init(&state->i2c_buffer_lock); ++ fe->tuner_priv = state; ++ ++ if (dib0070_reset(fe) != 0) ++ goto free_mem; ++ ++ printk(KERN_INFO "DiB0070: successfully identified\n"); ++ memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); ++ ++ fe->tuner_priv = state; ++ return fe; ++ ++free_mem: ++ kfree(state); ++ fe->tuner_priv = NULL; ++ return NULL; ++} ++EXPORT_SYMBOL(dib0070_attach); ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib0070.h b/drivers/media/dvb-frontends/dib0070.h +new file mode 100644 +index 0000000..45c31fa +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib0070.h +@@ -0,0 +1,76 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. ++ * ++ * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#ifndef DIB0070_H ++#define DIB0070_H ++ ++struct dvb_frontend; ++struct i2c_adapter; ++ ++#define DEFAULT_DIB0070_I2C_ADDRESS 0x60 ++ ++struct dib0070_wbd_gain_cfg { ++ u16 freq; ++ u16 wbd_gain_val; ++}; ++ ++struct dib0070_config { ++ u8 i2c_address; ++ ++ /* tuner pins controlled externally */ ++ int (*reset) (struct dvb_frontend *, int); ++ int (*sleep) (struct dvb_frontend *, int); ++ ++ /* offset in kHz */ ++ int freq_offset_khz_uhf; ++ int freq_offset_khz_vhf; ++ ++ u8 osc_buffer_state; /* 0= normal, 1= tri-state */ ++ u32 clock_khz; ++ u8 clock_pad_drive; /* (Drive + 1) * 2mA */ ++ ++ u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ ++ ++ u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ ++ ++ u8 flip_chip; ++ u8 enable_third_order_filter; ++ u8 charge_pump; ++ ++ const struct dib0070_wbd_gain_cfg *wbd_gain; ++ ++ u8 vga_filter; ++}; ++ ++#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); ++extern u16 dib0070_wbd_offset(struct dvb_frontend *); ++extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); ++extern u8 dib0070_get_rf_output(struct dvb_frontend *fe); ++extern int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no); ++#else ++static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c +new file mode 100644 +index 0000000..d9fe60b +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib0090.c +@@ -0,0 +1,2686 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. ++ * ++ * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ++ * This code is more or less generated from another driver, please ++ * excuse some codingstyle oddities. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "dib0090.h" ++#include "dibx000_common.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "DiB0090: "); \ ++ printk(args); \ ++ printk("\n"); \ ++ } \ ++} while (0) ++ ++#define CONFIG_SYS_DVBT ++#define CONFIG_SYS_ISDBT ++#define CONFIG_BAND_CBAND ++#define CONFIG_BAND_VHF ++#define CONFIG_BAND_UHF ++#define CONFIG_DIB0090_USE_PWM_AGC ++ ++#define EN_LNA0 0x8000 ++#define EN_LNA1 0x4000 ++#define EN_LNA2 0x2000 ++#define EN_LNA3 0x1000 ++#define EN_MIX0 0x0800 ++#define EN_MIX1 0x0400 ++#define EN_MIX2 0x0200 ++#define EN_MIX3 0x0100 ++#define EN_IQADC 0x0040 ++#define EN_PLL 0x0020 ++#define EN_TX 0x0010 ++#define EN_BB 0x0008 ++#define EN_LO 0x0004 ++#define EN_BIAS 0x0001 ++ ++#define EN_IQANA 0x0002 ++#define EN_DIGCLK 0x0080 /* not in the 0x24 reg, only in 0x1b */ ++#define EN_CRYSTAL 0x0002 ++ ++#define EN_UHF 0x22E9 ++#define EN_VHF 0x44E9 ++#define EN_LBD 0x11E9 ++#define EN_SBD 0x44E9 ++#define EN_CAB 0x88E9 ++ ++/* Calibration defines */ ++#define DC_CAL 0x1 ++#define WBD_CAL 0x2 ++#define TEMP_CAL 0x4 ++#define CAPTRIM_CAL 0x8 ++ ++#define KROSUS_PLL_LOCKED 0x800 ++#define KROSUS 0x2 ++ ++/* Use those defines to identify SOC version */ ++#define SOC 0x02 ++#define SOC_7090_P1G_11R1 0x82 ++#define SOC_7090_P1G_21R1 0x8a ++#define SOC_8090_P1G_11R1 0x86 ++#define SOC_8090_P1G_21R1 0x8e ++ ++/* else use thos ones to check */ ++#define P1A_B 0x0 ++#define P1C 0x1 ++#define P1D_E_F 0x3 ++#define P1G 0x7 ++#define P1G_21R2 0xf ++ ++#define MP001 0x1 /* Single 9090/8096 */ ++#define MP005 0x4 /* Single Sband */ ++#define MP008 0x6 /* Dual diversity VHF-UHF-LBAND */ ++#define MP009 0x7 /* Dual diversity 29098 CBAND-UHF-LBAND-SBAND */ ++ ++#define pgm_read_word(w) (*w) ++ ++struct dc_calibration; ++ ++struct dib0090_tuning { ++ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ ++ u8 switch_trim; ++ u8 lna_tune; ++ u16 lna_bias; ++ u16 v2i; ++ u16 mix; ++ u16 load; ++ u16 tuner_enable; ++}; ++ ++struct dib0090_pll { ++ u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ ++ u8 vco_band; ++ u8 hfdiv_code; ++ u8 hfdiv; ++ u8 topresc; ++}; ++ ++struct dib0090_identity { ++ u8 version; ++ u8 product; ++ u8 p1g; ++ u8 in_soc; ++}; ++ ++struct dib0090_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend *fe; ++ const struct dib0090_config *config; ++ ++ u8 current_band; ++ enum frontend_tune_state tune_state; ++ u32 current_rf; ++ ++ u16 wbd_offset; ++ s16 wbd_target; /* in dB */ ++ ++ s16 rf_gain_limit; /* take-over-point: where to split between bb and rf gain */ ++ s16 current_gain; /* keeps the currently programmed gain */ ++ u8 agc_step; /* new binary search */ ++ ++ u16 gain[2]; /* for channel monitoring */ ++ ++ const u16 *rf_ramp; ++ const u16 *bb_ramp; ++ ++ /* for the software AGC ramps */ ++ u16 bb_1_def; ++ u16 rf_lt_def; ++ u16 gain_reg[4]; ++ ++ /* for the captrim/dc-offset search */ ++ s8 step; ++ s16 adc_diff; ++ s16 min_adc_diff; ++ ++ s8 captrim; ++ s8 fcaptrim; ++ ++ const struct dc_calibration *dc; ++ u16 bb6, bb7; ++ ++ const struct dib0090_tuning *current_tune_table_index; ++ const struct dib0090_pll *current_pll_table_index; ++ ++ u8 tuner_is_tuned; ++ u8 agc_freeze; ++ ++ struct dib0090_identity identity; ++ ++ u32 rf_request; ++ u8 current_standard; ++ ++ u8 calibrate; ++ u32 rest; ++ u16 bias; ++ s16 temperature; ++ ++ u8 wbd_calibration_gain; ++ const struct dib0090_wbd_slope *current_wbd_table; ++ u16 wbdmux; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[3]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++}; ++ ++struct dib0090_fw_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend *fe; ++ struct dib0090_identity identity; ++ const struct dib0090_config *config; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg; ++ u8 i2c_write_buffer[2]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++}; ++ ++static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = reg; ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->config->i2c_address; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 1; ++ state->msg[1].addr = state->config->i2c_address; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = state->i2c_read_buffer; ++ state->msg[1].len = 2; ++ ++ if (i2c_transfer(state->i2c, state->msg, 2) != 2) { ++ printk(KERN_WARNING "DiB0090 I2C read failed\n"); ++ ret = 0; ++ } else ++ ret = (state->i2c_read_buffer[0] << 8) ++ | state->i2c_read_buffer[1]; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ state->i2c_write_buffer[0] = reg & 0xff; ++ state->i2c_write_buffer[1] = val >> 8; ++ state->i2c_write_buffer[2] = val & 0xff; ++ ++ memset(state->msg, 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->config->i2c_address; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 3; ++ ++ if (i2c_transfer(state->i2c, state->msg, 1) != 1) { ++ printk(KERN_WARNING "DiB0090 I2C write failed\n"); ++ ret = -EREMOTEIO; ++ } else ++ ret = 0; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = reg; ++ ++ memset(&state->msg, 0, sizeof(struct i2c_msg)); ++ state->msg.addr = reg; ++ state->msg.flags = I2C_M_RD; ++ state->msg.buf = state->i2c_read_buffer; ++ state->msg.len = 2; ++ if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { ++ printk(KERN_WARNING "DiB0090 I2C read failed\n"); ++ ret = 0; ++ } else ++ ret = (state->i2c_read_buffer[0] << 8) ++ | state->i2c_read_buffer[1]; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ state->i2c_write_buffer[0] = val >> 8; ++ state->i2c_write_buffer[1] = val & 0xff; ++ ++ memset(&state->msg, 0, sizeof(struct i2c_msg)); ++ state->msg.addr = reg; ++ state->msg.flags = 0; ++ state->msg.buf = state->i2c_write_buffer; ++ state->msg.len = 2; ++ if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { ++ printk(KERN_WARNING "DiB0090 I2C write failed\n"); ++ ret = -EREMOTEIO; ++ } else ++ ret = 0; ++ ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0) ++#define ADC_TARGET -220 ++#define GAIN_ALPHA 5 ++#define WBD_ALPHA 6 ++#define LPF 100 ++static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, u8 c) ++{ ++ do { ++ dib0090_write_reg(state, r++, *b++); ++ } while (--c); ++} ++ ++static int dib0090_identify(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ u16 v; ++ struct dib0090_identity *identity = &state->identity; ++ ++ v = dib0090_read_reg(state, 0x1a); ++ ++ identity->p1g = 0; ++ identity->in_soc = 0; ++ ++ dprintk("Tuner identification (Version = 0x%04x)", v); ++ ++ /* without PLL lock info */ ++ v &= ~KROSUS_PLL_LOCKED; ++ ++ identity->version = v & 0xff; ++ identity->product = (v >> 8) & 0xf; ++ ++ if (identity->product != KROSUS) ++ goto identification_error; ++ ++ if ((identity->version & 0x3) == SOC) { ++ identity->in_soc = 1; ++ switch (identity->version) { ++ case SOC_8090_P1G_11R1: ++ dprintk("SOC 8090 P1-G11R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_8090_P1G_21R1: ++ dprintk("SOC 8090 P1-G21R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_7090_P1G_11R1: ++ dprintk("SOC 7090 P1-G11R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_7090_P1G_21R1: ++ dprintk("SOC 7090 P1-G21R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ default: ++ goto identification_error; ++ } ++ } else { ++ switch ((identity->version >> 5) & 0x7) { ++ case MP001: ++ dprintk("MP001 : 9090/8096"); ++ break; ++ case MP005: ++ dprintk("MP005 : Single Sband"); ++ break; ++ case MP008: ++ dprintk("MP008 : diversity VHF-UHF-LBAND"); ++ break; ++ case MP009: ++ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); ++ break; ++ default: ++ goto identification_error; ++ } ++ ++ switch (identity->version & 0x1f) { ++ case P1G_21R2: ++ dprintk("P1G_21R2 detected"); ++ identity->p1g = 1; ++ break; ++ case P1G: ++ dprintk("P1G detected"); ++ identity->p1g = 1; ++ break; ++ case P1D_E_F: ++ dprintk("P1D/E/F detected"); ++ break; ++ case P1C: ++ dprintk("P1C detected"); ++ break; ++ case P1A_B: ++ dprintk("P1-A/B detected: driver is deactivated - not available"); ++ goto identification_error; ++ break; ++ default: ++ goto identification_error; ++ } ++ } ++ ++ return 0; ++ ++identification_error: ++ return -EIO; ++} ++ ++static int dib0090_fw_identify(struct dvb_frontend *fe) ++{ ++ struct dib0090_fw_state *state = fe->tuner_priv; ++ struct dib0090_identity *identity = &state->identity; ++ ++ u16 v = dib0090_fw_read_reg(state, 0x1a); ++ identity->p1g = 0; ++ identity->in_soc = 0; ++ ++ dprintk("FE: Tuner identification (Version = 0x%04x)", v); ++ ++ /* without PLL lock info */ ++ v &= ~KROSUS_PLL_LOCKED; ++ ++ identity->version = v & 0xff; ++ identity->product = (v >> 8) & 0xf; ++ ++ if (identity->product != KROSUS) ++ goto identification_error; ++ ++ if ((identity->version & 0x3) == SOC) { ++ identity->in_soc = 1; ++ switch (identity->version) { ++ case SOC_8090_P1G_11R1: ++ dprintk("SOC 8090 P1-G11R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_8090_P1G_21R1: ++ dprintk("SOC 8090 P1-G21R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_7090_P1G_11R1: ++ dprintk("SOC 7090 P1-G11R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ case SOC_7090_P1G_21R1: ++ dprintk("SOC 7090 P1-G21R1 Has been detected"); ++ identity->p1g = 1; ++ break; ++ default: ++ goto identification_error; ++ } ++ } else { ++ switch ((identity->version >> 5) & 0x7) { ++ case MP001: ++ dprintk("MP001 : 9090/8096"); ++ break; ++ case MP005: ++ dprintk("MP005 : Single Sband"); ++ break; ++ case MP008: ++ dprintk("MP008 : diversity VHF-UHF-LBAND"); ++ break; ++ case MP009: ++ dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); ++ break; ++ default: ++ goto identification_error; ++ } ++ ++ switch (identity->version & 0x1f) { ++ case P1G_21R2: ++ dprintk("P1G_21R2 detected"); ++ identity->p1g = 1; ++ break; ++ case P1G: ++ dprintk("P1G detected"); ++ identity->p1g = 1; ++ break; ++ case P1D_E_F: ++ dprintk("P1D/E/F detected"); ++ break; ++ case P1C: ++ dprintk("P1C detected"); ++ break; ++ case P1A_B: ++ dprintk("P1-A/B detected: driver is deactivated - not available"); ++ goto identification_error; ++ break; ++ default: ++ goto identification_error; ++ } ++ } ++ ++ return 0; ++ ++identification_error: ++ return -EIO; ++} ++ ++static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ u16 PllCfg, i, v; ++ ++ HARD_RESET(state); ++ ++ dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); ++ dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ ++ ++ if (!cfg->in_soc) { ++ /* adcClkOutRatio=8->7, release reset */ ++ dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); ++ if (cfg->clkoutdrive != 0) ++ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) ++ | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); ++ else ++ dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) ++ | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); ++ } ++ ++ /* Read Pll current config * */ ++ PllCfg = dib0090_read_reg(state, 0x21); ++ ++ /** Reconfigure PLL if current setting is different from default setting **/ ++ if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && (!cfg->in_soc) ++ && !cfg->io.pll_bypass) { ++ ++ /* Set Bypass mode */ ++ PllCfg |= (1 << 15); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ ++ /* Set Reset Pll */ ++ PllCfg &= ~(1 << 13); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ ++ /*** Set new Pll configuration in bypass and reset state ***/ ++ PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ ++ /* Remove Reset Pll */ ++ PllCfg |= (1 << 13); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ ++ /*** Wait for PLL lock ***/ ++ i = 100; ++ do { ++ v = !!(dib0090_read_reg(state, 0x1a) & 0x800); ++ if (v) ++ break; ++ } while (--i); ++ ++ if (i == 0) { ++ dprintk("Pll: Unable to lock Pll"); ++ return; ++ } ++ ++ /* Finally Remove Bypass mode */ ++ PllCfg &= ~(1 << 15); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ } ++ ++ if (cfg->io.pll_bypass) { ++ PllCfg |= (cfg->io.pll_bypass << 15); ++ dib0090_write_reg(state, 0x21, PllCfg); ++ } ++} ++ ++static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) ++{ ++ struct dib0090_fw_state *state = fe->tuner_priv; ++ u16 PllCfg; ++ u16 v; ++ int i; ++ ++ dprintk("fw reset digital"); ++ HARD_RESET(state); ++ ++ dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); ++ dib0090_fw_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ ++ ++ dib0090_fw_write_reg(state, 0x20, ++ ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (cfg->data_tx_drv << 4) | cfg->ls_cfg_pad_drv); ++ ++ v = (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 9) | (0 << 8) | (cfg->clkouttobamse << 4) | (0 << 2) | (0); ++ if (cfg->clkoutdrive != 0) ++ v |= cfg->clkoutdrive << 5; ++ else ++ v |= 7 << 5; ++ ++ v |= 2 << 10; ++ dib0090_fw_write_reg(state, 0x23, v); ++ ++ /* Read Pll current config * */ ++ PllCfg = dib0090_fw_read_reg(state, 0x21); ++ ++ /** Reconfigure PLL if current setting is different from default setting **/ ++ if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && !cfg->io.pll_bypass) { ++ ++ /* Set Bypass mode */ ++ PllCfg |= (1 << 15); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ ++ /* Set Reset Pll */ ++ PllCfg &= ~(1 << 13); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ ++ /*** Set new Pll configuration in bypass and reset state ***/ ++ PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ ++ /* Remove Reset Pll */ ++ PllCfg |= (1 << 13); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ ++ /*** Wait for PLL lock ***/ ++ i = 100; ++ do { ++ v = !!(dib0090_fw_read_reg(state, 0x1a) & 0x800); ++ if (v) ++ break; ++ } while (--i); ++ ++ if (i == 0) { ++ dprintk("Pll: Unable to lock Pll"); ++ return -EIO; ++ } ++ ++ /* Finally Remove Bypass mode */ ++ PllCfg &= ~(1 << 15); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ } ++ ++ if (cfg->io.pll_bypass) { ++ PllCfg |= (cfg->io.pll_bypass << 15); ++ dib0090_fw_write_reg(state, 0x21, PllCfg); ++ } ++ ++ return dib0090_fw_identify(fe); ++} ++ ++static int dib0090_wakeup(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ if (state->config->sleep) ++ state->config->sleep(fe, 0); ++ ++ /* enable dataTX in case we have been restarted in the wrong moment */ ++ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); ++ return 0; ++} ++ ++static int dib0090_sleep(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ if (state->config->sleep) ++ state->config->sleep(fe, 1); ++ return 0; ++} ++ ++void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ if (fast) ++ dib0090_write_reg(state, 0x04, 0); ++ else ++ dib0090_write_reg(state, 0x04, 1); ++} ++ ++EXPORT_SYMBOL(dib0090_dcc_freq); ++ ++static const u16 bb_ramp_pwm_normal_socs[] = { ++ 550, /* max BB gain in 10th of dB */ ++ (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ ++ 440, ++ (4 << 9) | 0, /* BB_RAMP3 = 26dB */ ++ (0 << 9) | 208, /* BB_RAMP4 */ ++ (4 << 9) | 208, /* BB_RAMP5 = 29dB */ ++ (0 << 9) | 440, /* BB_RAMP6 */ ++}; ++ ++static const u16 rf_ramp_pwm_cband_7090[] = { ++ 280, /* max RF gain in 10th of dB */ ++ 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ ++ 504, /* ramp_max = maximum X used on the ramp */ ++ (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ ++ (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ ++ (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ ++ (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ ++ (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ ++ (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ ++ (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ ++ (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ ++}; ++ ++static const uint16_t rf_ramp_pwm_cband_7090e_sensitivity[] = { ++ 186, ++ 40, ++ 746, ++ (10 << 10) | 345, ++ (0 << 10) | 746, ++ (0 << 10) | 0, ++ (0 << 10) | 0, ++ (28 << 10) | 200, ++ (0 << 10) | 345, ++ (20 << 10) | 0, ++ (0 << 10) | 200, ++}; ++ ++static const uint16_t rf_ramp_pwm_cband_7090e_aci[] = { ++ 86, ++ 40, ++ 345, ++ (0 << 10) | 0, ++ (0 << 10) | 0, ++ (0 << 10) | 0, ++ (0 << 10) | 0, ++ (28 << 10) | 200, ++ (0 << 10) | 345, ++ (20 << 10) | 0, ++ (0 << 10) | 200, ++}; ++ ++static const u16 rf_ramp_pwm_cband_8090[] = { ++ 345, /* max RF gain in 10th of dB */ ++ 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ ++ 1000, /* ramp_max = maximum X used on the ramp */ ++ (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ ++ (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ ++ (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ ++ (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ ++ (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ ++ (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ ++ (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ ++ (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 rf_ramp_pwm_uhf_7090[] = { ++ 407, /* max RF gain in 10th of dB */ ++ 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ ++ 529, /* ramp_max = maximum X used on the ramp */ ++ (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ ++ (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ ++ (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ ++ (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ ++ (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ ++ (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ ++ (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ ++ (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 rf_ramp_pwm_uhf_8090[] = { ++ 388, /* max RF gain in 10th of dB */ ++ 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ ++ 1008, /* ramp_max = maximum X used on the ramp */ ++ (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ ++ (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ ++ (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ ++ (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ ++ (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ ++ (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ ++ (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ ++ (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 rf_ramp_pwm_cband[] = { ++ 0, /* max RF gain in 10th of dB */ ++ 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ ++ 0, /* ramp_max = maximum X used on the ramp */ ++ (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */ ++ (0 << 10) | 0, /* 0x2d, LNA 1 */ ++ (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */ ++ (0 << 10) | 0, /* 0x2f, LNA 2 */ ++ (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */ ++ (0 << 10) | 0, /* 0x31, LNA 3 */ ++ (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ ++ (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 rf_ramp_vhf[] = { ++ 412, /* max RF gain in 10th of dB */ ++ 132, 307, 127, /* LNA1, 13.2dB */ ++ 105, 412, 255, /* LNA2, 10.5dB */ ++ 50, 50, 127, /* LNA3, 5dB */ ++ 125, 175, 127, /* LNA4, 12.5dB */ ++ 0, 0, 127, /* CBAND, 0dB */ ++}; ++ ++static const u16 rf_ramp_uhf[] = { ++ 412, /* max RF gain in 10th of dB */ ++ 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */ ++ 105, 412, 255, /* LNA2 : 10.5 dB */ ++ 50, 50, 127, /* LNA3 : 5.0 dB */ ++ 125, 175, 127, /* LNA4 : 12.5 dB */ ++ 0, 0, 127, /* CBAND : 0.0 dB */ ++}; ++ ++static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */ ++{ ++ 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */ ++ 84, 314, 127, /* LNA1 */ ++ 80, 230, 255, /* LNA2 */ ++ 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */ ++ 70, 70, 127, /* LNA4 */ ++ 0, 0, 127, /* CBAND */ ++}; ++ ++static const u16 rf_ramp_cband[] = { ++ 332, /* max RF gain in 10th of dB */ ++ 132, 252, 127, /* LNA1, dB */ ++ 80, 332, 255, /* LNA2, dB */ ++ 0, 0, 127, /* LNA3, dB */ ++ 0, 0, 127, /* LNA4, dB */ ++ 120, 120, 127, /* LT1 CBAND */ ++}; ++ ++static const u16 rf_ramp_pwm_vhf[] = { ++ 404, /* max RF gain in 10th of dB */ ++ 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ ++ 1011, /* ramp_max = maximum X used on the ramp */ ++ (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ ++ (0 << 10) | 756, /* 0x2d, LNA 1 */ ++ (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ ++ (0 << 10) | 1011, /* 0x2f, LNA 2 */ ++ (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */ ++ (0 << 10) | 417, /* 0x31, LNA 3 */ ++ (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */ ++ (0 << 10) | 290, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 rf_ramp_pwm_uhf[] = { ++ 404, /* max RF gain in 10th of dB */ ++ 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ ++ 1011, /* ramp_max = maximum X used on the ramp */ ++ (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ ++ (0 << 10) | 756, /* 0x2d, LNA 1 */ ++ (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ ++ (0 << 10) | 1011, /* 0x2f, LNA 2 */ ++ (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */ ++ (0 << 10) | 127, /* 0x31, LNA 3 */ ++ (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */ ++ (0 << 10) | 417, /* GAIN_4_2, LNA 4 */ ++}; ++ ++static const u16 bb_ramp_boost[] = { ++ 550, /* max BB gain in 10th of dB */ ++ 260, 260, 26, /* BB1, 26dB */ ++ 290, 550, 29, /* BB2, 29dB */ ++}; ++ ++static const u16 bb_ramp_pwm_normal[] = { ++ 500, /* max RF gain in 10th of dB */ ++ 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */ ++ 400, ++ (2 << 9) | 0, /* 0x35 = 21dB */ ++ (0 << 9) | 168, /* 0x36 */ ++ (2 << 9) | 168, /* 0x37 = 29dB */ ++ (0 << 9) | 400, /* 0x38 */ ++}; ++ ++struct slope { ++ s16 range; ++ s16 slope; ++}; ++static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val) ++{ ++ u8 i; ++ u16 rest; ++ u16 ret = 0; ++ for (i = 0; i < num; i++) { ++ if (val > slopes[i].range) ++ rest = slopes[i].range; ++ else ++ rest = val; ++ ret += (rest * slopes[i].slope) / slopes[i].range; ++ val -= rest; ++ } ++ return ret; ++} ++ ++static const struct slope dib0090_wbd_slopes[3] = { ++ {66, 120}, /* -64,-52: offset - 65 */ ++ {600, 170}, /* -52,-35: 65 - 665 */ ++ {170, 250}, /* -45,-10: 665 - 835 */ ++}; ++ ++static s16 dib0090_wbd_to_db(struct dib0090_state *state, u16 wbd) ++{ ++ wbd &= 0x3ff; ++ if (wbd < state->wbd_offset) ++ wbd = 0; ++ else ++ wbd -= state->wbd_offset; ++ /* -64dB is the floor */ ++ return -640 + (s16) slopes_to_scale(dib0090_wbd_slopes, ARRAY_SIZE(dib0090_wbd_slopes), wbd); ++} ++ ++static void dib0090_wbd_target(struct dib0090_state *state, u32 rf) ++{ ++ u16 offset = 250; ++ ++ /* TODO : DAB digital N+/-1 interferer perfs : offset = 10 */ ++ ++ if (state->current_band == BAND_VHF) ++ offset = 650; ++#ifndef FIRMWARE_FIREFLY ++ if (state->current_band == BAND_VHF) ++ offset = state->config->wbd_vhf_offset; ++ if (state->current_band == BAND_CBAND) ++ offset = state->config->wbd_cband_offset; ++#endif ++ ++ state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset); ++ dprintk("wbd-target: %d dB", (u32) state->wbd_target); ++} ++ ++static const int gain_reg_addr[4] = { ++ 0x08, 0x0a, 0x0f, 0x01 ++}; ++ ++static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16 top_delta, u8 force) ++{ ++ u16 rf, bb, ref; ++ u16 i, v, gain_reg[4] = { 0 }, gain; ++ const u16 *g; ++ ++ if (top_delta < -511) ++ top_delta = -511; ++ if (top_delta > 511) ++ top_delta = 511; ++ ++ if (force) { ++ top_delta *= (1 << WBD_ALPHA); ++ gain_delta *= (1 << GAIN_ALPHA); ++ } ++ ++ if (top_delta >= ((s16) (state->rf_ramp[0] << WBD_ALPHA) - state->rf_gain_limit)) /* overflow */ ++ state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; ++ else ++ state->rf_gain_limit += top_delta; ++ ++ if (state->rf_gain_limit < 0) /*underflow */ ++ state->rf_gain_limit = 0; ++ ++ /* use gain as a temporary variable and correct current_gain */ ++ gain = ((state->rf_gain_limit >> WBD_ALPHA) + state->bb_ramp[0]) << GAIN_ALPHA; ++ if (gain_delta >= ((s16) gain - state->current_gain)) /* overflow */ ++ state->current_gain = gain; ++ else ++ state->current_gain += gain_delta; ++ /* cannot be less than 0 (only if gain_delta is less than 0 we can have current_gain < 0) */ ++ if (state->current_gain < 0) ++ state->current_gain = 0; ++ ++ /* now split total gain to rf and bb gain */ ++ gain = state->current_gain >> GAIN_ALPHA; ++ ++ /* requested gain is bigger than rf gain limit - ACI/WBD adjustment */ ++ if (gain > (state->rf_gain_limit >> WBD_ALPHA)) { ++ rf = state->rf_gain_limit >> WBD_ALPHA; ++ bb = gain - rf; ++ if (bb > state->bb_ramp[0]) ++ bb = state->bb_ramp[0]; ++ } else { /* high signal level -> all gains put on RF */ ++ rf = gain; ++ bb = 0; ++ } ++ ++ state->gain[0] = rf; ++ state->gain[1] = bb; ++ ++ /* software ramp */ ++ /* Start with RF gains */ ++ g = state->rf_ramp + 1; /* point on RF LNA1 max gain */ ++ ref = rf; ++ for (i = 0; i < 7; i++) { /* Go over all amplifiers => 5RF amps + 2 BB amps = 7 amps */ ++ if (g[0] == 0 || ref < (g[1] - g[0])) /* if total gain of the current amp is null or this amp is not concerned because it starts to work from an higher gain value */ ++ v = 0; /* force the gain to write for the current amp to be null */ ++ else if (ref >= g[1]) /* Gain to set is higher than the high working point of this amp */ ++ v = g[2]; /* force this amp to be full gain */ ++ else /* compute the value to set to this amp because we are somewhere in his range */ ++ v = ((ref - (g[1] - g[0])) * g[2]) / g[0]; ++ ++ if (i == 0) /* LNA 1 reg mapping */ ++ gain_reg[0] = v; ++ else if (i == 1) /* LNA 2 reg mapping */ ++ gain_reg[0] |= v << 7; ++ else if (i == 2) /* LNA 3 reg mapping */ ++ gain_reg[1] = v; ++ else if (i == 3) /* LNA 4 reg mapping */ ++ gain_reg[1] |= v << 7; ++ else if (i == 4) /* CBAND LNA reg mapping */ ++ gain_reg[2] = v | state->rf_lt_def; ++ else if (i == 5) /* BB gain 1 reg mapping */ ++ gain_reg[3] = v << 3; ++ else if (i == 6) /* BB gain 2 reg mapping */ ++ gain_reg[3] |= v << 8; ++ ++ g += 3; /* go to next gain bloc */ ++ ++ /* When RF is finished, start with BB */ ++ if (i == 4) { ++ g = state->bb_ramp + 1; /* point on BB gain 1 max gain */ ++ ref = bb; ++ } ++ } ++ gain_reg[3] |= state->bb_1_def; ++ gain_reg[3] |= ((bb % 10) * 100) / 125; ++ ++#ifdef DEBUG_AGC ++ dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x", rf, bb, rf + bb, ++ gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]); ++#endif ++ ++ /* Write the amplifier regs */ ++ for (i = 0; i < 4; i++) { ++ v = gain_reg[i]; ++ if (force || state->gain_reg[i] != v) { ++ state->gain_reg[i] = v; ++ dib0090_write_reg(state, gain_reg_addr[i], v); ++ } ++ } ++} ++ ++static void dib0090_set_boost(struct dib0090_state *state, int onoff) ++{ ++ state->bb_1_def &= 0xdfff; ++ state->bb_1_def |= onoff << 13; ++} ++ ++static void dib0090_set_rframp(struct dib0090_state *state, const u16 * cfg) ++{ ++ state->rf_ramp = cfg; ++} ++ ++static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg) ++{ ++ state->rf_ramp = cfg; ++ ++ dib0090_write_reg(state, 0x2a, 0xffff); ++ ++ dprintk("total RF gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x2a)); ++ ++ dib0090_write_regs(state, 0x2c, cfg + 3, 6); ++ dib0090_write_regs(state, 0x3e, cfg + 9, 2); ++} ++ ++static void dib0090_set_bbramp(struct dib0090_state *state, const u16 * cfg) ++{ ++ state->bb_ramp = cfg; ++ dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ ++} ++ ++static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg) ++{ ++ state->bb_ramp = cfg; ++ ++ dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ ++ ++ dib0090_write_reg(state, 0x33, 0xffff); ++ dprintk("total BB gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x33)); ++ dib0090_write_regs(state, 0x35, cfg + 3, 4); ++} ++ ++void dib0090_pwm_gain_reset(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ /* reset the AGC */ ++ ++ if (state->config->use_pwm_agc) { ++#ifdef CONFIG_BAND_SBAND ++ if (state->current_band == BAND_SBAND) { ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband); ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost); ++ } else ++#endif ++#ifdef CONFIG_BAND_CBAND ++ if (state->current_band == BAND_CBAND) { ++ if (state->identity.in_soc) { ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); ++ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090); ++ else if (state->identity.version == SOC_7090_P1G_11R1 ++ || state->identity.version == SOC_7090_P1G_21R1) { ++ if (state->config->is_dib7090e) { ++ if (state->rf_ramp == NULL) ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090e_sensitivity); ++ else ++ dib0090_set_rframp_pwm(state, state->rf_ramp); ++ } else ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090); ++ } ++ } else { ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); ++ } ++ } else ++#endif ++#ifdef CONFIG_BAND_VHF ++ if (state->current_band == BAND_VHF) { ++ if (state->identity.in_soc) { ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); ++ } else { ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); ++ } ++ } else ++#endif ++ { ++ if (state->identity.in_soc) { ++ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090); ++ else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090); ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); ++ } else { ++ dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); ++ dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); ++ } ++ } ++ ++ if (state->rf_ramp[0] != 0) ++ dib0090_write_reg(state, 0x32, (3 << 11)); ++ else ++ dib0090_write_reg(state, 0x32, (0 << 11)); ++ ++ dib0090_write_reg(state, 0x04, 0x03); ++ dib0090_write_reg(state, 0x39, (1 << 10)); ++ } ++} ++ ++EXPORT_SYMBOL(dib0090_pwm_gain_reset); ++ ++void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ if (DC_servo_cutoff < 4) ++ dib0090_write_reg(state, 0x04, DC_servo_cutoff); ++} ++EXPORT_SYMBOL(dib0090_set_dc_servo); ++ ++static u32 dib0090_get_slow_adc_val(struct dib0090_state *state) ++{ ++ u16 adc_val = dib0090_read_reg(state, 0x1d); ++ if (state->identity.in_soc) ++ adc_val >>= 2; ++ return adc_val; ++} ++ ++int dib0090_gain_control(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ enum frontend_tune_state *tune_state = &state->tune_state; ++ int ret = 10; ++ ++ u16 wbd_val = 0; ++ u8 apply_gain_immediatly = 1; ++ s16 wbd_error = 0, adc_error = 0; ++ ++ if (*tune_state == CT_AGC_START) { ++ state->agc_freeze = 0; ++ dib0090_write_reg(state, 0x04, 0x0); ++ ++#ifdef CONFIG_BAND_SBAND ++ if (state->current_band == BAND_SBAND) { ++ dib0090_set_rframp(state, rf_ramp_sband); ++ dib0090_set_bbramp(state, bb_ramp_boost); ++ } else ++#endif ++#ifdef CONFIG_BAND_VHF ++ if (state->current_band == BAND_VHF && !state->identity.p1g) { ++ dib0090_set_rframp(state, rf_ramp_vhf); ++ dib0090_set_bbramp(state, bb_ramp_boost); ++ } else ++#endif ++#ifdef CONFIG_BAND_CBAND ++ if (state->current_band == BAND_CBAND && !state->identity.p1g) { ++ dib0090_set_rframp(state, rf_ramp_cband); ++ dib0090_set_bbramp(state, bb_ramp_boost); ++ } else ++#endif ++ if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { ++ dib0090_set_rframp(state, rf_ramp_cband_broadmatching); ++ dib0090_set_bbramp(state, bb_ramp_boost); ++ } else { ++ dib0090_set_rframp(state, rf_ramp_uhf); ++ dib0090_set_bbramp(state, bb_ramp_boost); ++ } ++ ++ dib0090_write_reg(state, 0x32, 0); ++ dib0090_write_reg(state, 0x39, 0); ++ ++ dib0090_wbd_target(state, state->current_rf); ++ ++ state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; ++ state->current_gain = ((state->rf_ramp[0] + state->bb_ramp[0]) / 2) << GAIN_ALPHA; ++ ++ *tune_state = CT_AGC_STEP_0; ++ } else if (!state->agc_freeze) { ++ s16 wbd = 0, i, cnt; ++ ++ int adc; ++ wbd_val = dib0090_get_slow_adc_val(state); ++ ++ if (*tune_state == CT_AGC_STEP_0) ++ cnt = 5; ++ else ++ cnt = 1; ++ ++ for (i = 0; i < cnt; i++) { ++ wbd_val = dib0090_get_slow_adc_val(state); ++ wbd += dib0090_wbd_to_db(state, wbd_val); ++ } ++ wbd /= cnt; ++ wbd_error = state->wbd_target - wbd; ++ ++ if (*tune_state == CT_AGC_STEP_0) { ++ if (wbd_error < 0 && state->rf_gain_limit > 0 && !state->identity.p1g) { ++#ifdef CONFIG_BAND_CBAND ++ /* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */ ++ u8 ltg2 = (state->rf_lt_def >> 10) & 0x7; ++ if (state->current_band == BAND_CBAND && ltg2) { ++ ltg2 >>= 1; ++ state->rf_lt_def &= ltg2 << 10; /* reduce in 3 steps from 7 to 0 */ ++ } ++#endif ++ } else { ++ state->agc_step = 0; ++ *tune_state = CT_AGC_STEP_1; ++ } ++ } else { ++ /* calc the adc power */ ++ adc = state->config->get_adc_power(fe); ++ adc = (adc * ((s32) 355774) + (((s32) 1) << 20)) >> 21; /* included in [0:-700] */ ++ ++ adc_error = (s16) (((s32) ADC_TARGET) - adc); ++#ifdef CONFIG_STANDARD_DAB ++ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) ++ adc_error -= 10; ++#endif ++#ifdef CONFIG_STANDARD_DVBT ++ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT && ++ (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16)) ++ adc_error += 60; ++#endif ++#ifdef CONFIG_SYS_ISDBT ++ if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count > ++ 0) ++ && ++ ((state->fe->dtv_property_cache.layer[0].modulation == ++ QAM_64) ++ || (state->fe->dtv_property_cache. ++ layer[0].modulation == QAM_16))) ++ || ++ ((state->fe->dtv_property_cache.layer[1].segment_count > ++ 0) ++ && ++ ((state->fe->dtv_property_cache.layer[1].modulation == ++ QAM_64) ++ || (state->fe->dtv_property_cache. ++ layer[1].modulation == QAM_16))) ++ || ++ ((state->fe->dtv_property_cache.layer[2].segment_count > ++ 0) ++ && ++ ((state->fe->dtv_property_cache.layer[2].modulation == ++ QAM_64) ++ || (state->fe->dtv_property_cache. ++ layer[2].modulation == QAM_16))) ++ ) ++ ) ++ adc_error += 60; ++#endif ++ ++ if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */ ++ if (ABS(adc_error) < 50 || state->agc_step++ > 5) { ++ ++#ifdef CONFIG_STANDARD_DAB ++ if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) { ++ dib0090_write_reg(state, 0x02, (1 << 15) | (15 << 11) | (31 << 6) | (63)); /* cap value = 63 : narrow BB filter : Fc = 1.8MHz */ ++ dib0090_write_reg(state, 0x04, 0x0); ++ } else ++#endif ++ { ++ dib0090_write_reg(state, 0x02, (1 << 15) | (3 << 11) | (6 << 6) | (32)); ++ dib0090_write_reg(state, 0x04, 0x01); /*0 = 1KHz ; 1 = 150Hz ; 2 = 50Hz ; 3 = 50KHz ; 4 = servo fast */ ++ } ++ ++ *tune_state = CT_AGC_STOP; ++ } ++ } else { ++ /* everything higher than or equal to CT_AGC_STOP means tracking */ ++ ret = 100; /* 10ms interval */ ++ apply_gain_immediatly = 0; ++ } ++ } ++#ifdef DEBUG_AGC ++ dprintk ++ ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", ++ (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, ++ (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); ++#endif ++ } ++ ++ /* apply gain */ ++ if (!state->agc_freeze) ++ dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly); ++ return ret; ++} ++ ++EXPORT_SYMBOL(dib0090_gain_control); ++ ++void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ if (rf) ++ *rf = state->gain[0]; ++ if (bb) ++ *bb = state->gain[1]; ++ if (rf_gain_limit) ++ *rf_gain_limit = state->rf_gain_limit; ++ if (rflt) ++ *rflt = (state->rf_lt_def >> 10) & 0x7; ++} ++ ++EXPORT_SYMBOL(dib0090_get_current_gain); ++ ++u16 dib0090_get_wbd_target(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ u32 f_MHz = state->fe->dtv_property_cache.frequency / 1000000; ++ s32 current_temp = state->temperature; ++ s32 wbd_thot, wbd_tcold; ++ const struct dib0090_wbd_slope *wbd = state->current_wbd_table; ++ ++ while (f_MHz > wbd->max_freq) ++ wbd++; ++ ++ dprintk("using wbd-table-entry with max freq %d", wbd->max_freq); ++ ++ if (current_temp < 0) ++ current_temp = 0; ++ if (current_temp > 128) ++ current_temp = 128; ++ ++ state->wbdmux &= ~(7 << 13); ++ if (wbd->wbd_gain != 0) ++ state->wbdmux |= (wbd->wbd_gain << 13); ++ else ++ state->wbdmux |= (4 << 13); ++ ++ dib0090_write_reg(state, 0x10, state->wbdmux); ++ ++ wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6); ++ wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6); ++ ++ wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7; ++ ++ state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); ++ dprintk("wbd-target: %d dB", (u32) state->wbd_target); ++ dprintk("wbd offset applied is %d", wbd_tcold); ++ ++ return state->wbd_offset + wbd_tcold; ++} ++EXPORT_SYMBOL(dib0090_get_wbd_target); ++ ++u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ return state->wbd_offset; ++} ++EXPORT_SYMBOL(dib0090_get_wbd_offset); ++ ++int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xfff8) ++ | ((sw3 & 1) << 2) | ((sw2 & 1) << 1) | (sw1 & 1)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib0090_set_switch); ++ ++int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x7fff) ++ | ((onoff & 1) << 15)); ++ return 0; ++} ++EXPORT_SYMBOL(dib0090_set_vga); ++ ++int dib0090_update_rframp_7090(struct dvb_frontend *fe, u8 cfg_sensitivity) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ if ((!state->identity.p1g) || (!state->identity.in_soc) ++ || ((state->identity.version != SOC_7090_P1G_21R1) ++ && (state->identity.version != SOC_7090_P1G_11R1))) { ++ dprintk("%s() function can only be used for dib7090P", __func__); ++ return -ENODEV; ++ } ++ ++ if (cfg_sensitivity) ++ state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_sensitivity; ++ else ++ state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_aci; ++ dib0090_pwm_gain_reset(fe); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib0090_update_rframp_7090); ++ ++static const u16 dib0090_defaults[] = { ++ ++ 25, 0x01, ++ 0x0000, ++ 0x99a0, ++ 0x6008, ++ 0x0000, ++ 0x8bcb, ++ 0x0000, ++ 0x0405, ++ 0x0000, ++ 0x0000, ++ 0x0000, ++ 0xb802, ++ 0x0300, ++ 0x2d12, ++ 0xbac0, ++ 0x7c00, ++ 0xdbb9, ++ 0x0954, ++ 0x0743, ++ 0x8000, ++ 0x0001, ++ 0x0040, ++ 0x0100, ++ 0x0000, ++ 0xe910, ++ 0x149e, ++ ++ 1, 0x1c, ++ 0xff2d, ++ ++ 1, 0x39, ++ 0x0000, ++ ++ 2, 0x1e, ++ 0x07FF, ++ 0x0007, ++ ++ 1, 0x24, ++ EN_UHF | EN_CRYSTAL, ++ ++ 2, 0x3c, ++ 0x3ff, ++ 0x111, ++ 0 ++}; ++ ++static const u16 dib0090_p1g_additionnal_defaults[] = { ++ 1, 0x05, ++ 0xabcd, ++ ++ 1, 0x11, ++ 0x00b4, ++ ++ 1, 0x1c, ++ 0xfffd, ++ ++ 1, 0x40, ++ 0x108, ++ 0 ++}; ++ ++static void dib0090_set_default_config(struct dib0090_state *state, const u16 * n) ++{ ++ u16 l, r; ++ ++ l = pgm_read_word(n++); ++ while (l) { ++ r = pgm_read_word(n++); ++ do { ++ dib0090_write_reg(state, r, pgm_read_word(n++)); ++ r++; ++ } while (--l); ++ l = pgm_read_word(n++); ++ } ++} ++ ++#define CAP_VALUE_MIN (u8) 9 ++#define CAP_VALUE_MAX (u8) 40 ++#define HR_MIN (u8) 25 ++#define HR_MAX (u8) 40 ++#define POLY_MIN (u8) 0 ++#define POLY_MAX (u8) 8 ++ ++static void dib0090_set_EFUSE(struct dib0090_state *state) ++{ ++ u8 c, h, n; ++ u16 e2, e4; ++ u16 cal; ++ ++ e2 = dib0090_read_reg(state, 0x26); ++ e4 = dib0090_read_reg(state, 0x28); ++ ++ if ((state->identity.version == P1D_E_F) || ++ (state->identity.version == P1G) || (e2 == 0xffff)) { ++ ++ dib0090_write_reg(state, 0x22, 0x10); ++ cal = (dib0090_read_reg(state, 0x22) >> 6) & 0x3ff; ++ ++ if ((cal < 670) || (cal == 1023)) ++ cal = 850; ++ n = 165 - ((cal * 10)>>6) ; ++ e2 = e4 = (3<<12) | (34<<6) | (n); ++ } ++ ++ if (e2 != e4) ++ e2 &= e4; /* Remove the redundancy */ ++ ++ if (e2 != 0xffff) { ++ c = e2 & 0x3f; ++ n = (e2 >> 12) & 0xf; ++ h = (e2 >> 6) & 0x3f; ++ ++ if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) ++ c = 32; ++ if ((h >= HR_MAX) || (h <= HR_MIN)) ++ h = 34; ++ if ((n >= POLY_MAX) || (n <= POLY_MIN)) ++ n = 3; ++ ++ dib0090_write_reg(state, 0x13, (h << 10)) ; ++ e2 = (n<<11) | ((h>>2)<<6) | (c); ++ dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */ ++ } ++} ++ ++static int dib0090_reset(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ dib0090_reset_digital(fe, state->config); ++ if (dib0090_identify(fe) < 0) ++ return -EIO; ++ ++#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT ++ if (!(state->identity.version & 0x1)) /* it is P1B - reset is already done */ ++ return 0; ++#endif ++ ++ if (!state->identity.in_soc) { ++ if ((dib0090_read_reg(state, 0x1a) >> 5) & 0x2) ++ dib0090_write_reg(state, 0x1b, (EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL)); ++ else ++ dib0090_write_reg(state, 0x1b, (EN_DIGCLK | EN_PLL | EN_CRYSTAL)); ++ } ++ ++ dib0090_set_default_config(state, dib0090_defaults); ++ ++ if (state->identity.in_soc) ++ dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ ++ ++ if (state->identity.p1g) ++ dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults); ++ ++ /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ ++ if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) ++ dib0090_set_EFUSE(state); ++ ++ /* Congigure in function of the crystal */ ++ if (state->config->force_crystal_mode != 0) ++ dib0090_write_reg(state, 0x14, ++ state->config->force_crystal_mode & 3); ++ else if (state->config->io.clock_khz >= 24000) ++ dib0090_write_reg(state, 0x14, 1); ++ else ++ dib0090_write_reg(state, 0x14, 2); ++ dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1); ++ ++ state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */ ++ ++ return 0; ++} ++ ++#define steps(u) (((u) > 15) ? ((u)-16) : (u)) ++#define INTERN_WAIT 10 ++static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_state *tune_state) ++{ ++ int ret = INTERN_WAIT * 10; ++ ++ switch (*tune_state) { ++ case CT_TUNER_STEP_2: ++ /* Turns to positive */ ++ dib0090_write_reg(state, 0x1f, 0x7); ++ *tune_state = CT_TUNER_STEP_3; ++ break; ++ ++ case CT_TUNER_STEP_3: ++ state->adc_diff = dib0090_read_reg(state, 0x1d); ++ ++ /* Turns to negative */ ++ dib0090_write_reg(state, 0x1f, 0x4); ++ *tune_state = CT_TUNER_STEP_4; ++ break; ++ ++ case CT_TUNER_STEP_4: ++ state->adc_diff -= dib0090_read_reg(state, 0x1d); ++ *tune_state = CT_TUNER_STEP_5; ++ ret = 0; ++ break; ++ ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++struct dc_calibration { ++ u8 addr; ++ u8 offset; ++ u8 pga:1; ++ u16 bb1; ++ u8 i:1; ++}; ++ ++static const struct dc_calibration dc_table[] = { ++ /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ ++ {0x06, 5, 1, (1 << 13) | (0 << 8) | (26 << 3), 1}, ++ {0x07, 11, 1, (1 << 13) | (0 << 8) | (26 << 3), 0}, ++ /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ ++ {0x06, 0, 0, (1 << 13) | (29 << 8) | (26 << 3), 1}, ++ {0x06, 10, 0, (1 << 13) | (29 << 8) | (26 << 3), 0}, ++ {0}, ++}; ++ ++static const struct dc_calibration dc_p1g_table[] = { ++ /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ ++ /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */ ++ {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, ++ {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, ++ /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ ++ {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, ++ {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, ++ {0}, ++}; ++ ++static void dib0090_set_trim(struct dib0090_state *state) ++{ ++ u16 *val; ++ ++ if (state->dc->addr == 0x07) ++ val = &state->bb7; ++ else ++ val = &state->bb6; ++ ++ *val &= ~(0x1f << state->dc->offset); ++ *val |= state->step << state->dc->offset; ++ ++ dib0090_write_reg(state, state->dc->addr, *val); ++} ++ ++static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) ++{ ++ int ret = 0; ++ u16 reg; ++ ++ switch (*tune_state) { ++ case CT_TUNER_START: ++ dprintk("Start DC offset calibration"); ++ ++ /* force vcm2 = 0.8V */ ++ state->bb6 = 0; ++ state->bb7 = 0x040d; ++ ++ /* the LNA AND LO are off */ ++ reg = dib0090_read_reg(state, 0x24) & 0x0ffb; /* shutdown lna and lo */ ++ dib0090_write_reg(state, 0x24, reg); ++ ++ state->wbdmux = dib0090_read_reg(state, 0x10); ++ dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); ++ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); ++ ++ state->dc = dc_table; ++ ++ if (state->identity.p1g) ++ state->dc = dc_p1g_table; ++ *tune_state = CT_TUNER_STEP_0; ++ ++ /* fall through */ ++ ++ case CT_TUNER_STEP_0: ++ dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q"); ++ dib0090_write_reg(state, 0x01, state->dc->bb1); ++ dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7)); ++ ++ state->step = 0; ++ state->min_adc_diff = 1023; ++ *tune_state = CT_TUNER_STEP_1; ++ ret = 50; ++ break; ++ ++ case CT_TUNER_STEP_1: ++ dib0090_set_trim(state); ++ *tune_state = CT_TUNER_STEP_2; ++ break; ++ ++ case CT_TUNER_STEP_2: ++ case CT_TUNER_STEP_3: ++ case CT_TUNER_STEP_4: ++ ret = dib0090_get_offset(state, tune_state); ++ break; ++ ++ case CT_TUNER_STEP_5: /* found an offset */ ++ dprintk("adc_diff = %d, current step= %d", (u32) state->adc_diff, state->step); ++ if (state->step == 0 && state->adc_diff < 0) { ++ state->min_adc_diff = -1023; ++ dprintk("Change of sign of the minimum adc diff"); ++ } ++ ++ dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d", state->adc_diff, state->min_adc_diff, state->step); ++ ++ /* first turn for this frequency */ ++ if (state->step == 0) { ++ if (state->dc->pga && state->adc_diff < 0) ++ state->step = 0x10; ++ if (state->dc->pga == 0 && state->adc_diff > 0) ++ state->step = 0x10; ++ } ++ ++ /* Look for a change of Sign in the Adc_diff.min_adc_diff is used to STORE the setp N-1 */ ++ if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) { ++ /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */ ++ state->step++; ++ state->min_adc_diff = state->adc_diff; ++ *tune_state = CT_TUNER_STEP_1; ++ } else { ++ /* the minimum was what we have seen in the step before */ ++ if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { ++ dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff); ++ state->step--; ++ } ++ ++ dib0090_set_trim(state); ++ dprintk("BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->dc->addr, state->adc_diff, state->step); ++ ++ state->dc++; ++ if (state->dc->addr == 0) /* done */ ++ *tune_state = CT_TUNER_STEP_6; ++ else ++ *tune_state = CT_TUNER_STEP_0; ++ ++ } ++ break; ++ ++ case CT_TUNER_STEP_6: ++ dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); ++ dib0090_write_reg(state, 0x1f, 0x7); ++ *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ ++ state->calibrate &= ~DC_CAL; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) ++{ ++ u8 wbd_gain; ++ const struct dib0090_wbd_slope *wbd = state->current_wbd_table; ++ ++ switch (*tune_state) { ++ case CT_TUNER_START: ++ while (state->current_rf / 1000 > wbd->max_freq) ++ wbd++; ++ if (wbd->wbd_gain != 0) ++ wbd_gain = wbd->wbd_gain; ++ else { ++ wbd_gain = 4; ++#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND) ++ if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) ++ wbd_gain = 2; ++#endif ++ } ++ ++ if (wbd_gain == state->wbd_calibration_gain) { /* the WBD calibration has already been done */ ++ *tune_state = CT_TUNER_START; ++ state->calibrate &= ~WBD_CAL; ++ return 0; ++ } ++ ++ dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); ++ ++ dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); ++ *tune_state = CT_TUNER_STEP_0; ++ state->wbd_calibration_gain = wbd_gain; ++ return 90; /* wait for the WBDMUX to switch and for the ADC to sample */ ++ ++ case CT_TUNER_STEP_0: ++ state->wbd_offset = dib0090_get_slow_adc_val(state); ++ dprintk("WBD calibration offset = %d", state->wbd_offset); ++ *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ ++ state->calibrate &= ~WBD_CAL; ++ break; ++ ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static void dib0090_set_bandwidth(struct dib0090_state *state) ++{ ++ u16 tmp; ++ ++ if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 5000) ++ tmp = (3 << 14); ++ else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 6000) ++ tmp = (2 << 14); ++ else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 7000) ++ tmp = (1 << 14); ++ else ++ tmp = (0 << 14); ++ ++ state->bb_1_def &= 0x3fff; ++ state->bb_1_def |= tmp; ++ ++ dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */ ++ ++ dib0090_write_reg(state, 0x03, 0x6008); /* = 0x6008 : vcm3_trim = 1 ; filter2_gm1_trim = 8 ; filter2_cutoff_freq = 0 */ ++ dib0090_write_reg(state, 0x04, 0x1); /* 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast */ ++ if (state->identity.in_soc) { ++ dib0090_write_reg(state, 0x05, 0x9bcf); /* attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 1 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 15 */ ++ } else { ++ dib0090_write_reg(state, 0x02, (5 << 11) | (8 << 6) | (22 & 0x3f)); /* 22 = cap_value */ ++ dib0090_write_reg(state, 0x05, 0xabcd); /* = 0xabcd : attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 2 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 13 */ ++ } ++} ++ ++static const struct dib0090_pll dib0090_pll_table[] = { ++#ifdef CONFIG_BAND_CBAND ++ {56000, 0, 9, 48, 6}, ++ {70000, 1, 9, 48, 6}, ++ {87000, 0, 8, 32, 4}, ++ {105000, 1, 8, 32, 4}, ++ {115000, 0, 7, 24, 6}, ++ {140000, 1, 7, 24, 6}, ++ {170000, 0, 6, 16, 4}, ++#endif ++#ifdef CONFIG_BAND_VHF ++ {200000, 1, 6, 16, 4}, ++ {230000, 0, 5, 12, 6}, ++ {280000, 1, 5, 12, 6}, ++ {340000, 0, 4, 8, 4}, ++ {380000, 1, 4, 8, 4}, ++ {450000, 0, 3, 6, 6}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {580000, 1, 3, 6, 6}, ++ {700000, 0, 2, 4, 4}, ++ {860000, 1, 2, 4, 4}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1800000, 1, 0, 2, 4}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2900000, 0, 14, 1, 4}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_tuning_table_fm_vhf_on_cband[] = { ++ ++#ifdef CONFIG_BAND_CBAND ++ {184000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, ++ {227000, 4, 3, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, ++ {380000, 4, 7, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, ++ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_tuning_table[] = { ++ ++#ifdef CONFIG_BAND_CBAND ++ {170000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, ++#endif ++#ifdef CONFIG_BAND_VHF ++ {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++ {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++ {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, ++ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_p1g_tuning_table[] = { ++#ifdef CONFIG_BAND_CBAND ++ {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, ++#endif ++#ifdef CONFIG_BAND_VHF ++ {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++ {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++ {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, ++ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, ++#endif ++}; ++ ++static const struct dib0090_pll dib0090_p1g_pll_table[] = { ++#ifdef CONFIG_BAND_CBAND ++ {57000, 0, 11, 48, 6}, ++ {70000, 1, 11, 48, 6}, ++ {86000, 0, 10, 32, 4}, ++ {105000, 1, 10, 32, 4}, ++ {115000, 0, 9, 24, 6}, ++ {140000, 1, 9, 24, 6}, ++ {170000, 0, 8, 16, 4}, ++#endif ++#ifdef CONFIG_BAND_VHF ++ {200000, 1, 8, 16, 4}, ++ {230000, 0, 7, 12, 6}, ++ {280000, 1, 7, 12, 6}, ++ {340000, 0, 6, 8, 4}, ++ {380000, 1, 6, 8, 4}, ++ {455000, 0, 5, 6, 6}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {580000, 1, 5, 6, 6}, ++ {680000, 0, 4, 4, 4}, ++ {860000, 1, 4, 4, 4}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1800000, 1, 2, 2, 4}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2900000, 0, 1, 1, 6}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = { ++#ifdef CONFIG_BAND_CBAND ++ {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, ++ {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, ++ {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, ++#endif ++#ifdef CONFIG_BAND_UHF ++ {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++ {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, ++#endif ++#ifdef CONFIG_BAND_LBAND ++ {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++ {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, ++#endif ++#ifdef CONFIG_BAND_SBAND ++ {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, ++ {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = { ++#ifdef CONFIG_BAND_CBAND ++ {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, ++ {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, ++ {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, ++ {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, ++#endif ++}; ++ ++static const struct dib0090_tuning dib0090_tuning_table_cband_7090e_sensitivity[] = { ++#ifdef CONFIG_BAND_CBAND ++ { 300000, 0 , 3, 0x8105, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, ++ { 380000, 0 , 10, 0x810F, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, ++ { 600000, 0 , 10, 0x815E, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++ { 660000, 0 , 5, 0x85E3, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++ { 720000, 0 , 5, 0x852E, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++ { 860000, 0 , 4, 0x85E5, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++#endif ++}; ++ ++int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, ++ u8 cfg_sensitivity) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ const struct dib0090_tuning *tune = ++ dib0090_tuning_table_cband_7090e_sensitivity; ++ const struct dib0090_tuning dib0090_tuning_table_cband_7090e_aci[] = { ++ { 300000, 0 , 3, 0x8165, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, ++ { 650000, 0 , 4, 0x815B, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++ { 860000, 0 , 5, 0x84EF, 0x280, 0x2d12, 0xb84e, EN_CAB }, ++ }; ++ ++ if ((!state->identity.p1g) || (!state->identity.in_soc) ++ || ((state->identity.version != SOC_7090_P1G_21R1) ++ && (state->identity.version != SOC_7090_P1G_11R1))) { ++ dprintk("%s() function can only be used for dib7090", __func__); ++ return -ENODEV; ++ } ++ ++ if (cfg_sensitivity) ++ tune = dib0090_tuning_table_cband_7090e_sensitivity; ++ else ++ tune = dib0090_tuning_table_cband_7090e_aci; ++ ++ while (state->rf_request > tune->max_freq) ++ tune++; ++ ++ dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x8000) ++ | (tune->lna_bias & 0x7fff)); ++ dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xf83f) ++ | ((tune->lna_tune << 6) & 0x07c0)); ++ return 0; ++} ++EXPORT_SYMBOL(dib0090_update_tuning_table_7090); ++ ++static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tune_state *tune_state) ++{ ++ int ret = 0; ++ u16 lo4 = 0xe900; ++ ++ s16 adc_target; ++ u16 adc; ++ s8 step_sign; ++ u8 force_soft_search = 0; ++ ++ if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) ++ force_soft_search = 1; ++ ++ if (*tune_state == CT_TUNER_START) { ++ dprintk("Start Captrim search : %s", (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO"); ++ dib0090_write_reg(state, 0x10, 0x2B1); ++ dib0090_write_reg(state, 0x1e, 0x0032); ++ ++ if (!state->tuner_is_tuned) { ++ /* prepare a complete captrim */ ++ if (!state->identity.p1g || force_soft_search) ++ state->step = state->captrim = state->fcaptrim = 64; ++ ++ state->current_rf = state->rf_request; ++ } else { /* we are already tuned to this frequency - the configuration is correct */ ++ if (!state->identity.p1g || force_soft_search) { ++ /* do a minimal captrim even if the frequency has not changed */ ++ state->step = 4; ++ state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; ++ } ++ } ++ state->adc_diff = 3000; ++ *tune_state = CT_TUNER_STEP_0; ++ ++ } else if (*tune_state == CT_TUNER_STEP_0) { ++ if (state->identity.p1g && !force_soft_search) { ++ u8 ratio = 31; ++ ++ dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1); ++ dib0090_read_reg(state, 0x40); ++ ret = 50; ++ } else { ++ state->step /= 2; ++ dib0090_write_reg(state, 0x18, lo4 | state->captrim); ++ ++ if (state->identity.in_soc) ++ ret = 25; ++ } ++ *tune_state = CT_TUNER_STEP_1; ++ ++ } else if (*tune_state == CT_TUNER_STEP_1) { ++ if (state->identity.p1g && !force_soft_search) { ++ dib0090_write_reg(state, 0x40, 0x18c | (0 << 1) | 0); ++ dib0090_read_reg(state, 0x40); ++ ++ state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F; ++ dprintk("***Final Captrim= 0x%x", state->fcaptrim); ++ *tune_state = CT_TUNER_STEP_3; ++ ++ } else { ++ /* MERGE for all krosus before P1G */ ++ adc = dib0090_get_slow_adc_val(state); ++ dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024); ++ ++ if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */ ++ adc_target = 200; ++ } else ++ adc_target = 400; ++ ++ if (adc >= adc_target) { ++ adc -= adc_target; ++ step_sign = -1; ++ } else { ++ adc = adc_target - adc; ++ step_sign = 1; ++ } ++ ++ if (adc < state->adc_diff) { ++ dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff); ++ state->adc_diff = adc; ++ state->fcaptrim = state->captrim; ++ } ++ ++ state->captrim += step_sign * state->step; ++ if (state->step >= 1) ++ *tune_state = CT_TUNER_STEP_0; ++ else ++ *tune_state = CT_TUNER_STEP_2; ++ ++ ret = 25; ++ } ++ } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */ ++ /*write the final cptrim config */ ++ dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim); ++ ++ *tune_state = CT_TUNER_STEP_3; ++ ++ } else if (*tune_state == CT_TUNER_STEP_3) { ++ state->calibrate &= ~CAPTRIM_CAL; ++ *tune_state = CT_TUNER_STEP_0; ++ } ++ ++ return ret; ++} ++ ++static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tune_state *tune_state) ++{ ++ int ret = 15; ++ s16 val; ++ ++ switch (*tune_state) { ++ case CT_TUNER_START: ++ state->wbdmux = dib0090_read_reg(state, 0x10); ++ dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); ++ ++ state->bias = dib0090_read_reg(state, 0x13); ++ dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); ++ ++ *tune_state = CT_TUNER_STEP_0; ++ /* wait for the WBDMUX to switch and for the ADC to sample */ ++ break; ++ ++ case CT_TUNER_STEP_0: ++ state->adc_diff = dib0090_get_slow_adc_val(state); ++ dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); ++ *tune_state = CT_TUNER_STEP_1; ++ break; ++ ++ case CT_TUNER_STEP_1: ++ val = dib0090_get_slow_adc_val(state); ++ state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; ++ ++ dprintk("temperature: %d C", state->temperature - 30); ++ ++ *tune_state = CT_TUNER_STEP_2; ++ break; ++ ++ case CT_TUNER_STEP_2: ++ dib0090_write_reg(state, 0x13, state->bias); ++ dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */ ++ ++ *tune_state = CT_TUNER_START; ++ state->calibrate &= ~TEMP_CAL; ++ if (state->config->analog_output == 0) ++ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); ++ ++ break; ++ ++ default: ++ ret = 0; ++ break; ++ } ++ return ret; ++} ++ ++#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ ++static int dib0090_tune(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ const struct dib0090_tuning *tune = state->current_tune_table_index; ++ const struct dib0090_pll *pll = state->current_pll_table_index; ++ enum frontend_tune_state *tune_state = &state->tune_state; ++ ++ u16 lo5, lo6, Den, tmp; ++ u32 FBDiv, Rest, FREF, VCOF_kHz = 0; ++ int ret = 10; /* 1ms is the default delay most of the time */ ++ u8 c, i; ++ ++ /************************* VCO ***************************/ ++ /* Default values for FG */ ++ /* from these are needed : */ ++ /* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */ ++ ++ /* in any case we first need to do a calibration if needed */ ++ if (*tune_state == CT_TUNER_START) { ++ /* deactivate DataTX before some calibrations */ ++ if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL)) ++ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); ++ else ++ /* Activate DataTX in case a calibration has been done before */ ++ if (state->config->analog_output == 0) ++ dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); ++ } ++ ++ if (state->calibrate & DC_CAL) ++ return dib0090_dc_offset_calibration(state, tune_state); ++ else if (state->calibrate & WBD_CAL) { ++ if (state->current_rf == 0) ++ state->current_rf = state->fe->dtv_property_cache.frequency / 1000; ++ return dib0090_wbd_calibration(state, tune_state); ++ } else if (state->calibrate & TEMP_CAL) ++ return dib0090_get_temperature(state, tune_state); ++ else if (state->calibrate & CAPTRIM_CAL) ++ return dib0090_captrim_search(state, tune_state); ++ ++ if (*tune_state == CT_TUNER_START) { ++ /* if soc and AGC pwm control, disengage mux to be able to R/W access to 0x01 register to set the right filter (cutoff_freq_select) during the tune sequence, otherwise, SOC SERPAR error when accessing to 0x01 */ ++ if (state->config->use_pwm_agc && state->identity.in_soc) { ++ tmp = dib0090_read_reg(state, 0x39); ++ if ((tmp >> 10) & 0x1) ++ dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); ++ } ++ ++ state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000); ++ state->rf_request = ++ state->fe->dtv_property_cache.frequency / 1000 + (state->current_band == ++ BAND_UHF ? state->config->freq_offset_khz_uhf : state->config-> ++ freq_offset_khz_vhf); ++ ++ /* in ISDB-T 1seg we shift tuning frequency */ ++ if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1 ++ && state->fe->dtv_property_cache.isdbt_partial_reception == 0)) { ++ const struct dib0090_low_if_offset_table *LUT_offset = state->config->low_if; ++ u8 found_offset = 0; ++ u32 margin_khz = 100; ++ ++ if (LUT_offset != NULL) { ++ while (LUT_offset->RF_freq != 0xffff) { ++ if (((state->rf_request > (LUT_offset->RF_freq - margin_khz)) ++ && (state->rf_request < (LUT_offset->RF_freq + margin_khz))) ++ && LUT_offset->std == state->fe->dtv_property_cache.delivery_system) { ++ state->rf_request += LUT_offset->offset_khz; ++ found_offset = 1; ++ break; ++ } ++ LUT_offset++; ++ } ++ } ++ ++ if (found_offset == 0) ++ state->rf_request += 400; ++ } ++ if (state->current_rf != state->rf_request || (state->current_standard != state->fe->dtv_property_cache.delivery_system)) { ++ state->tuner_is_tuned = 0; ++ state->current_rf = 0; ++ state->current_standard = 0; ++ ++ tune = dib0090_tuning_table; ++ if (state->identity.p1g) ++ tune = dib0090_p1g_tuning_table; ++ ++ tmp = (state->identity.version >> 5) & 0x7; ++ ++ if (state->identity.in_soc) { ++ if (state->config->force_cband_input) { /* Use the CBAND input for all band */ ++ if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF ++ || state->current_band & BAND_UHF) { ++ state->current_band = BAND_CBAND; ++ if (state->config->is_dib7090e) ++ tune = dib0090_tuning_table_cband_7090e_sensitivity; ++ else ++ tune = dib0090_tuning_table_cband_7090; ++ } ++ } else { /* Use the CBAND input for all band under UHF */ ++ if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF) { ++ state->current_band = BAND_CBAND; ++ if (state->config->is_dib7090e) ++ tune = dib0090_tuning_table_cband_7090e_sensitivity; ++ else ++ tune = dib0090_tuning_table_cband_7090; ++ } ++ } ++ } else ++ if (tmp == 0x4 || tmp == 0x7) { ++ /* CBAND tuner version for VHF */ ++ if (state->current_band == BAND_FM || state->current_band == BAND_CBAND || state->current_band == BAND_VHF) { ++ state->current_band = BAND_CBAND; /* Force CBAND */ ++ ++ tune = dib0090_tuning_table_fm_vhf_on_cband; ++ if (state->identity.p1g) ++ tune = dib0090_p1g_tuning_table_fm_vhf_on_cband; ++ } ++ } ++ ++ pll = dib0090_pll_table; ++ if (state->identity.p1g) ++ pll = dib0090_p1g_pll_table; ++ ++ /* Look for the interval */ ++ while (state->rf_request > tune->max_freq) ++ tune++; ++ while (state->rf_request > pll->max_freq) ++ pll++; ++ ++ state->current_tune_table_index = tune; ++ state->current_pll_table_index = pll; ++ ++ dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); ++ ++ VCOF_kHz = (pll->hfdiv * state->rf_request) * 2; ++ ++ FREF = state->config->io.clock_khz; ++ if (state->config->fref_clock_ratio != 0) ++ FREF /= state->config->fref_clock_ratio; ++ ++ FBDiv = (VCOF_kHz / pll->topresc / FREF); ++ Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; ++ ++ if (Rest < LPF) ++ Rest = 0; ++ else if (Rest < 2 * LPF) ++ Rest = 2 * LPF; ++ else if (Rest > (FREF - LPF)) { ++ Rest = 0; ++ FBDiv += 1; ++ } else if (Rest > (FREF - 2 * LPF)) ++ Rest = FREF - 2 * LPF; ++ Rest = (Rest * 6528) / (FREF / 10); ++ state->rest = Rest; ++ ++ /* external loop filter, otherwise: ++ * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4; ++ * lo6 = 0x0e34 */ ++ ++ if (Rest == 0) { ++ if (pll->vco_band) ++ lo5 = 0x049f; ++ else ++ lo5 = 0x041f; ++ } else { ++ if (pll->vco_band) ++ lo5 = 0x049e; ++ else if (state->config->analog_output) ++ lo5 = 0x041d; ++ else ++ lo5 = 0x041c; ++ } ++ ++ if (state->identity.p1g) { /* Bias is done automatically in P1G */ ++ if (state->identity.in_soc) { ++ if (state->identity.version == SOC_8090_P1G_11R1) ++ lo5 = 0x46f; ++ else ++ lo5 = 0x42f; ++ } else ++ lo5 = 0x42c; ++ } ++ ++ lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ ++ ++ if (!state->config->io.pll_int_loop_filt) { ++ if (state->identity.in_soc) ++ lo6 = 0xff98; ++ else if (state->identity.p1g || (Rest == 0)) ++ lo6 = 0xfff8; ++ else ++ lo6 = 0xff28; ++ } else ++ lo6 = (state->config->io.pll_int_loop_filt << 3); ++ ++ Den = 1; ++ ++ if (Rest > 0) { ++ if (state->config->analog_output) ++ lo6 |= (1 << 2) | 2; ++ else { ++ if (state->identity.in_soc) ++ lo6 |= (1 << 2) | 2; ++ else ++ lo6 |= (1 << 2) | 2; ++ } ++ Den = 255; ++ } ++ dib0090_write_reg(state, 0x15, (u16) FBDiv); ++ if (state->config->fref_clock_ratio != 0) ++ dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio); ++ else ++ dib0090_write_reg(state, 0x16, (Den << 8) | 1); ++ dib0090_write_reg(state, 0x17, (u16) Rest); ++ dib0090_write_reg(state, 0x19, lo5); ++ dib0090_write_reg(state, 0x1c, lo6); ++ ++ lo6 = tune->tuner_enable; ++ if (state->config->analog_output) ++ lo6 = (lo6 & 0xff9f) | 0x2; ++ ++ dib0090_write_reg(state, 0x24, lo6 | EN_LO | state->config->use_pwm_agc * EN_CRYSTAL); ++ ++ } ++ ++ state->current_rf = state->rf_request; ++ state->current_standard = state->fe->dtv_property_cache.delivery_system; ++ ++ ret = 20; ++ state->calibrate = CAPTRIM_CAL; /* captrim serach now */ ++ } ++ ++ else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */ ++ const struct dib0090_wbd_slope *wbd = state->current_wbd_table; ++ ++ while (state->current_rf / 1000 > wbd->max_freq) ++ wbd++; ++ ++ dib0090_write_reg(state, 0x1e, 0x07ff); ++ dprintk("Final Captrim: %d", (u32) state->fcaptrim); ++ dprintk("HFDIV code: %d", (u32) pll->hfdiv_code); ++ dprintk("VCO = %d", (u32) pll->vco_band); ++ dprintk("VCOF in kHz: %d ((%d*%d) << 1))", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request); ++ dprintk("REFDIV: %d, FREF: %d", (u32) 1, (u32) state->config->io.clock_khz); ++ dprintk("FBDIV: %d, Rest: %d", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17)); ++ dprintk("Num: %d, Den: %d, SD: %d", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8), ++ (u32) dib0090_read_reg(state, 0x1c) & 0x3); ++ ++#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ ++ c = 4; ++ i = 3; ++ ++ if (wbd->wbd_gain != 0) ++ c = wbd->wbd_gain; ++ ++ state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1)); ++ dib0090_write_reg(state, 0x10, state->wbdmux); ++ ++ if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) { ++ dprintk("P1G : The cable band is selected and lna_tune = %d", tune->lna_tune); ++ dib0090_write_reg(state, 0x09, tune->lna_bias); ++ dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim)); ++ } else ++ dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | tune->lna_bias); ++ ++ dib0090_write_reg(state, 0x0c, tune->v2i); ++ dib0090_write_reg(state, 0x0d, tune->mix); ++ dib0090_write_reg(state, 0x0e, tune->load); ++ *tune_state = CT_TUNER_STEP_1; ++ ++ } else if (*tune_state == CT_TUNER_STEP_1) { ++ /* initialize the lt gain register */ ++ state->rf_lt_def = 0x7c00; ++ ++ dib0090_set_bandwidth(state); ++ state->tuner_is_tuned = 1; ++ ++ state->calibrate |= WBD_CAL; ++ state->calibrate |= TEMP_CAL; ++ *tune_state = CT_TUNER_STOP; ++ } else ++ ret = FE_CALLBACK_TIME_NEVER; ++ return ret; ++} ++ ++static int dib0090_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ return state->tune_state; ++} ++ ++EXPORT_SYMBOL(dib0090_get_tune_state); ++ ++int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ state->tune_state = tune_state; ++ return 0; ++} ++ ++EXPORT_SYMBOL(dib0090_set_tune_state); ++ ++static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ ++ *frequency = 1000 * state->current_rf; ++ return 0; ++} ++ ++static int dib0090_set_params(struct dvb_frontend *fe) ++{ ++ struct dib0090_state *state = fe->tuner_priv; ++ u32 ret; ++ ++ state->tune_state = CT_TUNER_START; ++ ++ do { ++ ret = dib0090_tune(fe); ++ if (ret != FE_CALLBACK_TIME_NEVER) ++ msleep(ret / 10); ++ else ++ break; ++ } while (state->tune_state != CT_TUNER_STOP); ++ ++ return 0; ++} ++ ++static const struct dvb_tuner_ops dib0090_ops = { ++ .info = { ++ .name = "DiBcom DiB0090", ++ .frequency_min = 45000000, ++ .frequency_max = 860000000, ++ .frequency_step = 1000, ++ }, ++ .release = dib0090_release, ++ ++ .init = dib0090_wakeup, ++ .sleep = dib0090_sleep, ++ .set_params = dib0090_set_params, ++ .get_frequency = dib0090_get_frequency, ++}; ++ ++static const struct dvb_tuner_ops dib0090_fw_ops = { ++ .info = { ++ .name = "DiBcom DiB0090", ++ .frequency_min = 45000000, ++ .frequency_max = 860000000, ++ .frequency_step = 1000, ++ }, ++ .release = dib0090_release, ++ ++ .init = NULL, ++ .sleep = NULL, ++ .set_params = NULL, ++ .get_frequency = NULL, ++}; ++ ++static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = { ++ {470, 0, 250, 0, 100, 4}, ++ {860, 51, 866, 21, 375, 4}, ++ {1700, 0, 800, 0, 850, 4}, ++ {2900, 0, 250, 0, 100, 6}, ++ {0xFFFF, 0, 0, 0, 0, 0}, ++}; ++ ++struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) ++{ ++ struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ ++ st->config = config; ++ st->i2c = i2c; ++ st->fe = fe; ++ mutex_init(&st->i2c_buffer_lock); ++ fe->tuner_priv = st; ++ ++ if (config->wbd == NULL) ++ st->current_wbd_table = dib0090_wbd_table_default; ++ else ++ st->current_wbd_table = config->wbd; ++ ++ if (dib0090_reset(fe) != 0) ++ goto free_mem; ++ ++ printk(KERN_INFO "DiB0090: successfully identified\n"); ++ memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops)); ++ ++ return fe; ++ free_mem: ++ kfree(st); ++ fe->tuner_priv = NULL; ++ return NULL; ++} ++ ++EXPORT_SYMBOL(dib0090_register); ++ ++struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) ++{ ++ struct dib0090_fw_state *st = kzalloc(sizeof(struct dib0090_fw_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ ++ st->config = config; ++ st->i2c = i2c; ++ st->fe = fe; ++ mutex_init(&st->i2c_buffer_lock); ++ fe->tuner_priv = st; ++ ++ if (dib0090_fw_reset_digital(fe, st->config) != 0) ++ goto free_mem; ++ ++ dprintk("DiB0090 FW: successfully identified"); ++ memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops)); ++ ++ return fe; ++free_mem: ++ kfree(st); ++ fe->tuner_priv = NULL; ++ return NULL; ++} ++EXPORT_SYMBOL(dib0090_fw_register); ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_AUTHOR("Olivier Grenie "); ++MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib0090.h b/drivers/media/dvb-frontends/dib0090.h +new file mode 100644 +index 0000000..781dc49 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib0090.h +@@ -0,0 +1,187 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. ++ * ++ * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#ifndef DIB0090_H ++#define DIB0090_H ++ ++struct dvb_frontend; ++struct i2c_adapter; ++ ++#define DEFAULT_DIB0090_I2C_ADDRESS 0x60 ++ ++struct dib0090_io_config { ++ u32 clock_khz; ++ ++ u8 pll_bypass:1; ++ u8 pll_range:1; ++ u8 pll_prediv:6; ++ u8 pll_loopdiv:6; ++ ++ u8 adc_clock_ratio; /* valid is 8, 7 ,6 */ ++ u16 pll_int_loop_filt; ++}; ++ ++struct dib0090_wbd_slope { ++ u16 max_freq; /* for every frequency less than or equal to that field: this information is correct */ ++ u16 slope_cold; ++ u16 offset_cold; ++ u16 slope_hot; ++ u16 offset_hot; ++ u8 wbd_gain; ++}; ++ ++struct dib0090_low_if_offset_table { ++ int std; ++ u32 RF_freq; ++ s32 offset_khz; ++}; ++ ++struct dib0090_config { ++ struct dib0090_io_config io; ++ int (*reset) (struct dvb_frontend *, int); ++ int (*sleep) (struct dvb_frontend *, int); ++ ++ /* offset in kHz */ ++ int freq_offset_khz_uhf; ++ int freq_offset_khz_vhf; ++ ++ int (*get_adc_power) (struct dvb_frontend *); ++ ++ u8 clkouttobamse:1; /* activate or deactivate clock output */ ++ u8 analog_output; ++ ++ u8 i2c_address; ++ /* add drives and other things if necessary */ ++ u16 wbd_vhf_offset; ++ u16 wbd_cband_offset; ++ u8 use_pwm_agc; ++ u8 clkoutdrive; ++ ++ u8 ls_cfg_pad_drv; ++ u8 data_tx_drv; ++ ++ u8 in_soc; ++ const struct dib0090_low_if_offset_table *low_if; ++ u8 fref_clock_ratio; ++ u16 force_cband_input; ++ struct dib0090_wbd_slope *wbd; ++ u8 is_dib7090e; ++ u8 force_crystal_mode; ++}; ++ ++#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); ++extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); ++extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); ++extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe); ++extern u16 dib0090_get_wbd_target(struct dvb_frontend *tuner); ++extern u16 dib0090_get_wbd_offset(struct dvb_frontend *fe); ++extern int dib0090_gain_control(struct dvb_frontend *fe); ++extern enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe); ++extern int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); ++extern void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt); ++extern void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff); ++extern int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3); ++extern int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff); ++extern int dib0090_update_rframp_7090(struct dvb_frontend *fe, ++ u8 cfg_sensitivity); ++extern int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, ++ u8 cfg_sensitivity); ++#else ++static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline void dib0090_pwm_gain_reset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline u16 dib0090_get_wbd_target(struct dvb_frontend *tuner) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline int dib0090_gain_control(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return CT_DONE; ++} ++ ++static inline int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline int dib0090_set_switch(struct dvb_frontend *fe, ++ u8 sw1, u8 sw2, u8 sw3) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib0090_update_rframp_7090(struct dvb_frontend *fe, ++ u8 cfg_sensitivity) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, ++ u8 cfg_sensitivity) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib3000.h b/drivers/media/dvb-frontends/dib3000.h +new file mode 100644 +index 0000000..404f63a +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib3000.h +@@ -0,0 +1,56 @@ ++/* ++ * public header file of the frontend drivers for mobile DVB-T demodulators ++ * DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/) ++ * ++ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) ++ * ++ * based on GPL code from DibCom, which has ++ * ++ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ * ++ * Acknowledgements ++ * ++ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver ++ * sources, on which this driver (and the dvb-dibusb) are based. ++ * ++ * see Documentation/dvb/README.dvb-usb for more information ++ * ++ */ ++ ++#ifndef DIB3000_H ++#define DIB3000_H ++ ++#include ++ ++struct dib3000_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++struct dib_fe_xfer_ops ++{ ++ /* pid and transfer handling is done in the demodulator */ ++ int (*pid_parse)(struct dvb_frontend *fe, int onoff); ++ int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); ++ int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff); ++ int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); ++}; ++ ++#if defined(CONFIG_DVB_DIB3000MB) || (defined(CONFIG_DVB_DIB3000MB_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, ++ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); ++#else ++static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, ++ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_DIB3000MB ++ ++#endif // DIB3000_H +diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c +new file mode 100644 +index 0000000..af91e0c +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib3000mb.c +@@ -0,0 +1,829 @@ ++/* ++ * Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B ++ * DiBcom (http://www.dibcom.fr/) ++ * ++ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) ++ * ++ * based on GPL code from DibCom, which has ++ * ++ * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ * ++ * Acknowledgements ++ * ++ * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver ++ * sources, on which this driver (and the dvb-dibusb) are based. ++ * ++ * see Documentation/dvb/README.dvb-usb for more information ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "dib3000.h" ++#include "dib3000mb_priv.h" ++ ++/* Version information */ ++#define DRIVER_VERSION "0.1" ++#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator" ++#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); ++ ++#define deb_info(args...) dprintk(0x01,args) ++#define deb_i2c(args...) dprintk(0x02,args) ++#define deb_srch(args...) dprintk(0x04,args) ++#define deb_info(args...) dprintk(0x01,args) ++#define deb_xfer(args...) dprintk(0x02,args) ++#define deb_setf(args...) dprintk(0x04,args) ++#define deb_getf(args...) dprintk(0x08,args) ++ ++static int dib3000_read_reg(struct dib3000_state *state, u16 reg) ++{ ++ u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; ++ u8 rb[2]; ++ struct i2c_msg msg[] = { ++ { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, ++ { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, ++ }; ++ ++ if (i2c_transfer(state->i2c, msg, 2) != 2) ++ deb_i2c("i2c read error\n"); ++ ++ deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, ++ (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); ++ ++ return (rb[0] << 8) | rb[1]; ++} ++ ++static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) ++{ ++ u8 b[] = { ++ (reg >> 8) & 0xff, reg & 0xff, ++ (val >> 8) & 0xff, val & 0xff, ++ }; ++ struct i2c_msg msg[] = { ++ { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } ++ }; ++ deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); ++ ++ return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; ++} ++ ++static int dib3000_search_status(u16 irq,u16 lock) ++{ ++ if (irq & 0x02) { ++ if (lock & 0x01) { ++ deb_srch("auto search succeeded\n"); ++ return 1; // auto search succeeded ++ } else { ++ deb_srch("auto search not successful\n"); ++ return 0; // auto search failed ++ } ++ } else if (irq & 0x01) { ++ deb_srch("auto search failed\n"); ++ return 0; // auto search failed ++ } ++ return -1; // try again ++} ++ ++/* for auto search */ ++static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ ++ { /* fft */ ++ { /* gua */ ++ { 0, 1 }, /* 0 0 { 0,1 } */ ++ { 3, 9 }, /* 0 1 { 0,1 } */ ++ }, ++ { ++ { 2, 5 }, /* 1 0 { 0,1 } */ ++ { 6, 11 }, /* 1 1 { 0,1 } */ ++ } ++ }; ++ ++static int dib3000mb_get_frontend(struct dvb_frontend* fe); ++ ++static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ fe_code_rate_t fe_cr = FEC_NONE; ++ int search_state, seq; ++ ++ if (tuner && fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ deb_setf("bandwidth: "); ++ switch (c->bandwidth_hz) { ++ case 8000000: ++ deb_setf("8 MHz\n"); ++ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); ++ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); ++ break; ++ case 7000000: ++ deb_setf("7 MHz\n"); ++ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]); ++ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz); ++ break; ++ case 6000000: ++ deb_setf("6 MHz\n"); ++ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]); ++ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz); ++ break; ++ case 0: ++ return -EOPNOTSUPP; ++ default: ++ err("unknown bandwidth value."); ++ return -EINVAL; ++ } ++ } ++ wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); ++ ++ deb_setf("transmission mode: "); ++ switch (c->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ deb_setf("2k\n"); ++ wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K); ++ break; ++ case TRANSMISSION_MODE_8K: ++ deb_setf("8k\n"); ++ wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K); ++ break; ++ case TRANSMISSION_MODE_AUTO: ++ deb_setf("auto\n"); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ deb_setf("guard: "); ++ switch (c->guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ deb_setf("1_32\n"); ++ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32); ++ break; ++ case GUARD_INTERVAL_1_16: ++ deb_setf("1_16\n"); ++ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16); ++ break; ++ case GUARD_INTERVAL_1_8: ++ deb_setf("1_8\n"); ++ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8); ++ break; ++ case GUARD_INTERVAL_1_4: ++ deb_setf("1_4\n"); ++ wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4); ++ break; ++ case GUARD_INTERVAL_AUTO: ++ deb_setf("auto\n"); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ deb_setf("inversion: "); ++ switch (c->inversion) { ++ case INVERSION_OFF: ++ deb_setf("off\n"); ++ wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF); ++ break; ++ case INVERSION_AUTO: ++ deb_setf("auto "); ++ break; ++ case INVERSION_ON: ++ deb_setf("on\n"); ++ wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ deb_setf("modulation: "); ++ switch (c->modulation) { ++ case QPSK: ++ deb_setf("qpsk\n"); ++ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK); ++ break; ++ case QAM_16: ++ deb_setf("qam16\n"); ++ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM); ++ break; ++ case QAM_64: ++ deb_setf("qam64\n"); ++ wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM); ++ break; ++ case QAM_AUTO: ++ break; ++ default: ++ return -EINVAL; ++ } ++ deb_setf("hierarchy: "); ++ switch (c->hierarchy) { ++ case HIERARCHY_NONE: ++ deb_setf("none "); ++ /* fall through */ ++ case HIERARCHY_1: ++ deb_setf("alpha=1\n"); ++ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1); ++ break; ++ case HIERARCHY_2: ++ deb_setf("alpha=2\n"); ++ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2); ++ break; ++ case HIERARCHY_4: ++ deb_setf("alpha=4\n"); ++ wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4); ++ break; ++ case HIERARCHY_AUTO: ++ deb_setf("alpha=auto\n"); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ deb_setf("hierarchy: "); ++ if (c->hierarchy == HIERARCHY_NONE) { ++ deb_setf("none\n"); ++ wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF); ++ wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP); ++ fe_cr = c->code_rate_HP; ++ } else if (c->hierarchy != HIERARCHY_AUTO) { ++ deb_setf("on\n"); ++ wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON); ++ wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP); ++ fe_cr = c->code_rate_LP; ++ } ++ deb_setf("fec: "); ++ switch (fe_cr) { ++ case FEC_1_2: ++ deb_setf("1_2\n"); ++ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2); ++ break; ++ case FEC_2_3: ++ deb_setf("2_3\n"); ++ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3); ++ break; ++ case FEC_3_4: ++ deb_setf("3_4\n"); ++ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4); ++ break; ++ case FEC_5_6: ++ deb_setf("5_6\n"); ++ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6); ++ break; ++ case FEC_7_8: ++ deb_setf("7_8\n"); ++ wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8); ++ break; ++ case FEC_NONE: ++ deb_setf("none "); ++ break; ++ case FEC_AUTO: ++ deb_setf("auto\n"); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ seq = dib3000_seq ++ [c->transmission_mode == TRANSMISSION_MODE_AUTO] ++ [c->guard_interval == GUARD_INTERVAL_AUTO] ++ [c->inversion == INVERSION_AUTO]; ++ ++ deb_setf("seq? %d\n", seq); ++ ++ wr(DIB3000MB_REG_SEQ, seq); ++ ++ wr(DIB3000MB_REG_ISI, seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE); ++ ++ if (c->transmission_mode == TRANSMISSION_MODE_2K) { ++ if (c->guard_interval == GUARD_INTERVAL_1_8) { ++ wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_2K_1_8); ++ } else { ++ wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_DEFAULT); ++ } ++ ++ wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_2K); ++ } else { ++ wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_DEFAULT); ++ } ++ ++ wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_OFF); ++ wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); ++ wr(DIB3000MB_REG_MOBILE_MODE, DIB3000MB_MOBILE_MODE_OFF); ++ ++ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_high); ++ ++ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_ACTIVATE); ++ ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC + DIB3000MB_RESTART_CTRL); ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); ++ ++ /* wait for AGC lock */ ++ msleep(70); ++ ++ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); ++ ++ /* something has to be auto searched */ ++ if (c->modulation == QAM_AUTO || ++ c->hierarchy == HIERARCHY_AUTO || ++ fe_cr == FEC_AUTO || ++ c->inversion == INVERSION_AUTO) { ++ int as_count=0; ++ ++ deb_setf("autosearch enabled.\n"); ++ ++ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); ++ ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AUTO_SEARCH); ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); ++ ++ while ((search_state = ++ dib3000_search_status( ++ rd(DIB3000MB_REG_AS_IRQ_PENDING), ++ rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100) ++ msleep(1); ++ ++ deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count); ++ ++ if (search_state == 1) { ++ if (dib3000mb_get_frontend(fe) == 0) { ++ deb_setf("reading tuning data from frontend succeeded.\n"); ++ return dib3000mb_set_frontend(fe, 0); ++ } ++ } ++ ++ } else { ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_CTRL); ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); ++ } ++ ++ return 0; ++} ++ ++static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ ++ deb_info("dib3000mb is getting up.\n"); ++ wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_UP); ++ ++ wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC); ++ ++ wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE); ++ wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE_RST); ++ ++ wr(DIB3000MB_REG_CLOCK, DIB3000MB_CLOCK_DEFAULT); ++ ++ wr(DIB3000MB_REG_ELECT_OUT_MODE, DIB3000MB_ELECT_OUT_MODE_ON); ++ ++ wr(DIB3000MB_REG_DDS_FREQ_MSB, DIB3000MB_DDS_FREQ_MSB); ++ wr(DIB3000MB_REG_DDS_FREQ_LSB, DIB3000MB_DDS_FREQ_LSB); ++ ++ wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); ++ ++ wr_foreach(dib3000mb_reg_impulse_noise, ++ dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]); ++ ++ wr_foreach(dib3000mb_reg_agc_gain, dib3000mb_default_agc_gain); ++ ++ wr(DIB3000MB_REG_PHASE_NOISE, DIB3000MB_PHASE_NOISE_DEFAULT); ++ ++ wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase); ++ ++ wr_foreach(dib3000mb_reg_lock_duration, dib3000mb_default_lock_duration); ++ ++ wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); ++ ++ wr(DIB3000MB_REG_LOCK0_MASK, DIB3000MB_LOCK0_DEFAULT); ++ wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); ++ wr(DIB3000MB_REG_LOCK2_MASK, DIB3000MB_LOCK2_DEFAULT); ++ wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]); ++ ++ wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); ++ ++ wr(DIB3000MB_REG_UNK_68, DIB3000MB_UNK_68); ++ wr(DIB3000MB_REG_UNK_69, DIB3000MB_UNK_69); ++ wr(DIB3000MB_REG_UNK_71, DIB3000MB_UNK_71); ++ wr(DIB3000MB_REG_UNK_77, DIB3000MB_UNK_77); ++ wr(DIB3000MB_REG_UNK_78, DIB3000MB_UNK_78); ++ wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); ++ wr(DIB3000MB_REG_UNK_92, DIB3000MB_UNK_92); ++ wr(DIB3000MB_REG_UNK_96, DIB3000MB_UNK_96); ++ wr(DIB3000MB_REG_UNK_97, DIB3000MB_UNK_97); ++ wr(DIB3000MB_REG_UNK_106, DIB3000MB_UNK_106); ++ wr(DIB3000MB_REG_UNK_107, DIB3000MB_UNK_107); ++ wr(DIB3000MB_REG_UNK_108, DIB3000MB_UNK_108); ++ wr(DIB3000MB_REG_UNK_122, DIB3000MB_UNK_122); ++ wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); ++ wr(DIB3000MB_REG_BERLEN, DIB3000MB_BERLEN_DEFAULT); ++ ++ wr_foreach(dib3000mb_reg_filter_coeffs, dib3000mb_filter_coeffs); ++ ++ wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_ON); ++ wr(DIB3000MB_REG_MULTI_DEMOD_MSB, DIB3000MB_MULTI_DEMOD_MSB); ++ wr(DIB3000MB_REG_MULTI_DEMOD_LSB, DIB3000MB_MULTI_DEMOD_LSB); ++ ++ wr(DIB3000MB_REG_OUTPUT_MODE, DIB3000MB_OUTPUT_MODE_SLAVE); ++ ++ wr(DIB3000MB_REG_FIFO_142, DIB3000MB_FIFO_142); ++ wr(DIB3000MB_REG_MPEG2_OUT_MODE, DIB3000MB_MPEG2_OUT_MODE_188); ++ wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE); ++ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); ++ wr(DIB3000MB_REG_FIFO_146, DIB3000MB_FIFO_146); ++ wr(DIB3000MB_REG_FIFO_147, DIB3000MB_FIFO_147); ++ ++ wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); ++ ++ return 0; ++} ++ ++static int dib3000mb_get_frontend(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dib3000_state* state = fe->demodulator_priv; ++ fe_code_rate_t *cr; ++ u16 tps_val; ++ int inv_test1,inv_test2; ++ u32 dds_val, threshold = 0x800000; ++ ++ if (!rd(DIB3000MB_REG_TPS_LOCK)) ++ return 0; ++ ++ dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); ++ deb_getf("DDS_VAL: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB)); ++ if (dds_val < threshold) ++ inv_test1 = 0; ++ else if (dds_val == threshold) ++ inv_test1 = 1; ++ else ++ inv_test1 = 2; ++ ++ dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); ++ deb_getf("DDS_FREQ: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB)); ++ if (dds_val < threshold) ++ inv_test2 = 0; ++ else if (dds_val == threshold) ++ inv_test2 = 1; ++ else ++ inv_test2 = 2; ++ ++ c->inversion = ++ ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || ++ ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? ++ INVERSION_ON : INVERSION_OFF; ++ ++ deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, c->inversion); ++ ++ switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { ++ case DIB3000_CONSTELLATION_QPSK: ++ deb_getf("QPSK "); ++ c->modulation = QPSK; ++ break; ++ case DIB3000_CONSTELLATION_16QAM: ++ deb_getf("QAM16 "); ++ c->modulation = QAM_16; ++ break; ++ case DIB3000_CONSTELLATION_64QAM: ++ deb_getf("QAM64 "); ++ c->modulation = QAM_64; ++ break; ++ default: ++ err("Unexpected constellation returned by TPS (%d)", tps_val); ++ break; ++ } ++ deb_getf("TPS: %d\n", tps_val); ++ ++ if (rd(DIB3000MB_REG_TPS_HRCH)) { ++ deb_getf("HRCH ON\n"); ++ cr = &c->code_rate_LP; ++ c->code_rate_HP = FEC_NONE; ++ switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { ++ case DIB3000_ALPHA_0: ++ deb_getf("HIERARCHY_NONE "); ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case DIB3000_ALPHA_1: ++ deb_getf("HIERARCHY_1 "); ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case DIB3000_ALPHA_2: ++ deb_getf("HIERARCHY_2 "); ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case DIB3000_ALPHA_4: ++ deb_getf("HIERARCHY_4 "); ++ c->hierarchy = HIERARCHY_4; ++ break; ++ default: ++ err("Unexpected ALPHA value returned by TPS (%d)", tps_val); ++ break; ++ } ++ deb_getf("TPS: %d\n", tps_val); ++ ++ tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); ++ } else { ++ deb_getf("HRCH OFF\n"); ++ cr = &c->code_rate_HP; ++ c->code_rate_LP = FEC_NONE; ++ c->hierarchy = HIERARCHY_NONE; ++ ++ tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); ++ } ++ ++ switch (tps_val) { ++ case DIB3000_FEC_1_2: ++ deb_getf("FEC_1_2 "); ++ *cr = FEC_1_2; ++ break; ++ case DIB3000_FEC_2_3: ++ deb_getf("FEC_2_3 "); ++ *cr = FEC_2_3; ++ break; ++ case DIB3000_FEC_3_4: ++ deb_getf("FEC_3_4 "); ++ *cr = FEC_3_4; ++ break; ++ case DIB3000_FEC_5_6: ++ deb_getf("FEC_5_6 "); ++ *cr = FEC_4_5; ++ break; ++ case DIB3000_FEC_7_8: ++ deb_getf("FEC_7_8 "); ++ *cr = FEC_7_8; ++ break; ++ default: ++ err("Unexpected FEC returned by TPS (%d)", tps_val); ++ break; ++ } ++ deb_getf("TPS: %d\n",tps_val); ++ ++ switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { ++ case DIB3000_GUARD_TIME_1_32: ++ deb_getf("GUARD_INTERVAL_1_32 "); ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case DIB3000_GUARD_TIME_1_16: ++ deb_getf("GUARD_INTERVAL_1_16 "); ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case DIB3000_GUARD_TIME_1_8: ++ deb_getf("GUARD_INTERVAL_1_8 "); ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case DIB3000_GUARD_TIME_1_4: ++ deb_getf("GUARD_INTERVAL_1_4 "); ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ default: ++ err("Unexpected Guard Time returned by TPS (%d)", tps_val); ++ break; ++ } ++ deb_getf("TPS: %d\n", tps_val); ++ ++ switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { ++ case DIB3000_TRANSMISSION_MODE_2K: ++ deb_getf("TRANSMISSION_MODE_2K "); ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case DIB3000_TRANSMISSION_MODE_8K: ++ deb_getf("TRANSMISSION_MODE_8K "); ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ default: ++ err("unexpected transmission mode return by TPS (%d)", tps_val); ++ break; ++ } ++ deb_getf("TPS: %d\n", tps_val); ++ ++ return 0; ++} ++ ++static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ ++ *stat = 0; ++ ++ if (rd(DIB3000MB_REG_AGC_LOCK)) ++ *stat |= FE_HAS_SIGNAL; ++ if (rd(DIB3000MB_REG_CARRIER_LOCK)) ++ *stat |= FE_HAS_CARRIER; ++ if (rd(DIB3000MB_REG_VIT_LCK)) ++ *stat |= FE_HAS_VITERBI; ++ if (rd(DIB3000MB_REG_TS_SYNC_LOCK)) ++ *stat |= (FE_HAS_SYNC | FE_HAS_LOCK); ++ ++ deb_getf("actual status is %2x\n",*stat); ++ ++ deb_getf("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n", ++ rd(DIB3000MB_REG_TPS_LOCK), ++ rd(DIB3000MB_REG_TPS_QAM), ++ rd(DIB3000MB_REG_TPS_HRCH), ++ rd(DIB3000MB_REG_TPS_VIT_ALPHA), ++ rd(DIB3000MB_REG_TPS_CODE_RATE_HP), ++ rd(DIB3000MB_REG_TPS_CODE_RATE_LP), ++ rd(DIB3000MB_REG_TPS_GUARD_TIME), ++ rd(DIB3000MB_REG_TPS_FFT), ++ rd(DIB3000MB_REG_TPS_CELL_ID)); ++ ++ //*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ return 0; ++} ++ ++static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ ++ *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB)); ++ return 0; ++} ++ ++/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */ ++static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ ++ *strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170; ++ return 0; ++} ++ ++static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER); ++ int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) | ++ rd(DIB3000MB_REG_NOISE_POWER_LSB); ++ *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1); ++ return 0; ++} ++ ++static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ ++ *unc = rd(DIB3000MB_REG_PACKET_ERROR_RATE); ++ return 0; ++} ++ ++static int dib3000mb_sleep(struct dvb_frontend* fe) ++{ ++ struct dib3000_state* state = fe->demodulator_priv; ++ deb_info("dib3000mb is going to bed.\n"); ++ wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_DOWN); ++ return 0; ++} ++ ++static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 800; ++ return 0; ++} ++ ++static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe) ++{ ++ return dib3000mb_fe_init(fe, 0); ++} ++ ++static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend *fe) ++{ ++ return dib3000mb_set_frontend(fe, 1); ++} ++ ++static void dib3000mb_release(struct dvb_frontend* fe) ++{ ++ struct dib3000_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++/* pid filter and transfer stuff */ ++static int dib3000mb_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) ++{ ++ struct dib3000_state *state = fe->demodulator_priv; ++ pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); ++ wr(index+DIB3000MB_REG_FIRST_PID,pid); ++ return 0; ++} ++ ++static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib3000_state *state = fe->demodulator_priv; ++ ++ deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling"); ++ if (onoff) { ++ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE); ++ } else { ++ wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); ++ } ++ return 0; ++} ++ ++static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib3000_state *state = fe->demodulator_priv; ++ deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling"); ++ wr(DIB3000MB_REG_PID_PARSE,onoff); ++ return 0; ++} ++ ++static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr) ++{ ++ struct dib3000_state *state = fe->demodulator_priv; ++ if (onoff) { ++ wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr)); ++ } else { ++ wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr)); ++ } ++ return 0; ++} ++ ++static struct dvb_frontend_ops dib3000mb_ops; ++ ++struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, ++ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) ++{ ++ struct dib3000_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->config,config,sizeof(struct dib3000_config)); ++ ++ /* check for the correct demod */ ++ if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) ++ goto error; ++ ++ if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* set the xfer operations */ ++ xfer_ops->pid_parse = dib3000mb_pid_parse; ++ xfer_ops->fifo_ctrl = dib3000mb_fifo_control; ++ xfer_ops->pid_ctrl = dib3000mb_pid_control; ++ xfer_ops->tuner_pass_ctrl = dib3000mb_tuner_pass_ctrl; ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops dib3000mb_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DiBcom 3000M-B DVB-T", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib3000mb_release, ++ ++ .init = dib3000mb_fe_init_nonmobile, ++ .sleep = dib3000mb_sleep, ++ ++ .set_frontend = dib3000mb_set_frontend_and_tuner, ++ .get_frontend = dib3000mb_get_frontend, ++ .get_tune_settings = dib3000mb_fe_get_tune_settings, ++ ++ .read_status = dib3000mb_read_status, ++ .read_ber = dib3000mb_read_ber, ++ .read_signal_strength = dib3000mb_read_signal_strength, ++ .read_snr = dib3000mb_read_snr, ++ .read_ucblocks = dib3000mb_read_unc_blocks, ++}; ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(dib3000mb_attach); +diff --git a/drivers/media/dvb-frontends/dib3000mb_priv.h b/drivers/media/dvb-frontends/dib3000mb_priv.h +new file mode 100644 +index 0000000..9dc235a +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib3000mb_priv.h +@@ -0,0 +1,556 @@ ++/* ++ * dib3000mb_priv.h ++ * ++ * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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, version 2. ++ * ++ * for more information see dib3000mb.c . ++ */ ++ ++#ifndef __DIB3000MB_PRIV_H_INCLUDED__ ++#define __DIB3000MB_PRIV_H_INCLUDED__ ++ ++/* info and err, taken from usb.h, if there is anything available like by default. */ ++#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg) ++ ++/* handy shortcuts */ ++#define rd(reg) dib3000_read_reg(state,reg) ++ ++#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ ++ { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } ++ ++#define wr_foreach(a,v) { int i; \ ++ if (sizeof(a) != sizeof(v)) \ ++ err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\ ++ for (i=0; i < sizeof(a)/sizeof(u16); i++) \ ++ wr(a[i],v[i]); \ ++ } ++ ++#define set_or(reg,val) wr(reg,rd(reg) | val) ++ ++#define set_and(reg,val) wr(reg,rd(reg) & val) ++ ++/* debug */ ++ ++#define dprintk(level,args...) \ ++ do { if ((debug & level)) { printk(args); } } while (0) ++ ++/* mask for enabling a specific pid for the pid_filter */ ++#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) ++ ++/* common values for tuning */ ++#define DIB3000_ALPHA_0 ( 0) ++#define DIB3000_ALPHA_1 ( 1) ++#define DIB3000_ALPHA_2 ( 2) ++#define DIB3000_ALPHA_4 ( 4) ++ ++#define DIB3000_CONSTELLATION_QPSK ( 0) ++#define DIB3000_CONSTELLATION_16QAM ( 1) ++#define DIB3000_CONSTELLATION_64QAM ( 2) ++ ++#define DIB3000_GUARD_TIME_1_32 ( 0) ++#define DIB3000_GUARD_TIME_1_16 ( 1) ++#define DIB3000_GUARD_TIME_1_8 ( 2) ++#define DIB3000_GUARD_TIME_1_4 ( 3) ++ ++#define DIB3000_TRANSMISSION_MODE_2K ( 0) ++#define DIB3000_TRANSMISSION_MODE_8K ( 1) ++ ++#define DIB3000_SELECT_LP ( 0) ++#define DIB3000_SELECT_HP ( 1) ++ ++#define DIB3000_FEC_1_2 ( 1) ++#define DIB3000_FEC_2_3 ( 2) ++#define DIB3000_FEC_3_4 ( 3) ++#define DIB3000_FEC_5_6 ( 5) ++#define DIB3000_FEC_7_8 ( 7) ++ ++#define DIB3000_HRCH_OFF ( 0) ++#define DIB3000_HRCH_ON ( 1) ++ ++#define DIB3000_DDS_INVERSION_OFF ( 0) ++#define DIB3000_DDS_INVERSION_ON ( 1) ++ ++#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8)) ++#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7))) ++ ++#define DIB3000_REG_MANUFACTOR_ID ( 1025) ++#define DIB3000_I2C_ID_DIBCOM (0x01b3) ++ ++#define DIB3000_REG_DEVICE_ID ( 1026) ++#define DIB3000MB_DEVICE_ID (0x3000) ++#define DIB3000MC_DEVICE_ID (0x3001) ++#define DIB3000P_DEVICE_ID (0x3002) ++ ++/* frontend state */ ++struct dib3000_state { ++ struct i2c_adapter* i2c; ++ ++/* configuration settings */ ++ struct dib3000_config config; ++ ++ struct dvb_frontend frontend; ++ int timing_offset; ++ int timing_offset_comp_done; ++ ++ u32 last_tuned_bw; ++ u32 last_tuned_freq; ++}; ++ ++/* register addresses and some of their default values */ ++ ++/* restart subsystems */ ++#define DIB3000MB_REG_RESTART ( 0) ++ ++#define DIB3000MB_RESTART_OFF ( 0) ++#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1) ++#define DIB3000MB_RESTART_CTRL (1 << 2) ++#define DIB3000MB_RESTART_AGC (1 << 3) ++ ++/* FFT size */ ++#define DIB3000MB_REG_FFT ( 1) ++ ++/* Guard time */ ++#define DIB3000MB_REG_GUARD_TIME ( 2) ++ ++/* QAM */ ++#define DIB3000MB_REG_QAM ( 3) ++ ++/* Alpha coefficient high priority Viterbi algorithm */ ++#define DIB3000MB_REG_VIT_ALPHA ( 4) ++ ++/* spectrum inversion */ ++#define DIB3000MB_REG_DDS_INV ( 5) ++ ++/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ ++#define DIB3000MB_REG_DDS_FREQ_MSB ( 6) ++#define DIB3000MB_REG_DDS_FREQ_LSB ( 7) ++#define DIB3000MB_DDS_FREQ_MSB ( 178) ++#define DIB3000MB_DDS_FREQ_LSB ( 8990) ++ ++/* timing frequency (carrier spacing) */ ++static u16 dib3000mb_reg_timing_freq[] = { 8,9 }; ++static u16 dib3000mb_timing_freq[][2] = { ++ { 126 , 48873 }, /* 6 MHz */ ++ { 147 , 57019 }, /* 7 MHz */ ++ { 168 , 65164 }, /* 8 MHz */ ++}; ++ ++/* impulse noise parameter */ ++/* 36 ??? */ ++ ++static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 }; ++ ++enum dib3000mb_impulse_noise_type { ++ DIB3000MB_IMPNOISE_OFF, ++ DIB3000MB_IMPNOISE_MOBILE, ++ DIB3000MB_IMPNOISE_FIXED, ++ DIB3000MB_IMPNOISE_DEFAULT ++}; ++ ++static u16 dib3000mb_impulse_noise_values[][5] = { ++ { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ ++ { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ ++ { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */ ++ { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */ ++}; ++ ++/* ++ * Dual Automatic-Gain-Control ++ * - gains RF in tuner (AGC1) ++ * - gains IF after filtering (AGC2) ++ */ ++ ++/* also from 16 to 18 */ ++static u16 dib3000mb_reg_agc_gain[] = { ++ 19,20,21,22,23,24,25,26,27,28,29,30,31,32 ++}; ++ ++static u16 dib3000mb_default_agc_gain[] = ++ { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ ++ 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ ++ ++/* phase noise */ ++/* 36 is set when setting the impulse noise */ ++static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 }; ++ ++static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; ++ ++/* lock duration */ ++static u16 dib3000mb_reg_lock_duration[] = { 39,40 }; ++static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; ++ ++/* AGC loop bandwidth */ ++static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 }; ++ ++static u16 dib3000mb_agc_bandwidth_low[] = ++ { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; ++static u16 dib3000mb_agc_bandwidth_high[] = ++ { 2349, 5, 2349, 5, 2586, 2, 2586, 2 }; ++ ++/* ++ * lock0 definition (coff_lock) ++ */ ++#define DIB3000MB_REG_LOCK0_MASK ( 51) ++#define DIB3000MB_LOCK0_DEFAULT ( 4) ++ ++/* ++ * lock1 definition (cpil_lock) ++ * for auto search ++ * which values hide behind the lock masks ++ */ ++#define DIB3000MB_REG_LOCK1_MASK ( 52) ++#define DIB3000MB_LOCK1_SEARCH_4 (0x0004) ++#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800) ++#define DIB3000MB_LOCK1_DEFAULT (0x0001) ++ ++/* ++ * lock2 definition (fec_lock) */ ++#define DIB3000MB_REG_LOCK2_MASK ( 53) ++#define DIB3000MB_LOCK2_DEFAULT (0x0080) ++ ++/* ++ * SEQ ? what was that again ... :) ++ * changes when, inversion, guard time and fft is ++ * either automatically detected or not ++ */ ++#define DIB3000MB_REG_SEQ ( 54) ++ ++/* bandwidth */ ++static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; ++static u16 dib3000mb_bandwidth_6mhz[] = ++ { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; ++ ++static u16 dib3000mb_bandwidth_7mhz[] = ++ { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 }; ++ ++static u16 dib3000mb_bandwidth_8mhz[] = ++ { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 }; ++ ++#define DIB3000MB_REG_UNK_68 ( 68) ++#define DIB3000MB_UNK_68 ( 0) ++ ++#define DIB3000MB_REG_UNK_69 ( 69) ++#define DIB3000MB_UNK_69 ( 0) ++ ++#define DIB3000MB_REG_UNK_71 ( 71) ++#define DIB3000MB_UNK_71 ( 0) ++ ++#define DIB3000MB_REG_UNK_77 ( 77) ++#define DIB3000MB_UNK_77 ( 6) ++ ++#define DIB3000MB_REG_UNK_78 ( 78) ++#define DIB3000MB_UNK_78 (0x0080) ++ ++/* isi */ ++#define DIB3000MB_REG_ISI ( 79) ++#define DIB3000MB_ISI_ACTIVATE ( 0) ++#define DIB3000MB_ISI_INHIBIT ( 1) ++ ++/* sync impovement */ ++#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84) ++#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3) ++#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0) ++ ++/* phase noise compensation inhibition */ ++#define DIB3000MB_REG_PHASE_NOISE ( 87) ++#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0) ++ ++#define DIB3000MB_REG_UNK_92 ( 92) ++#define DIB3000MB_UNK_92 (0x0080) ++ ++#define DIB3000MB_REG_UNK_96 ( 96) ++#define DIB3000MB_UNK_96 (0x0010) ++ ++#define DIB3000MB_REG_UNK_97 ( 97) ++#define DIB3000MB_UNK_97 (0x0009) ++ ++/* mobile mode ??? */ ++#define DIB3000MB_REG_MOBILE_MODE ( 101) ++#define DIB3000MB_MOBILE_MODE_ON ( 1) ++#define DIB3000MB_MOBILE_MODE_OFF ( 0) ++ ++#define DIB3000MB_REG_UNK_106 ( 106) ++#define DIB3000MB_UNK_106 (0x0080) ++ ++#define DIB3000MB_REG_UNK_107 ( 107) ++#define DIB3000MB_UNK_107 (0x0080) ++ ++#define DIB3000MB_REG_UNK_108 ( 108) ++#define DIB3000MB_UNK_108 (0x0080) ++ ++/* fft */ ++#define DIB3000MB_REG_UNK_121 ( 121) ++#define DIB3000MB_UNK_121_2K ( 7) ++#define DIB3000MB_UNK_121_DEFAULT ( 5) ++ ++#define DIB3000MB_REG_UNK_122 ( 122) ++#define DIB3000MB_UNK_122 ( 2867) ++ ++/* QAM for mobile mode */ ++#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) ++#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) ++#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) ++#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) ++ ++/* ++ * data diversity when having more than one chip on-board ++ * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ++ */ ++#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127) ++#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0) ++#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2) ++ ++/* vit hrch */ ++#define DIB3000MB_REG_VIT_HRCH ( 128) ++ ++/* vit code rate */ ++#define DIB3000MB_REG_VIT_CODE_RATE ( 129) ++ ++/* vit select hp */ ++#define DIB3000MB_REG_VIT_HP ( 130) ++ ++/* time frame for Bit-Error-Rate calculation */ ++#define DIB3000MB_REG_BERLEN ( 135) ++#define DIB3000MB_BERLEN_LONG ( 0) ++#define DIB3000MB_BERLEN_DEFAULT ( 1) ++#define DIB3000MB_BERLEN_MEDIUM ( 2) ++#define DIB3000MB_BERLEN_SHORT ( 3) ++ ++/* 142 - 152 FIFO parameters ++ * which is what ? ++ */ ++ ++#define DIB3000MB_REG_FIFO_142 ( 142) ++#define DIB3000MB_FIFO_142 ( 0) ++ ++/* MPEG2 TS output mode */ ++#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143) ++#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) ++#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) ++ ++#define DIB3000MB_REG_PID_PARSE ( 144) ++#define DIB3000MB_PID_PARSE_INHIBIT ( 0) ++#define DIB3000MB_PID_PARSE_ACTIVATE ( 1) ++ ++#define DIB3000MB_REG_FIFO ( 145) ++#define DIB3000MB_FIFO_INHIBIT ( 1) ++#define DIB3000MB_FIFO_ACTIVATE ( 0) ++ ++#define DIB3000MB_REG_FIFO_146 ( 146) ++#define DIB3000MB_FIFO_146 ( 3) ++ ++#define DIB3000MB_REG_FIFO_147 ( 147) ++#define DIB3000MB_FIFO_147 (0x0100) ++ ++/* ++ * pidfilter ++ * it is not a hardware pidfilter but a filter which drops all pids ++ * except the ones set. Necessary because of the limited USB1.1 bandwidth. ++ * regs 153-168 ++ */ ++ ++#define DIB3000MB_REG_FIRST_PID ( 153) ++#define DIB3000MB_NUM_PIDS ( 16) ++ ++/* ++ * output mode ++ * USB devices have to use 'slave'-mode ++ * see also DIB3000MB_REG_ELECT_OUT_MODE ++ */ ++#define DIB3000MB_REG_OUTPUT_MODE ( 169) ++#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0) ++#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1) ++#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2) ++#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5) ++#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6) ++ ++/* irq event mask */ ++#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170) ++#define DIB3000MB_IRQ_EVENT_MASK ( 0) ++ ++/* filter coefficients */ ++static u16 dib3000mb_reg_filter_coeffs[] = { ++ 171, 172, 173, 174, 175, 176, 177, 178, ++ 179, 180, 181, 182, 183, 184, 185, 186, ++ 188, 189, 190, 191, 192, 194 ++}; ++ ++static u16 dib3000mb_filter_coeffs[] = { ++ 226, 160, 29, ++ 979, 998, 19, ++ 22, 1019, 1006, ++ 1022, 12, 6, ++ 1017, 1017, 3, ++ 6, 1019, ++ 1021, 2, 3, ++ 1, 0, ++}; ++ ++/* ++ * mobile algorithm (when you are moving with your device) ++ * but not faster than 90 km/h ++ */ ++#define DIB3000MB_REG_MOBILE_ALGO ( 195) ++#define DIB3000MB_MOBILE_ALGO_ON ( 0) ++#define DIB3000MB_MOBILE_ALGO_OFF ( 1) ++ ++/* multiple demodulators algorithm */ ++#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206) ++#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207) ++ ++/* terminator, no more demods */ ++#define DIB3000MB_MULTI_DEMOD_MSB ( 32767) ++#define DIB3000MB_MULTI_DEMOD_LSB ( 4095) ++ ++/* bring the device into a known */ ++#define DIB3000MB_REG_RESET_DEVICE ( 1024) ++#define DIB3000MB_RESET_DEVICE (0x812c) ++#define DIB3000MB_RESET_DEVICE_RST ( 0) ++ ++/* hardware clock configuration */ ++#define DIB3000MB_REG_CLOCK ( 1027) ++#define DIB3000MB_CLOCK_DEFAULT (0x9000) ++#define DIB3000MB_CLOCK_DIVERSITY (0x92b0) ++ ++/* power down config */ ++#define DIB3000MB_REG_POWER_CONTROL ( 1028) ++#define DIB3000MB_POWER_DOWN ( 1) ++#define DIB3000MB_POWER_UP ( 0) ++ ++/* electrical output mode */ ++#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029) ++#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0) ++#define DIB3000MB_ELECT_OUT_MODE_ON ( 1) ++ ++/* set the tuner i2c address */ ++#define DIB3000MB_REG_TUNER ( 1089) ++ ++/* monitoring registers (read only) */ ++ ++/* agc loop locked (size: 1) */ ++#define DIB3000MB_REG_AGC_LOCK ( 324) ++ ++/* agc power (size: 16) */ ++#define DIB3000MB_REG_AGC_POWER ( 325) ++ ++/* agc1 value (16) */ ++#define DIB3000MB_REG_AGC1_VALUE ( 326) ++ ++/* agc2 value (16) */ ++#define DIB3000MB_REG_AGC2_VALUE ( 327) ++ ++/* total RF power (16), can be used for signal strength */ ++#define DIB3000MB_REG_RF_POWER ( 328) ++ ++/* dds_frequency with offset (24) */ ++#define DIB3000MB_REG_DDS_VALUE_MSB ( 339) ++#define DIB3000MB_REG_DDS_VALUE_LSB ( 340) ++ ++/* timing offset signed (24) */ ++#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341) ++#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342) ++ ++/* fft start position (13) */ ++#define DIB3000MB_REG_FFT_WINDOW_POS ( 353) ++ ++/* carriers locked (1) */ ++#define DIB3000MB_REG_CARRIER_LOCK ( 355) ++ ++/* noise power (24) */ ++#define DIB3000MB_REG_NOISE_POWER_MSB ( 372) ++#define DIB3000MB_REG_NOISE_POWER_LSB ( 373) ++ ++#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374) ++#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375) ++ ++/* ++ * signal power (16), this and the above can be ++ * used to calculate the signal/noise - ratio ++ */ ++#define DIB3000MB_REG_SIGNAL_POWER ( 380) ++ ++/* mer (24) */ ++#define DIB3000MB_REG_MER_MSB ( 381) ++#define DIB3000MB_REG_MER_LSB ( 382) ++ ++/* ++ * Transmission Parameter Signalling (TPS) ++ * the following registers can be used to get TPS-information. ++ * The values are according to the DVB-T standard. ++ */ ++ ++/* TPS locked (1) */ ++#define DIB3000MB_REG_TPS_LOCK ( 394) ++ ++/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */ ++#define DIB3000MB_REG_TPS_QAM ( 398) ++ ++/* hierarchy from TPS (1) */ ++#define DIB3000MB_REG_TPS_HRCH ( 399) ++ ++/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */ ++#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400) ++ ++/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */ ++#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401) ++ ++/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */ ++#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402) ++ ++/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */ ++#define DIB3000MB_REG_TPS_GUARD_TIME ( 403) ++ ++/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */ ++#define DIB3000MB_REG_TPS_FFT ( 404) ++ ++/* cell id from TPS (16) */ ++#define DIB3000MB_REG_TPS_CELL_ID ( 406) ++ ++/* TPS (68) */ ++#define DIB3000MB_REG_TPS_1 ( 408) ++#define DIB3000MB_REG_TPS_2 ( 409) ++#define DIB3000MB_REG_TPS_3 ( 410) ++#define DIB3000MB_REG_TPS_4 ( 411) ++#define DIB3000MB_REG_TPS_5 ( 412) ++ ++/* bit error rate (before RS correction) (21) */ ++#define DIB3000MB_REG_BER_MSB ( 414) ++#define DIB3000MB_REG_BER_LSB ( 415) ++ ++/* packet error rate (uncorrected TS packets) (16) */ ++#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417) ++ ++/* uncorrected packet count (16) */ ++#define DIB3000MB_REG_UNC ( 420) ++ ++/* viterbi locked (1) */ ++#define DIB3000MB_REG_VIT_LCK ( 421) ++ ++/* viterbi inidcator (16) */ ++#define DIB3000MB_REG_VIT_INDICATOR ( 422) ++ ++/* transport stream sync lock (1) */ ++#define DIB3000MB_REG_TS_SYNC_LOCK ( 423) ++ ++/* transport stream RS lock (1) */ ++#define DIB3000MB_REG_TS_RS_LOCK ( 424) ++ ++/* lock mask 0 value (1) */ ++#define DIB3000MB_REG_LOCK0_VALUE ( 425) ++ ++/* lock mask 1 value (1) */ ++#define DIB3000MB_REG_LOCK1_VALUE ( 426) ++ ++/* lock mask 2 value (1) */ ++#define DIB3000MB_REG_LOCK2_VALUE ( 427) ++ ++/* interrupt pending for auto search */ ++#define DIB3000MB_REG_AS_IRQ_PENDING ( 434) ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c +new file mode 100644 +index 0000000..ffad181 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib3000mc.c +@@ -0,0 +1,940 @@ ++/* ++ * Driver for DiBcom DiB3000MC/P-demodulator. ++ * ++ * Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/) ++ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) ++ * ++ * This code is partially based on the previous dib3000mc.c . ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++ ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "dib3000mc.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++static int buggy_sfn_workaround; ++module_param(buggy_sfn_workaround, int, 0644); ++MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0) ++ ++struct dib3000mc_state { ++ struct dvb_frontend demod; ++ struct dib3000mc_config *cfg; ++ ++ u8 i2c_addr; ++ struct i2c_adapter *i2c_adap; ++ ++ struct dibx000_i2c_master i2c_master; ++ ++ u32 timf; ++ ++ u32 current_bandwidth; ++ ++ u16 dev_id; ++ ++ u8 sfn_workaround_active :1; ++}; ++ ++static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg) ++{ ++ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; ++ u8 rb[2]; ++ struct i2c_msg msg[2] = { ++ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, ++ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, ++ }; ++ ++ if (i2c_transfer(state->i2c_adap, msg, 2) != 2) ++ dprintk("i2c read error on %d\n",reg); ++ ++ return (rb[0] << 8) | rb[1]; ++} ++ ++static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val) ++{ ++ u8 b[4] = { ++ (reg >> 8) & 0xff, reg & 0xff, ++ (val >> 8) & 0xff, val & 0xff, ++ }; ++ struct i2c_msg msg = { ++ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 ++ }; ++ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; ++} ++ ++static int dib3000mc_identify(struct dib3000mc_state *state) ++{ ++ u16 value; ++ if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) { ++ dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value); ++ return -EREMOTEIO; ++ } ++ ++ value = dib3000mc_read_word(state, 1026); ++ if (value != 0x3001 && value != 0x3002) { ++ dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value); ++ return -EREMOTEIO; ++ } ++ state->dev_id = value; ++ ++ dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id); ++ ++ return 0; ++} ++ ++static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset) ++{ ++ u32 timf; ++ ++ if (state->timf == 0) { ++ timf = 1384402; // default value for 8MHz ++ if (update_offset) ++ msleep(200); // first time we do an update ++ } else ++ timf = state->timf; ++ ++ timf *= (bw / 1000); ++ ++ if (update_offset) { ++ s16 tim_offs = dib3000mc_read_word(state, 416); ++ ++ if (tim_offs & 0x2000) ++ tim_offs -= 0x4000; ++ ++ if (nfft == TRANSMISSION_MODE_2K) ++ tim_offs *= 4; ++ ++ timf += tim_offs; ++ state->timf = timf / (bw / 1000); ++ } ++ ++ dprintk("timf: %d\n", timf); ++ ++ dib3000mc_write_word(state, 23, (u16) (timf >> 16)); ++ dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff); ++ ++ return 0; ++} ++ ++static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state) ++{ ++ u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb; ++ if (state->cfg->pwm3_inversion) { ++ reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0); ++ reg_52 |= (1 << 2); ++ } else { ++ reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0); ++ reg_52 |= (1 << 8); ++ } ++ dib3000mc_write_word(state, 51, reg_51); ++ dib3000mc_write_word(state, 52, reg_52); ++ ++ if (state->cfg->use_pwm3) ++ dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0)); ++ else ++ dib3000mc_write_word(state, 245, 0); ++ ++ dib3000mc_write_word(state, 1040, 0x3); ++ return 0; ++} ++ ++static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode) ++{ ++ int ret = 0; ++ u16 fifo_threshold = 1792; ++ u16 outreg = 0; ++ u16 outmode = 0; ++ u16 elecout = 1; ++ u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */ ++ ++ dprintk("-I- Setting output mode for demod %p to %d\n", ++ &state->demod, mode); ++ ++ switch (mode) { ++ case OUTMODE_HIGH_Z: // disable ++ elecout = 0; ++ break; ++ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock ++ outmode = 0; ++ break; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock ++ outmode = 1; ++ break; ++ case OUTMODE_MPEG2_SERIAL: // STBs with serial input ++ outmode = 2; ++ break; ++ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding ++ elecout = 3; ++ /*ADDR @ 206 : ++ P_smo_error_discard [1;6:6] = 0 ++ P_smo_rs_discard [1;5:5] = 0 ++ P_smo_pid_parse [1;4:4] = 0 ++ P_smo_fifo_flush [1;3:3] = 0 ++ P_smo_mode [2;2:1] = 11 ++ P_smo_ovf_prot [1;0:0] = 0 ++ */ ++ smo_reg |= 3 << 1; ++ fifo_threshold = 512; ++ outmode = 5; ++ break; ++ case OUTMODE_DIVERSITY: ++ outmode = 4; ++ elecout = 1; ++ break; ++ default: ++ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); ++ outmode = 0; ++ break; ++ } ++ ++ if ((state->cfg->output_mpeg2_in_188_bytes)) ++ smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1 ++ ++ outreg = dib3000mc_read_word(state, 244) & 0x07FF; ++ outreg |= (outmode << 11); ++ ret |= dib3000mc_write_word(state, 244, outreg); ++ ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/ ++ ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */ ++ ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */ ++ return ret; ++} ++ ++static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw) ++{ ++ u16 bw_cfg[6] = { 0 }; ++ u16 imp_bw_cfg[3] = { 0 }; ++ u16 reg; ++ ++/* settings here are for 27.7MHz */ ++ switch (bw) { ++ case 8000: ++ bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20; ++ imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7; ++ break; ++ ++ case 7000: ++ bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7; ++ imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0; ++ break; ++ ++ case 6000: ++ bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5; ++ imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089; ++ break; ++ ++ case 5000: ++ bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500; ++ imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072; ++ break; ++ ++ default: return -EINVAL; ++ } ++ ++ for (reg = 6; reg < 12; reg++) ++ dib3000mc_write_word(state, reg, bw_cfg[reg - 6]); ++ dib3000mc_write_word(state, 12, 0x0000); ++ dib3000mc_write_word(state, 13, 0x03e8); ++ dib3000mc_write_word(state, 14, 0x0000); ++ dib3000mc_write_word(state, 15, 0x03f2); ++ dib3000mc_write_word(state, 16, 0x0001); ++ dib3000mc_write_word(state, 17, 0xb0d0); ++ // P_sec_len ++ dib3000mc_write_word(state, 18, 0x0393); ++ dib3000mc_write_word(state, 19, 0x8700); ++ ++ for (reg = 55; reg < 58; reg++) ++ dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]); ++ ++ // Timing configuration ++ dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0); ++ ++ return 0; ++} ++ ++static u16 impulse_noise_val[29] = ++ ++{ ++ 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3, ++ 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2, ++ 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd ++}; ++ ++static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft) ++{ ++ u16 i; ++ for (i = 58; i < 87; i++) ++ dib3000mc_write_word(state, i, impulse_noise_val[i-58]); ++ ++ if (nfft == TRANSMISSION_MODE_8K) { ++ dib3000mc_write_word(state, 58, 0x3b); ++ dib3000mc_write_word(state, 84, 0x00); ++ dib3000mc_write_word(state, 85, 0x8200); ++ } ++ ++ dib3000mc_write_word(state, 34, 0x1294); ++ dib3000mc_write_word(state, 35, 0x1ff8); ++ if (mode == 1) ++ dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10)); ++} ++ ++static int dib3000mc_init(struct dvb_frontend *demod) ++{ ++ struct dib3000mc_state *state = demod->demodulator_priv; ++ struct dibx000_agc_config *agc = state->cfg->agc; ++ ++ // Restart Configuration ++ dib3000mc_write_word(state, 1027, 0x8000); ++ dib3000mc_write_word(state, 1027, 0x0000); ++ ++ // power up the demod + mobility configuration ++ dib3000mc_write_word(state, 140, 0x0000); ++ dib3000mc_write_word(state, 1031, 0); ++ ++ if (state->cfg->mobile_mode) { ++ dib3000mc_write_word(state, 139, 0x0000); ++ dib3000mc_write_word(state, 141, 0x0000); ++ dib3000mc_write_word(state, 175, 0x0002); ++ dib3000mc_write_word(state, 1032, 0x0000); ++ } else { ++ dib3000mc_write_word(state, 139, 0x0001); ++ dib3000mc_write_word(state, 141, 0x0000); ++ dib3000mc_write_word(state, 175, 0x0000); ++ dib3000mc_write_word(state, 1032, 0x012C); ++ } ++ dib3000mc_write_word(state, 1033, 0x0000); ++ ++ // P_clk_cfg ++ dib3000mc_write_word(state, 1037, 0x3130); ++ ++ // other configurations ++ ++ // P_ctrl_sfreq ++ dib3000mc_write_word(state, 33, (5 << 0)); ++ dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0)); ++ ++ // Phase noise control ++ // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange ++ dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0)); ++ ++ if (state->cfg->phase_noise_mode == 0) ++ dib3000mc_write_word(state, 111, 0x00); ++ else ++ dib3000mc_write_word(state, 111, 0x02); ++ ++ // P_agc_global ++ dib3000mc_write_word(state, 50, 0x8000); ++ ++ // agc setup misc ++ dib3000mc_setup_pwm_state(state); ++ ++ // P_agc_counter_lock ++ dib3000mc_write_word(state, 53, 0x87); ++ // P_agc_counter_unlock ++ dib3000mc_write_word(state, 54, 0x87); ++ ++ /* agc */ ++ dib3000mc_write_word(state, 36, state->cfg->max_time); ++ dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0)); ++ dib3000mc_write_word(state, 38, state->cfg->pwm3_value); ++ dib3000mc_write_word(state, 39, state->cfg->ln_adc_level); ++ ++ // set_agc_loop_Bw ++ dib3000mc_write_word(state, 40, 0x0179); ++ dib3000mc_write_word(state, 41, 0x03f0); ++ ++ dib3000mc_write_word(state, 42, agc->agc1_max); ++ dib3000mc_write_word(state, 43, agc->agc1_min); ++ dib3000mc_write_word(state, 44, agc->agc2_max); ++ dib3000mc_write_word(state, 45, agc->agc2_min); ++ dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2); ++ dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2); ++ dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2); ++ dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2); ++ ++// Begin: TimeOut registers ++ // P_pha3_thres ++ dib3000mc_write_word(state, 110, 3277); ++ // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80 ++ dib3000mc_write_word(state, 26, 0x6680); ++ // lock_mask0 ++ dib3000mc_write_word(state, 1, 4); ++ // lock_mask1 ++ dib3000mc_write_word(state, 2, 4); ++ // lock_mask2 ++ dib3000mc_write_word(state, 3, 0x1000); ++ // P_search_maxtrial=1 ++ dib3000mc_write_word(state, 5, 1); ++ ++ dib3000mc_set_bandwidth(state, 8000); ++ ++ // div_lock_mask ++ dib3000mc_write_word(state, 4, 0x814); ++ ++ dib3000mc_write_word(state, 21, (1 << 9) | 0x164); ++ dib3000mc_write_word(state, 22, 0x463d); ++ ++ // Spurious rm cfg ++ // P_cspu_regul, P_cspu_win_cut ++ dib3000mc_write_word(state, 120, 0x200f); ++ // P_adp_selec_monit ++ dib3000mc_write_word(state, 134, 0); ++ ++ // Fec cfg ++ dib3000mc_write_word(state, 195, 0x10); ++ ++ // diversity register: P_dvsy_sync_wait.. ++ dib3000mc_write_word(state, 180, 0x2FF0); ++ ++ // Impulse noise configuration ++ dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K); ++ ++ // output mode set-up ++ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); ++ ++ /* close the i2c-gate */ ++ dib3000mc_write_word(state, 769, (1 << 7) ); ++ ++ return 0; ++} ++ ++static int dib3000mc_sleep(struct dvb_frontend *demod) ++{ ++ struct dib3000mc_state *state = demod->demodulator_priv; ++ ++ dib3000mc_write_word(state, 1031, 0xFFFF); ++ dib3000mc_write_word(state, 1032, 0xFFFF); ++ dib3000mc_write_word(state, 1033, 0xFFF0); ++ ++ return 0; ++} ++ ++static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam) ++{ ++ u16 cfg[4] = { 0 },reg; ++ switch (qam) { ++ case QPSK: ++ cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0; ++ break; ++ case QAM_16: ++ cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0; ++ break; ++ case QAM_64: ++ cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8; ++ break; ++ } ++ for (reg = 129; reg < 133; reg++) ++ dib3000mc_write_word(state, reg, cfg[reg - 129]); ++} ++ ++static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, ++ struct dtv_frontend_properties *ch, u16 seq) ++{ ++ u16 value; ++ u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); ++ ++ dib3000mc_set_bandwidth(state, bw); ++ dib3000mc_set_timing(state, ch->transmission_mode, bw, 0); ++ ++// if (boost) ++// dib3000mc_write_word(state, 100, (11 << 6) + 6); ++// else ++ dib3000mc_write_word(state, 100, (16 << 6) + 9); ++ ++ dib3000mc_write_word(state, 1027, 0x0800); ++ dib3000mc_write_word(state, 1027, 0x0000); ++ ++ //Default cfg isi offset adp ++ dib3000mc_write_word(state, 26, 0x6680); ++ dib3000mc_write_word(state, 29, 0x1273); ++ dib3000mc_write_word(state, 33, 5); ++ dib3000mc_set_adp_cfg(state, QAM_16); ++ dib3000mc_write_word(state, 133, 15564); ++ ++ dib3000mc_write_word(state, 12 , 0x0); ++ dib3000mc_write_word(state, 13 , 0x3e8); ++ dib3000mc_write_word(state, 14 , 0x0); ++ dib3000mc_write_word(state, 15 , 0x3f2); ++ ++ dib3000mc_write_word(state, 93,0); ++ dib3000mc_write_word(state, 94,0); ++ dib3000mc_write_word(state, 95,0); ++ dib3000mc_write_word(state, 96,0); ++ dib3000mc_write_word(state, 97,0); ++ dib3000mc_write_word(state, 98,0); ++ ++ dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode); ++ ++ value = 0; ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: value |= (0 << 7); break; ++ default: ++ case TRANSMISSION_MODE_8K: value |= (1 << 7); break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_32: value |= (0 << 5); break; ++ case GUARD_INTERVAL_1_16: value |= (1 << 5); break; ++ case GUARD_INTERVAL_1_4: value |= (3 << 5); break; ++ default: ++ case GUARD_INTERVAL_1_8: value |= (2 << 5); break; ++ } ++ switch (ch->modulation) { ++ case QPSK: value |= (0 << 3); break; ++ case QAM_16: value |= (1 << 3); break; ++ default: ++ case QAM_64: value |= (2 << 3); break; ++ } ++ switch (HIERARCHY_1) { ++ case HIERARCHY_2: value |= 2; break; ++ case HIERARCHY_4: value |= 4; break; ++ default: ++ case HIERARCHY_1: value |= 1; break; ++ } ++ dib3000mc_write_word(state, 0, value); ++ dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4)); ++ ++ value = 0; ++ if (ch->hierarchy == 1) ++ value |= (1 << 4); ++ if (1 == 1) ++ value |= 1; ++ switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { ++ case FEC_2_3: value |= (2 << 1); break; ++ case FEC_3_4: value |= (3 << 1); break; ++ case FEC_5_6: value |= (5 << 1); break; ++ case FEC_7_8: value |= (7 << 1); break; ++ default: ++ case FEC_1_2: value |= (1 << 1); break; ++ } ++ dib3000mc_write_word(state, 181, value); ++ ++ // diversity synchro delay add 50% SFN margin ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_8K: value = 256; break; ++ case TRANSMISSION_MODE_2K: ++ default: value = 64; break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_16: value *= 2; break; ++ case GUARD_INTERVAL_1_8: value *= 4; break; ++ case GUARD_INTERVAL_1_4: value *= 8; break; ++ default: ++ case GUARD_INTERVAL_1_32: value *= 1; break; ++ } ++ value <<= 4; ++ value |= dib3000mc_read_word(state, 180) & 0x000f; ++ dib3000mc_write_word(state, 180, value); ++ ++ // restart demod ++ value = dib3000mc_read_word(state, 0); ++ dib3000mc_write_word(state, 0, value | (1 << 9)); ++ dib3000mc_write_word(state, 0, value); ++ ++ msleep(30); ++ ++ dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode); ++} ++ ++static int dib3000mc_autosearch_start(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *chan = &demod->dtv_property_cache; ++ struct dib3000mc_state *state = demod->demodulator_priv; ++ u16 reg; ++// u32 val; ++ struct dtv_frontend_properties schan; ++ ++ schan = *chan; ++ ++ /* TODO what is that ? */ ++ ++ /* a channel for autosearch */ ++ schan.transmission_mode = TRANSMISSION_MODE_8K; ++ schan.guard_interval = GUARD_INTERVAL_1_32; ++ schan.modulation = QAM_64; ++ schan.code_rate_HP = FEC_2_3; ++ schan.code_rate_LP = FEC_2_3; ++ schan.hierarchy = 0; ++ ++ dib3000mc_set_channel_cfg(state, &schan, 11); ++ ++ reg = dib3000mc_read_word(state, 0); ++ dib3000mc_write_word(state, 0, reg | (1 << 8)); ++ dib3000mc_read_word(state, 511); ++ dib3000mc_write_word(state, 0, reg); ++ ++ return 0; ++} ++ ++static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod) ++{ ++ struct dib3000mc_state *state = demod->demodulator_priv; ++ u16 irq_pending = dib3000mc_read_word(state, 511); ++ ++ if (irq_pending & 0x1) // failed ++ return 1; ++ ++ if (irq_pending & 0x2) // succeeded ++ return 2; ++ ++ return 0; // still pending ++} ++ ++static int dib3000mc_tune(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib3000mc_state *state = demod->demodulator_priv; ++ ++ // ** configure demod ** ++ dib3000mc_set_channel_cfg(state, ch, 0); ++ ++ // activates isi ++ if (state->sfn_workaround_active) { ++ dprintk("SFN workaround is active\n"); ++ dib3000mc_write_word(state, 29, 0x1273); ++ dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift ++ } else { ++ dib3000mc_write_word(state, 29, 0x1073); ++ dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift ++ } ++ ++ dib3000mc_set_adp_cfg(state, (u8)ch->modulation); ++ if (ch->transmission_mode == TRANSMISSION_MODE_8K) { ++ dib3000mc_write_word(state, 26, 38528); ++ dib3000mc_write_word(state, 33, 8); ++ } else { ++ dib3000mc_write_word(state, 26, 30336); ++ dib3000mc_write_word(state, 33, 6); ++ } ++ ++ if (dib3000mc_read_word(state, 509) & 0x80) ++ dib3000mc_set_timing(state, ch->transmission_mode, ++ BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1); ++ ++ return 0; ++} ++ ++struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating) ++{ ++ struct dib3000mc_state *st = demod->demodulator_priv; ++ return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating); ++} ++ ++EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master); ++ ++static int dib3000mc_get_frontend(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ u16 tps = dib3000mc_read_word(state,458); ++ ++ fep->inversion = INVERSION_AUTO; ++ ++ fep->bandwidth_hz = state->current_bandwidth; ++ ++ switch ((tps >> 8) & 0x1) { ++ case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; ++ case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; ++ } ++ ++ switch (tps & 0x3) { ++ case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; ++ case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; ++ case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; ++ case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; ++ } ++ ++ switch ((tps >> 13) & 0x3) { ++ case 0: fep->modulation = QPSK; break; ++ case 1: fep->modulation = QAM_16; break; ++ case 2: ++ default: fep->modulation = QAM_64; break; ++ } ++ ++ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ ++ /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */ ++ ++ fep->hierarchy = HIERARCHY_NONE; ++ switch ((tps >> 5) & 0x7) { ++ case 1: fep->code_rate_HP = FEC_1_2; break; ++ case 2: fep->code_rate_HP = FEC_2_3; break; ++ case 3: fep->code_rate_HP = FEC_3_4; break; ++ case 5: fep->code_rate_HP = FEC_5_6; break; ++ case 7: ++ default: fep->code_rate_HP = FEC_7_8; break; ++ ++ } ++ ++ switch ((tps >> 2) & 0x7) { ++ case 1: fep->code_rate_LP = FEC_1_2; break; ++ case 2: fep->code_rate_LP = FEC_2_3; break; ++ case 3: fep->code_rate_LP = FEC_3_4; break; ++ case 5: fep->code_rate_LP = FEC_5_6; break; ++ case 7: ++ default: fep->code_rate_LP = FEC_7_8; break; ++ } ++ ++ return 0; ++} ++ ++static int dib3000mc_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); ++ ++ state->current_bandwidth = fep->bandwidth_hz; ++ dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); ++ ++ /* maybe the parameter has been changed */ ++ state->sfn_workaround_active = buggy_sfn_workaround; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ msleep(100); ++ } ++ ++ if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || ++ fep->guard_interval == GUARD_INTERVAL_AUTO || ++ fep->modulation == QAM_AUTO || ++ fep->code_rate_HP == FEC_AUTO) { ++ int i = 1000, found; ++ ++ dib3000mc_autosearch_start(fe); ++ do { ++ msleep(1); ++ found = dib3000mc_autosearch_is_irq(fe); ++ } while (found == 0 && i--); ++ ++ dprintk("autosearch returns: %d\n",found); ++ if (found == 0 || found == 1) ++ return 0; // no channel found ++ ++ dib3000mc_get_frontend(fe); ++ } ++ ++ ret = dib3000mc_tune(fe); ++ ++ /* make this a config parameter */ ++ dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO); ++ return ret; ++} ++ ++static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ u16 lock = dib3000mc_read_word(state, 509); ++ ++ *stat = 0; ++ ++ if (lock & 0x8000) ++ *stat |= FE_HAS_SIGNAL; ++ if (lock & 0x3000) ++ *stat |= FE_HAS_CARRIER; ++ if (lock & 0x0100) ++ *stat |= FE_HAS_VITERBI; ++ if (lock & 0x0010) ++ *stat |= FE_HAS_SYNC; ++ if (lock & 0x0008) ++ *stat |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501); ++ return 0; ++} ++ ++static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ *unc = dib3000mc_read_word(state, 508); ++ return 0; ++} ++ ++static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ u16 val = dib3000mc_read_word(state, 392); ++ *strength = 65535 - val; ++ return 0; ++} ++ ++static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) ++{ ++ *snr = 0x0000; ++ return 0; ++} ++ ++static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void dib3000mc_release(struct dvb_frontend *fe) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ dibx000_exit_i2c_master(&state->i2c_master); ++ kfree(state); ++} ++ ++int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0); ++ return 0; ++} ++EXPORT_SYMBOL(dib3000mc_pid_control); ++ ++int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4); ++ tmp |= (onoff << 4); ++ return dib3000mc_write_word(state, 206, tmp); ++} ++EXPORT_SYMBOL(dib3000mc_pid_parse); ++ ++void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg) ++{ ++ struct dib3000mc_state *state = fe->demodulator_priv; ++ state->cfg = cfg; ++} ++EXPORT_SYMBOL(dib3000mc_set_config); ++ ++int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]) ++{ ++ struct dib3000mc_state *dmcst; ++ int k; ++ u8 new_addr; ++ ++ static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26}; ++ ++ dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); ++ if (dmcst == NULL) ++ return -ENOMEM; ++ ++ dmcst->i2c_adap = i2c; ++ ++ for (k = no_of_demods-1; k >= 0; k--) { ++ dmcst->cfg = &cfg[k]; ++ ++ /* designated i2c address */ ++ new_addr = DIB3000MC_I2C_ADDRESS[k]; ++ dmcst->i2c_addr = new_addr; ++ if (dib3000mc_identify(dmcst) != 0) { ++ dmcst->i2c_addr = default_addr; ++ if (dib3000mc_identify(dmcst) != 0) { ++ dprintk("-E- DiB3000P/MC #%d: not identified\n", k); ++ kfree(dmcst); ++ return -ENODEV; ++ } ++ } ++ ++ dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK); ++ ++ // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0) ++ dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1); ++ dmcst->i2c_addr = new_addr; ++ } ++ ++ for (k = 0; k < no_of_demods; k++) { ++ dmcst->cfg = &cfg[k]; ++ dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k]; ++ ++ dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3); ++ ++ /* turn off data output */ ++ dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z); ++ } ++ ++ kfree(dmcst); ++ return 0; ++} ++EXPORT_SYMBOL(dib3000mc_i2c_enumeration); ++ ++static struct dvb_frontend_ops dib3000mc_ops; ++ ++struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg) ++{ ++ struct dvb_frontend *demod; ++ struct dib3000mc_state *st; ++ st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ ++ st->cfg = cfg; ++ st->i2c_adap = i2c_adap; ++ st->i2c_addr = i2c_addr; ++ ++ demod = &st->demod; ++ demod->demodulator_priv = st; ++ memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); ++ ++ if (dib3000mc_identify(st) != 0) ++ goto error; ++ ++ dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr); ++ ++ dib3000mc_write_word(st, 1037, 0x3130); ++ ++ return demod; ++ ++error: ++ kfree(st); ++ return NULL; ++} ++EXPORT_SYMBOL(dib3000mc_attach); ++ ++static struct dvb_frontend_ops dib3000mc_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DiBcom 3000MC/P", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib3000mc_release, ++ ++ .init = dib3000mc_init, ++ .sleep = dib3000mc_sleep, ++ ++ .set_frontend = dib3000mc_set_frontend, ++ .get_tune_settings = dib3000mc_fe_get_tune_settings, ++ .get_frontend = dib3000mc_get_frontend, ++ ++ .read_status = dib3000mc_read_status, ++ .read_ber = dib3000mc_read_ber, ++ .read_signal_strength = dib3000mc_read_signal_strength, ++ .read_snr = dib3000mc_read_snr, ++ .read_ucblocks = dib3000mc_read_unc_blocks, ++}; ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib3000mc.h b/drivers/media/dvb-frontends/dib3000mc.h +new file mode 100644 +index 0000000..d75ffad +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib3000mc.h +@@ -0,0 +1,85 @@ ++/* ++ * Driver for DiBcom DiB3000MC/P-demodulator. ++ * ++ * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/) ++ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de) ++ * ++ * This code is partially based on the previous dib3000mc.c . ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#ifndef DIB3000MC_H ++#define DIB3000MC_H ++ ++#include "dibx000_common.h" ++ ++struct dib3000mc_config { ++ struct dibx000_agc_config *agc; ++ ++ u8 phase_noise_mode; ++ u8 impulse_noise_mode; ++ ++ u8 pwm3_inversion; ++ u8 use_pwm3; ++ u16 pwm3_value; ++ ++ u16 max_time; ++ u16 ln_adc_level; ++ ++ u8 agc_command1 :1; ++ u8 agc_command2 :1; ++ ++ u8 mobile_mode; ++ ++ u8 output_mpeg2_in_188_bytes; ++}; ++ ++#define DEFAULT_DIB3000MC_I2C_ADDRESS 16 ++#define DEFAULT_DIB3000P_I2C_ADDRESS 24 ++ ++#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && \ ++ defined(MODULE)) ++extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, ++ struct dib3000mc_config *cfg); ++extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, ++ int no_of_demods, u8 default_addr, ++ struct dib3000mc_config cfg[]); ++extern ++struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, ++ int gating); ++#else ++static inline ++struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, ++ struct dib3000mc_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline ++int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, ++ int no_of_demods, u8 default_addr, ++ struct dib3000mc_config cfg[]) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline ++struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, ++ int gating) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_DIB3000MC ++ ++extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff); ++extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff); ++ ++extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *); ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c +new file mode 100644 +index 0000000..148bf79 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib7000m.c +@@ -0,0 +1,1473 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB7000M and ++ * first generation DiB7000P-demodulator-family. ++ * ++ * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "dib7000m.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M: "); printk(args); printk("\n"); } } while (0) ++ ++struct dib7000m_state { ++ struct dvb_frontend demod; ++ struct dib7000m_config cfg; ++ ++ u8 i2c_addr; ++ struct i2c_adapter *i2c_adap; ++ ++ struct dibx000_i2c_master i2c_master; ++ ++/* offset is 1 in case of the 7000MC */ ++ u8 reg_offs; ++ ++ u16 wbd_ref; ++ ++ u8 current_band; ++ u32 current_bandwidth; ++ struct dibx000_agc_config *current_agc; ++ u32 timf; ++ u32 timf_default; ++ u32 internal_clk; ++ ++ u8 div_force_off : 1; ++ u8 div_state : 1; ++ u16 div_sync_wait; ++ ++ u16 revision; ++ ++ u8 agc_state; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[4]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++}; ++ ++enum dib7000m_power_mode { ++ DIB7000M_POWER_ALL = 0, ++ ++ DIB7000M_POWER_NO, ++ DIB7000M_POWER_INTERF_ANALOG_AGC, ++ DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, ++ DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD, ++ DIB7000M_POWER_INTERFACE_ONLY, ++}; ++ ++static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = (reg >> 8) | 0x80; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 2; ++ state->msg[1].addr = state->i2c_addr >> 1; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = state->i2c_read_buffer; ++ state->msg[1].len = 2; ++ ++ if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) ++ dprintk("i2c read error on %d",reg); ++ ++ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; ++ mutex_unlock(&state->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ state->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ state->i2c_write_buffer[2] = (val >> 8) & 0xff; ++ state->i2c_write_buffer[3] = val & 0xff; ++ ++ memset(&state->msg[0], 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 4; ++ ++ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? ++ -EREMOTEIO : 0); ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf) ++{ ++ u16 l = 0, r, *n; ++ n = buf; ++ l = *n++; ++ while (l) { ++ r = *n++; ++ ++ if (state->reg_offs && (r >= 112 && r <= 331)) // compensate for 7000MC ++ r++; ++ ++ do { ++ dib7000m_write_word(state, r, *n++); ++ r++; ++ } while (--l); ++ l = *n++; ++ } ++} ++ ++static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode) ++{ ++ int ret = 0; ++ u16 outreg, fifo_threshold, smo_mode, ++ sram = 0x0005; /* by default SRAM output is disabled */ ++ ++ outreg = 0; ++ fifo_threshold = 1792; ++ smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1); ++ ++ dprintk( "setting output mode for demod %p to %d", &state->demod, mode); ++ ++ switch (mode) { ++ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock ++ outreg = (1 << 10); /* 0x0400 */ ++ break; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock ++ outreg = (1 << 10) | (1 << 6); /* 0x0440 */ ++ break; ++ case OUTMODE_MPEG2_SERIAL: // STBs with serial input ++ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ ++ break; ++ case OUTMODE_DIVERSITY: ++ if (state->cfg.hostbus_diversity) ++ outreg = (1 << 10) | (4 << 6); /* 0x0500 */ ++ else ++ sram |= 0x0c00; ++ break; ++ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding ++ smo_mode |= (3 << 1); ++ fifo_threshold = 512; ++ outreg = (1 << 10) | (5 << 6); ++ break; ++ case OUTMODE_HIGH_Z: // disable ++ outreg = 0; ++ break; ++ default: ++ dprintk( "Unhandled output_mode passed to be set for demod %p",&state->demod); ++ break; ++ } ++ ++ if (state->cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5) ; ++ ++ ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode); ++ ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */ ++ ret |= dib7000m_write_word(state, 1795, outreg); ++ ret |= dib7000m_write_word(state, 1805, sram); ++ ++ if (state->revision == 0x4003) { ++ u16 clk_cfg1 = dib7000m_read_word(state, 909) & 0xfffd; ++ if (mode == OUTMODE_DIVERSITY) ++ clk_cfg1 |= (1 << 1); // P_O_CLK_en ++ dib7000m_write_word(state, 909, clk_cfg1); ++ } ++ return ret; ++} ++ ++static void dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode) ++{ ++ /* by default everything is going to be powered off */ ++ u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff; ++ u8 offset = 0; ++ ++ /* now, depending on the requested mode, we power on */ ++ switch (mode) { ++ /* power up everything in the demod */ ++ case DIB7000M_POWER_ALL: ++ reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000; ++ break; ++ ++ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ ++ case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ ++ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); ++ break; ++ ++ case DIB7000M_POWER_INTERF_ANALOG_AGC: ++ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); ++ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); ++ reg_906 &= ~((1 << 0)); ++ break; ++ ++ case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: ++ reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000; ++ break; ++ ++ case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD: ++ reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000; ++ break; ++ case DIB7000M_POWER_NO: ++ break; ++ } ++ ++ /* always power down unused parts */ ++ if (!state->cfg.mobile_mode) ++ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); ++ ++ /* P_sdio_select_clk = 0 on MC and after*/ ++ if (state->revision != 0x4000) ++ reg_906 <<= 1; ++ ++ if (state->revision == 0x4003) ++ offset = 1; ++ ++ dib7000m_write_word(state, 903 + offset, reg_903); ++ dib7000m_write_word(state, 904 + offset, reg_904); ++ dib7000m_write_word(state, 905 + offset, reg_905); ++ dib7000m_write_word(state, 906 + offset, reg_906); ++} ++ ++static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no) ++{ ++ int ret = 0; ++ u16 reg_913 = dib7000m_read_word(state, 913), ++ reg_914 = dib7000m_read_word(state, 914); ++ ++ switch (no) { ++ case DIBX000_SLOW_ADC_ON: ++ reg_914 |= (1 << 1) | (1 << 0); ++ ret |= dib7000m_write_word(state, 914, reg_914); ++ reg_914 &= ~(1 << 1); ++ break; ++ ++ case DIBX000_SLOW_ADC_OFF: ++ reg_914 |= (1 << 1) | (1 << 0); ++ break; ++ ++ case DIBX000_ADC_ON: ++ if (state->revision == 0x4000) { // workaround for PA/MA ++ // power-up ADC ++ dib7000m_write_word(state, 913, 0); ++ dib7000m_write_word(state, 914, reg_914 & 0x3); ++ // power-down bandgag ++ dib7000m_write_word(state, 913, (1 << 15)); ++ dib7000m_write_word(state, 914, reg_914 & 0x3); ++ } ++ ++ reg_913 &= 0x0fff; ++ reg_914 &= 0x0003; ++ break; ++ ++ case DIBX000_ADC_OFF: // leave the VBG voltage on ++ reg_913 |= (1 << 14) | (1 << 13) | (1 << 12); ++ reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); ++ break; ++ ++ case DIBX000_VBG_ENABLE: ++ reg_913 &= ~(1 << 15); ++ break; ++ ++ case DIBX000_VBG_DISABLE: ++ reg_913 |= (1 << 15); ++ break; ++ ++ default: ++ break; ++ } ++ ++// dprintk( "913: %x, 914: %x", reg_913, reg_914); ++ ret |= dib7000m_write_word(state, 913, reg_913); ++ ret |= dib7000m_write_word(state, 914, reg_914); ++ ++ return ret; ++} ++ ++static int dib7000m_set_bandwidth(struct dib7000m_state *state, u32 bw) ++{ ++ u32 timf; ++ ++ if (!bw) ++ bw = 8000; ++ ++ // store the current bandwidth for later use ++ state->current_bandwidth = bw; ++ ++ if (state->timf == 0) { ++ dprintk( "using default timf"); ++ timf = state->timf_default; ++ } else { ++ dprintk( "using updated timf"); ++ timf = state->timf; ++ } ++ ++ timf = timf * (bw / 50) / 160; ++ ++ dib7000m_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); ++ dib7000m_write_word(state, 24, (u16) ((timf ) & 0xffff)); ++ ++ return 0; ++} ++ ++static int dib7000m_set_diversity_in(struct dvb_frontend *demod, int onoff) ++{ ++ struct dib7000m_state *state = demod->demodulator_priv; ++ ++ if (state->div_force_off) { ++ dprintk( "diversity combination deactivated - forced by COFDM parameters"); ++ onoff = 0; ++ } ++ state->div_state = (u8)onoff; ++ ++ if (onoff) { ++ dib7000m_write_word(state, 263 + state->reg_offs, 6); ++ dib7000m_write_word(state, 264 + state->reg_offs, 6); ++ dib7000m_write_word(state, 266 + state->reg_offs, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); ++ } else { ++ dib7000m_write_word(state, 263 + state->reg_offs, 1); ++ dib7000m_write_word(state, 264 + state->reg_offs, 0); ++ dib7000m_write_word(state, 266 + state->reg_offs, 0); ++ } ++ ++ return 0; ++} ++ ++static int dib7000m_sad_calib(struct dib7000m_state *state) ++{ ++ ++/* internal */ ++// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth ++ dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); ++ dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 ++ ++ /* do the calibration */ ++ dib7000m_write_word(state, 929, (1 << 0)); ++ dib7000m_write_word(state, 929, (0 << 0)); ++ ++ msleep(1); ++ ++ return 0; ++} ++ ++static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw) ++{ ++ dib7000m_write_word(state, 18, (u16) (((bw->internal*1000) >> 16) & 0xffff)); ++ dib7000m_write_word(state, 19, (u16) ( (bw->internal*1000) & 0xffff)); ++ dib7000m_write_word(state, 21, (u16) ( (bw->ifreq >> 16) & 0xffff)); ++ dib7000m_write_word(state, 22, (u16) ( bw->ifreq & 0xffff)); ++ ++ dib7000m_write_word(state, 928, bw->sad_cfg); ++} ++ ++static void dib7000m_reset_pll(struct dib7000m_state *state) ++{ ++ const struct dibx000_bandwidth_config *bw = state->cfg.bw; ++ u16 reg_907,reg_910; ++ ++ /* default */ ++ reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) | ++ (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | ++ (bw->enable_refdiv << 1) | (0 << 0); ++ reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset; ++ ++ // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value) ++ // this is only working only for 30 MHz crystals ++ if (!state->cfg.quartz_direct) { ++ reg_910 |= (1 << 5); // forcing the predivider to 1 ++ ++ // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2) ++ if(state->cfg.input_clk_is_div_2) ++ reg_907 |= (16 << 9); ++ else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary ++ reg_907 |= (8 << 9); ++ } else { ++ reg_907 |= (bw->pll_ratio & 0x3f) << 9; ++ reg_910 |= (bw->pll_prediv << 5); ++ } ++ ++ dib7000m_write_word(state, 910, reg_910); // pll cfg ++ dib7000m_write_word(state, 907, reg_907); // clk cfg0 ++ dib7000m_write_word(state, 908, 0x0006); // clk_cfg1 ++ ++ dib7000m_reset_pll_common(state, bw); ++} ++ ++static void dib7000mc_reset_pll(struct dib7000m_state *state) ++{ ++ const struct dibx000_bandwidth_config *bw = state->cfg.bw; ++ u16 clk_cfg1; ++ ++ // clk_cfg0 ++ dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0)); ++ ++ // clk_cfg1 ++ //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) | ++ clk_cfg1 = (0 << 14) | (3 << 12) |(0 << 11) | ++ (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) | ++ (1 << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0); ++ dib7000m_write_word(state, 908, clk_cfg1); ++ clk_cfg1 = (clk_cfg1 & 0xfff7) | (bw->pll_bypass << 3); ++ dib7000m_write_word(state, 908, clk_cfg1); ++ ++ // smpl_cfg ++ dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7)); ++ ++ dib7000m_reset_pll_common(state, bw); ++} ++ ++static int dib7000m_reset_gpio(struct dib7000m_state *st) ++{ ++ /* reset the GPIOs */ ++ dib7000m_write_word(st, 773, st->cfg.gpio_dir); ++ dib7000m_write_word(st, 774, st->cfg.gpio_val); ++ ++ /* TODO 782 is P_gpio_od */ ++ ++ dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos); ++ ++ dib7000m_write_word(st, 780, st->cfg.pwm_freq_div); ++ return 0; ++} ++ ++static u16 dib7000m_defaults_common[] = ++ ++{ ++ // auto search configuration ++ 3, 2, ++ 0x0004, ++ 0x1000, ++ 0x0814, ++ ++ 12, 6, ++ 0x001b, ++ 0x7740, ++ 0x005b, ++ 0x8d80, ++ 0x01c9, ++ 0xc380, ++ 0x0000, ++ 0x0080, ++ 0x0000, ++ 0x0090, ++ 0x0001, ++ 0xd4c0, ++ ++ 1, 26, ++ 0x6680, // P_corm_thres Lock algorithms configuration ++ ++ 1, 170, ++ 0x0410, // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on ++ ++ 8, 173, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ ++ 1, 182, ++ 8192, // P_fft_nb_to_cut ++ ++ 2, 195, ++ 0x0ccd, // P_pha3_thres ++ 0, // P_cti_use_cpe, P_cti_use_prog ++ ++ 1, 205, ++ 0x200f, // P_cspu_regul, P_cspu_win_cut ++ ++ 5, 214, ++ 0x023d, // P_adp_regul_cnt ++ 0x00a4, // P_adp_noise_cnt ++ 0x00a4, // P_adp_regul_ext ++ 0x7ff0, // P_adp_noise_ext ++ 0x3ccc, // P_adp_fil ++ ++ 1, 226, ++ 0, // P_2d_byp_ti_num ++ ++ 1, 255, ++ 0x800, // P_equal_thres_wgn ++ ++ 1, 263, ++ 0x0001, ++ ++ 1, 281, ++ 0x0010, // P_fec_* ++ ++ 1, 294, ++ 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard ++ ++ 0 ++}; ++ ++static u16 dib7000m_defaults[] = ++ ++{ ++ /* set ADC level to -16 */ ++ 11, 76, ++ (1 << 13) - 825 - 117, ++ (1 << 13) - 837 - 117, ++ (1 << 13) - 811 - 117, ++ (1 << 13) - 766 - 117, ++ (1 << 13) - 737 - 117, ++ (1 << 13) - 693 - 117, ++ (1 << 13) - 648 - 117, ++ (1 << 13) - 619 - 117, ++ (1 << 13) - 575 - 117, ++ (1 << 13) - 531 - 117, ++ (1 << 13) - 501 - 117, ++ ++ // Tuner IO bank: max drive (14mA) ++ 1, 912, ++ 0x2c8a, ++ ++ 1, 1817, ++ 1, ++ ++ 0, ++}; ++ ++static int dib7000m_demod_reset(struct dib7000m_state *state) ++{ ++ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); ++ ++ /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ ++ dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE); ++ ++ /* restart all parts */ ++ dib7000m_write_word(state, 898, 0xffff); ++ dib7000m_write_word(state, 899, 0xffff); ++ dib7000m_write_word(state, 900, 0xff0f); ++ dib7000m_write_word(state, 901, 0xfffc); ++ ++ dib7000m_write_word(state, 898, 0); ++ dib7000m_write_word(state, 899, 0); ++ dib7000m_write_word(state, 900, 0); ++ dib7000m_write_word(state, 901, 0); ++ ++ if (state->revision == 0x4000) ++ dib7000m_reset_pll(state); ++ else ++ dib7000mc_reset_pll(state); ++ ++ if (dib7000m_reset_gpio(state) != 0) ++ dprintk( "GPIO reset was not successful."); ++ ++ if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0) ++ dprintk( "OUTPUT_MODE could not be reset."); ++ ++ /* unforce divstr regardless whether i2c enumeration was done or not */ ++ dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) ); ++ ++ dib7000m_set_bandwidth(state, 8000); ++ ++ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON); ++ dib7000m_sad_calib(state); ++ dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF); ++ ++ if (state->cfg.dvbt_mode) ++ dib7000m_write_word(state, 1796, 0x0); // select DVB-T output ++ ++ if (state->cfg.mobile_mode) ++ dib7000m_write_word(state, 261 + state->reg_offs, 2); ++ else ++ dib7000m_write_word(state, 224 + state->reg_offs, 1); ++ ++ // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... ++ if(state->cfg.tuner_is_baseband) ++ dib7000m_write_word(state, 36, 0x0755); ++ else ++ dib7000m_write_word(state, 36, 0x1f55); ++ ++ // P_divclksel=3 P_divbitsel=1 ++ if (state->revision == 0x4000) ++ dib7000m_write_word(state, 909, (3 << 10) | (1 << 6)); ++ else ++ dib7000m_write_word(state, 909, (3 << 4) | 1); ++ ++ dib7000m_write_tab(state, dib7000m_defaults_common); ++ dib7000m_write_tab(state, dib7000m_defaults); ++ ++ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY); ++ ++ state->internal_clk = state->cfg.bw->internal; ++ ++ return 0; ++} ++ ++static void dib7000m_restart_agc(struct dib7000m_state *state) ++{ ++ // P_restart_iqc & P_restart_agc ++ dib7000m_write_word(state, 898, 0x0c00); ++ dib7000m_write_word(state, 898, 0x0000); ++} ++ ++static int dib7000m_agc_soft_split(struct dib7000m_state *state) ++{ ++ u16 agc,split_offset; ++ ++ if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) ++ return 0; ++ ++ // n_agc_global ++ agc = dib7000m_read_word(state, 390); ++ ++ if (agc > state->current_agc->split.min_thres) ++ split_offset = state->current_agc->split.min; ++ else if (agc < state->current_agc->split.max_thres) ++ split_offset = state->current_agc->split.max; ++ else ++ split_offset = state->current_agc->split.max * ++ (agc - state->current_agc->split.min_thres) / ++ (state->current_agc->split.max_thres - state->current_agc->split.min_thres); ++ ++ dprintk( "AGC split_offset: %d",split_offset); ++ ++ // P_agc_force_split and P_agc_split_offset ++ return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset); ++} ++ ++static int dib7000m_update_lna(struct dib7000m_state *state) ++{ ++ u16 dyn_gain; ++ ++ if (state->cfg.update_lna) { ++ // read dyn_gain here (because it is demod-dependent and not fe) ++ dyn_gain = dib7000m_read_word(state, 390); ++ ++ if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed ++ dib7000m_restart_agc(state); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band) ++{ ++ struct dibx000_agc_config *agc = NULL; ++ int i; ++ if (state->current_band == band && state->current_agc != NULL) ++ return 0; ++ state->current_band = band; ++ ++ for (i = 0; i < state->cfg.agc_config_count; i++) ++ if (state->cfg.agc[i].band_caps & band) { ++ agc = &state->cfg.agc[i]; ++ break; ++ } ++ ++ if (agc == NULL) { ++ dprintk( "no valid AGC configuration found for band 0x%02x",band); ++ return -EINVAL; ++ } ++ ++ state->current_agc = agc; ++ ++ /* AGC */ ++ dib7000m_write_word(state, 72 , agc->setup); ++ dib7000m_write_word(state, 73 , agc->inv_gain); ++ dib7000m_write_word(state, 74 , agc->time_stabiliz); ++ dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock); ++ ++ // Demod AGC loop configuration ++ dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp); ++ dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp); ++ ++ dprintk( "WBD: ref: %d, sel: %d, active: %d, alpha: %d", ++ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); ++ ++ /* AGC continued */ ++ if (state->wbd_ref != 0) ++ dib7000m_write_word(state, 102, state->wbd_ref); ++ else // use default ++ dib7000m_write_word(state, 102, agc->wbd_ref); ++ ++ dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); ++ dib7000m_write_word(state, 104, agc->agc1_max); ++ dib7000m_write_word(state, 105, agc->agc1_min); ++ dib7000m_write_word(state, 106, agc->agc2_max); ++ dib7000m_write_word(state, 107, agc->agc2_min); ++ dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); ++ dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2); ++ dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2); ++ dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2); ++ ++ if (state->revision > 0x4000) { // settings for the MC ++ dib7000m_write_word(state, 71, agc->agc1_pt3); ++// dprintk( "929: %x %d %d", ++// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel); ++ dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); ++ } else { ++ // wrong default values ++ u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 }; ++ for (i = 0; i < 9; i++) ++ dib7000m_write_word(state, 88 + i, b[i]); ++ } ++ return 0; ++} ++ ++static void dib7000m_update_timf(struct dib7000m_state *state) ++{ ++ u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437); ++ state->timf = timf * 160 / (state->current_bandwidth / 50); ++ dib7000m_write_word(state, 23, (u16) (timf >> 16)); ++ dib7000m_write_word(state, 24, (u16) (timf & 0xffff)); ++ dprintk( "updated timf_frequency: %d (default: %d)",state->timf, state->timf_default); ++} ++ ++static int dib7000m_agc_startup(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000m_state *state = demod->demodulator_priv; ++ u16 cfg_72 = dib7000m_read_word(state, 72); ++ int ret = -1; ++ u8 *agc_state = &state->agc_state; ++ u8 agc_split; ++ ++ switch (state->agc_state) { ++ case 0: ++ // set power-up level: interf+analog+AGC ++ dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC); ++ dib7000m_set_adc_state(state, DIBX000_ADC_ON); ++ ++ if (dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency/1000)) != 0) ++ return -1; ++ ++ ret = 7; /* ADC power up */ ++ (*agc_state)++; ++ break; ++ ++ case 1: ++ /* AGC initialization */ ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(&state->demod, 1); ++ ++ dib7000m_write_word(state, 75, 32768); ++ if (!state->current_agc->perform_agc_softsplit) { ++ /* we are using the wbd - so slow AGC startup */ ++ dib7000m_write_word(state, 103, 1 << 8); /* force 0 split on WBD and restart AGC */ ++ (*agc_state)++; ++ ret = 5; ++ } else { ++ /* default AGC startup */ ++ (*agc_state) = 4; ++ /* wait AGC rough lock time */ ++ ret = 7; ++ } ++ ++ dib7000m_restart_agc(state); ++ break; ++ ++ case 2: /* fast split search path after 5sec */ ++ dib7000m_write_word(state, 72, cfg_72 | (1 << 4)); /* freeze AGC loop */ ++ dib7000m_write_word(state, 103, 2 << 9); /* fast split search 0.25kHz */ ++ (*agc_state)++; ++ ret = 14; ++ break; ++ ++ case 3: /* split search ended */ ++ agc_split = (u8)dib7000m_read_word(state, 392); /* store the split value for the next time */ ++ dib7000m_write_word(state, 75, dib7000m_read_word(state, 390)); /* set AGC gain start value */ ++ ++ dib7000m_write_word(state, 72, cfg_72 & ~(1 << 4)); /* std AGC loop */ ++ dib7000m_write_word(state, 103, (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ ++ ++ dib7000m_restart_agc(state); ++ ++ dprintk( "SPLIT %p: %hd", demod, agc_split); ++ ++ (*agc_state)++; ++ ret = 5; ++ break; ++ ++ case 4: /* LNA startup */ ++ /* wait AGC accurate lock time */ ++ ret = 7; ++ ++ if (dib7000m_update_lna(state)) ++ // wait only AGC rough lock time ++ ret = 5; ++ else ++ (*agc_state)++; ++ break; ++ ++ case 5: ++ dib7000m_agc_soft_split(state); ++ ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(&state->demod, 0); ++ ++ (*agc_state)++; ++ break; ++ ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static void dib7000m_set_channel(struct dib7000m_state *state, struct dtv_frontend_properties *ch, ++ u8 seq) ++{ ++ u16 value, est[4]; ++ ++ dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); ++ ++ /* nfft, guard, qam, alpha */ ++ value = 0; ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: value |= (0 << 7); break; ++ case TRANSMISSION_MODE_4K: value |= (2 << 7); break; ++ default: ++ case TRANSMISSION_MODE_8K: value |= (1 << 7); break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_32: value |= (0 << 5); break; ++ case GUARD_INTERVAL_1_16: value |= (1 << 5); break; ++ case GUARD_INTERVAL_1_4: value |= (3 << 5); break; ++ default: ++ case GUARD_INTERVAL_1_8: value |= (2 << 5); break; ++ } ++ switch (ch->modulation) { ++ case QPSK: value |= (0 << 3); break; ++ case QAM_16: value |= (1 << 3); break; ++ default: ++ case QAM_64: value |= (2 << 3); break; ++ } ++ switch (HIERARCHY_1) { ++ case HIERARCHY_2: value |= 2; break; ++ case HIERARCHY_4: value |= 4; break; ++ default: ++ case HIERARCHY_1: value |= 1; break; ++ } ++ dib7000m_write_word(state, 0, value); ++ dib7000m_write_word(state, 5, (seq << 4)); ++ ++ /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ ++ value = 0; ++ if (1 != 0) ++ value |= (1 << 6); ++ if (ch->hierarchy == 1) ++ value |= (1 << 4); ++ if (1 == 1) ++ value |= 1; ++ switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { ++ case FEC_2_3: value |= (2 << 1); break; ++ case FEC_3_4: value |= (3 << 1); break; ++ case FEC_5_6: value |= (5 << 1); break; ++ case FEC_7_8: value |= (7 << 1); break; ++ default: ++ case FEC_1_2: value |= (1 << 1); break; ++ } ++ dib7000m_write_word(state, 267 + state->reg_offs, value); ++ ++ /* offset loop parameters */ ++ ++ /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */ ++ dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80); ++ ++ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ ++ dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3)); ++ ++ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */ ++ dib7000m_write_word(state, 32, (0 << 4) | 0x3); ++ ++ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */ ++ dib7000m_write_word(state, 33, (0 << 4) | 0x5); ++ ++ /* P_dvsy_sync_wait */ ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_8K: value = 256; break; ++ case TRANSMISSION_MODE_4K: value = 128; break; ++ case TRANSMISSION_MODE_2K: ++ default: value = 64; break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_16: value *= 2; break; ++ case GUARD_INTERVAL_1_8: value *= 4; break; ++ case GUARD_INTERVAL_1_4: value *= 8; break; ++ default: ++ case GUARD_INTERVAL_1_32: value *= 1; break; ++ } ++ state->div_sync_wait = (value * 3) / 2 + 32; // add 50% SFN margin + compensate for one DVSY-fifo TODO ++ ++ /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */ ++ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ ++ if (1 == 1 || state->revision > 0x4000) ++ state->div_force_off = 0; ++ else ++ state->div_force_off = 1; ++ dib7000m_set_diversity_in(&state->demod, state->div_state); ++ ++ /* channel estimation fine configuration */ ++ switch (ch->modulation) { ++ case QAM_64: ++ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ ++ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ ++ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ ++ break; ++ case QAM_16: ++ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ ++ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ ++ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ ++ break; ++ default: ++ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ ++ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ ++ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ ++ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ ++ break; ++ } ++ for (value = 0; value < 4; value++) ++ dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]); ++ ++ // set power-up level: autosearch ++ dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD); ++} ++ ++static int dib7000m_autosearch_start(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000m_state *state = demod->demodulator_priv; ++ struct dtv_frontend_properties schan; ++ int ret = 0; ++ u32 value, factor; ++ ++ schan = *ch; ++ ++ schan.modulation = QAM_64; ++ schan.guard_interval = GUARD_INTERVAL_1_32; ++ schan.transmission_mode = TRANSMISSION_MODE_8K; ++ schan.code_rate_HP = FEC_2_3; ++ schan.code_rate_LP = FEC_3_4; ++ schan.hierarchy = 0; ++ ++ dib7000m_set_channel(state, &schan, 7); ++ ++ factor = BANDWIDTH_TO_KHZ(schan.bandwidth_hz); ++ if (factor >= 5000) ++ factor = 1; ++ else ++ factor = 6; ++ ++ // always use the setting for 8MHz here lock_time for 7,6 MHz are longer ++ value = 30 * state->internal_clk * factor; ++ ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time ++ ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time ++ value = 100 * state->internal_clk * factor; ++ ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time ++ ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time ++ value = 500 * state->internal_clk * factor; ++ ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time ++ ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time ++ ++ // start search ++ value = dib7000m_read_word(state, 0); ++ ret |= dib7000m_write_word(state, 0, (u16) (value | (1 << 9))); ++ ++ /* clear n_irq_pending */ ++ if (state->revision == 0x4000) ++ dib7000m_write_word(state, 1793, 0); ++ else ++ dib7000m_read_word(state, 537); ++ ++ ret |= dib7000m_write_word(state, 0, (u16) value); ++ ++ return ret; ++} ++ ++static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg) ++{ ++ u16 irq_pending = dib7000m_read_word(state, reg); ++ ++ if (irq_pending & 0x1) { // failed ++ dprintk( "autosearch failed"); ++ return 1; ++ } ++ ++ if (irq_pending & 0x2) { // succeeded ++ dprintk( "autosearch succeeded"); ++ return 2; ++ } ++ return 0; // still pending ++} ++ ++static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod) ++{ ++ struct dib7000m_state *state = demod->demodulator_priv; ++ if (state->revision == 0x4000) ++ return dib7000m_autosearch_irq(state, 1793); ++ else ++ return dib7000m_autosearch_irq(state, 537); ++} ++ ++static int dib7000m_tune(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000m_state *state = demod->demodulator_priv; ++ int ret = 0; ++ u16 value; ++ ++ // we are already tuned - just resuming from suspend ++ if (ch != NULL) ++ dib7000m_set_channel(state, ch, 0); ++ else ++ return -EINVAL; ++ ++ // restart demod ++ ret |= dib7000m_write_word(state, 898, 0x4000); ++ ret |= dib7000m_write_word(state, 898, 0x0000); ++ msleep(45); ++ ++ dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD); ++ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ ++ ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); ++ ++ // never achieved a lock before - wait for timfreq to update ++ if (state->timf == 0) ++ msleep(200); ++ ++ //dump_reg(state); ++ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ ++ value = (6 << 8) | 0x80; ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: value |= (7 << 12); break; ++ case TRANSMISSION_MODE_4K: value |= (8 << 12); break; ++ default: ++ case TRANSMISSION_MODE_8K: value |= (9 << 12); break; ++ } ++ ret |= dib7000m_write_word(state, 26, value); ++ ++ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ ++ value = (0 << 4); ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: value |= 0x6; break; ++ case TRANSMISSION_MODE_4K: value |= 0x7; break; ++ default: ++ case TRANSMISSION_MODE_8K: value |= 0x8; break; ++ } ++ ret |= dib7000m_write_word(state, 32, value); ++ ++ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ ++ value = (0 << 4); ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: value |= 0x6; break; ++ case TRANSMISSION_MODE_4K: value |= 0x7; break; ++ default: ++ case TRANSMISSION_MODE_8K: value |= 0x8; break; ++ } ++ ret |= dib7000m_write_word(state, 33, value); ++ ++ // we achieved a lock - it's time to update the timf freq ++ if ((dib7000m_read_word(state, 535) >> 6) & 0x1) ++ dib7000m_update_timf(state); ++ ++ dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); ++ return ret; ++} ++ ++static int dib7000m_wakeup(struct dvb_frontend *demod) ++{ ++ struct dib7000m_state *state = demod->demodulator_priv; ++ ++ dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); ++ ++ if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) ++ dprintk( "could not start Slow ADC"); ++ ++ return 0; ++} ++ ++static int dib7000m_sleep(struct dvb_frontend *demod) ++{ ++ struct dib7000m_state *st = demod->demodulator_priv; ++ dib7000m_set_output_mode(st, OUTMODE_HIGH_Z); ++ dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY); ++ return dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | ++ dib7000m_set_adc_state(st, DIBX000_ADC_OFF); ++} ++ ++static int dib7000m_identify(struct dib7000m_state *state) ++{ ++ u16 value; ++ ++ if ((value = dib7000m_read_word(state, 896)) != 0x01b3) { ++ dprintk( "wrong Vendor ID (0x%x)",value); ++ return -EREMOTEIO; ++ } ++ ++ state->revision = dib7000m_read_word(state, 897); ++ if (state->revision != 0x4000 && ++ state->revision != 0x4001 && ++ state->revision != 0x4002 && ++ state->revision != 0x4003) { ++ dprintk( "wrong Device ID (0x%x)",value); ++ return -EREMOTEIO; ++ } ++ ++ /* protect this driver to be used with 7000PC */ ++ if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) { ++ dprintk( "this driver does not work with DiB7000PC"); ++ return -EREMOTEIO; ++ } ++ ++ switch (state->revision) { ++ case 0x4000: dprintk( "found DiB7000MA/PA/MB/PB"); break; ++ case 0x4001: state->reg_offs = 1; dprintk( "found DiB7000HC"); break; ++ case 0x4002: state->reg_offs = 1; dprintk( "found DiB7000MC"); break; ++ case 0x4003: state->reg_offs = 1; dprintk( "found DiB9000"); break; ++ } ++ ++ return 0; ++} ++ ++ ++static int dib7000m_get_frontend(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib7000m_state *state = fe->demodulator_priv; ++ u16 tps = dib7000m_read_word(state,480); ++ ++ fep->inversion = INVERSION_AUTO; ++ ++ fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); ++ ++ switch ((tps >> 8) & 0x3) { ++ case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; ++ case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; ++ /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ ++ } ++ ++ switch (tps & 0x3) { ++ case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; ++ case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; ++ case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; ++ case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; ++ } ++ ++ switch ((tps >> 14) & 0x3) { ++ case 0: fep->modulation = QPSK; break; ++ case 1: fep->modulation = QAM_16; break; ++ case 2: ++ default: fep->modulation = QAM_64; break; ++ } ++ ++ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ ++ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ ++ ++ fep->hierarchy = HIERARCHY_NONE; ++ switch ((tps >> 5) & 0x7) { ++ case 1: fep->code_rate_HP = FEC_1_2; break; ++ case 2: fep->code_rate_HP = FEC_2_3; break; ++ case 3: fep->code_rate_HP = FEC_3_4; break; ++ case 5: fep->code_rate_HP = FEC_5_6; break; ++ case 7: ++ default: fep->code_rate_HP = FEC_7_8; break; ++ ++ } ++ ++ switch ((tps >> 2) & 0x7) { ++ case 1: fep->code_rate_LP = FEC_1_2; break; ++ case 2: fep->code_rate_LP = FEC_2_3; break; ++ case 3: fep->code_rate_LP = FEC_3_4; break; ++ case 5: fep->code_rate_LP = FEC_5_6; break; ++ case 7: ++ default: fep->code_rate_LP = FEC_7_8; break; ++ } ++ ++ /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ ++ ++ return 0; ++} ++ ++static int dib7000m_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib7000m_state *state = fe->demodulator_priv; ++ int time, ret; ++ ++ dib7000m_set_output_mode(state, OUTMODE_HIGH_Z); ++ ++ dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); ++ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* start up the AGC */ ++ state->agc_state = 0; ++ do { ++ time = dib7000m_agc_startup(fe); ++ if (time != -1) ++ msleep(time); ++ } while (time != -1); ++ ++ if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || ++ fep->guard_interval == GUARD_INTERVAL_AUTO || ++ fep->modulation == QAM_AUTO || ++ fep->code_rate_HP == FEC_AUTO) { ++ int i = 800, found; ++ ++ dib7000m_autosearch_start(fe); ++ do { ++ msleep(1); ++ found = dib7000m_autosearch_is_irq(fe); ++ } while (found == 0 && i--); ++ ++ dprintk("autosearch returns: %d",found); ++ if (found == 0 || found == 1) ++ return 0; // no channel found ++ ++ dib7000m_get_frontend(fe); ++ } ++ ++ ret = dib7000m_tune(fe); ++ ++ /* make this a config parameter */ ++ dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO); ++ return ret; ++} ++ ++static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ u16 lock = dib7000m_read_word(state, 535); ++ ++ *stat = 0; ++ ++ if (lock & 0x8000) ++ *stat |= FE_HAS_SIGNAL; ++ if (lock & 0x3000) ++ *stat |= FE_HAS_CARRIER; ++ if (lock & 0x0100) ++ *stat |= FE_HAS_VITERBI; ++ if (lock & 0x0010) ++ *stat |= FE_HAS_SYNC; ++ if (lock & 0x0008) ++ *stat |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); ++ return 0; ++} ++ ++static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ *unc = dib7000m_read_word(state, 534); ++ return 0; ++} ++ ++static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ u16 val = dib7000m_read_word(state, 390); ++ *strength = 65535 - val; ++ return 0; ++} ++ ++static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) ++{ ++ *snr = 0x0000; ++ return 0; ++} ++ ++static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void dib7000m_release(struct dvb_frontend *demod) ++{ ++ struct dib7000m_state *st = demod->demodulator_priv; ++ dibx000_exit_i2c_master(&st->i2c_master); ++ kfree(st); ++} ++ ++struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) ++{ ++ struct dib7000m_state *st = demod->demodulator_priv; ++ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); ++} ++EXPORT_SYMBOL(dib7000m_get_i2c_master); ++ ++int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ u16 val = dib7000m_read_word(state, 294 + state->reg_offs) & 0xffef; ++ val |= (onoff & 0x1) << 4; ++ dprintk("PID filter enabled %d", onoff); ++ return dib7000m_write_word(state, 294 + state->reg_offs, val); ++} ++EXPORT_SYMBOL(dib7000m_pid_filter_ctrl); ++ ++int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ struct dib7000m_state *state = fe->demodulator_priv; ++ dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); ++ return dib7000m_write_word(state, 300 + state->reg_offs + id, ++ onoff ? (1 << 13) | pid : 0); ++} ++EXPORT_SYMBOL(dib7000m_pid_filter); ++ ++#if 0 ++/* used with some prototype boards */ ++int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, ++ u8 default_addr, struct dib7000m_config cfg[]) ++{ ++ struct dib7000m_state st = { .i2c_adap = i2c }; ++ int k = 0; ++ u8 new_addr = 0; ++ ++ for (k = no_of_demods-1; k >= 0; k--) { ++ st.cfg = cfg[k]; ++ ++ /* designated i2c address */ ++ new_addr = (0x40 + k) << 1; ++ st.i2c_addr = new_addr; ++ if (dib7000m_identify(&st) != 0) { ++ st.i2c_addr = default_addr; ++ if (dib7000m_identify(&st) != 0) { ++ dprintk("DiB7000M #%d: not identified", k); ++ return -EIO; ++ } ++ } ++ ++ /* start diversity to pull_down div_str - just for i2c-enumeration */ ++ dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY); ++ ++ dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output ++ ++ /* set new i2c address and force divstart */ ++ dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2); ++ ++ dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); ++ } ++ ++ for (k = 0; k < no_of_demods; k++) { ++ st.cfg = cfg[k]; ++ st.i2c_addr = (0x40 + k) << 1; ++ ++ // unforce divstr ++ dib7000m_write_word(&st,1794, st.i2c_addr << 2); ++ ++ /* deactivate div - it was just for i2c-enumeration */ ++ dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z); ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib7000m_i2c_enumeration); ++#endif ++ ++static struct dvb_frontend_ops dib7000m_ops; ++struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) ++{ ++ struct dvb_frontend *demod; ++ struct dib7000m_state *st; ++ st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ ++ memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config)); ++ st->i2c_adap = i2c_adap; ++ st->i2c_addr = i2c_addr; ++ ++ demod = &st->demod; ++ demod->demodulator_priv = st; ++ memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); ++ mutex_init(&st->i2c_buffer_lock); ++ ++ st->timf_default = cfg->bw->timf; ++ ++ if (dib7000m_identify(st) != 0) ++ goto error; ++ ++ if (st->revision == 0x4000) ++ dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr); ++ else ++ dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr); ++ ++ dib7000m_demod_reset(st); ++ ++ return demod; ++ ++error: ++ kfree(st); ++ return NULL; ++} ++EXPORT_SYMBOL(dib7000m_attach); ++ ++static struct dvb_frontend_ops dib7000m_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DiBcom 7000MA/MB/PA/PB/MC", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib7000m_release, ++ ++ .init = dib7000m_wakeup, ++ .sleep = dib7000m_sleep, ++ ++ .set_frontend = dib7000m_set_frontend, ++ .get_tune_settings = dib7000m_fe_get_tune_settings, ++ .get_frontend = dib7000m_get_frontend, ++ ++ .read_status = dib7000m_read_status, ++ .read_ber = dib7000m_read_ber, ++ .read_signal_strength = dib7000m_read_signal_strength, ++ .read_snr = dib7000m_read_snr, ++ .read_ucblocks = dib7000m_read_unc_blocks, ++}; ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib7000m.h b/drivers/media/dvb-frontends/dib7000m.h +new file mode 100644 +index 0000000..81fcf22 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib7000m.h +@@ -0,0 +1,90 @@ ++#ifndef DIB7000M_H ++#define DIB7000M_H ++ ++#include "dibx000_common.h" ++ ++struct dib7000m_config { ++ u8 dvbt_mode; ++ u8 output_mpeg2_in_188_bytes; ++ u8 hostbus_diversity; ++ u8 tuner_is_baseband; ++ u8 mobile_mode; ++ int (*update_lna) (struct dvb_frontend *, u16 agc_global); ++ ++ u8 agc_config_count; ++ struct dibx000_agc_config *agc; ++ ++ struct dibx000_bandwidth_config *bw; ++ ++#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff ++ u16 gpio_dir; ++#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000 ++ u16 gpio_val; ++#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12) ++#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) ++#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) ++#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf) ++#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff ++ u16 gpio_pwm_pos; ++ ++ u16 pwm_freq_div; ++ ++ u8 quartz_direct; ++ ++ u8 input_clk_is_div_2; ++ ++ int (*agc_control) (struct dvb_frontend *, u8 before); ++}; ++ ++#define DEFAULT_DIB7000M_I2C_ADDRESS 18 ++ ++#if defined(CONFIG_DVB_DIB7000M) || (defined(CONFIG_DVB_DIB7000M_MODULE) && \ ++ defined(MODULE)) ++extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, ++ struct dib7000m_config *cfg); ++extern struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *, ++ enum dibx000_i2c_interface, ++ int); ++extern int dib7000m_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); ++extern int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); ++#else ++static inline ++struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, struct dib7000m_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline ++struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *demod, ++ enum dibx000_i2c_interface intf, ++ int gating) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, ++ u16 pid, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, ++ uint8_t onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif ++ ++/* TODO ++extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); ++extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod); ++extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); ++extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod); ++*/ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c +new file mode 100644 +index 0000000..3e1eefa +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib7000p.c +@@ -0,0 +1,2457 @@ ++/* ++ * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC). ++ * ++ * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#include ++#include ++#include ++#include ++ ++#include "dvb_math.h" ++#include "dvb_frontend.h" ++ ++#include "dib7000p.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++static int buggy_sfn_workaround; ++module_param(buggy_sfn_workaround, int, 0644); ++MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0) ++ ++struct i2c_device { ++ struct i2c_adapter *i2c_adap; ++ u8 i2c_addr; ++}; ++ ++struct dib7000p_state { ++ struct dvb_frontend demod; ++ struct dib7000p_config cfg; ++ ++ u8 i2c_addr; ++ struct i2c_adapter *i2c_adap; ++ ++ struct dibx000_i2c_master i2c_master; ++ ++ u16 wbd_ref; ++ ++ u8 current_band; ++ u32 current_bandwidth; ++ struct dibx000_agc_config *current_agc; ++ u32 timf; ++ ++ u8 div_force_off:1; ++ u8 div_state:1; ++ u16 div_sync_wait; ++ ++ u8 agc_state; ++ ++ u16 gpio_dir; ++ u16 gpio_val; ++ ++ u8 sfn_workaround_active:1; ++ ++#define SOC7090 0x7090 ++ u16 version; ++ ++ u16 tuner_enable; ++ struct i2c_adapter dib7090_tuner_adap; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[4]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++ ++ u8 input_mode_mpeg; ++}; ++ ++enum dib7000p_power_mode { ++ DIB7000P_POWER_ALL = 0, ++ DIB7000P_POWER_ANALOG_ADC, ++ DIB7000P_POWER_INTERFACE_ONLY, ++}; ++ ++/* dib7090 specific fonctions */ ++static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode); ++static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff); ++static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode); ++static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode); ++ ++static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = reg >> 8; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 2; ++ state->msg[1].addr = state->i2c_addr >> 1; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = state->i2c_read_buffer; ++ state->msg[1].len = 2; ++ ++ if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) ++ dprintk("i2c read error on %d", reg); ++ ++ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ state->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ state->i2c_write_buffer[2] = (val >> 8) & 0xff; ++ state->i2c_write_buffer[3] = val & 0xff; ++ ++ memset(&state->msg[0], 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 4; ++ ++ ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? ++ -EREMOTEIO : 0); ++ mutex_unlock(&state->i2c_buffer_lock); ++ return ret; ++} ++ ++static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf) ++{ ++ u16 l = 0, r, *n; ++ n = buf; ++ l = *n++; ++ while (l) { ++ r = *n++; ++ ++ do { ++ dib7000p_write_word(state, r, *n++); ++ r++; ++ } while (--l); ++ l = *n++; ++ } ++} ++ ++static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) ++{ ++ int ret = 0; ++ u16 outreg, fifo_threshold, smo_mode; ++ ++ outreg = 0; ++ fifo_threshold = 1792; ++ smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); ++ ++ dprintk("setting output mode for demod %p to %d", &state->demod, mode); ++ ++ switch (mode) { ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ outreg = (1 << 10); /* 0x0400 */ ++ break; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: ++ outreg = (1 << 10) | (1 << 6); /* 0x0440 */ ++ break; ++ case OUTMODE_MPEG2_SERIAL: ++ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ ++ break; ++ case OUTMODE_DIVERSITY: ++ if (state->cfg.hostbus_diversity) ++ outreg = (1 << 10) | (4 << 6); /* 0x0500 */ ++ else ++ outreg = (1 << 11); ++ break; ++ case OUTMODE_MPEG2_FIFO: ++ smo_mode |= (3 << 1); ++ fifo_threshold = 512; ++ outreg = (1 << 10) | (5 << 6); ++ break; ++ case OUTMODE_ANALOG_ADC: ++ outreg = (1 << 10) | (3 << 6); ++ break; ++ case OUTMODE_HIGH_Z: ++ outreg = 0; ++ break; ++ default: ++ dprintk("Unhandled output_mode passed to be set for demod %p", &state->demod); ++ break; ++ } ++ ++ if (state->cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5); ++ ++ ret |= dib7000p_write_word(state, 235, smo_mode); ++ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ ++ if (state->version != SOC7090) ++ ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ ++ ++ return ret; ++} ++ ++static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ ++ if (state->div_force_off) { ++ dprintk("diversity combination deactivated - forced by COFDM parameters"); ++ onoff = 0; ++ dib7000p_write_word(state, 207, 0); ++ } else ++ dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); ++ ++ state->div_state = (u8) onoff; ++ ++ if (onoff) { ++ dib7000p_write_word(state, 204, 6); ++ dib7000p_write_word(state, 205, 16); ++ /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ ++ } else { ++ dib7000p_write_word(state, 204, 1); ++ dib7000p_write_word(state, 205, 0); ++ } ++ ++ return 0; ++} ++ ++static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) ++{ ++ /* by default everything is powered off */ ++ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); ++ ++ /* now, depending on the requested mode, we power on */ ++ switch (mode) { ++ /* power up everything in the demod */ ++ case DIB7000P_POWER_ALL: ++ reg_774 = 0x0000; ++ reg_775 = 0x0000; ++ reg_776 = 0x0; ++ reg_899 = 0x0; ++ if (state->version == SOC7090) ++ reg_1280 &= 0x001f; ++ else ++ reg_1280 &= 0x01ff; ++ break; ++ ++ case DIB7000P_POWER_ANALOG_ADC: ++ /* dem, cfg, iqc, sad, agc */ ++ reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9)); ++ /* nud */ ++ reg_776 &= ~((1 << 0)); ++ /* Dout */ ++ if (state->version != SOC7090) ++ reg_1280 &= ~((1 << 11)); ++ reg_1280 &= ~(1 << 6); ++ /* fall through wanted to enable the interfaces */ ++ ++ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ ++ case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ ++ if (state->version == SOC7090) ++ reg_1280 &= ~((1 << 7) | (1 << 5)); ++ else ++ reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); ++ break; ++ ++/* TODO following stuff is just converted from the dib7000-driver - check when is used what */ ++ } ++ ++ dib7000p_write_word(state, 774, reg_774); ++ dib7000p_write_word(state, 775, reg_775); ++ dib7000p_write_word(state, 776, reg_776); ++ dib7000p_write_word(state, 1280, reg_1280); ++ if (state->version != SOC7090) ++ dib7000p_write_word(state, 899, reg_899); ++ ++ return 0; ++} ++ ++static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) ++{ ++ u16 reg_908 = 0, reg_909 = 0; ++ u16 reg; ++ ++ if (state->version != SOC7090) { ++ reg_908 = dib7000p_read_word(state, 908); ++ reg_909 = dib7000p_read_word(state, 909); ++ } ++ ++ switch (no) { ++ case DIBX000_SLOW_ADC_ON: ++ if (state->version == SOC7090) { ++ reg = dib7000p_read_word(state, 1925); ++ ++ dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */ ++ ++ reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */ ++ msleep(200); ++ dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */ ++ ++ reg = dib7000p_read_word(state, 72) & ~((0x3 << 14) | (0x3 << 12)); ++ dib7000p_write_word(state, 72, reg | (1 << 14) | (3 << 12) | 524); /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; (Vin2 = Vcm) */ ++ } else { ++ reg_909 |= (1 << 1) | (1 << 0); ++ dib7000p_write_word(state, 909, reg_909); ++ reg_909 &= ~(1 << 1); ++ } ++ break; ++ ++ case DIBX000_SLOW_ADC_OFF: ++ if (state->version == SOC7090) { ++ reg = dib7000p_read_word(state, 1925); ++ dib7000p_write_word(state, 1925, (reg & ~(1 << 2)) | (1 << 4)); /* reset_sladc = 1 en_slowAdc = 0 */ ++ } else ++ reg_909 |= (1 << 1) | (1 << 0); ++ break; ++ ++ case DIBX000_ADC_ON: ++ reg_908 &= 0x0fff; ++ reg_909 &= 0x0003; ++ break; ++ ++ case DIBX000_ADC_OFF: ++ reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); ++ reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); ++ break; ++ ++ case DIBX000_VBG_ENABLE: ++ reg_908 &= ~(1 << 15); ++ break; ++ ++ case DIBX000_VBG_DISABLE: ++ reg_908 |= (1 << 15); ++ break; ++ ++ default: ++ break; ++ } ++ ++// dprintk( "908: %x, 909: %x\n", reg_908, reg_909); ++ ++ reg_909 |= (state->cfg.disable_sample_and_hold & 1) << 4; ++ reg_908 |= (state->cfg.enable_current_mirror & 1) << 7; ++ ++ if (state->version != SOC7090) { ++ dib7000p_write_word(state, 908, reg_908); ++ dib7000p_write_word(state, 909, reg_909); ++ } ++} ++ ++static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw) ++{ ++ u32 timf; ++ ++ // store the current bandwidth for later use ++ state->current_bandwidth = bw; ++ ++ if (state->timf == 0) { ++ dprintk("using default timf"); ++ timf = state->cfg.bw->timf; ++ } else { ++ dprintk("using updated timf"); ++ timf = state->timf; ++ } ++ ++ timf = timf * (bw / 50) / 160; ++ ++ dib7000p_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); ++ dib7000p_write_word(state, 24, (u16) ((timf) & 0xffff)); ++ ++ return 0; ++} ++ ++static int dib7000p_sad_calib(struct dib7000p_state *state) ++{ ++/* internal */ ++ dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); ++ ++ if (state->version == SOC7090) ++ dib7000p_write_word(state, 74, 2048); ++ else ++ dib7000p_write_word(state, 74, 776); ++ ++ /* do the calibration */ ++ dib7000p_write_word(state, 73, (1 << 0)); ++ dib7000p_write_word(state, 73, (0 << 0)); ++ ++ msleep(1); ++ ++ return 0; ++} ++ ++int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ if (value > 4095) ++ value = 4095; ++ state->wbd_ref = value; ++ return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value); ++} ++EXPORT_SYMBOL(dib7000p_set_wbd_ref); ++ ++int dib7000p_get_agc_values(struct dvb_frontend *fe, ++ u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ ++ if (agc_global != NULL) ++ *agc_global = dib7000p_read_word(state, 394); ++ if (agc1 != NULL) ++ *agc1 = dib7000p_read_word(state, 392); ++ if (agc2 != NULL) ++ *agc2 = dib7000p_read_word(state, 393); ++ if (wbd != NULL) ++ *wbd = dib7000p_read_word(state, 397); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib7000p_get_agc_values); ++ ++static void dib7000p_reset_pll(struct dib7000p_state *state) ++{ ++ struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; ++ u16 clk_cfg0; ++ ++ if (state->version == SOC7090) { ++ dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv)); ++ ++ while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) ++ ; ++ ++ dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15)); ++ } else { ++ /* force PLL bypass */ ++ clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) | ++ (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0); ++ ++ dib7000p_write_word(state, 900, clk_cfg0); ++ ++ /* P_pll_cfg */ ++ dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); ++ clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff); ++ dib7000p_write_word(state, 900, clk_cfg0); ++ } ++ ++ dib7000p_write_word(state, 18, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); ++ dib7000p_write_word(state, 19, (u16) ((bw->internal * 1000) & 0xffff)); ++ dib7000p_write_word(state, 21, (u16) ((bw->ifreq >> 16) & 0xffff)); ++ dib7000p_write_word(state, 22, (u16) ((bw->ifreq) & 0xffff)); ++ ++ dib7000p_write_word(state, 72, bw->sad_cfg); ++} ++ ++static u32 dib7000p_get_internal_freq(struct dib7000p_state *state) ++{ ++ u32 internal = (u32) dib7000p_read_word(state, 18) << 16; ++ internal |= (u32) dib7000p_read_word(state, 19); ++ internal /= 1000; ++ ++ return internal; ++} ++ ++int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856); ++ u8 loopdiv, prediv; ++ u32 internal, xtal; ++ ++ /* get back old values */ ++ prediv = reg_1856 & 0x3f; ++ loopdiv = (reg_1856 >> 6) & 0x3f; ++ ++ if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { ++ dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); ++ reg_1856 &= 0xf000; ++ reg_1857 = dib7000p_read_word(state, 1857); ++ dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); ++ ++ dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f)); ++ ++ /* write new system clk into P_sec_len */ ++ internal = dib7000p_get_internal_freq(state); ++ xtal = (internal / loopdiv) * prediv; ++ internal = 1000 * (xtal / bw->pll_prediv) * bw->pll_ratio; /* new internal */ ++ dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff)); ++ dib7000p_write_word(state, 19, (u16) (internal & 0xffff)); ++ ++ dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); ++ ++ while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) ++ dprintk("Waiting for PLL to lock"); ++ ++ return 0; ++ } ++ return -EIO; ++} ++EXPORT_SYMBOL(dib7000p_update_pll); ++ ++static int dib7000p_reset_gpio(struct dib7000p_state *st) ++{ ++ /* reset the GPIOs */ ++ dprintk("gpio dir: %x: val: %x, pwm_pos: %x", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos); ++ ++ dib7000p_write_word(st, 1029, st->gpio_dir); ++ dib7000p_write_word(st, 1030, st->gpio_val); ++ ++ /* TODO 1031 is P_gpio_od */ ++ ++ dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos); ++ ++ dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div); ++ return 0; ++} ++ ++static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val) ++{ ++ st->gpio_dir = dib7000p_read_word(st, 1029); ++ st->gpio_dir &= ~(1 << num); /* reset the direction bit */ ++ st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ ++ dib7000p_write_word(st, 1029, st->gpio_dir); ++ ++ st->gpio_val = dib7000p_read_word(st, 1030); ++ st->gpio_val &= ~(1 << num); /* reset the direction bit */ ++ st->gpio_val |= (val & 0x01) << num; /* set the new value */ ++ dib7000p_write_word(st, 1030, st->gpio_val); ++ ++ return 0; ++} ++ ++int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ return dib7000p_cfg_gpio(state, num, dir, val); ++} ++EXPORT_SYMBOL(dib7000p_set_gpio); ++ ++static u16 dib7000p_defaults[] = { ++ // auto search configuration ++ 3, 2, ++ 0x0004, ++ (1<<3)|(1<<11)|(1<<12)|(1<<13), ++ 0x0814, /* Equal Lock */ ++ ++ 12, 6, ++ 0x001b, ++ 0x7740, ++ 0x005b, ++ 0x8d80, ++ 0x01c9, ++ 0xc380, ++ 0x0000, ++ 0x0080, ++ 0x0000, ++ 0x0090, ++ 0x0001, ++ 0xd4c0, ++ ++ 1, 26, ++ 0x6680, ++ ++ /* set ADC level to -16 */ ++ 11, 79, ++ (1 << 13) - 825 - 117, ++ (1 << 13) - 837 - 117, ++ (1 << 13) - 811 - 117, ++ (1 << 13) - 766 - 117, ++ (1 << 13) - 737 - 117, ++ (1 << 13) - 693 - 117, ++ (1 << 13) - 648 - 117, ++ (1 << 13) - 619 - 117, ++ (1 << 13) - 575 - 117, ++ (1 << 13) - 531 - 117, ++ (1 << 13) - 501 - 117, ++ ++ 1, 142, ++ 0x0410, ++ ++ /* disable power smoothing */ ++ 8, 145, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ ++ 1, 154, ++ 1 << 13, ++ ++ 1, 168, ++ 0x0ccd, ++ ++ 1, 183, ++ 0x200f, ++ ++ 1, 212, ++ 0x169, ++ ++ 5, 187, ++ 0x023d, ++ 0x00a4, ++ 0x00a4, ++ 0x7ff0, ++ 0x3ccc, ++ ++ 1, 198, ++ 0x800, ++ ++ 1, 222, ++ 0x0010, ++ ++ 1, 235, ++ 0x0062, ++ ++ 0, ++}; ++ ++static int dib7000p_demod_reset(struct dib7000p_state *state) ++{ ++ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); ++ ++ if (state->version == SOC7090) ++ dibx000_reset_i2c_master(&state->i2c_master); ++ ++ dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); ++ ++ /* restart all parts */ ++ dib7000p_write_word(state, 770, 0xffff); ++ dib7000p_write_word(state, 771, 0xffff); ++ dib7000p_write_word(state, 772, 0x001f); ++ dib7000p_write_word(state, 1280, 0x001f - ((1 << 4) | (1 << 3))); ++ ++ dib7000p_write_word(state, 770, 0); ++ dib7000p_write_word(state, 771, 0); ++ dib7000p_write_word(state, 772, 0); ++ dib7000p_write_word(state, 1280, 0); ++ ++ if (state->version != SOC7090) { ++ dib7000p_write_word(state, 898, 0x0003); ++ dib7000p_write_word(state, 898, 0); ++ } ++ ++ /* default */ ++ dib7000p_reset_pll(state); ++ ++ if (dib7000p_reset_gpio(state) != 0) ++ dprintk("GPIO reset was not successful."); ++ ++ if (state->version == SOC7090) { ++ dib7000p_write_word(state, 899, 0); ++ ++ /* impulse noise */ ++ dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */ ++ dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */ ++ dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */ ++ dib7000p_write_word(state, 273, (0<<6) | 30); ++ } ++ if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) ++ dprintk("OUTPUT_MODE could not be reset."); ++ ++ dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); ++ dib7000p_sad_calib(state); ++ dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_OFF); ++ ++ /* unforce divstr regardless whether i2c enumeration was done or not */ ++ dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1)); ++ ++ dib7000p_set_bandwidth(state, 8000); ++ ++ if (state->version == SOC7090) { ++ dib7000p_write_word(state, 36, 0x0755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */ ++ } else { ++ if (state->cfg.tuner_is_baseband) ++ dib7000p_write_word(state, 36, 0x0755); ++ else ++ dib7000p_write_word(state, 36, 0x1f55); ++ } ++ ++ dib7000p_write_tab(state, dib7000p_defaults); ++ if (state->version != SOC7090) { ++ dib7000p_write_word(state, 901, 0x0006); ++ dib7000p_write_word(state, 902, (3 << 10) | (1 << 6)); ++ dib7000p_write_word(state, 905, 0x2c8e); ++ } ++ ++ dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); ++ ++ return 0; ++} ++ ++static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) ++{ ++ u16 tmp = 0; ++ tmp = dib7000p_read_word(state, 903); ++ dib7000p_write_word(state, 903, (tmp | 0x1)); ++ tmp = dib7000p_read_word(state, 900); ++ dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); ++} ++ ++static void dib7000p_restart_agc(struct dib7000p_state *state) ++{ ++ // P_restart_iqc & P_restart_agc ++ dib7000p_write_word(state, 770, (1 << 11) | (1 << 9)); ++ dib7000p_write_word(state, 770, 0x0000); ++} ++ ++static int dib7000p_update_lna(struct dib7000p_state *state) ++{ ++ u16 dyn_gain; ++ ++ if (state->cfg.update_lna) { ++ dyn_gain = dib7000p_read_word(state, 394); ++ if (state->cfg.update_lna(&state->demod, dyn_gain)) { ++ dib7000p_restart_agc(state); ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) ++{ ++ struct dibx000_agc_config *agc = NULL; ++ int i; ++ if (state->current_band == band && state->current_agc != NULL) ++ return 0; ++ state->current_band = band; ++ ++ for (i = 0; i < state->cfg.agc_config_count; i++) ++ if (state->cfg.agc[i].band_caps & band) { ++ agc = &state->cfg.agc[i]; ++ break; ++ } ++ ++ if (agc == NULL) { ++ dprintk("no valid AGC configuration found for band 0x%02x", band); ++ return -EINVAL; ++ } ++ ++ state->current_agc = agc; ++ ++ /* AGC */ ++ dib7000p_write_word(state, 75, agc->setup); ++ dib7000p_write_word(state, 76, agc->inv_gain); ++ dib7000p_write_word(state, 77, agc->time_stabiliz); ++ dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); ++ ++ // Demod AGC loop configuration ++ dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); ++ dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); ++ ++ /* AGC continued */ ++ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", ++ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); ++ ++ if (state->wbd_ref != 0) ++ dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref); ++ else ++ dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref); ++ ++ dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); ++ ++ dib7000p_write_word(state, 107, agc->agc1_max); ++ dib7000p_write_word(state, 108, agc->agc1_min); ++ dib7000p_write_word(state, 109, agc->agc2_max); ++ dib7000p_write_word(state, 110, agc->agc2_min); ++ dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2); ++ dib7000p_write_word(state, 112, agc->agc1_pt3); ++ dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); ++ dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); ++ dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); ++ return 0; ++} ++ ++static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) ++{ ++ u32 internal = dib7000p_get_internal_freq(state); ++ s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */ ++ u32 abs_offset_khz = ABS(offset_khz); ++ u32 dds = state->cfg.bw->ifreq & 0x1ffffff; ++ u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); ++ ++ dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d", offset_khz, internal, invert); ++ ++ if (offset_khz < 0) ++ unit_khz_dds_val *= -1; ++ ++ /* IF tuner */ ++ if (invert) ++ dds -= (abs_offset_khz * unit_khz_dds_val); /* /100 because of /100 on the unit_khz_dds_val line calc for better accuracy */ ++ else ++ dds += (abs_offset_khz * unit_khz_dds_val); ++ ++ if (abs_offset_khz <= (internal / 2)) { /* Max dds offset is the half of the demod freq */ ++ dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9))); ++ dib7000p_write_word(state, 22, (u16) (dds & 0xffff)); ++ } ++} ++ ++static int dib7000p_agc_startup(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000p_state *state = demod->demodulator_priv; ++ int ret = -1; ++ u8 *agc_state = &state->agc_state; ++ u8 agc_split; ++ u16 reg; ++ u32 upd_demod_gain_period = 0x1000; ++ ++ switch (state->agc_state) { ++ case 0: ++ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); ++ if (state->version == SOC7090) { ++ reg = dib7000p_read_word(state, 0x79b) & 0xff00; ++ dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */ ++ dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); ++ ++ /* enable adc i & q */ ++ reg = dib7000p_read_word(state, 0x780); ++ dib7000p_write_word(state, 0x780, (reg | (0x3)) & (~(1 << 7))); ++ } else { ++ dib7000p_set_adc_state(state, DIBX000_ADC_ON); ++ dib7000p_pll_clk_cfg(state); ++ } ++ ++ if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) ++ return -1; ++ ++ dib7000p_set_dds(state, 0); ++ ret = 7; ++ (*agc_state)++; ++ break; ++ ++ case 1: ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(&state->demod, 1); ++ ++ dib7000p_write_word(state, 78, 32768); ++ if (!state->current_agc->perform_agc_softsplit) { ++ /* we are using the wbd - so slow AGC startup */ ++ /* force 0 split on WBD and restart AGC */ ++ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8)); ++ (*agc_state)++; ++ ret = 5; ++ } else { ++ /* default AGC startup */ ++ (*agc_state) = 4; ++ /* wait AGC rough lock time */ ++ ret = 7; ++ } ++ ++ dib7000p_restart_agc(state); ++ break; ++ ++ case 2: /* fast split search path after 5sec */ ++ dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */ ++ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */ ++ (*agc_state)++; ++ ret = 14; ++ break; ++ ++ case 3: /* split search ended */ ++ agc_split = (u8) dib7000p_read_word(state, 396); /* store the split value for the next time */ ++ dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */ ++ ++ dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */ ++ dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ ++ ++ dib7000p_restart_agc(state); ++ ++ dprintk("SPLIT %p: %hd", demod, agc_split); ++ ++ (*agc_state)++; ++ ret = 5; ++ break; ++ ++ case 4: /* LNA startup */ ++ ret = 7; ++ ++ if (dib7000p_update_lna(state)) ++ ret = 5; ++ else ++ (*agc_state)++; ++ break; ++ ++ case 5: ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(&state->demod, 0); ++ (*agc_state)++; ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static void dib7000p_update_timf(struct dib7000p_state *state) ++{ ++ u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428); ++ state->timf = timf * 160 / (state->current_bandwidth / 50); ++ dib7000p_write_word(state, 23, (u16) (timf >> 16)); ++ dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); ++ dprintk("updated timf_frequency: %d (default: %d)", state->timf, state->cfg.bw->timf); ++ ++} ++ ++u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ switch (op) { ++ case DEMOD_TIMF_SET: ++ state->timf = timf; ++ break; ++ case DEMOD_TIMF_UPDATE: ++ dib7000p_update_timf(state); ++ break; ++ case DEMOD_TIMF_GET: ++ break; ++ } ++ dib7000p_set_bandwidth(state, state->current_bandwidth); ++ return state->timf; ++} ++EXPORT_SYMBOL(dib7000p_ctrl_timf); ++ ++static void dib7000p_set_channel(struct dib7000p_state *state, ++ struct dtv_frontend_properties *ch, u8 seq) ++{ ++ u16 value, est[4]; ++ ++ dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); ++ ++ /* nfft, guard, qam, alpha */ ++ value = 0; ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ value |= (0 << 7); ++ break; ++ case TRANSMISSION_MODE_4K: ++ value |= (2 << 7); ++ break; ++ default: ++ case TRANSMISSION_MODE_8K: ++ value |= (1 << 7); ++ break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ value |= (0 << 5); ++ break; ++ case GUARD_INTERVAL_1_16: ++ value |= (1 << 5); ++ break; ++ case GUARD_INTERVAL_1_4: ++ value |= (3 << 5); ++ break; ++ default: ++ case GUARD_INTERVAL_1_8: ++ value |= (2 << 5); ++ break; ++ } ++ switch (ch->modulation) { ++ case QPSK: ++ value |= (0 << 3); ++ break; ++ case QAM_16: ++ value |= (1 << 3); ++ break; ++ default: ++ case QAM_64: ++ value |= (2 << 3); ++ break; ++ } ++ switch (HIERARCHY_1) { ++ case HIERARCHY_2: ++ value |= 2; ++ break; ++ case HIERARCHY_4: ++ value |= 4; ++ break; ++ default: ++ case HIERARCHY_1: ++ value |= 1; ++ break; ++ } ++ dib7000p_write_word(state, 0, value); ++ dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ ++ ++ /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ ++ value = 0; ++ if (1 != 0) ++ value |= (1 << 6); ++ if (ch->hierarchy == 1) ++ value |= (1 << 4); ++ if (1 == 1) ++ value |= 1; ++ switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { ++ case FEC_2_3: ++ value |= (2 << 1); ++ break; ++ case FEC_3_4: ++ value |= (3 << 1); ++ break; ++ case FEC_5_6: ++ value |= (5 << 1); ++ break; ++ case FEC_7_8: ++ value |= (7 << 1); ++ break; ++ default: ++ case FEC_1_2: ++ value |= (1 << 1); ++ break; ++ } ++ dib7000p_write_word(state, 208, value); ++ ++ /* offset loop parameters */ ++ dib7000p_write_word(state, 26, 0x6680); ++ dib7000p_write_word(state, 32, 0x0003); ++ dib7000p_write_word(state, 29, 0x1273); ++ dib7000p_write_word(state, 33, 0x0005); ++ ++ /* P_dvsy_sync_wait */ ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_8K: ++ value = 256; ++ break; ++ case TRANSMISSION_MODE_4K: ++ value = 128; ++ break; ++ case TRANSMISSION_MODE_2K: ++ default: ++ value = 64; ++ break; ++ } ++ switch (ch->guard_interval) { ++ case GUARD_INTERVAL_1_16: ++ value *= 2; ++ break; ++ case GUARD_INTERVAL_1_8: ++ value *= 4; ++ break; ++ case GUARD_INTERVAL_1_4: ++ value *= 8; ++ break; ++ default: ++ case GUARD_INTERVAL_1_32: ++ value *= 1; ++ break; ++ } ++ if (state->cfg.diversity_delay == 0) ++ state->div_sync_wait = (value * 3) / 2 + 48; ++ else ++ state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; ++ ++ /* deactive the possibility of diversity reception if extended interleaver */ ++ state->div_force_off = !1 && ch->transmission_mode != TRANSMISSION_MODE_8K; ++ dib7000p_set_diversity_in(&state->demod, state->div_state); ++ ++ /* channel estimation fine configuration */ ++ switch (ch->modulation) { ++ case QAM_64: ++ est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ ++ est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ ++ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ ++ break; ++ case QAM_16: ++ est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ ++ est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ ++ est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ ++ break; ++ default: ++ est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ ++ est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ ++ est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ ++ est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ ++ break; ++ } ++ for (value = 0; value < 4; value++) ++ dib7000p_write_word(state, 187 + value, est[value]); ++} ++ ++static int dib7000p_autosearch_start(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000p_state *state = demod->demodulator_priv; ++ struct dtv_frontend_properties schan; ++ u32 value, factor; ++ u32 internal = dib7000p_get_internal_freq(state); ++ ++ schan = *ch; ++ schan.modulation = QAM_64; ++ schan.guard_interval = GUARD_INTERVAL_1_32; ++ schan.transmission_mode = TRANSMISSION_MODE_8K; ++ schan.code_rate_HP = FEC_2_3; ++ schan.code_rate_LP = FEC_3_4; ++ schan.hierarchy = 0; ++ ++ dib7000p_set_channel(state, &schan, 7); ++ ++ factor = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); ++ if (factor >= 5000) { ++ if (state->version == SOC7090) ++ factor = 2; ++ else ++ factor = 1; ++ } else ++ factor = 6; ++ ++ value = 30 * internal * factor; ++ dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); ++ dib7000p_write_word(state, 7, (u16) (value & 0xffff)); ++ value = 100 * internal * factor; ++ dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); ++ dib7000p_write_word(state, 9, (u16) (value & 0xffff)); ++ value = 500 * internal * factor; ++ dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); ++ dib7000p_write_word(state, 11, (u16) (value & 0xffff)); ++ ++ value = dib7000p_read_word(state, 0); ++ dib7000p_write_word(state, 0, (u16) ((1 << 9) | value)); ++ dib7000p_read_word(state, 1284); ++ dib7000p_write_word(state, 0, (u16) value); ++ ++ return 0; ++} ++ ++static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ u16 irq_pending = dib7000p_read_word(state, 1284); ++ ++ if (irq_pending & 0x1) ++ return 1; ++ ++ if (irq_pending & 0x2) ++ return 2; ++ ++ return 0; ++} ++ ++static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) ++{ ++ static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; ++ static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, ++ 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, ++ 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, ++ 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, ++ 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126, ++ 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, ++ 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165, ++ 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, ++ 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, ++ 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, ++ 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, ++ 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, ++ 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, ++ 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, ++ 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, ++ 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, ++ 255, 255, 255, 255, 255, 255 ++ }; ++ ++ u32 xtal = state->cfg.bw->xtal_hz / 1000; ++ int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz; ++ int k; ++ int coef_re[8], coef_im[8]; ++ int bw_khz = bw; ++ u32 pha; ++ ++ dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal); ++ ++ if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2) ++ return; ++ ++ bw_khz /= 100; ++ ++ dib7000p_write_word(state, 142, 0x0610); ++ ++ for (k = 0; k < 8; k++) { ++ pha = ((f_rel * (k + 1) * 112 * 80 / bw_khz) / 1000) & 0x3ff; ++ ++ if (pha == 0) { ++ coef_re[k] = 256; ++ coef_im[k] = 0; ++ } else if (pha < 256) { ++ coef_re[k] = sine[256 - (pha & 0xff)]; ++ coef_im[k] = sine[pha & 0xff]; ++ } else if (pha == 256) { ++ coef_re[k] = 0; ++ coef_im[k] = 256; ++ } else if (pha < 512) { ++ coef_re[k] = -sine[pha & 0xff]; ++ coef_im[k] = sine[256 - (pha & 0xff)]; ++ } else if (pha == 512) { ++ coef_re[k] = -256; ++ coef_im[k] = 0; ++ } else if (pha < 768) { ++ coef_re[k] = -sine[256 - (pha & 0xff)]; ++ coef_im[k] = -sine[pha & 0xff]; ++ } else if (pha == 768) { ++ coef_re[k] = 0; ++ coef_im[k] = -256; ++ } else { ++ coef_re[k] = sine[pha & 0xff]; ++ coef_im[k] = -sine[256 - (pha & 0xff)]; ++ } ++ ++ coef_re[k] *= notch[k]; ++ coef_re[k] += (1 << 14); ++ if (coef_re[k] >= (1 << 24)) ++ coef_re[k] = (1 << 24) - 1; ++ coef_re[k] /= (1 << 15); ++ ++ coef_im[k] *= notch[k]; ++ coef_im[k] += (1 << 14); ++ if (coef_im[k] >= (1 << 24)) ++ coef_im[k] = (1 << 24) - 1; ++ coef_im[k] /= (1 << 15); ++ ++ dprintk("PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]); ++ ++ dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); ++ dib7000p_write_word(state, 144, coef_im[k] & 0x3ff); ++ dib7000p_write_word(state, 143, (1 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); ++ } ++ dib7000p_write_word(state, 143, 0); ++} ++ ++static int dib7000p_tune(struct dvb_frontend *demod) ++{ ++ struct dtv_frontend_properties *ch = &demod->dtv_property_cache; ++ struct dib7000p_state *state = demod->demodulator_priv; ++ u16 tmp = 0; ++ ++ if (ch != NULL) ++ dib7000p_set_channel(state, ch, 0); ++ else ++ return -EINVAL; ++ ++ // restart demod ++ dib7000p_write_word(state, 770, 0x4000); ++ dib7000p_write_word(state, 770, 0x0000); ++ msleep(45); ++ ++ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ ++ tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3); ++ if (state->sfn_workaround_active) { ++ dprintk("SFN workaround is active"); ++ tmp |= (1 << 9); ++ dib7000p_write_word(state, 166, 0x4000); ++ } else { ++ dib7000p_write_word(state, 166, 0x0000); ++ } ++ dib7000p_write_word(state, 29, tmp); ++ ++ // never achieved a lock with that bandwidth so far - wait for osc-freq to update ++ if (state->timf == 0) ++ msleep(200); ++ ++ /* offset loop parameters */ ++ ++ /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ ++ tmp = (6 << 8) | 0x80; ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ tmp |= (2 << 12); ++ break; ++ case TRANSMISSION_MODE_4K: ++ tmp |= (3 << 12); ++ break; ++ default: ++ case TRANSMISSION_MODE_8K: ++ tmp |= (4 << 12); ++ break; ++ } ++ dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ ++ ++ /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ ++ tmp = (0 << 4); ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ tmp |= 0x6; ++ break; ++ case TRANSMISSION_MODE_4K: ++ tmp |= 0x7; ++ break; ++ default: ++ case TRANSMISSION_MODE_8K: ++ tmp |= 0x8; ++ break; ++ } ++ dib7000p_write_word(state, 32, tmp); ++ ++ /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ ++ tmp = (0 << 4); ++ switch (ch->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ tmp |= 0x6; ++ break; ++ case TRANSMISSION_MODE_4K: ++ tmp |= 0x7; ++ break; ++ default: ++ case TRANSMISSION_MODE_8K: ++ tmp |= 0x8; ++ break; ++ } ++ dib7000p_write_word(state, 33, tmp); ++ ++ tmp = dib7000p_read_word(state, 509); ++ if (!((tmp >> 6) & 0x1)) { ++ /* restart the fec */ ++ tmp = dib7000p_read_word(state, 771); ++ dib7000p_write_word(state, 771, tmp | (1 << 1)); ++ dib7000p_write_word(state, 771, tmp); ++ msleep(40); ++ tmp = dib7000p_read_word(state, 509); ++ } ++ // we achieved a lock - it's time to update the osc freq ++ if ((tmp >> 6) & 0x1) { ++ dib7000p_update_timf(state); ++ /* P_timf_alpha += 2 */ ++ tmp = dib7000p_read_word(state, 26); ++ dib7000p_write_word(state, 26, (tmp & ~(0xf << 12)) | ((((tmp >> 12) & 0xf) + 5) << 12)); ++ } ++ ++ if (state->cfg.spur_protect) ++ dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); ++ ++ dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); ++ return 0; ++} ++ ++static int dib7000p_wakeup(struct dvb_frontend *demod) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); ++ dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); ++ if (state->version == SOC7090) ++ dib7000p_sad_calib(state); ++ return 0; ++} ++ ++static int dib7000p_sleep(struct dvb_frontend *demod) ++{ ++ struct dib7000p_state *state = demod->demodulator_priv; ++ if (state->version == SOC7090) ++ return dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); ++ return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); ++} ++ ++static int dib7000p_identify(struct dib7000p_state *st) ++{ ++ u16 value; ++ dprintk("checking demod on I2C address: %d (%x)", st->i2c_addr, st->i2c_addr); ++ ++ if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { ++ dprintk("wrong Vendor ID (read=0x%x)", value); ++ return -EREMOTEIO; ++ } ++ ++ if ((value = dib7000p_read_word(st, 769)) != 0x4000) { ++ dprintk("wrong Device ID (%x)", value); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int dib7000p_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 tps = dib7000p_read_word(state, 463); ++ ++ fep->inversion = INVERSION_AUTO; ++ ++ fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); ++ ++ switch ((tps >> 8) & 0x3) { ++ case 0: ++ fep->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ fep->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ ++ } ++ ++ switch (tps & 0x3) { ++ case 0: ++ fep->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ fep->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ fep->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ fep->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((tps >> 14) & 0x3) { ++ case 0: ++ fep->modulation = QPSK; ++ break; ++ case 1: ++ fep->modulation = QAM_16; ++ break; ++ case 2: ++ default: ++ fep->modulation = QAM_64; ++ break; ++ } ++ ++ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ ++ /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ ++ ++ fep->hierarchy = HIERARCHY_NONE; ++ switch ((tps >> 5) & 0x7) { ++ case 1: ++ fep->code_rate_HP = FEC_1_2; ++ break; ++ case 2: ++ fep->code_rate_HP = FEC_2_3; ++ break; ++ case 3: ++ fep->code_rate_HP = FEC_3_4; ++ break; ++ case 5: ++ fep->code_rate_HP = FEC_5_6; ++ break; ++ case 7: ++ default: ++ fep->code_rate_HP = FEC_7_8; ++ break; ++ ++ } ++ ++ switch ((tps >> 2) & 0x7) { ++ case 1: ++ fep->code_rate_LP = FEC_1_2; ++ break; ++ case 2: ++ fep->code_rate_LP = FEC_2_3; ++ break; ++ case 3: ++ fep->code_rate_LP = FEC_3_4; ++ break; ++ case 5: ++ fep->code_rate_LP = FEC_5_6; ++ break; ++ case 7: ++ default: ++ fep->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ ++ ++ return 0; ++} ++ ++static int dib7000p_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fep = &fe->dtv_property_cache; ++ struct dib7000p_state *state = fe->demodulator_priv; ++ int time, ret; ++ ++ if (state->version == SOC7090) ++ dib7090_set_diversity_in(fe, 0); ++ else ++ dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); ++ ++ /* maybe the parameter has been changed */ ++ state->sfn_workaround_active = buggy_sfn_workaround; ++ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* start up the AGC */ ++ state->agc_state = 0; ++ do { ++ time = dib7000p_agc_startup(fe); ++ if (time != -1) ++ msleep(time); ++ } while (time != -1); ++ ++ if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || ++ fep->guard_interval == GUARD_INTERVAL_AUTO || fep->modulation == QAM_AUTO || fep->code_rate_HP == FEC_AUTO) { ++ int i = 800, found; ++ ++ dib7000p_autosearch_start(fe); ++ do { ++ msleep(1); ++ found = dib7000p_autosearch_is_irq(fe); ++ } while (found == 0 && i--); ++ ++ dprintk("autosearch returns: %d", found); ++ if (found == 0 || found == 1) ++ return 0; ++ ++ dib7000p_get_frontend(fe); ++ } ++ ++ ret = dib7000p_tune(fe); ++ ++ /* make this a config parameter */ ++ if (state->version == SOC7090) { ++ dib7090_set_output_mode(fe, state->cfg.output_mode); ++ if (state->cfg.enMpegOutput == 0) { ++ dib7090_setDibTxMux(state, MPEG_ON_DIBTX); ++ dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ } ++ } else ++ dib7000p_set_output_mode(state, state->cfg.output_mode); ++ ++ return ret; ++} ++ ++static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 lock = dib7000p_read_word(state, 509); ++ ++ *stat = 0; ++ ++ if (lock & 0x8000) ++ *stat |= FE_HAS_SIGNAL; ++ if (lock & 0x3000) ++ *stat |= FE_HAS_CARRIER; ++ if (lock & 0x0100) ++ *stat |= FE_HAS_VITERBI; ++ if (lock & 0x0010) ++ *stat |= FE_HAS_SYNC; ++ if ((lock & 0x0038) == 0x38) ++ *stat |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int dib7000p_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); ++ return 0; ++} ++ ++static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ *unc = dib7000p_read_word(state, 506); ++ return 0; ++} ++ ++static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 val = dib7000p_read_word(state, 394); ++ *strength = 65535 - val; ++ return 0; ++} ++ ++static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 val; ++ s32 signal_mant, signal_exp, noise_mant, noise_exp; ++ u32 result = 0; ++ ++ val = dib7000p_read_word(state, 479); ++ noise_mant = (val >> 4) & 0xff; ++ noise_exp = ((val & 0xf) << 2); ++ val = dib7000p_read_word(state, 480); ++ noise_exp += ((val >> 14) & 0x3); ++ if ((noise_exp & 0x20) != 0) ++ noise_exp -= 0x40; ++ ++ signal_mant = (val >> 6) & 0xFF; ++ signal_exp = (val & 0x3F); ++ if ((signal_exp & 0x20) != 0) ++ signal_exp -= 0x40; ++ ++ if (signal_mant != 0) ++ result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); ++ else ++ result = intlog10(2) * 10 * signal_exp - 100; ++ ++ if (noise_mant != 0) ++ result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); ++ else ++ result -= intlog10(2) * 10 * noise_exp - 100; ++ ++ *snr = result / ((1 << 24) / 10); ++ return 0; ++} ++ ++static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void dib7000p_release(struct dvb_frontend *demod) ++{ ++ struct dib7000p_state *st = demod->demodulator_priv; ++ dibx000_exit_i2c_master(&st->i2c_master); ++ i2c_del_adapter(&st->dib7090_tuner_adap); ++ kfree(st); ++} ++ ++int dib7000pc_detection(struct i2c_adapter *i2c_adap) ++{ ++ u8 *tx, *rx; ++ struct i2c_msg msg[2] = { ++ {.addr = 18 >> 1, .flags = 0, .len = 2}, ++ {.addr = 18 >> 1, .flags = I2C_M_RD, .len = 2}, ++ }; ++ int ret = 0; ++ ++ tx = kzalloc(2*sizeof(u8), GFP_KERNEL); ++ if (!tx) ++ return -ENOMEM; ++ rx = kzalloc(2*sizeof(u8), GFP_KERNEL); ++ if (!rx) { ++ ret = -ENOMEM; ++ goto rx_memory_error; ++ } ++ ++ msg[0].buf = tx; ++ msg[1].buf = rx; ++ ++ tx[0] = 0x03; ++ tx[1] = 0x00; ++ ++ if (i2c_transfer(i2c_adap, msg, 2) == 2) ++ if (rx[0] == 0x01 && rx[1] == 0xb3) { ++ dprintk("-D- DiB7000PC detected"); ++ return 1; ++ } ++ ++ msg[0].addr = msg[1].addr = 0x40; ++ ++ if (i2c_transfer(i2c_adap, msg, 2) == 2) ++ if (rx[0] == 0x01 && rx[1] == 0xb3) { ++ dprintk("-D- DiB7000PC detected"); ++ return 1; ++ } ++ ++ dprintk("-D- DiB7000PC not detected"); ++ ++ kfree(rx); ++rx_memory_error: ++ kfree(tx); ++ return ret; ++} ++EXPORT_SYMBOL(dib7000pc_detection); ++ ++struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) ++{ ++ struct dib7000p_state *st = demod->demodulator_priv; ++ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); ++} ++EXPORT_SYMBOL(dib7000p_get_i2c_master); ++ ++int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 val = dib7000p_read_word(state, 235) & 0xffef; ++ val |= (onoff & 0x1) << 4; ++ dprintk("PID filter enabled %d", onoff); ++ return dib7000p_write_word(state, 235, val); ++} ++EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); ++ ++int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); ++ return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); ++} ++EXPORT_SYMBOL(dib7000p_pid_filter); ++ ++int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) ++{ ++ struct dib7000p_state *dpst; ++ int k = 0; ++ u8 new_addr = 0; ++ ++ dpst = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); ++ if (!dpst) ++ return -ENOMEM; ++ ++ dpst->i2c_adap = i2c; ++ mutex_init(&dpst->i2c_buffer_lock); ++ ++ for (k = no_of_demods - 1; k >= 0; k--) { ++ dpst->cfg = cfg[k]; ++ ++ /* designated i2c address */ ++ if (cfg[k].default_i2c_addr != 0) ++ new_addr = cfg[k].default_i2c_addr + (k << 1); ++ else ++ new_addr = (0x40 + k) << 1; ++ dpst->i2c_addr = new_addr; ++ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ ++ if (dib7000p_identify(dpst) != 0) { ++ dpst->i2c_addr = default_addr; ++ dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ ++ if (dib7000p_identify(dpst) != 0) { ++ dprintk("DiB7000P #%d: not identified\n", k); ++ kfree(dpst); ++ return -EIO; ++ } ++ } ++ ++ /* start diversity to pull_down div_str - just for i2c-enumeration */ ++ dib7000p_set_output_mode(dpst, OUTMODE_DIVERSITY); ++ ++ /* set new i2c address and force divstart */ ++ dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2); ++ ++ dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); ++ } ++ ++ for (k = 0; k < no_of_demods; k++) { ++ dpst->cfg = cfg[k]; ++ if (cfg[k].default_i2c_addr != 0) ++ dpst->i2c_addr = (cfg[k].default_i2c_addr + k) << 1; ++ else ++ dpst->i2c_addr = (0x40 + k) << 1; ++ ++ // unforce divstr ++ dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2); ++ ++ /* deactivate div - it was just for i2c-enumeration */ ++ dib7000p_set_output_mode(dpst, OUTMODE_HIGH_Z); ++ } ++ ++ kfree(dpst); ++ return 0; ++} ++EXPORT_SYMBOL(dib7000p_i2c_enumeration); ++ ++static const s32 lut_1000ln_mant[] = { ++ 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600 ++}; ++ ++static s32 dib7000p_get_adc_power(struct dvb_frontend *fe) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u32 tmp_val = 0, exp = 0, mant = 0; ++ s32 pow_i; ++ u16 buf[2]; ++ u8 ix = 0; ++ ++ buf[0] = dib7000p_read_word(state, 0x184); ++ buf[1] = dib7000p_read_word(state, 0x185); ++ pow_i = (buf[0] << 16) | buf[1]; ++ dprintk("raw pow_i = %d", pow_i); ++ ++ tmp_val = pow_i; ++ while (tmp_val >>= 1) ++ exp++; ++ ++ mant = (pow_i * 1000 / (1 << exp)); ++ dprintk(" mant = %d exp = %d", mant / 1000, exp); ++ ++ ix = (u8) ((mant - 1000) / 100); /* index of the LUT */ ++ dprintk(" ix = %d", ix); ++ ++ pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908); ++ pow_i = (pow_i << 8) / 1000; ++ dprintk(" pow_i = %d", pow_i); ++ ++ return pow_i; ++} ++ ++static int map_addr_to_serpar_number(struct i2c_msg *msg) ++{ ++ if ((msg->buf[0] <= 15)) ++ msg->buf[0] -= 1; ++ else if (msg->buf[0] == 17) ++ msg->buf[0] = 15; ++ else if (msg->buf[0] == 16) ++ msg->buf[0] = 17; ++ else if (msg->buf[0] == 19) ++ msg->buf[0] = 16; ++ else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) ++ msg->buf[0] -= 3; ++ else if (msg->buf[0] == 28) ++ msg->buf[0] = 23; ++ else ++ return -EINVAL; ++ return 0; ++} ++ ++static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); ++ u8 n_overflow = 1; ++ u16 i = 1000; ++ u16 serpar_num = msg[0].buf[0]; ++ ++ while (n_overflow == 1 && i) { ++ n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; ++ i--; ++ if (i == 0) ++ dprintk("Tuner ITF: write busy (overflow)"); ++ } ++ dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); ++ dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); ++ ++ return num; ++} ++ ++static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); ++ u8 n_overflow = 1, n_empty = 1; ++ u16 i = 1000; ++ u16 serpar_num = msg[0].buf[0]; ++ u16 read_word; ++ ++ while (n_overflow == 1 && i) { ++ n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: read busy (overflow)"); ++ } ++ dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f)); ++ ++ i = 1000; ++ while (n_empty == 1 && i) { ++ n_empty = dib7000p_read_word(state, 1984) & 0x1; ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: read busy (empty)"); ++ } ++ read_word = dib7000p_read_word(state, 1987); ++ msg[1].buf[0] = (read_word >> 8) & 0xff; ++ msg[1].buf[1] = (read_word) & 0xff; ++ ++ return num; ++} ++ ++static int w7090p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ if (map_addr_to_serpar_number(&msg[0]) == 0) { /* else = Tuner regs to ignore : DIG_CFG, CTRL_RF_LT, PLL_CFG, PWM1_REG, ADCCLK, DIG_CFG_3; SLEEP_EN... */ ++ if (num == 1) { /* write */ ++ return w7090p_tuner_write_serpar(i2c_adap, msg, 1); ++ } else { /* read */ ++ return w7090p_tuner_read_serpar(i2c_adap, msg, 2); ++ } ++ } ++ return num; ++} ++ ++static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num, u16 apb_address) ++{ ++ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); ++ u16 word; ++ ++ if (num == 1) { /* write */ ++ dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); ++ } else { ++ word = dib7000p_read_word(state, apb_address); ++ msg[1].buf[0] = (word >> 8) & 0xff; ++ msg[1].buf[1] = (word) & 0xff; ++ } ++ ++ return num; ++} ++ ++static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); ++ ++ u16 apb_address = 0, word; ++ int i = 0; ++ switch (msg[0].buf[0]) { ++ case 0x12: ++ apb_address = 1920; ++ break; ++ case 0x14: ++ apb_address = 1921; ++ break; ++ case 0x24: ++ apb_address = 1922; ++ break; ++ case 0x1a: ++ apb_address = 1923; ++ break; ++ case 0x22: ++ apb_address = 1924; ++ break; ++ case 0x33: ++ apb_address = 1926; ++ break; ++ case 0x34: ++ apb_address = 1927; ++ break; ++ case 0x35: ++ apb_address = 1928; ++ break; ++ case 0x36: ++ apb_address = 1929; ++ break; ++ case 0x37: ++ apb_address = 1930; ++ break; ++ case 0x38: ++ apb_address = 1931; ++ break; ++ case 0x39: ++ apb_address = 1932; ++ break; ++ case 0x2a: ++ apb_address = 1935; ++ break; ++ case 0x2b: ++ apb_address = 1936; ++ break; ++ case 0x2c: ++ apb_address = 1937; ++ break; ++ case 0x2d: ++ apb_address = 1938; ++ break; ++ case 0x2e: ++ apb_address = 1939; ++ break; ++ case 0x2f: ++ apb_address = 1940; ++ break; ++ case 0x30: ++ apb_address = 1941; ++ break; ++ case 0x31: ++ apb_address = 1942; ++ break; ++ case 0x32: ++ apb_address = 1943; ++ break; ++ case 0x3e: ++ apb_address = 1944; ++ break; ++ case 0x3f: ++ apb_address = 1945; ++ break; ++ case 0x40: ++ apb_address = 1948; ++ break; ++ case 0x25: ++ apb_address = 914; ++ break; ++ case 0x26: ++ apb_address = 915; ++ break; ++ case 0x27: ++ apb_address = 917; ++ break; ++ case 0x28: ++ apb_address = 916; ++ break; ++ case 0x1d: ++ i = ((dib7000p_read_word(state, 72) >> 12) & 0x3); ++ word = dib7000p_read_word(state, 384 + i); ++ msg[1].buf[0] = (word >> 8) & 0xff; ++ msg[1].buf[1] = (word) & 0xff; ++ return num; ++ case 0x1f: ++ if (num == 1) { /* write */ ++ word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]); ++ word &= 0x3; ++ word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); ++ dib7000p_write_word(state, 72, word); /* Set the proper input */ ++ return num; ++ } ++ } ++ ++ if (apb_address != 0) /* R/W acces via APB */ ++ return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address); ++ else /* R/W access via SERPAR */ ++ return w7090p_tuner_rw_serpar(i2c_adap, msg, num); ++ ++ return 0; ++} ++ ++static u32 dib7000p_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm dib7090_tuner_xfer_algo = { ++ .master_xfer = dib7090_tuner_xfer, ++ .functionality = dib7000p_i2c_func, ++}; ++ ++struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) ++{ ++ struct dib7000p_state *st = fe->demodulator_priv; ++ return &st->dib7090_tuner_adap; ++} ++EXPORT_SYMBOL(dib7090_get_i2c_tuner); ++ ++static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive) ++{ ++ u16 reg; ++ ++ /* drive host bus 2, 3, 4 */ ++ reg = dib7000p_read_word(state, 1798) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive << 12) | (drive << 6) | drive; ++ dib7000p_write_word(state, 1798, reg); ++ ++ /* drive host bus 5,6 */ ++ reg = dib7000p_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); ++ reg |= (drive << 8) | (drive << 2); ++ dib7000p_write_word(state, 1799, reg); ++ ++ /* drive host bus 7, 8, 9 */ ++ reg = dib7000p_read_word(state, 1800) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive << 12) | (drive << 6) | drive; ++ dib7000p_write_word(state, 1800, reg); ++ ++ /* drive host bus 10, 11 */ ++ reg = dib7000p_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); ++ reg |= (drive << 8) | (drive << 2); ++ dib7000p_write_word(state, 1801, reg); ++ ++ /* drive host bus 12, 13, 14 */ ++ reg = dib7000p_read_word(state, 1802) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive << 12) | (drive << 6) | drive; ++ dib7000p_write_word(state, 1802, reg); ++ ++ return 0; ++} ++ ++static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 syncSize) ++{ ++ u32 quantif = 3; ++ u32 nom = (insertExtSynchro * P_Kin + syncSize); ++ u32 denom = P_Kout; ++ u32 syncFreq = ((nom << quantif) / denom); ++ ++ if ((syncFreq & ((1 << quantif) - 1)) != 0) ++ syncFreq = (syncFreq >> quantif) + 1; ++ else ++ syncFreq = (syncFreq >> quantif); ++ ++ if (syncFreq != 0) ++ syncFreq = syncFreq - 1; ++ ++ return syncFreq; ++} ++ ++static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize) ++{ ++ dprintk("Configure DibStream Tx"); ++ ++ dib7000p_write_word(state, 1615, 1); ++ dib7000p_write_word(state, 1603, P_Kin); ++ dib7000p_write_word(state, 1605, P_Kout); ++ dib7000p_write_word(state, 1606, insertExtSynchro); ++ dib7000p_write_word(state, 1608, synchroMode); ++ dib7000p_write_word(state, 1609, (syncWord >> 16) & 0xffff); ++ dib7000p_write_word(state, 1610, syncWord & 0xffff); ++ dib7000p_write_word(state, 1612, syncSize); ++ dib7000p_write_word(state, 1615, 0); ++ ++ return 0; ++} ++ ++static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, u32 syncWord, u32 syncSize, ++ u32 dataOutRate) ++{ ++ u32 syncFreq; ++ ++ dprintk("Configure DibStream Rx"); ++ if ((P_Kin != 0) && (P_Kout != 0)) { ++ syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize); ++ dib7000p_write_word(state, 1542, syncFreq); ++ } ++ dib7000p_write_word(state, 1554, 1); ++ dib7000p_write_word(state, 1536, P_Kin); ++ dib7000p_write_word(state, 1537, P_Kout); ++ dib7000p_write_word(state, 1539, synchroMode); ++ dib7000p_write_word(state, 1540, (syncWord >> 16) & 0xffff); ++ dib7000p_write_word(state, 1541, syncWord & 0xffff); ++ dib7000p_write_word(state, 1543, syncSize); ++ dib7000p_write_word(state, 1544, dataOutRate); ++ dib7000p_write_word(state, 1554, 0); ++ ++ return 0; ++} ++ ++static void dib7090_enMpegMux(struct dib7000p_state *state, int onoff) ++{ ++ u16 reg_1287 = dib7000p_read_word(state, 1287); ++ ++ switch (onoff) { ++ case 1: ++ reg_1287 &= ~(1<<7); ++ break; ++ case 0: ++ reg_1287 |= (1<<7); ++ break; ++ } ++ ++ dib7000p_write_word(state, 1287, reg_1287); ++} ++ ++static void dib7090_configMpegMux(struct dib7000p_state *state, ++ u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) ++{ ++ dprintk("Enable Mpeg mux"); ++ ++ dib7090_enMpegMux(state, 0); ++ ++ /* If the input mode is MPEG do not divide the serial clock */ ++ if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) ++ enSerialClkDiv2 = 0; ++ ++ dib7000p_write_word(state, 1287, ((pulseWidth & 0x1f) << 2) ++ | ((enSerialMode & 0x1) << 1) ++ | (enSerialClkDiv2 & 0x1)); ++ ++ dib7090_enMpegMux(state, 1); ++} ++ ++static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode) ++{ ++ u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 7); ++ ++ switch (mode) { ++ case MPEG_ON_DIBTX: ++ dprintk("SET MPEG ON DIBSTREAM TX"); ++ dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); ++ reg_1288 |= (1<<9); ++ break; ++ case DIV_ON_DIBTX: ++ dprintk("SET DIV_OUT ON DIBSTREAM TX"); ++ dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); ++ reg_1288 |= (1<<8); ++ break; ++ case ADC_ON_DIBTX: ++ dprintk("SET ADC_OUT ON DIBSTREAM TX"); ++ dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); ++ reg_1288 |= (1<<7); ++ break; ++ default: ++ break; ++ } ++ dib7000p_write_word(state, 1288, reg_1288); ++} ++ ++static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode) ++{ ++ u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 4); ++ ++ switch (mode) { ++ case DEMOUT_ON_HOSTBUS: ++ dprintk("SET DEM OUT OLD INTERF ON HOST BUS"); ++ dib7090_enMpegMux(state, 0); ++ reg_1288 |= (1<<6); ++ break; ++ case DIBTX_ON_HOSTBUS: ++ dprintk("SET DIBSTREAM TX ON HOST BUS"); ++ dib7090_enMpegMux(state, 0); ++ reg_1288 |= (1<<5); ++ break; ++ case MPEG_ON_HOSTBUS: ++ dprintk("SET MPEG MUX ON HOST BUS"); ++ reg_1288 |= (1<<4); ++ break; ++ default: ++ break; ++ } ++ dib7000p_write_word(state, 1288, reg_1288); ++} ++ ++int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 reg_1287; ++ ++ switch (onoff) { ++ case 0: /* only use the internal way - not the diversity input */ ++ dprintk("%s mode OFF : by default Enable Mpeg INPUT", __func__); ++ dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); ++ ++ /* Do not divide the serial clock of MPEG MUX */ ++ /* in SERIAL MODE in case input mode MPEG is used */ ++ reg_1287 = dib7000p_read_word(state, 1287); ++ /* enSerialClkDiv2 == 1 ? */ ++ if ((reg_1287 & 0x1) == 1) { ++ /* force enSerialClkDiv2 = 0 */ ++ reg_1287 &= ~0x1; ++ dib7000p_write_word(state, 1287, reg_1287); ++ } ++ state->input_mode_mpeg = 1; ++ break; ++ case 1: /* both ways */ ++ case 2: /* only the diversity input */ ++ dprintk("%s ON : Enable diversity INPUT", __func__); ++ dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); ++ state->input_mode_mpeg = 0; ++ break; ++ } ++ ++ dib7000p_set_diversity_in(&state->demod, onoff); ++ return 0; ++} ++ ++static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ ++ u16 outreg, smo_mode, fifo_threshold; ++ u8 prefer_mpeg_mux_use = 1; ++ int ret = 0; ++ ++ dib7090_host_bus_drive(state, 1); ++ ++ fifo_threshold = 1792; ++ smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); ++ outreg = dib7000p_read_word(state, 1286) & ~((1 << 10) | (0x7 << 6) | (1 << 1)); ++ ++ switch (mode) { ++ case OUTMODE_HIGH_Z: ++ outreg = 0; ++ break; ++ ++ case OUTMODE_MPEG2_SERIAL: ++ if (prefer_mpeg_mux_use) { ++ dprintk("setting output mode TS_SERIAL using Mpeg Mux"); ++ dib7090_configMpegMux(state, 3, 1, 1); ++ dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); ++ } else {/* Use Smooth block */ ++ dprintk("setting output mode TS_SERIAL using Smooth bloc"); ++ dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (2<<6) | (0 << 1); ++ } ++ break; ++ ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ if (prefer_mpeg_mux_use) { ++ dprintk("setting output mode TS_PARALLEL_GATED using Mpeg Mux"); ++ dib7090_configMpegMux(state, 2, 0, 0); ++ dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); ++ } else { /* Use Smooth block */ ++ dprintk("setting output mode TS_PARALLEL_GATED using Smooth block"); ++ dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (0<<6); ++ } ++ break; ++ ++ case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ ++ dprintk("setting output mode TS_PARALLEL_CONT using Smooth block"); ++ dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (1<<6); ++ break; ++ ++ case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ ++ dprintk("setting output mode TS_FIFO using Smooth block"); ++ dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (5<<6); ++ smo_mode |= (3 << 1); ++ fifo_threshold = 512; ++ break; ++ ++ case OUTMODE_DIVERSITY: ++ dprintk("setting output mode MODE_DIVERSITY"); ++ dib7090_setDibTxMux(state, DIV_ON_DIBTX); ++ dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ break; ++ ++ case OUTMODE_ANALOG_ADC: ++ dprintk("setting output mode MODE_ANALOG_ADC"); ++ dib7090_setDibTxMux(state, ADC_ON_DIBTX); ++ dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ break; ++ } ++ if (mode != OUTMODE_HIGH_Z) ++ outreg |= (1 << 10); ++ ++ if (state->cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5); ++ ++ ret |= dib7000p_write_word(state, 235, smo_mode); ++ ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ ++ ret |= dib7000p_write_word(state, 1286, outreg); ++ ++ return ret; ++} ++ ++int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 en_cur_state; ++ ++ dprintk("sleep dib7090: %d", onoff); ++ ++ en_cur_state = dib7000p_read_word(state, 1922); ++ ++ if (en_cur_state > 0xff) ++ state->tuner_enable = en_cur_state; ++ ++ if (onoff) ++ en_cur_state &= 0x00ff; ++ else { ++ if (state->tuner_enable != 0) ++ en_cur_state = state->tuner_enable; ++ } ++ ++ dib7000p_write_word(state, 1922, en_cur_state); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib7090_tuner_sleep); ++ ++int dib7090_get_adc_power(struct dvb_frontend *fe) ++{ ++ return dib7000p_get_adc_power(fe); ++} ++EXPORT_SYMBOL(dib7090_get_adc_power); ++ ++int dib7090_slave_reset(struct dvb_frontend *fe) ++{ ++ struct dib7000p_state *state = fe->demodulator_priv; ++ u16 reg; ++ ++ reg = dib7000p_read_word(state, 1794); ++ dib7000p_write_word(state, 1794, reg | (4 << 12)); ++ ++ dib7000p_write_word(state, 1032, 0xffff); ++ return 0; ++} ++EXPORT_SYMBOL(dib7090_slave_reset); ++ ++static struct dvb_frontend_ops dib7000p_ops; ++struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) ++{ ++ struct dvb_frontend *demod; ++ struct dib7000p_state *st; ++ st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ ++ memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config)); ++ st->i2c_adap = i2c_adap; ++ st->i2c_addr = i2c_addr; ++ st->gpio_val = cfg->gpio_val; ++ st->gpio_dir = cfg->gpio_dir; ++ ++ /* Ensure the output mode remains at the previous default if it's ++ * not specifically set by the caller. ++ */ ++ if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) ++ st->cfg.output_mode = OUTMODE_MPEG2_FIFO; ++ ++ demod = &st->demod; ++ demod->demodulator_priv = st; ++ memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); ++ mutex_init(&st->i2c_buffer_lock); ++ ++ dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ ++ ++ if (dib7000p_identify(st) != 0) ++ goto error; ++ ++ st->version = dib7000p_read_word(st, 897); ++ ++ /* FIXME: make sure the dev.parent field is initialized, or else ++ request_firmware() will hit an OOPS (this should be moved somewhere ++ more common) */ ++ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; ++ ++ dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); ++ ++ /* init 7090 tuner adapter */ ++ strncpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", sizeof(st->dib7090_tuner_adap.name)); ++ st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo; ++ st->dib7090_tuner_adap.algo_data = NULL; ++ st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent; ++ i2c_set_adapdata(&st->dib7090_tuner_adap, st); ++ i2c_add_adapter(&st->dib7090_tuner_adap); ++ ++ dib7000p_demod_reset(st); ++ ++ if (st->version == SOC7090) { ++ dib7090_set_output_mode(demod, st->cfg.output_mode); ++ dib7090_set_diversity_in(demod, 0); ++ } ++ ++ return demod; ++ ++error: ++ kfree(st); ++ return NULL; ++} ++EXPORT_SYMBOL(dib7000p_attach); ++ ++static struct dvb_frontend_ops dib7000p_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DiBcom 7000PC", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib7000p_release, ++ ++ .init = dib7000p_wakeup, ++ .sleep = dib7000p_sleep, ++ ++ .set_frontend = dib7000p_set_frontend, ++ .get_tune_settings = dib7000p_fe_get_tune_settings, ++ .get_frontend = dib7000p_get_frontend, ++ ++ .read_status = dib7000p_read_status, ++ .read_ber = dib7000p_read_ber, ++ .read_signal_strength = dib7000p_read_signal_strength, ++ .read_snr = dib7000p_read_snr, ++ .read_ucblocks = dib7000p_read_unc_blocks, ++}; ++ ++MODULE_AUTHOR("Olivier Grenie "); ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib7000p.h b/drivers/media/dvb-frontends/dib7000p.h +new file mode 100644 +index 0000000..b61b03a +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib7000p.h +@@ -0,0 +1,158 @@ ++#ifndef DIB7000P_H ++#define DIB7000P_H ++ ++#include "dibx000_common.h" ++ ++struct dib7000p_config { ++ u8 output_mpeg2_in_188_bytes; ++ u8 hostbus_diversity; ++ u8 tuner_is_baseband; ++ int (*update_lna) (struct dvb_frontend *, u16 agc_global); ++ ++ u8 agc_config_count; ++ struct dibx000_agc_config *agc; ++ struct dibx000_bandwidth_config *bw; ++ ++#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff ++ u16 gpio_dir; ++#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000 ++ u16 gpio_val; ++#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12) ++#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) ++#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) ++#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf) ++#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff ++ u16 gpio_pwm_pos; ++ ++ u16 pwm_freq_div; ++ ++ u8 quartz_direct; ++ ++ u8 spur_protect; ++ ++ int (*agc_control) (struct dvb_frontend *, u8 before); ++ ++ u8 output_mode; ++ u8 disable_sample_and_hold:1; ++ ++ u8 enable_current_mirror:1; ++ u16 diversity_delay; ++ ++ u8 default_i2c_addr; ++ u8 enMpegOutput:1; ++}; ++ ++#define DEFAULT_DIB7000P_I2C_ADDRESS 18 ++ ++#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \ ++ defined(MODULE)) ++extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); ++extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); ++extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); ++extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); ++extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); ++extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); ++extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); ++extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); ++extern int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); ++extern u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf); ++extern int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff); ++extern int dib7090_get_adc_power(struct dvb_frontend *fe); ++extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); ++extern int dib7090_slave_reset(struct dvb_frontend *fe); ++extern int dib7000p_get_agc_values(struct dvb_frontend *fe, ++ u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); ++#else ++static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7090_get_adc_power(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib7090_slave_reset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib7000p_get_agc_values(struct dvb_frontend *fe, ++ u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c +new file mode 100644 +index 0000000..1f3bcb5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib8000.c +@@ -0,0 +1,3560 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T). ++ * ++ * Copyright (C) 2009 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#include ++#include ++#include ++#include ++ ++#include "dvb_math.h" ++ ++#include "dvb_frontend.h" ++ ++#include "dib8000.h" ++ ++#define LAYER_ALL -1 ++#define LAYER_A 1 ++#define LAYER_B 2 ++#define LAYER_C 3 ++ ++#define FE_CALLBACK_TIME_NEVER 0xffffffff ++#define MAX_NUMBER_OF_FRONTENDS 6 ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) ++ ++#define FE_STATUS_TUNE_FAILED 0 ++ ++struct i2c_device { ++ struct i2c_adapter *adap; ++ u8 addr; ++ u8 *i2c_write_buffer; ++ u8 *i2c_read_buffer; ++ struct mutex *i2c_buffer_lock; ++}; ++ ++struct dib8000_state { ++ struct dib8000_config cfg; ++ ++ struct i2c_device i2c; ++ ++ struct dibx000_i2c_master i2c_master; ++ ++ u16 wbd_ref; ++ ++ u8 current_band; ++ u32 current_bandwidth; ++ struct dibx000_agc_config *current_agc; ++ u32 timf; ++ u32 timf_default; ++ ++ u8 div_force_off:1; ++ u8 div_state:1; ++ u16 div_sync_wait; ++ ++ u8 agc_state; ++ u8 differential_constellation; ++ u8 diversity_onoff; ++ ++ s16 ber_monitored_layer; ++ u16 gpio_dir; ++ u16 gpio_val; ++ ++ u16 revision; ++ u8 isdbt_cfg_loaded; ++ enum frontend_tune_state tune_state; ++ u32 status; ++ ++ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[4]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++ u8 input_mode_mpeg; ++ ++ u16 tuner_enable; ++ struct i2c_adapter dib8096p_tuner_adap; ++}; ++ ++enum dib8000_power_mode { ++ DIB8000_POWER_ALL = 0, ++ DIB8000_POWER_INTERFACE_ONLY, ++}; ++ ++static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) ++{ ++ u16 ret; ++ struct i2c_msg msg[2] = { ++ {.addr = i2c->addr >> 1, .flags = 0, .len = 2}, ++ {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2}, ++ }; ++ ++ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ msg[0].buf = i2c->i2c_write_buffer; ++ msg[0].buf[0] = reg >> 8; ++ msg[0].buf[1] = reg & 0xff; ++ msg[1].buf = i2c->i2c_read_buffer; ++ ++ if (i2c_transfer(i2c->adap, msg, 2) != 2) ++ dprintk("i2c read error on %d", reg); ++ ++ ret = (msg[1].buf[0] << 8) | msg[1].buf[1]; ++ mutex_unlock(i2c->i2c_buffer_lock); ++ return ret; ++} ++ ++static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ state->i2c_write_buffer[0] = reg >> 8; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c.addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 2; ++ state->msg[1].addr = state->i2c.addr >> 1; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = state->i2c_read_buffer; ++ state->msg[1].len = 2; ++ ++ if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2) ++ dprintk("i2c read error on %d", reg); ++ ++ ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; ++ mutex_unlock(&state->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static u32 dib8000_read32(struct dib8000_state *state, u16 reg) ++{ ++ u16 rw[2]; ++ ++ rw[0] = dib8000_read_word(state, reg + 0); ++ rw[1] = dib8000_read_word(state, reg + 1); ++ ++ return ((rw[0] << 16) | (rw[1])); ++} ++ ++static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) ++{ ++ struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4}; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ msg.buf = i2c->i2c_write_buffer; ++ msg.buf[0] = (reg >> 8) & 0xff; ++ msg.buf[1] = reg & 0xff; ++ msg.buf[2] = (val >> 8) & 0xff; ++ msg.buf[3] = val & 0xff; ++ ++ ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; ++ mutex_unlock(i2c->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ state->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ state->i2c_write_buffer[2] = (val >> 8) & 0xff; ++ state->i2c_write_buffer[3] = val & 0xff; ++ ++ memset(&state->msg[0], 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c.addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 4; ++ ++ ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? ++ -EREMOTEIO : 0); ++ mutex_unlock(&state->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static const s16 coeff_2k_sb_1seg_dqpsk[8] = { ++ (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, ++ (920 << 5) | 0x09 ++}; ++ ++static const s16 coeff_2k_sb_1seg[8] = { ++ (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f ++}; ++ ++static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { ++ (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, ++ (-931 << 5) | 0x0f ++}; ++ ++static const s16 coeff_2k_sb_3seg_0dqpsk[8] = { ++ (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, ++ (982 << 5) | 0x0c ++}; ++ ++static const s16 coeff_2k_sb_3seg_1dqpsk[8] = { ++ (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, ++ (-720 << 5) | 0x0d ++}; ++ ++static const s16 coeff_2k_sb_3seg[8] = { ++ (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, ++ (-610 << 5) | 0x0a ++}; ++ ++static const s16 coeff_4k_sb_1seg_dqpsk[8] = { ++ (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, ++ (-922 << 5) | 0x0d ++}; ++ ++static const s16 coeff_4k_sb_1seg[8] = { ++ (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, ++ (-655 << 5) | 0x0a ++}; ++ ++static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { ++ (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, ++ (-958 << 5) | 0x13 ++}; ++ ++static const s16 coeff_4k_sb_3seg_0dqpsk[8] = { ++ (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, ++ (-568 << 5) | 0x0f ++}; ++ ++static const s16 coeff_4k_sb_3seg_1dqpsk[8] = { ++ (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, ++ (-848 << 5) | 0x13 ++}; ++ ++static const s16 coeff_4k_sb_3seg[8] = { ++ (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, ++ (-869 << 5) | 0x13 ++}; ++ ++static const s16 coeff_8k_sb_1seg_dqpsk[8] = { ++ (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, ++ (-598 << 5) | 0x10 ++}; ++ ++static const s16 coeff_8k_sb_1seg[8] = { ++ (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, ++ (585 << 5) | 0x0f ++}; ++ ++static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { ++ (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, ++ (0 << 5) | 0x14 ++}; ++ ++static const s16 coeff_8k_sb_3seg_0dqpsk[8] = { ++ (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, ++ (-877 << 5) | 0x15 ++}; ++ ++static const s16 coeff_8k_sb_3seg_1dqpsk[8] = { ++ (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, ++ (-921 << 5) | 0x14 ++}; ++ ++static const s16 coeff_8k_sb_3seg[8] = { ++ (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, ++ (690 << 5) | 0x14 ++}; ++ ++static const s16 ana_fe_coeff_3seg[24] = { ++ 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 ++}; ++ ++static const s16 ana_fe_coeff_1seg[24] = { ++ 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 ++}; ++ ++static const s16 ana_fe_coeff_13seg[24] = { ++ 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 ++}; ++ ++static u16 fft_to_mode(struct dib8000_state *state) ++{ ++ u16 mode; ++ switch (state->fe[0]->dtv_property_cache.transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ mode = 1; ++ break; ++ case TRANSMISSION_MODE_4K: ++ mode = 2; ++ break; ++ default: ++ case TRANSMISSION_MODE_AUTO: ++ case TRANSMISSION_MODE_8K: ++ mode = 3; ++ break; ++ } ++ return mode; ++} ++ ++static void dib8000_set_acquisition_mode(struct dib8000_state *state) ++{ ++ u16 nud = dib8000_read_word(state, 298); ++ nud |= (1 << 3) | (1 << 0); ++ dprintk("acquisition mode activated"); ++ dib8000_write_word(state, 298, nud); ++} ++static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ ++ ++ outreg = 0; ++ fifo_threshold = 1792; ++ smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); ++ ++ dprintk("-I- Setting output mode for demod %p to %d", ++ &state->fe[0], mode); ++ ++ switch (mode) { ++ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock ++ outreg = (1 << 10); /* 0x0400 */ ++ break; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock ++ outreg = (1 << 10) | (1 << 6); /* 0x0440 */ ++ break; ++ case OUTMODE_MPEG2_SERIAL: // STBs with serial input ++ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ ++ break; ++ case OUTMODE_DIVERSITY: ++ if (state->cfg.hostbus_diversity) { ++ outreg = (1 << 10) | (4 << 6); /* 0x0500 */ ++ sram &= 0xfdff; ++ } else ++ sram |= 0x0c00; ++ break; ++ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding ++ smo_mode |= (3 << 1); ++ fifo_threshold = 512; ++ outreg = (1 << 10) | (5 << 6); ++ break; ++ case OUTMODE_HIGH_Z: // disable ++ outreg = 0; ++ break; ++ ++ case OUTMODE_ANALOG_ADC: ++ outreg = (1 << 10) | (3 << 6); ++ dib8000_set_acquisition_mode(state); ++ break; ++ ++ default: ++ dprintk("Unhandled output_mode passed to be set for demod %p", ++ &state->fe[0]); ++ return -EINVAL; ++ } ++ ++ if (state->cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5); ++ ++ dib8000_write_word(state, 299, smo_mode); ++ dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */ ++ dib8000_write_word(state, 1286, outreg); ++ dib8000_write_word(state, 1291, sram); ++ ++ return 0; ++} ++ ++static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; ++ ++ if (!state->differential_constellation) { ++ dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 ++ dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 ++ } else { ++ dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0 ++ dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0 ++ } ++ state->diversity_onoff = onoff; ++ ++ switch (onoff) { ++ case 0: /* only use the internal way - not the diversity input */ ++ dib8000_write_word(state, 270, 1); ++ dib8000_write_word(state, 271, 0); ++ break; ++ case 1: /* both ways */ ++ dib8000_write_word(state, 270, 6); ++ dib8000_write_word(state, 271, 6); ++ break; ++ case 2: /* only the diversity input */ ++ dib8000_write_word(state, 270, 0); ++ dib8000_write_word(state, 271, 1); ++ break; ++ } ++ return 0; ++} ++ ++static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode) ++{ ++ /* by default everything is going to be powered off */ ++ u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, ++ reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, ++ reg_1280; ++ ++ if (state->revision != 0x8090) ++ reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; ++ else ++ reg_1280 = (dib8000_read_word(state, 1280) & 0x707f) | 0x8f80; ++ ++ /* now, depending on the requested mode, we power on */ ++ switch (mode) { ++ /* power up everything in the demod */ ++ case DIB8000_POWER_ALL: ++ reg_774 = 0x0000; ++ reg_775 = 0x0000; ++ reg_776 = 0x0000; ++ reg_900 &= 0xfffc; ++ if (state->revision != 0x8090) ++ reg_1280 &= 0x00ff; ++ else ++ reg_1280 &= 0x707f; ++ break; ++ case DIB8000_POWER_INTERFACE_ONLY: ++ if (state->revision != 0x8090) ++ reg_1280 &= 0x00ff; ++ else ++ reg_1280 &= 0xfa7b; ++ break; ++ } ++ ++ dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x", reg_774, reg_775, reg_776, reg_900, reg_1280); ++ dib8000_write_word(state, 774, reg_774); ++ dib8000_write_word(state, 775, reg_775); ++ dib8000_write_word(state, 776, reg_776); ++ dib8000_write_word(state, 900, reg_900); ++ dib8000_write_word(state, 1280, reg_1280); ++} ++ ++static int dib8000_init_sdram(struct dib8000_state *state) ++{ ++ u16 reg = 0; ++ dprintk("Init sdram"); ++ ++ reg = dib8000_read_word(state, 274)&0xfff0; ++ /* P_dintlv_delay_ram = 7 because of MobileSdram */ ++ dib8000_write_word(state, 274, reg | 0x7); ++ ++ dib8000_write_word(state, 1803, (7<<2)); ++ ++ reg = dib8000_read_word(state, 1280); ++ /* force restart P_restart_sdram */ ++ dib8000_write_word(state, 1280, reg | (1<<2)); ++ ++ /* release restart P_restart_sdram */ ++ dib8000_write_word(state, 1280, reg); ++ ++ return 0; ++} ++ ++static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) ++{ ++ int ret = 0; ++ u16 reg, reg_907 = dib8000_read_word(state, 907); ++ u16 reg_908 = dib8000_read_word(state, 908); ++ ++ switch (no) { ++ case DIBX000_SLOW_ADC_ON: ++ if (state->revision != 0x8090) { ++ reg_908 |= (1 << 1) | (1 << 0); ++ ret |= dib8000_write_word(state, 908, reg_908); ++ reg_908 &= ~(1 << 1); ++ } else { ++ reg = dib8000_read_word(state, 1925); ++ /* en_slowAdc = 1 & reset_sladc = 1 */ ++ dib8000_write_word(state, 1925, reg | ++ (1<<4) | (1<<2)); ++ ++ /* read acces to make it works... strange ... */ ++ reg = dib8000_read_word(state, 1925); ++ msleep(20); ++ /* en_slowAdc = 1 & reset_sladc = 0 */ ++ dib8000_write_word(state, 1925, reg & ~(1<<4)); ++ ++ reg = dib8000_read_word(state, 921) & ~((0x3 << 14) ++ | (0x3 << 12)); ++ /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; ++ (Vin2 = Vcm) */ ++ dib8000_write_word(state, 921, reg | (1 << 14) ++ | (3 << 12)); ++ } ++ break; ++ ++ case DIBX000_SLOW_ADC_OFF: ++ if (state->revision == 0x8090) { ++ reg = dib8000_read_word(state, 1925); ++ /* reset_sladc = 1 en_slowAdc = 0 */ ++ dib8000_write_word(state, 1925, ++ (reg & ~(1<<2)) | (1<<4)); ++ } ++ reg_908 |= (1 << 1) | (1 << 0); ++ break; ++ ++ case DIBX000_ADC_ON: ++ reg_907 &= 0x0fff; ++ reg_908 &= 0x0003; ++ break; ++ ++ case DIBX000_ADC_OFF: // leave the VBG voltage on ++ reg_907 |= (1 << 14) | (1 << 13) | (1 << 12); ++ reg_908 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); ++ break; ++ ++ case DIBX000_VBG_ENABLE: ++ reg_907 &= ~(1 << 15); ++ break; ++ ++ case DIBX000_VBG_DISABLE: ++ reg_907 |= (1 << 15); ++ break; ++ ++ default: ++ break; ++ } ++ ++ ret |= dib8000_write_word(state, 907, reg_907); ++ ret |= dib8000_write_word(state, 908, reg_908); ++ ++ return ret; ++} ++ ++static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u32 timf; ++ ++ if (bw == 0) ++ bw = 6000; ++ ++ if (state->timf == 0) { ++ dprintk("using default timf"); ++ timf = state->timf_default; ++ } else { ++ dprintk("using updated timf"); ++ timf = state->timf; ++ } ++ ++ dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff)); ++ dib8000_write_word(state, 30, (u16) ((timf) & 0xffff)); ++ ++ return 0; ++} ++ ++static int dib8000_sad_calib(struct dib8000_state *state) ++{ ++ if (state->revision == 0x8090) { ++ dprintk("%s: the sad calibration is not needed for the dib8096P", ++ __func__); ++ return 0; ++ } ++ /* internal */ ++ dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); ++ dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 ++ ++ /* do the calibration */ ++ dib8000_write_word(state, 923, (1 << 0)); ++ dib8000_write_word(state, 923, (0 << 0)); ++ ++ msleep(1); ++ return 0; ++} ++ ++int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ if (value > 4095) ++ value = 4095; ++ state->wbd_ref = value; ++ return dib8000_write_word(state, 106, value); ++} ++ ++EXPORT_SYMBOL(dib8000_set_wbd_ref); ++static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) ++{ ++ dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); ++ if (state->revision != 0x8090) { ++ dib8000_write_word(state, 23, ++ (u16) (((bw->internal * 1000) >> 16) & 0xffff)); ++ dib8000_write_word(state, 24, ++ (u16) ((bw->internal * 1000) & 0xffff)); ++ } else { ++ dib8000_write_word(state, 23, (u16) (((bw->internal / 2 * 1000) >> 16) & 0xffff)); ++ dib8000_write_word(state, 24, ++ (u16) ((bw->internal / 2 * 1000) & 0xffff)); ++ } ++ dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff)); ++ dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff)); ++ dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003)); ++ ++ if (state->revision != 0x8090) ++ dib8000_write_word(state, 922, bw->sad_cfg); ++} ++ ++static void dib8000_reset_pll(struct dib8000_state *state) ++{ ++ const struct dibx000_bandwidth_config *pll = state->cfg.pll; ++ u16 clk_cfg1, reg; ++ ++ if (state->revision != 0x8090) { ++ dib8000_write_word(state, 901, ++ (pll->pll_prediv << 8) | (pll->pll_ratio << 0)); ++ ++ clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | ++ (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | ++ (1 << 3) | (pll->pll_range << 1) | ++ (pll->pll_reset << 0); ++ ++ dib8000_write_word(state, 902, clk_cfg1); ++ clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); ++ dib8000_write_word(state, 902, clk_cfg1); ++ ++ dprintk("clk_cfg1: 0x%04x", clk_cfg1); ++ ++ /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ ++ if (state->cfg.pll->ADClkSrc == 0) ++ dib8000_write_word(state, 904, ++ (0 << 15) | (0 << 12) | (0 << 10) | ++ (pll->modulo << 8) | ++ (pll->ADClkSrc << 7) | (0 << 1)); ++ else if (state->cfg.refclksel != 0) ++ dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | ++ ((state->cfg.refclksel & 0x3) << 10) | ++ (pll->modulo << 8) | ++ (pll->ADClkSrc << 7) | (0 << 1)); ++ else ++ dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | ++ (3 << 10) | (pll->modulo << 8) | ++ (pll->ADClkSrc << 7) | (0 << 1)); ++ } else { ++ dib8000_write_word(state, 1856, (!pll->pll_reset<<13) | ++ (pll->pll_range<<12) | (pll->pll_ratio<<6) | ++ (pll->pll_prediv)); ++ ++ reg = dib8000_read_word(state, 1857); ++ dib8000_write_word(state, 1857, reg|(!pll->pll_bypass<<15)); ++ ++ reg = dib8000_read_word(state, 1858); /* Force clk out pll /2 */ ++ dib8000_write_word(state, 1858, reg | 1); ++ ++ dib8000_write_word(state, 904, (pll->modulo << 8)); ++ } ++ ++ dib8000_reset_pll_common(state, pll); ++} ++ ++int dib8000_update_pll(struct dvb_frontend *fe, ++ struct dibx000_bandwidth_config *pll) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856); ++ u8 loopdiv, prediv; ++ u32 internal, xtal; ++ ++ /* get back old values */ ++ prediv = reg_1856 & 0x3f; ++ loopdiv = (reg_1856 >> 6) & 0x3f; ++ ++ if ((pll != NULL) && (pll->pll_prediv != prediv || ++ pll->pll_ratio != loopdiv)) { ++ dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); ++ reg_1856 &= 0xf000; ++ reg_1857 = dib8000_read_word(state, 1857); ++ /* disable PLL */ ++ dib8000_write_word(state, 1857, reg_1857 & ~(1 << 15)); ++ ++ dib8000_write_word(state, 1856, reg_1856 | ++ ((pll->pll_ratio & 0x3f) << 6) | ++ (pll->pll_prediv & 0x3f)); ++ ++ /* write new system clk into P_sec_len */ ++ internal = dib8000_read32(state, 23) / 1000; ++ dprintk("Old Internal = %d", internal); ++ xtal = 2 * (internal / loopdiv) * prediv; ++ internal = 1000 * (xtal/pll->pll_prediv) * pll->pll_ratio; ++ dprintk("Xtal = %d , New Fmem = %d New Fdemod = %d, New Fsampling = %d", xtal, internal/1000, internal/2000, internal/8000); ++ dprintk("New Internal = %d", internal); ++ ++ dib8000_write_word(state, 23, ++ (u16) (((internal / 2) >> 16) & 0xffff)); ++ dib8000_write_word(state, 24, (u16) ((internal / 2) & 0xffff)); ++ /* enable PLL */ ++ dib8000_write_word(state, 1857, reg_1857 | (1 << 15)); ++ ++ while (((dib8000_read_word(state, 1856)>>15)&0x1) != 1) ++ dprintk("Waiting for PLL to lock"); ++ ++ /* verify */ ++ reg_1856 = dib8000_read_word(state, 1856); ++ dprintk("PLL Updated with prediv = %d and loopdiv = %d", ++ reg_1856&0x3f, (reg_1856>>6)&0x3f); ++ ++ return 0; ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(dib8000_update_pll); ++ ++ ++static int dib8000_reset_gpio(struct dib8000_state *st) ++{ ++ /* reset the GPIOs */ ++ dib8000_write_word(st, 1029, st->cfg.gpio_dir); ++ dib8000_write_word(st, 1030, st->cfg.gpio_val); ++ ++ /* TODO 782 is P_gpio_od */ ++ ++ dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos); ++ ++ dib8000_write_word(st, 1037, st->cfg.pwm_freq_div); ++ return 0; ++} ++ ++static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) ++{ ++ st->cfg.gpio_dir = dib8000_read_word(st, 1029); ++ st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */ ++ st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */ ++ dib8000_write_word(st, 1029, st->cfg.gpio_dir); ++ ++ st->cfg.gpio_val = dib8000_read_word(st, 1030); ++ st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */ ++ st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */ ++ dib8000_write_word(st, 1030, st->cfg.gpio_val); ++ ++ dprintk("gpio dir: %x: gpio val: %x", st->cfg.gpio_dir, st->cfg.gpio_val); ++ ++ return 0; ++} ++ ++int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ return dib8000_cfg_gpio(state, num, dir, val); ++} ++ ++EXPORT_SYMBOL(dib8000_set_gpio); ++static const u16 dib8000_defaults[] = { ++ /* auto search configuration - lock0 by default waiting ++ * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ ++ 3, 7, ++ 0x0004, ++ 0x0400, ++ 0x0814, ++ ++ 12, 11, ++ 0x001b, ++ 0x7740, ++ 0x005b, ++ 0x8d80, ++ 0x01c9, ++ 0xc380, ++ 0x0000, ++ 0x0080, ++ 0x0000, ++ 0x0090, ++ 0x0001, ++ 0xd4c0, ++ ++ /*1, 32, ++ 0x6680 // P_corm_thres Lock algorithms configuration */ ++ ++ 11, 80, /* set ADC level to -16 */ ++ (1 << 13) - 825 - 117, ++ (1 << 13) - 837 - 117, ++ (1 << 13) - 811 - 117, ++ (1 << 13) - 766 - 117, ++ (1 << 13) - 737 - 117, ++ (1 << 13) - 693 - 117, ++ (1 << 13) - 648 - 117, ++ (1 << 13) - 619 - 117, ++ (1 << 13) - 575 - 117, ++ (1 << 13) - 531 - 117, ++ (1 << 13) - 501 - 117, ++ ++ 4, 108, ++ 0, ++ 0, ++ 0, ++ 0, ++ ++ 1, 175, ++ 0x0410, ++ 1, 179, ++ 8192, // P_fft_nb_to_cut ++ ++ 6, 181, ++ 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800 ++ 0x2800, ++ 0x2800, ++ 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800 ++ 0x2800, ++ 0x2800, ++ ++ 2, 193, ++ 0x0666, // P_pha3_thres ++ 0x0000, // P_cti_use_cpe, P_cti_use_prog ++ ++ 2, 205, ++ 0x200f, // P_cspu_regul, P_cspu_win_cut ++ 0x000f, // P_des_shift_work ++ ++ 5, 215, ++ 0x023d, // P_adp_regul_cnt ++ 0x00a4, // P_adp_noise_cnt ++ 0x00a4, // P_adp_regul_ext ++ 0x7ff0, // P_adp_noise_ext ++ 0x3ccc, // P_adp_fil ++ ++ 1, 230, ++ 0x0000, // P_2d_byp_ti_num ++ ++ 1, 263, ++ 0x800, //P_equal_thres_wgn ++ ++ 1, 268, ++ (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode ++ ++ 1, 270, ++ 0x0001, // P_div_lock0_wait ++ 1, 285, ++ 0x0020, //p_fec_ ++ 1, 299, ++ 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */ ++ ++ 1, 338, ++ (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 ++ (1 << 10) | ++ (0 << 9) | /* P_ctrl_pre_freq_inh=0 */ ++ (3 << 5) | /* P_ctrl_pre_freq_step=3 */ ++ (1 << 0), /* P_pre_freq_win_len=1 */ ++ ++ 0, ++}; ++ ++static u16 dib8000_identify(struct i2c_device *client) ++{ ++ u16 value; ++ ++ //because of glitches sometimes ++ value = dib8000_i2c_read16(client, 896); ++ ++ if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) { ++ dprintk("wrong Vendor ID (read=0x%x)", value); ++ return 0; ++ } ++ ++ value = dib8000_i2c_read16(client, 897); ++ if (value != 0x8000 && value != 0x8001 && ++ value != 0x8002 && value != 0x8090) { ++ dprintk("wrong Device ID (%x)", value); ++ return 0; ++ } ++ ++ switch (value) { ++ case 0x8000: ++ dprintk("found DiB8000A"); ++ break; ++ case 0x8001: ++ dprintk("found DiB8000B"); ++ break; ++ case 0x8002: ++ dprintk("found DiB8000C"); ++ break; ++ case 0x8090: ++ dprintk("found DiB8096P"); ++ break; ++ } ++ return value; ++} ++ ++static int dib8000_reset(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ if ((state->revision = dib8000_identify(&state->i2c)) == 0) ++ return -EINVAL; ++ ++ /* sram lead in, rdy */ ++ if (state->revision != 0x8090) ++ dib8000_write_word(state, 1287, 0x0003); ++ ++ if (state->revision == 0x8000) ++ dprintk("error : dib8000 MA not supported"); ++ ++ dibx000_reset_i2c_master(&state->i2c_master); ++ ++ dib8000_set_power_mode(state, DIB8000_POWER_ALL); ++ ++ /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ ++ dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); ++ ++ /* restart all parts */ ++ dib8000_write_word(state, 770, 0xffff); ++ dib8000_write_word(state, 771, 0xffff); ++ dib8000_write_word(state, 772, 0xfffc); ++ if (state->revision == 0x8090) ++ dib8000_write_word(state, 1280, 0x0045); ++ else ++ dib8000_write_word(state, 1280, 0x004d); ++ dib8000_write_word(state, 1281, 0x000c); ++ ++ dib8000_write_word(state, 770, 0x0000); ++ dib8000_write_word(state, 771, 0x0000); ++ dib8000_write_word(state, 772, 0x0000); ++ dib8000_write_word(state, 898, 0x0004); // sad ++ dib8000_write_word(state, 1280, 0x0000); ++ dib8000_write_word(state, 1281, 0x0000); ++ ++ /* drives */ ++ if (state->revision != 0x8090) { ++ if (state->cfg.drives) ++ dib8000_write_word(state, 906, state->cfg.drives); ++ else { ++ dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal."); ++ /* min drive SDRAM - not optimal - adjust */ ++ dib8000_write_word(state, 906, 0x2d98); ++ } ++ } ++ ++ dib8000_reset_pll(state); ++ if (state->revision != 0x8090) ++ dib8000_write_word(state, 898, 0x0004); ++ ++ if (dib8000_reset_gpio(state) != 0) ++ dprintk("GPIO reset was not successful."); ++ ++ if ((state->revision != 0x8090) && ++ (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0)) ++ dprintk("OUTPUT_MODE could not be resetted."); ++ ++ state->current_agc = NULL; ++ ++ // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... ++ /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */ ++ if (state->cfg.pll->ifreq == 0) ++ dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */ ++ else ++ dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */ ++ ++ { ++ u16 l = 0, r; ++ const u16 *n; ++ n = dib8000_defaults; ++ l = *n++; ++ while (l) { ++ r = *n++; ++ do { ++ dib8000_write_word(state, r, *n++); ++ r++; ++ } while (--l); ++ l = *n++; ++ } ++ } ++ if (state->revision != 0x8090) ++ dib8000_write_word(state, 903, (0 << 4) | 2); ++ state->isdbt_cfg_loaded = 0; ++ ++ //div_cfg override for special configs ++ if (state->cfg.div_cfg != 0) ++ dib8000_write_word(state, 903, state->cfg.div_cfg); ++ ++ /* unforce divstr regardless whether i2c enumeration was done or not */ ++ dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); ++ ++ dib8000_set_bandwidth(fe, 6000); ++ ++ dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); ++ if (state->revision != 0x8090) { ++ dib8000_sad_calib(state); ++ dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); ++ } ++ ++ dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); ++ ++ return 0; ++} ++ ++static void dib8000_restart_agc(struct dib8000_state *state) ++{ ++ // P_restart_iqc & P_restart_agc ++ dib8000_write_word(state, 770, 0x0a00); ++ dib8000_write_word(state, 770, 0x0000); ++} ++ ++static int dib8000_update_lna(struct dib8000_state *state) ++{ ++ u16 dyn_gain; ++ ++ if (state->cfg.update_lna) { ++ // read dyn_gain here (because it is demod-dependent and not tuner) ++ dyn_gain = dib8000_read_word(state, 390); ++ ++ if (state->cfg.update_lna(state->fe[0], dyn_gain)) { ++ dib8000_restart_agc(state); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) ++{ ++ struct dibx000_agc_config *agc = NULL; ++ int i; ++ u16 reg; ++ ++ if (state->current_band == band && state->current_agc != NULL) ++ return 0; ++ state->current_band = band; ++ ++ for (i = 0; i < state->cfg.agc_config_count; i++) ++ if (state->cfg.agc[i].band_caps & band) { ++ agc = &state->cfg.agc[i]; ++ break; ++ } ++ ++ if (agc == NULL) { ++ dprintk("no valid AGC configuration found for band 0x%02x", band); ++ return -EINVAL; ++ } ++ ++ state->current_agc = agc; ++ ++ /* AGC */ ++ dib8000_write_word(state, 76, agc->setup); ++ dib8000_write_word(state, 77, agc->inv_gain); ++ dib8000_write_word(state, 78, agc->time_stabiliz); ++ dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock); ++ ++ // Demod AGC loop configuration ++ dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp); ++ dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp); ++ ++ dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", ++ state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); ++ ++ /* AGC continued */ ++ if (state->wbd_ref != 0) ++ dib8000_write_word(state, 106, state->wbd_ref); ++ else // use default ++ dib8000_write_word(state, 106, agc->wbd_ref); ++ ++ if (state->revision == 0x8090) { ++ reg = dib8000_read_word(state, 922) & (0x3 << 2); ++ dib8000_write_word(state, 922, reg | (agc->wbd_sel << 2)); ++ } ++ ++ dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); ++ dib8000_write_word(state, 108, agc->agc1_max); ++ dib8000_write_word(state, 109, agc->agc1_min); ++ dib8000_write_word(state, 110, agc->agc2_max); ++ dib8000_write_word(state, 111, agc->agc2_min); ++ dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2); ++ dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); ++ dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); ++ dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); ++ ++ dib8000_write_word(state, 75, agc->agc1_pt3); ++ if (state->revision != 0x8090) ++ dib8000_write_word(state, 923, ++ (dib8000_read_word(state, 923) & 0xffe3) | ++ (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); ++ ++ return 0; ++} ++ ++void dib8000_pwm_agc_reset(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ dib8000_set_adc_state(state, DIBX000_ADC_ON); ++ dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))); ++} ++EXPORT_SYMBOL(dib8000_pwm_agc_reset); ++ ++static int dib8000_agc_soft_split(struct dib8000_state *state) ++{ ++ u16 agc, split_offset; ++ ++ if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) ++ return FE_CALLBACK_TIME_NEVER; ++ ++ // n_agc_global ++ agc = dib8000_read_word(state, 390); ++ ++ if (agc > state->current_agc->split.min_thres) ++ split_offset = state->current_agc->split.min; ++ else if (agc < state->current_agc->split.max_thres) ++ split_offset = state->current_agc->split.max; ++ else ++ split_offset = state->current_agc->split.max * ++ (agc - state->current_agc->split.min_thres) / ++ (state->current_agc->split.max_thres - state->current_agc->split.min_thres); ++ ++ dprintk("AGC split_offset: %d", split_offset); ++ ++ // P_agc_force_split and P_agc_split_offset ++ dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset); ++ return 5000; ++} ++ ++static int dib8000_agc_startup(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ enum frontend_tune_state *tune_state = &state->tune_state; ++ int ret = 0; ++ u16 reg, upd_demod_gain_period = 0x8000; ++ ++ switch (*tune_state) { ++ case CT_AGC_START: ++ // set power-up level: interf+analog+AGC ++ ++ if (state->revision != 0x8090) ++ dib8000_set_adc_state(state, DIBX000_ADC_ON); ++ else { ++ dib8000_set_power_mode(state, DIB8000_POWER_ALL); ++ ++ reg = dib8000_read_word(state, 1947)&0xff00; ++ dib8000_write_word(state, 1946, ++ upd_demod_gain_period & 0xFFFF); ++ /* bit 14 = enDemodGain */ ++ dib8000_write_word(state, 1947, reg | (1<<14) | ++ ((upd_demod_gain_period >> 16) & 0xFF)); ++ ++ /* enable adc i & q */ ++ reg = dib8000_read_word(state, 1920); ++ dib8000_write_word(state, 1920, (reg | 0x3) & ++ (~(1 << 7))); ++ } ++ ++ if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) { ++ *tune_state = CT_AGC_STOP; ++ state->status = FE_STATUS_TUNE_FAILED; ++ break; ++ } ++ ++ ret = 70; ++ *tune_state = CT_AGC_STEP_0; ++ break; ++ ++ case CT_AGC_STEP_0: ++ //AGC initialization ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(fe, 1); ++ ++ dib8000_restart_agc(state); ++ ++ // wait AGC rough lock time ++ ret = 50; ++ *tune_state = CT_AGC_STEP_1; ++ break; ++ ++ case CT_AGC_STEP_1: ++ // wait AGC accurate lock time ++ ret = 70; ++ ++ if (dib8000_update_lna(state)) ++ // wait only AGC rough lock time ++ ret = 50; ++ else ++ *tune_state = CT_AGC_STEP_2; ++ break; ++ ++ case CT_AGC_STEP_2: ++ dib8000_agc_soft_split(state); ++ ++ if (state->cfg.agc_control) ++ state->cfg.agc_control(fe, 0); ++ ++ *tune_state = CT_AGC_STOP; ++ break; ++ default: ++ ret = dib8000_agc_soft_split(state); ++ break; ++ } ++ return ret; ++ ++} ++ ++static void dib8096p_host_bus_drive(struct dib8000_state *state, u8 drive) ++{ ++ u16 reg; ++ ++ drive &= 0x7; ++ ++ /* drive host bus 2, 3, 4 */ ++ reg = dib8000_read_word(state, 1798) & ++ ~(0x7 | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive<<12) | (drive<<6) | drive; ++ dib8000_write_word(state, 1798, reg); ++ ++ /* drive host bus 5,6 */ ++ reg = dib8000_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); ++ reg |= (drive<<8) | (drive<<2); ++ dib8000_write_word(state, 1799, reg); ++ ++ /* drive host bus 7, 8, 9 */ ++ reg = dib8000_read_word(state, 1800) & ++ ~(0x7 | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive<<12) | (drive<<6) | drive; ++ dib8000_write_word(state, 1800, reg); ++ ++ /* drive host bus 10, 11 */ ++ reg = dib8000_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); ++ reg |= (drive<<8) | (drive<<2); ++ dib8000_write_word(state, 1801, reg); ++ ++ /* drive host bus 12, 13, 14 */ ++ reg = dib8000_read_word(state, 1802) & ++ ~(0x7 | (0x7 << 6) | (0x7 << 12)); ++ reg |= (drive<<12) | (drive<<6) | drive; ++ dib8000_write_word(state, 1802, reg); ++} ++ ++static u32 dib8096p_calcSyncFreq(u32 P_Kin, u32 P_Kout, ++ u32 insertExtSynchro, u32 syncSize) ++{ ++ u32 quantif = 3; ++ u32 nom = (insertExtSynchro * P_Kin+syncSize); ++ u32 denom = P_Kout; ++ u32 syncFreq = ((nom << quantif) / denom); ++ ++ if ((syncFreq & ((1 << quantif) - 1)) != 0) ++ syncFreq = (syncFreq >> quantif) + 1; ++ else ++ syncFreq = (syncFreq >> quantif); ++ ++ if (syncFreq != 0) ++ syncFreq = syncFreq - 1; ++ ++ return syncFreq; ++} ++ ++static void dib8096p_cfg_DibTx(struct dib8000_state *state, u32 P_Kin, ++ u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, ++ u32 syncWord, u32 syncSize) ++{ ++ dprintk("Configure DibStream Tx"); ++ ++ dib8000_write_word(state, 1615, 1); ++ dib8000_write_word(state, 1603, P_Kin); ++ dib8000_write_word(state, 1605, P_Kout); ++ dib8000_write_word(state, 1606, insertExtSynchro); ++ dib8000_write_word(state, 1608, synchroMode); ++ dib8000_write_word(state, 1609, (syncWord >> 16) & 0xffff); ++ dib8000_write_word(state, 1610, syncWord & 0xffff); ++ dib8000_write_word(state, 1612, syncSize); ++ dib8000_write_word(state, 1615, 0); ++} ++ ++static void dib8096p_cfg_DibRx(struct dib8000_state *state, u32 P_Kin, ++ u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, ++ u32 syncWord, u32 syncSize, u32 dataOutRate) ++{ ++ u32 syncFreq; ++ ++ dprintk("Configure DibStream Rx synchroMode = %d", synchroMode); ++ ++ if ((P_Kin != 0) && (P_Kout != 0)) { ++ syncFreq = dib8096p_calcSyncFreq(P_Kin, P_Kout, ++ insertExtSynchro, syncSize); ++ dib8000_write_word(state, 1542, syncFreq); ++ } ++ ++ dib8000_write_word(state, 1554, 1); ++ dib8000_write_word(state, 1536, P_Kin); ++ dib8000_write_word(state, 1537, P_Kout); ++ dib8000_write_word(state, 1539, synchroMode); ++ dib8000_write_word(state, 1540, (syncWord >> 16) & 0xffff); ++ dib8000_write_word(state, 1541, syncWord & 0xffff); ++ dib8000_write_word(state, 1543, syncSize); ++ dib8000_write_word(state, 1544, dataOutRate); ++ dib8000_write_word(state, 1554, 0); ++} ++ ++static void dib8096p_enMpegMux(struct dib8000_state *state, int onoff) ++{ ++ u16 reg_1287; ++ ++ reg_1287 = dib8000_read_word(state, 1287); ++ ++ switch (onoff) { ++ case 1: ++ reg_1287 &= ~(1 << 8); ++ break; ++ case 0: ++ reg_1287 |= (1 << 8); ++ break; ++ } ++ ++ dib8000_write_word(state, 1287, reg_1287); ++} ++ ++static void dib8096p_configMpegMux(struct dib8000_state *state, ++ u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) ++{ ++ u16 reg_1287; ++ ++ dprintk("Enable Mpeg mux"); ++ ++ dib8096p_enMpegMux(state, 0); ++ ++ /* If the input mode is MPEG do not divide the serial clock */ ++ if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) ++ enSerialClkDiv2 = 0; ++ ++ reg_1287 = ((pulseWidth & 0x1f) << 3) | ++ ((enSerialMode & 0x1) << 2) | (enSerialClkDiv2 & 0x1); ++ dib8000_write_word(state, 1287, reg_1287); ++ ++ dib8096p_enMpegMux(state, 1); ++} ++ ++static void dib8096p_setDibTxMux(struct dib8000_state *state, int mode) ++{ ++ u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 7); ++ ++ switch (mode) { ++ case MPEG_ON_DIBTX: ++ dprintk("SET MPEG ON DIBSTREAM TX"); ++ dib8096p_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); ++ reg_1288 |= (1 << 9); break; ++ case DIV_ON_DIBTX: ++ dprintk("SET DIV_OUT ON DIBSTREAM TX"); ++ dib8096p_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); ++ reg_1288 |= (1 << 8); break; ++ case ADC_ON_DIBTX: ++ dprintk("SET ADC_OUT ON DIBSTREAM TX"); ++ dib8096p_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); ++ reg_1288 |= (1 << 7); break; ++ default: ++ break; ++ } ++ dib8000_write_word(state, 1288, reg_1288); ++} ++ ++static void dib8096p_setHostBusMux(struct dib8000_state *state, int mode) ++{ ++ u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 4); ++ ++ switch (mode) { ++ case DEMOUT_ON_HOSTBUS: ++ dprintk("SET DEM OUT OLD INTERF ON HOST BUS"); ++ dib8096p_enMpegMux(state, 0); ++ reg_1288 |= (1 << 6); ++ break; ++ case DIBTX_ON_HOSTBUS: ++ dprintk("SET DIBSTREAM TX ON HOST BUS"); ++ dib8096p_enMpegMux(state, 0); ++ reg_1288 |= (1 << 5); ++ break; ++ case MPEG_ON_HOSTBUS: ++ dprintk("SET MPEG MUX ON HOST BUS"); ++ reg_1288 |= (1 << 4); ++ break; ++ default: ++ break; ++ } ++ dib8000_write_word(state, 1288, reg_1288); ++} ++ ++static int dib8096p_set_diversity_in(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 reg_1287; ++ ++ switch (onoff) { ++ case 0: /* only use the internal way - not the diversity input */ ++ dprintk("%s mode OFF : by default Enable Mpeg INPUT", ++ __func__); ++ /* outputRate = 8 */ ++ dib8096p_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); ++ ++ /* Do not divide the serial clock of MPEG MUX in ++ SERIAL MODE in case input mode MPEG is used */ ++ reg_1287 = dib8000_read_word(state, 1287); ++ /* enSerialClkDiv2 == 1 ? */ ++ if ((reg_1287 & 0x1) == 1) { ++ /* force enSerialClkDiv2 = 0 */ ++ reg_1287 &= ~0x1; ++ dib8000_write_word(state, 1287, reg_1287); ++ } ++ state->input_mode_mpeg = 1; ++ break; ++ case 1: /* both ways */ ++ case 2: /* only the diversity input */ ++ dprintk("%s ON : Enable diversity INPUT", __func__); ++ dib8096p_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); ++ state->input_mode_mpeg = 0; ++ break; ++ } ++ ++ dib8000_set_diversity_in(state->fe[0], onoff); ++ return 0; ++} ++ ++static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 outreg, smo_mode, fifo_threshold; ++ u8 prefer_mpeg_mux_use = 1; ++ int ret = 0; ++ ++ dib8096p_host_bus_drive(state, 1); ++ ++ fifo_threshold = 1792; ++ smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); ++ outreg = dib8000_read_word(state, 1286) & ++ ~((1 << 10) | (0x7 << 6) | (1 << 1)); ++ ++ switch (mode) { ++ case OUTMODE_HIGH_Z: ++ outreg = 0; ++ break; ++ ++ case OUTMODE_MPEG2_SERIAL: ++ if (prefer_mpeg_mux_use) { ++ dprintk("dib8096P setting output mode TS_SERIAL using Mpeg Mux"); ++ dib8096p_configMpegMux(state, 3, 1, 1); ++ dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); ++ } else {/* Use Smooth block */ ++ dprintk("dib8096P setting output mode TS_SERIAL using Smooth bloc"); ++ dib8096p_setHostBusMux(state, ++ DEMOUT_ON_HOSTBUS); ++ outreg |= (2 << 6) | (0 << 1); ++ } ++ break; ++ ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ if (prefer_mpeg_mux_use) { ++ dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Mpeg Mux"); ++ dib8096p_configMpegMux(state, 2, 0, 0); ++ dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); ++ } else { /* Use Smooth block */ ++ dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Smooth block"); ++ dib8096p_setHostBusMux(state, ++ DEMOUT_ON_HOSTBUS); ++ outreg |= (0 << 6); ++ } ++ break; ++ ++ case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ ++ dprintk("dib8096P setting output mode TS_PARALLEL_CONT using Smooth block"); ++ dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (1 << 6); ++ break; ++ ++ case OUTMODE_MPEG2_FIFO: ++ /* Using Smooth block because not supported ++ by new Mpeg Mux bloc */ ++ dprintk("dib8096P setting output mode TS_FIFO using Smooth block"); ++ dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); ++ outreg |= (5 << 6); ++ smo_mode |= (3 << 1); ++ fifo_threshold = 512; ++ break; ++ ++ case OUTMODE_DIVERSITY: ++ dprintk("dib8096P setting output mode MODE_DIVERSITY"); ++ dib8096p_setDibTxMux(state, DIV_ON_DIBTX); ++ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ break; ++ ++ case OUTMODE_ANALOG_ADC: ++ dprintk("dib8096P setting output mode MODE_ANALOG_ADC"); ++ dib8096p_setDibTxMux(state, ADC_ON_DIBTX); ++ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ break; ++ } ++ ++ if (mode != OUTMODE_HIGH_Z) ++ outreg |= (1<<10); ++ ++ dprintk("output_mpeg2_in_188_bytes = %d", ++ state->cfg.output_mpeg2_in_188_bytes); ++ if (state->cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5); ++ ++ ret |= dib8000_write_word(state, 299, smo_mode); ++ /* synchronous fread */ ++ ret |= dib8000_write_word(state, 299 + 1, fifo_threshold); ++ ret |= dib8000_write_word(state, 1286, outreg); ++ ++ return ret; ++} ++ ++static int map_addr_to_serpar_number(struct i2c_msg *msg) ++{ ++ if (msg->buf[0] <= 15) ++ msg->buf[0] -= 1; ++ else if (msg->buf[0] == 17) ++ msg->buf[0] = 15; ++ else if (msg->buf[0] == 16) ++ msg->buf[0] = 17; ++ else if (msg->buf[0] == 19) ++ msg->buf[0] = 16; ++ else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) ++ msg->buf[0] -= 3; ++ else if (msg->buf[0] == 28) ++ msg->buf[0] = 23; ++ else if (msg->buf[0] == 99) ++ msg->buf[0] = 99; ++ else ++ return -EINVAL; ++ return 0; ++} ++ ++static int dib8096p_tuner_write_serpar(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dib8000_state *state = i2c_get_adapdata(i2c_adap); ++ u8 n_overflow = 1; ++ u16 i = 1000; ++ u16 serpar_num = msg[0].buf[0]; ++ ++ while (n_overflow == 1 && i) { ++ n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; ++ i--; ++ if (i == 0) ++ dprintk("Tuner ITF: write busy (overflow)"); ++ } ++ dib8000_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); ++ dib8000_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); ++ ++ return num; ++} ++ ++static int dib8096p_tuner_read_serpar(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dib8000_state *state = i2c_get_adapdata(i2c_adap); ++ u8 n_overflow = 1, n_empty = 1; ++ u16 i = 1000; ++ u16 serpar_num = msg[0].buf[0]; ++ u16 read_word; ++ ++ while (n_overflow == 1 && i) { ++ n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: read busy (overflow)"); ++ } ++ dib8000_write_word(state, 1985, (0<<6) | (serpar_num&0x3f)); ++ ++ i = 1000; ++ while (n_empty == 1 && i) { ++ n_empty = dib8000_read_word(state, 1984)&0x1; ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: read busy (empty)"); ++ } ++ ++ read_word = dib8000_read_word(state, 1987); ++ msg[1].buf[0] = (read_word >> 8) & 0xff; ++ msg[1].buf[1] = (read_word) & 0xff; ++ ++ return num; ++} ++ ++static int dib8096p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ if (map_addr_to_serpar_number(&msg[0]) == 0) { ++ if (num == 1) /* write */ ++ return dib8096p_tuner_write_serpar(i2c_adap, msg, 1); ++ else /* read */ ++ return dib8096p_tuner_read_serpar(i2c_adap, msg, 2); ++ } ++ return num; ++} ++ ++static int dib8096p_rw_on_apb(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num, u16 apb_address) ++{ ++ struct dib8000_state *state = i2c_get_adapdata(i2c_adap); ++ u16 word; ++ ++ if (num == 1) { /* write */ ++ dib8000_write_word(state, apb_address, ++ ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); ++ } else { ++ word = dib8000_read_word(state, apb_address); ++ msg[1].buf[0] = (word >> 8) & 0xff; ++ msg[1].buf[1] = (word) & 0xff; ++ } ++ return num; ++} ++ ++static int dib8096p_tuner_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dib8000_state *state = i2c_get_adapdata(i2c_adap); ++ u16 apb_address = 0, word; ++ int i = 0; ++ ++ switch (msg[0].buf[0]) { ++ case 0x12: ++ apb_address = 1920; ++ break; ++ case 0x14: ++ apb_address = 1921; ++ break; ++ case 0x24: ++ apb_address = 1922; ++ break; ++ case 0x1a: ++ apb_address = 1923; ++ break; ++ case 0x22: ++ apb_address = 1924; ++ break; ++ case 0x33: ++ apb_address = 1926; ++ break; ++ case 0x34: ++ apb_address = 1927; ++ break; ++ case 0x35: ++ apb_address = 1928; ++ break; ++ case 0x36: ++ apb_address = 1929; ++ break; ++ case 0x37: ++ apb_address = 1930; ++ break; ++ case 0x38: ++ apb_address = 1931; ++ break; ++ case 0x39: ++ apb_address = 1932; ++ break; ++ case 0x2a: ++ apb_address = 1935; ++ break; ++ case 0x2b: ++ apb_address = 1936; ++ break; ++ case 0x2c: ++ apb_address = 1937; ++ break; ++ case 0x2d: ++ apb_address = 1938; ++ break; ++ case 0x2e: ++ apb_address = 1939; ++ break; ++ case 0x2f: ++ apb_address = 1940; ++ break; ++ case 0x30: ++ apb_address = 1941; ++ break; ++ case 0x31: ++ apb_address = 1942; ++ break; ++ case 0x32: ++ apb_address = 1943; ++ break; ++ case 0x3e: ++ apb_address = 1944; ++ break; ++ case 0x3f: ++ apb_address = 1945; ++ break; ++ case 0x40: ++ apb_address = 1948; ++ break; ++ case 0x25: ++ apb_address = 936; ++ break; ++ case 0x26: ++ apb_address = 937; ++ break; ++ case 0x27: ++ apb_address = 938; ++ break; ++ case 0x28: ++ apb_address = 939; ++ break; ++ case 0x1d: ++ /* get sad sel request */ ++ i = ((dib8000_read_word(state, 921) >> 12)&0x3); ++ word = dib8000_read_word(state, 924+i); ++ msg[1].buf[0] = (word >> 8) & 0xff; ++ msg[1].buf[1] = (word) & 0xff; ++ return num; ++ case 0x1f: ++ if (num == 1) { /* write */ ++ word = (u16) ((msg[0].buf[1] << 8) | ++ msg[0].buf[2]); ++ /* in the VGAMODE Sel are located on bit 0/1 */ ++ word &= 0x3; ++ word = (dib8000_read_word(state, 921) & ++ ~(3<<12)) | (word<<12); ++ /* Set the proper input */ ++ dib8000_write_word(state, 921, word); ++ return num; ++ } ++ } ++ ++ if (apb_address != 0) /* R/W acces via APB */ ++ return dib8096p_rw_on_apb(i2c_adap, msg, num, apb_address); ++ else /* R/W access via SERPAR */ ++ return dib8096p_tuner_rw_serpar(i2c_adap, msg, num); ++ ++ return 0; ++} ++ ++static u32 dib8096p_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm dib8096p_tuner_xfer_algo = { ++ .master_xfer = dib8096p_tuner_xfer, ++ .functionality = dib8096p_i2c_func, ++}; ++ ++struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *st = fe->demodulator_priv; ++ return &st->dib8096p_tuner_adap; ++} ++EXPORT_SYMBOL(dib8096p_get_i2c_tuner); ++ ++int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 en_cur_state; ++ ++ dprintk("sleep dib8096p: %d", onoff); ++ ++ en_cur_state = dib8000_read_word(state, 1922); ++ ++ /* LNAs and MIX are ON and therefore it is a valid configuration */ ++ if (en_cur_state > 0xff) ++ state->tuner_enable = en_cur_state ; ++ ++ if (onoff) ++ en_cur_state &= 0x00ff; ++ else { ++ if (state->tuner_enable != 0) ++ en_cur_state = state->tuner_enable; ++ } ++ ++ dib8000_write_word(state, 1922, en_cur_state); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dib8096p_tuner_sleep); ++ ++static const s32 lut_1000ln_mant[] = ++{ ++ 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600 ++}; ++ ++s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u32 ix = 0, tmp_val = 0, exp = 0, mant = 0; ++ s32 val; ++ ++ val = dib8000_read32(state, 384); ++ if (mode) { ++ tmp_val = val; ++ while (tmp_val >>= 1) ++ exp++; ++ mant = (val * 1000 / (1<demodulator_priv; ++ int val = 0; ++ ++ switch (IQ) { ++ case 1: ++ val = dib8000_read_word(state, 403); ++ break; ++ case 0: ++ val = dib8000_read_word(state, 404); ++ break; ++ } ++ if (val & 0x200) ++ val -= 1024; ++ ++ return val; ++} ++EXPORT_SYMBOL(dib8090p_get_dc_power); ++ ++static void dib8000_update_timf(struct dib8000_state *state) ++{ ++ u32 timf = state->timf = dib8000_read32(state, 435); ++ ++ dib8000_write_word(state, 29, (u16) (timf >> 16)); ++ dib8000_write_word(state, 30, (u16) (timf & 0xffff)); ++ dprintk("Updated timing frequency: %d (default: %d)", state->timf, state->timf_default); ++} ++ ++u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ switch (op) { ++ case DEMOD_TIMF_SET: ++ state->timf = timf; ++ break; ++ case DEMOD_TIMF_UPDATE: ++ dib8000_update_timf(state); ++ break; ++ case DEMOD_TIMF_GET: ++ break; ++ } ++ dib8000_set_bandwidth(state->fe[0], 6000); ++ ++ return state->timf; ++} ++EXPORT_SYMBOL(dib8000_ctrl_timf); ++ ++static const u16 adc_target_16dB[11] = { ++ (1 << 13) - 825 - 117, ++ (1 << 13) - 837 - 117, ++ (1 << 13) - 811 - 117, ++ (1 << 13) - 766 - 117, ++ (1 << 13) - 737 - 117, ++ (1 << 13) - 693 - 117, ++ (1 << 13) - 648 - 117, ++ (1 << 13) - 619 - 117, ++ (1 << 13) - 575 - 117, ++ (1 << 13) - 531 - 117, ++ (1 << 13) - 501 - 117 ++}; ++static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; ++ ++static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) ++{ ++ u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; ++ u8 guard, crate, constellation, timeI; ++ u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled ++ const s16 *ncoeff = NULL, *ana_fe; ++ u16 tmcc_pow = 0; ++ u16 coff_pow = 0x2800; ++ u16 init_prbs = 0xfff; ++ u16 ana_gain = 0; ++ ++ if (state->revision == 0x8090) ++ dib8000_init_sdram(state); ++ ++ if (state->ber_monitored_layer != LAYER_ALL) ++ dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); ++ else ++ dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); ++ ++ i = dib8000_read_word(state, 26) & 1; // P_dds_invspec ++ dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i); ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { ++ //compute new dds_freq for the seg and adjust prbs ++ int seg_offset = ++ state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - ++ (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - ++ (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); ++ int clk = state->cfg.pll->internal; ++ u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) ++ int dds_offset = seg_offset * segtodds; ++ int new_dds, sub_channel; ++ if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) ++ dds_offset -= (int)(segtodds / 2); ++ ++ if (state->cfg.pll->ifreq == 0) { ++ if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) { ++ dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); ++ new_dds = dds_offset; ++ } else ++ new_dds = dds_offset; ++ ++ // We shift tuning frequency if the wanted segment is : ++ // - the segment of center frequency with an odd total number of segments ++ // - the segment to the left of center frequency with an even total number of segments ++ // - the segment to the right of center frequency with an even total number of segments ++ if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) ++ && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) ++ && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) ++ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == ++ ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) ++ || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) ++ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2))) ++ || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) ++ && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == ++ ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) ++ )) { ++ new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) ++ } ++ } else { ++ if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) ++ new_dds = state->cfg.pll->ifreq - dds_offset; ++ else ++ new_dds = state->cfg.pll->ifreq + dds_offset; ++ } ++ dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); ++ dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) ++ sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; ++ else ++ sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; ++ sub_channel -= 6; ++ ++ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K ++ || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { ++ dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 ++ dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 ++ } else { ++ dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 ++ dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 ++ } ++ ++ switch (state->fe[0]->dtv_property_cache.transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ switch (sub_channel) { ++ case -6: ++ init_prbs = 0x0; ++ break; // 41, 0, 1 ++ case -5: ++ init_prbs = 0x423; ++ break; // 02~04 ++ case -4: ++ init_prbs = 0x9; ++ break; // 05~07 ++ case -3: ++ init_prbs = 0x5C7; ++ break; // 08~10 ++ case -2: ++ init_prbs = 0x7A6; ++ break; // 11~13 ++ case -1: ++ init_prbs = 0x3D8; ++ break; // 14~16 ++ case 0: ++ init_prbs = 0x527; ++ break; // 17~19 ++ case 1: ++ init_prbs = 0x7FF; ++ break; // 20~22 ++ case 2: ++ init_prbs = 0x79B; ++ break; // 23~25 ++ case 3: ++ init_prbs = 0x3D6; ++ break; // 26~28 ++ case 4: ++ init_prbs = 0x3A2; ++ break; // 29~31 ++ case 5: ++ init_prbs = 0x53B; ++ break; // 32~34 ++ case 6: ++ init_prbs = 0x2F4; ++ break; // 35~37 ++ default: ++ case 7: ++ init_prbs = 0x213; ++ break; // 38~40 ++ } ++ break; ++ ++ case TRANSMISSION_MODE_4K: ++ switch (sub_channel) { ++ case -6: ++ init_prbs = 0x0; ++ break; // 41, 0, 1 ++ case -5: ++ init_prbs = 0x208; ++ break; // 02~04 ++ case -4: ++ init_prbs = 0xC3; ++ break; // 05~07 ++ case -3: ++ init_prbs = 0x7B9; ++ break; // 08~10 ++ case -2: ++ init_prbs = 0x423; ++ break; // 11~13 ++ case -1: ++ init_prbs = 0x5C7; ++ break; // 14~16 ++ case 0: ++ init_prbs = 0x3D8; ++ break; // 17~19 ++ case 1: ++ init_prbs = 0x7FF; ++ break; // 20~22 ++ case 2: ++ init_prbs = 0x3D6; ++ break; // 23~25 ++ case 3: ++ init_prbs = 0x53B; ++ break; // 26~28 ++ case 4: ++ init_prbs = 0x213; ++ break; // 29~31 ++ case 5: ++ init_prbs = 0x29; ++ break; // 32~34 ++ case 6: ++ init_prbs = 0xD0; ++ break; // 35~37 ++ default: ++ case 7: ++ init_prbs = 0x48E; ++ break; // 38~40 ++ } ++ break; ++ ++ default: ++ case TRANSMISSION_MODE_8K: ++ switch (sub_channel) { ++ case -6: ++ init_prbs = 0x0; ++ break; // 41, 0, 1 ++ case -5: ++ init_prbs = 0x740; ++ break; // 02~04 ++ case -4: ++ init_prbs = 0x069; ++ break; // 05~07 ++ case -3: ++ init_prbs = 0x7DD; ++ break; // 08~10 ++ case -2: ++ init_prbs = 0x208; ++ break; // 11~13 ++ case -1: ++ init_prbs = 0x7B9; ++ break; // 14~16 ++ case 0: ++ init_prbs = 0x5C7; ++ break; // 17~19 ++ case 1: ++ init_prbs = 0x7FF; ++ break; // 20~22 ++ case 2: ++ init_prbs = 0x53B; ++ break; // 23~25 ++ case 3: ++ init_prbs = 0x29; ++ break; // 26~28 ++ case 4: ++ init_prbs = 0x48E; ++ break; // 29~31 ++ case 5: ++ init_prbs = 0x4C4; ++ break; // 32~34 ++ case 6: ++ init_prbs = 0x367; ++ break; // 33~37 ++ default: ++ case 7: ++ init_prbs = 0x684; ++ break; // 38~40 ++ } ++ break; ++ } ++ } else { ++ dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); ++ dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); ++ dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); ++ } ++ /*P_mode == ?? */ ++ dib8000_write_word(state, 10, (seq << 4)); ++ // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); ++ ++ switch (state->fe[0]->dtv_property_cache.guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ guard = 0; ++ break; ++ case GUARD_INTERVAL_1_16: ++ guard = 1; ++ break; ++ case GUARD_INTERVAL_1_8: ++ guard = 2; ++ break; ++ case GUARD_INTERVAL_1_4: ++ default: ++ guard = 3; ++ break; ++ } ++ ++ dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 ++ ++ max_constellation = DQPSK; ++ for (i = 0; i < 3; i++) { ++ switch (state->fe[0]->dtv_property_cache.layer[i].modulation) { ++ case DQPSK: ++ constellation = 0; ++ break; ++ case QPSK: ++ constellation = 1; ++ break; ++ case QAM_16: ++ constellation = 2; ++ break; ++ case QAM_64: ++ default: ++ constellation = 3; ++ break; ++ } ++ ++ switch (state->fe[0]->dtv_property_cache.layer[i].fec) { ++ case FEC_1_2: ++ crate = 1; ++ break; ++ case FEC_2_3: ++ crate = 2; ++ break; ++ case FEC_3_4: ++ crate = 3; ++ break; ++ case FEC_5_6: ++ crate = 5; ++ break; ++ case FEC_7_8: ++ default: ++ crate = 7; ++ break; ++ } ++ ++ if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) && ++ ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) || ++ (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)) ++ ) ++ timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving; ++ else ++ timeI = 0; ++ dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) | ++ (crate << 3) | timeI); ++ if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { ++ switch (max_constellation) { ++ case DQPSK: ++ case QPSK: ++ if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 || ++ state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) ++ max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; ++ break; ++ case QAM_16: ++ if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) ++ max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; ++ break; ++ } ++ } ++ } ++ ++ mode = fft_to_mode(state); ++ ++ //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ ++ ++ dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | ++ ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache. ++ isdbt_sb_mode & 1) << 4)); ++ ++ dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval); ++ ++ /* signal optimization parameter */ ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { ++ seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; ++ for (i = 1; i < 3; i++) ++ nbseg_diff += ++ (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; ++ for (i = 0; i < nbseg_diff; i++) ++ seg_diff_mask |= 1 << permu_seg[i + 1]; ++ } else { ++ for (i = 0; i < 3; i++) ++ nbseg_diff += ++ (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; ++ for (i = 0; i < nbseg_diff; i++) ++ seg_diff_mask |= 1 << permu_seg[i]; ++ } ++ dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); ++ ++ state->differential_constellation = (seg_diff_mask != 0); ++ if (state->revision != 0x8090) ++ dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); ++ else ++ dib8096p_set_diversity_in(state->fe[0], state->diversity_onoff); ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) ++ seg_mask13 = 0x00E0; ++ else // 1-segment ++ seg_mask13 = 0x0040; ++ } else ++ seg_mask13 = 0x1fff; ++ ++ // WRITE: Mode & Diff mask ++ dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); ++ ++ if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode)) ++ dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); ++ else ++ dib8000_write_word(state, 268, (2 << 9) | 39); //init value ++ ++ // ---- SMALL ---- ++ // P_small_seg_diff ++ dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 ++ ++ dib8000_write_word(state, 353, seg_mask13); // ADDR 353 ++ ++/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ ++ ++ // ---- SMALL ---- ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ switch (state->fe[0]->dtv_property_cache.transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ++ ncoeff = coeff_2k_sb_1seg_dqpsk; ++ else // QPSK or QAM ++ ncoeff = coeff_2k_sb_1seg; ++ } else { // 3-segments ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) ++ ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; ++ else // QPSK or QAM on external segments ++ ncoeff = coeff_2k_sb_3seg_0dqpsk; ++ } else { // QPSK or QAM on central segment ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) ++ ncoeff = coeff_2k_sb_3seg_1dqpsk; ++ else // QPSK or QAM on external segments ++ ncoeff = coeff_2k_sb_3seg; ++ } ++ } ++ break; ++ ++ case TRANSMISSION_MODE_4K: ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ++ ncoeff = coeff_4k_sb_1seg_dqpsk; ++ else // QPSK or QAM ++ ncoeff = coeff_4k_sb_1seg; ++ } else { // 3-segments ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ++ ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; ++ } else { // QPSK or QAM on external segments ++ ncoeff = coeff_4k_sb_3seg_0dqpsk; ++ } ++ } else { // QPSK or QAM on central segment ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ++ ncoeff = coeff_4k_sb_3seg_1dqpsk; ++ } else // QPSK or QAM on external segments ++ ncoeff = coeff_4k_sb_3seg; ++ } ++ } ++ break; ++ ++ case TRANSMISSION_MODE_AUTO: ++ case TRANSMISSION_MODE_8K: ++ default: ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) ++ ncoeff = coeff_8k_sb_1seg_dqpsk; ++ else // QPSK or QAM ++ ncoeff = coeff_8k_sb_1seg; ++ } else { // 3-segments ++ if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ++ ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; ++ } else { // QPSK or QAM on external segments ++ ncoeff = coeff_8k_sb_3seg_0dqpsk; ++ } ++ } else { // QPSK or QAM on central segment ++ if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { ++ ncoeff = coeff_8k_sb_3seg_1dqpsk; ++ } else // QPSK or QAM on external segments ++ ncoeff = coeff_8k_sb_3seg; ++ } ++ } ++ break; ++ } ++ for (i = 0; i < 8; i++) ++ dib8000_write_word(state, 343 + i, ncoeff[i]); ++ } ++ ++ // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 ++ dib8000_write_word(state, 351, ++ (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); ++ ++ // ---- COFF ---- ++ // Carloff, the most robust ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ ++ // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 ++ // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 ++ dib8000_write_word(state, 187, ++ (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) ++ | 0x3); ++ ++/* // P_small_coef_ext_enable = 1 */ ++/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { ++ ++ // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) ++ if (mode == 3) ++ dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); ++ else ++ dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); ++ // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, ++ // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 ++ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); ++ // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 ++ dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); ++ // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 ++ dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); ++ ++ // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k ++ dib8000_write_word(state, 181, 300); ++ dib8000_write_word(state, 182, 150); ++ dib8000_write_word(state, 183, 80); ++ dib8000_write_word(state, 184, 300); ++ dib8000_write_word(state, 185, 150); ++ dib8000_write_word(state, 186, 80); ++ } else { // Sound Broadcasting mode 3 seg ++ // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 ++ /* if (mode == 3) */ ++ /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ ++ /* else */ ++ /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ ++ dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); ++ ++ // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, ++ // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 ++ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); ++ // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 ++ dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); ++ //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 ++ dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); ++ ++ // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k ++ dib8000_write_word(state, 181, 350); ++ dib8000_write_word(state, 182, 300); ++ dib8000_write_word(state, 183, 250); ++ dib8000_write_word(state, 184, 350); ++ dib8000_write_word(state, 185, 300); ++ dib8000_write_word(state, 186, 250); ++ } ++ ++ } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments ++ dib8000_write_word(state, 180, (16 << 6) | 9); ++ dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); ++ coff_pow = 0x2800; ++ for (i = 0; i < 6; i++) ++ dib8000_write_word(state, 181 + i, coff_pow); ++ ++ // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, ++ // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 ++ dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); ++ ++ // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 ++ dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); ++ // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 ++ dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); ++ } ++ // ---- FFT ---- ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) ++ dib8000_write_word(state, 178, 64); // P_fft_powrange=64 ++ else ++ dib8000_write_word(state, 178, 32); // P_fft_powrange=32 ++ ++ /* make the cpil_coff_lock more robust but slower p_coff_winlen ++ * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) ++ */ ++ /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) ++ dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ ++ ++ dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ ++ dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ ++ dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ ++ if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) ++ dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ ++ else ++ dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ ++ dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ ++ //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ ++ if (!autosearching) ++ dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ ++ else ++ dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. ++ dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); ++ ++ dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ ++ ++ /* offset loop parameters */ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) ++ /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ ++ dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); ++ ++ else // Sound Broadcasting mode 3 seg ++ /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ ++ dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); ++ } else ++ // TODO in 13 seg, timf_alpha can always be the same or not ? ++ /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ ++ dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) ++ /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ ++ dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); ++ ++ else // Sound Broadcasting mode 3 seg ++ /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ ++ dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); ++ } else ++ /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ ++ dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); ++ ++ /* P_dvsy_sync_wait - reuse mode */ ++ switch (state->fe[0]->dtv_property_cache.transmission_mode) { ++ case TRANSMISSION_MODE_8K: ++ mode = 256; ++ break; ++ case TRANSMISSION_MODE_4K: ++ mode = 128; ++ break; ++ default: ++ case TRANSMISSION_MODE_2K: ++ mode = 64; ++ break; ++ } ++ if (state->cfg.diversity_delay == 0) ++ mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo ++ else ++ mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo ++ mode <<= 4; ++ dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); ++ ++ /* channel estimation fine configuration */ ++ switch (max_constellation) { ++ case QAM_64: ++ ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB ++ coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ ++ coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ ++ coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ ++ //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 ++ break; ++ case QAM_16: ++ ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB ++ coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ ++ coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ ++ coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ ++ coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ ++ //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) ++ break; ++ default: ++ ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level ++ coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ ++ coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ ++ coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ ++ coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ ++ break; ++ } ++ for (mode = 0; mode < 4; mode++) ++ dib8000_write_word(state, 215 + mode, coeff[mode]); ++ ++ // update ana_gain depending on max constellation ++ dib8000_write_word(state, 116, ana_gain); ++ // update ADC target depending on ana_gain ++ if (ana_gain) { // set -16dB ADC target for ana_gain=-1 ++ for (i = 0; i < 10; i++) ++ dib8000_write_word(state, 80 + i, adc_target_16dB[i]); ++ } else { // set -22dB ADC target for ana_gain=0 ++ for (i = 0; i < 10; i++) ++ dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); ++ } ++ ++ // ---- ANA_FE ---- ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) ++ ana_fe = ana_fe_coeff_3seg; ++ else // 1-segment ++ ana_fe = ana_fe_coeff_1seg; ++ } else ++ ana_fe = ana_fe_coeff_13seg; ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) ++ for (mode = 0; mode < 24; mode++) ++ dib8000_write_word(state, 117 + mode, ana_fe[mode]); ++ ++ // ---- CHAN_BLK ---- ++ for (i = 0; i < 13; i++) { ++ if ((((~seg_diff_mask) >> i) & 1) == 1) { ++ P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); ++ P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); ++ } ++ } ++ dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge ++ dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge ++ // "P_cspu_left_edge" not used => do not care ++ // "P_cspu_right_edge" not used => do not care ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 ++ dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 ++ && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { ++ //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 ++ dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 ++ } ++ } else if (state->isdbt_cfg_loaded == 0) { ++ dib8000_write_word(state, 228, 0); // default value ++ dib8000_write_word(state, 265, 31); // default value ++ dib8000_write_word(state, 205, 0x200f); // init value ++ } ++ // ---- TMCC ---- ++ for (i = 0; i < 3; i++) ++ tmcc_pow += ++ (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count); ++ // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); ++ // Threshold is set at 1/4 of max power. ++ tmcc_pow *= (1 << (9 - 2)); ++ ++ dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k ++ dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k ++ dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k ++ //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); ++ // ---- PHA3 ---- ++ ++ if (state->isdbt_cfg_loaded == 0) ++ dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ ++ ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) ++ state->isdbt_cfg_loaded = 0; ++ else ++ state->isdbt_cfg_loaded = 1; ++ ++} ++ ++static int dib8000_autosearch_start(struct dvb_frontend *fe) ++{ ++ u8 factor; ++ u32 value; ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ int slist = 0; ++ ++ state->fe[0]->dtv_property_cache.inversion = 0; ++ if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) ++ state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; ++ state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; ++ state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; ++ state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; ++ ++ //choose the right list, in sb, always do everything ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; ++ slist = 7; ++ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); ++ } else { ++ if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { ++ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { ++ slist = 7; ++ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 ++ } else ++ slist = 3; ++ } else { ++ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { ++ slist = 2; ++ dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 ++ } else ++ slist = 0; ++ } ++ ++ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; ++ if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; ++ ++ dprintk("using list for autosearch : %d", slist); ++ dib8000_set_channel(state, (unsigned char)slist, 1); ++ //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 ++ ++ factor = 1; ++ ++ //set lock_mask values ++ dib8000_write_word(state, 6, 0x4); ++ dib8000_write_word(state, 7, 0x8); ++ dib8000_write_word(state, 8, 0x1000); ++ ++ //set lock_mask wait time values ++ value = 50 * state->cfg.pll->internal * factor; ++ dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time ++ dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time ++ value = 100 * state->cfg.pll->internal * factor; ++ dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time ++ dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time ++ value = 1000 * state->cfg.pll->internal * factor; ++ dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time ++ dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time ++ ++ value = dib8000_read_word(state, 0); ++ dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); ++ dib8000_read_word(state, 1284); // reset the INT. n_irq_pending ++ dib8000_write_word(state, 0, (u16) value); ++ ++ } ++ ++ return 0; ++} ++ ++static int dib8000_autosearch_irq(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 irq_pending = dib8000_read_word(state, 1284); ++ ++ if (irq_pending & 0x1) { // failed ++ dprintk("dib8000_autosearch_irq failed"); ++ return 1; ++ } ++ ++ if (irq_pending & 0x2) { // succeeded ++ dprintk("dib8000_autosearch_irq succeeded"); ++ return 2; ++ } ++ ++ return 0; // still pending ++} ++ ++static int dib8000_tune(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ int ret = 0; ++ u16 lock, value, mode; ++ ++ // we are already tuned - just resuming from suspend ++ if (state == NULL) ++ return -EINVAL; ++ ++ mode = fft_to_mode(state); ++ ++ dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); ++ dib8000_set_channel(state, 0, 0); ++ ++ // restart demod ++ ret |= dib8000_write_word(state, 770, 0x4000); ++ ret |= dib8000_write_word(state, 770, 0x0000); ++ msleep(45); ++ ++ /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ ++ /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ ++ ++ // never achieved a lock before - wait for timfreq to update ++ if (state->timf == 0) { ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) ++ msleep(300); ++ else // Sound Broadcasting mode 3 seg ++ msleep(500); ++ } else // 13 seg ++ msleep(200); ++ } ++ if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { ++ if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { ++ ++ /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ ++ dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); ++ //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); ++ ++ /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ ++ ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); ++ ++ } else { // Sound Broadcasting mode 3 seg ++ ++ /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ ++ dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); ++ ++ ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); ++ } ++ ++ } else { // 13 seg ++ /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ ++ dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); ++ ++ ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); ++ ++ } ++ ++ // we achieved a coff_cpil_lock - it's time to update the timf ++ if (state->revision != 0x8090) ++ lock = dib8000_read_word(state, 568); ++ else ++ lock = dib8000_read_word(state, 570); ++ if ((lock >> 11) & 0x1) ++ dib8000_update_timf(state); ++ ++ //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start ++ dib8000_write_word(state, 6, 0x200); ++ ++ if (state->revision == 0x8002) { ++ value = dib8000_read_word(state, 903); ++ dib8000_write_word(state, 903, value & ~(1 << 3)); ++ msleep(1); ++ dib8000_write_word(state, 903, value | (1 << 3)); ++ } ++ ++ return ret; ++} ++ ++static int dib8000_wakeup(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ int ret; ++ ++ dib8000_set_power_mode(state, DIB8000_POWER_ALL); ++ dib8000_set_adc_state(state, DIBX000_ADC_ON); ++ if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) ++ dprintk("could not start Slow ADC"); ++ ++ if (state->revision != 0x8090) ++ dib8000_sad_calib(state); ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int dib8000_sleep(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ int ret; ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (state->revision != 0x8090) ++ dib8000_set_output_mode(fe, OUTMODE_HIGH_Z); ++ dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); ++ return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); ++} ++ ++enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ return state->tune_state; ++} ++EXPORT_SYMBOL(dib8000_get_tune_state); ++ ++int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ state->tune_state = tune_state; ++ return 0; ++} ++EXPORT_SYMBOL(dib8000_set_tune_state); ++ ++static int dib8000_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 i, val = 0; ++ fe_status_t stat; ++ u8 index_frontend, sub_index_frontend; ++ ++ fe->dtv_property_cache.bandwidth_hz = 6000000; ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); ++ if (stat&FE_HAS_SYNC) { ++ dprintk("TMCC lock on the slave%i", index_frontend); ++ /* synchronize the cache with the other frontends */ ++ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]); ++ for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { ++ if (sub_index_frontend != index_frontend) { ++ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; ++ state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; ++ state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; ++ state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; ++ state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; ++ for (i = 0; i < 3; i++) { ++ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; ++ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; ++ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; ++ state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; ++ } ++ } ++ } ++ return 0; ++ } ++ } ++ ++ fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; ++ ++ if (state->revision == 0x8090) ++ val = dib8000_read_word(state, 572); ++ else ++ val = dib8000_read_word(state, 570); ++ fe->dtv_property_cache.inversion = (val & 0x40) >> 6; ++ switch ((val & 0x30) >> 4) { ++ case 1: ++ fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 3: ++ default: ++ fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ ++ switch (val & 0x3) { ++ case 0: ++ fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; ++ dprintk("dib8000_get_frontend GI = 1/32 "); ++ break; ++ case 1: ++ fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; ++ dprintk("dib8000_get_frontend GI = 1/16 "); ++ break; ++ case 2: ++ dprintk("dib8000_get_frontend GI = 1/8 "); ++ fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ dprintk("dib8000_get_frontend GI = 1/4 "); ++ fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ val = dib8000_read_word(state, 505); ++ fe->dtv_property_cache.isdbt_partial_reception = val & 1; ++ dprintk("dib8000_get_frontend : partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); ++ ++ for (i = 0; i < 3; i++) { ++ val = dib8000_read_word(state, 493 + i); ++ fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; ++ dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); ++ ++ val = dib8000_read_word(state, 499 + i); ++ fe->dtv_property_cache.layer[i].interleaving = val & 0x3; ++ dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving); ++ ++ val = dib8000_read_word(state, 481 + i); ++ switch (val & 0x7) { ++ case 1: ++ fe->dtv_property_cache.layer[i].fec = FEC_1_2; ++ dprintk("dib8000_get_frontend : Layer %d Code Rate = 1/2 ", i); ++ break; ++ case 2: ++ fe->dtv_property_cache.layer[i].fec = FEC_2_3; ++ dprintk("dib8000_get_frontend : Layer %d Code Rate = 2/3 ", i); ++ break; ++ case 3: ++ fe->dtv_property_cache.layer[i].fec = FEC_3_4; ++ dprintk("dib8000_get_frontend : Layer %d Code Rate = 3/4 ", i); ++ break; ++ case 5: ++ fe->dtv_property_cache.layer[i].fec = FEC_5_6; ++ dprintk("dib8000_get_frontend : Layer %d Code Rate = 5/6 ", i); ++ break; ++ default: ++ fe->dtv_property_cache.layer[i].fec = FEC_7_8; ++ dprintk("dib8000_get_frontend : Layer %d Code Rate = 7/8 ", i); ++ break; ++ } ++ ++ val = dib8000_read_word(state, 487 + i); ++ switch (val & 0x3) { ++ case 0: ++ dprintk("dib8000_get_frontend : Layer %d DQPSK ", i); ++ fe->dtv_property_cache.layer[i].modulation = DQPSK; ++ break; ++ case 1: ++ fe->dtv_property_cache.layer[i].modulation = QPSK; ++ dprintk("dib8000_get_frontend : Layer %d QPSK ", i); ++ break; ++ case 2: ++ fe->dtv_property_cache.layer[i].modulation = QAM_16; ++ dprintk("dib8000_get_frontend : Layer %d QAM16 ", i); ++ break; ++ case 3: ++ default: ++ dprintk("dib8000_get_frontend : Layer %d QAM64 ", i); ++ fe->dtv_property_cache.layer[i].modulation = QAM_64; ++ break; ++ } ++ } ++ ++ /* synchronize the cache with the other frontends */ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode; ++ state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; ++ state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; ++ state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; ++ state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = fe->dtv_property_cache.isdbt_partial_reception; ++ for (i = 0; i < 3; i++) { ++ state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = fe->dtv_property_cache.layer[i].segment_count; ++ state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = fe->dtv_property_cache.layer[i].interleaving; ++ state->fe[index_frontend]->dtv_property_cache.layer[i].fec = fe->dtv_property_cache.layer[i].fec; ++ state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = fe->dtv_property_cache.layer[i].modulation; ++ } ++ } ++ return 0; ++} ++ ++static int dib8000_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 nbr_pending, exit_condition, index_frontend; ++ s8 index_frontend_success = -1; ++ int time, ret; ++ int time_slave = FE_CALLBACK_TIME_NEVER; ++ ++ if (state->fe[0]->dtv_property_cache.frequency == 0) { ++ dprintk("dib8000: must at least specify frequency "); ++ return 0; ++ } ++ ++ if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { ++ dprintk("dib8000: no bandwidth specified, set to default "); ++ state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000; ++ } ++ ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ /* synchronization of the cache */ ++ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; ++ memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); ++ ++ if (state->revision != 0x8090) ++ dib8000_set_output_mode(state->fe[index_frontend], ++ OUTMODE_HIGH_Z); ++ else ++ dib8096p_set_output_mode(state->fe[index_frontend], ++ OUTMODE_HIGH_Z); ++ if (state->fe[index_frontend]->ops.tuner_ops.set_params) ++ state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]); ++ ++ dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); ++ } ++ ++ /* start up the AGC */ ++ do { ++ time = dib8000_agc_startup(state->fe[0]); ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ time_slave = dib8000_agc_startup(state->fe[index_frontend]); ++ if (time == FE_CALLBACK_TIME_NEVER) ++ time = time_slave; ++ else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time)) ++ time = time_slave; ++ } ++ if (time != FE_CALLBACK_TIME_NEVER) ++ msleep(time / 10); ++ else ++ break; ++ exit_condition = 1; ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { ++ exit_condition = 0; ++ break; ++ } ++ } ++ } while (exit_condition == 0); ++ ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); ++ ++ if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || ++ (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || ++ (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || ++ (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || ++ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && ++ (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && ++ (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && ++ ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || ++ (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || ++ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && ++ (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && ++ (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && ++ ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || ++ (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || ++ (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && ++ (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && ++ (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && ++ ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || ++ (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || ++ (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || ++ ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && ++ ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || ++ ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && ++ ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { ++ int i = 100; ++ u8 found = 0; ++ u8 tune_failed = 0; ++ ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); ++ dib8000_autosearch_start(state->fe[index_frontend]); ++ } ++ ++ do { ++ msleep(20); ++ nbr_pending = 0; ++ exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ if (((tune_failed >> index_frontend) & 0x1) == 0) { ++ found = dib8000_autosearch_irq(state->fe[index_frontend]); ++ switch (found) { ++ case 0: /* tune pending */ ++ nbr_pending++; ++ break; ++ case 2: ++ dprintk("autosearch succeed on the frontend%i", index_frontend); ++ exit_condition = 2; ++ index_frontend_success = index_frontend; ++ break; ++ default: ++ dprintk("unhandled autosearch result"); ++ case 1: ++ tune_failed |= (1 << index_frontend); ++ dprintk("autosearch failed for the frontend%i", index_frontend); ++ break; ++ } ++ } ++ } ++ ++ /* if all tune are done and no success, exit: tune failed */ ++ if ((nbr_pending == 0) && (exit_condition == 0)) ++ exit_condition = 1; ++ } while ((exit_condition == 0) && i--); ++ ++ if (exit_condition == 1) { /* tune failed */ ++ dprintk("tune failed"); ++ return 0; ++ } ++ ++ dprintk("tune success on frontend%i", index_frontend_success); ++ ++ dib8000_get_frontend(fe); ++ } ++ ++ for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ ret = dib8000_tune(state->fe[index_frontend]); ++ ++ /* set output mode and diversity input */ ++ if (state->revision != 0x8090) { ++ dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); ++ for (index_frontend = 1; ++ (index_frontend < MAX_NUMBER_OF_FRONTENDS) && ++ (state->fe[index_frontend] != NULL); ++ index_frontend++) { ++ dib8000_set_output_mode(state->fe[index_frontend], ++ OUTMODE_DIVERSITY); ++ dib8000_set_diversity_in(state->fe[index_frontend-1], 1); ++ } ++ ++ /* turn off the diversity of the last chip */ ++ dib8000_set_diversity_in(state->fe[index_frontend-1], 0); ++ } else { ++ dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode); ++ if (state->cfg.enMpegOutput == 0) { ++ dib8096p_setDibTxMux(state, MPEG_ON_DIBTX); ++ dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); ++ } ++ for (index_frontend = 1; ++ (index_frontend < MAX_NUMBER_OF_FRONTENDS) && ++ (state->fe[index_frontend] != NULL); ++ index_frontend++) { ++ dib8096p_set_output_mode(state->fe[index_frontend], ++ OUTMODE_DIVERSITY); ++ dib8096p_set_diversity_in(state->fe[index_frontend-1], 1); ++ } ++ ++ /* turn off the diversity of the last chip */ ++ dib8096p_set_diversity_in(state->fe[index_frontend-1], 0); ++ } ++ ++ return ret; ++} ++ ++static u16 dib8000_read_lock(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ if (state->revision == 0x8090) ++ return dib8000_read_word(state, 570); ++ return dib8000_read_word(state, 568); ++} ++ ++static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u16 lock_slave = 0, lock; ++ u8 index_frontend; ++ ++ if (state->revision == 0x8090) ++ lock = dib8000_read_word(state, 570); ++ else ++ lock = dib8000_read_word(state, 568); ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ lock_slave |= dib8000_read_lock(state->fe[index_frontend]); ++ ++ *stat = 0; ++ ++ if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1)) ++ *stat |= FE_HAS_SIGNAL; ++ ++ if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */ ++ *stat |= FE_HAS_CARRIER; ++ ++ if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */ ++ *stat |= FE_HAS_SYNC; ++ ++ if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */ ++ *stat |= FE_HAS_LOCK; ++ ++ if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) { ++ lock = dib8000_read_word(state, 554); /* Viterbi Layer A */ ++ if (lock & 0x01) ++ *stat |= FE_HAS_VITERBI; ++ ++ lock = dib8000_read_word(state, 555); /* Viterbi Layer B */ ++ if (lock & 0x01) ++ *stat |= FE_HAS_VITERBI; ++ ++ lock = dib8000_read_word(state, 556); /* Viterbi Layer C */ ++ if (lock & 0x01) ++ *stat |= FE_HAS_VITERBI; ++ } ++ ++ return 0; ++} ++ ++static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ /* 13 segments */ ++ if (state->revision == 0x8090) ++ *ber = (dib8000_read_word(state, 562) << 16) | ++ dib8000_read_word(state, 563); ++ else ++ *ber = (dib8000_read_word(state, 560) << 16) | ++ dib8000_read_word(state, 561); ++ return 0; ++} ++ ++static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ /* packet error on 13 seg */ ++ if (state->revision == 0x8090) ++ *unc = dib8000_read_word(state, 567); ++ else ++ *unc = dib8000_read_word(state, 565); ++ return 0; ++} ++ ++static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ u16 val; ++ ++ *strength = 0; ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); ++ if (val > 65535 - *strength) ++ *strength = 65535; ++ else ++ *strength += val; ++ } ++ ++ val = 65535 - dib8000_read_word(state, 390); ++ if (val > 65535 - *strength) ++ *strength = 65535; ++ else ++ *strength += val; ++ return 0; ++} ++ ++static u32 dib8000_get_snr(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u32 n, s, exp; ++ u16 val; ++ ++ if (state->revision != 0x8090) ++ val = dib8000_read_word(state, 542); ++ else ++ val = dib8000_read_word(state, 544); ++ n = (val >> 6) & 0xff; ++ exp = (val & 0x3f); ++ if ((exp & 0x20) != 0) ++ exp -= 0x40; ++ n <<= exp+16; ++ ++ if (state->revision != 0x8090) ++ val = dib8000_read_word(state, 543); ++ else ++ val = dib8000_read_word(state, 545); ++ s = (val >> 6) & 0xff; ++ exp = (val & 0x3f); ++ if ((exp & 0x20) != 0) ++ exp -= 0x40; ++ s <<= exp+16; ++ ++ if (n > 0) { ++ u32 t = (s/n) << 16; ++ return t + ((s << 16) - n*t) / n; ++ } ++ return 0xffffffff; ++} ++ ++static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ u32 snr_master; ++ ++ snr_master = dib8000_get_snr(fe); ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ snr_master += dib8000_get_snr(state->fe[index_frontend]); ++ ++ if ((snr_master >> 16) != 0) { ++ snr_master = 10*intlog10(snr_master>>16); ++ *snr = snr_master / ((1 << 24) / 10); ++ } ++ else ++ *snr = 0; ++ ++ return 0; ++} ++ ++int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend = 1; ++ ++ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) ++ index_frontend++; ++ if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { ++ dprintk("set slave fe %p to index %i", fe_slave, index_frontend); ++ state->fe[index_frontend] = fe_slave; ++ return 0; ++ } ++ ++ dprintk("too many slave frontend"); ++ return -ENOMEM; ++} ++EXPORT_SYMBOL(dib8000_set_slave_frontend); ++ ++int dib8000_remove_slave_frontend(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ u8 index_frontend = 1; ++ ++ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) ++ index_frontend++; ++ if (index_frontend != 1) { ++ dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1); ++ state->fe[index_frontend] = NULL; ++ return 0; ++ } ++ ++ dprintk("no frontend to be removed"); ++ return -ENODEV; ++} ++EXPORT_SYMBOL(dib8000_remove_slave_frontend); ++ ++struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) ++{ ++ struct dib8000_state *state = fe->demodulator_priv; ++ ++ if (slave_index >= MAX_NUMBER_OF_FRONTENDS) ++ return NULL; ++ return state->fe[slave_index]; ++} ++EXPORT_SYMBOL(dib8000_get_slave_frontend); ++ ++ ++int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, ++ u8 default_addr, u8 first_addr, u8 is_dib8096p) ++{ ++ int k = 0, ret = 0; ++ u8 new_addr = 0; ++ struct i2c_device client = {.adap = host }; ++ ++ client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); ++ if (!client.i2c_write_buffer) { ++ dprintk("%s: not enough memory", __func__); ++ return -ENOMEM; ++ } ++ client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); ++ if (!client.i2c_read_buffer) { ++ dprintk("%s: not enough memory", __func__); ++ ret = -ENOMEM; ++ goto error_memory_read; ++ } ++ client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); ++ if (!client.i2c_buffer_lock) { ++ dprintk("%s: not enough memory", __func__); ++ ret = -ENOMEM; ++ goto error_memory_lock; ++ } ++ mutex_init(client.i2c_buffer_lock); ++ ++ for (k = no_of_demods - 1; k >= 0; k--) { ++ /* designated i2c address */ ++ new_addr = first_addr + (k << 1); ++ ++ client.addr = new_addr; ++ if (!is_dib8096p) ++ dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ ++ if (dib8000_identify(&client) == 0) { ++ /* sram lead in, rdy */ ++ if (!is_dib8096p) ++ dib8000_i2c_write16(&client, 1287, 0x0003); ++ client.addr = default_addr; ++ if (dib8000_identify(&client) == 0) { ++ dprintk("#%d: not identified", k); ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ ++ /* start diversity to pull_down div_str - just for i2c-enumeration */ ++ dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6)); ++ ++ /* set new i2c address and force divstart */ ++ dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2); ++ client.addr = new_addr; ++ dib8000_identify(&client); ++ ++ dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); ++ } ++ ++ for (k = 0; k < no_of_demods; k++) { ++ new_addr = first_addr | (k << 1); ++ client.addr = new_addr; ++ ++ // unforce divstr ++ dib8000_i2c_write16(&client, 1285, new_addr << 2); ++ ++ /* deactivate div - it was just for i2c-enumeration */ ++ dib8000_i2c_write16(&client, 1286, 0); ++ } ++ ++error: ++ kfree(client.i2c_buffer_lock); ++error_memory_lock: ++ kfree(client.i2c_read_buffer); ++error_memory_read: ++ kfree(client.i2c_write_buffer); ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL(dib8000_i2c_enumeration); ++static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ tune->step_size = 0; ++ tune->max_drift = 0; ++ return 0; ++} ++ ++static void dib8000_release(struct dvb_frontend *fe) ++{ ++ struct dib8000_state *st = fe->demodulator_priv; ++ u8 index_frontend; ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) ++ dvb_frontend_detach(st->fe[index_frontend]); ++ ++ dibx000_exit_i2c_master(&st->i2c_master); ++ i2c_del_adapter(&st->dib8096p_tuner_adap); ++ kfree(st->fe[0]); ++ kfree(st); ++} ++ ++struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) ++{ ++ struct dib8000_state *st = fe->demodulator_priv; ++ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); ++} ++ ++EXPORT_SYMBOL(dib8000_get_i2c_master); ++ ++int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct dib8000_state *st = fe->demodulator_priv; ++ u16 val = dib8000_read_word(st, 299) & 0xffef; ++ val |= (onoff & 0x1) << 4; ++ ++ dprintk("pid filter enabled %d", onoff); ++ return dib8000_write_word(st, 299, val); ++} ++EXPORT_SYMBOL(dib8000_pid_filter_ctrl); ++ ++int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ struct dib8000_state *st = fe->demodulator_priv; ++ dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); ++ return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); ++} ++EXPORT_SYMBOL(dib8000_pid_filter); ++ ++static const struct dvb_frontend_ops dib8000_ops = { ++ .delsys = { SYS_ISDBT }, ++ .info = { ++ .name = "DiBcom 8000 ISDB-T", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib8000_release, ++ ++ .init = dib8000_wakeup, ++ .sleep = dib8000_sleep, ++ ++ .set_frontend = dib8000_set_frontend, ++ .get_tune_settings = dib8000_fe_get_tune_settings, ++ .get_frontend = dib8000_get_frontend, ++ ++ .read_status = dib8000_read_status, ++ .read_ber = dib8000_read_ber, ++ .read_signal_strength = dib8000_read_signal_strength, ++ .read_snr = dib8000_read_snr, ++ .read_ucblocks = dib8000_read_unc_blocks, ++}; ++ ++struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) ++{ ++ struct dvb_frontend *fe; ++ struct dib8000_state *state; ++ ++ dprintk("dib8000_attach"); ++ ++ state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); ++ if (fe == NULL) ++ goto error; ++ ++ memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); ++ state->i2c.adap = i2c_adap; ++ state->i2c.addr = i2c_addr; ++ state->i2c.i2c_write_buffer = state->i2c_write_buffer; ++ state->i2c.i2c_read_buffer = state->i2c_read_buffer; ++ mutex_init(&state->i2c_buffer_lock); ++ state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock; ++ state->gpio_val = cfg->gpio_val; ++ state->gpio_dir = cfg->gpio_dir; ++ ++ /* Ensure the output mode remains at the previous default if it's ++ * not specifically set by the caller. ++ */ ++ if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) ++ state->cfg.output_mode = OUTMODE_MPEG2_FIFO; ++ ++ state->fe[0] = fe; ++ fe->demodulator_priv = state; ++ memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); ++ ++ state->timf_default = cfg->pll->timf; ++ ++ if (dib8000_identify(&state->i2c) == 0) ++ goto error; ++ ++ dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr); ++ ++ /* init 8096p tuner adapter */ ++ strncpy(state->dib8096p_tuner_adap.name, "DiB8096P tuner interface", ++ sizeof(state->dib8096p_tuner_adap.name)); ++ state->dib8096p_tuner_adap.algo = &dib8096p_tuner_xfer_algo; ++ state->dib8096p_tuner_adap.algo_data = NULL; ++ state->dib8096p_tuner_adap.dev.parent = state->i2c.adap->dev.parent; ++ i2c_set_adapdata(&state->dib8096p_tuner_adap, state); ++ i2c_add_adapter(&state->dib8096p_tuner_adap); ++ ++ dib8000_reset(fe); ++ ++ dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ ++ ++ return fe; ++ ++ error: ++ kfree(state); ++ return NULL; ++} ++ ++EXPORT_SYMBOL(dib8000_attach); ++ ++MODULE_AUTHOR("Olivier Grenie "); ++MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib8000.h b/drivers/media/dvb-frontends/dib8000.h +new file mode 100644 +index 0000000..39591bb +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib8000.h +@@ -0,0 +1,174 @@ ++#ifndef DIB8000_H ++#define DIB8000_H ++ ++#include "dibx000_common.h" ++ ++struct dib8000_config { ++ u8 output_mpeg2_in_188_bytes; ++ u8 hostbus_diversity; ++ u8 tuner_is_baseband; ++ int (*update_lna) (struct dvb_frontend *, u16 agc_global); ++ ++ u8 agc_config_count; ++ struct dibx000_agc_config *agc; ++ struct dibx000_bandwidth_config *pll; ++ ++#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff ++ u16 gpio_dir; ++#define DIB8000_GPIO_DEFAULT_VALUES 0x0000 ++ u16 gpio_val; ++#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12) ++#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) ++#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) ++#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf) ++#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff ++ u16 gpio_pwm_pos; ++ u16 pwm_freq_div; ++ ++ void (*agc_control) (struct dvb_frontend *, u8 before); ++ ++ u16 drives; ++ u16 diversity_delay; ++ u8 div_cfg; ++ u8 output_mode; ++ u8 refclksel; ++ u8 enMpegOutput:1; ++}; ++ ++#define DEFAULT_DIB8000_I2C_ADDRESS 18 ++ ++#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); ++extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); ++ ++extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, ++ u8 default_addr, u8 first_addr, u8 is_dib8096p); ++ ++extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); ++extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); ++extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff); ++extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); ++extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); ++extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe); ++extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe); ++extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode); ++extern struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe); ++extern int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff); ++extern int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ); ++extern u32 dib8000_ctrl_timf(struct dvb_frontend *fe, ++ uint8_t op, uint32_t timf); ++extern int dib8000_update_pll(struct dvb_frontend *fe, ++ struct dibx000_bandwidth_config *pll); ++extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); ++extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); ++extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); ++#else ++static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib8000_i2c_enumeration(struct i2c_adapter *host, ++ int no_of_demods, u8 default_addr, u8 first_addr, ++ u8 is_dib8096p) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++static inline int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++static inline enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return CT_SHUTDOWN; ++} ++static inline void dib8000_pwm_agc_reset(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++static inline struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++static inline int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++static inline u32 dib8000_ctrl_timf(struct dvb_frontend *fe, ++ uint8_t op, uint32_t timf) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++static inline int dib8000_update_pll(struct dvb_frontend *fe, ++ struct dibx000_bandwidth_config *pll) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++static inline int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++int dib8000_remove_slave_frontend(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c +new file mode 100644 +index 0000000..6201c59 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib9000.c +@@ -0,0 +1,2590 @@ ++/* ++ * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family. ++ * ++ * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2. ++ */ ++#include ++#include ++#include ++ ++#include "dvb_math.h" ++#include "dvb_frontend.h" ++ ++#include "dib9000.h" ++#include "dibx000_common.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0) ++#define MAX_NUMBER_OF_FRONTENDS 6 ++ ++struct i2c_device { ++ struct i2c_adapter *i2c_adap; ++ u8 i2c_addr; ++ u8 *i2c_read_buffer; ++ u8 *i2c_write_buffer; ++}; ++ ++struct dib9000_pid_ctrl { ++#define DIB9000_PID_FILTER_CTRL 0 ++#define DIB9000_PID_FILTER 1 ++ u8 cmd; ++ u8 id; ++ u16 pid; ++ u8 onoff; ++}; ++ ++struct dib9000_state { ++ struct i2c_device i2c; ++ ++ struct dibx000_i2c_master i2c_master; ++ struct i2c_adapter tuner_adap; ++ struct i2c_adapter component_bus; ++ ++ u16 revision; ++ u8 reg_offs; ++ ++ enum frontend_tune_state tune_state; ++ u32 status; ++ struct dvb_frontend_parametersContext channel_status; ++ ++ u8 fe_id; ++ ++#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff ++ u16 gpio_dir; ++#define DIB9000_GPIO_DEFAULT_VALUES 0x0000 ++ u16 gpio_val; ++#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff ++ u16 gpio_pwm_pos; ++ ++ union { /* common for all chips */ ++ struct { ++ u8 mobile_mode:1; ++ } host; ++ ++ struct { ++ struct dib9000_fe_memory_map { ++ u16 addr; ++ u16 size; ++ } fe_mm[18]; ++ u8 memcmd; ++ ++ struct mutex mbx_if_lock; /* to protect read/write operations */ ++ struct mutex mbx_lock; /* to protect the whole mailbox handling */ ++ ++ struct mutex mem_lock; /* to protect the memory accesses */ ++ struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */ ++ ++#define MBX_MAX_WORDS (256 - 200 - 2) ++#define DIB9000_MSG_CACHE_SIZE 2 ++ u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS]; ++ u8 fw_is_running; ++ } risc; ++ } platform; ++ ++ union { /* common for all platforms */ ++ struct { ++ struct dib9000_config cfg; ++ } d9; ++ } chip; ++ ++ struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; ++ u16 component_bus_speed; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[2]; ++ u8 i2c_write_buffer[255]; ++ u8 i2c_read_buffer[255]; ++ struct mutex demod_lock; ++ u8 get_frontend_internal; ++ struct dib9000_pid_ctrl pid_ctrl[10]; ++ s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ ++}; ++ ++static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0 ++}; ++ ++enum dib9000_power_mode { ++ DIB9000_POWER_ALL = 0, ++ ++ DIB9000_POWER_NO, ++ DIB9000_POWER_INTERF_ANALOG_AGC, ++ DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, ++ DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD, ++ DIB9000_POWER_INTERFACE_ONLY, ++}; ++ ++enum dib9000_out_messages { ++ OUT_MSG_HBM_ACK, ++ OUT_MSG_HOST_BUF_FAIL, ++ OUT_MSG_REQ_VERSION, ++ OUT_MSG_BRIDGE_I2C_W, ++ OUT_MSG_BRIDGE_I2C_R, ++ OUT_MSG_BRIDGE_APB_W, ++ OUT_MSG_BRIDGE_APB_R, ++ OUT_MSG_SCAN_CHANNEL, ++ OUT_MSG_MONIT_DEMOD, ++ OUT_MSG_CONF_GPIO, ++ OUT_MSG_DEBUG_HELP, ++ OUT_MSG_SUBBAND_SEL, ++ OUT_MSG_ENABLE_TIME_SLICE, ++ OUT_MSG_FE_FW_DL, ++ OUT_MSG_FE_CHANNEL_SEARCH, ++ OUT_MSG_FE_CHANNEL_TUNE, ++ OUT_MSG_FE_SLEEP, ++ OUT_MSG_FE_SYNC, ++ OUT_MSG_CTL_MONIT, ++ ++ OUT_MSG_CONF_SVC, ++ OUT_MSG_SET_HBM, ++ OUT_MSG_INIT_DEMOD, ++ OUT_MSG_ENABLE_DIVERSITY, ++ OUT_MSG_SET_OUTPUT_MODE, ++ OUT_MSG_SET_PRIORITARY_CHANNEL, ++ OUT_MSG_ACK_FRG, ++ OUT_MSG_INIT_PMU, ++}; ++ ++enum dib9000_in_messages { ++ IN_MSG_DATA, ++ IN_MSG_FRAME_INFO, ++ IN_MSG_CTL_MONIT, ++ IN_MSG_ACK_FREE_ITEM, ++ IN_MSG_DEBUG_BUF, ++ IN_MSG_MPE_MONITOR, ++ IN_MSG_RAWTS_MONITOR, ++ IN_MSG_END_BRIDGE_I2C_RW, ++ IN_MSG_END_BRIDGE_APB_RW, ++ IN_MSG_VERSION, ++ IN_MSG_END_OF_SCAN, ++ IN_MSG_MONIT_DEMOD, ++ IN_MSG_ERROR, ++ IN_MSG_FE_FW_DL_DONE, ++ IN_MSG_EVENT, ++ IN_MSG_ACK_CHANGE_SVC, ++ IN_MSG_HBM_PROF, ++}; ++ ++/* memory_access requests */ ++#define FE_MM_W_CHANNEL 0 ++#define FE_MM_W_FE_INFO 1 ++#define FE_MM_RW_SYNC 2 ++ ++#define FE_SYNC_CHANNEL 1 ++#define FE_SYNC_W_GENERIC_MONIT 2 ++#define FE_SYNC_COMPONENT_ACCESS 3 ++ ++#define FE_MM_R_CHANNEL_SEARCH_STATE 3 ++#define FE_MM_R_CHANNEL_UNION_CONTEXT 4 ++#define FE_MM_R_FE_INFO 5 ++#define FE_MM_R_FE_MONITOR 6 ++ ++#define FE_MM_W_CHANNEL_HEAD 7 ++#define FE_MM_W_CHANNEL_UNION 8 ++#define FE_MM_W_CHANNEL_CONTEXT 9 ++#define FE_MM_R_CHANNEL_UNION 10 ++#define FE_MM_R_CHANNEL_CONTEXT 11 ++#define FE_MM_R_CHANNEL_TUNE_STATE 12 ++ ++#define FE_MM_R_GENERIC_MONITORING_SIZE 13 ++#define FE_MM_W_GENERIC_MONITORING 14 ++#define FE_MM_R_GENERIC_MONITORING 15 ++ ++#define FE_MM_W_COMPONENT_ACCESS 16 ++#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17 ++static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len); ++static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len); ++ ++static u16 to_fw_output_mode(u16 mode) ++{ ++ switch (mode) { ++ case OUTMODE_HIGH_Z: ++ return 0; ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ return 4; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: ++ return 8; ++ case OUTMODE_MPEG2_SERIAL: ++ return 16; ++ case OUTMODE_DIVERSITY: ++ return 128; ++ case OUTMODE_MPEG2_FIFO: ++ return 2; ++ case OUTMODE_ANALOG_ADC: ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute) ++{ ++ u32 chunk_size = 126; ++ u32 l; ++ int ret; ++ ++ if (state->platform.risc.fw_is_running && (reg < 1024)) ++ return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len); ++ ++ memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c.i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = 2; ++ state->msg[1].addr = state->i2c.i2c_addr >> 1; ++ state->msg[1].flags = I2C_M_RD; ++ state->msg[1].buf = b; ++ state->msg[1].len = len; ++ ++ state->i2c_write_buffer[0] = reg >> 8; ++ state->i2c_write_buffer[1] = reg & 0xff; ++ ++ if (attribute & DATA_BUS_ACCESS_MODE_8BIT) ++ state->i2c_write_buffer[0] |= (1 << 5); ++ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++ state->i2c_write_buffer[0] |= (1 << 4); ++ ++ do { ++ l = len < chunk_size ? len : chunk_size; ++ state->msg[1].len = l; ++ state->msg[1].buf = b; ++ ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0; ++ if (ret != 0) { ++ dprintk("i2c read error on %d", reg); ++ return -EREMOTEIO; ++ } ++ ++ b += l; ++ len -= l; ++ ++ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) ++ reg += l / 2; ++ } while ((ret == 0) && len); ++ ++ return 0; ++} ++ ++static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg) ++{ ++ struct i2c_msg msg[2] = { ++ {.addr = i2c->i2c_addr >> 1, .flags = 0, ++ .buf = i2c->i2c_write_buffer, .len = 2}, ++ {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD, ++ .buf = i2c->i2c_read_buffer, .len = 2}, ++ }; ++ ++ i2c->i2c_write_buffer[0] = reg >> 8; ++ i2c->i2c_write_buffer[1] = reg & 0xff; ++ ++ if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) { ++ dprintk("read register %x error", reg); ++ return 0; ++ } ++ ++ return (i2c->i2c_read_buffer[0] << 8) | i2c->i2c_read_buffer[1]; ++} ++ ++static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg) ++{ ++ if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, 0) != 0) ++ return 0; ++ return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; ++} ++ ++static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute) ++{ ++ if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, ++ attribute) != 0) ++ return 0; ++ return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; ++} ++ ++#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++ ++static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute) ++{ ++ u32 chunk_size = 126; ++ u32 l; ++ int ret; ++ ++ if (state->platform.risc.fw_is_running && (reg < 1024)) { ++ if (dib9000_risc_apb_access_write ++ (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) ++ return -EINVAL; ++ return 0; ++ } ++ ++ memset(&state->msg[0], 0, sizeof(struct i2c_msg)); ++ state->msg[0].addr = state->i2c.i2c_addr >> 1; ++ state->msg[0].flags = 0; ++ state->msg[0].buf = state->i2c_write_buffer; ++ state->msg[0].len = len + 2; ++ ++ state->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ state->i2c_write_buffer[1] = (reg) & 0xff; ++ ++ if (attribute & DATA_BUS_ACCESS_MODE_8BIT) ++ state->i2c_write_buffer[0] |= (1 << 5); ++ if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++ state->i2c_write_buffer[0] |= (1 << 4); ++ ++ do { ++ l = len < chunk_size ? len : chunk_size; ++ state->msg[0].len = l + 2; ++ memcpy(&state->i2c_write_buffer[2], buf, l); ++ ++ ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; ++ ++ buf += l; ++ len -= l; ++ ++ if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) ++ reg += l / 2; ++ } while ((ret == 0) && len); ++ ++ return ret; ++} ++ ++static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) ++{ ++ struct i2c_msg msg = { ++ .addr = i2c->i2c_addr >> 1, .flags = 0, ++ .buf = i2c->i2c_write_buffer, .len = 4 ++ }; ++ ++ i2c->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ i2c->i2c_write_buffer[1] = reg & 0xff; ++ i2c->i2c_write_buffer[2] = (val >> 8) & 0xff; ++ i2c->i2c_write_buffer[3] = val & 0xff; ++ ++ return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; ++} ++ ++static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val) ++{ ++ u8 b[2] = { val >> 8, val & 0xff }; ++ return dib9000_write16_attr(state, reg, b, 2, 0); ++} ++ ++static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute) ++{ ++ u8 b[2] = { val >> 8, val & 0xff }; ++ return dib9000_write16_attr(state, reg, b, 2, attribute); ++} ++ ++#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0) ++#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++#define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute)) ++ ++#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0) ++#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0) ++ ++#define MAC_IRQ (1 << 1) ++#define IRQ_POL_MSK (1 << 4) ++ ++#define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++#define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) ++ ++static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading) ++{ ++ u8 b[14] = { 0 }; ++ ++/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */ ++/* b[0] = 0 << 7; */ ++ b[1] = 1; ++ ++/* b[2] = 0; */ ++/* b[3] = 0; */ ++ b[4] = (u8) (addr >> 8); ++ b[5] = (u8) (addr & 0xff); ++ ++/* b[10] = 0; */ ++/* b[11] = 0; */ ++ b[12] = (u8) (addr >> 8); ++ b[13] = (u8) (addr & 0xff); ++ ++ addr += len; ++/* b[6] = 0; */ ++/* b[7] = 0; */ ++ b[8] = (u8) (addr >> 8); ++ b[9] = (u8) (addr & 0xff); ++ ++ dib9000_write(state, 1056, b, 14); ++ if (reading) ++ dib9000_write_word(state, 1056, (1 << 15) | 1); ++ state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */ ++} ++ ++static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd) ++{ ++ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f]; ++ /* decide whether we need to "refresh" the memory controller */ ++ if (state->platform.risc.memcmd == cmd && /* same command */ ++ !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ ++ return; ++ dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80); ++ state->platform.risc.memcmd = cmd; ++} ++ ++static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len) ++{ ++ if (!state->platform.risc.fw_is_running) ++ return -EIO; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ dib9000_risc_mem_setup(state, cmd | 0x80); ++ dib9000_risc_mem_read_chunks(state, b, len); ++ mutex_unlock(&state->platform.risc.mem_lock); ++ return 0; ++} ++ ++static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b) ++{ ++ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd]; ++ if (!state->platform.risc.fw_is_running) ++ return -EIO; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ dib9000_risc_mem_setup(state, cmd); ++ dib9000_risc_mem_write_chunks(state, b, m->size); ++ mutex_unlock(&state->platform.risc.mem_lock); ++ return 0; ++} ++ ++static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len) ++{ ++ u16 offs; ++ ++ if (risc_id == 1) ++ offs = 16; ++ else ++ offs = 0; ++ ++ /* config crtl reg */ ++ dib9000_write_word(state, 1024 + offs, 0x000f); ++ dib9000_write_word(state, 1025 + offs, 0); ++ dib9000_write_word(state, 1031 + offs, key); ++ ++ dprintk("going to download %dB of microcode", len); ++ if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) { ++ dprintk("error while downloading microcode for RISC %c", 'A' + risc_id); ++ return -EIO; ++ } ++ ++ dprintk("Microcode for RISC %c loaded", 'A' + risc_id); ++ ++ return 0; ++} ++ ++static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id) ++{ ++ u16 mbox_offs; ++ u16 reset_reg; ++ u16 tries = 1000; ++ ++ if (risc_id == 1) ++ mbox_offs = 16; ++ else ++ mbox_offs = 0; ++ ++ /* Reset mailbox */ ++ dib9000_write_word(state, 1027 + mbox_offs, 0x8000); ++ ++ /* Read reset status */ ++ do { ++ reset_reg = dib9000_read_word(state, 1027 + mbox_offs); ++ msleep(100); ++ } while ((reset_reg & 0x8000) && --tries); ++ ++ if (reset_reg & 0x8000) { ++ dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id); ++ return -EIO; ++ } ++ dprintk("MBX: initialized"); ++ return 0; ++} ++ ++#define MAX_MAILBOX_TRY 100 ++static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr) ++{ ++ u8 *d, b[2]; ++ u16 tmp; ++ u16 size; ++ u32 i; ++ int ret = 0; ++ ++ if (!state->platform.risc.fw_is_running) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ tmp = MAX_MAILBOX_TRY; ++ do { ++ size = dib9000_read_word_attr(state, 1043, attr) & 0xff; ++ if ((size + len + 1) > MBX_MAX_WORDS && --tmp) { ++ dprintk("MBX: RISC mbx full, retrying"); ++ msleep(100); ++ } else ++ break; ++ } while (1); ++ ++ /*dprintk( "MBX: size: %d", size); */ ++ ++ if (tmp == 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++#ifdef DUMP_MSG ++ dprintk("--> %02x %d ", id, len + 1); ++ for (i = 0; i < len; i++) ++ dprintk("%04x ", data[i]); ++ dprintk("\n"); ++#endif ++ ++ /* byte-order conversion - works on big (where it is not necessary) or little endian */ ++ d = (u8 *) data; ++ for (i = 0; i < len; i++) { ++ tmp = data[i]; ++ *d++ = tmp >> 8; ++ *d++ = tmp & 0xff; ++ } ++ ++ /* write msg */ ++ b[0] = id; ++ b[1] = len + 1; ++ if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* update register nb_mes_in_RX */ ++ ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); ++ ++out: ++ mutex_unlock(&state->platform.risc.mbx_if_lock); ++ ++ return ret; ++} ++ ++static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr) ++{ ++#ifdef DUMP_MSG ++ u16 *d = data; ++#endif ++ ++ u16 tmp, i; ++ u8 size; ++ u8 mc_base; ++ ++ if (!state->platform.risc.fw_is_running) ++ return 0; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { ++ dprintk("could not get the lock"); ++ return 0; ++ } ++ if (risc_id == 1) ++ mc_base = 16; ++ else ++ mc_base = 0; ++ ++ /* Length and type in the first word */ ++ *data = dib9000_read_word_attr(state, 1029 + mc_base, attr); ++ ++ size = *data & 0xff; ++ if (size <= MBX_MAX_WORDS) { ++ data++; ++ size--; /* Initial word already read */ ++ ++ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr); ++ ++ /* to word conversion */ ++ for (i = 0; i < size; i++) { ++ tmp = *data; ++ *data = (tmp >> 8) | (tmp << 8); ++ data++; ++ } ++ ++#ifdef DUMP_MSG ++ dprintk("<-- "); ++ for (i = 0; i < size + 1; i++) ++ dprintk("%04x ", d[i]); ++ dprintk("\n"); ++#endif ++ } else { ++ dprintk("MBX: message is too big for message cache (%d), flushing message", size); ++ size--; /* Initial word already read */ ++ while (size--) ++ dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr); ++ } ++ /* Update register nb_mes_in_TX */ ++ dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); ++ ++ mutex_unlock(&state->platform.risc.mbx_if_lock); ++ ++ return size + 1; ++} ++ ++static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size) ++{ ++ u32 ts = data[1] << 16 | data[0]; ++ char *b = (char *)&data[2]; ++ ++ b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */ ++ if (*b == '~') { ++ b++; ++ dprintk(b); ++ } else ++ dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : ""); ++ return 1; ++} ++ ++static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr) ++{ ++ int i; ++ u8 size; ++ u16 *block; ++ /* find a free slot */ ++ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { ++ block = state->platform.risc.message_cache[i]; ++ if (*block == 0) { ++ size = dib9000_mbx_read(state, block, 1, attr); ++ ++/* dprintk( "MBX: fetched %04x message to cache", *block); */ ++ ++ switch (*block >> 8) { ++ case IN_MSG_DEBUG_BUF: ++ dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */ ++ *block = 0; /* free the block */ ++ break; ++#if 0 ++ case IN_MSG_DATA: /* FE-TRACE */ ++ dib9000_risc_data_process(state, block + 1, size); ++ *block = 0; ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ return 1; ++ } ++ } ++ dprintk("MBX: no free cache-slot found for new message..."); ++ return -1; ++} ++ ++static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) ++{ ++ if (risc_id == 0) ++ return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */ ++ else ++ return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */ ++} ++ ++static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) ++{ ++ int ret = 0; ++ ++ if (!state->platform.risc.fw_is_running) ++ return -1; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -1; ++ } ++ ++ if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ ++ ret = dib9000_mbx_fetch_to_cache(state, attr); ++ ++ dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ ++/* if (tmp) */ ++/* dprintk( "cleared IRQ: %x", tmp); */ ++ mutex_unlock(&state->platform.risc.mbx_lock); ++ ++ return ret; ++} ++ ++static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr) ++{ ++ u8 i; ++ u16 *block; ++ u16 timeout = 30; ++ ++ *msg = 0; ++ do { ++ /* dib9000_mbx_get_from_cache(); */ ++ for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { ++ block = state->platform.risc.message_cache[i]; ++ if ((*block >> 8) == id) { ++ *size = (*block & 0xff) - 1; ++ memcpy(msg, block + 1, (*size) * 2); ++ *block = 0; /* free the block */ ++ i = 0; /* signal that we found a message */ ++ break; ++ } ++ } ++ ++ if (i == 0) ++ break; ++ ++ if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */ ++ return -1; ++ ++ } while (--timeout); ++ ++ if (timeout == 0) { ++ dprintk("waiting for message %d timed out", id); ++ return -1; ++ } ++ ++ return i == 0; ++} ++ ++static int dib9000_risc_check_version(struct dib9000_state *state) ++{ ++ u8 r[4]; ++ u8 size; ++ u16 fw_version = 0; ++ ++ if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0) ++ return -EIO; ++ ++ if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0) ++ return -EIO; ++ ++ fw_version = (r[0] << 8) | r[1]; ++ dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]); ++ ++ if ((fw_version >> 10) != 7) ++ return -EINVAL; ++ ++ switch (fw_version & 0x3ff) { ++ case 11: ++ case 12: ++ case 14: ++ case 15: ++ case 16: ++ case 17: ++ break; ++ default: ++ dprintk("RISC: invalid firmware version"); ++ return -EINVAL; ++ } ++ ++ dprintk("RISC: valid firmware version"); ++ return 0; ++} ++ ++static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB) ++{ ++ /* Reconfig pool mac ram */ ++ dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */ ++ dib9000_write_word(state, 1226, 0x05); ++ ++ /* Toggles IP crypto to Host APB interface. */ ++ dib9000_write_word(state, 1542, 1); ++ ++ /* Set jump and no jump in the dma box */ ++ dib9000_write_word(state, 1074, 0); ++ dib9000_write_word(state, 1075, 0); ++ ++ /* Set MAC as APB Master. */ ++ dib9000_write_word(state, 1237, 0); ++ ++ /* Reset the RISCs */ ++ if (codeA != NULL) ++ dib9000_write_word(state, 1024, 2); ++ else ++ dib9000_write_word(state, 1024, 15); ++ if (codeB != NULL) ++ dib9000_write_word(state, 1040, 2); ++ ++ if (codeA != NULL) ++ dib9000_firmware_download(state, 0, 0x1234, codeA, lenA); ++ if (codeB != NULL) ++ dib9000_firmware_download(state, 1, 0x1234, codeB, lenB); ++ ++ /* Run the RISCs */ ++ if (codeA != NULL) ++ dib9000_write_word(state, 1024, 0); ++ if (codeB != NULL) ++ dib9000_write_word(state, 1040, 0); ++ ++ if (codeA != NULL) ++ if (dib9000_mbx_host_init(state, 0) != 0) ++ return -EIO; ++ if (codeB != NULL) ++ if (dib9000_mbx_host_init(state, 1) != 0) ++ return -EIO; ++ ++ msleep(100); ++ state->platform.risc.fw_is_running = 1; ++ ++ if (dib9000_risc_check_version(state) != 0) ++ return -EINVAL; ++ ++ state->platform.risc.memcmd = 0xff; ++ return 0; ++} ++ ++static u16 dib9000_identify(struct i2c_device *client) ++{ ++ u16 value; ++ ++ value = dib9000_i2c_read16(client, 896); ++ if (value != 0x01b3) { ++ dprintk("wrong Vendor ID (0x%x)", value); ++ return 0; ++ } ++ ++ value = dib9000_i2c_read16(client, 897); ++ if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) { ++ dprintk("wrong Device ID (0x%x)", value); ++ return 0; ++ } ++ ++ /* protect this driver to be used with 7000PC */ ++ if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) { ++ dprintk("this driver does not work with DiB7000PC"); ++ return 0; ++ } ++ ++ switch (value) { ++ case 0x4000: ++ dprintk("found DiB7000MA/PA/MB/PB"); ++ break; ++ case 0x4001: ++ dprintk("found DiB7000HC"); ++ break; ++ case 0x4002: ++ dprintk("found DiB7000MC"); ++ break; ++ case 0x4003: ++ dprintk("found DiB9000A"); ++ break; ++ case 0x4004: ++ dprintk("found DiB9000H"); ++ break; ++ case 0x4005: ++ dprintk("found DiB9000M"); ++ break; ++ } ++ ++ return value; ++} ++ ++static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode) ++{ ++ /* by default everything is going to be powered off */ ++ u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906; ++ u8 offset; ++ ++ if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005) ++ offset = 1; ++ else ++ offset = 0; ++ ++ reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */ ++ ++ /* now, depending on the requested mode, we power on */ ++ switch (mode) { ++ /* power up everything in the demod */ ++ case DIB9000_POWER_ALL: ++ reg_903 = 0x0000; ++ reg_904 = 0x0000; ++ reg_905 = 0x0000; ++ reg_906 = 0x0000; ++ break; ++ ++ /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ ++ case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ ++ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); ++ break; ++ ++ case DIB9000_POWER_INTERF_ANALOG_AGC: ++ reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); ++ reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); ++ reg_906 &= ~((1 << 0)); ++ break; ++ ++ case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: ++ reg_903 = 0x0000; ++ reg_904 = 0x801f; ++ reg_905 = 0x0000; ++ reg_906 &= ~((1 << 0)); ++ break; ++ ++ case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD: ++ reg_903 = 0x0000; ++ reg_904 = 0x8000; ++ reg_905 = 0x010b; ++ reg_906 &= ~((1 << 0)); ++ break; ++ default: ++ case DIB9000_POWER_NO: ++ break; ++ } ++ ++ /* always power down unused parts */ ++ if (!state->platform.host.mobile_mode) ++ reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); ++ ++ /* P_sdio_select_clk = 0 on MC and after */ ++ if (state->revision != 0x4000) ++ reg_906 <<= 1; ++ ++ dib9000_write_word(state, 903 + offset, reg_903); ++ dib9000_write_word(state, 904 + offset, reg_904); ++ dib9000_write_word(state, 905 + offset, reg_905); ++ dib9000_write_word(state, 906 + offset, reg_906); ++} ++ ++static int dib9000_fw_reset(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ ++ dib9000_write_word(state, 1817, 0x0003); ++ ++ dib9000_write_word(state, 1227, 1); ++ dib9000_write_word(state, 1227, 0); ++ ++ switch ((state->revision = dib9000_identify(&state->i2c))) { ++ case 0x4003: ++ case 0x4004: ++ case 0x4005: ++ state->reg_offs = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* reset the i2c-master to use the host interface */ ++ dibx000_reset_i2c_master(&state->i2c_master); ++ ++ dib9000_set_power_mode(state, DIB9000_POWER_ALL); ++ ++ /* unforce divstr regardless whether i2c enumeration was done or not */ ++ dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1)); ++ dib9000_write_word(state, 1796, 0); ++ dib9000_write_word(state, 1805, 0x805); ++ ++ /* restart all parts */ ++ dib9000_write_word(state, 898, 0xffff); ++ dib9000_write_word(state, 899, 0xffff); ++ dib9000_write_word(state, 900, 0x0001); ++ dib9000_write_word(state, 901, 0xff19); ++ dib9000_write_word(state, 902, 0x003c); ++ ++ dib9000_write_word(state, 898, 0); ++ dib9000_write_word(state, 899, 0); ++ dib9000_write_word(state, 900, 0); ++ dib9000_write_word(state, 901, 0); ++ dib9000_write_word(state, 902, 0); ++ ++ dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives); ++ ++ dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY); ++ ++ return 0; ++} ++ ++static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len) ++{ ++ u16 mb[10]; ++ u8 i, s; ++ ++ if (address >= 1024 || !state->platform.risc.fw_is_running) ++ return -EINVAL; ++ ++ /* dprintk( "APB access thru rd fw %d %x", address, attribute); */ ++ ++ mb[0] = (u16) address; ++ mb[1] = len / 2; ++ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute); ++ switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) { ++ case 1: ++ s--; ++ for (i = 0; i < s; i++) { ++ b[i * 2] = (mb[i + 1] >> 8) & 0xff; ++ b[i * 2 + 1] = (mb[i + 1]) & 0xff; ++ } ++ return 0; ++ default: ++ return -EIO; ++ } ++ return -EIO; ++} ++ ++static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len) ++{ ++ u16 mb[10]; ++ u8 s, i; ++ ++ if (address >= 1024 || !state->platform.risc.fw_is_running) ++ return -EINVAL; ++ ++ /* dprintk( "APB access thru wr fw %d %x", address, attribute); */ ++ ++ mb[0] = (unsigned short)address; ++ for (i = 0; i < len && i < 20; i += 2) ++ mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]); ++ ++ dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute); ++ return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL; ++} ++ ++static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i) ++{ ++ u8 index_loop = 10; ++ ++ if (!state->platform.risc.fw_is_running) ++ return 0; ++ dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i); ++ do { ++ dib9000_risc_mem_read(state, FE_MM_RW_SYNC, state->i2c_read_buffer, 1); ++ } while (state->i2c_read_buffer[0] && index_loop--); ++ ++ if (index_loop > 0) ++ return 0; ++ return -EIO; ++} ++ ++static int dib9000_fw_init(struct dib9000_state *state) ++{ ++ struct dibGPIOFunction *f; ++ u16 b[40] = { 0 }; ++ u8 i; ++ u8 size; ++ ++ if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0) ++ return -EIO; ++ ++ /* initialize the firmware */ ++ for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) { ++ f = &state->chip.d9.cfg.gpio_function[i]; ++ if (f->mask) { ++ switch (f->function) { ++ case BOARD_GPIO_FUNCTION_COMPONENT_ON: ++ b[0] = (u16) f->mask; ++ b[1] = (u16) f->direction; ++ b[2] = (u16) f->value; ++ break; ++ case BOARD_GPIO_FUNCTION_COMPONENT_OFF: ++ b[3] = (u16) f->mask; ++ b[4] = (u16) f->direction; ++ b[5] = (u16) f->value; ++ break; ++ } ++ } ++ } ++ if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0) ++ return -EIO; ++ ++ /* subband */ ++ b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */ ++ for (i = 0; i < state->chip.d9.cfg.subband.size; i++) { ++ b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz; ++ b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask; ++ b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction; ++ b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value; ++ } ++ b[1 + i * 4] = 0; /* fe_id */ ++ if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0) ++ return -EIO; ++ ++ /* 0 - id, 1 - no_of_frontends */ ++ b[0] = (0 << 8) | 1; ++ /* 0 = i2c-address demod, 0 = tuner */ ++ b[1] = (0 << 8) | (0); ++ b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff); ++ b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff); ++ b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff); ++ b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff); ++ b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff); ++ b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff); ++ b[29] = state->chip.d9.cfg.if_drives; ++ if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0) ++ return -EIO; ++ ++ if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0) ++ return -EIO; ++ ++ if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0) ++ return -EIO; ++ ++ if (size > ARRAY_SIZE(b)) { ++ dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, ++ (int)ARRAY_SIZE(b)); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < size; i += 2) { ++ state->platform.risc.fe_mm[i / 2].addr = b[i + 0]; ++ state->platform.risc.fe_mm[i / 2].size = b[i + 1]; ++ } ++ ++ return 0; ++} ++ ++static void dib9000_fw_set_channel_head(struct dib9000_state *state) ++{ ++ u8 b[9]; ++ u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000; ++ if (state->fe_id % 2) ++ freq += 101; ++ ++ b[0] = (u8) ((freq >> 0) & 0xff); ++ b[1] = (u8) ((freq >> 8) & 0xff); ++ b[2] = (u8) ((freq >> 16) & 0xff); ++ b[3] = (u8) ((freq >> 24) & 0xff); ++ b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff); ++ b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff); ++ b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff); ++ b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff); ++ b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */ ++ if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT) ++ b[8] |= 1; ++ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b); ++} ++ ++static int dib9000_fw_get_channel(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ struct dibDVBTChannel { ++ s8 spectrum_inversion; ++ ++ s8 nfft; ++ s8 guard; ++ s8 constellation; ++ ++ s8 hrch; ++ s8 alpha; ++ s8 code_rate_hp; ++ s8 code_rate_lp; ++ s8 select_hp; ++ ++ s8 intlv_native; ++ }; ++ struct dibDVBTChannel *ch; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ++ ret = -EIO; ++ goto error; ++ } ++ ++ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, ++ state->i2c_read_buffer, sizeof(struct dibDVBTChannel)); ++ ch = (struct dibDVBTChannel *)state->i2c_read_buffer; ++ ++ ++ switch (ch->spectrum_inversion & 0x7) { ++ case 1: ++ state->fe[0]->dtv_property_cache.inversion = INVERSION_ON; ++ break; ++ case 0: ++ state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO; ++ break; ++ } ++ switch (ch->nfft) { ++ case 0: ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 2: ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; ++ break; ++ case 1: ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; ++ break; ++ } ++ switch (ch->guard) { ++ case 0: ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; ++ break; ++ } ++ switch (ch->constellation) { ++ case 2: ++ state->fe[0]->dtv_property_cache.modulation = QAM_64; ++ break; ++ case 1: ++ state->fe[0]->dtv_property_cache.modulation = QAM_16; ++ break; ++ case 0: ++ state->fe[0]->dtv_property_cache.modulation = QPSK; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.modulation = QAM_AUTO; ++ break; ++ } ++ switch (ch->hrch) { ++ case 0: ++ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO; ++ break; ++ } ++ switch (ch->code_rate_hp) { ++ case 1: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2; ++ break; ++ case 2: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3; ++ break; ++ case 3: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4; ++ break; ++ case 5: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6; ++ break; ++ case 7: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO; ++ break; ++ } ++ switch (ch->code_rate_lp) { ++ case 1: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2; ++ break; ++ case 2: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3; ++ break; ++ case 3: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4; ++ break; ++ case 5: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6; ++ break; ++ case 7: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8; ++ break; ++ default: ++ case -1: ++ state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO; ++ break; ++ } ++ ++error: ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ return ret; ++} ++ ++static int dib9000_fw_set_channel_union(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ struct dibDVBTChannel { ++ s8 spectrum_inversion; ++ ++ s8 nfft; ++ s8 guard; ++ s8 constellation; ++ ++ s8 hrch; ++ s8 alpha; ++ s8 code_rate_hp; ++ s8 code_rate_lp; ++ s8 select_hp; ++ ++ s8 intlv_native; ++ }; ++ struct dibDVBTChannel ch; ++ ++ switch (state->fe[0]->dtv_property_cache.inversion) { ++ case INVERSION_ON: ++ ch.spectrum_inversion = 1; ++ break; ++ case INVERSION_OFF: ++ ch.spectrum_inversion = 0; ++ break; ++ default: ++ case INVERSION_AUTO: ++ ch.spectrum_inversion = -1; ++ break; ++ } ++ switch (state->fe[0]->dtv_property_cache.transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ ch.nfft = 0; ++ break; ++ case TRANSMISSION_MODE_4K: ++ ch.nfft = 2; ++ break; ++ case TRANSMISSION_MODE_8K: ++ ch.nfft = 1; ++ break; ++ default: ++ case TRANSMISSION_MODE_AUTO: ++ ch.nfft = 1; ++ break; ++ } ++ switch (state->fe[0]->dtv_property_cache.guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ ch.guard = 0; ++ break; ++ case GUARD_INTERVAL_1_16: ++ ch.guard = 1; ++ break; ++ case GUARD_INTERVAL_1_8: ++ ch.guard = 2; ++ break; ++ case GUARD_INTERVAL_1_4: ++ ch.guard = 3; ++ break; ++ default: ++ case GUARD_INTERVAL_AUTO: ++ ch.guard = -1; ++ break; ++ } ++ switch (state->fe[0]->dtv_property_cache.modulation) { ++ case QAM_64: ++ ch.constellation = 2; ++ break; ++ case QAM_16: ++ ch.constellation = 1; ++ break; ++ case QPSK: ++ ch.constellation = 0; ++ break; ++ default: ++ case QAM_AUTO: ++ ch.constellation = -1; ++ break; ++ } ++ switch (state->fe[0]->dtv_property_cache.hierarchy) { ++ case HIERARCHY_NONE: ++ ch.hrch = 0; ++ break; ++ case HIERARCHY_1: ++ case HIERARCHY_2: ++ case HIERARCHY_4: ++ ch.hrch = 1; ++ break; ++ default: ++ case HIERARCHY_AUTO: ++ ch.hrch = -1; ++ break; ++ } ++ ch.alpha = 1; ++ switch (state->fe[0]->dtv_property_cache.code_rate_HP) { ++ case FEC_1_2: ++ ch.code_rate_hp = 1; ++ break; ++ case FEC_2_3: ++ ch.code_rate_hp = 2; ++ break; ++ case FEC_3_4: ++ ch.code_rate_hp = 3; ++ break; ++ case FEC_5_6: ++ ch.code_rate_hp = 5; ++ break; ++ case FEC_7_8: ++ ch.code_rate_hp = 7; ++ break; ++ default: ++ case FEC_AUTO: ++ ch.code_rate_hp = -1; ++ break; ++ } ++ switch (state->fe[0]->dtv_property_cache.code_rate_LP) { ++ case FEC_1_2: ++ ch.code_rate_lp = 1; ++ break; ++ case FEC_2_3: ++ ch.code_rate_lp = 2; ++ break; ++ case FEC_3_4: ++ ch.code_rate_lp = 3; ++ break; ++ case FEC_5_6: ++ ch.code_rate_lp = 5; ++ break; ++ case FEC_7_8: ++ ch.code_rate_lp = 7; ++ break; ++ default: ++ case FEC_AUTO: ++ ch.code_rate_lp = -1; ++ break; ++ } ++ ch.select_hp = 1; ++ ch.intlv_native = 1; ++ ++ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch); ++ ++ return 0; ++} ++ ++static int dib9000_fw_tune(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN; ++ s8 i; ++ ++ switch (state->tune_state) { ++ case CT_DEMOD_START: ++ dib9000_fw_set_channel_head(state); ++ ++ /* write the channel context - a channel is initialized to 0, so it is OK */ ++ dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info); ++ dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info); ++ ++ if (search) ++ dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0); ++ else { ++ dib9000_fw_set_channel_union(fe); ++ dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0); ++ } ++ state->tune_state = CT_DEMOD_STEP_1; ++ break; ++ case CT_DEMOD_STEP_1: ++ if (search) ++ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, state->i2c_read_buffer, 1); ++ else ++ dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, state->i2c_read_buffer, 1); ++ i = (s8)state->i2c_read_buffer[0]; ++ switch (i) { /* something happened */ ++ case 0: ++ break; ++ case -2: /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */ ++ if (search) ++ state->status = FE_STATUS_DEMOD_SUCCESS; ++ else { ++ state->tune_state = CT_DEMOD_STOP; ++ state->status = FE_STATUS_LOCKED; ++ } ++ break; ++ default: ++ state->status = FE_STATUS_TUNE_FAILED; ++ state->tune_state = CT_DEMOD_STOP; ++ break; ++ } ++ break; ++ default: ++ ret = FE_CALLBACK_TIME_NEVER; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 mode = (u16) onoff; ++ return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1); ++} ++ ++static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 outreg, smo_mode; ++ ++ dprintk("setting output mode for demod %p to %d", fe, mode); ++ ++ switch (mode) { ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ outreg = (1 << 10); /* 0x0400 */ ++ break; ++ case OUTMODE_MPEG2_PAR_CONT_CLK: ++ outreg = (1 << 10) | (1 << 6); /* 0x0440 */ ++ break; ++ case OUTMODE_MPEG2_SERIAL: ++ outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ ++ break; ++ case OUTMODE_DIVERSITY: ++ outreg = (1 << 10) | (4 << 6); /* 0x0500 */ ++ break; ++ case OUTMODE_MPEG2_FIFO: ++ outreg = (1 << 10) | (5 << 6); ++ break; ++ case OUTMODE_HIGH_Z: ++ outreg = 0; ++ break; ++ default: ++ dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]); ++ return -EINVAL; ++ } ++ ++ dib9000_write_word(state, 1795, outreg); ++ ++ switch (mode) { ++ case OUTMODE_MPEG2_PAR_GATED_CLK: ++ case OUTMODE_MPEG2_PAR_CONT_CLK: ++ case OUTMODE_MPEG2_SERIAL: ++ case OUTMODE_MPEG2_FIFO: ++ smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1); ++ if (state->chip.d9.cfg.output_mpeg2_in_188_bytes) ++ smo_mode |= (1 << 5); ++ dib9000_write_word(state, 295, smo_mode); ++ break; ++ } ++ ++ outreg = to_fw_output_mode(mode); ++ return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1); ++} ++ ++static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dib9000_state *state = i2c_get_adapdata(i2c_adap); ++ u16 i, len, t, index_msg; ++ ++ for (index_msg = 0; index_msg < num; index_msg++) { ++ if (msg[index_msg].flags & I2C_M_RD) { /* read */ ++ len = msg[index_msg].len; ++ if (len > 16) ++ len = 16; ++ ++ if (dib9000_read_word(state, 790) != 0) ++ dprintk("TunerITF: read busy"); ++ ++ dib9000_write_word(state, 784, (u16) (msg[index_msg].addr)); ++ dib9000_write_word(state, 787, (len / 2) - 1); ++ dib9000_write_word(state, 786, 1); /* start read */ ++ ++ i = 1000; ++ while (dib9000_read_word(state, 790) != (len / 2) && i) ++ i--; ++ ++ if (i == 0) ++ dprintk("TunerITF: read failed"); ++ ++ for (i = 0; i < len; i += 2) { ++ t = dib9000_read_word(state, 785); ++ msg[index_msg].buf[i] = (t >> 8) & 0xff; ++ msg[index_msg].buf[i + 1] = (t) & 0xff; ++ } ++ if (dib9000_read_word(state, 790) != 0) ++ dprintk("TunerITF: read more data than expected"); ++ } else { ++ i = 1000; ++ while (dib9000_read_word(state, 789) && i) ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: write busy"); ++ ++ len = msg[index_msg].len; ++ if (len > 16) ++ len = 16; ++ ++ for (i = 0; i < len; i += 2) ++ dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]); ++ dib9000_write_word(state, 784, (u16) msg[index_msg].addr); ++ dib9000_write_word(state, 787, (len / 2) - 1); ++ dib9000_write_word(state, 786, 0); /* start write */ ++ ++ i = 1000; ++ while (dib9000_read_word(state, 791) > 0 && i) ++ i--; ++ if (i == 0) ++ dprintk("TunerITF: write failed"); ++ } ++ } ++ return num; ++} ++ ++int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ ++ state->component_bus_speed = speed; ++ return 0; ++} ++EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed); ++ ++static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dib9000_state *state = i2c_get_adapdata(i2c_adap); ++ u8 type = 0; /* I2C */ ++ u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4; ++ u16 scl = state->component_bus_speed; /* SCL frequency */ ++ struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER]; ++ u8 p[13] = { 0 }; ++ ++ p[0] = type; ++ p[1] = port; ++ p[2] = msg[0].addr << 1; ++ ++ p[3] = (u8) scl & 0xff; /* scl */ ++ p[4] = (u8) (scl >> 8); ++ ++ p[7] = 0; ++ p[8] = 0; ++ ++ p[9] = (u8) (msg[0].len); ++ p[10] = (u8) (msg[0].len >> 8); ++ if ((num > 1) && (msg[1].flags & I2C_M_RD)) { ++ p[11] = (u8) (msg[1].len); ++ p[12] = (u8) (msg[1].len >> 8); ++ } else { ++ p[11] = 0; ++ p[12] = 0; ++ } ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ return 0; ++ } ++ ++ dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p); ++ ++ { /* write-part */ ++ dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0); ++ dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len); ++ } ++ ++ /* do the transaction */ ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ return 0; ++ } ++ ++ /* read back any possible result */ ++ if ((num > 1) && (msg[1].flags & I2C_M_RD)) ++ dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); ++ ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ++ return num; ++} ++ ++static u32 dib9000_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm dib9000_tuner_algo = { ++ .master_xfer = dib9000_tuner_xfer, ++ .functionality = dib9000_i2c_func, ++}; ++ ++static struct i2c_algorithm dib9000_component_bus_algo = { ++ .master_xfer = dib9000_fw_component_bus_xfer, ++ .functionality = dib9000_i2c_func, ++}; ++ ++struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *st = fe->demodulator_priv; ++ return &st->tuner_adap; ++} ++EXPORT_SYMBOL(dib9000_get_tuner_interface); ++ ++struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *st = fe->demodulator_priv; ++ return &st->component_bus; ++} ++EXPORT_SYMBOL(dib9000_get_component_bus_interface); ++ ++struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) ++{ ++ struct dib9000_state *st = fe->demodulator_priv; ++ return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); ++} ++EXPORT_SYMBOL(dib9000_get_i2c_master); ++ ++int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) ++{ ++ struct dib9000_state *st = fe->demodulator_priv; ++ ++ st->i2c.i2c_adap = i2c; ++ return 0; ++} ++EXPORT_SYMBOL(dib9000_set_i2c_adapter); ++ ++static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val) ++{ ++ st->gpio_dir = dib9000_read_word(st, 773); ++ st->gpio_dir &= ~(1 << num); /* reset the direction bit */ ++ st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ ++ dib9000_write_word(st, 773, st->gpio_dir); ++ ++ st->gpio_val = dib9000_read_word(st, 774); ++ st->gpio_val &= ~(1 << num); /* reset the direction bit */ ++ st->gpio_val |= (val & 0x01) << num; /* set the new value */ ++ dib9000_write_word(st, 774, st->gpio_val); ++ ++ dprintk("gpio dir: %04x: gpio val: %04x", st->gpio_dir, st->gpio_val); ++ ++ return 0; ++} ++ ++int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ return dib9000_cfg_gpio(state, num, dir, val); ++} ++EXPORT_SYMBOL(dib9000_set_gpio); ++ ++int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 val; ++ int ret; ++ ++ if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) { ++ /* postpone the pid filtering cmd */ ++ dprintk("pid filter cmd postpone"); ++ state->pid_ctrl_index++; ++ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL; ++ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; ++ return 0; ++ } ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ ++ val = dib9000_read_word(state, 294 + 1) & 0xffef; ++ val |= (onoff & 0x1) << 4; ++ ++ dprintk("PID filter enabled %d", onoff); ++ ret = dib9000_write_word(state, 294 + 1, val); ++ mutex_unlock(&state->demod_lock); ++ return ret; ++ ++} ++EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); ++ ++int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ int ret; ++ ++ if (state->pid_ctrl_index != -2) { ++ /* postpone the pid filtering cmd */ ++ dprintk("pid filter postpone"); ++ if (state->pid_ctrl_index < 9) { ++ state->pid_ctrl_index++; ++ state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER; ++ state->pid_ctrl[state->pid_ctrl_index].id = id; ++ state->pid_ctrl[state->pid_ctrl_index].pid = pid; ++ state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; ++ } else ++ dprintk("can not add any more pid ctrl cmd"); ++ return 0; ++ } ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); ++ ret = dib9000_write_word(state, 300 + 1 + id, ++ onoff ? (1 << 13) | pid : 0); ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++EXPORT_SYMBOL(dib9000_fw_pid_filter); ++ ++int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ return dib9000_fw_init(state); ++} ++EXPORT_SYMBOL(dib9000_firmware_post_pll_init); ++ ++static void dib9000_release(struct dvb_frontend *demod) ++{ ++ struct dib9000_state *st = demod->demodulator_priv; ++ u8 index_frontend; ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) ++ dvb_frontend_detach(st->fe[index_frontend]); ++ ++ dibx000_exit_i2c_master(&st->i2c_master); ++ ++ i2c_del_adapter(&st->tuner_adap); ++ i2c_del_adapter(&st->component_bus); ++ kfree(st->fe[0]); ++ kfree(st); ++} ++ ++static int dib9000_wakeup(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int dib9000_sleep(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); ++ if (ret < 0) ++ goto error; ++ } ++ ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); ++ ++error: ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++ ++static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static int dib9000_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend, sub_index_frontend; ++ fe_status_t stat; ++ int ret = 0; ++ ++ if (state->get_frontend_internal == 0) { ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ } ++ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); ++ if (stat & FE_HAS_SYNC) { ++ dprintk("TPS lock on the slave%i", index_frontend); ++ ++ /* synchronize the cache with the other frontends */ ++ state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]); ++ for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); ++ sub_index_frontend++) { ++ if (sub_index_frontend != index_frontend) { ++ state->fe[sub_index_frontend]->dtv_property_cache.modulation = ++ state->fe[index_frontend]->dtv_property_cache.modulation; ++ state->fe[sub_index_frontend]->dtv_property_cache.inversion = ++ state->fe[index_frontend]->dtv_property_cache.inversion; ++ state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = ++ state->fe[index_frontend]->dtv_property_cache.transmission_mode; ++ state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = ++ state->fe[index_frontend]->dtv_property_cache.guard_interval; ++ state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = ++ state->fe[index_frontend]->dtv_property_cache.hierarchy; ++ state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = ++ state->fe[index_frontend]->dtv_property_cache.code_rate_HP; ++ state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = ++ state->fe[index_frontend]->dtv_property_cache.code_rate_LP; ++ state->fe[sub_index_frontend]->dtv_property_cache.rolloff = ++ state->fe[index_frontend]->dtv_property_cache.rolloff; ++ } ++ } ++ ret = 0; ++ goto return_value; ++ } ++ } ++ ++ /* get the channel from master chip */ ++ ret = dib9000_fw_get_channel(fe); ++ if (ret != 0) ++ goto return_value; ++ ++ /* synchronize the cache with the other frontends */ ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; ++ state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; ++ state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; ++ state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation; ++ state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy; ++ state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP; ++ state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP; ++ state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff; ++ } ++ ret = 0; ++ ++return_value: ++ if (state->get_frontend_internal == 0) ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++ ++static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ state->tune_state = tune_state; ++ if (tune_state == CT_DEMOD_START) ++ state->status = FE_STATUS_TUNE_PENDING; ++ ++ return 0; ++} ++ ++static u32 dib9000_get_status(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ return state->status; ++} ++ ++static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ ++ memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext)); ++ return 0; ++} ++ ++static int dib9000_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ int sleep_time, sleep_time_slave; ++ u32 frontend_status; ++ u8 nbr_pending, exit_condition, index_frontend, index_frontend_success; ++ struct dvb_frontend_parametersContext channel_status; ++ ++ /* check that the correct parameters are set */ ++ if (state->fe[0]->dtv_property_cache.frequency == 0) { ++ dprintk("dib9000: must specify frequency "); ++ return 0; ++ } ++ ++ if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { ++ dprintk("dib9000: must specify bandwidth "); ++ return 0; ++ } ++ ++ state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return 0; ++ } ++ ++ fe->dtv_property_cache.delivery_system = SYS_DVBT; ++ ++ /* set the master status */ ++ if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO || ++ state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO || ++ state->fe[0]->dtv_property_cache.modulation == QAM_AUTO || ++ state->fe[0]->dtv_property_cache.code_rate_HP == FEC_AUTO) { ++ /* no channel specified, autosearch the channel */ ++ state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN; ++ } else ++ state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; ++ ++ /* set mode and status for the different frontends */ ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ dib9000_fw_set_diversity_in(state->fe[index_frontend], 1); ++ ++ /* synchronization of the cache */ ++ memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); ++ ++ state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT; ++ dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z); ++ ++ dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status); ++ dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); ++ } ++ ++ /* actual tune */ ++ exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ ++ index_frontend_success = 0; ++ do { ++ sleep_time = dib9000_fw_tune(state->fe[0]); ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); ++ if (sleep_time == FE_CALLBACK_TIME_NEVER) ++ sleep_time = sleep_time_slave; ++ else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) ++ sleep_time = sleep_time_slave; ++ } ++ if (sleep_time != FE_CALLBACK_TIME_NEVER) ++ msleep(sleep_time / 10); ++ else ++ break; ++ ++ nbr_pending = 0; ++ exit_condition = 0; ++ index_frontend_success = 0; ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ frontend_status = -dib9000_get_status(state->fe[index_frontend]); ++ if (frontend_status > -FE_STATUS_TUNE_PENDING) { ++ exit_condition = 2; /* tune success */ ++ index_frontend_success = index_frontend; ++ break; ++ } ++ if (frontend_status == -FE_STATUS_TUNE_PENDING) ++ nbr_pending++; /* some frontends are still tuning */ ++ } ++ if ((exit_condition != 2) && (nbr_pending == 0)) ++ exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ ++ ++ } while (exit_condition == 0); ++ ++ /* check the tune result */ ++ if (exit_condition == 1) { /* tune failed */ ++ dprintk("tune failed"); ++ mutex_unlock(&state->demod_lock); ++ /* tune failed; put all the pid filtering cmd to junk */ ++ state->pid_ctrl_index = -1; ++ return 0; ++ } ++ ++ dprintk("tune success on frontend%i", index_frontend_success); ++ ++ /* synchronize all the channel cache */ ++ state->get_frontend_internal = 1; ++ dib9000_get_frontend(state->fe[0]); ++ state->get_frontend_internal = 0; ++ ++ /* retune the other frontends with the found channel */ ++ channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ /* only retune the frontends which was not tuned success */ ++ if (index_frontend != index_frontend_success) { ++ dib9000_set_channel_status(state->fe[index_frontend], &channel_status); ++ dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); ++ } ++ } ++ do { ++ sleep_time = FE_CALLBACK_TIME_NEVER; ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ if (index_frontend != index_frontend_success) { ++ sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); ++ if (sleep_time == FE_CALLBACK_TIME_NEVER) ++ sleep_time = sleep_time_slave; ++ else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) ++ sleep_time = sleep_time_slave; ++ } ++ } ++ if (sleep_time != FE_CALLBACK_TIME_NEVER) ++ msleep(sleep_time / 10); ++ else ++ break; ++ ++ nbr_pending = 0; ++ for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ if (index_frontend != index_frontend_success) { ++ frontend_status = -dib9000_get_status(state->fe[index_frontend]); ++ if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING)) ++ nbr_pending++; /* some frontends are still tuning */ ++ } ++ } ++ } while (nbr_pending != 0); ++ ++ /* set the output mode */ ++ dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode); ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); ++ ++ /* turn off the diversity for the last frontend */ ++ dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); ++ ++ mutex_unlock(&state->demod_lock); ++ if (state->pid_ctrl_index >= 0) { ++ u8 index_pid_filter_cmd; ++ u8 pid_ctrl_index = state->pid_ctrl_index; ++ ++ state->pid_ctrl_index = -2; ++ for (index_pid_filter_cmd = 0; ++ index_pid_filter_cmd <= pid_ctrl_index; ++ index_pid_filter_cmd++) { ++ if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL) ++ dib9000_fw_pid_filter_ctrl(state->fe[0], ++ state->pid_ctrl[index_pid_filter_cmd].onoff); ++ else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER) ++ dib9000_fw_pid_filter(state->fe[0], ++ state->pid_ctrl[index_pid_filter_cmd].id, ++ state->pid_ctrl[index_pid_filter_cmd].pid, ++ state->pid_ctrl[index_pid_filter_cmd].onoff); ++ } ++ } ++ /* do not postpone any more the pid filtering */ ++ state->pid_ctrl_index = -2; ++ ++ return 0; ++} ++ ++static u16 dib9000_read_lock(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ ++ return dib9000_read_word(state, 535); ++} ++ ++static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ u16 lock = 0, lock_slave = 0; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ lock_slave |= dib9000_read_lock(state->fe[index_frontend]); ++ ++ lock = dib9000_read_word(state, 535); ++ ++ *stat = 0; ++ ++ if ((lock & 0x8000) || (lock_slave & 0x8000)) ++ *stat |= FE_HAS_SIGNAL; ++ if ((lock & 0x3000) || (lock_slave & 0x3000)) ++ *stat |= FE_HAS_CARRIER; ++ if ((lock & 0x0100) || (lock_slave & 0x0100)) ++ *stat |= FE_HAS_VITERBI; ++ if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38)) ++ *stat |= FE_HAS_SYNC; ++ if ((lock & 0x0008) || (lock_slave & 0x0008)) ++ *stat |= FE_HAS_LOCK; ++ ++ mutex_unlock(&state->demod_lock); ++ ++ return 0; ++} ++ ++static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 *c; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ ret = -EINTR; ++ goto error; ++ } ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ret = -EIO; ++ goto error; ++ } ++ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, ++ state->i2c_read_buffer, 16 * 2); ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ++ c = (u16 *)state->i2c_read_buffer; ++ ++ *ber = c[10] << 16 | c[11]; ++ ++error: ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++ ++static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ u16 *c = (u16 *)state->i2c_read_buffer; ++ u16 val; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ *strength = 0; ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { ++ state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); ++ if (val > 65535 - *strength) ++ *strength = 65535; ++ else ++ *strength += val; ++ } ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ ret = -EINTR; ++ goto error; ++ } ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ret = -EIO; ++ goto error; ++ } ++ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ++ val = 65535 - c[4]; ++ if (val > 65535 - *strength) ++ *strength = 65535; ++ else ++ *strength += val; ++ ++error: ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++ ++static u32 dib9000_get_snr(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 *c = (u16 *)state->i2c_read_buffer; ++ u32 n, s, exp; ++ u16 val; ++ ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ return 0; ++ } ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ return 0; ++ } ++ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ++ val = c[7]; ++ n = (val >> 4) & 0xff; ++ exp = ((val & 0xf) << 2); ++ val = c[8]; ++ exp += ((val >> 14) & 0x3); ++ if ((exp & 0x20) != 0) ++ exp -= 0x40; ++ n <<= exp + 16; ++ ++ s = (val >> 6) & 0xFF; ++ exp = (val & 0x3F); ++ if ((exp & 0x20) != 0) ++ exp -= 0x40; ++ s <<= exp + 16; ++ ++ if (n > 0) { ++ u32 t = (s / n) << 16; ++ return t + ((s << 16) - n * t) / n; ++ } ++ return 0xffffffff; ++} ++ ++static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend; ++ u32 snr_master; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ snr_master = dib9000_get_snr(fe); ++ for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) ++ snr_master += dib9000_get_snr(state->fe[index_frontend]); ++ ++ if ((snr_master >> 16) != 0) { ++ snr_master = 10 * intlog10(snr_master >> 16); ++ *snr = snr_master / ((1 << 24) / 10); ++ } else ++ *snr = 0; ++ ++ mutex_unlock(&state->demod_lock); ++ ++ return 0; ++} ++ ++static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u16 *c = (u16 *)state->i2c_read_buffer; ++ int ret = 0; ++ ++ if (mutex_lock_interruptible(&state->demod_lock) < 0) { ++ dprintk("could not get the lock"); ++ return -EINTR; ++ } ++ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { ++ dprintk("could not get the lock"); ++ ret = -EINTR; ++ goto error; ++ } ++ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ret = -EIO; ++ goto error; ++ } ++ dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); ++ mutex_unlock(&state->platform.risc.mem_mbx_lock); ++ ++ *unc = c[12]; ++ ++error: ++ mutex_unlock(&state->demod_lock); ++ return ret; ++} ++ ++int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr) ++{ ++ int k = 0, ret = 0; ++ u8 new_addr = 0; ++ struct i2c_device client = {.i2c_adap = i2c }; ++ ++ client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); ++ if (!client.i2c_write_buffer) { ++ dprintk("%s: not enough memory", __func__); ++ return -ENOMEM; ++ } ++ client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); ++ if (!client.i2c_read_buffer) { ++ dprintk("%s: not enough memory", __func__); ++ ret = -ENOMEM; ++ goto error_memory; ++ } ++ ++ client.i2c_addr = default_addr + 16; ++ dib9000_i2c_write16(&client, 1796, 0x0); ++ ++ for (k = no_of_demods - 1; k >= 0; k--) { ++ /* designated i2c address */ ++ new_addr = first_addr + (k << 1); ++ client.i2c_addr = default_addr; ++ ++ dib9000_i2c_write16(&client, 1817, 3); ++ dib9000_i2c_write16(&client, 1796, 0); ++ dib9000_i2c_write16(&client, 1227, 1); ++ dib9000_i2c_write16(&client, 1227, 0); ++ ++ client.i2c_addr = new_addr; ++ dib9000_i2c_write16(&client, 1817, 3); ++ dib9000_i2c_write16(&client, 1796, 0); ++ dib9000_i2c_write16(&client, 1227, 1); ++ dib9000_i2c_write16(&client, 1227, 0); ++ ++ if (dib9000_identify(&client) == 0) { ++ client.i2c_addr = default_addr; ++ if (dib9000_identify(&client) == 0) { ++ dprintk("DiB9000 #%d: not identified", k); ++ ret = -EIO; ++ goto error; ++ } ++ } ++ ++ dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6)); ++ dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2); ++ ++ dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); ++ } ++ ++ for (k = 0; k < no_of_demods; k++) { ++ new_addr = first_addr | (k << 1); ++ client.i2c_addr = new_addr; ++ ++ dib9000_i2c_write16(&client, 1794, (new_addr << 2)); ++ dib9000_i2c_write16(&client, 1795, 0); ++ } ++ ++error: ++ kfree(client.i2c_read_buffer); ++error_memory: ++ kfree(client.i2c_write_buffer); ++ ++ return ret; ++} ++EXPORT_SYMBOL(dib9000_i2c_enumeration); ++ ++int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend = 1; ++ ++ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) ++ index_frontend++; ++ if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { ++ dprintk("set slave fe %p to index %i", fe_slave, index_frontend); ++ state->fe[index_frontend] = fe_slave; ++ return 0; ++ } ++ ++ dprintk("too many slave frontend"); ++ return -ENOMEM; ++} ++EXPORT_SYMBOL(dib9000_set_slave_frontend); ++ ++int dib9000_remove_slave_frontend(struct dvb_frontend *fe) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ u8 index_frontend = 1; ++ ++ while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) ++ index_frontend++; ++ if (index_frontend != 1) { ++ dprintk("remove slave fe %p (index %i)", state->fe[index_frontend - 1], index_frontend - 1); ++ state->fe[index_frontend] = NULL; ++ return 0; ++ } ++ ++ dprintk("no frontend to be removed"); ++ return -ENODEV; ++} ++EXPORT_SYMBOL(dib9000_remove_slave_frontend); ++ ++struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) ++{ ++ struct dib9000_state *state = fe->demodulator_priv; ++ ++ if (slave_index >= MAX_NUMBER_OF_FRONTENDS) ++ return NULL; ++ return state->fe[slave_index]; ++} ++EXPORT_SYMBOL(dib9000_get_slave_frontend); ++ ++static struct dvb_frontend_ops dib9000_ops; ++struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg) ++{ ++ struct dvb_frontend *fe; ++ struct dib9000_state *st; ++ st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL); ++ if (st == NULL) ++ return NULL; ++ fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); ++ if (fe == NULL) { ++ kfree(st); ++ return NULL; ++ } ++ ++ memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config)); ++ st->i2c.i2c_adap = i2c_adap; ++ st->i2c.i2c_addr = i2c_addr; ++ st->i2c.i2c_write_buffer = st->i2c_write_buffer; ++ st->i2c.i2c_read_buffer = st->i2c_read_buffer; ++ ++ st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS; ++ st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; ++ st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; ++ ++ mutex_init(&st->platform.risc.mbx_if_lock); ++ mutex_init(&st->platform.risc.mbx_lock); ++ mutex_init(&st->platform.risc.mem_lock); ++ mutex_init(&st->platform.risc.mem_mbx_lock); ++ mutex_init(&st->demod_lock); ++ st->get_frontend_internal = 0; ++ ++ st->pid_ctrl_index = -2; ++ ++ st->fe[0] = fe; ++ fe->demodulator_priv = st; ++ memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops)); ++ ++ /* Ensure the output mode remains at the previous default if it's ++ * not specifically set by the caller. ++ */ ++ if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) ++ st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO; ++ ++ if (dib9000_identify(&st->i2c) == 0) ++ goto error; ++ ++ dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr); ++ ++ st->tuner_adap.dev.parent = i2c_adap->dev.parent; ++ strncpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", sizeof(st->tuner_adap.name)); ++ st->tuner_adap.algo = &dib9000_tuner_algo; ++ st->tuner_adap.algo_data = NULL; ++ i2c_set_adapdata(&st->tuner_adap, st); ++ if (i2c_add_adapter(&st->tuner_adap) < 0) ++ goto error; ++ ++ st->component_bus.dev.parent = i2c_adap->dev.parent; ++ strncpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", sizeof(st->component_bus.name)); ++ st->component_bus.algo = &dib9000_component_bus_algo; ++ st->component_bus.algo_data = NULL; ++ st->component_bus_speed = 340; ++ i2c_set_adapdata(&st->component_bus, st); ++ if (i2c_add_adapter(&st->component_bus) < 0) ++ goto component_bus_add_error; ++ ++ dib9000_fw_reset(fe); ++ ++ return fe; ++ ++component_bus_add_error: ++ i2c_del_adapter(&st->tuner_adap); ++error: ++ kfree(st); ++ return NULL; ++} ++EXPORT_SYMBOL(dib9000_attach); ++ ++static struct dvb_frontend_ops dib9000_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DiBcom 9000", ++ .frequency_min = 44250000, ++ .frequency_max = 867250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dib9000_release, ++ ++ .init = dib9000_wakeup, ++ .sleep = dib9000_sleep, ++ ++ .set_frontend = dib9000_set_frontend, ++ .get_tune_settings = dib9000_fe_get_tune_settings, ++ .get_frontend = dib9000_get_frontend, ++ ++ .read_status = dib9000_read_status, ++ .read_ber = dib9000_read_ber, ++ .read_signal_strength = dib9000_read_signal_strength, ++ .read_snr = dib9000_read_snr, ++ .read_ucblocks = dib9000_read_unc_blocks, ++}; ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_AUTHOR("Olivier Grenie "); ++MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dib9000.h b/drivers/media/dvb-frontends/dib9000.h +new file mode 100644 +index 0000000..de1cc91 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dib9000.h +@@ -0,0 +1,131 @@ ++#ifndef DIB9000_H ++#define DIB9000_H ++ ++#include "dibx000_common.h" ++ ++struct dib9000_config { ++ u8 dvbt_mode; ++ u8 output_mpeg2_in_188_bytes; ++ u8 hostbus_diversity; ++ struct dibx000_bandwidth_config *bw; ++ ++ u16 if_drives; ++ ++ u32 timing_frequency; ++ u32 xtal_clock_khz; ++ u32 vcxo_timer; ++ u32 demod_clock_khz; ++ ++ const u8 *microcode_B_fe_buffer; ++ u32 microcode_B_fe_size; ++ ++ struct dibGPIOFunction gpio_function[2]; ++ struct dibSubbandSelection subband; ++ ++ u8 output_mode; ++}; ++ ++#define DEFAULT_DIB9000_I2C_ADDRESS 18 ++ ++#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); ++extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); ++extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); ++extern struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); ++extern int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); ++extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); ++extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); ++extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe); ++extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); ++extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe); ++extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); ++extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe); ++extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c); ++extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed); ++#else ++static inline struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib9000_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_remove_slave_frontend(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++ ++static inline int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dibx000_common.c b/drivers/media/dvb-frontends/dibx000_common.c +new file mode 100644 +index 0000000..43be723 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dibx000_common.c +@@ -0,0 +1,515 @@ ++#include ++#include ++#include ++ ++#include "dibx000_common.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); ++ ++#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); printk("\n"); } } while (0) ++ ++static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ mst->i2c_write_buffer[0] = (reg >> 8) & 0xff; ++ mst->i2c_write_buffer[1] = reg & 0xff; ++ mst->i2c_write_buffer[2] = (val >> 8) & 0xff; ++ mst->i2c_write_buffer[3] = val & 0xff; ++ ++ memset(mst->msg, 0, sizeof(struct i2c_msg)); ++ mst->msg[0].addr = mst->i2c_addr; ++ mst->msg[0].flags = 0; ++ mst->msg[0].buf = mst->i2c_write_buffer; ++ mst->msg[0].len = 4; ++ ++ ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0; ++ mutex_unlock(&mst->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) ++{ ++ u16 ret; ++ ++ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return 0; ++ } ++ ++ mst->i2c_write_buffer[0] = reg >> 8; ++ mst->i2c_write_buffer[1] = reg & 0xff; ++ ++ memset(mst->msg, 0, 2 * sizeof(struct i2c_msg)); ++ mst->msg[0].addr = mst->i2c_addr; ++ mst->msg[0].flags = 0; ++ mst->msg[0].buf = mst->i2c_write_buffer; ++ mst->msg[0].len = 2; ++ mst->msg[1].addr = mst->i2c_addr; ++ mst->msg[1].flags = I2C_M_RD; ++ mst->msg[1].buf = mst->i2c_read_buffer; ++ mst->msg[1].len = 2; ++ ++ if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2) ++ dprintk("i2c read error on %d", reg); ++ ++ ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1]; ++ mutex_unlock(&mst->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) ++{ ++ int i = 100; ++ u16 status; ++ ++ while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0) ++ ; ++ ++ /* i2c timed out */ ++ if (i == 0) ++ return -EREMOTEIO; ++ ++ /* no acknowledge */ ++ if ((status & 0x0080) == 0) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_msg *msg, u8 stop) ++{ ++ u16 data; ++ u16 da; ++ u16 i; ++ u16 txlen = msg->len, len; ++ const u8 *b = msg->buf; ++ ++ while (txlen) { ++ dibx000_read_word(mst, mst->base_reg + 2); ++ ++ len = txlen > 8 ? 8 : txlen; ++ for (i = 0; i < len; i += 2) { ++ data = *b++ << 8; ++ if (i+1 < len) ++ data |= *b++; ++ dibx000_write_word(mst, mst->base_reg, data); ++ } ++ da = (((u8) (msg->addr)) << 9) | ++ (1 << 8) | ++ (1 << 7) | ++ (0 << 6) | ++ (0 << 5) | ++ ((len & 0x7) << 2) | ++ (0 << 1) | ++ (0 << 0); ++ ++ if (txlen == msg->len) ++ da |= 1 << 5; /* start */ ++ ++ if (txlen-len == 0 && stop) ++ da |= 1 << 6; /* stop */ ++ ++ dibx000_write_word(mst, mst->base_reg+1, da); ++ ++ if (dibx000_is_i2c_done(mst) != 0) ++ return -EREMOTEIO; ++ txlen -= len; ++ } ++ ++ return 0; ++} ++ ++static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_msg *msg) ++{ ++ u16 da; ++ u8 *b = msg->buf; ++ u16 rxlen = msg->len, len; ++ ++ while (rxlen) { ++ len = rxlen > 8 ? 8 : rxlen; ++ da = (((u8) (msg->addr)) << 9) | ++ (1 << 8) | ++ (1 << 7) | ++ (0 << 6) | ++ (0 << 5) | ++ ((len & 0x7) << 2) | ++ (1 << 1) | ++ (0 << 0); ++ ++ if (rxlen == msg->len) ++ da |= 1 << 5; /* start */ ++ ++ if (rxlen-len == 0) ++ da |= 1 << 6; /* stop */ ++ dibx000_write_word(mst, mst->base_reg+1, da); ++ ++ if (dibx000_is_i2c_done(mst) != 0) ++ return -EREMOTEIO; ++ ++ rxlen -= len; ++ ++ while (len) { ++ da = dibx000_read_word(mst, mst->base_reg); ++ *b++ = (da >> 8) & 0xff; ++ len--; ++ if (len >= 1) { ++ *b++ = da & 0xff; ++ len--; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed) ++{ ++ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); ++ ++ if (mst->device_rev < DIB7000MC && speed < 235) ++ speed = 235; ++ return dibx000_write_word(mst, mst->base_reg + 3, (u16)(60000 / speed)); ++ ++} ++EXPORT_SYMBOL(dibx000_i2c_set_speed); ++ ++static u32 dibx000_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, ++ enum dibx000_i2c_interface intf) ++{ ++ if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { ++ dprintk("selecting interface: %d", intf); ++ mst->selected_interface = intf; ++ return dibx000_write_word(mst, mst->base_reg + 4, intf); ++ } ++ return 0; ++} ++ ++static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); ++ int msg_index; ++ int ret = 0; ++ ++ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2); ++ for (msg_index = 0; msg_index < num; msg_index++) { ++ if (msg[msg_index].flags & I2C_M_RD) { ++ ret = dibx000_master_i2c_read(mst, &msg[msg_index]); ++ if (ret != 0) ++ return 0; ++ } else { ++ ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); ++ if (ret != 0) ++ return 0; ++ } ++ } ++ ++ return num; ++} ++ ++static int dibx000_i2c_master_xfer_gpio34(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); ++ int msg_index; ++ int ret = 0; ++ ++ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_3_4); ++ for (msg_index = 0; msg_index < num; msg_index++) { ++ if (msg[msg_index].flags & I2C_M_RD) { ++ ret = dibx000_master_i2c_read(mst, &msg[msg_index]); ++ if (ret != 0) ++ return 0; ++ } else { ++ ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); ++ if (ret != 0) ++ return 0; ++ } ++ } ++ ++ return num; ++} ++ ++static struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = { ++ .master_xfer = dibx000_i2c_master_xfer_gpio12, ++ .functionality = dibx000_i2c_func, ++}; ++ ++static struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = { ++ .master_xfer = dibx000_i2c_master_xfer_gpio34, ++ .functionality = dibx000_i2c_func, ++}; ++ ++static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], ++ u8 addr, int onoff) ++{ ++ u16 val; ++ ++ ++ if (onoff) ++ val = addr << 8; // bit 7 = use master or not, if 0, the gate is open ++ else ++ val = 1 << 7; ++ ++ if (mst->device_rev > DIB7000) ++ val <<= 1; ++ ++ tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); ++ tx[1] = ((mst->base_reg + 1) & 0xff); ++ tx[2] = val >> 8; ++ tx[3] = val & 0xff; ++ ++ return 0; ++} ++ ++static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); ++ int ret; ++ ++ if (num > 32) { ++ dprintk("%s: too much I2C message to be transmitted (%i).\ ++ Maximum is 32", __func__, num); ++ return -ENOMEM; ++ } ++ ++ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7); ++ ++ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ ++ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); ++ ++ /* open the gate */ ++ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); ++ mst->msg[0].addr = mst->i2c_addr; ++ mst->msg[0].buf = &mst->i2c_write_buffer[0]; ++ mst->msg[0].len = 4; ++ ++ memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); ++ ++ /* close the gate */ ++ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); ++ mst->msg[num + 1].addr = mst->i2c_addr; ++ mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; ++ mst->msg[num + 1].len = 4; ++ ++ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? ++ num : -EIO); ++ ++ mutex_unlock(&mst->i2c_buffer_lock); ++ return ret; ++} ++ ++static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = { ++ .master_xfer = dibx000_i2c_gated_gpio67_xfer, ++ .functionality = dibx000_i2c_func, ++}; ++ ++static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); ++ int ret; ++ ++ if (num > 32) { ++ dprintk("%s: too much I2C message to be transmitted (%i).\ ++ Maximum is 32", __func__, num); ++ return -ENOMEM; ++ } ++ ++ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); ++ ++ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); ++ ++ /* open the gate */ ++ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); ++ mst->msg[0].addr = mst->i2c_addr; ++ mst->msg[0].buf = &mst->i2c_write_buffer[0]; ++ mst->msg[0].len = 4; ++ ++ memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); ++ ++ /* close the gate */ ++ dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); ++ mst->msg[num + 1].addr = mst->i2c_addr; ++ mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; ++ mst->msg[num + 1].len = 4; ++ ++ ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? ++ num : -EIO); ++ mutex_unlock(&mst->i2c_buffer_lock); ++ return ret; ++} ++ ++static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { ++ .master_xfer = dibx000_i2c_gated_tuner_xfer, ++ .functionality = dibx000_i2c_func, ++}; ++ ++struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, ++ enum dibx000_i2c_interface intf, ++ int gating) ++{ ++ struct i2c_adapter *i2c = NULL; ++ ++ switch (intf) { ++ case DIBX000_I2C_INTERFACE_TUNER: ++ if (gating) ++ i2c = &mst->gated_tuner_i2c_adap; ++ break; ++ case DIBX000_I2C_INTERFACE_GPIO_1_2: ++ if (!gating) ++ i2c = &mst->master_i2c_adap_gpio12; ++ break; ++ case DIBX000_I2C_INTERFACE_GPIO_3_4: ++ if (!gating) ++ i2c = &mst->master_i2c_adap_gpio34; ++ break; ++ case DIBX000_I2C_INTERFACE_GPIO_6_7: ++ if (gating) ++ i2c = &mst->master_i2c_adap_gpio67; ++ break; ++ default: ++ printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); ++ break; ++ } ++ ++ return i2c; ++} ++ ++EXPORT_SYMBOL(dibx000_get_i2c_adapter); ++ ++void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) ++{ ++ /* initialize the i2c-master by closing the gate */ ++ u8 tx[4]; ++ struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; ++ ++ dibx000_i2c_gate_ctrl(mst, tx, 0, 0); ++ i2c_transfer(mst->i2c_adap, &m, 1); ++ mst->selected_interface = 0xff; // the first time force a select of the I2C ++ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); ++} ++ ++EXPORT_SYMBOL(dibx000_reset_i2c_master); ++ ++static int i2c_adapter_init(struct i2c_adapter *i2c_adap, ++ struct i2c_algorithm *algo, const char *name, ++ struct dibx000_i2c_master *mst) ++{ ++ strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); ++ i2c_adap->algo = algo; ++ i2c_adap->algo_data = NULL; ++ i2c_set_adapdata(i2c_adap, mst); ++ if (i2c_add_adapter(i2c_adap) < 0) ++ return -ENODEV; ++ return 0; ++} ++ ++int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, ++ struct i2c_adapter *i2c_adap, u8 i2c_addr) ++{ ++ int ret; ++ ++ mutex_init(&mst->i2c_buffer_lock); ++ if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { ++ dprintk("could not acquire lock"); ++ return -EINVAL; ++ } ++ memset(mst->msg, 0, sizeof(struct i2c_msg)); ++ mst->msg[0].addr = i2c_addr >> 1; ++ mst->msg[0].flags = 0; ++ mst->msg[0].buf = mst->i2c_write_buffer; ++ mst->msg[0].len = 4; ++ ++ mst->device_rev = device_rev; ++ mst->i2c_adap = i2c_adap; ++ mst->i2c_addr = i2c_addr >> 1; ++ ++ if (device_rev == DIB7000P || device_rev == DIB8000) ++ mst->base_reg = 1024; ++ else ++ mst->base_reg = 768; ++ ++ mst->gated_tuner_i2c_adap.dev.parent = mst->i2c_adap->dev.parent; ++ if (i2c_adapter_init ++ (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, ++ "DiBX000 tuner I2C bus", mst) != 0) ++ printk(KERN_ERR ++ "DiBX000: could not initialize the tuner i2c_adapter\n"); ++ ++ mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent; ++ if (i2c_adapter_init ++ (&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo, ++ "DiBX000 master GPIO12 I2C bus", mst) != 0) ++ printk(KERN_ERR ++ "DiBX000: could not initialize the master i2c_adapter\n"); ++ ++ mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent; ++ if (i2c_adapter_init ++ (&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo, ++ "DiBX000 master GPIO34 I2C bus", mst) != 0) ++ printk(KERN_ERR ++ "DiBX000: could not initialize the master i2c_adapter\n"); ++ ++ mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent; ++ if (i2c_adapter_init ++ (&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo, ++ "DiBX000 master GPIO67 I2C bus", mst) != 0) ++ printk(KERN_ERR ++ "DiBX000: could not initialize the master i2c_adapter\n"); ++ ++ /* initialize the i2c-master by closing the gate */ ++ dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0); ++ ++ ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1); ++ mutex_unlock(&mst->i2c_buffer_lock); ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL(dibx000_init_i2c_master); ++ ++void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) ++{ ++ i2c_del_adapter(&mst->gated_tuner_i2c_adap); ++ i2c_del_adapter(&mst->master_i2c_adap_gpio12); ++ i2c_del_adapter(&mst->master_i2c_adap_gpio34); ++ i2c_del_adapter(&mst->master_i2c_adap_gpio67); ++} ++EXPORT_SYMBOL(dibx000_exit_i2c_master); ++ ++ ++u32 systime(void) ++{ ++ struct timespec t; ++ ++ t = current_kernel_time(); ++ return (t.tv_sec * 10000) + (t.tv_nsec / 100000); ++} ++EXPORT_SYMBOL(systime); ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Common function the DiBcom demodulator family"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dibx000_common.h b/drivers/media/dvb-frontends/dibx000_common.h +new file mode 100644 +index 0000000..5f48488 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dibx000_common.h +@@ -0,0 +1,280 @@ ++#ifndef DIBX000_COMMON_H ++#define DIBX000_COMMON_H ++ ++enum dibx000_i2c_interface { ++ DIBX000_I2C_INTERFACE_TUNER = 0, ++ DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, ++ DIBX000_I2C_INTERFACE_GPIO_3_4 = 2, ++ DIBX000_I2C_INTERFACE_GPIO_6_7 = 3 ++}; ++ ++struct dibx000_i2c_master { ++#define DIB3000MC 1 ++#define DIB7000 2 ++#define DIB7000P 11 ++#define DIB7000MC 12 ++#define DIB8000 13 ++ u16 device_rev; ++ ++ enum dibx000_i2c_interface selected_interface; ++ ++/* struct i2c_adapter tuner_i2c_adap; */ ++ struct i2c_adapter gated_tuner_i2c_adap; ++ struct i2c_adapter master_i2c_adap_gpio12; ++ struct i2c_adapter master_i2c_adap_gpio34; ++ struct i2c_adapter master_i2c_adap_gpio67; ++ ++ struct i2c_adapter *i2c_adap; ++ u8 i2c_addr; ++ ++ u16 base_reg; ++ ++ /* for the I2C transfer */ ++ struct i2c_msg msg[34]; ++ u8 i2c_write_buffer[8]; ++ u8 i2c_read_buffer[2]; ++ struct mutex i2c_buffer_lock; ++}; ++ ++extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, ++ u16 device_rev, struct i2c_adapter *i2c_adap, ++ u8 i2c_addr); ++extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master ++ *mst, ++ enum dibx000_i2c_interface ++ intf, int gating); ++extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); ++extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); ++extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed); ++ ++extern u32 systime(void); ++ ++#define BAND_LBAND 0x01 ++#define BAND_UHF 0x02 ++#define BAND_VHF 0x04 ++#define BAND_SBAND 0x08 ++#define BAND_FM 0x10 ++#define BAND_CBAND 0x20 ++ ++#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \ ++ (freq_kHz) <= 115000 ? BAND_FM : \ ++ (freq_kHz) <= 250000 ? BAND_VHF : \ ++ (freq_kHz) <= 863000 ? BAND_UHF : \ ++ (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) ++ ++struct dibx000_agc_config { ++ /* defines the capabilities of this AGC-setting - using the BAND_-defines */ ++ u8 band_caps; ++ ++ u16 setup; ++ ++ u16 inv_gain; ++ u16 time_stabiliz; ++ ++ u8 alpha_level; ++ u16 thlock; ++ ++ u8 wbd_inv; ++ u16 wbd_ref; ++ u8 wbd_sel; ++ u8 wbd_alpha; ++ ++ u16 agc1_max; ++ u16 agc1_min; ++ u16 agc2_max; ++ u16 agc2_min; ++ ++ u8 agc1_pt1; ++ u8 agc1_pt2; ++ u8 agc1_pt3; ++ ++ u8 agc1_slope1; ++ u8 agc1_slope2; ++ ++ u8 agc2_pt1; ++ u8 agc2_pt2; ++ ++ u8 agc2_slope1; ++ u8 agc2_slope2; ++ ++ u8 alpha_mant; ++ u8 alpha_exp; ++ ++ u8 beta_mant; ++ u8 beta_exp; ++ ++ u8 perform_agc_softsplit; ++ ++ struct { ++ u16 min; ++ u16 max; ++ u16 min_thres; ++ u16 max_thres; ++ } split; ++}; ++ ++struct dibx000_bandwidth_config { ++ u32 internal; ++ u32 sampling; ++ ++ u8 pll_prediv; ++ u8 pll_ratio; ++ u8 pll_range; ++ u8 pll_reset; ++ u8 pll_bypass; ++ ++ u8 enable_refdiv; ++ u8 bypclk_div; ++ u8 IO_CLK_en_core; ++ u8 ADClkSrc; ++ u8 modulo; ++ ++ u16 sad_cfg; ++ ++ u32 ifreq; ++ u32 timf; ++ ++ u32 xtal_hz; ++}; ++ ++enum dibx000_adc_states { ++ DIBX000_SLOW_ADC_ON = 0, ++ DIBX000_SLOW_ADC_OFF, ++ DIBX000_ADC_ON, ++ DIBX000_ADC_OFF, ++ DIBX000_VBG_ENABLE, ++ DIBX000_VBG_DISABLE, ++}; ++ ++#define BANDWIDTH_TO_KHZ(v) ((v) / 1000) ++#define BANDWIDTH_TO_HZ(v) ((v) * 1000) ++ ++/* Chip output mode. */ ++#define OUTMODE_HIGH_Z 0 ++#define OUTMODE_MPEG2_PAR_GATED_CLK 1 ++#define OUTMODE_MPEG2_PAR_CONT_CLK 2 ++#define OUTMODE_MPEG2_SERIAL 7 ++#define OUTMODE_DIVERSITY 4 ++#define OUTMODE_MPEG2_FIFO 5 ++#define OUTMODE_ANALOG_ADC 6 ++ ++#define INPUT_MODE_OFF 0x11 ++#define INPUT_MODE_DIVERSITY 0x12 ++#define INPUT_MODE_MPEG 0x13 ++ ++enum frontend_tune_state { ++ CT_TUNER_START = 10, ++ CT_TUNER_STEP_0, ++ CT_TUNER_STEP_1, ++ CT_TUNER_STEP_2, ++ CT_TUNER_STEP_3, ++ CT_TUNER_STEP_4, ++ CT_TUNER_STEP_5, ++ CT_TUNER_STEP_6, ++ CT_TUNER_STEP_7, ++ CT_TUNER_STOP, ++ ++ CT_AGC_START = 20, ++ CT_AGC_STEP_0, ++ CT_AGC_STEP_1, ++ CT_AGC_STEP_2, ++ CT_AGC_STEP_3, ++ CT_AGC_STEP_4, ++ CT_AGC_STOP, ++ ++ CT_DEMOD_START = 30, ++ CT_DEMOD_STEP_1, ++ CT_DEMOD_STEP_2, ++ CT_DEMOD_STEP_3, ++ CT_DEMOD_STEP_4, ++ CT_DEMOD_STEP_5, ++ CT_DEMOD_STEP_6, ++ CT_DEMOD_STEP_7, ++ CT_DEMOD_STEP_8, ++ CT_DEMOD_STEP_9, ++ CT_DEMOD_STEP_10, ++ CT_DEMOD_SEARCH_NEXT = 41, ++ CT_DEMOD_STEP_LOCKED, ++ CT_DEMOD_STOP, ++ ++ CT_DONE = 100, ++ CT_SHUTDOWN, ++ ++}; ++ ++struct dvb_frontend_parametersContext { ++#define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01 ++#define CHANNEL_STATUS_PARAMETERS_SET 0x02 ++ u8 status; ++ u32 tune_time_estimation[2]; ++ s32 tps_available; ++ u16 tps[9]; ++}; ++ ++#define FE_STATUS_TUNE_FAILED 0 ++#define FE_STATUS_TUNE_TIMED_OUT -1 ++#define FE_STATUS_TUNE_TIME_TOO_SHORT -2 ++#define FE_STATUS_TUNE_PENDING -3 ++#define FE_STATUS_STD_SUCCESS -4 ++#define FE_STATUS_FFT_SUCCESS -5 ++#define FE_STATUS_DEMOD_SUCCESS -6 ++#define FE_STATUS_LOCKED -7 ++#define FE_STATUS_DATA_LOCKED -8 ++ ++#define FE_CALLBACK_TIME_NEVER 0xffffffff ++ ++#define ABS(x) ((x < 0) ? (-x) : (x)) ++ ++#define DATA_BUS_ACCESS_MODE_8BIT 0x01 ++#define DATA_BUS_ACCESS_MODE_16BIT 0x02 ++#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10 ++ ++struct dibGPIOFunction { ++#define BOARD_GPIO_COMPONENT_BUS_ADAPTER 1 ++#define BOARD_GPIO_COMPONENT_DEMOD 2 ++ u8 component; ++ ++#define BOARD_GPIO_FUNCTION_BOARD_ON 1 ++#define BOARD_GPIO_FUNCTION_BOARD_OFF 2 ++#define BOARD_GPIO_FUNCTION_COMPONENT_ON 3 ++#define BOARD_GPIO_FUNCTION_COMPONENT_OFF 4 ++#define BOARD_GPIO_FUNCTION_SUBBAND_PWM 5 ++#define BOARD_GPIO_FUNCTION_SUBBAND_GPIO 6 ++ u8 function; ++ ++/* mask, direction and value are used specify which GPIO to change GPIO0 ++ * is LSB and possible GPIO31 is MSB. The same bit-position as in the ++ * mask is used for the direction and the value. Direction == 1 is OUT, ++ * 0 == IN. For direction "OUT" value is either 1 or 0, for direction IN ++ * value has no meaning. ++ * ++ * In case of BOARD_GPIO_FUNCTION_PWM mask is giving the GPIO to be ++ * used to do the PWM. Direction gives the PWModulator to be used. ++ * Value gives the PWM value in device-dependent scale. ++ */ ++ u32 mask; ++ u32 direction; ++ u32 value; ++}; ++ ++#define MAX_NB_SUBBANDS 8 ++struct dibSubbandSelection { ++ u8 size; /* Actual number of subbands. */ ++ struct { ++ u16 f_mhz; ++ struct dibGPIOFunction gpio; ++ } subband[MAX_NB_SUBBANDS]; ++}; ++ ++#define DEMOD_TIMF_SET 0x00 ++#define DEMOD_TIMF_GET 0x01 ++#define DEMOD_TIMF_UPDATE 0x02 ++ ++#define MPEG_ON_DIBTX 1 ++#define DIV_ON_DIBTX 2 ++#define ADC_ON_DIBTX 3 ++#define DEMOUT_ON_HOSTBUS 4 ++#define DIBTX_ON_HOSTBUS 5 ++#define MPEG_ON_HOSTBUS 6 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/drxd.h b/drivers/media/dvb-frontends/drxd.h +new file mode 100644 +index 0000000..216c8c3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxd.h +@@ -0,0 +1,73 @@ ++/* ++ * drxd.h: DRXD DVB-T demodulator driver ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef _DRXD_H_ ++#define _DRXD_H_ ++ ++#include ++#include ++ ++struct drxd_config { ++ u8 index; ++ ++ u8 pll_address; ++ u8 pll_type; ++#define DRXD_PLL_NONE 0 ++#define DRXD_PLL_DTT7520X 1 ++#define DRXD_PLL_MT3X0823 2 ++ ++ u32 clock; ++ u8 insert_rs_byte; ++ ++ u8 demod_address; ++ u8 demoda_address; ++ u8 demod_revision; ++ ++ /* If the tuner is not behind an i2c gate, be sure to flip this bit ++ or else the i2c bus could get wedged */ ++ u8 disable_i2c_gate_ctrl; ++ ++ u32 IF; ++ s16(*osc_deviation) (void *priv, s16 dev, int flag); ++}; ++ ++#if defined(CONFIG_DVB_DRXD) || \ ++ (defined(CONFIG_DVB_DRXD_MODULE) && defined(MODULE)) ++extern ++struct dvb_frontend *drxd_attach(const struct drxd_config *config, ++ void *priv, struct i2c_adapter *i2c, ++ struct device *dev); ++#else ++static inline ++struct dvb_frontend *drxd_attach(const struct drxd_config *config, ++ void *priv, struct i2c_adapter *i2c, ++ struct device *dev) ++{ ++ printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", ++ __func__); ++ return NULL; ++} ++#endif ++ ++extern int drxd_config_i2c(struct dvb_frontend *, int); ++#endif +diff --git a/drivers/media/dvb-frontends/drxd_firm.c b/drivers/media/dvb-frontends/drxd_firm.c +new file mode 100644 +index 0000000..5418b0b +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxd_firm.c +@@ -0,0 +1,929 @@ ++/* ++ * drxd_firm.c : DRXD firmware tables ++ * ++ * Copyright (C) 2006-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/* TODO: generate this file with a script from a settings file */ ++ ++/* Contains A2 firmware version: 1.4.2 ++ * Contains B1 firmware version: 3.3.33 ++ * Contains settings from driver 1.4.23 ++*/ ++ ++#include "drxd_firm.h" ++ ++#define ADDRESS(x) ((x) & 0xFF), (((x)>>8) & 0xFF), (((x)>>16) & 0xFF), (((x)>>24) & 0xFF) ++#define LENGTH(x) ((x) & 0xFF), (((x)>>8) & 0xFF) ++ ++/* Is written via block write, must be little endian */ ++#define DATA16(x) ((x) & 0xFF), (((x)>>8) & 0xFF) ++ ++#define WRBLOCK(a, l) ADDRESS(a), LENGTH(l) ++#define WR16(a, d) ADDRESS(a), LENGTH(1), DATA16(d) ++ ++#define END_OF_TABLE 0xFF, 0xFF, 0xFF, 0xFF ++ ++/* HI firmware patches */ ++ ++#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A ++#define HI_TR_FUNC_SIZE 9 /* size of this function in instruction words */ ++ ++u8 DRXD_InitAtomicRead[] = { ++ WRBLOCK(HI_TR_FUNC_ADDR, HI_TR_FUNC_SIZE), ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x60, 0x04, /* r0rami.dt -> ring.xba; */ ++ 0x61, 0x04, /* r0rami.dt -> ring.xad; */ ++ 0xE3, 0x07, /* HI_RA_RAM_USR_BEGIN -> ring.iad; */ ++ 0x40, 0x00, /* (long immediate) */ ++ 0x64, 0x04, /* r0rami.dt -> ring.len; */ ++ 0x65, 0x04, /* r0rami.dt -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x38, 0x00, /* 0 -> jumps.ad; */ ++ END_OF_TABLE ++}; ++ ++/* Pins D0 and D1 of the parallel MPEG output can be used ++ to set the I2C address of a device. */ ++ ++#define HI_RST_FUNC_ADDR (HI_IF_RAM_USR_BEGIN__A + HI_TR_FUNC_SIZE) ++#define HI_RST_FUNC_SIZE 54 /* size of this function in instruction words */ ++ ++/* D0 Version */ ++u8 DRXD_HiI2cPatch_1[] = { ++ WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), ++ 0xC8, 0x07, 0x01, 0x00, /* MASK -> reg0.dt; */ ++ 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ ++ 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ ++ 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ ++ 0x23, 0x00, /* &data -> ring.iad; */ ++ 0x24, 0x00, /* 0 -> ring.len; */ ++ 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x42, 0x00, /* &data+1 -> w0ram.ad; */ ++ 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ ++ 0x63, 0x00, /* &data+1 -> ring.iad; */ ++ 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ ++ 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ ++ 0x23, 0x00, /* &data -> ring.iad; */ ++ 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x42, 0x00, /* &data+1 -> w0ram.ad; */ ++ 0x0F, 0x04, /* r0ram.dt -> and.op; */ ++ 0x1C, 0x06, /* reg0.dt -> and.tr; */ ++ 0xCF, 0x04, /* and.rs -> add.op; */ ++ 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ ++ 0xD0, 0x04, /* add.rs -> add.tr; */ ++ 0xC8, 0x04, /* add.rs -> reg0.dt; */ ++ 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ ++ 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ ++ 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ ++ 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ ++ 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ ++ 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ ++ 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ ++ 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ ++ ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ ++ /* Force quick and dirty reset */ ++ WR16(B_HI_CT_REG_COMM_STATE__A, 0), ++ END_OF_TABLE ++}; ++ ++/* D0,D1 Version */ ++u8 DRXD_HiI2cPatch_3[] = { ++ WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), ++ 0xC8, 0x07, 0x03, 0x00, /* MASK -> reg0.dt; */ ++ 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ ++ 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ ++ 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ ++ 0x23, 0x00, /* &data -> ring.iad; */ ++ 0x24, 0x00, /* 0 -> ring.len; */ ++ 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x42, 0x00, /* &data+1 -> w0ram.ad; */ ++ 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ ++ 0x63, 0x00, /* &data+1 -> ring.iad; */ ++ 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ ++ 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ ++ 0x23, 0x00, /* &data -> ring.iad; */ ++ 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ ++ 0x26, 0x00, /* 0 -> ring.rdy; */ ++ 0x42, 0x00, /* &data+1 -> w0ram.ad; */ ++ 0x0F, 0x04, /* r0ram.dt -> and.op; */ ++ 0x1C, 0x06, /* reg0.dt -> and.tr; */ ++ 0xCF, 0x04, /* and.rs -> add.op; */ ++ 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ ++ 0xD0, 0x04, /* add.rs -> add.tr; */ ++ 0xC8, 0x04, /* add.rs -> reg0.dt; */ ++ 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ ++ 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ ++ 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ ++ 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x01, 0x00, /* 0 -> w0rami.dt; */ ++ 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ ++ 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ ++ 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ ++ 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ ++ 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ ++ ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), ++ (u16) (HI_RST_FUNC_ADDR & 0x3FF)), ++ ++ /* Force quick and dirty reset */ ++ WR16(B_HI_CT_REG_COMM_STATE__A, 0), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_ResetCEFR[] = { ++ WRBLOCK(CE_REG_FR_TREAL00__A, 57), ++ 0x52, 0x00, /* CE_REG_FR_TREAL00__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG00__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL01__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG01__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL02__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG02__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL03__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG03__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL04__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG04__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL05__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG05__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL06__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG06__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL07__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG07__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL08__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG08__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL09__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG09__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL10__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG10__A */ ++ 0x52, 0x00, /* CE_REG_FR_TREAL11__A */ ++ 0x00, 0x00, /* CE_REG_FR_TIMAG11__A */ ++ ++ 0x52, 0x00, /* CE_REG_FR_MID_TAP__A */ ++ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G00__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G01__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G02__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G03__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G04__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G05__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G06__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G07__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G08__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G09__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G10__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G11__A */ ++ 0x0B, 0x00, /* CE_REG_FR_SQS_G12__A */ ++ ++ 0xFF, 0x01, /* CE_REG_FR_RIO_G00__A */ ++ 0x90, 0x01, /* CE_REG_FR_RIO_G01__A */ ++ 0x0B, 0x01, /* CE_REG_FR_RIO_G02__A */ ++ 0xC8, 0x00, /* CE_REG_FR_RIO_G03__A */ ++ 0xA0, 0x00, /* CE_REG_FR_RIO_G04__A */ ++ 0x85, 0x00, /* CE_REG_FR_RIO_G05__A */ ++ 0x72, 0x00, /* CE_REG_FR_RIO_G06__A */ ++ 0x64, 0x00, /* CE_REG_FR_RIO_G07__A */ ++ 0x59, 0x00, /* CE_REG_FR_RIO_G08__A */ ++ 0x50, 0x00, /* CE_REG_FR_RIO_G09__A */ ++ 0x49, 0x00, /* CE_REG_FR_RIO_G10__A */ ++ ++ 0x10, 0x00, /* CE_REG_FR_MODE__A */ ++ 0x78, 0x00, /* CE_REG_FR_SQS_TRH__A */ ++ 0x00, 0x00, /* CE_REG_FR_RIO_GAIN__A */ ++ 0x00, 0x02, /* CE_REG_FR_BYPASS__A */ ++ 0x0D, 0x00, /* CE_REG_FR_PM_SET__A */ ++ 0x07, 0x00, /* CE_REG_FR_ERR_SH__A */ ++ 0x04, 0x00, /* CE_REG_FR_MAN_SH__A */ ++ 0x06, 0x00, /* CE_REG_FR_TAP_SH__A */ ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitFEA2_1[] = { ++ WRBLOCK(FE_AD_REG_PD__A, 3), ++ 0x00, 0x00, /* FE_AD_REG_PD__A */ ++ 0x01, 0x00, /* FE_AD_REG_INVEXT__A */ ++ 0x00, 0x00, /* FE_AD_REG_CLKNEG__A */ ++ ++ WRBLOCK(FE_AG_REG_DCE_AUR_CNT__A, 2), ++ 0x10, 0x00, /* FE_AG_REG_DCE_AUR_CNT__A */ ++ 0x10, 0x00, /* FE_AG_REG_DCE_RUR_CNT__A */ ++ ++ WRBLOCK(FE_AG_REG_ACE_AUR_CNT__A, 2), ++ 0x0E, 0x00, /* FE_AG_REG_ACE_AUR_CNT__A */ ++ 0x00, 0x00, /* FE_AG_REG_ACE_RUR_CNT__A */ ++ ++ WRBLOCK(FE_AG_REG_EGC_FLA_RGN__A, 5), ++ 0x04, 0x00, /* FE_AG_REG_EGC_FLA_RGN__A */ ++ 0x1F, 0x00, /* FE_AG_REG_EGC_SLO_RGN__A */ ++ 0x00, 0x00, /* FE_AG_REG_EGC_JMP_PSN__A */ ++ 0x00, 0x00, /* FE_AG_REG_EGC_FLA_INC__A */ ++ 0x00, 0x00, /* FE_AG_REG_EGC_FLA_DEC__A */ ++ ++ WRBLOCK(FE_AG_REG_GC1_AGC_MAX__A, 2), ++ 0xFF, 0x01, /* FE_AG_REG_GC1_AGC_MAX__A */ ++ 0x00, 0xFE, /* FE_AG_REG_GC1_AGC_MIN__A */ ++ ++ WRBLOCK(FE_AG_REG_IND_WIN__A, 29), ++ 0x00, 0x00, /* FE_AG_REG_IND_WIN__A */ ++ 0x05, 0x00, /* FE_AG_REG_IND_THD_LOL__A */ ++ 0x0F, 0x00, /* FE_AG_REG_IND_THD_HIL__A */ ++ 0x00, 0x00, /* FE_AG_REG_IND_DEL__A don't care */ ++ 0x1E, 0x00, /* FE_AG_REG_IND_PD1_WRI__A */ ++ 0x0C, 0x00, /* FE_AG_REG_PDA_AUR_CNT__A */ ++ 0x00, 0x00, /* FE_AG_REG_PDA_RUR_CNT__A */ ++ 0x00, 0x00, /* FE_AG_REG_PDA_AVE_DAT__A don't care */ ++ 0x00, 0x00, /* FE_AG_REG_PDC_RUR_CNT__A */ ++ 0x01, 0x00, /* FE_AG_REG_PDC_SET_LVL__A */ ++ 0x02, 0x00, /* FE_AG_REG_PDC_FLA_RGN__A */ ++ 0x00, 0x00, /* FE_AG_REG_PDC_JMP_PSN__A don't care */ ++ 0xFF, 0xFF, /* FE_AG_REG_PDC_FLA_STP__A */ ++ 0xFF, 0xFF, /* FE_AG_REG_PDC_SLO_STP__A */ ++ 0x00, 0x1F, /* FE_AG_REG_PDC_PD2_WRI__A don't care */ ++ 0x00, 0x00, /* FE_AG_REG_PDC_MAP_DAT__A don't care */ ++ 0x02, 0x00, /* FE_AG_REG_PDC_MAX__A */ ++ 0x0C, 0x00, /* FE_AG_REG_TGA_AUR_CNT__A */ ++ 0x00, 0x00, /* FE_AG_REG_TGA_RUR_CNT__A */ ++ 0x00, 0x00, /* FE_AG_REG_TGA_AVE_DAT__A don't care */ ++ 0x00, 0x00, /* FE_AG_REG_TGC_RUR_CNT__A */ ++ 0x22, 0x00, /* FE_AG_REG_TGC_SET_LVL__A */ ++ 0x15, 0x00, /* FE_AG_REG_TGC_FLA_RGN__A */ ++ 0x00, 0x00, /* FE_AG_REG_TGC_JMP_PSN__A don't care */ ++ 0x01, 0x00, /* FE_AG_REG_TGC_FLA_STP__A */ ++ 0x0A, 0x00, /* FE_AG_REG_TGC_SLO_STP__A */ ++ 0x00, 0x00, /* FE_AG_REG_TGC_MAP_DAT__A don't care */ ++ 0x10, 0x00, /* FE_AG_REG_FGA_AUR_CNT__A */ ++ 0x10, 0x00, /* FE_AG_REG_FGA_RUR_CNT__A */ ++ ++ WRBLOCK(FE_AG_REG_BGC_FGC_WRI__A, 2), ++ 0x00, 0x00, /* FE_AG_REG_BGC_FGC_WRI__A */ ++ 0x00, 0x00, /* FE_AG_REG_BGC_CGC_WRI__A */ ++ ++ WRBLOCK(FE_FD_REG_SCL__A, 3), ++ 0x05, 0x00, /* FE_FD_REG_SCL__A */ ++ 0x03, 0x00, /* FE_FD_REG_MAX_LEV__A */ ++ 0x05, 0x00, /* FE_FD_REG_NR__A */ ++ ++ WRBLOCK(FE_CF_REG_SCL__A, 5), ++ 0x16, 0x00, /* FE_CF_REG_SCL__A */ ++ 0x04, 0x00, /* FE_CF_REG_MAX_LEV__A */ ++ 0x06, 0x00, /* FE_CF_REG_NR__A */ ++ 0x00, 0x00, /* FE_CF_REG_IMP_VAL__A */ ++ 0x01, 0x00, /* FE_CF_REG_MEAS_VAL__A */ ++ ++ WRBLOCK(FE_CU_REG_FRM_CNT_RST__A, 2), ++ 0x00, 0x08, /* FE_CU_REG_FRM_CNT_RST__A */ ++ 0x00, 0x00, /* FE_CU_REG_FRM_CNT_STR__A */ ++ ++ END_OF_TABLE ++}; ++ ++ /* with PGA */ ++/* WR16COND( DRXD_WITH_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0004), */ ++ /* without PGA */ ++/* WR16COND( DRXD_WITHOUT_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0001), */ ++/* WR16(FE_AG_REG_AG_AGC_SIO__A, (extAttr -> FeAgRegAgAgcSio), 0x0000 );*/ ++/* WR16(FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ ++ ++u8 DRXD_InitFEA2_2[] = { ++ WR16(FE_AG_REG_CDR_RUR_CNT__A, 0x0010), ++ WR16(FE_AG_REG_FGM_WRI__A, 48), ++ /* Activate measurement, activate scale */ ++ WR16(FE_FD_REG_MEAS_VAL__A, 0x0001), ++ ++ WR16(FE_CU_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_CF_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_IF_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_FD_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_FS_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_AD_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_AG_REG_COMM_EXEC__A, 0x0001), ++ WR16(FE_AG_REG_AG_MODE_LOP__A, 0x895E), ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitFEB1_1[] = { ++ WR16(B_FE_AD_REG_PD__A, 0x0000), ++ WR16(B_FE_AD_REG_CLKNEG__A, 0x0000), ++ WR16(B_FE_AG_REG_BGC_FGC_WRI__A, 0x0000), ++ WR16(B_FE_AG_REG_BGC_CGC_WRI__A, 0x0000), ++ WR16(B_FE_AG_REG_AG_MODE_LOP__A, 0x000a), ++ WR16(B_FE_AG_REG_IND_PD1_WRI__A, 35), ++ WR16(B_FE_AG_REG_IND_WIN__A, 0), ++ WR16(B_FE_AG_REG_IND_THD_LOL__A, 8), ++ WR16(B_FE_AG_REG_IND_THD_HIL__A, 8), ++ WR16(B_FE_CF_REG_IMP_VAL__A, 1), ++ WR16(B_FE_AG_REG_EGC_FLA_RGN__A, 7), ++ END_OF_TABLE ++}; ++ ++ /* with PGA */ ++/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , 0x0000, 0x0000); */ ++ /* without PGA */ ++/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , ++ B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000);*/ ++ /* WR16(B_FE_AG_REG_AG_AGC_SIO__A,(extAttr -> FeAgRegAgAgcSio), 0x0000 );*//*added HS 23-05-2005 */ ++/* WR16(B_FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ ++ ++u8 DRXD_InitFEB1_2[] = { ++ WR16(B_FE_COMM_EXEC__A, 0x0001), ++ ++ /* RF-AGC setup */ ++ WR16(B_FE_AG_REG_PDA_AUR_CNT__A, 0x0C), ++ WR16(B_FE_AG_REG_PDC_SET_LVL__A, 0x01), ++ WR16(B_FE_AG_REG_PDC_FLA_RGN__A, 0x02), ++ WR16(B_FE_AG_REG_PDC_FLA_STP__A, 0xFFFF), ++ WR16(B_FE_AG_REG_PDC_SLO_STP__A, 0xFFFF), ++ WR16(B_FE_AG_REG_PDC_MAX__A, 0x02), ++ WR16(B_FE_AG_REG_TGA_AUR_CNT__A, 0x0C), ++ WR16(B_FE_AG_REG_TGC_SET_LVL__A, 0x22), ++ WR16(B_FE_AG_REG_TGC_FLA_RGN__A, 0x15), ++ WR16(B_FE_AG_REG_TGC_FLA_STP__A, 0x01), ++ WR16(B_FE_AG_REG_TGC_SLO_STP__A, 0x0A), ++ ++ WR16(B_FE_CU_REG_DIV_NFC_CLP__A, 0), ++ WR16(B_FE_CU_REG_CTR_NFC_OCR__A, 25000), ++ WR16(B_FE_CU_REG_CTR_NFC_ICR__A, 1), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitCPA2[] = { ++ WRBLOCK(CP_REG_BR_SPL_OFFSET__A, 2), ++ 0x07, 0x00, /* CP_REG_BR_SPL_OFFSET__A */ ++ 0x0A, 0x00, /* CP_REG_BR_STR_DEL__A */ ++ ++ WRBLOCK(CP_REG_RT_ANG_INC0__A, 4), ++ 0x00, 0x00, /* CP_REG_RT_ANG_INC0__A */ ++ 0x00, 0x00, /* CP_REG_RT_ANG_INC1__A */ ++ 0x03, 0x00, /* CP_REG_RT_DETECT_ENA__A */ ++ 0x03, 0x00, /* CP_REG_RT_DETECT_TRH__A */ ++ ++ WRBLOCK(CP_REG_AC_NEXP_OFFS__A, 5), ++ 0x32, 0x00, /* CP_REG_AC_NEXP_OFFS__A */ ++ 0x62, 0x00, /* CP_REG_AC_AVER_POW__A */ ++ 0x82, 0x00, /* CP_REG_AC_MAX_POW__A */ ++ 0x26, 0x00, /* CP_REG_AC_WEIGHT_MAN__A */ ++ 0x0F, 0x00, /* CP_REG_AC_WEIGHT_EXP__A */ ++ ++ WRBLOCK(CP_REG_AC_AMP_MODE__A, 2), ++ 0x02, 0x00, /* CP_REG_AC_AMP_MODE__A */ ++ 0x01, 0x00, /* CP_REG_AC_AMP_FIX__A */ ++ ++ WR16(CP_REG_INTERVAL__A, 0x0005), ++ WR16(CP_REG_RT_EXP_MARG__A, 0x0004), ++ WR16(CP_REG_AC_ANG_MODE__A, 0x0003), ++ ++ WR16(CP_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitCPB1[] = { ++ WR16(B_CP_REG_BR_SPL_OFFSET__A, 0x0008), ++ WR16(B_CP_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitCEA2[] = { ++ WRBLOCK(CE_REG_AVG_POW__A, 4), ++ 0x62, 0x00, /* CE_REG_AVG_POW__A */ ++ 0x78, 0x00, /* CE_REG_MAX_POW__A */ ++ 0x62, 0x00, /* CE_REG_ATT__A */ ++ 0x17, 0x00, /* CE_REG_NRED__A */ ++ ++ WRBLOCK(CE_REG_NE_ERR_SELECT__A, 2), ++ 0x07, 0x00, /* CE_REG_NE_ERR_SELECT__A */ ++ 0xEB, 0xFF, /* CE_REG_NE_TD_CAL__A */ ++ ++ WRBLOCK(CE_REG_NE_MIXAVG__A, 2), ++ 0x06, 0x00, /* CE_REG_NE_MIXAVG__A */ ++ 0x00, 0x00, /* CE_REG_NE_NUPD_OFS__A */ ++ ++ WRBLOCK(CE_REG_PE_NEXP_OFFS__A, 2), ++ 0x00, 0x00, /* CE_REG_PE_NEXP_OFFS__A */ ++ 0x00, 0x00, /* CE_REG_PE_TIMESHIFT__A */ ++ ++ WRBLOCK(CE_REG_TP_A0_TAP_NEW__A, 3), ++ 0x00, 0x01, /* CE_REG_TP_A0_TAP_NEW__A */ ++ 0x01, 0x00, /* CE_REG_TP_A0_TAP_NEW_VALID__A */ ++ 0x0E, 0x00, /* CE_REG_TP_A0_MU_LMS_STEP__A */ ++ ++ WRBLOCK(CE_REG_TP_A1_TAP_NEW__A, 3), ++ 0x00, 0x00, /* CE_REG_TP_A1_TAP_NEW__A */ ++ 0x01, 0x00, /* CE_REG_TP_A1_TAP_NEW_VALID__A */ ++ 0x0A, 0x00, /* CE_REG_TP_A1_MU_LMS_STEP__A */ ++ ++ WRBLOCK(CE_REG_FI_SHT_INCR__A, 2), ++ 0x12, 0x00, /* CE_REG_FI_SHT_INCR__A */ ++ 0x0C, 0x00, /* CE_REG_FI_EXP_NORM__A */ ++ ++ WRBLOCK(CE_REG_IR_INPUTSEL__A, 3), ++ 0x00, 0x00, /* CE_REG_IR_INPUTSEL__A */ ++ 0x00, 0x00, /* CE_REG_IR_STARTPOS__A */ ++ 0xFF, 0x00, /* CE_REG_IR_NEXP_THRES__A */ ++ ++ WR16(CE_REG_TI_NEXP_OFFS__A, 0x0000), ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitCEB1[] = { ++ WR16(B_CE_REG_TI_PHN_ENABLE__A, 0x0001), ++ WR16(B_CE_REG_FR_PM_SET__A, 0x000D), ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitEQA2[] = { ++ WRBLOCK(EQ_REG_OT_QNT_THRES0__A, 4), ++ 0x1E, 0x00, /* EQ_REG_OT_QNT_THRES0__A */ ++ 0x1F, 0x00, /* EQ_REG_OT_QNT_THRES1__A */ ++ 0x06, 0x00, /* EQ_REG_OT_CSI_STEP__A */ ++ 0x02, 0x00, /* EQ_REG_OT_CSI_OFFSET__A */ ++ ++ WR16(EQ_REG_TD_REQ_SMB_CNT__A, 0x0200), ++ WR16(EQ_REG_IS_CLIP_EXP__A, 0x001F), ++ WR16(EQ_REG_SN_OFFSET__A, (u16) (-7)), ++ WR16(EQ_REG_RC_SEL_CAR__A, 0x0002), ++ WR16(EQ_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitEQB1[] = { ++ WR16(B_EQ_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_ResetECRAM[] = { ++ /* Reset packet sync bytes in EC_VD ram */ ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), ++ ++ /* Reset packet sync bytes in EC_RS ram */ ++ WR16(EC_RS_EC_RAM__A, 0x0000), ++ WR16(EC_RS_EC_RAM__A + 204, 0x0000), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitECA2[] = { ++ WRBLOCK(EC_SB_REG_CSI_HI__A, 6), ++ 0x1F, 0x00, /* EC_SB_REG_CSI_HI__A */ ++ 0x1E, 0x00, /* EC_SB_REG_CSI_LO__A */ ++ 0x01, 0x00, /* EC_SB_REG_SMB_TGL__A */ ++ 0x7F, 0x00, /* EC_SB_REG_SNR_HI__A */ ++ 0x7F, 0x00, /* EC_SB_REG_SNR_MID__A */ ++ 0x7F, 0x00, /* EC_SB_REG_SNR_LO__A */ ++ ++ WRBLOCK(EC_RS_REG_REQ_PCK_CNT__A, 2), ++ 0x00, 0x10, /* EC_RS_REG_REQ_PCK_CNT__A */ ++ DATA16(EC_RS_REG_VAL_PCK), /* EC_RS_REG_VAL__A */ ++ ++ WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), ++ 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ ++ 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ ++ 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ ++ 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ ++ 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ ++ ++ WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), ++ 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ ++ 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ ++ ++ WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), ++ 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ ++ 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ ++ 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ ++ 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ ++ 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ ++ 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ ++ 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ ++ ++ WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), ++ 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ ++ 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ ++ ++ WR16(EC_SB_REG_CSI_OFS__A, 0x0001), ++ WR16(EC_VD_REG_FORCE__A, 0x0002), ++ WR16(EC_VD_REG_REQ_SMB_CNT__A, 0x0001), ++ WR16(EC_VD_REG_RLK_ENA__A, 0x0001), ++ WR16(EC_OD_REG_SYNC__A, 0x0664), ++ WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), ++ WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), ++ /* Output zero on monitorbus pads, power saving */ ++ WR16(EC_OC_REG_OCR_MON_UOS__A, ++ (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), ++ WR16(EC_OC_REG_OCR_MON_WRI__A, ++ EC_OC_REG_OCR_MON_WRI_INIT), ++ ++/* CHK_ERROR(ResetECRAM(demod)); */ ++ /* Reset packet sync bytes in EC_VD ram */ ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), ++ ++ /* Reset packet sync bytes in EC_RS ram */ ++ WR16(EC_RS_EC_RAM__A, 0x0000), ++ WR16(EC_RS_EC_RAM__A + 204, 0x0000), ++ ++ WR16(EC_SB_REG_COMM_EXEC__A, 0x0001), ++ WR16(EC_VD_REG_COMM_EXEC__A, 0x0001), ++ WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), ++ WR16(EC_RS_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitECB1[] = { ++ WR16(B_EC_SB_REG_CSI_OFS0__A, 0x0001), ++ WR16(B_EC_SB_REG_CSI_OFS1__A, 0x0001), ++ WR16(B_EC_SB_REG_CSI_OFS2__A, 0x0001), ++ WR16(B_EC_SB_REG_CSI_LO__A, 0x000c), ++ WR16(B_EC_SB_REG_CSI_HI__A, 0x0018), ++ WR16(B_EC_SB_REG_SNR_HI__A, 0x007f), ++ WR16(B_EC_SB_REG_SNR_MID__A, 0x007f), ++ WR16(B_EC_SB_REG_SNR_LO__A, 0x007f), ++ ++ WR16(B_EC_OC_REG_DTO_CLKMODE__A, 0x0002), ++ WR16(B_EC_OC_REG_DTO_PER__A, 0x0006), ++ WR16(B_EC_OC_REG_DTO_BUR__A, 0x0001), ++ WR16(B_EC_OC_REG_RCR_CLKMODE__A, 0x0000), ++ WR16(B_EC_OC_REG_RCN_GAI_LVL__A, 0x000D), ++ WR16(B_EC_OC_REG_OC_MPG_SIO__A, 0x0000), ++ ++ /* Needed because shadow registers do not have correct default value */ ++ WR16(B_EC_OC_REG_RCN_CST_LOP__A, 0x1000), ++ WR16(B_EC_OC_REG_RCN_CST_HIP__A, 0x0000), ++ WR16(B_EC_OC_REG_RCN_CRA_LOP__A, 0x0000), ++ WR16(B_EC_OC_REG_RCN_CRA_HIP__A, 0x00C0), ++ WR16(B_EC_OC_REG_RCN_CLP_LOP__A, 0x0000), ++ WR16(B_EC_OC_REG_RCN_CLP_HIP__A, 0x00C0), ++ WR16(B_EC_OC_REG_DTO_INC_LOP__A, 0x0000), ++ WR16(B_EC_OC_REG_DTO_INC_HIP__A, 0x00C0), ++ ++ WR16(B_EC_OD_REG_SYNC__A, 0x0664), ++ WR16(B_EC_RS_REG_REQ_PCK_CNT__A, 0x1000), ++ ++/* CHK_ERROR(ResetECRAM(demod)); */ ++ /* Reset packet sync bytes in EC_VD ram */ ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), ++ ++ /* Reset packet sync bytes in EC_RS ram */ ++ WR16(EC_RS_EC_RAM__A, 0x0000), ++ WR16(EC_RS_EC_RAM__A + 204, 0x0000), ++ ++ WR16(B_EC_SB_REG_COMM_EXEC__A, 0x0001), ++ WR16(B_EC_VD_REG_COMM_EXEC__A, 0x0001), ++ WR16(B_EC_OD_REG_COMM_EXEC__A, 0x0001), ++ WR16(B_EC_RS_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_ResetECA2[] = { ++ ++ WR16(EC_OC_REG_COMM_EXEC__A, 0x0000), ++ WR16(EC_OD_REG_COMM_EXEC__A, 0x0000), ++ ++ WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), ++ 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ ++ 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ ++ 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ ++ 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ ++ 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ ++ ++ WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), ++ 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ ++ 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ ++ ++ WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), ++ 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ ++ 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ ++ 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ ++ 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ ++ 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ ++ 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ ++ 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ ++ ++ WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), ++ 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ ++ 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ ++ ++ WR16(EC_OD_REG_SYNC__A, 0x0664), ++ WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), ++ WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), ++ /* Output zero on monitorbus pads, power saving */ ++ WR16(EC_OC_REG_OCR_MON_UOS__A, ++ (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | ++ EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), ++ WR16(EC_OC_REG_OCR_MON_WRI__A, ++ EC_OC_REG_OCR_MON_WRI_INIT), ++ ++/* CHK_ERROR(ResetECRAM(demod)); */ ++ /* Reset packet sync bytes in EC_VD ram */ ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), ++ WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), ++ ++ /* Reset packet sync bytes in EC_RS ram */ ++ WR16(EC_RS_EC_RAM__A, 0x0000), ++ WR16(EC_RS_EC_RAM__A + 204, 0x0000), ++ ++ WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitSC[] = { ++ WR16(SC_COMM_EXEC__A, 0), ++ WR16(SC_COMM_STATE__A, 0), ++ ++#ifdef COMPILE_FOR_QT ++ WR16(SC_RA_RAM_BE_OPT_DELAY__A, 0x100), ++#endif ++ ++ /* SC is not started, this is done in SetChannels() */ ++ END_OF_TABLE ++}; ++ ++/* Diversity settings */ ++ ++u8 DRXD_InitDiversityFront[] = { ++ /* Start demod ********* RF in , diversity out **************************** */ ++ WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | ++ B_SC_RA_RAM_CONFIG_FREQSCAN__M), ++ ++ WR16(B_SC_RA_RAM_LC_ABS_2K__A, 0x7), ++ WR16(B_SC_RA_RAM_LC_ABS_8K__A, 0x7), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), ++ ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), ++ ++ WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), ++ WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), ++ WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), ++ ++ WR16(B_CC_REG_DIVERSITY__A, 0x0001), ++ WR16(B_EC_OC_REG_OC_MODE_HIP__A, 0x0010), ++ WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | ++ B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), ++ ++ /* 0x2a ), *//* CE to PASS mux */ ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_InitDiversityEnd[] = { ++ /* End demod *********** combining RF in and diversity in, MPEG TS out **** */ ++ /* disable near/far; switch on timing slave mode */ ++ WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | ++ B_SC_RA_RAM_CONFIG_FREQSCAN__M | ++ B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M | ++ B_SC_RA_RAM_CONFIG_SLAVE__M | ++ B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M ++/* MV from CtrlDiversity */ ++ ), ++#ifdef DRXDDIV_SRMM_SLAVING ++ WR16(SC_RA_RAM_LC_ABS_2K__A, 0x3c7), ++ WR16(SC_RA_RAM_LC_ABS_8K__A, 0x3c7), ++#else ++ WR16(SC_RA_RAM_LC_ABS_2K__A, 0x7), ++ WR16(SC_RA_RAM_LC_ABS_8K__A, 0x7), ++#endif ++ ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), ++ ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), ++ ++ WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), ++ WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), ++ WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), ++ ++ WR16(B_CC_REG_DIVERSITY__A, 0x0001), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_DisableDiversity[] = { ++ WR16(B_SC_RA_RAM_LC_ABS_2K__A, B_SC_RA_RAM_LC_ABS_2K__PRE), ++ WR16(B_SC_RA_RAM_LC_ABS_8K__A, B_SC_RA_RAM_LC_ABS_8K__PRE), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, ++ B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, ++ B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE), ++ WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, ++ B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, ++ B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, ++ B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, ++ B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE), ++ ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, ++ B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, ++ B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE), ++ WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, ++ B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, ++ B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, ++ B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE), ++ WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, ++ B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE), ++ ++ WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, B_LC_RA_RAM_FILTER_CRMM_A__PRE), ++ WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, B_LC_RA_RAM_FILTER_CRMM_B__PRE), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, B_LC_RA_RAM_FILTER_SRMM_A__PRE), ++ WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, B_LC_RA_RAM_FILTER_SRMM_B__PRE), ++ WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, B_LC_RA_RAM_FILTER_SYM_SET__PRE), ++ ++ WR16(B_CC_REG_DIVERSITY__A, 0x0000), ++ WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_INIT), /* combining disabled */ ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_StartDiversityFront[] = { ++ /* Start demod, RF in and diversity out, no combining */ ++ WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), ++ WR16(B_FE_AD_REG_FDB_IN__A, 0x0), ++ WR16(B_FE_AD_REG_INVEXT__A, 0x0), ++ WR16(B_EQ_REG_COMM_MB__A, 0x12), /* EQ to MB out */ ++ WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | /* CE to PASS mux */ ++ B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), ++ ++ WR16(SC_RA_RAM_ECHO_SHIFT_LIM__A, 2), ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_StartDiversityEnd[] = { ++ /* End demod, combining RF in and diversity in, MPEG TS out */ ++ WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), /* disable impulse noise cruncher */ ++ WR16(B_FE_AD_REG_INVEXT__A, 0x0), /* clock inversion (for sohard board) */ ++ WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apperently no mb delay matching is best */ ++ ++ WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_DIV_ON | /* org = 0x81 combining enabled */ ++ B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | ++ B_EQ_REG_RC_SEL_CAR_PASS_A_CC | B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC), ++ ++ END_OF_TABLE ++}; ++ ++u8 DRXD_DiversityDelay8MHZ[] = { ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1150 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1100 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 1000 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 800 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5420 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5200 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4800 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 4000 - 50), ++ END_OF_TABLE ++}; ++ ++u8 DRXD_DiversityDelay6MHZ[] = /* also used ok for 7 MHz */ ++{ ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1100 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1000 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 900 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 600 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5300 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5000 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4500 - 50), ++ WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 3500 - 50), ++ END_OF_TABLE ++}; +diff --git a/drivers/media/dvb-frontends/drxd_firm.h b/drivers/media/dvb-frontends/drxd_firm.h +new file mode 100644 +index 0000000..41597e8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxd_firm.h +@@ -0,0 +1,115 @@ ++/* ++ * drxd_firm.h ++ * ++ * Copyright (C) 2006-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef _DRXD_FIRM_H_ ++#define _DRXD_FIRM_H_ ++ ++#include ++#include "drxd_map_firm.h" ++ ++#define VERSION_MAJOR 1 ++#define VERSION_MINOR 4 ++#define VERSION_PATCH 23 ++ ++#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A ++ ++#define DRXD_MAX_RETRIES (1000) ++#define HI_I2C_DELAY 84 ++#define HI_I2C_BRIDGE_DELAY 750 ++ ++#define EQ_TD_TPS_PWR_UNKNOWN 0x00C0 /* Unknown configurations */ ++#define EQ_TD_TPS_PWR_QPSK 0x016a ++#define EQ_TD_TPS_PWR_QAM16_ALPHAN 0x0195 ++#define EQ_TD_TPS_PWR_QAM16_ALPHA1 0x0195 ++#define EQ_TD_TPS_PWR_QAM16_ALPHA2 0x011E ++#define EQ_TD_TPS_PWR_QAM16_ALPHA4 0x01CE ++#define EQ_TD_TPS_PWR_QAM64_ALPHAN 0x019F ++#define EQ_TD_TPS_PWR_QAM64_ALPHA1 0x019F ++#define EQ_TD_TPS_PWR_QAM64_ALPHA2 0x00F8 ++#define EQ_TD_TPS_PWR_QAM64_ALPHA4 0x014D ++ ++#define DRXD_DEF_AG_PWD_CONSUMER 0x000E ++#define DRXD_DEF_AG_PWD_PRO 0x0000 ++#define DRXD_DEF_AG_AGC_SIO 0x0000 ++ ++#define DRXD_FE_CTRL_MAX 1023 ++ ++#define DRXD_OSCDEV_DO_SCAN (16) ++ ++#define DRXD_OSCDEV_DONT_SCAN (0) ++ ++#define DRXD_OSCDEV_STEP (275) ++ ++#define DRXD_SCAN_TIMEOUT (650) ++ ++#define DRXD_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) ++#define DRXD_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) ++#define DRXD_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) ++ ++#define IRLEN_COARSE_8K (10) ++#define IRLEN_FINE_8K (10) ++#define IRLEN_COARSE_2K (7) ++#define IRLEN_FINE_2K (9) ++#define DIFF_INVALID (511) ++#define DIFF_TARGET (4) ++#define DIFF_MARGIN (1) ++ ++extern u8 DRXD_InitAtomicRead[]; ++extern u8 DRXD_HiI2cPatch_1[]; ++extern u8 DRXD_HiI2cPatch_3[]; ++ ++extern u8 DRXD_InitSC[]; ++ ++extern u8 DRXD_ResetCEFR[]; ++extern u8 DRXD_InitFEA2_1[]; ++extern u8 DRXD_InitFEA2_2[]; ++extern u8 DRXD_InitCPA2[]; ++extern u8 DRXD_InitCEA2[]; ++extern u8 DRXD_InitEQA2[]; ++extern u8 DRXD_InitECA2[]; ++extern u8 DRXD_ResetECA2[]; ++extern u8 DRXD_ResetECRAM[]; ++ ++extern u8 DRXD_A2_microcode[]; ++extern u32 DRXD_A2_microcode_length; ++ ++extern u8 DRXD_InitFEB1_1[]; ++extern u8 DRXD_InitFEB1_2[]; ++extern u8 DRXD_InitCPB1[]; ++extern u8 DRXD_InitCEB1[]; ++extern u8 DRXD_InitEQB1[]; ++extern u8 DRXD_InitECB1[]; ++ ++extern u8 DRXD_InitDiversityFront[]; ++extern u8 DRXD_InitDiversityEnd[]; ++extern u8 DRXD_DisableDiversity[]; ++extern u8 DRXD_StartDiversityFront[]; ++extern u8 DRXD_StartDiversityEnd[]; ++ ++extern u8 DRXD_DiversityDelay8MHZ[]; ++extern u8 DRXD_DiversityDelay6MHZ[]; ++ ++extern u8 DRXD_B1_microcode[]; ++extern u32 DRXD_B1_microcode_length; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c +new file mode 100644 +index 0000000..9a21347 +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxd_hard.c +@@ -0,0 +1,2997 @@ ++/* ++ * drxd_hard.c: DVB-T Demodulator Micronas DRX3975D-A2,DRX397xD-B1 ++ * ++ * Copyright (C) 2003-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "drxd.h" ++#include "drxd_firm.h" ++ ++#define DRX_FW_FILENAME_A2 "drxd-a2-1.1.fw" ++#define DRX_FW_FILENAME_B1 "drxd-b1-1.1.fw" ++ ++#define CHUNK_SIZE 48 ++ ++#define DRX_I2C_RMW 0x10 ++#define DRX_I2C_BROADCAST 0x20 ++#define DRX_I2C_CLEARCRC 0x80 ++#define DRX_I2C_SINGLE_MASTER 0xC0 ++#define DRX_I2C_MODEFLAGS 0xC0 ++#define DRX_I2C_FLAGS 0xF0 ++ ++#ifndef SIZEOF_ARRAY ++#define SIZEOF_ARRAY(array) (sizeof((array))/sizeof((array)[0])) ++#endif ++ ++#define DEFAULT_LOCK_TIMEOUT 1100 ++ ++#define DRX_CHANNEL_AUTO 0 ++#define DRX_CHANNEL_HIGH 1 ++#define DRX_CHANNEL_LOW 2 ++ ++#define DRX_LOCK_MPEG 1 ++#define DRX_LOCK_FEC 2 ++#define DRX_LOCK_DEMOD 4 ++ ++/****************************************************************************/ ++ ++enum CSCDState { ++ CSCD_INIT = 0, ++ CSCD_SET, ++ CSCD_SAVED ++}; ++ ++enum CDrxdState { ++ DRXD_UNINITIALIZED = 0, ++ DRXD_STOPPED, ++ DRXD_STARTED ++}; ++ ++enum AGC_CTRL_MODE { ++ AGC_CTRL_AUTO = 0, ++ AGC_CTRL_USER, ++ AGC_CTRL_OFF ++}; ++ ++enum OperationMode { ++ OM_Default, ++ OM_DVBT_Diversity_Front, ++ OM_DVBT_Diversity_End ++}; ++ ++struct SCfgAgc { ++ enum AGC_CTRL_MODE ctrlMode; ++ u16 outputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ ++ u16 settleLevel; /* range [0, ... , 1023], 1/n of fullscale range */ ++ u16 minOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ ++ u16 maxOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ ++ u16 speed; /* range [0, ... , 1023], 1/n of fullscale range */ ++ ++ u16 R1; ++ u16 R2; ++ u16 R3; ++}; ++ ++struct SNoiseCal { ++ int cpOpt; ++ short cpNexpOfs; ++ short tdCal2k; ++ short tdCal8k; ++}; ++ ++enum app_env { ++ APPENV_STATIC = 0, ++ APPENV_PORTABLE = 1, ++ APPENV_MOBILE = 2 ++}; ++ ++enum EIFFilter { ++ IFFILTER_SAW = 0, ++ IFFILTER_DISCRETE = 1 ++}; ++ ++struct drxd_state { ++ struct dvb_frontend frontend; ++ struct dvb_frontend_ops ops; ++ struct dtv_frontend_properties props; ++ ++ const struct firmware *fw; ++ struct device *dev; ++ ++ struct i2c_adapter *i2c; ++ void *priv; ++ struct drxd_config config; ++ ++ int i2c_access; ++ int init_done; ++ struct mutex mutex; ++ ++ u8 chip_adr; ++ u16 hi_cfg_timing_div; ++ u16 hi_cfg_bridge_delay; ++ u16 hi_cfg_wakeup_key; ++ u16 hi_cfg_ctrl; ++ ++ u16 intermediate_freq; ++ u16 osc_clock_freq; ++ ++ enum CSCDState cscd_state; ++ enum CDrxdState drxd_state; ++ ++ u16 sys_clock_freq; ++ s16 osc_clock_deviation; ++ u16 expected_sys_clock_freq; ++ ++ u16 insert_rs_byte; ++ u16 enable_parallel; ++ ++ int operation_mode; ++ ++ struct SCfgAgc if_agc_cfg; ++ struct SCfgAgc rf_agc_cfg; ++ ++ struct SNoiseCal noise_cal; ++ ++ u32 fe_fs_add_incr; ++ u32 org_fe_fs_add_incr; ++ u16 current_fe_if_incr; ++ ++ u16 m_FeAgRegAgPwd; ++ u16 m_FeAgRegAgAgcSio; ++ ++ u16 m_EcOcRegOcModeLop; ++ u16 m_EcOcRegSncSncLvl; ++ u8 *m_InitAtomicRead; ++ u8 *m_HiI2cPatch; ++ ++ u8 *m_ResetCEFR; ++ u8 *m_InitFE_1; ++ u8 *m_InitFE_2; ++ u8 *m_InitCP; ++ u8 *m_InitCE; ++ u8 *m_InitEQ; ++ u8 *m_InitSC; ++ u8 *m_InitEC; ++ u8 *m_ResetECRAM; ++ u8 *m_InitDiversityFront; ++ u8 *m_InitDiversityEnd; ++ u8 *m_DisableDiversity; ++ u8 *m_StartDiversityFront; ++ u8 *m_StartDiversityEnd; ++ ++ u8 *m_DiversityDelay8MHZ; ++ u8 *m_DiversityDelay6MHZ; ++ ++ u8 *microcode; ++ u32 microcode_length; ++ ++ int type_A; ++ int PGA; ++ int diversity; ++ int tuner_mirrors; ++ ++ enum app_env app_env_default; ++ enum app_env app_env_diversity; ++ ++}; ++ ++/****************************************************************************/ ++/* I2C **********************************************************************/ ++/****************************************************************************/ ++ ++static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 * data, int len) ++{ ++ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len }; ++ ++ if (i2c_transfer(adap, &msg, 1) != 1) ++ return -1; ++ return 0; ++} ++ ++static int i2c_read(struct i2c_adapter *adap, ++ u8 adr, u8 *msg, int len, u8 *answ, int alen) ++{ ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = adr, .flags = 0, ++ .buf = msg, .len = len ++ }, { ++ .addr = adr, .flags = I2C_M_RD, ++ .buf = answ, .len = alen ++ } ++ }; ++ if (i2c_transfer(adap, msgs, 2) != 2) ++ return -1; ++ return 0; ++} ++ ++static inline u32 MulDiv32(u32 a, u32 b, u32 c) ++{ ++ u64 tmp64; ++ ++ tmp64 = (u64)a * (u64)b; ++ do_div(tmp64, c); ++ ++ return (u32) tmp64; ++} ++ ++static int Read16(struct drxd_state *state, u32 reg, u16 *data, u8 flags) ++{ ++ u8 adr = state->config.demod_address; ++ u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, ++ flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff ++ }; ++ u8 mm2[2]; ++ if (i2c_read(state->i2c, adr, mm1, 4, mm2, 2) < 0) ++ return -1; ++ if (data) ++ *data = mm2[0] | (mm2[1] << 8); ++ return mm2[0] | (mm2[1] << 8); ++} ++ ++static int Read32(struct drxd_state *state, u32 reg, u32 *data, u8 flags) ++{ ++ u8 adr = state->config.demod_address; ++ u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, ++ flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff ++ }; ++ u8 mm2[4]; ++ ++ if (i2c_read(state->i2c, adr, mm1, 4, mm2, 4) < 0) ++ return -1; ++ if (data) ++ *data = ++ mm2[0] | (mm2[1] << 8) | (mm2[2] << 16) | (mm2[3] << 24); ++ return 0; ++} ++ ++static int Write16(struct drxd_state *state, u32 reg, u16 data, u8 flags) ++{ ++ u8 adr = state->config.demod_address; ++ u8 mm[6] = { reg & 0xff, (reg >> 16) & 0xff, ++ flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, ++ data & 0xff, (data >> 8) & 0xff ++ }; ++ ++ if (i2c_write(state->i2c, adr, mm, 6) < 0) ++ return -1; ++ return 0; ++} ++ ++static int Write32(struct drxd_state *state, u32 reg, u32 data, u8 flags) ++{ ++ u8 adr = state->config.demod_address; ++ u8 mm[8] = { reg & 0xff, (reg >> 16) & 0xff, ++ flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, ++ data & 0xff, (data >> 8) & 0xff, ++ (data >> 16) & 0xff, (data >> 24) & 0xff ++ }; ++ ++ if (i2c_write(state->i2c, adr, mm, 8) < 0) ++ return -1; ++ return 0; ++} ++ ++static int write_chunk(struct drxd_state *state, ++ u32 reg, u8 *data, u32 len, u8 flags) ++{ ++ u8 adr = state->config.demod_address; ++ u8 mm[CHUNK_SIZE + 4] = { reg & 0xff, (reg >> 16) & 0xff, ++ flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff ++ }; ++ int i; ++ ++ for (i = 0; i < len; i++) ++ mm[4 + i] = data[i]; ++ if (i2c_write(state->i2c, adr, mm, 4 + len) < 0) { ++ printk(KERN_ERR "error in write_chunk\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static int WriteBlock(struct drxd_state *state, ++ u32 Address, u16 BlockSize, u8 *pBlock, u8 Flags) ++{ ++ while (BlockSize > 0) { ++ u16 Chunk = BlockSize > CHUNK_SIZE ? CHUNK_SIZE : BlockSize; ++ ++ if (write_chunk(state, Address, pBlock, Chunk, Flags) < 0) ++ return -1; ++ pBlock += Chunk; ++ Address += (Chunk >> 1); ++ BlockSize -= Chunk; ++ } ++ return 0; ++} ++ ++static int WriteTable(struct drxd_state *state, u8 * pTable) ++{ ++ int status = 0; ++ ++ if (pTable == NULL) ++ return 0; ++ ++ while (!status) { ++ u16 Length; ++ u32 Address = pTable[0] | (pTable[1] << 8) | ++ (pTable[2] << 16) | (pTable[3] << 24); ++ ++ if (Address == 0xFFFFFFFF) ++ break; ++ pTable += sizeof(u32); ++ ++ Length = pTable[0] | (pTable[1] << 8); ++ pTable += sizeof(u16); ++ if (!Length) ++ break; ++ status = WriteBlock(state, Address, Length * 2, pTable, 0); ++ pTable += (Length * 2); ++ } ++ return status; ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static int ResetCEFR(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_ResetCEFR); ++} ++ ++static int InitCP(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_InitCP); ++} ++ ++static int InitCE(struct drxd_state *state) ++{ ++ int status; ++ enum app_env AppEnv = state->app_env_default; ++ ++ do { ++ status = WriteTable(state, state->m_InitCE); ++ if (status < 0) ++ break; ++ ++ if (state->operation_mode == OM_DVBT_Diversity_Front || ++ state->operation_mode == OM_DVBT_Diversity_End) { ++ AppEnv = state->app_env_diversity; ++ } ++ if (AppEnv == APPENV_STATIC) { ++ status = Write16(state, CE_REG_TAPSET__A, 0x0000, 0); ++ if (status < 0) ++ break; ++ } else if (AppEnv == APPENV_PORTABLE) { ++ status = Write16(state, CE_REG_TAPSET__A, 0x0001, 0); ++ if (status < 0) ++ break; ++ } else if (AppEnv == APPENV_MOBILE && state->type_A) { ++ status = Write16(state, CE_REG_TAPSET__A, 0x0002, 0); ++ if (status < 0) ++ break; ++ } else if (AppEnv == APPENV_MOBILE && !state->type_A) { ++ status = Write16(state, CE_REG_TAPSET__A, 0x0006, 0); ++ if (status < 0) ++ break; ++ } ++ ++ /* start ce */ ++ status = Write16(state, B_CE_REG_COMM_EXEC__A, 0x0001, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ return status; ++} ++ ++static int StopOC(struct drxd_state *state) ++{ ++ int status = 0; ++ u16 ocSyncLvl = 0; ++ u16 ocModeLop = state->m_EcOcRegOcModeLop; ++ u16 dtoIncLop = 0; ++ u16 dtoIncHip = 0; ++ ++ do { ++ /* Store output configuration */ ++ status = Read16(state, EC_OC_REG_SNC_ISC_LVL__A, &ocSyncLvl, 0); ++ if (status < 0) ++ break; ++ /* CHK_ERROR(Read16(EC_OC_REG_OC_MODE_LOP__A, &ocModeLop)); */ ++ state->m_EcOcRegSncSncLvl = ocSyncLvl; ++ /* m_EcOcRegOcModeLop = ocModeLop; */ ++ ++ /* Flush FIFO (byte-boundary) at fixed rate */ ++ status = Read16(state, EC_OC_REG_RCN_MAP_LOP__A, &dtoIncLop, 0); ++ if (status < 0) ++ break; ++ status = Read16(state, EC_OC_REG_RCN_MAP_HIP__A, &dtoIncHip, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_DTO_INC_LOP__A, dtoIncLop, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_DTO_INC_HIP__A, dtoIncHip, 0); ++ if (status < 0) ++ break; ++ ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M); ++ ocModeLop |= EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC; ++ status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); ++ if (status < 0) ++ break; ++ ++ msleep(1); ++ /* Output pins to '0' */ ++ status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS__M, 0); ++ if (status < 0) ++ break; ++ ++ /* Force the OC out of sync */ ++ ocSyncLvl &= ~(EC_OC_REG_SNC_ISC_LVL_OSC__M); ++ status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, ocSyncLvl, 0); ++ if (status < 0) ++ break; ++ ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M); ++ ocModeLop |= EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE; ++ ocModeLop |= 0x2; /* Magically-out-of-sync */ ++ status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_COMM_INT_STA__A, 0x0, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ ++ return status; ++} ++ ++static int StartOC(struct drxd_state *state) ++{ ++ int status = 0; ++ ++ do { ++ /* Stop OC */ ++ status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); ++ if (status < 0) ++ break; ++ ++ /* Restore output configuration */ ++ status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, state->m_EcOcRegSncSncLvl, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, state->m_EcOcRegOcModeLop, 0); ++ if (status < 0) ++ break; ++ ++ /* Output pins active again */ ++ status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS_INIT, 0); ++ if (status < 0) ++ break; ++ ++ /* Start OC */ ++ status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ return status; ++} ++ ++static int InitEQ(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_InitEQ); ++} ++ ++static int InitEC(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_InitEC); ++} ++ ++static int InitSC(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_InitSC); ++} ++ ++static int InitAtomicRead(struct drxd_state *state) ++{ ++ return WriteTable(state, state->m_InitAtomicRead); ++} ++ ++static int CorrectSysClockDeviation(struct drxd_state *state); ++ ++static int DRX_GetLockStatus(struct drxd_state *state, u32 * pLockStatus) ++{ ++ u16 ScRaRamLock = 0; ++ const u16 mpeg_lock_mask = (SC_RA_RAM_LOCK_MPEG__M | ++ SC_RA_RAM_LOCK_FEC__M | ++ SC_RA_RAM_LOCK_DEMOD__M); ++ const u16 fec_lock_mask = (SC_RA_RAM_LOCK_FEC__M | ++ SC_RA_RAM_LOCK_DEMOD__M); ++ const u16 demod_lock_mask = SC_RA_RAM_LOCK_DEMOD__M; ++ ++ int status; ++ ++ *pLockStatus = 0; ++ ++ status = Read16(state, SC_RA_RAM_LOCK__A, &ScRaRamLock, 0x0000); ++ if (status < 0) { ++ printk(KERN_ERR "Can't read SC_RA_RAM_LOCK__A status = %08x\n", status); ++ return status; ++ } ++ ++ if (state->drxd_state != DRXD_STARTED) ++ return 0; ++ ++ if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask) { ++ *pLockStatus |= DRX_LOCK_MPEG; ++ CorrectSysClockDeviation(state); ++ } ++ ++ if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask) ++ *pLockStatus |= DRX_LOCK_FEC; ++ ++ if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask) ++ *pLockStatus |= DRX_LOCK_DEMOD; ++ return 0; ++} ++ ++/****************************************************************************/ ++ ++static int SetCfgIfAgc(struct drxd_state *state, struct SCfgAgc *cfg) ++{ ++ int status; ++ ++ if (cfg->outputLevel > DRXD_FE_CTRL_MAX) ++ return -1; ++ ++ if (cfg->ctrlMode == AGC_CTRL_USER) { ++ do { ++ u16 FeAgRegPm1AgcWri; ++ u16 FeAgRegAgModeLop; ++ ++ status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); ++ if (status < 0) ++ break; ++ FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); ++ FeAgRegAgModeLop |= FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC; ++ status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); ++ if (status < 0) ++ break; ++ ++ FeAgRegPm1AgcWri = (u16) (cfg->outputLevel & ++ FE_AG_REG_PM1_AGC_WRI__M); ++ status = Write16(state, FE_AG_REG_PM1_AGC_WRI__A, FeAgRegPm1AgcWri, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { ++ if (((cfg->maxOutputLevel) < (cfg->minOutputLevel)) || ++ ((cfg->maxOutputLevel) > DRXD_FE_CTRL_MAX) || ++ ((cfg->speed) > DRXD_FE_CTRL_MAX) || ++ ((cfg->settleLevel) > DRXD_FE_CTRL_MAX) ++ ) ++ return -1; ++ do { ++ u16 FeAgRegAgModeLop; ++ u16 FeAgRegEgcSetLvl; ++ u16 slope, offset; ++ ++ /* == Mode == */ ++ ++ status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); ++ if (status < 0) ++ break; ++ FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); ++ FeAgRegAgModeLop |= ++ FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC; ++ status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); ++ if (status < 0) ++ break; ++ ++ /* == Settle level == */ ++ ++ FeAgRegEgcSetLvl = (u16) ((cfg->settleLevel >> 1) & ++ FE_AG_REG_EGC_SET_LVL__M); ++ status = Write16(state, FE_AG_REG_EGC_SET_LVL__A, FeAgRegEgcSetLvl, 0); ++ if (status < 0) ++ break; ++ ++ /* == Min/Max == */ ++ ++ slope = (u16) ((cfg->maxOutputLevel - ++ cfg->minOutputLevel) / 2); ++ offset = (u16) ((cfg->maxOutputLevel + ++ cfg->minOutputLevel) / 2 - 511); ++ ++ status = Write16(state, FE_AG_REG_GC1_AGC_RIC__A, slope, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_GC1_AGC_OFF__A, offset, 0); ++ if (status < 0) ++ break; ++ ++ /* == Speed == */ ++ { ++ const u16 maxRur = 8; ++ const u16 slowIncrDecLUT[] = { 3, 4, 4, 5, 6 }; ++ const u16 fastIncrDecLUT[] = { 14, 15, 15, 16, ++ 17, 18, 18, 19, ++ 20, 21, 22, 23, ++ 24, 26, 27, 28, ++ 29, 31 ++ }; ++ ++ u16 fineSteps = (DRXD_FE_CTRL_MAX + 1) / ++ (maxRur + 1); ++ u16 fineSpeed = (u16) (cfg->speed - ++ ((cfg->speed / ++ fineSteps) * ++ fineSteps)); ++ u16 invRurCount = (u16) (cfg->speed / ++ fineSteps); ++ u16 rurCount; ++ if (invRurCount > maxRur) { ++ rurCount = 0; ++ fineSpeed += fineSteps; ++ } else { ++ rurCount = maxRur - invRurCount; ++ } ++ ++ /* ++ fastInc = default * ++ (2^(fineSpeed/fineSteps)) ++ => range[default...2*default> ++ slowInc = default * ++ (2^(fineSpeed/fineSteps)) ++ */ ++ { ++ u16 fastIncrDec = ++ fastIncrDecLUT[fineSpeed / ++ ((fineSteps / ++ (14 + 1)) + 1)]; ++ u16 slowIncrDec = ++ slowIncrDecLUT[fineSpeed / ++ (fineSteps / ++ (3 + 1))]; ++ ++ status = Write16(state, FE_AG_REG_EGC_RUR_CNT__A, rurCount, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_EGC_FAS_INC__A, fastIncrDec, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_EGC_FAS_DEC__A, fastIncrDec, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_EGC_SLO_INC__A, slowIncrDec, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_EGC_SLO_DEC__A, slowIncrDec, 0); ++ if (status < 0) ++ break; ++ } ++ } ++ } while (0); ++ ++ } else { ++ /* No OFF mode for IF control */ ++ return -1; ++ } ++ return status; ++} ++ ++static int SetCfgRfAgc(struct drxd_state *state, struct SCfgAgc *cfg) ++{ ++ int status = 0; ++ ++ if (cfg->outputLevel > DRXD_FE_CTRL_MAX) ++ return -1; ++ ++ if (cfg->ctrlMode == AGC_CTRL_USER) { ++ do { ++ u16 AgModeLop = 0; ++ u16 level = (cfg->outputLevel); ++ ++ if (level == DRXD_FE_CTRL_MAX) ++ level++; ++ ++ status = Write16(state, FE_AG_REG_PM2_AGC_WRI__A, level, 0x0000); ++ if (status < 0) ++ break; ++ ++ /*==== Mode ====*/ ++ ++ /* Powerdown PD2, WRI source */ ++ state->m_FeAgRegAgPwd &= ~(FE_AG_REG_AG_PWD_PWD_PD2__M); ++ state->m_FeAgRegAgPwd |= ++ FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; ++ status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | ++ FE_AG_REG_AG_MODE_LOP_MODE_E__M)); ++ AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | ++ FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); ++ status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* enable AGC2 pin */ ++ { ++ u16 FeAgRegAgAgcSio = 0; ++ status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ FeAgRegAgAgcSio &= ++ ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); ++ FeAgRegAgAgcSio |= ++ FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; ++ status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ } ++ ++ } while (0); ++ } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { ++ u16 AgModeLop = 0; ++ ++ do { ++ u16 level; ++ /* Automatic control */ ++ /* Powerup PD2, AGC2 as output, TGC source */ ++ (state->m_FeAgRegAgPwd) &= ++ ~(FE_AG_REG_AG_PWD_PWD_PD2__M); ++ (state->m_FeAgRegAgPwd) |= ++ FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; ++ status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | ++ FE_AG_REG_AG_MODE_LOP_MODE_E__M)); ++ AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | ++ FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC); ++ status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ /* Settle level */ ++ level = (((cfg->settleLevel) >> 4) & ++ FE_AG_REG_TGC_SET_LVL__M); ++ status = Write16(state, FE_AG_REG_TGC_SET_LVL__A, level, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* Min/max: don't care */ ++ ++ /* Speed: TODO */ ++ ++ /* enable AGC2 pin */ ++ { ++ u16 FeAgRegAgAgcSio = 0; ++ status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ FeAgRegAgAgcSio &= ++ ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); ++ FeAgRegAgAgcSio |= ++ FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; ++ status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ } ++ ++ } while (0); ++ } else { ++ u16 AgModeLop = 0; ++ ++ do { ++ /* No RF AGC control */ ++ /* Powerdown PD2, AGC2 as output, WRI source */ ++ (state->m_FeAgRegAgPwd) &= ++ ~(FE_AG_REG_AG_PWD_PWD_PD2__M); ++ (state->m_FeAgRegAgPwd) |= ++ FE_AG_REG_AG_PWD_PWD_PD2_ENABLE; ++ status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | ++ FE_AG_REG_AG_MODE_LOP_MODE_E__M)); ++ AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | ++ FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); ++ status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* set FeAgRegAgAgcSio AGC2 (RF) as input */ ++ { ++ u16 FeAgRegAgAgcSio = 0; ++ status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ FeAgRegAgAgcSio &= ++ ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); ++ FeAgRegAgAgcSio |= ++ FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT; ++ status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ } ++ } while (0); ++ } ++ return status; ++} ++ ++static int ReadIFAgc(struct drxd_state *state, u32 * pValue) ++{ ++ int status = 0; ++ ++ *pValue = 0; ++ if (state->if_agc_cfg.ctrlMode != AGC_CTRL_OFF) { ++ u16 Value; ++ status = Read16(state, FE_AG_REG_GC1_AGC_DAT__A, &Value, 0); ++ Value &= FE_AG_REG_GC1_AGC_DAT__M; ++ if (status >= 0) { ++ /* 3.3V ++ | ++ R1 ++ | ++ Vin - R3 - * -- Vout ++ | ++ R2 ++ | ++ GND ++ */ ++ u32 R1 = state->if_agc_cfg.R1; ++ u32 R2 = state->if_agc_cfg.R2; ++ u32 R3 = state->if_agc_cfg.R3; ++ ++ u32 Vmax, Rpar, Vmin, Vout; ++ ++ if (R2 == 0 && (R1 == 0 || R3 == 0)) ++ return 0; ++ ++ Vmax = (3300 * R2) / (R1 + R2); ++ Rpar = (R2 * R3) / (R3 + R2); ++ Vmin = (3300 * Rpar) / (R1 + Rpar); ++ Vout = Vmin + ((Vmax - Vmin) * Value) / 1024; ++ ++ *pValue = Vout; ++ } ++ } ++ return status; ++} ++ ++static int load_firmware(struct drxd_state *state, const char *fw_name) ++{ ++ const struct firmware *fw; ++ ++ if (request_firmware(&fw, fw_name, state->dev) < 0) { ++ printk(KERN_ERR "drxd: firmware load failure [%s]\n", fw_name); ++ return -EIO; ++ } ++ ++ state->microcode = kmemdup(fw->data, fw->size, GFP_KERNEL); ++ if (state->microcode == NULL) { ++ release_firmware(fw); ++ printk(KERN_ERR "drxd: firmware load failure: no memory\n"); ++ return -ENOMEM; ++ } ++ ++ state->microcode_length = fw->size; ++ release_firmware(fw); ++ return 0; ++} ++ ++static int DownloadMicrocode(struct drxd_state *state, ++ const u8 *pMCImage, u32 Length) ++{ ++ u8 *pSrc; ++ u32 Address; ++ u16 nBlocks; ++ u16 BlockSize; ++ u32 offset = 0; ++ int i, status = 0; ++ ++ pSrc = (u8 *) pMCImage; ++ /* We're not using Flags */ ++ /* Flags = (pSrc[0] << 8) | pSrc[1]; */ ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ nBlocks = (pSrc[0] << 8) | pSrc[1]; ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ for (i = 0; i < nBlocks; i++) { ++ Address = (pSrc[0] << 24) | (pSrc[1] << 16) | ++ (pSrc[2] << 8) | pSrc[3]; ++ pSrc += sizeof(u32); ++ offset += sizeof(u32); ++ ++ BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16); ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ /* We're not using Flags */ ++ /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */ ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ /* We're not using BlockCRC */ ++ /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */ ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ status = WriteBlock(state, Address, BlockSize, ++ pSrc, DRX_I2C_CLEARCRC); ++ if (status < 0) ++ break; ++ pSrc += BlockSize; ++ offset += BlockSize; ++ } ++ ++ return status; ++} ++ ++static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult) ++{ ++ u32 nrRetries = 0; ++ u16 waitCmd; ++ int status; ++ ++ status = Write16(state, HI_RA_RAM_SRV_CMD__A, cmd, 0); ++ if (status < 0) ++ return status; ++ ++ do { ++ nrRetries += 1; ++ if (nrRetries > DRXD_MAX_RETRIES) { ++ status = -1; ++ break; ++ } ++ status = Read16(state, HI_RA_RAM_SRV_CMD__A, &waitCmd, 0); ++ } while (waitCmd != 0); ++ ++ if (status >= 0) ++ status = Read16(state, HI_RA_RAM_SRV_RES__A, pResult, 0); ++ return status; ++} ++ ++static int HI_CfgCommand(struct drxd_state *state) ++{ ++ int status = 0; ++ ++ mutex_lock(&state->mutex); ++ Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); ++ Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, state->hi_cfg_timing_div, 0); ++ Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, state->hi_cfg_bridge_delay, 0); ++ Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, state->hi_cfg_wakeup_key, 0); ++ Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, state->hi_cfg_ctrl, 0); ++ ++ Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); ++ ++ if ((state->hi_cfg_ctrl & HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) == ++ HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) ++ status = Write16(state, HI_RA_RAM_SRV_CMD__A, ++ HI_RA_RAM_SRV_CMD_CONFIG, 0); ++ else ++ status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, 0); ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++static int InitHI(struct drxd_state *state) ++{ ++ state->hi_cfg_wakeup_key = (state->chip_adr); ++ /* port/bridge/power down ctrl */ ++ state->hi_cfg_ctrl = HI_RA_RAM_SRV_CFG_ACT_SLV0_ON; ++ return HI_CfgCommand(state); ++} ++ ++static int HI_ResetCommand(struct drxd_state *state) ++{ ++ int status; ++ ++ mutex_lock(&state->mutex); ++ status = Write16(state, HI_RA_RAM_SRV_RST_KEY__A, ++ HI_RA_RAM_SRV_RST_KEY_ACT, 0); ++ if (status == 0) ++ status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, 0); ++ mutex_unlock(&state->mutex); ++ msleep(1); ++ return status; ++} ++ ++static int DRX_ConfigureI2CBridge(struct drxd_state *state, int bEnableBridge) ++{ ++ state->hi_cfg_ctrl &= (~HI_RA_RAM_SRV_CFG_ACT_BRD__M); ++ if (bEnableBridge) ++ state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_ON; ++ else ++ state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_OFF; ++ ++ return HI_CfgCommand(state); ++} ++ ++#define HI_TR_WRITE 0x9 ++#define HI_TR_READ 0xA ++#define HI_TR_READ_WRITE 0xB ++#define HI_TR_BROADCAST 0x4 ++ ++#if 0 ++static int AtomicReadBlock(struct drxd_state *state, ++ u32 Addr, u16 DataSize, u8 *pData, u8 Flags) ++{ ++ int status; ++ int i = 0; ++ ++ /* Parameter check */ ++ if ((!pData) || ((DataSize & 1) != 0)) ++ return -1; ++ ++ mutex_lock(&state->mutex); ++ ++ do { ++ /* Instruct HI to read n bytes */ ++ /* TODO use proper names forthese egisters */ ++ status = Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, (HI_TR_FUNC_ADDR & 0xFFFF), 0); ++ if (status < 0) ++ break; ++ status = Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, (u16) (Addr >> 16), 0); ++ if (status < 0) ++ break; ++ status = Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, (u16) (Addr & 0xFFFF), 0); ++ if (status < 0) ++ break; ++ status = Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, (u16) ((DataSize / 2) - 1), 0); ++ if (status < 0) ++ break; ++ status = Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, HI_TR_READ, 0); ++ if (status < 0) ++ break; ++ ++ status = HI_Command(state, HI_RA_RAM_SRV_CMD_EXECUTE, 0); ++ if (status < 0) ++ break; ++ ++ } while (0); ++ ++ if (status >= 0) { ++ for (i = 0; i < (DataSize / 2); i += 1) { ++ u16 word; ++ ++ status = Read16(state, (HI_RA_RAM_USR_BEGIN__A + i), ++ &word, 0); ++ if (status < 0) ++ break; ++ pData[2 * i] = (u8) (word & 0xFF); ++ pData[(2 * i) + 1] = (u8) (word >> 8); ++ } ++ } ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++static int AtomicReadReg32(struct drxd_state *state, ++ u32 Addr, u32 *pData, u8 Flags) ++{ ++ u8 buf[sizeof(u32)]; ++ int status; ++ ++ if (!pData) ++ return -1; ++ status = AtomicReadBlock(state, Addr, sizeof(u32), buf, Flags); ++ *pData = (((u32) buf[0]) << 0) + ++ (((u32) buf[1]) << 8) + ++ (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24); ++ return status; ++} ++#endif ++ ++static int StopAllProcessors(struct drxd_state *state) ++{ ++ return Write16(state, HI_COMM_EXEC__A, ++ SC_COMM_EXEC_CTL_STOP, DRX_I2C_BROADCAST); ++} ++ ++static int EnableAndResetMB(struct drxd_state *state) ++{ ++ if (state->type_A) { ++ /* disable? monitor bus observe @ EC_OC */ ++ Write16(state, EC_OC_REG_OC_MON_SIO__A, 0x0000, 0x0000); ++ } ++ ++ /* do inverse broadcast, followed by explicit write to HI */ ++ Write16(state, HI_COMM_MB__A, 0x0000, DRX_I2C_BROADCAST); ++ Write16(state, HI_COMM_MB__A, 0x0000, 0x0000); ++ return 0; ++} ++ ++static int InitCC(struct drxd_state *state) ++{ ++ if (state->osc_clock_freq == 0 || ++ state->osc_clock_freq > 20000 || ++ (state->osc_clock_freq % 4000) != 0) { ++ printk(KERN_ERR "invalid osc frequency %d\n", state->osc_clock_freq); ++ return -1; ++ } ++ ++ Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); ++ Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL | ++ CC_REG_PLL_MODE_PUMP_CUR_12, 0); ++ Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0); ++ Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0); ++ Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); ++ ++ return 0; ++} ++ ++static int ResetECOD(struct drxd_state *state) ++{ ++ int status = 0; ++ ++ if (state->type_A) ++ status = Write16(state, EC_OD_REG_SYNC__A, 0x0664, 0); ++ else ++ status = Write16(state, B_EC_OD_REG_SYNC__A, 0x0664, 0); ++ ++ if (!(status < 0)) ++ status = WriteTable(state, state->m_ResetECRAM); ++ if (!(status < 0)) ++ status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0001, 0); ++ return status; ++} ++ ++/* Configure PGA switch */ ++ ++static int SetCfgPga(struct drxd_state *state, int pgaSwitch) ++{ ++ int status; ++ u16 AgModeLop = 0; ++ u16 AgModeHip = 0; ++ do { ++ if (pgaSwitch) { ++ /* PGA on */ ++ /* fine gain */ ++ status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); ++ AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC; ++ status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* coarse gain */ ++ status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); ++ if (status < 0) ++ break; ++ AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); ++ AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC; ++ status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* enable fine and coarse gain, enable AAF, ++ no ext resistor */ ++ status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN, 0x0000); ++ if (status < 0) ++ break; ++ } else { ++ /* PGA off, bypass */ ++ ++ /* fine gain */ ++ status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); ++ AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC; ++ status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* coarse gain */ ++ status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); ++ if (status < 0) ++ break; ++ AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); ++ AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC; ++ status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* disable fine and coarse gain, enable AAF, ++ no ext resistor */ ++ status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000); ++ if (status < 0) ++ break; ++ } ++ } while (0); ++ return status; ++} ++ ++static int InitFE(struct drxd_state *state) ++{ ++ int status; ++ ++ do { ++ status = WriteTable(state, state->m_InitFE_1); ++ if (status < 0) ++ break; ++ ++ if (state->type_A) { ++ status = Write16(state, FE_AG_REG_AG_PGA_MODE__A, ++ FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, ++ 0); ++ } else { ++ if (state->PGA) ++ status = SetCfgPga(state, 0); ++ else ++ status = ++ Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, ++ B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, ++ 0); ++ } ++ ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, state->m_FeAgRegAgAgcSio, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = WriteTable(state, state->m_InitFE_2); ++ if (status < 0) ++ break; ++ ++ } while (0); ++ ++ return status; ++} ++ ++static int InitFT(struct drxd_state *state) ++{ ++ /* ++ norm OFFSET, MB says =2 voor 8K en =3 voor 2K waarschijnlijk ++ SC stuff ++ */ ++ return Write16(state, FT_REG_COMM_EXEC__A, 0x0001, 0x0000); ++} ++ ++static int SC_WaitForReady(struct drxd_state *state) ++{ ++ u16 curCmd; ++ int i; ++ ++ for (i = 0; i < DRXD_MAX_RETRIES; i += 1) { ++ int status = Read16(state, SC_RA_RAM_CMD__A, &curCmd, 0); ++ if (status == 0 || curCmd == 0) ++ return status; ++ } ++ return -1; ++} ++ ++static int SC_SendCommand(struct drxd_state *state, u16 cmd) ++{ ++ int status = 0; ++ u16 errCode; ++ ++ Write16(state, SC_RA_RAM_CMD__A, cmd, 0); ++ SC_WaitForReady(state); ++ ++ Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0); ++ ++ if (errCode == 0xFFFF) { ++ printk(KERN_ERR "Command Error\n"); ++ status = -1; ++ } ++ ++ return status; ++} ++ ++static int SC_ProcStartCommand(struct drxd_state *state, ++ u16 subCmd, u16 param0, u16 param1) ++{ ++ int status = 0; ++ u16 scExec; ++ ++ mutex_lock(&state->mutex); ++ do { ++ Read16(state, SC_COMM_EXEC__A, &scExec, 0); ++ if (scExec != 1) { ++ status = -1; ++ break; ++ } ++ SC_WaitForReady(state); ++ Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); ++ Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); ++ Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); ++ ++ SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START); ++ } while (0); ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++static int SC_SetPrefParamCommand(struct drxd_state *state, ++ u16 subCmd, u16 param0, u16 param1) ++{ ++ int status; ++ ++ mutex_lock(&state->mutex); ++ do { ++ status = SC_WaitForReady(state); ++ if (status < 0) ++ break; ++ status = Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); ++ if (status < 0) ++ break; ++ ++ status = SC_SendCommand(state, SC_RA_RAM_CMD_SET_PREF_PARAM); ++ if (status < 0) ++ break; ++ } while (0); ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++#if 0 ++static int SC_GetOpParamCommand(struct drxd_state *state, u16 * result) ++{ ++ int status = 0; ++ ++ mutex_lock(&state->mutex); ++ do { ++ status = SC_WaitForReady(state); ++ if (status < 0) ++ break; ++ status = SC_SendCommand(state, SC_RA_RAM_CMD_GET_OP_PARAM); ++ if (status < 0) ++ break; ++ status = Read16(state, SC_RA_RAM_PARAM0__A, result, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ mutex_unlock(&state->mutex); ++ return status; ++} ++#endif ++ ++static int ConfigureMPEGOutput(struct drxd_state *state, int bEnableOutput) ++{ ++ int status; ++ ++ do { ++ u16 EcOcRegIprInvMpg = 0; ++ u16 EcOcRegOcModeLop = 0; ++ u16 EcOcRegOcModeHip = 0; ++ u16 EcOcRegOcMpgSio = 0; ++ ++ /*CHK_ERROR(Read16(state, EC_OC_REG_OC_MODE_LOP__A, &EcOcRegOcModeLop, 0)); */ ++ ++ if (state->operation_mode == OM_DVBT_Diversity_Front) { ++ if (bEnableOutput) { ++ EcOcRegOcModeHip |= ++ B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR; ++ } else ++ EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; ++ EcOcRegOcModeLop |= ++ EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; ++ } else { ++ EcOcRegOcModeLop = state->m_EcOcRegOcModeLop; ++ ++ if (bEnableOutput) ++ EcOcRegOcMpgSio &= (~(EC_OC_REG_OC_MPG_SIO__M)); ++ else ++ EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; ++ ++ /* Don't Insert RS Byte */ ++ if (state->insert_rs_byte) { ++ EcOcRegOcModeLop &= ++ (~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M)); ++ EcOcRegOcModeHip &= ++ (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); ++ EcOcRegOcModeHip |= ++ EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE; ++ } else { ++ EcOcRegOcModeLop |= ++ EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; ++ EcOcRegOcModeHip &= ++ (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); ++ EcOcRegOcModeHip |= ++ EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE; ++ } ++ ++ /* Mode = Parallel */ ++ if (state->enable_parallel) ++ EcOcRegOcModeLop &= ++ (~(EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M)); ++ else ++ EcOcRegOcModeLop |= ++ EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL; ++ } ++ /* Invert Data */ ++ /* EcOcRegIprInvMpg |= 0x00FF; */ ++ EcOcRegIprInvMpg &= (~(0x00FF)); ++ ++ /* Invert Error ( we don't use the pin ) */ ++ /* EcOcRegIprInvMpg |= 0x0100; */ ++ EcOcRegIprInvMpg &= (~(0x0100)); ++ ++ /* Invert Start ( we don't use the pin ) */ ++ /* EcOcRegIprInvMpg |= 0x0200; */ ++ EcOcRegIprInvMpg &= (~(0x0200)); ++ ++ /* Invert Valid ( we don't use the pin ) */ ++ /* EcOcRegIprInvMpg |= 0x0400; */ ++ EcOcRegIprInvMpg &= (~(0x0400)); ++ ++ /* Invert Clock */ ++ /* EcOcRegIprInvMpg |= 0x0800; */ ++ EcOcRegIprInvMpg &= (~(0x0800)); ++ ++ /* EcOcRegOcModeLop =0x05; */ ++ status = Write16(state, EC_OC_REG_IPR_INV_MPG__A, EcOcRegIprInvMpg, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, EcOcRegOcModeLop, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_OC_MODE_HIP__A, EcOcRegOcModeHip, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OC_REG_OC_MPG_SIO__A, EcOcRegOcMpgSio, 0); ++ if (status < 0) ++ break; ++ } while (0); ++ return status; ++} ++ ++static int SetDeviceTypeId(struct drxd_state *state) ++{ ++ int status = 0; ++ u16 deviceId = 0; ++ ++ do { ++ status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); ++ if (status < 0) ++ break; ++ /* TODO: why twice? */ ++ status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); ++ if (status < 0) ++ break; ++ printk(KERN_INFO "drxd: deviceId = %04x\n", deviceId); ++ ++ state->type_A = 0; ++ state->PGA = 0; ++ state->diversity = 0; ++ if (deviceId == 0) { /* on A2 only 3975 available */ ++ state->type_A = 1; ++ printk(KERN_INFO "DRX3975D-A2\n"); ++ } else { ++ deviceId >>= 12; ++ printk(KERN_INFO "DRX397%dD-B1\n", deviceId); ++ switch (deviceId) { ++ case 4: ++ state->diversity = 1; ++ case 3: ++ case 7: ++ state->PGA = 1; ++ break; ++ case 6: ++ state->diversity = 1; ++ case 5: ++ case 8: ++ break; ++ default: ++ status = -1; ++ break; ++ } ++ } ++ } while (0); ++ ++ if (status < 0) ++ return status; ++ ++ /* Init Table selection */ ++ state->m_InitAtomicRead = DRXD_InitAtomicRead; ++ state->m_InitSC = DRXD_InitSC; ++ state->m_ResetECRAM = DRXD_ResetECRAM; ++ if (state->type_A) { ++ state->m_ResetCEFR = DRXD_ResetCEFR; ++ state->m_InitFE_1 = DRXD_InitFEA2_1; ++ state->m_InitFE_2 = DRXD_InitFEA2_2; ++ state->m_InitCP = DRXD_InitCPA2; ++ state->m_InitCE = DRXD_InitCEA2; ++ state->m_InitEQ = DRXD_InitEQA2; ++ state->m_InitEC = DRXD_InitECA2; ++ if (load_firmware(state, DRX_FW_FILENAME_A2)) ++ return -EIO; ++ } else { ++ state->m_ResetCEFR = NULL; ++ state->m_InitFE_1 = DRXD_InitFEB1_1; ++ state->m_InitFE_2 = DRXD_InitFEB1_2; ++ state->m_InitCP = DRXD_InitCPB1; ++ state->m_InitCE = DRXD_InitCEB1; ++ state->m_InitEQ = DRXD_InitEQB1; ++ state->m_InitEC = DRXD_InitECB1; ++ if (load_firmware(state, DRX_FW_FILENAME_B1)) ++ return -EIO; ++ } ++ if (state->diversity) { ++ state->m_InitDiversityFront = DRXD_InitDiversityFront; ++ state->m_InitDiversityEnd = DRXD_InitDiversityEnd; ++ state->m_DisableDiversity = DRXD_DisableDiversity; ++ state->m_StartDiversityFront = DRXD_StartDiversityFront; ++ state->m_StartDiversityEnd = DRXD_StartDiversityEnd; ++ state->m_DiversityDelay8MHZ = DRXD_DiversityDelay8MHZ; ++ state->m_DiversityDelay6MHZ = DRXD_DiversityDelay6MHZ; ++ } else { ++ state->m_InitDiversityFront = NULL; ++ state->m_InitDiversityEnd = NULL; ++ state->m_DisableDiversity = NULL; ++ state->m_StartDiversityFront = NULL; ++ state->m_StartDiversityEnd = NULL; ++ state->m_DiversityDelay8MHZ = NULL; ++ state->m_DiversityDelay6MHZ = NULL; ++ } ++ ++ return status; ++} ++ ++static int CorrectSysClockDeviation(struct drxd_state *state) ++{ ++ int status; ++ s32 incr = 0; ++ s32 nomincr = 0; ++ u32 bandwidth = 0; ++ u32 sysClockInHz = 0; ++ u32 sysClockFreq = 0; /* in kHz */ ++ s16 oscClockDeviation; ++ s16 Diff; ++ ++ do { ++ /* Retrieve bandwidth and incr, sanity check */ ++ ++ /* These accesses should be AtomicReadReg32, but that ++ causes trouble (at least for diversity */ ++ status = Read32(state, LC_RA_RAM_IFINCR_NOM_L__A, ((u32 *) &nomincr), 0); ++ if (status < 0) ++ break; ++ status = Read32(state, FE_IF_REG_INCR0__A, (u32 *) &incr, 0); ++ if (status < 0) ++ break; ++ ++ if (state->type_A) { ++ if ((nomincr - incr < -500) || (nomincr - incr > 500)) ++ break; ++ } else { ++ if ((nomincr - incr < -2000) || (nomincr - incr > 2000)) ++ break; ++ } ++ ++ switch (state->props.bandwidth_hz) { ++ case 8000000: ++ bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; ++ break; ++ case 7000000: ++ bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; ++ break; ++ case 6000000: ++ bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; ++ break; ++ default: ++ return -1; ++ break; ++ } ++ ++ /* Compute new sysclock value ++ sysClockFreq = (((incr + 2^23)*bandwidth)/2^21)/1000 */ ++ incr += (1 << 23); ++ sysClockInHz = MulDiv32(incr, bandwidth, 1 << 21); ++ sysClockFreq = (u32) (sysClockInHz / 1000); ++ /* rounding */ ++ if ((sysClockInHz % 1000) > 500) ++ sysClockFreq++; ++ ++ /* Compute clock deviation in ppm */ ++ oscClockDeviation = (u16) ((((s32) (sysClockFreq) - ++ (s32) ++ (state->expected_sys_clock_freq)) * ++ 1000000L) / ++ (s32) ++ (state->expected_sys_clock_freq)); ++ ++ Diff = oscClockDeviation - state->osc_clock_deviation; ++ /*printk(KERN_INFO "sysclockdiff=%d\n", Diff); */ ++ if (Diff >= -200 && Diff <= 200) { ++ state->sys_clock_freq = (u16) sysClockFreq; ++ if (oscClockDeviation != state->osc_clock_deviation) { ++ if (state->config.osc_deviation) { ++ state->config.osc_deviation(state->priv, ++ oscClockDeviation, ++ 1); ++ state->osc_clock_deviation = ++ oscClockDeviation; ++ } ++ } ++ /* switch OFF SRMM scan in SC */ ++ status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DONT_SCAN, 0); ++ if (status < 0) ++ break; ++ /* overrule FE_IF internal value for ++ proper re-locking */ ++ status = Write16(state, SC_RA_RAM_IF_SAVE__AX, state->current_fe_if_incr, 0); ++ if (status < 0) ++ break; ++ state->cscd_state = CSCD_SAVED; ++ } ++ } while (0); ++ ++ return status; ++} ++ ++static int DRX_Stop(struct drxd_state *state) ++{ ++ int status; ++ ++ if (state->drxd_state != DRXD_STARTED) ++ return 0; ++ ++ do { ++ if (state->cscd_state != CSCD_SAVED) { ++ u32 lock; ++ status = DRX_GetLockStatus(state, &lock); ++ if (status < 0) ++ break; ++ } ++ ++ status = StopOC(state); ++ if (status < 0) ++ break; ++ ++ state->drxd_state = DRXD_STOPPED; ++ ++ status = ConfigureMPEGOutput(state, 0); ++ if (status < 0) ++ break; ++ ++ if (state->type_A) { ++ /* Stop relevant processors off the device */ ++ status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ } else { ++ /* Stop all processors except HI & CC & FE */ ++ status = Write16(state, B_SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_FT_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_CP_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_CE_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_EQ_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0); ++ if (status < 0) ++ break; ++ } ++ ++ } while (0); ++ return status; ++} ++ ++#if 0 /* Currently unused */ ++static int SetOperationMode(struct drxd_state *state, int oMode) ++{ ++ int status; ++ ++ do { ++ if (state->drxd_state != DRXD_STOPPED) { ++ status = -1; ++ break; ++ } ++ ++ if (oMode == state->operation_mode) { ++ status = 0; ++ break; ++ } ++ ++ if (oMode != OM_Default && !state->diversity) { ++ status = -1; ++ break; ++ } ++ ++ switch (oMode) { ++ case OM_DVBT_Diversity_Front: ++ status = WriteTable(state, state->m_InitDiversityFront); ++ break; ++ case OM_DVBT_Diversity_End: ++ status = WriteTable(state, state->m_InitDiversityEnd); ++ break; ++ case OM_Default: ++ /* We need to check how to ++ get DRXD out of diversity */ ++ default: ++ status = WriteTable(state, state->m_DisableDiversity); ++ break; ++ } ++ } while (0); ++ ++ if (!status) ++ state->operation_mode = oMode; ++ return status; ++} ++#endif ++ ++static int StartDiversity(struct drxd_state *state) ++{ ++ int status = 0; ++ u16 rcControl; ++ ++ do { ++ if (state->operation_mode == OM_DVBT_Diversity_Front) { ++ status = WriteTable(state, state->m_StartDiversityFront); ++ if (status < 0) ++ break; ++ } else if (state->operation_mode == OM_DVBT_Diversity_End) { ++ status = WriteTable(state, state->m_StartDiversityEnd); ++ if (status < 0) ++ break; ++ if (state->props.bandwidth_hz == 8000000) { ++ status = WriteTable(state, state->m_DiversityDelay8MHZ); ++ if (status < 0) ++ break; ++ } else { ++ status = WriteTable(state, state->m_DiversityDelay6MHZ); ++ if (status < 0) ++ break; ++ } ++ ++ status = Read16(state, B_EQ_REG_RC_SEL_CAR__A, &rcControl, 0); ++ if (status < 0) ++ break; ++ rcControl &= ~(B_EQ_REG_RC_SEL_CAR_FFTMODE__M); ++ rcControl |= B_EQ_REG_RC_SEL_CAR_DIV_ON | ++ /* combining enabled */ ++ B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | ++ B_EQ_REG_RC_SEL_CAR_PASS_A_CC | ++ B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC; ++ status = Write16(state, B_EQ_REG_RC_SEL_CAR__A, rcControl, 0); ++ if (status < 0) ++ break; ++ } ++ } while (0); ++ return status; ++} ++ ++static int SetFrequencyShift(struct drxd_state *state, ++ u32 offsetFreq, int channelMirrored) ++{ ++ int negativeShift = (state->tuner_mirrors == channelMirrored); ++ ++ /* Handle all mirroring ++ * ++ * Note: ADC mirroring (aliasing) is implictly handled by limiting ++ * feFsRegAddInc to 28 bits below ++ * (if the result before masking is more than 28 bits, this means ++ * that the ADC is mirroring. ++ * The masking is in fact the aliasing of the ADC) ++ * ++ */ ++ ++ /* Compute register value, unsigned computation */ ++ state->fe_fs_add_incr = MulDiv32(state->intermediate_freq + ++ offsetFreq, ++ 1 << 28, state->sys_clock_freq); ++ /* Remove integer part */ ++ state->fe_fs_add_incr &= 0x0FFFFFFFL; ++ if (negativeShift) ++ state->fe_fs_add_incr = ((1 << 28) - state->fe_fs_add_incr); ++ ++ /* Save the frequency shift without tunerOffset compensation ++ for CtrlGetChannel. */ ++ state->org_fe_fs_add_incr = MulDiv32(state->intermediate_freq, ++ 1 << 28, state->sys_clock_freq); ++ /* Remove integer part */ ++ state->org_fe_fs_add_incr &= 0x0FFFFFFFL; ++ if (negativeShift) ++ state->org_fe_fs_add_incr = ((1L << 28) - ++ state->org_fe_fs_add_incr); ++ ++ return Write32(state, FE_FS_REG_ADD_INC_LOP__A, ++ state->fe_fs_add_incr, 0); ++} ++ ++static int SetCfgNoiseCalibration(struct drxd_state *state, ++ struct SNoiseCal *noiseCal) ++{ ++ u16 beOptEna; ++ int status = 0; ++ ++ do { ++ status = Read16(state, SC_RA_RAM_BE_OPT_ENA__A, &beOptEna, 0); ++ if (status < 0) ++ break; ++ if (noiseCal->cpOpt) { ++ beOptEna |= (1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); ++ } else { ++ beOptEna &= ~(1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); ++ status = Write16(state, CP_REG_AC_NEXP_OFFS__A, noiseCal->cpNexpOfs, 0); ++ if (status < 0) ++ break; ++ } ++ status = Write16(state, SC_RA_RAM_BE_OPT_ENA__A, beOptEna, 0); ++ if (status < 0) ++ break; ++ ++ if (!state->type_A) { ++ status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_2K__A, noiseCal->tdCal2k, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_8K__A, noiseCal->tdCal8k, 0); ++ if (status < 0) ++ break; ++ } ++ } while (0); ++ ++ return status; ++} ++ ++static int DRX_Start(struct drxd_state *state, s32 off) ++{ ++ struct dtv_frontend_properties *p = &state->props; ++ int status; ++ ++ u16 transmissionParams = 0; ++ u16 operationMode = 0; ++ u16 qpskTdTpsPwr = 0; ++ u16 qam16TdTpsPwr = 0; ++ u16 qam64TdTpsPwr = 0; ++ u32 feIfIncr = 0; ++ u32 bandwidth = 0; ++ int mirrorFreqSpect; ++ ++ u16 qpskSnCeGain = 0; ++ u16 qam16SnCeGain = 0; ++ u16 qam64SnCeGain = 0; ++ u16 qpskIsGainMan = 0; ++ u16 qam16IsGainMan = 0; ++ u16 qam64IsGainMan = 0; ++ u16 qpskIsGainExp = 0; ++ u16 qam16IsGainExp = 0; ++ u16 qam64IsGainExp = 0; ++ u16 bandwidthParam = 0; ++ ++ if (off < 0) ++ off = (off - 500) / 1000; ++ else ++ off = (off + 500) / 1000; ++ ++ do { ++ if (state->drxd_state != DRXD_STOPPED) ++ return -1; ++ status = ResetECOD(state); ++ if (status < 0) ++ break; ++ if (state->type_A) { ++ status = InitSC(state); ++ if (status < 0) ++ break; ++ } else { ++ status = InitFT(state); ++ if (status < 0) ++ break; ++ status = InitCP(state); ++ if (status < 0) ++ break; ++ status = InitCE(state); ++ if (status < 0) ++ break; ++ status = InitEQ(state); ++ if (status < 0) ++ break; ++ status = InitSC(state); ++ if (status < 0) ++ break; ++ } ++ ++ /* Restore current IF & RF AGC settings */ ++ ++ status = SetCfgIfAgc(state, &state->if_agc_cfg); ++ if (status < 0) ++ break; ++ status = SetCfgRfAgc(state, &state->rf_agc_cfg); ++ if (status < 0) ++ break; ++ ++ mirrorFreqSpect = (state->props.inversion == INVERSION_ON); ++ ++ switch (p->transmission_mode) { ++ default: /* Not set, detect it automatically */ ++ operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; ++ /* fall through , try first guess DRX_FFTMODE_8K */ ++ case TRANSMISSION_MODE_8K: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; ++ if (state->type_A) { ++ status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_8K, 0x0000); ++ if (status < 0) ++ break; ++ qpskSnCeGain = 99; ++ qam16SnCeGain = 83; ++ qam64SnCeGain = 67; ++ } ++ break; ++ case TRANSMISSION_MODE_2K: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_2K; ++ if (state->type_A) { ++ status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_2K, 0x0000); ++ if (status < 0) ++ break; ++ qpskSnCeGain = 97; ++ qam16SnCeGain = 71; ++ qam64SnCeGain = 65; ++ } ++ break; ++ } ++ ++ switch (p->guard_interval) { ++ case GUARD_INTERVAL_1_4: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; ++ break; ++ case GUARD_INTERVAL_1_8: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_8; ++ break; ++ case GUARD_INTERVAL_1_16: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_16; ++ break; ++ case GUARD_INTERVAL_1_32: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_32; ++ break; ++ default: /* Not set, detect it automatically */ ++ operationMode |= SC_RA_RAM_OP_AUTO_GUARD__M; ++ /* try first guess 1/4 */ ++ transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; ++ break; ++ } ++ ++ switch (p->hierarchy) { ++ case HIERARCHY_1: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A1; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0001, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_ALPHA__A, 0x0001, 0x0000); ++ if (status < 0) ++ break; ++ ++ qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; ++ qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA1; ++ qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA1; ++ ++ qpskIsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; ++ qam16IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; ++ qam64IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; ++ ++ qpskIsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; ++ qam16IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; ++ qam64IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; ++ } ++ break; ++ ++ case HIERARCHY_2: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A2; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0002, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_ALPHA__A, 0x0002, 0x0000); ++ if (status < 0) ++ break; ++ ++ qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; ++ qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA2; ++ qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA2; ++ ++ qpskIsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; ++ qam16IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE; ++ qam64IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE; ++ ++ qpskIsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; ++ qam16IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE; ++ qam64IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE; ++ } ++ break; ++ case HIERARCHY_4: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A4; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0003, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_ALPHA__A, 0x0003, 0x0000); ++ if (status < 0) ++ break; ++ ++ qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; ++ qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA4; ++ qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA4; ++ ++ qpskIsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; ++ qam16IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE; ++ qam64IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE; ++ ++ qpskIsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; ++ qam16IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE; ++ qam64IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE; ++ } ++ break; ++ case HIERARCHY_AUTO: ++ default: ++ /* Not set, detect it automatically, start with none */ ++ operationMode |= SC_RA_RAM_OP_AUTO_HIER__M; ++ transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_NO; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_ALPHA__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ ++ qpskTdTpsPwr = EQ_TD_TPS_PWR_QPSK; ++ qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHAN; ++ qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHAN; ++ ++ qpskIsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE; ++ qam16IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; ++ qam64IsGainMan = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; ++ ++ qpskIsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE; ++ qam16IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; ++ qam64IsGainExp = ++ SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; ++ } ++ break; ++ } ++ status = status; ++ if (status < 0) ++ break; ++ ++ switch (p->modulation) { ++ default: ++ operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; ++ /* fall through , try first guess ++ DRX_CONSTELLATION_QAM64 */ ++ case QAM_64: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_CONST__A, 0x0002, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_64QAM, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0020, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0008, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0002, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam64TdTpsPwr, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_SN_CEGAIN__A, qam64SnCeGain, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam64IsGainMan, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam64IsGainExp, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ case QPSK: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QPSK; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_CONST__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_QPSK, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qpskTdTpsPwr, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_SN_CEGAIN__A, qpskSnCeGain, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qpskIsGainMan, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qpskIsGainExp, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ ++ case QAM_16: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM16; ++ if (state->type_A) { ++ status = Write16(state, EQ_REG_OT_CONST__A, 0x0001, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_16QAM, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0004, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); ++ if (status < 0) ++ break; ++ ++ status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam16TdTpsPwr, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_SN_CEGAIN__A, qam16SnCeGain, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam16IsGainMan, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam16IsGainExp, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ ++ } ++ status = status; ++ if (status < 0) ++ break; ++ ++ switch (DRX_CHANNEL_HIGH) { ++ default: ++ case DRX_CHANNEL_AUTO: ++ case DRX_CHANNEL_LOW: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_LO; ++ status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_LO, 0x0000); ++ if (status < 0) ++ break; ++ break; ++ case DRX_CHANNEL_HIGH: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_HI; ++ status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_HI, 0x0000); ++ if (status < 0) ++ break; ++ break; ++ ++ } ++ ++ switch (p->code_rate_HP) { ++ case FEC_1_2: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_1_2; ++ if (state->type_A) { ++ status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C1_2, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ default: ++ operationMode |= SC_RA_RAM_OP_AUTO_RATE__M; ++ case FEC_2_3: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3; ++ if (state->type_A) { ++ status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C2_3, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ case FEC_3_4: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_3_4; ++ if (state->type_A) { ++ status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C3_4, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ case FEC_5_6: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_5_6; ++ if (state->type_A) { ++ status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C5_6, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ case FEC_7_8: ++ transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_7_8; ++ if (state->type_A) { ++ status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C7_8, 0x0000); ++ if (status < 0) ++ break; ++ } ++ break; ++ } ++ status = status; ++ if (status < 0) ++ break; ++ ++ /* First determine real bandwidth (Hz) */ ++ /* Also set delay for impulse noise cruncher (only A2) */ ++ /* Also set parameters for EC_OC fix, note ++ EC_OC_REG_TMD_HIL_MAR is changed ++ by SC for fix for some 8K,1/8 guard but is restored by ++ InitEC and ResetEC ++ functions */ ++ switch (p->bandwidth_hz) { ++ case 0: ++ p->bandwidth_hz = 8000000; ++ /* fall through */ ++ case 8000000: ++ /* (64/7)*(8/8)*1000000 */ ++ bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; ++ ++ bandwidthParam = 0; ++ status = Write16(state, ++ FE_AG_REG_IND_DEL__A, 50, 0x0000); ++ break; ++ case 7000000: ++ /* (64/7)*(7/8)*1000000 */ ++ bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; ++ bandwidthParam = 0x4807; /*binary:0100 1000 0000 0111 */ ++ status = Write16(state, ++ FE_AG_REG_IND_DEL__A, 59, 0x0000); ++ break; ++ case 6000000: ++ /* (64/7)*(6/8)*1000000 */ ++ bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; ++ bandwidthParam = 0x0F07; /*binary: 0000 1111 0000 0111 */ ++ status = Write16(state, ++ FE_AG_REG_IND_DEL__A, 71, 0x0000); ++ break; ++ default: ++ status = -EINVAL; ++ } ++ if (status < 0) ++ break; ++ ++ status = Write16(state, SC_RA_RAM_BAND__A, bandwidthParam, 0x0000); ++ if (status < 0) ++ break; ++ ++ { ++ u16 sc_config; ++ status = Read16(state, SC_RA_RAM_CONFIG__A, &sc_config, 0); ++ if (status < 0) ++ break; ++ ++ /* enable SLAVE mode in 2k 1/32 to ++ prevent timing change glitches */ ++ if ((p->transmission_mode == TRANSMISSION_MODE_2K) && ++ (p->guard_interval == GUARD_INTERVAL_1_32)) { ++ /* enable slave */ ++ sc_config |= SC_RA_RAM_CONFIG_SLAVE__M; ++ } else { ++ /* disable slave */ ++ sc_config &= ~SC_RA_RAM_CONFIG_SLAVE__M; ++ } ++ status = Write16(state, SC_RA_RAM_CONFIG__A, sc_config, 0); ++ if (status < 0) ++ break; ++ } ++ ++ status = SetCfgNoiseCalibration(state, &state->noise_cal); ++ if (status < 0) ++ break; ++ ++ if (state->cscd_state == CSCD_INIT) { ++ /* switch on SRMM scan in SC */ ++ status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DO_SCAN, 0x0000); ++ if (status < 0) ++ break; ++/* CHK_ERROR(Write16(SC_RA_RAM_SAMPLE_RATE_STEP__A, DRXD_OSCDEV_STEP, 0x0000));*/ ++ state->cscd_state = CSCD_SET; ++ } ++ ++ /* Now compute FE_IF_REG_INCR */ ++ /*((( SysFreq/BandWidth)/2)/2) -1) * 2^23) => ++ ((SysFreq / BandWidth) * (2^21) ) - (2^23) */ ++ feIfIncr = MulDiv32(state->sys_clock_freq * 1000, ++ (1ULL << 21), bandwidth) - (1 << 23); ++ status = Write16(state, FE_IF_REG_INCR0__A, (u16) (feIfIncr & FE_IF_REG_INCR0__M), 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, FE_IF_REG_INCR1__A, (u16) ((feIfIncr >> FE_IF_REG_INCR0__W) & FE_IF_REG_INCR1__M), 0x0000); ++ if (status < 0) ++ break; ++ /* Bandwidth setting done */ ++ ++ /* Mirror & frequency offset */ ++ SetFrequencyShift(state, off, mirrorFreqSpect); ++ ++ /* Start SC, write channel settings to SC */ ++ ++ /* Enable SC after setting all other parameters */ ++ status = Write16(state, SC_COMM_STATE__A, 0, 0x0000); ++ if (status < 0) ++ break; ++ status = Write16(state, SC_COMM_EXEC__A, 1, 0x0000); ++ if (status < 0) ++ break; ++ ++ /* Write SC parameter registers, operation mode */ ++#if 1 ++ operationMode = (SC_RA_RAM_OP_AUTO_MODE__M | ++ SC_RA_RAM_OP_AUTO_GUARD__M | ++ SC_RA_RAM_OP_AUTO_CONST__M | ++ SC_RA_RAM_OP_AUTO_HIER__M | ++ SC_RA_RAM_OP_AUTO_RATE__M); ++#endif ++ status = SC_SetPrefParamCommand(state, 0x0000, transmissionParams, operationMode); ++ if (status < 0) ++ break; ++ ++ /* Start correct processes to get in lock */ ++ status = SC_ProcStartCommand(state, SC_RA_RAM_PROC_LOCKTRACK, SC_RA_RAM_SW_EVENT_RUN_NMASK__M, SC_RA_RAM_LOCKTRACK_MIN); ++ if (status < 0) ++ break; ++ ++ status = StartOC(state); ++ if (status < 0) ++ break; ++ ++ if (state->operation_mode != OM_Default) { ++ status = StartDiversity(state); ++ if (status < 0) ++ break; ++ } ++ ++ state->drxd_state = DRXD_STARTED; ++ } while (0); ++ ++ return status; ++} ++ ++static int CDRXD(struct drxd_state *state, u32 IntermediateFrequency) ++{ ++ u32 ulRfAgcOutputLevel = 0xffffffff; ++ u32 ulRfAgcSettleLevel = 528; /* Optimum value for MT2060 */ ++ u32 ulRfAgcMinLevel = 0; /* Currently unused */ ++ u32 ulRfAgcMaxLevel = DRXD_FE_CTRL_MAX; /* Currently unused */ ++ u32 ulRfAgcSpeed = 0; /* Currently unused */ ++ u32 ulRfAgcMode = 0; /*2; Off */ ++ u32 ulRfAgcR1 = 820; ++ u32 ulRfAgcR2 = 2200; ++ u32 ulRfAgcR3 = 150; ++ u32 ulIfAgcMode = 0; /* Auto */ ++ u32 ulIfAgcOutputLevel = 0xffffffff; ++ u32 ulIfAgcSettleLevel = 0xffffffff; ++ u32 ulIfAgcMinLevel = 0xffffffff; ++ u32 ulIfAgcMaxLevel = 0xffffffff; ++ u32 ulIfAgcSpeed = 0xffffffff; ++ u32 ulIfAgcR1 = 820; ++ u32 ulIfAgcR2 = 2200; ++ u32 ulIfAgcR3 = 150; ++ u32 ulClock = state->config.clock; ++ u32 ulSerialMode = 0; ++ u32 ulEcOcRegOcModeLop = 4; /* Dynamic DTO source */ ++ u32 ulHiI2cDelay = HI_I2C_DELAY; ++ u32 ulHiI2cBridgeDelay = HI_I2C_BRIDGE_DELAY; ++ u32 ulHiI2cPatch = 0; ++ u32 ulEnvironment = APPENV_PORTABLE; ++ u32 ulEnvironmentDiversity = APPENV_MOBILE; ++ u32 ulIFFilter = IFFILTER_SAW; ++ ++ state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; ++ state->if_agc_cfg.outputLevel = 0; ++ state->if_agc_cfg.settleLevel = 140; ++ state->if_agc_cfg.minOutputLevel = 0; ++ state->if_agc_cfg.maxOutputLevel = 1023; ++ state->if_agc_cfg.speed = 904; ++ ++ if (ulIfAgcMode == 1 && ulIfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { ++ state->if_agc_cfg.ctrlMode = AGC_CTRL_USER; ++ state->if_agc_cfg.outputLevel = (u16) (ulIfAgcOutputLevel); ++ } ++ ++ if (ulIfAgcMode == 0 && ++ ulIfAgcSettleLevel <= DRXD_FE_CTRL_MAX && ++ ulIfAgcMinLevel <= DRXD_FE_CTRL_MAX && ++ ulIfAgcMaxLevel <= DRXD_FE_CTRL_MAX && ++ ulIfAgcSpeed <= DRXD_FE_CTRL_MAX) { ++ state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; ++ state->if_agc_cfg.settleLevel = (u16) (ulIfAgcSettleLevel); ++ state->if_agc_cfg.minOutputLevel = (u16) (ulIfAgcMinLevel); ++ state->if_agc_cfg.maxOutputLevel = (u16) (ulIfAgcMaxLevel); ++ state->if_agc_cfg.speed = (u16) (ulIfAgcSpeed); ++ } ++ ++ state->if_agc_cfg.R1 = (u16) (ulIfAgcR1); ++ state->if_agc_cfg.R2 = (u16) (ulIfAgcR2); ++ state->if_agc_cfg.R3 = (u16) (ulIfAgcR3); ++ ++ state->rf_agc_cfg.R1 = (u16) (ulRfAgcR1); ++ state->rf_agc_cfg.R2 = (u16) (ulRfAgcR2); ++ state->rf_agc_cfg.R3 = (u16) (ulRfAgcR3); ++ ++ state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; ++ /* rest of the RFAgcCfg structure currently unused */ ++ if (ulRfAgcMode == 1 && ulRfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { ++ state->rf_agc_cfg.ctrlMode = AGC_CTRL_USER; ++ state->rf_agc_cfg.outputLevel = (u16) (ulRfAgcOutputLevel); ++ } ++ ++ if (ulRfAgcMode == 0 && ++ ulRfAgcSettleLevel <= DRXD_FE_CTRL_MAX && ++ ulRfAgcMinLevel <= DRXD_FE_CTRL_MAX && ++ ulRfAgcMaxLevel <= DRXD_FE_CTRL_MAX && ++ ulRfAgcSpeed <= DRXD_FE_CTRL_MAX) { ++ state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; ++ state->rf_agc_cfg.settleLevel = (u16) (ulRfAgcSettleLevel); ++ state->rf_agc_cfg.minOutputLevel = (u16) (ulRfAgcMinLevel); ++ state->rf_agc_cfg.maxOutputLevel = (u16) (ulRfAgcMaxLevel); ++ state->rf_agc_cfg.speed = (u16) (ulRfAgcSpeed); ++ } ++ ++ if (ulRfAgcMode == 2) ++ state->rf_agc_cfg.ctrlMode = AGC_CTRL_OFF; ++ ++ if (ulEnvironment <= 2) ++ state->app_env_default = (enum app_env) ++ (ulEnvironment); ++ if (ulEnvironmentDiversity <= 2) ++ state->app_env_diversity = (enum app_env) ++ (ulEnvironmentDiversity); ++ ++ if (ulIFFilter == IFFILTER_DISCRETE) { ++ /* discrete filter */ ++ state->noise_cal.cpOpt = 0; ++ state->noise_cal.cpNexpOfs = 40; ++ state->noise_cal.tdCal2k = -40; ++ state->noise_cal.tdCal8k = -24; ++ } else { ++ /* SAW filter */ ++ state->noise_cal.cpOpt = 1; ++ state->noise_cal.cpNexpOfs = 0; ++ state->noise_cal.tdCal2k = -21; ++ state->noise_cal.tdCal8k = -24; ++ } ++ state->m_EcOcRegOcModeLop = (u16) (ulEcOcRegOcModeLop); ++ ++ state->chip_adr = (state->config.demod_address << 1) | 1; ++ switch (ulHiI2cPatch) { ++ case 1: ++ state->m_HiI2cPatch = DRXD_HiI2cPatch_1; ++ break; ++ case 3: ++ state->m_HiI2cPatch = DRXD_HiI2cPatch_3; ++ break; ++ default: ++ state->m_HiI2cPatch = NULL; ++ } ++ ++ /* modify tuner and clock attributes */ ++ state->intermediate_freq = (u16) (IntermediateFrequency / 1000); ++ /* expected system clock frequency in kHz */ ++ state->expected_sys_clock_freq = 48000; ++ /* real system clock frequency in kHz */ ++ state->sys_clock_freq = 48000; ++ state->osc_clock_freq = (u16) ulClock; ++ state->osc_clock_deviation = 0; ++ state->cscd_state = CSCD_INIT; ++ state->drxd_state = DRXD_UNINITIALIZED; ++ ++ state->PGA = 0; ++ state->type_A = 0; ++ state->tuner_mirrors = 0; ++ ++ /* modify MPEG output attributes */ ++ state->insert_rs_byte = state->config.insert_rs_byte; ++ state->enable_parallel = (ulSerialMode != 1); ++ ++ /* Timing div, 250ns/Psys */ ++ /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */ ++ ++ state->hi_cfg_timing_div = (u16) ((state->sys_clock_freq / 1000) * ++ ulHiI2cDelay) / 1000; ++ /* Bridge delay, uses oscilator clock */ ++ /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */ ++ state->hi_cfg_bridge_delay = (u16) ((state->osc_clock_freq / 1000) * ++ ulHiI2cBridgeDelay) / 1000; ++ ++ state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; ++ /* state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; */ ++ state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; ++ return 0; ++} ++ ++static int DRXD_init(struct drxd_state *state, const u8 *fw, u32 fw_size) ++{ ++ int status = 0; ++ u32 driverVersion; ++ ++ if (state->init_done) ++ return 0; ++ ++ CDRXD(state, state->config.IF ? state->config.IF : 36000000); ++ ++ do { ++ state->operation_mode = OM_Default; ++ ++ status = SetDeviceTypeId(state); ++ if (status < 0) ++ break; ++ ++ /* Apply I2c address patch to B1 */ ++ if (!state->type_A && state->m_HiI2cPatch != NULL) ++ status = WriteTable(state, state->m_HiI2cPatch); ++ if (status < 0) ++ break; ++ ++ if (state->type_A) { ++ /* HI firmware patch for UIO readout, ++ avoid clearing of result register */ ++ status = Write16(state, 0x43012D, 0x047f, 0); ++ if (status < 0) ++ break; ++ } ++ ++ status = HI_ResetCommand(state); ++ if (status < 0) ++ break; ++ ++ status = StopAllProcessors(state); ++ if (status < 0) ++ break; ++ status = InitCC(state); ++ if (status < 0) ++ break; ++ ++ state->osc_clock_deviation = 0; ++ ++ if (state->config.osc_deviation) ++ state->osc_clock_deviation = ++ state->config.osc_deviation(state->priv, 0, 0); ++ { ++ /* Handle clock deviation */ ++ s32 devB; ++ s32 devA = (s32) (state->osc_clock_deviation) * ++ (s32) (state->expected_sys_clock_freq); ++ /* deviation in kHz */ ++ s32 deviation = (devA / (1000000L)); ++ /* rounding, signed */ ++ if (devA > 0) ++ devB = (2); ++ else ++ devB = (-2); ++ if ((devB * (devA % 1000000L) > 1000000L)) { ++ /* add +1 or -1 */ ++ deviation += (devB / 2); ++ } ++ ++ state->sys_clock_freq = ++ (u16) ((state->expected_sys_clock_freq) + ++ deviation); ++ } ++ status = InitHI(state); ++ if (status < 0) ++ break; ++ status = InitAtomicRead(state); ++ if (status < 0) ++ break; ++ ++ status = EnableAndResetMB(state); ++ if (status < 0) ++ break; ++ if (state->type_A) ++ status = ResetCEFR(state); ++ if (status < 0) ++ break; ++ ++ if (fw) { ++ status = DownloadMicrocode(state, fw, fw_size); ++ if (status < 0) ++ break; ++ } else { ++ status = DownloadMicrocode(state, state->microcode, state->microcode_length); ++ if (status < 0) ++ break; ++ } ++ ++ if (state->PGA) { ++ state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; ++ SetCfgPga(state, 0); /* PGA = 0 dB */ ++ } else { ++ state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; ++ } ++ ++ state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; ++ ++ status = InitFE(state); ++ if (status < 0) ++ break; ++ status = InitFT(state); ++ if (status < 0) ++ break; ++ status = InitCP(state); ++ if (status < 0) ++ break; ++ status = InitCE(state); ++ if (status < 0) ++ break; ++ status = InitEQ(state); ++ if (status < 0) ++ break; ++ status = InitEC(state); ++ if (status < 0) ++ break; ++ status = InitSC(state); ++ if (status < 0) ++ break; ++ ++ status = SetCfgIfAgc(state, &state->if_agc_cfg); ++ if (status < 0) ++ break; ++ status = SetCfgRfAgc(state, &state->rf_agc_cfg); ++ if (status < 0) ++ break; ++ ++ state->cscd_state = CSCD_INIT; ++ status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); ++ if (status < 0) ++ break; ++ ++ driverVersion = (((VERSION_MAJOR / 10) << 4) + ++ (VERSION_MAJOR % 10)) << 24; ++ driverVersion += (((VERSION_MINOR / 10) << 4) + ++ (VERSION_MINOR % 10)) << 16; ++ driverVersion += ((VERSION_PATCH / 1000) << 12) + ++ ((VERSION_PATCH / 100) << 8) + ++ ((VERSION_PATCH / 10) << 4) + (VERSION_PATCH % 10); ++ ++ status = Write32(state, SC_RA_RAM_DRIVER_VERSION__AX, driverVersion, 0); ++ if (status < 0) ++ break; ++ ++ status = StopOC(state); ++ if (status < 0) ++ break; ++ ++ state->drxd_state = DRXD_STOPPED; ++ state->init_done = 1; ++ status = 0; ++ } while (0); ++ return status; ++} ++ ++static int DRXD_status(struct drxd_state *state, u32 *pLockStatus) ++{ ++ DRX_GetLockStatus(state, pLockStatus); ++ ++ /*if (*pLockStatus&DRX_LOCK_MPEG) */ ++ if (*pLockStatus & DRX_LOCK_FEC) { ++ ConfigureMPEGOutput(state, 1); ++ /* Get status again, in case we have MPEG lock now */ ++ /*DRX_GetLockStatus(state, pLockStatus); */ ++ } ++ ++ return 0; ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static int drxd_read_signal_strength(struct dvb_frontend *fe, u16 * strength) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ u32 value; ++ int res; ++ ++ res = ReadIFAgc(state, &value); ++ if (res < 0) ++ *strength = 0; ++ else ++ *strength = 0xffff - (value << 4); ++ return 0; ++} ++ ++static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ u32 lock; ++ ++ DRXD_status(state, &lock); ++ *status = 0; ++ /* No MPEG lock in V255 firmware, bug ? */ ++#if 1 ++ if (lock & DRX_LOCK_MPEG) ++ *status |= FE_HAS_LOCK; ++#else ++ if (lock & DRX_LOCK_FEC) ++ *status |= FE_HAS_LOCK; ++#endif ++ if (lock & DRX_LOCK_FEC) ++ *status |= FE_HAS_VITERBI | FE_HAS_SYNC; ++ if (lock & DRX_LOCK_DEMOD) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ ++ return 0; ++} ++ ++static int drxd_init(struct dvb_frontend *fe) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ int err = 0; ++ ++/* if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */ ++ return DRXD_init(state, 0, 0); ++ ++ err = DRXD_init(state, state->fw->data, state->fw->size); ++ release_firmware(state->fw); ++ return err; ++} ++ ++int drxd_config_i2c(struct dvb_frontend *fe, int onoff) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ ++ if (state->config.disable_i2c_gate_ctrl == 1) ++ return 0; ++ ++ return DRX_ConfigureI2CBridge(state, onoff); ++} ++EXPORT_SYMBOL(drxd_config_i2c); ++ ++static int drxd_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *sets) ++{ ++ sets->min_delay_ms = 10000; ++ sets->max_drift = 0; ++ sets->step_size = 0; ++ return 0; ++} ++ ++static int drxd_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ *ber = 0; ++ return 0; ++} ++ ++static int drxd_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ *snr = 0; ++ return 0; ++} ++ ++static int drxd_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) ++{ ++ *ucblocks = 0; ++ return 0; ++} ++ ++static int drxd_sleep(struct dvb_frontend *fe) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ ++ ConfigureMPEGOutput(state, 0); ++ return 0; ++} ++ ++static int drxd_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ return drxd_config_i2c(fe, enable); ++} ++ ++static int drxd_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct drxd_state *state = fe->demodulator_priv; ++ s32 off = 0; ++ ++ state->props = *p; ++ DRX_Stop(state); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ msleep(200); ++ ++ return DRX_Start(state, off); ++} ++ ++static void drxd_release(struct dvb_frontend *fe) ++{ ++ struct drxd_state *state = fe->demodulator_priv; ++ ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops drxd_ops = { ++ .delsys = { SYS_DVBT}, ++ .info = { ++ .name = "Micronas DRXD DVB-T", ++ .frequency_min = 47125000, ++ .frequency_max = 855250000, ++ .frequency_stepsize = 166667, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_16 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS}, ++ ++ .release = drxd_release, ++ .init = drxd_init, ++ .sleep = drxd_sleep, ++ .i2c_gate_ctrl = drxd_i2c_gate_ctrl, ++ ++ .set_frontend = drxd_set_frontend, ++ .get_tune_settings = drxd_get_tune_settings, ++ ++ .read_status = drxd_read_status, ++ .read_ber = drxd_read_ber, ++ .read_signal_strength = drxd_read_signal_strength, ++ .read_snr = drxd_read_snr, ++ .read_ucblocks = drxd_read_ucblocks, ++}; ++ ++struct dvb_frontend *drxd_attach(const struct drxd_config *config, ++ void *priv, struct i2c_adapter *i2c, ++ struct device *dev) ++{ ++ struct drxd_state *state = NULL; ++ ++ state = kmalloc(sizeof(struct drxd_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ memset(state, 0, sizeof(*state)); ++ ++ state->ops = drxd_ops; ++ state->dev = dev; ++ state->config = *config; ++ state->i2c = i2c; ++ state->priv = priv; ++ ++ mutex_init(&state->mutex); ++ ++ if (Read16(state, 0, 0, 0) < 0) ++ goto error; ++ ++ state->frontend.ops = drxd_ops; ++ state->frontend.demodulator_priv = state; ++ ConfigureMPEGOutput(state, 0); ++ /* add few initialization to allow gate control */ ++ CDRXD(state, state->config.IF ? state->config.IF : 36000000); ++ InitHI(state); ++ ++ return &state->frontend; ++ ++error: ++ printk(KERN_ERR "drxd: not found\n"); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(drxd_attach); ++ ++MODULE_DESCRIPTION("DRXD driver"); ++MODULE_AUTHOR("Micronas"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/drxd_map_firm.h b/drivers/media/dvb-frontends/drxd_map_firm.h +new file mode 100644 +index 0000000..6bc553a +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxd_map_firm.h +@@ -0,0 +1,1013 @@ ++/* ++ * drx3973d_map_firm.h ++ * ++ * Copyright (C) 2006-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef __DRX3973D_MAP__H__ ++#define __DRX3973D_MAP__H__ ++ ++/* ++ * Note: originally, this file contained 12000+ lines of data ++ * Probably a few lines for every firwmare assembler instruction. However, ++ * only a few defines were actually used. So, removed all uneeded lines. ++ * If ever needed, the other lines can be easily obtained via git history. ++ */ ++ ++#define HI_COMM_EXEC__A 0x400000 ++#define HI_COMM_MB__A 0x400002 ++#define HI_CT_REG_COMM_STATE__A 0x410001 ++#define HI_RA_RAM_SRV_RES__A 0x420031 ++#define HI_RA_RAM_SRV_CMD__A 0x420032 ++#define HI_RA_RAM_SRV_CMD_RESET 0x2 ++#define HI_RA_RAM_SRV_CMD_CONFIG 0x3 ++#define HI_RA_RAM_SRV_CMD_EXECUTE 0x6 ++#define HI_RA_RAM_SRV_RST_KEY__A 0x420033 ++#define HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 ++#define HI_RA_RAM_SRV_CFG_KEY__A 0x420033 ++#define HI_RA_RAM_SRV_CFG_DIV__A 0x420034 ++#define HI_RA_RAM_SRV_CFG_BDL__A 0x420035 ++#define HI_RA_RAM_SRV_CFG_WUP__A 0x420036 ++#define HI_RA_RAM_SRV_CFG_ACT__A 0x420037 ++#define HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 ++#define HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 ++#define HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 ++#define HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 ++#define HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 ++#define HI_RA_RAM_USR_BEGIN__A 0x420040 ++#define HI_IF_RAM_TRP_BPT0__AX 0x430000 ++#define HI_IF_RAM_USR_BEGIN__A 0x430200 ++#define SC_COMM_EXEC__A 0x800000 ++#define SC_COMM_EXEC_CTL_STOP 0x0 ++#define SC_COMM_STATE__A 0x800001 ++#define SC_RA_RAM_PARAM0__A 0x820040 ++#define SC_RA_RAM_PARAM1__A 0x820041 ++#define SC_RA_RAM_CMD_ADDR__A 0x820042 ++#define SC_RA_RAM_CMD__A 0x820043 ++#define SC_RA_RAM_CMD_PROC_START 0x1 ++#define SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 ++#define SC_RA_RAM_CMD_GET_OP_PARAM 0x5 ++#define SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 ++#define SC_RA_RAM_LOCKTRACK_MIN 0x1 ++#define SC_RA_RAM_OP_PARAM_MODE_2K 0x0 ++#define SC_RA_RAM_OP_PARAM_MODE_8K 0x1 ++#define SC_RA_RAM_OP_PARAM_GUARD_32 0x0 ++#define SC_RA_RAM_OP_PARAM_GUARD_16 0x4 ++#define SC_RA_RAM_OP_PARAM_GUARD_8 0x8 ++#define SC_RA_RAM_OP_PARAM_GUARD_4 0xC ++#define SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 ++#define SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 ++#define SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 ++#define SC_RA_RAM_OP_PARAM_HIER_NO 0x0 ++#define SC_RA_RAM_OP_PARAM_HIER_A1 0x40 ++#define SC_RA_RAM_OP_PARAM_HIER_A2 0x80 ++#define SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 ++#define SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 ++#define SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 ++#define SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 ++#define SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 ++#define SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 ++#define SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 ++#define SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 ++#define SC_RA_RAM_OP_AUTO_MODE__M 0x1 ++#define SC_RA_RAM_OP_AUTO_GUARD__M 0x2 ++#define SC_RA_RAM_OP_AUTO_CONST__M 0x4 ++#define SC_RA_RAM_OP_AUTO_HIER__M 0x8 ++#define SC_RA_RAM_OP_AUTO_RATE__M 0x10 ++#define SC_RA_RAM_LOCK__A 0x82004B ++#define SC_RA_RAM_LOCK_DEMOD__M 0x1 ++#define SC_RA_RAM_LOCK_FEC__M 0x2 ++#define SC_RA_RAM_LOCK_MPEG__M 0x4 ++#define SC_RA_RAM_BE_OPT_ENA__A 0x82004C ++#define SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 ++#define SC_RA_RAM_BE_OPT_DELAY__A 0x82004D ++#define SC_RA_RAM_CONFIG__A 0x820050 ++#define SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 ++#define SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 ++#define SC_RA_RAM_CONFIG_SLAVE__M 0x20 ++#define SC_RA_RAM_IF_SAVE__AX 0x82008E ++#define SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 ++#define SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 ++#define SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 ++#define SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 ++#define SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 ++#define SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 ++#define SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 ++#define SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 ++#define SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 ++#define SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 ++#define SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 ++#define SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 ++#define SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 ++#define SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 ++#define SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 ++#define SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 ++#define SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 ++#define SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 ++#define SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA ++#define SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB ++#define SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB ++#define SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 ++#define SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC ++#define SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 ++#define SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD ++#define SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 ++#define SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 ++#define SC_RA_RAM_BAND__A 0x8200EC ++#define SC_RA_RAM_LC_ABS_2K__A 0x8200F4 ++#define SC_RA_RAM_LC_ABS_2K__PRE 0x1F ++#define SC_RA_RAM_LC_ABS_8K__A 0x8200F5 ++#define SC_RA_RAM_LC_ABS_8K__PRE 0x1F ++#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x1D6 ++#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 ++#define SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1BB ++#define SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x5 ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x1EF ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x15E ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x5 ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x11A ++#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x6 ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x1FB ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x12F ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x5 ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x197 ++#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x5 ++#define SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE ++#define SC_RA_RAM_PROC_LOCKTRACK 0x0 ++#define FE_COMM_EXEC__A 0xC00000 ++#define FE_AD_REG_COMM_EXEC__A 0xC10000 ++#define FE_AD_REG_FDB_IN__A 0xC10012 ++#define FE_AD_REG_PD__A 0xC10013 ++#define FE_AD_REG_INVEXT__A 0xC10014 ++#define FE_AD_REG_CLKNEG__A 0xC10015 ++#define FE_AG_REG_COMM_EXEC__A 0xC20000 ++#define FE_AG_REG_AG_MODE_LOP__A 0xC20010 ++#define FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 ++#define FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 ++#define FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 ++#define FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 ++#define FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 ++#define FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 ++#define FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 ++#define FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 ++#define FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 ++#define FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 ++#define FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 ++#define FE_AG_REG_AG_MODE_HIP__A 0xC20011 ++#define FE_AG_REG_AG_PGA_MODE__A 0xC20012 ++#define FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 ++#define FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 ++#define FE_AG_REG_AG_AGC_SIO__A 0xC20013 ++#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 ++#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 ++#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 ++#define FE_AG_REG_AG_PWD__A 0xC20015 ++#define FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 ++#define FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 ++#define FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 ++#define FE_AG_REG_DCE_AUR_CNT__A 0xC20016 ++#define FE_AG_REG_DCE_RUR_CNT__A 0xC20017 ++#define FE_AG_REG_ACE_AUR_CNT__A 0xC2001A ++#define FE_AG_REG_ACE_RUR_CNT__A 0xC2001B ++#define FE_AG_REG_CDR_RUR_CNT__A 0xC20020 ++#define FE_AG_REG_EGC_RUR_CNT__A 0xC20024 ++#define FE_AG_REG_EGC_SET_LVL__A 0xC20025 ++#define FE_AG_REG_EGC_SET_LVL__M 0x1FF ++#define FE_AG_REG_EGC_FLA_RGN__A 0xC20026 ++#define FE_AG_REG_EGC_SLO_RGN__A 0xC20027 ++#define FE_AG_REG_EGC_JMP_PSN__A 0xC20028 ++#define FE_AG_REG_EGC_FLA_INC__A 0xC20029 ++#define FE_AG_REG_EGC_FLA_DEC__A 0xC2002A ++#define FE_AG_REG_EGC_SLO_INC__A 0xC2002B ++#define FE_AG_REG_EGC_SLO_DEC__A 0xC2002C ++#define FE_AG_REG_EGC_FAS_INC__A 0xC2002D ++#define FE_AG_REG_EGC_FAS_DEC__A 0xC2002E ++#define FE_AG_REG_PM1_AGC_WRI__A 0xC20030 ++#define FE_AG_REG_PM1_AGC_WRI__M 0x7FF ++#define FE_AG_REG_GC1_AGC_RIC__A 0xC20031 ++#define FE_AG_REG_GC1_AGC_OFF__A 0xC20032 ++#define FE_AG_REG_GC1_AGC_MAX__A 0xC20033 ++#define FE_AG_REG_GC1_AGC_MIN__A 0xC20034 ++#define FE_AG_REG_GC1_AGC_DAT__A 0xC20035 ++#define FE_AG_REG_GC1_AGC_DAT__M 0x3FF ++#define FE_AG_REG_PM2_AGC_WRI__A 0xC20036 ++#define FE_AG_REG_IND_WIN__A 0xC2003C ++#define FE_AG_REG_IND_THD_LOL__A 0xC2003D ++#define FE_AG_REG_IND_THD_HIL__A 0xC2003E ++#define FE_AG_REG_IND_DEL__A 0xC2003F ++#define FE_AG_REG_IND_PD1_WRI__A 0xC20040 ++#define FE_AG_REG_PDA_AUR_CNT__A 0xC20041 ++#define FE_AG_REG_PDA_RUR_CNT__A 0xC20042 ++#define FE_AG_REG_PDA_AVE_DAT__A 0xC20043 ++#define FE_AG_REG_PDC_RUR_CNT__A 0xC20044 ++#define FE_AG_REG_PDC_SET_LVL__A 0xC20045 ++#define FE_AG_REG_PDC_FLA_RGN__A 0xC20046 ++#define FE_AG_REG_PDC_JMP_PSN__A 0xC20047 ++#define FE_AG_REG_PDC_FLA_STP__A 0xC20048 ++#define FE_AG_REG_PDC_SLO_STP__A 0xC20049 ++#define FE_AG_REG_PDC_PD2_WRI__A 0xC2004A ++#define FE_AG_REG_PDC_MAP_DAT__A 0xC2004B ++#define FE_AG_REG_PDC_MAX__A 0xC2004C ++#define FE_AG_REG_TGA_AUR_CNT__A 0xC2004D ++#define FE_AG_REG_TGA_RUR_CNT__A 0xC2004E ++#define FE_AG_REG_TGA_AVE_DAT__A 0xC2004F ++#define FE_AG_REG_TGC_RUR_CNT__A 0xC20050 ++#define FE_AG_REG_TGC_SET_LVL__A 0xC20051 ++#define FE_AG_REG_TGC_SET_LVL__M 0x3F ++#define FE_AG_REG_TGC_FLA_RGN__A 0xC20052 ++#define FE_AG_REG_TGC_JMP_PSN__A 0xC20053 ++#define FE_AG_REG_TGC_FLA_STP__A 0xC20054 ++#define FE_AG_REG_TGC_SLO_STP__A 0xC20055 ++#define FE_AG_REG_TGC_MAP_DAT__A 0xC20056 ++#define FE_AG_REG_FGA_AUR_CNT__A 0xC20057 ++#define FE_AG_REG_FGA_RUR_CNT__A 0xC20058 ++#define FE_AG_REG_FGM_WRI__A 0xC20061 ++#define FE_AG_REG_BGC_FGC_WRI__A 0xC20068 ++#define FE_AG_REG_BGC_CGC_WRI__A 0xC20069 ++#define FE_FS_REG_COMM_EXEC__A 0xC30000 ++#define FE_FS_REG_ADD_INC_LOP__A 0xC30010 ++#define FE_FD_REG_COMM_EXEC__A 0xC40000 ++#define FE_FD_REG_SCL__A 0xC40010 ++#define FE_FD_REG_MAX_LEV__A 0xC40011 ++#define FE_FD_REG_NR__A 0xC40012 ++#define FE_FD_REG_MEAS_VAL__A 0xC40014 ++#define FE_IF_REG_COMM_EXEC__A 0xC50000 ++#define FE_IF_REG_INCR0__A 0xC50010 ++#define FE_IF_REG_INCR0__W 16 ++#define FE_IF_REG_INCR0__M 0xFFFF ++#define FE_IF_REG_INCR1__A 0xC50011 ++#define FE_IF_REG_INCR1__M 0xFF ++#define FE_CF_REG_COMM_EXEC__A 0xC60000 ++#define FE_CF_REG_SCL__A 0xC60010 ++#define FE_CF_REG_MAX_LEV__A 0xC60011 ++#define FE_CF_REG_NR__A 0xC60012 ++#define FE_CF_REG_IMP_VAL__A 0xC60013 ++#define FE_CF_REG_MEAS_VAL__A 0xC60014 ++#define FE_CU_REG_COMM_EXEC__A 0xC70000 ++#define FE_CU_REG_FRM_CNT_RST__A 0xC70011 ++#define FE_CU_REG_FRM_CNT_STR__A 0xC70012 ++#define FT_COMM_EXEC__A 0x1000000 ++#define FT_REG_COMM_EXEC__A 0x1010000 ++#define CP_COMM_EXEC__A 0x1400000 ++#define CP_REG_COMM_EXEC__A 0x1410000 ++#define CP_REG_INTERVAL__A 0x1410011 ++#define CP_REG_BR_SPL_OFFSET__A 0x1410023 ++#define CP_REG_BR_STR_DEL__A 0x1410024 ++#define CP_REG_RT_ANG_INC0__A 0x1410030 ++#define CP_REG_RT_ANG_INC1__A 0x1410031 ++#define CP_REG_RT_DETECT_ENA__A 0x1410032 ++#define CP_REG_RT_DETECT_TRH__A 0x1410033 ++#define CP_REG_RT_EXP_MARG__A 0x141003E ++#define CP_REG_AC_NEXP_OFFS__A 0x1410040 ++#define CP_REG_AC_AVER_POW__A 0x1410041 ++#define CP_REG_AC_MAX_POW__A 0x1410042 ++#define CP_REG_AC_WEIGHT_MAN__A 0x1410043 ++#define CP_REG_AC_WEIGHT_EXP__A 0x1410044 ++#define CP_REG_AC_AMP_MODE__A 0x1410047 ++#define CP_REG_AC_AMP_FIX__A 0x1410048 ++#define CP_REG_AC_ANG_MODE__A 0x141004A ++#define CE_COMM_EXEC__A 0x1800000 ++#define CE_REG_COMM_EXEC__A 0x1810000 ++#define CE_REG_TAPSET__A 0x1810011 ++#define CE_REG_AVG_POW__A 0x1810012 ++#define CE_REG_MAX_POW__A 0x1810013 ++#define CE_REG_ATT__A 0x1810014 ++#define CE_REG_NRED__A 0x1810015 ++#define CE_REG_NE_ERR_SELECT__A 0x1810043 ++#define CE_REG_NE_TD_CAL__A 0x1810044 ++#define CE_REG_NE_MIXAVG__A 0x1810046 ++#define CE_REG_NE_NUPD_OFS__A 0x1810047 ++#define CE_REG_PE_NEXP_OFFS__A 0x1810050 ++#define CE_REG_PE_TIMESHIFT__A 0x1810051 ++#define CE_REG_TP_A0_TAP_NEW__A 0x1810064 ++#define CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 ++#define CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 ++#define CE_REG_TP_A1_TAP_NEW__A 0x1810068 ++#define CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 ++#define CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A ++#define CE_REG_TI_NEXP_OFFS__A 0x1810070 ++#define CE_REG_FI_SHT_INCR__A 0x1810090 ++#define CE_REG_FI_EXP_NORM__A 0x1810091 ++#define CE_REG_IR_INPUTSEL__A 0x18100A0 ++#define CE_REG_IR_STARTPOS__A 0x18100A1 ++#define CE_REG_IR_NEXP_THRES__A 0x18100A2 ++#define CE_REG_FR_TREAL00__A 0x1820010 ++#define CE_REG_FR_TIMAG00__A 0x1820011 ++#define CE_REG_FR_TREAL01__A 0x1820012 ++#define CE_REG_FR_TIMAG01__A 0x1820013 ++#define CE_REG_FR_TREAL02__A 0x1820014 ++#define CE_REG_FR_TIMAG02__A 0x1820015 ++#define CE_REG_FR_TREAL03__A 0x1820016 ++#define CE_REG_FR_TIMAG03__A 0x1820017 ++#define CE_REG_FR_TREAL04__A 0x1820018 ++#define CE_REG_FR_TIMAG04__A 0x1820019 ++#define CE_REG_FR_TREAL05__A 0x182001A ++#define CE_REG_FR_TIMAG05__A 0x182001B ++#define CE_REG_FR_TREAL06__A 0x182001C ++#define CE_REG_FR_TIMAG06__A 0x182001D ++#define CE_REG_FR_TREAL07__A 0x182001E ++#define CE_REG_FR_TIMAG07__A 0x182001F ++#define CE_REG_FR_TREAL08__A 0x1820020 ++#define CE_REG_FR_TIMAG08__A 0x1820021 ++#define CE_REG_FR_TREAL09__A 0x1820022 ++#define CE_REG_FR_TIMAG09__A 0x1820023 ++#define CE_REG_FR_TREAL10__A 0x1820024 ++#define CE_REG_FR_TIMAG10__A 0x1820025 ++#define CE_REG_FR_TREAL11__A 0x1820026 ++#define CE_REG_FR_TIMAG11__A 0x1820027 ++#define CE_REG_FR_MID_TAP__A 0x1820028 ++#define CE_REG_FR_SQS_G00__A 0x1820029 ++#define CE_REG_FR_SQS_G01__A 0x182002A ++#define CE_REG_FR_SQS_G02__A 0x182002B ++#define CE_REG_FR_SQS_G03__A 0x182002C ++#define CE_REG_FR_SQS_G04__A 0x182002D ++#define CE_REG_FR_SQS_G05__A 0x182002E ++#define CE_REG_FR_SQS_G06__A 0x182002F ++#define CE_REG_FR_SQS_G07__A 0x1820030 ++#define CE_REG_FR_SQS_G08__A 0x1820031 ++#define CE_REG_FR_SQS_G09__A 0x1820032 ++#define CE_REG_FR_SQS_G10__A 0x1820033 ++#define CE_REG_FR_SQS_G11__A 0x1820034 ++#define CE_REG_FR_SQS_G12__A 0x1820035 ++#define CE_REG_FR_RIO_G00__A 0x1820036 ++#define CE_REG_FR_RIO_G01__A 0x1820037 ++#define CE_REG_FR_RIO_G02__A 0x1820038 ++#define CE_REG_FR_RIO_G03__A 0x1820039 ++#define CE_REG_FR_RIO_G04__A 0x182003A ++#define CE_REG_FR_RIO_G05__A 0x182003B ++#define CE_REG_FR_RIO_G06__A 0x182003C ++#define CE_REG_FR_RIO_G07__A 0x182003D ++#define CE_REG_FR_RIO_G08__A 0x182003E ++#define CE_REG_FR_RIO_G09__A 0x182003F ++#define CE_REG_FR_RIO_G10__A 0x1820040 ++#define CE_REG_FR_MODE__A 0x1820041 ++#define CE_REG_FR_SQS_TRH__A 0x1820042 ++#define CE_REG_FR_RIO_GAIN__A 0x1820043 ++#define CE_REG_FR_BYPASS__A 0x1820044 ++#define CE_REG_FR_PM_SET__A 0x1820045 ++#define CE_REG_FR_ERR_SH__A 0x1820046 ++#define CE_REG_FR_MAN_SH__A 0x1820047 ++#define CE_REG_FR_TAP_SH__A 0x1820048 ++#define EQ_COMM_EXEC__A 0x1C00000 ++#define EQ_REG_COMM_EXEC__A 0x1C10000 ++#define EQ_REG_COMM_MB__A 0x1C10002 ++#define EQ_REG_IS_GAIN_MAN__A 0x1C10015 ++#define EQ_REG_IS_GAIN_EXP__A 0x1C10016 ++#define EQ_REG_IS_CLIP_EXP__A 0x1C10017 ++#define EQ_REG_SN_CEGAIN__A 0x1C1002A ++#define EQ_REG_SN_OFFSET__A 0x1C1002B ++#define EQ_REG_RC_SEL_CAR__A 0x1C10032 ++#define EQ_REG_RC_SEL_CAR_INIT 0x0 ++#define EQ_REG_RC_SEL_CAR_DIV_ON 0x1 ++#define EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 ++#define EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 ++#define EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 ++#define EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 ++#define EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 ++#define EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 ++#define EQ_REG_OT_CONST__A 0x1C10046 ++#define EQ_REG_OT_ALPHA__A 0x1C10047 ++#define EQ_REG_OT_QNT_THRES0__A 0x1C10048 ++#define EQ_REG_OT_QNT_THRES1__A 0x1C10049 ++#define EQ_REG_OT_CSI_STEP__A 0x1C1004A ++#define EQ_REG_OT_CSI_OFFSET__A 0x1C1004B ++#define EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 ++#define EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 ++#define EC_SB_REG_COMM_EXEC__A 0x2010000 ++#define EC_SB_REG_TR_MODE__A 0x2010010 ++#define EC_SB_REG_TR_MODE_8K 0x0 ++#define EC_SB_REG_TR_MODE_2K 0x1 ++#define EC_SB_REG_CONST__A 0x2010011 ++#define EC_SB_REG_CONST_QPSK 0x0 ++#define EC_SB_REG_CONST_16QAM 0x1 ++#define EC_SB_REG_CONST_64QAM 0x2 ++#define EC_SB_REG_ALPHA__A 0x2010012 ++#define EC_SB_REG_PRIOR__A 0x2010013 ++#define EC_SB_REG_PRIOR_HI 0x0 ++#define EC_SB_REG_PRIOR_LO 0x1 ++#define EC_SB_REG_CSI_HI__A 0x2010014 ++#define EC_SB_REG_CSI_LO__A 0x2010015 ++#define EC_SB_REG_SMB_TGL__A 0x2010016 ++#define EC_SB_REG_SNR_HI__A 0x2010017 ++#define EC_SB_REG_SNR_MID__A 0x2010018 ++#define EC_SB_REG_SNR_LO__A 0x2010019 ++#define EC_SB_REG_SCALE_MSB__A 0x201001A ++#define EC_SB_REG_SCALE_BIT2__A 0x201001B ++#define EC_SB_REG_SCALE_LSB__A 0x201001C ++#define EC_SB_REG_CSI_OFS__A 0x201001D ++#define EC_VD_REG_COMM_EXEC__A 0x2090000 ++#define EC_VD_REG_FORCE__A 0x2090010 ++#define EC_VD_REG_SET_CODERATE__A 0x2090011 ++#define EC_VD_REG_SET_CODERATE_C1_2 0x0 ++#define EC_VD_REG_SET_CODERATE_C2_3 0x1 ++#define EC_VD_REG_SET_CODERATE_C3_4 0x2 ++#define EC_VD_REG_SET_CODERATE_C5_6 0x3 ++#define EC_VD_REG_SET_CODERATE_C7_8 0x4 ++#define EC_VD_REG_REQ_SMB_CNT__A 0x2090012 ++#define EC_VD_REG_RLK_ENA__A 0x2090014 ++#define EC_OD_REG_COMM_EXEC__A 0x2110000 ++#define EC_OD_REG_SYNC__A 0x2110010 ++#define EC_OD_DEINT_RAM__A 0x2120000 ++#define EC_RS_REG_COMM_EXEC__A 0x2130000 ++#define EC_RS_REG_REQ_PCK_CNT__A 0x2130010 ++#define EC_RS_REG_VAL__A 0x2130011 ++#define EC_RS_REG_VAL_PCK 0x1 ++#define EC_RS_EC_RAM__A 0x2140000 ++#define EC_OC_REG_COMM_EXEC__A 0x2150000 ++#define EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 ++#define EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 ++#define EC_OC_REG_COMM_INT_STA__A 0x2150007 ++#define EC_OC_REG_OC_MODE_LOP__A 0x2150010 ++#define EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 ++#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 ++#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 ++#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 ++#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 ++#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 ++#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 ++#define EC_OC_REG_OC_MODE_HIP__A 0x2150011 ++#define EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 ++#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 ++#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 ++#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 ++#define EC_OC_REG_OC_MPG_SIO__A 0x2150012 ++#define EC_OC_REG_OC_MPG_SIO__M 0xFFF ++#define EC_OC_REG_OC_MON_SIO__A 0x2150013 ++#define EC_OC_REG_DTO_INC_LOP__A 0x2150014 ++#define EC_OC_REG_DTO_INC_HIP__A 0x2150015 ++#define EC_OC_REG_SNC_ISC_LVL__A 0x2150016 ++#define EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 ++#define EC_OC_REG_TMD_TOP_MODE__A 0x215001D ++#define EC_OC_REG_TMD_TOP_CNT__A 0x215001E ++#define EC_OC_REG_TMD_HIL_MAR__A 0x215001F ++#define EC_OC_REG_TMD_LOL_MAR__A 0x2150020 ++#define EC_OC_REG_TMD_CUR_CNT__A 0x2150021 ++#define EC_OC_REG_AVR_ASH_CNT__A 0x2150023 ++#define EC_OC_REG_AVR_BSH_CNT__A 0x2150024 ++#define EC_OC_REG_RCN_MODE__A 0x2150027 ++#define EC_OC_REG_RCN_CRA_LOP__A 0x2150028 ++#define EC_OC_REG_RCN_CRA_HIP__A 0x2150029 ++#define EC_OC_REG_RCN_CST_LOP__A 0x215002A ++#define EC_OC_REG_RCN_CST_HIP__A 0x215002B ++#define EC_OC_REG_RCN_SET_LVL__A 0x215002C ++#define EC_OC_REG_RCN_GAI_LVL__A 0x215002D ++#define EC_OC_REG_RCN_CLP_LOP__A 0x2150032 ++#define EC_OC_REG_RCN_CLP_HIP__A 0x2150033 ++#define EC_OC_REG_RCN_MAP_LOP__A 0x2150034 ++#define EC_OC_REG_RCN_MAP_HIP__A 0x2150035 ++#define EC_OC_REG_OCR_MPG_UOS__A 0x2150036 ++#define EC_OC_REG_OCR_MPG_UOS__M 0xFFF ++#define EC_OC_REG_OCR_MPG_UOS_INIT 0x0 ++#define EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 ++#define EC_OC_REG_OCR_MON_UOS__A 0x2150039 ++#define EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE 0x1 ++#define EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE 0x2 ++#define EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE 0x4 ++#define EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE 0x8 ++#define EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE 0x10 ++#define EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE 0x20 ++#define EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE 0x40 ++#define EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE 0x80 ++#define EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE 0x100 ++#define EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE 0x200 ++#define EC_OC_REG_OCR_MON_UOS_VAL_ENABLE 0x400 ++#define EC_OC_REG_OCR_MON_UOS_CLK_ENABLE 0x800 ++#define EC_OC_REG_OCR_MON_WRI__A 0x215003A ++#define EC_OC_REG_OCR_MON_WRI_INIT 0x0 ++#define EC_OC_REG_IPR_INV_MPG__A 0x2150045 ++#define CC_REG_OSC_MODE__A 0x2410010 ++#define CC_REG_OSC_MODE_M20 0x1 ++#define CC_REG_PLL_MODE__A 0x2410011 ++#define CC_REG_PLL_MODE_BYPASS_PLL 0x1 ++#define CC_REG_PLL_MODE_PUMP_CUR_12 0x14 ++#define CC_REG_REF_DIVIDE__A 0x2410012 ++#define CC_REG_PWD_MODE__A 0x2410015 ++#define CC_REG_PWD_MODE_DOWN_PLL 0x2 ++#define CC_REG_UPDATE__A 0x2410017 ++#define CC_REG_UPDATE_KEY 0x3973 ++#define CC_REG_JTAGID_L__A 0x2410019 ++#define LC_COMM_EXEC__A 0x2800000 ++#define LC_RA_RAM_IFINCR_NOM_L__A 0x282000C ++#define LC_RA_RAM_FILTER_SYM_SET__A 0x282001A ++#define LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 ++#define LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 ++#define LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 ++#define LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 ++#define LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 ++#define LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 ++#define LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 ++#define LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 ++#define LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 ++#define B_HI_COMM_EXEC__A 0x400000 ++#define B_HI_COMM_MB__A 0x400002 ++#define B_HI_CT_REG_COMM_STATE__A 0x410001 ++#define B_HI_RA_RAM_SRV_RES__A 0x420031 ++#define B_HI_RA_RAM_SRV_CMD__A 0x420032 ++#define B_HI_RA_RAM_SRV_CMD_RESET 0x2 ++#define B_HI_RA_RAM_SRV_CMD_CONFIG 0x3 ++#define B_HI_RA_RAM_SRV_CMD_EXECUTE 0x6 ++#define B_HI_RA_RAM_SRV_RST_KEY__A 0x420033 ++#define B_HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 ++#define B_HI_RA_RAM_SRV_CFG_KEY__A 0x420033 ++#define B_HI_RA_RAM_SRV_CFG_DIV__A 0x420034 ++#define B_HI_RA_RAM_SRV_CFG_BDL__A 0x420035 ++#define B_HI_RA_RAM_SRV_CFG_WUP__A 0x420036 ++#define B_HI_RA_RAM_SRV_CFG_ACT__A 0x420037 ++#define B_HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 ++#define B_HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 ++#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 ++#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 ++#define B_HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 ++#define B_HI_RA_RAM_USR_BEGIN__A 0x420040 ++#define B_HI_IF_RAM_TRP_BPT0__AX 0x430000 ++#define B_HI_IF_RAM_USR_BEGIN__A 0x430200 ++#define B_SC_COMM_EXEC__A 0x800000 ++#define B_SC_COMM_EXEC_CTL_STOP 0x0 ++#define B_SC_COMM_STATE__A 0x800001 ++#define B_SC_RA_RAM_PARAM0__A 0x820040 ++#define B_SC_RA_RAM_PARAM1__A 0x820041 ++#define B_SC_RA_RAM_CMD_ADDR__A 0x820042 ++#define B_SC_RA_RAM_CMD__A 0x820043 ++#define B_SC_RA_RAM_CMD_PROC_START 0x1 ++#define B_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 ++#define B_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 ++#define B_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 ++#define B_SC_RA_RAM_LOCKTRACK_MIN 0x1 ++#define B_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 ++#define B_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 ++#define B_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 ++#define B_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 ++#define B_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 ++#define B_SC_RA_RAM_OP_PARAM_GUARD_4 0xC ++#define B_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 ++#define B_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 ++#define B_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 ++#define B_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 ++#define B_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 ++#define B_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 ++#define B_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 ++#define B_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 ++#define B_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 ++#define B_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 ++#define B_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 ++#define B_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 ++#define B_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 ++#define B_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 ++#define B_SC_RA_RAM_OP_AUTO_MODE__M 0x1 ++#define B_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 ++#define B_SC_RA_RAM_OP_AUTO_CONST__M 0x4 ++#define B_SC_RA_RAM_OP_AUTO_HIER__M 0x8 ++#define B_SC_RA_RAM_OP_AUTO_RATE__M 0x10 ++#define B_SC_RA_RAM_LOCK__A 0x82004B ++#define B_SC_RA_RAM_LOCK_DEMOD__M 0x1 ++#define B_SC_RA_RAM_LOCK_FEC__M 0x2 ++#define B_SC_RA_RAM_LOCK_MPEG__M 0x4 ++#define B_SC_RA_RAM_BE_OPT_ENA__A 0x82004C ++#define B_SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 ++#define B_SC_RA_RAM_BE_OPT_DELAY__A 0x82004D ++#define B_SC_RA_RAM_CONFIG__A 0x820050 ++#define B_SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 ++#define B_SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 ++#define B_SC_RA_RAM_CONFIG_SLAVE__M 0x20 ++#define B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M 0x200 ++#define B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M 0x400 ++#define B_SC_RA_RAM_CO_TD_CAL_2K__A 0x82005D ++#define B_SC_RA_RAM_CO_TD_CAL_8K__A 0x82005E ++#define B_SC_RA_RAM_IF_SAVE__AX 0x82008E ++#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A 0x820098 ++#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A 0x820099 ++#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A 0x82009A ++#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A 0x82009B ++#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A 0x82009C ++#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A 0x82009D ++#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A 0x82009E ++#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A 0x82009F ++#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 ++#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 ++#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 ++#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 ++#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 ++#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 ++#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 ++#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 ++#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 ++#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 ++#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 ++#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 ++#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 ++#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 ++#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 ++#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 ++#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 ++#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 ++#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA ++#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB ++#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB ++#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 ++#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC ++#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 ++#define B_SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD ++#define B_SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 ++#define B_SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 ++#define B_SC_RA_RAM_BAND__A 0x8200EC ++#define B_SC_RA_RAM_LC_ABS_2K__A 0x8200F4 ++#define B_SC_RA_RAM_LC_ABS_2K__PRE 0x1F ++#define B_SC_RA_RAM_LC_ABS_8K__A 0x8200F5 ++#define B_SC_RA_RAM_LC_ABS_8K__PRE 0x1F ++#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x100 ++#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 ++#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1E2 ++#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x4 ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x10D ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x17D ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x4 ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x133 ++#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x5 ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x114 ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x14A ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x4 ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x1BB ++#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x4 ++#define B_SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE ++#define B_SC_RA_RAM_PROC_LOCKTRACK 0x0 ++#define B_FE_COMM_EXEC__A 0xC00000 ++#define B_FE_AD_REG_COMM_EXEC__A 0xC10000 ++#define B_FE_AD_REG_FDB_IN__A 0xC10012 ++#define B_FE_AD_REG_PD__A 0xC10013 ++#define B_FE_AD_REG_INVEXT__A 0xC10014 ++#define B_FE_AD_REG_CLKNEG__A 0xC10015 ++#define B_FE_AG_REG_COMM_EXEC__A 0xC20000 ++#define B_FE_AG_REG_AG_MODE_LOP__A 0xC20010 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 ++#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 ++#define B_FE_AG_REG_AG_MODE_HIP__A 0xC20011 ++#define B_FE_AG_REG_AG_MODE_HIP_MODE_J__M 0x8 ++#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC 0x0 ++#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC 0x8 ++#define B_FE_AG_REG_AG_PGA_MODE__A 0xC20012 ++#define B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 ++#define B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 ++#define B_FE_AG_REG_AG_AGC_SIO__A 0xC20013 ++#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 ++#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 ++#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 ++#define B_FE_AG_REG_AG_PWD__A 0xC20015 ++#define B_FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 ++#define B_FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 ++#define B_FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 ++#define B_FE_AG_REG_DCE_AUR_CNT__A 0xC20016 ++#define B_FE_AG_REG_DCE_RUR_CNT__A 0xC20017 ++#define B_FE_AG_REG_ACE_AUR_CNT__A 0xC2001A ++#define B_FE_AG_REG_ACE_RUR_CNT__A 0xC2001B ++#define B_FE_AG_REG_CDR_RUR_CNT__A 0xC20020 ++#define B_FE_AG_REG_EGC_RUR_CNT__A 0xC20024 ++#define B_FE_AG_REG_EGC_SET_LVL__A 0xC20025 ++#define B_FE_AG_REG_EGC_SET_LVL__M 0x1FF ++#define B_FE_AG_REG_EGC_FLA_RGN__A 0xC20026 ++#define B_FE_AG_REG_EGC_SLO_RGN__A 0xC20027 ++#define B_FE_AG_REG_EGC_JMP_PSN__A 0xC20028 ++#define B_FE_AG_REG_EGC_FLA_INC__A 0xC20029 ++#define B_FE_AG_REG_EGC_FLA_DEC__A 0xC2002A ++#define B_FE_AG_REG_EGC_SLO_INC__A 0xC2002B ++#define B_FE_AG_REG_EGC_SLO_DEC__A 0xC2002C ++#define B_FE_AG_REG_EGC_FAS_INC__A 0xC2002D ++#define B_FE_AG_REG_EGC_FAS_DEC__A 0xC2002E ++#define B_FE_AG_REG_PM1_AGC_WRI__A 0xC20030 ++#define B_FE_AG_REG_PM1_AGC_WRI__M 0x7FF ++#define B_FE_AG_REG_GC1_AGC_RIC__A 0xC20031 ++#define B_FE_AG_REG_GC1_AGC_OFF__A 0xC20032 ++#define B_FE_AG_REG_GC1_AGC_MAX__A 0xC20033 ++#define B_FE_AG_REG_GC1_AGC_MIN__A 0xC20034 ++#define B_FE_AG_REG_GC1_AGC_DAT__A 0xC20035 ++#define B_FE_AG_REG_GC1_AGC_DAT__M 0x3FF ++#define B_FE_AG_REG_PM2_AGC_WRI__A 0xC20036 ++#define B_FE_AG_REG_IND_WIN__A 0xC2003C ++#define B_FE_AG_REG_IND_THD_LOL__A 0xC2003D ++#define B_FE_AG_REG_IND_THD_HIL__A 0xC2003E ++#define B_FE_AG_REG_IND_DEL__A 0xC2003F ++#define B_FE_AG_REG_IND_PD1_WRI__A 0xC20040 ++#define B_FE_AG_REG_PDA_AUR_CNT__A 0xC20041 ++#define B_FE_AG_REG_PDA_RUR_CNT__A 0xC20042 ++#define B_FE_AG_REG_PDA_AVE_DAT__A 0xC20043 ++#define B_FE_AG_REG_PDC_RUR_CNT__A 0xC20044 ++#define B_FE_AG_REG_PDC_SET_LVL__A 0xC20045 ++#define B_FE_AG_REG_PDC_FLA_RGN__A 0xC20046 ++#define B_FE_AG_REG_PDC_JMP_PSN__A 0xC20047 ++#define B_FE_AG_REG_PDC_FLA_STP__A 0xC20048 ++#define B_FE_AG_REG_PDC_SLO_STP__A 0xC20049 ++#define B_FE_AG_REG_PDC_PD2_WRI__A 0xC2004A ++#define B_FE_AG_REG_PDC_MAP_DAT__A 0xC2004B ++#define B_FE_AG_REG_PDC_MAX__A 0xC2004C ++#define B_FE_AG_REG_TGA_AUR_CNT__A 0xC2004D ++#define B_FE_AG_REG_TGA_RUR_CNT__A 0xC2004E ++#define B_FE_AG_REG_TGA_AVE_DAT__A 0xC2004F ++#define B_FE_AG_REG_TGC_RUR_CNT__A 0xC20050 ++#define B_FE_AG_REG_TGC_SET_LVL__A 0xC20051 ++#define B_FE_AG_REG_TGC_SET_LVL__M 0x3F ++#define B_FE_AG_REG_TGC_FLA_RGN__A 0xC20052 ++#define B_FE_AG_REG_TGC_JMP_PSN__A 0xC20053 ++#define B_FE_AG_REG_TGC_FLA_STP__A 0xC20054 ++#define B_FE_AG_REG_TGC_SLO_STP__A 0xC20055 ++#define B_FE_AG_REG_TGC_MAP_DAT__A 0xC20056 ++#define B_FE_AG_REG_FGM_WRI__A 0xC20061 ++#define B_FE_AG_REG_BGC_FGC_WRI__A 0xC20068 ++#define B_FE_AG_REG_BGC_CGC_WRI__A 0xC20069 ++#define B_FE_FS_REG_COMM_EXEC__A 0xC30000 ++#define B_FE_FS_REG_ADD_INC_LOP__A 0xC30010 ++#define B_FE_FD_REG_COMM_EXEC__A 0xC40000 ++#define B_FE_FD_REG_SCL__A 0xC40010 ++#define B_FE_FD_REG_MAX_LEV__A 0xC40011 ++#define B_FE_FD_REG_NR__A 0xC40012 ++#define B_FE_FD_REG_MEAS_VAL__A 0xC40014 ++#define B_FE_IF_REG_COMM_EXEC__A 0xC50000 ++#define B_FE_IF_REG_INCR0__A 0xC50010 ++#define B_FE_IF_REG_INCR0__W 16 ++#define B_FE_IF_REG_INCR0__M 0xFFFF ++#define B_FE_IF_REG_INCR1__A 0xC50011 ++#define B_FE_IF_REG_INCR1__M 0xFF ++#define B_FE_CF_REG_COMM_EXEC__A 0xC60000 ++#define B_FE_CF_REG_SCL__A 0xC60010 ++#define B_FE_CF_REG_MAX_LEV__A 0xC60011 ++#define B_FE_CF_REG_NR__A 0xC60012 ++#define B_FE_CF_REG_IMP_VAL__A 0xC60013 ++#define B_FE_CF_REG_MEAS_VAL__A 0xC60014 ++#define B_FE_CU_REG_COMM_EXEC__A 0xC70000 ++#define B_FE_CU_REG_FRM_CNT_RST__A 0xC70011 ++#define B_FE_CU_REG_FRM_CNT_STR__A 0xC70012 ++#define B_FE_CU_REG_CTR_NFC_ICR__A 0xC70020 ++#define B_FE_CU_REG_CTR_NFC_OCR__A 0xC70021 ++#define B_FE_CU_REG_DIV_NFC_CLP__A 0xC70027 ++#define B_FT_COMM_EXEC__A 0x1000000 ++#define B_FT_REG_COMM_EXEC__A 0x1010000 ++#define B_CP_COMM_EXEC__A 0x1400000 ++#define B_CP_REG_COMM_EXEC__A 0x1410000 ++#define B_CP_REG_INTERVAL__A 0x1410011 ++#define B_CP_REG_BR_SPL_OFFSET__A 0x1410023 ++#define B_CP_REG_BR_STR_DEL__A 0x1410024 ++#define B_CP_REG_RT_ANG_INC0__A 0x1410030 ++#define B_CP_REG_RT_ANG_INC1__A 0x1410031 ++#define B_CP_REG_RT_DETECT_TRH__A 0x1410033 ++#define B_CP_REG_AC_NEXP_OFFS__A 0x1410040 ++#define B_CP_REG_AC_AVER_POW__A 0x1410041 ++#define B_CP_REG_AC_MAX_POW__A 0x1410042 ++#define B_CP_REG_AC_WEIGHT_MAN__A 0x1410043 ++#define B_CP_REG_AC_WEIGHT_EXP__A 0x1410044 ++#define B_CP_REG_AC_AMP_MODE__A 0x1410047 ++#define B_CP_REG_AC_AMP_FIX__A 0x1410048 ++#define B_CP_REG_AC_ANG_MODE__A 0x141004A ++#define B_CE_COMM_EXEC__A 0x1800000 ++#define B_CE_REG_COMM_EXEC__A 0x1810000 ++#define B_CE_REG_TAPSET__A 0x1810011 ++#define B_CE_REG_AVG_POW__A 0x1810012 ++#define B_CE_REG_MAX_POW__A 0x1810013 ++#define B_CE_REG_ATT__A 0x1810014 ++#define B_CE_REG_NRED__A 0x1810015 ++#define B_CE_REG_NE_ERR_SELECT__A 0x1810043 ++#define B_CE_REG_NE_TD_CAL__A 0x1810044 ++#define B_CE_REG_NE_MIXAVG__A 0x1810046 ++#define B_CE_REG_NE_NUPD_OFS__A 0x1810047 ++#define B_CE_REG_PE_NEXP_OFFS__A 0x1810050 ++#define B_CE_REG_PE_TIMESHIFT__A 0x1810051 ++#define B_CE_REG_TP_A0_TAP_NEW__A 0x1810064 ++#define B_CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 ++#define B_CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 ++#define B_CE_REG_TP_A1_TAP_NEW__A 0x1810068 ++#define B_CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 ++#define B_CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A ++#define B_CE_REG_TI_PHN_ENABLE__A 0x1810073 ++#define B_CE_REG_FI_SHT_INCR__A 0x1810090 ++#define B_CE_REG_FI_EXP_NORM__A 0x1810091 ++#define B_CE_REG_IR_INPUTSEL__A 0x18100A0 ++#define B_CE_REG_IR_STARTPOS__A 0x18100A1 ++#define B_CE_REG_IR_NEXP_THRES__A 0x18100A2 ++#define B_CE_REG_FR_TREAL00__A 0x1820010 ++#define B_CE_REG_FR_TIMAG00__A 0x1820011 ++#define B_CE_REG_FR_TREAL01__A 0x1820012 ++#define B_CE_REG_FR_TIMAG01__A 0x1820013 ++#define B_CE_REG_FR_TREAL02__A 0x1820014 ++#define B_CE_REG_FR_TIMAG02__A 0x1820015 ++#define B_CE_REG_FR_TREAL03__A 0x1820016 ++#define B_CE_REG_FR_TIMAG03__A 0x1820017 ++#define B_CE_REG_FR_TREAL04__A 0x1820018 ++#define B_CE_REG_FR_TIMAG04__A 0x1820019 ++#define B_CE_REG_FR_TREAL05__A 0x182001A ++#define B_CE_REG_FR_TIMAG05__A 0x182001B ++#define B_CE_REG_FR_TREAL06__A 0x182001C ++#define B_CE_REG_FR_TIMAG06__A 0x182001D ++#define B_CE_REG_FR_TREAL07__A 0x182001E ++#define B_CE_REG_FR_TIMAG07__A 0x182001F ++#define B_CE_REG_FR_TREAL08__A 0x1820020 ++#define B_CE_REG_FR_TIMAG08__A 0x1820021 ++#define B_CE_REG_FR_TREAL09__A 0x1820022 ++#define B_CE_REG_FR_TIMAG09__A 0x1820023 ++#define B_CE_REG_FR_TREAL10__A 0x1820024 ++#define B_CE_REG_FR_TIMAG10__A 0x1820025 ++#define B_CE_REG_FR_TREAL11__A 0x1820026 ++#define B_CE_REG_FR_TIMAG11__A 0x1820027 ++#define B_CE_REG_FR_MID_TAP__A 0x1820028 ++#define B_CE_REG_FR_SQS_G00__A 0x1820029 ++#define B_CE_REG_FR_SQS_G01__A 0x182002A ++#define B_CE_REG_FR_SQS_G02__A 0x182002B ++#define B_CE_REG_FR_SQS_G03__A 0x182002C ++#define B_CE_REG_FR_SQS_G04__A 0x182002D ++#define B_CE_REG_FR_SQS_G05__A 0x182002E ++#define B_CE_REG_FR_SQS_G06__A 0x182002F ++#define B_CE_REG_FR_SQS_G07__A 0x1820030 ++#define B_CE_REG_FR_SQS_G08__A 0x1820031 ++#define B_CE_REG_FR_SQS_G09__A 0x1820032 ++#define B_CE_REG_FR_SQS_G10__A 0x1820033 ++#define B_CE_REG_FR_SQS_G11__A 0x1820034 ++#define B_CE_REG_FR_SQS_G12__A 0x1820035 ++#define B_CE_REG_FR_RIO_G00__A 0x1820036 ++#define B_CE_REG_FR_RIO_G01__A 0x1820037 ++#define B_CE_REG_FR_RIO_G02__A 0x1820038 ++#define B_CE_REG_FR_RIO_G03__A 0x1820039 ++#define B_CE_REG_FR_RIO_G04__A 0x182003A ++#define B_CE_REG_FR_RIO_G05__A 0x182003B ++#define B_CE_REG_FR_RIO_G06__A 0x182003C ++#define B_CE_REG_FR_RIO_G07__A 0x182003D ++#define B_CE_REG_FR_RIO_G08__A 0x182003E ++#define B_CE_REG_FR_RIO_G09__A 0x182003F ++#define B_CE_REG_FR_RIO_G10__A 0x1820040 ++#define B_CE_REG_FR_MODE__A 0x1820041 ++#define B_CE_REG_FR_SQS_TRH__A 0x1820042 ++#define B_CE_REG_FR_RIO_GAIN__A 0x1820043 ++#define B_CE_REG_FR_BYPASS__A 0x1820044 ++#define B_CE_REG_FR_PM_SET__A 0x1820045 ++#define B_CE_REG_FR_ERR_SH__A 0x1820046 ++#define B_CE_REG_FR_MAN_SH__A 0x1820047 ++#define B_CE_REG_FR_TAP_SH__A 0x1820048 ++#define B_EQ_COMM_EXEC__A 0x1C00000 ++#define B_EQ_REG_COMM_EXEC__A 0x1C10000 ++#define B_EQ_REG_COMM_MB__A 0x1C10002 ++#define B_EQ_REG_IS_GAIN_MAN__A 0x1C10015 ++#define B_EQ_REG_IS_GAIN_EXP__A 0x1C10016 ++#define B_EQ_REG_IS_CLIP_EXP__A 0x1C10017 ++#define B_EQ_REG_SN_CEGAIN__A 0x1C1002A ++#define B_EQ_REG_SN_OFFSET__A 0x1C1002B ++#define B_EQ_REG_RC_SEL_CAR__A 0x1C10032 ++#define B_EQ_REG_RC_SEL_CAR_INIT 0x2 ++#define B_EQ_REG_RC_SEL_CAR_DIV_ON 0x1 ++#define B_EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 ++#define B_EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 ++#define B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 ++#define B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 ++#define B_EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 ++#define B_EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 ++#define B_EQ_REG_RC_SEL_CAR_FFTMODE__M 0x80 ++#define B_EQ_REG_OT_CONST__A 0x1C10046 ++#define B_EQ_REG_OT_ALPHA__A 0x1C10047 ++#define B_EQ_REG_OT_QNT_THRES0__A 0x1C10048 ++#define B_EQ_REG_OT_QNT_THRES1__A 0x1C10049 ++#define B_EQ_REG_OT_CSI_STEP__A 0x1C1004A ++#define B_EQ_REG_OT_CSI_OFFSET__A 0x1C1004B ++#define B_EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 ++#define B_EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 ++#define B_EC_SB_REG_COMM_EXEC__A 0x2010000 ++#define B_EC_SB_REG_TR_MODE__A 0x2010010 ++#define B_EC_SB_REG_TR_MODE_8K 0x0 ++#define B_EC_SB_REG_TR_MODE_2K 0x1 ++#define B_EC_SB_REG_CONST__A 0x2010011 ++#define B_EC_SB_REG_CONST_QPSK 0x0 ++#define B_EC_SB_REG_CONST_16QAM 0x1 ++#define B_EC_SB_REG_CONST_64QAM 0x2 ++#define B_EC_SB_REG_ALPHA__A 0x2010012 ++#define B_EC_SB_REG_PRIOR__A 0x2010013 ++#define B_EC_SB_REG_PRIOR_HI 0x0 ++#define B_EC_SB_REG_PRIOR_LO 0x1 ++#define B_EC_SB_REG_CSI_HI__A 0x2010014 ++#define B_EC_SB_REG_CSI_LO__A 0x2010015 ++#define B_EC_SB_REG_SMB_TGL__A 0x2010016 ++#define B_EC_SB_REG_SNR_HI__A 0x2010017 ++#define B_EC_SB_REG_SNR_MID__A 0x2010018 ++#define B_EC_SB_REG_SNR_LO__A 0x2010019 ++#define B_EC_SB_REG_SCALE_MSB__A 0x201001A ++#define B_EC_SB_REG_SCALE_BIT2__A 0x201001B ++#define B_EC_SB_REG_SCALE_LSB__A 0x201001C ++#define B_EC_SB_REG_CSI_OFS0__A 0x201001D ++#define B_EC_SB_REG_CSI_OFS1__A 0x201001E ++#define B_EC_SB_REG_CSI_OFS2__A 0x201001F ++#define B_EC_VD_REG_COMM_EXEC__A 0x2090000 ++#define B_EC_VD_REG_FORCE__A 0x2090010 ++#define B_EC_VD_REG_SET_CODERATE__A 0x2090011 ++#define B_EC_VD_REG_SET_CODERATE_C1_2 0x0 ++#define B_EC_VD_REG_SET_CODERATE_C2_3 0x1 ++#define B_EC_VD_REG_SET_CODERATE_C3_4 0x2 ++#define B_EC_VD_REG_SET_CODERATE_C5_6 0x3 ++#define B_EC_VD_REG_SET_CODERATE_C7_8 0x4 ++#define B_EC_VD_REG_REQ_SMB_CNT__A 0x2090012 ++#define B_EC_VD_REG_RLK_ENA__A 0x2090014 ++#define B_EC_OD_REG_COMM_EXEC__A 0x2110000 ++#define B_EC_OD_REG_SYNC__A 0x2110664 ++#define B_EC_OD_DEINT_RAM__A 0x2120000 ++#define B_EC_RS_REG_COMM_EXEC__A 0x2130000 ++#define B_EC_RS_REG_REQ_PCK_CNT__A 0x2130010 ++#define B_EC_RS_REG_VAL__A 0x2130011 ++#define B_EC_RS_REG_VAL_PCK 0x1 ++#define B_EC_RS_EC_RAM__A 0x2140000 ++#define B_EC_OC_REG_COMM_EXEC__A 0x2150000 ++#define B_EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 ++#define B_EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 ++#define B_EC_OC_REG_COMM_INT_STA__A 0x2150007 ++#define B_EC_OC_REG_OC_MODE_LOP__A 0x2150010 ++#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 ++#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 ++#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 ++#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 ++#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 ++#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 ++#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 ++#define B_EC_OC_REG_OC_MODE_HIP__A 0x2150011 ++#define B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 ++#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 ++#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 ++#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 ++#define B_EC_OC_REG_OC_MPG_SIO__A 0x2150012 ++#define B_EC_OC_REG_OC_MPG_SIO__M 0xFFF ++#define B_EC_OC_REG_DTO_INC_LOP__A 0x2150014 ++#define B_EC_OC_REG_DTO_INC_HIP__A 0x2150015 ++#define B_EC_OC_REG_SNC_ISC_LVL__A 0x2150016 ++#define B_EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 ++#define B_EC_OC_REG_TMD_TOP_MODE__A 0x215001D ++#define B_EC_OC_REG_TMD_TOP_CNT__A 0x215001E ++#define B_EC_OC_REG_TMD_HIL_MAR__A 0x215001F ++#define B_EC_OC_REG_TMD_LOL_MAR__A 0x2150020 ++#define B_EC_OC_REG_TMD_CUR_CNT__A 0x2150021 ++#define B_EC_OC_REG_AVR_ASH_CNT__A 0x2150023 ++#define B_EC_OC_REG_AVR_BSH_CNT__A 0x2150024 ++#define B_EC_OC_REG_RCN_MODE__A 0x2150027 ++#define B_EC_OC_REG_RCN_CRA_LOP__A 0x2150028 ++#define B_EC_OC_REG_RCN_CRA_HIP__A 0x2150029 ++#define B_EC_OC_REG_RCN_CST_LOP__A 0x215002A ++#define B_EC_OC_REG_RCN_CST_HIP__A 0x215002B ++#define B_EC_OC_REG_RCN_SET_LVL__A 0x215002C ++#define B_EC_OC_REG_RCN_GAI_LVL__A 0x215002D ++#define B_EC_OC_REG_RCN_CLP_LOP__A 0x2150032 ++#define B_EC_OC_REG_RCN_CLP_HIP__A 0x2150033 ++#define B_EC_OC_REG_RCN_MAP_LOP__A 0x2150034 ++#define B_EC_OC_REG_RCN_MAP_HIP__A 0x2150035 ++#define B_EC_OC_REG_OCR_MPG_UOS__A 0x2150036 ++#define B_EC_OC_REG_OCR_MPG_UOS__M 0xFFF ++#define B_EC_OC_REG_OCR_MPG_UOS_INIT 0x0 ++#define B_EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 ++#define B_EC_OC_REG_IPR_INV_MPG__A 0x2150045 ++#define B_EC_OC_REG_DTO_CLKMODE__A 0x2150047 ++#define B_EC_OC_REG_DTO_PER__A 0x2150048 ++#define B_EC_OC_REG_DTO_BUR__A 0x2150049 ++#define B_EC_OC_REG_RCR_CLKMODE__A 0x215004A ++#define B_CC_REG_OSC_MODE__A 0x2410010 ++#define B_CC_REG_OSC_MODE_M20 0x1 ++#define B_CC_REG_PLL_MODE__A 0x2410011 ++#define B_CC_REG_PLL_MODE_BYPASS_PLL 0x1 ++#define B_CC_REG_PLL_MODE_PUMP_CUR_12 0x14 ++#define B_CC_REG_REF_DIVIDE__A 0x2410012 ++#define B_CC_REG_PWD_MODE__A 0x2410015 ++#define B_CC_REG_PWD_MODE_DOWN_PLL 0x2 ++#define B_CC_REG_UPDATE__A 0x2410017 ++#define B_CC_REG_UPDATE_KEY 0x3973 ++#define B_CC_REG_JTAGID_L__A 0x2410019 ++#define B_CC_REG_DIVERSITY__A 0x241001B ++#define B_LC_COMM_EXEC__A 0x2800000 ++#define B_LC_RA_RAM_IFINCR_NOM_L__A 0x282000C ++#define B_LC_RA_RAM_FILTER_SYM_SET__A 0x282001A ++#define B_LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 ++#define B_LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 ++#define B_LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 ++#define B_LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 ++#define B_LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 ++#define B_LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 ++#define B_LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 ++#define B_LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 ++#define B_LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/drxk.h b/drivers/media/dvb-frontends/drxk.h +new file mode 100644 +index 0000000..94fecfb +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxk.h +@@ -0,0 +1,68 @@ ++#ifndef _DRXK_H_ ++#define _DRXK_H_ ++ ++#include ++#include ++ ++/** ++ * struct drxk_config - Configure the initial parameters for DRX-K ++ * ++ * @adr: I2C Address of the DRX-K ++ * @parallel_ts: True means that the device uses parallel TS, ++ * Serial otherwise. ++ * @dynamic_clk: True means that the clock will be dynamically ++ * adjusted. Static clock otherwise. ++ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG. ++ * @single_master: Device is on the single master mode ++ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner ++ * @antenna_gpio: GPIO bit used to control the antenna ++ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1 ++ * means that 1=DVBC, 0 = DVBT. Zero means the opposite. ++ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength. ++ * @microcode_name: Name of the firmware file with the microcode ++ * @qam_demod_parameter_count: The number of parameters used for the command ++ * to set the demodulator parameters. All ++ * firmwares are using the 2-parameter commmand. ++ * An exception is the "drxk_a3.mc" firmware, ++ * which uses the 4-parameter command. ++ * A value of 0 (default) or lower indicates that ++ * the correct number of parameters will be ++ * automatically detected. ++ * @load_firmware_sync: Force the firmware load to be synchronous. ++ * ++ * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is ++ * UIO-3. ++ */ ++struct drxk_config { ++ u8 adr; ++ bool single_master; ++ bool no_i2c_bridge; ++ bool parallel_ts; ++ bool dynamic_clk; ++ bool enable_merr_cfg; ++ bool load_firmware_sync; ++ ++ bool antenna_dvbt; ++ u16 antenna_gpio; ++ ++ u8 mpeg_out_clk_strength; ++ int chunk_size; ++ ++ const char *microcode_name; ++ int qam_demod_parameter_count; ++}; ++ ++#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *drxk_attach(const struct drxk_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c +new file mode 100644 +index 0000000..c2fc7da +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxk_hard.c +@@ -0,0 +1,6633 @@ ++/* ++ * drxk_hard: DRX-K DVB-C/T demodulator driver ++ * ++ * Copyright (C) 2010-2011 Digital Devices GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "drxk.h" ++#include "drxk_hard.h" ++ ++static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode); ++static int PowerDownQAM(struct drxk_state *state); ++static int SetDVBTStandard(struct drxk_state *state, ++ enum OperationMode oMode); ++static int SetQAMStandard(struct drxk_state *state, ++ enum OperationMode oMode); ++static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, ++ s32 tunerFreqOffset); ++static int SetDVBTStandard(struct drxk_state *state, ++ enum OperationMode oMode); ++static int DVBTStart(struct drxk_state *state); ++static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz, ++ s32 tunerFreqOffset); ++static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus); ++static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus); ++static int SwitchAntennaToQAM(struct drxk_state *state); ++static int SwitchAntennaToDVBT(struct drxk_state *state); ++ ++static bool IsDVBT(struct drxk_state *state) ++{ ++ return state->m_OperationMode == OM_DVBT; ++} ++ ++static bool IsQAM(struct drxk_state *state) ++{ ++ return state->m_OperationMode == OM_QAM_ITU_A || ++ state->m_OperationMode == OM_QAM_ITU_B || ++ state->m_OperationMode == OM_QAM_ITU_C; ++} ++ ++#define NOA1ROM 0 ++ ++#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0) ++#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0) ++ ++#define DEFAULT_MER_83 165 ++#define DEFAULT_MER_93 250 ++ ++#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH ++#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02) ++#endif ++ ++#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH ++#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03) ++#endif ++ ++#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700 ++#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500 ++ ++#ifndef DRXK_KI_RAGC_ATV ++#define DRXK_KI_RAGC_ATV 4 ++#endif ++#ifndef DRXK_KI_IAGC_ATV ++#define DRXK_KI_IAGC_ATV 6 ++#endif ++#ifndef DRXK_KI_DAGC_ATV ++#define DRXK_KI_DAGC_ATV 7 ++#endif ++ ++#ifndef DRXK_KI_RAGC_QAM ++#define DRXK_KI_RAGC_QAM 3 ++#endif ++#ifndef DRXK_KI_IAGC_QAM ++#define DRXK_KI_IAGC_QAM 4 ++#endif ++#ifndef DRXK_KI_DAGC_QAM ++#define DRXK_KI_DAGC_QAM 7 ++#endif ++#ifndef DRXK_KI_RAGC_DVBT ++#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2) ++#endif ++#ifndef DRXK_KI_IAGC_DVBT ++#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2) ++#endif ++#ifndef DRXK_KI_DAGC_DVBT ++#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7) ++#endif ++ ++#ifndef DRXK_AGC_DAC_OFFSET ++#define DRXK_AGC_DAC_OFFSET (0x800) ++#endif ++ ++#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ ++#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) ++#endif ++ ++#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ ++#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) ++#endif ++ ++#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ ++#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) ++#endif ++ ++#ifndef DRXK_QAM_SYMBOLRATE_MAX ++#define DRXK_QAM_SYMBOLRATE_MAX (7233000) ++#endif ++ ++#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56 ++#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64 ++#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0 ++#define DRXK_BL_ROM_OFFSET_TAPS_BG 24 ++#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32 ++#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40 ++#define DRXK_BL_ROM_OFFSET_TAPS_FM 48 ++#define DRXK_BL_ROM_OFFSET_UCODE 0 ++ ++#define DRXK_BLC_TIMEOUT 100 ++ ++#define DRXK_BLCC_NR_ELEMENTS_TAPS 2 ++#define DRXK_BLCC_NR_ELEMENTS_UCODE 6 ++ ++#define DRXK_BLDC_NR_ELEMENTS_TAPS 28 ++ ++#ifndef DRXK_OFDM_NE_NOTCH_WIDTH ++#define DRXK_OFDM_NE_NOTCH_WIDTH (4) ++#endif ++ ++#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960) ++#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480) ++#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008) ++#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992) ++#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520) ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++ ++#define dprintk(level, fmt, arg...) do { \ ++if (debug >= level) \ ++ printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \ ++} while (0) ++ ++ ++static inline u32 MulDiv32(u32 a, u32 b, u32 c) ++{ ++ u64 tmp64; ++ ++ tmp64 = (u64) a * (u64) b; ++ do_div(tmp64, c); ++ ++ return (u32) tmp64; ++} ++ ++static inline u32 Frac28a(u32 a, u32 c) ++{ ++ int i = 0; ++ u32 Q1 = 0; ++ u32 R0 = 0; ++ ++ R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */ ++ Q1 = a / c; /* integer part, only the 4 least significant bits ++ will be visible in the result */ ++ ++ /* division using radix 16, 7 nibbles in the result */ ++ for (i = 0; i < 7; i++) { ++ Q1 = (Q1 << 4) | (R0 / c); ++ R0 = (R0 % c) << 4; ++ } ++ /* rounding */ ++ if ((R0 >> 3) >= c) ++ Q1++; ++ ++ return Q1; ++} ++ ++static u32 Log10Times100(u32 x) ++{ ++ static const u8 scale = 15; ++ static const u8 indexWidth = 5; ++ u8 i = 0; ++ u32 y = 0; ++ u32 d = 0; ++ u32 k = 0; ++ u32 r = 0; ++ /* ++ log2lut[n] = (1< 0; k--) { ++ if (x & (((u32) 1) << scale)) ++ break; ++ x <<= 1; ++ } ++ } else { ++ for (k = scale; k < 31; k++) { ++ if ((x & (((u32) (-1)) << (scale + 1))) == 0) ++ break; ++ x >>= 1; ++ } ++ } ++ /* ++ Now x has binary point between bit[scale] and bit[scale-1] ++ and 1.0 <= x < 2.0 */ ++ ++ /* correction for divison: log(x) = log(x/y)+log(y) */ ++ y = k * ((((u32) 1) << scale) * 200); ++ ++ /* remove integer part */ ++ x &= ((((u32) 1) << scale) - 1); ++ /* get index */ ++ i = (u8) (x >> (scale - indexWidth)); ++ /* compute delta (x - a) */ ++ d = x & ((((u32) 1) << (scale - indexWidth)) - 1); ++ /* compute log, multiplication (d* (..)) must be within range ! */ ++ y += log2lut[i] + ++ ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth)); ++ /* Conver to log10() */ ++ y /= 108853; /* (log2(10) << scale) */ ++ r = (y >> 1); ++ /* rounding */ ++ if (y & ((u32) 1)) ++ r++; ++ return r; ++} ++ ++/****************************************************************************/ ++/* I2C **********************************************************************/ ++/****************************************************************************/ ++ ++static int drxk_i2c_lock(struct drxk_state *state) ++{ ++ i2c_lock_adapter(state->i2c); ++ state->drxk_i2c_exclusive_lock = true; ++ ++ return 0; ++} ++ ++static void drxk_i2c_unlock(struct drxk_state *state) ++{ ++ if (!state->drxk_i2c_exclusive_lock) ++ return; ++ ++ i2c_unlock_adapter(state->i2c); ++ state->drxk_i2c_exclusive_lock = false; ++} ++ ++static int drxk_i2c_transfer(struct drxk_state *state, struct i2c_msg *msgs, ++ unsigned len) ++{ ++ if (state->drxk_i2c_exclusive_lock) ++ return __i2c_transfer(state->i2c, msgs, len); ++ else ++ return i2c_transfer(state->i2c, msgs, len); ++} ++ ++static int i2c_read1(struct drxk_state *state, u8 adr, u8 *val) ++{ ++ struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1} ++ }; ++ ++ return drxk_i2c_transfer(state, msgs, 1); ++} ++ ++static int i2c_write(struct drxk_state *state, u8 adr, u8 *data, int len) ++{ ++ int status; ++ struct i2c_msg msg = { ++ .addr = adr, .flags = 0, .buf = data, .len = len }; ++ ++ dprintk(3, ":"); ++ if (debug > 2) { ++ int i; ++ for (i = 0; i < len; i++) ++ printk(KERN_CONT " %02x", data[i]); ++ printk(KERN_CONT "\n"); ++ } ++ status = drxk_i2c_transfer(state, &msg, 1); ++ if (status >= 0 && status != 1) ++ status = -EIO; ++ ++ if (status < 0) ++ printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr); ++ ++ return status; ++} ++ ++static int i2c_read(struct drxk_state *state, ++ u8 adr, u8 *msg, int len, u8 *answ, int alen) ++{ ++ int status; ++ struct i2c_msg msgs[2] = { ++ {.addr = adr, .flags = 0, ++ .buf = msg, .len = len}, ++ {.addr = adr, .flags = I2C_M_RD, ++ .buf = answ, .len = alen} ++ }; ++ ++ status = drxk_i2c_transfer(state, msgs, 2); ++ if (status != 2) { ++ if (debug > 2) ++ printk(KERN_CONT ": ERROR!\n"); ++ if (status >= 0) ++ status = -EIO; ++ ++ printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr); ++ return status; ++ } ++ if (debug > 2) { ++ int i; ++ dprintk(2, ": read from"); ++ for (i = 0; i < len; i++) ++ printk(KERN_CONT " %02x", msg[i]); ++ printk(KERN_CONT ", value = "); ++ for (i = 0; i < alen; i++) ++ printk(KERN_CONT " %02x", answ[i]); ++ printk(KERN_CONT "\n"); ++ } ++ return 0; ++} ++ ++static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags) ++{ ++ int status; ++ u8 adr = state->demod_address, mm1[4], mm2[2], len; ++ ++ if (state->single_master) ++ flags |= 0xC0; ++ ++ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { ++ mm1[0] = (((reg << 1) & 0xFF) | 0x01); ++ mm1[1] = ((reg >> 16) & 0xFF); ++ mm1[2] = ((reg >> 24) & 0xFF) | flags; ++ mm1[3] = ((reg >> 7) & 0xFF); ++ len = 4; ++ } else { ++ mm1[0] = ((reg << 1) & 0xFF); ++ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); ++ len = 2; ++ } ++ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); ++ status = i2c_read(state, adr, mm1, len, mm2, 2); ++ if (status < 0) ++ return status; ++ if (data) ++ *data = mm2[0] | (mm2[1] << 8); ++ ++ return 0; ++} ++ ++static int read16(struct drxk_state *state, u32 reg, u16 *data) ++{ ++ return read16_flags(state, reg, data, 0); ++} ++ ++static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags) ++{ ++ int status; ++ u8 adr = state->demod_address, mm1[4], mm2[4], len; ++ ++ if (state->single_master) ++ flags |= 0xC0; ++ ++ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { ++ mm1[0] = (((reg << 1) & 0xFF) | 0x01); ++ mm1[1] = ((reg >> 16) & 0xFF); ++ mm1[2] = ((reg >> 24) & 0xFF) | flags; ++ mm1[3] = ((reg >> 7) & 0xFF); ++ len = 4; ++ } else { ++ mm1[0] = ((reg << 1) & 0xFF); ++ mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); ++ len = 2; ++ } ++ dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); ++ status = i2c_read(state, adr, mm1, len, mm2, 4); ++ if (status < 0) ++ return status; ++ if (data) ++ *data = mm2[0] | (mm2[1] << 8) | ++ (mm2[2] << 16) | (mm2[3] << 24); ++ ++ return 0; ++} ++ ++static int read32(struct drxk_state *state, u32 reg, u32 *data) ++{ ++ return read32_flags(state, reg, data, 0); ++} ++ ++static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags) ++{ ++ u8 adr = state->demod_address, mm[6], len; ++ ++ if (state->single_master) ++ flags |= 0xC0; ++ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { ++ mm[0] = (((reg << 1) & 0xFF) | 0x01); ++ mm[1] = ((reg >> 16) & 0xFF); ++ mm[2] = ((reg >> 24) & 0xFF) | flags; ++ mm[3] = ((reg >> 7) & 0xFF); ++ len = 4; ++ } else { ++ mm[0] = ((reg << 1) & 0xFF); ++ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); ++ len = 2; ++ } ++ mm[len] = data & 0xff; ++ mm[len + 1] = (data >> 8) & 0xff; ++ ++ dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags); ++ return i2c_write(state, adr, mm, len + 2); ++} ++ ++static int write16(struct drxk_state *state, u32 reg, u16 data) ++{ ++ return write16_flags(state, reg, data, 0); ++} ++ ++static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags) ++{ ++ u8 adr = state->demod_address, mm[8], len; ++ ++ if (state->single_master) ++ flags |= 0xC0; ++ if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { ++ mm[0] = (((reg << 1) & 0xFF) | 0x01); ++ mm[1] = ((reg >> 16) & 0xFF); ++ mm[2] = ((reg >> 24) & 0xFF) | flags; ++ mm[3] = ((reg >> 7) & 0xFF); ++ len = 4; ++ } else { ++ mm[0] = ((reg << 1) & 0xFF); ++ mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); ++ len = 2; ++ } ++ mm[len] = data & 0xff; ++ mm[len + 1] = (data >> 8) & 0xff; ++ mm[len + 2] = (data >> 16) & 0xff; ++ mm[len + 3] = (data >> 24) & 0xff; ++ dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags); ++ ++ return i2c_write(state, adr, mm, len + 4); ++} ++ ++static int write32(struct drxk_state *state, u32 reg, u32 data) ++{ ++ return write32_flags(state, reg, data, 0); ++} ++ ++static int write_block(struct drxk_state *state, u32 Address, ++ const int BlockSize, const u8 pBlock[]) ++{ ++ int status = 0, BlkSize = BlockSize; ++ u8 Flags = 0; ++ ++ if (state->single_master) ++ Flags |= 0xC0; ++ ++ while (BlkSize > 0) { ++ int Chunk = BlkSize > state->m_ChunkSize ? ++ state->m_ChunkSize : BlkSize; ++ u8 *AdrBuf = &state->Chunk[0]; ++ u32 AdrLength = 0; ++ ++ if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) { ++ AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01); ++ AdrBuf[1] = ((Address >> 16) & 0xFF); ++ AdrBuf[2] = ((Address >> 24) & 0xFF); ++ AdrBuf[3] = ((Address >> 7) & 0xFF); ++ AdrBuf[2] |= Flags; ++ AdrLength = 4; ++ if (Chunk == state->m_ChunkSize) ++ Chunk -= 2; ++ } else { ++ AdrBuf[0] = ((Address << 1) & 0xFF); ++ AdrBuf[1] = (((Address >> 16) & 0x0F) | ++ ((Address >> 18) & 0xF0)); ++ AdrLength = 2; ++ } ++ memcpy(&state->Chunk[AdrLength], pBlock, Chunk); ++ dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags); ++ if (debug > 1) { ++ int i; ++ if (pBlock) ++ for (i = 0; i < Chunk; i++) ++ printk(KERN_CONT " %02x", pBlock[i]); ++ printk(KERN_CONT "\n"); ++ } ++ status = i2c_write(state, state->demod_address, ++ &state->Chunk[0], Chunk + AdrLength); ++ if (status < 0) { ++ printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n", ++ __func__, Address); ++ break; ++ } ++ pBlock += Chunk; ++ Address += (Chunk >> 1); ++ BlkSize -= Chunk; ++ } ++ return status; ++} ++ ++#ifndef DRXK_MAX_RETRIES_POWERUP ++#define DRXK_MAX_RETRIES_POWERUP 20 ++#endif ++ ++static int PowerUpDevice(struct drxk_state *state) ++{ ++ int status; ++ u8 data = 0; ++ u16 retryCount = 0; ++ ++ dprintk(1, "\n"); ++ ++ status = i2c_read1(state, state->demod_address, &data); ++ if (status < 0) { ++ do { ++ data = 0; ++ status = i2c_write(state, state->demod_address, ++ &data, 1); ++ msleep(10); ++ retryCount++; ++ if (status < 0) ++ continue; ++ status = i2c_read1(state, state->demod_address, ++ &data); ++ } while (status < 0 && ++ (retryCount < DRXK_MAX_RETRIES_POWERUP)); ++ if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP) ++ goto error; ++ } ++ ++ /* Make sure all clk domains are active */ ++ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); ++ if (status < 0) ++ goto error; ++ /* Enable pll lock tests */ ++ status = write16(state, SIO_CC_PLL_LOCK__A, 1); ++ if (status < 0) ++ goto error; ++ ++ state->m_currentPowerMode = DRX_POWER_UP; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++ ++static int init_state(struct drxk_state *state) ++{ ++ /* ++ * FIXME: most (all?) of the values bellow should be moved into ++ * struct drxk_config, as they are probably board-specific ++ */ ++ u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO; ++ u32 ulVSBIfAgcOutputLevel = 0; ++ u32 ulVSBIfAgcMinLevel = 0; ++ u32 ulVSBIfAgcMaxLevel = 0x7FFF; ++ u32 ulVSBIfAgcSpeed = 3; ++ ++ u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO; ++ u32 ulVSBRfAgcOutputLevel = 0; ++ u32 ulVSBRfAgcMinLevel = 0; ++ u32 ulVSBRfAgcMaxLevel = 0x7FFF; ++ u32 ulVSBRfAgcSpeed = 3; ++ u32 ulVSBRfAgcTop = 9500; ++ u32 ulVSBRfAgcCutOffCurrent = 4000; ++ ++ u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO; ++ u32 ulATVIfAgcOutputLevel = 0; ++ u32 ulATVIfAgcMinLevel = 0; ++ u32 ulATVIfAgcMaxLevel = 0; ++ u32 ulATVIfAgcSpeed = 3; ++ ++ u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF; ++ u32 ulATVRfAgcOutputLevel = 0; ++ u32 ulATVRfAgcMinLevel = 0; ++ u32 ulATVRfAgcMaxLevel = 0; ++ u32 ulATVRfAgcTop = 9500; ++ u32 ulATVRfAgcCutOffCurrent = 4000; ++ u32 ulATVRfAgcSpeed = 3; ++ ++ u32 ulQual83 = DEFAULT_MER_83; ++ u32 ulQual93 = DEFAULT_MER_93; ++ ++ u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; ++ u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; ++ ++ /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ ++ /* io_pad_cfg_mode output mode is drive always */ ++ /* io_pad_cfg_drive is set to power 2 (23 mA) */ ++ u32 ulGPIOCfg = 0x0113; ++ u32 ulInvertTSClock = 0; ++ u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH; ++ u32 ulDVBTBitrate = 50000000; ++ u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8; ++ ++ u32 ulInsertRSByte = 0; ++ ++ u32 ulRfMirror = 1; ++ u32 ulPowerDown = 0; ++ ++ dprintk(1, "\n"); ++ ++ state->m_hasLNA = false; ++ state->m_hasDVBT = false; ++ state->m_hasDVBC = false; ++ state->m_hasATV = false; ++ state->m_hasOOB = false; ++ state->m_hasAudio = false; ++ ++ if (!state->m_ChunkSize) ++ state->m_ChunkSize = 124; ++ ++ state->m_oscClockFreq = 0; ++ state->m_smartAntInverted = false; ++ state->m_bPDownOpenBridge = false; ++ ++ /* real system clock frequency in kHz */ ++ state->m_sysClockFreq = 151875; ++ /* Timing div, 250ns/Psys */ ++ /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */ ++ state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) * ++ HI_I2C_DELAY) / 1000; ++ /* Clipping */ ++ if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M) ++ state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M; ++ state->m_HICfgWakeUpKey = (state->demod_address << 1); ++ /* port/bridge/power down ctrl */ ++ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; ++ ++ state->m_bPowerDown = (ulPowerDown != 0); ++ ++ state->m_DRXK_A3_PATCH_CODE = false; ++ ++ /* Init AGC and PGA parameters */ ++ /* VSB IF */ ++ state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode); ++ state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel); ++ state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel); ++ state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel); ++ state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed); ++ state->m_vsbPgaCfg = 140; ++ ++ /* VSB RF */ ++ state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode); ++ state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel); ++ state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel); ++ state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel); ++ state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed); ++ state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop); ++ state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent); ++ state->m_vsbPreSawCfg.reference = 0x07; ++ state->m_vsbPreSawCfg.usePreSaw = true; ++ ++ state->m_Quality83percent = DEFAULT_MER_83; ++ state->m_Quality93percent = DEFAULT_MER_93; ++ if (ulQual93 <= 500 && ulQual83 < ulQual93) { ++ state->m_Quality83percent = ulQual83; ++ state->m_Quality93percent = ulQual93; ++ } ++ ++ /* ATV IF */ ++ state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode); ++ state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel); ++ state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel); ++ state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel); ++ state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed); ++ ++ /* ATV RF */ ++ state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode); ++ state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel); ++ state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel); ++ state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel); ++ state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed); ++ state->m_atvRfAgcCfg.top = (ulATVRfAgcTop); ++ state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent); ++ state->m_atvPreSawCfg.reference = 0x04; ++ state->m_atvPreSawCfg.usePreSaw = true; ++ ++ ++ /* DVBT RF */ ++ state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF; ++ state->m_dvbtRfAgcCfg.outputLevel = 0; ++ state->m_dvbtRfAgcCfg.minOutputLevel = 0; ++ state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF; ++ state->m_dvbtRfAgcCfg.top = 0x2100; ++ state->m_dvbtRfAgcCfg.cutOffCurrent = 4000; ++ state->m_dvbtRfAgcCfg.speed = 1; ++ ++ ++ /* DVBT IF */ ++ state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO; ++ state->m_dvbtIfAgcCfg.outputLevel = 0; ++ state->m_dvbtIfAgcCfg.minOutputLevel = 0; ++ state->m_dvbtIfAgcCfg.maxOutputLevel = 9000; ++ state->m_dvbtIfAgcCfg.top = 13424; ++ state->m_dvbtIfAgcCfg.cutOffCurrent = 0; ++ state->m_dvbtIfAgcCfg.speed = 3; ++ state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30; ++ state->m_dvbtIfAgcCfg.IngainTgtMax = 30000; ++ /* state->m_dvbtPgaCfg = 140; */ ++ ++ state->m_dvbtPreSawCfg.reference = 4; ++ state->m_dvbtPreSawCfg.usePreSaw = false; ++ ++ /* QAM RF */ ++ state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF; ++ state->m_qamRfAgcCfg.outputLevel = 0; ++ state->m_qamRfAgcCfg.minOutputLevel = 6023; ++ state->m_qamRfAgcCfg.maxOutputLevel = 27000; ++ state->m_qamRfAgcCfg.top = 0x2380; ++ state->m_qamRfAgcCfg.cutOffCurrent = 4000; ++ state->m_qamRfAgcCfg.speed = 3; ++ ++ /* QAM IF */ ++ state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO; ++ state->m_qamIfAgcCfg.outputLevel = 0; ++ state->m_qamIfAgcCfg.minOutputLevel = 0; ++ state->m_qamIfAgcCfg.maxOutputLevel = 9000; ++ state->m_qamIfAgcCfg.top = 0x0511; ++ state->m_qamIfAgcCfg.cutOffCurrent = 0; ++ state->m_qamIfAgcCfg.speed = 3; ++ state->m_qamIfAgcCfg.IngainTgtMax = 5119; ++ state->m_qamIfAgcCfg.FastClipCtrlDelay = 50; ++ ++ state->m_qamPgaCfg = 140; ++ state->m_qamPreSawCfg.reference = 4; ++ state->m_qamPreSawCfg.usePreSaw = false; ++ ++ state->m_OperationMode = OM_NONE; ++ state->m_DrxkState = DRXK_UNINITIALIZED; ++ ++ /* MPEG output configuration */ ++ state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */ ++ state->m_insertRSByte = false; /* If TRUE; insert RS byte */ ++ state->m_invertDATA = false; /* If TRUE; invert DATA signals */ ++ state->m_invertERR = false; /* If TRUE; invert ERR signal */ ++ state->m_invertSTR = false; /* If TRUE; invert STR signals */ ++ state->m_invertVAL = false; /* If TRUE; invert VAL signals */ ++ state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */ ++ ++ /* If TRUE; static MPEG clockrate will be used; ++ otherwise clockrate will adapt to the bitrate of the TS */ ++ ++ state->m_DVBTBitrate = ulDVBTBitrate; ++ state->m_DVBCBitrate = ulDVBCBitrate; ++ ++ state->m_TSDataStrength = (ulTSDataStrength & 0x07); ++ ++ /* Maximum bitrate in b/s in case static clockrate is selected */ ++ state->m_mpegTsStaticBitrate = 19392658; ++ state->m_disableTEIhandling = false; ++ ++ if (ulInsertRSByte) ++ state->m_insertRSByte = true; ++ ++ state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; ++ if (ulMpegLockTimeOut < 10000) ++ state->m_MpegLockTimeOut = ulMpegLockTimeOut; ++ state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; ++ if (ulDemodLockTimeOut < 10000) ++ state->m_DemodLockTimeOut = ulDemodLockTimeOut; ++ ++ /* QAM defaults */ ++ state->m_Constellation = DRX_CONSTELLATION_AUTO; ++ state->m_qamInterleaveMode = DRXK_QAM_I12_J17; ++ state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */ ++ state->m_fecRsPrescale = 1; ++ ++ state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM; ++ state->m_agcFastClipCtrlDelay = 0; ++ ++ state->m_GPIOCfg = (ulGPIOCfg); ++ ++ state->m_bPowerDown = false; ++ state->m_currentPowerMode = DRX_POWER_DOWN; ++ ++ state->m_rfmirror = (ulRfMirror == 0); ++ state->m_IfAgcPol = false; ++ return 0; ++} ++ ++static int DRXX_Open(struct drxk_state *state) ++{ ++ int status = 0; ++ u32 jtag = 0; ++ u16 bid = 0; ++ u16 key = 0; ++ ++ dprintk(1, "\n"); ++ /* stop lock indicator process */ ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ /* Check device id */ ++ status = read16(state, SIO_TOP_COMM_KEY__A, &key); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); ++ if (status < 0) ++ goto error; ++ status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag); ++ if (status < 0) ++ goto error; ++ status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_TOP_COMM_KEY__A, key); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int GetDeviceCapabilities(struct drxk_state *state) ++{ ++ u16 sioPdrOhwCfg = 0; ++ u32 sioTopJtagidLo = 0; ++ int status; ++ const char *spin = ""; ++ ++ dprintk(1, "\n"); ++ ++ /* driver 0.9.0 */ ++ /* stop lock indicator process */ ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); ++ if (status < 0) ++ goto error; ++ status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); ++ if (status < 0) ++ goto error; ++ ++ switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) { ++ case 0: ++ /* ignore (bypass ?) */ ++ break; ++ case 1: ++ /* 27 MHz */ ++ state->m_oscClockFreq = 27000; ++ break; ++ case 2: ++ /* 20.25 MHz */ ++ state->m_oscClockFreq = 20250; ++ break; ++ case 3: ++ /* 4 MHz */ ++ state->m_oscClockFreq = 20250; ++ break; ++ default: ++ printk(KERN_ERR "drxk: Clock Frequency is unknown\n"); ++ return -EINVAL; ++ } ++ /* ++ Determine device capabilities ++ Based on pinning v14 ++ */ ++ status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo); ++ if (status < 0) ++ goto error; ++ ++ printk(KERN_INFO "drxk: status = 0x%08x\n", sioTopJtagidLo); ++ ++ /* driver 0.9.0 */ ++ switch ((sioTopJtagidLo >> 29) & 0xF) { ++ case 0: ++ state->m_deviceSpin = DRXK_SPIN_A1; ++ spin = "A1"; ++ break; ++ case 2: ++ state->m_deviceSpin = DRXK_SPIN_A2; ++ spin = "A2"; ++ break; ++ case 3: ++ state->m_deviceSpin = DRXK_SPIN_A3; ++ spin = "A3"; ++ break; ++ default: ++ state->m_deviceSpin = DRXK_SPIN_UNKNOWN; ++ status = -EINVAL; ++ printk(KERN_ERR "drxk: Spin %d unknown\n", ++ (sioTopJtagidLo >> 29) & 0xF); ++ goto error2; ++ } ++ switch ((sioTopJtagidLo >> 12) & 0xFF) { ++ case 0x13: ++ /* typeId = DRX3913K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = false; ++ state->m_hasAudio = false; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = true; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = false; ++ state->m_hasGPIO1 = false; ++ state->m_hasIRQN = false; ++ break; ++ case 0x15: ++ /* typeId = DRX3915K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = false; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = false; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x16: ++ /* typeId = DRX3916K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = false; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = false; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x18: ++ /* typeId = DRX3918K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = true; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = false; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x21: ++ /* typeId = DRX3921K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = true; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = true; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x23: ++ /* typeId = DRX3923K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = true; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = true; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x25: ++ /* typeId = DRX3925K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = true; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = true; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ case 0x26: ++ /* typeId = DRX3926K_TYPE_ID */ ++ state->m_hasLNA = false; ++ state->m_hasOOB = false; ++ state->m_hasATV = true; ++ state->m_hasAudio = false; ++ state->m_hasDVBT = true; ++ state->m_hasDVBC = true; ++ state->m_hasSAWSW = true; ++ state->m_hasGPIO2 = true; ++ state->m_hasGPIO1 = true; ++ state->m_hasIRQN = false; ++ break; ++ default: ++ printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n", ++ ((sioTopJtagidLo >> 12) & 0xFF)); ++ status = -EINVAL; ++ goto error2; ++ } ++ ++ printk(KERN_INFO ++ "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n", ++ ((sioTopJtagidLo >> 12) & 0xFF), spin, ++ state->m_oscClockFreq / 1000, ++ state->m_oscClockFreq % 1000); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++error2: ++ return status; ++} ++ ++static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult) ++{ ++ int status; ++ bool powerdown_cmd; ++ ++ dprintk(1, "\n"); ++ ++ /* Write command */ ++ status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd); ++ if (status < 0) ++ goto error; ++ if (cmd == SIO_HI_RA_RAM_CMD_RESET) ++ msleep(1); ++ ++ powerdown_cmd = ++ (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) && ++ ((state->m_HICfgCtrl) & ++ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) == ++ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ); ++ if (powerdown_cmd == false) { ++ /* Wait until command rdy */ ++ u32 retryCount = 0; ++ u16 waitCmd; ++ ++ do { ++ msleep(1); ++ retryCount += 1; ++ status = read16(state, SIO_HI_RA_RAM_CMD__A, ++ &waitCmd); ++ } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES) ++ && (waitCmd != 0)); ++ if (status < 0) ++ goto error; ++ status = read16(state, SIO_HI_RA_RAM_RES__A, pResult); ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int HI_CfgCommand(struct drxk_state *state) ++{ ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ mutex_lock(&state->mutex); ++ ++ status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); ++ if (status < 0) ++ goto error; ++ status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0); ++ if (status < 0) ++ goto error; ++ ++ state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; ++error: ++ mutex_unlock(&state->mutex); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int InitHI(struct drxk_state *state) ++{ ++ dprintk(1, "\n"); ++ ++ state->m_HICfgWakeUpKey = (state->demod_address << 1); ++ state->m_HICfgTimeout = 0x96FF; ++ /* port/bridge/power down ctrl */ ++ state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; ++ ++ return HI_CfgCommand(state); ++} ++ ++static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable) ++{ ++ int status = -1; ++ u16 sioPdrMclkCfg = 0; ++ u16 sioPdrMdxCfg = 0; ++ u16 err_cfg = 0; ++ ++ dprintk(1, ": mpeg %s, %s mode\n", ++ mpegEnable ? "enable" : "disable", ++ state->m_enableParallel ? "parallel" : "serial"); ++ ++ /* stop lock indicator process */ ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ ++ /* MPEG TS pad configuration */ ++ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); ++ if (status < 0) ++ goto error; ++ ++ if (mpegEnable == false) { ++ /* Set MPEG TS pads to inputmode */ ++ status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ } else { ++ /* Enable MPEG output */ ++ sioPdrMdxCfg = ++ ((state->m_TSDataStrength << ++ SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003); ++ sioPdrMclkCfg = ((state->m_TSClockkStrength << ++ SIO_PDR_MCLK_CFG_DRIVE__B) | ++ 0x0003); ++ ++ status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ ++ if (state->enable_merr_cfg) ++ err_cfg = sioPdrMdxCfg; ++ ++ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg); ++ if (status < 0) ++ goto error; ++ ++ if (state->m_enableParallel == true) { ++ /* paralel -> enable MD1 to MD7 */ ++ status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ } else { ++ sioPdrMdxCfg = ((state->m_TSDataStrength << ++ SIO_PDR_MD0_CFG_DRIVE__B) ++ | 0x0003); ++ /* serial -> disable MD1 to MD7 */ ++ status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ } ++ status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg); ++ if (status < 0) ++ goto error; ++ } ++ /* Enable MB output over MPEG pads and ctl input */ ++ status = write16(state, SIO_PDR_MON_CFG__A, 0x0000); ++ if (status < 0) ++ goto error; ++ /* Write nomagic word to enable pdr reg write */ ++ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int MPEGTSDisable(struct drxk_state *state) ++{ ++ dprintk(1, "\n"); ++ ++ return MPEGTSConfigurePins(state, false); ++} ++ ++static int BLChainCmd(struct drxk_state *state, ++ u16 romOffset, u16 nrOfElements, u32 timeOut) ++{ ++ u16 blStatus = 0; ++ int status; ++ unsigned long end; ++ ++ dprintk(1, "\n"); ++ mutex_lock(&state->mutex); ++ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); ++ if (status < 0) ++ goto error; ++ ++ end = jiffies + msecs_to_jiffies(timeOut); ++ do { ++ msleep(1); ++ status = read16(state, SIO_BL_STATUS__A, &blStatus); ++ if (status < 0) ++ goto error; ++ } while ((blStatus == 0x1) && ++ ((time_is_after_jiffies(end)))); ++ ++ if (blStatus == 0x1) { ++ printk(KERN_ERR "drxk: SIO not ready\n"); ++ status = -EINVAL; ++ goto error2; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++error2: ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++ ++static int DownloadMicrocode(struct drxk_state *state, ++ const u8 pMCImage[], u32 Length) ++{ ++ const u8 *pSrc = pMCImage; ++ u32 Address; ++ u16 nBlocks; ++ u16 BlockSize; ++ u32 offset = 0; ++ u32 i; ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ ++ /* down the drain (we don't care about MAGIC_WORD) */ ++#if 0 ++ /* For future reference */ ++ Drain = (pSrc[0] << 8) | pSrc[1]; ++#endif ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ nBlocks = (pSrc[0] << 8) | pSrc[1]; ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ for (i = 0; i < nBlocks; i += 1) { ++ Address = (pSrc[0] << 24) | (pSrc[1] << 16) | ++ (pSrc[2] << 8) | pSrc[3]; ++ pSrc += sizeof(u32); ++ offset += sizeof(u32); ++ ++ BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16); ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++#if 0 ++ /* For future reference */ ++ Flags = (pSrc[0] << 8) | pSrc[1]; ++#endif ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++#if 0 ++ /* For future reference */ ++ BlockCRC = (pSrc[0] << 8) | pSrc[1]; ++#endif ++ pSrc += sizeof(u16); ++ offset += sizeof(u16); ++ ++ if (offset + BlockSize > Length) { ++ printk(KERN_ERR "drxk: Firmware is corrupted.\n"); ++ return -EINVAL; ++ } ++ ++ status = write_block(state, Address, BlockSize, pSrc); ++ if (status < 0) { ++ printk(KERN_ERR "drxk: Error %d while loading firmware\n", status); ++ break; ++ } ++ pSrc += BlockSize; ++ offset += BlockSize; ++ } ++ return status; ++} ++ ++static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable) ++{ ++ int status; ++ u16 data = 0; ++ u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON; ++ u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED; ++ unsigned long end; ++ ++ dprintk(1, "\n"); ++ ++ if (enable == false) { ++ desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF; ++ desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN; ++ } ++ ++ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); ++ if (status >= 0 && data == desiredStatus) { ++ /* tokenring already has correct status */ ++ return status; ++ } ++ /* Disable/enable dvbt tokenring bridge */ ++ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl); ++ ++ end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT); ++ do { ++ status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); ++ if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end)) ++ break; ++ msleep(1); ++ } while (1); ++ if (data != desiredStatus) { ++ printk(KERN_ERR "drxk: SIO not ready\n"); ++ return -EINVAL; ++ } ++ return status; ++} ++ ++static int MPEGTSStop(struct drxk_state *state) ++{ ++ int status = 0; ++ u16 fecOcSncMode = 0; ++ u16 fecOcIprMode = 0; ++ ++ dprintk(1, "\n"); ++ ++ /* Gracefull shutdown (byte boundaries) */ ++ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode); ++ if (status < 0) ++ goto error; ++ fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M; ++ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode); ++ if (status < 0) ++ goto error; ++ ++ /* Suppress MCLK during absence of data */ ++ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode); ++ if (status < 0) ++ goto error; ++ fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M; ++ status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int scu_command(struct drxk_state *state, ++ u16 cmd, u8 parameterLen, ++ u16 *parameter, u8 resultLen, u16 *result) ++{ ++#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15 ++#error DRXK register mapping no longer compatible with this routine! ++#endif ++ u16 curCmd = 0; ++ int status = -EINVAL; ++ unsigned long end; ++ u8 buffer[34]; ++ int cnt = 0, ii; ++ const char *p; ++ char errname[30]; ++ ++ dprintk(1, "\n"); ++ ++ if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) || ++ ((resultLen > 0) && (result == NULL))) { ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++ } ++ ++ mutex_lock(&state->mutex); ++ ++ /* assume that the command register is ready ++ since it is checked afterwards */ ++ for (ii = parameterLen - 1; ii >= 0; ii -= 1) { ++ buffer[cnt++] = (parameter[ii] & 0xFF); ++ buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF); ++ } ++ buffer[cnt++] = (cmd & 0xFF); ++ buffer[cnt++] = ((cmd >> 8) & 0xFF); ++ ++ write_block(state, SCU_RAM_PARAM_0__A - ++ (parameterLen - 1), cnt, buffer); ++ /* Wait until SCU has processed command */ ++ end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME); ++ do { ++ msleep(1); ++ status = read16(state, SCU_RAM_COMMAND__A, &curCmd); ++ if (status < 0) ++ goto error; ++ } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end))); ++ if (curCmd != DRX_SCU_READY) { ++ printk(KERN_ERR "drxk: SCU not ready\n"); ++ status = -EIO; ++ goto error2; ++ } ++ /* read results */ ++ if ((resultLen > 0) && (result != NULL)) { ++ s16 err; ++ int ii; ++ ++ for (ii = resultLen - 1; ii >= 0; ii -= 1) { ++ status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]); ++ if (status < 0) ++ goto error; ++ } ++ ++ /* Check if an error was reported by SCU */ ++ err = (s16)result[0]; ++ if (err >= 0) ++ goto error; ++ ++ /* check for the known error codes */ ++ switch (err) { ++ case SCU_RESULT_UNKCMD: ++ p = "SCU_RESULT_UNKCMD"; ++ break; ++ case SCU_RESULT_UNKSTD: ++ p = "SCU_RESULT_UNKSTD"; ++ break; ++ case SCU_RESULT_SIZE: ++ p = "SCU_RESULT_SIZE"; ++ break; ++ case SCU_RESULT_INVPAR: ++ p = "SCU_RESULT_INVPAR"; ++ break; ++ default: /* Other negative values are errors */ ++ sprintf(errname, "ERROR: %d\n", err); ++ p = errname; ++ } ++ printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd); ++ print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt); ++ status = -EINVAL; ++ goto error2; ++ } ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++error2: ++ mutex_unlock(&state->mutex); ++ return status; ++} ++ ++static int SetIqmAf(struct drxk_state *state, bool active) ++{ ++ u16 data = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ /* Configure IQM */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ ++ if (!active) { ++ data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY ++ | IQM_AF_STDBY_STDBY_AMP_STANDBY ++ | IQM_AF_STDBY_STDBY_PD_STANDBY ++ | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY ++ | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY); ++ } else { ++ data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY) ++ & (~IQM_AF_STDBY_STDBY_AMP_STANDBY) ++ & (~IQM_AF_STDBY_STDBY_PD_STANDBY) ++ & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY) ++ & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY) ++ ); ++ } ++ status = write16(state, IQM_AF_STDBY__A, data); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode) ++{ ++ int status = 0; ++ u16 sioCcPwdMode = 0; ++ ++ dprintk(1, "\n"); ++ ++ /* Check arguments */ ++ if (mode == NULL) ++ return -EINVAL; ++ ++ switch (*mode) { ++ case DRX_POWER_UP: ++ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE; ++ break; ++ case DRXK_POWER_DOWN_OFDM: ++ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM; ++ break; ++ case DRXK_POWER_DOWN_CORE: ++ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK; ++ break; ++ case DRXK_POWER_DOWN_PLL: ++ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL; ++ break; ++ case DRX_POWER_DOWN: ++ sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC; ++ break; ++ default: ++ /* Unknow sleep mode */ ++ return -EINVAL; ++ } ++ ++ /* If already in requested power mode, do nothing */ ++ if (state->m_currentPowerMode == *mode) ++ return 0; ++ ++ /* For next steps make sure to start from DRX_POWER_UP mode */ ++ if (state->m_currentPowerMode != DRX_POWER_UP) { ++ status = PowerUpDevice(state); ++ if (status < 0) ++ goto error; ++ status = DVBTEnableOFDMTokenRing(state, true); ++ if (status < 0) ++ goto error; ++ } ++ ++ if (*mode == DRX_POWER_UP) { ++ /* Restore analog & pin configuartion */ ++ } else { ++ /* Power down to requested mode */ ++ /* Backup some register settings */ ++ /* Set pins with possible pull-ups connected ++ to them in input mode */ ++ /* Analog power down */ ++ /* ADC power down */ ++ /* Power down device */ ++ /* stop all comm_exec */ ++ /* Stop and power down previous standard */ ++ switch (state->m_OperationMode) { ++ case OM_DVBT: ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = PowerDownDVBT(state, false); ++ if (status < 0) ++ goto error; ++ break; ++ case OM_QAM_ITU_A: ++ case OM_QAM_ITU_C: ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = PowerDownQAM(state); ++ if (status < 0) ++ goto error; ++ break; ++ default: ++ break; ++ } ++ status = DVBTEnableOFDMTokenRing(state, false); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); ++ if (status < 0) ++ goto error; ++ ++ if (*mode != DRXK_POWER_DOWN_OFDM) { ++ state->m_HICfgCtrl |= ++ SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; ++ status = HI_CfgCommand(state); ++ if (status < 0) ++ goto error; ++ } ++ } ++ state->m_currentPowerMode = *mode; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode) ++{ ++ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; ++ u16 cmdResult = 0; ++ u16 data = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ status = read16(state, SCU_COMM_EXEC__A, &data); ++ if (status < 0) ++ goto error; ++ if (data == SCU_COMM_EXEC_ACTIVE) { ++ /* Send OFDM stop command */ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ /* Send OFDM reset command */ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ } ++ ++ /* Reset datapath for OFDM, processors first */ ++ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); ++ if (status < 0) ++ goto error; ++ ++ /* powerdown AFE */ ++ status = SetIqmAf(state, false); ++ if (status < 0) ++ goto error; ++ ++ /* powerdown to OFDM mode */ ++ if (setPowerMode) { ++ status = CtrlPowerMode(state, &powerMode); ++ if (status < 0) ++ goto error; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SetOperationMode(struct drxk_state *state, ++ enum OperationMode oMode) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ /* ++ Stop and power down previous standard ++ TODO investigate total power down instead of partial ++ power down depending on "previous" standard. ++ */ ++ ++ /* disable HW lock indicator */ ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ ++ /* Device is already at the required mode */ ++ if (state->m_OperationMode == oMode) ++ return 0; ++ ++ switch (state->m_OperationMode) { ++ /* OM_NONE was added for start up */ ++ case OM_NONE: ++ break; ++ case OM_DVBT: ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = PowerDownDVBT(state, true); ++ if (status < 0) ++ goto error; ++ state->m_OperationMode = OM_NONE; ++ break; ++ case OM_QAM_ITU_A: /* fallthrough */ ++ case OM_QAM_ITU_C: ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = PowerDownQAM(state); ++ if (status < 0) ++ goto error; ++ state->m_OperationMode = OM_NONE; ++ break; ++ case OM_QAM_ITU_B: ++ default: ++ status = -EINVAL; ++ goto error; ++ } ++ ++ /* ++ Power up new standard ++ */ ++ switch (oMode) { ++ case OM_DVBT: ++ dprintk(1, ": DVB-T\n"); ++ state->m_OperationMode = oMode; ++ status = SetDVBTStandard(state, oMode); ++ if (status < 0) ++ goto error; ++ break; ++ case OM_QAM_ITU_A: /* fallthrough */ ++ case OM_QAM_ITU_C: ++ dprintk(1, ": DVB-C Annex %c\n", ++ (state->m_OperationMode == OM_QAM_ITU_A) ? 'A' : 'C'); ++ state->m_OperationMode = oMode; ++ status = SetQAMStandard(state, oMode); ++ if (status < 0) ++ goto error; ++ break; ++ case OM_QAM_ITU_B: ++ default: ++ status = -EINVAL; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int Start(struct drxk_state *state, s32 offsetFreq, ++ s32 IntermediateFrequency) ++{ ++ int status = -EINVAL; ++ ++ u16 IFreqkHz; ++ s32 OffsetkHz = offsetFreq / 1000; ++ ++ dprintk(1, "\n"); ++ if (state->m_DrxkState != DRXK_STOPPED && ++ state->m_DrxkState != DRXK_DTV_STARTED) ++ goto error; ++ ++ state->m_bMirrorFreqSpect = (state->props.inversion == INVERSION_ON); ++ ++ if (IntermediateFrequency < 0) { ++ state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect; ++ IntermediateFrequency = -IntermediateFrequency; ++ } ++ ++ switch (state->m_OperationMode) { ++ case OM_QAM_ITU_A: ++ case OM_QAM_ITU_C: ++ IFreqkHz = (IntermediateFrequency / 1000); ++ status = SetQAM(state, IFreqkHz, OffsetkHz); ++ if (status < 0) ++ goto error; ++ state->m_DrxkState = DRXK_DTV_STARTED; ++ break; ++ case OM_DVBT: ++ IFreqkHz = (IntermediateFrequency / 1000); ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = SetDVBT(state, IFreqkHz, OffsetkHz); ++ if (status < 0) ++ goto error; ++ status = DVBTStart(state); ++ if (status < 0) ++ goto error; ++ state->m_DrxkState = DRXK_DTV_STARTED; ++ break; ++ default: ++ break; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int ShutDown(struct drxk_state *state) ++{ ++ dprintk(1, "\n"); ++ ++ MPEGTSStop(state); ++ return 0; ++} ++ ++static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus, ++ u32 Time) ++{ ++ int status = -EINVAL; ++ ++ dprintk(1, "\n"); ++ ++ if (pLockStatus == NULL) ++ goto error; ++ ++ *pLockStatus = NOT_LOCKED; ++ ++ /* define the SCU command code */ ++ switch (state->m_OperationMode) { ++ case OM_QAM_ITU_A: ++ case OM_QAM_ITU_B: ++ case OM_QAM_ITU_C: ++ status = GetQAMLockStatus(state, pLockStatus); ++ break; ++ case OM_DVBT: ++ status = GetDVBTLockStatus(state, pLockStatus); ++ break; ++ default: ++ break; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int MPEGTSStart(struct drxk_state *state) ++{ ++ int status; ++ ++ u16 fecOcSncMode = 0; ++ ++ /* Allow OC to sync again */ ++ status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode); ++ if (status < 0) ++ goto error; ++ fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M; ++ status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_SNC_UNLOCK__A, 1); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int MPEGTSDtoInit(struct drxk_state *state) ++{ ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ /* Rate integration settings */ ++ status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4); ++ if (status < 0) ++ goto error; ++ ++ /* Additional configuration */ ++ status = write16(state, FEC_OC_OCR_INVERT__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_SNC_LWM__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_SNC_HWM__A, 12); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int MPEGTSDtoSetup(struct drxk_state *state, ++ enum OperationMode oMode) ++{ ++ int status; ++ ++ u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */ ++ u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */ ++ u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */ ++ u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */ ++ u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */ ++ u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */ ++ u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */ ++ u16 fecOcTmdMode = 0; ++ u16 fecOcTmdIntUpdRate = 0; ++ u32 maxBitRate = 0; ++ bool staticCLK = false; ++ ++ dprintk(1, "\n"); ++ ++ /* Check insertion of the Reed-Solomon parity bytes */ ++ status = read16(state, FEC_OC_MODE__A, &fecOcRegMode); ++ if (status < 0) ++ goto error; ++ status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode); ++ if (status < 0) ++ goto error; ++ fecOcRegMode &= (~FEC_OC_MODE_PARITY__M); ++ fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M); ++ if (state->m_insertRSByte == true) { ++ /* enable parity symbol forward */ ++ fecOcRegMode |= FEC_OC_MODE_PARITY__M; ++ /* MVAL disable during parity bytes */ ++ fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M; ++ /* TS burst length to 204 */ ++ fecOcDtoBurstLen = 204; ++ } ++ ++ /* Check serial or parrallel output */ ++ fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); ++ if (state->m_enableParallel == false) { ++ /* MPEG data output is serial -> set ipr_mode[0] */ ++ fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M; ++ } ++ ++ switch (oMode) { ++ case OM_DVBT: ++ maxBitRate = state->m_DVBTBitrate; ++ fecOcTmdMode = 3; ++ fecOcRcnCtlRate = 0xC00000; ++ staticCLK = state->m_DVBTStaticCLK; ++ break; ++ case OM_QAM_ITU_A: /* fallthrough */ ++ case OM_QAM_ITU_C: ++ fecOcTmdMode = 0x0004; ++ fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */ ++ maxBitRate = state->m_DVBCBitrate; ++ staticCLK = state->m_DVBCStaticCLK; ++ break; ++ default: ++ status = -EINVAL; ++ } /* switch (standard) */ ++ if (status < 0) ++ goto error; ++ ++ /* Configure DTO's */ ++ if (staticCLK) { ++ u32 bitRate = 0; ++ ++ /* Rational DTO for MCLK source (static MCLK rate), ++ Dynamic DTO for optimal grouping ++ (avoid intra-packet gaps), ++ DTO offset enable to sync TS burst with MSTRT */ ++ fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M | ++ FEC_OC_DTO_MODE_OFFSET_ENABLE__M); ++ fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M | ++ FEC_OC_FCT_MODE_VIRT_ENA__M); ++ ++ /* Check user defined bitrate */ ++ bitRate = maxBitRate; ++ if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */ ++ bitRate = 75900000UL; ++ } ++ /* Rational DTO period: ++ dto_period = (Fsys / bitrate) - 2 ++ ++ Result should be floored, ++ to make sure >= requested bitrate ++ */ ++ fecOcDtoPeriod = (u16) (((state->m_sysClockFreq) ++ * 1000) / bitRate); ++ if (fecOcDtoPeriod <= 2) ++ fecOcDtoPeriod = 0; ++ else ++ fecOcDtoPeriod -= 2; ++ fecOcTmdIntUpdRate = 8; ++ } else { ++ /* (commonAttr->staticCLK == false) => dynamic mode */ ++ fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M; ++ fecOcFctMode = FEC_OC_FCT_MODE__PRE; ++ fecOcTmdIntUpdRate = 5; ++ } ++ ++ /* Write appropriate registers with requested configuration */ ++ status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_MODE__A, fecOcRegMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode); ++ if (status < 0) ++ goto error; ++ ++ /* Rate integration settings */ ++ status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int MPEGTSConfigurePolarity(struct drxk_state *state) ++{ ++ u16 fecOcRegIprInvert = 0; ++ ++ /* Data mask for the output data byte */ ++ u16 InvertDataMask = ++ FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M | ++ FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M | ++ FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M | ++ FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M; ++ ++ dprintk(1, "\n"); ++ ++ /* Control selective inversion of output bits */ ++ fecOcRegIprInvert &= (~(InvertDataMask)); ++ if (state->m_invertDATA == true) ++ fecOcRegIprInvert |= InvertDataMask; ++ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M)); ++ if (state->m_invertERR == true) ++ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M; ++ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M)); ++ if (state->m_invertSTR == true) ++ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M; ++ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M)); ++ if (state->m_invertVAL == true) ++ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M; ++ fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M)); ++ if (state->m_invertCLK == true) ++ fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M; ++ ++ return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert); ++} ++ ++#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000 ++ ++static int SetAgcRf(struct drxk_state *state, ++ struct SCfgAgc *pAgcCfg, bool isDTV) ++{ ++ int status = -EINVAL; ++ u16 data = 0; ++ struct SCfgAgc *pIfAgcSettings; ++ ++ dprintk(1, "\n"); ++ ++ if (pAgcCfg == NULL) ++ goto error; ++ ++ switch (pAgcCfg->ctrlMode) { ++ case DRXK_AGC_CTRL_AUTO: ++ /* Enable RF AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ ++ /* Enable SCU RF AGC loop */ ++ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; ++ ++ /* Polarity */ ++ if (state->m_RfAgcPol) ++ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; ++ else ++ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Set speed (using complementary reduction value) */ ++ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); ++ if (status < 0) ++ goto error; ++ ++ data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M; ++ data |= (~(pAgcCfg->speed << ++ SCU_RAM_AGC_KI_RED_RAGC_RED__B) ++ & SCU_RAM_AGC_KI_RED_RAGC_RED__M); ++ ++ status = write16(state, SCU_RAM_AGC_KI_RED__A, data); ++ if (status < 0) ++ goto error; ++ ++ if (IsDVBT(state)) ++ pIfAgcSettings = &state->m_dvbtIfAgcCfg; ++ else if (IsQAM(state)) ++ pIfAgcSettings = &state->m_qamIfAgcCfg; ++ else ++ pIfAgcSettings = &state->m_atvIfAgcCfg; ++ if (pIfAgcSettings == NULL) { ++ status = -EINVAL; ++ goto error; ++ } ++ ++ /* Set TOP, only if IF-AGC is in AUTO mode */ ++ if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO) ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top); ++ if (status < 0) ++ goto error; ++ ++ /* Cut-Off current */ ++ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent); ++ if (status < 0) ++ goto error; ++ ++ /* Max. output level */ ++ status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel); ++ if (status < 0) ++ goto error; ++ ++ break; ++ ++ case DRXK_AGC_CTRL_USER: ++ /* Enable RF AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Disable SCU RF AGC loop */ ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; ++ if (state->m_RfAgcPol) ++ data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; ++ else ++ data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* SCU c.o.c. to 0, enabling full control range */ ++ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0); ++ if (status < 0) ++ goto error; ++ ++ /* Write value to output pin */ ++ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel); ++ if (status < 0) ++ goto error; ++ break; ++ ++ case DRXK_AGC_CTRL_OFF: ++ /* Disable RF AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Disable SCU RF AGC loop */ ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ break; ++ ++ default: ++ status = -EINVAL; ++ ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000 ++ ++static int SetAgcIf(struct drxk_state *state, ++ struct SCfgAgc *pAgcCfg, bool isDTV) ++{ ++ u16 data = 0; ++ int status = 0; ++ struct SCfgAgc *pRfAgcSettings; ++ ++ dprintk(1, "\n"); ++ ++ switch (pAgcCfg->ctrlMode) { ++ case DRXK_AGC_CTRL_AUTO: ++ ++ /* Enable IF AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ ++ /* Enable SCU IF AGC loop */ ++ data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; ++ ++ /* Polarity */ ++ if (state->m_IfAgcPol) ++ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; ++ else ++ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Set speed (using complementary reduction value) */ ++ status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); ++ if (status < 0) ++ goto error; ++ data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M; ++ data |= (~(pAgcCfg->speed << ++ SCU_RAM_AGC_KI_RED_IAGC_RED__B) ++ & SCU_RAM_AGC_KI_RED_IAGC_RED__M); ++ ++ status = write16(state, SCU_RAM_AGC_KI_RED__A, data); ++ if (status < 0) ++ goto error; ++ ++ if (IsQAM(state)) ++ pRfAgcSettings = &state->m_qamRfAgcCfg; ++ else ++ pRfAgcSettings = &state->m_atvRfAgcCfg; ++ if (pRfAgcSettings == NULL) ++ return -1; ++ /* Restore TOP */ ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top); ++ if (status < 0) ++ goto error; ++ break; ++ ++ case DRXK_AGC_CTRL_USER: ++ ++ /* Enable IF AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ ++ /* Disable SCU IF AGC loop */ ++ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; ++ ++ /* Polarity */ ++ if (state->m_IfAgcPol) ++ data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; ++ else ++ data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Write value to output pin */ ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel); ++ if (status < 0) ++ goto error; ++ break; ++ ++ case DRXK_AGC_CTRL_OFF: ++ ++ /* Disable If AGC DAC */ ++ status = read16(state, IQM_AF_STDBY__A, &data); ++ if (status < 0) ++ goto error; ++ data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; ++ status = write16(state, IQM_AF_STDBY__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Disable SCU IF AGC loop */ ++ status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; ++ status = write16(state, SCU_RAM_AGC_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ break; ++ } /* switch (agcSettingsIf->ctrlMode) */ ++ ++ /* always set the top to support ++ configurations without if-loop */ ++ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int ReadIFAgc(struct drxk_state *state, u32 *pValue) ++{ ++ u16 agcDacLvl; ++ int status; ++ u16 Level = 0; ++ ++ dprintk(1, "\n"); ++ ++ status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl); ++ if (status < 0) { ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++ } ++ ++ *pValue = 0; ++ ++ if (agcDacLvl > DRXK_AGC_DAC_OFFSET) ++ Level = agcDacLvl - DRXK_AGC_DAC_OFFSET; ++ if (Level < 14000) ++ *pValue = (14000 - Level) / 4; ++ else ++ *pValue = 0; ++ ++ return status; ++} ++ ++static int GetQAMSignalToNoise(struct drxk_state *state, ++ s32 *pSignalToNoise) ++{ ++ int status = 0; ++ u16 qamSlErrPower = 0; /* accum. error between ++ raw and sliced symbols */ ++ u32 qamSlSigPower = 0; /* used for MER, depends of ++ QAM modulation */ ++ u32 qamSlMer = 0; /* QAM MER */ ++ ++ dprintk(1, "\n"); ++ ++ /* MER calculation */ ++ ++ /* get the register value needed for MER */ ++ status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower); ++ if (status < 0) { ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return -EINVAL; ++ } ++ ++ switch (state->props.modulation) { ++ case QAM_16: ++ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2; ++ break; ++ case QAM_32: ++ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2; ++ break; ++ case QAM_64: ++ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2; ++ break; ++ case QAM_128: ++ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2; ++ break; ++ default: ++ case QAM_256: ++ qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2; ++ break; ++ } ++ ++ if (qamSlErrPower > 0) { ++ qamSlMer = Log10Times100(qamSlSigPower) - ++ Log10Times100((u32) qamSlErrPower); ++ } ++ *pSignalToNoise = qamSlMer; ++ ++ return status; ++} ++ ++static int GetDVBTSignalToNoise(struct drxk_state *state, ++ s32 *pSignalToNoise) ++{ ++ int status; ++ u16 regData = 0; ++ u32 EqRegTdSqrErrI = 0; ++ u32 EqRegTdSqrErrQ = 0; ++ u16 EqRegTdSqrErrExp = 0; ++ u16 EqRegTdTpsPwrOfs = 0; ++ u16 EqRegTdReqSmbCnt = 0; ++ u32 tpsCnt = 0; ++ u32 SqrErrIQ = 0; ++ u32 a = 0; ++ u32 b = 0; ++ u32 c = 0; ++ u32 iMER = 0; ++ u16 transmissionParams = 0; ++ ++ dprintk(1, "\n"); ++ ++ status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs); ++ if (status < 0) ++ goto error; ++ status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt); ++ if (status < 0) ++ goto error; ++ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp); ++ if (status < 0) ++ goto error; ++ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, ®Data); ++ if (status < 0) ++ goto error; ++ /* Extend SQR_ERR_I operational range */ ++ EqRegTdSqrErrI = (u32) regData; ++ if ((EqRegTdSqrErrExp > 11) && ++ (EqRegTdSqrErrI < 0x00000FFFUL)) { ++ EqRegTdSqrErrI += 0x00010000UL; ++ } ++ status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, ®Data); ++ if (status < 0) ++ goto error; ++ /* Extend SQR_ERR_Q operational range */ ++ EqRegTdSqrErrQ = (u32) regData; ++ if ((EqRegTdSqrErrExp > 11) && ++ (EqRegTdSqrErrQ < 0x00000FFFUL)) ++ EqRegTdSqrErrQ += 0x00010000UL; ++ ++ status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams); ++ if (status < 0) ++ goto error; ++ ++ /* Check input data for MER */ ++ ++ /* MER calculation (in 0.1 dB) without math.h */ ++ if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0)) ++ iMER = 0; ++ else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) { ++ /* No error at all, this must be the HW reset value ++ * Apparently no first measurement yet ++ * Set MER to 0.0 */ ++ iMER = 0; ++ } else { ++ SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) << ++ EqRegTdSqrErrExp; ++ if ((transmissionParams & ++ OFDM_SC_RA_RAM_OP_PARAM_MODE__M) ++ == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K) ++ tpsCnt = 17; ++ else ++ tpsCnt = 68; ++ ++ /* IMER = 100 * log10 (x) ++ where x = (EqRegTdTpsPwrOfs^2 * ++ EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ ++ ++ => IMER = a + b -c ++ where a = 100 * log10 (EqRegTdTpsPwrOfs^2) ++ b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt) ++ c = 100 * log10 (SqrErrIQ) ++ */ ++ ++ /* log(x) x = 9bits * 9bits->18 bits */ ++ a = Log10Times100(EqRegTdTpsPwrOfs * ++ EqRegTdTpsPwrOfs); ++ /* log(x) x = 16bits * 7bits->23 bits */ ++ b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt); ++ /* log(x) x = (16bits + 16bits) << 15 ->32 bits */ ++ c = Log10Times100(SqrErrIQ); ++ ++ iMER = a + b; ++ /* No negative MER, clip to zero */ ++ if (iMER > c) ++ iMER -= c; ++ else ++ iMER = 0; ++ } ++ *pSignalToNoise = iMER; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise) ++{ ++ dprintk(1, "\n"); ++ ++ *pSignalToNoise = 0; ++ switch (state->m_OperationMode) { ++ case OM_DVBT: ++ return GetDVBTSignalToNoise(state, pSignalToNoise); ++ case OM_QAM_ITU_A: ++ case OM_QAM_ITU_C: ++ return GetQAMSignalToNoise(state, pSignalToNoise); ++ default: ++ break; ++ } ++ return 0; ++} ++ ++#if 0 ++static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality) ++{ ++ /* SNR Values for quasi errorfree reception rom Nordig 2.2 */ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ ++ static s32 QE_SN[] = { ++ 51, /* QPSK 1/2 */ ++ 69, /* QPSK 2/3 */ ++ 79, /* QPSK 3/4 */ ++ 89, /* QPSK 5/6 */ ++ 97, /* QPSK 7/8 */ ++ 108, /* 16-QAM 1/2 */ ++ 131, /* 16-QAM 2/3 */ ++ 146, /* 16-QAM 3/4 */ ++ 156, /* 16-QAM 5/6 */ ++ 160, /* 16-QAM 7/8 */ ++ 165, /* 64-QAM 1/2 */ ++ 187, /* 64-QAM 2/3 */ ++ 202, /* 64-QAM 3/4 */ ++ 216, /* 64-QAM 5/6 */ ++ 225, /* 64-QAM 7/8 */ ++ }; ++ ++ *pQuality = 0; ++ ++ do { ++ s32 SignalToNoise = 0; ++ u16 Constellation = 0; ++ u16 CodeRate = 0; ++ u32 SignalToNoiseRel; ++ u32 BERQuality; ++ ++ status = GetDVBTSignalToNoise(state, &SignalToNoise); ++ if (status < 0) ++ break; ++ status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation); ++ if (status < 0) ++ break; ++ Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M; ++ ++ status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate); ++ if (status < 0) ++ break; ++ CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M; ++ ++ if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM || ++ CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8) ++ break; ++ SignalToNoiseRel = SignalToNoise - ++ QE_SN[Constellation * 5 + CodeRate]; ++ BERQuality = 100; ++ ++ if (SignalToNoiseRel < -70) ++ *pQuality = 0; ++ else if (SignalToNoiseRel < 30) ++ *pQuality = ((SignalToNoiseRel + 70) * ++ BERQuality) / 100; ++ else ++ *pQuality = BERQuality; ++ } while (0); ++ return 0; ++}; ++ ++static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality) ++{ ++ int status = 0; ++ *pQuality = 0; ++ ++ dprintk(1, "\n"); ++ ++ do { ++ u32 SignalToNoise = 0; ++ u32 BERQuality = 100; ++ u32 SignalToNoiseRel = 0; ++ ++ status = GetQAMSignalToNoise(state, &SignalToNoise); ++ if (status < 0) ++ break; ++ ++ switch (state->props.modulation) { ++ case QAM_16: ++ SignalToNoiseRel = SignalToNoise - 200; ++ break; ++ case QAM_32: ++ SignalToNoiseRel = SignalToNoise - 230; ++ break; /* Not in NorDig */ ++ case QAM_64: ++ SignalToNoiseRel = SignalToNoise - 260; ++ break; ++ case QAM_128: ++ SignalToNoiseRel = SignalToNoise - 290; ++ break; ++ default: ++ case QAM_256: ++ SignalToNoiseRel = SignalToNoise - 320; ++ break; ++ } ++ ++ if (SignalToNoiseRel < -70) ++ *pQuality = 0; ++ else if (SignalToNoiseRel < 30) ++ *pQuality = ((SignalToNoiseRel + 70) * ++ BERQuality) / 100; ++ else ++ *pQuality = BERQuality; ++ } while (0); ++ ++ return status; ++} ++ ++static int GetQuality(struct drxk_state *state, s32 *pQuality) ++{ ++ dprintk(1, "\n"); ++ ++ switch (state->m_OperationMode) { ++ case OM_DVBT: ++ return GetDVBTQuality(state, pQuality); ++ case OM_QAM_ITU_A: ++ return GetDVBCQuality(state, pQuality); ++ default: ++ break; ++ } ++ ++ return 0; ++} ++#endif ++ ++/* Free data ram in SIO HI */ ++#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040 ++#define SIO_HI_RA_RAM_USR_END__A 0x420060 ++ ++#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A) ++#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7) ++#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ ++#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE ++ ++#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F) ++#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F) ++#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF) ++ ++static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge) ++{ ++ int status = -EINVAL; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return 0; ++ if (state->m_DrxkState == DRXK_POWERED_DOWN) ++ goto error; ++ ++ if (state->no_i2c_bridge) ++ return 0; ++ ++ status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); ++ if (status < 0) ++ goto error; ++ if (bEnableBridge) { ++ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED); ++ if (status < 0) ++ goto error; ++ } else { ++ status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN); ++ if (status < 0) ++ goto error; ++ } ++ ++ status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SetPreSaw(struct drxk_state *state, ++ struct SCfgPreSaw *pPreSawCfg) ++{ ++ int status = -EINVAL; ++ ++ dprintk(1, "\n"); ++ ++ if ((pPreSawCfg == NULL) ++ || (pPreSawCfg->reference > IQM_AF_PDREF__M)) ++ goto error; ++ ++ status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int BLDirectCmd(struct drxk_state *state, u32 targetAddr, ++ u16 romOffset, u16 nrOfElements, u32 timeOut) ++{ ++ u16 blStatus = 0; ++ u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF); ++ u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF); ++ int status; ++ unsigned long end; ++ ++ dprintk(1, "\n"); ++ ++ mutex_lock(&state->mutex); ++ status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_TGT_HDR__A, blockbank); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_TGT_ADDR__A, offset); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_SRC_ADDR__A, romOffset); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); ++ if (status < 0) ++ goto error; ++ ++ end = jiffies + msecs_to_jiffies(timeOut); ++ do { ++ status = read16(state, SIO_BL_STATUS__A, &blStatus); ++ if (status < 0) ++ goto error; ++ } while ((blStatus == 0x1) && time_is_after_jiffies(end)); ++ if (blStatus == 0x1) { ++ printk(KERN_ERR "drxk: SIO not ready\n"); ++ status = -EINVAL; ++ goto error2; ++ } ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++error2: ++ mutex_unlock(&state->mutex); ++ return status; ++ ++} ++ ++static int ADCSyncMeasurement(struct drxk_state *state, u16 *count) ++{ ++ u16 data = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ /* Start measurement */ ++ status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_START_LOCK__A, 1); ++ if (status < 0) ++ goto error; ++ ++ *count = 0; ++ status = read16(state, IQM_AF_PHASE0__A, &data); ++ if (status < 0) ++ goto error; ++ if (data == 127) ++ *count = *count + 1; ++ status = read16(state, IQM_AF_PHASE1__A, &data); ++ if (status < 0) ++ goto error; ++ if (data == 127) ++ *count = *count + 1; ++ status = read16(state, IQM_AF_PHASE2__A, &data); ++ if (status < 0) ++ goto error; ++ if (data == 127) ++ *count = *count + 1; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int ADCSynchronization(struct drxk_state *state) ++{ ++ u16 count = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ status = ADCSyncMeasurement(state, &count); ++ if (status < 0) ++ goto error; ++ ++ if (count == 1) { ++ /* Try sampling on a diffrent edge */ ++ u16 clkNeg = 0; ++ ++ status = read16(state, IQM_AF_CLKNEG__A, &clkNeg); ++ if (status < 0) ++ goto error; ++ if ((clkNeg & IQM_AF_CLKNEG_CLKNEGDATA__M) == ++ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) { ++ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); ++ clkNeg |= ++ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG; ++ } else { ++ clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); ++ clkNeg |= ++ IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS; ++ } ++ status = write16(state, IQM_AF_CLKNEG__A, clkNeg); ++ if (status < 0) ++ goto error; ++ status = ADCSyncMeasurement(state, &count); ++ if (status < 0) ++ goto error; ++ } ++ ++ if (count < 2) ++ status = -EINVAL; ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SetFrequencyShifter(struct drxk_state *state, ++ u16 intermediateFreqkHz, ++ s32 tunerFreqOffset, bool isDTV) ++{ ++ bool selectPosImage = false; ++ u32 rfFreqResidual = tunerFreqOffset; ++ u32 fmFrequencyShift = 0; ++ bool tunerMirror = !state->m_bMirrorFreqSpect; ++ u32 adcFreq; ++ bool adcFlip; ++ int status; ++ u32 ifFreqActual; ++ u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3); ++ u32 frequencyShift; ++ bool imageToSelect; ++ ++ dprintk(1, "\n"); ++ ++ /* ++ Program frequency shifter ++ No need to account for mirroring on RF ++ */ ++ if (isDTV) { ++ if ((state->m_OperationMode == OM_QAM_ITU_A) || ++ (state->m_OperationMode == OM_QAM_ITU_C) || ++ (state->m_OperationMode == OM_DVBT)) ++ selectPosImage = true; ++ else ++ selectPosImage = false; ++ } ++ if (tunerMirror) ++ /* tuner doesn't mirror */ ++ ifFreqActual = intermediateFreqkHz + ++ rfFreqResidual + fmFrequencyShift; ++ else ++ /* tuner mirrors */ ++ ifFreqActual = intermediateFreqkHz - ++ rfFreqResidual - fmFrequencyShift; ++ if (ifFreqActual > samplingFrequency / 2) { ++ /* adc mirrors */ ++ adcFreq = samplingFrequency - ifFreqActual; ++ adcFlip = true; ++ } else { ++ /* adc doesn't mirror */ ++ adcFreq = ifFreqActual; ++ adcFlip = false; ++ } ++ ++ frequencyShift = adcFreq; ++ imageToSelect = state->m_rfmirror ^ tunerMirror ^ ++ adcFlip ^ selectPosImage; ++ state->m_IqmFsRateOfs = ++ Frac28a((frequencyShift), samplingFrequency); ++ ++ if (imageToSelect) ++ state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1; ++ ++ /* Program frequency shifter with tuner offset compensation */ ++ /* frequencyShift += tunerFreqOffset; TODO */ ++ status = write32(state, IQM_FS_RATE_OFS_LO__A, ++ state->m_IqmFsRateOfs); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int InitAGC(struct drxk_state *state, bool isDTV) ++{ ++ u16 ingainTgt = 0; ++ u16 ingainTgtMin = 0; ++ u16 ingainTgtMax = 0; ++ u16 clpCyclen = 0; ++ u16 clpSumMin = 0; ++ u16 clpDirTo = 0; ++ u16 snsSumMin = 0; ++ u16 snsSumMax = 0; ++ u16 clpSumMax = 0; ++ u16 snsDirTo = 0; ++ u16 kiInnergainMin = 0; ++ u16 ifIaccuHiTgt = 0; ++ u16 ifIaccuHiTgtMin = 0; ++ u16 ifIaccuHiTgtMax = 0; ++ u16 data = 0; ++ u16 fastClpCtrlDelay = 0; ++ u16 clpCtrlMode = 0; ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ ++ /* Common settings */ ++ snsSumMax = 1023; ++ ifIaccuHiTgtMin = 2047; ++ clpCyclen = 500; ++ clpSumMax = 1023; ++ ++ /* AGCInit() not available for DVBT; init done in microcode */ ++ if (!IsQAM(state)) { ++ printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode); ++ return -EINVAL; ++ } ++ ++ /* FIXME: Analog TV AGC require different settings */ ++ ++ /* Standard specific settings */ ++ clpSumMin = 8; ++ clpDirTo = (u16) -9; ++ clpCtrlMode = 0; ++ snsSumMin = 8; ++ snsDirTo = (u16) -9; ++ kiInnergainMin = (u16) -1030; ++ ifIaccuHiTgtMax = 0x2380; ++ ifIaccuHiTgt = 0x2380; ++ ingainTgtMin = 0x0511; ++ ingainTgt = 0x0511; ++ ingainTgtMax = 5119; ++ fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay; ++ ++ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500); ++ if (status < 0) ++ goto error; ++ ++ /* Initialize inner-loop KI gain factors */ ++ status = read16(state, SCU_RAM_AGC_KI__A, &data); ++ if (status < 0) ++ goto error; ++ ++ data = 0x0657; ++ data &= ~SCU_RAM_AGC_KI_RF__M; ++ data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B); ++ data &= ~SCU_RAM_AGC_KI_IF__M; ++ data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B); ++ ++ status = write16(state, SCU_RAM_AGC_KI__A, data); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr) ++{ ++ int status; ++ ++ dprintk(1, "\n"); ++ if (packetErr == NULL) ++ status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0); ++ else ++ status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int DVBTScCommand(struct drxk_state *state, ++ u16 cmd, u16 subcmd, ++ u16 param0, u16 param1, u16 param2, ++ u16 param3, u16 param4) ++{ ++ u16 curCmd = 0; ++ u16 errCode = 0; ++ u16 retryCnt = 0; ++ u16 scExec = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec); ++ if (scExec != 1) { ++ /* SC is not running */ ++ status = -EINVAL; ++ } ++ if (status < 0) ++ goto error; ++ ++ /* Wait until sc is ready to receive command */ ++ retryCnt = 0; ++ do { ++ msleep(1); ++ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd); ++ retryCnt++; ++ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES)); ++ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0)) ++ goto error; ++ ++ /* Write sub-command */ ++ switch (cmd) { ++ /* All commands using sub-cmd */ ++ case OFDM_SC_RA_RAM_CMD_PROC_START: ++ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: ++ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: ++ status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd); ++ if (status < 0) ++ goto error; ++ break; ++ default: ++ /* Do nothing */ ++ break; ++ } ++ ++ /* Write needed parameters and the command */ ++ switch (cmd) { ++ /* All commands using 5 parameters */ ++ /* All commands using 4 parameters */ ++ /* All commands using 3 parameters */ ++ /* All commands using 2 parameters */ ++ case OFDM_SC_RA_RAM_CMD_PROC_START: ++ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: ++ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: ++ status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); ++ /* All commands using 1 parameters */ ++ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: ++ case OFDM_SC_RA_RAM_CMD_USER_IO: ++ status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); ++ /* All commands using 0 parameters */ ++ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: ++ case OFDM_SC_RA_RAM_CMD_NULL: ++ /* Write command */ ++ status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd); ++ break; ++ default: ++ /* Unknown command */ ++ status = -EINVAL; ++ } ++ if (status < 0) ++ goto error; ++ ++ /* Wait until sc is ready processing command */ ++ retryCnt = 0; ++ do { ++ msleep(1); ++ status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd); ++ retryCnt++; ++ } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES)); ++ if (retryCnt >= DRXK_MAX_RETRIES && (status < 0)) ++ goto error; ++ ++ /* Check for illegal cmd */ ++ status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode); ++ if (errCode == 0xFFFF) { ++ /* illegal command */ ++ status = -EINVAL; ++ } ++ if (status < 0) ++ goto error; ++ ++ /* Retreive results parameters from SC */ ++ switch (cmd) { ++ /* All commands yielding 5 results */ ++ /* All commands yielding 4 results */ ++ /* All commands yielding 3 results */ ++ /* All commands yielding 2 results */ ++ /* All commands yielding 1 result */ ++ case OFDM_SC_RA_RAM_CMD_USER_IO: ++ case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: ++ status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0)); ++ /* All commands yielding 0 results */ ++ case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: ++ case OFDM_SC_RA_RAM_CMD_SET_TIMER: ++ case OFDM_SC_RA_RAM_CMD_PROC_START: ++ case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: ++ case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: ++ case OFDM_SC_RA_RAM_CMD_NULL: ++ break; ++ default: ++ /* Unknown command */ ++ status = -EINVAL; ++ break; ++ } /* switch (cmd->cmd) */ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int PowerUpDVBT(struct drxk_state *state) ++{ ++ enum DRXPowerMode powerMode = DRX_POWER_UP; ++ int status; ++ ++ dprintk(1, "\n"); ++ status = CtrlPowerMode(state, &powerMode); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled) ++{ ++ int status; ++ ++ dprintk(1, "\n"); ++ if (*enabled == true) ++ status = write16(state, IQM_CF_BYPASSDET__A, 0); ++ else ++ status = write16(state, IQM_CF_BYPASSDET__A, 1); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++#define DEFAULT_FR_THRES_8K 4000 ++static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled) ++{ ++ ++ int status; ++ ++ dprintk(1, "\n"); ++ if (*enabled == true) { ++ /* write mask to 1 */ ++ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, ++ DEFAULT_FR_THRES_8K); ++ } else { ++ /* write mask to 0 */ ++ status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0); ++ } ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int DVBTCtrlSetEchoThreshold(struct drxk_state *state, ++ struct DRXKCfgDvbtEchoThres_t *echoThres) ++{ ++ u16 data = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data); ++ if (status < 0) ++ goto error; ++ ++ switch (echoThres->fftMode) { ++ case DRX_FFTMODE_2K: ++ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M; ++ data |= ((echoThres->threshold << ++ OFDM_SC_RA_RAM_ECHO_THRES_2K__B) ++ & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M)); ++ break; ++ case DRX_FFTMODE_8K: ++ data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M; ++ data |= ((echoThres->threshold << ++ OFDM_SC_RA_RAM_ECHO_THRES_8K__B) ++ & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int DVBTCtrlSetSqiSpeed(struct drxk_state *state, ++ enum DRXKCfgDvbtSqiSpeed *speed) ++{ ++ int status = -EINVAL; ++ ++ dprintk(1, "\n"); ++ ++ switch (*speed) { ++ case DRXK_DVBT_SQI_SPEED_FAST: ++ case DRXK_DVBT_SQI_SPEED_MEDIUM: ++ case DRXK_DVBT_SQI_SPEED_SLOW: ++ break; ++ default: ++ goto error; ++ } ++ status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A, ++ (u16) *speed); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief Activate DVBT specific presets ++* \param demod instance of demodulator. ++* \return DRXStatus_t. ++* ++* Called in DVBTSetStandard ++* ++*/ ++static int DVBTActivatePresets(struct drxk_state *state) ++{ ++ int status; ++ bool setincenable = false; ++ bool setfrenable = true; ++ ++ struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K }; ++ struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K }; ++ ++ dprintk(1, "\n"); ++ status = DVBTCtrlSetIncEnable(state, &setincenable); ++ if (status < 0) ++ goto error; ++ status = DVBTCtrlSetFrEnable(state, &setfrenable); ++ if (status < 0) ++ goto error; ++ status = DVBTCtrlSetEchoThreshold(state, &echoThres2k); ++ if (status < 0) ++ goto error; ++ status = DVBTCtrlSetEchoThreshold(state, &echoThres8k); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief Initialize channelswitch-independent settings for DVBT. ++* \param demod instance of demodulator. ++* \return DRXStatus_t. ++* ++* For ROM code channel filter taps are loaded from the bootloader. For microcode ++* the DVB-T taps from the drxk_filters.h are used. ++*/ ++static int SetDVBTStandard(struct drxk_state *state, ++ enum OperationMode oMode) ++{ ++ u16 cmdResult = 0; ++ u16 data = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ ++ PowerUpDVBT(state); ++ /* added antenna switch */ ++ SwitchAntennaToDVBT(state); ++ /* send OFDM reset command */ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ /* send OFDM setenv command */ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ /* reset datapath for OFDM, processors first */ ++ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); ++ if (status < 0) ++ goto error; ++ ++ /* IQM setup */ ++ /* synchronize on ofdstate->m_festart */ ++ status = write16(state, IQM_AF_UPD_SEL__A, 1); ++ if (status < 0) ++ goto error; ++ /* window size for clipping ADC detection */ ++ status = write16(state, IQM_AF_CLP_LEN__A, 0); ++ if (status < 0) ++ goto error; ++ /* window size for for sense pre-SAW detection */ ++ status = write16(state, IQM_AF_SNS_LEN__A, 0); ++ if (status < 0) ++ goto error; ++ /* sense threshold for sense pre-SAW detection */ ++ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); ++ if (status < 0) ++ goto error; ++ status = SetIqmAf(state, true); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_AF_AGC_RF__A, 0); ++ if (status < 0) ++ goto error; ++ ++ /* Impulse noise cruncher setup */ ++ status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */ ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */ ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */ ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_RC_STRETCH__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */ ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */ ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_SCALE__A, 1600); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_SCALE_SH__A, 0); ++ if (status < 0) ++ goto error; ++ ++ /* virtual clipping threshold for clipping ADC detection */ ++ status = write16(state, IQM_AF_CLP_TH__A, 448); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */ ++ if (status < 0) ++ goto error; ++ ++ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */ ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2); ++ if (status < 0) ++ goto error; ++ /* enable power measurement interrupt */ ++ status = write16(state, IQM_CF_COMM_INT_MSK__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); ++ if (status < 0) ++ goto error; ++ ++ /* IQM will not be reset from here, sync ADC and update/init AGC */ ++ status = ADCSynchronization(state); ++ if (status < 0) ++ goto error; ++ status = SetPreSaw(state, &state->m_dvbtPreSawCfg); ++ if (status < 0) ++ goto error; ++ ++ /* Halt SCU to enable safe non-atomic accesses */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); ++ if (status < 0) ++ goto error; ++ ++ status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true); ++ if (status < 0) ++ goto error; ++ status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true); ++ if (status < 0) ++ goto error; ++ ++ /* Set Noise Estimation notch width and enable DC fix */ ++ status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data); ++ if (status < 0) ++ goto error; ++ data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M; ++ status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data); ++ if (status < 0) ++ goto error; ++ ++ /* Activate SCU to enable SCU commands */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ ++ if (!state->m_DRXK_A3_ROM_CODE) { ++ /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */ ++ status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay); ++ if (status < 0) ++ goto error; ++ } ++ ++ /* OFDM_SC setup */ ++#ifdef COMPILE_FOR_NONRT ++ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2); ++ if (status < 0) ++ goto error; ++#endif ++ ++ /* FEC setup */ ++ status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */ ++ if (status < 0) ++ goto error; ++ ++ ++#ifdef COMPILE_FOR_NONRT ++ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400); ++ if (status < 0) ++ goto error; ++#else ++ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000); ++ if (status < 0) ++ goto error; ++#endif ++ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001); ++ if (status < 0) ++ goto error; ++ ++ /* Setup MPEG bus */ ++ status = MPEGTSDtoSetup(state, OM_DVBT); ++ if (status < 0) ++ goto error; ++ /* Set DVBT Presets */ ++ status = DVBTActivatePresets(state); ++ if (status < 0) ++ goto error; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++/** ++* \brief Start dvbt demodulating for channel. ++* \param demod instance of demodulator. ++* \return DRXStatus_t. ++*/ ++static int DVBTStart(struct drxk_state *state) ++{ ++ u16 param1; ++ int status; ++ /* DRXKOfdmScCmd_t scCmd; */ ++ ++ dprintk(1, "\n"); ++ /* Start correct processes to get in lock */ ++ /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */ ++ param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN; ++ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0); ++ if (status < 0) ++ goto error; ++ /* Start FEC OC */ ++ status = MPEGTSStart(state); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++ ++/*============================================================================*/ ++ ++/** ++* \brief Set up dvbt demodulator for channel. ++* \param demod instance of demodulator. ++* \return DRXStatus_t. ++* // original DVBTSetChannel() ++*/ ++static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz, ++ s32 tunerFreqOffset) ++{ ++ u16 cmdResult = 0; ++ u16 transmissionParams = 0; ++ u16 operationMode = 0; ++ u32 iqmRcRateOfs = 0; ++ u32 bandwidth = 0; ++ u16 param1; ++ int status; ++ ++ dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset); ++ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ /* Halt SCU to enable safe non-atomic accesses */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); ++ if (status < 0) ++ goto error; ++ ++ /* Stop processors */ ++ status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ ++ /* Mandatory fix, always stop CP, required to set spl offset back to ++ hardware default (is set to 0 by ucode during pilot detection */ ++ status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ ++ /*== Write channel settings to device =====================================*/ ++ ++ /* mode */ ++ switch (state->props.transmission_mode) { ++ case TRANSMISSION_MODE_AUTO: ++ default: ++ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M; ++ /* fall through , try first guess DRX_FFTMODE_8K */ ++ case TRANSMISSION_MODE_8K: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; ++ break; ++ case TRANSMISSION_MODE_2K: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K; ++ break; ++ } ++ ++ /* guard */ ++ switch (state->props.guard_interval) { ++ default: ++ case GUARD_INTERVAL_AUTO: ++ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M; ++ /* fall through , try first guess DRX_GUARD_1DIV4 */ ++ case GUARD_INTERVAL_1_4: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; ++ break; ++ case GUARD_INTERVAL_1_32: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32; ++ break; ++ case GUARD_INTERVAL_1_16: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16; ++ break; ++ case GUARD_INTERVAL_1_8: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8; ++ break; ++ } ++ ++ /* hierarchy */ ++ switch (state->props.hierarchy) { ++ case HIERARCHY_AUTO: ++ case HIERARCHY_NONE: ++ default: ++ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M; ++ /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */ ++ /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */ ++ /* break; */ ++ case HIERARCHY_1: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1; ++ break; ++ case HIERARCHY_2: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2; ++ break; ++ case HIERARCHY_4: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4; ++ break; ++ } ++ ++ ++ /* modulation */ ++ switch (state->props.modulation) { ++ case QAM_AUTO: ++ default: ++ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M; ++ /* fall through , try first guess DRX_CONSTELLATION_QAM64 */ ++ case QAM_64: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; ++ break; ++ case QPSK: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK; ++ break; ++ case QAM_16: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16; ++ break; ++ } ++#if 0 ++ /* No hierachical channels support in BDA */ ++ /* Priority (only for hierarchical channels) */ ++ switch (channel->priority) { ++ case DRX_PRIORITY_LOW: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO; ++ WR16(devAddr, OFDM_EC_SB_PRIOR__A, ++ OFDM_EC_SB_PRIOR_LO); ++ break; ++ case DRX_PRIORITY_HIGH: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; ++ WR16(devAddr, OFDM_EC_SB_PRIOR__A, ++ OFDM_EC_SB_PRIOR_HI)); ++ break; ++ case DRX_PRIORITY_UNKNOWN: /* fall through */ ++ default: ++ status = -EINVAL; ++ goto error; ++ } ++#else ++ /* Set Priorty high */ ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; ++ status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI); ++ if (status < 0) ++ goto error; ++#endif ++ ++ /* coderate */ ++ switch (state->props.code_rate_HP) { ++ case FEC_AUTO: ++ default: ++ operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M; ++ /* fall through , try first guess DRX_CODERATE_2DIV3 */ ++ case FEC_2_3: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; ++ break; ++ case FEC_1_2: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2; ++ break; ++ case FEC_3_4: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4; ++ break; ++ case FEC_5_6: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6; ++ break; ++ case FEC_7_8: ++ transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8; ++ break; ++ } ++ ++ /* SAW filter selection: normaly not necesarry, but if wanted ++ the application can select a SAW filter via the driver by using UIOs */ ++ /* First determine real bandwidth (Hz) */ ++ /* Also set delay for impulse noise cruncher */ ++ /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed ++ by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC ++ functions */ ++ switch (state->props.bandwidth_hz) { ++ case 0: ++ state->props.bandwidth_hz = 8000000; ++ /* fall though */ ++ case 8000000: ++ bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ; ++ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052); ++ if (status < 0) ++ goto error; ++ /* cochannel protection for PAL 8 MHz */ ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); ++ if (status < 0) ++ goto error; ++ break; ++ case 7000000: ++ bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ; ++ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491); ++ if (status < 0) ++ goto error; ++ /* cochannel protection for PAL 7 MHz */ ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); ++ if (status < 0) ++ goto error; ++ break; ++ case 6000000: ++ bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ; ++ status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073); ++ if (status < 0) ++ goto error; ++ /* cochannel protection for NTSC 6 MHz */ ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); ++ if (status < 0) ++ goto error; ++ break; ++ default: ++ status = -EINVAL; ++ goto error; ++ } ++ ++ if (iqmRcRateOfs == 0) { ++ /* Now compute IQM_RC_RATE_OFS ++ (((SysFreq/BandWidth)/2)/2) -1) * 2^23) ++ => ++ ((SysFreq / BandWidth) * (2^21)) - (2^23) ++ */ ++ /* (SysFreq / BandWidth) * (2^28) */ ++ /* assert (MAX(sysClk)/MIN(bandwidth) < 16) ++ => assert(MAX(sysClk) < 16*MIN(bandwidth)) ++ => assert(109714272 > 48000000) = true so Frac 28 can be used */ ++ iqmRcRateOfs = Frac28a((u32) ++ ((state->m_sysClockFreq * ++ 1000) / 3), bandwidth); ++ /* (SysFreq / BandWidth) * (2^21), rounding before truncating */ ++ if ((iqmRcRateOfs & 0x7fL) >= 0x40) ++ iqmRcRateOfs += 0x80L; ++ iqmRcRateOfs = iqmRcRateOfs >> 7; ++ /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */ ++ iqmRcRateOfs = iqmRcRateOfs - (1 << 23); ++ } ++ ++ iqmRcRateOfs &= ++ ((((u32) IQM_RC_RATE_OFS_HI__M) << ++ IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M); ++ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs); ++ if (status < 0) ++ goto error; ++ ++ /* Bandwidth setting done */ ++ ++#if 0 ++ status = DVBTSetFrequencyShift(demod, channel, tunerOffset); ++ if (status < 0) ++ goto error; ++#endif ++ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true); ++ if (status < 0) ++ goto error; ++ ++ /*== Start SC, write channel settings to SC ===============================*/ ++ ++ /* Activate SCU to enable SCU commands */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ ++ /* Enable SC after setting all other parameters */ ++ status = write16(state, OFDM_SC_COMM_STATE__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, OFDM_SC_COMM_EXEC__A, 1); ++ if (status < 0) ++ goto error; ++ ++ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ /* Write SC parameter registers, set all AUTO flags in operation mode */ ++ param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M | ++ OFDM_SC_RA_RAM_OP_AUTO_GUARD__M | ++ OFDM_SC_RA_RAM_OP_AUTO_CONST__M | ++ OFDM_SC_RA_RAM_OP_AUTO_HIER__M | ++ OFDM_SC_RA_RAM_OP_AUTO_RATE__M); ++ status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM, ++ 0, transmissionParams, param1, 0, 0, 0); ++ if (status < 0) ++ goto error; ++ ++ if (!state->m_DRXK_A3_ROM_CODE) ++ status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++ ++/*============================================================================*/ ++ ++/** ++* \brief Retreive lock status . ++* \param demod Pointer to demodulator instance. ++* \param lockStat Pointer to lock status structure. ++* \return DRXStatus_t. ++* ++*/ ++static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus) ++{ ++ int status; ++ const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M | ++ OFDM_SC_RA_RAM_LOCK_FEC__M); ++ const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M); ++ const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M; ++ ++ u16 ScRaRamLock = 0; ++ u16 ScCommExec = 0; ++ ++ dprintk(1, "\n"); ++ ++ *pLockStatus = NOT_LOCKED; ++ /* driver 0.9.0 */ ++ /* Check if SC is running */ ++ status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec); ++ if (status < 0) ++ goto end; ++ if (ScCommExec == OFDM_SC_COMM_EXEC_STOP) ++ goto end; ++ ++ status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock); ++ if (status < 0) ++ goto end; ++ ++ if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask) ++ *pLockStatus = MPEG_LOCK; ++ else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask) ++ *pLockStatus = FEC_LOCK; ++ else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask) ++ *pLockStatus = DEMOD_LOCK; ++ else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M) ++ *pLockStatus = NEVER_LOCK; ++end: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int PowerUpQAM(struct drxk_state *state) ++{ ++ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; ++ int status; ++ ++ dprintk(1, "\n"); ++ status = CtrlPowerMode(state, &powerMode); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++ ++/** Power Down QAM */ ++static int PowerDownQAM(struct drxk_state *state) ++{ ++ u16 data = 0; ++ u16 cmdResult; ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ status = read16(state, SCU_COMM_EXEC__A, &data); ++ if (status < 0) ++ goto error; ++ if (data == SCU_COMM_EXEC_ACTIVE) { ++ /* ++ STOP demodulator ++ QAM and HW blocks ++ */ ++ /* stop all comstate->m_exec */ ++ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ } ++ /* powerdown AFE */ ++ status = SetIqmAf(state, false); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief Setup of the QAM Measurement intervals for signal quality ++* \param demod instance of demod. ++* \param modulation current modulation. ++* \return DRXStatus_t. ++* ++* NOTE: ++* Take into account that for certain settings the errorcounters can overflow. ++* The implementation does not check this. ++* ++*/ ++static int SetQAMMeasurement(struct drxk_state *state, ++ enum EDrxkConstellation modulation, ++ u32 symbolRate) ++{ ++ u32 fecBitsDesired = 0; /* BER accounting period */ ++ u32 fecRsPeriodTotal = 0; /* Total period */ ++ u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */ ++ u16 fecRsPeriod = 0; /* Value for corresponding I2C register */ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ ++ fecRsPrescale = 1; ++ /* fecBitsDesired = symbolRate [kHz] * ++ FrameLenght [ms] * ++ (modulation + 1) * ++ SyncLoss (== 1) * ++ ViterbiLoss (==1) ++ */ ++ switch (modulation) { ++ case DRX_CONSTELLATION_QAM16: ++ fecBitsDesired = 4 * symbolRate; ++ break; ++ case DRX_CONSTELLATION_QAM32: ++ fecBitsDesired = 5 * symbolRate; ++ break; ++ case DRX_CONSTELLATION_QAM64: ++ fecBitsDesired = 6 * symbolRate; ++ break; ++ case DRX_CONSTELLATION_QAM128: ++ fecBitsDesired = 7 * symbolRate; ++ break; ++ case DRX_CONSTELLATION_QAM256: ++ fecBitsDesired = 8 * symbolRate; ++ break; ++ default: ++ status = -EINVAL; ++ } ++ if (status < 0) ++ goto error; ++ ++ fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */ ++ fecBitsDesired *= 500; /* meas. period [ms] */ ++ ++ /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */ ++ /* fecRsPeriodTotal = fecBitsDesired / 1632 */ ++ fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */ ++ ++ /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */ ++ fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16); ++ if (fecRsPrescale == 0) { ++ /* Divide by zero (though impossible) */ ++ status = -EINVAL; ++ if (status < 0) ++ goto error; ++ } ++ fecRsPeriod = ++ ((u16) fecRsPeriodTotal + ++ (fecRsPrescale >> 1)) / fecRsPrescale; ++ ++ /* write corresponding registers */ ++ status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SetQAM16(struct drxk_state *state) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ /* QAM Equalizer Setup */ ++ /* Equalizer */ ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517); ++ if (status < 0) ++ goto error; ++ /* Decision Feedback Equalizer */ ++ status = write16(state, QAM_DQ_QUAL_FUN0__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN1__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN2__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN3__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN4__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_SY_SYNC_HWM__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_AWM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_LWM__A, 3); ++ if (status < 0) ++ goto error; ++ ++ /* QAM Slicer Settings */ ++ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16); ++ if (status < 0) ++ goto error; ++ ++ /* QAM Loop Controller Coeficients */ ++ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM State Machine (FSM) Thresholds */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM FSM Tracking Parameters */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127); ++ if (status < 0) ++ goto error; ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief QAM32 specific setup ++* \param demod instance of demod. ++* \return DRXStatus_t. ++*/ ++static int SetQAM32(struct drxk_state *state) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ ++ /* QAM Equalizer Setup */ ++ /* Equalizer */ ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707); ++ if (status < 0) ++ goto error; ++ ++ /* Decision Feedback Equalizer */ ++ status = write16(state, QAM_DQ_QUAL_FUN0__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN1__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN2__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN3__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_SY_SYNC_HWM__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_AWM__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_LWM__A, 3); ++ if (status < 0) ++ goto error; ++ ++ /* QAM Slicer Settings */ ++ ++ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM Loop Controller Coeficients */ ++ ++ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM State Machine (FSM) Thresholds */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM FSM Tracking Parameters */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief QAM64 specific setup ++* \param demod instance of demod. ++* \return DRXStatus_t. ++*/ ++static int SetQAM64(struct drxk_state *state) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ /* QAM Equalizer Setup */ ++ /* Equalizer */ ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609); ++ if (status < 0) ++ goto error; ++ ++ /* Decision Feedback Equalizer */ ++ status = write16(state, QAM_DQ_QUAL_FUN0__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN1__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN2__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN3__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_SY_SYNC_HWM__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_AWM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_LWM__A, 3); ++ if (status < 0) ++ goto error; ++ ++ /* QAM Slicer Settings */ ++ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM Loop Controller Coeficients */ ++ ++ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM State Machine (FSM) Thresholds */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM FSM Tracking Parameters */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief QAM128 specific setup ++* \param demod: instance of demod. ++* \return DRXStatus_t. ++*/ ++static int SetQAM128(struct drxk_state *state) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ /* QAM Equalizer Setup */ ++ /* Equalizer */ ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238); ++ if (status < 0) ++ goto error; ++ ++ /* Decision Feedback Equalizer */ ++ status = write16(state, QAM_DQ_QUAL_FUN0__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN1__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN2__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN3__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN4__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_SY_SYNC_HWM__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_AWM__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_LWM__A, 3); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM Slicer Settings */ ++ ++ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM Loop Controller Coeficients */ ++ ++ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM State Machine (FSM) Thresholds */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); ++ if (status < 0) ++ goto error; ++ ++ /* QAM FSM Tracking Parameters */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief QAM256 specific setup ++* \param demod: instance of demod. ++* \return DRXStatus_t. ++*/ ++static int SetQAM256(struct drxk_state *state) ++{ ++ int status = 0; ++ ++ dprintk(1, "\n"); ++ /* QAM Equalizer Setup */ ++ /* Equalizer */ ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385); ++ if (status < 0) ++ goto error; ++ ++ /* Decision Feedback Equalizer */ ++ status = write16(state, QAM_DQ_QUAL_FUN0__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN1__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN2__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN3__A, 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN4__A, 6); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_SY_SYNC_HWM__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_AWM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_SYNC_LWM__A, 3); ++ if (status < 0) ++ goto error; ++ ++ /* QAM Slicer Settings */ ++ ++ status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM Loop Controller Coeficients */ ++ ++ status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM State Machine (FSM) Thresholds */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); ++ if (status < 0) ++ goto error; ++ ++ ++ /* QAM FSM Tracking Parameters */ ++ ++ status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++ ++/*============================================================================*/ ++/** ++* \brief Reset QAM block. ++* \param demod: instance of demod. ++* \param channel: pointer to channel data. ++* \return DRXStatus_t. ++*/ ++static int QAMResetQAM(struct drxk_state *state) ++{ ++ int status; ++ u16 cmdResult; ++ ++ dprintk(1, "\n"); ++ /* Stop QAM comstate->m_exec */ ++ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief Set QAM symbolrate. ++* \param demod: instance of demod. ++* \param channel: pointer to channel data. ++* \return DRXStatus_t. ++*/ ++static int QAMSetSymbolrate(struct drxk_state *state) ++{ ++ u32 adcFrequency = 0; ++ u32 symbFreq = 0; ++ u32 iqmRcRate = 0; ++ u16 ratesel = 0; ++ u32 lcSymbRate = 0; ++ int status; ++ ++ dprintk(1, "\n"); ++ /* Select & calculate correct IQM rate */ ++ adcFrequency = (state->m_sysClockFreq * 1000) / 3; ++ ratesel = 0; ++ /* printk(KERN_DEBUG "drxk: SR %d\n", state->props.symbol_rate); */ ++ if (state->props.symbol_rate <= 1188750) ++ ratesel = 3; ++ else if (state->props.symbol_rate <= 2377500) ++ ratesel = 2; ++ else if (state->props.symbol_rate <= 4755000) ++ ratesel = 1; ++ status = write16(state, IQM_FD_RATESEL__A, ratesel); ++ if (status < 0) ++ goto error; ++ ++ /* ++ IqmRcRate = ((Fadc / (symbolrate * (4<props.symbol_rate * (1 << ratesel); ++ if (symbFreq == 0) { ++ /* Divide by zero */ ++ status = -EINVAL; ++ goto error; ++ } ++ iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) + ++ (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) - ++ (1 << 23); ++ status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate); ++ if (status < 0) ++ goto error; ++ state->m_iqmRcRate = iqmRcRate; ++ /* ++ LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15)) ++ */ ++ symbFreq = state->props.symbol_rate; ++ if (adcFrequency == 0) { ++ /* Divide by zero */ ++ status = -EINVAL; ++ goto error; ++ } ++ lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) + ++ (Frac28a((symbFreq % adcFrequency), adcFrequency) >> ++ 16); ++ if (lcSymbRate > 511) ++ lcSymbRate = 511; ++ status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate); ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++/*============================================================================*/ ++ ++/** ++* \brief Get QAM lock status. ++* \param demod: instance of demod. ++* \param channel: pointer to channel data. ++* \return DRXStatus_t. ++*/ ++ ++static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) ++{ ++ int status; ++ u16 Result[2] = { 0, 0 }; ++ ++ dprintk(1, "\n"); ++ *pLockStatus = NOT_LOCKED; ++ status = scu_command(state, ++ SCU_RAM_COMMAND_STANDARD_QAM | ++ SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2, ++ Result); ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) { ++ /* 0x0000 NOT LOCKED */ ++ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) { ++ /* 0x4000 DEMOD LOCKED */ ++ *pLockStatus = DEMOD_LOCK; ++ } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) { ++ /* 0x8000 DEMOD + FEC LOCKED (system lock) */ ++ *pLockStatus = MPEG_LOCK; ++ } else { ++ /* 0xC000 NEVER LOCKED */ ++ /* (system will never be able to lock to the signal) */ ++ /* TODO: check this, intermediate & standard specific lock states are not ++ taken into account here */ ++ *pLockStatus = NEVER_LOCK; ++ } ++ return status; ++} ++ ++#define QAM_MIRROR__M 0x03 ++#define QAM_MIRROR_NORMAL 0x00 ++#define QAM_MIRRORED 0x01 ++#define QAM_MIRROR_AUTO_ON 0x02 ++#define QAM_LOCKRANGE__M 0x10 ++#define QAM_LOCKRANGE_NORMAL 0x10 ++ ++static int QAMDemodulatorCommand(struct drxk_state *state, ++ int numberOfParameters) ++{ ++ int status; ++ u16 cmdResult; ++ u16 setParamParameters[4] = { 0, 0, 0, 0 }; ++ ++ setParamParameters[0] = state->m_Constellation; /* modulation */ ++ setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ ++ ++ if (numberOfParameters == 2) { ++ u16 setEnvParameters[1] = { 0 }; ++ ++ if (state->m_OperationMode == OM_QAM_ITU_C) ++ setEnvParameters[0] = QAM_TOP_ANNEX_C; ++ else ++ setEnvParameters[0] = QAM_TOP_ANNEX_A; ++ ++ status = scu_command(state, ++ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, ++ 1, setEnvParameters, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ status = scu_command(state, ++ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, ++ numberOfParameters, setParamParameters, ++ 1, &cmdResult); ++ } else if (numberOfParameters == 4) { ++ if (state->m_OperationMode == OM_QAM_ITU_C) ++ setParamParameters[2] = QAM_TOP_ANNEX_C; ++ else ++ setParamParameters[2] = QAM_TOP_ANNEX_A; ++ ++ setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); ++ /* Env parameters */ ++ /* check for LOCKRANGE Extented */ ++ /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ ++ ++ status = scu_command(state, ++ SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, ++ numberOfParameters, setParamParameters, ++ 1, &cmdResult); ++ } else { ++ printk(KERN_WARNING "drxk: Unknown QAM demodulator parameter " ++ "count %d\n", numberOfParameters); ++ status = -EINVAL; ++ } ++ ++error: ++ if (status < 0) ++ printk(KERN_WARNING "drxk: Warning %d on %s\n", ++ status, __func__); ++ return status; ++} ++ ++static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, ++ s32 tunerFreqOffset) ++{ ++ int status; ++ u16 cmdResult; ++ int qamDemodParamCount = state->qam_demod_parameter_count; ++ ++ dprintk(1, "\n"); ++ /* ++ * STEP 1: reset demodulator ++ * resets FEC DI and FEC RS ++ * resets QAM block ++ * resets SCU variables ++ */ ++ status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = QAMResetQAM(state); ++ if (status < 0) ++ goto error; ++ ++ /* ++ * STEP 2: configure demodulator ++ * -set params; resets IQM,QAM,FEC HW; initializes some ++ * SCU variables ++ */ ++ status = QAMSetSymbolrate(state); ++ if (status < 0) ++ goto error; ++ ++ /* Set params */ ++ switch (state->props.modulation) { ++ case QAM_256: ++ state->m_Constellation = DRX_CONSTELLATION_QAM256; ++ break; ++ case QAM_AUTO: ++ case QAM_64: ++ state->m_Constellation = DRX_CONSTELLATION_QAM64; ++ break; ++ case QAM_16: ++ state->m_Constellation = DRX_CONSTELLATION_QAM16; ++ break; ++ case QAM_32: ++ state->m_Constellation = DRX_CONSTELLATION_QAM32; ++ break; ++ case QAM_128: ++ state->m_Constellation = DRX_CONSTELLATION_QAM128; ++ break; ++ default: ++ status = -EINVAL; ++ break; ++ } ++ if (status < 0) ++ goto error; ++ ++ /* Use the 4-parameter if it's requested or we're probing for ++ * the correct command. */ ++ if (state->qam_demod_parameter_count == 4 ++ || !state->qam_demod_parameter_count) { ++ qamDemodParamCount = 4; ++ status = QAMDemodulatorCommand(state, qamDemodParamCount); ++ } ++ ++ /* Use the 2-parameter command if it was requested or if we're ++ * probing for the correct command and the 4-parameter command ++ * failed. */ ++ if (state->qam_demod_parameter_count == 2 ++ || (!state->qam_demod_parameter_count && status < 0)) { ++ qamDemodParamCount = 2; ++ status = QAMDemodulatorCommand(state, qamDemodParamCount); ++ } ++ ++ if (status < 0) { ++ dprintk(1, "Could not set demodulator parameters. Make " ++ "sure qam_demod_parameter_count (%d) is correct for " ++ "your firmware (%s).\n", ++ state->qam_demod_parameter_count, ++ state->microcode_name); ++ goto error; ++ } else if (!state->qam_demod_parameter_count) { ++ dprintk(1, "Auto-probing the correct QAM demodulator command " ++ "parameters was successful - using %d parameters.\n", ++ qamDemodParamCount); ++ ++ /* ++ * One of our commands was successful. We don't need to ++ * auto-probe anymore, now that we got the correct command. ++ */ ++ state->qam_demod_parameter_count = qamDemodParamCount; ++ } ++ ++ /* ++ * STEP 3: enable the system in a mode where the ADC provides valid ++ * signal setup modulation independent registers ++ */ ++#if 0 ++ status = SetFrequency(channel, tunerFreqOffset)); ++ if (status < 0) ++ goto error; ++#endif ++ status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true); ++ if (status < 0) ++ goto error; ++ ++ /* Setup BER measurement */ ++ status = SetQAMMeasurement(state, state->m_Constellation, state->props.symbol_rate); ++ if (status < 0) ++ goto error; ++ ++ /* Reset default values */ ++ status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE); ++ if (status < 0) ++ goto error; ++ ++ /* Reset default LC values */ ++ status = write16(state, QAM_LC_RATE_LIMIT__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_LPF_FACTORP__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_LPF_FACTORI__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_MODE__A, 7); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, QAM_LC_QUAL_TAB0__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB1__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB2__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB3__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB4__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB5__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB6__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB8__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB9__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB10__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB12__A, 2); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB15__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB16__A, 3); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB20__A, 4); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_LC_QUAL_TAB25__A, 4); ++ if (status < 0) ++ goto error; ++ ++ /* Mirroring, QAM-block starting point not inverted */ ++ status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS); ++ if (status < 0) ++ goto error; ++ ++ /* Halt SCU to enable safe non-atomic accesses */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); ++ if (status < 0) ++ goto error; ++ ++ /* STEP 4: modulation specific setup */ ++ switch (state->props.modulation) { ++ case QAM_16: ++ status = SetQAM16(state); ++ break; ++ case QAM_32: ++ status = SetQAM32(state); ++ break; ++ case QAM_AUTO: ++ case QAM_64: ++ status = SetQAM64(state); ++ break; ++ case QAM_128: ++ status = SetQAM128(state); ++ break; ++ case QAM_256: ++ status = SetQAM256(state); ++ break; ++ default: ++ status = -EINVAL; ++ break; ++ } ++ if (status < 0) ++ goto error; ++ ++ /* Activate SCU to enable SCU commands */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ ++ /* Re-configure MPEG output, requires knowledge of channel bitrate */ ++ /* extAttr->currentChannel.modulation = channel->modulation; */ ++ /* extAttr->currentChannel.symbolrate = channel->symbolrate; */ ++ status = MPEGTSDtoSetup(state, state->m_OperationMode); ++ if (status < 0) ++ goto error; ++ ++ /* Start processes */ ++ status = MPEGTSStart(state); ++ if (status < 0) ++ goto error; ++ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); ++ if (status < 0) ++ goto error; ++ ++ /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */ ++ status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult); ++ if (status < 0) ++ goto error; ++ ++ /* update global DRXK data container */ ++/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */ ++ ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SetQAMStandard(struct drxk_state *state, ++ enum OperationMode oMode) ++{ ++ int status; ++#ifdef DRXK_QAM_TAPS ++#define DRXK_QAMA_TAPS_SELECT ++#include "drxk_filters.h" ++#undef DRXK_QAMA_TAPS_SELECT ++#endif ++ ++ dprintk(1, "\n"); ++ ++ /* added antenna switch */ ++ SwitchAntennaToQAM(state); ++ ++ /* Ensure correct power-up mode */ ++ status = PowerUpQAM(state); ++ if (status < 0) ++ goto error; ++ /* Reset QAM block */ ++ status = QAMResetQAM(state); ++ if (status < 0) ++ goto error; ++ ++ /* Setup IQM */ ++ ++ status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); ++ if (status < 0) ++ goto error; ++ ++ /* Upload IQM Channel Filter settings by ++ boot loader from ROM table */ ++ switch (oMode) { ++ case OM_QAM_ITU_A: ++ status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); ++ break; ++ case OM_QAM_ITU_C: ++ status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); ++ if (status < 0) ++ goto error; ++ status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); ++ break; ++ default: ++ status = -EINVAL; ++ } ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B)); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_SYMMETRIC__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B))); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_RC_STRETCH__A, 21); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_CLP_LEN__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_CLP_TH__A, 448); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_SNS_LEN__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, IQM_FS_ADJ_SEL__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_RC_ADJ_SEL__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_ADJ_SEL__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_UPD_SEL__A, 0); ++ if (status < 0) ++ goto error; ++ ++ /* IQM Impulse Noise Processing Unit */ ++ status = write16(state, IQM_CF_CLP_VAL__A, 500); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_DATATH__A, 1000); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_BYPASSDET__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_DET_LCT__A, 0); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_WND_LEN__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_CF_PKDTH__A, 1); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_INC_BYPASS__A, 1); ++ if (status < 0) ++ goto error; ++ ++ /* turn on IQMAF. Must be done before setAgc**() */ ++ status = SetIqmAf(state, true); ++ if (status < 0) ++ goto error; ++ status = write16(state, IQM_AF_START_LOCK__A, 0x01); ++ if (status < 0) ++ goto error; ++ ++ /* IQM will not be reset from here, sync ADC and update/init AGC */ ++ status = ADCSynchronization(state); ++ if (status < 0) ++ goto error; ++ ++ /* Set the FSM step period */ ++ status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000); ++ if (status < 0) ++ goto error; ++ ++ /* Halt SCU to enable safe non-atomic accesses */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); ++ if (status < 0) ++ goto error; ++ ++ /* No more resets of the IQM, current standard correctly set => ++ now AGCs can be configured. */ ++ ++ status = InitAGC(state, true); ++ if (status < 0) ++ goto error; ++ status = SetPreSaw(state, &(state->m_qamPreSawCfg)); ++ if (status < 0) ++ goto error; ++ ++ /* Configure AGC's */ ++ status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true); ++ if (status < 0) ++ goto error; ++ status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true); ++ if (status < 0) ++ goto error; ++ ++ /* Activate SCU to enable SCU commands */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int WriteGPIO(struct drxk_state *state) ++{ ++ int status; ++ u16 value = 0; ++ ++ dprintk(1, "\n"); ++ /* stop lock indicator process */ ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ ++ /* Write magic word to enable pdr reg write */ ++ status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); ++ if (status < 0) ++ goto error; ++ ++ if (state->m_hasSAWSW) { ++ if (state->UIO_mask & 0x0001) { /* UIO-1 */ ++ /* write to io pad configuration register - output mode */ ++ status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); ++ if (status < 0) ++ goto error; ++ ++ /* use corresponding bit in io data output registar */ ++ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); ++ if (status < 0) ++ goto error; ++ if ((state->m_GPIO & 0x0001) == 0) ++ value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */ ++ else ++ value |= 0x8000; /* write one to 15th bit - 1st UIO */ ++ /* write back to io data output register */ ++ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); ++ if (status < 0) ++ goto error; ++ } ++ if (state->UIO_mask & 0x0002) { /* UIO-2 */ ++ /* write to io pad configuration register - output mode */ ++ status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg); ++ if (status < 0) ++ goto error; ++ ++ /* use corresponding bit in io data output registar */ ++ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); ++ if (status < 0) ++ goto error; ++ if ((state->m_GPIO & 0x0002) == 0) ++ value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */ ++ else ++ value |= 0x4000; /* write one to 14th bit - 2st UIO */ ++ /* write back to io data output register */ ++ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); ++ if (status < 0) ++ goto error; ++ } ++ if (state->UIO_mask & 0x0004) { /* UIO-3 */ ++ /* write to io pad configuration register - output mode */ ++ status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg); ++ if (status < 0) ++ goto error; ++ ++ /* use corresponding bit in io data output registar */ ++ status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); ++ if (status < 0) ++ goto error; ++ if ((state->m_GPIO & 0x0004) == 0) ++ value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */ ++ else ++ value |= 0x0004; /* write one to 2nd bit - 3rd UIO */ ++ /* write back to io data output register */ ++ status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); ++ if (status < 0) ++ goto error; ++ } ++ } ++ /* Write magic word to disable pdr reg write */ ++ status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SwitchAntennaToQAM(struct drxk_state *state) ++{ ++ int status = 0; ++ bool gpio_state; ++ ++ dprintk(1, "\n"); ++ ++ if (!state->antenna_gpio) ++ return 0; ++ ++ gpio_state = state->m_GPIO & state->antenna_gpio; ++ ++ if (state->antenna_dvbt ^ gpio_state) { ++ /* Antenna is on DVB-T mode. Switch */ ++ if (state->antenna_dvbt) ++ state->m_GPIO &= ~state->antenna_gpio; ++ else ++ state->m_GPIO |= state->antenna_gpio; ++ status = WriteGPIO(state); ++ } ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++static int SwitchAntennaToDVBT(struct drxk_state *state) ++{ ++ int status = 0; ++ bool gpio_state; ++ ++ dprintk(1, "\n"); ++ ++ if (!state->antenna_gpio) ++ return 0; ++ ++ gpio_state = state->m_GPIO & state->antenna_gpio; ++ ++ if (!(state->antenna_dvbt ^ gpio_state)) { ++ /* Antenna is on DVB-C mode. Switch */ ++ if (state->antenna_dvbt) ++ state->m_GPIO |= state->antenna_gpio; ++ else ++ state->m_GPIO &= ~state->antenna_gpio; ++ status = WriteGPIO(state); ++ } ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ return status; ++} ++ ++ ++static int PowerDownDevice(struct drxk_state *state) ++{ ++ /* Power down to requested mode */ ++ /* Backup some register settings */ ++ /* Set pins with possible pull-ups connected to them in input mode */ ++ /* Analog power down */ ++ /* ADC power down */ ++ /* Power down device */ ++ int status; ++ ++ dprintk(1, "\n"); ++ if (state->m_bPDownOpenBridge) { ++ /* Open I2C bridge before power down of DRXK */ ++ status = ConfigureI2CBridge(state, true); ++ if (status < 0) ++ goto error; ++ } ++ /* driver 0.9.0 */ ++ status = DVBTEnableOFDMTokenRing(state, false); ++ if (status < 0) ++ goto error; ++ ++ status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); ++ if (status < 0) ++ goto error; ++ state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; ++ status = HI_CfgCommand(state); ++error: ++ if (status < 0) ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ ++ return status; ++} ++ ++static int init_drxk(struct drxk_state *state) ++{ ++ int status = 0, n = 0; ++ enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; ++ u16 driverVersion; ++ ++ dprintk(1, "\n"); ++ if ((state->m_DrxkState == DRXK_UNINITIALIZED)) { ++ drxk_i2c_lock(state); ++ status = PowerUpDevice(state); ++ if (status < 0) ++ goto error; ++ status = DRXX_Open(state); ++ if (status < 0) ++ goto error; ++ /* Soft reset of OFDM-, sys- and osc-clockdomain */ ++ status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M); ++ if (status < 0) ++ goto error; ++ status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); ++ if (status < 0) ++ goto error; ++ /* TODO is this needed, if yes how much delay in worst case scenario */ ++ msleep(1); ++ state->m_DRXK_A3_PATCH_CODE = true; ++ status = GetDeviceCapabilities(state); ++ if (status < 0) ++ goto error; ++ ++ /* Bridge delay, uses oscilator clock */ ++ /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */ ++ /* SDA brdige delay */ ++ state->m_HICfgBridgeDelay = ++ (u16) ((state->m_oscClockFreq / 1000) * ++ HI_I2C_BRIDGE_DELAY) / 1000; ++ /* Clipping */ ++ if (state->m_HICfgBridgeDelay > ++ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) { ++ state->m_HICfgBridgeDelay = ++ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M; ++ } ++ /* SCL bridge delay, same as SDA for now */ ++ state->m_HICfgBridgeDelay += ++ state->m_HICfgBridgeDelay << ++ SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B; ++ ++ status = InitHI(state); ++ if (status < 0) ++ goto error; ++ /* disable various processes */ ++#if NOA1ROM ++ if (!(state->m_DRXK_A1_ROM_CODE) ++ && !(state->m_DRXK_A2_ROM_CODE)) ++#endif ++ { ++ status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); ++ if (status < 0) ++ goto error; ++ } ++ ++ /* disable MPEG port */ ++ status = MPEGTSDisable(state); ++ if (status < 0) ++ goto error; ++ ++ /* Stop AUD and SCU */ ++ status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ ++ /* enable token-ring bus through OFDM block for possible ucode upload */ ++ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON); ++ if (status < 0) ++ goto error; ++ ++ /* include boot loader section */ ++ status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ status = BLChainCmd(state, 0, 6, 100); ++ if (status < 0) ++ goto error; ++ ++ if (state->fw) { ++ status = DownloadMicrocode(state, state->fw->data, ++ state->fw->size); ++ if (status < 0) ++ goto error; ++ } ++ ++ /* disable token-ring bus through OFDM block for possible ucode upload */ ++ status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF); ++ if (status < 0) ++ goto error; ++ ++ /* Run SCU for a little while to initialize microcode version numbers */ ++ status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); ++ if (status < 0) ++ goto error; ++ status = DRXX_Open(state); ++ if (status < 0) ++ goto error; ++ /* added for test */ ++ msleep(30); ++ ++ powerMode = DRXK_POWER_DOWN_OFDM; ++ status = CtrlPowerMode(state, &powerMode); ++ if (status < 0) ++ goto error; ++ ++ /* Stamp driver version number in SCU data RAM in BCD code ++ Done to enable field application engineers to retreive drxdriver version ++ via I2C from SCU RAM. ++ Not using SCU command interface for SCU register access since no ++ microcode may be present. ++ */ ++ driverVersion = ++ (((DRXK_VERSION_MAJOR / 100) % 10) << 12) + ++ (((DRXK_VERSION_MAJOR / 10) % 10) << 8) + ++ ((DRXK_VERSION_MAJOR % 10) << 4) + ++ (DRXK_VERSION_MINOR % 10); ++ status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion); ++ if (status < 0) ++ goto error; ++ driverVersion = ++ (((DRXK_VERSION_PATCH / 1000) % 10) << 12) + ++ (((DRXK_VERSION_PATCH / 100) % 10) << 8) + ++ (((DRXK_VERSION_PATCH / 10) % 10) << 4) + ++ (DRXK_VERSION_PATCH % 10); ++ status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion); ++ if (status < 0) ++ goto error; ++ ++ printk(KERN_INFO "DRXK driver version %d.%d.%d\n", ++ DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR, ++ DRXK_VERSION_PATCH); ++ ++ /* Dirty fix of default values for ROM/PATCH microcode ++ Dirty because this fix makes it impossible to setup suitable values ++ before calling DRX_Open. This solution requires changes to RF AGC speed ++ to be done via the CTRL function after calling DRX_Open */ ++ ++ /* m_dvbtRfAgcCfg.speed = 3; */ ++ ++ /* Reset driver debug flags to 0 */ ++ status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0); ++ if (status < 0) ++ goto error; ++ /* driver 0.9.0 */ ++ /* Setup FEC OC: ++ NOTE: No more full FEC resets allowed afterwards!! */ ++ status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP); ++ if (status < 0) ++ goto error; ++ /* MPEGTS functions are still the same */ ++ status = MPEGTSDtoInit(state); ++ if (status < 0) ++ goto error; ++ status = MPEGTSStop(state); ++ if (status < 0) ++ goto error; ++ status = MPEGTSConfigurePolarity(state); ++ if (status < 0) ++ goto error; ++ status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput); ++ if (status < 0) ++ goto error; ++ /* added: configure GPIO */ ++ status = WriteGPIO(state); ++ if (status < 0) ++ goto error; ++ ++ state->m_DrxkState = DRXK_STOPPED; ++ ++ if (state->m_bPowerDown) { ++ status = PowerDownDevice(state); ++ if (status < 0) ++ goto error; ++ state->m_DrxkState = DRXK_POWERED_DOWN; ++ } else ++ state->m_DrxkState = DRXK_STOPPED; ++ ++ /* Initialize the supported delivery systems */ ++ n = 0; ++ if (state->m_hasDVBC) { ++ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; ++ state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; ++ strlcat(state->frontend.ops.info.name, " DVB-C", ++ sizeof(state->frontend.ops.info.name)); ++ } ++ if (state->m_hasDVBT) { ++ state->frontend.ops.delsys[n++] = SYS_DVBT; ++ strlcat(state->frontend.ops.info.name, " DVB-T", ++ sizeof(state->frontend.ops.info.name)); ++ } ++ drxk_i2c_unlock(state); ++ } ++error: ++ if (status < 0) { ++ state->m_DrxkState = DRXK_NO_DEV; ++ drxk_i2c_unlock(state); ++ printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); ++ } ++ ++ return status; ++} ++ ++static void load_firmware_cb(const struct firmware *fw, ++ void *context) ++{ ++ struct drxk_state *state = context; ++ ++ dprintk(1, ": %s\n", fw ? "firmware loaded" : "firmware not loaded"); ++ if (!fw) { ++ printk(KERN_ERR ++ "drxk: Could not load firmware file %s.\n", ++ state->microcode_name); ++ printk(KERN_INFO ++ "drxk: Copy %s to your hotplug directory!\n", ++ state->microcode_name); ++ state->microcode_name = NULL; ++ ++ /* ++ * As firmware is now load asynchronous, it is not possible ++ * anymore to fail at frontend attach. We might silently ++ * return here, and hope that the driver won't crash. ++ * We might also change all DVB callbacks to return -ENODEV ++ * if the device is not initialized. ++ * As the DRX-K devices have their own internal firmware, ++ * let's just hope that it will match a firmware revision ++ * compatible with this driver and proceed. ++ */ ++ } ++ state->fw = fw; ++ ++ init_drxk(state); ++} ++ ++static void drxk_release(struct dvb_frontend *fe) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "\n"); ++ if (state->fw) ++ release_firmware(state->fw); ++ ++ kfree(state); ++} ++ ++static int drxk_sleep(struct dvb_frontend *fe) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return 0; ++ ++ ShutDown(state); ++ return 0; ++} ++ ++static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ ++ dprintk(1, ": %s\n", enable ? "enable" : "disable"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ ++ return ConfigureI2CBridge(state, enable ? true : false); ++} ++ ++static int drxk_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ u32 delsys = p->delivery_system, old_delsys; ++ struct drxk_state *state = fe->demodulator_priv; ++ u32 IF; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ if (!fe->ops.tuner_ops.get_if_frequency) { ++ printk(KERN_ERR ++ "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); ++ return -EINVAL; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ old_delsys = state->props.delivery_system; ++ state->props = *p; ++ ++ if (old_delsys != delsys) { ++ ShutDown(state); ++ switch (delsys) { ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ if (!state->m_hasDVBC) ++ return -EINVAL; ++ state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? true : false; ++ if (state->m_itut_annex_c) ++ SetOperationMode(state, OM_QAM_ITU_C); ++ else ++ SetOperationMode(state, OM_QAM_ITU_A); ++ break; ++ case SYS_DVBT: ++ if (!state->m_hasDVBT) ++ return -EINVAL; ++ SetOperationMode(state, OM_DVBT); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ fe->ops.tuner_ops.get_if_frequency(fe, &IF); ++ Start(state, 0, IF); ++ ++ /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */ ++ ++ return 0; ++} ++ ++static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ u32 stat; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ *status = 0; ++ GetLockStatus(state, &stat, 0); ++ if (stat == MPEG_LOCK) ++ *status |= 0x1f; ++ if (stat == FEC_LOCK) ++ *status |= 0x0f; ++ if (stat == DEMOD_LOCK) ++ *status |= 0x07; ++ return 0; ++} ++ ++static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ *ber = 0; ++ return 0; ++} ++ ++static int drxk_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ u32 val = 0; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ ReadIFAgc(state, &val); ++ *strength = val & 0xffff; ++ return 0; ++} ++ ++static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ s32 snr2; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ GetSignalToNoise(state, &snr2); ++ *snr = snr2 & 0xffff; ++ return 0; ++} ++ ++static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ u16 err; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ DVBTQAMGetAccPktErr(state, &err); ++ *ucblocks = (u32) err; ++ return 0; ++} ++ ++static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings ++ *sets) ++{ ++ struct drxk_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ dprintk(1, "\n"); ++ ++ if (state->m_DrxkState == DRXK_NO_DEV) ++ return -ENODEV; ++ if (state->m_DrxkState == DRXK_UNINITIALIZED) ++ return -EAGAIN; ++ ++ switch (p->delivery_system) { ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ case SYS_DVBT: ++ sets->min_delay_ms = 3000; ++ sets->max_drift = 0; ++ sets->step_size = 0; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static struct dvb_frontend_ops drxk_ops = { ++ /* .delsys will be filled dynamically */ ++ .info = { ++ .name = "DRXK", ++ .frequency_min = 47000000, ++ .frequency_max = 865000000, ++ /* For DVB-C */ ++ .symbol_rate_min = 870000, ++ .symbol_rate_max = 11700000, ++ /* For DVB-T */ ++ .frequency_stepsize = 166667, ++ ++ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_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_MUTE_TS | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | ++ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO ++ }, ++ ++ .release = drxk_release, ++ .sleep = drxk_sleep, ++ .i2c_gate_ctrl = drxk_gate_ctrl, ++ ++ .set_frontend = drxk_set_parameters, ++ .get_tune_settings = drxk_get_tune_settings, ++ ++ .read_status = drxk_read_status, ++ .read_ber = drxk_read_ber, ++ .read_signal_strength = drxk_read_signal_strength, ++ .read_snr = drxk_read_snr, ++ .read_ucblocks = drxk_read_ucblocks, ++}; ++ ++struct dvb_frontend *drxk_attach(const struct drxk_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct drxk_state *state = NULL; ++ u8 adr = config->adr; ++ int status; ++ ++ dprintk(1, "\n"); ++ state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->i2c = i2c; ++ state->demod_address = adr; ++ state->single_master = config->single_master; ++ state->microcode_name = config->microcode_name; ++ state->qam_demod_parameter_count = config->qam_demod_parameter_count; ++ state->no_i2c_bridge = config->no_i2c_bridge; ++ state->antenna_gpio = config->antenna_gpio; ++ state->antenna_dvbt = config->antenna_dvbt; ++ state->m_ChunkSize = config->chunk_size; ++ state->enable_merr_cfg = config->enable_merr_cfg; ++ ++ if (config->dynamic_clk) { ++ state->m_DVBTStaticCLK = 0; ++ state->m_DVBCStaticCLK = 0; ++ } else { ++ state->m_DVBTStaticCLK = 1; ++ state->m_DVBCStaticCLK = 1; ++ } ++ ++ ++ if (config->mpeg_out_clk_strength) ++ state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07; ++ else ++ state->m_TSClockkStrength = 0x06; ++ ++ if (config->parallel_ts) ++ state->m_enableParallel = true; ++ else ++ state->m_enableParallel = false; ++ ++ /* NOTE: as more UIO bits will be used, add them to the mask */ ++ state->UIO_mask = config->antenna_gpio; ++ ++ /* Default gpio to DVB-C */ ++ if (!state->antenna_dvbt && state->antenna_gpio) ++ state->m_GPIO |= state->antenna_gpio; ++ else ++ state->m_GPIO &= ~state->antenna_gpio; ++ ++ mutex_init(&state->mutex); ++ ++ memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ init_state(state); ++ ++ /* Load firmware and initialize DRX-K */ ++ if (state->microcode_name) { ++ if (config->load_firmware_sync) { ++ const struct firmware *fw = NULL; ++ ++ status = request_firmware(&fw, state->microcode_name, ++ state->i2c->dev.parent); ++ if (status < 0) ++ fw = NULL; ++ load_firmware_cb(fw, state); ++ } else { ++ status = request_firmware_nowait(THIS_MODULE, 1, ++ state->microcode_name, ++ state->i2c->dev.parent, ++ GFP_KERNEL, ++ state, load_firmware_cb); ++ if (status < 0) { ++ printk(KERN_ERR ++ "drxk: failed to request a firmware\n"); ++ return NULL; ++ } ++ } ++ } else if (init_drxk(state) < 0) ++ goto error; ++ ++ printk(KERN_INFO "drxk: frontend initialized.\n"); ++ return &state->frontend; ++ ++error: ++ printk(KERN_ERR "drxk: not found\n"); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(drxk_attach); ++ ++MODULE_DESCRIPTION("DRX-K driver"); ++MODULE_AUTHOR("Ralph Metzler"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/drxk_hard.h b/drivers/media/dvb-frontends/drxk_hard.h +new file mode 100644 +index 0000000..d18a896 +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxk_hard.h +@@ -0,0 +1,360 @@ ++#include "drxk_map.h" ++ ++#define DRXK_VERSION_MAJOR 0 ++#define DRXK_VERSION_MINOR 9 ++#define DRXK_VERSION_PATCH 4300 ++ ++#define HI_I2C_DELAY 42 ++#define HI_I2C_BRIDGE_DELAY 350 ++#define DRXK_MAX_RETRIES 100 ++ ++#define DRIVER_4400 1 ++ ++#define DRXX_JTAGID 0x039210D9 ++#define DRXX_J_JTAGID 0x239310D9 ++#define DRXX_K_JTAGID 0x039210D9 ++ ++#define DRX_UNKNOWN 254 ++#define DRX_AUTO 255 ++ ++#define DRX_SCU_READY 0 ++#define DRXK_MAX_WAITTIME (200) ++#define SCU_RESULT_OK 0 ++#define SCU_RESULT_SIZE -4 ++#define SCU_RESULT_INVPAR -3 ++#define SCU_RESULT_UNKSTD -2 ++#define SCU_RESULT_UNKCMD -1 ++ ++#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT ++#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200) ++#endif ++ ++#define DRXK_8VSB_MPEG_BIT_RATE 19392658UL /*bps*/ ++#define DRXK_DVBT_MPEG_BIT_RATE 32000000UL /*bps*/ ++#define DRXK_QAM16_MPEG_BIT_RATE 27000000UL /*bps*/ ++#define DRXK_QAM32_MPEG_BIT_RATE 33000000UL /*bps*/ ++#define DRXK_QAM64_MPEG_BIT_RATE 40000000UL /*bps*/ ++#define DRXK_QAM128_MPEG_BIT_RATE 46000000UL /*bps*/ ++#define DRXK_QAM256_MPEG_BIT_RATE 52000000UL /*bps*/ ++#define DRXK_MAX_MPEG_BIT_RATE 52000000UL /*bps*/ ++ ++#define IQM_CF_OUT_ENA_OFDM__M 0x4 ++#define IQM_FS_ADJ_SEL_B_QAM 0x1 ++#define IQM_FS_ADJ_SEL_B_OFF 0x0 ++#define IQM_FS_ADJ_SEL_B_VSB 0x2 ++#define IQM_RC_ADJ_SEL_B_OFF 0x0 ++#define IQM_RC_ADJ_SEL_B_QAM 0x1 ++#define IQM_RC_ADJ_SEL_B_VSB 0x2 ++ ++enum OperationMode { ++ OM_NONE, ++ OM_QAM_ITU_A, ++ OM_QAM_ITU_B, ++ OM_QAM_ITU_C, ++ OM_DVBT ++}; ++ ++enum DRXPowerMode { ++ DRX_POWER_UP = 0, ++ DRX_POWER_MODE_1, ++ DRX_POWER_MODE_2, ++ DRX_POWER_MODE_3, ++ DRX_POWER_MODE_4, ++ DRX_POWER_MODE_5, ++ DRX_POWER_MODE_6, ++ DRX_POWER_MODE_7, ++ DRX_POWER_MODE_8, ++ ++ DRX_POWER_MODE_9, ++ DRX_POWER_MODE_10, ++ DRX_POWER_MODE_11, ++ DRX_POWER_MODE_12, ++ DRX_POWER_MODE_13, ++ DRX_POWER_MODE_14, ++ DRX_POWER_MODE_15, ++ DRX_POWER_MODE_16, ++ DRX_POWER_DOWN = 255 ++}; ++ ++ ++/** /brief Intermediate power mode for DRXK, power down OFDM clock domain */ ++#ifndef DRXK_POWER_DOWN_OFDM ++#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1 ++#endif ++ ++/** /brief Intermediate power mode for DRXK, power down core (sysclk) */ ++#ifndef DRXK_POWER_DOWN_CORE ++#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9 ++#endif ++ ++/** /brief Intermediate power mode for DRXK, power down pll (only osc runs) */ ++#ifndef DRXK_POWER_DOWN_PLL ++#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10 ++#endif ++ ++ ++enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF }; ++enum EDrxkState { ++ DRXK_UNINITIALIZED = 0, ++ DRXK_STOPPED, ++ DRXK_DTV_STARTED, ++ DRXK_ATV_STARTED, ++ DRXK_POWERED_DOWN, ++ DRXK_NO_DEV /* If drxk init failed */ ++}; ++ ++enum EDrxkCoefArrayIndex { ++ DRXK_COEF_IDX_MN = 0, ++ DRXK_COEF_IDX_FM , ++ DRXK_COEF_IDX_L , ++ DRXK_COEF_IDX_LP , ++ DRXK_COEF_IDX_BG , ++ DRXK_COEF_IDX_DK , ++ DRXK_COEF_IDX_I , ++ DRXK_COEF_IDX_MAX ++}; ++enum EDrxkSifAttenuation { ++ DRXK_SIF_ATTENUATION_0DB, ++ DRXK_SIF_ATTENUATION_3DB, ++ DRXK_SIF_ATTENUATION_6DB, ++ DRXK_SIF_ATTENUATION_9DB ++}; ++enum EDrxkConstellation { ++ DRX_CONSTELLATION_BPSK = 0, ++ DRX_CONSTELLATION_QPSK, ++ DRX_CONSTELLATION_PSK8, ++ DRX_CONSTELLATION_QAM16, ++ DRX_CONSTELLATION_QAM32, ++ DRX_CONSTELLATION_QAM64, ++ DRX_CONSTELLATION_QAM128, ++ DRX_CONSTELLATION_QAM256, ++ DRX_CONSTELLATION_QAM512, ++ DRX_CONSTELLATION_QAM1024, ++ DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN, ++ DRX_CONSTELLATION_AUTO = DRX_AUTO ++}; ++enum EDrxkInterleaveMode { ++ DRXK_QAM_I12_J17 = 16, ++ DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN ++}; ++enum { ++ DRXK_SPIN_A1 = 0, ++ DRXK_SPIN_A2, ++ DRXK_SPIN_A3, ++ DRXK_SPIN_UNKNOWN ++}; ++ ++enum DRXKCfgDvbtSqiSpeed { ++ DRXK_DVBT_SQI_SPEED_FAST = 0, ++ DRXK_DVBT_SQI_SPEED_MEDIUM, ++ DRXK_DVBT_SQI_SPEED_SLOW, ++ DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN ++} ; ++ ++enum DRXFftmode_t { ++ DRX_FFTMODE_2K = 0, ++ DRX_FFTMODE_4K, ++ DRX_FFTMODE_8K, ++ DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN, ++ DRX_FFTMODE_AUTO = DRX_AUTO ++}; ++ ++enum DRXMPEGStrWidth_t { ++ DRX_MPEG_STR_WIDTH_1, ++ DRX_MPEG_STR_WIDTH_8 ++}; ++ ++enum DRXQamLockRange_t { ++ DRX_QAM_LOCKRANGE_NORMAL, ++ DRX_QAM_LOCKRANGE_EXTENDED ++}; ++ ++struct DRXKCfgDvbtEchoThres_t { ++ u16 threshold; ++ enum DRXFftmode_t fftMode; ++} ; ++ ++struct SCfgAgc { ++ enum AGC_CTRL_MODE ctrlMode; /* off, user, auto */ ++ u16 outputLevel; /* range dependent on AGC */ ++ u16 minOutputLevel; /* range dependent on AGC */ ++ u16 maxOutputLevel; /* range dependent on AGC */ ++ u16 speed; /* range dependent on AGC */ ++ u16 top; /* rf-agc take over point */ ++ u16 cutOffCurrent; /* rf-agc is accelerated if output current ++ is below cut-off current */ ++ u16 IngainTgtMax; ++ u16 FastClipCtrlDelay; ++}; ++ ++struct SCfgPreSaw { ++ u16 reference; /* pre SAW reference value, range 0 .. 31 */ ++ bool usePreSaw; /* TRUE algorithms must use pre SAW sense */ ++}; ++ ++struct DRXKOfdmScCmd_t { ++ u16 cmd; /**< Command number */ ++ u16 subcmd; /**< Sub-command parameter*/ ++ u16 param0; /**< General purpous param */ ++ u16 param1; /**< General purpous param */ ++ u16 param2; /**< General purpous param */ ++ u16 param3; /**< General purpous param */ ++ u16 param4; /**< General purpous param */ ++}; ++ ++struct drxk_state { ++ struct dvb_frontend frontend; ++ struct dtv_frontend_properties props; ++ struct device *dev; ++ ++ struct i2c_adapter *i2c; ++ u8 demod_address; ++ void *priv; ++ ++ struct mutex mutex; ++ ++ u32 m_Instance; /**< Channel 1,2,3 or 4 */ ++ ++ int m_ChunkSize; ++ u8 Chunk[256]; ++ ++ bool m_hasLNA; ++ bool m_hasDVBT; ++ bool m_hasDVBC; ++ bool m_hasAudio; ++ bool m_hasATV; ++ bool m_hasOOB; ++ bool m_hasSAWSW; /**< TRUE if mat_tx is available */ ++ bool m_hasGPIO1; /**< TRUE if mat_rx is available */ ++ bool m_hasGPIO2; /**< TRUE if GPIO is available */ ++ bool m_hasIRQN; /**< TRUE if IRQN is available */ ++ u16 m_oscClockFreq; ++ u16 m_HICfgTimingDiv; ++ u16 m_HICfgBridgeDelay; ++ u16 m_HICfgWakeUpKey; ++ u16 m_HICfgTimeout; ++ u16 m_HICfgCtrl; ++ s32 m_sysClockFreq; /**< system clock frequency in kHz */ ++ ++ enum EDrxkState m_DrxkState; /**< State of Drxk (init,stopped,started) */ ++ enum OperationMode m_OperationMode; /**< digital standards */ ++ struct SCfgAgc m_vsbRfAgcCfg; /**< settings for VSB RF-AGC */ ++ struct SCfgAgc m_vsbIfAgcCfg; /**< settings for VSB IF-AGC */ ++ u16 m_vsbPgaCfg; /**< settings for VSB PGA */ ++ struct SCfgPreSaw m_vsbPreSawCfg; /**< settings for pre SAW sense */ ++ s32 m_Quality83percent; /**< MER level (*0.1 dB) for 83% quality indication */ ++ s32 m_Quality93percent; /**< MER level (*0.1 dB) for 93% quality indication */ ++ bool m_smartAntInverted; ++ bool m_bDebugEnableBridge; ++ bool m_bPDownOpenBridge; /**< only open DRXK bridge before power-down once it has been accessed */ ++ bool m_bPowerDown; /**< Power down when not used */ ++ ++ u32 m_IqmFsRateOfs; /**< frequency shift as written to DRXK register (28bit fixpoint) */ ++ ++ bool m_enableMPEGOutput; /**< If TRUE, enable MPEG output */ ++ bool m_insertRSByte; /**< If TRUE, insert RS byte */ ++ bool m_enableParallel; /**< If TRUE, parallel out otherwise serial */ ++ bool m_invertDATA; /**< If TRUE, invert DATA signals */ ++ bool m_invertERR; /**< If TRUE, invert ERR signal */ ++ bool m_invertSTR; /**< If TRUE, invert STR signals */ ++ bool m_invertVAL; /**< If TRUE, invert VAL signals */ ++ bool m_invertCLK; /**< If TRUE, invert CLK signals */ ++ bool m_DVBCStaticCLK; ++ bool m_DVBTStaticCLK; /**< If TRUE, static MPEG clockrate will ++ be used, otherwise clockrate will ++ adapt to the bitrate of the TS */ ++ u32 m_DVBTBitrate; ++ u32 m_DVBCBitrate; ++ ++ u8 m_TSDataStrength; ++ u8 m_TSClockkStrength; ++ ++ bool m_itut_annex_c; /* If true, uses ITU-T DVB-C Annex C, instead of Annex A */ ++ ++ enum DRXMPEGStrWidth_t m_widthSTR; /**< MPEG start width */ ++ u32 m_mpegTsStaticBitrate; /**< Maximum bitrate in b/s in case ++ static clockrate is selected */ ++ ++ /* LARGE_INTEGER m_StartTime; */ /**< Contains the time of the last demod start */ ++ s32 m_MpegLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */ ++ s32 m_DemodLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */ ++ ++ bool m_disableTEIhandling; ++ ++ bool m_RfAgcPol; ++ bool m_IfAgcPol; ++ ++ struct SCfgAgc m_atvRfAgcCfg; /**< settings for ATV RF-AGC */ ++ struct SCfgAgc m_atvIfAgcCfg; /**< settings for ATV IF-AGC */ ++ struct SCfgPreSaw m_atvPreSawCfg; /**< settings for ATV pre SAW sense */ ++ bool m_phaseCorrectionBypass; ++ s16 m_atvTopVidPeak; ++ u16 m_atvTopNoiseTh; ++ enum EDrxkSifAttenuation m_sifAttenuation; ++ bool m_enableCVBSOutput; ++ bool m_enableSIFOutput; ++ bool m_bMirrorFreqSpect; ++ enum EDrxkConstellation m_Constellation; /**< Constellation type of the channel */ ++ u32 m_CurrSymbolRate; /**< Current QAM symbol rate */ ++ struct SCfgAgc m_qamRfAgcCfg; /**< settings for QAM RF-AGC */ ++ struct SCfgAgc m_qamIfAgcCfg; /**< settings for QAM IF-AGC */ ++ u16 m_qamPgaCfg; /**< settings for QAM PGA */ ++ struct SCfgPreSaw m_qamPreSawCfg; /**< settings for QAM pre SAW sense */ ++ enum EDrxkInterleaveMode m_qamInterleaveMode; /**< QAM Interleave mode */ ++ u16 m_fecRsPlen; ++ u16 m_fecRsPrescale; ++ ++ enum DRXKCfgDvbtSqiSpeed m_sqiSpeed; ++ ++ u16 m_GPIO; ++ u16 m_GPIOCfg; ++ ++ struct SCfgAgc m_dvbtRfAgcCfg; /**< settings for QAM RF-AGC */ ++ struct SCfgAgc m_dvbtIfAgcCfg; /**< settings for QAM IF-AGC */ ++ struct SCfgPreSaw m_dvbtPreSawCfg; /**< settings for QAM pre SAW sense */ ++ ++ u16 m_agcFastClipCtrlDelay; ++ bool m_adcCompPassed; ++ u16 m_adcCompCoef[64]; ++ u16 m_adcState; ++ ++ u8 *m_microcode; ++ int m_microcode_length; ++ bool m_DRXK_A3_ROM_CODE; ++ bool m_DRXK_A3_PATCH_CODE; ++ ++ bool m_rfmirror; ++ u8 m_deviceSpin; ++ u32 m_iqmRcRate; ++ ++ enum DRXPowerMode m_currentPowerMode; ++ ++ /* when true, avoids other devices to use the I2C bus */ ++ bool drxk_i2c_exclusive_lock; ++ ++ /* ++ * Configurable parameters at the driver. They stores the values found ++ * at struct drxk_config. ++ */ ++ ++ u16 UIO_mask; /* Bits used by UIO */ ++ ++ bool enable_merr_cfg; ++ bool single_master; ++ bool no_i2c_bridge; ++ bool antenna_dvbt; ++ u16 antenna_gpio; ++ ++ /* Firmware */ ++ const char *microcode_name; ++ struct completion fw_wait_load; ++ const struct firmware *fw; ++ int qam_demod_parameter_count; ++}; ++ ++#define NEVER_LOCK 0 ++#define NOT_LOCKED 1 ++#define DEMOD_LOCK 2 ++#define FEC_LOCK 3 ++#define MPEG_LOCK 4 ++ +diff --git a/drivers/media/dvb-frontends/drxk_map.h b/drivers/media/dvb-frontends/drxk_map.h +new file mode 100644 +index 0000000..23e16c1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/drxk_map.h +@@ -0,0 +1,451 @@ ++#define AUD_COMM_EXEC__A 0x1000000 ++#define AUD_COMM_EXEC_STOP 0x0 ++#define FEC_COMM_EXEC__A 0x1C00000 ++#define FEC_COMM_EXEC_STOP 0x0 ++#define FEC_COMM_EXEC_ACTIVE 0x1 ++#define FEC_DI_COMM_EXEC__A 0x1C20000 ++#define FEC_DI_COMM_EXEC_STOP 0x0 ++#define FEC_DI_INPUT_CTL__A 0x1C20016 ++#define FEC_RS_COMM_EXEC__A 0x1C30000 ++#define FEC_RS_COMM_EXEC_STOP 0x0 ++#define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012 ++#define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013 ++#define FEC_OC_MODE__A 0x1C40011 ++#define FEC_OC_MODE_PARITY__M 0x1 ++#define FEC_OC_DTO_MODE__A 0x1C40014 ++#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1 ++#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4 ++#define FEC_OC_DTO_PERIOD__A 0x1C40015 ++#define FEC_OC_DTO_BURST_LEN__A 0x1C40018 ++#define FEC_OC_FCT_MODE__A 0x1C4001A ++#define FEC_OC_FCT_MODE__PRE 0x0 ++#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1 ++#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2 ++#define FEC_OC_TMD_MODE__A 0x1C4001E ++#define FEC_OC_TMD_COUNT__A 0x1C4001F ++#define FEC_OC_TMD_HI_MARGIN__A 0x1C40020 ++#define FEC_OC_TMD_LO_MARGIN__A 0x1C40021 ++#define FEC_OC_TMD_INT_UPD_RATE__A 0x1C40023 ++#define FEC_OC_AVR_PARM_A__A 0x1C40026 ++#define FEC_OC_AVR_PARM_B__A 0x1C40027 ++#define FEC_OC_RCN_GAIN__A 0x1C4002E ++#define FEC_OC_RCN_CTL_RATE_LO__A 0x1C40030 ++#define FEC_OC_RCN_CTL_STEP_LO__A 0x1C40032 ++#define FEC_OC_RCN_CTL_STEP_HI__A 0x1C40033 ++#define FEC_OC_SNC_MODE__A 0x1C40040 ++#define FEC_OC_SNC_MODE_SHUTDOWN__M 0x10 ++#define FEC_OC_SNC_LWM__A 0x1C40041 ++#define FEC_OC_SNC_HWM__A 0x1C40042 ++#define FEC_OC_SNC_UNLOCK__A 0x1C40043 ++#define FEC_OC_SNC_FAIL_PERIOD__A 0x1C40046 ++#define FEC_OC_IPR_MODE__A 0x1C40048 ++#define FEC_OC_IPR_MODE_SERIAL__M 0x1 ++#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4 ++#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10 ++#define FEC_OC_IPR_INVERT__A 0x1C40049 ++#define FEC_OC_IPR_INVERT_MD0__M 0x1 ++#define FEC_OC_IPR_INVERT_MD1__M 0x2 ++#define FEC_OC_IPR_INVERT_MD2__M 0x4 ++#define FEC_OC_IPR_INVERT_MD3__M 0x8 ++#define FEC_OC_IPR_INVERT_MD4__M 0x10 ++#define FEC_OC_IPR_INVERT_MD5__M 0x20 ++#define FEC_OC_IPR_INVERT_MD6__M 0x40 ++#define FEC_OC_IPR_INVERT_MD7__M 0x80 ++#define FEC_OC_IPR_INVERT_MERR__M 0x100 ++#define FEC_OC_IPR_INVERT_MSTRT__M 0x200 ++#define FEC_OC_IPR_INVERT_MVAL__M 0x400 ++#define FEC_OC_IPR_INVERT_MCLK__M 0x800 ++#define FEC_OC_OCR_INVERT__A 0x1C40052 ++#define IQM_COMM_EXEC__A 0x1800000 ++#define IQM_COMM_EXEC_B_STOP 0x0 ++#define IQM_COMM_EXEC_B_ACTIVE 0x1 ++#define IQM_FS_RATE_OFS_LO__A 0x1820010 ++#define IQM_FS_ADJ_SEL__A 0x1820014 ++#define IQM_FS_ADJ_SEL_B_OFF 0x0 ++#define IQM_FS_ADJ_SEL_B_QAM 0x1 ++#define IQM_FS_ADJ_SEL_B_VSB 0x2 ++#define IQM_FD_RATESEL__A 0x1830010 ++#define IQM_RC_RATE_OFS_LO__A 0x1840010 ++#define IQM_RC_RATE_OFS_LO__W 16 ++#define IQM_RC_RATE_OFS_LO__M 0xFFFF ++#define IQM_RC_RATE_OFS_HI__M 0xFF ++#define IQM_RC_ADJ_SEL__A 0x1840014 ++#define IQM_RC_ADJ_SEL_B_OFF 0x0 ++#define IQM_RC_ADJ_SEL_B_QAM 0x1 ++#define IQM_RC_ADJ_SEL_B_VSB 0x2 ++#define IQM_RC_STRETCH__A 0x1840016 ++#define IQM_CF_COMM_INT_MSK__A 0x1860006 ++#define IQM_CF_SYMMETRIC__A 0x1860010 ++#define IQM_CF_MIDTAP__A 0x1860011 ++#define IQM_CF_MIDTAP_RE__B 0 ++#define IQM_CF_MIDTAP_IM__B 1 ++#define IQM_CF_OUT_ENA__A 0x1860012 ++#define IQM_CF_OUT_ENA_QAM__B 1 ++#define IQM_CF_OUT_ENA_OFDM__M 0x4 ++#define IQM_CF_ADJ_SEL__A 0x1860013 ++#define IQM_CF_SCALE__A 0x1860014 ++#define IQM_CF_SCALE_SH__A 0x1860015 ++#define IQM_CF_SCALE_SH__PRE 0x0 ++#define IQM_CF_POW_MEAS_LEN__A 0x1860017 ++#define IQM_CF_DS_ENA__A 0x1860019 ++#define IQM_CF_TAP_RE0__A 0x1860020 ++#define IQM_CF_TAP_IM0__A 0x1860040 ++#define IQM_CF_CLP_VAL__A 0x1860060 ++#define IQM_CF_DATATH__A 0x1860061 ++#define IQM_CF_PKDTH__A 0x1860062 ++#define IQM_CF_WND_LEN__A 0x1860063 ++#define IQM_CF_DET_LCT__A 0x1860064 ++#define IQM_CF_BYPASSDET__A 0x1860067 ++#define IQM_AF_COMM_EXEC__A 0x1870000 ++#define IQM_AF_COMM_EXEC_ACTIVE 0x1 ++#define IQM_AF_CLKNEG__A 0x1870012 ++#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2 ++#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0 ++#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2 ++#define IQM_AF_START_LOCK__A 0x187001B ++#define IQM_AF_PHASE0__A 0x187001C ++#define IQM_AF_PHASE1__A 0x187001D ++#define IQM_AF_PHASE2__A 0x187001E ++#define IQM_AF_CLP_LEN__A 0x1870023 ++#define IQM_AF_CLP_TH__A 0x1870024 ++#define IQM_AF_SNS_LEN__A 0x1870026 ++#define IQM_AF_AGC_IF__A 0x1870028 ++#define IQM_AF_AGC_RF__A 0x1870029 ++#define IQM_AF_PDREF__A 0x187002B ++#define IQM_AF_PDREF__M 0x1F ++#define IQM_AF_STDBY__A 0x187002C ++#define IQM_AF_STDBY_STDBY_ADC_STANDBY 0x2 ++#define IQM_AF_STDBY_STDBY_AMP_STANDBY 0x4 ++#define IQM_AF_STDBY_STDBY_PD_STANDBY 0x8 ++#define IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY 0x10 ++#define IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY 0x20 ++#define IQM_AF_AMUX__A 0x187002D ++#define IQM_AF_AMUX_SIGNAL2ADC 0x1 ++#define IQM_AF_UPD_SEL__A 0x187002F ++#define IQM_AF_INC_LCT__A 0x1870034 ++#define IQM_AF_INC_BYPASS__A 0x1870036 ++#define OFDM_CP_COMM_EXEC__A 0x2800000 ++#define OFDM_CP_COMM_EXEC_STOP 0x0 ++#define OFDM_EC_SB_PRIOR__A 0x3410013 ++#define OFDM_EC_SB_PRIOR_HI 0x0 ++#define OFDM_EC_SB_PRIOR_LO 0x1 ++#define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054 ++#define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3 ++#define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2 ++#define OFDM_EQ_TOP_TD_TPS_CODE_HP__A 0x3010056 ++#define OFDM_EQ_TOP_TD_TPS_CODE_HP__M 0x7 ++#define OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8 0x4 ++#define OFDM_EQ_TOP_TD_SQR_ERR_I__A 0x301005E ++#define OFDM_EQ_TOP_TD_SQR_ERR_Q__A 0x301005F ++#define OFDM_EQ_TOP_TD_SQR_ERR_EXP__A 0x3010060 ++#define OFDM_EQ_TOP_TD_REQ_SMB_CNT__A 0x3010061 ++#define OFDM_EQ_TOP_TD_TPS_PWR_OFS__A 0x3010062 ++#define OFDM_LC_COMM_EXEC__A 0x3800000 ++#define OFDM_LC_COMM_EXEC_STOP 0x0 ++#define OFDM_SC_COMM_EXEC__A 0x3C00000 ++#define OFDM_SC_COMM_EXEC_STOP 0x0 ++#define OFDM_SC_COMM_STATE__A 0x3C00001 ++#define OFDM_SC_RA_RAM_PARAM0__A 0x3C20040 ++#define OFDM_SC_RA_RAM_PARAM1__A 0x3C20041 ++#define OFDM_SC_RA_RAM_CMD_ADDR__A 0x3C20042 ++#define OFDM_SC_RA_RAM_CMD__A 0x3C20043 ++#define OFDM_SC_RA_RAM_CMD_NULL 0x0 ++#define OFDM_SC_RA_RAM_CMD_PROC_START 0x1 ++#define OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 ++#define OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM 0x4 ++#define OFDM_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 ++#define OFDM_SC_RA_RAM_CMD_USER_IO 0x6 ++#define OFDM_SC_RA_RAM_CMD_SET_TIMER 0x7 ++#define OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING 0x8 ++#define OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 ++#define OFDM_SC_RA_RAM_LOCKTRACK_MIN 0x1 ++#define OFDM_SC_RA_RAM_OP_PARAM__A 0x3C20048 ++#define OFDM_SC_RA_RAM_OP_PARAM_MODE__M 0x3 ++#define OFDM_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 ++#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 ++#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 ++#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_4 0xC ++#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 ++#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 ++#define OFDM_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 ++#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 ++#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 ++#define OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 ++#define OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 ++#define OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 ++#define OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 ++#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 ++#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 ++#define OFDM_SC_RA_RAM_OP_AUTO_MODE__M 0x1 ++#define OFDM_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 ++#define OFDM_SC_RA_RAM_OP_AUTO_CONST__M 0x4 ++#define OFDM_SC_RA_RAM_OP_AUTO_HIER__M 0x8 ++#define OFDM_SC_RA_RAM_OP_AUTO_RATE__M 0x10 ++#define OFDM_SC_RA_RAM_LOCK__A 0x3C2004B ++#define OFDM_SC_RA_RAM_LOCK_DEMOD__M 0x1 ++#define OFDM_SC_RA_RAM_LOCK_FEC__M 0x2 ++#define OFDM_SC_RA_RAM_LOCK_MPEG__M 0x4 ++#define OFDM_SC_RA_RAM_LOCK_NODVBT__M 0x8 ++#define OFDM_SC_RA_RAM_BE_OPT_DELAY__A 0x3C2004D ++#define OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A 0x3C2004E ++#define OFDM_SC_RA_RAM_ECHO_THRES__A 0x3C2004F ++#define OFDM_SC_RA_RAM_ECHO_THRES_8K__B 0 ++#define OFDM_SC_RA_RAM_ECHO_THRES_8K__M 0xFF ++#define OFDM_SC_RA_RAM_ECHO_THRES_2K__B 8 ++#define OFDM_SC_RA_RAM_ECHO_THRES_2K__M 0xFF00 ++#define OFDM_SC_RA_RAM_CONFIG__A 0x3C20050 ++#define OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M 0x800 ++#define OFDM_SC_RA_RAM_FR_THRES_8K__A 0x3C2007D ++#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A 0x3C200E0 ++#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A 0x3C200E1 ++#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A 0x3C200E3 ++#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A 0x3C200E4 ++#define OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A 0x3C200F8 ++#define QAM_COMM_EXEC__A 0x1400000 ++#define QAM_COMM_EXEC_STOP 0x0 ++#define QAM_COMM_EXEC_ACTIVE 0x1 ++#define QAM_TOP_ANNEX_A 0x0 ++#define QAM_TOP_ANNEX_C 0x2 ++#define QAM_SL_ERR_POWER__A 0x1430017 ++#define QAM_DQ_QUAL_FUN0__A 0x1440018 ++#define QAM_DQ_QUAL_FUN1__A 0x1440019 ++#define QAM_DQ_QUAL_FUN2__A 0x144001A ++#define QAM_DQ_QUAL_FUN3__A 0x144001B ++#define QAM_DQ_QUAL_FUN4__A 0x144001C ++#define QAM_DQ_QUAL_FUN5__A 0x144001D ++#define QAM_LC_MODE__A 0x1450010 ++#define QAM_LC_QUAL_TAB0__A 0x1450018 ++#define QAM_LC_QUAL_TAB1__A 0x1450019 ++#define QAM_LC_QUAL_TAB2__A 0x145001A ++#define QAM_LC_QUAL_TAB3__A 0x145001B ++#define QAM_LC_QUAL_TAB4__A 0x145001C ++#define QAM_LC_QUAL_TAB5__A 0x145001D ++#define QAM_LC_QUAL_TAB6__A 0x145001E ++#define QAM_LC_QUAL_TAB8__A 0x145001F ++#define QAM_LC_QUAL_TAB9__A 0x1450020 ++#define QAM_LC_QUAL_TAB10__A 0x1450021 ++#define QAM_LC_QUAL_TAB12__A 0x1450022 ++#define QAM_LC_QUAL_TAB15__A 0x1450023 ++#define QAM_LC_QUAL_TAB16__A 0x1450024 ++#define QAM_LC_QUAL_TAB20__A 0x1450025 ++#define QAM_LC_QUAL_TAB25__A 0x1450026 ++#define QAM_LC_LPF_FACTORP__A 0x1450028 ++#define QAM_LC_LPF_FACTORI__A 0x1450029 ++#define QAM_LC_RATE_LIMIT__A 0x145002A ++#define QAM_LC_SYMBOL_FREQ__A 0x145002B ++#define QAM_SY_TIMEOUT__A 0x1470011 ++#define QAM_SY_TIMEOUT__PRE 0x3A98 ++#define QAM_SY_SYNC_LWM__A 0x1470012 ++#define QAM_SY_SYNC_AWM__A 0x1470013 ++#define QAM_SY_SYNC_HWM__A 0x1470014 ++#define QAM_SY_SP_INV__A 0x1470017 ++#define QAM_SY_SP_INV_SPECTRUM_INV_DIS 0x0 ++#define SCU_COMM_EXEC__A 0x800000 ++#define SCU_COMM_EXEC_STOP 0x0 ++#define SCU_COMM_EXEC_ACTIVE 0x1 ++#define SCU_COMM_EXEC_HOLD 0x2 ++#define SCU_RAM_DRIVER_DEBUG__A 0x831EBF ++#define SCU_RAM_QAM_FSM_STEP_PERIOD__A 0x831EC4 ++#define SCU_RAM_GPIO__A 0x831EC7 ++#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0 ++#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8 ++#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB ++#define SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A 0x831F05 ++#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831F15 ++#define SCU_RAM_AGC_KI_CYCLEN__A 0x831F17 ++#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831F18 ++#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831F19 ++#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831F1A ++#define SCU_RAM_AGC_RF_MAX__A 0x831F1B ++#define SCU_RAM_AGC_CONFIG__A 0x831F24 ++#define SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M 0x1 ++#define SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M 0x2 ++#define SCU_RAM_AGC_CONFIG_INV_IF_POL__M 0x100 ++#define SCU_RAM_AGC_CONFIG_INV_RF_POL__M 0x200 ++#define SCU_RAM_AGC_KI__A 0x831F25 ++#define SCU_RAM_AGC_KI_RF__B 4 ++#define SCU_RAM_AGC_KI_RF__M 0xF0 ++#define SCU_RAM_AGC_KI_IF__B 8 ++#define SCU_RAM_AGC_KI_IF__M 0xF00 ++#define SCU_RAM_AGC_KI_RED__A 0x831F26 ++#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2 ++#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC ++#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4 ++#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30 ++#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831F27 ++#define SCU_RAM_AGC_KI_MINGAIN__A 0x831F28 ++#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831F29 ++#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831F2A ++#define SCU_RAM_AGC_KI_MIN__A 0x831F2B ++#define SCU_RAM_AGC_KI_MAX__A 0x831F2C ++#define SCU_RAM_AGC_CLP_SUM__A 0x831F2D ++#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831F2E ++#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831F2F ++#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831F30 ++#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831F31 ++#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831F32 ++#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831F33 ++#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831F34 ++#define SCU_RAM_AGC_SNS_SUM__A 0x831F35 ++#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831F36 ++#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831F37 ++#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831F38 ++#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831F39 ++#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831F3A ++#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831F3B ++#define SCU_RAM_AGC_INGAIN_TGT__A 0x831F3D ++#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831F3E ++#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831F3F ++#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831F40 ++#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831F41 ++#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831F42 ++#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831F43 ++#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831F44 ++#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831F45 ++#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831F46 ++#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831F47 ++#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84 ++#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85 ++#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86 ++#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87 ++#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88 ++#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89 ++#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A ++#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E ++#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F ++#define SCU_RAM_QAM_FSM_PTH__A 0x831F90 ++#define SCU_RAM_QAM_FSM_MTH__A 0x831F91 ++#define SCU_RAM_QAM_FSM_CTH__A 0x831F92 ++#define SCU_RAM_QAM_FSM_QTH__A 0x831F93 ++#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94 ++#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95 ++#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96 ++#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97 ++#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99 ++#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A ++#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B ++#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C ++#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D ++#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E ++#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F ++#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0 ++#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1 ++#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2 ++#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3 ++#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4 ++#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5 ++#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6 ++#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7 ++#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8 ++#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9 ++#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA ++#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB ++#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC ++#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD ++#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE ++#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF ++#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0 ++#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1 ++#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2 ++#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000 ++#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000 ++#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000 ++#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831FEA ++#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB ++#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC ++#define SCU_RAM_PARAM_15__A 0x831FED ++#define SCU_RAM_PARAM_0__A 0x831FFC ++#define SCU_RAM_COMMAND__A 0x831FFD ++#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1 ++#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2 ++#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3 ++#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4 ++#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5 ++#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9 ++#define SCU_RAM_COMMAND_STANDARD_QAM 0x200 ++#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400 ++#define SIO_TOP_COMM_KEY__A 0x41000F ++#define SIO_TOP_COMM_KEY_KEY 0xFABA ++#define SIO_TOP_JTAGID_LO__A 0x410012 ++#define SIO_HI_RA_RAM_RES__A 0x420031 ++#define SIO_HI_RA_RAM_CMD__A 0x420032 ++#define SIO_HI_RA_RAM_CMD_RESET 0x2 ++#define SIO_HI_RA_RAM_CMD_CONFIG 0x3 ++#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7 ++#define SIO_HI_RA_RAM_PAR_1__A 0x420033 ++#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945 ++#define SIO_HI_RA_RAM_PAR_2__A 0x420034 ++#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F ++#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0 ++#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4 ++#define SIO_HI_RA_RAM_PAR_3__A 0x420035 ++#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F ++#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7 ++#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0 ++#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8 ++#define SIO_HI_RA_RAM_PAR_4__A 0x420036 ++#define SIO_HI_RA_RAM_PAR_5__A 0x420037 ++#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1 ++#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8 ++#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8 ++#define SIO_HI_RA_RAM_PAR_6__A 0x420038 ++#define SIO_CC_PLL_LOCK__A 0x450012 ++#define SIO_CC_PWD_MODE__A 0x450015 ++#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0 ++#define SIO_CC_PWD_MODE_LEVEL_OFDM 0x1 ++#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x2 ++#define SIO_CC_PWD_MODE_LEVEL_PLL 0x3 ++#define SIO_CC_PWD_MODE_LEVEL_OSC 0x4 ++#define SIO_CC_SOFT_RST__A 0x450016 ++#define SIO_CC_SOFT_RST_OFDM__M 0x1 ++#define SIO_CC_SOFT_RST_SYS__M 0x2 ++#define SIO_CC_SOFT_RST_OSC__M 0x4 ++#define SIO_CC_UPDATE__A 0x450017 ++#define SIO_CC_UPDATE_KEY 0xFABA ++#define SIO_OFDM_SH_OFDM_RING_ENABLE__A 0x470010 ++#define SIO_OFDM_SH_OFDM_RING_ENABLE_OFF 0x0 ++#define SIO_OFDM_SH_OFDM_RING_ENABLE_ON 0x1 ++#define SIO_OFDM_SH_OFDM_RING_STATUS__A 0x470012 ++#define SIO_OFDM_SH_OFDM_RING_STATUS_DOWN 0x0 ++#define SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED 0x1 ++#define SIO_BL_COMM_EXEC__A 0x480000 ++#define SIO_BL_COMM_EXEC_ACTIVE 0x1 ++#define SIO_BL_STATUS__A 0x480010 ++#define SIO_BL_MODE__A 0x480011 ++#define SIO_BL_MODE_DIRECT 0x0 ++#define SIO_BL_MODE_CHAIN 0x1 ++#define SIO_BL_ENABLE__A 0x480012 ++#define SIO_BL_ENABLE_ON 0x1 ++#define SIO_BL_TGT_HDR__A 0x480014 ++#define SIO_BL_TGT_ADDR__A 0x480015 ++#define SIO_BL_SRC_ADDR__A 0x480016 ++#define SIO_BL_SRC_LEN__A 0x480017 ++#define SIO_BL_CHAIN_ADDR__A 0x480018 ++#define SIO_BL_CHAIN_LEN__A 0x480019 ++#define SIO_PDR_MON_CFG__A 0x7F0010 ++#define SIO_PDR_UIO_IN_HI__A 0x7F0015 ++#define SIO_PDR_UIO_OUT_LO__A 0x7F0016 ++#define SIO_PDR_OHW_CFG__A 0x7F001F ++#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 ++#define SIO_PDR_GPIO_CFG__A 0x7F0021 ++#define SIO_PDR_MSTRT_CFG__A 0x7F0025 ++#define SIO_PDR_MERR_CFG__A 0x7F0026 ++#define SIO_PDR_MCLK_CFG__A 0x7F0028 ++#define SIO_PDR_MCLK_CFG_DRIVE__B 3 ++#define SIO_PDR_MVAL_CFG__A 0x7F0029 ++#define SIO_PDR_MD0_CFG__A 0x7F002A ++#define SIO_PDR_MD0_CFG_DRIVE__B 3 ++#define SIO_PDR_MD1_CFG__A 0x7F002B ++#define SIO_PDR_MD2_CFG__A 0x7F002C ++#define SIO_PDR_MD3_CFG__A 0x7F002D ++#define SIO_PDR_MD4_CFG__A 0x7F002F ++#define SIO_PDR_MD5_CFG__A 0x7F0030 ++#define SIO_PDR_MD6_CFG__A 0x7F0031 ++#define SIO_PDR_MD7_CFG__A 0x7F0032 ++#define SIO_PDR_SMA_RX_CFG__A 0x7F0037 ++#define SIO_PDR_SMA_TX_CFG__A 0x7F0038 +diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c +new file mode 100644 +index 0000000..1e344b0 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ds3000.c +@@ -0,0 +1,1134 @@ ++/* ++ Montage Technology DS3000 - DVBS/S2 Demodulator driver ++ Copyright (C) 2009-2012 Konstantin Dimitrov ++ ++ Copyright (C) 2009-2012 TurboSight.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 ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "ts2020.h" ++#include "ds3000.h" ++ ++static int debug; ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(args); \ ++ } while (0) ++ ++/* as of March 2009 current DS3000 firmware version is 1.78 */ ++/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */ ++#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" ++ ++#define DS3000_SAMPLE_RATE 96000 /* in kHz */ ++ ++/* Register values to initialise the demod in DVB-S mode */ ++static u8 ds3000_dvbs_init_tab[] = { ++ 0x23, 0x05, ++ 0x08, 0x03, ++ 0x0c, 0x00, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x31, 0x40, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x40, ++ 0x4b, 0x04, ++ 0x4d, 0x91, ++ 0x5d, 0xc8, ++ 0x50, 0x77, ++ 0x51, 0x77, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x56, 0x01, ++ 0x63, 0x43, ++ 0x64, 0x30, ++ 0x65, 0x40, ++ 0x68, 0x26, ++ 0x69, 0x4c, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0x76, 0x00, ++ 0x77, 0xd1, ++ 0x78, 0x0c, ++ 0x79, 0x80, ++ 0x7f, 0x04, ++ 0x7c, 0x00, ++ 0x80, 0x86, ++ 0x81, 0xa6, ++ 0x85, 0x04, ++ 0xcd, 0xf4, ++ 0x90, 0x33, ++ 0xa0, 0x44, ++ 0xc0, 0x18, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0x80, ++ 0xc6, 0x80, ++ 0xc7, 0x0a, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xfe, 0x92, ++ 0xe0, 0xf8, ++ 0xe6, 0x8b, ++ 0xd0, 0x40, ++ 0xf8, 0x20, ++ 0xfa, 0x0f, ++ 0xfd, 0x20, ++ 0xad, 0x20, ++ 0xae, 0x07, ++ 0xb8, 0x00, ++}; ++ ++/* Register values to initialise the demod in DVB-S2 mode */ ++static u8 ds3000_dvbs2_init_tab[] = { ++ 0x23, 0x0f, ++ 0x08, 0x07, ++ 0x0c, 0x00, ++ 0x21, 0x54, ++ 0x25, 0x82, ++ 0x27, 0x31, ++ 0x30, 0x08, ++ 0x31, 0x32, ++ 0x32, 0x32, ++ 0x33, 0x35, ++ 0x35, 0xff, ++ 0x3a, 0x00, ++ 0x37, 0x10, ++ 0x38, 0x10, ++ 0x39, 0x02, ++ 0x42, 0x60, ++ 0x4a, 0x80, ++ 0x4b, 0x04, ++ 0x4d, 0x81, ++ 0x5d, 0x88, ++ 0x50, 0x36, ++ 0x51, 0x36, ++ 0x52, 0x36, ++ 0x53, 0x36, ++ 0x63, 0x60, ++ 0x64, 0x10, ++ 0x65, 0x10, ++ 0x68, 0x04, ++ 0x69, 0x29, ++ 0x70, 0x20, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x40, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x60, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x80, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0xa0, ++ 0x71, 0x70, ++ 0x72, 0x04, ++ 0x73, 0x00, ++ 0x70, 0x1f, ++ 0xa0, 0x44, ++ 0xc0, 0x08, ++ 0xc1, 0x10, ++ 0xc2, 0x08, ++ 0xc3, 0x10, ++ 0xc4, 0x08, ++ 0xc5, 0xf0, ++ 0xc6, 0xf0, ++ 0xc7, 0x0a, ++ 0xc8, 0x1a, ++ 0xc9, 0x80, ++ 0xca, 0x23, ++ 0xcb, 0x24, ++ 0xce, 0x74, ++ 0x90, 0x03, ++ 0x76, 0x80, ++ 0x77, 0x42, ++ 0x78, 0x0a, ++ 0x79, 0x80, ++ 0xad, 0x40, ++ 0xae, 0x07, ++ 0x7f, 0xd4, ++ 0x7c, 0x00, ++ 0x80, 0xa8, ++ 0x81, 0xda, ++ 0x7c, 0x01, ++ 0x80, 0xda, ++ 0x81, 0xec, ++ 0x7c, 0x02, ++ 0x80, 0xca, ++ 0x81, 0xeb, ++ 0x7c, 0x03, ++ 0x80, 0xba, ++ 0x81, 0xdb, ++ 0x85, 0x08, ++ 0x86, 0x00, ++ 0x87, 0x02, ++ 0x89, 0x80, ++ 0x8b, 0x44, ++ 0x8c, 0xaa, ++ 0x8a, 0x10, ++ 0xba, 0x00, ++ 0xf5, 0x04, ++ 0xfe, 0x44, ++ 0xd2, 0x32, ++ 0xb8, 0x00, ++}; ++ ++struct ds3000_state { ++ struct i2c_adapter *i2c; ++ const struct ds3000_config *config; ++ struct dvb_frontend frontend; ++ /* previous uncorrected block counter for DVB-S2 */ ++ u16 prevUCBS2; ++}; ++ ++static int ds3000_writereg(struct ds3000_state *state, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," ++ " value == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int ds3000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ ++ if (enable) ++ ds3000_writereg(state, 0x03, 0x12); ++ else ++ ds3000_writereg(state, 0x03, 0x02); ++ ++ return 0; ++} ++ ++/* I2C write for 8k firmware load */ ++static int ds3000_writeFW(struct ds3000_state *state, int reg, ++ const u8 *data, u16 len) ++{ ++ int i, ret = 0; ++ struct i2c_msg msg; ++ u8 *buf; ++ ++ buf = kmalloc(33, GFP_KERNEL); ++ if (buf == NULL) { ++ printk(KERN_ERR "Unable to kmalloc\n"); ++ return -ENOMEM; ++ } ++ ++ *(buf) = reg; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = 33; ++ ++ for (i = 0; i < len; i += 32) { ++ memcpy(buf + 1, data + i, 32); ++ ++ dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ printk(KERN_ERR "%s: write error(err == %i, " ++ "reg == 0x%02x\n", __func__, ret, reg); ++ ret = -EREMOTEIO; ++ goto error; ++ } ++ } ++ ret = 0; ++ ++error: ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int ds3000_readreg(struct ds3000_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); ++ return ret; ++ } ++ ++ dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); ++ ++ return b1[0]; ++} ++ ++static int ds3000_load_firmware(struct dvb_frontend *fe, ++ const struct firmware *fw); ++ ++static int ds3000_firmware_ondemand(struct dvb_frontend *fe) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ const struct firmware *fw; ++ int ret = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ ret = ds3000_readreg(state, 0xb2); ++ if (ret < 0) ++ return ret; ++ ++ /* Load firmware */ ++ /* request the firmware, this will block until someone uploads it */ ++ printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, ++ DS3000_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); ++ if (ret) { ++ printk(KERN_ERR "%s: No firmware uploaded (timeout or file not " ++ "found?)\n", __func__); ++ return ret; ++ } ++ ++ ret = ds3000_load_firmware(fe, fw); ++ if (ret) ++ printk("%s: Writing firmware to device failed\n", __func__); ++ ++ release_firmware(fw); ++ ++ dprintk("%s: Firmware upload %s\n", __func__, ++ ret == 0 ? "complete" : "failed"); ++ ++ return ret; ++} ++ ++static int ds3000_load_firmware(struct dvb_frontend *fe, ++ const struct firmware *fw) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ int ret = 0; ++ ++ dprintk("%s\n", __func__); ++ dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", ++ fw->size, ++ fw->data[0], ++ fw->data[1], ++ fw->data[fw->size - 2], ++ fw->data[fw->size - 1]); ++ ++ /* Begin the firmware load process */ ++ ds3000_writereg(state, 0xb2, 0x01); ++ /* write the entire firmware */ ++ ret = ds3000_writeFW(state, 0xb0, fw->data, fw->size); ++ ds3000_writereg(state, 0xb2, 0x00); ++ ++ return ret; ++} ++ ++static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ u8 data; ++ ++ dprintk("%s(%d)\n", __func__, voltage); ++ ++ data = ds3000_readreg(state, 0xa2); ++ data |= 0x03; /* bit0 V/H, bit1 off/on */ ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_18: ++ data &= ~0x03; ++ break; ++ case SEC_VOLTAGE_13: ++ data &= ~0x03; ++ data |= 0x01; ++ break; ++ case SEC_VOLTAGE_OFF: ++ break; ++ } ++ ++ ds3000_writereg(state, 0xa2, data); ++ ++ return 0; ++} ++ ++static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int lock; ++ ++ *status = 0; ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ lock = ds3000_readreg(state, 0xd1); ++ if ((lock & 0x07) == 0x07) ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | ++ FE_HAS_LOCK; ++ ++ break; ++ case SYS_DVBS2: ++ lock = ds3000_readreg(state, 0x0d); ++ if ((lock & 0x8f) == 0x8f) ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | ++ FE_HAS_LOCK; ++ ++ break; ++ default: ++ return 1; ++ } ++ ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, *status == 0 ? 0 : 1); ++ ++ dprintk("%s: status = 0x%02x\n", __func__, lock); ++ ++ return 0; ++} ++ ++/* read DS3000 BER value */ ++static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 data; ++ u32 ber_reading, lpdc_frames; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ /* set the number of bytes checked during ++ BER estimation */ ++ ds3000_writereg(state, 0xf9, 0x04); ++ /* read BER estimation status */ ++ data = ds3000_readreg(state, 0xf8); ++ /* check if BER estimation is ready */ ++ if ((data & 0x10) == 0) { ++ /* this is the number of error bits, ++ to calculate the bit error rate ++ divide to 8388608 */ ++ *ber = (ds3000_readreg(state, 0xf7) << 8) | ++ ds3000_readreg(state, 0xf6); ++ /* start counting error bits */ ++ /* need to be set twice ++ otherwise it fails sometimes */ ++ data |= 0x10; ++ ds3000_writereg(state, 0xf8, data); ++ ds3000_writereg(state, 0xf8, data); ++ } else ++ /* used to indicate that BER estimation ++ is not ready, i.e. BER is unknown */ ++ *ber = 0xffffffff; ++ break; ++ case SYS_DVBS2: ++ /* read the number of LPDC decoded frames */ ++ lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) | ++ (ds3000_readreg(state, 0xd6) << 8) | ++ ds3000_readreg(state, 0xd5); ++ /* read the number of packets with bad CRC */ ++ ber_reading = (ds3000_readreg(state, 0xf8) << 8) | ++ ds3000_readreg(state, 0xf7); ++ if (lpdc_frames > 750) { ++ /* clear LPDC frame counters */ ++ ds3000_writereg(state, 0xd1, 0x01); ++ /* clear bad packets counter */ ++ ds3000_writereg(state, 0xf9, 0x01); ++ /* enable bad packets counter */ ++ ds3000_writereg(state, 0xf9, 0x00); ++ /* enable LPDC frame counters */ ++ ds3000_writereg(state, 0xd1, 0x00); ++ *ber = ber_reading; ++ } else ++ /* used to indicate that BER estimation is not ready, ++ i.e. BER is unknown */ ++ *ber = 0xffffffff; ++ break; ++ default: ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int ds3000_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ if (fe->ops.tuner_ops.get_rf_strength) ++ fe->ops.tuner_ops.get_rf_strength(fe, signal_strength); ++ ++ return 0; ++} ++ ++/* calculate DS3000 snr value in dB */ ++static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 snr_reading, snr_value; ++ u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp; ++ static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */ ++ 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03, ++ 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717, ++ 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505 ++ }; ++ static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */ ++ 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103, ++ 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5, ++ 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6, ++ 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888, ++ 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51, ++ 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68, ++ 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206, ++ 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a, ++ 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649, ++ 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813, ++ 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1, ++ 0x49e9, 0x4a20, 0x4a57 ++ }; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ snr_reading = ds3000_readreg(state, 0xff); ++ snr_reading /= 8; ++ if (snr_reading == 0) ++ *snr = 0x0000; ++ else { ++ if (snr_reading > 20) ++ snr_reading = 20; ++ snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026; ++ /* cook the value to be suitable for szap-s2 ++ human readable output */ ++ *snr = snr_value * 8 * 655; ++ } ++ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, ++ snr_reading, *snr); ++ break; ++ case SYS_DVBS2: ++ dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) + ++ (ds3000_readreg(state, 0x8d) << 4); ++ dvbs2_signal_reading = ds3000_readreg(state, 0x8e); ++ tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; ++ if (tmp == 0) { ++ *snr = 0x0000; ++ return 0; ++ } ++ if (dvbs2_noise_reading == 0) { ++ snr_value = 0x0013; ++ /* cook the value to be suitable for szap-s2 ++ human readable output */ ++ *snr = 0xffff; ++ return 0; ++ } ++ if (tmp > dvbs2_noise_reading) { ++ snr_reading = tmp / dvbs2_noise_reading; ++ if (snr_reading > 80) ++ snr_reading = 80; ++ snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000; ++ /* cook the value to be suitable for szap-s2 ++ human readable output */ ++ *snr = snr_value * 5 * 655; ++ } else { ++ snr_reading = dvbs2_noise_reading / tmp; ++ if (snr_reading > 80) ++ snr_reading = 80; ++ *snr = -(dvbs2_snr_tab[snr_reading] / 1000); ++ } ++ dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, ++ snr_reading, *snr); ++ break; ++ default: ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* read DS3000 uncorrected blocks */ ++static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 data; ++ u16 _ucblocks; ++ ++ dprintk("%s()\n", __func__); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ *ucblocks = (ds3000_readreg(state, 0xf5) << 8) | ++ ds3000_readreg(state, 0xf4); ++ data = ds3000_readreg(state, 0xf8); ++ /* clear packet counters */ ++ data &= ~0x20; ++ ds3000_writereg(state, 0xf8, data); ++ /* enable packet counters */ ++ data |= 0x20; ++ ds3000_writereg(state, 0xf8, data); ++ break; ++ case SYS_DVBS2: ++ _ucblocks = (ds3000_readreg(state, 0xe2) << 8) | ++ ds3000_readreg(state, 0xe1); ++ if (_ucblocks > state->prevUCBS2) ++ *ucblocks = _ucblocks - state->prevUCBS2; ++ else ++ *ucblocks = state->prevUCBS2 - _ucblocks; ++ state->prevUCBS2 = _ucblocks; ++ break; ++ default: ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ u8 data; ++ ++ dprintk("%s(%d)\n", __func__, tone); ++ if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { ++ printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); ++ return -EINVAL; ++ } ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ ds3000_writereg(state, 0xa2, data); ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ dprintk("%s: setting tone on\n", __func__); ++ data = ds3000_readreg(state, 0xa1); ++ data &= ~0x43; ++ data |= 0x04; ++ ds3000_writereg(state, 0xa1, data); ++ break; ++ case SEC_TONE_OFF: ++ dprintk("%s: setting tone off\n", __func__); ++ data = ds3000_readreg(state, 0xa2); ++ data |= 0x80; ++ ds3000_writereg(state, 0xa2, data); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *d) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ int i; ++ u8 data; ++ ++ /* Dump DiSEqC message */ ++ dprintk("%s(", __func__); ++ for (i = 0 ; i < d->msg_len;) { ++ dprintk("0x%02x", d->msg[i]); ++ if (++i < d->msg_len) ++ dprintk(", "); ++ } ++ ++ /* enable DiSEqC message send pin */ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ ds3000_writereg(state, 0xa2, data); ++ ++ /* DiSEqC message */ ++ for (i = 0; i < d->msg_len; i++) ++ ds3000_writereg(state, 0xa3 + i, d->msg[i]); ++ ++ data = ds3000_readreg(state, 0xa1); ++ /* clear DiSEqC message length and status, ++ enable DiSEqC message send */ ++ data &= ~0xf8; ++ /* set DiSEqC mode, modulation active during 33 pulses, ++ set DiSEqC message length */ ++ data |= ((d->msg_len - 1) << 3) | 0x07; ++ ds3000_writereg(state, 0xa1, data); ++ ++ /* wait up to 150ms for DiSEqC transmission to complete */ ++ for (i = 0; i < 15; i++) { ++ data = ds3000_readreg(state, 0xa1); ++ if ((data & 0x40) == 0) ++ break; ++ msleep(10); ++ } ++ ++ /* DiSEqC timeout after 150ms */ ++ if (i == 15) { ++ data = ds3000_readreg(state, 0xa1); ++ data &= ~0x80; ++ data |= 0x40; ++ ds3000_writereg(state, 0xa1, data); ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ data |= 0x80; ++ ds3000_writereg(state, 0xa2, data); ++ ++ return 1; ++ } ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ data |= 0x80; ++ ds3000_writereg(state, 0xa2, data); ++ ++ return 0; ++} ++ ++/* Send DiSEqC burst */ ++static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ int i; ++ u8 data; ++ ++ dprintk("%s()\n", __func__); ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ ds3000_writereg(state, 0xa2, data); ++ ++ /* DiSEqC burst */ ++ if (burst == SEC_MINI_A) ++ /* Unmodulated tone burst */ ++ ds3000_writereg(state, 0xa1, 0x02); ++ else if (burst == SEC_MINI_B) ++ /* Modulated tone burst */ ++ ds3000_writereg(state, 0xa1, 0x01); ++ else ++ return -EINVAL; ++ ++ msleep(13); ++ for (i = 0; i < 5; i++) { ++ data = ds3000_readreg(state, 0xa1); ++ if ((data & 0x40) == 0) ++ break; ++ msleep(1); ++ } ++ ++ if (i == 5) { ++ data = ds3000_readreg(state, 0xa1); ++ data &= ~0x80; ++ data |= 0x40; ++ ds3000_writereg(state, 0xa1, data); ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ data |= 0x80; ++ ds3000_writereg(state, 0xa2, data); ++ ++ return 1; ++ } ++ ++ data = ds3000_readreg(state, 0xa2); ++ data &= ~0xc0; ++ data |= 0x80; ++ ds3000_writereg(state, 0xa2, data); ++ ++ return 0; ++} ++ ++static void ds3000_release(struct dvb_frontend *fe) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 0); ++ ++ dprintk("%s\n", __func__); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops ds3000_ops; ++ ++struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct ds3000_state *state = NULL; ++ int ret; ++ ++ dprintk("%s\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct ds3000_state), GFP_KERNEL); ++ if (state == NULL) { ++ printk(KERN_ERR "Unable to kmalloc\n"); ++ goto error2; ++ } ++ ++ state->config = config; ++ state->i2c = i2c; ++ state->prevUCBS2 = 0; ++ ++ /* check if the demod is present */ ++ ret = ds3000_readreg(state, 0x00) & 0xfe; ++ if (ret != 0xe0) { ++ printk(KERN_ERR "Invalid probe, probably not a DS3000\n"); ++ goto error3; ++ } ++ ++ printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n", ++ ds3000_readreg(state, 0x02), ++ ds3000_readreg(state, 0x01)); ++ ++ memcpy(&state->frontend.ops, &ds3000_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error3: ++ kfree(state); ++error2: ++ return NULL; ++} ++EXPORT_SYMBOL(ds3000_attach); ++ ++static int ds3000_set_carrier_offset(struct dvb_frontend *fe, ++ s32 carrier_offset_khz) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ s32 tmp; ++ ++ tmp = carrier_offset_khz; ++ tmp *= 65536; ++ tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE); ++ ++ if (tmp < 0) ++ tmp += 65536; ++ ++ ds3000_writereg(state, 0x5f, tmp >> 8); ++ ds3000_writereg(state, 0x5e, tmp & 0xff); ++ ++ return 0; ++} ++ ++static int ds3000_set_frontend(struct dvb_frontend *fe) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ int i; ++ fe_status_t status; ++ s32 offset_khz; ++ u32 frequency; ++ u16 value; ++ ++ dprintk("%s() ", __func__); ++ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ /* Tune */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* ds3000 global reset */ ++ ds3000_writereg(state, 0x07, 0x80); ++ ds3000_writereg(state, 0x07, 0x00); ++ /* ds3000 build-in uC reset */ ++ ds3000_writereg(state, 0xb2, 0x01); ++ /* ds3000 software reset */ ++ ds3000_writereg(state, 0x00, 0x01); ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ /* initialise the demod in DVB-S mode */ ++ for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) ++ ds3000_writereg(state, ++ ds3000_dvbs_init_tab[i], ++ ds3000_dvbs_init_tab[i + 1]); ++ value = ds3000_readreg(state, 0xfe); ++ value &= 0xc0; ++ value |= 0x1b; ++ ds3000_writereg(state, 0xfe, value); ++ break; ++ case SYS_DVBS2: ++ /* initialise the demod in DVB-S2 mode */ ++ for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) ++ ds3000_writereg(state, ++ ds3000_dvbs2_init_tab[i], ++ ds3000_dvbs2_init_tab[i + 1]); ++ if (c->symbol_rate >= 30000000) ++ ds3000_writereg(state, 0xfe, 0x54); ++ else ++ ds3000_writereg(state, 0xfe, 0x98); ++ break; ++ default: ++ return 1; ++ } ++ ++ /* enable 27MHz clock output */ ++ ds3000_writereg(state, 0x29, 0x80); ++ /* enable ac coupling */ ++ ds3000_writereg(state, 0x25, 0x8a); ++ ++ /* enhance symbol rate performance */ ++ if ((c->symbol_rate / 1000) <= 5000) { ++ value = 29777 / (c->symbol_rate / 1000) + 1; ++ if (value % 2 != 0) ++ value++; ++ ds3000_writereg(state, 0xc3, 0x0d); ++ ds3000_writereg(state, 0xc8, value); ++ ds3000_writereg(state, 0xc4, 0x10); ++ ds3000_writereg(state, 0xc7, 0x0e); ++ } else if ((c->symbol_rate / 1000) <= 10000) { ++ value = 92166 / (c->symbol_rate / 1000) + 1; ++ if (value % 2 != 0) ++ value++; ++ ds3000_writereg(state, 0xc3, 0x07); ++ ds3000_writereg(state, 0xc8, value); ++ ds3000_writereg(state, 0xc4, 0x09); ++ ds3000_writereg(state, 0xc7, 0x12); ++ } else if ((c->symbol_rate / 1000) <= 20000) { ++ value = 64516 / (c->symbol_rate / 1000) + 1; ++ ds3000_writereg(state, 0xc3, value); ++ ds3000_writereg(state, 0xc8, 0x0e); ++ ds3000_writereg(state, 0xc4, 0x07); ++ ds3000_writereg(state, 0xc7, 0x18); ++ } else { ++ value = 129032 / (c->symbol_rate / 1000) + 1; ++ ds3000_writereg(state, 0xc3, value); ++ ds3000_writereg(state, 0xc8, 0x0a); ++ ds3000_writereg(state, 0xc4, 0x05); ++ ds3000_writereg(state, 0xc7, 0x24); ++ } ++ ++ /* normalized symbol rate rounded to the closest integer */ ++ value = (((c->symbol_rate / 1000) << 16) + ++ (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; ++ ds3000_writereg(state, 0x61, value & 0x00ff); ++ ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); ++ ++ /* co-channel interference cancellation disabled */ ++ ds3000_writereg(state, 0x56, 0x00); ++ ++ /* equalizer disabled */ ++ ds3000_writereg(state, 0x76, 0x00); ++ ++ /*ds3000_writereg(state, 0x08, 0x03); ++ ds3000_writereg(state, 0xfd, 0x22); ++ ds3000_writereg(state, 0x08, 0x07); ++ ds3000_writereg(state, 0xfd, 0x42); ++ ds3000_writereg(state, 0x08, 0x07);*/ ++ ++ if (state->config->ci_mode) { ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ default: ++ ds3000_writereg(state, 0xfd, 0x80); ++ break; ++ case SYS_DVBS2: ++ ds3000_writereg(state, 0xfd, 0x01); ++ break; ++ } ++ } ++ ++ /* ds3000 out of software reset */ ++ ds3000_writereg(state, 0x00, 0x00); ++ /* start ds3000 build-in uC */ ++ ds3000_writereg(state, 0xb2, 0x00); ++ ++ if (fe->ops.tuner_ops.get_frequency) { ++ fe->ops.tuner_ops.get_frequency(fe, &frequency); ++ offset_khz = frequency - c->frequency; ++ ds3000_set_carrier_offset(fe, offset_khz); ++ } ++ ++ for (i = 0; i < 30 ; i++) { ++ ds3000_read_status(fe, &status); ++ if (status & FE_HAS_LOCK) ++ break; ++ ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int ds3000_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ if (re_tune) { ++ int ret = ds3000_set_frontend(fe); ++ if (ret) ++ return ret; ++ } ++ ++ *delay = HZ / 5; ++ ++ return ds3000_read_status(fe, status); ++} ++ ++static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 0); ++ ++ dprintk("%s()\n", __func__); ++ return DVBFE_ALGO_HW; ++} ++ ++/* ++ * Initialise or wake up device ++ * ++ * Power config will reset and load initial firmware if required ++ */ ++static int ds3000_initfe(struct dvb_frontend *fe) ++{ ++ struct ds3000_state *state = fe->demodulator_priv; ++ int ret; ++ ++ dprintk("%s()\n", __func__); ++ /* hard reset */ ++ ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); ++ msleep(1); ++ ++ /* Load the firmware if required */ ++ ret = ds3000_firmware_ondemand(fe); ++ if (ret != 0) { ++ printk(KERN_ERR "%s: Unable initialize firmware\n", __func__); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static struct dvb_frontend_ops ds3000_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2 }, ++ .info = { ++ .name = "Montage Technology DS3000", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1011, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_QPSK | FE_CAN_RECOVER ++ }, ++ ++ .release = ds3000_release, ++ ++ .init = ds3000_initfe, ++ .i2c_gate_ctrl = ds3000_i2c_gate_ctrl, ++ .read_status = ds3000_read_status, ++ .read_ber = ds3000_read_ber, ++ .read_signal_strength = ds3000_read_signal_strength, ++ .read_snr = ds3000_read_snr, ++ .read_ucblocks = ds3000_read_ucblocks, ++ .set_voltage = ds3000_set_voltage, ++ .set_tone = ds3000_set_tone, ++ .diseqc_send_master_cmd = ds3000_send_diseqc_msg, ++ .diseqc_send_burst = ds3000_diseqc_send_burst, ++ .get_frontend_algo = ds3000_get_algo, ++ ++ .set_frontend = ds3000_set_frontend, ++ .tune = ds3000_tune, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " ++ "DS3000 hardware"); ++MODULE_AUTHOR("Konstantin Dimitrov "); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(DS3000_DEFAULT_FIRMWARE); +diff --git a/drivers/media/dvb-frontends/ds3000.h b/drivers/media/dvb-frontends/ds3000.h +new file mode 100644 +index 0000000..478ad66 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ds3000.h +@@ -0,0 +1,50 @@ ++/* ++ Montage Technology DS3000 - DVBS/S2 Demodulator driver ++ Copyright (C) 2009-2012 Konstantin Dimitrov ++ ++ Copyright (C) 2009-2012 TurboSight.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. ++ */ ++ ++#ifndef DS3000_H ++#define DS3000_H ++ ++#include ++ ++struct ds3000_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ u8 ci_mode; ++ /* Set device param to start dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++ /* Hook for Lock LED */ ++ void (*set_lock_led)(struct dvb_frontend *fe, int offon); ++}; ++ ++#if defined(CONFIG_DVB_DS3000) || \ ++ (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline ++struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_DS3000 */ ++#endif /* DS3000_H */ +diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c +new file mode 100644 +index 0000000..6d8fe88 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dvb-pll.c +@@ -0,0 +1,820 @@ ++/* ++ * descriptions + helper functions for simple dvb plls. ++ * ++ * (c) 2004 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb-pll.h" ++ ++struct dvb_pll_priv { ++ /* pll number */ ++ int nr; ++ ++ /* i2c details */ ++ int pll_i2c_address; ++ struct i2c_adapter *i2c; ++ ++ /* the PLL descriptor */ ++ struct dvb_pll_desc *pll_desc; ++ ++ /* cached frequency/bandwidth */ ++ u32 frequency; ++ u32 bandwidth; ++}; ++ ++#define DVB_PLL_MAX 64 ++ ++static unsigned int dvb_pll_devcount; ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable verbose debug messages"); ++ ++static unsigned int id[DVB_PLL_MAX] = ++ { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED }; ++module_param_array(id, int, NULL, 0644); ++MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)"); ++ ++/* ----------------------------------------------------------- */ ++ ++struct dvb_pll_desc { ++ char *name; ++ u32 min; ++ u32 max; ++ u32 iffreq; ++ void (*set)(struct dvb_frontend *fe, u8 *buf); ++ u8 *initdata; ++ u8 *initdata2; ++ u8 *sleepdata; ++ int count; ++ struct { ++ u32 limit; ++ u32 stepsize; ++ u8 config; ++ u8 cb; ++ } entries[12]; ++}; ++ ++/* ----------------------------------------------------------- */ ++/* descriptions */ ++ ++static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { ++ .name = "Thomson dtt7579", ++ .min = 177000000, ++ .max = 858000000, ++ .iffreq= 36166667, ++ .sleepdata = (u8[]){ 2, 0xb4, 0x03 }, ++ .count = 4, ++ .entries = { ++ { 443250000, 166667, 0xb4, 0x02 }, ++ { 542000000, 166667, 0xb4, 0x08 }, ++ { 771000000, 166667, 0xbc, 0x08 }, ++ { 999999999, 166667, 0xf4, 0x08 }, ++ }, ++}; ++ ++static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ if (bw == 7000000) ++ buf[3] |= 0x10; ++} ++ ++static struct dvb_pll_desc dvb_pll_thomson_dtt759x = { ++ .name = "Thomson dtt759x", ++ .min = 177000000, ++ .max = 896000000, ++ .set = thomson_dtt759x_bw, ++ .iffreq= 36166667, ++ .sleepdata = (u8[]){ 2, 0x84, 0x03 }, ++ .count = 5, ++ .entries = { ++ { 264000000, 166667, 0xb4, 0x02 }, ++ { 470000000, 166667, 0xbc, 0x02 }, ++ { 735000000, 166667, 0xbc, 0x08 }, ++ { 835000000, 166667, 0xf4, 0x08 }, ++ { 999999999, 166667, 0xfc, 0x08 }, ++ }, ++}; ++ ++static void thomson_dtt7520x_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ if (bw == 8000000) ++ buf[3] ^= 0x10; ++} ++ ++static struct dvb_pll_desc dvb_pll_thomson_dtt7520x = { ++ .name = "Thomson dtt7520x", ++ .min = 185000000, ++ .max = 900000000, ++ .set = thomson_dtt7520x_bw, ++ .iffreq = 36166667, ++ .count = 7, ++ .entries = { ++ { 305000000, 166667, 0xb4, 0x12 }, ++ { 405000000, 166667, 0xbc, 0x12 }, ++ { 445000000, 166667, 0xbc, 0x12 }, ++ { 465000000, 166667, 0xf4, 0x18 }, ++ { 735000000, 166667, 0xfc, 0x18 }, ++ { 835000000, 166667, 0xbc, 0x18 }, ++ { 999999999, 166667, 0xfc, 0x18 }, ++ }, ++}; ++ ++static struct dvb_pll_desc dvb_pll_lg_z201 = { ++ .name = "LG z201", ++ .min = 174000000, ++ .max = 862000000, ++ .iffreq= 36166667, ++ .sleepdata = (u8[]){ 2, 0xbc, 0x03 }, ++ .count = 5, ++ .entries = { ++ { 157500000, 166667, 0xbc, 0x01 }, ++ { 443250000, 166667, 0xbc, 0x02 }, ++ { 542000000, 166667, 0xbc, 0x04 }, ++ { 830000000, 166667, 0xf4, 0x04 }, ++ { 999999999, 166667, 0xfc, 0x04 }, ++ }, ++}; ++ ++static struct dvb_pll_desc dvb_pll_unknown_1 = { ++ .name = "unknown 1", /* used by dntv live dvb-t */ ++ .min = 174000000, ++ .max = 862000000, ++ .iffreq= 36166667, ++ .count = 9, ++ .entries = { ++ { 150000000, 166667, 0xb4, 0x01 }, ++ { 173000000, 166667, 0xbc, 0x01 }, ++ { 250000000, 166667, 0xb4, 0x02 }, ++ { 400000000, 166667, 0xbc, 0x02 }, ++ { 420000000, 166667, 0xf4, 0x02 }, ++ { 470000000, 166667, 0xfc, 0x02 }, ++ { 600000000, 166667, 0xbc, 0x08 }, ++ { 730000000, 166667, 0xf4, 0x08 }, ++ { 999999999, 166667, 0xfc, 0x08 }, ++ }, ++}; ++ ++/* Infineon TUA6010XS ++ * used in Thomson Cable Tuner ++ */ ++static struct dvb_pll_desc dvb_pll_tua6010xs = { ++ .name = "Infineon TUA6010XS", ++ .min = 44250000, ++ .max = 858000000, ++ .iffreq= 36125000, ++ .count = 3, ++ .entries = { ++ { 115750000, 62500, 0x8e, 0x03 }, ++ { 403250000, 62500, 0x8e, 0x06 }, ++ { 999999999, 62500, 0x8e, 0x85 }, ++ }, ++}; ++ ++/* Panasonic env57h1xd5 (some Philips PLL ?) */ ++static struct dvb_pll_desc dvb_pll_env57h1xd5 = { ++ .name = "Panasonic ENV57H1XD5", ++ .min = 44250000, ++ .max = 858000000, ++ .iffreq= 36125000, ++ .count = 4, ++ .entries = { ++ { 153000000, 166667, 0xc2, 0x41 }, ++ { 470000000, 166667, 0xc2, 0x42 }, ++ { 526000000, 166667, 0xc2, 0x84 }, ++ { 999999999, 166667, 0xc2, 0xa4 }, ++ }, ++}; ++ ++/* Philips TDA6650/TDA6651 ++ * used in Panasonic ENV77H11D5 ++ */ ++static void tda665x_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ if (bw == 8000000) ++ buf[3] |= 0x08; ++} ++ ++static struct dvb_pll_desc dvb_pll_tda665x = { ++ .name = "Philips TDA6650/TDA6651", ++ .min = 44250000, ++ .max = 858000000, ++ .set = tda665x_bw, ++ .iffreq= 36166667, ++ .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab }, ++ .count = 12, ++ .entries = { ++ { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, ++ { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, ++ { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, ++ { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, ++ { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, ++ { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, ++ { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, ++ { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, ++ { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, ++ { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, ++ { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, ++ { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, ++ } ++}; ++ ++/* Infineon TUA6034 ++ * used in LG TDTP E102P ++ */ ++static void tua6034_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ if (bw == 7000000) ++ buf[3] |= 0x08; ++} ++ ++static struct dvb_pll_desc dvb_pll_tua6034 = { ++ .name = "Infineon TUA6034", ++ .min = 44250000, ++ .max = 858000000, ++ .iffreq= 36166667, ++ .count = 3, ++ .set = tua6034_bw, ++ .entries = { ++ { 174500000, 62500, 0xce, 0x01 }, ++ { 230000000, 62500, 0xce, 0x02 }, ++ { 999999999, 62500, 0xce, 0x04 }, ++ }, ++}; ++ ++/* ALPS TDED4 ++ * used in Nebula-Cards and USB boxes ++ */ ++static void tded4_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ if (bw == 8000000) ++ buf[3] |= 0x04; ++} ++ ++static struct dvb_pll_desc dvb_pll_tded4 = { ++ .name = "ALPS TDED4", ++ .min = 47000000, ++ .max = 863000000, ++ .iffreq= 36166667, ++ .set = tded4_bw, ++ .count = 4, ++ .entries = { ++ { 153000000, 166667, 0x85, 0x01 }, ++ { 470000000, 166667, 0x85, 0x02 }, ++ { 823000000, 166667, 0x85, 0x08 }, ++ { 999999999, 166667, 0x85, 0x88 }, ++ } ++}; ++ ++/* ALPS TDHU2 ++ * used in AverTVHD MCE A180 ++ */ ++static struct dvb_pll_desc dvb_pll_tdhu2 = { ++ .name = "ALPS TDHU2", ++ .min = 54000000, ++ .max = 864000000, ++ .iffreq= 44000000, ++ .count = 4, ++ .entries = { ++ { 162000000, 62500, 0x85, 0x01 }, ++ { 426000000, 62500, 0x85, 0x02 }, ++ { 782000000, 62500, 0x85, 0x08 }, ++ { 999999999, 62500, 0x85, 0x88 }, ++ } ++}; ++ ++/* Samsung TBMV30111IN / TBMV30712IN1 ++ * used in Air2PC ATSC - 2nd generation (nxt2002) ++ */ ++static struct dvb_pll_desc dvb_pll_samsung_tbmv = { ++ .name = "Samsung TBMV30111IN / TBMV30712IN1", ++ .min = 54000000, ++ .max = 860000000, ++ .iffreq= 44000000, ++ .count = 6, ++ .entries = { ++ { 172000000, 166667, 0xb4, 0x01 }, ++ { 214000000, 166667, 0xb4, 0x02 }, ++ { 467000000, 166667, 0xbc, 0x02 }, ++ { 721000000, 166667, 0xbc, 0x08 }, ++ { 841000000, 166667, 0xf4, 0x08 }, ++ { 999999999, 166667, 0xfc, 0x02 }, ++ } ++}; ++ ++/* ++ * Philips SD1878 Tuner. ++ */ ++static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { ++ .name = "Philips SD1878", ++ .min = 950000, ++ .max = 2150000, ++ .iffreq= 249, /* zero-IF, offset 249 is to round up */ ++ .count = 4, ++ .entries = { ++ { 1250000, 500, 0xc4, 0x00}, ++ { 1450000, 500, 0xc4, 0x40}, ++ { 2050000, 500, 0xc4, 0x80}, ++ { 2150000, 500, 0xc4, 0xc0}, ++ }, ++}; ++ ++static void opera1_bw(struct dvb_frontend *fe, u8 *buf) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ u32 b_w = (c->symbol_rate * 27) / 32000; ++ struct i2c_msg msg = { ++ .addr = priv->pll_i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 4 ++ }; ++ int result; ++ u8 lpf; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ result = i2c_transfer(priv->i2c, &msg, 1); ++ if (result != 1) ++ printk(KERN_ERR "%s: i2c_transfer failed:%d", ++ __func__, result); ++ ++ if (b_w <= 10000) ++ lpf = 0xc; ++ else if (b_w <= 12000) ++ lpf = 0x2; ++ else if (b_w <= 14000) ++ lpf = 0xa; ++ else if (b_w <= 16000) ++ lpf = 0x6; ++ else if (b_w <= 18000) ++ lpf = 0xe; ++ else if (b_w <= 20000) ++ lpf = 0x1; ++ else if (b_w <= 22000) ++ lpf = 0x9; ++ else if (b_w <= 24000) ++ lpf = 0x5; ++ else if (b_w <= 26000) ++ lpf = 0xd; ++ else if (b_w <= 28000) ++ lpf = 0x3; ++ else ++ lpf = 0xb; ++ buf[2] ^= 0x1c; /* Flip bits 3-5 */ ++ /* Set lpf */ ++ buf[2] |= ((lpf >> 2) & 0x3) << 3; ++ buf[3] |= (lpf & 0x3) << 2; ++ ++ return; ++} ++ ++static struct dvb_pll_desc dvb_pll_opera1 = { ++ .name = "Opera Tuner", ++ .min = 900000, ++ .max = 2250000, ++ .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 }, ++ .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 }, ++ .iffreq= 0, ++ .set = opera1_bw, ++ .count = 8, ++ .entries = { ++ { 1064000, 500, 0xf9, 0xc2 }, ++ { 1169000, 500, 0xf9, 0xe2 }, ++ { 1299000, 500, 0xf9, 0x20 }, ++ { 1444000, 500, 0xf9, 0x40 }, ++ { 1606000, 500, 0xf9, 0x60 }, ++ { 1777000, 500, 0xf9, 0x80 }, ++ { 1941000, 500, 0xf9, 0xa0 }, ++ { 2250000, 500, 0xf9, 0xc0 }, ++ } ++}; ++ ++static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ struct i2c_msg msg = { ++ .addr = priv->pll_i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 4 ++ }; ++ int result; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ result = i2c_transfer(priv->i2c, &msg, 1); ++ if (result != 1) ++ printk(KERN_ERR "%s: i2c_transfer failed:%d", ++ __func__, result); ++ ++ buf[2] = 0x9e; ++ buf[3] = 0x90; ++ ++ return; ++} ++ ++/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ ++static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { ++ .name = "Samsung DTOS403IH102A", ++ .min = 44250000, ++ .max = 858000000, ++ .iffreq = 36125000, ++ .count = 8, ++ .set = samsung_dtos403ih102a_set, ++ .entries = { ++ { 135000000, 62500, 0xbe, 0x01 }, ++ { 177000000, 62500, 0xf6, 0x01 }, ++ { 370000000, 62500, 0xbe, 0x02 }, ++ { 450000000, 62500, 0xf6, 0x02 }, ++ { 466000000, 62500, 0xfe, 0x02 }, ++ { 538000000, 62500, 0xbe, 0x08 }, ++ { 826000000, 62500, 0xf6, 0x08 }, ++ { 999999999, 62500, 0xfe, 0x08 }, ++ } ++}; ++ ++/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ ++static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { ++ .name = "Samsung TDTC9251DH0", ++ .min = 48000000, ++ .max = 863000000, ++ .iffreq = 36166667, ++ .count = 3, ++ .entries = { ++ { 157500000, 166667, 0xcc, 0x09 }, ++ { 443000000, 166667, 0xcc, 0x0a }, ++ { 863000000, 166667, 0xcc, 0x08 }, ++ } ++}; ++ ++/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ ++static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { ++ .name = "Samsung TBDU18132", ++ .min = 950000, ++ .max = 2150000, /* guesses */ ++ .iffreq = 0, ++ .count = 2, ++ .entries = { ++ { 1550000, 125, 0x84, 0x82 }, ++ { 4095937, 125, 0x84, 0x80 }, ++ } ++ /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported ++ * by this driver. The two extra bits are 0x60 in the third byte. 15 ++ * bits is enough for over 4 GHz, which is enough to cover the range ++ * of this tuner. We could use the additional divisor bits by adding ++ * more entries, e.g. ++ { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, }, ++ { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, }, ++ { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ ++}; ++ ++/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ ++static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { ++ .name = "Samsung TBMU24112", ++ .min = 950000, ++ .max = 2150000, /* guesses */ ++ .iffreq = 0, ++ .count = 2, ++ .entries = { ++ { 1500000, 125, 0x84, 0x18 }, ++ { 9999999, 125, 0x84, 0x08 }, ++ } ++}; ++ ++/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */ ++/* byte 4 : 1 * * AGD R3 R2 R1 R0 ++ * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 ++ * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 ++ * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 ++ * 47 - 153 0 * 0 0 0 0 0 1 0x01 ++ * 153 - 430 0 * 0 0 0 0 1 0 0x02 ++ * 430 - 822 0 * 0 0 1 0 0 0 0x08 ++ * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ ++static struct dvb_pll_desc dvb_pll_alps_tdee4 = { ++ .name = "ALPS TDEE4", ++ .min = 47000000, ++ .max = 862000000, ++ .iffreq = 36125000, ++ .count = 4, ++ .entries = { ++ { 153000000, 62500, 0x95, 0x01 }, ++ { 430000000, 62500, 0x95, 0x02 }, ++ { 822000000, 62500, 0x95, 0x08 }, ++ { 999999999, 62500, 0x95, 0x88 }, ++ } ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++static struct dvb_pll_desc *pll_list[] = { ++ [DVB_PLL_UNDEFINED] = NULL, ++ [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, ++ [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, ++ [DVB_PLL_THOMSON_DTT7520X] = &dvb_pll_thomson_dtt7520x, ++ [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201, ++ [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1, ++ [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs, ++ [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5, ++ [DVB_PLL_TUA6034] = &dvb_pll_tua6034, ++ [DVB_PLL_TDA665X] = &dvb_pll_tda665x, ++ [DVB_PLL_TDED4] = &dvb_pll_tded4, ++ [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4, ++ [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, ++ [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, ++ [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, ++ [DVB_PLL_OPERA1] = &dvb_pll_opera1, ++ [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, ++ [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, ++ [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, ++ [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, ++}; ++ ++/* ----------------------------------------------------------- */ ++/* code */ ++ ++static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf, ++ const u32 frequency) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ struct dvb_pll_desc *desc = priv->pll_desc; ++ u32 div; ++ int i; ++ ++ if (frequency && (frequency < desc->min || frequency > desc->max)) ++ return -EINVAL; ++ ++ for (i = 0; i < desc->count; i++) { ++ if (frequency > desc->entries[i].limit) ++ continue; ++ break; ++ } ++ ++ if (debug) ++ printk("pll: %s: freq=%d | i=%d/%d\n", desc->name, ++ frequency, i, desc->count); ++ if (i == desc->count) ++ return -EINVAL; ++ ++ div = (frequency + desc->iffreq + ++ desc->entries[i].stepsize/2) / desc->entries[i].stepsize; ++ buf[0] = div >> 8; ++ buf[1] = div & 0xff; ++ buf[2] = desc->entries[i].config; ++ buf[3] = desc->entries[i].cb; ++ ++ if (desc->set) ++ desc->set(fe, buf); ++ ++ if (debug) ++ printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", ++ desc->name, div, buf[0], buf[1], buf[2], buf[3]); ++ ++ // calculate the frequency we set it to ++ return (div * desc->entries[i].stepsize) - desc->iffreq; ++} ++ ++static int dvb_pll_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int dvb_pll_sleep(struct dvb_frontend *fe) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ ++ if (priv->i2c == NULL) ++ return -EINVAL; ++ ++ if (priv->pll_desc->sleepdata) { ++ struct i2c_msg msg = { .flags = 0, ++ .addr = priv->pll_i2c_address, ++ .buf = priv->pll_desc->sleepdata + 1, ++ .len = priv->pll_desc->sleepdata[0] }; ++ ++ int result; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { ++ return result; ++ } ++ return 0; ++ } ++ /* Shouldn't be called when initdata is NULL, maybe BUG()? */ ++ return -EINVAL; ++} ++ ++static int dvb_pll_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ u8 buf[4]; ++ struct i2c_msg msg = ++ { .addr = priv->pll_i2c_address, .flags = 0, ++ .buf = buf, .len = sizeof(buf) }; ++ int result; ++ u32 frequency = 0; ++ ++ if (priv->i2c == NULL) ++ return -EINVAL; ++ ++ result = dvb_pll_configure(fe, buf, c->frequency); ++ if (result < 0) ++ return result; ++ else ++ frequency = result; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { ++ return result; ++ } ++ ++ priv->frequency = frequency; ++ priv->bandwidth = c->bandwidth_hz; ++ ++ return 0; ++} ++ ++static int dvb_pll_calc_regs(struct dvb_frontend *fe, ++ u8 *buf, int buf_len) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ int result; ++ u32 frequency = 0; ++ ++ if (buf_len < 5) ++ return -EINVAL; ++ ++ result = dvb_pll_configure(fe, buf + 1, c->frequency); ++ if (result < 0) ++ return result; ++ else ++ frequency = result; ++ ++ buf[0] = priv->pll_i2c_address; ++ ++ priv->frequency = frequency; ++ priv->bandwidth = c->bandwidth_hz; ++ ++ return 5; ++} ++ ++static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ *frequency = priv->frequency; ++ return 0; ++} ++ ++static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ *bandwidth = priv->bandwidth; ++ return 0; ++} ++ ++static int dvb_pll_init(struct dvb_frontend *fe) ++{ ++ struct dvb_pll_priv *priv = fe->tuner_priv; ++ ++ if (priv->i2c == NULL) ++ return -EINVAL; ++ ++ if (priv->pll_desc->initdata) { ++ struct i2c_msg msg = { .flags = 0, ++ .addr = priv->pll_i2c_address, ++ .buf = priv->pll_desc->initdata + 1, ++ .len = priv->pll_desc->initdata[0] }; ++ ++ int result; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ result = i2c_transfer(priv->i2c, &msg, 1); ++ if (result != 1) ++ return result; ++ if (priv->pll_desc->initdata2) { ++ msg.buf = priv->pll_desc->initdata2 + 1; ++ msg.len = priv->pll_desc->initdata2[0]; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ result = i2c_transfer(priv->i2c, &msg, 1); ++ if (result != 1) ++ return result; ++ } ++ return 0; ++ } ++ /* Shouldn't be called when initdata is NULL, maybe BUG()? */ ++ return -EINVAL; ++} ++ ++static struct dvb_tuner_ops dvb_pll_tuner_ops = { ++ .release = dvb_pll_release, ++ .sleep = dvb_pll_sleep, ++ .init = dvb_pll_init, ++ .set_params = dvb_pll_set_params, ++ .calc_regs = dvb_pll_calc_regs, ++ .get_frequency = dvb_pll_get_frequency, ++ .get_bandwidth = dvb_pll_get_bandwidth, ++}; ++ ++struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, ++ struct i2c_adapter *i2c, ++ unsigned int pll_desc_id) ++{ ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, ++ .buf = b1, .len = 1 }; ++ struct dvb_pll_priv *priv = NULL; ++ int ret; ++ struct dvb_pll_desc *desc; ++ ++ if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) && ++ (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list))) ++ pll_desc_id = id[dvb_pll_devcount]; ++ ++ BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list)); ++ ++ desc = pll_list[pll_desc_id]; ++ ++ if (i2c != NULL) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer (i2c, &msg, 1); ++ if (ret != 1) ++ return NULL; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->pll_i2c_address = pll_addr; ++ priv->i2c = i2c; ++ priv->pll_desc = desc; ++ priv->nr = dvb_pll_devcount++; ++ ++ memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ ++ strncpy(fe->ops.tuner_ops.info.name, desc->name, ++ sizeof(fe->ops.tuner_ops.info.name)); ++ fe->ops.tuner_ops.info.frequency_min = desc->min; ++ fe->ops.tuner_ops.info.frequency_max = desc->max; ++ if (!desc->initdata) ++ fe->ops.tuner_ops.init = NULL; ++ if (!desc->sleepdata) ++ fe->ops.tuner_ops.sleep = NULL; ++ ++ fe->tuner_priv = priv; ++ ++ if ((debug) || (id[priv->nr] == pll_desc_id)) { ++ printk("dvb-pll[%d]", priv->nr); ++ if (i2c != NULL) ++ printk(" %d-%04x", i2c_adapter_id(i2c), pll_addr); ++ printk(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name, ++ id[priv->nr] == pll_desc_id ? ++ "insmod option" : "autodetected"); ++ } ++ ++ return fe; ++} ++EXPORT_SYMBOL(dvb_pll_attach); ++ ++MODULE_DESCRIPTION("dvb pll library"); ++MODULE_AUTHOR("Gerd Knorr"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/dvb-pll.h b/drivers/media/dvb-frontends/dvb-pll.h +new file mode 100644 +index 0000000..4de754f +--- /dev/null ++++ b/drivers/media/dvb-frontends/dvb-pll.h +@@ -0,0 +1,57 @@ ++/* ++ * descriptions + helper functions for simple dvb plls. ++ */ ++ ++#ifndef __DVB_PLL_H__ ++#define __DVB_PLL_H__ ++ ++#include ++#include "dvb_frontend.h" ++ ++#define DVB_PLL_UNDEFINED 0 ++#define DVB_PLL_THOMSON_DTT7579 1 ++#define DVB_PLL_THOMSON_DTT759X 2 ++#define DVB_PLL_LG_Z201 3 ++#define DVB_PLL_UNKNOWN_1 4 ++#define DVB_PLL_TUA6010XS 5 ++#define DVB_PLL_ENV57H1XD5 6 ++#define DVB_PLL_TUA6034 7 ++#define DVB_PLL_TDA665X 8 ++#define DVB_PLL_TDED4 9 ++#define DVB_PLL_TDHU2 10 ++#define DVB_PLL_SAMSUNG_TBMV 11 ++#define DVB_PLL_PHILIPS_SD1878_TDA8261 12 ++#define DVB_PLL_OPERA1 13 ++#define DVB_PLL_SAMSUNG_DTOS403IH102A 14 ++#define DVB_PLL_SAMSUNG_TDTC9251DH0 15 ++#define DVB_PLL_SAMSUNG_TBDU18132 16 ++#define DVB_PLL_SAMSUNG_TBMU24112 17 ++#define DVB_PLL_TDEE4 18 ++#define DVB_PLL_THOMSON_DTT7520X 19 ++ ++/** ++ * Attach a dvb-pll to the supplied frontend structure. ++ * ++ * @param fe Frontend to attach to. ++ * @param pll_addr i2c address of the PLL (if used). ++ * @param i2c i2c adapter to use (set to NULL if not used). ++ * @param pll_desc_id dvb_pll_desc to use. ++ * @return Frontend pointer on success, NULL on failure ++ */ ++#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, ++ int pll_addr, ++ struct i2c_adapter *i2c, ++ unsigned int pll_desc_id); ++#else ++static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, ++ int pll_addr, ++ struct i2c_adapter *i2c, ++ unsigned int pll_desc_id) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.c b/drivers/media/dvb-frontends/dvb_dummy_fe.c +new file mode 100644 +index 0000000..d5acc30 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dvb_dummy_fe.c +@@ -0,0 +1,267 @@ ++/* ++ * Driver for Dummy Frontend ++ * ++ * Written by Emard ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "dvb_dummy_fe.h" ++ ++ ++struct dvb_dummy_fe_state { ++ struct dvb_frontend frontend; ++}; ++ ++ ++static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ *status = FE_HAS_SIGNAL ++ | FE_HAS_CARRIER ++ | FE_HAS_VITERBI ++ | FE_HAS_SYNC ++ | FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ *ber = 0; ++ return 0; ++} ++ ++static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ *strength = 0; ++ return 0; ++} ++ ++static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ *snr = 0; ++ return 0; ++} ++ ++static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ *ucblocks = 0; ++ return 0; ++} ++ ++/* ++ * Only needed if it actually reads something from the hardware ++ */ ++static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int dvb_dummy_fe_set_frontend(struct dvb_frontend *fe) ++{ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ return 0; ++} ++ ++static int dvb_dummy_fe_sleep(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int dvb_dummy_fe_init(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ return 0; ++} ++ ++static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ return 0; ++} ++ ++static void dvb_dummy_fe_release(struct dvb_frontend* fe) ++{ ++ struct dvb_dummy_fe_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops; ++ ++struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) ++{ ++ struct dvb_dummy_fe_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; ++ ++struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) ++{ ++ struct dvb_dummy_fe_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops dvb_dummy_fe_qam_ops; ++ ++struct dvb_frontend *dvb_dummy_fe_qam_attach(void) ++{ ++ struct dvb_dummy_fe_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Dummy DVB-T", ++ .frequency_min = 0, ++ .frequency_max = 863250000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = dvb_dummy_fe_release, ++ ++ .init = dvb_dummy_fe_init, ++ .sleep = dvb_dummy_fe_sleep, ++ ++ .set_frontend = dvb_dummy_fe_set_frontend, ++ .get_frontend = dvb_dummy_fe_get_frontend, ++ ++ .read_status = dvb_dummy_fe_read_status, ++ .read_ber = dvb_dummy_fe_read_ber, ++ .read_signal_strength = dvb_dummy_fe_read_signal_strength, ++ .read_snr = dvb_dummy_fe_read_snr, ++ .read_ucblocks = dvb_dummy_fe_read_ucblocks, ++}; ++ ++static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A }, ++ .info = { ++ .name = "Dummy DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 51000000, ++ .frequency_max = 858000000, ++ .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ ++ .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ ++ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | ++ FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO ++ }, ++ ++ .release = dvb_dummy_fe_release, ++ ++ .init = dvb_dummy_fe_init, ++ .sleep = dvb_dummy_fe_sleep, ++ ++ .set_frontend = dvb_dummy_fe_set_frontend, ++ .get_frontend = dvb_dummy_fe_get_frontend, ++ ++ .read_status = dvb_dummy_fe_read_status, ++ .read_ber = dvb_dummy_fe_read_ber, ++ .read_signal_strength = dvb_dummy_fe_read_signal_strength, ++ .read_snr = dvb_dummy_fe_read_snr, ++ .read_ucblocks = dvb_dummy_fe_read_ucblocks, ++}; ++ ++static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Dummy DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 250, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 29500, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .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 ++ }, ++ ++ .release = dvb_dummy_fe_release, ++ ++ .init = dvb_dummy_fe_init, ++ .sleep = dvb_dummy_fe_sleep, ++ ++ .set_frontend = dvb_dummy_fe_set_frontend, ++ .get_frontend = dvb_dummy_fe_get_frontend, ++ ++ .read_status = dvb_dummy_fe_read_status, ++ .read_ber = dvb_dummy_fe_read_ber, ++ .read_signal_strength = dvb_dummy_fe_read_signal_strength, ++ .read_snr = dvb_dummy_fe_read_snr, ++ .read_ucblocks = dvb_dummy_fe_read_ucblocks, ++ ++ .set_voltage = dvb_dummy_fe_set_voltage, ++ .set_tone = dvb_dummy_fe_set_tone, ++}; ++ ++MODULE_DESCRIPTION("DVB DUMMY Frontend"); ++MODULE_AUTHOR("Emard"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach); ++EXPORT_SYMBOL(dvb_dummy_fe_qam_attach); ++EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach); +diff --git a/drivers/media/dvb-frontends/dvb_dummy_fe.h b/drivers/media/dvb-frontends/dvb_dummy_fe.h +new file mode 100644 +index 0000000..1fcb987 +--- /dev/null ++++ b/drivers/media/dvb-frontends/dvb_dummy_fe.h +@@ -0,0 +1,51 @@ ++/* ++ * Driver for Dummy Frontend ++ * ++ * Written by Emard ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef DVB_DUMMY_FE_H ++#define DVB_DUMMY_FE_H ++ ++#include ++#include "dvb_frontend.h" ++ ++#if defined(CONFIG_DVB_DUMMY_FE) || (defined(CONFIG_DVB_DUMMY_FE_MODULE) && \ ++defined(MODULE)) ++extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void); ++extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void); ++extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void); ++#else ++static inline struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline struct dvb_frontend *dvb_dummy_fe_qam_attach(void) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_DUMMY_FE */ ++ ++#endif // DVB_DUMMY_FE_H +diff --git a/drivers/media/dvb-frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c +new file mode 100644 +index 0000000..9d42480 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ec100.c +@@ -0,0 +1,345 @@ ++/* ++ * E3C EC100 demodulator driver ++ * ++ * Copyright (C) 2009 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include "dvb_frontend.h" ++#include "ec100.h" ++ ++struct ec100_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend frontend; ++ struct ec100_config config; ++ ++ u16 ber; ++}; ++ ++/* write single register */ ++static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val) ++{ ++ int ret; ++ u8 buf[2] = {reg, val}; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = state->config.demod_address, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&state->i2c->dev, "%s: i2c wr failed=%d reg=%02x\n", ++ KBUILD_MODNAME, ret, reg); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++/* read single register */ ++static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val) ++{ ++ int ret; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = state->config.demod_address, ++ .flags = 0, ++ .len = 1, ++ .buf = ® ++ }, { ++ .addr = state->config.demod_address, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = val ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&state->i2c->dev, "%s: i2c rd failed=%d reg=%02x\n", ++ KBUILD_MODNAME, ret, reg); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++static int ec100_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct ec100_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp, tmp2; ++ ++ dev_dbg(&state->i2c->dev, "%s: frequency=%d bandwidth_hz=%d\n", ++ __func__, c->frequency, c->bandwidth_hz); ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ ret = ec100_write_reg(state, 0x04, 0x06); ++ if (ret) ++ goto error; ++ ret = ec100_write_reg(state, 0x67, 0x58); ++ if (ret) ++ goto error; ++ ret = ec100_write_reg(state, 0x05, 0x18); ++ if (ret) ++ goto error; ++ ++ /* reg/bw | 6 | 7 | 8 ++ -------+------+------+------ ++ A 0x1b | 0xa1 | 0xe7 | 0x2c ++ A 0x1c | 0x55 | 0x63 | 0x72 ++ -------+------+------+------ ++ B 0x1b | 0xb7 | 0x00 | 0x49 ++ B 0x1c | 0x55 | 0x64 | 0x72 */ ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ tmp = 0xb7; ++ tmp2 = 0x55; ++ break; ++ case 7000000: ++ tmp = 0x00; ++ tmp2 = 0x64; ++ break; ++ case 8000000: ++ default: ++ tmp = 0x49; ++ tmp2 = 0x72; ++ } ++ ++ ret = ec100_write_reg(state, 0x1b, tmp); ++ if (ret) ++ goto error; ++ ret = ec100_write_reg(state, 0x1c, tmp2); ++ if (ret) ++ goto error; ++ ++ ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */ ++ if (ret) ++ goto error; ++ ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */ ++ if (ret) ++ goto error; ++ ++ ret = ec100_write_reg(state, 0x08, 0x24); ++ if (ret) ++ goto error; ++ ++ ret = ec100_write_reg(state, 0x00, 0x00); /* go */ ++ if (ret) ++ goto error; ++ ret = ec100_write_reg(state, 0x00, 0x20); /* go */ ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int ec100_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 300; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ ++ return 0; ++} ++ ++static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct ec100_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ *status = 0; ++ ++ ret = ec100_read_reg(state, 0x42, &tmp); ++ if (ret) ++ goto error; ++ ++ if (tmp & 0x80) { ++ /* bit7 set - have lock */ ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | ++ FE_HAS_SYNC | FE_HAS_LOCK; ++ } else { ++ ret = ec100_read_reg(state, 0x01, &tmp); ++ if (ret) ++ goto error; ++ ++ if (tmp & 0x10) { ++ /* bit4 set - have signal */ ++ *status |= FE_HAS_SIGNAL; ++ if (!(tmp & 0x01)) { ++ /* bit0 clear - have ~valid signal */ ++ *status |= FE_HAS_CARRIER | FE_HAS_VITERBI; ++ } ++ } ++ } ++ ++ return ret; ++error: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct ec100_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp, tmp2; ++ u16 ber2; ++ ++ *ber = 0; ++ ++ ret = ec100_read_reg(state, 0x65, &tmp); ++ if (ret) ++ goto error; ++ ret = ec100_read_reg(state, 0x66, &tmp2); ++ if (ret) ++ goto error; ++ ++ ber2 = (tmp2 << 8) | tmp; ++ ++ /* if counter overflow or clear */ ++ if (ber2 < state->ber) ++ *ber = ber2; ++ else ++ *ber = ber2 - state->ber; ++ ++ state->ber = ber2; ++ ++ return ret; ++error: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct ec100_state *state = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ ++ ret = ec100_read_reg(state, 0x24, &tmp); ++ if (ret) { ++ *strength = 0; ++ goto error; ++ } ++ ++ *strength = ((tmp << 8) | tmp); ++ ++ return ret; ++error: ++ dev_dbg(&state->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ *snr = 0; ++ return 0; ++} ++ ++static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ return 0; ++} ++ ++static void ec100_release(struct dvb_frontend *fe) ++{ ++ struct ec100_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops ec100_ops; ++ ++struct dvb_frontend *ec100_attach(const struct ec100_config *config, ++ struct i2c_adapter *i2c) ++{ ++ int ret; ++ struct ec100_state *state = NULL; ++ u8 tmp; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->config, config, sizeof(struct ec100_config)); ++ ++ /* check if the demod is there */ ++ ret = ec100_read_reg(state, 0x33, &tmp); ++ if (ret || tmp != 0x0b) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &ec100_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(ec100_attach); ++ ++static struct dvb_frontend_ops ec100_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "E3C EC100 DVB-T", ++ .caps = ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = ec100_release, ++ .set_frontend = ec100_set_frontend, ++ .get_tune_settings = ec100_get_tune_settings, ++ .read_status = ec100_read_status, ++ .read_ber = ec100_read_ber, ++ .read_signal_strength = ec100_read_signal_strength, ++ .read_snr = ec100_read_snr, ++ .read_ucblocks = ec100_read_ucblocks, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/ec100.h b/drivers/media/dvb-frontends/ec100.h +new file mode 100644 +index 0000000..b847971 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ec100.h +@@ -0,0 +1,46 @@ ++/* ++ * E3C EC100 demodulator driver ++ * ++ * Copyright (C) 2009 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef EC100_H ++#define EC100_H ++ ++#include ++ ++struct ec100_config { ++ /* demodulator's I2C address */ ++ u8 demod_address; ++}; ++ ++ ++#if defined(CONFIG_DVB_EC100) || \ ++ (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *ec100_attach( ++ const struct ec100_config *config, struct i2c_adapter *i2c) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* EC100_H */ +diff --git a/drivers/media/dvb-frontends/eds1547.h b/drivers/media/dvb-frontends/eds1547.h +new file mode 100644 +index 0000000..c983f2f +--- /dev/null ++++ b/drivers/media/dvb-frontends/eds1547.h +@@ -0,0 +1,133 @@ ++/* eds1547.h Earda EDS-1547 tuner support ++* ++* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) ++* ++* This program is free software; you can redistribute it and/or modify it ++* under the terms of the GNU General Public License as published by the ++* Free Software Foundation, version 2. ++* ++* see Documentation/dvb/README.dvb-usb for more information ++*/ ++ ++#ifndef EDS1547 ++#define EDS1547 ++ ++static u8 stv0288_earda_inittab[] = { ++ 0x01, 0x57, ++ 0x02, 0x20, ++ 0x03, 0x8e, ++ 0x04, 0x8e, ++ 0x05, 0x12, ++ 0x06, 0x00, ++ 0x07, 0x00, ++ 0x09, 0x00, ++ 0x0a, 0x04, ++ 0x0b, 0x00, ++ 0x0c, 0x00, ++ 0x0d, 0x00, ++ 0x0e, 0xd4, ++ 0x0f, 0x30, ++ 0x11, 0x44, ++ 0x12, 0x03, ++ 0x13, 0x48, ++ 0x14, 0x84, ++ 0x15, 0x45, ++ 0x16, 0xb7, ++ 0x17, 0x9c, ++ 0x18, 0x00, ++ 0x19, 0xa6, ++ 0x1a, 0x88, ++ 0x1b, 0x8f, ++ 0x1c, 0xf0, ++ 0x20, 0x0b, ++ 0x21, 0x54, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x2b, 0xff, ++ 0x2c, 0xf7, ++ 0x30, 0x00, ++ 0x31, 0x1e, ++ 0x32, 0x14, ++ 0x33, 0x0f, ++ 0x34, 0x09, ++ 0x35, 0x0c, ++ 0x36, 0x05, ++ 0x37, 0x2f, ++ 0x38, 0x16, ++ 0x39, 0xbd, ++ 0x3a, 0x00, ++ 0x3b, 0x13, ++ 0x3c, 0x11, ++ 0x3d, 0x30, ++ 0x40, 0x63, ++ 0x41, 0x04, ++ 0x42, 0x20, ++ 0x43, 0x00, ++ 0x44, 0x00, ++ 0x45, 0x00, ++ 0x46, 0x00, ++ 0x47, 0x00, ++ 0x4a, 0x00, ++ 0x50, 0x10, ++ 0x51, 0x36, ++ 0x52, 0x09, ++ 0x53, 0x94, ++ 0x54, 0x62, ++ 0x55, 0x29, ++ 0x56, 0x64, ++ 0x57, 0x2b, ++ 0x58, 0x54, ++ 0x59, 0x86, ++ 0x5a, 0x00, ++ 0x5b, 0x9b, ++ 0x5c, 0x08, ++ 0x5d, 0x7f, ++ 0x5e, 0x00, ++ 0x5f, 0xff, ++ 0x70, 0x00, ++ 0x71, 0x00, ++ 0x72, 0x00, ++ 0x74, 0x00, ++ 0x75, 0x00, ++ 0x76, 0x00, ++ 0x81, 0x00, ++ 0x82, 0x3f, ++ 0x83, 0x3f, ++ 0x84, 0x00, ++ 0x85, 0x00, ++ 0x88, 0x00, ++ 0x89, 0x00, ++ 0x8a, 0x00, ++ 0x8b, 0x00, ++ 0x8c, 0x00, ++ 0x90, 0x00, ++ 0x91, 0x00, ++ 0x92, 0x00, ++ 0x93, 0x00, ++ 0x94, 0x1c, ++ 0x97, 0x00, ++ 0xa0, 0x48, ++ 0xa1, 0x00, ++ 0xb0, 0xb8, ++ 0xb1, 0x3a, ++ 0xb2, 0x10, ++ 0xb3, 0x82, ++ 0xb4, 0x80, ++ 0xb5, 0x82, ++ 0xb6, 0x82, ++ 0xb7, 0x82, ++ 0xb8, 0x20, ++ 0xb9, 0x00, ++ 0xf0, 0x00, ++ 0xf1, 0x00, ++ 0xf2, 0xc0, ++ 0xff,0xff, ++}; ++ ++static struct stv0288_config earda_config = { ++ .demod_address = 0x68, ++ .min_delay_ms = 100, ++ .inittab = stv0288_earda_inittab, ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c +new file mode 100644 +index 0000000..d7b9d54 +--- /dev/null ++++ b/drivers/media/dvb-frontends/hd29l2.c +@@ -0,0 +1,866 @@ ++/* ++ * HDIC HD29L2 DMB-TH demodulator driver ++ * ++ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D ++ * ++ * Author: Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "hd29l2_priv.h" ++ ++/* write multiple registers */ ++static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) ++{ ++ int ret; ++ u8 buf[2 + len]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = 0x00; ++ buf[1] = reg; ++ memcpy(&buf[2], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, ++ "%s: i2c wr failed=%d reg=%02x len=%d\n", ++ KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++/* read multiple registers */ ++static int hd29l2_rd_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) ++{ ++ int ret; ++ u8 buf[2] = { 0x00, reg }; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = 2, ++ .buf = buf, ++ }, { ++ .addr = priv->cfg.i2c_addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = val, ++ } ++ }; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, ++ "%s: i2c rd failed=%d reg=%02x len=%d\n", ++ KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ ++ return ret; ++} ++ ++/* write single register */ ++static int hd29l2_wr_reg(struct hd29l2_priv *priv, u8 reg, u8 val) ++{ ++ return hd29l2_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register */ ++static int hd29l2_rd_reg(struct hd29l2_priv *priv, u8 reg, u8 *val) ++{ ++ return hd29l2_rd_regs(priv, reg, val, 1); ++} ++ ++/* write single register with mask */ ++static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = hd29l2_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return hd29l2_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register with mask */ ++int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ ret = hd29l2_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ tmp &= mask; ++ ++ /* find position of the first bit */ ++ for (i = 0; i < 8; i++) { ++ if ((mask >> i) & 0x01) ++ break; ++ } ++ *val = tmp >> i; ++ ++ return 0; ++} ++ ++static int hd29l2_soft_reset(struct hd29l2_priv *priv) ++{ ++ int ret; ++ u8 tmp; ++ ++ ret = hd29l2_rd_reg(priv, 0x26, &tmp); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_wr_reg(priv, 0x26, 0x0d); ++ if (ret) ++ goto err; ++ ++ usleep_range(10000, 20000); ++ ++ ret = hd29l2_wr_reg(priv, 0x26, tmp); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ int ret, i; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 tmp; ++ ++ dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable); ++ ++ /* set tuner address for demod */ ++ if (!priv->tuner_i2c_addr_programmed && enable) { ++ /* no need to set tuner address every time, once is enough */ ++ ret = hd29l2_wr_reg(priv, 0x9d, priv->cfg.tuner_i2c_addr << 1); ++ if (ret) ++ goto err; ++ ++ priv->tuner_i2c_addr_programmed = true; ++ } ++ ++ /* open / close gate */ ++ ret = hd29l2_wr_reg(priv, 0x9f, enable); ++ if (ret) ++ goto err; ++ ++ /* wait demod ready */ ++ for (i = 10; i; i--) { ++ ret = hd29l2_rd_reg(priv, 0x9e, &tmp); ++ if (ret) ++ goto err; ++ ++ if (tmp == enable) ++ break; ++ ++ usleep_range(5000, 10000); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ int ret; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 buf[2]; ++ ++ *status = 0; ++ ++ ret = hd29l2_rd_reg(priv, 0x05, &buf[0]); ++ if (ret) ++ goto err; ++ ++ if (buf[0] & 0x01) { ++ /* full lock */ ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | ++ FE_HAS_SYNC | FE_HAS_LOCK; ++ } else { ++ ret = hd29l2_rd_reg(priv, 0x0d, &buf[1]); ++ if (ret) ++ goto err; ++ ++ if ((buf[1] & 0xfe) == 0x78) ++ /* partial lock */ ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC; ++ } ++ ++ priv->fe_status = *status; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ int ret; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 buf[2]; ++ u16 tmp; ++ ++ if (!(priv->fe_status & FE_HAS_LOCK)) { ++ *snr = 0; ++ ret = 0; ++ goto err; ++ } ++ ++ ret = hd29l2_rd_regs(priv, 0x0b, buf, 2); ++ if (ret) ++ goto err; ++ ++ tmp = (buf[0] << 8) | buf[1]; ++ ++ /* report SNR in dB * 10 */ ++ #define LOG10_20736_24 72422627 /* log10(20736) << 24 */ ++ if (tmp) ++ *snr = (LOG10_20736_24 - intlog10(tmp)) / ((1 << 24) / 100); ++ else ++ *snr = 0; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ int ret; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 buf[2]; ++ u16 tmp; ++ ++ *strength = 0; ++ ++ ret = hd29l2_rd_regs(priv, 0xd5, buf, 2); ++ if (ret) ++ goto err; ++ ++ tmp = buf[0] << 8 | buf[1]; ++ tmp = ~tmp & 0x0fff; ++ ++ /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ ++ *strength = tmp * 0xffff / 0x0fff; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ int ret; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 buf[2]; ++ ++ if (!(priv->fe_status & FE_HAS_SYNC)) { ++ *ber = 0; ++ ret = 0; ++ goto err; ++ } ++ ++ ret = hd29l2_rd_regs(priv, 0xd9, buf, 2); ++ if (ret) { ++ *ber = 0; ++ goto err; ++ } ++ ++ /* LDPC BER */ ++ *ber = ((buf[0] & 0x0f) << 8) | buf[1]; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ /* no way to read? */ ++ *ucblocks = 0; ++ return 0; ++} ++ ++static enum dvbfe_search hd29l2_search(struct dvb_frontend *fe) ++{ ++ int ret, i; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 tmp, buf[3]; ++ u8 modulation, carrier, guard_interval, interleave, code_rate; ++ u64 num64; ++ u32 if_freq, if_ctl; ++ bool auto_mode; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d frequency=%d " \ ++ "bandwidth_hz=%d modulation=%d inversion=%d " \ ++ "fec_inner=%d guard_interval=%d\n", __func__, ++ c->delivery_system, c->frequency, c->bandwidth_hz, ++ c->modulation, c->inversion, c->fec_inner, ++ c->guard_interval); ++ ++ /* as for now we detect always params automatically */ ++ auto_mode = true; ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* get and program IF */ ++ if (fe->ops.tuner_ops.get_if_frequency) ++ fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); ++ else ++ if_freq = 0; ++ ++ if (if_freq) { ++ /* normal IF */ ++ ++ /* calc IF control value */ ++ num64 = if_freq; ++ num64 *= 0x800000; ++ num64 = div_u64(num64, HD29L2_XTAL); ++ num64 -= 0x800000; ++ if_ctl = num64; ++ ++ tmp = 0xfc; /* tuner type normal */ ++ } else { ++ /* zero IF */ ++ if_ctl = 0; ++ tmp = 0xfe; /* tuner type Zero-IF */ ++ } ++ ++ buf[0] = ((if_ctl >> 0) & 0xff); ++ buf[1] = ((if_ctl >> 8) & 0xff); ++ buf[2] = ((if_ctl >> 16) & 0xff); ++ ++ /* program IF control */ ++ ret = hd29l2_wr_regs(priv, 0x14, buf, 3); ++ if (ret) ++ goto err; ++ ++ /* program tuner type */ ++ ret = hd29l2_wr_reg(priv, 0xab, tmp); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&priv->i2c->dev, "%s: if_freq=%d if_ctl=%x\n", ++ __func__, if_freq, if_ctl); ++ ++ if (auto_mode) { ++ /* ++ * use auto mode ++ */ ++ ++ /* disable quick mode */ ++ ret = hd29l2_wr_reg_mask(priv, 0xac, 0 << 7, 0x80); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_wr_reg_mask(priv, 0x82, 1 << 1, 0x02); ++ if (ret) ++ goto err; ++ ++ /* enable auto mode */ ++ ret = hd29l2_wr_reg_mask(priv, 0x7d, 1 << 6, 0x40); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_wr_reg_mask(priv, 0x81, 1 << 3, 0x08); ++ if (ret) ++ goto err; ++ ++ /* soft reset */ ++ ret = hd29l2_soft_reset(priv); ++ if (ret) ++ goto err; ++ ++ /* detect modulation */ ++ for (i = 30; i; i--) { ++ msleep(100); ++ ++ ret = hd29l2_rd_reg(priv, 0x0d, &tmp); ++ if (ret) ++ goto err; ++ ++ if ((((tmp & 0xf0) >= 0x10) && ++ ((tmp & 0x0f) == 0x08)) || (tmp >= 0x2c)) ++ break; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) ++ /* detection failed */ ++ return DVBFE_ALGO_SEARCH_FAILED; ++ ++ /* read modulation */ ++ ret = hd29l2_rd_reg_mask(priv, 0x7d, &modulation, 0x07); ++ if (ret) ++ goto err; ++ } else { ++ /* ++ * use manual mode ++ */ ++ ++ modulation = HD29L2_QAM64; ++ carrier = HD29L2_CARRIER_MULTI; ++ guard_interval = HD29L2_PN945; ++ interleave = HD29L2_INTERLEAVER_420; ++ code_rate = HD29L2_CODE_RATE_08; ++ ++ tmp = (code_rate << 3) | modulation; ++ ret = hd29l2_wr_reg_mask(priv, 0x7d, tmp, 0x5f); ++ if (ret) ++ goto err; ++ ++ tmp = (carrier << 2) | guard_interval; ++ ret = hd29l2_wr_reg_mask(priv, 0x81, tmp, 0x0f); ++ if (ret) ++ goto err; ++ ++ tmp = interleave; ++ ret = hd29l2_wr_reg_mask(priv, 0x82, tmp, 0x03); ++ if (ret) ++ goto err; ++ } ++ ++ /* ensure modulation validy */ ++ /* 0=QAM4_NR, 1=QAM4, 2=QAM16, 3=QAM32, 4=QAM64 */ ++ if (modulation > (ARRAY_SIZE(reg_mod_vals_tab[0].val) - 1)) { ++ dev_dbg(&priv->i2c->dev, "%s: modulation=%d not valid\n", ++ __func__, modulation); ++ goto err; ++ } ++ ++ /* program registers according to modulation */ ++ for (i = 0; i < ARRAY_SIZE(reg_mod_vals_tab); i++) { ++ ret = hd29l2_wr_reg(priv, reg_mod_vals_tab[i].reg, ++ reg_mod_vals_tab[i].val[modulation]); ++ if (ret) ++ goto err; ++ } ++ ++ /* read guard interval */ ++ ret = hd29l2_rd_reg_mask(priv, 0x81, &guard_interval, 0x03); ++ if (ret) ++ goto err; ++ ++ /* read carrier mode */ ++ ret = hd29l2_rd_reg_mask(priv, 0x81, &carrier, 0x04); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&priv->i2c->dev, ++ "%s: modulation=%d guard_interval=%d carrier=%d\n", ++ __func__, modulation, guard_interval, carrier); ++ ++ if ((carrier == HD29L2_CARRIER_MULTI) && (modulation == HD29L2_QAM64) && ++ (guard_interval == HD29L2_PN945)) { ++ dev_dbg(&priv->i2c->dev, "%s: C=3780 && QAM64 && PN945\n", ++ __func__); ++ ++ ret = hd29l2_wr_reg(priv, 0x42, 0x33); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_wr_reg(priv, 0xdd, 0x01); ++ if (ret) ++ goto err; ++ } ++ ++ usleep_range(10000, 20000); ++ ++ /* soft reset */ ++ ret = hd29l2_soft_reset(priv); ++ if (ret) ++ goto err; ++ ++ /* wait demod lock */ ++ for (i = 30; i; i--) { ++ msleep(100); ++ ++ /* read lock bit */ ++ ret = hd29l2_rd_reg_mask(priv, 0x05, &tmp, 0x01); ++ if (ret) ++ goto err; ++ ++ if (tmp) ++ break; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) ++ return DVBFE_ALGO_SEARCH_AGAIN; ++ ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static int hd29l2_get_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static int hd29l2_get_frontend(struct dvb_frontend *fe) ++{ ++ int ret; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 buf[3]; ++ u32 if_ctl; ++ char *str_constellation, *str_code_rate, *str_constellation_code_rate, ++ *str_guard_interval, *str_carrier, *str_guard_interval_carrier, ++ *str_interleave, *str_interleave_; ++ ++ ret = hd29l2_rd_reg(priv, 0x7d, &buf[0]); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_rd_regs(priv, 0x81, &buf[1], 2); ++ if (ret) ++ goto err; ++ ++ /* constellation, 0x7d[2:0] */ ++ switch ((buf[0] >> 0) & 0x07) { ++ case 0: /* QAM4NR */ ++ str_constellation = "QAM4NR"; ++ c->modulation = QAM_AUTO; /* FIXME */ ++ break; ++ case 1: /* QAM4 */ ++ str_constellation = "QAM4"; ++ c->modulation = QPSK; /* FIXME */ ++ break; ++ case 2: ++ str_constellation = "QAM16"; ++ c->modulation = QAM_16; ++ break; ++ case 3: ++ str_constellation = "QAM32"; ++ c->modulation = QAM_32; ++ break; ++ case 4: ++ str_constellation = "QAM64"; ++ c->modulation = QAM_64; ++ break; ++ default: ++ str_constellation = "?"; ++ } ++ ++ /* LDPC code rate, 0x7d[4:3] */ ++ switch ((buf[0] >> 3) & 0x03) { ++ case 0: /* 0.4 */ ++ str_code_rate = "0.4"; ++ c->fec_inner = FEC_AUTO; /* FIXME */ ++ break; ++ case 1: /* 0.6 */ ++ str_code_rate = "0.6"; ++ c->fec_inner = FEC_3_5; ++ break; ++ case 2: /* 0.8 */ ++ str_code_rate = "0.8"; ++ c->fec_inner = FEC_4_5; ++ break; ++ default: ++ str_code_rate = "?"; ++ } ++ ++ /* constellation & code rate set, 0x7d[6] */ ++ switch ((buf[0] >> 6) & 0x01) { ++ case 0: ++ str_constellation_code_rate = "manual"; ++ break; ++ case 1: ++ str_constellation_code_rate = "auto"; ++ break; ++ default: ++ str_constellation_code_rate = "?"; ++ } ++ ++ /* frame header, 0x81[1:0] */ ++ switch ((buf[1] >> 0) & 0x03) { ++ case 0: /* PN945 */ ++ str_guard_interval = "PN945"; ++ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ ++ break; ++ case 1: /* PN595 */ ++ str_guard_interval = "PN595"; ++ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ ++ break; ++ case 2: /* PN420 */ ++ str_guard_interval = "PN420"; ++ c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ ++ break; ++ default: ++ str_guard_interval = "?"; ++ } ++ ++ /* carrier, 0x81[2] */ ++ switch ((buf[1] >> 2) & 0x01) { ++ case 0: ++ str_carrier = "C=1"; ++ break; ++ case 1: ++ str_carrier = "C=3780"; ++ break; ++ default: ++ str_carrier = "?"; ++ } ++ ++ /* frame header & carrier set, 0x81[3] */ ++ switch ((buf[1] >> 3) & 0x01) { ++ case 0: ++ str_guard_interval_carrier = "manual"; ++ break; ++ case 1: ++ str_guard_interval_carrier = "auto"; ++ break; ++ default: ++ str_guard_interval_carrier = "?"; ++ } ++ ++ /* interleave, 0x82[0] */ ++ switch ((buf[2] >> 0) & 0x01) { ++ case 0: ++ str_interleave = "M=720"; ++ break; ++ case 1: ++ str_interleave = "M=240"; ++ break; ++ default: ++ str_interleave = "?"; ++ } ++ ++ /* interleave set, 0x82[1] */ ++ switch ((buf[2] >> 1) & 0x01) { ++ case 0: ++ str_interleave_ = "manual"; ++ break; ++ case 1: ++ str_interleave_ = "auto"; ++ break; ++ default: ++ str_interleave_ = "?"; ++ } ++ ++ /* ++ * We can read out current detected NCO and use that value next ++ * time instead of calculating new value from targed IF. ++ * I think it will not effect receiver sensitivity but gaining lock ++ * after tune could be easier... ++ */ ++ ret = hd29l2_rd_regs(priv, 0xb1, &buf[0], 3); ++ if (ret) ++ goto err; ++ ++ if_ctl = (buf[0] << 16) | ((buf[1] - 7) << 8) | buf[2]; ++ ++ dev_dbg(&priv->i2c->dev, "%s: %s %s %s | %s %s %s | %s %s | NCO=%06x\n", ++ __func__, str_constellation, str_code_rate, ++ str_constellation_code_rate, str_guard_interval, ++ str_carrier, str_guard_interval_carrier, str_interleave, ++ str_interleave_, if_ctl); ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int hd29l2_init(struct dvb_frontend *fe) ++{ ++ int ret, i; ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ u8 tmp; ++ static const struct reg_val tab[] = { ++ { 0x3a, 0x06 }, ++ { 0x3b, 0x03 }, ++ { 0x3c, 0x04 }, ++ { 0xaf, 0x06 }, ++ { 0xb0, 0x1b }, ++ { 0x80, 0x64 }, ++ { 0x10, 0x38 }, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ ++ /* reset demod */ ++ /* it is recommended to HW reset chip using RST_N pin */ ++ if (fe->callback) { ++ ret = fe->callback(fe, DVB_FRONTEND_COMPONENT_DEMOD, 0, 0); ++ if (ret) ++ goto err; ++ ++ /* reprogramming needed because HW reset clears registers */ ++ priv->tuner_i2c_addr_programmed = false; ++ } ++ ++ /* init */ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = hd29l2_wr_reg(priv, tab[i].reg, tab[i].val); ++ if (ret) ++ goto err; ++ } ++ ++ /* TS params */ ++ ret = hd29l2_rd_reg(priv, 0x36, &tmp); ++ if (ret) ++ goto err; ++ ++ tmp &= 0x1b; ++ tmp |= priv->cfg.ts_mode; ++ ret = hd29l2_wr_reg(priv, 0x36, tmp); ++ if (ret) ++ goto err; ++ ++ ret = hd29l2_rd_reg(priv, 0x31, &tmp); ++ tmp &= 0xef; ++ ++ if (!(priv->cfg.ts_mode >> 7)) ++ /* set b4 for serial TS */ ++ tmp |= 0x10; ++ ++ ret = hd29l2_wr_reg(priv, 0x31, tmp); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static void hd29l2_release(struct dvb_frontend *fe) ++{ ++ struct hd29l2_priv *priv = fe->demodulator_priv; ++ kfree(priv); ++} ++ ++static struct dvb_frontend_ops hd29l2_ops; ++ ++struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config, ++ struct i2c_adapter *i2c) ++{ ++ int ret; ++ struct hd29l2_priv *priv = NULL; ++ u8 tmp; ++ ++ /* allocate memory for the internal state */ ++ priv = kzalloc(sizeof(struct hd29l2_priv), GFP_KERNEL); ++ if (priv == NULL) ++ goto err; ++ ++ /* setup the state */ ++ priv->i2c = i2c; ++ memcpy(&priv->cfg, config, sizeof(struct hd29l2_config)); ++ ++ ++ /* check if the demod is there */ ++ ret = hd29l2_rd_reg(priv, 0x00, &tmp); ++ if (ret) ++ goto err; ++ ++ /* create dvb_frontend */ ++ memcpy(&priv->fe.ops, &hd29l2_ops, sizeof(struct dvb_frontend_ops)); ++ priv->fe.demodulator_priv = priv; ++ ++ return &priv->fe; ++err: ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(hd29l2_attach); ++ ++static struct dvb_frontend_ops hd29l2_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "HDIC HD29L2 DMB-TH", ++ .frequency_min = 474000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 10000, ++ .caps = FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_BANDWIDTH_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER ++ }, ++ ++ .release = hd29l2_release, ++ ++ .init = hd29l2_init, ++ ++ .get_frontend_algo = hd29l2_get_frontend_algo, ++ .search = hd29l2_search, ++ .get_frontend = hd29l2_get_frontend, ++ ++ .read_status = hd29l2_read_status, ++ .read_snr = hd29l2_read_snr, ++ .read_signal_strength = hd29l2_read_signal_strength, ++ .read_ber = hd29l2_read_ber, ++ .read_ucblocks = hd29l2_read_ucblocks, ++ ++ .i2c_gate_ctrl = hd29l2_i2c_gate_ctrl, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("HDIC HD29L2 DMB-TH demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/hd29l2.h b/drivers/media/dvb-frontends/hd29l2.h +new file mode 100644 +index 0000000..4ad00d7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/hd29l2.h +@@ -0,0 +1,66 @@ ++/* ++ * HDIC HD29L2 DMB-TH demodulator driver ++ * ++ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D ++ * ++ * Author: Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef HD29L2_H ++#define HD29L2_H ++ ++#include ++ ++struct hd29l2_config { ++ /* ++ * demodulator I2C address ++ */ ++ u8 i2c_addr; ++ ++ /* ++ * tuner I2C address ++ * only needed when tuner is behind demod I2C-gate ++ */ ++ u8 tuner_i2c_addr; ++ ++ /* ++ * TS settings ++ */ ++#define HD29L2_TS_SERIAL 0x00 ++#define HD29L2_TS_PARALLEL 0x80 ++#define HD29L2_TS_CLK_NORMAL 0x40 ++#define HD29L2_TS_CLK_INVERTED 0x00 ++#define HD29L2_TS_CLK_GATED 0x20 ++#define HD29L2_TS_CLK_FREE 0x00 ++ u8 ts_mode; ++}; ++ ++ ++#if defined(CONFIG_DVB_HD29L2) || \ ++ (defined(CONFIG_DVB_HD29L2_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *hd29l2_attach( ++const struct hd29l2_config *config, struct i2c_adapter *i2c) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* HD29L2_H */ +diff --git a/drivers/media/dvb-frontends/hd29l2_priv.h b/drivers/media/dvb-frontends/hd29l2_priv.h +new file mode 100644 +index 0000000..4d571a2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/hd29l2_priv.h +@@ -0,0 +1,301 @@ ++/* ++ * HDIC HD29L2 DMB-TH demodulator driver ++ * ++ * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D ++ * ++ * Author: Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef HD29L2_PRIV ++#define HD29L2_PRIV ++ ++#include ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "hd29l2.h" ++ ++#define HD29L2_XTAL 30400000 /* Hz */ ++ ++ ++#define HD29L2_QAM4NR 0x00 ++#define HD29L2_QAM4 0x01 ++#define HD29L2_QAM16 0x02 ++#define HD29L2_QAM32 0x03 ++#define HD29L2_QAM64 0x04 ++ ++#define HD29L2_CODE_RATE_04 0x00 ++#define HD29L2_CODE_RATE_06 0x08 ++#define HD29L2_CODE_RATE_08 0x10 ++ ++#define HD29L2_PN945 0x00 ++#define HD29L2_PN595 0x01 ++#define HD29L2_PN420 0x02 ++ ++#define HD29L2_CARRIER_SINGLE 0x00 ++#define HD29L2_CARRIER_MULTI 0x01 ++ ++#define HD29L2_INTERLEAVER_720 0x00 ++#define HD29L2_INTERLEAVER_420 0x01 ++ ++struct reg_val { ++ u8 reg; ++ u8 val; ++}; ++ ++struct reg_mod_vals { ++ u8 reg; ++ u8 val[5]; ++}; ++ ++struct hd29l2_priv { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct hd29l2_config cfg; ++ u8 tuner_i2c_addr_programmed:1; ++ ++ fe_status_t fe_status; ++}; ++ ++static const struct reg_mod_vals reg_mod_vals_tab[] = { ++ /* REG, QAM4NR, QAM4,QAM16,QAM32,QAM64 */ ++ { 0x01, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, ++ { 0x02, { 0x07, 0x07, 0x07, 0x07, 0x07 } }, ++ { 0x03, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, ++ { 0x04, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x05, { 0x61, 0x60, 0x60, 0x61, 0x60 } }, ++ { 0x06, { 0xff, 0xff, 0xff, 0xff, 0xff } }, ++ { 0x07, { 0xff, 0xff, 0xff, 0xff, 0xff } }, ++ { 0x08, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x09, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x0a, { 0x15, 0x15, 0x03, 0x03, 0x03 } }, ++ { 0x0d, { 0x78, 0x78, 0x88, 0x78, 0x78 } }, ++ { 0x0e, { 0xa0, 0x90, 0xa0, 0xa0, 0xa0 } }, ++ { 0x0f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x10, { 0xa0, 0xa0, 0x58, 0x38, 0x38 } }, ++ { 0x11, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x12, { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a } }, ++ { 0x13, { 0xa2, 0xa2, 0xa2, 0xa2, 0xa2 } }, ++ { 0x17, { 0x40, 0x40, 0x40, 0x40, 0x40 } }, ++ { 0x18, { 0x21, 0x21, 0x42, 0x52, 0x42 } }, ++ { 0x19, { 0x21, 0x21, 0x62, 0x72, 0x62 } }, ++ { 0x1a, { 0x32, 0x43, 0xa9, 0xb9, 0xa9 } }, ++ { 0x1b, { 0x32, 0x43, 0xb9, 0xd8, 0xb9 } }, ++ { 0x1c, { 0x02, 0x02, 0x03, 0x02, 0x03 } }, ++ { 0x1d, { 0x0c, 0x0c, 0x01, 0x02, 0x02 } }, ++ { 0x1e, { 0x02, 0x02, 0x02, 0x01, 0x02 } }, ++ { 0x1f, { 0x02, 0x02, 0x01, 0x02, 0x04 } }, ++ { 0x20, { 0x01, 0x02, 0x01, 0x01, 0x01 } }, ++ { 0x21, { 0x08, 0x08, 0x0a, 0x0a, 0x0a } }, ++ { 0x22, { 0x06, 0x06, 0x04, 0x05, 0x05 } }, ++ { 0x23, { 0x06, 0x06, 0x05, 0x03, 0x05 } }, ++ { 0x24, { 0x08, 0x08, 0x05, 0x07, 0x07 } }, ++ { 0x25, { 0x16, 0x10, 0x10, 0x0a, 0x10 } }, ++ { 0x26, { 0x14, 0x14, 0x04, 0x04, 0x04 } }, ++ { 0x27, { 0x58, 0x58, 0x58, 0x5c, 0x58 } }, ++ { 0x28, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0x29, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0x2a, { 0x08, 0x0a, 0x08, 0x08, 0x08 } }, ++ { 0x2b, { 0x08, 0x08, 0x08, 0x08, 0x08 } }, ++ { 0x2c, { 0x06, 0x06, 0x06, 0x06, 0x06 } }, ++ { 0x2d, { 0x05, 0x06, 0x06, 0x06, 0x06 } }, ++ { 0x2e, { 0x21, 0x21, 0x21, 0x21, 0x21 } }, ++ { 0x2f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x30, { 0x14, 0x14, 0x14, 0x14, 0x14 } }, ++ { 0x33, { 0xb7, 0xb7, 0xb7, 0xb7, 0xb7 } }, ++ { 0x34, { 0x81, 0x81, 0x81, 0x81, 0x81 } }, ++ { 0x35, { 0x80, 0x80, 0x80, 0x80, 0x80 } }, ++ { 0x37, { 0x70, 0x70, 0x70, 0x70, 0x70 } }, ++ { 0x38, { 0x04, 0x04, 0x02, 0x02, 0x02 } }, ++ { 0x39, { 0x07, 0x07, 0x05, 0x05, 0x05 } }, ++ { 0x3a, { 0x06, 0x06, 0x06, 0x06, 0x06 } }, ++ { 0x3b, { 0x03, 0x03, 0x03, 0x03, 0x03 } }, ++ { 0x3c, { 0x07, 0x06, 0x04, 0x04, 0x04 } }, ++ { 0x3d, { 0xf0, 0xf0, 0xf0, 0xf0, 0x80 } }, ++ { 0x3e, { 0x60, 0x60, 0x60, 0x60, 0xff } }, ++ { 0x3f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x40, { 0x5b, 0x5b, 0x5b, 0x57, 0x50 } }, ++ { 0x41, { 0x30, 0x30, 0x30, 0x30, 0x18 } }, ++ { 0x42, { 0x20, 0x20, 0x20, 0x00, 0x30 } }, ++ { 0x43, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x44, { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f } }, ++ { 0x45, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x46, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0x47, { 0x00, 0x00, 0x95, 0x00, 0x95 } }, ++ { 0x48, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } }, ++ { 0x49, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } }, ++ { 0x4a, { 0x40, 0x40, 0x33, 0x11, 0x11 } }, ++ { 0x4b, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, ++ { 0x4c, { 0x40, 0x40, 0x99, 0x11, 0x11 } }, ++ { 0x4d, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, ++ { 0x4e, { 0x40, 0x40, 0x66, 0x77, 0x77 } }, ++ { 0x4f, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, ++ { 0x50, { 0x40, 0x40, 0x88, 0x33, 0x11 } }, ++ { 0x51, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, ++ { 0x52, { 0x40, 0x40, 0x88, 0x02, 0x02 } }, ++ { 0x53, { 0x40, 0x40, 0x00, 0x02, 0x02 } }, ++ { 0x54, { 0x00, 0x00, 0x88, 0x33, 0x33 } }, ++ { 0x55, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, ++ { 0x56, { 0x00, 0x00, 0x00, 0x0b, 0x00 } }, ++ { 0x57, { 0x40, 0x40, 0x0a, 0x0b, 0x0a } }, ++ { 0x58, { 0xaa, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x59, { 0x7a, 0x40, 0x02, 0x02, 0x02 } }, ++ { 0x5a, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, ++ { 0x5b, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, ++ { 0x5c, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, ++ { 0x5d, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, ++ { 0x5e, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } }, ++ { 0x5f, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } }, ++ { 0x60, { 0x40, 0x40, 0x00, 0x30, 0x30 } }, ++ { 0x61, { 0x40, 0x40, 0x10, 0x30, 0x30 } }, ++ { 0x62, { 0x40, 0x40, 0x00, 0x30, 0x30 } }, ++ { 0x63, { 0x40, 0x40, 0x05, 0x30, 0x30 } }, ++ { 0x64, { 0x40, 0x40, 0x06, 0x00, 0x30 } }, ++ { 0x65, { 0x40, 0x40, 0x06, 0x08, 0x30 } }, ++ { 0x66, { 0x40, 0x40, 0x00, 0x00, 0x20 } }, ++ { 0x67, { 0x40, 0x40, 0x01, 0x04, 0x20 } }, ++ { 0x68, { 0x00, 0x00, 0x30, 0x00, 0x20 } }, ++ { 0x69, { 0xa0, 0xa0, 0x00, 0x08, 0x20 } }, ++ { 0x6a, { 0x00, 0x00, 0x30, 0x00, 0x25 } }, ++ { 0x6b, { 0xa0, 0xa0, 0x00, 0x06, 0x25 } }, ++ { 0x6c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x6d, { 0xa0, 0x60, 0x0c, 0x03, 0x0c } }, ++ { 0x6e, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x6f, { 0xa0, 0x60, 0x04, 0x01, 0x04 } }, ++ { 0x70, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } }, ++ { 0x71, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } }, ++ { 0x72, { 0x58, 0x58, 0xff, 0xff, 0xff } }, ++ { 0x73, { 0x58, 0x58, 0xff, 0xff, 0xff } }, ++ { 0x74, { 0x06, 0x06, 0x09, 0x05, 0x05 } }, ++ { 0x75, { 0x06, 0x06, 0x0a, 0x10, 0x10 } }, ++ { 0x76, { 0x10, 0x10, 0x06, 0x0a, 0x0a } }, ++ { 0x77, { 0x12, 0x18, 0x28, 0x10, 0x28 } }, ++ { 0x78, { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8 } }, ++ { 0x79, { 0x15, 0x15, 0x03, 0x03, 0x03 } }, ++ { 0x7a, { 0x02, 0x02, 0x01, 0x04, 0x03 } }, ++ { 0x7b, { 0x01, 0x02, 0x03, 0x03, 0x03 } }, ++ { 0x7c, { 0x28, 0x28, 0x28, 0x28, 0x28 } }, ++ { 0x7f, { 0x25, 0x92, 0x5f, 0x17, 0x2d } }, ++ { 0x80, { 0x64, 0x64, 0x64, 0x74, 0x64 } }, ++ { 0x83, { 0x06, 0x03, 0x04, 0x04, 0x04 } }, ++ { 0x84, { 0xff, 0xff, 0xff, 0xff, 0xff } }, ++ { 0x85, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, ++ { 0x86, { 0x00, 0x00, 0x11, 0x11, 0x11 } }, ++ { 0x87, { 0x03, 0x03, 0x03, 0x03, 0x03 } }, ++ { 0x88, { 0x09, 0x09, 0x09, 0x09, 0x09 } }, ++ { 0x89, { 0x20, 0x20, 0x30, 0x20, 0x20 } }, ++ { 0x8a, { 0x03, 0x03, 0x02, 0x03, 0x02 } }, ++ { 0x8b, { 0x00, 0x07, 0x09, 0x00, 0x09 } }, ++ { 0x8c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x8d, { 0x4f, 0x4f, 0x4f, 0x3f, 0x4f } }, ++ { 0x8e, { 0xf0, 0xf0, 0x60, 0xf0, 0xa0 } }, ++ { 0x8f, { 0xe8, 0xe8, 0xe8, 0xe8, 0xe8 } }, ++ { 0x90, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, ++ { 0x91, { 0x40, 0x40, 0x70, 0x70, 0x10 } }, ++ { 0x92, { 0x00, 0x00, 0x00, 0x00, 0x04 } }, ++ { 0x93, { 0x60, 0x60, 0x60, 0x60, 0x60 } }, ++ { 0x94, { 0x00, 0x00, 0x00, 0x00, 0x03 } }, ++ { 0x95, { 0x09, 0x09, 0x47, 0x47, 0x47 } }, ++ { 0x96, { 0x80, 0xa0, 0xa0, 0x40, 0xa0 } }, ++ { 0x97, { 0x60, 0x60, 0x60, 0x60, 0x60 } }, ++ { 0x98, { 0x50, 0x50, 0x50, 0x30, 0x50 } }, ++ { 0x99, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, ++ { 0x9a, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0x9b, { 0x40, 0x40, 0x40, 0x30, 0x40 } }, ++ { 0x9c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa0, { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 } }, ++ { 0xa1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa2, { 0x30, 0x30, 0x00, 0x30, 0x00 } }, ++ { 0xa3, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa4, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa5, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xa8, { 0x77, 0x77, 0x77, 0x77, 0x77 } }, ++ { 0xa9, { 0x02, 0x02, 0x02, 0x02, 0x02 } }, ++ { 0xaa, { 0x40, 0x40, 0x40, 0x40, 0x40 } }, ++ { 0xac, { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f } }, ++ { 0xad, { 0x14, 0x14, 0x14, 0x14, 0x14 } }, ++ { 0xae, { 0x78, 0x78, 0x78, 0x78, 0x78 } }, ++ { 0xaf, { 0x06, 0x06, 0x06, 0x06, 0x07 } }, ++ { 0xb0, { 0x1b, 0x1b, 0x1b, 0x19, 0x1b } }, ++ { 0xb1, { 0x18, 0x17, 0x17, 0x18, 0x17 } }, ++ { 0xb2, { 0x35, 0x82, 0x82, 0x38, 0x82 } }, ++ { 0xb3, { 0xb6, 0xce, 0xc7, 0x5c, 0xb0 } }, ++ { 0xb4, { 0x3f, 0x3e, 0x3e, 0x3f, 0x3e } }, ++ { 0xb5, { 0x70, 0x58, 0x50, 0x68, 0x50 } }, ++ { 0xb6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xb7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xb8, { 0x03, 0x03, 0x01, 0x01, 0x01 } }, ++ { 0xb9, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xba, { 0x06, 0x06, 0x0a, 0x05, 0x0a } }, ++ { 0xbb, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xbc, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xbd, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xbe, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xbf, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc3, { 0x00, 0x00, 0x88, 0x66, 0x88 } }, ++ { 0xc4, { 0x10, 0x10, 0x00, 0x00, 0x00 } }, ++ { 0xc5, { 0x00, 0x00, 0x44, 0x60, 0x44 } }, ++ { 0xc6, { 0x10, 0x0a, 0x00, 0x00, 0x00 } }, ++ { 0xc7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc8, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xc9, { 0x90, 0x04, 0x00, 0x00, 0x00 } }, ++ { 0xca, { 0x90, 0x08, 0x01, 0x01, 0x01 } }, ++ { 0xcb, { 0xa0, 0x04, 0x00, 0x44, 0x00 } }, ++ { 0xcc, { 0xa0, 0x10, 0x03, 0x00, 0x03 } }, ++ { 0xcd, { 0x06, 0x06, 0x06, 0x05, 0x06 } }, ++ { 0xce, { 0x05, 0x05, 0x01, 0x01, 0x01 } }, ++ { 0xcf, { 0x40, 0x20, 0x18, 0x18, 0x18 } }, ++ { 0xd0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xd1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xd2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xd3, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xd4, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, ++ { 0xd5, { 0x05, 0x05, 0x05, 0x03, 0x05 } }, ++ { 0xd6, { 0xac, 0x22, 0xca, 0x8f, 0xca } }, ++ { 0xd7, { 0x20, 0x20, 0x20, 0x20, 0x20 } }, ++ { 0xd8, { 0x01, 0x01, 0x01, 0x01, 0x01 } }, ++ { 0xd9, { 0x00, 0x00, 0x0f, 0x00, 0x0f } }, ++ { 0xda, { 0x00, 0xff, 0xff, 0x0e, 0xff } }, ++ { 0xdb, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0xdc, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0xdd, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, ++ { 0xde, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0xdf, { 0x42, 0x42, 0x44, 0x44, 0x04 } }, ++ { 0xe0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xe1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xe2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xe3, { 0x00, 0x00, 0x26, 0x06, 0x26 } }, ++ { 0xe4, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xe5, { 0x01, 0x0a, 0x01, 0x01, 0x01 } }, ++ { 0xe6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xe7, { 0x08, 0x08, 0x08, 0x08, 0x08 } }, ++ { 0xe8, { 0x63, 0x63, 0x63, 0x63, 0x63 } }, ++ { 0xe9, { 0x59, 0x59, 0x59, 0x59, 0x59 } }, ++ { 0xea, { 0x80, 0x80, 0x20, 0x80, 0x80 } }, ++ { 0xeb, { 0x37, 0x37, 0x78, 0x37, 0x77 } }, ++ { 0xec, { 0x1f, 0x1f, 0x25, 0x25, 0x25 } }, ++ { 0xed, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, ++ { 0xee, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++ { 0xef, { 0x70, 0x70, 0x58, 0x38, 0x58 } }, ++ { 0xf0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, ++}; ++ ++#endif /* HD29L2_PRIV */ +diff --git a/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c +new file mode 100644 +index 0000000..0c642a5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6405.c +@@ -0,0 +1,164 @@ ++/* ++ * isl6405.c - driver for dual lnb supply and control ic ISL6405 ++ * ++ * Copyright (C) 2008 Hartmut Hackmann ++ * Copyright (C) 2006 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "isl6405.h" ++ ++struct isl6405 { ++ u8 config; ++ u8 override_or; ++ u8 override_and; ++ struct i2c_adapter *i2c; ++ u8 i2c_addr; ++}; ++ ++static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, ++ .buf = &isl6405->config, ++ .len = sizeof(isl6405->config) }; ++ ++ if (isl6405->override_or & 0x80) { ++ isl6405->config &= ~(ISL6405_VSEL2 | ISL6405_EN2); ++ switch (voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ isl6405->config |= ISL6405_EN2; ++ break; ++ case SEC_VOLTAGE_18: ++ isl6405->config |= (ISL6405_EN2 | ISL6405_VSEL2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } else { ++ isl6405->config &= ~(ISL6405_VSEL1 | ISL6405_EN1); ++ switch (voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ isl6405->config |= ISL6405_EN1; ++ break; ++ case SEC_VOLTAGE_18: ++ isl6405->config |= (ISL6405_EN1 | ISL6405_VSEL1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ } ++ isl6405->config |= isl6405->override_or; ++ isl6405->config &= isl6405->override_and; ++ ++ return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int isl6405_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) ++{ ++ struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, ++ .buf = &isl6405->config, ++ .len = sizeof(isl6405->config) }; ++ ++ if (isl6405->override_or & 0x80) { ++ if (arg) ++ isl6405->config |= ISL6405_LLC2; ++ else ++ isl6405->config &= ~ISL6405_LLC2; ++ } else { ++ if (arg) ++ isl6405->config |= ISL6405_LLC1; ++ else ++ isl6405->config &= ~ISL6405_LLC1; ++ } ++ isl6405->config |= isl6405->override_or; ++ isl6405->config &= isl6405->override_and; ++ ++ return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static void isl6405_release(struct dvb_frontend *fe) ++{ ++ /* power off */ ++ isl6405_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ /* free */ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, ++ u8 i2c_addr, u8 override_set, u8 override_clear) ++{ ++ struct isl6405 *isl6405 = kmalloc(sizeof(struct isl6405), GFP_KERNEL); ++ if (!isl6405) ++ return NULL; ++ ++ /* default configuration */ ++ if (override_set & 0x80) ++ isl6405->config = ISL6405_ISEL2; ++ else ++ isl6405->config = ISL6405_ISEL1; ++ isl6405->i2c = i2c; ++ isl6405->i2c_addr = i2c_addr; ++ fe->sec_priv = isl6405; ++ ++ /* bits which should be forced to '1' */ ++ isl6405->override_or = override_set; ++ ++ /* bits which should be forced to '0' */ ++ isl6405->override_and = ~override_clear; ++ ++ /* detect if it is present or not */ ++ if (isl6405_set_voltage(fe, SEC_VOLTAGE_OFF)) { ++ kfree(isl6405); ++ fe->sec_priv = NULL; ++ return NULL; ++ } ++ ++ /* install release callback */ ++ fe->ops.release_sec = isl6405_release; ++ ++ /* override frontend ops */ ++ fe->ops.set_voltage = isl6405_set_voltage; ++ fe->ops.enable_high_lnb_voltage = isl6405_enable_high_lnb_voltage; ++ ++ return fe; ++} ++EXPORT_SYMBOL(isl6405_attach); ++ ++MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405"); ++MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/isl6405.h b/drivers/media/dvb-frontends/isl6405.h +new file mode 100644 +index 0000000..1c793d3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6405.h +@@ -0,0 +1,74 @@ ++/* ++ * isl6405.h - driver for dual lnb supply and control ic ISL6405 ++ * ++ * Copyright (C) 2008 Hartmut Hackmann ++ * Copyright (C) 2006 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef _ISL6405_H ++#define _ISL6405_H ++ ++#include ++ ++/* system register bits */ ++ ++/* this bit selects register (control) 1 or 2 ++ note that the bit maps are different */ ++ ++#define ISL6405_SR 0x80 ++ ++/* SR = 0 */ ++#define ISL6405_OLF1 0x01 ++#define ISL6405_EN1 0x02 ++#define ISL6405_VSEL1 0x04 ++#define ISL6405_LLC1 0x08 ++#define ISL6405_ENT1 0x10 ++#define ISL6405_ISEL1 0x20 ++#define ISL6405_DCL 0x40 ++ ++/* SR = 1 */ ++#define ISL6405_OLF2 0x01 ++#define ISL6405_OTF 0x02 ++#define ISL6405_EN2 0x04 ++#define ISL6405_VSEL2 0x08 ++#define ISL6405_LLC2 0x10 ++#define ISL6405_ENT2 0x20 ++#define ISL6405_ISEL2 0x40 ++ ++#if defined(CONFIG_DVB_ISL6405) || (defined(CONFIG_DVB_ISL6405_MODULE) && defined(MODULE)) ++/* override_set and override_clear control which system register bits (above) ++ * to always set & clear ++ */ ++extern struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, ++ u8 i2c_addr, u8 override_set, u8 override_clear); ++#else ++static inline struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 i2c_addr, ++ u8 override_set, u8 override_clear) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_ISL6405 */ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c +new file mode 100644 +index 0000000..0cb3f0f +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6421.c +@@ -0,0 +1,141 @@ ++/* ++ * isl6421.h - driver for lnb supply and control ic ISL6421 ++ * ++ * Copyright (C) 2006 Andrew de Quincey ++ * Copyright (C) 2006 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "isl6421.h" ++ ++struct isl6421 { ++ u8 config; ++ u8 override_or; ++ u8 override_and; ++ struct i2c_adapter *i2c; ++ u8 i2c_addr; ++}; ++ ++static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, ++ .buf = &isl6421->config, ++ .len = sizeof(isl6421->config) }; ++ ++ isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); ++ ++ switch(voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ isl6421->config |= ISL6421_EN1; ++ break; ++ case SEC_VOLTAGE_18: ++ isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ isl6421->config |= isl6421->override_or; ++ isl6421->config &= isl6421->override_and; ++ ++ return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) ++{ ++ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, ++ .buf = &isl6421->config, ++ .len = sizeof(isl6421->config) }; ++ ++ if (arg) ++ isl6421->config |= ISL6421_LLC1; ++ else ++ isl6421->config &= ~ISL6421_LLC1; ++ ++ isl6421->config |= isl6421->override_or; ++ isl6421->config &= isl6421->override_and; ++ ++ return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static void isl6421_release(struct dvb_frontend *fe) ++{ ++ /* power off */ ++ isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ /* free */ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, ++ u8 override_set, u8 override_clear) ++{ ++ struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); ++ if (!isl6421) ++ return NULL; ++ ++ /* default configuration */ ++ isl6421->config = ISL6421_ISEL1; ++ isl6421->i2c = i2c; ++ isl6421->i2c_addr = i2c_addr; ++ fe->sec_priv = isl6421; ++ ++ /* bits which should be forced to '1' */ ++ isl6421->override_or = override_set; ++ ++ /* bits which should be forced to '0' */ ++ isl6421->override_and = ~override_clear; ++ ++ /* detect if it is present or not */ ++ if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { ++ kfree(isl6421); ++ fe->sec_priv = NULL; ++ return NULL; ++ } ++ ++ /* install release callback */ ++ fe->ops.release_sec = isl6421_release; ++ ++ /* override frontend ops */ ++ fe->ops.set_voltage = isl6421_set_voltage; ++ fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; ++ ++ return fe; ++} ++EXPORT_SYMBOL(isl6421_attach); ++ ++MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); ++MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/isl6421.h b/drivers/media/dvb-frontends/isl6421.h +new file mode 100644 +index 0000000..47e4518 +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6421.h +@@ -0,0 +1,55 @@ ++/* ++ * isl6421.h - driver for lnb supply and control ic ISL6421 ++ * ++ * Copyright (C) 2006 Andrew de Quincey ++ * Copyright (C) 2006 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef _ISL6421_H ++#define _ISL6421_H ++ ++#include ++ ++/* system register bits */ ++#define ISL6421_OLF1 0x01 ++#define ISL6421_EN1 0x02 ++#define ISL6421_VSEL1 0x04 ++#define ISL6421_LLC1 0x08 ++#define ISL6421_ENT1 0x10 ++#define ISL6421_ISEL1 0x20 ++#define ISL6421_DCL 0x40 ++ ++#if defined(CONFIG_DVB_ISL6421) || (defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE)) ++/* override_set and override_clear control which system register bits (above) to always set & clear */ ++extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, ++ u8 override_set, u8 override_clear); ++#else ++static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, ++ u8 override_set, u8 override_clear) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_ISL6421 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/isl6423.c b/drivers/media/dvb-frontends/isl6423.c +new file mode 100644 +index 0000000..dca5beb +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6423.c +@@ -0,0 +1,308 @@ ++/* ++ Intersil ISL6423 SEC and LNB Power supply controller ++ ++ Copyright (C) Manu Abraham ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "isl6423.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++ ++#define FE_ERROR 0 ++#define FE_NOTICE 1 ++#define FE_INFO 2 ++#define FE_DEBUG 3 ++#define FE_DEBUGREG 4 ++ ++#define dprintk(__y, __z, format, arg...) do { \ ++ if (__z) { \ ++ if ((verbose > FE_ERROR) && (verbose > __y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_NOTICE) && (verbose > __y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_INFO) && (verbose > __y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_DEBUG) && (verbose > __y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (verbose > __y) \ ++ printk(format, ##arg); \ ++ } \ ++} while (0) ++ ++struct isl6423_dev { ++ const struct isl6423_config *config; ++ struct i2c_adapter *i2c; ++ ++ u8 reg_3; ++ u8 reg_4; ++ ++ unsigned int verbose; ++}; ++ ++static int isl6423_write(struct isl6423_dev *isl6423, u8 reg) ++{ ++ struct i2c_adapter *i2c = isl6423->i2c; ++ u8 addr = isl6423->config->addr; ++ int err = 0; ++ ++ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = ®, .len = 1 }; ++ ++ dprintk(FE_DEBUG, 1, "write reg %02X", reg); ++ err = i2c_transfer(i2c, &msg, 1); ++ if (err < 0) ++ goto exit; ++ return 0; ++ ++exit: ++ dprintk(FE_ERROR, 1, "I/O error <%d>", err); ++ return err; ++} ++ ++static int isl6423_set_modulation(struct dvb_frontend *fe) ++{ ++ struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; ++ const struct isl6423_config *config = isl6423->config; ++ int err = 0; ++ u8 reg_2 = 0; ++ ++ reg_2 = 0x01 << 5; ++ ++ if (config->mod_extern) ++ reg_2 |= (1 << 3); ++ else ++ reg_2 |= (1 << 4); ++ ++ err = isl6423_write(isl6423, reg_2); ++ if (err < 0) ++ goto exit; ++ return 0; ++ ++exit: ++ dprintk(FE_ERROR, 1, "I/O error <%d>", err); ++ return err; ++} ++ ++static int isl6423_voltage_boost(struct dvb_frontend *fe, long arg) ++{ ++ struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; ++ u8 reg_3 = isl6423->reg_3; ++ u8 reg_4 = isl6423->reg_4; ++ int err = 0; ++ ++ if (arg) { ++ /* EN = 1, VSPEN = 1, VBOT = 1 */ ++ reg_4 |= (1 << 4); ++ reg_4 |= 0x1; ++ reg_3 |= (1 << 3); ++ } else { ++ /* EN = 1, VSPEN = 1, VBOT = 0 */ ++ reg_4 |= (1 << 4); ++ reg_4 &= ~0x1; ++ reg_3 |= (1 << 3); ++ } ++ err = isl6423_write(isl6423, reg_3); ++ if (err < 0) ++ goto exit; ++ ++ err = isl6423_write(isl6423, reg_4); ++ if (err < 0) ++ goto exit; ++ ++ isl6423->reg_3 = reg_3; ++ isl6423->reg_4 = reg_4; ++ ++ return 0; ++exit: ++ dprintk(FE_ERROR, 1, "I/O error <%d>", err); ++ return err; ++} ++ ++ ++static int isl6423_set_voltage(struct dvb_frontend *fe, ++ enum fe_sec_voltage voltage) ++{ ++ struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; ++ u8 reg_3 = isl6423->reg_3; ++ u8 reg_4 = isl6423->reg_4; ++ int err = 0; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_OFF: ++ /* EN = 0 */ ++ reg_4 &= ~(1 << 4); ++ break; ++ ++ case SEC_VOLTAGE_13: ++ /* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */ ++ reg_4 |= (1 << 4); ++ reg_4 &= ~0x3; ++ reg_3 |= (1 << 3); ++ break; ++ ++ case SEC_VOLTAGE_18: ++ /* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */ ++ reg_4 |= (1 << 4); ++ reg_4 |= 0x2; ++ reg_4 &= ~0x1; ++ reg_3 |= (1 << 3); ++ break; ++ ++ default: ++ break; ++ } ++ err = isl6423_write(isl6423, reg_3); ++ if (err < 0) ++ goto exit; ++ ++ err = isl6423_write(isl6423, reg_4); ++ if (err < 0) ++ goto exit; ++ ++ isl6423->reg_3 = reg_3; ++ isl6423->reg_4 = reg_4; ++ ++ return 0; ++exit: ++ dprintk(FE_ERROR, 1, "I/O error <%d>", err); ++ return err; ++} ++ ++static int isl6423_set_current(struct dvb_frontend *fe) ++{ ++ struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; ++ u8 reg_3 = isl6423->reg_3; ++ const struct isl6423_config *config = isl6423->config; ++ int err = 0; ++ ++ switch (config->current_max) { ++ case SEC_CURRENT_275m: ++ /* 275mA */ ++ /* ISELH = 0, ISELL = 0 */ ++ reg_3 &= ~0x3; ++ break; ++ ++ case SEC_CURRENT_515m: ++ /* 515mA */ ++ /* ISELH = 0, ISELL = 1 */ ++ reg_3 &= ~0x2; ++ reg_3 |= 0x1; ++ break; ++ ++ case SEC_CURRENT_635m: ++ /* 635mA */ ++ /* ISELH = 1, ISELL = 0 */ ++ reg_3 &= ~0x1; ++ reg_3 |= 0x2; ++ break; ++ ++ case SEC_CURRENT_800m: ++ /* 800mA */ ++ /* ISELH = 1, ISELL = 1 */ ++ reg_3 |= 0x3; ++ break; ++ } ++ ++ err = isl6423_write(isl6423, reg_3); ++ if (err < 0) ++ goto exit; ++ ++ switch (config->curlim) { ++ case SEC_CURRENT_LIM_ON: ++ /* DCL = 0 */ ++ reg_3 &= ~0x10; ++ break; ++ ++ case SEC_CURRENT_LIM_OFF: ++ /* DCL = 1 */ ++ reg_3 |= 0x10; ++ break; ++ } ++ ++ err = isl6423_write(isl6423, reg_3); ++ if (err < 0) ++ goto exit; ++ ++ isl6423->reg_3 = reg_3; ++ ++ return 0; ++exit: ++ dprintk(FE_ERROR, 1, "I/O error <%d>", err); ++ return err; ++} ++ ++static void isl6423_release(struct dvb_frontend *fe) ++{ ++ isl6423_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, ++ const struct isl6423_config *config) ++{ ++ struct isl6423_dev *isl6423; ++ ++ isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL); ++ if (!isl6423) ++ return NULL; ++ ++ isl6423->config = config; ++ isl6423->i2c = i2c; ++ fe->sec_priv = isl6423; ++ ++ /* SR3H = 0, SR3M = 1, SR3L = 0 */ ++ isl6423->reg_3 = 0x02 << 5; ++ /* SR4H = 0, SR4M = 1, SR4L = 1 */ ++ isl6423->reg_4 = 0x03 << 5; ++ ++ if (isl6423_set_current(fe)) ++ goto exit; ++ ++ if (isl6423_set_modulation(fe)) ++ goto exit; ++ ++ fe->ops.release_sec = isl6423_release; ++ fe->ops.set_voltage = isl6423_set_voltage; ++ fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost; ++ isl6423->verbose = verbose; ++ ++ return fe; ++ ++exit: ++ kfree(isl6423); ++ fe->sec_priv = NULL; ++ return NULL; ++} ++EXPORT_SYMBOL(isl6423_attach); ++ ++MODULE_DESCRIPTION("ISL6423 SEC"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/isl6423.h b/drivers/media/dvb-frontends/isl6423.h +new file mode 100644 +index 0000000..e1a37fb +--- /dev/null ++++ b/drivers/media/dvb-frontends/isl6423.h +@@ -0,0 +1,63 @@ ++/* ++ Intersil ISL6423 SEC and LNB Power supply controller ++ ++ Copyright (C) Manu Abraham ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __ISL_6423_H ++#define __ISL_6423_H ++ ++#include ++ ++enum isl6423_current { ++ SEC_CURRENT_275m = 0, ++ SEC_CURRENT_515m, ++ SEC_CURRENT_635m, ++ SEC_CURRENT_800m, ++}; ++ ++enum isl6423_curlim { ++ SEC_CURRENT_LIM_ON = 1, ++ SEC_CURRENT_LIM_OFF ++}; ++ ++struct isl6423_config { ++ enum isl6423_current current_max; ++ enum isl6423_curlim curlim; ++ u8 addr; ++ u8 mod_extern; ++}; ++ ++#if defined(CONFIG_DVB_ISL6423) || (defined(CONFIG_DVB_ISL6423_MODULE) && defined(MODULE)) ++ ++ ++extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, ++ const struct isl6423_config *config); ++ ++#else ++static inline struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, ++ const struct isl6423_config *config) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif /* CONFIG_DVB_ISL6423 */ ++ ++#endif /* __ISL_6423_H */ +diff --git a/drivers/media/dvb-frontends/it913x-fe-priv.h b/drivers/media/dvb-frontends/it913x-fe-priv.h +new file mode 100644 +index 0000000..eb6fd8a +--- /dev/null ++++ b/drivers/media/dvb-frontends/it913x-fe-priv.h +@@ -0,0 +1,1051 @@ ++ ++struct it913xset { u32 pro; ++ u32 address; ++ u8 reg[15]; ++ u8 count; ++}; ++ ++struct adctable { u32 adcFrequency; ++ u32 bandwidth; ++ u32 coeff_1_2048; ++ u32 coeff_1_4096; ++ u32 coeff_1_8191; ++ u32 coeff_1_8192; ++ u32 coeff_1_8193; ++ u32 coeff_2_2k; ++ u32 coeff_2_4k; ++ u32 coeff_2_8k; ++ u16 bfsfcw_fftinx_ratio; ++ u16 fftinx_bfsfcw_ratio; ++}; ++ ++/* clock and coeff tables only table 3 is used with IT9137*/ ++/* TODO other tables relate AF9035 may be removed */ ++static struct adctable tab1[] = { ++ { 20156250, 6000000, ++ 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a, ++ 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c }, ++ { 20156250, 7000000, ++ 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007, ++ 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 }, ++ { 20156250, 8000000, ++ 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3, ++ 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 }, ++ { 20156250, 5000000, ++ 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e, ++ 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 } ++}; ++ ++static struct adctable tab2[] = { ++ { 20187500, 6000000, ++ 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426, ++ 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c }, ++ { 20187500, 7000000, ++ 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81, ++ 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 }, ++ { 20187500, 8000000, ++ 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd, ++ 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 }, ++ { 20187500, 5000000, ++ 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca, ++ 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 } ++ ++}; ++ ++static struct adctable tab3[] = { ++ { 20250000, 6000000, ++ 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1, ++ 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b }, ++ { 20250000, 7000000, ++ 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36, ++ 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 }, ++ { 20250000, 8000000, ++ 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab, ++ 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce }, ++ { 20250000, 5000000, ++ 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b, ++ 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 } ++ ++}; ++ ++static struct adctable tab4[] = { ++ { 20583333, 6000000, ++ 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12, ++ 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 }, ++ { 20583333, 7000000, ++ 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf, ++ 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e }, ++ { 20583333, 8000000, ++ 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d, ++ 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 }, ++ { 20583333, 5000000, ++ 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64, ++ 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c } ++ ++}; ++ ++static struct adctable tab5[] = { ++ { 20416667, 6000000, ++ 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a, ++ 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 }, ++ { 20416667, 7000000, ++ 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e, ++ 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 }, ++ { 20416667, 8000000, ++ 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2, ++ 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb }, ++ { 20416667, 5000000, ++ 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865, ++ 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f } ++ ++}; ++ ++static struct adctable tab6[] = { ++ { 20480000, 6000000, ++ 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c, ++ 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 }, ++ { 20480000, 7000000, ++ 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0, ++ 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 }, ++ { 20480000, 8000000, ++ 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25, ++ 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 }, ++ { 20480000, 5000000, ++ 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7, ++ 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e } ++}; ++ ++static struct adctable tab7[] = { ++ { 20500000, 6000000, ++ 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c, ++ 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 }, ++ { 20500000, 7000000, ++ 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce, ++ 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 }, ++ { 20500000, 8000000, ++ 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210, ++ 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 }, ++ { 20500000, 5000000, ++ 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a, ++ 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d } ++ ++}; ++ ++static struct adctable tab8[] = { ++ { 20625000, 6000000, ++ 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de, ++ 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 }, ++ { 20625000, 7000000, ++ 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8, ++ 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d }, ++ { 20625000, 8000000, ++ 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2, ++ 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 }, ++ { 20625000, 5000000, ++ 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3, ++ 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c } ++ ++}; ++ ++struct table { ++ u32 xtal; ++ struct adctable *table; ++}; ++ ++static struct table fe_clockTable[] = { ++ {12000000, tab3}, /* 12.00MHz */ ++ {20480000, tab6}, /* 20.48MHz */ ++ {36000000, tab3}, /* 36.00MHz */ ++ {30000000, tab1}, /* 30.00MHz */ ++ {26000000, tab4}, /* 26.00MHz */ ++ {28000000, tab5}, /* 28.00MHz */ ++ {32000000, tab7}, /* 32.00MHz */ ++ {34000000, tab2}, /* 34.00MHz */ ++ {24000000, tab1}, /* 24.00MHz */ ++ {22000000, tab8}, /* 22.00MHz */ ++}; ++ ++/* fe get */ ++fe_code_rate_t fe_code[] = { ++ FEC_1_2, ++ FEC_2_3, ++ FEC_3_4, ++ FEC_5_6, ++ FEC_7_8, ++ FEC_NONE, ++}; ++ ++fe_guard_interval_t fe_gi[] = { ++ GUARD_INTERVAL_1_32, ++ GUARD_INTERVAL_1_16, ++ GUARD_INTERVAL_1_8, ++ GUARD_INTERVAL_1_4, ++}; ++ ++fe_hierarchy_t fe_hi[] = { ++ HIERARCHY_NONE, ++ HIERARCHY_1, ++ HIERARCHY_2, ++ HIERARCHY_4, ++}; ++ ++fe_transmit_mode_t fe_mode[] = { ++ TRANSMISSION_MODE_2K, ++ TRANSMISSION_MODE_8K, ++ TRANSMISSION_MODE_4K, ++}; ++ ++fe_modulation_t fe_con[] = { ++ QPSK, ++ QAM_16, ++ QAM_64, ++}; ++ ++enum { ++ PRIORITY_HIGH = 0, /* High-priority stream */ ++ PRIORITY_LOW, /* Low-priority stream */ ++}; ++ ++/* Standard demodulator functions */ ++static struct it913xset set_solo_fe[] = { ++ {PRO_LINK, GPIOH5_EN, {0x01}, 0x01}, ++ {PRO_LINK, GPIOH5_ON, {0x01}, 0x01}, ++ {PRO_LINK, GPIOH5_O, {0x00}, 0x01}, ++ {PRO_LINK, GPIOH5_O, {0x01}, 0x01}, ++ {PRO_LINK, DVBT_INTEN, {0x04}, 0x01}, ++ {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01}, ++ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, ++ {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01}, ++ {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01}, ++ {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01}, ++ {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01}, ++ {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01}, ++ {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01}, ++ {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01}, ++ {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01}, ++ {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01}, ++ {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01}, ++ {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, ++ {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03}, ++ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ ++}; ++ ++ ++static struct it913xset init_1[] = { ++ {PRO_LINK, LOCK3_OUT, {0x01}, 0x01}, ++ {PRO_LINK, PADMISCDRSR, {0x01}, 0x01}, ++ {PRO_LINK, PADMISCDR2, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec57, {0x00, 0x00}, 0x02}, ++ {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */ ++ {PRO_LINK, PADMISCDR8, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++ ++/* Version 1 types */ ++static struct it913xset it9135_v1[] = { ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x007e, {0x04}, 0x01}, ++ {PRO_DMOD, 0x0081, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x008a, {0x01}, 0x01}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06}, 0x01}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009f, {0xe1}, 0x01}, ++ {PRO_DMOD, 0x00a0, {0xcf}, 0x01}, ++ {PRO_DMOD, 0x00a3, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a5, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a6, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a9, {0x00}, 0x01}, ++ {PRO_DMOD, 0x00aa, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00c2, {0x05}, 0x01}, ++ {PRO_DMOD, 0x00c6, {0x19}, 0x01}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf016, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf017, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf018, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf019, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf01a, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf021, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf022, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf023, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf02b, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf02c, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf064, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf065, {0xf9}, 0x01}, ++ {PRO_DMOD, 0xf066, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf067, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf06f, {0xe0}, 0x01}, ++ {PRO_DMOD, 0xf070, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf072, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf073, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf078, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf087, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09b, {0x3f}, 0x01}, ++ {PRO_DMOD, 0xf09c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09d, {0x20}, 0x01}, ++ {PRO_DMOD, 0xf09e, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09f, {0x0c}, 0x01}, ++ {PRO_DMOD, 0xf0a0, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14d, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf15b, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf15d, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf15e, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf167, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf168, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf17a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf17b, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36}, 0x01}, ++ {PRO_DMOD, 0xf1bd, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf1cb, {0xa0}, 0x01}, ++ {PRO_DMOD, 0xf1cc, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf40e, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf40f, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf410, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15}, 0x01}, ++ {PRO_DMOD, 0xf562, {0x20}, 0x01}, ++ {PRO_DMOD, 0xf5df, {0xfb}, 0x01}, ++ {PRO_DMOD, 0xf5e0, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf5e3, {0x09}, 0x01}, ++ {PRO_DMOD, 0xf5e4, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5e5, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf600, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf601, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf602, {0x0b}, 0x01}, ++ {PRO_DMOD, 0xf603, {0x0e}, 0x01}, ++ {PRO_DMOD, 0xf604, {0x11}, 0x01}, ++ {PRO_DMOD, 0xf605, {0x14}, 0x01}, ++ {PRO_DMOD, 0xf606, {0x17}, 0x01}, ++ {PRO_DMOD, 0xf607, {0x1f}, 0x01}, ++ {PRO_DMOD, 0xf60e, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf60f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf610, {0x32}, 0x01}, ++ {PRO_DMOD, 0xf611, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf707, {0xfc}, 0x01}, ++ {PRO_DMOD, 0xf708, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf709, {0x37}, 0x01}, ++ {PRO_DMOD, 0xf70a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf810, {0x54}, 0x01}, ++ {PRO_DMOD, 0xf811, {0x5a}, 0x01}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_38[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x38}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8, ++ 0xd0, 0xc3, 0x01}, 0x0a}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, ++ {PRO_DMOD, 0x00c4, {0x00}, 0x01}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, ++ {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77, ++ 0x00, 0x02, 0xc8, 0x05, 0x7b}, 0x0c}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, ++ {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0x8a, 0xa0}, 0x04}, ++ {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03}, ++ {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, ++ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05}, ++ {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8, 0x59}, 0x05}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf085, {0x00, 0x02, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, ++ 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_51[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x51}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x06, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc0, 0x96, ++ 0xcf, 0xc3, 0x01}, 0x0a}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, ++ {PRO_DMOD, 0x00c4, {0x00}, 0x01}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, ++ {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x7a, 0x77, ++ 0x01, 0x02, 0xb0, 0x02, 0x7a}, 0x0c}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, ++ {PRO_DMOD, 0x011a, {0xc0, 0x7a, 0xac, 0x8c}, 0x04}, ++ {PRO_DMOD, 0x0122, {0x02, 0x70, 0xa4}, 0x03}, ++ {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, ++ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05}, ++ {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc0, 0x59}, 0x05}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, ++ 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_52[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x52}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x10}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xa0, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x03, 0x0a, 0x03, 0xb3, 0x97, ++ 0xc0, 0x9e, 0x01}, 0x0a}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, ++ {PRO_DMOD, 0x00c4, {0x00}, 0x01}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, ++ {PRO_DMOD, 0x00f3, {0x05, 0x91, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x74, 0x77, ++ 0x02, 0x02, 0xae, 0x02, 0x6e}, 0x0c}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, ++ {PRO_DMOD, 0x011a, {0xcd, 0x62, 0xa4, 0x8c}, 0x04}, ++ {PRO_DMOD, 0x0122, {0x03, 0x18, 0x9e}, 0x03}, ++ {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, ++ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x00, 0x00, 0x07, 0x00, 0x06}, 0x05}, ++ {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xb6, 0x59}, 0x05}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, ++ 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++/* Version 2 types */ ++static struct it913xset it9135_v2[] = { ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x007e, {0x04}, 0x01}, ++ {PRO_DMOD, 0x0081, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x008a, {0x01}, 0x01}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06}, 0x01}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009f, {0xe1}, 0x01}, ++ {PRO_DMOD, 0x00a0, {0xcf}, 0x01}, ++ {PRO_DMOD, 0x00a3, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a5, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a6, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00a9, {0x00}, 0x01}, ++ {PRO_DMOD, 0x00aa, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00c2, {0x05}, 0x01}, ++ {PRO_DMOD, 0x00c6, {0x19}, 0x01}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf02b, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf064, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf065, {0xf9}, 0x01}, ++ {PRO_DMOD, 0xf066, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf067, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf06f, {0xe0}, 0x01}, ++ {PRO_DMOD, 0xf070, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf072, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf073, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf078, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf087, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09b, {0x3f}, 0x01}, ++ {PRO_DMOD, 0xf09c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09d, {0x20}, 0x01}, ++ {PRO_DMOD, 0xf09e, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09f, {0x0c}, 0x01}, ++ {PRO_DMOD, 0xf0a0, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14d, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf15b, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf15d, {0x03}, 0x01}, ++ {PRO_DMOD, 0xf15e, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf167, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf168, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf17a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf17b, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36}, 0x01}, ++ {PRO_DMOD, 0xf1bd, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf1cb, {0xa0}, 0x01}, ++ {PRO_DMOD, 0xf1cc, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf40e, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf40f, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf410, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15}, 0x01}, ++ {PRO_DMOD, 0xf562, {0x20}, 0x01}, ++ {PRO_DMOD, 0xf5e3, {0x09}, 0x01}, ++ {PRO_DMOD, 0xf5e4, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf5e5, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf600, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf601, {0x08}, 0x01}, ++ {PRO_DMOD, 0xf602, {0x0b}, 0x01}, ++ {PRO_DMOD, 0xf603, {0x0e}, 0x01}, ++ {PRO_DMOD, 0xf604, {0x11}, 0x01}, ++ {PRO_DMOD, 0xf605, {0x14}, 0x01}, ++ {PRO_DMOD, 0xf606, {0x17}, 0x01}, ++ {PRO_DMOD, 0xf607, {0x1f}, 0x01}, ++ {PRO_DMOD, 0xf60e, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf60f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf610, {0x32}, 0x01}, ++ {PRO_DMOD, 0xf611, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf707, {0xfc}, 0x01}, ++ {PRO_DMOD, 0xf708, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf709, {0x37}, 0x01}, ++ {PRO_DMOD, 0xf70a, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf810, {0x54}, 0x01}, ++ {PRO_DMOD, 0xf811, {0x5a}, 0x01}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_60[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x60}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x006a, {0x03}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04}, 0x01}, ++ {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, ++ {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbe, 0xa0, 0xc6, 0xb6, 0x01}, 0x07}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, ++ {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x0a, 0x50, 0x7b, 0x8c, ++ 0x00, 0x02, 0xbe, 0x00}, 0x0b}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, ++ {PRO_DMOD, 0x011a, {0xbe}, 0x01}, ++ {PRO_DMOD, 0x0124, {0xae}, 0x01}, ++ {PRO_DMOD, 0x0127, {0x00}, 0x01}, ++ {PRO_DMOD, 0x012a, {0x56, 0x50, 0x47, 0x42}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x00}, 0x01}, ++ {PRO_DMOD, 0x013b, {0x08}, 0x01}, ++ {PRO_DMOD, 0x013f, {0x5b}, 0x01}, ++ {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x19, 0x19, 0x8c, 0x8c, 0x8c, ++ 0x6e, 0x8c, 0x50, 0x8c, 0x8c, 0xac, 0xc6, ++ 0x33}, 0x0f}, ++ {PRO_DMOD, 0x0151, {0x28}, 0x01}, ++ {PRO_DMOD, 0x0153, {0xbc}, 0x01}, ++ {PRO_DMOD, 0x0178, {0x09}, 0x01}, ++ {PRO_DMOD, 0x0181, {0x94, 0x6e}, 0x02}, ++ {PRO_DMOD, 0x0185, {0x24}, 0x01}, ++ {PRO_DMOD, 0x0187, {0x00, 0x00, 0xbe, 0x02, 0x80}, 0x05}, ++ {PRO_DMOD, 0xed02, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee42, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee82, {0xff}, 0x01}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf087, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17 ++ , 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_61[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x61}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x06}, 0x01}, ++ {PRO_DMOD, 0x006a, {0x03}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x90, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04}, 0x01}, ++ {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, ++ {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbc, 0x9c, 0xcc, 0xa8, 0x01}, 0x07}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, ++ {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x08, 0x50, 0x7b, 0x8c, ++ 0x01, 0x02, 0xc8, 0x00}, 0x0b}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, ++ {PRO_DMOD, 0x011a, {0xc6}, 0x01}, ++ {PRO_DMOD, 0x0124, {0xa8}, 0x01}, ++ {PRO_DMOD, 0x0127, {0x00}, 0x01}, ++ {PRO_DMOD, 0x012a, {0x59, 0x50, 0x47, 0x42}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x00}, 0x01}, ++ {PRO_DMOD, 0x013b, {0x05}, 0x01}, ++ {PRO_DMOD, 0x013f, {0x5b}, 0x01}, ++ {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x59, 0x8c, 0x8c, 0x8c, ++ 0x7b, 0x8c, 0x50, 0x8c, 0x8c, 0xa8, 0xc6, ++ 0x33}, 0x0f}, ++ {PRO_DMOD, 0x0151, {0x28}, 0x01}, ++ {PRO_DMOD, 0x0153, {0xcc}, 0x01}, ++ {PRO_DMOD, 0x0178, {0x09}, 0x01}, ++ {PRO_DMOD, 0x0181, {0x9c, 0x76}, 0x02}, ++ {PRO_DMOD, 0x0185, {0x28}, 0x01}, ++ {PRO_DMOD, 0x0187, {0x01, 0x00, 0xaa, 0x02, 0x80}, 0x05}, ++ {PRO_DMOD, 0xed02, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee42, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee82, {0xff}, 0x01}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf087, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, ++ 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++static struct it913xset it9135_62[] = { ++ {PRO_DMOD, 0x0043, {0x00}, 0x01}, ++ {PRO_DMOD, 0x0046, {0x62}, 0x01}, ++ {PRO_DMOD, 0x0051, {0x01}, 0x01}, ++ {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0x0068, {0x0a}, 0x01}, ++ {PRO_DMOD, 0x006a, {0x03}, 0x01}, ++ {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, ++ {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05}, ++ {PRO_DMOD, 0x007e, {0x04}, 0x01}, ++ {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, ++ {PRO_DMOD, 0x0084, { 0x0a, 0x33, 0xb8, 0x9c, 0xb2, 0xa6, 0x01}, ++ 0x07}, ++ {PRO_DMOD, 0x008e, {0x01}, 0x01}, ++ {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, ++ {PRO_DMOD, 0x0099, {0x01}, 0x01}, ++ {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, ++ {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, ++ {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, ++ {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, ++ {PRO_DMOD, 0x00b0, {0x01}, 0x01}, ++ {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, ++ {PRO_DMOD, 0x00b6, {0x14}, 0x01}, ++ {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, ++ {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, ++ {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, ++ {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, ++ {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, ++ {PRO_DMOD, 0x00fc, { 0x02, 0x03, 0x02, 0x09, 0x50, 0x6e, 0x8c, ++ 0x02, 0x02, 0xc2, 0x00}, 0x0b}, ++ {PRO_DMOD, 0x0109, {0x02}, 0x01}, ++ {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, ++ {PRO_DMOD, 0x011a, {0xb8}, 0x01}, ++ {PRO_DMOD, 0x0124, {0xa8}, 0x01}, ++ {PRO_DMOD, 0x0127, {0x00}, 0x01}, ++ {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, ++ {PRO_DMOD, 0x0137, {0x00}, 0x01}, ++ {PRO_DMOD, 0x013b, {0x05}, 0x01}, ++ {PRO_DMOD, 0x013f, {0x5b}, 0x01}, ++ {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x19, 0x8c, 0x8c, 0x8c, ++ 0x7b, 0x8c, 0x50, 0x70, 0x8c, 0x96, 0xd0, ++ 0x33}, 0x0f}, ++ {PRO_DMOD, 0x0151, {0x28}, 0x01}, ++ {PRO_DMOD, 0x0153, {0xb2}, 0x01}, ++ {PRO_DMOD, 0x0178, {0x09}, 0x01}, ++ {PRO_DMOD, 0x0181, {0x9c, 0x6e}, 0x02}, ++ {PRO_DMOD, 0x0185, {0x24}, 0x01}, ++ {PRO_DMOD, 0x0187, {0x00, 0x00, 0xb8, 0x02, 0x80}, 0x05}, ++ {PRO_DMOD, 0xed02, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee42, {0xff}, 0x01}, ++ {PRO_DMOD, 0xee82, {0xff}, 0x01}, ++ {PRO_DMOD, 0xf000, {0x0f}, 0x01}, ++ {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, ++ {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, ++ {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, ++ {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf087, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, ++ {PRO_DMOD, 0xf130, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf132, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf144, {0x1a}, 0x01}, ++ {PRO_DMOD, 0xf146, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf14a, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf14f, {0x04}, 0x01}, ++ {PRO_DMOD, 0xf158, {0x7f}, 0x01}, ++ {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, ++ {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, ++ {PRO_DMOD, 0xf163, {0x05}, 0x01}, ++ {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, ++ {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf183, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf19d, {0x40}, 0x01}, ++ {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, ++ {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, ++ {PRO_DMOD, 0xf204, {0x10}, 0x01}, ++ {PRO_DMOD, 0xf214, {0x00}, 0x01}, ++ {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, ++ {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, ++ {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, ++ {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, ++ {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, ++ {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, ++ {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, ++ {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, ++ 0x1f}, 0x08}, ++ {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, ++ {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, ++ {PRO_DMOD, 0xf78b, {0x01}, 0x01}, ++ {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, ++ {PRO_DMOD, 0xf905, {0x01}, 0x01}, ++ {PRO_DMOD, 0xfb06, {0x03}, 0x01}, ++ {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ ++}; ++ ++/* Tuner setting scripts (still keeping it9137) */ ++static struct it913xset it9137_tuner_off[] = { ++ {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */ ++ {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */ ++ {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04}, ++ {PRO_DMOD, 0xec06, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00}, 0x0c}, ++ {PRO_DMOD, 0xec12, {0x00, 0x00, 0x00, 0x00}, 0x04}, ++ {PRO_DMOD, 0xec17, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00}, 0x09}, ++ {PRO_DMOD, 0xec22, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00}, 0x0a}, ++ {PRO_DMOD, 0xec20, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec3f, {0x01}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ ++}; ++ ++static struct it913xset set_it9135_template[] = { ++ {PRO_DMOD, 0xee06, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec56, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4d, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4e, {0x00}, 0x01}, ++ {PRO_DMOD, 0x011e, {0x00}, 0x01}, /* Older Devices */ ++ {PRO_DMOD, 0x011f, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ ++}; ++ ++static struct it913xset set_it9137_template[] = { ++ {PRO_DMOD, 0xee06, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec56, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4c, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4d, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4e, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec4f, {0x00}, 0x01}, ++ {PRO_DMOD, 0xec50, {0x00}, 0x01}, ++ {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ ++}; +diff --git a/drivers/media/dvb-frontends/it913x-fe.c b/drivers/media/dvb-frontends/it913x-fe.c +new file mode 100644 +index 0000000..6e1c6eb +--- /dev/null ++++ b/drivers/media/dvb-frontends/it913x-fe.c +@@ -0,0 +1,1045 @@ ++/* ++ * Driver for it913x-fe Frontend ++ * ++ * with support for on chip it9137 integral tuner ++ * ++ * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) ++ * IT9137 Copyright (C) ITE Tech Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "it913x-fe.h" ++#include "it913x-fe-priv.h" ++ ++static int it913x_debug; ++ ++module_param_named(debug, it913x_debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); ++ ++#define dprintk(level, args...) do { \ ++ if (level & it913x_debug) \ ++ printk(KERN_DEBUG "it913x-fe: " args); \ ++} while (0) ++ ++#define deb_info(args...) dprintk(0x01, args) ++#define debug_data_snipet(level, name, p) \ ++ dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ ++ *p, *(p+1), *(p+2), *(p+3), *(p+4), \ ++ *(p+5), *(p+6), *(p+7)); ++#define info(format, arg...) \ ++ printk(KERN_INFO "it913x-fe: " format "\n" , ## arg) ++ ++struct it913x_fe_state { ++ struct dvb_frontend frontend; ++ struct i2c_adapter *i2c_adap; ++ struct ite_config *config; ++ u8 i2c_addr; ++ u32 frequency; ++ fe_modulation_t constellation; ++ fe_transmit_mode_t transmission_mode; ++ u8 priority; ++ u32 crystalFrequency; ++ u32 adcFrequency; ++ u8 tuner_type; ++ struct adctable *table; ++ fe_status_t it913x_status; ++ u16 tun_xtal; ++ u8 tun_fdiv; ++ u8 tun_clk_mode; ++ u32 tun_fn_min; ++ u32 ucblocks; ++}; ++ ++static int it913x_read_reg(struct it913x_fe_state *state, ++ u32 reg, u8 *data, u8 count) ++{ ++ int ret; ++ u8 pro = PRO_DMOD; /* All reads from demodulator */ ++ u8 b[4]; ++ struct i2c_msg msg[2] = { ++ { .addr = state->i2c_addr + (pro << 1), .flags = 0, ++ .buf = b, .len = sizeof(b) }, ++ { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD, ++ .buf = data, .len = count } ++ }; ++ b[0] = (u8) reg >> 24; ++ b[1] = (u8)(reg >> 16) & 0xff; ++ b[2] = (u8)(reg >> 8) & 0xff; ++ b[3] = (u8) reg & 0xff; ++ ++ ret = i2c_transfer(state->i2c_adap, msg, 2); ++ ++ return ret; ++} ++ ++static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg) ++{ ++ int ret; ++ u8 b[1]; ++ ret = it913x_read_reg(state, reg, &b[0], sizeof(b)); ++ return (ret < 0) ? -ENODEV : b[0]; ++} ++ ++static int it913x_write(struct it913x_fe_state *state, ++ u8 pro, u32 reg, u8 buf[], u8 count) ++{ ++ u8 b[256]; ++ struct i2c_msg msg[1] = { ++ { .addr = state->i2c_addr + (pro << 1), .flags = 0, ++ .buf = b, .len = count + 4 } ++ }; ++ int ret; ++ ++ b[0] = (u8) reg >> 24; ++ b[1] = (u8)(reg >> 16) & 0xff; ++ b[2] = (u8)(reg >> 8) & 0xff; ++ b[3] = (u8) reg & 0xff; ++ memcpy(&b[4], buf, count); ++ ++ ret = i2c_transfer(state->i2c_adap, msg, 1); ++ ++ if (ret < 0) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int it913x_write_reg(struct it913x_fe_state *state, ++ u8 pro, u32 reg, u32 data) ++{ ++ int ret; ++ u8 b[4]; ++ u8 s; ++ ++ b[0] = data >> 24; ++ b[1] = (data >> 16) & 0xff; ++ b[2] = (data >> 8) & 0xff; ++ b[3] = data & 0xff; ++ /* expand write as needed */ ++ if (data < 0x100) ++ s = 3; ++ else if (data < 0x1000) ++ s = 2; ++ else if (data < 0x100000) ++ s = 1; ++ else ++ s = 0; ++ ++ ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s); ++ ++ return ret; ++} ++ ++static int it913x_fe_script_loader(struct it913x_fe_state *state, ++ struct it913xset *loadscript) ++{ ++ int ret, i; ++ if (loadscript == NULL) ++ return -EINVAL; ++ ++ for (i = 0; i < 1000; ++i) { ++ if (loadscript[i].pro == 0xff) ++ break; ++ ret = it913x_write(state, loadscript[i].pro, ++ loadscript[i].address, ++ loadscript[i].reg, loadscript[i].count); ++ if (ret < 0) ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int it913x_init_tuner(struct it913x_fe_state *state) ++{ ++ int ret, i, reg; ++ u8 val, nv_val; ++ u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; ++ u8 b[2]; ++ ++ reg = it913x_read_reg_u8(state, 0xec86); ++ switch (reg) { ++ case 0: ++ state->tun_clk_mode = reg; ++ state->tun_xtal = 2000; ++ state->tun_fdiv = 3; ++ val = 16; ++ break; ++ case -ENODEV: ++ return -ENODEV; ++ case 1: ++ default: ++ state->tun_clk_mode = reg; ++ state->tun_xtal = 640; ++ state->tun_fdiv = 1; ++ val = 6; ++ break; ++ } ++ ++ reg = it913x_read_reg_u8(state, 0xed03); ++ ++ if (reg < 0) ++ return -ENODEV; ++ else if (reg < ARRAY_SIZE(nv)) ++ nv_val = nv[reg]; ++ else ++ nv_val = 2; ++ ++ for (i = 0; i < 50; i++) { ++ ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b)); ++ reg = (b[1] << 8) + b[0]; ++ if (reg > 0) ++ break; ++ if (ret < 0) ++ return -ENODEV; ++ udelay(2000); ++ } ++ state->tun_fn_min = state->tun_xtal * reg; ++ state->tun_fn_min /= (state->tun_fdiv * nv_val); ++ deb_info("Tuner fn_min %d", state->tun_fn_min); ++ ++ if (state->config->chip_ver > 1) ++ msleep(50); ++ else { ++ for (i = 0; i < 50; i++) { ++ reg = it913x_read_reg_u8(state, 0xec82); ++ if (reg > 0) ++ break; ++ if (reg < 0) ++ return -ENODEV; ++ udelay(2000); ++ } ++ } ++ ++ return it913x_write_reg(state, PRO_DMOD, 0xed81, val); ++} ++ ++static int it9137_set_tuner(struct it913x_fe_state *state, ++ u32 bandwidth, u32 frequency_m) ++{ ++ struct it913xset *set_tuner = set_it9137_template; ++ int ret, reg; ++ u32 frequency = frequency_m / 1000; ++ u32 freq, temp_f, tmp; ++ u16 iqik_m_cal; ++ u16 n_div; ++ u8 n; ++ u8 l_band; ++ u8 lna_band; ++ u8 bw; ++ ++ if (state->config->firmware_ver == 1) ++ set_tuner = set_it9135_template; ++ else ++ set_tuner = set_it9137_template; ++ ++ deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth); ++ ++ if (frequency >= 51000 && frequency <= 440000) { ++ l_band = 0; ++ lna_band = 0; ++ } else if (frequency > 440000 && frequency <= 484000) { ++ l_band = 1; ++ lna_band = 1; ++ } else if (frequency > 484000 && frequency <= 533000) { ++ l_band = 1; ++ lna_band = 2; ++ } else if (frequency > 533000 && frequency <= 587000) { ++ l_band = 1; ++ lna_band = 3; ++ } else if (frequency > 587000 && frequency <= 645000) { ++ l_band = 1; ++ lna_band = 4; ++ } else if (frequency > 645000 && frequency <= 710000) { ++ l_band = 1; ++ lna_band = 5; ++ } else if (frequency > 710000 && frequency <= 782000) { ++ l_band = 1; ++ lna_band = 6; ++ } else if (frequency > 782000 && frequency <= 860000) { ++ l_band = 1; ++ lna_band = 7; ++ } else if (frequency > 1450000 && frequency <= 1492000) { ++ l_band = 1; ++ lna_band = 0; ++ } else if (frequency > 1660000 && frequency <= 1685000) { ++ l_band = 1; ++ lna_band = 1; ++ } else ++ return -EINVAL; ++ set_tuner[0].reg[0] = lna_band; ++ ++ switch (bandwidth) { ++ case 5000000: ++ bw = 0; ++ break; ++ case 6000000: ++ bw = 2; ++ break; ++ case 7000000: ++ bw = 4; ++ break; ++ default: ++ case 8000000: ++ bw = 6; ++ break; ++ } ++ ++ set_tuner[1].reg[0] = bw; ++ set_tuner[2].reg[0] = 0xa0 | (l_band << 3); ++ ++ if (frequency > 53000 && frequency <= 74000) { ++ n_div = 48; ++ n = 0; ++ } else if (frequency > 74000 && frequency <= 111000) { ++ n_div = 32; ++ n = 1; ++ } else if (frequency > 111000 && frequency <= 148000) { ++ n_div = 24; ++ n = 2; ++ } else if (frequency > 148000 && frequency <= 222000) { ++ n_div = 16; ++ n = 3; ++ } else if (frequency > 222000 && frequency <= 296000) { ++ n_div = 12; ++ n = 4; ++ } else if (frequency > 296000 && frequency <= 445000) { ++ n_div = 8; ++ n = 5; ++ } else if (frequency > 445000 && frequency <= state->tun_fn_min) { ++ n_div = 6; ++ n = 6; ++ } else if (frequency > state->tun_fn_min && frequency <= 950000) { ++ n_div = 4; ++ n = 7; ++ } else if (frequency > 1450000 && frequency <= 1680000) { ++ n_div = 2; ++ n = 0; ++ } else ++ return -EINVAL; ++ ++ reg = it913x_read_reg_u8(state, 0xed81); ++ iqik_m_cal = (u16)reg * n_div; ++ ++ if (reg < 0x20) { ++ if (state->tun_clk_mode == 0) ++ iqik_m_cal = (iqik_m_cal * 9) >> 5; ++ else ++ iqik_m_cal >>= 1; ++ } else { ++ iqik_m_cal = 0x40 - iqik_m_cal; ++ if (state->tun_clk_mode == 0) ++ iqik_m_cal = ~((iqik_m_cal * 9) >> 5); ++ else ++ iqik_m_cal = ~(iqik_m_cal >> 1); ++ } ++ ++ temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv; ++ freq = temp_f / state->tun_xtal; ++ tmp = freq * state->tun_xtal; ++ ++ if ((temp_f - tmp) >= (state->tun_xtal >> 1)) ++ freq++; ++ ++ freq += (u32) n << 13; ++ /* Frequency OMEGA_IQIK_M_CAL_MID*/ ++ temp_f = freq + (u32)iqik_m_cal; ++ ++ set_tuner[3].reg[0] = temp_f & 0xff; ++ set_tuner[4].reg[0] = (temp_f >> 8) & 0xff; ++ ++ deb_info("High Frequency = %04x", temp_f); ++ ++ /* Lower frequency */ ++ set_tuner[5].reg[0] = freq & 0xff; ++ set_tuner[6].reg[0] = (freq >> 8) & 0xff; ++ ++ deb_info("low Frequency = %04x", freq); ++ ++ ret = it913x_fe_script_loader(state, set_tuner); ++ ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++static int it913x_fe_select_bw(struct it913x_fe_state *state, ++ u32 bandwidth, u32 adcFrequency) ++{ ++ int ret, i; ++ u8 buffer[256]; ++ u32 coeff[8]; ++ u16 bfsfcw_fftinx_ratio; ++ u16 fftinx_bfsfcw_ratio; ++ u8 count; ++ u8 bw; ++ u8 adcmultiplier; ++ ++ deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency); ++ ++ switch (bandwidth) { ++ case 5000000: ++ bw = 3; ++ break; ++ case 6000000: ++ bw = 0; ++ break; ++ case 7000000: ++ bw = 1; ++ break; ++ default: ++ case 8000000: ++ bw = 2; ++ break; ++ } ++ ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw); ++ ++ if (state->table == NULL) ++ return -EINVAL; ++ ++ /* In write order */ ++ coeff[0] = state->table[bw].coeff_1_2048; ++ coeff[1] = state->table[bw].coeff_2_2k; ++ coeff[2] = state->table[bw].coeff_1_8191; ++ coeff[3] = state->table[bw].coeff_1_8192; ++ coeff[4] = state->table[bw].coeff_1_8193; ++ coeff[5] = state->table[bw].coeff_2_8k; ++ coeff[6] = state->table[bw].coeff_1_4096; ++ coeff[7] = state->table[bw].coeff_2_4k; ++ bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio; ++ fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio; ++ ++ /* ADC multiplier */ ++ ret = it913x_read_reg_u8(state, ADC_X_2); ++ if (ret < 0) ++ return -EINVAL; ++ ++ adcmultiplier = ret; ++ ++ count = 0; ++ ++ /* Build Buffer for COEFF Registers */ ++ for (i = 0; i < 8; i++) { ++ if (adcmultiplier == 1) ++ coeff[i] /= 2; ++ buffer[count++] = (coeff[i] >> 24) & 0x3; ++ buffer[count++] = (coeff[i] >> 16) & 0xff; ++ buffer[count++] = (coeff[i] >> 8) & 0xff; ++ buffer[count++] = coeff[i] & 0xff; ++ } ++ ++ /* bfsfcw_fftinx_ratio register 0x21-0x22 */ ++ buffer[count++] = bfsfcw_fftinx_ratio & 0xff; ++ buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff; ++ /* fftinx_bfsfcw_ratio register 0x23-0x24 */ ++ buffer[count++] = fftinx_bfsfcw_ratio & 0xff; ++ buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff; ++ /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/ ++ ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count); ++ ++ for (i = 0; i < 42; i += 8) ++ debug_data_snipet(0x1, "Buffer", &buffer[i]); ++ ++ return ret; ++} ++ ++ ++ ++static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int ret, i; ++ fe_status_t old_status = state->it913x_status; ++ *status = 0; ++ ++ if (state->it913x_status == 0) { ++ ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); ++ if (ret == 0x1) { ++ *status |= FE_HAS_SIGNAL; ++ for (i = 0; i < 40; i++) { ++ ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK); ++ if (ret == 0x1) ++ break; ++ msleep(25); ++ } ++ if (ret == 0x1) ++ *status |= FE_HAS_CARRIER ++ | FE_HAS_VITERBI ++ | FE_HAS_SYNC; ++ state->it913x_status = *status; ++ } ++ } ++ ++ if (state->it913x_status & FE_HAS_SYNC) { ++ ret = it913x_read_reg_u8(state, TPSD_LOCK); ++ if (ret == 0x1) ++ *status |= FE_HAS_LOCK ++ | state->it913x_status; ++ else ++ state->it913x_status = 0; ++ if (old_status != state->it913x_status) ++ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret); ++ } ++ ++ return 0; ++} ++ ++/* FEC values based on fe_code_rate_t non supported values 0*/ ++int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88}; ++int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82}; ++int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76}; ++ ++static int it913x_get_signal_strength(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ u8 code_rate; ++ int ret, temp; ++ u8 lna_gain_os; ++ ++ ret = it913x_read_reg_u8(state, VAR_P_INBAND); ++ if (ret < 0) ++ return ret; ++ ++ /* VHF/UHF gain offset */ ++ if (state->frequency < 300000000) ++ lna_gain_os = 7; ++ else ++ lna_gain_os = 14; ++ ++ temp = (ret - 100) - lna_gain_os; ++ ++ if (state->priority == PRIORITY_HIGH) ++ code_rate = p->code_rate_HP; ++ else ++ code_rate = p->code_rate_LP; ++ ++ if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval)) ++ return -EINVAL; ++ ++ deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp); ++ ++ /* Apply FEC offset values*/ ++ switch (p->modulation) { ++ case QPSK: ++ temp -= it913x_qpsk_pval[code_rate]; ++ break; ++ case QAM_16: ++ temp -= it913x_16qam_pval[code_rate]; ++ break; ++ case QAM_64: ++ temp -= it913x_64qam_pval[code_rate]; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (temp < -15) ++ ret = 0; ++ else if ((-15 <= temp) && (temp < 0)) ++ ret = (2 * (temp + 15)) / 3; ++ else if ((0 <= temp) && (temp < 20)) ++ ret = 4 * temp + 10; ++ else if ((20 <= temp) && (temp < 35)) ++ ret = (2 * (temp - 20)) / 3 + 90; ++ else if (temp >= 35) ++ ret = 100; ++ ++ deb_info("Signal Strength :%d", ret); ++ ++ return ret; ++} ++ ++static int it913x_fe_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int ret = 0; ++ if (state->config->read_slevel) { ++ if (state->it913x_status & FE_HAS_SIGNAL) ++ ret = it913x_read_reg_u8(state, SIGNAL_LEVEL); ++ } else ++ ret = it913x_get_signal_strength(fe); ++ ++ if (ret >= 0) ++ *strength = (u16)((u32)ret * 0xffff / 0x64); ++ ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int ret; ++ u8 reg[3]; ++ u32 snr_val, snr_min, snr_max; ++ u32 temp; ++ ++ ret = it913x_read_reg(state, 0x2c, reg, sizeof(reg)); ++ ++ snr_val = (u32)(reg[2] << 16) | (reg[1] << 8) | reg[0]; ++ ++ ret |= it913x_read_reg(state, 0xf78b, reg, 1); ++ if (reg[0]) ++ snr_val /= reg[0]; ++ ++ if (state->transmission_mode == TRANSMISSION_MODE_2K) ++ snr_val *= 4; ++ else if (state->transmission_mode == TRANSMISSION_MODE_4K) ++ snr_val *= 2; ++ ++ if (state->constellation == QPSK) { ++ snr_min = 0xb4711; ++ snr_max = 0x191451; ++ } else if (state->constellation == QAM_16) { ++ snr_min = 0x4f0d5; ++ snr_max = 0xc7925; ++ } else if (state->constellation == QAM_64) { ++ snr_min = 0x256d0; ++ snr_max = 0x626be; ++ } else ++ return -EINVAL; ++ ++ if (snr_val < snr_min) ++ *snr = 0; ++ else if (snr_val < snr_max) { ++ temp = (snr_val - snr_min) >> 5; ++ temp *= 0xffff; ++ temp /= (snr_max - snr_min) >> 5; ++ *snr = (u16)temp; ++ } else ++ *snr = 0xffff; ++ ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ u8 reg[5]; ++ /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */ ++ it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); ++ state->ucblocks += (u32)(reg[1] << 8) | reg[0]; ++ *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2]; ++ return 0; ++} ++ ++static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int ret; ++ u8 reg[2]; ++ /* Aborted Packets */ ++ ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); ++ state->ucblocks += (u32)(reg[1] << 8) | reg[0]; ++ *ucblocks = state->ucblocks; ++ return ret; ++} ++ ++static int it913x_fe_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ u8 reg[8]; ++ ++ it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); ++ ++ if (reg[3] < 3) ++ p->modulation = fe_con[reg[3]]; ++ ++ if (reg[0] < 3) ++ p->transmission_mode = fe_mode[reg[0]]; ++ ++ if (reg[1] < 4) ++ p->guard_interval = fe_gi[reg[1]]; ++ ++ if (reg[2] < 4) ++ p->hierarchy = fe_hi[reg[2]]; ++ ++ state->priority = reg[5]; ++ ++ p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE; ++ p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE; ++ ++ /* Update internal state to reflect the autodetected props */ ++ state->constellation = p->modulation; ++ state->transmission_mode = p->transmission_mode; ++ ++ return 0; ++} ++ ++static int it913x_fe_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int i; ++ u8 empty_ch, last_ch; ++ ++ state->it913x_status = 0; ++ ++ /* Set bw*/ ++ it913x_fe_select_bw(state, p->bandwidth_hz, ++ state->adcFrequency); ++ ++ /* Training Mode Off */ ++ it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); ++ ++ /* Clear Empty Channel */ ++ it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); ++ ++ /* Clear bits */ ++ it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); ++ /* LED on */ ++ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); ++ /* Select Band*/ ++ if ((p->frequency >= 51000000) && (p->frequency <= 230000000)) ++ i = 0; ++ else if ((p->frequency >= 350000000) && (p->frequency <= 900000000)) ++ i = 1; ++ else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000)) ++ i = 2; ++ else ++ return -EOPNOTSUPP; ++ ++ it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); ++ ++ deb_info("Frontend Set Tuner Type %02x", state->tuner_type); ++ switch (state->tuner_type) { ++ case IT9135_38: ++ case IT9135_51: ++ case IT9135_52: ++ case IT9135_60: ++ case IT9135_61: ++ case IT9135_62: ++ it9137_set_tuner(state, ++ p->bandwidth_hz, p->frequency); ++ break; ++ default: ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ break; ++ } ++ /* LED off */ ++ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); ++ /* Trigger ofsm */ ++ it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); ++ last_ch = 2; ++ for (i = 0; i < 40; ++i) { ++ empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); ++ if (last_ch == 1 && empty_ch == 1) ++ break; ++ if (last_ch == 2 && empty_ch == 2) ++ return 0; ++ last_ch = empty_ch; ++ msleep(25); ++ } ++ for (i = 0; i < 40; ++i) { ++ if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1) ++ break; ++ msleep(25); ++ } ++ ++ state->frequency = p->frequency; ++ return 0; ++} ++ ++static int it913x_fe_suspend(struct it913x_fe_state *state) ++{ ++ int ret, i; ++ u8 b; ++ ++ ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1); ++ ++ ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); ++ ++ for (i = 0; i < 128; i++) { ++ ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1); ++ if (ret < 0) ++ return -ENODEV; ++ if (b == 0) ++ break; ++ ++ } ++ ++ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8); ++ /* Turn LED off */ ++ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); ++ ++ ret |= it913x_fe_script_loader(state, it9137_tuner_off); ++ ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++/* Power sequence */ ++/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */ ++/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */ ++ ++static int it913x_fe_sleep(struct dvb_frontend *fe) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ return it913x_fe_suspend(state); ++} ++ ++static u32 compute_div(u32 a, u32 b, u32 x) ++{ ++ u32 res = 0; ++ u32 c = 0; ++ u32 i = 0; ++ ++ if (a > b) { ++ c = a / b; ++ a = a - c * b; ++ } ++ ++ for (i = 0; i < x; i++) { ++ if (a >= b) { ++ res += 1; ++ a -= b; ++ } ++ a <<= 1; ++ res <<= 1; ++ } ++ ++ res = (c << x) + res; ++ ++ return res; ++} ++ ++static int it913x_fe_start(struct it913x_fe_state *state) ++{ ++ struct it913xset *set_lna; ++ struct it913xset *set_mode; ++ int ret; ++ u8 adf = (state->config->adf & 0xf); ++ u32 adc, xtal; ++ u8 b[4]; ++ ++ if (state->config->chip_ver == 1) ++ ret = it913x_init_tuner(state); ++ ++ info("ADF table value :%02x", adf); ++ ++ if (adf < 10) { ++ state->crystalFrequency = fe_clockTable[adf].xtal ; ++ state->table = fe_clockTable[adf].table; ++ state->adcFrequency = state->table->adcFrequency; ++ ++ adc = compute_div(state->adcFrequency, 1000000ul, 19ul); ++ xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul); ++ ++ } else ++ return -EINVAL; ++ ++ /* Set LED indicator on GPIOH3 */ ++ ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1); ++ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1); ++ ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); ++ ++ ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type); ++ ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01); ++ ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01); ++ ++ b[0] = xtal & 0xff; ++ b[1] = (xtal >> 8) & 0xff; ++ b[2] = (xtal >> 16) & 0xff; ++ b[3] = (xtal >> 24); ++ ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4); ++ ++ b[0] = adc & 0xff; ++ b[1] = (adc >> 8) & 0xff; ++ b[2] = (adc >> 16) & 0xff; ++ ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3); ++ ++ if (state->config->adc_x2) ++ ret |= it913x_write_reg(state, PRO_DMOD, ADC_X_2, 0x01); ++ b[0] = 0; ++ b[1] = 0; ++ b[2] = 0; ++ ret |= it913x_write(state, PRO_DMOD, 0x0029, b, 3); ++ ++ info("Crystal Frequency :%d Adc Frequency :%d ADC X2: %02x", ++ state->crystalFrequency, state->adcFrequency, ++ state->config->adc_x2); ++ deb_info("Xtal value :%04x Adc value :%04x", xtal, adc); ++ ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* v1 or v2 tuner script */ ++ if (state->config->chip_ver > 1) ++ ret = it913x_fe_script_loader(state, it9135_v2); ++ else ++ ret = it913x_fe_script_loader(state, it9135_v1); ++ if (ret < 0) ++ return ret; ++ ++ /* LNA Scripts */ ++ switch (state->tuner_type) { ++ case IT9135_51: ++ set_lna = it9135_51; ++ break; ++ case IT9135_52: ++ set_lna = it9135_52; ++ break; ++ case IT9135_60: ++ set_lna = it9135_60; ++ break; ++ case IT9135_61: ++ set_lna = it9135_61; ++ break; ++ case IT9135_62: ++ set_lna = it9135_62; ++ break; ++ case IT9135_38: ++ default: ++ set_lna = it9135_38; ++ } ++ info("Tuner LNA type :%02x", state->tuner_type); ++ ++ ret = it913x_fe_script_loader(state, set_lna); ++ if (ret < 0) ++ return ret; ++ ++ if (state->config->chip_ver == 2) { ++ ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x1); ++ ret |= it913x_write_reg(state, PRO_LINK, PADODPU, 0x0); ++ ret |= it913x_write_reg(state, PRO_LINK, AGC_O_D, 0x0); ++ ret |= it913x_init_tuner(state); ++ } ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Always solo frontend */ ++ set_mode = set_solo_fe; ++ ret |= it913x_fe_script_loader(state, set_mode); ++ ++ ret |= it913x_fe_suspend(state); ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++static int it913x_fe_init(struct dvb_frontend *fe) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ int ret = 0; ++ /* Power Up Tuner - common all versions */ ++ ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1); ++ ++ ret |= it913x_fe_script_loader(state, init_1); ++ ++ ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0); ++ ++ ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0); ++ ++ return (ret < 0) ? -ENODEV : 0; ++} ++ ++static void it913x_fe_release(struct dvb_frontend *fe) ++{ ++ struct it913x_fe_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops it913x_fe_ofdm_ops; ++ ++struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, struct ite_config *config) ++{ ++ struct it913x_fe_state *state = NULL; ++ int ret; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ if (config == NULL) ++ goto error; ++ ++ state->i2c_adap = i2c_adap; ++ state->i2c_addr = i2c_addr; ++ state->config = config; ++ ++ switch (state->config->tuner_id_0) { ++ case IT9135_51: ++ case IT9135_52: ++ case IT9135_60: ++ case IT9135_61: ++ case IT9135_62: ++ state->tuner_type = state->config->tuner_id_0; ++ break; ++ default: ++ case IT9135_38: ++ state->tuner_type = IT9135_38; ++ } ++ ++ ret = it913x_fe_start(state); ++ if (ret < 0) ++ goto error; ++ ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(it913x_fe_attach); ++ ++static struct dvb_frontend_ops it913x_fe_ofdm_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "it913x-fe DVB-T", ++ .frequency_min = 51000000, ++ .frequency_max = 1680000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = it913x_fe_release, ++ ++ .init = it913x_fe_init, ++ .sleep = it913x_fe_sleep, ++ ++ .set_frontend = it913x_fe_set_frontend, ++ .get_frontend = it913x_fe_get_frontend, ++ ++ .read_status = it913x_fe_read_status, ++ .read_signal_strength = it913x_fe_read_signal_strength, ++ .read_snr = it913x_fe_read_snr, ++ .read_ber = it913x_fe_read_ber, ++ .read_ucblocks = it913x_fe_read_ucblocks, ++}; ++ ++MODULE_DESCRIPTION("it913x Frontend and it9137 tuner"); ++MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); ++MODULE_VERSION("1.15"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/it913x-fe.h b/drivers/media/dvb-frontends/it913x-fe.h +new file mode 100644 +index 0000000..07fa459 +--- /dev/null ++++ b/drivers/media/dvb-frontends/it913x-fe.h +@@ -0,0 +1,237 @@ ++/* ++ * Driver for it913x Frontend ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef IT913X_FE_H ++#define IT913X_FE_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct ite_config { ++ u8 chip_ver; ++ u16 chip_type; ++ u32 firmware; ++ u8 firmware_ver; ++ u8 adc_x2; ++ u8 tuner_id_0; ++ u8 tuner_id_1; ++ u8 dual_mode; ++ u8 adf; ++ /* option to read SIGNAL_LEVEL */ ++ u8 read_slevel; ++}; ++ ++#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \ ++defined(MODULE)) ++extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, struct ite_config *config); ++#else ++static inline struct dvb_frontend *it913x_fe_attach( ++ struct i2c_adapter *i2c_adap, ++ u8 i2c_addr, struct ite_config *config) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_IT913X_FE */ ++#define I2C_BASE_ADDR 0x10 ++#define DEV_0 0x0 ++#define DEV_1 0x10 ++#define PRO_LINK 0x0 ++#define PRO_DMOD 0x1 ++#define DEV_0_DMOD (PRO_DMOD << 0x7) ++#define DEV_1_DMOD (DEV_0_DMOD | DEV_1) ++#define CHIP2_I2C_ADDR 0x3a ++ ++#define AFE_MEM0 0xfb24 ++ ++#define MP2_SW_RST 0xf99d ++#define MP2IF2_SW_RST 0xf9a4 ++ ++#define PADODPU 0xd827 ++#define THIRDODPU 0xd828 ++#define AGC_O_D 0xd829 ++ ++#define EP0_TX_EN 0xdd11 ++#define EP0_TX_NAK 0xdd13 ++#define EP4_TX_LEN_LSB 0xdd88 ++#define EP4_TX_LEN_MSB 0xdd89 ++#define EP4_MAX_PKT 0xdd0c ++#define EP5_TX_LEN_LSB 0xdd8a ++#define EP5_TX_LEN_MSB 0xdd8b ++#define EP5_MAX_PKT 0xdd0d ++ ++#define IO_MUX_POWER_CLK 0xd800 ++#define CLK_O_EN 0xd81a ++#define I2C_CLK 0xf103 ++#define I2C_CLK_100 0x7 ++#define I2C_CLK_400 0x1a ++ ++#define D_TPSD_LOCK 0xf5a9 ++#define MP2IF2_EN 0xf9a3 ++#define MP2IF_SERIAL 0xf985 ++#define TSIS_ENABLE 0xf9cd ++#define MP2IF2_HALF_PSB 0xf9a5 ++#define MP2IF_STOP_EN 0xf9b5 ++#define MPEG_FULL_SPEED 0xf990 ++#define TOP_HOSTB_SER_MODE 0xd91c ++ ++#define PID_RST 0xf992 ++#define PID_EN 0xf993 ++#define PID_INX_EN 0xf994 ++#define PID_INX 0xf995 ++#define PID_LSB 0xf996 ++#define PID_MSB 0xf997 ++ ++#define MP2IF_MPEG_PAR_MODE 0xf986 ++#define DCA_UPPER_CHIP 0xf731 ++#define DCA_LOWER_CHIP 0xf732 ++#define DCA_PLATCH 0xf730 ++#define DCA_FPGA_LATCH 0xf778 ++#define DCA_STAND_ALONE 0xf73c ++#define DCA_ENABLE 0xf776 ++ ++#define DVBT_INTEN 0xf41f ++#define DVBT_ENABLE 0xf41a ++#define HOSTB_DCA_LOWER 0xd91f ++#define HOSTB_MPEG_PAR_MODE 0xd91b ++#define HOSTB_MPEG_SER_MODE 0xd91c ++#define HOSTB_MPEG_SER_DO7 0xd91d ++#define HOSTB_DCA_UPPER 0xd91e ++#define PADMISCDR2 0xd830 ++#define PADMISCDR4 0xd831 ++#define PADMISCDR8 0xd832 ++#define PADMISCDRSR 0xd833 ++#define LOCK3_OUT 0xd8fd ++ ++#define GPIOH1_O 0xd8af ++#define GPIOH1_EN 0xd8b0 ++#define GPIOH1_ON 0xd8b1 ++#define GPIOH3_O 0xd8b3 ++#define GPIOH3_EN 0xd8b4 ++#define GPIOH3_ON 0xd8b5 ++#define GPIOH5_O 0xd8bb ++#define GPIOH5_EN 0xd8bc ++#define GPIOH5_ON 0xd8bd ++ ++#define AFE_MEM0 0xfb24 ++ ++#define REG_TPSD_TX_MODE 0xf900 ++#define REG_TPSD_GI 0xf901 ++#define REG_TPSD_HIER 0xf902 ++#define REG_TPSD_CONST 0xf903 ++#define REG_BW 0xf904 ++#define REG_PRIV 0xf905 ++#define REG_TPSD_HP_CODE 0xf906 ++#define REG_TPSD_LP_CODE 0xf907 ++ ++#define MP2IF_SYNC_LK 0xf999 ++#define ADC_FREQ 0xf1cd ++ ++#define TRIGGER_OFSM 0x0000 ++/* COEFF Registers start at 0x0001 to 0x0020 */ ++#define COEFF_1_2048 0x0001 ++#define XTAL_CLK 0x0025 ++#define BFS_FCW 0x0029 ++ ++/* Error Regs */ ++#define RSD_ABORT_PKT_LSB 0x0032 ++#define RSD_ABORT_PKT_MSB 0x0033 ++#define RSD_BIT_ERR_0_7 0x0034 ++#define RSD_BIT_ERR_8_15 0x0035 ++#define RSD_BIT_ERR_23_16 0x0036 ++#define RSD_BIT_COUNT_LSB 0x0037 ++#define RSD_BIT_COUNT_MSB 0x0038 ++ ++#define TPSD_LOCK 0x003c ++#define TRAINING_MODE 0x0040 ++#define ADC_X_2 0x0045 ++#define TUNER_ID 0x0046 ++#define EMPTY_CHANNEL_STATUS 0x0047 ++#define SIGNAL_LEVEL 0x0048 ++#define SIGNAL_QUALITY 0x0049 ++#define EST_SIGNAL_LEVEL 0x004a ++#define FREE_BAND 0x004b ++#define SUSPEND_FLAG 0x004c ++#define VAR_P_INBAND 0x00f7 ++ ++/* Build in tuner types */ ++#define IT9137 0x38 ++#define IT9135_38 0x38 ++#define IT9135_51 0x51 ++#define IT9135_52 0x52 ++#define IT9135_60 0x60 ++#define IT9135_61 0x61 ++#define IT9135_62 0x62 ++ ++enum { ++ CMD_DEMOD_READ = 0, ++ CMD_DEMOD_WRITE, ++ CMD_TUNER_READ, ++ CMD_TUNER_WRITE, ++ CMD_REG_EEPROM_READ, ++ CMD_REG_EEPROM_WRITE, ++ CMD_DATA_READ, ++ CMD_VAR_READ = 8, ++ CMD_VAR_WRITE, ++ CMD_PLATFORM_GET, ++ CMD_PLATFORM_SET, ++ CMD_IP_CACHE, ++ CMD_IP_ADD, ++ CMD_IP_REMOVE, ++ CMD_PID_ADD, ++ CMD_PID_REMOVE, ++ CMD_SIPSI_GET, ++ CMD_SIPSI_MPE_RESET, ++ CMD_H_PID_ADD = 0x15, ++ CMD_H_PID_REMOVE, ++ CMD_ABORT, ++ CMD_IR_GET, ++ CMD_IR_SET, ++ CMD_FW_DOWNLOAD = 0x21, ++ CMD_QUERYINFO, ++ CMD_BOOT, ++ CMD_FW_DOWNLOAD_BEGIN, ++ CMD_FW_DOWNLOAD_END, ++ CMD_RUN_CODE, ++ CMD_SCATTER_READ = 0x28, ++ CMD_SCATTER_WRITE, ++ CMD_GENERIC_READ, ++ CMD_GENERIC_WRITE ++}; ++ ++enum { ++ READ_LONG, ++ WRITE_LONG, ++ READ_SHORT, ++ WRITE_SHORT, ++ READ_DATA, ++ WRITE_DATA, ++ WRITE_CMD, ++}; ++ ++enum { ++ IT9135_AUTO = 0, ++ IT9137_FW, ++ IT9135_V1_FW, ++ IT9135_V2_FW, ++}; ++ ++#endif /* IT913X_FE_H */ +diff --git a/drivers/media/dvb-frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c +new file mode 100644 +index 0000000..c1c3400 +--- /dev/null ++++ b/drivers/media/dvb-frontends/itd1000.c +@@ -0,0 +1,399 @@ ++/* ++ * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" ++ * ++ * Copyright (c) 2007-8 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "itd1000.h" ++#include "itd1000_priv.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); ++ ++#define itd_dbg(args...) do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "ITD1000: " args);\ ++ } \ ++} while (0) ++ ++#define itd_warn(args...) do { \ ++ printk(KERN_WARNING "ITD1000: " args); \ ++} while (0) ++ ++#define itd_info(args...) do { \ ++ printk(KERN_INFO "ITD1000: " args); \ ++} while (0) ++ ++/* don't write more than one byte with flexcop behind */ ++static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len) ++{ ++ u8 buf[1+len]; ++ struct i2c_msg msg = { ++ .addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1 ++ }; ++ buf[0] = reg; ++ memcpy(&buf[1], v, len); ++ ++ /* itd_dbg("wr %02x: %02x\n", reg, v[0]); */ ++ ++ if (i2c_transfer(state->i2c, &msg, 1) != 1) { ++ printk(KERN_WARNING "itd1000 I2C write failed\n"); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int itd1000_read_reg(struct itd1000_state *state, u8 reg) ++{ ++ u8 val; ++ struct i2c_msg msg[2] = { ++ { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }, ++ }; ++ ++ /* ugly flexcop workaround */ ++ itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1); ++ ++ if (i2c_transfer(state->i2c, msg, 2) != 2) { ++ itd_warn("itd1000 I2C read failed\n"); ++ return -EREMOTEIO; ++ } ++ return val; ++} ++ ++static inline int itd1000_write_reg(struct itd1000_state *state, u8 r, u8 v) ++{ ++ int ret = itd1000_write_regs(state, r, &v, 1); ++ state->shadow[r] = v; ++ return ret; ++} ++ ++ ++static struct { ++ u32 symbol_rate; ++ u8 pgaext : 4; /* PLLFH */ ++ u8 bbgvmin : 4; /* BBGVMIN */ ++} itd1000_lpf_pga[] = { ++ { 0, 0x8, 0x3 }, ++ { 5200000, 0x8, 0x3 }, ++ { 12200000, 0x4, 0x3 }, ++ { 15400000, 0x2, 0x3 }, ++ { 19800000, 0x2, 0x3 }, ++ { 21500000, 0x2, 0x3 }, ++ { 24500000, 0x2, 0x3 }, ++ { 28400000, 0x2, 0x3 }, ++ { 33400000, 0x2, 0x3 }, ++ { 34400000, 0x1, 0x4 }, ++ { 34400000, 0x1, 0x4 }, ++ { 38400000, 0x1, 0x4 }, ++ { 38400000, 0x1, 0x4 }, ++ { 40400000, 0x1, 0x4 }, ++ { 45400000, 0x1, 0x4 }, ++}; ++ ++static void itd1000_set_lpf_bw(struct itd1000_state *state, u32 symbol_rate) ++{ ++ u8 i; ++ u8 con1 = itd1000_read_reg(state, CON1) & 0xfd; ++ u8 pllfh = itd1000_read_reg(state, PLLFH) & 0x0f; ++ u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0; ++ u8 bw = itd1000_read_reg(state, BW) & 0xf0; ++ ++ itd_dbg("symbol_rate = %d\n", symbol_rate); ++ ++ /* not sure what is that ? - starting to download the table */ ++ itd1000_write_reg(state, CON1, con1 | (1 << 1)); ++ ++ for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++) ++ if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) { ++ itd_dbg("symrate: index: %d pgaext: %x, bbgvmin: %x\n", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin); ++ itd1000_write_reg(state, PLLFH, pllfh | (itd1000_lpf_pga[i].pgaext << 4)); ++ itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin)); ++ itd1000_write_reg(state, BW, bw | (i & 0x0f)); ++ break; ++ } ++ ++ itd1000_write_reg(state, CON1, con1 | (0 << 1)); ++} ++ ++static struct { ++ u8 vcorg; ++ u32 fmax_rg; ++} itd1000_vcorg[] = { ++ { 1, 920000 }, ++ { 2, 971000 }, ++ { 3, 1031000 }, ++ { 4, 1091000 }, ++ { 5, 1171000 }, ++ { 6, 1281000 }, ++ { 7, 1381000 }, ++ { 8, 500000 }, /* this is intentional. */ ++ { 9, 1451000 }, ++ { 10, 1531000 }, ++ { 11, 1631000 }, ++ { 12, 1741000 }, ++ { 13, 1891000 }, ++ { 14, 2071000 }, ++ { 15, 2250000 }, ++}; ++ ++static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz) ++{ ++ u8 i; ++ u8 gvbb_i2c = itd1000_read_reg(state, GVBB_I2C) & 0xbf; ++ u8 vco_chp1_i2c = itd1000_read_reg(state, VCO_CHP1_I2C) & 0x0f; ++ u8 adcout; ++ ++ /* reserved bit again (reset ?) */ ++ itd1000_write_reg(state, GVBB_I2C, gvbb_i2c | (1 << 6)); ++ ++ for (i = 0; i < ARRAY_SIZE(itd1000_vcorg); i++) { ++ if (freq_khz < itd1000_vcorg[i].fmax_rg) { ++ itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | (itd1000_vcorg[i].vcorg << 4)); ++ msleep(1); ++ ++ adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f; ++ ++ itd_dbg("VCO: %dkHz: %d -> ADCOUT: %d %02x\n", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c); ++ ++ if (adcout > 13) { ++ if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15)) ++ itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg + 1) << 4)); ++ } else if (adcout < 2) { ++ if (!(itd1000_vcorg[i].vcorg == 1 || itd1000_vcorg[i].vcorg == 9)) ++ itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg - 1) << 4)); ++ } ++ break; ++ } ++ } ++} ++ ++static const struct { ++ u32 freq; ++ u8 values[10]; /* RFTR, RFST1 - RFST9 */ ++} itd1000_fre_values[] = { ++ { 1075000, { 0x59, 0x1d, 0x1c, 0x17, 0x16, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, ++ { 1250000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, ++ { 1450000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, ++ { 1650000, { 0x69, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, ++ { 1750000, { 0x69, 0x1e, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, ++ { 1850000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, ++ { 1900000, { 0x69, 0x1d, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, ++ { 1950000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0d, 0x0b, 0x0a } }, ++ { 2050000, { 0x69, 0x1e, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0b, 0x0a } }, ++ { 2150000, { 0x69, 0x1d, 0x1c, 0x17, 0x15, 0x14, 0x13, 0x0f, 0x0e, 0x0b } } ++}; ++ ++ ++#define FREF 16 ++ ++static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz) ++{ ++ int i, j; ++ u32 plln, pllf; ++ u64 tmp; ++ ++ plln = (freq_khz * 1000) / 2 / FREF; ++ ++ /* Compute the factional part times 1000 */ ++ tmp = plln % 1000000; ++ plln /= 1000000; ++ ++ tmp *= 1048576; ++ do_div(tmp, 1000000); ++ pllf = (u32) tmp; ++ ++ state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF; ++ itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln); ++ ++ itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */ ++ itd1000_write_reg(state, PLLNL, plln & 0xff); ++ itd1000_write_reg(state, PLLFH, (itd1000_read_reg(state, PLLFH) & 0xf0) | ((pllf >> 16) & 0x0f)); ++ itd1000_write_reg(state, PLLFM, (pllf >> 8) & 0xff); ++ itd1000_write_reg(state, PLLFL, (pllf >> 0) & 0xff); ++ ++ for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) { ++ if (freq_khz <= itd1000_fre_values[i].freq) { ++ itd_dbg("fre_values: %d\n", i); ++ itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]); ++ for (j = 0; j < 9; j++) ++ itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]); ++ break; ++ } ++ } ++ ++ itd1000_set_vco(state, freq_khz); ++} ++ ++static int itd1000_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct itd1000_state *state = fe->tuner_priv; ++ u8 pllcon1; ++ ++ itd1000_set_lo(state, c->frequency); ++ itd1000_set_lpf_bw(state, c->symbol_rate); ++ ++ pllcon1 = itd1000_read_reg(state, PLLCON1) & 0x7f; ++ itd1000_write_reg(state, PLLCON1, pllcon1 | (1 << 7)); ++ itd1000_write_reg(state, PLLCON1, pllcon1); ++ ++ return 0; ++} ++ ++static int itd1000_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct itd1000_state *state = fe->tuner_priv; ++ *frequency = state->frequency; ++ return 0; ++} ++ ++static int itd1000_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ return 0; ++} ++ ++static u8 itd1000_init_tab[][2] = { ++ { PLLCON1, 0x65 }, /* Register does not change */ ++ { PLLNH, 0x80 }, /* Bits [7:6] do not change */ ++ { RESERVED_0X6D, 0x3b }, ++ { VCO_CHP2_I2C, 0x12 }, ++ { 0x72, 0xf9 }, /* No such regsister defined */ ++ { RESERVED_0X73, 0xff }, ++ { RESERVED_0X74, 0xb2 }, ++ { RESERVED_0X75, 0xc7 }, ++ { EXTGVBBRF, 0xf0 }, ++ { DIVAGCCK, 0x80 }, ++ { BBTR, 0xa0 }, ++ { RESERVED_0X7E, 0x4f }, ++ { 0x82, 0x88 }, /* No such regsister defined */ ++ { 0x83, 0x80 }, /* No such regsister defined */ ++ { 0x84, 0x80 }, /* No such regsister defined */ ++ { RESERVED_0X85, 0x74 }, ++ { RESERVED_0X86, 0xff }, ++ { RESERVED_0X88, 0x02 }, ++ { RESERVED_0X89, 0x16 }, ++ { RFST0, 0x1f }, ++ { RESERVED_0X94, 0x66 }, ++ { RESERVED_0X95, 0x66 }, ++ { RESERVED_0X96, 0x77 }, ++ { RESERVED_0X97, 0x99 }, ++ { RESERVED_0X98, 0xff }, ++ { RESERVED_0X99, 0xfc }, ++ { RESERVED_0X9A, 0xba }, ++ { RESERVED_0X9B, 0xaa }, ++}; ++ ++static u8 itd1000_reinit_tab[][2] = { ++ { VCO_CHP1_I2C, 0x8a }, ++ { BW, 0x87 }, ++ { GVBB_I2C, 0x03 }, ++ { BBGVMIN, 0x03 }, ++ { CON1, 0x2e }, ++}; ++ ++ ++static int itd1000_init(struct dvb_frontend *fe) ++{ ++ struct itd1000_state *state = fe->tuner_priv; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(itd1000_init_tab); i++) ++ itd1000_write_reg(state, itd1000_init_tab[i][0], itd1000_init_tab[i][1]); ++ ++ for (i = 0; i < ARRAY_SIZE(itd1000_reinit_tab); i++) ++ itd1000_write_reg(state, itd1000_reinit_tab[i][0], itd1000_reinit_tab[i][1]); ++ ++ return 0; ++} ++ ++static int itd1000_sleep(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int itd1000_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static const struct dvb_tuner_ops itd1000_tuner_ops = { ++ .info = { ++ .name = "Integrant ITD1000", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 125, /* kHz for QPSK frontends */ ++ }, ++ ++ .release = itd1000_release, ++ ++ .init = itd1000_init, ++ .sleep = itd1000_sleep, ++ ++ .set_params = itd1000_set_parameters, ++ .get_frequency = itd1000_get_frequency, ++ .get_bandwidth = itd1000_get_bandwidth ++}; ++ ++ ++struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) ++{ ++ struct itd1000_state *state = NULL; ++ u8 i = 0; ++ ++ state = kzalloc(sizeof(struct itd1000_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ ++ state->cfg = cfg; ++ state->i2c = i2c; ++ ++ i = itd1000_read_reg(state, 0); ++ if (i != 0) { ++ kfree(state); ++ return NULL; ++ } ++ itd_info("successfully identified (ID: %d)\n", i); ++ ++ memset(state->shadow, 0xff, sizeof(state->shadow)); ++ for (i = 0x65; i < 0x9c; i++) ++ state->shadow[i] = itd1000_read_reg(state, i); ++ ++ memcpy(&fe->ops.tuner_ops, &itd1000_tuner_ops, sizeof(struct dvb_tuner_ops)); ++ ++ fe->tuner_priv = state; ++ ++ return fe; ++} ++EXPORT_SYMBOL(itd1000_attach); ++ ++MODULE_AUTHOR("Patrick Boettcher "); ++MODULE_DESCRIPTION("Integrant ITD1000 driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/itd1000.h b/drivers/media/dvb-frontends/itd1000.h +new file mode 100644 +index 0000000..5e18df0 +--- /dev/null ++++ b/drivers/media/dvb-frontends/itd1000.h +@@ -0,0 +1,42 @@ ++/* ++ * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" ++ * ++ * Copyright (c) 2007 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef ITD1000_H ++#define ITD1000_H ++ ++struct dvb_frontend; ++struct i2c_adapter; ++ ++struct itd1000_config { ++ u8 i2c_address; ++}; ++ ++#if defined(CONFIG_DVB_TUNER_ITD1000) || (defined(CONFIG_DVB_TUNER_ITD1000_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); ++#else ++static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/itd1000_priv.h b/drivers/media/dvb-frontends/itd1000_priv.h +new file mode 100644 +index 0000000..08ca851 +--- /dev/null ++++ b/drivers/media/dvb-frontends/itd1000_priv.h +@@ -0,0 +1,88 @@ ++/* ++ * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" ++ * ++ * Copyright (c) 2007 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef ITD1000_PRIV_H ++#define ITD1000_PRIV_H ++ ++struct itd1000_state { ++ struct itd1000_config *cfg; ++ struct i2c_adapter *i2c; ++ ++ u32 frequency; /* contains the value resulting from the LO-setting */ ++ ++ /* ugly workaround for flexcop's incapable i2c-controller ++ * FIXME, if possible ++ */ ++ u8 shadow[256]; ++}; ++ ++enum itd1000_register { ++ VCO_CHP1 = 0x65, ++ VCO_CHP2, ++ PLLCON1, ++ PLLNH, ++ PLLNL, ++ PLLFH, ++ PLLFM, ++ PLLFL, ++ RESERVED_0X6D, ++ PLLLOCK, ++ VCO_CHP2_I2C, ++ VCO_CHP1_I2C, ++ BW, ++ RESERVED_0X73 = 0x73, ++ RESERVED_0X74, ++ RESERVED_0X75, ++ GVBB, ++ GVRF, ++ GVBB_I2C, ++ EXTGVBBRF, ++ DIVAGCCK, ++ BBTR, ++ RFTR, ++ BBGVMIN, ++ RESERVED_0X7E, ++ RESERVED_0X85 = 0x85, ++ RESERVED_0X86, ++ CON1, ++ RESERVED_0X88, ++ RESERVED_0X89, ++ RFST0, ++ RFST1, ++ RFST2, ++ RFST3, ++ RFST4, ++ RFST5, ++ RFST6, ++ RFST7, ++ RFST8, ++ RFST9, ++ RESERVED_0X94, ++ RESERVED_0X95, ++ RESERVED_0X96, ++ RESERVED_0X97, ++ RESERVED_0X98, ++ RESERVED_0X99, ++ RESERVED_0X9A, ++ RESERVED_0X9B, ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c +new file mode 100644 +index 0000000..0e3387e +--- /dev/null ++++ b/drivers/media/dvb-frontends/ix2505v.c +@@ -0,0 +1,325 @@ ++/** ++ * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner ++ * ++ * Copyright (C) 2010 Malcolm Priestley ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License Version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "ix2505v.h" ++ ++static int ix2505v_debug; ++#define dprintk(level, args...) do { \ ++ if (ix2505v_debug & level) \ ++ printk(KERN_DEBUG "ix2505v: " args); \ ++} while (0) ++ ++#define deb_info(args...) dprintk(0x01, args) ++#define deb_i2c(args...) dprintk(0x02, args) ++ ++struct ix2505v_state { ++ struct i2c_adapter *i2c; ++ const struct ix2505v_config *config; ++ u32 frequency; ++}; ++ ++/** ++ * Data read format of the Sharp IX2505V B0017 ++ * ++ * byte1: 1 | 1 | 0 | 0 | 0 | MA1 | MA0 | 1 ++ * byte2: POR | FL | RD2 | RD1 | RD0 | X | X | X ++ * ++ * byte1 = address ++ * byte2; ++ * POR = Power on Reset (VCC H=<2.2v L=>2.2v) ++ * FL = Phase Lock (H=lock L=unlock) ++ * RD0-2 = Reserved internal operations ++ * ++ * Only POR can be used to check the tuner is present ++ * ++ * Caution: after byte2 the I2C reverts to write mode continuing to read ++ * may corrupt tuning data. ++ * ++ */ ++ ++static int ix2505v_read_status_reg(struct ix2505v_state *state) ++{ ++ u8 addr = state->config->tuner_address; ++ u8 b2[] = {0}; ++ int ret; ++ ++ struct i2c_msg msg[1] = { ++ { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 1); ++ deb_i2c("Read %s ", __func__); ++ ++ return (ret == 1) ? (int) b2[0] : -1; ++} ++ ++static int ix2505v_write(struct ix2505v_state *state, u8 buf[], u8 count) ++{ ++ struct i2c_msg msg[1] = { ++ { .addr = state->config->tuner_address, .flags = 0, ++ .buf = buf, .len = count }, ++ }; ++ ++ int ret; ++ ++ ret = i2c_transfer(state->i2c, msg, 1); ++ ++ if (ret != 1) { ++ deb_i2c("%s: i2c error, ret=%d\n", __func__, ret); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ix2505v_release(struct dvb_frontend *fe) ++{ ++ struct ix2505v_state *state = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(state); ++ ++ return 0; ++} ++ ++/** ++ * Data write format of the Sharp IX2505V B0017 ++ * ++ * byte1: 1 | 1 | 0 | 0 | 0 | 0(MA1)| 0(MA0)| 0 ++ * byte2: 0 | BG1 | BG2 | N8 | N7 | N6 | N5 | N4 ++ * byte3: N3 | N2 | N1 | A5 | A4 | A3 | A2 | A1 ++ * byte4: 1 | 1(C1) | 1(C0) | PD5 | PD4 | TM | 0(RTS)| 1(REF) ++ * byte5: BA2 | BA1 | BA0 | PSC | PD3 |PD2/TS2|DIV/TS1|PD0/TS0 ++ * ++ * byte1 = address ++ * ++ * Write order ++ * 1) byte1 -> byte2 -> byte3 -> byte4 -> byte5 ++ * 2) byte1 -> byte4 -> byte5 -> byte2 -> byte3 ++ * 3) byte1 -> byte2 -> byte3 -> byte4 ++ * 4) byte1 -> byte4 -> byte5 -> byte2 ++ * 5) byte1 -> byte2 -> byte3 ++ * 6) byte1 -> byte4 -> byte5 ++ * 7) byte1 -> byte2 ++ * 8) byte1 -> byte4 ++ * ++ * Recommended Setup ++ * 1 -> 8 -> 6 ++ */ ++ ++static int ix2505v_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct ix2505v_state *state = fe->tuner_priv; ++ u32 frequency = c->frequency; ++ u32 b_w = (c->symbol_rate * 27) / 32000; ++ u32 div_factor, N , A, x; ++ int ret = 0, len; ++ u8 gain, cc, ref, psc, local_osc, lpf; ++ u8 data[4] = {0}; ++ ++ if ((frequency < fe->ops.info.frequency_min) ++ || (frequency > fe->ops.info.frequency_max)) ++ return -EINVAL; ++ ++ if (state->config->tuner_gain) ++ gain = (state->config->tuner_gain < 4) ++ ? state->config->tuner_gain : 0; ++ else ++ gain = 0x0; ++ ++ if (state->config->tuner_chargepump) ++ cc = state->config->tuner_chargepump; ++ else ++ cc = 0x3; ++ ++ ref = 8; /* REF =1 */ ++ psc = 32; /* PSC = 0 */ ++ ++ div_factor = (frequency * ref) / 40; /* local osc = 4Mhz */ ++ x = div_factor / psc; ++ N = x/100; ++ A = ((x - (N * 100)) * psc) / 100; ++ ++ data[0] = ((gain & 0x3) << 5) | (N >> 3); ++ data[1] = (N << 5) | (A & 0x1f); ++ data[2] = 0x81 | ((cc & 0x3) << 5) ; /*PD5,PD4 & TM = 0|C1,C0|REF=1*/ ++ ++ deb_info("Frq=%d x=%d N=%d A=%d\n", frequency, x, N, A); ++ ++ if (frequency <= 1065000) ++ local_osc = (6 << 5) | 2; ++ else if (frequency <= 1170000) ++ local_osc = (7 << 5) | 2; ++ else if (frequency <= 1300000) ++ local_osc = (1 << 5); ++ else if (frequency <= 1445000) ++ local_osc = (2 << 5); ++ else if (frequency <= 1607000) ++ local_osc = (3 << 5); ++ else if (frequency <= 1778000) ++ local_osc = (4 << 5); ++ else if (frequency <= 1942000) ++ local_osc = (5 << 5); ++ else /*frequency up to 2150000*/ ++ local_osc = (6 << 5); ++ ++ data[3] = local_osc; /* all other bits set 0 */ ++ ++ if (b_w <= 10000) ++ lpf = 0xc; ++ else if (b_w <= 12000) ++ lpf = 0x2; ++ else if (b_w <= 14000) ++ lpf = 0xa; ++ else if (b_w <= 16000) ++ lpf = 0x6; ++ else if (b_w <= 18000) ++ lpf = 0xe; ++ else if (b_w <= 20000) ++ lpf = 0x1; ++ else if (b_w <= 22000) ++ lpf = 0x9; ++ else if (b_w <= 24000) ++ lpf = 0x5; ++ else if (b_w <= 26000) ++ lpf = 0xd; ++ else if (b_w <= 28000) ++ lpf = 0x3; ++ else ++ lpf = 0xb; ++ ++ deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); ++ deb_info("Data 0=[%4phN]\n", data); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ len = sizeof(data); ++ ret |= ix2505v_write(state, data, len); ++ ++ data[2] |= 0x4; /* set TM = 1 other bits same */ ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ len = 1; ++ ret |= ix2505v_write(state, &data[2], len); /* write byte 4 only */ ++ ++ msleep(10); ++ ++ data[2] |= ((lpf >> 2) & 0x3) << 3; /* lpf */ ++ data[3] |= (lpf & 0x3) << 2; ++ ++ deb_info("Data 2=[%x%x]\n", data[2], data[3]); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ len = 2; ++ ret |= ix2505v_write(state, &data[2], len); /* write byte 4 & 5 */ ++ ++ if (state->config->min_delay_ms) ++ msleep(state->config->min_delay_ms); ++ ++ state->frequency = frequency; ++ ++ return ret; ++} ++ ++static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct ix2505v_state *state = fe->tuner_priv; ++ ++ *frequency = state->frequency; ++ ++ return 0; ++} ++ ++static struct dvb_tuner_ops ix2505v_tuner_ops = { ++ .info = { ++ .name = "Sharp IX2505V (B0017)", ++ .frequency_min = 950000, ++ .frequency_max = 2175000 ++ }, ++ .release = ix2505v_release, ++ .set_params = ix2505v_set_params, ++ .get_frequency = ix2505v_get_frequency, ++}; ++ ++struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, ++ const struct ix2505v_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct ix2505v_state *state = NULL; ++ int ret; ++ ++ if (NULL == config) { ++ deb_i2c("%s: no config ", __func__); ++ goto error; ++ } ++ ++ state = kzalloc(sizeof(struct ix2505v_state), GFP_KERNEL); ++ if (NULL == state) ++ return NULL; ++ ++ state->config = config; ++ state->i2c = i2c; ++ ++ if (state->config->tuner_write_only) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = ix2505v_read_status_reg(state); ++ ++ if (ret & 0x80) { ++ deb_i2c("%s: No IX2505V found\n", __func__); ++ goto error; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ fe->tuner_priv = state; ++ ++ memcpy(&fe->ops.tuner_ops, &ix2505v_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ deb_i2c("%s: initialization (%s addr=0x%02x) ok\n", ++ __func__, fe->ops.tuner_ops.info.name, config->tuner_address); ++ ++ return fe; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(ix2505v_attach); ++ ++module_param_named(debug, ix2505v_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++MODULE_DESCRIPTION("DVB IX2505V tuner driver"); ++MODULE_AUTHOR("Malcolm Priestley"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/ix2505v.h b/drivers/media/dvb-frontends/ix2505v.h +new file mode 100644 +index 0000000..67e89d6 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ix2505v.h +@@ -0,0 +1,64 @@ ++/** ++ * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner ++ * ++ * Copyright (C) 2010 Malcolm Priestley ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License Version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef DVB_IX2505V_H ++#define DVB_IX2505V_H ++ ++#include ++#include "dvb_frontend.h" ++ ++/** ++ * Attach a ix2505v tuner to the supplied frontend structure. ++ * ++ * @param fe Frontend to attach to. ++ * @param config ix2505v_config structure ++ * @return FE pointer on success, NULL on failure. ++ */ ++ ++struct ix2505v_config { ++ u8 tuner_address; ++ ++ /*Baseband AMP gain control 0/1=0dB(default) 2=-2bB 3=-4dB */ ++ u8 tuner_gain; ++ ++ /*Charge pump output +/- 0=120 1=260 2=555 3=1200(default) */ ++ u8 tuner_chargepump; ++ ++ /* delay after tune */ ++ int min_delay_ms; ++ ++ /* disables reads*/ ++ u8 tuner_write_only; ++ ++}; ++ ++#if defined(CONFIG_DVB_IX2505V) || \ ++ (defined(CONFIG_DVB_IX2505V_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, ++ const struct ix2505v_config *config, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, ++ const struct ix2505v_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* DVB_IX2505V_H */ +diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c +new file mode 100644 +index 0000000..ddf866c +--- /dev/null ++++ b/drivers/media/dvb-frontends/l64781.c +@@ -0,0 +1,609 @@ ++/* ++ driver for LSI L64781 COFDM demodulator ++ ++ Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH ++ Marko Kohtala ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "l64781.h" ++ ++ ++struct l64781_state { ++ struct i2c_adapter* i2c; ++ const struct l64781_config* config; ++ struct dvb_frontend frontend; ++ ++ /* private demodulator data */ ++ unsigned int first:1; ++}; ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "l64781: " args); \ ++ } while (0) ++ ++static int debug; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++ ++static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf [] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ ++ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) ++ dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", ++ __func__, reg, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int l64781_readreg (struct l64781_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) return ret; ++ ++ return b1[0]; ++} ++ ++static void apply_tps (struct l64781_state* state) ++{ ++ l64781_writereg (state, 0x2a, 0x00); ++ l64781_writereg (state, 0x2a, 0x01); ++ ++ /* This here is a little bit questionable because it enables ++ the automatic update of TPS registers. I think we'd need to ++ handle the IRQ from FE to update some other registers as ++ well, or at least implement some magic to tuning to correct ++ to the TPS received from transmission. */ ++ l64781_writereg (state, 0x2a, 0x02); ++} ++ ++ ++static void reset_afc (struct l64781_state* state) ++{ ++ /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for ++ timing offset */ ++ l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ ++ l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ ++ l64781_writereg (state, 0x09, 0); ++ l64781_writereg (state, 0x0a, 0); ++ l64781_writereg (state, 0x07, 0x8e); ++ l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ ++ l64781_writereg (state, 0x11, 0x80); /* stall TIM */ ++ l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ ++ l64781_writereg (state, 0x12, 0); ++ l64781_writereg (state, 0x13, 0); ++ l64781_writereg (state, 0x11, 0x00); ++} ++ ++static int reset_and_configure (struct l64781_state* state) ++{ ++ u8 buf [] = { 0x06 }; ++ struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; ++ // NOTE: this is correct in writing to address 0x00 ++ ++ return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; ++} ++ ++static int apply_frontend_param(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct l64781_state* state = fe->demodulator_priv; ++ /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ ++ static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; ++ /* QPSK, QAM_16, QAM_64 */ ++ static const u8 qam_tab [] = { 2, 4, 0, 6 }; ++ static const u8 guard_tab [] = { 1, 2, 4, 8 }; ++ /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ ++ static const u32 ppm = 8000; ++ u32 ddfs_offset_fixed; ++/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ ++/* bw_tab[p->bandWidth]<<10)/15625; */ ++ u32 init_freq; ++ u32 spi_bias; ++ u8 val0x04; ++ u8 val0x05; ++ u8 val0x06; ++ int bw; ++ ++ switch (p->bandwidth_hz) { ++ case 8000000: ++ bw = 8; ++ break; ++ case 7000000: ++ bw = 7; ++ break; ++ case 6000000: ++ bw = 6; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ if (p->inversion != INVERSION_ON && ++ p->inversion != INVERSION_OFF) ++ return -EINVAL; ++ ++ if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && ++ p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && ++ p->code_rate_HP != FEC_7_8) ++ return -EINVAL; ++ ++ if (p->hierarchy != HIERARCHY_NONE && ++ (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && ++ p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && ++ p->code_rate_LP != FEC_7_8)) ++ return -EINVAL; ++ ++ if (p->modulation != QPSK && p->modulation != QAM_16 && ++ p->modulation != QAM_64) ++ return -EINVAL; ++ ++ if (p->transmission_mode != TRANSMISSION_MODE_2K && ++ p->transmission_mode != TRANSMISSION_MODE_8K) ++ return -EINVAL; ++ ++ if ((int)p->guard_interval < GUARD_INTERVAL_1_32 || ++ p->guard_interval > GUARD_INTERVAL_1_4) ++ return -EINVAL; ++ ++ if ((int)p->hierarchy < HIERARCHY_NONE || ++ p->hierarchy > HIERARCHY_4) ++ return -EINVAL; ++ ++ ddfs_offset_fixed = 0x4000-(ppm<<16)/bw/1000000; ++ ++ /* This works up to 20000 ppm, it overflows if too large ppm! */ ++ init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / ++ bw & 0xFFFFFF); ++ ++ /* SPI bias calculation is slightly modified to fit in 32bit */ ++ /* will work for high ppm only... */ ++ spi_bias = 378 * (1 << 10); ++ spi_bias *= 16; ++ spi_bias *= bw; ++ spi_bias *= qam_tab[p->modulation]; ++ spi_bias /= p->code_rate_HP + 1; ++ spi_bias /= (guard_tab[p->guard_interval] + 32); ++ spi_bias *= 1000; ++ spi_bias /= 1000 + ppm/1000; ++ spi_bias *= p->code_rate_HP; ++ ++ val0x04 = (p->transmission_mode << 2) | p->guard_interval; ++ val0x05 = fec_tab[p->code_rate_HP]; ++ ++ if (p->hierarchy != HIERARCHY_NONE) ++ val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; ++ ++ val0x06 = (p->hierarchy << 2) | p->modulation; ++ ++ l64781_writereg (state, 0x04, val0x04); ++ l64781_writereg (state, 0x05, val0x05); ++ l64781_writereg (state, 0x06, val0x06); ++ ++ reset_afc (state); ++ ++ /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ ++ l64781_writereg (state, 0x15, ++ p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); ++ l64781_writereg (state, 0x16, init_freq & 0xff); ++ l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); ++ l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); ++ ++ l64781_writereg (state, 0x1b, spi_bias & 0xff); ++ l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); ++ l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | ++ (p->inversion == INVERSION_ON ? 0x80 : 0x00)); ++ ++ l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); ++ l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); ++ ++ l64781_readreg (state, 0x00); /* clear interrupt registers... */ ++ l64781_readreg (state, 0x01); /* dto. */ ++ ++ apply_tps (state); ++ ++ return 0; ++} ++ ++static int get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct l64781_state* state = fe->demodulator_priv; ++ int tmp; ++ ++ ++ tmp = l64781_readreg(state, 0x04); ++ switch(tmp & 3) { ++ case 0: ++ p->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ p->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ p->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ p->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ switch((tmp >> 2) & 3) { ++ case 0: ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ p->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ default: ++ printk(KERN_WARNING "Unexpected value for transmission_mode\n"); ++ } ++ ++ tmp = l64781_readreg(state, 0x05); ++ switch(tmp & 7) { ++ case 0: ++ p->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_HP = FEC_7_8; ++ break; ++ default: ++ printk("Unexpected value for code_rate_HP\n"); ++ } ++ switch((tmp >> 3) & 7) { ++ case 0: ++ p->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_LP = FEC_7_8; ++ break; ++ default: ++ printk("Unexpected value for code_rate_LP\n"); ++ } ++ ++ tmp = l64781_readreg(state, 0x06); ++ switch(tmp & 3) { ++ case 0: ++ p->modulation = QPSK; ++ break; ++ case 1: ++ p->modulation = QAM_16; ++ break; ++ case 2: ++ p->modulation = QAM_64; ++ break; ++ default: ++ printk(KERN_WARNING "Unexpected value for modulation\n"); ++ } ++ switch((tmp >> 2) & 7) { ++ case 0: ++ p->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ p->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ p->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ p->hierarchy = HIERARCHY_4; ++ break; ++ default: ++ printk("Unexpected value for hierarchy\n"); ++ } ++ ++ ++ tmp = l64781_readreg (state, 0x1d); ++ p->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; ++ ++ tmp = (int) (l64781_readreg (state, 0x08) | ++ (l64781_readreg (state, 0x09) << 8) | ++ (l64781_readreg (state, 0x0a) << 16)); ++ p->frequency += tmp; ++ ++ return 0; ++} ++ ++static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ int sync = l64781_readreg (state, 0x32); ++ int gain = l64781_readreg (state, 0x0e); ++ ++ l64781_readreg (state, 0x00); /* clear interrupt registers... */ ++ l64781_readreg (state, 0x01); /* dto. */ ++ ++ *status = 0; ++ ++ if (gain > 5) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 0x02) /* VCXO locked, this criteria should be ok */ ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 0x20) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 0x40) ++ *status |= FE_HAS_SYNC; ++ ++ if (sync == 0x7f) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int l64781_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ /* XXX FIXME: set up counting period (reg 0x26...0x28) ++ */ ++ *ber = l64781_readreg (state, 0x39) ++ | (l64781_readreg (state, 0x3a) << 8); ++ ++ return 0; ++} ++ ++static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ u8 gain = l64781_readreg (state, 0x0e); ++ *signal_strength = (gain << 8) | gain; ++ ++ return 0; ++} ++ ++static int l64781_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ u8 avg_quality = 0xff - l64781_readreg (state, 0x33); ++ *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ ++ ++ return 0; ++} ++ ++static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ *ucblocks = l64781_readreg (state, 0x37) ++ | (l64781_readreg (state, 0x38) << 8); ++ ++ return 0; ++} ++ ++static int l64781_sleep(struct dvb_frontend* fe) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ /* Power down */ ++ return l64781_writereg (state, 0x3e, 0x5a); ++} ++ ++static int l64781_init(struct dvb_frontend* fe) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ ++ reset_and_configure (state); ++ ++ /* Power up */ ++ l64781_writereg (state, 0x3e, 0xa5); ++ ++ /* Reset hard */ ++ l64781_writereg (state, 0x2a, 0x04); ++ l64781_writereg (state, 0x2a, 0x00); ++ ++ /* Set tuner specific things */ ++ /* AFC_POL, set also in reset_afc */ ++ l64781_writereg (state, 0x07, 0x8e); ++ ++ /* Use internal ADC */ ++ l64781_writereg (state, 0x0b, 0x81); ++ ++ /* AGC loop gain, and polarity is positive */ ++ l64781_writereg (state, 0x0c, 0x84); ++ ++ /* Internal ADC outputs two's complement */ ++ l64781_writereg (state, 0x0d, 0x8c); ++ ++ /* With ppm=8000, it seems the DTR_SENSITIVITY will result in ++ value of 2 with all possible bandwidths and guard ++ intervals, which is the initial value anyway. */ ++ /*l64781_writereg (state, 0x19, 0x92);*/ ++ ++ /* Everything is two's complement, soft bit and CSI_OUT too */ ++ l64781_writereg (state, 0x1e, 0x09); ++ ++ /* delay a bit after first init attempt */ ++ if (state->first) { ++ state->first = 0; ++ msleep(200); ++ } ++ ++ return 0; ++} ++ ++static int l64781_get_tune_settings(struct dvb_frontend* fe, ++ struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 4000; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void l64781_release(struct dvb_frontend* fe) ++{ ++ struct l64781_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops l64781_ops; ++ ++struct dvb_frontend* l64781_attach(const struct l64781_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct l64781_state* state = NULL; ++ int reg0x3e = -1; ++ u8 b0 [] = { 0x1a }; ++ u8 b1 [] = { 0x00 }; ++ struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->first = 1; ++ ++ /** ++ * the L64781 won't show up before we send the reset_and_configure() ++ * broadcast. If nothing responds there is no L64781 on the bus... ++ */ ++ if (reset_and_configure(state) < 0) { ++ dprintk("No response to reset and configure broadcast...\n"); ++ goto error; ++ } ++ ++ /* The chip always responds to reads */ ++ if (i2c_transfer(state->i2c, msg, 2) != 2) { ++ dprintk("No response to read on I2C bus\n"); ++ goto error; ++ } ++ ++ /* Save current register contents for bailout */ ++ reg0x3e = l64781_readreg(state, 0x3e); ++ ++ /* Reading the POWER_DOWN register always returns 0 */ ++ if (reg0x3e != 0) { ++ dprintk("Device doesn't look like L64781\n"); ++ goto error; ++ } ++ ++ /* Turn the chip off */ ++ l64781_writereg (state, 0x3e, 0x5a); ++ ++ /* Responds to all reads with 0 */ ++ if (l64781_readreg(state, 0x1a) != 0) { ++ dprintk("Read 1 returned unexpcted value\n"); ++ goto error; ++ } ++ ++ /* Turn the chip on */ ++ l64781_writereg (state, 0x3e, 0xa5); ++ ++ /* Responds with register default value */ ++ if (l64781_readreg(state, 0x1a) != 0xa1) { ++ dprintk("Read 2 returned unexpcted value\n"); ++ goto error; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ if (reg0x3e >= 0) ++ l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops l64781_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "LSI L64781 DVB-T", ++ /* .frequency_min = ???,*/ ++ /* .frequency_max = ???,*/ ++ .frequency_stepsize = 166666, ++ /* .frequency_tolerance = ???,*/ ++ /* .symbol_rate_tolerance = ???,*/ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = l64781_release, ++ ++ .init = l64781_init, ++ .sleep = l64781_sleep, ++ ++ .set_frontend = apply_frontend_param, ++ .get_frontend = get_frontend, ++ .get_tune_settings = l64781_get_tune_settings, ++ ++ .read_status = l64781_read_status, ++ .read_ber = l64781_read_ber, ++ .read_signal_strength = l64781_read_signal_strength, ++ .read_snr = l64781_read_snr, ++ .read_ucblocks = l64781_read_ucblocks, ++}; ++ ++MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(l64781_attach); +diff --git a/drivers/media/dvb-frontends/l64781.h b/drivers/media/dvb-frontends/l64781.h +new file mode 100644 +index 0000000..1305a9e +--- /dev/null ++++ b/drivers/media/dvb-frontends/l64781.h +@@ -0,0 +1,46 @@ ++/* ++ driver for LSI L64781 COFDM demodulator ++ ++ Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH ++ Marko Kohtala ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef L64781_H ++#define L64781_H ++ ++#include ++ ++struct l64781_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++#if defined(CONFIG_DVB_L64781) || (defined(CONFIG_DVB_L64781_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_L64781 ++ ++#endif // L64781_H +diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c +new file mode 100644 +index 0000000..5fd14f8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lg2160.c +@@ -0,0 +1,1464 @@ ++/* ++ * Support for LG2160 - ATSC/MH ++ * ++ * Copyright (C) 2010 Michael Krufky ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include "lg2160.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); ++ ++#define DBG_INFO 1 ++#define DBG_REG 2 ++ ++#define lg_printk(kern, fmt, arg...) \ ++ printk(kern "%s: " fmt, __func__, ##arg) ++ ++#define lg_info(fmt, arg...) printk(KERN_INFO "lg2160: " fmt, ##arg) ++#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) ++#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) ++#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ ++ lg_printk(KERN_DEBUG, fmt, ##arg) ++#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ ++ lg_printk(KERN_DEBUG, fmt, ##arg) ++ ++#define lg_fail(ret) \ ++({ \ ++ int __ret; \ ++ __ret = (ret < 0); \ ++ if (__ret) \ ++ lg_err("error %d on line %d\n", ret, __LINE__); \ ++ __ret; \ ++}) ++ ++struct lg216x_state { ++ struct i2c_adapter *i2c_adap; ++ const struct lg2160_config *cfg; ++ ++ struct dvb_frontend frontend; ++ ++ u32 current_frequency; ++ u8 parade_id; ++ u8 fic_ver; ++ unsigned int last_reset; ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_write_reg(struct lg216x_state *state, u16 reg, u8 val) ++{ ++ int ret; ++ u8 buf[] = { reg >> 8, reg & 0xff, val }; ++ struct i2c_msg msg = { ++ .addr = state->cfg->i2c_addr, .flags = 0, ++ .buf = buf, .len = 3, ++ }; ++ ++ lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); ++ ++ ret = i2c_transfer(state->i2c_adap, &msg, 1); ++ ++ if (ret != 1) { ++ lg_err("error (addr %02x %02x <- %02x, err = %i)\n", ++ msg.buf[0], msg.buf[1], msg.buf[2], ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int lg216x_read_reg(struct lg216x_state *state, u16 reg, u8 *val) ++{ ++ int ret; ++ u8 reg_buf[] = { reg >> 8, reg & 0xff }; ++ struct i2c_msg msg[] = { ++ { .addr = state->cfg->i2c_addr, ++ .flags = 0, .buf = reg_buf, .len = 2 }, ++ { .addr = state->cfg->i2c_addr, ++ .flags = I2C_M_RD, .buf = val, .len = 1 }, ++ }; ++ ++ lg_reg("reg: 0x%04x\n", reg); ++ ++ ret = i2c_transfer(state->i2c_adap, msg, 2); ++ ++ if (ret != 2) { ++ lg_err("error (addr %02x reg %04x error (ret == %i)\n", ++ state->cfg->i2c_addr, reg, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++struct lg216x_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++static int lg216x_write_regs(struct lg216x_state *state, ++ struct lg216x_reg *regs, int len) ++{ ++ int i, ret; ++ ++ lg_reg("writing %d registers...\n", len); ++ ++ for (i = 0; i < len; i++) { ++ ret = lg216x_write_reg(state, regs[i].reg, regs[i].val); ++ if (lg_fail(ret)) ++ return ret; ++ } ++ return 0; ++} ++ ++static int lg216x_set_reg_bit(struct lg216x_state *state, ++ u16 reg, int bit, int onoff) ++{ ++ u8 val; ++ int ret; ++ ++ lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); ++ ++ ret = lg216x_read_reg(state, reg, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= ~(1 << bit); ++ val |= (onoff & 1) << bit; ++ ++ ret = lg216x_write_reg(state, reg, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ int ret; ++ ++ if (state->cfg->deny_i2c_rptr) ++ return 0; ++ ++ lg_dbg("(%d)\n", enable); ++ ++ ret = lg216x_set_reg_bit(state, 0x0000, 0, enable ? 0 : 1); ++ ++ msleep(1); ++ ++ return ret; ++} ++ ++static int lg216x_soft_reset(struct lg216x_state *state) ++{ ++ int ret; ++ ++ lg_dbg("\n"); ++ ++ ret = lg216x_write_reg(state, 0x0002, 0x00); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ msleep(20); ++ ret = lg216x_write_reg(state, 0x0002, 0x01); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ state->last_reset = jiffies_to_msecs(jiffies); ++fail: ++ return ret; ++} ++ ++static int lg216x_initialize(struct lg216x_state *state) ++{ ++ int ret; ++ ++ static struct lg216x_reg lg2160_init[] = { ++#if 0 ++ { .reg = 0x0015, .val = 0xe6 }, ++#else ++ { .reg = 0x0015, .val = 0xf7 }, ++ { .reg = 0x001b, .val = 0x52 }, ++ { .reg = 0x0208, .val = 0x00 }, ++ { .reg = 0x0209, .val = 0x82 }, ++ { .reg = 0x0210, .val = 0xf9 }, ++ { .reg = 0x020a, .val = 0x00 }, ++ { .reg = 0x020b, .val = 0x82 }, ++ { .reg = 0x020d, .val = 0x28 }, ++ { .reg = 0x020f, .val = 0x14 }, ++#endif ++ }; ++ ++ static struct lg216x_reg lg2161_init[] = { ++ { .reg = 0x0000, .val = 0x41 }, ++ { .reg = 0x0001, .val = 0xfb }, ++ { .reg = 0x0216, .val = 0x00 }, ++ { .reg = 0x0219, .val = 0x00 }, ++ { .reg = 0x021b, .val = 0x55 }, ++ { .reg = 0x0606, .val = 0x0a }, ++ }; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_write_regs(state, ++ lg2160_init, ARRAY_SIZE(lg2160_init)); ++ break; ++ case LG2161: ++ ret = lg216x_write_regs(state, ++ lg2161_init, ARRAY_SIZE(lg2161_init)); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_soft_reset(state); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_set_if(struct lg216x_state *state) ++{ ++ u8 val; ++ int ret; ++ ++ lg_dbg("%d KHz\n", state->cfg->if_khz); ++ ++ ret = lg216x_read_reg(state, 0x0132, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xfb; ++ val |= (0 == state->cfg->if_khz) ? 0x04 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0132, val); ++ lg_fail(ret); ++ ++ /* if NOT zero IF, 6 MHz is the default */ ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg2160_agc_fix(struct lg216x_state *state, ++ int if_agc_fix, int rf_agc_fix) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0100, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xf3; ++ val |= (if_agc_fix) ? 0x08 : 0x00; ++ val |= (rf_agc_fix) ? 0x04 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0100, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++#if 0 ++static int lg2160_agc_freeze(struct lg216x_state *state, ++ int if_agc_freeze, int rf_agc_freeze) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0100, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xcf; ++ val |= (if_agc_freeze) ? 0x20 : 0x00; ++ val |= (rf_agc_freeze) ? 0x10 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0100, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++#endif ++ ++static int lg2160_agc_polarity(struct lg216x_state *state, ++ int if_agc_polarity, int rf_agc_polarity) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0100, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xfc; ++ val |= (if_agc_polarity) ? 0x02 : 0x00; ++ val |= (rf_agc_polarity) ? 0x01 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0100, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg2160_tuner_pwr_save_polarity(struct lg216x_state *state, ++ int polarity) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0008, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xfe; ++ val |= (polarity) ? 0x01 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0008, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg2160_spectrum_polarity(struct lg216x_state *state, ++ int inverted) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0132, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xfd; ++ val |= (inverted) ? 0x02 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0132, val); ++ lg_fail(ret); ++fail: ++ return lg216x_soft_reset(state); ++} ++ ++static int lg2160_tuner_pwr_save(struct lg216x_state *state, int onoff) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0007, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xbf; ++ val |= (onoff) ? 0x40 : 0x00; ++ ++ ret = lg216x_write_reg(state, 0x0007, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg216x_set_parade(struct lg216x_state *state, int id) ++{ ++ int ret; ++ ++ ret = lg216x_write_reg(state, 0x013e, id & 0x7f); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ state->parade_id = id & 0x7f; ++fail: ++ return ret; ++} ++ ++static int lg216x_set_ensemble(struct lg216x_state *state, int id) ++{ ++ int ret; ++ u16 reg; ++ u8 val; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ reg = 0x0400; ++ break; ++ case LG2161: ++ default: ++ reg = 0x0500; ++ break; ++ } ++ ++ ret = lg216x_read_reg(state, reg, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xfe; ++ val |= (id) ? 0x01 : 0x00; ++ ++ ret = lg216x_write_reg(state, reg, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg2160_set_spi_clock(struct lg216x_state *state) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0014, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0xf3; ++ val |= (state->cfg->spi_clock << 2); ++ ++ ret = lg216x_write_reg(state, 0x0014, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg2161_set_output_interface(struct lg216x_state *state) ++{ ++ u8 val; ++ int ret; ++ ++ ret = lg216x_read_reg(state, 0x0014, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= ~0x07; ++ val |= state->cfg->output_if; /* FIXME: needs sanity check */ ++ ++ ret = lg216x_write_reg(state, 0x0014, val); ++ lg_fail(ret); ++fail: ++ return ret; ++} ++ ++static int lg216x_enable_fic(struct lg216x_state *state, int onoff) ++{ ++ int ret; ++ ++ ret = lg216x_write_reg(state, 0x0017, 0x23); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_write_reg(state, 0x0016, 0xfc); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_write_reg(state, 0x0016, ++ 0xfc | ((onoff) ? 0x02 : 0x00)); ++ break; ++ case LG2161: ++ ret = lg216x_write_reg(state, 0x0016, (onoff) ? 0x10 : 0x00); ++ break; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_initialize(state); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ if (onoff) { ++ ret = lg216x_write_reg(state, 0x0017, 0x03); ++ lg_fail(ret); ++ } ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_get_fic_version(struct lg216x_state *state, u8 *ficver) ++{ ++ u8 val; ++ int ret; ++ ++ *ficver = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0128, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *ficver = (val >> 3) & 0x1f; ++fail: ++ return ret; ++} ++ ++#if 0 ++static int lg2160_get_parade_id(struct lg216x_state *state, u8 *id) ++{ ++ u8 val; ++ int ret; ++ ++ *id = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0123, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *id = val & 0x7f; ++fail: ++ return ret; ++} ++#endif ++ ++static int lg216x_get_nog(struct lg216x_state *state, u8 *nog) ++{ ++ u8 val; ++ int ret; ++ ++ *nog = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0124, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *nog = ((val >> 4) & 0x07) + 1; ++fail: ++ return ret; ++} ++ ++static int lg216x_get_tnog(struct lg216x_state *state, u8 *tnog) ++{ ++ u8 val; ++ int ret; ++ ++ *tnog = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0125, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *tnog = val & 0x1f; ++fail: ++ return ret; ++} ++ ++static int lg216x_get_sgn(struct lg216x_state *state, u8 *sgn) ++{ ++ u8 val; ++ int ret; ++ ++ *sgn = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0124, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *sgn = val & 0x0f; ++fail: ++ return ret; ++} ++ ++static int lg216x_get_prc(struct lg216x_state *state, u8 *prc) ++{ ++ u8 val; ++ int ret; ++ ++ *prc = 0xff; /* invalid value */ ++ ++ ret = lg216x_read_reg(state, 0x0125, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *prc = ((val >> 5) & 0x07) + 1; ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_get_rs_frame_mode(struct lg216x_state *state, ++ enum atscmh_rs_frame_mode *rs_framemode) ++{ ++ u8 val; ++ int ret; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0410, &val); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x0513, &val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch ((val >> 4) & 0x03) { ++#if 1 ++ default: ++#endif ++ case 0x00: ++ *rs_framemode = ATSCMH_RSFRAME_PRI_ONLY; ++ break; ++ case 0x01: ++ *rs_framemode = ATSCMH_RSFRAME_PRI_SEC; ++ break; ++#if 0 ++ default: ++ *rs_framemode = ATSCMH_RSFRAME_RES; ++ break; ++#endif ++ } ++fail: ++ return ret; ++} ++ ++static ++int lg216x_get_rs_frame_ensemble(struct lg216x_state *state, ++ enum atscmh_rs_frame_ensemble *rs_frame_ens) ++{ ++ u8 val; ++ int ret; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0400, &val); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x0500, &val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= 0x01; ++ *rs_frame_ens = (enum atscmh_rs_frame_ensemble) val; ++fail: ++ return ret; ++} ++ ++static int lg216x_get_rs_code_mode(struct lg216x_state *state, ++ enum atscmh_rs_code_mode *rs_code_pri, ++ enum atscmh_rs_code_mode *rs_code_sec) ++{ ++ u8 val; ++ int ret; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0410, &val); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x0513, &val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *rs_code_pri = (enum atscmh_rs_code_mode) ((val >> 2) & 0x03); ++ *rs_code_sec = (enum atscmh_rs_code_mode) (val & 0x03); ++fail: ++ return ret; ++} ++ ++static int lg216x_get_sccc_block_mode(struct lg216x_state *state, ++ enum atscmh_sccc_block_mode *sccc_block) ++{ ++ u8 val; ++ int ret; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0315, &val); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x0511, &val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch (val & 0x03) { ++ case 0x00: ++ *sccc_block = ATSCMH_SCCC_BLK_SEP; ++ break; ++ case 0x01: ++ *sccc_block = ATSCMH_SCCC_BLK_COMB; ++ break; ++ default: ++ *sccc_block = ATSCMH_SCCC_BLK_RES; ++ break; ++ } ++fail: ++ return ret; ++} ++ ++static int lg216x_get_sccc_code_mode(struct lg216x_state *state, ++ enum atscmh_sccc_code_mode *mode_a, ++ enum atscmh_sccc_code_mode *mode_b, ++ enum atscmh_sccc_code_mode *mode_c, ++ enum atscmh_sccc_code_mode *mode_d) ++{ ++ u8 val; ++ int ret; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0316, &val); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x0512, &val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch ((val >> 6) & 0x03) { ++ case 0x00: ++ *mode_a = ATSCMH_SCCC_CODE_HLF; ++ break; ++ case 0x01: ++ *mode_a = ATSCMH_SCCC_CODE_QTR; ++ break; ++ default: ++ *mode_a = ATSCMH_SCCC_CODE_RES; ++ break; ++ } ++ ++ switch ((val >> 4) & 0x03) { ++ case 0x00: ++ *mode_b = ATSCMH_SCCC_CODE_HLF; ++ break; ++ case 0x01: ++ *mode_b = ATSCMH_SCCC_CODE_QTR; ++ break; ++ default: ++ *mode_b = ATSCMH_SCCC_CODE_RES; ++ break; ++ } ++ ++ switch ((val >> 2) & 0x03) { ++ case 0x00: ++ *mode_c = ATSCMH_SCCC_CODE_HLF; ++ break; ++ case 0x01: ++ *mode_c = ATSCMH_SCCC_CODE_QTR; ++ break; ++ default: ++ *mode_c = ATSCMH_SCCC_CODE_RES; ++ break; ++ } ++ ++ switch (val & 0x03) { ++ case 0x00: ++ *mode_d = ATSCMH_SCCC_CODE_HLF; ++ break; ++ case 0x01: ++ *mode_d = ATSCMH_SCCC_CODE_QTR; ++ break; ++ default: ++ *mode_d = ATSCMH_SCCC_CODE_RES; ++ break; ++ } ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++#if 0 ++static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err) ++{ ++ u8 fic_err; ++ int ret; ++ ++ *err = 0; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg216x_read_reg(state, 0x0012, &fic_err); ++ break; ++ case LG2161: ++ ret = lg216x_read_reg(state, 0x001e, &fic_err); ++ break; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *err = fic_err; ++fail: ++ return ret; ++} ++ ++static int lg2160_read_crc_err_count(struct lg216x_state *state, u16 *err) ++{ ++ u8 crc_err1, crc_err2; ++ int ret; ++ ++ *err = 0; ++ ++ ret = lg216x_read_reg(state, 0x0411, &crc_err1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_read_reg(state, 0x0412, &crc_err2); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *err = (u16)(((crc_err2 & 0x0f) << 8) | crc_err1); ++fail: ++ return ret; ++} ++ ++static int lg2161_read_crc_err_count(struct lg216x_state *state, u16 *err) ++{ ++ u8 crc_err; ++ int ret; ++ ++ *err = 0; ++ ++ ret = lg216x_read_reg(state, 0x0612, &crc_err); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *err = (u16)crc_err; ++fail: ++ return ret; ++} ++ ++static int lg216x_read_crc_err_count(struct lg216x_state *state, u16 *err) ++{ ++ int ret; ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg2160_read_crc_err_count(state, err); ++ break; ++ case LG2161: ++ ret = lg2161_read_crc_err_count(state, err); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int lg2160_read_rs_err_count(struct lg216x_state *state, u16 *err) ++{ ++ u8 rs_err1, rs_err2; ++ int ret; ++ ++ *err = 0; ++ ++ ret = lg216x_read_reg(state, 0x0413, &rs_err1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_read_reg(state, 0x0414, &rs_err2); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *err = (u16)(((rs_err2 & 0x0f) << 8) | rs_err1); ++fail: ++ return ret; ++} ++ ++static int lg2161_read_rs_err_count(struct lg216x_state *state, u16 *err) ++{ ++ u8 rs_err1, rs_err2; ++ int ret; ++ ++ *err = 0; ++ ++ ret = lg216x_read_reg(state, 0x0613, &rs_err1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_read_reg(state, 0x0614, &rs_err2); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *err = (u16)((rs_err1 << 8) | rs_err2); ++fail: ++ return ret; ++} ++ ++static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err) ++{ ++ int ret; ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg2160_read_rs_err_count(state, err); ++ break; ++ case LG2161: ++ ret = lg2161_read_rs_err_count(state, err); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++#endif ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_get_frontend(struct dvb_frontend *fe) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ int ret; ++ ++ lg_dbg("\n"); ++ ++ fe->dtv_property_cache.modulation = VSB_8; ++ fe->dtv_property_cache.frequency = state->current_frequency; ++ fe->dtv_property_cache.delivery_system = SYS_ATSCMH; ++ ++ ret = lg216x_get_fic_version(state, ++ &fe->dtv_property_cache.atscmh_fic_ver); ++ if (lg_fail(ret)) ++ goto fail; ++ if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) { ++ state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver; ++ ++#if 0 ++ ret = lg2160_get_parade_id(state, ++ &fe->dtv_property_cache.atscmh_parade_id); ++ if (lg_fail(ret)) ++ goto fail; ++/* #else */ ++ fe->dtv_property_cache.atscmh_parade_id = state->parade_id; ++#endif ++ ret = lg216x_get_nog(state, ++ &fe->dtv_property_cache.atscmh_nog); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_tnog(state, ++ &fe->dtv_property_cache.atscmh_tnog); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_sgn(state, ++ &fe->dtv_property_cache.atscmh_sgn); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_prc(state, ++ &fe->dtv_property_cache.atscmh_prc); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_get_rs_frame_mode(state, ++ (enum atscmh_rs_frame_mode *) ++ &fe->dtv_property_cache.atscmh_rs_frame_mode); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_rs_frame_ensemble(state, ++ (enum atscmh_rs_frame_ensemble *) ++ &fe->dtv_property_cache.atscmh_rs_frame_ensemble); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_rs_code_mode(state, ++ (enum atscmh_rs_code_mode *) ++ &fe->dtv_property_cache.atscmh_rs_code_mode_pri, ++ (enum atscmh_rs_code_mode *) ++ &fe->dtv_property_cache.atscmh_rs_code_mode_sec); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_sccc_block_mode(state, ++ (enum atscmh_sccc_block_mode *) ++ &fe->dtv_property_cache.atscmh_sccc_block_mode); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_get_sccc_code_mode(state, ++ (enum atscmh_sccc_code_mode *) ++ &fe->dtv_property_cache.atscmh_sccc_code_mode_a, ++ (enum atscmh_sccc_code_mode *) ++ &fe->dtv_property_cache.atscmh_sccc_code_mode_b, ++ (enum atscmh_sccc_code_mode *) ++ &fe->dtv_property_cache.atscmh_sccc_code_mode_c, ++ (enum atscmh_sccc_code_mode *) ++ &fe->dtv_property_cache.atscmh_sccc_code_mode_d); ++ if (lg_fail(ret)) ++ goto fail; ++ } ++#if 0 ++ ret = lg216x_read_fic_err_count(state, ++ (u8 *)&fe->dtv_property_cache.atscmh_fic_err); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_read_crc_err_count(state, ++ &fe->dtv_property_cache.atscmh_crc_err); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_read_rs_err_count(state, ++ &fe->dtv_property_cache.atscmh_rs_err); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ if (((fe->dtv_property_cache.atscmh_rs_err >= 240) && ++ (fe->dtv_property_cache.atscmh_crc_err >= 240)) && ++ ((jiffies_to_msecs(jiffies) - state->last_reset) > 6000)) ++ ret = lg216x_soft_reset(state); ++ break; ++ case LG2161: ++ /* no fix needed here (as far as we know) */ ++ ret = 0; ++ break; ++ } ++ lg_fail(ret); ++#endif ++fail: ++ return ret; ++} ++ ++static int lg216x_get_property(struct dvb_frontend *fe, ++ struct dtv_property *tvp) ++{ ++ return (DTV_ATSCMH_FIC_VER == tvp->cmd) ? ++ lg216x_get_frontend(fe) : 0; ++} ++ ++ ++static int lg2160_set_frontend(struct dvb_frontend *fe) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ int ret; ++ ++ lg_dbg("(%d)\n", fe->dtv_property_cache.frequency); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ state->current_frequency = fe->dtv_property_cache.frequency; ++ } ++ ++ ret = lg2160_agc_fix(state, 0, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg2160_agc_polarity(state, 0, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg2160_tuner_pwr_save_polarity(state, 1); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg216x_set_if(state); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lg2160_spectrum_polarity(state, state->cfg->spectral_inversion); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ /* be tuned before this point */ ++ ret = lg216x_soft_reset(state); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg2160_tuner_pwr_save(state, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg2160_set_spi_clock(state); ++ if (lg_fail(ret)) ++ goto fail; ++ break; ++ case LG2161: ++ ret = lg2161_set_output_interface(state); ++ if (lg_fail(ret)) ++ goto fail; ++ break; ++ } ++ ++ ret = lg216x_set_parade(state, fe->dtv_property_cache.atscmh_parade_id); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_set_ensemble(state, ++ fe->dtv_property_cache.atscmh_rs_frame_ensemble); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_initialize(state); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_enable_fic(state, 1); ++ lg_fail(ret); ++ ++ lg216x_get_frontend(fe); ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg2160_read_lock_status(struct lg216x_state *state, ++ int *acq_lock, int *sync_lock) ++{ ++ u8 val; ++ int ret; ++ ++ *acq_lock = 0; ++ *sync_lock = 0; ++ ++ ret = lg216x_read_reg(state, 0x011b, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *sync_lock = (val & 0x20) ? 0 : 1; ++ *acq_lock = (val & 0x40) ? 0 : 1; ++fail: ++ return ret; ++} ++ ++#ifdef USE_LG2161_LOCK_BITS ++static int lg2161_read_lock_status(struct lg216x_state *state, ++ int *acq_lock, int *sync_lock) ++{ ++ u8 val; ++ int ret; ++ ++ *acq_lock = 0; ++ *sync_lock = 0; ++ ++ ret = lg216x_read_reg(state, 0x0304, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *sync_lock = (val & 0x80) ? 0 : 1; ++ ++ ret = lg216x_read_reg(state, 0x011b, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *acq_lock = (val & 0x40) ? 0 : 1; ++fail: ++ return ret; ++} ++#endif ++ ++static int lg216x_read_lock_status(struct lg216x_state *state, ++ int *acq_lock, int *sync_lock) ++{ ++#ifdef USE_LG2161_LOCK_BITS ++ int ret; ++ switch (state->cfg->lg_chip) { ++ case LG2160: ++ ret = lg2160_read_lock_status(state, acq_lock, sync_lock); ++ break; ++ case LG2161: ++ ret = lg2161_read_lock_status(state, acq_lock, sync_lock); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++#else ++ return lg2160_read_lock_status(state, acq_lock, sync_lock); ++#endif ++} ++ ++static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ int ret, acq_lock, sync_lock; ++ ++ *status = 0; ++ ++ ret = lg216x_read_lock_status(state, &acq_lock, &sync_lock); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ lg_dbg("%s%s\n", ++ acq_lock ? "SIGNALEXIST " : "", ++ sync_lock ? "SYNCLOCK" : ""); ++ ++ if (acq_lock) ++ *status |= FE_HAS_SIGNAL; ++ if (sync_lock) ++ *status |= FE_HAS_SYNC; ++ ++ if (*status) ++ *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; ++ ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg2160_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ u8 snr1, snr2; ++ int ret; ++ ++ *snr = 0; ++ ++ ret = lg216x_read_reg(state, 0x0202, &snr1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_read_reg(state, 0x0203, &snr2); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ if ((snr1 == 0xba) || (snr2 == 0xdf)) ++ *snr = 0; ++ else ++#if 1 ++ *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 >> 4); ++#else /* BCD */ ++ *snr = (snr2 | (snr1 << 8)); ++#endif ++fail: ++ return ret; ++} ++ ++static int lg2161_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ u8 snr1, snr2; ++ int ret; ++ ++ *snr = 0; ++ ++ ret = lg216x_read_reg(state, 0x0302, &snr1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lg216x_read_reg(state, 0x0303, &snr2); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ if ((snr1 == 0xba) || (snr2 == 0xfd)) ++ *snr = 0; ++ else ++ ++ *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 & 0x0f); ++fail: ++ return ret; ++} ++ ++static int lg216x_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++#if 0 ++ /* borrowed from lgdt330x.c ++ * ++ * Calculate strength from SNR up to 35dB ++ * Even though the SNR can go higher than 35dB, ++ * there is some comfort factor in having a range of ++ * strong signals that can show at 100% ++ */ ++ struct lg216x_state *state = fe->demodulator_priv; ++ u16 snr; ++ int ret; ++#endif ++ *strength = 0; ++#if 0 ++ ret = fe->ops.read_snr(fe, &snr); ++ if (lg_fail(ret)) ++ goto fail; ++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ ++ /* scale the range 0 - 35*2^24 into 0 - 65535 */ ++ if (state->snr >= 8960 * 0x10000) ++ *strength = 0xffff; ++ else ++ *strength = state->snr / 8960; ++fail: ++ return ret; ++#else ++ return 0; ++#endif ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++#if 0 ++ struct lg216x_state *state = fe->demodulator_priv; ++ int ret; ++ ++ ret = lg216x_read_rs_err_count(state, ++ &fe->dtv_property_cache.atscmh_rs_err); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ *ucblocks = fe->dtv_property_cache.atscmh_rs_err; ++fail: ++#else ++ *ucblocks = 0; ++#endif ++ return 0; ++} ++ ++static int lg216x_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings ++ *fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 500; ++ lg_dbg("\n"); ++ return 0; ++} ++ ++static void lg216x_release(struct dvb_frontend *fe) ++{ ++ struct lg216x_state *state = fe->demodulator_priv; ++ lg_dbg("\n"); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops lg2160_ops = { ++ .delsys = { SYS_ATSCMH }, ++ .info = { ++ .name = "LG Electronics LG2160 ATSC/MH Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ }, ++ .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, ++#if 0 ++ .init = lg216x_init, ++ .sleep = lg216x_sleep, ++#endif ++ .get_property = lg216x_get_property, ++ ++ .set_frontend = lg2160_set_frontend, ++ .get_frontend = lg216x_get_frontend, ++ .get_tune_settings = lg216x_get_tune_settings, ++ .read_status = lg216x_read_status, ++#if 0 ++ .read_ber = lg216x_read_ber, ++#endif ++ .read_signal_strength = lg216x_read_signal_strength, ++ .read_snr = lg2160_read_snr, ++ .read_ucblocks = lg216x_read_ucblocks, ++ .release = lg216x_release, ++}; ++ ++static struct dvb_frontend_ops lg2161_ops = { ++ .delsys = { SYS_ATSCMH }, ++ .info = { ++ .name = "LG Electronics LG2161 ATSC/MH Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ }, ++ .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, ++#if 0 ++ .init = lg216x_init, ++ .sleep = lg216x_sleep, ++#endif ++ .get_property = lg216x_get_property, ++ ++ .set_frontend = lg2160_set_frontend, ++ .get_frontend = lg216x_get_frontend, ++ .get_tune_settings = lg216x_get_tune_settings, ++ .read_status = lg216x_read_status, ++#if 0 ++ .read_ber = lg216x_read_ber, ++#endif ++ .read_signal_strength = lg216x_read_signal_strength, ++ .read_snr = lg2161_read_snr, ++ .read_ucblocks = lg216x_read_ucblocks, ++ .release = lg216x_release, ++}; ++ ++struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ struct lg216x_state *state = NULL; ++ ++ lg_dbg("(%d-%04x)\n", ++ i2c_adap ? i2c_adapter_id(i2c_adap) : 0, ++ config ? config->i2c_addr : 0); ++ ++ state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->cfg = config; ++ state->i2c_adap = i2c_adap; ++ state->fic_ver = 0xff; ++ state->parade_id = 0xff; ++ ++ switch (config->lg_chip) { ++ default: ++ lg_warn("invalid chip requested, defaulting to LG2160"); ++ /* fall-thru */ ++ case LG2160: ++ memcpy(&state->frontend.ops, &lg2160_ops, ++ sizeof(struct dvb_frontend_ops)); ++ break; ++ case LG2161: ++ memcpy(&state->frontend.ops, &lg2161_ops, ++ sizeof(struct dvb_frontend_ops)); ++ break; ++ } ++ ++ state->frontend.demodulator_priv = state; ++ state->current_frequency = -1; ++ /* parade 1 by default */ ++ state->frontend.dtv_property_cache.atscmh_parade_id = 1; ++ ++ return &state->frontend; ++} ++EXPORT_SYMBOL(lg2160_attach); ++ ++MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); ++MODULE_AUTHOR("Michael Krufky "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.3"); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/lg2160.h b/drivers/media/dvb-frontends/lg2160.h +new file mode 100644 +index 0000000..9e2c0f4 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lg2160.h +@@ -0,0 +1,84 @@ ++/* ++ * Support for LG2160 - ATSC/MH ++ * ++ * Copyright (C) 2010 Michael Krufky ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _LG2160_H_ ++#define _LG2160_H_ ++ ++#include ++#include "dvb_frontend.h" ++ ++enum lg_chip_type { ++ LG2160 = 0, ++ LG2161 = 1, ++}; ++ ++#define LG2161_1019 LG2161 ++#define LG2161_1040 LG2161 ++ ++enum lg2160_spi_clock { ++ LG2160_SPI_3_125_MHZ = 0, ++ LG2160_SPI_6_25_MHZ = 1, ++ LG2160_SPI_12_5_MHZ = 2, ++}; ++ ++#if 0 ++enum lg2161_oif { ++ LG2161_OIF_EBI2_SLA = 1, ++ LG2161_OIF_SDIO_SLA = 2, ++ LG2161_OIF_SPI_SLA = 3, ++ LG2161_OIF_SPI_MAS = 4, ++ LG2161_OIF_SERIAL_TS = 7, ++}; ++#endif ++ ++struct lg2160_config { ++ u8 i2c_addr; ++ ++ /* user defined IF frequency in KHz */ ++ u16 if_khz; ++ ++ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ ++ int deny_i2c_rptr:1; ++ ++ /* spectral inversion - 0:disabled 1:enabled */ ++ int spectral_inversion:1; ++ ++ unsigned int output_if; ++ enum lg2160_spi_clock spi_clock; ++ enum lg_chip_type lg_chip; ++}; ++ ++#if defined(CONFIG_DVB_LG2160) || (defined(CONFIG_DVB_LG2160_MODULE) && \ ++ defined(MODULE)) ++extern ++struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, ++ struct i2c_adapter *i2c_adap); ++#else ++static inline ++struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LG2160 */ ++ ++#endif /* _LG2160_H_ */ +diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c +new file mode 100644 +index 0000000..1d2c473 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgdt3305.c +@@ -0,0 +1,1222 @@ ++/* ++ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM ++ * ++ * Copyright (C) 2008, 2009, 2010 Michael Krufky ++ * ++ * LGDT3304 support by Jarod Wilson ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include "dvb_math.h" ++#include "lgdt3305.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); ++ ++#define DBG_INFO 1 ++#define DBG_REG 2 ++ ++#define lg_printk(kern, fmt, arg...) \ ++ printk(kern "%s: " fmt, __func__, ##arg) ++ ++#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) ++#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) ++#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) ++#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ ++ lg_printk(KERN_DEBUG, fmt, ##arg) ++#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ ++ lg_printk(KERN_DEBUG, fmt, ##arg) ++ ++#define lg_fail(ret) \ ++({ \ ++ int __ret; \ ++ __ret = (ret < 0); \ ++ if (__ret) \ ++ lg_err("error %d on line %d\n", ret, __LINE__); \ ++ __ret; \ ++}) ++ ++struct lgdt3305_state { ++ struct i2c_adapter *i2c_adap; ++ const struct lgdt3305_config *cfg; ++ ++ struct dvb_frontend frontend; ++ ++ fe_modulation_t current_modulation; ++ u32 current_frequency; ++ u32 snr; ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* FIXME: verify & document the LGDT3304 registers */ ++ ++#define LGDT3305_GEN_CTRL_1 0x0000 ++#define LGDT3305_GEN_CTRL_2 0x0001 ++#define LGDT3305_GEN_CTRL_3 0x0002 ++#define LGDT3305_GEN_STATUS 0x0003 ++#define LGDT3305_GEN_CONTROL 0x0007 ++#define LGDT3305_GEN_CTRL_4 0x000a ++#define LGDT3305_DGTL_AGC_REF_1 0x0012 ++#define LGDT3305_DGTL_AGC_REF_2 0x0013 ++#define LGDT3305_CR_CTR_FREQ_1 0x0106 ++#define LGDT3305_CR_CTR_FREQ_2 0x0107 ++#define LGDT3305_CR_CTR_FREQ_3 0x0108 ++#define LGDT3305_CR_CTR_FREQ_4 0x0109 ++#define LGDT3305_CR_MSE_1 0x011b ++#define LGDT3305_CR_MSE_2 0x011c ++#define LGDT3305_CR_LOCK_STATUS 0x011d ++#define LGDT3305_CR_CTRL_7 0x0126 ++#define LGDT3305_AGC_POWER_REF_1 0x0300 ++#define LGDT3305_AGC_POWER_REF_2 0x0301 ++#define LGDT3305_AGC_DELAY_PT_1 0x0302 ++#define LGDT3305_AGC_DELAY_PT_2 0x0303 ++#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 ++#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 ++#define LGDT3305_IFBW_1 0x0308 ++#define LGDT3305_IFBW_2 0x0309 ++#define LGDT3305_AGC_CTRL_1 0x030c ++#define LGDT3305_AGC_CTRL_4 0x0314 ++#define LGDT3305_EQ_MSE_1 0x0413 ++#define LGDT3305_EQ_MSE_2 0x0414 ++#define LGDT3305_EQ_MSE_3 0x0415 ++#define LGDT3305_PT_MSE_1 0x0417 ++#define LGDT3305_PT_MSE_2 0x0418 ++#define LGDT3305_PT_MSE_3 0x0419 ++#define LGDT3305_FEC_BLOCK_CTRL 0x0504 ++#define LGDT3305_FEC_LOCK_STATUS 0x050a ++#define LGDT3305_FEC_PKT_ERR_1 0x050c ++#define LGDT3305_FEC_PKT_ERR_2 0x050d ++#define LGDT3305_TP_CTRL_1 0x050e ++#define LGDT3305_BERT_PERIOD 0x0801 ++#define LGDT3305_BERT_ERROR_COUNT_1 0x080a ++#define LGDT3305_BERT_ERROR_COUNT_2 0x080b ++#define LGDT3305_BERT_ERROR_COUNT_3 0x080c ++#define LGDT3305_BERT_ERROR_COUNT_4 0x080d ++ ++static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) ++{ ++ int ret; ++ u8 buf[] = { reg >> 8, reg & 0xff, val }; ++ struct i2c_msg msg = { ++ .addr = state->cfg->i2c_addr, .flags = 0, ++ .buf = buf, .len = 3, ++ }; ++ ++ lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); ++ ++ ret = i2c_transfer(state->i2c_adap, &msg, 1); ++ ++ if (ret != 1) { ++ lg_err("error (addr %02x %02x <- %02x, err = %i)\n", ++ msg.buf[0], msg.buf[1], msg.buf[2], ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) ++{ ++ int ret; ++ u8 reg_buf[] = { reg >> 8, reg & 0xff }; ++ struct i2c_msg msg[] = { ++ { .addr = state->cfg->i2c_addr, ++ .flags = 0, .buf = reg_buf, .len = 2 }, ++ { .addr = state->cfg->i2c_addr, ++ .flags = I2C_M_RD, .buf = val, .len = 1 }, ++ }; ++ ++ lg_reg("reg: 0x%04x\n", reg); ++ ++ ret = i2c_transfer(state->i2c_adap, msg, 2); ++ ++ if (ret != 2) { ++ lg_err("error (addr %02x reg %04x error (ret == %i)\n", ++ state->cfg->i2c_addr, reg, ret); ++ if (ret < 0) ++ return ret; ++ else ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++#define read_reg(state, reg) \ ++({ \ ++ u8 __val; \ ++ int ret = lgdt3305_read_reg(state, reg, &__val); \ ++ if (lg_fail(ret)) \ ++ __val = 0; \ ++ __val; \ ++}) ++ ++static int lgdt3305_set_reg_bit(struct lgdt3305_state *state, ++ u16 reg, int bit, int onoff) ++{ ++ u8 val; ++ int ret; ++ ++ lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); ++ ++ ret = lgdt3305_read_reg(state, reg, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= ~(1 << bit); ++ val |= (onoff & 1) << bit; ++ ++ ret = lgdt3305_write_reg(state, reg, val); ++fail: ++ return ret; ++} ++ ++struct lgdt3305_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++static int lgdt3305_write_regs(struct lgdt3305_state *state, ++ struct lgdt3305_reg *regs, int len) ++{ ++ int i, ret; ++ ++ lg_reg("writing %d registers...\n", len); ++ ++ for (i = 0; i < len - 1; i++) { ++ ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); ++ if (lg_fail(ret)) ++ return ret; ++ } ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_soft_reset(struct lgdt3305_state *state) ++{ ++ int ret; ++ ++ lg_dbg("\n"); ++ ++ ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ msleep(20); ++ ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); ++fail: ++ return ret; ++} ++ ++static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, ++ enum lgdt3305_mpeg_mode mode) ++{ ++ lg_dbg("(%d)\n", mode); ++ return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); ++} ++ ++static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state, ++ enum lgdt3305_tp_clock_edge edge, ++ enum lgdt3305_tp_valid_polarity valid) ++{ ++ u8 val; ++ int ret; ++ ++ lg_dbg("edge = %d, valid = %d\n", edge, valid); ++ ++ ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ val &= ~0x09; ++ ++ if (edge) ++ val |= 0x08; ++ if (valid) ++ val |= 0x01; ++ ++ ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_soft_reset(state); ++fail: ++ return ret; ++} ++ ++static int lgdt3305_set_modulation(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u8 opermode; ++ int ret; ++ ++ lg_dbg("\n"); ++ ++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ opermode &= ~0x03; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ opermode |= 0x03; ++ break; ++ case QAM_64: ++ opermode |= 0x00; ++ break; ++ case QAM_256: ++ opermode |= 0x01; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); ++fail: ++ return ret; ++} ++ ++static int lgdt3305_set_filter_extension(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ int val; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ val = 0; ++ break; ++ case QAM_64: ++ case QAM_256: ++ val = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ lg_dbg("val = %d\n", val); ++ ++ return lgdt3305_set_reg_bit(state, 0x043f, 2, val); ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u16 agc_ref; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ agc_ref = 0x32c4; ++ break; ++ case QAM_64: ++ agc_ref = 0x2a00; ++ break; ++ case QAM_256: ++ agc_ref = 0x2a80; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ lg_dbg("agc ref: 0x%04x\n", agc_ref); ++ ++ lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); ++ lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); ++ ++ return 0; ++} ++ ++static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u16 ifbw, rfbw, agcdelay; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ agcdelay = 0x04c0; ++ rfbw = 0x8000; ++ ifbw = 0x8000; ++ break; ++ case QAM_64: ++ case QAM_256: ++ agcdelay = 0x046b; ++ rfbw = 0x8889; ++ /* FIXME: investigate optimal ifbw & rfbw values for the ++ * DT3304 and re-write this switch..case block */ ++ if (state->cfg->demod_chip == LGDT3304) ++ ifbw = 0x6666; ++ else /* (state->cfg->demod_chip == LGDT3305) */ ++ ifbw = 0x8888; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (state->cfg->rf_agc_loop) { ++ lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); ++ ++ /* rf agc loop filter bandwidth */ ++ lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, ++ agcdelay >> 8); ++ lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, ++ agcdelay & 0xff); ++ ++ lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, ++ rfbw >> 8); ++ lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, ++ rfbw & 0xff); ++ } else { ++ lg_dbg("ifbw: 0x%04x\n", ifbw); ++ ++ /* if agc loop filter bandwidth */ ++ lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); ++ lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); ++ } ++ ++ return 0; ++} ++ ++static int lgdt3305_agc_setup(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ int lockdten, acqen; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ lockdten = 0; ++ acqen = 0; ++ break; ++ case QAM_64: ++ case QAM_256: ++ lockdten = 1; ++ acqen = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); ++ ++ /* control agc function */ ++ switch (state->cfg->demod_chip) { ++ case LGDT3304: ++ lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); ++ lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); ++ break; ++ case LGDT3305: ++ lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); ++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return lgdt3305_rfagc_loop(state, p); ++} ++ ++static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u16 usref = 0; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ if (state->cfg->usref_8vsb) ++ usref = state->cfg->usref_8vsb; ++ break; ++ case QAM_64: ++ if (state->cfg->usref_qam64) ++ usref = state->cfg->usref_qam64; ++ break; ++ case QAM_256: ++ if (state->cfg->usref_qam256) ++ usref = state->cfg->usref_qam256; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (usref) { ++ lg_dbg("set manual mode: 0x%04x\n", usref); ++ ++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); ++ ++ lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, ++ 0xff & (usref >> 8)); ++ lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, ++ 0xff & (usref >> 0)); ++ } ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_spectral_inversion(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p, ++ int inversion) ++{ ++ int ret; ++ ++ lg_dbg("(%d)\n", inversion); ++ ++ switch (p->modulation) { ++ case VSB_8: ++ ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, ++ inversion ? 0xf9 : 0x79); ++ break; ++ case QAM_64: ++ case QAM_256: ++ ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, ++ inversion ? 0xfd : 0xff); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++static int lgdt3305_set_if(struct lgdt3305_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u16 if_freq_khz; ++ u8 nco1, nco2, nco3, nco4; ++ u64 nco; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ if_freq_khz = state->cfg->vsb_if_khz; ++ break; ++ case QAM_64: ++ case QAM_256: ++ if_freq_khz = state->cfg->qam_if_khz; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ nco = if_freq_khz / 10; ++ ++ switch (p->modulation) { ++ case VSB_8: ++ nco <<= 24; ++ do_div(nco, 625); ++ break; ++ case QAM_64: ++ case QAM_256: ++ nco <<= 28; ++ do_div(nco, 625); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ nco1 = (nco >> 24) & 0x3f; ++ nco1 |= 0x40; ++ nco2 = (nco >> 16) & 0xff; ++ nco3 = (nco >> 8) & 0xff; ++ nco4 = nco & 0xff; ++ ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); ++ ++ lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", ++ if_freq_khz, nco1, nco2, nco3, nco4); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ ++ if (state->cfg->deny_i2c_rptr) ++ return 0; ++ ++ lg_dbg("(%d)\n", enable); ++ ++ return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, ++ enable ? 0 : 1); ++} ++ ++static int lgdt3305_sleep(struct dvb_frontend *fe) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ u8 gen_ctrl_3, gen_ctrl_4; ++ ++ lg_dbg("\n"); ++ ++ gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); ++ gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); ++ ++ /* hold in software reset while sleeping */ ++ gen_ctrl_3 &= ~0x01; ++ /* tristate the IF-AGC pin */ ++ gen_ctrl_3 |= 0x02; ++ /* tristate the RF-AGC pin */ ++ gen_ctrl_3 |= 0x04; ++ ++ /* disable vsb/qam module */ ++ gen_ctrl_4 &= ~0x01; ++ /* disable adc module */ ++ gen_ctrl_4 &= ~0x02; ++ ++ lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); ++ lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); ++ ++ return 0; ++} ++ ++static int lgdt3305_init(struct dvb_frontend *fe) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ int ret; ++ ++ static struct lgdt3305_reg lgdt3304_init_data[] = { ++ { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, ++ { .reg = 0x000d, .val = 0x02, }, ++ { .reg = 0x000e, .val = 0x02, }, ++ { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, ++ { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, ++ { .reg = 0x0112, .val = 0x17, }, ++ { .reg = 0x0113, .val = 0x15, }, ++ { .reg = 0x0114, .val = 0x18, }, ++ { .reg = 0x0115, .val = 0xff, }, ++ { .reg = 0x0116, .val = 0x3c, }, ++ { .reg = 0x0214, .val = 0x67, }, ++ { .reg = 0x0424, .val = 0x8d, }, ++ { .reg = 0x0427, .val = 0x12, }, ++ { .reg = 0x0428, .val = 0x4f, }, ++ { .reg = LGDT3305_IFBW_1, .val = 0x80, }, ++ { .reg = LGDT3305_IFBW_2, .val = 0x00, }, ++ { .reg = 0x030a, .val = 0x08, }, ++ { .reg = 0x030b, .val = 0x9b, }, ++ { .reg = 0x030d, .val = 0x00, }, ++ { .reg = 0x030e, .val = 0x1c, }, ++ { .reg = 0x0314, .val = 0xe1, }, ++ { .reg = 0x000d, .val = 0x82, }, ++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, ++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, ++ }; ++ ++ static struct lgdt3305_reg lgdt3305_init_data[] = { ++ { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, ++ { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, ++ { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, ++ { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, ++ { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, ++ { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, ++ { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, ++ { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, ++ { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, ++ { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, ++ { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, ++ { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, ++ { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, ++ { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, ++ { .reg = LGDT3305_IFBW_1, .val = 0x80, }, ++ { .reg = LGDT3305_IFBW_2, .val = 0x00, }, ++ { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, ++ { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, ++ { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, ++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, ++ }; ++ ++ lg_dbg("\n"); ++ ++ switch (state->cfg->demod_chip) { ++ case LGDT3304: ++ ret = lgdt3305_write_regs(state, lgdt3304_init_data, ++ ARRAY_SIZE(lgdt3304_init_data)); ++ break; ++ case LGDT3305: ++ ret = lgdt3305_write_regs(state, lgdt3305_init_data, ++ ARRAY_SIZE(lgdt3305_init_data)); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_soft_reset(state); ++fail: ++ return ret; ++} ++ ++static int lgdt3304_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ int ret; ++ ++ lg_dbg("(%d, %d)\n", p->frequency, p->modulation); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ state->current_frequency = p->frequency; ++ } ++ ++ ret = lgdt3305_set_modulation(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_passband_digital_agc(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_agc_setup(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ ++ switch (p->modulation) { ++ case VSB_8: ++ lgdt3305_write_reg(state, 0x030d, 0x00); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); ++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); ++ break; ++ case QAM_64: ++ case QAM_256: ++ lgdt3305_write_reg(state, 0x030d, 0x14); ++ ret = lgdt3305_set_if(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ++ ret = lgdt3305_spectral_inversion(state, p, ++ state->cfg->spectral_inversion ++ ? 1 : 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ state->current_modulation = p->modulation; ++ ++ ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ ++ ret = lgdt3305_mpeg_mode_polarity(state, ++ state->cfg->tpclk_edge, ++ state->cfg->tpvalid_polarity); ++fail: ++ return ret; ++} ++ ++static int lgdt3305_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ int ret; ++ ++ lg_dbg("(%d, %d)\n", p->frequency, p->modulation); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ ret = fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ if (lg_fail(ret)) ++ goto fail; ++ state->current_frequency = p->frequency; ++ } ++ ++ ret = lgdt3305_set_modulation(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_passband_digital_agc(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lgdt3305_set_agc_power_ref(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lgdt3305_agc_setup(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ /* low if */ ++ ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_set_if(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lgdt3305_spectral_inversion(state, p, ++ state->cfg->spectral_inversion ++ ? 1 : 0); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ ret = lgdt3305_set_filter_extension(state, p); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ state->current_modulation = p->modulation; ++ ++ ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ ++ ret = lgdt3305_mpeg_mode_polarity(state, ++ state->cfg->tpclk_edge, ++ state->cfg->tpvalid_polarity); ++fail: ++ return ret; ++} ++ ++static int lgdt3305_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ ++ lg_dbg("\n"); ++ ++ p->modulation = state->current_modulation; ++ p->frequency = state->current_frequency; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, ++ int *locked) ++{ ++ u8 val; ++ int ret; ++ char *cr_lock_state = ""; ++ ++ *locked = 0; ++ ++ ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ if (val & (1 << 1)) ++ *locked = 1; ++ ++ switch (val & 0x07) { ++ case 0: ++ cr_lock_state = "QAM UNLOCK"; ++ break; ++ case 4: ++ cr_lock_state = "QAM 1stLock"; ++ break; ++ case 6: ++ cr_lock_state = "QAM 2ndLock"; ++ break; ++ case 7: ++ cr_lock_state = "QAM FinalLock"; ++ break; ++ default: ++ cr_lock_state = "CLOCKQAM-INVALID!"; ++ break; ++ } ++ break; ++ case VSB_8: ++ if (val & (1 << 7)) { ++ *locked = 1; ++ cr_lock_state = "CLOCKVSB"; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ lg_dbg("(%d) %s\n", *locked, cr_lock_state); ++fail: ++ return ret; ++} ++ ++static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, ++ int *locked) ++{ ++ u8 val; ++ int ret, mpeg_lock, fec_lock, viterbi_lock; ++ ++ *locked = 0; ++ ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ ret = lgdt3305_read_reg(state, ++ LGDT3305_FEC_LOCK_STATUS, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ mpeg_lock = (val & (1 << 0)) ? 1 : 0; ++ fec_lock = (val & (1 << 2)) ? 1 : 0; ++ viterbi_lock = (val & (1 << 3)) ? 1 : 0; ++ ++ *locked = mpeg_lock && fec_lock && viterbi_lock; ++ ++ lg_dbg("(%d) %s%s%s\n", *locked, ++ mpeg_lock ? "mpeg lock " : "", ++ fec_lock ? "fec lock " : "", ++ viterbi_lock ? "viterbi lock" : ""); ++ break; ++ case VSB_8: ++ default: ++ ret = -EINVAL; ++ } ++fail: ++ return ret; ++} ++ ++static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ u8 val; ++ int ret, signal, inlock, nofecerr, snrgood, ++ cr_lock, fec_lock, sync_lock; ++ ++ *status = 0; ++ ++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ signal = (val & (1 << 4)) ? 1 : 0; ++ inlock = (val & (1 << 3)) ? 0 : 1; ++ sync_lock = (val & (1 << 2)) ? 1 : 0; ++ nofecerr = (val & (1 << 1)) ? 1 : 0; ++ snrgood = (val & (1 << 0)) ? 1 : 0; ++ ++ lg_dbg("%s%s%s%s%s\n", ++ signal ? "SIGNALEXIST " : "", ++ inlock ? "INLOCK " : "", ++ sync_lock ? "SYNCLOCK " : "", ++ nofecerr ? "NOFECERR " : "", ++ snrgood ? "SNRGOOD " : ""); ++ ++ ret = lgdt3305_read_cr_lock_status(state, &cr_lock); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ if (signal) ++ *status |= FE_HAS_SIGNAL; ++ if (cr_lock) ++ *status |= FE_HAS_CARRIER; ++ if (nofecerr) ++ *status |= FE_HAS_VITERBI; ++ if (sync_lock) ++ *status |= FE_HAS_SYNC; ++ ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ /* signal bit is unreliable on the DT3304 in QAM mode */ ++ if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) ++ *status |= FE_HAS_SIGNAL; ++ ++ ret = lgdt3305_read_fec_lock_status(state, &fec_lock); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ if (fec_lock) ++ *status |= FE_HAS_LOCK; ++ break; ++ case VSB_8: ++ if (inlock) ++ *status |= FE_HAS_LOCK; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* borrowed from lgdt330x.c */ ++static u32 calculate_snr(u32 mse, u32 c) ++{ ++ if (mse == 0) /* no signal */ ++ return 0; ++ ++ mse = intlog10(mse); ++ if (mse > c) { ++ /* Negative SNR, which is possible, but realisticly the ++ demod will lose lock before the signal gets this bad. The ++ API only allows for unsigned values, so just return 0 */ ++ return 0; ++ } ++ return 10*(c - mse); ++} ++ ++static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ u32 noise; /* noise value */ ++ u32 c; /* per-modulation SNR calculation constant */ ++ ++ switch (state->current_modulation) { ++ case VSB_8: ++#ifdef USE_PTMSE ++ /* Use Phase Tracker Mean-Square Error Register */ ++ /* SNR for ranges from -13.11 to +44.08 */ ++ noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | ++ (read_reg(state, LGDT3305_PT_MSE_2) << 8) | ++ (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); ++ c = 73957994; /* log10(25*32^2)*2^24 */ ++#else ++ /* Use Equalizer Mean-Square Error Register */ ++ /* SNR for ranges from -16.12 to +44.08 */ ++ noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | ++ (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | ++ (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); ++ c = 73957994; /* log10(25*32^2)*2^24 */ ++#endif ++ break; ++ case QAM_64: ++ case QAM_256: ++ noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | ++ (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); ++ ++ c = (state->current_modulation == QAM_64) ? ++ 97939837 : 98026066; ++ /* log10(688128)*2^24 and log10(696320)*2^24 */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ state->snr = calculate_snr(noise, c); ++ /* report SNR in dB * 10 */ ++ *snr = (state->snr / ((1 << 24) / 10)); ++ lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, ++ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); ++ ++ return 0; ++} ++ ++static int lgdt3305_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ /* borrowed from lgdt330x.c ++ * ++ * Calculate strength from SNR up to 35dB ++ * Even though the SNR can go higher than 35dB, ++ * there is some comfort factor in having a range of ++ * strong signals that can show at 100% ++ */ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ u16 snr; ++ int ret; ++ ++ *strength = 0; ++ ++ ret = fe->ops.read_snr(fe, &snr); ++ if (lg_fail(ret)) ++ goto fail; ++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ ++ /* scale the range 0 - 35*2^24 into 0 - 65535 */ ++ if (state->snr >= 8960 * 0x10000) ++ *strength = 0xffff; ++ else ++ *strength = state->snr / 8960; ++fail: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ *ber = 0; ++ return 0; ++} ++ ++static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ ++ *ucblocks = ++ (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | ++ (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); ++ ++ return 0; ++} ++ ++static int lgdt3305_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings ++ *fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 500; ++ lg_dbg("\n"); ++ return 0; ++} ++ ++static void lgdt3305_release(struct dvb_frontend *fe) ++{ ++ struct lgdt3305_state *state = fe->demodulator_priv; ++ lg_dbg("\n"); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops lgdt3304_ops; ++static struct dvb_frontend_ops lgdt3305_ops; ++ ++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ struct lgdt3305_state *state = NULL; ++ int ret; ++ u8 val; ++ ++ lg_dbg("(%d-%04x)\n", ++ i2c_adap ? i2c_adapter_id(i2c_adap) : 0, ++ config ? config->i2c_addr : 0); ++ ++ state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); ++ if (state == NULL) ++ goto fail; ++ ++ state->cfg = config; ++ state->i2c_adap = i2c_adap; ++ ++ switch (config->demod_chip) { ++ case LGDT3304: ++ memcpy(&state->frontend.ops, &lgdt3304_ops, ++ sizeof(struct dvb_frontend_ops)); ++ break; ++ case LGDT3305: ++ memcpy(&state->frontend.ops, &lgdt3305_ops, ++ sizeof(struct dvb_frontend_ops)); ++ break; ++ default: ++ goto fail; ++ } ++ state->frontend.demodulator_priv = state; ++ ++ /* verify that we're talking to a lg dt3304/5 */ ++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); ++ if ((lg_fail(ret)) | (val == 0)) ++ goto fail; ++ ret = lgdt3305_write_reg(state, 0x0808, 0x80); ++ if (lg_fail(ret)) ++ goto fail; ++ ret = lgdt3305_read_reg(state, 0x0808, &val); ++ if ((lg_fail(ret)) | (val != 0x80)) ++ goto fail; ++ ret = lgdt3305_write_reg(state, 0x0808, 0x00); ++ if (lg_fail(ret)) ++ goto fail; ++ ++ state->current_frequency = -1; ++ state->current_modulation = -1; ++ ++ return &state->frontend; ++fail: ++ lg_warn("unable to detect %s hardware\n", ++ config->demod_chip ? "LGDT3304" : "LGDT3305"); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(lgdt3305_attach); ++ ++static struct dvb_frontend_ops lgdt3304_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "LG Electronics LGDT3304 VSB/QAM Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, ++ .init = lgdt3305_init, ++ .set_frontend = lgdt3304_set_parameters, ++ .get_frontend = lgdt3305_get_frontend, ++ .get_tune_settings = lgdt3305_get_tune_settings, ++ .read_status = lgdt3305_read_status, ++ .read_ber = lgdt3305_read_ber, ++ .read_signal_strength = lgdt3305_read_signal_strength, ++ .read_snr = lgdt3305_read_snr, ++ .read_ucblocks = lgdt3305_read_ucblocks, ++ .release = lgdt3305_release, ++}; ++ ++static struct dvb_frontend_ops lgdt3305_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "LG Electronics LGDT3305 VSB/QAM Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, ++ .init = lgdt3305_init, ++ .sleep = lgdt3305_sleep, ++ .set_frontend = lgdt3305_set_parameters, ++ .get_frontend = lgdt3305_get_frontend, ++ .get_tune_settings = lgdt3305_get_tune_settings, ++ .read_status = lgdt3305_read_status, ++ .read_ber = lgdt3305_read_ber, ++ .read_signal_strength = lgdt3305_read_signal_strength, ++ .read_snr = lgdt3305_read_snr, ++ .read_ucblocks = lgdt3305_read_ucblocks, ++ .release = lgdt3305_release, ++}; ++ ++MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); ++MODULE_AUTHOR("Michael Krufky "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.2"); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h +new file mode 100644 +index 0000000..02172ec +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgdt3305.h +@@ -0,0 +1,91 @@ ++/* ++ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM ++ * ++ * Copyright (C) 2008, 2009, 2010 Michael Krufky ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _LGDT3305_H_ ++#define _LGDT3305_H_ ++ ++#include ++#include "dvb_frontend.h" ++ ++ ++enum lgdt3305_mpeg_mode { ++ LGDT3305_MPEG_PARALLEL = 0, ++ LGDT3305_MPEG_SERIAL = 1, ++}; ++ ++enum lgdt3305_tp_clock_edge { ++ LGDT3305_TPCLK_RISING_EDGE = 0, ++ LGDT3305_TPCLK_FALLING_EDGE = 1, ++}; ++ ++enum lgdt3305_tp_valid_polarity { ++ LGDT3305_TP_VALID_LOW = 0, ++ LGDT3305_TP_VALID_HIGH = 1, ++}; ++ ++enum lgdt_demod_chip_type { ++ LGDT3305 = 0, ++ LGDT3304 = 1, ++}; ++ ++struct lgdt3305_config { ++ u8 i2c_addr; ++ ++ /* user defined IF frequency in KHz */ ++ u16 qam_if_khz; ++ u16 vsb_if_khz; ++ ++ /* AGC Power reference - defaults are used if left unset */ ++ u16 usref_8vsb; /* default: 0x32c4 */ ++ u16 usref_qam64; /* default: 0x5400 */ ++ u16 usref_qam256; /* default: 0x2a80 */ ++ ++ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ ++ unsigned int deny_i2c_rptr:1; ++ ++ /* spectral inversion - 0:disabled 1:enabled */ ++ unsigned int spectral_inversion:1; ++ ++ /* use RF AGC loop - 0:disabled 1:enabled */ ++ unsigned int rf_agc_loop:1; ++ ++ enum lgdt3305_mpeg_mode mpeg_mode; ++ enum lgdt3305_tp_clock_edge tpclk_edge; ++ enum lgdt3305_tp_valid_polarity tpvalid_polarity; ++ enum lgdt_demod_chip_type demod_chip; ++}; ++ ++#if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ ++ defined(MODULE)) ++extern ++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, ++ struct i2c_adapter *i2c_adap); ++#else ++static inline ++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LGDT3305 */ ++ ++#endif /* _LGDT3305_H_ */ +diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c +new file mode 100644 +index 0000000..e046622 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgdt330x.c +@@ -0,0 +1,831 @@ ++/* ++ * Support for LGDT3302 and LGDT3303 - VSB/QAM ++ * ++ * Copyright (C) 2005 Wilson Michaels ++ * ++ * 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. ++ * ++ */ ++ ++/* ++ * NOTES ABOUT THIS DRIVER ++ * ++ * This Linux driver supports: ++ * DViCO FusionHDTV 3 Gold-Q ++ * DViCO FusionHDTV 3 Gold-T ++ * DViCO FusionHDTV 5 Gold ++ * DViCO FusionHDTV 5 Lite ++ * DViCO FusionHDTV 5 USB Gold ++ * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) ++ * pcHDTV HD5500 ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "lgdt330x_priv.h" ++#include "lgdt330x.h" ++ ++/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ ++/* #define USE_EQMSE */ ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off)."); ++#define dprintk(args...) \ ++do { \ ++if (debug) printk(KERN_DEBUG "lgdt330x: " args); \ ++} while (0) ++ ++struct lgdt330x_state ++{ ++ struct i2c_adapter* i2c; ++ ++ /* Configuration settings */ ++ const struct lgdt330x_config* config; ++ ++ struct dvb_frontend frontend; ++ ++ /* Demodulator private data */ ++ fe_modulation_t current_modulation; ++ u32 snr; /* Result of last SNR calculation */ ++ ++ /* Tuner private data */ ++ u32 current_frequency; ++}; ++ ++static int i2c_write_demod_bytes (struct lgdt330x_state* state, ++ u8 *buf, /* data bytes to send */ ++ int len /* number of bytes to send */ ) ++{ ++ struct i2c_msg msg = ++ { .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 }; ++ int i; ++ int err; ++ ++ for (i=0; ii2c, &msg, 1)) != 1) { ++ printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __func__, msg.buf[0], msg.buf[1], err); ++ if (err < 0) ++ return err; ++ else ++ return -EREMOTEIO; ++ } ++ msg.buf += 2; ++ } ++ return 0; ++} ++ ++/* ++ * This routine writes the register (reg) to the demod bus ++ * then reads the data returned for (len) bytes. ++ */ ++ ++static int i2c_read_demod_bytes(struct lgdt330x_state *state, ++ enum I2C_REG reg, u8 *buf, int len) ++{ ++ u8 wr [] = { reg }; ++ struct i2c_msg msg [] = { ++ { .addr = state->config->demod_address, ++ .flags = 0, .buf = wr, .len = 1 }, ++ { .addr = state->config->demod_address, ++ .flags = I2C_M_RD, .buf = buf, .len = len }, ++ }; ++ int ret; ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) { ++ printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret); ++ if (ret >= 0) ++ ret = -EIO; ++ } else { ++ ret = 0; ++ } ++ return ret; ++} ++ ++/* Software reset */ ++static int lgdt3302_SwReset(struct lgdt330x_state* state) ++{ ++ u8 ret; ++ u8 reset[] = { ++ IRQ_MASK, ++ 0x00 /* bit 6 is active low software reset ++ * bits 5-0 are 1 to mask interrupts */ ++ }; ++ ++ ret = i2c_write_demod_bytes(state, ++ reset, sizeof(reset)); ++ if (ret == 0) { ++ ++ /* force reset high (inactive) and unmask interrupts */ ++ reset[1] = 0x7f; ++ ret = i2c_write_demod_bytes(state, ++ reset, sizeof(reset)); ++ } ++ return ret; ++} ++ ++static int lgdt3303_SwReset(struct lgdt330x_state* state) ++{ ++ u8 ret; ++ u8 reset[] = { ++ 0x02, ++ 0x00 /* bit 0 is active low software reset */ ++ }; ++ ++ ret = i2c_write_demod_bytes(state, ++ reset, sizeof(reset)); ++ if (ret == 0) { ++ ++ /* force reset high (inactive) */ ++ reset[1] = 0x01; ++ ret = i2c_write_demod_bytes(state, ++ reset, sizeof(reset)); ++ } ++ return ret; ++} ++ ++static int lgdt330x_SwReset(struct lgdt330x_state* state) ++{ ++ switch (state->config->demod_chip) { ++ case LGDT3302: ++ return lgdt3302_SwReset(state); ++ case LGDT3303: ++ return lgdt3303_SwReset(state); ++ default: ++ return -ENODEV; ++ } ++} ++ ++static int lgdt330x_init(struct dvb_frontend* fe) ++{ ++ /* Hardware reset is done using gpio[0] of cx23880x chip. ++ * I'd like to do it here, but don't know how to find chip address. ++ * cx88-cards.c arranges for the reset bit to be inactive (high). ++ * Maybe there needs to be a callable function in cx88-core or ++ * the caller of this function needs to do it. */ ++ ++ /* ++ * Array of byte pairs ++ * to initialize each different chip ++ */ ++ static u8 lgdt3302_init_data[] = { ++ /* Use 50MHz parameter values from spec sheet since xtal is 50 */ ++ /* Change the value of NCOCTFV[25:0] of carrier ++ recovery center frequency register */ ++ VSB_CARRIER_FREQ0, 0x00, ++ VSB_CARRIER_FREQ1, 0x87, ++ VSB_CARRIER_FREQ2, 0x8e, ++ VSB_CARRIER_FREQ3, 0x01, ++ /* Change the TPCLK pin polarity ++ data is valid on falling clock */ ++ DEMUX_CONTROL, 0xfb, ++ /* Change the value of IFBW[11:0] of ++ AGC IF/RF loop filter bandwidth register */ ++ AGC_RF_BANDWIDTH0, 0x40, ++ AGC_RF_BANDWIDTH1, 0x93, ++ AGC_RF_BANDWIDTH2, 0x00, ++ /* Change the value of bit 6, 'nINAGCBY' and ++ 'NSSEL[1:0] of ACG function control register 2 */ ++ AGC_FUNC_CTRL2, 0xc6, ++ /* Change the value of bit 6 'RFFIX' ++ of AGC function control register 3 */ ++ AGC_FUNC_CTRL3, 0x40, ++ /* Set the value of 'INLVTHD' register 0x2a/0x2c ++ to 0x7fe */ ++ AGC_DELAY0, 0x07, ++ AGC_DELAY2, 0xfe, ++ /* Change the value of IAGCBW[15:8] ++ of inner AGC loop filter bandwidth */ ++ AGC_LOOP_BANDWIDTH0, 0x08, ++ AGC_LOOP_BANDWIDTH1, 0x9a ++ }; ++ ++ static u8 lgdt3303_init_data[] = { ++ 0x4c, 0x14 ++ }; ++ ++ static u8 flip_1_lgdt3303_init_data[] = { ++ 0x4c, 0x14, ++ 0x87, 0xf3 ++ }; ++ ++ static u8 flip_2_lgdt3303_init_data[] = { ++ 0x4c, 0x14, ++ 0x87, 0xda ++ }; ++ ++ struct lgdt330x_state* state = fe->demodulator_priv; ++ char *chip_name; ++ int err; ++ ++ switch (state->config->demod_chip) { ++ case LGDT3302: ++ chip_name = "LGDT3302"; ++ err = i2c_write_demod_bytes(state, lgdt3302_init_data, ++ sizeof(lgdt3302_init_data)); ++ break; ++ case LGDT3303: ++ chip_name = "LGDT3303"; ++ switch (state->config->clock_polarity_flip) { ++ case 2: ++ err = i2c_write_demod_bytes(state, ++ flip_2_lgdt3303_init_data, ++ sizeof(flip_2_lgdt3303_init_data)); ++ break; ++ case 1: ++ err = i2c_write_demod_bytes(state, ++ flip_1_lgdt3303_init_data, ++ sizeof(flip_1_lgdt3303_init_data)); ++ break; ++ case 0: ++ default: ++ err = i2c_write_demod_bytes(state, lgdt3303_init_data, ++ sizeof(lgdt3303_init_data)); ++ } ++ break; ++ default: ++ chip_name = "undefined"; ++ printk (KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n"); ++ err = -ENODEV; ++ } ++ dprintk("%s entered as %s\n", __func__, chip_name); ++ if (err < 0) ++ return err; ++ return lgdt330x_SwReset(state); ++} ++ ++static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ *ber = 0; /* Not supplied by the demod chips */ ++ return 0; ++} ++ ++static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct lgdt330x_state* state = fe->demodulator_priv; ++ int err; ++ u8 buf[2]; ++ ++ *ucblocks = 0; ++ ++ switch (state->config->demod_chip) { ++ case LGDT3302: ++ err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, ++ buf, sizeof(buf)); ++ break; ++ case LGDT3303: ++ err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, ++ buf, sizeof(buf)); ++ break; ++ default: ++ printk(KERN_WARNING ++ "Only LGDT3302 and LGDT3303 are supported chips.\n"); ++ err = -ENODEV; ++ } ++ if (err < 0) ++ return err; ++ ++ *ucblocks = (buf[0] << 8) | buf[1]; ++ return 0; ++} ++ ++static int lgdt330x_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ /* ++ * Array of byte pairs ++ * to initialize 8VSB for lgdt3303 chip 50 MHz IF ++ */ ++ static u8 lgdt3303_8vsb_44_data[] = { ++ 0x04, 0x00, ++ 0x0d, 0x40, ++ 0x0e, 0x87, ++ 0x0f, 0x8e, ++ 0x10, 0x01, ++ 0x47, 0x8b }; ++ ++ /* ++ * Array of byte pairs ++ * to initialize QAM for lgdt3303 chip ++ */ ++ static u8 lgdt3303_qam_data[] = { ++ 0x04, 0x00, ++ 0x0d, 0x00, ++ 0x0e, 0x00, ++ 0x0f, 0x00, ++ 0x10, 0x00, ++ 0x51, 0x63, ++ 0x47, 0x66, ++ 0x48, 0x66, ++ 0x4d, 0x1a, ++ 0x49, 0x08, ++ 0x4a, 0x9b }; ++ ++ struct lgdt330x_state* state = fe->demodulator_priv; ++ ++ static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; ++ ++ int err = 0; ++ /* Change only if we are actually changing the modulation */ ++ if (state->current_modulation != p->modulation) { ++ switch (p->modulation) { ++ case VSB_8: ++ dprintk("%s: VSB_8 MODE\n", __func__); ++ ++ /* Select VSB mode */ ++ top_ctrl_cfg[1] = 0x03; ++ ++ /* Select ANT connector if supported by card */ ++ if (state->config->pll_rf_set) ++ state->config->pll_rf_set(fe, 1); ++ ++ if (state->config->demod_chip == LGDT3303) { ++ err = i2c_write_demod_bytes(state, lgdt3303_8vsb_44_data, ++ sizeof(lgdt3303_8vsb_44_data)); ++ } ++ break; ++ ++ case QAM_64: ++ dprintk("%s: QAM_64 MODE\n", __func__); ++ ++ /* Select QAM_64 mode */ ++ top_ctrl_cfg[1] = 0x00; ++ ++ /* Select CABLE connector if supported by card */ ++ if (state->config->pll_rf_set) ++ state->config->pll_rf_set(fe, 0); ++ ++ if (state->config->demod_chip == LGDT3303) { ++ err = i2c_write_demod_bytes(state, lgdt3303_qam_data, ++ sizeof(lgdt3303_qam_data)); ++ } ++ break; ++ ++ case QAM_256: ++ dprintk("%s: QAM_256 MODE\n", __func__); ++ ++ /* Select QAM_256 mode */ ++ top_ctrl_cfg[1] = 0x01; ++ ++ /* Select CABLE connector if supported by card */ ++ if (state->config->pll_rf_set) ++ state->config->pll_rf_set(fe, 0); ++ ++ if (state->config->demod_chip == LGDT3303) { ++ err = i2c_write_demod_bytes(state, lgdt3303_qam_data, ++ sizeof(lgdt3303_qam_data)); ++ } ++ break; ++ default: ++ printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __func__, p->modulation); ++ return -1; ++ } ++ if (err < 0) ++ printk(KERN_WARNING "lgdt330x: %s: error blasting " ++ "bytes to lgdt3303 for modulation type(%d)\n", ++ __func__, p->modulation); ++ ++ /* ++ * select serial or parallel MPEG harware interface ++ * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303 ++ * Parallel: 0x00 ++ */ ++ top_ctrl_cfg[1] |= state->config->serial_mpeg; ++ ++ /* Select the requested mode */ ++ i2c_write_demod_bytes(state, top_ctrl_cfg, ++ sizeof(top_ctrl_cfg)); ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ state->current_modulation = p->modulation; ++ } ++ ++ /* Tune to the specified frequency */ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Keep track of the new frequency */ ++ /* FIXME this is the wrong way to do this... */ ++ /* The tuner is shared with the video4linux analog API */ ++ state->current_frequency = p->frequency; ++ ++ lgdt330x_SwReset(state); ++ return 0; ++} ++ ++static int lgdt330x_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgdt330x_state *state = fe->demodulator_priv; ++ p->frequency = state->current_frequency; ++ return 0; ++} ++ ++static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct lgdt330x_state* state = fe->demodulator_priv; ++ u8 buf[3]; ++ ++ *status = 0; /* Reset status result */ ++ ++ /* AGC status register */ ++ i2c_read_demod_bytes(state, AGC_STATUS, buf, 1); ++ dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); ++ if ((buf[0] & 0x0c) == 0x8){ ++ /* Test signal does not exist flag */ ++ /* as well as the AGC lock flag. */ ++ *status |= FE_HAS_SIGNAL; ++ } ++ ++ /* ++ * You must set the Mask bits to 1 in the IRQ_MASK in order ++ * to see that status bit in the IRQ_STATUS register. ++ * This is done in SwReset(); ++ */ ++ /* signal status */ ++ i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf)); ++ dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __func__, buf[0], buf[1], buf[2]); ++ ++ ++ /* sync status */ ++ if ((buf[2] & 0x03) == 0x01) { ++ *status |= FE_HAS_SYNC; ++ } ++ ++ /* FEC error status */ ++ if ((buf[2] & 0x0c) == 0x08) { ++ *status |= FE_HAS_LOCK; ++ *status |= FE_HAS_VITERBI; ++ } ++ ++ /* Carrier Recovery Lock Status Register */ ++ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); ++ dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ /* Need to understand why there are 3 lock levels here */ ++ if ((buf[0] & 0x07) == 0x07) ++ *status |= FE_HAS_CARRIER; ++ break; ++ case VSB_8: ++ if ((buf[0] & 0x80) == 0x80) ++ *status |= FE_HAS_CARRIER; ++ break; ++ default: ++ printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct lgdt330x_state* state = fe->demodulator_priv; ++ int err; ++ u8 buf[3]; ++ ++ *status = 0; /* Reset status result */ ++ ++ /* lgdt3303 AGC status register */ ++ err = i2c_read_demod_bytes(state, 0x58, buf, 1); ++ if (err < 0) ++ return err; ++ ++ dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); ++ if ((buf[0] & 0x21) == 0x01){ ++ /* Test input signal does not exist flag */ ++ /* as well as the AGC lock flag. */ ++ *status |= FE_HAS_SIGNAL; ++ } ++ ++ /* Carrier Recovery Lock Status Register */ ++ i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); ++ dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); ++ switch (state->current_modulation) { ++ case QAM_256: ++ case QAM_64: ++ /* Need to understand why there are 3 lock levels here */ ++ if ((buf[0] & 0x07) == 0x07) ++ *status |= FE_HAS_CARRIER; ++ else ++ break; ++ i2c_read_demod_bytes(state, 0x8a, buf, 1); ++ if ((buf[0] & 0x04) == 0x04) ++ *status |= FE_HAS_SYNC; ++ if ((buf[0] & 0x01) == 0x01) ++ *status |= FE_HAS_LOCK; ++ if ((buf[0] & 0x08) == 0x08) ++ *status |= FE_HAS_VITERBI; ++ break; ++ case VSB_8: ++ if ((buf[0] & 0x80) == 0x80) ++ *status |= FE_HAS_CARRIER; ++ else ++ break; ++ i2c_read_demod_bytes(state, 0x38, buf, 1); ++ if ((buf[0] & 0x02) == 0x00) ++ *status |= FE_HAS_SYNC; ++ if ((buf[0] & 0x01) == 0x01) { ++ *status |= FE_HAS_LOCK; ++ *status |= FE_HAS_VITERBI; ++ } ++ break; ++ default: ++ printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); ++ } ++ return 0; ++} ++ ++/* Calculate SNR estimation (scaled by 2^24) ++ ++ 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM ++ equations from LGDT3303 datasheet. VSB is the same between the '02 ++ and '03, so maybe QAM is too? Perhaps someone with a newer datasheet ++ that has QAM information could verify? ++ ++ For 8-VSB: (two ways, take your pick) ++ LGDT3302: ++ SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) ++ LGDT3303: ++ SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) ++ LGDT3302 & LGDT3303: ++ SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) ++ For 64-QAM: ++ SNR = 10 * log10( 688128 / MSEQAM) ++ For 256-QAM: ++ SNR = 10 * log10( 696320 / MSEQAM) ++ ++ We re-write the snr equation as: ++ SNR * 2^24 = 10*(c - intlog10(MSE)) ++ Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ ++ ++static u32 calculate_snr(u32 mse, u32 c) ++{ ++ if (mse == 0) /* No signal */ ++ return 0; ++ ++ mse = intlog10(mse); ++ if (mse > c) { ++ /* Negative SNR, which is possible, but realisticly the ++ demod will lose lock before the signal gets this bad. The ++ API only allows for unsigned values, so just return 0 */ ++ return 0; ++ } ++ return 10*(c - mse); ++} ++ ++static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; ++ u8 buf[5]; /* read data buffer */ ++ u32 noise; /* noise value */ ++ u32 c; /* per-modulation SNR calculation constant */ ++ ++ switch(state->current_modulation) { ++ case VSB_8: ++ i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); ++#ifdef USE_EQMSE ++ /* Use Equalizer Mean-Square Error Register */ ++ /* SNR for ranges from -15.61 to +41.58 */ ++ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; ++ c = 69765745; /* log10(25*24^2)*2^24 */ ++#else ++ /* Use Phase Tracker Mean-Square Error Register */ ++ /* SNR for ranges from -13.11 to +44.08 */ ++ noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; ++ c = 73957994; /* log10(25*32^2)*2^24 */ ++#endif ++ break; ++ case QAM_64: ++ case QAM_256: ++ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); ++ noise = ((buf[0] & 3) << 8) | buf[1]; ++ c = state->current_modulation == QAM_64 ? 97939837 : 98026066; ++ /* log10(688128)*2^24 and log10(696320)*2^24 */ ++ break; ++ default: ++ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", ++ __func__); ++ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ ++ } ++ ++ state->snr = calculate_snr(noise, c); ++ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ ++ ++ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, ++ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); ++ ++ return 0; ++} ++ ++static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; ++ u8 buf[5]; /* read data buffer */ ++ u32 noise; /* noise value */ ++ u32 c; /* per-modulation SNR calculation constant */ ++ ++ switch(state->current_modulation) { ++ case VSB_8: ++ i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); ++#ifdef USE_EQMSE ++ /* Use Equalizer Mean-Square Error Register */ ++ /* SNR for ranges from -16.12 to +44.08 */ ++ noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; ++ c = 73957994; /* log10(25*32^2)*2^24 */ ++#else ++ /* Use Phase Tracker Mean-Square Error Register */ ++ /* SNR for ranges from -13.11 to +44.08 */ ++ noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; ++ c = 73957994; /* log10(25*32^2)*2^24 */ ++#endif ++ break; ++ case QAM_64: ++ case QAM_256: ++ i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); ++ noise = (buf[0] << 8) | buf[1]; ++ c = state->current_modulation == QAM_64 ? 97939837 : 98026066; ++ /* log10(688128)*2^24 and log10(696320)*2^24 */ ++ break; ++ default: ++ printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", ++ __func__); ++ return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ ++ } ++ ++ state->snr = calculate_snr(noise, c); ++ *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ ++ ++ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, ++ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); ++ ++ return 0; ++} ++ ++static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ /* Calculate Strength from SNR up to 35dB */ ++ /* Even though the SNR can go higher than 35dB, there is some comfort */ ++ /* factor in having a range of strong signals that can show at 100% */ ++ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; ++ u16 snr; ++ int ret; ++ ++ ret = fe->ops.read_snr(fe, &snr); ++ if (ret != 0) ++ return ret; ++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ ++ /* scale the range 0 - 35*2^24 into 0 - 65535 */ ++ if (state->snr >= 8960 * 0x10000) ++ *strength = 0xffff; ++ else ++ *strength = state->snr / 8960; ++ ++ return 0; ++} ++ ++static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) ++{ ++ /* I have no idea about this - it may not be needed */ ++ fe_tune_settings->min_delay_ms = 500; ++ fe_tune_settings->step_size = 0; ++ fe_tune_settings->max_drift = 0; ++ return 0; ++} ++ ++static void lgdt330x_release(struct dvb_frontend* fe) ++{ ++ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops lgdt3302_ops; ++static struct dvb_frontend_ops lgdt3303_ops; ++ ++struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct lgdt330x_state* state = NULL; ++ u8 buf[1]; ++ ++ /* Allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct lgdt330x_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* Setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* Create dvb_frontend */ ++ switch (config->demod_chip) { ++ case LGDT3302: ++ memcpy(&state->frontend.ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ case LGDT3303: ++ memcpy(&state->frontend.ops, &lgdt3303_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ default: ++ goto error; ++ } ++ state->frontend.demodulator_priv = state; ++ ++ /* Verify communication with demod chip */ ++ if (i2c_read_demod_bytes(state, 2, buf, 1)) ++ goto error; ++ ++ state->current_frequency = -1; ++ state->current_modulation = -1; ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ dprintk("%s: ERROR\n",__func__); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops lgdt3302_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name= "LG Electronics LGDT3302 VSB/QAM Frontend", ++ .frequency_min= 54000000, ++ .frequency_max= 858000000, ++ .frequency_stepsize= 62500, ++ .symbol_rate_min = 5056941, /* QAM 64 */ ++ .symbol_rate_max = 10762000, /* VSB 8 */ ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ .init = lgdt330x_init, ++ .set_frontend = lgdt330x_set_parameters, ++ .get_frontend = lgdt330x_get_frontend, ++ .get_tune_settings = lgdt330x_get_tune_settings, ++ .read_status = lgdt3302_read_status, ++ .read_ber = lgdt330x_read_ber, ++ .read_signal_strength = lgdt330x_read_signal_strength, ++ .read_snr = lgdt3302_read_snr, ++ .read_ucblocks = lgdt330x_read_ucblocks, ++ .release = lgdt330x_release, ++}; ++ ++static struct dvb_frontend_ops lgdt3303_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name= "LG Electronics LGDT3303 VSB/QAM Frontend", ++ .frequency_min= 54000000, ++ .frequency_max= 858000000, ++ .frequency_stepsize= 62500, ++ .symbol_rate_min = 5056941, /* QAM 64 */ ++ .symbol_rate_max = 10762000, /* VSB 8 */ ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ .init = lgdt330x_init, ++ .set_frontend = lgdt330x_set_parameters, ++ .get_frontend = lgdt330x_get_frontend, ++ .get_tune_settings = lgdt330x_get_tune_settings, ++ .read_status = lgdt3303_read_status, ++ .read_ber = lgdt330x_read_ber, ++ .read_signal_strength = lgdt330x_read_signal_strength, ++ .read_snr = lgdt3303_read_snr, ++ .read_ucblocks = lgdt330x_read_ucblocks, ++ .release = lgdt330x_release, ++}; ++ ++MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); ++MODULE_AUTHOR("Wilson Michaels"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(lgdt330x_attach); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h +new file mode 100644 +index 0000000..9012504 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgdt330x.h +@@ -0,0 +1,73 @@ ++/* ++ * Support for LGDT3302 and LGDT3303 - VSB/QAM ++ * ++ * Copyright (C) 2005 Wilson Michaels ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef LGDT330X_H ++#define LGDT330X_H ++ ++#include ++ ++typedef enum lg_chip_t { ++ UNDEFINED, ++ LGDT3302, ++ LGDT3303 ++}lg_chip_type; ++ ++struct lgdt330x_config ++{ ++ /* The demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* LG demodulator chip LGDT3302 or LGDT3303 */ ++ lg_chip_type demod_chip; ++ ++ /* MPEG hardware interface - 0:parallel 1:serial */ ++ int serial_mpeg; ++ ++ /* PLL interface */ ++ int (*pll_rf_set) (struct dvb_frontend* fe, int index); ++ ++ /* Need to set device param for start_dma */ ++ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); ++ ++ /* Flip the polarity of the mpeg data transfer clock using alternate init data ++ * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled */ ++ int clock_polarity_flip; ++}; ++ ++#if defined(CONFIG_DVB_LGDT330X) || (defined(CONFIG_DVB_LGDT330X_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_LGDT330X ++ ++#endif /* LGDT330X_H */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h +new file mode 100644 +index 0000000..38c7669 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgdt330x_priv.h +@@ -0,0 +1,77 @@ ++/* ++ * Support for LGDT3302 and LGDT3303 - VSB/QAM ++ * ++ * Copyright (C) 2005 Wilson Michaels ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _LGDT330X_PRIV_ ++#define _LGDT330X_PRIV_ ++ ++/* i2c control register addresses */ ++enum I2C_REG { ++ TOP_CONTROL= 0x00, ++ IRQ_MASK= 0x01, ++ IRQ_STATUS= 0x02, ++ VSB_CARRIER_FREQ0= 0x16, ++ VSB_CARRIER_FREQ1= 0x17, ++ VSB_CARRIER_FREQ2= 0x18, ++ VSB_CARRIER_FREQ3= 0x19, ++ CARRIER_MSEQAM1= 0x1a, ++ CARRIER_MSEQAM2= 0x1b, ++ CARRIER_LOCK= 0x1c, ++ TIMING_RECOVERY= 0x1d, ++ AGC_DELAY0= 0x2a, ++ AGC_DELAY1= 0x2b, ++ AGC_DELAY2= 0x2c, ++ AGC_RF_BANDWIDTH0= 0x2d, ++ AGC_RF_BANDWIDTH1= 0x2e, ++ AGC_RF_BANDWIDTH2= 0x2f, ++ AGC_LOOP_BANDWIDTH0= 0x30, ++ AGC_LOOP_BANDWIDTH1= 0x31, ++ AGC_FUNC_CTRL1= 0x32, ++ AGC_FUNC_CTRL2= 0x33, ++ AGC_FUNC_CTRL3= 0x34, ++ AGC_RFIF_ACC0= 0x39, ++ AGC_RFIF_ACC1= 0x3a, ++ AGC_RFIF_ACC2= 0x3b, ++ AGC_STATUS= 0x3f, ++ SYNC_STATUS_VSB= 0x43, ++ DEMUX_CONTROL= 0x66, ++ LGDT3302_EQPH_ERR0= 0x47, ++ LGDT3302_EQ_ERR1= 0x48, ++ LGDT3302_EQ_ERR2= 0x49, ++ LGDT3302_PH_ERR1= 0x4a, ++ LGDT3302_PH_ERR2= 0x4b, ++ LGDT3302_PACKET_ERR_COUNTER1= 0x6a, ++ LGDT3302_PACKET_ERR_COUNTER2= 0x6b, ++ LGDT3303_EQPH_ERR0= 0x6e, ++ LGDT3303_EQ_ERR1= 0x6f, ++ LGDT3303_EQ_ERR2= 0x70, ++ LGDT3303_PH_ERR1= 0x71, ++ LGDT3303_PH_ERR2= 0x72, ++ LGDT3303_PACKET_ERR_COUNTER1= 0x8b, ++ LGDT3303_PACKET_ERR_COUNTER2= 0x8c, ++}; ++ ++#endif /* _LGDT330X_PRIV_ */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/lgs8gl5.c b/drivers/media/dvb-frontends/lgs8gl5.c +new file mode 100644 +index 0000000..416cce3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgs8gl5.c +@@ -0,0 +1,453 @@ ++/* ++ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver ++ ++ Copyright (C) 2008 Sirius International (Hong Kong) Limited ++ Timothy Lee ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "lgs8gl5.h" ++ ++ ++#define REG_RESET 0x02 ++#define REG_RESET_OFF 0x01 ++#define REG_03 0x03 ++#define REG_04 0x04 ++#define REG_07 0x07 ++#define REG_09 0x09 ++#define REG_0A 0x0a ++#define REG_0B 0x0b ++#define REG_0C 0x0c ++#define REG_37 0x37 ++#define REG_STRENGTH 0x4b ++#define REG_STRENGTH_MASK 0x7f ++#define REG_STRENGTH_CARRIER 0x80 ++#define REG_INVERSION 0x7c ++#define REG_INVERSION_ON 0x80 ++#define REG_7D 0x7d ++#define REG_7E 0x7e ++#define REG_A2 0xa2 ++#define REG_STATUS 0xa4 ++#define REG_STATUS_SYNC 0x04 ++#define REG_STATUS_LOCK 0x01 ++ ++ ++struct lgs8gl5_state { ++ struct i2c_adapter *i2c; ++ const struct lgs8gl5_config *config; ++ struct dvb_frontend frontend; ++}; ++ ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "lgs8gl5: " args); \ ++ } while (0) ++ ++ ++/* Writes into demod's register */ ++static int ++lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = {reg, data}; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) ++ dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n", ++ __func__, reg, data, ret); ++ return (ret != 1) ? -1 : 0; ++} ++ ++ ++/* Reads from demod's register */ ++static int ++lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = {reg}; ++ u8 b1[] = {0}; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, ++ { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) ++ return -EIO; ++ ++ return b1[0]; ++} ++ ++ ++static int ++lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data) ++{ ++ lgs8gl5_read_reg(state, reg); ++ lgs8gl5_write_reg(state, reg, data); ++ return 0; ++} ++ ++ ++/* Writes into alternate device's register */ ++/* TODO: Find out what that device is for! */ ++static int ++lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 b0[] = {reg}; ++ u8 b1[] = {0}; ++ u8 b2[] = {reg, data}; ++ struct i2c_msg msg[3] = { ++ { ++ .addr = state->config->demod_address + 2, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, ++ { ++ .addr = state->config->demod_address + 2, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ }, ++ { ++ .addr = state->config->demod_address + 2, ++ .flags = 0, ++ .buf = b2, ++ .len = 2 ++ }, ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 3); ++ return (ret != 3) ? -1 : 0; ++} ++ ++ ++static void ++lgs8gl5_soft_reset(struct lgs8gl5_state *state) ++{ ++ u8 val; ++ ++ dprintk("%s\n", __func__); ++ ++ val = lgs8gl5_read_reg(state, REG_RESET); ++ lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF); ++ lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF); ++ msleep(5); ++} ++ ++ ++/* Starts demodulation */ ++static void ++lgs8gl5_start_demod(struct lgs8gl5_state *state) ++{ ++ u8 val; ++ int n; ++ ++ dprintk("%s\n", __func__); ++ ++ lgs8gl5_update_alt_reg(state, 0xc2, 0x28); ++ lgs8gl5_soft_reset(state); ++ lgs8gl5_update_reg(state, REG_07, 0x10); ++ lgs8gl5_update_reg(state, REG_07, 0x10); ++ lgs8gl5_write_reg(state, REG_09, 0x0e); ++ lgs8gl5_write_reg(state, REG_0A, 0xe5); ++ lgs8gl5_write_reg(state, REG_0B, 0x35); ++ lgs8gl5_write_reg(state, REG_0C, 0x30); ++ ++ lgs8gl5_update_reg(state, REG_03, 0x00); ++ lgs8gl5_update_reg(state, REG_7E, 0x01); ++ lgs8gl5_update_alt_reg(state, 0xc5, 0x00); ++ lgs8gl5_update_reg(state, REG_04, 0x02); ++ lgs8gl5_update_reg(state, REG_37, 0x01); ++ lgs8gl5_soft_reset(state); ++ ++ /* Wait for carrier */ ++ for (n = 0; n < 10; n++) { ++ val = lgs8gl5_read_reg(state, REG_STRENGTH); ++ dprintk("Wait for carrier[%d] 0x%02X\n", n, val); ++ if (val & REG_STRENGTH_CARRIER) ++ break; ++ msleep(4); ++ } ++ if (!(val & REG_STRENGTH_CARRIER)) ++ return; ++ ++ /* Wait for lock */ ++ for (n = 0; n < 20; n++) { ++ val = lgs8gl5_read_reg(state, REG_STATUS); ++ dprintk("Wait for lock[%d] 0x%02X\n", n, val); ++ if (val & REG_STATUS_LOCK) ++ break; ++ msleep(12); ++ } ++ if (!(val & REG_STATUS_LOCK)) ++ return; ++ ++ lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2)); ++ lgs8gl5_soft_reset(state); ++} ++ ++ ++static int ++lgs8gl5_init(struct dvb_frontend *fe) ++{ ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ lgs8gl5_update_alt_reg(state, 0xc2, 0x28); ++ lgs8gl5_soft_reset(state); ++ lgs8gl5_update_reg(state, REG_07, 0x10); ++ lgs8gl5_update_reg(state, REG_07, 0x10); ++ lgs8gl5_write_reg(state, REG_09, 0x0e); ++ lgs8gl5_write_reg(state, REG_0A, 0xe5); ++ lgs8gl5_write_reg(state, REG_0B, 0x35); ++ lgs8gl5_write_reg(state, REG_0C, 0x30); ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); ++ u8 flags = lgs8gl5_read_reg(state, REG_STATUS); ++ ++ *status = 0; ++ ++ if ((level & REG_STRENGTH_MASK) > 0) ++ *status |= FE_HAS_SIGNAL; ++ if (level & REG_STRENGTH_CARRIER) ++ *status |= FE_HAS_CARRIER; ++ if (flags & REG_STATUS_SYNC) ++ *status |= FE_HAS_SYNC; ++ if (flags & REG_STATUS_LOCK) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ *ber = 0; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) ++{ ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); ++ *signal_strength = (level & REG_STRENGTH_MASK) << 8; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); ++ *snr = (level & REG_STRENGTH_MASK) << 8; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (p->bandwidth_hz != 8000000) ++ return -EINVAL; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* lgs8gl5_set_inversion(state, p->inversion); */ ++ ++ lgs8gl5_start_demod(state); ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ u8 inv = lgs8gl5_read_reg(state, REG_INVERSION); ++ ++ p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF; ++ ++ p->code_rate_HP = FEC_1_2; ++ p->code_rate_LP = FEC_7_8; ++ p->guard_interval = GUARD_INTERVAL_1_32; ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ p->modulation = QAM_64; ++ p->hierarchy = HIERARCHY_NONE; ++ p->bandwidth_hz = 8000000; ++ ++ return 0; ++} ++ ++ ++static int ++lgs8gl5_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 240; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++ ++static void ++lgs8gl5_release(struct dvb_frontend *fe) ++{ ++ struct lgs8gl5_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++ ++static struct dvb_frontend_ops lgs8gl5_ops; ++ ++ ++struct dvb_frontend* ++lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) ++{ ++ struct lgs8gl5_state *state = NULL; ++ ++ dprintk("%s\n", __func__); ++ ++ /* Allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* Setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* Check if the demod is there */ ++ if (lgs8gl5_read_reg(state, REG_RESET) < 0) ++ goto error; ++ ++ /* Create dvb_frontend */ ++ memcpy(&state->frontend.ops, &lgs8gl5_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(lgs8gl5_attach); ++ ++ ++static struct dvb_frontend_ops lgs8gl5_ops = { ++ .delsys = { SYS_DTMB }, ++ .info = { ++ .name = "Legend Silicon LGS-8GL5 DMB-TH", ++ .frequency_min = 474000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 10000, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_BANDWIDTH_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER ++ }, ++ ++ .release = lgs8gl5_release, ++ ++ .init = lgs8gl5_init, ++ ++ .set_frontend = lgs8gl5_set_frontend, ++ .get_frontend = lgs8gl5_get_frontend, ++ .get_tune_settings = lgs8gl5_get_tune_settings, ++ ++ .read_status = lgs8gl5_read_status, ++ .read_ber = lgs8gl5_read_ber, ++ .read_signal_strength = lgs8gl5_read_signal_strength, ++ .read_snr = lgs8gl5_read_snr, ++ .read_ucblocks = lgs8gl5_read_ucblocks, ++}; ++ ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver"); ++MODULE_AUTHOR("Timothy Lee"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/lgs8gl5.h b/drivers/media/dvb-frontends/lgs8gl5.h +new file mode 100644 +index 0000000..d141767 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgs8gl5.h +@@ -0,0 +1,45 @@ ++/* ++ Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver ++ ++ Copyright (C) 2008 Sirius International (Hong Kong) Limited ++ Timothy Lee ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef LGS8GL5_H ++#define LGS8GL5_H ++ ++#include ++ ++struct lgs8gl5_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++#if defined(CONFIG_DVB_LGS8GL5) || \ ++ (defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *lgs8gl5_attach( ++ const struct lgs8gl5_config *config, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *lgs8gl5_attach( ++ const struct lgs8gl5_config *config, struct i2c_adapter *i2c) { ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LGS8GL5 */ ++ ++#endif /* LGS8GL5_H */ +diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c +new file mode 100644 +index 0000000..3c92f36 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgs8gxx.c +@@ -0,0 +1,1075 @@ ++/* ++ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator ++ * LGS8913, LGS8GL5, LGS8G75 ++ * experimental support LGS8G42, LGS8G52 ++ * ++ * Copyright (C) 2007-2009 David T.L. Wong ++ * Copyright (C) 2008 Sirius International (Hong Kong) Limited ++ * Timothy Lee (for initial work on LGS8GL5) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "lgs8gxx.h" ++#include "lgs8gxx_priv.h" ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "lgs8gxx: " args); \ ++ } while (0) ++ ++static int debug; ++static int fake_signal_str = 1; ++ ++#define LGS8GXX_FIRMWARE "lgs8g75.fw" ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++module_param(fake_signal_str, int, 0644); ++MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." ++"Signal strength calculation is slow.(default:on)."); ++ ++/* LGS8GXX internal helper functions */ ++ ++static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; ++ ++ msg.addr = priv->config->demod_address; ++ if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) ++ msg.addr += 0x02; ++ ++ if (debug >= 2) ++ dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); ++ ++ ret = i2c_transfer(priv->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", ++ __func__, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) ++{ ++ int ret; ++ u8 dev_addr; ++ ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .flags = 0, .buf = b0, .len = 1 }, ++ { .flags = I2C_M_RD, .buf = b1, .len = 1 }, ++ }; ++ ++ dev_addr = priv->config->demod_address; ++ if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) ++ dev_addr += 0x02; ++ msg[1].addr = msg[0].addr = dev_addr; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret != 2) { ++ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); ++ return -1; ++ } ++ ++ *p_data = b1[0]; ++ if (debug >= 2) ++ dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); ++ return 0; ++} ++ ++static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) ++{ ++ lgs8gxx_write_reg(priv, 0x02, 0x00); ++ msleep(1); ++ lgs8gxx_write_reg(priv, 0x02, 0x01); ++ msleep(100); ++ ++ return 0; ++} ++ ++static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, ++ u8 val, u8 delay, u8 tries) ++{ ++ u8 t; ++ int i; ++ ++ for (i = 0; i < tries; i++) { ++ lgs8gxx_read_reg(priv, reg, &t); ++ ++ if ((t & mask) == val) ++ return 0; ++ msleep(delay); ++ } ++ ++ return 1; ++} ++ ++static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) ++{ ++ const struct lgs8gxx_config *config = priv->config; ++ u8 if_conf; ++ ++ if_conf = 0x10; /* AGC output on, RF_AGC output off; */ ++ ++ if_conf |= ++ ((config->ext_adc) ? 0x80 : 0x00) | ++ ((config->if_neg_center) ? 0x04 : 0x00) | ++ ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ ++ ((config->adc_signed) ? 0x02 : 0x00) | ++ ((config->if_neg_edge) ? 0x01 : 0x00); ++ ++ if (config->ext_adc && ++ (config->prod == LGS8GXX_PROD_LGS8G52)) { ++ lgs8gxx_write_reg(priv, 0xBA, 0x40); ++ } ++ ++ lgs8gxx_write_reg(priv, 0x07, if_conf); ++ ++ return 0; ++} ++ ++static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) ++{ ++ u64 val; ++ u32 v32; ++ u32 if_clk; ++ ++ if_clk = priv->config->if_clk_freq; ++ ++ val = freq; ++ if (freq != 0) { ++ val <<= 32; ++ if (if_clk != 0) ++ do_div(val, if_clk); ++ v32 = val & 0xFFFFFFFF; ++ dprintk("Set IF Freq to %dkHz\n", freq); ++ } else { ++ v32 = 0; ++ dprintk("Set IF Freq to baseband\n"); ++ } ++ dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); ++ lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); ++ lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); ++ lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); ++ } else { ++ lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); ++ lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); ++ lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); ++ lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); ++ } ++ ++ return 0; ++} ++ ++static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) ++{ ++ u64 val; ++ u32 v32 = 0; ++ u8 reg_addr, t; ++ int i; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) ++ reg_addr = 0x23; ++ else ++ reg_addr = 0x48; ++ ++ for (i = 0; i < 4; i++) { ++ lgs8gxx_read_reg(priv, reg_addr, &t); ++ v32 <<= 8; ++ v32 |= t; ++ reg_addr--; ++ } ++ ++ val = v32; ++ val *= priv->config->if_clk_freq; ++ val >>= 32; ++ dprintk("AFC = %u kHz\n", (u32)val); ++ return 0; ++} ++ ++static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) ++{ ++ u8 t; ++ u8 prod = priv->config->prod; ++ ++ if (prod == LGS8GXX_PROD_LGS8913) ++ lgs8gxx_write_reg(priv, 0xC6, 0x01); ++ ++ if (prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_read_reg(priv, 0x0C, &t); ++ t &= (~0x04); ++ lgs8gxx_write_reg(priv, 0x0C, t | 0x80); ++ lgs8gxx_write_reg(priv, 0x39, 0x00); ++ lgs8gxx_write_reg(priv, 0x3D, 0x04); ++ } else if (prod == LGS8GXX_PROD_LGS8913 || ++ prod == LGS8GXX_PROD_LGS8GL5 || ++ prod == LGS8GXX_PROD_LGS8G42 || ++ prod == LGS8GXX_PROD_LGS8G52 || ++ prod == LGS8GXX_PROD_LGS8G54) { ++ lgs8gxx_read_reg(priv, 0x7E, &t); ++ lgs8gxx_write_reg(priv, 0x7E, t | 0x01); ++ ++ /* clear FEC self reset */ ++ lgs8gxx_read_reg(priv, 0xC5, &t); ++ lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); ++ } ++ ++ if (prod == LGS8GXX_PROD_LGS8913) { ++ /* FEC auto detect */ ++ lgs8gxx_write_reg(priv, 0xC1, 0x03); ++ ++ lgs8gxx_read_reg(priv, 0x7C, &t); ++ t = (t & 0x8C) | 0x03; ++ lgs8gxx_write_reg(priv, 0x7C, t); ++ ++ /* BER test mode */ ++ lgs8gxx_read_reg(priv, 0xC3, &t); ++ t = (t & 0xEF) | 0x10; ++ lgs8gxx_write_reg(priv, 0xC3, t); ++ } ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G52) ++ lgs8gxx_write_reg(priv, 0xD9, 0x40); ++ ++ return 0; ++} ++ ++static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) ++{ ++ u8 t; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ u8 t2; ++ lgs8gxx_read_reg(priv, 0x0C, &t); ++ t &= (~0x80); ++ lgs8gxx_write_reg(priv, 0x0C, t); ++ ++ lgs8gxx_read_reg(priv, 0x0C, &t); ++ lgs8gxx_read_reg(priv, 0x19, &t2); ++ ++ if (((t&0x03) == 0x01) && (t2&0x01)) { ++ lgs8gxx_write_reg(priv, 0x6E, 0x05); ++ lgs8gxx_write_reg(priv, 0x39, 0x02); ++ lgs8gxx_write_reg(priv, 0x39, 0x03); ++ lgs8gxx_write_reg(priv, 0x3D, 0x05); ++ lgs8gxx_write_reg(priv, 0x3E, 0x28); ++ lgs8gxx_write_reg(priv, 0x53, 0x80); ++ } else { ++ lgs8gxx_write_reg(priv, 0x6E, 0x3F); ++ lgs8gxx_write_reg(priv, 0x39, 0x00); ++ lgs8gxx_write_reg(priv, 0x3D, 0x04); ++ } ++ ++ lgs8gxx_soft_reset(priv); ++ return 0; ++ } ++ ++ /* turn off auto-detect; manual settings */ ++ lgs8gxx_write_reg(priv, 0x7E, 0); ++ if (priv->config->prod == LGS8GXX_PROD_LGS8913) ++ lgs8gxx_write_reg(priv, 0xC1, 0); ++ ++ lgs8gxx_read_reg(priv, 0xC5, &t); ++ t = (t & 0xE0) | 0x06; ++ lgs8gxx_write_reg(priv, 0xC5, t); ++ ++ lgs8gxx_soft_reset(priv); ++ ++ return 0; ++} ++ ++static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) ++{ ++ int ret = 0; ++ u8 t; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) ++ ret = lgs8gxx_read_reg(priv, 0x13, &t); ++ else ++ ret = lgs8gxx_read_reg(priv, 0x4B, &t); ++ if (ret != 0) ++ return ret; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) ++ *locked = ((t & 0x80) == 0x80) ? 1 : 0; ++ else ++ *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; ++ return 0; ++} ++ ++/* Wait for Code Acquisition Lock */ ++static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) ++{ ++ int ret = 0; ++ u8 reg, mask, val; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ reg = 0x13; ++ mask = 0x80; ++ val = 0x80; ++ } else { ++ reg = 0x4B; ++ mask = 0xC0; ++ val = 0xC0; ++ } ++ ++ ret = wait_reg_mask(priv, reg, mask, val, 50, 40); ++ *locked = (ret == 0) ? 1 : 0; ++ ++ return 0; ++} ++ ++static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, ++ u8 *finished) ++{ ++ int ret = 0; ++ u8 reg, mask, val; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ reg = 0x1f; ++ mask = 0xC0; ++ val = 0x80; ++ } else { ++ reg = 0xA4; ++ mask = 0x03; ++ val = 0x01; ++ } ++ ++ ret = wait_reg_mask(priv, reg, mask, val, 10, 20); ++ *finished = (ret == 0) ? 1 : 0; ++ ++ return 0; ++} ++ ++static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, ++ u8 *locked) ++{ ++ int err = 0; ++ u8 ad_fini = 0; ++ u8 t1, t2; ++ ++ if (gi == GI_945) ++ dprintk("try GI 945\n"); ++ else if (gi == GI_595) ++ dprintk("try GI 595\n"); ++ else if (gi == GI_420) ++ dprintk("try GI 420\n"); ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_read_reg(priv, 0x0C, &t1); ++ lgs8gxx_read_reg(priv, 0x18, &t2); ++ t1 &= ~(GI_MASK); ++ t1 |= gi; ++ t2 &= 0xFE; ++ t2 |= cpn ? 0x01 : 0x00; ++ lgs8gxx_write_reg(priv, 0x0C, t1); ++ lgs8gxx_write_reg(priv, 0x18, t2); ++ } else { ++ lgs8gxx_write_reg(priv, 0x04, gi); ++ } ++ lgs8gxx_soft_reset(priv); ++ err = lgs8gxx_wait_ca_lock(priv, locked); ++ if (err || !(*locked)) ++ return err; ++ err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); ++ if (err != 0) ++ return err; ++ if (ad_fini) { ++ dprintk("auto detect finished\n"); ++ } else ++ *locked = 0; ++ ++ return 0; ++} ++ ++static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, ++ u8 *detected_param, u8 *gi) ++{ ++ int i, j; ++ int err = 0; ++ u8 locked = 0, tmp_gi; ++ ++ dprintk("%s\n", __func__); ++ ++ lgs8gxx_set_mode_auto(priv); ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_write_reg(priv, 0x67, 0xAA); ++ lgs8gxx_write_reg(priv, 0x6E, 0x3F); ++ } else { ++ /* Guard Interval */ ++ lgs8gxx_write_reg(priv, 0x03, 00); ++ } ++ ++ for (i = 0; i < 2; i++) { ++ for (j = 0; j < 2; j++) { ++ tmp_gi = GI_945; ++ err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); ++ if (err) ++ goto out; ++ if (locked) ++ goto locked; ++ } ++ for (j = 0; j < 2; j++) { ++ tmp_gi = GI_420; ++ err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); ++ if (err) ++ goto out; ++ if (locked) ++ goto locked; ++ } ++ tmp_gi = GI_595; ++ err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); ++ if (err) ++ goto out; ++ if (locked) ++ goto locked; ++ } ++ ++locked: ++ if ((err == 0) && (locked == 1)) { ++ u8 t; ++ ++ if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_read_reg(priv, 0xA2, &t); ++ *detected_param = t; ++ } else { ++ lgs8gxx_read_reg(priv, 0x1F, &t); ++ *detected_param = t & 0x3F; ++ } ++ ++ if (tmp_gi == GI_945) ++ dprintk("GI 945 locked\n"); ++ else if (tmp_gi == GI_595) ++ dprintk("GI 595 locked\n"); ++ else if (tmp_gi == GI_420) ++ dprintk("GI 420 locked\n"); ++ *gi = tmp_gi; ++ } ++ if (!locked) ++ err = -1; ++ ++out: ++ return err; ++} ++ ++static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) ++{ ++ s8 err; ++ u8 gi = 0x2; ++ u8 detected_param = 0; ++ ++ err = lgs8gxx_auto_detect(priv, &detected_param, &gi); ++ ++ if (err != 0) { ++ dprintk("lgs8gxx_auto_detect failed\n"); ++ } else ++ dprintk("detected param = 0x%02X\n", detected_param); ++ ++ /* Apply detected parameters */ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8913) { ++ u8 inter_leave_len = detected_param & TIM_MASK ; ++ /* Fix 8913 time interleaver detection bug */ ++ inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; ++ detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; ++ detected_param |= inter_leave_len; ++ } ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ u8 t; ++ lgs8gxx_read_reg(priv, 0x19, &t); ++ t &= 0x81; ++ t |= detected_param << 1; ++ lgs8gxx_write_reg(priv, 0x19, t); ++ } else { ++ lgs8gxx_write_reg(priv, 0x7D, detected_param); ++ if (priv->config->prod == LGS8GXX_PROD_LGS8913) ++ lgs8gxx_write_reg(priv, 0xC0, detected_param); ++ } ++ /* lgs8gxx_soft_reset(priv); */ ++ ++ /* Enter manual mode */ ++ lgs8gxx_set_mode_manual(priv); ++ ++ switch (gi) { ++ case GI_945: ++ priv->curr_gi = 945; break; ++ case GI_595: ++ priv->curr_gi = 595; break; ++ case GI_420: ++ priv->curr_gi = 420; break; ++ default: ++ priv->curr_gi = 945; break; ++ } ++} ++ ++static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, ++ u8 serial, u8 clk_pol, u8 clk_gated) ++{ ++ int ret = 0; ++ u8 t, reg_addr; ++ ++ reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; ++ ret = lgs8gxx_read_reg(priv, reg_addr, &t); ++ if (ret != 0) ++ return ret; ++ ++ t &= 0xF8; ++ t |= serial ? TS_SERIAL : TS_PARALLEL; ++ t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; ++ t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; ++ ++ ret = lgs8gxx_write_reg(priv, reg_addr, t); ++ if (ret != 0) ++ return ret; ++ ++ return 0; ++} ++ ++/* A/D input peak-to-peak voltage range */ ++static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, ++ u8 sel) ++{ ++ u8 r26 = 0x73, r27 = 0x90; ++ ++ if (priv->config->prod != LGS8GXX_PROD_LGS8G75) ++ return 0; ++ ++ r26 |= (sel & 0x01) << 7; ++ r27 |= (sel & 0x02) >> 1; ++ lgs8gxx_write_reg(priv, 0x26, r26); ++ lgs8gxx_write_reg(priv, 0x27, r27); ++ ++ return 0; ++} ++ ++/* LGS8913 demod frontend functions */ ++ ++static int lgs8913_init(struct lgs8gxx_state *priv) ++{ ++ u8 t; ++ ++ /* LGS8913 specific */ ++ lgs8gxx_write_reg(priv, 0xc1, 0x3); ++ ++ lgs8gxx_read_reg(priv, 0x7c, &t); ++ lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); ++ ++ /* LGS8913 specific */ ++ lgs8gxx_read_reg(priv, 0xc3, &t); ++ lgs8gxx_write_reg(priv, 0xc3, t&0x10); ++ ++ ++ return 0; ++} ++ ++static int lgs8g75_init_data(struct lgs8gxx_state *priv) ++{ ++ const struct firmware *fw; ++ int rc; ++ int i; ++ ++ rc = request_firmware(&fw, LGS8GXX_FIRMWARE, &priv->i2c->dev); ++ if (rc) ++ return rc; ++ ++ lgs8gxx_write_reg(priv, 0xC6, 0x40); ++ ++ lgs8gxx_write_reg(priv, 0x3D, 0x04); ++ lgs8gxx_write_reg(priv, 0x39, 0x00); ++ ++ lgs8gxx_write_reg(priv, 0x3A, 0x00); ++ lgs8gxx_write_reg(priv, 0x38, 0x00); ++ lgs8gxx_write_reg(priv, 0x3B, 0x00); ++ lgs8gxx_write_reg(priv, 0x38, 0x00); ++ ++ for (i = 0; i < fw->size; i++) { ++ lgs8gxx_write_reg(priv, 0x38, 0x00); ++ lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); ++ lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); ++ lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); ++ } ++ ++ lgs8gxx_write_reg(priv, 0x38, 0x00); ++ ++ release_firmware(fw); ++ return 0; ++} ++ ++static int lgs8gxx_init(struct dvb_frontend *fe) ++{ ++ struct lgs8gxx_state *priv = ++ (struct lgs8gxx_state *)fe->demodulator_priv; ++ const struct lgs8gxx_config *config = priv->config; ++ u8 data = 0; ++ s8 err; ++ dprintk("%s\n", __func__); ++ ++ lgs8gxx_read_reg(priv, 0, &data); ++ dprintk("reg 0 = 0x%02X\n", data); ++ ++ if (config->prod == LGS8GXX_PROD_LGS8G75) ++ lgs8g75_set_adc_vpp(priv, config->adc_vpp); ++ ++ /* Setup MPEG output format */ ++ err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, ++ config->ts_clk_pol, ++ config->ts_clk_gated); ++ if (err != 0) ++ return -EIO; ++ ++ if (config->prod == LGS8GXX_PROD_LGS8913) ++ lgs8913_init(priv); ++ lgs8gxx_set_if_freq(priv, priv->config->if_freq); ++ lgs8gxx_set_ad_mode(priv); ++ ++ return 0; ++} ++ ++static void lgs8gxx_release(struct dvb_frontend *fe) ++{ ++ struct lgs8gxx_state *state = fe->demodulator_priv; ++ dprintk("%s\n", __func__); ++ ++ kfree(state); ++} ++ ++ ++static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ ++ if (len != 2) ++ return -EINVAL; ++ ++ return lgs8gxx_write_reg(priv, buf[0], buf[1]); ++} ++ ++static int lgs8gxx_set_fe(struct dvb_frontend *fe) ++{ ++ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ /* set frequency */ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* start auto lock */ ++ lgs8gxx_auto_lock(priv); ++ ++ msleep(10); ++ ++ return 0; ++} ++ ++static int lgs8gxx_get_fe(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; ++ dprintk("%s\n", __func__); ++ ++ /* TODO: get real readings from device */ ++ /* inversion status */ ++ fe_params->inversion = INVERSION_OFF; ++ ++ /* bandwidth */ ++ fe_params->bandwidth_hz = 8000000; ++ ++ fe_params->code_rate_HP = FEC_AUTO; ++ fe_params->code_rate_LP = FEC_AUTO; ++ ++ fe_params->modulation = QAM_AUTO; ++ ++ /* transmission mode */ ++ fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; ++ ++ /* guard interval */ ++ fe_params->guard_interval = GUARD_INTERVAL_AUTO; ++ ++ /* hierarchy */ ++ fe_params->hierarchy = HIERARCHY_NONE; ++ ++ return 0; ++} ++ ++static ++int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ /* FIXME: copy from tda1004x.c */ ++ fesettings->min_delay_ms = 800; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ s8 ret; ++ u8 t, locked = 0; ++ ++ dprintk("%s\n", __func__); ++ *fe_status = 0; ++ ++ lgs8gxx_get_afc_phase(priv); ++ lgs8gxx_is_locked(priv, &locked); ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ if (locked) ++ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ return 0; ++ } ++ ++ ret = lgs8gxx_read_reg(priv, 0x4B, &t); ++ if (ret != 0) ++ return -EIO; ++ ++ dprintk("Reg 0x4B: 0x%02X\n", t); ++ ++ *fe_status = 0; ++ if (priv->config->prod == LGS8GXX_PROD_LGS8913) { ++ if ((t & 0x40) == 0x40) ++ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ if ((t & 0x80) == 0x80) ++ *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | ++ FE_HAS_LOCK; ++ } else { ++ if ((t & 0x80) == 0x80) ++ *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ ++ /* success */ ++ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); ++ return 0; ++} ++ ++static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) ++{ ++ u16 v; ++ u8 agc_lvl[2], cat; ++ ++ dprintk("%s()\n", __func__); ++ lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); ++ lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); ++ ++ v = agc_lvl[0]; ++ v <<= 8; ++ v |= agc_lvl[1]; ++ ++ dprintk("agc_lvl: 0x%04X\n", v); ++ ++ if (v < 0x100) ++ cat = 0; ++ else if (v < 0x190) ++ cat = 5; ++ else if (v < 0x2A8) ++ cat = 4; ++ else if (v < 0x381) ++ cat = 3; ++ else if (v < 0x400) ++ cat = 2; ++ else if (v == 0x400) ++ cat = 1; ++ else ++ cat = 0; ++ ++ *signal = cat * 65535 / 5; ++ ++ return 0; ++} ++ ++static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) ++{ ++ u8 t; s8 ret; ++ s16 max_strength = 0; ++ u8 str; ++ u16 i, gi = priv->curr_gi; ++ ++ dprintk("%s\n", __func__); ++ ++ ret = lgs8gxx_read_reg(priv, 0x4B, &t); ++ if (ret != 0) ++ return -EIO; ++ ++ if (fake_signal_str) { ++ if ((t & 0xC0) == 0xC0) { ++ dprintk("Fake signal strength\n"); ++ *signal = 0x7FFF; ++ } else ++ *signal = 0; ++ return 0; ++ } ++ ++ dprintk("gi = %d\n", gi); ++ for (i = 0; i < gi; i++) { ++ ++ if ((i & 0xFF) == 0) ++ lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); ++ lgs8gxx_write_reg(priv, 0x83, i & 0xFF); ++ ++ lgs8gxx_read_reg(priv, 0x94, &str); ++ if (max_strength < str) ++ max_strength = str; ++ } ++ ++ *signal = max_strength; ++ dprintk("%s: signal=0x%02X\n", __func__, *signal); ++ ++ lgs8gxx_read_reg(priv, 0x95, &t); ++ dprintk("%s: AVG Noise=0x%02X\n", __func__, t); ++ ++ return 0; ++} ++ ++static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) ++{ ++ u8 t; ++ s16 v = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ lgs8gxx_read_reg(priv, 0xB1, &t); ++ v |= t; ++ v <<= 8; ++ lgs8gxx_read_reg(priv, 0xB0, &t); ++ v |= t; ++ ++ *signal = v; ++ dprintk("%s: signal=0x%02X\n", __func__, *signal); ++ ++ return 0; ++} ++ ++static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8913) ++ return lgs8913_read_signal_strength(priv, signal); ++ else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) ++ return lgs8g75_read_signal_strength(priv, signal); ++ else ++ return lgs8gxx_read_signal_agc(priv, signal); ++} ++ ++static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ u8 t; ++ *snr = 0; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) ++ lgs8gxx_read_reg(priv, 0x34, &t); ++ else ++ lgs8gxx_read_reg(priv, 0x95, &t); ++ dprintk("AVG Noise=0x%02X\n", t); ++ *snr = 256 - t; ++ *snr <<= 8; ++ dprintk("snr=0x%x\n", *snr); ++ ++ return 0; ++} ++ ++static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); ++ return 0; ++} ++ ++static void packet_counter_start(struct lgs8gxx_state *priv) ++{ ++ u8 orig, t; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_read_reg(priv, 0x30, &orig); ++ orig &= 0xE7; ++ t = orig | 0x10; ++ lgs8gxx_write_reg(priv, 0x30, t); ++ t = orig | 0x18; ++ lgs8gxx_write_reg(priv, 0x30, t); ++ t = orig | 0x10; ++ lgs8gxx_write_reg(priv, 0x30, t); ++ } else { ++ lgs8gxx_write_reg(priv, 0xC6, 0x01); ++ lgs8gxx_write_reg(priv, 0xC6, 0x41); ++ lgs8gxx_write_reg(priv, 0xC6, 0x01); ++ } ++} ++ ++static void packet_counter_stop(struct lgs8gxx_state *priv) ++{ ++ u8 t; ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ lgs8gxx_read_reg(priv, 0x30, &t); ++ t &= 0xE7; ++ lgs8gxx_write_reg(priv, 0x30, t); ++ } else { ++ lgs8gxx_write_reg(priv, 0xC6, 0x81); ++ } ++} ++ ++static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ u8 reg_err, reg_total, t; ++ u32 total_cnt = 0, err_cnt = 0; ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ packet_counter_start(priv); ++ msleep(200); ++ packet_counter_stop(priv); ++ ++ if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { ++ reg_total = 0x28; reg_err = 0x2C; ++ } else { ++ reg_total = 0xD0; reg_err = 0xD4; ++ } ++ ++ for (i = 0; i < 4; i++) { ++ total_cnt <<= 8; ++ lgs8gxx_read_reg(priv, reg_total+3-i, &t); ++ total_cnt |= t; ++ } ++ for (i = 0; i < 4; i++) { ++ err_cnt <<= 8; ++ lgs8gxx_read_reg(priv, reg_err+3-i, &t); ++ err_cnt |= t; ++ } ++ dprintk("error=%d total=%d\n", err_cnt, total_cnt); ++ ++ if (total_cnt == 0) ++ *ber = 0; ++ else ++ *ber = err_cnt * 100 / total_cnt; ++ ++ dprintk("%s: ber=0x%x\n", __func__, *ber); ++ return 0; ++} ++ ++static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct lgs8gxx_state *priv = fe->demodulator_priv; ++ ++ if (priv->config->tuner_address == 0) ++ return 0; ++ if (enable) { ++ u8 v = 0x80 | priv->config->tuner_address; ++ return lgs8gxx_write_reg(priv, 0x01, v); ++ } ++ return lgs8gxx_write_reg(priv, 0x01, 0); ++} ++ ++static struct dvb_frontend_ops lgs8gxx_ops = { ++ .delsys = { SYS_DTMB }, ++ .info = { ++ .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", ++ .frequency_min = 474000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 10000, ++ .caps = ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO ++ }, ++ ++ .release = lgs8gxx_release, ++ ++ .init = lgs8gxx_init, ++ .write = lgs8gxx_write, ++ .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, ++ ++ .set_frontend = lgs8gxx_set_fe, ++ .get_frontend = lgs8gxx_get_fe, ++ .get_tune_settings = lgs8gxx_get_tune_settings, ++ ++ .read_status = lgs8gxx_read_status, ++ .read_ber = lgs8gxx_read_ber, ++ .read_signal_strength = lgs8gxx_read_signal_strength, ++ .read_snr = lgs8gxx_read_snr, ++ .read_ucblocks = lgs8gxx_read_ucblocks, ++}; ++ ++struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct lgs8gxx_state *priv = NULL; ++ u8 data = 0; ++ ++ dprintk("%s()\n", __func__); ++ ++ if (config == NULL || i2c == NULL) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); ++ if (priv == NULL) ++ goto error_out; ++ ++ priv->config = config; ++ priv->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (lgs8gxx_read_reg(priv, 0, &data) != 0) { ++ dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", ++ __func__, priv->config->demod_address); ++ goto error_out; ++ } ++ ++ lgs8gxx_read_reg(priv, 1, &data); ++ ++ memcpy(&priv->frontend.ops, &lgs8gxx_ops, ++ sizeof(struct dvb_frontend_ops)); ++ priv->frontend.demodulator_priv = priv; ++ ++ if (config->prod == LGS8GXX_PROD_LGS8G75) ++ lgs8g75_init_data(priv); ++ ++ return &priv->frontend; ++ ++error_out: ++ dprintk("%s() error_out\n", __func__); ++ kfree(priv); ++ return NULL; ++ ++} ++EXPORT_SYMBOL(lgs8gxx_attach); ++ ++MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); ++MODULE_AUTHOR("David T. L. Wong "); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(LGS8GXX_FIRMWARE); +diff --git a/drivers/media/dvb-frontends/lgs8gxx.h b/drivers/media/dvb-frontends/lgs8gxx.h +new file mode 100644 +index 0000000..33c3c5e +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgs8gxx.h +@@ -0,0 +1,95 @@ ++/* ++ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator ++ * LGS8913, LGS8GL5, LGS8G75 ++ * experimental support LGS8G42, LGS8G52 ++ * ++ * Copyright (C) 2007-2009 David T.L. Wong ++ * Copyright (C) 2008 Sirius International (Hong Kong) Limited ++ * Timothy Lee (for initial work on LGS8GL5) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef __LGS8GXX_H__ ++#define __LGS8GXX_H__ ++ ++#include ++#include ++ ++#define LGS8GXX_PROD_LGS8913 0 ++#define LGS8GXX_PROD_LGS8GL5 1 ++#define LGS8GXX_PROD_LGS8G42 3 ++#define LGS8GXX_PROD_LGS8G52 4 ++#define LGS8GXX_PROD_LGS8G54 5 ++#define LGS8GXX_PROD_LGS8G75 6 ++ ++struct lgs8gxx_config { ++ ++ /* product type */ ++ u8 prod; ++ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* parallel or serial transport stream */ ++ u8 serial_ts; ++ ++ /* transport stream polarity*/ ++ u8 ts_clk_pol; ++ ++ /* transport stream clock gated by ts_valid */ ++ u8 ts_clk_gated; ++ ++ /* A/D Clock frequency */ ++ u32 if_clk_freq; /* in kHz */ ++ ++ /* IF frequency */ ++ u32 if_freq; /* in kHz */ ++ ++ /*Use External ADC*/ ++ u8 ext_adc; ++ ++ /*External ADC output two's complement*/ ++ u8 adc_signed; ++ ++ /*Sample IF data at falling edge of IF_CLK*/ ++ u8 if_neg_edge; ++ ++ /*IF use Negative center frequency*/ ++ u8 if_neg_center; ++ ++ /*8G75 internal ADC input range selection*/ ++ /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ ++ u8 adc_vpp; ++ ++ /* slave address and configuration of the tuner */ ++ u8 tuner_address; ++}; ++ ++#if defined(CONFIG_DVB_LGS8GXX) || \ ++ (defined(CONFIG_DVB_LGS8GXX_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline ++struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, ++ struct i2c_adapter *i2c) { ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LGS8GXX */ ++ ++#endif /* __LGS8GXX_H__ */ +diff --git a/drivers/media/dvb-frontends/lgs8gxx_priv.h b/drivers/media/dvb-frontends/lgs8gxx_priv.h +new file mode 100644 +index 0000000..8ef376f +--- /dev/null ++++ b/drivers/media/dvb-frontends/lgs8gxx_priv.h +@@ -0,0 +1,70 @@ ++/* ++ * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator ++ * LGS8913, LGS8GL5, LGS8G75 ++ * experimental support LGS8G42, LGS8G52 ++ * ++ * Copyright (C) 2007-2009 David T.L. Wong ++ * Copyright (C) 2008 Sirius International (Hong Kong) Limited ++ * Timothy Lee (for initial work on LGS8GL5) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef LGS8913_PRIV_H ++#define LGS8913_PRIV_H ++ ++struct lgs8gxx_state { ++ struct i2c_adapter *i2c; ++ /* configuration settings */ ++ const struct lgs8gxx_config *config; ++ struct dvb_frontend frontend; ++ u16 curr_gi; /* current guard interval */ ++}; ++ ++#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */ ++#define SC_QAM64 0x10 /* 64QAM modulation */ ++#define SC_QAM32 0x0C /* 32QAM modulation */ ++#define SC_QAM16 0x08 /* 16QAM modulation */ ++#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ ++#define SC_QAM4 0x00 /* 4QAM modulation */ ++ ++#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ ++#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */ ++#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */ ++#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ ++ ++#define TIM_MASK 0x20 /* Time Interleave Length Mask */ ++#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ ++#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ ++ ++#define CF_MASK 0x80 /* Control Frame Mask */ ++#define CF_EN 0x80 /* Control Frame On */ ++ ++#define GI_MASK 0x03 /* Guard Interval Mask */ ++#define GI_420 0x00 /* 1/9 Guard Interval */ ++#define GI_595 0x01 /* */ ++#define GI_945 0x02 /* 1/4 Guard Interval */ ++ ++ ++#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */ ++#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */ ++#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */ ++#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */ ++#define TS_CLK_GATED 0x00 /* MPEG clock gated */ ++#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/ ++ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/lnbh24.h b/drivers/media/dvb-frontends/lnbh24.h +new file mode 100644 +index 0000000..c059b16 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lnbh24.h +@@ -0,0 +1,55 @@ ++/* ++ * lnbh24.h - driver for lnb supply and control ic lnbh24 ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _LNBH24_H ++#define _LNBH24_H ++ ++/* system register bits */ ++#define LNBH24_OLF 0x01 ++#define LNBH24_OTF 0x02 ++#define LNBH24_EN 0x04 ++#define LNBH24_VSEL 0x08 ++#define LNBH24_LLC 0x10 ++#define LNBH24_TEN 0x20 ++#define LNBH24_TTX 0x40 ++#define LNBH24_PCL 0x80 ++ ++#include ++ ++#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ ++ && defined(MODULE)) ++/* override_set and override_clear control which ++ system register bits (above) to always set & clear */ ++extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear, u8 i2c_addr); ++#else ++static inline struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear, u8 i2c_addr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c +new file mode 100644 +index 0000000..f3ba7b5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lnbp21.c +@@ -0,0 +1,188 @@ ++/* ++ * lnbp21.c - driver for lnb supply and control ic lnbp21 ++ * ++ * Copyright (C) 2006, 2009 Oliver Endriss ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "lnbp21.h" ++#include "lnbh24.h" ++ ++struct lnbp21 { ++ u8 config; ++ u8 override_or; ++ u8 override_and; ++ struct i2c_adapter *i2c; ++ u8 i2c_addr; ++}; ++ ++static int lnbp21_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, ++ .buf = &lnbp21->config, ++ .len = sizeof(lnbp21->config) }; ++ ++ lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); ++ ++ switch(voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ lnbp21->config |= LNBP21_EN; ++ break; ++ case SEC_VOLTAGE_18: ++ lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ lnbp21->config |= lnbp21->override_or; ++ lnbp21->config &= lnbp21->override_and; ++ ++ return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) ++{ ++ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, ++ .buf = &lnbp21->config, ++ .len = sizeof(lnbp21->config) }; ++ ++ if (arg) ++ lnbp21->config |= LNBP21_LLC; ++ else ++ lnbp21->config &= ~LNBP21_LLC; ++ ++ lnbp21->config |= lnbp21->override_or; ++ lnbp21->config &= lnbp21->override_and; ++ ++ return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int lnbp21_set_tone(struct dvb_frontend *fe, ++ fe_sec_tone_mode_t tone) ++{ ++ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; ++ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, ++ .buf = &lnbp21->config, ++ .len = sizeof(lnbp21->config) }; ++ ++ switch (tone) { ++ case SEC_TONE_OFF: ++ lnbp21->config &= ~LNBP21_TEN; ++ break; ++ case SEC_TONE_ON: ++ lnbp21->config |= LNBP21_TEN; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ lnbp21->config |= lnbp21->override_or; ++ lnbp21->config &= lnbp21->override_and; ++ ++ return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static void lnbp21_release(struct dvb_frontend *fe) ++{ ++ /* LNBP power off */ ++ lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ /* free data */ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear, u8 i2c_addr, u8 config) ++{ ++ struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); ++ if (!lnbp21) ++ return NULL; ++ ++ /* default configuration */ ++ lnbp21->config = config; ++ lnbp21->i2c = i2c; ++ lnbp21->i2c_addr = i2c_addr; ++ fe->sec_priv = lnbp21; ++ ++ /* bits which should be forced to '1' */ ++ lnbp21->override_or = override_set; ++ ++ /* bits which should be forced to '0' */ ++ lnbp21->override_and = ~override_clear; ++ ++ /* detect if it is present or not */ ++ if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { ++ kfree(lnbp21); ++ return NULL; ++ } ++ ++ /* install release callback */ ++ fe->ops.release_sec = lnbp21_release; ++ ++ /* override frontend ops */ ++ fe->ops.set_voltage = lnbp21_set_voltage; ++ fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; ++ if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/ ++ fe->ops.set_tone = lnbp21_set_tone; ++ printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); ++ ++ return fe; ++} ++ ++struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear, u8 i2c_addr) ++{ ++ return lnbx2x_attach(fe, i2c, override_set, override_clear, ++ i2c_addr, LNBH24_TTX); ++} ++EXPORT_SYMBOL(lnbh24_attach); ++ ++struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear) ++{ ++ return lnbx2x_attach(fe, i2c, override_set, override_clear, ++ 0x08, LNBP21_ISEL); ++} ++EXPORT_SYMBOL(lnbp21_attach); ++ ++MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24"); ++MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/lnbp21.h b/drivers/media/dvb-frontends/lnbp21.h +new file mode 100644 +index 0000000..fcdf1c6 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lnbp21.h +@@ -0,0 +1,75 @@ ++/* ++ * lnbp21.h - driver for lnb supply and control ic lnbp21 ++ * ++ * Copyright (C) 2006 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef _LNBP21_H ++#define _LNBP21_H ++ ++/* system register bits */ ++/* [RO] 0=OK; 1=over current limit flag */ ++#define LNBP21_OLF 0x01 ++/* [RO] 0=OK; 1=over temperature flag (150 C) */ ++#define LNBP21_OTF 0x02 ++/* [RW] 0=disable LNB power, enable loopthrough ++ 1=enable LNB power, disable loopthrough */ ++#define LNBP21_EN 0x04 ++/* [RW] 0=low voltage (13/14V, vert pol) ++ 1=high voltage (18/19V,horiz pol) */ ++#define LNBP21_VSEL 0x08 ++/* [RW] increase LNB voltage by 1V: ++ 0=13/18V; 1=14/19V */ ++#define LNBP21_LLC 0x10 ++/* [RW] 0=tone controlled by DSQIN pin ++ 1=tone enable, disable DSQIN */ ++#define LNBP21_TEN 0x20 ++/* [RW] current limit select: ++ 0:Iout=500-650mA Isc=300mA ++ 1:Iout=400-550mA Isc=200mA */ ++#define LNBP21_ISEL 0x40 ++/* [RW] short-circuit protect: ++ 0=pulsed (dynamic) curr limiting ++ 1=static curr limiting */ ++#define LNBP21_PCL 0x80 ++ ++#include ++ ++#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ ++ && defined(MODULE)) ++/* override_set and override_clear control which ++ system register bits (above) to always set & clear */ ++extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear); ++#else ++static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 override_set, ++ u8 override_clear) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c +new file mode 100644 +index 0000000..c463da7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/lnbp22.c +@@ -0,0 +1,148 @@ ++/* ++ * lnbp22.h - driver for lnb supply and control ic lnbp22 ++ * ++ * Copyright (C) 2006 Dominik Kuhlen ++ * Based on lnbp21 driver ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "lnbp22.h" ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); ++ ++ ++#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg) ++ ++struct lnbp22 { ++ u8 config[4]; ++ struct i2c_adapter *i2c; ++}; ++ ++static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; ++ struct i2c_msg msg = { ++ .addr = 0x08, ++ .flags = 0, ++ .buf = (char *)&lnbp22->config, ++ .len = sizeof(lnbp22->config), ++ }; ++ ++ dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage, ++ SEC_VOLTAGE_18, SEC_VOLTAGE_13); ++ ++ lnbp22->config[3] = 0x60; /* Power down */ ++ switch (voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ lnbp22->config[3] |= LNBP22_EN; ++ break; ++ case SEC_VOLTAGE_18: ++ lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); ++ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) ++{ ++ struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv; ++ struct i2c_msg msg = { ++ .addr = 0x08, ++ .flags = 0, ++ .buf = (char *)&lnbp22->config, ++ .len = sizeof(lnbp22->config), ++ }; ++ ++ dprintk(1, "%s: %d\n", __func__, (int)arg); ++ if (arg) ++ lnbp22->config[3] |= LNBP22_LLC; ++ else ++ lnbp22->config[3] &= ~LNBP22_LLC; ++ ++ return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static void lnbp22_release(struct dvb_frontend *fe) ++{ ++ dprintk(1, "%s\n", __func__); ++ /* LNBP power off */ ++ lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF); ++ ++ /* free data */ ++ kfree(fe->sec_priv); ++ fe->sec_priv = NULL; ++} ++ ++struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c) ++{ ++ struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL); ++ if (!lnbp22) ++ return NULL; ++ ++ /* default configuration */ ++ lnbp22->config[0] = 0x00; /* ? */ ++ lnbp22->config[1] = 0x28; /* ? */ ++ lnbp22->config[2] = 0x48; /* ? */ ++ lnbp22->config[3] = 0x60; /* Power down */ ++ lnbp22->i2c = i2c; ++ fe->sec_priv = lnbp22; ++ ++ /* detect if it is present or not */ ++ if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) { ++ dprintk(0, "%s LNBP22 not found\n", __func__); ++ kfree(lnbp22); ++ fe->sec_priv = NULL; ++ return NULL; ++ } ++ ++ /* install release callback */ ++ fe->ops.release_sec = lnbp22_release; ++ ++ /* override frontend ops */ ++ fe->ops.set_voltage = lnbp22_set_voltage; ++ fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage; ++ ++ return fe; ++} ++EXPORT_SYMBOL(lnbp22_attach); ++ ++MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); ++MODULE_AUTHOR("Dominik Kuhlen"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/lnbp22.h b/drivers/media/dvb-frontends/lnbp22.h +new file mode 100644 +index 0000000..63e2dec +--- /dev/null ++++ b/drivers/media/dvb-frontends/lnbp22.h +@@ -0,0 +1,57 @@ ++/* ++ * lnbp22.h - driver for lnb supply and control ic lnbp22 ++ * ++ * Copyright (C) 2006 Dominik Kuhlen ++ * Based on lnbp21.h ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef _LNBP22_H ++#define _LNBP22_H ++ ++/* Enable */ ++#define LNBP22_EN 0x10 ++/* Voltage selection */ ++#define LNBP22_VSEL 0x02 ++/* Plus 1 Volt Bit */ ++#define LNBP22_LLC 0x01 ++ ++#include ++ ++#if defined(CONFIG_DVB_LNBP22) || \ ++ (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE)) ++/* ++ * override_set and override_clear control which system register bits (above) ++ * to always set & clear ++ */ ++extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_LNBP22 */ ++ ++#endif /* _LNBP22_H */ +diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c +new file mode 100644 +index 0000000..4da5272 +--- /dev/null ++++ b/drivers/media/dvb-frontends/m88rs2000.c +@@ -0,0 +1,745 @@ ++/* ++ Driver for M88RS2000 demodulator and tuner ++ ++ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com) ++ Beta Driver ++ ++ Include various calculation code from DS3000 driver. ++ Copyright (C) 2009 Konstantin Dimitrov. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include "dvb_frontend.h" ++#include "m88rs2000.h" ++ ++struct m88rs2000_state { ++ struct i2c_adapter *i2c; ++ const struct m88rs2000_config *config; ++ struct dvb_frontend frontend; ++ u8 no_lock_count; ++ u32 tuner_frequency; ++ u32 symbol_rate; ++ fe_code_rate_t fec_inner; ++ u8 tuner_level; ++ int errmode; ++}; ++ ++static int m88rs2000_debug; ++ ++module_param_named(debug, m88rs2000_debug, int, 0644); ++MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); ++ ++#define dprintk(level, args...) do { \ ++ if (level & m88rs2000_debug) \ ++ printk(KERN_DEBUG "m88rs2000-fe: " args); \ ++} while (0) ++ ++#define deb_info(args...) dprintk(0x01, args) ++#define info(format, arg...) \ ++ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg) ++ ++static int m88rs2000_writereg(struct m88rs2000_state *state, ++ u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_addr, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_addr, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_addr, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n", ++ __func__, reg, ret); ++ ++ return b1[0]; ++} ++ ++static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ int ret; ++ u32 temp; ++ u8 b[3]; ++ ++ if ((srate < 1000000) || (srate > 45000000)) ++ return -EINVAL; ++ ++ temp = srate / 1000; ++ temp *= 11831; ++ temp /= 68; ++ temp -= 3; ++ ++ b[0] = (u8) (temp >> 16) & 0xff; ++ b[1] = (u8) (temp >> 8) & 0xff; ++ b[2] = (u8) temp & 0xff; ++ ret = m88rs2000_writereg(state, 0x93, b[2]); ++ ret |= m88rs2000_writereg(state, 0x94, b[1]); ++ ret |= m88rs2000_writereg(state, 0x95, b[0]); ++ ++ deb_info("m88rs2000: m88rs2000_set_symbolrate\n"); ++ return ret; ++} ++ ++static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *m) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ ++ int i; ++ u8 reg; ++ deb_info("%s\n", __func__); ++ m88rs2000_writereg(state, 0x9a, 0x30); ++ reg = m88rs2000_readreg(state, 0xb2); ++ reg &= 0x3f; ++ m88rs2000_writereg(state, 0xb2, reg); ++ for (i = 0; i < m->msg_len; i++) ++ m88rs2000_writereg(state, 0xb3 + i, m->msg[i]); ++ ++ reg = m88rs2000_readreg(state, 0xb1); ++ reg &= 0x87; ++ reg |= ((m->msg_len - 1) << 3) | 0x07; ++ reg &= 0x7f; ++ m88rs2000_writereg(state, 0xb1, reg); ++ ++ for (i = 0; i < 15; i++) { ++ if ((m88rs2000_readreg(state, 0xb1) & 0x40) == 0x0) ++ break; ++ msleep(20); ++ } ++ ++ reg = m88rs2000_readreg(state, 0xb1); ++ if ((reg & 0x40) > 0x0) { ++ reg &= 0x7f; ++ reg |= 0x40; ++ m88rs2000_writereg(state, 0xb1, reg); ++ } ++ ++ reg = m88rs2000_readreg(state, 0xb2); ++ reg &= 0x3f; ++ reg |= 0x80; ++ m88rs2000_writereg(state, 0xb2, reg); ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ ++ ++ return 0; ++} ++ ++static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 reg0, reg1; ++ deb_info("%s\n", __func__); ++ m88rs2000_writereg(state, 0x9a, 0x30); ++ msleep(50); ++ reg0 = m88rs2000_readreg(state, 0xb1); ++ reg1 = m88rs2000_readreg(state, 0xb2); ++ /* TODO complete this section */ ++ m88rs2000_writereg(state, 0xb2, reg1); ++ m88rs2000_writereg(state, 0xb1, reg0); ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ ++ return 0; ++} ++ ++static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 reg0, reg1; ++ m88rs2000_writereg(state, 0x9a, 0x30); ++ reg0 = m88rs2000_readreg(state, 0xb1); ++ reg1 = m88rs2000_readreg(state, 0xb2); ++ ++ reg1 &= 0x3f; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ reg0 |= 0x4; ++ reg0 &= 0xbc; ++ break; ++ case SEC_TONE_OFF: ++ reg1 |= 0x80; ++ break; ++ default: ++ break; ++ } ++ m88rs2000_writereg(state, 0xb2, reg1); ++ m88rs2000_writereg(state, 0xb1, reg0); ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ return 0; ++} ++ ++struct inittab { ++ u8 cmd; ++ u8 reg; ++ u8 val; ++}; ++ ++struct inittab m88rs2000_setup[] = { ++ {DEMOD_WRITE, 0x9a, 0x30}, ++ {DEMOD_WRITE, 0x00, 0x01}, ++ {WRITE_DELAY, 0x19, 0x00}, ++ {DEMOD_WRITE, 0x00, 0x00}, ++ {DEMOD_WRITE, 0x9a, 0xb0}, ++ {DEMOD_WRITE, 0x81, 0xc1}, ++ {DEMOD_WRITE, 0x81, 0x81}, ++ {DEMOD_WRITE, 0x86, 0xc6}, ++ {DEMOD_WRITE, 0x9a, 0x30}, ++ {DEMOD_WRITE, 0xf0, 0x22}, ++ {DEMOD_WRITE, 0xf1, 0xbf}, ++ {DEMOD_WRITE, 0xb0, 0x45}, ++ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/ ++ {DEMOD_WRITE, 0x9a, 0xb0}, ++ {0xff, 0xaa, 0xff} ++}; ++ ++struct inittab m88rs2000_shutdown[] = { ++ {DEMOD_WRITE, 0x9a, 0x30}, ++ {DEMOD_WRITE, 0xb0, 0x00}, ++ {DEMOD_WRITE, 0xf1, 0x89}, ++ {DEMOD_WRITE, 0x00, 0x01}, ++ {DEMOD_WRITE, 0x9a, 0xb0}, ++ {DEMOD_WRITE, 0x81, 0x81}, ++ {0xff, 0xaa, 0xff} ++}; ++ ++struct inittab fe_reset[] = { ++ {DEMOD_WRITE, 0x00, 0x01}, ++ {DEMOD_WRITE, 0xf1, 0xbf}, ++ {DEMOD_WRITE, 0x00, 0x01}, ++ {DEMOD_WRITE, 0x20, 0x81}, ++ {DEMOD_WRITE, 0x21, 0x80}, ++ {DEMOD_WRITE, 0x10, 0x33}, ++ {DEMOD_WRITE, 0x11, 0x44}, ++ {DEMOD_WRITE, 0x12, 0x07}, ++ {DEMOD_WRITE, 0x18, 0x20}, ++ {DEMOD_WRITE, 0x28, 0x04}, ++ {DEMOD_WRITE, 0x29, 0x8e}, ++ {DEMOD_WRITE, 0x3b, 0xff}, ++ {DEMOD_WRITE, 0x32, 0x10}, ++ {DEMOD_WRITE, 0x33, 0x02}, ++ {DEMOD_WRITE, 0x34, 0x30}, ++ {DEMOD_WRITE, 0x35, 0xff}, ++ {DEMOD_WRITE, 0x38, 0x50}, ++ {DEMOD_WRITE, 0x39, 0x68}, ++ {DEMOD_WRITE, 0x3c, 0x7f}, ++ {DEMOD_WRITE, 0x3d, 0x0f}, ++ {DEMOD_WRITE, 0x45, 0x20}, ++ {DEMOD_WRITE, 0x46, 0x24}, ++ {DEMOD_WRITE, 0x47, 0x7c}, ++ {DEMOD_WRITE, 0x48, 0x16}, ++ {DEMOD_WRITE, 0x49, 0x04}, ++ {DEMOD_WRITE, 0x4a, 0x01}, ++ {DEMOD_WRITE, 0x4b, 0x78}, ++ {DEMOD_WRITE, 0X4d, 0xd2}, ++ {DEMOD_WRITE, 0x4e, 0x6d}, ++ {DEMOD_WRITE, 0x50, 0x30}, ++ {DEMOD_WRITE, 0x51, 0x30}, ++ {DEMOD_WRITE, 0x54, 0x7b}, ++ {DEMOD_WRITE, 0x56, 0x09}, ++ {DEMOD_WRITE, 0x58, 0x59}, ++ {DEMOD_WRITE, 0x59, 0x37}, ++ {DEMOD_WRITE, 0x63, 0xfa}, ++ {0xff, 0xaa, 0xff} ++}; ++ ++struct inittab fe_trigger[] = { ++ {DEMOD_WRITE, 0x97, 0x04}, ++ {DEMOD_WRITE, 0x99, 0x77}, ++ {DEMOD_WRITE, 0x9b, 0x64}, ++ {DEMOD_WRITE, 0x9e, 0x00}, ++ {DEMOD_WRITE, 0x9f, 0xf8}, ++ {DEMOD_WRITE, 0xa0, 0x20}, ++ {DEMOD_WRITE, 0xa1, 0xe0}, ++ {DEMOD_WRITE, 0xa3, 0x38}, ++ {DEMOD_WRITE, 0x98, 0xff}, ++ {DEMOD_WRITE, 0xc0, 0x0f}, ++ {DEMOD_WRITE, 0x89, 0x01}, ++ {DEMOD_WRITE, 0x00, 0x00}, ++ {WRITE_DELAY, 0x0a, 0x00}, ++ {DEMOD_WRITE, 0x00, 0x01}, ++ {DEMOD_WRITE, 0x00, 0x00}, ++ {DEMOD_WRITE, 0x9a, 0xb0}, ++ {0xff, 0xaa, 0xff} ++}; ++ ++static int m88rs2000_tab_set(struct m88rs2000_state *state, ++ struct inittab *tab) ++{ ++ int ret = 0; ++ u8 i; ++ if (tab == NULL) ++ return -EINVAL; ++ ++ for (i = 0; i < 255; i++) { ++ switch (tab[i].cmd) { ++ case 0x01: ++ ret = m88rs2000_writereg(state, tab[i].reg, ++ tab[i].val); ++ break; ++ case 0x10: ++ if (tab[i].reg > 0) ++ mdelay(tab[i].reg); ++ break; ++ case 0xff: ++ if (tab[i].reg == 0xaa && tab[i].val == 0xff) ++ return 0; ++ case 0x00: ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret < 0) ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 data; ++ ++ data = m88rs2000_readreg(state, 0xb2); ++ data |= 0x03; /* bit0 V/H, bit1 off/on */ ++ ++ switch (volt) { ++ case SEC_VOLTAGE_18: ++ data &= ~0x03; ++ break; ++ case SEC_VOLTAGE_13: ++ data &= ~0x03; ++ data |= 0x01; ++ break; ++ case SEC_VOLTAGE_OFF: ++ break; ++ } ++ ++ m88rs2000_writereg(state, 0xb2, data); ++ ++ return 0; ++} ++ ++static int m88rs2000_init(struct dvb_frontend *fe) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ int ret; ++ ++ deb_info("m88rs2000: init chip\n"); ++ /* Setup frontend from shutdown/cold */ ++ if (state->config->inittab) ++ ret = m88rs2000_tab_set(state, ++ (struct inittab *)state->config->inittab); ++ else ++ ret = m88rs2000_tab_set(state, m88rs2000_setup); ++ ++ return ret; ++} ++ ++static int m88rs2000_sleep(struct dvb_frontend *fe) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ int ret; ++ /* Shutdown the frondend */ ++ ret = m88rs2000_tab_set(state, m88rs2000_shutdown); ++ return ret; ++} ++ ++static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 reg = m88rs2000_readreg(state, 0x8c); ++ ++ *status = 0; ++ ++ if ((reg & 0x7) == 0x7) { ++ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI ++ | FE_HAS_SYNC | FE_HAS_LOCK; ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, CALL_IS_READ); ++ } ++ return 0; ++} ++ ++static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 tmp0, tmp1; ++ ++ m88rs2000_writereg(state, 0x9a, 0x30); ++ tmp0 = m88rs2000_readreg(state, 0xd8); ++ if ((tmp0 & 0x10) != 0) { ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ *ber = 0xffffffff; ++ return 0; ++ } ++ ++ *ber = (m88rs2000_readreg(state, 0xd7) << 8) | ++ m88rs2000_readreg(state, 0xd6); ++ ++ tmp1 = m88rs2000_readreg(state, 0xd9); ++ m88rs2000_writereg(state, 0xd9, (tmp1 & ~7) | 4); ++ /* needs twice */ ++ m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); ++ m88rs2000_writereg(state, 0xd8, (tmp0 & ~8) | 0x30); ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ ++ return 0; ++} ++ ++static int m88rs2000_read_signal_strength(struct dvb_frontend *fe, ++ u16 *strength) ++{ ++ if (fe->ops.tuner_ops.get_rf_strength) ++ fe->ops.tuner_ops.get_rf_strength(fe, strength); ++ ++ return 0; ++} ++ ++static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ ++ *snr = 512 * m88rs2000_readreg(state, 0x65); ++ ++ return 0; ++} ++ ++static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ u8 tmp; ++ ++ *ucblocks = (m88rs2000_readreg(state, 0xd5) << 8) | ++ m88rs2000_readreg(state, 0xd4); ++ tmp = m88rs2000_readreg(state, 0xd8); ++ m88rs2000_writereg(state, 0xd8, tmp & ~0x20); ++ /* needs two times */ ++ m88rs2000_writereg(state, 0xd8, tmp | 0x20); ++ m88rs2000_writereg(state, 0xd8, tmp | 0x20); ++ ++ return 0; ++} ++ ++static int m88rs2000_set_fec(struct m88rs2000_state *state, ++ fe_code_rate_t fec) ++{ ++ u16 fec_set; ++ switch (fec) { ++ /* This is not confirmed kept for reference */ ++/* case FEC_1_2: ++ fec_set = 0x88; ++ break; ++ case FEC_2_3: ++ fec_set = 0x68; ++ break; ++ case FEC_3_4: ++ fec_set = 0x48; ++ break; ++ case FEC_5_6: ++ fec_set = 0x28; ++ break; ++ case FEC_7_8: ++ fec_set = 0x18; ++ break; */ ++ case FEC_AUTO: ++ default: ++ fec_set = 0x08; ++ } ++ m88rs2000_writereg(state, 0x76, fec_set); ++ ++ return 0; ++} ++ ++ ++static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state) ++{ ++ u8 reg; ++ m88rs2000_writereg(state, 0x9a, 0x30); ++ reg = m88rs2000_readreg(state, 0x76); ++ m88rs2000_writereg(state, 0x9a, 0xb0); ++ ++ switch (reg) { ++ case 0x88: ++ return FEC_1_2; ++ case 0x68: ++ return FEC_2_3; ++ case 0x48: ++ return FEC_3_4; ++ case 0x28: ++ return FEC_5_6; ++ case 0x18: ++ return FEC_7_8; ++ case 0x08: ++ default: ++ break; ++ } ++ ++ return FEC_AUTO; ++} ++ ++static int m88rs2000_set_frontend(struct dvb_frontend *fe) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ fe_status_t status; ++ int i, ret = 0; ++ s32 tmp; ++ u32 tuner_freq; ++ u16 offset = 0; ++ u8 reg; ++ ++ state->no_lock_count = 0; ++ ++ if (c->delivery_system != SYS_DVBS) { ++ deb_info("%s: unsupported delivery " ++ "system selected (%d)\n", ++ __func__, c->delivery_system); ++ return -EOPNOTSUPP; ++ } ++ ++ /* Set Tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ ret = fe->ops.tuner_ops.set_params(fe); ++ ++ if (ret < 0) ++ return -ENODEV; ++ ++ if (fe->ops.tuner_ops.get_frequency) ++ ret = fe->ops.tuner_ops.get_frequency(fe, &tuner_freq); ++ ++ if (ret < 0) ++ return -ENODEV; ++ ++ offset = tuner_freq - c->frequency; ++ ++ /* calculate offset assuming 96000kHz*/ ++ tmp = offset; ++ tmp *= 65536; ++ ++ tmp = (2 * tmp + 96000) / (2 * 96000); ++ if (tmp < 0) ++ tmp += 65536; ++ ++ offset = tmp & 0xffff; ++ ++ ret = m88rs2000_writereg(state, 0x9a, 0x30); ++ /* Unknown usually 0xc6 sometimes 0xc1 */ ++ reg = m88rs2000_readreg(state, 0x86); ++ ret |= m88rs2000_writereg(state, 0x86, reg); ++ /* Offset lower nibble always 0 */ ++ ret |= m88rs2000_writereg(state, 0x9c, (offset >> 8)); ++ ret |= m88rs2000_writereg(state, 0x9d, offset & 0xf0); ++ ++ ++ /* Reset Demod */ ++ ret = m88rs2000_tab_set(state, fe_reset); ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Unknown */ ++ reg = m88rs2000_readreg(state, 0x70); ++ ret = m88rs2000_writereg(state, 0x70, reg); ++ ++ /* Set FEC */ ++ ret |= m88rs2000_set_fec(state, c->fec_inner); ++ ret |= m88rs2000_writereg(state, 0x85, 0x1); ++ ret |= m88rs2000_writereg(state, 0x8a, 0xbf); ++ ret |= m88rs2000_writereg(state, 0x8d, 0x1e); ++ ret |= m88rs2000_writereg(state, 0x90, 0xf1); ++ ret |= m88rs2000_writereg(state, 0x91, 0x08); ++ ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Set Symbol Rate */ ++ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate); ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Set up Demod */ ++ ret = m88rs2000_tab_set(state, fe_trigger); ++ if (ret < 0) ++ return -ENODEV; ++ ++ for (i = 0; i < 25; i++) { ++ reg = m88rs2000_readreg(state, 0x8c); ++ if ((reg & 0x7) == 0x7) { ++ status = FE_HAS_LOCK; ++ break; ++ } ++ state->no_lock_count++; ++ if (state->no_lock_count == 15) { ++ reg = m88rs2000_readreg(state, 0x70); ++ reg ^= 0x4; ++ m88rs2000_writereg(state, 0x70, reg); ++ state->no_lock_count = 0; ++ } ++ msleep(20); ++ } ++ ++ if (status & FE_HAS_LOCK) { ++ state->fec_inner = m88rs2000_get_fec(state); ++ /* Uknown suspect SNR level */ ++ reg = m88rs2000_readreg(state, 0x65); ++ } ++ ++ state->tuner_frequency = c->frequency; ++ state->symbol_rate = c->symbol_rate; ++ return 0; ++} ++ ++static int m88rs2000_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ c->fec_inner = state->fec_inner; ++ c->frequency = state->tuner_frequency; ++ c->symbol_rate = state->symbol_rate; ++ return 0; ++} ++ ++static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ ++ if (enable) ++ m88rs2000_writereg(state, 0x81, 0x84); ++ else ++ m88rs2000_writereg(state, 0x81, 0x81); ++ udelay(10); ++ return 0; ++} ++ ++static void m88rs2000_release(struct dvb_frontend *fe) ++{ ++ struct m88rs2000_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops m88rs2000_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "M88RS2000 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1000, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, /* ppm */ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_QPSK | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = m88rs2000_release, ++ .init = m88rs2000_init, ++ .sleep = m88rs2000_sleep, ++ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl, ++ .read_status = m88rs2000_read_status, ++ .read_ber = m88rs2000_read_ber, ++ .read_signal_strength = m88rs2000_read_signal_strength, ++ .read_snr = m88rs2000_read_snr, ++ .read_ucblocks = m88rs2000_read_ucblocks, ++ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg, ++ .diseqc_send_burst = m88rs2000_send_diseqc_burst, ++ .set_tone = m88rs2000_set_tone, ++ .set_voltage = m88rs2000_set_voltage, ++ ++ .set_frontend = m88rs2000_set_frontend, ++ .get_frontend = m88rs2000_get_frontend, ++}; ++ ++struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct m88rs2000_state *state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->tuner_frequency = 0; ++ state->symbol_rate = 0; ++ state->fec_inner = 0; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &m88rs2000_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(m88rs2000_attach); ++ ++MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); ++MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("1.13"); ++ +diff --git a/drivers/media/dvb-frontends/m88rs2000.h b/drivers/media/dvb-frontends/m88rs2000.h +new file mode 100644 +index 0000000..5a8023e +--- /dev/null ++++ b/drivers/media/dvb-frontends/m88rs2000.h +@@ -0,0 +1,60 @@ ++/* ++ Driver for M88RS2000 demodulator ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef M88RS2000_H ++#define M88RS2000_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct m88rs2000_config { ++ /* Demodulator i2c address */ ++ u8 demod_addr; ++ ++ u8 *inittab; ++ ++ /* minimum delay before retuning */ ++ int min_delay_ms; ++ ++ int (*set_ts_params)(struct dvb_frontend *, int); ++}; ++ ++enum { ++ CALL_IS_SET_FRONTEND = 0x0, ++ CALL_IS_READ, ++}; ++ ++#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \ ++ defined(MODULE)) ++extern struct dvb_frontend *m88rs2000_attach( ++ const struct m88rs2000_config *config, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *m88rs2000_attach( ++ const struct m88rs2000_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_M88RS2000 */ ++ ++enum { ++ DEMOD_WRITE = 0x1, ++ WRITE_DELAY = 0x10, ++}; ++#endif /* M88RS2000_H */ +diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c +new file mode 100644 +index 0000000..9ae40ab +--- /dev/null ++++ b/drivers/media/dvb-frontends/mb86a16.c +@@ -0,0 +1,1878 @@ ++/* ++ Fujitsu MB86A16 DVB-S/DSS DC Receiver driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "mb86a16.h" ++#include "mb86a16_priv.h" ++ ++unsigned int verbose = 5; ++module_param(verbose, int, 0644); ++ ++#define ABS(x) ((x) < 0 ? (-x) : (x)) ++ ++struct mb86a16_state { ++ struct i2c_adapter *i2c_adap; ++ const struct mb86a16_config *config; ++ struct dvb_frontend frontend; ++ ++ /* tuning parameters */ ++ int frequency; ++ int srate; ++ ++ /* Internal stuff */ ++ int master_clk; ++ int deci; ++ int csel; ++ int rsel; ++}; ++ ++#define MB86A16_ERROR 0 ++#define MB86A16_NOTICE 1 ++#define MB86A16_INFO 2 ++#define MB86A16_DEBUG 3 ++ ++#define dprintk(x, y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((x > MB86A16_ERROR) && (x > y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__, ##arg); \ ++ else if ((x > MB86A16_NOTICE) && (x > y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__, ##arg); \ ++ else if ((x > MB86A16_INFO) && (x > y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__, ##arg); \ ++ else if ((x > MB86A16_DEBUG) && (x > y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__, ##arg); \ ++ } else { \ ++ if (x > y) \ ++ printk(format, ##arg); \ ++ } \ ++} while (0) ++ ++#define TRACE_IN dprintk(verbose, MB86A16_DEBUG, 1, "-->()") ++#define TRACE_OUT dprintk(verbose, MB86A16_DEBUG, 1, "()-->") ++ ++static int mb86a16_write(struct mb86a16_state *state, u8 reg, u8 val) ++{ ++ int ret; ++ u8 buf[] = { reg, val }; ++ ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ dprintk(verbose, MB86A16_DEBUG, 1, ++ "writing to [0x%02x],Reg[0x%02x],Data[0x%02x]", ++ state->config->demod_address, buf[0], buf[1]); ++ ++ ret = i2c_transfer(state->i2c_adap, &msg, 1); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ret = i2c_transfer(state->i2c_adap, msg, 2); ++ if (ret != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=0x%i)", ++ reg, ret); ++ ++ return -EREMOTEIO; ++ } ++ *val = b1[0]; ++ ++ return ret; ++} ++ ++static int CNTM_set(struct mb86a16_state *state, ++ unsigned char timint1, ++ unsigned char timint2, ++ unsigned char cnext) ++{ ++ unsigned char val; ++ ++ val = (timint1 << 4) | (timint2 << 2) | cnext; ++ if (mb86a16_write(state, MB86A16_CNTMR, val) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int smrt_set(struct mb86a16_state *state, int rate) ++{ ++ int tmp ; ++ int m ; ++ unsigned char STOFS0, STOFS1; ++ ++ m = 1 << state->deci; ++ tmp = (8192 * state->master_clk - 2 * m * rate * 8192 + state->master_clk / 2) / state->master_clk; ++ ++ STOFS0 = tmp & 0x0ff; ++ STOFS1 = (tmp & 0xf00) >> 8; ++ ++ if (mb86a16_write(state, MB86A16_SRATE1, (state->deci << 2) | ++ (state->csel << 1) | ++ state->rsel) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_SRATE2, STOFS0) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_SRATE3, STOFS1) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -1; ++} ++ ++static int srst(struct mb86a16_state *state) ++{ ++ if (mb86a16_write(state, MB86A16_RESET, 0x04) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ ++} ++ ++static int afcex_data_set(struct mb86a16_state *state, ++ unsigned char AFCEX_L, ++ unsigned char AFCEX_H) ++{ ++ if (mb86a16_write(state, MB86A16_AFCEXL, AFCEX_L) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_AFCEXH, AFCEX_H) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ ++ return -1; ++} ++ ++static int afcofs_data_set(struct mb86a16_state *state, ++ unsigned char AFCEX_L, ++ unsigned char AFCEX_H) ++{ ++ if (mb86a16_write(state, 0x58, AFCEX_L) < 0) ++ goto err; ++ if (mb86a16_write(state, 0x59, AFCEX_H) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int stlp_set(struct mb86a16_state *state, ++ unsigned char STRAS, ++ unsigned char STRBS) ++{ ++ if (mb86a16_write(state, MB86A16_STRFILTCOEF1, (STRBS << 3) | (STRAS)) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int Vi_set(struct mb86a16_state *state, unsigned char ETH, unsigned char VIA) ++{ ++ if (mb86a16_write(state, MB86A16_VISET2, 0x04) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_VISET3, 0xf5) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int initial_set(struct mb86a16_state *state) ++{ ++ if (stlp_set(state, 5, 7)) ++ goto err; ++ ++ udelay(100); ++ if (afcex_data_set(state, 0, 0)) ++ goto err; ++ ++ udelay(100); ++ if (afcofs_data_set(state, 0, 0)) ++ goto err; ++ ++ udelay(100); ++ if (mb86a16_write(state, MB86A16_CRLFILTCOEF1, 0x16) < 0) ++ goto err; ++ if (mb86a16_write(state, 0x2f, 0x21) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_VIMAG, 0x38) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_FAGCS1, 0x00) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_FAGCS2, 0x1c) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_FAGCS3, 0x20) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_FAGCS4, 0x1e) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_FAGCS5, 0x23) < 0) ++ goto err; ++ if (mb86a16_write(state, 0x54, 0xff) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_TSOUT, 0x00) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int S01T_set(struct mb86a16_state *state, ++ unsigned char s1t, ++ unsigned s0t) ++{ ++ if (mb86a16_write(state, 0x33, (s1t << 3) | s0t) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++ ++static int EN_set(struct mb86a16_state *state, ++ int cren, ++ int afcen) ++{ ++ unsigned char val; ++ ++ val = 0x7a | (cren << 7) | (afcen << 2); ++ if (mb86a16_write(state, 0x49, val) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int AFCEXEN_set(struct mb86a16_state *state, ++ int afcexen, ++ int smrt) ++{ ++ unsigned char AFCA ; ++ ++ if (smrt > 18875) ++ AFCA = 4; ++ else if (smrt > 9375) ++ AFCA = 3; ++ else if (smrt > 2250) ++ AFCA = 2; ++ else ++ AFCA = 1; ++ ++ if (mb86a16_write(state, 0x2a, 0x02 | (afcexen << 5) | (AFCA << 2)) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int DAGC_data_set(struct mb86a16_state *state, ++ unsigned char DAGCA, ++ unsigned char DAGCW) ++{ ++ if (mb86a16_write(state, 0x2d, (DAGCA << 3) | DAGCW) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static void smrt_info_get(struct mb86a16_state *state, int rate) ++{ ++ if (rate >= 37501) { ++ state->deci = 0; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 30001) { ++ state->deci = 0; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 26251) { ++ state->deci = 0; state->csel = 1; state->rsel = 0; ++ } else if (rate >= 22501) { ++ state->deci = 0; state->csel = 1; state->rsel = 1; ++ } else if (rate >= 18751) { ++ state->deci = 1; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 15001) { ++ state->deci = 1; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 13126) { ++ state->deci = 1; state->csel = 1; state->rsel = 0; ++ } else if (rate >= 11251) { ++ state->deci = 1; state->csel = 1; state->rsel = 1; ++ } else if (rate >= 9376) { ++ state->deci = 2; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 7501) { ++ state->deci = 2; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 6563) { ++ state->deci = 2; state->csel = 1; state->rsel = 0; ++ } else if (rate >= 5626) { ++ state->deci = 2; state->csel = 1; state->rsel = 1; ++ } else if (rate >= 4688) { ++ state->deci = 3; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 3751) { ++ state->deci = 3; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 3282) { ++ state->deci = 3; state->csel = 1; state->rsel = 0; ++ } else if (rate >= 2814) { ++ state->deci = 3; state->csel = 1; state->rsel = 1; ++ } else if (rate >= 2344) { ++ state->deci = 4; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 1876) { ++ state->deci = 4; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 1641) { ++ state->deci = 4; state->csel = 1; state->rsel = 0; ++ } else if (rate >= 1407) { ++ state->deci = 4; state->csel = 1; state->rsel = 1; ++ } else if (rate >= 1172) { ++ state->deci = 5; state->csel = 0; state->rsel = 0; ++ } else if (rate >= 939) { ++ state->deci = 5; state->csel = 0; state->rsel = 1; ++ } else if (rate >= 821) { ++ state->deci = 5; state->csel = 1; state->rsel = 0; ++ } else { ++ state->deci = 5; state->csel = 1; state->rsel = 1; ++ } ++ ++ if (state->csel == 0) ++ state->master_clk = 92000; ++ else ++ state->master_clk = 61333; ++ ++} ++ ++static int signal_det(struct mb86a16_state *state, ++ int smrt, ++ unsigned char *SIG) ++{ ++ ++ int ret ; ++ int smrtd ; ++ int wait_sym ; ++ ++ u32 wait_t; ++ unsigned char S[3] ; ++ int i ; ++ ++ if (*SIG > 45) { ++ if (CNTM_set(state, 2, 1, 2) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); ++ return -1; ++ } ++ wait_sym = 40000; ++ } else { ++ if (CNTM_set(state, 3, 1, 2) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); ++ return -1; ++ } ++ wait_sym = 80000; ++ } ++ for (i = 0; i < 3; i++) { ++ if (i == 0) ++ smrtd = smrt * 98 / 100; ++ else if (i == 1) ++ smrtd = smrt; ++ else ++ smrtd = smrt * 102 / 100; ++ smrt_info_get(state, smrtd); ++ smrt_set(state, smrtd); ++ srst(state); ++ wait_t = (wait_sym + 99 * smrtd / 100) / smrtd; ++ if (wait_t == 0) ++ wait_t = 1; ++ msleep_interruptible(10); ++ if (mb86a16_read(state, 0x37, &(S[i])) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ } ++ if ((S[1] > S[0] * 112 / 100) && ++ (S[1] > S[2] * 112 / 100)) { ++ ++ ret = 1; ++ } else { ++ ret = 0; ++ } ++ *SIG = S[1]; ++ ++ if (CNTM_set(state, 0, 1, 2) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static int rf_val_set(struct mb86a16_state *state, ++ int f, ++ int smrt, ++ unsigned char R) ++{ ++ unsigned char C, F, B; ++ int M; ++ unsigned char rf_val[5]; ++ int ack = -1; ++ ++ if (smrt > 37750) ++ C = 1; ++ else if (smrt > 18875) ++ C = 2; ++ else if (smrt > 5500) ++ C = 3; ++ else ++ C = 4; ++ ++ if (smrt > 30500) ++ F = 3; ++ else if (smrt > 9375) ++ F = 1; ++ else if (smrt > 4625) ++ F = 0; ++ else ++ F = 2; ++ ++ if (f < 1060) ++ B = 0; ++ else if (f < 1175) ++ B = 1; ++ else if (f < 1305) ++ B = 2; ++ else if (f < 1435) ++ B = 3; ++ else if (f < 1570) ++ B = 4; ++ else if (f < 1715) ++ B = 5; ++ else if (f < 1845) ++ B = 6; ++ else if (f < 1980) ++ B = 7; ++ else if (f < 2080) ++ B = 8; ++ else ++ B = 9; ++ ++ M = f * (1 << R) / 2; ++ ++ rf_val[0] = 0x01 | (C << 3) | (F << 1); ++ rf_val[1] = (R << 5) | ((M & 0x1f000) >> 12); ++ rf_val[2] = (M & 0x00ff0) >> 4; ++ rf_val[3] = ((M & 0x0000f) << 4) | B; ++ ++ /* Frequency Set */ ++ if (mb86a16_write(state, 0x21, rf_val[0]) < 0) ++ ack = 0; ++ if (mb86a16_write(state, 0x22, rf_val[1]) < 0) ++ ack = 0; ++ if (mb86a16_write(state, 0x23, rf_val[2]) < 0) ++ ack = 0; ++ if (mb86a16_write(state, 0x24, rf_val[3]) < 0) ++ ack = 0; ++ if (mb86a16_write(state, 0x25, 0x01) < 0) ++ ack = 0; ++ if (ack == 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "RF Setup - I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int afcerr_chk(struct mb86a16_state *state) ++{ ++ unsigned char AFCM_L, AFCM_H ; ++ int AFCM ; ++ int afcm, afcerr ; ++ ++ if (mb86a16_read(state, 0x0e, &AFCM_L) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x0f, &AFCM_H) != 2) ++ goto err; ++ ++ AFCM = (AFCM_H << 8) + AFCM_L; ++ ++ if (AFCM > 2048) ++ afcm = AFCM - 4096; ++ else ++ afcm = AFCM; ++ afcerr = afcm * state->master_clk / 8192; ++ ++ return afcerr; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int dagcm_val_get(struct mb86a16_state *state) ++{ ++ int DAGCM; ++ unsigned char DAGCM_H, DAGCM_L; ++ ++ if (mb86a16_read(state, 0x45, &DAGCM_L) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x46, &DAGCM_H) != 2) ++ goto err; ++ ++ DAGCM = (DAGCM_H << 8) + DAGCM_L; ++ ++ return DAGCM; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int mb86a16_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ u8 stat, stat2; ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ *status = 0; ++ ++ if (mb86a16_read(state, MB86A16_SIG1, &stat) != 2) ++ goto err; ++ if (mb86a16_read(state, MB86A16_SIG2, &stat2) != 2) ++ goto err; ++ if ((stat > 25) && (stat2 > 25)) ++ *status |= FE_HAS_SIGNAL; ++ if ((stat > 45) && (stat2 > 45)) ++ *status |= FE_HAS_CARRIER; ++ ++ if (mb86a16_read(state, MB86A16_STATUS, &stat) != 2) ++ goto err; ++ ++ if (stat & 0x01) ++ *status |= FE_HAS_SYNC; ++ if (stat & 0x01) ++ *status |= FE_HAS_VITERBI; ++ ++ if (mb86a16_read(state, MB86A16_FRAMESYNC, &stat) != 2) ++ goto err; ++ ++ if ((stat & 0x0f) && (*status & FE_HAS_VITERBI)) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int sync_chk(struct mb86a16_state *state, ++ unsigned char *VIRM) ++{ ++ unsigned char val; ++ int sync; ++ ++ if (mb86a16_read(state, 0x0d, &val) != 2) ++ goto err; ++ ++ dprintk(verbose, MB86A16_INFO, 1, "Status = %02x,", val); ++ sync = val & 0x01; ++ *VIRM = (val & 0x1c) >> 2; ++ ++ return sync; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ ++} ++ ++static int freqerr_chk(struct mb86a16_state *state, ++ int fTP, ++ int smrt, ++ int unit) ++{ ++ unsigned char CRM, AFCML, AFCMH; ++ unsigned char temp1, temp2, temp3; ++ int crm, afcm, AFCM; ++ int crrerr, afcerr; /* kHz */ ++ int frqerr; /* MHz */ ++ int afcen, afcexen = 0; ++ int R, M, fOSC, fOSC_OFS; ++ ++ if (mb86a16_read(state, 0x43, &CRM) != 2) ++ goto err; ++ ++ if (CRM > 127) ++ crm = CRM - 256; ++ else ++ crm = CRM; ++ ++ crrerr = smrt * crm / 256; ++ if (mb86a16_read(state, 0x49, &temp1) != 2) ++ goto err; ++ ++ afcen = (temp1 & 0x04) >> 2; ++ if (afcen == 0) { ++ if (mb86a16_read(state, 0x2a, &temp1) != 2) ++ goto err; ++ afcexen = (temp1 & 0x20) >> 5; ++ } ++ ++ if (afcen == 1) { ++ if (mb86a16_read(state, 0x0e, &AFCML) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x0f, &AFCMH) != 2) ++ goto err; ++ } else if (afcexen == 1) { ++ if (mb86a16_read(state, 0x2b, &AFCML) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x2c, &AFCMH) != 2) ++ goto err; ++ } ++ if ((afcen == 1) || (afcexen == 1)) { ++ smrt_info_get(state, smrt); ++ AFCM = ((AFCMH & 0x01) << 8) + AFCML; ++ if (AFCM > 255) ++ afcm = AFCM - 512; ++ else ++ afcm = AFCM; ++ ++ afcerr = afcm * state->master_clk / 8192; ++ } else ++ afcerr = 0; ++ ++ if (mb86a16_read(state, 0x22, &temp1) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x23, &temp2) != 2) ++ goto err; ++ if (mb86a16_read(state, 0x24, &temp3) != 2) ++ goto err; ++ ++ R = (temp1 & 0xe0) >> 5; ++ M = ((temp1 & 0x1f) << 12) + (temp2 << 4) + (temp3 >> 4); ++ if (R == 0) ++ fOSC = 2 * M; ++ else ++ fOSC = M; ++ ++ fOSC_OFS = fOSC - fTP; ++ ++ if (unit == 0) { /* MHz */ ++ if (crrerr + afcerr + fOSC_OFS * 1000 >= 0) ++ frqerr = (crrerr + afcerr + fOSC_OFS * 1000 + 500) / 1000; ++ else ++ frqerr = (crrerr + afcerr + fOSC_OFS * 1000 - 500) / 1000; ++ } else { /* kHz */ ++ frqerr = crrerr + afcerr + fOSC_OFS * 1000; ++ } ++ ++ return frqerr; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static unsigned char vco_dev_get(struct mb86a16_state *state, int smrt) ++{ ++ unsigned char R; ++ ++ if (smrt > 9375) ++ R = 0; ++ else ++ R = 1; ++ ++ return R; ++} ++ ++static void swp_info_get(struct mb86a16_state *state, ++ int fOSC_start, ++ int smrt, ++ int v, int R, ++ int swp_ofs, ++ int *fOSC, ++ int *afcex_freq, ++ unsigned char *AFCEX_L, ++ unsigned char *AFCEX_H) ++{ ++ int AFCEX ; ++ int crnt_swp_freq ; ++ ++ crnt_swp_freq = fOSC_start * 1000 + v * swp_ofs; ++ ++ if (R == 0) ++ *fOSC = (crnt_swp_freq + 1000) / 2000 * 2; ++ else ++ *fOSC = (crnt_swp_freq + 500) / 1000; ++ ++ if (*fOSC >= crnt_swp_freq) ++ *afcex_freq = *fOSC * 1000 - crnt_swp_freq; ++ else ++ *afcex_freq = crnt_swp_freq - *fOSC * 1000; ++ ++ AFCEX = *afcex_freq * 8192 / state->master_clk; ++ *AFCEX_L = AFCEX & 0x00ff; ++ *AFCEX_H = (AFCEX & 0x0f00) >> 8; ++} ++ ++ ++static int swp_freq_calcuation(struct mb86a16_state *state, int i, int v, int *V, int vmax, int vmin, ++ int SIGMIN, int fOSC, int afcex_freq, int swp_ofs, unsigned char *SIG1) ++{ ++ int swp_freq ; ++ ++ if ((i % 2 == 1) && (v <= vmax)) { ++ /* positive v (case 1) */ ++ if ((v - 1 == vmin) && ++ (*(V + 30 + v) >= 0) && ++ (*(V + 30 + v - 1) >= 0) && ++ (*(V + 30 + v - 1) > *(V + 30 + v)) && ++ (*(V + 30 + v - 1) > SIGMIN)) { ++ ++ swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; ++ *SIG1 = *(V + 30 + v - 1); ++ } else if ((v == vmax) && ++ (*(V + 30 + v) >= 0) && ++ (*(V + 30 + v - 1) >= 0) && ++ (*(V + 30 + v) > *(V + 30 + v - 1)) && ++ (*(V + 30 + v) > SIGMIN)) { ++ /* (case 2) */ ++ swp_freq = fOSC * 1000 + afcex_freq; ++ *SIG1 = *(V + 30 + v); ++ } else if ((*(V + 30 + v) > 0) && ++ (*(V + 30 + v - 1) > 0) && ++ (*(V + 30 + v - 2) > 0) && ++ (*(V + 30 + v - 3) > 0) && ++ (*(V + 30 + v - 1) > *(V + 30 + v)) && ++ (*(V + 30 + v - 2) > *(V + 30 + v - 3)) && ++ ((*(V + 30 + v - 1) > SIGMIN) || ++ (*(V + 30 + v - 2) > SIGMIN))) { ++ /* (case 3) */ ++ if (*(V + 30 + v - 1) >= *(V + 30 + v - 2)) { ++ swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; ++ *SIG1 = *(V + 30 + v - 1); ++ } else { ++ swp_freq = fOSC * 1000 + afcex_freq - swp_ofs * 2; ++ *SIG1 = *(V + 30 + v - 2); ++ } ++ } else if ((v == vmax) && ++ (*(V + 30 + v) >= 0) && ++ (*(V + 30 + v - 1) >= 0) && ++ (*(V + 30 + v - 2) >= 0) && ++ (*(V + 30 + v) > *(V + 30 + v - 2)) && ++ (*(V + 30 + v - 1) > *(V + 30 + v - 2)) && ++ ((*(V + 30 + v) > SIGMIN) || ++ (*(V + 30 + v - 1) > SIGMIN))) { ++ /* (case 4) */ ++ if (*(V + 30 + v) >= *(V + 30 + v - 1)) { ++ swp_freq = fOSC * 1000 + afcex_freq; ++ *SIG1 = *(V + 30 + v); ++ } else { ++ swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; ++ *SIG1 = *(V + 30 + v - 1); ++ } ++ } else { ++ swp_freq = -1 ; ++ } ++ } else if ((i % 2 == 0) && (v >= vmin)) { ++ /* Negative v (case 1) */ ++ if ((*(V + 30 + v) > 0) && ++ (*(V + 30 + v + 1) > 0) && ++ (*(V + 30 + v + 2) > 0) && ++ (*(V + 30 + v + 1) > *(V + 30 + v)) && ++ (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && ++ (*(V + 30 + v + 1) > SIGMIN)) { ++ ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; ++ *SIG1 = *(V + 30 + v + 1); ++ } else if ((v + 1 == vmax) && ++ (*(V + 30 + v) >= 0) && ++ (*(V + 30 + v + 1) >= 0) && ++ (*(V + 30 + v + 1) > *(V + 30 + v)) && ++ (*(V + 30 + v + 1) > SIGMIN)) { ++ /* (case 2) */ ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; ++ *SIG1 = *(V + 30 + v); ++ } else if ((v == vmin) && ++ (*(V + 30 + v) > 0) && ++ (*(V + 30 + v + 1) > 0) && ++ (*(V + 30 + v + 2) > 0) && ++ (*(V + 30 + v) > *(V + 30 + v + 1)) && ++ (*(V + 30 + v) > *(V + 30 + v + 2)) && ++ (*(V + 30 + v) > SIGMIN)) { ++ /* (case 3) */ ++ swp_freq = fOSC * 1000 + afcex_freq; ++ *SIG1 = *(V + 30 + v); ++ } else if ((*(V + 30 + v) >= 0) && ++ (*(V + 30 + v + 1) >= 0) && ++ (*(V + 30 + v + 2) >= 0) && ++ (*(V + 30 + v + 3) >= 0) && ++ (*(V + 30 + v + 1) > *(V + 30 + v)) && ++ (*(V + 30 + v + 2) > *(V + 30 + v + 3)) && ++ ((*(V + 30 + v + 1) > SIGMIN) || ++ (*(V + 30 + v + 2) > SIGMIN))) { ++ /* (case 4) */ ++ if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; ++ *SIG1 = *(V + 30 + v + 1); ++ } else { ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; ++ *SIG1 = *(V + 30 + v + 2); ++ } ++ } else if ((*(V + 30 + v) >= 0) && ++ (*(V + 30 + v + 1) >= 0) && ++ (*(V + 30 + v + 2) >= 0) && ++ (*(V + 30 + v + 3) >= 0) && ++ (*(V + 30 + v) > *(V + 30 + v + 2)) && ++ (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && ++ (*(V + 30 + v) > *(V + 30 + v + 3)) && ++ (*(V + 30 + v + 1) > *(V + 30 + v + 3)) && ++ ((*(V + 30 + v) > SIGMIN) || ++ (*(V + 30 + v + 1) > SIGMIN))) { ++ /* (case 5) */ ++ if (*(V + 30 + v) >= *(V + 30 + v + 1)) { ++ swp_freq = fOSC * 1000 + afcex_freq; ++ *SIG1 = *(V + 30 + v); ++ } else { ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; ++ *SIG1 = *(V + 30 + v + 1); ++ } ++ } else if ((v + 2 == vmin) && ++ (*(V + 30 + v) >= 0) && ++ (*(V + 30 + v + 1) >= 0) && ++ (*(V + 30 + v + 2) >= 0) && ++ (*(V + 30 + v + 1) > *(V + 30 + v)) && ++ (*(V + 30 + v + 2) > *(V + 30 + v)) && ++ ((*(V + 30 + v + 1) > SIGMIN) || ++ (*(V + 30 + v + 2) > SIGMIN))) { ++ /* (case 6) */ ++ if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; ++ *SIG1 = *(V + 30 + v + 1); ++ } else { ++ swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; ++ *SIG1 = *(V + 30 + v + 2); ++ } ++ } else if ((vmax == 0) && (vmin == 0) && (*(V + 30 + v) > SIGMIN)) { ++ swp_freq = fOSC * 1000; ++ *SIG1 = *(V + 30 + v); ++ } else ++ swp_freq = -1; ++ } else ++ swp_freq = -1; ++ ++ return swp_freq; ++} ++ ++static void swp_info_get2(struct mb86a16_state *state, ++ int smrt, ++ int R, ++ int swp_freq, ++ int *afcex_freq, ++ int *fOSC, ++ unsigned char *AFCEX_L, ++ unsigned char *AFCEX_H) ++{ ++ int AFCEX ; ++ ++ if (R == 0) ++ *fOSC = (swp_freq + 1000) / 2000 * 2; ++ else ++ *fOSC = (swp_freq + 500) / 1000; ++ ++ if (*fOSC >= swp_freq) ++ *afcex_freq = *fOSC * 1000 - swp_freq; ++ else ++ *afcex_freq = swp_freq - *fOSC * 1000; ++ ++ AFCEX = *afcex_freq * 8192 / state->master_clk; ++ *AFCEX_L = AFCEX & 0x00ff; ++ *AFCEX_H = (AFCEX & 0x0f00) >> 8; ++} ++ ++static void afcex_info_get(struct mb86a16_state *state, ++ int afcex_freq, ++ unsigned char *AFCEX_L, ++ unsigned char *AFCEX_H) ++{ ++ int AFCEX ; ++ ++ AFCEX = afcex_freq * 8192 / state->master_clk; ++ *AFCEX_L = AFCEX & 0x00ff; ++ *AFCEX_H = (AFCEX & 0x0f00) >> 8; ++} ++ ++static int SEQ_set(struct mb86a16_state *state, unsigned char loop) ++{ ++ /* SLOCK0 = 0 */ ++ if (mb86a16_write(state, 0x32, 0x02 | (loop << 2)) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int iq_vt_set(struct mb86a16_state *state, unsigned char IQINV) ++{ ++ /* Viterbi Rate, IQ Settings */ ++ if (mb86a16_write(state, 0x06, 0xdf | (IQINV << 5)) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int FEC_srst(struct mb86a16_state *state) ++{ ++ if (mb86a16_write(state, MB86A16_RESET, 0x02) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int S2T_set(struct mb86a16_state *state, unsigned char S2T) ++{ ++ if (mb86a16_write(state, 0x34, 0x70 | S2T) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int S45T_set(struct mb86a16_state *state, unsigned char S4T, unsigned char S5T) ++{ ++ if (mb86a16_write(state, 0x35, 0x00 | (S5T << 4) | S4T) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++ ++static int mb86a16_set_fe(struct mb86a16_state *state) ++{ ++ u8 agcval, cnmval; ++ ++ int i, j; ++ int fOSC = 0; ++ int fOSC_start = 0; ++ int wait_t; ++ int fcp; ++ int swp_ofs; ++ int V[60]; ++ u8 SIG1MIN; ++ ++ unsigned char CREN, AFCEN, AFCEXEN; ++ unsigned char SIG1; ++ unsigned char TIMINT1, TIMINT2, TIMEXT; ++ unsigned char S0T, S1T; ++ unsigned char S2T; ++/* unsigned char S2T, S3T; */ ++ unsigned char S4T, S5T; ++ unsigned char AFCEX_L, AFCEX_H; ++ unsigned char R; ++ unsigned char VIRM; ++ unsigned char ETH, VIA; ++ unsigned char junk; ++ ++ int loop; ++ int ftemp; ++ int v, vmax, vmin; ++ int vmax_his, vmin_his; ++ int swp_freq, prev_swp_freq[20]; ++ int prev_freq_num; ++ int signal_dupl; ++ int afcex_freq; ++ int signal; ++ int afcerr; ++ int temp_freq, delta_freq; ++ int dagcm[4]; ++ int smrt_d; ++/* int freq_err; */ ++ int n; ++ int ret = -1; ++ int sync; ++ ++ dprintk(verbose, MB86A16_INFO, 1, "freq=%d Mhz, symbrt=%d Ksps", state->frequency, state->srate); ++ ++ fcp = 3000; ++ swp_ofs = state->srate / 4; ++ ++ for (i = 0; i < 60; i++) ++ V[i] = -1; ++ ++ for (i = 0; i < 20; i++) ++ prev_swp_freq[i] = 0; ++ ++ SIG1MIN = 25; ++ ++ for (n = 0; ((n < 3) && (ret == -1)); n++) { ++ SEQ_set(state, 0); ++ iq_vt_set(state, 0); ++ ++ CREN = 0; ++ AFCEN = 0; ++ AFCEXEN = 1; ++ TIMINT1 = 0; ++ TIMINT2 = 1; ++ TIMEXT = 2; ++ S1T = 0; ++ S0T = 0; ++ ++ if (initial_set(state) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "initial set failed"); ++ return -1; ++ } ++ if (DAGC_data_set(state, 3, 2) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); ++ return -1; ++ } ++ if (EN_set(state, CREN, AFCEN) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); ++ return -1; /* (0, 0) */ ++ } ++ if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); ++ return -1; /* (1, smrt) = (1, symbolrate) */ ++ } ++ if (CNTM_set(state, TIMINT1, TIMINT2, TIMEXT) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "CNTM set error"); ++ return -1; /* (0, 1, 2) */ ++ } ++ if (S01T_set(state, S1T, S0T) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); ++ return -1; /* (0, 0) */ ++ } ++ smrt_info_get(state, state->srate); ++ if (smrt_set(state, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "smrt info get error"); ++ return -1; ++ } ++ ++ R = vco_dev_get(state, state->srate); ++ if (R == 1) ++ fOSC_start = state->frequency; ++ ++ else if (R == 0) { ++ if (state->frequency % 2 == 0) { ++ fOSC_start = state->frequency; ++ } else { ++ fOSC_start = state->frequency + 1; ++ if (fOSC_start > 2150) ++ fOSC_start = state->frequency - 1; ++ } ++ } ++ loop = 1; ++ ftemp = fOSC_start * 1000; ++ vmax = 0 ; ++ while (loop == 1) { ++ ftemp = ftemp + swp_ofs; ++ vmax++; ++ ++ /* Upper bound */ ++ if (ftemp > 2150000) { ++ loop = 0; ++ vmax--; ++ } else { ++ if ((ftemp == 2150000) || ++ (ftemp - state->frequency * 1000 >= fcp + state->srate / 4)) ++ loop = 0; ++ } ++ } ++ ++ loop = 1; ++ ftemp = fOSC_start * 1000; ++ vmin = 0 ; ++ while (loop == 1) { ++ ftemp = ftemp - swp_ofs; ++ vmin--; ++ ++ /* Lower bound */ ++ if (ftemp < 950000) { ++ loop = 0; ++ vmin++; ++ } else { ++ if ((ftemp == 950000) || ++ (state->frequency * 1000 - ftemp >= fcp + state->srate / 4)) ++ loop = 0; ++ } ++ } ++ ++ wait_t = (8000 + state->srate / 2) / state->srate; ++ if (wait_t == 0) ++ wait_t = 1; ++ ++ i = 0; ++ j = 0; ++ prev_freq_num = 0; ++ loop = 1; ++ signal = 0; ++ vmax_his = 0; ++ vmin_his = 0; ++ v = 0; ++ ++ while (loop == 1) { ++ swp_info_get(state, fOSC_start, state->srate, ++ v, R, swp_ofs, &fOSC, ++ &afcex_freq, &AFCEX_L, &AFCEX_H); ++ ++ udelay(100); ++ if (rf_val_set(state, fOSC, state->srate, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); ++ return -1; ++ } ++ udelay(100); ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); ++ return -1; ++ } ++ if (srst(state) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "srst error"); ++ return -1; ++ } ++ msleep_interruptible(wait_t); ++ ++ if (mb86a16_read(state, 0x37, &SIG1) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -1; ++ } ++ V[30 + v] = SIG1 ; ++ swp_freq = swp_freq_calcuation(state, i, v, V, vmax, vmin, ++ SIG1MIN, fOSC, afcex_freq, ++ swp_ofs, &SIG1); /* changed */ ++ ++ signal_dupl = 0; ++ for (j = 0; j < prev_freq_num; j++) { ++ if ((ABS(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) { ++ signal_dupl = 1; ++ dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j); ++ } ++ } ++ if ((signal_dupl == 0) && (swp_freq > 0) && (ABS(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) { ++ dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate); ++ prev_swp_freq[prev_freq_num] = swp_freq; ++ prev_freq_num++; ++ swp_info_get2(state, state->srate, R, swp_freq, ++ &afcex_freq, &fOSC, ++ &AFCEX_L, &AFCEX_H); ++ ++ if (rf_val_set(state, fOSC, state->srate, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); ++ return -1; ++ } ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); ++ return -1; ++ } ++ signal = signal_det(state, state->srate, &SIG1); ++ if (signal == 1) { ++ dprintk(verbose, MB86A16_ERROR, 1, "***** Signal Found *****"); ++ loop = 0; ++ } else { ++ dprintk(verbose, MB86A16_ERROR, 1, "!!!!! No signal !!!!!, try again..."); ++ smrt_info_get(state, state->srate); ++ if (smrt_set(state, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); ++ return -1; ++ } ++ } ++ } ++ if (v > vmax) ++ vmax_his = 1 ; ++ if (v < vmin) ++ vmin_his = 1 ; ++ i++; ++ ++ if ((i % 2 == 1) && (vmax_his == 1)) ++ i++; ++ if ((i % 2 == 0) && (vmin_his == 1)) ++ i++; ++ ++ if (i % 2 == 1) ++ v = (i + 1) / 2; ++ else ++ v = -i / 2; ++ ++ if ((vmax_his == 1) && (vmin_his == 1)) ++ loop = 0 ; ++ } ++ ++ if (signal == 1) { ++ dprintk(verbose, MB86A16_INFO, 1, " Start Freq Error Check"); ++ S1T = 7 ; ++ S0T = 1 ; ++ CREN = 0 ; ++ AFCEN = 1 ; ++ AFCEXEN = 0 ; ++ ++ if (S01T_set(state, S1T, S0T) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); ++ return -1; ++ } ++ smrt_info_get(state, state->srate); ++ if (smrt_set(state, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); ++ return -1; ++ } ++ if (EN_set(state, CREN, AFCEN) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); ++ return -1; ++ } ++ if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); ++ return -1; ++ } ++ afcex_info_get(state, afcex_freq, &AFCEX_L, &AFCEX_H); ++ if (afcofs_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "AFCOFS data set error"); ++ return -1; ++ } ++ if (srst(state) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "srst error"); ++ return -1; ++ } ++ /* delay 4~200 */ ++ wait_t = 200000 / state->master_clk + 200000 / state->srate; ++ msleep(wait_t); ++ afcerr = afcerr_chk(state); ++ if (afcerr == -1) ++ return -1; ++ ++ swp_freq = fOSC * 1000 + afcerr ; ++ AFCEXEN = 1 ; ++ if (state->srate >= 1500) ++ smrt_d = state->srate / 3; ++ else ++ smrt_d = state->srate / 2; ++ smrt_info_get(state, smrt_d); ++ if (smrt_set(state, smrt_d) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); ++ return -1; ++ } ++ if (AFCEXEN_set(state, AFCEXEN, smrt_d) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); ++ return -1; ++ } ++ R = vco_dev_get(state, smrt_d); ++ if (DAGC_data_set(state, 2, 0) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); ++ return -1; ++ } ++ for (i = 0; i < 3; i++) { ++ temp_freq = swp_freq + (i - 1) * state->srate / 8; ++ swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); ++ if (rf_val_set(state, fOSC, smrt_d, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); ++ return -1; ++ } ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); ++ return -1; ++ } ++ wait_t = 200000 / state->master_clk + 40000 / smrt_d; ++ msleep(wait_t); ++ dagcm[i] = dagcm_val_get(state); ++ } ++ if ((dagcm[0] > dagcm[1]) && ++ (dagcm[0] > dagcm[2]) && ++ (dagcm[0] - dagcm[1] > 2 * (dagcm[2] - dagcm[1]))) { ++ ++ temp_freq = swp_freq - 2 * state->srate / 8; ++ swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); ++ if (rf_val_set(state, fOSC, smrt_d, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); ++ return -1; ++ } ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); ++ return -1; ++ } ++ wait_t = 200000 / state->master_clk + 40000 / smrt_d; ++ msleep(wait_t); ++ dagcm[3] = dagcm_val_get(state); ++ if (dagcm[3] > dagcm[1]) ++ delta_freq = (dagcm[2] - dagcm[0] + dagcm[1] - dagcm[3]) * state->srate / 300; ++ else ++ delta_freq = 0; ++ } else if ((dagcm[2] > dagcm[1]) && ++ (dagcm[2] > dagcm[0]) && ++ (dagcm[2] - dagcm[1] > 2 * (dagcm[0] - dagcm[1]))) { ++ ++ temp_freq = swp_freq + 2 * state->srate / 8; ++ swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); ++ if (rf_val_set(state, fOSC, smrt_d, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set"); ++ return -1; ++ } ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); ++ return -1; ++ } ++ wait_t = 200000 / state->master_clk + 40000 / smrt_d; ++ msleep(wait_t); ++ dagcm[3] = dagcm_val_get(state); ++ if (dagcm[3] > dagcm[1]) ++ delta_freq = (dagcm[2] - dagcm[0] + dagcm[3] - dagcm[1]) * state->srate / 300; ++ else ++ delta_freq = 0 ; ++ ++ } else { ++ delta_freq = 0 ; ++ } ++ dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq); ++ swp_freq += delta_freq; ++ dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq); ++ if (ABS(state->frequency * 1000 - swp_freq) > 3800) { ++ dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !"); ++ } else { ++ ++ S1T = 0; ++ S0T = 3; ++ CREN = 1; ++ AFCEN = 0; ++ AFCEXEN = 1; ++ ++ if (S01T_set(state, S1T, S0T) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); ++ return -1; ++ } ++ if (DAGC_data_set(state, 0, 0) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); ++ return -1; ++ } ++ R = vco_dev_get(state, state->srate); ++ smrt_info_get(state, state->srate); ++ if (smrt_set(state, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); ++ return -1; ++ } ++ if (EN_set(state, CREN, AFCEN) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); ++ return -1; ++ } ++ if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); ++ return -1; ++ } ++ swp_info_get2(state, state->srate, R, swp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); ++ if (rf_val_set(state, fOSC, state->srate, R) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); ++ return -1; ++ } ++ if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); ++ return -1; ++ } ++ if (srst(state) < 0) { ++ dprintk(verbose, MB86A16_ERROR, 1, "srst error"); ++ return -1; ++ } ++ wait_t = 7 + (10000 + state->srate / 2) / state->srate; ++ if (wait_t == 0) ++ wait_t = 1; ++ msleep_interruptible(wait_t); ++ if (mb86a16_read(state, 0x37, &SIG1) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ if (SIG1 > 110) { ++ S2T = 4; S4T = 1; S5T = 6; ETH = 4; VIA = 6; ++ wait_t = 7 + (917504 + state->srate / 2) / state->srate; ++ } else if (SIG1 > 105) { ++ S2T = 4; S4T = 2; S5T = 8; ETH = 7; VIA = 2; ++ wait_t = 7 + (1048576 + state->srate / 2) / state->srate; ++ } else if (SIG1 > 85) { ++ S2T = 5; S4T = 2; S5T = 8; ETH = 7; VIA = 2; ++ wait_t = 7 + (1310720 + state->srate / 2) / state->srate; ++ } else if (SIG1 > 65) { ++ S2T = 6; S4T = 2; S5T = 8; ETH = 7; VIA = 2; ++ wait_t = 7 + (1572864 + state->srate / 2) / state->srate; ++ } else { ++ S2T = 7; S4T = 2; S5T = 8; ETH = 7; VIA = 2; ++ wait_t = 7 + (2097152 + state->srate / 2) / state->srate; ++ } ++ wait_t *= 2; /* FOS */ ++ S2T_set(state, S2T); ++ S45T_set(state, S4T, S5T); ++ Vi_set(state, ETH, VIA); ++ srst(state); ++ msleep_interruptible(wait_t); ++ sync = sync_chk(state, &VIRM); ++ dprintk(verbose, MB86A16_INFO, 1, "-------- Viterbi=[%d] SYNC=[%d] ---------", VIRM, sync); ++ if (VIRM) { ++ if (VIRM == 4) { ++ /* 5/6 */ ++ if (SIG1 > 110) ++ wait_t = (786432 + state->srate / 2) / state->srate; ++ else ++ wait_t = (1572864 + state->srate / 2) / state->srate; ++ if (state->srate < 5000) ++ /* FIXME ! , should be a long wait ! */ ++ msleep_interruptible(wait_t); ++ else ++ msleep_interruptible(wait_t); ++ ++ if (sync_chk(state, &junk) == 0) { ++ iq_vt_set(state, 1); ++ FEC_srst(state); ++ } ++ } ++ /* 1/2, 2/3, 3/4, 7/8 */ ++ if (SIG1 > 110) ++ wait_t = (786432 + state->srate / 2) / state->srate; ++ else ++ wait_t = (1572864 + state->srate / 2) / state->srate; ++ msleep_interruptible(wait_t); ++ SEQ_set(state, 1); ++ } else { ++ dprintk(verbose, MB86A16_INFO, 1, "NO -- SYNC"); ++ SEQ_set(state, 1); ++ ret = -1; ++ } ++ } ++ } else { ++ dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL"); ++ ret = -1; ++ } ++ ++ sync = sync_chk(state, &junk); ++ if (sync) { ++ dprintk(verbose, MB86A16_INFO, 1, "******* SYNC *******"); ++ freqerr_chk(state, state->frequency, state->srate, 1); ++ ret = 0; ++ break; ++ } ++ } ++ ++ mb86a16_read(state, 0x15, &agcval); ++ mb86a16_read(state, 0x26, &cnmval); ++ dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval); ++ ++ return ret; ++} ++ ++static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ int i; ++ u8 regs; ++ ++ if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) ++ goto err; ++ ++ regs = 0x18; ++ ++ if (cmd->msg_len > 5 || cmd->msg_len < 4) ++ return -EINVAL; ++ ++ for (i = 0; i < cmd->msg_len; i++) { ++ if (mb86a16_write(state, regs, cmd->msg[i]) < 0) ++ goto err; ++ ++ regs++; ++ } ++ i += 0x90; ++ ++ msleep_interruptible(10); ++ ++ if (mb86a16_write(state, MB86A16_DCC1, i) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ switch (burst) { ++ case SEC_MINI_A: ++ if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | ++ MB86A16_DCC1_TBEN | ++ MB86A16_DCC1_TBO) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) ++ goto err; ++ break; ++ case SEC_MINI_B: ++ if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | ++ MB86A16_DCC1_TBEN) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) ++ goto err; ++ break; ++ } ++ ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int mb86a16_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ if (mb86a16_write(state, MB86A16_TONEOUT2, 0x00) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | ++ MB86A16_DCC1_CTOE) < 0) ++ ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) ++ goto err; ++ break; ++ case SEC_TONE_OFF: ++ if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) ++ goto err; ++ if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) ++ goto err; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++ ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static enum dvbfe_search mb86a16_search(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ state->frequency = p->frequency / 1000; ++ state->srate = p->symbol_rate / 1000; ++ ++ if (!mb86a16_set_fe(state)) { ++ dprintk(verbose, MB86A16_ERROR, 1, "Successfully acquired LOCK"); ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } ++ ++ dprintk(verbose, MB86A16_ERROR, 1, "Lock acquisition failed!"); ++ return DVBFE_ALGO_SEARCH_FAILED; ++} ++ ++static void mb86a16_release(struct dvb_frontend *fe) ++{ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static int mb86a16_init(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int mb86a16_sleep(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int mb86a16_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ u8 ber_mon, ber_tab, ber_lsb, ber_mid, ber_msb, ber_tim, ber_rst; ++ u32 timer; ++ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ *ber = 0; ++ if (mb86a16_read(state, MB86A16_BERMON, &ber_mon) != 2) ++ goto err; ++ if (mb86a16_read(state, MB86A16_BERTAB, &ber_tab) != 2) ++ goto err; ++ if (mb86a16_read(state, MB86A16_BERLSB, &ber_lsb) != 2) ++ goto err; ++ if (mb86a16_read(state, MB86A16_BERMID, &ber_mid) != 2) ++ goto err; ++ if (mb86a16_read(state, MB86A16_BERMSB, &ber_msb) != 2) ++ goto err; ++ /* BER monitor invalid when BER_EN = 0 */ ++ if (ber_mon & 0x04) { ++ /* coarse, fast calculation */ ++ *ber = ber_tab & 0x1f; ++ dprintk(verbose, MB86A16_DEBUG, 1, "BER coarse=[0x%02x]", *ber); ++ if (ber_mon & 0x01) { ++ /* ++ * BER_SEL = 1, The monitored BER is the estimated ++ * value with a Reed-Solomon decoder error amount at ++ * the deinterleaver output. ++ * monitored BER is expressed as a 20 bit output in total ++ */ ++ ber_rst = ber_mon >> 3; ++ *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; ++ if (ber_rst == 0) ++ timer = 12500000; ++ if (ber_rst == 1) ++ timer = 25000000; ++ if (ber_rst == 2) ++ timer = 50000000; ++ if (ber_rst == 3) ++ timer = 100000000; ++ ++ *ber /= timer; ++ dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); ++ } else { ++ /* ++ * BER_SEL = 0, The monitored BER is the estimated ++ * value with a Viterbi decoder error amount at the ++ * QPSK demodulator output. ++ * monitored BER is expressed as a 24 bit output in total ++ */ ++ ber_tim = ber_mon >> 1; ++ *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; ++ if (ber_tim == 0) ++ timer = 16; ++ if (ber_tim == 1) ++ timer = 24; ++ ++ *ber /= 2 ^ timer; ++ dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); ++ } ++ } ++ return 0; ++err: ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++} ++ ++static int mb86a16_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ u8 agcm = 0; ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ *strength = 0; ++ if (mb86a16_read(state, MB86A16_AGCM, &agcm) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ *strength = ((0xff - agcm) * 100) / 256; ++ dprintk(verbose, MB86A16_DEBUG, 1, "Signal strength=[%d %%]", (u8) *strength); ++ *strength = (0xffff - 0xff) + agcm; ++ ++ return 0; ++} ++ ++struct cnr { ++ u8 cn_reg; ++ u8 cn_val; ++}; ++ ++static const struct cnr cnr_tab[] = { ++ { 35, 2 }, ++ { 40, 3 }, ++ { 50, 4 }, ++ { 60, 5 }, ++ { 70, 6 }, ++ { 80, 7 }, ++ { 92, 8 }, ++ { 103, 9 }, ++ { 115, 10 }, ++ { 138, 12 }, ++ { 162, 15 }, ++ { 180, 18 }, ++ { 185, 19 }, ++ { 189, 20 }, ++ { 195, 22 }, ++ { 199, 24 }, ++ { 201, 25 }, ++ { 202, 26 }, ++ { 203, 27 }, ++ { 205, 28 }, ++ { 208, 30 } ++}; ++ ++static int mb86a16_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct mb86a16_state *state = fe->demodulator_priv; ++ int i = 0; ++ int low_tide = 2, high_tide = 30, q_level; ++ u8 cn; ++ ++ *snr = 0; ++ if (mb86a16_read(state, 0x26, &cn) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(cnr_tab); i++) { ++ if (cn < cnr_tab[i].cn_reg) { ++ *snr = cnr_tab[i].cn_val; ++ break; ++ } ++ } ++ q_level = (*snr * 100) / (high_tide - low_tide); ++ dprintk(verbose, MB86A16_ERROR, 1, "SNR (Quality) = [%d dB], Level=%d %%", *snr, q_level); ++ *snr = (0xffff - 0xff) + *snr; ++ ++ return 0; ++} ++ ++static int mb86a16_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ u8 dist; ++ struct mb86a16_state *state = fe->demodulator_priv; ++ ++ if (mb86a16_read(state, MB86A16_DISTMON, &dist) != 2) { ++ dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); ++ return -EREMOTEIO; ++ } ++ *ucblocks = dist; ++ ++ return 0; ++} ++ ++static enum dvbfe_algo mb86a16_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static struct dvb_frontend_ops mb86a16_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Fujitsu MB86A16 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 3000, ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | FE_CAN_QPSK | ++ FE_CAN_FEC_AUTO ++ }, ++ .release = mb86a16_release, ++ ++ .get_frontend_algo = mb86a16_frontend_algo, ++ .search = mb86a16_search, ++ .init = mb86a16_init, ++ .sleep = mb86a16_sleep, ++ .read_status = mb86a16_read_status, ++ ++ .read_ber = mb86a16_read_ber, ++ .read_signal_strength = mb86a16_read_signal_strength, ++ .read_snr = mb86a16_read_snr, ++ .read_ucblocks = mb86a16_read_ucblocks, ++ ++ .diseqc_send_master_cmd = mb86a16_send_diseqc_msg, ++ .diseqc_send_burst = mb86a16_send_diseqc_burst, ++ .set_tone = mb86a16_set_tone, ++}; ++ ++struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ u8 dev_id = 0; ++ struct mb86a16_state *state = NULL; ++ ++ state = kmalloc(sizeof(struct mb86a16_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ state->config = config; ++ state->i2c_adap = i2c_adap; ++ ++ mb86a16_read(state, 0x7f, &dev_id); ++ if (dev_id != 0xfe) ++ goto error; ++ ++ memcpy(&state->frontend.ops, &mb86a16_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ state->frontend.ops.set_voltage = state->config->set_voltage; ++ ++ return &state->frontend; ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(mb86a16_attach); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Manu Abraham"); +diff --git a/drivers/media/dvb-frontends/mb86a16.h b/drivers/media/dvb-frontends/mb86a16.h +new file mode 100644 +index 0000000..6ea8c37 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mb86a16.h +@@ -0,0 +1,52 @@ ++/* ++ Fujitsu MB86A16 DVB-S/DSS DC Receiver driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MB86A16_H ++#define __MB86A16_H ++ ++#include ++#include "dvb_frontend.h" ++ ++ ++struct mb86a16_config { ++ u8 demod_address; ++ ++ int (*set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); ++}; ++ ++ ++ ++#if defined(CONFIG_DVB_MB86A16) || (defined(CONFIG_DVB_MB86A16_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, ++ struct i2c_adapter *i2c_adap); ++ ++#else ++ ++static inline struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, ++ struct i2c_adapter *i2c_adap) ++{ ++ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif /* CONFIG_DVB_MB86A16 */ ++ ++#endif /* __MB86A16_H */ +diff --git a/drivers/media/dvb-frontends/mb86a16_priv.h b/drivers/media/dvb-frontends/mb86a16_priv.h +new file mode 100644 +index 0000000..360a35a +--- /dev/null ++++ b/drivers/media/dvb-frontends/mb86a16_priv.h +@@ -0,0 +1,151 @@ ++/* ++ Fujitsu MB86A16 DVB-S/DSS DC Receiver driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MB86A16_PRIV_H ++#define __MB86A16_PRIV_H ++ ++#define MB86A16_TSOUT 0x00 ++#define MB86A16_TSOUT_HIZSEL (0x01 << 5) ++#define MB86A16_TSOUT_HIZCNTI (0x01 << 4) ++#define MB86A16_TSOUT_MODE (0x01 << 3) ++#define MB86A16_TSOUT_ORDER (0x01 << 2) ++#define MB86A16_TSOUT_ERROR (0x01 << 1) ++#define Mb86A16_TSOUT_EDGE (0x01 << 0) ++ ++#define MB86A16_FEC 0x01 ++#define MB86A16_FEC_FSYNC (0x01 << 5) ++#define MB86A16_FEC_PCKB8 (0x01 << 4) ++#define MB86A16_FEC_DVDS (0x01 << 3) ++#define MB86A16_FEC_EREN (0x01 << 2) ++#define Mb86A16_FEC_RSEN (0x01 << 1) ++#define MB86A16_FEC_DIEN (0x01 << 0) ++ ++#define MB86A16_AGC 0x02 ++#define MB86A16_AGC_AGMD (0x01 << 6) ++#define MB86A16_AGC_AGCW (0x0f << 2) ++#define MB86A16_AGC_AGCP (0x01 << 1) ++#define MB86A16_AGC_AGCR (0x01 << 0) ++ ++#define MB86A16_SRATE1 0x03 ++#define MB86A16_SRATE1_DECI (0x07 << 2) ++#define MB86A16_SRATE1_CSEL (0x01 << 1) ++#define MB86A16_SRATE1_RSEL (0x01 << 0) ++ ++#define MB86A16_SRATE2 0x04 ++#define MB86A16_SRATE2_STOFSL (0xff << 0) ++ ++#define MB86A16_SRATE3 0x05 ++#define MB86A16_SRATE2_STOFSH (0xff << 0) ++ ++#define MB86A16_VITERBI 0x06 ++#define MB86A16_FRAMESYNC 0x07 ++#define MB86A16_CRLFILTCOEF1 0x08 ++#define MB86A16_CRLFILTCOEF2 0x09 ++#define MB86A16_STRFILTCOEF1 0x0a ++#define MB86A16_STRFILTCOEF2 0x0b ++#define MB86A16_RESET 0x0c ++#define MB86A16_STATUS 0x0d ++#define MB86A16_AFCML 0x0e ++#define MB86A16_AFCMH 0x0f ++#define MB86A16_BERMON 0x10 ++#define MB86A16_BERTAB 0x11 ++#define MB86A16_BERLSB 0x12 ++#define MB86A16_BERMID 0x13 ++#define MB86A16_BERMSB 0x14 ++#define MB86A16_AGCM 0x15 ++ ++#define MB86A16_DCC1 0x16 ++#define MB86A16_DCC1_DISTA (0x01 << 7) ++#define MB86A16_DCC1_PRTY (0x01 << 6) ++#define MB86A16_DCC1_CTOE (0x01 << 5) ++#define MB86A16_DCC1_TBEN (0x01 << 4) ++#define MB86A16_DCC1_TBO (0x01 << 3) ++#define MB86A16_DCC1_NUM (0x07 << 0) ++ ++#define MB86A16_DCC2 0x17 ++#define MB86A16_DCC2_DCBST (0x01 << 0) ++ ++#define MB86A16_DCC3 0x18 ++#define MB86A16_DCC3_CODE0 (0xff << 0) ++ ++#define MB86A16_DCC4 0x19 ++#define MB86A16_DCC4_CODE1 (0xff << 0) ++ ++#define MB86A16_DCC5 0x1a ++#define MB86A16_DCC5_CODE2 (0xff << 0) ++ ++#define MB86A16_DCC6 0x1b ++#define MB86A16_DCC6_CODE3 (0xff << 0) ++ ++#define MB86A16_DCC7 0x1c ++#define MB86A16_DCC7_CODE4 (0xff << 0) ++ ++#define MB86A16_DCC8 0x1d ++#define MB86A16_DCC8_CODE5 (0xff << 0) ++ ++#define MB86A16_DCCOUT 0x1e ++#define MB86A16_DCCOUT_DISEN (0x01 << 0) ++ ++#define MB86A16_TONEOUT1 0x1f ++#define MB86A16_TONE_TDIVL (0xff << 0) ++ ++#define MB86A16_TONEOUT2 0x20 ++#define MB86A16_TONE_TMD (0x03 << 2) ++#define MB86A16_TONE_TDIVH (0x03 << 0) ++ ++#define MB86A16_FREQ1 0x21 ++#define MB86A16_FREQ2 0x22 ++#define MB86A16_FREQ3 0x23 ++#define MB86A16_FREQ4 0x24 ++#define MB86A16_FREQSET 0x25 ++#define MB86A16_CNM 0x26 ++#define MB86A16_PORT0 0x27 ++#define MB86A16_PORT1 0x28 ++#define MB86A16_DRCFILT 0x29 ++#define MB86A16_AFC 0x2a ++#define MB86A16_AFCEXL 0x2b ++#define MB86A16_AFCEXH 0x2c ++#define MB86A16_DAGC 0x2d ++#define MB86A16_SEQMODE 0x32 ++#define MB86A16_S0S1T 0x33 ++#define MB86A16_S2S3T 0x34 ++#define MB86A16_S4S5T 0x35 ++#define MB86A16_CNTMR 0x36 ++#define MB86A16_SIG1 0x37 ++#define MB86A16_SIG2 0x38 ++#define MB86A16_VIMAG 0x39 ++#define MB86A16_VISET1 0x3a ++#define MB86A16_VISET2 0x3b ++#define MB86A16_VISET3 0x3c ++#define MB86A16_FAGCS1 0x3d ++#define MB86A16_FAGCS2 0x3e ++#define MB86A16_FAGCS3 0x3f ++#define MB86A16_FAGCS4 0x40 ++#define MB86A16_FAGCS5 0x41 ++#define MB86A16_FAGCS6 0x42 ++#define MB86A16_CRM 0x43 ++#define MB86A16_STRM 0x44 ++#define MB86A16_DAGCML 0x45 ++#define MB86A16_DAGCMH 0x46 ++#define MB86A16_QPSKTST 0x49 ++#define MB86A16_DISTMON 0x52 ++#define MB86A16_VERSION 0x7f ++ ++#endif /* __MB86A16_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c +new file mode 100644 +index 0000000..fade566 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mb86a20s.c +@@ -0,0 +1,701 @@ ++/* ++ * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver ++ * ++ * Copyright (C) 2010 Mauro Carvalho Chehab ++ * Copyright (C) 2009-2010 Douglas Landgraf ++ * ++ * FIXME: Need to port to DVB v5.2 API ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "mb86a20s.h" ++ ++static int debug = 1; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define rc(args...) do { \ ++ printk(KERN_ERR "mb86a20s: " args); \ ++} while (0) ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ ++ printk(args); \ ++ } \ ++ } while (0) ++ ++struct mb86a20s_state { ++ struct i2c_adapter *i2c; ++ const struct mb86a20s_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ bool need_init; ++}; ++ ++struct regdata { ++ u8 reg; ++ u8 data; ++}; ++ ++/* ++ * Initialization sequence: Use whatevere default values that PV SBTVD ++ * does on its initialisation, obtained via USB snoop ++ */ ++static struct regdata mb86a20s_init[] = { ++ { 0x70, 0x0f }, ++ { 0x70, 0xff }, ++ { 0x08, 0x01 }, ++ { 0x09, 0x3e }, ++ { 0x50, 0xd1 }, { 0x51, 0x22 }, ++ { 0x39, 0x01 }, ++ { 0x71, 0x00 }, ++ { 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 }, ++ { 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 }, ++ { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, ++ { 0x3b, 0x21 }, ++ { 0x3c, 0x3a }, ++ { 0x01, 0x0d }, ++ { 0x04, 0x08 }, { 0x05, 0x05 }, ++ { 0x04, 0x0e }, { 0x05, 0x00 }, ++ { 0x04, 0x0f }, { 0x05, 0x14 }, ++ { 0x04, 0x0b }, { 0x05, 0x8c }, ++ { 0x04, 0x00 }, { 0x05, 0x00 }, ++ { 0x04, 0x01 }, { 0x05, 0x07 }, ++ { 0x04, 0x02 }, { 0x05, 0x0f }, ++ { 0x04, 0x03 }, { 0x05, 0xa0 }, ++ { 0x04, 0x09 }, { 0x05, 0x00 }, ++ { 0x04, 0x0a }, { 0x05, 0xff }, ++ { 0x04, 0x27 }, { 0x05, 0x64 }, ++ { 0x04, 0x28 }, { 0x05, 0x00 }, ++ { 0x04, 0x1e }, { 0x05, 0xff }, ++ { 0x04, 0x29 }, { 0x05, 0x0a }, ++ { 0x04, 0x32 }, { 0x05, 0x0a }, ++ { 0x04, 0x14 }, { 0x05, 0x02 }, ++ { 0x04, 0x04 }, { 0x05, 0x00 }, ++ { 0x04, 0x05 }, { 0x05, 0x22 }, ++ { 0x04, 0x06 }, { 0x05, 0x0e }, ++ { 0x04, 0x07 }, { 0x05, 0xd8 }, ++ { 0x04, 0x12 }, { 0x05, 0x00 }, ++ { 0x04, 0x13 }, { 0x05, 0xff }, ++ { 0x04, 0x15 }, { 0x05, 0x4e }, ++ { 0x04, 0x16 }, { 0x05, 0x20 }, ++ { 0x52, 0x01 }, ++ { 0x50, 0xa7 }, { 0x51, 0xff }, ++ { 0x50, 0xa8 }, { 0x51, 0xff }, ++ { 0x50, 0xa9 }, { 0x51, 0xff }, ++ { 0x50, 0xaa }, { 0x51, 0xff }, ++ { 0x50, 0xab }, { 0x51, 0xff }, ++ { 0x50, 0xac }, { 0x51, 0xff }, ++ { 0x50, 0xad }, { 0x51, 0xff }, ++ { 0x50, 0xae }, { 0x51, 0xff }, ++ { 0x50, 0xaf }, { 0x51, 0xff }, ++ { 0x5e, 0x07 }, ++ { 0x50, 0xdc }, { 0x51, 0x01 }, ++ { 0x50, 0xdd }, { 0x51, 0xf4 }, ++ { 0x50, 0xde }, { 0x51, 0x01 }, ++ { 0x50, 0xdf }, { 0x51, 0xf4 }, ++ { 0x50, 0xe0 }, { 0x51, 0x01 }, ++ { 0x50, 0xe1 }, { 0x51, 0xf4 }, ++ { 0x50, 0xb0 }, { 0x51, 0x07 }, ++ { 0x50, 0xb2 }, { 0x51, 0xff }, ++ { 0x50, 0xb3 }, { 0x51, 0xff }, ++ { 0x50, 0xb4 }, { 0x51, 0xff }, ++ { 0x50, 0xb5 }, { 0x51, 0xff }, ++ { 0x50, 0xb6 }, { 0x51, 0xff }, ++ { 0x50, 0xb7 }, { 0x51, 0xff }, ++ { 0x50, 0x50 }, { 0x51, 0x02 }, ++ { 0x50, 0x51 }, { 0x51, 0x04 }, ++ { 0x45, 0x04 }, ++ { 0x48, 0x04 }, ++ { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ ++ { 0x50, 0xd6 }, { 0x51, 0x1f }, ++ { 0x50, 0xd2 }, { 0x51, 0x03 }, ++ { 0x50, 0xd7 }, { 0x51, 0x3f }, ++ { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, ++ { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, ++ { 0x04, 0x40 }, { 0x05, 0x01 }, ++ { 0x28, 0x00 }, { 0x29, 0x10 }, ++ { 0x28, 0x05 }, { 0x29, 0x02 }, ++ { 0x1c, 0x01 }, ++ { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, ++ { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, ++ { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, ++ { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, ++ { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, ++ { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, ++ { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, ++ { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, ++ { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, ++ { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, ++ { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, ++ { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, ++ { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, ++ { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, ++ { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, ++ { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, ++ { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, ++ { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, ++ { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, ++ { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, ++ { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, ++ { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, ++ { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, ++ { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, ++ { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, ++ { 0x50, 0x1e }, { 0x51, 0x5d }, ++ { 0x50, 0x22 }, { 0x51, 0x00 }, ++ { 0x50, 0x23 }, { 0x51, 0xc8 }, ++ { 0x50, 0x24 }, { 0x51, 0x00 }, ++ { 0x50, 0x25 }, { 0x51, 0xf0 }, ++ { 0x50, 0x26 }, { 0x51, 0x00 }, ++ { 0x50, 0x27 }, { 0x51, 0xc3 }, ++ { 0x50, 0x39 }, { 0x51, 0x02 }, ++ { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, ++ { 0xd0, 0x00 }, ++}; ++ ++static struct regdata mb86a20s_reset_reception[] = { ++ { 0x70, 0xf0 }, ++ { 0x70, 0xff }, ++ { 0x08, 0x01 }, ++ { 0x08, 0x00 }, ++}; ++ ++static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, ++ u8 i2c_addr, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 ++ }; ++ int rc; ++ ++ rc = i2c_transfer(state->i2c, &msg, 1); ++ if (rc != 1) { ++ printk("%s: writereg error (rc == %i, reg == 0x%02x," ++ " data == 0x%02x)\n", __func__, rc, reg, data); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state, ++ u8 i2c_addr, struct regdata *rd, int size) ++{ ++ int i, rc; ++ ++ for (i = 0; i < size; i++) { ++ rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg, ++ rd[i].data); ++ if (rc < 0) ++ return rc; ++ } ++ return 0; ++} ++ ++static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, ++ u8 i2c_addr, u8 reg) ++{ ++ u8 val; ++ int rc; ++ struct i2c_msg msg[] = { ++ { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } ++ }; ++ ++ rc = i2c_transfer(state->i2c, msg, 2); ++ ++ if (rc != 2) { ++ rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); ++ return rc; ++ } ++ ++ return val; ++} ++ ++#define mb86a20s_readreg(state, reg) \ ++ mb86a20s_i2c_readreg(state, state->config->demod_address, reg) ++#define mb86a20s_writereg(state, reg, val) \ ++ mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val) ++#define mb86a20s_writeregdata(state, regdata) \ ++ mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ ++ regdata, ARRAY_SIZE(regdata)) ++ ++static int mb86a20s_initfe(struct dvb_frontend *fe) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ int rc; ++ u8 regD5 = 1; ++ ++ dprintk("\n"); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ /* Initialize the frontend */ ++ rc = mb86a20s_writeregdata(state, mb86a20s_init); ++ if (rc < 0) ++ goto err; ++ ++ if (!state->config->is_serial) { ++ regD5 &= ~1; ++ ++ rc = mb86a20s_writereg(state, 0x50, 0xd5); ++ if (rc < 0) ++ goto err; ++ rc = mb86a20s_writereg(state, 0x51, regD5); ++ if (rc < 0) ++ goto err; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++err: ++ if (rc < 0) { ++ state->need_init = true; ++ printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); ++ } else { ++ state->need_init = false; ++ dprintk("Initialization succeeded.\n"); ++ } ++ return rc; ++} ++ ++static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ unsigned rf_max, rf_min, rf; ++ u8 val; ++ ++ dprintk("\n"); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ /* Does a binary search to get RF strength */ ++ rf_max = 0xfff; ++ rf_min = 0; ++ do { ++ rf = (rf_max + rf_min) / 2; ++ mb86a20s_writereg(state, 0x04, 0x1f); ++ mb86a20s_writereg(state, 0x05, rf >> 8); ++ mb86a20s_writereg(state, 0x04, 0x20); ++ mb86a20s_writereg(state, 0x04, rf); ++ ++ val = mb86a20s_readreg(state, 0x02); ++ if (val & 0x08) ++ rf_min = (rf_max + rf_min) / 2; ++ else ++ rf_max = (rf_max + rf_min) / 2; ++ if (rf_max - rf_min < 4) { ++ *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; ++ break; ++ } ++ } while (1); ++ ++ dprintk("signal strength = %d\n", *strength); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ return 0; ++} ++ ++static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("\n"); ++ *status = 0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ val = mb86a20s_readreg(state, 0x0a) & 0xf; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (val >= 2) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (val >= 4) ++ *status |= FE_HAS_CARRIER; ++ ++ if (val >= 5) ++ *status |= FE_HAS_VITERBI; ++ ++ if (val >= 7) ++ *status |= FE_HAS_SYNC; ++ ++ if (val >= 8) /* Maybe 9? */ ++ *status |= FE_HAS_LOCK; ++ ++ dprintk("val = %d, status = 0x%02x\n", val, *status); ++ ++ return 0; ++} ++ ++static int mb86a20s_set_frontend(struct dvb_frontend *fe) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ int rc; ++#if 0 ++ /* ++ * FIXME: Properly implement the set frontend properties ++ */ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++#endif ++ ++ dprintk("\n"); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ dprintk("Calling tuner set parameters\n"); ++ fe->ops.tuner_ops.set_params(fe); ++ ++ /* ++ * Make it more reliable: if, for some reason, the initial ++ * device initialization doesn't happen, initialize it when ++ * a SBTVD parameters are adjusted. ++ * ++ * Unfortunately, due to a hard to track bug at tda829x/tda18271, ++ * the agc callback logic is not called during DVB attach time, ++ * causing mb86a20s to not be initialized with Kworld SBTVD. ++ * So, this hack is needed, in order to make Kworld SBTVD to work. ++ */ ++ if (state->need_init) ++ mb86a20s_initfe(fe); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ return rc; ++} ++ ++static int mb86a20s_get_modulation(struct mb86a20s_state *state, ++ unsigned layer) ++{ ++ int rc; ++ static unsigned char reg[] = { ++ [0] = 0x86, /* Layer A */ ++ [1] = 0x8a, /* Layer B */ ++ [2] = 0x8e, /* Layer C */ ++ }; ++ ++ if (layer >= ARRAY_SIZE(reg)) ++ return -EINVAL; ++ rc = mb86a20s_writereg(state, 0x6d, reg[layer]); ++ if (rc < 0) ++ return rc; ++ rc = mb86a20s_readreg(state, 0x6e); ++ if (rc < 0) ++ return rc; ++ switch ((rc & 0x70) >> 4) { ++ case 0: ++ return DQPSK; ++ case 1: ++ return QPSK; ++ case 2: ++ return QAM_16; ++ case 3: ++ return QAM_64; ++ default: ++ return QAM_AUTO; ++ } ++} ++ ++static int mb86a20s_get_fec(struct mb86a20s_state *state, ++ unsigned layer) ++{ ++ int rc; ++ ++ static unsigned char reg[] = { ++ [0] = 0x87, /* Layer A */ ++ [1] = 0x8b, /* Layer B */ ++ [2] = 0x8f, /* Layer C */ ++ }; ++ ++ if (layer >= ARRAY_SIZE(reg)) ++ return -EINVAL; ++ rc = mb86a20s_writereg(state, 0x6d, reg[layer]); ++ if (rc < 0) ++ return rc; ++ rc = mb86a20s_readreg(state, 0x6e); ++ if (rc < 0) ++ return rc; ++ switch (rc) { ++ case 0: ++ return FEC_1_2; ++ case 1: ++ return FEC_2_3; ++ case 2: ++ return FEC_3_4; ++ case 3: ++ return FEC_5_6; ++ case 4: ++ return FEC_7_8; ++ default: ++ return FEC_AUTO; ++ } ++} ++ ++static int mb86a20s_get_interleaving(struct mb86a20s_state *state, ++ unsigned layer) ++{ ++ int rc; ++ ++ static unsigned char reg[] = { ++ [0] = 0x88, /* Layer A */ ++ [1] = 0x8c, /* Layer B */ ++ [2] = 0x90, /* Layer C */ ++ }; ++ ++ if (layer >= ARRAY_SIZE(reg)) ++ return -EINVAL; ++ rc = mb86a20s_writereg(state, 0x6d, reg[layer]); ++ if (rc < 0) ++ return rc; ++ rc = mb86a20s_readreg(state, 0x6e); ++ if (rc < 0) ++ return rc; ++ if (rc > 3) ++ return -EINVAL; /* Not used */ ++ return rc; ++} ++ ++static int mb86a20s_get_segment_count(struct mb86a20s_state *state, ++ unsigned layer) ++{ ++ int rc, count; ++ ++ static unsigned char reg[] = { ++ [0] = 0x89, /* Layer A */ ++ [1] = 0x8d, /* Layer B */ ++ [2] = 0x91, /* Layer C */ ++ }; ++ ++ if (layer >= ARRAY_SIZE(reg)) ++ return -EINVAL; ++ rc = mb86a20s_writereg(state, 0x6d, reg[layer]); ++ if (rc < 0) ++ return rc; ++ rc = mb86a20s_readreg(state, 0x6e); ++ if (rc < 0) ++ return rc; ++ count = (rc >> 4) & 0x0f; ++ ++ return count; ++} ++ ++static int mb86a20s_get_frontend(struct dvb_frontend *fe) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ int i, rc; ++ ++ /* Fixed parameters */ ++ p->delivery_system = SYS_ISDBT; ++ p->bandwidth_hz = 6000000; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ /* Check for partial reception */ ++ rc = mb86a20s_writereg(state, 0x6d, 0x85); ++ if (rc >= 0) ++ rc = mb86a20s_readreg(state, 0x6e); ++ if (rc >= 0) ++ p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; ++ ++ /* Get per-layer data */ ++ p->isdbt_layer_enabled = 0; ++ for (i = 0; i < 3; i++) { ++ rc = mb86a20s_get_segment_count(state, i); ++ if (rc >= 0 && rc < 14) ++ p->layer[i].segment_count = rc; ++ if (rc == 0x0f) ++ continue; ++ p->isdbt_layer_enabled |= 1 << i; ++ rc = mb86a20s_get_modulation(state, i); ++ if (rc >= 0) ++ p->layer[i].modulation = rc; ++ rc = mb86a20s_get_fec(state, i); ++ if (rc >= 0) ++ p->layer[i].fec = rc; ++ rc = mb86a20s_get_interleaving(state, i); ++ if (rc >= 0) ++ p->layer[i].interleaving = rc; ++ } ++ ++ p->isdbt_sb_mode = 0; ++ rc = mb86a20s_writereg(state, 0x6d, 0x84); ++ if ((rc >= 0) && ((rc & 0x60) == 0x20)) { ++ p->isdbt_sb_mode = 1; ++ /* At least, one segment should exist */ ++ if (!p->isdbt_sb_segment_count) ++ p->isdbt_sb_segment_count = 1; ++ } else ++ p->isdbt_sb_segment_count = 0; ++ ++ /* Get transmission mode and guard interval */ ++ p->transmission_mode = TRANSMISSION_MODE_AUTO; ++ p->guard_interval = GUARD_INTERVAL_AUTO; ++ rc = mb86a20s_readreg(state, 0x07); ++ if (rc >= 0) { ++ if ((rc & 0x60) == 0x20) { ++ switch (rc & 0x0c >> 2) { ++ case 0: ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ p->transmission_mode = TRANSMISSION_MODE_4K; ++ break; ++ case 2: ++ p->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ } ++ if (!(rc & 0x10)) { ++ switch (rc & 0x3) { ++ case 0: ++ p->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ case 1: ++ p->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 2: ++ p->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ } ++ } ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ return 0; ++} ++ ++static int mb86a20s_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ int rc = 0; ++ ++ dprintk("\n"); ++ ++ if (re_tune) ++ rc = mb86a20s_set_frontend(fe); ++ ++ if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) ++ mb86a20s_read_status(fe, status); ++ ++ return rc; ++} ++ ++static void mb86a20s_release(struct dvb_frontend *fe) ++{ ++ struct mb86a20s_state *state = fe->demodulator_priv; ++ ++ dprintk("\n"); ++ ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops mb86a20s_ops; ++ ++struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, ++ struct i2c_adapter *i2c) ++{ ++ u8 rev; ++ ++ /* allocate memory for the internal state */ ++ struct mb86a20s_state *state = ++ kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); ++ ++ dprintk("\n"); ++ if (state == NULL) { ++ rc("Unable to kzalloc\n"); ++ goto error; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &mb86a20s_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* Check if it is a mb86a20s frontend */ ++ rev = mb86a20s_readreg(state, 0); ++ ++ if (rev == 0x13) { ++ printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); ++ } else { ++ printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", ++ rev); ++ goto error; ++ } ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(mb86a20s_attach); ++ ++static struct dvb_frontend_ops mb86a20s_ops = { ++ .delsys = { SYS_ISDBT }, ++ /* Use dib8000 values per default */ ++ .info = { ++ .name = "Fujitsu mb86A20s", ++ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER | ++ 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_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, ++ /* Actually, those values depend on the used tuner */ ++ .frequency_min = 45000000, ++ .frequency_max = 864000000, ++ .frequency_stepsize = 62500, ++ }, ++ ++ .release = mb86a20s_release, ++ ++ .init = mb86a20s_initfe, ++ .set_frontend = mb86a20s_set_frontend, ++ .get_frontend = mb86a20s_get_frontend, ++ .read_status = mb86a20s_read_status, ++ .read_signal_strength = mb86a20s_read_signal_strength, ++ .tune = mb86a20s_tune, ++}; ++ ++MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); ++MODULE_AUTHOR("Mauro Carvalho Chehab "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/mb86a20s.h b/drivers/media/dvb-frontends/mb86a20s.h +new file mode 100644 +index 0000000..bf22e77 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mb86a20s.h +@@ -0,0 +1,52 @@ ++/* ++ * Fujitsu mb86a20s driver ++ * ++ * Copyright (C) 2010 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 version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#ifndef MB86A20S_H ++#define MB86A20S_H ++ ++#include ++ ++/** ++ * struct mb86a20s_config - Define the per-device attributes of the frontend ++ * ++ * @demod_address: the demodulator's i2c address ++ */ ++ ++struct mb86a20s_config { ++ u8 demod_address; ++ bool is_serial; ++}; ++ ++#if defined(CONFIG_DVB_MB86A20S) || (defined(CONFIG_DVB_MB86A20S_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, ++ struct i2c_adapter *i2c); ++extern struct i2c_adapter *mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *); ++#else ++static inline struct dvb_frontend *mb86a20s_attach( ++ const struct mb86a20s_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static struct i2c_adapter * ++ mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* MB86A20S */ +diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c +new file mode 100644 +index 0000000..ec388c1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt312.c +@@ -0,0 +1,839 @@ ++/* ++ Driver for Zarlink VP310/MT312/ZL10313 Satellite Channel Decoder ++ ++ Copyright (C) 2003 Andreas Oberritter ++ Copyright (C) 2008 Matthias Schwarzott ++ ++ 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. ++ ++ References: ++ http://products.zarlink.com/product_profiles/MT312.htm ++ http://products.zarlink.com/product_profiles/SL1935.htm ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "mt312_priv.h" ++#include "mt312.h" ++ ++ ++struct mt312_state { ++ struct i2c_adapter *i2c; ++ /* configuration settings */ ++ const struct mt312_config *config; ++ struct dvb_frontend frontend; ++ ++ u8 id; ++ unsigned long xtal; ++ u8 freq_mult; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "mt312: " args); \ ++ } while (0) ++ ++#define MT312_PLL_CLK 10000000UL /* 10 MHz */ ++#define MT312_PLL_CLK_10_111 10111000UL /* 10.111 MHz */ ++ ++static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, ++ u8 *buf, const size_t count) ++{ ++ int ret; ++ struct i2c_msg msg[2]; ++ u8 regbuf[1] = { reg }; ++ ++ msg[0].addr = state->config->demod_address; ++ msg[0].flags = 0; ++ msg[0].buf = regbuf; ++ msg[0].len = 1; ++ msg[1].addr = state->config->demod_address; ++ msg[1].flags = I2C_M_RD; ++ msg[1].buf = buf; ++ msg[1].len = count; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_DEBUG "%s: ret == %d\n", __func__, ret); ++ return -EREMOTEIO; ++ } ++ ++ if (debug) { ++ int i; ++ dprintk("R(%d):", reg & 0x7f); ++ for (i = 0; i < count; i++) ++ printk(KERN_CONT " %02x", buf[i]); ++ printk("\n"); ++ } ++ ++ return 0; ++} ++ ++static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, ++ const u8 *src, const size_t count) ++{ ++ int ret; ++ u8 buf[count + 1]; ++ struct i2c_msg msg; ++ ++ if (debug) { ++ int i; ++ dprintk("W(%d):", reg & 0x7f); ++ for (i = 0; i < count; i++) ++ printk(KERN_CONT " %02x", src[i]); ++ printk("\n"); ++ } ++ ++ buf[0] = reg; ++ memcpy(&buf[1], src, count); ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = count + 1; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) { ++ dprintk("%s: ret == %d\n", __func__, ret); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static inline int mt312_readreg(struct mt312_state *state, ++ const enum mt312_reg_addr reg, u8 *val) ++{ ++ return mt312_read(state, reg, val, 1); ++} ++ ++static inline int mt312_writereg(struct mt312_state *state, ++ const enum mt312_reg_addr reg, const u8 val) ++{ ++ return mt312_write(state, reg, &val, 1); ++} ++ ++static inline u32 mt312_div(u32 a, u32 b) ++{ ++ return (a + (b / 2)) / b; ++} ++ ++static int mt312_reset(struct mt312_state *state, const u8 full) ++{ ++ return mt312_writereg(state, RESET, full ? 0x80 : 0x40); ++} ++ ++static int mt312_get_inversion(struct mt312_state *state, ++ fe_spectral_inversion_t *i) ++{ ++ int ret; ++ u8 vit_mode; ++ ++ ret = mt312_readreg(state, VIT_MODE, &vit_mode); ++ if (ret < 0) ++ return ret; ++ ++ if (vit_mode & 0x80) /* auto inversion was used */ ++ *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; ++ ++ return 0; ++} ++ ++static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) ++{ ++ int ret; ++ u8 sym_rate_h; ++ u8 dec_ratio; ++ u16 sym_rat_op; ++ u16 monitor; ++ u8 buf[2]; ++ ++ ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h); ++ if (ret < 0) ++ return ret; ++ ++ if (sym_rate_h & 0x80) { ++ /* symbol rate search was used */ ++ ret = mt312_writereg(state, MON_CTRL, 0x03); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ monitor = (buf[0] << 8) | buf[1]; ++ ++ dprintk("sr(auto) = %u\n", ++ mt312_div(monitor * 15625, 4)); ++ } else { ++ ret = mt312_writereg(state, MON_CTRL, 0x05); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ dec_ratio = ((buf[0] >> 5) & 0x07) * 32; ++ ++ ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ sym_rat_op = (buf[0] << 8) | buf[1]; ++ ++ dprintk("sym_rat_op=%d dec_ratio=%d\n", ++ sym_rat_op, dec_ratio); ++ dprintk("*sr(manual) = %lu\n", ++ (((state->xtal * 8192) / (sym_rat_op + 8192)) * ++ 2) - dec_ratio); ++ } ++ ++ return 0; ++} ++ ++static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr) ++{ ++ const fe_code_rate_t fec_tab[8] = ++ { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, ++ FEC_AUTO, FEC_AUTO }; ++ ++ int ret; ++ u8 fec_status; ++ ++ ret = mt312_readreg(state, FEC_STATUS, &fec_status); ++ if (ret < 0) ++ return ret; ++ ++ *cr = fec_tab[(fec_status >> 4) & 0x07]; ++ ++ return 0; ++} ++ ++static int mt312_initfe(struct dvb_frontend *fe) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ /* wake up */ ++ ret = mt312_writereg(state, CONFIG, ++ (state->freq_mult == 6 ? 0x88 : 0x8c)); ++ if (ret < 0) ++ return ret; ++ ++ /* wait at least 150 usec */ ++ udelay(150); ++ ++ /* full reset */ ++ ret = mt312_reset(state, 1); ++ if (ret < 0) ++ return ret; ++ ++/* Per datasheet, write correct values. 09/28/03 ACCJr. ++ * If we don't do this, we won't get FE_HAS_VITERBI in the VP310. */ ++ { ++ u8 buf_def[8] = { 0x14, 0x12, 0x03, 0x02, ++ 0x01, 0x00, 0x00, 0x00 }; ++ ++ ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ switch (state->id) { ++ case ID_ZL10313: ++ /* enable ADC */ ++ ret = mt312_writereg(state, GPP_CTRL, 0x80); ++ if (ret < 0) ++ return ret; ++ ++ /* configure ZL10313 for optimal ADC performance */ ++ buf[0] = 0x80; ++ buf[1] = 0xB0; ++ ret = mt312_write(state, HW_CTRL, buf, 2); ++ if (ret < 0) ++ return ret; ++ ++ /* enable MPEG output and ADCs */ ++ ret = mt312_writereg(state, HW_CTRL, 0x00); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, MPEG_CTRL, 0x00); ++ if (ret < 0) ++ return ret; ++ ++ break; ++ } ++ ++ /* SYS_CLK */ ++ buf[0] = mt312_div(state->xtal * state->freq_mult * 2, 1000000); ++ ++ /* DISEQC_RATIO */ ++ buf[1] = mt312_div(state->xtal, 22000 * 4); ++ ++ ret = mt312_write(state, SYS_CLK, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, SNR_THS_HIGH, 0x32); ++ if (ret < 0) ++ return ret; ++ ++ /* different MOCLK polarity */ ++ switch (state->id) { ++ case ID_ZL10313: ++ buf[0] = 0x33; ++ break; ++ default: ++ buf[0] = 0x53; ++ break; ++ } ++ ++ ret = mt312_writereg(state, OP_CTRL, buf[0]); ++ if (ret < 0) ++ return ret; ++ ++ /* TS_SW_LIM */ ++ buf[0] = 0x8c; ++ buf[1] = 0x98; ++ ++ ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, CS_SW_LIM, 0x69); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt312_send_master_cmd(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *c) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 diseqc_mode; ++ ++ if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) ++ return -EINVAL; ++ ++ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, DISEQC_MODE, ++ (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3) ++ | 0x04); ++ if (ret < 0) ++ return ret; ++ ++ /* is there a better way to wait for message to be transmitted */ ++ msleep(100); ++ ++ /* set DISEQC_MODE[2:0] to zero if a return message is expected */ ++ if (c->msg[0] & 0x02) { ++ ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ const u8 mini_tab[2] = { 0x02, 0x03 }; ++ ++ int ret; ++ u8 diseqc_mode; ++ ++ if (c > SEC_MINI_B) ++ return -EINVAL; ++ ++ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, DISEQC_MODE, ++ (diseqc_mode & 0x40) | mini_tab[c]); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ const u8 tone_tab[2] = { 0x01, 0x00 }; ++ ++ int ret; ++ u8 diseqc_mode; ++ ++ if (t > SEC_TONE_OFF) ++ return -EINVAL; ++ ++ ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_writereg(state, DISEQC_MODE, ++ (diseqc_mode & 0x40) | tone_tab[t]); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; ++ u8 val; ++ ++ if (v > SEC_VOLTAGE_OFF) ++ return -EINVAL; ++ ++ val = volt_tab[v]; ++ if (state->config->voltage_inverted) ++ val ^= 0x40; ++ ++ return mt312_writereg(state, DISEQC_MODE, val); ++} ++ ++static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 status[3]; ++ ++ *s = 0; ++ ++ ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status)); ++ if (ret < 0) ++ return ret; ++ ++ dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x," ++ " FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]); ++ ++ if (status[0] & 0xc0) ++ *s |= FE_HAS_SIGNAL; /* signal noise ratio */ ++ if (status[0] & 0x04) ++ *s |= FE_HAS_CARRIER; /* qpsk carrier lock */ ++ if (status[2] & 0x02) ++ *s |= FE_HAS_VITERBI; /* viterbi lock */ ++ if (status[2] & 0x04) ++ *s |= FE_HAS_SYNC; /* byte align lock */ ++ if (status[0] & 0x01) ++ *s |= FE_HAS_LOCK; /* qpsk lock */ ++ ++ return 0; ++} ++ ++static int mt312_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[3]; ++ ++ ret = mt312_read(state, RS_BERCNT_H, buf, 3); ++ if (ret < 0) ++ return ret; ++ ++ *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64; ++ ++ return 0; ++} ++ ++static int mt312_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[3]; ++ u16 agc; ++ s16 err_db; ++ ++ ret = mt312_read(state, AGC_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ agc = (buf[0] << 6) | (buf[1] >> 2); ++ err_db = (s16) (((buf[1] & 0x03) << 14) | buf[2] << 6) >> 6; ++ ++ *signal_strength = agc; ++ ++ dprintk("agc=%08x err_db=%hd\n", agc, err_db); ++ ++ return 0; ++} ++ ++static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ ret = mt312_read(state, M_SNR_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1); ++ ++ return 0; ++} ++ ++static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ ret = mt312_read(state, RS_UBC_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ *ubc = (buf[0] << 8) | buf[1]; ++ ++ return 0; ++} ++ ++static int mt312_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 buf[5], config_val; ++ u16 sr; ++ ++ const u8 fec_tab[10] = ++ { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f }; ++ const u8 inv_tab[3] = { 0x00, 0x40, 0x80 }; ++ ++ dprintk("%s: Freq %d\n", __func__, p->frequency); ++ ++ if ((p->frequency < fe->ops.info.frequency_min) ++ || (p->frequency > fe->ops.info.frequency_max)) ++ return -EINVAL; ++ ++ if (((int)p->inversion < INVERSION_OFF) ++ || (p->inversion > INVERSION_ON)) ++ return -EINVAL; ++ ++ if ((p->symbol_rate < fe->ops.info.symbol_rate_min) ++ || (p->symbol_rate > fe->ops.info.symbol_rate_max)) ++ return -EINVAL; ++ ++ if (((int)p->fec_inner < FEC_NONE) ++ || (p->fec_inner > FEC_AUTO)) ++ return -EINVAL; ++ ++ if ((p->fec_inner == FEC_4_5) ++ || (p->fec_inner == FEC_8_9)) ++ return -EINVAL; ++ ++ switch (state->id) { ++ case ID_VP310: ++ /* For now we will do this only for the VP310. ++ * It should be better for the mt312 as well, ++ * but tuning will be slower. ACCJr 09/29/03 ++ */ ++ ret = mt312_readreg(state, CONFIG, &config_val); ++ if (ret < 0) ++ return ret; ++ if (p->symbol_rate >= 30000000) { ++ /* Note that 30MS/s should use 90MHz */ ++ if (state->freq_mult == 6) { ++ /* We are running 60MHz */ ++ state->freq_mult = 9; ++ ret = mt312_initfe(fe); ++ if (ret < 0) ++ return ret; ++ } ++ } else { ++ if (state->freq_mult == 9) { ++ /* We are running 90MHz */ ++ state->freq_mult = 6; ++ ret = mt312_initfe(fe); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ break; ++ ++ case ID_MT312: ++ case ID_ZL10313: ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* sr = (u16)(sr * 256.0 / 1000000.0) */ ++ sr = mt312_div(p->symbol_rate * 4, 15625); ++ ++ /* SYM_RATE */ ++ buf[0] = (sr >> 8) & 0x3f; ++ buf[1] = (sr >> 0) & 0xff; ++ ++ /* VIT_MODE */ ++ buf[2] = inv_tab[p->inversion] | fec_tab[p->fec_inner]; ++ ++ /* QPSK_CTRL */ ++ buf[3] = 0x40; /* swap I and Q before QPSK demodulation */ ++ ++ if (p->symbol_rate < 10000000) ++ buf[3] |= 0x04; /* use afc mode */ ++ ++ /* GO */ ++ buf[4] = 0x01; ++ ++ ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf)); ++ if (ret < 0) ++ return ret; ++ ++ mt312_reset(state, 0); ++ ++ return 0; ++} ++ ++static int mt312_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ ++ ret = mt312_get_inversion(state, &p->inversion); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_get_symbol_rate(state, &p->symbol_rate); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt312_get_code_rate(state, &p->fec_inner); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt312_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ ++ u8 val = 0x00; ++ int ret; ++ ++ switch (state->id) { ++ case ID_ZL10313: ++ ret = mt312_readreg(state, GPP_CTRL, &val); ++ if (ret < 0) ++ goto error; ++ ++ /* preserve this bit to not accidentally shutdown ADC */ ++ val &= 0x80; ++ break; ++ } ++ ++ if (enable) ++ val |= 0x40; ++ else ++ val &= ~0x40; ++ ++ ret = mt312_writereg(state, GPP_CTRL, val); ++ ++error: ++ return ret; ++} ++ ++static int mt312_sleep(struct dvb_frontend *fe) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ int ret; ++ u8 config; ++ ++ /* reset all registers to defaults */ ++ ret = mt312_reset(state, 1); ++ if (ret < 0) ++ return ret; ++ ++ if (state->id == ID_ZL10313) { ++ /* reset ADC */ ++ ret = mt312_writereg(state, GPP_CTRL, 0x00); ++ if (ret < 0) ++ return ret; ++ ++ /* full shutdown of ADCs, mpeg bus tristated */ ++ ret = mt312_writereg(state, HW_CTRL, 0x0d); ++ if (ret < 0) ++ return ret; ++ } ++ ++ ret = mt312_readreg(state, CONFIG, &config); ++ if (ret < 0) ++ return ret; ++ ++ /* enter standby */ ++ ret = mt312_writereg(state, CONFIG, config & 0x7f); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int mt312_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *fesettings) ++{ ++ fesettings->min_delay_ms = 50; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void mt312_release(struct dvb_frontend *fe) ++{ ++ struct mt312_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++#define MT312_SYS_CLK 90000000UL /* 90 MHz */ ++static struct dvb_frontend_ops mt312_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Zarlink ???? DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ /* FIXME: adjust freq to real used xtal */ ++ .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, ++ .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ ++ .symbol_rate_max = MT312_SYS_CLK / 2, ++ .caps = ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | ++ FE_CAN_RECOVER ++ }, ++ ++ .release = mt312_release, ++ ++ .init = mt312_initfe, ++ .sleep = mt312_sleep, ++ .i2c_gate_ctrl = mt312_i2c_gate_ctrl, ++ ++ .set_frontend = mt312_set_frontend, ++ .get_frontend = mt312_get_frontend, ++ .get_tune_settings = mt312_get_tune_settings, ++ ++ .read_status = mt312_read_status, ++ .read_ber = mt312_read_ber, ++ .read_signal_strength = mt312_read_signal_strength, ++ .read_snr = mt312_read_snr, ++ .read_ucblocks = mt312_read_ucblocks, ++ ++ .diseqc_send_master_cmd = mt312_send_master_cmd, ++ .diseqc_send_burst = mt312_send_burst, ++ .set_tone = mt312_set_tone, ++ .set_voltage = mt312_set_voltage, ++}; ++ ++struct dvb_frontend *mt312_attach(const struct mt312_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct mt312_state *state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (mt312_readreg(state, ID, &state->id) < 0) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &mt312_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ switch (state->id) { ++ case ID_VP310: ++ strcpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S"); ++ state->xtal = MT312_PLL_CLK; ++ state->freq_mult = 9; ++ break; ++ case ID_MT312: ++ strcpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S"); ++ state->xtal = MT312_PLL_CLK; ++ state->freq_mult = 6; ++ break; ++ case ID_ZL10313: ++ strcpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S"); ++ state->xtal = MT312_PLL_CLK_10_111; ++ state->freq_mult = 9; ++ break; ++ default: ++ printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313" ++ " are supported chips.\n"); ++ goto error; ++ } ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(mt312_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Zarlink VP310/MT312/ZL10313 DVB-S Demodulator driver"); ++MODULE_AUTHOR("Andreas Oberritter "); ++MODULE_AUTHOR("Matthias Schwarzott "); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/media/dvb-frontends/mt312.h b/drivers/media/dvb-frontends/mt312.h +new file mode 100644 +index 0000000..29e3bb5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt312.h +@@ -0,0 +1,51 @@ ++/* ++ Driver for Zarlink MT312 Satellite Channel Decoder ++ ++ Copyright (C) 2003 Andreas Oberritter ++ ++ 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. ++ ++ References: ++ http://products.zarlink.com/product_profiles/MT312.htm ++ http://products.zarlink.com/product_profiles/SL1935.htm ++*/ ++ ++#ifndef MT312_H ++#define MT312_H ++ ++#include ++ ++struct mt312_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* inverted voltage setting */ ++ unsigned int voltage_inverted:1; ++}; ++ ++#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) ++struct dvb_frontend *mt312_attach(const struct mt312_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *mt312_attach( ++ const struct mt312_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_MT312 */ ++ ++#endif /* MT312_H */ +diff --git a/drivers/media/dvb-frontends/mt312_priv.h b/drivers/media/dvb-frontends/mt312_priv.h +new file mode 100644 +index 0000000..a3959f9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt312_priv.h +@@ -0,0 +1,165 @@ ++/* ++ Driver for Zarlink MT312 QPSK Frontend ++ ++ Copyright (C) 2003 Andreas Oberritter ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef _DVB_FRONTENDS_MT312_PRIV ++#define _DVB_FRONTENDS_MT312_PRIV ++ ++enum mt312_reg_addr { ++ QPSK_INT_H = 0, ++ QPSK_INT_M = 1, ++ QPSK_INT_L = 2, ++ FEC_INT = 3, ++ QPSK_STAT_H = 4, ++ QPSK_STAT_L = 5, ++ FEC_STATUS = 6, ++ LNB_FREQ_H = 7, ++ LNB_FREQ_L = 8, ++ M_SNR_H = 9, ++ M_SNR_L = 10, ++ VIT_ERRCNT_H = 11, ++ VIT_ERRCNT_M = 12, ++ VIT_ERRCNT_L = 13, ++ RS_BERCNT_H = 14, ++ RS_BERCNT_M = 15, ++ RS_BERCNT_L = 16, ++ RS_UBC_H = 17, ++ RS_UBC_L = 18, ++ SIG_LEVEL = 19, ++ GPP_CTRL = 20, ++ RESET = 21, ++ DISEQC_MODE = 22, ++ SYM_RATE_H = 23, ++ SYM_RATE_L = 24, ++ VIT_MODE = 25, ++ QPSK_CTRL = 26, ++ GO = 27, ++ IE_QPSK_H = 28, ++ IE_QPSK_M = 29, ++ IE_QPSK_L = 30, ++ IE_FEC = 31, ++ QPSK_STAT_EN = 32, ++ FEC_STAT_EN = 33, ++ SYS_CLK = 34, ++ DISEQC_RATIO = 35, ++ DISEQC_INSTR = 36, ++ FR_LIM = 37, ++ FR_OFF = 38, ++ AGC_CTRL = 39, ++ AGC_INIT = 40, ++ AGC_REF = 41, ++ AGC_MAX = 42, ++ AGC_MIN = 43, ++ AGC_LK_TH = 44, ++ TS_AGC_LK_TH = 45, ++ AGC_PWR_SET = 46, ++ QPSK_MISC = 47, ++ SNR_THS_LOW = 48, ++ SNR_THS_HIGH = 49, ++ TS_SW_RATE = 50, ++ TS_SW_LIM_L = 51, ++ TS_SW_LIM_H = 52, ++ CS_SW_RATE_1 = 53, ++ CS_SW_RATE_2 = 54, ++ CS_SW_RATE_3 = 55, ++ CS_SW_RATE_4 = 56, ++ CS_SW_LIM = 57, ++ TS_LPK = 58, ++ TS_LPK_M = 59, ++ TS_LPK_L = 60, ++ CS_KPROP_H = 61, ++ CS_KPROP_L = 62, ++ CS_KINT_H = 63, ++ CS_KINT_L = 64, ++ QPSK_SCALE = 65, ++ TLD_OUTCLK_TH = 66, ++ TLD_INCLK_TH = 67, ++ FLD_TH = 68, ++ PLD_OUTLK3 = 69, ++ PLD_OUTLK2 = 70, ++ PLD_OUTLK1 = 71, ++ PLD_OUTLK0 = 72, ++ PLD_INLK3 = 73, ++ PLD_INLK2 = 74, ++ PLD_INLK1 = 75, ++ PLD_INLK0 = 76, ++ PLD_ACC_TIME = 77, ++ SWEEP_PAR = 78, ++ STARTUP_TIME = 79, ++ LOSSLOCK_TH = 80, ++ FEC_LOCK_TM = 81, ++ LOSSLOCK_TM = 82, ++ VIT_ERRPER_H = 83, ++ VIT_ERRPER_M = 84, ++ VIT_ERRPER_L = 85, ++ HW_CTRL = 84, /* ZL10313 only */ ++ MPEG_CTRL = 85, /* ZL10313 only */ ++ VIT_SETUP = 86, ++ VIT_REF0 = 87, ++ VIT_REF1 = 88, ++ VIT_REF2 = 89, ++ VIT_REF3 = 90, ++ VIT_REF4 = 91, ++ VIT_REF5 = 92, ++ VIT_REF6 = 93, ++ VIT_MAXERR = 94, ++ BA_SETUPT = 95, ++ OP_CTRL = 96, ++ FEC_SETUP = 97, ++ PROG_SYNC = 98, ++ AFC_SEAR_TH = 99, ++ CSACC_DIF_TH = 100, ++ QPSK_LK_CT = 101, ++ QPSK_ST_CT = 102, ++ MON_CTRL = 103, ++ QPSK_RESET = 104, ++ QPSK_TST_CT = 105, ++ QPSK_TST_ST = 106, ++ TEST_R = 107, ++ AGC_H = 108, ++ AGC_M = 109, ++ AGC_L = 110, ++ FREQ_ERR1_H = 111, ++ FREQ_ERR1_M = 112, ++ FREQ_ERR1_L = 113, ++ FREQ_ERR2_H = 114, ++ FREQ_ERR2_L = 115, ++ SYM_RAT_OP_H = 116, ++ SYM_RAT_OP_L = 117, ++ DESEQC2_INT = 118, ++ DISEQC2_STAT = 119, ++ DISEQC2_FIFO = 120, ++ DISEQC2_CTRL1 = 121, ++ DISEQC2_CTRL2 = 122, ++ MONITOR_H = 123, ++ MONITOR_L = 124, ++ TEST_MODE = 125, ++ ID = 126, ++ CONFIG = 127 ++}; ++ ++enum mt312_model_id { ++ ID_VP310 = 1, ++ ID_MT312 = 3, ++ ID_ZL10313 = 5, ++}; ++ ++#endif /* DVB_FRONTENDS_MT312_PRIV */ +diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c +new file mode 100644 +index 0000000..2c3b50e +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt352.c +@@ -0,0 +1,610 @@ ++/* ++ * Driver for Zarlink DVB-T MT352 demodulator ++ * ++ * Written by Holger Waechtler ++ * and Daniel Mack ++ * ++ * AVerMedia AVerTV DVB-T 771 support by ++ * Wolfram Joost ++ * ++ * Support for Samsung TDTC9251DH01C(M) tuner ++ * Copyright (C) 2004 Antonio Mancuso ++ * Amauri Celani ++ * ++ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by ++ * Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "mt352_priv.h" ++#include "mt352.h" ++ ++struct mt352_state { ++ struct i2c_adapter* i2c; ++ struct dvb_frontend frontend; ++ ++ /* configuration settings */ ++ struct mt352_config config; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "mt352: " args); \ ++ } while (0) ++ ++static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ u8 buf[2] = { reg, val }; ++ struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, ++ .buf = buf, .len = 2 }; ++ int err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err); ++ return err; ++ } ++ return 0; ++} ++ ++static int _mt352_write(struct dvb_frontend* fe, const u8 ibuf[], int ilen) ++{ ++ int err,i; ++ for (i=0; i < ilen-1; i++) ++ if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1]))) ++ return err; ++ ++ return 0; ++} ++ ++static int mt352_read_register(struct mt352_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config.demod_address, ++ .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = state->config.demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk("%s: readreg error (reg=%d, ret==%i)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ return b1[0]; ++} ++ ++static int mt352_sleep(struct dvb_frontend* fe) ++{ ++ static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; ++ ++ _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); ++ return 0; ++} ++ ++static void mt352_calc_nominal_rate(struct mt352_state* state, ++ u32 bandwidth, ++ unsigned char *buf) ++{ ++ u32 adc_clock = 20480; /* 20.340 MHz */ ++ u32 bw,value; ++ ++ switch (bandwidth) { ++ case 6000000: ++ bw = 6; ++ break; ++ case 7000000: ++ bw = 7; ++ break; ++ case 8000000: ++ default: ++ bw = 8; ++ break; ++ } ++ if (state->config.adc_clock) ++ adc_clock = state->config.adc_clock; ++ ++ value = 64 * bw * (1<<16) / (7 * 8); ++ value = value * 1000 / adc_clock; ++ dprintk("%s: bw %d, adc_clock %d => 0x%x\n", ++ __func__, bw, adc_clock, value); ++ buf[0] = msb(value); ++ buf[1] = lsb(value); ++} ++ ++static void mt352_calc_input_freq(struct mt352_state* state, ++ unsigned char *buf) ++{ ++ int adc_clock = 20480; /* 20.480000 MHz */ ++ int if2 = 36167; /* 36.166667 MHz */ ++ int ife,value; ++ ++ if (state->config.adc_clock) ++ adc_clock = state->config.adc_clock; ++ if (state->config.if2) ++ if2 = state->config.if2; ++ ++ if (adc_clock >= if2 * 2) ++ ife = if2; ++ else { ++ ife = adc_clock - (if2 % adc_clock); ++ if (ife > adc_clock / 2) ++ ife = adc_clock - ife; ++ } ++ value = -16374 * ife / adc_clock; ++ dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", ++ __func__, if2, ife, adc_clock, value, value & 0x3fff); ++ buf[0] = msb(value); ++ buf[1] = lsb(value); ++} ++ ++static int mt352_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *op = &fe->dtv_property_cache; ++ struct mt352_state* state = fe->demodulator_priv; ++ unsigned char buf[13]; ++ static unsigned char tuner_go[] = { 0x5d, 0x01 }; ++ static unsigned char fsm_go[] = { 0x5e, 0x01 }; ++ unsigned int tps = 0; ++ ++ switch (op->code_rate_HP) { ++ case FEC_2_3: ++ tps |= (1 << 7); ++ break; ++ case FEC_3_4: ++ tps |= (2 << 7); ++ break; ++ case FEC_5_6: ++ tps |= (3 << 7); ++ break; ++ case FEC_7_8: ++ tps |= (4 << 7); ++ break; ++ case FEC_1_2: ++ case FEC_AUTO: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (op->code_rate_LP) { ++ case FEC_2_3: ++ tps |= (1 << 4); ++ break; ++ case FEC_3_4: ++ tps |= (2 << 4); ++ break; ++ case FEC_5_6: ++ tps |= (3 << 4); ++ break; ++ case FEC_7_8: ++ tps |= (4 << 4); ++ break; ++ case FEC_1_2: ++ case FEC_AUTO: ++ break; ++ case FEC_NONE: ++ if (op->hierarchy == HIERARCHY_AUTO || ++ op->hierarchy == HIERARCHY_NONE) ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (op->modulation) { ++ case QPSK: ++ break; ++ case QAM_AUTO: ++ case QAM_16: ++ tps |= (1 << 13); ++ break; ++ case QAM_64: ++ tps |= (2 << 13); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (op->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ case TRANSMISSION_MODE_AUTO: ++ break; ++ case TRANSMISSION_MODE_8K: ++ tps |= (1 << 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (op->guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ case GUARD_INTERVAL_AUTO: ++ break; ++ case GUARD_INTERVAL_1_16: ++ tps |= (1 << 2); ++ break; ++ case GUARD_INTERVAL_1_8: ++ tps |= (2 << 2); ++ break; ++ case GUARD_INTERVAL_1_4: ++ tps |= (3 << 2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (op->hierarchy) { ++ case HIERARCHY_AUTO: ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ tps |= (1 << 10); ++ break; ++ case HIERARCHY_2: ++ tps |= (2 << 10); ++ break; ++ case HIERARCHY_4: ++ tps |= (3 << 10); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ++ buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */ ++ ++ buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */ ++ buf[2] = lsb(tps); ++ ++ buf[3] = 0x50; // old ++// buf[3] = 0xf4; // pinnacle ++ ++ mt352_calc_nominal_rate(state, op->bandwidth_hz, buf+4); ++ mt352_calc_input_freq(state, buf+6); ++ ++ if (state->config.no_tuner) { ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ _mt352_write(fe, buf, 8); ++ _mt352_write(fe, fsm_go, 2); ++ } else { ++ if (fe->ops.tuner_ops.calc_regs) { ++ fe->ops.tuner_ops.calc_regs(fe, buf+8, 5); ++ buf[8] <<= 1; ++ _mt352_write(fe, buf, sizeof(buf)); ++ _mt352_write(fe, tuner_go, 2); ++ } ++ } ++ ++ return 0; ++} ++ ++static int mt352_get_parameters(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *op = &fe->dtv_property_cache; ++ struct mt352_state* state = fe->demodulator_priv; ++ u16 tps; ++ u16 div; ++ u8 trl; ++ static const u8 tps_fec_to_api[8] = ++ { ++ FEC_1_2, ++ FEC_2_3, ++ FEC_3_4, ++ FEC_5_6, ++ FEC_7_8, ++ FEC_AUTO, ++ FEC_AUTO, ++ FEC_AUTO ++ }; ++ ++ if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) ++ return -EINVAL; ++ ++ /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because ++ * the mt352 sometimes works with the wrong parameters ++ */ ++ tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); ++ div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); ++ trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); ++ ++ op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; ++ op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; ++ ++ switch ( (tps >> 13) & 3) ++ { ++ case 0: ++ op->modulation = QPSK; ++ break; ++ case 1: ++ op->modulation = QAM_16; ++ break; ++ case 2: ++ op->modulation = QAM_64; ++ break; ++ default: ++ op->modulation = QAM_AUTO; ++ break; ++ } ++ ++ op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K; ++ ++ switch ( (tps >> 2) & 3) ++ { ++ case 0: ++ op->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ op->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ op->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ op->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ default: ++ op->guard_interval = GUARD_INTERVAL_AUTO; ++ break; ++ } ++ ++ switch ( (tps >> 10) & 7) ++ { ++ case 0: ++ op->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ op->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ op->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ op->hierarchy = HIERARCHY_4; ++ break; ++ default: ++ op->hierarchy = HIERARCHY_AUTO; ++ break; ++ } ++ ++ op->frequency = (500 * (div - IF_FREQUENCYx6)) / 3 * 1000; ++ ++ if (trl == 0x72) ++ op->bandwidth_hz = 8000000; ++ else if (trl == 0x64) ++ op->bandwidth_hz = 7000000; ++ else ++ op->bandwidth_hz = 6000000; ++ ++ ++ if (mt352_read_register(state, STATUS_2) & 0x02) ++ op->inversion = INVERSION_OFF; ++ else ++ op->inversion = INVERSION_ON; ++ ++ return 0; ++} ++ ++static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ int s0, s1, s3; ++ ++ /* FIXME: ++ * ++ * The MT352 design manual from Zarlink states (page 46-47): ++ * ++ * Notes about the TUNER_GO register: ++ * ++ * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status ++ * byte is copied from the tuner to the STATUS_3 register and ++ * completion of the read operation is indicated by bit-5 of the ++ * INTERRUPT_3 register. ++ */ ++ ++ if ((s0 = mt352_read_register(state, STATUS_0)) < 0) ++ return -EREMOTEIO; ++ if ((s1 = mt352_read_register(state, STATUS_1)) < 0) ++ return -EREMOTEIO; ++ if ((s3 = mt352_read_register(state, STATUS_3)) < 0) ++ return -EREMOTEIO; ++ ++ *status = 0; ++ if (s0 & (1 << 4)) ++ *status |= FE_HAS_CARRIER; ++ if (s0 & (1 << 1)) ++ *status |= FE_HAS_VITERBI; ++ if (s0 & (1 << 5)) ++ *status |= FE_HAS_LOCK; ++ if (s1 & (1 << 1)) ++ *status |= FE_HAS_SYNC; ++ if (s3 & (1 << 6)) ++ *status |= FE_HAS_SIGNAL; ++ ++ if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != ++ (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) ++ *status &= ~FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int mt352_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ ++ *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | ++ (mt352_read_register (state, RS_ERR_CNT_1) << 8) | ++ (mt352_read_register (state, RS_ERR_CNT_0)); ++ ++ return 0; ++} ++ ++static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ ++ /* align the 12 bit AGC gain with the most significant bits */ ++ u16 signal = ((mt352_read_register(state, AGC_GAIN_1) & 0x0f) << 12) | ++ (mt352_read_register(state, AGC_GAIN_0) << 4); ++ ++ /* inverse of gain is signal strength */ ++ *strength = ~signal; ++ return 0; ++} ++ ++static int mt352_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ ++ u8 _snr = mt352_read_register (state, SNR); ++ *snr = (_snr << 8) | _snr; ++ ++ return 0; ++} ++ ++static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ ++ *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | ++ (mt352_read_register (state, RS_UBC_0)); ++ ++ return 0; ++} ++ ++static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 800; ++ fe_tune_settings->step_size = 0; ++ fe_tune_settings->max_drift = 0; ++ ++ return 0; ++} ++ ++static int mt352_init(struct dvb_frontend* fe) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ ++ static u8 mt352_reset_attach [] = { RESET, 0xC0 }; ++ ++ dprintk("%s: hello\n",__func__); ++ ++ if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || ++ (mt352_read_register(state, CONFIG) & 0x20) == 0) { ++ ++ /* Do a "hard" reset */ ++ _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); ++ return state->config.demod_init(fe); ++ } ++ ++ return 0; ++} ++ ++static void mt352_release(struct dvb_frontend* fe) ++{ ++ struct mt352_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops mt352_ops; ++ ++struct dvb_frontend* mt352_attach(const struct mt352_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct mt352_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct mt352_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->config,config,sizeof(struct mt352_config)); ++ ++ /* check if the demod is there */ ++ if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops mt352_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Zarlink MT352 DVB-T", ++ .frequency_min = 174000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 166667, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = mt352_release, ++ ++ .init = mt352_init, ++ .sleep = mt352_sleep, ++ .write = _mt352_write, ++ ++ .set_frontend = mt352_set_parameters, ++ .get_frontend = mt352_get_parameters, ++ .get_tune_settings = mt352_get_tune_settings, ++ ++ .read_status = mt352_read_status, ++ .read_ber = mt352_read_ber, ++ .read_signal_strength = mt352_read_signal_strength, ++ .read_snr = mt352_read_snr, ++ .read_ucblocks = mt352_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(mt352_attach); +diff --git a/drivers/media/dvb-frontends/mt352.h b/drivers/media/dvb-frontends/mt352.h +new file mode 100644 +index 0000000..ca2562d +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt352.h +@@ -0,0 +1,73 @@ ++/* ++ * Driver for Zarlink DVB-T MT352 demodulator ++ * ++ * Written by Holger Waechtler ++ * and Daniel Mack ++ * ++ * AVerMedia AVerTV DVB-T 771 support by ++ * Wolfram Joost ++ * ++ * Support for Samsung TDTC9251DH01C(M) tuner ++ * Copyright (C) 2004 Antonio Mancuso ++ * Amauri Celani ++ * ++ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by ++ * Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef MT352_H ++#define MT352_H ++ ++#include ++ ++struct mt352_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* frequencies in kHz */ ++ int adc_clock; // default: 20480 ++ int if2; // default: 36166 ++ ++ /* set if no pll is connected to the secondary i2c bus */ ++ int no_tuner; ++ ++ /* Initialise the demodulator and PLL. Cannot be NULL */ ++ int (*demod_init)(struct dvb_frontend* fe); ++}; ++ ++#if defined(CONFIG_DVB_MT352) || (defined(CONFIG_DVB_MT352_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_MT352 ++ ++static inline int mt352_write(struct dvb_frontend *fe, const u8 buf[], int len) { ++ int r = 0; ++ if (fe->ops.write) ++ r = fe->ops.write(fe, buf, len); ++ return r; ++} ++ ++#endif // MT352_H +diff --git a/drivers/media/dvb-frontends/mt352_priv.h b/drivers/media/dvb-frontends/mt352_priv.h +new file mode 100644 +index 0000000..44ad0d4 +--- /dev/null ++++ b/drivers/media/dvb-frontends/mt352_priv.h +@@ -0,0 +1,127 @@ ++/* ++ * Driver for Zarlink DVB-T MT352 demodulator ++ * ++ * Written by Holger Waechtler ++ * and Daniel Mack ++ * ++ * AVerMedia AVerTV DVB-T 771 support by ++ * Wolfram Joost ++ * ++ * Support for Samsung TDTC9251DH01C(M) tuner ++ * Copyright (C) 2004 Antonio Mancuso ++ * Amauri Celani ++ * ++ * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by ++ * Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef _MT352_PRIV_ ++#define _MT352_PRIV_ ++ ++#define ID_MT352 0x13 ++ ++#define msb(x) (((x) >> 8) & 0xff) ++#define lsb(x) ((x) & 0xff) ++ ++enum mt352_reg_addr { ++ STATUS_0 = 0x00, ++ STATUS_1 = 0x01, ++ STATUS_2 = 0x02, ++ STATUS_3 = 0x03, ++ STATUS_4 = 0x04, ++ INTERRUPT_0 = 0x05, ++ INTERRUPT_1 = 0x06, ++ INTERRUPT_2 = 0x07, ++ INTERRUPT_3 = 0x08, ++ SNR = 0x09, ++ VIT_ERR_CNT_2 = 0x0A, ++ VIT_ERR_CNT_1 = 0x0B, ++ VIT_ERR_CNT_0 = 0x0C, ++ RS_ERR_CNT_2 = 0x0D, ++ RS_ERR_CNT_1 = 0x0E, ++ RS_ERR_CNT_0 = 0x0F, ++ RS_UBC_1 = 0x10, ++ RS_UBC_0 = 0x11, ++ AGC_GAIN_3 = 0x12, ++ AGC_GAIN_2 = 0x13, ++ AGC_GAIN_1 = 0x14, ++ AGC_GAIN_0 = 0x15, ++ FREQ_OFFSET_2 = 0x17, ++ FREQ_OFFSET_1 = 0x18, ++ FREQ_OFFSET_0 = 0x19, ++ TIMING_OFFSET_1 = 0x1A, ++ TIMING_OFFSET_0 = 0x1B, ++ CHAN_FREQ_1 = 0x1C, ++ CHAN_FREQ_0 = 0x1D, ++ TPS_RECEIVED_1 = 0x1E, ++ TPS_RECEIVED_0 = 0x1F, ++ TPS_CURRENT_1 = 0x20, ++ TPS_CURRENT_0 = 0x21, ++ TPS_CELL_ID_1 = 0x22, ++ TPS_CELL_ID_0 = 0x23, ++ TPS_MISC_DATA_2 = 0x24, ++ TPS_MISC_DATA_1 = 0x25, ++ TPS_MISC_DATA_0 = 0x26, ++ RESET = 0x50, ++ TPS_GIVEN_1 = 0x51, ++ TPS_GIVEN_0 = 0x52, ++ ACQ_CTL = 0x53, ++ TRL_NOMINAL_RATE_1 = 0x54, ++ TRL_NOMINAL_RATE_0 = 0x55, ++ INPUT_FREQ_1 = 0x56, ++ INPUT_FREQ_0 = 0x57, ++ TUNER_ADDR = 0x58, ++ CHAN_START_1 = 0x59, ++ CHAN_START_0 = 0x5A, ++ CONT_1 = 0x5B, ++ CONT_0 = 0x5C, ++ TUNER_GO = 0x5D, ++ STATUS_EN_0 = 0x5F, ++ STATUS_EN_1 = 0x60, ++ INTERRUPT_EN_0 = 0x61, ++ INTERRUPT_EN_1 = 0x62, ++ INTERRUPT_EN_2 = 0x63, ++ INTERRUPT_EN_3 = 0x64, ++ AGC_TARGET = 0x67, ++ AGC_CTL = 0x68, ++ CAPT_RANGE = 0x75, ++ SNR_SELECT_1 = 0x79, ++ SNR_SELECT_0 = 0x7A, ++ RS_ERR_PER_1 = 0x7C, ++ RS_ERR_PER_0 = 0x7D, ++ CHIP_ID = 0x7F, ++ CHAN_STOP_1 = 0x80, ++ CHAN_STOP_0 = 0x81, ++ CHAN_STEP_1 = 0x82, ++ CHAN_STEP_0 = 0x83, ++ FEC_LOCK_TIME = 0x85, ++ OFDM_LOCK_TIME = 0x86, ++ ACQ_DELAY = 0x87, ++ SCAN_CTL = 0x88, ++ CLOCK_CTL = 0x89, ++ CONFIG = 0x8A, ++ MCLK_RATIO = 0x8B, ++ GPP_CTL = 0x8C, ++ ADC_CTL_1 = 0x8E, ++ ADC_CTL_0 = 0x8F ++}; ++ ++/* here we assume 1/6MHz == 166.66kHz stepsize */ ++#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ ++ ++#endif /* _MT352_PRIV_ */ +diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c +new file mode 100644 +index 0000000..8e28894 +--- /dev/null ++++ b/drivers/media/dvb-frontends/nxt200x.c +@@ -0,0 +1,1242 @@ ++/* ++ * Support for NXT2002 and NXT2004 - VSB/QAM ++ * ++ * Copyright (C) 2005 Kirk Lapray ++ * Copyright (C) 2006 Michael Krufky ++ * based on nxt2002 by Taylor Jacob ++ * and nxt2004 by Jean-Francois Thibert ++ * ++ * 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. ++ * ++*/ ++ ++/* ++ * NOTES ABOUT THIS DRIVER ++ * ++ * This Linux driver supports: ++ * B2C2/BBTI Technisat Air2PC - ATSC (NXT2002) ++ * AverTVHD MCE A180 (NXT2004) ++ * ATI HDTV Wonder (NXT2004) ++ * ++ * This driver needs external firmware. Please use the command ++ * "/Documentation/dvb/get_dvb_firmware nxt2002" or ++ * "/Documentation/dvb/get_dvb_firmware nxt2004" to ++ * download/extract the appropriate firmware, and then copy it to ++ * /usr/lib/hotplug/firmware/ or /lib/firmware/ ++ * (depending on configuration of firmware hotplug). ++ */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw" ++#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw" ++#define CRC_CCIT_MASK 0x1021 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "nxt200x.h" ++ ++struct nxt200x_state { ++ ++ struct i2c_adapter* i2c; ++ const struct nxt200x_config* config; ++ struct dvb_frontend frontend; ++ ++ /* demodulator private data */ ++ nxt_chip_type demod_chip; ++ u8 initialised:1; ++}; ++ ++static int debug; ++#define dprintk(args...) do { if (debug) pr_debug(args); } while (0) ++ ++static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len) ++{ ++ int err; ++ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len }; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n", ++ __func__, addr, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len) ++{ ++ int err; ++ struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len }; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n", ++ __func__, addr, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg, ++ const u8 *buf, u8 len) ++{ ++ u8 buf2 [len+1]; ++ int err; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 }; ++ ++ buf2[0] = reg; ++ memcpy(&buf2[1], buf, len); ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ pr_warn("%s: i2c write error (addr 0x%02x, err == %i)\n", ++ __func__, state->config->demod_address, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len) ++{ ++ u8 reg2 [] = { reg }; ++ ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } }; ++ ++ int err; ++ ++ if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { ++ pr_warn("%s: i2c read error (addr 0x%02x, err == %i)\n", ++ __func__, state->config->demod_address, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static u16 nxt200x_crc(u16 crc, u8 c) ++{ ++ u8 i; ++ u16 input = (u16) c & 0xFF; ++ ++ input<<=8; ++ for(i=0; i<8; i++) { ++ if((crc^input) & 0x8000) ++ crc=(crc<<1)^CRC_CCIT_MASK; ++ else ++ crc<<=1; ++ input<<=1; ++ } ++ return crc; ++} ++ ++static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) ++{ ++ u8 attr, len2, buf; ++ dprintk("%s\n", __func__); ++ ++ /* set mutli register register */ ++ nxt200x_writebytes(state, 0x35, ®, 1); ++ ++ /* send the actual data */ ++ nxt200x_writebytes(state, 0x36, data, len); ++ ++ switch (state->demod_chip) { ++ case NXT2002: ++ len2 = len; ++ buf = 0x02; ++ break; ++ case NXT2004: ++ /* probably not right, but gives correct values */ ++ attr = 0x02; ++ if (reg & 0x80) { ++ attr = attr << 1; ++ if (reg & 0x04) ++ attr = attr >> 1; ++ } ++ /* set write bit */ ++ len2 = ((attr << 4) | 0x10) | len; ++ buf = 0x80; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ /* set multi register length */ ++ nxt200x_writebytes(state, 0x34, &len2, 1); ++ ++ /* toggle the multireg write bit */ ++ nxt200x_writebytes(state, 0x21, &buf, 1); ++ ++ nxt200x_readbytes(state, 0x21, &buf, 1); ++ ++ switch (state->demod_chip) { ++ case NXT2002: ++ if ((buf & 0x02) == 0) ++ return 0; ++ break; ++ case NXT2004: ++ if (buf == 0) ++ return 0; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ pr_warn("Error writing multireg register 0x%02X\n", reg); ++ ++ return 0; ++} ++ ++static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) ++{ ++ int i; ++ u8 buf, len2, attr; ++ dprintk("%s\n", __func__); ++ ++ /* set mutli register register */ ++ nxt200x_writebytes(state, 0x35, ®, 1); ++ ++ switch (state->demod_chip) { ++ case NXT2002: ++ /* set multi register length */ ++ len2 = len & 0x80; ++ nxt200x_writebytes(state, 0x34, &len2, 1); ++ ++ /* read the actual data */ ++ nxt200x_readbytes(state, reg, data, len); ++ return 0; ++ break; ++ case NXT2004: ++ /* probably not right, but gives correct values */ ++ attr = 0x02; ++ if (reg & 0x80) { ++ attr = attr << 1; ++ if (reg & 0x04) ++ attr = attr >> 1; ++ } ++ ++ /* set multi register length */ ++ len2 = (attr << 4) | len; ++ nxt200x_writebytes(state, 0x34, &len2, 1); ++ ++ /* toggle the multireg bit*/ ++ buf = 0x80; ++ nxt200x_writebytes(state, 0x21, &buf, 1); ++ ++ /* read the actual data */ ++ for(i = 0; i < len; i++) { ++ nxt200x_readbytes(state, 0x36 + i, &data[i], 1); ++ } ++ return 0; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++} ++ ++static void nxt200x_microcontroller_stop (struct nxt200x_state* state) ++{ ++ u8 buf, stopval, counter = 0; ++ dprintk("%s\n", __func__); ++ ++ /* set correct stop value */ ++ switch (state->demod_chip) { ++ case NXT2002: ++ stopval = 0x40; ++ break; ++ case NXT2004: ++ stopval = 0x10; ++ break; ++ default: ++ stopval = 0; ++ break; ++ } ++ ++ buf = 0x80; ++ nxt200x_writebytes(state, 0x22, &buf, 1); ++ ++ while (counter < 20) { ++ nxt200x_readbytes(state, 0x31, &buf, 1); ++ if (buf & stopval) ++ return; ++ msleep(10); ++ counter++; ++ } ++ ++ pr_warn("Timeout waiting for nxt200x to stop. This is ok after " ++ "firmware upload.\n"); ++ return; ++} ++ ++static void nxt200x_microcontroller_start (struct nxt200x_state* state) ++{ ++ u8 buf; ++ dprintk("%s\n", __func__); ++ ++ buf = 0x00; ++ nxt200x_writebytes(state, 0x22, &buf, 1); ++} ++ ++static void nxt2004_microcontroller_init (struct nxt200x_state* state) ++{ ++ u8 buf[9]; ++ u8 counter = 0; ++ dprintk("%s\n", __func__); ++ ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x2b, buf, 1); ++ buf[0] = 0x70; ++ nxt200x_writebytes(state, 0x34, buf, 1); ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x35, buf, 1); ++ buf[0] = 0x01; buf[1] = 0x23; buf[2] = 0x45; buf[3] = 0x67; buf[4] = 0x89; ++ buf[5] = 0xAB; buf[6] = 0xCD; buf[7] = 0xEF; buf[8] = 0xC0; ++ nxt200x_writebytes(state, 0x36, buf, 9); ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x21, buf, 1); ++ ++ while (counter < 20) { ++ nxt200x_readbytes(state, 0x21, buf, 1); ++ if (buf[0] == 0) ++ return; ++ msleep(10); ++ counter++; ++ } ++ ++ pr_warn("Timeout waiting for nxt2004 to init.\n"); ++ ++ return; ++} ++ ++static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) ++{ ++ u8 buf, count = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ dprintk("Tuner Bytes: %*ph\n", 4, data + 1); ++ ++ /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip. ++ * direct write is required for Philips TUV1236D and ALPS TDHU2 */ ++ switch (state->demod_chip) { ++ case NXT2004: ++ if (i2c_writebytes(state, data[0], data+1, 4)) ++ pr_warn("error writing to tuner\n"); ++ /* wait until we have a lock */ ++ while (count < 20) { ++ i2c_readbytes(state, data[0], &buf, 1); ++ if (buf & 0x40) ++ return 0; ++ msleep(100); ++ count++; ++ } ++ pr_warn("timeout waiting for tuner lock\n"); ++ break; ++ case NXT2002: ++ /* set the i2c transfer speed to the tuner */ ++ buf = 0x03; ++ nxt200x_writebytes(state, 0x20, &buf, 1); ++ ++ /* setup to transfer 4 bytes via i2c */ ++ buf = 0x04; ++ nxt200x_writebytes(state, 0x34, &buf, 1); ++ ++ /* write actual tuner bytes */ ++ nxt200x_writebytes(state, 0x36, data+1, 4); ++ ++ /* set tuner i2c address */ ++ buf = data[0] << 1; ++ nxt200x_writebytes(state, 0x35, &buf, 1); ++ ++ /* write UC Opmode to begin transfer */ ++ buf = 0x80; ++ nxt200x_writebytes(state, 0x21, &buf, 1); ++ ++ while (count < 20) { ++ nxt200x_readbytes(state, 0x21, &buf, 1); ++ if ((buf & 0x80)== 0x00) ++ return 0; ++ msleep(100); ++ count++; ++ } ++ pr_warn("timeout error writing to tuner\n"); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ return 0; ++} ++ ++static void nxt200x_agc_reset(struct nxt200x_state* state) ++{ ++ u8 buf; ++ dprintk("%s\n", __func__); ++ ++ switch (state->demod_chip) { ++ case NXT2002: ++ buf = 0x08; ++ nxt200x_writebytes(state, 0x08, &buf, 1); ++ buf = 0x00; ++ nxt200x_writebytes(state, 0x08, &buf, 1); ++ break; ++ case NXT2004: ++ nxt200x_readreg_multibyte(state, 0x08, &buf, 1); ++ buf = 0x08; ++ nxt200x_writereg_multibyte(state, 0x08, &buf, 1); ++ buf = 0x00; ++ nxt200x_writereg_multibyte(state, 0x08, &buf, 1); ++ break; ++ default: ++ break; ++ } ++ return; ++} ++ ++static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) ++{ ++ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 buf[3], written = 0, chunkpos = 0; ++ u16 rambase, position, crc = 0; ++ ++ dprintk("%s\n", __func__); ++ dprintk("Firmware is %zu bytes\n", fw->size); ++ ++ /* Get the RAM base for this nxt2002 */ ++ nxt200x_readbytes(state, 0x10, buf, 1); ++ ++ if (buf[0] & 0x10) ++ rambase = 0x1000; ++ else ++ rambase = 0x0000; ++ ++ dprintk("rambase on this nxt2002 is %04X\n", rambase); ++ ++ /* Hold the micro in reset while loading firmware */ ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x2B, buf, 1); ++ ++ for (position = 0; position < fw->size; position++) { ++ if (written == 0) { ++ crc = 0; ++ chunkpos = 0x28; ++ buf[0] = ((rambase + position) >> 8); ++ buf[1] = (rambase + position) & 0xFF; ++ buf[2] = 0x81; ++ /* write starting address */ ++ nxt200x_writebytes(state, 0x29, buf, 3); ++ } ++ written++; ++ chunkpos++; ++ ++ if ((written % 4) == 0) ++ nxt200x_writebytes(state, chunkpos, &fw->data[position-3], 4); ++ ++ crc = nxt200x_crc(crc, fw->data[position]); ++ ++ if ((written == 255) || (position+1 == fw->size)) { ++ /* write remaining bytes of firmware */ ++ nxt200x_writebytes(state, chunkpos+4-(written %4), ++ &fw->data[position-(written %4) + 1], ++ written %4); ++ buf[0] = crc << 8; ++ buf[1] = crc & 0xFF; ++ ++ /* write crc */ ++ nxt200x_writebytes(state, 0x2C, buf, 2); ++ ++ /* do a read to stop things */ ++ nxt200x_readbytes(state, 0x2A, buf, 1); ++ ++ /* set transfer mode to complete */ ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x2B, buf, 1); ++ ++ written = 0; ++ } ++ } ++ ++ return 0; ++}; ++ ++static int nxt2004_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) ++{ ++ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 buf[3]; ++ u16 rambase, position, crc=0; ++ ++ dprintk("%s\n", __func__); ++ dprintk("Firmware is %zu bytes\n", fw->size); ++ ++ /* set rambase */ ++ rambase = 0x1000; ++ ++ /* hold the micro in reset while loading firmware */ ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x2B, buf,1); ++ ++ /* calculate firmware CRC */ ++ for (position = 0; position < fw->size; position++) { ++ crc = nxt200x_crc(crc, fw->data[position]); ++ } ++ ++ buf[0] = rambase >> 8; ++ buf[1] = rambase & 0xFF; ++ buf[2] = 0x81; ++ /* write starting address */ ++ nxt200x_writebytes(state,0x29,buf,3); ++ ++ for (position = 0; position < fw->size;) { ++ nxt200x_writebytes(state, 0x2C, &fw->data[position], ++ fw->size-position > 255 ? 255 : fw->size-position); ++ position += (fw->size-position > 255 ? 255 : fw->size-position); ++ } ++ buf[0] = crc >> 8; ++ buf[1] = crc & 0xFF; ++ ++ dprintk("firmware crc is 0x%02X 0x%02X\n", buf[0], buf[1]); ++ ++ /* write crc */ ++ nxt200x_writebytes(state, 0x2C, buf,2); ++ ++ /* do a read to stop things */ ++ nxt200x_readbytes(state, 0x2C, buf, 1); ++ ++ /* set transfer mode to complete */ ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x2B, buf,1); ++ ++ return 0; ++}; ++ ++static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 buf[5]; ++ ++ /* stop the micro first */ ++ nxt200x_microcontroller_stop(state); ++ ++ if (state->demod_chip == NXT2004) { ++ /* make sure demod is set to digital */ ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x14, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x17, buf, 1); ++ } ++ ++ /* set additional params */ ++ switch (p->modulation) { ++ case QAM_64: ++ case QAM_256: ++ /* Set punctured clock for QAM */ ++ /* This is just a guess since I am unable to test it */ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 1); ++ break; ++ case VSB_8: ++ /* Set non-punctured clock for VSB */ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ if (fe->ops.tuner_ops.calc_regs) { ++ /* get tuning information */ ++ fe->ops.tuner_ops.calc_regs(fe, buf, 5); ++ ++ /* write frequency information */ ++ nxt200x_writetuner(state, buf); ++ } ++ ++ /* reset the agc now that tuning has been completed */ ++ nxt200x_agc_reset(state); ++ ++ /* set target power level */ ++ switch (p->modulation) { ++ case QAM_64: ++ case QAM_256: ++ buf[0] = 0x74; ++ break; ++ case VSB_8: ++ buf[0] = 0x70; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ nxt200x_writebytes(state, 0x42, buf, 1); ++ ++ /* configure sdm */ ++ switch (state->demod_chip) { ++ case NXT2002: ++ buf[0] = 0x87; ++ break; ++ case NXT2004: ++ buf[0] = 0x07; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ nxt200x_writebytes(state, 0x57, buf, 1); ++ ++ /* write sdm1 input */ ++ buf[0] = 0x10; ++ buf[1] = 0x00; ++ switch (state->demod_chip) { ++ case NXT2002: ++ nxt200x_writereg_multibyte(state, 0x58, buf, 2); ++ break; ++ case NXT2004: ++ nxt200x_writebytes(state, 0x58, buf, 2); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ /* write sdmx input */ ++ switch (p->modulation) { ++ case QAM_64: ++ buf[0] = 0x68; ++ break; ++ case QAM_256: ++ buf[0] = 0x64; ++ break; ++ case VSB_8: ++ buf[0] = 0x60; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ buf[1] = 0x00; ++ switch (state->demod_chip) { ++ case NXT2002: ++ nxt200x_writereg_multibyte(state, 0x5C, buf, 2); ++ break; ++ case NXT2004: ++ nxt200x_writebytes(state, 0x5C, buf, 2); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ /* write adc power lpf fc */ ++ buf[0] = 0x05; ++ nxt200x_writebytes(state, 0x43, buf, 1); ++ ++ if (state->demod_chip == NXT2004) { ++ /* write ??? */ ++ buf[0] = 0x00; ++ buf[1] = 0x00; ++ nxt200x_writebytes(state, 0x46, buf, 2); ++ } ++ ++ /* write accumulator2 input */ ++ buf[0] = 0x80; ++ buf[1] = 0x00; ++ switch (state->demod_chip) { ++ case NXT2002: ++ nxt200x_writereg_multibyte(state, 0x4B, buf, 2); ++ break; ++ case NXT2004: ++ nxt200x_writebytes(state, 0x4B, buf, 2); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ /* write kg1 */ ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x4D, buf, 1); ++ ++ /* write sdm12 lpf fc */ ++ buf[0] = 0x44; ++ nxt200x_writebytes(state, 0x55, buf, 1); ++ ++ /* write agc control reg */ ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x41, buf, 1); ++ ++ if (state->demod_chip == NXT2004) { ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x24; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ /* soft reset? */ ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x10; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x04; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x81, buf, 1); ++ buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x82, buf, 3); ++ nxt200x_readreg_multibyte(state, 0x88, buf, 1); ++ buf[0] = 0x11; ++ nxt200x_writereg_multibyte(state, 0x88, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x44; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ } ++ ++ /* write agc ucgp0 */ ++ switch (p->modulation) { ++ case QAM_64: ++ buf[0] = 0x02; ++ break; ++ case QAM_256: ++ buf[0] = 0x03; ++ break; ++ case VSB_8: ++ buf[0] = 0x00; ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ nxt200x_writebytes(state, 0x30, buf, 1); ++ ++ /* write agc control reg */ ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x41, buf, 1); ++ ++ /* write accumulator2 input */ ++ buf[0] = 0x80; ++ buf[1] = 0x00; ++ switch (state->demod_chip) { ++ case NXT2002: ++ nxt200x_writereg_multibyte(state, 0x49, buf, 2); ++ nxt200x_writereg_multibyte(state, 0x4B, buf, 2); ++ break; ++ case NXT2004: ++ nxt200x_writebytes(state, 0x49, buf, 2); ++ nxt200x_writebytes(state, 0x4B, buf, 2); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ ++ /* write agc control reg */ ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x41, buf, 1); ++ ++ nxt200x_microcontroller_start(state); ++ ++ if (state->demod_chip == NXT2004) { ++ nxt2004_microcontroller_init(state); ++ ++ /* ???? */ ++ buf[0] = 0xF0; ++ buf[1] = 0x00; ++ nxt200x_writebytes(state, 0x5C, buf, 2); ++ } ++ ++ /* adjacent channel detection should be done here, but I don't ++ have any stations with this need so I cannot test it */ ++ ++ return 0; ++} ++ ++static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 lock; ++ nxt200x_readbytes(state, 0x31, &lock, 1); ++ ++ *status = 0; ++ if (lock & 0x20) { ++ *status |= FE_HAS_SIGNAL; ++ *status |= FE_HAS_CARRIER; ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ *status |= FE_HAS_LOCK; ++ } ++ return 0; ++} ++ ++static int nxt200x_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 b[3]; ++ ++ nxt200x_readreg_multibyte(state, 0xE6, b, 3); ++ ++ *ber = ((b[0] << 8) + b[1]) * 8; ++ ++ return 0; ++} ++ ++static int nxt200x_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 b[2]; ++ u16 temp = 0; ++ ++ /* setup to read cluster variance */ ++ b[0] = 0x00; ++ nxt200x_writebytes(state, 0xA1, b, 1); ++ ++ /* get multreg val */ ++ nxt200x_readreg_multibyte(state, 0xA6, b, 2); ++ ++ temp = (b[0] << 8) | b[1]; ++ *strength = ((0x7FFF - temp) & 0x0FFF) * 16; ++ ++ return 0; ++} ++ ++static int nxt200x_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 b[2]; ++ u16 temp = 0, temp2; ++ u32 snrdb = 0; ++ ++ /* setup to read cluster variance */ ++ b[0] = 0x00; ++ nxt200x_writebytes(state, 0xA1, b, 1); ++ ++ /* get multreg val from 0xA6 */ ++ nxt200x_readreg_multibyte(state, 0xA6, b, 2); ++ ++ temp = (b[0] << 8) | b[1]; ++ temp2 = 0x7FFF - temp; ++ ++ /* snr will be in db */ ++ if (temp2 > 0x7F00) ++ snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) ); ++ else if (temp2 > 0x7EC0) ++ snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) ); ++ else if (temp2 > 0x7C00) ++ snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) ); ++ else ++ snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) ); ++ ++ /* the value reported back from the frontend will be FFFF=32db 0000=0db */ ++ *snr = snrdb * (0xFFFF/32000); ++ ++ return 0; ++} ++ ++static int nxt200x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ u8 b[3]; ++ ++ nxt200x_readreg_multibyte(state, 0xE6, b, 3); ++ *ucblocks = b[2]; ++ ++ return 0; ++} ++ ++static int nxt200x_sleep(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int nxt2002_init(struct dvb_frontend* fe) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ const struct firmware *fw; ++ int ret; ++ u8 buf[2]; ++ ++ /* request the firmware, this will block until someone uploads it */ ++ pr_debug("%s: Waiting for firmware upload (%s)...\n", ++ __func__, NXT2002_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__); ++ if (ret) { ++ pr_err("%s: No firmware uploaded (timeout or file not found?)" ++ "\n", __func__); ++ return ret; ++ } ++ ++ ret = nxt2002_load_firmware(fe, fw); ++ release_firmware(fw); ++ if (ret) { ++ pr_err("%s: Writing firmware to device failed\n", __func__); ++ return ret; ++ } ++ pr_info("%s: Firmware upload complete\n", __func__); ++ ++ /* Put the micro into reset */ ++ nxt200x_microcontroller_stop(state); ++ ++ /* ensure transfer is complete */ ++ buf[0]=0x00; ++ nxt200x_writebytes(state, 0x2B, buf, 1); ++ ++ /* Put the micro into reset for real this time */ ++ nxt200x_microcontroller_stop(state); ++ ++ /* soft reset everything (agc,frontend,eq,fec)*/ ++ buf[0] = 0x0F; ++ nxt200x_writebytes(state, 0x08, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x08, buf, 1); ++ ++ /* write agc sdm configure */ ++ buf[0] = 0xF1; ++ nxt200x_writebytes(state, 0x57, buf, 1); ++ ++ /* write mod output format */ ++ buf[0] = 0x20; ++ nxt200x_writebytes(state, 0x09, buf, 1); ++ ++ /* write fec mpeg mode */ ++ buf[0] = 0x7E; ++ buf[1] = 0x00; ++ nxt200x_writebytes(state, 0xE9, buf, 2); ++ ++ /* write mux selection */ ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0xCC, buf, 1); ++ ++ return 0; ++} ++ ++static int nxt2004_init(struct dvb_frontend* fe) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ const struct firmware *fw; ++ int ret; ++ u8 buf[3]; ++ ++ /* ??? */ ++ buf[0]=0x00; ++ nxt200x_writebytes(state, 0x1E, buf, 1); ++ ++ /* request the firmware, this will block until someone uploads it */ ++ pr_debug("%s: Waiting for firmware upload (%s)...\n", ++ __func__, NXT2004_DEFAULT_FIRMWARE); ++ ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ pr_debug("%s: Waiting for firmware upload(2)...\n", __func__); ++ if (ret) { ++ pr_err("%s: No firmware uploaded (timeout or file not found?)" ++ "\n", __func__); ++ return ret; ++ } ++ ++ ret = nxt2004_load_firmware(fe, fw); ++ release_firmware(fw); ++ if (ret) { ++ pr_err("%s: Writing firmware to device failed\n", __func__); ++ return ret; ++ } ++ pr_info("%s: Firmware upload complete\n", __func__); ++ ++ /* ensure transfer is complete */ ++ buf[0] = 0x01; ++ nxt200x_writebytes(state, 0x19, buf, 1); ++ ++ nxt2004_microcontroller_init(state); ++ nxt200x_microcontroller_stop(state); ++ nxt200x_microcontroller_stop(state); ++ nxt2004_microcontroller_init(state); ++ nxt200x_microcontroller_stop(state); ++ ++ /* soft reset everything (agc,frontend,eq,fec)*/ ++ buf[0] = 0xFF; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ ++ /* write agc sdm configure */ ++ buf[0] = 0xD7; ++ nxt200x_writebytes(state, 0x57, buf, 1); ++ ++ /* ???*/ ++ buf[0] = 0x07; ++ buf[1] = 0xfe; ++ nxt200x_writebytes(state, 0x35, buf, 2); ++ buf[0] = 0x12; ++ nxt200x_writebytes(state, 0x34, buf, 1); ++ buf[0] = 0x80; ++ nxt200x_writebytes(state, 0x21, buf, 1); ++ ++ /* ???*/ ++ buf[0] = 0x21; ++ nxt200x_writebytes(state, 0x0A, buf, 1); ++ ++ /* ???*/ ++ buf[0] = 0x01; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ /* write fec mpeg mode */ ++ buf[0] = 0x7E; ++ buf[1] = 0x00; ++ nxt200x_writebytes(state, 0xE9, buf, 2); ++ ++ /* write mux selection */ ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0xCC, buf, 1); ++ ++ /* ???*/ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ /* soft reset? */ ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x10; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ ++ /* ???*/ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x01; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x70; ++ nxt200x_writereg_multibyte(state, 0x81, buf, 1); ++ buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66; ++ nxt200x_writereg_multibyte(state, 0x82, buf, 3); ++ ++ nxt200x_readreg_multibyte(state, 0x88, buf, 1); ++ buf[0] = 0x11; ++ nxt200x_writereg_multibyte(state, 0x88, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x40; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ nxt200x_readbytes(state, 0x10, buf, 1); ++ buf[0] = 0x10; ++ nxt200x_writebytes(state, 0x10, buf, 1); ++ nxt200x_readbytes(state, 0x0A, buf, 1); ++ buf[0] = 0x21; ++ nxt200x_writebytes(state, 0x0A, buf, 1); ++ ++ nxt2004_microcontroller_init(state); ++ ++ buf[0] = 0x21; ++ nxt200x_writebytes(state, 0x0A, buf, 1); ++ buf[0] = 0x7E; ++ nxt200x_writebytes(state, 0xE9, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0xEA, buf, 1); ++ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ /* soft reset? */ ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x10; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ nxt200x_readreg_multibyte(state, 0x08, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x08, buf, 1); ++ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x04; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x81, buf, 1); ++ buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; ++ nxt200x_writereg_multibyte(state, 0x82, buf, 3); ++ ++ nxt200x_readreg_multibyte(state, 0x88, buf, 1); ++ buf[0] = 0x11; ++ nxt200x_writereg_multibyte(state, 0x88, buf, 1); ++ ++ nxt200x_readreg_multibyte(state, 0x80, buf, 1); ++ buf[0] = 0x44; ++ nxt200x_writereg_multibyte(state, 0x80, buf, 1); ++ ++ /* initialize tuner */ ++ nxt200x_readbytes(state, 0x10, buf, 1); ++ buf[0] = 0x12; ++ nxt200x_writebytes(state, 0x10, buf, 1); ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x13, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x16, buf, 1); ++ buf[0] = 0x04; ++ nxt200x_writebytes(state, 0x14, buf, 1); ++ buf[0] = 0x00; ++ nxt200x_writebytes(state, 0x14, buf, 1); ++ nxt200x_writebytes(state, 0x17, buf, 1); ++ nxt200x_writebytes(state, 0x14, buf, 1); ++ nxt200x_writebytes(state, 0x17, buf, 1); ++ ++ return 0; ++} ++ ++static int nxt200x_init(struct dvb_frontend* fe) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ int ret = 0; ++ ++ if (!state->initialised) { ++ switch (state->demod_chip) { ++ case NXT2002: ++ ret = nxt2002_init(fe); ++ break; ++ case NXT2004: ++ ret = nxt2004_init(fe); ++ break; ++ default: ++ return -EINVAL; ++ break; ++ } ++ state->initialised = 1; ++ } ++ return ret; ++} ++ ++static int nxt200x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 500; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void nxt200x_release(struct dvb_frontend* fe) ++{ ++ struct nxt200x_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops nxt200x_ops; ++ ++struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct nxt200x_state* state = NULL; ++ u8 buf [] = {0,0,0,0,0}; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct nxt200x_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ ++ /* read card id */ ++ nxt200x_readbytes(state, 0x00, buf, 5); ++ dprintk("NXT info: %*ph\n", 5, buf); ++ ++ /* set demod chip */ ++ switch (buf[0]) { ++ case 0x04: ++ state->demod_chip = NXT2002; ++ pr_info("NXT2002 Detected\n"); ++ break; ++ case 0x05: ++ state->demod_chip = NXT2004; ++ pr_info("NXT2004 Detected\n"); ++ break; ++ default: ++ goto error; ++ } ++ ++ /* make sure demod chip is supported */ ++ switch (state->demod_chip) { ++ case NXT2002: ++ if (buf[0] != 0x04) goto error; /* device id */ ++ if (buf[1] != 0x02) goto error; /* fab id */ ++ if (buf[2] != 0x11) goto error; /* month */ ++ if (buf[3] != 0x20) goto error; /* year msb */ ++ if (buf[4] != 0x00) goto error; /* year lsb */ ++ break; ++ case NXT2004: ++ if (buf[0] != 0x05) goto error; /* device id */ ++ break; ++ default: ++ goto error; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &nxt200x_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ pr_err("Unknown/Unsupported NXT chip: %*ph\n", 5, buf); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops nxt200x_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Nextwave NXT200X VSB/QAM frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 860000000, ++ .frequency_stepsize = 166666, /* stepsize is just a guess */ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256 ++ }, ++ ++ .release = nxt200x_release, ++ ++ .init = nxt200x_init, ++ .sleep = nxt200x_sleep, ++ ++ .set_frontend = nxt200x_setup_frontend_parameters, ++ .get_tune_settings = nxt200x_get_tune_settings, ++ ++ .read_status = nxt200x_read_status, ++ .read_ber = nxt200x_read_ber, ++ .read_signal_strength = nxt200x_read_signal_strength, ++ .read_snr = nxt200x_read_snr, ++ .read_ucblocks = nxt200x_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); ++MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(nxt200x_attach); ++ +diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h +new file mode 100644 +index 0000000..f3c8458 +--- /dev/null ++++ b/drivers/media/dvb-frontends/nxt200x.h +@@ -0,0 +1,63 @@ ++/* ++ * Support for NXT2002 and NXT2004 - VSB/QAM ++ * ++ * Copyright (C) 2005 Kirk Lapray (kirk.lapray@gmail.com) ++ * based on nxt2002 by Taylor Jacob ++ * and nxt2004 by Jean-Francois Thibert (jeanfrancois@sagetv.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. ++ * ++*/ ++ ++#ifndef NXT200X_H ++#define NXT200X_H ++ ++#include ++#include ++ ++typedef enum nxt_chip_t { ++ NXTUNDEFINED, ++ NXT2002, ++ NXT2004 ++}nxt_chip_type; ++ ++struct nxt200x_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* need to set device param for start_dma */ ++ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); ++}; ++ ++#if defined(CONFIG_DVB_NXT200X) || (defined(CONFIG_DVB_NXT200X_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_NXT200X ++ ++#endif /* NXT200X_H */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c +new file mode 100644 +index 0000000..90ae6c7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/nxt6000.c +@@ -0,0 +1,616 @@ ++/* ++ NxtWave Communications - NXT6000 demodulator driver ++ ++ Copyright (C) 2002-2003 Florian Schirmer ++ Copyright (C) 2003 Paul Andreassen ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "nxt6000_priv.h" ++#include "nxt6000.h" ++ ++ ++ ++struct nxt6000_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct nxt6000_config* config; ++ struct dvb_frontend frontend; ++}; ++ ++static int debug; ++#define dprintk if (debug) printk ++ ++static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; ++ int ret; ++ ++ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) ++ dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret); ++ ++ return (ret != 1) ? -EIO : 0; ++} ++ ++static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msgs[] = { ++ {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, ++ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} ++ }; ++ ++ ret = i2c_transfer(state->i2c, msgs, 2); ++ ++ if (ret != 2) ++ dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret); ++ ++ return b1[0]; ++} ++ ++static void nxt6000_reset(struct nxt6000_state* state) ++{ ++ u8 val; ++ ++ val = nxt6000_readreg(state, OFDM_COR_CTL); ++ ++ nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT); ++ nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT); ++} ++ ++static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth) ++{ ++ u16 nominal_rate; ++ int result; ++ ++ switch (bandwidth) { ++ case 6000000: ++ nominal_rate = 0x55B7; ++ break; ++ ++ case 7000000: ++ nominal_rate = 0x6400; ++ break; ++ ++ case 8000000: ++ nominal_rate = 0x7249; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) ++ return result; ++ ++ return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); ++} ++ ++static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval) ++{ ++ switch (guard_interval) { ++ ++ case GUARD_INTERVAL_1_32: ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); ++ ++ case GUARD_INTERVAL_1_16: ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); ++ ++ case GUARD_INTERVAL_AUTO: ++ case GUARD_INTERVAL_1_8: ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); ++ ++ case GUARD_INTERVAL_1_4: ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion) ++{ ++ switch (inversion) { ++ ++ case INVERSION_OFF: ++ return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00); ++ ++ case INVERSION_ON: ++ return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); ++ ++ default: ++ return -EINVAL; ++ ++ } ++} ++ ++static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode) ++{ ++ int result; ++ ++ switch (transmission_mode) { ++ ++ case TRANSMISSION_MODE_2K: ++ if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) ++ return result; ++ ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); ++ ++ case TRANSMISSION_MODE_8K: ++ case TRANSMISSION_MODE_AUTO: ++ if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) ++ return result; ++ ++ return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); ++ ++ default: ++ return -EINVAL; ++ ++ } ++} ++ ++static void nxt6000_setup(struct dvb_frontend* fe) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); ++ nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01); ++ nxt6000_writereg(state, VIT_BERTIME_2, 0x00); // BER Timer = 0x000200 * 256 = 131072 bits ++ nxt6000_writereg(state, VIT_BERTIME_1, 0x02); // ++ nxt6000_writereg(state, VIT_BERTIME_0, 0x00); // ++ nxt6000_writereg(state, VIT_COR_INTEN, 0x98); // Enable BER interrupts ++ nxt6000_writereg(state, VIT_COR_CTL, 0x82); // Enable BER measurement ++ nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC | 0x02 ); ++ nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); ++ nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); ++ nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); ++ nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06); ++ nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31); ++ nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); ++ nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */ ++ nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2); ++ nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); ++ nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49); ++ nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72); ++ nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5); ++ nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); ++ nxt6000_writereg(state, DIAG_CONFIG, TB_SET); ++ ++ if (state->config->clock_inversion) ++ nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); ++ else ++ nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); ++ ++ nxt6000_writereg(state, TS_FORMAT, 0); ++} ++ ++static void nxt6000_dump_status(struct nxt6000_state *state) ++{ ++ u8 val; ++ ++/* ++ printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT)); ++ printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS)); ++ printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT)); ++ printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT)); ++ printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1)); ++ printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2)); ++ printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3)); ++ printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4)); ++ printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1)); ++ printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2)); ++*/ ++ printk("NXT6000 status:"); ++ ++ val = nxt6000_readreg(state, RS_COR_STAT); ++ ++ printk(" DATA DESCR LOCK: %d,", val & 0x01); ++ printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01); ++ ++ val = nxt6000_readreg(state, VIT_SYNC_STATUS); ++ ++ printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01); ++ ++ switch ((val >> 4) & 0x07) { ++ ++ case 0x00: ++ printk(" VITERBI CODERATE: 1/2,"); ++ break; ++ ++ case 0x01: ++ printk(" VITERBI CODERATE: 2/3,"); ++ break; ++ ++ case 0x02: ++ printk(" VITERBI CODERATE: 3/4,"); ++ break; ++ ++ case 0x03: ++ printk(" VITERBI CODERATE: 5/6,"); ++ break; ++ ++ case 0x04: ++ printk(" VITERBI CODERATE: 7/8,"); ++ break; ++ ++ default: ++ printk(" VITERBI CODERATE: Reserved,"); ++ ++ } ++ ++ val = nxt6000_readreg(state, OFDM_COR_STAT); ++ ++ printk(" CHCTrack: %d,", (val >> 7) & 0x01); ++ printk(" TPSLock: %d,", (val >> 6) & 0x01); ++ printk(" SYRLock: %d,", (val >> 5) & 0x01); ++ printk(" AGCLock: %d,", (val >> 4) & 0x01); ++ ++ switch (val & 0x0F) { ++ ++ case 0x00: ++ printk(" CoreState: IDLE,"); ++ break; ++ ++ case 0x02: ++ printk(" CoreState: WAIT_AGC,"); ++ break; ++ ++ case 0x03: ++ printk(" CoreState: WAIT_SYR,"); ++ break; ++ ++ case 0x04: ++ printk(" CoreState: WAIT_PPM,"); ++ break; ++ ++ case 0x01: ++ printk(" CoreState: WAIT_TRL,"); ++ break; ++ ++ case 0x05: ++ printk(" CoreState: WAIT_TPS,"); ++ break; ++ ++ case 0x06: ++ printk(" CoreState: MONITOR_TPS,"); ++ break; ++ ++ default: ++ printk(" CoreState: Reserved,"); ++ ++ } ++ ++ val = nxt6000_readreg(state, OFDM_SYR_STAT); ++ ++ printk(" SYRLock: %d,", (val >> 4) & 0x01); ++ printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K"); ++ ++ switch ((val >> 4) & 0x03) { ++ ++ case 0x00: ++ printk(" SYRGuard: 1/32,"); ++ break; ++ ++ case 0x01: ++ printk(" SYRGuard: 1/16,"); ++ break; ++ ++ case 0x02: ++ printk(" SYRGuard: 1/8,"); ++ break; ++ ++ case 0x03: ++ printk(" SYRGuard: 1/4,"); ++ break; ++ } ++ ++ val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); ++ ++ switch ((val >> 4) & 0x07) { ++ ++ case 0x00: ++ printk(" TPSLP: 1/2,"); ++ break; ++ ++ case 0x01: ++ printk(" TPSLP: 2/3,"); ++ break; ++ ++ case 0x02: ++ printk(" TPSLP: 3/4,"); ++ break; ++ ++ case 0x03: ++ printk(" TPSLP: 5/6,"); ++ break; ++ ++ case 0x04: ++ printk(" TPSLP: 7/8,"); ++ break; ++ ++ default: ++ printk(" TPSLP: Reserved,"); ++ ++ } ++ ++ switch (val & 0x07) { ++ ++ case 0x00: ++ printk(" TPSHP: 1/2,"); ++ break; ++ ++ case 0x01: ++ printk(" TPSHP: 2/3,"); ++ break; ++ ++ case 0x02: ++ printk(" TPSHP: 3/4,"); ++ break; ++ ++ case 0x03: ++ printk(" TPSHP: 5/6,"); ++ break; ++ ++ case 0x04: ++ printk(" TPSHP: 7/8,"); ++ break; ++ ++ default: ++ printk(" TPSHP: Reserved,"); ++ ++ } ++ ++ val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); ++ ++ printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K"); ++ ++ switch ((val >> 4) & 0x03) { ++ ++ case 0x00: ++ printk(" TPSGuard: 1/32,"); ++ break; ++ ++ case 0x01: ++ printk(" TPSGuard: 1/16,"); ++ break; ++ ++ case 0x02: ++ printk(" TPSGuard: 1/8,"); ++ break; ++ ++ case 0x03: ++ printk(" TPSGuard: 1/4,"); ++ break; ++ ++ } ++ ++ /* Strange magic required to gain access to RF_AGC_STATUS */ ++ nxt6000_readreg(state, RF_AGC_VAL_1); ++ val = nxt6000_readreg(state, RF_AGC_STATUS); ++ val = nxt6000_readreg(state, RF_AGC_STATUS); ++ ++ printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01); ++ printk("\n"); ++} ++ ++static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ u8 core_status; ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ *status = 0; ++ ++ core_status = nxt6000_readreg(state, OFDM_COR_STAT); ++ ++ if (core_status & AGCLOCKED) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) ++ *status |= FE_HAS_CARRIER; ++ ++ if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) ++ *status |= FE_HAS_VITERBI; ++ ++ if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS) ++ *status |= FE_HAS_SYNC; ++ ++ if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))) ++ *status |= FE_HAS_LOCK; ++ ++ if (debug) ++ nxt6000_dump_status(state); ++ ++ return 0; ++} ++ ++static int nxt6000_init(struct dvb_frontend* fe) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ nxt6000_reset(state); ++ nxt6000_setup(fe); ++ ++ return 0; ++} ++ ++static int nxt6000_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct nxt6000_state* state = fe->demodulator_priv; ++ int result; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ result = nxt6000_set_bandwidth(state, p->bandwidth_hz); ++ if (result < 0) ++ return result; ++ ++ result = nxt6000_set_guard_interval(state, p->guard_interval); ++ if (result < 0) ++ return result; ++ ++ result = nxt6000_set_transmission_mode(state, p->transmission_mode); ++ if (result < 0) ++ return result; ++ ++ result = nxt6000_set_inversion(state, p->inversion); ++ if (result < 0) ++ return result; ++ ++ msleep(500); ++ return 0; ++} ++ ++static void nxt6000_release(struct dvb_frontend* fe) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static int nxt6000_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ *snr = nxt6000_readreg( state, OFDM_CHC_SNR) / 8; ++ ++ return 0; ++} ++ ++static int nxt6000_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18 ); ++ ++ *ber = (nxt6000_readreg( state, VIT_BER_1 ) << 8 ) | ++ nxt6000_readreg( state, VIT_BER_0 ); ++ ++ nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18); // Clear BER Done interrupts ++ ++ return 0; ++} ++ ++static int nxt6000_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ *signal_strength = (short) (511 - ++ (nxt6000_readreg(state, AGC_GAIN_1) + ++ ((nxt6000_readreg(state, AGC_GAIN_2) & 0x03) << 8))); ++ ++ return 0; ++} ++ ++static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 500; ++ return 0; ++} ++ ++static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct nxt6000_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); ++ } else { ++ return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); ++ } ++} ++ ++static struct dvb_frontend_ops nxt6000_ops; ++ ++struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct nxt6000_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops nxt6000_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "NxtWave NXT6000 DVB-T", ++ .frequency_min = 0, ++ .frequency_max = 863250000, ++ .frequency_stepsize = 62500, ++ /*.frequency_tolerance = *//* FIXME: 12% of SR */ ++ .symbol_rate_min = 0, /* FIXME */ ++ .symbol_rate_max = 9360000, /* FIXME */ ++ .symbol_rate_tolerance = 4000, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .release = nxt6000_release, ++ ++ .init = nxt6000_init, ++ .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, ++ ++ .get_tune_settings = nxt6000_fe_get_tune_settings, ++ ++ .set_frontend = nxt6000_set_frontend, ++ ++ .read_status = nxt6000_read_status, ++ .read_ber = nxt6000_read_ber, ++ .read_signal_strength = nxt6000_read_signal_strength, ++ .read_snr = nxt6000_read_snr, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); ++MODULE_AUTHOR("Florian Schirmer"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(nxt6000_attach); +diff --git a/drivers/media/dvb-frontends/nxt6000.h b/drivers/media/dvb-frontends/nxt6000.h +new file mode 100644 +index 0000000..878eb38 +--- /dev/null ++++ b/drivers/media/dvb-frontends/nxt6000.h +@@ -0,0 +1,48 @@ ++/* ++ NxtWave Communications - NXT6000 demodulator driver ++ ++ Copyright (C) 2002-2003 Florian Schirmer ++ Copyright (C) 2003 Paul Andreassen ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef NXT6000_H ++#define NXT6000_H ++ ++#include ++ ++struct nxt6000_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* should clock inversion be used? */ ++ u8 clock_inversion:1; ++}; ++ ++#if defined(CONFIG_DVB_NXT6000) || (defined(CONFIG_DVB_NXT6000_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_NXT6000 ++ ++#endif // NXT6000_H +diff --git a/drivers/media/dvb-frontends/nxt6000_priv.h b/drivers/media/dvb-frontends/nxt6000_priv.h +new file mode 100644 +index 0000000..0422e58 +--- /dev/null ++++ b/drivers/media/dvb-frontends/nxt6000_priv.h +@@ -0,0 +1,286 @@ ++/* ++ * Public Include File for DRV6000 users ++ * (ie. NxtWave Communications - NXT6000 demodulator driver) ++ * ++ * Copyright (C) 2001 NxtWave Communications, Inc. ++ * ++ */ ++ ++/* Nxt6000 Register Addresses and Bit Masks */ ++ ++/* Maximum Register Number */ ++#define MAXNXT6000REG (0x9A) ++ ++/* 0x1B A_VIT_BER_0 aka 0x3A */ ++#define A_VIT_BER_0 (0x1B) ++ ++/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */ ++#define A_VIT_BER_TIMER_0 (0x1D) ++ ++/* 0x21 RS_COR_STAT */ ++#define RS_COR_STAT (0x21) ++#define RSCORESTATUS (0x03) ++ ++/* 0x22 RS_COR_INTEN */ ++#define RS_COR_INTEN (0x22) ++ ++/* 0x23 RS_COR_INSTAT */ ++#define RS_COR_INSTAT (0x23) ++#define INSTAT_ERROR (0x04) ++#define LOCK_LOSS_BITS (0x03) ++ ++/* 0x24 RS_COR_SYNC_PARAM */ ++#define RS_COR_SYNC_PARAM (0x24) ++#define SYNC_PARAM (0x03) ++ ++/* 0x25 BER_CTRL */ ++#define BER_CTRL (0x25) ++#define BER_ENABLE (0x02) ++#define BER_RESET (0x01) ++ ++/* 0x26 BER_PAY */ ++#define BER_PAY (0x26) ++ ++/* 0x27 BER_PKT_L */ ++#define BER_PKT_L (0x27) ++#define BER_PKTOVERFLOW (0x80) ++ ++/* 0x30 VIT_COR_CTL */ ++#define VIT_COR_CTL (0x30) ++#define BER_CONTROL (0x02) ++#define VIT_COR_MASK (0x82) ++#define VIT_COR_RESYNC (0x80) ++ ++ ++/* 0x32 VIT_SYNC_STATUS */ ++#define VIT_SYNC_STATUS (0x32) ++#define VITINSYNC (0x80) ++ ++/* 0x33 VIT_COR_INTEN */ ++#define VIT_COR_INTEN (0x33) ++#define GLOBAL_ENABLE (0x80) ++ ++/* 0x34 VIT_COR_INTSTAT */ ++#define VIT_COR_INTSTAT (0x34) ++#define BER_DONE (0x08) ++#define BER_OVERFLOW (0x10) ++ ++/* 0x38 VIT_BERTIME_2 */ ++#define VIT_BERTIME_2 (0x38) ++ ++/* 0x39 VIT_BERTIME_1 */ ++#define VIT_BERTIME_1 (0x39) ++ ++/* 0x3A VIT_BERTIME_0 */ ++#define VIT_BERTIME_0 (0x3a) ++ ++ /* 0x38 OFDM_BERTimer *//* Use the alias registers */ ++#define A_VIT_BER_TIMER_0 (0x1D) ++ ++ /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */ ++#define A_VIT_BER_0 (0x1B) ++ ++/* 0x3B VIT_BER_1 */ ++#define VIT_BER_1 (0x3b) ++ ++/* 0x3C VIT_BER_0 */ ++#define VIT_BER_0 (0x3c) ++ ++/* 0x40 OFDM_COR_CTL */ ++#define OFDM_COR_CTL (0x40) ++#define COREACT (0x20) ++#define HOLDSM (0x10) ++#define WAIT_AGC (0x02) ++#define WAIT_SYR (0x03) ++ ++/* 0x41 OFDM_COR_STAT */ ++#define OFDM_COR_STAT (0x41) ++#define COR_STATUS (0x0F) ++#define MONITOR_TPS (0x06) ++#define TPSLOCKED (0x40) ++#define AGCLOCKED (0x10) ++ ++/* 0x42 OFDM_COR_INTEN */ ++#define OFDM_COR_INTEN (0x42) ++#define TPSRCVBAD (0x04) ++#define TPSRCVCHANGED (0x02) ++#define TPSRCVUPDATE (0x01) ++ ++/* 0x43 OFDM_COR_INSTAT */ ++#define OFDM_COR_INSTAT (0x43) ++ ++/* 0x44 OFDM_COR_MODEGUARD */ ++#define OFDM_COR_MODEGUARD (0x44) ++#define FORCEMODE (0x08) ++#define FORCEMODE8K (0x04) ++ ++/* 0x45 OFDM_AGC_CTL */ ++#define OFDM_AGC_CTL (0x45) ++#define INITIAL_AGC_BW (0x08) ++#define AGCNEG (0x02) ++#define AGCLAST (0x10) ++ ++/* 0x48 OFDM_AGC_TARGET */ ++#define OFDM_AGC_TARGET (0x48) ++#define OFDM_AGC_TARGET_DEFAULT (0x28) ++#define OFDM_AGC_TARGET_IMPULSE (0x38) ++ ++/* 0x49 OFDM_AGC_GAIN_1 */ ++#define OFDM_AGC_GAIN_1 (0x49) ++ ++/* 0x4B OFDM_ITB_CTL */ ++#define OFDM_ITB_CTL (0x4B) ++#define ITBINV (0x01) ++ ++/* 0x49 AGC_GAIN_1 */ ++#define AGC_GAIN_1 (0x49) ++ ++/* 0x4A AGC_GAIN_2 */ ++#define AGC_GAIN_2 (0x4A) ++ ++/* 0x4C OFDM_ITB_FREQ_1 */ ++#define OFDM_ITB_FREQ_1 (0x4C) ++ ++/* 0x4D OFDM_ITB_FREQ_2 */ ++#define OFDM_ITB_FREQ_2 (0x4D) ++ ++/* 0x4E OFDM_CAS_CTL */ ++#define OFDM_CAS_CTL (0x4E) ++#define ACSDIS (0x40) ++#define CCSEN (0x80) ++ ++/* 0x4F CAS_FREQ */ ++#define CAS_FREQ (0x4F) ++ ++/* 0x51 OFDM_SYR_CTL */ ++#define OFDM_SYR_CTL (0x51) ++#define SIXTH_ENABLE (0x80) ++#define SYR_TRACKING_DISABLE (0x01) ++ ++/* 0x52 OFDM_SYR_STAT */ ++#define OFDM_SYR_STAT (0x52) ++#define GI14_2K_SYR_LOCK (0x13) ++#define GI14_8K_SYR_LOCK (0x17) ++#define GI14_SYR_LOCK (0x10) ++ ++/* 0x55 OFDM_SYR_OFFSET_1 */ ++#define OFDM_SYR_OFFSET_1 (0x55) ++ ++/* 0x56 OFDM_SYR_OFFSET_2 */ ++#define OFDM_SYR_OFFSET_2 (0x56) ++ ++/* 0x58 OFDM_SCR_CTL */ ++#define OFDM_SCR_CTL (0x58) ++#define SYR_ADJ_DECAY_MASK (0x70) ++#define SYR_ADJ_DECAY (0x30) ++ ++/* 0x59 OFDM_PPM_CTL_1 */ ++#define OFDM_PPM_CTL_1 (0x59) ++#define PPMMAX_MASK (0x30) ++#define PPM256 (0x30) ++ ++/* 0x5B OFDM_TRL_NOMINALRATE_1 */ ++#define OFDM_TRL_NOMINALRATE_1 (0x5B) ++ ++/* 0x5C OFDM_TRL_NOMINALRATE_2 */ ++#define OFDM_TRL_NOMINALRATE_2 (0x5C) ++ ++/* 0x5D OFDM_TRL_TIME_1 */ ++#define OFDM_TRL_TIME_1 (0x5D) ++ ++/* 0x60 OFDM_CRL_FREQ_1 */ ++#define OFDM_CRL_FREQ_1 (0x60) ++ ++/* 0x63 OFDM_CHC_CTL_1 */ ++#define OFDM_CHC_CTL_1 (0x63) ++#define MANMEAN1 (0xF0); ++#define CHCFIR (0x01) ++ ++/* 0x64 OFDM_CHC_SNR */ ++#define OFDM_CHC_SNR (0x64) ++ ++/* 0x65 OFDM_BDI_CTL */ ++#define OFDM_BDI_CTL (0x65) ++#define LP_SELECT (0x02) ++ ++/* 0x67 OFDM_TPS_RCVD_1 */ ++#define OFDM_TPS_RCVD_1 (0x67) ++#define TPSFRAME (0x03) ++ ++/* 0x68 OFDM_TPS_RCVD_2 */ ++#define OFDM_TPS_RCVD_2 (0x68) ++ ++/* 0x69 OFDM_TPS_RCVD_3 */ ++#define OFDM_TPS_RCVD_3 (0x69) ++ ++/* 0x6A OFDM_TPS_RCVD_4 */ ++#define OFDM_TPS_RCVD_4 (0x6A) ++ ++/* 0x6B OFDM_TPS_RESERVED_1 */ ++#define OFDM_TPS_RESERVED_1 (0x6B) ++ ++/* 0x6C OFDM_TPS_RESERVED_2 */ ++#define OFDM_TPS_RESERVED_2 (0x6C) ++ ++/* 0x73 OFDM_MSC_REV */ ++#define OFDM_MSC_REV (0x73) ++ ++/* 0x76 OFDM_SNR_CARRIER_2 */ ++#define OFDM_SNR_CARRIER_2 (0x76) ++#define MEAN_MASK (0x80) ++#define MEANBIT (0x80) ++ ++/* 0x80 ANALOG_CONTROL_0 */ ++#define ANALOG_CONTROL_0 (0x80) ++#define POWER_DOWN_ADC (0x40) ++ ++/* 0x81 ENABLE_TUNER_IIC */ ++#define ENABLE_TUNER_IIC (0x81) ++#define ENABLE_TUNER_BIT (0x01) ++ ++/* 0x82 EN_DMD_RACQ */ ++#define EN_DMD_RACQ (0x82) ++#define EN_DMD_RACQ_REG_VAL (0x81) ++#define EN_DMD_RACQ_REG_VAL_14 (0x01) ++ ++/* 0x84 SNR_COMMAND */ ++#define SNR_COMMAND (0x84) ++#define SNRStat (0x80) ++ ++/* 0x85 SNRCARRIERNUMBER_LSB */ ++#define SNRCARRIERNUMBER_LSB (0x85) ++ ++/* 0x87 SNRMINTHRESHOLD_LSB */ ++#define SNRMINTHRESHOLD_LSB (0x87) ++ ++/* 0x89 SNR_PER_CARRIER_LSB */ ++#define SNR_PER_CARRIER_LSB (0x89) ++ ++/* 0x8B SNRBELOWTHRESHOLD_LSB */ ++#define SNRBELOWTHRESHOLD_LSB (0x8B) ++ ++/* 0x91 RF_AGC_VAL_1 */ ++#define RF_AGC_VAL_1 (0x91) ++ ++/* 0x92 RF_AGC_STATUS */ ++#define RF_AGC_STATUS (0x92) ++ ++/* 0x98 DIAG_CONFIG */ ++#define DIAG_CONFIG (0x98) ++#define DIAG_MASK (0x70) ++#define TB_SET (0x10) ++#define TRAN_SELECT (0x07) ++#define SERIAL_SELECT (0x01) ++ ++/* 0x99 SUB_DIAG_MODE_SEL */ ++#define SUB_DIAG_MODE_SEL (0x99) ++#define CLKINVERSION (0x01) ++ ++/* 0x9A TS_FORMAT */ ++#define TS_FORMAT (0x9A) ++#define ERROR_SENSE (0x08) ++#define VALID_SENSE (0x04) ++#define SYNC_SENSE (0x02) ++#define GATED_CLOCK (0x01) ++ ++#define NXT6000ASICDEVICE (0x0b) +diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c +new file mode 100644 +index 0000000..5ef9218 +--- /dev/null ++++ b/drivers/media/dvb-frontends/or51132.c +@@ -0,0 +1,631 @@ ++/* ++ * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM ++ * ++ * ++ * Copyright (C) 2007 Trent Piepho ++ * ++ * Copyright (C) 2005 Kirk Lapray ++ * ++ * Based on code from Jack Kelliher (kelliher@xmission.com) ++ * Copyright (C) 2002 & pcHDTV, inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++*/ ++ ++/* ++ * This driver needs two external firmware files. Please copy ++ * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to ++ * /usr/lib/hotplug/firmware/ or /lib/firmware/ ++ * (depending on configuration of firmware hotplug). ++ */ ++#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw" ++#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_math.h" ++#include "dvb_frontend.h" ++#include "or51132.h" ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "or51132: " args); \ ++ } while (0) ++ ++ ++struct or51132_state ++{ ++ struct i2c_adapter* i2c; ++ ++ /* Configuration settings */ ++ const struct or51132_config* config; ++ ++ struct dvb_frontend frontend; ++ ++ /* Demodulator private data */ ++ fe_modulation_t current_modulation; ++ u32 snr; /* Result of last SNR calculation */ ++ ++ /* Tuner private data */ ++ u32 current_frequency; ++}; ++ ++ ++/* Write buffer to demod */ ++static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) ++{ ++ int err; ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = (u8*)buf, .len = len }; ++ ++ /* msleep(20); */ /* doesn't appear to be necessary */ ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { ++ printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", ++ msg.addr, msg.len, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); ++ Less code and more efficient that loading a buffer on the stack with ++ the bytes to send and then calling or51132_writebuf() on that. */ ++#define or51132_writebytes(state, data...) \ ++ ({ static const u8 _data[] = {data}; \ ++ or51132_writebuf(state, _data, sizeof(_data)); }) ++ ++/* Read data from demod into buffer. Returns 0 on success. */ ++static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) ++{ ++ int err; ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = I2C_M_RD, .buf = buf, .len = len }; ++ ++ /* msleep(20); */ /* doesn't appear to be necessary */ ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { ++ printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", ++ msg.addr, msg.len, err); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++/* Reads a 16-bit demod register. Returns <0 on error. */ ++static int or51132_readreg(struct or51132_state *state, u8 reg) ++{ ++ u8 buf[2] = { 0x04, reg }; ++ struct i2c_msg msg[2] = { ++ {.addr = state->config->demod_address, .flags = 0, ++ .buf = buf, .len = 2 }, ++ {.addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = buf, .len = 2 }}; ++ int err; ++ ++ if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { ++ printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", ++ reg, err); ++ return -EREMOTEIO; ++ } ++ return buf[0] | (buf[1] << 8); ++} ++ ++static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) ++{ ++ struct or51132_state* state = fe->demodulator_priv; ++ static const u8 run_buf[] = {0x7F,0x01}; ++ u8 rec_buf[8]; ++ u32 firmwareAsize, firmwareBsize; ++ int i,ret; ++ ++ dprintk("Firmware is %Zd bytes\n",fw->size); ++ ++ /* Get size of firmware A and B */ ++ firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); ++ dprintk("FirmwareA is %i bytes\n",firmwareAsize); ++ firmwareBsize = le32_to_cpu(*((__le32*)(fw->data+4))); ++ dprintk("FirmwareB is %i bytes\n",firmwareBsize); ++ ++ /* Upload firmware */ ++ if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { ++ printk(KERN_WARNING "or51132: load_firmware error 1\n"); ++ return ret; ++ } ++ if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], ++ firmwareBsize))) { ++ printk(KERN_WARNING "or51132: load_firmware error 2\n"); ++ return ret; ++ } ++ ++ if ((ret = or51132_writebuf(state, run_buf, 2))) { ++ printk(KERN_WARNING "or51132: load_firmware error 3\n"); ++ return ret; ++ } ++ if ((ret = or51132_writebuf(state, run_buf, 2))) { ++ printk(KERN_WARNING "or51132: load_firmware error 4\n"); ++ return ret; ++ } ++ ++ /* 50ms for operation to begin */ ++ msleep(50); ++ ++ /* Read back ucode version to besure we loaded correctly and are really up and running */ ++ /* Get uCode version */ ++ if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { ++ printk(KERN_WARNING "or51132: load_firmware error a\n"); ++ return ret; ++ } ++ if ((ret = or51132_writebytes(state, 0x04, 0x17))) { ++ printk(KERN_WARNING "or51132: load_firmware error b\n"); ++ return ret; ++ } ++ if ((ret = or51132_writebytes(state, 0x00, 0x00))) { ++ printk(KERN_WARNING "or51132: load_firmware error c\n"); ++ return ret; ++ } ++ for (i=0;i<4;i++) { ++ /* Once upon a time, this command might have had something ++ to do with getting the firmware version, but it's ++ not used anymore: ++ {0x04,0x00,0x30,0x00,i+1} */ ++ /* Read 8 bytes, two bytes at a time */ ++ if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { ++ printk(KERN_WARNING ++ "or51132: load_firmware error d - %d\n",i); ++ return ret; ++ } ++ } ++ ++ printk(KERN_WARNING ++ "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n", ++ rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2], ++ rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6], ++ rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, ++ rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); ++ ++ if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { ++ printk(KERN_WARNING "or51132: load_firmware error e\n"); ++ return ret; ++ } ++ return 0; ++}; ++ ++static int or51132_init(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int or51132_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ *ber = 0; ++ return 0; ++} ++ ++static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ *ucblocks = 0; ++ return 0; ++} ++ ++static int or51132_sleep(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int or51132_setmode(struct dvb_frontend* fe) ++{ ++ struct or51132_state* state = fe->demodulator_priv; ++ u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; ++ u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; ++ ++ dprintk("setmode %d\n",(int)state->current_modulation); ++ ++ switch (state->current_modulation) { ++ case VSB_8: ++ /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ ++ cmd_buf1[2] = 0x50; ++ /* REC MODE inv IF spectrum, Normal */ ++ cmd_buf2[1] = 0x03; ++ /* Channel MODE ATSC/VSB8 */ ++ cmd_buf2[2] = 0x06; ++ break; ++ /* All QAM modes are: ++ Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high ++ REC MODE Normal Carrier Lock */ ++ case QAM_AUTO: ++ /* Channel MODE Auto QAM64/256 */ ++ cmd_buf2[2] = 0x4f; ++ break; ++ case QAM_256: ++ /* Channel MODE QAM256 */ ++ cmd_buf2[2] = 0x45; ++ break; ++ case QAM_64: ++ /* Channel MODE QAM64 */ ++ cmd_buf2[2] = 0x43; ++ break; ++ default: ++ printk(KERN_WARNING ++ "or51132: setmode: Modulation set to unsupported value (%d)\n", ++ state->current_modulation); ++ return -EINVAL; ++ } ++ ++ /* Set Receiver 1 register */ ++ if (or51132_writebuf(state, cmd_buf1, 3)) { ++ printk(KERN_WARNING "or51132: set_mode error 1\n"); ++ return -EREMOTEIO; ++ } ++ dprintk("set #1 to %02x\n", cmd_buf1[2]); ++ ++ /* Set operation mode in Receiver 6 register */ ++ if (or51132_writebuf(state, cmd_buf2, 3)) { ++ printk(KERN_WARNING "or51132: set_mode error 2\n"); ++ return -EREMOTEIO; ++ } ++ dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); ++ ++ return 0; ++} ++ ++/* Some modulations use the same firmware. This classifies modulations ++ by the firmware they use. */ ++#define MOD_FWCLASS_UNKNOWN 0 ++#define MOD_FWCLASS_VSB 1 ++#define MOD_FWCLASS_QAM 2 ++static int modulation_fw_class(fe_modulation_t modulation) ++{ ++ switch(modulation) { ++ case VSB_8: ++ return MOD_FWCLASS_VSB; ++ case QAM_AUTO: ++ case QAM_64: ++ case QAM_256: ++ return MOD_FWCLASS_QAM; ++ default: ++ return MOD_FWCLASS_UNKNOWN; ++ } ++} ++ ++static int or51132_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ int ret; ++ struct or51132_state* state = fe->demodulator_priv; ++ const struct firmware *fw; ++ const char *fwname; ++ int clock_mode; ++ ++ /* Upload new firmware only if we need a different one */ ++ if (modulation_fw_class(state->current_modulation) != ++ modulation_fw_class(p->modulation)) { ++ switch (modulation_fw_class(p->modulation)) { ++ case MOD_FWCLASS_VSB: ++ dprintk("set_parameters VSB MODE\n"); ++ fwname = OR51132_VSB_FIRMWARE; ++ ++ /* Set non-punctured clock for VSB */ ++ clock_mode = 0; ++ break; ++ case MOD_FWCLASS_QAM: ++ dprintk("set_parameters QAM MODE\n"); ++ fwname = OR51132_QAM_FIRMWARE; ++ ++ /* Set punctured clock for QAM */ ++ clock_mode = 1; ++ break; ++ default: ++ printk("or51132: Modulation type(%d) UNSUPPORTED\n", ++ p->modulation); ++ return -1; ++ } ++ printk("or51132: Waiting for firmware upload(%s)...\n", ++ fwname); ++ ret = request_firmware(&fw, fwname, state->i2c->dev.parent); ++ if (ret) { ++ printk(KERN_WARNING "or51132: No firmware up" ++ "loaded(timeout or file not found?)\n"); ++ return ret; ++ } ++ ret = or51132_load_firmware(fe, fw); ++ release_firmware(fw); ++ if (ret) { ++ printk(KERN_WARNING "or51132: Writing firmware to " ++ "device failed!\n"); ++ return ret; ++ } ++ printk("or51132: Firmware upload complete.\n"); ++ state->config->set_ts_params(fe, clock_mode); ++ } ++ /* Change only if we are actually changing the modulation */ ++ if (state->current_modulation != p->modulation) { ++ state->current_modulation = p->modulation; ++ or51132_setmode(fe); ++ } ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Set to current mode */ ++ or51132_setmode(fe); ++ ++ /* Update current frequency */ ++ state->current_frequency = p->frequency; ++ return 0; ++} ++ ++static int or51132_get_parameters(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct or51132_state* state = fe->demodulator_priv; ++ int status; ++ int retry = 1; ++ ++start: ++ /* Receiver Status */ ++ if ((status = or51132_readreg(state, 0x00)) < 0) { ++ printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); ++ return -EREMOTEIO; ++ } ++ switch(status&0xff) { ++ case 0x06: ++ p->modulation = VSB_8; ++ break; ++ case 0x43: ++ p->modulation = QAM_64; ++ break; ++ case 0x45: ++ p->modulation = QAM_256; ++ break; ++ default: ++ if (retry--) ++ goto start; ++ printk(KERN_WARNING "or51132: unknown status 0x%02x\n", ++ status&0xff); ++ return -EREMOTEIO; ++ } ++ ++ /* FIXME: Read frequency from frontend, take AFC into account */ ++ p->frequency = state->current_frequency; ++ ++ /* FIXME: How to read inversion setting? Receiver 6 register? */ ++ p->inversion = INVERSION_AUTO; ++ ++ return 0; ++} ++ ++static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct or51132_state* state = fe->demodulator_priv; ++ int reg; ++ ++ /* Receiver Status */ ++ if ((reg = or51132_readreg(state, 0x00)) < 0) { ++ printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); ++ *status = 0; ++ return -EREMOTEIO; ++ } ++ dprintk("%s: read_status %04x\n", __func__, reg); ++ ++ if (reg & 0x0100) /* Receiver Lock */ ++ *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| ++ FE_HAS_SYNC|FE_HAS_LOCK; ++ else ++ *status = 0; ++ return 0; ++} ++ ++/* Calculate SNR estimation (scaled by 2^24) ++ ++ 8-VSB SNR and QAM equations from Oren datasheets ++ ++ For 8-VSB: ++ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K ++ ++ Where K = 0 if NTSC rejection filter is OFF; and ++ K = 3 if NTSC rejection filter is ON ++ ++ For QAM64: ++ SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) ++ ++ For QAM256: ++ SNR[dB] = 10 * log10(907832426.314266 / MSE^2 ) ++ ++ We re-write the snr equation as: ++ SNR * 2^24 = 10*(c - 2*intlog10(MSE)) ++ Where for QAM256, c = log10(907832426.314266) * 2^24 ++ and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */ ++ ++static u32 calculate_snr(u32 mse, u32 c) ++{ ++ if (mse == 0) /* No signal */ ++ return 0; ++ ++ mse = 2*intlog10(mse); ++ if (mse > c) { ++ /* Negative SNR, which is possible, but realisticly the ++ demod will lose lock before the signal gets this bad. The ++ API only allows for unsigned values, so just return 0 */ ++ return 0; ++ } ++ return 10*(c - mse); ++} ++ ++static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct or51132_state* state = fe->demodulator_priv; ++ int noise, reg; ++ u32 c, usK = 0; ++ int retry = 1; ++ ++start: ++ /* SNR after Equalizer */ ++ noise = or51132_readreg(state, 0x02); ++ if (noise < 0) { ++ printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); ++ return -EREMOTEIO; ++ } ++ dprintk("read_snr noise (%d)\n", noise); ++ ++ /* Read status, contains modulation type for QAM_AUTO and ++ NTSC filter for VSB */ ++ reg = or51132_readreg(state, 0x00); ++ if (reg < 0) { ++ printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); ++ return -EREMOTEIO; ++ } ++ ++ switch (reg&0xff) { ++ case 0x06: ++ if (reg & 0x1000) usK = 3 << 24; ++ /* Fall through to QAM64 case */ ++ case 0x43: ++ c = 150204167; ++ break; ++ case 0x45: ++ c = 150290396; ++ break; ++ default: ++ printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); ++ if (retry--) goto start; ++ return -EREMOTEIO; ++ } ++ dprintk("%s: modulation %02x, NTSC rej O%s\n", __func__, ++ reg&0xff, reg&0x1000?"n":"ff"); ++ ++ /* Calculate SNR using noise, c, and NTSC rejection correction */ ++ state->snr = calculate_snr(noise, c) - usK; ++ *snr = (state->snr) >> 16; ++ ++ dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, ++ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); ++ ++ return 0; ++} ++ ++static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ /* Calculate Strength from SNR up to 35dB */ ++ /* Even though the SNR can go higher than 35dB, there is some comfort */ ++ /* factor in having a range of strong signals that can show at 100% */ ++ struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv; ++ u16 snr; ++ int ret; ++ ++ ret = fe->ops.read_snr(fe, &snr); ++ if (ret != 0) ++ return ret; ++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ ++ /* scale the range 0 - 35*2^24 into 0 - 65535 */ ++ if (state->snr >= 8960 * 0x10000) ++ *strength = 0xffff; ++ else ++ *strength = state->snr / 8960; ++ ++ return 0; ++} ++ ++static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 500; ++ fe_tune_settings->step_size = 0; ++ fe_tune_settings->max_drift = 0; ++ ++ return 0; ++} ++ ++static void or51132_release(struct dvb_frontend* fe) ++{ ++ struct or51132_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops or51132_ops; ++ ++struct dvb_frontend* or51132_attach(const struct or51132_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct or51132_state* state = NULL; ++ ++ /* Allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ ++ /* Setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->current_frequency = -1; ++ state->current_modulation = -1; ++ ++ /* Create dvb_frontend */ ++ memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops or51132_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Oren OR51132 VSB/QAM Frontend", ++ .frequency_min = 44000000, ++ .frequency_max = 958000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | ++ FE_CAN_8VSB ++ }, ++ ++ .release = or51132_release, ++ ++ .init = or51132_init, ++ .sleep = or51132_sleep, ++ ++ .set_frontend = or51132_set_parameters, ++ .get_frontend = or51132_get_parameters, ++ .get_tune_settings = or51132_get_tune_settings, ++ ++ .read_status = or51132_read_status, ++ .read_ber = or51132_read_ber, ++ .read_signal_strength = or51132_read_signal_strength, ++ .read_snr = or51132_read_snr, ++ .read_ucblocks = or51132_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); ++MODULE_AUTHOR("Kirk Lapray"); ++MODULE_AUTHOR("Trent Piepho"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(or51132_attach); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h +new file mode 100644 +index 0000000..1b8e04d +--- /dev/null ++++ b/drivers/media/dvb-frontends/or51132.h +@@ -0,0 +1,55 @@ ++/* ++ * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM ++ * ++ * Copyright (C) 2005 Kirk Lapray ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++*/ ++ ++#ifndef OR51132_H ++#define OR51132_H ++ ++#include ++#include ++ ++struct or51132_config ++{ ++ /* The demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* Need to set device param for start_dma */ ++ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); ++}; ++ ++#if defined(CONFIG_DVB_OR51132) || (defined(CONFIG_DVB_OR51132_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_OR51132 ++ ++#endif // OR51132_H ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c +new file mode 100644 +index 0000000..10cfc05 +--- /dev/null ++++ b/drivers/media/dvb-frontends/or51211.c +@@ -0,0 +1,570 @@ ++/* ++ * Support for OR51211 (pcHDTV HD-2000) - VSB ++ * ++ * Copyright (C) 2005 Kirk Lapray ++ * ++ * Based on code from Jack Kelliher (kelliher@xmission.com) ++ * Copyright (C) 2002 & pcHDTV, inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++*/ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__ ++ ++/* ++ * This driver needs external firmware. Please use the command ++ * "/Documentation/dvb/get_dvb_firmware or51211" to ++ * download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ * or /lib/firmware (depending on configuration of firmware hotplug). ++ */ ++#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_math.h" ++#include "dvb_frontend.h" ++#include "or51211.h" ++ ++static int debug; ++#define dprintk(args...) \ ++ do { if (debug) pr_debug(args); } while (0) ++ ++static u8 run_buf[] = {0x7f,0x01}; ++static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC ++ ++struct or51211_state { ++ ++ struct i2c_adapter* i2c; ++ ++ /* Configuration settings */ ++ const struct or51211_config* config; ++ ++ struct dvb_frontend frontend; ++ struct bt878* bt; ++ ++ /* Demodulator private data */ ++ u8 initialized:1; ++ u32 snr; /* Result of last SNR claculation */ ++ ++ /* Tuner private data */ ++ u32 current_frequency; ++}; ++ ++static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, ++ int len) ++{ ++ int err; ++ struct i2c_msg msg; ++ msg.addr = reg; ++ msg.flags = 0; ++ msg.len = len; ++ msg.buf = (u8 *)buf; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ pr_warn("error (addr %02x, err == %i)\n", reg, err); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) ++{ ++ int err; ++ struct i2c_msg msg; ++ msg.addr = reg; ++ msg.flags = I2C_M_RD; ++ msg.len = len; ++ msg.buf = buf; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ pr_warn("error (addr %02x, err == %i)\n", reg, err); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int or51211_load_firmware (struct dvb_frontend* fe, ++ const struct firmware *fw) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ u8 tudata[585]; ++ int i; ++ ++ dprintk("Firmware is %zd bytes\n",fw->size); ++ ++ /* Get eprom data */ ++ tudata[0] = 17; ++ if (i2c_writebytes(state,0x50,tudata,1)) { ++ pr_warn("error eprom addr\n"); ++ return -1; ++ } ++ if (i2c_readbytes(state,0x50,&tudata[145],192)) { ++ pr_warn("error eprom\n"); ++ return -1; ++ } ++ ++ /* Create firmware buffer */ ++ for (i = 0; i < 145; i++) ++ tudata[i] = fw->data[i]; ++ ++ for (i = 0; i < 248; i++) ++ tudata[i+337] = fw->data[145+i]; ++ ++ state->config->reset(fe); ++ ++ if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { ++ pr_warn("error 1\n"); ++ return -1; ++ } ++ msleep(1); ++ ++ if (i2c_writebytes(state,state->config->demod_address, ++ &fw->data[393],8125)) { ++ pr_warn("error 2\n"); ++ return -1; ++ } ++ msleep(1); ++ ++ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { ++ pr_warn("error 3\n"); ++ return -1; ++ } ++ ++ /* Wait at least 5 msec */ ++ msleep(10); ++ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { ++ pr_warn("error 4\n"); ++ return -1; ++ } ++ msleep(10); ++ ++ pr_info("Done.\n"); ++ return 0; ++}; ++ ++static int or51211_setmode(struct dvb_frontend* fe, int mode) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ u8 rec_buf[14]; ++ ++ state->config->setmode(fe, mode); ++ ++ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { ++ pr_warn("error 1\n"); ++ return -1; ++ } ++ ++ /* Wait at least 5 msec */ ++ msleep(10); ++ if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { ++ pr_warn("error 2\n"); ++ return -1; ++ } ++ ++ msleep(10); ++ ++ /* Set operation mode in Receiver 1 register; ++ * type 1: ++ * data 0x50h Automatic sets receiver channel conditions ++ * Automatic NTSC rejection filter ++ * Enable MPEG serial data output ++ * MPEG2tr ++ * High tuner phase noise ++ * normal +/-150kHz Carrier acquisition range ++ */ ++ if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { ++ pr_warn("error 3\n"); ++ return -1; ++ } ++ ++ rec_buf[0] = 0x04; ++ rec_buf[1] = 0x00; ++ rec_buf[2] = 0x03; ++ rec_buf[3] = 0x00; ++ msleep(20); ++ if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { ++ pr_warn("error 5\n"); ++ } ++ msleep(3); ++ if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { ++ pr_warn("error 6\n"); ++ return -1; ++ } ++ dprintk("rec status %02x %02x\n", rec_buf[10], rec_buf[11]); ++ ++ return 0; ++} ++ ++static int or51211_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct or51211_state* state = fe->demodulator_priv; ++ ++ /* Change only if we are actually changing the channel */ ++ if (state->current_frequency != p->frequency) { ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Set to ATSC mode */ ++ or51211_setmode(fe,0); ++ ++ /* Update current frequency */ ++ state->current_frequency = p->frequency; ++ } ++ return 0; ++} ++ ++static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ unsigned char rec_buf[2]; ++ unsigned char snd_buf[] = {0x04,0x00,0x03,0x00}; ++ *status = 0; ++ ++ /* Receiver Status */ ++ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { ++ pr_warn("write error\n"); ++ return -1; ++ } ++ msleep(3); ++ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { ++ pr_warn("read error\n"); ++ return -1; ++ } ++ dprintk("%x %x\n", rec_buf[0], rec_buf[1]); ++ ++ if (rec_buf[0] & 0x01) { /* Receiver Lock */ ++ *status |= FE_HAS_SIGNAL; ++ *status |= FE_HAS_CARRIER; ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ *status |= FE_HAS_LOCK; ++ } ++ return 0; ++} ++ ++/* Calculate SNR estimation (scaled by 2^24) ++ ++ 8-VSB SNR equation from Oren datasheets ++ ++ For 8-VSB: ++ SNR[dB] = 10 * log10(219037.9454 / MSE^2 ) ++ ++ We re-write the snr equation as: ++ SNR * 2^24 = 10*(c - 2*intlog10(MSE)) ++ Where for 8-VSB, c = log10(219037.9454) * 2^24 */ ++ ++static u32 calculate_snr(u32 mse, u32 c) ++{ ++ if (mse == 0) /* No signal */ ++ return 0; ++ ++ mse = 2*intlog10(mse); ++ if (mse > c) { ++ /* Negative SNR, which is possible, but realisticly the ++ demod will lose lock before the signal gets this bad. The ++ API only allows for unsigned values, so just return 0 */ ++ return 0; ++ } ++ return 10*(c - mse); ++} ++ ++static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ u8 rec_buf[2]; ++ u8 snd_buf[3]; ++ ++ /* SNR after Equalizer */ ++ snd_buf[0] = 0x04; ++ snd_buf[1] = 0x00; ++ snd_buf[2] = 0x04; ++ ++ if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { ++ pr_warn("error writing snr reg\n"); ++ return -1; ++ } ++ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { ++ pr_warn("read_status read error\n"); ++ return -1; ++ } ++ ++ state->snr = calculate_snr(rec_buf[0], 89599047); ++ *snr = (state->snr) >> 16; ++ ++ dprintk("noise = 0x%02x, snr = %d.%02d dB\n", rec_buf[0], ++ state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); ++ ++ return 0; ++} ++ ++static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ /* Calculate Strength from SNR up to 35dB */ ++ /* Even though the SNR can go higher than 35dB, there is some comfort */ ++ /* factor in having a range of strong signals that can show at 100% */ ++ struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv; ++ u16 snr; ++ int ret; ++ ++ ret = fe->ops.read_snr(fe, &snr); ++ if (ret != 0) ++ return ret; ++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ ++ /* scale the range 0 - 35*2^24 into 0 - 65535 */ ++ if (state->snr >= 8960 * 0x10000) ++ *strength = 0xffff; ++ else ++ *strength = state->snr / 8960; ++ ++ return 0; ++} ++ ++static int or51211_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ *ber = -ENOSYS; ++ return 0; ++} ++ ++static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ *ucblocks = -ENOSYS; ++ return 0; ++} ++ ++static int or51211_sleep(struct dvb_frontend* fe) ++{ ++ return 0; ++} ++ ++static int or51211_init(struct dvb_frontend* fe) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ const struct or51211_config* config = state->config; ++ const struct firmware* fw; ++ unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00}; ++ unsigned char rec_buf[14]; ++ int ret,i; ++ ++ if (!state->initialized) { ++ /* Request the firmware, this will block until it uploads */ ++ pr_info("Waiting for firmware upload (%s)...\n", ++ OR51211_DEFAULT_FIRMWARE); ++ ret = config->request_firmware(fe, &fw, ++ OR51211_DEFAULT_FIRMWARE); ++ pr_info("Got Hotplug firmware\n"); ++ if (ret) { ++ pr_warn("No firmware uploaded " ++ "(timeout or file not found?)\n"); ++ return ret; ++ } ++ ++ ret = or51211_load_firmware(fe, fw); ++ release_firmware(fw); ++ if (ret) { ++ pr_warn("Writing firmware to device failed!\n"); ++ return ret; ++ } ++ pr_info("Firmware upload complete.\n"); ++ ++ /* Set operation mode in Receiver 1 register; ++ * type 1: ++ * data 0x50h Automatic sets receiver channel conditions ++ * Automatic NTSC rejection filter ++ * Enable MPEG serial data output ++ * MPEG2tr ++ * High tuner phase noise ++ * normal +/-150kHz Carrier acquisition range ++ */ ++ if (i2c_writebytes(state,state->config->demod_address, ++ cmd_buf,3)) { ++ pr_warn("Load DVR Error 5\n"); ++ return -1; ++ } ++ ++ /* Read back ucode version to besure we loaded correctly */ ++ /* and are really up and running */ ++ rec_buf[0] = 0x04; ++ rec_buf[1] = 0x00; ++ rec_buf[2] = 0x03; ++ rec_buf[3] = 0x00; ++ msleep(30); ++ if (i2c_writebytes(state,state->config->demod_address, ++ rec_buf,3)) { ++ pr_warn("Load DVR Error A\n"); ++ return -1; ++ } ++ msleep(3); ++ if (i2c_readbytes(state,state->config->demod_address, ++ &rec_buf[10],2)) { ++ pr_warn("Load DVR Error B\n"); ++ return -1; ++ } ++ ++ rec_buf[0] = 0x04; ++ rec_buf[1] = 0x00; ++ rec_buf[2] = 0x01; ++ rec_buf[3] = 0x00; ++ msleep(20); ++ if (i2c_writebytes(state,state->config->demod_address, ++ rec_buf,3)) { ++ pr_warn("Load DVR Error C\n"); ++ return -1; ++ } ++ msleep(3); ++ if (i2c_readbytes(state,state->config->demod_address, ++ &rec_buf[12],2)) { ++ pr_warn("Load DVR Error D\n"); ++ return -1; ++ } ++ ++ for (i = 0; i < 8; i++) ++ rec_buf[i]=0xed; ++ ++ for (i = 0; i < 5; i++) { ++ msleep(30); ++ get_ver_buf[4] = i+1; ++ if (i2c_writebytes(state,state->config->demod_address, ++ get_ver_buf,5)) { ++ pr_warn("Load DVR Error 6 - %d\n", i); ++ return -1; ++ } ++ msleep(3); ++ ++ if (i2c_readbytes(state,state->config->demod_address, ++ &rec_buf[i*2],2)) { ++ pr_warn("Load DVR Error 7 - %d\n", i); ++ return -1; ++ } ++ /* If we didn't receive the right index, try again */ ++ if ((int)rec_buf[i*2+1]!=i+1){ ++ i--; ++ } ++ } ++ dprintk("read_fwbits %10ph\n", rec_buf); ++ ++ pr_info("ver TU%02x%02x%02x VSB mode %02x Status %02x\n", ++ rec_buf[2], rec_buf[4], rec_buf[6], rec_buf[12], ++ rec_buf[10]); ++ ++ rec_buf[0] = 0x04; ++ rec_buf[1] = 0x00; ++ rec_buf[2] = 0x03; ++ rec_buf[3] = 0x00; ++ msleep(20); ++ if (i2c_writebytes(state,state->config->demod_address, ++ rec_buf,3)) { ++ pr_warn("Load DVR Error 8\n"); ++ return -1; ++ } ++ msleep(20); ++ if (i2c_readbytes(state,state->config->demod_address, ++ &rec_buf[8],2)) { ++ pr_warn("Load DVR Error 9\n"); ++ return -1; ++ } ++ state->initialized = 1; ++ } ++ ++ return 0; ++} ++ ++static int or51211_get_tune_settings(struct dvb_frontend* fe, ++ struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 500; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void or51211_release(struct dvb_frontend* fe) ++{ ++ struct or51211_state* state = fe->demodulator_priv; ++ state->config->sleep(fe); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops or51211_ops; ++ ++struct dvb_frontend* or51211_attach(const struct or51211_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct or51211_state* state = NULL; ++ ++ /* Allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); ++ if (state == NULL) ++ return NULL; ++ ++ /* Setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialized = 0; ++ state->current_frequency = 0; ++ ++ /* Create dvb_frontend */ ++ memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops or51211_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Oren OR51211 VSB Frontend", ++ .frequency_min = 44000000, ++ .frequency_max = 958000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_8VSB ++ }, ++ ++ .release = or51211_release, ++ ++ .init = or51211_init, ++ .sleep = or51211_sleep, ++ ++ .set_frontend = or51211_set_parameters, ++ .get_tune_settings = or51211_get_tune_settings, ++ ++ .read_status = or51211_read_status, ++ .read_ber = or51211_read_ber, ++ .read_signal_strength = or51211_read_signal_strength, ++ .read_snr = or51211_read_snr, ++ .read_ucblocks = or51211_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver"); ++MODULE_AUTHOR("Kirk Lapray"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(or51211_attach); ++ +diff --git a/drivers/media/dvb-frontends/or51211.h b/drivers/media/dvb-frontends/or51211.h +new file mode 100644 +index 0000000..3ce0508 +--- /dev/null ++++ b/drivers/media/dvb-frontends/or51211.h +@@ -0,0 +1,53 @@ ++/* ++ * Support for OR51211 (pcHDTV HD-2000) - VSB ++ * ++ * Copyright (C) 2005 Kirk Lapray ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++*/ ++ ++#ifndef OR51211_H ++#define OR51211_H ++ ++#include ++#include ++ ++struct or51211_config ++{ ++ /* The demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* Request firmware for device */ ++ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); ++ void (*setmode)(struct dvb_frontend * fe, int mode); ++ void (*reset)(struct dvb_frontend * fe); ++ void (*sleep)(struct dvb_frontend * fe); ++}; ++ ++#if defined(CONFIG_DVB_OR51211) || (defined(CONFIG_DVB_OR51211_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_OR51211 ++ ++#endif // OR51211_H ++ +diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c +new file mode 100644 +index 0000000..362d26d +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2830.c +@@ -0,0 +1,761 @@ ++/* ++ * Realtek RTL2830 DVB-T demodulator driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++ ++/* ++ * Driver implements own I2C-adapter for tuner I2C access. That's since chip ++ * have unusual I2C-gate control which closes gate automatically after each ++ * I2C transfer. Using own I2C adapter we can workaround that. ++ */ ++ ++#include "rtl2830_priv.h" ++ ++/* write multiple hardware registers */ ++static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, const u8 *val, int len) ++{ ++ int ret; ++ u8 buf[1+len]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = 1+len, ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = reg; ++ memcpy(&buf[1], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* read multiple hardware registers */ ++static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len) ++{ ++ int ret; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = 1, ++ .buf = ®, ++ }, { ++ .addr = priv->cfg.i2c_addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = val, ++ } ++ }; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* write multiple registers */ ++static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, const u8 *val, ++ int len) ++{ ++ int ret; ++ u8 reg2 = (reg >> 0) & 0xff; ++ u8 page = (reg >> 8) & 0xff; ++ ++ /* switch bank if needed */ ++ if (page != priv->page) { ++ ret = rtl2830_wr(priv, 0x00, &page, 1); ++ if (ret) ++ return ret; ++ ++ priv->page = page; ++ } ++ ++ return rtl2830_wr(priv, reg2, val, len); ++} ++ ++/* read multiple registers */ ++static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len) ++{ ++ int ret; ++ u8 reg2 = (reg >> 0) & 0xff; ++ u8 page = (reg >> 8) & 0xff; ++ ++ /* switch bank if needed */ ++ if (page != priv->page) { ++ ret = rtl2830_wr(priv, 0x00, &page, 1); ++ if (ret) ++ return ret; ++ ++ priv->page = page; ++ } ++ ++ return rtl2830_rd(priv, reg2, val, len); ++} ++ ++/* read single register */ ++static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val) ++{ ++ return rtl2830_rd_regs(priv, reg, val, 1); ++} ++ ++/* write single register with mask */ ++static int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = rtl2830_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return rtl2830_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register with mask */ ++static int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ ret = rtl2830_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ tmp &= mask; ++ ++ /* find position of the first bit */ ++ for (i = 0; i < 8; i++) { ++ if ((mask >> i) & 0x01) ++ break; ++ } ++ *val = tmp >> i; ++ ++ return 0; ++} ++ ++static int rtl2830_init(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ int ret, i; ++ struct rtl2830_reg_val_mask tab[] = { ++ { 0x00d, 0x01, 0x03 }, ++ { 0x00d, 0x10, 0x10 }, ++ { 0x104, 0x00, 0x1e }, ++ { 0x105, 0x80, 0x80 }, ++ { 0x110, 0x02, 0x03 }, ++ { 0x110, 0x08, 0x0c }, ++ { 0x17b, 0x00, 0x40 }, ++ { 0x17d, 0x05, 0x0f }, ++ { 0x17d, 0x50, 0xf0 }, ++ { 0x18c, 0x08, 0x0f }, ++ { 0x18d, 0x00, 0xc0 }, ++ { 0x188, 0x05, 0x0f }, ++ { 0x189, 0x00, 0xfc }, ++ { 0x2d5, 0x02, 0x02 }, ++ { 0x2f1, 0x02, 0x06 }, ++ { 0x2f1, 0x20, 0xf8 }, ++ { 0x16d, 0x00, 0x01 }, ++ { 0x1a6, 0x00, 0x80 }, ++ { 0x106, priv->cfg.vtop, 0x3f }, ++ { 0x107, priv->cfg.krf, 0x3f }, ++ { 0x112, 0x28, 0xff }, ++ { 0x103, priv->cfg.agc_targ_val, 0xff }, ++ { 0x00a, 0x02, 0x07 }, ++ { 0x140, 0x0c, 0x3c }, ++ { 0x140, 0x40, 0xc0 }, ++ { 0x15b, 0x05, 0x07 }, ++ { 0x15b, 0x28, 0x38 }, ++ { 0x15c, 0x05, 0x07 }, ++ { 0x15c, 0x28, 0x38 }, ++ { 0x115, priv->cfg.spec_inv, 0x01 }, ++ { 0x16f, 0x01, 0x07 }, ++ { 0x170, 0x18, 0x38 }, ++ { 0x172, 0x0f, 0x0f }, ++ { 0x173, 0x08, 0x38 }, ++ { 0x175, 0x01, 0x07 }, ++ { 0x176, 0x00, 0xc0 }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret) ++ goto err; ++ } ++ ++ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2); ++ if (ret) ++ goto err; ++ ++ ret = rtl2830_wr_regs(priv, 0x195, ++ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8); ++ if (ret) ++ goto err; ++ ++ /* TODO: spec init */ ++ ++ /* soft reset */ ++ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04); ++ if (ret) ++ goto err; ++ ++ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04); ++ if (ret) ++ goto err; ++ ++ priv->sleeping = false; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_sleep(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ priv->sleeping = true; ++ return 0; ++} ++ ++static int rtl2830_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 500; ++ s->step_size = fe->ops.info.frequency_stepsize * 2; ++ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; ++ ++ return 0; ++} ++ ++static int rtl2830_set_frontend(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i; ++ u64 num; ++ u8 buf[3], tmp; ++ u32 if_ctl, if_frequency; ++ static const u8 bw_params1[3][34] = { ++ { ++ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41, ++ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a, ++ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82, ++ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */ ++ }, { ++ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca, ++ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca, ++ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e, ++ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */ ++ }, { ++ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0, ++ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a, ++ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f, ++ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */ ++ }, ++ }; ++ static const u8 bw_params2[3][6] = { ++ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30}, /* 6 MHz */ ++ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98}, /* 7 MHz */ ++ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */ ++ }; ++ ++ dev_dbg(&priv->i2c->dev, ++ "%s: frequency=%d bandwidth_hz=%d inversion=%d\n", ++ __func__, c->frequency, c->bandwidth_hz, c->inversion); ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ i = 0; ++ break; ++ case 7000000: ++ i = 1; ++ break; ++ case 8000000: ++ i = 2; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__); ++ return -EINVAL; ++ } ++ ++ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06); ++ if (ret) ++ goto err; ++ ++ /* program if frequency */ ++ if (fe->ops.tuner_ops.get_if_frequency) ++ ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); ++ else ++ ret = -EINVAL; ++ ++ if (ret < 0) ++ goto err; ++ ++ num = if_frequency % priv->cfg.xtal; ++ num *= 0x400000; ++ num = div_u64(num, priv->cfg.xtal); ++ num = -num; ++ if_ctl = num & 0x3fffff; ++ dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d if_ctl=%08x\n", ++ __func__, if_frequency, if_ctl); ++ ++ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */ ++ if (ret) ++ goto err; ++ ++ buf[0] = tmp << 6; ++ buf[0] |= (if_ctl >> 16) & 0x3f; ++ buf[1] = (if_ctl >> 8) & 0xff; ++ buf[2] = (if_ctl >> 0) & 0xff; ++ ++ ret = rtl2830_wr_regs(priv, 0x119, buf, 3); ++ if (ret) ++ goto err; ++ ++ /* 1/2 split I2C write */ ++ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17); ++ if (ret) ++ goto err; ++ ++ /* 2/2 split I2C write */ ++ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17); ++ if (ret) ++ goto err; ++ ++ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_get_frontend(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[3]; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2830_rd_regs(priv, 0x33c, buf, 2); ++ if (ret) ++ goto err; ++ ++ ret = rtl2830_rd_reg(priv, 0x351, &buf[2]); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf); ++ ++ switch ((buf[0] >> 2) & 3) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ } ++ ++ switch ((buf[2] >> 2) & 1) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ } ++ ++ switch ((buf[2] >> 0) & 3) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((buf[0] >> 4) & 7) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ switch ((buf[1] >> 3) & 7) { ++ case 0: ++ c->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ switch ((buf[1] >> 0) & 7) { ++ case 0: ++ c->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ *status = 0; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */ ++ if (ret) ++ goto err; ++ ++ if (tmp == 11) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } else if (tmp == 10) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI; ++ } ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ int ret, hierarchy, constellation; ++ u8 buf[2], tmp; ++ u16 tmp16; ++#define CONSTELLATION_NUM 3 ++#define HIERARCHY_NUM 4 ++ static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { ++ { 70705899, 70705899, 70705899, 70705899 }, ++ { 82433173, 82433173, 87483115, 94445660 }, ++ { 92888734, 92888734, 95487525, 99770748 }, ++ }; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ /* reports SNR in resolution of 0.1 dB */ ++ ++ ret = rtl2830_rd_reg(priv, 0x33c, &tmp); ++ if (ret) ++ goto err; ++ ++ constellation = (tmp >> 2) & 0x03; /* [3:2] */ ++ if (constellation > CONSTELLATION_NUM - 1) ++ goto err; ++ ++ hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ ++ if (hierarchy > HIERARCHY_NUM - 1) ++ goto err; ++ ++ ret = rtl2830_rd_regs(priv, 0x40c, buf, 2); ++ if (ret) ++ goto err; ++ ++ tmp16 = buf[0] << 8 | buf[1]; ++ ++ if (tmp16) ++ *snr = (snr_constant[constellation][hierarchy] - ++ intlog10(tmp16)) / ((1 << 24) / 100); ++ else ++ *snr = 0; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2830_rd_regs(priv, 0x34e, buf, 2); ++ if (ret) ++ goto err; ++ ++ *ber = buf[0] << 8 | buf[1]; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ *ucblocks = 0; ++ return 0; ++} ++ ++static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ u16 if_agc_raw, if_agc; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2830_rd_regs(priv, 0x359, buf, 2); ++ if (ret) ++ goto err; ++ ++ if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff; ++ ++ if (if_agc_raw & (1 << 9)) ++ if_agc = -(~(if_agc_raw - 1) & 0x1ff); ++ else ++ if_agc = if_agc_raw; ++ ++ *strength = (u8) (55 - if_agc / 182); ++ *strength |= *strength << 8; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static struct dvb_frontend_ops rtl2830_ops; ++ ++static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg msg[], int num) ++{ ++ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap); ++ int ret; ++ ++ /* open i2c-gate */ ++ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08); ++ if (ret) ++ goto err; ++ ++ ret = i2c_transfer(priv->i2c, msg, num); ++ if (ret < 0) ++ dev_warn(&priv->i2c->dev, "%s: tuner i2c failed=%d\n", ++ KBUILD_MODNAME, ret); ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static struct i2c_algorithm rtl2830_tuner_i2c_algo = { ++ .master_xfer = rtl2830_tuner_i2c_xfer, ++ .functionality = rtl2830_tuner_i2c_func, ++}; ++ ++struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ return &priv->tuner_i2c_adapter; ++} ++EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter); ++ ++static void rtl2830_release(struct dvb_frontend *fe) ++{ ++ struct rtl2830_priv *priv = fe->demodulator_priv; ++ ++ i2c_del_adapter(&priv->tuner_i2c_adapter); ++ kfree(priv); ++} ++ ++struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg, ++ struct i2c_adapter *i2c) ++{ ++ struct rtl2830_priv *priv = NULL; ++ int ret = 0; ++ u8 tmp; ++ ++ /* allocate memory for the internal state */ ++ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL); ++ if (priv == NULL) ++ goto err; ++ ++ /* setup the priv */ ++ priv->i2c = i2c; ++ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config)); ++ ++ /* check if the demod is there */ ++ ret = rtl2830_rd_reg(priv, 0x000, &tmp); ++ if (ret) ++ goto err; ++ ++ /* create dvb_frontend */ ++ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops)); ++ priv->fe.demodulator_priv = priv; ++ ++ /* create tuner i2c adapter */ ++ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter", ++ sizeof(priv->tuner_i2c_adapter.name)); ++ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo; ++ priv->tuner_i2c_adapter.algo_data = NULL; ++ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv); ++ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) { ++ dev_err(&i2c->dev, ++ "%s: tuner i2c bus could not be initialized\n", ++ KBUILD_MODNAME); ++ goto err; ++ } ++ ++ priv->sleeping = true; ++ ++ return &priv->fe; ++err: ++ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(rtl2830_attach); ++ ++static struct dvb_frontend_ops rtl2830_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Realtek RTL2830 (DVB-T)", ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = rtl2830_release, ++ ++ .init = rtl2830_init, ++ .sleep = rtl2830_sleep, ++ ++ .get_tune_settings = rtl2830_get_tune_settings, ++ ++ .set_frontend = rtl2830_set_frontend, ++ .get_frontend = rtl2830_get_frontend, ++ ++ .read_status = rtl2830_read_status, ++ .read_snr = rtl2830_read_snr, ++ .read_ber = rtl2830_read_ber, ++ .read_ucblocks = rtl2830_read_ucblocks, ++ .read_signal_strength = rtl2830_read_signal_strength, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h +new file mode 100644 +index 0000000..f4349a1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2830.h +@@ -0,0 +1,90 @@ ++/* ++ * Realtek RTL2830 DVB-T demodulator driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RTL2830_H ++#define RTL2830_H ++ ++#include ++ ++struct rtl2830_config { ++ /* ++ * Demodulator I2C address. ++ */ ++ u8 i2c_addr; ++ ++ /* ++ * Xtal frequency. ++ * Hz ++ * 4000000, 16000000, 25000000, 28800000 ++ */ ++ u32 xtal; ++ ++ /* ++ * TS output mode. ++ */ ++ u8 ts_mode; ++ ++ /* ++ * Spectrum inversion. ++ */ ++ bool spec_inv; ++ ++ /* ++ */ ++ u8 vtop; ++ ++ /* ++ */ ++ u8 krf; ++ ++ /* ++ */ ++ u8 agc_targ_val; ++}; ++ ++#if defined(CONFIG_DVB_RTL2830) || \ ++ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *rtl2830_attach( ++ const struct rtl2830_config *config, ++ struct i2c_adapter *i2c ++); ++ ++extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( ++ struct dvb_frontend *fe ++); ++#else ++static inline struct dvb_frontend *rtl2830_attach( ++ const struct rtl2830_config *config, ++ struct i2c_adapter *i2c ++) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( ++ struct dvb_frontend *fe ++) ++{ ++ return NULL; ++} ++#endif ++ ++#endif /* RTL2830_H */ +diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h +new file mode 100644 +index 0000000..fab10ec +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2830_priv.h +@@ -0,0 +1,45 @@ ++/* ++ * Realtek RTL2830 DVB-T demodulator driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RTL2830_PRIV_H ++#define RTL2830_PRIV_H ++ ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "rtl2830.h" ++ ++struct rtl2830_priv { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct rtl2830_config cfg; ++ struct i2c_adapter tuner_i2c_adapter; ++ ++ bool sleeping; ++ ++ u8 page; /* active register page */ ++}; ++ ++struct rtl2830_reg_val_mask { ++ u16 reg; ++ u8 val; ++ u8 mask; ++}; ++ ++#endif /* RTL2830_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c +new file mode 100644 +index 0000000..7388769 +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2832.c +@@ -0,0 +1,932 @@ ++/* ++ * Realtek RTL2832 DVB-T demodulator driver ++ * ++ * Copyright (C) 2012 Thomas Mair ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "rtl2832_priv.h" ++#include "dvb_math.h" ++#include ++ ++int rtl2832_debug; ++module_param_named(debug, rtl2832_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++#define REG_MASK(b) (BIT(b + 1) - 1) ++ ++static const struct rtl2832_reg_entry registers[] = { ++ [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2}, ++ [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3}, ++ [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2}, ++ [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0}, ++ [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7}, ++ [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7}, ++ [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6}, ++ [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0}, ++ [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0}, ++ [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0}, ++ [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0}, ++ [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0}, ++ [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0}, ++ [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0}, ++ [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0}, ++ [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0}, ++ [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4}, ++ [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0}, ++ [DVBT_REG_PI] = {0x0, 0xa, 2, 0}, ++ [DVBT_PIP_ON] = {0x0, 0x21, 3, 3}, ++ [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0}, ++ [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0}, ++ [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0}, ++ [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0}, ++ [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0}, ++ [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0}, ++ [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0}, ++ [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0}, ++ [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0}, ++ [DVBT_KB_P1] = {0x1, 0x64, 3, 1}, ++ [DVBT_KB_P2] = {0x1, 0x64, 6, 4}, ++ [DVBT_KB_P3] = {0x1, 0x65, 2, 0}, ++ [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4}, ++ [DVBT_AD_AVI] = {0x0, 0x9, 1, 0}, ++ [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2}, ++ [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4}, ++ [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0}, ++ [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3}, ++ [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0}, ++ [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3}, ++ [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0}, ++ [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6}, ++ [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0}, ++ [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0}, ++ [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2}, ++ [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4}, ++ [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3}, ++ [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2}, ++ [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4}, ++ [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0}, ++ [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3}, ++ [DVBT_GI_IDX] = {0x3, 0x51, 1, 0}, ++ [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2}, ++ [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0}, ++ [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0}, ++ [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0}, ++ [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0}, ++ [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0}, ++ [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0}, ++ [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0}, ++ [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1}, ++ [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0}, ++ [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5}, ++ [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6}, ++ [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7}, ++ [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0}, ++ [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0}, ++ [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0}, ++ [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0}, ++ [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6}, ++ [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0}, ++ [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6}, ++ [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0}, ++ [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0}, ++ [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0}, ++ [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0}, ++ [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1}, ++ [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1}, ++ [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7}, ++ [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0}, ++ [DVBT_VTOP1] = {0x1, 0x6, 5, 0}, ++ [DVBT_VTOP2] = {0x1, 0xc9, 5, 0}, ++ [DVBT_VTOP3] = {0x1, 0xca, 5, 0}, ++ [DVBT_KRF1] = {0x1, 0xcb, 7, 0}, ++ [DVBT_KRF2] = {0x1, 0x7, 7, 0}, ++ [DVBT_KRF3] = {0x1, 0xcd, 7, 0}, ++ [DVBT_KRF4] = {0x1, 0xce, 7, 0}, ++ [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0}, ++ [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0}, ++ [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0}, ++ [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0}, ++ [DVBT_THD_DW1] = {0x1, 0xde, 7, 0}, ++ [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0}, ++ [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3}, ++ [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0}, ++ [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5}, ++ [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6}, ++ [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7}, ++ [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0}, ++ [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1}, ++ [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2}, ++ [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3}, ++ [DVBT_SERIAL] = {0x1, 0x7c, 4, 4}, ++ [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5}, ++ [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0}, ++ [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4}, ++ [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7}, ++ [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6}, ++ [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4}, ++ [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3}, ++ [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2}, ++ [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1}, ++ [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0}, ++ [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4}, ++ [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3}, ++ [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2}, ++ [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1}, ++ [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0}, ++ [DVBT_SM_PASS] = {0x1, 0x93, 11, 0}, ++ [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0}, ++ [DVBT_RSSI_R] = {0x3, 0x1, 6, 0}, ++ [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0}, ++ [DVBT_REG_MON] = {0x0, 0xd, 1, 0}, ++ [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2}, ++ [DVBT_REG_GPE] = {0x0, 0xd, 7, 7}, ++ [DVBT_REG_GPO] = {0x0, 0x10, 0, 0}, ++ [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0}, ++}; ++ ++/* write multiple hardware registers */ ++static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) ++{ ++ int ret; ++ u8 buf[1+len]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = 1+len, ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = reg; ++ memcpy(&buf[1], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* read multiple hardware registers */ ++static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) ++{ ++ int ret; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = priv->cfg.i2c_addr, ++ .flags = 0, ++ .len = 1, ++ .buf = ®, ++ }, { ++ .addr = priv->cfg.i2c_addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = val, ++ } ++ }; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* write multiple registers */ ++static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, ++ int len) ++{ ++ int ret; ++ ++ /* switch bank if needed */ ++ if (page != priv->page) { ++ ret = rtl2832_wr(priv, 0x00, &page, 1); ++ if (ret) ++ return ret; ++ ++ priv->page = page; ++} ++ ++return rtl2832_wr(priv, reg, val, len); ++} ++ ++/* read multiple registers */ ++static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, ++ int len) ++{ ++ int ret; ++ ++ /* switch bank if needed */ ++ if (page != priv->page) { ++ ret = rtl2832_wr(priv, 0x00, &page, 1); ++ if (ret) ++ return ret; ++ ++ priv->page = page; ++ } ++ ++ return rtl2832_rd(priv, reg, val, len); ++} ++ ++#if 0 /* currently not used */ ++/* write single register */ ++static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val) ++{ ++ return rtl2832_wr_regs(priv, reg, page, &val, 1); ++} ++#endif ++ ++/* read single register */ ++static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val) ++{ ++ return rtl2832_rd_regs(priv, reg, page, val, 1); ++} ++ ++static int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val) ++{ ++ int ret; ++ ++ u8 reg_start_addr; ++ u8 msb, lsb; ++ u8 page; ++ u8 reading[4]; ++ u32 reading_tmp; ++ int i; ++ ++ u8 len; ++ u32 mask; ++ ++ reg_start_addr = registers[reg].start_address; ++ msb = registers[reg].msb; ++ lsb = registers[reg].lsb; ++ page = registers[reg].page; ++ ++ len = (msb >> 3) + 1; ++ mask = REG_MASK(msb - lsb); ++ ++ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); ++ if (ret) ++ goto err; ++ ++ reading_tmp = 0; ++ for (i = 0; i < len; i++) ++ reading_tmp |= reading[i] << ((len - 1 - i) * 8); ++ ++ *val = (reading_tmp >> lsb) & mask; ++ ++ return ret; ++ ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++ ++} ++ ++static int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val) ++{ ++ int ret, i; ++ u8 len; ++ u8 reg_start_addr; ++ u8 msb, lsb; ++ u8 page; ++ u32 mask; ++ ++ ++ u8 reading[4]; ++ u8 writing[4]; ++ u32 reading_tmp; ++ u32 writing_tmp; ++ ++ ++ reg_start_addr = registers[reg].start_address; ++ msb = registers[reg].msb; ++ lsb = registers[reg].lsb; ++ page = registers[reg].page; ++ ++ len = (msb >> 3) + 1; ++ mask = REG_MASK(msb - lsb); ++ ++ ++ ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); ++ if (ret) ++ goto err; ++ ++ reading_tmp = 0; ++ for (i = 0; i < len; i++) ++ reading_tmp |= reading[i] << ((len - 1 - i) * 8); ++ ++ writing_tmp = reading_tmp & ~(mask << lsb); ++ writing_tmp |= ((val & mask) << lsb); ++ ++ ++ for (i = 0; i < len; i++) ++ writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; ++ ++ ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len); ++ if (ret) ++ goto err; ++ ++ return ret; ++ ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++ ++} ++ ++static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ int ret; ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ ++ dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable); ++ ++ /* gate already open or close */ ++ if (priv->i2c_gate_state == enable) ++ return 0; ++ ++ ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0)); ++ if (ret) ++ goto err; ++ ++ priv->i2c_gate_state = enable; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_init(struct dvb_frontend *fe) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ int i, ret, len; ++ u8 en_bbin; ++ u64 pset_iffreq; ++ const struct rtl2832_reg_value *init; ++ ++ /* initialization values for the demodulator registers */ ++ struct rtl2832_reg_value rtl2832_initial_regs[] = { ++ {DVBT_AD_EN_REG, 0x1}, ++ {DVBT_AD_EN_REG1, 0x1}, ++ {DVBT_RSD_BER_FAIL_VAL, 0x2800}, ++ {DVBT_MGD_THD0, 0x10}, ++ {DVBT_MGD_THD1, 0x20}, ++ {DVBT_MGD_THD2, 0x20}, ++ {DVBT_MGD_THD3, 0x40}, ++ {DVBT_MGD_THD4, 0x22}, ++ {DVBT_MGD_THD5, 0x32}, ++ {DVBT_MGD_THD6, 0x37}, ++ {DVBT_MGD_THD7, 0x39}, ++ {DVBT_EN_BK_TRK, 0x0}, ++ {DVBT_EN_CACQ_NOTCH, 0x0}, ++ {DVBT_AD_AV_REF, 0x2a}, ++ {DVBT_REG_PI, 0x6}, ++ {DVBT_PIP_ON, 0x0}, ++ {DVBT_CDIV_PH0, 0x8}, ++ {DVBT_CDIV_PH1, 0x8}, ++ {DVBT_SCALE1_B92, 0x4}, ++ {DVBT_SCALE1_B93, 0xb0}, ++ {DVBT_SCALE1_BA7, 0x78}, ++ {DVBT_SCALE1_BA9, 0x28}, ++ {DVBT_SCALE1_BAA, 0x59}, ++ {DVBT_SCALE1_BAB, 0x83}, ++ {DVBT_SCALE1_BAC, 0xd4}, ++ {DVBT_SCALE1_BB0, 0x65}, ++ {DVBT_SCALE1_BB1, 0x43}, ++ {DVBT_KB_P1, 0x1}, ++ {DVBT_KB_P2, 0x4}, ++ {DVBT_KB_P3, 0x7}, ++ {DVBT_K1_CR_STEP12, 0xa}, ++ {DVBT_REG_GPE, 0x1}, ++ {DVBT_SERIAL, 0x0}, ++ {DVBT_CDIV_PH0, 0x9}, ++ {DVBT_CDIV_PH1, 0x9}, ++ {DVBT_MPEG_IO_OPT_2_2, 0x0}, ++ {DVBT_MPEG_IO_OPT_1_0, 0x0}, ++ {DVBT_TRK_KS_P2, 0x4}, ++ {DVBT_TRK_KS_I2, 0x7}, ++ {DVBT_TR_THD_SET2, 0x6}, ++ {DVBT_TRK_KC_I2, 0x5}, ++ {DVBT_CR_THD_SET2, 0x1}, ++ {DVBT_SPEC_INV, 0x0}, ++ }; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ ++ en_bbin = (priv->cfg.if_dvbt == 0 ? 0x1 : 0x0); ++ ++ /* ++ * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) ++ * / CrystalFreqHz) ++ */ ++ pset_iffreq = priv->cfg.if_dvbt % priv->cfg.xtal; ++ pset_iffreq *= 0x400000; ++ pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); ++ pset_iffreq = pset_iffreq & 0x3fffff; ++ ++ for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { ++ ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, ++ rtl2832_initial_regs[i].value); ++ if (ret) ++ goto err; ++ } ++ ++ /* load tuner specific settings */ ++ dev_dbg(&priv->i2c->dev, "%s: load settings for tuner=%02x\n", ++ __func__, priv->cfg.tuner); ++ switch (priv->cfg.tuner) { ++ case RTL2832_TUNER_FC0012: ++ case RTL2832_TUNER_FC0013: ++ len = ARRAY_SIZE(rtl2832_tuner_init_fc0012); ++ init = rtl2832_tuner_init_fc0012; ++ break; ++ case RTL2832_TUNER_TUA9001: ++ len = ARRAY_SIZE(rtl2832_tuner_init_tua9001); ++ init = rtl2832_tuner_init_tua9001; ++ break; ++ case RTL2832_TUNER_E4000: ++ len = ARRAY_SIZE(rtl2832_tuner_init_e4000); ++ init = rtl2832_tuner_init_e4000; ++ break; ++ default: ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (i = 0; i < len; i++) { ++ ret = rtl2832_wr_demod_reg(priv, init[i].reg, init[i].value); ++ if (ret) ++ goto err; ++ } ++ ++ /* if frequency settings */ ++ ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); ++ if (ret) ++ goto err; ++ ++ ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); ++ if (ret) ++ goto err; ++ ++ priv->sleeping = false; ++ ++ return ret; ++ ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_sleep(struct dvb_frontend *fe) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ priv->sleeping = true; ++ return 0; ++} ++ ++static int rtl2832_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ s->min_delay_ms = 1000; ++ s->step_size = fe->ops.info.frequency_stepsize * 2; ++ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; ++ return 0; ++} ++ ++static int rtl2832_set_frontend(struct dvb_frontend *fe) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i, j; ++ u64 bw_mode, num, num2; ++ u32 resamp_ratio, cfreq_off_ratio; ++ static u8 bw_params[3][32] = { ++ /* 6 MHz bandwidth */ ++ { ++ 0xf5, 0xff, 0x15, 0x38, 0x5d, 0x6d, 0x52, 0x07, 0xfa, 0x2f, ++ 0x53, 0xf5, 0x3f, 0xca, 0x0b, 0x91, 0xea, 0x30, 0x63, 0xb2, ++ 0x13, 0xda, 0x0b, 0xc4, 0x18, 0x7e, 0x16, 0x66, 0x08, 0x67, ++ 0x19, 0xe0, ++ }, ++ ++ /* 7 MHz bandwidth */ ++ { ++ 0xe7, 0xcc, 0xb5, 0xba, 0xe8, 0x2f, 0x67, 0x61, 0x00, 0xaf, ++ 0x86, 0xf2, 0xbf, 0x59, 0x04, 0x11, 0xb6, 0x33, 0xa4, 0x30, ++ 0x15, 0x10, 0x0a, 0x42, 0x18, 0xf8, 0x17, 0xd9, 0x07, 0x22, ++ 0x19, 0x10, ++ }, ++ ++ /* 8 MHz bandwidth */ ++ { ++ 0x09, 0xf6, 0xd2, 0xa7, 0x9a, 0xc9, 0x27, 0x77, 0x06, 0xbf, ++ 0xec, 0xf4, 0x4f, 0x0b, 0xfc, 0x01, 0x63, 0x35, 0x54, 0xa7, ++ 0x16, 0x66, 0x08, 0xb4, 0x19, 0x6e, 0x19, 0x65, 0x05, 0xc8, ++ 0x19, 0xe0, ++ }, ++ }; ++ ++ ++ dev_dbg(&priv->i2c->dev, "%s: frequency=%d bandwidth_hz=%d " \ ++ "inversion=%d\n", __func__, c->frequency, ++ c->bandwidth_hz, c->inversion); ++ ++ /* program tuner */ ++ if (fe->ops.tuner_ops.set_params) ++ fe->ops.tuner_ops.set_params(fe); ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ i = 0; ++ bw_mode = 48000000; ++ break; ++ case 7000000: ++ i = 1; ++ bw_mode = 56000000; ++ break; ++ case 8000000: ++ i = 2; ++ bw_mode = 64000000; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__); ++ return -EINVAL; ++ } ++ ++ for (j = 0; j < sizeof(bw_params[0]); j++) { ++ ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1); ++ if (ret) ++ goto err; ++ } ++ ++ /* calculate and set resample ratio ++ * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22) ++ * / ConstWithBandwidthMode) ++ */ ++ num = priv->cfg.xtal * 7; ++ num *= 0x400000; ++ num = div_u64(num, bw_mode); ++ resamp_ratio = num & 0x3ffffff; ++ ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio); ++ if (ret) ++ goto err; ++ ++ /* calculate and set cfreq off ratio ++ * CFREQ_OFF_RATIO = - floor(ConstWithBandwidthMode * pow(2, 20) ++ * / (CrystalFreqHz * 7)) ++ */ ++ num = bw_mode << 20; ++ num2 = priv->cfg.xtal * 7; ++ num = div_u64(num, num2); ++ num = -num; ++ cfreq_off_ratio = num & 0xfffff; ++ ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio); ++ if (ret) ++ goto err; ++ ++ ++ /* soft reset */ ++ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); ++ if (ret) ++ goto err; ++ ++ ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); ++ if (ret) ++ goto err; ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_get_frontend(struct dvb_frontend *fe) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret; ++ u8 buf[3]; ++ ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2832_rd_regs(priv, 0x3c, 3, buf, 2); ++ if (ret) ++ goto err; ++ ++ ret = rtl2832_rd_reg(priv, 0x51, 3, &buf[2]); ++ if (ret) ++ goto err; ++ ++ dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf); ++ ++ switch ((buf[0] >> 2) & 3) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ } ++ ++ switch ((buf[2] >> 2) & 1) { ++ case 0: ++ c->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ c->transmission_mode = TRANSMISSION_MODE_8K; ++ } ++ ++ switch ((buf[2] >> 0) & 3) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ switch ((buf[0] >> 4) & 7) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ switch ((buf[1] >> 3) & 7) { ++ case 0: ++ c->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ switch ((buf[1] >> 0) & 7) { ++ case 0: ++ c->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ c->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ c->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ c->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ c->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ int ret; ++ u32 tmp; ++ *status = 0; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ if (priv->sleeping) ++ return 0; ++ ++ ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp); ++ if (ret) ++ goto err; ++ ++ if (tmp == 11) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ /* TODO find out if this is also true for rtl2832? */ ++ /*else if (tmp == 10) { ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | ++ FE_HAS_VITERBI; ++ }*/ ++ ++ return ret; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ int ret, hierarchy, constellation; ++ u8 buf[2], tmp; ++ u16 tmp16; ++#define CONSTELLATION_NUM 3 ++#define HIERARCHY_NUM 4 ++ static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { ++ { 85387325, 85387325, 85387325, 85387325 }, ++ { 86676178, 86676178, 87167949, 87795660 }, ++ { 87659938, 87659938, 87885178, 88241743 }, ++ }; ++ ++ /* reports SNR in resolution of 0.1 dB */ ++ ++ ret = rtl2832_rd_reg(priv, 0x3c, 3, &tmp); ++ if (ret) ++ goto err; ++ ++ constellation = (tmp >> 2) & 0x03; /* [3:2] */ ++ if (constellation > CONSTELLATION_NUM - 1) ++ goto err; ++ ++ hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ ++ if (hierarchy > HIERARCHY_NUM - 1) ++ goto err; ++ ++ ret = rtl2832_rd_regs(priv, 0x0c, 4, buf, 2); ++ if (ret) ++ goto err; ++ ++ tmp16 = buf[0] << 8 | buf[1]; ++ ++ if (tmp16) ++ *snr = (snr_constant[constellation][hierarchy] - ++ intlog10(tmp16)) / ((1 << 24) / 100); ++ else ++ *snr = 0; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ ret = rtl2832_rd_regs(priv, 0x4e, 3, buf, 2); ++ if (ret) ++ goto err; ++ ++ *ber = buf[0] << 8 | buf[1]; ++ ++ return 0; ++err: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static struct dvb_frontend_ops rtl2832_ops; ++ ++static void rtl2832_release(struct dvb_frontend *fe) ++{ ++ struct rtl2832_priv *priv = fe->demodulator_priv; ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ kfree(priv); ++} ++ ++struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg, ++ struct i2c_adapter *i2c) ++{ ++ struct rtl2832_priv *priv = NULL; ++ int ret = 0; ++ u8 tmp; ++ ++ dev_dbg(&i2c->dev, "%s:\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL); ++ if (priv == NULL) ++ goto err; ++ ++ /* setup the priv */ ++ priv->i2c = i2c; ++ priv->tuner = cfg->tuner; ++ memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config)); ++ ++ /* check if the demod is there */ ++ ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp); ++ if (ret) ++ goto err; ++ ++ /* create dvb_frontend */ ++ memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); ++ priv->fe.demodulator_priv = priv; ++ ++ /* TODO implement sleep mode */ ++ priv->sleeping = true; ++ ++ return &priv->fe; ++err: ++ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(rtl2832_attach); ++ ++static struct dvb_frontend_ops rtl2832_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Realtek RTL2832 (DVB-T)", ++ .frequency_min = 174000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 166667, ++ .caps = FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = rtl2832_release, ++ ++ .init = rtl2832_init, ++ .sleep = rtl2832_sleep, ++ ++ .get_tune_settings = rtl2832_get_tune_settings, ++ ++ .set_frontend = rtl2832_set_frontend, ++ .get_frontend = rtl2832_get_frontend, ++ ++ .read_status = rtl2832_read_status, ++ .read_snr = rtl2832_read_snr, ++ .read_ber = rtl2832_read_ber, ++ ++ .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl, ++}; ++ ++MODULE_AUTHOR("Thomas Mair "); ++MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.5"); +diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h +new file mode 100644 +index 0000000..785a466 +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2832.h +@@ -0,0 +1,75 @@ ++/* ++ * Realtek RTL2832 DVB-T demodulator driver ++ * ++ * Copyright (C) 2012 Thomas Mair ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RTL2832_H ++#define RTL2832_H ++ ++#include ++ ++struct rtl2832_config { ++ /* ++ * Demodulator I2C address. ++ */ ++ u8 i2c_addr; ++ ++ /* ++ * Xtal frequency. ++ * Hz ++ * 4000000, 16000000, 25000000, 28800000 ++ */ ++ u32 xtal; ++ ++ /* ++ * IFs for all used modes. ++ * Hz ++ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000 ++ */ ++ u32 if_dvbt; ++ ++ /* ++ * tuner ++ * XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver. ++ */ ++#define RTL2832_TUNER_TUA9001 0x24 ++#define RTL2832_TUNER_FC0012 0x26 ++#define RTL2832_TUNER_E4000 0x27 ++#define RTL2832_TUNER_FC0013 0x29 ++ u8 tuner; ++}; ++ ++#if defined(CONFIG_DVB_RTL2832) || \ ++ (defined(CONFIG_DVB_RTL2832_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *rtl2832_attach( ++ const struct rtl2832_config *cfg, ++ struct i2c_adapter *i2c ++); ++#else ++static inline struct dvb_frontend *rtl2832_attach( ++ const struct rtl2832_config *config, ++ struct i2c_adapter *i2c ++) ++{ ++ pr_warn("%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++ ++#endif /* RTL2832_H */ +diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h +new file mode 100644 +index 0000000..7d97ce9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/rtl2832_priv.h +@@ -0,0 +1,342 @@ ++/* ++ * Realtek RTL2832 DVB-T demodulator driver ++ * ++ * Copyright (C) 2012 Thomas Mair ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RTL2832_PRIV_H ++#define RTL2832_PRIV_H ++ ++#include "dvb_frontend.h" ++#include "rtl2832.h" ++ ++struct rtl2832_priv { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct rtl2832_config cfg; ++ ++ bool i2c_gate_state; ++ bool sleeping; ++ ++ u8 tuner; ++ u8 page; /* active register page */ ++}; ++ ++struct rtl2832_reg_entry { ++ u8 page; ++ u8 start_address; ++ u8 msb; ++ u8 lsb; ++}; ++ ++struct rtl2832_reg_value { ++ int reg; ++ u32 value; ++}; ++ ++ ++/* Demod register bit names */ ++enum DVBT_REG_BIT_NAME { ++ DVBT_SOFT_RST, ++ DVBT_IIC_REPEAT, ++ DVBT_TR_WAIT_MIN_8K, ++ DVBT_RSD_BER_FAIL_VAL, ++ DVBT_EN_BK_TRK, ++ DVBT_REG_PI, ++ DVBT_REG_PFREQ_1_0, ++ DVBT_PD_DA8, ++ DVBT_LOCK_TH, ++ DVBT_BER_PASS_SCAL, ++ DVBT_CE_FFSM_BYPASS, ++ DVBT_ALPHAIIR_N, ++ DVBT_ALPHAIIR_DIF, ++ DVBT_EN_TRK_SPAN, ++ DVBT_LOCK_TH_LEN, ++ DVBT_CCI_THRE, ++ DVBT_CCI_MON_SCAL, ++ DVBT_CCI_M0, ++ DVBT_CCI_M1, ++ DVBT_CCI_M2, ++ DVBT_CCI_M3, ++ DVBT_SPEC_INIT_0, ++ DVBT_SPEC_INIT_1, ++ DVBT_SPEC_INIT_2, ++ DVBT_AD_EN_REG, ++ DVBT_AD_EN_REG1, ++ DVBT_EN_BBIN, ++ DVBT_MGD_THD0, ++ DVBT_MGD_THD1, ++ DVBT_MGD_THD2, ++ DVBT_MGD_THD3, ++ DVBT_MGD_THD4, ++ DVBT_MGD_THD5, ++ DVBT_MGD_THD6, ++ DVBT_MGD_THD7, ++ DVBT_EN_CACQ_NOTCH, ++ DVBT_AD_AV_REF, ++ DVBT_PIP_ON, ++ DVBT_SCALE1_B92, ++ DVBT_SCALE1_B93, ++ DVBT_SCALE1_BA7, ++ DVBT_SCALE1_BA9, ++ DVBT_SCALE1_BAA, ++ DVBT_SCALE1_BAB, ++ DVBT_SCALE1_BAC, ++ DVBT_SCALE1_BB0, ++ DVBT_SCALE1_BB1, ++ DVBT_KB_P1, ++ DVBT_KB_P2, ++ DVBT_KB_P3, ++ DVBT_OPT_ADC_IQ, ++ DVBT_AD_AVI, ++ DVBT_AD_AVQ, ++ DVBT_K1_CR_STEP12, ++ DVBT_TRK_KS_P2, ++ DVBT_TRK_KS_I2, ++ DVBT_TR_THD_SET2, ++ DVBT_TRK_KC_P2, ++ DVBT_TRK_KC_I2, ++ DVBT_CR_THD_SET2, ++ DVBT_PSET_IFFREQ, ++ DVBT_SPEC_INV, ++ DVBT_BW_INDEX, ++ DVBT_RSAMP_RATIO, ++ DVBT_CFREQ_OFF_RATIO, ++ DVBT_FSM_STAGE, ++ DVBT_RX_CONSTEL, ++ DVBT_RX_HIER, ++ DVBT_RX_C_RATE_LP, ++ DVBT_RX_C_RATE_HP, ++ DVBT_GI_IDX, ++ DVBT_FFT_MODE_IDX, ++ DVBT_RSD_BER_EST, ++ DVBT_CE_EST_EVM, ++ DVBT_RF_AGC_VAL, ++ DVBT_IF_AGC_VAL, ++ DVBT_DAGC_VAL, ++ DVBT_SFREQ_OFF, ++ DVBT_CFREQ_OFF, ++ DVBT_POLAR_RF_AGC, ++ DVBT_POLAR_IF_AGC, ++ DVBT_AAGC_HOLD, ++ DVBT_EN_RF_AGC, ++ DVBT_EN_IF_AGC, ++ DVBT_IF_AGC_MIN, ++ DVBT_IF_AGC_MAX, ++ DVBT_RF_AGC_MIN, ++ DVBT_RF_AGC_MAX, ++ DVBT_IF_AGC_MAN, ++ DVBT_IF_AGC_MAN_VAL, ++ DVBT_RF_AGC_MAN, ++ DVBT_RF_AGC_MAN_VAL, ++ DVBT_DAGC_TRG_VAL, ++ DVBT_AGC_TARG_VAL, ++ DVBT_LOOP_GAIN_3_0, ++ DVBT_LOOP_GAIN_4, ++ DVBT_VTOP, ++ DVBT_KRF, ++ DVBT_AGC_TARG_VAL_0, ++ DVBT_AGC_TARG_VAL_8_1, ++ DVBT_AAGC_LOOP_GAIN, ++ DVBT_LOOP_GAIN2_3_0, ++ DVBT_LOOP_GAIN2_4, ++ DVBT_LOOP_GAIN3, ++ DVBT_VTOP1, ++ DVBT_VTOP2, ++ DVBT_VTOP3, ++ DVBT_KRF1, ++ DVBT_KRF2, ++ DVBT_KRF3, ++ DVBT_KRF4, ++ DVBT_EN_GI_PGA, ++ DVBT_THD_LOCK_UP, ++ DVBT_THD_LOCK_DW, ++ DVBT_THD_UP1, ++ DVBT_THD_DW1, ++ DVBT_INTER_CNT_LEN, ++ DVBT_GI_PGA_STATE, ++ DVBT_EN_AGC_PGA, ++ DVBT_CKOUTPAR, ++ DVBT_CKOUT_PWR, ++ DVBT_SYNC_DUR, ++ DVBT_ERR_DUR, ++ DVBT_SYNC_LVL, ++ DVBT_ERR_LVL, ++ DVBT_VAL_LVL, ++ DVBT_SERIAL, ++ DVBT_SER_LSB, ++ DVBT_CDIV_PH0, ++ DVBT_CDIV_PH1, ++ DVBT_MPEG_IO_OPT_2_2, ++ DVBT_MPEG_IO_OPT_1_0, ++ DVBT_CKOUTPAR_PIP, ++ DVBT_CKOUT_PWR_PIP, ++ DVBT_SYNC_LVL_PIP, ++ DVBT_ERR_LVL_PIP, ++ DVBT_VAL_LVL_PIP, ++ DVBT_CKOUTPAR_PID, ++ DVBT_CKOUT_PWR_PID, ++ DVBT_SYNC_LVL_PID, ++ DVBT_ERR_LVL_PID, ++ DVBT_VAL_LVL_PID, ++ DVBT_SM_PASS, ++ DVBT_UPDATE_REG_2, ++ DVBT_BTHD_P3, ++ DVBT_BTHD_D3, ++ DVBT_FUNC4_REG0, ++ DVBT_FUNC4_REG1, ++ DVBT_FUNC4_REG2, ++ DVBT_FUNC4_REG3, ++ DVBT_FUNC4_REG4, ++ DVBT_FUNC4_REG5, ++ DVBT_FUNC4_REG6, ++ DVBT_FUNC4_REG7, ++ DVBT_FUNC4_REG8, ++ DVBT_FUNC4_REG9, ++ DVBT_FUNC4_REG10, ++ DVBT_FUNC5_REG0, ++ DVBT_FUNC5_REG1, ++ DVBT_FUNC5_REG2, ++ DVBT_FUNC5_REG3, ++ DVBT_FUNC5_REG4, ++ DVBT_FUNC5_REG5, ++ DVBT_FUNC5_REG6, ++ DVBT_FUNC5_REG7, ++ DVBT_FUNC5_REG8, ++ DVBT_FUNC5_REG9, ++ DVBT_FUNC5_REG10, ++ DVBT_FUNC5_REG11, ++ DVBT_FUNC5_REG12, ++ DVBT_FUNC5_REG13, ++ DVBT_FUNC5_REG14, ++ DVBT_FUNC5_REG15, ++ DVBT_FUNC5_REG16, ++ DVBT_FUNC5_REG17, ++ DVBT_FUNC5_REG18, ++ DVBT_AD7_SETTING, ++ DVBT_RSSI_R, ++ DVBT_ACI_DET_IND, ++ DVBT_REG_MON, ++ DVBT_REG_MONSEL, ++ DVBT_REG_GPE, ++ DVBT_REG_GPO, ++ DVBT_REG_4MSEL, ++ DVBT_TEST_REG_1, ++ DVBT_TEST_REG_2, ++ DVBT_TEST_REG_3, ++ DVBT_TEST_REG_4, ++ DVBT_REG_BIT_NAME_ITEM_TERMINATOR, ++}; ++ ++static const struct rtl2832_reg_value rtl2832_tuner_init_tua9001[] = { ++ {DVBT_DAGC_TRG_VAL, 0x39}, ++ {DVBT_AGC_TARG_VAL_0, 0x0}, ++ {DVBT_AGC_TARG_VAL_8_1, 0x5a}, ++ {DVBT_AAGC_LOOP_GAIN, 0x16}, ++ {DVBT_LOOP_GAIN2_3_0, 0x6}, ++ {DVBT_LOOP_GAIN2_4, 0x1}, ++ {DVBT_LOOP_GAIN3, 0x16}, ++ {DVBT_VTOP1, 0x35}, ++ {DVBT_VTOP2, 0x21}, ++ {DVBT_VTOP3, 0x21}, ++ {DVBT_KRF1, 0x0}, ++ {DVBT_KRF2, 0x40}, ++ {DVBT_KRF3, 0x10}, ++ {DVBT_KRF4, 0x10}, ++ {DVBT_IF_AGC_MIN, 0x80}, ++ {DVBT_IF_AGC_MAX, 0x7f}, ++ {DVBT_RF_AGC_MIN, 0x9c}, ++ {DVBT_RF_AGC_MAX, 0x7f}, ++ {DVBT_POLAR_RF_AGC, 0x0}, ++ {DVBT_POLAR_IF_AGC, 0x0}, ++ {DVBT_AD7_SETTING, 0xe9f4}, ++ {DVBT_OPT_ADC_IQ, 0x1}, ++ {DVBT_AD_AVI, 0x0}, ++ {DVBT_AD_AVQ, 0x0}, ++}; ++ ++static const struct rtl2832_reg_value rtl2832_tuner_init_fc0012[] = { ++ {DVBT_DAGC_TRG_VAL, 0x5a}, ++ {DVBT_AGC_TARG_VAL_0, 0x0}, ++ {DVBT_AGC_TARG_VAL_8_1, 0x5a}, ++ {DVBT_AAGC_LOOP_GAIN, 0x16}, ++ {DVBT_LOOP_GAIN2_3_0, 0x6}, ++ {DVBT_LOOP_GAIN2_4, 0x1}, ++ {DVBT_LOOP_GAIN3, 0x16}, ++ {DVBT_VTOP1, 0x35}, ++ {DVBT_VTOP2, 0x21}, ++ {DVBT_VTOP3, 0x21}, ++ {DVBT_KRF1, 0x0}, ++ {DVBT_KRF2, 0x40}, ++ {DVBT_KRF3, 0x10}, ++ {DVBT_KRF4, 0x10}, ++ {DVBT_IF_AGC_MIN, 0x80}, ++ {DVBT_IF_AGC_MAX, 0x7f}, ++ {DVBT_RF_AGC_MIN, 0x80}, ++ {DVBT_RF_AGC_MAX, 0x7f}, ++ {DVBT_POLAR_RF_AGC, 0x0}, ++ {DVBT_POLAR_IF_AGC, 0x0}, ++ {DVBT_AD7_SETTING, 0xe9bf}, ++ {DVBT_EN_GI_PGA, 0x0}, ++ {DVBT_THD_LOCK_UP, 0x0}, ++ {DVBT_THD_LOCK_DW, 0x0}, ++ {DVBT_THD_UP1, 0x11}, ++ {DVBT_THD_DW1, 0xef}, ++ {DVBT_INTER_CNT_LEN, 0xc}, ++ {DVBT_GI_PGA_STATE, 0x0}, ++ {DVBT_EN_AGC_PGA, 0x1}, ++ {DVBT_IF_AGC_MAN, 0x0}, ++}; ++ ++static const struct rtl2832_reg_value rtl2832_tuner_init_e4000[] = { ++ {DVBT_DAGC_TRG_VAL, 0x5a}, ++ {DVBT_AGC_TARG_VAL_0, 0x0}, ++ {DVBT_AGC_TARG_VAL_8_1, 0x5a}, ++ {DVBT_AAGC_LOOP_GAIN, 0x18}, ++ {DVBT_LOOP_GAIN2_3_0, 0x8}, ++ {DVBT_LOOP_GAIN2_4, 0x1}, ++ {DVBT_LOOP_GAIN3, 0x18}, ++ {DVBT_VTOP1, 0x35}, ++ {DVBT_VTOP2, 0x21}, ++ {DVBT_VTOP3, 0x21}, ++ {DVBT_KRF1, 0x0}, ++ {DVBT_KRF2, 0x40}, ++ {DVBT_KRF3, 0x10}, ++ {DVBT_KRF4, 0x10}, ++ {DVBT_IF_AGC_MIN, 0x80}, ++ {DVBT_IF_AGC_MAX, 0x7f}, ++ {DVBT_RF_AGC_MIN, 0x80}, ++ {DVBT_RF_AGC_MAX, 0x7f}, ++ {DVBT_POLAR_RF_AGC, 0x0}, ++ {DVBT_POLAR_IF_AGC, 0x0}, ++ {DVBT_AD7_SETTING, 0xe9d4}, ++ {DVBT_EN_GI_PGA, 0x0}, ++ {DVBT_THD_LOCK_UP, 0x0}, ++ {DVBT_THD_LOCK_DW, 0x0}, ++ {DVBT_THD_UP1, 0x14}, ++ {DVBT_THD_DW1, 0xec}, ++ {DVBT_INTER_CNT_LEN, 0xc}, ++ {DVBT_GI_PGA_STATE, 0x0}, ++ {DVBT_EN_AGC_PGA, 0x1}, ++ {DVBT_REG_GPE, 0x1}, ++ {DVBT_REG_GPO, 0x1}, ++ {DVBT_REG_MONSEL, 0x1}, ++ {DVBT_REG_MON, 0x1}, ++ {DVBT_REG_4MSEL, 0x0}, ++}; ++ ++#endif /* RTL2832_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c +new file mode 100644 +index 0000000..f71b062 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1409.c +@@ -0,0 +1,1029 @@ ++/* ++ Samsung S5H1409 VSB/QAM demodulator driver ++ ++ Copyright (C) 2006 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "s5h1409.h" ++ ++struct s5h1409_state { ++ ++ struct i2c_adapter *i2c; ++ ++ /* configuration settings */ ++ const struct s5h1409_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ /* previous uncorrected block counter */ ++ fe_modulation_t current_modulation; ++ ++ u32 current_frequency; ++ int if_freq; ++ ++ u32 is_qam_locked; ++ ++ /* QAM tuning state goes through the following state transitions */ ++#define QAM_STATE_UNTUNED 0 ++#define QAM_STATE_TUNING_STARTED 1 ++#define QAM_STATE_INTERLEAVE_SET 2 ++#define QAM_STATE_QAM_OPTIMIZED_L1 3 ++#define QAM_STATE_QAM_OPTIMIZED_L2 4 ++#define QAM_STATE_QAM_OPTIMIZED_L3 5 ++ u8 qam_state; ++}; ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++#define dprintk if (debug) printk ++ ++/* Register values to initialise the demod, this will set VSB by default */ ++static struct init_tab { ++ u8 reg; ++ u16 data; ++} init_tab[] = { ++ { 0x00, 0x0071, }, ++ { 0x01, 0x3213, }, ++ { 0x09, 0x0025, }, ++ { 0x1c, 0x001d, }, ++ { 0x1f, 0x002d, }, ++ { 0x20, 0x001d, }, ++ { 0x22, 0x0022, }, ++ { 0x23, 0x0020, }, ++ { 0x29, 0x110f, }, ++ { 0x2a, 0x10b4, }, ++ { 0x2b, 0x10ae, }, ++ { 0x2c, 0x0031, }, ++ { 0x31, 0x010d, }, ++ { 0x32, 0x0100, }, ++ { 0x44, 0x0510, }, ++ { 0x54, 0x0104, }, ++ { 0x58, 0x2222, }, ++ { 0x59, 0x1162, }, ++ { 0x5a, 0x3211, }, ++ { 0x5d, 0x0370, }, ++ { 0x5e, 0x0296, }, ++ { 0x61, 0x0010, }, ++ { 0x63, 0x4a00, }, ++ { 0x65, 0x0800, }, ++ { 0x71, 0x0003, }, ++ { 0x72, 0x0470, }, ++ { 0x81, 0x0002, }, ++ { 0x82, 0x0600, }, ++ { 0x86, 0x0002, }, ++ { 0x8a, 0x2c38, }, ++ { 0x8b, 0x2a37, }, ++ { 0x92, 0x302f, }, ++ { 0x93, 0x3332, }, ++ { 0x96, 0x000c, }, ++ { 0x99, 0x0101, }, ++ { 0x9c, 0x2e37, }, ++ { 0x9d, 0x2c37, }, ++ { 0x9e, 0x2c37, }, ++ { 0xab, 0x0100, }, ++ { 0xac, 0x1003, }, ++ { 0xad, 0x103f, }, ++ { 0xe2, 0x0100, }, ++ { 0xe3, 0x1000, }, ++ { 0x28, 0x1010, }, ++ { 0xb1, 0x000e, }, ++}; ++ ++/* VSB SNR lookup table */ ++static struct vsb_snr_tab { ++ u16 val; ++ u16 data; ++} vsb_snr_tab[] = { ++ { 924, 300, }, ++ { 923, 300, }, ++ { 918, 295, }, ++ { 915, 290, }, ++ { 911, 285, }, ++ { 906, 280, }, ++ { 901, 275, }, ++ { 896, 270, }, ++ { 891, 265, }, ++ { 885, 260, }, ++ { 879, 255, }, ++ { 873, 250, }, ++ { 864, 245, }, ++ { 858, 240, }, ++ { 850, 235, }, ++ { 841, 230, }, ++ { 832, 225, }, ++ { 823, 220, }, ++ { 812, 215, }, ++ { 802, 210, }, ++ { 788, 205, }, ++ { 778, 200, }, ++ { 767, 195, }, ++ { 753, 190, }, ++ { 740, 185, }, ++ { 725, 180, }, ++ { 707, 175, }, ++ { 689, 170, }, ++ { 671, 165, }, ++ { 656, 160, }, ++ { 637, 155, }, ++ { 616, 150, }, ++ { 542, 145, }, ++ { 519, 140, }, ++ { 507, 135, }, ++ { 497, 130, }, ++ { 492, 125, }, ++ { 474, 120, }, ++ { 300, 111, }, ++ { 0, 0, }, ++}; ++ ++/* QAM64 SNR lookup table */ ++static struct qam64_snr_tab { ++ u16 val; ++ u16 data; ++} qam64_snr_tab[] = { ++ { 1, 0, }, ++ { 12, 300, }, ++ { 15, 290, }, ++ { 18, 280, }, ++ { 22, 270, }, ++ { 23, 268, }, ++ { 24, 266, }, ++ { 25, 264, }, ++ { 27, 262, }, ++ { 28, 260, }, ++ { 29, 258, }, ++ { 30, 256, }, ++ { 32, 254, }, ++ { 33, 252, }, ++ { 34, 250, }, ++ { 35, 249, }, ++ { 36, 248, }, ++ { 37, 247, }, ++ { 38, 246, }, ++ { 39, 245, }, ++ { 40, 244, }, ++ { 41, 243, }, ++ { 42, 241, }, ++ { 43, 240, }, ++ { 44, 239, }, ++ { 45, 238, }, ++ { 46, 237, }, ++ { 47, 236, }, ++ { 48, 235, }, ++ { 49, 234, }, ++ { 50, 233, }, ++ { 51, 232, }, ++ { 52, 231, }, ++ { 53, 230, }, ++ { 55, 229, }, ++ { 56, 228, }, ++ { 57, 227, }, ++ { 58, 226, }, ++ { 59, 225, }, ++ { 60, 224, }, ++ { 62, 223, }, ++ { 63, 222, }, ++ { 65, 221, }, ++ { 66, 220, }, ++ { 68, 219, }, ++ { 69, 218, }, ++ { 70, 217, }, ++ { 72, 216, }, ++ { 73, 215, }, ++ { 75, 214, }, ++ { 76, 213, }, ++ { 78, 212, }, ++ { 80, 211, }, ++ { 81, 210, }, ++ { 83, 209, }, ++ { 84, 208, }, ++ { 85, 207, }, ++ { 87, 206, }, ++ { 89, 205, }, ++ { 91, 204, }, ++ { 93, 203, }, ++ { 95, 202, }, ++ { 96, 201, }, ++ { 104, 200, }, ++ { 255, 0, }, ++}; ++ ++/* QAM256 SNR lookup table */ ++static struct qam256_snr_tab { ++ u16 val; ++ u16 data; ++} qam256_snr_tab[] = { ++ { 1, 0, }, ++ { 12, 400, }, ++ { 13, 390, }, ++ { 15, 380, }, ++ { 17, 360, }, ++ { 19, 350, }, ++ { 22, 348, }, ++ { 23, 346, }, ++ { 24, 344, }, ++ { 25, 342, }, ++ { 26, 340, }, ++ { 27, 336, }, ++ { 28, 334, }, ++ { 29, 332, }, ++ { 30, 330, }, ++ { 31, 328, }, ++ { 32, 326, }, ++ { 33, 325, }, ++ { 34, 322, }, ++ { 35, 320, }, ++ { 37, 318, }, ++ { 39, 316, }, ++ { 40, 314, }, ++ { 41, 312, }, ++ { 42, 310, }, ++ { 43, 308, }, ++ { 46, 306, }, ++ { 47, 304, }, ++ { 49, 302, }, ++ { 51, 300, }, ++ { 53, 298, }, ++ { 54, 297, }, ++ { 55, 296, }, ++ { 56, 295, }, ++ { 57, 294, }, ++ { 59, 293, }, ++ { 60, 292, }, ++ { 61, 291, }, ++ { 63, 290, }, ++ { 64, 289, }, ++ { 65, 288, }, ++ { 66, 287, }, ++ { 68, 286, }, ++ { 69, 285, }, ++ { 71, 284, }, ++ { 72, 283, }, ++ { 74, 282, }, ++ { 75, 281, }, ++ { 76, 280, }, ++ { 77, 279, }, ++ { 78, 278, }, ++ { 81, 277, }, ++ { 83, 276, }, ++ { 84, 275, }, ++ { 86, 274, }, ++ { 87, 273, }, ++ { 89, 272, }, ++ { 90, 271, }, ++ { 92, 270, }, ++ { 93, 269, }, ++ { 95, 268, }, ++ { 96, 267, }, ++ { 98, 266, }, ++ { 100, 265, }, ++ { 102, 264, }, ++ { 104, 263, }, ++ { 105, 262, }, ++ { 106, 261, }, ++ { 110, 260, }, ++ { 255, 0, }, ++}; ++ ++/* 8 bit registers, 16 bit values */ ++static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data >> 8, data & 0xff }; ++ ++ struct i2c_msg msg = { .addr = state->config->demod_address, ++ .flags = 0, .buf = buf, .len = 3 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static u16 s5h1409_readreg(struct s5h1409_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0, 0 }; ++ ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, ++ .buf = b1, .len = 2 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk("%s: readreg error (ret == %i)\n", __func__, ret); ++ return (b1[0] << 8) | b1[1]; ++} ++ ++static int s5h1409_softreset(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ s5h1409_writereg(state, 0xf5, 0); ++ s5h1409_writereg(state, 0xf5, 1); ++ state->is_qam_locked = 0; ++ state->qam_state = QAM_STATE_UNTUNED; ++ return 0; ++} ++ ++#define S5H1409_VSB_IF_FREQ 5380 ++#define S5H1409_QAM_IF_FREQ (state->config->qam_if) ++ ++static int s5h1409_set_if_freq(struct dvb_frontend *fe, int KHz) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d KHz)\n", __func__, KHz); ++ ++ switch (KHz) { ++ case 4000: ++ s5h1409_writereg(state, 0x87, 0x014b); ++ s5h1409_writereg(state, 0x88, 0x0cb5); ++ s5h1409_writereg(state, 0x89, 0x03e2); ++ break; ++ case 5380: ++ case 44000: ++ default: ++ s5h1409_writereg(state, 0x87, 0x01be); ++ s5h1409_writereg(state, 0x88, 0x0436); ++ s5h1409_writereg(state, 0x89, 0x054d); ++ break; ++ } ++ state->if_freq = KHz; ++ ++ return 0; ++} ++ ++static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, inverted); ++ ++ if (inverted == 1) ++ return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ ++ else ++ return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */ ++} ++ ++static int s5h1409_enable_modulation(struct dvb_frontend *fe, ++ fe_modulation_t m) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(0x%08x)\n", __func__, m); ++ ++ switch (m) { ++ case VSB_8: ++ dprintk("%s() VSB_8\n", __func__); ++ if (state->if_freq != S5H1409_VSB_IF_FREQ) ++ s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ); ++ s5h1409_writereg(state, 0xf4, 0); ++ break; ++ case QAM_64: ++ case QAM_256: ++ case QAM_AUTO: ++ dprintk("%s() QAM_AUTO (64/256)\n", __func__); ++ if (state->if_freq != S5H1409_QAM_IF_FREQ) ++ s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ); ++ s5h1409_writereg(state, 0xf4, 1); ++ s5h1409_writereg(state, 0x85, 0x110); ++ break; ++ default: ++ dprintk("%s() Invalid modulation\n", __func__); ++ return -EINVAL; ++ } ++ ++ state->current_modulation = m; ++ s5h1409_softreset(fe); ++ ++ return 0; ++} ++ ++static int s5h1409_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (enable) ++ return s5h1409_writereg(state, 0xf3, 1); ++ else ++ return s5h1409_writereg(state, 0xf3, 0); ++} ++ ++static int s5h1409_set_gpio(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (enable) ++ return s5h1409_writereg(state, 0xe3, ++ s5h1409_readreg(state, 0xe3) | 0x1100); ++ else ++ return s5h1409_writereg(state, 0xe3, ++ s5h1409_readreg(state, 0xe3) & 0xfeff); ++} ++ ++static int s5h1409_sleep(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ return s5h1409_writereg(state, 0xf2, enable); ++} ++ ++static int s5h1409_register_reset(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ return s5h1409_writereg(state, 0xfa, 0); ++} ++ ++static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg; ++ ++ if (state->qam_state < QAM_STATE_INTERLEAVE_SET) { ++ /* We should not perform amhum optimization until ++ the interleave mode has been configured */ ++ return; ++ } ++ ++ if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) { ++ /* We've already reached the maximum optimization level, so ++ dont bother banging on the status registers */ ++ return; ++ } ++ ++ /* QAM EQ lock check */ ++ reg = s5h1409_readreg(state, 0xf0); ++ ++ if ((reg >> 13) & 0x1) { ++ reg &= 0xff; ++ ++ s5h1409_writereg(state, 0x96, 0x000c); ++ if (reg < 0x68) { ++ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) { ++ dprintk("%s() setting QAM state to OPT_L3\n", ++ __func__); ++ s5h1409_writereg(state, 0x93, 0x3130); ++ s5h1409_writereg(state, 0x9e, 0x2836); ++ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3; ++ } ++ } else { ++ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) { ++ dprintk("%s() setting QAM state to OPT_L2\n", ++ __func__); ++ s5h1409_writereg(state, 0x93, 0x3332); ++ s5h1409_writereg(state, 0x9e, 0x2c37); ++ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2; ++ } ++ } ++ ++ } else { ++ if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) { ++ dprintk("%s() setting QAM state to OPT_L1\n", __func__); ++ s5h1409_writereg(state, 0x96, 0x0008); ++ s5h1409_writereg(state, 0x93, 0x3332); ++ s5h1409_writereg(state, 0x9e, 0x2c37); ++ state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1; ++ } ++ } ++} ++ ++static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg; ++ ++ if (state->is_qam_locked) ++ return; ++ ++ /* QAM EQ lock check */ ++ reg = s5h1409_readreg(state, 0xf0); ++ ++ if ((reg >> 13) & 0x1) { ++ ++ state->is_qam_locked = 1; ++ reg &= 0xff; ++ ++ s5h1409_writereg(state, 0x96, 0x00c); ++ if ((reg < 0x38) || (reg > 0x68)) { ++ s5h1409_writereg(state, 0x93, 0x3332); ++ s5h1409_writereg(state, 0x9e, 0x2c37); ++ } else { ++ s5h1409_writereg(state, 0x93, 0x3130); ++ s5h1409_writereg(state, 0x9e, 0x2836); ++ } ++ ++ } else { ++ s5h1409_writereg(state, 0x96, 0x0008); ++ s5h1409_writereg(state, 0x93, 0x3332); ++ s5h1409_writereg(state, 0x9e, 0x2c37); ++ } ++} ++ ++static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg, reg1, reg2; ++ ++ if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) { ++ /* We've done the optimization already */ ++ return; ++ } ++ ++ reg = s5h1409_readreg(state, 0xf1); ++ ++ /* Master lock */ ++ if ((reg >> 15) & 0x1) { ++ if (state->qam_state == QAM_STATE_UNTUNED || ++ state->qam_state == QAM_STATE_TUNING_STARTED) { ++ dprintk("%s() setting QAM state to INTERLEAVE_SET\n", ++ __func__); ++ reg1 = s5h1409_readreg(state, 0xb2); ++ reg2 = s5h1409_readreg(state, 0xad); ++ ++ s5h1409_writereg(state, 0x96, 0x0020); ++ s5h1409_writereg(state, 0xad, ++ (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); ++ state->qam_state = QAM_STATE_INTERLEAVE_SET; ++ } ++ } else { ++ if (state->qam_state == QAM_STATE_UNTUNED) { ++ dprintk("%s() setting QAM state to TUNING_STARTED\n", ++ __func__); ++ s5h1409_writereg(state, 0x96, 0x08); ++ s5h1409_writereg(state, 0xab, ++ s5h1409_readreg(state, 0xab) | 0x1001); ++ state->qam_state = QAM_STATE_TUNING_STARTED; ++ } ++ } ++} ++ ++static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg, reg1, reg2; ++ ++ reg = s5h1409_readreg(state, 0xf1); ++ ++ /* Master lock */ ++ if ((reg >> 15) & 0x1) { ++ if (state->qam_state != 2) { ++ state->qam_state = 2; ++ reg1 = s5h1409_readreg(state, 0xb2); ++ reg2 = s5h1409_readreg(state, 0xad); ++ ++ s5h1409_writereg(state, 0x96, 0x20); ++ s5h1409_writereg(state, 0xad, ++ (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); ++ s5h1409_writereg(state, 0xab, ++ s5h1409_readreg(state, 0xab) & 0xeffe); ++ } ++ } else { ++ if (state->qam_state != 1) { ++ state->qam_state = 1; ++ s5h1409_writereg(state, 0x96, 0x08); ++ s5h1409_writereg(state, 0xab, ++ s5h1409_readreg(state, 0xab) | 0x1001); ++ } ++ } ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++static int s5h1409_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(frequency=%d)\n", __func__, p->frequency); ++ ++ s5h1409_softreset(fe); ++ ++ state->current_frequency = p->frequency; ++ ++ s5h1409_enable_modulation(fe, p->modulation); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Issue a reset to the demod so it knows to resync against the ++ newly tuned frequency */ ++ s5h1409_softreset(fe); ++ ++ /* Optimize the demod for QAM */ ++ if (state->current_modulation != VSB_8) { ++ /* This almost certainly applies to all boards, but for now ++ only do it for the HVR-1600. Once the other boards are ++ tested, the "legacy" versions can just go away */ ++ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { ++ s5h1409_set_qam_interleave_mode(fe); ++ s5h1409_set_qam_amhum_mode(fe); ++ } else { ++ s5h1409_set_qam_amhum_mode_legacy(fe); ++ s5h1409_set_qam_interleave_mode_legacy(fe); ++ } ++ } ++ ++ return 0; ++} ++ ++static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 val; ++ ++ dprintk("%s(%d)\n", __func__, mode); ++ ++ val = s5h1409_readreg(state, 0xac) & 0xcfff; ++ switch (mode) { ++ case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: ++ val |= 0x0000; ++ break; ++ case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: ++ dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); ++ val |= 0x1000; ++ break; ++ case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: ++ val |= 0x2000; ++ break; ++ case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: ++ val |= 0x3000; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Configure MPEG Signal Timing charactistics */ ++ return s5h1409_writereg(state, 0xac, val); ++} ++ ++/* Reset the demod hardware and reset all of the configuration registers ++ to a default state. */ ++static int s5h1409_init(struct dvb_frontend *fe) ++{ ++ int i; ++ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ dprintk("%s()\n", __func__); ++ ++ s5h1409_sleep(fe, 0); ++ s5h1409_register_reset(fe); ++ ++ for (i = 0; i < ARRAY_SIZE(init_tab); i++) ++ s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data); ++ ++ /* The datasheet says that after initialisation, VSB is default */ ++ state->current_modulation = VSB_8; ++ ++ /* Optimize for the HVR-1600 if appropriate. Note that some of these ++ may get folded into the generic case after testing with other ++ devices */ ++ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { ++ /* VSB AGC REF */ ++ s5h1409_writereg(state, 0x09, 0x0050); ++ ++ /* Unknown but Windows driver does it... */ ++ s5h1409_writereg(state, 0x21, 0x0001); ++ s5h1409_writereg(state, 0x50, 0x030e); ++ ++ /* QAM AGC REF */ ++ s5h1409_writereg(state, 0x82, 0x0800); ++ } ++ ++ if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) ++ s5h1409_writereg(state, 0xab, ++ s5h1409_readreg(state, 0xab) | 0x100); /* Serial */ ++ else ++ s5h1409_writereg(state, 0xab, ++ s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */ ++ ++ s5h1409_set_spectralinversion(fe, state->config->inversion); ++ s5h1409_set_if_freq(fe, state->if_freq); ++ s5h1409_set_gpio(fe, state->config->gpio); ++ s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing); ++ s5h1409_softreset(fe); ++ ++ /* Note: Leaving the I2C gate closed. */ ++ s5h1409_i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg; ++ u32 tuner_status = 0; ++ ++ *status = 0; ++ ++ /* Optimize the demod for QAM */ ++ if (state->current_modulation != VSB_8) { ++ /* This almost certainly applies to all boards, but for now ++ only do it for the HVR-1600. Once the other boards are ++ tested, the "legacy" versions can just go away */ ++ if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { ++ s5h1409_set_qam_interleave_mode(fe); ++ s5h1409_set_qam_amhum_mode(fe); ++ } ++ } ++ ++ /* Get the demodulator status */ ++ reg = s5h1409_readreg(state, 0xf1); ++ if (reg & 0x1000) ++ *status |= FE_HAS_VITERBI; ++ if (reg & 0x8000) ++ *status |= FE_HAS_LOCK | FE_HAS_SYNC; ++ ++ switch (state->config->status_mode) { ++ case S5H1409_DEMODLOCKING: ++ if (*status & FE_HAS_VITERBI) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ case S5H1409_TUNERLOCKING: ++ /* Get the tuner status */ ++ if (fe->ops.tuner_ops.get_status) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ fe->ops.tuner_ops.get_status(fe, &tuner_status); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (tuner_status) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ } ++ ++ dprintk("%s() status 0x%08x\n", __func__, *status); ++ ++ return 0; ++} ++ ++static int s5h1409_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { ++ if (v < qam256_snr_tab[i].val) { ++ *snr = qam256_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ return ret; ++} ++ ++static int s5h1409_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { ++ if (v < qam64_snr_tab[i].val) { ++ *snr = qam64_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ return ret; ++} ++ ++static int s5h1409_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { ++ if (v > vsb_snr_tab[i].val) { ++ *snr = vsb_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ dprintk("%s() snr=%d\n", __func__, *snr); ++ return ret; ++} ++ ++static int s5h1409_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ u16 reg; ++ dprintk("%s()\n", __func__); ++ ++ switch (state->current_modulation) { ++ case QAM_64: ++ reg = s5h1409_readreg(state, 0xf0) & 0xff; ++ return s5h1409_qam64_lookup_snr(fe, snr, reg); ++ case QAM_256: ++ reg = s5h1409_readreg(state, 0xf0) & 0xff; ++ return s5h1409_qam256_lookup_snr(fe, snr, reg); ++ case VSB_8: ++ reg = s5h1409_readreg(state, 0xf1) & 0x3ff; ++ return s5h1409_vsb_lookup_snr(fe, snr, reg); ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int s5h1409_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ /* borrowed from lgdt330x.c ++ * ++ * Calculate strength from SNR up to 35dB ++ * Even though the SNR can go higher than 35dB, ++ * there is some comfort factor in having a range of ++ * strong signals that can show at 100% ++ */ ++ u16 snr; ++ u32 tmp; ++ int ret = s5h1409_read_snr(fe, &snr); ++ ++ *signal_strength = 0; ++ ++ if (0 == ret) { ++ /* The following calculation method was chosen ++ * purely for the sake of code re-use from the ++ * other demod drivers that use this method */ ++ ++ /* Convert from SNR in dB * 10 to 8.24 fixed-point */ ++ tmp = (snr * ((1 << 24) / 10)); ++ ++ /* Convert from 8.24 fixed-point to ++ * scale the range 0 - 35*2^24 into 0 - 65535*/ ++ if (tmp >= 8960 * 0x10000) ++ *signal_strength = 0xffff; ++ else ++ *signal_strength = tmp / 8960; ++ } ++ ++ return ret; ++} ++ ++static int s5h1409_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ *ucblocks = s5h1409_readreg(state, 0xb5); ++ ++ return 0; ++} ++ ++static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ return s5h1409_read_ucblocks(fe, ber); ++} ++ ++static int s5h1409_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1409_state *state = fe->demodulator_priv; ++ ++ p->frequency = state->current_frequency; ++ p->modulation = state->current_modulation; ++ ++ return 0; ++} ++ ++static int s5h1409_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void s5h1409_release(struct dvb_frontend *fe) ++{ ++ struct s5h1409_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops s5h1409_ops; ++ ++struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct s5h1409_state *state = NULL; ++ u16 reg; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->current_modulation = 0; ++ state->if_freq = S5H1409_VSB_IF_FREQ; ++ ++ /* check if the demod exists */ ++ reg = s5h1409_readreg(state, 0x04); ++ if ((reg != 0x0066) && (reg != 0x007f)) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &s5h1409_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ if (s5h1409_init(&state->frontend) != 0) { ++ printk(KERN_ERR "%s: Failed to initialize correctly\n", ++ __func__); ++ goto error; ++ } ++ ++ /* Note: Leaving the I2C gate open here. */ ++ s5h1409_i2c_gate_ctrl(&state->frontend, 1); ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(s5h1409_attach); ++ ++static struct dvb_frontend_ops s5h1409_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Samsung S5H1409 QAM/8VSB Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ ++ .init = s5h1409_init, ++ .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl, ++ .set_frontend = s5h1409_set_frontend, ++ .get_frontend = s5h1409_get_frontend, ++ .get_tune_settings = s5h1409_get_tune_settings, ++ .read_status = s5h1409_read_status, ++ .read_ber = s5h1409_read_ber, ++ .read_signal_strength = s5h1409_read_signal_strength, ++ .read_snr = s5h1409_read_snr, ++ .read_ucblocks = s5h1409_read_ucblocks, ++ .release = s5h1409_release, ++}; ++ ++MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); ++ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ */ +diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h +new file mode 100644 +index 0000000..91f2ebd +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1409.h +@@ -0,0 +1,88 @@ ++/* ++ Samsung S5H1409 VSB/QAM demodulator driver ++ ++ Copyright (C) 2006 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef __S5H1409_H__ ++#define __S5H1409_H__ ++ ++#include ++ ++struct s5h1409_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* serial/parallel output */ ++#define S5H1409_PARALLEL_OUTPUT 0 ++#define S5H1409_SERIAL_OUTPUT 1 ++ u8 output_mode; ++ ++ /* GPIO Setting */ ++#define S5H1409_GPIO_OFF 0 ++#define S5H1409_GPIO_ON 1 ++ u8 gpio; ++ ++ /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */ ++ u16 qam_if; ++ ++ /* Spectral Inversion */ ++#define S5H1409_INVERSION_OFF 0 ++#define S5H1409_INVERSION_ON 1 ++ u8 inversion; ++ ++ /* Return lock status based on tuner lock, or demod lock */ ++#define S5H1409_TUNERLOCKING 0 ++#define S5H1409_DEMODLOCKING 1 ++ u8 status_mode; ++ ++ /* MPEG signal timing */ ++#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 ++#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 ++#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 ++#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 ++ u16 mpeg_timing; ++ ++ /* HVR-1600 optimizations (to better work with MXL5005s) ++ Note: some of these are likely to be folded into the generic driver ++ after being regression tested with other boards */ ++#define S5H1409_HVR1600_NOOPTIMIZE 0 ++#define S5H1409_HVR1600_OPTIMIZE 1 ++ u8 hvr1600_opt; ++}; ++ ++#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *s5h1409_attach( ++ const struct s5h1409_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_S5H1409 */ ++ ++#endif /* __S5H1409_H__ */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ */ +diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c +new file mode 100644 +index 0000000..6cc4b7a +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1411.c +@@ -0,0 +1,951 @@ ++/* ++ Samsung S5H1411 VSB/QAM demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "s5h1411.h" ++ ++struct s5h1411_state { ++ ++ struct i2c_adapter *i2c; ++ ++ /* configuration settings */ ++ const struct s5h1411_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ fe_modulation_t current_modulation; ++ unsigned int first_tune:1; ++ ++ u32 current_frequency; ++ int if_freq; ++ ++ u8 inversion; ++}; ++ ++static int debug; ++ ++#define dprintk(arg...) do { \ ++ if (debug) \ ++ printk(arg); \ ++ } while (0) ++ ++/* Register values to initialise the demod, defaults to VSB */ ++static struct init_tab { ++ u8 addr; ++ u8 reg; ++ u16 data; ++} init_tab[] = { ++ { S5H1411_I2C_TOP_ADDR, 0x00, 0x0071, }, ++ { S5H1411_I2C_TOP_ADDR, 0x08, 0x0047, }, ++ { S5H1411_I2C_TOP_ADDR, 0x1c, 0x0400, }, ++ { S5H1411_I2C_TOP_ADDR, 0x1e, 0x0370, }, ++ { S5H1411_I2C_TOP_ADDR, 0x1f, 0x342c, }, ++ { S5H1411_I2C_TOP_ADDR, 0x24, 0x0231, }, ++ { S5H1411_I2C_TOP_ADDR, 0x25, 0x1011, }, ++ { S5H1411_I2C_TOP_ADDR, 0x26, 0x0f07, }, ++ { S5H1411_I2C_TOP_ADDR, 0x27, 0x0f04, }, ++ { S5H1411_I2C_TOP_ADDR, 0x28, 0x070f, }, ++ { S5H1411_I2C_TOP_ADDR, 0x29, 0x2820, }, ++ { S5H1411_I2C_TOP_ADDR, 0x2a, 0x102e, }, ++ { S5H1411_I2C_TOP_ADDR, 0x2b, 0x0220, }, ++ { S5H1411_I2C_TOP_ADDR, 0x2e, 0x0d0e, }, ++ { S5H1411_I2C_TOP_ADDR, 0x2f, 0x1013, }, ++ { S5H1411_I2C_TOP_ADDR, 0x31, 0x171b, }, ++ { S5H1411_I2C_TOP_ADDR, 0x32, 0x0e0f, }, ++ { S5H1411_I2C_TOP_ADDR, 0x33, 0x0f10, }, ++ { S5H1411_I2C_TOP_ADDR, 0x34, 0x170e, }, ++ { S5H1411_I2C_TOP_ADDR, 0x35, 0x4b10, }, ++ { S5H1411_I2C_TOP_ADDR, 0x36, 0x0f17, }, ++ { S5H1411_I2C_TOP_ADDR, 0x3c, 0x1577, }, ++ { S5H1411_I2C_TOP_ADDR, 0x3d, 0x081a, }, ++ { S5H1411_I2C_TOP_ADDR, 0x3e, 0x77ee, }, ++ { S5H1411_I2C_TOP_ADDR, 0x40, 0x1e09, }, ++ { S5H1411_I2C_TOP_ADDR, 0x41, 0x0f0c, }, ++ { S5H1411_I2C_TOP_ADDR, 0x42, 0x1f10, }, ++ { S5H1411_I2C_TOP_ADDR, 0x4d, 0x0509, }, ++ { S5H1411_I2C_TOP_ADDR, 0x4e, 0x0a00, }, ++ { S5H1411_I2C_TOP_ADDR, 0x50, 0x0000, }, ++ { S5H1411_I2C_TOP_ADDR, 0x5b, 0x0000, }, ++ { S5H1411_I2C_TOP_ADDR, 0x5c, 0x0008, }, ++ { S5H1411_I2C_TOP_ADDR, 0x57, 0x1101, }, ++ { S5H1411_I2C_TOP_ADDR, 0x65, 0x007c, }, ++ { S5H1411_I2C_TOP_ADDR, 0x68, 0x0512, }, ++ { S5H1411_I2C_TOP_ADDR, 0x69, 0x0258, }, ++ { S5H1411_I2C_TOP_ADDR, 0x70, 0x0004, }, ++ { S5H1411_I2C_TOP_ADDR, 0x71, 0x0007, }, ++ { S5H1411_I2C_TOP_ADDR, 0x76, 0x00a9, }, ++ { S5H1411_I2C_TOP_ADDR, 0x78, 0x3141, }, ++ { S5H1411_I2C_TOP_ADDR, 0x7a, 0x3141, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb3, 0x8003, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb5, 0xa6bb, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb6, 0x0609, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb7, 0x2f06, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb8, 0x003f, }, ++ { S5H1411_I2C_TOP_ADDR, 0xb9, 0x2700, }, ++ { S5H1411_I2C_TOP_ADDR, 0xba, 0xfac8, }, ++ { S5H1411_I2C_TOP_ADDR, 0xbe, 0x1003, }, ++ { S5H1411_I2C_TOP_ADDR, 0xbf, 0x103f, }, ++ { S5H1411_I2C_TOP_ADDR, 0xce, 0x2000, }, ++ { S5H1411_I2C_TOP_ADDR, 0xcf, 0x0800, }, ++ { S5H1411_I2C_TOP_ADDR, 0xd0, 0x0800, }, ++ { S5H1411_I2C_TOP_ADDR, 0xd1, 0x0400, }, ++ { S5H1411_I2C_TOP_ADDR, 0xd2, 0x0800, }, ++ { S5H1411_I2C_TOP_ADDR, 0xd3, 0x2000, }, ++ { S5H1411_I2C_TOP_ADDR, 0xd4, 0x3000, }, ++ { S5H1411_I2C_TOP_ADDR, 0xdb, 0x4a9b, }, ++ { S5H1411_I2C_TOP_ADDR, 0xdc, 0x1000, }, ++ { S5H1411_I2C_TOP_ADDR, 0xde, 0x0001, }, ++ { S5H1411_I2C_TOP_ADDR, 0xdf, 0x0000, }, ++ { S5H1411_I2C_TOP_ADDR, 0xe3, 0x0301, }, ++ { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0000, }, ++ { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0001, }, ++ { S5H1411_I2C_QAM_ADDR, 0x08, 0x0600, }, ++ { S5H1411_I2C_QAM_ADDR, 0x18, 0x4201, }, ++ { S5H1411_I2C_QAM_ADDR, 0x1e, 0x6476, }, ++ { S5H1411_I2C_QAM_ADDR, 0x21, 0x0830, }, ++ { S5H1411_I2C_QAM_ADDR, 0x0c, 0x5679, }, ++ { S5H1411_I2C_QAM_ADDR, 0x0d, 0x579b, }, ++ { S5H1411_I2C_QAM_ADDR, 0x24, 0x0102, }, ++ { S5H1411_I2C_QAM_ADDR, 0x31, 0x7488, }, ++ { S5H1411_I2C_QAM_ADDR, 0x32, 0x0a08, }, ++ { S5H1411_I2C_QAM_ADDR, 0x3d, 0x8689, }, ++ { S5H1411_I2C_QAM_ADDR, 0x49, 0x0048, }, ++ { S5H1411_I2C_QAM_ADDR, 0x57, 0x2012, }, ++ { S5H1411_I2C_QAM_ADDR, 0x5d, 0x7676, }, ++ { S5H1411_I2C_QAM_ADDR, 0x04, 0x0400, }, ++ { S5H1411_I2C_QAM_ADDR, 0x58, 0x00c0, }, ++ { S5H1411_I2C_QAM_ADDR, 0x5b, 0x0100, }, ++}; ++ ++/* VSB SNR lookup table */ ++static struct vsb_snr_tab { ++ u16 val; ++ u16 data; ++} vsb_snr_tab[] = { ++ { 0x39f, 300, }, ++ { 0x39b, 295, }, ++ { 0x397, 290, }, ++ { 0x394, 285, }, ++ { 0x38f, 280, }, ++ { 0x38b, 275, }, ++ { 0x387, 270, }, ++ { 0x382, 265, }, ++ { 0x37d, 260, }, ++ { 0x377, 255, }, ++ { 0x370, 250, }, ++ { 0x36a, 245, }, ++ { 0x364, 240, }, ++ { 0x35b, 235, }, ++ { 0x353, 230, }, ++ { 0x349, 225, }, ++ { 0x340, 320, }, ++ { 0x337, 215, }, ++ { 0x327, 210, }, ++ { 0x31b, 205, }, ++ { 0x310, 200, }, ++ { 0x302, 195, }, ++ { 0x2f3, 190, }, ++ { 0x2e4, 185, }, ++ { 0x2d7, 180, }, ++ { 0x2cd, 175, }, ++ { 0x2bb, 170, }, ++ { 0x2a9, 165, }, ++ { 0x29e, 160, }, ++ { 0x284, 155, }, ++ { 0x27a, 150, }, ++ { 0x260, 145, }, ++ { 0x23a, 140, }, ++ { 0x224, 135, }, ++ { 0x213, 130, }, ++ { 0x204, 125, }, ++ { 0x1fe, 120, }, ++ { 0, 0, }, ++}; ++ ++/* QAM64 SNR lookup table */ ++static struct qam64_snr_tab { ++ u16 val; ++ u16 data; ++} qam64_snr_tab[] = { ++ { 0x0001, 0, }, ++ { 0x0af0, 300, }, ++ { 0x0d80, 290, }, ++ { 0x10a0, 280, }, ++ { 0x14b5, 270, }, ++ { 0x1590, 268, }, ++ { 0x1680, 266, }, ++ { 0x17b0, 264, }, ++ { 0x18c0, 262, }, ++ { 0x19b0, 260, }, ++ { 0x1ad0, 258, }, ++ { 0x1d00, 256, }, ++ { 0x1da0, 254, }, ++ { 0x1ef0, 252, }, ++ { 0x2050, 250, }, ++ { 0x20f0, 249, }, ++ { 0x21d0, 248, }, ++ { 0x22b0, 247, }, ++ { 0x23a0, 246, }, ++ { 0x2470, 245, }, ++ { 0x24f0, 244, }, ++ { 0x25a0, 243, }, ++ { 0x26c0, 242, }, ++ { 0x27b0, 241, }, ++ { 0x28d0, 240, }, ++ { 0x29b0, 239, }, ++ { 0x2ad0, 238, }, ++ { 0x2ba0, 237, }, ++ { 0x2c80, 236, }, ++ { 0x2d20, 235, }, ++ { 0x2e00, 234, }, ++ { 0x2f10, 233, }, ++ { 0x3050, 232, }, ++ { 0x3190, 231, }, ++ { 0x3300, 230, }, ++ { 0x3340, 229, }, ++ { 0x3200, 228, }, ++ { 0x3550, 227, }, ++ { 0x3610, 226, }, ++ { 0x3600, 225, }, ++ { 0x3700, 224, }, ++ { 0x3800, 223, }, ++ { 0x3920, 222, }, ++ { 0x3a20, 221, }, ++ { 0x3b30, 220, }, ++ { 0x3d00, 219, }, ++ { 0x3e00, 218, }, ++ { 0x4000, 217, }, ++ { 0x4100, 216, }, ++ { 0x4300, 215, }, ++ { 0x4400, 214, }, ++ { 0x4600, 213, }, ++ { 0x4700, 212, }, ++ { 0x4800, 211, }, ++ { 0x4a00, 210, }, ++ { 0x4b00, 209, }, ++ { 0x4d00, 208, }, ++ { 0x4f00, 207, }, ++ { 0x5050, 206, }, ++ { 0x5200, 205, }, ++ { 0x53c0, 204, }, ++ { 0x5450, 203, }, ++ { 0x5650, 202, }, ++ { 0x5820, 201, }, ++ { 0x6000, 200, }, ++ { 0xffff, 0, }, ++}; ++ ++/* QAM256 SNR lookup table */ ++static struct qam256_snr_tab { ++ u16 val; ++ u16 data; ++} qam256_snr_tab[] = { ++ { 0x0001, 0, }, ++ { 0x0970, 400, }, ++ { 0x0a90, 390, }, ++ { 0x0b90, 380, }, ++ { 0x0d90, 370, }, ++ { 0x0ff0, 360, }, ++ { 0x1240, 350, }, ++ { 0x1345, 348, }, ++ { 0x13c0, 346, }, ++ { 0x14c0, 344, }, ++ { 0x1500, 342, }, ++ { 0x1610, 340, }, ++ { 0x1700, 338, }, ++ { 0x1800, 336, }, ++ { 0x18b0, 334, }, ++ { 0x1900, 332, }, ++ { 0x1ab0, 330, }, ++ { 0x1bc0, 328, }, ++ { 0x1cb0, 326, }, ++ { 0x1db0, 324, }, ++ { 0x1eb0, 322, }, ++ { 0x2030, 320, }, ++ { 0x2200, 318, }, ++ { 0x2280, 316, }, ++ { 0x2410, 314, }, ++ { 0x25b0, 312, }, ++ { 0x27a0, 310, }, ++ { 0x2840, 308, }, ++ { 0x29d0, 306, }, ++ { 0x2b10, 304, }, ++ { 0x2d30, 302, }, ++ { 0x2f20, 300, }, ++ { 0x30c0, 298, }, ++ { 0x3260, 297, }, ++ { 0x32c0, 296, }, ++ { 0x3300, 295, }, ++ { 0x33b0, 294, }, ++ { 0x34b0, 293, }, ++ { 0x35a0, 292, }, ++ { 0x3650, 291, }, ++ { 0x3800, 290, }, ++ { 0x3900, 289, }, ++ { 0x3a50, 288, }, ++ { 0x3b30, 287, }, ++ { 0x3cb0, 286, }, ++ { 0x3e20, 285, }, ++ { 0x3fa0, 284, }, ++ { 0x40a0, 283, }, ++ { 0x41c0, 282, }, ++ { 0x42f0, 281, }, ++ { 0x44a0, 280, }, ++ { 0x4600, 279, }, ++ { 0x47b0, 278, }, ++ { 0x4900, 277, }, ++ { 0x4a00, 276, }, ++ { 0x4ba0, 275, }, ++ { 0x4d00, 274, }, ++ { 0x4f00, 273, }, ++ { 0x5000, 272, }, ++ { 0x51f0, 272, }, ++ { 0x53a0, 270, }, ++ { 0x5520, 269, }, ++ { 0x5700, 268, }, ++ { 0x5800, 267, }, ++ { 0x5a00, 266, }, ++ { 0x5c00, 265, }, ++ { 0x5d00, 264, }, ++ { 0x5f00, 263, }, ++ { 0x6000, 262, }, ++ { 0x6200, 261, }, ++ { 0x6400, 260, }, ++ { 0xffff, 0, }, ++}; ++ ++/* 8 bit registers, 16 bit values */ ++static int s5h1411_writereg(struct s5h1411_state *state, ++ u8 addr, u8 reg, u16 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data >> 8, data & 0xff }; ++ ++ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, " ++ "ret == %i)\n", __func__, addr, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static u16 s5h1411_readreg(struct s5h1411_state *state, u8 addr, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0, 0 }; ++ ++ struct i2c_msg msg[] = { ++ { .addr = addr, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk(KERN_ERR "%s: readreg error (ret == %i)\n", ++ __func__, ret); ++ return (b1[0] << 8) | b1[1]; ++} ++ ++static int s5h1411_softreset(struct dvb_frontend *fe) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 0); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 1); ++ return 0; ++} ++ ++static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d KHz)\n", __func__, KHz); ++ ++ switch (KHz) { ++ case 3250: ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x10d5); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x5342); ++ s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x10d9); ++ break; ++ case 3500: ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1225); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x1e96); ++ s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1225); ++ break; ++ case 4000: ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x14bc); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0xb53e); ++ s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x14bd); ++ break; ++ default: ++ dprintk("%s(%d KHz) Invalid, defaulting to 5380\n", ++ __func__, KHz); ++ /* no break, need to continue */ ++ case 5380: ++ case 44000: ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x3655); ++ s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1be4); ++ break; ++ } ++ ++ state->if_freq = KHz; ++ ++ return 0; ++} ++ ++static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 val; ++ ++ dprintk("%s(%d)\n", __func__, mode); ++ ++ val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff; ++ switch (mode) { ++ case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: ++ val |= 0x0000; ++ break; ++ case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: ++ dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); ++ val |= 0x1000; ++ break; ++ case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: ++ val |= 0x2000; ++ break; ++ case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: ++ val |= 0x3000; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Configure MPEG Signal Timing charactistics */ ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbe, val); ++} ++ ++static int s5h1411_set_spectralinversion(struct dvb_frontend *fe, int inversion) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 val; ++ ++ dprintk("%s(%d)\n", __func__, inversion); ++ val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x24) & ~0x1000; ++ ++ if (inversion == 1) ++ val |= 0x1000; /* Inverted */ ++ ++ state->inversion = inversion; ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x24, val); ++} ++ ++static int s5h1411_set_serialmode(struct dvb_frontend *fe, int serial) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 val; ++ ++ dprintk("%s(%d)\n", __func__, serial); ++ val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbd) & ~0x100; ++ ++ if (serial == 1) ++ val |= 0x100; ++ ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbd, val); ++} ++ ++static int s5h1411_enable_modulation(struct dvb_frontend *fe, ++ fe_modulation_t m) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(0x%08x)\n", __func__, m); ++ ++ if ((state->first_tune == 0) && (m == state->current_modulation)) { ++ dprintk("%s() Already at desired modulation. Skipping...\n", ++ __func__); ++ return 0; ++ } ++ ++ switch (m) { ++ case VSB_8: ++ dprintk("%s() VSB_8\n", __func__); ++ s5h1411_set_if_freq(fe, state->config->vsb_if); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x71); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x00); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0xf1); ++ break; ++ case QAM_64: ++ case QAM_256: ++ case QAM_AUTO: ++ dprintk("%s() QAM_AUTO (64/256)\n", __func__); ++ s5h1411_set_if_freq(fe, state->config->qam_if); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x0171); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x0001); ++ s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x16, 0x1101); ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0x00f0); ++ break; ++ default: ++ dprintk("%s() Invalid modulation\n", __func__); ++ return -EINVAL; ++ } ++ ++ state->current_modulation = m; ++ state->first_tune = 0; ++ s5h1411_softreset(fe); ++ ++ return 0; ++} ++ ++static int s5h1411_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (enable) ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); ++ else ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 0); ++} ++ ++static int s5h1411_set_gpio(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 val; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xe0) & ~0x02; ++ ++ if (enable) ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, ++ val | 0x02); ++ else ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, val); ++} ++ ++static int s5h1411_set_powerstate(struct dvb_frontend *fe, int enable) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(%d)\n", __func__, enable); ++ ++ if (enable) ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 1); ++ else { ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 0); ++ s5h1411_softreset(fe); ++ } ++ ++ return 0; ++} ++ ++static int s5h1411_sleep(struct dvb_frontend *fe) ++{ ++ return s5h1411_set_powerstate(fe, 1); ++} ++ ++static int s5h1411_register_reset(struct dvb_frontend *fe) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s()\n", __func__); ++ ++ return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf3, 0); ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++static int s5h1411_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ dprintk("%s(frequency=%d)\n", __func__, p->frequency); ++ ++ s5h1411_softreset(fe); ++ ++ state->current_frequency = p->frequency; ++ ++ s5h1411_enable_modulation(fe, p->modulation); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ fe->ops.tuner_ops.set_params(fe); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Issue a reset to the demod so it knows to resync against the ++ newly tuned frequency */ ++ s5h1411_softreset(fe); ++ ++ return 0; ++} ++ ++/* Reset the demod hardware and reset all of the configuration registers ++ to a default state. */ ++static int s5h1411_init(struct dvb_frontend *fe) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("%s()\n", __func__); ++ ++ s5h1411_set_powerstate(fe, 0); ++ s5h1411_register_reset(fe); ++ ++ for (i = 0; i < ARRAY_SIZE(init_tab); i++) ++ s5h1411_writereg(state, init_tab[i].addr, ++ init_tab[i].reg, ++ init_tab[i].data); ++ ++ /* The datasheet says that after initialisation, VSB is default */ ++ state->current_modulation = VSB_8; ++ ++ /* Although the datasheet says it's in VSB, empirical evidence ++ shows problems getting lock on the first tuning request. Make ++ sure we call enable_modulation the first time around */ ++ state->first_tune = 1; ++ ++ if (state->config->output_mode == S5H1411_SERIAL_OUTPUT) ++ /* Serial */ ++ s5h1411_set_serialmode(fe, 1); ++ else ++ /* Parallel */ ++ s5h1411_set_serialmode(fe, 0); ++ ++ s5h1411_set_spectralinversion(fe, state->config->inversion); ++ s5h1411_set_if_freq(fe, state->config->vsb_if); ++ s5h1411_set_gpio(fe, state->config->gpio); ++ s5h1411_set_mpeg_timing(fe, state->config->mpeg_timing); ++ s5h1411_softreset(fe); ++ ++ /* Note: Leaving the I2C gate closed. */ ++ s5h1411_i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int s5h1411_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 reg; ++ u32 tuner_status = 0; ++ ++ *status = 0; ++ ++ /* Register F2 bit 15 = Master Lock, removed */ ++ ++ switch (state->current_modulation) { ++ case QAM_64: ++ case QAM_256: ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf0); ++ if (reg & 0x10) /* QAM FEC Lock */ ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ if (reg & 0x100) /* QAM EQ Lock */ ++ *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ ++ break; ++ case VSB_8: ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf2); ++ if (reg & 0x1000) /* FEC Lock */ ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ if (reg & 0x2000) /* EQ Lock */ ++ *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x53); ++ if (reg & 0x1) /* AFC Lock */ ++ *status |= FE_HAS_SIGNAL; ++ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (state->config->status_mode) { ++ case S5H1411_DEMODLOCKING: ++ if (*status & FE_HAS_VITERBI) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ case S5H1411_TUNERLOCKING: ++ /* Get the tuner status */ ++ if (fe->ops.tuner_ops.get_status) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ fe->ops.tuner_ops.get_status(fe, &tuner_status); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (tuner_status) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ break; ++ } ++ ++ dprintk("%s() status 0x%08x\n", __func__, *status); ++ ++ return 0; ++} ++ ++static int s5h1411_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { ++ if (v < qam256_snr_tab[i].val) { ++ *snr = qam256_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ return ret; ++} ++ ++static int s5h1411_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { ++ if (v < qam64_snr_tab[i].val) { ++ *snr = qam64_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ return ret; ++} ++ ++static int s5h1411_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) ++{ ++ int i, ret = -EINVAL; ++ dprintk("%s()\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { ++ if (v > vsb_snr_tab[i].val) { ++ *snr = vsb_snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ dprintk("%s() snr=%d\n", __func__, *snr); ++ return ret; ++} ++ ++static int s5h1411_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ u16 reg; ++ dprintk("%s()\n", __func__); ++ ++ switch (state->current_modulation) { ++ case QAM_64: ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); ++ return s5h1411_qam64_lookup_snr(fe, snr, reg); ++ case QAM_256: ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); ++ return s5h1411_qam256_lookup_snr(fe, snr, reg); ++ case VSB_8: ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, ++ 0xf2) & 0x3ff; ++ return s5h1411_vsb_lookup_snr(fe, snr, reg); ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int s5h1411_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ /* borrowed from lgdt330x.c ++ * ++ * Calculate strength from SNR up to 35dB ++ * Even though the SNR can go higher than 35dB, ++ * there is some comfort factor in having a range of ++ * strong signals that can show at 100% ++ */ ++ u16 snr; ++ u32 tmp; ++ int ret = s5h1411_read_snr(fe, &snr); ++ ++ *signal_strength = 0; ++ ++ if (0 == ret) { ++ /* The following calculation method was chosen ++ * purely for the sake of code re-use from the ++ * other demod drivers that use this method */ ++ ++ /* Convert from SNR in dB * 10 to 8.24 fixed-point */ ++ tmp = (snr * ((1 << 24) / 10)); ++ ++ /* Convert from 8.24 fixed-point to ++ * scale the range 0 - 35*2^24 into 0 - 65535*/ ++ if (tmp >= 8960 * 0x10000) ++ *signal_strength = 0xffff; ++ else ++ *signal_strength = tmp / 8960; ++ } ++ ++ return ret; ++} ++ ++static int s5h1411_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ *ucblocks = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xc9); ++ ++ return 0; ++} ++ ++static int s5h1411_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ return s5h1411_read_ucblocks(fe, ber); ++} ++ ++static int s5h1411_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1411_state *state = fe->demodulator_priv; ++ ++ p->frequency = state->current_frequency; ++ p->modulation = state->current_modulation; ++ ++ return 0; ++} ++ ++static int s5h1411_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void s5h1411_release(struct dvb_frontend *fe) ++{ ++ struct s5h1411_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops s5h1411_ops; ++ ++struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct s5h1411_state *state = NULL; ++ u16 reg; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->current_modulation = VSB_8; ++ state->inversion = state->config->inversion; ++ ++ /* check if the demod exists */ ++ reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x05); ++ if (reg != 0x0066) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &s5h1411_ops, ++ sizeof(struct dvb_frontend_ops)); ++ ++ state->frontend.demodulator_priv = state; ++ ++ if (s5h1411_init(&state->frontend) != 0) { ++ printk(KERN_ERR "%s: Failed to initialize correctly\n", ++ __func__); ++ goto error; ++ } ++ ++ /* Note: Leaving the I2C gate open here. */ ++ s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); ++ ++ /* Put the device into low-power mode until first use */ ++ s5h1411_set_powerstate(&state->frontend, 1); ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(s5h1411_attach); ++ ++static struct dvb_frontend_ops s5h1411_ops = { ++ .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, ++ .info = { ++ .name = "Samsung S5H1411 QAM/8VSB Frontend", ++ .frequency_min = 54000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 62500, ++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ ++ .init = s5h1411_init, ++ .sleep = s5h1411_sleep, ++ .i2c_gate_ctrl = s5h1411_i2c_gate_ctrl, ++ .set_frontend = s5h1411_set_frontend, ++ .get_frontend = s5h1411_get_frontend, ++ .get_tune_settings = s5h1411_get_tune_settings, ++ .read_status = s5h1411_read_status, ++ .read_ber = s5h1411_read_ber, ++ .read_signal_strength = s5h1411_read_signal_strength, ++ .read_snr = s5h1411_read_snr, ++ .read_ucblocks = s5h1411_read_ucblocks, ++ .release = s5h1411_release, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ */ +diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h +new file mode 100644 +index 0000000..45ec0f8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1411.h +@@ -0,0 +1,90 @@ ++/* ++ Samsung S5H1411 VSB/QAM demodulator driver ++ ++ Copyright (C) 2008 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef __S5H1411_H__ ++#define __S5H1411_H__ ++ ++#include ++ ++#define S5H1411_I2C_TOP_ADDR (0x32 >> 1) ++#define S5H1411_I2C_QAM_ADDR (0x34 >> 1) ++ ++struct s5h1411_config { ++ ++ /* serial/parallel output */ ++#define S5H1411_PARALLEL_OUTPUT 0 ++#define S5H1411_SERIAL_OUTPUT 1 ++ u8 output_mode; ++ ++ /* GPIO Setting */ ++#define S5H1411_GPIO_OFF 0 ++#define S5H1411_GPIO_ON 1 ++ u8 gpio; ++ ++ /* MPEG signal timing */ ++#define S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 ++#define S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 ++#define S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 ++#define S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 ++ u16 mpeg_timing; ++ ++ /* IF Freq for QAM and VSB in KHz */ ++#define S5H1411_IF_3250 3250 ++#define S5H1411_IF_3500 3500 ++#define S5H1411_IF_4000 4000 ++#define S5H1411_IF_5380 5380 ++#define S5H1411_IF_44000 44000 ++#define S5H1411_VSB_IF_DEFAULT S5H1411_IF_44000 ++#define S5H1411_QAM_IF_DEFAULT S5H1411_IF_44000 ++ u16 qam_if; ++ u16 vsb_if; ++ ++ /* Spectral Inversion */ ++#define S5H1411_INVERSION_OFF 0 ++#define S5H1411_INVERSION_ON 1 ++ u8 inversion; ++ ++ /* Return lock status based on tuner lock, or demod lock */ ++#define S5H1411_TUNERLOCKING 0 ++#define S5H1411_DEMODLOCKING 1 ++ u8 status_mode; ++}; ++ ++#if defined(CONFIG_DVB_S5H1411) || \ ++ (defined(CONFIG_DVB_S5H1411_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *s5h1411_attach( ++ const struct s5h1411_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_S5H1411 */ ++ ++#endif /* __S5H1411_H__ */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ */ +diff --git a/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c +new file mode 100644 +index 0000000..e2fec9e +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1420.c +@@ -0,0 +1,960 @@ ++/* ++ * Driver for ++ * Samsung S5H1420 and ++ * PnpNetwork PN1010 QPSK Demodulator ++ * ++ * Copyright (C) 2005 Andrew de Quincey ++ * Copyright (C) 2005-8 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#include "dvb_frontend.h" ++#include "s5h1420.h" ++#include "s5h1420_priv.h" ++ ++#define TONE_FREQ 22000 ++ ++struct s5h1420_state { ++ struct i2c_adapter* i2c; ++ const struct s5h1420_config* config; ++ ++ struct dvb_frontend frontend; ++ struct i2c_adapter tuner_i2c_adapter; ++ ++ u8 CON_1_val; ++ ++ u8 postlocked:1; ++ u32 fclk; ++ u32 tunedfreq; ++ fe_code_rate_t fec_inner; ++ u32 symbol_rate; ++ ++ /* FIXME: ugly workaround for flexcop's incapable i2c-controller ++ * it does not support repeated-start, workaround: write addr-1 ++ * and then read ++ */ ++ u8 shadow[256]; ++}; ++ ++static u32 s5h1420_getsymbolrate(struct s5h1420_state* state); ++static int s5h1420_get_tune_settings(struct dvb_frontend* fe, ++ struct dvb_frontend_tune_settings* fesettings); ++ ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debugging"); ++ ++#define dprintk(x...) do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "S5H1420: " x); \ ++} while (0) ++ ++static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg) ++{ ++ int ret; ++ u8 b[2]; ++ struct i2c_msg msg[] = { ++ { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 }, ++ { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 }, ++ }; ++ ++ b[0] = (reg - 1) & 0xff; ++ b[1] = state->shadow[(reg - 1) & 0xff]; ++ ++ if (state->config->repeated_start_workaround) { ++ ret = i2c_transfer(state->i2c, msg, 3); ++ if (ret != 3) ++ return ret; ++ } else { ++ ret = i2c_transfer(state->i2c, &msg[1], 1); ++ if (ret != 1) ++ return ret; ++ ret = i2c_transfer(state->i2c, &msg[2], 1); ++ if (ret != 1) ++ return ret; ++ } ++ ++ /* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */ ++ ++ return b[0]; ++} ++ ++static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ int err; ++ ++ /* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ state->shadow[reg] = data; ++ ++ return 0; ++} ++ ++static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ dprintk("enter %s\n", __func__); ++ ++ switch(voltage) { ++ case SEC_VOLTAGE_13: ++ s5h1420_writereg(state, 0x3c, ++ (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02); ++ break; ++ ++ case SEC_VOLTAGE_18: ++ s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03); ++ break; ++ ++ case SEC_VOLTAGE_OFF: ++ s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd); ++ break; ++ } ++ ++ dprintk("leave %s\n", __func__); ++ return 0; ++} ++ ++static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ dprintk("enter %s\n", __func__); ++ switch(tone) { ++ case SEC_TONE_ON: ++ s5h1420_writereg(state, 0x3b, ++ (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08); ++ break; ++ ++ case SEC_TONE_OFF: ++ s5h1420_writereg(state, 0x3b, ++ (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01); ++ break; ++ } ++ dprintk("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++static int s5h1420_send_master_cmd (struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ u8 val; ++ int i; ++ unsigned long timeout; ++ int result = 0; ++ ++ dprintk("enter %s\n", __func__); ++ if (cmd->msg_len > 8) ++ return -EINVAL; ++ ++ /* setup for DISEQC */ ++ val = s5h1420_readreg(state, 0x3b); ++ s5h1420_writereg(state, 0x3b, 0x02); ++ msleep(15); ++ ++ /* write the DISEQC command bytes */ ++ for(i=0; i< cmd->msg_len; i++) { ++ s5h1420_writereg(state, 0x3d + i, cmd->msg[i]); ++ } ++ ++ /* kick off transmission */ ++ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | ++ ((cmd->msg_len-1) << 4) | 0x08); ++ ++ /* wait for transmission to complete */ ++ timeout = jiffies + ((100*HZ) / 1000); ++ while(time_before(jiffies, timeout)) { ++ if (!(s5h1420_readreg(state, 0x3b) & 0x08)) ++ break; ++ ++ msleep(5); ++ } ++ if (time_after(jiffies, timeout)) ++ result = -ETIMEDOUT; ++ ++ /* restore original settings */ ++ s5h1420_writereg(state, 0x3b, val); ++ msleep(15); ++ dprintk("leave %s\n", __func__); ++ return result; ++} ++ ++static int s5h1420_recv_slave_reply (struct dvb_frontend* fe, ++ struct dvb_diseqc_slave_reply* reply) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ u8 val; ++ int i; ++ int length; ++ unsigned long timeout; ++ int result = 0; ++ ++ /* setup for DISEQC receive */ ++ val = s5h1420_readreg(state, 0x3b); ++ s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */ ++ msleep(15); ++ ++ /* wait for reception to complete */ ++ timeout = jiffies + ((reply->timeout*HZ) / 1000); ++ while(time_before(jiffies, timeout)) { ++ if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */ ++ break; ++ ++ msleep(5); ++ } ++ if (time_after(jiffies, timeout)) { ++ result = -ETIMEDOUT; ++ goto exit; ++ } ++ ++ /* check error flag - FIXME: not sure what this does - docs do not describe ++ * beyond "error flag for diseqc receive data :( */ ++ if (s5h1420_readreg(state, 0x49)) { ++ result = -EIO; ++ goto exit; ++ } ++ ++ /* check length */ ++ length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4; ++ if (length > sizeof(reply->msg)) { ++ result = -EOVERFLOW; ++ goto exit; ++ } ++ reply->msg_len = length; ++ ++ /* extract data */ ++ for(i=0; i< length; i++) { ++ reply->msg[i] = s5h1420_readreg(state, 0x3d + i); ++ } ++ ++exit: ++ /* restore original settings */ ++ s5h1420_writereg(state, 0x3b, val); ++ msleep(15); ++ return result; ++} ++ ++static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ u8 val; ++ int result = 0; ++ unsigned long timeout; ++ ++ /* setup for tone burst */ ++ val = s5h1420_readreg(state, 0x3b); ++ s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01); ++ ++ /* set value for B position if requested */ ++ if (minicmd == SEC_MINI_B) { ++ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04); ++ } ++ msleep(15); ++ ++ /* start transmission */ ++ s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08); ++ ++ /* wait for transmission to complete */ ++ timeout = jiffies + ((100*HZ) / 1000); ++ while(time_before(jiffies, timeout)) { ++ if (!(s5h1420_readreg(state, 0x3b) & 0x08)) ++ break; ++ ++ msleep(5); ++ } ++ if (time_after(jiffies, timeout)) ++ result = -ETIMEDOUT; ++ ++ /* restore original settings */ ++ s5h1420_writereg(state, 0x3b, val); ++ msleep(15); ++ return result; ++} ++ ++static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state) ++{ ++ u8 val; ++ fe_status_t status = 0; ++ ++ val = s5h1420_readreg(state, 0x14); ++ if (val & 0x02) ++ status |= FE_HAS_SIGNAL; ++ if (val & 0x01) ++ status |= FE_HAS_CARRIER; ++ val = s5h1420_readreg(state, 0x36); ++ if (val & 0x01) ++ status |= FE_HAS_VITERBI; ++ if (val & 0x20) ++ status |= FE_HAS_SYNC; ++ if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC)) ++ status |= FE_HAS_LOCK; ++ ++ return status; ++} ++ ++static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("enter %s\n", __func__); ++ ++ if (status == NULL) ++ return -EINVAL; ++ ++ /* determine lock state */ ++ *status = s5h1420_get_status_bits(state); ++ ++ /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert ++ the inversion, wait a bit and check again */ ++ if (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI)) { ++ val = s5h1420_readreg(state, Vit10); ++ if ((val & 0x07) == 0x03) { ++ if (val & 0x08) ++ s5h1420_writereg(state, Vit09, 0x13); ++ else ++ s5h1420_writereg(state, Vit09, 0x1b); ++ ++ /* wait a bit then update lock status */ ++ mdelay(200); ++ *status = s5h1420_get_status_bits(state); ++ } ++ } ++ ++ /* perform post lock setup */ ++ if ((*status & FE_HAS_LOCK) && !state->postlocked) { ++ ++ /* calculate the data rate */ ++ u32 tmp = s5h1420_getsymbolrate(state); ++ switch (s5h1420_readreg(state, Vit10) & 0x07) { ++ case 0: tmp = (tmp * 2 * 1) / 2; break; ++ case 1: tmp = (tmp * 2 * 2) / 3; break; ++ case 2: tmp = (tmp * 2 * 3) / 4; break; ++ case 3: tmp = (tmp * 2 * 5) / 6; break; ++ case 4: tmp = (tmp * 2 * 6) / 7; break; ++ case 5: tmp = (tmp * 2 * 7) / 8; break; ++ } ++ ++ if (tmp == 0) { ++ printk(KERN_ERR "s5h1420: avoided division by 0\n"); ++ tmp = 1; ++ } ++ tmp = state->fclk / tmp; ++ ++ ++ /* set the MPEG_CLK_INTL for the calculated data rate */ ++ if (tmp < 2) ++ val = 0x00; ++ else if (tmp < 5) ++ val = 0x01; ++ else if (tmp < 9) ++ val = 0x02; ++ else if (tmp < 13) ++ val = 0x03; ++ else if (tmp < 17) ++ val = 0x04; ++ else if (tmp < 25) ++ val = 0x05; ++ else if (tmp < 33) ++ val = 0x06; ++ else ++ val = 0x07; ++ dprintk("for MPEG_CLK_INTL %d %x\n", tmp, val); ++ ++ s5h1420_writereg(state, FEC01, 0x18); ++ s5h1420_writereg(state, FEC01, 0x10); ++ s5h1420_writereg(state, FEC01, val); ++ ++ /* Enable "MPEG_Out" */ ++ val = s5h1420_readreg(state, Mpeg02); ++ s5h1420_writereg(state, Mpeg02, val | (1 << 6)); ++ ++ /* kicker disable */ ++ val = s5h1420_readreg(state, QPSK01) & 0x7f; ++ s5h1420_writereg(state, QPSK01, val); ++ ++ /* DC freeze TODO it was never activated by default or it can stay activated */ ++ ++ if (s5h1420_getsymbolrate(state) >= 20000000) { ++ s5h1420_writereg(state, Loop04, 0x8a); ++ s5h1420_writereg(state, Loop05, 0x6a); ++ } else { ++ s5h1420_writereg(state, Loop04, 0x58); ++ s5h1420_writereg(state, Loop05, 0x27); ++ } ++ ++ /* post-lock processing has been done! */ ++ state->postlocked = 1; ++ } ++ ++ dprintk("leave %s\n", __func__); ++ ++ return 0; ++} ++ ++static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ s5h1420_writereg(state, 0x46, 0x1d); ++ mdelay(25); ++ ++ *ber = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); ++ ++ return 0; ++} ++ ++static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ u8 val = s5h1420_readreg(state, 0x15); ++ ++ *strength = (u16) ((val << 8) | val); ++ ++ return 0; ++} ++ ++static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ s5h1420_writereg(state, 0x46, 0x1f); ++ mdelay(25); ++ ++ *ucblocks = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); ++ ++ return 0; ++} ++ ++static void s5h1420_reset(struct s5h1420_state* state) ++{ ++ dprintk("%s\n", __func__); ++ s5h1420_writereg (state, 0x01, 0x08); ++ s5h1420_writereg (state, 0x01, 0x00); ++ udelay(10); ++} ++ ++static void s5h1420_setsymbolrate(struct s5h1420_state* state, ++ struct dtv_frontend_properties *p) ++{ ++ u8 v; ++ u64 val; ++ ++ dprintk("enter %s\n", __func__); ++ ++ val = ((u64) p->symbol_rate / 1000ULL) * (1ULL<<24); ++ if (p->symbol_rate < 29000000) ++ val *= 2; ++ do_div(val, (state->fclk / 1000)); ++ ++ dprintk("symbol rate register: %06llx\n", (unsigned long long)val); ++ ++ v = s5h1420_readreg(state, Loop01); ++ s5h1420_writereg(state, Loop01, v & 0x7f); ++ s5h1420_writereg(state, Tnco01, val >> 16); ++ s5h1420_writereg(state, Tnco02, val >> 8); ++ s5h1420_writereg(state, Tnco03, val & 0xff); ++ s5h1420_writereg(state, Loop01, v | 0x80); ++ dprintk("leave %s\n", __func__); ++} ++ ++static u32 s5h1420_getsymbolrate(struct s5h1420_state* state) ++{ ++ return state->symbol_rate; ++} ++ ++static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset) ++{ ++ int val; ++ u8 v; ++ ++ dprintk("enter %s\n", __func__); ++ ++ /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so ++ * divide fclk by 1000000 to get the correct value. */ ++ val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000)); ++ ++ dprintk("phase rotator/freqoffset: %d %06x\n", freqoffset, val); ++ ++ v = s5h1420_readreg(state, Loop01); ++ s5h1420_writereg(state, Loop01, v & 0xbf); ++ s5h1420_writereg(state, Pnco01, val >> 16); ++ s5h1420_writereg(state, Pnco02, val >> 8); ++ s5h1420_writereg(state, Pnco03, val & 0xff); ++ s5h1420_writereg(state, Loop01, v | 0x40); ++ dprintk("leave %s\n", __func__); ++} ++ ++static int s5h1420_getfreqoffset(struct s5h1420_state* state) ++{ ++ int val; ++ ++ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08); ++ val = s5h1420_readreg(state, 0x0e) << 16; ++ val |= s5h1420_readreg(state, 0x0f) << 8; ++ val |= s5h1420_readreg(state, 0x10); ++ s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7); ++ ++ if (val & 0x800000) ++ val |= 0xff000000; ++ ++ /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so ++ * divide fclk by 1000000 to get the correct value. */ ++ val = (((-val) * (state->fclk/1000000)) / (1<<24)); ++ ++ return val; ++} ++ ++static void s5h1420_setfec_inversion(struct s5h1420_state* state, ++ struct dtv_frontend_properties *p) ++{ ++ u8 inversion = 0; ++ u8 vit08, vit09; ++ ++ dprintk("enter %s\n", __func__); ++ ++ if (p->inversion == INVERSION_OFF) ++ inversion = state->config->invert ? 0x08 : 0; ++ else if (p->inversion == INVERSION_ON) ++ inversion = state->config->invert ? 0 : 0x08; ++ ++ if ((p->fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) { ++ vit08 = 0x3f; ++ vit09 = 0; ++ } else { ++ switch (p->fec_inner) { ++ case FEC_1_2: ++ vit08 = 0x01; vit09 = 0x10; ++ break; ++ ++ case FEC_2_3: ++ vit08 = 0x02; vit09 = 0x11; ++ break; ++ ++ case FEC_3_4: ++ vit08 = 0x04; vit09 = 0x12; ++ break; ++ ++ case FEC_5_6: ++ vit08 = 0x08; vit09 = 0x13; ++ break; ++ ++ case FEC_6_7: ++ vit08 = 0x10; vit09 = 0x14; ++ break; ++ ++ case FEC_7_8: ++ vit08 = 0x20; vit09 = 0x15; ++ break; ++ ++ default: ++ return; ++ } ++ } ++ vit09 |= inversion; ++ dprintk("fec: %02x %02x\n", vit08, vit09); ++ s5h1420_writereg(state, Vit08, vit08); ++ s5h1420_writereg(state, Vit09, vit09); ++ dprintk("leave %s\n", __func__); ++} ++ ++static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state) ++{ ++ switch(s5h1420_readreg(state, 0x32) & 0x07) { ++ case 0: ++ return FEC_1_2; ++ ++ case 1: ++ return FEC_2_3; ++ ++ case 2: ++ return FEC_3_4; ++ ++ case 3: ++ return FEC_5_6; ++ ++ case 4: ++ return FEC_6_7; ++ ++ case 5: ++ return FEC_7_8; ++ } ++ ++ return FEC_NONE; ++} ++ ++static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state) ++{ ++ if (s5h1420_readreg(state, 0x32) & 0x08) ++ return INVERSION_ON; ++ ++ return INVERSION_OFF; ++} ++ ++static int s5h1420_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1420_state* state = fe->demodulator_priv; ++ int frequency_delta; ++ struct dvb_frontend_tune_settings fesettings; ++ ++ dprintk("enter %s\n", __func__); ++ ++ /* check if we should do a fast-tune */ ++ s5h1420_get_tune_settings(fe, &fesettings); ++ frequency_delta = p->frequency - state->tunedfreq; ++ if ((frequency_delta > -fesettings.max_drift) && ++ (frequency_delta < fesettings.max_drift) && ++ (frequency_delta != 0) && ++ (state->fec_inner == p->fec_inner) && ++ (state->symbol_rate == p->symbol_rate)) { ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (fe->ops.tuner_ops.get_frequency) { ++ u32 tmp; ++ fe->ops.tuner_ops.get_frequency(fe, &tmp); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ s5h1420_setfreqoffset(state, p->frequency - tmp); ++ } else { ++ s5h1420_setfreqoffset(state, 0); ++ } ++ dprintk("simple tune\n"); ++ return 0; ++ } ++ dprintk("tuning demod\n"); ++ ++ /* first of all, software reset */ ++ s5h1420_reset(state); ++ ++ /* set s5h1420 fclk PLL according to desired symbol rate */ ++ if (p->symbol_rate > 33000000) ++ state->fclk = 80000000; ++ else if (p->symbol_rate > 28500000) ++ state->fclk = 59000000; ++ else if (p->symbol_rate > 25000000) ++ state->fclk = 86000000; ++ else if (p->symbol_rate > 1900000) ++ state->fclk = 88000000; ++ else ++ state->fclk = 44000000; ++ ++ dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); ++ s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8); ++ s5h1420_writereg(state, PLL02, 0x40); ++ s5h1420_writereg(state, DiS01, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); ++ ++ /* TODO DC offset removal, config parameter ? */ ++ if (p->symbol_rate > 29000000) ++ s5h1420_writereg(state, QPSK01, 0xae | 0x10); ++ else ++ s5h1420_writereg(state, QPSK01, 0xac | 0x10); ++ ++ /* set misc registers */ ++ s5h1420_writereg(state, CON_1, 0x00); ++ s5h1420_writereg(state, QPSK02, 0x00); ++ s5h1420_writereg(state, Pre01, 0xb0); ++ ++ s5h1420_writereg(state, Loop01, 0xF0); ++ s5h1420_writereg(state, Loop02, 0x2a); /* e7 for s5h1420 */ ++ s5h1420_writereg(state, Loop03, 0x79); /* 78 for s5h1420 */ ++ if (p->symbol_rate > 20000000) ++ s5h1420_writereg(state, Loop04, 0x79); ++ else ++ s5h1420_writereg(state, Loop04, 0x58); ++ s5h1420_writereg(state, Loop05, 0x6b); ++ ++ if (p->symbol_rate >= 8000000) ++ s5h1420_writereg(state, Post01, (0 << 6) | 0x10); ++ else if (p->symbol_rate >= 4000000) ++ s5h1420_writereg(state, Post01, (1 << 6) | 0x10); ++ else ++ s5h1420_writereg(state, Post01, (3 << 6) | 0x10); ++ ++ s5h1420_writereg(state, Monitor12, 0x00); /* unfreeze DC compensation */ ++ ++ s5h1420_writereg(state, Sync01, 0x33); ++ s5h1420_writereg(state, Mpeg01, state->config->cdclk_polarity); ++ s5h1420_writereg(state, Mpeg02, 0x3d); /* Parallel output more, disabled -> enabled later */ ++ s5h1420_writereg(state, Err01, 0x03); /* 0x1d for s5h1420 */ ++ ++ s5h1420_writereg(state, Vit06, 0x6e); /* 0x8e for s5h1420 */ ++ s5h1420_writereg(state, DiS03, 0x00); ++ s5h1420_writereg(state, Rf01, 0x61); /* Tuner i2c address - for the gate controller */ ++ ++ /* set tuner PLL */ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ s5h1420_setfreqoffset(state, 0); ++ } ++ ++ /* set the reset of the parameters */ ++ s5h1420_setsymbolrate(state, p); ++ s5h1420_setfec_inversion(state, p); ++ ++ /* start QPSK */ ++ s5h1420_writereg(state, QPSK01, s5h1420_readreg(state, QPSK01) | 1); ++ ++ state->fec_inner = p->fec_inner; ++ state->symbol_rate = p->symbol_rate; ++ state->postlocked = 0; ++ state->tunedfreq = p->frequency; ++ ++ dprintk("leave %s\n", __func__); ++ return 0; ++} ++ ++static int s5h1420_get_frontend(struct dvb_frontend* fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state); ++ p->inversion = s5h1420_getinversion(state); ++ p->symbol_rate = s5h1420_getsymbolrate(state); ++ p->fec_inner = s5h1420_getfec(state); ++ ++ return 0; ++} ++ ++static int s5h1420_get_tune_settings(struct dvb_frontend* fe, ++ struct dvb_frontend_tune_settings* fesettings) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ if (p->symbol_rate > 20000000) { ++ fesettings->min_delay_ms = 50; ++ fesettings->step_size = 2000; ++ fesettings->max_drift = 8000; ++ } else if (p->symbol_rate > 12000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 1500; ++ fesettings->max_drift = 9000; ++ } else if (p->symbol_rate > 8000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 1000; ++ fesettings->max_drift = 8000; ++ } else if (p->symbol_rate > 4000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 500; ++ fesettings->max_drift = 7000; ++ } else if (p->symbol_rate > 2000000) { ++ fesettings->min_delay_ms = 200; ++ fesettings->step_size = (p->symbol_rate / 8000); ++ fesettings->max_drift = 14 * fesettings->step_size; ++ } else { ++ fesettings->min_delay_ms = 200; ++ fesettings->step_size = (p->symbol_rate / 8000); ++ fesettings->max_drift = 18 * fesettings->step_size; ++ } ++ ++ return 0; ++} ++ ++static int s5h1420_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ if (enable) ++ return s5h1420_writereg(state, 0x02, state->CON_1_val | 1); ++ else ++ return s5h1420_writereg(state, 0x02, state->CON_1_val & 0xfe); ++} ++ ++static int s5h1420_init (struct dvb_frontend* fe) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ ++ /* disable power down and do reset */ ++ state->CON_1_val = state->config->serial_mpeg << 4; ++ s5h1420_writereg(state, 0x02, state->CON_1_val); ++ msleep(10); ++ s5h1420_reset(state); ++ ++ return 0; ++} ++ ++static int s5h1420_sleep(struct dvb_frontend* fe) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ state->CON_1_val = 0x12; ++ return s5h1420_writereg(state, 0x02, state->CON_1_val); ++} ++ ++static void s5h1420_release(struct dvb_frontend* fe) ++{ ++ struct s5h1420_state* state = fe->demodulator_priv; ++ i2c_del_adapter(&state->tuner_i2c_adapter); ++ kfree(state); ++} ++ ++static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) ++{ ++ struct s5h1420_state *state = i2c_get_adapdata(i2c_adap); ++ struct i2c_msg m[1 + num]; ++ u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */ ++ ++ memset(m, 0, sizeof(struct i2c_msg) * (1 + num)); ++ ++ m[0].addr = state->config->demod_address; ++ m[0].buf = tx_open; ++ m[0].len = 2; ++ ++ memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); ++ ++ return i2c_transfer(state->i2c, m, 1+num) == 1 + num ? num : -EIO; ++} ++ ++static struct i2c_algorithm s5h1420_tuner_i2c_algo = { ++ .master_xfer = s5h1420_tuner_i2c_tuner_xfer, ++ .functionality = s5h1420_tuner_i2c_func, ++}; ++ ++struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ struct s5h1420_state *state = fe->demodulator_priv; ++ return &state->tuner_i2c_adapter; ++} ++EXPORT_SYMBOL(s5h1420_get_tuner_i2c_adapter); ++ ++static struct dvb_frontend_ops s5h1420_ops; ++ ++struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, ++ struct i2c_adapter *i2c) ++{ ++ /* allocate memory for the internal state */ ++ struct s5h1420_state *state = kzalloc(sizeof(struct s5h1420_state), GFP_KERNEL); ++ u8 i; ++ ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->postlocked = 0; ++ state->fclk = 88000000; ++ state->tunedfreq = 0; ++ state->fec_inner = FEC_NONE; ++ state->symbol_rate = 0; ++ ++ /* check if the demod is there + identify it */ ++ i = s5h1420_readreg(state, ID01); ++ if (i != 0x03) ++ goto error; ++ ++ memset(state->shadow, 0xff, sizeof(state->shadow)); ++ ++ for (i = 0; i < 0x50; i++) ++ state->shadow[i] = s5h1420_readreg(state, i); ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* create tuner i2c adapter */ ++ strlcpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", ++ sizeof(state->tuner_i2c_adapter.name)); ++ state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo; ++ state->tuner_i2c_adapter.algo_data = NULL; ++ i2c_set_adapdata(&state->tuner_i2c_adapter, state); ++ if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { ++ printk(KERN_ERR "S5H1420/PN1010: tuner i2c bus could not be initialized\n"); ++ goto error; ++ } ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(s5h1420_attach); ++ ++static struct dvb_frontend_ops s5h1420_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 29500, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ /* .symbol_rate_tolerance = ???,*/ ++ .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_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK ++ }, ++ ++ .release = s5h1420_release, ++ ++ .init = s5h1420_init, ++ .sleep = s5h1420_sleep, ++ .i2c_gate_ctrl = s5h1420_i2c_gate_ctrl, ++ ++ .set_frontend = s5h1420_set_frontend, ++ .get_frontend = s5h1420_get_frontend, ++ .get_tune_settings = s5h1420_get_tune_settings, ++ ++ .read_status = s5h1420_read_status, ++ .read_ber = s5h1420_read_ber, ++ .read_signal_strength = s5h1420_read_signal_strength, ++ .read_ucblocks = s5h1420_read_ucblocks, ++ ++ .diseqc_send_master_cmd = s5h1420_send_master_cmd, ++ .diseqc_recv_slave_reply = s5h1420_recv_slave_reply, ++ .diseqc_send_burst = s5h1420_send_burst, ++ .set_tone = s5h1420_set_tone, ++ .set_voltage = s5h1420_set_voltage, ++}; ++ ++MODULE_DESCRIPTION("Samsung S5H1420/PnpNetwork PN1010 DVB-S Demodulator driver"); ++MODULE_AUTHOR("Andrew de Quincey, Patrick Boettcher"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/s5h1420.h b/drivers/media/dvb-frontends/s5h1420.h +new file mode 100644 +index 0000000..ff30813 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1420.h +@@ -0,0 +1,61 @@ ++/* ++ * Driver for ++ * Samsung S5H1420 and ++ * PnpNetwork PN1010 QPSK Demodulator ++ * ++ * Copyright (C) 2005 Andrew de Quincey ++ * Copyright (C) 2005-8 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef S5H1420_H ++#define S5H1420_H ++ ++#include ++ ++struct s5h1420_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* does the inversion require inversion? */ ++ u8 invert:1; ++ ++ u8 repeated_start_workaround:1; ++ u8 cdclk_polarity:1; /* 1 == falling edge, 0 == raising edge */ ++ ++ u8 serial_mpeg:1; ++}; ++ ++#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, ++ struct i2c_adapter *i2c); ++extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); ++#else ++static inline struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ return NULL; ++} ++#endif // CONFIG_DVB_S5H1420 ++ ++#endif // S5H1420_H +diff --git a/drivers/media/dvb-frontends/s5h1420_priv.h b/drivers/media/dvb-frontends/s5h1420_priv.h +new file mode 100644 +index 0000000..d9c58d2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1420_priv.h +@@ -0,0 +1,102 @@ ++/* ++ * Driver for ++ * Samsung S5H1420 and ++ * PnpNetwork PN1010 QPSK Demodulator ++ * ++ * Copyright (C) 2005 Andrew de Quincey ++ * Copyright (C) 2005 Patrick Boettcher ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at your option) ++ * any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., 675 Mass ++ * Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef S5H1420_PRIV ++#define S5H1420_PRIV ++ ++#include ++ ++enum s5h1420_register { ++ ID01 = 0x00, ++ CON_0 = 0x01, ++ CON_1 = 0x02, ++ PLL01 = 0x03, ++ PLL02 = 0x04, ++ QPSK01 = 0x05, ++ QPSK02 = 0x06, ++ Pre01 = 0x07, ++ Post01 = 0x08, ++ Loop01 = 0x09, ++ Loop02 = 0x0a, ++ Loop03 = 0x0b, ++ Loop04 = 0x0c, ++ Loop05 = 0x0d, ++ Pnco01 = 0x0e, ++ Pnco02 = 0x0f, ++ Pnco03 = 0x10, ++ Tnco01 = 0x11, ++ Tnco02 = 0x12, ++ Tnco03 = 0x13, ++ Monitor01 = 0x14, ++ Monitor02 = 0x15, ++ Monitor03 = 0x16, ++ Monitor04 = 0x17, ++ Monitor05 = 0x18, ++ Monitor06 = 0x19, ++ Monitor07 = 0x1a, ++ Monitor12 = 0x1f, ++ ++ FEC01 = 0x22, ++ Soft01 = 0x23, ++ Soft02 = 0x24, ++ Soft03 = 0x25, ++ Soft04 = 0x26, ++ Soft05 = 0x27, ++ Soft06 = 0x28, ++ Vit01 = 0x29, ++ Vit02 = 0x2a, ++ Vit03 = 0x2b, ++ Vit04 = 0x2c, ++ Vit05 = 0x2d, ++ Vit06 = 0x2e, ++ Vit07 = 0x2f, ++ Vit08 = 0x30, ++ Vit09 = 0x31, ++ Vit10 = 0x32, ++ Vit11 = 0x33, ++ Vit12 = 0x34, ++ Sync01 = 0x35, ++ Sync02 = 0x36, ++ Rs01 = 0x37, ++ Mpeg01 = 0x38, ++ Mpeg02 = 0x39, ++ DiS01 = 0x3a, ++ DiS02 = 0x3b, ++ DiS03 = 0x3c, ++ DiS04 = 0x3d, ++ DiS05 = 0x3e, ++ DiS06 = 0x3f, ++ DiS07 = 0x40, ++ DiS08 = 0x41, ++ DiS09 = 0x42, ++ DiS10 = 0x43, ++ DiS11 = 0x44, ++ Rf01 = 0x45, ++ Err01 = 0x46, ++ Err02 = 0x47, ++ Err03 = 0x48, ++ Err04 = 0x49, ++}; ++ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c +new file mode 100644 +index 0000000..6ec16a2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1432.c +@@ -0,0 +1,403 @@ ++/* ++ * Samsung s5h1432 DVB-T demodulator driver ++ * ++ * Copyright (C) 2009 Bill Liu ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "s5h1432.h" ++ ++struct s5h1432_state { ++ ++ struct i2c_adapter *i2c; ++ ++ /* configuration settings */ ++ const struct s5h1432_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ fe_modulation_t current_modulation; ++ unsigned int first_tune:1; ++ ++ u32 current_frequency; ++ int if_freq; ++ ++ u8 inversion; ++}; ++ ++static int debug; ++ ++#define dprintk(arg...) do { \ ++ if (debug) \ ++ printk(arg); \ ++ } while (0) ++ ++static int s5h1432_writereg(struct s5h1432_state *state, ++ u8 addr, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ ++ struct i2c_msg msg = {.addr = addr, .flags = 0, .buf = buf, .len = 2 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, " ++ "ret == %i)\n", __func__, addr, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static u8 s5h1432_readreg(struct s5h1432_state *state, u8 addr, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ ++ struct i2c_msg msg[] = { ++ {.addr = addr, .flags = 0, .buf = b0, .len = 1}, ++ {.addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 1} ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk(KERN_ERR "%s: readreg error (ret == %i)\n", ++ __func__, ret); ++ return b1[0]; ++} ++ ++static int s5h1432_sleep(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int s5h1432_set_channel_bandwidth(struct dvb_frontend *fe, ++ u32 bandwidth) ++{ ++ struct s5h1432_state *state = fe->demodulator_priv; ++ ++ u8 reg = 0; ++ ++ /* Register [0x2E] bit 3:2 : 8MHz = 0; 7MHz = 1; 6MHz = 2 */ ++ reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x2E); ++ reg &= ~(0x0C); ++ switch (bandwidth) { ++ case 6: ++ reg |= 0x08; ++ break; ++ case 7: ++ reg |= 0x04; ++ break; ++ case 8: ++ reg |= 0x00; ++ break; ++ default: ++ return 0; ++ } ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2E, reg); ++ return 1; ++} ++ ++static int s5h1432_set_IF(struct dvb_frontend *fe, u32 ifFreqHz) ++{ ++ struct s5h1432_state *state = fe->demodulator_priv; ++ ++ switch (ifFreqHz) { ++ case TAIWAN_HI_IF_FREQ_44_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x15); ++ break; ++ case EUROPE_HI_IF_FREQ_36_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x40); ++ break; ++ case IF_FREQ_6_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xe0); ++ break; ++ case IF_FREQ_3point3_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); ++ break; ++ case IF_FREQ_3point5_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xED); ++ break; ++ case IF_FREQ_4_MHZ: ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0xAA); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0xAA); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEA); ++ break; ++ default: ++ { ++ u32 value = 0; ++ value = (u32) (((48000 - (ifFreqHz / 1000)) * 512 * ++ (u32) 32768) / (48 * 1000)); ++ printk(KERN_INFO ++ "Default IFFreq %d :reg value = 0x%x\n", ++ ifFreqHz, value); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, ++ (u8) value & 0xFF); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, ++ (u8) (value >> 8) & 0xFF); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, ++ (u8) (value >> 16) & 0xFF); ++ break; ++ } ++ ++ } ++ ++ return 1; ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++static int s5h1432_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ u32 dvb_bandwidth = 8; ++ struct s5h1432_state *state = fe->demodulator_priv; ++ ++ if (p->frequency == state->current_frequency) { ++ /*current_frequency = p->frequency; */ ++ /*state->current_frequency = p->frequency; */ ++ } else { ++ fe->ops.tuner_ops.set_params(fe); ++ msleep(300); ++ s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); ++ switch (p->bandwidth_hz) { ++ case 6000000: ++ dvb_bandwidth = 6; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ case 7000000: ++ dvb_bandwidth = 7; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ case 8000000: ++ dvb_bandwidth = 8; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ default: ++ return 0; ++ } ++ /*fe->ops.tuner_ops.set_params(fe); */ ++/*Soft Reset chip*/ ++ msleep(30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); ++ msleep(30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); ++ ++ s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); ++ switch (p->bandwidth_hz) { ++ case 6000000: ++ dvb_bandwidth = 6; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ case 7000000: ++ dvb_bandwidth = 7; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ case 8000000: ++ dvb_bandwidth = 8; ++ s5h1432_set_IF(fe, IF_FREQ_4_MHZ); ++ break; ++ default: ++ return 0; ++ } ++ /*fe->ops.tuner_ops.set_params(fe); */ ++ /*Soft Reset chip*/ ++ msleep(30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); ++ msleep(30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); ++ ++ } ++ ++ state->current_frequency = p->frequency; ++ ++ return 0; ++} ++ ++static int s5h1432_init(struct dvb_frontend *fe) ++{ ++ struct s5h1432_state *state = fe->demodulator_priv; ++ ++ u8 reg = 0; ++ state->current_frequency = 0; ++ printk(KERN_INFO " s5h1432_init().\n"); ++ ++ /*Set VSB mode as default, this also does a soft reset */ ++ /*Initialize registers */ ++ ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x04, 0xa8); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x05, 0x01); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x07, 0x70); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x19, 0x80); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1b, 0x9D); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1c, 0x30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1d, 0x20); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x1B); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2e, 0x40); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, 0x84); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x50, 0x5a); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x5a, 0xd3); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x68, 0x50); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xb8, 0x3c); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xc4, 0x10); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xcc, 0x9c); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xDA, 0x00); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe1, 0x94); ++ /* s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf4, 0xa1); */ ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf9, 0x00); ++ ++ /*For NXP tuner*/ ++ ++ /*Set 3.3MHz as default IF frequency */ ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); ++ /* Set reg 0x1E to get the full dynamic range */ ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x31); ++ ++ /* Mode setting in demod */ ++ reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x42); ++ reg |= 0x80; ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, reg); ++ /* Serial mode */ ++ ++ /* Soft Reset chip */ ++ ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); ++ msleep(30); ++ s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); ++ ++ ++ return 0; ++} ++ ++static int s5h1432_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ return 0; ++} ++ ++static int s5h1432_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ return 0; ++} ++ ++static int s5h1432_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ return 0; ++} ++ ++static int s5h1432_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ ++ return 0; ++} ++ ++static int s5h1432_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ return 0; ++} ++ ++static int s5h1432_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ return 0; ++} ++ ++static void s5h1432_release(struct dvb_frontend *fe) ++{ ++ struct s5h1432_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops s5h1432_ops; ++ ++struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct s5h1432_state *state = NULL; ++ ++ printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n"); ++ /* allocate memory for the internal state */ ++ state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->current_modulation = QAM_16; ++ state->inversion = state->config->inversion; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &s5h1432_ops, ++ sizeof(struct dvb_frontend_ops)); ++ ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++} ++EXPORT_SYMBOL(s5h1432_attach); ++ ++static struct dvb_frontend_ops s5h1432_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Samsung s5h1432 DVB-T Frontend", ++ .frequency_min = 177000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER}, ++ ++ .init = s5h1432_init, ++ .sleep = s5h1432_sleep, ++ .set_frontend = s5h1432_set_frontend, ++ .get_tune_settings = s5h1432_get_tune_settings, ++ .read_status = s5h1432_read_status, ++ .read_ber = s5h1432_read_ber, ++ .read_signal_strength = s5h1432_read_signal_strength, ++ .read_snr = s5h1432_read_snr, ++ .read_ucblocks = s5h1432_read_ucblocks, ++ .release = s5h1432_release, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++MODULE_DESCRIPTION("Samsung s5h1432 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Bill Liu"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/s5h1432.h b/drivers/media/dvb-frontends/s5h1432.h +new file mode 100644 +index 0000000..b57438c +--- /dev/null ++++ b/drivers/media/dvb-frontends/s5h1432.h +@@ -0,0 +1,91 @@ ++/* ++ * Samsung s5h1432 VSB/QAM demodulator driver ++ * ++ * Copyright (C) 2009 Bill Liu ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef __S5H1432_H__ ++#define __S5H1432_H__ ++ ++#include ++ ++#define S5H1432_I2C_TOP_ADDR (0x02 >> 1) ++ ++#define TAIWAN_HI_IF_FREQ_44_MHZ 44000000 ++#define EUROPE_HI_IF_FREQ_36_MHZ 36000000 ++#define IF_FREQ_6_MHZ 6000000 ++#define IF_FREQ_3point3_MHZ 3300000 ++#define IF_FREQ_3point5_MHZ 3500000 ++#define IF_FREQ_4_MHZ 4000000 ++ ++struct s5h1432_config { ++ ++ /* serial/parallel output */ ++#define S5H1432_PARALLEL_OUTPUT 0 ++#define S5H1432_SERIAL_OUTPUT 1 ++ u8 output_mode; ++ ++ /* GPIO Setting */ ++#define S5H1432_GPIO_OFF 0 ++#define S5H1432_GPIO_ON 1 ++ u8 gpio; ++ ++ /* MPEG signal timing */ ++#define S5H1432_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 ++#define S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 ++#define S5H1432_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 ++#define S5H1432_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 ++ u16 mpeg_timing; ++ ++ /* IF Freq for QAM and VSB in KHz */ ++#define S5H1432_IF_3250 3250 ++#define S5H1432_IF_3500 3500 ++#define S5H1432_IF_4000 4000 ++#define S5H1432_IF_5380 5380 ++#define S5H1432_IF_44000 44000 ++#define S5H1432_VSB_IF_DEFAULT s5h1432_IF_44000 ++#define S5H1432_QAM_IF_DEFAULT s5h1432_IF_44000 ++ u16 qam_if; ++ u16 vsb_if; ++ ++ /* Spectral Inversion */ ++#define S5H1432_INVERSION_OFF 0 ++#define S5H1432_INVERSION_ON 1 ++ u8 inversion; ++ ++ /* Return lock status based on tuner lock, or demod lock */ ++#define S5H1432_TUNERLOCKING 0 ++#define S5H1432_DEMODLOCKING 1 ++ u8 status_mode; ++}; ++ ++#if defined(CONFIG_DVB_S5H1432) || \ ++ (defined(CONFIG_DVB_S5H1432_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *s5h1432_attach(const struct s5h1432_config ++ *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_s5h1432 */ ++ ++#endif /* __s5h1432_H__ */ +diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c +new file mode 100644 +index 0000000..a271ac3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s921.c +@@ -0,0 +1,544 @@ ++/* ++ * Sharp VA3A5JZ921 One Seg Broadcast Module driver ++ * This device is labeled as just S. 921 at the top of the frontend can ++ * ++ * Copyright (C) 2009-2010 Mauro Carvalho Chehab ++ * Copyright (C) 2009-2010 Douglas Landgraf ++ * ++ * Developed for Leadership SBTVD 1seg device sold in Brazil ++ * ++ * Frontend module based on cx24123 driver, getting some info from ++ * the old s921 driver. ++ * ++ * FIXME: Need to port to DVB v5.2 API ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "s921.h" ++ ++static int debug = 1; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); ++ ++#define rc(args...) do { \ ++ printk(KERN_ERR "s921: " args); \ ++} while (0) ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) { \ ++ printk(KERN_DEBUG "s921: %s: ", __func__); \ ++ printk(args); \ ++ } \ ++ } while (0) ++ ++struct s921_state { ++ struct i2c_adapter *i2c; ++ const struct s921_config *config; ++ ++ struct dvb_frontend frontend; ++ ++ /* The Demod can't easily provide these, we cache them */ ++ u32 currentfreq; ++}; ++ ++/* ++ * Various tuner defaults need to be established for a given frequency kHz. ++ * fixme: The bounds on the bands do not match the doc in real life. ++ * fixme: Some of them have been moved, other might need adjustment. ++ */ ++static struct s921_bandselect_val { ++ u32 freq_low; ++ u8 band_reg; ++} s921_bandselect[] = { ++ { 0, 0x7b }, ++ { 485140000, 0x5b }, ++ { 515140000, 0x3b }, ++ { 545140000, 0x1b }, ++ { 599140000, 0xfb }, ++ { 623140000, 0xdb }, ++ { 659140000, 0xbb }, ++ { 713140000, 0x9b }, ++}; ++ ++struct regdata { ++ u8 reg; ++ u8 data; ++}; ++ ++static struct regdata s921_init[] = { ++ { 0x01, 0x80 }, /* Probably, a reset sequence */ ++ { 0x01, 0x40 }, ++ { 0x01, 0x80 }, ++ { 0x01, 0x40 }, ++ ++ { 0x02, 0x00 }, ++ { 0x03, 0x40 }, ++ { 0x04, 0x01 }, ++ { 0x05, 0x00 }, ++ { 0x06, 0x00 }, ++ { 0x07, 0x00 }, ++ { 0x08, 0x00 }, ++ { 0x09, 0x00 }, ++ { 0x0a, 0x00 }, ++ { 0x0b, 0x5a }, ++ { 0x0c, 0x00 }, ++ { 0x0d, 0x00 }, ++ { 0x0f, 0x00 }, ++ { 0x13, 0x1b }, ++ { 0x14, 0x80 }, ++ { 0x15, 0x40 }, ++ { 0x17, 0x70 }, ++ { 0x18, 0x01 }, ++ { 0x19, 0x12 }, ++ { 0x1a, 0x01 }, ++ { 0x1b, 0x12 }, ++ { 0x1c, 0xa0 }, ++ { 0x1d, 0x00 }, ++ { 0x1e, 0x0a }, ++ { 0x1f, 0x08 }, ++ { 0x20, 0x40 }, ++ { 0x21, 0xff }, ++ { 0x22, 0x4c }, ++ { 0x23, 0x4e }, ++ { 0x24, 0x4c }, ++ { 0x25, 0x00 }, ++ { 0x26, 0x00 }, ++ { 0x27, 0xf4 }, ++ { 0x28, 0x60 }, ++ { 0x29, 0x88 }, ++ { 0x2a, 0x40 }, ++ { 0x2b, 0x40 }, ++ { 0x2c, 0xff }, ++ { 0x2d, 0x00 }, ++ { 0x2e, 0xff }, ++ { 0x2f, 0x00 }, ++ { 0x30, 0x20 }, ++ { 0x31, 0x06 }, ++ { 0x32, 0x0c }, ++ { 0x34, 0x0f }, ++ { 0x37, 0xfe }, ++ { 0x38, 0x00 }, ++ { 0x39, 0x63 }, ++ { 0x3a, 0x10 }, ++ { 0x3b, 0x10 }, ++ { 0x47, 0x00 }, ++ { 0x49, 0xe5 }, ++ { 0x4b, 0x00 }, ++ { 0x50, 0xc0 }, ++ { 0x52, 0x20 }, ++ { 0x54, 0x5a }, ++ { 0x55, 0x5b }, ++ { 0x56, 0x40 }, ++ { 0x57, 0x70 }, ++ { 0x5c, 0x50 }, ++ { 0x5d, 0x00 }, ++ { 0x62, 0x17 }, ++ { 0x63, 0x2f }, ++ { 0x64, 0x6f }, ++ { 0x68, 0x00 }, ++ { 0x69, 0x89 }, ++ { 0x6a, 0x00 }, ++ { 0x6b, 0x00 }, ++ { 0x6c, 0x00 }, ++ { 0x6d, 0x00 }, ++ { 0x6e, 0x00 }, ++ { 0x70, 0x10 }, ++ { 0x71, 0x00 }, ++ { 0x75, 0x00 }, ++ { 0x76, 0x30 }, ++ { 0x77, 0x01 }, ++ { 0xaf, 0x00 }, ++ { 0xb0, 0xa0 }, ++ { 0xb2, 0x3d }, ++ { 0xb3, 0x25 }, ++ { 0xb4, 0x8b }, ++ { 0xb5, 0x4b }, ++ { 0xb6, 0x3f }, ++ { 0xb7, 0xff }, ++ { 0xb8, 0xff }, ++ { 0xb9, 0xfc }, ++ { 0xba, 0x00 }, ++ { 0xbb, 0x00 }, ++ { 0xbc, 0x00 }, ++ { 0xd0, 0x30 }, ++ { 0xe4, 0x84 }, ++ { 0xf0, 0x48 }, ++ { 0xf1, 0x19 }, ++ { 0xf2, 0x5a }, ++ { 0xf3, 0x8e }, ++ { 0xf4, 0x2d }, ++ { 0xf5, 0x07 }, ++ { 0xf6, 0x5a }, ++ { 0xf7, 0xba }, ++ { 0xf8, 0xd7 }, ++}; ++ ++static struct regdata s921_prefreq[] = { ++ { 0x47, 0x60 }, ++ { 0x68, 0x00 }, ++ { 0x69, 0x89 }, ++ { 0xf0, 0x48 }, ++ { 0xf1, 0x19 }, ++}; ++ ++static struct regdata s921_postfreq[] = { ++ { 0xf5, 0xae }, ++ { 0xf6, 0xb7 }, ++ { 0xf7, 0xba }, ++ { 0xf8, 0xd7 }, ++ { 0x68, 0x0a }, ++ { 0x69, 0x09 }, ++}; ++ ++static int s921_i2c_writereg(struct s921_state *state, ++ u8 i2c_addr, int reg, int data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 ++ }; ++ int rc; ++ ++ rc = i2c_transfer(state->i2c, &msg, 1); ++ if (rc != 1) { ++ printk("%s: writereg rcor(rc == %i, reg == 0x%02x," ++ " data == 0x%02x)\n", __func__, rc, reg, data); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int s921_i2c_writeregdata(struct s921_state *state, u8 i2c_addr, ++ struct regdata *rd, int size) ++{ ++ int i, rc; ++ ++ for (i = 0; i < size; i++) { ++ rc = s921_i2c_writereg(state, i2c_addr, rd[i].reg, rd[i].data); ++ if (rc < 0) ++ return rc; ++ } ++ return 0; ++} ++ ++static int s921_i2c_readreg(struct s921_state *state, u8 i2c_addr, u8 reg) ++{ ++ u8 val; ++ int rc; ++ struct i2c_msg msg[] = { ++ { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } ++ }; ++ ++ rc = i2c_transfer(state->i2c, msg, 2); ++ ++ if (rc != 2) { ++ rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc); ++ return rc; ++ } ++ ++ return val; ++} ++ ++#define s921_readreg(state, reg) \ ++ s921_i2c_readreg(state, state->config->demod_address, reg) ++#define s921_writereg(state, reg, val) \ ++ s921_i2c_writereg(state, state->config->demod_address, reg, val) ++#define s921_writeregdata(state, regdata) \ ++ s921_i2c_writeregdata(state, state->config->demod_address, \ ++ regdata, ARRAY_SIZE(regdata)) ++ ++static int s921_pll_tune(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s921_state *state = fe->demodulator_priv; ++ int band, rc, i; ++ unsigned long f_offset; ++ u8 f_switch; ++ u64 offset; ++ ++ dprintk("frequency=%i\n", p->frequency); ++ ++ for (band = 0; band < ARRAY_SIZE(s921_bandselect); band++) ++ if (p->frequency < s921_bandselect[band].freq_low) ++ break; ++ band--; ++ ++ if (band < 0) { ++ rc("%s: frequency out of range\n", __func__); ++ return -EINVAL; ++ } ++ ++ f_switch = s921_bandselect[band].band_reg; ++ ++ offset = ((u64)p->frequency) * 258; ++ do_div(offset, 6000000); ++ f_offset = ((unsigned long)offset) + 2321; ++ ++ rc = s921_writeregdata(state, s921_prefreq); ++ if (rc < 0) ++ return rc; ++ ++ rc = s921_writereg(state, 0xf2, (f_offset >> 8) & 0xff); ++ if (rc < 0) ++ return rc; ++ ++ rc = s921_writereg(state, 0xf3, f_offset & 0xff); ++ if (rc < 0) ++ return rc; ++ ++ rc = s921_writereg(state, 0xf4, f_switch); ++ if (rc < 0) ++ return rc; ++ ++ rc = s921_writeregdata(state, s921_postfreq); ++ if (rc < 0) ++ return rc; ++ ++ for (i = 0 ; i < 6; i++) { ++ rc = s921_readreg(state, 0x80); ++ dprintk("status 0x80: %02x\n", rc); ++ } ++ rc = s921_writereg(state, 0x01, 0x40); ++ if (rc < 0) ++ return rc; ++ ++ rc = s921_readreg(state, 0x01); ++ dprintk("status 0x01: %02x\n", rc); ++ ++ rc = s921_readreg(state, 0x80); ++ dprintk("status 0x80: %02x\n", rc); ++ ++ rc = s921_readreg(state, 0x80); ++ dprintk("status 0x80: %02x\n", rc); ++ ++ rc = s921_readreg(state, 0x32); ++ dprintk("status 0x32: %02x\n", rc); ++ ++ dprintk("pll tune band=%d, pll=%d\n", f_switch, (int)f_offset); ++ ++ return 0; ++} ++ ++static int s921_initfe(struct dvb_frontend *fe) ++{ ++ struct s921_state *state = fe->demodulator_priv; ++ int rc; ++ ++ dprintk("\n"); ++ ++ rc = s921_writeregdata(state, s921_init); ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct s921_state *state = fe->demodulator_priv; ++ int regstatus, rc; ++ ++ *status = 0; ++ ++ rc = s921_readreg(state, 0x81); ++ if (rc < 0) ++ return rc; ++ ++ regstatus = rc << 8; ++ ++ rc = s921_readreg(state, 0x82); ++ if (rc < 0) ++ return rc; ++ ++ regstatus |= rc; ++ ++ dprintk("status = %04x\n", regstatus); ++ ++ /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */ ++ if ((regstatus & 0xff) == 0x40) { ++ *status = FE_HAS_SIGNAL | ++ FE_HAS_CARRIER | ++ FE_HAS_VITERBI | ++ FE_HAS_SYNC | ++ FE_HAS_LOCK; ++ } else if (regstatus & 0x40) { ++ /* This is close to Full Sync, but not enough to get useful info */ ++ *status = FE_HAS_SIGNAL | ++ FE_HAS_CARRIER | ++ FE_HAS_VITERBI | ++ FE_HAS_SYNC; ++ } ++ ++ return 0; ++} ++ ++static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ fe_status_t status; ++ struct s921_state *state = fe->demodulator_priv; ++ int rc; ++ ++ /* FIXME: Use the proper register for it... 0x80? */ ++ rc = s921_read_status(fe, &status); ++ if (rc < 0) ++ return rc; ++ ++ *strength = (status & FE_HAS_LOCK) ? 0xffff : 0; ++ ++ dprintk("strength = 0x%04x\n", *strength); ++ ++ rc = s921_readreg(state, 0x01); ++ dprintk("status 0x01: %02x\n", rc); ++ ++ rc = s921_readreg(state, 0x80); ++ dprintk("status 0x80: %02x\n", rc); ++ ++ rc = s921_readreg(state, 0x32); ++ dprintk("status 0x32: %02x\n", rc); ++ ++ return 0; ++} ++ ++static int s921_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s921_state *state = fe->demodulator_priv; ++ int rc; ++ ++ dprintk("\n"); ++ ++ /* FIXME: We don't know how to use non-auto mode */ ++ ++ rc = s921_pll_tune(fe); ++ if (rc < 0) ++ return rc; ++ ++ state->currentfreq = p->frequency; ++ ++ return 0; ++} ++ ++static int s921_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct s921_state *state = fe->demodulator_priv; ++ ++ /* FIXME: Probably it is possible to get it from regs f1 and f2 */ ++ p->frequency = state->currentfreq; ++ p->delivery_system = SYS_ISDBT; ++ ++ return 0; ++} ++ ++static int s921_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ int rc = 0; ++ ++ dprintk("\n"); ++ ++ if (re_tune) ++ rc = s921_set_frontend(fe); ++ ++ if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) ++ s921_read_status(fe, status); ++ ++ return rc; ++} ++ ++static int s921_get_algo(struct dvb_frontend *fe) ++{ ++ return 1; /* FE_ALGO_HW */ ++} ++ ++static void s921_release(struct dvb_frontend *fe) ++{ ++ struct s921_state *state = fe->demodulator_priv; ++ ++ dprintk("\n"); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops s921_ops; ++ ++struct dvb_frontend *s921_attach(const struct s921_config *config, ++ struct i2c_adapter *i2c) ++{ ++ /* allocate memory for the internal state */ ++ struct s921_state *state = ++ kzalloc(sizeof(struct s921_state), GFP_KERNEL); ++ ++ dprintk("\n"); ++ if (!state) { ++ rc("Unable to kzalloc\n"); ++ return NULL; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &s921_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++} ++EXPORT_SYMBOL(s921_attach); ++ ++static struct dvb_frontend_ops s921_ops = { ++ .delsys = { SYS_ISDBT }, ++ /* Use dib8000 values per default */ ++ .info = { ++ .name = "Sharp S921", ++ .frequency_min = 470000000, ++ /* ++ * Max should be 770MHz instead, according with Sharp docs, ++ * but Leadership doc says it works up to 806 MHz. This is ++ * required to get channel 69, used in Brazil ++ */ ++ .frequency_max = 806000000, ++ .frequency_tolerance = 0, ++ .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 = s921_release, ++ ++ .init = s921_initfe, ++ .set_frontend = s921_set_frontend, ++ .get_frontend = s921_get_frontend, ++ .read_status = s921_read_status, ++ .read_signal_strength = s921_read_signal_strength, ++ .tune = s921_tune, ++ .get_frontend_algo = s921_get_algo, ++}; ++ ++MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware"); ++MODULE_AUTHOR("Mauro Carvalho Chehab "); ++MODULE_AUTHOR("Douglas Landgraf "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/s921.h b/drivers/media/dvb-frontends/s921.h +new file mode 100644 +index 0000000..f220d82 +--- /dev/null ++++ b/drivers/media/dvb-frontends/s921.h +@@ -0,0 +1,47 @@ ++/* ++ * Sharp s921 driver ++ * ++ * Copyright (C) 2009 Mauro Carvalho Chehab ++ * Copyright (C) 2009 Douglas Landgraf ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#ifndef S921_H ++#define S921_H ++ ++#include ++ ++struct s921_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *s921_attach(const struct s921_config *config, ++ struct i2c_adapter *i2c); ++extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *); ++#else ++static inline struct dvb_frontend *s921_attach( ++ const struct s921_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static struct i2c_adapter * ++ s921_get_tuner_i2c_adapter(struct dvb_frontend *fe) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* S921_H */ +diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c +new file mode 100644 +index 0000000..73b47cc +--- /dev/null ++++ b/drivers/media/dvb-frontends/si21xx.c +@@ -0,0 +1,951 @@ ++/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator ++* ++* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License as published by ++* the Free Software Foundation; either version 2 of the License, or ++* (at your option) any later version. ++* ++*/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "si21xx.h" ++ ++#define REVISION_REG 0x00 ++#define SYSTEM_MODE_REG 0x01 ++#define TS_CTRL_REG_1 0x02 ++#define TS_CTRL_REG_2 0x03 ++#define PIN_CTRL_REG_1 0x04 ++#define PIN_CTRL_REG_2 0x05 ++#define LOCK_STATUS_REG_1 0x0f ++#define LOCK_STATUS_REG_2 0x10 ++#define ACQ_STATUS_REG 0x11 ++#define ACQ_CTRL_REG_1 0x13 ++#define ACQ_CTRL_REG_2 0x14 ++#define PLL_DIVISOR_REG 0x15 ++#define COARSE_TUNE_REG 0x16 ++#define FINE_TUNE_REG_L 0x17 ++#define FINE_TUNE_REG_H 0x18 ++ ++#define ANALOG_AGC_POWER_LEVEL_REG 0x28 ++#define CFO_ESTIMATOR_CTRL_REG_1 0x29 ++#define CFO_ESTIMATOR_CTRL_REG_2 0x2a ++#define CFO_ESTIMATOR_CTRL_REG_3 0x2b ++ ++#define SYM_RATE_ESTIMATE_REG_L 0x31 ++#define SYM_RATE_ESTIMATE_REG_M 0x32 ++#define SYM_RATE_ESTIMATE_REG_H 0x33 ++ ++#define CFO_ESTIMATOR_OFFSET_REG_L 0x36 ++#define CFO_ESTIMATOR_OFFSET_REG_H 0x37 ++#define CFO_ERROR_REG_L 0x38 ++#define CFO_ERROR_REG_H 0x39 ++#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a ++ ++#define SYM_RATE_REG_L 0x3f ++#define SYM_RATE_REG_M 0x40 ++#define SYM_RATE_REG_H 0x41 ++#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 ++#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 ++ ++#define C_N_ESTIMATOR_CTRL_REG 0x7c ++#define C_N_ESTIMATOR_THRSHLD_REG 0x7d ++#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e ++#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f ++ ++#define BLIND_SCAN_CTRL_REG 0x80 ++ ++#define LSA_CTRL_REG_1 0x8D ++#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f ++#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 ++#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 ++#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 ++#define INBAND_POWER_THRSHLD_REG 0x93 ++#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 ++ ++#define VIT_SRCH_CTRL_REG_1 0xa0 ++#define VIT_SRCH_CTRL_REG_2 0xa1 ++#define VIT_SRCH_CTRL_REG_3 0xa2 ++#define VIT_SRCH_STATUS_REG 0xa3 ++#define VITERBI_BER_COUNT_REG_L 0xab ++#define REED_SOLOMON_CTRL_REG 0xb0 ++#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 ++#define PRBS_CTRL_REG 0xb5 ++ ++#define LNB_CTRL_REG_1 0xc0 ++#define LNB_CTRL_REG_2 0xc1 ++#define LNB_CTRL_REG_3 0xc2 ++#define LNB_CTRL_REG_4 0xc3 ++#define LNB_CTRL_STATUS_REG 0xc4 ++#define LNB_FIFO_REGS_0 0xc5 ++#define LNB_FIFO_REGS_1 0xc6 ++#define LNB_FIFO_REGS_2 0xc7 ++#define LNB_FIFO_REGS_3 0xc8 ++#define LNB_FIFO_REGS_4 0xc9 ++#define LNB_FIFO_REGS_5 0xca ++#define LNB_SUPPLY_CTRL_REG_1 0xcb ++#define LNB_SUPPLY_CTRL_REG_2 0xcc ++#define LNB_SUPPLY_CTRL_REG_3 0xcd ++#define LNB_SUPPLY_CTRL_REG_4 0xce ++#define LNB_SUPPLY_STATUS_REG 0xcf ++ ++#define FAIL -1 ++#define PASS 0 ++ ++#define ALLOWABLE_FS_COUNT 10 ++#define STATUS_BER 0 ++#define STATUS_UCBLOCKS 1 ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "si21xx: " args); \ ++ } while (0) ++ ++enum { ++ ACTIVE_HIGH, ++ ACTIVE_LOW ++}; ++enum { ++ BYTE_WIDE, ++ BIT_WIDE ++}; ++enum { ++ CLK_GAPPED_MODE, ++ CLK_CONTINUOUS_MODE ++}; ++enum { ++ RISING_EDGE, ++ FALLING_EDGE ++}; ++enum { ++ MSB_FIRST, ++ LSB_FIRST ++}; ++enum { ++ SERIAL, ++ PARALLEL ++}; ++ ++struct si21xx_state { ++ struct i2c_adapter *i2c; ++ const struct si21xx_config *config; ++ struct dvb_frontend frontend; ++ u8 initialised:1; ++ int errmode; ++ int fs; /*Sampling rate of the ADC in MHz*/ ++}; ++ ++/* register default initialization */ ++static u8 serit_sp1511lhb_inittab[] = { ++ 0x01, 0x28, /* set i2c_inc_disable */ ++ 0x20, 0x03, ++ 0x27, 0x20, ++ 0xe0, 0x45, ++ 0xe1, 0x08, ++ 0xfe, 0x01, ++ 0x01, 0x28, ++ 0x89, 0x09, ++ 0x04, 0x80, ++ 0x05, 0x01, ++ 0x06, 0x00, ++ 0x20, 0x03, ++ 0x24, 0x88, ++ 0x29, 0x09, ++ 0x2a, 0x0f, ++ 0x2c, 0x10, ++ 0x2d, 0x19, ++ 0x2e, 0x08, ++ 0x2f, 0x10, ++ 0x30, 0x19, ++ 0x34, 0x20, ++ 0x35, 0x03, ++ 0x45, 0x02, ++ 0x46, 0x45, ++ 0x47, 0xd0, ++ 0x48, 0x00, ++ 0x49, 0x40, ++ 0x4a, 0x03, ++ 0x4c, 0xfd, ++ 0x4f, 0x2e, ++ 0x50, 0x2e, ++ 0x51, 0x10, ++ 0x52, 0x10, ++ 0x56, 0x92, ++ 0x59, 0x00, ++ 0x5a, 0x2d, ++ 0x5b, 0x33, ++ 0x5c, 0x1f, ++ 0x5f, 0x76, ++ 0x62, 0xc0, ++ 0x63, 0xc0, ++ 0x64, 0xf3, ++ 0x65, 0xf3, ++ 0x79, 0x40, ++ 0x6a, 0x40, ++ 0x6b, 0x0a, ++ 0x6c, 0x80, ++ 0x6d, 0x27, ++ 0x71, 0x06, ++ 0x75, 0x60, ++ 0x78, 0x00, ++ 0x79, 0xb5, ++ 0x7c, 0x05, ++ 0x7d, 0x1a, ++ 0x87, 0x55, ++ 0x88, 0x72, ++ 0x8f, 0x08, ++ 0x90, 0xe0, ++ 0x94, 0x40, ++ 0xa0, 0x3f, ++ 0xa1, 0xc0, ++ 0xa4, 0xcc, ++ 0xa5, 0x66, ++ 0xa6, 0x66, ++ 0xa7, 0x7b, ++ 0xa8, 0x7b, ++ 0xa9, 0x7b, ++ 0xaa, 0x9a, ++ 0xed, 0x04, ++ 0xad, 0x00, ++ 0xae, 0x03, ++ 0xcc, 0xab, ++ 0x01, 0x08, ++ 0xff, 0xff ++}; ++ ++/* low level read/writes */ ++static int si21_writeregs(struct si21xx_state *state, u8 reg1, ++ u8 *data, int len) ++{ ++ int ret; ++ u8 buf[60];/* = { reg1, data };*/ ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = len + 1 ++ }; ++ ++ msg.buf[0] = reg1; ++ memcpy(msg.buf + 1, data, len); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, " ++ "ret == %i)\n", __func__, reg1, data[0], ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ if (len != 2) ++ return -EINVAL; ++ ++ return si21_writereg(state, buf[0], buf[1]); ++} ++ ++static u8 si21_readreg(struct si21xx_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", ++ __func__, reg, ret); ++ ++ return b1[0]; ++} ++ ++static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) ++{ ++ int ret; ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = ®1, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b, ++ .len = len ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk("%s: readreg error (ret == %i)\n", __func__, ret); ++ ++ return ret == 2 ? 0 : -1; ++} ++ ++static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) ++{ ++ unsigned long start = jiffies; ++ ++ dprintk("%s\n", __func__); ++ ++ while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { ++ if (jiffies - start > timeout) { ++ dprintk("%s: timeout!!\n", __func__); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u32 sym_rate, data_rate; ++ int i; ++ u8 sym_rate_bytes[3]; ++ ++ dprintk("%s : srate = %i\n", __func__ , srate); ++ ++ if ((srate < 1000000) || (srate > 45000000)) ++ return -EINVAL; ++ ++ data_rate = srate; ++ sym_rate = 0; ++ ++ for (i = 0; i < 4; ++i) { ++ sym_rate /= 100; ++ sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / ++ state->fs; ++ data_rate /= 100; ++ } ++ for (i = 0; i < 3; ++i) ++ sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); ++ ++ si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); ++ ++ return 0; ++} ++ ++static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *m) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 lnb_status; ++ u8 LNB_CTRL_1; ++ int status; ++ ++ dprintk("%s\n", __func__); ++ ++ status = PASS; ++ LNB_CTRL_1 = 0; ++ ++ status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); ++ status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); ++ ++ /*fill the FIFO*/ ++ status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); ++ ++ LNB_CTRL_1 = (lnb_status & 0x70); ++ LNB_CTRL_1 |= m->msg_len; ++ ++ LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ ++ ++ status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); ++ ++ return status; ++} ++ ++static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("%s\n", __func__); ++ ++ if (si21xx_wait_diseqc_idle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ val = (0x80 | si21_readreg(state, 0xc1)); ++ if (si21_writereg(state, LNB_CTRL_REG_1, ++ burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) ++ return -EREMOTEIO; ++ ++ if (si21xx_wait_diseqc_idle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ if (si21_writereg(state, LNB_CTRL_REG_1, val)) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++/* 30.06.2008 */ ++static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk("%s\n", __func__); ++ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); ++ ++ case SEC_TONE_OFF: ++ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ u8 val; ++ dprintk("%s: %s\n", __func__, ++ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : ++ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); ++ ++ ++ val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); ++ ++ switch (volt) { ++ case SEC_VOLTAGE_18: ++ return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); ++ break; ++ case SEC_VOLTAGE_13: ++ return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); ++ break; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int si21xx_init(struct dvb_frontend *fe) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ int i; ++ int status = 0; ++ u8 reg1; ++ u8 val; ++ u8 reg2[2]; ++ ++ dprintk("%s\n", __func__); ++ ++ for (i = 0; ; i += 2) { ++ reg1 = serit_sp1511lhb_inittab[i]; ++ val = serit_sp1511lhb_inittab[i+1]; ++ if (reg1 == 0xff && val == 0xff) ++ break; ++ si21_writeregs(state, reg1, &val, 1); ++ } ++ ++ /*DVB QPSK SYSTEM MODE REG*/ ++ reg1 = 0x08; ++ si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); ++ ++ /*transport stream config*/ ++ /* ++ mode = PARALLEL; ++ sdata_form = LSB_FIRST; ++ clk_edge = FALLING_EDGE; ++ clk_mode = CLK_GAPPED_MODE; ++ strt_len = BYTE_WIDE; ++ sync_pol = ACTIVE_HIGH; ++ val_pol = ACTIVE_HIGH; ++ err_pol = ACTIVE_HIGH; ++ sclk_rate = 0x00; ++ parity = 0x00 ; ++ data_delay = 0x00; ++ clk_delay = 0x00; ++ pclk_smooth = 0x00; ++ */ ++ reg2[0] = ++ PARALLEL + (LSB_FIRST << 1) ++ + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) ++ + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) ++ + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); ++ ++ reg2[1] = 0; ++ /* sclk_rate + (parity << 2) ++ + (data_delay << 3) + (clk_delay << 4) ++ + (pclk_smooth << 5); ++ */ ++ status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); ++ if (status != 0) ++ dprintk(" %s : TS Set Error\n", __func__); ++ ++ return 0; ++ ++} ++ ++static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 regs_read[2]; ++ u8 reg_read; ++ u8 i; ++ u8 lock; ++ u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); ++ ++ si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); ++ reg_read = 0; ++ ++ for (i = 0; i < 7; ++i) ++ reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); ++ ++ lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); ++ ++ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); ++ *status = 0; ++ ++ if (signal > 10) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (lock & 0x2) ++ *status |= FE_HAS_CARRIER; ++ ++ if (lock & 0x20) ++ *status |= FE_HAS_VITERBI; ++ ++ if (lock & 0x40) ++ *status |= FE_HAS_SYNC; ++ ++ if ((lock & 0x7b) == 0x7b) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, ++ (u8*)agclevel, 0x01);*/ ++ ++ u16 signal = (3 * si21_readreg(state, 0x27) * ++ si21_readreg(state, 0x28)); ++ ++ dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, ++ si21_readreg(state, 0x27), ++ si21_readreg(state, 0x28), (int) signal); ++ ++ signal <<= 4; ++ *strength = signal; ++ ++ return 0; ++} ++ ++static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (state->errmode != STATUS_BER) ++ return 0; ++ ++ *ber = (si21_readreg(state, 0x1d) << 8) | ++ si21_readreg(state, 0x1e); ++ ++ return 0; ++} ++ ++static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | ++ si21_readreg(state, 0x25)); ++ xsnr = 3 * (xsnr - 0xa100); ++ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; ++ ++ dprintk("%s\n", __func__); ++ ++ return 0; ++} ++ ++static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (state->errmode != STATUS_UCBLOCKS) ++ *ucblocks = 0; ++ else ++ *ucblocks = (si21_readreg(state, 0x1d) << 8) | ++ si21_readreg(state, 0x1e); ++ ++ return 0; ++} ++ ++/* initiates a channel acquisition sequence ++ using the specified symbol rate and code rate */ ++static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, ++ fe_code_rate_t crate) ++{ ++ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 coderates[] = { ++ 0x0, 0x01, 0x02, 0x04, 0x00, ++ 0x8, 0x10, 0x20, 0x00, 0x3f ++ }; ++ ++ u8 coderate_ptr; ++ int status; ++ u8 start_acq = 0x80; ++ u8 reg, regs[3]; ++ ++ dprintk("%s\n", __func__); ++ ++ status = PASS; ++ coderate_ptr = coderates[crate]; ++ ++ si21xx_set_symbolrate(fe, symbrate); ++ ++ /* write code rates to use in the Viterbi search */ ++ status |= si21_writeregs(state, ++ VIT_SRCH_CTRL_REG_1, ++ &coderate_ptr, 0x01); ++ ++ /* clear acq_start bit */ ++ status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); ++ reg &= ~start_acq; ++ status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); ++ ++ /* use new Carrier Frequency Offset Estimator (QuickLock) */ ++ regs[0] = 0xCB; ++ regs[1] = 0x40; ++ regs[2] = 0xCB; ++ ++ status |= si21_writeregs(state, ++ TWO_DB_BNDWDTH_THRSHLD_REG, ++ ®s[0], 0x03); ++ reg = 0x56; ++ status |= si21_writeregs(state, ++ LSA_CTRL_REG_1, ®, 1); ++ reg = 0x05; ++ status |= si21_writeregs(state, ++ BLIND_SCAN_CTRL_REG, ®, 1); ++ /* start automatic acq */ ++ status |= si21_writeregs(state, ++ ACQ_CTRL_REG_2, &start_acq, 0x01); ++ ++ return status; ++} ++ ++static int si21xx_set_frontend(struct dvb_frontend *fe) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) ++ datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ ++ ++ /* in MHz */ ++ unsigned char coarse_tune_freq; ++ int fine_tune_freq; ++ unsigned char sample_rate = 0; ++ /* boolean */ ++ bool inband_interferer_ind; ++ ++ /* INTERMEDIATE VALUES */ ++ int icoarse_tune_freq; /* MHz */ ++ int ifine_tune_freq; /* MHz */ ++ unsigned int band_high; ++ unsigned int band_low; ++ unsigned int x1; ++ unsigned int x2; ++ int i; ++ bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; ++ bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; ++ int status; ++ ++ /* allowable sample rates for ADC in MHz */ ++ int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, ++ 196, 204, 205, 206, 207 ++ }; ++ /* in MHz */ ++ int if_limit_high; ++ int if_limit_low; ++ int lnb_lo; ++ int lnb_uncertanity; ++ ++ int rf_freq; ++ int data_rate; ++ unsigned char regs[4]; ++ ++ dprintk("%s : FE_SET_FRONTEND\n", __func__); ++ ++ if (c->delivery_system != SYS_DVBS) { ++ dprintk("%s: unsupported delivery system selected (%d)\n", ++ __func__, c->delivery_system); ++ return -EOPNOTSUPP; ++ } ++ ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) ++ inband_interferer_div2[i] = inband_interferer_div4[i] = false; ++ ++ if_limit_high = -700000; ++ if_limit_low = -100000; ++ /* in MHz */ ++ lnb_lo = 0; ++ lnb_uncertanity = 0; ++ ++ rf_freq = 10 * c->frequency ; ++ data_rate = c->symbol_rate / 100; ++ ++ status = PASS; ++ ++ band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) ++ + (data_rate * 135)) / 200; ++ ++ band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) ++ + (data_rate * 135)) / 200; ++ ++ ++ icoarse_tune_freq = 100000 * ++ (((rf_freq - lnb_lo) - ++ (if_limit_low + if_limit_high) / 2) ++ / 100000); ++ ++ ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; ++ ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { ++ x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * ++ (afs[i] * 2500) + afs[i] * 2500; ++ ++ x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * ++ (afs[i] * 2500); ++ ++ if (((band_low < x1) && (x1 < band_high)) || ++ ((band_low < x2) && (x2 < band_high))) ++ inband_interferer_div4[i] = true; ++ ++ } ++ ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { ++ x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * ++ (afs[i] * 5000) + afs[i] * 5000; ++ ++ x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * ++ (afs[i] * 5000); ++ ++ if (((band_low < x1) && (x1 < band_high)) || ++ ((band_low < x2) && (x2 < band_high))) ++ inband_interferer_div2[i] = true; ++ } ++ ++ inband_interferer_ind = true; ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { ++ if (inband_interferer_div2[i] || inband_interferer_div4[i]) { ++ inband_interferer_ind = false; ++ break; ++ } ++ } ++ ++ if (inband_interferer_ind) { ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { ++ if (!inband_interferer_div2[i]) { ++ sample_rate = (u8) afs[i]; ++ break; ++ } ++ } ++ } else { ++ for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { ++ if ((inband_interferer_div2[i] || ++ !inband_interferer_div4[i])) { ++ sample_rate = (u8) afs[i]; ++ break; ++ } ++ } ++ ++ } ++ ++ if (sample_rate > 207 || sample_rate < 192) ++ sample_rate = 200; ++ ++ fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / ++ ((sample_rate) * 1000)); ++ ++ coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); ++ ++ regs[0] = sample_rate; ++ regs[1] = coarse_tune_freq; ++ regs[2] = fine_tune_freq & 0xFF; ++ regs[3] = fine_tune_freq >> 8 & 0xFF; ++ ++ status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); ++ ++ state->fs = sample_rate;/*ADC MHz*/ ++ si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); ++ ++ return 0; ++} ++ ++static int si21xx_sleep(struct dvb_frontend *fe) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ u8 regdata; ++ ++ dprintk("%s\n", __func__); ++ ++ si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); ++ regdata |= 1 << 6; ++ si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); ++ state->initialised = 0; ++ ++ return 0; ++} ++ ++static void si21xx_release(struct dvb_frontend *fe) ++{ ++ struct si21xx_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops si21xx_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "SL SI21XX DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, /* ppm */ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_QPSK | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = si21xx_release, ++ .init = si21xx_init, ++ .sleep = si21xx_sleep, ++ .write = si21_write, ++ .read_status = si21_read_status, ++ .read_ber = si21_read_ber, ++ .read_signal_strength = si21_read_signal_strength, ++ .read_snr = si21_read_snr, ++ .read_ucblocks = si21_read_ucblocks, ++ .diseqc_send_master_cmd = si21xx_send_diseqc_msg, ++ .diseqc_send_burst = si21xx_send_diseqc_burst, ++ .set_tone = si21xx_set_tone, ++ .set_voltage = si21xx_set_voltage, ++ ++ .set_frontend = si21xx_set_frontend, ++}; ++ ++struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct si21xx_state *state = NULL; ++ int id; ++ ++ dprintk("%s\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ state->errmode = STATUS_BER; ++ ++ /* check if the demod is there */ ++ id = si21_readreg(state, SYSTEM_MODE_REG); ++ si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ ++ msleep(200); ++ id = si21_readreg(state, 0x00); ++ ++ /* register 0x00 contains: ++ 0x34 for SI2107 ++ 0x24 for SI2108 ++ 0x14 for SI2109 ++ 0x04 for SI2110 ++ */ ++ if (id != 0x04 && id != 0x14) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &si21xx_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(si21xx_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); ++MODULE_AUTHOR("Igor M. Liplianin"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/si21xx.h b/drivers/media/dvb-frontends/si21xx.h +new file mode 100644 +index 0000000..141b5b8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/si21xx.h +@@ -0,0 +1,37 @@ ++#ifndef SI21XX_H ++#define SI21XX_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct si21xx_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* minimum delay before retuning */ ++ int min_delay_ms; ++}; ++ ++#if defined(CONFIG_DVB_SI21XX) || \ ++ (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *si21xx_attach( ++ const struct si21xx_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val) ++{ ++ int r = 0; ++ u8 buf[] = {reg, val}; ++ if (fe->ops.write) ++ r = fe->ops.write(fe, buf, 2); ++ return r; ++} ++ ++#endif +diff --git a/drivers/media/dvb-frontends/sp8870.c b/drivers/media/dvb-frontends/sp8870.c +new file mode 100644 +index 0000000..2aa8ef7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/sp8870.c +@@ -0,0 +1,620 @@ ++/* ++ Driver for Spase SP8870 demodulator ++ ++ Copyright (C) 1999 Juergen Peitz ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++/* ++ * This driver needs external firmware. Please use the command ++ * "/Documentation/dvb/get_dvb_firmware alps_tdlb7" to ++ * download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ * or /lib/firmware (depending on configuration of firmware hotplug). ++ */ ++#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "sp8870.h" ++ ++ ++struct sp8870_state { ++ ++ struct i2c_adapter* i2c; ++ ++ const struct sp8870_config* config; ++ ++ struct dvb_frontend frontend; ++ ++ /* demodulator private data */ ++ u8 initialised:1; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "sp8870: " args); \ ++ } while (0) ++ ++/* firmware size for sp8870 */ ++#define SP8870_FIRMWARE_SIZE 16382 ++ ++/* starting point for firmware in file 'Sc_main.mc' */ ++#define SP8870_FIRMWARE_OFFSET 0x0A ++ ++static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) ++{ ++ u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; ++ int err; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int sp8870_readreg (struct sp8870_state* state, u16 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg >> 8 , reg & 0xff }; ++ u8 b1 [] = { 0, 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ dprintk("%s: readreg error (ret == %i)\n", __func__, ret); ++ return -1; ++ } ++ ++ return (b1[0] << 8 | b1[1]); ++} ++ ++static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) ++{ ++ struct i2c_msg msg; ++ const char *fw_buf = fw->data; ++ int fw_pos; ++ u8 tx_buf[255]; ++ int tx_len; ++ int err = 0; ++ ++ dprintk ("%s: ...\n", __func__); ++ ++ if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) ++ return -EINVAL; ++ ++ // system controller stop ++ sp8870_writereg(state, 0x0F00, 0x0000); ++ ++ // instruction RAM register hiword ++ sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); ++ ++ // instruction RAM MWR ++ sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); ++ ++ // do firmware upload ++ fw_pos = SP8870_FIRMWARE_OFFSET; ++ while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ ++ tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; ++ // write register 0xCF0A ++ tx_buf[0] = 0xCF; ++ tx_buf[1] = 0x0A; ++ memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.buf = tx_buf; ++ msg.len = tx_len + 2; ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ printk("%s: firmware upload failed!\n", __func__); ++ printk ("%s: i2c error (err == %i)\n", __func__, err); ++ return err; ++ } ++ fw_pos += tx_len; ++ } ++ ++ dprintk ("%s: done!\n", __func__); ++ return 0; ++}; ++ ++static void sp8870_microcontroller_stop (struct sp8870_state* state) ++{ ++ sp8870_writereg(state, 0x0F08, 0x000); ++ sp8870_writereg(state, 0x0F09, 0x000); ++ ++ // microcontroller STOP ++ sp8870_writereg(state, 0x0F00, 0x000); ++} ++ ++static void sp8870_microcontroller_start (struct sp8870_state* state) ++{ ++ sp8870_writereg(state, 0x0F08, 0x000); ++ sp8870_writereg(state, 0x0F09, 0x000); ++ ++ // microcontroller START ++ sp8870_writereg(state, 0x0F00, 0x001); ++ // not documented but if we don't read 0x0D01 out here ++ // we don't get a correct data valid signal ++ sp8870_readreg(state, 0x0D01); ++} ++ ++static int sp8870_read_data_valid_signal(struct sp8870_state* state) ++{ ++ return (sp8870_readreg(state, 0x0D02) > 0); ++} ++ ++static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) ++{ ++ int known_parameters = 1; ++ ++ *reg0xc05 = 0x000; ++ ++ switch (p->modulation) { ++ case QPSK: ++ break; ++ case QAM_16: ++ *reg0xc05 |= (1 << 10); ++ break; ++ case QAM_64: ++ *reg0xc05 |= (2 << 10); ++ break; ++ case QAM_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (p->hierarchy) { ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ *reg0xc05 |= (1 << 7); ++ break; ++ case HIERARCHY_2: ++ *reg0xc05 |= (2 << 7); ++ break; ++ case HIERARCHY_4: ++ *reg0xc05 |= (3 << 7); ++ break; ++ case HIERARCHY_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (p->code_rate_HP) { ++ case FEC_1_2: ++ break; ++ case FEC_2_3: ++ *reg0xc05 |= (1 << 3); ++ break; ++ case FEC_3_4: ++ *reg0xc05 |= (2 << 3); ++ break; ++ case FEC_5_6: ++ *reg0xc05 |= (3 << 3); ++ break; ++ case FEC_7_8: ++ *reg0xc05 |= (4 << 3); ++ break; ++ case FEC_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (known_parameters) ++ *reg0xc05 |= (2 << 1); /* use specified parameters */ ++ else ++ *reg0xc05 |= (1 << 1); /* enable autoprobing */ ++ ++ return 0; ++} ++ ++static int sp8870_wake_up(struct sp8870_state* state) ++{ ++ // enable TS output and interface pins ++ return sp8870_writereg(state, 0xC18, 0x00D); ++} ++ ++static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct sp8870_state* state = fe->demodulator_priv; ++ int err; ++ u16 reg0xc05; ++ ++ if ((err = configure_reg0xc05(p, ®0xc05))) ++ return err; ++ ++ // system controller stop ++ sp8870_microcontroller_stop(state); ++ ++ // set tuner parameters ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ // sample rate correction bit [23..17] ++ sp8870_writereg(state, 0x0319, 0x000A); ++ ++ // sample rate correction bit [16..0] ++ sp8870_writereg(state, 0x031A, 0x0AAB); ++ ++ // integer carrier offset ++ sp8870_writereg(state, 0x0309, 0x0400); ++ ++ // fractional carrier offset ++ sp8870_writereg(state, 0x030A, 0x0000); ++ ++ // filter for 6/7/8 Mhz channel ++ if (p->bandwidth_hz == 6000000) ++ sp8870_writereg(state, 0x0311, 0x0002); ++ else if (p->bandwidth_hz == 7000000) ++ sp8870_writereg(state, 0x0311, 0x0001); ++ else ++ sp8870_writereg(state, 0x0311, 0x0000); ++ ++ // scan order: 2k first = 0x0000, 8k first = 0x0001 ++ if (p->transmission_mode == TRANSMISSION_MODE_2K) ++ sp8870_writereg(state, 0x0338, 0x0000); ++ else ++ sp8870_writereg(state, 0x0338, 0x0001); ++ ++ sp8870_writereg(state, 0xc05, reg0xc05); ++ ++ // read status reg in order to clear pending irqs ++ sp8870_readreg(state, 0x200); ++ ++ // system controller start ++ sp8870_microcontroller_start(state); ++ ++ return 0; ++} ++ ++static int sp8870_init (struct dvb_frontend* fe) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ const struct firmware *fw = NULL; ++ ++ sp8870_wake_up(state); ++ if (state->initialised) return 0; ++ state->initialised = 1; ++ ++ dprintk ("%s\n", __func__); ++ ++ ++ /* request the firmware, this will block until someone uploads it */ ++ printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); ++ if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { ++ printk("sp8870: no firmware upload (timeout or file not found?)\n"); ++ return -EIO; ++ } ++ ++ if (sp8870_firmware_upload(state, fw)) { ++ printk("sp8870: writing firmware to device failed\n"); ++ release_firmware(fw); ++ return -EIO; ++ } ++ release_firmware(fw); ++ printk("sp8870: firmware upload complete\n"); ++ ++ /* enable TS output and interface pins */ ++ sp8870_writereg(state, 0xc18, 0x00d); ++ ++ // system controller stop ++ sp8870_microcontroller_stop(state); ++ ++ // ADC mode ++ sp8870_writereg(state, 0x0301, 0x0003); ++ ++ // Reed Solomon parity bytes passed to output ++ sp8870_writereg(state, 0x0C13, 0x0001); ++ ++ // MPEG clock is suppressed if no valid data ++ sp8870_writereg(state, 0x0C14, 0x0001); ++ ++ /* bit 0x010: enable data valid signal */ ++ sp8870_writereg(state, 0x0D00, 0x010); ++ sp8870_writereg(state, 0x0D01, 0x000); ++ ++ return 0; ++} ++ ++static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ int status; ++ int signal; ++ ++ *fe_status = 0; ++ ++ status = sp8870_readreg (state, 0x0200); ++ if (status < 0) ++ return -EIO; ++ ++ signal = sp8870_readreg (state, 0x0303); ++ if (signal < 0) ++ return -EIO; ++ ++ if (signal > 0x0F) ++ *fe_status |= FE_HAS_SIGNAL; ++ if (status & 0x08) ++ *fe_status |= FE_HAS_SYNC; ++ if (status & 0x04) ++ *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; ++ ++ return 0; ++} ++ ++static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ int ret; ++ u32 tmp; ++ ++ *ber = 0; ++ ++ ret = sp8870_readreg(state, 0xC08); ++ if (ret < 0) ++ return -EIO; ++ ++ tmp = ret & 0x3F; ++ ++ ret = sp8870_readreg(state, 0xC07); ++ if (ret < 0) ++ return -EIO; ++ ++ tmp = ret << 6; ++ ++ if (tmp >= 0x3FFF0) ++ tmp = ~0; ++ ++ *ber = tmp; ++ ++ return 0; ++} ++ ++static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ int ret; ++ u16 tmp; ++ ++ *signal = 0; ++ ++ ret = sp8870_readreg (state, 0x306); ++ if (ret < 0) ++ return -EIO; ++ ++ tmp = ret << 8; ++ ++ ret = sp8870_readreg (state, 0x303); ++ if (ret < 0) ++ return -EIO; ++ ++ tmp |= ret; ++ ++ if (tmp) ++ *signal = 0xFFFF - tmp; ++ ++ return 0; ++} ++ ++static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ int ret; ++ ++ *ublocks = 0; ++ ++ ret = sp8870_readreg(state, 0xC0C); ++ if (ret < 0) ++ return -EIO; ++ ++ if (ret == 0xFFFF) ++ ret = ~0; ++ ++ *ublocks = ret; ++ ++ return 0; ++} ++ ++/* number of trials to recover from lockup */ ++#define MAXTRIALS 5 ++/* maximum checks for data valid signal */ ++#define MAXCHECKS 100 ++ ++/* only for debugging: counter for detected lockups */ ++static int lockups; ++/* only for debugging: counter for channel switches */ ++static int switches; ++ ++static int sp8870_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct sp8870_state* state = fe->demodulator_priv; ++ ++ /* ++ The firmware of the sp8870 sometimes locks up after setting frontend parameters. ++ We try to detect this by checking the data valid signal. ++ If it is not set after MAXCHECKS we try to recover the lockup by setting ++ the frontend parameters again. ++ */ ++ ++ int err = 0; ++ int valid = 0; ++ int trials = 0; ++ int check_count = 0; ++ ++ dprintk("%s: frequency = %i\n", __func__, p->frequency); ++ ++ for (trials = 1; trials <= MAXTRIALS; trials++) { ++ ++ err = sp8870_set_frontend_parameters(fe); ++ if (err) ++ return err; ++ ++ for (check_count = 0; check_count < MAXCHECKS; check_count++) { ++// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); ++ valid = sp8870_read_data_valid_signal(state); ++ if (valid) { ++ dprintk("%s: delay = %i usec\n", ++ __func__, check_count * 10); ++ break; ++ } ++ udelay(10); ++ } ++ if (valid) ++ break; ++ } ++ ++ if (!valid) { ++ printk("%s: firmware crash!!!!!!\n", __func__); ++ return -EIO; ++ } ++ ++ if (debug) { ++ if (valid) { ++ if (trials > 1) { ++ printk("%s: firmware lockup!!!\n", __func__); ++ printk("%s: recovered after %i trial(s))\n", __func__, trials - 1); ++ lockups++; ++ } ++ } ++ switches++; ++ printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups); ++ } ++ ++ return 0; ++} ++ ++static int sp8870_sleep(struct dvb_frontend* fe) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ ++ // tristate TS output and disable interface pins ++ return sp8870_writereg(state, 0xC18, 0x000); ++} ++ ++static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 350; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return sp8870_writereg(state, 0x206, 0x001); ++ } else { ++ return sp8870_writereg(state, 0x206, 0x000); ++ } ++} ++ ++static void sp8870_release(struct dvb_frontend* fe) ++{ ++ struct sp8870_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops sp8870_ops; ++ ++struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct sp8870_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ ++ /* check if the demod is there */ ++ if (sp8870_readreg(state, 0x0200) < 0) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops sp8870_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Spase SP8870 DVB-T", ++ .frequency_min = 470000000, ++ .frequency_max = 860000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | ++ FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER ++ }, ++ ++ .release = sp8870_release, ++ ++ .init = sp8870_init, ++ .sleep = sp8870_sleep, ++ .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, ++ ++ .set_frontend = sp8870_set_frontend, ++ .get_tune_settings = sp8870_get_tune_settings, ++ ++ .read_status = sp8870_read_status, ++ .read_ber = sp8870_read_ber, ++ .read_signal_strength = sp8870_read_signal_strength, ++ .read_ucblocks = sp8870_read_uncorrected_blocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); ++MODULE_AUTHOR("Juergen Peitz"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(sp8870_attach); +diff --git a/drivers/media/dvb-frontends/sp8870.h b/drivers/media/dvb-frontends/sp8870.h +new file mode 100644 +index 0000000..a764a79 +--- /dev/null ++++ b/drivers/media/dvb-frontends/sp8870.h +@@ -0,0 +1,50 @@ ++/* ++ Driver for Spase SP8870 demodulator ++ ++ Copyright (C) 1999 Juergen Peitz ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef SP8870_H ++#define SP8870_H ++ ++#include ++#include ++ ++struct sp8870_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* request firmware for device */ ++ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); ++}; ++ ++#if defined(CONFIG_DVB_SP8870) || (defined(CONFIG_DVB_SP8870_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_SP8870 ++ ++#endif // SP8870_H +diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c +new file mode 100644 +index 0000000..1bb81b5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/sp887x.c +@@ -0,0 +1,629 @@ ++/* ++ Driver for the Spase sp887x demodulator ++*/ ++ ++/* ++ * This driver needs external firmware. Please use the command ++ * "/Documentation/dvb/get_dvb_firmware sp887x" to ++ * download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ * or /lib/firmware (depending on configuration of firmware hotplug). ++ */ ++#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "sp887x.h" ++ ++ ++struct sp887x_state { ++ struct i2c_adapter* i2c; ++ const struct sp887x_config* config; ++ struct dvb_frontend frontend; ++ ++ /* demodulator private data */ ++ u8 initialised:1; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "sp887x: " args); \ ++ } while (0) ++ ++static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len) ++{ ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len }; ++ int err; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ printk ("%s: i2c write error (addr %02x, err == %i)\n", ++ __func__, state->config->demod_address, err); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data) ++{ ++ u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 }; ++ int ret; ++ ++ if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { ++ /** ++ * in case of soft reset we ignore ACK errors... ++ */ ++ if (!(reg == 0xf1a && data == 0x000 && ++ (ret == -EREMOTEIO || ret == -EFAULT))) ++ { ++ printk("%s: writereg error " ++ "(reg %03x, data %03x, ret == %i)\n", ++ __func__, reg & 0xffff, data & 0xffff, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int sp887x_readreg (struct sp887x_state* state, u16 reg) ++{ ++ u8 b0 [] = { reg >> 8 , reg & 0xff }; ++ u8 b1 [2]; ++ int ret; ++ struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; ++ ++ if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { ++ printk("%s: readreg error (ret == %i)\n", __func__, ret); ++ return -1; ++ } ++ ++ return (((b1[0] << 8) | b1[1]) & 0xfff); ++} ++ ++static void sp887x_microcontroller_stop (struct sp887x_state* state) ++{ ++ dprintk("%s\n", __func__); ++ sp887x_writereg(state, 0xf08, 0x000); ++ sp887x_writereg(state, 0xf09, 0x000); ++ ++ /* microcontroller STOP */ ++ sp887x_writereg(state, 0xf00, 0x000); ++} ++ ++static void sp887x_microcontroller_start (struct sp887x_state* state) ++{ ++ dprintk("%s\n", __func__); ++ sp887x_writereg(state, 0xf08, 0x000); ++ sp887x_writereg(state, 0xf09, 0x000); ++ ++ /* microcontroller START */ ++ sp887x_writereg(state, 0xf00, 0x001); ++} ++ ++static void sp887x_setup_agc (struct sp887x_state* state) ++{ ++ /* setup AGC parameters */ ++ dprintk("%s\n", __func__); ++ sp887x_writereg(state, 0x33c, 0x054); ++ sp887x_writereg(state, 0x33b, 0x04c); ++ sp887x_writereg(state, 0x328, 0x000); ++ sp887x_writereg(state, 0x327, 0x005); ++ sp887x_writereg(state, 0x326, 0x001); ++ sp887x_writereg(state, 0x325, 0x001); ++ sp887x_writereg(state, 0x324, 0x001); ++ sp887x_writereg(state, 0x318, 0x050); ++ sp887x_writereg(state, 0x317, 0x3fe); ++ sp887x_writereg(state, 0x316, 0x001); ++ sp887x_writereg(state, 0x313, 0x005); ++ sp887x_writereg(state, 0x312, 0x002); ++ sp887x_writereg(state, 0x306, 0x000); ++ sp887x_writereg(state, 0x303, 0x000); ++} ++ ++#define BLOCKSIZE 30 ++#define FW_SIZE 0x4000 ++/** ++ * load firmware and setup MPEG interface... ++ */ ++static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ u8 buf [BLOCKSIZE+2]; ++ int i; ++ int fw_size = fw->size; ++ const unsigned char *mem = fw->data; ++ ++ dprintk("%s\n", __func__); ++ ++ /* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */ ++ if (fw_size < FW_SIZE+10) ++ return -ENODEV; ++ ++ mem = fw->data + 10; ++ ++ /* soft reset */ ++ sp887x_writereg(state, 0xf1a, 0x000); ++ ++ sp887x_microcontroller_stop (state); ++ ++ printk ("%s: firmware upload... ", __func__); ++ ++ /* setup write pointer to -1 (end of memory) */ ++ /* bit 0x8000 in address is set to enable 13bit mode */ ++ sp887x_writereg(state, 0x8f08, 0x1fff); ++ ++ /* dummy write (wrap around to start of memory) */ ++ sp887x_writereg(state, 0x8f0a, 0x0000); ++ ++ for (i = 0; i < FW_SIZE; i += BLOCKSIZE) { ++ int c = BLOCKSIZE; ++ int err; ++ ++ if (i+c > FW_SIZE) ++ c = FW_SIZE - i; ++ ++ /* bit 0x8000 in address is set to enable 13bit mode */ ++ /* bit 0x4000 enables multibyte read/write transfers */ ++ /* write register is 0xf0a */ ++ buf[0] = 0xcf; ++ buf[1] = 0x0a; ++ ++ memcpy(&buf[2], mem + i, c); ++ ++ if ((err = i2c_writebytes (state, buf, c+2)) < 0) { ++ printk ("failed.\n"); ++ printk ("%s: i2c error (err == %i)\n", __func__, err); ++ return err; ++ } ++ } ++ ++ /* don't write RS bytes between packets */ ++ sp887x_writereg(state, 0xc13, 0x001); ++ ++ /* suppress clock if (!data_valid) */ ++ sp887x_writereg(state, 0xc14, 0x000); ++ ++ /* setup MPEG interface... */ ++ sp887x_writereg(state, 0xc1a, 0x872); ++ sp887x_writereg(state, 0xc1b, 0x001); ++ sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ ++ sp887x_writereg(state, 0xc1a, 0x871); ++ ++ /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */ ++ sp887x_writereg(state, 0x301, 0x002); ++ ++ sp887x_setup_agc(state); ++ ++ /* bit 0x010: enable data valid signal */ ++ sp887x_writereg(state, 0xd00, 0x010); ++ sp887x_writereg(state, 0x0d1, 0x000); ++ return 0; ++}; ++ ++static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) ++{ ++ int known_parameters = 1; ++ ++ *reg0xc05 = 0x000; ++ ++ switch (p->modulation) { ++ case QPSK: ++ break; ++ case QAM_16: ++ *reg0xc05 |= (1 << 10); ++ break; ++ case QAM_64: ++ *reg0xc05 |= (2 << 10); ++ break; ++ case QAM_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (p->hierarchy) { ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ *reg0xc05 |= (1 << 7); ++ break; ++ case HIERARCHY_2: ++ *reg0xc05 |= (2 << 7); ++ break; ++ case HIERARCHY_4: ++ *reg0xc05 |= (3 << 7); ++ break; ++ case HIERARCHY_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (p->code_rate_HP) { ++ case FEC_1_2: ++ break; ++ case FEC_2_3: ++ *reg0xc05 |= (1 << 3); ++ break; ++ case FEC_3_4: ++ *reg0xc05 |= (2 << 3); ++ break; ++ case FEC_5_6: ++ *reg0xc05 |= (3 << 3); ++ break; ++ case FEC_7_8: ++ *reg0xc05 |= (4 << 3); ++ break; ++ case FEC_AUTO: ++ known_parameters = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (known_parameters) ++ *reg0xc05 |= (2 << 1); /* use specified parameters */ ++ else ++ *reg0xc05 |= (1 << 1); /* enable autoprobing */ ++ ++ return 0; ++} ++ ++/** ++ * estimates division of two 24bit numbers, ++ * derived from the ves1820/stv0299 driver code ++ */ ++static void divide (int n, int d, int *quotient_i, int *quotient_f) ++{ ++ unsigned int q, r; ++ ++ r = (n % d) << 8; ++ q = (r / d); ++ ++ if (quotient_i) ++ *quotient_i = q; ++ ++ if (quotient_f) { ++ r = (r % d) << 8; ++ q = (q << 8) | (r / d); ++ r = (r % d) << 8; ++ *quotient_f = (q << 8) | (r / d); ++ } ++} ++ ++static void sp887x_correct_offsets (struct sp887x_state* state, ++ struct dtv_frontend_properties *p, ++ int actual_freq) ++{ ++ static const u32 srate_correction [] = { 1879617, 4544878, 8098561 }; ++ int bw_index; ++ int freq_offset = actual_freq - p->frequency; ++ int sysclock = 61003; //[kHz] ++ int ifreq = 36000000; ++ int freq; ++ int frequency_shift; ++ ++ switch (p->bandwidth_hz) { ++ default: ++ case 8000000: ++ bw_index = 0; ++ break; ++ case 7000000: ++ bw_index = 1; ++ break; ++ case 6000000: ++ bw_index = 2; ++ break; ++ } ++ ++ if (p->inversion == INVERSION_ON) ++ freq = ifreq - freq_offset; ++ else ++ freq = ifreq + freq_offset; ++ ++ divide(freq / 333, sysclock, NULL, &frequency_shift); ++ ++ if (p->inversion == INVERSION_ON) ++ frequency_shift = -frequency_shift; ++ ++ /* sample rate correction */ ++ sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12); ++ sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff); ++ ++ /* carrier offset correction */ ++ sp887x_writereg(state, 0x309, frequency_shift >> 12); ++ sp887x_writereg(state, 0x30a, frequency_shift & 0xfff); ++} ++ ++static int sp887x_setup_frontend_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct sp887x_state* state = fe->demodulator_priv; ++ unsigned actual_freq; ++ int err; ++ u16 val, reg0xc05; ++ ++ if (p->bandwidth_hz != 8000000 && ++ p->bandwidth_hz != 7000000 && ++ p->bandwidth_hz != 6000000) ++ return -EINVAL; ++ ++ if ((err = configure_reg0xc05(p, ®0xc05))) ++ return err; ++ ++ sp887x_microcontroller_stop(state); ++ ++ /* setup the PLL */ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ if (fe->ops.tuner_ops.get_frequency) { ++ fe->ops.tuner_ops.get_frequency(fe, &actual_freq); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } else { ++ actual_freq = p->frequency; ++ } ++ ++ /* read status reg in order to clear bandwidth_hz == 6000000) ++ val = 2; ++ else if (p->bandwidth_hz == 7000000) ++ val = 1; ++ else ++ val = 0; ++ ++ sp887x_writereg(state, 0x311, val); ++ ++ /* scan order: 2k first = 0, 8k first = 1 */ ++ if (p->transmission_mode == TRANSMISSION_MODE_2K) ++ sp887x_writereg(state, 0x338, 0x000); ++ else ++ sp887x_writereg(state, 0x338, 0x001); ++ ++ sp887x_writereg(state, 0xc05, reg0xc05); ++ ++ if (p->bandwidth_hz == 6000000) ++ val = 2 << 3; ++ else if (p->bandwidth_hz == 7000000) ++ val = 3 << 3; ++ else ++ val = 0 << 3; ++ ++ /* enable OFDM and SAW bits as lock indicators in sync register 0xf17, ++ * optimize algorithm for given bandwidth... ++ */ ++ sp887x_writereg(state, 0xf14, 0x160 | val); ++ sp887x_writereg(state, 0xf15, 0x000); ++ ++ sp887x_microcontroller_start(state); ++ return 0; ++} ++ ++static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ u16 snr12 = sp887x_readreg(state, 0xf16); ++ u16 sync0x200 = sp887x_readreg(state, 0x200); ++ u16 sync0xf17 = sp887x_readreg(state, 0xf17); ++ ++ *status = 0; ++ ++ if (snr12 > 0x00f) ++ *status |= FE_HAS_SIGNAL; ++ ++ //if (sync0x200 & 0x004) ++ // *status |= FE_HAS_SYNC | FE_HAS_CARRIER; ++ ++ //if (sync0x200 & 0x008) ++ // *status |= FE_HAS_VITERBI; ++ ++ if ((sync0xf17 & 0x00f) == 0x002) { ++ *status |= FE_HAS_LOCK; ++ *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER; ++ } ++ ++ if (sync0x200 & 0x001) { /* tuner adjustment requested...*/ ++ int steps = (sync0x200 >> 4) & 0x00f; ++ if (steps & 0x008) ++ steps = -steps; ++ dprintk("sp887x: implement tuner adjustment (%+i steps)!!\n", ++ steps); ++ } ++ ++ return 0; ++} ++ ++static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ *ber = (sp887x_readreg(state, 0xc08) & 0x3f) | ++ (sp887x_readreg(state, 0xc07) << 6); ++ sp887x_writereg(state, 0xc08, 0x000); ++ sp887x_writereg(state, 0xc07, 0x000); ++ if (*ber >= 0x3fff0) ++ *ber = ~0; ++ ++ return 0; ++} ++ ++static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ u16 snr12 = sp887x_readreg(state, 0xf16); ++ u32 signal = 3 * (snr12 << 4); ++ *strength = (signal < 0xffff) ? signal : 0xffff; ++ ++ return 0; ++} ++ ++static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ u16 snr12 = sp887x_readreg(state, 0xf16); ++ *snr = (snr12 << 4) | (snr12 >> 8); ++ ++ return 0; ++} ++ ++static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ *ucblocks = sp887x_readreg(state, 0xc0c); ++ if (*ucblocks == 0xfff) ++ *ucblocks = ~0; ++ ++ return 0; ++} ++ ++static int sp887x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return sp887x_writereg(state, 0x206, 0x001); ++ } else { ++ return sp887x_writereg(state, 0x206, 0x000); ++ } ++} ++ ++static int sp887x_sleep(struct dvb_frontend* fe) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ ++ /* tristate TS output and disable interface pins */ ++ sp887x_writereg(state, 0xc18, 0x000); ++ ++ return 0; ++} ++ ++static int sp887x_init(struct dvb_frontend* fe) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ const struct firmware *fw = NULL; ++ int ret; ++ ++ if (!state->initialised) { ++ /* request the firmware, this will block until someone uploads it */ ++ printk("sp887x: waiting for firmware upload (%s)...\n", SP887X_DEFAULT_FIRMWARE); ++ ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE); ++ if (ret) { ++ printk("sp887x: no firmware upload (timeout or file not found?)\n"); ++ return ret; ++ } ++ ++ ret = sp887x_initial_setup(fe, fw); ++ release_firmware(fw); ++ if (ret) { ++ printk("sp887x: writing firmware to device failed\n"); ++ return ret; ++ } ++ printk("sp887x: firmware upload complete\n"); ++ state->initialised = 1; ++ } ++ ++ /* enable TS output and interface pins */ ++ sp887x_writereg(state, 0xc18, 0x00d); ++ ++ return 0; ++} ++ ++static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 350; ++ fesettings->step_size = 166666*2; ++ fesettings->max_drift = (166666*2)+1; ++ return 0; ++} ++ ++static void sp887x_release(struct dvb_frontend* fe) ++{ ++ struct sp887x_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops sp887x_ops; ++ ++struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct sp887x_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ ++ /* check if the demod is there */ ++ if (sp887x_readreg(state, 0x0200) < 0) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &sp887x_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops sp887x_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Spase SP887x DVB-T", ++ .frequency_min = 50500000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | ++ FE_CAN_RECOVER ++ }, ++ ++ .release = sp887x_release, ++ ++ .init = sp887x_init, ++ .sleep = sp887x_sleep, ++ .i2c_gate_ctrl = sp887x_i2c_gate_ctrl, ++ ++ .set_frontend = sp887x_setup_frontend_parameters, ++ .get_tune_settings = sp887x_get_tune_settings, ++ ++ .read_status = sp887x_read_status, ++ .read_ber = sp887x_read_ber, ++ .read_signal_strength = sp887x_read_signal_strength, ++ .read_snr = sp887x_read_snr, ++ .read_ucblocks = sp887x_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(sp887x_attach); +diff --git a/drivers/media/dvb-frontends/sp887x.h b/drivers/media/dvb-frontends/sp887x.h +new file mode 100644 +index 0000000..04eff6e +--- /dev/null ++++ b/drivers/media/dvb-frontends/sp887x.h +@@ -0,0 +1,32 @@ ++/* ++ Driver for the Spase sp887x demodulator ++*/ ++ ++#ifndef SP887X_H ++#define SP887X_H ++ ++#include ++#include ++ ++struct sp887x_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* request firmware for device */ ++ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); ++}; ++ ++#if defined(CONFIG_DVB_SP887X) || (defined(CONFIG_DVB_SP887X_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_SP887X ++ ++#endif // SP887X_H +diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c +new file mode 100644 +index 0000000..117a569 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_algo.c +@@ -0,0 +1,1525 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ 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 "stb0899_drv.h" ++#include "stb0899_priv.h" ++#include "stb0899_reg.h" ++ ++static inline u32 stb0899_do_div(u64 n, u32 d) ++{ ++ /* wrap do_div() for ease of use */ ++ ++ do_div(n, d); ++ return n; ++} ++ ++#if 0 ++/* These functions are currently unused */ ++/* ++ * stb0899_calc_srate ++ * Compute symbol rate ++ */ ++static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) ++{ ++ u64 tmp; ++ ++ /* srate = (SFR * master_clk) >> 20 */ ++ ++ /* sfr is of size 20 bit, stored with an offset of 4 bit */ ++ tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2]; ++ tmp &= ~0xf; ++ tmp *= master_clk; ++ tmp >>= 24; ++ ++ return tmp; ++} ++ ++/* ++ * stb0899_get_srate ++ * Get the current symbol rate ++ */ ++static u32 stb0899_get_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 sfr[3]; ++ ++ stb0899_read_regs(state, STB0899_SFRH, sfr, 3); ++ ++ return stb0899_calc_srate(internal->master_clk, sfr); ++} ++#endif ++ ++/* ++ * stb0899_set_srate ++ * Set symbol frequency ++ * MasterClock: master clock frequency (hz) ++ * SymbolRate: symbol rate (bauds) ++ * return symbol frequency ++ */ ++static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate) ++{ ++ u32 tmp; ++ u8 sfr[3]; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "-->"); ++ /* ++ * in order to have the maximum precision, the symbol rate entered into ++ * the chip is computed as the closest value of the "true value". ++ * In this purpose, the symbol rate value is rounded (1 is added on the bit ++ * below the LSB ) ++ * ++ * srate = (SFR * master_clk) >> 20 ++ * <=> ++ * SFR = srate << 20 / master_clk ++ * ++ * rounded: ++ * SFR = (srate << 21 + master_clk) / (2 * master_clk) ++ * ++ * stored as 20 bit number with an offset of 4 bit: ++ * sfr = SFR << 4; ++ */ ++ ++ tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); ++ tmp <<= 4; ++ ++ sfr[0] = tmp >> 16; ++ sfr[1] = tmp >> 8; ++ sfr[2] = tmp; ++ ++ stb0899_write_regs(state, STB0899_SFRH, sfr, 3); ++ ++ return srate; ++} ++ ++/* ++ * stb0899_calc_derot_time ++ * Compute the amount of time needed by the derotator to lock ++ * SymbolRate: Symbol rate ++ * return: derotator time constant (ms) ++ */ ++static long stb0899_calc_derot_time(long srate) ++{ ++ if (srate > 0) ++ return (100000 / (srate / 1000)); ++ else ++ return 0; ++} ++ ++/* ++ * stb0899_carr_width ++ * Compute the width of the carrier ++ * return: width of carrier (kHz or Mhz) ++ */ ++long stb0899_carr_width(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ return (internal->srate + (internal->srate * internal->rolloff) / 100); ++} ++ ++/* ++ * stb0899_first_subrange ++ * Compute the first subrange of the search ++ */ ++static void stb0899_first_subrange(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ struct stb0899_config *config = state->config; ++ ++ int range = 0; ++ u32 bandwidth = 0; ++ ++ if (config->tuner_get_bandwidth) { ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ config->tuner_get_bandwidth(&state->frontend, &bandwidth); ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ range = bandwidth - stb0899_carr_width(state) / 2; ++ } ++ ++ if (range > 0) ++ internal->sub_range = min(internal->srch_range, range); ++ else ++ internal->sub_range = 0; ++ ++ internal->freq = params->freq; ++ internal->tuner_offst = 0L; ++ internal->sub_dir = 1; ++} ++ ++/* ++ * stb0899_check_tmg ++ * check for timing lock ++ * internal.Ttiming: time to wait for loop lock ++ */ ++static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ int lock; ++ u8 reg; ++ s8 timing; ++ ++ msleep(internal->t_derot); ++ ++ stb0899_write_reg(state, STB0899_RTF, 0xf2); ++ reg = stb0899_read_reg(state, STB0899_TLIR); ++ lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg); ++ timing = stb0899_read_reg(state, STB0899_RTF); ++ ++ if (lock >= 42) { ++ if ((lock > 48) && (abs(timing) >= 110)) { ++ internal->status = ANALOGCARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !"); ++ } else { ++ internal->status = TIMINGOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !"); ++ } ++ } else { ++ internal->status = NOTIMING; ++ dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !"); ++ } ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_tmg ++ * perform a fs/2 zig-zag to find timing ++ */ ++static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; ++ int index = 0; ++ u8 cfr[2]; ++ ++ internal->status = NOTIMING; ++ ++ /* timing loop computation & symbol rate optimisation */ ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_step = (params->srate / 2L) / internal->mclk; ++ ++ while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { ++ index++; ++ derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ ++ ++ if (abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ } ++ internal->direction = -internal->direction; /* Change zigzag direction */ ++ } ++ ++ if (internal->status == TIMINGOK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_carrier ++ * Check for carrier found ++ */ ++static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 reg; ++ ++ msleep(internal->t_derot); /* wait for derotator ok */ ++ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ reg = stb0899_read_reg(state, STB0899_DSTATUS); ++ dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg); ++ if (STB0899_GETFIELD(CARRIER_FOUND, reg)) { ++ internal->status = CARRIEROK; ++ dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !"); ++ } else { ++ internal->status = NOCARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_carrier ++ * Search for a QPSK carrier with the derotator ++ */ ++static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; ++ int index = 0; ++ u8 cfr[2]; ++ u8 reg; ++ ++ internal->status = NOCARRIER; ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_freq = internal->derot_freq; ++ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ do { ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); ++ if (stb0899_check_carrier(state) == NOCARRIER) { ++ index++; ++ last_derot_freq = derot_freq; ++ derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ ++ ++ if(abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ } ++ } ++ ++ internal->direction = -internal->direction; /* Change zigzag direction */ ++ } while ((internal->status != CARRIEROK) && next_loop); ++ ++ if (internal->status == CARRIEROK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq); ++ } else { ++ internal->derot_freq = last_derot_freq; ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_data ++ * Check for data found ++ */ ++static enum stb0899_status stb0899_check_data(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ int lock = 0, index = 0, dataTime = 500, loop; ++ u8 reg; ++ ++ internal->status = NODATA; ++ ++ /* RESET FEC */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESACS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ msleep(1); ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESACS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ if (params->srate <= 2000000) ++ dataTime = 2000; ++ else if (params->srate <= 5000000) ++ dataTime = 1500; ++ else if (params->srate <= 15000000) ++ dataTime = 1000; ++ else ++ dataTime = 500; ++ ++ /* clear previous failed END_LOOPVIT */ ++ stb0899_read_reg(state, STB0899_VSTATUS); ++ ++ stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ ++ while (1) { ++ /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); ++ loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg); ++ ++ if (lock || loop || (index > dataTime)) ++ break; ++ index++; ++ } ++ ++ if (lock) { /* DATA LOCK indicator */ ++ internal->status = DATAOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_search_data ++ * Search for a QPSK carrier with the derotator ++ */ ++static enum stb0899_status stb0899_search_data(struct stb0899_state *state) ++{ ++ short int derot_freq, derot_step, derot_limit, next_loop = 3; ++ u8 cfr[2]; ++ u8 reg; ++ int index = 1; ++ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ derot_step = (params->srate / 4L) / internal->mclk; ++ derot_limit = (internal->sub_range / 2L) / internal->mclk; ++ derot_freq = internal->derot_freq; ++ ++ do { ++ if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { ++ ++ derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ ++ if (abs(derot_freq) > derot_limit) ++ next_loop--; ++ ++ if (next_loop) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk); ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); ++ STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ ++ ++ stb0899_check_carrier(state); ++ index++; ++ } ++ } ++ internal->direction = -internal->direction; /* change zig zag direction */ ++ } while ((internal->status != DATAOK) && next_loop); ++ ++ if (internal->status == DATAOK) { ++ stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ ++ internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); ++ dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_check_range ++ * check if the found frequency is in the correct range ++ */ ++static enum stb0899_status stb0899_check_range(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ int range_offst, tp_freq; ++ ++ range_offst = internal->srch_range / 2000; ++ tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000; ++ ++ if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) { ++ internal->status = RANGEOK; ++ dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !"); ++ } else { ++ internal->status = OUTOFRANGE; ++ dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !"); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * NextSubRange ++ * Compute the next subrange of the search ++ */ ++static void next_sub_range(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_params *params = &state->params; ++ ++ long old_sub_range; ++ ++ if (internal->sub_dir > 0) { ++ old_sub_range = internal->sub_range; ++ internal->sub_range = min((internal->srch_range / 2) - ++ (internal->tuner_offst + internal->sub_range / 2), ++ internal->sub_range); ++ ++ if (internal->sub_range < 0) ++ internal->sub_range = 0; ++ ++ internal->tuner_offst += (old_sub_range + internal->sub_range) / 2; ++ } ++ ++ internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000; ++ internal->sub_dir = -internal->sub_dir; ++} ++ ++/* ++ * stb0899_dvbs_algo ++ * Search for a signal, timing, carrier and data for a ++ * given frequency in a given range ++ */ ++enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state) ++{ ++ struct stb0899_params *params = &state->params; ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u8 bclc, reg; ++ u8 cfr[2]; ++ u8 eq_const[10]; ++ s32 clnI = 3; ++ u32 bandwidth = 0; ++ ++ /* BETA values rated @ 99MHz */ ++ s32 betaTab[5][4] = { ++ /* 5 10 20 30MBps */ ++ { 37, 34, 32, 31 }, /* QPSK 1/2 */ ++ { 37, 35, 33, 31 }, /* QPSK 2/3 */ ++ { 37, 35, 33, 31 }, /* QPSK 3/4 */ ++ { 37, 36, 33, 32 }, /* QPSK 5/6 */ ++ { 37, 36, 33, 32 } /* QPSK 7/8 */ ++ }; ++ ++ internal->direction = 1; ++ ++ stb0899_set_srate(state, internal->master_clk, params->srate); ++ /* Carrier loop optimization versus symbol rate for acquisition*/ ++ if (params->srate <= 5000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0x89); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x1c); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 0; ++ } else if (params->srate <= 15000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0xc9); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x22); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 1; ++ } else if(params->srate <= 25000000) { ++ stb0899_write_reg(state, STB0899_ACLC, 0x89); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x27); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 2; ++ } else { ++ stb0899_write_reg(state, STB0899_ACLC, 0xc8); ++ bclc = stb0899_read_reg(state, STB0899_BCLC); ++ STB0899_SETFIELD_VAL(BETA, bclc, 0x29); ++ stb0899_write_reg(state, STB0899_BCLC, bclc); ++ clnI = 3; ++ } ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition"); ++ /* Set the timing loop to acquisition */ ++ stb0899_write_reg(state, STB0899_RTC, 0x46); ++ stb0899_write_reg(state, STB0899_CFD, 0xee); ++ ++ /* !! WARNING !! ++ * Do not read any status variables while acquisition, ++ * If any needed, read before the acquisition starts ++ * querying status while acquiring causes the ++ * acquisition to go bad and hence no locks. ++ */ ++ dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d", ++ internal->derot_percent, params->srate, internal->mclk); ++ ++ /* Initial calculations */ ++ internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */ ++ internal->t_derot = stb0899_calc_derot_time(params->srate); ++ internal->t_data = 500; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger"); ++ /* RESET Stream merger */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* ++ * Set KDIVIDER to an intermediate value between ++ * 1/2 and 7/8 for acquisition ++ */ ++ reg = stb0899_read_reg(state, STB0899_DEMAPVIT); ++ STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60); ++ stb0899_write_reg(state, STB0899_DEMAPVIT, reg); ++ ++ stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */ ++ stb0899_write_reg(state, STB0899_VITSYNC, 0x19); ++ ++ stb0899_first_subrange(state); ++ do { ++ /* Initialisations */ ++ cfr[0] = cfr[1] = 0; ++ stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */ ++ ++ stb0899_write_reg(state, STB0899_RTF, 0); ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ internal->derot_freq = 0; ++ internal->status = NOAGC1; ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ /* Move tuner to frequency */ ++ dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency"); ++ if (state->config->tuner_set_frequency) ++ state->config->tuner_set_frequency(&state->frontend, internal->freq); ++ ++ if (state->config->tuner_get_frequency) ++ state->config->tuner_get_frequency(&state->frontend, &internal->freq); ++ ++ msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */ ++ dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq); ++ internal->status = AGC1OK; ++ ++ /* There is signal in the band */ ++ if (config->tuner_get_bandwidth) ++ config->tuner_get_bandwidth(&state->frontend, &bandwidth); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ if (params->srate <= bandwidth / 2) ++ stb0899_search_tmg(state); /* For low rates (SCPC) */ ++ else ++ stb0899_check_tmg(state); /* For high rates (MCPC) */ ++ ++ if (internal->status == TIMINGOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "TIMING OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "CARRIER OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_search_data(state) == DATAOK) { /* Check for data */ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "DATA OK ! Derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ if (stb0899_check_range(state) == RANGEOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "RANGE OK ! derot freq=%d, mclk=%d", ++ internal->derot_freq, internal->mclk); ++ ++ internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000); ++ reg = stb0899_read_reg(state, STB0899_PLPARM); ++ internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg); ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "freq=%d, internal resultant freq=%d", ++ params->freq, internal->freq); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "internal puncture rate=%d", ++ internal->fecrate); ++ } ++ } ++ } ++ } ++ if (internal->status != RANGEOK) ++ next_sub_range(state); ++ ++ } while (internal->sub_range && internal->status != RANGEOK); ++ ++ /* Set the timing loop to tracking */ ++ stb0899_write_reg(state, STB0899_RTC, 0x33); ++ stb0899_write_reg(state, STB0899_CFD, 0xf7); ++ /* if locked and range ok, set Kdiv */ ++ if (internal->status == RANGEOK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !"); ++ stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */ ++ stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */ ++ ++ /* ++ * Carrier loop optimization versus ++ * symbol Rate/Puncture Rate for Tracking ++ */ ++ reg = stb0899_read_reg(state, STB0899_BCLC); ++ switch (internal->fecrate) { ++ case STB0899_FEC_1_2: /* 13 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_2_3: /* 18 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 44); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_3_4: /* 21 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 60); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_5_6: /* 24 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 75); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ case STB0899_FEC_6_7: /* 25 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 88); ++ stb0899_write_reg(state, STB0899_ACLC, 0x88); ++ stb0899_write_reg(state, STB0899_BCLC, 0x9a); ++ break; ++ case STB0899_FEC_7_8: /* 26 */ ++ stb0899_write_reg(state, STB0899_DEMAPVIT, 94); ++ STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]); ++ stb0899_write_reg(state, STB0899_BCLC, reg); ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate"); ++ break; ++ } ++ /* release stream merger RESET */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* disable carrier detector */ ++ reg = stb0899_read_reg(state, STB0899_CFD); ++ STB0899_SETFIELD_VAL(CFD_ON, reg, 0); ++ stb0899_write_reg(state, STB0899_CFD, reg); ++ ++ stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10); ++ } ++ ++ return internal->status; ++} ++ ++/* ++ * stb0899_dvbs2_config_uwp ++ * Configure UWP state machine ++ */ ++static void stb0899_dvbs2_config_uwp(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ u32 uwp1, uwp2, uwp3, reg; ++ ++ uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); ++ uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2); ++ uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3); ++ ++ STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave); ++ STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant); ++ STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof); ++ ++ STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse); ++ STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine); ++ STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold); ++ ++ STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq); ++ STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO); ++ STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg); ++} ++ ++/* ++ * stb0899_dvbs2_config_csm_auto ++ * Set CSM to AUTO mode ++ */ ++static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) ++{ ++ u32 reg; ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); ++} ++ ++static long Log2Int(int number) ++{ ++ int i; ++ ++ i = 0; ++ while ((1 << i) <= abs(number)) ++ i++; ++ ++ if (number == 0) ++ i = 1; ++ ++ return i - 1; ++} ++ ++/* ++ * stb0899_dvbs2_calc_srate ++ * compute BTR_NOM_FREQ for the symbol rate ++ */ ++static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq; ++ u32 master_clk, srate; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ decim = 1 << dec_rate; ++ master_clk = internal->master_clk / 1000; ++ srate = internal->srate / 1000; ++ ++ if (decim <= 4) { ++ intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk; ++ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; ++ } else { ++ intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100; ++ remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; ++ } ++ btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk); ++ ++ return btr_nom_freq; ++} ++ ++/* ++ * stb0899_dvbs2_calc_dev ++ * compute the correction to be applied to symbol rate ++ */ ++static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u32 dec_ratio, correction, master_clk, srate; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ ++ master_clk = internal->master_clk / 1000; /* for integer Caculation*/ ++ srate = internal->srate / 1000; /* for integer Caculation*/ ++ correction = (512 * master_clk) / (2 * dec_ratio * srate); ++ ++ return correction; ++} ++ ++/* ++ * stb0899_dvbs2_set_srate ++ * Set DVBS2 symbol rate ++ */ ++static void stb0899_dvbs2_set_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq; ++ u32 correction, freq_adj, band_lim, decim_cntrl, reg; ++ u8 anti_alias; ++ ++ /*set decimation to 1*/ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ ++ win_sel = 0; ++ if (dec_rate >= 5) ++ win_sel = dec_rate - 4; ++ ++ decim = (1 << dec_rate); ++ /* (FSamp/Fsymbol *100) for integer Caculation */ ++ f_sym = internal->master_clk / ((decim * internal->srate) / 1000); ++ ++ if (f_sym <= 2250) /* don't band limit signal going into btr block*/ ++ band_lim = 1; ++ else ++ band_lim = 0; /* band limit signal going into btr block*/ ++ ++ decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl); ++ ++ if (f_sym <= 3450) ++ anti_alias = 0; ++ else if (f_sym <= 4250) ++ anti_alias = 1; ++ else ++ anti_alias = 2; ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias); ++ btr_nom_freq = stb0899_dvbs2_calc_srate(state); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq); ++ ++ correction = stb0899_dvbs2_calc_dev(state); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); ++ STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); ++ ++ /* scale UWP+CSM frequency to sample rate*/ ++ freq_adj = internal->srate / (internal->master_clk / 4096); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj); ++} ++ ++/* ++ * stb0899_dvbs2_set_btr_loopbw ++ * set bit timing loop bandwidth as a percentage of the symbol rate ++ */ ++static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 sym_peak = 23, zeta = 707, loopbw_percent = 60; ++ s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft; ++ s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift; ++ u32 decim, K, wn, k_direct, k_indirect; ++ u32 reg; ++ ++ dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); ++ dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; ++ dec_rate = Log2Int(dec_ratio); ++ decim = (1 << dec_rate); ++ ++ sym_peak *= 576000; ++ K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000); ++ K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/ ++ ++ if (K != 0) { ++ K = sym_peak / K; ++ wn = (4 * zeta * zeta) + 1000000; ++ wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/ ++ ++ k_indirect = (wn * wn) / K; ++ k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/ ++ k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/ ++ k_direct *= 100; ++ ++ k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2; ++ k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset; ++ k_btr1 = k_direct / (1 << k_direct_shift); ++ k_btr1 /= 10000; ++ ++ k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/; ++ k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset; ++ k_btr0 = k_indirect * (1 << (-k_indirect_shift)); ++ k_btr0 /= 1000000; ++ ++ k_btr2_rshft = 0; ++ if (k_btr0_rshft > 15) { ++ k_btr2_rshft = k_btr0_rshft - 15; ++ k_btr0_rshft = 15; ++ } ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN); ++ STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft); ++ STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0); ++ STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft); ++ STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1); ++ STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg); ++ } else ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f); ++} ++ ++/* ++ * stb0899_dvbs2_set_carr_freq ++ * set nominal frequency for carrier search ++ */ ++static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk) ++{ ++ struct stb0899_config *config = state->config; ++ s32 crl_nom_freq; ++ u32 reg; ++ ++ crl_nom_freq = (1 << config->crl_nco_bits) / master_clk; ++ crl_nom_freq *= carr_freq; ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++} ++ ++/* ++ * stb0899_dvbs2_init_calc ++ * Initialize DVBS2 UWP, CSM, carrier and timing loops ++ */ ++static void stb0899_dvbs2_init_calc(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ s32 steps, step_size; ++ u32 range, reg; ++ ++ /* config uwp and csm */ ++ stb0899_dvbs2_config_uwp(state); ++ stb0899_dvbs2_config_csm_auto(state); ++ ++ /* initialize BTR */ ++ stb0899_dvbs2_set_srate(state); ++ stb0899_dvbs2_set_btr_loopbw(state); ++ ++ if (internal->srate / 1000000 >= 15) ++ step_size = (1 << 17) / 5; ++ else if (internal->srate / 1000000 >= 10) ++ step_size = (1 << 17) / 7; ++ else if (internal->srate / 1000000 >= 5) ++ step_size = (1 << 17) / 10; ++ else ++ step_size = (1 << 17) / 4; ++ ++ range = internal->srch_range / 1000000; ++ steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000)); ++ steps = (steps + 6) / 10; ++ steps = (steps == 0) ? 1 : steps; ++ if (steps % 2 == 0) ++ stb0899_dvbs2_set_carr_freq(state, internal->center_freq - ++ (internal->step_size * (internal->srate / 20000000)), ++ (internal->master_clk) / 1000000); ++ else ++ stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000); ++ ++ /*Set Carrier Search params (zigzag, num steps and freq step size*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2); ++ STB0899_SETFIELD_VAL(ZIGZAG, reg, 1); ++ STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps); ++ STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg); ++} ++ ++/* ++ * stb0899_dvbs2_btr_init ++ * initialize the timing loop ++ */ ++static void stb0899_dvbs2_btr_init(struct stb0899_state *state) ++{ ++ u32 reg; ++ ++ /* set enable BTR loopback */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); ++ STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1); ++ STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); ++ ++ /* fix btr freq accum at 0 */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000); ++ ++ /* fix btr freq accum at 0 */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000); ++} ++ ++/* ++ * stb0899_dvbs2_reacquire ++ * trigger a DVB-S2 acquisition ++ */ ++static void stb0899_dvbs2_reacquire(struct stb0899_state *state) ++{ ++ u32 reg = 0; ++ ++ /* demod soft reset */ ++ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); ++ ++ /*Reset Timing Loop */ ++ stb0899_dvbs2_btr_init(state); ++ ++ /* reset Carrier loop */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30)); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30)); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0); ++ ++ /*release demod soft reset */ ++ reg = 0; ++ STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); ++ ++ /* start acquisition process */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0); ++ ++ /* equalizer Init */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1); ++ ++ /*Start equilizer */ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0); ++ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0); ++ STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05); ++ STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ ++ /* RESET Packet delineator */ ++ stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a); ++} ++ ++/* ++ * stb0899_dvbs2_get_dmd_status ++ * get DVB-S2 Demod LOCK status ++ */ ++static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout) ++{ ++ int time = -10, lock = 0, uwp, csm; ++ u32 reg; ++ ++ do { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS); ++ dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg); ++ if (STB0899_GETFIELD(IF_AGC_LOCK, reg)) ++ dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !"); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); ++ dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg); ++ uwp = STB0899_GETFIELD(UWP_LOCK, reg); ++ csm = STB0899_GETFIELD(CSM_LOCK, reg); ++ if (uwp && csm) ++ lock = 1; ++ ++ time += 10; ++ msleep(10); ++ ++ } while ((!lock) && (time <= timeout)); ++ ++ if (lock) { ++ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !"); ++ return DVBS2_DEMOD_LOCK; ++ } else { ++ return DVBS2_DEMOD_NOLOCK; ++ } ++} ++ ++/* ++ * stb0899_dvbs2_get_data_lock ++ * get FEC status ++ */ ++static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout) ++{ ++ int time = 0, lock = 0; ++ u8 reg; ++ ++ while ((!lock) && (time < timeout)) { ++ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); ++ dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg); ++ lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg); ++ time++; ++ } ++ ++ return lock; ++} ++ ++/* ++ * stb0899_dvbs2_get_fec_status ++ * get DVB-S2 FEC LOCK status ++ */ ++static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout) ++{ ++ int time = 0, Locked; ++ ++ do { ++ Locked = stb0899_dvbs2_get_data_lock(state, 1); ++ time++; ++ msleep(1); ++ ++ } while ((!Locked) && (time < timeout)); ++ ++ if (Locked) { ++ dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !"); ++ return DVBS2_FEC_LOCK; ++ } else { ++ return DVBS2_FEC_NOLOCK; ++ } ++} ++ ++ ++/* ++ * stb0899_dvbs2_init_csm ++ * set parameters for manual mode ++ */ ++static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ ++ s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80; ++ s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr; ++ u32 csm1, csm2, csm3, csm4; ++ ++ if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) { ++ switch (modcod) { ++ case STB0899_QPSK_12: ++ gamma_acq = 25; ++ gamma_rho_acq = 2700; ++ gamma_trk = 12; ++ gamma_rho_trk = 180; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_35: ++ gamma_acq = 38; ++ gamma_rho_acq = 7182; ++ gamma_trk = 14; ++ gamma_rho_trk = 308; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_23: ++ gamma_acq = 42; ++ gamma_rho_acq = 9408; ++ gamma_trk = 17; ++ gamma_rho_trk = 476; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_34: ++ gamma_acq = 53; ++ gamma_rho_acq = 16642; ++ gamma_trk = 19; ++ gamma_rho_trk = 646; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_45: ++ gamma_acq = 53; ++ gamma_rho_acq = 17119; ++ gamma_trk = 22; ++ gamma_rho_trk = 880; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_56: ++ gamma_acq = 55; ++ gamma_rho_acq = 19250; ++ gamma_trk = 23; ++ gamma_rho_trk = 989; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_89: ++ gamma_acq = 60; ++ gamma_rho_acq = 24240; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ case STB0899_QPSK_910: ++ gamma_acq = 66; ++ gamma_rho_acq = 29634; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ default: ++ gamma_acq = 66; ++ gamma_rho_acq = 29634; ++ gamma_trk = 24; ++ gamma_rho_trk = 1176; ++ lock_count_thr = 8; ++ break; ++ } ++ ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2); ++ csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3); ++ csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4); ++ ++ STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass); ++ STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain); ++ STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift); ++ STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk); ++ STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk); ++ STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr); ++ STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4); ++ } ++} ++ ++/* ++ * stb0899_dvbs2_get_srate ++ * get DVB-S2 Symbol Rate ++ */ ++static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg; ++ int div1, div2, rem1, rem2; ++ ++ div1 = config->btr_nco_bits / 2; ++ div2 = config->btr_nco_bits - div1 - 1; ++ ++ bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL); ++ decimRate = STB0899_GETFIELD(DECIM_RATE, reg); ++ decimRate = (1 << decimRate); ++ ++ intval1 = internal->master_clk / (1 << div1); ++ intval2 = bTrNomFreq / (1 << div2); ++ ++ rem1 = internal->master_clk % (1 << div1); ++ rem2 = bTrNomFreq % (1 << div2); ++ /* only for integer calculation */ ++ srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1)); ++ srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */ ++ ++ return srate; ++} ++ ++/* ++ * stb0899_dvbs2_algo ++ * Search for signal, timing, carrier and data for a given ++ * frequency in a given range ++ */ ++enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ enum stb0899_modcod modcod; ++ ++ s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum; ++ int i = 0; ++ u32 reg, csm1; ++ ++ if (internal->srate <= 2000000) { ++ searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */ ++ FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */ ++ } else if (internal->srate <= 5000000) { ++ searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */ ++ FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */ ++ } else if (internal->srate <= 10000000) { ++ searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs srate <= 15000000) { ++ searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs srate <= 20000000) { ++ searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */ ++ FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */ ++ } else if (internal->srate <= 25000000) { ++ searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */ ++ FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ ++ } else { ++ searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */ ++ FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ ++ } ++ ++ /* Maintain Stream Merger in reset during acquisition */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ /* Move tuner to frequency */ ++ if (state->config->tuner_set_frequency) ++ state->config->tuner_set_frequency(&state->frontend, internal->freq); ++ if (state->config->tuner_get_frequency) ++ state->config->tuner_get_frequency(&state->frontend, &internal->freq); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ /* Set IF AGC to acquisition */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4); ++ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); ++ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); ++ ++ /* Initialisation */ ++ stb0899_dvbs2_init_calc(state); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ switch (internal->inversion) { ++ case IQ_SWAP_OFF: ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0); ++ break; ++ case IQ_SWAP_ON: ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); ++ break; ++ case IQ_SWAP_AUTO: /* use last successful search first */ ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); ++ break; ++ } ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); ++ stb0899_dvbs2_reacquire(state); ++ ++ /* Wait for demod lock (UWP and CSM) */ ++ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); ++ ++ if (internal->status == DVBS2_DEMOD_LOCK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !"); ++ i = 0; ++ /* Demod Locked, check FEC status */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ ++ /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/ ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ /* Read the frequency offset*/ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++ stb0899_dvbs2_reacquire(state); ++ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); ++ i++; ++ } ++ } ++ ++ if (internal->status != DVBS2_FEC_LOCK) { ++ if (internal->inversion == IQ_SWAP_AUTO) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg); ++ /* IQ Spectrum Inversion */ ++ STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); ++ /* start acquistion process */ ++ stb0899_dvbs2_reacquire(state); ++ ++ /* Wait for demod lock (UWP and CSM) */ ++ internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); ++ if (internal->status == DVBS2_DEMOD_LOCK) { ++ i = 0; ++ /* Demod Locked, check FEC */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ /*try thrice for false locks, (UWP and CSM Locked but no FEC) */ ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ /* Read the frequency offset*/ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); ++ STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); ++ ++ stb0899_dvbs2_reacquire(state); ++ internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); ++ i++; ++ } ++ } ++/* ++ if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED) ++ pParams->IQLocked = !iqSpectrum; ++*/ ++ } ++ } ++ if (internal->status == DVBS2_FEC_LOCK) { ++ dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !"); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; ++ pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; ++ ++ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && ++ (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) && ++ (pilots == 1)) { ++ ++ stb0899_dvbs2_init_csm(state, pilots, modcod); ++ /* Wait for UWP,CSM and data LOCK 20ms max */ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ ++ i = 0; ++ while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); ++ STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); ++ ++ internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); ++ i++; ++ } ++ } ++ ++ if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && ++ (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) && ++ (pilots == 1)) { ++ ++ /* Equalizer Disable update */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ } ++ ++ /* slow down the Equalizer once locked */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); ++ STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); ++ ++ /* Store signal parameters */ ++ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); ++ ++ offsetfreq = offsetfreq / ((1 << 30) / 1000); ++ offsetfreq *= (internal->master_clk / 1000000); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); ++ if (STB0899_GETFIELD(SPECTRUM_INVERT, reg)) ++ offsetfreq *= -1; ++ ++ internal->freq = internal->freq - offsetfreq; ++ internal->srate = stb0899_dvbs2_get_srate(state); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; ++ internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; ++ internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01; ++ ++ /* Set IF AGC to tracking */ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3); ++ ++ /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/ ++ if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23)) ++ STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16); ++ ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); ++ STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); ++ } ++ ++ /* Release Stream Merger Reset */ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESRS, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ return internal->status; ++} +diff --git a/drivers/media/dvb-frontends/stb0899_cfg.h b/drivers/media/dvb-frontends/stb0899_cfg.h +new file mode 100644 +index 0000000..0867906 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_cfg.h +@@ -0,0 +1,287 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STB0899_CFG_H ++#define __STB0899_CFG_H ++ ++static const struct stb0899_s2_reg stb0899_s2_init_2[] = { ++ ++ { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */ ++ { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */ ++ { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */ ++ { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */ ++ { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */ ++ { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */ ++ { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */ ++ ++ { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */ ++ { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */ ++ ++ { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */ ++ { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */ ++ { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */ ++ { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */ ++ { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */ ++ { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */ ++ { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */ ++ { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */ ++ { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */ ++ { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */ ++ { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */ ++ { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */ ++ { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */ ++ { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */ ++ { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */ ++ { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */ ++ { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */ ++ { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */ ++ { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */ ++ { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */ ++ { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */ ++ { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */ ++ { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */ ++ { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */ ++ { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */ ++ { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */ ++ { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */ ++ { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */ ++ { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */ ++ { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */ ++ { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */ ++ { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */ ++ { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */ ++ { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */ ++ { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */ ++ { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */ ++ { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */ ++ { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */ ++ { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */ ++ { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */ ++ { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */ ++ { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */ ++ { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */ ++ { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */ ++ { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */ ++ { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */ ++ { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */ ++ { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */ ++ { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */ ++ { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */ ++ { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */ ++ { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */ ++ { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */ ++ { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */ ++ { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */ ++ { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */ ++ { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */ ++ { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */ ++ { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */ ++ { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */ ++ { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */ ++ { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */ ++ { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */ ++ { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */ ++ { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */ ++ { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */ ++ { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */ ++ { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */ ++ { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */ ++ { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */ ++ { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */ ++ { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */ ++ { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */ ++ { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */ ++ { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */ ++ { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */ ++ { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */ ++ { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */ ++ { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */ ++ { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */ ++ { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */ ++ { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */ ++ { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */ ++ { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */ ++ { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */ ++ { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */ ++ { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */ ++ { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */ ++ { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */ ++ { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */ ++ { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */ ++ { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */ ++ { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */ ++ { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */ ++ { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */ ++ { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */ ++ { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */ ++ { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */ ++ { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */ ++ { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */ ++ { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */ ++ { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */ ++ { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */ ++ { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */ ++ { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */ ++ { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */ ++ { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */ ++ { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */ ++ { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */ ++ { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */ ++ { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */ ++ { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */ ++ { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */ ++ { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */ ++ { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */ ++ { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */ ++ { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */ ++ { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */ ++ { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */ ++ { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/ ++ { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/ ++ { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/ ++ { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */ ++ { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */ ++ { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */ ++ { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */ ++ { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */ ++ { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */ ++ { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */ ++ { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */ ++ { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */ ++ { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */ ++ { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */ ++ { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/ ++ { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */ ++ { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */ ++ { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */ ++ { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */ ++ { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */ ++ { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */ ++ { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */ ++ { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */ ++ { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */ ++ { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */ ++ { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/ ++ { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */ ++ { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */ ++ { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */ ++ { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */ ++ { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */ ++ { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */ ++ { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */ ++ { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */ ++ { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */ ++ { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */ ++ { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/ ++ { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */ ++ { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */ ++ { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */ ++ { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */ ++ { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */ ++ { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */ ++ { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */ ++ { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */ ++ { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */ ++ { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */ ++ { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/ ++ { 0xffff , 0xffffffff , 0xffffffff }, ++}; ++static const struct stb0899_s2_reg stb0899_s2_init_4[] = { ++ { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */ ++ { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */ ++ { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */ ++ { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */ ++ { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */ ++ { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */ ++ { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */ ++ { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */ ++ { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */ ++ { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */ ++ { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */ ++ { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */ ++ { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */ ++ { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */ ++ { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */ ++ { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */ ++ { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */ ++ { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */ ++ { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */ ++ { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */ ++ { 0xffff , 0xffffffff , 0xffffffff }, ++}; ++ ++static const struct stb0899_s1_reg stb0899_s1_init_5[] = { ++ { STB0899_TSTCK , 0x00 }, ++ { STB0899_TSTRES , 0x00 }, ++ { STB0899_TSTOUT , 0x00 }, ++ { STB0899_TSTIN , 0x00 }, ++ { STB0899_TSTSYS , 0x00 }, ++ { STB0899_TSTCHIP , 0x00 }, ++ { STB0899_TSTFREE , 0x00 }, ++ { STB0899_TSTI2C , 0x00 }, ++ { STB0899_BITSPEEDM , 0x00 }, ++ { STB0899_BITSPEEDL , 0x00 }, ++ { STB0899_TBUSBIT , 0x00 }, ++ { STB0899_TSTDIS , 0x00 }, ++ { STB0899_TSTDISRX , 0x00 }, ++ { STB0899_TSTJETON , 0x00 }, ++ { STB0899_TSTDCADJ , 0x00 }, ++ { STB0899_TSTAGC1 , 0x00 }, ++ { STB0899_TSTAGC1N , 0x00 }, ++ { STB0899_TSTPOLYPH , 0x00 }, ++ { STB0899_TSTR , 0x00 }, ++ { STB0899_TSTAGC2 , 0x00 }, ++ { STB0899_TSTCTL1 , 0x00 }, ++ { STB0899_TSTCTL2 , 0x00 }, ++ { STB0899_TSTCTL3 , 0x00 }, ++ { STB0899_TSTDEMAP , 0x00 }, ++ { STB0899_TSTDEMAP2 , 0x00 }, ++ { STB0899_TSTDEMMON , 0x00 }, ++ { STB0899_TSTRATE , 0x00 }, ++ { STB0899_TSTSELOUT , 0x00 }, ++ { STB0899_TSYNC , 0x00 }, ++ { STB0899_TSTERR , 0x00 }, ++ { STB0899_TSTRAM1 , 0x00 }, ++ { STB0899_TSTVSELOUT , 0x00 }, ++ { STB0899_TSTFORCEIN , 0x00 }, ++ { STB0899_TSTRS1 , 0x00 }, ++ { STB0899_TSTRS2 , 0x00 }, ++ { STB0899_TSTRS3 , 0x00 }, ++ { STB0899_GHOSTREG , 0x81 }, ++ { 0xffff , 0xff }, ++}; ++ ++#define STB0899_DVBS2_ESNO_AVE 3 ++#define STB0899_DVBS2_ESNO_QUANT 32 ++#define STB0899_DVBS2_AVFRAMES_COARSE 10 ++#define STB0899_DVBS2_AVFRAMES_FINE 20 ++#define STB0899_DVBS2_MISS_THRESHOLD 6 ++#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125 ++#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758 ++#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350 ++#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100 ++ ++#define STB0899_DVBS2_BTR_NCO_BITS 28 ++#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15 ++#define STB0899_DVBS2_CRL_NCO_BITS 30 ++#define STB0899_DVBS2_LDPC_MAX_ITER 70 ++ ++#endif //__STB0899_CFG_H +diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c +new file mode 100644 +index 0000000..cc278b3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_drv.c +@@ -0,0 +1,1652 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "dvb_frontend.h" ++ ++#include "stb0899_drv.h" ++#include "stb0899_priv.h" ++#include "stb0899_reg.h" ++ ++static unsigned int verbose = 0;//1; ++module_param(verbose, int, 0644); ++ ++/* C/N in dB/10, NIRM/NIRL */ ++static const struct stb0899_tab stb0899_cn_tab[] = { ++ { 200, 2600 }, ++ { 190, 2700 }, ++ { 180, 2860 }, ++ { 170, 3020 }, ++ { 160, 3210 }, ++ { 150, 3440 }, ++ { 140, 3710 }, ++ { 130, 4010 }, ++ { 120, 4360 }, ++ { 110, 4740 }, ++ { 100, 5190 }, ++ { 90, 5670 }, ++ { 80, 6200 }, ++ { 70, 6770 }, ++ { 60, 7360 }, ++ { 50, 7970 }, ++ { 40, 8250 }, ++ { 30, 9000 }, ++ { 20, 9450 }, ++ { 15, 9600 }, ++}; ++ ++/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10. ++ * As measured, connected to a modulator. ++ * -8.0 to -50.0 dBm directly connected, ++ * -52.0 to -74.8 with extra attenuation. ++ * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm. ++ * Crude linear extrapolation below -84.8dBm and above -8.0dBm. ++ */ ++static const struct stb0899_tab stb0899_dvbsrf_tab[] = { ++ { -750, -128 }, ++ { -748, -94 }, ++ { -745, -92 }, ++ { -735, -90 }, ++ { -720, -87 }, ++ { -670, -77 }, ++ { -640, -70 }, ++ { -610, -62 }, ++ { -600, -60 }, ++ { -590, -56 }, ++ { -560, -41 }, ++ { -540, -25 }, ++ { -530, -17 }, ++ { -520, -11 }, ++ { -500, 1 }, ++ { -490, 6 }, ++ { -480, 10 }, ++ { -440, 22 }, ++ { -420, 27 }, ++ { -400, 31 }, ++ { -380, 34 }, ++ { -340, 40 }, ++ { -320, 43 }, ++ { -280, 48 }, ++ { -250, 52 }, ++ { -230, 55 }, ++ { -180, 61 }, ++ { -140, 66 }, ++ { -90, 73 }, ++ { -80, 74 }, ++ { 500, 127 } ++}; ++ ++/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10. ++ * As measured, connected to a modulator. ++ * -8.0 to -50.1 dBm directly connected, ++ * -53.0 to -76.6 with extra attenuation. ++ * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm. ++ * Crude linear extrapolation below -76.6dBm and above -8.0dBm. ++ */ ++static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { ++ { 700, 0 }, ++ { -80, 3217 }, ++ { -150, 3893 }, ++ { -190, 4217 }, ++ { -240, 4621 }, ++ { -280, 4945 }, ++ { -320, 5273 }, ++ { -350, 5545 }, ++ { -370, 5741 }, ++ { -410, 6147 }, ++ { -450, 6671 }, ++ { -490, 7413 }, ++ { -501, 7665 }, ++ { -530, 8767 }, ++ { -560, 10219 }, ++ { -580, 10939 }, ++ { -590, 11518 }, ++ { -600, 11723 }, ++ { -650, 12659 }, ++ { -690, 13219 }, ++ { -730, 13645 }, ++ { -750, 13909 }, ++ { -766, 14153 }, ++ { -950, 16383 } ++}; ++ ++/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ ++static struct stb0899_tab stb0899_quant_tab[] = { ++ { 0, 0 }, ++ { 0, 100 }, ++ { 600, 200 }, ++ { 950, 299 }, ++ { 1200, 398 }, ++ { 1400, 501 }, ++ { 1560, 603 }, ++ { 1690, 700 }, ++ { 1810, 804 }, ++ { 1910, 902 }, ++ { 2000, 1000 }, ++ { 2080, 1096 }, ++ { 2160, 1202 }, ++ { 2230, 1303 }, ++ { 2350, 1496 }, ++ { 2410, 1603 }, ++ { 2460, 1698 }, ++ { 2510, 1799 }, ++ { 2600, 1995 }, ++ { 2650, 2113 }, ++ { 2690, 2213 }, ++ { 2720, 2291 }, ++ { 2760, 2399 }, ++ { 2800, 2512 }, ++ { 2860, 2692 }, ++ { 2930, 2917 }, ++ { 2960, 3020 }, ++ { 3010, 3199 }, ++ { 3040, 3311 }, ++ { 3060, 3388 }, ++ { 3120, 3631 }, ++ { 3190, 3936 }, ++ { 3400, 5012 }, ++ { 3610, 6383 }, ++ { 3800, 7943 }, ++ { 4210, 12735 }, ++ { 4500, 17783 }, ++ { 4690, 22131 }, ++ { 4810, 25410 } ++}; ++ ++/* DVB-S2 Es/N0 estimate in dB/100 vs read value */ ++static struct stb0899_tab stb0899_est_tab[] = { ++ { 0, 0 }, ++ { 0, 1 }, ++ { 301, 2 }, ++ { 1204, 16 }, ++ { 1806, 64 }, ++ { 2408, 256 }, ++ { 2709, 512 }, ++ { 3010, 1023 }, ++ { 3311, 2046 }, ++ { 3612, 4093 }, ++ { 3823, 6653 }, ++ { 3913, 8185 }, ++ { 4010, 10233 }, ++ { 4107, 12794 }, ++ { 4214, 16368 }, ++ { 4266, 18450 }, ++ { 4311, 20464 }, ++ { 4353, 22542 }, ++ { 4391, 24604 }, ++ { 4425, 26607 }, ++ { 4457, 28642 }, ++ { 4487, 30690 }, ++ { 4515, 32734 }, ++ { 4612, 40926 }, ++ { 4692, 49204 }, ++ { 4816, 65464 }, ++ { 4913, 81846 }, ++ { 4993, 98401 }, ++ { 5060, 114815 }, ++ { 5118, 131220 }, ++ { 5200, 158489 }, ++ { 5300, 199526 }, ++ { 5400, 251189 }, ++ { 5500, 316228 }, ++ { 5600, 398107 }, ++ { 5720, 524807 }, ++ { 5721, 526017 }, ++}; ++ ++static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) ++{ ++ int ret; ++ ++ u8 b0[] = { reg >> 8, reg & 0xff }; ++ u8 buf; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 2 ++ },{ ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = &buf, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) { ++ if (ret != -ERESTARTSYS) ++ dprintk(state->verbose, FE_ERROR, 1, ++ "Read error, Reg=[0x%02x], Status=%d", ++ reg, ret); ++ ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x", ++ reg, buf); ++ ++ return (unsigned int)buf; ++} ++ ++int stb0899_read_reg(struct stb0899_state *state, unsigned int reg) ++{ ++ int result; ++ ++ result = _stb0899_read_reg(state, reg); ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((reg != 0xf2ff) && (reg != 0xf6ff) && ++ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ _stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ return result; ++} ++ ++u32 _stb0899_read_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset) ++{ ++ int status; ++ u32 data; ++ u8 buf[7] = { 0 }; ++ u16 tmpaddr; ++ ++ u8 buf_0[] = { ++ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ ++ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ ++ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ ++ }; ++ u8 buf_1[] = { ++ 0x00, /* 0xf3 Reg Offset */ ++ 0x00, /* 0x44 Reg Offset */ ++ }; ++ ++ struct i2c_msg msg_0 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_0, ++ .len = 6 ++ }; ++ ++ struct i2c_msg msg_1 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_1, ++ .len = 2 ++ }; ++ ++ struct i2c_msg msg_r = { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = 4 ++ }; ++ ++ tmpaddr = stb0899_reg_offset & 0xff00; ++ if (!(stb0899_reg_offset & 0x8)) ++ tmpaddr = stb0899_reg_offset | 0x20; ++ ++ buf_1[0] = GETBYTE(tmpaddr, BYTE1); ++ buf_1[1] = GETBYTE(tmpaddr, BYTE0); ++ ++ status = i2c_transfer(state->i2c, &msg_0, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ ++ goto err; ++ } ++ ++ /* Dummy */ ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (status < 1) ++ goto err; ++ ++ status = i2c_transfer(state->i2c, &msg_r, 1); ++ if (status < 1) ++ goto err; ++ ++ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); ++ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); ++ ++ /* Actual */ ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ goto err; ++ } ++ ++ status = i2c_transfer(state->i2c, &msg_r, 1); ++ if (status < 1) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); ++ return status < 0 ? status : -EREMOTEIO; ++ } ++ ++ data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]); ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data); ++ ++ return data; ++ ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_write_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset, ++ u32 stb0899_data) ++{ ++ int status; ++ ++ /* Base Address Setup */ ++ u8 buf_0[] = { ++ GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ ++ GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ ++ GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ ++ GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ ++ }; ++ u8 buf_1[] = { ++ 0x00, /* 0xf3 Reg Offset */ ++ 0x00, /* 0x44 Reg Offset */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ 0x00, /* data */ ++ }; ++ ++ struct i2c_msg msg_0 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_0, ++ .len = 6 ++ }; ++ ++ struct i2c_msg msg_1 = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf_1, ++ .len = 6 ++ }; ++ ++ buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); ++ buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); ++ buf_1[2] = GETBYTE(stb0899_data, BYTE0); ++ buf_1[3] = GETBYTE(stb0899_data, BYTE1); ++ buf_1[4] = GETBYTE(stb0899_data, BYTE2); ++ buf_1[5] = GETBYTE(stb0899_data, BYTE3); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data); ++ ++ status = i2c_transfer(state->i2c, &msg_0, 1); ++ if (unlikely(status < 1)) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); ++ goto err; ++ } ++ status = i2c_transfer(state->i2c, &msg_1, 1); ++ if (unlikely(status < 1)) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", ++ __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); ++ ++ return status < 0 ? status : -EREMOTEIO; ++ } ++ ++ return 0; ++ ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count) ++{ ++ int status; ++ ++ u8 b0[] = { reg >> 8, reg & 0xff }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 2 ++ },{ ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = count ++ } ++ }; ++ ++ status = i2c_transfer(state->i2c, msg, 2); ++ if (status != 2) { ++ if (status != -ERESTARTSYS) ++ printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n", ++ __func__, reg, count, status); ++ goto err; ++ } ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((reg != 0xf2ff) && (reg != 0xf6ff) && ++ (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ _stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) { ++ int i; ++ ++ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); ++ for (i = 0; i < count; i++) { ++ printk(" %02x", buf[i]); ++ } ++ printk("\n"); ++ } ++ ++ return 0; ++err: ++ return status < 0 ? status : -EREMOTEIO; ++} ++ ++int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count) ++{ ++ int ret; ++ u8 buf[2 + count]; ++ struct i2c_msg i2c_msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 + count ++ }; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ memcpy(&buf[2], data, count); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) { ++ int i; ++ ++ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); ++ for (i = 0; i < count; i++) ++ printk(" %02x", data[i]); ++ printk("\n"); ++ } ++ ret = i2c_transfer(state->i2c, &i2c_msg, 1); ++ ++ /* ++ * Bug ID 9: ++ * access to 0xf2xx/0xf6xx ++ * must be followed by read from 0xf2ff/0xf6ff. ++ */ ++ if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) ++ stb0899_read_reg(state, (reg | 0x00ff)); ++ ++ if (ret != 1) { ++ if (ret != -ERESTARTSYS) ++ dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", ++ reg, data[0], count, ret); ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data) ++{ ++ return stb0899_write_regs(state, reg, &data, 1); ++} ++ ++/* ++ * stb0899_get_mclk ++ * Get STB0899 master clock frequency ++ * ExtClk: external clock frequency (Hz) ++ */ ++static u32 stb0899_get_mclk(struct stb0899_state *state) ++{ ++ u32 mclk = 0, div = 0; ++ ++ div = stb0899_read_reg(state, STB0899_NCOARSE); ++ mclk = (div + 1) * state->config->xtal_freq / 6; ++ dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk); ++ ++ return mclk; ++} ++ ++/* ++ * stb0899_set_mclk ++ * Set STB0899 master Clock frequency ++ * Mclk: demodulator master clock ++ * ExtClk: external clock frequency (Hz) ++ */ ++static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ u8 mdiv = 0; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config); ++ mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1; ++ dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv); ++ ++ stb0899_write_reg(state, STB0899_NCOARSE, mdiv); ++ internal->master_clk = stb0899_get_mclk(state); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk); ++} ++ ++static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable) ++{ ++ struct stb0899_config *config = state->config; ++ const struct stb0899_postproc *postproc = config->postproc; ++ ++ /* post process event */ ++ if (postproc) { ++ if (enable) { ++ if (postproc[ctl].level == STB0899_GPIOPULLUP) ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x02); ++ else ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x82); ++ } else { ++ if (postproc[ctl].level == STB0899_GPIOPULLUP) ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x82); ++ else ++ stb0899_write_reg(state, postproc[ctl].gpio, 0x02); ++ } ++ } ++ return 0; ++} ++ ++static void stb0899_release(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); ++ kfree(state); ++} ++ ++/* ++ * stb0899_get_alpha ++ * return: rolloff ++ */ ++static int stb0899_get_alpha(struct stb0899_state *state) ++{ ++ u8 mode_coeff; ++ ++ mode_coeff = stb0899_read_reg(state, STB0899_DEMOD); ++ ++ if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1) ++ return 20; ++ else ++ return 35; ++} ++ ++/* ++ * stb0899_init_calc ++ */ ++static void stb0899_init_calc(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ int master_clk; ++ u8 agc[2]; ++ u32 reg; ++ ++ /* Read registers (in burst mode) */ ++ stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ ++ ++ /* Initial calculations */ ++ master_clk = stb0899_get_mclk(state); ++ internal->t_agc1 = 0; ++ internal->t_agc2 = 0; ++ internal->master_clk = master_clk; ++ internal->mclk = master_clk / 65536L; ++ internal->rolloff = stb0899_get_alpha(state); ++ ++ /* DVBS2 Initial calculations */ ++ /* Set AGC value to the middle */ ++ internal->agc_gain = 8154; ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); ++ STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain); ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); ++ ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA); ++ internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg); ++ ++ internal->center_freq = 0; ++ internal->av_frame_coarse = 10; ++ internal->av_frame_fine = 20; ++ internal->step_size = 2; ++/* ++ if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO)) ++ pParams->IQLocked = 0; ++ else ++ pParams->IQLocked = 1; ++*/ ++} ++ ++static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (1) { ++ reg = stb0899_read_reg(state, STB0899_DISSTATUS); ++ if (!STB0899_GETFIELD(FIFOFULL, reg)) ++ break; ++ if ((jiffies - start) > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ return 0; ++} ++ ++static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, i; ++ ++ if (cmd->msg_len > 8) ++ return -EINVAL; ++ ++ /* enable FIFO precharge */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ for (i = 0; i < cmd->msg_len; i++) { ++ /* wait for FIFO empty */ ++ if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); ++ } ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ msleep(100); ++ return 0; ++} ++ ++static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (!STB0899_GETFIELD(RXEND, reg)) { ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST0); ++ if (jiffies - start > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, length = 0, i; ++ int result; ++ ++ if (stb0899_wait_diseqc_rxidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST0); ++ if (STB0899_GETFIELD(RXEND, reg)) { ++ ++ reg = stb0899_read_reg(state, STB0899_DISRX_ST1); ++ length = STB0899_GETFIELD(FIFOBYTENBR, reg); ++ ++ if (length > sizeof (reply->msg)) { ++ result = -EOVERFLOW; ++ goto exit; ++ } ++ reply->msg_len = length; ++ ++ /* extract data */ ++ for (i = 0; i < length; i++) ++ reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO); ++ } ++ ++ return 0; ++exit: ++ ++ return result; ++} ++ ++static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) ++{ ++ u8 reg = 0; ++ unsigned long start = jiffies; ++ ++ while (!STB0899_GETFIELD(TXIDLE, reg)) { ++ reg = stb0899_read_reg(state, STB0899_DISSTATUS); ++ if (jiffies - start > timeout) { ++ dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ return 0; ++} ++ ++static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ u8 reg, old_state; ++ ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ old_state = reg; ++ /* set to burst mode */ ++ STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ switch (burst) { ++ case SEC_MINI_A: ++ /* unmodulated */ ++ stb0899_write_reg(state, STB0899_DISFIFO, 0x00); ++ break; ++ case SEC_MINI_B: ++ /* modulated */ ++ stb0899_write_reg(state, STB0899_DISFIFO, 0xff); ++ break; ++ } ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ /* restore state */ ++ stb0899_write_reg(state, STB0899_DISCNTRL1, old_state); ++ ++ return 0; ++} ++ ++static int stb0899_diseqc_init(struct stb0899_state *state) ++{ ++/* ++ struct dvb_diseqc_slave_reply rx_data; ++*/ ++ u8 f22_tx, reg; ++ ++ u32 mclk, tx_freq = 22000;/* count = 0, i; */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL2); ++ STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL2, reg); ++ ++ /* disable Tx spy */ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ ++ reg = stb0899_read_reg(state, STB0899_DISCNTRL1); ++ STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0); ++ stb0899_write_reg(state, STB0899_DISCNTRL1, reg); ++ ++ mclk = stb0899_get_mclk(state); ++ f22_tx = mclk / (tx_freq * 32); ++ stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ ++ state->rx_freq = 20000; ++ ++ return 0; ++} ++ ++static int stb0899_sleep(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++/* ++ u8 reg; ++*/ ++ dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); ++ ++ return 0; ++} ++ ++static int stb0899_wakeup(struct dvb_frontend *fe) ++{ ++ int rc; ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI))) ++ return rc; ++ /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */ ++ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00))) ++ return rc; ++ if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00))) ++ return rc; ++ ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1); ++ ++ return 0; ++} ++ ++static int stb0899_init(struct dvb_frontend *fe) ++{ ++ int i; ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_config *config = state->config; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... "); ++ ++ /* init device */ ++ dprintk(state->verbose, FE_DEBUG, 1, "init device"); ++ for (i = 0; config->init_dev[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod"); ++ /* init S2 demod */ ++ for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++) ++ stb0899_write_s2reg(state, STB0899_S2DEMOD, ++ config->init_s2_demod[i].base_address, ++ config->init_s2_demod[i].offset, ++ config->init_s2_demod[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod"); ++ /* init S1 demod */ ++ for (i = 0; config->init_s1_demod[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC"); ++ /* init S2 fec */ ++ for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++) ++ stb0899_write_s2reg(state, STB0899_S2FEC, ++ config->init_s2_fec[i].base_address, ++ config->init_s2_fec[i].offset, ++ config->init_s2_fec[i].data); ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "init TST"); ++ /* init test */ ++ for (i = 0; config->init_tst[i].address != 0xffff; i++) ++ stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data); ++ ++ stb0899_init_calc(state); ++ stb0899_diseqc_init(state); ++ ++ return 0; ++} ++ ++static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val) ++{ ++ int res = 0; ++ int min = 0, med; ++ ++ if (val < tab[min].read) ++ res = tab[min].real; ++ else if (val >= tab[max].read) ++ res = tab[max].real; ++ else { ++ while ((max - min) > 1) { ++ med = (max + min) / 2; ++ if (val >= tab[min].read && val < tab[med].read) ++ max = med; ++ else ++ min = med; ++ } ++ res = ((val - tab[min].read) * ++ (tab[max].real - tab[min].real) / ++ (tab[max].read - tab[min].read)) + ++ tab[min].real; ++ } ++ ++ return res; ++} ++ ++static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ int val; ++ u32 reg; ++ *strength = 0; ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ ++ reg = stb0899_read_reg(state, STB0899_AGCIQIN); ++ val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg); ++ ++ *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); ++ *strength += 750; ++ dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", ++ val & 0xff, *strength); ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN); ++ val = STB0899_GETFIELD(IF_AGC_GAIN, reg); ++ ++ *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); ++ *strength += 950; ++ dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", ++ val & 0x3fff, *strength); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ unsigned int val, quant, quantn = -1, est, estn = -1; ++ u8 buf[2]; ++ u32 reg; ++ ++ *snr = 0; ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ ++ stb0899_read_regs(state, STB0899_NIRM, buf, 2); ++ val = MAKEWORD16(buf[0], buf[1]); ++ ++ *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); ++ dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", ++ buf[0], buf[1], val, *snr); ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); ++ quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg); ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); ++ est = STB0899_GETFIELD(ESN0_EST, reg); ++ if (est == 1) ++ val = 301; /* C/N = 30.1 dB */ ++ else if (est == 2) ++ val = 270; /* C/N = 27.0 dB */ ++ else { ++ /* quantn = 100 * log(quant^2) */ ++ quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100); ++ /* estn = 100 * log(est) */ ++ estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est); ++ /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */ ++ val = (quantn - estn) / 10; ++ } ++ *snr = val; ++ dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", ++ quant, quantn, est, estn, val); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ u8 reg; ++ *status = 0; ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS"); ++ if (internal->lock) { ++ reg = stb0899_read_reg(state, STB0899_VSTATUS); ++ if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ ++ reg = stb0899_read_reg(state, STB0899_PLPARM); ++ if (STB0899_GETFIELD(VITCURPUN, reg)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC"); ++ *status |= FE_HAS_VITERBI | FE_HAS_SYNC; ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); ++ } ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2"); ++ if (internal->lock) { ++ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); ++ if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) { ++ *status |= FE_HAS_CARRIER; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER"); ++ ++ reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); ++ if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) { ++ *status |= FE_HAS_LOCK; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK"); ++ ++ } ++ if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) { ++ *status |= FE_HAS_VITERBI; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI"); ++ } ++ if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) { ++ *status |= FE_HAS_SYNC; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC"); ++ /* post process event */ ++ stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); ++ } ++ } ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * stb0899_get_error ++ * viterbi error for DVB-S/DSS ++ * packet error for DVB-S2 ++ * Bit Error Rate or Packet Error Rate * 10 ^ 7 ++ */ ++static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ u8 lsb, msb; ++ ++ *ber = 0; ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ if (internal->lock) { ++ lsb = stb0899_read_reg(state, STB0899_ECNT1L); ++ msb = stb0899_read_reg(state, STB0899_ECNT1M); ++ *ber = MAKEWORD16(msb, lsb); ++ /* Viterbi Check */ ++ if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { ++ /* Error Rate */ ++ *ber *= 9766; ++ /* ber = ber * 10 ^ 7 */ ++ *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); ++ *ber /= 8; ++ } ++ } ++ break; ++ case SYS_DVBS2: ++ if (internal->lock) { ++ lsb = stb0899_read_reg(state, STB0899_ECNT1L); ++ msb = stb0899_read_reg(state, STB0899_ECNT1M); ++ *ber = MAKEWORD16(msb, lsb); ++ /* ber = ber * 10 ^ 7 */ ++ *ber *= 10000000; ++ *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00); ++ break; ++ case SEC_VOLTAGE_18: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); ++ break; ++ case SEC_VOLTAGE_OFF: ++ stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82); ++ stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ u8 div, reg; ++ ++ /* wait for diseqc idle */ ++ if (stb0899_wait_diseqc_txidle(state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ div = (internal->master_clk / 100) / 5632; ++ div = (div + 5) / 10; ++ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66); ++ reg = stb0899_read_reg(state, STB0899_ACRPRESC); ++ STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03); ++ stb0899_write_reg(state, STB0899_ACRPRESC, reg); ++ stb0899_write_reg(state, STB0899_ACRDIV1, div); ++ break; ++ case SEC_TONE_OFF: ++ stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ int i2c_stat; ++ struct stb0899_state *state = fe->demodulator_priv; ++ ++ i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT); ++ if (i2c_stat < 0) ++ goto err; ++ ++ if (enable) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ..."); ++ i2c_stat |= STB0899_I2CTON; ++ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) ++ goto err; ++ } else { ++ dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ..."); ++ i2c_stat &= ~STB0899_I2CTON; ++ if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) ++ goto err; ++ } ++ return 0; ++err: ++ dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed"); ++ return -EREMOTEIO; ++} ++ ++ ++static inline void CONVERT32(u32 x, char *str) ++{ ++ *str++ = (x >> 24) & 0xff; ++ *str++ = (x >> 16) & 0xff; ++ *str++ = (x >> 8) & 0xff; ++ *str++ = (x >> 0) & 0xff; ++ *str = '\0'; ++} ++ ++static int stb0899_get_dev_id(struct stb0899_state *state) ++{ ++ u8 chip_id, release; ++ u16 id; ++ u32 demod_ver = 0, fec_ver = 0; ++ char demod_str[5] = { 0 }; ++ char fec_str[5] = { 0 }; ++ ++ id = stb0899_read_reg(state, STB0899_DEV_ID); ++ dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id); ++ chip_id = STB0899_GETFIELD(CHIP_ID, id); ++ release = STB0899_GETFIELD(CHIP_REL, id); ++ ++ dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]", ++ chip_id, release); ++ ++ CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str); ++ ++ demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID); ++ dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); ++ CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); ++ fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); ++ if (! (chip_id > 0)) { ++ dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); ++ ++ return -ENODEV; ++ } ++ dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver); ++ ++ return 0; ++} ++ ++static void stb0899_set_delivery(struct stb0899_state *state) ++{ ++ u8 reg; ++ u8 stop_clk[2]; ++ ++ stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1); ++ stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2); ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S"); ++ /* FECM/Viterbi ON */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xb1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x40); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x42); ++ stb0899_write_reg(state, STB0899_TSLPL, 0x12); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); ++ break; ++ case SYS_DVBS2: ++ /* FECM/Viterbi OFF */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xb1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x42); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x40); ++ stb0899_write_reg(state, STB0899_TSLPL, 0x02); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 0); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0); ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0); ++ break; ++ case SYS_DSS: ++ /* FECM/Viterbi ON */ ++ reg = stb0899_read_reg(state, STB0899_FECM); ++ STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1); ++ STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); ++ stb0899_write_reg(state, STB0899_FECM, reg); ++ ++ stb0899_write_reg(state, STB0899_RSULC, 0xa1); ++ stb0899_write_reg(state, STB0899_TSULC, 0x61); ++ stb0899_write_reg(state, STB0899_RSLLC, 0x42); ++ ++ reg = stb0899_read_reg(state, STB0899_TSTRES); ++ STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); ++ stb0899_write_reg(state, STB0899_TSTRES, reg); ++ ++ STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); ++ STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); ++ STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); ++ ++ STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); ++ ++ STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); ++ break; ++ default: ++ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); ++ break; ++ } ++ STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0); ++ stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2); ++} ++ ++/* ++ * stb0899_set_iterations ++ * set the LDPC iteration scale function ++ */ ++static void stb0899_set_iterations(struct stb0899_state *state) ++{ ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ ++ s32 iter_scale; ++ u32 reg; ++ ++ iter_scale = 17 * (internal->master_clk / 1000); ++ iter_scale += 410000; ++ iter_scale /= (internal->srate / 1000000); ++ iter_scale /= 1000; ++ ++ if (iter_scale > config->ldpc_max_iter) ++ iter_scale = config->ldpc_max_iter; ++ ++ reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER); ++ STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); ++ stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); ++} ++ ++static enum dvbfe_search stb0899_search(struct dvb_frontend *fe) ++{ ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_params *i_params = &state->params; ++ struct stb0899_internal *internal = &state->internal; ++ struct stb0899_config *config = state->config; ++ struct dtv_frontend_properties *props = &fe->dtv_property_cache; ++ ++ u32 SearchRange, gain; ++ ++ i_params->freq = props->frequency; ++ i_params->srate = props->symbol_rate; ++ state->delsys = props->delivery_system; ++ dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys); ++ ++ SearchRange = 10000000; ++ dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate); ++ /* checking Search Range is meaningless for a fixed 3 Mhz */ ++ if (INRANGE(i_params->srate, 1000000, 45000000)) { ++ dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE"); ++ stb0899_set_delivery(state); ++ ++ if (state->config->tuner_set_rfsiggain) { ++ if (internal->srate > 15000000) ++ gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */ ++ else if (internal->srate > 5000000) ++ gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */ ++ else ++ gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */ ++ state->config->tuner_set_rfsiggain(fe, gain); ++ } ++ ++ if (i_params->srate <= 5000000) ++ stb0899_set_mclk(state, config->lo_clk); ++ else ++ stb0899_set_mclk(state, config->hi_clk); ++ ++ switch (state->delsys) { ++ case SYS_DVBS: ++ case SYS_DSS: ++ dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system"); ++ internal->freq = i_params->freq; ++ internal->srate = i_params->srate; ++ /* ++ * search = user search range + ++ * 500Khz + ++ * 2 * Tuner_step_size + ++ * 10% of the symbol rate ++ */ ++ internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5); ++ internal->derot_percent = 30; ++ ++ /* What to do for tuners having no bandwidth setup ? */ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ if (state->config->tuner_set_bandwidth) ++ state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10); ++ if (state->config->tuner_get_bandwidth) ++ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++ /* Set DVB-S1 AGC */ ++ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11); ++ ++ /* Run the search algorithm */ ++ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo .."); ++ if (stb0899_dvbs_algo(state) == RANGEOK) { ++ internal->lock = 1; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "-------------------------------------> DVB-S LOCK !"); ++ ++// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */ ++// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); ++// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); ++// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status); ++// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl); ++ ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ internal->lock = 0; ++ ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ break; ++ case SYS_DVBS2: ++ internal->freq = i_params->freq; ++ internal->srate = i_params->srate; ++ internal->srch_range = SearchRange; ++ ++ /* enable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 1); ++ ++ if (state->config->tuner_set_bandwidth) ++ state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange)); ++ if (state->config->tuner_get_bandwidth) ++ state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); ++ ++ /* disable tuner I/O */ ++ stb0899_i2c_gate_ctrl(&state->frontend, 0); ++ ++// pParams->SpectralInv = pSearch->IQ_Inversion; ++ ++ /* Set DVB-S2 AGC */ ++ stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c); ++ ++ /* Set IterScale =f(MCLK,SYMB) */ ++ stb0899_set_iterations(state); ++ ++ /* Run the search algorithm */ ++ dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo .."); ++ if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) { ++ internal->lock = 1; ++ dprintk(state->verbose, FE_DEBUG, 1, ++ "-------------------------------------> DVB-S2 LOCK !"); ++ ++// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */ ++// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); ++// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); ++ ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ internal->lock = 0; ++ ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ break; ++ default: ++ dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); ++ return DVBFE_ALGO_SEARCH_INVALID; ++ } ++ } ++ ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static int stb0899_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stb0899_state *state = fe->demodulator_priv; ++ struct stb0899_internal *internal = &state->internal; ++ ++ dprintk(state->verbose, FE_DEBUG, 1, "Get params"); ++ p->symbol_rate = internal->srate; ++ p->frequency = internal->freq; ++ ++ return 0; ++} ++ ++static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static struct dvb_frontend_ops stb0899_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, ++ .info = { ++ .name = "STB0899 Multistandard", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 0, ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 5000000, ++ .symbol_rate_max = 45000000, ++ ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_QPSK ++ }, ++ ++ .release = stb0899_release, ++ .init = stb0899_init, ++ .sleep = stb0899_sleep, ++// .wakeup = stb0899_wakeup, ++ ++ .i2c_gate_ctrl = stb0899_i2c_gate_ctrl, ++ ++ .get_frontend_algo = stb0899_frontend_algo, ++ .search = stb0899_search, ++ .get_frontend = stb0899_get_frontend, ++ ++ ++ .read_status = stb0899_read_status, ++ .read_snr = stb0899_read_snr, ++ .read_signal_strength = stb0899_read_signal_strength, ++ .read_ber = stb0899_read_ber, ++ ++ .set_voltage = stb0899_set_voltage, ++ .set_tone = stb0899_set_tone, ++ ++ .diseqc_send_master_cmd = stb0899_send_diseqc_msg, ++ .diseqc_recv_slave_reply = stb0899_recv_slave_reply, ++ .diseqc_send_burst = stb0899_send_diseqc_burst, ++}; ++ ++struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c) ++{ ++ struct stb0899_state *state = NULL; ++ enum stb0899_inversion inversion; ++ ++ state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ inversion = config->inversion; ++ state->verbose = &verbose; ++ state->config = config; ++ state->i2c = i2c; ++ state->frontend.ops = stb0899_ops; ++ state->frontend.demodulator_priv = state; ++ state->internal.inversion = inversion; ++ ++ stb0899_wakeup(&state->frontend); ++ if (stb0899_get_dev_id(state) == -ENODEV) { ++ printk("%s: Exiting .. !\n", __func__); ++ goto error; ++ } ++ ++ printk("%s: Attaching STB0899 \n", __func__); ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stb0899_attach); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stb0899_drv.h b/drivers/media/dvb-frontends/stb0899_drv.h +new file mode 100644 +index 0000000..98b200c +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_drv.h +@@ -0,0 +1,162 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STB0899_DRV_H ++#define __STB0899_DRV_H ++ ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#define STB0899_TSMODE_SERIAL 1 ++#define STB0899_CLKPOL_FALLING 2 ++#define STB0899_CLKNULL_PARITY 3 ++#define STB0899_SYNC_FORCED 4 ++#define STB0899_FECMODE_DSS 5 ++ ++struct stb0899_s1_reg { ++ u16 address; ++ u8 data; ++}; ++ ++struct stb0899_s2_reg { ++ u16 offset; ++ u32 base_address; ++ u32 data; ++}; ++ ++enum stb0899_inversion { ++ IQ_SWAP_OFF = 0, ++ IQ_SWAP_ON, ++ IQ_SWAP_AUTO ++}; ++ ++#define STB0899_GPIO00 0xf140 ++#define STB0899_GPIO01 0xf141 ++#define STB0899_GPIO02 0xf142 ++#define STB0899_GPIO03 0xf143 ++#define STB0899_GPIO04 0xf144 ++#define STB0899_GPIO05 0xf145 ++#define STB0899_GPIO06 0xf146 ++#define STB0899_GPIO07 0xf147 ++#define STB0899_GPIO08 0xf148 ++#define STB0899_GPIO09 0xf149 ++#define STB0899_GPIO10 0xf14a ++#define STB0899_GPIO11 0xf14b ++#define STB0899_GPIO12 0xf14c ++#define STB0899_GPIO13 0xf14d ++#define STB0899_GPIO14 0xf14e ++#define STB0899_GPIO15 0xf14f ++#define STB0899_GPIO16 0xf150 ++#define STB0899_GPIO17 0xf151 ++#define STB0899_GPIO18 0xf152 ++#define STB0899_GPIO19 0xf153 ++#define STB0899_GPIO20 0xf154 ++ ++#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */ ++#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */ ++ ++#define STB0899_POSTPROC_GPIO_POWER 0x00 ++#define STB0899_POSTPROC_GPIO_LOCK 0x01 ++ ++/* ++ * Post process output configuration control ++ * 1. POWER ON/OFF (index 0) ++ * 2. FE_HAS_LOCK/LOCK_LOSS (index 1) ++ * ++ * @gpio = one of the above listed GPIO's ++ * @level = output state: pulled up or low ++ */ ++struct stb0899_postproc { ++ u16 gpio; ++ u8 level; ++}; ++ ++struct stb0899_config { ++ const struct stb0899_s1_reg *init_dev; ++ const struct stb0899_s2_reg *init_s2_demod; ++ const struct stb0899_s1_reg *init_s1_demod; ++ const struct stb0899_s2_reg *init_s2_fec; ++ const struct stb0899_s1_reg *init_tst; ++ ++ const struct stb0899_postproc *postproc; ++ ++ enum stb0899_inversion inversion; ++ ++ u32 xtal_freq; ++ ++ u8 demod_address; ++ u8 ts_output_mode; ++ u8 block_sync_mode; ++ u8 ts_pfbit_toggle; ++ ++ u8 clock_polarity; ++ u8 data_clk_parity; ++ u8 fec_mode; ++ u8 data_output_ctl; ++ u8 data_fifo_mode; ++ u8 out_rate_comp; ++ u8 i2c_repeater; ++// int inversion; ++ int lo_clk; ++ int hi_clk; ++ ++ u32 esno_ave; ++ u32 esno_quant; ++ u32 avframes_coarse; ++ u32 avframes_fine; ++ u32 miss_threshold; ++ u32 uwp_threshold_acq; ++ u32 uwp_threshold_track; ++ u32 uwp_threshold_sof; ++ u32 sof_search_timeout; ++ ++ u32 btr_nco_bits; ++ u32 btr_gain_shift_offset; ++ u32 crl_nco_bits; ++ u32 ldpc_max_iter; ++ ++ int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency); ++ int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency); ++ int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); ++ int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); ++ int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); ++}; ++ ++#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, ++ struct i2c_adapter *i2c); ++ ++#else ++ ++static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif //CONFIG_DVB_STB0899 ++ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stb0899_priv.h b/drivers/media/dvb-frontends/stb0899_priv.h +new file mode 100644 +index 0000000..82395b9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_priv.h +@@ -0,0 +1,263 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STB0899_PRIV_H ++#define __STB0899_PRIV_H ++ ++#include "dvb_frontend.h" ++#include "stb0899_drv.h" ++ ++#define FE_ERROR 0 ++#define FE_NOTICE 1 ++#define FE_INFO 2 ++#define FE_DEBUG 3 ++#define FE_DEBUGREG 4 ++ ++#define dprintk(x, y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((*x > FE_ERROR) && (*x > y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((*x > FE_NOTICE) && (*x > y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((*x > FE_INFO) && (*x > y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((*x > FE_DEBUG) && (*x > y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (*x > y) \ ++ printk(format, ##arg); \ ++ } \ ++} while(0) ++ ++#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ ++ ((y <= val) && (val <= x)) ? 1 : 0) ++ ++#define BYTE0 0 ++#define BYTE1 8 ++#define BYTE2 16 ++#define BYTE3 24 ++ ++#define GETBYTE(x, y) (((x) >> (y)) & 0xff) ++#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) ++#define MAKEWORD16(a, b) (((a) << 8) | (b)) ++ ++#define LSB(x) ((x & 0xff)) ++#define MSB(y) ((y >> 8) & 0xff) ++ ++ ++#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1)) ++ ++ ++#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \ ++ offset))) | ((val & \ ++ ((1 << width) - 1)) << offset) ++ ++#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\ ++ STB0899_OFFST_##bitf))) | \ ++ (val << STB0899_OFFST_##bitf)) ++ ++ ++enum stb0899_status { ++ NOAGC1 = 0, ++ AGC1OK, ++ NOTIMING, ++ ANALOGCARRIER, ++ TIMINGOK, ++ NOAGC2, ++ AGC2OK, ++ NOCARRIER, ++ CARRIEROK, ++ NODATA, ++ FALSELOCK, ++ DATAOK, ++ OUTOFRANGE, ++ RANGEOK, ++ DVBS2_DEMOD_LOCK, ++ DVBS2_DEMOD_NOLOCK, ++ DVBS2_FEC_LOCK, ++ DVBS2_FEC_NOLOCK ++}; ++ ++enum stb0899_modcod { ++ STB0899_DUMMY_PLF, ++ STB0899_QPSK_14, ++ STB0899_QPSK_13, ++ STB0899_QPSK_25, ++ STB0899_QPSK_12, ++ STB0899_QPSK_35, ++ STB0899_QPSK_23, ++ STB0899_QPSK_34, ++ STB0899_QPSK_45, ++ STB0899_QPSK_56, ++ STB0899_QPSK_89, ++ STB0899_QPSK_910, ++ STB0899_8PSK_35, ++ STB0899_8PSK_23, ++ STB0899_8PSK_34, ++ STB0899_8PSK_56, ++ STB0899_8PSK_89, ++ STB0899_8PSK_910, ++ STB0899_16APSK_23, ++ STB0899_16APSK_34, ++ STB0899_16APSK_45, ++ STB0899_16APSK_56, ++ STB0899_16APSK_89, ++ STB0899_16APSK_910, ++ STB0899_32APSK_34, ++ STB0899_32APSK_45, ++ STB0899_32APSK_56, ++ STB0899_32APSK_89, ++ STB0899_32APSK_910 ++}; ++ ++enum stb0899_frame { ++ STB0899_LONG_FRAME, ++ STB0899_SHORT_FRAME ++}; ++ ++enum stb0899_alpha { ++ RRC_20, ++ RRC_25, ++ RRC_35 ++}; ++ ++struct stb0899_tab { ++ s32 real; ++ s32 read; ++}; ++ ++enum stb0899_fec { ++ STB0899_FEC_1_2 = 13, ++ STB0899_FEC_2_3 = 18, ++ STB0899_FEC_3_4 = 21, ++ STB0899_FEC_5_6 = 24, ++ STB0899_FEC_6_7 = 25, ++ STB0899_FEC_7_8 = 26 ++}; ++ ++struct stb0899_params { ++ u32 freq; /* Frequency */ ++ u32 srate; /* Symbol rate */ ++ enum fe_code_rate fecrate; ++}; ++ ++struct stb0899_internal { ++ u32 master_clk; ++ u32 freq; /* Demod internal Frequency */ ++ u32 srate; /* Demod internal Symbol rate */ ++ enum stb0899_fec fecrate; /* Demod internal FEC rate */ ++ s32 srch_range; /* Demod internal Search Range */ ++ s32 sub_range; /* Demod current sub range (Hz) */ ++ s32 tuner_step; /* Tuner step (Hz) */ ++ s32 tuner_offst; /* Relative offset to carrier (Hz) */ ++ u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */ ++ ++ s32 mclk; /* Masterclock Divider factor (binary) */ ++ s32 rolloff; /* Current RollOff of the filter (x100) */ ++ ++ s16 derot_freq; /* Current derotator frequency (Hz) */ ++ s16 derot_percent; ++ ++ s16 direction; /* Current derotator search direction */ ++ s16 derot_step; /* Derotator step (binary value) */ ++ s16 t_derot; /* Derotator time constant (ms) */ ++ s16 t_data; /* Data recovery time constant (ms) */ ++ s16 sub_dir; /* Direction of the next sub range */ ++ ++ s16 t_agc1; /* Agc1 time constant (ms) */ ++ s16 t_agc2; /* Agc2 time constant (ms) */ ++ ++ u32 lock; /* Demod internal lock state */ ++ enum stb0899_status status; /* Demod internal status */ ++ ++ /* DVB-S2 */ ++ s32 agc_gain; /* RF AGC Gain */ ++ s32 center_freq; /* Nominal carrier frequency */ ++ s32 av_frame_coarse; /* Coarse carrier freq search frames */ ++ s32 av_frame_fine; /* Fine carrier freq search frames */ ++ ++ s16 step_size; /* Carrier frequency search step size */ ++ ++ enum stb0899_alpha rrc_alpha; ++ enum stb0899_inversion inversion; ++ enum stb0899_modcod modcod; ++ u8 pilots; /* Pilots found */ ++ ++ enum stb0899_frame frame_length; ++ u8 v_status; /* VSTATUS */ ++ u8 err_ctrl; /* ERRCTRLn */ ++}; ++ ++struct stb0899_state { ++ struct i2c_adapter *i2c; ++ struct stb0899_config *config; ++ struct dvb_frontend frontend; ++ ++ u32 *verbose; /* Cached module verbosity level */ ++ ++ struct stb0899_internal internal; /* Device internal parameters */ ++ ++ /* cached params from API */ ++ enum fe_delivery_system delsys; ++ struct stb0899_params params; ++ ++ u32 rx_freq; /* DiSEqC 2.0 receiver freq */ ++ struct mutex search_lock; ++}; ++/* stb0899.c */ ++extern int stb0899_read_reg(struct stb0899_state *state, ++ unsigned int reg); ++ ++extern u32 _stb0899_read_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset); ++ ++extern int stb0899_read_regs(struct stb0899_state *state, ++ unsigned int reg, u8 *buf, ++ u32 count); ++ ++extern int stb0899_write_regs(struct stb0899_state *state, ++ unsigned int reg, u8 *data, ++ u32 count); ++ ++extern int stb0899_write_reg(struct stb0899_state *state, ++ unsigned int reg, ++ u8 data); ++ ++extern int stb0899_write_s2reg(struct stb0899_state *state, ++ u32 stb0899_i2cdev, ++ u32 stb0899_base_addr, ++ u16 stb0899_reg_offset, ++ u32 stb0899_data); ++ ++extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); ++ ++ ++#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG)) ++//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA)) ++ ++/* stb0899_algo.c */ ++extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state); ++extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state); ++extern long stb0899_carr_width(struct stb0899_state *state); ++ ++#endif //__STB0899_PRIV_H +diff --git a/drivers/media/dvb-frontends/stb0899_reg.h b/drivers/media/dvb-frontends/stb0899_reg.h +new file mode 100644 +index 0000000..ba1ed56 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb0899_reg.h +@@ -0,0 +1,2027 @@ ++/* ++ STB0899 Multistandard Frontend driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STB0899_REG_H ++#define __STB0899_REG_H ++ ++/* S1 */ ++#define STB0899_DEV_ID 0xf000 ++#define STB0899_CHIP_ID (0x0f << 4) ++#define STB0899_OFFST_CHIP_ID 4 ++#define STB0899_WIDTH_CHIP_ID 4 ++#define STB0899_CHIP_REL (0x0f << 0) ++#define STB0899_OFFST_CHIP_REL 0 ++#define STB0899_WIDTH_CHIP_REL 4 ++ ++#define STB0899_DEMOD 0xf40e ++#define STB0899_MODECOEFF (0x01 << 0) ++#define STB0899_OFFST_MODECOEFF 0 ++#define STB0899_WIDTH_MODECOEFF 1 ++ ++#define STB0899_RCOMPC 0xf410 ++#define STB0899_AGC1CN 0xf412 ++#define STB0899_AGC1REF 0xf413 ++#define STB0899_RTC 0xf417 ++#define STB0899_TMGCFG 0xf418 ++#define STB0899_AGC2REF 0xf419 ++#define STB0899_TLSR 0xf41a ++ ++#define STB0899_CFD 0xf41b ++#define STB0899_CFD_ON (0x01 << 7) ++#define STB0899_OFFST_CFD_ON 7 ++#define STB0899_WIDTH_CFD_ON 1 ++ ++#define STB0899_ACLC 0xf41c ++ ++#define STB0899_BCLC 0xf41d ++#define STB0899_OFFST_ALGO 6 ++#define STB0899_WIDTH_ALGO_QPSK2 2 ++#define STB0899_ALGO_QPSK2 (2 << 6) ++#define STB0899_ALGO_QPSK1 (1 << 6) ++#define STB0899_ALGO_BPSK (0 << 6) ++#define STB0899_OFFST_BETA 0 ++#define STB0899_WIDTH_BETA 6 ++ ++#define STB0899_EQON 0xf41e ++#define STB0899_LDT 0xf41f ++#define STB0899_LDT2 0xf420 ++#define STB0899_EQUALREF 0xf425 ++#define STB0899_TMGRAMP 0xf426 ++#define STB0899_TMGTHD 0xf427 ++#define STB0899_IDCCOMP 0xf428 ++#define STB0899_QDCCOMP 0xf429 ++#define STB0899_POWERI 0xf42a ++#define STB0899_POWERQ 0xf42b ++#define STB0899_RCOMP 0xf42c ++ ++#define STB0899_AGCIQIN 0xf42e ++#define STB0899_AGCIQVALUE (0xff << 0) ++#define STB0899_OFFST_AGCIQVALUE 0 ++#define STB0899_WIDTH_AGCIQVALUE 8 ++ ++#define STB0899_AGC2I1 0xf436 ++#define STB0899_AGC2I2 0xf437 ++ ++#define STB0899_TLIR 0xf438 ++#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0) ++#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0 ++#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8 ++ ++#define STB0899_RTF 0xf439 ++#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0) ++#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0 ++#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8 ++ ++#define STB0899_DSTATUS 0xf43a ++#define STB0899_CARRIER_FOUND (0x01 << 7) ++#define STB0899_OFFST_CARRIER_FOUND 7 ++#define STB0899_WIDTH_CARRIER_FOUND 1 ++#define STB0899_TMG_LOCK (0x01 << 6) ++#define STB0899_OFFST_TMG_LOCK 6 ++#define STB0899_WIDTH_TMG_LOCK 1 ++#define STB0899_DEMOD_LOCK (0x01 << 5) ++#define STB0899_OFFST_DEMOD_LOCK 5 ++#define STB0899_WIDTH_DEMOD_LOCK 1 ++#define STB0899_TMG_AUTO (0x01 << 4) ++#define STB0899_OFFST_TMG_AUTO 4 ++#define STB0899_WIDTH_TMG_AUTO 1 ++#define STB0899_END_MAIN (0x01 << 3) ++#define STB0899_OFFST_END_MAIN 3 ++#define STB0899_WIDTH_END_MAIN 1 ++ ++#define STB0899_LDI 0xf43b ++#define STB0899_OFFST_LDI 0 ++#define STB0899_WIDTH_LDI 8 ++ ++#define STB0899_CFRM 0xf43e ++#define STB0899_OFFST_CFRM 0 ++#define STB0899_WIDTH_CFRM 8 ++ ++#define STB0899_CFRL 0xf43f ++#define STB0899_OFFST_CFRL 0 ++#define STB0899_WIDTH_CFRL 8 ++ ++#define STB0899_NIRM 0xf440 ++#define STB0899_OFFST_NIRM 0 ++#define STB0899_WIDTH_NIRM 8 ++ ++#define STB0899_NIRL 0xf441 ++#define STB0899_OFFST_NIRL 0 ++#define STB0899_WIDTH_NIRL 8 ++ ++#define STB0899_ISYMB 0xf444 ++#define STB0899_QSYMB 0xf445 ++ ++#define STB0899_SFRH 0xf446 ++#define STB0899_OFFST_SFRH 0 ++#define STB0899_WIDTH_SFRH 8 ++ ++#define STB0899_SFRM 0xf447 ++#define STB0899_OFFST_SFRM 0 ++#define STB0899_WIDTH_SFRM 8 ++ ++#define STB0899_SFRL 0xf448 ++#define STB0899_OFFST_SFRL 4 ++#define STB0899_WIDTH_SFRL 4 ++ ++#define STB0899_SFRUPH 0xf44c ++#define STB0899_SFRUPM 0xf44d ++#define STB0899_SFRUPL 0xf44e ++ ++#define STB0899_EQUAI1 0xf4e0 ++#define STB0899_EQUAQ1 0xf4e1 ++#define STB0899_EQUAI2 0xf4e2 ++#define STB0899_EQUAQ2 0xf4e3 ++#define STB0899_EQUAI3 0xf4e4 ++#define STB0899_EQUAQ3 0xf4e5 ++#define STB0899_EQUAI4 0xf4e6 ++#define STB0899_EQUAQ4 0xf4e7 ++#define STB0899_EQUAI5 0xf4e8 ++#define STB0899_EQUAQ5 0xf4e9 ++ ++#define STB0899_DSTATUS2 0xf50c ++#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7) ++#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7 ++#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1 ++#define STB0899_DS2_END_MAINLOOP (0x01 << 6) ++#define STB0899_OFFST_DS2_END_MAINLOOP 6 ++#define STB0899_WIDTH_DS2_END_MAINLOOP 1 ++#define STB0899_DS2_CFSYNC (0x01 << 5) ++#define STB0899_OFFST_DS2_CFSYNC 5 ++#define STB0899_WIDTH_DS2_CFSYNC 1 ++#define STB0899_DS2_TMGLOCK (0x01 << 4) ++#define STB0899_OFFST_DS2_TMGLOCK 4 ++#define STB0899_WIDTH_DS2_TMGLOCK 1 ++#define STB0899_DS2_DEMODWAIT (0x01 << 3) ++#define STB0899_OFFST_DS2_DEMODWAIT 3 ++#define STB0899_WIDTH_DS2_DEMODWAIT 1 ++#define STB0899_DS2_FECON (0x01 << 1) ++#define STB0899_OFFST_DS2_FECON 1 ++#define STB0899_WIDTH_DS2_FECON 1 ++ ++/* S1 FEC */ ++#define STB0899_VSTATUS 0xf50d ++#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7) ++#define STB0899_OFFST_VSTATUS_VITERBI_ON 7 ++#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1 ++#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6) ++#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6 ++#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1 ++#define STB0899_VSTATUS_PRFVIT (0x01 << 4) ++#define STB0899_OFFST_VSTATUS_PRFVIT 4 ++#define STB0899_WIDTH_VSTATUS_PRFVIT 1 ++#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3) ++#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3 ++#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1 ++ ++#define STB0899_VERROR 0xf50f ++ ++#define STB0899_IQSWAP 0xf523 ++#define STB0899_SYM (0x01 << 3) ++#define STB0899_OFFST_SYM 3 ++#define STB0899_WIDTH_SYM 1 ++ ++#define STB0899_FECAUTO1 0xf530 ++#define STB0899_DSSSRCH (0x01 << 3) ++#define STB0899_OFFST_DSSSRCH 3 ++#define STB0899_WIDTH_DSSSRCH 1 ++#define STB0899_SYMSRCH (0x01 << 2) ++#define STB0899_OFFST_SYMSRCH 2 ++#define STB0899_WIDTH_SYMSRCH 1 ++#define STB0899_QPSKSRCH (0x01 << 1) ++#define STB0899_OFFST_QPSKSRCH 1 ++#define STB0899_WIDTH_QPSKSRCH 1 ++#define STB0899_BPSKSRCH (0x01 << 0) ++#define STB0899_OFFST_BPSKSRCH 0 ++#define STB0899_WIDTH_BPSKSRCH 1 ++ ++#define STB0899_FECM 0xf533 ++#define STB0899_FECM_NOT_DVB (0x01 << 7) ++#define STB0899_OFFST_FECM_NOT_DVB 7 ++#define STB0899_WIDTH_FECM_NOT_DVB 1 ++#define STB0899_FECM_RSVD1 (0x07 << 4) ++#define STB0899_OFFST_FECM_RSVD1 4 ++#define STB0899_WIDTH_FECM_RSVD1 3 ++#define STB0899_FECM_VITERBI_ON (0x01 << 3) ++#define STB0899_OFFST_FECM_VITERBI_ON 3 ++#define STB0899_WIDTH_FECM_VITERBI_ON 1 ++#define STB0899_FECM_RSVD0 (0x01 << 2) ++#define STB0899_OFFST_FECM_RSVD0 2 ++#define STB0899_WIDTH_FECM_RSVD0 1 ++#define STB0899_FECM_SYNCDIS (0x01 << 1) ++#define STB0899_OFFST_FECM_SYNCDIS 1 ++#define STB0899_WIDTH_FECM_SYNCDIS 1 ++#define STB0899_FECM_SYMI (0x01 << 0) ++#define STB0899_OFFST_FECM_SYMI 0 ++#define STB0899_WIDTH_FECM_SYMI 1 ++ ++#define STB0899_VTH12 0xf534 ++#define STB0899_VTH23 0xf535 ++#define STB0899_VTH34 0xf536 ++#define STB0899_VTH56 0xf537 ++#define STB0899_VTH67 0xf538 ++#define STB0899_VTH78 0xf539 ++ ++#define STB0899_PRVIT 0xf53c ++#define STB0899_PR_7_8 (0x01 << 5) ++#define STB0899_OFFST_PR_7_8 5 ++#define STB0899_WIDTH_PR_7_8 1 ++#define STB0899_PR_6_7 (0x01 << 4) ++#define STB0899_OFFST_PR_6_7 4 ++#define STB0899_WIDTH_PR_6_7 1 ++#define STB0899_PR_5_6 (0x01 << 3) ++#define STB0899_OFFST_PR_5_6 3 ++#define STB0899_WIDTH_PR_5_6 1 ++#define STB0899_PR_3_4 (0x01 << 2) ++#define STB0899_OFFST_PR_3_4 2 ++#define STB0899_WIDTH_PR_3_4 1 ++#define STB0899_PR_2_3 (0x01 << 1) ++#define STB0899_OFFST_PR_2_3 1 ++#define STB0899_WIDTH_PR_2_3 1 ++#define STB0899_PR_1_2 (0x01 << 0) ++#define STB0899_OFFST_PR_1_2 0 ++#define STB0899_WIDTH_PR_1_2 1 ++ ++#define STB0899_VITSYNC 0xf53d ++#define STB0899_AM (0x01 << 7) ++#define STB0899_OFFST_AM 7 ++#define STB0899_WIDTH_AM 1 ++#define STB0899_FREEZE (0x01 << 6) ++#define STB0899_OFFST_FREEZE 6 ++#define STB0899_WIDTH_FREEZE 1 ++#define STB0899_SN_65536 (0x03 << 4) ++#define STB0899_OFFST_SN_65536 4 ++#define STB0899_WIDTH_SN_65536 2 ++#define STB0899_SN_16384 (0x01 << 5) ++#define STB0899_OFFST_SN_16384 5 ++#define STB0899_WIDTH_SN_16384 1 ++#define STB0899_SN_4096 (0x01 << 4) ++#define STB0899_OFFST_SN_4096 4 ++#define STB0899_WIDTH_SN_4096 1 ++#define STB0899_SN_1024 (0x00 << 4) ++#define STB0899_OFFST_SN_1024 4 ++#define STB0899_WIDTH_SN_1024 0 ++#define STB0899_TO_128 (0x03 << 2) ++#define STB0899_OFFST_TO_128 2 ++#define STB0899_WIDTH_TO_128 2 ++#define STB0899_TO_64 (0x01 << 3) ++#define STB0899_OFFST_TO_64 3 ++#define STB0899_WIDTH_TO_64 1 ++#define STB0899_TO_32 (0x01 << 2) ++#define STB0899_OFFST_TO_32 2 ++#define STB0899_WIDTH_TO_32 1 ++#define STB0899_TO_16 (0x00 << 2) ++#define STB0899_OFFST_TO_16 2 ++#define STB0899_WIDTH_TO_16 0 ++#define STB0899_HYST_128 (0x03 << 1) ++#define STB0899_OFFST_HYST_128 1 ++#define STB0899_WIDTH_HYST_128 2 ++#define STB0899_HYST_64 (0x01 << 1) ++#define STB0899_OFFST_HYST_64 1 ++#define STB0899_WIDTH_HYST_64 1 ++#define STB0899_HYST_32 (0x01 << 0) ++#define STB0899_OFFST_HYST_32 0 ++#define STB0899_WIDTH_HYST_32 1 ++#define STB0899_HYST_16 (0x00 << 0) ++#define STB0899_OFFST_HYST_16 0 ++#define STB0899_WIDTH_HYST_16 0 ++ ++#define STB0899_RSULC 0xf548 ++#define STB0899_ULDIL_ON (0x01 << 7) ++#define STB0899_OFFST_ULDIL_ON 7 ++#define STB0899_WIDTH_ULDIL_ON 1 ++#define STB0899_ULAUTO_ON (0x01 << 6) ++#define STB0899_OFFST_ULAUTO_ON 6 ++#define STB0899_WIDTH_ULAUTO_ON 1 ++#define STB0899_ULRS_ON (0x01 << 5) ++#define STB0899_OFFST_ULRS_ON 5 ++#define STB0899_WIDTH_ULRS_ON 1 ++#define STB0899_ULDESCRAM_ON (0x01 << 4) ++#define STB0899_OFFST_ULDESCRAM_ON 4 ++#define STB0899_WIDTH_ULDESCRAM_ON 1 ++#define STB0899_UL_DISABLE (0x01 << 2) ++#define STB0899_OFFST_UL_DISABLE 2 ++#define STB0899_WIDTH_UL_DISABLE 1 ++#define STB0899_NOFTHRESHOLD (0x01 << 0) ++#define STB0899_OFFST_NOFTHRESHOLD 0 ++#define STB0899_WIDTH_NOFTHRESHOLD 1 ++ ++#define STB0899_RSLLC 0xf54a ++#define STB0899_DEMAPVIT 0xf583 ++#define STB0899_DEMAPVIT_RSVD (0x01 << 7) ++#define STB0899_OFFST_DEMAPVIT_RSVD 7 ++#define STB0899_WIDTH_DEMAPVIT_RSVD 1 ++#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0) ++#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0 ++#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7 ++ ++#define STB0899_PLPARM 0xf58c ++#define STB0899_VITMAPPING (0x07 << 5) ++#define STB0899_OFFST_VITMAPPING 5 ++#define STB0899_WIDTH_VITMAPPING 3 ++#define STB0899_VITMAPPING_BPSK (0x01 << 5) ++#define STB0899_OFFST_VITMAPPING_BPSK 5 ++#define STB0899_WIDTH_VITMAPPING_BPSK 1 ++#define STB0899_VITMAPPING_QPSK (0x00 << 5) ++#define STB0899_OFFST_VITMAPPING_QPSK 5 ++#define STB0899_WIDTH_VITMAPPING_QPSK 0 ++#define STB0899_VITCURPUN (0x1f << 0) ++#define STB0899_OFFST_VITCURPUN 0 ++#define STB0899_WIDTH_VITCURPUN 5 ++#define STB0899_VITCURPUN_1_2 (0x0d << 0) ++#define STB0899_VITCURPUN_2_3 (0x12 << 0) ++#define STB0899_VITCURPUN_3_4 (0x15 << 0) ++#define STB0899_VITCURPUN_5_6 (0x18 << 0) ++#define STB0899_VITCURPUN_6_7 (0x19 << 0) ++#define STB0899_VITCURPUN_7_8 (0x1a << 0) ++ ++/* S2 DEMOD */ ++#define STB0899_OFF0_DMD_STATUS 0xf300 ++#define STB0899_BASE_DMD_STATUS 0x00000000 ++#define STB0899_IF_AGC_LOCK (0x01 << 8) ++#define STB0899_OFFST_IF_AGC_LOCK 0 ++#define STB0899_WIDTH_IF_AGC_LOCK 1 ++ ++#define STB0899_OFF0_CRL_FREQ 0xf304 ++#define STB0899_BASE_CRL_FREQ 0x00000000 ++#define STB0899_CARR_FREQ (0x3fffffff << 0) ++#define STB0899_OFFST_CARR_FREQ 0 ++#define STB0899_WIDTH_CARR_FREQ 30 ++ ++#define STB0899_OFF0_BTR_FREQ 0xf308 ++#define STB0899_BASE_BTR_FREQ 0x00000000 ++#define STB0899_BTR_FREQ (0xfffffff << 0) ++#define STB0899_OFFST_BTR_FREQ 0 ++#define STB0899_WIDTH_BTR_FREQ 28 ++ ++#define STB0899_OFF0_IF_AGC_GAIN 0xf30c ++#define STB0899_BASE_IF_AGC_GAIN 0x00000000 ++#define STB0899_IF_AGC_GAIN (0x3fff < 0) ++#define STB0899_OFFST_IF_AGC_GAIN 0 ++#define STB0899_WIDTH_IF_AGC_GAIN 14 ++ ++#define STB0899_OFF0_BB_AGC_GAIN 0xf310 ++#define STB0899_BASE_BB_AGC_GAIN 0x00000000 ++#define STB0899_BB_AGC_GAIN (0x3fff < 0) ++#define STB0899_OFFST_BB_AGC_GAIN 0 ++#define STB0899_WIDTH_BB_AGC_GAIN 14 ++ ++#define STB0899_OFF0_DC_OFFSET 0xf314 ++#define STB0899_BASE_DC_OFFSET 0x00000000 ++#define STB0899_I (0xff < 8) ++#define STB0899_OFFST_I 8 ++#define STB0899_WIDTH_I 8 ++#define STB0899_Q (0xff < 0) ++#define STB0899_OFFST_Q 8 ++#define STB0899_WIDTH_Q 8 ++ ++#define STB0899_OFF0_DMD_CNTRL 0xf31c ++#define STB0899_BASE_DMD_CNTRL 0x00000000 ++#define STB0899_ADC0_PINS1IN (0x01 << 6) ++#define STB0899_OFFST_ADC0_PINS1IN 6 ++#define STB0899_WIDTH_ADC0_PINS1IN 1 ++#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3) ++#define STB0899_OFFST_IN2COMP1_OFFBIN0 3 ++#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1 ++#define STB0899_DC_COMP (0x01 << 2) ++#define STB0899_OFFST_DC_COMP 2 ++#define STB0899_WIDTH_DC_COMP 1 ++#define STB0899_MODMODE (0x03 << 0) ++#define STB0899_OFFST_MODMODE 0 ++#define STB0899_WIDTH_MODMODE 2 ++ ++#define STB0899_OFF0_IF_AGC_CNTRL 0xf320 ++#define STB0899_BASE_IF_AGC_CNTRL 0x00000000 ++#define STB0899_IF_GAIN_INIT (0x3fff << 13) ++#define STB0899_OFFST_IF_GAIN_INIT 13 ++#define STB0899_WIDTH_IF_GAIN_INIT 14 ++#define STB0899_IF_GAIN_SENSE (0x01 << 12) ++#define STB0899_OFFST_IF_GAIN_SENSE 12 ++#define STB0899_WIDTH_IF_GAIN_SENSE 1 ++#define STB0899_IF_LOOP_GAIN (0x0f << 8) ++#define STB0899_OFFST_IF_LOOP_GAIN 8 ++#define STB0899_WIDTH_IF_LOOP_GAIN 4 ++#define STB0899_IF_LD_GAIN_INIT (0x01 << 7) ++#define STB0899_OFFST_IF_LD_GAIN_INIT 7 ++#define STB0899_WIDTH_IF_LD_GAIN_INIT 1 ++#define STB0899_IF_AGC_REF (0x7f << 0) ++#define STB0899_OFFST_IF_AGC_REF 0 ++#define STB0899_WIDTH_IF_AGC_REF 7 ++ ++#define STB0899_OFF0_BB_AGC_CNTRL 0xf324 ++#define STB0899_BASE_BB_AGC_CNTRL 0x00000000 ++#define STB0899_BB_GAIN_INIT (0x3fff << 12) ++#define STB0899_OFFST_BB_GAIN_INIT 12 ++#define STB0899_WIDTH_BB_GAIN_INIT 14 ++#define STB0899_BB_LOOP_GAIN (0x0f << 8) ++#define STB0899_OFFST_BB_LOOP_GAIN 8 ++#define STB0899_WIDTH_BB_LOOP_GAIN 4 ++#define STB0899_BB_LD_GAIN_INIT (0x01 << 7) ++#define STB0899_OFFST_BB_LD_GAIN_INIT 7 ++#define STB0899_WIDTH_BB_LD_GAIN_INIT 1 ++#define STB0899_BB_AGC_REF (0x7f << 0) ++#define STB0899_OFFST_BB_AGC_REF 0 ++#define STB0899_WIDTH_BB_AGC_REF 7 ++ ++#define STB0899_OFF0_CRL_CNTRL 0xf328 ++#define STB0899_BASE_CRL_CNTRL 0x00000000 ++#define STB0899_CRL_LOCK_CLEAR (0x01 << 5) ++#define STB0899_OFFST_CRL_LOCK_CLEAR 5 ++#define STB0899_WIDTH_CRL_LOCK_CLEAR 1 ++#define STB0899_CRL_SWPR_CLEAR (0x01 << 4) ++#define STB0899_OFFST_CRL_SWPR_CLEAR 4 ++#define STB0899_WIDTH_CRL_SWPR_CLEAR 1 ++#define STB0899_CRL_SWP_ENA (0x01 << 3) ++#define STB0899_OFFST_CRL_SWP_ENA 3 ++#define STB0899_WIDTH_CRL_SWP_ENA 1 ++#define STB0899_CRL_DET_SEL (0x01 << 2) ++#define STB0899_OFFST_CRL_DET_SEL 2 ++#define STB0899_WIDTH_CRL_DET_SEL 1 ++#define STB0899_CRL_SENSE (0x01 << 1) ++#define STB0899_OFFST_CRL_SENSE 1 ++#define STB0899_WIDTH_CRL_SENSE 1 ++#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0) ++#define STB0899_OFFST_CRL_PHSERR_CLEAR 0 ++#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1 ++ ++#define STB0899_OFF0_CRL_PHS_INIT 0xf32c ++#define STB0899_BASE_CRL_PHS_INIT 0x00000000 ++#define STB0899_CRL_PHS_INIT_31 (0x1 << 30) ++#define STB0899_OFFST_CRL_PHS_INIT_31 30 ++#define STB0899_WIDTH_CRL_PHS_INIT_31 1 ++#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24) ++#define STB0899_OFFST_CRL_LD_INIT_PHASE 24 ++#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1 ++#define STB0899_CRL_INIT_PHASE (0xffffff << 0) ++#define STB0899_OFFST_CRL_INIT_PHASE 0 ++#define STB0899_WIDTH_CRL_INIT_PHASE 24 ++ ++#define STB0899_OFF0_CRL_FREQ_INIT 0xf330 ++#define STB0899_BASE_CRL_FREQ_INIT 0x00000000 ++#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30) ++#define STB0899_OFFST_CRL_FREQ_INIT_31 30 ++#define STB0899_WIDTH_CRL_FREQ_INIT_31 1 ++#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24) ++#define STB0899_OFFST_CRL_LD_FREQ_INIT 24 ++#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1 ++#define STB0899_CRL_FREQ_INIT (0xffffff << 0) ++#define STB0899_OFFST_CRL_FREQ_INIT 0 ++#define STB0899_WIDTH_CRL_FREQ_INIT 24 ++ ++#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334 ++#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000 ++#define STB0899_KCRL2_RSHFT (0xf << 16) ++#define STB0899_OFFST_KCRL2_RSHFT 16 ++#define STB0899_WIDTH_KCRL2_RSHFT 4 ++#define STB0899_KCRL1 (0xf << 12) ++#define STB0899_OFFST_KCRL1 12 ++#define STB0899_WIDTH_KCRL1 4 ++#define STB0899_KCRL1_RSHFT (0xf << 8) ++#define STB0899_OFFST_KCRL1_RSHFT 8 ++#define STB0899_WIDTH_KCRL1_RSHFT 4 ++#define STB0899_KCRL0 (0xf << 4) ++#define STB0899_OFFST_KCRL0 4 ++#define STB0899_WIDTH_KCRL0 4 ++#define STB0899_KCRL0_RSHFT (0xf << 0) ++#define STB0899_OFFST_KCRL0_RSHFT 0 ++#define STB0899_WIDTH_KCRL0_RSHFT 4 ++ ++#define STB0899_OFF0_CRL_NOM_FREQ 0xf338 ++#define STB0899_BASE_CRL_NOM_FREQ 0x00000000 ++#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0) ++#define STB0899_OFFST_CRL_NOM_FREQ 0 ++#define STB0899_WIDTH_CRL_NOM_FREQ 30 ++ ++#define STB0899_OFF0_CRL_SWP_RATE 0xf33c ++#define STB0899_BASE_CRL_SWP_RATE 0x00000000 ++#define STB0899_CRL_SWP_RATE (0x3fffffff << 0) ++#define STB0899_OFFST_CRL_SWP_RATE 0 ++#define STB0899_WIDTH_CRL_SWP_RATE 30 ++ ++#define STB0899_OFF0_CRL_MAX_SWP 0xf340 ++#define STB0899_BASE_CRL_MAX_SWP 0x00000000 ++#define STB0899_CRL_MAX_SWP (0x3fffffff << 0) ++#define STB0899_OFFST_CRL_MAX_SWP 0 ++#define STB0899_WIDTH_CRL_MAX_SWP 30 ++ ++#define STB0899_OFF0_CRL_LK_CNTRL 0xf344 ++#define STB0899_BASE_CRL_LK_CNTRL 0x00000000 ++ ++#define STB0899_OFF0_DECIM_CNTRL 0xf348 ++#define STB0899_BASE_DECIM_CNTRL 0x00000000 ++#define STB0899_BAND_LIMIT_B (0x01 << 5) ++#define STB0899_OFFST_BAND_LIMIT_B 5 ++#define STB0899_WIDTH_BAND_LIMIT_B 1 ++#define STB0899_WIN_SEL (0x03 << 3) ++#define STB0899_OFFST_WIN_SEL 3 ++#define STB0899_WIDTH_WIN_SEL 2 ++#define STB0899_DECIM_RATE (0x07 << 0) ++#define STB0899_OFFST_DECIM_RATE 0 ++#define STB0899_WIDTH_DECIM_RATE 3 ++ ++#define STB0899_OFF0_BTR_CNTRL 0xf34c ++#define STB0899_BASE_BTR_CNTRL 0x00000000 ++#define STB0899_BTR_FREQ_CORR (0x7ff << 4) ++#define STB0899_OFFST_BTR_FREQ_CORR 4 ++#define STB0899_WIDTH_BTR_FREQ_CORR 11 ++#define STB0899_BTR_CLR_LOCK (0x01 << 3) ++#define STB0899_OFFST_BTR_CLR_LOCK 3 ++#define STB0899_WIDTH_BTR_CLR_LOCK 1 ++#define STB0899_BTR_SENSE (0x01 << 2) ++#define STB0899_OFFST_BTR_SENSE 2 ++#define STB0899_WIDTH_BTR_SENSE 1 ++#define STB0899_BTR_ERR_ENA (0x01 << 1) ++#define STB0899_OFFST_BTR_ERR_ENA 1 ++#define STB0899_WIDTH_BTR_ERR_ENA 1 ++#define STB0899_INTRP_PHS_SENSE (0x01 << 0) ++#define STB0899_OFFST_INTRP_PHS_SENSE 0 ++#define STB0899_WIDTH_INTRP_PHS_SENSE 1 ++ ++#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350 ++#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000 ++#define STB0899_KBTR2_RSHFT (0x0f << 16) ++#define STB0899_OFFST_KBTR2_RSHFT 16 ++#define STB0899_WIDTH_KBTR2_RSHFT 4 ++#define STB0899_KBTR1 (0x0f << 12) ++#define STB0899_OFFST_KBTR1 12 ++#define STB0899_WIDTH_KBTR1 4 ++#define STB0899_KBTR1_RSHFT (0x0f << 8) ++#define STB0899_OFFST_KBTR1_RSHFT 8 ++#define STB0899_WIDTH_KBTR1_RSHFT 4 ++#define STB0899_KBTR0 (0x0f << 4) ++#define STB0899_OFFST_KBTR0 4 ++#define STB0899_WIDTH_KBTR0 4 ++#define STB0899_KBTR0_RSHFT (0x0f << 0) ++#define STB0899_OFFST_KBTR0_RSHFT 0 ++#define STB0899_WIDTH_KBTR0_RSHFT 4 ++ ++#define STB0899_OFF0_BTR_PHS_INIT 0xf354 ++#define STB0899_BASE_BTR_PHS_INIT 0x00000000 ++#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28) ++#define STB0899_OFFST_BTR_LD_PHASE_INIT 28 ++#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1 ++#define STB0899_BTR_INIT_PHASE (0xfffffff << 0) ++#define STB0899_OFFST_BTR_INIT_PHASE 0 ++#define STB0899_WIDTH_BTR_INIT_PHASE 28 ++ ++#define STB0899_OFF0_BTR_FREQ_INIT 0xf358 ++#define STB0899_BASE_BTR_FREQ_INIT 0x00000000 ++#define STB0899_BTR_LD_FREQ_INIT (1 << 28) ++#define STB0899_OFFST_BTR_LD_FREQ_INIT 28 ++#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1 ++#define STB0899_BTR_FREQ_INIT (0xfffffff << 0) ++#define STB0899_OFFST_BTR_FREQ_INIT 0 ++#define STB0899_WIDTH_BTR_FREQ_INIT 28 ++ ++#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c ++#define STB0899_BASE_BTR_NOM_FREQ 0x00000000 ++#define STB0899_BTR_NOM_FREQ (0xfffffff << 0) ++#define STB0899_OFFST_BTR_NOM_FREQ 0 ++#define STB0899_WIDTH_BTR_NOM_FREQ 28 ++ ++#define STB0899_OFF0_BTR_LK_CNTRL 0xf360 ++#define STB0899_BASE_BTR_LK_CNTRL 0x00000000 ++#define STB0899_BTR_MIN_ENERGY (0x0f << 24) ++#define STB0899_OFFST_BTR_MIN_ENERGY 24 ++#define STB0899_WIDTH_BTR_MIN_ENERGY 4 ++#define STB0899_BTR_LOCK_TH_LO (0xff << 16) ++#define STB0899_OFFST_BTR_LOCK_TH_LO 16 ++#define STB0899_WIDTH_BTR_LOCK_TH_LO 8 ++#define STB0899_BTR_LOCK_TH_HI (0xff << 8) ++#define STB0899_OFFST_BTR_LOCK_TH_HI 8 ++#define STB0899_WIDTH_BTR_LOCK_TH_HI 8 ++#define STB0899_BTR_LOCK_GAIN (0x03 << 6) ++#define STB0899_OFFST_BTR_LOCK_GAIN 6 ++#define STB0899_WIDTH_BTR_LOCK_GAIN 2 ++#define STB0899_BTR_LOCK_LEAK (0x3f << 0) ++#define STB0899_OFFST_BTR_LOCK_LEAK 0 ++#define STB0899_WIDTH_BTR_LOCK_LEAK 6 ++ ++#define STB0899_OFF0_DECN_CNTRL 0xf364 ++#define STB0899_BASE_DECN_CNTRL 0x00000000 ++ ++#define STB0899_OFF0_TP_CNTRL 0xf368 ++#define STB0899_BASE_TP_CNTRL 0x00000000 ++ ++#define STB0899_OFF0_TP_BUF_STATUS 0xf36c ++#define STB0899_BASE_TP_BUF_STATUS 0x00000000 ++#define STB0899_TP_BUFFER_FULL (1 << 0) ++ ++#define STB0899_OFF0_DC_ESTIM 0xf37c ++#define STB0899_BASE_DC_ESTIM 0x0000 ++#define STB0899_I_DC_ESTIMATE (0xff << 8) ++#define STB0899_OFFST_I_DC_ESTIMATE 8 ++#define STB0899_WIDTH_I_DC_ESTIMATE 8 ++#define STB0899_Q_DC_ESTIMATE (0xff << 0) ++#define STB0899_OFFST_Q_DC_ESTIMATE 0 ++#define STB0899_WIDTH_Q_DC_ESTIMATE 8 ++ ++#define STB0899_OFF0_FLL_CNTRL 0xf310 ++#define STB0899_BASE_FLL_CNTRL 0x00000020 ++#define STB0899_CRL_FLL_ACC (0x01 << 4) ++#define STB0899_OFFST_CRL_FLL_ACC 4 ++#define STB0899_WIDTH_CRL_FLL_ACC 1 ++#define STB0899_FLL_AVG_PERIOD (0x0f << 0) ++#define STB0899_OFFST_FLL_AVG_PERIOD 0 ++#define STB0899_WIDTH_FLL_AVG_PERIOD 4 ++ ++#define STB0899_OFF0_FLL_FREQ_WD 0xf314 ++#define STB0899_BASE_FLL_FREQ_WD 0x00000020 ++#define STB0899_FLL_FREQ_WD (0xffffffff << 0) ++#define STB0899_OFFST_FLL_FREQ_WD 0 ++#define STB0899_WIDTH_FLL_FREQ_WD 32 ++ ++#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358 ++#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020 ++#define STB0899_ANTI_ALIAS_SELB (0x03 << 0) ++#define STB0899_OFFST_ANTI_ALIAS_SELB 0 ++#define STB0899_WIDTH_ANTI_ALIAS_SELB 2 ++ ++#define STB0899_OFF0_RRC_ALPHA 0xf35c ++#define STB0899_BASE_RRC_ALPHA 0x00000020 ++#define STB0899_RRC_ALPHA (0x03 << 0) ++#define STB0899_OFFST_RRC_ALPHA 0 ++#define STB0899_WIDTH_RRC_ALPHA 2 ++ ++#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360 ++#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020 ++#define STB0899_DC_ADAPT_LSHFT (0x077 << 0) ++#define STB0899_OFFST_DC_ADAPT_LSHFT 0 ++#define STB0899_WIDTH_DC_ADAPT_LSHFT 3 ++ ++#define STB0899_OFF0_IMB_OFFSET 0xf364 ++#define STB0899_BASE_IMB_OFFSET 0x00000020 ++#define STB0899_PHS_IMB_COMP (0xff << 8) ++#define STB0899_OFFST_PHS_IMB_COMP 8 ++#define STB0899_WIDTH_PHS_IMB_COMP 8 ++#define STB0899_AMPL_IMB_COMP (0xff << 0) ++#define STB0899_OFFST_AMPL_IMB_COMP 0 ++#define STB0899_WIDTH_AMPL_IMB_COMP 8 ++ ++#define STB0899_OFF0_IMB_ESTIMATE 0xf368 ++#define STB0899_BASE_IMB_ESTIMATE 0x00000020 ++#define STB0899_PHS_IMB_ESTIMATE (0xff << 8) ++#define STB0899_OFFST_PHS_IMB_ESTIMATE 8 ++#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8 ++#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0) ++#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0 ++#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8 ++ ++#define STB0899_OFF0_IMB_CNTRL 0xf36c ++#define STB0899_BASE_IMB_CNTRL 0x00000020 ++#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4) ++#define STB0899_OFFST_PHS_ADAPT_LSHFT 4 ++#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3 ++#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1) ++#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1 ++#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3 ++#define STB0899_IMB_COMP (0x01 << 0) ++#define STB0899_OFFST_IMB_COMP 0 ++#define STB0899_WIDTH_IMB_COMP 1 ++ ++#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374 ++#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020 ++#define STB0899_IF_AGC_LOCK_TH (0xff << 11) ++#define STB0899_OFFST_IF_AGC_LOCK_TH 11 ++#define STB0899_WIDTH_IF_AGC_LOCK_TH 8 ++#define STB0899_IF_AGC_SD_DIV (0xff << 3) ++#define STB0899_OFFST_IF_AGC_SD_DIV 3 ++#define STB0899_WIDTH_IF_AGC_SD_DIV 8 ++#define STB0899_IF_AGC_DUMP_PER (0x07 << 0) ++#define STB0899_OFFST_IF_AGC_DUMP_PER 0 ++#define STB0899_WIDTH_IF_AGC_DUMP_PER 3 ++ ++#define STB0899_OFF0_DMD_CNTRL2 0xf378 ++#define STB0899_BASE_DMD_CNTRL2 0x00000020 ++#define STB0899_SPECTRUM_INVERT (0x01 << 2) ++#define STB0899_OFFST_SPECTRUM_INVERT 2 ++#define STB0899_WIDTH_SPECTRUM_INVERT 1 ++#define STB0899_AGC_MODE (0x01 << 1) ++#define STB0899_OFFST_AGC_MODE 1 ++#define STB0899_WIDTH_AGC_MODE 1 ++#define STB0899_CRL_FREQ_ADJ (0x01 << 0) ++#define STB0899_OFFST_CRL_FREQ_ADJ 0 ++#define STB0899_WIDTH_CRL_FREQ_ADJ 1 ++ ++#define STB0899_OFF0_TP_BUFFER 0xf300 ++#define STB0899_BASE_TP_BUFFER 0x00000040 ++#define STB0899_TP_BUFFER_IN (0xffff << 0) ++#define STB0899_OFFST_TP_BUFFER_IN 0 ++#define STB0899_WIDTH_TP_BUFFER_IN 16 ++ ++#define STB0899_OFF0_TP_BUFFER1 0xf304 ++#define STB0899_BASE_TP_BUFFER1 0x00000040 ++#define STB0899_OFF0_TP_BUFFER2 0xf308 ++#define STB0899_BASE_TP_BUFFER2 0x00000040 ++#define STB0899_OFF0_TP_BUFFER3 0xf30c ++#define STB0899_BASE_TP_BUFFER3 0x00000040 ++#define STB0899_OFF0_TP_BUFFER4 0xf310 ++#define STB0899_BASE_TP_BUFFER4 0x00000040 ++#define STB0899_OFF0_TP_BUFFER5 0xf314 ++#define STB0899_BASE_TP_BUFFER5 0x00000040 ++#define STB0899_OFF0_TP_BUFFER6 0xf318 ++#define STB0899_BASE_TP_BUFFER6 0x00000040 ++#define STB0899_OFF0_TP_BUFFER7 0xf31c ++#define STB0899_BASE_TP_BUFFER7 0x00000040 ++#define STB0899_OFF0_TP_BUFFER8 0xf320 ++#define STB0899_BASE_TP_BUFFER8 0x00000040 ++#define STB0899_OFF0_TP_BUFFER9 0xf324 ++#define STB0899_BASE_TP_BUFFER9 0x00000040 ++#define STB0899_OFF0_TP_BUFFER10 0xf328 ++#define STB0899_BASE_TP_BUFFER10 0x00000040 ++#define STB0899_OFF0_TP_BUFFER11 0xf32c ++#define STB0899_BASE_TP_BUFFER11 0x00000040 ++#define STB0899_OFF0_TP_BUFFER12 0xf330 ++#define STB0899_BASE_TP_BUFFER12 0x00000040 ++#define STB0899_OFF0_TP_BUFFER13 0xf334 ++#define STB0899_BASE_TP_BUFFER13 0x00000040 ++#define STB0899_OFF0_TP_BUFFER14 0xf338 ++#define STB0899_BASE_TP_BUFFER14 0x00000040 ++#define STB0899_OFF0_TP_BUFFER15 0xf33c ++#define STB0899_BASE_TP_BUFFER15 0x00000040 ++#define STB0899_OFF0_TP_BUFFER16 0xf340 ++#define STB0899_BASE_TP_BUFFER16 0x00000040 ++#define STB0899_OFF0_TP_BUFFER17 0xf344 ++#define STB0899_BASE_TP_BUFFER17 0x00000040 ++#define STB0899_OFF0_TP_BUFFER18 0xf348 ++#define STB0899_BASE_TP_BUFFER18 0x00000040 ++#define STB0899_OFF0_TP_BUFFER19 0xf34c ++#define STB0899_BASE_TP_BUFFER19 0x00000040 ++#define STB0899_OFF0_TP_BUFFER20 0xf350 ++#define STB0899_BASE_TP_BUFFER20 0x00000040 ++#define STB0899_OFF0_TP_BUFFER21 0xf354 ++#define STB0899_BASE_TP_BUFFER21 0x00000040 ++#define STB0899_OFF0_TP_BUFFER22 0xf358 ++#define STB0899_BASE_TP_BUFFER22 0x00000040 ++#define STB0899_OFF0_TP_BUFFER23 0xf35c ++#define STB0899_BASE_TP_BUFFER23 0x00000040 ++#define STB0899_OFF0_TP_BUFFER24 0xf360 ++#define STB0899_BASE_TP_BUFFER24 0x00000040 ++#define STB0899_OFF0_TP_BUFFER25 0xf364 ++#define STB0899_BASE_TP_BUFFER25 0x00000040 ++#define STB0899_OFF0_TP_BUFFER26 0xf368 ++#define STB0899_BASE_TP_BUFFER26 0x00000040 ++#define STB0899_OFF0_TP_BUFFER27 0xf36c ++#define STB0899_BASE_TP_BUFFER27 0x00000040 ++#define STB0899_OFF0_TP_BUFFER28 0xf370 ++#define STB0899_BASE_TP_BUFFER28 0x00000040 ++#define STB0899_OFF0_TP_BUFFER29 0xf374 ++#define STB0899_BASE_TP_BUFFER29 0x00000040 ++#define STB0899_OFF0_TP_BUFFER30 0xf378 ++#define STB0899_BASE_TP_BUFFER30 0x00000040 ++#define STB0899_OFF0_TP_BUFFER31 0xf37c ++#define STB0899_BASE_TP_BUFFER31 0x00000040 ++#define STB0899_OFF0_TP_BUFFER32 0xf300 ++#define STB0899_BASE_TP_BUFFER32 0x00000060 ++#define STB0899_OFF0_TP_BUFFER33 0xf304 ++#define STB0899_BASE_TP_BUFFER33 0x00000060 ++#define STB0899_OFF0_TP_BUFFER34 0xf308 ++#define STB0899_BASE_TP_BUFFER34 0x00000060 ++#define STB0899_OFF0_TP_BUFFER35 0xf30c ++#define STB0899_BASE_TP_BUFFER35 0x00000060 ++#define STB0899_OFF0_TP_BUFFER36 0xf310 ++#define STB0899_BASE_TP_BUFFER36 0x00000060 ++#define STB0899_OFF0_TP_BUFFER37 0xf314 ++#define STB0899_BASE_TP_BUFFER37 0x00000060 ++#define STB0899_OFF0_TP_BUFFER38 0xf318 ++#define STB0899_BASE_TP_BUFFER38 0x00000060 ++#define STB0899_OFF0_TP_BUFFER39 0xf31c ++#define STB0899_BASE_TP_BUFFER39 0x00000060 ++#define STB0899_OFF0_TP_BUFFER40 0xf320 ++#define STB0899_BASE_TP_BUFFER40 0x00000060 ++#define STB0899_OFF0_TP_BUFFER41 0xf324 ++#define STB0899_BASE_TP_BUFFER41 0x00000060 ++#define STB0899_OFF0_TP_BUFFER42 0xf328 ++#define STB0899_BASE_TP_BUFFER42 0x00000060 ++#define STB0899_OFF0_TP_BUFFER43 0xf32c ++#define STB0899_BASE_TP_BUFFER43 0x00000060 ++#define STB0899_OFF0_TP_BUFFER44 0xf330 ++#define STB0899_BASE_TP_BUFFER44 0x00000060 ++#define STB0899_OFF0_TP_BUFFER45 0xf334 ++#define STB0899_BASE_TP_BUFFER45 0x00000060 ++#define STB0899_OFF0_TP_BUFFER46 0xf338 ++#define STB0899_BASE_TP_BUFFER46 0x00000060 ++#define STB0899_OFF0_TP_BUFFER47 0xf33c ++#define STB0899_BASE_TP_BUFFER47 0x00000060 ++#define STB0899_OFF0_TP_BUFFER48 0xf340 ++#define STB0899_BASE_TP_BUFFER48 0x00000060 ++#define STB0899_OFF0_TP_BUFFER49 0xf344 ++#define STB0899_BASE_TP_BUFFER49 0x00000060 ++#define STB0899_OFF0_TP_BUFFER50 0xf348 ++#define STB0899_BASE_TP_BUFFER50 0x00000060 ++#define STB0899_OFF0_TP_BUFFER51 0xf34c ++#define STB0899_BASE_TP_BUFFER51 0x00000060 ++#define STB0899_OFF0_TP_BUFFER52 0xf350 ++#define STB0899_BASE_TP_BUFFER52 0x00000060 ++#define STB0899_OFF0_TP_BUFFER53 0xf354 ++#define STB0899_BASE_TP_BUFFER53 0x00000060 ++#define STB0899_OFF0_TP_BUFFER54 0xf358 ++#define STB0899_BASE_TP_BUFFER54 0x00000060 ++#define STB0899_OFF0_TP_BUFFER55 0xf35c ++#define STB0899_BASE_TP_BUFFER55 0x00000060 ++#define STB0899_OFF0_TP_BUFFER56 0xf360 ++#define STB0899_BASE_TP_BUFFER56 0x00000060 ++#define STB0899_OFF0_TP_BUFFER57 0xf364 ++#define STB0899_BASE_TP_BUFFER57 0x00000060 ++#define STB0899_OFF0_TP_BUFFER58 0xf368 ++#define STB0899_BASE_TP_BUFFER58 0x00000060 ++#define STB0899_OFF0_TP_BUFFER59 0xf36c ++#define STB0899_BASE_TP_BUFFER59 0x00000060 ++#define STB0899_OFF0_TP_BUFFER60 0xf370 ++#define STB0899_BASE_TP_BUFFER60 0x00000060 ++#define STB0899_OFF0_TP_BUFFER61 0xf374 ++#define STB0899_BASE_TP_BUFFER61 0x00000060 ++#define STB0899_OFF0_TP_BUFFER62 0xf378 ++#define STB0899_BASE_TP_BUFFER62 0x00000060 ++#define STB0899_OFF0_TP_BUFFER63 0xf37c ++#define STB0899_BASE_TP_BUFFER63 0x00000060 ++ ++#define STB0899_OFF0_RESET_CNTRL 0xf300 ++#define STB0899_BASE_RESET_CNTRL 0x00000400 ++#define STB0899_DVBS2_RESET (0x01 << 0) ++#define STB0899_OFFST_DVBS2_RESET 0 ++#define STB0899_WIDTH_DVBS2_RESET 1 ++ ++#define STB0899_OFF0_ACM_ENABLE 0xf304 ++#define STB0899_BASE_ACM_ENABLE 0x00000400 ++#define STB0899_ACM_ENABLE 1 ++ ++#define STB0899_OFF0_DESCR_CNTRL 0xf30c ++#define STB0899_BASE_DESCR_CNTRL 0x00000400 ++#define STB0899_OFFST_DESCR_CNTRL 0 ++#define STB0899_WIDTH_DESCR_CNTRL 16 ++ ++#define STB0899_OFF0_UWP_CNTRL1 0xf320 ++#define STB0899_BASE_UWP_CNTRL1 0x00000400 ++#define STB0899_UWP_TH_SOF (0x7fff << 11) ++#define STB0899_OFFST_UWP_TH_SOF 11 ++#define STB0899_WIDTH_UWP_TH_SOF 15 ++#define STB0899_UWP_ESN0_QUANT (0xff << 3) ++#define STB0899_OFFST_UWP_ESN0_QUANT 3 ++#define STB0899_WIDTH_UWP_ESN0_QUANT 8 ++#define STB0899_UWP_ESN0_AVE (0x03 << 1) ++#define STB0899_OFFST_UWP_ESN0_AVE 1 ++#define STB0899_WIDTH_UWP_ESN0_AVE 2 ++#define STB0899_UWP_START (0x01 << 0) ++#define STB0899_OFFST_UWP_START 0 ++#define STB0899_WIDTH_UWP_START 1 ++ ++#define STB0899_OFF0_UWP_CNTRL2 0xf324 ++#define STB0899_BASE_UWP_CNTRL2 0x00000400 ++#define STB0899_UWP_MISS_TH (0xff << 16) ++#define STB0899_OFFST_UWP_MISS_TH 16 ++#define STB0899_WIDTH_UWP_MISS_TH 8 ++#define STB0899_FE_FINE_TRK (0xff << 8) ++#define STB0899_OFFST_FE_FINE_TRK 8 ++#define STB0899_WIDTH_FE_FINE_TRK 8 ++#define STB0899_FE_COARSE_TRK (0xff << 0) ++#define STB0899_OFFST_FE_COARSE_TRK 0 ++#define STB0899_WIDTH_FE_COARSE_TRK 8 ++ ++#define STB0899_OFF0_UWP_STAT1 0xf328 ++#define STB0899_BASE_UWP_STAT1 0x00000400 ++#define STB0899_UWP_STATE (0x03ff << 15) ++#define STB0899_OFFST_UWP_STATE 15 ++#define STB0899_WIDTH_UWP_STATE 10 ++#define STB0899_UW_MAX_PEAK (0x7fff << 0) ++#define STB0899_OFFST_UW_MAX_PEAK 0 ++#define STB0899_WIDTH_UW_MAX_PEAK 15 ++ ++#define STB0899_OFF0_UWP_STAT2 0xf32c ++#define STB0899_BASE_UWP_STAT2 0x00000400 ++#define STB0899_ESNO_EST (0x07ffff << 7) ++#define STB0899_OFFST_ESN0_EST 7 ++#define STB0899_WIDTH_ESN0_EST 19 ++#define STB0899_UWP_DECODE_MOD (0x7f << 0) ++#define STB0899_OFFST_UWP_DECODE_MOD 0 ++#define STB0899_WIDTH_UWP_DECODE_MOD 7 ++ ++#define STB0899_OFF0_DMD_CORE_ID 0xf334 ++#define STB0899_BASE_DMD_CORE_ID 0x00000400 ++#define STB0899_CORE_ID (0xffffffff << 0) ++#define STB0899_OFFST_CORE_ID 0 ++#define STB0899_WIDTH_CORE_ID 32 ++ ++#define STB0899_OFF0_DMD_VERSION_ID 0xf33c ++#define STB0899_BASE_DMD_VERSION_ID 0x00000400 ++#define STB0899_VERSION_ID (0xff << 0) ++#define STB0899_OFFST_VERSION_ID 0 ++#define STB0899_WIDTH_VERSION_ID 8 ++ ++#define STB0899_OFF0_DMD_STAT2 0xf340 ++#define STB0899_BASE_DMD_STAT2 0x00000400 ++#define STB0899_CSM_LOCK (0x01 << 1) ++#define STB0899_OFFST_CSM_LOCK 1 ++#define STB0899_WIDTH_CSM_LOCK 1 ++#define STB0899_UWP_LOCK (0x01 << 0) ++#define STB0899_OFFST_UWP_LOCK 0 ++#define STB0899_WIDTH_UWP_LOCK 1 ++ ++#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344 ++#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400 ++#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0) ++#define STB0899_OFFST_FREQ_ADJ_SCALE 0 ++#define STB0899_WIDTH_FREQ_ADJ_SCALE 12 ++ ++#define STB0899_OFF0_UWP_CNTRL3 0xf34c ++#define STB0899_BASE_UWP_CNTRL3 0x00000400 ++#define STB0899_UWP_TH_TRACK (0x7fff << 15) ++#define STB0899_OFFST_UWP_TH_TRACK 15 ++#define STB0899_WIDTH_UWP_TH_TRACK 15 ++#define STB0899_UWP_TH_ACQ (0x7fff << 0) ++#define STB0899_OFFST_UWP_TH_ACQ 0 ++#define STB0899_WIDTH_UWP_TH_ACQ 15 ++ ++#define STB0899_OFF0_SYM_CLK_SEL 0xf350 ++#define STB0899_BASE_SYM_CLK_SEL 0x00000400 ++#define STB0899_SYM_CLK_SEL (0x03 << 0) ++#define STB0899_OFFST_SYM_CLK_SEL 0 ++#define STB0899_WIDTH_SYM_CLK_SEL 2 ++ ++#define STB0899_OFF0_SOF_SRCH_TO 0xf354 ++#define STB0899_BASE_SOF_SRCH_TO 0x00000400 ++#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0) ++#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0 ++#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22 ++ ++#define STB0899_OFF0_ACQ_CNTRL1 0xf358 ++#define STB0899_BASE_ACQ_CNTRL1 0x00000400 ++#define STB0899_FE_FINE_ACQ (0xff << 8) ++#define STB0899_OFFST_FE_FINE_ACQ 8 ++#define STB0899_WIDTH_FE_FINE_ACQ 8 ++#define STB0899_FE_COARSE_ACQ (0xff << 0) ++#define STB0899_OFFST_FE_COARSE_ACQ 0 ++#define STB0899_WIDTH_FE_COARSE_ACQ 8 ++ ++#define STB0899_OFF0_ACQ_CNTRL2 0xf35c ++#define STB0899_BASE_ACQ_CNTRL2 0x00000400 ++#define STB0899_ZIGZAG (0x01 << 25) ++#define STB0899_OFFST_ZIGZAG 25 ++#define STB0899_WIDTH_ZIGZAG 1 ++#define STB0899_NUM_STEPS (0xff << 17) ++#define STB0899_OFFST_NUM_STEPS 17 ++#define STB0899_WIDTH_NUM_STEPS 8 ++#define STB0899_FREQ_STEPSIZE (0x1ffff << 0) ++#define STB0899_OFFST_FREQ_STEPSIZE 0 ++#define STB0899_WIDTH_FREQ_STEPSIZE 17 ++ ++#define STB0899_OFF0_ACQ_CNTRL3 0xf360 ++#define STB0899_BASE_ACQ_CNTRL3 0x00000400 ++#define STB0899_THRESHOLD_SCL (0x3f << 23) ++#define STB0899_OFFST_THRESHOLD_SCL 23 ++#define STB0899_WIDTH_THRESHOLD_SCL 6 ++#define STB0899_UWP_TH_SRCH (0x7fff << 8) ++#define STB0899_OFFST_UWP_TH_SRCH 8 ++#define STB0899_WIDTH_UWP_TH_SRCH 15 ++#define STB0899_AUTO_REACQUIRE (0x01 << 7) ++#define STB0899_OFFST_AUTO_REACQUIRE 7 ++#define STB0899_WIDTH_AUTO_REACQUIRE 1 ++#define STB0899_TRACK_LOCK_SEL (0x01 << 6) ++#define STB0899_OFFST_TRACK_LOCK_SEL 6 ++#define STB0899_WIDTH_TRACK_LOCK_SEL 1 ++#define STB0899_ACQ_SEARCH_MODE (0x03 << 4) ++#define STB0899_OFFST_ACQ_SEARCH_MODE 4 ++#define STB0899_WIDTH_ACQ_SEARCH_MODE 2 ++#define STB0899_CONFIRM_FRAMES (0x0f << 0) ++#define STB0899_OFFST_CONFIRM_FRAMES 0 ++#define STB0899_WIDTH_CONFIRM_FRAMES 4 ++ ++#define STB0899_OFF0_FE_SETTLE 0xf364 ++#define STB0899_BASE_FE_SETTLE 0x00000400 ++#define STB0899_SETTLING_TIME (0x3fffff << 0) ++#define STB0899_OFFST_SETTLING_TIME 0 ++#define STB0899_WIDTH_SETTLING_TIME 22 ++ ++#define STB0899_OFF0_AC_DWELL 0xf368 ++#define STB0899_BASE_AC_DWELL 0x00000400 ++#define STB0899_DWELL_TIME (0x3fffff << 0) ++#define STB0899_OFFST_DWELL_TIME 0 ++#define STB0899_WIDTH_DWELL_TIME 22 ++ ++#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c ++#define STB0899_BASE_ACQUIRE_TRIG 0x00000400 ++#define STB0899_ACQUIRE (0x01 << 0) ++#define STB0899_OFFST_ACQUIRE 0 ++#define STB0899_WIDTH_ACQUIRE 1 ++ ++#define STB0899_OFF0_LOCK_LOST 0xf370 ++#define STB0899_BASE_LOCK_LOST 0x00000400 ++#define STB0899_LOCK_LOST (0x01 << 0) ++#define STB0899_OFFST_LOCK_LOST 0 ++#define STB0899_WIDTH_LOCK_LOST 1 ++ ++#define STB0899_OFF0_ACQ_STAT1 0xf374 ++#define STB0899_BASE_ACQ_STAT1 0x00000400 ++#define STB0899_STEP_FREQ (0x1fffff << 11) ++#define STB0899_OFFST_STEP_FREQ 11 ++#define STB0899_WIDTH_STEP_FREQ 21 ++#define STB0899_ACQ_STATE (0x07 << 8) ++#define STB0899_OFFST_ACQ_STATE 8 ++#define STB0899_WIDTH_ACQ_STATE 3 ++#define STB0899_UW_DETECT_COUNT (0xff << 0) ++#define STB0899_OFFST_UW_DETECT_COUNT 0 ++#define STB0899_WIDTH_UW_DETECT_COUNT 8 ++ ++#define STB0899_OFF0_ACQ_TIMEOUT 0xf378 ++#define STB0899_BASE_ACQ_TIMEOUT 0x00000400 ++#define STB0899_ACQ_TIMEOUT (0x3fffff << 0) ++#define STB0899_OFFST_ACQ_TIMEOUT 0 ++#define STB0899_WIDTH_ACQ_TIMEOUT 22 ++ ++#define STB0899_OFF0_ACQ_TIME 0xf37c ++#define STB0899_BASE_ACQ_TIME 0x00000400 ++#define STB0899_ACQ_TIME_SYM (0xffffff << 0) ++#define STB0899_OFFST_ACQ_TIME_SYM 0 ++#define STB0899_WIDTH_ACQ_TIME_SYM 24 ++ ++#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308 ++#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440 ++#define STB0899_FINAL_GAIN_INIT (0x3fff << 12) ++#define STB0899_OFFST_FINAL_GAIN_INIT 12 ++#define STB0899_WIDTH_FINAL_GAIN_INIT 14 ++#define STB0899_FINAL_LOOP_GAIN (0x0f << 8) ++#define STB0899_OFFST_FINAL_LOOP_GAIN 8 ++#define STB0899_WIDTH_FINAL_LOOP_GAIN 4 ++#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7) ++#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7 ++#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1 ++#define STB0899_FINAL_AGC_REF (0x7f << 0) ++#define STB0899_OFFST_FINAL_AGC_REF 0 ++#define STB0899_WIDTH_FINAL_AGC_REF 7 ++ ++#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c ++#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440 ++#define STB0899_FINAL_AGC_GAIN (0x3fff << 0) ++#define STB0899_OFFST_FINAL_AGC_GAIN 0 ++#define STB0899_WIDTH_FINAL_AGC_GAIN 14 ++ ++#define STB0899_OFF0_EQUALIZER_INIT 0xf310 ++#define STB0899_BASE_EQUALIZER_INIT 0x00000440 ++#define STB0899_EQ_SRST (0x01 << 1) ++#define STB0899_OFFST_EQ_SRST 1 ++#define STB0899_WIDTH_EQ_SRST 1 ++#define STB0899_EQ_INIT (0x01 << 0) ++#define STB0899_OFFST_EQ_INIT 0 ++#define STB0899_WIDTH_EQ_INIT 1 ++ ++#define STB0899_OFF0_EQ_CNTRL 0xf314 ++#define STB0899_BASE_EQ_CNTRL 0x00000440 ++#define STB0899_EQ_ADAPT_MODE (0x01 << 18) ++#define STB0899_OFFST_EQ_ADAPT_MODE 18 ++#define STB0899_WIDTH_EQ_ADAPT_MODE 1 ++#define STB0899_EQ_DELAY (0x0f << 14) ++#define STB0899_OFFST_EQ_DELAY 14 ++#define STB0899_WIDTH_EQ_DELAY 4 ++#define STB0899_EQ_QUANT_LEVEL (0xff << 6) ++#define STB0899_OFFST_EQ_QUANT_LEVEL 6 ++#define STB0899_WIDTH_EQ_QUANT_LEVEL 8 ++#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5) ++#define STB0899_OFFST_EQ_DISABLE_UPDATE 5 ++#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1 ++#define STB0899_EQ_BYPASS (0x01 << 4) ++#define STB0899_OFFST_EQ_BYPASS 4 ++#define STB0899_WIDTH_EQ_BYPASS 1 ++#define STB0899_EQ_SHIFT (0x0f << 0) ++#define STB0899_OFFST_EQ_SHIFT 0 ++#define STB0899_WIDTH_EQ_SHIFT 4 ++ ++#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320 ++#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324 ++#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328 ++#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c ++#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330 ++#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334 ++#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338 ++#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c ++#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340 ++#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344 ++#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348 ++#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440 ++#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0) ++#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0 ++#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12 ++ ++#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350 ++#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354 ++#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358 ++#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c ++#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360 ++#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364 ++#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368 ++#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c ++#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370 ++#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374 ++#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378 ++#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440 ++#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0) ++#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0 ++#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12 ++ ++#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300 ++#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304 ++#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308 ++#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c ++#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310 ++#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314 ++#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318 ++#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c ++#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320 ++#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324 ++#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328 ++#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460 ++#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0) ++#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0 ++#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12 ++ ++#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330 ++#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334 ++#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338 ++#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c ++#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340 ++#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344 ++#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348 ++#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c ++#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350 ++#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354 ++#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358 ++#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460 ++#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0) ++#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0 ++#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12 ++ ++/* S2 FEC */ ++#define STB0899_OFF0_BLOCK_LNGTH 0xfa04 ++#define STB0899_BASE_BLOCK_LNGTH 0x00000000 ++#define STB0899_BLOCK_LENGTH (0xff << 0) ++#define STB0899_OFFST_BLOCK_LENGTH 0 ++#define STB0899_WIDTH_BLOCK_LENGTH 8 ++ ++#define STB0899_OFF0_ROW_STR 0xfa08 ++#define STB0899_BASE_ROW_STR 0x00000000 ++#define STB0899_ROW_STRIDE (0xff << 0) ++#define STB0899_OFFST_ROW_STRIDE 0 ++#define STB0899_WIDTH_ROW_STRIDE 8 ++ ++#define STB0899_OFF0_MAX_ITER 0xfa0c ++#define STB0899_BASE_MAX_ITER 0x00000000 ++#define STB0899_MAX_ITERATIONS (0xff << 0) ++#define STB0899_OFFST_MAX_ITERATIONS 0 ++#define STB0899_WIDTH_MAX_ITERATIONS 8 ++ ++#define STB0899_OFF0_BN_END_ADDR 0xfa10 ++#define STB0899_BASE_BN_END_ADDR 0x00000000 ++#define STB0899_BN_END_ADDR (0x0fff << 0) ++#define STB0899_OFFST_BN_END_ADDR 0 ++#define STB0899_WIDTH_BN_END_ADDR 12 ++ ++#define STB0899_OFF0_CN_END_ADDR 0xfa14 ++#define STB0899_BASE_CN_END_ADDR 0x00000000 ++#define STB0899_CN_END_ADDR (0x0fff << 0) ++#define STB0899_OFFST_CN_END_ADDR 0 ++#define STB0899_WIDTH_CN_END_ADDR 12 ++ ++#define STB0899_OFF0_INFO_LENGTH 0xfa1c ++#define STB0899_BASE_INFO_LENGTH 0x00000000 ++#define STB0899_INFO_LENGTH (0xff << 0) ++#define STB0899_OFFST_INFO_LENGTH 0 ++#define STB0899_WIDTH_INFO_LENGTH 8 ++ ++#define STB0899_OFF0_BOT_ADDR 0xfa20 ++#define STB0899_BASE_BOT_ADDR 0x00000000 ++#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0) ++#define STB0899_OFFST_BOTTOM_BASE_ADDR 0 ++#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10 ++ ++#define STB0899_OFF0_BCH_BLK_LN 0xfa24 ++#define STB0899_BASE_BCH_BLK_LN 0x00000000 ++#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0) ++#define STB0899_OFFST_BCH_BLOCK_LENGTH 0 ++#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16 ++ ++#define STB0899_OFF0_BCH_T 0xfa28 ++#define STB0899_BASE_BCH_T 0x00000000 ++#define STB0899_BCH_T (0x0f << 0) ++#define STB0899_OFFST_BCH_T 0 ++#define STB0899_WIDTH_BCH_T 4 ++ ++#define STB0899_OFF0_CNFG_MODE 0xfa00 ++#define STB0899_BASE_CNFG_MODE 0x00000800 ++#define STB0899_MODCOD (0x1f << 2) ++#define STB0899_OFFST_MODCOD 2 ++#define STB0899_WIDTH_MODCOD 5 ++#define STB0899_MODCOD_SEL (0x01 << 1) ++#define STB0899_OFFST_MODCOD_SEL 1 ++#define STB0899_WIDTH_MODCOD_SEL 1 ++#define STB0899_CONFIG_MODE (0x01 << 0) ++#define STB0899_OFFST_CONFIG_MODE 0 ++#define STB0899_WIDTH_CONFIG_MODE 1 ++ ++#define STB0899_OFF0_LDPC_STAT 0xfa04 ++#define STB0899_BASE_LDPC_STAT 0x00000800 ++#define STB0899_ITERATION (0xff << 3) ++#define STB0899_OFFST_ITERATION 3 ++#define STB0899_WIDTH_ITERATION 8 ++#define STB0899_LDPC_DEC_STATE (0x07 << 0) ++#define STB0899_OFFST_LDPC_DEC_STATE 0 ++#define STB0899_WIDTH_LDPC_DEC_STATE 3 ++ ++#define STB0899_OFF0_ITER_SCALE 0xfa08 ++#define STB0899_BASE_ITER_SCALE 0x00000800 ++#define STB0899_ITERATION_SCALE (0xff << 0) ++#define STB0899_OFFST_ITERATION_SCALE 0 ++#define STB0899_WIDTH_ITERATION_SCALE 8 ++ ++#define STB0899_OFF0_INPUT_MODE 0xfa0c ++#define STB0899_BASE_INPUT_MODE 0x00000800 ++#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0) ++#define STB0899_OFFST_SD_BLOCK1_STREAM0 0 ++#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1 ++ ++#define STB0899_OFF0_LDPCDECRST 0xfa10 ++#define STB0899_BASE_LDPCDECRST 0x00000800 ++#define STB0899_LDPC_DEC_RST (0x01 << 0) ++#define STB0899_OFFST_LDPC_DEC_RST 0 ++#define STB0899_WIDTH_LDPC_DEC_RST 1 ++ ++#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14 ++#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800 ++#define STB0899_CLKS_PER_BYTE (0x0f << 0) ++#define STB0899_OFFST_CLKS_PER_BYTE 0 ++#define STB0899_WIDTH_CLKS_PER_BYTE 5 ++ ++#define STB0899_OFF0_BCH_ERRORS 0xfa18 ++#define STB0899_BASE_BCH_ERRORS 0x00000800 ++#define STB0899_BCH_ERRORS (0x0f << 0) ++#define STB0899_OFFST_BCH_ERRORS 0 ++#define STB0899_WIDTH_BCH_ERRORS 4 ++ ++#define STB0899_OFF0_LDPC_ERRORS 0xfa1c ++#define STB0899_BASE_LDPC_ERRORS 0x00000800 ++#define STB0899_LDPC_ERRORS (0xffff << 0) ++#define STB0899_OFFST_LDPC_ERRORS 0 ++#define STB0899_WIDTH_LDPC_ERRORS 16 ++ ++#define STB0899_OFF0_BCH_MODE 0xfa20 ++#define STB0899_BASE_BCH_MODE 0x00000800 ++#define STB0899_BCH_CORRECT_N (0x01 << 1) ++#define STB0899_OFFST_BCH_CORRECT_N 1 ++#define STB0899_WIDTH_BCH_CORRECT_N 1 ++#define STB0899_FULL_BYPASS (0x01 << 0) ++#define STB0899_OFFST_FULL_BYPASS 0 ++#define STB0899_WIDTH_FULL_BYPASS 1 ++ ++#define STB0899_OFF0_ERR_ACC_PER 0xfa24 ++#define STB0899_BASE_ERR_ACC_PER 0x00000800 ++#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0) ++#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0 ++#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4 ++ ++#define STB0899_OFF0_BCH_ERR_ACC 0xfa28 ++#define STB0899_BASE_BCH_ERR_ACC 0x00000800 ++#define STB0899_BCH_ERR_ACCUM (0xff << 0) ++#define STB0899_OFFST_BCH_ERR_ACCUM 0 ++#define STB0899_WIDTH_BCH_ERR_ACCUM 8 ++ ++#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c ++#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800 ++#define STB0899_FEC_CORE_ID (0xffffffff << 0) ++#define STB0899_OFFST_FEC_CORE_ID 0 ++#define STB0899_WIDTH_FEC_CORE_ID 32 ++ ++#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34 ++#define STB0899_BASE_FEC_VER_ID_REG 0x00000800 ++#define STB0899_FEC_VER_ID (0xff << 0) ++#define STB0899_OFFST_FEC_VER_ID 0 ++#define STB0899_WIDTH_FEC_VER_ID 8 ++ ++#define STB0899_OFF0_FEC_TP_SEL 0xfa38 ++#define STB0899_BASE_FEC_TP_SEL 0x00000800 ++ ++#define STB0899_OFF0_CSM_CNTRL1 0xf310 ++#define STB0899_BASE_CSM_CNTRL1 0x00000400 ++#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19) ++#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19 ++#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1 ++#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18) ++#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18 ++#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1 ++#define STB0899_CSM_AUTO_PARAM (0x01 << 17) ++#define STB0899_OFFST_CSM_AUTO_PARAM 17 ++#define STB0899_WIDTH_CSM_AUTO_PARAM 1 ++#define STB0899_FE_LOOP_SHIFT (0x07 << 14) ++#define STB0899_OFFST_FE_LOOP_SHIFT 14 ++#define STB0899_WIDTH_FE_LOOP_SHIFT 3 ++#define STB0899_CSM_AGC_SHIFT (0x07 << 11) ++#define STB0899_OFFST_CSM_AGC_SHIFT 11 ++#define STB0899_WIDTH_CSM_AGC_SHIFT 3 ++#define STB0899_CSM_AGC_GAIN (0x1ff << 2) ++#define STB0899_OFFST_CSM_AGC_GAIN 2 ++#define STB0899_WIDTH_CSM_AGC_GAIN 9 ++#define STB0899_CSM_TWO_PASS (0x01 << 1) ++#define STB0899_OFFST_CSM_TWO_PASS 1 ++#define STB0899_WIDTH_CSM_TWO_PASS 1 ++#define STB0899_CSM_DVT_TABLE (0x01 << 0) ++#define STB0899_OFFST_CSM_DVT_TABLE 0 ++#define STB0899_WIDTH_CSM_DVT_TABLE 1 ++ ++#define STB0899_OFF0_CSM_CNTRL2 0xf314 ++#define STB0899_BASE_CSM_CNTRL2 0x00000400 ++#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9) ++#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9 ++#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9 ++#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0) ++#define STB0899_OFFST_CSM_GAMMA_ACQ 0 ++#define STB0899_WIDTH_CSM_GAMMA_ACQ 9 ++ ++#define STB0899_OFF0_CSM_CNTRL3 0xf318 ++#define STB0899_BASE_CSM_CNTRL3 0x00000400 ++#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9) ++#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9 ++#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9 ++#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0) ++#define STB0899_OFFST_CSM_GAMMA_TRACK 0 ++#define STB0899_WIDTH_CSM_GAMMA_TRACK 9 ++ ++#define STB0899_OFF0_CSM_CNTRL4 0xf31c ++#define STB0899_BASE_CSM_CNTRL4 0x00000400 ++#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8) ++#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8 ++#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4 ++#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0) ++#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0 ++#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8 ++ ++/* Check on chapter 8 page 42 */ ++#define STB0899_ERRCTRL1 0xf574 ++#define STB0899_ERRCTRL2 0xf575 ++#define STB0899_ERRCTRL3 0xf576 ++#define STB0899_ERR_SRC_S1 (0x1f << 3) ++#define STB0899_OFFST_ERR_SRC_S1 3 ++#define STB0899_WIDTH_ERR_SRC_S1 5 ++#define STB0899_ERR_SRC_S2 (0x0f << 0) ++#define STB0899_OFFST_ERR_SRC_S2 0 ++#define STB0899_WIDTH_ERR_SRC_S2 4 ++#define STB0899_NOE (0x07 << 0) ++#define STB0899_OFFST_NOE 0 ++#define STB0899_WIDTH_NOE 3 ++ ++#define STB0899_ECNT1M 0xf524 ++#define STB0899_ECNT1L 0xf525 ++#define STB0899_ECNT2M 0xf526 ++#define STB0899_ECNT2L 0xf527 ++#define STB0899_ECNT3M 0xf528 ++#define STB0899_ECNT3L 0xf529 ++ ++#define STB0899_DMONMSK1 0xf57b ++#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7) ++#define STB0899_DMONMSK1_FREE_14 (1 << 6) ++#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5) ++#define STB0899_DMONMSK1_FREE_12 (1 << 4) ++#define STB0899_DMONMSK1_FREE_11 (1 << 3) ++#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2) ++#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1) ++#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0) ++ ++#define STB0899_DMONMSK0 0xf57c ++#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7) ++#define STB0899_DMONMSK0_FREE_6 (1 << 6) ++#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5) ++#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4) ++#define STB0899_DMONMSK0_FREE_3 (1 << 3) ++#define STB0899_DMONMSK0_FREE_2 (1 << 2) ++#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1) ++#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0) ++ ++#define STB0899_TSULC 0xf549 ++#define STB0899_ULNOSYNCBYTES (0x01 << 7) ++#define STB0899_OFFST_ULNOSYNCBYTES 7 ++#define STB0899_WIDTH_ULNOSYNCBYTES 1 ++#define STB0899_ULPARITY_ON (0x01 << 6) ++#define STB0899_OFFST_ULPARITY_ON 6 ++#define STB0899_WIDTH_ULPARITY_ON 1 ++#define STB0899_ULSYNCOUTRS (0x01 << 5) ++#define STB0899_OFFST_ULSYNCOUTRS 5 ++#define STB0899_WIDTH_ULSYNCOUTRS 1 ++#define STB0899_ULDSS_PACKETS (0x01 << 0) ++#define STB0899_OFFST_ULDSS_PACKETS 0 ++#define STB0899_WIDTH_ULDSS_PACKETS 1 ++ ++#define STB0899_TSLPL 0xf54b ++#define STB0899_LLDVBS2_MODE (0x01 << 4) ++#define STB0899_OFFST_LLDVBS2_MODE 4 ++#define STB0899_WIDTH_LLDVBS2_MODE 1 ++#define STB0899_LLISSYI_ON (0x01 << 3) ++#define STB0899_OFFST_LLISSYI_ON 3 ++#define STB0899_WIDTH_LLISSYI_ON 1 ++#define STB0899_LLNPD_ON (0x01 << 2) ++#define STB0899_OFFST_LLNPD_ON 2 ++#define STB0899_WIDTH_LLNPD_ON 1 ++#define STB0899_LLCRC8_ON (0x01 << 1) ++#define STB0899_OFFST_LLCRC8_ON 1 ++#define STB0899_WIDTH_LLCRC8_ON 1 ++ ++#define STB0899_TSCFGH 0xf54c ++#define STB0899_OUTRS_PS (0x01 << 6) ++#define STB0899_OFFST_OUTRS_PS 6 ++#define STB0899_WIDTH_OUTRS_PS 1 ++#define STB0899_SYNCBYTE (0x01 << 5) ++#define STB0899_OFFST_SYNCBYTE 5 ++#define STB0899_WIDTH_SYNCBYTE 1 ++#define STB0899_PFBIT (0x01 << 4) ++#define STB0899_OFFST_PFBIT 4 ++#define STB0899_WIDTH_PFBIT 1 ++#define STB0899_ERR_BIT (0x01 << 3) ++#define STB0899_OFFST_ERR_BIT 3 ++#define STB0899_WIDTH_ERR_BIT 1 ++#define STB0899_MPEG (0x01 << 2) ++#define STB0899_OFFST_MPEG 2 ++#define STB0899_WIDTH_MPEG 1 ++#define STB0899_CLK_POL (0x01 << 1) ++#define STB0899_OFFST_CLK_POL 1 ++#define STB0899_WIDTH_CLK_POL 1 ++#define STB0899_FORCE0 (0x01 << 0) ++#define STB0899_OFFST_FORCE0 0 ++#define STB0899_WIDTH_FORCE0 1 ++ ++#define STB0899_TSCFGM 0xf54d ++#define STB0899_LLPRIORITY (0x01 << 3) ++#define STB0899_OFFST_LLPRIORIY 3 ++#define STB0899_WIDTH_LLPRIORITY 1 ++#define STB0899_EN188 (0x01 << 2) ++#define STB0899_OFFST_EN188 2 ++#define STB0899_WIDTH_EN188 1 ++ ++#define STB0899_TSCFGL 0xf54e ++#define STB0899_DEL_ERRPCK (0x01 << 7) ++#define STB0899_OFFST_DEL_ERRPCK 7 ++#define STB0899_WIDTH_DEL_ERRPCK 1 ++#define STB0899_ERRFLAGSTD (0x01 << 5) ++#define STB0899_OFFST_ERRFLAGSTD 5 ++#define STB0899_WIDTH_ERRFLAGSTD 1 ++#define STB0899_MPEGERR (0x01 << 4) ++#define STB0899_OFFST_MPEGERR 4 ++#define STB0899_WIDTH_MPEGERR 1 ++#define STB0899_BCH_CHK (0x01 << 3) ++#define STB0899_OFFST_BCH_CHK 5 ++#define STB0899_WIDTH_BCH_CHK 1 ++#define STB0899_CRC8CHK (0x01 << 2) ++#define STB0899_OFFST_CRC8CHK 2 ++#define STB0899_WIDTH_CRC8CHK 1 ++#define STB0899_SPEC_INFO (0x01 << 1) ++#define STB0899_OFFST_SPEC_INFO 1 ++#define STB0899_WIDTH_SPEC_INFO 1 ++#define STB0899_LOW_PRIO_CLK (0x01 << 0) ++#define STB0899_OFFST_LOW_PRIO_CLK 0 ++#define STB0899_WIDTH_LOW_PRIO_CLK 1 ++#define STB0899_ERROR_NORM (0x00 << 0) ++#define STB0899_OFFST_ERROR_NORM 0 ++#define STB0899_WIDTH_ERROR_NORM 0 ++ ++#define STB0899_TSOUT 0xf54f ++#define STB0899_RSSYNCDEL 0xf550 ++#define STB0899_TSINHDELH 0xf551 ++#define STB0899_TSINHDELM 0xf552 ++#define STB0899_TSINHDELL 0xf553 ++#define STB0899_TSLLSTKM 0xf55a ++#define STB0899_TSLLSTKL 0xf55b ++#define STB0899_TSULSTKM 0xf55c ++#define STB0899_TSULSTKL 0xf55d ++#define STB0899_TSSTATUS 0xf561 ++ ++#define STB0899_PDELCTRL 0xf600 ++#define STB0899_INVERT_RES (0x01 << 7) ++#define STB0899_OFFST_INVERT_RES 7 ++#define STB0899_WIDTH_INVERT_RES 1 ++#define STB0899_FORCE_ACCEPTED (0x01 << 6) ++#define STB0899_OFFST_FORCE_ACCEPTED 6 ++#define STB0899_WIDTH_FORCE_ACCEPTED 1 ++#define STB0899_FILTER_EN (0x01 << 5) ++#define STB0899_OFFST_FILTER_EN 5 ++#define STB0899_WIDTH_FILTER_EN 1 ++#define STB0899_LOCKFALL_THRESH (0x01 << 4) ++#define STB0899_OFFST_LOCKFALL_THRESH 4 ++#define STB0899_WIDTH_LOCKFALL_THRESH 1 ++#define STB0899_HYST_EN (0x01 << 3) ++#define STB0899_OFFST_HYST_EN 3 ++#define STB0899_WIDTH_HYST_EN 1 ++#define STB0899_HYST_SWRST (0x01 << 2) ++#define STB0899_OFFST_HYST_SWRST 2 ++#define STB0899_WIDTH_HYST_SWRST 1 ++#define STB0899_ALGO_EN (0x01 << 1) ++#define STB0899_OFFST_ALGO_EN 1 ++#define STB0899_WIDTH_ALGO_EN 1 ++#define STB0899_ALGO_SWRST (0x01 << 0) ++#define STB0899_OFFST_ALGO_SWRST 0 ++#define STB0899_WIDTH_ALGO_SWRST 1 ++ ++#define STB0899_PDELCTRL2 0xf601 ++#define STB0899_BBHCTRL1 0xf602 ++#define STB0899_BBHCTRL2 0xf603 ++#define STB0899_HYSTTHRESH 0xf604 ++ ++#define STB0899_MATCSTM 0xf605 ++#define STB0899_MATCSTL 0xf606 ++#define STB0899_UPLCSTM 0xf607 ++#define STB0899_UPLCSTL 0xf608 ++#define STB0899_DFLCSTM 0xf609 ++#define STB0899_DFLCSTL 0xf60a ++#define STB0899_SYNCCST 0xf60b ++#define STB0899_SYNCDCSTM 0xf60c ++#define STB0899_SYNCDCSTL 0xf60d ++#define STB0899_ISI_ENTRY 0xf60e ++#define STB0899_ISI_BIT_EN 0xf60f ++#define STB0899_MATSTRM 0xf610 ++#define STB0899_MATSTRL 0xf611 ++#define STB0899_UPLSTRM 0xf612 ++#define STB0899_UPLSTRL 0xf613 ++#define STB0899_DFLSTRM 0xf614 ++#define STB0899_DFLSTRL 0xf615 ++#define STB0899_SYNCSTR 0xf616 ++#define STB0899_SYNCDSTRM 0xf617 ++#define STB0899_SYNCDSTRL 0xf618 ++ ++#define STB0899_CFGPDELSTATUS1 0xf619 ++#define STB0899_BADDFL (0x01 << 6) ++#define STB0899_OFFST_BADDFL 6 ++#define STB0899_WIDTH_BADDFL 1 ++#define STB0899_CONTINUOUS_STREAM (0x01 << 5) ++#define STB0899_OFFST_CONTINUOUS_STREAM 5 ++#define STB0899_WIDTH_CONTINUOUS_STREAM 1 ++#define STB0899_ACCEPTED_STREAM (0x01 << 4) ++#define STB0899_OFFST_ACCEPTED_STREAM 4 ++#define STB0899_WIDTH_ACCEPTED_STREAM 1 ++#define STB0899_BCH_ERRFLAG (0x01 << 3) ++#define STB0899_OFFST_BCH_ERRFLAG 3 ++#define STB0899_WIDTH_BCH_ERRFLAG 1 ++#define STB0899_CRCRES (0x01 << 2) ++#define STB0899_OFFST_CRCRES 2 ++#define STB0899_WIDTH_CRCRES 1 ++#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1) ++#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1 ++#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1 ++#define STB0899_1STLOCK (0x01 << 0) ++#define STB0899_OFFST_1STLOCK 0 ++#define STB0899_WIDTH_1STLOCK 1 ++ ++#define STB0899_CFGPDELSTATUS2 0xf61a ++#define STB0899_BBFERRORM 0xf61b ++#define STB0899_BBFERRORL 0xf61c ++#define STB0899_UPKTERRORM 0xf61d ++#define STB0899_UPKTERRORL 0xf61e ++ ++#define STB0899_TSTCK 0xff10 ++ ++#define STB0899_TSTRES 0xff11 ++#define STB0899_FRESLDPC (0x01 << 7) ++#define STB0899_OFFST_FRESLDPC 7 ++#define STB0899_WIDTH_FRESLDPC 1 ++#define STB0899_FRESRS (0x01 << 6) ++#define STB0899_OFFST_FRESRS 6 ++#define STB0899_WIDTH_FRESRS 1 ++#define STB0899_FRESVIT (0x01 << 5) ++#define STB0899_OFFST_FRESVIT 5 ++#define STB0899_WIDTH_FRESVIT 1 ++#define STB0899_FRESMAS1_2 (0x01 << 4) ++#define STB0899_OFFST_FRESMAS1_2 4 ++#define STB0899_WIDTH_FRESMAS1_2 1 ++#define STB0899_FRESACS (0x01 << 3) ++#define STB0899_OFFST_FRESACS 3 ++#define STB0899_WIDTH_FRESACS 1 ++#define STB0899_FRESSYM (0x01 << 2) ++#define STB0899_OFFST_FRESSYM 2 ++#define STB0899_WIDTH_FRESSYM 1 ++#define STB0899_FRESMAS (0x01 << 1) ++#define STB0899_OFFST_FRESMAS 1 ++#define STB0899_WIDTH_FRESMAS 1 ++#define STB0899_FRESINT (0x01 << 0) ++#define STB0899_OFFST_FRESINIT 0 ++#define STB0899_WIDTH_FRESINIT 1 ++ ++#define STB0899_TSTOUT 0xff12 ++#define STB0899_EN_SIGNATURE (0x01 << 7) ++#define STB0899_OFFST_EN_SIGNATURE 7 ++#define STB0899_WIDTH_EN_SIGNATURE 1 ++#define STB0899_BCLK_CLK (0x01 << 6) ++#define STB0899_OFFST_BCLK_CLK 6 ++#define STB0899_WIDTH_BCLK_CLK 1 ++#define STB0899_SGNL_OUT (0x01 << 5) ++#define STB0899_OFFST_SGNL_OUT 5 ++#define STB0899_WIDTH_SGNL_OUT 1 ++#define STB0899_TS (0x01 << 4) ++#define STB0899_OFFST_TS 4 ++#define STB0899_WIDTH_TS 1 ++#define STB0899_CTEST (0x01 << 0) ++#define STB0899_OFFST_CTEST 0 ++#define STB0899_WIDTH_CTEST 1 ++ ++#define STB0899_TSTIN 0xff13 ++#define STB0899_TEST_IN (0x01 << 7) ++#define STB0899_OFFST_TEST_IN 7 ++#define STB0899_WIDTH_TEST_IN 1 ++#define STB0899_EN_ADC (0x01 << 6) ++#define STB0899_OFFST_EN_ADC 6 ++#define STB0899_WIDTH_ENADC 1 ++#define STB0899_SGN_ADC (0x01 << 5) ++#define STB0899_OFFST_SGN_ADC 5 ++#define STB0899_WIDTH_SGN_ADC 1 ++#define STB0899_BCLK_IN (0x01 << 4) ++#define STB0899_OFFST_BCLK_IN 4 ++#define STB0899_WIDTH_BCLK_IN 1 ++#define STB0899_JETONIN_MODE (0x01 << 3) ++#define STB0899_OFFST_JETONIN_MODE 3 ++#define STB0899_WIDTH_JETONIN_MODE 1 ++#define STB0899_BCLK_VALUE (0x01 << 2) ++#define STB0899_OFFST_BCLK_VALUE 2 ++#define STB0899_WIDTH_BCLK_VALUE 1 ++#define STB0899_SGNRST_T12 (0x01 << 1) ++#define STB0899_OFFST_SGNRST_T12 1 ++#define STB0899_WIDTH_SGNRST_T12 1 ++#define STB0899_LOWSP_ENAX (0x01 << 0) ++#define STB0899_OFFST_LOWSP_ENAX 0 ++#define STB0899_WIDTH_LOWSP_ENAX 1 ++ ++#define STB0899_TSTSYS 0xff14 ++#define STB0899_TSTCHIP 0xff15 ++#define STB0899_TSTFREE 0xff16 ++#define STB0899_TSTI2C 0xff17 ++#define STB0899_BITSPEEDM 0xff1c ++#define STB0899_BITSPEEDL 0xff1d ++#define STB0899_TBUSBIT 0xff1e ++#define STB0899_TSTDIS 0xff24 ++#define STB0899_TSTDISRX 0xff25 ++#define STB0899_TSTJETON 0xff28 ++#define STB0899_TSTDCADJ 0xff40 ++#define STB0899_TSTAGC1 0xff41 ++#define STB0899_TSTAGC1N 0xff42 ++#define STB0899_TSTPOLYPH 0xff48 ++#define STB0899_TSTR 0xff49 ++#define STB0899_TSTAGC2 0xff4a ++#define STB0899_TSTCTL1 0xff4b ++#define STB0899_TSTCTL2 0xff4c ++#define STB0899_TSTCTL3 0xff4d ++#define STB0899_TSTDEMAP 0xff50 ++#define STB0899_TSTDEMAP2 0xff51 ++#define STB0899_TSTDEMMON 0xff52 ++#define STB0899_TSTRATE 0xff53 ++#define STB0899_TSTSELOUT 0xff54 ++#define STB0899_TSYNC 0xff55 ++#define STB0899_TSTERR 0xff56 ++#define STB0899_TSTRAM1 0xff58 ++#define STB0899_TSTVSELOUT 0xff59 ++#define STB0899_TSTFORCEIN 0xff5a ++#define STB0899_TSTRS1 0xff5c ++#define STB0899_TSTRS2 0xff5d ++#define STB0899_TSTRS3 0xff53 ++ ++#define STB0899_INTBUFSTATUS 0xf200 ++#define STB0899_INTBUFCTRL 0xf201 ++#define STB0899_PCKLENUL 0xf55e ++#define STB0899_PCKLENLL 0xf55f ++#define STB0899_RSPCKLEN 0xf560 ++ ++/* 2 registers */ ++#define STB0899_SYNCDCST 0xf60c ++ ++/* DiSEqC */ ++#define STB0899_DISCNTRL1 0xf0a0 ++#define STB0899_TIMOFF (0x01 << 7) ++#define STB0899_OFFST_TIMOFF 7 ++#define STB0899_WIDTH_TIMOFF 1 ++#define STB0899_DISEQCRESET (0x01 << 6) ++#define STB0899_OFFST_DISEQCRESET 6 ++#define STB0899_WIDTH_DISEQCRESET 1 ++#define STB0899_TIMCMD (0x03 << 4) ++#define STB0899_OFFST_TIMCMD 4 ++#define STB0899_WIDTH_TIMCMD 2 ++#define STB0899_DISPRECHARGE (0x01 << 2) ++#define STB0899_OFFST_DISPRECHARGE 2 ++#define STB0899_WIDTH_DISPRECHARGE 1 ++#define STB0899_DISEQCMODE (0x03 << 0) ++#define STB0899_OFFST_DISEQCMODE 0 ++#define STB0899_WIDTH_DISEQCMODE 2 ++ ++#define STB0899_DISCNTRL2 0xf0a1 ++#define STB0899_RECEIVER_ON (0x01 << 7) ++#define STB0899_OFFST_RECEIVER_ON 7 ++#define STB0899_WIDTH_RECEIVER_ON 1 ++#define STB0899_IGNO_SHORT_22K (0x01 << 6) ++#define STB0899_OFFST_IGNO_SHORT_22K 6 ++#define STB0899_WIDTH_IGNO_SHORT_22K 1 ++#define STB0899_ONECHIP_TRX (0x01 << 5) ++#define STB0899_OFFST_ONECHIP_TRX 5 ++#define STB0899_WIDTH_ONECHIP_TRX 1 ++#define STB0899_EXT_ENVELOP (0x01 << 4) ++#define STB0899_OFFST_EXT_ENVELOP 4 ++#define STB0899_WIDTH_EXT_ENVELOP 1 ++#define STB0899_PIN_SELECT (0x03 << 2) ++#define STB0899_OFFST_PIN_SELCT 2 ++#define STB0899_WIDTH_PIN_SELCT 2 ++#define STB0899_IRQ_RXEND (0x01 << 1) ++#define STB0899_OFFST_IRQ_RXEND 1 ++#define STB0899_WIDTH_IRQ_RXEND 1 ++#define STB0899_IRQ_4NBYTES (0x01 << 0) ++#define STB0899_OFFST_IRQ_4NBYTES 0 ++#define STB0899_WIDTH_IRQ_4NBYTES 1 ++ ++#define STB0899_DISRX_ST0 0xf0a4 ++#define STB0899_RXEND (0x01 << 7) ++#define STB0899_OFFST_RXEND 7 ++#define STB0899_WIDTH_RXEND 1 ++#define STB0899_RXACTIVE (0x01 << 6) ++#define STB0899_OFFST_RXACTIVE 6 ++#define STB0899_WIDTH_RXACTIVE 1 ++#define STB0899_SHORT22K (0x01 << 5) ++#define STB0899_OFFST_SHORT22K 5 ++#define STB0899_WIDTH_SHORT22K 1 ++#define STB0899_CONTTONE (0x01 << 4) ++#define STB0899_OFFST_CONTTONE 4 ++#define STB0899_WIDTH_CONTONE 1 ++#define STB0899_4BFIFOREDY (0x01 << 3) ++#define STB0899_OFFST_4BFIFOREDY 3 ++#define STB0899_WIDTH_4BFIFOREDY 1 ++#define STB0899_FIFOEMPTY (0x01 << 2) ++#define STB0899_OFFST_FIFOEMPTY 2 ++#define STB0899_WIDTH_FIFOEMPTY 1 ++#define STB0899_ABORTTRX (0x01 << 0) ++#define STB0899_OFFST_ABORTTRX 0 ++#define STB0899_WIDTH_ABORTTRX 1 ++ ++#define STB0899_DISRX_ST1 0xf0a5 ++#define STB0899_RXFAIL (0x01 << 7) ++#define STB0899_OFFST_RXFAIL 7 ++#define STB0899_WIDTH_RXFAIL 1 ++#define STB0899_FIFOPFAIL (0x01 << 6) ++#define STB0899_OFFST_FIFOPFAIL 6 ++#define STB0899_WIDTH_FIFOPFAIL 1 ++#define STB0899_RXNONBYTES (0x01 << 5) ++#define STB0899_OFFST_RXNONBYTES 5 ++#define STB0899_WIDTH_RXNONBYTES 1 ++#define STB0899_FIFOOVF (0x01 << 4) ++#define STB0899_OFFST_FIFOOVF 4 ++#define STB0899_WIDTH_FIFOOVF 1 ++#define STB0899_FIFOBYTENBR (0x0f << 0) ++#define STB0899_OFFST_FIFOBYTENBR 0 ++#define STB0899_WIDTH_FIFOBYTENBR 4 ++ ++#define STB0899_DISPARITY 0xf0a6 ++ ++#define STB0899_DISFIFO 0xf0a7 ++ ++#define STB0899_DISSTATUS 0xf0a8 ++#define STB0899_FIFOFULL (0x01 << 6) ++#define STB0899_OFFST_FIFOFULL 6 ++#define STB0899_WIDTH_FIFOFULL 1 ++#define STB0899_TXIDLE (0x01 << 5) ++#define STB0899_OFFST_TXIDLE 5 ++#define STB0899_WIDTH_TXIDLE 1 ++#define STB0899_GAPBURST (0x01 << 4) ++#define STB0899_OFFST_GAPBURST 4 ++#define STB0899_WIDTH_GAPBURST 1 ++#define STB0899_TXFIFOBYTES (0x0f << 0) ++#define STB0899_OFFST_TXFIFOBYTES 0 ++#define STB0899_WIDTH_TXFIFOBYTES 4 ++#define STB0899_DISF22 0xf0a9 ++ ++#define STB0899_DISF22RX 0xf0aa ++ ++/* General Purpose */ ++#define STB0899_SYSREG 0xf101 ++#define STB0899_ACRPRESC 0xf110 ++#define STB0899_OFFST_RSVD2 7 ++#define STB0899_WIDTH_RSVD2 1 ++#define STB0899_OFFST_ACRPRESC 4 ++#define STB0899_WIDTH_ACRPRESC 3 ++#define STB0899_OFFST_RSVD1 3 ++#define STB0899_WIDTH_RSVD1 1 ++#define STB0899_OFFST_ACRPRESC2 0 ++#define STB0899_WIDTH_ACRPRESC2 3 ++ ++#define STB0899_ACRDIV1 0xf111 ++#define STB0899_ACRDIV2 0xf112 ++#define STB0899_DACR1 0xf113 ++#define STB0899_DACR2 0xf114 ++#define STB0899_OUTCFG 0xf11c ++#define STB0899_MODECFG 0xf11d ++#define STB0899_NCOARSE 0xf1b3 ++ ++#define STB0899_SYNTCTRL 0xf1b6 ++#define STB0899_STANDBY (0x01 << 7) ++#define STB0899_OFFST_STANDBY 7 ++#define STB0899_WIDTH_STANDBY 1 ++#define STB0899_BYPASSPLL (0x01 << 6) ++#define STB0899_OFFST_BYPASSPLL 6 ++#define STB0899_WIDTH_BYPASSPLL 1 ++#define STB0899_SEL1XRATIO (0x01 << 5) ++#define STB0899_OFFST_SEL1XRATIO 5 ++#define STB0899_WIDTH_SEL1XRATIO 1 ++#define STB0899_SELOSCI (0x01 << 1) ++#define STB0899_OFFST_SELOSCI 1 ++#define STB0899_WIDTH_SELOSCI 1 ++ ++#define STB0899_FILTCTRL 0xf1b7 ++#define STB0899_SYSCTRL 0xf1b8 ++ ++#define STB0899_STOPCLK1 0xf1c2 ++#define STB0899_STOP_CKINTBUF108 (0x01 << 7) ++#define STB0899_OFFST_STOP_CKINTBUF108 7 ++#define STB0899_WIDTH_STOP_CKINTBUF108 1 ++#define STB0899_STOP_CKINTBUF216 (0x01 << 6) ++#define STB0899_OFFST_STOP_CKINTBUF216 6 ++#define STB0899_WIDTH_STOP_CKINTBUF216 1 ++#define STB0899_STOP_CHK8PSK (0x01 << 5) ++#define STB0899_OFFST_STOP_CHK8PSK 5 ++#define STB0899_WIDTH_STOP_CHK8PSK 1 ++#define STB0899_STOP_CKFEC108 (0x01 << 4) ++#define STB0899_OFFST_STOP_CKFEC108 4 ++#define STB0899_WIDTH_STOP_CKFEC108 1 ++#define STB0899_STOP_CKFEC216 (0x01 << 3) ++#define STB0899_OFFST_STOP_CKFEC216 3 ++#define STB0899_WIDTH_STOP_CKFEC216 1 ++#define STB0899_STOP_CKCORE216 (0x01 << 2) ++#define STB0899_OFFST_STOP_CKCORE216 2 ++#define STB0899_WIDTH_STOP_CKCORE216 1 ++#define STB0899_STOP_CKADCI108 (0x01 << 1) ++#define STB0899_OFFST_STOP_CKADCI108 1 ++#define STB0899_WIDTH_STOP_CKADCI108 1 ++#define STB0899_STOP_INVCKADCI108 (0x01 << 0) ++#define STB0899_OFFST_STOP_INVCKADCI108 0 ++#define STB0899_WIDTH_STOP_INVCKADCI108 1 ++ ++#define STB0899_STOPCLK2 0xf1c3 ++#define STB0899_STOP_CKS2DMD108 (0x01 << 2) ++#define STB0899_OFFST_STOP_CKS2DMD108 2 ++#define STB0899_WIDTH_STOP_CKS2DMD108 1 ++#define STB0899_STOP_CKPKDLIN108 (0x01 << 1) ++#define STB0899_OFFST_STOP_CKPKDLIN108 1 ++#define STB0899_WIDTH_STOP_CKPKDLIN108 1 ++#define STB0899_STOP_CKPKDLIN216 (0x01 << 0) ++#define STB0899_OFFST_STOP_CKPKDLIN216 0 ++#define STB0899_WIDTH_STOP_CKPKDLIN216 1 ++ ++#define STB0899_TSTTNR1 0xf1e0 ++#define STB0899_BYPASS_ADC (0x01 << 7) ++#define STB0899_OFFST_BYPASS_ADC 7 ++#define STB0899_WIDTH_BYPASS_ADC 1 ++#define STB0899_INVADCICKOUT (0x01 << 6) ++#define STB0899_OFFST_INVADCICKOUT 6 ++#define STB0899_WIDTH_INVADCICKOUT 1 ++#define STB0899_ADCTEST_VOLTAGE (0x03 << 4) ++#define STB0899_OFFST_ADCTEST_VOLTAGE 4 ++#define STB0899_WIDTH_ADCTEST_VOLTAGE 1 ++#define STB0899_ADC_RESET (0x01 << 3) ++#define STB0899_OFFST_ADC_RESET 3 ++#define STB0899_WIDTH_ADC_RESET 1 ++#define STB0899_TSTTNR1_2 (0x01 << 2) ++#define STB0899_OFFST_TSTTNR1_2 2 ++#define STB0899_WIDTH_TSTTNR1_2 1 ++#define STB0899_ADCPON (0x01 << 1) ++#define STB0899_OFFST_ADCPON 1 ++#define STB0899_WIDTH_ADCPON 1 ++#define STB0899_ADCIN_MODE (0x01 << 0) ++#define STB0899_OFFST_ADCIN_MODE 0 ++#define STB0899_WIDTH_ADCIN_MODE 1 ++ ++#define STB0899_TSTTNR2 0xf1e1 ++#define STB0899_TSTTNR2_7 (0x01 << 7) ++#define STB0899_OFFST_TSTTNR2_7 7 ++#define STB0899_WIDTH_TSTTNR2_7 1 ++#define STB0899_NOT_DISRX_WIRED (0x01 << 6) ++#define STB0899_OFFST_NOT_DISRX_WIRED 6 ++#define STB0899_WIDTH_NOT_DISRX_WIRED 1 ++#define STB0899_DISEQC_DCURRENT (0x01 << 5) ++#define STB0899_OFFST_DISEQC_DCURRENT 5 ++#define STB0899_WIDTH_DISEQC_DCURRENT 1 ++#define STB0899_DISEQC_ZCURRENT (0x01 << 4) ++#define STB0899_OFFST_DISEQC_ZCURRENT 4 ++#define STB0899_WIDTH_DISEQC_ZCURRENT 1 ++#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2) ++#define STB0899_OFFST_DISEQC_SINC_SOURCE 2 ++#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2 ++#define STB0899_SELIQSRC (0x03 << 0) ++#define STB0899_OFFST_SELIQSRC 0 ++#define STB0899_WIDTH_SELIQSRC 2 ++ ++#define STB0899_TSTTNR3 0xf1e2 ++ ++#define STB0899_I2CCFG 0xf129 ++#define STB0899_I2CCFGRSVD (0x0f << 4) ++#define STB0899_OFFST_I2CCFGRSVD 4 ++#define STB0899_WIDTH_I2CCFGRSVD 4 ++#define STB0899_I2CFASTMODE (0x01 << 3) ++#define STB0899_OFFST_I2CFASTMODE 3 ++#define STB0899_WIDTH_I2CFASTMODE 1 ++#define STB0899_STATUSWR (0x01 << 2) ++#define STB0899_OFFST_STATUSWR 2 ++#define STB0899_WIDTH_STATUSWR 1 ++#define STB0899_I2CADDRINC (0x03 << 0) ++#define STB0899_OFFST_I2CADDRINC 0 ++#define STB0899_WIDTH_I2CADDRINC 2 ++ ++#define STB0899_I2CRPT 0xf12a ++#define STB0899_I2CTON (0x01 << 7) ++#define STB0899_OFFST_I2CTON 7 ++#define STB0899_WIDTH_I2CTON 1 ++#define STB0899_ENARPTLEVEL (0x01 << 6) ++#define STB0899_OFFST_ENARPTLEVEL 6 ++#define STB0899_WIDTH_ENARPTLEVEL 2 ++#define STB0899_SCLTDELAY (0x01 << 3) ++#define STB0899_OFFST_SCLTDELAY 3 ++#define STB0899_WIDTH_SCLTDELAY 1 ++#define STB0899_STOPENA (0x01 << 2) ++#define STB0899_OFFST_STOPENA 2 ++#define STB0899_WIDTH_STOPENA 1 ++#define STB0899_STOPSDAT2SDA (0x01 << 1) ++#define STB0899_OFFST_STOPSDAT2SDA 1 ++#define STB0899_WIDTH_STOPSDAT2SDA 1 ++ ++#define STB0899_IOPVALUE8 0xf136 ++#define STB0899_IOPVALUE7 0xf137 ++#define STB0899_IOPVALUE6 0xf138 ++#define STB0899_IOPVALUE5 0xf139 ++#define STB0899_IOPVALUE4 0xf13a ++#define STB0899_IOPVALUE3 0xf13b ++#define STB0899_IOPVALUE2 0xf13c ++#define STB0899_IOPVALUE1 0xf13d ++#define STB0899_IOPVALUE0 0xf13e ++ ++#define STB0899_GPIO00CFG 0xf140 ++ ++#define STB0899_GPIO01CFG 0xf141 ++#define STB0899_GPIO02CFG 0xf142 ++#define STB0899_GPIO03CFG 0xf143 ++#define STB0899_GPIO04CFG 0xf144 ++#define STB0899_GPIO05CFG 0xf145 ++#define STB0899_GPIO06CFG 0xf146 ++#define STB0899_GPIO07CFG 0xf147 ++#define STB0899_GPIO08CFG 0xf148 ++#define STB0899_GPIO09CFG 0xf149 ++#define STB0899_GPIO10CFG 0xf14a ++#define STB0899_GPIO11CFG 0xf14b ++#define STB0899_GPIO12CFG 0xf14c ++#define STB0899_GPIO13CFG 0xf14d ++#define STB0899_GPIO14CFG 0xf14e ++#define STB0899_GPIO15CFG 0xf14f ++#define STB0899_GPIO16CFG 0xf150 ++#define STB0899_GPIO17CFG 0xf151 ++#define STB0899_GPIO18CFG 0xf152 ++#define STB0899_GPIO19CFG 0xf153 ++#define STB0899_GPIO20CFG 0xf154 ++ ++#define STB0899_SDATCFG 0xf155 ++#define STB0899_SCLTCFG 0xf156 ++#define STB0899_AGCRFCFG 0xf157 ++#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */ ++#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */ ++#define STB0899_DIRCLKCFG 0xf15a ++#define STB0899_CLKOUT27CFG 0xf15b ++#define STB0899_STDBYCFG 0xf15c ++#define STB0899_CS0CFG 0xf15d ++#define STB0899_CS1CFG 0xf15e ++#define STB0899_DISEQCOCFG 0xf15f ++ ++#define STB0899_GPIO32CFG 0xf160 ++#define STB0899_GPIO33CFG 0xf161 ++#define STB0899_GPIO34CFG 0xf162 ++#define STB0899_GPIO35CFG 0xf163 ++#define STB0899_GPIO36CFG 0xf164 ++#define STB0899_GPIO37CFG 0xf165 ++#define STB0899_GPIO38CFG 0xf166 ++#define STB0899_GPIO39CFG 0xf167 ++ ++#define STB0899_IRQSTATUS_3 0xf120 ++#define STB0899_IRQSTATUS_2 0xf121 ++#define STB0899_IRQSTATUS_1 0xf122 ++#define STB0899_IRQSTATUS_0 0xf123 ++ ++#define STB0899_IRQMSK_3 0xf124 ++#define STB0899_IRQMSK_2 0xf125 ++#define STB0899_IRQMSK_1 0xf126 ++#define STB0899_IRQMSK_0 0xf127 ++ ++#define STB0899_IRQCFG 0xf128 ++ ++#define STB0899_GHOSTREG 0xf000 ++ ++#define STB0899_S2DEMOD 0xf3fc ++#define STB0899_S2FEC 0xfafc ++ ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c +new file mode 100644 +index 0000000..a0c3c52 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6000.c +@@ -0,0 +1,256 @@ ++ /* ++ Driver for ST STB6000 DVBS Silicon tuner ++ ++ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "stb6000.h" ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "stb6000: " args); \ ++ } while (0) ++ ++struct stb6000_priv { ++ /* i2c details */ ++ int i2c_address; ++ struct i2c_adapter *i2c; ++ u32 frequency; ++}; ++ ++static int stb6000_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int stb6000_sleep(struct dvb_frontend *fe) ++{ ++ struct stb6000_priv *priv = fe->tuner_priv; ++ int ret; ++ u8 buf[] = { 10, 0 }; ++ struct i2c_msg msg = { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(priv->i2c, &msg, 1); ++ if (ret != 1) ++ dprintk("%s: i2c error\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return (ret == 1) ? 0 : ret; ++} ++ ++static int stb6000_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stb6000_priv *priv = fe->tuner_priv; ++ unsigned int n, m; ++ int ret; ++ u32 freq_mhz; ++ int bandwidth; ++ u8 buf[12]; ++ struct i2c_msg msg = { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 12 ++ }; ++ ++ dprintk("%s:\n", __func__); ++ ++ freq_mhz = p->frequency / 1000; ++ bandwidth = p->symbol_rate / 1000000; ++ ++ if (bandwidth > 31) ++ bandwidth = 31; ++ ++ if ((freq_mhz > 949) && (freq_mhz < 2151)) { ++ buf[0] = 0x01; ++ buf[1] = 0xac; ++ if (freq_mhz < 1950) ++ buf[1] = 0xaa; ++ if (freq_mhz < 1800) ++ buf[1] = 0xa8; ++ if (freq_mhz < 1650) ++ buf[1] = 0xa6; ++ if (freq_mhz < 1530) ++ buf[1] = 0xa5; ++ if (freq_mhz < 1470) ++ buf[1] = 0xa4; ++ if (freq_mhz < 1370) ++ buf[1] = 0xa2; ++ if (freq_mhz < 1300) ++ buf[1] = 0xa1; ++ if (freq_mhz < 1200) ++ buf[1] = 0xa0; ++ if (freq_mhz < 1075) ++ buf[1] = 0xbc; ++ if (freq_mhz < 1000) ++ buf[1] = 0xba; ++ if (freq_mhz < 1075) { ++ n = freq_mhz / 8; /* vco=lo*4 */ ++ m = 2; ++ } else { ++ n = freq_mhz / 16; /* vco=lo*2 */ ++ m = 1; ++ } ++ buf[2] = n >> 1; ++ buf[3] = (unsigned char)(((n & 1) << 7) | ++ (m * freq_mhz - n * 16) | 0x60); ++ buf[4] = 0x04; ++ buf[5] = 0x0e; ++ ++ buf[6] = (unsigned char)(bandwidth); ++ ++ buf[7] = 0xd8; ++ buf[8] = 0xd0; ++ buf[9] = 0x50; ++ buf[10] = 0xeb; ++ buf[11] = 0x4f; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(priv->i2c, &msg, 1); ++ if (ret != 1) ++ dprintk("%s: i2c error\n", __func__); ++ ++ udelay(10); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ buf[0] = 0x07; ++ buf[1] = 0xdf; ++ buf[2] = 0xd0; ++ buf[3] = 0x50; ++ buf[4] = 0xfb; ++ msg.len = 5; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(priv->i2c, &msg, 1); ++ if (ret != 1) ++ dprintk("%s: i2c error\n", __func__); ++ ++ udelay(10); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ priv->frequency = freq_mhz * 1000; ++ ++ return (ret == 1) ? 0 : ret; ++ } ++ return -1; ++} ++ ++static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct stb6000_priv *priv = fe->tuner_priv; ++ *frequency = priv->frequency; ++ return 0; ++} ++ ++static struct dvb_tuner_ops stb6000_tuner_ops = { ++ .info = { ++ .name = "ST STB6000", ++ .frequency_min = 950000, ++ .frequency_max = 2150000 ++ }, ++ .release = stb6000_release, ++ .sleep = stb6000_sleep, ++ .set_params = stb6000_set_params, ++ .get_frequency = stb6000_get_frequency, ++}; ++ ++struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, ++ struct i2c_adapter *i2c) ++{ ++ struct stb6000_priv *priv = NULL; ++ u8 b0[] = { 0 }; ++ u8 b1[] = { 0, 0 }; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = addr, ++ .flags = 0, ++ .buf = b0, ++ .len = 0 ++ }, { ++ .addr = addr, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 2 ++ } ++ }; ++ int ret; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ /* is some i2c device here ? */ ++ ret = i2c_transfer(i2c, msg, 2); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ if (ret != 2) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->i2c_address = addr; ++ priv->i2c = i2c; ++ ++ memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ ++ fe->tuner_priv = priv; ++ ++ return fe; ++} ++EXPORT_SYMBOL(stb6000_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("DVB STB6000 driver"); ++MODULE_AUTHOR("Igor M. Liplianin "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stb6000.h b/drivers/media/dvb-frontends/stb6000.h +new file mode 100644 +index 0000000..7be479c +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6000.h +@@ -0,0 +1,51 @@ ++ /* ++ Driver for ST stb6000 DVBS Silicon tuner ++ ++ Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#ifndef __DVB_STB6000_H__ ++#define __DVB_STB6000_H__ ++ ++#include ++#include "dvb_frontend.h" ++ ++/** ++ * Attach a stb6000 tuner to the supplied frontend structure. ++ * ++ * @param fe Frontend to attach to. ++ * @param addr i2c address of the tuner. ++ * @param i2c i2c adapter to use. ++ * @return FE pointer on success, NULL on failure. ++ */ ++#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, ++ int addr, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_STB6000 */ ++ ++#endif /* __DVB_STB6000_H__ */ +diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c +new file mode 100644 +index 0000000..45f9523 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6100.c +@@ -0,0 +1,607 @@ ++/* ++ STB6100 Silicon Tuner ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "stb6100.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++ ++ ++#define FE_ERROR 0 ++#define FE_NOTICE 1 ++#define FE_INFO 2 ++#define FE_DEBUG 3 ++ ++#define dprintk(x, y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((x > FE_ERROR) && (x > y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > FE_NOTICE) && (x > y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > FE_INFO) && (x > y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > FE_DEBUG) && (x > y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (x > y) \ ++ printk(format, ##arg); \ ++ } \ ++} while (0) ++ ++struct stb6100_lkup { ++ u32 val_low; ++ u32 val_high; ++ u8 reg; ++}; ++ ++static int stb6100_release(struct dvb_frontend *fe); ++ ++static const struct stb6100_lkup lkup[] = { ++ { 0, 950000, 0x0a }, ++ { 950000, 1000000, 0x0a }, ++ { 1000000, 1075000, 0x0c }, ++ { 1075000, 1200000, 0x00 }, ++ { 1200000, 1300000, 0x01 }, ++ { 1300000, 1370000, 0x02 }, ++ { 1370000, 1470000, 0x04 }, ++ { 1470000, 1530000, 0x05 }, ++ { 1530000, 1650000, 0x06 }, ++ { 1650000, 1800000, 0x08 }, ++ { 1800000, 1950000, 0x0a }, ++ { 1950000, 2150000, 0x0c }, ++ { 2150000, 9999999, 0x0c }, ++ { 0, 0, 0x00 } ++}; ++ ++/* Register names for easy debugging. */ ++static const char *stb6100_regnames[] = { ++ [STB6100_LD] = "LD", ++ [STB6100_VCO] = "VCO", ++ [STB6100_NI] = "NI", ++ [STB6100_NF_LSB] = "NF", ++ [STB6100_K] = "K", ++ [STB6100_G] = "G", ++ [STB6100_F] = "F", ++ [STB6100_DLB] = "DLB", ++ [STB6100_TEST1] = "TEST1", ++ [STB6100_FCCK] = "FCCK", ++ [STB6100_LPEN] = "LPEN", ++ [STB6100_TEST3] = "TEST3", ++}; ++ ++/* Template for normalisation, i.e. setting unused or undocumented ++ * bits as required according to the documentation. ++ */ ++struct stb6100_regmask { ++ u8 mask; ++ u8 set; ++}; ++ ++static const struct stb6100_regmask stb6100_template[] = { ++ [STB6100_LD] = { 0xff, 0x00 }, ++ [STB6100_VCO] = { 0xff, 0x00 }, ++ [STB6100_NI] = { 0xff, 0x00 }, ++ [STB6100_NF_LSB] = { 0xff, 0x00 }, ++ [STB6100_K] = { 0xc7, 0x38 }, ++ [STB6100_G] = { 0xef, 0x10 }, ++ [STB6100_F] = { 0x1f, 0xc0 }, ++ [STB6100_DLB] = { 0x38, 0xc4 }, ++ [STB6100_TEST1] = { 0x00, 0x8f }, ++ [STB6100_FCCK] = { 0x40, 0x0d }, ++ [STB6100_LPEN] = { 0xf0, 0x0b }, ++ [STB6100_TEST3] = { 0x00, 0xde }, ++}; ++ ++/* ++ * Currently unused. Some boards might need it in the future ++ */ ++static inline void stb6100_normalise_regs(u8 regs[]) ++{ ++ int i; ++ ++ for (i = 0; i < STB6100_NUMREGS; i++) ++ regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set; ++} ++ ++static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) ++{ ++ int rc; ++ struct i2c_msg msg = { ++ .addr = state->config->tuner_address, ++ .flags = I2C_M_RD, ++ .buf = regs, ++ .len = STB6100_NUMREGS ++ }; ++ ++ rc = i2c_transfer(state->i2c, &msg, 1); ++ if (unlikely(rc != 1)) { ++ dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]", ++ state->config->tuner_address, rc); ++ ++ return -EREMOTEIO; ++ } ++ if (unlikely(verbose > FE_DEBUG)) { ++ int i; ++ ++ dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); ++ for (i = 0; i < STB6100_NUMREGS; i++) ++ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]); ++ } ++ return 0; ++} ++ ++static int stb6100_read_reg(struct stb6100_state *state, u8 reg) ++{ ++ u8 regs[STB6100_NUMREGS]; ++ ++ struct i2c_msg msg = { ++ .addr = state->config->tuner_address + reg, ++ .flags = I2C_M_RD, ++ .buf = regs, ++ .len = 1 ++ }; ++ ++ i2c_transfer(state->i2c, &msg, 1); ++ ++ if (unlikely(reg >= STB6100_NUMREGS)) { ++ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); ++ return -EINVAL; ++ } ++ if (unlikely(verbose > FE_DEBUG)) { ++ dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); ++ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[reg], regs[0]); ++ } ++ ++ return (unsigned int)regs[0]; ++} ++ ++static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len) ++{ ++ int rc; ++ u8 cmdbuf[len + 1]; ++ struct i2c_msg msg = { ++ .addr = state->config->tuner_address, ++ .flags = 0, ++ .buf = cmdbuf, ++ .len = len + 1 ++ }; ++ ++ if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) { ++ dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d", ++ start, len); ++ return -EINVAL; ++ } ++ memcpy(&cmdbuf[1], buf, len); ++ cmdbuf[0] = start; ++ ++ if (unlikely(verbose > FE_DEBUG)) { ++ int i; ++ ++ dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len); ++ for (i = 0; i < len; i++) ++ dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]); ++ } ++ rc = i2c_transfer(state->i2c, &msg, 1); ++ if (unlikely(rc != 1)) { ++ dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]", ++ (unsigned int)state->config->tuner_address, start, len, rc); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data) ++{ ++ if (unlikely(reg >= STB6100_NUMREGS)) { ++ dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); ++ return -EREMOTEIO; ++ } ++ data = (data & stb6100_template[reg].mask) | stb6100_template[reg].set; ++ return stb6100_write_reg_range(state, &data, reg, 1); ++} ++ ++ ++static int stb6100_get_status(struct dvb_frontend *fe, u32 *status) ++{ ++ int rc; ++ struct stb6100_state *state = fe->tuner_priv; ++ ++ rc = stb6100_read_reg(state, STB6100_LD); ++ if (rc < 0) { ++ dprintk(verbose, FE_ERROR, 1, "%s failed", __func__); ++ return rc; ++ } ++ return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0; ++} ++ ++static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ int rc; ++ u8 f; ++ struct stb6100_state *state = fe->tuner_priv; ++ ++ rc = stb6100_read_reg(state, STB6100_F); ++ if (rc < 0) ++ return rc; ++ f = rc & STB6100_F_F; ++ ++ state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */ ++ ++ *bandwidth = state->bandwidth = state->status.bandwidth * 1000; ++ dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth); ++ return 0; ++} ++ ++static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ u32 tmp; ++ int rc; ++ struct stb6100_state *state = fe->tuner_priv; ++ ++ dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth); ++ ++ bandwidth /= 2; /* ZIF */ ++ ++ if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */ ++ tmp = 31; ++ else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */ ++ tmp = 0; ++ else /* if 5 < bw/2 < 36 */ ++ tmp = (bandwidth + 500000) / 1000000 - 5; ++ ++ /* Turn on LPF bandwidth setting clock control, ++ * set bandwidth, wait 10ms, turn off. ++ */ ++ rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK); ++ if (rc < 0) ++ return rc; ++ rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp); ++ if (rc < 0) ++ return rc; ++ ++ msleep(5); /* This is dangerous as another (related) thread may start */ ++ ++ rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); ++ if (rc < 0) ++ return rc; ++ ++ msleep(10); /* This is dangerous as another (related) thread may start */ ++ ++ return 0; ++} ++ ++static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ int rc; ++ u32 nint, nfrac, fvco; ++ int psd2, odiv; ++ struct stb6100_state *state = fe->tuner_priv; ++ u8 regs[STB6100_NUMREGS]; ++ ++ rc = stb6100_read_regs(state, regs); ++ if (rc < 0) ++ return rc; ++ ++ odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT; ++ psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT; ++ nint = regs[STB6100_NI]; ++ nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB]; ++ fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2); ++ *frequency = state->frequency = fvco >> (odiv + 1); ++ ++ dprintk(verbose, FE_DEBUG, 1, ++ "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u", ++ state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac); ++ return 0; ++} ++ ++ ++static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) ++{ ++ int rc; ++ const struct stb6100_lkup *ptr; ++ struct stb6100_state *state = fe->tuner_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ u32 srate = 0, fvco, nint, nfrac; ++ u8 regs[STB6100_NUMREGS]; ++ u8 g, psd2, odiv; ++ ++ dprintk(verbose, FE_DEBUG, 1, "Version 2010-8-14 13:51"); ++ ++ if (fe->ops.get_frontend) { ++ dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters"); ++ fe->ops.get_frontend(fe); ++ } ++ srate = p->symbol_rate; ++ ++ /* Set up tuner cleanly, LPF calibration on */ ++ rc = stb6100_write_reg(state, STB6100_FCCK, 0x4d | STB6100_FCCK_FCCK); ++ if (rc < 0) ++ return rc; /* allow LPF calibration */ ++ ++ /* PLL Loop disabled, bias on, VCO on, synth on */ ++ regs[STB6100_LPEN] = 0xeb; ++ rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); ++ if (rc < 0) ++ return rc; ++ ++ /* Program the registers with their data values */ ++ ++ /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */ ++ if (frequency <= 1075000) ++ odiv = 1; ++ else ++ odiv = 0; ++ ++ /* VCO enabled, search clock off as per LL3.7, 3.4.1 */ ++ regs[STB6100_VCO] = 0xe0 | (odiv << STB6100_VCO_ODIV_SHIFT); ++ ++ /* OSM */ ++ for (ptr = lkup; ++ (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high); ++ ptr++); ++ ++ if (ptr->val_high == 0) { ++ printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency); ++ return -EINVAL; ++ } ++ regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg; ++ rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); ++ if (rc < 0) ++ return rc; ++ ++ if ((frequency > 1075000) && (frequency <= 1325000)) ++ psd2 = 0; ++ else ++ psd2 = 1; ++ /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */ ++ fvco = frequency << (1 + odiv); ++ /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */ ++ nint = fvco / (state->reference << psd2); ++ /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */ ++ nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2)) ++ << (9 - psd2), state->reference); ++ ++ /* NI */ ++ regs[STB6100_NI] = nint; ++ rc = stb6100_write_reg(state, STB6100_NI, regs[STB6100_NI]); ++ if (rc < 0) ++ return rc; ++ ++ /* NF */ ++ regs[STB6100_NF_LSB] = nfrac; ++ rc = stb6100_write_reg(state, STB6100_NF_LSB, regs[STB6100_NF_LSB]); ++ if (rc < 0) ++ return rc; ++ ++ /* K */ ++ regs[STB6100_K] = (0x38 & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT); ++ regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB); ++ rc = stb6100_write_reg(state, STB6100_K, regs[STB6100_K]); ++ if (rc < 0) ++ return rc; ++ ++ /* G Baseband gain. */ ++ if (srate >= 15000000) ++ g = 9; /* +4 dB */ ++ else if (srate >= 5000000) ++ g = 11; /* +8 dB */ ++ else ++ g = 14; /* +14 dB */ ++ ++ regs[STB6100_G] = (0x10 & ~STB6100_G_G) | g; ++ regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */ ++ regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */ ++ rc = stb6100_write_reg(state, STB6100_G, regs[STB6100_G]); ++ if (rc < 0) ++ return rc; ++ ++ /* F we don't write as it is set up in BW set */ ++ ++ /* DLB set DC servo loop BW to 160Hz (LLA 3.8 / 2.1) */ ++ regs[STB6100_DLB] = 0xcc; ++ rc = stb6100_write_reg(state, STB6100_DLB, regs[STB6100_DLB]); ++ if (rc < 0) ++ return rc; ++ ++ dprintk(verbose, FE_DEBUG, 1, ++ "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u", ++ frequency, srate, (unsigned int)g, (unsigned int)odiv, ++ (unsigned int)psd2, state->reference, ++ ptr->reg, fvco, nint, nfrac); ++ ++ /* Set up the test registers */ ++ regs[STB6100_TEST1] = 0x8f; ++ rc = stb6100_write_reg(state, STB6100_TEST1, regs[STB6100_TEST1]); ++ if (rc < 0) ++ return rc; ++ regs[STB6100_TEST3] = 0xde; ++ rc = stb6100_write_reg(state, STB6100_TEST3, regs[STB6100_TEST3]); ++ if (rc < 0) ++ return rc; ++ ++ /* Bring up tuner according to LLA 3.7 3.4.1, step 2 */ ++ regs[STB6100_LPEN] = 0xfb; /* PLL Loop enabled, bias on, VCO on, synth on */ ++ rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); ++ if (rc < 0) ++ return rc; ++ ++ msleep(2); ++ ++ /* Bring up tuner according to LLA 3.7 3.4.1, step 3 */ ++ regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */ ++ rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); ++ if (rc < 0) ++ return rc; ++ ++ msleep(10); /* This is dangerous as another (related) thread may start */ /* wait for LO to lock */ ++ ++ regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */ ++ regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */ ++ rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); ++ if (rc < 0) ++ return rc; ++ ++ rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); ++ if (rc < 0) ++ return rc; /* Stop LPF calibration */ ++ ++ msleep(10); /* This is dangerous as another (related) thread may start */ ++ /* wait for stabilisation, (should not be necessary) */ ++ return 0; ++} ++ ++static int stb6100_sleep(struct dvb_frontend *fe) ++{ ++ /* TODO: power down */ ++ return 0; ++} ++ ++static int stb6100_init(struct dvb_frontend *fe) ++{ ++ struct stb6100_state *state = fe->tuner_priv; ++ struct tuner_state *status = &state->status; ++ ++ status->tunerstep = 125000; ++ status->ifreq = 0; ++ status->refclock = 27000000; /* Hz */ ++ status->iqsense = 1; ++ status->bandwidth = 36000; /* kHz */ ++ state->bandwidth = status->bandwidth * 1000; /* Hz */ ++ state->reference = status->refclock / 1000; /* kHz */ ++ ++ /* Set default bandwidth. Modified, PN 13-May-10 */ ++ return 0; ++} ++ ++static int stb6100_get_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *state) ++{ ++ switch (param) { ++ case DVBFE_TUNER_FREQUENCY: ++ stb6100_get_frequency(fe, &state->frequency); ++ break; ++ case DVBFE_TUNER_TUNERSTEP: ++ break; ++ case DVBFE_TUNER_IFFREQ: ++ break; ++ case DVBFE_TUNER_BANDWIDTH: ++ stb6100_get_bandwidth(fe, &state->bandwidth); ++ break; ++ case DVBFE_TUNER_REFCLOCK: ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int stb6100_set_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *state) ++{ ++ struct stb6100_state *tstate = fe->tuner_priv; ++ ++ switch (param) { ++ case DVBFE_TUNER_FREQUENCY: ++ stb6100_set_frequency(fe, state->frequency); ++ tstate->frequency = state->frequency; ++ break; ++ case DVBFE_TUNER_TUNERSTEP: ++ break; ++ case DVBFE_TUNER_IFFREQ: ++ break; ++ case DVBFE_TUNER_BANDWIDTH: ++ stb6100_set_bandwidth(fe, state->bandwidth); ++ tstate->bandwidth = state->bandwidth; ++ break; ++ case DVBFE_TUNER_REFCLOCK: ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct dvb_tuner_ops stb6100_ops = { ++ .info = { ++ .name = "STB6100 Silicon Tuner", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 0, ++ }, ++ ++ .init = stb6100_init, ++ .sleep = stb6100_sleep, ++ .get_status = stb6100_get_status, ++ .get_state = stb6100_get_state, ++ .set_state = stb6100_set_state, ++ .release = stb6100_release ++}; ++ ++struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, ++ const struct stb6100_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stb6100_state *state = NULL; ++ ++ state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->config = config; ++ state->i2c = i2c; ++ state->frontend = fe; ++ state->reference = config->refclock / 1000; /* kHz */ ++ fe->tuner_priv = state; ++ fe->ops.tuner_ops = stb6100_ops; ++ ++ printk("%s: Attaching STB6100 \n", __func__); ++ return fe; ++} ++ ++static int stb6100_release(struct dvb_frontend *fe) ++{ ++ struct stb6100_state *state = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(state); ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(stb6100_attach); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++ ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("STB6100 Silicon tuner"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stb6100.h b/drivers/media/dvb-frontends/stb6100.h +new file mode 100644 +index 0000000..2ab0966 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6100.h +@@ -0,0 +1,115 @@ ++/* ++ STB6100 Silicon Tuner ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STB_6100_REG_H ++#define __STB_6100_REG_H ++ ++#include ++#include "dvb_frontend.h" ++ ++#define STB6100_LD 0x00 ++#define STB6100_LD_LOCK (1 << 0) ++ ++#define STB6100_VCO 0x01 ++#define STB6100_VCO_OSCH (0x01 << 7) ++#define STB6100_VCO_OSCH_SHIFT 7 ++#define STB6100_VCO_OCK (0x03 << 5) ++#define STB6100_VCO_OCK_SHIFT 5 ++#define STB6100_VCO_ODIV (0x01 << 4) ++#define STB6100_VCO_ODIV_SHIFT 4 ++#define STB6100_VCO_OSM (0x0f << 0) ++ ++#define STB6100_NI 0x02 ++#define STB6100_NF_LSB 0x03 ++ ++#define STB6100_K 0x04 ++#define STB6100_K_PSD2 (0x01 << 2) ++#define STB6100_K_PSD2_SHIFT 2 ++#define STB6100_K_NF_MSB (0x03 << 0) ++ ++#define STB6100_G 0x05 ++#define STB6100_G_G (0x0f << 0) ++#define STB6100_G_GCT (0x07 << 5) ++ ++#define STB6100_F 0x06 ++#define STB6100_F_F (0x1f << 0) ++ ++#define STB6100_DLB 0x07 ++ ++#define STB6100_TEST1 0x08 ++ ++#define STB6100_FCCK 0x09 ++#define STB6100_FCCK_FCCK (0x01 << 6) ++ ++#define STB6100_LPEN 0x0a ++#define STB6100_LPEN_LPEN (0x01 << 4) ++#define STB6100_LPEN_SYNP (0x01 << 5) ++#define STB6100_LPEN_OSCP (0x01 << 6) ++#define STB6100_LPEN_BEN (0x01 << 7) ++ ++#define STB6100_TEST3 0x0b ++ ++#define STB6100_NUMREGS 0x0c ++ ++ ++#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ ++ ((y <= val) && (val <= x)) ? 1 : 0) ++ ++#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0) ++ ++struct stb6100_config { ++ u8 tuner_address; ++ u32 refclock; ++}; ++ ++struct stb6100_state { ++ struct i2c_adapter *i2c; ++ ++ const struct stb6100_config *config; ++ struct dvb_tuner_ops ops; ++ struct dvb_frontend *frontend; ++ struct tuner_state status; ++ ++ u32 frequency; ++ u32 srate; ++ u32 bandwidth; ++ u32 reference; ++}; ++ ++#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, ++ const struct stb6100_config *config, ++ struct i2c_adapter *i2c); ++ ++#else ++ ++static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, ++ const struct stb6100_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif //CONFIG_DVB_STB6100 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stb6100_cfg.h b/drivers/media/dvb-frontends/stb6100_cfg.h +new file mode 100644 +index 0000000..6314d18 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6100_cfg.h +@@ -0,0 +1,104 @@ ++/* ++ STB6100 Silicon Tuner ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ Copyright (C) ST Microelectronics ++ ++ 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. ++*/ ++ ++static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ *frequency = t_state.frequency; ++ } ++ return 0; ++} ++ ++static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ t_state.frequency = frequency; ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->set_state) { ++ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = &fe->ops; ++ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ *bandwidth = t_state.bandwidth; ++ } ++ return 0; ++} ++ ++static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ t_state.bandwidth = bandwidth; ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->set_state) { ++ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ } ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/stb6100_proc.h b/drivers/media/dvb-frontends/stb6100_proc.h +new file mode 100644 +index 0000000..112163a +--- /dev/null ++++ b/drivers/media/dvb-frontends/stb6100_proc.h +@@ -0,0 +1,138 @@ ++/* ++ STB6100 Silicon Tuner wrapper ++ Copyright (C)2009 Igor M. Liplianin (liplianin@me.by) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; 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. ++*/ ++ ++static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 1); ++ ++ err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state); ++ if (err < 0) { ++ printk(KERN_ERR "%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 0); ++ ++ *frequency = state.frequency; ++ } ++ ++ return 0; ++} ++ ++static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state state; ++ int err = 0; ++ ++ state.frequency = frequency; ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->set_state) { ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 1); ++ ++ err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state); ++ if (err < 0) { ++ printk(KERN_ERR "%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 0); ++ ++ } ++ ++ return 0; ++} ++ ++static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 1); ++ ++ err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state); ++ if (err < 0) { ++ printk(KERN_ERR "%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 0); ++ ++ *bandwidth = state.bandwidth; ++ } ++ ++ return 0; ++} ++ ++static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state state; ++ int err = 0; ++ ++ state.bandwidth = bandwidth; ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->set_state) { ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 1); ++ ++ err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state); ++ if (err < 0) { ++ printk(KERN_ERR "%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ ++ if (frontend_ops->i2c_gate_ctrl) ++ frontend_ops->i2c_gate_ctrl(fe, 0); ++ ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c +new file mode 100644 +index 0000000..632b251 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0288.c +@@ -0,0 +1,626 @@ ++/* ++ Driver for ST STV0288 demodulator ++ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de ++ for Reel Multimedia ++ Copyright (C) 2008 TurboSight.com, Bob Liu ++ Copyright (C) 2008 Igor M. Liplianin ++ Removed stb6000 specific tuner code and revised some ++ procedures. ++ 2010-09-01 Josef Pavlik ++ Fixed diseqc_msg, diseqc_burst and set_tone problems ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "stv0288.h" ++ ++struct stv0288_state { ++ struct i2c_adapter *i2c; ++ const struct stv0288_config *config; ++ struct dvb_frontend frontend; ++ ++ u8 initialised:1; ++ u32 tuner_frequency; ++ u32 symbol_rate; ++ fe_code_rate_t fec_inner; ++ int errmode; ++}; ++ ++#define STATUS_BER 0 ++#define STATUS_UCBLOCKS 1 ++ ++static int debug; ++static int debug_legacy_dish_switch; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG "stv0288: " args); \ ++ } while (0) ++ ++ ++static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ if (len != 2) ++ return -EINVAL; ++ ++ return stv0288_writeregI(state, buf[0], buf[1]); ++} ++ ++static u8 stv0288_readreg(struct stv0288_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", ++ __func__, reg, ret); ++ ++ return b1[0]; ++} ++ ++static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ unsigned int temp; ++ unsigned char b[3]; ++ ++ if ((srate < 1000000) || (srate > 45000000)) ++ return -EINVAL; ++ ++ stv0288_writeregI(state, 0x22, 0); ++ stv0288_writeregI(state, 0x23, 0); ++ stv0288_writeregI(state, 0x2b, 0xff); ++ stv0288_writeregI(state, 0x2c, 0xf7); ++ ++ temp = (unsigned int)srate / 1000; ++ ++ temp = temp * 32768; ++ temp = temp / 25; ++ temp = temp / 125; ++ b[0] = (unsigned char)((temp >> 12) & 0xff); ++ b[1] = (unsigned char)((temp >> 4) & 0xff); ++ b[2] = (unsigned char)((temp << 4) & 0xf0); ++ stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ ++ stv0288_writeregI(state, 0x29, 0); /* SFRM */ ++ stv0288_writeregI(state, 0x2a, 0); /* SFRL */ ++ ++ stv0288_writeregI(state, 0x28, b[0]); ++ stv0288_writeregI(state, 0x29, b[1]); ++ stv0288_writeregI(state, 0x2a, b[2]); ++ dprintk("stv0288: stv0288_set_symbolrate\n"); ++ ++ return 0; ++} ++ ++static int stv0288_send_diseqc_msg(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *m) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ int i; ++ ++ dprintk("%s\n", __func__); ++ ++ stv0288_writeregI(state, 0x09, 0); ++ msleep(30); ++ stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */ ++ ++ for (i = 0; i < m->msg_len; i++) { ++ if (stv0288_writeregI(state, 0x06, m->msg[i])) ++ return -EREMOTEIO; ++ } ++ msleep(m->msg_len*12); ++ return 0; ++} ++ ++static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t burst) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */ ++ return -EREMOTEIO; ++ ++ if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff)) ++ return -EREMOTEIO; ++ ++ msleep(15); ++ if (stv0288_writeregI(state, 0x05, 0x12)) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */ ++ return -EREMOTEIO; ++ break; ++ ++ case SEC_TONE_OFF: ++ if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/ ++ return -EREMOTEIO; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static u8 stv0288_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x20, ++ 0x09, 0x0, ++ 0x0a, 0x4, ++ 0x0b, 0x0, ++ 0x0c, 0x0, ++ 0x0d, 0x0, ++ 0x0e, 0xd4, ++ 0x0f, 0x30, ++ 0x11, 0x80, ++ 0x12, 0x03, ++ 0x13, 0x48, ++ 0x14, 0x84, ++ 0x15, 0x45, ++ 0x16, 0xb7, ++ 0x17, 0x9c, ++ 0x18, 0x0, ++ 0x19, 0xa6, ++ 0x1a, 0x88, ++ 0x1b, 0x8f, ++ 0x1c, 0xf0, ++ 0x20, 0x0b, ++ 0x21, 0x54, ++ 0x22, 0x0, ++ 0x23, 0x0, ++ 0x2b, 0xff, ++ 0x2c, 0xf7, ++ 0x30, 0x0, ++ 0x31, 0x1e, ++ 0x32, 0x14, ++ 0x33, 0x0f, ++ 0x34, 0x09, ++ 0x35, 0x0c, ++ 0x36, 0x05, ++ 0x37, 0x2f, ++ 0x38, 0x16, ++ 0x39, 0xbe, ++ 0x3a, 0x0, ++ 0x3b, 0x13, ++ 0x3c, 0x11, ++ 0x3d, 0x30, ++ 0x40, 0x63, ++ 0x41, 0x04, ++ 0x42, 0x20, ++ 0x43, 0x00, ++ 0x44, 0x00, ++ 0x45, 0x00, ++ 0x46, 0x00, ++ 0x47, 0x00, ++ 0x4a, 0x00, ++ 0x50, 0x10, ++ 0x51, 0x38, ++ 0x52, 0x21, ++ 0x58, 0x54, ++ 0x59, 0x86, ++ 0x5a, 0x0, ++ 0x5b, 0x9b, ++ 0x5c, 0x08, ++ 0x5d, 0x7f, ++ 0x5e, 0x0, ++ 0x5f, 0xff, ++ 0x70, 0x0, ++ 0x71, 0x0, ++ 0x72, 0x0, ++ 0x74, 0x0, ++ 0x75, 0x0, ++ 0x76, 0x0, ++ 0x81, 0x0, ++ 0x82, 0x3f, ++ 0x83, 0x3f, ++ 0x84, 0x0, ++ 0x85, 0x0, ++ 0x88, 0x0, ++ 0x89, 0x0, ++ 0x8a, 0x0, ++ 0x8b, 0x0, ++ 0x8c, 0x0, ++ 0x90, 0x0, ++ 0x91, 0x0, ++ 0x92, 0x0, ++ 0x93, 0x0, ++ 0x94, 0x1c, ++ 0x97, 0x0, ++ 0xa0, 0x48, ++ 0xa1, 0x0, ++ 0xb0, 0xb8, ++ 0xb1, 0x3a, ++ 0xb2, 0x10, ++ 0xb3, 0x82, ++ 0xb4, 0x80, ++ 0xb5, 0x82, ++ 0xb6, 0x82, ++ 0xb7, 0x82, ++ 0xb8, 0x20, ++ 0xb9, 0x0, ++ 0xf0, 0x0, ++ 0xf1, 0x0, ++ 0xf2, 0xc0, ++ 0x51, 0x36, ++ 0x52, 0x09, ++ 0x53, 0x94, ++ 0x54, 0x62, ++ 0x55, 0x29, ++ 0x56, 0x64, ++ 0x57, 0x2b, ++ 0xff, 0xff, ++}; ++ ++static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) ++{ ++ dprintk("%s: %s\n", __func__, ++ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : ++ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); ++ ++ return 0; ++} ++ ++static int stv0288_init(struct dvb_frontend *fe) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ int i; ++ u8 reg; ++ u8 val; ++ ++ dprintk("stv0288: init chip\n"); ++ stv0288_writeregI(state, 0x41, 0x04); ++ msleep(50); ++ ++ /* we have default inittab */ ++ if (state->config->inittab == NULL) { ++ for (i = 0; !(stv0288_inittab[i] == 0xff && ++ stv0288_inittab[i + 1] == 0xff); i += 2) ++ stv0288_writeregI(state, stv0288_inittab[i], ++ stv0288_inittab[i + 1]); ++ } else { ++ for (i = 0; ; i += 2) { ++ reg = state->config->inittab[i]; ++ val = state->config->inittab[i+1]; ++ if (reg == 0xff && val == 0xff) ++ break; ++ stv0288_writeregI(state, reg, val); ++ } ++ } ++ return 0; ++} ++ ++static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ u8 sync = stv0288_readreg(state, 0x24); ++ if (sync == 255) ++ sync = 0; ++ ++ dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); ++ ++ *status = 0; ++ if (sync & 0x80) ++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; ++ if (sync & 0x10) ++ *status |= FE_HAS_VITERBI; ++ if (sync & 0x08) { ++ *status |= FE_HAS_LOCK; ++ dprintk("stv0288 has locked\n"); ++ } ++ ++ return 0; ++} ++ ++static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ if (state->errmode != STATUS_BER) ++ return 0; ++ *ber = (stv0288_readreg(state, 0x26) << 8) | ++ stv0288_readreg(state, 0x27); ++ dprintk("stv0288_read_ber %d\n", *ber); ++ ++ return 0; ++} ++ ++ ++static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8)); ++ ++ ++ signal = signal * 5 / 4; ++ *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; ++ dprintk("stv0288_read_signal_strength %d\n", *strength); ++ ++ return 0; ++} ++static int stv0288_sleep(struct dvb_frontend *fe) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ stv0288_writeregI(state, 0x41, 0x84); ++ state->initialised = 0; ++ ++ return 0; ++} ++static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8) ++ | stv0288_readreg(state, 0x2e)); ++ xsnr = 3 * (xsnr - 0xa100); ++ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; ++ dprintk("stv0288_read_snr %d\n", *snr); ++ ++ return 0; ++} ++ ++static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ if (state->errmode != STATUS_BER) ++ return 0; ++ *ucblocks = (stv0288_readreg(state, 0x26) << 8) | ++ stv0288_readreg(state, 0x27); ++ dprintk("stv0288_read_ber %d\n", *ucblocks); ++ ++ return 0; ++} ++ ++static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p) ++{ ++ dprintk("%s(..)\n", __func__); ++ return 0; ++} ++ ++static int stv0288_set_frontend(struct dvb_frontend *fe) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ char tm; ++ unsigned char tda[3]; ++ u8 reg, time_out = 0; ++ ++ dprintk("%s : FE_SET_FRONTEND\n", __func__); ++ ++ if (c->delivery_system != SYS_DVBS) { ++ dprintk("%s: unsupported delivery " ++ "system selected (%d)\n", ++ __func__, c->delivery_system); ++ return -EOPNOTSUPP; ++ } ++ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ /* only frequency & symbol_rate are used for tuner*/ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ udelay(10); ++ stv0288_set_symbolrate(fe, c->symbol_rate); ++ /* Carrier lock control register */ ++ stv0288_writeregI(state, 0x15, 0xc5); ++ ++ tda[2] = 0x0; /* CFRL */ ++ for (tm = -9; tm < 7;) { ++ /* Viterbi status */ ++ reg = stv0288_readreg(state, 0x24); ++ if (reg & 0x8) ++ break; ++ if (reg & 0x80) { ++ time_out++; ++ if (time_out > 10) ++ break; ++ tda[2] += 40; ++ if (tda[2] < 40) ++ tm++; ++ } else { ++ tm++; ++ tda[2] = 0; ++ time_out = 0; ++ } ++ tda[1] = (unsigned char)tm; ++ stv0288_writeregI(state, 0x2b, tda[1]); ++ stv0288_writeregI(state, 0x2c, tda[2]); ++ msleep(30); ++ } ++ state->tuner_frequency = c->frequency; ++ state->fec_inner = FEC_AUTO; ++ state->symbol_rate = c->symbol_rate; ++ ++ return 0; ++} ++ ++static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ ++ if (enable) ++ stv0288_writeregI(state, 0x01, 0xb5); ++ else ++ stv0288_writeregI(state, 0x01, 0x35); ++ ++ udelay(1); ++ ++ return 0; ++} ++ ++static void stv0288_release(struct dvb_frontend *fe) ++{ ++ struct stv0288_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops stv0288_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "ST STV0288 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1000, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, /* ppm */ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_QPSK | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = stv0288_release, ++ .init = stv0288_init, ++ .sleep = stv0288_sleep, ++ .write = stv0288_write, ++ .i2c_gate_ctrl = stv0288_i2c_gate_ctrl, ++ .read_status = stv0288_read_status, ++ .read_ber = stv0288_read_ber, ++ .read_signal_strength = stv0288_read_signal_strength, ++ .read_snr = stv0288_read_snr, ++ .read_ucblocks = stv0288_read_ucblocks, ++ .diseqc_send_master_cmd = stv0288_send_diseqc_msg, ++ .diseqc_send_burst = stv0288_send_diseqc_burst, ++ .set_tone = stv0288_set_tone, ++ .set_voltage = stv0288_set_voltage, ++ ++ .set_property = stv0288_set_property, ++ .set_frontend = stv0288_set_frontend, ++}; ++ ++struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv0288_state *state = NULL; ++ int id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ state->tuner_frequency = 0; ++ state->symbol_rate = 0; ++ state->fec_inner = 0; ++ state->errmode = STATUS_BER; ++ ++ stv0288_writeregI(state, 0x41, 0x04); ++ msleep(200); ++ id = stv0288_readreg(state, 0x00); ++ dprintk("stv0288 id %x\n", id); ++ ++ /* register 0x00 contains 0x11 for STV0288 */ ++ if (id != 0x11) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &stv0288_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ ++ return NULL; ++} ++EXPORT_SYMBOL(stv0288_attach); ++ ++module_param(debug_legacy_dish_switch, int, 0444); ++MODULE_PARM_DESC(debug_legacy_dish_switch, ++ "Enable timing analysis for Dish Network legacy switches"); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver"); ++MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin"); ++MODULE_LICENSE("GPL"); ++ +diff --git a/drivers/media/dvb-frontends/stv0288.h b/drivers/media/dvb-frontends/stv0288.h +new file mode 100644 +index 0000000..f2b53db +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0288.h +@@ -0,0 +1,67 @@ ++/* ++ Driver for ST STV0288 demodulator ++ ++ Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de ++ for Reel Multimedia ++ Copyright (C) 2008 TurboSight.com, ++ Copyright (C) 2008 Igor M. Liplianin ++ Removed stb6000 specific tuner code and revised some ++ procedures. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef STV0288_H ++#define STV0288_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct stv0288_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ u8* inittab; ++ ++ /* minimum delay before retuning */ ++ int min_delay_ms; ++ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++}; ++ ++#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \ ++ defined(MODULE)) ++extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_STV0288 */ ++ ++static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val) ++{ ++ int r = 0; ++ u8 buf[] = { reg, val }; ++ if (fe->ops.write) ++ r = fe->ops.write(fe, buf, 2); ++ return r; ++} ++ ++#endif /* STV0288_H */ +diff --git a/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c +new file mode 100644 +index 0000000..d40f226 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0297.c +@@ -0,0 +1,722 @@ ++/* ++ Driver for STV0297 demodulator ++ ++ Copyright (C) 2004 Andrew de Quincey ++ Copyright (C) 2003-2004 Dennis Noermann ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "stv0297.h" ++ ++struct stv0297_state { ++ struct i2c_adapter *i2c; ++ const struct stv0297_config *config; ++ struct dvb_frontend frontend; ++ ++ unsigned long last_ber; ++ unsigned long base_freq; ++}; ++ ++#if 1 ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++#define STV0297_CLOCK_KHZ 28900 ++ ++ ++static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int stv0297_readreg(struct stv0297_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, ++ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} ++ }; ++ ++ // this device needs a STOP between the register and data ++ if (state->config->stop_during_read) { ++ if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); ++ return -1; ++ } ++ if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); ++ return -1; ++ } ++ } else { ++ if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); ++ return -1; ++ } ++ } ++ ++ return b1[0]; ++} ++ ++static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data) ++{ ++ int val; ++ ++ val = stv0297_readreg(state, reg); ++ val &= ~mask; ++ val |= (data & mask); ++ stv0297_writereg(state, reg, val); ++ ++ return 0; ++} ++ ++static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len) ++{ ++ int ret; ++ struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = ++ ®1,.len = 1}, ++ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len} ++ }; ++ ++ // this device needs a STOP between the register and data ++ if (state->config->stop_during_read) { ++ if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); ++ return -1; ++ } ++ if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); ++ return -1; ++ } ++ } else { ++ if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++static u32 stv0297_get_symbolrate(struct stv0297_state *state) ++{ ++ u64 tmp; ++ ++ tmp = stv0297_readreg(state, 0x55); ++ tmp |= stv0297_readreg(state, 0x56) << 8; ++ tmp |= stv0297_readreg(state, 0x57) << 16; ++ tmp |= stv0297_readreg(state, 0x58) << 24; ++ ++ tmp *= STV0297_CLOCK_KHZ; ++ tmp >>= 32; ++ ++ return (u32) tmp; ++} ++ ++static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate) ++{ ++ long tmp; ++ ++ tmp = 131072L * srate; /* 131072 = 2^17 */ ++ tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */ ++ tmp = tmp * 8192L; /* 8192 = 2^13 */ ++ ++ stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF)); ++ stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8)); ++ stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16)); ++ stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24)); ++} ++ ++static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate) ++{ ++ long tmp; ++ ++ tmp = (long) fshift *262144L; /* 262144 = 2*18 */ ++ tmp /= symrate; ++ tmp *= 1024; /* 1024 = 2*10 */ ++ ++ // adjust ++ if (tmp >= 0) { ++ tmp += 500000; ++ } else { ++ tmp -= 500000; ++ } ++ tmp /= 1000000; ++ ++ stv0297_writereg(state, 0x60, tmp & 0xFF); ++ stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); ++} ++ ++static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset) ++{ ++ long tmp; ++ ++ /* symrate is hardcoded to 10000 */ ++ tmp = offset * 26844L; /* (2**28)/10000 */ ++ if (tmp < 0) ++ tmp += 0x10000000; ++ tmp &= 0x0FFFFFFF; ++ ++ stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF)); ++ stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8)); ++ stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16)); ++ stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f); ++} ++ ++/* ++static long stv0297_get_carrieroffset(struct stv0297_state *state) ++{ ++ s64 tmp; ++ ++ stv0297_writereg(state, 0x6B, 0x00); ++ ++ tmp = stv0297_readreg(state, 0x66); ++ tmp |= (stv0297_readreg(state, 0x67) << 8); ++ tmp |= (stv0297_readreg(state, 0x68) << 16); ++ tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24; ++ ++ tmp *= stv0297_get_symbolrate(state); ++ tmp >>= 28; ++ ++ return (s32) tmp; ++} ++*/ ++ ++static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq) ++{ ++ s32 tmp; ++ ++ if (freq > 10000) ++ freq -= STV0297_CLOCK_KHZ; ++ ++ tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16); ++ tmp = (freq * 1000) / tmp; ++ if (tmp > 0xffff) ++ tmp = 0xffff; ++ ++ stv0297_writereg_mask(state, 0x25, 0x80, 0x80); ++ stv0297_writereg(state, 0x21, tmp >> 8); ++ stv0297_writereg(state, 0x20, tmp); ++} ++ ++static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulation) ++{ ++ int val = 0; ++ ++ switch (modulation) { ++ case QAM_16: ++ val = 0; ++ break; ++ ++ case QAM_32: ++ val = 1; ++ break; ++ ++ case QAM_64: ++ val = 4; ++ break; ++ ++ case QAM_128: ++ val = 2; ++ break; ++ ++ case QAM_256: ++ val = 3; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ stv0297_writereg_mask(state, 0x00, 0x70, val << 4); ++ ++ return 0; ++} ++ ++static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_inversion_t inversion) ++{ ++ int val = 0; ++ ++ switch (inversion) { ++ case INVERSION_OFF: ++ val = 0; ++ break; ++ ++ case INVERSION_ON: ++ val = 1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ stv0297_writereg_mask(state, 0x83, 0x08, val << 3); ++ ++ return 0; ++} ++ ++static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ ++ if (enable) { ++ stv0297_writereg(state, 0x87, 0x78); ++ stv0297_writereg(state, 0x86, 0xc8); ++ } ++ ++ return 0; ++} ++ ++static int stv0297_init(struct dvb_frontend *fe) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ int i; ++ ++ /* load init table */ ++ for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) ++ stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); ++ msleep(200); ++ ++ state->last_ber = 0; ++ ++ return 0; ++} ++ ++static int stv0297_sleep(struct dvb_frontend *fe) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ ++ stv0297_writereg_mask(state, 0x80, 1, 1); ++ ++ return 0; ++} ++ ++static int stv0297_read_status(struct dvb_frontend *fe, fe_status_t * status) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ ++ u8 sync = stv0297_readreg(state, 0xDF); ++ ++ *status = 0; ++ if (sync & 0x80) ++ *status |= ++ FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; ++ return 0; ++} ++ ++static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ u8 BER[3]; ++ ++ stv0297_readregs(state, 0xA0, BER, 3); ++ if (!(BER[0] & 0x80)) { ++ state->last_ber = BER[2] << 8 | BER[1]; ++ stv0297_writereg_mask(state, 0xA0, 0x80, 0x80); ++ } ++ ++ *ber = state->last_ber; ++ ++ return 0; ++} ++ ++ ++static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ u8 STRENGTH[3]; ++ u16 tmp; ++ ++ stv0297_readregs(state, 0x41, STRENGTH, 3); ++ tmp = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; ++ if (STRENGTH[2] & 0x20) { ++ if (tmp < 0x200) ++ tmp = 0; ++ else ++ tmp = tmp - 0x200; ++ } else { ++ if (tmp > 0x1ff) ++ tmp = 0; ++ else ++ tmp = 0x1ff - tmp; ++ } ++ *strength = (tmp << 7) | (tmp >> 2); ++ return 0; ++} ++ ++static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ u8 SNR[2]; ++ ++ stv0297_readregs(state, 0x07, SNR, 2); ++ *snr = SNR[1] << 8 | SNR[0]; ++ ++ return 0; ++} ++ ++static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ ++ stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */ ++ ++ *ucblocks = (stv0297_readreg(state, 0xD5) << 8) ++ | stv0297_readreg(state, 0xD4); ++ ++ stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */ ++ stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */ ++ ++ return 0; ++} ++ ++static int stv0297_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0297_state *state = fe->demodulator_priv; ++ int u_threshold; ++ int initial_u; ++ int blind_u; ++ int delay; ++ int sweeprate; ++ int carrieroffset; ++ unsigned long timeout; ++ fe_spectral_inversion_t inversion; ++ ++ switch (p->modulation) { ++ case QAM_16: ++ case QAM_32: ++ case QAM_64: ++ delay = 100; ++ sweeprate = 1000; ++ break; ++ ++ case QAM_128: ++ case QAM_256: ++ delay = 200; ++ sweeprate = 500; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // determine inversion dependent parameters ++ inversion = p->inversion; ++ if (state->config->invert) ++ inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; ++ carrieroffset = -330; ++ switch (inversion) { ++ case INVERSION_OFF: ++ break; ++ ++ case INVERSION_ON: ++ sweeprate = -sweeprate; ++ carrieroffset = -carrieroffset; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ stv0297_init(fe); ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* clear software interrupts */ ++ stv0297_writereg(state, 0x82, 0x0); ++ ++ /* set initial demodulation frequency */ ++ stv0297_set_initialdemodfreq(state, 7250); ++ ++ /* setup AGC */ ++ stv0297_writereg_mask(state, 0x43, 0x10, 0x00); ++ stv0297_writereg(state, 0x41, 0x00); ++ stv0297_writereg_mask(state, 0x42, 0x03, 0x01); ++ stv0297_writereg_mask(state, 0x36, 0x60, 0x00); ++ stv0297_writereg_mask(state, 0x36, 0x18, 0x00); ++ stv0297_writereg_mask(state, 0x71, 0x80, 0x80); ++ stv0297_writereg(state, 0x72, 0x00); ++ stv0297_writereg(state, 0x73, 0x00); ++ stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); ++ stv0297_writereg_mask(state, 0x43, 0x08, 0x00); ++ stv0297_writereg_mask(state, 0x71, 0x80, 0x00); ++ ++ /* setup STL */ ++ stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); ++ stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); ++ stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); ++ stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); ++ stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); ++ ++ /* disable frequency sweep */ ++ stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); ++ ++ /* reset deinterleaver */ ++ stv0297_writereg_mask(state, 0x81, 0x01, 0x01); ++ stv0297_writereg_mask(state, 0x81, 0x01, 0x00); ++ ++ /* ??? */ ++ stv0297_writereg_mask(state, 0x83, 0x20, 0x20); ++ stv0297_writereg_mask(state, 0x83, 0x20, 0x00); ++ ++ /* reset equaliser */ ++ u_threshold = stv0297_readreg(state, 0x00) & 0xf; ++ initial_u = stv0297_readreg(state, 0x01) >> 4; ++ blind_u = stv0297_readreg(state, 0x01) & 0xf; ++ stv0297_writereg_mask(state, 0x84, 0x01, 0x01); ++ stv0297_writereg_mask(state, 0x84, 0x01, 0x00); ++ stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); ++ stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); ++ stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); ++ ++ /* data comes from internal A/D */ ++ stv0297_writereg_mask(state, 0x87, 0x80, 0x00); ++ ++ /* clear phase registers */ ++ stv0297_writereg(state, 0x63, 0x00); ++ stv0297_writereg(state, 0x64, 0x00); ++ stv0297_writereg(state, 0x65, 0x00); ++ stv0297_writereg(state, 0x66, 0x00); ++ stv0297_writereg(state, 0x67, 0x00); ++ stv0297_writereg(state, 0x68, 0x00); ++ stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); ++ ++ /* set parameters */ ++ stv0297_set_qam(state, p->modulation); ++ stv0297_set_symbolrate(state, p->symbol_rate / 1000); ++ stv0297_set_sweeprate(state, sweeprate, p->symbol_rate / 1000); ++ stv0297_set_carrieroffset(state, carrieroffset); ++ stv0297_set_inversion(state, inversion); ++ ++ /* kick off lock */ ++ /* Disable corner detection for higher QAMs */ ++ if (p->modulation == QAM_128 || ++ p->modulation == QAM_256) ++ stv0297_writereg_mask(state, 0x88, 0x08, 0x00); ++ else ++ stv0297_writereg_mask(state, 0x88, 0x08, 0x08); ++ ++ stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); ++ stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); ++ stv0297_writereg_mask(state, 0x43, 0x40, 0x40); ++ stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); ++ stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); ++ stv0297_writereg_mask(state, 0x03, 0x03, 0x03); ++ stv0297_writereg_mask(state, 0x43, 0x10, 0x10); ++ ++ /* wait for WGAGC lock */ ++ timeout = jiffies + msecs_to_jiffies(2000); ++ while (time_before(jiffies, timeout)) { ++ msleep(10); ++ if (stv0297_readreg(state, 0x43) & 0x08) ++ break; ++ } ++ if (time_after(jiffies, timeout)) { ++ goto timeout; ++ } ++ msleep(20); ++ ++ /* wait for equaliser partial convergence */ ++ timeout = jiffies + msecs_to_jiffies(500); ++ while (time_before(jiffies, timeout)) { ++ msleep(10); ++ ++ if (stv0297_readreg(state, 0x82) & 0x04) { ++ break; ++ } ++ } ++ if (time_after(jiffies, timeout)) { ++ goto timeout; ++ } ++ ++ /* wait for equaliser full convergence */ ++ timeout = jiffies + msecs_to_jiffies(delay); ++ while (time_before(jiffies, timeout)) { ++ msleep(10); ++ ++ if (stv0297_readreg(state, 0x82) & 0x08) { ++ break; ++ } ++ } ++ if (time_after(jiffies, timeout)) { ++ goto timeout; ++ } ++ ++ /* disable sweep */ ++ stv0297_writereg_mask(state, 0x6a, 1, 0); ++ stv0297_writereg_mask(state, 0x88, 8, 0); ++ ++ /* wait for main lock */ ++ timeout = jiffies + msecs_to_jiffies(20); ++ while (time_before(jiffies, timeout)) { ++ msleep(10); ++ ++ if (stv0297_readreg(state, 0xDF) & 0x80) { ++ break; ++ } ++ } ++ if (time_after(jiffies, timeout)) { ++ goto timeout; ++ } ++ msleep(100); ++ ++ /* is it still locked after that delay? */ ++ if (!(stv0297_readreg(state, 0xDF) & 0x80)) { ++ goto timeout; ++ } ++ ++ /* success!! */ ++ stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); ++ state->base_freq = p->frequency; ++ return 0; ++ ++timeout: ++ stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); ++ return 0; ++} ++ ++static int stv0297_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0297_state *state = fe->demodulator_priv; ++ int reg_00, reg_83; ++ ++ reg_00 = stv0297_readreg(state, 0x00); ++ reg_83 = stv0297_readreg(state, 0x83); ++ ++ p->frequency = state->base_freq; ++ p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; ++ if (state->config->invert) ++ p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; ++ p->symbol_rate = stv0297_get_symbolrate(state) * 1000; ++ p->fec_inner = FEC_NONE; ++ ++ switch ((reg_00 >> 4) & 0x7) { ++ case 0: ++ p->modulation = QAM_16; ++ break; ++ case 1: ++ p->modulation = QAM_32; ++ break; ++ case 2: ++ p->modulation = QAM_128; ++ break; ++ case 3: ++ p->modulation = QAM_256; ++ break; ++ case 4: ++ p->modulation = QAM_64; ++ break; ++ } ++ ++ return 0; ++} ++ ++static void stv0297_release(struct dvb_frontend *fe) ++{ ++ struct stv0297_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops stv0297_ops; ++ ++struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv0297_state *state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->last_ber = 0; ++ state->base_freq = 0; ++ ++ /* check if the demod is there */ ++ if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops stv0297_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A }, ++ .info = { ++ .name = "ST STV0297 DVB-C", ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 62500, ++ .symbol_rate_min = 870000, ++ .symbol_rate_max = 11700000, ++ .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO}, ++ ++ .release = stv0297_release, ++ ++ .init = stv0297_init, ++ .sleep = stv0297_sleep, ++ .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, ++ ++ .set_frontend = stv0297_set_frontend, ++ .get_frontend = stv0297_get_frontend, ++ ++ .read_status = stv0297_read_status, ++ .read_ber = stv0297_read_ber, ++ .read_signal_strength = stv0297_read_signal_strength, ++ .read_snr = stv0297_read_snr, ++ .read_ucblocks = stv0297_read_ucblocks, ++}; ++ ++MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); ++MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(stv0297_attach); +diff --git a/drivers/media/dvb-frontends/stv0297.h b/drivers/media/dvb-frontends/stv0297.h +new file mode 100644 +index 0000000..3f8f946 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0297.h +@@ -0,0 +1,57 @@ ++/* ++ Driver for STV0297 demodulator ++ ++ Copyright (C) 2003-2004 Dennis Noermann ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef STV0297_H ++#define STV0297_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct stv0297_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* inittab - array of pairs of values. ++ * First of each pair is the register, second is the value. ++ * List should be terminated with an 0xff, 0xff pair. ++ */ ++ u8* inittab; ++ ++ /* does the "inversion" need inverted? */ ++ u8 invert:1; ++ ++ /* set to 1 if the device requires an i2c STOP during reading */ ++ u8 stop_during_read:1; ++}; ++ ++#if defined(CONFIG_DVB_STV0297) || (defined(CONFIG_DVB_STV0297_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_STV0297 ++ ++#endif // STV0297_H +diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c +new file mode 100644 +index 0000000..b57ecf4 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0299.c +@@ -0,0 +1,762 @@ ++/* ++ Driver for ST STV0299 demodulator ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ , ++ , ++ ++ ++ ++ Philips SU1278/SH ++ ++ Copyright (C) 2002 by Peter Schildmann ++ ++ ++ LG TDQF-S001F ++ ++ Copyright (C) 2002 Felix Domke ++ & Andreas Oberritter ++ ++ ++ Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B ++ ++ Copyright (C) 2003 Vadim Catana : ++ ++ Support for Philips SU1278 on Technotrend hardware ++ ++ Copyright (C) 2004 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "stv0299.h" ++ ++struct stv0299_state { ++ struct i2c_adapter* i2c; ++ const struct stv0299_config* config; ++ struct dvb_frontend frontend; ++ ++ u8 initialised:1; ++ u32 tuner_frequency; ++ u32 symbol_rate; ++ fe_code_rate_t fec_inner; ++ int errmode; ++ u32 ucblocks; ++ u8 mcr_reg; ++}; ++ ++#define STATUS_BER 0 ++#define STATUS_UCBLOCKS 1 ++ ++static int debug; ++static int debug_legacy_dish_switch; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "stv0299: " args); \ ++ } while (0) ++ ++ ++static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf [] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ ++ ret = i2c_transfer (state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " ++ "ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int stv0299_write(struct dvb_frontend* fe, const u8 buf[], int len) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ if (len != 2) ++ return -EINVAL; ++ ++ return stv0299_writeregI(state, buf[0], buf[1]); ++} ++ ++static u8 stv0299_readreg (struct stv0299_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", ++ __func__, reg, ret); ++ ++ return b1[0]; ++} ++ ++static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len) ++{ ++ int ret; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk("%s: readreg error (ret == %i)\n", __func__, ret); ++ ++ return ret == 2 ? 0 : ret; ++} ++ ++static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) ++{ ++ dprintk ("%s\n", __func__); ++ ++ switch (fec) { ++ case FEC_AUTO: ++ { ++ return stv0299_writeregI (state, 0x31, 0x1f); ++ } ++ case FEC_1_2: ++ { ++ return stv0299_writeregI (state, 0x31, 0x01); ++ } ++ case FEC_2_3: ++ { ++ return stv0299_writeregI (state, 0x31, 0x02); ++ } ++ case FEC_3_4: ++ { ++ return stv0299_writeregI (state, 0x31, 0x04); ++ } ++ case FEC_5_6: ++ { ++ return stv0299_writeregI (state, 0x31, 0x08); ++ } ++ case FEC_7_8: ++ { ++ return stv0299_writeregI (state, 0x31, 0x10); ++ } ++ default: ++ { ++ return -EINVAL; ++ } ++ } ++} ++ ++static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state) ++{ ++ static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6, ++ FEC_7_8, FEC_1_2 }; ++ u8 index; ++ ++ dprintk ("%s\n", __func__); ++ ++ index = stv0299_readreg (state, 0x1b); ++ index &= 0x7; ++ ++ if (index > 4) ++ return FEC_AUTO; ++ ++ return fec_tab [index]; ++} ++ ++static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) ++{ ++ unsigned long start = jiffies; ++ ++ dprintk ("%s\n", __func__); ++ ++ while (stv0299_readreg(state, 0x0a) & 1) { ++ if (jiffies - start > timeout) { ++ dprintk ("%s: timeout!!\n", __func__); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) ++{ ++ unsigned long start = jiffies; ++ ++ dprintk ("%s\n", __func__); ++ ++ while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { ++ if (jiffies - start > timeout) { ++ dprintk ("%s: timeout!!\n", __func__); ++ return -ETIMEDOUT; ++ } ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u64 big = srate; ++ u32 ratio; ++ ++ // check rate is within limits ++ if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; ++ ++ // calculate value to program ++ big = big << 20; ++ big += (state->config->mclk-1); // round correctly ++ do_div(big, state->config->mclk); ++ ratio = big << 4; ++ ++ return state->config->set_symbol_rate(fe, srate, ratio); ++} ++ ++static int stv0299_get_symbolrate (struct stv0299_state* state) ++{ ++ u32 Mclk = state->config->mclk / 4096L; ++ u32 srate; ++ s32 offset; ++ u8 sfr[3]; ++ s8 rtf; ++ ++ dprintk ("%s\n", __func__); ++ ++ stv0299_readregs (state, 0x1f, sfr, 3); ++ stv0299_readregs (state, 0x1a, (u8 *)&rtf, 1); ++ ++ srate = (sfr[0] << 8) | sfr[1]; ++ srate *= Mclk; ++ srate /= 16; ++ srate += (sfr[2] >> 4) * Mclk / 256; ++ offset = (s32) rtf * (srate / 4096L); ++ offset /= 128; ++ ++ dprintk ("%s : srate = %i\n", __func__, srate); ++ dprintk ("%s : ofset = %i\n", __func__, offset); ++ ++ srate += offset; ++ ++ srate += 1000; ++ srate /= 2000; ++ srate *= 2000; ++ ++ return srate; ++} ++ ++static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd *m) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u8 val; ++ int i; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (stv0299_wait_diseqc_idle (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ val = stv0299_readreg (state, 0x08); ++ ++ if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ ++ return -EREMOTEIO; ++ ++ for (i=0; imsg_len; i++) { ++ if (stv0299_wait_diseqc_fifo (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ if (stv0299_writeregI (state, 0x09, m->msg[i])) ++ return -EREMOTEIO; ++ } ++ ++ if (stv0299_wait_diseqc_idle (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ return 0; ++} ++ ++static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (stv0299_wait_diseqc_idle (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ val = stv0299_readreg (state, 0x08); ++ ++ if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ ++ return -EREMOTEIO; ++ ++ if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) ++ return -EREMOTEIO; ++ ++ if (stv0299_wait_diseqc_idle (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ if (stv0299_writeregI (state, 0x08, val)) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u8 val; ++ ++ if (stv0299_wait_diseqc_idle (state, 100) < 0) ++ return -ETIMEDOUT; ++ ++ val = stv0299_readreg (state, 0x08); ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ return stv0299_writeregI (state, 0x08, val | 0x3); ++ ++ case SEC_TONE_OFF: ++ return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u8 reg0x08; ++ u8 reg0x0c; ++ ++ dprintk("%s: %s\n", __func__, ++ voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : ++ voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); ++ ++ reg0x08 = stv0299_readreg (state, 0x08); ++ reg0x0c = stv0299_readreg (state, 0x0c); ++ ++ /** ++ * H/V switching over OP0, OP1 and OP2 are LNB power enable bits ++ */ ++ reg0x0c &= 0x0f; ++ reg0x08 = (reg0x08 & 0x3f) | (state->config->lock_output << 6); ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) ++ reg0x0c |= 0x10; /* OP1 off, OP0 on */ ++ else ++ reg0x0c |= 0x40; /* OP1 on, OP0 off */ ++ break; ++ case SEC_VOLTAGE_18: ++ reg0x0c |= 0x50; /* OP1 on, OP0 on */ ++ break; ++ case SEC_VOLTAGE_OFF: ++ /* LNB power off! */ ++ reg0x08 = 0x00; ++ reg0x0c = 0x00; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (state->config->op0_off) ++ reg0x0c &= ~0x10; ++ ++ stv0299_writeregI(state, 0x08, reg0x08); ++ return stv0299_writeregI(state, 0x0c, reg0x0c); ++} ++ ++static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long cmd) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ u8 reg0x08; ++ u8 reg0x0c; ++ u8 lv_mask = 0x40; ++ u8 last = 1; ++ int i; ++ struct timeval nexttime; ++ struct timeval tv[10]; ++ ++ reg0x08 = stv0299_readreg (state, 0x08); ++ reg0x0c = stv0299_readreg (state, 0x0c); ++ reg0x0c &= 0x0f; ++ stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); ++ if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) ++ lv_mask = 0x10; ++ ++ cmd = cmd << 1; ++ if (debug_legacy_dish_switch) ++ printk ("%s switch command: 0x%04lx\n",__func__, cmd); ++ ++ do_gettimeofday (&nexttime); ++ if (debug_legacy_dish_switch) ++ tv[0] = nexttime; ++ stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ ++ ++ dvb_frontend_sleep_until(&nexttime, 32000); ++ ++ for (i=0; i<9; i++) { ++ if (debug_legacy_dish_switch) ++ do_gettimeofday (&tv[i+1]); ++ if((cmd & 0x01) != last) { ++ /* set voltage to (last ? 13V : 18V) */ ++ stv0299_writeregI (state, 0x0c, reg0x0c | (last ? lv_mask : 0x50)); ++ last = (last) ? 0 : 1; ++ } ++ ++ cmd = cmd >> 1; ++ ++ if (i != 8) ++ dvb_frontend_sleep_until(&nexttime, 8000); ++ } ++ if (debug_legacy_dish_switch) { ++ printk ("%s(%d): switch delay (should be 32k followed by all 8k\n", ++ __func__, fe->dvb->num); ++ for (i = 1; i < 10; i++) ++ printk ("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); ++ } ++ ++ return 0; ++} ++ ++static int stv0299_init (struct dvb_frontend* fe) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ int i; ++ u8 reg; ++ u8 val; ++ ++ dprintk("stv0299: init chip\n"); ++ ++ stv0299_writeregI(state, 0x02, 0x30 | state->mcr_reg); ++ msleep(50); ++ ++ for (i = 0; ; i += 2) { ++ reg = state->config->inittab[i]; ++ val = state->config->inittab[i+1]; ++ if (reg == 0xff && val == 0xff) ++ break; ++ if (reg == 0x0c && state->config->op0_off) ++ val &= ~0x10; ++ if (reg == 0x2) ++ state->mcr_reg = val & 0xf; ++ stv0299_writeregI(state, reg, val); ++ } ++ ++ return 0; ++} ++ ++static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ u8 signal = 0xff - stv0299_readreg (state, 0x18); ++ u8 sync = stv0299_readreg (state, 0x1b); ++ ++ dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); ++ *status = 0; ++ ++ if (signal > 10) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 0x80) ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 0x10) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 0x08) ++ *status |= FE_HAS_SYNC; ++ ++ if ((sync & 0x98) == 0x98) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ if (state->errmode != STATUS_BER) ++ return -ENOSYS; ++ ++ *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); ++ ++ return 0; ++} ++ ++static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8) ++ | stv0299_readreg (state, 0x19)); ++ ++ dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __func__, ++ stv0299_readreg (state, 0x18), ++ stv0299_readreg (state, 0x19), (int) signal); ++ ++ signal = signal * 5 / 4; ++ *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; ++ ++ return 0; ++} ++ ++static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8) ++ | stv0299_readreg (state, 0x25)); ++ xsnr = 3 * (xsnr - 0xa100); ++ *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; ++ ++ return 0; ++} ++ ++static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ if (state->errmode != STATUS_UCBLOCKS) ++ return -ENOSYS; ++ ++ state->ucblocks += stv0299_readreg(state, 0x1e); ++ state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); ++ *ucblocks = state->ucblocks; ++ ++ return 0; ++} ++ ++static int stv0299_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0299_state* state = fe->demodulator_priv; ++ int invval = 0; ++ ++ dprintk ("%s : FE_SET_FRONTEND\n", __func__); ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ // set the inversion ++ if (p->inversion == INVERSION_OFF) invval = 0; ++ else if (p->inversion == INVERSION_ON) invval = 1; ++ else { ++ printk("stv0299 does not support auto-inversion\n"); ++ return -EINVAL; ++ } ++ if (state->config->invert) invval = (~invval) & 1; ++ stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ stv0299_set_FEC(state, p->fec_inner); ++ stv0299_set_symbolrate(fe, p->symbol_rate); ++ stv0299_writeregI(state, 0x22, 0x00); ++ stv0299_writeregI(state, 0x23, 0x00); ++ ++ state->tuner_frequency = p->frequency; ++ state->fec_inner = p->fec_inner; ++ state->symbol_rate = p->symbol_rate; ++ ++ return 0; ++} ++ ++static int stv0299_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0299_state* state = fe->demodulator_priv; ++ s32 derot_freq; ++ int invval; ++ ++ derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8) ++ | stv0299_readreg (state, 0x23)); ++ ++ derot_freq *= (state->config->mclk >> 16); ++ derot_freq += 500; ++ derot_freq /= 1000; ++ ++ p->frequency += derot_freq; ++ ++ invval = stv0299_readreg (state, 0x0c) & 1; ++ if (state->config->invert) invval = (~invval) & 1; ++ p->inversion = invval ? INVERSION_ON : INVERSION_OFF; ++ ++ p->fec_inner = stv0299_get_fec(state); ++ p->symbol_rate = stv0299_get_symbolrate(state); ++ ++ return 0; ++} ++ ++static int stv0299_sleep(struct dvb_frontend* fe) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ stv0299_writeregI(state, 0x02, 0xb0 | state->mcr_reg); ++ state->initialised = 0; ++ ++ return 0; ++} ++ ++static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ stv0299_writeregI(state, 0x05, 0xb5); ++ } else { ++ stv0299_writeregI(state, 0x05, 0x35); ++ } ++ udelay(1); ++ return 0; ++} ++ ++static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ fesettings->min_delay_ms = state->config->min_delay_ms; ++ if (p->symbol_rate < 10000000) { ++ fesettings->step_size = p->symbol_rate / 32000; ++ fesettings->max_drift = 5000; ++ } else { ++ fesettings->step_size = p->symbol_rate / 16000; ++ fesettings->max_drift = p->symbol_rate / 2000; ++ } ++ return 0; ++} ++ ++static void stv0299_release(struct dvb_frontend* fe) ++{ ++ struct stv0299_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops stv0299_ops; ++ ++struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct stv0299_state* state = NULL; ++ int id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->initialised = 0; ++ state->tuner_frequency = 0; ++ state->symbol_rate = 0; ++ state->fec_inner = 0; ++ state->errmode = STATUS_BER; ++ ++ /* check if the demod is there */ ++ stv0299_writeregI(state, 0x02, 0x30); /* standby off */ ++ msleep(200); ++ id = stv0299_readreg(state, 0x00); ++ ++ /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ ++ /* register 0x00 might contain 0x80 when returning from standby */ ++ if (id != 0xa1 && id != 0x80) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &stv0299_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops stv0299_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "ST STV0299 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, /* ppm */ ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_QPSK | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = stv0299_release, ++ ++ .init = stv0299_init, ++ .sleep = stv0299_sleep, ++ .write = stv0299_write, ++ .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, ++ ++ .set_frontend = stv0299_set_frontend, ++ .get_frontend = stv0299_get_frontend, ++ .get_tune_settings = stv0299_get_tune_settings, ++ ++ .read_status = stv0299_read_status, ++ .read_ber = stv0299_read_ber, ++ .read_signal_strength = stv0299_read_signal_strength, ++ .read_snr = stv0299_read_snr, ++ .read_ucblocks = stv0299_read_ucblocks, ++ ++ .diseqc_send_master_cmd = stv0299_send_diseqc_msg, ++ .diseqc_send_burst = stv0299_send_diseqc_burst, ++ .set_tone = stv0299_set_tone, ++ .set_voltage = stv0299_set_voltage, ++ .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd, ++}; ++ ++module_param(debug_legacy_dish_switch, int, 0444); ++MODULE_PARM_DESC(debug_legacy_dish_switch, "Enable timing analysis for Dish Network legacy switches"); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); ++MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " ++ "Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(stv0299_attach); +diff --git a/drivers/media/dvb-frontends/stv0299.h b/drivers/media/dvb-frontends/stv0299.h +new file mode 100644 +index 0000000..ba219b7 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0299.h +@@ -0,0 +1,118 @@ ++/* ++ Driver for ST STV0299 demodulator ++ ++ Copyright (C) 2001-2002 Convergence Integrated Media GmbH ++ , ++ , ++ ++ ++ ++ Philips SU1278/SH ++ ++ Copyright (C) 2002 by Peter Schildmann ++ ++ ++ LG TDQF-S001F ++ ++ Copyright (C) 2002 Felix Domke ++ & Andreas Oberritter ++ ++ ++ Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B ++ ++ Copyright (C) 2003 Vadim Catana : ++ ++ Support for Philips SU1278 on Technotrend hardware ++ ++ Copyright (C) 2004 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef STV0299_H ++#define STV0299_H ++ ++#include ++#include "dvb_frontend.h" ++ ++#define STV0299_LOCKOUTPUT_0 0 ++#define STV0299_LOCKOUTPUT_1 1 ++#define STV0299_LOCKOUTPUT_CF 2 ++#define STV0299_LOCKOUTPUT_LK 3 ++ ++#define STV0299_VOLT13_OP0 0 ++#define STV0299_VOLT13_OP1 1 ++ ++struct stv0299_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* inittab - array of pairs of values. ++ * First of each pair is the register, second is the value. ++ * List should be terminated with an 0xff, 0xff pair. ++ */ ++ const u8* inittab; ++ ++ /* master clock to use */ ++ u32 mclk; ++ ++ /* does the inversion require inversion? */ ++ u8 invert:1; ++ ++ /* Skip reinitialisation? */ ++ u8 skip_reinit:1; ++ ++ /* LOCK OUTPUT setting */ ++ u8 lock_output:2; ++ ++ /* Is 13v controlled by OP0 or OP1? */ ++ u8 volt13_op0_op1:1; ++ ++ /* Turn-off OP0? */ ++ u8 op0_off:1; ++ ++ /* minimum delay before retuning */ ++ int min_delay_ms; ++ ++ /* Set the symbol rate */ ++ int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio); ++ ++ /* Set device param to start dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++}; ++ ++#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_STV0299 ++ ++static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { ++ int r = 0; ++ u8 buf[] = {reg, val}; ++ if (fe->ops.write) ++ r = fe->ops.write(fe, buf, 2); ++ return r; ++} ++ ++#endif // STV0299_H +diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c +new file mode 100644 +index 0000000..0c8e459 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0367.c +@@ -0,0 +1,3451 @@ ++/* ++ * stv0367.c ++ * ++ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2010,2011 NetUP Inc. ++ * Copyright (C) 2010,2011 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "stv0367.h" ++#include "stv0367_regs.h" ++#include "stv0367_priv.h" ++ ++static int stvdebug; ++module_param_named(debug, stvdebug, int, 0644); ++ ++static int i2cdebug; ++module_param_named(i2c_debug, i2cdebug, int, 0644); ++ ++#define dprintk(args...) \ ++ do { \ ++ if (stvdebug) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ /* DVB-C */ ++ ++struct stv0367cab_state { ++ enum stv0367_cab_signal_type state; ++ u32 mclk; ++ u32 adc_clk; ++ s32 search_range; ++ s32 derot_offset; ++ /* results */ ++ int locked; /* channel found */ ++ u32 freq_khz; /* found frequency (in kHz) */ ++ u32 symbol_rate; /* found symbol rate (in Bds) */ ++ enum stv0367cab_mod modulation; /* modulation */ ++ fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */ ++}; ++ ++struct stv0367ter_state { ++ /* DVB-T */ ++ enum stv0367_ter_signal_type state; ++ enum stv0367_ter_if_iq_mode if_iq_mode; ++ enum stv0367_ter_mode mode;/* mode 2K or 8K */ ++ fe_guard_interval_t guard; ++ enum stv0367_ter_hierarchy hierarchy; ++ u32 frequency; ++ fe_spectral_inversion_t sense; /* current search spectrum */ ++ u8 force; /* force mode/guard */ ++ u8 bw; /* channel width 6, 7 or 8 in MHz */ ++ u8 pBW; /* channel width used during previous lock */ ++ u32 pBER; ++ u32 pPER; ++ u32 ucblocks; ++ s8 echo_pos; /* echo position */ ++ u8 first_lock; ++ u8 unlock_counter; ++ u32 agc_val; ++}; ++ ++struct stv0367_state { ++ struct dvb_frontend fe; ++ struct i2c_adapter *i2c; ++ /* config settings */ ++ const struct stv0367_config *config; ++ u8 chip_id; ++ /* DVB-C */ ++ struct stv0367cab_state *cab_state; ++ /* DVB-T */ ++ struct stv0367ter_state *ter_state; ++}; ++ ++struct st_register { ++ u16 addr; ++ u8 value; ++}; ++ ++/* values for STV4100 XTAL=30M int clk=53.125M*/ ++static struct st_register def0367ter[STV0367TER_NBREGS] = { ++ {R367TER_ID, 0x60}, ++ {R367TER_I2CRPT, 0xa0}, ++ /* {R367TER_I2CRPT, 0x22},*/ ++ {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */ ++ {R367TER_IOCFG0, 0x40}, ++ {R367TER_DAC0R, 0x00}, ++ {R367TER_IOCFG1, 0x00}, ++ {R367TER_DAC1R, 0x00}, ++ {R367TER_IOCFG2, 0x62}, ++ {R367TER_SDFR, 0x00}, ++ {R367TER_STATUS, 0xf8}, ++ {R367TER_AUX_CLK, 0x0a}, ++ {R367TER_FREESYS1, 0x00}, ++ {R367TER_FREESYS2, 0x00}, ++ {R367TER_FREESYS3, 0x00}, ++ {R367TER_GPIO_CFG, 0x55}, ++ {R367TER_GPIO_CMD, 0x00}, ++ {R367TER_AGC2MAX, 0xff}, ++ {R367TER_AGC2MIN, 0x00}, ++ {R367TER_AGC1MAX, 0xff}, ++ {R367TER_AGC1MIN, 0x00}, ++ {R367TER_AGCR, 0xbc}, ++ {R367TER_AGC2TH, 0x00}, ++ {R367TER_AGC12C, 0x00}, ++ {R367TER_AGCCTRL1, 0x85}, ++ {R367TER_AGCCTRL2, 0x1f}, ++ {R367TER_AGC1VAL1, 0x00}, ++ {R367TER_AGC1VAL2, 0x00}, ++ {R367TER_AGC2VAL1, 0x6f}, ++ {R367TER_AGC2VAL2, 0x05}, ++ {R367TER_AGC2PGA, 0x00}, ++ {R367TER_OVF_RATE1, 0x00}, ++ {R367TER_OVF_RATE2, 0x00}, ++ {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */ ++ {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */ ++ {R367TER_INC_DEROT1, 0x55}, ++ {R367TER_INC_DEROT2, 0x55}, ++ {R367TER_PPM_CPAMP_DIR, 0x2c}, ++ {R367TER_PPM_CPAMP_INV, 0x00}, ++ {R367TER_FREESTFE_1, 0x00}, ++ {R367TER_FREESTFE_2, 0x1c}, ++ {R367TER_DCOFFSET, 0x00}, ++ {R367TER_EN_PROCESS, 0x05}, ++ {R367TER_SDI_SMOOTHER, 0x80}, ++ {R367TER_FE_LOOP_OPEN, 0x1c}, ++ {R367TER_FREQOFF1, 0x00}, ++ {R367TER_FREQOFF2, 0x00}, ++ {R367TER_FREQOFF3, 0x00}, ++ {R367TER_TIMOFF1, 0x00}, ++ {R367TER_TIMOFF2, 0x00}, ++ {R367TER_EPQ, 0x02}, ++ {R367TER_EPQAUTO, 0x01}, ++ {R367TER_SYR_UPDATE, 0xf5}, ++ {R367TER_CHPFREE, 0x00}, ++ {R367TER_PPM_STATE_MAC, 0x23}, ++ {R367TER_INR_THRESHOLD, 0xff}, ++ {R367TER_EPQ_TPS_ID_CELL, 0xf9}, ++ {R367TER_EPQ_CFG, 0x00}, ++ {R367TER_EPQ_STATUS, 0x01}, ++ {R367TER_AUTORELOCK, 0x81}, ++ {R367TER_BER_THR_VMSB, 0x00}, ++ {R367TER_BER_THR_MSB, 0x00}, ++ {R367TER_BER_THR_LSB, 0x00}, ++ {R367TER_CCD, 0x83}, ++ {R367TER_SPECTR_CFG, 0x00}, ++ {R367TER_CHC_DUMMY, 0x18}, ++ {R367TER_INC_CTL, 0x88}, ++ {R367TER_INCTHRES_COR1, 0xb4}, ++ {R367TER_INCTHRES_COR2, 0x96}, ++ {R367TER_INCTHRES_DET1, 0x0e}, ++ {R367TER_INCTHRES_DET2, 0x11}, ++ {R367TER_IIR_CELLNB, 0x8d}, ++ {R367TER_IIRCX_COEFF1_MSB, 0x00}, ++ {R367TER_IIRCX_COEFF1_LSB, 0x00}, ++ {R367TER_IIRCX_COEFF2_MSB, 0x09}, ++ {R367TER_IIRCX_COEFF2_LSB, 0x18}, ++ {R367TER_IIRCX_COEFF3_MSB, 0x14}, ++ {R367TER_IIRCX_COEFF3_LSB, 0x9c}, ++ {R367TER_IIRCX_COEFF4_MSB, 0x00}, ++ {R367TER_IIRCX_COEFF4_LSB, 0x00}, ++ {R367TER_IIRCX_COEFF5_MSB, 0x36}, ++ {R367TER_IIRCX_COEFF5_LSB, 0x42}, ++ {R367TER_FEPATH_CFG, 0x00}, ++ {R367TER_PMC1_FUNC, 0x65}, ++ {R367TER_PMC1_FOR, 0x00}, ++ {R367TER_PMC2_FUNC, 0x00}, ++ {R367TER_STATUS_ERR_DA, 0xe0}, ++ {R367TER_DIG_AGC_R, 0xfe}, ++ {R367TER_COMAGC_TARMSB, 0x0b}, ++ {R367TER_COM_AGC_TAR_ENMODE, 0x41}, ++ {R367TER_COM_AGC_CFG, 0x3e}, ++ {R367TER_COM_AGC_GAIN1, 0x39}, ++ {R367TER_AUT_AGC_TARGETMSB, 0x0b}, ++ {R367TER_LOCK_DET_MSB, 0x01}, ++ {R367TER_AGCTAR_LOCK_LSBS, 0x40}, ++ {R367TER_AUT_GAIN_EN, 0xf4}, ++ {R367TER_AUT_CFG, 0xf0}, ++ {R367TER_LOCKN, 0x23}, ++ {R367TER_INT_X_3, 0x00}, ++ {R367TER_INT_X_2, 0x03}, ++ {R367TER_INT_X_1, 0x8d}, ++ {R367TER_INT_X_0, 0xa0}, ++ {R367TER_MIN_ERRX_MSB, 0x00}, ++ {R367TER_COR_CTL, 0x23}, ++ {R367TER_COR_STAT, 0xf6}, ++ {R367TER_COR_INTEN, 0x00}, ++ {R367TER_COR_INTSTAT, 0x3f}, ++ {R367TER_COR_MODEGUARD, 0x03}, ++ {R367TER_AGC_CTL, 0x08}, ++ {R367TER_AGC_MANUAL1, 0x00}, ++ {R367TER_AGC_MANUAL2, 0x00}, ++ {R367TER_AGC_TARG, 0x16}, ++ {R367TER_AGC_GAIN1, 0x53}, ++ {R367TER_AGC_GAIN2, 0x1d}, ++ {R367TER_RESERVED_1, 0x00}, ++ {R367TER_RESERVED_2, 0x00}, ++ {R367TER_RESERVED_3, 0x00}, ++ {R367TER_CAS_CTL, 0x44}, ++ {R367TER_CAS_FREQ, 0xb3}, ++ {R367TER_CAS_DAGCGAIN, 0x12}, ++ {R367TER_SYR_CTL, 0x04}, ++ {R367TER_SYR_STAT, 0x10}, ++ {R367TER_SYR_NCO1, 0x00}, ++ {R367TER_SYR_NCO2, 0x00}, ++ {R367TER_SYR_OFFSET1, 0x00}, ++ {R367TER_SYR_OFFSET2, 0x00}, ++ {R367TER_FFT_CTL, 0x00}, ++ {R367TER_SCR_CTL, 0x70}, ++ {R367TER_PPM_CTL1, 0xf8}, ++ {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */ ++ {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */ ++ {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */ ++ {R367TER_TRL_TIME1, 0x1d}, ++ {R367TER_TRL_TIME2, 0xfc}, ++ {R367TER_CRL_CTL, 0x24}, ++ {R367TER_CRL_FREQ1, 0xad}, ++ {R367TER_CRL_FREQ2, 0x9d}, ++ {R367TER_CRL_FREQ3, 0xff}, ++ {R367TER_CHC_CTL, 0x01}, ++ {R367TER_CHC_SNR, 0xf0}, ++ {R367TER_BDI_CTL, 0x00}, ++ {R367TER_DMP_CTL, 0x00}, ++ {R367TER_TPS_RCVD1, 0x30}, ++ {R367TER_TPS_RCVD2, 0x02}, ++ {R367TER_TPS_RCVD3, 0x01}, ++ {R367TER_TPS_RCVD4, 0x00}, ++ {R367TER_TPS_ID_CELL1, 0x00}, ++ {R367TER_TPS_ID_CELL2, 0x00}, ++ {R367TER_TPS_RCVD5_SET1, 0x02}, ++ {R367TER_TPS_SET2, 0x02}, ++ {R367TER_TPS_SET3, 0x01}, ++ {R367TER_TPS_CTL, 0x00}, ++ {R367TER_CTL_FFTOSNUM, 0x34}, ++ {R367TER_TESTSELECT, 0x09}, ++ {R367TER_MSC_REV, 0x0a}, ++ {R367TER_PIR_CTL, 0x00}, ++ {R367TER_SNR_CARRIER1, 0xa1}, ++ {R367TER_SNR_CARRIER2, 0x9a}, ++ {R367TER_PPM_CPAMP, 0x2c}, ++ {R367TER_TSM_AP0, 0x00}, ++ {R367TER_TSM_AP1, 0x00}, ++ {R367TER_TSM_AP2 , 0x00}, ++ {R367TER_TSM_AP3, 0x00}, ++ {R367TER_TSM_AP4, 0x00}, ++ {R367TER_TSM_AP5, 0x00}, ++ {R367TER_TSM_AP6, 0x00}, ++ {R367TER_TSM_AP7, 0x00}, ++ {R367TER_TSTRES, 0x00}, ++ {R367TER_ANACTRL, 0x0D},/* PLL stoped, restart at init!!! */ ++ {R367TER_TSTBUS, 0x00}, ++ {R367TER_TSTRATE, 0x00}, ++ {R367TER_CONSTMODE, 0x01}, ++ {R367TER_CONSTCARR1, 0x00}, ++ {R367TER_CONSTCARR2, 0x00}, ++ {R367TER_ICONSTEL, 0x0a}, ++ {R367TER_QCONSTEL, 0x15}, ++ {R367TER_TSTBISTRES0, 0x00}, ++ {R367TER_TSTBISTRES1, 0x00}, ++ {R367TER_TSTBISTRES2, 0x28}, ++ {R367TER_TSTBISTRES3, 0x00}, ++ {R367TER_RF_AGC1, 0xff}, ++ {R367TER_RF_AGC2, 0x83}, ++ {R367TER_ANADIGCTRL, 0x19}, ++ {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */ ++ {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */ ++ {R367TER_PLLSETUP, 0x18}, ++ {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */ ++ {R367TER_TSTBIST, 0x00}, ++ {R367TER_PAD_COMP_CTRL, 0x00}, ++ {R367TER_PAD_COMP_WR, 0x00}, ++ {R367TER_PAD_COMP_RD, 0xe0}, ++ {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00}, ++ {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00}, ++ {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00}, ++ {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00}, ++ {R367TER_SYR_FLAG, 0x00}, ++ {R367TER_CRL_TARGET1, 0x00}, ++ {R367TER_CRL_TARGET2, 0x00}, ++ {R367TER_CRL_TARGET3, 0x00}, ++ {R367TER_CRL_TARGET4, 0x00}, ++ {R367TER_CRL_FLAG, 0x00}, ++ {R367TER_TRL_TARGET1, 0x00}, ++ {R367TER_TRL_TARGET2, 0x00}, ++ {R367TER_TRL_CHC, 0x00}, ++ {R367TER_CHC_SNR_TARG, 0x00}, ++ {R367TER_TOP_TRACK, 0x00}, ++ {R367TER_TRACKER_FREE1, 0x00}, ++ {R367TER_ERROR_CRL1, 0x00}, ++ {R367TER_ERROR_CRL2, 0x00}, ++ {R367TER_ERROR_CRL3, 0x00}, ++ {R367TER_ERROR_CRL4, 0x00}, ++ {R367TER_DEC_NCO1, 0x2c}, ++ {R367TER_DEC_NCO2, 0x0f}, ++ {R367TER_DEC_NCO3, 0x20}, ++ {R367TER_SNR, 0xf1}, ++ {R367TER_SYR_FFTADJ1, 0x00}, ++ {R367TER_SYR_FFTADJ2, 0x00}, ++ {R367TER_SYR_CHCADJ1, 0x00}, ++ {R367TER_SYR_CHCADJ2, 0x00}, ++ {R367TER_SYR_OFF, 0x00}, ++ {R367TER_PPM_OFFSET1, 0x00}, ++ {R367TER_PPM_OFFSET2, 0x03}, ++ {R367TER_TRACKER_FREE2, 0x00}, ++ {R367TER_DEBG_LT10, 0x00}, ++ {R367TER_DEBG_LT11, 0x00}, ++ {R367TER_DEBG_LT12, 0x00}, ++ {R367TER_DEBG_LT13, 0x00}, ++ {R367TER_DEBG_LT14, 0x00}, ++ {R367TER_DEBG_LT15, 0x00}, ++ {R367TER_DEBG_LT16, 0x00}, ++ {R367TER_DEBG_LT17, 0x00}, ++ {R367TER_DEBG_LT18, 0x00}, ++ {R367TER_DEBG_LT19, 0x00}, ++ {R367TER_DEBG_LT1A, 0x00}, ++ {R367TER_DEBG_LT1B, 0x00}, ++ {R367TER_DEBG_LT1C, 0x00}, ++ {R367TER_DEBG_LT1D, 0x00}, ++ {R367TER_DEBG_LT1E, 0x00}, ++ {R367TER_DEBG_LT1F, 0x00}, ++ {R367TER_RCCFGH, 0x00}, ++ {R367TER_RCCFGM, 0x00}, ++ {R367TER_RCCFGL, 0x00}, ++ {R367TER_RCINSDELH, 0x00}, ++ {R367TER_RCINSDELM, 0x00}, ++ {R367TER_RCINSDELL, 0x00}, ++ {R367TER_RCSTATUS, 0x00}, ++ {R367TER_RCSPEED, 0x6f}, ++ {R367TER_RCDEBUGM, 0xe7}, ++ {R367TER_RCDEBUGL, 0x9b}, ++ {R367TER_RCOBSCFG, 0x00}, ++ {R367TER_RCOBSM, 0x00}, ++ {R367TER_RCOBSL, 0x00}, ++ {R367TER_RCFECSPY, 0x00}, ++ {R367TER_RCFSPYCFG, 0x00}, ++ {R367TER_RCFSPYDATA, 0x00}, ++ {R367TER_RCFSPYOUT, 0x00}, ++ {R367TER_RCFSTATUS, 0x00}, ++ {R367TER_RCFGOODPACK, 0x00}, ++ {R367TER_RCFPACKCNT, 0x00}, ++ {R367TER_RCFSPYMISC, 0x00}, ++ {R367TER_RCFBERCPT4, 0x00}, ++ {R367TER_RCFBERCPT3, 0x00}, ++ {R367TER_RCFBERCPT2, 0x00}, ++ {R367TER_RCFBERCPT1, 0x00}, ++ {R367TER_RCFBERCPT0, 0x00}, ++ {R367TER_RCFBERERR2, 0x00}, ++ {R367TER_RCFBERERR1, 0x00}, ++ {R367TER_RCFBERERR0, 0x00}, ++ {R367TER_RCFSTATESM, 0x00}, ++ {R367TER_RCFSTATESL, 0x00}, ++ {R367TER_RCFSPYBER, 0x00}, ++ {R367TER_RCFSPYDISTM, 0x00}, ++ {R367TER_RCFSPYDISTL, 0x00}, ++ {R367TER_RCFSPYOBS7, 0x00}, ++ {R367TER_RCFSPYOBS6, 0x00}, ++ {R367TER_RCFSPYOBS5, 0x00}, ++ {R367TER_RCFSPYOBS4, 0x00}, ++ {R367TER_RCFSPYOBS3, 0x00}, ++ {R367TER_RCFSPYOBS2, 0x00}, ++ {R367TER_RCFSPYOBS1, 0x00}, ++ {R367TER_RCFSPYOBS0, 0x00}, ++ {R367TER_TSGENERAL, 0x00}, ++ {R367TER_RC1SPEED, 0x6f}, ++ {R367TER_TSGSTATUS, 0x18}, ++ {R367TER_FECM, 0x01}, ++ {R367TER_VTH12, 0xff}, ++ {R367TER_VTH23, 0xa1}, ++ {R367TER_VTH34, 0x64}, ++ {R367TER_VTH56, 0x40}, ++ {R367TER_VTH67, 0x00}, ++ {R367TER_VTH78, 0x2c}, ++ {R367TER_VITCURPUN, 0x12}, ++ {R367TER_VERROR, 0x01}, ++ {R367TER_PRVIT, 0x3f}, ++ {R367TER_VAVSRVIT, 0x00}, ++ {R367TER_VSTATUSVIT, 0xbd}, ++ {R367TER_VTHINUSE, 0xa1}, ++ {R367TER_KDIV12, 0x20}, ++ {R367TER_KDIV23, 0x40}, ++ {R367TER_KDIV34, 0x20}, ++ {R367TER_KDIV56, 0x30}, ++ {R367TER_KDIV67, 0x00}, ++ {R367TER_KDIV78, 0x30}, ++ {R367TER_SIGPOWER, 0x54}, ++ {R367TER_DEMAPVIT, 0x40}, ++ {R367TER_VITSCALE, 0x00}, ++ {R367TER_FFEC1PRG, 0x00}, ++ {R367TER_FVITCURPUN, 0x12}, ++ {R367TER_FVERROR, 0x01}, ++ {R367TER_FVSTATUSVIT, 0xbd}, ++ {R367TER_DEBUG_LT1, 0x00}, ++ {R367TER_DEBUG_LT2, 0x00}, ++ {R367TER_DEBUG_LT3, 0x00}, ++ {R367TER_TSTSFMET, 0x00}, ++ {R367TER_SELOUT, 0x00}, ++ {R367TER_TSYNC, 0x00}, ++ {R367TER_TSTERR, 0x00}, ++ {R367TER_TSFSYNC, 0x00}, ++ {R367TER_TSTSFERR, 0x00}, ++ {R367TER_TSTTSSF1, 0x01}, ++ {R367TER_TSTTSSF2, 0x1f}, ++ {R367TER_TSTTSSF3, 0x00}, ++ {R367TER_TSTTS1, 0x00}, ++ {R367TER_TSTTS2, 0x1f}, ++ {R367TER_TSTTS3, 0x01}, ++ {R367TER_TSTTS4, 0x00}, ++ {R367TER_TSTTSRC, 0x00}, ++ {R367TER_TSTTSRS, 0x00}, ++ {R367TER_TSSTATEM, 0xb0}, ++ {R367TER_TSSTATEL, 0x40}, ++ {R367TER_TSCFGH, 0xC0}, ++ {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */ ++ {R367TER_TSCFGL, 0x20}, ++ {R367TER_TSSYNC, 0x00}, ++ {R367TER_TSINSDELH, 0x00}, ++ {R367TER_TSINSDELM, 0x00}, ++ {R367TER_TSINSDELL, 0x00}, ++ {R367TER_TSDIVN, 0x03}, ++ {R367TER_TSDIVPM, 0x00}, ++ {R367TER_TSDIVPL, 0x00}, ++ {R367TER_TSDIVQM, 0x00}, ++ {R367TER_TSDIVQL, 0x00}, ++ {R367TER_TSDILSTKM, 0x00}, ++ {R367TER_TSDILSTKL, 0x00}, ++ {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */ ++ {R367TER_TSSTATUS, 0x81}, ++ {R367TER_TSSTATUS2, 0x6a}, ++ {R367TER_TSBITRATEM, 0x0f}, ++ {R367TER_TSBITRATEL, 0xc6}, ++ {R367TER_TSPACKLENM, 0x00}, ++ {R367TER_TSPACKLENL, 0xfc}, ++ {R367TER_TSBLOCLENM, 0x0a}, ++ {R367TER_TSBLOCLENL, 0x80}, ++ {R367TER_TSDLYH, 0x90}, ++ {R367TER_TSDLYM, 0x68}, ++ {R367TER_TSDLYL, 0x01}, ++ {R367TER_TSNPDAV, 0x00}, ++ {R367TER_TSBUFSTATH, 0x00}, ++ {R367TER_TSBUFSTATM, 0x00}, ++ {R367TER_TSBUFSTATL, 0x00}, ++ {R367TER_TSDEBUGM, 0xcf}, ++ {R367TER_TSDEBUGL, 0x1e}, ++ {R367TER_TSDLYSETH, 0x00}, ++ {R367TER_TSDLYSETM, 0x68}, ++ {R367TER_TSDLYSETL, 0x00}, ++ {R367TER_TSOBSCFG, 0x00}, ++ {R367TER_TSOBSM, 0x47}, ++ {R367TER_TSOBSL, 0x1f}, ++ {R367TER_ERRCTRL1, 0x95}, ++ {R367TER_ERRCNT1H, 0x80}, ++ {R367TER_ERRCNT1M, 0x00}, ++ {R367TER_ERRCNT1L, 0x00}, ++ {R367TER_ERRCTRL2, 0x95}, ++ {R367TER_ERRCNT2H, 0x00}, ++ {R367TER_ERRCNT2M, 0x00}, ++ {R367TER_ERRCNT2L, 0x00}, ++ {R367TER_FECSPY, 0x88}, ++ {R367TER_FSPYCFG, 0x2c}, ++ {R367TER_FSPYDATA, 0x3a}, ++ {R367TER_FSPYOUT, 0x06}, ++ {R367TER_FSTATUS, 0x61}, ++ {R367TER_FGOODPACK, 0xff}, ++ {R367TER_FPACKCNT, 0xff}, ++ {R367TER_FSPYMISC, 0x66}, ++ {R367TER_FBERCPT4, 0x00}, ++ {R367TER_FBERCPT3, 0x00}, ++ {R367TER_FBERCPT2, 0x36}, ++ {R367TER_FBERCPT1, 0x36}, ++ {R367TER_FBERCPT0, 0x14}, ++ {R367TER_FBERERR2, 0x00}, ++ {R367TER_FBERERR1, 0x03}, ++ {R367TER_FBERERR0, 0x28}, ++ {R367TER_FSTATESM, 0x00}, ++ {R367TER_FSTATESL, 0x02}, ++ {R367TER_FSPYBER, 0x00}, ++ {R367TER_FSPYDISTM, 0x01}, ++ {R367TER_FSPYDISTL, 0x9f}, ++ {R367TER_FSPYOBS7, 0xc9}, ++ {R367TER_FSPYOBS6, 0x99}, ++ {R367TER_FSPYOBS5, 0x08}, ++ {R367TER_FSPYOBS4, 0xec}, ++ {R367TER_FSPYOBS3, 0x01}, ++ {R367TER_FSPYOBS2, 0x0f}, ++ {R367TER_FSPYOBS1, 0xf5}, ++ {R367TER_FSPYOBS0, 0x08}, ++ {R367TER_SFDEMAP, 0x40}, ++ {R367TER_SFERROR, 0x00}, ++ {R367TER_SFAVSR, 0x30}, ++ {R367TER_SFECSTATUS, 0xcc}, ++ {R367TER_SFKDIV12, 0x20}, ++ {R367TER_SFKDIV23, 0x40}, ++ {R367TER_SFKDIV34, 0x20}, ++ {R367TER_SFKDIV56, 0x20}, ++ {R367TER_SFKDIV67, 0x00}, ++ {R367TER_SFKDIV78, 0x20}, ++ {R367TER_SFDILSTKM, 0x00}, ++ {R367TER_SFDILSTKL, 0x00}, ++ {R367TER_SFSTATUS, 0xb5}, ++ {R367TER_SFDLYH, 0x90}, ++ {R367TER_SFDLYM, 0x60}, ++ {R367TER_SFDLYL, 0x01}, ++ {R367TER_SFDLYSETH, 0xc0}, ++ {R367TER_SFDLYSETM, 0x60}, ++ {R367TER_SFDLYSETL, 0x00}, ++ {R367TER_SFOBSCFG, 0x00}, ++ {R367TER_SFOBSM, 0x47}, ++ {R367TER_SFOBSL, 0x05}, ++ {R367TER_SFECINFO, 0x40}, ++ {R367TER_SFERRCTRL, 0x74}, ++ {R367TER_SFERRCNTH, 0x80}, ++ {R367TER_SFERRCNTM , 0x00}, ++ {R367TER_SFERRCNTL, 0x00}, ++ {R367TER_SYMBRATEM, 0x2f}, ++ {R367TER_SYMBRATEL, 0x50}, ++ {R367TER_SYMBSTATUS, 0x7f}, ++ {R367TER_SYMBCFG, 0x00}, ++ {R367TER_SYMBFIFOM, 0xf4}, ++ {R367TER_SYMBFIFOL, 0x0d}, ++ {R367TER_SYMBOFFSM, 0xf0}, ++ {R367TER_SYMBOFFSL, 0x2d}, ++ {R367TER_DEBUG_LT4, 0x00}, ++ {R367TER_DEBUG_LT5, 0x00}, ++ {R367TER_DEBUG_LT6, 0x00}, ++ {R367TER_DEBUG_LT7, 0x00}, ++ {R367TER_DEBUG_LT8, 0x00}, ++ {R367TER_DEBUG_LT9, 0x00}, ++}; ++ ++#define RF_LOOKUP_TABLE_SIZE 31 ++#define RF_LOOKUP_TABLE2_SIZE 16 ++/* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/ ++s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { ++ {/*AGC1*/ ++ 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63, ++ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, ++ 76, 77, 78, 80, 83, 85, 88, ++ }, {/*RF(dbm)*/ ++ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, ++ 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 46, 47, ++ 49, 50, 52, 53, 54, 55, 56, ++ } ++}; ++/* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/ ++s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { ++ {/*AGC2*/ ++ 28, 29, 31, 32, 34, 35, 36, 37, ++ 38, 39, 40, 41, 42, 43, 44, 45, ++ }, {/*RF(dbm)*/ ++ 57, 58, 59, 60, 61, 62, 63, 64, ++ 65, 66, 67, 68, 69, 70, 71, 72, ++ } ++}; ++ ++static struct st_register def0367cab[STV0367CAB_NBREGS] = { ++ {R367CAB_ID, 0x60}, ++ {R367CAB_I2CRPT, 0xa0}, ++ /*{R367CAB_I2CRPT, 0x22},*/ ++ {R367CAB_TOPCTRL, 0x10}, ++ {R367CAB_IOCFG0, 0x80}, ++ {R367CAB_DAC0R, 0x00}, ++ {R367CAB_IOCFG1, 0x00}, ++ {R367CAB_DAC1R, 0x00}, ++ {R367CAB_IOCFG2, 0x00}, ++ {R367CAB_SDFR, 0x00}, ++ {R367CAB_AUX_CLK, 0x00}, ++ {R367CAB_FREESYS1, 0x00}, ++ {R367CAB_FREESYS2, 0x00}, ++ {R367CAB_FREESYS3, 0x00}, ++ {R367CAB_GPIO_CFG, 0x55}, ++ {R367CAB_GPIO_CMD, 0x01}, ++ {R367CAB_TSTRES, 0x00}, ++ {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/ ++ {R367CAB_TSTBUS, 0x00}, ++ {R367CAB_RF_AGC1, 0xea}, ++ {R367CAB_RF_AGC2, 0x82}, ++ {R367CAB_ANADIGCTRL, 0x0b}, ++ {R367CAB_PLLMDIV, 0x01}, ++ {R367CAB_PLLNDIV, 0x08}, ++ {R367CAB_PLLSETUP, 0x18}, ++ {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */ ++ {R367CAB_TSTBIST, 0x00}, ++ {R367CAB_CTRL_1, 0x00}, ++ {R367CAB_CTRL_2, 0x03}, ++ {R367CAB_IT_STATUS1, 0x2b}, ++ {R367CAB_IT_STATUS2, 0x08}, ++ {R367CAB_IT_EN1, 0x00}, ++ {R367CAB_IT_EN2, 0x00}, ++ {R367CAB_CTRL_STATUS, 0x04}, ++ {R367CAB_TEST_CTL, 0x00}, ++ {R367CAB_AGC_CTL, 0x73}, ++ {R367CAB_AGC_IF_CFG, 0x50}, ++ {R367CAB_AGC_RF_CFG, 0x00}, ++ {R367CAB_AGC_PWM_CFG, 0x03}, ++ {R367CAB_AGC_PWR_REF_L, 0x5a}, ++ {R367CAB_AGC_PWR_REF_H, 0x00}, ++ {R367CAB_AGC_RF_TH_L, 0xff}, ++ {R367CAB_AGC_RF_TH_H, 0x07}, ++ {R367CAB_AGC_IF_LTH_L, 0x00}, ++ {R367CAB_AGC_IF_LTH_H, 0x08}, ++ {R367CAB_AGC_IF_HTH_L, 0xff}, ++ {R367CAB_AGC_IF_HTH_H, 0x07}, ++ {R367CAB_AGC_PWR_RD_L, 0xa0}, ++ {R367CAB_AGC_PWR_RD_M, 0xe9}, ++ {R367CAB_AGC_PWR_RD_H, 0x03}, ++ {R367CAB_AGC_PWM_IFCMD_L, 0xe4}, ++ {R367CAB_AGC_PWM_IFCMD_H, 0x00}, ++ {R367CAB_AGC_PWM_RFCMD_L, 0xff}, ++ {R367CAB_AGC_PWM_RFCMD_H, 0x07}, ++ {R367CAB_IQDEM_CFG, 0x01}, ++ {R367CAB_MIX_NCO_LL, 0x22}, ++ {R367CAB_MIX_NCO_HL, 0x96}, ++ {R367CAB_MIX_NCO_HH, 0x55}, ++ {R367CAB_SRC_NCO_LL, 0xff}, ++ {R367CAB_SRC_NCO_LH, 0x0c}, ++ {R367CAB_SRC_NCO_HL, 0xf5}, ++ {R367CAB_SRC_NCO_HH, 0x20}, ++ {R367CAB_IQDEM_GAIN_SRC_L, 0x06}, ++ {R367CAB_IQDEM_GAIN_SRC_H, 0x01}, ++ {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe}, ++ {R367CAB_IQDEM_DCRM_CFG_LH, 0xff}, ++ {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f}, ++ {R367CAB_IQDEM_DCRM_CFG_HH, 0x00}, ++ {R367CAB_IQDEM_ADJ_COEFF0, 0x34}, ++ {R367CAB_IQDEM_ADJ_COEFF1, 0xae}, ++ {R367CAB_IQDEM_ADJ_COEFF2, 0x46}, ++ {R367CAB_IQDEM_ADJ_COEFF3, 0x77}, ++ {R367CAB_IQDEM_ADJ_COEFF4, 0x96}, ++ {R367CAB_IQDEM_ADJ_COEFF5, 0x69}, ++ {R367CAB_IQDEM_ADJ_COEFF6, 0xc7}, ++ {R367CAB_IQDEM_ADJ_COEFF7, 0x01}, ++ {R367CAB_IQDEM_ADJ_EN, 0x04}, ++ {R367CAB_IQDEM_ADJ_AGC_REF, 0x94}, ++ {R367CAB_ALLPASSFILT1, 0xc9}, ++ {R367CAB_ALLPASSFILT2, 0x2d}, ++ {R367CAB_ALLPASSFILT3, 0xa3}, ++ {R367CAB_ALLPASSFILT4, 0xfb}, ++ {R367CAB_ALLPASSFILT5, 0xf6}, ++ {R367CAB_ALLPASSFILT6, 0x45}, ++ {R367CAB_ALLPASSFILT7, 0x6f}, ++ {R367CAB_ALLPASSFILT8, 0x7e}, ++ {R367CAB_ALLPASSFILT9, 0x05}, ++ {R367CAB_ALLPASSFILT10, 0x0a}, ++ {R367CAB_ALLPASSFILT11, 0x51}, ++ {R367CAB_TRL_AGC_CFG, 0x20}, ++ {R367CAB_TRL_LPF_CFG, 0x28}, ++ {R367CAB_TRL_LPF_ACQ_GAIN, 0x44}, ++ {R367CAB_TRL_LPF_TRK_GAIN, 0x22}, ++ {R367CAB_TRL_LPF_OUT_GAIN, 0x03}, ++ {R367CAB_TRL_LOCKDET_LTH, 0x04}, ++ {R367CAB_TRL_LOCKDET_HTH, 0x11}, ++ {R367CAB_TRL_LOCKDET_TRGVAL, 0x20}, ++ {R367CAB_IQ_QAM, 0x01}, ++ {R367CAB_FSM_STATE, 0xa0}, ++ {R367CAB_FSM_CTL, 0x08}, ++ {R367CAB_FSM_STS, 0x0c}, ++ {R367CAB_FSM_SNR0_HTH, 0x00}, ++ {R367CAB_FSM_SNR1_HTH, 0x00}, ++ {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */ ++ {R367CAB_FSM_SNR0_LTH, 0x00}, ++ {R367CAB_FSM_SNR1_LTH, 0x00}, ++ {R367CAB_FSM_EQA1_HTH, 0x00}, ++ {R367CAB_FSM_TEMPO, 0x32}, ++ {R367CAB_FSM_CONFIG, 0x03}, ++ {R367CAB_EQU_I_TESTTAP_L, 0x11}, ++ {R367CAB_EQU_I_TESTTAP_M, 0x00}, ++ {R367CAB_EQU_I_TESTTAP_H, 0x00}, ++ {R367CAB_EQU_TESTAP_CFG, 0x00}, ++ {R367CAB_EQU_Q_TESTTAP_L, 0xff}, ++ {R367CAB_EQU_Q_TESTTAP_M, 0x00}, ++ {R367CAB_EQU_Q_TESTTAP_H, 0x00}, ++ {R367CAB_EQU_TAP_CTRL, 0x00}, ++ {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11}, ++ {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05}, ++ {R367CAB_EQU_CTR_HIPOW_L, 0x00}, ++ {R367CAB_EQU_CTR_HIPOW_H, 0x00}, ++ {R367CAB_EQU_I_EQU_LO, 0xef}, ++ {R367CAB_EQU_I_EQU_HI, 0x00}, ++ {R367CAB_EQU_Q_EQU_LO, 0xee}, ++ {R367CAB_EQU_Q_EQU_HI, 0x00}, ++ {R367CAB_EQU_MAPPER, 0xc5}, ++ {R367CAB_EQU_SWEEP_RATE, 0x80}, ++ {R367CAB_EQU_SNR_LO, 0x64}, ++ {R367CAB_EQU_SNR_HI, 0x03}, ++ {R367CAB_EQU_GAMMA_LO, 0x00}, ++ {R367CAB_EQU_GAMMA_HI, 0x00}, ++ {R367CAB_EQU_ERR_GAIN, 0x36}, ++ {R367CAB_EQU_RADIUS, 0xaa}, ++ {R367CAB_EQU_FFE_MAINTAP, 0x00}, ++ {R367CAB_EQU_FFE_LEAKAGE, 0x63}, ++ {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf}, ++ {R367CAB_EQU_GAIN_WIDE, 0x88}, ++ {R367CAB_EQU_GAIN_NARROW, 0x41}, ++ {R367CAB_EQU_CTR_LPF_GAIN, 0xd1}, ++ {R367CAB_EQU_CRL_LPF_GAIN, 0xa7}, ++ {R367CAB_EQU_GLOBAL_GAIN, 0x06}, ++ {R367CAB_EQU_CRL_LD_SEN, 0x85}, ++ {R367CAB_EQU_CRL_LD_VAL, 0xe2}, ++ {R367CAB_EQU_CRL_TFR, 0x20}, ++ {R367CAB_EQU_CRL_BISTH_LO, 0x00}, ++ {R367CAB_EQU_CRL_BISTH_HI, 0x00}, ++ {R367CAB_EQU_SWEEP_RANGE_LO, 0x00}, ++ {R367CAB_EQU_SWEEP_RANGE_HI, 0x00}, ++ {R367CAB_EQU_CRL_LIMITER, 0x40}, ++ {R367CAB_EQU_MODULUS_MAP, 0x90}, ++ {R367CAB_EQU_PNT_GAIN, 0xa7}, ++ {R367CAB_FEC_AC_CTR_0, 0x16}, ++ {R367CAB_FEC_AC_CTR_1, 0x0b}, ++ {R367CAB_FEC_AC_CTR_2, 0x88}, ++ {R367CAB_FEC_AC_CTR_3, 0x02}, ++ {R367CAB_FEC_STATUS, 0x12}, ++ {R367CAB_RS_COUNTER_0, 0x7d}, ++ {R367CAB_RS_COUNTER_1, 0xd0}, ++ {R367CAB_RS_COUNTER_2, 0x19}, ++ {R367CAB_RS_COUNTER_3, 0x0b}, ++ {R367CAB_RS_COUNTER_4, 0xa3}, ++ {R367CAB_RS_COUNTER_5, 0x00}, ++ {R367CAB_BERT_0, 0x01}, ++ {R367CAB_BERT_1, 0x25}, ++ {R367CAB_BERT_2, 0x41}, ++ {R367CAB_BERT_3, 0x39}, ++ {R367CAB_OUTFORMAT_0, 0xc2}, ++ {R367CAB_OUTFORMAT_1, 0x22}, ++ {R367CAB_SMOOTHER_2, 0x28}, ++ {R367CAB_TSMF_CTRL_0, 0x01}, ++ {R367CAB_TSMF_CTRL_1, 0xc6}, ++ {R367CAB_TSMF_CTRL_3, 0x43}, ++ {R367CAB_TS_ON_ID_0, 0x00}, ++ {R367CAB_TS_ON_ID_1, 0x00}, ++ {R367CAB_TS_ON_ID_2, 0x00}, ++ {R367CAB_TS_ON_ID_3, 0x00}, ++ {R367CAB_RE_STATUS_0, 0x00}, ++ {R367CAB_RE_STATUS_1, 0x00}, ++ {R367CAB_RE_STATUS_2, 0x00}, ++ {R367CAB_RE_STATUS_3, 0x00}, ++ {R367CAB_TS_STATUS_0, 0x00}, ++ {R367CAB_TS_STATUS_1, 0x00}, ++ {R367CAB_TS_STATUS_2, 0xa0}, ++ {R367CAB_TS_STATUS_3, 0x00}, ++ {R367CAB_T_O_ID_0, 0x00}, ++ {R367CAB_T_O_ID_1, 0x00}, ++ {R367CAB_T_O_ID_2, 0x00}, ++ {R367CAB_T_O_ID_3, 0x00}, ++}; ++ ++static ++int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) ++{ ++ u8 buf[len + 2]; ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = buf, ++ .len = len + 2 ++ }; ++ int ret; ++ ++ buf[0] = MSB(reg); ++ buf[1] = LSB(reg); ++ memcpy(buf + 2, data, len); ++ ++ if (i2cdebug) ++ printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c write error!\n", __func__); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static int stv0367_writereg(struct stv0367_state *state, u16 reg, u8 data) ++{ ++ return stv0367_writeregs(state, reg, &data, 1); ++} ++ ++static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) ++{ ++ u8 b0[] = { 0, 0 }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 2 ++ }, { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ int ret; ++ ++ b0[0] = MSB(reg); ++ b0[1] = LSB(reg); ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) ++ printk(KERN_ERR "%s: i2c read error\n", __func__); ++ ++ if (i2cdebug) ++ printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]); ++ ++ return b1[0]; ++} ++ ++static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) ++{ ++ u8 position = 0, i = 0; ++ ++ (*mask) = label & 0xff; ++ ++ while ((position == 0) && (i < 8)) { ++ position = ((*mask) >> i) & 0x01; ++ i++; ++ } ++ ++ (*pos) = (i - 1); ++} ++ ++static void stv0367_writebits(struct stv0367_state *state, u32 label, u8 val) ++{ ++ u8 reg, mask, pos; ++ ++ reg = stv0367_readreg(state, (label >> 16) & 0xffff); ++ extract_mask_pos(label, &mask, &pos); ++ ++ val = mask & (val << pos); ++ ++ reg = (reg & (~mask)) | val; ++ stv0367_writereg(state, (label >> 16) & 0xffff, reg); ++ ++} ++ ++static void stv0367_setbits(u8 *reg, u32 label, u8 val) ++{ ++ u8 mask, pos; ++ ++ extract_mask_pos(label, &mask, &pos); ++ ++ val = mask & (val << pos); ++ ++ (*reg) = ((*reg) & (~mask)) | val; ++} ++ ++static u8 stv0367_readbits(struct stv0367_state *state, u32 label) ++{ ++ u8 val = 0xff; ++ u8 mask, pos; ++ ++ extract_mask_pos(label, &mask, &pos); ++ ++ val = stv0367_readreg(state, label >> 16); ++ val = (val & mask) >> pos; ++ ++ return val; ++} ++ ++#if 0 /* Currently, unused */ ++static u8 stv0367_getbits(u8 reg, u32 label) ++{ ++ u8 mask, pos; ++ ++ extract_mask_pos(label, &mask, &pos); ++ ++ return (reg & mask) >> pos; ++} ++#endif ++static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ u8 tmp = stv0367_readreg(state, R367TER_I2CRPT); ++ ++ dprintk("%s:\n", __func__); ++ ++ if (enable) { ++ stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 0); ++ stv0367_setbits(&tmp, F367TER_I2CT_ON, 1); ++ } else { ++ stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 1); ++ stv0367_setbits(&tmp, F367TER_I2CT_ON, 0); ++ } ++ ++ stv0367_writereg(state, R367TER_I2CRPT, tmp); ++ ++ return 0; ++} ++ ++static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ u32 freq = 0; ++ int err = 0; ++ ++ dprintk("%s:\n", __func__); ++ ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_frequency) { ++ err = tuner_ops->get_frequency(fe, &freq); ++ if (err < 0) { ++ printk(KERN_ERR "%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ ++ dprintk("%s: frequency=%d\n", __func__, freq); ++ ++ } else ++ return -1; ++ ++ return freq; ++} ++ ++static u16 CellsCoeffs_8MHz_367cofdm[3][6][5] = { ++ { ++ {0x10EF, 0xE205, 0x10EF, 0xCE49, 0x6DA7}, /* CELL 1 COEFFS 27M*/ ++ {0x2151, 0xc557, 0x2151, 0xc705, 0x6f93}, /* CELL 2 COEFFS */ ++ {0x2503, 0xc000, 0x2503, 0xc375, 0x7194}, /* CELL 3 COEFFS */ ++ {0x20E9, 0xca94, 0x20e9, 0xc153, 0x7194}, /* CELL 4 COEFFS */ ++ {0x06EF, 0xF852, 0x06EF, 0xC057, 0x7207}, /* CELL 5 COEFFS */ ++ {0x0000, 0x0ECC, 0x0ECC, 0x0000, 0x3647} /* CELL 6 COEFFS */ ++ }, { ++ {0x10A0, 0xE2AF, 0x10A1, 0xCE76, 0x6D6D}, /* CELL 1 COEFFS 25M*/ ++ {0x20DC, 0xC676, 0x20D9, 0xC80A, 0x6F29}, ++ {0x2532, 0xC000, 0x251D, 0xC391, 0x706F}, ++ {0x1F7A, 0xCD2B, 0x2032, 0xC15E, 0x711F}, ++ {0x0698, 0xFA5E, 0x0568, 0xC059, 0x7193}, ++ {0x0000, 0x0918, 0x149C, 0x0000, 0x3642} /* CELL 6 COEFFS */ ++ }, { ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} ++ } ++}; ++ ++static u16 CellsCoeffs_7MHz_367cofdm[3][6][5] = { ++ { ++ {0x12CA, 0xDDAF, 0x12CA, 0xCCEB, 0x6FB1}, /* CELL 1 COEFFS 27M*/ ++ {0x2329, 0xC000, 0x2329, 0xC6B0, 0x725F}, /* CELL 2 COEFFS */ ++ {0x2394, 0xC000, 0x2394, 0xC2C7, 0x7410}, /* CELL 3 COEFFS */ ++ {0x251C, 0xC000, 0x251C, 0xC103, 0x74D9}, /* CELL 4 COEFFS */ ++ {0x0804, 0xF546, 0x0804, 0xC040, 0x7544}, /* CELL 5 COEFFS */ ++ {0x0000, 0x0CD9, 0x0CD9, 0x0000, 0x370A} /* CELL 6 COEFFS */ ++ }, { ++ {0x1285, 0xDE47, 0x1285, 0xCD17, 0x6F76}, /*25M*/ ++ {0x234C, 0xC000, 0x2348, 0xC6DA, 0x7206}, ++ {0x23B4, 0xC000, 0x23AC, 0xC2DB, 0x73B3}, ++ {0x253D, 0xC000, 0x25B6, 0xC10B, 0x747F}, ++ {0x0721, 0xF79C, 0x065F, 0xC041, 0x74EB}, ++ {0x0000, 0x08FA, 0x1162, 0x0000, 0x36FF} ++ }, { ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} ++ } ++}; ++ ++static u16 CellsCoeffs_6MHz_367cofdm[3][6][5] = { ++ { ++ {0x1699, 0xD5B8, 0x1699, 0xCBC3, 0x713B}, /* CELL 1 COEFFS 27M*/ ++ {0x2245, 0xC000, 0x2245, 0xC568, 0x74D5}, /* CELL 2 COEFFS */ ++ {0x227F, 0xC000, 0x227F, 0xC1FC, 0x76C6}, /* CELL 3 COEFFS */ ++ {0x235E, 0xC000, 0x235E, 0xC0A7, 0x778A}, /* CELL 4 COEFFS */ ++ {0x0ECB, 0xEA0B, 0x0ECB, 0xC027, 0x77DD}, /* CELL 5 COEFFS */ ++ {0x0000, 0x0B68, 0x0B68, 0x0000, 0xC89A}, /* CELL 6 COEFFS */ ++ }, { ++ {0x1655, 0xD64E, 0x1658, 0xCBEF, 0x70FE}, /*25M*/ ++ {0x225E, 0xC000, 0x2256, 0xC589, 0x7489}, ++ {0x2293, 0xC000, 0x2295, 0xC209, 0x767E}, ++ {0x2377, 0xC000, 0x23AA, 0xC0AB, 0x7746}, ++ {0x0DC7, 0xEBC8, 0x0D07, 0xC027, 0x7799}, ++ {0x0000, 0x0888, 0x0E9C, 0x0000, 0x3757} ++ ++ }, { ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, ++ {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} ++ } ++}; ++ ++static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz) ++{ ++ u32 mclk_Hz = 0; /* master clock frequency (Hz) */ ++ u32 m, n, p; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (stv0367_readbits(state, F367TER_BYPASS_PLLXN) == 0) { ++ n = (u32)stv0367_readbits(state, F367TER_PLL_NDIV); ++ if (n == 0) ++ n = n + 1; ++ ++ m = (u32)stv0367_readbits(state, F367TER_PLL_MDIV); ++ if (m == 0) ++ m = m + 1; ++ ++ p = (u32)stv0367_readbits(state, F367TER_PLL_PDIV); ++ if (p > 5) ++ p = 5; ++ ++ mclk_Hz = ((ExtClk_Hz / 2) * n) / (m * (1 << p)); ++ ++ dprintk("N=%d M=%d P=%d mclk_Hz=%d ExtClk_Hz=%d\n", ++ n, m, p, mclk_Hz, ExtClk_Hz); ++ } else ++ mclk_Hz = ExtClk_Hz; ++ ++ dprintk("%s: mclk_Hz=%d\n", __func__, mclk_Hz); ++ ++ return mclk_Hz; ++} ++ ++static int stv0367ter_filt_coeff_init(struct stv0367_state *state, ++ u16 CellsCoeffs[3][6][5], u32 DemodXtal) ++{ ++ int i, j, k, freq; ++ ++ dprintk("%s:\n", __func__); ++ ++ freq = stv0367ter_get_mclk(state, DemodXtal); ++ ++ if (freq == 53125000) ++ k = 1; /* equivalent to Xtal 25M on 362*/ ++ else if (freq == 54000000) ++ k = 0; /* equivalent to Xtal 27M on 362*/ ++ else if (freq == 52500000) ++ k = 2; /* equivalent to Xtal 30M on 362*/ ++ else ++ return 0; ++ ++ for (i = 1; i <= 6; i++) { ++ stv0367_writebits(state, F367TER_IIR_CELL_NB, i - 1); ++ ++ for (j = 1; j <= 5; j++) { ++ stv0367_writereg(state, ++ (R367TER_IIRCX_COEFF1_MSB + 2 * (j - 1)), ++ MSB(CellsCoeffs[k][i-1][j-1])); ++ stv0367_writereg(state, ++ (R367TER_IIRCX_COEFF1_LSB + 2 * (j - 1)), ++ LSB(CellsCoeffs[k][i-1][j-1])); ++ } ++ } ++ ++ return 1; ++ ++} ++ ++static void stv0367ter_agc_iir_lock_detect_set(struct stv0367_state *state) ++{ ++ dprintk("%s:\n", __func__); ++ ++ stv0367_writebits(state, F367TER_LOCK_DETECT_LSB, 0x00); ++ ++ /* Lock detect 1 */ ++ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x00); ++ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); ++ ++ /* Lock detect 2 */ ++ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x01); ++ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); ++ ++ /* Lock detect 3 */ ++ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x02); ++ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); ++ ++ /* Lock detect 4 */ ++ stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x03); ++ stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); ++ ++} ++ ++static int stv0367_iir_filt_init(struct stv0367_state *state, u8 Bandwidth, ++ u32 DemodXtalValue) ++{ ++ dprintk("%s:\n", __func__); ++ ++ stv0367_writebits(state, F367TER_NRST_IIR, 0); ++ ++ switch (Bandwidth) { ++ case 6: ++ if (!stv0367ter_filt_coeff_init(state, ++ CellsCoeffs_6MHz_367cofdm, ++ DemodXtalValue)) ++ return 0; ++ break; ++ case 7: ++ if (!stv0367ter_filt_coeff_init(state, ++ CellsCoeffs_7MHz_367cofdm, ++ DemodXtalValue)) ++ return 0; ++ break; ++ case 8: ++ if (!stv0367ter_filt_coeff_init(state, ++ CellsCoeffs_8MHz_367cofdm, ++ DemodXtalValue)) ++ return 0; ++ break; ++ default: ++ return 0; ++ } ++ ++ stv0367_writebits(state, F367TER_NRST_IIR, 1); ++ ++ return 1; ++} ++ ++static void stv0367ter_agc_iir_rst(struct stv0367_state *state) ++{ ++ ++ u8 com_n; ++ ++ dprintk("%s:\n", __func__); ++ ++ com_n = stv0367_readbits(state, F367TER_COM_N); ++ ++ stv0367_writebits(state, F367TER_COM_N, 0x07); ++ ++ stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x00); ++ stv0367_writebits(state, F367TER_COM_AGC_ON, 0x00); ++ ++ stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x01); ++ stv0367_writebits(state, F367TER_COM_AGC_ON, 0x01); ++ ++ stv0367_writebits(state, F367TER_COM_N, com_n); ++ ++} ++ ++static int stv0367ter_duration(s32 mode, int tempo1, int tempo2, int tempo3) ++{ ++ int local_tempo = 0; ++ switch (mode) { ++ case 0: ++ local_tempo = tempo1; ++ break; ++ case 1: ++ local_tempo = tempo2; ++ break ; ++ ++ case 2: ++ local_tempo = tempo3; ++ break; ++ ++ default: ++ break; ++ } ++ /* msleep(local_tempo); */ ++ return local_tempo; ++} ++ ++static enum ++stv0367_ter_signal_type stv0367ter_check_syr(struct stv0367_state *state) ++{ ++ int wd = 100; ++ unsigned short int SYR_var; ++ s32 SYRStatus; ++ ++ dprintk("%s:\n", __func__); ++ ++ SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); ++ ++ while ((!SYR_var) && (wd > 0)) { ++ usleep_range(2000, 3000); ++ wd -= 2; ++ SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); ++ } ++ ++ if (!SYR_var) ++ SYRStatus = FE_TER_NOSYMBOL; ++ else ++ SYRStatus = FE_TER_SYMBOLOK; ++ ++ dprintk("stv0367ter_check_syr SYRStatus %s\n", ++ SYR_var == 0 ? "No Symbol" : "OK"); ++ ++ return SYRStatus; ++} ++ ++static enum ++stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state, ++ s32 FFTmode) ++{ ++ ++ s32 CPAMPvalue = 0, CPAMPStatus, CPAMPMin; ++ int wd = 0; ++ ++ dprintk("%s:\n", __func__); ++ ++ switch (FFTmode) { ++ case 0: /*2k mode*/ ++ CPAMPMin = 20; ++ wd = 10; ++ break; ++ case 1: /*8k mode*/ ++ CPAMPMin = 80; ++ wd = 55; ++ break; ++ case 2: /*4k mode*/ ++ CPAMPMin = 40; ++ wd = 30; ++ break; ++ default: ++ CPAMPMin = 0xffff; /*drives to NOCPAMP */ ++ break; ++ } ++ ++ dprintk("%s: CPAMPMin=%d wd=%d\n", __func__, CPAMPMin, wd); ++ ++ CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); ++ while ((CPAMPvalue < CPAMPMin) && (wd > 0)) { ++ usleep_range(1000, 2000); ++ wd -= 1; ++ CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); ++ /*dprintk("CPAMPvalue= %d at wd=%d\n",CPAMPvalue,wd); */ ++ } ++ dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd); ++ if (CPAMPvalue < CPAMPMin) { ++ CPAMPStatus = FE_TER_NOCPAMP; ++ printk(KERN_ERR "CPAMP failed\n"); ++ } else { ++ printk(KERN_ERR "CPAMP OK !\n"); ++ CPAMPStatus = FE_TER_CPAMPOK; ++ } ++ ++ return CPAMPStatus; ++} ++ ++static enum stv0367_ter_signal_type ++stv0367ter_lock_algo(struct stv0367_state *state) ++{ ++ enum stv0367_ter_signal_type ret_flag; ++ short int wd, tempo; ++ u8 try, u_var1 = 0, u_var2 = 0, u_var3 = 0, u_var4 = 0, mode, guard; ++ u8 tmp, tmp2; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (state == NULL) ++ return FE_TER_SWNOK; ++ ++ try = 0; ++ do { ++ ret_flag = FE_TER_LOCKOK; ++ ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); ++ ++ if (state->config->if_iq_mode != 0) ++ stv0367_writebits(state, F367TER_COM_N, 0x07); ++ ++ stv0367_writebits(state, F367TER_GUARD, 3);/* suggest 2k 1/4 */ ++ stv0367_writebits(state, F367TER_MODE, 0); ++ stv0367_writebits(state, F367TER_SYR_TR_DIS, 0); ++ usleep_range(5000, 10000); ++ ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); ++ ++ ++ if (stv0367ter_check_syr(state) == FE_TER_NOSYMBOL) ++ return FE_TER_NOSYMBOL; ++ else { /* ++ if chip locked on wrong mode first try, ++ it must lock correctly second try */ ++ mode = stv0367_readbits(state, F367TER_SYR_MODE); ++ if (stv0367ter_check_cpamp(state, mode) == ++ FE_TER_NOCPAMP) { ++ if (try == 0) ++ ret_flag = FE_TER_NOCPAMP; ++ ++ } ++ } ++ ++ try++; ++ } while ((try < 10) && (ret_flag != FE_TER_LOCKOK)); ++ ++ tmp = stv0367_readreg(state, R367TER_SYR_STAT); ++ tmp2 = stv0367_readreg(state, R367TER_STATUS); ++ dprintk("state=%p\n", state); ++ dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n", ++ mode, tmp, tmp2); ++ ++ tmp = stv0367_readreg(state, R367TER_PRVIT); ++ tmp2 = stv0367_readreg(state, R367TER_I2CRPT); ++ dprintk("PRVIT=0x%x I2CRPT=0x%x\n", tmp, tmp2); ++ ++ tmp = stv0367_readreg(state, R367TER_GAIN_SRC1); ++ dprintk("GAIN_SRC1=0x%x\n", tmp); ++ ++ if ((mode != 0) && (mode != 1) && (mode != 2)) ++ return FE_TER_SWNOK; ++ ++ /*guard=stv0367_readbits(state,F367TER_SYR_GUARD); */ ++ ++ /*suppress EPQ auto for SYR_GARD 1/16 or 1/32 ++ and set channel predictor in automatic */ ++#if 0 ++ switch (guard) { ++ ++ case 0: ++ case 1: ++ stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); ++ stv0367_writereg(state, R367TER_CHC_CTL, 0x01); ++ break; ++ case 2: ++ case 3: ++ stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); ++ stv0367_writereg(state, R367TER_CHC_CTL, 0x11); ++ break; ++ ++ default: ++ return FE_TER_SWNOK; ++ } ++#endif ++ ++ /*reset fec an reedsolo FOR 367 only*/ ++ stv0367_writebits(state, F367TER_RST_SFEC, 1); ++ stv0367_writebits(state, F367TER_RST_REEDSOLO, 1); ++ usleep_range(1000, 2000); ++ stv0367_writebits(state, F367TER_RST_SFEC, 0); ++ stv0367_writebits(state, F367TER_RST_REEDSOLO, 0); ++ ++ u_var1 = stv0367_readbits(state, F367TER_LK); ++ u_var2 = stv0367_readbits(state, F367TER_PRF); ++ u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); ++ /* u_var4=stv0367_readbits(state,F367TER_TSFIFO_LINEOK); */ ++ ++ wd = stv0367ter_duration(mode, 125, 500, 250); ++ tempo = stv0367ter_duration(mode, 4, 16, 8); ++ ++ /*while ( ((!u_var1)||(!u_var2)||(!u_var3)||(!u_var4)) && (wd>=0)) */ ++ while (((!u_var1) || (!u_var2) || (!u_var3)) && (wd >= 0)) { ++ usleep_range(1000 * tempo, 1000 * (tempo + 1)); ++ wd -= tempo; ++ u_var1 = stv0367_readbits(state, F367TER_LK); ++ u_var2 = stv0367_readbits(state, F367TER_PRF); ++ u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); ++ /*u_var4=stv0367_readbits(state, F367TER_TSFIFO_LINEOK); */ ++ } ++ ++ if (!u_var1) ++ return FE_TER_NOLOCK; ++ ++ ++ if (!u_var2) ++ return FE_TER_NOPRFOUND; ++ ++ if (!u_var3) ++ return FE_TER_NOTPS; ++ ++ guard = stv0367_readbits(state, F367TER_SYR_GUARD); ++ stv0367_writereg(state, R367TER_CHC_CTL, 0x11); ++ switch (guard) { ++ case 0: ++ case 1: ++ stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); ++ /*stv0367_writereg(state,R367TER_CHC_CTL, 0x1);*/ ++ stv0367_writebits(state, F367TER_SYR_FILTER, 0); ++ break; ++ case 2: ++ case 3: ++ stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); ++ /*stv0367_writereg(state,R367TER_CHC_CTL, 0x11);*/ ++ stv0367_writebits(state, F367TER_SYR_FILTER, 1); ++ break; ++ ++ default: ++ return FE_TER_SWNOK; ++ } ++ ++ /* apply Sfec workaround if 8K 64QAM CR!=1/2*/ ++ if ((stv0367_readbits(state, F367TER_TPS_CONST) == 2) && ++ (mode == 1) && ++ (stv0367_readbits(state, F367TER_TPS_HPCODE) != 0)) { ++ stv0367_writereg(state, R367TER_SFDLYSETH, 0xc0); ++ stv0367_writereg(state, R367TER_SFDLYSETM, 0x60); ++ stv0367_writereg(state, R367TER_SFDLYSETL, 0x0); ++ } else ++ stv0367_writereg(state, R367TER_SFDLYSETH, 0x0); ++ ++ wd = stv0367ter_duration(mode, 125, 500, 250); ++ u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); ++ ++ while ((!u_var4) && (wd >= 0)) { ++ usleep_range(1000 * tempo, 1000 * (tempo + 1)); ++ wd -= tempo; ++ u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); ++ } ++ ++ if (!u_var4) ++ return FE_TER_NOLOCK; ++ ++ /* for 367 leave COM_N at 0x7 for IQ_mode*/ ++ /*if(ter_state->if_iq_mode!=FE_TER_NORMAL_IF_TUNER) { ++ tempo=0; ++ while ((stv0367_readbits(state,F367TER_COM_USEGAINTRK)!=1) && ++ (stv0367_readbits(state,F367TER_COM_AGCLOCK)!=1)&&(tempo<100)) { ++ ChipWaitOrAbort(state,1); ++ tempo+=1; ++ } ++ ++ stv0367_writebits(state,F367TER_COM_N,0x17); ++ } */ ++ ++ stv0367_writebits(state, F367TER_SYR_TR_DIS, 1); ++ ++ dprintk("FE_TER_LOCKOK !!!\n"); ++ ++ return FE_TER_LOCKOK; ++ ++} ++ ++static void stv0367ter_set_ts_mode(struct stv0367_state *state, ++ enum stv0367_ts_mode PathTS) ++{ ++ ++ dprintk("%s:\n", __func__); ++ ++ if (state == NULL) ++ return; ++ ++ stv0367_writebits(state, F367TER_TS_DIS, 0); ++ switch (PathTS) { ++ default: ++ /*for removing warning :default we can assume in parallel mode*/ ++ case STV0367_PARALLEL_PUNCT_CLOCK: ++ stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 0); ++ stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 0); ++ break; ++ case STV0367_SERIAL_PUNCT_CLOCK: ++ stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 1); ++ stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 1); ++ break; ++ } ++} ++ ++static void stv0367ter_set_clk_pol(struct stv0367_state *state, ++ enum stv0367_clk_pol clock) ++{ ++ ++ dprintk("%s:\n", __func__); ++ ++ if (state == NULL) ++ return; ++ ++ switch (clock) { ++ case STV0367_RISINGEDGE_CLOCK: ++ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 1); ++ break; ++ case STV0367_FALLINGEDGE_CLOCK: ++ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); ++ break; ++ /*case FE_TER_CLOCK_POLARITY_DEFAULT:*/ ++ default: ++ stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); ++ break; ++ } ++} ++ ++#if 0 ++static void stv0367ter_core_sw(struct stv0367_state *state) ++{ ++ ++ dprintk("%s:\n", __func__); ++ ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); ++ msleep(350); ++} ++#endif ++static int stv0367ter_standby(struct dvb_frontend *fe, u8 standby_on) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (standby_on) { ++ stv0367_writebits(state, F367TER_STDBY, 1); ++ stv0367_writebits(state, F367TER_STDBY_FEC, 1); ++ stv0367_writebits(state, F367TER_STDBY_CORE, 1); ++ } else { ++ stv0367_writebits(state, F367TER_STDBY, 0); ++ stv0367_writebits(state, F367TER_STDBY_FEC, 0); ++ stv0367_writebits(state, F367TER_STDBY_CORE, 0); ++ } ++ ++ return 0; ++} ++ ++static int stv0367ter_sleep(struct dvb_frontend *fe) ++{ ++ return stv0367ter_standby(fe, 1); ++} ++ ++static int stv0367ter_init(struct dvb_frontend *fe) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ int i; ++ ++ dprintk("%s:\n", __func__); ++ ++ ter_state->pBER = 0; ++ ++ for (i = 0; i < STV0367TER_NBREGS; i++) ++ stv0367_writereg(state, def0367ter[i].addr, ++ def0367ter[i].value); ++ ++ switch (state->config->xtal) { ++ /*set internal freq to 53.125MHz */ ++ case 25000000: ++ stv0367_writereg(state, R367TER_PLLMDIV, 0xa); ++ stv0367_writereg(state, R367TER_PLLNDIV, 0x55); ++ stv0367_writereg(state, R367TER_PLLSETUP, 0x18); ++ break; ++ default: ++ case 27000000: ++ dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n"); ++ stv0367_writereg(state, R367TER_PLLMDIV, 0x1); ++ stv0367_writereg(state, R367TER_PLLNDIV, 0x8); ++ stv0367_writereg(state, R367TER_PLLSETUP, 0x18); ++ break; ++ case 30000000: ++ stv0367_writereg(state, R367TER_PLLMDIV, 0xc); ++ stv0367_writereg(state, R367TER_PLLNDIV, 0x55); ++ stv0367_writereg(state, R367TER_PLLSETUP, 0x18); ++ break; ++ } ++ ++ stv0367_writereg(state, R367TER_I2CRPT, 0xa0); ++ stv0367_writereg(state, R367TER_ANACTRL, 0x00); ++ ++ /*Set TS1 and TS2 to serial or parallel mode */ ++ stv0367ter_set_ts_mode(state, state->config->ts_mode); ++ stv0367ter_set_clk_pol(state, state->config->clk_pol); ++ ++ state->chip_id = stv0367_readreg(state, R367TER_ID); ++ ter_state->first_lock = 0; ++ ter_state->unlock_counter = 2; ++ ++ return 0; ++} ++ ++static int stv0367ter_algo(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ int offset = 0, tempo = 0; ++ u8 u_var; ++ u8 /*constell,*/ counter; ++ s8 step; ++ s32 timing_offset = 0; ++ u32 trl_nomrate = 0, InternalFreq = 0, temp = 0; ++ ++ dprintk("%s:\n", __func__); ++ ++ ter_state->frequency = p->frequency; ++ ter_state->force = FE_TER_FORCENONE ++ + stv0367_readbits(state, F367TER_FORCE) * 2; ++ ter_state->if_iq_mode = state->config->if_iq_mode; ++ switch (state->config->if_iq_mode) { ++ case FE_TER_NORMAL_IF_TUNER: /* Normal IF mode */ ++ dprintk("ALGO: FE_TER_NORMAL_IF_TUNER selected\n"); ++ stv0367_writebits(state, F367TER_TUNER_BB, 0); ++ stv0367_writebits(state, F367TER_LONGPATH_IF, 0); ++ stv0367_writebits(state, F367TER_DEMUX_SWAP, 0); ++ break; ++ case FE_TER_LONGPATH_IF_TUNER: /* Long IF mode */ ++ dprintk("ALGO: FE_TER_LONGPATH_IF_TUNER selected\n"); ++ stv0367_writebits(state, F367TER_TUNER_BB, 0); ++ stv0367_writebits(state, F367TER_LONGPATH_IF, 1); ++ stv0367_writebits(state, F367TER_DEMUX_SWAP, 1); ++ break; ++ case FE_TER_IQ_TUNER: /* IQ mode */ ++ dprintk("ALGO: FE_TER_IQ_TUNER selected\n"); ++ stv0367_writebits(state, F367TER_TUNER_BB, 1); ++ stv0367_writebits(state, F367TER_PPM_INVSEL, 0); ++ break; ++ default: ++ printk(KERN_ERR "ALGO: wrong TUNER type selected\n"); ++ return -EINVAL; ++ } ++ ++ usleep_range(5000, 7000); ++ ++ switch (p->inversion) { ++ case INVERSION_AUTO: ++ default: ++ dprintk("%s: inversion AUTO\n", __func__); ++ if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) ++ stv0367_writebits(state, F367TER_IQ_INVERT, ++ ter_state->sense); ++ else ++ stv0367_writebits(state, F367TER_INV_SPECTR, ++ ter_state->sense); ++ ++ break; ++ case INVERSION_ON: ++ case INVERSION_OFF: ++ if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) ++ stv0367_writebits(state, F367TER_IQ_INVERT, ++ p->inversion); ++ else ++ stv0367_writebits(state, F367TER_INV_SPECTR, ++ p->inversion); ++ ++ break; ++ } ++ ++ if ((ter_state->if_iq_mode != FE_TER_NORMAL_IF_TUNER) && ++ (ter_state->pBW != ter_state->bw)) { ++ stv0367ter_agc_iir_lock_detect_set(state); ++ ++ /*set fine agc target to 180 for LPIF or IQ mode*/ ++ /* set Q_AGCTarget */ ++ stv0367_writebits(state, F367TER_SEL_IQNTAR, 1); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); ++ /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ ++ ++ /* set Q_AGCTarget */ ++ stv0367_writebits(state, F367TER_SEL_IQNTAR, 0); ++ stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); ++ /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ ++ ++ if (!stv0367_iir_filt_init(state, ter_state->bw, ++ state->config->xtal)) ++ return -EINVAL; ++ /*set IIR filter once for 6,7 or 8MHz BW*/ ++ ter_state->pBW = ter_state->bw; ++ ++ stv0367ter_agc_iir_rst(state); ++ } ++ ++ if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) ++ stv0367_writebits(state, F367TER_BDI_LPSEL, 0x01); ++ else ++ stv0367_writebits(state, F367TER_BDI_LPSEL, 0x00); ++ ++ InternalFreq = stv0367ter_get_mclk(state, state->config->xtal) / 1000; ++ temp = (int) ++ ((((ter_state->bw * 64 * (1 << 15) * 100) ++ / (InternalFreq)) * 10) / 7); ++ ++ stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, temp % 2); ++ temp = temp / 2; ++ stv0367_writebits(state, F367TER_TRL_NOMRATE_HI, temp / 256); ++ stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, temp % 256); ++ ++ temp = stv0367_readbits(state, F367TER_TRL_NOMRATE_HI) * 512 + ++ stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + ++ stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB); ++ temp = (int)(((1 << 17) * ter_state->bw * 1000) / (7 * (InternalFreq))); ++ stv0367_writebits(state, F367TER_GAIN_SRC_HI, temp / 256); ++ stv0367_writebits(state, F367TER_GAIN_SRC_LO, temp % 256); ++ temp = stv0367_readbits(state, F367TER_GAIN_SRC_HI) * 256 + ++ stv0367_readbits(state, F367TER_GAIN_SRC_LO); ++ ++ temp = (int) ++ ((InternalFreq - state->config->if_khz) * (1 << 16) ++ / (InternalFreq)); ++ ++ dprintk("DEROT temp=0x%x\n", temp); ++ stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256); ++ stv0367_writebits(state, F367TER_INC_DEROT_LO, temp % 256); ++ ++ ter_state->echo_pos = 0; ++ ter_state->ucblocks = 0; /* liplianin */ ++ ter_state->pBER = 0; /* liplianin */ ++ stv0367_writebits(state, F367TER_LONG_ECHO, ter_state->echo_pos); ++ ++ if (stv0367ter_lock_algo(state) != FE_TER_LOCKOK) ++ return 0; ++ ++ ter_state->state = FE_TER_LOCKOK; ++ ++ ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE); ++ ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD); ++ ++ ter_state->first_lock = 1; /* we know sense now :) */ ++ ++ ter_state->agc_val = ++ (stv0367_readbits(state, F367TER_AGC1_VAL_LO) << 16) + ++ (stv0367_readbits(state, F367TER_AGC1_VAL_HI) << 24) + ++ stv0367_readbits(state, F367TER_AGC2_VAL_LO) + ++ (stv0367_readbits(state, F367TER_AGC2_VAL_HI) << 8); ++ ++ /* Carrier offset calculation */ ++ stv0367_writebits(state, F367TER_FREEZE, 1); ++ offset = (stv0367_readbits(state, F367TER_CRL_FOFFSET_VHI) << 16) ; ++ offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_HI) << 8); ++ offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_LO)); ++ stv0367_writebits(state, F367TER_FREEZE, 0); ++ if (offset > 8388607) ++ offset -= 16777216; ++ ++ offset = offset * 2 / 16384; ++ ++ if (ter_state->mode == FE_TER_MODE_2K) ++ offset = (offset * 4464) / 1000;/*** 1 FFT BIN=4.464khz***/ ++ else if (ter_state->mode == FE_TER_MODE_4K) ++ offset = (offset * 223) / 100;/*** 1 FFT BIN=2.23khz***/ ++ else if (ter_state->mode == FE_TER_MODE_8K) ++ offset = (offset * 111) / 100;/*** 1 FFT BIN=1.1khz***/ ++ ++ if (stv0367_readbits(state, F367TER_PPM_INVSEL) == 1) { ++ if ((stv0367_readbits(state, F367TER_INV_SPECTR) == ++ (stv0367_readbits(state, ++ F367TER_STATUS_INV_SPECRUM) == 1))) ++ offset = offset * -1; ++ } ++ ++ if (ter_state->bw == 6) ++ offset = (offset * 6) / 8; ++ else if (ter_state->bw == 7) ++ offset = (offset * 7) / 8; ++ ++ ter_state->frequency += offset; ++ ++ tempo = 10; /* exit even if timing_offset stays null */ ++ while ((timing_offset == 0) && (tempo > 0)) { ++ usleep_range(10000, 20000); /*was 20ms */ ++ /* fine tuning of timing offset if required */ ++ timing_offset = stv0367_readbits(state, F367TER_TRL_TOFFSET_LO) ++ + 256 * stv0367_readbits(state, ++ F367TER_TRL_TOFFSET_HI); ++ if (timing_offset >= 32768) ++ timing_offset -= 65536; ++ trl_nomrate = (512 * stv0367_readbits(state, ++ F367TER_TRL_NOMRATE_HI) ++ + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 ++ + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB)); ++ ++ timing_offset = ((signed)(1000000 / trl_nomrate) * ++ timing_offset) / 2048; ++ tempo--; ++ } ++ ++ if (timing_offset <= 0) { ++ timing_offset = (timing_offset - 11) / 22; ++ step = -1; ++ } else { ++ timing_offset = (timing_offset + 11) / 22; ++ step = 1; ++ } ++ ++ for (counter = 0; counter < abs(timing_offset); counter++) { ++ trl_nomrate += step; ++ stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, ++ trl_nomrate % 2); ++ stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, ++ trl_nomrate / 2); ++ usleep_range(1000, 2000); ++ } ++ ++ usleep_range(5000, 6000); ++ /* unlocks could happen in case of trl centring big step, ++ then a core off/on restarts demod */ ++ u_var = stv0367_readbits(state, F367TER_LK); ++ ++ if (!u_var) { ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); ++ msleep(20); ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); ++ } ++ ++ return 0; ++} ++ ++static int stv0367ter_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ ++ /*u8 trials[2]; */ ++ s8 num_trials, index; ++ u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF }; ++ ++ stv0367ter_init(fe); ++ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ switch (p->transmission_mode) { ++ default: ++ case TRANSMISSION_MODE_AUTO: ++ case TRANSMISSION_MODE_2K: ++ ter_state->mode = FE_TER_MODE_2K; ++ break; ++/* case TRANSMISSION_MODE_4K: ++ pLook.mode = FE_TER_MODE_4K; ++ break;*/ ++ case TRANSMISSION_MODE_8K: ++ ter_state->mode = FE_TER_MODE_8K; ++ break; ++ } ++ ++ switch (p->guard_interval) { ++ default: ++ case GUARD_INTERVAL_1_32: ++ case GUARD_INTERVAL_1_16: ++ case GUARD_INTERVAL_1_8: ++ case GUARD_INTERVAL_1_4: ++ ter_state->guard = p->guard_interval; ++ break; ++ case GUARD_INTERVAL_AUTO: ++ ter_state->guard = GUARD_INTERVAL_1_32; ++ break; ++ } ++ ++ switch (p->bandwidth_hz) { ++ case 6000000: ++ ter_state->bw = FE_TER_CHAN_BW_6M; ++ break; ++ case 7000000: ++ ter_state->bw = FE_TER_CHAN_BW_7M; ++ break; ++ case 8000000: ++ default: ++ ter_state->bw = FE_TER_CHAN_BW_8M; ++ } ++ ++ ter_state->hierarchy = FE_TER_HIER_NONE; ++ ++ switch (p->inversion) { ++ case INVERSION_OFF: ++ case INVERSION_ON: ++ num_trials = 1; ++ break; ++ default: ++ num_trials = 2; ++ if (ter_state->first_lock) ++ num_trials = 1; ++ break; ++ } ++ ++ ter_state->state = FE_TER_NOLOCK; ++ index = 0; ++ ++ while (((index) < num_trials) && (ter_state->state != FE_TER_LOCKOK)) { ++ if (!ter_state->first_lock) { ++ if (p->inversion == INVERSION_AUTO) ++ ter_state->sense = SenseTrials[index]; ++ ++ } ++ stv0367ter_algo(fe); ++ ++ if ((ter_state->state == FE_TER_LOCKOK) && ++ (p->inversion == INVERSION_AUTO) && ++ (index == 1)) { ++ /* invert spectrum sense */ ++ SenseTrials[index] = SenseTrials[0]; ++ SenseTrials[(index + 1) % 2] = (SenseTrials[1] + 1) % 2; ++ } ++ ++ index++; ++ } ++ ++ return 0; ++} ++ ++static int stv0367ter_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ u32 errs = 0; ++ ++ /*wait for counting completion*/ ++ if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) { ++ errs = ++ ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) ++ * (1 << 16)) ++ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) ++ * (1 << 8)) ++ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); ++ ter_state->ucblocks = errs; ++ } ++ ++ (*ucblocks) = ter_state->ucblocks; ++ ++ return 0; ++} ++ ++static int stv0367ter_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ ++ int error = 0; ++ enum stv0367_ter_mode mode; ++ int constell = 0,/* snr = 0,*/ Data = 0; ++ ++ p->frequency = stv0367_get_tuner_freq(fe); ++ if ((int)p->frequency < 0) ++ p->frequency = -p->frequency; ++ ++ constell = stv0367_readbits(state, F367TER_TPS_CONST); ++ if (constell == 0) ++ p->modulation = QPSK; ++ else if (constell == 1) ++ p->modulation = QAM_16; ++ else ++ p->modulation = QAM_64; ++ ++ p->inversion = stv0367_readbits(state, F367TER_INV_SPECTR); ++ ++ /* Get the Hierarchical mode */ ++ Data = stv0367_readbits(state, F367TER_TPS_HIERMODE); ++ ++ switch (Data) { ++ case 0: ++ p->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ p->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ p->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ p->hierarchy = HIERARCHY_4; ++ break; ++ default: ++ p->hierarchy = HIERARCHY_AUTO; ++ break; /* error */ ++ } ++ ++ /* Get the FEC Rate */ ++ if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) ++ Data = stv0367_readbits(state, F367TER_TPS_LPCODE); ++ else ++ Data = stv0367_readbits(state, F367TER_TPS_HPCODE); ++ ++ switch (Data) { ++ case 0: ++ p->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_HP = FEC_7_8; ++ break; ++ default: ++ p->code_rate_HP = FEC_AUTO; ++ break; /* error */ ++ } ++ ++ mode = stv0367_readbits(state, F367TER_SYR_MODE); ++ ++ switch (mode) { ++ case FE_TER_MODE_2K: ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++/* case FE_TER_MODE_4K: ++ p->transmission_mode = TRANSMISSION_MODE_4K; ++ break;*/ ++ case FE_TER_MODE_8K: ++ p->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ default: ++ p->transmission_mode = TRANSMISSION_MODE_AUTO; ++ } ++ ++ p->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD); ++ ++ return error; ++} ++ ++static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ u32 snru32 = 0; ++ int cpt = 0; ++ u8 cut = stv0367_readbits(state, F367TER_IDENTIFICATIONREG); ++ ++ while (cpt < 10) { ++ usleep_range(2000, 3000); ++ if (cut == 0x50) /*cut 1.0 cut 1.1*/ ++ snru32 += stv0367_readbits(state, F367TER_CHCSNR) / 4; ++ else /*cu2.0*/ ++ snru32 += 125 * stv0367_readbits(state, F367TER_CHCSNR); ++ ++ cpt++; ++ } ++ ++ snru32 /= 10;/*average on 10 values*/ ++ ++ *snr = snru32 / 1000; ++ ++ return 0; ++} ++ ++#if 0 ++static int stv0367ter_status(struct dvb_frontend *fe) ++{ ++ ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ int locked = FALSE; ++ ++ locked = (stv0367_readbits(state, F367TER_LK)); ++ if (!locked) ++ ter_state->unlock_counter += 1; ++ else ++ ter_state->unlock_counter = 0; ++ ++ if (ter_state->unlock_counter > 2) { ++ if (!stv0367_readbits(state, F367TER_TPS_LOCK) || ++ (!stv0367_readbits(state, F367TER_LK))) { ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); ++ usleep_range(2000, 3000); ++ stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); ++ msleep(350); ++ locked = (stv0367_readbits(state, F367TER_TPS_LOCK)) && ++ (stv0367_readbits(state, F367TER_LK)); ++ } ++ ++ } ++ ++ return locked; ++} ++#endif ++static int stv0367ter_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ dprintk("%s:\n", __func__); ++ ++ *status = 0; ++ ++ if (stv0367_readbits(state, F367TER_LK)) { ++ *status |= FE_HAS_LOCK; ++ dprintk("%s: stv0367 has locked\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static int stv0367ter_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367ter_state *ter_state = state->ter_state; ++ u32 Errors = 0, tber = 0, temporary = 0; ++ int abc = 0, def = 0; ++ ++ ++ /*wait for counting completion*/ ++ if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) ++ Errors = ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT) ++ * (1 << 16)) ++ + ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT_HI) ++ * (1 << 8)) ++ + ((u32)stv0367_readbits(state, ++ F367TER_SFEC_ERR_CNT_LO)); ++ /*measurement not completed, load previous value*/ ++ else { ++ tber = ter_state->pBER; ++ return 0; ++ } ++ ++ abc = stv0367_readbits(state, F367TER_SFEC_ERR_SOURCE); ++ def = stv0367_readbits(state, F367TER_SFEC_NUM_EVENT); ++ ++ if (Errors == 0) { ++ tber = 0; ++ } else if (abc == 0x7) { ++ if (Errors <= 4) { ++ temporary = (Errors * 1000000000) / (8 * (1 << 14)); ++ temporary = temporary; ++ } else if (Errors <= 42) { ++ temporary = (Errors * 100000000) / (8 * (1 << 14)); ++ temporary = temporary * 10; ++ } else if (Errors <= 429) { ++ temporary = (Errors * 10000000) / (8 * (1 << 14)); ++ temporary = temporary * 100; ++ } else if (Errors <= 4294) { ++ temporary = (Errors * 1000000) / (8 * (1 << 14)); ++ temporary = temporary * 1000; ++ } else if (Errors <= 42949) { ++ temporary = (Errors * 100000) / (8 * (1 << 14)); ++ temporary = temporary * 10000; ++ } else if (Errors <= 429496) { ++ temporary = (Errors * 10000) / (8 * (1 << 14)); ++ temporary = temporary * 100000; ++ } else { /*if (Errors<4294967) 2^22 max error*/ ++ temporary = (Errors * 1000) / (8 * (1 << 14)); ++ temporary = temporary * 100000; /* still to *10 */ ++ } ++ ++ /* Byte error*/ ++ if (def == 2) ++ /*tber=Errors/(8*(1 <<14));*/ ++ tber = temporary; ++ else if (def == 3) ++ /*tber=Errors/(8*(1 <<16));*/ ++ tber = temporary / 4; ++ else if (def == 4) ++ /*tber=Errors/(8*(1 <<18));*/ ++ tber = temporary / 16; ++ else if (def == 5) ++ /*tber=Errors/(8*(1 <<20));*/ ++ tber = temporary / 64; ++ else if (def == 6) ++ /*tber=Errors/(8*(1 <<22));*/ ++ tber = temporary / 256; ++ else ++ /* should not pass here*/ ++ tber = 0; ++ ++ if ((Errors < 4294967) && (Errors > 429496)) ++ tber *= 10; ++ ++ } ++ ++ /* save actual value */ ++ ter_state->pBER = tber; ++ ++ (*ber) = tber; ++ ++ return 0; ++} ++#if 0 ++static u32 stv0367ter_get_per(struct stv0367_state *state) ++{ ++ struct stv0367ter_state *ter_state = state->ter_state; ++ u32 Errors = 0, Per = 0, temporary = 0; ++ int abc = 0, def = 0, cpt = 0; ++ ++ while (((stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 1) && ++ (cpt < 400)) || ((Errors == 0) && (cpt < 400))) { ++ usleep_range(1000, 2000); ++ Errors = ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) ++ * (1 << 16)) ++ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) ++ * (1 << 8)) ++ + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); ++ cpt++; ++ } ++ abc = stv0367_readbits(state, F367TER_ERR_SRC1); ++ def = stv0367_readbits(state, F367TER_NUM_EVT1); ++ ++ if (Errors == 0) ++ Per = 0; ++ else if (abc == 0x9) { ++ if (Errors <= 4) { ++ temporary = (Errors * 1000000000) / (8 * (1 << 8)); ++ temporary = temporary; ++ } else if (Errors <= 42) { ++ temporary = (Errors * 100000000) / (8 * (1 << 8)); ++ temporary = temporary * 10; ++ } else if (Errors <= 429) { ++ temporary = (Errors * 10000000) / (8 * (1 << 8)); ++ temporary = temporary * 100; ++ } else if (Errors <= 4294) { ++ temporary = (Errors * 1000000) / (8 * (1 << 8)); ++ temporary = temporary * 1000; ++ } else if (Errors <= 42949) { ++ temporary = (Errors * 100000) / (8 * (1 << 8)); ++ temporary = temporary * 10000; ++ } else { /*if(Errors<=429496) 2^16 errors max*/ ++ temporary = (Errors * 10000) / (8 * (1 << 8)); ++ temporary = temporary * 100000; ++ } ++ ++ /* pkt error*/ ++ if (def == 2) ++ /*Per=Errors/(1 << 8);*/ ++ Per = temporary; ++ else if (def == 3) ++ /*Per=Errors/(1 << 10);*/ ++ Per = temporary / 4; ++ else if (def == 4) ++ /*Per=Errors/(1 << 12);*/ ++ Per = temporary / 16; ++ else if (def == 5) ++ /*Per=Errors/(1 << 14);*/ ++ Per = temporary / 64; ++ else if (def == 6) ++ /*Per=Errors/(1 << 16);*/ ++ Per = temporary / 256; ++ else ++ Per = 0; ++ ++ } ++ /* save actual value */ ++ ter_state->pPER = Per; ++ ++ return Per; ++} ++#endif ++static int stv0367_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings ++ *fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 1000; ++ fe_tune_settings->step_size = 0; ++ fe_tune_settings->max_drift = 0; ++ ++ return 0; ++} ++ ++static void stv0367_release(struct dvb_frontend *fe) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ kfree(state->ter_state); ++ kfree(state->cab_state); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops stv0367ter_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "ST STV0367 DVB-T", ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 15625, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | ++ FE_CAN_INVERSION_AUTO | ++ FE_CAN_MUTE_TS ++ }, ++ .release = stv0367_release, ++ .init = stv0367ter_init, ++ .sleep = stv0367ter_sleep, ++ .i2c_gate_ctrl = stv0367ter_gate_ctrl, ++ .set_frontend = stv0367ter_set_frontend, ++ .get_frontend = stv0367ter_get_frontend, ++ .get_tune_settings = stv0367_get_tune_settings, ++ .read_status = stv0367ter_read_status, ++ .read_ber = stv0367ter_read_ber,/* too slow */ ++/* .read_signal_strength = stv0367_read_signal_strength,*/ ++ .read_snr = stv0367ter_read_snr, ++ .read_ucblocks = stv0367ter_read_ucblocks, ++}; ++ ++struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv0367_state *state = NULL; ++ struct stv0367ter_state *ter_state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL); ++ if (ter_state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ state->config = config; ++ state->ter_state = ter_state; ++ state->fe.ops = stv0367ter_ops; ++ state->fe.demodulator_priv = state; ++ state->chip_id = stv0367_readreg(state, 0xf000); ++ ++ dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); ++ ++ /* check if the demod is there */ ++ if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) ++ goto error; ++ ++ return &state->fe; ++ ++error: ++ kfree(ter_state); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stv0367ter_attach); ++ ++static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ dprintk("%s:\n", __func__); ++ ++ stv0367_writebits(state, F367CAB_I2CT_ON, (enable > 0) ? 1 : 0); ++ ++ return 0; ++} ++ ++static u32 stv0367cab_get_mclk(struct dvb_frontend *fe, u32 ExtClk_Hz) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ u32 mclk_Hz = 0;/* master clock frequency (Hz) */ ++ u32 M, N, P; ++ ++ ++ if (stv0367_readbits(state, F367CAB_BYPASS_PLLXN) == 0) { ++ N = (u32)stv0367_readbits(state, F367CAB_PLL_NDIV); ++ if (N == 0) ++ N = N + 1; ++ ++ M = (u32)stv0367_readbits(state, F367CAB_PLL_MDIV); ++ if (M == 0) ++ M = M + 1; ++ ++ P = (u32)stv0367_readbits(state, F367CAB_PLL_PDIV); ++ ++ if (P > 5) ++ P = 5; ++ ++ mclk_Hz = ((ExtClk_Hz / 2) * N) / (M * (1 << P)); ++ dprintk("stv0367cab_get_mclk BYPASS_PLLXN mclk_Hz=%d\n", ++ mclk_Hz); ++ } else ++ mclk_Hz = ExtClk_Hz; ++ ++ dprintk("stv0367cab_get_mclk final mclk_Hz=%d\n", mclk_Hz); ++ ++ return mclk_Hz; ++} ++ ++static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz) ++{ ++ u32 ADCClk_Hz = ExtClk_Hz; ++ ++ ADCClk_Hz = stv0367cab_get_mclk(fe, ExtClk_Hz); ++ ++ return ADCClk_Hz; ++} ++ ++static enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state, ++ u32 SymbolRate, ++ enum stv0367cab_mod QAMSize) ++{ ++ /* Set QAM size */ ++ stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize); ++ ++ /* Set Registers settings specific to the QAM size */ ++ switch (QAMSize) { ++ case FE_CAB_MOD_QAM4: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ break; ++ case FE_CAB_MOD_QAM16: ++ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x64); ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); ++ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x8a); ++ break; ++ case FE_CAB_MOD_QAM32: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x6e); ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xb7); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x9d); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); ++ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); ++ break; ++ case FE_CAB_MOD_QAM64: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82); ++ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); ++ if (SymbolRate > 45000000) { ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5); ++ } else if (SymbolRate > 25000000) { ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); ++ } else { ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); ++ } ++ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); ++ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x99); ++ break; ++ case FE_CAB_MOD_QAM128: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76); ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1); ++ if (SymbolRate > 45000000) ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); ++ else if (SymbolRate > 25000000) ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); ++ else ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97); ++ ++ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x8e); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); ++ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); ++ break; ++ case FE_CAB_MOD_QAM256: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94); ++ stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); ++ stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); ++ if (SymbolRate > 45000000) ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ else if (SymbolRate > 25000000) ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); ++ else ++ stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); ++ ++ stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x85); ++ stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); ++ stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); ++ break; ++ case FE_CAB_MOD_QAM512: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ break; ++ case FE_CAB_MOD_QAM1024: ++ stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); ++ break; ++ default: ++ break; ++ } ++ ++ return QAMSize; ++} ++ ++static u32 stv0367cab_set_derot_freq(struct stv0367_state *state, ++ u32 adc_hz, s32 derot_hz) ++{ ++ u32 sampled_if = 0; ++ u32 adc_khz; ++ ++ adc_khz = adc_hz / 1000; ++ ++ dprintk("%s: adc_hz=%d derot_hz=%d\n", __func__, adc_hz, derot_hz); ++ ++ if (adc_khz != 0) { ++ if (derot_hz < 1000000) ++ derot_hz = adc_hz / 4; /* ZIF operation */ ++ if (derot_hz > adc_hz) ++ derot_hz = derot_hz - adc_hz; ++ sampled_if = (u32)derot_hz / 1000; ++ sampled_if *= 32768; ++ sampled_if /= adc_khz; ++ sampled_if *= 256; ++ } ++ ++ if (sampled_if > 8388607) ++ sampled_if = 8388607; ++ ++ dprintk("%s: sampled_if=0x%x\n", __func__, sampled_if); ++ ++ stv0367_writereg(state, R367CAB_MIX_NCO_LL, sampled_if); ++ stv0367_writereg(state, R367CAB_MIX_NCO_HL, (sampled_if >> 8)); ++ stv0367_writebits(state, F367CAB_MIX_NCO_INC_HH, (sampled_if >> 16)); ++ ++ return derot_hz; ++} ++ ++static u32 stv0367cab_get_derot_freq(struct stv0367_state *state, u32 adc_hz) ++{ ++ u32 sampled_if; ++ ++ sampled_if = stv0367_readbits(state, F367CAB_MIX_NCO_INC_LL) + ++ (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HL) << 8) + ++ (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HH) << 16); ++ ++ sampled_if /= 256; ++ sampled_if *= (adc_hz / 1000); ++ sampled_if += 1; ++ sampled_if /= 32768; ++ ++ return sampled_if; ++} ++ ++static u32 stv0367cab_set_srate(struct stv0367_state *state, u32 adc_hz, ++ u32 mclk_hz, u32 SymbolRate, ++ enum stv0367cab_mod QAMSize) ++{ ++ u32 QamSizeCorr = 0; ++ u32 u32_tmp = 0, u32_tmp1 = 0; ++ u32 adp_khz; ++ ++ dprintk("%s:\n", __func__); ++ ++ /* Set Correction factor of SRC gain */ ++ switch (QAMSize) { ++ case FE_CAB_MOD_QAM4: ++ QamSizeCorr = 1110; ++ break; ++ case FE_CAB_MOD_QAM16: ++ QamSizeCorr = 1032; ++ break; ++ case FE_CAB_MOD_QAM32: ++ QamSizeCorr = 954; ++ break; ++ case FE_CAB_MOD_QAM64: ++ QamSizeCorr = 983; ++ break; ++ case FE_CAB_MOD_QAM128: ++ QamSizeCorr = 957; ++ break; ++ case FE_CAB_MOD_QAM256: ++ QamSizeCorr = 948; ++ break; ++ case FE_CAB_MOD_QAM512: ++ QamSizeCorr = 0; ++ break; ++ case FE_CAB_MOD_QAM1024: ++ QamSizeCorr = 944; ++ break; ++ default: ++ break; ++ } ++ ++ /* Transfer ratio calculation */ ++ if (adc_hz != 0) { ++ u32_tmp = 256 * SymbolRate; ++ u32_tmp = u32_tmp / adc_hz; ++ } ++ stv0367_writereg(state, R367CAB_EQU_CRL_TFR, (u8)u32_tmp); ++ ++ /* Symbol rate and SRC gain calculation */ ++ adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ ++ if (adp_khz != 0) { ++ u32_tmp = SymbolRate; ++ u32_tmp1 = SymbolRate; ++ ++ if (u32_tmp < 2097152) { /* 2097152 = 2^21 */ ++ /* Symbol rate calculation */ ++ u32_tmp *= 2048; /* 2048 = 2^11 */ ++ u32_tmp = u32_tmp / adp_khz; ++ u32_tmp = u32_tmp * 16384; /* 16384 = 2^14 */ ++ u32_tmp /= 125 ; /* 125 = 1000/2^3 */ ++ u32_tmp = u32_tmp * 8; /* 8 = 2^3 */ ++ ++ /* SRC Gain Calculation */ ++ u32_tmp1 *= 2048; /* *2*2^10 */ ++ u32_tmp1 /= 439; /* *2/878 */ ++ u32_tmp1 *= 256; /* *2^8 */ ++ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ ++ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ ++ u32_tmp1 = u32_tmp1 / 10000000; ++ ++ } else if (u32_tmp < 4194304) { /* 4194304 = 2**22 */ ++ /* Symbol rate calculation */ ++ u32_tmp *= 1024 ; /* 1024 = 2**10 */ ++ u32_tmp = u32_tmp / adp_khz; ++ u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ ++ u32_tmp /= 125 ; /* 125 = 1000/2**3 */ ++ u32_tmp = u32_tmp * 16; /* 16 = 2**4 */ ++ ++ /* SRC Gain Calculation */ ++ u32_tmp1 *= 1024; /* *2*2^9 */ ++ u32_tmp1 /= 439; /* *2/878 */ ++ u32_tmp1 *= 256; /* *2^8 */ ++ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz)*/ ++ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ ++ u32_tmp1 = u32_tmp1 / 5000000; ++ } else if (u32_tmp < 8388607) { /* 8388607 = 2**23 */ ++ /* Symbol rate calculation */ ++ u32_tmp *= 512 ; /* 512 = 2**9 */ ++ u32_tmp = u32_tmp / adp_khz; ++ u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ ++ u32_tmp /= 125 ; /* 125 = 1000/2**3 */ ++ u32_tmp = u32_tmp * 32; /* 32 = 2**5 */ ++ ++ /* SRC Gain Calculation */ ++ u32_tmp1 *= 512; /* *2*2^8 */ ++ u32_tmp1 /= 439; /* *2/878 */ ++ u32_tmp1 *= 256; /* *2^8 */ ++ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ ++ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ ++ u32_tmp1 = u32_tmp1 / 2500000; ++ } else { ++ /* Symbol rate calculation */ ++ u32_tmp *= 256 ; /* 256 = 2**8 */ ++ u32_tmp = u32_tmp / adp_khz; ++ u32_tmp = u32_tmp * 16384; /* 16384 = 2**13 */ ++ u32_tmp /= 125 ; /* 125 = 1000/2**3 */ ++ u32_tmp = u32_tmp * 64; /* 64 = 2**6 */ ++ ++ /* SRC Gain Calculation */ ++ u32_tmp1 *= 256; /* 2*2^7 */ ++ u32_tmp1 /= 439; /* *2/878 */ ++ u32_tmp1 *= 256; /* *2^8 */ ++ u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ ++ u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ ++ u32_tmp1 = u32_tmp1 / 1250000; ++ } ++ } ++#if 0 ++ /* Filters' coefficients are calculated and written ++ into registers only if the filters are enabled */ ++ if (stv0367_readbits(state, F367CAB_ADJ_EN)) { ++ stv0367cab_SetIirAdjacentcoefficient(state, mclk_hz, ++ SymbolRate); ++ /* AllPass filter must be enabled ++ when the adjacents filter is used */ ++ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 1); ++ stv0367cab_SetAllPasscoefficient(state, mclk_hz, SymbolRate); ++ } else ++ /* AllPass filter must be disabled ++ when the adjacents filter is not used */ ++#endif ++ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); ++ ++ stv0367_writereg(state, R367CAB_SRC_NCO_LL, u32_tmp); ++ stv0367_writereg(state, R367CAB_SRC_NCO_LH, (u32_tmp >> 8)); ++ stv0367_writereg(state, R367CAB_SRC_NCO_HL, (u32_tmp >> 16)); ++ stv0367_writereg(state, R367CAB_SRC_NCO_HH, (u32_tmp >> 24)); ++ ++ stv0367_writereg(state, R367CAB_IQDEM_GAIN_SRC_L, u32_tmp1 & 0x00ff); ++ stv0367_writebits(state, F367CAB_GAIN_SRC_HI, (u32_tmp1 >> 8) & 0x00ff); ++ ++ return SymbolRate ; ++} ++ ++static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz) ++{ ++ u32 regsym; ++ u32 adp_khz; ++ ++ regsym = stv0367_readreg(state, R367CAB_SRC_NCO_LL) + ++ (stv0367_readreg(state, R367CAB_SRC_NCO_LH) << 8) + ++ (stv0367_readreg(state, R367CAB_SRC_NCO_HL) << 16) + ++ (stv0367_readreg(state, R367CAB_SRC_NCO_HH) << 24); ++ ++ adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ ++ ++ if (regsym < 134217728) { /* 134217728L = 2**27*/ ++ regsym = regsym * 32; /* 32 = 2**5 */ ++ regsym = regsym / 32768; /* 32768L = 2**15 */ ++ regsym = adp_khz * regsym; /* AdpClk in kHz */ ++ regsym = regsym / 128; /* 128 = 2**7 */ ++ regsym *= 125 ; /* 125 = 1000/2**3 */ ++ regsym /= 2048 ; /* 2048 = 2**11 */ ++ } else if (regsym < 268435456) { /* 268435456L = 2**28 */ ++ regsym = regsym * 16; /* 16 = 2**4 */ ++ regsym = regsym / 32768; /* 32768L = 2**15 */ ++ regsym = adp_khz * regsym; /* AdpClk in kHz */ ++ regsym = regsym / 128; /* 128 = 2**7 */ ++ regsym *= 125 ; /* 125 = 1000/2**3*/ ++ regsym /= 1024 ; /* 256 = 2**10*/ ++ } else if (regsym < 536870912) { /* 536870912L = 2**29*/ ++ regsym = regsym * 8; /* 8 = 2**3 */ ++ regsym = regsym / 32768; /* 32768L = 2**15 */ ++ regsym = adp_khz * regsym; /* AdpClk in kHz */ ++ regsym = regsym / 128; /* 128 = 2**7 */ ++ regsym *= 125 ; /* 125 = 1000/2**3 */ ++ regsym /= 512 ; /* 128 = 2**9 */ ++ } else { ++ regsym = regsym * 4; /* 4 = 2**2 */ ++ regsym = regsym / 32768; /* 32768L = 2**15 */ ++ regsym = adp_khz * regsym; /* AdpClk in kHz */ ++ regsym = regsym / 128; /* 128 = 2**7 */ ++ regsym *= 125 ; /* 125 = 1000/2**3 */ ++ regsym /= 256 ; /* 64 = 2**8 */ ++ } ++ ++ return regsym; ++} ++ ++static int stv0367cab_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ dprintk("%s:\n", __func__); ++ ++ *status = 0; ++ ++ if (stv0367_readbits(state, F367CAB_QAMFEC_LOCK)) { ++ *status |= FE_HAS_LOCK; ++ dprintk("%s: stv0367 has locked\n", __func__); ++ } ++ ++ return 0; ++} ++ ++static int stv0367cab_standby(struct dvb_frontend *fe, u8 standby_on) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (standby_on) { ++ stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x03); ++ stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x01); ++ stv0367_writebits(state, F367CAB_STDBY, 1); ++ stv0367_writebits(state, F367CAB_STDBY_CORE, 1); ++ stv0367_writebits(state, F367CAB_EN_BUFFER_I, 0); ++ stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 0); ++ stv0367_writebits(state, F367CAB_POFFQ, 1); ++ stv0367_writebits(state, F367CAB_POFFI, 1); ++ } else { ++ stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x00); ++ stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x00); ++ stv0367_writebits(state, F367CAB_STDBY, 0); ++ stv0367_writebits(state, F367CAB_STDBY_CORE, 0); ++ stv0367_writebits(state, F367CAB_EN_BUFFER_I, 1); ++ stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 1); ++ stv0367_writebits(state, F367CAB_POFFQ, 0); ++ stv0367_writebits(state, F367CAB_POFFI, 0); ++ } ++ ++ return 0; ++} ++ ++static int stv0367cab_sleep(struct dvb_frontend *fe) ++{ ++ return stv0367cab_standby(fe, 1); ++} ++ ++static int stv0367cab_init(struct dvb_frontend *fe) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367cab_state *cab_state = state->cab_state; ++ int i; ++ ++ dprintk("%s:\n", __func__); ++ ++ for (i = 0; i < STV0367CAB_NBREGS; i++) ++ stv0367_writereg(state, def0367cab[i].addr, ++ def0367cab[i].value); ++ ++ switch (state->config->ts_mode) { ++ case STV0367_DVBCI_CLOCK: ++ dprintk("Setting TSMode = STV0367_DVBCI_CLOCK\n"); ++ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x03); ++ break; ++ case STV0367_SERIAL_PUNCT_CLOCK: ++ case STV0367_SERIAL_CONT_CLOCK: ++ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x01); ++ break; ++ case STV0367_PARALLEL_PUNCT_CLOCK: ++ case STV0367_OUTPUTMODE_DEFAULT: ++ stv0367_writebits(state, F367CAB_OUTFORMAT, 0x00); ++ break; ++ } ++ ++ switch (state->config->clk_pol) { ++ case STV0367_RISINGEDGE_CLOCK: ++ stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x00); ++ break; ++ case STV0367_FALLINGEDGE_CLOCK: ++ case STV0367_CLOCKPOLARITY_DEFAULT: ++ stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x01); ++ break; ++ } ++ ++ stv0367_writebits(state, F367CAB_SYNC_STRIP, 0x00); ++ ++ stv0367_writebits(state, F367CAB_CT_NBST, 0x01); ++ ++ stv0367_writebits(state, F367CAB_TS_SWAP, 0x01); ++ ++ stv0367_writebits(state, F367CAB_FIFO_BYPASS, 0x00); ++ ++ stv0367_writereg(state, R367CAB_ANACTRL, 0x00);/*PLL enabled and used */ ++ ++ cab_state->mclk = stv0367cab_get_mclk(fe, state->config->xtal); ++ cab_state->adc_clk = stv0367cab_get_adc_freq(fe, state->config->xtal); ++ ++ return 0; ++} ++static ++enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ struct stv0367cab_state *cab_state = state->cab_state; ++ enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC; ++ u32 QAMFEC_Lock, QAM_Lock, u32_tmp, ++ LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols, ++ CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut; ++ u8 TrackAGCAccum; ++ s32 tmp; ++ ++ dprintk("%s:\n", __func__); ++ ++ /* Timeouts calculation */ ++ /* A max lock time of 25 ms is allowed for delayed AGC */ ++ AGCTimeOut = 25; ++ /* 100000 symbols needed by the TRL as a maximum value */ ++ TRLTimeOut = 100000000 / p->symbol_rate; ++ /* CRLSymbols is the needed number of symbols to achieve a lock ++ within [-4%, +4%] of the symbol rate. ++ CRL timeout is calculated ++ for a lock within [-search_range, +search_range]. ++ EQL timeout can be changed depending on ++ the micro-reflections we want to handle. ++ A characterization must be performed ++ with these echoes to get new timeout values. ++ */ ++ switch (p->modulation) { ++ case QAM_16: ++ CRLSymbols = 150000; ++ EQLTimeOut = 100; ++ break; ++ case QAM_32: ++ CRLSymbols = 250000; ++ EQLTimeOut = 100; ++ break; ++ case QAM_64: ++ CRLSymbols = 200000; ++ EQLTimeOut = 100; ++ break; ++ case QAM_128: ++ CRLSymbols = 250000; ++ EQLTimeOut = 100; ++ break; ++ case QAM_256: ++ CRLSymbols = 250000; ++ EQLTimeOut = 100; ++ break; ++ default: ++ CRLSymbols = 200000; ++ EQLTimeOut = 100; ++ break; ++ } ++#if 0 ++ if (pIntParams->search_range < 0) { ++ CRLTimeOut = (25 * CRLSymbols * ++ (-pIntParams->search_range / 1000)) / ++ (pIntParams->symbol_rate / 1000); ++ } else ++#endif ++ CRLTimeOut = (25 * CRLSymbols * (cab_state->search_range / 1000)) / ++ (p->symbol_rate / 1000); ++ ++ CRLTimeOut = (1000 * CRLTimeOut) / p->symbol_rate; ++ /* Timeouts below 50ms are coerced */ ++ if (CRLTimeOut < 50) ++ CRLTimeOut = 50; ++ /* A maximum of 100 TS packets is needed to get FEC lock even in case ++ the spectrum inversion needs to be changed. ++ This is equal to 20 ms in case of the lowest symbol rate of 0.87Msps ++ */ ++ FECTimeOut = 20; ++ DemodTimeOut = AGCTimeOut + TRLTimeOut + CRLTimeOut + EQLTimeOut; ++ ++ dprintk("%s: DemodTimeOut=%d\n", __func__, DemodTimeOut); ++ ++ /* Reset the TRL to ensure nothing starts until the ++ AGC is stable which ensures a better lock time ++ */ ++ stv0367_writereg(state, R367CAB_CTRL_1, 0x04); ++ /* Set AGC accumulation time to minimum and lock threshold to maximum ++ in order to speed up the AGC lock */ ++ TrackAGCAccum = stv0367_readbits(state, F367CAB_AGC_ACCUMRSTSEL); ++ stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, 0x0); ++ /* Modulus Mapper is disabled */ ++ stv0367_writebits(state, F367CAB_MODULUSMAP_EN, 0); ++ /* Disable the sweep function */ ++ stv0367_writebits(state, F367CAB_SWEEP_EN, 0); ++ /* The sweep function is never used, Sweep rate must be set to 0 */ ++ /* Set the derotator frequency in Hz */ ++ stv0367cab_set_derot_freq(state, cab_state->adc_clk, ++ (1000 * (s32)state->config->if_khz + cab_state->derot_offset)); ++ /* Disable the Allpass Filter when the symbol rate is out of range */ ++ if ((p->symbol_rate > 10800000) | (p->symbol_rate < 1800000)) { ++ stv0367_writebits(state, F367CAB_ADJ_EN, 0); ++ stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); ++ } ++#if 0 ++ /* Check if the tuner is locked */ ++ tuner_lock = stv0367cab_tuner_get_status(fe); ++ if (tuner_lock == 0) ++ return FE_367CAB_NOTUNER; ++#endif ++ /* Relase the TRL to start demodulator acquisition */ ++ /* Wait for QAM lock */ ++ LockTime = 0; ++ stv0367_writereg(state, R367CAB_CTRL_1, 0x00); ++ do { ++ QAM_Lock = stv0367_readbits(state, F367CAB_FSM_STATUS); ++ if ((LockTime >= (DemodTimeOut - EQLTimeOut)) && ++ (QAM_Lock == 0x04)) ++ /* ++ * We don't wait longer, the frequency/phase offset ++ * must be too big ++ */ ++ LockTime = DemodTimeOut; ++ else if ((LockTime >= (AGCTimeOut + TRLTimeOut)) && ++ (QAM_Lock == 0x02)) ++ /* ++ * We don't wait longer, either there is no signal or ++ * it is not the right symbol rate or it is an analog ++ * carrier ++ */ ++ { ++ LockTime = DemodTimeOut; ++ u32_tmp = stv0367_readbits(state, ++ F367CAB_AGC_PWR_WORD_LO) + ++ (stv0367_readbits(state, ++ F367CAB_AGC_PWR_WORD_ME) << 8) + ++ (stv0367_readbits(state, ++ F367CAB_AGC_PWR_WORD_HI) << 16); ++ if (u32_tmp >= 131072) ++ u32_tmp = 262144 - u32_tmp; ++ u32_tmp = u32_tmp / (1 << (11 - stv0367_readbits(state, ++ F367CAB_AGC_IF_BWSEL))); ++ ++ if (u32_tmp < stv0367_readbits(state, ++ F367CAB_AGC_PWRREF_LO) + ++ 256 * stv0367_readbits(state, ++ F367CAB_AGC_PWRREF_HI) - 10) ++ QAM_Lock = 0x0f; ++ } else { ++ usleep_range(10000, 20000); ++ LockTime += 10; ++ } ++ dprintk("QAM_Lock=0x%x LockTime=%d\n", QAM_Lock, LockTime); ++ tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); ++ ++ dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); ++ ++ } while (((QAM_Lock != 0x0c) && (QAM_Lock != 0x0b)) && ++ (LockTime < DemodTimeOut)); ++ ++ dprintk("QAM_Lock=0x%x\n", QAM_Lock); ++ ++ tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); ++ dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); ++ tmp = stv0367_readreg(state, R367CAB_IT_STATUS2); ++ dprintk("R367CAB_IT_STATUS2=0x%x\n", tmp); ++ ++ tmp = stv0367cab_get_derot_freq(state, cab_state->adc_clk); ++ dprintk("stv0367cab_get_derot_freq=0x%x\n", tmp); ++ ++ if ((QAM_Lock == 0x0c) || (QAM_Lock == 0x0b)) { ++ /* Wait for FEC lock */ ++ LockTime = 0; ++ do { ++ usleep_range(5000, 7000); ++ LockTime += 5; ++ QAMFEC_Lock = stv0367_readbits(state, ++ F367CAB_QAMFEC_LOCK); ++ } while (!QAMFEC_Lock && (LockTime < FECTimeOut)); ++ } else ++ QAMFEC_Lock = 0; ++ ++ if (QAMFEC_Lock) { ++ signalType = FE_CAB_DATAOK; ++ cab_state->modulation = p->modulation; ++ cab_state->spect_inv = stv0367_readbits(state, ++ F367CAB_QUAD_INV); ++#if 0 ++/* not clear for me */ ++ if (state->config->if_khz != 0) { ++ if (state->config->if_khz > cab_state->adc_clk / 1000) { ++ cab_state->freq_khz = ++ FE_Cab_TunerGetFrequency(pIntParams->hTuner) ++ - stv0367cab_get_derot_freq(state, cab_state->adc_clk) ++ - cab_state->adc_clk / 1000 + state->config->if_khz; ++ } else { ++ cab_state->freq_khz = ++ FE_Cab_TunerGetFrequency(pIntParams->hTuner) ++ - stv0367cab_get_derot_freq(state, cab_state->adc_clk) ++ + state->config->if_khz; ++ } ++ } else { ++ cab_state->freq_khz = ++ FE_Cab_TunerGetFrequency(pIntParams->hTuner) + ++ stv0367cab_get_derot_freq(state, ++ cab_state->adc_clk) - ++ cab_state->adc_clk / 4000; ++ } ++#endif ++ cab_state->symbol_rate = stv0367cab_GetSymbolRate(state, ++ cab_state->mclk); ++ cab_state->locked = 1; ++ ++ /* stv0367_setbits(state, F367CAB_AGC_ACCUMRSTSEL,7);*/ ++ } else { ++ switch (QAM_Lock) { ++ case 1: ++ signalType = FE_CAB_NOAGC; ++ break; ++ case 2: ++ signalType = FE_CAB_NOTIMING; ++ break; ++ case 3: ++ signalType = FE_CAB_TIMINGOK; ++ break; ++ case 4: ++ signalType = FE_CAB_NOCARRIER; ++ break; ++ case 5: ++ signalType = FE_CAB_CARRIEROK; ++ break; ++ case 7: ++ signalType = FE_CAB_NOBLIND; ++ break; ++ case 8: ++ signalType = FE_CAB_BLINDOK; ++ break; ++ case 10: ++ signalType = FE_CAB_NODEMOD; ++ break; ++ case 11: ++ signalType = FE_CAB_DEMODOK; ++ break; ++ case 12: ++ signalType = FE_CAB_DEMODOK; ++ break; ++ case 13: ++ signalType = FE_CAB_NODEMOD; ++ break; ++ case 14: ++ signalType = FE_CAB_NOBLIND; ++ break; ++ case 15: ++ signalType = FE_CAB_NOSIGNAL; ++ break; ++ default: ++ break; ++ } ++ ++ } ++ ++ /* Set the AGC control values to tracking values */ ++ stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, TrackAGCAccum); ++ return signalType; ++} ++ ++static int stv0367cab_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367cab_state *cab_state = state->cab_state; ++ enum stv0367cab_mod QAMSize = 0; ++ ++ dprintk("%s: freq = %d, srate = %d\n", __func__, ++ p->frequency, p->symbol_rate); ++ ++ cab_state->derot_offset = 0; ++ ++ switch (p->modulation) { ++ case QAM_16: ++ QAMSize = FE_CAB_MOD_QAM16; ++ break; ++ case QAM_32: ++ QAMSize = FE_CAB_MOD_QAM32; ++ break; ++ case QAM_64: ++ QAMSize = FE_CAB_MOD_QAM64; ++ break; ++ case QAM_128: ++ QAMSize = FE_CAB_MOD_QAM128; ++ break; ++ case QAM_256: ++ QAMSize = FE_CAB_MOD_QAM256; ++ break; ++ default: ++ break; ++ } ++ ++ stv0367cab_init(fe); ++ ++ /* Tuner Frequency Setting */ ++ if (fe->ops.tuner_ops.set_params) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ stv0367cab_SetQamSize( ++ state, ++ p->symbol_rate, ++ QAMSize); ++ ++ stv0367cab_set_srate(state, ++ cab_state->adc_clk, ++ cab_state->mclk, ++ p->symbol_rate, ++ QAMSize); ++ /* Search algorithm launch, [-1.1*RangeOffset, +1.1*RangeOffset] scan */ ++ cab_state->state = stv0367cab_algo(state, p); ++ return 0; ++} ++ ++static int stv0367cab_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0367_state *state = fe->demodulator_priv; ++ struct stv0367cab_state *cab_state = state->cab_state; ++ ++ enum stv0367cab_mod QAMSize; ++ ++ dprintk("%s:\n", __func__); ++ ++ p->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk); ++ ++ QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); ++ switch (QAMSize) { ++ case FE_CAB_MOD_QAM16: ++ p->modulation = QAM_16; ++ break; ++ case FE_CAB_MOD_QAM32: ++ p->modulation = QAM_32; ++ break; ++ case FE_CAB_MOD_QAM64: ++ p->modulation = QAM_64; ++ break; ++ case FE_CAB_MOD_QAM128: ++ p->modulation = QAM_128; ++ break; ++ case QAM_256: ++ p->modulation = QAM_256; ++ break; ++ default: ++ break; ++ } ++ ++ p->frequency = stv0367_get_tuner_freq(fe); ++ ++ dprintk("%s: tuner frequency = %d\n", __func__, p->frequency); ++ ++ if (state->config->if_khz == 0) { ++ p->frequency += ++ (stv0367cab_get_derot_freq(state, cab_state->adc_clk) - ++ cab_state->adc_clk / 4000); ++ return 0; ++ } ++ ++ if (state->config->if_khz > cab_state->adc_clk / 1000) ++ p->frequency += (state->config->if_khz ++ - stv0367cab_get_derot_freq(state, cab_state->adc_clk) ++ - cab_state->adc_clk / 1000); ++ else ++ p->frequency += (state->config->if_khz ++ - stv0367cab_get_derot_freq(state, cab_state->adc_clk)); ++ ++ return 0; ++} ++ ++#if 0 ++void stv0367cab_GetErrorCount(state, enum stv0367cab_mod QAMSize, ++ u32 symbol_rate, FE_367qam_Monitor *Monitor_results) ++{ ++ stv0367cab_OptimiseNByteAndGetBER(state, QAMSize, symbol_rate, Monitor_results); ++ stv0367cab_GetPacketsCount(state, Monitor_results); ++ ++ return; ++} ++ ++static int stv0367cab_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ return 0; ++} ++#endif ++static s32 stv0367cab_get_rf_lvl(struct stv0367_state *state) ++{ ++ s32 rfLevel = 0; ++ s32 RfAgcPwm = 0, IfAgcPwm = 0; ++ u8 i; ++ ++ stv0367_writebits(state, F367CAB_STDBY_ADCGP, 0x0); ++ ++ RfAgcPwm = ++ (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_LO) & 0x03) + ++ (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_HI) << 2); ++ RfAgcPwm = 100 * RfAgcPwm / 1023; ++ ++ IfAgcPwm = ++ stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_LO) + ++ (stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_HI) << 8); ++ if (IfAgcPwm >= 2048) ++ IfAgcPwm -= 2048; ++ else ++ IfAgcPwm += 2048; ++ ++ IfAgcPwm = 100 * IfAgcPwm / 4095; ++ ++ /* For DTT75467 on NIM */ ++ if (RfAgcPwm < 90 && IfAgcPwm < 28) { ++ for (i = 0; i < RF_LOOKUP_TABLE_SIZE; i++) { ++ if (RfAgcPwm <= stv0367cab_RF_LookUp1[0][i]) { ++ rfLevel = (-1) * stv0367cab_RF_LookUp1[1][i]; ++ break; ++ } ++ } ++ if (i == RF_LOOKUP_TABLE_SIZE) ++ rfLevel = -56; ++ } else { /*if IF AGC>10*/ ++ for (i = 0; i < RF_LOOKUP_TABLE2_SIZE; i++) { ++ if (IfAgcPwm <= stv0367cab_RF_LookUp2[0][i]) { ++ rfLevel = (-1) * stv0367cab_RF_LookUp2[1][i]; ++ break; ++ } ++ } ++ if (i == RF_LOOKUP_TABLE2_SIZE) ++ rfLevel = -72; ++ } ++ return rfLevel; ++} ++ ++static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ ++ s32 signal = stv0367cab_get_rf_lvl(state); ++ ++ dprintk("%s: signal=%d dBm\n", __func__, signal); ++ ++ if (signal <= -72) ++ *strength = 65535; ++ else ++ *strength = (22 + signal) * (-1311); ++ ++ dprintk("%s: strength=%d\n", __func__, (*strength)); ++ ++ return 0; ++} ++ ++static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ u32 noisepercentage; ++ enum stv0367cab_mod QAMSize; ++ u32 regval = 0, temp = 0; ++ int power, i; ++ ++ QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); ++ switch (QAMSize) { ++ case FE_CAB_MOD_QAM4: ++ power = 21904; ++ break; ++ case FE_CAB_MOD_QAM16: ++ power = 20480; ++ break; ++ case FE_CAB_MOD_QAM32: ++ power = 23040; ++ break; ++ case FE_CAB_MOD_QAM64: ++ power = 21504; ++ break; ++ case FE_CAB_MOD_QAM128: ++ power = 23616; ++ break; ++ case FE_CAB_MOD_QAM256: ++ power = 21760; ++ break; ++ case FE_CAB_MOD_QAM512: ++ power = 1; ++ break; ++ case FE_CAB_MOD_QAM1024: ++ power = 21280; ++ break; ++ default: ++ power = 1; ++ break; ++ } ++ ++ for (i = 0; i < 10; i++) { ++ regval += (stv0367_readbits(state, F367CAB_SNR_LO) ++ + 256 * stv0367_readbits(state, F367CAB_SNR_HI)); ++ } ++ ++ regval /= 10; /*for average over 10 times in for loop above*/ ++ if (regval != 0) { ++ temp = power ++ * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER))); ++ temp /= regval; ++ } ++ ++ /* table values, not needed to calculate logarithms */ ++ if (temp >= 5012) ++ noisepercentage = 100; ++ else if (temp >= 3981) ++ noisepercentage = 93; ++ else if (temp >= 3162) ++ noisepercentage = 86; ++ else if (temp >= 2512) ++ noisepercentage = 79; ++ else if (temp >= 1995) ++ noisepercentage = 72; ++ else if (temp >= 1585) ++ noisepercentage = 65; ++ else if (temp >= 1259) ++ noisepercentage = 58; ++ else if (temp >= 1000) ++ noisepercentage = 50; ++ else if (temp >= 794) ++ noisepercentage = 43; ++ else if (temp >= 501) ++ noisepercentage = 36; ++ else if (temp >= 316) ++ noisepercentage = 29; ++ else if (temp >= 200) ++ noisepercentage = 22; ++ else if (temp >= 158) ++ noisepercentage = 14; ++ else if (temp >= 126) ++ noisepercentage = 7; ++ else ++ noisepercentage = 0; ++ ++ dprintk("%s: noisepercentage=%d\n", __func__, noisepercentage); ++ ++ *snr = (noisepercentage * 65535) / 100; ++ ++ return 0; ++} ++ ++static int stv0367cab_read_ucblcks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct stv0367_state *state = fe->demodulator_priv; ++ int corrected, tscount; ++ ++ *ucblocks = (stv0367_readreg(state, R367CAB_RS_COUNTER_5) << 8) ++ | stv0367_readreg(state, R367CAB_RS_COUNTER_4); ++ corrected = (stv0367_readreg(state, R367CAB_RS_COUNTER_3) << 8) ++ | stv0367_readreg(state, R367CAB_RS_COUNTER_2); ++ tscount = (stv0367_readreg(state, R367CAB_RS_COUNTER_2) << 8) ++ | stv0367_readreg(state, R367CAB_RS_COUNTER_1); ++ ++ dprintk("%s: uncorrected blocks=%d corrected blocks=%d tscount=%d\n", ++ __func__, *ucblocks, corrected, tscount); ++ ++ return 0; ++}; ++ ++static struct dvb_frontend_ops stv0367cab_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A }, ++ .info = { ++ .name = "ST STV0367 DVB-C", ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 62500, ++ .symbol_rate_min = 870000, ++ .symbol_rate_max = 11700000, ++ .caps = 0x400 |/* FE_CAN_QAM_4 */ ++ FE_CAN_QAM_16 | FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | FE_CAN_FEC_AUTO ++ }, ++ .release = stv0367_release, ++ .init = stv0367cab_init, ++ .sleep = stv0367cab_sleep, ++ .i2c_gate_ctrl = stv0367cab_gate_ctrl, ++ .set_frontend = stv0367cab_set_frontend, ++ .get_frontend = stv0367cab_get_frontend, ++ .read_status = stv0367cab_read_status, ++/* .read_ber = stv0367cab_read_ber, */ ++ .read_signal_strength = stv0367cab_read_strength, ++ .read_snr = stv0367cab_read_snr, ++ .read_ucblocks = stv0367cab_read_ucblcks, ++ .get_tune_settings = stv0367_get_tune_settings, ++}; ++ ++struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv0367_state *state = NULL; ++ struct stv0367cab_state *cab_state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL); ++ if (cab_state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ state->config = config; ++ cab_state->search_range = 280000; ++ state->cab_state = cab_state; ++ state->fe.ops = stv0367cab_ops; ++ state->fe.demodulator_priv = state; ++ state->chip_id = stv0367_readreg(state, 0xf000); ++ ++ dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); ++ ++ /* check if the demod is there */ ++ if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) ++ goto error; ++ ++ return &state->fe; ++ ++error: ++ kfree(cab_state); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stv0367cab_attach); ++ ++MODULE_PARM_DESC(debug, "Set debug"); ++MODULE_PARM_DESC(i2c_debug, "Set i2c debug"); ++ ++MODULE_AUTHOR("Igor M. Liplianin"); ++MODULE_DESCRIPTION("ST STV0367 DVB-C/T demodulator driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stv0367.h b/drivers/media/dvb-frontends/stv0367.h +new file mode 100644 +index 0000000..93cc4a5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0367.h +@@ -0,0 +1,66 @@ ++/* ++ * stv0367.h ++ * ++ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2010,2011 NetUP Inc. ++ * Copyright (C) 2010,2011 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0367_H ++#define STV0367_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct stv0367_config { ++ u8 demod_address; ++ u32 xtal; ++ u32 if_khz;/*4500*/ ++ int if_iq_mode; ++ int ts_mode; ++ int clk_pol; ++}; ++ ++#if defined(CONFIG_DVB_STV0367) || (defined(CONFIG_DVB_STV0367_MODULE) \ ++ && defined(MODULE)) ++extern struct ++dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c); ++extern struct ++dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct ++dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline struct ++dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stv0367_priv.h b/drivers/media/dvb-frontends/stv0367_priv.h +new file mode 100644 +index 0000000..995db06 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0367_priv.h +@@ -0,0 +1,212 @@ ++/* ++ * stv0367_priv.h ++ * ++ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2010,2011 NetUP Inc. ++ * Copyright (C) 2010,2011 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++/* Common driver error constants */ ++ ++#ifndef STV0367_PRIV_H ++#define STV0367_PRIV_H ++ ++#ifndef TRUE ++ #define TRUE (1 == 1) ++#endif ++#ifndef FALSE ++ #define FALSE (!TRUE) ++#endif ++ ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++/* MACRO definitions */ ++#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) ++#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) ++#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) ++#define INRANGE(X, Y, Z) \ ++ ((((X) <= (Y)) && ((Y) <= (Z))) || \ ++ (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) ++ ++#ifndef MAKEWORD ++#define MAKEWORD(X, Y) (((X) << 8) + (Y)) ++#endif ++ ++#define LSB(X) (((X) & 0xff)) ++#define MSB(Y) (((Y) >> 8) & 0xff) ++#define MMSB(Y)(((Y) >> 16) & 0xff) ++ ++enum stv0367_ter_signal_type { ++ FE_TER_NOAGC = 0, ++ FE_TER_AGCOK = 5, ++ FE_TER_NOTPS = 6, ++ FE_TER_TPSOK = 7, ++ FE_TER_NOSYMBOL = 8, ++ FE_TER_BAD_CPQ = 9, ++ FE_TER_PRFOUNDOK = 10, ++ FE_TER_NOPRFOUND = 11, ++ FE_TER_LOCKOK = 12, ++ FE_TER_NOLOCK = 13, ++ FE_TER_SYMBOLOK = 15, ++ FE_TER_CPAMPOK = 16, ++ FE_TER_NOCPAMP = 17, ++ FE_TER_SWNOK = 18 ++}; ++ ++enum stv0367_ts_mode { ++ STV0367_OUTPUTMODE_DEFAULT, ++ STV0367_SERIAL_PUNCT_CLOCK, ++ STV0367_SERIAL_CONT_CLOCK, ++ STV0367_PARALLEL_PUNCT_CLOCK, ++ STV0367_DVBCI_CLOCK ++}; ++ ++enum stv0367_clk_pol { ++ STV0367_CLOCKPOLARITY_DEFAULT, ++ STV0367_RISINGEDGE_CLOCK, ++ STV0367_FALLINGEDGE_CLOCK ++}; ++ ++enum stv0367_ter_bw { ++ FE_TER_CHAN_BW_6M = 6, ++ FE_TER_CHAN_BW_7M = 7, ++ FE_TER_CHAN_BW_8M = 8 ++}; ++ ++#if 0 ++enum FE_TER_Rate_TPS { ++ FE_TER_TPS_1_2 = 0, ++ FE_TER_TPS_2_3 = 1, ++ FE_TER_TPS_3_4 = 2, ++ FE_TER_TPS_5_6 = 3, ++ FE_TER_TPS_7_8 = 4 ++}; ++#endif ++ ++enum stv0367_ter_mode { ++ FE_TER_MODE_2K, ++ FE_TER_MODE_8K, ++ FE_TER_MODE_4K ++}; ++#if 0 ++enum FE_TER_Hierarchy_Alpha { ++ FE_TER_HIER_ALPHA_NONE, /* Regular modulation */ ++ FE_TER_HIER_ALPHA_1, /* Hierarchical modulation a = 1*/ ++ FE_TER_HIER_ALPHA_2, /* Hierarchical modulation a = 2*/ ++ FE_TER_HIER_ALPHA_4 /* Hierarchical modulation a = 4*/ ++}; ++#endif ++enum stv0367_ter_hierarchy { ++ FE_TER_HIER_NONE, /*Hierarchy None*/ ++ FE_TER_HIER_LOW_PRIO, /*Hierarchy : Low Priority*/ ++ FE_TER_HIER_HIGH_PRIO, /*Hierarchy : High Priority*/ ++ FE_TER_HIER_PRIO_ANY /*Hierarchy :Any*/ ++}; ++ ++#if 0 ++enum fe_stv0367_ter_spec { ++ FE_TER_INVERSION_NONE = 0, ++ FE_TER_INVERSION = 1, ++ FE_TER_INVERSION_AUTO = 2, ++ FE_TER_INVERSION_UNK = 4 ++}; ++#endif ++ ++enum stv0367_ter_if_iq_mode { ++ FE_TER_NORMAL_IF_TUNER = 0, ++ FE_TER_LONGPATH_IF_TUNER = 1, ++ FE_TER_IQ_TUNER = 2 ++ ++}; ++ ++#if 0 ++enum FE_TER_FECRate { ++ FE_TER_FEC_NONE = 0x00, /* no FEC rate specified */ ++ FE_TER_FEC_ALL = 0xFF, /* Logical OR of all FECs */ ++ FE_TER_FEC_1_2 = 1, ++ FE_TER_FEC_2_3 = (1 << 1), ++ FE_TER_FEC_3_4 = (1 << 2), ++ FE_TER_FEC_4_5 = (1 << 3), ++ FE_TER_FEC_5_6 = (1 << 4), ++ FE_TER_FEC_6_7 = (1 << 5), ++ FE_TER_FEC_7_8 = (1 << 6), ++ FE_TER_FEC_8_9 = (1 << 7) ++}; ++ ++enum FE_TER_Rate { ++ FE_TER_FE_1_2 = 0, ++ FE_TER_FE_2_3 = 1, ++ FE_TER_FE_3_4 = 2, ++ FE_TER_FE_5_6 = 3, ++ FE_TER_FE_6_7 = 4, ++ FE_TER_FE_7_8 = 5 ++}; ++#endif ++ ++enum stv0367_ter_force { ++ FE_TER_FORCENONE = 0, ++ FE_TER_FORCE_M_G = 1 ++}; ++ ++enum stv0367cab_mod { ++ FE_CAB_MOD_QAM4, ++ FE_CAB_MOD_QAM16, ++ FE_CAB_MOD_QAM32, ++ FE_CAB_MOD_QAM64, ++ FE_CAB_MOD_QAM128, ++ FE_CAB_MOD_QAM256, ++ FE_CAB_MOD_QAM512, ++ FE_CAB_MOD_QAM1024 ++}; ++#if 0 ++enum { ++ FE_CAB_FEC_A = 1, /* J83 Annex A */ ++ FE_CAB_FEC_B = (1 << 1),/* J83 Annex B */ ++ FE_CAB_FEC_C = (1 << 2) /* J83 Annex C */ ++} FE_CAB_FECType_t; ++#endif ++struct stv0367_cab_signal_info { ++ int locked; ++ u32 frequency; /* kHz */ ++ u32 symbol_rate; /* Mbds */ ++ enum stv0367cab_mod modulation; ++ fe_spectral_inversion_t spect_inv; ++ s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */ ++ u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */ ++ u32 BER; /* Bit error rate (x 10000000) */ ++}; ++ ++enum stv0367_cab_signal_type { ++ FE_CAB_NOTUNER, ++ FE_CAB_NOAGC, ++ FE_CAB_NOSIGNAL, ++ FE_CAB_NOTIMING, ++ FE_CAB_TIMINGOK, ++ FE_CAB_NOCARRIER, ++ FE_CAB_CARRIEROK, ++ FE_CAB_NOBLIND, ++ FE_CAB_BLINDOK, ++ FE_CAB_NODEMOD, ++ FE_CAB_DEMODOK, ++ FE_CAB_DATAOK ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stv0367_regs.h b/drivers/media/dvb-frontends/stv0367_regs.h +new file mode 100644 +index 0000000..a96fbdc +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0367_regs.h +@@ -0,0 +1,3614 @@ ++/* ++ * stv0367_regs.h ++ * ++ * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2010,2011 NetUP Inc. ++ * Copyright (C) 2010,2011 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0367_REGS_H ++#define STV0367_REGS_H ++ ++/* ID */ ++#define R367TER_ID 0xf000 ++#define F367TER_IDENTIFICATIONREG 0xf00000ff ++ ++/* I2CRPT */ ++#define R367TER_I2CRPT 0xf001 ++#define F367TER_I2CT_ON 0xf0010080 ++#define F367TER_ENARPT_LEVEL 0xf0010070 ++#define F367TER_SCLT_DELAY 0xf0010008 ++#define F367TER_SCLT_NOD 0xf0010004 ++#define F367TER_STOP_ENABLE 0xf0010002 ++#define F367TER_SDAT_NOD 0xf0010001 ++ ++/* TOPCTRL */ ++#define R367TER_TOPCTRL 0xf002 ++#define F367TER_STDBY 0xf0020080 ++#define F367TER_STDBY_FEC 0xf0020040 ++#define F367TER_STDBY_CORE 0xf0020020 ++#define F367TER_QAM_COFDM 0xf0020010 ++#define F367TER_TS_DIS 0xf0020008 ++#define F367TER_DIR_CLK_216 0xf0020004 ++#define F367TER_TUNER_BB 0xf0020002 ++#define F367TER_DVBT_H 0xf0020001 ++ ++/* IOCFG0 */ ++#define R367TER_IOCFG0 0xf003 ++#define F367TER_OP0_SD 0xf0030080 ++#define F367TER_OP0_VAL 0xf0030040 ++#define F367TER_OP0_OD 0xf0030020 ++#define F367TER_OP0_INV 0xf0030010 ++#define F367TER_OP0_DACVALUE_HI 0xf003000f ++ ++/* DAc0R */ ++#define R367TER_DAC0R 0xf004 ++#define F367TER_OP0_DACVALUE_LO 0xf00400ff ++ ++/* IOCFG1 */ ++#define R367TER_IOCFG1 0xf005 ++#define F367TER_IP0 0xf0050040 ++#define F367TER_OP1_OD 0xf0050020 ++#define F367TER_OP1_INV 0xf0050010 ++#define F367TER_OP1_DACVALUE_HI 0xf005000f ++ ++/* DAC1R */ ++#define R367TER_DAC1R 0xf006 ++#define F367TER_OP1_DACVALUE_LO 0xf00600ff ++ ++/* IOCFG2 */ ++#define R367TER_IOCFG2 0xf007 ++#define F367TER_OP2_LOCK_CONF 0xf00700e0 ++#define F367TER_OP2_OD 0xf0070010 ++#define F367TER_OP2_VAL 0xf0070008 ++#define F367TER_OP1_LOCK_CONF 0xf0070007 ++ ++/* SDFR */ ++#define R367TER_SDFR 0xf008 ++#define F367TER_OP0_FREQ 0xf00800f0 ++#define F367TER_OP1_FREQ 0xf008000f ++ ++/* STATUS */ ++#define R367TER_STATUS 0xf009 ++#define F367TER_TPS_LOCK 0xf0090080 ++#define F367TER_SYR_LOCK 0xf0090040 ++#define F367TER_AGC_LOCK 0xf0090020 ++#define F367TER_PRF 0xf0090010 ++#define F367TER_LK 0xf0090008 ++#define F367TER_PR 0xf0090007 ++ ++/* AUX_CLK */ ++#define R367TER_AUX_CLK 0xf00a ++#define F367TER_AUXFEC_CTL 0xf00a00c0 ++#define F367TER_DIS_CKX4 0xf00a0020 ++#define F367TER_CKSEL 0xf00a0018 ++#define F367TER_CKDIV_PROG 0xf00a0006 ++#define F367TER_AUXCLK_ENA 0xf00a0001 ++ ++/* FREESYS1 */ ++#define R367TER_FREESYS1 0xf00b ++#define F367TER_FREE_SYS1 0xf00b00ff ++ ++/* FREESYS2 */ ++#define R367TER_FREESYS2 0xf00c ++#define F367TER_FREE_SYS2 0xf00c00ff ++ ++/* FREESYS3 */ ++#define R367TER_FREESYS3 0xf00d ++#define F367TER_FREE_SYS3 0xf00d00ff ++ ++/* GPIO_CFG */ ++#define R367TER_GPIO_CFG 0xf00e ++#define F367TER_GPIO7_NOD 0xf00e0080 ++#define F367TER_GPIO7_CFG 0xf00e0040 ++#define F367TER_GPIO6_NOD 0xf00e0020 ++#define F367TER_GPIO6_CFG 0xf00e0010 ++#define F367TER_GPIO5_NOD 0xf00e0008 ++#define F367TER_GPIO5_CFG 0xf00e0004 ++#define F367TER_GPIO4_NOD 0xf00e0002 ++#define F367TER_GPIO4_CFG 0xf00e0001 ++ ++/* GPIO_CMD */ ++#define R367TER_GPIO_CMD 0xf00f ++#define F367TER_GPIO7_VAL 0xf00f0008 ++#define F367TER_GPIO6_VAL 0xf00f0004 ++#define F367TER_GPIO5_VAL 0xf00f0002 ++#define F367TER_GPIO4_VAL 0xf00f0001 ++ ++/* AGC2MAX */ ++#define R367TER_AGC2MAX 0xf010 ++#define F367TER_AGC2_MAX 0xf01000ff ++ ++/* AGC2MIN */ ++#define R367TER_AGC2MIN 0xf011 ++#define F367TER_AGC2_MIN 0xf01100ff ++ ++/* AGC1MAX */ ++#define R367TER_AGC1MAX 0xf012 ++#define F367TER_AGC1_MAX 0xf01200ff ++ ++/* AGC1MIN */ ++#define R367TER_AGC1MIN 0xf013 ++#define F367TER_AGC1_MIN 0xf01300ff ++ ++/* AGCR */ ++#define R367TER_AGCR 0xf014 ++#define F367TER_RATIO_A 0xf01400e0 ++#define F367TER_RATIO_B 0xf0140018 ++#define F367TER_RATIO_C 0xf0140007 ++ ++/* AGC2TH */ ++#define R367TER_AGC2TH 0xf015 ++#define F367TER_AGC2_THRES 0xf01500ff ++ ++/* AGC12c */ ++#define R367TER_AGC12C 0xf016 ++#define F367TER_AGC1_IV 0xf0160080 ++#define F367TER_AGC1_OD 0xf0160040 ++#define F367TER_AGC1_LOAD 0xf0160020 ++#define F367TER_AGC2_IV 0xf0160010 ++#define F367TER_AGC2_OD 0xf0160008 ++#define F367TER_AGC2_LOAD 0xf0160004 ++#define F367TER_AGC12_MODE 0xf0160003 ++ ++/* AGCCTRL1 */ ++#define R367TER_AGCCTRL1 0xf017 ++#define F367TER_DAGC_ON 0xf0170080 ++#define F367TER_INVERT_AGC12 0xf0170040 ++#define F367TER_AGC1_MODE 0xf0170008 ++#define F367TER_AGC2_MODE 0xf0170007 ++ ++/* AGCCTRL2 */ ++#define R367TER_AGCCTRL2 0xf018 ++#define F367TER_FRZ2_CTRL 0xf0180060 ++#define F367TER_FRZ1_CTRL 0xf0180018 ++#define F367TER_TIME_CST 0xf0180007 ++ ++/* AGC1VAL1 */ ++#define R367TER_AGC1VAL1 0xf019 ++#define F367TER_AGC1_VAL_LO 0xf01900ff ++ ++/* AGC1VAL2 */ ++#define R367TER_AGC1VAL2 0xf01a ++#define F367TER_AGC1_VAL_HI 0xf01a000f ++ ++/* AGC2VAL1 */ ++#define R367TER_AGC2VAL1 0xf01b ++#define F367TER_AGC2_VAL_LO 0xf01b00ff ++ ++/* AGC2VAL2 */ ++#define R367TER_AGC2VAL2 0xf01c ++#define F367TER_AGC2_VAL_HI 0xf01c000f ++ ++/* AGC2PGA */ ++#define R367TER_AGC2PGA 0xf01d ++#define F367TER_AGC2_PGA 0xf01d00ff ++ ++/* OVF_RATE1 */ ++#define R367TER_OVF_RATE1 0xf01e ++#define F367TER_OVF_RATE_HI 0xf01e000f ++ ++/* OVF_RATE2 */ ++#define R367TER_OVF_RATE2 0xf01f ++#define F367TER_OVF_RATE_LO 0xf01f00ff ++ ++/* GAIN_SRC1 */ ++#define R367TER_GAIN_SRC1 0xf020 ++#define F367TER_INV_SPECTR 0xf0200080 ++#define F367TER_IQ_INVERT 0xf0200040 ++#define F367TER_INR_BYPASS 0xf0200020 ++#define F367TER_STATUS_INV_SPECRUM 0xf0200010 ++#define F367TER_GAIN_SRC_HI 0xf020000f ++ ++/* GAIN_SRC2 */ ++#define R367TER_GAIN_SRC2 0xf021 ++#define F367TER_GAIN_SRC_LO 0xf02100ff ++ ++/* INC_DEROT1 */ ++#define R367TER_INC_DEROT1 0xf022 ++#define F367TER_INC_DEROT_HI 0xf02200ff ++ ++/* INC_DEROT2 */ ++#define R367TER_INC_DEROT2 0xf023 ++#define F367TER_INC_DEROT_LO 0xf02300ff ++ ++/* PPM_CPAMP_DIR */ ++#define R367TER_PPM_CPAMP_DIR 0xf024 ++#define F367TER_PPM_CPAMP_DIRECT 0xf02400ff ++ ++/* PPM_CPAMP_INV */ ++#define R367TER_PPM_CPAMP_INV 0xf025 ++#define F367TER_PPM_CPAMP_INVER 0xf02500ff ++ ++/* FREESTFE_1 */ ++#define R367TER_FREESTFE_1 0xf026 ++#define F367TER_SYMBOL_NUMBER_INC 0xf02600c0 ++#define F367TER_SEL_LSB 0xf0260004 ++#define F367TER_AVERAGE_ON 0xf0260002 ++#define F367TER_DC_ADJ 0xf0260001 ++ ++/* FREESTFE_2 */ ++#define R367TER_FREESTFE_2 0xf027 ++#define F367TER_SEL_SRCOUT 0xf02700c0 ++#define F367TER_SEL_SYRTHR 0xf027001f ++ ++/* DCOFFSET */ ++#define R367TER_DCOFFSET 0xf028 ++#define F367TER_SELECT_I_Q 0xf0280080 ++#define F367TER_DC_OFFSET 0xf028007f ++ ++/* EN_PROCESS */ ++#define R367TER_EN_PROCESS 0xf029 ++#define F367TER_FREE 0xf02900f0 ++#define F367TER_ENAB_MANUAL 0xf0290001 ++ ++/* SDI_SMOOTHER */ ++#define R367TER_SDI_SMOOTHER 0xf02a ++#define F367TER_DIS_SMOOTH 0xf02a0080 ++#define F367TER_SDI_INC_SMOOTHER 0xf02a007f ++ ++/* FE_LOOP_OPEN */ ++#define R367TER_FE_LOOP_OPEN 0xf02b ++#define F367TER_TRL_LOOP_OP 0xf02b0002 ++#define F367TER_CRL_LOOP_OP 0xf02b0001 ++ ++/* FREQOFF1 */ ++#define R367TER_FREQOFF1 0xf02c ++#define F367TER_FREQ_OFFSET_LOOP_OPEN_VHI 0xf02c00ff ++ ++/* FREQOFF2 */ ++#define R367TER_FREQOFF2 0xf02d ++#define F367TER_FREQ_OFFSET_LOOP_OPEN_HI 0xf02d00ff ++ ++/* FREQOFF3 */ ++#define R367TER_FREQOFF3 0xf02e ++#define F367TER_FREQ_OFFSET_LOOP_OPEN_LO 0xf02e00ff ++ ++/* TIMOFF1 */ ++#define R367TER_TIMOFF1 0xf02f ++#define F367TER_TIM_OFFSET_LOOP_OPEN_HI 0xf02f00ff ++ ++/* TIMOFF2 */ ++#define R367TER_TIMOFF2 0xf030 ++#define F367TER_TIM_OFFSET_LOOP_OPEN_LO 0xf03000ff ++ ++/* EPQ */ ++#define R367TER_EPQ 0xf031 ++#define F367TER_EPQ1 0xf03100ff ++ ++/* EPQAUTO */ ++#define R367TER_EPQAUTO 0xf032 ++#define F367TER_EPQ2 0xf03200ff ++ ++/* SYR_UPDATE */ ++#define R367TER_SYR_UPDATE 0xf033 ++#define F367TER_SYR_PROTV 0xf0330080 ++#define F367TER_SYR_PROTV_GAIN 0xf0330060 ++#define F367TER_SYR_FILTER 0xf0330010 ++#define F367TER_SYR_TRACK_THRES 0xf033000c ++ ++/* CHPFREE */ ++#define R367TER_CHPFREE 0xf034 ++#define F367TER_CHP_FREE 0xf03400ff ++ ++/* PPM_STATE_MAC */ ++#define R367TER_PPM_STATE_MAC 0xf035 ++#define F367TER_PPM_STATE_MACHINE_DECODER 0xf035003f ++ ++/* INR_THRESHOLD */ ++#define R367TER_INR_THRESHOLD 0xf036 ++#define F367TER_INR_THRESH 0xf03600ff ++ ++/* EPQ_TPS_ID_CELL */ ++#define R367TER_EPQ_TPS_ID_CELL 0xf037 ++#define F367TER_ENABLE_LGTH_TO_CF 0xf0370080 ++#define F367TER_DIS_TPS_RSVD 0xf0370040 ++#define F367TER_DIS_BCH 0xf0370020 ++#define F367TER_DIS_ID_CEL 0xf0370010 ++#define F367TER_TPS_ADJUST_SYM 0xf037000f ++ ++/* EPQ_CFG */ ++#define R367TER_EPQ_CFG 0xf038 ++#define F367TER_EPQ_RANGE 0xf0380002 ++#define F367TER_EPQ_SOFT 0xf0380001 ++ ++/* EPQ_STATUS */ ++#define R367TER_EPQ_STATUS 0xf039 ++#define F367TER_SLOPE_INC 0xf03900fc ++#define F367TER_TPS_FIELD 0xf0390003 ++ ++/* AUTORELOCK */ ++#define R367TER_AUTORELOCK 0xf03a ++#define F367TER_BYPASS_BER_TEMPO 0xf03a0080 ++#define F367TER_BER_TEMPO 0xf03a0070 ++#define F367TER_BYPASS_COFDM_TEMPO 0xf03a0008 ++#define F367TER_COFDM_TEMPO 0xf03a0007 ++ ++/* BER_THR_VMSB */ ++#define R367TER_BER_THR_VMSB 0xf03b ++#define F367TER_BER_THRESHOLD_HI 0xf03b00ff ++ ++/* BER_THR_MSB */ ++#define R367TER_BER_THR_MSB 0xf03c ++#define F367TER_BER_THRESHOLD_MID 0xf03c00ff ++ ++/* BER_THR_LSB */ ++#define R367TER_BER_THR_LSB 0xf03d ++#define F367TER_BER_THRESHOLD_LO 0xf03d00ff ++ ++/* CCD */ ++#define R367TER_CCD 0xf03e ++#define F367TER_CCD_DETECTED 0xf03e0080 ++#define F367TER_CCD_RESET 0xf03e0040 ++#define F367TER_CCD_THRESHOLD 0xf03e000f ++ ++/* SPECTR_CFG */ ++#define R367TER_SPECTR_CFG 0xf03f ++#define F367TER_SPECT_CFG 0xf03f0003 ++ ++/* CONSTMU_MSB */ ++#define R367TER_CONSTMU_MSB 0xf040 ++#define F367TER_CONSTMU_FREEZE 0xf0400080 ++#define F367TER_CONSTNU_FORCE_EN 0xf0400040 ++#define F367TER_CONST_MU_MSB 0xf040003f ++ ++/* CONSTMU_LSB */ ++#define R367TER_CONSTMU_LSB 0xf041 ++#define F367TER_CONST_MU_LSB 0xf04100ff ++ ++/* CONSTMU_MAX_MSB */ ++#define R367TER_CONSTMU_MAX_MSB 0xf042 ++#define F367TER_CONST_MU_MAX_MSB 0xf042003f ++ ++/* CONSTMU_MAX_LSB */ ++#define R367TER_CONSTMU_MAX_LSB 0xf043 ++#define F367TER_CONST_MU_MAX_LSB 0xf04300ff ++ ++/* ALPHANOISE */ ++#define R367TER_ALPHANOISE 0xf044 ++#define F367TER_USE_ALLFILTER 0xf0440080 ++#define F367TER_INTER_ON 0xf0440040 ++#define F367TER_ALPHA_NOISE 0xf044001f ++ ++/* MAXGP_MSB */ ++#define R367TER_MAXGP_MSB 0xf045 ++#define F367TER_MUFILTER_LENGTH 0xf04500f0 ++#define F367TER_MAX_GP_MSB 0xf045000f ++ ++/* MAXGP_LSB */ ++#define R367TER_MAXGP_LSB 0xf046 ++#define F367TER_MAX_GP_LSB 0xf04600ff ++ ++/* ALPHAMSB */ ++#define R367TER_ALPHAMSB 0xf047 ++#define F367TER_CHC_DATARATE 0xf04700c0 ++#define F367TER_ALPHA_MSB 0xf047003f ++ ++/* ALPHALSB */ ++#define R367TER_ALPHALSB 0xf048 ++#define F367TER_ALPHA_LSB 0xf04800ff ++ ++/* PILOT_ACCU */ ++#define R367TER_PILOT_ACCU 0xf049 ++#define F367TER_USE_SCAT4ADDAPT 0xf0490080 ++#define F367TER_PILOT_ACC 0xf049001f ++ ++/* PILOTMU_ACCU */ ++#define R367TER_PILOTMU_ACCU 0xf04a ++#define F367TER_DISCARD_BAD_SP 0xf04a0080 ++#define F367TER_DISCARD_BAD_CP 0xf04a0040 ++#define F367TER_PILOT_MU_ACCU 0xf04a001f ++ ++/* FILT_CHANNEL_EST */ ++#define R367TER_FILT_CHANNEL_EST 0xf04b ++#define F367TER_USE_FILT_PILOT 0xf04b0080 ++#define F367TER_FILT_CHANNEL 0xf04b007f ++ ++/* ALPHA_NOPISE_FREQ */ ++#define R367TER_ALPHA_NOPISE_FREQ 0xf04c ++#define F367TER_NOISE_FREQ_FILT 0xf04c0040 ++#define F367TER_ALPHA_NOISE_FREQ 0xf04c003f ++ ++/* RATIO_PILOT */ ++#define R367TER_RATIO_PILOT 0xf04d ++#define F367TER_RATIO_MEAN_SP 0xf04d00f0 ++#define F367TER_RATIO_MEAN_CP 0xf04d000f ++ ++/* CHC_CTL */ ++#define R367TER_CHC_CTL 0xf04e ++#define F367TER_TRACK_EN 0xf04e0080 ++#define F367TER_NOISE_NORM_EN 0xf04e0040 ++#define F367TER_FORCE_CHC_RESET 0xf04e0020 ++#define F367TER_SHORT_TIME 0xf04e0010 ++#define F367TER_FORCE_STATE_EN 0xf04e0008 ++#define F367TER_FORCE_STATE 0xf04e0007 ++ ++/* EPQ_ADJUST */ ++#define R367TER_EPQ_ADJUST 0xf04f ++#define F367TER_ADJUST_SCAT_IND 0xf04f00c0 ++#define F367TER_ONE_SYMBOL 0xf04f0010 ++#define F367TER_EPQ_DECAY 0xf04f000e ++#define F367TER_HOLD_SLOPE 0xf04f0001 ++ ++/* EPQ_THRES */ ++#define R367TER_EPQ_THRES 0xf050 ++#define F367TER_EPQ_THR 0xf05000ff ++ ++/* OMEGA_CTL */ ++#define R367TER_OMEGA_CTL 0xf051 ++#define F367TER_OMEGA_RST 0xf0510080 ++#define F367TER_FREEZE_OMEGA 0xf0510040 ++#define F367TER_OMEGA_SEL 0xf051003f ++ ++/* GP_CTL */ ++#define R367TER_GP_CTL 0xf052 ++#define F367TER_CHC_STATE 0xf05200e0 ++#define F367TER_FREEZE_GP 0xf0520010 ++#define F367TER_GP_SEL 0xf052000f ++ ++/* MUMSB */ ++#define R367TER_MUMSB 0xf053 ++#define F367TER_MU_MSB 0xf053007f ++ ++/* MULSB */ ++#define R367TER_MULSB 0xf054 ++#define F367TER_MU_LSB 0xf05400ff ++ ++/* GPMSB */ ++#define R367TER_GPMSB 0xf055 ++#define F367TER_CSI_THRESHOLD 0xf05500e0 ++#define F367TER_GP_MSB 0xf055000f ++ ++/* GPLSB */ ++#define R367TER_GPLSB 0xf056 ++#define F367TER_GP_LSB 0xf05600ff ++ ++/* OMEGAMSB */ ++#define R367TER_OMEGAMSB 0xf057 ++#define F367TER_OMEGA_MSB 0xf057007f ++ ++/* OMEGALSB */ ++#define R367TER_OMEGALSB 0xf058 ++#define F367TER_OMEGA_LSB 0xf05800ff ++ ++/* SCAT_NB */ ++#define R367TER_SCAT_NB 0xf059 ++#define F367TER_CHC_TEST 0xf05900f8 ++#define F367TER_SCAT_NUMB 0xf0590003 ++ ++/* CHC_DUMMY */ ++#define R367TER_CHC_DUMMY 0xf05a ++#define F367TER_CHC_DUM 0xf05a00ff ++ ++/* INC_CTL */ ++#define R367TER_INC_CTL 0xf05b ++#define F367TER_INC_BYPASS 0xf05b0080 ++#define F367TER_INC_NDEPTH 0xf05b000c ++#define F367TER_INC_MADEPTH 0xf05b0003 ++ ++/* INCTHRES_COR1 */ ++#define R367TER_INCTHRES_COR1 0xf05c ++#define F367TER_INC_THRES_COR1 0xf05c00ff ++ ++/* INCTHRES_COR2 */ ++#define R367TER_INCTHRES_COR2 0xf05d ++#define F367TER_INC_THRES_COR2 0xf05d00ff ++ ++/* INCTHRES_DET1 */ ++#define R367TER_INCTHRES_DET1 0xf05e ++#define F367TER_INC_THRES_DET1 0xf05e003f ++ ++/* INCTHRES_DET2 */ ++#define R367TER_INCTHRES_DET2 0xf05f ++#define F367TER_INC_THRES_DET2 0xf05f003f ++ ++/* IIR_CELLNB */ ++#define R367TER_IIR_CELLNB 0xf060 ++#define F367TER_NRST_IIR 0xf0600080 ++#define F367TER_IIR_CELL_NB 0xf0600007 ++ ++/* IIRCX_COEFF1_MSB */ ++#define R367TER_IIRCX_COEFF1_MSB 0xf061 ++#define F367TER_IIR_CX_COEFF1_MSB 0xf06100ff ++ ++/* IIRCX_COEFF1_LSB */ ++#define R367TER_IIRCX_COEFF1_LSB 0xf062 ++#define F367TER_IIR_CX_COEFF1_LSB 0xf06200ff ++ ++/* IIRCX_COEFF2_MSB */ ++#define R367TER_IIRCX_COEFF2_MSB 0xf063 ++#define F367TER_IIR_CX_COEFF2_MSB 0xf06300ff ++ ++/* IIRCX_COEFF2_LSB */ ++#define R367TER_IIRCX_COEFF2_LSB 0xf064 ++#define F367TER_IIR_CX_COEFF2_LSB 0xf06400ff ++ ++/* IIRCX_COEFF3_MSB */ ++#define R367TER_IIRCX_COEFF3_MSB 0xf065 ++#define F367TER_IIR_CX_COEFF3_MSB 0xf06500ff ++ ++/* IIRCX_COEFF3_LSB */ ++#define R367TER_IIRCX_COEFF3_LSB 0xf066 ++#define F367TER_IIR_CX_COEFF3_LSB 0xf06600ff ++ ++/* IIRCX_COEFF4_MSB */ ++#define R367TER_IIRCX_COEFF4_MSB 0xf067 ++#define F367TER_IIR_CX_COEFF4_MSB 0xf06700ff ++ ++/* IIRCX_COEFF4_LSB */ ++#define R367TER_IIRCX_COEFF4_LSB 0xf068 ++#define F367TER_IIR_CX_COEFF4_LSB 0xf06800ff ++ ++/* IIRCX_COEFF5_MSB */ ++#define R367TER_IIRCX_COEFF5_MSB 0xf069 ++#define F367TER_IIR_CX_COEFF5_MSB 0xf06900ff ++ ++/* IIRCX_COEFF5_LSB */ ++#define R367TER_IIRCX_COEFF5_LSB 0xf06a ++#define F367TER_IIR_CX_COEFF5_LSB 0xf06a00ff ++ ++/* FEPATH_CFG */ ++#define R367TER_FEPATH_CFG 0xf06b ++#define F367TER_DEMUX_SWAP 0xf06b0004 ++#define F367TER_DIGAGC_SWAP 0xf06b0002 ++#define F367TER_LONGPATH_IF 0xf06b0001 ++ ++/* PMC1_FUNC */ ++#define R367TER_PMC1_FUNC 0xf06c ++#define F367TER_SOFT_RSTN 0xf06c0080 ++#define F367TER_PMC1_AVERAGE_TIME 0xf06c0078 ++#define F367TER_PMC1_WAIT_TIME 0xf06c0006 ++#define F367TER_PMC1_2N_SEL 0xf06c0001 ++ ++/* PMC1_FOR */ ++#define R367TER_PMC1_FOR 0xf06d ++#define F367TER_PMC1_FORCE 0xf06d0080 ++#define F367TER_PMC1_FORCE_VALUE 0xf06d007c ++ ++/* PMC2_FUNC */ ++#define R367TER_PMC2_FUNC 0xf06e ++#define F367TER_PMC2_SOFT_STN 0xf06e0080 ++#define F367TER_PMC2_ACCU_TIME 0xf06e0070 ++#define F367TER_PMC2_CMDP_MN 0xf06e0008 ++#define F367TER_PMC2_SWAP 0xf06e0004 ++ ++/* STATUS_ERR_DA */ ++#define R367TER_STATUS_ERR_DA 0xf06f ++#define F367TER_COM_USEGAINTRK 0xf06f0080 ++#define F367TER_COM_AGCLOCK 0xf06f0040 ++#define F367TER_AUT_AGCLOCK 0xf06f0020 ++#define F367TER_MIN_ERR_X_LSB 0xf06f000f ++ ++/* DIG_AGC_R */ ++#define R367TER_DIG_AGC_R 0xf070 ++#define F367TER_COM_SOFT_RSTN 0xf0700080 ++#define F367TER_COM_AGC_ON 0xf0700040 ++#define F367TER_COM_EARLY 0xf0700020 ++#define F367TER_AUT_SOFT_RESETN 0xf0700010 ++#define F367TER_AUT_AGC_ON 0xf0700008 ++#define F367TER_AUT_EARLY 0xf0700004 ++#define F367TER_AUT_ROT_EN 0xf0700002 ++#define F367TER_LOCK_SOFT_RESETN 0xf0700001 ++ ++/* COMAGC_TARMSB */ ++#define R367TER_COMAGC_TARMSB 0xf071 ++#define F367TER_COM_AGC_TARGET_MSB 0xf07100ff ++ ++/* COM_AGC_TAR_ENMODE */ ++#define R367TER_COM_AGC_TAR_ENMODE 0xf072 ++#define F367TER_COM_AGC_TARGET_LSB 0xf07200f0 ++#define F367TER_COM_ENMODE 0xf072000f ++ ++/* COM_AGC_CFG */ ++#define R367TER_COM_AGC_CFG 0xf073 ++#define F367TER_COM_N 0xf07300f8 ++#define F367TER_COM_STABMODE 0xf0730006 ++#define F367TER_ERR_SEL 0xf0730001 ++ ++/* COM_AGC_GAIN1 */ ++#define R367TER_COM_AGC_GAIN1 0xf074 ++#define F367TER_COM_GAIN1aCK 0xf07400f0 ++#define F367TER_COM_GAIN1TRK 0xf074000f ++ ++/* AUT_AGC_TARGETMSB */ ++#define R367TER_AUT_AGC_TARGETMSB 0xf075 ++#define F367TER_AUT_AGC_TARGET_MSB 0xf07500ff ++ ++/* LOCK_DET_MSB */ ++#define R367TER_LOCK_DET_MSB 0xf076 ++#define F367TER_LOCK_DETECT_MSB 0xf07600ff ++ ++/* AGCTAR_LOCK_LSBS */ ++#define R367TER_AGCTAR_LOCK_LSBS 0xf077 ++#define F367TER_AUT_AGC_TARGET_LSB 0xf07700f0 ++#define F367TER_LOCK_DETECT_LSB 0xf077000f ++ ++/* AUT_GAIN_EN */ ++#define R367TER_AUT_GAIN_EN 0xf078 ++#define F367TER_AUT_ENMODE 0xf07800f0 ++#define F367TER_AUT_GAIN2 0xf078000f ++ ++/* AUT_CFG */ ++#define R367TER_AUT_CFG 0xf079 ++#define F367TER_AUT_N 0xf07900f8 ++#define F367TER_INT_CHOICE 0xf0790006 ++#define F367TER_INT_LOAD 0xf0790001 ++ ++/* LOCKN */ ++#define R367TER_LOCKN 0xf07a ++#define F367TER_LOCK_N 0xf07a00f8 ++#define F367TER_SEL_IQNTAR 0xf07a0004 ++#define F367TER_LOCK_DETECT_CHOICE 0xf07a0003 ++ ++/* INT_X_3 */ ++#define R367TER_INT_X_3 0xf07b ++#define F367TER_INT_X3 0xf07b00ff ++ ++/* INT_X_2 */ ++#define R367TER_INT_X_2 0xf07c ++#define F367TER_INT_X2 0xf07c00ff ++ ++/* INT_X_1 */ ++#define R367TER_INT_X_1 0xf07d ++#define F367TER_INT_X1 0xf07d00ff ++ ++/* INT_X_0 */ ++#define R367TER_INT_X_0 0xf07e ++#define F367TER_INT_X0 0xf07e00ff ++ ++/* MIN_ERRX_MSB */ ++#define R367TER_MIN_ERRX_MSB 0xf07f ++#define F367TER_MIN_ERR_X_MSB 0xf07f00ff ++ ++/* COR_CTL */ ++#define R367TER_COR_CTL 0xf080 ++#define F367TER_CORE_ACTIVE 0xf0800020 ++#define F367TER_HOLD 0xf0800010 ++#define F367TER_CORE_STATE_CTL 0xf080000f ++ ++/* COR_STAT */ ++#define R367TER_COR_STAT 0xf081 ++#define F367TER_SCATT_LOCKED 0xf0810080 ++#define F367TER_TPS_LOCKED 0xf0810040 ++#define F367TER_SYR_LOCKED_COR 0xf0810020 ++#define F367TER_AGC_LOCKED_STAT 0xf0810010 ++#define F367TER_CORE_STATE_STAT 0xf081000f ++ ++/* COR_INTEN */ ++#define R367TER_COR_INTEN 0xf082 ++#define F367TER_INTEN 0xf0820080 ++#define F367TER_INTEN_SYR 0xf0820020 ++#define F367TER_INTEN_FFT 0xf0820010 ++#define F367TER_INTEN_AGC 0xf0820008 ++#define F367TER_INTEN_TPS1 0xf0820004 ++#define F367TER_INTEN_TPS2 0xf0820002 ++#define F367TER_INTEN_TPS3 0xf0820001 ++ ++/* COR_INTSTAT */ ++#define R367TER_COR_INTSTAT 0xf083 ++#define F367TER_INTSTAT_SYR 0xf0830020 ++#define F367TER_INTSTAT_FFT 0xf0830010 ++#define F367TER_INTSAT_AGC 0xf0830008 ++#define F367TER_INTSTAT_TPS1 0xf0830004 ++#define F367TER_INTSTAT_TPS2 0xf0830002 ++#define F367TER_INTSTAT_TPS3 0xf0830001 ++ ++/* COR_MODEGUARD */ ++#define R367TER_COR_MODEGUARD 0xf084 ++#define F367TER_FORCE 0xf0840010 ++#define F367TER_MODE 0xf084000c ++#define F367TER_GUARD 0xf0840003 ++ ++/* AGC_CTL */ ++#define R367TER_AGC_CTL 0xf085 ++#define F367TER_AGC_TIMING_FACTOR 0xf08500e0 ++#define F367TER_AGC_LAST 0xf0850010 ++#define F367TER_AGC_GAIN 0xf085000c ++#define F367TER_AGC_NEG 0xf0850002 ++#define F367TER_AGC_SET 0xf0850001 ++ ++/* AGC_MANUAL1 */ ++#define R367TER_AGC_MANUAL1 0xf086 ++#define F367TER_AGC_VAL_LO 0xf08600ff ++ ++/* AGC_MANUAL2 */ ++#define R367TER_AGC_MANUAL2 0xf087 ++#define F367TER_AGC_VAL_HI 0xf087000f ++ ++/* AGC_TARG */ ++#define R367TER_AGC_TARG 0xf088 ++#define F367TER_AGC_TARGET 0xf08800ff ++ ++/* AGC_GAIN1 */ ++#define R367TER_AGC_GAIN1 0xf089 ++#define F367TER_AGC_GAIN_LO 0xf08900ff ++ ++/* AGC_GAIN2 */ ++#define R367TER_AGC_GAIN2 0xf08a ++#define F367TER_AGC_LOCKED_GAIN2 0xf08a0010 ++#define F367TER_AGC_GAIN_HI 0xf08a000f ++ ++/* RESERVED_1 */ ++#define R367TER_RESERVED_1 0xf08b ++#define F367TER_RESERVED1 0xf08b00ff ++ ++/* RESERVED_2 */ ++#define R367TER_RESERVED_2 0xf08c ++#define F367TER_RESERVED2 0xf08c00ff ++ ++/* RESERVED_3 */ ++#define R367TER_RESERVED_3 0xf08d ++#define F367TER_RESERVED3 0xf08d00ff ++ ++/* CAS_CTL */ ++#define R367TER_CAS_CTL 0xf08e ++#define F367TER_CCS_ENABLE 0xf08e0080 ++#define F367TER_ACS_DISABLE 0xf08e0040 ++#define F367TER_DAGC_DIS 0xf08e0020 ++#define F367TER_DAGC_GAIN 0xf08e0018 ++#define F367TER_CCSMU 0xf08e0007 ++ ++/* CAS_FREQ */ ++#define R367TER_CAS_FREQ 0xf08f ++#define F367TER_CCS_FREQ 0xf08f00ff ++ ++/* CAS_DAGCGAIN */ ++#define R367TER_CAS_DAGCGAIN 0xf090 ++#define F367TER_CAS_DAGC_GAIN 0xf09000ff ++ ++/* SYR_CTL */ ++#define R367TER_SYR_CTL 0xf091 ++#define F367TER_SICTH_ENABLE 0xf0910080 ++#define F367TER_LONG_ECHO 0xf0910078 ++#define F367TER_AUTO_LE_EN 0xf0910004 ++#define F367TER_SYR_BYPASS 0xf0910002 ++#define F367TER_SYR_TR_DIS 0xf0910001 ++ ++/* SYR_STAT */ ++#define R367TER_SYR_STAT 0xf092 ++#define F367TER_SYR_LOCKED_STAT 0xf0920010 ++#define F367TER_SYR_MODE 0xf092000c ++#define F367TER_SYR_GUARD 0xf0920003 ++ ++/* SYR_NCO1 */ ++#define R367TER_SYR_NCO1 0xf093 ++#define F367TER_SYR_NCO_LO 0xf09300ff ++ ++/* SYR_NCO2 */ ++#define R367TER_SYR_NCO2 0xf094 ++#define F367TER_SYR_NCO_HI 0xf094003f ++ ++/* SYR_OFFSET1 */ ++#define R367TER_SYR_OFFSET1 0xf095 ++#define F367TER_SYR_OFFSET_LO 0xf09500ff ++ ++/* SYR_OFFSET2 */ ++#define R367TER_SYR_OFFSET2 0xf096 ++#define F367TER_SYR_OFFSET_HI 0xf096003f ++ ++/* FFT_CTL */ ++#define R367TER_FFT_CTL 0xf097 ++#define F367TER_SHIFT_FFT_TRIG 0xf0970018 ++#define F367TER_FFT_TRIGGER 0xf0970004 ++#define F367TER_FFT_MANUAL 0xf0970002 ++#define F367TER_IFFT_MODE 0xf0970001 ++ ++/* SCR_CTL */ ++#define R367TER_SCR_CTL 0xf098 ++#define F367TER_SYRADJDECAY 0xf0980070 ++#define F367TER_SCR_CPEDIS 0xf0980002 ++#define F367TER_SCR_DIS 0xf0980001 ++ ++/* PPM_CTL1 */ ++#define R367TER_PPM_CTL1 0xf099 ++#define F367TER_PPM_MAXFREQ 0xf0990030 ++#define F367TER_PPM_MAXTIM 0xf0990008 ++#define F367TER_PPM_INVSEL 0xf0990004 ++#define F367TER_PPM_SCATDIS 0xf0990002 ++#define F367TER_PPM_BYP 0xf0990001 ++ ++/* TRL_CTL */ ++#define R367TER_TRL_CTL 0xf09a ++#define F367TER_TRL_NOMRATE_LSB 0xf09a0080 ++#define F367TER_TRL_GAIN_FACTOR 0xf09a0078 ++#define F367TER_TRL_LOOPGAIN 0xf09a0007 ++ ++/* TRL_NOMRATE1 */ ++#define R367TER_TRL_NOMRATE1 0xf09b ++#define F367TER_TRL_NOMRATE_LO 0xf09b00ff ++ ++/* TRL_NOMRATE2 */ ++#define R367TER_TRL_NOMRATE2 0xf09c ++#define F367TER_TRL_NOMRATE_HI 0xf09c00ff ++ ++/* TRL_TIME1 */ ++#define R367TER_TRL_TIME1 0xf09d ++#define F367TER_TRL_TOFFSET_LO 0xf09d00ff ++ ++/* TRL_TIME2 */ ++#define R367TER_TRL_TIME2 0xf09e ++#define F367TER_TRL_TOFFSET_HI 0xf09e00ff ++ ++/* CRL_CTL */ ++#define R367TER_CRL_CTL 0xf09f ++#define F367TER_CRL_DIS 0xf09f0080 ++#define F367TER_CRL_GAIN_FACTOR 0xf09f0078 ++#define F367TER_CRL_LOOPGAIN 0xf09f0007 ++ ++/* CRL_FREQ1 */ ++#define R367TER_CRL_FREQ1 0xf0a0 ++#define F367TER_CRL_FOFFSET_LO 0xf0a000ff ++ ++/* CRL_FREQ2 */ ++#define R367TER_CRL_FREQ2 0xf0a1 ++#define F367TER_CRL_FOFFSET_HI 0xf0a100ff ++ ++/* CRL_FREQ3 */ ++#define R367TER_CRL_FREQ3 0xf0a2 ++#define F367TER_CRL_FOFFSET_VHI 0xf0a200ff ++ ++/* TPS_SFRAME_CTL */ ++#define R367TER_TPS_SFRAME_CTL 0xf0a3 ++#define F367TER_TPS_SFRAME_SYNC 0xf0a30001 ++ ++/* CHC_SNR */ ++#define R367TER_CHC_SNR 0xf0a4 ++#define F367TER_CHCSNR 0xf0a400ff ++ ++/* BDI_CTL */ ++#define R367TER_BDI_CTL 0xf0a5 ++#define F367TER_BDI_LPSEL 0xf0a50002 ++#define F367TER_BDI_SERIAL 0xf0a50001 ++ ++/* DMP_CTL */ ++#define R367TER_DMP_CTL 0xf0a6 ++#define F367TER_DMP_SCALING_FACTOR 0xf0a6001e ++#define F367TER_DMP_SDDIS 0xf0a60001 ++ ++/* TPS_RCVD1 */ ++#define R367TER_TPS_RCVD1 0xf0a7 ++#define F367TER_TPS_CHANGE 0xf0a70040 ++#define F367TER_BCH_OK 0xf0a70020 ++#define F367TER_TPS_SYNC 0xf0a70010 ++#define F367TER_TPS_FRAME 0xf0a70003 ++ ++/* TPS_RCVD2 */ ++#define R367TER_TPS_RCVD2 0xf0a8 ++#define F367TER_TPS_HIERMODE 0xf0a80070 ++#define F367TER_TPS_CONST 0xf0a80003 ++ ++/* TPS_RCVD3 */ ++#define R367TER_TPS_RCVD3 0xf0a9 ++#define F367TER_TPS_LPCODE 0xf0a90070 ++#define F367TER_TPS_HPCODE 0xf0a90007 ++ ++/* TPS_RCVD4 */ ++#define R367TER_TPS_RCVD4 0xf0aa ++#define F367TER_TPS_GUARD 0xf0aa0030 ++#define F367TER_TPS_MODE 0xf0aa0003 ++ ++/* TPS_ID_CELL1 */ ++#define R367TER_TPS_ID_CELL1 0xf0ab ++#define F367TER_TPS_ID_CELL_LO 0xf0ab00ff ++ ++/* TPS_ID_CELL2 */ ++#define R367TER_TPS_ID_CELL2 0xf0ac ++#define F367TER_TPS_ID_CELL_HI 0xf0ac00ff ++ ++/* TPS_RCVD5_SET1 */ ++#define R367TER_TPS_RCVD5_SET1 0xf0ad ++#define F367TER_TPS_NA 0xf0ad00fC ++#define F367TER_TPS_SETFRAME 0xf0ad0003 ++ ++/* TPS_SET2 */ ++#define R367TER_TPS_SET2 0xf0ae ++#define F367TER_TPS_SETHIERMODE 0xf0ae0070 ++#define F367TER_TPS_SETCONST 0xf0ae0003 ++ ++/* TPS_SET3 */ ++#define R367TER_TPS_SET3 0xf0af ++#define F367TER_TPS_SETLPCODE 0xf0af0070 ++#define F367TER_TPS_SETHPCODE 0xf0af0007 ++ ++/* TPS_CTL */ ++#define R367TER_TPS_CTL 0xf0b0 ++#define F367TER_TPS_IMM 0xf0b00004 ++#define F367TER_TPS_BCHDIS 0xf0b00002 ++#define F367TER_TPS_UPDDIS 0xf0b00001 ++ ++/* CTL_FFTOSNUM */ ++#define R367TER_CTL_FFTOSNUM 0xf0b1 ++#define F367TER_SYMBOL_NUMBER 0xf0b1007f ++ ++/* TESTSELECT */ ++#define R367TER_TESTSELECT 0xf0b2 ++#define F367TER_TEST_SELECT 0xf0b2001f ++ ++/* MSC_REV */ ++#define R367TER_MSC_REV 0xf0b3 ++#define F367TER_REV_NUMBER 0xf0b300ff ++ ++/* PIR_CTL */ ++#define R367TER_PIR_CTL 0xf0b4 ++#define F367TER_FREEZE 0xf0b40001 ++ ++/* SNR_CARRIER1 */ ++#define R367TER_SNR_CARRIER1 0xf0b5 ++#define F367TER_SNR_CARRIER_LO 0xf0b500ff ++ ++/* SNR_CARRIER2 */ ++#define R367TER_SNR_CARRIER2 0xf0b6 ++#define F367TER_MEAN 0xf0b600c0 ++#define F367TER_SNR_CARRIER_HI 0xf0b6001f ++ ++/* PPM_CPAMP */ ++#define R367TER_PPM_CPAMP 0xf0b7 ++#define F367TER_PPM_CPC 0xf0b700ff ++ ++/* TSM_AP0 */ ++#define R367TER_TSM_AP0 0xf0b8 ++#define F367TER_ADDRESS_BYTE_0 0xf0b800ff ++ ++/* TSM_AP1 */ ++#define R367TER_TSM_AP1 0xf0b9 ++#define F367TER_ADDRESS_BYTE_1 0xf0b900ff ++ ++/* TSM_AP2 */ ++#define R367TER_TSM_AP2 0xf0bA ++#define F367TER_DATA_BYTE_0 0xf0ba00ff ++ ++/* TSM_AP3 */ ++#define R367TER_TSM_AP3 0xf0bB ++#define F367TER_DATA_BYTE_1 0xf0bb00ff ++ ++/* TSM_AP4 */ ++#define R367TER_TSM_AP4 0xf0bC ++#define F367TER_DATA_BYTE_2 0xf0bc00ff ++ ++/* TSM_AP5 */ ++#define R367TER_TSM_AP5 0xf0bD ++#define F367TER_DATA_BYTE_3 0xf0bd00ff ++ ++/* TSM_AP6 */ ++#define R367TER_TSM_AP6 0xf0bE ++#define F367TER_TSM_AP_6 0xf0be00ff ++ ++/* TSM_AP7 */ ++#define R367TER_TSM_AP7 0xf0bF ++#define F367TER_MEM_SELECT_BYTE 0xf0bf00ff ++ ++/* TSTRES */ ++#define R367TER_TSTRES 0xf0c0 ++#define F367TER_FRES_DISPLAY 0xf0c00080 ++#define F367TER_FRES_FIFO_AD 0xf0c00020 ++#define F367TER_FRESRS 0xf0c00010 ++#define F367TER_FRESACS 0xf0c00008 ++#define F367TER_FRESFEC 0xf0c00004 ++#define F367TER_FRES_PRIF 0xf0c00002 ++#define F367TER_FRESCORE 0xf0c00001 ++ ++/* ANACTRL */ ++#define R367TER_ANACTRL 0xf0c1 ++#define F367TER_BYPASS_XTAL 0xf0c10040 ++#define F367TER_BYPASS_PLLXN 0xf0c1000c ++#define F367TER_DIS_PAD_OSC 0xf0c10002 ++#define F367TER_STDBY_PLLXN 0xf0c10001 ++ ++/* TSTBUS */ ++#define R367TER_TSTBUS 0xf0c2 ++#define F367TER_TS_BYTE_CLK_INV 0xf0c20080 ++#define F367TER_CFG_IP 0xf0c20070 ++#define F367TER_CFG_TST 0xf0c2000f ++ ++/* TSTRATE */ ++#define R367TER_TSTRATE 0xf0c6 ++#define F367TER_FORCEPHA 0xf0c60080 ++#define F367TER_FNEWPHA 0xf0c60010 ++#define F367TER_FROT90 0xf0c60008 ++#define F367TER_FR 0xf0c60007 ++ ++/* CONSTMODE */ ++#define R367TER_CONSTMODE 0xf0cb ++#define F367TER_TST_PRIF 0xf0cb00e0 ++#define F367TER_CAR_TYPE 0xf0cb0018 ++#define F367TER_CONST_MODE 0xf0cb0003 ++ ++/* CONSTCARR1 */ ++#define R367TER_CONSTCARR1 0xf0cc ++#define F367TER_CONST_CARR_LO 0xf0cc00ff ++ ++/* CONSTCARR2 */ ++#define R367TER_CONSTCARR2 0xf0cd ++#define F367TER_CONST_CARR_HI 0xf0cd001f ++ ++/* ICONSTEL */ ++#define R367TER_ICONSTEL 0xf0ce ++#define F367TER_PICONSTEL 0xf0ce00ff ++ ++/* QCONSTEL */ ++#define R367TER_QCONSTEL 0xf0cf ++#define F367TER_PQCONSTEL 0xf0cf00ff ++ ++/* TSTBISTRES0 */ ++#define R367TER_TSTBISTRES0 0xf0d0 ++#define F367TER_BEND_PPM 0xf0d00080 ++#define F367TER_BBAD_PPM 0xf0d00040 ++#define F367TER_BEND_FFTW 0xf0d00020 ++#define F367TER_BBAD_FFTW 0xf0d00010 ++#define F367TER_BEND_FFT_BUF 0xf0d00008 ++#define F367TER_BBAD_FFT_BUF 0xf0d00004 ++#define F367TER_BEND_SYR 0xf0d00002 ++#define F367TER_BBAD_SYR 0xf0d00001 ++ ++/* TSTBISTRES1 */ ++#define R367TER_TSTBISTRES1 0xf0d1 ++#define F367TER_BEND_CHC_CP 0xf0d10080 ++#define F367TER_BBAD_CHC_CP 0xf0d10040 ++#define F367TER_BEND_CHCI 0xf0d10020 ++#define F367TER_BBAD_CHCI 0xf0d10010 ++#define F367TER_BEND_BDI 0xf0d10008 ++#define F367TER_BBAD_BDI 0xf0d10004 ++#define F367TER_BEND_SDI 0xf0d10002 ++#define F367TER_BBAD_SDI 0xf0d10001 ++ ++/* TSTBISTRES2 */ ++#define R367TER_TSTBISTRES2 0xf0d2 ++#define F367TER_BEND_CHC_INC 0xf0d20080 ++#define F367TER_BBAD_CHC_INC 0xf0d20040 ++#define F367TER_BEND_CHC_SPP 0xf0d20020 ++#define F367TER_BBAD_CHC_SPP 0xf0d20010 ++#define F367TER_BEND_CHC_CPP 0xf0d20008 ++#define F367TER_BBAD_CHC_CPP 0xf0d20004 ++#define F367TER_BEND_CHC_SP 0xf0d20002 ++#define F367TER_BBAD_CHC_SP 0xf0d20001 ++ ++/* TSTBISTRES3 */ ++#define R367TER_TSTBISTRES3 0xf0d3 ++#define F367TER_BEND_QAM 0xf0d30080 ++#define F367TER_BBAD_QAM 0xf0d30040 ++#define F367TER_BEND_SFEC_VIT 0xf0d30020 ++#define F367TER_BBAD_SFEC_VIT 0xf0d30010 ++#define F367TER_BEND_SFEC_DLINE 0xf0d30008 ++#define F367TER_BBAD_SFEC_DLINE 0xf0d30004 ++#define F367TER_BEND_SFEC_HW 0xf0d30002 ++#define F367TER_BBAD_SFEC_HW 0xf0d30001 ++ ++/* RF_AGC1 */ ++#define R367TER_RF_AGC1 0xf0d4 ++#define F367TER_RF_AGC1_LEVEL_HI 0xf0d400ff ++ ++/* RF_AGC2 */ ++#define R367TER_RF_AGC2 0xf0d5 ++#define F367TER_REF_ADGP 0xf0d50080 ++#define F367TER_STDBY_ADCGP 0xf0d50020 ++#define F367TER_CHANNEL_SEL 0xf0d5001c ++#define F367TER_RF_AGC1_LEVEL_LO 0xf0d50003 ++ ++/* ANADIGCTRL */ ++#define R367TER_ANADIGCTRL 0xf0d7 ++#define F367TER_SEL_CLKDEM 0xf0d70020 ++#define F367TER_EN_BUFFER_Q 0xf0d70010 ++#define F367TER_EN_BUFFER_I 0xf0d70008 ++#define F367TER_ADC_RIS_EGDE 0xf0d70004 ++#define F367TER_SGN_ADC 0xf0d70002 ++#define F367TER_SEL_AD12_SYNC 0xf0d70001 ++ ++/* PLLMDIV */ ++#define R367TER_PLLMDIV 0xf0d8 ++#define F367TER_PLL_MDIV 0xf0d800ff ++ ++/* PLLNDIV */ ++#define R367TER_PLLNDIV 0xf0d9 ++#define F367TER_PLL_NDIV 0xf0d900ff ++ ++/* PLLSETUP */ ++#define R367TER_PLLSETUP 0xf0dA ++#define F367TER_PLL_PDIV 0xf0da0070 ++#define F367TER_PLL_KDIV 0xf0da000f ++ ++/* DUAL_AD12 */ ++#define R367TER_DUAL_AD12 0xf0dB ++#define F367TER_FS20M 0xf0db0020 ++#define F367TER_FS50M 0xf0db0010 ++#define F367TER_INMODe0 0xf0db0008 ++#define F367TER_POFFQ 0xf0db0004 ++#define F367TER_POFFI 0xf0db0002 ++#define F367TER_INMODE1 0xf0db0001 ++ ++/* TSTBIST */ ++#define R367TER_TSTBIST 0xf0dC ++#define F367TER_TST_BYP_CLK 0xf0dc0080 ++#define F367TER_TST_GCLKENA_STD 0xf0dc0040 ++#define F367TER_TST_GCLKENA 0xf0dc0020 ++#define F367TER_TST_MEMBIST 0xf0dc001f ++ ++/* PAD_COMP_CTRL */ ++#define R367TER_PAD_COMP_CTRL 0xf0dD ++#define F367TER_COMPTQ 0xf0dd0010 ++#define F367TER_COMPEN 0xf0dd0008 ++#define F367TER_FREEZE2 0xf0dd0004 ++#define F367TER_SLEEP_INHBT 0xf0dd0002 ++#define F367TER_CHIP_SLEEP 0xf0dd0001 ++ ++/* PAD_COMP_WR */ ++#define R367TER_PAD_COMP_WR 0xf0de ++#define F367TER_WR_ASRC 0xf0de007f ++ ++/* PAD_COMP_RD */ ++#define R367TER_PAD_COMP_RD 0xf0df ++#define F367TER_COMPOK 0xf0df0080 ++#define F367TER_RD_ASRC 0xf0df007f ++ ++/* SYR_TARGET_FFTADJT_MSB */ ++#define R367TER_SYR_TARGET_FFTADJT_MSB 0xf100 ++#define F367TER_SYR_START 0xf1000080 ++#define F367TER_SYR_TARGET_FFTADJ_HI 0xf100000f ++ ++/* SYR_TARGET_FFTADJT_LSB */ ++#define R367TER_SYR_TARGET_FFTADJT_LSB 0xf101 ++#define F367TER_SYR_TARGET_FFTADJ_LO 0xf10100ff ++ ++/* SYR_TARGET_CHCADJT_MSB */ ++#define R367TER_SYR_TARGET_CHCADJT_MSB 0xf102 ++#define F367TER_SYR_TARGET_CHCADJ_HI 0xf102000f ++ ++/* SYR_TARGET_CHCADJT_LSB */ ++#define R367TER_SYR_TARGET_CHCADJT_LSB 0xf103 ++#define F367TER_SYR_TARGET_CHCADJ_LO 0xf10300ff ++ ++/* SYR_FLAG */ ++#define R367TER_SYR_FLAG 0xf104 ++#define F367TER_TRIG_FLG1 0xf1040080 ++#define F367TER_TRIG_FLG0 0xf1040040 ++#define F367TER_FFT_FLG1 0xf1040008 ++#define F367TER_FFT_FLG0 0xf1040004 ++#define F367TER_CHC_FLG1 0xf1040002 ++#define F367TER_CHC_FLG0 0xf1040001 ++ ++/* CRL_TARGET1 */ ++#define R367TER_CRL_TARGET1 0xf105 ++#define F367TER_CRL_START 0xf1050080 ++#define F367TER_CRL_TARGET_VHI 0xf105000f ++ ++/* CRL_TARGET2 */ ++#define R367TER_CRL_TARGET2 0xf106 ++#define F367TER_CRL_TARGET_HI 0xf10600ff ++ ++/* CRL_TARGET3 */ ++#define R367TER_CRL_TARGET3 0xf107 ++#define F367TER_CRL_TARGET_LO 0xf10700ff ++ ++/* CRL_TARGET4 */ ++#define R367TER_CRL_TARGET4 0xf108 ++#define F367TER_CRL_TARGET_VLO 0xf10800ff ++ ++/* CRL_FLAG */ ++#define R367TER_CRL_FLAG 0xf109 ++#define F367TER_CRL_FLAG1 0xf1090002 ++#define F367TER_CRL_FLAG0 0xf1090001 ++ ++/* TRL_TARGET1 */ ++#define R367TER_TRL_TARGET1 0xf10a ++#define F367TER_TRL_TARGET_HI 0xf10a00ff ++ ++/* TRL_TARGET2 */ ++#define R367TER_TRL_TARGET2 0xf10b ++#define F367TER_TRL_TARGET_LO 0xf10b00ff ++ ++/* TRL_CHC */ ++#define R367TER_TRL_CHC 0xf10c ++#define F367TER_TRL_START 0xf10c0080 ++#define F367TER_CHC_START 0xf10c0040 ++#define F367TER_TRL_FLAG1 0xf10c0002 ++#define F367TER_TRL_FLAG0 0xf10c0001 ++ ++/* CHC_SNR_TARG */ ++#define R367TER_CHC_SNR_TARG 0xf10d ++#define F367TER_CHC_SNR_TARGET 0xf10d00ff ++ ++/* TOP_TRACK */ ++#define R367TER_TOP_TRACK 0xf10e ++#define F367TER_TOP_START 0xf10e0080 ++#define F367TER_FIRST_FLAG 0xf10e0070 ++#define F367TER_TOP_FLAG1 0xf10e0008 ++#define F367TER_TOP_FLAG0 0xf10e0004 ++#define F367TER_CHC_FLAG1 0xf10e0002 ++#define F367TER_CHC_FLAG0 0xf10e0001 ++ ++/* TRACKER_FREE1 */ ++#define R367TER_TRACKER_FREE1 0xf10f ++#define F367TER_TRACKER_FREE_1 0xf10f00ff ++ ++/* ERROR_CRL1 */ ++#define R367TER_ERROR_CRL1 0xf110 ++#define F367TER_ERROR_CRL_VHI 0xf11000ff ++ ++/* ERROR_CRL2 */ ++#define R367TER_ERROR_CRL2 0xf111 ++#define F367TER_ERROR_CRL_HI 0xf11100ff ++ ++/* ERROR_CRL3 */ ++#define R367TER_ERROR_CRL3 0xf112 ++#define F367TER_ERROR_CRL_LOI 0xf11200ff ++ ++/* ERROR_CRL4 */ ++#define R367TER_ERROR_CRL4 0xf113 ++#define F367TER_ERROR_CRL_VLO 0xf11300ff ++ ++/* DEC_NCO1 */ ++#define R367TER_DEC_NCO1 0xf114 ++#define F367TER_DEC_NCO_VHI 0xf11400ff ++ ++/* DEC_NCO2 */ ++#define R367TER_DEC_NCO2 0xf115 ++#define F367TER_DEC_NCO_HI 0xf11500ff ++ ++/* DEC_NCO3 */ ++#define R367TER_DEC_NCO3 0xf116 ++#define F367TER_DEC_NCO_LO 0xf11600ff ++ ++/* SNR */ ++#define R367TER_SNR 0xf117 ++#define F367TER_SNRATIO 0xf11700ff ++ ++/* SYR_FFTADJ1 */ ++#define R367TER_SYR_FFTADJ1 0xf118 ++#define F367TER_SYR_FFTADJ_HI 0xf11800ff ++ ++/* SYR_FFTADJ2 */ ++#define R367TER_SYR_FFTADJ2 0xf119 ++#define F367TER_SYR_FFTADJ_LO 0xf11900ff ++ ++/* SYR_CHCADJ1 */ ++#define R367TER_SYR_CHCADJ1 0xf11a ++#define F367TER_SYR_CHCADJ_HI 0xf11a00ff ++ ++/* SYR_CHCADJ2 */ ++#define R367TER_SYR_CHCADJ2 0xf11b ++#define F367TER_SYR_CHCADJ_LO 0xf11b00ff ++ ++/* SYR_OFF */ ++#define R367TER_SYR_OFF 0xf11c ++#define F367TER_SYR_OFFSET 0xf11c00ff ++ ++/* PPM_OFFSET1 */ ++#define R367TER_PPM_OFFSET1 0xf11d ++#define F367TER_PPM_OFFSET_HI 0xf11d00ff ++ ++/* PPM_OFFSET2 */ ++#define R367TER_PPM_OFFSET2 0xf11e ++#define F367TER_PPM_OFFSET_LO 0xf11e00ff ++ ++/* TRACKER_FREE2 */ ++#define R367TER_TRACKER_FREE2 0xf11f ++#define F367TER_TRACKER_FREE_2 0xf11f00ff ++ ++/* DEBG_LT10 */ ++#define R367TER_DEBG_LT10 0xf120 ++#define F367TER_DEBUG_LT10 0xf12000ff ++ ++/* DEBG_LT11 */ ++#define R367TER_DEBG_LT11 0xf121 ++#define F367TER_DEBUG_LT11 0xf12100ff ++ ++/* DEBG_LT12 */ ++#define R367TER_DEBG_LT12 0xf122 ++#define F367TER_DEBUG_LT12 0xf12200ff ++ ++/* DEBG_LT13 */ ++#define R367TER_DEBG_LT13 0xf123 ++#define F367TER_DEBUG_LT13 0xf12300ff ++ ++/* DEBG_LT14 */ ++#define R367TER_DEBG_LT14 0xf124 ++#define F367TER_DEBUG_LT14 0xf12400ff ++ ++/* DEBG_LT15 */ ++#define R367TER_DEBG_LT15 0xf125 ++#define F367TER_DEBUG_LT15 0xf12500ff ++ ++/* DEBG_LT16 */ ++#define R367TER_DEBG_LT16 0xf126 ++#define F367TER_DEBUG_LT16 0xf12600ff ++ ++/* DEBG_LT17 */ ++#define R367TER_DEBG_LT17 0xf127 ++#define F367TER_DEBUG_LT17 0xf12700ff ++ ++/* DEBG_LT18 */ ++#define R367TER_DEBG_LT18 0xf128 ++#define F367TER_DEBUG_LT18 0xf12800ff ++ ++/* DEBG_LT19 */ ++#define R367TER_DEBG_LT19 0xf129 ++#define F367TER_DEBUG_LT19 0xf12900ff ++ ++/* DEBG_LT1a */ ++#define R367TER_DEBG_LT1A 0xf12a ++#define F367TER_DEBUG_LT1A 0xf12a00ff ++ ++/* DEBG_LT1b */ ++#define R367TER_DEBG_LT1B 0xf12b ++#define F367TER_DEBUG_LT1B 0xf12b00ff ++ ++/* DEBG_LT1c */ ++#define R367TER_DEBG_LT1C 0xf12c ++#define F367TER_DEBUG_LT1C 0xf12c00ff ++ ++/* DEBG_LT1D */ ++#define R367TER_DEBG_LT1D 0xf12d ++#define F367TER_DEBUG_LT1D 0xf12d00ff ++ ++/* DEBG_LT1E */ ++#define R367TER_DEBG_LT1E 0xf12e ++#define F367TER_DEBUG_LT1E 0xf12e00ff ++ ++/* DEBG_LT1F */ ++#define R367TER_DEBG_LT1F 0xf12f ++#define F367TER_DEBUG_LT1F 0xf12f00ff ++ ++/* RCCFGH */ ++#define R367TER_RCCFGH 0xf200 ++#define F367TER_TSRCFIFO_DVBCI 0xf2000080 ++#define F367TER_TSRCFIFO_SERIAL 0xf2000040 ++#define F367TER_TSRCFIFO_DISABLE 0xf2000020 ++#define F367TER_TSFIFO_2TORC 0xf2000010 ++#define F367TER_TSRCFIFO_HSGNLOUT 0xf2000008 ++#define F367TER_TSRCFIFO_ERRMODE 0xf2000006 ++#define F367TER_RCCFGH_0 0xf2000001 ++ ++/* RCCFGM */ ++#define R367TER_RCCFGM 0xf201 ++#define F367TER_TSRCFIFO_MANSPEED 0xf20100c0 ++#define F367TER_TSRCFIFO_PERMDATA 0xf2010020 ++#define F367TER_TSRCFIFO_NONEWSGNL 0xf2010010 ++#define F367TER_RCBYTE_OVERSAMPLING 0xf201000e ++#define F367TER_TSRCFIFO_INVDATA 0xf2010001 ++ ++/* RCCFGL */ ++#define R367TER_RCCFGL 0xf202 ++#define F367TER_TSRCFIFO_BCLKDEL1cK 0xf20200c0 ++#define F367TER_RCCFGL_5 0xf2020020 ++#define F367TER_TSRCFIFO_DUTY50 0xf2020010 ++#define F367TER_TSRCFIFO_NSGNL2dATA 0xf2020008 ++#define F367TER_TSRCFIFO_DISSERMUX 0xf2020004 ++#define F367TER_RCCFGL_1 0xf2020002 ++#define F367TER_TSRCFIFO_STOPCKDIS 0xf2020001 ++ ++/* RCINSDELH */ ++#define R367TER_RCINSDELH 0xf203 ++#define F367TER_TSRCDEL_SYNCBYTE 0xf2030080 ++#define F367TER_TSRCDEL_XXHEADER 0xf2030040 ++#define F367TER_TSRCDEL_BBHEADER 0xf2030020 ++#define F367TER_TSRCDEL_DATAFIELD 0xf2030010 ++#define F367TER_TSRCINSDEL_ISCR 0xf2030008 ++#define F367TER_TSRCINSDEL_NPD 0xf2030004 ++#define F367TER_TSRCINSDEL_RSPARITY 0xf2030002 ++#define F367TER_TSRCINSDEL_CRC8 0xf2030001 ++ ++/* RCINSDELM */ ++#define R367TER_RCINSDELM 0xf204 ++#define F367TER_TSRCINS_BBPADDING 0xf2040080 ++#define F367TER_TSRCINS_BCHFEC 0xf2040040 ++#define F367TER_TSRCINS_LDPCFEC 0xf2040020 ++#define F367TER_TSRCINS_EMODCOD 0xf2040010 ++#define F367TER_TSRCINS_TOKEN 0xf2040008 ++#define F367TER_TSRCINS_XXXERR 0xf2040004 ++#define F367TER_TSRCINS_MATYPE 0xf2040002 ++#define F367TER_TSRCINS_UPL 0xf2040001 ++ ++/* RCINSDELL */ ++#define R367TER_RCINSDELL 0xf205 ++#define F367TER_TSRCINS_DFL 0xf2050080 ++#define F367TER_TSRCINS_SYNCD 0xf2050040 ++#define F367TER_TSRCINS_BLOCLEN 0xf2050020 ++#define F367TER_TSRCINS_SIGPCOUNT 0xf2050010 ++#define F367TER_TSRCINS_FIFO 0xf2050008 ++#define F367TER_TSRCINS_REALPACK 0xf2050004 ++#define F367TER_TSRCINS_TSCONFIG 0xf2050002 ++#define F367TER_TSRCINS_LATENCY 0xf2050001 ++ ++/* RCSTATUS */ ++#define R367TER_RCSTATUS 0xf206 ++#define F367TER_TSRCFIFO_LINEOK 0xf2060080 ++#define F367TER_TSRCFIFO_ERROR 0xf2060040 ++#define F367TER_TSRCFIFO_DATA7 0xf2060020 ++#define F367TER_RCSTATUS_4 0xf2060010 ++#define F367TER_TSRCFIFO_DEMODSEL 0xf2060008 ++#define F367TER_TSRC1FIFOSPEED_STORE 0xf2060004 ++#define F367TER_RCSTATUS_1 0xf2060002 ++#define F367TER_TSRCSERIAL_IMPOSSIBLE 0xf2060001 ++ ++/* RCSPEED */ ++#define R367TER_RCSPEED 0xf207 ++#define F367TER_TSRCFIFO_OUTSPEED 0xf20700ff ++ ++/* RCDEBUGM */ ++#define R367TER_RCDEBUGM 0xf208 ++#define F367TER_SD_UNSYNC 0xf2080080 ++#define F367TER_ULFLOCK_DETECTM 0xf2080040 ++#define F367TER_SUL_SELECTOS 0xf2080020 ++#define F367TER_DILUL_NOSCRBLE 0xf2080010 ++#define F367TER_NUL_SCRB 0xf2080008 ++#define F367TER_UL_SCRB 0xf2080004 ++#define F367TER_SCRAULBAD 0xf2080002 ++#define F367TER_SCRAUL_UNSYNC 0xf2080001 ++ ++/* RCDEBUGL */ ++#define R367TER_RCDEBUGL 0xf209 ++#define F367TER_RS_ERR 0xf2090080 ++#define F367TER_LLFLOCK_DETECTM 0xf2090040 ++#define F367TER_NOT_SUL_SELECTOS 0xf2090020 ++#define F367TER_DILLL_NOSCRBLE 0xf2090010 ++#define F367TER_NLL_SCRB 0xf2090008 ++#define F367TER_LL_SCRB 0xf2090004 ++#define F367TER_SCRALLBAD 0xf2090002 ++#define F367TER_SCRALL_UNSYNC 0xf2090001 ++ ++/* RCOBSCFG */ ++#define R367TER_RCOBSCFG 0xf20a ++#define F367TER_TSRCFIFO_OBSCFG 0xf20a00ff ++ ++/* RCOBSM */ ++#define R367TER_RCOBSM 0xf20b ++#define F367TER_TSRCFIFO_OBSDATA_HI 0xf20b00ff ++ ++/* RCOBSL */ ++#define R367TER_RCOBSL 0xf20c ++#define F367TER_TSRCFIFO_OBSDATA_LO 0xf20c00ff ++ ++/* RCFECSPY */ ++#define R367TER_RCFECSPY 0xf210 ++#define F367TER_SPYRC_ENABLE 0xf2100080 ++#define F367TER_RCNO_SYNCBYTE 0xf2100040 ++#define F367TER_RCSERIAL_MODE 0xf2100020 ++#define F367TER_RCUNUSUAL_PACKET 0xf2100010 ++#define F367TER_BERRCMETER_DATAMODE 0xf210000c ++#define F367TER_BERRCMETER_LMODE 0xf2100002 ++#define F367TER_BERRCMETER_RESET 0xf2100001 ++ ++/* RCFSPYCFG */ ++#define R367TER_RCFSPYCFG 0xf211 ++#define F367TER_FECSPYRC_INPUT 0xf21100c0 ++#define F367TER_RCRST_ON_ERROR 0xf2110020 ++#define F367TER_RCONE_SHOT 0xf2110010 ++#define F367TER_RCI2C_MODE 0xf211000c ++#define F367TER_SPYRC_HSTERESIS 0xf2110003 ++ ++/* RCFSPYDATA */ ++#define R367TER_RCFSPYDATA 0xf212 ++#define F367TER_SPYRC_STUFFING 0xf2120080 ++#define F367TER_RCNOERR_PKTJITTER 0xf2120040 ++#define F367TER_SPYRC_CNULLPKT 0xf2120020 ++#define F367TER_SPYRC_OUTDATA_MODE 0xf212001f ++ ++/* RCFSPYOUT */ ++#define R367TER_RCFSPYOUT 0xf213 ++#define F367TER_FSPYRC_DIRECT 0xf2130080 ++#define F367TER_RCFSPYOUT_6 0xf2130040 ++#define F367TER_SPYRC_OUTDATA_BUS 0xf2130038 ++#define F367TER_RCSTUFF_MODE 0xf2130007 ++ ++/* RCFSTATUS */ ++#define R367TER_RCFSTATUS 0xf214 ++#define F367TER_SPYRC_ENDSIM 0xf2140080 ++#define F367TER_RCVALID_SIM 0xf2140040 ++#define F367TER_RCFOUND_SIGNAL 0xf2140020 ++#define F367TER_RCDSS_SYNCBYTE 0xf2140010 ++#define F367TER_RCRESULT_STATE 0xf214000f ++ ++/* RCFGOODPACK */ ++#define R367TER_RCFGOODPACK 0xf215 ++#define F367TER_RCGOOD_PACKET 0xf21500ff ++ ++/* RCFPACKCNT */ ++#define R367TER_RCFPACKCNT 0xf216 ++#define F367TER_RCPACKET_COUNTER 0xf21600ff ++ ++/* RCFSPYMISC */ ++#define R367TER_RCFSPYMISC 0xf217 ++#define F367TER_RCLABEL_COUNTER 0xf21700ff ++ ++/* RCFBERCPT4 */ ++#define R367TER_RCFBERCPT4 0xf218 ++#define F367TER_FBERRCMETER_CPT_MMMMSB 0xf21800ff ++ ++/* RCFBERCPT3 */ ++#define R367TER_RCFBERCPT3 0xf219 ++#define F367TER_FBERRCMETER_CPT_MMMSB 0xf21900ff ++ ++/* RCFBERCPT2 */ ++#define R367TER_RCFBERCPT2 0xf21a ++#define F367TER_FBERRCMETER_CPT_MMSB 0xf21a00ff ++ ++/* RCFBERCPT1 */ ++#define R367TER_RCFBERCPT1 0xf21b ++#define F367TER_FBERRCMETER_CPT_MSB 0xf21b00ff ++ ++/* RCFBERCPT0 */ ++#define R367TER_RCFBERCPT0 0xf21c ++#define F367TER_FBERRCMETER_CPT_LSB 0xf21c00ff ++ ++/* RCFBERERR2 */ ++#define R367TER_RCFBERERR2 0xf21d ++#define F367TER_FBERRCMETER_ERR_HI 0xf21d00ff ++ ++/* RCFBERERR1 */ ++#define R367TER_RCFBERERR1 0xf21e ++#define F367TER_FBERRCMETER_ERR 0xf21e00ff ++ ++/* RCFBERERR0 */ ++#define R367TER_RCFBERERR0 0xf21f ++#define F367TER_FBERRCMETER_ERR_LO 0xf21f00ff ++ ++/* RCFSTATESM */ ++#define R367TER_RCFSTATESM 0xf220 ++#define F367TER_RCRSTATE_F 0xf2200080 ++#define F367TER_RCRSTATE_E 0xf2200040 ++#define F367TER_RCRSTATE_D 0xf2200020 ++#define F367TER_RCRSTATE_C 0xf2200010 ++#define F367TER_RCRSTATE_B 0xf2200008 ++#define F367TER_RCRSTATE_A 0xf2200004 ++#define F367TER_RCRSTATE_9 0xf2200002 ++#define F367TER_RCRSTATE_8 0xf2200001 ++ ++/* RCFSTATESL */ ++#define R367TER_RCFSTATESL 0xf221 ++#define F367TER_RCRSTATE_7 0xf2210080 ++#define F367TER_RCRSTATE_6 0xf2210040 ++#define F367TER_RCRSTATE_5 0xf2210020 ++#define F367TER_RCRSTATE_4 0xf2210010 ++#define F367TER_RCRSTATE_3 0xf2210008 ++#define F367TER_RCRSTATE_2 0xf2210004 ++#define F367TER_RCRSTATE_1 0xf2210002 ++#define F367TER_RCRSTATE_0 0xf2210001 ++ ++/* RCFSPYBER */ ++#define R367TER_RCFSPYBER 0xf222 ++#define F367TER_RCFSPYBER_7 0xf2220080 ++#define F367TER_SPYRCOBS_XORREAD 0xf2220040 ++#define F367TER_FSPYRCBER_OBSMODE 0xf2220020 ++#define F367TER_FSPYRCBER_SYNCBYT 0xf2220010 ++#define F367TER_FSPYRCBER_UNSYNC 0xf2220008 ++#define F367TER_FSPYRCBER_CTIME 0xf2220007 ++ ++/* RCFSPYDISTM */ ++#define R367TER_RCFSPYDISTM 0xf223 ++#define F367TER_RCPKTTIME_DISTANCE_HI 0xf22300ff ++ ++/* RCFSPYDISTL */ ++#define R367TER_RCFSPYDISTL 0xf224 ++#define F367TER_RCPKTTIME_DISTANCE_LO 0xf22400ff ++ ++/* RCFSPYOBS7 */ ++#define R367TER_RCFSPYOBS7 0xf228 ++#define F367TER_RCSPYOBS_SPYFAIL 0xf2280080 ++#define F367TER_RCSPYOBS_SPYFAIL1 0xf2280040 ++#define F367TER_RCSPYOBS_ERROR 0xf2280020 ++#define F367TER_RCSPYOBS_STROUT 0xf2280010 ++#define F367TER_RCSPYOBS_RESULTSTATE1 0xf228000f ++ ++/* RCFSPYOBS6 */ ++#define R367TER_RCFSPYOBS6 0xf229 ++#define F367TER_RCSPYOBS_RESULTSTATe0 0xf22900f0 ++#define F367TER_RCSPYOBS_RESULTSTATEM1 0xf229000f ++ ++/* RCFSPYOBS5 */ ++#define R367TER_RCFSPYOBS5 0xf22a ++#define F367TER_RCSPYOBS_BYTEOFPACKET1 0xf22a00ff ++ ++/* RCFSPYOBS4 */ ++#define R367TER_RCFSPYOBS4 0xf22b ++#define F367TER_RCSPYOBS_BYTEVALUE1 0xf22b00ff ++ ++/* RCFSPYOBS3 */ ++#define R367TER_RCFSPYOBS3 0xf22c ++#define F367TER_RCSPYOBS_DATA1 0xf22c00ff ++ ++/* RCFSPYOBS2 */ ++#define R367TER_RCFSPYOBS2 0xf22d ++#define F367TER_RCSPYOBS_DATa0 0xf22d00ff ++ ++/* RCFSPYOBS1 */ ++#define R367TER_RCFSPYOBS1 0xf22e ++#define F367TER_RCSPYOBS_DATAM1 0xf22e00ff ++ ++/* RCFSPYOBS0 */ ++#define R367TER_RCFSPYOBS0 0xf22f ++#define F367TER_RCSPYOBS_DATAM2 0xf22f00ff ++ ++/* TSGENERAL */ ++#define R367TER_TSGENERAL 0xf230 ++#define F367TER_TSGENERAL_7 0xf2300080 ++#define F367TER_TSGENERAL_6 0xf2300040 ++#define F367TER_TSFIFO_BCLK1aLL 0xf2300020 ++#define F367TER_TSGENERAL_4 0xf2300010 ++#define F367TER_MUXSTREAM_OUTMODE 0xf2300008 ++#define F367TER_TSFIFO_PERMPARAL 0xf2300006 ++#define F367TER_RST_REEDSOLO 0xf2300001 ++ ++/* RC1SPEED */ ++#define R367TER_RC1SPEED 0xf231 ++#define F367TER_TSRCFIFO1_OUTSPEED 0xf23100ff ++ ++/* TSGSTATUS */ ++#define R367TER_TSGSTATUS 0xf232 ++#define F367TER_TSGSTATUS_7 0xf2320080 ++#define F367TER_TSGSTATUS_6 0xf2320040 ++#define F367TER_RSMEM_FULL 0xf2320020 ++#define F367TER_RS_MULTCALC 0xf2320010 ++#define F367TER_RSIN_OVERTIME 0xf2320008 ++#define F367TER_TSFIFO3_DEMODSEL 0xf2320004 ++#define F367TER_TSFIFO2_DEMODSEL 0xf2320002 ++#define F367TER_TSFIFO1_DEMODSEL 0xf2320001 ++ ++ ++/* FECM */ ++#define R367TER_FECM 0xf233 ++#define F367TER_DSS_DVB 0xf2330080 ++#define F367TER_DEMOD_BYPASS 0xf2330040 ++#define F367TER_CMP_SLOWMODE 0xf2330020 ++#define F367TER_DSS_SRCH 0xf2330010 ++#define F367TER_FECM_3 0xf2330008 ++#define F367TER_DIFF_MODEVIT 0xf2330004 ++#define F367TER_SYNCVIT 0xf2330002 ++#define F367TER_I2CSYM 0xf2330001 ++ ++/* VTH12 */ ++#define R367TER_VTH12 0xf234 ++#define F367TER_VTH_12 0xf23400ff ++ ++/* VTH23 */ ++#define R367TER_VTH23 0xf235 ++#define F367TER_VTH_23 0xf23500ff ++ ++/* VTH34 */ ++#define R367TER_VTH34 0xf236 ++#define F367TER_VTH_34 0xf23600ff ++ ++/* VTH56 */ ++#define R367TER_VTH56 0xf237 ++#define F367TER_VTH_56 0xf23700ff ++ ++/* VTH67 */ ++#define R367TER_VTH67 0xf238 ++#define F367TER_VTH_67 0xf23800ff ++ ++/* VTH78 */ ++#define R367TER_VTH78 0xf239 ++#define F367TER_VTH_78 0xf23900ff ++ ++/* VITCURPUN */ ++#define R367TER_VITCURPUN 0xf23a ++#define F367TER_VIT_MAPPING 0xf23a00e0 ++#define F367TER_VIT_CURPUN 0xf23a001f ++ ++/* VERROR */ ++#define R367TER_VERROR 0xf23b ++#define F367TER_REGERR_VIT 0xf23b00ff ++ ++/* PRVIT */ ++#define R367TER_PRVIT 0xf23c ++#define F367TER_PRVIT_7 0xf23c0080 ++#define F367TER_DIS_VTHLOCK 0xf23c0040 ++#define F367TER_E7_8VIT 0xf23c0020 ++#define F367TER_E6_7VIT 0xf23c0010 ++#define F367TER_E5_6VIT 0xf23c0008 ++#define F367TER_E3_4VIT 0xf23c0004 ++#define F367TER_E2_3VIT 0xf23c0002 ++#define F367TER_E1_2VIT 0xf23c0001 ++ ++/* VAVSRVIT */ ++#define R367TER_VAVSRVIT 0xf23d ++#define F367TER_AMVIT 0xf23d0080 ++#define F367TER_FROZENVIT 0xf23d0040 ++#define F367TER_SNVIT 0xf23d0030 ++#define F367TER_TOVVIT 0xf23d000c ++#define F367TER_HYPVIT 0xf23d0003 ++ ++/* VSTATUSVIT */ ++#define R367TER_VSTATUSVIT 0xf23e ++#define F367TER_VITERBI_ON 0xf23e0080 ++#define F367TER_END_LOOPVIT 0xf23e0040 ++#define F367TER_VITERBI_DEPRF 0xf23e0020 ++#define F367TER_PRFVIT 0xf23e0010 ++#define F367TER_LOCKEDVIT 0xf23e0008 ++#define F367TER_VITERBI_DELOCK 0xf23e0004 ++#define F367TER_VIT_DEMODSEL 0xf23e0002 ++#define F367TER_VITERBI_COMPOUT 0xf23e0001 ++ ++/* VTHINUSE */ ++#define R367TER_VTHINUSE 0xf23f ++#define F367TER_VIT_INUSE 0xf23f00ff ++ ++/* KDIV12 */ ++#define R367TER_KDIV12 0xf240 ++#define F367TER_KDIV12_MANUAL 0xf2400080 ++#define F367TER_K_DIVIDER_12 0xf240007f ++ ++/* KDIV23 */ ++#define R367TER_KDIV23 0xf241 ++#define F367TER_KDIV23_MANUAL 0xf2410080 ++#define F367TER_K_DIVIDER_23 0xf241007f ++ ++/* KDIV34 */ ++#define R367TER_KDIV34 0xf242 ++#define F367TER_KDIV34_MANUAL 0xf2420080 ++#define F367TER_K_DIVIDER_34 0xf242007f ++ ++/* KDIV56 */ ++#define R367TER_KDIV56 0xf243 ++#define F367TER_KDIV56_MANUAL 0xf2430080 ++#define F367TER_K_DIVIDER_56 0xf243007f ++ ++/* KDIV67 */ ++#define R367TER_KDIV67 0xf244 ++#define F367TER_KDIV67_MANUAL 0xf2440080 ++#define F367TER_K_DIVIDER_67 0xf244007f ++ ++/* KDIV78 */ ++#define R367TER_KDIV78 0xf245 ++#define F367TER_KDIV78_MANUAL 0xf2450080 ++#define F367TER_K_DIVIDER_78 0xf245007f ++ ++/* SIGPOWER */ ++#define R367TER_SIGPOWER 0xf246 ++#define F367TER_SIGPOWER_MANUAL 0xf2460080 ++#define F367TER_SIG_POWER 0xf246007f ++ ++/* DEMAPVIT */ ++#define R367TER_DEMAPVIT 0xf247 ++#define F367TER_DEMAPVIT_7 0xf2470080 ++#define F367TER_K_DIVIDER_VIT 0xf247007f ++ ++/* VITSCALE */ ++#define R367TER_VITSCALE 0xf248 ++#define F367TER_NVTH_NOSRANGE 0xf2480080 ++#define F367TER_VERROR_MAXMODE 0xf2480040 ++#define F367TER_KDIV_MODE 0xf2480030 ++#define F367TER_NSLOWSN_LOCKED 0xf2480008 ++#define F367TER_DELOCK_PRFLOSS 0xf2480004 ++#define F367TER_DIS_RSFLOCK 0xf2480002 ++#define F367TER_VITSCALE_0 0xf2480001 ++ ++/* FFEC1PRG */ ++#define R367TER_FFEC1PRG 0xf249 ++#define F367TER_FDSS_DVB 0xf2490080 ++#define F367TER_FDSS_SRCH 0xf2490040 ++#define F367TER_FFECPROG_5 0xf2490020 ++#define F367TER_FFECPROG_4 0xf2490010 ++#define F367TER_FFECPROG_3 0xf2490008 ++#define F367TER_FFECPROG_2 0xf2490004 ++#define F367TER_FTS1_DISABLE 0xf2490002 ++#define F367TER_FTS2_DISABLE 0xf2490001 ++ ++/* FVITCURPUN */ ++#define R367TER_FVITCURPUN 0xf24a ++#define F367TER_FVIT_MAPPING 0xf24a00e0 ++#define F367TER_FVIT_CURPUN 0xf24a001f ++ ++/* FVERROR */ ++#define R367TER_FVERROR 0xf24b ++#define F367TER_FREGERR_VIT 0xf24b00ff ++ ++/* FVSTATUSVIT */ ++#define R367TER_FVSTATUSVIT 0xf24c ++#define F367TER_FVITERBI_ON 0xf24c0080 ++#define F367TER_F1END_LOOPVIT 0xf24c0040 ++#define F367TER_FVITERBI_DEPRF 0xf24c0020 ++#define F367TER_FPRFVIT 0xf24c0010 ++#define F367TER_FLOCKEDVIT 0xf24c0008 ++#define F367TER_FVITERBI_DELOCK 0xf24c0004 ++#define F367TER_FVIT_DEMODSEL 0xf24c0002 ++#define F367TER_FVITERBI_COMPOUT 0xf24c0001 ++ ++/* DEBUG_LT1 */ ++#define R367TER_DEBUG_LT1 0xf24d ++#define F367TER_DBG_LT1 0xf24d00ff ++ ++/* DEBUG_LT2 */ ++#define R367TER_DEBUG_LT2 0xf24e ++#define F367TER_DBG_LT2 0xf24e00ff ++ ++/* DEBUG_LT3 */ ++#define R367TER_DEBUG_LT3 0xf24f ++#define F367TER_DBG_LT3 0xf24f00ff ++ ++/* TSTSFMET */ ++#define R367TER_TSTSFMET 0xf250 ++#define F367TER_TSTSFEC_METRIQUES 0xf25000ff ++ ++/* SELOUT */ ++#define R367TER_SELOUT 0xf252 ++#define F367TER_EN_SYNC 0xf2520080 ++#define F367TER_EN_TBUSDEMAP 0xf2520040 ++#define F367TER_SELOUT_5 0xf2520020 ++#define F367TER_SELOUT_4 0xf2520010 ++#define F367TER_TSTSYNCHRO_MODE 0xf2520002 ++ ++/* TSYNC */ ++#define R367TER_TSYNC 0xf253 ++#define F367TER_CURPUN_INCMODE 0xf2530080 ++#define F367TER_CERR_TSTMODE 0xf2530040 ++#define F367TER_SHIFTSOF_MODE 0xf2530030 ++#define F367TER_SLOWPHA_MODE 0xf2530008 ++#define F367TER_PXX_BYPALL 0xf2530004 ++#define F367TER_FROTA45_FIRST 0xf2530002 ++#define F367TER_TST_BCHERROR 0xf2530001 ++ ++/* TSTERR */ ++#define R367TER_TSTERR 0xf254 ++#define F367TER_TST_LONGPKT 0xf2540080 ++#define F367TER_TST_ISSYION 0xf2540040 ++#define F367TER_TST_NPDON 0xf2540020 ++#define F367TER_TSTERR_4 0xf2540010 ++#define F367TER_TRACEBACK_MODE 0xf2540008 ++#define F367TER_TST_RSPARITY 0xf2540004 ++#define F367TER_METRIQUE_MODE 0xf2540003 ++ ++/* TSFSYNC */ ++#define R367TER_TSFSYNC 0xf255 ++#define F367TER_EN_SFECSYNC 0xf2550080 ++#define F367TER_EN_SFECDEMAP 0xf2550040 ++#define F367TER_SFCERR_TSTMODE 0xf2550020 ++#define F367TER_SFECPXX_BYPALL 0xf2550010 ++#define F367TER_SFECTSTSYNCHRO_MODE 0xf255000f ++ ++/* TSTSFERR */ ++#define R367TER_TSTSFERR 0xf256 ++#define F367TER_TSTSTERR_7 0xf2560080 ++#define F367TER_TSTSTERR_6 0xf2560040 ++#define F367TER_TSTSTERR_5 0xf2560020 ++#define F367TER_TSTSTERR_4 0xf2560010 ++#define F367TER_SFECTRACEBACK_MODE 0xf2560008 ++#define F367TER_SFEC_NCONVPROG 0xf2560004 ++#define F367TER_SFECMETRIQUE_MODE 0xf2560003 ++ ++/* TSTTSSF1 */ ++#define R367TER_TSTTSSF1 0xf258 ++#define F367TER_TSTERSSF 0xf2580080 ++#define F367TER_TSTTSSFEN 0xf2580040 ++#define F367TER_SFEC_OUTMODE 0xf2580030 ++#define F367TER_XLSF_NOFTHRESHOLD 0xf2580008 ++#define F367TER_TSTTSSF_STACKSEL 0xf2580007 ++ ++/* TSTTSSF2 */ ++#define R367TER_TSTTSSF2 0xf259 ++#define F367TER_DILSF_DBBHEADER 0xf2590080 ++#define F367TER_TSTTSSF_DISBUG 0xf2590040 ++#define F367TER_TSTTSSF_NOBADSTART 0xf2590020 ++#define F367TER_TSTTSSF_SELECT 0xf259001f ++ ++/* TSTTSSF3 */ ++#define R367TER_TSTTSSF3 0xf25a ++#define F367TER_TSTTSSF3_7 0xf25a0080 ++#define F367TER_TSTTSSF3_6 0xf25a0040 ++#define F367TER_TSTTSSF3_5 0xf25a0020 ++#define F367TER_TSTTSSF3_4 0xf25a0010 ++#define F367TER_TSTTSSF3_3 0xf25a0008 ++#define F367TER_TSTTSSF3_2 0xf25a0004 ++#define F367TER_TSTTSSF3_1 0xf25a0002 ++#define F367TER_DISSF_CLKENABLE 0xf25a0001 ++ ++/* TSTTS1 */ ++#define R367TER_TSTTS1 0xf25c ++#define F367TER_TSTERS 0xf25c0080 ++#define F367TER_TSFIFO_DSSSYNCB 0xf25c0040 ++#define F367TER_TSTTS_FSPYBEFRS 0xf25c0020 ++#define F367TER_NFORCE_SYNCBYTE 0xf25c0010 ++#define F367TER_XL_NOFTHRESHOLD 0xf25c0008 ++#define F367TER_TSTTS_FRFORCEPKT 0xf25c0004 ++#define F367TER_DESCR_NOTAUTO 0xf25c0002 ++#define F367TER_TSTTSEN 0xf25c0001 ++ ++/* TSTTS2 */ ++#define R367TER_TSTTS2 0xf25d ++#define F367TER_DIL_DBBHEADER 0xf25d0080 ++#define F367TER_TSTTS_NOBADXXX 0xf25d0040 ++#define F367TER_TSFIFO_DELSPEEDUP 0xf25d0020 ++#define F367TER_TSTTS_SELECT 0xf25d001f ++ ++/* TSTTS3 */ ++#define R367TER_TSTTS3 0xf25e ++#define F367TER_TSTTS_NOPKTGAIN 0xf25e0080 ++#define F367TER_TSTTS_NOPKTENE 0xf25e0040 ++#define F367TER_TSTTS_ISOLATION 0xf25e0020 ++#define F367TER_TSTTS_DISBUG 0xf25e0010 ++#define F367TER_TSTTS_NOBADSTART 0xf25e0008 ++#define F367TER_TSTTS_STACKSEL 0xf25e0007 ++ ++/* TSTTS4 */ ++#define R367TER_TSTTS4 0xf25f ++#define F367TER_TSTTS4_7 0xf25f0080 ++#define F367TER_TSTTS4_6 0xf25f0040 ++#define F367TER_TSTTS4_5 0xf25f0020 ++#define F367TER_TSTTS_DISDSTATE 0xf25f0010 ++#define F367TER_TSTTS_FASTNOSYNC 0xf25f0008 ++#define F367TER_EXT_FECSPYIN 0xf25f0004 ++#define F367TER_TSTTS_NODPZERO 0xf25f0002 ++#define F367TER_TSTTS_NODIV3 0xf25f0001 ++ ++/* TSTTSRC */ ++#define R367TER_TSTTSRC 0xf26c ++#define F367TER_TSTTSRC_7 0xf26c0080 ++#define F367TER_TSRCFIFO_DSSSYNCB 0xf26c0040 ++#define F367TER_TSRCFIFO_DPUNACTIVE 0xf26c0020 ++#define F367TER_TSRCFIFO_DELSPEEDUP 0xf26c0010 ++#define F367TER_TSTTSRC_NODIV3 0xf26c0008 ++#define F367TER_TSTTSRC_FRFORCEPKT 0xf26c0004 ++#define F367TER_SAT25_SDDORIGINE 0xf26c0002 ++#define F367TER_TSTTSRC_INACTIVE 0xf26c0001 ++ ++/* TSTTSRS */ ++#define R367TER_TSTTSRS 0xf26d ++#define F367TER_TSTTSRS_7 0xf26d0080 ++#define F367TER_TSTTSRS_6 0xf26d0040 ++#define F367TER_TSTTSRS_5 0xf26d0020 ++#define F367TER_TSTTSRS_4 0xf26d0010 ++#define F367TER_TSTTSRS_3 0xf26d0008 ++#define F367TER_TSTTSRS_2 0xf26d0004 ++#define F367TER_TSTRS_DISRS2 0xf26d0002 ++#define F367TER_TSTRS_DISRS1 0xf26d0001 ++ ++/* TSSTATEM */ ++#define R367TER_TSSTATEM 0xf270 ++#define F367TER_TSDIL_ON 0xf2700080 ++#define F367TER_TSSKIPRS_ON 0xf2700040 ++#define F367TER_TSRS_ON 0xf2700020 ++#define F367TER_TSDESCRAMB_ON 0xf2700010 ++#define F367TER_TSFRAME_MODE 0xf2700008 ++#define F367TER_TS_DISABLE 0xf2700004 ++#define F367TER_TSACM_MODE 0xf2700002 ++#define F367TER_TSOUT_NOSYNC 0xf2700001 ++ ++/* TSSTATEL */ ++#define R367TER_TSSTATEL 0xf271 ++#define F367TER_TSNOSYNCBYTE 0xf2710080 ++#define F367TER_TSPARITY_ON 0xf2710040 ++#define F367TER_TSSYNCOUTRS_ON 0xf2710020 ++#define F367TER_TSDVBS2_MODE 0xf2710010 ++#define F367TER_TSISSYI_ON 0xf2710008 ++#define F367TER_TSNPD_ON 0xf2710004 ++#define F367TER_TSCRC8_ON 0xf2710002 ++#define F367TER_TSDSS_PACKET 0xf2710001 ++ ++/* TSCFGH */ ++#define R367TER_TSCFGH 0xf272 ++#define F367TER_TSFIFO_DVBCI 0xf2720080 ++#define F367TER_TSFIFO_SERIAL 0xf2720040 ++#define F367TER_TSFIFO_TEIUPDATE 0xf2720020 ++#define F367TER_TSFIFO_DUTY50 0xf2720010 ++#define F367TER_TSFIFO_HSGNLOUT 0xf2720008 ++#define F367TER_TSFIFO_ERRMODE 0xf2720006 ++#define F367TER_RST_HWARE 0xf2720001 ++ ++/* TSCFGM */ ++#define R367TER_TSCFGM 0xf273 ++#define F367TER_TSFIFO_MANSPEED 0xf27300c0 ++#define F367TER_TSFIFO_PERMDATA 0xf2730020 ++#define F367TER_TSFIFO_NONEWSGNL 0xf2730010 ++#define F367TER_TSFIFO_BITSPEED 0xf2730008 ++#define F367TER_NPD_SPECDVBS2 0xf2730004 ++#define F367TER_TSFIFO_STOPCKDIS 0xf2730002 ++#define F367TER_TSFIFO_INVDATA 0xf2730001 ++ ++/* TSCFGL */ ++#define R367TER_TSCFGL 0xf274 ++#define F367TER_TSFIFO_BCLKDEL1cK 0xf27400c0 ++#define F367TER_BCHERROR_MODE 0xf2740030 ++#define F367TER_TSFIFO_NSGNL2dATA 0xf2740008 ++#define F367TER_TSFIFO_EMBINDVB 0xf2740004 ++#define F367TER_TSFIFO_DPUNACT 0xf2740002 ++#define F367TER_TSFIFO_NPDOFF 0xf2740001 ++ ++/* TSSYNC */ ++#define R367TER_TSSYNC 0xf275 ++#define F367TER_TSFIFO_PERMUTE 0xf2750080 ++#define F367TER_TSFIFO_FISCR3B 0xf2750060 ++#define F367TER_TSFIFO_SYNCMODE 0xf2750018 ++#define F367TER_TSFIFO_SYNCSEL 0xf2750007 ++ ++/* TSINSDELH */ ++#define R367TER_TSINSDELH 0xf276 ++#define F367TER_TSDEL_SYNCBYTE 0xf2760080 ++#define F367TER_TSDEL_XXHEADER 0xf2760040 ++#define F367TER_TSDEL_BBHEADER 0xf2760020 ++#define F367TER_TSDEL_DATAFIELD 0xf2760010 ++#define F367TER_TSINSDEL_ISCR 0xf2760008 ++#define F367TER_TSINSDEL_NPD 0xf2760004 ++#define F367TER_TSINSDEL_RSPARITY 0xf2760002 ++#define F367TER_TSINSDEL_CRC8 0xf2760001 ++ ++/* TSINSDELM */ ++#define R367TER_TSINSDELM 0xf277 ++#define F367TER_TSINS_BBPADDING 0xf2770080 ++#define F367TER_TSINS_BCHFEC 0xf2770040 ++#define F367TER_TSINS_LDPCFEC 0xf2770020 ++#define F367TER_TSINS_EMODCOD 0xf2770010 ++#define F367TER_TSINS_TOKEN 0xf2770008 ++#define F367TER_TSINS_XXXERR 0xf2770004 ++#define F367TER_TSINS_MATYPE 0xf2770002 ++#define F367TER_TSINS_UPL 0xf2770001 ++ ++/* TSINSDELL */ ++#define R367TER_TSINSDELL 0xf278 ++#define F367TER_TSINS_DFL 0xf2780080 ++#define F367TER_TSINS_SYNCD 0xf2780040 ++#define F367TER_TSINS_BLOCLEN 0xf2780020 ++#define F367TER_TSINS_SIGPCOUNT 0xf2780010 ++#define F367TER_TSINS_FIFO 0xf2780008 ++#define F367TER_TSINS_REALPACK 0xf2780004 ++#define F367TER_TSINS_TSCONFIG 0xf2780002 ++#define F367TER_TSINS_LATENCY 0xf2780001 ++ ++/* TSDIVN */ ++#define R367TER_TSDIVN 0xf279 ++#define F367TER_TSFIFO_LOWSPEED 0xf2790080 ++#define F367TER_BYTE_OVERSAMPLING 0xf2790070 ++#define F367TER_TSMANUAL_PACKETNBR 0xf279000f ++ ++/* TSDIVPM */ ++#define R367TER_TSDIVPM 0xf27a ++#define F367TER_TSMANUAL_P_HI 0xf27a00ff ++ ++/* TSDIVPL */ ++#define R367TER_TSDIVPL 0xf27b ++#define F367TER_TSMANUAL_P_LO 0xf27b00ff ++ ++/* TSDIVQM */ ++#define R367TER_TSDIVQM 0xf27c ++#define F367TER_TSMANUAL_Q_HI 0xf27c00ff ++ ++/* TSDIVQL */ ++#define R367TER_TSDIVQL 0xf27d ++#define F367TER_TSMANUAL_Q_LO 0xf27d00ff ++ ++/* TSDILSTKM */ ++#define R367TER_TSDILSTKM 0xf27e ++#define F367TER_TSFIFO_DILSTK_HI 0xf27e00ff ++ ++/* TSDILSTKL */ ++#define R367TER_TSDILSTKL 0xf27f ++#define F367TER_TSFIFO_DILSTK_LO 0xf27f00ff ++ ++/* TSSPEED */ ++#define R367TER_TSSPEED 0xf280 ++#define F367TER_TSFIFO_OUTSPEED 0xf28000ff ++ ++/* TSSTATUS */ ++#define R367TER_TSSTATUS 0xf281 ++#define F367TER_TSFIFO_LINEOK 0xf2810080 ++#define F367TER_TSFIFO_ERROR 0xf2810040 ++#define F367TER_TSFIFO_DATA7 0xf2810020 ++#define F367TER_TSFIFO_NOSYNC 0xf2810010 ++#define F367TER_ISCR_INITIALIZED 0xf2810008 ++#define F367TER_ISCR_UPDATED 0xf2810004 ++#define F367TER_SOFFIFO_UNREGUL 0xf2810002 ++#define F367TER_DIL_READY 0xf2810001 ++ ++/* TSSTATUS2 */ ++#define R367TER_TSSTATUS2 0xf282 ++#define F367TER_TSFIFO_DEMODSEL 0xf2820080 ++#define F367TER_TSFIFOSPEED_STORE 0xf2820040 ++#define F367TER_DILXX_RESET 0xf2820020 ++#define F367TER_TSSERIAL_IMPOSSIBLE 0xf2820010 ++#define F367TER_TSFIFO_UNDERSPEED 0xf2820008 ++#define F367TER_BITSPEED_EVENT 0xf2820004 ++#define F367TER_UL_SCRAMBDETECT 0xf2820002 ++#define F367TER_ULDTV67_FALSELOCK 0xf2820001 ++ ++/* TSBITRATEM */ ++#define R367TER_TSBITRATEM 0xf283 ++#define F367TER_TSFIFO_BITRATE_HI 0xf28300ff ++ ++/* TSBITRATEL */ ++#define R367TER_TSBITRATEL 0xf284 ++#define F367TER_TSFIFO_BITRATE_LO 0xf28400ff ++ ++/* TSPACKLENM */ ++#define R367TER_TSPACKLENM 0xf285 ++#define F367TER_TSFIFO_PACKCPT 0xf28500e0 ++#define F367TER_DIL_RPLEN_HI 0xf285001f ++ ++/* TSPACKLENL */ ++#define R367TER_TSPACKLENL 0xf286 ++#define F367TER_DIL_RPLEN_LO 0xf28600ff ++ ++/* TSBLOCLENM */ ++#define R367TER_TSBLOCLENM 0xf287 ++#define F367TER_TSFIFO_PFLEN_HI 0xf28700ff ++ ++/* TSBLOCLENL */ ++#define R367TER_TSBLOCLENL 0xf288 ++#define F367TER_TSFIFO_PFLEN_LO 0xf28800ff ++ ++/* TSDLYH */ ++#define R367TER_TSDLYH 0xf289 ++#define F367TER_SOFFIFO_TSTIMEVALID 0xf2890080 ++#define F367TER_SOFFIFO_SPEEDUP 0xf2890040 ++#define F367TER_SOFFIFO_STOP 0xf2890020 ++#define F367TER_SOFFIFO_REGULATED 0xf2890010 ++#define F367TER_SOFFIFO_REALSBOFF_HI 0xf289000f ++ ++/* TSDLYM */ ++#define R367TER_TSDLYM 0xf28a ++#define F367TER_SOFFIFO_REALSBOFF_MED 0xf28a00ff ++ ++/* TSDLYL */ ++#define R367TER_TSDLYL 0xf28b ++#define F367TER_SOFFIFO_REALSBOFF_LO 0xf28b00ff ++ ++/* TSNPDAV */ ++#define R367TER_TSNPDAV 0xf28c ++#define F367TER_TSNPD_AVERAGE 0xf28c00ff ++ ++/* TSBUFSTATH */ ++#define R367TER_TSBUFSTATH 0xf28d ++#define F367TER_TSISCR_3BYTES 0xf28d0080 ++#define F367TER_TSISCR_NEWDATA 0xf28d0040 ++#define F367TER_TSISCR_BUFSTAT_HI 0xf28d003f ++ ++/* TSBUFSTATM */ ++#define R367TER_TSBUFSTATM 0xf28e ++#define F367TER_TSISCR_BUFSTAT_MED 0xf28e00ff ++ ++/* TSBUFSTATL */ ++#define R367TER_TSBUFSTATL 0xf28f ++#define F367TER_TSISCR_BUFSTAT_LO 0xf28f00ff ++ ++/* TSDEBUGM */ ++#define R367TER_TSDEBUGM 0xf290 ++#define F367TER_TSFIFO_ILLPACKET 0xf2900080 ++#define F367TER_DIL_NOSYNC 0xf2900040 ++#define F367TER_DIL_ISCR 0xf2900020 ++#define F367TER_DILOUT_BSYNCB 0xf2900010 ++#define F367TER_TSFIFO_EMPTYPKT 0xf2900008 ++#define F367TER_TSFIFO_EMPTYRD 0xf2900004 ++#define F367TER_SOFFIFO_STOPM 0xf2900002 ++#define F367TER_SOFFIFO_SPEEDUPM 0xf2900001 ++ ++/* TSDEBUGL */ ++#define R367TER_TSDEBUGL 0xf291 ++#define F367TER_TSFIFO_PACKLENFAIL 0xf2910080 ++#define F367TER_TSFIFO_SYNCBFAIL 0xf2910040 ++#define F367TER_TSFIFO_VITLIBRE 0xf2910020 ++#define F367TER_TSFIFO_BOOSTSPEEDM 0xf2910010 ++#define F367TER_TSFIFO_UNDERSPEEDM 0xf2910008 ++#define F367TER_TSFIFO_ERROR_EVNT 0xf2910004 ++#define F367TER_TSFIFO_FULL 0xf2910002 ++#define F367TER_TSFIFO_OVERFLOWM 0xf2910001 ++ ++/* TSDLYSETH */ ++#define R367TER_TSDLYSETH 0xf292 ++#define F367TER_SOFFIFO_OFFSET 0xf29200e0 ++#define F367TER_SOFFIFO_SYMBOFFSET_HI 0xf292001f ++ ++/* TSDLYSETM */ ++#define R367TER_TSDLYSETM 0xf293 ++#define F367TER_SOFFIFO_SYMBOFFSET_MED 0xf29300ff ++ ++/* TSDLYSETL */ ++#define R367TER_TSDLYSETL 0xf294 ++#define F367TER_SOFFIFO_SYMBOFFSET_LO 0xf29400ff ++ ++/* TSOBSCFG */ ++#define R367TER_TSOBSCFG 0xf295 ++#define F367TER_TSFIFO_OBSCFG 0xf29500ff ++ ++/* TSOBSM */ ++#define R367TER_TSOBSM 0xf296 ++#define F367TER_TSFIFO_OBSDATA_HI 0xf29600ff ++ ++/* TSOBSL */ ++#define R367TER_TSOBSL 0xf297 ++#define F367TER_TSFIFO_OBSDATA_LO 0xf29700ff ++ ++/* ERRCTRL1 */ ++#define R367TER_ERRCTRL1 0xf298 ++#define F367TER_ERR_SRC1 0xf29800f0 ++#define F367TER_ERRCTRL1_3 0xf2980008 ++#define F367TER_NUM_EVT1 0xf2980007 ++ ++/* ERRCNT1H */ ++#define R367TER_ERRCNT1H 0xf299 ++#define F367TER_ERRCNT1_OLDVALUE 0xf2990080 ++#define F367TER_ERR_CNT1 0xf299007f ++ ++/* ERRCNT1M */ ++#define R367TER_ERRCNT1M 0xf29a ++#define F367TER_ERR_CNT1_HI 0xf29a00ff ++ ++/* ERRCNT1L */ ++#define R367TER_ERRCNT1L 0xf29b ++#define F367TER_ERR_CNT1_LO 0xf29b00ff ++ ++/* ERRCTRL2 */ ++#define R367TER_ERRCTRL2 0xf29c ++#define F367TER_ERR_SRC2 0xf29c00f0 ++#define F367TER_ERRCTRL2_3 0xf29c0008 ++#define F367TER_NUM_EVT2 0xf29c0007 ++ ++/* ERRCNT2H */ ++#define R367TER_ERRCNT2H 0xf29d ++#define F367TER_ERRCNT2_OLDVALUE 0xf29d0080 ++#define F367TER_ERR_CNT2_HI 0xf29d007f ++ ++/* ERRCNT2M */ ++#define R367TER_ERRCNT2M 0xf29e ++#define F367TER_ERR_CNT2_MED 0xf29e00ff ++ ++/* ERRCNT2L */ ++#define R367TER_ERRCNT2L 0xf29f ++#define F367TER_ERR_CNT2_LO 0xf29f00ff ++ ++/* FECSPY */ ++#define R367TER_FECSPY 0xf2a0 ++#define F367TER_SPY_ENABLE 0xf2a00080 ++#define F367TER_NO_SYNCBYTE 0xf2a00040 ++#define F367TER_SERIAL_MODE 0xf2a00020 ++#define F367TER_UNUSUAL_PACKET 0xf2a00010 ++#define F367TER_BERMETER_DATAMODE 0xf2a0000c ++#define F367TER_BERMETER_LMODE 0xf2a00002 ++#define F367TER_BERMETER_RESET 0xf2a00001 ++ ++/* FSPYCFG */ ++#define R367TER_FSPYCFG 0xf2a1 ++#define F367TER_FECSPY_INPUT 0xf2a100c0 ++#define F367TER_RST_ON_ERROR 0xf2a10020 ++#define F367TER_ONE_SHOT 0xf2a10010 ++#define F367TER_I2C_MOD 0xf2a1000c ++#define F367TER_SPY_HYSTERESIS 0xf2a10003 ++ ++/* FSPYDATA */ ++#define R367TER_FSPYDATA 0xf2a2 ++#define F367TER_SPY_STUFFING 0xf2a20080 ++#define F367TER_NOERROR_PKTJITTER 0xf2a20040 ++#define F367TER_SPY_CNULLPKT 0xf2a20020 ++#define F367TER_SPY_OUTDATA_MODE 0xf2a2001f ++ ++/* FSPYOUT */ ++#define R367TER_FSPYOUT 0xf2a3 ++#define F367TER_FSPY_DIRECT 0xf2a30080 ++#define F367TER_FSPYOUT_6 0xf2a30040 ++#define F367TER_SPY_OUTDATA_BUS 0xf2a30038 ++#define F367TER_STUFF_MODE 0xf2a30007 ++ ++/* FSTATUS */ ++#define R367TER_FSTATUS 0xf2a4 ++#define F367TER_SPY_ENDSIM 0xf2a40080 ++#define F367TER_VALID_SIM 0xf2a40040 ++#define F367TER_FOUND_SIGNAL 0xf2a40020 ++#define F367TER_DSS_SYNCBYTE 0xf2a40010 ++#define F367TER_RESULT_STATE 0xf2a4000f ++ ++/* FGOODPACK */ ++#define R367TER_FGOODPACK 0xf2a5 ++#define F367TER_FGOOD_PACKET 0xf2a500ff ++ ++/* FPACKCNT */ ++#define R367TER_FPACKCNT 0xf2a6 ++#define F367TER_FPACKET_COUNTER 0xf2a600ff ++ ++/* FSPYMISC */ ++#define R367TER_FSPYMISC 0xf2a7 ++#define F367TER_FLABEL_COUNTER 0xf2a700ff ++ ++/* FBERCPT4 */ ++#define R367TER_FBERCPT4 0xf2a8 ++#define F367TER_FBERMETER_CPT5 0xf2a800ff ++ ++/* FBERCPT3 */ ++#define R367TER_FBERCPT3 0xf2a9 ++#define F367TER_FBERMETER_CPT4 0xf2a900ff ++ ++/* FBERCPT2 */ ++#define R367TER_FBERCPT2 0xf2aa ++#define F367TER_FBERMETER_CPT3 0xf2aa00ff ++ ++/* FBERCPT1 */ ++#define R367TER_FBERCPT1 0xf2ab ++#define F367TER_FBERMETER_CPT2 0xf2ab00ff ++ ++/* FBERCPT0 */ ++#define R367TER_FBERCPT0 0xf2ac ++#define F367TER_FBERMETER_CPT1 0xf2ac00ff ++ ++/* FBERERR2 */ ++#define R367TER_FBERERR2 0xf2ad ++#define F367TER_FBERMETER_ERR_HI 0xf2ad00ff ++ ++/* FBERERR1 */ ++#define R367TER_FBERERR1 0xf2ae ++#define F367TER_FBERMETER_ERR_MED 0xf2ae00ff ++ ++/* FBERERR0 */ ++#define R367TER_FBERERR0 0xf2af ++#define F367TER_FBERMETER_ERR_LO 0xf2af00ff ++ ++/* FSTATESM */ ++#define R367TER_FSTATESM 0xf2b0 ++#define F367TER_RSTATE_F 0xf2b00080 ++#define F367TER_RSTATE_E 0xf2b00040 ++#define F367TER_RSTATE_D 0xf2b00020 ++#define F367TER_RSTATE_C 0xf2b00010 ++#define F367TER_RSTATE_B 0xf2b00008 ++#define F367TER_RSTATE_A 0xf2b00004 ++#define F367TER_RSTATE_9 0xf2b00002 ++#define F367TER_RSTATE_8 0xf2b00001 ++ ++/* FSTATESL */ ++#define R367TER_FSTATESL 0xf2b1 ++#define F367TER_RSTATE_7 0xf2b10080 ++#define F367TER_RSTATE_6 0xf2b10040 ++#define F367TER_RSTATE_5 0xf2b10020 ++#define F367TER_RSTATE_4 0xf2b10010 ++#define F367TER_RSTATE_3 0xf2b10008 ++#define F367TER_RSTATE_2 0xf2b10004 ++#define F367TER_RSTATE_1 0xf2b10002 ++#define F367TER_RSTATE_0 0xf2b10001 ++ ++/* FSPYBER */ ++#define R367TER_FSPYBER 0xf2b2 ++#define F367TER_FSPYBER_7 0xf2b20080 ++#define F367TER_FSPYOBS_XORREAD 0xf2b20040 ++#define F367TER_FSPYBER_OBSMODE 0xf2b20020 ++#define F367TER_FSPYBER_SYNCBYTE 0xf2b20010 ++#define F367TER_FSPYBER_UNSYNC 0xf2b20008 ++#define F367TER_FSPYBER_CTIME 0xf2b20007 ++ ++/* FSPYDISTM */ ++#define R367TER_FSPYDISTM 0xf2b3 ++#define F367TER_PKTTIME_DISTANCE_HI 0xf2b300ff ++ ++/* FSPYDISTL */ ++#define R367TER_FSPYDISTL 0xf2b4 ++#define F367TER_PKTTIME_DISTANCE_LO 0xf2b400ff ++ ++/* FSPYOBS7 */ ++#define R367TER_FSPYOBS7 0xf2b8 ++#define F367TER_FSPYOBS_SPYFAIL 0xf2b80080 ++#define F367TER_FSPYOBS_SPYFAIL1 0xf2b80040 ++#define F367TER_FSPYOBS_ERROR 0xf2b80020 ++#define F367TER_FSPYOBS_STROUT 0xf2b80010 ++#define F367TER_FSPYOBS_RESULTSTATE1 0xf2b8000f ++ ++/* FSPYOBS6 */ ++#define R367TER_FSPYOBS6 0xf2b9 ++#define F367TER_FSPYOBS_RESULTSTATe0 0xf2b900f0 ++#define F367TER_FSPYOBS_RESULTSTATEM1 0xf2b9000f ++ ++/* FSPYOBS5 */ ++#define R367TER_FSPYOBS5 0xf2ba ++#define F367TER_FSPYOBS_BYTEOFPACKET1 0xf2ba00ff ++ ++/* FSPYOBS4 */ ++#define R367TER_FSPYOBS4 0xf2bb ++#define F367TER_FSPYOBS_BYTEVALUE1 0xf2bb00ff ++ ++/* FSPYOBS3 */ ++#define R367TER_FSPYOBS3 0xf2bc ++#define F367TER_FSPYOBS_DATA1 0xf2bc00ff ++ ++/* FSPYOBS2 */ ++#define R367TER_FSPYOBS2 0xf2bd ++#define F367TER_FSPYOBS_DATa0 0xf2bd00ff ++ ++/* FSPYOBS1 */ ++#define R367TER_FSPYOBS1 0xf2be ++#define F367TER_FSPYOBS_DATAM1 0xf2be00ff ++ ++/* FSPYOBS0 */ ++#define R367TER_FSPYOBS0 0xf2bf ++#define F367TER_FSPYOBS_DATAM2 0xf2bf00ff ++ ++/* SFDEMAP */ ++#define R367TER_SFDEMAP 0xf2c0 ++#define F367TER_SFDEMAP_7 0xf2c00080 ++#define F367TER_SFEC_K_DIVIDER_VIT 0xf2c0007f ++ ++/* SFERROR */ ++#define R367TER_SFERROR 0xf2c1 ++#define F367TER_SFEC_REGERR_VIT 0xf2c100ff ++ ++/* SFAVSR */ ++#define R367TER_SFAVSR 0xf2c2 ++#define F367TER_SFEC_SUMERRORS 0xf2c20080 ++#define F367TER_SERROR_MAXMODE 0xf2c20040 ++#define F367TER_SN_SFEC 0xf2c20030 ++#define F367TER_KDIV_MODE_SFEC 0xf2c2000c ++#define F367TER_SFAVSR_1 0xf2c20002 ++#define F367TER_SFAVSR_0 0xf2c20001 ++ ++/* SFECSTATUS */ ++#define R367TER_SFECSTATUS 0xf2c3 ++#define F367TER_SFEC_ON 0xf2c30080 ++#define F367TER_SFSTATUS_6 0xf2c30040 ++#define F367TER_SFSTATUS_5 0xf2c30020 ++#define F367TER_SFSTATUS_4 0xf2c30010 ++#define F367TER_LOCKEDSFEC 0xf2c30008 ++#define F367TER_SFEC_DELOCK 0xf2c30004 ++#define F367TER_SFEC_DEMODSEL1 0xf2c30002 ++#define F367TER_SFEC_OVFON 0xf2c30001 ++ ++/* SFKDIV12 */ ++#define R367TER_SFKDIV12 0xf2c4 ++#define F367TER_SFECKDIV12_MAN 0xf2c40080 ++#define F367TER_SFEC_K_DIVIDER_12 0xf2c4007f ++ ++/* SFKDIV23 */ ++#define R367TER_SFKDIV23 0xf2c5 ++#define F367TER_SFECKDIV23_MAN 0xf2c50080 ++#define F367TER_SFEC_K_DIVIDER_23 0xf2c5007f ++ ++/* SFKDIV34 */ ++#define R367TER_SFKDIV34 0xf2c6 ++#define F367TER_SFECKDIV34_MAN 0xf2c60080 ++#define F367TER_SFEC_K_DIVIDER_34 0xf2c6007f ++ ++/* SFKDIV56 */ ++#define R367TER_SFKDIV56 0xf2c7 ++#define F367TER_SFECKDIV56_MAN 0xf2c70080 ++#define F367TER_SFEC_K_DIVIDER_56 0xf2c7007f ++ ++/* SFKDIV67 */ ++#define R367TER_SFKDIV67 0xf2c8 ++#define F367TER_SFECKDIV67_MAN 0xf2c80080 ++#define F367TER_SFEC_K_DIVIDER_67 0xf2c8007f ++ ++/* SFKDIV78 */ ++#define R367TER_SFKDIV78 0xf2c9 ++#define F367TER_SFECKDIV78_MAN 0xf2c90080 ++#define F367TER_SFEC_K_DIVIDER_78 0xf2c9007f ++ ++/* SFDILSTKM */ ++#define R367TER_SFDILSTKM 0xf2ca ++#define F367TER_SFEC_PACKCPT 0xf2ca00e0 ++#define F367TER_SFEC_DILSTK_HI 0xf2ca001f ++ ++/* SFDILSTKL */ ++#define R367TER_SFDILSTKL 0xf2cb ++#define F367TER_SFEC_DILSTK_LO 0xf2cb00ff ++ ++/* SFSTATUS */ ++#define R367TER_SFSTATUS 0xf2cc ++#define F367TER_SFEC_LINEOK 0xf2cc0080 ++#define F367TER_SFEC_ERROR 0xf2cc0040 ++#define F367TER_SFEC_DATA7 0xf2cc0020 ++#define F367TER_SFEC_OVERFLOW 0xf2cc0010 ++#define F367TER_SFEC_DEMODSEL2 0xf2cc0008 ++#define F367TER_SFEC_NOSYNC 0xf2cc0004 ++#define F367TER_SFEC_UNREGULA 0xf2cc0002 ++#define F367TER_SFEC_READY 0xf2cc0001 ++ ++/* SFDLYH */ ++#define R367TER_SFDLYH 0xf2cd ++#define F367TER_SFEC_TSTIMEVALID 0xf2cd0080 ++#define F367TER_SFEC_SPEEDUP 0xf2cd0040 ++#define F367TER_SFEC_STOP 0xf2cd0020 ++#define F367TER_SFEC_REGULATED 0xf2cd0010 ++#define F367TER_SFEC_REALSYMBOFFSET 0xf2cd000f ++ ++/* SFDLYM */ ++#define R367TER_SFDLYM 0xf2ce ++#define F367TER_SFEC_REALSYMBOFFSET_HI 0xf2ce00ff ++ ++/* SFDLYL */ ++#define R367TER_SFDLYL 0xf2cf ++#define F367TER_SFEC_REALSYMBOFFSET_LO 0xf2cf00ff ++ ++/* SFDLYSETH */ ++#define R367TER_SFDLYSETH 0xf2d0 ++#define F367TER_SFEC_OFFSET 0xf2d000e0 ++#define F367TER_SFECDLYSETH_4 0xf2d00010 ++#define F367TER_RST_SFEC 0xf2d00008 ++#define F367TER_SFECDLYSETH_2 0xf2d00004 ++#define F367TER_SFEC_DISABLE 0xf2d00002 ++#define F367TER_SFEC_UNREGUL 0xf2d00001 ++ ++/* SFDLYSETM */ ++#define R367TER_SFDLYSETM 0xf2d1 ++#define F367TER_SFECDLYSETM_7 0xf2d10080 ++#define F367TER_SFEC_SYMBOFFSET_HI 0xf2d1007f ++ ++/* SFDLYSETL */ ++#define R367TER_SFDLYSETL 0xf2d2 ++#define F367TER_SFEC_SYMBOFFSET_LO 0xf2d200ff ++ ++/* SFOBSCFG */ ++#define R367TER_SFOBSCFG 0xf2d3 ++#define F367TER_SFEC_OBSCFG 0xf2d300ff ++ ++/* SFOBSM */ ++#define R367TER_SFOBSM 0xf2d4 ++#define F367TER_SFEC_OBSDATA_HI 0xf2d400ff ++ ++/* SFOBSL */ ++#define R367TER_SFOBSL 0xf2d5 ++#define F367TER_SFEC_OBSDATA_LO 0xf2d500ff ++ ++/* SFECINFO */ ++#define R367TER_SFECINFO 0xf2d6 ++#define F367TER_SFECINFO_7 0xf2d60080 ++#define F367TER_SFEC_SYNCDLSB 0xf2d60070 ++#define F367TER_SFCE_S1cPHASE 0xf2d6000f ++ ++/* SFERRCTRL */ ++#define R367TER_SFERRCTRL 0xf2d8 ++#define F367TER_SFEC_ERR_SOURCE 0xf2d800f0 ++#define F367TER_SFERRCTRL_3 0xf2d80008 ++#define F367TER_SFEC_NUM_EVENT 0xf2d80007 ++ ++/* SFERRCNTH */ ++#define R367TER_SFERRCNTH 0xf2d9 ++#define F367TER_SFERRC_OLDVALUE 0xf2d90080 ++#define F367TER_SFEC_ERR_CNT 0xf2d9007f ++ ++/* SFERRCNTM */ ++#define R367TER_SFERRCNTM 0xf2da ++#define F367TER_SFEC_ERR_CNT_HI 0xf2da00ff ++ ++/* SFERRCNTL */ ++#define R367TER_SFERRCNTL 0xf2db ++#define F367TER_SFEC_ERR_CNT_LO 0xf2db00ff ++ ++/* SYMBRATEM */ ++#define R367TER_SYMBRATEM 0xf2e0 ++#define F367TER_DEFGEN_SYMBRATE_HI 0xf2e000ff ++ ++/* SYMBRATEL */ ++#define R367TER_SYMBRATEL 0xf2e1 ++#define F367TER_DEFGEN_SYMBRATE_LO 0xf2e100ff ++ ++/* SYMBSTATUS */ ++#define R367TER_SYMBSTATUS 0xf2e2 ++#define F367TER_SYMBDLINE2_OFF 0xf2e20080 ++#define F367TER_SDDL_REINIT1 0xf2e20040 ++#define F367TER_SDD_REINIT1 0xf2e20020 ++#define F367TER_TOKENID_ERROR 0xf2e20010 ++#define F367TER_SYMBRATE_OVERFLOW 0xf2e20008 ++#define F367TER_SYMBRATE_UNDERFLOW 0xf2e20004 ++#define F367TER_TOKENID_RSTEVENT 0xf2e20002 ++#define F367TER_TOKENID_RESET1 0xf2e20001 ++ ++/* SYMBCFG */ ++#define R367TER_SYMBCFG 0xf2e3 ++#define F367TER_SYMBCFG_7 0xf2e30080 ++#define F367TER_SYMBCFG_6 0xf2e30040 ++#define F367TER_SYMBCFG_5 0xf2e30020 ++#define F367TER_SYMBCFG_4 0xf2e30010 ++#define F367TER_SYMRATE_FSPEED 0xf2e3000c ++#define F367TER_SYMRATE_SSPEED 0xf2e30003 ++ ++/* SYMBFIFOM */ ++#define R367TER_SYMBFIFOM 0xf2e4 ++#define F367TER_SYMBFIFOM_7 0xf2e40080 ++#define F367TER_SYMBFIFOM_6 0xf2e40040 ++#define F367TER_DEFGEN_SYMFIFO_HI 0xf2e4003f ++ ++/* SYMBFIFOL */ ++#define R367TER_SYMBFIFOL 0xf2e5 ++#define F367TER_DEFGEN_SYMFIFO_LO 0xf2e500ff ++ ++/* SYMBOFFSM */ ++#define R367TER_SYMBOFFSM 0xf2e6 ++#define F367TER_TOKENID_RESET2 0xf2e60080 ++#define F367TER_SDDL_REINIT2 0xf2e60040 ++#define F367TER_SDD_REINIT2 0xf2e60020 ++#define F367TER_SYMBOFFSM_4 0xf2e60010 ++#define F367TER_SYMBOFFSM_3 0xf2e60008 ++#define F367TER_DEFGEN_SYMBOFFSET_HI 0xf2e60007 ++ ++/* SYMBOFFSL */ ++#define R367TER_SYMBOFFSL 0xf2e7 ++#define F367TER_DEFGEN_SYMBOFFSET_LO 0xf2e700ff ++ ++/* DEBUG_LT4 */ ++#define R367TER_DEBUG_LT4 0xf400 ++#define F367TER_F_DEBUG_LT4 0xf40000ff ++ ++/* DEBUG_LT5 */ ++#define R367TER_DEBUG_LT5 0xf401 ++#define F367TER_F_DEBUG_LT5 0xf40100ff ++ ++/* DEBUG_LT6 */ ++#define R367TER_DEBUG_LT6 0xf402 ++#define F367TER_F_DEBUG_LT6 0xf40200ff ++ ++/* DEBUG_LT7 */ ++#define R367TER_DEBUG_LT7 0xf403 ++#define F367TER_F_DEBUG_LT7 0xf40300ff ++ ++/* DEBUG_LT8 */ ++#define R367TER_DEBUG_LT8 0xf404 ++#define F367TER_F_DEBUG_LT8 0xf40400ff ++ ++/* DEBUG_LT9 */ ++#define R367TER_DEBUG_LT9 0xf405 ++#define F367TER_F_DEBUG_LT9 0xf40500ff ++ ++#define STV0367TER_NBREGS 445 ++ ++/* ID */ ++#define R367CAB_ID 0xf000 ++#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff ++ ++/* I2CRPT */ ++#define R367CAB_I2CRPT 0xf001 ++#define F367CAB_I2CT_ON 0xf0010080 ++#define F367CAB_ENARPT_LEVEL 0xf0010070 ++#define F367CAB_SCLT_DELAY 0xf0010008 ++#define F367CAB_SCLT_NOD 0xf0010004 ++#define F367CAB_STOP_ENABLE 0xf0010002 ++#define F367CAB_SDAT_NOD 0xf0010001 ++ ++/* TOPCTRL */ ++#define R367CAB_TOPCTRL 0xf002 ++#define F367CAB_STDBY 0xf0020080 ++#define F367CAB_STDBY_CORE 0xf0020020 ++#define F367CAB_QAM_COFDM 0xf0020010 ++#define F367CAB_TS_DIS 0xf0020008 ++#define F367CAB_DIR_CLK_216 0xf0020004 ++ ++/* IOCFG0 */ ++#define R367CAB_IOCFG0 0xf003 ++#define F367CAB_OP0_SD 0xf0030080 ++#define F367CAB_OP0_VAL 0xf0030040 ++#define F367CAB_OP0_OD 0xf0030020 ++#define F367CAB_OP0_INV 0xf0030010 ++#define F367CAB_OP0_DACVALUE_HI 0xf003000f ++ ++/* DAc0R */ ++#define R367CAB_DAC0R 0xf004 ++#define F367CAB_OP0_DACVALUE_LO 0xf00400ff ++ ++/* IOCFG1 */ ++#define R367CAB_IOCFG1 0xf005 ++#define F367CAB_IP0 0xf0050040 ++#define F367CAB_OP1_OD 0xf0050020 ++#define F367CAB_OP1_INV 0xf0050010 ++#define F367CAB_OP1_DACVALUE_HI 0xf005000f ++ ++/* DAC1R */ ++#define R367CAB_DAC1R 0xf006 ++#define F367CAB_OP1_DACVALUE_LO 0xf00600ff ++ ++/* IOCFG2 */ ++#define R367CAB_IOCFG2 0xf007 ++#define F367CAB_OP2_LOCK_CONF 0xf00700e0 ++#define F367CAB_OP2_OD 0xf0070010 ++#define F367CAB_OP2_VAL 0xf0070008 ++#define F367CAB_OP1_LOCK_CONF 0xf0070007 ++ ++/* SDFR */ ++#define R367CAB_SDFR 0xf008 ++#define F367CAB_OP0_FREQ 0xf00800f0 ++#define F367CAB_OP1_FREQ 0xf008000f ++ ++/* AUX_CLK */ ++#define R367CAB_AUX_CLK 0xf00a ++#define F367CAB_AUXFEC_CTL 0xf00a00c0 ++#define F367CAB_DIS_CKX4 0xf00a0020 ++#define F367CAB_CKSEL 0xf00a0018 ++#define F367CAB_CKDIV_PROG 0xf00a0006 ++#define F367CAB_AUXCLK_ENA 0xf00a0001 ++ ++/* FREESYS1 */ ++#define R367CAB_FREESYS1 0xf00b ++#define F367CAB_FREESYS_1 0xf00b00ff ++ ++/* FREESYS2 */ ++#define R367CAB_FREESYS2 0xf00c ++#define F367CAB_FREESYS_2 0xf00c00ff ++ ++/* FREESYS3 */ ++#define R367CAB_FREESYS3 0xf00d ++#define F367CAB_FREESYS_3 0xf00d00ff ++ ++/* GPIO_CFG */ ++#define R367CAB_GPIO_CFG 0xf00e ++#define F367CAB_GPIO7_OD 0xf00e0080 ++#define F367CAB_GPIO7_CFG 0xf00e0040 ++#define F367CAB_GPIO6_OD 0xf00e0020 ++#define F367CAB_GPIO6_CFG 0xf00e0010 ++#define F367CAB_GPIO5_OD 0xf00e0008 ++#define F367CAB_GPIO5_CFG 0xf00e0004 ++#define F367CAB_GPIO4_OD 0xf00e0002 ++#define F367CAB_GPIO4_CFG 0xf00e0001 ++ ++/* GPIO_CMD */ ++#define R367CAB_GPIO_CMD 0xf00f ++#define F367CAB_GPIO7_VAL 0xf00f0008 ++#define F367CAB_GPIO6_VAL 0xf00f0004 ++#define F367CAB_GPIO5_VAL 0xf00f0002 ++#define F367CAB_GPIO4_VAL 0xf00f0001 ++ ++/* TSTRES */ ++#define R367CAB_TSTRES 0xf0c0 ++#define F367CAB_FRES_DISPLAY 0xf0c00080 ++#define F367CAB_FRES_FIFO_AD 0xf0c00020 ++#define F367CAB_FRESRS 0xf0c00010 ++#define F367CAB_FRESACS 0xf0c00008 ++#define F367CAB_FRESFEC 0xf0c00004 ++#define F367CAB_FRES_PRIF 0xf0c00002 ++#define F367CAB_FRESCORE 0xf0c00001 ++ ++/* ANACTRL */ ++#define R367CAB_ANACTRL 0xf0c1 ++#define F367CAB_BYPASS_XTAL 0xf0c10040 ++#define F367CAB_BYPASS_PLLXN 0xf0c1000c ++#define F367CAB_DIS_PAD_OSC 0xf0c10002 ++#define F367CAB_STDBY_PLLXN 0xf0c10001 ++ ++/* TSTBUS */ ++#define R367CAB_TSTBUS 0xf0c2 ++#define F367CAB_TS_BYTE_CLK_INV 0xf0c20080 ++#define F367CAB_CFG_IP 0xf0c20070 ++#define F367CAB_CFG_TST 0xf0c2000f ++ ++/* RF_AGC1 */ ++#define R367CAB_RF_AGC1 0xf0d4 ++#define F367CAB_RF_AGC1_LEVEL_HI 0xf0d400ff ++ ++/* RF_AGC2 */ ++#define R367CAB_RF_AGC2 0xf0d5 ++#define F367CAB_REF_ADGP 0xf0d50080 ++#define F367CAB_STDBY_ADCGP 0xf0d50020 ++#define F367CAB_RF_AGC1_LEVEL_LO 0xf0d50003 ++ ++/* ANADIGCTRL */ ++#define R367CAB_ANADIGCTRL 0xf0d7 ++#define F367CAB_SEL_CLKDEM 0xf0d70020 ++#define F367CAB_EN_BUFFER_Q 0xf0d70010 ++#define F367CAB_EN_BUFFER_I 0xf0d70008 ++#define F367CAB_ADC_RIS_EGDE 0xf0d70004 ++#define F367CAB_SGN_ADC 0xf0d70002 ++#define F367CAB_SEL_AD12_SYNC 0xf0d70001 ++ ++/* PLLMDIV */ ++#define R367CAB_PLLMDIV 0xf0d8 ++#define F367CAB_PLL_MDIV 0xf0d800ff ++ ++/* PLLNDIV */ ++#define R367CAB_PLLNDIV 0xf0d9 ++#define F367CAB_PLL_NDIV 0xf0d900ff ++ ++/* PLLSETUP */ ++#define R367CAB_PLLSETUP 0xf0da ++#define F367CAB_PLL_PDIV 0xf0da0070 ++#define F367CAB_PLL_KDIV 0xf0da000f ++ ++/* DUAL_AD12 */ ++#define R367CAB_DUAL_AD12 0xf0db ++#define F367CAB_FS20M 0xf0db0020 ++#define F367CAB_FS50M 0xf0db0010 ++#define F367CAB_INMODe0 0xf0db0008 ++#define F367CAB_POFFQ 0xf0db0004 ++#define F367CAB_POFFI 0xf0db0002 ++#define F367CAB_INMODE1 0xf0db0001 ++ ++/* TSTBIST */ ++#define R367CAB_TSTBIST 0xf0dc ++#define F367CAB_TST_BYP_CLK 0xf0dc0080 ++#define F367CAB_TST_GCLKENA_STD 0xf0dc0040 ++#define F367CAB_TST_GCLKENA 0xf0dc0020 ++#define F367CAB_TST_MEMBIST 0xf0dc001f ++ ++/* CTRL_1 */ ++#define R367CAB_CTRL_1 0xf402 ++#define F367CAB_SOFT_RST 0xf4020080 ++#define F367CAB_EQU_RST 0xf4020008 ++#define F367CAB_CRL_RST 0xf4020004 ++#define F367CAB_TRL_RST 0xf4020002 ++#define F367CAB_AGC_RST 0xf4020001 ++ ++/* CTRL_2 */ ++#define R367CAB_CTRL_2 0xf403 ++#define F367CAB_DEINT_RST 0xf4030008 ++#define F367CAB_RS_RST 0xf4030004 ++ ++/* IT_STATUS1 */ ++#define R367CAB_IT_STATUS1 0xf408 ++#define F367CAB_SWEEP_OUT 0xf4080080 ++#define F367CAB_FSM_CRL 0xf4080040 ++#define F367CAB_CRL_LOCK 0xf4080020 ++#define F367CAB_MFSM 0xf4080010 ++#define F367CAB_TRL_LOCK 0xf4080008 ++#define F367CAB_TRL_AGC_LIMIT 0xf4080004 ++#define F367CAB_ADJ_AGC_LOCK 0xf4080002 ++#define F367CAB_AGC_QAM_LOCK 0xf4080001 ++ ++/* IT_STATUS2 */ ++#define R367CAB_IT_STATUS2 0xf409 ++#define F367CAB_TSMF_CNT 0xf4090080 ++#define F367CAB_TSMF_EOF 0xf4090040 ++#define F367CAB_TSMF_RDY 0xf4090020 ++#define F367CAB_FEC_NOCORR 0xf4090010 ++#define F367CAB_SYNCSTATE 0xf4090008 ++#define F367CAB_DEINT_LOCK 0xf4090004 ++#define F367CAB_FADDING_FRZ 0xf4090002 ++#define F367CAB_TAPMON_ALARM 0xf4090001 ++ ++/* IT_EN1 */ ++#define R367CAB_IT_EN1 0xf40a ++#define F367CAB_SWEEP_OUTE 0xf40a0080 ++#define F367CAB_FSM_CRLE 0xf40a0040 ++#define F367CAB_CRL_LOCKE 0xf40a0020 ++#define F367CAB_MFSME 0xf40a0010 ++#define F367CAB_TRL_LOCKE 0xf40a0008 ++#define F367CAB_TRL_AGC_LIMITE 0xf40a0004 ++#define F367CAB_ADJ_AGC_LOCKE 0xf40a0002 ++#define F367CAB_AGC_LOCKE 0xf40a0001 ++ ++/* IT_EN2 */ ++#define R367CAB_IT_EN2 0xf40b ++#define F367CAB_TSMF_CNTE 0xf40b0080 ++#define F367CAB_TSMF_EOFE 0xf40b0040 ++#define F367CAB_TSMF_RDYE 0xf40b0020 ++#define F367CAB_FEC_NOCORRE 0xf40b0010 ++#define F367CAB_SYNCSTATEE 0xf40b0008 ++#define F367CAB_DEINT_LOCKE 0xf40b0004 ++#define F367CAB_FADDING_FRZE 0xf40b0002 ++#define F367CAB_TAPMON_ALARME 0xf40b0001 ++ ++/* CTRL_STATUS */ ++#define R367CAB_CTRL_STATUS 0xf40c ++#define F367CAB_QAMFEC_LOCK 0xf40c0004 ++#define F367CAB_TSMF_LOCK 0xf40c0002 ++#define F367CAB_TSMF_ERROR 0xf40c0001 ++ ++/* TEST_CTL */ ++#define R367CAB_TEST_CTL 0xf40f ++#define F367CAB_TST_BLK_SEL 0xf40f0060 ++#define F367CAB_TST_BUS_SEL 0xf40f001f ++ ++/* AGC_CTL */ ++#define R367CAB_AGC_CTL 0xf410 ++#define F367CAB_AGC_LCK_TH 0xf41000f0 ++#define F367CAB_AGC_ACCUMRSTSEL 0xf4100007 ++ ++/* AGC_IF_CFG */ ++#define R367CAB_AGC_IF_CFG 0xf411 ++#define F367CAB_AGC_IF_BWSEL 0xf41100f0 ++#define F367CAB_AGC_IF_FREEZE 0xf4110002 ++ ++/* AGC_RF_CFG */ ++#define R367CAB_AGC_RF_CFG 0xf412 ++#define F367CAB_AGC_RF_BWSEL 0xf4120070 ++#define F367CAB_AGC_RF_FREEZE 0xf4120002 ++ ++/* AGC_PWM_CFG */ ++#define R367CAB_AGC_PWM_CFG 0xf413 ++#define F367CAB_AGC_RF_PWM_TST 0xf4130080 ++#define F367CAB_AGC_RF_PWM_INV 0xf4130040 ++#define F367CAB_AGC_IF_PWM_TST 0xf4130008 ++#define F367CAB_AGC_IF_PWM_INV 0xf4130004 ++#define F367CAB_AGC_PWM_CLKDIV 0xf4130003 ++ ++/* AGC_PWR_REF_L */ ++#define R367CAB_AGC_PWR_REF_L 0xf414 ++#define F367CAB_AGC_PWRREF_LO 0xf41400ff ++ ++/* AGC_PWR_REF_H */ ++#define R367CAB_AGC_PWR_REF_H 0xf415 ++#define F367CAB_AGC_PWRREF_HI 0xf4150003 ++ ++/* AGC_RF_TH_L */ ++#define R367CAB_AGC_RF_TH_L 0xf416 ++#define F367CAB_AGC_RF_TH_LO 0xf41600ff ++ ++/* AGC_RF_TH_H */ ++#define R367CAB_AGC_RF_TH_H 0xf417 ++#define F367CAB_AGC_RF_TH_HI 0xf417000f ++ ++/* AGC_IF_LTH_L */ ++#define R367CAB_AGC_IF_LTH_L 0xf418 ++#define F367CAB_AGC_IF_THLO_LO 0xf41800ff ++ ++/* AGC_IF_LTH_H */ ++#define R367CAB_AGC_IF_LTH_H 0xf419 ++#define F367CAB_AGC_IF_THLO_HI 0xf419000f ++ ++/* AGC_IF_HTH_L */ ++#define R367CAB_AGC_IF_HTH_L 0xf41a ++#define F367CAB_AGC_IF_THHI_LO 0xf41a00ff ++ ++/* AGC_IF_HTH_H */ ++#define R367CAB_AGC_IF_HTH_H 0xf41b ++#define F367CAB_AGC_IF_THHI_HI 0xf41b000f ++ ++/* AGC_PWR_RD_L */ ++#define R367CAB_AGC_PWR_RD_L 0xf41c ++#define F367CAB_AGC_PWR_WORD_LO 0xf41c00ff ++ ++/* AGC_PWR_RD_M */ ++#define R367CAB_AGC_PWR_RD_M 0xf41d ++#define F367CAB_AGC_PWR_WORD_ME 0xf41d00ff ++ ++/* AGC_PWR_RD_H */ ++#define R367CAB_AGC_PWR_RD_H 0xf41e ++#define F367CAB_AGC_PWR_WORD_HI 0xf41e0003 ++ ++/* AGC_PWM_IFCMD_L */ ++#define R367CAB_AGC_PWM_IFCMD_L 0xf420 ++#define F367CAB_AGC_IF_PWMCMD_LO 0xf42000ff ++ ++/* AGC_PWM_IFCMD_H */ ++#define R367CAB_AGC_PWM_IFCMD_H 0xf421 ++#define F367CAB_AGC_IF_PWMCMD_HI 0xf421000f ++ ++/* AGC_PWM_RFCMD_L */ ++#define R367CAB_AGC_PWM_RFCMD_L 0xf422 ++#define F367CAB_AGC_RF_PWMCMD_LO 0xf42200ff ++ ++/* AGC_PWM_RFCMD_H */ ++#define R367CAB_AGC_PWM_RFCMD_H 0xf423 ++#define F367CAB_AGC_RF_PWMCMD_HI 0xf423000f ++ ++/* IQDEM_CFG */ ++#define R367CAB_IQDEM_CFG 0xf424 ++#define F367CAB_IQDEM_CLK_SEL 0xf4240004 ++#define F367CAB_IQDEM_INVIQ 0xf4240002 ++#define F367CAB_IQDEM_A2dTYPE 0xf4240001 ++ ++/* MIX_NCO_LL */ ++#define R367CAB_MIX_NCO_LL 0xf425 ++#define F367CAB_MIX_NCO_INC_LL 0xf42500ff ++ ++/* MIX_NCO_HL */ ++#define R367CAB_MIX_NCO_HL 0xf426 ++#define F367CAB_MIX_NCO_INC_HL 0xf42600ff ++ ++/* MIX_NCO_HH */ ++#define R367CAB_MIX_NCO_HH 0xf427 ++#define F367CAB_MIX_NCO_INVCNST 0xf4270080 ++#define F367CAB_MIX_NCO_INC_HH 0xf427007f ++ ++/* SRC_NCO_LL */ ++#define R367CAB_SRC_NCO_LL 0xf428 ++#define F367CAB_SRC_NCO_INC_LL 0xf42800ff ++ ++/* SRC_NCO_LH */ ++#define R367CAB_SRC_NCO_LH 0xf429 ++#define F367CAB_SRC_NCO_INC_LH 0xf42900ff ++ ++/* SRC_NCO_HL */ ++#define R367CAB_SRC_NCO_HL 0xf42a ++#define F367CAB_SRC_NCO_INC_HL 0xf42a00ff ++ ++/* SRC_NCO_HH */ ++#define R367CAB_SRC_NCO_HH 0xf42b ++#define F367CAB_SRC_NCO_INC_HH 0xf42b007f ++ ++/* IQDEM_GAIN_SRC_L */ ++#define R367CAB_IQDEM_GAIN_SRC_L 0xf42c ++#define F367CAB_GAIN_SRC_LO 0xf42c00ff ++ ++/* IQDEM_GAIN_SRC_H */ ++#define R367CAB_IQDEM_GAIN_SRC_H 0xf42d ++#define F367CAB_GAIN_SRC_HI 0xf42d0003 ++ ++/* IQDEM_DCRM_CFG_LL */ ++#define R367CAB_IQDEM_DCRM_CFG_LL 0xf430 ++#define F367CAB_DCRM0_DCIN_L 0xf43000ff ++ ++/* IQDEM_DCRM_CFG_LH */ ++#define R367CAB_IQDEM_DCRM_CFG_LH 0xf431 ++#define F367CAB_DCRM1_I_DCIN_L 0xf43100fc ++#define F367CAB_DCRM0_DCIN_H 0xf4310003 ++ ++/* IQDEM_DCRM_CFG_HL */ ++#define R367CAB_IQDEM_DCRM_CFG_HL 0xf432 ++#define F367CAB_DCRM1_Q_DCIN_L 0xf43200f0 ++#define F367CAB_DCRM1_I_DCIN_H 0xf432000f ++ ++/* IQDEM_DCRM_CFG_HH */ ++#define R367CAB_IQDEM_DCRM_CFG_HH 0xf433 ++#define F367CAB_DCRM1_FRZ 0xf4330080 ++#define F367CAB_DCRM0_FRZ 0xf4330040 ++#define F367CAB_DCRM1_Q_DCIN_H 0xf433003f ++ ++/* IQDEM_ADJ_COEFf0 */ ++#define R367CAB_IQDEM_ADJ_COEFF0 0xf434 ++#define F367CAB_ADJIIR_COEFF10_L 0xf43400ff ++ ++/* IQDEM_ADJ_COEFF1 */ ++#define R367CAB_IQDEM_ADJ_COEFF1 0xf435 ++#define F367CAB_ADJIIR_COEFF11_L 0xf43500fc ++#define F367CAB_ADJIIR_COEFF10_H 0xf4350003 ++ ++/* IQDEM_ADJ_COEFF2 */ ++#define R367CAB_IQDEM_ADJ_COEFF2 0xf436 ++#define F367CAB_ADJIIR_COEFF12_L 0xf43600f0 ++#define F367CAB_ADJIIR_COEFF11_H 0xf436000f ++ ++/* IQDEM_ADJ_COEFF3 */ ++#define R367CAB_IQDEM_ADJ_COEFF3 0xf437 ++#define F367CAB_ADJIIR_COEFF20_L 0xf43700c0 ++#define F367CAB_ADJIIR_COEFF12_H 0xf437003f ++ ++/* IQDEM_ADJ_COEFF4 */ ++#define R367CAB_IQDEM_ADJ_COEFF4 0xf438 ++#define F367CAB_ADJIIR_COEFF20_H 0xf43800ff ++ ++/* IQDEM_ADJ_COEFF5 */ ++#define R367CAB_IQDEM_ADJ_COEFF5 0xf439 ++#define F367CAB_ADJIIR_COEFF21_L 0xf43900ff ++ ++/* IQDEM_ADJ_COEFF6 */ ++#define R367CAB_IQDEM_ADJ_COEFF6 0xf43a ++#define F367CAB_ADJIIR_COEFF22_L 0xf43a00fc ++#define F367CAB_ADJIIR_COEFF21_H 0xf43a0003 ++ ++/* IQDEM_ADJ_COEFF7 */ ++#define R367CAB_IQDEM_ADJ_COEFF7 0xf43b ++#define F367CAB_ADJIIR_COEFF22_H 0xf43b000f ++ ++/* IQDEM_ADJ_EN */ ++#define R367CAB_IQDEM_ADJ_EN 0xf43c ++#define F367CAB_ALLPASSFILT_EN 0xf43c0008 ++#define F367CAB_ADJ_AGC_EN 0xf43c0004 ++#define F367CAB_ADJ_COEFF_FRZ 0xf43c0002 ++#define F367CAB_ADJ_EN 0xf43c0001 ++ ++/* IQDEM_ADJ_AGC_REF */ ++#define R367CAB_IQDEM_ADJ_AGC_REF 0xf43d ++#define F367CAB_ADJ_AGC_REF 0xf43d00ff ++ ++/* ALLPASSFILT1 */ ++#define R367CAB_ALLPASSFILT1 0xf440 ++#define F367CAB_ALLPASSFILT_COEFF1_LO 0xf44000ff ++ ++/* ALLPASSFILT2 */ ++#define R367CAB_ALLPASSFILT2 0xf441 ++#define F367CAB_ALLPASSFILT_COEFF1_ME 0xf44100ff ++ ++/* ALLPASSFILT3 */ ++#define R367CAB_ALLPASSFILT3 0xf442 ++#define F367CAB_ALLPASSFILT_COEFF2_LO 0xf44200c0 ++#define F367CAB_ALLPASSFILT_COEFF1_HI 0xf442003f ++ ++/* ALLPASSFILT4 */ ++#define R367CAB_ALLPASSFILT4 0xf443 ++#define F367CAB_ALLPASSFILT_COEFF2_MEL 0xf44300ff ++ ++/* ALLPASSFILT5 */ ++#define R367CAB_ALLPASSFILT5 0xf444 ++#define F367CAB_ALLPASSFILT_COEFF2_MEH 0xf44400ff ++ ++/* ALLPASSFILT6 */ ++#define R367CAB_ALLPASSFILT6 0xf445 ++#define F367CAB_ALLPASSFILT_COEFF3_LO 0xf44500f0 ++#define F367CAB_ALLPASSFILT_COEFF2_HI 0xf445000f ++ ++/* ALLPASSFILT7 */ ++#define R367CAB_ALLPASSFILT7 0xf446 ++#define F367CAB_ALLPASSFILT_COEFF3_MEL 0xf44600ff ++ ++/* ALLPASSFILT8 */ ++#define R367CAB_ALLPASSFILT8 0xf447 ++#define F367CAB_ALLPASSFILT_COEFF3_MEH 0xf44700ff ++ ++/* ALLPASSFILT9 */ ++#define R367CAB_ALLPASSFILT9 0xf448 ++#define F367CAB_ALLPASSFILT_COEFF4_LO 0xf44800fc ++#define F367CAB_ALLPASSFILT_COEFF3_HI 0xf4480003 ++ ++/* ALLPASSFILT10 */ ++#define R367CAB_ALLPASSFILT10 0xf449 ++#define F367CAB_ALLPASSFILT_COEFF4_ME 0xf44900ff ++ ++/* ALLPASSFILT11 */ ++#define R367CAB_ALLPASSFILT11 0xf44a ++#define F367CAB_ALLPASSFILT_COEFF4_HI 0xf44a00ff ++ ++/* TRL_AGC_CFG */ ++#define R367CAB_TRL_AGC_CFG 0xf450 ++#define F367CAB_TRL_AGC_FREEZE 0xf4500080 ++#define F367CAB_TRL_AGC_REF 0xf450007f ++ ++/* TRL_LPF_CFG */ ++#define R367CAB_TRL_LPF_CFG 0xf454 ++#define F367CAB_NYQPOINT_INV 0xf4540040 ++#define F367CAB_TRL_SHIFT 0xf4540030 ++#define F367CAB_NYQ_COEFF_SEL 0xf454000c ++#define F367CAB_TRL_LPF_FREEZE 0xf4540002 ++#define F367CAB_TRL_LPF_CRT 0xf4540001 ++ ++/* TRL_LPF_ACQ_GAIN */ ++#define R367CAB_TRL_LPF_ACQ_GAIN 0xf455 ++#define F367CAB_TRL_GDIR_ACQ 0xf4550070 ++#define F367CAB_TRL_GINT_ACQ 0xf4550007 ++ ++/* TRL_LPF_TRK_GAIN */ ++#define R367CAB_TRL_LPF_TRK_GAIN 0xf456 ++#define F367CAB_TRL_GDIR_TRK 0xf4560070 ++#define F367CAB_TRL_GINT_TRK 0xf4560007 ++ ++/* TRL_LPF_OUT_GAIN */ ++#define R367CAB_TRL_LPF_OUT_GAIN 0xf457 ++#define F367CAB_TRL_GAIN_OUT 0xf4570007 ++ ++/* TRL_LOCKDET_LTH */ ++#define R367CAB_TRL_LOCKDET_LTH 0xf458 ++#define F367CAB_TRL_LCK_THLO 0xf4580007 ++ ++/* TRL_LOCKDET_HTH */ ++#define R367CAB_TRL_LOCKDET_HTH 0xf459 ++#define F367CAB_TRL_LCK_THHI 0xf45900ff ++ ++/* TRL_LOCKDET_TRGVAL */ ++#define R367CAB_TRL_LOCKDET_TRGVAL 0xf45a ++#define F367CAB_TRL_LCK_TRG 0xf45a00ff ++ ++/* IQ_QAM */ ++#define R367CAB_IQ_QAM 0xf45c ++#define F367CAB_IQ_INPUT 0xf45c0008 ++#define F367CAB_DETECT_MODE 0xf45c0007 ++ ++/* FSM_STATE */ ++#define R367CAB_FSM_STATE 0xf460 ++#define F367CAB_CRL_DFE 0xf4600080 ++#define F367CAB_DFE_START 0xf4600040 ++#define F367CAB_CTRLG_START 0xf4600030 ++#define F367CAB_FSM_FORCESTATE 0xf460000f ++ ++/* FSM_CTL */ ++#define R367CAB_FSM_CTL 0xf461 ++#define F367CAB_FEC2_EN 0xf4610040 ++#define F367CAB_SIT_EN 0xf4610020 ++#define F367CAB_TRL_AHEAD 0xf4610010 ++#define F367CAB_TRL2_EN 0xf4610008 ++#define F367CAB_FSM_EQA1_EN 0xf4610004 ++#define F367CAB_FSM_BKP_DIS 0xf4610002 ++#define F367CAB_FSM_FORCE_EN 0xf4610001 ++ ++/* FSM_STS */ ++#define R367CAB_FSM_STS 0xf462 ++#define F367CAB_FSM_STATUS 0xf462000f ++ ++/* FSM_SNR0_HTH */ ++#define R367CAB_FSM_SNR0_HTH 0xf463 ++#define F367CAB_SNR0_HTH 0xf46300ff ++ ++/* FSM_SNR1_HTH */ ++#define R367CAB_FSM_SNR1_HTH 0xf464 ++#define F367CAB_SNR1_HTH 0xf46400ff ++ ++/* FSM_SNR2_HTH */ ++#define R367CAB_FSM_SNR2_HTH 0xf465 ++#define F367CAB_SNR2_HTH 0xf46500ff ++ ++/* FSM_SNR0_LTH */ ++#define R367CAB_FSM_SNR0_LTH 0xf466 ++#define F367CAB_SNR0_LTH 0xf46600ff ++ ++/* FSM_SNR1_LTH */ ++#define R367CAB_FSM_SNR1_LTH 0xf467 ++#define F367CAB_SNR1_LTH 0xf46700ff ++ ++/* FSM_EQA1_HTH */ ++#define R367CAB_FSM_EQA1_HTH 0xf468 ++#define F367CAB_SNR3_HTH_LO 0xf46800f0 ++#define F367CAB_EQA1_HTH 0xf468000f ++ ++/* FSM_TEMPO */ ++#define R367CAB_FSM_TEMPO 0xf469 ++#define F367CAB_SIT 0xf46900c0 ++#define F367CAB_WST 0xf4690038 ++#define F367CAB_ELT 0xf4690006 ++#define F367CAB_SNR3_HTH_HI 0xf4690001 ++ ++/* FSM_CONFIG */ ++#define R367CAB_FSM_CONFIG 0xf46a ++#define F367CAB_FEC2_DFEOFF 0xf46a0004 ++#define F367CAB_PRIT_STATE 0xf46a0002 ++#define F367CAB_MODMAP_STATE 0xf46a0001 ++ ++/* EQU_I_TESTTAP_L */ ++#define R367CAB_EQU_I_TESTTAP_L 0xf474 ++#define F367CAB_I_TEST_TAP_L 0xf47400ff ++ ++/* EQU_I_TESTTAP_M */ ++#define R367CAB_EQU_I_TESTTAP_M 0xf475 ++#define F367CAB_I_TEST_TAP_M 0xf47500ff ++ ++/* EQU_I_TESTTAP_H */ ++#define R367CAB_EQU_I_TESTTAP_H 0xf476 ++#define F367CAB_I_TEST_TAP_H 0xf476001f ++ ++/* EQU_TESTAP_CFG */ ++#define R367CAB_EQU_TESTAP_CFG 0xf477 ++#define F367CAB_TEST_FFE_DFE_SEL 0xf4770040 ++#define F367CAB_TEST_TAP_SELECT 0xf477003f ++ ++/* EQU_Q_TESTTAP_L */ ++#define R367CAB_EQU_Q_TESTTAP_L 0xf478 ++#define F367CAB_Q_TEST_TAP_L 0xf47800ff ++ ++/* EQU_Q_TESTTAP_M */ ++#define R367CAB_EQU_Q_TESTTAP_M 0xf479 ++#define F367CAB_Q_TEST_TAP_M 0xf47900ff ++ ++/* EQU_Q_TESTTAP_H */ ++#define R367CAB_EQU_Q_TESTTAP_H 0xf47a ++#define F367CAB_Q_TEST_TAP_H 0xf47a001f ++ ++/* EQU_TAP_CTRL */ ++#define R367CAB_EQU_TAP_CTRL 0xf47b ++#define F367CAB_MTAP_FRZ 0xf47b0010 ++#define F367CAB_PRE_FREEZE 0xf47b0008 ++#define F367CAB_DFE_TAPMON_EN 0xf47b0004 ++#define F367CAB_FFE_TAPMON_EN 0xf47b0002 ++#define F367CAB_MTAP_ONLY 0xf47b0001 ++ ++/* EQU_CTR_CRL_CONTROL_L */ ++#define R367CAB_EQU_CTR_CRL_CONTROL_L 0xf47c ++#define F367CAB_EQU_CTR_CRL_CONTROL_LO 0xf47c00ff ++ ++/* EQU_CTR_CRL_CONTROL_H */ ++#define R367CAB_EQU_CTR_CRL_CONTROL_H 0xf47d ++#define F367CAB_EQU_CTR_CRL_CONTROL_HI 0xf47d00ff ++ ++/* EQU_CTR_HIPOW_L */ ++#define R367CAB_EQU_CTR_HIPOW_L 0xf47e ++#define F367CAB_CTR_HIPOW_L 0xf47e00ff ++ ++/* EQU_CTR_HIPOW_H */ ++#define R367CAB_EQU_CTR_HIPOW_H 0xf47f ++#define F367CAB_CTR_HIPOW_H 0xf47f00ff ++ ++/* EQU_I_EQU_LO */ ++#define R367CAB_EQU_I_EQU_LO 0xf480 ++#define F367CAB_EQU_I_EQU_L 0xf48000ff ++ ++/* EQU_I_EQU_HI */ ++#define R367CAB_EQU_I_EQU_HI 0xf481 ++#define F367CAB_EQU_I_EQU_H 0xf4810003 ++ ++/* EQU_Q_EQU_LO */ ++#define R367CAB_EQU_Q_EQU_LO 0xf482 ++#define F367CAB_EQU_Q_EQU_L 0xf48200ff ++ ++/* EQU_Q_EQU_HI */ ++#define R367CAB_EQU_Q_EQU_HI 0xf483 ++#define F367CAB_EQU_Q_EQU_H 0xf4830003 ++ ++/* EQU_MAPPER */ ++#define R367CAB_EQU_MAPPER 0xf484 ++#define F367CAB_QUAD_AUTO 0xf4840080 ++#define F367CAB_QUAD_INV 0xf4840040 ++#define F367CAB_QAM_MODE 0xf4840007 ++ ++/* EQU_SWEEP_RATE */ ++#define R367CAB_EQU_SWEEP_RATE 0xf485 ++#define F367CAB_SNR_PER 0xf48500c0 ++#define F367CAB_SWEEP_RATE 0xf485003f ++ ++/* EQU_SNR_LO */ ++#define R367CAB_EQU_SNR_LO 0xf486 ++#define F367CAB_SNR_LO 0xf48600ff ++ ++/* EQU_SNR_HI */ ++#define R367CAB_EQU_SNR_HI 0xf487 ++#define F367CAB_SNR_HI 0xf48700ff ++ ++/* EQU_GAMMA_LO */ ++#define R367CAB_EQU_GAMMA_LO 0xf488 ++#define F367CAB_GAMMA_LO 0xf48800ff ++ ++/* EQU_GAMMA_HI */ ++#define R367CAB_EQU_GAMMA_HI 0xf489 ++#define F367CAB_GAMMA_ME 0xf48900ff ++ ++/* EQU_ERR_GAIN */ ++#define R367CAB_EQU_ERR_GAIN 0xf48a ++#define F367CAB_EQA1MU 0xf48a0070 ++#define F367CAB_CRL2MU 0xf48a000e ++#define F367CAB_GAMMA_HI 0xf48a0001 ++ ++/* EQU_RADIUS */ ++#define R367CAB_EQU_RADIUS 0xf48b ++#define F367CAB_RADIUS 0xf48b00ff ++ ++/* EQU_FFE_MAINTAP */ ++#define R367CAB_EQU_FFE_MAINTAP 0xf48c ++#define F367CAB_FFE_MAINTAP_INIT 0xf48c00ff ++ ++/* EQU_FFE_LEAKAGE */ ++#define R367CAB_EQU_FFE_LEAKAGE 0xf48e ++#define F367CAB_LEAK_PER 0xf48e00f0 ++#define F367CAB_EQU_OUTSEL 0xf48e0002 ++#define F367CAB_PNT2dFE 0xf48e0001 ++ ++/* EQU_FFE_MAINTAP_POS */ ++#define R367CAB_EQU_FFE_MAINTAP_POS 0xf48f ++#define F367CAB_FFE_LEAK_EN 0xf48f0080 ++#define F367CAB_DFE_LEAK_EN 0xf48f0040 ++#define F367CAB_FFE_MAINTAP_POS 0xf48f003f ++ ++/* EQU_GAIN_WIDE */ ++#define R367CAB_EQU_GAIN_WIDE 0xf490 ++#define F367CAB_DFE_GAIN_WIDE 0xf49000f0 ++#define F367CAB_FFE_GAIN_WIDE 0xf490000f ++ ++/* EQU_GAIN_NARROW */ ++#define R367CAB_EQU_GAIN_NARROW 0xf491 ++#define F367CAB_DFE_GAIN_NARROW 0xf49100f0 ++#define F367CAB_FFE_GAIN_NARROW 0xf491000f ++ ++/* EQU_CTR_LPF_GAIN */ ++#define R367CAB_EQU_CTR_LPF_GAIN 0xf492 ++#define F367CAB_CTR_GTO 0xf4920080 ++#define F367CAB_CTR_GDIR 0xf4920070 ++#define F367CAB_SWEEP_EN 0xf4920008 ++#define F367CAB_CTR_GINT 0xf4920007 ++ ++/* EQU_CRL_LPF_GAIN */ ++#define R367CAB_EQU_CRL_LPF_GAIN 0xf493 ++#define F367CAB_CRL_GTO 0xf4930080 ++#define F367CAB_CRL_GDIR 0xf4930070 ++#define F367CAB_SWEEP_DIR 0xf4930008 ++#define F367CAB_CRL_GINT 0xf4930007 ++ ++/* EQU_GLOBAL_GAIN */ ++#define R367CAB_EQU_GLOBAL_GAIN 0xf494 ++#define F367CAB_CRL_GAIN 0xf49400f8 ++#define F367CAB_CTR_INC_GAIN 0xf4940004 ++#define F367CAB_CTR_FRAC 0xf4940003 ++ ++/* EQU_CRL_LD_SEN */ ++#define R367CAB_EQU_CRL_LD_SEN 0xf495 ++#define F367CAB_CTR_BADPOINT_EN 0xf4950080 ++#define F367CAB_CTR_GAIN 0xf4950070 ++#define F367CAB_LIMANEN 0xf4950008 ++#define F367CAB_CRL_LD_SEN 0xf4950007 ++ ++/* EQU_CRL_LD_VAL */ ++#define R367CAB_EQU_CRL_LD_VAL 0xf496 ++#define F367CAB_CRL_BISTH_LIMIT 0xf4960080 ++#define F367CAB_CARE_EN 0xf4960040 ++#define F367CAB_CRL_LD_PER 0xf4960030 ++#define F367CAB_CRL_LD_WST 0xf496000c ++#define F367CAB_CRL_LD_TFS 0xf4960003 ++ ++/* EQU_CRL_TFR */ ++#define R367CAB_EQU_CRL_TFR 0xf497 ++#define F367CAB_CRL_LD_TFR 0xf49700ff ++ ++/* EQU_CRL_BISTH_LO */ ++#define R367CAB_EQU_CRL_BISTH_LO 0xf498 ++#define F367CAB_CRL_BISTH_LO 0xf49800ff ++ ++/* EQU_CRL_BISTH_HI */ ++#define R367CAB_EQU_CRL_BISTH_HI 0xf499 ++#define F367CAB_CRL_BISTH_HI 0xf49900ff ++ ++/* EQU_SWEEP_RANGE_LO */ ++#define R367CAB_EQU_SWEEP_RANGE_LO 0xf49a ++#define F367CAB_SWEEP_RANGE_LO 0xf49a00ff ++ ++/* EQU_SWEEP_RANGE_HI */ ++#define R367CAB_EQU_SWEEP_RANGE_HI 0xf49b ++#define F367CAB_SWEEP_RANGE_HI 0xf49b00ff ++ ++/* EQU_CRL_LIMITER */ ++#define R367CAB_EQU_CRL_LIMITER 0xf49c ++#define F367CAB_BISECTOR_EN 0xf49c0080 ++#define F367CAB_PHEST128_EN 0xf49c0040 ++#define F367CAB_CRL_LIM 0xf49c003f ++ ++/* EQU_MODULUS_MAP */ ++#define R367CAB_EQU_MODULUS_MAP 0xf49d ++#define F367CAB_PNT_DEPTH 0xf49d00e0 ++#define F367CAB_MODULUS_CMP 0xf49d001f ++ ++/* EQU_PNT_GAIN */ ++#define R367CAB_EQU_PNT_GAIN 0xf49e ++#define F367CAB_PNT_EN 0xf49e0080 ++#define F367CAB_MODULUSMAP_EN 0xf49e0040 ++#define F367CAB_PNT_GAIN 0xf49e003f ++ ++/* FEC_AC_CTR_0 */ ++#define R367CAB_FEC_AC_CTR_0 0xf4a8 ++#define F367CAB_BE_BYPASS 0xf4a80020 ++#define F367CAB_REFRESH47 0xf4a80010 ++#define F367CAB_CT_NBST 0xf4a80008 ++#define F367CAB_TEI_ENA 0xf4a80004 ++#define F367CAB_DS_ENA 0xf4a80002 ++#define F367CAB_TSMF_EN 0xf4a80001 ++ ++/* FEC_AC_CTR_1 */ ++#define R367CAB_FEC_AC_CTR_1 0xf4a9 ++#define F367CAB_DEINT_DEPTH 0xf4a900ff ++ ++/* FEC_AC_CTR_2 */ ++#define R367CAB_FEC_AC_CTR_2 0xf4aa ++#define F367CAB_DEINT_M 0xf4aa00f8 ++#define F367CAB_DIS_UNLOCK 0xf4aa0004 ++#define F367CAB_DESCR_MODE 0xf4aa0003 ++ ++/* FEC_AC_CTR_3 */ ++#define R367CAB_FEC_AC_CTR_3 0xf4ab ++#define F367CAB_DI_UNLOCK 0xf4ab0080 ++#define F367CAB_DI_FREEZE 0xf4ab0040 ++#define F367CAB_MISMATCH 0xf4ab0030 ++#define F367CAB_ACQ_MODE 0xf4ab000c ++#define F367CAB_TRK_MODE 0xf4ab0003 ++ ++/* FEC_STATUS */ ++#define R367CAB_FEC_STATUS 0xf4ac ++#define F367CAB_DEINT_SMCNTR 0xf4ac00e0 ++#define F367CAB_DEINT_SYNCSTATE 0xf4ac0018 ++#define F367CAB_DEINT_SYNLOST 0xf4ac0004 ++#define F367CAB_DESCR_SYNCSTATE 0xf4ac0002 ++ ++/* RS_COUNTER_0 */ ++#define R367CAB_RS_COUNTER_0 0xf4ae ++#define F367CAB_BK_CT_L 0xf4ae00ff ++ ++/* RS_COUNTER_1 */ ++#define R367CAB_RS_COUNTER_1 0xf4af ++#define F367CAB_BK_CT_H 0xf4af00ff ++ ++/* RS_COUNTER_2 */ ++#define R367CAB_RS_COUNTER_2 0xf4b0 ++#define F367CAB_CORR_CT_L 0xf4b000ff ++ ++/* RS_COUNTER_3 */ ++#define R367CAB_RS_COUNTER_3 0xf4b1 ++#define F367CAB_CORR_CT_H 0xf4b100ff ++ ++/* RS_COUNTER_4 */ ++#define R367CAB_RS_COUNTER_4 0xf4b2 ++#define F367CAB_UNCORR_CT_L 0xf4b200ff ++ ++/* RS_COUNTER_5 */ ++#define R367CAB_RS_COUNTER_5 0xf4b3 ++#define F367CAB_UNCORR_CT_H 0xf4b300ff ++ ++/* BERT_0 */ ++#define R367CAB_BERT_0 0xf4b4 ++#define F367CAB_RS_NOCORR 0xf4b40004 ++#define F367CAB_CT_HOLD 0xf4b40002 ++#define F367CAB_CT_CLEAR 0xf4b40001 ++ ++/* BERT_1 */ ++#define R367CAB_BERT_1 0xf4b5 ++#define F367CAB_BERT_ON 0xf4b50020 ++#define F367CAB_BERT_ERR_SRC 0xf4b50010 ++#define F367CAB_BERT_ERR_MODE 0xf4b50008 ++#define F367CAB_BERT_NBYTE 0xf4b50007 ++ ++/* BERT_2 */ ++#define R367CAB_BERT_2 0xf4b6 ++#define F367CAB_BERT_ERRCOUNT_L 0xf4b600ff ++ ++/* BERT_3 */ ++#define R367CAB_BERT_3 0xf4b7 ++#define F367CAB_BERT_ERRCOUNT_H 0xf4b700ff ++ ++/* OUTFORMAT_0 */ ++#define R367CAB_OUTFORMAT_0 0xf4b8 ++#define F367CAB_CLK_POLARITY 0xf4b80080 ++#define F367CAB_FEC_TYPE 0xf4b80040 ++#define F367CAB_SYNC_STRIP 0xf4b80008 ++#define F367CAB_TS_SWAP 0xf4b80004 ++#define F367CAB_OUTFORMAT 0xf4b80003 ++ ++/* OUTFORMAT_1 */ ++#define R367CAB_OUTFORMAT_1 0xf4b9 ++#define F367CAB_CI_DIVRANGE 0xf4b900ff ++ ++/* SMOOTHER_2 */ ++#define R367CAB_SMOOTHER_2 0xf4be ++#define F367CAB_FIFO_BYPASS 0xf4be0020 ++ ++/* TSMF_CTRL_0 */ ++#define R367CAB_TSMF_CTRL_0 0xf4c0 ++#define F367CAB_TS_NUMBER 0xf4c0001e ++#define F367CAB_SEL_MODE 0xf4c00001 ++ ++/* TSMF_CTRL_1 */ ++#define R367CAB_TSMF_CTRL_1 0xf4c1 ++#define F367CAB_CHECK_ERROR_BIT 0xf4c10080 ++#define F367CAB_CHCK_F_SYNC 0xf4c10040 ++#define F367CAB_H_MODE 0xf4c10008 ++#define F367CAB_D_V_MODE 0xf4c10004 ++#define F367CAB_MODE 0xf4c10003 ++ ++/* TSMF_CTRL_3 */ ++#define R367CAB_TSMF_CTRL_3 0xf4c3 ++#define F367CAB_SYNC_IN_COUNT 0xf4c300f0 ++#define F367CAB_SYNC_OUT_COUNT 0xf4c3000f ++ ++/* TS_ON_ID_0 */ ++#define R367CAB_TS_ON_ID_0 0xf4c4 ++#define F367CAB_TS_ID_L 0xf4c400ff ++ ++/* TS_ON_ID_1 */ ++#define R367CAB_TS_ON_ID_1 0xf4c5 ++#define F367CAB_TS_ID_H 0xf4c500ff ++ ++/* TS_ON_ID_2 */ ++#define R367CAB_TS_ON_ID_2 0xf4c6 ++#define F367CAB_ON_ID_L 0xf4c600ff ++ ++/* TS_ON_ID_3 */ ++#define R367CAB_TS_ON_ID_3 0xf4c7 ++#define F367CAB_ON_ID_H 0xf4c700ff ++ ++/* RE_STATUS_0 */ ++#define R367CAB_RE_STATUS_0 0xf4c8 ++#define F367CAB_RECEIVE_STATUS_L 0xf4c800ff ++ ++/* RE_STATUS_1 */ ++#define R367CAB_RE_STATUS_1 0xf4c9 ++#define F367CAB_RECEIVE_STATUS_LH 0xf4c900ff ++ ++/* RE_STATUS_2 */ ++#define R367CAB_RE_STATUS_2 0xf4ca ++#define F367CAB_RECEIVE_STATUS_HL 0xf4ca00ff ++ ++/* RE_STATUS_3 */ ++#define R367CAB_RE_STATUS_3 0xf4cb ++#define F367CAB_RECEIVE_STATUS_HH 0xf4cb003f ++ ++/* TS_STATUS_0 */ ++#define R367CAB_TS_STATUS_0 0xf4cc ++#define F367CAB_TS_STATUS_L 0xf4cc00ff ++ ++/* TS_STATUS_1 */ ++#define R367CAB_TS_STATUS_1 0xf4cd ++#define F367CAB_TS_STATUS_H 0xf4cd007f ++ ++/* TS_STATUS_2 */ ++#define R367CAB_TS_STATUS_2 0xf4ce ++#define F367CAB_ERROR 0xf4ce0080 ++#define F367CAB_EMERGENCY 0xf4ce0040 ++#define F367CAB_CRE_TS 0xf4ce0030 ++#define F367CAB_VER 0xf4ce000e ++#define F367CAB_M_LOCK 0xf4ce0001 ++ ++/* TS_STATUS_3 */ ++#define R367CAB_TS_STATUS_3 0xf4cf ++#define F367CAB_UPDATE_READY 0xf4cf0080 ++#define F367CAB_END_FRAME_HEADER 0xf4cf0040 ++#define F367CAB_CONTCNT 0xf4cf0020 ++#define F367CAB_TS_IDENTIFIER_SEL 0xf4cf000f ++ ++/* T_O_ID_0 */ ++#define R367CAB_T_O_ID_0 0xf4d0 ++#define F367CAB_ON_ID_I_L 0xf4d000ff ++ ++/* T_O_ID_1 */ ++#define R367CAB_T_O_ID_1 0xf4d1 ++#define F367CAB_ON_ID_I_H 0xf4d100ff ++ ++/* T_O_ID_2 */ ++#define R367CAB_T_O_ID_2 0xf4d2 ++#define F367CAB_TS_ID_I_L 0xf4d200ff ++ ++/* T_O_ID_3 */ ++#define R367CAB_T_O_ID_3 0xf4d3 ++#define F367CAB_TS_ID_I_H 0xf4d300ff ++ ++#define STV0367CAB_NBREGS 187 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stv0900.h b/drivers/media/dvb-frontends/stv0900.h +new file mode 100644 +index 0000000..91c7ee8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0900.h +@@ -0,0 +1,74 @@ ++/* ++ * stv0900.h ++ * ++ * Driver for ST STV0900 satellite demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0900_H ++#define STV0900_H ++ ++#include ++#include "dvb_frontend.h" ++ ++struct stv0900_reg { ++ u16 addr; ++ u8 val; ++}; ++ ++struct stv0900_config { ++ u8 demod_address; ++ u8 demod_mode; ++ u32 xtal; ++ u8 clkmode;/* 0 for CLKI, 2 for XTALI */ ++ ++ u8 diseqc_mode; ++ ++ u8 path1_mode; ++ u8 path2_mode; ++ struct stv0900_reg *ts_config_regs; ++ u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */ ++ u8 tun2_maddress; ++ u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ ++ u8 tun2_adc; ++ u8 tun1_type;/* for now 3 for stb6100 auto, else - software */ ++ u8 tun2_type; ++ /* Set device param to start dma */ ++ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); ++ /* Hook for Lock LED */ ++ void (*set_lock_led)(struct dvb_frontend *fe, int offon); ++}; ++ ++#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, ++ struct i2c_adapter *i2c, int demod); ++#else ++static inline struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, ++ struct i2c_adapter *i2c, int demod) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif ++ +diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c +new file mode 100644 +index 0000000..0fb34e1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0900_core.c +@@ -0,0 +1,1985 @@ ++/* ++ * stv0900_core.c ++ * ++ * Driver for ST STV0900 satellite demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "stv0900.h" ++#include "stv0900_reg.h" ++#include "stv0900_priv.h" ++#include "stv0900_init.h" ++ ++int stvdebug = 1; ++module_param_named(debug, stvdebug, int, 0644); ++ ++/* internal params node */ ++struct stv0900_inode { ++ /* pointer for internal params, one for each pair of demods */ ++ struct stv0900_internal *internal; ++ struct stv0900_inode *next_inode; ++}; ++ ++/* first internal params */ ++static struct stv0900_inode *stv0900_first_inode; ++ ++/* find chip by i2c adapter and i2c address */ ++static struct stv0900_inode *find_inode(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr) ++{ ++ struct stv0900_inode *temp_chip = stv0900_first_inode; ++ ++ if (temp_chip != NULL) { ++ /* ++ Search of the last stv0900 chip or ++ find it by i2c adapter and i2c address */ ++ while ((temp_chip != NULL) && ++ ((temp_chip->internal->i2c_adap != i2c_adap) || ++ (temp_chip->internal->i2c_addr != i2c_addr))) ++ ++ temp_chip = temp_chip->next_inode; ++ ++ } ++ ++ return temp_chip; ++} ++ ++/* deallocating chip */ ++static void remove_inode(struct stv0900_internal *internal) ++{ ++ struct stv0900_inode *prev_node = stv0900_first_inode; ++ struct stv0900_inode *del_node = find_inode(internal->i2c_adap, ++ internal->i2c_addr); ++ ++ if (del_node != NULL) { ++ if (del_node == stv0900_first_inode) { ++ stv0900_first_inode = del_node->next_inode; ++ } else { ++ while (prev_node->next_inode != del_node) ++ prev_node = prev_node->next_inode; ++ ++ if (del_node->next_inode == NULL) ++ prev_node->next_inode = NULL; ++ else ++ prev_node->next_inode = ++ prev_node->next_inode->next_inode; ++ } ++ ++ kfree(del_node); ++ } ++} ++ ++/* allocating new chip */ ++static struct stv0900_inode *append_internal(struct stv0900_internal *internal) ++{ ++ struct stv0900_inode *new_node = stv0900_first_inode; ++ ++ if (new_node == NULL) { ++ new_node = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL); ++ stv0900_first_inode = new_node; ++ } else { ++ while (new_node->next_inode != NULL) ++ new_node = new_node->next_inode; ++ ++ new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), ++ GFP_KERNEL); ++ if (new_node->next_inode != NULL) ++ new_node = new_node->next_inode; ++ else ++ new_node = NULL; ++ } ++ ++ if (new_node != NULL) { ++ new_node->internal = internal; ++ new_node->next_inode = NULL; ++ } ++ ++ return new_node; ++} ++ ++s32 ge2comp(s32 a, s32 width) ++{ ++ if (width == 32) ++ return a; ++ else ++ return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a; ++} ++ ++void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr, ++ u8 reg_data) ++{ ++ u8 data[3]; ++ int ret; ++ struct i2c_msg i2cmsg = { ++ .addr = intp->i2c_addr, ++ .flags = 0, ++ .len = 3, ++ .buf = data, ++ }; ++ ++ data[0] = MSB(reg_addr); ++ data[1] = LSB(reg_addr); ++ data[2] = reg_data; ++ ++ ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1); ++ if (ret != 1) ++ dprintk("%s: i2c error %d\n", __func__, ret); ++} ++ ++u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) ++{ ++ int ret; ++ u8 b0[] = { MSB(reg), LSB(reg) }; ++ u8 buf = 0; ++ struct i2c_msg msg[] = { ++ { ++ .addr = intp->i2c_addr, ++ .flags = 0, ++ .buf = b0, ++ .len = 2, ++ }, { ++ .addr = intp->i2c_addr, ++ .flags = I2C_M_RD, ++ .buf = &buf, ++ .len = 1, ++ }, ++ }; ++ ++ ret = i2c_transfer(intp->i2c_adap, msg, 2); ++ if (ret != 2) ++ dprintk("%s: i2c error %d, reg[0x%02x]\n", ++ __func__, ret, reg); ++ ++ return buf; ++} ++ ++static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) ++{ ++ u8 position = 0, i = 0; ++ ++ (*mask) = label & 0xff; ++ ++ while ((position == 0) && (i < 8)) { ++ position = ((*mask) >> i) & 0x01; ++ i++; ++ } ++ ++ (*pos) = (i - 1); ++} ++ ++void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) ++{ ++ u8 reg, mask, pos; ++ ++ reg = stv0900_read_reg(intp, (label >> 16) & 0xffff); ++ extract_mask_pos(label, &mask, &pos); ++ ++ val = mask & (val << pos); ++ ++ reg = (reg & (~mask)) | val; ++ stv0900_write_reg(intp, (label >> 16) & 0xffff, reg); ++ ++} ++ ++u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) ++{ ++ u8 val = 0xff; ++ u8 mask, pos; ++ ++ extract_mask_pos(label, &mask, &pos); ++ ++ val = stv0900_read_reg(intp, label >> 16); ++ val = (val & mask) >> pos; ++ ++ return val; ++} ++ ++static enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) ++{ ++ s32 i; ++ ++ if (intp == NULL) ++ return STV0900_INVALID_HANDLE; ++ ++ intp->chip_id = stv0900_read_reg(intp, R0900_MID); ++ ++ if (intp->errs != STV0900_NO_ERROR) ++ return intp->errs; ++ ++ /*Startup sequence*/ ++ stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c); ++ stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c); ++ msleep(3); ++ stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c); ++ stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f); ++ stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20); ++ stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20); ++ stv0900_write_reg(intp, R0900_NCOARSE, 0x13); ++ msleep(3); ++ stv0900_write_reg(intp, R0900_I2CCFG, 0x08); ++ ++ switch (intp->clkmode) { ++ case 0: ++ case 2: ++ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 ++ | intp->clkmode); ++ break; ++ default: ++ /* preserve SELOSCI bit */ ++ i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); ++ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i); ++ break; ++ } ++ ++ msleep(3); ++ for (i = 0; i < 181; i++) ++ stv0900_write_reg(intp, STV0900_InitVal[i][0], ++ STV0900_InitVal[i][1]); ++ ++ if (stv0900_read_reg(intp, R0900_MID) >= 0x20) { ++ stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c); ++ for (i = 0; i < 32; i++) ++ stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0], ++ STV0900_Cut20_AddOnVal[i][1]); ++ } ++ ++ stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c); ++ stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c); ++ ++ stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01); ++ stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21); ++ ++ stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20); ++ stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20); ++ ++ stv0900_write_reg(intp, R0900_TSTRES0, 0x80); ++ stv0900_write_reg(intp, R0900_TSTRES0, 0x00); ++ ++ return STV0900_NO_ERROR; ++} ++ ++static u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) ++{ ++ u32 mclk = 90000000, div = 0, ad_div = 0; ++ ++ div = stv0900_get_bits(intp, F0900_M_DIV); ++ ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); ++ ++ mclk = (div + 1) * ext_clk / ad_div; ++ ++ dprintk("%s: Calculated Mclk = %d\n", __func__, mclk); ++ ++ return mclk; ++} ++ ++static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) ++{ ++ u32 m_div, clk_sel; ++ ++ if (intp == NULL) ++ return STV0900_INVALID_HANDLE; ++ ++ if (intp->errs) ++ return STV0900_I2C_ERROR; ++ ++ dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, ++ intp->quartz); ++ ++ clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); ++ m_div = ((clk_sel * mclk) / intp->quartz) - 1; ++ stv0900_write_bits(intp, F0900_M_DIV, m_div); ++ intp->mclk = stv0900_get_mclk_freq(intp, ++ intp->quartz); ++ ++ /*Set the DiseqC frequency to 22KHz */ ++ /* ++ Formula: ++ DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) ++ DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) ++ */ ++ m_div = intp->mclk / 704000; ++ stv0900_write_reg(intp, R0900_P1_F22TX, m_div); ++ stv0900_write_reg(intp, R0900_P1_F22RX, m_div); ++ ++ stv0900_write_reg(intp, R0900_P2_F22TX, m_div); ++ stv0900_write_reg(intp, R0900_P2_F22RX, m_div); ++ ++ if ((intp->errs)) ++ return STV0900_I2C_ERROR; ++ ++ return STV0900_NO_ERROR; ++} ++ ++static u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 lsb, msb, hsb, err_val; ++ ++ switch (cntr) { ++ case 0: ++ default: ++ hsb = stv0900_get_bits(intp, ERR_CNT12); ++ msb = stv0900_get_bits(intp, ERR_CNT11); ++ lsb = stv0900_get_bits(intp, ERR_CNT10); ++ break; ++ case 1: ++ hsb = stv0900_get_bits(intp, ERR_CNT22); ++ msb = stv0900_get_bits(intp, ERR_CNT21); ++ lsb = stv0900_get_bits(intp, ERR_CNT20); ++ break; ++ } ++ ++ err_val = (hsb << 16) + (msb << 8) + (lsb); ++ ++ return err_val; ++} ++ ++static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ stv0900_write_bits(intp, I2CT_ON, enable); ++ ++ return 0; ++} ++ ++static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp, ++ enum fe_stv0900_clock_type path1_ts, ++ enum fe_stv0900_clock_type path2_ts) ++{ ++ ++ dprintk("%s\n", __func__); ++ ++ if (intp->chip_id >= 0x20) { ++ switch (path1_ts) { ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ switch (path2_ts) { ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ stv0900_write_reg(intp, R0900_TSGENERAL, ++ 0x00); ++ break; ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_reg(intp, R0900_TSGENERAL, ++ 0x06); ++ stv0900_write_bits(intp, ++ F0900_P1_TSFIFO_MANSPEED, 3); ++ stv0900_write_bits(intp, ++ F0900_P2_TSFIFO_MANSPEED, 0); ++ stv0900_write_reg(intp, ++ R0900_P1_TSSPEED, 0x14); ++ stv0900_write_reg(intp, ++ R0900_P2_TSSPEED, 0x28); ++ break; ++ } ++ break; ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ switch (path2_ts) { ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ stv0900_write_reg(intp, ++ R0900_TSGENERAL, 0x0C); ++ break; ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_reg(intp, ++ R0900_TSGENERAL, 0x0A); ++ dprintk("%s: 0x0a\n", __func__); ++ break; ++ } ++ break; ++ } ++ } else { ++ switch (path1_ts) { ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ switch (path2_ts) { ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ stv0900_write_reg(intp, R0900_TSGENERAL1X, ++ 0x10); ++ break; ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_reg(intp, R0900_TSGENERAL1X, ++ 0x16); ++ stv0900_write_bits(intp, ++ F0900_P1_TSFIFO_MANSPEED, 3); ++ stv0900_write_bits(intp, ++ F0900_P2_TSFIFO_MANSPEED, 0); ++ stv0900_write_reg(intp, R0900_P1_TSSPEED, ++ 0x14); ++ stv0900_write_reg(intp, R0900_P2_TSSPEED, ++ 0x28); ++ break; ++ } ++ ++ break; ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ switch (path2_ts) { ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ case STV0900_SERIAL_CONT_CLOCK: ++ default: ++ stv0900_write_reg(intp, R0900_TSGENERAL1X, ++ 0x14); ++ break; ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_reg(intp, R0900_TSGENERAL1X, ++ 0x12); ++ dprintk("%s: 0x12\n", __func__); ++ break; ++ } ++ ++ break; ++ } ++ } ++ ++ switch (path1_ts) { ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); ++ break; ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); ++ break; ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); ++ break; ++ case STV0900_SERIAL_CONT_CLOCK: ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); ++ stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); ++ break; ++ default: ++ break; ++ } ++ ++ switch (path2_ts) { ++ case STV0900_PARALLEL_PUNCT_CLOCK: ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); ++ break; ++ case STV0900_DVBCI_CLOCK: ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); ++ break; ++ case STV0900_SERIAL_PUNCT_CLOCK: ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); ++ break; ++ case STV0900_SERIAL_CONT_CLOCK: ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); ++ stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); ++ break; ++ default: ++ break; ++ } ++ ++ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); ++ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); ++ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); ++ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); ++} ++ ++void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, ++ u32 bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ ++ if (tuner_ops->set_frequency) { ++ if ((tuner_ops->set_frequency(fe, frequency)) < 0) ++ dprintk("%s: Invalid parameter\n", __func__); ++ else ++ dprintk("%s: Frequency=%d\n", __func__, frequency); ++ ++ } ++ ++ if (tuner_ops->set_bandwidth) { ++ if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) ++ dprintk("%s: Invalid parameter\n", __func__); ++ else ++ dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); ++ ++ } ++} ++ ++void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ ++ if (tuner_ops->set_bandwidth) { ++ if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) ++ dprintk("%s: Invalid parameter\n", __func__); ++ else ++ dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); ++ ++ } ++} ++ ++u32 stv0900_get_freq_auto(struct stv0900_internal *intp, int demod) ++{ ++ u32 freq, round; ++ /* Formulat : ++ Tuner_Frequency(MHz) = Regs / 64 ++ Tuner_granularity(MHz) = Regs / 2048 ++ real_Tuner_Frequency = Tuner_Frequency(MHz) - Tuner_granularity(MHz) ++ */ ++ freq = (stv0900_get_bits(intp, TUN_RFFREQ2) << 10) + ++ (stv0900_get_bits(intp, TUN_RFFREQ1) << 2) + ++ stv0900_get_bits(intp, TUN_RFFREQ0); ++ ++ freq = (freq * 1000) / 64; ++ ++ round = (stv0900_get_bits(intp, TUN_RFRESTE1) >> 2) + ++ stv0900_get_bits(intp, TUN_RFRESTE0); ++ ++ round = (round * 1000) / 2048; ++ ++ return freq + round; ++} ++ ++void stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, ++ u32 Bandwidth, int demod) ++{ ++ u32 tunerFrequency; ++ /* Formulat: ++ Tuner_frequency_reg= Frequency(MHz)*64 ++ */ ++ tunerFrequency = (Frequency * 64) / 1000; ++ ++ stv0900_write_bits(intp, TUN_RFFREQ2, (tunerFrequency >> 10)); ++ stv0900_write_bits(intp, TUN_RFFREQ1, (tunerFrequency >> 2) & 0xff); ++ stv0900_write_bits(intp, TUN_RFFREQ0, (tunerFrequency & 0x03)); ++ /* Low Pass Filter = BW /2 (MHz)*/ ++ stv0900_write_bits(intp, TUN_BW, Bandwidth / 2000000); ++ /* Tuner Write trig */ ++ stv0900_write_reg(intp, TNRLD, 1); ++} ++ ++static s32 stv0900_get_rf_level(struct stv0900_internal *intp, ++ const struct stv0900_table *lookup, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 agc_gain = 0, ++ imin, ++ imax, ++ i, ++ rf_lvl = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ if ((lookup == NULL) || (lookup->size <= 0)) ++ return 0; ++ ++ agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), ++ stv0900_get_bits(intp, AGCIQ_VALUE0)); ++ ++ imin = 0; ++ imax = lookup->size - 1; ++ if (INRANGE(lookup->table[imin].regval, agc_gain, ++ lookup->table[imax].regval)) { ++ while ((imax - imin) > 1) { ++ i = (imax + imin) >> 1; ++ ++ if (INRANGE(lookup->table[imin].regval, ++ agc_gain, ++ lookup->table[i].regval)) ++ imax = i; ++ else ++ imin = i; ++ } ++ ++ rf_lvl = (s32)agc_gain - lookup->table[imin].regval; ++ rf_lvl *= (lookup->table[imax].realval - ++ lookup->table[imin].realval); ++ rf_lvl /= (lookup->table[imax].regval - ++ lookup->table[imin].regval); ++ rf_lvl += lookup->table[imin].realval; ++ } else if (agc_gain > lookup->table[0].regval) ++ rf_lvl = 5; ++ else if (agc_gain < lookup->table[lookup->size-1].regval) ++ rf_lvl = -100; ++ ++ dprintk("%s: RFLevel = %d\n", __func__, rf_lvl); ++ ++ return rf_lvl; ++} ++ ++static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *internal = state->internal; ++ s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf, ++ state->demod); ++ ++ rflevel = (rflevel + 100) * (65535 / 70); ++ if (rflevel < 0) ++ rflevel = 0; ++ ++ if (rflevel > 65535) ++ rflevel = 65535; ++ ++ *strength = rflevel; ++ ++ return 0; ++} ++ ++static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, ++ const struct stv0900_table *lookup) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ s32 c_n = -100, ++ regval, ++ imin, ++ imax, ++ i, ++ noise_field1, ++ noise_field0; ++ ++ dprintk("%s\n", __func__); ++ ++ if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { ++ noise_field1 = NOSPLHT_NORMED1; ++ noise_field0 = NOSPLHT_NORMED0; ++ } else { ++ noise_field1 = NOSDATAT_NORMED1; ++ noise_field0 = NOSDATAT_NORMED0; ++ } ++ ++ if (stv0900_get_bits(intp, LOCK_DEFINITIF)) { ++ if ((lookup != NULL) && lookup->size) { ++ regval = 0; ++ msleep(5); ++ for (i = 0; i < 16; i++) { ++ regval += MAKEWORD(stv0900_get_bits(intp, ++ noise_field1), ++ stv0900_get_bits(intp, ++ noise_field0)); ++ msleep(1); ++ } ++ ++ regval /= 16; ++ imin = 0; ++ imax = lookup->size - 1; ++ if (INRANGE(lookup->table[imin].regval, ++ regval, ++ lookup->table[imax].regval)) { ++ while ((imax - imin) > 1) { ++ i = (imax + imin) >> 1; ++ if (INRANGE(lookup->table[imin].regval, ++ regval, ++ lookup->table[i].regval)) ++ imax = i; ++ else ++ imin = i; ++ } ++ ++ c_n = ((regval - lookup->table[imin].regval) ++ * (lookup->table[imax].realval ++ - lookup->table[imin].realval) ++ / (lookup->table[imax].regval ++ - lookup->table[imin].regval)) ++ + lookup->table[imin].realval; ++ } else if (regval < lookup->table[imin].regval) ++ c_n = 1000; ++ } ++ } ++ ++ return c_n; ++} ++ ++static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ u8 err_val1, err_val0; ++ u32 header_err_val = 0; ++ ++ *ucblocks = 0x0; ++ if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { ++ /* DVB-S2 delineator errors count */ ++ ++ /* retreiving number for errnous headers */ ++ err_val1 = stv0900_read_reg(intp, BBFCRCKO1); ++ err_val0 = stv0900_read_reg(intp, BBFCRCKO0); ++ header_err_val = (err_val1 << 8) | err_val0; ++ ++ /* retreiving number for errnous packets */ ++ err_val1 = stv0900_read_reg(intp, UPCRCKO1); ++ err_val0 = stv0900_read_reg(intp, UPCRCKO0); ++ *ucblocks = (err_val1 << 8) | err_val0; ++ *ucblocks += header_err_val; ++ } ++ ++ return 0; ++} ++ ++static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ s32 snrlcl = stv0900_carr_get_quality(fe, ++ (const struct stv0900_table *)&stv0900_s2_cn); ++ snrlcl = (snrlcl + 30) * 384; ++ if (snrlcl < 0) ++ snrlcl = 0; ++ ++ if (snrlcl > 65535) ++ snrlcl = 65535; ++ ++ *snr = snrlcl; ++ ++ return 0; ++} ++ ++static u32 stv0900_get_ber(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 ber = 10000000, i; ++ s32 demod_state; ++ ++ demod_state = stv0900_get_bits(intp, HEADER_MODE); ++ ++ switch (demod_state) { ++ case STV0900_SEARCH: ++ case STV0900_PLH_DETECTED: ++ default: ++ ber = 10000000; ++ break; ++ case STV0900_DVBS_FOUND: ++ ber = 0; ++ for (i = 0; i < 5; i++) { ++ msleep(5); ++ ber += stv0900_get_err_count(intp, 0, demod); ++ } ++ ++ ber /= 5; ++ if (stv0900_get_bits(intp, PRFVIT)) { ++ ber *= 9766; ++ ber = ber >> 13; ++ } ++ ++ break; ++ case STV0900_DVBS2_FOUND: ++ ber = 0; ++ for (i = 0; i < 5; i++) { ++ msleep(5); ++ ber += stv0900_get_err_count(intp, 0, demod); ++ } ++ ++ ber /= 5; ++ if (stv0900_get_bits(intp, PKTDELIN_LOCK)) { ++ ber *= 9766; ++ ber = ber >> 13; ++ } ++ ++ break; ++ } ++ ++ return ber; ++} ++ ++static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *internal = state->internal; ++ ++ *ber = stv0900_get_ber(internal, state->demod); ++ ++ return 0; ++} ++ ++int stv0900_get_demod_lock(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod, s32 time_out) ++{ ++ s32 timer = 0, ++ lock = 0; ++ ++ enum fe_stv0900_search_state dmd_state; ++ ++ while ((timer < time_out) && (lock == 0)) { ++ dmd_state = stv0900_get_bits(intp, HEADER_MODE); ++ dprintk("Demod State = %d\n", dmd_state); ++ switch (dmd_state) { ++ case STV0900_SEARCH: ++ case STV0900_PLH_DETECTED: ++ default: ++ lock = 0; ++ break; ++ case STV0900_DVBS2_FOUND: ++ case STV0900_DVBS_FOUND: ++ lock = stv0900_get_bits(intp, LOCK_DEFINITIF); ++ break; ++ } ++ ++ if (lock == 0) ++ msleep(10); ++ ++ timer += 10; ++ } ++ ++ if (lock) ++ dprintk("DEMOD LOCK OK\n"); ++ else ++ dprintk("DEMOD LOCK FAIL\n"); ++ ++ return lock; ++} ++ ++void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 regflist, ++ i; ++ ++ dprintk("%s\n", __func__); ++ ++ regflist = MODCODLST0; ++ ++ for (i = 0; i < 16; i++) ++ stv0900_write_reg(intp, regflist + i, 0xff); ++} ++ ++void stv0900_activate_s2_modcod(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 matype, ++ mod_code, ++ fmod, ++ reg_index, ++ field_index; ++ ++ dprintk("%s\n", __func__); ++ ++ if (intp->chip_id <= 0x11) { ++ msleep(5); ++ ++ mod_code = stv0900_read_reg(intp, PLHMODCOD); ++ matype = mod_code & 0x3; ++ mod_code = (mod_code & 0x7f) >> 2; ++ ++ reg_index = MODCODLSTF - mod_code / 2; ++ field_index = mod_code % 2; ++ ++ switch (matype) { ++ case 0: ++ default: ++ fmod = 14; ++ break; ++ case 1: ++ fmod = 13; ++ break; ++ case 2: ++ fmod = 11; ++ break; ++ case 3: ++ fmod = 7; ++ break; ++ } ++ ++ if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910)) ++ && (matype <= 1)) { ++ if (field_index == 0) ++ stv0900_write_reg(intp, reg_index, ++ 0xf0 | fmod); ++ else ++ stv0900_write_reg(intp, reg_index, ++ (fmod << 4) | 0xf); ++ } ++ ++ } else if (intp->chip_id >= 0x12) { ++ for (reg_index = 0; reg_index < 7; reg_index++) ++ stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff); ++ ++ stv0900_write_reg(intp, MODCODLSTE, 0xff); ++ stv0900_write_reg(intp, MODCODLSTF, 0xcf); ++ for (reg_index = 0; reg_index < 8; reg_index++) ++ stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc); ++ ++ ++ } ++} ++ ++void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 reg_index; ++ ++ dprintk("%s\n", __func__); ++ ++ stv0900_write_reg(intp, MODCODLST0, 0xff); ++ stv0900_write_reg(intp, MODCODLST1, 0xf0); ++ stv0900_write_reg(intp, MODCODLSTF, 0x0f); ++ for (reg_index = 0; reg_index < 13; reg_index++) ++ stv0900_write_reg(intp, MODCODLST2 + reg_index, 0); ++ ++} ++ ++static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++void stv0900_start_search(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 freq; ++ s16 freq_s16 ; ++ ++ stv0900_write_bits(intp, DEMOD_MODE, 0x1f); ++ if (intp->chip_id == 0x10) ++ stv0900_write_reg(intp, CORRELEXP, 0xaa); ++ ++ if (intp->chip_id < 0x20) ++ stv0900_write_reg(intp, CARHDR, 0x55); ++ ++ if (intp->chip_id <= 0x20) { ++ if (intp->symbol_rate[0] <= 5000000) { ++ stv0900_write_reg(intp, CARCFG, 0x44); ++ stv0900_write_reg(intp, CFRUP1, 0x0f); ++ stv0900_write_reg(intp, CFRUP0, 0xff); ++ stv0900_write_reg(intp, CFRLOW1, 0xf0); ++ stv0900_write_reg(intp, CFRLOW0, 0x00); ++ stv0900_write_reg(intp, RTCS2, 0x68); ++ } else { ++ stv0900_write_reg(intp, CARCFG, 0xc4); ++ stv0900_write_reg(intp, RTCS2, 0x44); ++ } ++ ++ } else { /*cut 3.0 above*/ ++ if (intp->symbol_rate[demod] <= 5000000) ++ stv0900_write_reg(intp, RTCS2, 0x68); ++ else ++ stv0900_write_reg(intp, RTCS2, 0x44); ++ ++ stv0900_write_reg(intp, CARCFG, 0x46); ++ if (intp->srch_algo[demod] == STV0900_WARM_START) { ++ freq = 1000 << 16; ++ freq /= (intp->mclk / 1000); ++ freq_s16 = (s16)freq; ++ } else { ++ freq = (intp->srch_range[demod] / 2000); ++ if (intp->symbol_rate[demod] <= 5000000) ++ freq += 80; ++ else ++ freq += 600; ++ ++ freq = freq << 16; ++ freq /= (intp->mclk / 1000); ++ freq_s16 = (s16)freq; ++ } ++ ++ stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16)); ++ stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16)); ++ freq_s16 *= (-1); ++ stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16)); ++ stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16)); ++ } ++ ++ stv0900_write_reg(intp, CFRINIT1, 0); ++ stv0900_write_reg(intp, CFRINIT0, 0); ++ ++ if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, EQUALCFG, 0x41); ++ stv0900_write_reg(intp, FFECFG, 0x41); ++ ++ if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || ++ (intp->srch_standard[demod] == STV0900_SEARCH_DSS) || ++ (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) { ++ stv0900_write_reg(intp, VITSCALE, ++ 0x82); ++ stv0900_write_reg(intp, VAVSRVIT, 0x0); ++ } ++ } ++ ++ stv0900_write_reg(intp, SFRSTEP, 0x00); ++ stv0900_write_reg(intp, TMGTHRISE, 0xe0); ++ stv0900_write_reg(intp, TMGTHFALL, 0xc0); ++ stv0900_write_bits(intp, SCAN_ENABLE, 0); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0); ++ stv0900_write_reg(intp, RTC, 0x88); ++ if (intp->chip_id >= 0x20) { ++ if (intp->symbol_rate[demod] < 2000000) { ++ if (intp->chip_id <= 0x20) ++ stv0900_write_reg(intp, CARFREQ, 0x39); ++ else /*cut 3.0*/ ++ stv0900_write_reg(intp, CARFREQ, 0x89); ++ ++ stv0900_write_reg(intp, CARHDR, 0x40); ++ } else if (intp->symbol_rate[demod] < 10000000) { ++ stv0900_write_reg(intp, CARFREQ, 0x4c); ++ stv0900_write_reg(intp, CARHDR, 0x20); ++ } else { ++ stv0900_write_reg(intp, CARFREQ, 0x4b); ++ stv0900_write_reg(intp, CARHDR, 0x20); ++ } ++ ++ } else { ++ if (intp->symbol_rate[demod] < 10000000) ++ stv0900_write_reg(intp, CARFREQ, 0xef); ++ else ++ stv0900_write_reg(intp, CARFREQ, 0xed); ++ } ++ ++ switch (intp->srch_algo[demod]) { ++ case STV0900_WARM_START: ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ break; ++ case STV0900_COLD_START: ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, DMDISTATE, 0x15); ++ break; ++ default: ++ break; ++ } ++} ++ ++u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, ++ s32 pilot, u8 chip_id) ++{ ++ u8 aclc_value = 0x29; ++ s32 i; ++ const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; ++ ++ dprintk("%s\n", __func__); ++ ++ if (chip_id <= 0x12) { ++ cls2 = FE_STV0900_S2CarLoop; ++ cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; ++ cllas2 = FE_STV0900_S2APSKCarLoopCut30; ++ } else if (chip_id == 0x20) { ++ cls2 = FE_STV0900_S2CarLoopCut20; ++ cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; ++ cllas2 = FE_STV0900_S2APSKCarLoopCut20; ++ } else { ++ cls2 = FE_STV0900_S2CarLoopCut30; ++ cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; ++ cllas2 = FE_STV0900_S2APSKCarLoopCut30; ++ } ++ ++ if (modcode < STV0900_QPSK_12) { ++ i = 0; ++ while ((i < 3) && (modcode != cllqs2[i].modcode)) ++ i++; ++ ++ if (i >= 3) ++ i = 2; ++ } else { ++ i = 0; ++ while ((i < 14) && (modcode != cls2[i].modcode)) ++ i++; ++ ++ if (i >= 14) { ++ i = 0; ++ while ((i < 11) && (modcode != cllas2[i].modcode)) ++ i++; ++ ++ if (i >= 11) ++ i = 10; ++ } ++ } ++ ++ if (modcode <= STV0900_QPSK_25) { ++ if (pilot) { ++ if (srate <= 3000000) ++ aclc_value = cllqs2[i].car_loop_pilots_on_2; ++ else if (srate <= 7000000) ++ aclc_value = cllqs2[i].car_loop_pilots_on_5; ++ else if (srate <= 15000000) ++ aclc_value = cllqs2[i].car_loop_pilots_on_10; ++ else if (srate <= 25000000) ++ aclc_value = cllqs2[i].car_loop_pilots_on_20; ++ else ++ aclc_value = cllqs2[i].car_loop_pilots_on_30; ++ } else { ++ if (srate <= 3000000) ++ aclc_value = cllqs2[i].car_loop_pilots_off_2; ++ else if (srate <= 7000000) ++ aclc_value = cllqs2[i].car_loop_pilots_off_5; ++ else if (srate <= 15000000) ++ aclc_value = cllqs2[i].car_loop_pilots_off_10; ++ else if (srate <= 25000000) ++ aclc_value = cllqs2[i].car_loop_pilots_off_20; ++ else ++ aclc_value = cllqs2[i].car_loop_pilots_off_30; ++ } ++ ++ } else if (modcode <= STV0900_8PSK_910) { ++ if (pilot) { ++ if (srate <= 3000000) ++ aclc_value = cls2[i].car_loop_pilots_on_2; ++ else if (srate <= 7000000) ++ aclc_value = cls2[i].car_loop_pilots_on_5; ++ else if (srate <= 15000000) ++ aclc_value = cls2[i].car_loop_pilots_on_10; ++ else if (srate <= 25000000) ++ aclc_value = cls2[i].car_loop_pilots_on_20; ++ else ++ aclc_value = cls2[i].car_loop_pilots_on_30; ++ } else { ++ if (srate <= 3000000) ++ aclc_value = cls2[i].car_loop_pilots_off_2; ++ else if (srate <= 7000000) ++ aclc_value = cls2[i].car_loop_pilots_off_5; ++ else if (srate <= 15000000) ++ aclc_value = cls2[i].car_loop_pilots_off_10; ++ else if (srate <= 25000000) ++ aclc_value = cls2[i].car_loop_pilots_off_20; ++ else ++ aclc_value = cls2[i].car_loop_pilots_off_30; ++ } ++ ++ } else { ++ if (srate <= 3000000) ++ aclc_value = cllas2[i].car_loop_pilots_on_2; ++ else if (srate <= 7000000) ++ aclc_value = cllas2[i].car_loop_pilots_on_5; ++ else if (srate <= 15000000) ++ aclc_value = cllas2[i].car_loop_pilots_on_10; ++ else if (srate <= 25000000) ++ aclc_value = cllas2[i].car_loop_pilots_on_20; ++ else ++ aclc_value = cllas2[i].car_loop_pilots_on_30; ++ } ++ ++ return aclc_value; ++} ++ ++u8 stv0900_get_optim_short_carr_loop(s32 srate, ++ enum fe_stv0900_modulation modulation, ++ u8 chip_id) ++{ ++ const struct stv0900_short_frames_car_loop_optim *s2scl; ++ const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30; ++ s32 mod_index = 0; ++ u8 aclc_value = 0x0b; ++ ++ dprintk("%s\n", __func__); ++ ++ s2scl = FE_STV0900_S2ShortCarLoop; ++ s2sclc30 = FE_STV0900_S2ShortCarLoopCut30; ++ ++ switch (modulation) { ++ case STV0900_QPSK: ++ default: ++ mod_index = 0; ++ break; ++ case STV0900_8PSK: ++ mod_index = 1; ++ break; ++ case STV0900_16APSK: ++ mod_index = 2; ++ break; ++ case STV0900_32APSK: ++ mod_index = 3; ++ break; ++ } ++ ++ if (chip_id >= 0x30) { ++ if (srate <= 3000000) ++ aclc_value = s2sclc30[mod_index].car_loop_2; ++ else if (srate <= 7000000) ++ aclc_value = s2sclc30[mod_index].car_loop_5; ++ else if (srate <= 15000000) ++ aclc_value = s2sclc30[mod_index].car_loop_10; ++ else if (srate <= 25000000) ++ aclc_value = s2sclc30[mod_index].car_loop_20; ++ else ++ aclc_value = s2sclc30[mod_index].car_loop_30; ++ ++ } else if (chip_id >= 0x20) { ++ if (srate <= 3000000) ++ aclc_value = s2scl[mod_index].car_loop_cut20_2; ++ else if (srate <= 7000000) ++ aclc_value = s2scl[mod_index].car_loop_cut20_5; ++ else if (srate <= 15000000) ++ aclc_value = s2scl[mod_index].car_loop_cut20_10; ++ else if (srate <= 25000000) ++ aclc_value = s2scl[mod_index].car_loop_cut20_20; ++ else ++ aclc_value = s2scl[mod_index].car_loop_cut20_30; ++ ++ } else { ++ if (srate <= 3000000) ++ aclc_value = s2scl[mod_index].car_loop_cut12_2; ++ else if (srate <= 7000000) ++ aclc_value = s2scl[mod_index].car_loop_cut12_5; ++ else if (srate <= 15000000) ++ aclc_value = s2scl[mod_index].car_loop_cut12_10; ++ else if (srate <= 25000000) ++ aclc_value = s2scl[mod_index].car_loop_cut12_20; ++ else ++ aclc_value = s2scl[mod_index].car_loop_cut12_30; ++ ++ } ++ ++ return aclc_value; ++} ++ ++static ++enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_mode LDPC_Mode, ++ enum fe_stv0900_demod_num demod) ++{ ++ enum fe_stv0900_error error = STV0900_NO_ERROR; ++ s32 reg_ind; ++ ++ dprintk("%s\n", __func__); ++ ++ switch (LDPC_Mode) { ++ case STV0900_DUAL: ++ default: ++ if ((intp->demod_mode != STV0900_DUAL) ++ || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) { ++ stv0900_write_reg(intp, R0900_GENCFG, 0x1d); ++ ++ intp->demod_mode = STV0900_DUAL; ++ ++ stv0900_write_bits(intp, F0900_FRESFEC, 1); ++ stv0900_write_bits(intp, F0900_FRESFEC, 0); ++ ++ for (reg_ind = 0; reg_ind < 7; reg_ind++) ++ stv0900_write_reg(intp, ++ R0900_P1_MODCODLST0 + reg_ind, ++ 0xff); ++ for (reg_ind = 0; reg_ind < 8; reg_ind++) ++ stv0900_write_reg(intp, ++ R0900_P1_MODCODLST7 + reg_ind, ++ 0xcc); ++ ++ stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff); ++ stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf); ++ ++ for (reg_ind = 0; reg_ind < 7; reg_ind++) ++ stv0900_write_reg(intp, ++ R0900_P2_MODCODLST0 + reg_ind, ++ 0xff); ++ for (reg_ind = 0; reg_ind < 8; reg_ind++) ++ stv0900_write_reg(intp, ++ R0900_P2_MODCODLST7 + reg_ind, ++ 0xcc); ++ ++ stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff); ++ stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf); ++ } ++ ++ break; ++ case STV0900_SINGLE: ++ if (demod == STV0900_DEMOD_2) { ++ stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1); ++ stv0900_activate_s2_modcod_single(intp, ++ STV0900_DEMOD_2); ++ stv0900_write_reg(intp, R0900_GENCFG, 0x06); ++ } else { ++ stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2); ++ stv0900_activate_s2_modcod_single(intp, ++ STV0900_DEMOD_1); ++ stv0900_write_reg(intp, R0900_GENCFG, 0x04); ++ } ++ ++ intp->demod_mode = STV0900_SINGLE; ++ ++ stv0900_write_bits(intp, F0900_FRESFEC, 1); ++ stv0900_write_bits(intp, F0900_FRESFEC, 0); ++ stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1); ++ stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0); ++ stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1); ++ stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0); ++ break; ++ } ++ ++ return error; ++} ++ ++static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, ++ struct stv0900_init_params *p_init) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ enum fe_stv0900_error error = STV0900_NO_ERROR; ++ enum fe_stv0900_error demodError = STV0900_NO_ERROR; ++ struct stv0900_internal *intp = NULL; ++ int selosci, i; ++ ++ struct stv0900_inode *temp_int = find_inode(state->i2c_adap, ++ state->config->demod_address); ++ ++ dprintk("%s\n", __func__); ++ ++ if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) { ++ state->internal = temp_int->internal; ++ (state->internal->dmds_used)++; ++ dprintk("%s: Find Internal Structure!\n", __func__); ++ return STV0900_NO_ERROR; ++ } else { ++ state->internal = kmalloc(sizeof(struct stv0900_internal), ++ GFP_KERNEL); ++ if (state->internal == NULL) ++ return STV0900_INVALID_HANDLE; ++ temp_int = append_internal(state->internal); ++ if (temp_int == NULL) { ++ kfree(state->internal); ++ state->internal = NULL; ++ return STV0900_INVALID_HANDLE; ++ } ++ state->internal->dmds_used = 1; ++ state->internal->i2c_adap = state->i2c_adap; ++ state->internal->i2c_addr = state->config->demod_address; ++ state->internal->clkmode = state->config->clkmode; ++ state->internal->errs = STV0900_NO_ERROR; ++ dprintk("%s: Create New Internal Structure!\n", __func__); ++ } ++ ++ if (state->internal == NULL) { ++ error = STV0900_INVALID_HANDLE; ++ return error; ++ } ++ ++ demodError = stv0900_initialize(state->internal); ++ if (demodError == STV0900_NO_ERROR) { ++ error = STV0900_NO_ERROR; ++ } else { ++ if (demodError == STV0900_INVALID_HANDLE) ++ error = STV0900_INVALID_HANDLE; ++ else ++ error = STV0900_I2C_ERROR; ++ ++ return error; ++ } ++ ++ intp = state->internal; ++ ++ intp->demod_mode = p_init->demod_mode; ++ stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1); ++ intp->chip_id = stv0900_read_reg(intp, R0900_MID); ++ intp->rolloff = p_init->rolloff; ++ intp->quartz = p_init->dmd_ref_clk; ++ ++ stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); ++ stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); ++ ++ intp->ts_config = p_init->ts_config; ++ if (intp->ts_config == NULL) ++ stv0900_set_ts_parallel_serial(intp, ++ p_init->path1_ts_clock, ++ p_init->path2_ts_clock); ++ else { ++ for (i = 0; intp->ts_config[i].addr != 0xffff; i++) ++ stv0900_write_reg(intp, ++ intp->ts_config[i].addr, ++ intp->ts_config[i].val); ++ ++ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); ++ stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); ++ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); ++ stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); ++ } ++ ++ intp->tuner_type[0] = p_init->tuner1_type; ++ intp->tuner_type[1] = p_init->tuner2_type; ++ /* tuner init */ ++ switch (p_init->tuner1_type) { ++ case 3: /*FE_AUTO_STB6100:*/ ++ stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x3c); ++ stv0900_write_reg(intp, R0900_P1_TNRCFG2, 0x86); ++ stv0900_write_reg(intp, R0900_P1_TNRCFG3, 0x18); ++ stv0900_write_reg(intp, R0900_P1_TNRXTAL, 27); /* 27MHz */ ++ stv0900_write_reg(intp, R0900_P1_TNRSTEPS, 0x05); ++ stv0900_write_reg(intp, R0900_P1_TNRGAIN, 0x17); ++ stv0900_write_reg(intp, R0900_P1_TNRADJ, 0x1f); ++ stv0900_write_reg(intp, R0900_P1_TNRCTL2, 0x0); ++ stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 3); ++ break; ++ /* case FE_SW_TUNER: */ ++ default: ++ stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 6); ++ break; ++ } ++ ++ stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); ++ switch (p_init->tuner1_adc) { ++ case 1: ++ stv0900_write_reg(intp, R0900_TSTTNR1, 0x26); ++ break; ++ default: ++ break; ++ } ++ ++ stv0900_write_reg(intp, R0900_P1_TNRLD, 1); /* hw tuner */ ++ ++ /* tuner init */ ++ switch (p_init->tuner2_type) { ++ case 3: /*FE_AUTO_STB6100:*/ ++ stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x3c); ++ stv0900_write_reg(intp, R0900_P2_TNRCFG2, 0x86); ++ stv0900_write_reg(intp, R0900_P2_TNRCFG3, 0x18); ++ stv0900_write_reg(intp, R0900_P2_TNRXTAL, 27); /* 27MHz */ ++ stv0900_write_reg(intp, R0900_P2_TNRSTEPS, 0x05); ++ stv0900_write_reg(intp, R0900_P2_TNRGAIN, 0x17); ++ stv0900_write_reg(intp, R0900_P2_TNRADJ, 0x1f); ++ stv0900_write_reg(intp, R0900_P2_TNRCTL2, 0x0); ++ stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 3); ++ break; ++ /* case FE_SW_TUNER: */ ++ default: ++ stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 6); ++ break; ++ } ++ ++ stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); ++ switch (p_init->tuner2_adc) { ++ case 1: ++ stv0900_write_reg(intp, R0900_TSTTNR3, 0x26); ++ break; ++ default: ++ break; ++ } ++ ++ stv0900_write_reg(intp, R0900_P2_TNRLD, 1); /* hw tuner */ ++ ++ stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); ++ stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); ++ stv0900_set_mclk(intp, 135000000); ++ msleep(3); ++ ++ switch (intp->clkmode) { ++ case 0: ++ case 2: ++ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode); ++ break; ++ default: ++ selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); ++ stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci); ++ break; ++ } ++ msleep(3); ++ ++ intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz); ++ if (intp->errs) ++ error = STV0900_I2C_ERROR; ++ ++ return error; ++} ++ ++static int stv0900_status(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ enum fe_stv0900_search_state demod_state; ++ int locked = FALSE; ++ u8 tsbitrate0_val, tsbitrate1_val; ++ s32 bitrate; ++ ++ demod_state = stv0900_get_bits(intp, HEADER_MODE); ++ switch (demod_state) { ++ case STV0900_SEARCH: ++ case STV0900_PLH_DETECTED: ++ default: ++ locked = FALSE; ++ break; ++ case STV0900_DVBS2_FOUND: ++ locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && ++ stv0900_get_bits(intp, PKTDELIN_LOCK) && ++ stv0900_get_bits(intp, TSFIFO_LINEOK); ++ break; ++ case STV0900_DVBS_FOUND: ++ locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && ++ stv0900_get_bits(intp, LOCKEDVIT) && ++ stv0900_get_bits(intp, TSFIFO_LINEOK); ++ break; ++ } ++ ++ dprintk("%s: locked = %d\n", __func__, locked); ++ ++ if (stvdebug) { ++ /* Print TS bitrate */ ++ tsbitrate0_val = stv0900_read_reg(intp, TSBITRATE0); ++ tsbitrate1_val = stv0900_read_reg(intp, TSBITRATE1); ++ /* Formula Bit rate = Mclk * px_tsfifo_bitrate / 16384 */ ++ bitrate = (stv0900_get_mclk_freq(intp, intp->quartz)/1000000) ++ * (tsbitrate1_val << 8 | tsbitrate0_val); ++ bitrate /= 16384; ++ dprintk("TS bitrate = %d Mbit/sec\n", bitrate); ++ } ++ ++ return locked; ++} ++ ++static int stv0900_set_mis(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod, int mis) ++{ ++ enum fe_stv0900_error error = STV0900_NO_ERROR; ++ ++ dprintk("%s\n", __func__); ++ ++ if (mis < 0 || mis > 255) { ++ dprintk("Disable MIS filtering\n"); ++ stv0900_write_bits(intp, FILTER_EN, 0); ++ } else { ++ dprintk("Enable MIS filtering - %d\n", mis); ++ stv0900_write_bits(intp, FILTER_EN, 1); ++ stv0900_write_reg(intp, ISIENTRY, mis); ++ stv0900_write_reg(intp, ISIBITENA, 0xff); ++ } ++ ++ return error; ++} ++ ++ ++static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ struct stv0900_search_params p_search; ++ struct stv0900_signal_info p_result = intp->result[demod]; ++ ++ enum fe_stv0900_error error = STV0900_NO_ERROR; ++ ++ dprintk("%s: ", __func__); ++ ++ if (!(INRANGE(100000, c->symbol_rate, 70000000))) ++ return DVBFE_ALGO_SEARCH_FAILED; ++ ++ if (state->config->set_ts_params) ++ state->config->set_ts_params(fe, 0); ++ ++ stv0900_set_mis(intp, demod, c->stream_id); ++ ++ p_result.locked = FALSE; ++ p_search.path = demod; ++ p_search.frequency = c->frequency; ++ p_search.symbol_rate = c->symbol_rate; ++ p_search.search_range = 10000000; ++ p_search.fec = STV0900_FEC_UNKNOWN; ++ p_search.standard = STV0900_AUTO_SEARCH; ++ p_search.iq_inversion = STV0900_IQ_AUTO; ++ p_search.search_algo = STV0900_BLIND_SEARCH; ++ /* Speeds up DVB-S searching */ ++ if (c->delivery_system == SYS_DVBS) ++ p_search.standard = STV0900_SEARCH_DVBS1; ++ ++ intp->srch_standard[demod] = p_search.standard; ++ intp->symbol_rate[demod] = p_search.symbol_rate; ++ intp->srch_range[demod] = p_search.search_range; ++ intp->freq[demod] = p_search.frequency; ++ intp->srch_algo[demod] = p_search.search_algo; ++ intp->srch_iq_inv[demod] = p_search.iq_inversion; ++ intp->fec[demod] = p_search.fec; ++ if ((stv0900_algo(fe) == STV0900_RANGEOK) && ++ (intp->errs == STV0900_NO_ERROR)) { ++ p_result.locked = intp->result[demod].locked; ++ p_result.standard = intp->result[demod].standard; ++ p_result.frequency = intp->result[demod].frequency; ++ p_result.symbol_rate = intp->result[demod].symbol_rate; ++ p_result.fec = intp->result[demod].fec; ++ p_result.modcode = intp->result[demod].modcode; ++ p_result.pilot = intp->result[demod].pilot; ++ p_result.frame_len = intp->result[demod].frame_len; ++ p_result.spectrum = intp->result[demod].spectrum; ++ p_result.rolloff = intp->result[demod].rolloff; ++ p_result.modulation = intp->result[demod].modulation; ++ } else { ++ p_result.locked = FALSE; ++ switch (intp->err[demod]) { ++ case STV0900_I2C_ERROR: ++ error = STV0900_I2C_ERROR; ++ break; ++ case STV0900_NO_ERROR: ++ default: ++ error = STV0900_SEARCH_FAILED; ++ break; ++ } ++ } ++ ++ if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) { ++ dprintk("Search Success\n"); ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ dprintk("Search Fail\n"); ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ ++} ++ ++static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ ++ dprintk("%s: ", __func__); ++ ++ if ((stv0900_status(state->internal, state->demod)) == TRUE) { ++ dprintk("DEMOD LOCK OK\n"); ++ *status = FE_HAS_CARRIER ++ | FE_HAS_VITERBI ++ | FE_HAS_SYNC ++ | FE_HAS_LOCK; ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 1); ++ } else { ++ *status = 0; ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 0); ++ dprintk("DEMOD LOCK FAIL\n"); ++ } ++ ++ return 0; ++} ++ ++static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) ++{ ++ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ if (stop_ts == TRUE) ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ else ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ ++ return 0; ++} ++ ++static int stv0900_diseqc_init(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode); ++ stv0900_write_bits(intp, DISEQC_RESET, 1); ++ stv0900_write_bits(intp, DISEQC_RESET, 0); ++ ++ return 0; ++} ++ ++static int stv0900_init(struct dvb_frontend *fe) ++{ ++ dprintk("%s\n", __func__); ++ ++ stv0900_stop_ts(fe, 1); ++ stv0900_diseqc_init(fe); ++ ++ return 0; ++} ++ ++static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data, ++ u32 NbData, enum fe_stv0900_demod_num demod) ++{ ++ s32 i = 0; ++ ++ stv0900_write_bits(intp, DIS_PRECHARGE, 1); ++ while (i < NbData) { ++ while (stv0900_get_bits(intp, FIFO_FULL)) ++ ;/* checkpatch complains */ ++ stv0900_write_reg(intp, DISTXDATA, data[i]); ++ i++; ++ } ++ ++ stv0900_write_bits(intp, DIS_PRECHARGE, 0); ++ i = 0; ++ while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) { ++ msleep(10); ++ i++; ++ } ++ ++ return 0; ++} ++ ++static int stv0900_send_master_cmd(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ ++ return stv0900_diseqc_send(state->internal, ++ cmd->msg, ++ cmd->msg_len, ++ state->demod); ++} ++ ++static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ u8 data; ++ ++ ++ switch (burst) { ++ case SEC_MINI_A: ++ stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */ ++ data = 0x00; ++ stv0900_diseqc_send(intp, &data, 1, state->demod); ++ break; ++ case SEC_MINI_B: ++ stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */ ++ data = 0xff; ++ stv0900_diseqc_send(intp, &data, 1, state->demod); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int stv0900_recv_slave_reply(struct dvb_frontend *fe, ++ struct dvb_diseqc_slave_reply *reply) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ s32 i = 0; ++ ++ reply->msg_len = 0; ++ ++ while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) { ++ msleep(10); ++ i++; ++ } ++ ++ if (stv0900_get_bits(intp, RX_END)) { ++ reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR); ++ ++ for (i = 0; i < reply->msg_len; i++) ++ reply->msg[i] = stv0900_read_reg(intp, DISRXDATA); ++ } ++ ++ return 0; ++} ++ ++static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off")); ++ ++ switch (toneoff) { ++ case SEC_TONE_ON: ++ /*Set the DiseqC mode to 22Khz _continues_ tone*/ ++ stv0900_write_bits(intp, DISTX_MODE, 0); ++ stv0900_write_bits(intp, DISEQC_RESET, 1); ++ /*release DiseqC reset to enable the 22KHz tone*/ ++ stv0900_write_bits(intp, DISEQC_RESET, 0); ++ break; ++ case SEC_TONE_OFF: ++ /*return diseqc mode to config->diseqc_mode. ++ Usually it's without _continues_ tone */ ++ stv0900_write_bits(intp, DISTX_MODE, ++ state->config->diseqc_mode); ++ /*maintain the DiseqC reset to disable the 22KHz tone*/ ++ stv0900_write_bits(intp, DISEQC_RESET, 1); ++ stv0900_write_bits(intp, DISEQC_RESET, 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void stv0900_release(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 0); ++ ++ if ((--(state->internal->dmds_used)) <= 0) { ++ ++ dprintk("%s: Actually removing\n", __func__); ++ ++ remove_inode(state->internal); ++ kfree(state->internal); ++ } ++ ++ kfree(state); ++} ++ ++static int stv0900_sleep(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (state->config->set_lock_led) ++ state->config->set_lock_led(fe, 0); ++ ++ return 0; ++} ++ ++static int stv0900_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ struct stv0900_signal_info p_result = intp->result[demod]; ++ ++ p->frequency = p_result.locked ? p_result.frequency : 0; ++ p->symbol_rate = p_result.locked ? p_result.symbol_rate : 0; ++ return 0; ++} ++ ++static struct dvb_frontend_ops stv0900_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, ++ .info = { ++ .name = "STV0900 frontend", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .symbol_rate_tolerance = 500, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_7_8 | FE_CAN_QPSK | ++ FE_CAN_2G_MODULATION | ++ FE_CAN_FEC_AUTO ++ }, ++ .release = stv0900_release, ++ .init = stv0900_init, ++ .get_frontend = stv0900_get_frontend, ++ .sleep = stv0900_sleep, ++ .get_frontend_algo = stv0900_frontend_algo, ++ .i2c_gate_ctrl = stv0900_i2c_gate_ctrl, ++ .diseqc_send_master_cmd = stv0900_send_master_cmd, ++ .diseqc_send_burst = stv0900_send_burst, ++ .diseqc_recv_slave_reply = stv0900_recv_slave_reply, ++ .set_tone = stv0900_set_tone, ++ .search = stv0900_search, ++ .read_status = stv0900_read_status, ++ .read_ber = stv0900_read_ber, ++ .read_signal_strength = stv0900_read_signal_strength, ++ .read_snr = stv0900_read_snr, ++ .read_ucblocks = stv0900_read_ucblocks, ++}; ++ ++struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, ++ struct i2c_adapter *i2c, ++ int demod) ++{ ++ struct stv0900_state *state = NULL; ++ struct stv0900_init_params init_params; ++ enum fe_stv0900_error err_stv0900; ++ ++ state = kzalloc(sizeof(struct stv0900_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ state->demod = demod; ++ state->config = config; ++ state->i2c_adap = i2c; ++ ++ memcpy(&state->frontend.ops, &stv0900_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ switch (demod) { ++ case 0: ++ case 1: ++ init_params.dmd_ref_clk = config->xtal; ++ init_params.demod_mode = config->demod_mode; ++ init_params.rolloff = STV0900_35; ++ init_params.path1_ts_clock = config->path1_mode; ++ init_params.tun1_maddress = config->tun1_maddress; ++ init_params.tun1_iq_inv = STV0900_IQ_NORMAL; ++ init_params.tuner1_adc = config->tun1_adc; ++ init_params.tuner1_type = config->tun1_type; ++ init_params.path2_ts_clock = config->path2_mode; ++ init_params.ts_config = config->ts_config_regs; ++ init_params.tun2_maddress = config->tun2_maddress; ++ init_params.tuner2_adc = config->tun2_adc; ++ init_params.tuner2_type = config->tun2_type; ++ init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; ++ ++ err_stv0900 = stv0900_init_internal(&state->frontend, ++ &init_params); ++ ++ if (err_stv0900) ++ goto error; ++ ++ if (state->internal->chip_id >= 0x30) ++ state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; ++ ++ break; ++ default: ++ goto error; ++ break; ++ } ++ ++ dprintk("%s: Attaching STV0900 demodulator(%d) \n", __func__, demod); ++ return &state->frontend; ++ ++error: ++ dprintk("%s: Failed to attach STV0900 demodulator(%d) \n", ++ __func__, demod); ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stv0900_attach); ++ ++MODULE_PARM_DESC(debug, "Set debug"); ++ ++MODULE_AUTHOR("Igor M. Liplianin"); ++MODULE_DESCRIPTION("ST STV0900 frontend"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stv0900_init.h b/drivers/media/dvb-frontends/stv0900_init.h +new file mode 100644 +index 0000000..b684df9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0900_init.h +@@ -0,0 +1,584 @@ ++/* ++ * stv0900_init.h ++ * ++ * Driver for ST STV0900 satellite demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0900_INIT_H ++#define STV0900_INIT_H ++ ++#include "stv0900_priv.h" ++ ++/* DVBS2 C/N Look-Up table */ ++static const struct stv0900_table stv0900_s2_cn = { ++ 55, ++ { ++ { -30, 13348 }, /*C/N=-3dB*/ ++ { -20, 12640 }, /*C/N=-2dB*/ ++ { -10, 11883 }, /*C/N=-1dB*/ ++ { 0, 11101 }, /*C/N=-0dB*/ ++ { 5, 10718 }, /*C/N=0.5dB*/ ++ { 10, 10339 }, /*C/N=1.0dB*/ ++ { 15, 9947 }, /*C/N=1.5dB*/ ++ { 20, 9552 }, /*C/N=2.0dB*/ ++ { 25, 9183 }, /*C/N=2.5dB*/ ++ { 30, 8799 }, /*C/N=3.0dB*/ ++ { 35, 8422 }, /*C/N=3.5dB*/ ++ { 40, 8062 }, /*C/N=4.0dB*/ ++ { 45, 7707 }, /*C/N=4.5dB*/ ++ { 50, 7353 }, /*C/N=5.0dB*/ ++ { 55, 7025 }, /*C/N=5.5dB*/ ++ { 60, 6684 }, /*C/N=6.0dB*/ ++ { 65, 6331 }, /*C/N=6.5dB*/ ++ { 70, 6036 }, /*C/N=7.0dB*/ ++ { 75, 5727 }, /*C/N=7.5dB*/ ++ { 80, 5437 }, /*C/N=8.0dB*/ ++ { 85, 5164 }, /*C/N=8.5dB*/ ++ { 90, 4902 }, /*C/N=9.0dB*/ ++ { 95, 4653 }, /*C/N=9.5dB*/ ++ { 100, 4408 }, /*C/N=10.0dB*/ ++ { 105, 4187 }, /*C/N=10.5dB*/ ++ { 110, 3961 }, /*C/N=11.0dB*/ ++ { 115, 3751 }, /*C/N=11.5dB*/ ++ { 120, 3558 }, /*C/N=12.0dB*/ ++ { 125, 3368 }, /*C/N=12.5dB*/ ++ { 130, 3191 }, /*C/N=13.0dB*/ ++ { 135, 3017 }, /*C/N=13.5dB*/ ++ { 140, 2862 }, /*C/N=14.0dB*/ ++ { 145, 2710 }, /*C/N=14.5dB*/ ++ { 150, 2565 }, /*C/N=15.0dB*/ ++ { 160, 2300 }, /*C/N=16.0dB*/ ++ { 170, 2058 }, /*C/N=17.0dB*/ ++ { 180, 1849 }, /*C/N=18.0dB*/ ++ { 190, 1663 }, /*C/N=19.0dB*/ ++ { 200, 1495 }, /*C/N=20.0dB*/ ++ { 210, 1349 }, /*C/N=21.0dB*/ ++ { 220, 1222 }, /*C/N=22.0dB*/ ++ { 230, 1110 }, /*C/N=23.0dB*/ ++ { 240, 1011 }, /*C/N=24.0dB*/ ++ { 250, 925 }, /*C/N=25.0dB*/ ++ { 260, 853 }, /*C/N=26.0dB*/ ++ { 270, 789 }, /*C/N=27.0dB*/ ++ { 280, 734 }, /*C/N=28.0dB*/ ++ { 290, 690 }, /*C/N=29.0dB*/ ++ { 300, 650 }, /*C/N=30.0dB*/ ++ { 310, 619 }, /*C/N=31.0dB*/ ++ { 320, 593 }, /*C/N=32.0dB*/ ++ { 330, 571 }, /*C/N=33.0dB*/ ++ { 400, 498 }, /*C/N=40.0dB*/ ++ { 450, 484 }, /*C/N=45.0dB*/ ++ { 500, 481 } /*C/N=50.0dB*/ ++ } ++}; ++ ++/* RF level C/N Look-Up table */ ++static const struct stv0900_table stv0900_rf = { ++ 14, ++ { ++ { -5, 0xCAA1 }, /*-5dBm*/ ++ { -10, 0xC229 }, /*-10dBm*/ ++ { -15, 0xBB08 }, /*-15dBm*/ ++ { -20, 0xB4BC }, /*-20dBm*/ ++ { -25, 0xAD5A }, /*-25dBm*/ ++ { -30, 0xA298 }, /*-30dBm*/ ++ { -35, 0x98A8 }, /*-35dBm*/ ++ { -40, 0x8389 }, /*-40dBm*/ ++ { -45, 0x59BE }, /*-45dBm*/ ++ { -50, 0x3A14 }, /*-50dBm*/ ++ { -55, 0x2D11 }, /*-55dBm*/ ++ { -60, 0x210D }, /*-60dBm*/ ++ { -65, 0xA14F }, /*-65dBm*/ ++ { -70, 0x7AA } /*-70dBm*/ ++ } ++}; ++ ++struct stv0900_car_loop_optim { ++ enum fe_stv0900_modcode modcode; ++ u8 car_loop_pilots_on_2; ++ u8 car_loop_pilots_off_2; ++ u8 car_loop_pilots_on_5; ++ u8 car_loop_pilots_off_5; ++ u8 car_loop_pilots_on_10; ++ u8 car_loop_pilots_off_10; ++ u8 car_loop_pilots_on_20; ++ u8 car_loop_pilots_off_20; ++ u8 car_loop_pilots_on_30; ++ u8 car_loop_pilots_off_30; ++ ++}; ++ ++struct stv0900_short_frames_car_loop_optim { ++ enum fe_stv0900_modulation modulation; ++ u8 car_loop_cut12_2; /* Cut 1.2, SR<=3msps */ ++ u8 car_loop_cut20_2; /* Cut 2.0, SR<3msps */ ++ u8 car_loop_cut12_5; /* Cut 1.2, 3 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0900_PRIV_H ++#define STV0900_PRIV_H ++ ++#include ++ ++#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) ++#define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \ ++ || (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) ++ ++#ifndef MAKEWORD ++#define MAKEWORD(X, Y) (((X) << 8) + (Y)) ++#endif ++ ++#define LSB(X) (((X) & 0xFF)) ++#define MSB(Y) (((Y) >> 8) & 0xFF) ++ ++#ifndef TRUE ++#define TRUE (1 == 1) ++#endif ++#ifndef FALSE ++#define FALSE (!TRUE) ++#endif ++ ++#define dprintk(args...) \ ++ do { \ ++ if (stvdebug) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++#define STV0900_MAXLOOKUPSIZE 500 ++#define STV0900_BLIND_SEARCH_AGC2_TH 700 ++#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400 ++#define IQPOWER_THRESHOLD 30 ++ ++/* One point of the lookup table */ ++struct stv000_lookpoint { ++ s32 realval;/* real value */ ++ s32 regval;/* binary value */ ++}; ++ ++/* Lookup table definition */ ++struct stv0900_table{ ++ s32 size;/* Size of the lookup table */ ++ struct stv000_lookpoint table[STV0900_MAXLOOKUPSIZE];/* Lookup table */ ++}; ++ ++enum fe_stv0900_error { ++ STV0900_NO_ERROR = 0, ++ STV0900_INVALID_HANDLE, ++ STV0900_BAD_PARAMETER, ++ STV0900_I2C_ERROR, ++ STV0900_SEARCH_FAILED, ++}; ++ ++enum fe_stv0900_clock_type { ++ STV0900_USE_REGISTERS_DEFAULT, ++ STV0900_SERIAL_PUNCT_CLOCK,/*Serial punctured clock */ ++ STV0900_SERIAL_CONT_CLOCK,/*Serial continues clock */ ++ STV0900_PARALLEL_PUNCT_CLOCK,/*Parallel punctured clock */ ++ STV0900_DVBCI_CLOCK/*Parallel continues clock : DVBCI */ ++}; ++ ++enum fe_stv0900_search_state { ++ STV0900_SEARCH = 0, ++ STV0900_PLH_DETECTED, ++ STV0900_DVBS2_FOUND, ++ STV0900_DVBS_FOUND ++ ++}; ++ ++enum fe_stv0900_ldpc_state { ++ STV0900_PATH1_OFF_PATH2_OFF = 0, ++ STV0900_PATH1_ON_PATH2_OFF = 1, ++ STV0900_PATH1_OFF_PATH2_ON = 2, ++ STV0900_PATH1_ON_PATH2_ON = 3 ++}; ++ ++enum fe_stv0900_signal_type { ++ STV0900_NOAGC1 = 0, ++ STV0900_AGC1OK, ++ STV0900_NOTIMING, ++ STV0900_ANALOGCARRIER, ++ STV0900_TIMINGOK, ++ STV0900_NOAGC2, ++ STV0900_AGC2OK, ++ STV0900_NOCARRIER, ++ STV0900_CARRIEROK, ++ STV0900_NODATA, ++ STV0900_DATAOK, ++ STV0900_OUTOFRANGE, ++ STV0900_RANGEOK ++}; ++ ++enum fe_stv0900_demod_num { ++ STV0900_DEMOD_1, ++ STV0900_DEMOD_2 ++}; ++ ++enum fe_stv0900_tracking_standard { ++ STV0900_DVBS1_STANDARD,/* Found Standard*/ ++ STV0900_DVBS2_STANDARD, ++ STV0900_DSS_STANDARD, ++ STV0900_TURBOCODE_STANDARD, ++ STV0900_UNKNOWN_STANDARD ++}; ++ ++enum fe_stv0900_search_standard { ++ STV0900_AUTO_SEARCH, ++ STV0900_SEARCH_DVBS1,/* Search Standard*/ ++ STV0900_SEARCH_DVBS2, ++ STV0900_SEARCH_DSS, ++ STV0900_SEARCH_TURBOCODE ++}; ++ ++enum fe_stv0900_search_algo { ++ STV0900_BLIND_SEARCH,/* offset freq and SR are Unknown */ ++ STV0900_COLD_START,/* only the SR is known */ ++ STV0900_WARM_START/* offset freq and SR are known */ ++}; ++ ++enum fe_stv0900_modulation { ++ STV0900_QPSK, ++ STV0900_8PSK, ++ STV0900_16APSK, ++ STV0900_32APSK, ++ STV0900_UNKNOWN ++}; ++ ++enum fe_stv0900_modcode { ++ STV0900_DUMMY_PLF, ++ STV0900_QPSK_14, ++ STV0900_QPSK_13, ++ STV0900_QPSK_25, ++ STV0900_QPSK_12, ++ STV0900_QPSK_35, ++ STV0900_QPSK_23, ++ STV0900_QPSK_34, ++ STV0900_QPSK_45, ++ STV0900_QPSK_56, ++ STV0900_QPSK_89, ++ STV0900_QPSK_910, ++ STV0900_8PSK_35, ++ STV0900_8PSK_23, ++ STV0900_8PSK_34, ++ STV0900_8PSK_56, ++ STV0900_8PSK_89, ++ STV0900_8PSK_910, ++ STV0900_16APSK_23, ++ STV0900_16APSK_34, ++ STV0900_16APSK_45, ++ STV0900_16APSK_56, ++ STV0900_16APSK_89, ++ STV0900_16APSK_910, ++ STV0900_32APSK_34, ++ STV0900_32APSK_45, ++ STV0900_32APSK_56, ++ STV0900_32APSK_89, ++ STV0900_32APSK_910, ++ STV0900_MODCODE_UNKNOWN ++}; ++ ++enum fe_stv0900_fec {/*DVBS1, DSS and turbo code puncture rate*/ ++ STV0900_FEC_1_2 = 0, ++ STV0900_FEC_2_3, ++ STV0900_FEC_3_4, ++ STV0900_FEC_4_5,/*for turbo code only*/ ++ STV0900_FEC_5_6, ++ STV0900_FEC_6_7,/*for DSS only */ ++ STV0900_FEC_7_8, ++ STV0900_FEC_8_9,/*for turbo code only*/ ++ STV0900_FEC_UNKNOWN ++}; ++ ++enum fe_stv0900_frame_length { ++ STV0900_LONG_FRAME, ++ STV0900_SHORT_FRAME ++}; ++ ++enum fe_stv0900_pilot { ++ STV0900_PILOTS_OFF, ++ STV0900_PILOTS_ON ++}; ++ ++enum fe_stv0900_rolloff { ++ STV0900_35, ++ STV0900_25, ++ STV0900_20 ++}; ++ ++enum fe_stv0900_search_iq { ++ STV0900_IQ_AUTO, ++ STV0900_IQ_AUTO_NORMAL_FIRST, ++ STV0900_IQ_FORCE_NORMAL, ++ STV0900_IQ_FORCE_SWAPPED ++}; ++ ++enum stv0900_iq_inversion { ++ STV0900_IQ_NORMAL, ++ STV0900_IQ_SWAPPED ++}; ++ ++enum fe_stv0900_diseqc_mode { ++ STV0900_22KHZ_Continues = 0, ++ STV0900_DISEQC_2_3_PWM = 2, ++ STV0900_DISEQC_3_3_PWM = 3, ++ STV0900_DISEQC_2_3_ENVELOP = 4, ++ STV0900_DISEQC_3_3_ENVELOP = 5 ++}; ++ ++enum fe_stv0900_demod_mode { ++ STV0900_SINGLE = 0, ++ STV0900_DUAL ++}; ++ ++struct stv0900_init_params{ ++ u32 dmd_ref_clk;/* Reference,Input clock for the demod in Hz */ ++ ++ /* Demodulator Type (single demod or dual demod) */ ++ enum fe_stv0900_demod_mode demod_mode; ++ enum fe_stv0900_rolloff rolloff; ++ enum fe_stv0900_clock_type path1_ts_clock; ++ ++ u8 tun1_maddress; ++ int tuner1_adc; ++ int tuner1_type; ++ ++ /* IQ from the tuner1 to the demod */ ++ enum stv0900_iq_inversion tun1_iq_inv; ++ enum fe_stv0900_clock_type path2_ts_clock; ++ ++ u8 tun2_maddress; ++ int tuner2_adc; ++ int tuner2_type; ++ ++ /* IQ from the tuner2 to the demod */ ++ enum stv0900_iq_inversion tun2_iq_inv; ++ struct stv0900_reg *ts_config; ++}; ++ ++struct stv0900_search_params { ++ enum fe_stv0900_demod_num path;/* Path Used demod1 or 2 */ ++ ++ u32 frequency;/* Transponder frequency (in KHz) */ ++ u32 symbol_rate;/* Transponder symbol rate (in bds)*/ ++ u32 search_range;/* Range of the search (in Hz) */ ++ ++ enum fe_stv0900_search_standard standard; ++ enum fe_stv0900_modulation modulation; ++ enum fe_stv0900_fec fec; ++ enum fe_stv0900_modcode modcode; ++ enum fe_stv0900_search_iq iq_inversion; ++ enum fe_stv0900_search_algo search_algo; ++ ++}; ++ ++struct stv0900_signal_info { ++ int locked;/* Transponder locked */ ++ u32 frequency;/* Transponder frequency (in KHz) */ ++ u32 symbol_rate;/* Transponder symbol rate (in Mbds) */ ++ ++ enum fe_stv0900_tracking_standard standard; ++ enum fe_stv0900_fec fec; ++ enum fe_stv0900_modcode modcode; ++ enum fe_stv0900_modulation modulation; ++ enum fe_stv0900_pilot pilot; ++ enum fe_stv0900_frame_length frame_len; ++ enum stv0900_iq_inversion spectrum; ++ enum fe_stv0900_rolloff rolloff; ++ ++ s32 Power;/* Power of the RF signal (dBm) */ ++ s32 C_N;/* Carrier to noise ratio (dB x10)*/ ++ u32 BER;/* Bit error rate (x10^7) */ ++ ++}; ++ ++struct stv0900_internal{ ++ s32 quartz; ++ s32 mclk; ++ /* manual RollOff for DVBS1/DSS only */ ++ enum fe_stv0900_rolloff rolloff; ++ /* Demodulator use for single demod or for dual demod) */ ++ enum fe_stv0900_demod_mode demod_mode; ++ ++ /*Demods */ ++ s32 freq[2]; ++ s32 bw[2]; ++ s32 symbol_rate[2]; ++ s32 srch_range[2]; ++ /* for software/auto tuner */ ++ int tuner_type[2]; ++ ++ /* algorithm for search Blind, Cold or Warm*/ ++ enum fe_stv0900_search_algo srch_algo[2]; ++ /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/ ++ enum fe_stv0900_search_standard srch_standard[2]; ++ /* inversion search : auto, auto norma first, normal or inverted */ ++ enum fe_stv0900_search_iq srch_iq_inv[2]; ++ enum fe_stv0900_modcode modcode[2]; ++ enum fe_stv0900_modulation modulation[2]; ++ enum fe_stv0900_fec fec[2]; ++ ++ struct stv0900_signal_info result[2]; ++ enum fe_stv0900_error err[2]; ++ ++ ++ struct i2c_adapter *i2c_adap; ++ u8 i2c_addr; ++ u8 clkmode;/* 0 for CLKI, 2 for XTALI */ ++ u8 chip_id; ++ struct stv0900_reg *ts_config; ++ enum fe_stv0900_error errs; ++ int dmds_used; ++}; ++ ++/* state for each demod */ ++struct stv0900_state { ++ /* pointer for internal params, one for each pair of demods */ ++ struct stv0900_internal *internal; ++ struct i2c_adapter *i2c_adap; ++ const struct stv0900_config *config; ++ struct dvb_frontend frontend; ++ int demod; ++}; ++ ++extern int stvdebug; ++ ++extern s32 ge2comp(s32 a, s32 width); ++ ++extern void stv0900_write_reg(struct stv0900_internal *i_params, ++ u16 reg_addr, u8 reg_data); ++ ++extern u8 stv0900_read_reg(struct stv0900_internal *i_params, ++ u16 reg_addr); ++ ++extern void stv0900_write_bits(struct stv0900_internal *i_params, ++ u32 label, u8 val); ++ ++extern u8 stv0900_get_bits(struct stv0900_internal *i_params, ++ u32 label); ++ ++extern int stv0900_get_demod_lock(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod, s32 time_out); ++extern int stv0900_check_signal_presence(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod); ++ ++extern enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe); ++ ++extern void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, ++ u32 bandwidth); ++extern void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth); ++ ++extern void stv0900_start_search(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod); ++ ++extern u8 stv0900_get_optim_carr_loop(s32 srate, ++ enum fe_stv0900_modcode modcode, ++ s32 pilot, u8 chip_id); ++ ++extern u8 stv0900_get_optim_short_carr_loop(s32 srate, ++ enum fe_stv0900_modulation modulation, ++ u8 chip_id); ++ ++extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod); ++ ++extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod); ++ ++extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params, ++ enum fe_stv0900_demod_num demod); ++ ++extern enum ++fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, ++ enum fe_stv0900_demod_num demod); ++ ++extern u32 ++stv0900_get_freq_auto(struct stv0900_internal *intp, int demod); ++ ++extern void ++stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, ++ u32 Bandwidth, int demod); ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stv0900_reg.h b/drivers/media/dvb-frontends/stv0900_reg.h +new file mode 100644 +index 0000000..511ed2a +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0900_reg.h +@@ -0,0 +1,3984 @@ ++/* ++ * stv0900_reg.h ++ * ++ * Driver for ST STV0900 satellite demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef STV0900_REG_H ++#define STV0900_REG_H ++ ++extern s32 shiftx(s32 x, int demod, s32 shift); ++ ++#define REGx(x) shiftx(x, demod, 0x200) ++#define FLDx(x) shiftx(x, demod, 0x2000000) ++ ++/*MID*/ ++#define R0900_MID 0xf100 ++#define F0900_MCHIP_IDENT 0xf10000f0 ++#define F0900_MRELEASE 0xf100000f ++ ++/*DACR1*/ ++#define R0900_DACR1 0xf113 ++#define F0900_DAC_MODE 0xf11300e0 ++#define F0900_DAC_VALUE1 0xf113000f ++ ++/*DACR2*/ ++#define R0900_DACR2 0xf114 ++#define F0900_DAC_VALUE0 0xf11400ff ++ ++/*OUTCFG*/ ++#define R0900_OUTCFG 0xf11c ++#define F0900_OUTSERRS1_HZ 0xf11c0040 ++#define F0900_OUTSERRS2_HZ 0xf11c0020 ++#define F0900_OUTSERRS3_HZ 0xf11c0010 ++#define F0900_OUTPARRS3_HZ 0xf11c0008 ++ ++/*IRQSTATUS3*/ ++#define R0900_IRQSTATUS3 0xf120 ++#define F0900_SPLL_LOCK 0xf1200020 ++#define F0900_SSTREAM_LCK_3 0xf1200010 ++#define F0900_SSTREAM_LCK_2 0xf1200008 ++#define F0900_SSTREAM_LCK_1 0xf1200004 ++#define F0900_SDVBS1_PRF_2 0xf1200002 ++#define F0900_SDVBS1_PRF_1 0xf1200001 ++ ++/*IRQSTATUS2*/ ++#define R0900_IRQSTATUS2 0xf121 ++#define F0900_SSPY_ENDSIM_3 0xf1210080 ++#define F0900_SSPY_ENDSIM_2 0xf1210040 ++#define F0900_SSPY_ENDSIM_1 0xf1210020 ++#define F0900_SPKTDEL_ERROR_2 0xf1210010 ++#define F0900_SPKTDEL_LOCKB_2 0xf1210008 ++#define F0900_SPKTDEL_LOCK_2 0xf1210004 ++#define F0900_SPKTDEL_ERROR_1 0xf1210002 ++#define F0900_SPKTDEL_LOCKB_1 0xf1210001 ++ ++/*IRQSTATUS1*/ ++#define R0900_IRQSTATUS1 0xf122 ++#define F0900_SPKTDEL_LOCK_1 0xf1220080 ++#define F0900_SDEMOD_LOCKB_2 0xf1220004 ++#define F0900_SDEMOD_LOCK_2 0xf1220002 ++#define F0900_SDEMOD_IRQ_2 0xf1220001 ++ ++/*IRQSTATUS0*/ ++#define R0900_IRQSTATUS0 0xf123 ++#define F0900_SDEMOD_LOCKB_1 0xf1230080 ++#define F0900_SDEMOD_LOCK_1 0xf1230040 ++#define F0900_SDEMOD_IRQ_1 0xf1230020 ++#define F0900_SBCH_ERRFLAG 0xf1230010 ++#define F0900_SDISEQC2RX_IRQ 0xf1230008 ++#define F0900_SDISEQC2TX_IRQ 0xf1230004 ++#define F0900_SDISEQC1RX_IRQ 0xf1230002 ++#define F0900_SDISEQC1TX_IRQ 0xf1230001 ++ ++/*IRQMASK3*/ ++#define R0900_IRQMASK3 0xf124 ++#define F0900_MPLL_LOCK 0xf1240020 ++#define F0900_MSTREAM_LCK_3 0xf1240010 ++#define F0900_MSTREAM_LCK_2 0xf1240008 ++#define F0900_MSTREAM_LCK_1 0xf1240004 ++#define F0900_MDVBS1_PRF_2 0xf1240002 ++#define F0900_MDVBS1_PRF_1 0xf1240001 ++ ++/*IRQMASK2*/ ++#define R0900_IRQMASK2 0xf125 ++#define F0900_MSPY_ENDSIM_3 0xf1250080 ++#define F0900_MSPY_ENDSIM_2 0xf1250040 ++#define F0900_MSPY_ENDSIM_1 0xf1250020 ++#define F0900_MPKTDEL_ERROR_2 0xf1250010 ++#define F0900_MPKTDEL_LOCKB_2 0xf1250008 ++#define F0900_MPKTDEL_LOCK_2 0xf1250004 ++#define F0900_MPKTDEL_ERROR_1 0xf1250002 ++#define F0900_MPKTDEL_LOCKB_1 0xf1250001 ++ ++/*IRQMASK1*/ ++#define R0900_IRQMASK1 0xf126 ++#define F0900_MPKTDEL_LOCK_1 0xf1260080 ++#define F0900_MEXTPINB2 0xf1260040 ++#define F0900_MEXTPIN2 0xf1260020 ++#define F0900_MEXTPINB1 0xf1260010 ++#define F0900_MEXTPIN1 0xf1260008 ++#define F0900_MDEMOD_LOCKB_2 0xf1260004 ++#define F0900_MDEMOD_LOCK_2 0xf1260002 ++#define F0900_MDEMOD_IRQ_2 0xf1260001 ++ ++/*IRQMASK0*/ ++#define R0900_IRQMASK0 0xf127 ++#define F0900_MDEMOD_LOCKB_1 0xf1270080 ++#define F0900_MDEMOD_LOCK_1 0xf1270040 ++#define F0900_MDEMOD_IRQ_1 0xf1270020 ++#define F0900_MBCH_ERRFLAG 0xf1270010 ++#define F0900_MDISEQC2RX_IRQ 0xf1270008 ++#define F0900_MDISEQC2TX_IRQ 0xf1270004 ++#define F0900_MDISEQC1RX_IRQ 0xf1270002 ++#define F0900_MDISEQC1TX_IRQ 0xf1270001 ++ ++/*I2CCFG*/ ++#define R0900_I2CCFG 0xf129 ++#define F0900_I2C_FASTMODE 0xf1290008 ++#define F0900_I2CADDR_INC 0xf1290003 ++ ++/*P1_I2CRPT*/ ++#define R0900_P1_I2CRPT 0xf12a ++#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1) ++#define F0900_P1_I2CT_ON 0xf12a0080 ++#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000) ++#define F0900_P1_ENARPT_LEVEL 0xf12a0070 ++#define F0900_P1_SCLT_DELAY 0xf12a0008 ++#define F0900_P1_STOP_ENABLE 0xf12a0004 ++#define F0900_P1_STOP_SDAT2SDA 0xf12a0002 ++ ++/*P2_I2CRPT*/ ++#define R0900_P2_I2CRPT 0xf12b ++#define F0900_P2_I2CT_ON 0xf12b0080 ++#define F0900_P2_ENARPT_LEVEL 0xf12b0070 ++#define F0900_P2_SCLT_DELAY 0xf12b0008 ++#define F0900_P2_STOP_ENABLE 0xf12b0004 ++#define F0900_P2_STOP_SDAT2SDA 0xf12b0002 ++ ++/*IOPVALUE6*/ ++#define R0900_IOPVALUE6 0xf138 ++#define F0900_VSCL 0xf1380004 ++#define F0900_VSDA 0xf1380002 ++#define F0900_VDATA3_0 0xf1380001 ++ ++/*IOPVALUE5*/ ++#define R0900_IOPVALUE5 0xf139 ++#define F0900_VDATA3_1 0xf1390080 ++#define F0900_VDATA3_2 0xf1390040 ++#define F0900_VDATA3_3 0xf1390020 ++#define F0900_VDATA3_4 0xf1390010 ++#define F0900_VDATA3_5 0xf1390008 ++#define F0900_VDATA3_6 0xf1390004 ++#define F0900_VDATA3_7 0xf1390002 ++#define F0900_VCLKOUT3 0xf1390001 ++ ++/*IOPVALUE4*/ ++#define R0900_IOPVALUE4 0xf13a ++#define F0900_VSTROUT3 0xf13a0080 ++#define F0900_VDPN3 0xf13a0040 ++#define F0900_VERROR3 0xf13a0020 ++#define F0900_VDATA2_7 0xf13a0010 ++#define F0900_VCLKOUT2 0xf13a0008 ++#define F0900_VSTROUT2 0xf13a0004 ++#define F0900_VDPN2 0xf13a0002 ++#define F0900_VERROR2 0xf13a0001 ++ ++/*IOPVALUE3*/ ++#define R0900_IOPVALUE3 0xf13b ++#define F0900_VDATA1_7 0xf13b0080 ++#define F0900_VCLKOUT1 0xf13b0040 ++#define F0900_VSTROUT1 0xf13b0020 ++#define F0900_VDPN1 0xf13b0010 ++#define F0900_VERROR1 0xf13b0008 ++#define F0900_VCLKOUT27 0xf13b0004 ++#define F0900_VDISEQCOUT2 0xf13b0002 ++#define F0900_VSCLT2 0xf13b0001 ++ ++/*IOPVALUE2*/ ++#define R0900_IOPVALUE2 0xf13c ++#define F0900_VSDAT2 0xf13c0080 ++#define F0900_VAGCRF2 0xf13c0040 ++#define F0900_VDISEQCOUT1 0xf13c0020 ++#define F0900_VSCLT1 0xf13c0010 ++#define F0900_VSDAT1 0xf13c0008 ++#define F0900_VAGCRF1 0xf13c0004 ++#define F0900_VDIRCLK 0xf13c0002 ++#define F0900_VSTDBY 0xf13c0001 ++ ++/*IOPVALUE1*/ ++#define R0900_IOPVALUE1 0xf13d ++#define F0900_VCS1 0xf13d0080 ++#define F0900_VCS0 0xf13d0040 ++#define F0900_VGPIO13 0xf13d0020 ++#define F0900_VGPIO12 0xf13d0010 ++#define F0900_VGPIO11 0xf13d0008 ++#define F0900_VGPIO10 0xf13d0004 ++#define F0900_VGPIO9 0xf13d0002 ++#define F0900_VGPIO8 0xf13d0001 ++ ++/*IOPVALUE0*/ ++#define R0900_IOPVALUE0 0xf13e ++#define F0900_VGPIO7 0xf13e0080 ++#define F0900_VGPIO6 0xf13e0040 ++#define F0900_VGPIO5 0xf13e0020 ++#define F0900_VGPIO4 0xf13e0010 ++#define F0900_VGPIO3 0xf13e0008 ++#define F0900_VGPIO2 0xf13e0004 ++#define F0900_VGPIO1 0xf13e0002 ++#define F0900_VCLKI2 0xf13e0001 ++ ++/*CLKI2CFG*/ ++#define R0900_CLKI2CFG 0xf140 ++#define F0900_CLKI2_OPD 0xf1400080 ++#define F0900_CLKI2_CONFIG 0xf140007e ++#define F0900_CLKI2_XOR 0xf1400001 ++ ++/*GPIO1CFG*/ ++#define R0900_GPIO1CFG 0xf141 ++#define F0900_GPIO1_OPD 0xf1410080 ++#define F0900_GPIO1_CONFIG 0xf141007e ++#define F0900_GPIO1_XOR 0xf1410001 ++ ++/*GPIO2CFG*/ ++#define R0900_GPIO2CFG 0xf142 ++#define F0900_GPIO2_OPD 0xf1420080 ++#define F0900_GPIO2_CONFIG 0xf142007e ++#define F0900_GPIO2_XOR 0xf1420001 ++ ++/*GPIO3CFG*/ ++#define R0900_GPIO3CFG 0xf143 ++#define F0900_GPIO3_OPD 0xf1430080 ++#define F0900_GPIO3_CONFIG 0xf143007e ++#define F0900_GPIO3_XOR 0xf1430001 ++ ++/*GPIO4CFG*/ ++#define R0900_GPIO4CFG 0xf144 ++#define F0900_GPIO4_OPD 0xf1440080 ++#define F0900_GPIO4_CONFIG 0xf144007e ++#define F0900_GPIO4_XOR 0xf1440001 ++ ++/*GPIO5CFG*/ ++#define R0900_GPIO5CFG 0xf145 ++#define F0900_GPIO5_OPD 0xf1450080 ++#define F0900_GPIO5_CONFIG 0xf145007e ++#define F0900_GPIO5_XOR 0xf1450001 ++ ++/*GPIO6CFG*/ ++#define R0900_GPIO6CFG 0xf146 ++#define F0900_GPIO6_OPD 0xf1460080 ++#define F0900_GPIO6_CONFIG 0xf146007e ++#define F0900_GPIO6_XOR 0xf1460001 ++ ++/*GPIO7CFG*/ ++#define R0900_GPIO7CFG 0xf147 ++#define F0900_GPIO7_OPD 0xf1470080 ++#define F0900_GPIO7_CONFIG 0xf147007e ++#define F0900_GPIO7_XOR 0xf1470001 ++ ++/*GPIO8CFG*/ ++#define R0900_GPIO8CFG 0xf148 ++#define F0900_GPIO8_OPD 0xf1480080 ++#define F0900_GPIO8_CONFIG 0xf148007e ++#define F0900_GPIO8_XOR 0xf1480001 ++ ++/*GPIO9CFG*/ ++#define R0900_GPIO9CFG 0xf149 ++#define F0900_GPIO9_OPD 0xf1490080 ++#define F0900_GPIO9_CONFIG 0xf149007e ++#define F0900_GPIO9_XOR 0xf1490001 ++ ++/*GPIO10CFG*/ ++#define R0900_GPIO10CFG 0xf14a ++#define F0900_GPIO10_OPD 0xf14a0080 ++#define F0900_GPIO10_CONFIG 0xf14a007e ++#define F0900_GPIO10_XOR 0xf14a0001 ++ ++/*GPIO11CFG*/ ++#define R0900_GPIO11CFG 0xf14b ++#define F0900_GPIO11_OPD 0xf14b0080 ++#define F0900_GPIO11_CONFIG 0xf14b007e ++#define F0900_GPIO11_XOR 0xf14b0001 ++ ++/*GPIO12CFG*/ ++#define R0900_GPIO12CFG 0xf14c ++#define F0900_GPIO12_OPD 0xf14c0080 ++#define F0900_GPIO12_CONFIG 0xf14c007e ++#define F0900_GPIO12_XOR 0xf14c0001 ++ ++/*GPIO13CFG*/ ++#define R0900_GPIO13CFG 0xf14d ++#define F0900_GPIO13_OPD 0xf14d0080 ++#define F0900_GPIO13_CONFIG 0xf14d007e ++#define F0900_GPIO13_XOR 0xf14d0001 ++ ++/*CS0CFG*/ ++#define R0900_CS0CFG 0xf14e ++#define F0900_CS0_OPD 0xf14e0080 ++#define F0900_CS0_CONFIG 0xf14e007e ++#define F0900_CS0_XOR 0xf14e0001 ++ ++/*CS1CFG*/ ++#define R0900_CS1CFG 0xf14f ++#define F0900_CS1_OPD 0xf14f0080 ++#define F0900_CS1_CONFIG 0xf14f007e ++#define F0900_CS1_XOR 0xf14f0001 ++ ++/*STDBYCFG*/ ++#define R0900_STDBYCFG 0xf150 ++#define F0900_STDBY_OPD 0xf1500080 ++#define F0900_STDBY_CONFIG 0xf150007e ++#define F0900_STBDY_XOR 0xf1500001 ++ ++/*DIRCLKCFG*/ ++#define R0900_DIRCLKCFG 0xf151 ++#define F0900_DIRCLK_OPD 0xf1510080 ++#define F0900_DIRCLK_CONFIG 0xf151007e ++#define F0900_DIRCLK_XOR 0xf1510001 ++ ++/*AGCRF1CFG*/ ++#define R0900_AGCRF1CFG 0xf152 ++#define F0900_AGCRF1_OPD 0xf1520080 ++#define F0900_AGCRF1_CONFIG 0xf152007e ++#define F0900_AGCRF1_XOR 0xf1520001 ++ ++/*SDAT1CFG*/ ++#define R0900_SDAT1CFG 0xf153 ++#define F0900_SDAT1_OPD 0xf1530080 ++#define F0900_SDAT1_CONFIG 0xf153007e ++#define F0900_SDAT1_XOR 0xf1530001 ++ ++/*SCLT1CFG*/ ++#define R0900_SCLT1CFG 0xf154 ++#define F0900_SCLT1_OPD 0xf1540080 ++#define F0900_SCLT1_CONFIG 0xf154007e ++#define F0900_SCLT1_XOR 0xf1540001 ++ ++/*DISEQCO1CFG*/ ++#define R0900_DISEQCO1CFG 0xf155 ++#define F0900_DISEQCO1_OPD 0xf1550080 ++#define F0900_DISEQCO1_CONFIG 0xf155007e ++#define F0900_DISEQC1_XOR 0xf1550001 ++ ++/*AGCRF2CFG*/ ++#define R0900_AGCRF2CFG 0xf156 ++#define F0900_AGCRF2_OPD 0xf1560080 ++#define F0900_AGCRF2_CONFIG 0xf156007e ++#define F0900_AGCRF2_XOR 0xf1560001 ++ ++/*SDAT2CFG*/ ++#define R0900_SDAT2CFG 0xf157 ++#define F0900_SDAT2_OPD 0xf1570080 ++#define F0900_SDAT2_CONFIG 0xf157007e ++#define F0900_SDAT2_XOR 0xf1570001 ++ ++/*SCLT2CFG*/ ++#define R0900_SCLT2CFG 0xf158 ++#define F0900_SCLT2_OPD 0xf1580080 ++#define F0900_SCLT2_CONFIG 0xf158007e ++#define F0900_SCLT2_XOR 0xf1580001 ++ ++/*DISEQCO2CFG*/ ++#define R0900_DISEQCO2CFG 0xf159 ++#define F0900_DISEQCO2_OPD 0xf1590080 ++#define F0900_DISEQCO2_CONFIG 0xf159007e ++#define F0900_DISEQC2_XOR 0xf1590001 ++ ++/*CLKOUT27CFG*/ ++#define R0900_CLKOUT27CFG 0xf15a ++#define F0900_CLKOUT27_OPD 0xf15a0080 ++#define F0900_CLKOUT27_CONFIG 0xf15a007e ++#define F0900_CLKOUT27_XOR 0xf15a0001 ++ ++/*ERROR1CFG*/ ++#define R0900_ERROR1CFG 0xf15b ++#define F0900_ERROR1_OPD 0xf15b0080 ++#define F0900_ERROR1_CONFIG 0xf15b007e ++#define F0900_ERROR1_XOR 0xf15b0001 ++ ++/*DPN1CFG*/ ++#define R0900_DPN1CFG 0xf15c ++#define F0900_DPN1_OPD 0xf15c0080 ++#define F0900_DPN1_CONFIG 0xf15c007e ++#define F0900_DPN1_XOR 0xf15c0001 ++ ++/*STROUT1CFG*/ ++#define R0900_STROUT1CFG 0xf15d ++#define F0900_STROUT1_OPD 0xf15d0080 ++#define F0900_STROUT1_CONFIG 0xf15d007e ++#define F0900_STROUT1_XOR 0xf15d0001 ++ ++/*CLKOUT1CFG*/ ++#define R0900_CLKOUT1CFG 0xf15e ++#define F0900_CLKOUT1_OPD 0xf15e0080 ++#define F0900_CLKOUT1_CONFIG 0xf15e007e ++#define F0900_CLKOUT1_XOR 0xf15e0001 ++ ++/*DATA71CFG*/ ++#define R0900_DATA71CFG 0xf15f ++#define F0900_DATA71_OPD 0xf15f0080 ++#define F0900_DATA71_CONFIG 0xf15f007e ++#define F0900_DATA71_XOR 0xf15f0001 ++ ++/*ERROR2CFG*/ ++#define R0900_ERROR2CFG 0xf160 ++#define F0900_ERROR2_OPD 0xf1600080 ++#define F0900_ERROR2_CONFIG 0xf160007e ++#define F0900_ERROR2_XOR 0xf1600001 ++ ++/*DPN2CFG*/ ++#define R0900_DPN2CFG 0xf161 ++#define F0900_DPN2_OPD 0xf1610080 ++#define F0900_DPN2_CONFIG 0xf161007e ++#define F0900_DPN2_XOR 0xf1610001 ++ ++/*STROUT2CFG*/ ++#define R0900_STROUT2CFG 0xf162 ++#define F0900_STROUT2_OPD 0xf1620080 ++#define F0900_STROUT2_CONFIG 0xf162007e ++#define F0900_STROUT2_XOR 0xf1620001 ++ ++/*CLKOUT2CFG*/ ++#define R0900_CLKOUT2CFG 0xf163 ++#define F0900_CLKOUT2_OPD 0xf1630080 ++#define F0900_CLKOUT2_CONFIG 0xf163007e ++#define F0900_CLKOUT2_XOR 0xf1630001 ++ ++/*DATA72CFG*/ ++#define R0900_DATA72CFG 0xf164 ++#define F0900_DATA72_OPD 0xf1640080 ++#define F0900_DATA72_CONFIG 0xf164007e ++#define F0900_DATA72_XOR 0xf1640001 ++ ++/*ERROR3CFG*/ ++#define R0900_ERROR3CFG 0xf165 ++#define F0900_ERROR3_OPD 0xf1650080 ++#define F0900_ERROR3_CONFIG 0xf165007e ++#define F0900_ERROR3_XOR 0xf1650001 ++ ++/*DPN3CFG*/ ++#define R0900_DPN3CFG 0xf166 ++#define F0900_DPN3_OPD 0xf1660080 ++#define F0900_DPN3_CONFIG 0xf166007e ++#define F0900_DPN3_XOR 0xf1660001 ++ ++/*STROUT3CFG*/ ++#define R0900_STROUT3CFG 0xf167 ++#define F0900_STROUT3_OPD 0xf1670080 ++#define F0900_STROUT3_CONFIG 0xf167007e ++#define F0900_STROUT3_XOR 0xf1670001 ++ ++/*CLKOUT3CFG*/ ++#define R0900_CLKOUT3CFG 0xf168 ++#define F0900_CLKOUT3_OPD 0xf1680080 ++#define F0900_CLKOUT3_CONFIG 0xf168007e ++#define F0900_CLKOUT3_XOR 0xf1680001 ++ ++/*DATA73CFG*/ ++#define R0900_DATA73CFG 0xf169 ++#define F0900_DATA73_OPD 0xf1690080 ++#define F0900_DATA73_CONFIG 0xf169007e ++#define F0900_DATA73_XOR 0xf1690001 ++ ++/*STRSTATUS1*/ ++#define R0900_STRSTATUS1 0xf16a ++#define F0900_STRSTATUS_SEL2 0xf16a00f0 ++#define F0900_STRSTATUS_SEL1 0xf16a000f ++ ++/*STRSTATUS2*/ ++#define R0900_STRSTATUS2 0xf16b ++#define F0900_STRSTATUS_SEL4 0xf16b00f0 ++#define F0900_STRSTATUS_SEL3 0xf16b000f ++ ++/*STRSTATUS3*/ ++#define R0900_STRSTATUS3 0xf16c ++#define F0900_STRSTATUS_SEL6 0xf16c00f0 ++#define F0900_STRSTATUS_SEL5 0xf16c000f ++ ++/*FSKTFC2*/ ++#define R0900_FSKTFC2 0xf170 ++#define F0900_FSKT_KMOD 0xf17000fc ++#define F0900_FSKT_CAR2 0xf1700003 ++ ++/*FSKTFC1*/ ++#define R0900_FSKTFC1 0xf171 ++#define F0900_FSKT_CAR1 0xf17100ff ++ ++/*FSKTFC0*/ ++#define R0900_FSKTFC0 0xf172 ++#define F0900_FSKT_CAR0 0xf17200ff ++ ++/*FSKTDELTAF1*/ ++#define R0900_FSKTDELTAF1 0xf173 ++#define F0900_FSKT_DELTAF1 0xf173000f ++ ++/*FSKTDELTAF0*/ ++#define R0900_FSKTDELTAF0 0xf174 ++#define F0900_FSKT_DELTAF0 0xf17400ff ++ ++/*FSKTCTRL*/ ++#define R0900_FSKTCTRL 0xf175 ++#define F0900_FSKT_EN_SGN 0xf1750040 ++#define F0900_FSKT_MOD_SGN 0xf1750020 ++#define F0900_FSKT_MOD_EN 0xf175001c ++#define F0900_FSKT_DACMODE 0xf1750003 ++ ++/*FSKRFC2*/ ++#define R0900_FSKRFC2 0xf176 ++#define F0900_FSKR_DETSGN 0xf1760040 ++#define F0900_FSKR_OUTSGN 0xf1760020 ++#define F0900_FSKR_KAGC 0xf176001c ++#define F0900_FSKR_CAR2 0xf1760003 ++ ++/*FSKRFC1*/ ++#define R0900_FSKRFC1 0xf177 ++#define F0900_FSKR_CAR1 0xf17700ff ++ ++/*FSKRFC0*/ ++#define R0900_FSKRFC0 0xf178 ++#define F0900_FSKR_CAR0 0xf17800ff ++ ++/*FSKRK1*/ ++#define R0900_FSKRK1 0xf179 ++#define F0900_FSKR_K1_EXP 0xf17900e0 ++#define F0900_FSKR_K1_MANT 0xf179001f ++ ++/*FSKRK2*/ ++#define R0900_FSKRK2 0xf17a ++#define F0900_FSKR_K2_EXP 0xf17a00e0 ++#define F0900_FSKR_K2_MANT 0xf17a001f ++ ++/*FSKRAGCR*/ ++#define R0900_FSKRAGCR 0xf17b ++#define F0900_FSKR_OUTCTL 0xf17b00c0 ++#define F0900_FSKR_AGC_REF 0xf17b003f ++ ++/*FSKRAGC*/ ++#define R0900_FSKRAGC 0xf17c ++#define F0900_FSKR_AGC_ACCU 0xf17c00ff ++ ++/*FSKRALPHA*/ ++#define R0900_FSKRALPHA 0xf17d ++#define F0900_FSKR_ALPHA_EXP 0xf17d001c ++#define F0900_FSKR_ALPHA_M 0xf17d0003 ++ ++/*FSKRPLTH1*/ ++#define R0900_FSKRPLTH1 0xf17e ++#define F0900_FSKR_BETA 0xf17e00f0 ++#define F0900_FSKR_PLL_TRESH1 0xf17e000f ++ ++/*FSKRPLTH0*/ ++#define R0900_FSKRPLTH0 0xf17f ++#define F0900_FSKR_PLL_TRESH0 0xf17f00ff ++ ++/*FSKRDF1*/ ++#define R0900_FSKRDF1 0xf180 ++#define F0900_FSKR_OUT 0xf1800080 ++#define F0900_FSKR_DELTAF1 0xf180001f ++ ++/*FSKRDF0*/ ++#define R0900_FSKRDF0 0xf181 ++#define F0900_FSKR_DELTAF0 0xf18100ff ++ ++/*FSKRSTEPP*/ ++#define R0900_FSKRSTEPP 0xf182 ++#define F0900_FSKR_STEP_PLUS 0xf18200ff ++ ++/*FSKRSTEPM*/ ++#define R0900_FSKRSTEPM 0xf183 ++#define F0900_FSKR_STEP_MINUS 0xf18300ff ++ ++/*FSKRDET1*/ ++#define R0900_FSKRDET1 0xf184 ++#define F0900_FSKR_DETECT 0xf1840080 ++#define F0900_FSKR_CARDET_ACCU1 0xf184000f ++ ++/*FSKRDET0*/ ++#define R0900_FSKRDET0 0xf185 ++#define F0900_FSKR_CARDET_ACCU0 0xf18500ff ++ ++/*FSKRDTH1*/ ++#define R0900_FSKRDTH1 0xf186 ++#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0 ++#define F0900_FSKR_CARDET_THRESH1 0xf186000f ++ ++/*FSKRDTH0*/ ++#define R0900_FSKRDTH0 0xf187 ++#define F0900_FSKR_CARDET_THRESH0 0xf18700ff ++ ++/*FSKRLOSS*/ ++#define R0900_FSKRLOSS 0xf188 ++#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff ++ ++/*P2_DISTXCTL*/ ++#define R0900_P2_DISTXCTL 0xf190 ++#define F0900_P2_TIM_OFF 0xf1900080 ++#define F0900_P2_DISEQC_RESET 0xf1900040 ++#define F0900_P2_TIM_CMD 0xf1900030 ++#define F0900_P2_DIS_PRECHARGE 0xf1900008 ++#define F0900_P2_DISTX_MODE 0xf1900007 ++ ++/*P2_DISRXCTL*/ ++#define R0900_P2_DISRXCTL 0xf191 ++#define F0900_P2_RECEIVER_ON 0xf1910080 ++#define F0900_P2_IGNO_SHORT22K 0xf1910040 ++#define F0900_P2_ONECHIP_TRX 0xf1910020 ++#define F0900_P2_EXT_ENVELOP 0xf1910010 ++#define F0900_P2_PIN_SELECT0 0xf191000c ++#define F0900_P2_IRQ_RXEND 0xf1910002 ++#define F0900_P2_IRQ_4NBYTES 0xf1910001 ++ ++/*P2_DISRX_ST0*/ ++#define R0900_P2_DISRX_ST0 0xf194 ++#define F0900_P2_RX_END 0xf1940080 ++#define F0900_P2_RX_ACTIVE 0xf1940040 ++#define F0900_P2_SHORT_22KHZ 0xf1940020 ++#define F0900_P2_CONT_TONE 0xf1940010 ++#define F0900_P2_FIFO_4BREADY 0xf1940008 ++#define F0900_P2_FIFO_EMPTY 0xf1940004 ++#define F0900_P2_ABORT_DISRX 0xf1940001 ++ ++/*P2_DISRX_ST1*/ ++#define R0900_P2_DISRX_ST1 0xf195 ++#define F0900_P2_RX_FAIL 0xf1950080 ++#define F0900_P2_FIFO_PARITYFAIL 0xf1950040 ++#define F0900_P2_RX_NONBYTE 0xf1950020 ++#define F0900_P2_FIFO_OVERFLOW 0xf1950010 ++#define F0900_P2_FIFO_BYTENBR 0xf195000f ++ ++/*P2_DISRXDATA*/ ++#define R0900_P2_DISRXDATA 0xf196 ++#define F0900_P2_DISRX_DATA 0xf19600ff ++ ++/*P2_DISTXDATA*/ ++#define R0900_P2_DISTXDATA 0xf197 ++#define F0900_P2_DISEQC_FIFO 0xf19700ff ++ ++/*P2_DISTXSTATUS*/ ++#define R0900_P2_DISTXSTATUS 0xf198 ++#define F0900_P2_TX_FAIL 0xf1980080 ++#define F0900_P2_FIFO_FULL 0xf1980040 ++#define F0900_P2_TX_IDLE 0xf1980020 ++#define F0900_P2_GAP_BURST 0xf1980010 ++#define F0900_P2_TXFIFO_BYTES 0xf198000f ++ ++/*P2_F22TX*/ ++#define R0900_P2_F22TX 0xf199 ++#define F0900_P2_F22_REG 0xf19900ff ++ ++/*P2_F22RX*/ ++#define R0900_P2_F22RX 0xf19a ++#define F0900_P2_F22RX_REG 0xf19a00ff ++ ++/*P2_ACRPRESC*/ ++#define R0900_P2_ACRPRESC 0xf19c ++#define F0900_P2_ACR_PRESC 0xf19c0007 ++ ++/*P2_ACRDIV*/ ++#define R0900_P2_ACRDIV 0xf19d ++#define F0900_P2_ACR_DIV 0xf19d00ff ++ ++/*P1_DISTXCTL*/ ++#define R0900_P1_DISTXCTL 0xf1a0 ++#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10) ++#define F0900_P1_TIM_OFF 0xf1a00080 ++#define F0900_P1_DISEQC_RESET 0xf1a00040 ++#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000) ++#define F0900_P1_TIM_CMD 0xf1a00030 ++#define F0900_P1_DIS_PRECHARGE 0xf1a00008 ++#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000) ++#define F0900_P1_DISTX_MODE 0xf1a00007 ++#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000) ++ ++/*P1_DISRXCTL*/ ++#define R0900_P1_DISRXCTL 0xf1a1 ++#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10) ++#define F0900_P1_RECEIVER_ON 0xf1a10080 ++#define F0900_P1_IGNO_SHORT22K 0xf1a10040 ++#define F0900_P1_ONECHIP_TRX 0xf1a10020 ++#define F0900_P1_EXT_ENVELOP 0xf1a10010 ++#define F0900_P1_PIN_SELECT0 0xf1a1000c ++#define F0900_P1_IRQ_RXEND 0xf1a10002 ++#define F0900_P1_IRQ_4NBYTES 0xf1a10001 ++ ++/*P1_DISRX_ST0*/ ++#define R0900_P1_DISRX_ST0 0xf1a4 ++#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10) ++#define F0900_P1_RX_END 0xf1a40080 ++#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000) ++#define F0900_P1_RX_ACTIVE 0xf1a40040 ++#define F0900_P1_SHORT_22KHZ 0xf1a40020 ++#define F0900_P1_CONT_TONE 0xf1a40010 ++#define F0900_P1_FIFO_4BREADY 0xf1a40008 ++#define F0900_P1_FIFO_EMPTY 0xf1a40004 ++#define F0900_P1_ABORT_DISRX 0xf1a40001 ++ ++/*P1_DISRX_ST1*/ ++#define R0900_P1_DISRX_ST1 0xf1a5 ++#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10) ++#define F0900_P1_RX_FAIL 0xf1a50080 ++#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040 ++#define F0900_P1_RX_NONBYTE 0xf1a50020 ++#define F0900_P1_FIFO_OVERFLOW 0xf1a50010 ++#define F0900_P1_FIFO_BYTENBR 0xf1a5000f ++#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000) ++ ++/*P1_DISRXDATA*/ ++#define R0900_P1_DISRXDATA 0xf1a6 ++#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10) ++#define F0900_P1_DISRX_DATA 0xf1a600ff ++ ++/*P1_DISTXDATA*/ ++#define R0900_P1_DISTXDATA 0xf1a7 ++#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10) ++#define F0900_P1_DISEQC_FIFO 0xf1a700ff ++ ++/*P1_DISTXSTATUS*/ ++#define R0900_P1_DISTXSTATUS 0xf1a8 ++#define F0900_P1_TX_FAIL 0xf1a80080 ++#define F0900_P1_FIFO_FULL 0xf1a80040 ++#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000) ++#define F0900_P1_TX_IDLE 0xf1a80020 ++#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000) ++#define F0900_P1_GAP_BURST 0xf1a80010 ++#define F0900_P1_TXFIFO_BYTES 0xf1a8000f ++ ++/*P1_F22TX*/ ++#define R0900_P1_F22TX 0xf1a9 ++#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10) ++#define F0900_P1_F22_REG 0xf1a900ff ++ ++/*P1_F22RX*/ ++#define R0900_P1_F22RX 0xf1aa ++#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10) ++#define F0900_P1_F22RX_REG 0xf1aa00ff ++ ++/*P1_ACRPRESC*/ ++#define R0900_P1_ACRPRESC 0xf1ac ++#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10) ++#define F0900_P1_ACR_PRESC 0xf1ac0007 ++ ++/*P1_ACRDIV*/ ++#define R0900_P1_ACRDIV 0xf1ad ++#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10) ++#define F0900_P1_ACR_DIV 0xf1ad00ff ++ ++/*NCOARSE*/ ++#define R0900_NCOARSE 0xf1b3 ++#define F0900_M_DIV 0xf1b300ff ++ ++/*SYNTCTRL*/ ++#define R0900_SYNTCTRL 0xf1b6 ++#define F0900_STANDBY 0xf1b60080 ++#define F0900_BYPASSPLLCORE 0xf1b60040 ++#define F0900_SELX1RATIO 0xf1b60020 ++#define F0900_STOP_PLL 0xf1b60008 ++#define F0900_BYPASSPLLFSK 0xf1b60004 ++#define F0900_SELOSCI 0xf1b60002 ++#define F0900_BYPASSPLLADC 0xf1b60001 ++ ++/*FILTCTRL*/ ++#define R0900_FILTCTRL 0xf1b7 ++#define F0900_INV_CLK135 0xf1b70080 ++#define F0900_SEL_FSKCKDIV 0xf1b70004 ++#define F0900_INV_CLKFSK 0xf1b70002 ++#define F0900_BYPASS_APPLI 0xf1b70001 ++ ++/*PLLSTAT*/ ++#define R0900_PLLSTAT 0xf1b8 ++#define F0900_PLLLOCK 0xf1b80001 ++ ++/*STOPCLK1*/ ++#define R0900_STOPCLK1 0xf1c2 ++#define F0900_STOP_CLKPKDT2 0xf1c20040 ++#define F0900_STOP_CLKPKDT1 0xf1c20020 ++#define F0900_STOP_CLKFEC 0xf1c20010 ++#define F0900_STOP_CLKADCI2 0xf1c20008 ++#define F0900_INV_CLKADCI2 0xf1c20004 ++#define F0900_STOP_CLKADCI1 0xf1c20002 ++#define F0900_INV_CLKADCI1 0xf1c20001 ++ ++/*STOPCLK2*/ ++#define R0900_STOPCLK2 0xf1c3 ++#define F0900_STOP_CLKSAMP2 0xf1c30010 ++#define F0900_STOP_CLKSAMP1 0xf1c30008 ++#define F0900_STOP_CLKVIT2 0xf1c30004 ++#define F0900_STOP_CLKVIT1 0xf1c30002 ++#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2) ++#define F0900_STOP_CLKTS 0xf1c30001 ++ ++/*TSTTNR0*/ ++#define R0900_TSTTNR0 0xf1df ++#define F0900_SEL_FSK 0xf1df0080 ++#define F0900_FSK_PON 0xf1df0004 ++ ++/*TSTTNR1*/ ++#define R0900_TSTTNR1 0xf1e0 ++#define F0900_ADC1_PON 0xf1e00002 ++#define F0900_ADC1_INMODE 0xf1e00001 ++ ++/*TSTTNR2*/ ++#define R0900_TSTTNR2 0xf1e1 ++#define F0900_DISEQC1_PON 0xf1e10020 ++ ++/*TSTTNR3*/ ++#define R0900_TSTTNR3 0xf1e2 ++#define F0900_ADC2_PON 0xf1e20002 ++#define F0900_ADC2_INMODE 0xf1e20001 ++ ++/*TSTTNR4*/ ++#define R0900_TSTTNR4 0xf1e3 ++#define F0900_DISEQC2_PON 0xf1e30020 ++ ++/*P2_IQCONST*/ ++#define R0900_P2_IQCONST 0xf200 ++#define F0900_P2_CONSTEL_SELECT 0xf2000060 ++#define F0900_P2_IQSYMB_SEL 0xf200001f ++ ++/*P2_NOSCFG*/ ++#define R0900_P2_NOSCFG 0xf201 ++#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020 ++#define F0900_P2_NOSPLH_BETA 0xf2010018 ++#define F0900_P2_NOSDATA_BETA 0xf2010007 ++ ++/*P2_ISYMB*/ ++#define R0900_P2_ISYMB 0xf202 ++#define F0900_P2_I_SYMBOL 0xf20201ff ++ ++/*P2_QSYMB*/ ++#define R0900_P2_QSYMB 0xf203 ++#define F0900_P2_Q_SYMBOL 0xf20301ff ++ ++/*P2_AGC1CFG*/ ++#define R0900_P2_AGC1CFG 0xf204 ++#define F0900_P2_DC_FROZEN 0xf2040080 ++#define F0900_P2_DC_CORRECT 0xf2040040 ++#define F0900_P2_AMM_FROZEN 0xf2040020 ++#define F0900_P2_AMM_CORRECT 0xf2040010 ++#define F0900_P2_QUAD_FROZEN 0xf2040008 ++#define F0900_P2_QUAD_CORRECT 0xf2040004 ++ ++/*P2_AGC1CN*/ ++#define R0900_P2_AGC1CN 0xf206 ++#define F0900_P2_AGC1_LOCKED 0xf2060080 ++#define F0900_P2_AGC1_MINPOWER 0xf2060010 ++#define F0900_P2_AGCOUT_FAST 0xf2060008 ++#define F0900_P2_AGCIQ_BETA 0xf2060007 ++ ++/*P2_AGC1REF*/ ++#define R0900_P2_AGC1REF 0xf207 ++#define F0900_P2_AGCIQ_REF 0xf20700ff ++ ++/*P2_IDCCOMP*/ ++#define R0900_P2_IDCCOMP 0xf208 ++#define F0900_P2_IAVERAGE_ADJ 0xf20801ff ++ ++/*P2_QDCCOMP*/ ++#define R0900_P2_QDCCOMP 0xf209 ++#define F0900_P2_QAVERAGE_ADJ 0xf20901ff ++ ++/*P2_POWERI*/ ++#define R0900_P2_POWERI 0xf20a ++#define F0900_P2_POWER_I 0xf20a00ff ++ ++/*P2_POWERQ*/ ++#define R0900_P2_POWERQ 0xf20b ++#define F0900_P2_POWER_Q 0xf20b00ff ++ ++/*P2_AGC1AMM*/ ++#define R0900_P2_AGC1AMM 0xf20c ++#define F0900_P2_AMM_VALUE 0xf20c00ff ++ ++/*P2_AGC1QUAD*/ ++#define R0900_P2_AGC1QUAD 0xf20d ++#define F0900_P2_QUAD_VALUE 0xf20d01ff ++ ++/*P2_AGCIQIN1*/ ++#define R0900_P2_AGCIQIN1 0xf20e ++#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff ++ ++/*P2_AGCIQIN0*/ ++#define R0900_P2_AGCIQIN0 0xf20f ++#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff ++ ++/*P2_DEMOD*/ ++#define R0900_P2_DEMOD 0xf210 ++#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080 ++#define F0900_P2_SPECINV_CONTROL 0xf2100030 ++#define F0900_P2_FORCE_ENASAMP 0xf2100008 ++#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004 ++#define F0900_P2_ROLLOFF_CONTROL 0xf2100003 ++ ++/*P2_DMDMODCOD*/ ++#define R0900_P2_DMDMODCOD 0xf211 ++#define F0900_P2_MANUAL_MODCOD 0xf2110080 ++#define F0900_P2_DEMOD_MODCOD 0xf211007c ++#define F0900_P2_DEMOD_TYPE 0xf2110003 ++ ++/*P2_DSTATUS*/ ++#define R0900_P2_DSTATUS 0xf212 ++#define F0900_P2_CAR_LOCK 0xf2120080 ++#define F0900_P2_TMGLOCK_QUALITY 0xf2120060 ++#define F0900_P2_LOCK_DEFINITIF 0xf2120008 ++#define F0900_P2_OVADC_DETECT 0xf2120001 ++ ++/*P2_DSTATUS2*/ ++#define R0900_P2_DSTATUS2 0xf213 ++#define F0900_P2_DEMOD_DELOCK 0xf2130080 ++#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008 ++#define F0900_P2_AGC2_OVERFLOW 0xf2130004 ++#define F0900_P2_CFR_OVERFLOW 0xf2130002 ++#define F0900_P2_GAMMA_OVERUNDER 0xf2130001 ++ ++/*P2_DMDCFGMD*/ ++#define R0900_P2_DMDCFGMD 0xf214 ++#define F0900_P2_DVBS2_ENABLE 0xf2140080 ++#define F0900_P2_DVBS1_ENABLE 0xf2140040 ++#define F0900_P2_SCAN_ENABLE 0xf2140010 ++#define F0900_P2_CFR_AUTOSCAN 0xf2140008 ++#define F0900_P2_TUN_RNG 0xf2140003 ++ ++/*P2_DMDCFG2*/ ++#define R0900_P2_DMDCFG2 0xf215 ++#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040 ++#define F0900_P2_INFINITE_RELOCK 0xf2150010 ++ ++/*P2_DMDISTATE*/ ++#define R0900_P2_DMDISTATE 0xf216 ++#define F0900_P2_I2C_DEMOD_MODE 0xf216001f ++ ++/*P2_DMDT0M*/ ++#define R0900_P2_DMDT0M 0xf217 ++#define F0900_P2_DMDT0_MIN 0xf21700ff ++ ++/*P2_DMDSTATE*/ ++#define R0900_P2_DMDSTATE 0xf21b ++#define F0900_P2_HEADER_MODE 0xf21b0060 ++ ++/*P2_DMDFLYW*/ ++#define R0900_P2_DMDFLYW 0xf21c ++#define F0900_P2_I2C_IRQVAL 0xf21c00f0 ++#define F0900_P2_FLYWHEEL_CPT 0xf21c000f ++ ++/*P2_DSTATUS3*/ ++#define R0900_P2_DSTATUS3 0xf21d ++#define F0900_P2_DEMOD_CFGMODE 0xf21d0060 ++ ++/*P2_DMDCFG3*/ ++#define R0900_P2_DMDCFG3 0xf21e ++#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008 ++ ++/*P2_DMDCFG4*/ ++#define R0900_P2_DMDCFG4 0xf21f ++#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008 ++ ++/*P2_CORRELMANT*/ ++#define R0900_P2_CORRELMANT 0xf220 ++#define F0900_P2_CORREL_MANT 0xf22000ff ++ ++/*P2_CORRELABS*/ ++#define R0900_P2_CORRELABS 0xf221 ++#define F0900_P2_CORREL_ABS 0xf22100ff ++ ++/*P2_CORRELEXP*/ ++#define R0900_P2_CORRELEXP 0xf222 ++#define F0900_P2_CORREL_ABSEXP 0xf22200f0 ++#define F0900_P2_CORREL_EXP 0xf222000f ++ ++/*P2_PLHMODCOD*/ ++#define R0900_P2_PLHMODCOD 0xf224 ++#define F0900_P2_SPECINV_DEMOD 0xf2240080 ++#define F0900_P2_PLH_MODCOD 0xf224007c ++#define F0900_P2_PLH_TYPE 0xf2240003 ++ ++/*P2_DMDREG*/ ++#define R0900_P2_DMDREG 0xf225 ++#define F0900_P2_DECIM_PLFRAMES 0xf2250001 ++ ++/*P2_AGC2O*/ ++#define R0900_P2_AGC2O 0xf22c ++#define F0900_P2_AGC2_COEF 0xf22c0007 ++ ++/*P2_AGC2REF*/ ++#define R0900_P2_AGC2REF 0xf22d ++#define F0900_P2_AGC2_REF 0xf22d00ff ++ ++/*P2_AGC1ADJ*/ ++#define R0900_P2_AGC1ADJ 0xf22e ++#define F0900_P2_AGC1_ADJUSTED 0xf22e007f ++ ++/*P2_AGC2I1*/ ++#define R0900_P2_AGC2I1 0xf236 ++#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff ++ ++/*P2_AGC2I0*/ ++#define R0900_P2_AGC2I0 0xf237 ++#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff ++ ++/*P2_CARCFG*/ ++#define R0900_P2_CARCFG 0xf238 ++#define F0900_P2_CFRUPLOW_AUTO 0xf2380080 ++#define F0900_P2_CFRUPLOW_TEST 0xf2380040 ++#define F0900_P2_ROTAON 0xf2380004 ++#define F0900_P2_PH_DET_ALGO 0xf2380003 ++ ++/*P2_ACLC*/ ++#define R0900_P2_ACLC 0xf239 ++#define F0900_P2_CAR_ALPHA_MANT 0xf2390030 ++#define F0900_P2_CAR_ALPHA_EXP 0xf239000f ++ ++/*P2_BCLC*/ ++#define R0900_P2_BCLC 0xf23a ++#define F0900_P2_CAR_BETA_MANT 0xf23a0030 ++#define F0900_P2_CAR_BETA_EXP 0xf23a000f ++ ++/*P2_CARFREQ*/ ++#define R0900_P2_CARFREQ 0xf23d ++#define F0900_P2_KC_COARSE_EXP 0xf23d00f0 ++#define F0900_P2_BETA_FREQ 0xf23d000f ++ ++/*P2_CARHDR*/ ++#define R0900_P2_CARHDR 0xf23e ++#define F0900_P2_K_FREQ_HDR 0xf23e00ff ++ ++/*P2_LDT*/ ++#define R0900_P2_LDT 0xf23f ++#define F0900_P2_CARLOCK_THRES 0xf23f01ff ++ ++/*P2_LDT2*/ ++#define R0900_P2_LDT2 0xf240 ++#define F0900_P2_CARLOCK_THRES2 0xf24001ff ++ ++/*P2_CFRICFG*/ ++#define R0900_P2_CFRICFG 0xf241 ++#define F0900_P2_NEG_CFRSTEP 0xf2410001 ++ ++/*P2_CFRUP1*/ ++#define R0900_P2_CFRUP1 0xf242 ++#define F0900_P2_CFR_UP1 0xf24201ff ++ ++/*P2_CFRUP0*/ ++#define R0900_P2_CFRUP0 0xf243 ++#define F0900_P2_CFR_UP0 0xf24300ff ++ ++/*P2_CFRLOW1*/ ++#define R0900_P2_CFRLOW1 0xf246 ++#define F0900_P2_CFR_LOW1 0xf24601ff ++ ++/*P2_CFRLOW0*/ ++#define R0900_P2_CFRLOW0 0xf247 ++#define F0900_P2_CFR_LOW0 0xf24700ff ++ ++/*P2_CFRINIT1*/ ++#define R0900_P2_CFRINIT1 0xf248 ++#define F0900_P2_CFR_INIT1 0xf24801ff ++ ++/*P2_CFRINIT0*/ ++#define R0900_P2_CFRINIT0 0xf249 ++#define F0900_P2_CFR_INIT0 0xf24900ff ++ ++/*P2_CFRINC1*/ ++#define R0900_P2_CFRINC1 0xf24a ++#define F0900_P2_MANUAL_CFRINC 0xf24a0080 ++#define F0900_P2_CFR_INC1 0xf24a003f ++ ++/*P2_CFRINC0*/ ++#define R0900_P2_CFRINC0 0xf24b ++#define F0900_P2_CFR_INC0 0xf24b00f8 ++ ++/*P2_CFR2*/ ++#define R0900_P2_CFR2 0xf24c ++#define F0900_P2_CAR_FREQ2 0xf24c01ff ++ ++/*P2_CFR1*/ ++#define R0900_P2_CFR1 0xf24d ++#define F0900_P2_CAR_FREQ1 0xf24d00ff ++ ++/*P2_CFR0*/ ++#define R0900_P2_CFR0 0xf24e ++#define F0900_P2_CAR_FREQ0 0xf24e00ff ++ ++/*P2_LDI*/ ++#define R0900_P2_LDI 0xf24f ++#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff ++ ++/*P2_TMGCFG*/ ++#define R0900_P2_TMGCFG 0xf250 ++#define F0900_P2_TMGLOCK_BETA 0xf25000c0 ++#define F0900_P2_DO_TIMING_CORR 0xf2500010 ++#define F0900_P2_TMG_MINFREQ 0xf2500003 ++ ++/*P2_RTC*/ ++#define R0900_P2_RTC 0xf251 ++#define F0900_P2_TMGALPHA_EXP 0xf25100f0 ++#define F0900_P2_TMGBETA_EXP 0xf251000f ++ ++/*P2_RTCS2*/ ++#define R0900_P2_RTCS2 0xf252 ++#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0 ++#define F0900_P2_TMGBETAS2_EXP 0xf252000f ++ ++/*P2_TMGTHRISE*/ ++#define R0900_P2_TMGTHRISE 0xf253 ++#define F0900_P2_TMGLOCK_THRISE 0xf25300ff ++ ++/*P2_TMGTHFALL*/ ++#define R0900_P2_TMGTHFALL 0xf254 ++#define F0900_P2_TMGLOCK_THFALL 0xf25400ff ++ ++/*P2_SFRUPRATIO*/ ++#define R0900_P2_SFRUPRATIO 0xf255 ++#define F0900_P2_SFR_UPRATIO 0xf25500ff ++ ++/*P2_SFRLOWRATIO*/ ++#define R0900_P2_SFRLOWRATIO 0xf256 ++#define F0900_P2_SFR_LOWRATIO 0xf25600ff ++ ++/*P2_KREFTMG*/ ++#define R0900_P2_KREFTMG 0xf258 ++#define F0900_P2_KREF_TMG 0xf25800ff ++ ++/*P2_SFRSTEP*/ ++#define R0900_P2_SFRSTEP 0xf259 ++#define F0900_P2_SFR_SCANSTEP 0xf25900f0 ++#define F0900_P2_SFR_CENTERSTEP 0xf259000f ++ ++/*P2_TMGCFG2*/ ++#define R0900_P2_TMGCFG2 0xf25a ++#define F0900_P2_SFRRATIO_FINE 0xf25a0001 ++ ++/*P2_KREFTMG2*/ ++#define R0900_P2_KREFTMG2 0xf25b ++#define F0900_P2_KREF_TMG2 0xf25b00ff ++ ++/*P2_SFRINIT1*/ ++#define R0900_P2_SFRINIT1 0xf25e ++#define F0900_P2_SFR_INIT1 0xf25e007f ++ ++/*P2_SFRINIT0*/ ++#define R0900_P2_SFRINIT0 0xf25f ++#define F0900_P2_SFR_INIT0 0xf25f00ff ++ ++/*P2_SFRUP1*/ ++#define R0900_P2_SFRUP1 0xf260 ++#define F0900_P2_AUTO_GUP 0xf2600080 ++#define F0900_P2_SYMB_FREQ_UP1 0xf260007f ++ ++/*P2_SFRUP0*/ ++#define R0900_P2_SFRUP0 0xf261 ++#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff ++ ++/*P2_SFRLOW1*/ ++#define R0900_P2_SFRLOW1 0xf262 ++#define F0900_P2_AUTO_GLOW 0xf2620080 ++#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f ++ ++/*P2_SFRLOW0*/ ++#define R0900_P2_SFRLOW0 0xf263 ++#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff ++ ++/*P2_SFR3*/ ++#define R0900_P2_SFR3 0xf264 ++#define F0900_P2_SYMB_FREQ3 0xf26400ff ++ ++/*P2_SFR2*/ ++#define R0900_P2_SFR2 0xf265 ++#define F0900_P2_SYMB_FREQ2 0xf26500ff ++ ++/*P2_SFR1*/ ++#define R0900_P2_SFR1 0xf266 ++#define F0900_P2_SYMB_FREQ1 0xf26600ff ++ ++/*P2_SFR0*/ ++#define R0900_P2_SFR0 0xf267 ++#define F0900_P2_SYMB_FREQ0 0xf26700ff ++ ++/*P2_TMGREG2*/ ++#define R0900_P2_TMGREG2 0xf268 ++#define F0900_P2_TMGREG2 0xf26800ff ++ ++/*P2_TMGREG1*/ ++#define R0900_P2_TMGREG1 0xf269 ++#define F0900_P2_TMGREG1 0xf26900ff ++ ++/*P2_TMGREG0*/ ++#define R0900_P2_TMGREG0 0xf26a ++#define F0900_P2_TMGREG0 0xf26a00ff ++ ++/*P2_TMGLOCK1*/ ++#define R0900_P2_TMGLOCK1 0xf26b ++#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff ++ ++/*P2_TMGLOCK0*/ ++#define R0900_P2_TMGLOCK0 0xf26c ++#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff ++ ++/*P2_TMGOBS*/ ++#define R0900_P2_TMGOBS 0xf26d ++#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0 ++ ++/*P2_EQUALCFG*/ ++#define R0900_P2_EQUALCFG 0xf26f ++#define F0900_P2_EQUAL_ON 0xf26f0040 ++#define F0900_P2_MU_EQUALDFE 0xf26f0007 ++ ++/*P2_EQUAI1*/ ++#define R0900_P2_EQUAI1 0xf270 ++#define F0900_P2_EQUA_ACCI1 0xf27001ff ++ ++/*P2_EQUAQ1*/ ++#define R0900_P2_EQUAQ1 0xf271 ++#define F0900_P2_EQUA_ACCQ1 0xf27101ff ++ ++/*P2_EQUAI2*/ ++#define R0900_P2_EQUAI2 0xf272 ++#define F0900_P2_EQUA_ACCI2 0xf27201ff ++ ++/*P2_EQUAQ2*/ ++#define R0900_P2_EQUAQ2 0xf273 ++#define F0900_P2_EQUA_ACCQ2 0xf27301ff ++ ++/*P2_EQUAI3*/ ++#define R0900_P2_EQUAI3 0xf274 ++#define F0900_P2_EQUA_ACCI3 0xf27401ff ++ ++/*P2_EQUAQ3*/ ++#define R0900_P2_EQUAQ3 0xf275 ++#define F0900_P2_EQUA_ACCQ3 0xf27501ff ++ ++/*P2_EQUAI4*/ ++#define R0900_P2_EQUAI4 0xf276 ++#define F0900_P2_EQUA_ACCI4 0xf27601ff ++ ++/*P2_EQUAQ4*/ ++#define R0900_P2_EQUAQ4 0xf277 ++#define F0900_P2_EQUA_ACCQ4 0xf27701ff ++ ++/*P2_EQUAI5*/ ++#define R0900_P2_EQUAI5 0xf278 ++#define F0900_P2_EQUA_ACCI5 0xf27801ff ++ ++/*P2_EQUAQ5*/ ++#define R0900_P2_EQUAQ5 0xf279 ++#define F0900_P2_EQUA_ACCQ5 0xf27901ff ++ ++/*P2_EQUAI6*/ ++#define R0900_P2_EQUAI6 0xf27a ++#define F0900_P2_EQUA_ACCI6 0xf27a01ff ++ ++/*P2_EQUAQ6*/ ++#define R0900_P2_EQUAQ6 0xf27b ++#define F0900_P2_EQUA_ACCQ6 0xf27b01ff ++ ++/*P2_EQUAI7*/ ++#define R0900_P2_EQUAI7 0xf27c ++#define F0900_P2_EQUA_ACCI7 0xf27c01ff ++ ++/*P2_EQUAQ7*/ ++#define R0900_P2_EQUAQ7 0xf27d ++#define F0900_P2_EQUA_ACCQ7 0xf27d01ff ++ ++/*P2_EQUAI8*/ ++#define R0900_P2_EQUAI8 0xf27e ++#define F0900_P2_EQUA_ACCI8 0xf27e01ff ++ ++/*P2_EQUAQ8*/ ++#define R0900_P2_EQUAQ8 0xf27f ++#define F0900_P2_EQUA_ACCQ8 0xf27f01ff ++ ++/*P2_NNOSDATAT1*/ ++#define R0900_P2_NNOSDATAT1 0xf280 ++#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff ++ ++/*P2_NNOSDATAT0*/ ++#define R0900_P2_NNOSDATAT0 0xf281 ++#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff ++ ++/*P2_NNOSDATA1*/ ++#define R0900_P2_NNOSDATA1 0xf282 ++#define F0900_P2_NOSDATA_NORMED1 0xf28200ff ++ ++/*P2_NNOSDATA0*/ ++#define R0900_P2_NNOSDATA0 0xf283 ++#define F0900_P2_NOSDATA_NORMED0 0xf28300ff ++ ++/*P2_NNOSPLHT1*/ ++#define R0900_P2_NNOSPLHT1 0xf284 ++#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff ++ ++/*P2_NNOSPLHT0*/ ++#define R0900_P2_NNOSPLHT0 0xf285 ++#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff ++ ++/*P2_NNOSPLH1*/ ++#define R0900_P2_NNOSPLH1 0xf286 ++#define F0900_P2_NOSPLH_NORMED1 0xf28600ff ++ ++/*P2_NNOSPLH0*/ ++#define R0900_P2_NNOSPLH0 0xf287 ++#define F0900_P2_NOSPLH_NORMED0 0xf28700ff ++ ++/*P2_NOSDATAT1*/ ++#define R0900_P2_NOSDATAT1 0xf288 ++#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff ++ ++/*P2_NOSDATAT0*/ ++#define R0900_P2_NOSDATAT0 0xf289 ++#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff ++ ++/*P2_NOSDATA1*/ ++#define R0900_P2_NOSDATA1 0xf28a ++#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff ++ ++/*P2_NOSDATA0*/ ++#define R0900_P2_NOSDATA0 0xf28b ++#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff ++ ++/*P2_NOSPLHT1*/ ++#define R0900_P2_NOSPLHT1 0xf28c ++#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff ++ ++/*P2_NOSPLHT0*/ ++#define R0900_P2_NOSPLHT0 0xf28d ++#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff ++ ++/*P2_NOSPLH1*/ ++#define R0900_P2_NOSPLH1 0xf28e ++#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff ++ ++/*P2_NOSPLH0*/ ++#define R0900_P2_NOSPLH0 0xf28f ++#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff ++ ++/*P2_CAR2CFG*/ ++#define R0900_P2_CAR2CFG 0xf290 ++#define F0900_P2_CARRIER3_DISABLE 0xf2900040 ++#define F0900_P2_ROTA2ON 0xf2900004 ++#define F0900_P2_PH_DET_ALGO2 0xf2900003 ++ ++/*P2_CFR2CFR1*/ ++#define R0900_P2_CFR2CFR1 0xf291 ++#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0 ++#define F0900_P2_EN_S2CAR2CENTER 0xf2910020 ++#define F0900_P2_DIS_BCHERRCFR2 0xf2910010 ++#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007 ++ ++/*P2_CFR22*/ ++#define R0900_P2_CFR22 0xf293 ++#define F0900_P2_CAR2_FREQ2 0xf29301ff ++ ++/*P2_CFR21*/ ++#define R0900_P2_CFR21 0xf294 ++#define F0900_P2_CAR2_FREQ1 0xf29400ff ++ ++/*P2_CFR20*/ ++#define R0900_P2_CFR20 0xf295 ++#define F0900_P2_CAR2_FREQ0 0xf29500ff ++ ++/*P2_ACLC2S2Q*/ ++#define R0900_P2_ACLC2S2Q 0xf297 ++#define F0900_P2_ENAB_SPSKSYMB 0xf2970080 ++#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030 ++#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f ++ ++/*P2_ACLC2S28*/ ++#define R0900_P2_ACLC2S28 0xf298 ++#define F0900_P2_OLDI3Q_MODE 0xf2980080 ++#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030 ++#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f ++ ++/*P2_ACLC2S216A*/ ++#define R0900_P2_ACLC2S216A 0xf299 ++#define F0900_P2_DIS_C3STOPA2 0xf2990080 ++#define F0900_P2_CAR2S2_16ADERAT 0xf2990040 ++#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030 ++#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f ++ ++/*P2_ACLC2S232A*/ ++#define R0900_P2_ACLC2S232A 0xf29a ++#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040 ++#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030 ++#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f ++ ++/*P2_BCLC2S2Q*/ ++#define R0900_P2_BCLC2S2Q 0xf29c ++#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030 ++#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f ++ ++/*P2_BCLC2S28*/ ++#define R0900_P2_BCLC2S28 0xf29d ++#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030 ++#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f ++ ++/*P2_BCLC2S216A*/ ++#define R0900_P2_BCLC2S216A 0xf29e ++ ++/*P2_BCLC2S232A*/ ++#define R0900_P2_BCLC2S232A 0xf29f ++ ++/*P2_PLROOT2*/ ++#define R0900_P2_PLROOT2 0xf2ac ++#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c ++#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003 ++ ++/*P2_PLROOT1*/ ++#define R0900_P2_PLROOT1 0xf2ad ++#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff ++ ++/*P2_PLROOT0*/ ++#define R0900_P2_PLROOT0 0xf2ae ++#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff ++ ++/*P2_MODCODLST0*/ ++#define R0900_P2_MODCODLST0 0xf2b0 ++ ++/*P2_MODCODLST1*/ ++#define R0900_P2_MODCODLST1 0xf2b1 ++#define F0900_P2_DIS_MODCOD29 0xf2b100f0 ++#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f ++ ++/*P2_MODCODLST2*/ ++#define R0900_P2_MODCODLST2 0xf2b2 ++#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0 ++#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f ++ ++/*P2_MODCODLST3*/ ++#define R0900_P2_MODCODLST3 0xf2b3 ++#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0 ++#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f ++ ++/*P2_MODCODLST4*/ ++#define R0900_P2_MODCODLST4 0xf2b4 ++#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0 ++#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f ++ ++/*P2_MODCODLST5*/ ++#define R0900_P2_MODCODLST5 0xf2b5 ++#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0 ++#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f ++ ++/*P2_MODCODLST6*/ ++#define R0900_P2_MODCODLST6 0xf2b6 ++#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0 ++#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f ++ ++/*P2_MODCODLST7*/ ++#define R0900_P2_MODCODLST7 0xf2b7 ++#define F0900_P2_DIS_8P_9_10 0xf2b700f0 ++#define F0900_P2_DIS_8P_8_9 0xf2b7000f ++ ++/*P2_MODCODLST8*/ ++#define R0900_P2_MODCODLST8 0xf2b8 ++#define F0900_P2_DIS_8P_5_6 0xf2b800f0 ++#define F0900_P2_DIS_8P_3_4 0xf2b8000f ++ ++/*P2_MODCODLST9*/ ++#define R0900_P2_MODCODLST9 0xf2b9 ++#define F0900_P2_DIS_8P_2_3 0xf2b900f0 ++#define F0900_P2_DIS_8P_3_5 0xf2b9000f ++ ++/*P2_MODCODLSTA*/ ++#define R0900_P2_MODCODLSTA 0xf2ba ++#define F0900_P2_DIS_QP_9_10 0xf2ba00f0 ++#define F0900_P2_DIS_QP_8_9 0xf2ba000f ++ ++/*P2_MODCODLSTB*/ ++#define R0900_P2_MODCODLSTB 0xf2bb ++#define F0900_P2_DIS_QP_5_6 0xf2bb00f0 ++#define F0900_P2_DIS_QP_4_5 0xf2bb000f ++ ++/*P2_MODCODLSTC*/ ++#define R0900_P2_MODCODLSTC 0xf2bc ++#define F0900_P2_DIS_QP_3_4 0xf2bc00f0 ++#define F0900_P2_DIS_QP_2_3 0xf2bc000f ++ ++/*P2_MODCODLSTD*/ ++#define R0900_P2_MODCODLSTD 0xf2bd ++#define F0900_P2_DIS_QP_3_5 0xf2bd00f0 ++#define F0900_P2_DIS_QP_1_2 0xf2bd000f ++ ++/*P2_MODCODLSTE*/ ++#define R0900_P2_MODCODLSTE 0xf2be ++#define F0900_P2_DIS_QP_2_5 0xf2be00f0 ++#define F0900_P2_DIS_QP_1_3 0xf2be000f ++ ++/*P2_MODCODLSTF*/ ++#define R0900_P2_MODCODLSTF 0xf2bf ++#define F0900_P2_DIS_QP_1_4 0xf2bf00f0 ++ ++/*P2_GAUSSR0*/ ++#define R0900_P2_GAUSSR0 0xf2c0 ++#define F0900_P2_EN_CCIMODE 0xf2c00080 ++#define F0900_P2_R0_GAUSSIEN 0xf2c0007f ++ ++/*P2_CCIR0*/ ++#define R0900_P2_CCIR0 0xf2c1 ++#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080 ++#define F0900_P2_R0_CCI 0xf2c1007f ++ ++/*P2_CCIQUANT*/ ++#define R0900_P2_CCIQUANT 0xf2c2 ++#define F0900_P2_CCI_BETA 0xf2c200e0 ++#define F0900_P2_CCI_QUANT 0xf2c2001f ++ ++/*P2_CCITHRES*/ ++#define R0900_P2_CCITHRES 0xf2c3 ++#define F0900_P2_CCI_THRESHOLD 0xf2c300ff ++ ++/*P2_CCIACC*/ ++#define R0900_P2_CCIACC 0xf2c4 ++#define F0900_P2_CCI_VALUE 0xf2c400ff ++ ++/*P2_DMDRESCFG*/ ++#define R0900_P2_DMDRESCFG 0xf2c6 ++#define F0900_P2_DMDRES_RESET 0xf2c60080 ++#define F0900_P2_DMDRES_STRALL 0xf2c60008 ++#define F0900_P2_DMDRES_NEWONLY 0xf2c60004 ++#define F0900_P2_DMDRES_NOSTORE 0xf2c60002 ++ ++/*P2_DMDRESADR*/ ++#define R0900_P2_DMDRESADR 0xf2c7 ++#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040 ++#define F0900_P2_DMDRES_MEMFULL 0xf2c70030 ++#define F0900_P2_DMDRES_RESNBR 0xf2c7000f ++ ++/*P2_DMDRESDATA7*/ ++#define R0900_P2_DMDRESDATA7 0xf2c8 ++#define F0900_P2_DMDRES_DATA7 0xf2c800ff ++ ++/*P2_DMDRESDATA6*/ ++#define R0900_P2_DMDRESDATA6 0xf2c9 ++#define F0900_P2_DMDRES_DATA6 0xf2c900ff ++ ++/*P2_DMDRESDATA5*/ ++#define R0900_P2_DMDRESDATA5 0xf2ca ++#define F0900_P2_DMDRES_DATA5 0xf2ca00ff ++ ++/*P2_DMDRESDATA4*/ ++#define R0900_P2_DMDRESDATA4 0xf2cb ++#define F0900_P2_DMDRES_DATA4 0xf2cb00ff ++ ++/*P2_DMDRESDATA3*/ ++#define R0900_P2_DMDRESDATA3 0xf2cc ++#define F0900_P2_DMDRES_DATA3 0xf2cc00ff ++ ++/*P2_DMDRESDATA2*/ ++#define R0900_P2_DMDRESDATA2 0xf2cd ++#define F0900_P2_DMDRES_DATA2 0xf2cd00ff ++ ++/*P2_DMDRESDATA1*/ ++#define R0900_P2_DMDRESDATA1 0xf2ce ++#define F0900_P2_DMDRES_DATA1 0xf2ce00ff ++ ++/*P2_DMDRESDATA0*/ ++#define R0900_P2_DMDRESDATA0 0xf2cf ++#define F0900_P2_DMDRES_DATA0 0xf2cf00ff ++ ++/*P2_FFEI1*/ ++#define R0900_P2_FFEI1 0xf2d0 ++#define F0900_P2_FFE_ACCI1 0xf2d001ff ++ ++/*P2_FFEQ1*/ ++#define R0900_P2_FFEQ1 0xf2d1 ++#define F0900_P2_FFE_ACCQ1 0xf2d101ff ++ ++/*P2_FFEI2*/ ++#define R0900_P2_FFEI2 0xf2d2 ++#define F0900_P2_FFE_ACCI2 0xf2d201ff ++ ++/*P2_FFEQ2*/ ++#define R0900_P2_FFEQ2 0xf2d3 ++#define F0900_P2_FFE_ACCQ2 0xf2d301ff ++ ++/*P2_FFEI3*/ ++#define R0900_P2_FFEI3 0xf2d4 ++#define F0900_P2_FFE_ACCI3 0xf2d401ff ++ ++/*P2_FFEQ3*/ ++#define R0900_P2_FFEQ3 0xf2d5 ++#define F0900_P2_FFE_ACCQ3 0xf2d501ff ++ ++/*P2_FFEI4*/ ++#define R0900_P2_FFEI4 0xf2d6 ++#define F0900_P2_FFE_ACCI4 0xf2d601ff ++ ++/*P2_FFEQ4*/ ++#define R0900_P2_FFEQ4 0xf2d7 ++#define F0900_P2_FFE_ACCQ4 0xf2d701ff ++ ++/*P2_FFECFG*/ ++#define R0900_P2_FFECFG 0xf2d8 ++#define F0900_P2_EQUALFFE_ON 0xf2d80040 ++#define F0900_P2_MU_EQUALFFE 0xf2d80007 ++ ++/*P2_TNRCFG*/ ++#define R0900_P2_TNRCFG 0xf2e0 ++#define F0900_P2_TUN_ACKFAIL 0xf2e00080 ++#define F0900_P2_TUN_TYPE 0xf2e00070 ++#define F0900_P2_TUN_SECSTOP 0xf2e00008 ++#define F0900_P2_TUN_VCOSRCH 0xf2e00004 ++#define F0900_P2_TUN_MADDRESS 0xf2e00003 ++ ++/*P2_TNRCFG2*/ ++#define R0900_P2_TNRCFG2 0xf2e1 ++#define F0900_P2_TUN_IQSWAP 0xf2e10080 ++#define F0900_P2_DIS_BWCALC 0xf2e10004 ++#define F0900_P2_SHORT_WAITSTATES 0xf2e10002 ++ ++/*P2_TNRXTAL*/ ++#define R0900_P2_TNRXTAL 0xf2e4 ++#define F0900_P2_TUN_XTALFREQ 0xf2e4001f ++ ++/*P2_TNRSTEPS*/ ++#define R0900_P2_TNRSTEPS 0xf2e7 ++#define F0900_P2_TUNER_BW0P125 0xf2e70080 ++#define F0900_P2_BWINC_OFFSET 0xf2e70170 ++#define F0900_P2_SOFTSTEP_RNG 0xf2e70008 ++#define F0900_P2_TUN_BWOFFSET 0xf2e70007 ++ ++/*P2_TNRGAIN*/ ++#define R0900_P2_TNRGAIN 0xf2e8 ++#define F0900_P2_TUN_KDIVEN 0xf2e800c0 ++#define F0900_P2_STB6X00_OCK 0xf2e80030 ++#define F0900_P2_TUN_GAIN 0xf2e8000f ++ ++/*P2_TNRRF1*/ ++#define R0900_P2_TNRRF1 0xf2e9 ++#define F0900_P2_TUN_RFFREQ2 0xf2e900ff ++ ++/*P2_TNRRF0*/ ++#define R0900_P2_TNRRF0 0xf2ea ++#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff ++ ++/*P2_TNRBW*/ ++#define R0900_P2_TNRBW 0xf2eb ++#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0 ++#define F0900_P2_TUN_BW 0xf2eb003f ++ ++/*P2_TNRADJ*/ ++#define R0900_P2_TNRADJ 0xf2ec ++#define F0900_P2_STB61X0_CALTIME 0xf2ec0040 ++ ++/*P2_TNRCTL2*/ ++#define R0900_P2_TNRCTL2 0xf2ed ++#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080 ++#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040 ++#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020 ++#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010 ++#define F0900_P2_STB61X0_CALOFF 0xf2ed0008 ++#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004 ++#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002 ++#define F0900_P2_STB6XX0_SYN 0xf2ed0001 ++ ++/*P2_TNRCFG3*/ ++#define R0900_P2_TNRCFG3 0xf2ee ++#define F0900_P2_TUN_PLLFREQ 0xf2ee001c ++#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003 ++ ++/*P2_TNRLAUNCH*/ ++#define R0900_P2_TNRLAUNCH 0xf2f0 ++ ++/*P2_TNRLD*/ ++#define R0900_P2_TNRLD 0xf2f0 ++#define F0900_P2_TUNLD_VCOING 0xf2f00080 ++#define F0900_P2_TUN_REG1FAIL 0xf2f00040 ++#define F0900_P2_TUN_REG2FAIL 0xf2f00020 ++#define F0900_P2_TUN_REG3FAIL 0xf2f00010 ++#define F0900_P2_TUN_REG4FAIL 0xf2f00008 ++#define F0900_P2_TUN_REG5FAIL 0xf2f00004 ++#define F0900_P2_TUN_BWING 0xf2f00002 ++#define F0900_P2_TUN_LOCKED 0xf2f00001 ++ ++/*P2_TNROBSL*/ ++#define R0900_P2_TNROBSL 0xf2f6 ++#define F0900_P2_TUN_I2CABORTED 0xf2f60080 ++#define F0900_P2_TUN_LPEN 0xf2f60040 ++#define F0900_P2_TUN_FCCK 0xf2f60020 ++#define F0900_P2_TUN_I2CLOCKED 0xf2f60010 ++#define F0900_P2_TUN_PROGDONE 0xf2f6000c ++#define F0900_P2_TUN_RFRESTE1 0xf2f60003 ++ ++/*P2_TNRRESTE*/ ++#define R0900_P2_TNRRESTE 0xf2f7 ++#define F0900_P2_TUN_RFRESTE0 0xf2f700ff ++ ++/*P2_SMAPCOEF7*/ ++#define R0900_P2_SMAPCOEF7 0xf300 ++#define F0900_P2_DIS_QSCALE 0xf3000080 ++#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f ++ ++/*P2_SMAPCOEF6*/ ++#define R0900_P2_SMAPCOEF6 0xf301 ++#define F0900_P2_ADJ_8PSKLLR1 0xf3010004 ++#define F0900_P2_OLD_8PSKLLR1 0xf3010002 ++#define F0900_P2_DIS_AB8PSK 0xf3010001 ++ ++/*P2_SMAPCOEF5*/ ++#define R0900_P2_SMAPCOEF5 0xf302 ++#define F0900_P2_DIS_8SCALE 0xf3020080 ++#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f ++ ++/*P2_NCO2MAX1*/ ++#define R0900_P2_NCO2MAX1 0xf314 ++#define F0900_P2_TETA2_MAXVABS1 0xf31400ff ++ ++/*P2_NCO2MAX0*/ ++#define R0900_P2_NCO2MAX0 0xf315 ++#define F0900_P2_TETA2_MAXVABS0 0xf31500ff ++ ++/*P2_NCO2FR1*/ ++#define R0900_P2_NCO2FR1 0xf316 ++#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff ++ ++/*P2_NCO2FR0*/ ++#define R0900_P2_NCO2FR0 0xf317 ++#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff ++ ++/*P2_CFR2AVRGE1*/ ++#define R0900_P2_CFR2AVRGE1 0xf318 ++#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff ++ ++/*P2_CFR2AVRGE0*/ ++#define R0900_P2_CFR2AVRGE0 0xf319 ++#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff ++ ++/*P2_DMDPLHSTAT*/ ++#define R0900_P2_DMDPLHSTAT 0xf320 ++#define F0900_P2_PLH_STATISTIC 0xf32000ff ++ ++/*P2_LOCKTIME3*/ ++#define R0900_P2_LOCKTIME3 0xf322 ++#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff ++ ++/*P2_LOCKTIME2*/ ++#define R0900_P2_LOCKTIME2 0xf323 ++#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff ++ ++/*P2_LOCKTIME1*/ ++#define R0900_P2_LOCKTIME1 0xf324 ++#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff ++ ++/*P2_LOCKTIME0*/ ++#define R0900_P2_LOCKTIME0 0xf325 ++#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff ++ ++/*P2_VITSCALE*/ ++#define R0900_P2_VITSCALE 0xf332 ++#define F0900_P2_NVTH_NOSRANGE 0xf3320080 ++#define F0900_P2_VERROR_MAXMODE 0xf3320040 ++#define F0900_P2_NSLOWSN_LOCKED 0xf3320008 ++#define F0900_P2_DIS_RSFLOCK 0xf3320002 ++ ++/*P2_FECM*/ ++#define R0900_P2_FECM 0xf333 ++#define F0900_P2_DSS_DVB 0xf3330080 ++#define F0900_P2_DSS_SRCH 0xf3330010 ++#define F0900_P2_SYNCVIT 0xf3330002 ++#define F0900_P2_IQINV 0xf3330001 ++ ++/*P2_VTH12*/ ++#define R0900_P2_VTH12 0xf334 ++#define F0900_P2_VTH12 0xf33400ff ++ ++/*P2_VTH23*/ ++#define R0900_P2_VTH23 0xf335 ++#define F0900_P2_VTH23 0xf33500ff ++ ++/*P2_VTH34*/ ++#define R0900_P2_VTH34 0xf336 ++#define F0900_P2_VTH34 0xf33600ff ++ ++/*P2_VTH56*/ ++#define R0900_P2_VTH56 0xf337 ++#define F0900_P2_VTH56 0xf33700ff ++ ++/*P2_VTH67*/ ++#define R0900_P2_VTH67 0xf338 ++#define F0900_P2_VTH67 0xf33800ff ++ ++/*P2_VTH78*/ ++#define R0900_P2_VTH78 0xf339 ++#define F0900_P2_VTH78 0xf33900ff ++ ++/*P2_VITCURPUN*/ ++#define R0900_P2_VITCURPUN 0xf33a ++#define F0900_P2_VIT_CURPUN 0xf33a001f ++ ++/*P2_VERROR*/ ++#define R0900_P2_VERROR 0xf33b ++#define F0900_P2_REGERR_VIT 0xf33b00ff ++ ++/*P2_PRVIT*/ ++#define R0900_P2_PRVIT 0xf33c ++#define F0900_P2_DIS_VTHLOCK 0xf33c0040 ++#define F0900_P2_E7_8VIT 0xf33c0020 ++#define F0900_P2_E6_7VIT 0xf33c0010 ++#define F0900_P2_E5_6VIT 0xf33c0008 ++#define F0900_P2_E3_4VIT 0xf33c0004 ++#define F0900_P2_E2_3VIT 0xf33c0002 ++#define F0900_P2_E1_2VIT 0xf33c0001 ++ ++/*P2_VAVSRVIT*/ ++#define R0900_P2_VAVSRVIT 0xf33d ++#define F0900_P2_AMVIT 0xf33d0080 ++#define F0900_P2_FROZENVIT 0xf33d0040 ++#define F0900_P2_SNVIT 0xf33d0030 ++#define F0900_P2_TOVVIT 0xf33d000c ++#define F0900_P2_HYPVIT 0xf33d0003 ++ ++/*P2_VSTATUSVIT*/ ++#define R0900_P2_VSTATUSVIT 0xf33e ++#define F0900_P2_PRFVIT 0xf33e0010 ++#define F0900_P2_LOCKEDVIT 0xf33e0008 ++ ++/*P2_VTHINUSE*/ ++#define R0900_P2_VTHINUSE 0xf33f ++#define F0900_P2_VIT_INUSE 0xf33f00ff ++ ++/*P2_KDIV12*/ ++#define R0900_P2_KDIV12 0xf340 ++#define F0900_P2_K_DIVIDER_12 0xf340007f ++ ++/*P2_KDIV23*/ ++#define R0900_P2_KDIV23 0xf341 ++#define F0900_P2_K_DIVIDER_23 0xf341007f ++ ++/*P2_KDIV34*/ ++#define R0900_P2_KDIV34 0xf342 ++#define F0900_P2_K_DIVIDER_34 0xf342007f ++ ++/*P2_KDIV56*/ ++#define R0900_P2_KDIV56 0xf343 ++#define F0900_P2_K_DIVIDER_56 0xf343007f ++ ++/*P2_KDIV67*/ ++#define R0900_P2_KDIV67 0xf344 ++#define F0900_P2_K_DIVIDER_67 0xf344007f ++ ++/*P2_KDIV78*/ ++#define R0900_P2_KDIV78 0xf345 ++#define F0900_P2_K_DIVIDER_78 0xf345007f ++ ++/*P2_PDELCTRL1*/ ++#define R0900_P2_PDELCTRL1 0xf350 ++#define F0900_P2_INV_MISMASK 0xf3500080 ++#define F0900_P2_FILTER_EN 0xf3500020 ++#define F0900_P2_EN_MIS00 0xf3500002 ++#define F0900_P2_ALGOSWRST 0xf3500001 ++ ++/*P2_PDELCTRL2*/ ++#define R0900_P2_PDELCTRL2 0xf351 ++#define F0900_P2_RESET_UPKO_COUNT 0xf3510040 ++#define F0900_P2_FRAME_MODE 0xf3510002 ++#define F0900_P2_NOBCHERRFLG_USE 0xf3510001 ++ ++/*P2_HYSTTHRESH*/ ++#define R0900_P2_HYSTTHRESH 0xf354 ++#define F0900_P2_UNLCK_THRESH 0xf35400f0 ++#define F0900_P2_DELIN_LCK_THRESH 0xf354000f ++ ++/*P2_ISIENTRY*/ ++#define R0900_P2_ISIENTRY 0xf35e ++#define F0900_P2_ISI_ENTRY 0xf35e00ff ++ ++/*P2_ISIBITENA*/ ++#define R0900_P2_ISIBITENA 0xf35f ++#define F0900_P2_ISI_BIT_EN 0xf35f00ff ++ ++/*P2_MATSTR1*/ ++#define R0900_P2_MATSTR1 0xf360 ++#define F0900_P2_MATYPE_CURRENT1 0xf36000ff ++ ++/*P2_MATSTR0*/ ++#define R0900_P2_MATSTR0 0xf361 ++#define F0900_P2_MATYPE_CURRENT0 0xf36100ff ++ ++/*P2_UPLSTR1*/ ++#define R0900_P2_UPLSTR1 0xf362 ++#define F0900_P2_UPL_CURRENT1 0xf36200ff ++ ++/*P2_UPLSTR0*/ ++#define R0900_P2_UPLSTR0 0xf363 ++#define F0900_P2_UPL_CURRENT0 0xf36300ff ++ ++/*P2_DFLSTR1*/ ++#define R0900_P2_DFLSTR1 0xf364 ++#define F0900_P2_DFL_CURRENT1 0xf36400ff ++ ++/*P2_DFLSTR0*/ ++#define R0900_P2_DFLSTR0 0xf365 ++#define F0900_P2_DFL_CURRENT0 0xf36500ff ++ ++/*P2_SYNCSTR*/ ++#define R0900_P2_SYNCSTR 0xf366 ++#define F0900_P2_SYNC_CURRENT 0xf36600ff ++ ++/*P2_SYNCDSTR1*/ ++#define R0900_P2_SYNCDSTR1 0xf367 ++#define F0900_P2_SYNCD_CURRENT1 0xf36700ff ++ ++/*P2_SYNCDSTR0*/ ++#define R0900_P2_SYNCDSTR0 0xf368 ++#define F0900_P2_SYNCD_CURRENT0 0xf36800ff ++ ++/*P2_PDELSTATUS1*/ ++#define R0900_P2_PDELSTATUS1 0xf369 ++#define F0900_P2_PKTDELIN_DELOCK 0xf3690080 ++#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040 ++#define F0900_P2_CONTINUOUS_STREAM 0xf3690020 ++#define F0900_P2_UNACCEPTED_STREAM 0xf3690010 ++#define F0900_P2_BCH_ERROR_FLAG 0xf3690008 ++#define F0900_P2_PKTDELIN_LOCK 0xf3690002 ++#define F0900_P2_FIRST_LOCK 0xf3690001 ++ ++/*P2_PDELSTATUS2*/ ++#define R0900_P2_PDELSTATUS2 0xf36a ++#define F0900_P2_FRAME_MODCOD 0xf36a007c ++#define F0900_P2_FRAME_TYPE 0xf36a0003 ++ ++/*P2_BBFCRCKO1*/ ++#define R0900_P2_BBFCRCKO1 0xf36b ++#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff ++ ++/*P2_BBFCRCKO0*/ ++#define R0900_P2_BBFCRCKO0 0xf36c ++#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff ++ ++/*P2_UPCRCKO1*/ ++#define R0900_P2_UPCRCKO1 0xf36d ++#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff ++ ++/*P2_UPCRCKO0*/ ++#define R0900_P2_UPCRCKO0 0xf36e ++#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff ++ ++/*P2_PDELCTRL3*/ ++#define R0900_P2_PDELCTRL3 0xf36f ++#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080 ++#define F0900_P2_NOFIFO_BCHERR 0xf36f0020 ++ ++/*P2_TSSTATEM*/ ++#define R0900_P2_TSSTATEM 0xf370 ++#define F0900_P2_TSDIL_ON 0xf3700080 ++#define F0900_P2_TSRS_ON 0xf3700020 ++#define F0900_P2_TSDESCRAMB_ON 0xf3700010 ++#define F0900_P2_TSFRAME_MODE 0xf3700008 ++#define F0900_P2_TS_DISABLE 0xf3700004 ++#define F0900_P2_TSOUT_NOSYNC 0xf3700001 ++ ++/*P2_TSCFGH*/ ++#define R0900_P2_TSCFGH 0xf372 ++#define F0900_P2_TSFIFO_DVBCI 0xf3720080 ++#define F0900_P2_TSFIFO_SERIAL 0xf3720040 ++#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020 ++#define F0900_P2_TSFIFO_DUTY50 0xf3720010 ++#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008 ++#define F0900_P2_TSFIFO_ERRMODE 0xf3720006 ++#define F0900_P2_RST_HWARE 0xf3720001 ++ ++/*P2_TSCFGM*/ ++#define R0900_P2_TSCFGM 0xf373 ++#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0 ++#define F0900_P2_TSFIFO_PERMDATA 0xf3730020 ++#define F0900_P2_TSFIFO_DPUNACT 0xf3730002 ++#define F0900_P2_TSFIFO_INVDATA 0xf3730001 ++ ++/*P2_TSCFGL*/ ++#define R0900_P2_TSCFGL 0xf374 ++#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0 ++#define F0900_P2_BCHERROR_MODE 0xf3740030 ++#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008 ++#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004 ++#define F0900_P2_TSFIFO_BITSPEED 0xf3740003 ++ ++/*P2_TSINSDELH*/ ++#define R0900_P2_TSINSDELH 0xf376 ++#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080 ++#define F0900_P2_TSDEL_XXHEADER 0xf3760040 ++#define F0900_P2_TSDEL_BBHEADER 0xf3760020 ++#define F0900_P2_TSDEL_DATAFIELD 0xf3760010 ++#define F0900_P2_TSINSDEL_ISCR 0xf3760008 ++#define F0900_P2_TSINSDEL_NPD 0xf3760004 ++#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002 ++#define F0900_P2_TSINSDEL_CRC8 0xf3760001 ++ ++/*P2_TSDIVN*/ ++#define R0900_P2_TSDIVN 0xf379 ++#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0 ++ ++/*P2_TSCFG4*/ ++#define R0900_P2_TSCFG4 0xf37a ++#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0 ++ ++/*P2_TSSPEED*/ ++#define R0900_P2_TSSPEED 0xf380 ++#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff ++ ++/*P2_TSSTATUS*/ ++#define R0900_P2_TSSTATUS 0xf381 ++#define F0900_P2_TSFIFO_LINEOK 0xf3810080 ++#define F0900_P2_TSFIFO_ERROR 0xf3810040 ++#define F0900_P2_DIL_READY 0xf3810001 ++ ++/*P2_TSSTATUS2*/ ++#define R0900_P2_TSSTATUS2 0xf382 ++#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080 ++#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040 ++#define F0900_P2_DILXX_RESET 0xf3820020 ++#define F0900_P2_TSSERIAL_IMPOS 0xf3820010 ++#define F0900_P2_SCRAMBDETECT 0xf3820002 ++ ++/*P2_TSBITRATE1*/ ++#define R0900_P2_TSBITRATE1 0xf383 ++#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff ++ ++/*P2_TSBITRATE0*/ ++#define R0900_P2_TSBITRATE0 0xf384 ++#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff ++ ++/*P2_ERRCTRL1*/ ++#define R0900_P2_ERRCTRL1 0xf398 ++#define F0900_P2_ERR_SOURCE1 0xf39800f0 ++#define F0900_P2_NUM_EVENT1 0xf3980007 ++ ++/*P2_ERRCNT12*/ ++#define R0900_P2_ERRCNT12 0xf399 ++#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080 ++#define F0900_P2_ERR_CNT12 0xf399007f ++ ++/*P2_ERRCNT11*/ ++#define R0900_P2_ERRCNT11 0xf39a ++#define F0900_P2_ERR_CNT11 0xf39a00ff ++ ++/*P2_ERRCNT10*/ ++#define R0900_P2_ERRCNT10 0xf39b ++#define F0900_P2_ERR_CNT10 0xf39b00ff ++ ++/*P2_ERRCTRL2*/ ++#define R0900_P2_ERRCTRL2 0xf39c ++#define F0900_P2_ERR_SOURCE2 0xf39c00f0 ++#define F0900_P2_NUM_EVENT2 0xf39c0007 ++ ++/*P2_ERRCNT22*/ ++#define R0900_P2_ERRCNT22 0xf39d ++#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080 ++#define F0900_P2_ERR_CNT22 0xf39d007f ++ ++/*P2_ERRCNT21*/ ++#define R0900_P2_ERRCNT21 0xf39e ++#define F0900_P2_ERR_CNT21 0xf39e00ff ++ ++/*P2_ERRCNT20*/ ++#define R0900_P2_ERRCNT20 0xf39f ++#define F0900_P2_ERR_CNT20 0xf39f00ff ++ ++/*P2_FECSPY*/ ++#define R0900_P2_FECSPY 0xf3a0 ++#define F0900_P2_SPY_ENABLE 0xf3a00080 ++#define F0900_P2_NO_SYNCBYTE 0xf3a00040 ++#define F0900_P2_SERIAL_MODE 0xf3a00020 ++#define F0900_P2_UNUSUAL_PACKET 0xf3a00010 ++#define F0900_P2_BERMETER_DATAMODE 0xf3a00008 ++#define F0900_P2_BERMETER_LMODE 0xf3a00002 ++#define F0900_P2_BERMETER_RESET 0xf3a00001 ++ ++/*P2_FSPYCFG*/ ++#define R0900_P2_FSPYCFG 0xf3a1 ++#define F0900_P2_FECSPY_INPUT 0xf3a100c0 ++#define F0900_P2_RST_ON_ERROR 0xf3a10020 ++#define F0900_P2_ONE_SHOT 0xf3a10010 ++#define F0900_P2_I2C_MODE 0xf3a1000c ++#define F0900_P2_SPY_HYSTERESIS 0xf3a10003 ++ ++/*P2_FSPYDATA*/ ++#define R0900_P2_FSPYDATA 0xf3a2 ++#define F0900_P2_SPY_STUFFING 0xf3a20080 ++#define F0900_P2_SPY_CNULLPKT 0xf3a20020 ++#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f ++ ++/*P2_FSPYOUT*/ ++#define R0900_P2_FSPYOUT 0xf3a3 ++#define F0900_P2_FSPY_DIRECT 0xf3a30080 ++#define F0900_P2_STUFF_MODE 0xf3a30007 ++ ++/*P2_FSTATUS*/ ++#define R0900_P2_FSTATUS 0xf3a4 ++#define F0900_P2_SPY_ENDSIM 0xf3a40080 ++#define F0900_P2_VALID_SIM 0xf3a40040 ++#define F0900_P2_FOUND_SIGNAL 0xf3a40020 ++#define F0900_P2_DSS_SYNCBYTE 0xf3a40010 ++#define F0900_P2_RESULT_STATE 0xf3a4000f ++ ++/*P2_FBERCPT4*/ ++#define R0900_P2_FBERCPT4 0xf3a8 ++#define F0900_P2_FBERMETER_CPT4 0xf3a800ff ++ ++/*P2_FBERCPT3*/ ++#define R0900_P2_FBERCPT3 0xf3a9 ++#define F0900_P2_FBERMETER_CPT3 0xf3a900ff ++ ++/*P2_FBERCPT2*/ ++#define R0900_P2_FBERCPT2 0xf3aa ++#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff ++ ++/*P2_FBERCPT1*/ ++#define R0900_P2_FBERCPT1 0xf3ab ++#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff ++ ++/*P2_FBERCPT0*/ ++#define R0900_P2_FBERCPT0 0xf3ac ++#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff ++ ++/*P2_FBERERR2*/ ++#define R0900_P2_FBERERR2 0xf3ad ++#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff ++ ++/*P2_FBERERR1*/ ++#define R0900_P2_FBERERR1 0xf3ae ++#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff ++ ++/*P2_FBERERR0*/ ++#define R0900_P2_FBERERR0 0xf3af ++#define F0900_P2_FBERMETER_ERR0 0xf3af00ff ++ ++/*P2_FSPYBER*/ ++#define R0900_P2_FSPYBER 0xf3b2 ++#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010 ++#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008 ++#define F0900_P2_FSPYBER_CTIME 0xf3b20007 ++ ++/*P1_IQCONST*/ ++#define R0900_P1_IQCONST 0xf400 ++#define IQCONST REGx(R0900_P1_IQCONST) ++#define F0900_P1_CONSTEL_SELECT 0xf4000060 ++#define F0900_P1_IQSYMB_SEL 0xf400001f ++ ++/*P1_NOSCFG*/ ++#define R0900_P1_NOSCFG 0xf401 ++#define NOSCFG REGx(R0900_P1_NOSCFG) ++#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020 ++#define F0900_P1_NOSPLH_BETA 0xf4010018 ++#define F0900_P1_NOSDATA_BETA 0xf4010007 ++ ++/*P1_ISYMB*/ ++#define R0900_P1_ISYMB 0xf402 ++#define ISYMB REGx(R0900_P1_ISYMB) ++#define F0900_P1_I_SYMBOL 0xf40201ff ++ ++/*P1_QSYMB*/ ++#define R0900_P1_QSYMB 0xf403 ++#define QSYMB REGx(R0900_P1_QSYMB) ++#define F0900_P1_Q_SYMBOL 0xf40301ff ++ ++/*P1_AGC1CFG*/ ++#define R0900_P1_AGC1CFG 0xf404 ++#define AGC1CFG REGx(R0900_P1_AGC1CFG) ++#define F0900_P1_DC_FROZEN 0xf4040080 ++#define F0900_P1_DC_CORRECT 0xf4040040 ++#define F0900_P1_AMM_FROZEN 0xf4040020 ++#define F0900_P1_AMM_CORRECT 0xf4040010 ++#define F0900_P1_QUAD_FROZEN 0xf4040008 ++#define F0900_P1_QUAD_CORRECT 0xf4040004 ++ ++/*P1_AGC1CN*/ ++#define R0900_P1_AGC1CN 0xf406 ++#define AGC1CN REGx(R0900_P1_AGC1CN) ++#define F0900_P1_AGC1_LOCKED 0xf4060080 ++#define F0900_P1_AGC1_MINPOWER 0xf4060010 ++#define F0900_P1_AGCOUT_FAST 0xf4060008 ++#define F0900_P1_AGCIQ_BETA 0xf4060007 ++ ++/*P1_AGC1REF*/ ++#define R0900_P1_AGC1REF 0xf407 ++#define AGC1REF REGx(R0900_P1_AGC1REF) ++#define F0900_P1_AGCIQ_REF 0xf40700ff ++ ++/*P1_IDCCOMP*/ ++#define R0900_P1_IDCCOMP 0xf408 ++#define IDCCOMP REGx(R0900_P1_IDCCOMP) ++#define F0900_P1_IAVERAGE_ADJ 0xf40801ff ++ ++/*P1_QDCCOMP*/ ++#define R0900_P1_QDCCOMP 0xf409 ++#define QDCCOMP REGx(R0900_P1_QDCCOMP) ++#define F0900_P1_QAVERAGE_ADJ 0xf40901ff ++ ++/*P1_POWERI*/ ++#define R0900_P1_POWERI 0xf40a ++#define POWERI REGx(R0900_P1_POWERI) ++#define F0900_P1_POWER_I 0xf40a00ff ++#define POWER_I FLDx(F0900_P1_POWER_I) ++ ++/*P1_POWERQ*/ ++#define R0900_P1_POWERQ 0xf40b ++#define POWERQ REGx(R0900_P1_POWERQ) ++#define F0900_P1_POWER_Q 0xf40b00ff ++#define POWER_Q FLDx(F0900_P1_POWER_Q) ++ ++/*P1_AGC1AMM*/ ++#define R0900_P1_AGC1AMM 0xf40c ++#define AGC1AMM REGx(R0900_P1_AGC1AMM) ++#define F0900_P1_AMM_VALUE 0xf40c00ff ++ ++/*P1_AGC1QUAD*/ ++#define R0900_P1_AGC1QUAD 0xf40d ++#define AGC1QUAD REGx(R0900_P1_AGC1QUAD) ++#define F0900_P1_QUAD_VALUE 0xf40d01ff ++ ++/*P1_AGCIQIN1*/ ++#define R0900_P1_AGCIQIN1 0xf40e ++#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1) ++#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff ++#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1) ++ ++/*P1_AGCIQIN0*/ ++#define R0900_P1_AGCIQIN0 0xf40f ++#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0) ++#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff ++#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0) ++ ++/*P1_DEMOD*/ ++#define R0900_P1_DEMOD 0xf410 ++#define DEMOD REGx(R0900_P1_DEMOD) ++#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080 ++#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF) ++ ++#define F0900_P1_SPECINV_CONTROL 0xf4100030 ++#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL) ++#define F0900_P1_FORCE_ENASAMP 0xf4100008 ++#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004 ++#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF) ++#define F0900_P1_ROLLOFF_CONTROL 0xf4100003 ++#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL) ++ ++/*P1_DMDMODCOD*/ ++#define R0900_P1_DMDMODCOD 0xf411 ++#define DMDMODCOD REGx(R0900_P1_DMDMODCOD) ++#define F0900_P1_MANUAL_MODCOD 0xf4110080 ++#define F0900_P1_DEMOD_MODCOD 0xf411007c ++#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD) ++#define F0900_P1_DEMOD_TYPE 0xf4110003 ++#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE) ++ ++/*P1_DSTATUS*/ ++#define R0900_P1_DSTATUS 0xf412 ++#define DSTATUS REGx(R0900_P1_DSTATUS) ++#define F0900_P1_CAR_LOCK 0xf4120080 ++#define F0900_P1_TMGLOCK_QUALITY 0xf4120060 ++#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY) ++#define F0900_P1_LOCK_DEFINITIF 0xf4120008 ++#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF) ++#define F0900_P1_OVADC_DETECT 0xf4120001 ++ ++/*P1_DSTATUS2*/ ++#define R0900_P1_DSTATUS2 0xf413 ++#define DSTATUS2 REGx(R0900_P1_DSTATUS2) ++#define F0900_P1_DEMOD_DELOCK 0xf4130080 ++#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008 ++#define F0900_P1_AGC2_OVERFLOW 0xf4130004 ++#define F0900_P1_CFR_OVERFLOW 0xf4130002 ++#define F0900_P1_GAMMA_OVERUNDER 0xf4130001 ++ ++/*P1_DMDCFGMD*/ ++#define R0900_P1_DMDCFGMD 0xf414 ++#define DMDCFGMD REGx(R0900_P1_DMDCFGMD) ++#define F0900_P1_DVBS2_ENABLE 0xf4140080 ++#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE) ++#define F0900_P1_DVBS1_ENABLE 0xf4140040 ++#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE) ++#define F0900_P1_SCAN_ENABLE 0xf4140010 ++#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE) ++#define F0900_P1_CFR_AUTOSCAN 0xf4140008 ++#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN) ++#define F0900_P1_TUN_RNG 0xf4140003 ++ ++/*P1_DMDCFG2*/ ++#define R0900_P1_DMDCFG2 0xf415 ++#define DMDCFG2 REGx(R0900_P1_DMDCFG2) ++#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040 ++#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL) ++#define F0900_P1_INFINITE_RELOCK 0xf4150010 ++ ++/*P1_DMDISTATE*/ ++#define R0900_P1_DMDISTATE 0xf416 ++#define DMDISTATE REGx(R0900_P1_DMDISTATE) ++#define F0900_P1_I2C_DEMOD_MODE 0xf416001f ++#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE) ++ ++/*P1_DMDT0M*/ ++#define R0900_P1_DMDT0M 0xf417 ++#define DMDT0M REGx(R0900_P1_DMDT0M) ++#define F0900_P1_DMDT0_MIN 0xf41700ff ++ ++/*P1_DMDSTATE*/ ++#define R0900_P1_DMDSTATE 0xf41b ++#define DMDSTATE REGx(R0900_P1_DMDSTATE) ++#define F0900_P1_HEADER_MODE 0xf41b0060 ++#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE) ++ ++/*P1_DMDFLYW*/ ++#define R0900_P1_DMDFLYW 0xf41c ++#define DMDFLYW REGx(R0900_P1_DMDFLYW) ++#define F0900_P1_I2C_IRQVAL 0xf41c00f0 ++#define F0900_P1_FLYWHEEL_CPT 0xf41c000f ++#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT) ++ ++/*P1_DSTATUS3*/ ++#define R0900_P1_DSTATUS3 0xf41d ++#define DSTATUS3 REGx(R0900_P1_DSTATUS3) ++#define F0900_P1_DEMOD_CFGMODE 0xf41d0060 ++ ++/*P1_DMDCFG3*/ ++#define R0900_P1_DMDCFG3 0xf41e ++#define DMDCFG3 REGx(R0900_P1_DMDCFG3) ++#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008 ++ ++/*P1_DMDCFG4*/ ++#define R0900_P1_DMDCFG4 0xf41f ++#define DMDCFG4 REGx(R0900_P1_DMDCFG4) ++#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008 ++ ++/*P1_CORRELMANT*/ ++#define R0900_P1_CORRELMANT 0xf420 ++#define CORRELMANT REGx(R0900_P1_CORRELMANT) ++#define F0900_P1_CORREL_MANT 0xf42000ff ++ ++/*P1_CORRELABS*/ ++#define R0900_P1_CORRELABS 0xf421 ++#define CORRELABS REGx(R0900_P1_CORRELABS) ++#define F0900_P1_CORREL_ABS 0xf42100ff ++ ++/*P1_CORRELEXP*/ ++#define R0900_P1_CORRELEXP 0xf422 ++#define CORRELEXP REGx(R0900_P1_CORRELEXP) ++#define F0900_P1_CORREL_ABSEXP 0xf42200f0 ++#define F0900_P1_CORREL_EXP 0xf422000f ++ ++/*P1_PLHMODCOD*/ ++#define R0900_P1_PLHMODCOD 0xf424 ++#define PLHMODCOD REGx(R0900_P1_PLHMODCOD) ++#define F0900_P1_SPECINV_DEMOD 0xf4240080 ++#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD) ++#define F0900_P1_PLH_MODCOD 0xf424007c ++#define F0900_P1_PLH_TYPE 0xf4240003 ++ ++/*P1_DMDREG*/ ++#define R0900_P1_DMDREG 0xf425 ++#define DMDREG REGx(R0900_P1_DMDREG) ++#define F0900_P1_DECIM_PLFRAMES 0xf4250001 ++ ++/*P1_AGC2O*/ ++#define R0900_P1_AGC2O 0xf42c ++#define AGC2O REGx(R0900_P1_AGC2O) ++#define F0900_P1_AGC2_COEF 0xf42c0007 ++ ++/*P1_AGC2REF*/ ++#define R0900_P1_AGC2REF 0xf42d ++#define AGC2REF REGx(R0900_P1_AGC2REF) ++#define F0900_P1_AGC2_REF 0xf42d00ff ++ ++/*P1_AGC1ADJ*/ ++#define R0900_P1_AGC1ADJ 0xf42e ++#define AGC1ADJ REGx(R0900_P1_AGC1ADJ) ++#define F0900_P1_AGC1_ADJUSTED 0xf42e007f ++ ++/*P1_AGC2I1*/ ++#define R0900_P1_AGC2I1 0xf436 ++#define AGC2I1 REGx(R0900_P1_AGC2I1) ++#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff ++ ++/*P1_AGC2I0*/ ++#define R0900_P1_AGC2I0 0xf437 ++#define AGC2I0 REGx(R0900_P1_AGC2I0) ++#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff ++ ++/*P1_CARCFG*/ ++#define R0900_P1_CARCFG 0xf438 ++#define CARCFG REGx(R0900_P1_CARCFG) ++#define F0900_P1_CFRUPLOW_AUTO 0xf4380080 ++#define F0900_P1_CFRUPLOW_TEST 0xf4380040 ++#define F0900_P1_ROTAON 0xf4380004 ++#define F0900_P1_PH_DET_ALGO 0xf4380003 ++ ++/*P1_ACLC*/ ++#define R0900_P1_ACLC 0xf439 ++#define ACLC REGx(R0900_P1_ACLC) ++#define F0900_P1_CAR_ALPHA_MANT 0xf4390030 ++#define F0900_P1_CAR_ALPHA_EXP 0xf439000f ++ ++/*P1_BCLC*/ ++#define R0900_P1_BCLC 0xf43a ++#define BCLC REGx(R0900_P1_BCLC) ++#define F0900_P1_CAR_BETA_MANT 0xf43a0030 ++#define F0900_P1_CAR_BETA_EXP 0xf43a000f ++ ++/*P1_CARFREQ*/ ++#define R0900_P1_CARFREQ 0xf43d ++#define CARFREQ REGx(R0900_P1_CARFREQ) ++#define F0900_P1_KC_COARSE_EXP 0xf43d00f0 ++#define F0900_P1_BETA_FREQ 0xf43d000f ++ ++/*P1_CARHDR*/ ++#define R0900_P1_CARHDR 0xf43e ++#define CARHDR REGx(R0900_P1_CARHDR) ++#define F0900_P1_K_FREQ_HDR 0xf43e00ff ++ ++/*P1_LDT*/ ++#define R0900_P1_LDT 0xf43f ++#define LDT REGx(R0900_P1_LDT) ++#define F0900_P1_CARLOCK_THRES 0xf43f01ff ++ ++/*P1_LDT2*/ ++#define R0900_P1_LDT2 0xf440 ++#define LDT2 REGx(R0900_P1_LDT2) ++#define F0900_P1_CARLOCK_THRES2 0xf44001ff ++ ++/*P1_CFRICFG*/ ++#define R0900_P1_CFRICFG 0xf441 ++#define CFRICFG REGx(R0900_P1_CFRICFG) ++#define F0900_P1_NEG_CFRSTEP 0xf4410001 ++ ++/*P1_CFRUP1*/ ++#define R0900_P1_CFRUP1 0xf442 ++#define CFRUP1 REGx(R0900_P1_CFRUP1) ++#define F0900_P1_CFR_UP1 0xf44201ff ++#define CFR_UP1 FLDx(F0900_P1_CFR_UP1) ++ ++/*P1_CFRUP0*/ ++#define R0900_P1_CFRUP0 0xf443 ++#define CFRUP0 REGx(R0900_P1_CFRUP0) ++#define F0900_P1_CFR_UP0 0xf44300ff ++#define CFR_UP0 FLDx(F0900_P1_CFR_UP0) ++ ++/*P1_CFRLOW1*/ ++#define R0900_P1_CFRLOW1 0xf446 ++#define CFRLOW1 REGx(R0900_P1_CFRLOW1) ++#define F0900_P1_CFR_LOW1 0xf44601ff ++#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1) ++ ++/*P1_CFRLOW0*/ ++#define R0900_P1_CFRLOW0 0xf447 ++#define CFRLOW0 REGx(R0900_P1_CFRLOW0) ++#define F0900_P1_CFR_LOW0 0xf44700ff ++#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0) ++ ++/*P1_CFRINIT1*/ ++#define R0900_P1_CFRINIT1 0xf448 ++#define CFRINIT1 REGx(R0900_P1_CFRINIT1) ++#define F0900_P1_CFR_INIT1 0xf44801ff ++#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1) ++ ++/*P1_CFRINIT0*/ ++#define R0900_P1_CFRINIT0 0xf449 ++#define CFRINIT0 REGx(R0900_P1_CFRINIT0) ++#define F0900_P1_CFR_INIT0 0xf44900ff ++#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0) ++ ++/*P1_CFRINC1*/ ++#define R0900_P1_CFRINC1 0xf44a ++#define CFRINC1 REGx(R0900_P1_CFRINC1) ++#define F0900_P1_MANUAL_CFRINC 0xf44a0080 ++#define F0900_P1_CFR_INC1 0xf44a003f ++ ++/*P1_CFRINC0*/ ++#define R0900_P1_CFRINC0 0xf44b ++#define CFRINC0 REGx(R0900_P1_CFRINC0) ++#define F0900_P1_CFR_INC0 0xf44b00f8 ++ ++/*P1_CFR2*/ ++#define R0900_P1_CFR2 0xf44c ++#define CFR2 REGx(R0900_P1_CFR2) ++#define F0900_P1_CAR_FREQ2 0xf44c01ff ++#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2) ++ ++/*P1_CFR1*/ ++#define R0900_P1_CFR1 0xf44d ++#define CFR1 REGx(R0900_P1_CFR1) ++#define F0900_P1_CAR_FREQ1 0xf44d00ff ++#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1) ++ ++/*P1_CFR0*/ ++#define R0900_P1_CFR0 0xf44e ++#define CFR0 REGx(R0900_P1_CFR0) ++#define F0900_P1_CAR_FREQ0 0xf44e00ff ++#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0) ++ ++/*P1_LDI*/ ++#define R0900_P1_LDI 0xf44f ++#define LDI REGx(R0900_P1_LDI) ++#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff ++ ++/*P1_TMGCFG*/ ++#define R0900_P1_TMGCFG 0xf450 ++#define TMGCFG REGx(R0900_P1_TMGCFG) ++#define F0900_P1_TMGLOCK_BETA 0xf45000c0 ++#define F0900_P1_DO_TIMING_CORR 0xf4500010 ++#define F0900_P1_TMG_MINFREQ 0xf4500003 ++ ++/*P1_RTC*/ ++#define R0900_P1_RTC 0xf451 ++#define RTC REGx(R0900_P1_RTC) ++#define F0900_P1_TMGALPHA_EXP 0xf45100f0 ++#define F0900_P1_TMGBETA_EXP 0xf451000f ++ ++/*P1_RTCS2*/ ++#define R0900_P1_RTCS2 0xf452 ++#define RTCS2 REGx(R0900_P1_RTCS2) ++#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0 ++#define F0900_P1_TMGBETAS2_EXP 0xf452000f ++ ++/*P1_TMGTHRISE*/ ++#define R0900_P1_TMGTHRISE 0xf453 ++#define TMGTHRISE REGx(R0900_P1_TMGTHRISE) ++#define F0900_P1_TMGLOCK_THRISE 0xf45300ff ++ ++/*P1_TMGTHFALL*/ ++#define R0900_P1_TMGTHFALL 0xf454 ++#define TMGTHFALL REGx(R0900_P1_TMGTHFALL) ++#define F0900_P1_TMGLOCK_THFALL 0xf45400ff ++ ++/*P1_SFRUPRATIO*/ ++#define R0900_P1_SFRUPRATIO 0xf455 ++#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO) ++#define F0900_P1_SFR_UPRATIO 0xf45500ff ++ ++/*P1_SFRLOWRATIO*/ ++#define R0900_P1_SFRLOWRATIO 0xf456 ++#define F0900_P1_SFR_LOWRATIO 0xf45600ff ++ ++/*P1_KREFTMG*/ ++#define R0900_P1_KREFTMG 0xf458 ++#define KREFTMG REGx(R0900_P1_KREFTMG) ++#define F0900_P1_KREF_TMG 0xf45800ff ++ ++/*P1_SFRSTEP*/ ++#define R0900_P1_SFRSTEP 0xf459 ++#define SFRSTEP REGx(R0900_P1_SFRSTEP) ++#define F0900_P1_SFR_SCANSTEP 0xf45900f0 ++#define F0900_P1_SFR_CENTERSTEP 0xf459000f ++ ++/*P1_TMGCFG2*/ ++#define R0900_P1_TMGCFG2 0xf45a ++#define TMGCFG2 REGx(R0900_P1_TMGCFG2) ++#define F0900_P1_SFRRATIO_FINE 0xf45a0001 ++ ++/*P1_KREFTMG2*/ ++#define R0900_P1_KREFTMG2 0xf45b ++#define KREFTMG2 REGx(R0900_P1_KREFTMG2) ++#define F0900_P1_KREF_TMG2 0xf45b00ff ++ ++/*P1_SFRINIT1*/ ++#define R0900_P1_SFRINIT1 0xf45e ++#define SFRINIT1 REGx(R0900_P1_SFRINIT1) ++#define F0900_P1_SFR_INIT1 0xf45e007f ++ ++/*P1_SFRINIT0*/ ++#define R0900_P1_SFRINIT0 0xf45f ++#define SFRINIT0 REGx(R0900_P1_SFRINIT0) ++#define F0900_P1_SFR_INIT0 0xf45f00ff ++ ++/*P1_SFRUP1*/ ++#define R0900_P1_SFRUP1 0xf460 ++#define SFRUP1 REGx(R0900_P1_SFRUP1) ++#define F0900_P1_AUTO_GUP 0xf4600080 ++#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP) ++#define F0900_P1_SYMB_FREQ_UP1 0xf460007f ++ ++/*P1_SFRUP0*/ ++#define R0900_P1_SFRUP0 0xf461 ++#define SFRUP0 REGx(R0900_P1_SFRUP0) ++#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff ++ ++/*P1_SFRLOW1*/ ++#define R0900_P1_SFRLOW1 0xf462 ++#define SFRLOW1 REGx(R0900_P1_SFRLOW1) ++#define F0900_P1_AUTO_GLOW 0xf4620080 ++#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW) ++#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f ++ ++/*P1_SFRLOW0*/ ++#define R0900_P1_SFRLOW0 0xf463 ++#define SFRLOW0 REGx(R0900_P1_SFRLOW0) ++#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff ++ ++/*P1_SFR3*/ ++#define R0900_P1_SFR3 0xf464 ++#define SFR3 REGx(R0900_P1_SFR3) ++#define F0900_P1_SYMB_FREQ3 0xf46400ff ++#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3) ++ ++/*P1_SFR2*/ ++#define R0900_P1_SFR2 0xf465 ++#define SFR2 REGx(R0900_P1_SFR2) ++#define F0900_P1_SYMB_FREQ2 0xf46500ff ++#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2) ++ ++/*P1_SFR1*/ ++#define R0900_P1_SFR1 0xf466 ++#define SFR1 REGx(R0900_P1_SFR1) ++#define F0900_P1_SYMB_FREQ1 0xf46600ff ++#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1) ++ ++/*P1_SFR0*/ ++#define R0900_P1_SFR0 0xf467 ++#define SFR0 REGx(R0900_P1_SFR0) ++#define F0900_P1_SYMB_FREQ0 0xf46700ff ++#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0) ++ ++/*P1_TMGREG2*/ ++#define R0900_P1_TMGREG2 0xf468 ++#define TMGREG2 REGx(R0900_P1_TMGREG2) ++#define F0900_P1_TMGREG2 0xf46800ff ++ ++/*P1_TMGREG1*/ ++#define R0900_P1_TMGREG1 0xf469 ++#define TMGREG1 REGx(R0900_P1_TMGREG1) ++#define F0900_P1_TMGREG1 0xf46900ff ++ ++/*P1_TMGREG0*/ ++#define R0900_P1_TMGREG0 0xf46a ++#define TMGREG0 REGx(R0900_P1_TMGREG0) ++#define F0900_P1_TMGREG0 0xf46a00ff ++ ++/*P1_TMGLOCK1*/ ++#define R0900_P1_TMGLOCK1 0xf46b ++#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1) ++#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff ++ ++/*P1_TMGLOCK0*/ ++#define R0900_P1_TMGLOCK0 0xf46c ++#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0) ++#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff ++ ++/*P1_TMGOBS*/ ++#define R0900_P1_TMGOBS 0xf46d ++#define TMGOBS REGx(R0900_P1_TMGOBS) ++#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0 ++#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS) ++ ++/*P1_EQUALCFG*/ ++#define R0900_P1_EQUALCFG 0xf46f ++#define EQUALCFG REGx(R0900_P1_EQUALCFG) ++#define F0900_P1_EQUAL_ON 0xf46f0040 ++#define F0900_P1_MU_EQUALDFE 0xf46f0007 ++ ++/*P1_EQUAI1*/ ++#define R0900_P1_EQUAI1 0xf470 ++#define EQUAI1 REGx(R0900_P1_EQUAI1) ++#define F0900_P1_EQUA_ACCI1 0xf47001ff ++ ++/*P1_EQUAQ1*/ ++#define R0900_P1_EQUAQ1 0xf471 ++#define EQUAQ1 REGx(R0900_P1_EQUAQ1) ++#define F0900_P1_EQUA_ACCQ1 0xf47101ff ++ ++/*P1_EQUAI2*/ ++#define R0900_P1_EQUAI2 0xf472 ++#define EQUAI2 REGx(R0900_P1_EQUAI2) ++#define F0900_P1_EQUA_ACCI2 0xf47201ff ++ ++/*P1_EQUAQ2*/ ++#define R0900_P1_EQUAQ2 0xf473 ++#define EQUAQ2 REGx(R0900_P1_EQUAQ2) ++#define F0900_P1_EQUA_ACCQ2 0xf47301ff ++ ++/*P1_EQUAI3*/ ++#define R0900_P1_EQUAI3 0xf474 ++#define EQUAI3 REGx(R0900_P1_EQUAI3) ++#define F0900_P1_EQUA_ACCI3 0xf47401ff ++ ++/*P1_EQUAQ3*/ ++#define R0900_P1_EQUAQ3 0xf475 ++#define EQUAQ3 REGx(R0900_P1_EQUAQ3) ++#define F0900_P1_EQUA_ACCQ3 0xf47501ff ++ ++/*P1_EQUAI4*/ ++#define R0900_P1_EQUAI4 0xf476 ++#define EQUAI4 REGx(R0900_P1_EQUAI4) ++#define F0900_P1_EQUA_ACCI4 0xf47601ff ++ ++/*P1_EQUAQ4*/ ++#define R0900_P1_EQUAQ4 0xf477 ++#define EQUAQ4 REGx(R0900_P1_EQUAQ4) ++#define F0900_P1_EQUA_ACCQ4 0xf47701ff ++ ++/*P1_EQUAI5*/ ++#define R0900_P1_EQUAI5 0xf478 ++#define EQUAI5 REGx(R0900_P1_EQUAI5) ++#define F0900_P1_EQUA_ACCI5 0xf47801ff ++ ++/*P1_EQUAQ5*/ ++#define R0900_P1_EQUAQ5 0xf479 ++#define EQUAQ5 REGx(R0900_P1_EQUAQ5) ++#define F0900_P1_EQUA_ACCQ5 0xf47901ff ++ ++/*P1_EQUAI6*/ ++#define R0900_P1_EQUAI6 0xf47a ++#define EQUAI6 REGx(R0900_P1_EQUAI6) ++#define F0900_P1_EQUA_ACCI6 0xf47a01ff ++ ++/*P1_EQUAQ6*/ ++#define R0900_P1_EQUAQ6 0xf47b ++#define EQUAQ6 REGx(R0900_P1_EQUAQ6) ++#define F0900_P1_EQUA_ACCQ6 0xf47b01ff ++ ++/*P1_EQUAI7*/ ++#define R0900_P1_EQUAI7 0xf47c ++#define EQUAI7 REGx(R0900_P1_EQUAI7) ++#define F0900_P1_EQUA_ACCI7 0xf47c01ff ++ ++/*P1_EQUAQ7*/ ++#define R0900_P1_EQUAQ7 0xf47d ++#define EQUAQ7 REGx(R0900_P1_EQUAQ7) ++#define F0900_P1_EQUA_ACCQ7 0xf47d01ff ++ ++/*P1_EQUAI8*/ ++#define R0900_P1_EQUAI8 0xf47e ++#define EQUAI8 REGx(R0900_P1_EQUAI8) ++#define F0900_P1_EQUA_ACCI8 0xf47e01ff ++ ++/*P1_EQUAQ8*/ ++#define R0900_P1_EQUAQ8 0xf47f ++#define EQUAQ8 REGx(R0900_P1_EQUAQ8) ++#define F0900_P1_EQUA_ACCQ8 0xf47f01ff ++ ++/*P1_NNOSDATAT1*/ ++#define R0900_P1_NNOSDATAT1 0xf480 ++#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1) ++#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff ++#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1) ++ ++/*P1_NNOSDATAT0*/ ++#define R0900_P1_NNOSDATAT0 0xf481 ++#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0) ++#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff ++#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0) ++ ++/*P1_NNOSDATA1*/ ++#define R0900_P1_NNOSDATA1 0xf482 ++#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1) ++#define F0900_P1_NOSDATA_NORMED1 0xf48200ff ++ ++/*P1_NNOSDATA0*/ ++#define R0900_P1_NNOSDATA0 0xf483 ++#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0) ++#define F0900_P1_NOSDATA_NORMED0 0xf48300ff ++ ++/*P1_NNOSPLHT1*/ ++#define R0900_P1_NNOSPLHT1 0xf484 ++#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1) ++#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff ++#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1) ++ ++/*P1_NNOSPLHT0*/ ++#define R0900_P1_NNOSPLHT0 0xf485 ++#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0) ++#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff ++#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0) ++ ++/*P1_NNOSPLH1*/ ++#define R0900_P1_NNOSPLH1 0xf486 ++#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1) ++#define F0900_P1_NOSPLH_NORMED1 0xf48600ff ++ ++/*P1_NNOSPLH0*/ ++#define R0900_P1_NNOSPLH0 0xf487 ++#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0) ++#define F0900_P1_NOSPLH_NORMED0 0xf48700ff ++ ++/*P1_NOSDATAT1*/ ++#define R0900_P1_NOSDATAT1 0xf488 ++#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1) ++#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff ++ ++/*P1_NOSDATAT0*/ ++#define R0900_P1_NOSDATAT0 0xf489 ++#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0) ++#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff ++ ++/*P1_NOSDATA1*/ ++#define R0900_P1_NOSDATA1 0xf48a ++#define NOSDATA1 REGx(R0900_P1_NOSDATA1) ++#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff ++ ++/*P1_NOSDATA0*/ ++#define R0900_P1_NOSDATA0 0xf48b ++#define NOSDATA0 REGx(R0900_P1_NOSDATA0) ++#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff ++ ++/*P1_NOSPLHT1*/ ++#define R0900_P1_NOSPLHT1 0xf48c ++#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1) ++#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff ++ ++/*P1_NOSPLHT0*/ ++#define R0900_P1_NOSPLHT0 0xf48d ++#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0) ++#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff ++ ++/*P1_NOSPLH1*/ ++#define R0900_P1_NOSPLH1 0xf48e ++#define NOSPLH1 REGx(R0900_P1_NOSPLH1) ++#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff ++ ++/*P1_NOSPLH0*/ ++#define R0900_P1_NOSPLH0 0xf48f ++#define NOSPLH0 REGx(R0900_P1_NOSPLH0) ++#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff ++ ++/*P1_CAR2CFG*/ ++#define R0900_P1_CAR2CFG 0xf490 ++#define CAR2CFG REGx(R0900_P1_CAR2CFG) ++#define F0900_P1_CARRIER3_DISABLE 0xf4900040 ++#define F0900_P1_ROTA2ON 0xf4900004 ++#define F0900_P1_PH_DET_ALGO2 0xf4900003 ++ ++/*P1_CFR2CFR1*/ ++#define R0900_P1_CFR2CFR1 0xf491 ++#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1) ++#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0 ++#define F0900_P1_EN_S2CAR2CENTER 0xf4910020 ++#define F0900_P1_DIS_BCHERRCFR2 0xf4910010 ++#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007 ++ ++/*P1_CFR22*/ ++#define R0900_P1_CFR22 0xf493 ++#define CFR22 REGx(R0900_P1_CFR22) ++#define F0900_P1_CAR2_FREQ2 0xf49301ff ++ ++/*P1_CFR21*/ ++#define R0900_P1_CFR21 0xf494 ++#define CFR21 REGx(R0900_P1_CFR21) ++#define F0900_P1_CAR2_FREQ1 0xf49400ff ++ ++/*P1_CFR20*/ ++#define R0900_P1_CFR20 0xf495 ++#define CFR20 REGx(R0900_P1_CFR20) ++#define F0900_P1_CAR2_FREQ0 0xf49500ff ++ ++/*P1_ACLC2S2Q*/ ++#define R0900_P1_ACLC2S2Q 0xf497 ++#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q) ++#define F0900_P1_ENAB_SPSKSYMB 0xf4970080 ++#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030 ++#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f ++ ++/*P1_ACLC2S28*/ ++#define R0900_P1_ACLC2S28 0xf498 ++#define ACLC2S28 REGx(R0900_P1_ACLC2S28) ++#define F0900_P1_OLDI3Q_MODE 0xf4980080 ++#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030 ++#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f ++ ++/*P1_ACLC2S216A*/ ++#define R0900_P1_ACLC2S216A 0xf499 ++#define ACLC2S216A REGx(R0900_P1_ACLC2S216A) ++#define F0900_P1_DIS_C3STOPA2 0xf4990080 ++#define F0900_P1_CAR2S2_16ADERAT 0xf4990040 ++#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030 ++#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f ++ ++/*P1_ACLC2S232A*/ ++#define R0900_P1_ACLC2S232A 0xf49a ++#define ACLC2S232A REGx(R0900_P1_ACLC2S232A) ++#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040 ++#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030 ++#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f ++ ++/*P1_BCLC2S2Q*/ ++#define R0900_P1_BCLC2S2Q 0xf49c ++#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q) ++#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030 ++#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f ++ ++/*P1_BCLC2S28*/ ++#define R0900_P1_BCLC2S28 0xf49d ++#define BCLC2S28 REGx(R0900_P1_BCLC2S28) ++#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030 ++#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f ++ ++/*P1_BCLC2S216A*/ ++#define R0900_P1_BCLC2S216A 0xf49e ++#define BCLC2S216A REGx(R0900_P1_BCLC2S216A) ++ ++/*P1_BCLC2S232A*/ ++#define R0900_P1_BCLC2S232A 0xf49f ++#define BCLC2S232A REGx(R0900_P1_BCLC2S232A) ++ ++/*P1_PLROOT2*/ ++#define R0900_P1_PLROOT2 0xf4ac ++#define PLROOT2 REGx(R0900_P1_PLROOT2) ++#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c ++#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003 ++ ++/*P1_PLROOT1*/ ++#define R0900_P1_PLROOT1 0xf4ad ++#define PLROOT1 REGx(R0900_P1_PLROOT1) ++#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff ++ ++/*P1_PLROOT0*/ ++#define R0900_P1_PLROOT0 0xf4ae ++#define PLROOT0 REGx(R0900_P1_PLROOT0) ++#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff ++ ++/*P1_MODCODLST0*/ ++#define R0900_P1_MODCODLST0 0xf4b0 ++#define MODCODLST0 REGx(R0900_P1_MODCODLST0) ++ ++/*P1_MODCODLST1*/ ++#define R0900_P1_MODCODLST1 0xf4b1 ++#define MODCODLST1 REGx(R0900_P1_MODCODLST1) ++#define F0900_P1_DIS_MODCOD29 0xf4b100f0 ++#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f ++ ++/*P1_MODCODLST2*/ ++#define R0900_P1_MODCODLST2 0xf4b2 ++#define MODCODLST2 REGx(R0900_P1_MODCODLST2) ++#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0 ++#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f ++ ++/*P1_MODCODLST3*/ ++#define R0900_P1_MODCODLST3 0xf4b3 ++#define MODCODLST3 REGx(R0900_P1_MODCODLST3) ++#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0 ++#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f ++ ++/*P1_MODCODLST4*/ ++#define R0900_P1_MODCODLST4 0xf4b4 ++#define MODCODLST4 REGx(R0900_P1_MODCODLST4) ++#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0 ++#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f ++ ++/*P1_MODCODLST5*/ ++#define R0900_P1_MODCODLST5 0xf4b5 ++#define MODCODLST5 REGx(R0900_P1_MODCODLST5) ++#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0 ++#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f ++ ++/*P1_MODCODLST6*/ ++#define R0900_P1_MODCODLST6 0xf4b6 ++#define MODCODLST6 REGx(R0900_P1_MODCODLST6) ++#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0 ++#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f ++ ++/*P1_MODCODLST7*/ ++#define R0900_P1_MODCODLST7 0xf4b7 ++#define MODCODLST7 REGx(R0900_P1_MODCODLST7) ++#define F0900_P1_DIS_8P_9_10 0xf4b700f0 ++#define F0900_P1_DIS_8P_8_9 0xf4b7000f ++ ++/*P1_MODCODLST8*/ ++#define R0900_P1_MODCODLST8 0xf4b8 ++#define MODCODLST8 REGx(R0900_P1_MODCODLST8) ++#define F0900_P1_DIS_8P_5_6 0xf4b800f0 ++#define F0900_P1_DIS_8P_3_4 0xf4b8000f ++ ++/*P1_MODCODLST9*/ ++#define R0900_P1_MODCODLST9 0xf4b9 ++#define MODCODLST9 REGx(R0900_P1_MODCODLST9) ++#define F0900_P1_DIS_8P_2_3 0xf4b900f0 ++#define F0900_P1_DIS_8P_3_5 0xf4b9000f ++ ++/*P1_MODCODLSTA*/ ++#define R0900_P1_MODCODLSTA 0xf4ba ++#define MODCODLSTA REGx(R0900_P1_MODCODLSTA) ++#define F0900_P1_DIS_QP_9_10 0xf4ba00f0 ++#define F0900_P1_DIS_QP_8_9 0xf4ba000f ++ ++/*P1_MODCODLSTB*/ ++#define R0900_P1_MODCODLSTB 0xf4bb ++#define MODCODLSTB REGx(R0900_P1_MODCODLSTB) ++#define F0900_P1_DIS_QP_5_6 0xf4bb00f0 ++#define F0900_P1_DIS_QP_4_5 0xf4bb000f ++ ++/*P1_MODCODLSTC*/ ++#define R0900_P1_MODCODLSTC 0xf4bc ++#define MODCODLSTC REGx(R0900_P1_MODCODLSTC) ++#define F0900_P1_DIS_QP_3_4 0xf4bc00f0 ++#define F0900_P1_DIS_QP_2_3 0xf4bc000f ++ ++/*P1_MODCODLSTD*/ ++#define R0900_P1_MODCODLSTD 0xf4bd ++#define MODCODLSTD REGx(R0900_P1_MODCODLSTD) ++#define F0900_P1_DIS_QP_3_5 0xf4bd00f0 ++#define F0900_P1_DIS_QP_1_2 0xf4bd000f ++ ++/*P1_MODCODLSTE*/ ++#define R0900_P1_MODCODLSTE 0xf4be ++#define MODCODLSTE REGx(R0900_P1_MODCODLSTE) ++#define F0900_P1_DIS_QP_2_5 0xf4be00f0 ++#define F0900_P1_DIS_QP_1_3 0xf4be000f ++ ++/*P1_MODCODLSTF*/ ++#define R0900_P1_MODCODLSTF 0xf4bf ++#define MODCODLSTF REGx(R0900_P1_MODCODLSTF) ++#define F0900_P1_DIS_QP_1_4 0xf4bf00f0 ++ ++/*P1_GAUSSR0*/ ++#define R0900_P1_GAUSSR0 0xf4c0 ++#define GAUSSR0 REGx(R0900_P1_GAUSSR0) ++#define F0900_P1_EN_CCIMODE 0xf4c00080 ++#define F0900_P1_R0_GAUSSIEN 0xf4c0007f ++ ++/*P1_CCIR0*/ ++#define R0900_P1_CCIR0 0xf4c1 ++#define CCIR0 REGx(R0900_P1_CCIR0) ++#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080 ++#define F0900_P1_R0_CCI 0xf4c1007f ++ ++/*P1_CCIQUANT*/ ++#define R0900_P1_CCIQUANT 0xf4c2 ++#define CCIQUANT REGx(R0900_P1_CCIQUANT) ++#define F0900_P1_CCI_BETA 0xf4c200e0 ++#define F0900_P1_CCI_QUANT 0xf4c2001f ++ ++/*P1_CCITHRES*/ ++#define R0900_P1_CCITHRES 0xf4c3 ++#define CCITHRES REGx(R0900_P1_CCITHRES) ++#define F0900_P1_CCI_THRESHOLD 0xf4c300ff ++ ++/*P1_CCIACC*/ ++#define R0900_P1_CCIACC 0xf4c4 ++#define CCIACC REGx(R0900_P1_CCIACC) ++#define F0900_P1_CCI_VALUE 0xf4c400ff ++ ++/*P1_DMDRESCFG*/ ++#define R0900_P1_DMDRESCFG 0xf4c6 ++#define DMDRESCFG REGx(R0900_P1_DMDRESCFG) ++#define F0900_P1_DMDRES_RESET 0xf4c60080 ++#define F0900_P1_DMDRES_STRALL 0xf4c60008 ++#define F0900_P1_DMDRES_NEWONLY 0xf4c60004 ++#define F0900_P1_DMDRES_NOSTORE 0xf4c60002 ++ ++/*P1_DMDRESADR*/ ++#define R0900_P1_DMDRESADR 0xf4c7 ++#define DMDRESADR REGx(R0900_P1_DMDRESADR) ++#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040 ++#define F0900_P1_DMDRES_MEMFULL 0xf4c70030 ++#define F0900_P1_DMDRES_RESNBR 0xf4c7000f ++ ++/*P1_DMDRESDATA7*/ ++#define R0900_P1_DMDRESDATA7 0xf4c8 ++#define F0900_P1_DMDRES_DATA7 0xf4c800ff ++ ++/*P1_DMDRESDATA6*/ ++#define R0900_P1_DMDRESDATA6 0xf4c9 ++#define F0900_P1_DMDRES_DATA6 0xf4c900ff ++ ++/*P1_DMDRESDATA5*/ ++#define R0900_P1_DMDRESDATA5 0xf4ca ++#define F0900_P1_DMDRES_DATA5 0xf4ca00ff ++ ++/*P1_DMDRESDATA4*/ ++#define R0900_P1_DMDRESDATA4 0xf4cb ++#define F0900_P1_DMDRES_DATA4 0xf4cb00ff ++ ++/*P1_DMDRESDATA3*/ ++#define R0900_P1_DMDRESDATA3 0xf4cc ++#define F0900_P1_DMDRES_DATA3 0xf4cc00ff ++ ++/*P1_DMDRESDATA2*/ ++#define R0900_P1_DMDRESDATA2 0xf4cd ++#define F0900_P1_DMDRES_DATA2 0xf4cd00ff ++ ++/*P1_DMDRESDATA1*/ ++#define R0900_P1_DMDRESDATA1 0xf4ce ++#define F0900_P1_DMDRES_DATA1 0xf4ce00ff ++ ++/*P1_DMDRESDATA0*/ ++#define R0900_P1_DMDRESDATA0 0xf4cf ++#define F0900_P1_DMDRES_DATA0 0xf4cf00ff ++ ++/*P1_FFEI1*/ ++#define R0900_P1_FFEI1 0xf4d0 ++#define FFEI1 REGx(R0900_P1_FFEI1) ++#define F0900_P1_FFE_ACCI1 0xf4d001ff ++ ++/*P1_FFEQ1*/ ++#define R0900_P1_FFEQ1 0xf4d1 ++#define FFEQ1 REGx(R0900_P1_FFEQ1) ++#define F0900_P1_FFE_ACCQ1 0xf4d101ff ++ ++/*P1_FFEI2*/ ++#define R0900_P1_FFEI2 0xf4d2 ++#define FFEI2 REGx(R0900_P1_FFEI2) ++#define F0900_P1_FFE_ACCI2 0xf4d201ff ++ ++/*P1_FFEQ2*/ ++#define R0900_P1_FFEQ2 0xf4d3 ++#define FFEQ2 REGx(R0900_P1_FFEQ2) ++#define F0900_P1_FFE_ACCQ2 0xf4d301ff ++ ++/*P1_FFEI3*/ ++#define R0900_P1_FFEI3 0xf4d4 ++#define FFEI3 REGx(R0900_P1_FFEI3) ++#define F0900_P1_FFE_ACCI3 0xf4d401ff ++ ++/*P1_FFEQ3*/ ++#define R0900_P1_FFEQ3 0xf4d5 ++#define FFEQ3 REGx(R0900_P1_FFEQ3) ++#define F0900_P1_FFE_ACCQ3 0xf4d501ff ++ ++/*P1_FFEI4*/ ++#define R0900_P1_FFEI4 0xf4d6 ++#define FFEI4 REGx(R0900_P1_FFEI4) ++#define F0900_P1_FFE_ACCI4 0xf4d601ff ++ ++/*P1_FFEQ4*/ ++#define R0900_P1_FFEQ4 0xf4d7 ++#define FFEQ4 REGx(R0900_P1_FFEQ4) ++#define F0900_P1_FFE_ACCQ4 0xf4d701ff ++ ++/*P1_FFECFG*/ ++#define R0900_P1_FFECFG 0xf4d8 ++#define FFECFG REGx(R0900_P1_FFECFG) ++#define F0900_P1_EQUALFFE_ON 0xf4d80040 ++#define F0900_P1_MU_EQUALFFE 0xf4d80007 ++ ++/*P1_TNRCFG*/ ++#define R0900_P1_TNRCFG 0xf4e0 ++#define TNRCFG REGx(R0900_P1_TNRCFG) ++#define F0900_P1_TUN_ACKFAIL 0xf4e00080 ++#define F0900_P1_TUN_TYPE 0xf4e00070 ++#define F0900_P1_TUN_SECSTOP 0xf4e00008 ++#define F0900_P1_TUN_VCOSRCH 0xf4e00004 ++#define F0900_P1_TUN_MADDRESS 0xf4e00003 ++ ++/*P1_TNRCFG2*/ ++#define R0900_P1_TNRCFG2 0xf4e1 ++#define TNRCFG2 REGx(R0900_P1_TNRCFG2) ++#define F0900_P1_TUN_IQSWAP 0xf4e10080 ++#define F0900_P1_DIS_BWCALC 0xf4e10004 ++#define F0900_P1_SHORT_WAITSTATES 0xf4e10002 ++ ++/*P1_TNRXTAL*/ ++#define R0900_P1_TNRXTAL 0xf4e4 ++#define TNRXTAL REGx(R0900_P1_TNRXTAL) ++#define F0900_P1_TUN_XTALFREQ 0xf4e4001f ++ ++/*P1_TNRSTEPS*/ ++#define R0900_P1_TNRSTEPS 0xf4e7 ++#define TNRSTEPS REGx(R0900_P1_TNRSTEPS) ++#define F0900_P1_TUNER_BW0P125 0xf4e70080 ++#define F0900_P1_BWINC_OFFSET 0xf4e70170 ++#define F0900_P1_SOFTSTEP_RNG 0xf4e70008 ++#define F0900_P1_TUN_BWOFFSET 0xf4e70007 ++ ++/*P1_TNRGAIN*/ ++#define R0900_P1_TNRGAIN 0xf4e8 ++#define TNRGAIN REGx(R0900_P1_TNRGAIN) ++#define F0900_P1_TUN_KDIVEN 0xf4e800c0 ++#define F0900_P1_STB6X00_OCK 0xf4e80030 ++#define F0900_P1_TUN_GAIN 0xf4e8000f ++ ++/*P1_TNRRF1*/ ++#define R0900_P1_TNRRF1 0xf4e9 ++#define TNRRF1 REGx(R0900_P1_TNRRF1) ++#define F0900_P1_TUN_RFFREQ2 0xf4e900ff ++#define TUN_RFFREQ2 FLDx(F0900_P1_TUN_RFFREQ2) ++ ++/*P1_TNRRF0*/ ++#define R0900_P1_TNRRF0 0xf4ea ++#define TNRRF0 REGx(R0900_P1_TNRRF0) ++#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff ++#define TUN_RFFREQ1 FLDx(F0900_P1_TUN_RFFREQ1) ++ ++/*P1_TNRBW*/ ++#define R0900_P1_TNRBW 0xf4eb ++#define TNRBW REGx(R0900_P1_TNRBW) ++#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 ++#define TUN_RFFREQ0 FLDx(F0900_P1_TUN_RFFREQ0) ++#define F0900_P1_TUN_BW 0xf4eb003f ++#define TUN_BW FLDx(F0900_P1_TUN_BW) ++ ++/*P1_TNRADJ*/ ++#define R0900_P1_TNRADJ 0xf4ec ++#define TNRADJ REGx(R0900_P1_TNRADJ) ++#define F0900_P1_STB61X0_CALTIME 0xf4ec0040 ++ ++/*P1_TNRCTL2*/ ++#define R0900_P1_TNRCTL2 0xf4ed ++#define TNRCTL2 REGx(R0900_P1_TNRCTL2) ++#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080 ++#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040 ++#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020 ++#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010 ++#define F0900_P1_STB61X0_CALOFF 0xf4ed0008 ++#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004 ++#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002 ++#define F0900_P1_STB6XX0_SYN 0xf4ed0001 ++ ++/*P1_TNRCFG3*/ ++#define R0900_P1_TNRCFG3 0xf4ee ++#define TNRCFG3 REGx(R0900_P1_TNRCFG3) ++#define F0900_P1_TUN_PLLFREQ 0xf4ee001c ++#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003 ++ ++/*P1_TNRLAUNCH*/ ++#define R0900_P1_TNRLAUNCH 0xf4f0 ++#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH) ++ ++/*P1_TNRLD*/ ++#define R0900_P1_TNRLD 0xf4f0 ++#define TNRLD REGx(R0900_P1_TNRLD) ++#define F0900_P1_TUNLD_VCOING 0xf4f00080 ++#define F0900_P1_TUN_REG1FAIL 0xf4f00040 ++#define F0900_P1_TUN_REG2FAIL 0xf4f00020 ++#define F0900_P1_TUN_REG3FAIL 0xf4f00010 ++#define F0900_P1_TUN_REG4FAIL 0xf4f00008 ++#define F0900_P1_TUN_REG5FAIL 0xf4f00004 ++#define F0900_P1_TUN_BWING 0xf4f00002 ++#define F0900_P1_TUN_LOCKED 0xf4f00001 ++ ++/*P1_TNROBSL*/ ++#define R0900_P1_TNROBSL 0xf4f6 ++#define TNROBSL REGx(R0900_P1_TNROBSL) ++#define F0900_P1_TUN_I2CABORTED 0xf4f60080 ++#define F0900_P1_TUN_LPEN 0xf4f60040 ++#define F0900_P1_TUN_FCCK 0xf4f60020 ++#define F0900_P1_TUN_I2CLOCKED 0xf4f60010 ++#define F0900_P1_TUN_PROGDONE 0xf4f6000c ++#define F0900_P1_TUN_RFRESTE1 0xf4f60003 ++#define TUN_RFRESTE1 FLDx(F0900_P1_TUN_RFRESTE1) ++ ++/*P1_TNRRESTE*/ ++#define R0900_P1_TNRRESTE 0xf4f7 ++#define TNRRESTE REGx(R0900_P1_TNRRESTE) ++#define F0900_P1_TUN_RFRESTE0 0xf4f700ff ++#define TUN_RFRESTE0 FLDx(F0900_P1_TUN_RFRESTE0) ++ ++/*P1_SMAPCOEF7*/ ++#define R0900_P1_SMAPCOEF7 0xf500 ++#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7) ++#define F0900_P1_DIS_QSCALE 0xf5000080 ++#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f ++ ++/*P1_SMAPCOEF6*/ ++#define R0900_P1_SMAPCOEF6 0xf501 ++#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6) ++#define F0900_P1_ADJ_8PSKLLR1 0xf5010004 ++#define F0900_P1_OLD_8PSKLLR1 0xf5010002 ++#define F0900_P1_DIS_AB8PSK 0xf5010001 ++ ++/*P1_SMAPCOEF5*/ ++#define R0900_P1_SMAPCOEF5 0xf502 ++#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5) ++#define F0900_P1_DIS_8SCALE 0xf5020080 ++#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f ++ ++/*P1_NCO2MAX1*/ ++#define R0900_P1_NCO2MAX1 0xf514 ++#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1) ++#define F0900_P1_TETA2_MAXVABS1 0xf51400ff ++ ++/*P1_NCO2MAX0*/ ++#define R0900_P1_NCO2MAX0 0xf515 ++#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0) ++#define F0900_P1_TETA2_MAXVABS0 0xf51500ff ++ ++/*P1_NCO2FR1*/ ++#define R0900_P1_NCO2FR1 0xf516 ++#define NCO2FR1 REGx(R0900_P1_NCO2FR1) ++#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff ++ ++/*P1_NCO2FR0*/ ++#define R0900_P1_NCO2FR0 0xf517 ++#define NCO2FR0 REGx(R0900_P1_NCO2FR0) ++#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff ++ ++/*P1_CFR2AVRGE1*/ ++#define R0900_P1_CFR2AVRGE1 0xf518 ++#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1) ++#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff ++ ++/*P1_CFR2AVRGE0*/ ++#define R0900_P1_CFR2AVRGE0 0xf519 ++#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0) ++#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff ++ ++/*P1_DMDPLHSTAT*/ ++#define R0900_P1_DMDPLHSTAT 0xf520 ++#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT) ++#define F0900_P1_PLH_STATISTIC 0xf52000ff ++ ++/*P1_LOCKTIME3*/ ++#define R0900_P1_LOCKTIME3 0xf522 ++#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3) ++#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff ++ ++/*P1_LOCKTIME2*/ ++#define R0900_P1_LOCKTIME2 0xf523 ++#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2) ++#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff ++ ++/*P1_LOCKTIME1*/ ++#define R0900_P1_LOCKTIME1 0xf524 ++#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1) ++#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff ++ ++/*P1_LOCKTIME0*/ ++#define R0900_P1_LOCKTIME0 0xf525 ++#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0) ++#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff ++ ++/*P1_VITSCALE*/ ++#define R0900_P1_VITSCALE 0xf532 ++#define VITSCALE REGx(R0900_P1_VITSCALE) ++#define F0900_P1_NVTH_NOSRANGE 0xf5320080 ++#define F0900_P1_VERROR_MAXMODE 0xf5320040 ++#define F0900_P1_NSLOWSN_LOCKED 0xf5320008 ++#define F0900_P1_DIS_RSFLOCK 0xf5320002 ++ ++/*P1_FECM*/ ++#define R0900_P1_FECM 0xf533 ++#define FECM REGx(R0900_P1_FECM) ++#define F0900_P1_DSS_DVB 0xf5330080 ++#define DSS_DVB FLDx(F0900_P1_DSS_DVB) ++#define F0900_P1_DSS_SRCH 0xf5330010 ++#define F0900_P1_SYNCVIT 0xf5330002 ++#define F0900_P1_IQINV 0xf5330001 ++#define IQINV FLDx(F0900_P1_IQINV) ++ ++/*P1_VTH12*/ ++#define R0900_P1_VTH12 0xf534 ++#define VTH12 REGx(R0900_P1_VTH12) ++#define F0900_P1_VTH12 0xf53400ff ++ ++/*P1_VTH23*/ ++#define R0900_P1_VTH23 0xf535 ++#define VTH23 REGx(R0900_P1_VTH23) ++#define F0900_P1_VTH23 0xf53500ff ++ ++/*P1_VTH34*/ ++#define R0900_P1_VTH34 0xf536 ++#define VTH34 REGx(R0900_P1_VTH34) ++#define F0900_P1_VTH34 0xf53600ff ++ ++/*P1_VTH56*/ ++#define R0900_P1_VTH56 0xf537 ++#define VTH56 REGx(R0900_P1_VTH56) ++#define F0900_P1_VTH56 0xf53700ff ++ ++/*P1_VTH67*/ ++#define R0900_P1_VTH67 0xf538 ++#define VTH67 REGx(R0900_P1_VTH67) ++#define F0900_P1_VTH67 0xf53800ff ++ ++/*P1_VTH78*/ ++#define R0900_P1_VTH78 0xf539 ++#define VTH78 REGx(R0900_P1_VTH78) ++#define F0900_P1_VTH78 0xf53900ff ++ ++/*P1_VITCURPUN*/ ++#define R0900_P1_VITCURPUN 0xf53a ++#define VITCURPUN REGx(R0900_P1_VITCURPUN) ++#define F0900_P1_VIT_CURPUN 0xf53a001f ++#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN) ++ ++/*P1_VERROR*/ ++#define R0900_P1_VERROR 0xf53b ++#define VERROR REGx(R0900_P1_VERROR) ++#define F0900_P1_REGERR_VIT 0xf53b00ff ++ ++/*P1_PRVIT*/ ++#define R0900_P1_PRVIT 0xf53c ++#define PRVIT REGx(R0900_P1_PRVIT) ++#define F0900_P1_DIS_VTHLOCK 0xf53c0040 ++#define F0900_P1_E7_8VIT 0xf53c0020 ++#define F0900_P1_E6_7VIT 0xf53c0010 ++#define F0900_P1_E5_6VIT 0xf53c0008 ++#define F0900_P1_E3_4VIT 0xf53c0004 ++#define F0900_P1_E2_3VIT 0xf53c0002 ++#define F0900_P1_E1_2VIT 0xf53c0001 ++ ++/*P1_VAVSRVIT*/ ++#define R0900_P1_VAVSRVIT 0xf53d ++#define VAVSRVIT REGx(R0900_P1_VAVSRVIT) ++#define F0900_P1_AMVIT 0xf53d0080 ++#define F0900_P1_FROZENVIT 0xf53d0040 ++#define F0900_P1_SNVIT 0xf53d0030 ++#define F0900_P1_TOVVIT 0xf53d000c ++#define F0900_P1_HYPVIT 0xf53d0003 ++ ++/*P1_VSTATUSVIT*/ ++#define R0900_P1_VSTATUSVIT 0xf53e ++#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT) ++#define F0900_P1_PRFVIT 0xf53e0010 ++#define PRFVIT FLDx(F0900_P1_PRFVIT) ++#define F0900_P1_LOCKEDVIT 0xf53e0008 ++#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT) ++ ++/*P1_VTHINUSE*/ ++#define R0900_P1_VTHINUSE 0xf53f ++#define VTHINUSE REGx(R0900_P1_VTHINUSE) ++#define F0900_P1_VIT_INUSE 0xf53f00ff ++ ++/*P1_KDIV12*/ ++#define R0900_P1_KDIV12 0xf540 ++#define KDIV12 REGx(R0900_P1_KDIV12) ++#define F0900_P1_K_DIVIDER_12 0xf540007f ++ ++/*P1_KDIV23*/ ++#define R0900_P1_KDIV23 0xf541 ++#define KDIV23 REGx(R0900_P1_KDIV23) ++#define F0900_P1_K_DIVIDER_23 0xf541007f ++ ++/*P1_KDIV34*/ ++#define R0900_P1_KDIV34 0xf542 ++#define KDIV34 REGx(R0900_P1_KDIV34) ++#define F0900_P1_K_DIVIDER_34 0xf542007f ++ ++/*P1_KDIV56*/ ++#define R0900_P1_KDIV56 0xf543 ++#define KDIV56 REGx(R0900_P1_KDIV56) ++#define F0900_P1_K_DIVIDER_56 0xf543007f ++ ++/*P1_KDIV67*/ ++#define R0900_P1_KDIV67 0xf544 ++#define KDIV67 REGx(R0900_P1_KDIV67) ++#define F0900_P1_K_DIVIDER_67 0xf544007f ++ ++/*P1_KDIV78*/ ++#define R0900_P1_KDIV78 0xf545 ++#define KDIV78 REGx(R0900_P1_KDIV78) ++#define F0900_P1_K_DIVIDER_78 0xf545007f ++ ++/*P1_PDELCTRL1*/ ++#define R0900_P1_PDELCTRL1 0xf550 ++#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) ++#define F0900_P1_INV_MISMASK 0xf5500080 ++#define INV_MISMASK FLDx(F0900_P1_INV_MISMASK) ++#define F0900_P1_FILTER_EN 0xf5500020 ++#define FILTER_EN FLDx(F0900_P1_FILTER_EN) ++#define F0900_P1_EN_MIS00 0xf5500002 ++#define EN_MIS00 FLDx(F0900_P1_EN_MIS00) ++#define F0900_P1_ALGOSWRST 0xf5500001 ++#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) ++ ++/*P1_PDELCTRL2*/ ++#define R0900_P1_PDELCTRL2 0xf551 ++#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2) ++#define F0900_P1_RESET_UPKO_COUNT 0xf5510040 ++#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT) ++#define F0900_P1_FRAME_MODE 0xf5510002 ++#define F0900_P1_NOBCHERRFLG_USE 0xf5510001 ++ ++/*P1_HYSTTHRESH*/ ++#define R0900_P1_HYSTTHRESH 0xf554 ++#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH) ++#define F0900_P1_UNLCK_THRESH 0xf55400f0 ++#define F0900_P1_DELIN_LCK_THRESH 0xf554000f ++ ++/*P1_ISIENTRY*/ ++#define R0900_P1_ISIENTRY 0xf55e ++#define ISIENTRY REGx(R0900_P1_ISIENTRY) ++#define F0900_P1_ISI_ENTRY 0xf55e00ff ++ ++/*P1_ISIBITENA*/ ++#define R0900_P1_ISIBITENA 0xf55f ++#define ISIBITENA REGx(R0900_P1_ISIBITENA) ++#define F0900_P1_ISI_BIT_EN 0xf55f00ff ++ ++/*P1_MATSTR1*/ ++#define R0900_P1_MATSTR1 0xf560 ++#define MATSTR1 REGx(R0900_P1_MATSTR1) ++#define F0900_P1_MATYPE_CURRENT1 0xf56000ff ++ ++/*P1_MATSTR0*/ ++#define R0900_P1_MATSTR0 0xf561 ++#define MATSTR0 REGx(R0900_P1_MATSTR0) ++#define F0900_P1_MATYPE_CURRENT0 0xf56100ff ++ ++/*P1_UPLSTR1*/ ++#define R0900_P1_UPLSTR1 0xf562 ++#define UPLSTR1 REGx(R0900_P1_UPLSTR1) ++#define F0900_P1_UPL_CURRENT1 0xf56200ff ++ ++/*P1_UPLSTR0*/ ++#define R0900_P1_UPLSTR0 0xf563 ++#define UPLSTR0 REGx(R0900_P1_UPLSTR0) ++#define F0900_P1_UPL_CURRENT0 0xf56300ff ++ ++/*P1_DFLSTR1*/ ++#define R0900_P1_DFLSTR1 0xf564 ++#define DFLSTR1 REGx(R0900_P1_DFLSTR1) ++#define F0900_P1_DFL_CURRENT1 0xf56400ff ++ ++/*P1_DFLSTR0*/ ++#define R0900_P1_DFLSTR0 0xf565 ++#define DFLSTR0 REGx(R0900_P1_DFLSTR0) ++#define F0900_P1_DFL_CURRENT0 0xf56500ff ++ ++/*P1_SYNCSTR*/ ++#define R0900_P1_SYNCSTR 0xf566 ++#define SYNCSTR REGx(R0900_P1_SYNCSTR) ++#define F0900_P1_SYNC_CURRENT 0xf56600ff ++ ++/*P1_SYNCDSTR1*/ ++#define R0900_P1_SYNCDSTR1 0xf567 ++#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1) ++#define F0900_P1_SYNCD_CURRENT1 0xf56700ff ++ ++/*P1_SYNCDSTR0*/ ++#define R0900_P1_SYNCDSTR0 0xf568 ++#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0) ++#define F0900_P1_SYNCD_CURRENT0 0xf56800ff ++ ++/*P1_PDELSTATUS1*/ ++#define R0900_P1_PDELSTATUS1 0xf569 ++#define F0900_P1_PKTDELIN_DELOCK 0xf5690080 ++#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040 ++#define F0900_P1_CONTINUOUS_STREAM 0xf5690020 ++#define F0900_P1_UNACCEPTED_STREAM 0xf5690010 ++#define F0900_P1_BCH_ERROR_FLAG 0xf5690008 ++#define F0900_P1_PKTDELIN_LOCK 0xf5690002 ++#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK) ++#define F0900_P1_FIRST_LOCK 0xf5690001 ++ ++/*P1_PDELSTATUS2*/ ++#define R0900_P1_PDELSTATUS2 0xf56a ++#define F0900_P1_FRAME_MODCOD 0xf56a007c ++#define F0900_P1_FRAME_TYPE 0xf56a0003 ++ ++/*P1_BBFCRCKO1*/ ++#define R0900_P1_BBFCRCKO1 0xf56b ++#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1) ++#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff ++ ++/*P1_BBFCRCKO0*/ ++#define R0900_P1_BBFCRCKO0 0xf56c ++#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0) ++#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff ++ ++/*P1_UPCRCKO1*/ ++#define R0900_P1_UPCRCKO1 0xf56d ++#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1) ++#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff ++ ++/*P1_UPCRCKO0*/ ++#define R0900_P1_UPCRCKO0 0xf56e ++#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0) ++#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff ++ ++/*P1_PDELCTRL3*/ ++#define R0900_P1_PDELCTRL3 0xf56f ++#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3) ++#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080 ++#define F0900_P1_NOFIFO_BCHERR 0xf56f0020 ++ ++/*P1_TSSTATEM*/ ++#define R0900_P1_TSSTATEM 0xf570 ++#define TSSTATEM REGx(R0900_P1_TSSTATEM) ++#define F0900_P1_TSDIL_ON 0xf5700080 ++#define F0900_P1_TSRS_ON 0xf5700020 ++#define F0900_P1_TSDESCRAMB_ON 0xf5700010 ++#define F0900_P1_TSFRAME_MODE 0xf5700008 ++#define F0900_P1_TS_DISABLE 0xf5700004 ++#define F0900_P1_TSOUT_NOSYNC 0xf5700001 ++ ++/*P1_TSCFGH*/ ++#define R0900_P1_TSCFGH 0xf572 ++#define TSCFGH REGx(R0900_P1_TSCFGH) ++#define F0900_P1_TSFIFO_DVBCI 0xf5720080 ++#define F0900_P1_TSFIFO_SERIAL 0xf5720040 ++#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020 ++#define F0900_P1_TSFIFO_DUTY50 0xf5720010 ++#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008 ++#define F0900_P1_TSFIFO_ERRMODE 0xf5720006 ++#define F0900_P1_RST_HWARE 0xf5720001 ++#define RST_HWARE FLDx(F0900_P1_RST_HWARE) ++ ++/*P1_TSCFGM*/ ++#define R0900_P1_TSCFGM 0xf573 ++#define TSCFGM REGx(R0900_P1_TSCFGM) ++#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0 ++#define F0900_P1_TSFIFO_PERMDATA 0xf5730020 ++#define F0900_P1_TSFIFO_DPUNACT 0xf5730002 ++#define F0900_P1_TSFIFO_INVDATA 0xf5730001 ++ ++/*P1_TSCFGL*/ ++#define R0900_P1_TSCFGL 0xf574 ++#define TSCFGL REGx(R0900_P1_TSCFGL) ++#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0 ++#define F0900_P1_BCHERROR_MODE 0xf5740030 ++#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008 ++#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004 ++#define F0900_P1_TSFIFO_BITSPEED 0xf5740003 ++ ++/*P1_TSINSDELH*/ ++#define R0900_P1_TSINSDELH 0xf576 ++#define TSINSDELH REGx(R0900_P1_TSINSDELH) ++#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080 ++#define F0900_P1_TSDEL_XXHEADER 0xf5760040 ++#define F0900_P1_TSDEL_BBHEADER 0xf5760020 ++#define F0900_P1_TSDEL_DATAFIELD 0xf5760010 ++#define F0900_P1_TSINSDEL_ISCR 0xf5760008 ++#define F0900_P1_TSINSDEL_NPD 0xf5760004 ++#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002 ++#define F0900_P1_TSINSDEL_CRC8 0xf5760001 ++ ++/*P1_TSDIVN*/ ++#define R0900_P1_TSDIVN 0xf579 ++#define TSDIVN REGx(R0900_P1_TSDIVN) ++#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0 ++ ++/*P1_TSCFG4*/ ++#define R0900_P1_TSCFG4 0xf57a ++#define TSCFG4 REGx(R0900_P1_TSCFG4) ++#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0 ++ ++/*P1_TSSPEED*/ ++#define R0900_P1_TSSPEED 0xf580 ++#define TSSPEED REGx(R0900_P1_TSSPEED) ++#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff ++ ++/*P1_TSSTATUS*/ ++#define R0900_P1_TSSTATUS 0xf581 ++#define TSSTATUS REGx(R0900_P1_TSSTATUS) ++#define F0900_P1_TSFIFO_LINEOK 0xf5810080 ++#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK) ++#define F0900_P1_TSFIFO_ERROR 0xf5810040 ++#define F0900_P1_DIL_READY 0xf5810001 ++ ++/*P1_TSSTATUS2*/ ++#define R0900_P1_TSSTATUS2 0xf582 ++#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2) ++#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080 ++#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040 ++#define F0900_P1_DILXX_RESET 0xf5820020 ++#define F0900_P1_TSSERIAL_IMPOS 0xf5820010 ++#define F0900_P1_SCRAMBDETECT 0xf5820002 ++ ++/*P1_TSBITRATE1*/ ++#define R0900_P1_TSBITRATE1 0xf583 ++#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1) ++#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff ++ ++/*P1_TSBITRATE0*/ ++#define R0900_P1_TSBITRATE0 0xf584 ++#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0) ++#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff ++ ++/*P1_ERRCTRL1*/ ++#define R0900_P1_ERRCTRL1 0xf598 ++#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1) ++#define F0900_P1_ERR_SOURCE1 0xf59800f0 ++#define F0900_P1_NUM_EVENT1 0xf5980007 ++ ++/*P1_ERRCNT12*/ ++#define R0900_P1_ERRCNT12 0xf599 ++#define ERRCNT12 REGx(R0900_P1_ERRCNT12) ++#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080 ++#define F0900_P1_ERR_CNT12 0xf599007f ++#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12) ++ ++/*P1_ERRCNT11*/ ++#define R0900_P1_ERRCNT11 0xf59a ++#define ERRCNT11 REGx(R0900_P1_ERRCNT11) ++#define F0900_P1_ERR_CNT11 0xf59a00ff ++#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11) ++ ++/*P1_ERRCNT10*/ ++#define R0900_P1_ERRCNT10 0xf59b ++#define ERRCNT10 REGx(R0900_P1_ERRCNT10) ++#define F0900_P1_ERR_CNT10 0xf59b00ff ++#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10) ++ ++/*P1_ERRCTRL2*/ ++#define R0900_P1_ERRCTRL2 0xf59c ++#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2) ++#define F0900_P1_ERR_SOURCE2 0xf59c00f0 ++#define F0900_P1_NUM_EVENT2 0xf59c0007 ++ ++/*P1_ERRCNT22*/ ++#define R0900_P1_ERRCNT22 0xf59d ++#define ERRCNT22 REGx(R0900_P1_ERRCNT22) ++#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080 ++#define F0900_P1_ERR_CNT22 0xf59d007f ++#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22) ++ ++/*P1_ERRCNT21*/ ++#define R0900_P1_ERRCNT21 0xf59e ++#define ERRCNT21 REGx(R0900_P1_ERRCNT21) ++#define F0900_P1_ERR_CNT21 0xf59e00ff ++#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21) ++ ++/*P1_ERRCNT20*/ ++#define R0900_P1_ERRCNT20 0xf59f ++#define ERRCNT20 REGx(R0900_P1_ERRCNT20) ++#define F0900_P1_ERR_CNT20 0xf59f00ff ++#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20) ++ ++/*P1_FECSPY*/ ++#define R0900_P1_FECSPY 0xf5a0 ++#define FECSPY REGx(R0900_P1_FECSPY) ++#define F0900_P1_SPY_ENABLE 0xf5a00080 ++#define F0900_P1_NO_SYNCBYTE 0xf5a00040 ++#define F0900_P1_SERIAL_MODE 0xf5a00020 ++#define F0900_P1_UNUSUAL_PACKET 0xf5a00010 ++#define F0900_P1_BERMETER_DATAMODE 0xf5a00008 ++#define F0900_P1_BERMETER_LMODE 0xf5a00002 ++#define F0900_P1_BERMETER_RESET 0xf5a00001 ++ ++/*P1_FSPYCFG*/ ++#define R0900_P1_FSPYCFG 0xf5a1 ++#define FSPYCFG REGx(R0900_P1_FSPYCFG) ++#define F0900_P1_FECSPY_INPUT 0xf5a100c0 ++#define F0900_P1_RST_ON_ERROR 0xf5a10020 ++#define F0900_P1_ONE_SHOT 0xf5a10010 ++#define F0900_P1_I2C_MODE 0xf5a1000c ++#define F0900_P1_SPY_HYSTERESIS 0xf5a10003 ++ ++/*P1_FSPYDATA*/ ++#define R0900_P1_FSPYDATA 0xf5a2 ++#define FSPYDATA REGx(R0900_P1_FSPYDATA) ++#define F0900_P1_SPY_STUFFING 0xf5a20080 ++#define F0900_P1_SPY_CNULLPKT 0xf5a20020 ++#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f ++ ++/*P1_FSPYOUT*/ ++#define R0900_P1_FSPYOUT 0xf5a3 ++#define FSPYOUT REGx(R0900_P1_FSPYOUT) ++#define F0900_P1_FSPY_DIRECT 0xf5a30080 ++#define F0900_P1_STUFF_MODE 0xf5a30007 ++ ++/*P1_FSTATUS*/ ++#define R0900_P1_FSTATUS 0xf5a4 ++#define FSTATUS REGx(R0900_P1_FSTATUS) ++#define F0900_P1_SPY_ENDSIM 0xf5a40080 ++#define F0900_P1_VALID_SIM 0xf5a40040 ++#define F0900_P1_FOUND_SIGNAL 0xf5a40020 ++#define F0900_P1_DSS_SYNCBYTE 0xf5a40010 ++#define F0900_P1_RESULT_STATE 0xf5a4000f ++ ++/*P1_FBERCPT4*/ ++#define R0900_P1_FBERCPT4 0xf5a8 ++#define FBERCPT4 REGx(R0900_P1_FBERCPT4) ++#define F0900_P1_FBERMETER_CPT4 0xf5a800ff ++ ++/*P1_FBERCPT3*/ ++#define R0900_P1_FBERCPT3 0xf5a9 ++#define FBERCPT3 REGx(R0900_P1_FBERCPT3) ++#define F0900_P1_FBERMETER_CPT3 0xf5a900ff ++ ++/*P1_FBERCPT2*/ ++#define R0900_P1_FBERCPT2 0xf5aa ++#define FBERCPT2 REGx(R0900_P1_FBERCPT2) ++#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff ++ ++/*P1_FBERCPT1*/ ++#define R0900_P1_FBERCPT1 0xf5ab ++#define FBERCPT1 REGx(R0900_P1_FBERCPT1) ++#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff ++ ++/*P1_FBERCPT0*/ ++#define R0900_P1_FBERCPT0 0xf5ac ++#define FBERCPT0 REGx(R0900_P1_FBERCPT0) ++#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff ++ ++/*P1_FBERERR2*/ ++#define R0900_P1_FBERERR2 0xf5ad ++#define FBERERR2 REGx(R0900_P1_FBERERR2) ++#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff ++ ++/*P1_FBERERR1*/ ++#define R0900_P1_FBERERR1 0xf5ae ++#define FBERERR1 REGx(R0900_P1_FBERERR1) ++#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff ++ ++/*P1_FBERERR0*/ ++#define R0900_P1_FBERERR0 0xf5af ++#define FBERERR0 REGx(R0900_P1_FBERERR0) ++#define F0900_P1_FBERMETER_ERR0 0xf5af00ff ++ ++/*P1_FSPYBER*/ ++#define R0900_P1_FSPYBER 0xf5b2 ++#define FSPYBER REGx(R0900_P1_FSPYBER) ++#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010 ++#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008 ++#define F0900_P1_FSPYBER_CTIME 0xf5b20007 ++ ++/*RCCFG2*/ ++#define R0900_RCCFG2 0xf600 ++ ++/*TSGENERAL*/ ++#define R0900_TSGENERAL 0xf630 ++#define F0900_TSFIFO_DISTS2PAR 0xf6300040 ++#define F0900_MUXSTREAM_OUTMODE 0xf6300008 ++#define F0900_TSFIFO_PERMPARAL 0xf6300006 ++ ++/*TSGENERAL1X*/ ++#define R0900_TSGENERAL1X 0xf670 ++ ++/*NBITER_NF4*/ ++#define R0900_NBITER_NF4 0xfa03 ++#define F0900_NBITER_NF_QP_1_2 0xfa0300ff ++ ++/*NBITER_NF5*/ ++#define R0900_NBITER_NF5 0xfa04 ++#define F0900_NBITER_NF_QP_3_5 0xfa0400ff ++ ++/*NBITER_NF6*/ ++#define R0900_NBITER_NF6 0xfa05 ++#define F0900_NBITER_NF_QP_2_3 0xfa0500ff ++ ++/*NBITER_NF7*/ ++#define R0900_NBITER_NF7 0xfa06 ++#define F0900_NBITER_NF_QP_3_4 0xfa0600ff ++ ++/*NBITER_NF8*/ ++#define R0900_NBITER_NF8 0xfa07 ++#define F0900_NBITER_NF_QP_4_5 0xfa0700ff ++ ++/*NBITER_NF9*/ ++#define R0900_NBITER_NF9 0xfa08 ++#define F0900_NBITER_NF_QP_5_6 0xfa0800ff ++ ++/*NBITER_NF10*/ ++#define R0900_NBITER_NF10 0xfa09 ++#define F0900_NBITER_NF_QP_8_9 0xfa0900ff ++ ++/*NBITER_NF11*/ ++#define R0900_NBITER_NF11 0xfa0a ++#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff ++ ++/*NBITER_NF12*/ ++#define R0900_NBITER_NF12 0xfa0b ++#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff ++ ++/*NBITER_NF13*/ ++#define R0900_NBITER_NF13 0xfa0c ++#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff ++ ++/*NBITER_NF14*/ ++#define R0900_NBITER_NF14 0xfa0d ++#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff ++ ++/*NBITER_NF15*/ ++#define R0900_NBITER_NF15 0xfa0e ++#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff ++ ++/*NBITER_NF16*/ ++#define R0900_NBITER_NF16 0xfa0f ++#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff ++ ++/*NBITER_NF17*/ ++#define R0900_NBITER_NF17 0xfa10 ++#define F0900_NBITER_NF_8P_9_10 0xfa1000ff ++ ++/*NBITERNOERR*/ ++#define R0900_NBITERNOERR 0xfa3f ++#define F0900_NBITER_STOP_CRIT 0xfa3f000f ++ ++/*GAINLLR_NF4*/ ++#define R0900_GAINLLR_NF4 0xfa43 ++#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f ++ ++/*GAINLLR_NF5*/ ++#define R0900_GAINLLR_NF5 0xfa44 ++#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f ++ ++/*GAINLLR_NF6*/ ++#define R0900_GAINLLR_NF6 0xfa45 ++#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f ++ ++/*GAINLLR_NF7*/ ++#define R0900_GAINLLR_NF7 0xfa46 ++#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f ++ ++/*GAINLLR_NF8*/ ++#define R0900_GAINLLR_NF8 0xfa47 ++#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f ++ ++/*GAINLLR_NF9*/ ++#define R0900_GAINLLR_NF9 0xfa48 ++#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f ++ ++/*GAINLLR_NF10*/ ++#define R0900_GAINLLR_NF10 0xfa49 ++#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f ++ ++/*GAINLLR_NF11*/ ++#define R0900_GAINLLR_NF11 0xfa4a ++#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f ++ ++/*GAINLLR_NF12*/ ++#define R0900_GAINLLR_NF12 0xfa4b ++#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f ++ ++/*GAINLLR_NF13*/ ++#define R0900_GAINLLR_NF13 0xfa4c ++#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f ++ ++/*GAINLLR_NF14*/ ++#define R0900_GAINLLR_NF14 0xfa4d ++#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f ++ ++/*GAINLLR_NF15*/ ++#define R0900_GAINLLR_NF15 0xfa4e ++#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f ++ ++/*GAINLLR_NF16*/ ++#define R0900_GAINLLR_NF16 0xfa4f ++#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f ++ ++/*GAINLLR_NF17*/ ++#define R0900_GAINLLR_NF17 0xfa50 ++#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f ++ ++/*CFGEXT*/ ++#define R0900_CFGEXT 0xfa80 ++#define F0900_STAGMODE 0xfa800080 ++#define F0900_BYPBCH 0xfa800040 ++#define F0900_BYPLDPC 0xfa800020 ++#define F0900_LDPCMODE 0xfa800010 ++#define F0900_INVLLRSIGN 0xfa800008 ++#define F0900_SHORTMULT 0xfa800004 ++#define F0900_EXTERNTX 0xfa800001 ++ ++/*GENCFG*/ ++#define R0900_GENCFG 0xfa86 ++#define F0900_BROADCAST 0xfa860010 ++#define F0900_PRIORITY 0xfa860002 ++#define F0900_DDEMOD 0xfa860001 ++ ++/*LDPCERR1*/ ++#define R0900_LDPCERR1 0xfa96 ++#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff ++ ++/*LDPCERR0*/ ++#define R0900_LDPCERR0 0xfa97 ++#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff ++ ++/*BCHERR*/ ++#define R0900_BCHERR 0xfa98 ++#define F0900_ERRORFLAG 0xfa980010 ++#define F0900_BCH_ERRORS_COUNTER 0xfa98000f ++ ++/*TSTRES0*/ ++#define R0900_TSTRES0 0xff11 ++#define F0900_FRESFEC 0xff110080 ++ ++/*P2_TCTL4*/ ++#define R0900_P2_TCTL4 0xff28 ++#define F0900_P2_PN4_SELECT 0xff280020 ++ ++/*P1_TCTL4*/ ++#define R0900_P1_TCTL4 0xff48 ++#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20) ++#define F0900_P1_PN4_SELECT 0xff480020 ++ ++/*P2_TSTDISRX*/ ++#define R0900_P2_TSTDISRX 0xff65 ++#define F0900_P2_PIN_SELECT1 0xff650008 ++ ++/*P1_TSTDISRX*/ ++#define R0900_P1_TSTDISRX 0xff67 ++#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2) ++#define F0900_P1_PIN_SELECT1 0xff670008 ++#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000) ++ ++#define STV0900_NBREGS 723 ++#define STV0900_NBFIELDS 1420 ++ ++#endif ++ +diff --git a/drivers/media/dvb-frontends/stv0900_sw.c b/drivers/media/dvb-frontends/stv0900_sw.c +new file mode 100644 +index 0000000..4af2078 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv0900_sw.c +@@ -0,0 +1,2032 @@ ++/* ++ * stv0900_sw.c ++ * ++ * Driver for ST STV0900 satellite demodulator IC. ++ * ++ * Copyright (C) ST Microelectronics. ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "stv0900.h" ++#include "stv0900_reg.h" ++#include "stv0900_priv.h" ++ ++s32 shiftx(s32 x, int demod, s32 shift) ++{ ++ if (demod == 1) ++ return x - shift; ++ ++ return x; ++} ++ ++int stv0900_check_signal_presence(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 carr_offset, ++ agc2_integr, ++ max_carrier; ++ ++ int no_signal = FALSE; ++ ++ carr_offset = (stv0900_read_reg(intp, CFR2) << 8) ++ | stv0900_read_reg(intp, CFR1); ++ carr_offset = ge2comp(carr_offset, 16); ++ agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8) ++ | stv0900_read_reg(intp, AGC2I0); ++ max_carrier = intp->srch_range[demod] / 1000; ++ ++ max_carrier += (max_carrier / 10); ++ max_carrier = 65536 * (max_carrier / 2); ++ max_carrier /= intp->mclk / 1000; ++ if (max_carrier > 0x4000) ++ max_carrier = 0x4000; ++ ++ if ((agc2_integr > 0x2000) ++ || (carr_offset > (2 * max_carrier)) ++ || (carr_offset < (-2 * max_carrier))) ++ no_signal = TRUE; ++ ++ return no_signal; ++} ++ ++static void stv0900_get_sw_loop_params(struct stv0900_internal *intp, ++ s32 *frequency_inc, s32 *sw_timeout, ++ s32 *steps, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 timeout, freq_inc, max_steps, srate, max_carrier; ++ ++ enum fe_stv0900_search_standard standard; ++ ++ srate = intp->symbol_rate[demod]; ++ max_carrier = intp->srch_range[demod] / 1000; ++ max_carrier += max_carrier / 10; ++ standard = intp->srch_standard[demod]; ++ ++ max_carrier = 65536 * (max_carrier / 2); ++ max_carrier /= intp->mclk / 1000; ++ ++ if (max_carrier > 0x4000) ++ max_carrier = 0x4000; ++ ++ freq_inc = srate; ++ freq_inc /= intp->mclk >> 10; ++ freq_inc = freq_inc << 6; ++ ++ switch (standard) { ++ case STV0900_SEARCH_DVBS1: ++ case STV0900_SEARCH_DSS: ++ freq_inc *= 3; ++ timeout = 20; ++ break; ++ case STV0900_SEARCH_DVBS2: ++ freq_inc *= 4; ++ timeout = 25; ++ break; ++ case STV0900_AUTO_SEARCH: ++ default: ++ freq_inc *= 3; ++ timeout = 25; ++ break; ++ } ++ ++ freq_inc /= 100; ++ ++ if ((freq_inc > max_carrier) || (freq_inc < 0)) ++ freq_inc = max_carrier / 2; ++ ++ timeout *= 27500; ++ ++ if (srate > 0) ++ timeout /= srate / 1000; ++ ++ if ((timeout > 100) || (timeout < 0)) ++ timeout = 100; ++ ++ max_steps = (max_carrier / freq_inc) + 1; ++ ++ if ((max_steps > 100) || (max_steps < 0)) { ++ max_steps = 100; ++ freq_inc = max_carrier / max_steps; ++ } ++ ++ *frequency_inc = freq_inc; ++ *sw_timeout = timeout; ++ *steps = max_steps; ++ ++} ++ ++static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, ++ s32 FreqIncr, s32 Timeout, int zigzag, ++ s32 MaxStep, enum fe_stv0900_demod_num demod) ++{ ++ int no_signal, ++ lock = FALSE; ++ s32 stepCpt, ++ freqOffset, ++ max_carrier; ++ ++ max_carrier = intp->srch_range[demod] / 1000; ++ max_carrier += (max_carrier / 10); ++ ++ max_carrier = 65536 * (max_carrier / 2); ++ max_carrier /= intp->mclk / 1000; ++ ++ if (max_carrier > 0x4000) ++ max_carrier = 0x4000; ++ ++ if (zigzag == TRUE) ++ freqOffset = 0; ++ else ++ freqOffset = -max_carrier + FreqIncr; ++ ++ stepCpt = 0; ++ ++ do { ++ stv0900_write_reg(intp, DMDISTATE, 0x1c); ++ stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff); ++ stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ stv0900_write_bits(intp, ALGOSWRST, 1); ++ ++ if (intp->chip_id == 0x12) { ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ } ++ ++ if (zigzag == TRUE) { ++ if (freqOffset >= 0) ++ freqOffset = -freqOffset - 2 * FreqIncr; ++ else ++ freqOffset = -freqOffset; ++ } else ++ freqOffset += + 2 * FreqIncr; ++ ++ stepCpt++; ++ lock = stv0900_get_demod_lock(intp, demod, Timeout); ++ no_signal = stv0900_check_signal_presence(intp, demod); ++ ++ } while ((lock == FALSE) ++ && (no_signal == FALSE) ++ && ((freqOffset - FreqIncr) < max_carrier) ++ && ((freqOffset + FreqIncr) > -max_carrier) ++ && (stepCpt < MaxStep)); ++ ++ stv0900_write_bits(intp, ALGOSWRST, 0); ++ ++ return lock; ++} ++ ++static int stv0900_sw_algo(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ int lock = FALSE, ++ no_signal, ++ zigzag; ++ s32 s2fw, ++ fqc_inc, ++ sft_stp_tout, ++ trial_cntr, ++ max_steps; ++ ++ stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout, ++ &max_steps, demod); ++ switch (intp->srch_standard[demod]) { ++ case STV0900_SEARCH_DVBS1: ++ case STV0900_SEARCH_DSS: ++ if (intp->chip_id >= 0x20) ++ stv0900_write_reg(intp, CARFREQ, 0x3b); ++ else ++ stv0900_write_reg(intp, CARFREQ, 0xef); ++ ++ stv0900_write_reg(intp, DMDCFGMD, 0x49); ++ zigzag = FALSE; ++ break; ++ case STV0900_SEARCH_DVBS2: ++ if (intp->chip_id >= 0x20) ++ stv0900_write_reg(intp, CORRELABS, 0x79); ++ else ++ stv0900_write_reg(intp, CORRELABS, 0x68); ++ ++ stv0900_write_reg(intp, DMDCFGMD, 0x89); ++ ++ zigzag = TRUE; ++ break; ++ case STV0900_AUTO_SEARCH: ++ default: ++ if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, CARFREQ, 0x3b); ++ stv0900_write_reg(intp, CORRELABS, 0x79); ++ } else { ++ stv0900_write_reg(intp, CARFREQ, 0xef); ++ stv0900_write_reg(intp, CORRELABS, 0x68); ++ } ++ ++ stv0900_write_reg(intp, DMDCFGMD, 0xc9); ++ zigzag = FALSE; ++ break; ++ } ++ ++ trial_cntr = 0; ++ do { ++ lock = stv0900_search_carr_sw_loop(intp, ++ fqc_inc, ++ sft_stp_tout, ++ zigzag, ++ max_steps, ++ demod); ++ no_signal = stv0900_check_signal_presence(intp, demod); ++ trial_cntr++; ++ if ((lock == TRUE) ++ || (no_signal == TRUE) ++ || (trial_cntr == 2)) { ++ ++ if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, CARFREQ, 0x49); ++ stv0900_write_reg(intp, CORRELABS, 0x9e); ++ } else { ++ stv0900_write_reg(intp, CARFREQ, 0xed); ++ stv0900_write_reg(intp, CORRELABS, 0x88); ++ } ++ ++ if ((stv0900_get_bits(intp, HEADER_MODE) == ++ STV0900_DVBS2_FOUND) && ++ (lock == TRUE)) { ++ msleep(sft_stp_tout); ++ s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT); ++ ++ if (s2fw < 0xd) { ++ msleep(sft_stp_tout); ++ s2fw = stv0900_get_bits(intp, ++ FLYWHEEL_CPT); ++ } ++ ++ if (s2fw < 0xd) { ++ lock = FALSE; ++ ++ if (trial_cntr < 2) { ++ if (intp->chip_id >= 0x20) ++ stv0900_write_reg(intp, ++ CORRELABS, ++ 0x79); ++ else ++ stv0900_write_reg(intp, ++ CORRELABS, ++ 0x68); ++ ++ stv0900_write_reg(intp, ++ DMDCFGMD, ++ 0x89); ++ } ++ } ++ } ++ } ++ ++ } while ((lock == FALSE) ++ && (trial_cntr < 2) ++ && (no_signal == FALSE)); ++ ++ return lock; ++} ++ ++static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp, ++ u32 mclk, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 rem1, rem2, intval1, intval2, srate; ++ ++ srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) + ++ (stv0900_get_bits(intp, SYMB_FREQ2) << 16) + ++ (stv0900_get_bits(intp, SYMB_FREQ1) << 8) + ++ (stv0900_get_bits(intp, SYMB_FREQ0)); ++ dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n", ++ srate, stv0900_get_bits(intp, SYMB_FREQ0), ++ stv0900_get_bits(intp, SYMB_FREQ1), ++ stv0900_get_bits(intp, SYMB_FREQ2), ++ stv0900_get_bits(intp, SYMB_FREQ3)); ++ ++ intval1 = (mclk) >> 16; ++ intval2 = (srate) >> 16; ++ ++ rem1 = (mclk) % 0x10000; ++ rem2 = (srate) % 0x10000; ++ srate = (intval1 * intval2) + ++ ((intval1 * rem2) >> 16) + ++ ((intval2 * rem1) >> 16); ++ ++ return srate; ++} ++ ++static void stv0900_set_symbol_rate(struct stv0900_internal *intp, ++ u32 mclk, u32 srate, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 symb; ++ ++ dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, ++ srate, demod); ++ ++ if (srate > 60000000) { ++ symb = srate << 4; ++ symb /= (mclk >> 12); ++ } else if (srate > 6000000) { ++ symb = srate << 6; ++ symb /= (mclk >> 10); ++ } else { ++ symb = srate << 9; ++ symb /= (mclk >> 7); ++ } ++ ++ stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f); ++ stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff)); ++} ++ ++static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp, ++ u32 mclk, u32 srate, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 symb; ++ ++ srate = 105 * (srate / 100); ++ ++ if (srate > 60000000) { ++ symb = srate << 4; ++ symb /= (mclk >> 12); ++ } else if (srate > 6000000) { ++ symb = srate << 6; ++ symb /= (mclk >> 10); ++ } else { ++ symb = srate << 9; ++ symb /= (mclk >> 7); ++ } ++ ++ if (symb < 0x7fff) { ++ stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f); ++ stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff)); ++ } else { ++ stv0900_write_reg(intp, SFRUP1, 0x7f); ++ stv0900_write_reg(intp, SFRUP1 + 1, 0xff); ++ } ++} ++ ++static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp, ++ u32 mclk, u32 srate, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 symb; ++ ++ srate = 95 * (srate / 100); ++ if (srate > 60000000) { ++ symb = srate << 4; ++ symb /= (mclk >> 12); ++ ++ } else if (srate > 6000000) { ++ symb = srate << 6; ++ symb /= (mclk >> 10); ++ ++ } else { ++ symb = srate << 9; ++ symb /= (mclk >> 7); ++ } ++ ++ stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff); ++ stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff)); ++} ++ ++static s32 stv0900_get_timing_offst(struct stv0900_internal *intp, ++ u32 srate, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 timingoffset; ++ ++ ++ timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) + ++ (stv0900_read_reg(intp, TMGREG2 + 1) << 8) + ++ (stv0900_read_reg(intp, TMGREG2 + 2)); ++ ++ timingoffset = ge2comp(timingoffset, 24); ++ ++ ++ if (timingoffset == 0) ++ timingoffset = 1; ++ ++ timingoffset = ((s32)srate * 10) / ((s32)0x1000000 / timingoffset); ++ timingoffset /= 320; ++ ++ return timingoffset; ++} ++ ++static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 rolloff; ++ ++ if (intp->chip_id == 0x10) { ++ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); ++ rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03; ++ stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff); ++ } else if (intp->chip_id <= 0x20) ++ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0); ++ else /* cut 3.0 */ ++ stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0); ++} ++ ++static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) ++{ ++ u32 rolloff; ++ ++ switch (ro) { ++ case STV0900_20: ++ rolloff = 20; ++ break; ++ case STV0900_25: ++ rolloff = 25; ++ break; ++ case STV0900_35: ++ default: ++ rolloff = 35; ++ break; ++ } ++ ++ return srate + (srate * rolloff) / 100; ++} ++ ++static int stv0900_check_timing_lock(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ int timingLock = FALSE; ++ s32 i, ++ timingcpt = 0; ++ u8 car_freq, ++ tmg_th_high, ++ tmg_th_low; ++ ++ car_freq = stv0900_read_reg(intp, CARFREQ); ++ tmg_th_high = stv0900_read_reg(intp, TMGTHRISE); ++ tmg_th_low = stv0900_read_reg(intp, TMGTHFALL); ++ stv0900_write_reg(intp, TMGTHRISE, 0x20); ++ stv0900_write_reg(intp, TMGTHFALL, 0x0); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ stv0900_write_reg(intp, RTC, 0x80); ++ stv0900_write_reg(intp, RTCS2, 0x40); ++ stv0900_write_reg(intp, CARFREQ, 0x0); ++ stv0900_write_reg(intp, CFRINIT1, 0x0); ++ stv0900_write_reg(intp, CFRINIT0, 0x0); ++ stv0900_write_reg(intp, AGC2REF, 0x65); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ msleep(7); ++ ++ for (i = 0; i < 10; i++) { ++ if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) ++ timingcpt++; ++ ++ msleep(1); ++ } ++ ++ if (timingcpt >= 3) ++ timingLock = TRUE; ++ ++ stv0900_write_reg(intp, AGC2REF, 0x38); ++ stv0900_write_reg(intp, RTC, 0x88); ++ stv0900_write_reg(intp, RTCS2, 0x68); ++ stv0900_write_reg(intp, CARFREQ, car_freq); ++ stv0900_write_reg(intp, TMGTHRISE, tmg_th_high); ++ stv0900_write_reg(intp, TMGTHFALL, tmg_th_low); ++ ++ return timingLock; ++} ++ ++static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, ++ s32 demod_timeout) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ int lock = FALSE, ++ d = demod; ++ s32 srate, ++ search_range, ++ locktimeout, ++ currier_step, ++ nb_steps, ++ current_step, ++ direction, ++ tuner_freq, ++ timeout, ++ freq; ++ ++ srate = intp->symbol_rate[d]; ++ search_range = intp->srch_range[d]; ++ ++ if (srate >= 10000000) ++ locktimeout = demod_timeout / 3; ++ else ++ locktimeout = demod_timeout / 2; ++ ++ lock = stv0900_get_demod_lock(intp, d, locktimeout); ++ ++ if (lock != FALSE) ++ return lock; ++ ++ if (srate >= 10000000) { ++ if (stv0900_check_timing_lock(intp, d) == TRUE) { ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, DMDISTATE, 0x15); ++ lock = stv0900_get_demod_lock(intp, d, demod_timeout); ++ } else ++ lock = FALSE; ++ ++ return lock; ++ } ++ ++ if (intp->chip_id <= 0x20) { ++ if (srate <= 1000000) ++ currier_step = 500; ++ else if (srate <= 4000000) ++ currier_step = 1000; ++ else if (srate <= 7000000) ++ currier_step = 2000; ++ else if (srate <= 10000000) ++ currier_step = 3000; ++ else ++ currier_step = 5000; ++ ++ if (srate >= 2000000) { ++ timeout = (demod_timeout / 3); ++ if (timeout > 1000) ++ timeout = 1000; ++ } else ++ timeout = (demod_timeout / 2); ++ } else { ++ /*cut 3.0 */ ++ currier_step = srate / 4000; ++ timeout = (demod_timeout * 3) / 4; ++ } ++ ++ nb_steps = ((search_range / 1000) / currier_step); ++ ++ if ((nb_steps % 2) != 0) ++ nb_steps += 1; ++ ++ if (nb_steps <= 0) ++ nb_steps = 2; ++ else if (nb_steps > 12) ++ nb_steps = 12; ++ ++ current_step = 1; ++ direction = 1; ++ ++ if (intp->chip_id <= 0x20) { ++ tuner_freq = intp->freq[d]; ++ intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d], ++ intp->rolloff) + intp->symbol_rate[d]; ++ } else ++ tuner_freq = 0; ++ ++ while ((current_step <= nb_steps) && (lock == FALSE)) { ++ if (direction > 0) ++ tuner_freq += (current_step * currier_step); ++ else ++ tuner_freq -= (current_step * currier_step); ++ ++ if (intp->chip_id <= 0x20) { ++ if (intp->tuner_type[d] == 3) ++ stv0900_set_tuner_auto(intp, tuner_freq, ++ intp->bw[d], demod); ++ else ++ stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); ++ ++ stv0900_write_reg(intp, DMDISTATE, 0x1c); ++ stv0900_write_reg(intp, CFRINIT1, 0); ++ stv0900_write_reg(intp, CFRINIT0, 0); ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, DMDISTATE, 0x15); ++ } else { ++ stv0900_write_reg(intp, DMDISTATE, 0x1c); ++ freq = (tuner_freq * 65536) / (intp->mclk / 1000); ++ stv0900_write_bits(intp, CFR_INIT1, MSB(freq)); ++ stv0900_write_bits(intp, CFR_INIT0, LSB(freq)); ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, DMDISTATE, 0x05); ++ } ++ ++ lock = stv0900_get_demod_lock(intp, d, timeout); ++ direction *= -1; ++ current_step++; ++ } ++ ++ return lock; ++} ++ ++static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, ++ s32 srate, ++ enum fe_stv0900_search_algo algo) ++{ ++ switch (algo) { ++ case STV0900_BLIND_SEARCH: ++ if (srate <= 1500000) { ++ (*demod_timeout) = 1500; ++ (*fec_timeout) = 400; ++ } else if (srate <= 5000000) { ++ (*demod_timeout) = 1000; ++ (*fec_timeout) = 300; ++ } else { ++ (*demod_timeout) = 700; ++ (*fec_timeout) = 100; ++ } ++ ++ break; ++ case STV0900_COLD_START: ++ case STV0900_WARM_START: ++ default: ++ if (srate <= 1000000) { ++ (*demod_timeout) = 3000; ++ (*fec_timeout) = 1700; ++ } else if (srate <= 2000000) { ++ (*demod_timeout) = 2500; ++ (*fec_timeout) = 1100; ++ } else if (srate <= 5000000) { ++ (*demod_timeout) = 1000; ++ (*fec_timeout) = 550; ++ } else if (srate <= 10000000) { ++ (*demod_timeout) = 700; ++ (*fec_timeout) = 250; ++ } else if (srate <= 20000000) { ++ (*demod_timeout) = 400; ++ (*fec_timeout) = 130; ++ } else { ++ (*demod_timeout) = 300; ++ (*fec_timeout) = 100; ++ } ++ ++ break; ++ ++ } ++ ++ if (algo == STV0900_WARM_START) ++ (*demod_timeout) /= 2; ++} ++ ++static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ ++ s32 vth_reg = VTH12; ++ ++ dprintk("%s\n", __func__); ++ ++ stv0900_write_reg(intp, vth_reg++, 0xd0); ++ stv0900_write_reg(intp, vth_reg++, 0x7d); ++ stv0900_write_reg(intp, vth_reg++, 0x53); ++ stv0900_write_reg(intp, vth_reg++, 0x2f); ++ stv0900_write_reg(intp, vth_reg++, 0x24); ++ stv0900_write_reg(intp, vth_reg++, 0x1f); ++} ++ ++static void stv0900_set_viterbi_standard(struct stv0900_internal *intp, ++ enum fe_stv0900_search_standard standard, ++ enum fe_stv0900_fec fec, ++ enum fe_stv0900_demod_num demod) ++{ ++ dprintk("%s: ViterbiStandard = ", __func__); ++ ++ switch (standard) { ++ case STV0900_AUTO_SEARCH: ++ dprintk("Auto\n"); ++ stv0900_write_reg(intp, FECM, 0x10); ++ stv0900_write_reg(intp, PRVIT, 0x3f); ++ break; ++ case STV0900_SEARCH_DVBS1: ++ dprintk("DVBS1\n"); ++ stv0900_write_reg(intp, FECM, 0x00); ++ switch (fec) { ++ case STV0900_FEC_UNKNOWN: ++ default: ++ stv0900_write_reg(intp, PRVIT, 0x2f); ++ break; ++ case STV0900_FEC_1_2: ++ stv0900_write_reg(intp, PRVIT, 0x01); ++ break; ++ case STV0900_FEC_2_3: ++ stv0900_write_reg(intp, PRVIT, 0x02); ++ break; ++ case STV0900_FEC_3_4: ++ stv0900_write_reg(intp, PRVIT, 0x04); ++ break; ++ case STV0900_FEC_5_6: ++ stv0900_write_reg(intp, PRVIT, 0x08); ++ break; ++ case STV0900_FEC_7_8: ++ stv0900_write_reg(intp, PRVIT, 0x20); ++ break; ++ } ++ ++ break; ++ case STV0900_SEARCH_DSS: ++ dprintk("DSS\n"); ++ stv0900_write_reg(intp, FECM, 0x80); ++ switch (fec) { ++ case STV0900_FEC_UNKNOWN: ++ default: ++ stv0900_write_reg(intp, PRVIT, 0x13); ++ break; ++ case STV0900_FEC_1_2: ++ stv0900_write_reg(intp, PRVIT, 0x01); ++ break; ++ case STV0900_FEC_2_3: ++ stv0900_write_reg(intp, PRVIT, 0x02); ++ break; ++ case STV0900_FEC_6_7: ++ stv0900_write_reg(intp, PRVIT, 0x10); ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++} ++ ++static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ enum fe_stv0900_fec prate; ++ s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN); ++ ++ switch (rate_fld) { ++ case 13: ++ prate = STV0900_FEC_1_2; ++ break; ++ case 18: ++ prate = STV0900_FEC_2_3; ++ break; ++ case 21: ++ prate = STV0900_FEC_3_4; ++ break; ++ case 24: ++ prate = STV0900_FEC_5_6; ++ break; ++ case 25: ++ prate = STV0900_FEC_6_7; ++ break; ++ case 26: ++ prate = STV0900_FEC_7_8; ++ break; ++ default: ++ prate = STV0900_FEC_UNKNOWN; ++ break; ++ } ++ ++ return prate; ++} ++ ++static void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod, ++ u32 srate) ++{ ++ if (intp->chip_id >= 0x30) { ++ if (srate >= 15000000) { ++ stv0900_write_reg(intp, ACLC, 0x2b); ++ stv0900_write_reg(intp, BCLC, 0x1a); ++ } else if ((srate >= 7000000) && (15000000 > srate)) { ++ stv0900_write_reg(intp, ACLC, 0x0c); ++ stv0900_write_reg(intp, BCLC, 0x1b); ++ } else if (srate < 7000000) { ++ stv0900_write_reg(intp, ACLC, 0x2c); ++ stv0900_write_reg(intp, BCLC, 0x1c); ++ } ++ ++ } else { /*cut 2.0 and 1.x*/ ++ stv0900_write_reg(intp, ACLC, 0x1a); ++ stv0900_write_reg(intp, BCLC, 0x09); ++ } ++ ++} ++ ++static void stv0900_track_optimization(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ s32 srate, ++ pilots, ++ aclc, ++ freq1, ++ freq0, ++ i = 0, ++ timed, ++ timef, ++ blind_tun_sw = 0, ++ modulation; ++ ++ enum fe_stv0900_modcode foundModcod; ++ ++ dprintk("%s\n", __func__); ++ ++ srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); ++ srate += stv0900_get_timing_offst(intp, srate, demod); ++ ++ switch (intp->result[demod].standard) { ++ case STV0900_DVBS1_STANDARD: ++ case STV0900_DSS_STANDARD: ++ dprintk("%s: found DVB-S or DSS\n", __func__); ++ if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) { ++ stv0900_write_bits(intp, DVBS1_ENABLE, 1); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 0); ++ } ++ ++ stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff); ++ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); ++ ++ if (intp->chip_id < 0x30) { ++ stv0900_write_reg(intp, ERRCTRL1, 0x75); ++ break; ++ } ++ ++ if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) { ++ stv0900_write_reg(intp, GAUSSR0, 0x98); ++ stv0900_write_reg(intp, CCIR0, 0x18); ++ } else { ++ stv0900_write_reg(intp, GAUSSR0, 0x18); ++ stv0900_write_reg(intp, CCIR0, 0x18); ++ } ++ ++ stv0900_write_reg(intp, ERRCTRL1, 0x75); ++ break; ++ case STV0900_DVBS2_STANDARD: ++ dprintk("%s: found DVB-S2\n", __func__); ++ stv0900_write_bits(intp, DVBS1_ENABLE, 0); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 1); ++ stv0900_write_reg(intp, ACLC, 0); ++ stv0900_write_reg(intp, BCLC, 0); ++ if (intp->result[demod].frame_len == STV0900_LONG_FRAME) { ++ foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD); ++ pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; ++ aclc = stv0900_get_optim_carr_loop(srate, ++ foundModcod, ++ pilots, ++ intp->chip_id); ++ if (foundModcod <= STV0900_QPSK_910) ++ stv0900_write_reg(intp, ACLC2S2Q, aclc); ++ else if (foundModcod <= STV0900_8PSK_910) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S28, aclc); ++ } ++ ++ if ((intp->demod_mode == STV0900_SINGLE) && ++ (foundModcod > STV0900_8PSK_910)) { ++ if (foundModcod <= STV0900_16APSK_910) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S216A, ++ aclc); ++ } else if (foundModcod <= STV0900_32APSK_910) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S232A, ++ aclc); ++ } ++ } ++ ++ } else { ++ modulation = intp->result[demod].modulation; ++ aclc = stv0900_get_optim_short_carr_loop(srate, ++ modulation, intp->chip_id); ++ if (modulation == STV0900_QPSK) ++ stv0900_write_reg(intp, ACLC2S2Q, aclc); ++ else if (modulation == STV0900_8PSK) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S28, aclc); ++ } else if (modulation == STV0900_16APSK) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S216A, aclc); ++ } else if (modulation == STV0900_32APSK) { ++ stv0900_write_reg(intp, ACLC2S2Q, 0x2a); ++ stv0900_write_reg(intp, ACLC2S232A, aclc); ++ } ++ ++ } ++ ++ if (intp->chip_id <= 0x11) { ++ if (intp->demod_mode != STV0900_SINGLE) ++ stv0900_activate_s2_modcod(intp, demod); ++ ++ } ++ ++ stv0900_write_reg(intp, ERRCTRL1, 0x67); ++ break; ++ case STV0900_UNKNOWN_STANDARD: ++ default: ++ dprintk("%s: found unknown standard\n", __func__); ++ stv0900_write_bits(intp, DVBS1_ENABLE, 1); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 1); ++ break; ++ } ++ ++ freq1 = stv0900_read_reg(intp, CFR2); ++ freq0 = stv0900_read_reg(intp, CFR1); ++ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { ++ stv0900_write_reg(intp, SFRSTEP, 0x00); ++ stv0900_write_bits(intp, SCAN_ENABLE, 0); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ stv0900_write_reg(intp, TMGCFG2, 0xc1); ++ stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); ++ blind_tun_sw = 1; ++ if (intp->result[demod].standard != STV0900_DVBS2_STANDARD) ++ stv0900_set_dvbs1_track_car_loop(intp, demod, srate); ++ ++ } ++ ++ if (intp->chip_id >= 0x20) { ++ if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || ++ (intp->srch_standard[demod] == ++ STV0900_SEARCH_DSS) || ++ (intp->srch_standard[demod] == ++ STV0900_AUTO_SEARCH)) { ++ stv0900_write_reg(intp, VAVSRVIT, 0x0a); ++ stv0900_write_reg(intp, VITSCALE, 0x0); ++ } ++ } ++ ++ if (intp->chip_id < 0x20) ++ stv0900_write_reg(intp, CARHDR, 0x08); ++ ++ if (intp->chip_id == 0x10) ++ stv0900_write_reg(intp, CORRELEXP, 0x0a); ++ ++ stv0900_write_reg(intp, AGC2REF, 0x38); ++ ++ if ((intp->chip_id >= 0x20) || ++ (blind_tun_sw == 1) || ++ (intp->symbol_rate[demod] < 10000000)) { ++ stv0900_write_reg(intp, CFRINIT1, freq1); ++ stv0900_write_reg(intp, CFRINIT0, freq0); ++ intp->bw[demod] = stv0900_carrier_width(srate, ++ intp->rolloff) + 10000000; ++ ++ if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { ++ if (intp->srch_algo[demod] != STV0900_WARM_START) { ++ if (intp->tuner_type[demod] == 3) ++ stv0900_set_tuner_auto(intp, ++ intp->freq[demod], ++ intp->bw[demod], ++ demod); ++ else ++ stv0900_set_bandwidth(fe, ++ intp->bw[demod]); ++ } ++ } ++ ++ if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || ++ (intp->symbol_rate[demod] < 10000000)) ++ msleep(50); ++ else ++ msleep(5); ++ ++ stv0900_get_lock_timeout(&timed, &timef, srate, ++ STV0900_WARM_START); ++ ++ if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) { ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, CFRINIT1, freq1); ++ stv0900_write_reg(intp, CFRINIT0, freq0); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ i = 0; ++ while ((stv0900_get_demod_lock(intp, ++ demod, ++ timed / 2) == FALSE) && ++ (i <= 2)) { ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, CFRINIT1, freq1); ++ stv0900_write_reg(intp, CFRINIT0, freq0); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ i++; ++ } ++ } ++ ++ } ++ ++ if (intp->chip_id >= 0x20) ++ stv0900_write_reg(intp, CARFREQ, 0x49); ++ ++ if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) || ++ (intp->result[demod].standard == STV0900_DSS_STANDARD)) ++ stv0900_set_viterbi_tracq(intp, demod); ++ ++} ++ ++static int stv0900_get_fec_lock(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod, s32 time_out) ++{ ++ s32 timer = 0, lock = 0; ++ ++ enum fe_stv0900_search_state dmd_state; ++ ++ dprintk("%s\n", __func__); ++ ++ dmd_state = stv0900_get_bits(intp, HEADER_MODE); ++ ++ while ((timer < time_out) && (lock == 0)) { ++ switch (dmd_state) { ++ case STV0900_SEARCH: ++ case STV0900_PLH_DETECTED: ++ default: ++ lock = 0; ++ break; ++ case STV0900_DVBS2_FOUND: ++ lock = stv0900_get_bits(intp, PKTDELIN_LOCK); ++ break; ++ case STV0900_DVBS_FOUND: ++ lock = stv0900_get_bits(intp, LOCKEDVIT); ++ break; ++ } ++ ++ if (lock == 0) { ++ msleep(10); ++ timer += 10; ++ } ++ } ++ ++ if (lock) ++ dprintk("%s: DEMOD FEC LOCK OK\n", __func__); ++ else ++ dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__); ++ ++ return lock; ++} ++ ++static int stv0900_wait_for_lock(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod, ++ s32 dmd_timeout, s32 fec_timeout) ++{ ++ ++ s32 timer = 0, lock = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ lock = stv0900_get_demod_lock(intp, demod, dmd_timeout); ++ ++ if (lock) ++ lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout); ++ ++ if (lock) { ++ lock = 0; ++ ++ dprintk("%s: Timer = %d, time_out = %d\n", ++ __func__, timer, fec_timeout); ++ ++ while ((timer < fec_timeout) && (lock == 0)) { ++ lock = stv0900_get_bits(intp, TSFIFO_LINEOK); ++ msleep(1); ++ timer++; ++ } ++ } ++ ++ if (lock) ++ dprintk("%s: DEMOD LOCK OK\n", __func__); ++ else ++ dprintk("%s: DEMOD LOCK FAIL\n", __func__); ++ ++ if (lock) ++ return TRUE; ++ else ++ return FALSE; ++} ++ ++enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, ++ enum fe_stv0900_demod_num demod) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_tracking_standard fnd_standard; ++ ++ int hdr_mode = stv0900_get_bits(intp, HEADER_MODE); ++ ++ switch (hdr_mode) { ++ case 2: ++ fnd_standard = STV0900_DVBS2_STANDARD; ++ break; ++ case 3: ++ if (stv0900_get_bits(intp, DSS_DVB) == 1) ++ fnd_standard = STV0900_DSS_STANDARD; ++ else ++ fnd_standard = STV0900_DVBS1_STANDARD; ++ ++ break; ++ default: ++ fnd_standard = STV0900_UNKNOWN_STANDARD; ++ } ++ ++ dprintk("%s: standard %d\n", __func__, fnd_standard); ++ ++ return fnd_standard; ++} ++ ++static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 derot, ++ rem1, ++ rem2, ++ intval1, ++ intval2; ++ ++ derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) + ++ (stv0900_get_bits(intp, CAR_FREQ1) << 8) + ++ (stv0900_get_bits(intp, CAR_FREQ0)); ++ ++ derot = ge2comp(derot, 24); ++ intval1 = mclk >> 12; ++ intval2 = derot >> 12; ++ rem1 = mclk % 0x1000; ++ rem2 = derot % 0x1000; ++ derot = (intval1 * intval2) + ++ ((intval1 * rem2) >> 12) + ++ ((intval2 * rem1) >> 12); ++ ++ return derot; ++} ++ ++static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ u32 freq = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ ++ if (tuner_ops->get_frequency) { ++ if ((tuner_ops->get_frequency(fe, &freq)) < 0) ++ dprintk("%s: Invalid parameter\n", __func__); ++ else ++ dprintk("%s: Frequency=%d\n", __func__, freq); ++ ++ } ++ ++ return freq; ++} ++ ++static enum ++fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE; ++ struct stv0900_signal_info *result = &intp->result[demod]; ++ s32 offsetFreq, ++ srate_offset; ++ int i = 0, ++ d = demod; ++ ++ u8 timing; ++ ++ msleep(5); ++ if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) { ++ timing = stv0900_read_reg(intp, TMGREG2); ++ i = 0; ++ stv0900_write_reg(intp, SFRSTEP, 0x5c); ++ ++ while ((i <= 50) && (timing != 0) && (timing != 0xff)) { ++ timing = stv0900_read_reg(intp, TMGREG2); ++ msleep(5); ++ i += 5; ++ } ++ } ++ ++ result->standard = stv0900_get_standard(fe, d); ++ if (intp->tuner_type[demod] == 3) ++ result->frequency = stv0900_get_freq_auto(intp, d); ++ else ++ result->frequency = stv0900_get_tuner_freq(fe); ++ ++ offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; ++ result->frequency += offsetFreq; ++ result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); ++ srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d); ++ result->symbol_rate += srate_offset; ++ result->fec = stv0900_get_vit_fec(intp, d); ++ result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD); ++ result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; ++ result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; ++ result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); ++ ++ dprintk("%s: modcode=0x%x \n", __func__, result->modcode); ++ ++ switch (result->standard) { ++ case STV0900_DVBS2_STANDARD: ++ result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); ++ if (result->modcode <= STV0900_QPSK_910) ++ result->modulation = STV0900_QPSK; ++ else if (result->modcode <= STV0900_8PSK_910) ++ result->modulation = STV0900_8PSK; ++ else if (result->modcode <= STV0900_16APSK_910) ++ result->modulation = STV0900_16APSK; ++ else if (result->modcode <= STV0900_32APSK_910) ++ result->modulation = STV0900_32APSK; ++ else ++ result->modulation = STV0900_UNKNOWN; ++ break; ++ case STV0900_DVBS1_STANDARD: ++ case STV0900_DSS_STANDARD: ++ result->spectrum = stv0900_get_bits(intp, IQINV); ++ result->modulation = STV0900_QPSK; ++ break; ++ default: ++ break; ++ } ++ ++ if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || ++ (intp->symbol_rate[d] < 10000000)) { ++ offsetFreq = result->frequency - intp->freq[d]; ++ if (intp->tuner_type[demod] == 3) ++ intp->freq[d] = stv0900_get_freq_auto(intp, d); ++ else ++ intp->freq[d] = stv0900_get_tuner_freq(fe); ++ ++ if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) ++ range = STV0900_RANGEOK; ++ else if (ABS(offsetFreq) <= ++ (stv0900_carrier_width(result->symbol_rate, ++ result->rolloff) / 2000)) ++ range = STV0900_RANGEOK; ++ ++ } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) ++ range = STV0900_RANGEOK; ++ ++ dprintk("%s: range %d\n", __func__, range); ++ ++ return range; ++} ++ ++static enum ++fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ enum fe_stv0900_signal_type signal_type = STV0900_NODATA; ++ ++ s32 srate, ++ demod_timeout, ++ fec_timeout, ++ freq1, ++ freq0; ++ ++ intp->result[demod].locked = FALSE; ++ ++ if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) { ++ srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); ++ srate += stv0900_get_timing_offst(intp, srate, demod); ++ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) ++ stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); ++ ++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, ++ srate, STV0900_WARM_START); ++ freq1 = stv0900_read_reg(intp, CFR2); ++ freq0 = stv0900_read_reg(intp, CFR1); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ stv0900_write_bits(intp, SPECINV_CONTROL, ++ STV0900_IQ_FORCE_SWAPPED); ++ stv0900_write_reg(intp, DMDISTATE, 0x1c); ++ stv0900_write_reg(intp, CFRINIT1, freq1); ++ stv0900_write_reg(intp, CFRINIT0, freq0); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ if (stv0900_wait_for_lock(intp, demod, ++ demod_timeout, fec_timeout) == TRUE) { ++ intp->result[demod].locked = TRUE; ++ signal_type = stv0900_get_signal_params(fe); ++ stv0900_track_optimization(fe); ++ } else { ++ stv0900_write_bits(intp, SPECINV_CONTROL, ++ STV0900_IQ_FORCE_NORMAL); ++ stv0900_write_reg(intp, DMDISTATE, 0x1c); ++ stv0900_write_reg(intp, CFRINIT1, freq1); ++ stv0900_write_reg(intp, CFRINIT0, freq0); ++ stv0900_write_reg(intp, DMDISTATE, 0x18); ++ if (stv0900_wait_for_lock(intp, demod, ++ demod_timeout, fec_timeout) == TRUE) { ++ intp->result[demod].locked = TRUE; ++ signal_type = stv0900_get_signal_params(fe); ++ stv0900_track_optimization(fe); ++ } ++ ++ } ++ ++ } else ++ intp->result[demod].locked = FALSE; ++ ++ return signal_type; ++} ++ ++static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ u32 minagc2level = 0xffff, ++ agc2level, ++ init_freq, freq_step; ++ ++ s32 i, j, nb_steps, direction; ++ ++ dprintk("%s\n", __func__); ++ ++ stv0900_write_reg(intp, AGC2REF, 0x38); ++ stv0900_write_bits(intp, SCAN_ENABLE, 0); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ ++ stv0900_write_bits(intp, AUTO_GUP, 1); ++ stv0900_write_bits(intp, AUTO_GLOW, 1); ++ ++ stv0900_write_reg(intp, DMDT0M, 0x0); ++ ++ stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); ++ nb_steps = -1 + (intp->srch_range[demod] / 1000000); ++ nb_steps /= 2; ++ nb_steps = (2 * nb_steps) + 1; ++ ++ if (nb_steps < 0) ++ nb_steps = 1; ++ ++ direction = 1; ++ ++ freq_step = (1000000 << 8) / (intp->mclk >> 8); ++ ++ init_freq = 0; ++ ++ for (i = 0; i < nb_steps; i++) { ++ if (direction > 0) ++ init_freq = init_freq + (freq_step * i); ++ else ++ init_freq = init_freq - (freq_step * i); ++ ++ direction *= -1; ++ stv0900_write_reg(intp, DMDISTATE, 0x5C); ++ stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff); ++ stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff); ++ stv0900_write_reg(intp, DMDISTATE, 0x58); ++ msleep(10); ++ agc2level = 0; ++ ++ for (j = 0; j < 10; j++) ++ agc2level += (stv0900_read_reg(intp, AGC2I1) << 8) ++ | stv0900_read_reg(intp, AGC2I0); ++ ++ agc2level /= 10; ++ ++ if (agc2level < minagc2level) ++ minagc2level = agc2level; ++ ++ } ++ ++ return (u16)minagc2level; ++} ++ ++static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ int timing_lck = FALSE; ++ s32 i, timingcpt = 0, ++ direction = 1, ++ nb_steps, ++ current_step = 0, ++ tuner_freq; ++ u32 agc2_th, ++ coarse_srate = 0, ++ agc2_integr = 0, ++ currier_step = 1200; ++ ++ if (intp->chip_id >= 0x30) ++ agc2_th = 0x2e00; ++ else ++ agc2_th = 0x1f00; ++ ++ stv0900_write_bits(intp, DEMOD_MODE, 0x1f); ++ stv0900_write_reg(intp, TMGCFG, 0x12); ++ stv0900_write_reg(intp, TMGTHRISE, 0xf0); ++ stv0900_write_reg(intp, TMGTHFALL, 0xe0); ++ stv0900_write_bits(intp, SCAN_ENABLE, 1); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 1); ++ stv0900_write_reg(intp, SFRUP1, 0x83); ++ stv0900_write_reg(intp, SFRUP0, 0xc0); ++ stv0900_write_reg(intp, SFRLOW1, 0x82); ++ stv0900_write_reg(intp, SFRLOW0, 0xa0); ++ stv0900_write_reg(intp, DMDT0M, 0x0); ++ stv0900_write_reg(intp, AGC2REF, 0x50); ++ ++ if (intp->chip_id >= 0x30) { ++ stv0900_write_reg(intp, CARFREQ, 0x99); ++ stv0900_write_reg(intp, SFRSTEP, 0x98); ++ } else if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, CARFREQ, 0x6a); ++ stv0900_write_reg(intp, SFRSTEP, 0x95); ++ } else { ++ stv0900_write_reg(intp, CARFREQ, 0xed); ++ stv0900_write_reg(intp, SFRSTEP, 0x73); ++ } ++ ++ if (intp->symbol_rate[demod] <= 2000000) ++ currier_step = 1000; ++ else if (intp->symbol_rate[demod] <= 5000000) ++ currier_step = 2000; ++ else if (intp->symbol_rate[demod] <= 12000000) ++ currier_step = 3000; ++ else ++ currier_step = 5000; ++ ++ nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step); ++ nb_steps /= 2; ++ nb_steps = (2 * nb_steps) + 1; ++ ++ if (nb_steps < 0) ++ nb_steps = 1; ++ else if (nb_steps > 10) { ++ nb_steps = 11; ++ currier_step = (intp->srch_range[demod] / 1000) / 10; ++ } ++ ++ current_step = 0; ++ direction = 1; ++ ++ tuner_freq = intp->freq[demod]; ++ ++ while ((timing_lck == FALSE) && (current_step < nb_steps)) { ++ stv0900_write_reg(intp, DMDISTATE, 0x5f); ++ stv0900_write_bits(intp, DEMOD_MODE, 0); ++ ++ msleep(50); ++ ++ for (i = 0; i < 10; i++) { ++ if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) ++ timingcpt++; ++ ++ agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) | ++ stv0900_read_reg(intp, AGC2I0); ++ } ++ ++ agc2_integr /= 10; ++ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); ++ current_step++; ++ direction *= -1; ++ ++ dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started." ++ " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", ++ tuner_freq, agc2_integr, coarse_srate, timingcpt); ++ ++ if ((timingcpt >= 5) && ++ (agc2_integr < agc2_th) && ++ (coarse_srate < 55000000) && ++ (coarse_srate > 850000)) ++ timing_lck = TRUE; ++ else if (current_step < nb_steps) { ++ if (direction > 0) ++ tuner_freq += (current_step * currier_step); ++ else ++ tuner_freq -= (current_step * currier_step); ++ ++ if (intp->tuner_type[demod] == 3) ++ stv0900_set_tuner_auto(intp, tuner_freq, ++ intp->bw[demod], demod); ++ else ++ stv0900_set_tuner(fe, tuner_freq, ++ intp->bw[demod]); ++ } ++ } ++ ++ if (timing_lck == FALSE) ++ coarse_srate = 0; ++ else ++ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); ++ ++ return coarse_srate; ++} ++ ++static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ u32 coarse_srate, ++ coarse_freq, ++ symb, ++ symbmax, ++ symbmin, ++ symbcomp; ++ ++ coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); ++ ++ if (coarse_srate > 3000000) { ++ symbmax = 13 * (coarse_srate / 10); ++ symbmax = (symbmax / 1000) * 65536; ++ symbmax /= (intp->mclk / 1000); ++ ++ symbmin = 10 * (coarse_srate / 13); ++ symbmin = (symbmin / 1000)*65536; ++ symbmin /= (intp->mclk / 1000); ++ ++ symb = (coarse_srate / 1000) * 65536; ++ symb /= (intp->mclk / 1000); ++ } else { ++ symbmax = 13 * (coarse_srate / 10); ++ symbmax = (symbmax / 100) * 65536; ++ symbmax /= (intp->mclk / 100); ++ ++ symbmin = 10 * (coarse_srate / 14); ++ symbmin = (symbmin / 100) * 65536; ++ symbmin /= (intp->mclk / 100); ++ ++ symb = (coarse_srate / 100) * 65536; ++ symb /= (intp->mclk / 100); ++ } ++ ++ symbcomp = 13 * (coarse_srate / 10); ++ coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) ++ | stv0900_read_reg(intp, CFR1); ++ ++ if (symbcomp < intp->symbol_rate[demod]) ++ coarse_srate = 0; ++ else { ++ stv0900_write_reg(intp, DMDISTATE, 0x1f); ++ stv0900_write_reg(intp, TMGCFG2, 0xc1); ++ stv0900_write_reg(intp, TMGTHRISE, 0x20); ++ stv0900_write_reg(intp, TMGTHFALL, 0x00); ++ stv0900_write_reg(intp, TMGCFG, 0xd2); ++ stv0900_write_bits(intp, CFR_AUTOSCAN, 0); ++ stv0900_write_reg(intp, AGC2REF, 0x38); ++ ++ if (intp->chip_id >= 0x30) ++ stv0900_write_reg(intp, CARFREQ, 0x79); ++ else if (intp->chip_id >= 0x20) ++ stv0900_write_reg(intp, CARFREQ, 0x49); ++ else ++ stv0900_write_reg(intp, CARFREQ, 0xed); ++ ++ stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f); ++ stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff)); ++ ++ stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f); ++ stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff)); ++ ++ stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff); ++ stv0900_write_reg(intp, SFRINIT0, (symb & 0xff)); ++ ++ stv0900_write_reg(intp, DMDT0M, 0x20); ++ stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff); ++ stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff); ++ stv0900_write_reg(intp, DMDISTATE, 0x15); ++ } ++ ++ return coarse_srate; ++} ++ ++static int stv0900_blind_search_algo(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ u8 k_ref_tmg, ++ k_ref_tmg_max, ++ k_ref_tmg_min; ++ u32 coarse_srate, ++ agc2_th; ++ int lock = FALSE, ++ coarse_fail = FALSE; ++ s32 demod_timeout = 500, ++ fec_timeout = 50, ++ fail_cpt, ++ i, ++ agc2_overflow; ++ u16 agc2_int; ++ u8 dstatus2; ++ ++ dprintk("%s\n", __func__); ++ ++ if (intp->chip_id < 0x20) { ++ k_ref_tmg_max = 233; ++ k_ref_tmg_min = 143; ++ } else { ++ k_ref_tmg_max = 110; ++ k_ref_tmg_min = 10; ++ } ++ ++ if (intp->chip_id <= 0x20) ++ agc2_th = STV0900_BLIND_SEARCH_AGC2_TH; ++ else ++ agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30; ++ ++ agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); ++ ++ dprintk("%s agc2_int=%d agc2_th=%d \n", __func__, agc2_int, agc2_th); ++ if (agc2_int > agc2_th) ++ return FALSE; ++ ++ if (intp->chip_id == 0x10) ++ stv0900_write_reg(intp, CORRELEXP, 0xaa); ++ ++ if (intp->chip_id < 0x20) ++ stv0900_write_reg(intp, CARHDR, 0x55); ++ else ++ stv0900_write_reg(intp, CARHDR, 0x20); ++ ++ if (intp->chip_id <= 0x20) ++ stv0900_write_reg(intp, CARCFG, 0xc4); ++ else ++ stv0900_write_reg(intp, CARCFG, 0x6); ++ ++ stv0900_write_reg(intp, RTCS2, 0x44); ++ ++ if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, EQUALCFG, 0x41); ++ stv0900_write_reg(intp, FFECFG, 0x41); ++ stv0900_write_reg(intp, VITSCALE, 0x82); ++ stv0900_write_reg(intp, VAVSRVIT, 0x0); ++ } ++ ++ k_ref_tmg = k_ref_tmg_max; ++ ++ do { ++ stv0900_write_reg(intp, KREFTMG, k_ref_tmg); ++ if (stv0900_search_srate_coarse(fe) != 0) { ++ coarse_srate = stv0900_search_srate_fine(fe); ++ ++ if (coarse_srate != 0) { ++ stv0900_get_lock_timeout(&demod_timeout, ++ &fec_timeout, ++ coarse_srate, ++ STV0900_BLIND_SEARCH); ++ lock = stv0900_get_demod_lock(intp, ++ demod, ++ demod_timeout); ++ } else ++ lock = FALSE; ++ } else { ++ fail_cpt = 0; ++ agc2_overflow = 0; ++ ++ for (i = 0; i < 10; i++) { ++ agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8) ++ | stv0900_read_reg(intp, AGC2I0); ++ ++ if (agc2_int >= 0xff00) ++ agc2_overflow++; ++ ++ dstatus2 = stv0900_read_reg(intp, DSTATUS2); ++ ++ if (((dstatus2 & 0x1) == 0x1) && ++ ((dstatus2 >> 7) == 1)) ++ fail_cpt++; ++ } ++ ++ if ((fail_cpt > 7) || (agc2_overflow > 7)) ++ coarse_fail = TRUE; ++ ++ lock = FALSE; ++ } ++ k_ref_tmg -= 30; ++ } while ((k_ref_tmg >= k_ref_tmg_min) && ++ (lock == FALSE) && ++ (coarse_fail == FALSE)); ++ ++ return lock; ++} ++ ++static void stv0900_set_viterbi_acq(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ s32 vth_reg = VTH12; ++ ++ dprintk("%s\n", __func__); ++ ++ stv0900_write_reg(intp, vth_reg++, 0x96); ++ stv0900_write_reg(intp, vth_reg++, 0x64); ++ stv0900_write_reg(intp, vth_reg++, 0x36); ++ stv0900_write_reg(intp, vth_reg++, 0x23); ++ stv0900_write_reg(intp, vth_reg++, 0x1e); ++ stv0900_write_reg(intp, vth_reg++, 0x19); ++} ++ ++static void stv0900_set_search_standard(struct stv0900_internal *intp, ++ enum fe_stv0900_demod_num demod) ++{ ++ ++ dprintk("%s\n", __func__); ++ ++ switch (intp->srch_standard[demod]) { ++ case STV0900_SEARCH_DVBS1: ++ dprintk("Search Standard = DVBS1\n"); ++ break; ++ case STV0900_SEARCH_DSS: ++ dprintk("Search Standard = DSS\n"); ++ case STV0900_SEARCH_DVBS2: ++ break; ++ dprintk("Search Standard = DVBS2\n"); ++ case STV0900_AUTO_SEARCH: ++ default: ++ dprintk("Search Standard = AUTO\n"); ++ break; ++ } ++ ++ switch (intp->srch_standard[demod]) { ++ case STV0900_SEARCH_DVBS1: ++ case STV0900_SEARCH_DSS: ++ stv0900_write_bits(intp, DVBS1_ENABLE, 1); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 0); ++ stv0900_write_bits(intp, STOP_CLKVIT, 0); ++ stv0900_set_dvbs1_track_car_loop(intp, ++ demod, ++ intp->symbol_rate[demod]); ++ stv0900_write_reg(intp, CAR2CFG, 0x22); ++ ++ stv0900_set_viterbi_acq(intp, demod); ++ stv0900_set_viterbi_standard(intp, ++ intp->srch_standard[demod], ++ intp->fec[demod], demod); ++ ++ break; ++ case STV0900_SEARCH_DVBS2: ++ stv0900_write_bits(intp, DVBS1_ENABLE, 0); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 1); ++ stv0900_write_bits(intp, STOP_CLKVIT, 1); ++ stv0900_write_reg(intp, ACLC, 0x1a); ++ stv0900_write_reg(intp, BCLC, 0x09); ++ if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ ++ stv0900_write_reg(intp, CAR2CFG, 0x26); ++ else ++ stv0900_write_reg(intp, CAR2CFG, 0x66); ++ ++ if (intp->demod_mode != STV0900_SINGLE) { ++ if (intp->chip_id <= 0x11) ++ stv0900_stop_all_s2_modcod(intp, demod); ++ else ++ stv0900_activate_s2_modcod(intp, demod); ++ ++ } else ++ stv0900_activate_s2_modcod_single(intp, demod); ++ ++ stv0900_set_viterbi_tracq(intp, demod); ++ ++ break; ++ case STV0900_AUTO_SEARCH: ++ default: ++ stv0900_write_bits(intp, DVBS1_ENABLE, 1); ++ stv0900_write_bits(intp, DVBS2_ENABLE, 1); ++ stv0900_write_bits(intp, STOP_CLKVIT, 0); ++ stv0900_write_reg(intp, ACLC, 0x1a); ++ stv0900_write_reg(intp, BCLC, 0x09); ++ stv0900_set_dvbs1_track_car_loop(intp, ++ demod, ++ intp->symbol_rate[demod]); ++ if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ ++ stv0900_write_reg(intp, CAR2CFG, 0x26); ++ else ++ stv0900_write_reg(intp, CAR2CFG, 0x66); ++ ++ if (intp->demod_mode != STV0900_SINGLE) { ++ if (intp->chip_id <= 0x11) ++ stv0900_stop_all_s2_modcod(intp, demod); ++ else ++ stv0900_activate_s2_modcod(intp, demod); ++ ++ } else ++ stv0900_activate_s2_modcod_single(intp, demod); ++ ++ stv0900_set_viterbi_tracq(intp, demod); ++ stv0900_set_viterbi_standard(intp, ++ intp->srch_standard[demod], ++ intp->fec[demod], demod); ++ ++ break; ++ } ++} ++ ++enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) ++{ ++ struct stv0900_state *state = fe->demodulator_priv; ++ struct stv0900_internal *intp = state->internal; ++ enum fe_stv0900_demod_num demod = state->demod; ++ ++ s32 demod_timeout = 500, fec_timeout = 50; ++ s32 aq_power, agc1_power, i; ++ ++ int lock = FALSE, low_sr = FALSE; ++ ++ enum fe_stv0900_signal_type signal_type = STV0900_NOCARRIER; ++ enum fe_stv0900_search_algo algo; ++ int no_signal = FALSE; ++ ++ dprintk("%s\n", __func__); ++ ++ algo = intp->srch_algo[demod]; ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ stv0900_write_reg(intp, DMDISTATE, 0x5c); ++ if (intp->chip_id >= 0x20) { ++ if (intp->symbol_rate[demod] > 5000000) ++ stv0900_write_reg(intp, CORRELABS, 0x9e); ++ else ++ stv0900_write_reg(intp, CORRELABS, 0x82); ++ } else ++ stv0900_write_reg(intp, CORRELABS, 0x88); ++ ++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, ++ intp->symbol_rate[demod], ++ intp->srch_algo[demod]); ++ ++ if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { ++ intp->bw[demod] = 2 * 36000000; ++ ++ stv0900_write_reg(intp, TMGCFG2, 0xc0); ++ stv0900_write_reg(intp, CORRELMANT, 0x70); ++ ++ stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); ++ } else { ++ stv0900_write_reg(intp, DMDT0M, 0x20); ++ stv0900_write_reg(intp, TMGCFG, 0xd2); ++ ++ if (intp->symbol_rate[demod] < 2000000) ++ stv0900_write_reg(intp, CORRELMANT, 0x63); ++ else ++ stv0900_write_reg(intp, CORRELMANT, 0x70); ++ ++ stv0900_write_reg(intp, AGC2REF, 0x38); ++ ++ intp->bw[demod] = ++ stv0900_carrier_width(intp->symbol_rate[demod], ++ intp->rolloff); ++ if (intp->chip_id >= 0x20) { ++ stv0900_write_reg(intp, KREFTMG, 0x5a); ++ ++ if (intp->srch_algo[demod] == STV0900_COLD_START) { ++ intp->bw[demod] += 10000000; ++ intp->bw[demod] *= 15; ++ intp->bw[demod] /= 10; ++ } else if (intp->srch_algo[demod] == STV0900_WARM_START) ++ intp->bw[demod] += 10000000; ++ ++ } else { ++ stv0900_write_reg(intp, KREFTMG, 0xc1); ++ intp->bw[demod] += 10000000; ++ intp->bw[demod] *= 15; ++ intp->bw[demod] /= 10; ++ } ++ ++ stv0900_write_reg(intp, TMGCFG2, 0xc1); ++ ++ stv0900_set_symbol_rate(intp, intp->mclk, ++ intp->symbol_rate[demod], demod); ++ stv0900_set_max_symbol_rate(intp, intp->mclk, ++ intp->symbol_rate[demod], demod); ++ stv0900_set_min_symbol_rate(intp, intp->mclk, ++ intp->symbol_rate[demod], demod); ++ if (intp->symbol_rate[demod] >= 10000000) ++ low_sr = FALSE; ++ else ++ low_sr = TRUE; ++ ++ } ++ ++ if (intp->tuner_type[demod] == 3) ++ stv0900_set_tuner_auto(intp, intp->freq[demod], ++ intp->bw[demod], demod); ++ else ++ stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); ++ ++ agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), ++ stv0900_get_bits(intp, AGCIQ_VALUE0)); ++ ++ aq_power = 0; ++ ++ if (agc1_power == 0) { ++ for (i = 0; i < 5; i++) ++ aq_power += (stv0900_get_bits(intp, POWER_I) + ++ stv0900_get_bits(intp, POWER_Q)) / 2; ++ ++ aq_power /= 5; ++ } ++ ++ if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) { ++ intp->result[demod].locked = FALSE; ++ signal_type = STV0900_NOAGC1; ++ dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__); ++ } else { ++ stv0900_write_bits(intp, SPECINV_CONTROL, ++ intp->srch_iq_inv[demod]); ++ if (intp->chip_id <= 0x20) /*cut 2.0*/ ++ stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); ++ else /*cut 3.0*/ ++ stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1); ++ ++ stv0900_set_search_standard(intp, demod); ++ ++ if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH) ++ stv0900_start_search(intp, demod); ++ } ++ ++ if (signal_type == STV0900_NOAGC1) ++ return signal_type; ++ ++ if (intp->chip_id == 0x12) { ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ msleep(3); ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ } ++ ++ if (algo == STV0900_BLIND_SEARCH) ++ lock = stv0900_blind_search_algo(fe); ++ else if (algo == STV0900_COLD_START) ++ lock = stv0900_get_demod_cold_lock(fe, demod_timeout); ++ else if (algo == STV0900_WARM_START) ++ lock = stv0900_get_demod_lock(intp, demod, demod_timeout); ++ ++ if ((lock == FALSE) && (algo == STV0900_COLD_START)) { ++ if (low_sr == FALSE) { ++ if (stv0900_check_timing_lock(intp, demod) == TRUE) ++ lock = stv0900_sw_algo(intp, demod); ++ } ++ } ++ ++ if (lock == TRUE) ++ signal_type = stv0900_get_signal_params(fe); ++ ++ if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) { ++ stv0900_track_optimization(fe); ++ if (intp->chip_id <= 0x11) { ++ if ((stv0900_get_standard(fe, 0) == ++ STV0900_DVBS1_STANDARD) && ++ (stv0900_get_standard(fe, 1) == ++ STV0900_DVBS1_STANDARD)) { ++ msleep(20); ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ } else { ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ msleep(3); ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ } ++ ++ } else if (intp->chip_id >= 0x20) { ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ msleep(3); ++ stv0900_write_bits(intp, RST_HWARE, 1); ++ stv0900_write_bits(intp, RST_HWARE, 0); ++ } ++ ++ if (stv0900_wait_for_lock(intp, demod, ++ fec_timeout, fec_timeout) == TRUE) { ++ lock = TRUE; ++ intp->result[demod].locked = TRUE; ++ if (intp->result[demod].standard == ++ STV0900_DVBS2_STANDARD) { ++ stv0900_set_dvbs2_rolloff(intp, demod); ++ stv0900_write_bits(intp, RESET_UPKO_COUNT, 1); ++ stv0900_write_bits(intp, RESET_UPKO_COUNT, 0); ++ stv0900_write_reg(intp, ERRCTRL1, 0x67); ++ } else { ++ stv0900_write_reg(intp, ERRCTRL1, 0x75); ++ } ++ ++ stv0900_write_reg(intp, FBERCPT4, 0); ++ stv0900_write_reg(intp, ERRCTRL2, 0xc1); ++ } else { ++ lock = FALSE; ++ signal_type = STV0900_NODATA; ++ no_signal = stv0900_check_signal_presence(intp, demod); ++ ++ intp->result[demod].locked = FALSE; ++ } ++ } ++ ++ if ((signal_type != STV0900_NODATA) || (no_signal != FALSE)) ++ return signal_type; ++ ++ if (intp->chip_id > 0x11) { ++ intp->result[demod].locked = FALSE; ++ return signal_type; ++ } ++ ++ if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) && ++ (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST)) ++ signal_type = stv0900_dvbs1_acq_workaround(fe); ++ ++ return signal_type; ++} ++ +diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c +new file mode 100644 +index 0000000..13caec0 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv090x.c +@@ -0,0 +1,4855 @@ ++/* ++ STV0900/0903 Multistandard Broadcast Frontend driver ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "dvb_frontend.h" ++ ++#include "stv6110x.h" /* for demodulator internal modes */ ++ ++#include "stv090x_reg.h" ++#include "stv090x.h" ++#include "stv090x_priv.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++ ++/* internal params node */ ++struct stv090x_dev { ++ /* pointer for internal params, one for each pair of demods */ ++ struct stv090x_internal *internal; ++ struct stv090x_dev *next_dev; ++}; ++ ++/* first internal params */ ++static struct stv090x_dev *stv090x_first_dev; ++ ++/* find chip by i2c adapter and i2c address */ ++static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap, ++ u8 i2c_addr) ++{ ++ struct stv090x_dev *temp_dev = stv090x_first_dev; ++ ++ /* ++ Search of the last stv0900 chip or ++ find it by i2c adapter and i2c address */ ++ while ((temp_dev != NULL) && ++ ((temp_dev->internal->i2c_adap != i2c_adap) || ++ (temp_dev->internal->i2c_addr != i2c_addr))) { ++ ++ temp_dev = temp_dev->next_dev; ++ } ++ ++ return temp_dev; ++} ++ ++/* deallocating chip */ ++static void remove_dev(struct stv090x_internal *internal) ++{ ++ struct stv090x_dev *prev_dev = stv090x_first_dev; ++ struct stv090x_dev *del_dev = find_dev(internal->i2c_adap, ++ internal->i2c_addr); ++ ++ if (del_dev != NULL) { ++ if (del_dev == stv090x_first_dev) { ++ stv090x_first_dev = del_dev->next_dev; ++ } else { ++ while (prev_dev->next_dev != del_dev) ++ prev_dev = prev_dev->next_dev; ++ ++ prev_dev->next_dev = del_dev->next_dev; ++ } ++ ++ kfree(del_dev); ++ } ++} ++ ++/* allocating new chip */ ++static struct stv090x_dev *append_internal(struct stv090x_internal *internal) ++{ ++ struct stv090x_dev *new_dev; ++ struct stv090x_dev *temp_dev; ++ ++ new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL); ++ if (new_dev != NULL) { ++ new_dev->internal = internal; ++ new_dev->next_dev = NULL; ++ ++ /* append to list */ ++ if (stv090x_first_dev == NULL) { ++ stv090x_first_dev = new_dev; ++ } else { ++ temp_dev = stv090x_first_dev; ++ while (temp_dev->next_dev != NULL) ++ temp_dev = temp_dev->next_dev; ++ ++ temp_dev->next_dev = new_dev; ++ } ++ } ++ ++ return new_dev; ++} ++ ++ ++/* DVBS1 and DSS C/N Lookup table */ ++static const struct stv090x_tab stv090x_s1cn_tab[] = { ++ { 0, 8917 }, /* 0.0dB */ ++ { 5, 8801 }, /* 0.5dB */ ++ { 10, 8667 }, /* 1.0dB */ ++ { 15, 8522 }, /* 1.5dB */ ++ { 20, 8355 }, /* 2.0dB */ ++ { 25, 8175 }, /* 2.5dB */ ++ { 30, 7979 }, /* 3.0dB */ ++ { 35, 7763 }, /* 3.5dB */ ++ { 40, 7530 }, /* 4.0dB */ ++ { 45, 7282 }, /* 4.5dB */ ++ { 50, 7026 }, /* 5.0dB */ ++ { 55, 6781 }, /* 5.5dB */ ++ { 60, 6514 }, /* 6.0dB */ ++ { 65, 6241 }, /* 6.5dB */ ++ { 70, 5965 }, /* 7.0dB */ ++ { 75, 5690 }, /* 7.5dB */ ++ { 80, 5424 }, /* 8.0dB */ ++ { 85, 5161 }, /* 8.5dB */ ++ { 90, 4902 }, /* 9.0dB */ ++ { 95, 4654 }, /* 9.5dB */ ++ { 100, 4417 }, /* 10.0dB */ ++ { 105, 4186 }, /* 10.5dB */ ++ { 110, 3968 }, /* 11.0dB */ ++ { 115, 3757 }, /* 11.5dB */ ++ { 120, 3558 }, /* 12.0dB */ ++ { 125, 3366 }, /* 12.5dB */ ++ { 130, 3185 }, /* 13.0dB */ ++ { 135, 3012 }, /* 13.5dB */ ++ { 140, 2850 }, /* 14.0dB */ ++ { 145, 2698 }, /* 14.5dB */ ++ { 150, 2550 }, /* 15.0dB */ ++ { 160, 2283 }, /* 16.0dB */ ++ { 170, 2042 }, /* 17.0dB */ ++ { 180, 1827 }, /* 18.0dB */ ++ { 190, 1636 }, /* 19.0dB */ ++ { 200, 1466 }, /* 20.0dB */ ++ { 210, 1315 }, /* 21.0dB */ ++ { 220, 1181 }, /* 22.0dB */ ++ { 230, 1064 }, /* 23.0dB */ ++ { 240, 960 }, /* 24.0dB */ ++ { 250, 869 }, /* 25.0dB */ ++ { 260, 792 }, /* 26.0dB */ ++ { 270, 724 }, /* 27.0dB */ ++ { 280, 665 }, /* 28.0dB */ ++ { 290, 616 }, /* 29.0dB */ ++ { 300, 573 }, /* 30.0dB */ ++ { 310, 537 }, /* 31.0dB */ ++ { 320, 507 }, /* 32.0dB */ ++ { 330, 483 }, /* 33.0dB */ ++ { 400, 398 }, /* 40.0dB */ ++ { 450, 381 }, /* 45.0dB */ ++ { 500, 377 } /* 50.0dB */ ++}; ++ ++/* DVBS2 C/N Lookup table */ ++static const struct stv090x_tab stv090x_s2cn_tab[] = { ++ { -30, 13348 }, /* -3.0dB */ ++ { -20, 12640 }, /* -2d.0B */ ++ { -10, 11883 }, /* -1.0dB */ ++ { 0, 11101 }, /* -0.0dB */ ++ { 5, 10718 }, /* 0.5dB */ ++ { 10, 10339 }, /* 1.0dB */ ++ { 15, 9947 }, /* 1.5dB */ ++ { 20, 9552 }, /* 2.0dB */ ++ { 25, 9183 }, /* 2.5dB */ ++ { 30, 8799 }, /* 3.0dB */ ++ { 35, 8422 }, /* 3.5dB */ ++ { 40, 8062 }, /* 4.0dB */ ++ { 45, 7707 }, /* 4.5dB */ ++ { 50, 7353 }, /* 5.0dB */ ++ { 55, 7025 }, /* 5.5dB */ ++ { 60, 6684 }, /* 6.0dB */ ++ { 65, 6331 }, /* 6.5dB */ ++ { 70, 6036 }, /* 7.0dB */ ++ { 75, 5727 }, /* 7.5dB */ ++ { 80, 5437 }, /* 8.0dB */ ++ { 85, 5164 }, /* 8.5dB */ ++ { 90, 4902 }, /* 9.0dB */ ++ { 95, 4653 }, /* 9.5dB */ ++ { 100, 4408 }, /* 10.0dB */ ++ { 105, 4187 }, /* 10.5dB */ ++ { 110, 3961 }, /* 11.0dB */ ++ { 115, 3751 }, /* 11.5dB */ ++ { 120, 3558 }, /* 12.0dB */ ++ { 125, 3368 }, /* 12.5dB */ ++ { 130, 3191 }, /* 13.0dB */ ++ { 135, 3017 }, /* 13.5dB */ ++ { 140, 2862 }, /* 14.0dB */ ++ { 145, 2710 }, /* 14.5dB */ ++ { 150, 2565 }, /* 15.0dB */ ++ { 160, 2300 }, /* 16.0dB */ ++ { 170, 2058 }, /* 17.0dB */ ++ { 180, 1849 }, /* 18.0dB */ ++ { 190, 1663 }, /* 19.0dB */ ++ { 200, 1495 }, /* 20.0dB */ ++ { 210, 1349 }, /* 21.0dB */ ++ { 220, 1222 }, /* 22.0dB */ ++ { 230, 1110 }, /* 23.0dB */ ++ { 240, 1011 }, /* 24.0dB */ ++ { 250, 925 }, /* 25.0dB */ ++ { 260, 853 }, /* 26.0dB */ ++ { 270, 789 }, /* 27.0dB */ ++ { 280, 734 }, /* 28.0dB */ ++ { 290, 690 }, /* 29.0dB */ ++ { 300, 650 }, /* 30.0dB */ ++ { 310, 619 }, /* 31.0dB */ ++ { 320, 593 }, /* 32.0dB */ ++ { 330, 571 }, /* 33.0dB */ ++ { 400, 498 }, /* 40.0dB */ ++ { 450, 484 }, /* 45.0dB */ ++ { 500, 481 } /* 50.0dB */ ++}; ++ ++/* RF level C/N lookup table */ ++static const struct stv090x_tab stv090x_rf_tab[] = { ++ { -5, 0xcaa1 }, /* -5dBm */ ++ { -10, 0xc229 }, /* -10dBm */ ++ { -15, 0xbb08 }, /* -15dBm */ ++ { -20, 0xb4bc }, /* -20dBm */ ++ { -25, 0xad5a }, /* -25dBm */ ++ { -30, 0xa298 }, /* -30dBm */ ++ { -35, 0x98a8 }, /* -35dBm */ ++ { -40, 0x8389 }, /* -40dBm */ ++ { -45, 0x59be }, /* -45dBm */ ++ { -50, 0x3a14 }, /* -50dBm */ ++ { -55, 0x2d11 }, /* -55dBm */ ++ { -60, 0x210d }, /* -60dBm */ ++ { -65, 0xa14f }, /* -65dBm */ ++ { -70, 0x07aa } /* -70dBm */ ++}; ++ ++ ++static struct stv090x_reg stv0900_initval[] = { ++ ++ { STV090x_OUTCFG, 0x00 }, ++ { STV090x_MODECFG, 0xff }, ++ { STV090x_AGCRF1CFG, 0x11 }, ++ { STV090x_AGCRF2CFG, 0x13 }, ++ { STV090x_TSGENERAL1X, 0x14 }, ++ { STV090x_TSTTNR2, 0x21 }, ++ { STV090x_TSTTNR4, 0x21 }, ++ { STV090x_P2_DISTXCTL, 0x22 }, ++ { STV090x_P2_F22TX, 0xc0 }, ++ { STV090x_P2_F22RX, 0xc0 }, ++ { STV090x_P2_DISRXCTL, 0x00 }, ++ { STV090x_P2_DMDCFGMD, 0xF9 }, ++ { STV090x_P2_DEMOD, 0x08 }, ++ { STV090x_P2_DMDCFG3, 0xc4 }, ++ { STV090x_P2_CARFREQ, 0xed }, ++ { STV090x_P2_LDT, 0xd0 }, ++ { STV090x_P2_LDT2, 0xb8 }, ++ { STV090x_P2_TMGCFG, 0xd2 }, ++ { STV090x_P2_TMGTHRISE, 0x20 }, ++ { STV090x_P1_TMGCFG, 0xd2 }, ++ ++ { STV090x_P2_TMGTHFALL, 0x00 }, ++ { STV090x_P2_FECSPY, 0x88 }, ++ { STV090x_P2_FSPYDATA, 0x3a }, ++ { STV090x_P2_FBERCPT4, 0x00 }, ++ { STV090x_P2_FSPYBER, 0x10 }, ++ { STV090x_P2_ERRCTRL1, 0x35 }, ++ { STV090x_P2_ERRCTRL2, 0xc1 }, ++ { STV090x_P2_CFRICFG, 0xf8 }, ++ { STV090x_P2_NOSCFG, 0x1c }, ++ { STV090x_P2_DMDTOM, 0x20 }, ++ { STV090x_P2_CORRELMANT, 0x70 }, ++ { STV090x_P2_CORRELABS, 0x88 }, ++ { STV090x_P2_AGC2O, 0x5b }, ++ { STV090x_P2_AGC2REF, 0x38 }, ++ { STV090x_P2_CARCFG, 0xe4 }, ++ { STV090x_P2_ACLC, 0x1A }, ++ { STV090x_P2_BCLC, 0x09 }, ++ { STV090x_P2_CARHDR, 0x08 }, ++ { STV090x_P2_KREFTMG, 0xc1 }, ++ { STV090x_P2_SFRUPRATIO, 0xf0 }, ++ { STV090x_P2_SFRLOWRATIO, 0x70 }, ++ { STV090x_P2_SFRSTEP, 0x58 }, ++ { STV090x_P2_TMGCFG2, 0x01 }, ++ { STV090x_P2_CAR2CFG, 0x26 }, ++ { STV090x_P2_BCLC2S2Q, 0x86 }, ++ { STV090x_P2_BCLC2S28, 0x86 }, ++ { STV090x_P2_SMAPCOEF7, 0x77 }, ++ { STV090x_P2_SMAPCOEF6, 0x85 }, ++ { STV090x_P2_SMAPCOEF5, 0x77 }, ++ { STV090x_P2_TSCFGL, 0x20 }, ++ { STV090x_P2_DMDCFG2, 0x3b }, ++ { STV090x_P2_MODCODLST0, 0xff }, ++ { STV090x_P2_MODCODLST1, 0xff }, ++ { STV090x_P2_MODCODLST2, 0xff }, ++ { STV090x_P2_MODCODLST3, 0xff }, ++ { STV090x_P2_MODCODLST4, 0xff }, ++ { STV090x_P2_MODCODLST5, 0xff }, ++ { STV090x_P2_MODCODLST6, 0xff }, ++ { STV090x_P2_MODCODLST7, 0xcc }, ++ { STV090x_P2_MODCODLST8, 0xcc }, ++ { STV090x_P2_MODCODLST9, 0xcc }, ++ { STV090x_P2_MODCODLSTA, 0xcc }, ++ { STV090x_P2_MODCODLSTB, 0xcc }, ++ { STV090x_P2_MODCODLSTC, 0xcc }, ++ { STV090x_P2_MODCODLSTD, 0xcc }, ++ { STV090x_P2_MODCODLSTE, 0xcc }, ++ { STV090x_P2_MODCODLSTF, 0xcf }, ++ { STV090x_P1_DISTXCTL, 0x22 }, ++ { STV090x_P1_F22TX, 0xc0 }, ++ { STV090x_P1_F22RX, 0xc0 }, ++ { STV090x_P1_DISRXCTL, 0x00 }, ++ { STV090x_P1_DMDCFGMD, 0xf9 }, ++ { STV090x_P1_DEMOD, 0x08 }, ++ { STV090x_P1_DMDCFG3, 0xc4 }, ++ { STV090x_P1_DMDTOM, 0x20 }, ++ { STV090x_P1_CARFREQ, 0xed }, ++ { STV090x_P1_LDT, 0xd0 }, ++ { STV090x_P1_LDT2, 0xb8 }, ++ { STV090x_P1_TMGCFG, 0xd2 }, ++ { STV090x_P1_TMGTHRISE, 0x20 }, ++ { STV090x_P1_TMGTHFALL, 0x00 }, ++ { STV090x_P1_SFRUPRATIO, 0xf0 }, ++ { STV090x_P1_SFRLOWRATIO, 0x70 }, ++ { STV090x_P1_TSCFGL, 0x20 }, ++ { STV090x_P1_FECSPY, 0x88 }, ++ { STV090x_P1_FSPYDATA, 0x3a }, ++ { STV090x_P1_FBERCPT4, 0x00 }, ++ { STV090x_P1_FSPYBER, 0x10 }, ++ { STV090x_P1_ERRCTRL1, 0x35 }, ++ { STV090x_P1_ERRCTRL2, 0xc1 }, ++ { STV090x_P1_CFRICFG, 0xf8 }, ++ { STV090x_P1_NOSCFG, 0x1c }, ++ { STV090x_P1_CORRELMANT, 0x70 }, ++ { STV090x_P1_CORRELABS, 0x88 }, ++ { STV090x_P1_AGC2O, 0x5b }, ++ { STV090x_P1_AGC2REF, 0x38 }, ++ { STV090x_P1_CARCFG, 0xe4 }, ++ { STV090x_P1_ACLC, 0x1A }, ++ { STV090x_P1_BCLC, 0x09 }, ++ { STV090x_P1_CARHDR, 0x08 }, ++ { STV090x_P1_KREFTMG, 0xc1 }, ++ { STV090x_P1_SFRSTEP, 0x58 }, ++ { STV090x_P1_TMGCFG2, 0x01 }, ++ { STV090x_P1_CAR2CFG, 0x26 }, ++ { STV090x_P1_BCLC2S2Q, 0x86 }, ++ { STV090x_P1_BCLC2S28, 0x86 }, ++ { STV090x_P1_SMAPCOEF7, 0x77 }, ++ { STV090x_P1_SMAPCOEF6, 0x85 }, ++ { STV090x_P1_SMAPCOEF5, 0x77 }, ++ { STV090x_P1_DMDCFG2, 0x3b }, ++ { STV090x_P1_MODCODLST0, 0xff }, ++ { STV090x_P1_MODCODLST1, 0xff }, ++ { STV090x_P1_MODCODLST2, 0xff }, ++ { STV090x_P1_MODCODLST3, 0xff }, ++ { STV090x_P1_MODCODLST4, 0xff }, ++ { STV090x_P1_MODCODLST5, 0xff }, ++ { STV090x_P1_MODCODLST6, 0xff }, ++ { STV090x_P1_MODCODLST7, 0xcc }, ++ { STV090x_P1_MODCODLST8, 0xcc }, ++ { STV090x_P1_MODCODLST9, 0xcc }, ++ { STV090x_P1_MODCODLSTA, 0xcc }, ++ { STV090x_P1_MODCODLSTB, 0xcc }, ++ { STV090x_P1_MODCODLSTC, 0xcc }, ++ { STV090x_P1_MODCODLSTD, 0xcc }, ++ { STV090x_P1_MODCODLSTE, 0xcc }, ++ { STV090x_P1_MODCODLSTF, 0xcf }, ++ { STV090x_GENCFG, 0x1d }, ++ { STV090x_NBITER_NF4, 0x37 }, ++ { STV090x_NBITER_NF5, 0x29 }, ++ { STV090x_NBITER_NF6, 0x37 }, ++ { STV090x_NBITER_NF7, 0x33 }, ++ { STV090x_NBITER_NF8, 0x31 }, ++ { STV090x_NBITER_NF9, 0x2f }, ++ { STV090x_NBITER_NF10, 0x39 }, ++ { STV090x_NBITER_NF11, 0x3a }, ++ { STV090x_NBITER_NF12, 0x29 }, ++ { STV090x_NBITER_NF13, 0x37 }, ++ { STV090x_NBITER_NF14, 0x33 }, ++ { STV090x_NBITER_NF15, 0x2f }, ++ { STV090x_NBITER_NF16, 0x39 }, ++ { STV090x_NBITER_NF17, 0x3a }, ++ { STV090x_NBITERNOERR, 0x04 }, ++ { STV090x_GAINLLR_NF4, 0x0C }, ++ { STV090x_GAINLLR_NF5, 0x0F }, ++ { STV090x_GAINLLR_NF6, 0x11 }, ++ { STV090x_GAINLLR_NF7, 0x14 }, ++ { STV090x_GAINLLR_NF8, 0x17 }, ++ { STV090x_GAINLLR_NF9, 0x19 }, ++ { STV090x_GAINLLR_NF10, 0x20 }, ++ { STV090x_GAINLLR_NF11, 0x21 }, ++ { STV090x_GAINLLR_NF12, 0x0D }, ++ { STV090x_GAINLLR_NF13, 0x0F }, ++ { STV090x_GAINLLR_NF14, 0x13 }, ++ { STV090x_GAINLLR_NF15, 0x1A }, ++ { STV090x_GAINLLR_NF16, 0x1F }, ++ { STV090x_GAINLLR_NF17, 0x21 }, ++ { STV090x_RCCFGH, 0x20 }, ++ { STV090x_P1_FECM, 0x01 }, /* disable DSS modes */ ++ { STV090x_P2_FECM, 0x01 }, /* disable DSS modes */ ++ { STV090x_P1_PRVIT, 0x2F }, /* disable PR 6/7 */ ++ { STV090x_P2_PRVIT, 0x2F }, /* disable PR 6/7 */ ++}; ++ ++static struct stv090x_reg stv0903_initval[] = { ++ { STV090x_OUTCFG, 0x00 }, ++ { STV090x_AGCRF1CFG, 0x11 }, ++ { STV090x_STOPCLK1, 0x48 }, ++ { STV090x_STOPCLK2, 0x14 }, ++ { STV090x_TSTTNR1, 0x27 }, ++ { STV090x_TSTTNR2, 0x21 }, ++ { STV090x_P1_DISTXCTL, 0x22 }, ++ { STV090x_P1_F22TX, 0xc0 }, ++ { STV090x_P1_F22RX, 0xc0 }, ++ { STV090x_P1_DISRXCTL, 0x00 }, ++ { STV090x_P1_DMDCFGMD, 0xF9 }, ++ { STV090x_P1_DEMOD, 0x08 }, ++ { STV090x_P1_DMDCFG3, 0xc4 }, ++ { STV090x_P1_CARFREQ, 0xed }, ++ { STV090x_P1_TNRCFG2, 0x82 }, ++ { STV090x_P1_LDT, 0xd0 }, ++ { STV090x_P1_LDT2, 0xb8 }, ++ { STV090x_P1_TMGCFG, 0xd2 }, ++ { STV090x_P1_TMGTHRISE, 0x20 }, ++ { STV090x_P1_TMGTHFALL, 0x00 }, ++ { STV090x_P1_SFRUPRATIO, 0xf0 }, ++ { STV090x_P1_SFRLOWRATIO, 0x70 }, ++ { STV090x_P1_TSCFGL, 0x20 }, ++ { STV090x_P1_FECSPY, 0x88 }, ++ { STV090x_P1_FSPYDATA, 0x3a }, ++ { STV090x_P1_FBERCPT4, 0x00 }, ++ { STV090x_P1_FSPYBER, 0x10 }, ++ { STV090x_P1_ERRCTRL1, 0x35 }, ++ { STV090x_P1_ERRCTRL2, 0xc1 }, ++ { STV090x_P1_CFRICFG, 0xf8 }, ++ { STV090x_P1_NOSCFG, 0x1c }, ++ { STV090x_P1_DMDTOM, 0x20 }, ++ { STV090x_P1_CORRELMANT, 0x70 }, ++ { STV090x_P1_CORRELABS, 0x88 }, ++ { STV090x_P1_AGC2O, 0x5b }, ++ { STV090x_P1_AGC2REF, 0x38 }, ++ { STV090x_P1_CARCFG, 0xe4 }, ++ { STV090x_P1_ACLC, 0x1A }, ++ { STV090x_P1_BCLC, 0x09 }, ++ { STV090x_P1_CARHDR, 0x08 }, ++ { STV090x_P1_KREFTMG, 0xc1 }, ++ { STV090x_P1_SFRSTEP, 0x58 }, ++ { STV090x_P1_TMGCFG2, 0x01 }, ++ { STV090x_P1_CAR2CFG, 0x26 }, ++ { STV090x_P1_BCLC2S2Q, 0x86 }, ++ { STV090x_P1_BCLC2S28, 0x86 }, ++ { STV090x_P1_SMAPCOEF7, 0x77 }, ++ { STV090x_P1_SMAPCOEF6, 0x85 }, ++ { STV090x_P1_SMAPCOEF5, 0x77 }, ++ { STV090x_P1_DMDCFG2, 0x3b }, ++ { STV090x_P1_MODCODLST0, 0xff }, ++ { STV090x_P1_MODCODLST1, 0xff }, ++ { STV090x_P1_MODCODLST2, 0xff }, ++ { STV090x_P1_MODCODLST3, 0xff }, ++ { STV090x_P1_MODCODLST4, 0xff }, ++ { STV090x_P1_MODCODLST5, 0xff }, ++ { STV090x_P1_MODCODLST6, 0xff }, ++ { STV090x_P1_MODCODLST7, 0xcc }, ++ { STV090x_P1_MODCODLST8, 0xcc }, ++ { STV090x_P1_MODCODLST9, 0xcc }, ++ { STV090x_P1_MODCODLSTA, 0xcc }, ++ { STV090x_P1_MODCODLSTB, 0xcc }, ++ { STV090x_P1_MODCODLSTC, 0xcc }, ++ { STV090x_P1_MODCODLSTD, 0xcc }, ++ { STV090x_P1_MODCODLSTE, 0xcc }, ++ { STV090x_P1_MODCODLSTF, 0xcf }, ++ { STV090x_GENCFG, 0x1c }, ++ { STV090x_NBITER_NF4, 0x37 }, ++ { STV090x_NBITER_NF5, 0x29 }, ++ { STV090x_NBITER_NF6, 0x37 }, ++ { STV090x_NBITER_NF7, 0x33 }, ++ { STV090x_NBITER_NF8, 0x31 }, ++ { STV090x_NBITER_NF9, 0x2f }, ++ { STV090x_NBITER_NF10, 0x39 }, ++ { STV090x_NBITER_NF11, 0x3a }, ++ { STV090x_NBITER_NF12, 0x29 }, ++ { STV090x_NBITER_NF13, 0x37 }, ++ { STV090x_NBITER_NF14, 0x33 }, ++ { STV090x_NBITER_NF15, 0x2f }, ++ { STV090x_NBITER_NF16, 0x39 }, ++ { STV090x_NBITER_NF17, 0x3a }, ++ { STV090x_NBITERNOERR, 0x04 }, ++ { STV090x_GAINLLR_NF4, 0x0C }, ++ { STV090x_GAINLLR_NF5, 0x0F }, ++ { STV090x_GAINLLR_NF6, 0x11 }, ++ { STV090x_GAINLLR_NF7, 0x14 }, ++ { STV090x_GAINLLR_NF8, 0x17 }, ++ { STV090x_GAINLLR_NF9, 0x19 }, ++ { STV090x_GAINLLR_NF10, 0x20 }, ++ { STV090x_GAINLLR_NF11, 0x21 }, ++ { STV090x_GAINLLR_NF12, 0x0D }, ++ { STV090x_GAINLLR_NF13, 0x0F }, ++ { STV090x_GAINLLR_NF14, 0x13 }, ++ { STV090x_GAINLLR_NF15, 0x1A }, ++ { STV090x_GAINLLR_NF16, 0x1F }, ++ { STV090x_GAINLLR_NF17, 0x21 }, ++ { STV090x_RCCFGH, 0x20 }, ++ { STV090x_P1_FECM, 0x01 }, /*disable the DSS mode */ ++ { STV090x_P1_PRVIT, 0x2f } /*disable puncture rate 6/7*/ ++}; ++ ++static struct stv090x_reg stv0900_cut20_val[] = { ++ ++ { STV090x_P2_DMDCFG3, 0xe8 }, ++ { STV090x_P2_DMDCFG4, 0x10 }, ++ { STV090x_P2_CARFREQ, 0x38 }, ++ { STV090x_P2_CARHDR, 0x20 }, ++ { STV090x_P2_KREFTMG, 0x5a }, ++ { STV090x_P2_SMAPCOEF7, 0x06 }, ++ { STV090x_P2_SMAPCOEF6, 0x00 }, ++ { STV090x_P2_SMAPCOEF5, 0x04 }, ++ { STV090x_P2_NOSCFG, 0x0c }, ++ { STV090x_P1_DMDCFG3, 0xe8 }, ++ { STV090x_P1_DMDCFG4, 0x10 }, ++ { STV090x_P1_CARFREQ, 0x38 }, ++ { STV090x_P1_CARHDR, 0x20 }, ++ { STV090x_P1_KREFTMG, 0x5a }, ++ { STV090x_P1_SMAPCOEF7, 0x06 }, ++ { STV090x_P1_SMAPCOEF6, 0x00 }, ++ { STV090x_P1_SMAPCOEF5, 0x04 }, ++ { STV090x_P1_NOSCFG, 0x0c }, ++ { STV090x_GAINLLR_NF4, 0x21 }, ++ { STV090x_GAINLLR_NF5, 0x21 }, ++ { STV090x_GAINLLR_NF6, 0x20 }, ++ { STV090x_GAINLLR_NF7, 0x1F }, ++ { STV090x_GAINLLR_NF8, 0x1E }, ++ { STV090x_GAINLLR_NF9, 0x1E }, ++ { STV090x_GAINLLR_NF10, 0x1D }, ++ { STV090x_GAINLLR_NF11, 0x1B }, ++ { STV090x_GAINLLR_NF12, 0x20 }, ++ { STV090x_GAINLLR_NF13, 0x20 }, ++ { STV090x_GAINLLR_NF14, 0x20 }, ++ { STV090x_GAINLLR_NF15, 0x20 }, ++ { STV090x_GAINLLR_NF16, 0x20 }, ++ { STV090x_GAINLLR_NF17, 0x21 }, ++}; ++ ++static struct stv090x_reg stv0903_cut20_val[] = { ++ { STV090x_P1_DMDCFG3, 0xe8 }, ++ { STV090x_P1_DMDCFG4, 0x10 }, ++ { STV090x_P1_CARFREQ, 0x38 }, ++ { STV090x_P1_CARHDR, 0x20 }, ++ { STV090x_P1_KREFTMG, 0x5a }, ++ { STV090x_P1_SMAPCOEF7, 0x06 }, ++ { STV090x_P1_SMAPCOEF6, 0x00 }, ++ { STV090x_P1_SMAPCOEF5, 0x04 }, ++ { STV090x_P1_NOSCFG, 0x0c }, ++ { STV090x_GAINLLR_NF4, 0x21 }, ++ { STV090x_GAINLLR_NF5, 0x21 }, ++ { STV090x_GAINLLR_NF6, 0x20 }, ++ { STV090x_GAINLLR_NF7, 0x1F }, ++ { STV090x_GAINLLR_NF8, 0x1E }, ++ { STV090x_GAINLLR_NF9, 0x1E }, ++ { STV090x_GAINLLR_NF10, 0x1D }, ++ { STV090x_GAINLLR_NF11, 0x1B }, ++ { STV090x_GAINLLR_NF12, 0x20 }, ++ { STV090x_GAINLLR_NF13, 0x20 }, ++ { STV090x_GAINLLR_NF14, 0x20 }, ++ { STV090x_GAINLLR_NF15, 0x20 }, ++ { STV090x_GAINLLR_NF16, 0x20 }, ++ { STV090x_GAINLLR_NF17, 0x21 } ++}; ++ ++/* Cut 2.0 Long Frame Tracking CR loop */ ++static struct stv090x_long_frame_crloop stv090x_s2_crl_cut20[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_QPSK_12, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x1e }, ++ { STV090x_QPSK_35, 0x2f, 0x3f, 0x2e, 0x2f, 0x3d, 0x0f, 0x0e, 0x2e, 0x3d, 0x0e }, ++ { STV090x_QPSK_23, 0x2f, 0x3f, 0x2e, 0x2f, 0x0e, 0x0f, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_QPSK_34, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_QPSK_45, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_QPSK_56, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_QPSK_89, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_QPSK_910, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, ++ { STV090x_8PSK_35, 0x3c, 0x3e, 0x1c, 0x2e, 0x0c, 0x1e, 0x2b, 0x2d, 0x1b, 0x1d }, ++ { STV090x_8PSK_23, 0x1d, 0x3e, 0x3c, 0x2e, 0x2c, 0x1e, 0x0c, 0x2d, 0x2b, 0x1d }, ++ { STV090x_8PSK_34, 0x0e, 0x3e, 0x3d, 0x2e, 0x0d, 0x1e, 0x2c, 0x2d, 0x0c, 0x1d }, ++ { STV090x_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, ++ { STV090x_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, ++ { STV090x_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d } ++}; ++ ++/* Cut 3.0 Long Frame Tracking CR loop */ ++static struct stv090x_long_frame_crloop stv090x_s2_crl_cut30[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_QPSK_12, 0x3c, 0x2c, 0x0c, 0x2c, 0x1b, 0x2c, 0x1b, 0x1c, 0x0b, 0x3b }, ++ { STV090x_QPSK_35, 0x0d, 0x0d, 0x0c, 0x0d, 0x1b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, ++ { STV090x_QPSK_23, 0x1d, 0x0d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, ++ { STV090x_QPSK_34, 0x1d, 0x1d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, ++ { STV090x_QPSK_45, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, ++ { STV090x_QPSK_56, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, ++ { STV090x_QPSK_89, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, ++ { STV090x_QPSK_910, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, ++ { STV090x_8PSK_35, 0x39, 0x29, 0x39, 0x19, 0x19, 0x19, 0x19, 0x19, 0x09, 0x19 }, ++ { STV090x_8PSK_23, 0x2a, 0x39, 0x1a, 0x0a, 0x39, 0x0a, 0x29, 0x39, 0x29, 0x0a }, ++ { STV090x_8PSK_34, 0x2b, 0x3a, 0x1b, 0x1b, 0x3a, 0x1b, 0x1a, 0x0b, 0x1a, 0x3a }, ++ { STV090x_8PSK_56, 0x0c, 0x1b, 0x3b, 0x3b, 0x1b, 0x3b, 0x3a, 0x3b, 0x3a, 0x1b }, ++ { STV090x_8PSK_89, 0x0d, 0x3c, 0x2c, 0x2c, 0x2b, 0x0c, 0x0b, 0x3b, 0x0b, 0x1b }, ++ { STV090x_8PSK_910, 0x0d, 0x0d, 0x2c, 0x3c, 0x3b, 0x1c, 0x0b, 0x3b, 0x0b, 0x1b } ++}; ++ ++/* Cut 2.0 Long Frame Tracking CR Loop */ ++static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut20[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_16APSK_23, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x0c }, ++ { STV090x_16APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0c, 0x2d, 0x0c, 0x1d, 0x0c }, ++ { STV090x_16APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, ++ { STV090x_16APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, ++ { STV090x_16APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, ++ { STV090x_16APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, ++ { STV090x_32APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, ++ { STV090x_32APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, ++ { STV090x_32APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, ++ { STV090x_32APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, ++ { STV090x_32APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c } ++}; ++ ++/* Cut 3.0 Long Frame Tracking CR Loop */ ++static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut30[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_16APSK_23, 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x0a, 0x3a, 0x0a, 0x2a, 0x0a }, ++ { STV090x_16APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0a, 0x3b, 0x0a, 0x1b, 0x0a }, ++ { STV090x_16APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, ++ { STV090x_16APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, ++ { STV090x_16APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, ++ { STV090x_16APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, ++ { STV090x_32APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, ++ { STV090x_32APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, ++ { STV090x_32APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, ++ { STV090x_32APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, ++ { STV090x_32APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } ++}; ++ ++static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut20[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_QPSK_14, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x2d, 0x1f, 0x3d, 0x3e }, ++ { STV090x_QPSK_13, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x3d, 0x0f, 0x3d, 0x2e }, ++ { STV090x_QPSK_25, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x2e } ++}; ++ ++static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut30[] = { ++ /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ ++ { STV090x_QPSK_14, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x2a, 0x1c, 0x3a, 0x3b }, ++ { STV090x_QPSK_13, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x3a, 0x0c, 0x3a, 0x2b }, ++ { STV090x_QPSK_25, 0x1c, 0x3c, 0x1b, 0x3c, 0x3a, 0x1c, 0x3a, 0x3b, 0x3a, 0x2b } ++}; ++ ++/* Cut 2.0 Short Frame Tracking CR Loop */ ++static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut20[] = { ++ /* MODCOD 2M 5M 10M 20M 30M */ ++ { STV090x_QPSK, 0x2f, 0x2e, 0x0e, 0x0e, 0x3d }, ++ { STV090x_8PSK, 0x3e, 0x0e, 0x2d, 0x0d, 0x3c }, ++ { STV090x_16APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d }, ++ { STV090x_32APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d } ++}; ++ ++/* Cut 3.0 Short Frame Tracking CR Loop */ ++static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut30[] = { ++ /* MODCOD 2M 5M 10M 20M 30M */ ++ { STV090x_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A }, ++ { STV090x_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 }, ++ { STV090x_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, ++ { STV090x_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A } ++}; ++ ++static inline s32 comp2(s32 __x, s32 __width) ++{ ++ if (__width == 32) ++ return __x; ++ else ++ return (__x >= (1 << (__width - 1))) ? (__x - (1 << __width)) : __x; ++} ++ ++static int stv090x_read_reg(struct stv090x_state *state, unsigned int reg) ++{ ++ const struct stv090x_config *config = state->config; ++ int ret; ++ ++ u8 b0[] = { reg >> 8, reg & 0xff }; ++ u8 buf; ++ ++ struct i2c_msg msg[] = { ++ { .addr = config->address, .flags = 0, .buf = b0, .len = 2 }, ++ { .addr = config->address, .flags = I2C_M_RD, .buf = &buf, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ if (ret != 2) { ++ if (ret != -ERESTARTSYS) ++ dprintk(FE_ERROR, 1, ++ "Read error, Reg=[0x%02x], Status=%d", ++ reg, ret); ++ ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) ++ dprintk(FE_ERROR, 1, "Reg=[0x%02x], data=%02x", ++ reg, buf); ++ ++ return (unsigned int) buf; ++} ++ ++static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8 *data, u32 count) ++{ ++ const struct stv090x_config *config = state->config; ++ int ret; ++ u8 buf[2 + count]; ++ struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count }; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ memcpy(&buf[2], data, count); ++ ++ if (unlikely(*state->verbose >= FE_DEBUGREG)) { ++ int i; ++ ++ printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); ++ for (i = 0; i < count; i++) ++ printk(" %02x", data[i]); ++ printk("\n"); ++ } ++ ++ ret = i2c_transfer(state->i2c, &i2c_msg, 1); ++ if (ret != 1) { ++ if (ret != -ERESTARTSYS) ++ dprintk(FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", ++ reg, data[0], count, ret); ++ return ret < 0 ? ret : -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 data) ++{ ++ return stv090x_write_regs(state, reg, &data, 1); ++} ++ ++static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) ++{ ++ u32 reg; ++ ++ /* ++ * NOTE! A lock is used as a FSM to control the state in which ++ * access is serialized between two tuners on the same demod. ++ * This has nothing to do with a lock to protect a critical section ++ * which may in some other cases be confused with protecting I/O ++ * access to the demodulator gate. ++ * In case of any error, the lock is unlocked and exit within the ++ * relevant operations themselves. ++ */ ++ if (enable) { ++ if (state->config->tuner_i2c_lock) ++ state->config->tuner_i2c_lock(&state->frontend, 1); ++ else ++ mutex_lock(&state->internal->tuner_lock); ++ } ++ ++ reg = STV090x_READ_DEMOD(state, I2CRPT); ++ if (enable) { ++ dprintk(FE_DEBUG, 1, "Enable Gate"); ++ STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0) ++ goto err; ++ ++ } else { ++ dprintk(FE_DEBUG, 1, "Disable Gate"); ++ STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 0); ++ if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0) ++ goto err; ++ } ++ ++ if (!enable) { ++ if (state->config->tuner_i2c_lock) ++ state->config->tuner_i2c_lock(&state->frontend, 0); ++ else ++ mutex_unlock(&state->internal->tuner_lock); ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ if (state->config->tuner_i2c_lock) ++ state->config->tuner_i2c_lock(&state->frontend, 0); ++ else ++ mutex_unlock(&state->internal->tuner_lock); ++ return -1; ++} ++ ++static void stv090x_get_lock_tmg(struct stv090x_state *state) ++{ ++ switch (state->algo) { ++ case STV090x_BLIND_SEARCH: ++ dprintk(FE_DEBUG, 1, "Blind Search"); ++ if (state->srate <= 1500000) { /*10Msps< SR <=15Msps*/ ++ state->DemodTimeout = 1500; ++ state->FecTimeout = 400; ++ } else if (state->srate <= 5000000) { /*10Msps< SR <=15Msps*/ ++ state->DemodTimeout = 1000; ++ state->FecTimeout = 300; ++ } else { /*SR >20Msps*/ ++ state->DemodTimeout = 700; ++ state->FecTimeout = 100; ++ } ++ break; ++ ++ case STV090x_COLD_SEARCH: ++ case STV090x_WARM_SEARCH: ++ default: ++ dprintk(FE_DEBUG, 1, "Normal Search"); ++ if (state->srate <= 1000000) { /*SR <=1Msps*/ ++ state->DemodTimeout = 4500; ++ state->FecTimeout = 1700; ++ } else if (state->srate <= 2000000) { /*1Msps < SR <= 2Msps */ ++ state->DemodTimeout = 2500; ++ state->FecTimeout = 1100; ++ } else if (state->srate <= 5000000) { /*2Msps < SR <= 5Msps */ ++ state->DemodTimeout = 1000; ++ state->FecTimeout = 550; ++ } else if (state->srate <= 10000000) { /*5Msps < SR <= 10Msps */ ++ state->DemodTimeout = 700; ++ state->FecTimeout = 250; ++ } else if (state->srate <= 20000000) { /*10Msps < SR <= 20Msps */ ++ state->DemodTimeout = 400; ++ state->FecTimeout = 130; ++ } else { /*SR >20Msps*/ ++ state->DemodTimeout = 300; ++ state->FecTimeout = 100; ++ } ++ break; ++ } ++ ++ if (state->algo == STV090x_WARM_SEARCH) ++ state->DemodTimeout /= 2; ++} ++ ++static int stv090x_set_srate(struct stv090x_state *state, u32 srate) ++{ ++ u32 sym; ++ ++ if (srate > 60000000) { ++ sym = (srate << 4); /* SR * 2^16 / master_clk */ ++ sym /= (state->internal->mclk >> 12); ++ } else if (srate > 6000000) { ++ sym = (srate << 6); ++ sym /= (state->internal->mclk >> 10); ++ } else { ++ sym = (srate << 9); ++ sym /= (state->internal->mclk >> 7); ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRINIT0, (sym & 0xff)) < 0) /* LSB */ ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate) ++{ ++ u32 sym; ++ ++ srate = 105 * (srate / 100); ++ if (srate > 60000000) { ++ sym = (srate << 4); /* SR * 2^16 / master_clk */ ++ sym /= (state->internal->mclk >> 12); ++ } else if (srate > 6000000) { ++ sym = (srate << 6); ++ sym /= (state->internal->mclk >> 10); ++ } else { ++ sym = (srate << 9); ++ sym /= (state->internal->mclk >> 7); ++ } ++ ++ if (sym < 0x7fff) { ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) /* MSB */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) /* LSB */ ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x7f) < 0) /* MSB */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xff) < 0) /* LSB */ ++ goto err; ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate) ++{ ++ u32 sym; ++ ++ srate = 95 * (srate / 100); ++ if (srate > 60000000) { ++ sym = (srate << 4); /* SR * 2^16 / master_clk */ ++ sym /= (state->internal->mclk >> 12); ++ } else if (srate > 6000000) { ++ sym = (srate << 6); ++ sym /= (state->internal->mclk >> 10); ++ } else { ++ sym = (srate << 9); ++ sym /= (state->internal->mclk >> 7); ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */ ++ goto err; ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static u32 stv090x_car_width(u32 srate, enum stv090x_rolloff rolloff) ++{ ++ u32 ro; ++ ++ switch (rolloff) { ++ case STV090x_RO_20: ++ ro = 20; ++ break; ++ case STV090x_RO_25: ++ ro = 25; ++ break; ++ case STV090x_RO_35: ++ default: ++ ro = 35; ++ break; ++ } ++ ++ return srate + (srate * ro) / 100; ++} ++ ++static int stv090x_set_vit_thacq(struct stv090x_state *state) ++{ ++ if (STV090x_WRITE_DEMOD(state, VTH12, 0x96) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH23, 0x64) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH34, 0x36) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH56, 0x23) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH67, 0x1e) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH78, 0x19) < 0) ++ goto err; ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_vit_thtracq(struct stv090x_state *state) ++{ ++ if (STV090x_WRITE_DEMOD(state, VTH12, 0xd0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH23, 0x7d) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH34, 0x53) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH56, 0x2f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH67, 0x24) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VTH78, 0x1f) < 0) ++ goto err; ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_viterbi(struct stv090x_state *state) ++{ ++ switch (state->search_mode) { ++ case STV090x_SEARCH_AUTO: ++ if (STV090x_WRITE_DEMOD(state, FECM, 0x10) < 0) /* DVB-S and DVB-S2 */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x3f) < 0) /* all puncture rate */ ++ goto err; ++ break; ++ case STV090x_SEARCH_DVBS1: ++ if (STV090x_WRITE_DEMOD(state, FECM, 0x00) < 0) /* disable DSS */ ++ goto err; ++ switch (state->fec) { ++ case STV090x_PR12: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR23: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR34: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x04) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR56: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x08) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR78: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x20) < 0) ++ goto err; ++ break; ++ ++ default: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x2f) < 0) /* all */ ++ goto err; ++ break; ++ } ++ break; ++ case STV090x_SEARCH_DSS: ++ if (STV090x_WRITE_DEMOD(state, FECM, 0x80) < 0) ++ goto err; ++ switch (state->fec) { ++ case STV090x_PR12: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR23: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) ++ goto err; ++ break; ++ ++ case STV090x_PR67: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x10) < 0) ++ goto err; ++ break; ++ ++ default: ++ if (STV090x_WRITE_DEMOD(state, PRVIT, 0x13) < 0) /* 1/2, 2/3, 6/7 */ ++ goto err; ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_stop_modcod(struct stv090x_state *state) ++{ ++ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xff) < 0) ++ goto err; ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_activate_modcod(struct stv090x_state *state) ++{ ++ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xfc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_activate_modcod_single(struct stv090x_state *state) ++{ ++ ++ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xf0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0x0f) < 0) ++ goto err; ++ ++ return 0; ++ ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable) ++{ ++ u32 reg; ++ ++ switch (state->demod) { ++ case STV090x_DEMODULATOR_0: ++ mutex_lock(&state->internal->demod_lock); ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ mutex_unlock(&state->internal->demod_lock); ++ break; ++ ++ case STV090x_DEMODULATOR_1: ++ mutex_lock(&state->internal->demod_lock); ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ mutex_unlock(&state->internal->demod_lock); ++ break; ++ ++ default: ++ dprintk(FE_ERROR, 1, "Wrong demodulator!"); ++ break; ++ } ++ return 0; ++err: ++ mutex_unlock(&state->internal->demod_lock); ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_dvbs_track_crl(struct stv090x_state *state) ++{ ++ if (state->internal->dev_ver >= 0x30) { ++ /* Set ACLC BCLC optimised value vs SR */ ++ if (state->srate >= 15000000) { ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1a) < 0) ++ goto err; ++ } else if ((state->srate >= 7000000) && (15000000 > state->srate)) { ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0x0c) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1b) < 0) ++ goto err; ++ } else if (state->srate < 7000000) { ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0x2c) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0x1c) < 0) ++ goto err; ++ } ++ ++ } else { ++ /* Cut 2.0 */ ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) ++ goto err; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_delivery_search(struct stv090x_state *state) ++{ ++ u32 reg; ++ ++ switch (state->search_mode) { ++ case STV090x_SEARCH_DVBS1: ++ case STV090x_SEARCH_DSS: ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ /* Activate Viterbi decoder in legacy search, ++ * do not use FRESVIT1, might impact VITERBI2 ++ */ ++ if (stv090x_vitclk_ctl(state, 0) < 0) ++ goto err; ++ ++ if (stv090x_dvbs_track_crl(state) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x22) < 0) /* disable DVB-S2 */ ++ goto err; ++ ++ if (stv090x_set_vit_thacq(state) < 0) ++ goto err; ++ if (stv090x_set_viterbi(state) < 0) ++ goto err; ++ break; ++ ++ case STV090x_SEARCH_DVBS2: ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ if (stv090x_vitclk_ctl(state, 1) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) /* stop DVB-S CR loop */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver <= 0x20) { ++ /* enable S2 carrier loop */ ++ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) ++ goto err; ++ } else { ++ /* > Cut 3: Stop carrier 3 */ ++ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) ++ goto err; ++ } ++ ++ if (state->demod_mode != STV090x_SINGLE) { ++ /* Cut 2: enable link during search */ ++ if (stv090x_activate_modcod(state) < 0) ++ goto err; ++ } else { ++ /* Single demodulator ++ * Authorize SHORT and LONG frames, ++ * QPSK, 8PSK, 16APSK and 32APSK ++ */ ++ if (stv090x_activate_modcod_single(state) < 0) ++ goto err; ++ } ++ ++ if (stv090x_set_vit_thtracq(state) < 0) ++ goto err; ++ break; ++ ++ case STV090x_SEARCH_AUTO: ++ default: ++ /* enable DVB-S2 and DVB-S2 in Auto MODE */ ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ if (stv090x_vitclk_ctl(state, 0) < 0) ++ goto err; ++ ++ if (stv090x_dvbs_track_crl(state) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver <= 0x20) { ++ /* enable S2 carrier loop */ ++ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) ++ goto err; ++ } else { ++ /* > Cut 3: Stop carrier 3 */ ++ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) ++ goto err; ++ } ++ ++ if (state->demod_mode != STV090x_SINGLE) { ++ /* Cut 2: enable link during search */ ++ if (stv090x_activate_modcod(state) < 0) ++ goto err; ++ } else { ++ /* Single demodulator ++ * Authorize SHORT and LONG frames, ++ * QPSK, 8PSK, 16APSK and 32APSK ++ */ ++ if (stv090x_activate_modcod_single(state) < 0) ++ goto err; ++ } ++ ++ if (stv090x_set_vit_thacq(state) < 0) ++ goto err; ++ ++ if (stv090x_set_viterbi(state) < 0) ++ goto err; ++ break; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_start_search(struct stv090x_state *state) ++{ ++ u32 reg, freq_abs; ++ s16 freq; ++ ++ /* Reset demodulator */ ++ reg = STV090x_READ_DEMOD(state, DMDISTATE); ++ STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver <= 0x20) { ++ if (state->srate <= 5000000) { ++ if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0) ++ goto err; ++ ++ /*enlarge the timing bandwidth for Low SR*/ ++ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) ++ goto err; ++ } else { ++ /* If the symbol rate is >5 Msps ++ Set The carrier search up and low to auto mode */ ++ if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) ++ goto err; ++ /*reduce the timing bandwidth for high SR*/ ++ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) ++ goto err; ++ } ++ } else { ++ /* >= Cut 3 */ ++ if (state->srate <= 5000000) { ++ /* enlarge the timing bandwidth for Low SR */ ++ STV090x_WRITE_DEMOD(state, RTCS2, 0x68); ++ } else { ++ /* reduce timing bandwidth for high SR */ ++ STV090x_WRITE_DEMOD(state, RTCS2, 0x44); ++ } ++ ++ /* Set CFR min and max to manual mode */ ++ STV090x_WRITE_DEMOD(state, CARCFG, 0x46); ++ ++ if (state->algo == STV090x_WARM_SEARCH) { ++ /* WARM Start ++ * CFR min = -1MHz, ++ * CFR max = +1MHz ++ */ ++ freq_abs = 1000 << 16; ++ freq_abs /= (state->internal->mclk / 1000); ++ freq = (s16) freq_abs; ++ } else { ++ /* COLD Start ++ * CFR min =- (SearchRange / 2 + 600KHz) ++ * CFR max = +(SearchRange / 2 + 600KHz) ++ * (600KHz for the tuner step size) ++ */ ++ freq_abs = (state->search_range / 2000) + 600; ++ freq_abs = freq_abs << 16; ++ freq_abs /= (state->internal->mclk / 1000); ++ freq = (s16) freq_abs; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0) ++ goto err; ++ ++ freq *= -1; ++ ++ if (STV090x_WRITE_DEMOD(state, CFRLOW1, MSB(freq)) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRLOW0, LSB(freq)) < 0) ++ goto err; ++ ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) ++ goto err; ++ ++ if ((state->search_mode == STV090x_SEARCH_DVBS1) || ++ (state->search_mode == STV090x_SEARCH_DSS) || ++ (state->search_mode == STV090x_SEARCH_AUTO)) { ++ ++ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) ++ goto err; ++ } ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xe0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xc0) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ reg = STV090x_READ_DEMOD(state, DMDCFG2); ++ STV090x_SETFIELD_Px(reg, S1S2_SEQUENTIAL_FIELD, 0x0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ /*Frequency offset detector setting*/ ++ if (state->srate < 2000000) { ++ if (state->internal->dev_ver <= 0x20) { ++ /* Cut 2 */ ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) ++ goto err; ++ } else { ++ /* Cut 3 */ ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0) ++ goto err; ++ } ++ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0) ++ goto err; ++ } else if (state->srate < 10000000) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) ++ goto err; ++ } ++ } else { ++ if (state->srate < 10000000) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xef) < 0) ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xed) < 0) ++ goto err; ++ } ++ } ++ ++ switch (state->algo) { ++ case STV090x_WARM_SEARCH: ++ /* The symbol rate and the exact ++ * carrier Frequency are known ++ */ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) ++ goto err; ++ break; ++ ++ case STV090x_COLD_SEARCH: ++ /* The symbol rate is known */ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) ++ goto err; ++ break; ++ ++ default: ++ break; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_get_agc2_min_level(struct stv090x_state *state) ++{ ++ u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg; ++ s32 i, j, steps, dir; ++ ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) ++ goto err; ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) /* SR = 65 Msps Max */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) /* SR= 400 ksps Min */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) /* stop acq @ coarse carrier state */ ++ goto err; ++ if (stv090x_set_srate(state, 1000000) < 0) ++ goto err; ++ ++ steps = state->search_range / 1000000; ++ if (steps <= 0) ++ steps = 1; ++ ++ dir = 1; ++ freq_step = (1000000 * 256) / (state->internal->mclk / 256); ++ freq_init = 0; ++ ++ for (i = 0; i < steps; i++) { ++ if (dir > 0) ++ freq_init = freq_init + (freq_step * i); ++ else ++ freq_init = freq_init - (freq_step * i); ++ ++ dir *= -1; ++ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_init >> 8) & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_init & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */ ++ goto err; ++ msleep(10); ++ ++ agc2 = 0; ++ for (j = 0; j < 10; j++) { ++ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | ++ STV090x_READ_DEMOD(state, AGC2I0); ++ } ++ agc2 /= 10; ++ if (agc2 < agc2_min) ++ agc2_min = agc2; ++ } ++ ++ return agc2_min; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk) ++{ ++ u8 r3, r2, r1, r0; ++ s32 srate, int_1, int_2, tmp_1, tmp_2; ++ ++ r3 = STV090x_READ_DEMOD(state, SFR3); ++ r2 = STV090x_READ_DEMOD(state, SFR2); ++ r1 = STV090x_READ_DEMOD(state, SFR1); ++ r0 = STV090x_READ_DEMOD(state, SFR0); ++ ++ srate = ((r3 << 24) | (r2 << 16) | (r1 << 8) | r0); ++ ++ int_1 = clk >> 16; ++ int_2 = srate >> 16; ++ ++ tmp_1 = clk % 0x10000; ++ tmp_2 = srate % 0x10000; ++ ++ srate = (int_1 * int_2) + ++ ((int_1 * tmp_2) >> 16) + ++ ((int_2 * tmp_1) >> 16); ++ ++ return srate; ++} ++ ++static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) ++{ ++ struct dvb_frontend *fe = &state->frontend; ++ ++ int tmg_lock = 0, i; ++ s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq; ++ u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; ++ u32 agc2th; ++ ++ if (state->internal->dev_ver >= 0x30) ++ agc2th = 0x2e00; ++ else ++ agc2th = 0x1f00; ++ ++ reg = STV090x_READ_DEMOD(state, DMDISTATE); ++ STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0) ++ goto err; ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x30) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) ++ goto err; ++ ++ } else if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) ++ goto err; ++ } ++ ++ if (state->srate <= 2000000) ++ car_step = 1000; ++ else if (state->srate <= 5000000) ++ car_step = 2000; ++ else if (state->srate <= 12000000) ++ car_step = 3000; ++ else ++ car_step = 5000; ++ ++ steps = -1 + ((state->search_range / 1000) / car_step); ++ steps /= 2; ++ steps = (2 * steps) + 1; ++ if (steps < 0) ++ steps = 1; ++ else if (steps > 10) { ++ steps = 11; ++ car_step = (state->search_range / 1000) / 10; ++ } ++ cur_step = 0; ++ dir = 1; ++ freq = state->frequency; ++ ++ while ((!tmg_lock) && (cur_step < steps)) { ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0) ++ goto err; ++ /* trigger acquisition */ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0) ++ goto err; ++ msleep(50); ++ for (i = 0; i < 10; i++) { ++ reg = STV090x_READ_DEMOD(state, DSTATUS); ++ if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) ++ tmg_cpt++; ++ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | ++ STV090x_READ_DEMOD(state, AGC2I0); ++ } ++ agc2 /= 10; ++ srate_coarse = stv090x_get_srate(state, state->internal->mclk); ++ cur_step++; ++ dir *= -1; ++ if ((tmg_cpt >= 5) && (agc2 < agc2th) && ++ (srate_coarse < 50000000) && (srate_coarse > 850000)) ++ tmg_lock = 1; ++ else if (cur_step < steps) { ++ if (dir > 0) ++ freq += cur_step * car_step; ++ else ++ freq -= cur_step * car_step; ++ ++ /* Setup tuner */ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_set_frequency) { ++ if (state->config->tuner_set_frequency(fe, freq) < 0) ++ goto err_gateoff; ++ } ++ ++ if (state->config->tuner_set_bandwidth) { ++ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ msleep(50); ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_get_status) { ++ if (state->config->tuner_get_status(fe, ®) < 0) ++ goto err_gateoff; ++ } ++ ++ if (reg) ++ dprintk(FE_DEBUG, 1, "Tuner phase locked"); ++ else ++ dprintk(FE_DEBUG, 1, "Tuner unlocked"); ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ } ++ } ++ if (!tmg_lock) ++ srate_coarse = 0; ++ else ++ srate_coarse = stv090x_get_srate(state, state->internal->mclk); ++ ++ return srate_coarse; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static u32 stv090x_srate_srch_fine(struct stv090x_state *state) ++{ ++ u32 srate_coarse, freq_coarse, sym, reg; ++ ++ srate_coarse = stv090x_get_srate(state, state->internal->mclk); ++ freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8; ++ freq_coarse |= STV090x_READ_DEMOD(state, CFR1); ++ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ ++ ++ if (sym < state->srate) ++ srate_coarse = 0; ++ else { ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) ++ goto err; ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x30) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) ++ goto err; ++ } else if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) ++ goto err; ++ } ++ ++ if (srate_coarse > 3000000) { ++ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ ++ sym = (sym / 1000) * 65536; ++ sym /= (state->internal->mclk / 1000); ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) ++ goto err; ++ sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */ ++ sym = (sym / 1000) * 65536; ++ sym /= (state->internal->mclk / 1000); ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) ++ goto err; ++ sym = (srate_coarse / 1000) * 65536; ++ sym /= (state->internal->mclk / 1000); ++ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) ++ goto err; ++ } else { ++ sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ ++ sym = (sym / 100) * 65536; ++ sym /= (state->internal->mclk / 100); ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) ++ goto err; ++ sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */ ++ sym = (sym / 100) * 65536; ++ sym /= (state->internal->mclk / 100); ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) ++ goto err; ++ sym = (srate_coarse / 100) * 65536; ++ sym /= (state->internal->mclk / 100); ++ if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) ++ goto err; ++ } ++ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_coarse >> 8) & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_coarse & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) /* trigger acquisition */ ++ goto err; ++ } ++ ++ return srate_coarse; ++ ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout) ++{ ++ s32 timer = 0, lock = 0; ++ u32 reg; ++ u8 stat; ++ ++ while ((timer < timeout) && (!lock)) { ++ reg = STV090x_READ_DEMOD(state, DMDSTATE); ++ stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); ++ ++ switch (stat) { ++ case 0: /* searching */ ++ case 1: /* first PLH detected */ ++ default: ++ dprintk(FE_DEBUG, 1, "Demodulator searching .."); ++ lock = 0; ++ break; ++ case 2: /* DVB-S2 mode */ ++ case 3: /* DVB-S1/legacy mode */ ++ reg = STV090x_READ_DEMOD(state, DSTATUS); ++ lock = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); ++ break; ++ } ++ ++ if (!lock) ++ msleep(10); ++ else ++ dprintk(FE_DEBUG, 1, "Demodulator acquired LOCK"); ++ ++ timer += 10; ++ } ++ return lock; ++} ++ ++static int stv090x_blind_search(struct stv090x_state *state) ++{ ++ u32 agc2, reg, srate_coarse; ++ s32 cpt_fail, agc2_ovflw, i; ++ u8 k_ref, k_max, k_min; ++ int coarse_fail = 0; ++ int lock; ++ ++ k_max = 110; ++ k_min = 10; ++ ++ agc2 = stv090x_get_agc2_min_level(state); ++ ++ if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) { ++ lock = 0; ++ } else { ++ ++ if (state->internal->dev_ver <= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) ++ goto err; ++ } else { ++ /* > Cut 3 */ ++ if (STV090x_WRITE_DEMOD(state, CARCFG, 0x06) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) /* set viterbi hysteresis */ ++ goto err; ++ } ++ ++ k_ref = k_max; ++ do { ++ if (STV090x_WRITE_DEMOD(state, KREFTMG, k_ref) < 0) ++ goto err; ++ if (stv090x_srate_srch_coarse(state) != 0) { ++ srate_coarse = stv090x_srate_srch_fine(state); ++ if (srate_coarse != 0) { ++ stv090x_get_lock_tmg(state); ++ lock = stv090x_get_dmdlock(state, ++ state->DemodTimeout); ++ } else { ++ lock = 0; ++ } ++ } else { ++ cpt_fail = 0; ++ agc2_ovflw = 0; ++ for (i = 0; i < 10; i++) { ++ agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | ++ STV090x_READ_DEMOD(state, AGC2I0); ++ if (agc2 >= 0xff00) ++ agc2_ovflw++; ++ reg = STV090x_READ_DEMOD(state, DSTATUS2); ++ if ((STV090x_GETFIELD_Px(reg, CFR_OVERFLOW_FIELD) == 0x01) && ++ (STV090x_GETFIELD_Px(reg, DEMOD_DELOCK_FIELD) == 0x01)) ++ ++ cpt_fail++; ++ } ++ if ((cpt_fail > 7) || (agc2_ovflw > 7)) ++ coarse_fail = 1; ++ ++ lock = 0; ++ } ++ k_ref -= 20; ++ } while ((k_ref >= k_min) && (!lock) && (!coarse_fail)); ++ } ++ ++ return lock; ++ ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_chk_tmg(struct stv090x_state *state) ++{ ++ u32 reg; ++ s32 tmg_cpt = 0, i; ++ u8 freq, tmg_thh, tmg_thl; ++ int tmg_lock = 0; ++ ++ freq = STV090x_READ_DEMOD(state, CARFREQ); ++ tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE); ++ tmg_thl = STV090x_READ_DEMOD(state, TMGTHFALL); ++ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); /* stop carrier offset search */ ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, RTC, 0x80) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x40) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x00) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) /* set car ofset to 0 */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x65) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) /* trigger acquisition */ ++ goto err; ++ msleep(10); ++ ++ for (i = 0; i < 10; i++) { ++ reg = STV090x_READ_DEMOD(state, DSTATUS); ++ if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) ++ tmg_cpt++; ++ msleep(1); ++ } ++ if (tmg_cpt >= 3) ++ tmg_lock = 1; ++ ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) /* DVB-S1 timing */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) /* DVB-S2 timing */ ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, freq) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHRISE, tmg_thh) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGTHFALL, tmg_thl) < 0) ++ goto err; ++ ++ return tmg_lock; ++ ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) ++{ ++ struct dvb_frontend *fe = &state->frontend; ++ ++ u32 reg; ++ s32 car_step, steps, cur_step, dir, freq, timeout_lock; ++ int lock = 0; ++ ++ if (state->srate >= 10000000) ++ timeout_lock = timeout_dmd / 3; ++ else ++ timeout_lock = timeout_dmd / 2; ++ ++ lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */ ++ if (!lock) { ++ if (state->srate >= 10000000) { ++ if (stv090x_chk_tmg(state)) { ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) ++ goto err; ++ lock = stv090x_get_dmdlock(state, timeout_dmd); ++ } else { ++ lock = 0; ++ } ++ } else { ++ if (state->srate <= 4000000) ++ car_step = 1000; ++ else if (state->srate <= 7000000) ++ car_step = 2000; ++ else if (state->srate <= 10000000) ++ car_step = 3000; ++ else ++ car_step = 5000; ++ ++ steps = (state->search_range / 1000) / car_step; ++ steps /= 2; ++ steps = 2 * (steps + 1); ++ if (steps < 0) ++ steps = 2; ++ else if (steps > 12) ++ steps = 12; ++ ++ cur_step = 1; ++ dir = 1; ++ ++ if (!lock) { ++ freq = state->frequency; ++ state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate; ++ while ((cur_step <= steps) && (!lock)) { ++ if (dir > 0) ++ freq += cur_step * car_step; ++ else ++ freq -= cur_step * car_step; ++ ++ /* Setup tuner */ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_set_frequency) { ++ if (state->config->tuner_set_frequency(fe, freq) < 0) ++ goto err_gateoff; ++ } ++ ++ if (state->config->tuner_set_bandwidth) { ++ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ msleep(50); ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_get_status) { ++ if (state->config->tuner_get_status(fe, ®) < 0) ++ goto err_gateoff; ++ } ++ ++ if (reg) ++ dprintk(FE_DEBUG, 1, "Tuner phase locked"); ++ else ++ dprintk(FE_DEBUG, 1, "Tuner unlocked"); ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) ++ goto err; ++ lock = stv090x_get_dmdlock(state, (timeout_dmd / 3)); ++ ++ dir *= -1; ++ cur_step++; ++ } ++ } ++ } ++ } ++ ++ return lock; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s32 *timeout_sw, s32 *steps) ++{ ++ s32 timeout, inc, steps_max, srate, car_max; ++ ++ srate = state->srate; ++ car_max = state->search_range / 1000; ++ car_max += car_max / 10; ++ car_max = 65536 * (car_max / 2); ++ car_max /= (state->internal->mclk / 1000); ++ ++ if (car_max > 0x4000) ++ car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */ ++ ++ inc = srate; ++ inc /= state->internal->mclk / 1000; ++ inc *= 256; ++ inc *= 256; ++ inc /= 1000; ++ ++ switch (state->search_mode) { ++ case STV090x_SEARCH_DVBS1: ++ case STV090x_SEARCH_DSS: ++ inc *= 3; /* freq step = 3% of srate */ ++ timeout = 20; ++ break; ++ ++ case STV090x_SEARCH_DVBS2: ++ inc *= 4; ++ timeout = 25; ++ break; ++ ++ case STV090x_SEARCH_AUTO: ++ default: ++ inc *= 3; ++ timeout = 25; ++ break; ++ } ++ inc /= 100; ++ if ((inc > car_max) || (inc < 0)) ++ inc = car_max / 2; /* increment <= 1/8 Mclk */ ++ ++ timeout *= 27500; /* 27.5 Msps reference */ ++ if (srate > 0) ++ timeout /= (srate / 1000); ++ ++ if ((timeout > 100) || (timeout < 0)) ++ timeout = 100; ++ ++ steps_max = (car_max / inc) + 1; /* min steps = 3 */ ++ if ((steps_max > 100) || (steps_max < 0)) { ++ steps_max = 100; /* max steps <= 100 */ ++ inc = car_max / steps_max; ++ } ++ *freq_inc = inc; ++ *timeout_sw = timeout; ++ *steps = steps_max; ++ ++ return 0; ++} ++ ++static int stv090x_chk_signal(struct stv090x_state *state) ++{ ++ s32 offst_car, agc2, car_max; ++ int no_signal; ++ ++ offst_car = STV090x_READ_DEMOD(state, CFR2) << 8; ++ offst_car |= STV090x_READ_DEMOD(state, CFR1); ++ offst_car = comp2(offst_car, 16); ++ ++ agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8; ++ agc2 |= STV090x_READ_DEMOD(state, AGC2I0); ++ car_max = state->search_range / 1000; ++ ++ car_max += (car_max / 10); /* 10% margin */ ++ car_max = (65536 * car_max / 2); ++ car_max /= state->internal->mclk / 1000; ++ ++ if (car_max > 0x4000) ++ car_max = 0x4000; ++ ++ if ((agc2 > 0x2000) || (offst_car > 2 * car_max) || (offst_car < -2 * car_max)) { ++ no_signal = 1; ++ dprintk(FE_DEBUG, 1, "No Signal"); ++ } else { ++ no_signal = 0; ++ dprintk(FE_DEBUG, 1, "Found Signal"); ++ } ++ ++ return no_signal; ++} ++ ++static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 timeout, int zigzag, s32 steps_max) ++{ ++ int no_signal, lock = 0; ++ s32 cpt_step = 0, offst_freq, car_max; ++ u32 reg; ++ ++ car_max = state->search_range / 1000; ++ car_max += (car_max / 10); ++ car_max = (65536 * car_max / 2); ++ car_max /= (state->internal->mclk / 1000); ++ if (car_max > 0x4000) ++ car_max = 0x4000; ++ ++ if (zigzag) ++ offst_freq = 0; ++ else ++ offst_freq = -car_max + inc; ++ ++ do { ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, ((offst_freq / 256) & 0xff)) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, offst_freq & 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, PDELCTRL1); ++ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x1); /* stop DVB-S2 packet delin */ ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ ++ if (zigzag) { ++ if (offst_freq >= 0) ++ offst_freq = -offst_freq - 2 * inc; ++ else ++ offst_freq = -offst_freq; ++ } else { ++ offst_freq += 2 * inc; ++ } ++ ++ cpt_step++; ++ ++ lock = stv090x_get_dmdlock(state, timeout); ++ no_signal = stv090x_chk_signal(state); ++ ++ } while ((!lock) && ++ (!no_signal) && ++ ((offst_freq - inc) < car_max) && ++ ((offst_freq + inc) > -car_max) && ++ (cpt_step < steps_max)); ++ ++ reg = STV090x_READ_DEMOD(state, PDELCTRL1); ++ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ ++ return lock; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_sw_algo(struct stv090x_state *state) ++{ ++ int no_signal, zigzag, lock = 0; ++ u32 reg; ++ ++ s32 dvbs2_fly_wheel; ++ s32 inc, timeout_step, trials, steps_max; ++ ++ /* get params */ ++ stv090x_get_loop_params(state, &inc, &timeout_step, &steps_max); ++ ++ switch (state->search_mode) { ++ case STV090x_SEARCH_DVBS1: ++ case STV090x_SEARCH_DSS: ++ /* accelerate the frequency detector */ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x49) < 0) ++ goto err; ++ zigzag = 0; ++ break; ++ ++ case STV090x_SEARCH_DVBS2: ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) ++ goto err; ++ zigzag = 1; ++ break; ++ ++ case STV090x_SEARCH_AUTO: ++ default: ++ /* accelerate the frequency detector */ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0xc9) < 0) ++ goto err; ++ zigzag = 0; ++ break; ++ } ++ ++ trials = 0; ++ do { ++ lock = stv090x_search_car_loop(state, inc, timeout_step, zigzag, steps_max); ++ no_signal = stv090x_chk_signal(state); ++ trials++; ++ ++ /*run the SW search 2 times maximum*/ ++ if (lock || no_signal || (trials == 2)) { ++ /*Check if the demod is not losing lock in DVBS2*/ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) ++ goto err; ++ } ++ ++ reg = STV090x_READ_DEMOD(state, DMDSTATE); ++ if ((lock) && (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == STV090x_DVBS2)) { ++ /*Check if the demod is not losing lock in DVBS2*/ ++ msleep(timeout_step); ++ reg = STV090x_READ_DEMOD(state, DMDFLYW); ++ dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); ++ if (dvbs2_fly_wheel < 0xd) { /*if correct frames is decrementing */ ++ msleep(timeout_step); ++ reg = STV090x_READ_DEMOD(state, DMDFLYW); ++ dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); ++ } ++ if (dvbs2_fly_wheel < 0xd) { ++ /*FALSE lock, The demod is losing lock */ ++ lock = 0; ++ if (trials < 2) { ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) ++ goto err; ++ } ++ } ++ } ++ } ++ } while ((!lock) && (trials < 2) && (!no_signal)); ++ ++ return lock; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static enum stv090x_delsys stv090x_get_std(struct stv090x_state *state) ++{ ++ u32 reg; ++ enum stv090x_delsys delsys; ++ ++ reg = STV090x_READ_DEMOD(state, DMDSTATE); ++ if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 2) ++ delsys = STV090x_DVBS2; ++ else if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 3) { ++ reg = STV090x_READ_DEMOD(state, FECM); ++ if (STV090x_GETFIELD_Px(reg, DSS_DVB_FIELD) == 1) ++ delsys = STV090x_DSS; ++ else ++ delsys = STV090x_DVBS1; ++ } else { ++ delsys = STV090x_ERROR; ++ } ++ ++ return delsys; ++} ++ ++/* in Hz */ ++static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) ++{ ++ s32 derot, int_1, int_2, tmp_1, tmp_2; ++ ++ derot = STV090x_READ_DEMOD(state, CFR2) << 16; ++ derot |= STV090x_READ_DEMOD(state, CFR1) << 8; ++ derot |= STV090x_READ_DEMOD(state, CFR0); ++ ++ derot = comp2(derot, 24); ++ int_1 = mclk >> 12; ++ int_2 = derot >> 12; ++ ++ /* carrier_frequency = MasterClock * Reg / 2^24 */ ++ tmp_1 = mclk % 0x1000; ++ tmp_2 = derot % 0x1000; ++ ++ derot = (int_1 * int_2) + ++ ((int_1 * tmp_2) >> 12) + ++ ((int_2 * tmp_1) >> 12); ++ ++ return derot; ++} ++ ++static int stv090x_get_viterbi(struct stv090x_state *state) ++{ ++ u32 reg, rate; ++ ++ reg = STV090x_READ_DEMOD(state, VITCURPUN); ++ rate = STV090x_GETFIELD_Px(reg, VIT_CURPUN_FIELD); ++ ++ switch (rate) { ++ case 13: ++ state->fec = STV090x_PR12; ++ break; ++ ++ case 18: ++ state->fec = STV090x_PR23; ++ break; ++ ++ case 21: ++ state->fec = STV090x_PR34; ++ break; ++ ++ case 24: ++ state->fec = STV090x_PR56; ++ break; ++ ++ case 25: ++ state->fec = STV090x_PR67; ++ break; ++ ++ case 26: ++ state->fec = STV090x_PR78; ++ break; ++ ++ default: ++ state->fec = STV090x_PRERR; ++ break; ++ } ++ ++ return 0; ++} ++ ++static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state) ++{ ++ struct dvb_frontend *fe = &state->frontend; ++ ++ u8 tmg; ++ u32 reg; ++ s32 i = 0, offst_freq; ++ ++ msleep(5); ++ ++ if (state->algo == STV090x_BLIND_SEARCH) { ++ tmg = STV090x_READ_DEMOD(state, TMGREG2); ++ STV090x_WRITE_DEMOD(state, SFRSTEP, 0x5c); ++ while ((i <= 50) && (tmg != 0) && (tmg != 0xff)) { ++ tmg = STV090x_READ_DEMOD(state, TMGREG2); ++ msleep(5); ++ i += 5; ++ } ++ } ++ state->delsys = stv090x_get_std(state); ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_get_frequency) { ++ if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000; ++ state->frequency += offst_freq; ++ ++ if (stv090x_get_viterbi(state) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, DMDMODCOD); ++ state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); ++ state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; ++ state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1; ++ reg = STV090x_READ_DEMOD(state, TMGOBS); ++ state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); ++ reg = STV090x_READ_DEMOD(state, FECM); ++ state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD); ++ ++ if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) { ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_get_frequency) { ++ if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) ++ return STV090x_RANGEOK; ++ else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000)) ++ return STV090x_RANGEOK; ++ else ++ return STV090x_OUTOFRANGE; /* Out of Range */ ++ } else { ++ if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) ++ return STV090x_RANGEOK; ++ else ++ return STV090x_OUTOFRANGE; ++ } ++ ++ return STV090x_OUTOFRANGE; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static u32 stv090x_get_tmgoffst(struct stv090x_state *state, u32 srate) ++{ ++ s32 offst_tmg; ++ ++ offst_tmg = STV090x_READ_DEMOD(state, TMGREG2) << 16; ++ offst_tmg |= STV090x_READ_DEMOD(state, TMGREG1) << 8; ++ offst_tmg |= STV090x_READ_DEMOD(state, TMGREG0); ++ ++ offst_tmg = comp2(offst_tmg, 24); /* 2's complement */ ++ if (!offst_tmg) ++ offst_tmg = 1; ++ ++ offst_tmg = ((s32) srate * 10) / ((s32) 0x1000000 / offst_tmg); ++ offst_tmg /= 320; ++ ++ return offst_tmg; ++} ++ ++static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_modcod modcod, s32 pilots) ++{ ++ u8 aclc = 0x29; ++ s32 i; ++ struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low; ++ ++ if (state->internal->dev_ver == 0x20) { ++ car_loop = stv090x_s2_crl_cut20; ++ car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20; ++ car_loop_apsk_low = stv090x_s2_apsk_crl_cut20; ++ } else { ++ /* >= Cut 3 */ ++ car_loop = stv090x_s2_crl_cut30; ++ car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut30; ++ car_loop_apsk_low = stv090x_s2_apsk_crl_cut30; ++ } ++ ++ if (modcod < STV090x_QPSK_12) { ++ i = 0; ++ while ((i < 3) && (modcod != car_loop_qpsk_low[i].modcod)) ++ i++; ++ ++ if (i >= 3) ++ i = 2; ++ ++ } else { ++ i = 0; ++ while ((i < 14) && (modcod != car_loop[i].modcod)) ++ i++; ++ ++ if (i >= 14) { ++ i = 0; ++ while ((i < 11) && (modcod != car_loop_apsk_low[i].modcod)) ++ i++; ++ ++ if (i >= 11) ++ i = 10; ++ } ++ } ++ ++ if (modcod <= STV090x_QPSK_25) { ++ if (pilots) { ++ if (state->srate <= 3000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_on_2; ++ else if (state->srate <= 7000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_on_5; ++ else if (state->srate <= 15000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_on_10; ++ else if (state->srate <= 25000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_on_20; ++ else ++ aclc = car_loop_qpsk_low[i].crl_pilots_on_30; ++ } else { ++ if (state->srate <= 3000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_off_2; ++ else if (state->srate <= 7000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_off_5; ++ else if (state->srate <= 15000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_off_10; ++ else if (state->srate <= 25000000) ++ aclc = car_loop_qpsk_low[i].crl_pilots_off_20; ++ else ++ aclc = car_loop_qpsk_low[i].crl_pilots_off_30; ++ } ++ ++ } else if (modcod <= STV090x_8PSK_910) { ++ if (pilots) { ++ if (state->srate <= 3000000) ++ aclc = car_loop[i].crl_pilots_on_2; ++ else if (state->srate <= 7000000) ++ aclc = car_loop[i].crl_pilots_on_5; ++ else if (state->srate <= 15000000) ++ aclc = car_loop[i].crl_pilots_on_10; ++ else if (state->srate <= 25000000) ++ aclc = car_loop[i].crl_pilots_on_20; ++ else ++ aclc = car_loop[i].crl_pilots_on_30; ++ } else { ++ if (state->srate <= 3000000) ++ aclc = car_loop[i].crl_pilots_off_2; ++ else if (state->srate <= 7000000) ++ aclc = car_loop[i].crl_pilots_off_5; ++ else if (state->srate <= 15000000) ++ aclc = car_loop[i].crl_pilots_off_10; ++ else if (state->srate <= 25000000) ++ aclc = car_loop[i].crl_pilots_off_20; ++ else ++ aclc = car_loop[i].crl_pilots_off_30; ++ } ++ } else { /* 16APSK and 32APSK */ ++ if (state->srate <= 3000000) ++ aclc = car_loop_apsk_low[i].crl_pilots_on_2; ++ else if (state->srate <= 7000000) ++ aclc = car_loop_apsk_low[i].crl_pilots_on_5; ++ else if (state->srate <= 15000000) ++ aclc = car_loop_apsk_low[i].crl_pilots_on_10; ++ else if (state->srate <= 25000000) ++ aclc = car_loop_apsk_low[i].crl_pilots_on_20; ++ else ++ aclc = car_loop_apsk_low[i].crl_pilots_on_30; ++ } ++ ++ return aclc; ++} ++ ++static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) ++{ ++ struct stv090x_short_frame_crloop *short_crl = NULL; ++ s32 index = 0; ++ u8 aclc = 0x0b; ++ ++ switch (state->modulation) { ++ case STV090x_QPSK: ++ default: ++ index = 0; ++ break; ++ case STV090x_8PSK: ++ index = 1; ++ break; ++ case STV090x_16APSK: ++ index = 2; ++ break; ++ case STV090x_32APSK: ++ index = 3; ++ break; ++ } ++ ++ if (state->internal->dev_ver >= 0x30) { ++ /* Cut 3.0 and up */ ++ short_crl = stv090x_s2_short_crl_cut30; ++ } else { ++ /* Cut 2.0 and up: we don't support cuts older than 2.0 */ ++ short_crl = stv090x_s2_short_crl_cut20; ++ } ++ ++ if (state->srate <= 3000000) ++ aclc = short_crl[index].crl_2; ++ else if (state->srate <= 7000000) ++ aclc = short_crl[index].crl_5; ++ else if (state->srate <= 15000000) ++ aclc = short_crl[index].crl_10; ++ else if (state->srate <= 25000000) ++ aclc = short_crl[index].crl_20; ++ else ++ aclc = short_crl[index].crl_30; ++ ++ return aclc; ++} ++ ++static int stv090x_optimize_track(struct stv090x_state *state) ++{ ++ struct dvb_frontend *fe = &state->frontend; ++ ++ enum stv090x_modcod modcod; ++ ++ s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; ++ u32 reg; ++ ++ srate = stv090x_get_srate(state, state->internal->mclk); ++ srate += stv090x_get_tmgoffst(state, srate); ++ ++ switch (state->delsys) { ++ case STV090x_DVBS1: ++ case STV090x_DSS: ++ if (state->search_mode == STV090x_SEARCH_AUTO) { ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ } ++ reg = STV090x_READ_DEMOD(state, DEMOD); ++ STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); ++ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x01); ++ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x30) { ++ if (stv090x_get_viterbi(state) < 0) ++ goto err; ++ ++ if (state->fec == STV090x_PR12) { ++ if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x98) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x18) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) ++ goto err; ++ } ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) ++ goto err; ++ break; ++ ++ case STV090x_DVBS2: ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ if (state->internal->dev_ver >= 0x30) { ++ if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) ++ goto err; ++ } ++ if (state->frame_len == STV090x_LONG_FRAME) { ++ reg = STV090x_READ_DEMOD(state, DMDMODCOD); ++ modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); ++ pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; ++ aclc = stv090x_optimize_carloop(state, modcod, pilots); ++ if (modcod <= STV090x_QPSK_910) { ++ STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc); ++ } else if (modcod <= STV090x_8PSK_910) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) ++ goto err; ++ } ++ if ((state->demod_mode == STV090x_SINGLE) && (modcod > STV090x_8PSK_910)) { ++ if (modcod <= STV090x_16APSK_910) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) ++ goto err; ++ } ++ } ++ } else { ++ /*Carrier loop setting for short frame*/ ++ aclc = stv090x_optimize_carloop_short(state); ++ if (state->modulation == STV090x_QPSK) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc) < 0) ++ goto err; ++ } else if (state->modulation == STV090x_8PSK) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) ++ goto err; ++ } else if (state->modulation == STV090x_16APSK) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) ++ goto err; ++ } else if (state->modulation == STV090x_32APSK) { ++ if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) ++ goto err; ++ } ++ } ++ ++ STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67); /* PER */ ++ break; ++ ++ case STV090x_ERROR: ++ default: ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); ++ STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ break; ++ } ++ ++ f_1 = STV090x_READ_DEMOD(state, CFR2); ++ f_0 = STV090x_READ_DEMOD(state, CFR1); ++ reg = STV090x_READ_DEMOD(state, TMGOBS); ++ ++ if (state->algo == STV090x_BLIND_SEARCH) { ++ STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00); ++ reg = STV090x_READ_DEMOD(state, DMDCFGMD); ++ STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0x00); ++ STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) ++ goto err; ++ ++ if (stv090x_set_srate(state, srate) < 0) ++ goto err; ++ blind_tune = 1; ++ ++ if (stv090x_dvbs_track_crl(state) < 0) ++ goto err; ++ } ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if ((state->search_mode == STV090x_SEARCH_DVBS1) || ++ (state->search_mode == STV090x_SEARCH_DSS) || ++ (state->search_mode == STV090x_SEARCH_AUTO)) { ++ ++ if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x0a) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x00) < 0) ++ goto err; ++ } ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) ++ goto err; ++ ++ /* AUTO tracking MODE */ ++ if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x80) < 0) ++ goto err; ++ /* AUTO tracking MODE */ ++ if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0) ++ goto err; ++ ++ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) || ++ (state->srate < 10000000)) { ++ /* update initial carrier freq with the found freq offset */ ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) ++ goto err; ++ state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000; ++ ++ if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) { ++ ++ if (state->algo != STV090x_WARM_SEARCH) { ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_set_bandwidth) { ++ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ } ++ } ++ if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) ++ msleep(50); /* blind search: wait 50ms for SR stabilization */ ++ else ++ msleep(5); ++ ++ stv090x_get_lock_tmg(state); ++ ++ if (!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) { ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) ++ goto err; ++ ++ i = 0; ++ ++ while ((!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) && (i <= 2)) { ++ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) ++ goto err; ++ i++; ++ } ++ } ++ ++ } ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) ++ goto err; ++ } ++ ++ if ((state->delsys == STV090x_DVBS1) || (state->delsys == STV090x_DSS)) ++ stv090x_set_vit_thtracq(state); ++ ++ return 0; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_get_feclock(struct stv090x_state *state, s32 timeout) ++{ ++ s32 timer = 0, lock = 0, stat; ++ u32 reg; ++ ++ while ((timer < timeout) && (!lock)) { ++ reg = STV090x_READ_DEMOD(state, DMDSTATE); ++ stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); ++ ++ switch (stat) { ++ case 0: /* searching */ ++ case 1: /* first PLH detected */ ++ default: ++ lock = 0; ++ break; ++ ++ case 2: /* DVB-S2 mode */ ++ reg = STV090x_READ_DEMOD(state, PDELSTATUS1); ++ lock = STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD); ++ break; ++ ++ case 3: /* DVB-S1/legacy mode */ ++ reg = STV090x_READ_DEMOD(state, VSTATUSVIT); ++ lock = STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD); ++ break; ++ } ++ if (!lock) { ++ msleep(10); ++ timer += 10; ++ } ++ } ++ return lock; ++} ++ ++static int stv090x_get_lock(struct stv090x_state *state, s32 timeout_dmd, s32 timeout_fec) ++{ ++ u32 reg; ++ s32 timer = 0; ++ int lock; ++ ++ lock = stv090x_get_dmdlock(state, timeout_dmd); ++ if (lock) ++ lock = stv090x_get_feclock(state, timeout_fec); ++ ++ if (lock) { ++ lock = 0; ++ ++ while ((timer < timeout_fec) && (!lock)) { ++ reg = STV090x_READ_DEMOD(state, TSSTATUS); ++ lock = STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD); ++ msleep(1); ++ timer++; ++ } ++ } ++ ++ return lock; ++} ++ ++static int stv090x_set_s2rolloff(struct stv090x_state *state) ++{ ++ u32 reg; ++ ++ if (state->internal->dev_ver <= 0x20) { ++ /* rolloff to auto mode if DVBS2 */ ++ reg = STV090x_READ_DEMOD(state, DEMOD); ++ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) ++ goto err; ++ } else { ++ /* DVB-S2 rolloff to auto mode if DVBS2 */ ++ reg = STV090x_READ_DEMOD(state, DEMOD); ++ STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) ++ goto err; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++ ++static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) ++{ ++ struct dvb_frontend *fe = &state->frontend; ++ enum stv090x_signal_state signal_state = STV090x_NOCARRIER; ++ u32 reg; ++ s32 agc1_power, power_iq = 0, i; ++ int lock = 0, low_sr = 0; ++ ++ reg = STV090x_READ_DEMOD(state, TSCFGH); ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */ ++ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */ ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if (state->srate > 5000000) { ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0) ++ goto err; ++ } ++ } ++ ++ stv090x_get_lock_tmg(state); ++ ++ if (state->algo == STV090x_BLIND_SEARCH) { ++ state->tuner_bw = 2 * 36000000; /* wide bw for unknown srate */ ++ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) /* wider srate scan */ ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) ++ goto err; ++ if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */ ++ goto err; ++ } else { ++ /* known srate */ ++ if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) ++ goto err; ++ ++ if (state->srate < 2000000) { ++ /* SR < 2MSPS */ ++ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x63) < 0) ++ goto err; ++ } else { ++ /* SR >= 2Msps */ ++ if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) ++ goto err; ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) ++ goto err; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0) ++ goto err; ++ if (state->algo == STV090x_COLD_SEARCH) ++ state->tuner_bw = (15 * (stv090x_car_width(state->srate, state->rolloff) + 10000000)) / 10; ++ else if (state->algo == STV090x_WARM_SEARCH) ++ state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + 10000000; ++ } ++ ++ /* if cold start or warm (Symbolrate is known) ++ * use a Narrow symbol rate scan range ++ */ ++ if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) /* narrow srate scan */ ++ goto err; ++ ++ if (stv090x_set_srate(state, state->srate) < 0) ++ goto err; ++ ++ if (stv090x_set_max_srate(state, state->internal->mclk, ++ state->srate) < 0) ++ goto err; ++ if (stv090x_set_min_srate(state, state->internal->mclk, ++ state->srate) < 0) ++ goto err; ++ ++ if (state->srate >= 10000000) ++ low_sr = 0; ++ else ++ low_sr = 1; ++ } ++ ++ /* Setup tuner */ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_set_bbgain) { ++ reg = state->config->tuner_bbgain; ++ if (reg == 0) ++ reg = 10; /* default: 10dB */ ++ if (state->config->tuner_set_bbgain(fe, reg) < 0) ++ goto err_gateoff; ++ } ++ ++ if (state->config->tuner_set_frequency) { ++ if (state->config->tuner_set_frequency(fe, state->frequency) < 0) ++ goto err_gateoff; ++ } ++ ++ if (state->config->tuner_set_bandwidth) { ++ if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ msleep(50); ++ ++ if (state->config->tuner_get_status) { ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ if (state->config->tuner_get_status(fe, ®) < 0) ++ goto err_gateoff; ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ if (reg) ++ dprintk(FE_DEBUG, 1, "Tuner phase locked"); ++ else { ++ dprintk(FE_DEBUG, 1, "Tuner unlocked"); ++ return STV090x_NOCARRIER; ++ } ++ } ++ ++ msleep(10); ++ agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1), ++ STV090x_READ_DEMOD(state, AGCIQIN0)); ++ ++ if (agc1_power == 0) { ++ /* If AGC1 integrator value is 0 ++ * then read POWERI, POWERQ ++ */ ++ for (i = 0; i < 5; i++) { ++ power_iq += (STV090x_READ_DEMOD(state, POWERI) + ++ STV090x_READ_DEMOD(state, POWERQ)) >> 1; ++ } ++ power_iq /= 5; ++ } ++ ++ if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) { ++ dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq); ++ lock = 0; ++ signal_state = STV090x_NOAGC1; ++ } else { ++ reg = STV090x_READ_DEMOD(state, DEMOD); ++ STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); ++ ++ if (state->internal->dev_ver <= 0x20) { ++ /* rolloff to auto mode if DVBS2 */ ++ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1); ++ } else { ++ /* DVB-S2 rolloff to auto mode if DVBS2 */ ++ STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 1); ++ } ++ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) ++ goto err; ++ ++ if (stv090x_delivery_search(state) < 0) ++ goto err; ++ ++ if (state->algo != STV090x_BLIND_SEARCH) { ++ if (stv090x_start_search(state) < 0) ++ goto err; ++ } ++ } ++ ++ if (signal_state == STV090x_NOAGC1) ++ return signal_state; ++ ++ if (state->algo == STV090x_BLIND_SEARCH) ++ lock = stv090x_blind_search(state); ++ ++ else if (state->algo == STV090x_COLD_SEARCH) ++ lock = stv090x_get_coldlock(state, state->DemodTimeout); ++ ++ else if (state->algo == STV090x_WARM_SEARCH) ++ lock = stv090x_get_dmdlock(state, state->DemodTimeout); ++ ++ if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) { ++ if (!low_sr) { ++ if (stv090x_chk_tmg(state)) ++ lock = stv090x_sw_algo(state); ++ } ++ } ++ ++ if (lock) ++ signal_state = stv090x_get_sig_params(state); ++ ++ if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */ ++ stv090x_optimize_track(state); ++ ++ if (state->internal->dev_ver >= 0x20) { ++ /* >= Cut 2.0 :release TS reset after ++ * demod lock and optimized Tracking ++ */ ++ reg = STV090x_READ_DEMOD(state, TSCFGH); ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ ++ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) ++ goto err; ++ ++ msleep(3); ++ ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* merger reset */ ++ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) ++ goto err; ++ ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ ++ if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) ++ goto err; ++ } ++ ++ lock = stv090x_get_lock(state, state->FecTimeout, ++ state->FecTimeout); ++ if (lock) { ++ if (state->delsys == STV090x_DVBS2) { ++ stv090x_set_s2rolloff(state); ++ ++ reg = STV090x_READ_DEMOD(state, PDELCTRL2); ++ STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 1); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) ++ goto err; ++ /* Reset DVBS2 packet delinator error counter */ ++ reg = STV090x_READ_DEMOD(state, PDELCTRL2); ++ STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 0); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67) < 0) /* PER */ ++ goto err; ++ } else { ++ if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) ++ goto err; ++ } ++ /* Reset the Total packet counter */ ++ if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0x00) < 0) ++ goto err; ++ /* Reset the packet Error counter2 */ ++ if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) ++ goto err; ++ } else { ++ signal_state = STV090x_NODATA; ++ stv090x_chk_signal(state); ++ } ++ } ++ return signal_state; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_mis(struct stv090x_state *state, int mis) ++{ ++ u32 reg; ++ ++ if (mis < 0 || mis > 255) { ++ dprintk(FE_DEBUG, 1, "Disable MIS filtering"); ++ reg = STV090x_READ_DEMOD(state, PDELCTRL1); ++ STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ } else { ++ dprintk(FE_DEBUG, 1, "Enable MIS filtering - %d", mis); ++ reg = STV090x_READ_DEMOD(state, PDELCTRL1); ++ STV090x_SETFIELD_Px(reg, FILTER_EN_FIELD, 0x01); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ISIENTRY, mis) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ISIBITENA, 0xff) < 0) ++ goto err; ++ } ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static enum dvbfe_search stv090x_search(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *props = &fe->dtv_property_cache; ++ ++ if (props->frequency == 0) ++ return DVBFE_ALGO_SEARCH_INVALID; ++ ++ state->delsys = props->delivery_system; ++ state->frequency = props->frequency; ++ state->srate = props->symbol_rate; ++ state->search_mode = STV090x_SEARCH_AUTO; ++ state->algo = STV090x_COLD_SEARCH; ++ state->fec = STV090x_PRERR; ++ if (state->srate > 10000000) { ++ dprintk(FE_DEBUG, 1, "Search range: 10 MHz"); ++ state->search_range = 10000000; ++ } else { ++ dprintk(FE_DEBUG, 1, "Search range: 5 MHz"); ++ state->search_range = 5000000; ++ } ++ ++ stv090x_set_mis(state, props->stream_id); ++ ++ if (stv090x_algo(state) == STV090x_RANGEOK) { ++ dprintk(FE_DEBUG, 1, "Search success!"); ++ return DVBFE_ALGO_SEARCH_SUCCESS; ++ } else { ++ dprintk(FE_DEBUG, 1, "Search failed!"); ++ return DVBFE_ALGO_SEARCH_FAILED; ++ } ++ ++ return DVBFE_ALGO_SEARCH_ERROR; ++} ++ ++static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg, dstatus; ++ u8 search_state; ++ ++ *status = 0; ++ ++ dstatus = STV090x_READ_DEMOD(state, DSTATUS); ++ if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD)) ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ ++ reg = STV090x_READ_DEMOD(state, DMDSTATE); ++ search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); ++ ++ switch (search_state) { ++ case 0: /* searching */ ++ case 1: /* first PLH detected */ ++ default: ++ dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)"); ++ break; ++ ++ case 2: /* DVB-S2 mode */ ++ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); ++ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { ++ reg = STV090x_READ_DEMOD(state, PDELSTATUS1); ++ if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { ++ *status |= FE_HAS_VITERBI; ++ reg = STV090x_READ_DEMOD(state, TSSTATUS); ++ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ } ++ break; ++ ++ case 3: /* DVB-S1/legacy mode */ ++ dprintk(FE_DEBUG, 1, "Delivery system: DVB-S"); ++ if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { ++ reg = STV090x_READ_DEMOD(state, VSTATUSVIT); ++ if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) { ++ *status |= FE_HAS_VITERBI; ++ reg = STV090x_READ_DEMOD(state, TSSTATUS); ++ if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ } ++ } ++ break; ++ } ++ ++ return 0; ++} ++ ++static int stv090x_read_per(struct dvb_frontend *fe, u32 *per) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ ++ s32 count_4, count_3, count_2, count_1, count_0, count; ++ u32 reg, h, m, l; ++ enum fe_status status; ++ ++ stv090x_read_status(fe, &status); ++ if (!(status & FE_HAS_LOCK)) { ++ *per = 1 << 23; /* Max PER */ ++ } else { ++ /* Counter 2 */ ++ reg = STV090x_READ_DEMOD(state, ERRCNT22); ++ h = STV090x_GETFIELD_Px(reg, ERR_CNT2_FIELD); ++ ++ reg = STV090x_READ_DEMOD(state, ERRCNT21); ++ m = STV090x_GETFIELD_Px(reg, ERR_CNT21_FIELD); ++ ++ reg = STV090x_READ_DEMOD(state, ERRCNT20); ++ l = STV090x_GETFIELD_Px(reg, ERR_CNT20_FIELD); ++ ++ *per = ((h << 16) | (m << 8) | l); ++ ++ count_4 = STV090x_READ_DEMOD(state, FBERCPT4); ++ count_3 = STV090x_READ_DEMOD(state, FBERCPT3); ++ count_2 = STV090x_READ_DEMOD(state, FBERCPT2); ++ count_1 = STV090x_READ_DEMOD(state, FBERCPT1); ++ count_0 = STV090x_READ_DEMOD(state, FBERCPT0); ++ ++ if ((!count_4) && (!count_3)) { ++ count = (count_2 & 0xff) << 16; ++ count |= (count_1 & 0xff) << 8; ++ count |= count_0 & 0xff; ++ } else { ++ count = 1 << 24; ++ } ++ if (count == 0) ++ *per = 1; ++ } ++ if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) ++{ ++ int res = 0; ++ int min = 0, med; ++ ++ if ((val >= tab[min].read && val < tab[max].read) || ++ (val >= tab[max].read && val < tab[min].read)) { ++ while ((max - min) > 1) { ++ med = (max + min) / 2; ++ if ((val >= tab[min].read && val < tab[med].read) || ++ (val >= tab[med].read && val < tab[min].read)) ++ max = med; ++ else ++ min = med; ++ } ++ res = ((val - tab[min].read) * ++ (tab[max].real - tab[min].real) / ++ (tab[max].read - tab[min].read)) + ++ tab[min].real; ++ } else { ++ if (tab[min].read < tab[max].read) { ++ if (val < tab[min].read) ++ res = tab[min].real; ++ else if (val >= tab[max].read) ++ res = tab[max].real; ++ } else { ++ if (val >= tab[min].read) ++ res = tab[min].real; ++ else if (val < tab[max].read) ++ res = tab[max].real; ++ } ++ } ++ ++ return res; ++} ++ ++static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg; ++ s32 agc_0, agc_1, agc; ++ s32 str; ++ ++ reg = STV090x_READ_DEMOD(state, AGCIQIN1); ++ agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); ++ reg = STV090x_READ_DEMOD(state, AGCIQIN0); ++ agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); ++ agc = MAKEWORD16(agc_1, agc_0); ++ ++ str = stv090x_table_lookup(stv090x_rf_tab, ++ ARRAY_SIZE(stv090x_rf_tab) - 1, agc); ++ if (agc > stv090x_rf_tab[0].read) ++ str = 0; ++ else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read) ++ str = -100; ++ *strength = (str + 100) * 0xFFFF / 100; ++ ++ return 0; ++} ++ ++static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg_0, reg_1, reg, i; ++ s32 val_0, val_1, val = 0; ++ u8 lock_f; ++ s32 div; ++ u32 last; ++ ++ switch (state->delsys) { ++ case STV090x_DVBS2: ++ reg = STV090x_READ_DEMOD(state, DSTATUS); ++ lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); ++ if (lock_f) { ++ msleep(5); ++ for (i = 0; i < 16; i++) { ++ reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1); ++ val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); ++ reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0); ++ val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD); ++ val += MAKEWORD16(val_1, val_0); ++ msleep(1); ++ } ++ val /= 16; ++ last = ARRAY_SIZE(stv090x_s2cn_tab) - 1; ++ div = stv090x_s2cn_tab[0].read - ++ stv090x_s2cn_tab[last].read; ++ *cnr = 0xFFFF - ((val * 0xFFFF) / div); ++ } ++ break; ++ ++ case STV090x_DVBS1: ++ case STV090x_DSS: ++ reg = STV090x_READ_DEMOD(state, DSTATUS); ++ lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); ++ if (lock_f) { ++ msleep(5); ++ for (i = 0; i < 16; i++) { ++ reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1); ++ val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); ++ reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0); ++ val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD); ++ val += MAKEWORD16(val_1, val_0); ++ msleep(1); ++ } ++ val /= 16; ++ last = ARRAY_SIZE(stv090x_s1cn_tab) - 1; ++ div = stv090x_s1cn_tab[0].read - ++ stv090x_s1cn_tab[last].read; ++ *cnr = 0xFFFF - ((val * 0xFFFF) / div); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int stv090x_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg; ++ ++ reg = STV090x_READ_DEMOD(state, DISTXCTL); ++ switch (tone) { ++ case SEC_TONE_ON: ++ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ break; ++ ++ case SEC_TONE_OFF: ++ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++ ++static enum dvbfe_algo stv090x_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_CUSTOM; ++} ++ ++static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg, idle = 0, fifo_full = 1; ++ int i; ++ ++ reg = STV090x_READ_DEMOD(state, DISTXCTL); ++ ++ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, ++ (state->config->diseqc_envelope_mode) ? 4 : 2); ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ for (i = 0; i < cmd->msg_len; i++) { ++ ++ while (fifo_full) { ++ reg = STV090x_READ_DEMOD(state, DISTXSTATUS); ++ fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DISTXDATA, cmd->msg[i]) < 0) ++ goto err; ++ } ++ reg = STV090x_READ_DEMOD(state, DISTXCTL); ++ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ i = 0; ++ ++ while ((!idle) && (i < 10)) { ++ reg = STV090x_READ_DEMOD(state, DISTXSTATUS); ++ idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); ++ msleep(10); ++ i++; ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg, idle = 0, fifo_full = 1; ++ u8 mode, value; ++ int i; ++ ++ reg = STV090x_READ_DEMOD(state, DISTXCTL); ++ ++ if (burst == SEC_MINI_A) { ++ mode = (state->config->diseqc_envelope_mode) ? 5 : 3; ++ value = 0x00; ++ } else { ++ mode = (state->config->diseqc_envelope_mode) ? 4 : 2; ++ value = 0xFF; ++ } ++ ++ STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, mode); ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ while (fifo_full) { ++ reg = STV090x_READ_DEMOD(state, DISTXSTATUS); ++ fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); ++ } ++ ++ if (STV090x_WRITE_DEMOD(state, DISTXDATA, value) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, DISTXCTL); ++ STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); ++ if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) ++ goto err; ++ ++ i = 0; ++ ++ while ((!idle) && (i < 10)) { ++ reg = STV090x_READ_DEMOD(state, DISTXSTATUS); ++ idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); ++ msleep(10); ++ i++; ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg = 0, i = 0, rx_end = 0; ++ ++ while ((rx_end != 1) && (i < 10)) { ++ msleep(10); ++ i++; ++ reg = STV090x_READ_DEMOD(state, DISRX_ST0); ++ rx_end = STV090x_GETFIELD_Px(reg, RX_END_FIELD); ++ } ++ ++ if (rx_end) { ++ reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD); ++ for (i = 0; i < reply->msg_len; i++) ++ reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA); ++ } ++ ++ return 0; ++} ++ ++static int stv090x_sleep(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg; ++ u8 full_standby = 0; ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (state->config->tuner_sleep) { ++ if (state->config->tuner_sleep(fe) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep", ++ state->device == STV0900 ? "STV0900" : "STV0903", ++ state->demod); ++ ++ mutex_lock(&state->internal->demod_lock); ++ ++ switch (state->demod) { ++ case STV090x_DEMODULATOR_0: ++ /* power off ADC 1 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR1); ++ STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) ++ goto err; ++ /* power off DiSEqC 1 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR2); ++ STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) ++ goto err; ++ ++ /* check whether path 2 is already sleeping, that is when ++ ADC2 is off */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR3); ++ if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0) ++ full_standby = 1; ++ ++ /* stop clocks */ ++ reg = stv090x_read_reg(state, STV090x_STOPCLK1); ++ /* packet delineator 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1); ++ /* ADC 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1); ++ /* FEC clock is shared between the two paths, only stop it ++ when full standby is possible */ ++ if (full_standby) ++ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ /* sampling 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); ++ /* viterbi 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1); ++ /* TS clock is shared between the two paths, only stop it ++ when full standby is possible */ ++ if (full_standby) ++ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_DEMODULATOR_1: ++ /* power off ADC 2 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR3); ++ STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) ++ goto err; ++ /* power off DiSEqC 2 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR4); ++ STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) ++ goto err; ++ ++ /* check whether path 1 is already sleeping, that is when ++ ADC1 is off */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR1); ++ if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0) ++ full_standby = 1; ++ ++ /* stop clocks */ ++ reg = stv090x_read_reg(state, STV090x_STOPCLK1); ++ /* packet delineator 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1); ++ /* ADC 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1); ++ /* FEC clock is shared between the two paths, only stop it ++ when full standby is possible */ ++ if (full_standby) ++ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ /* sampling 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); ++ /* viterbi 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1); ++ /* TS clock is shared between the two paths, only stop it ++ when full standby is possible */ ++ if (full_standby) ++ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ break; ++ ++ default: ++ dprintk(FE_ERROR, 1, "Wrong demodulator!"); ++ break; ++ } ++ ++ if (full_standby) { ++ /* general power off */ ++ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); ++ STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) ++ goto err; ++ } ++ ++ mutex_unlock(&state->internal->demod_lock); ++ return 0; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ mutex_unlock(&state->internal->demod_lock); ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_wakeup(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u32 reg; ++ ++ dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby", ++ state->device == STV0900 ? "STV0900" : "STV0903", ++ state->demod); ++ ++ mutex_lock(&state->internal->demod_lock); ++ ++ /* general power on */ ++ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); ++ STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) ++ goto err; ++ ++ switch (state->demod) { ++ case STV090x_DEMODULATOR_0: ++ /* power on ADC 1 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR1); ++ STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) ++ goto err; ++ /* power on DiSEqC 1 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR2); ++ STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) ++ goto err; ++ ++ /* activate clocks */ ++ reg = stv090x_read_reg(state, STV090x_STOPCLK1); ++ /* packet delineator 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0); ++ /* ADC 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0); ++ /* FEC clock */ ++ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ /* sampling 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0); ++ /* viterbi 1 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0); ++ /* TS clock */ ++ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_DEMODULATOR_1: ++ /* power on ADC 2 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR3); ++ STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) ++ goto err; ++ /* power on DiSEqC 2 */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR4); ++ STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) ++ goto err; ++ ++ /* activate clocks */ ++ reg = stv090x_read_reg(state, STV090x_STOPCLK1); ++ /* packet delineator 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0); ++ /* ADC 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0); ++ /* FEC clock */ ++ STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_STOPCLK2); ++ /* sampling 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0); ++ /* viterbi 2 clock */ ++ STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0); ++ /* TS clock */ ++ STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) ++ goto err; ++ break; ++ ++ default: ++ dprintk(FE_ERROR, 1, "Wrong demodulator!"); ++ break; ++ } ++ ++ mutex_unlock(&state->internal->demod_lock); ++ return 0; ++err: ++ mutex_unlock(&state->internal->demod_lock); ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static void stv090x_release(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ ++ state->internal->num_used--; ++ if (state->internal->num_used <= 0) { ++ ++ dprintk(FE_ERROR, 1, "Actually removing"); ++ ++ remove_dev(state->internal); ++ kfree(state->internal); ++ } ++ ++ kfree(state); ++} ++ ++static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc_mode) ++{ ++ u32 reg = 0; ++ ++ reg = stv090x_read_reg(state, STV090x_GENCFG); ++ ++ switch (ldpc_mode) { ++ case STV090x_DUAL: ++ default: ++ if ((state->demod_mode != STV090x_DUAL) || (STV090x_GETFIELD(reg, DDEMOD_FIELD) != 1)) { ++ /* set LDPC to dual mode */ ++ if (stv090x_write_reg(state, STV090x_GENCFG, 0x1d) < 0) ++ goto err; ++ ++ state->demod_mode = STV090x_DUAL; ++ ++ reg = stv090x_read_reg(state, STV090x_TSTRES0); ++ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); ++ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) ++ goto err; ++ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); ++ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) ++ goto err; ++ ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) ++ goto err; ++ } ++ break; ++ ++ case STV090x_SINGLE: ++ if (stv090x_stop_modcod(state) < 0) ++ goto err; ++ if (stv090x_activate_modcod_single(state) < 0) ++ goto err; ++ ++ if (state->demod == STV090x_DEMODULATOR_1) { ++ if (stv090x_write_reg(state, STV090x_GENCFG, 0x06) < 0) /* path 2 */ ++ goto err; ++ } else { ++ if (stv090x_write_reg(state, STV090x_GENCFG, 0x04) < 0) /* path 1 */ ++ goto err; ++ } ++ ++ reg = stv090x_read_reg(state, STV090x_TSTRES0); ++ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); ++ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) ++ goto err; ++ STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); ++ if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, PDELCTRL1); ++ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x01); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x00); ++ if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) ++ goto err; ++ break; ++ } ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++/* return (Hz), clk in Hz*/ ++static u32 stv090x_get_mclk(struct stv090x_state *state) ++{ ++ const struct stv090x_config *config = state->config; ++ u32 div, reg; ++ u8 ratio; ++ ++ div = stv090x_read_reg(state, STV090x_NCOARSE); ++ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); ++ ratio = STV090x_GETFIELD(reg, SELX1RATIO_FIELD) ? 4 : 6; ++ ++ return (div + 1) * config->xtal / ratio; /* kHz */ ++} ++ ++static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk) ++{ ++ const struct stv090x_config *config = state->config; ++ u32 reg, div, clk_sel; ++ ++ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); ++ clk_sel = ((STV090x_GETFIELD(reg, SELX1RATIO_FIELD) == 1) ? 4 : 6); ++ ++ div = ((clk_sel * mclk) / config->xtal) - 1; ++ ++ reg = stv090x_read_reg(state, STV090x_NCOARSE); ++ STV090x_SETFIELD(reg, M_DIV_FIELD, div); ++ if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0) ++ goto err; ++ ++ state->internal->mclk = stv090x_get_mclk(state); ++ ++ /*Set the DiseqC frequency to 22KHz */ ++ div = state->internal->mclk / 704000; ++ if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0) ++ goto err; ++ if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_set_tspath(struct stv090x_state *state) ++{ ++ u32 reg; ++ ++ if (state->internal->dev_ver >= 0x20) { ++ switch (state->config->ts1_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ stv090x_write_reg(state, STV090x_TSGENERAL, 0x00); ++ break; ++ ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */ ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) ++ goto err; ++ break; ++ } ++ break; ++ ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0a) < 0) ++ goto err; ++ break; ++ } ++ break; ++ } ++ } else { ++ switch (state->config->ts1_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10); ++ break; ++ ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x16); ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) ++ goto err; ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 0); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) ++ goto err; ++ break; ++ } ++ break; ++ ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ default: ++ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14); ++ break; ++ ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x12); ++ break; ++ } ++ break; ++ } ++ } ++ ++ switch (state->config->ts1_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_DVBCI: ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ default: ++ break; ++ } ++ ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_DVBCI: ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); ++ STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); ++ STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); ++ STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (state->config->ts1_clk > 0) { ++ u32 speed; ++ ++ switch (state->config->ts1_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ default: ++ speed = state->internal->mclk / ++ (state->config->ts1_clk / 4); ++ if (speed < 0x08) ++ speed = 0x08; ++ if (speed > 0xFF) ++ speed = 0xFF; ++ break; ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ speed = state->internal->mclk / ++ (state->config->ts1_clk / 32); ++ if (speed < 0x20) ++ speed = 0x20; ++ if (speed > 0xFF) ++ speed = 0xFF; ++ break; ++ } ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) ++ goto err; ++ } ++ ++ if (state->config->ts2_clk > 0) { ++ u32 speed; ++ ++ switch (state->config->ts2_mode) { ++ case STV090x_TSMODE_PARALLEL_PUNCTURED: ++ case STV090x_TSMODE_DVBCI: ++ default: ++ speed = state->internal->mclk / ++ (state->config->ts2_clk / 4); ++ if (speed < 0x08) ++ speed = 0x08; ++ if (speed > 0xFF) ++ speed = 0xFF; ++ break; ++ case STV090x_TSMODE_SERIAL_PUNCTURED: ++ case STV090x_TSMODE_SERIAL_CONTINUOUS: ++ speed = state->internal->mclk / ++ (state->config->ts2_clk / 32); ++ if (speed < 0x20) ++ speed = 0x20; ++ if (speed > 0xFF) ++ speed = 0xFF; ++ break; ++ } ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); ++ STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0) ++ goto err; ++ } ++ ++ reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) ++ goto err; ++ ++ reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); ++ if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_init(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ const struct stv090x_config *config = state->config; ++ u32 reg; ++ ++ if (state->internal->mclk == 0) { ++ /* call tuner init to configure the tuner's clock output ++ divider directly before setting up the master clock of ++ the stv090x. */ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (config->tuner_init) { ++ if (config->tuner_init(fe) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ ++ msleep(5); ++ if (stv090x_write_reg(state, STV090x_SYNTCTRL, ++ 0x20 | config->clk_mode) < 0) ++ goto err; ++ stv090x_get_mclk(state); ++ } ++ ++ if (stv090x_wakeup(fe) < 0) { ++ dprintk(FE_ERROR, 1, "Error waking device"); ++ goto err; ++ } ++ ++ if (stv090x_ldpc_mode(state, state->demod_mode) < 0) ++ goto err; ++ ++ reg = STV090x_READ_DEMOD(state, TNRCFG2); ++ STV090x_SETFIELD_Px(reg, TUN_IQSWAP_FIELD, state->inversion); ++ if (STV090x_WRITE_DEMOD(state, TNRCFG2, reg) < 0) ++ goto err; ++ reg = STV090x_READ_DEMOD(state, DEMOD); ++ STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); ++ if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) ++ goto err; ++ ++ if (stv090x_i2c_gate_ctrl(state, 1) < 0) ++ goto err; ++ ++ if (config->tuner_set_mode) { ++ if (config->tuner_set_mode(fe, TUNER_WAKE) < 0) ++ goto err_gateoff; ++ } ++ ++ if (config->tuner_init) { ++ if (config->tuner_init(fe) < 0) ++ goto err_gateoff; ++ } ++ ++ if (stv090x_i2c_gate_ctrl(state, 0) < 0) ++ goto err; ++ ++ if (stv090x_set_tspath(state) < 0) ++ goto err; ++ ++ return 0; ++ ++err_gateoff: ++ stv090x_i2c_gate_ctrl(state, 0); ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++static int stv090x_setup(struct dvb_frontend *fe) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ const struct stv090x_config *config = state->config; ++ const struct stv090x_reg *stv090x_initval = NULL; ++ const struct stv090x_reg *stv090x_cut20_val = NULL; ++ unsigned long t1_size = 0, t2_size = 0; ++ u32 reg = 0; ++ ++ int i; ++ ++ if (state->device == STV0900) { ++ dprintk(FE_DEBUG, 1, "Initializing STV0900"); ++ stv090x_initval = stv0900_initval; ++ t1_size = ARRAY_SIZE(stv0900_initval); ++ stv090x_cut20_val = stv0900_cut20_val; ++ t2_size = ARRAY_SIZE(stv0900_cut20_val); ++ } else if (state->device == STV0903) { ++ dprintk(FE_DEBUG, 1, "Initializing STV0903"); ++ stv090x_initval = stv0903_initval; ++ t1_size = ARRAY_SIZE(stv0903_initval); ++ stv090x_cut20_val = stv0903_cut20_val; ++ t2_size = ARRAY_SIZE(stv0903_cut20_val); ++ } ++ ++ /* STV090x init */ ++ ++ /* Stop Demod */ ++ if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) ++ goto err; ++ ++ msleep(5); ++ ++ /* Set No Tuner Mode */ ++ if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) ++ goto err; ++ ++ /* I2C repeater OFF */ ++ STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level); ++ if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) ++ goto err; ++ ++ if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */ ++ goto err; ++ msleep(5); ++ if (stv090x_write_reg(state, STV090x_I2CCFG, 0x08) < 0) /* 1/41 oversampling */ ++ goto err; ++ if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) /* enable PLL */ ++ goto err; ++ msleep(5); ++ ++ /* write initval */ ++ dprintk(FE_DEBUG, 1, "Setting up initial values"); ++ for (i = 0; i < t1_size; i++) { ++ if (stv090x_write_reg(state, stv090x_initval[i].addr, stv090x_initval[i].data) < 0) ++ goto err; ++ } ++ ++ state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID); ++ if (state->internal->dev_ver >= 0x20) { ++ if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) ++ goto err; ++ ++ /* write cut20_val*/ ++ dprintk(FE_DEBUG, 1, "Setting up Cut 2.0 initial values"); ++ for (i = 0; i < t2_size; i++) { ++ if (stv090x_write_reg(state, stv090x_cut20_val[i].addr, stv090x_cut20_val[i].data) < 0) ++ goto err; ++ } ++ ++ } else if (state->internal->dev_ver < 0x20) { ++ dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!", ++ state->internal->dev_ver); ++ ++ goto err; ++ } else if (state->internal->dev_ver > 0x30) { ++ /* we shouldn't bail out from here */ ++ dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!", ++ state->internal->dev_ver); ++ } ++ ++ /* ADC1 range */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR1); ++ STV090x_SETFIELD(reg, ADC1_INMODE_FIELD, ++ (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) ++ goto err; ++ ++ /* ADC2 range */ ++ reg = stv090x_read_reg(state, STV090x_TSTTNR3); ++ STV090x_SETFIELD(reg, ADC2_INMODE_FIELD, ++ (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1); ++ if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) ++ goto err; ++ ++ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) ++ goto err; ++ if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) ++ goto err; ++ ++ return 0; ++err: ++ dprintk(FE_ERROR, 1, "I/O error"); ++ return -1; ++} ++ ++int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, ++ u8 xor_value) ++{ ++ struct stv090x_state *state = fe->demodulator_priv; ++ u8 reg = 0; ++ ++ STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir); ++ STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value); ++ STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value); ++ ++ return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); ++} ++EXPORT_SYMBOL(stv090x_set_gpio); ++ ++static struct dvb_frontend_ops stv090x_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, ++ .info = { ++ .name = "STV090x Multistandard", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 0, ++ .frequency_tolerance = 0, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_2G_MODULATION ++ }, ++ ++ .release = stv090x_release, ++ .init = stv090x_init, ++ ++ .sleep = stv090x_sleep, ++ .get_frontend_algo = stv090x_frontend_algo, ++ ++ .diseqc_send_master_cmd = stv090x_send_diseqc_msg, ++ .diseqc_send_burst = stv090x_send_diseqc_burst, ++ .diseqc_recv_slave_reply = stv090x_recv_slave_reply, ++ .set_tone = stv090x_set_tone, ++ ++ .search = stv090x_search, ++ .read_status = stv090x_read_status, ++ .read_ber = stv090x_read_per, ++ .read_signal_strength = stv090x_read_signal_strength, ++ .read_snr = stv090x_read_cnr, ++}; ++ ++ ++struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, ++ struct i2c_adapter *i2c, ++ enum stv090x_demodulator demod) ++{ ++ struct stv090x_state *state = NULL; ++ struct stv090x_dev *temp_int; ++ ++ state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ state->verbose = &verbose; ++ state->config = config; ++ state->i2c = i2c; ++ state->frontend.ops = stv090x_ops; ++ state->frontend.demodulator_priv = state; ++ state->demod = demod; ++ state->demod_mode = config->demod_mode; /* Single or Dual mode */ ++ state->device = config->device; ++ state->rolloff = STV090x_RO_35; /* default */ ++ ++ temp_int = find_dev(state->i2c, ++ state->config->address); ++ ++ if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) { ++ state->internal = temp_int->internal; ++ state->internal->num_used++; ++ dprintk(FE_INFO, 1, "Found Internal Structure!"); ++ } else { ++ state->internal = kmalloc(sizeof(struct stv090x_internal), ++ GFP_KERNEL); ++ if (!state->internal) ++ goto error; ++ temp_int = append_internal(state->internal); ++ if (!temp_int) { ++ kfree(state->internal); ++ goto error; ++ } ++ state->internal->num_used = 1; ++ state->internal->mclk = 0; ++ state->internal->dev_ver = 0; ++ state->internal->i2c_adap = state->i2c; ++ state->internal->i2c_addr = state->config->address; ++ dprintk(FE_INFO, 1, "Create New Internal Structure!"); ++ ++ mutex_init(&state->internal->demod_lock); ++ mutex_init(&state->internal->tuner_lock); ++ ++ if (stv090x_setup(&state->frontend) < 0) { ++ dprintk(FE_ERROR, 1, "Error setting up device"); ++ goto err_remove; ++ } ++ } ++ ++ if (state->internal->dev_ver >= 0x30) ++ state->frontend.ops.info.caps |= FE_CAN_MULTISTREAM; ++ ++ /* workaround for stuck DiSEqC output */ ++ if (config->diseqc_envelope_mode) ++ stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); ++ ++ dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", ++ state->device == STV0900 ? "STV0900" : "STV0903", ++ demod, ++ state->internal->dev_ver); ++ ++ return &state->frontend; ++ ++err_remove: ++ remove_dev(state->internal); ++ kfree(state->internal); ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(stv090x_attach); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stv090x.h b/drivers/media/dvb-frontends/stv090x.h +new file mode 100644 +index 0000000..29cdc2b +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv090x.h +@@ -0,0 +1,134 @@ ++/* ++ STV0900/0903 Multistandard Broadcast Frontend driver ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV090x_H ++#define __STV090x_H ++ ++enum stv090x_demodulator { ++ STV090x_DEMODULATOR_0 = 1, ++ STV090x_DEMODULATOR_1 ++}; ++ ++enum stv090x_device { ++ STV0903 = 0, ++ STV0900, ++}; ++ ++enum stv090x_mode { ++ STV090x_DUAL = 0, ++ STV090x_SINGLE ++}; ++ ++enum stv090x_tsmode { ++ STV090x_TSMODE_SERIAL_PUNCTURED = 1, ++ STV090x_TSMODE_SERIAL_CONTINUOUS, ++ STV090x_TSMODE_PARALLEL_PUNCTURED, ++ STV090x_TSMODE_DVBCI ++}; ++ ++enum stv090x_clkmode { ++ STV090x_CLK_INT = 0, /* Clk i/p = CLKI */ ++ STV090x_CLK_EXT = 2 /* Clk i/p = XTALI */ ++}; ++ ++enum stv090x_i2crpt { ++ STV090x_RPTLEVEL_256 = 0, ++ STV090x_RPTLEVEL_128 = 1, ++ STV090x_RPTLEVEL_64 = 2, ++ STV090x_RPTLEVEL_32 = 3, ++ STV090x_RPTLEVEL_16 = 4, ++ STV090x_RPTLEVEL_8 = 5, ++ STV090x_RPTLEVEL_4 = 6, ++ STV090x_RPTLEVEL_2 = 7, ++}; ++ ++enum stv090x_adc_range { ++ STV090x_ADC_2Vpp = 0, ++ STV090x_ADC_1Vpp = 1 ++}; ++ ++struct stv090x_config { ++ enum stv090x_device device; ++ enum stv090x_mode demod_mode; ++ enum stv090x_clkmode clk_mode; ++ ++ u32 xtal; /* default: 8000000 */ ++ u8 address; /* default: 0x68 */ ++ ++ u8 ts1_mode; ++ u8 ts2_mode; ++ u32 ts1_clk; ++ u32 ts2_clk; ++ ++ u8 ts1_tei : 1; ++ u8 ts2_tei : 1; ++ ++ enum stv090x_i2crpt repeater_level; ++ ++ u8 tuner_bbgain; /* default: 10db */ ++ enum stv090x_adc_range adc1_range; /* default: 2Vpp */ ++ enum stv090x_adc_range adc2_range; /* default: 2Vpp */ ++ ++ bool diseqc_envelope_mode; ++ ++ int (*tuner_init) (struct dvb_frontend *fe); ++ int (*tuner_sleep) (struct dvb_frontend *fe); ++ int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); ++ int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); ++ int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); ++ int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth); ++ int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth); ++ int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain); ++ int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); ++ int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); ++ int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); ++ void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock); ++}; ++ ++#if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, ++ struct i2c_adapter *i2c, ++ enum stv090x_demodulator demod); ++ ++/* dir = 0 -> output, dir = 1 -> input/open-drain */ ++extern int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, ++ u8 dir, u8 value, u8 xor_value); ++ ++#else ++ ++static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, ++ struct i2c_adapter *i2c, ++ enum stv090x_demodulator demod) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++static inline int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, ++ u8 opd, u8 value, u8 xor_value) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return -ENODEV; ++} ++#endif /* CONFIG_DVB_STV090x */ ++ ++#endif /* __STV090x_H */ +diff --git a/drivers/media/dvb-frontends/stv090x_priv.h b/drivers/media/dvb-frontends/stv090x_priv.h +new file mode 100644 +index 0000000..5b780c8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv090x_priv.h +@@ -0,0 +1,279 @@ ++/* ++ STV0900/0903 Multistandard Broadcast Frontend driver ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV090x_PRIV_H ++#define __STV090x_PRIV_H ++ ++#include "dvb_frontend.h" ++ ++#define FE_ERROR 0 ++#define FE_NOTICE 1 ++#define FE_INFO 2 ++#define FE_DEBUG 3 ++#define FE_DEBUGREG 4 ++ ++#define dprintk(__y, __z, format, arg...) do { \ ++ if (__z) { \ ++ if ((verbose > FE_ERROR) && (verbose > __y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_NOTICE) && (verbose > __y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_INFO) && (verbose > __y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_DEBUG) && (verbose > __y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (verbose > __y) \ ++ printk(format, ##arg); \ ++ } \ ++} while (0) ++ ++#define STV090x_READ_DEMOD(__state, __reg) (( \ ++ (__state)->demod == STV090x_DEMODULATOR_1) ? \ ++ stv090x_read_reg(__state, STV090x_P2_##__reg) : \ ++ stv090x_read_reg(__state, STV090x_P1_##__reg)) ++ ++#define STV090x_WRITE_DEMOD(__state, __reg, __data) (( \ ++ (__state)->demod == STV090x_DEMODULATOR_1) ? \ ++ stv090x_write_reg(__state, STV090x_P2_##__reg, __data) :\ ++ stv090x_write_reg(__state, STV090x_P1_##__reg, __data)) ++ ++#define STV090x_ADDR_OFFST(__state, __x) (( \ ++ (__state->demod) == STV090x_DEMODULATOR_1) ? \ ++ STV090x_P1_##__x : \ ++ STV090x_P2_##__x) ++ ++ ++#define STV090x_SETFIELD(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_##bitf) - 1) <<\ ++ STV090x_OFFST_##bitf))) | \ ++ (val << STV090x_OFFST_##bitf)) ++ ++#define STV090x_GETFIELD(val, bitf) ((val >> STV090x_OFFST_##bitf) & ((1 << STV090x_WIDTH_##bitf) - 1)) ++ ++ ++#define STV090x_SETFIELD_Px(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_Px_##bitf) - 1) <<\ ++ STV090x_OFFST_Px_##bitf))) | \ ++ (val << STV090x_OFFST_Px_##bitf)) ++ ++#define STV090x_GETFIELD_Px(val, bitf) ((val >> STV090x_OFFST_Px_##bitf) & ((1 << STV090x_WIDTH_Px_##bitf) - 1)) ++ ++#define MAKEWORD16(__a, __b) (((__a) << 8) | (__b)) ++ ++#define MSB(__x) ((__x >> 8) & 0xff) ++#define LSB(__x) (__x & 0xff) ++ ++ ++#define STV090x_IQPOWER_THRESHOLD 30 ++#define STV090x_SEARCH_AGC2_TH_CUT20 700 ++#define STV090x_SEARCH_AGC2_TH_CUT30 1400 ++ ++#define STV090x_SEARCH_AGC2_TH(__ver) \ ++ ((__ver <= 0x20) ? \ ++ STV090x_SEARCH_AGC2_TH_CUT20 : \ ++ STV090x_SEARCH_AGC2_TH_CUT30) ++ ++enum stv090x_signal_state { ++ STV090x_NOAGC1, ++ STV090x_NOCARRIER, ++ STV090x_NODATA, ++ STV090x_DATAOK, ++ STV090x_RANGEOK, ++ STV090x_OUTOFRANGE ++}; ++ ++enum stv090x_fec { ++ STV090x_PR12 = 0, ++ STV090x_PR23, ++ STV090x_PR34, ++ STV090x_PR45, ++ STV090x_PR56, ++ STV090x_PR67, ++ STV090x_PR78, ++ STV090x_PR89, ++ STV090x_PR910, ++ STV090x_PRERR ++}; ++ ++enum stv090x_modulation { ++ STV090x_QPSK, ++ STV090x_8PSK, ++ STV090x_16APSK, ++ STV090x_32APSK, ++ STV090x_UNKNOWN ++}; ++ ++enum stv090x_frame { ++ STV090x_LONG_FRAME, ++ STV090x_SHORT_FRAME ++}; ++ ++enum stv090x_pilot { ++ STV090x_PILOTS_OFF, ++ STV090x_PILOTS_ON ++}; ++ ++enum stv090x_rolloff { ++ STV090x_RO_35, ++ STV090x_RO_25, ++ STV090x_RO_20 ++}; ++ ++enum stv090x_inversion { ++ STV090x_IQ_AUTO, ++ STV090x_IQ_NORMAL, ++ STV090x_IQ_SWAP ++}; ++ ++enum stv090x_modcod { ++ STV090x_DUMMY_PLF = 0, ++ STV090x_QPSK_14, ++ STV090x_QPSK_13, ++ STV090x_QPSK_25, ++ STV090x_QPSK_12, ++ STV090x_QPSK_35, ++ STV090x_QPSK_23, ++ STV090x_QPSK_34, ++ STV090x_QPSK_45, ++ STV090x_QPSK_56, ++ STV090x_QPSK_89, ++ STV090x_QPSK_910, ++ STV090x_8PSK_35, ++ STV090x_8PSK_23, ++ STV090x_8PSK_34, ++ STV090x_8PSK_56, ++ STV090x_8PSK_89, ++ STV090x_8PSK_910, ++ STV090x_16APSK_23, ++ STV090x_16APSK_34, ++ STV090x_16APSK_45, ++ STV090x_16APSK_56, ++ STV090x_16APSK_89, ++ STV090x_16APSK_910, ++ STV090x_32APSK_34, ++ STV090x_32APSK_45, ++ STV090x_32APSK_56, ++ STV090x_32APSK_89, ++ STV090x_32APSK_910, ++ STV090x_MODCODE_UNKNOWN ++}; ++ ++enum stv090x_search { ++ STV090x_SEARCH_DSS = 0, ++ STV090x_SEARCH_DVBS1, ++ STV090x_SEARCH_DVBS2, ++ STV090x_SEARCH_AUTO ++}; ++ ++enum stv090x_algo { ++ STV090x_BLIND_SEARCH, ++ STV090x_COLD_SEARCH, ++ STV090x_WARM_SEARCH ++}; ++ ++enum stv090x_delsys { ++ STV090x_ERROR = 0, ++ STV090x_DVBS1 = 1, ++ STV090x_DVBS2, ++ STV090x_DSS ++}; ++ ++struct stv090x_long_frame_crloop { ++ enum stv090x_modcod modcod; ++ ++ u8 crl_pilots_on_2; ++ u8 crl_pilots_off_2; ++ u8 crl_pilots_on_5; ++ u8 crl_pilots_off_5; ++ u8 crl_pilots_on_10; ++ u8 crl_pilots_off_10; ++ u8 crl_pilots_on_20; ++ u8 crl_pilots_off_20; ++ u8 crl_pilots_on_30; ++ u8 crl_pilots_off_30; ++}; ++ ++struct stv090x_short_frame_crloop { ++ enum stv090x_modulation modulation; ++ ++ u8 crl_2; /* SR < 3M */ ++ u8 crl_5; /* 3 < SR <= 7M */ ++ u8 crl_10; /* 7 < SR <= 15M */ ++ u8 crl_20; /* 10 < SR <= 25M */ ++ u8 crl_30; /* 10 < SR <= 45M */ ++}; ++ ++struct stv090x_reg { ++ u16 addr; ++ u8 data; ++}; ++ ++struct stv090x_tab { ++ s32 real; ++ s32 read; ++}; ++ ++struct stv090x_internal { ++ struct i2c_adapter *i2c_adap; ++ u8 i2c_addr; ++ ++ struct mutex demod_lock; /* Lock access to shared register */ ++ struct mutex tuner_lock; /* Lock access to tuners */ ++ s32 mclk; /* Masterclock Divider factor */ ++ u32 dev_ver; ++ ++ int num_used; ++}; ++ ++struct stv090x_state { ++ enum stv090x_device device; ++ enum stv090x_demodulator demod; ++ enum stv090x_mode demod_mode; ++ struct stv090x_internal *internal; ++ ++ struct i2c_adapter *i2c; ++ const struct stv090x_config *config; ++ struct dvb_frontend frontend; ++ ++ u32 *verbose; /* Cached module verbosity */ ++ ++ enum stv090x_delsys delsys; ++ enum stv090x_fec fec; ++ enum stv090x_modulation modulation; ++ enum stv090x_modcod modcod; ++ enum stv090x_search search_mode; ++ enum stv090x_frame frame_len; ++ enum stv090x_pilot pilots; ++ enum stv090x_rolloff rolloff; ++ enum stv090x_inversion inversion; ++ enum stv090x_algo algo; ++ ++ u32 frequency; ++ u32 srate; ++ ++ s32 tuner_bw; ++ ++ s32 search_range; ++ ++ s32 DemodTimeout; ++ s32 FecTimeout; ++}; ++ ++#endif /* __STV090x_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/stv090x_reg.h b/drivers/media/dvb-frontends/stv090x_reg.h +new file mode 100644 +index 0000000..93741ee +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv090x_reg.h +@@ -0,0 +1,2371 @@ ++/* ++ STV0900/0903 Multistandard Broadcast Frontend driver ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV090x_REG_H ++#define __STV090x_REG_H ++ ++#define STV090x_MID 0xf100 ++#define STV090x_OFFST_MCHIP_IDENT_FIELD 4 ++#define STV090x_WIDTH_MCHIP_IDENT_FIELD 4 ++#define STV090x_OFFST_MRELEASE_FIELD 0 ++#define STV090x_WIDTH_MRELEASE_FIELD 4 ++ ++#define STV090x_DACR1 0xf113 ++#define STV090x_OFFST_DACR1_MODE_FIELD 5 ++#define STV090x_WIDTH_DACR1_MODE_FIELD 3 ++#define STV090x_OFFST_DACR1_VALUE_FIELD 0 ++#define STV090x_WIDTH_DACR1_VALUE_FIELD 4 ++ ++#define STV090x_DACR2 0xf114 ++#define STV090x_OFFST_DACR2_VALUE_FIELD 0 ++#define STV090x_WIDTH_DACR2_VALUE_FIELD 8 ++ ++#define STV090x_OUTCFG 0xf11c ++#define STV090x_OFFST_OUTSERRS1_HZ_FIELD 6 ++#define STV090x_WIDTH_OUTSERRS1_HZ_FIELD 1 ++#define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5 ++#define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1 ++#define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4 ++#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1 ++#define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3 ++#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 ++ ++#define STV090x_MODECFG 0xf11d ++ ++#define STV090x_IRQSTATUS3 0xf120 ++#define STV090x_OFFST_SPLL_LOCK_FIELD 5 ++#define STV090x_WIDTH_SPLL_LOCK_FIELD 1 ++#define STV090x_OFFST_SSTREAM_LCK_3_FIELD 4 ++#define STV090x_WIDTH_SSTREAM_LCK_3_FIELD 1 ++#define STV090x_OFFST_SSTREAM_LCK_2_FIELD 3 ++#define STV090x_WIDTH_SSTREAM_LCK_2_FIELD 1 ++#define STV090x_OFFST_SSTREAM_LCK_1_FIELD 2 ++#define STV090x_WIDTH_SSTREAM_LCK_1_FIELD 1 ++#define STV090x_OFFST_SDVBS1_PRF_2_FIELD 1 ++#define STV090x_WIDTH_SDVBS1_PRF_2_FIELD 1 ++#define STV090x_OFFST_SDVBS1_PRF_1_FIELD 0 ++#define STV090x_WIDTH_SDVBS1_PRF_1_FIELD 1 ++ ++#define STV090x_IRQSTATUS2 0xf121 ++#define STV090x_OFFST_SSPY_ENDSIM_3_FIELD 7 ++#define STV090x_WIDTH_SSPY_ENDSIM_3_FIELD 1 ++#define STV090x_OFFST_SSPY_ENDSIM_2_FIELD 6 ++#define STV090x_WIDTH_SSPY_ENDSIM_2_FIELD 1 ++#define STV090x_OFFST_SSPY_ENDSIM_1_FIELD 5 ++#define STV090x_WIDTH_SSPY_ENDSIM_1_FIELD 1 ++#define STV090x_OFFST_SPKTDEL_ERROR_2_FIELD 4 ++#define STV090x_WIDTH_SPKTDEL_ERROR_2_FIELD 1 ++#define STV090x_OFFST_SPKTDEL_LOCKB_2_FIELD 3 ++#define STV090x_WIDTH_SPKTDEL_LOCKB_2_FIELD 1 ++#define STV090x_OFFST_SPKTDEL_LOCK_2_FIELD 2 ++#define STV090x_WIDTH_SPKTDEL_LOCK_2_FIELD 1 ++#define STV090x_OFFST_SPKTDEL_ERROR_1_FIELD 1 ++#define STV090x_WIDTH_SPKTDEL_ERROR_1_FIELD 1 ++#define STV090x_OFFST_SPKTDEL_LOCKB_1_FIELD 0 ++#define STV090x_WIDTH_SPKTDEL_LOCKB_1_FIELD 1 ++ ++#define STV090x_IRQSTATUS1 0xf122 ++#define STV090x_OFFST_SPKTDEL_LOCK_1_FIELD 7 ++#define STV090x_WIDTH_SPKTDEL_LOCK_1_FIELD 1 ++#define STV090x_OFFST_SDEMOD_LOCKB_2_FIELD 2 ++#define STV090x_WIDTH_SDEMOD_LOCKB_2_FIELD 1 ++#define STV090x_OFFST_SDEMOD_LOCK_2_FIELD 1 ++#define STV090x_WIDTH_SDEMOD_LOCK_2_FIELD 1 ++#define STV090x_OFFST_SDEMOD_IRQ_2_FIELD 0 ++#define STV090x_WIDTH_SDEMOD_IRQ_2_FIELD 1 ++ ++#define STV090x_IRQSTATUS0 0xf123 ++#define STV090x_OFFST_SDEMOD_LOCKB_1_FIELD 7 ++#define STV090x_WIDTH_SDEMOD_LOCKB_1_FIELD 1 ++#define STV090x_OFFST_SDEMOD_LOCK_1_FIELD 6 ++#define STV090x_WIDTH_SDEMOD_LOCK_1_FIELD 1 ++#define STV090x_OFFST_SDEMOD_IRQ_1_FIELD 5 ++#define STV090x_WIDTH_SDEMOD_IRQ_1_FIELD 1 ++#define STV090x_OFFST_SBCH_ERRFLAG_FIELD 4 ++#define STV090x_WIDTH_SBCH_ERRFLAG_FIELD 1 ++#define STV090x_OFFST_SDISEQC2RX_IRQ_FIELD 3 ++#define STV090x_WIDTH_SDISEQC2RX_IRQ_FIELD 1 ++#define STV090x_OFFST_SDISEQC2TX_IRQ_FIELD 2 ++#define STV090x_WIDTH_SDISEQC2TX_IRQ_FIELD 1 ++#define STV090x_OFFST_SDISEQC1RX_IRQ_FIELD 1 ++#define STV090x_WIDTH_SDISEQC1RX_IRQ_FIELD 1 ++#define STV090x_OFFST_SDISEQC1TX_IRQ_FIELD 0 ++#define STV090x_WIDTH_SDISEQC1TX_IRQ_FIELD 1 ++ ++#define STV090x_IRQMASK3 0xf124 ++#define STV090x_OFFST_MPLL_LOCK_FIELD 5 ++#define STV090x_WIDTH_MPLL_LOCK_FIELD 1 ++#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4 ++#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1 ++#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3 ++#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1 ++#define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2 ++#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1 ++#define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1 ++#define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1 ++#define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0 ++#define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1 ++ ++#define STV090x_IRQMASK2 0xf125 ++#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7 ++#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1 ++#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6 ++#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1 ++#define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5 ++#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1 ++#define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4 ++#define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1 ++#define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3 ++#define STV090x_WIDTH_MPKTDEL_LOCKB_2_FIELD 1 ++#define STV090x_OFFST_MPKTDEL_LOCK_2_FIELD 2 ++#define STV090x_WIDTH_MPKTDEL_LOCK_2_FIELD 1 ++#define STV090x_OFFST_MPKTDEL_ERROR_1_FIELD 1 ++#define STV090x_WIDTH_MPKTDEL_ERROR_1_FIELD 1 ++#define STV090x_OFFST_MPKTDEL_LOCKB_1_FIELD 0 ++#define STV090x_WIDTH_MPKTDEL_LOCKB_1_FIELD 1 ++ ++#define STV090x_IRQMASK1 0xf126 ++#define STV090x_OFFST_MPKTDEL_LOCK_1_FIELD 7 ++#define STV090x_WIDTH_MPKTDEL_LOCK_1_FIELD 1 ++#define STV090x_OFFST_MEXTPINB2_FIELD 6 ++#define STV090x_WIDTH_MEXTPINB2_FIELD 1 ++#define STV090x_OFFST_MEXTPIN2_FIELD 5 ++#define STV090x_WIDTH_MEXTPIN2_FIELD 1 ++#define STV090x_OFFST_MEXTPINB1_FIELD 4 ++#define STV090x_WIDTH_MEXTPINB1_FIELD 1 ++#define STV090x_OFFST_MEXTPIN1_FIELD 3 ++#define STV090x_WIDTH_MEXTPIN1_FIELD 1 ++#define STV090x_OFFST_MDEMOD_LOCKB_2_FIELD 2 ++#define STV090x_WIDTH_MDEMOD_LOCKB_2_FIELD 1 ++#define STV090x_OFFST_MDEMOD_LOCK_2_FIELD 1 ++#define STV090x_WIDTH_MDEMOD_LOCK_2_FIELD 1 ++#define STV090x_OFFST_MDEMOD_IRQ_2_FIELD 0 ++#define STV090x_WIDTH_MDEMOD_IRQ_2_FIELD 1 ++ ++#define STV090x_IRQMASK0 0xf127 ++#define STV090x_OFFST_MDEMOD_LOCKB_1_FIELD 7 ++#define STV090x_WIDTH_MDEMOD_LOCKB_1_FIELD 1 ++#define STV090x_OFFST_MDEMOD_LOCK_1_FIELD 6 ++#define STV090x_WIDTH_MDEMOD_LOCK_1_FIELD 1 ++#define STV090x_OFFST_MDEMOD_IRQ_1_FIELD 5 ++#define STV090x_WIDTH_MDEMOD_IRQ_1_FIELD 1 ++#define STV090x_OFFST_MBCH_ERRFLAG_FIELD 4 ++#define STV090x_WIDTH_MBCH_ERRFLAG_FIELD 1 ++#define STV090x_OFFST_MDISEQC2RX_IRQ_FIELD 3 ++#define STV090x_WIDTH_MDISEQC2RX_IRQ_FIELD 1 ++#define STV090x_OFFST_MDISEQC2TX_IRQ_FIELD 2 ++#define STV090x_WIDTH_MDISEQC2TX_IRQ_FIELD 1 ++#define STV090x_OFFST_MDISEQC1RX_IRQ_FIELD 1 ++#define STV090x_WIDTH_MDISEQC1RX_IRQ_FIELD 1 ++#define STV090x_OFFST_MDISEQC1TX_IRQ_FIELD 0 ++#define STV090x_WIDTH_MDISEQC1TX_IRQ_FIELD 1 ++ ++#define STV090x_I2CCFG 0xf129 ++#define STV090x_OFFST_12C_FASTMODE_FIELD 3 ++#define STV090x_WIDTH_12C_FASTMODE_FIELD 1 ++#define STV090x_OFFST_12CADDR_INC_FIELD 0 ++#define STV090x_WIDTH_12CADDR_INC_FIELD 2 ++ ++#define STV090x_Px_I2CRPT(__x) (0xf12a + (__x - 1) * 0x1) ++#define STV090x_P1_I2CRPT STV090x_Px_I2CRPT(1) ++#define STV090x_P2_I2CRPT STV090x_Px_I2CRPT(2) ++#define STV090x_OFFST_Px_I2CT_ON_FIELD 7 ++#define STV090x_WIDTH_Px_I2CT_ON_FIELD 1 ++#define STV090x_OFFST_Px_ENARPT_LEVEL_FIELD 4 ++#define STV090x_WIDTH_Px_ENARPT_LEVEL_FIELD 3 ++#define STV090x_OFFST_Px_SCLT_DELAY_FIELD 3 ++#define STV090x_WIDTH_Px_SCLT_DELAY_FIELD 1 ++#define STV090x_OFFST_Px_STOP_ENABLE_FIELD 2 ++#define STV090x_WIDTH_Px_STOP_ENABLE_FIELD 1 ++#define STV090x_OFFST_Px_STOP_SDAT2SDA_FIELD 1 ++#define STV090x_WIDTH_Px_STOP_SDAT2SDA_FIELD 1 ++ ++#define STV090x_CLKI2CFG 0xf140 ++#define STV090x_OFFST_CLKI2_OPD_FIELD 7 ++#define STV090x_WIDTH_CLKI2_OPD_FIELD 1 ++#define STV090x_OFFST_CLKI2_CONFIG_FIELD 1 ++#define STV090x_WIDTH_CLKI2_CONFIG_FIELD 6 ++#define STV090x_OFFST_CLKI2_XOR_FIELD 0 ++#define STV090x_WIDTH_CLKI2_XOR_FIELD 1 ++ ++#define STV090x_GPIOxCFG(__x) (0xf141 + (__x - 1)) ++#define STV090x_GPIO1CFG STV090x_GPIOxCFG(1) ++#define STV090x_GPIO2CFG STV090x_GPIOxCFG(2) ++#define STV090x_GPIO3CFG STV090x_GPIOxCFG(3) ++#define STV090x_GPIO4CFG STV090x_GPIOxCFG(4) ++#define STV090x_GPIO5CFG STV090x_GPIOxCFG(5) ++#define STV090x_GPIO6CFG STV090x_GPIOxCFG(6) ++#define STV090x_GPIO7CFG STV090x_GPIOxCFG(7) ++#define STV090x_GPIO8CFG STV090x_GPIOxCFG(8) ++#define STV090x_GPIO9CFG STV090x_GPIOxCFG(9) ++#define STV090x_GPIO10CFG STV090x_GPIOxCFG(10) ++#define STV090x_GPIO11CFG STV090x_GPIOxCFG(11) ++#define STV090x_GPIO12CFG STV090x_GPIOxCFG(12) ++#define STV090x_GPIO13CFG STV090x_GPIOxCFG(13) ++#define STV090x_OFFST_GPIOx_OPD_FIELD 7 ++#define STV090x_WIDTH_GPIOx_OPD_FIELD 1 ++#define STV090x_OFFST_GPIOx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_GPIOx_CONFIG_FIELD 6 ++#define STV090x_OFFST_GPIOx_XOR_FIELD 0 ++#define STV090x_WIDTH_GPIOx_XOR_FIELD 1 ++ ++#define STV090x_CSxCFG(__x) (0xf14e + __x * 0x1) ++#define STV090x_CS0CFG STV090x_CSxCFG(0) ++#define STV090x_CS1CFG STV090x_CSxCFG(1) ++#define STV090x_OFFST_CSX_OPD_FIELD 7 ++#define STV090x_WIDTH_CSX_OPD_FIELD 1 ++#define STV090x_OFFST_CSX_CONFIG_FIELD 1 ++#define STV090x_WIDTH_CSX_CONFIG_FIELD 6 ++#define STV090x_OFFST_CSX_XOR_FIELD 0 ++#define STV090x_WIDTH_CSX_XOR_FIELD 1 ++ ++ ++#define STV090x_STDBYCFG 0xf150 ++#define STV090x_OFFST_STDBY_OPD_FIELD 7 ++#define STV090x_WIDTH_STDBY_OPD_FIELD 1 ++#define STV090x_OFFST_STDBY_CONFIG_FIELD 1 ++#define STV090x_WIDTH_STDBY_CONFIG_FIELD 6 ++#define STV090x_OFFST_STDBY_XOR_FIELD 0 ++#define STV090x_WIDTH_STDBY_XOR_FIELD 1 ++ ++#define STV090x_DIRCLKCFG 0xf151 ++#define STV090x_OFFST_DIRCLK_OPD_FIELD 7 ++#define STV090x_WIDTH_DIRCLK_OPD_FIELD 1 ++#define STV090x_OFFST_DIRCLK_CONFIG_FIELD 1 ++#define STV090x_WIDTH_DIRCLK_CONFIG_FIELD 6 ++#define STV090x_OFFST_DIRCLK_XOR_FIELD 0 ++#define STV090x_WIDTH_DIRCLK_XOR_FIELD 1 ++ ++ ++#define STV090x_AGCRFxCFG(__x) (0xf152 + (__x - 1) * 0x4) ++#define STV090x_AGCRF1CFG STV090x_AGCRFxCFG(1) ++#define STV090x_AGCRF2CFG STV090x_AGCRFxCFG(2) ++#define STV090x_OFFST_AGCRFx_OPD_FIELD 7 ++#define STV090x_WIDTH_AGCRFx_OPD_FIELD 1 ++#define STV090x_OFFST_AGCRFx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_AGCRFx_CONFIG_FIELD 6 ++#define STV090x_OFFST_AGCRFx_XOR_FIELD 0 ++#define STV090x_WIDTH_AGCRFx_XOR_FIELD 1 ++ ++#define STV090x_SDATxCFG(__x) (0xf153 + (__x - 1) * 0x4) ++#define STV090x_SDAT1CFG STV090x_SDATxCFG(1) ++#define STV090x_SDAT2CFG STV090x_SDATxCFG(2) ++#define STV090x_OFFST_SDATx_OPD_FIELD 7 ++#define STV090x_WIDTH_SDATx_OPD_FIELD 1 ++#define STV090x_OFFST_SDATx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_SDATx_CONFIG_FIELD 6 ++#define STV090x_OFFST_SDATx_XOR_FIELD 0 ++#define STV090x_WIDTH_SDATx_XOR_FIELD 1 ++ ++#define STV090x_SCLTxCFG(__x) (0xf154 + (__x - 1) * 0x4) ++#define STV090x_SCLT1CFG STV090x_SCLTxCFG(1) ++#define STV090x_SCLT2CFG STV090x_SCLTxCFG(2) ++#define STV090x_OFFST_SCLTx_OPD_FIELD 7 ++#define STV090x_WIDTH_SCLTx_OPD_FIELD 1 ++#define STV090x_OFFST_SCLTx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_SCLTx_CONFIG_FIELD 6 ++#define STV090x_OFFST_SCLTx_XOR_FIELD 0 ++#define STV090x_WIDTH_SCLTx_XOR_FIELD 1 ++ ++#define STV090x_DISEQCOxCFG(__x) (0xf155 + (__x - 1) * 0x4) ++#define STV090x_DISEQCO1CFG STV090x_DISEQCOxCFG(1) ++#define STV090x_DISEQCO2CFG STV090x_DISEQCOxCFG(2) ++#define STV090x_OFFST_DISEQCOx_OPD_FIELD 7 ++#define STV090x_WIDTH_DISEQCOx_OPD_FIELD 1 ++#define STV090x_OFFST_DISEQCOx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_DISEQCOx_CONFIG_FIELD 6 ++#define STV090x_OFFST_DISEQCOx_XOR_FIELD 0 ++#define STV090x_WIDTH_DISEQCOx_XOR_FIELD 1 ++ ++#define STV090x_CLKOUT27CFG 0xf15a ++#define STV090x_OFFST_CLKOUT27_OPD_FIELD 7 ++#define STV090x_WIDTH_CLKOUT27_OPD_FIELD 1 ++#define STV090x_OFFST_CLKOUT27_CONFIG_FIELD 1 ++#define STV090x_WIDTH_CLKOUT27_CONFIG_FIELD 6 ++#define STV090x_OFFST_CLKOUT27_XOR_FIELD 0 ++#define STV090x_WIDTH_CLKOUT27_XOR_FIELD 1 ++ ++#define STV090x_ERRORxCFG(__x) (0xf15b + (__x - 1) * 0x5) ++#define STV090x_ERROR1CFG STV090x_ERRORxCFG(1) ++#define STV090x_ERROR2CFG STV090x_ERRORxCFG(2) ++#define STV090x_ERROR3CFG STV090x_ERRORxCFG(3) ++#define STV090x_OFFST_ERRORx_OPD_FIELD 7 ++#define STV090x_WIDTH_ERRORx_OPD_FIELD 1 ++#define STV090x_OFFST_ERRORx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_ERRORx_CONFIG_FIELD 6 ++#define STV090x_OFFST_ERRORx_XOR_FIELD 0 ++#define STV090x_WIDTH_ERRORx_XOR_FIELD 1 ++ ++#define STV090x_DPNxCFG(__x) (0xf15c + (__x - 1) * 0x5) ++#define STV090x_DPN1CFG STV090x_DPNxCFG(1) ++#define STV090x_DPN2CFG STV090x_DPNxCFG(2) ++#define STV090x_DPN3CFG STV090x_DPNxCFG(3) ++#define STV090x_OFFST_DPNx_OPD_FIELD 7 ++#define STV090x_WIDTH_DPNx_OPD_FIELD 1 ++#define STV090x_OFFST_DPNx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_DPNx_CONFIG_FIELD 6 ++#define STV090x_OFFST_DPNx_XOR_FIELD 0 ++#define STV090x_WIDTH_DPNx_XOR_FIELD 1 ++ ++#define STV090x_STROUTxCFG(__x) (0xf15d + (__x - 1) * 0x5) ++#define STV090x_STROUT1CFG STV090x_STROUTxCFG(1) ++#define STV090x_STROUT2CFG STV090x_STROUTxCFG(2) ++#define STV090x_STROUT3CFG STV090x_STROUTxCFG(3) ++#define STV090x_OFFST_STROUTx_OPD_FIELD 7 ++#define STV090x_WIDTH_STROUTx_OPD_FIELD 1 ++#define STV090x_OFFST_STROUTx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_STROUTx_CONFIG_FIELD 6 ++#define STV090x_OFFST_STROUTx_XOR_FIELD 0 ++#define STV090x_WIDTH_STROUTx_XOR_FIELD 1 ++ ++#define STV090x_CLKOUTxCFG(__x) (0xf15e + (__x - 1) * 0x5) ++#define STV090x_CLKOUT1CFG STV090x_CLKOUTxCFG(1) ++#define STV090x_CLKOUT2CFG STV090x_CLKOUTxCFG(2) ++#define STV090x_CLKOUT3CFG STV090x_CLKOUTxCFG(3) ++#define STV090x_OFFST_CLKOUTx_OPD_FIELD 7 ++#define STV090x_WIDTH_CLKOUTx_OPD_FIELD 1 ++#define STV090x_OFFST_CLKOUTx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_CLKOUTx_CONFIG_FIELD 6 ++#define STV090x_OFFST_CLKOUTx_XOR_FIELD 0 ++#define STV090x_WIDTH_CLKOUTx_XOR_FIELD 1 ++ ++#define STV090x_DATAxCFG(__x) (0xf15f + (__x - 71) * 0x5) ++#define STV090x_DATA71CFG STV090x_DATAxCFG(71) ++#define STV090x_DATA72CFG STV090x_DATAxCFG(72) ++#define STV090x_DATA73CFG STV090x_DATAxCFG(73) ++#define STV090x_OFFST_DATAx_OPD_FIELD 7 ++#define STV090x_WIDTH_DATAx_OPD_FIELD 1 ++#define STV090x_OFFST_DATAx_CONFIG_FIELD 1 ++#define STV090x_WIDTH_DATAx_CONFIG_FIELD 6 ++#define STV090x_OFFST_DATAx_XOR_FIELD 0 ++#define STV090x_WIDTH_DATAx_XOR_FIELD 1 ++ ++#define STV090x_NCOARSE 0xf1b3 ++#define STV090x_OFFST_M_DIV_FIELD 0 ++#define STV090x_WIDTH_M_DIV_FIELD 8 ++ ++#define STV090x_SYNTCTRL 0xf1b6 ++#define STV090x_OFFST_STANDBY_FIELD 7 ++#define STV090x_WIDTH_STANDBY_FIELD 1 ++#define STV090x_OFFST_BYPASSPLLCORE_FIELD 6 ++#define STV090x_WIDTH_BYPASSPLLCORE_FIELD 1 ++#define STV090x_OFFST_SELX1RATIO_FIELD 5 ++#define STV090x_WIDTH_SELX1RATIO_FIELD 1 ++#define STV090x_OFFST_STOP_PLL_FIELD 3 ++#define STV090x_WIDTH_STOP_PLL_FIELD 1 ++#define STV090x_OFFST_BYPASSPLLFSK_FIELD 2 ++#define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1 ++#define STV090x_OFFST_SELOSCI_FIELD 1 ++#define STV090x_WIDTH_SELOSCI_FIELD 1 ++#define STV090x_OFFST_BYPASSPLLADC_FIELD 0 ++#define STV090x_WIDTH_BYPASSPLLADC_FIELD 1 ++ ++#define STV090x_FILTCTRL 0xf1b7 ++#define STV090x_OFFST_INV_CLK135_FIELD 7 ++#define STV090x_WIDTH_INV_CLK135_FIELD 1 ++#define STV090x_OFFST_SEL_FSKCKDIV_FIELD 2 ++#define STV090x_WIDTH_SEL_FSKCKDIV_FIELD 1 ++#define STV090x_OFFST_INV_CLKFSK_FIELD 1 ++#define STV090x_WIDTH_INV_CLKFSK_FIELD 1 ++#define STV090x_OFFST_BYPASS_APPLI_FIELD 0 ++#define STV090x_WIDTH_BYPASS_APPLI_FIELD 1 ++ ++#define STV090x_PLLSTAT 0xf1b8 ++#define STV090x_OFFST_PLLLOCK_FIELD 0 ++#define STV090x_WIDTH_PLLLOCK_FIELD 1 ++ ++#define STV090x_STOPCLK1 0xf1c2 ++#define STV090x_OFFST_STOP_CLKPKDT2_FIELD 6 ++#define STV090x_WIDTH_STOP_CLKPKDT2_FIELD 1 ++#define STV090x_OFFST_STOP_CLKPKDT1_FIELD 5 ++#define STV090x_WIDTH_STOP_CLKPKDT1_FIELD 1 ++#define STV090x_OFFST_STOP_CLKFEC_FIELD 4 ++#define STV090x_WIDTH_STOP_CLKFEC_FIELD 1 ++#define STV090x_OFFST_STOP_CLKADCI2_FIELD 3 ++#define STV090x_WIDTH_STOP_CLKADCI2_FIELD 1 ++#define STV090x_OFFST_INV_CLKADCI2_FIELD 2 ++#define STV090x_WIDTH_INV_CLKADCI2_FIELD 1 ++#define STV090x_OFFST_STOP_CLKADCI1_FIELD 1 ++#define STV090x_WIDTH_STOP_CLKADCI1_FIELD 1 ++#define STV090x_OFFST_INV_CLKADCI1_FIELD 0 ++#define STV090x_WIDTH_INV_CLKADCI1_FIELD 1 ++ ++#define STV090x_STOPCLK2 0xf1c3 ++#define STV090x_OFFST_STOP_CLKSAMP2_FIELD 4 ++#define STV090x_WIDTH_STOP_CLKSAMP2_FIELD 1 ++#define STV090x_OFFST_STOP_CLKSAMP1_FIELD 3 ++#define STV090x_WIDTH_STOP_CLKSAMP1_FIELD 1 ++#define STV090x_OFFST_STOP_CLKVIT2_FIELD 2 ++#define STV090x_WIDTH_STOP_CLKVIT2_FIELD 1 ++#define STV090x_OFFST_STOP_CLKVIT1_FIELD 1 ++#define STV090x_WIDTH_STOP_CLKVIT1_FIELD 1 ++#define STV090x_OFFST_STOP_CLKTS_FIELD 0 ++#define STV090x_WIDTH_STOP_CLKTS_FIELD 1 ++ ++#define STV090x_TSTTNR0 0xf1df ++#define STV090x_OFFST_SEL_FSK_FIELD 7 ++#define STV090x_WIDTH_SEL_FSK_FIELD 1 ++#define STV090x_OFFST_FSK_PON_FIELD 2 ++#define STV090x_WIDTH_FSK_PON_FIELD 1 ++ ++#define STV090x_TSTTNR1 0xf1e0 ++#define STV090x_OFFST_ADC1_PON_FIELD 1 ++#define STV090x_WIDTH_ADC1_PON_FIELD 1 ++#define STV090x_OFFST_ADC1_INMODE_FIELD 0 ++#define STV090x_WIDTH_ADC1_INMODE_FIELD 1 ++ ++#define STV090x_TSTTNR2 0xf1e1 ++#define STV090x_OFFST_DISEQC1_PON_FIELD 5 ++#define STV090x_WIDTH_DISEQC1_PON_FIELD 1 ++ ++#define STV090x_TSTTNR3 0xf1e2 ++#define STV090x_OFFST_ADC2_PON_FIELD 1 ++#define STV090x_WIDTH_ADC2_PON_FIELD 1 ++#define STV090x_OFFST_ADC2_INMODE_FIELD 0 ++#define STV090x_WIDTH_ADC2_INMODE_FIELD 1 ++ ++#define STV090x_TSTTNR4 0xf1e3 ++#define STV090x_OFFST_DISEQC2_PON_FIELD 5 ++#define STV090x_WIDTH_DISEQC2_PON_FIELD 1 ++ ++#define STV090x_FSKTFC2 0xf170 ++#define STV090x_OFFST_FSKT_KMOD_FIELD 2 ++#define STV090x_WIDTH_FSKT_KMOD_FIELD 6 ++#define STV090x_OFFST_FSKT_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKT_CAR_FIELD 2 ++ ++#define STV090x_FSKTFC1 0xf171 ++#define STV090x_OFFST_FSKTC1_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKTC1_CAR_FIELD 8 ++ ++#define STV090x_FSKTFC0 0xf172 ++#define STV090x_OFFST_FSKTC0_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKTC0_CAR_FIELD 8 ++ ++#define STV090x_FSKTDELTAF1 0xf173 ++#define STV090x_OFFST_FSKTF1_DELTAF_FIELD 0 ++#define STV090x_WIDTH_FSKTF1_DELTAF_FIELD 4 ++ ++#define STV090x_FSKTDELTAF0 0xf174 ++#define STV090x_OFFST_FSKTF0_DELTAF_FIELD 0 ++#define STV090x_WIDTH_FSKTF0_DELTAF_FIELD 8 ++ ++#define STV090x_FSKTCTRL 0xf175 ++#define STV090x_OFFST_FSKT_EN_SGN_FIELD 6 ++#define STV090x_WIDTH_FSKT_EN_SGN_FIELD 1 ++#define STV090x_OFFST_FSKT_MOD_SGN_FIELD 5 ++#define STV090x_WIDTH_FSKT_MOD_SGN_FIELD 1 ++#define STV090x_OFFST_FSKT_MOD_EN_FIELD 2 ++#define STV090x_WIDTH_FSKT_MOD_EN_FIELD 3 ++#define STV090x_OFFST_FSKT_DACMODE_FIELD 0 ++#define STV090x_WIDTH_FSKT_DACMODE_FIELD 2 ++ ++#define STV090x_FSKRFC2 0xf176 ++#define STV090x_OFFST_FSKRC2_DETSGN_FIELD 6 ++#define STV090x_WIDTH_FSKRC2_DETSGN_FIELD 1 ++#define STV090x_OFFST_FSKRC2_OUTSGN_FIELD 5 ++#define STV090x_WIDTH_FSKRC2_OUTSGN_FIELD 1 ++#define STV090x_OFFST_FSKRC2_KAGC_FIELD 2 ++#define STV090x_WIDTH_FSKRC2_KAGC_FIELD 3 ++#define STV090x_OFFST_FSKRC2_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKRC2_CAR_FIELD 2 ++ ++#define STV090x_FSKRFC1 0xf177 ++#define STV090x_OFFST_FSKRC1_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKRC1_CAR_FIELD 8 ++ ++#define STV090x_FSKRFC0 0xf178 ++#define STV090x_OFFST_FSKRC0_CAR_FIELD 0 ++#define STV090x_WIDTH_FSKRC0_CAR_FIELD 8 ++ ++#define STV090x_FSKRK1 0xf179 ++#define STV090x_OFFST_FSKR_K1_EXP_FIELD 5 ++#define STV090x_WIDTH_FSKR_K1_EXP_FIELD 3 ++#define STV090x_OFFST_FSKR_K1_MANT_FIELD 0 ++#define STV090x_WIDTH_FSKR_K1_MANT_FIELD 5 ++ ++#define STV090x_FSKRK2 0xf17a ++#define STV090x_OFFST_FSKR_K2_EXP_FIELD 5 ++#define STV090x_WIDTH_FSKR_K2_EXP_FIELD 3 ++#define STV090x_OFFST_FSKR_K2_MANT_FIELD 0 ++#define STV090x_WIDTH_FSKR_K2_MANT_FIELD 5 ++ ++#define STV090x_FSKRAGCR 0xf17b ++#define STV090x_OFFST_FSKR_OUTCTL_FIELD 6 ++#define STV090x_WIDTH_FSKR_OUTCTL_FIELD 2 ++#define STV090x_OFFST_FSKR_AGC_REF_FIELD 0 ++#define STV090x_WIDTH_FSKR_AGC_REF_FIELD 6 ++ ++#define STV090x_FSKRAGC 0xf17c ++#define STV090x_OFFST_FSKR_AGC_ACCU_FIELD 0 ++#define STV090x_WIDTH_FSKR_AGC_ACCU_FIELD 8 ++ ++#define STV090x_FSKRALPHA 0xf17d ++#define STV090x_OFFST_FSKR_ALPHA_EXP_FIELD 2 ++#define STV090x_WIDTH_FSKR_ALPHA_EXP_FIELD 3 ++#define STV090x_OFFST_FSKR_ALPHA_M_FIELD 0 ++#define STV090x_WIDTH_FSKR_ALPHA_M_FIELD 2 ++ ++#define STV090x_FSKRPLTH1 0xf17e ++#define STV090x_OFFST_FSKR_BETA_FIELD 4 ++#define STV090x_WIDTH_FSKR_BETA_FIELD 4 ++#define STV090x_OFFST_FSKR_PLL_TRESH1_FIELD 0 ++#define STV090x_WIDTH_FSKR_PLL_TRESH1_FIELD 4 ++ ++#define STV090x_FSKRPLTH0 0xf17f ++#define STV090x_OFFST_FSKR_PLL_TRESH0_FIELD 0 ++#define STV090x_WIDTH_FSKR_PLL_TRESH0_FIELD 8 ++ ++#define STV090x_FSKRDF1 0xf180 ++#define STV090x_OFFST_FSKR_DELTAF1_FIELD 0 ++#define STV090x_WIDTH_FSKR_DELTAF1_FIELD 5 ++ ++#define STV090x_FSKRDF0 0xf181 ++#define STV090x_OFFST_FSKR_DELTAF0_FIELD 0 ++#define STV090x_WIDTH_FSKR_DELTAF0_FIELD 8 ++ ++#define STV090x_FSKRSTEPP 0xf182 ++#define STV090x_OFFST_FSKR_STEP_PLUS_FIELD 0 ++#define STV090x_WIDTH_FSKR_STEP_PLUS_FIELD 8 ++ ++#define STV090x_FSKRSTEPM 0xf183 ++#define STV090x_OFFST_FSKR_STEP_MINUS_FIELD 0 ++#define STV090x_WIDTH_FSKR_STEP_MINUS_FIELD 8 ++ ++#define STV090x_FSKRDET1 0xf184 ++#define STV090x_OFFST_FSKR_CARDET1_ACCU_FIELD 0 ++#define STV090x_WIDTH_FSKR_CARDET1_ACCU_FIELD 4 ++ ++#define STV090x_FSKRDET0 0xf185 ++#define STV090x_OFFST_FSKR_CARDET0_ACCU_FIELD 0 ++#define STV090x_WIDTH_FSKR_CARDET0_ACCU_FIELD 8 ++ ++#define STV090x_FSKRDTH1 0xf186 ++#define STV090x_OFFST_FSKR_CARLOSS_THRESH1_FIELD 4 ++#define STV090x_WIDTH_FSKR_CARLOSS_THRESH1_FIELD 4 ++#define STV090x_OFFST_FSKR_CARDET_THRESH1_FIELD 0 ++#define STV090x_WIDTH_FSKR_CARDET_THRESH1_FIELD 4 ++ ++#define STV090x_FSKRDTH0 0xf187 ++#define STV090x_OFFST_FSKR_CARDET_THRESH0_FIELD 0 ++#define STV090x_WIDTH_FSKR_CARDET_THRESH0_FIELD 8 ++ ++#define STV090x_FSKRLOSS 0xf188 ++#define STV090x_OFFST_FSKR_CARLOSS_THRESH_FIELD 0 ++#define STV090x_WIDTH_FSKR_CARLOSS_THRESH_FIELD 8 ++ ++#define STV090x_Px_DISTXCTL(__x) (0xF1A0 - (__x - 1) * 0x10) ++#define STV090x_P1_DISTXCTL STV090x_Px_DISTXCTL(1) ++#define STV090x_P2_DISTXCTL STV090x_Px_DISTXCTL(2) ++#define STV090x_OFFST_Px_TIM_OFF_FIELD 7 ++#define STV090x_WIDTH_Px_TIM_OFF_FIELD 1 ++#define STV090x_OFFST_Px_DISEQC_RESET_FIELD 6 ++#define STV090x_WIDTH_Px_DISEQC_RESET_FIELD 1 ++#define STV090x_OFFST_Px_TIM_CMD_FIELD 4 ++#define STV090x_WIDTH_Px_TIM_CMD_FIELD 2 ++#define STV090x_OFFST_Px_DIS_PRECHARGE_FIELD 3 ++#define STV090x_WIDTH_Px_DIS_PRECHARGE_FIELD 1 ++#define STV090x_OFFST_Px_DISTX_MODE_FIELD 0 ++#define STV090x_WIDTH_Px_DISTX_MODE_FIELD 3 ++ ++#define STV090x_Px_DISRXCTL(__x) (0xf1a1 - (__x - 1) * 0x10) ++#define STV090x_P1_DISRXCTL STV090x_Px_DISRXCTL(1) ++#define STV090x_P2_DISRXCTL STV090x_Px_DISRXCTL(2) ++#define STV090x_OFFST_Px_RECEIVER_ON_FIELD 7 ++#define STV090x_WIDTH_Px_RECEIVER_ON_FIELD 1 ++#define STV090x_OFFST_Px_IGNO_SHORT22K_FIELD 6 ++#define STV090x_WIDTH_Px_IGNO_SHORT22K_FIELD 1 ++#define STV090x_OFFST_Px_ONECHIP_TRX_FIELD 5 ++#define STV090x_WIDTH_Px_ONECHIP_TRX_FIELD 1 ++#define STV090x_OFFST_Px_EXT_ENVELOP_FIELD 4 ++#define STV090x_WIDTH_Px_EXT_ENVELOP_FIELD 1 ++#define STV090x_OFFST_Px_PIN_SELECT_FIELD 2 ++#define STV090x_WIDTH_Px_PIN_SELECT_FIELD 2 ++#define STV090x_OFFST_Px_IRQ_RXEND_FIELD 1 ++#define STV090x_WIDTH_Px_IRQ_RXEND_FIELD 1 ++#define STV090x_OFFST_Px_IRQ_4NBYTES_FIELD 0 ++#define STV090x_WIDTH_Px_IRQ_4NBYTES_FIELD 1 ++ ++#define STV090x_Px_DISRX_ST0(__x) (0xf1a4 - (__x - 1) * 0x10) ++#define STV090x_P1_DISRX_ST0 STV090x_Px_DISRX_ST0(1) ++#define STV090x_P2_DISRX_ST0 STV090x_Px_DISRX_ST0(2) ++#define STV090x_OFFST_Px_RX_END_FIELD 7 ++#define STV090x_WIDTH_Px_RX_END_FIELD 1 ++#define STV090x_OFFST_Px_RX_ACTIVE_FIELD 6 ++#define STV090x_WIDTH_Px_RX_ACTIVE_FIELD 1 ++#define STV090x_OFFST_Px_SHORT_22KHZ_FIELD 5 ++#define STV090x_WIDTH_Px_SHORT_22KHZ_FIELD 1 ++#define STV090x_OFFST_Px_CONT_TONE_FIELD 4 ++#define STV090x_WIDTH_Px_CONT_TONE_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3 ++#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2 ++#define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1 ++#define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0 ++#define STV090x_WIDTH_Px_ABORT_DISRX_FIELD 1 ++ ++#define STV090x_Px_DISRX_ST1(__x) (0xf1a5 - (__x - 1) * 0x10) ++#define STV090x_P1_DISRX_ST1 STV090x_Px_DISRX_ST1(1) ++#define STV090x_P2_DISRX_ST1 STV090x_Px_DISRX_ST1(2) ++#define STV090x_OFFST_Px_RX_FAIL_FIELD 7 ++#define STV090x_WIDTH_Px_RX_FAIL_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_PARITYFAIL_FIELD 6 ++#define STV090x_WIDTH_Px_FIFO_PARITYFAIL_FIELD 1 ++#define STV090x_OFFST_Px_RX_NONBYTE_FIELD 5 ++#define STV090x_WIDTH_Px_RX_NONBYTE_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_OVERFLOW_FIELD 4 ++#define STV090x_WIDTH_Px_FIFO_OVERFLOW_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_BYTENBR_FIELD 0 ++#define STV090x_WIDTH_Px_FIFO_BYTENBR_FIELD 4 ++ ++#define STV090x_Px_DISRXDATA(__x) (0xf1a6 - (__x - 1) * 0x10) ++#define STV090x_P1_DISRXDATA STV090x_Px_DISRXDATA(1) ++#define STV090x_P2_DISRXDATA STV090x_Px_DISRXDATA(2) ++#define STV090x_OFFST_Px_DISRX_DATA_FIELD 0 ++#define STV090x_WIDTH_Px_DISRX_DATA_FIELD 8 ++ ++#define STV090x_Px_DISTXDATA(__x) (0xf1a7 - (__x - 1) * 0x10) ++#define STV090x_P1_DISTXDATA STV090x_Px_DISTXDATA(1) ++#define STV090x_P2_DISTXDATA STV090x_Px_DISTXDATA(2) ++#define STV090x_OFFST_Px_DISEQC_FIFO_FIELD 0 ++#define STV090x_WIDTH_Px_DISEQC_FIFO_FIELD 8 ++ ++#define STV090x_Px_DISTXSTATUS(__x) (0xf1a8 - (__x - 1) * 0x10) ++#define STV090x_P1_DISTXSTATUS STV090x_Px_DISTXSTATUS(1) ++#define STV090x_P2_DISTXSTATUS STV090x_Px_DISTXSTATUS(2) ++#define STV090x_OFFST_Px_TX_FAIL_FIELD 7 ++#define STV090x_WIDTH_Px_TX_FAIL_FIELD 1 ++#define STV090x_OFFST_Px_FIFO_FULL_FIELD 6 ++#define STV090x_WIDTH_Px_FIFO_FULL_FIELD 1 ++#define STV090x_OFFST_Px_TX_IDLE_FIELD 5 ++#define STV090x_WIDTH_Px_TX_IDLE_FIELD 1 ++#define STV090x_OFFST_Px_GAP_BURST_FIELD 4 ++#define STV090x_WIDTH_Px_GAP_BURST_FIELD 1 ++#define STV090x_OFFST_Px_TXFIFO_BYTES_FIELD 0 ++#define STV090x_WIDTH_Px_TXFIFO_BYTES_FIELD 4 ++ ++#define STV090x_Px_F22TX(__x) (0xf1a9 - (__x - 1) * 0x10) ++#define STV090x_P1_F22TX STV090x_Px_F22TX(1) ++#define STV090x_P2_F22TX STV090x_Px_F22TX(2) ++#define STV090x_OFFST_Px_F22_REG_FIELD 0 ++#define STV090x_WIDTH_Px_F22_REG_FIELD 8 ++ ++#define STV090x_Px_F22RX(__x) (0xf1aa - (__x - 1) * 0x10) ++#define STV090x_P1_F22RX STV090x_Px_F22RX(1) ++#define STV090x_P2_F22RX STV090x_Px_F22RX(2) ++#define STV090x_OFFST_Px_F22RX_REG_FIELD 0 ++#define STV090x_WIDTH_Px_F22RX_REG_FIELD 8 ++ ++#define STV090x_Px_ACRPRESC(__x) (0xf1ac - (__x - 1) * 0x10) ++#define STV090x_P1_ACRPRESC STV090x_Px_ACRPRESC(1) ++#define STV090x_P2_ACRPRESC STV090x_Px_ACRPRESC(2) ++#define STV090x_OFFST_Px_ACR_PRESC_FIELD 0 ++#define STV090x_WIDTH_Px_ACR_PRESC_FIELD 3 ++ ++#define STV090x_Px_ACRDIV(__x) (0xf1ad - (__x - 1) * 0x10) ++#define STV090x_P1_ACRDIV STV090x_Px_ACRDIV(1) ++#define STV090x_P2_ACRDIV STV090x_Px_ACRDIV(2) ++#define STV090x_OFFST_Px_ACR_DIV_FIELD 0 ++#define STV090x_WIDTH_Px_ACR_DIV_FIELD 8 ++ ++#define STV090x_Px_IQCONST(__x) (0xF400 - (__x - 1) * 0x200) ++#define STV090x_P1_IQCONST STV090x_Px_IQCONST(1) ++#define STV090x_P2_IQCONST STV090x_Px_IQCONST(2) ++#define STV090x_OFFST_Px_CONSTEL_SELECT_FIELD 5 ++#define STV090x_WIDTH_Px_CONSTEL_SELECT_FIELD 2 ++ ++#define STV090x_Px_NOSCFG(__x) (0xF401 - (__x - 1) * 0x200) ++#define STV090x_P1_NOSCFG STV090x_Px_NOSCFG(1) ++#define STV090x_P2_NOSCFG STV090x_Px_NOSCFG(2) ++#define STV090x_OFFST_Px_NOSPLH_BETA_FIELD 3 ++#define STV090x_WIDTH_Px_NOSPLH_BETA_FIELD 2 ++#define STV090x_OFFST_Px_NOSDATA_BETA_FIELD 0 ++#define STV090x_WIDTH_Px_NOSDATA_BETA_FIELD 3 ++ ++#define STV090x_Px_ISYMB(__x) (0xF402 - (__x - 1) * 0x200) ++#define STV090x_P1_ISYMB STV090x_Px_ISYMB(1) ++#define STV090x_P2_ISYMB STV090x_Px_ISYMB(2) ++#define STV090x_OFFST_Px_I_SYMBOL_FIELD 0 ++#define STV090x_WIDTH_Px_I_SYMBOL_FIELD 8 ++ ++#define STV090x_Px_QSYMB(__x) (0xF403 - (__x - 1) * 0x200) ++#define STV090x_P1_QSYMB STV090x_Px_QSYMB(1) ++#define STV090x_P2_QSYMB STV090x_Px_QSYMB(2) ++#define STV090x_OFFST_Px_Q_SYMBOL_FIELD 0 ++#define STV090x_WIDTH_Px_Q_SYMBOL_FIELD 8 ++ ++#define STV090x_Px_AGC1CFG(__x) (0xF404 - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1CFG STV090x_Px_AGC1CFG(1) ++#define STV090x_P2_AGC1CFG STV090x_Px_AGC1CFG(2) ++#define STV090x_OFFST_Px_DC_FROZEN_FIELD 7 ++#define STV090x_WIDTH_Px_DC_FROZEN_FIELD 1 ++#define STV090x_OFFST_Px_DC_CORRECT_FIELD 6 ++#define STV090x_WIDTH_Px_DC_CORRECT_FIELD 1 ++#define STV090x_OFFST_Px_AMM_FROZEN_FIELD 5 ++#define STV090x_WIDTH_Px_AMM_FROZEN_FIELD 1 ++#define STV090x_OFFST_Px_AMM_CORRECT_FIELD 4 ++#define STV090x_WIDTH_Px_AMM_CORRECT_FIELD 1 ++#define STV090x_OFFST_Px_QUAD_FROZEN_FIELD 3 ++#define STV090x_WIDTH_Px_QUAD_FROZEN_FIELD 1 ++#define STV090x_OFFST_Px_QUAD_CORRECT_FIELD 2 ++#define STV090x_WIDTH_Px_QUAD_CORRECT_FIELD 1 ++ ++#define STV090x_Px_AGC1CN(__x) (0xF406 - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1CN STV090x_Px_AGC1CN(1) ++#define STV090x_P2_AGC1CN STV090x_Px_AGC1CN(2) ++#define STV090x_WIDTH_Px_AGC1_LOCKED_FIELD 7 ++#define STV090x_OFFST_Px_AGC1_LOCKED_FIELD 1 ++#define STV090x_OFFST_Px_AGC1_MINPOWER_FIELD 4 ++#define STV090x_WIDTH_Px_AGC1_MINPOWER_FIELD 1 ++#define STV090x_OFFST_Px_AGCOUT_FAST_FIELD 3 ++#define STV090x_WIDTH_Px_AGCOUT_FAST_FIELD 1 ++#define STV090x_OFFST_Px_AGCIQ_BETA_FIELD 0 ++#define STV090x_WIDTH_Px_AGCIQ_BETA_FIELD 3 ++ ++#define STV090x_Px_AGC1REF(__x) (0xF407 - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1REF STV090x_Px_AGC1REF(1) ++#define STV090x_P2_AGC1REF STV090x_Px_AGC1REF(2) ++#define STV090x_OFFST_Px_AGCIQ_REF_FIELD 0 ++#define STV090x_WIDTH_Px_AGCIQ_REF_FIELD 8 ++ ++#define STV090x_Px_IDCCOMP(__x) (0xF408 - (__x - 1) * 0x200) ++#define STV090x_P1_IDCCOMP STV090x_Px_IDCCOMP(1) ++#define STV090x_P2_IDCCOMP STV090x_Px_IDCCOMP(2) ++#define STV090x_OFFST_Px_IAVERAGE_ADJ_FIELD 0 ++#define STV090x_WIDTH_Px_IAVERAGE_ADJ_FIELD 8 ++ ++#define STV090x_Px_QDCCOMP(__x) (0xF409 - (__x - 1) * 0x200) ++#define STV090x_P1_QDCCOMP STV090x_Px_QDCCOMP(1) ++#define STV090x_P2_QDCCOMP STV090x_Px_QDCCOMP(2) ++#define STV090x_OFFST_Px_QAVERAGE_ADJ_FIELD 0 ++#define STV090x_WIDTH_Px_QAVERAGE_ADJ_FIELD 8 ++ ++#define STV090x_Px_POWERI(__x) (0xF40A - (__x - 1) * 0x200) ++#define STV090x_P1_POWERI STV090x_Px_POWERI(1) ++#define STV090x_P2_POWERI STV090x_Px_POWERI(2) ++#define STV090x_OFFST_Px_POWER_I_FIELD 0 ++#define STV090x_WIDTH_Px_POWER_I_FIELD 8 ++ ++#define STV090x_Px_POWERQ(__x) (0xF40B - (__x - 1) * 0x200) ++#define STV090x_P1_POWERQ STV090x_Px_POWERQ(1) ++#define STV090x_P2_POWERQ STV090x_Px_POWERQ(2) ++#define STV090x_OFFST_Px_POWER_Q_FIELD 0 ++#define STV090x_WIDTH_Px_POWER_Q_FIELD 8 ++ ++#define STV090x_Px_AGC1AMM(__x) (0xF40C - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1AMM STV090x_Px_AGC1AMM(1) ++#define STV090x_P2_AGC1AMM STV090x_Px_AGC1AMM(2) ++#define STV090x_OFFST_Px_AMM_VALUE_FIELD 0 ++#define STV090x_WIDTH_Px_AMM_VALUE_FIELD 8 ++ ++#define STV090x_Px_AGC1QUAD(__x) (0xF40D - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1QUAD STV090x_Px_AGC1QUAD(1) ++#define STV090x_P2_AGC1QUAD STV090x_Px_AGC1QUAD(2) ++#define STV090x_OFFST_Px_QUAD_VALUE_FIELD 0 ++#define STV090x_WIDTH_Px_QUAD_VALUE_FIELD 8 ++ ++#define STV090x_Px_AGCIQINy(__x, __y) (0xF40F - (__x-1) * 0x200 - __y * 0x1) ++#define STV090x_P1_AGCIQIN0 STV090x_Px_AGCIQINy(1, 0) ++#define STV090x_P1_AGCIQIN1 STV090x_Px_AGCIQINy(1, 1) ++#define STV090x_P2_AGCIQIN0 STV090x_Px_AGCIQINy(2, 0) ++#define STV090x_P2_AGCIQIN1 STV090x_Px_AGCIQINy(2, 1) ++#define STV090x_OFFST_Px_AGCIQ_VALUE_FIELD 0 ++#define STV090x_WIDTH_Px_AGCIQ_VALUE_FIELD 8 ++ ++#define STV090x_Px_DEMOD(__x) (0xF410 - (__x - 1) * 0x200) ++#define STV090x_P1_DEMOD STV090x_Px_DEMOD(1) ++#define STV090x_P2_DEMOD STV090x_Px_DEMOD(2) ++#define STV090x_OFFST_Px_MANUAL_S2ROLLOFF_FIELD 7 ++#define STV090x_WIDTH_Px_MANUAL_S2ROLLOFF_FIELD 1 ++#define STV090x_OFFST_Px_DEMOD_STOP_FIELD 6 ++#define STV090x_WIDTH_Px_DEMOD_STOP_FIELD 1 ++#define STV090x_OFFST_Px_SPECINV_CONTROL_FIELD 4 ++#define STV090x_WIDTH_Px_SPECINV_CONTROL_FIELD 2 ++#define STV090x_OFFST_Px_FORCE_ENASAMP_FIELD 3 ++#define STV090x_WIDTH_Px_FORCE_ENASAMP_FIELD 1 ++#define STV090x_OFFST_Px_MANUAL_SXROLLOFF_FIELD 2 ++#define STV090x_WIDTH_Px_MANUAL_SXROLLOFF_FIELD 1 ++#define STV090x_OFFST_Px_ROLLOFF_CONTROL_FIELD 0 ++#define STV090x_WIDTH_Px_ROLLOFF_CONTROL_FIELD 2 ++ ++#define STV090x_Px_DMDMODCOD(__x) (0xF411 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDMODCOD STV090x_Px_DMDMODCOD(1) ++#define STV090x_P2_DMDMODCOD STV090x_Px_DMDMODCOD(2) ++#define STV090x_OFFST_Px_MANUAL_MODCOD_FIELD 7 ++#define STV090x_WIDTH_Px_MANUAL_MODCOD_FIELD 1 ++#define STV090x_OFFST_Px_DEMOD_MODCOD_FIELD 2 ++#define STV090x_WIDTH_Px_DEMOD_MODCOD_FIELD 5 ++#define STV090x_OFFST_Px_DEMOD_TYPE_FIELD 0 ++#define STV090x_WIDTH_Px_DEMOD_TYPE_FIELD 2 ++ ++#define STV090x_Px_DSTATUS(__x) (0xF412 - (__x - 1) * 0x200) ++#define STV090x_P1_DSTATUS STV090x_Px_DSTATUS(1) ++#define STV090x_P2_DSTATUS STV090x_Px_DSTATUS(2) ++#define STV090x_OFFST_Px_CAR_LOCK_FIELD 7 ++#define STV090x_WIDTH_Px_CAR_LOCK_FIELD 1 ++#define STV090x_OFFST_Px_TMGLOCK_QUALITY_FIELD 5 ++#define STV090x_WIDTH_Px_TMGLOCK_QUALITY_FIELD 2 ++#define STV090x_OFFST_Px_LOCK_DEFINITIF_FIELD 3 ++#define STV090x_WIDTH_Px_LOCK_DEFINITIF_FIELD 1 ++ ++#define STV090x_Px_DSTATUS2(__x) (0xF413 - (__x - 1) * 0x200) ++#define STV090x_P1_DSTATUS2 STV090x_Px_DSTATUS2(1) ++#define STV090x_P2_DSTATUS2 STV090x_Px_DSTATUS2(2) ++#define STV090x_OFFST_Px_DEMOD_DELOCK_FIELD 7 ++#define STV090x_WIDTH_Px_DEMOD_DELOCK_FIELD 1 ++#define STV090x_OFFST_Px_AGC1_NOSIGNALACK_FIELD 3 ++#define STV090x_WIDTH_Px_AGC1_NOSIGNALACK_FIELD 1 ++#define STV090x_OFFST_Px_AGC2_OVERFLOW_FIELD 2 ++#define STV090x_WIDTH_Px_AGC2_OVERFLOW_FIELD 1 ++#define STV090x_OFFST_Px_CFR_OVERFLOW_FIELD 1 ++#define STV090x_WIDTH_Px_CFR_OVERFLOW_FIELD 1 ++#define STV090x_OFFST_Px_GAMMA_OVERUNDER_FIELD 0 ++#define STV090x_WIDTH_Px_GAMMA_OVERUNDER_FIELD 1 ++ ++#define STV090x_Px_DMDCFGMD(__x) (0xF414 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDCFGMD STV090x_Px_DMDCFGMD(1) ++#define STV090x_P2_DMDCFGMD STV090x_Px_DMDCFGMD(2) ++#define STV090x_OFFST_Px_DVBS2_ENABLE_FIELD 7 ++#define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1 ++#define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6 ++#define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1 ++#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 ++#define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1 ++#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3 ++#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 ++#define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2 ++#define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1 ++#define STV090x_OFFST_Px_TUN_RNG_FIELD 0 ++#define STV090x_WIDTH_Px_TUN_RNG_FIELD 2 ++ ++#define STV090x_Px_DMDCFG2(__x) (0xF415 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDCFG2 STV090x_Px_DMDCFG2(1) ++#define STV090x_P2_DMDCFG2 STV090x_Px_DMDCFG2(2) ++#define STV090x_OFFST_Px_S1S2_SEQUENTIAL_FIELD 6 ++#define STV090x_WIDTH_Px_S1S2_SEQUENTIAL_FIELD 1 ++ ++#define STV090x_Px_DMDISTATE(__x) (0xF416 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDISTATE STV090x_Px_DMDISTATE(1) ++#define STV090x_P2_DMDISTATE STV090x_Px_DMDISTATE(2) ++#define STV090x_OFFST_Px_I2C_DEMOD_MODE_FIELD 0 ++#define STV090x_WIDTH_Px_I2C_DEMOD_MODE_FIELD 5 ++ ++#define STV090x_Px_DMDTOM(__x) (0xF417 - (__x - 1) * 0x200) /* check */ ++#define STV090x_P1_DMDTOM STV090x_Px_DMDTOM(1) ++#define STV090x_P2_DMDTOM STV090x_Px_DMDTOM(2) ++ ++#define STV090x_Px_DMDSTATE(__x) (0xF41B - (__x - 1) * 0x200) ++#define STV090x_P1_DMDSTATE STV090x_Px_DMDSTATE(1) ++#define STV090x_P2_DMDSTATE STV090x_Px_DMDSTATE(2) ++#define STV090x_OFFST_Px_HEADER_MODE_FIELD 5 ++#define STV090x_WIDTH_Px_HEADER_MODE_FIELD 2 ++ ++#define STV090x_Px_DMDFLYW(__x) (0xF41C - (__x - 1) * 0x200) ++#define STV090x_P1_DMDFLYW STV090x_Px_DMDFLYW(1) ++#define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2) ++#define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4 ++#define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4 ++#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4 ++ ++#define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200) ++#define STV090x_P1_DSTATUS3 STV090x_Px_DSTATUS3(1) ++#define STV090x_P2_DSTATUS3 STV090x_Px_DSTATUS3(2) ++#define STV090x_OFFST_Px_DEMOD_CFGMODE_FIELD 5 ++#define STV090x_WIDTH_Px_DEMOD_CFGMODE_FIELD 2 ++ ++#define STV090x_Px_DMDCFG3(__x) (0xF41E - (__x - 1) * 0x200) ++#define STV090x_P1_DMDCFG3 STV090x_Px_DMDCFG3(1) ++#define STV090x_P2_DMDCFG3 STV090x_Px_DMDCFG3(2) ++#define STV090x_OFFST_Px_NOSTOP_FIFOFULL_FIELD 3 ++#define STV090x_WIDTH_Px_NOSTOP_FIFOFULL_FIELD 1 ++ ++#define STV090x_Px_DMDCFG4(__x) (0xf41f - (__x - 1) * 0x200) ++#define STV090x_P1_DMDCFG4 STV090x_Px_DMDCFG4(1) ++#define STV090x_P2_DMDCFG4 STV090x_Px_DMDCFG4(2) ++ ++#define STV090x_Px_CORRELMANT(__x) (0xF420 - (__x - 1) * 0x200) ++#define STV090x_P1_CORRELMANT STV090x_Px_CORRELMANT(1) ++#define STV090x_P2_CORRELMANT STV090x_Px_CORRELMANT(2) ++#define STV090x_OFFST_Px_CORREL_MANT_FIELD 0 ++#define STV090x_WIDTH_Px_CORREL_MANT_FIELD 8 ++ ++#define STV090x_Px_CORRELABS(__x) (0xF421 - (__x - 1) * 0x200) ++#define STV090x_P1_CORRELABS STV090x_Px_CORRELABS(1) ++#define STV090x_P2_CORRELABS STV090x_Px_CORRELABS(2) ++#define STV090x_OFFST_Px_CORREL_ABS_FIELD 0 ++#define STV090x_WIDTH_Px_CORREL_ABS_FIELD 8 ++ ++#define STV090x_Px_CORRELEXP(__x) (0xF422 - (__x - 1) * 0x200) ++#define STV090x_P1_CORRELEXP STV090x_Px_CORRELEXP(1) ++#define STV090x_P2_CORRELEXP STV090x_Px_CORRELEXP(2) ++#define STV090x_OFFST_Px_CORREL_ABSEXP_FIELD 4 ++#define STV090x_WIDTH_Px_CORREL_ABSEXP_FIELD 4 ++#define STV090x_OFFST_Px_CORREL_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_CORREL_EXP_FIELD 4 ++ ++#define STV090x_Px_PLHMODCOD(__x) (0xF424 - (__x - 1) * 0x200) ++#define STV090x_P1_PLHMODCOD STV090x_Px_PLHMODCOD(1) ++#define STV090x_P2_PLHMODCOD STV090x_Px_PLHMODCOD(2) ++#define STV090x_OFFST_Px_SPECINV_DEMOD_FIELD 7 ++#define STV090x_WIDTH_Px_SPECINV_DEMOD_FIELD 1 ++#define STV090x_OFFST_Px_PLH_MODCOD_FIELD 2 ++#define STV090x_WIDTH_Px_PLH_MODCOD_FIELD 5 ++#define STV090x_OFFST_Px_PLH_TYPE_FIELD 0 ++#define STV090x_WIDTH_Px_PLH_TYPE_FIELD 2 ++ ++#define STV090x_Px_AGCK32(__x) (0xf42b - (__x - 1) * 0x200) ++#define STV090x_P1_AGCK32 STV090x_Px_AGCK32(1) ++#define STV090x_P2_AGCK32 STV090x_Px_AGCK32(2) ++ ++#define STV090x_Px_AGC2O(__x) (0xF42C - (__x - 1) * 0x200) ++#define STV090x_P1_AGC2O STV090x_Px_AGC2O(1) ++#define STV090x_P2_AGC2O STV090x_Px_AGC2O(2) ++ ++#define STV090x_Px_AGC2REF(__x) (0xF42D - (__x - 1) * 0x200) ++#define STV090x_P1_AGC2REF STV090x_Px_AGC2REF(1) ++#define STV090x_P2_AGC2REF STV090x_Px_AGC2REF(2) ++#define STV090x_OFFST_Px_AGC2_REF_FIELD 0 ++#define STV090x_WIDTH_Px_AGC2_REF_FIELD 8 ++ ++#define STV090x_Px_AGC1ADJ(__x) (0xF42E - (__x - 1) * 0x200) ++#define STV090x_P1_AGC1ADJ STV090x_Px_AGC1ADJ(1) ++#define STV090x_P2_AGC1ADJ STV090x_Px_AGC1ADJ(2) ++#define STV090x_OFFST_Px_AGC1_ADJUSTED_FIELD 0 ++#define STV090x_WIDTH_Px_AGC1_ADJUSTED_FIELD 7 ++ ++#define STV090x_Px_AGC2Iy(__x, __y) (0xF437 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_AGC2I0 STV090x_Px_AGC2Iy(1, 0) ++#define STV090x_P1_AGC2I1 STV090x_Px_AGC2Iy(1, 1) ++#define STV090x_P2_AGC2I0 STV090x_Px_AGC2Iy(2, 0) ++#define STV090x_P2_AGC2I1 STV090x_Px_AGC2Iy(2, 1) ++#define STV090x_OFFST_Px_AGC2_INTEGRATOR_FIELD 0 ++#define STV090x_WIDTH_Px_AGC2_INTEGRATOR_FIELD 8 ++ ++#define STV090x_Px_CARCFG(__x) (0xF438 - (__x - 1) * 0x200) ++#define STV090x_P1_CARCFG STV090x_Px_CARCFG(1) ++#define STV090x_P2_CARCFG STV090x_Px_CARCFG(2) ++#define STV090x_OFFST_Px_EN_CAR2CENTER_FIELD 5 ++#define STV090x_WIDTH_Px_EN_CAR2CENTER_FIELD 1 ++#define STV090x_OFFST_Px_ROTATON_FIELD 2 ++#define STV090x_WIDTH_Px_ROTATON_FIELD 1 ++#define STV090x_OFFST_Px_PH_DET_ALGO_FIELD 0 ++#define STV090x_WIDTH_Px_PH_DET_ALGO_FIELD 2 ++ ++#define STV090x_Px_ACLC(__x) (0xF439 - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC STV090x_Px_ACLC(1) ++#define STV090x_P2_ACLC STV090x_Px_ACLC(2) ++#define STV090x_OFFST_Px_CAR_ALPHA_MANT_FIELD 4 ++#define STV090x_WIDTH_Px_CAR_ALPHA_MANT_FIELD 2 ++#define STV090x_OFFST_Px_CAR_ALPHA_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_CAR_ALPHA_EXP_FIELD 4 ++ ++#define STV090x_Px_BCLC(__x) (0xF43A - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC STV090x_Px_BCLC(1) ++#define STV090x_P2_BCLC STV090x_Px_BCLC(2) ++#define STV090x_OFFST_Px_CAR_BETA_MANT_FIELD 4 ++#define STV090x_WIDTH_Px_CAR_BETA_MANT_FIELD 2 ++#define STV090x_OFFST_Px_CAR_BETA_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_CAR_BETA_EXP_FIELD 4 ++ ++#define STV090x_Px_CARFREQ(__x) (0xF43D - (__x - 1) * 0x200) ++#define STV090x_P1_CARFREQ STV090x_Px_CARFREQ(1) ++#define STV090x_P2_CARFREQ STV090x_Px_CARFREQ(2) ++#define STV090x_OFFST_Px_KC_COARSE_EXP_FIELD 4 ++#define STV090x_WIDTH_Px_KC_COARSE_EXP_FIELD 4 ++#define STV090x_OFFST_Px_BETA_FREQ_FIELD 0 ++#define STV090x_WIDTH_Px_BETA_FREQ_FIELD 4 ++ ++#define STV090x_Px_CARHDR(__x) (0xF43E - (__x - 1) * 0x200) ++#define STV090x_P1_CARHDR STV090x_Px_CARHDR(1) ++#define STV090x_P2_CARHDR STV090x_Px_CARHDR(2) ++#define STV090x_OFFST_Px_FREQ_HDR_FIELD 0 ++#define STV090x_WIDTH_Px_FREQ_HDR_FIELD 8 ++ ++#define STV090x_Px_LDT(__x) (0xF43F - (__x - 1) * 0x200) ++#define STV090x_P1_LDT STV090x_Px_LDT(1) ++#define STV090x_P2_LDT STV090x_Px_LDT(2) ++#define STV090x_OFFST_Px_CARLOCK_THRES_FIELD 0 ++#define STV090x_WIDTH_Px_CARLOCK_THRES_FIELD 8 ++ ++#define STV090x_Px_LDT2(__x) (0xF440 - (__x - 1) * 0x200) ++#define STV090x_P1_LDT2 STV090x_Px_LDT2(1) ++#define STV090x_P2_LDT2 STV090x_Px_LDT2(2) ++#define STV090x_OFFST_Px_CARLOCK_THRES2_FIELD 0 ++#define STV090x_WIDTH_Px_CARLOCK_THRES2_FIELD 8 ++ ++#define STV090x_Px_CFRICFG(__x) (0xF441 - (__x - 1) * 0x200) ++#define STV090x_P1_CFRICFG STV090x_Px_CFRICFG(1) ++#define STV090x_P2_CFRICFG STV090x_Px_CFRICFG(2) ++#define STV090x_OFFST_Px_NEG_CFRSTEP_FIELD 0 ++#define STV090x_WIDTH_Px_NEG_CFRSTEP_FIELD 1 ++ ++#define STV090x_Pn_CFRUPy(__x, __y) (0xF443 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_CFRUP0 STV090x_Pn_CFRUPy(1, 0) ++#define STV090x_P1_CFRUP1 STV090x_Pn_CFRUPy(1, 1) ++#define STV090x_P2_CFRUP0 STV090x_Pn_CFRUPy(2, 0) ++#define STV090x_P2_CFRUP1 STV090x_Pn_CFRUPy(2, 1) ++#define STV090x_OFFST_Px_CFR_UP_FIELD 0 ++#define STV090x_WIDTH_Px_CFR_UP_FIELD 8 ++ ++#define STV090x_Pn_CFRLOWy(__x, __y) (0xF447 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_CFRLOW0 STV090x_Pn_CFRLOWy(1, 0) ++#define STV090x_P1_CFRLOW1 STV090x_Pn_CFRLOWy(1, 1) ++#define STV090x_P2_CFRLOW0 STV090x_Pn_CFRLOWy(2, 0) ++#define STV090x_P2_CFRLOW1 STV090x_Pn_CFRLOWy(2, 1) ++#define STV090x_OFFST_Px_CFR_LOW_FIELD 0 ++#define STV090x_WIDTH_Px_CFR_LOW_FIELD 8 ++ ++#define STV090x_Pn_CFRINITy(__x, __y) (0xF449 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_CFRINIT0 STV090x_Pn_CFRINITy(1, 0) ++#define STV090x_P1_CFRINIT1 STV090x_Pn_CFRINITy(1, 1) ++#define STV090x_P2_CFRINIT0 STV090x_Pn_CFRINITy(2, 0) ++#define STV090x_P2_CFRINIT1 STV090x_Pn_CFRINITy(2, 1) ++#define STV090x_OFFST_Px_CFR_INIT_FIELD 0 ++#define STV090x_WIDTH_Px_CFR_INIT_FIELD 8 ++ ++#define STV090x_Px_CFRINC1(__x) (0xF44A - (__x - 1) * 0x200) ++#define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1) ++#define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2) ++#define STV090x_OFFST_Px_CFR_INC1_FIELD 0 ++#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */ ++ ++#define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200) ++#define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1) ++#define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2) ++#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */ ++#define STV090x_WIDTH_Px_CFR_INC0_FIELD 4 ++ ++#define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_CFR0 STV090x_Pn_CFRy(1, 0) ++#define STV090x_P1_CFR1 STV090x_Pn_CFRy(1, 1) ++#define STV090x_P1_CFR2 STV090x_Pn_CFRy(1, 2) ++#define STV090x_P2_CFR0 STV090x_Pn_CFRy(2, 0) ++#define STV090x_P2_CFR1 STV090x_Pn_CFRy(2, 1) ++#define STV090x_P2_CFR2 STV090x_Pn_CFRy(2, 2) ++#define STV090x_OFFST_Px_CAR_FREQ_FIELD 0 ++#define STV090x_WIDTH_Px_CAR_FREQ_FIELD 8 ++ ++#define STV090x_Px_LDI(__x) (0xF44F - (__x - 1) * 0x200) ++#define STV090x_P1_LDI STV090x_Px_LDI(1) ++#define STV090x_P2_LDI STV090x_Px_LDI(2) ++#define STV090x_OFFST_Px_LOCK_DET_INTEGR_FIELD 0 ++#define STV090x_WIDTH_Px_LOCK_DET_INTEGR_FIELD 8 ++ ++#define STV090x_Px_TMGCFG(__x) (0xF450 - (__x - 1) * 0x200) ++#define STV090x_P1_TMGCFG STV090x_Px_TMGCFG(1) ++#define STV090x_P2_TMGCFG STV090x_Px_TMGCFG(2) ++#define STV090x_OFFST_Px_TMGLOCK_BETA_FIELD 6 ++#define STV090x_WIDTH_Px_TMGLOCK_BETA_FIELD 2 ++#define STV090x_OFFST_Px_DO_TIMING_FIELD 4 ++#define STV090x_WIDTH_Px_DO_TIMING_FIELD 1 ++#define STV090x_OFFST_Px_TMG_MINFREQ_FIELD 0 ++#define STV090x_WIDTH_Px_TMG_MINFREQ_FIELD 2 ++ ++#define STV090x_Px_RTC(__x) (0xF451 - (__x - 1) * 0x200) ++#define STV090x_P1_RTC STV090x_Px_RTC(1) ++#define STV090x_P2_RTC STV090x_Px_RTC(2) ++#define STV090x_OFFST_Px_TMGALPHA_EXP_FIELD 4 ++#define STV090x_WIDTH_Px_TMGALPHA_EXP_FIELD 4 ++#define STV090x_OFFST_Px_TMGBETA_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_TMGBETA_EXP_FIELD 4 ++ ++#define STV090x_Px_RTCS2(__x) (0xF452 - (__x - 1) * 0x200) ++#define STV090x_P1_RTCS2 STV090x_Px_RTCS2(1) ++#define STV090x_P2_RTCS2 STV090x_Px_RTCS2(2) ++#define STV090x_OFFST_Px_TMGALPHAS2_EXP_FIELD 4 ++#define STV090x_WIDTH_Px_TMGALPHAS2_EXP_FIELD 4 ++#define STV090x_OFFST_Px_TMGBETAS2_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_TMGBETAS2_EXP_FIELD 4 ++ ++#define STV090x_Px_TMGTHRISE(__x) (0xF453 - (__x - 1) * 0x200) ++#define STV090x_P1_TMGTHRISE STV090x_Px_TMGTHRISE(1) ++#define STV090x_P2_TMGTHRISE STV090x_Px_TMGTHRISE(2) ++#define STV090x_OFFST_Px_TMGLOCK_THRISE_FIELD 0 ++#define STV090x_WIDTH_Px_TMGLOCK_THRISE_FIELD 8 ++ ++#define STV090x_Px_TMGTHFALL(__x) (0xF454 - (__x - 1) * 0x200) ++#define STV090x_P1_TMGTHFALL STV090x_Px_TMGTHFALL(1) ++#define STV090x_P2_TMGTHFALL STV090x_Px_TMGTHFALL(2) ++#define STV090x_OFFST_Px_TMGLOCK_THFALL_FIELD 0 ++#define STV090x_WIDTH_Px_TMGLOCK_THFALL_FIELD 8 ++ ++#define STV090x_Px_SFRUPRATIO(__x) (0xF455 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRUPRATIO STV090x_Px_SFRUPRATIO(1) ++#define STV090x_P2_SFRUPRATIO STV090x_Px_SFRUPRATIO(2) ++#define STV090x_OFFST_Px_SFR_UPRATIO_FIELD 0 ++#define STV090x_WIDTH_Px_SFR_UPRATIO_FIELD 8 ++ ++#define STV090x_Px_SFRLOWRATIO(__x) (0xF456 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(1) ++#define STV090x_P2_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(2) ++#define STV090x_OFFST_Px_SFR_LOWRATIO_FIELD 0 ++#define STV090x_WIDTH_Px_SFR_LOWRATIO_FIELD 8 ++ ++#define STV090x_Px_KREFTMG(__x) (0xF458 - (__x - 1) * 0x200) ++#define STV090x_P1_KREFTMG STV090x_Px_KREFTMG(1) ++#define STV090x_P2_KREFTMG STV090x_Px_KREFTMG(2) ++#define STV090x_OFFST_Px_KREF_TMG_FIELD 0 ++#define STV090x_WIDTH_Px_KREF_TMG_FIELD 8 ++ ++#define STV090x_Px_SFRSTEP(__x) (0xF459 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRSTEP STV090x_Px_SFRSTEP(1) ++#define STV090x_P2_SFRSTEP STV090x_Px_SFRSTEP(2) ++#define STV090x_OFFST_Px_SFR_SCANSTEP_FIELD 4 ++#define STV090x_WIDTH_Px_SFR_SCANSTEP_FIELD 4 ++#define STV090x_OFFST_Px_SFR_CENTERSTEP_FIELD 0 ++#define STV090x_WIDTH_Px_SFR_CENTERSTEP_FIELD 4 ++ ++#define STV090x_Px_TMGCFG2(__x) (0xF45A - (__x - 1) * 0x200) ++#define STV090x_P1_TMGCFG2 STV090x_Px_TMGCFG2(1) ++#define STV090x_P2_TMGCFG2 STV090x_Px_TMGCFG2(2) ++#define STV090x_OFFST_Px_SFRRATIO_FINE_FIELD 0 ++#define STV090x_WIDTH_Px_SFRRATIO_FINE_FIELD 1 ++ ++#define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200) ++#define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1) ++#define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2) ++#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0 ++#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7 ++ ++#define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200) ++#define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1) ++#define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2) ++#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0 ++#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8 ++ ++#define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1) ++#define STV090x_P2_SFRUP1 STV090x_Px_SFRUP1(2) ++#define STV090x_OFFST_Px_SYMB_FREQ_UP1_FIELD 0 ++#define STV090x_WIDTH_Px_SYMB_FREQ_UP1_FIELD 7 ++ ++#define STV090x_Px_SFRUP0(__x) (0xF461 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRUP0 STV090x_Px_SFRUP0(1) ++#define STV090x_P2_SFRUP0 STV090x_Px_SFRUP0(2) ++#define STV090x_OFFST_Px_SYMB_FREQ_UP0_FIELD 0 ++#define STV090x_WIDTH_Px_SYMB_FREQ_UP0_FIELD 8 ++ ++#define STV090x_Px_SFRLOW1(__x) (0xF462 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRLOW1 STV090x_Px_SFRLOW1(1) ++#define STV090x_P2_SFRLOW1 STV090x_Px_SFRLOW1(2) ++#define STV090x_OFFST_Px_SYMB_FREQ_LOW1_FIELD 0 ++#define STV090x_WIDTH_Px_SYMB_FREQ_LOW1_FIELD 7 ++ ++#define STV090x_Px_SFRLOW0(__x) (0xF463 - (__x - 1) * 0x200) ++#define STV090x_P1_SFRLOW0 STV090x_Px_SFRLOW0(1) ++#define STV090x_P2_SFRLOW0 STV090x_Px_SFRLOW0(2) ++#define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0 ++#define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8 ++ ++#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y) ++#define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0) ++#define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1) ++#define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2) ++#define STV090x_P1_SFR3 STV090x_Px_SFRy(1, 3) ++#define STV090x_P2_SFR0 STV090x_Px_SFRy(2, 0) ++#define STV090x_P2_SFR1 STV090x_Px_SFRy(2, 1) ++#define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2) ++#define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3) ++#define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0 ++#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8 ++ ++#define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200) ++#define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1) ++#define STV090x_P2_TMGREG2 STV090x_Px_TMGREG2(2) ++#define STV090x_OFFST_Px_TMGREG_FIELD 0 ++#define STV090x_WIDTH_Px_TMGREG_FIELD 8 ++ ++#define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200) ++#define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1) ++#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) ++#define STV090x_OFFST_Px_TMGREG_FIELD 0 ++#define STV090x_WIDTH_Px_TMGREG_FIELD 8 ++ ++#define STV090x_Px_TMGREG0(__x) (0xF46A - (__x - 1) * 0x200) ++#define STV090x_P1_TMGREG0 STV090x_Px_TMGREG0(1) ++#define STV090x_P2_TMGREG0 STV090x_Px_TMGREG0(2) ++#define STV090x_OFFST_Px_TMGREG_FIELD 0 ++#define STV090x_WIDTH_Px_TMGREG_FIELD 8 ++ ++#define STV090x_Px_TMGLOCKy(__x, __y) (0xF46C - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_TMGLOCK0 STV090x_Px_TMGLOCKy(1, 0) ++#define STV090x_P1_TMGLOCK1 STV090x_Px_TMGLOCKy(1, 1) ++#define STV090x_P2_TMGLOCK0 STV090x_Px_TMGLOCKy(2, 0) ++#define STV090x_P2_TMGLOCK1 STV090x_Px_TMGLOCKy(2, 1) ++#define STV090x_OFFST_Px_TMGLOCK_LEVEL_FIELD 0 ++#define STV090x_WIDTH_Px_TMGLOCK_LEVEL_FIELD 8 ++ ++#define STV090x_Px_TMGOBS(__x) (0xF46D - (__x - 1) * 0x200) ++#define STV090x_P1_TMGOBS STV090x_Px_TMGOBS(1) ++#define STV090x_P2_TMGOBS STV090x_Px_TMGOBS(2) ++#define STV090x_OFFST_Px_ROLLOFF_STATUS_FIELD 6 ++#define STV090x_WIDTH_Px_ROLLOFF_STATUS_FIELD 2 ++ ++#define STV090x_Px_EQUALCFG(__x) (0xF46F - (__x - 1) * 0x200) ++#define STV090x_P1_EQUALCFG STV090x_Px_EQUALCFG(1) ++#define STV090x_P2_EQUALCFG STV090x_Px_EQUALCFG(2) ++#define STV090x_OFFST_Px_EQUAL_ON_FIELD 6 ++#define STV090x_WIDTH_Px_EQUAL_ON_FIELD 1 ++#define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0 ++#define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3 ++ ++#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1)) ++#define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1) ++#define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2) ++#define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3) ++#define STV090x_P1_EQUAI4 STV090x_Px_EQUAIy(1, 4) ++#define STV090x_P1_EQUAI5 STV090x_Px_EQUAIy(1, 5) ++#define STV090x_P1_EQUAI6 STV090x_Px_EQUAIy(1, 6) ++#define STV090x_P1_EQUAI7 STV090x_Px_EQUAIy(1, 7) ++#define STV090x_P1_EQUAI8 STV090x_Px_EQUAIy(1, 8) ++ ++#define STV090x_P2_EQUAI1 STV090x_Px_EQUAIy(2, 1) ++#define STV090x_P2_EQUAI2 STV090x_Px_EQUAIy(2, 2) ++#define STV090x_P2_EQUAI3 STV090x_Px_EQUAIy(2, 3) ++#define STV090x_P2_EQUAI4 STV090x_Px_EQUAIy(2, 4) ++#define STV090x_P2_EQUAI5 STV090x_Px_EQUAIy(2, 5) ++#define STV090x_P2_EQUAI6 STV090x_Px_EQUAIy(2, 6) ++#define STV090x_P2_EQUAI7 STV090x_Px_EQUAIy(2, 7) ++#define STV090x_P2_EQUAI8 STV090x_Px_EQUAIy(2, 8) ++#define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0 ++#define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8 ++ ++#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1)) ++#define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1) ++#define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2) ++#define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3) ++#define STV090x_P1_EQUAQ4 STV090x_Px_EQUAQy(1, 4) ++#define STV090x_P1_EQUAQ5 STV090x_Px_EQUAQy(1, 5) ++#define STV090x_P1_EQUAQ6 STV090x_Px_EQUAQy(1, 6) ++#define STV090x_P1_EQUAQ7 STV090x_Px_EQUAQy(1, 7) ++#define STV090x_P1_EQUAQ8 STV090x_Px_EQUAQy(1, 8) ++ ++#define STV090x_P2_EQUAQ1 STV090x_Px_EQUAQy(2, 1) ++#define STV090x_P2_EQUAQ2 STV090x_Px_EQUAQy(2, 2) ++#define STV090x_P2_EQUAQ3 STV090x_Px_EQUAQy(2, 3) ++#define STV090x_P2_EQUAQ4 STV090x_Px_EQUAQy(2, 4) ++#define STV090x_P2_EQUAQ5 STV090x_Px_EQUAQy(2, 5) ++#define STV090x_P2_EQUAQ6 STV090x_Px_EQUAQy(2, 6) ++#define STV090x_P2_EQUAQ7 STV090x_Px_EQUAQy(2, 7) ++#define STV090x_P2_EQUAQ8 STV090x_Px_EQUAQy(2, 8) ++#define STV090x_OFFST_Px_EQUA_ACCQy_FIELD 0 ++#define STV090x_WIDTH_Px_EQUA_ACCQy_FIELD 8 ++ ++#define STV090x_Px_NNOSDATATy(__x, __y) (0xf481 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NNOSDATAT0 STV090x_Px_NNOSDATATy(1, 0) ++#define STV090x_P1_NNOSDATAT1 STV090x_Px_NNOSDATATy(1, 1) ++#define STV090x_P2_NNOSDATAT0 STV090x_Px_NNOSDATATy(2, 0) ++#define STV090x_P2_NNOSDATAT1 STV090x_Px_NNOSDATATy(2, 1) ++#define STV090x_OFFST_Px_NOSDATAT_NORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSDATAT_NORMED_FIELD 8 ++ ++#define STV090x_Px_NNOSDATAy(__x, __y) (0xf483 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NNOSDATA0 STV090x_Px_NNOSDATAy(1, 0) ++#define STV090x_P1_NNOSDATA1 STV090x_Px_NNOSDATAy(1, 1) ++#define STV090x_P2_NNOSDATA0 STV090x_Px_NNOSDATAy(2, 0) ++#define STV090x_P2_NNOSDATA1 STV090x_Px_NNOSDATAy(2, 1) ++#define STV090x_OFFST_Px_NOSDATA_NORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSDATA_NORMED_FIELD 8 ++ ++#define STV090x_Px_NNOSPLHTy(__x, __y) (0xf485 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NNOSPLHT0 STV090x_Px_NNOSPLHTy(1, 0) ++#define STV090x_P1_NNOSPLHT1 STV090x_Px_NNOSPLHTy(1, 1) ++#define STV090x_P2_NNOSPLHT0 STV090x_Px_NNOSPLHTy(2, 0) ++#define STV090x_P2_NNOSPLHT1 STV090x_Px_NNOSPLHTy(2, 1) ++#define STV090x_OFFST_Px_NOSPLHT_NORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSPLHT_NORMED_FIELD 8 ++ ++#define STV090x_Px_NNOSPLHy(__x, __y) (0xf487 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NNOSPLH0 STV090x_Px_NNOSPLHy(1, 0) ++#define STV090x_P1_NNOSPLH1 STV090x_Px_NNOSPLHy(1, 1) ++#define STV090x_P2_NNOSPLH0 STV090x_Px_NNOSPLHy(2, 0) ++#define STV090x_P2_NNOSPLH1 STV090x_Px_NNOSPLHy(2, 1) ++#define STV090x_OFFST_Px_NOSPLH_NORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSPLH_NORMED_FIELD 8 ++ ++#define STV090x_Px_NOSDATATy(__x, __y) (0xf489 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NOSDATAT0 STV090x_Px_NOSDATATy(1, 0) ++#define STV090x_P1_NOSDATAT1 STV090x_Px_NOSDATATy(1, 1) ++#define STV090x_P2_NOSDATAT0 STV090x_Px_NOSDATATy(2, 0) ++#define STV090x_P2_NOSDATAT1 STV090x_Px_NOSDATATy(2, 1) ++#define STV090x_OFFST_Px_NOSDATAT_UNNORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSDATAT_UNNORMED_FIELD 8 ++ ++#define STV090x_Px_NOSDATAy(__x, __y) (0xf48b - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NOSDATA0 STV090x_Px_NOSDATAy(1, 0) ++#define STV090x_P1_NOSDATA1 STV090x_Px_NOSDATAy(1, 1) ++#define STV090x_P2_NOSDATA0 STV090x_Px_NOSDATAy(2, 0) ++#define STV090x_P2_NOSDATA1 STV090x_Px_NOSDATAy(2, 1) ++#define STV090x_OFFST_Px_NOSDATA_UNNORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSDATA_UNNORMED_FIELD 8 ++ ++#define STV090x_Px_NOSPLHTy(__x, __y) (0xf48d - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NOSPLHT0 STV090x_Px_NOSPLHTy(1, 0) ++#define STV090x_P1_NOSPLHT1 STV090x_Px_NOSPLHTy(1, 1) ++#define STV090x_P2_NOSPLHT0 STV090x_Px_NOSPLHTy(2, 0) ++#define STV090x_P2_NOSPLHT1 STV090x_Px_NOSPLHTy(2, 1) ++#define STV090x_OFFST_Px_NOSPLHT_UNNORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSPLHT_UNNORMED_FIELD 8 ++ ++#define STV090x_Px_NOSPLHy(__x, __y) (0xf48f - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0) ++#define STV090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1) ++#define STV090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0) ++#define STV090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1) ++#define STV090x_OFFST_Px_NOSPLH_UNNORMED_FIELD 0 ++#define STV090x_WIDTH_Px_NOSPLH_UNNORMED_FIELD 8 ++ ++#define STV090x_Px_CAR2CFG(__x) (0xf490 - (__x - 1) * 0x200) ++#define STV090x_P1_CAR2CFG STV090x_Px_CAR2CFG(1) ++#define STV090x_P2_CAR2CFG STV090x_Px_CAR2CFG(2) ++#define STV090x_OFFST_Px_PN4_SELECT_FIELD 6 ++#define STV090x_WIDTH_Px_PN4_SELECT_FIELD 1 ++#define STV090x_OFFST_Px_CFR2_STOPDVBS1_FIELD 5 ++#define STV090x_WIDTH_Px_CFR2_STOPDVBS1_FIELD 1 ++#define STV090x_OFFST_Px_ROTA2ON_FIELD 2 ++#define STV090x_WIDTH_Px_ROTA2ON_FIELD 1 ++#define STV090x_OFFST_Px_PH_DET_ALGO2_FIELD 0 ++#define STV090x_WIDTH_Px_PH_DET_ALGO2_FIELD 2 ++ ++#define STV090x_Px_ACLC2(__x) (0xf491 - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC2 STV090x_Px_ACLC2(1) ++#define STV090x_P2_ACLC2 STV090x_Px_ACLC2(2) ++#define STV090x_OFFST_Px_CAR2_ALPHA_MANT_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2_ALPHA_MANT_FIELD 2 ++#define STV090x_OFFST_Px_CAR2_ALPHA_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2_ALPHA_EXP_FIELD 4 ++ ++#define STV090x_Px_BCLC2(__x) (0xf492 - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC2 STV090x_Px_BCLC2(1) ++#define STV090x_P2_BCLC2 STV090x_Px_BCLC2(2) ++#define STV090x_OFFST_Px_CAR2_BETA_MANT_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2_BETA_MANT_FIELD 2 ++#define STV090x_OFFST_Px_CAR2_BETA_EXP_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2_BETA_EXP_FIELD 4 ++ ++#define STV090x_Px_ACLC2S2Q(__x) (0xf497 - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC2S2Q STV090x_Px_ACLC2S2Q(1) ++#define STV090x_P2_ACLC2S2Q STV090x_Px_ACLC2S2Q(2) ++#define STV090x_OFFST_Px_ENAB_SPSKSYMB_FIELD 7 ++#define STV090x_WIDTH_Px_ENAB_SPSKSYMB_FIELD 1 ++#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_E_FIELD 4 ++ ++#define STV090x_Px_ACLC2S28(__x) (0xf498 - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC2S28 STV090x_Px_ACLC2S28(1) ++#define STV090x_P2_ACLC2S28 STV090x_Px_ACLC2S28(2) ++#define STV090x_OFFST_Px_CAR2S2_8_ALPH_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_8_ALPH_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_E_FIELD 4 ++ ++#define STV090x_Px_ACLC2S216A(__x) (0xf499 - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC2S216A STV090x_Px_ACLC2S216A(1) ++#define STV090x_P2_ACLC2S216A STV090x_Px_ACLC2S216A(2) ++#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4 ++ ++#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200) ++#define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1) ++#define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2) ++#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_E_FIELD 4 ++ ++#define STV090x_Px_BCLC2S2Q(__x) (0xf49c - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC2S2Q STV090x_Px_BCLC2S2Q(1) ++#define STV090x_P2_BCLC2S2Q STV090x_Px_BCLC2S2Q(2) ++#define STV090x_OFFST_Px_CAR2S2_Q_BETA_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_Q_BETA_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_E_FIELD 4 ++ ++#define STV090x_Px_BCLC2S28(__x) (0xf49d - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC2S28 STV090x_Px_BCLC2S28(1) ++#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(2) ++#define STV090x_OFFST_Px_CAR2S2_8_BETA_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_8_BETA_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4 ++ ++#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) ++#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(2) ++#define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4 ++ ++#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) ++#define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) ++#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(2) ++#define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 ++#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_M_FIELD 2 ++#define STV090x_OFFST_Px_CAR2S2_32A_BETA_E_FIELD 0 ++#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_E_FIELD 4 ++ ++#define STV090x_Px_PLROOT2(__x) (0xf4ac - (__x - 1) * 0x200) ++#define STV090x_P1_PLROOT2 STV090x_Px_PLROOT2(1) ++#define STV090x_P2_PLROOT2 STV090x_Px_PLROOT2(2) ++#define STV090x_OFFST_Px_PLSCRAMB_MODE_FIELD 2 ++#define STV090x_WIDTH_Px_PLSCRAMB_MODE_FIELD 2 ++#define STV090x_OFFST_Px_PLSCRAMB_ROOT_FIELD 0 ++#define STV090x_WIDTH_Px_PLSCRAMB_ROOT_FIELD 2 ++ ++#define STV090x_Px_PLROOT1(__x) (0xf4ad - (__x - 1) * 0x200) ++#define STV090x_P1_PLROOT1 STV090x_Px_PLROOT1(1) ++#define STV090x_P2_PLROOT1 STV090x_Px_PLROOT1(2) ++#define STV090x_OFFST_Px_PLSCRAMB_ROOT1_FIELD 0 ++#define STV090x_WIDTH_Px_PLSCRAMB_ROOT1_FIELD 8 ++ ++#define STV090x_Px_PLROOT0(__x) (0xf4ae - (__x - 1) * 0x200) ++#define STV090x_P1_PLROOT0 STV090x_Px_PLROOT0(1) ++#define STV090x_P2_PLROOT0 STV090x_Px_PLROOT0(2) ++#define STV090x_OFFST_Px_PLSCRAMB_ROOT0_FIELD 0 ++#define STV090x_WIDTH_Px_PLSCRAMB_ROOT0_FIELD 8 ++ ++#define STV090x_Px_MODCODLST0(__x) (0xf4b0 - (__x - 1) * 0x200) /* check */ ++#define STV090x_P1_MODCODLST0 STV090x_Px_MODCODLST0(1) ++#define STV090x_P2_MODCODLST0 STV090x_Px_MODCODLST0(2) ++ ++#define STV090x_Px_MODCODLST1(__x) (0xf4b1 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1) ++#define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2) ++#define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4 ++#define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4 ++ ++#define STV090x_Px_MODCODLST2(__x) (0xf4b2 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST2 STV090x_Px_MODCODLST2(1) ++#define STV090x_P2_MODCODLST2 STV090x_Px_MODCODLST2(2) ++#define STV090x_OFFST_Px_DIS_32PSK_8_9_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_32PSK_8_9_FIELD 4 ++#define STV090x_OFFST_Px_DIS_32PSK_5_6_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_32PSK_5_6_FIELD 4 ++ ++#define STV090x_Px_MODCODLST3(__x) (0xf4b3 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST3 STV090x_Px_MODCODLST3(1) ++#define STV090x_P2_MODCODLST3 STV090x_Px_MODCODLST3(2) ++#define STV090x_OFFST_Px_DIS_32PSK_4_5_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_32PSK_4_5_FIELD 4 ++#define STV090x_OFFST_Px_DIS_32PSK_3_4_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_32PSK_3_4_FIELD 4 ++ ++#define STV090x_Px_MODCODLST4(__x) (0xf4b4 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST4 STV090x_Px_MODCODLST4(1) ++#define STV090x_P2_MODCODLST4 STV090x_Px_MODCODLST4(2) ++#define STV090x_OFFST_Px_DIS_16PSK_9_10_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_16PSK_9_10_FIELD 4 ++#define STV090x_OFFST_Px_DIS_16PSK_8_9_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_16PSK_8_9_FIELD 4 ++ ++#define STV090x_Px_MODCODLST5(__x) (0xf4b5 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST5 STV090x_Px_MODCODLST5(1) ++#define STV090x_P2_MODCODLST5 STV090x_Px_MODCODLST5(2) ++#define STV090x_OFFST_Px_DIS_16PSK_5_6_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_16PSK_5_6_FIELD 4 ++#define STV090x_OFFST_Px_DIS_16PSK_4_5_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_16PSK_4_5_FIELD 4 ++ ++#define STV090x_Px_MODCODLST6(__x) (0xf4b6 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST6 STV090x_Px_MODCODLST6(1) ++#define STV090x_P2_MODCODLST6 STV090x_Px_MODCODLST6(2) ++#define STV090x_OFFST_Px_DIS_16PSK_3_4_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_16PSK_3_4_FIELD 4 ++#define STV090x_OFFST_Px_DIS_16PSK_2_3_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_16PSK_2_3_FIELD 4 ++ ++#define STV090x_Px_MODCODLST7(__x) (0xf4b7 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST7 STV090x_Px_MODCODLST7(1) ++#define STV090x_P2_MODCODLST7 STV090x_Px_MODCODLST7(2) ++#define STV090x_OFFST_Px_DIS_8P_9_10_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_8P_9_10_FIELD 4 ++#define STV090x_OFFST_Px_DIS_8P_8_9_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_8P_8_9_FIELD 4 ++ ++#define STV090x_Px_MODCODLST8(__x) (0xf4b8 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST8 STV090x_Px_MODCODLST8(1) ++#define STV090x_P2_MODCODLST8 STV090x_Px_MODCODLST8(2) ++#define STV090x_OFFST_Px_DIS_8P_5_6_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_8P_5_6_FIELD 4 ++#define STV090x_OFFST_Px_DIS_8P_3_4_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_8P_3_4_FIELD 4 ++ ++#define STV090x_Px_MODCODLST9(__x) (0xf4b9 - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLST9 STV090x_Px_MODCODLST9(1) ++#define STV090x_P2_MODCODLST9 STV090x_Px_MODCODLST9(2) ++#define STV090x_OFFST_Px_DIS_8P_2_3_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_8P_2_3_FIELD 4 ++#define STV090x_OFFST_Px_DIS_8P_3_5_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_8P_3_5_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTA(__x) (0xf4ba - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTA STV090x_Px_MODCODLSTA(1) ++#define STV090x_P2_MODCODLSTA STV090x_Px_MODCODLSTA(2) ++#define STV090x_OFFST_Px_DIS_QP_9_10_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_9_10_FIELD 4 ++#define STV090x_OFFST_Px_DIS_QP_8_9_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_QP_8_9_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTB(__x) (0xf4bb - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTB STV090x_Px_MODCODLSTB(1) ++#define STV090x_P2_MODCODLSTB STV090x_Px_MODCODLSTB(2) ++#define STV090x_OFFST_Px_DIS_QP_5_6_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_5_6_FIELD 4 ++#define STV090x_OFFST_Px_DIS_QP_4_5_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_QP_4_5_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTC(__x) (0xf4bc - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTC STV090x_Px_MODCODLSTC(1) ++#define STV090x_P2_MODCODLSTC STV090x_Px_MODCODLSTC(2) ++#define STV090x_OFFST_Px_DIS_QP_3_4_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_3_4_FIELD 4 ++#define STV090x_OFFST_Px_DIS_QP_2_3_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_QP_2_3_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTD(__x) (0xf4bd - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTD STV090x_Px_MODCODLSTD(1) ++#define STV090x_P2_MODCODLSTD STV090x_Px_MODCODLSTD(2) ++#define STV090x_OFFST_Px_DIS_QP_3_5_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_3_5_FIELD 4 ++#define STV090x_OFFST_Px_DIS_QP_1_2_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_QP_1_2_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTE(__x) (0xf4be - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTE STV090x_Px_MODCODLSTE(1) ++#define STV090x_P2_MODCODLSTE STV090x_Px_MODCODLSTE(2) ++#define STV090x_OFFST_Px_DIS_QP_2_5_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_2_5_FIELD 4 ++#define STV090x_OFFST_Px_DIS_QP_1_3_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_QP_1_3_FIELD 4 ++ ++#define STV090x_Px_MODCODLSTF(__x) (0xf4bf - (__x - 1) * 0x200) ++#define STV090x_P1_MODCODLSTF STV090x_Px_MODCODLSTF(1) ++#define STV090x_P2_MODCODLSTF STV090x_Px_MODCODLSTF(2) ++#define STV090x_OFFST_Px_DIS_QP_1_4_FIELD 4 ++#define STV090x_WIDTH_Px_DIS_QP_1_4_FIELD 4 ++ ++#define STV090x_Px_GAUSSR0(__x) (0xf4c0 - (__x - 1) * 0x200) ++#define STV090x_P1_GAUSSR0 STV090x_Px_GAUSSR0(1) ++#define STV090x_P2_GAUSSR0 STV090x_Px_GAUSSR0(2) ++#define STV090x_OFFST_Px_EN_CCIMODE_FIELD 7 ++#define STV090x_WIDTH_Px_EN_CCIMODE_FIELD 1 ++#define STV090x_OFFST_Px_R0_GAUSSIEN_FIELD 0 ++#define STV090x_WIDTH_Px_R0_GAUSSIEN_FIELD 7 ++ ++#define STV090x_Px_CCIR0(__x) (0xf4c1 - (__x - 1) * 0x200) ++#define STV090x_P1_CCIR0 STV090x_Px_CCIR0(1) ++#define STV090x_P2_CCIR0 STV090x_Px_CCIR0(2) ++#define STV090x_OFFST_Px_CCIDETECT_PLH_FIELD 7 ++#define STV090x_WIDTH_Px_CCIDETECT_PLH_FIELD 1 ++#define STV090x_OFFST_Px_R0_CCI_FIELD 0 ++#define STV090x_WIDTH_Px_R0_CCI_FIELD 7 ++ ++#define STV090x_Px_CCIQUANT(__x) (0xf4c2 - (__x - 1) * 0x200) ++#define STV090x_P1_CCIQUANT STV090x_Px_CCIQUANT(1) ++#define STV090x_P2_CCIQUANT STV090x_Px_CCIQUANT(2) ++#define STV090x_OFFST_Px_CCI_BETA_FIELD 5 ++#define STV090x_WIDTH_Px_CCI_BETA_FIELD 3 ++#define STV090x_OFFST_Px_CCI_QUANT_FIELD 0 ++#define STV090x_WIDTH_Px_CCI_QUANT_FIELD 5 ++ ++#define STV090x_Px_CCITHRESH(__x) (0xf4c3 - (__x - 1) * 0x200) ++#define STV090x_P1_CCITHRESH STV090x_Px_CCITHRESH(1) ++#define STV090x_P2_CCITHRESH STV090x_Px_CCITHRESH(2) ++#define STV090x_OFFST_Px_CCI_THRESHOLD_FIELD 0 ++#define STV090x_WIDTH_Px_CCI_THRESHOLD_FIELD 8 ++ ++#define STV090x_Px_CCIACC(__x) (0xf4c4 - (__x - 1) * 0x200) ++#define STV090x_P1_CCIACC STV090x_Px_CCIACC(1) ++#define STV090x_P2_CCIACC STV090x_Px_CCIACC(2) ++#define STV090x_OFFST_Px_CCI_VALUE_FIELD 0 ++#define STV090x_WIDTH_Px_CCI_VALUE_FIELD 8 ++ ++#define STV090x_Px_DMDRESCFG(__x) (0xF4C6 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDRESCFG STV090x_Px_DMDRESCFG(1) ++#define STV090x_P2_DMDRESCFG STV090x_Px_DMDRESCFG(2) ++#define STV090x_OFFST_Px_DMDRES_RESET_FIELD 7 ++#define STV090x_WIDTH_Px_DMDRES_RESET_FIELD 1 ++ ++#define STV090x_Px_DMDRESADR(__x) (0xF4C7 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDRESADR STV090x_Px_DMDRESADR(1) ++#define STV090x_P2_DMDRESADR STV090x_Px_DMDRESADR(2) ++#define STV090x_OFFST_Px_DMDRES_RESNBR_FIELD 0 ++#define STV090x_WIDTH_Px_DMDRES_RESNBR_FIELD 4 ++ ++#define STV090x_Px_DMDRESDATAy(__x, __y) (0xF4C8 - (__x - 1) * 0x200 + (7 - __y)) ++#define STV090x_P1_DMDRESDATA0 STV090x_Px_DMDRESDATAy(1, 0) ++#define STV090x_P1_DMDRESDATA1 STV090x_Px_DMDRESDATAy(1, 1) ++#define STV090x_P1_DMDRESDATA2 STV090x_Px_DMDRESDATAy(1, 2) ++#define STV090x_P1_DMDRESDATA3 STV090x_Px_DMDRESDATAy(1, 3) ++#define STV090x_P1_DMDRESDATA4 STV090x_Px_DMDRESDATAy(1, 4) ++#define STV090x_P1_DMDRESDATA5 STV090x_Px_DMDRESDATAy(1, 5) ++#define STV090x_P1_DMDRESDATA6 STV090x_Px_DMDRESDATAy(1, 6) ++#define STV090x_P1_DMDRESDATA7 STV090x_Px_DMDRESDATAy(1, 7) ++#define STV090x_P2_DMDRESDATA0 STV090x_Px_DMDRESDATAy(2, 0) ++#define STV090x_P2_DMDRESDATA1 STV090x_Px_DMDRESDATAy(2, 1) ++#define STV090x_P2_DMDRESDATA2 STV090x_Px_DMDRESDATAy(2, 2) ++#define STV090x_P2_DMDRESDATA3 STV090x_Px_DMDRESDATAy(2, 3) ++#define STV090x_P2_DMDRESDATA4 STV090x_Px_DMDRESDATAy(2, 4) ++#define STV090x_P2_DMDRESDATA5 STV090x_Px_DMDRESDATAy(2, 5) ++#define STV090x_P2_DMDRESDATA6 STV090x_Px_DMDRESDATAy(2, 6) ++#define STV090x_P2_DMDRESDATA7 STV090x_Px_DMDRESDATAy(2, 7) ++#define STV090x_OFFST_Px_DMDRES_DATA_FIELD 0 ++#define STV090x_WIDTH_Px_DMDRES_DATA_FIELD 8 ++ ++#define STV090x_Px_FFEIy(__x, __y) (0xf4d0 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) ++#define STV090x_P1_FFEI1 STV090x_Px_FFEIy(1, 1) ++#define STV090x_P1_FFEI2 STV090x_Px_FFEIy(1, 2) ++#define STV090x_P1_FFEI3 STV090x_Px_FFEIy(1, 3) ++#define STV090x_P1_FFEI4 STV090x_Px_FFEIy(1, 4) ++#define STV090x_P2_FFEI1 STV090x_Px_FFEIy(2, 1) ++#define STV090x_P2_FFEI2 STV090x_Px_FFEIy(2, 2) ++#define STV090x_P2_FFEI3 STV090x_Px_FFEIy(2, 3) ++#define STV090x_P2_FFEI4 STV090x_Px_FFEIy(2, 4) ++#define STV090x_OFFST_Px_FFE_ACCIy_FIELD 0 ++#define STV090x_WIDTH_Px_FFE_ACCIy_FIELD 8 ++ ++#define STV090x_Px_FFEQy(__x, __y) (0xf4d1 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) ++#define STV090x_P1_FFEQ1 STV090x_Px_FFEQy(1, 1) ++#define STV090x_P1_FFEQ2 STV090x_Px_FFEQy(1, 2) ++#define STV090x_P1_FFEQ3 STV090x_Px_FFEQy(1, 3) ++#define STV090x_P1_FFEQ4 STV090x_Px_FFEQy(1, 4) ++#define STV090x_P2_FFEQ1 STV090x_Px_FFEQy(2, 1) ++#define STV090x_P2_FFEQ2 STV090x_Px_FFEQy(2, 2) ++#define STV090x_P2_FFEQ3 STV090x_Px_FFEQy(2, 3) ++#define STV090x_P2_FFEQ4 STV090x_Px_FFEQy(2, 4) ++#define STV090x_OFFST_Px_FFE_ACCQy_FIELD 0 ++#define STV090x_WIDTH_Px_FFE_ACCQy_FIELD 8 ++ ++#define STV090x_Px_FFECFG(__x) (0xf4d8 - (__x - 1) * 0x200) ++#define STV090x_P1_FFECFG STV090x_Px_FFECFG(1) ++#define STV090x_P2_FFECFG STV090x_Px_FFECFG(2) ++#define STV090x_OFFST_Px_EQUALFFE_ON_FIELD 6 ++#define STV090x_WIDTH_Px_EQUALFFE_ON_FIELD 1 ++ ++#define STV090x_Px_SMAPCOEF7(__x) (0xf500 - (__x - 1) * 0x200) ++#define STV090x_P1_SMAPCOEF7 STV090x_Px_SMAPCOEF7(1) ++#define STV090x_P2_SMAPCOEF7 STV090x_Px_SMAPCOEF7(2) ++#define STV090x_OFFST_Px_DIS_QSCALE_FIELD 7 ++#define STV090x_WIDTH_Px_DIS_QSCALE_FIELD 1 ++#define STV090x_OFFST_Px_SMAPCOEF_Q_LLR12_FIELD 0 ++#define STV090x_WIDTH_Px_SMAPCOEF_Q_LLR12_FIELD 7 ++ ++#define STV090x_Px_SMAPCOEF6(__x) (0xf501 - (__x - 1) * 0x200) ++#define STV090x_P1_SMAPCOEF6 STV090x_Px_SMAPCOEF6(1) ++#define STV090x_P2_SMAPCOEF6 STV090x_Px_SMAPCOEF6(2) ++#define STV090x_OFFST_Px_ADJ_8PSKLLR1_FIELD 2 ++#define STV090x_WIDTH_Px_ADJ_8PSKLLR1_FIELD 1 ++#define STV090x_OFFST_Px_OLD_8PSKLLR1_FIELD 1 ++#define STV090x_WIDTH_Px_OLD_8PSKLLR1_FIELD 1 ++#define STV090x_OFFST_Px_DIS_AB8PSK_FIELD 0 ++#define STV090x_WIDTH_Px_DIS_AB8PSK_FIELD 1 ++ ++#define STV090x_Px_SMAPCOEF5(__x) (0xf502 - (__x - 1) * 0x200) ++#define STV090x_P1_SMAPCOEF5 STV090x_Px_SMAPCOEF5(1) ++#define STV090x_P2_SMAPCOEF5 STV090x_Px_SMAPCOEF5(2) ++#define STV090x_OFFST_Px_DIS_8SCALE_FIELD 7 ++#define STV090x_WIDTH_Px_DIS_8SCALE_FIELD 1 ++#define STV090x_OFFST_Px_SMAPCOEF_8P_LLR23_FIELD 0 ++#define STV090x_WIDTH_Px_SMAPCOEF_8P_LLR23_FIELD 7 ++ ++#define STV090x_Px_DMDPLHSTAT(__x) (0xF520 - (__x - 1) * 0x200) ++#define STV090x_P1_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(1) ++#define STV090x_P2_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(2) ++#define STV090x_OFFST_Px_PLH_STATISTIC_FIELD 0 ++#define STV090x_WIDTH_Px_PLH_STATISTIC_FIELD 8 ++ ++#define STV090x_Px_LOCKTIMEy(__x, __y) (0xF525 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_LOCKTIME0 STV090x_Px_LOCKTIMEy(1, 0) ++#define STV090x_P1_LOCKTIME1 STV090x_Px_LOCKTIMEy(1, 1) ++#define STV090x_P1_LOCKTIME2 STV090x_Px_LOCKTIMEy(1, 2) ++#define STV090x_P1_LOCKTIME3 STV090x_Px_LOCKTIMEy(1, 3) ++#define STV090x_P2_LOCKTIME0 STV090x_Px_LOCKTIMEy(2, 0) ++#define STV090x_P2_LOCKTIME1 STV090x_Px_LOCKTIMEy(2, 1) ++#define STV090x_P2_LOCKTIME2 STV090x_Px_LOCKTIMEy(2, 2) ++#define STV090x_P2_LOCKTIME3 STV090x_Px_LOCKTIMEy(2, 3) ++#define STV090x_OFFST_Px_DEMOD_LOCKTIME_FIELD 0 ++#define STV090x_WIDTH_Px_DEMOD_LOCKTIME_FIELD 8 ++ ++#define STV090x_Px_TNRCFG(__x) (0xf4e0 - (__x - 1) * 0x200) /* check */ ++#define STV090x_P1_TNRCFG STV090x_Px_TNRCFG(1) ++#define STV090x_P2_TNRCFG STV090x_Px_TNRCFG(2) ++ ++#define STV090x_Px_TNRCFG2(__x) (0xf4e1 - (__x - 1) * 0x200) ++#define STV090x_P1_TNRCFG2 STV090x_Px_TNRCFG2(1) ++#define STV090x_P2_TNRCFG2 STV090x_Px_TNRCFG2(2) ++#define STV090x_OFFST_Px_TUN_IQSWAP_FIELD 7 ++#define STV090x_WIDTH_Px_TUN_IQSWAP_FIELD 1 ++ ++#define STV090x_Px_VITSCALE(__x) (0xf532 - (__x - 1) * 0x200) ++#define STV090x_P1_VITSCALE STV090x_Px_VITSCALE(1) ++#define STV090x_P2_VITSCALE STV090x_Px_VITSCALE(2) ++#define STV090x_OFFST_Px_NVTH_NOSRANGE_FIELD 7 ++#define STV090x_WIDTH_Px_NVTH_NOSRANGE_FIELD 1 ++#define STV090x_OFFST_Px_VERROR_MAXMODE_FIELD 6 ++#define STV090x_WIDTH_Px_VERROR_MAXMODE_FIELD 1 ++#define STV090x_OFFST_Px_NSLOWSN_LOCKED_FIELD 3 ++#define STV090x_WIDTH_Px_NSLOWSN_LOCKED_FIELD 1 ++#define STV090x_OFFST_Px_DIS_RSFLOCK_FIELD 1 ++#define STV090x_WIDTH_Px_DIS_RSFLOCK_FIELD 1 ++ ++#define STV090x_Px_FECM(__x) (0xf533 - (__x - 1) * 0x200) ++#define STV090x_P1_FECM STV090x_Px_FECM(1) ++#define STV090x_P2_FECM STV090x_Px_FECM(2) ++#define STV090x_OFFST_Px_DSS_DVB_FIELD 7 ++#define STV090x_WIDTH_Px_DSS_DVB_FIELD 1 ++#define STV090x_OFFST_Px_DSS_SRCH_FIELD 4 ++#define STV090x_WIDTH_Px_DSS_SRCH_FIELD 1 ++#define STV090x_OFFST_Px_SYNCVIT_FIELD 1 ++#define STV090x_WIDTH_Px_SYNCVIT_FIELD 1 ++#define STV090x_OFFST_Px_IQINV_FIELD 0 ++#define STV090x_WIDTH_Px_IQINV_FIELD 1 ++ ++#define STV090x_Px_VTH12(__x) (0xf534 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH12 STV090x_Px_VTH12(1) ++#define STV090x_P2_VTH12 STV090x_Px_VTH12(2) ++#define STV090x_OFFST_Px_VTH12_FIELD 0 ++#define STV090x_WIDTH_Px_VTH12_FIELD 8 ++ ++#define STV090x_Px_VTH23(__x) (0xf535 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH23 STV090x_Px_VTH23(1) ++#define STV090x_P2_VTH23 STV090x_Px_VTH23(2) ++#define STV090x_OFFST_Px_VTH23_FIELD 0 ++#define STV090x_WIDTH_Px_VTH23_FIELD 8 ++ ++#define STV090x_Px_VTH34(__x) (0xf536 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH34 STV090x_Px_VTH34(1) ++#define STV090x_P2_VTH34 STV090x_Px_VTH34(2) ++#define STV090x_OFFST_Px_VTH34_FIELD 0 ++#define STV090x_WIDTH_Px_VTH34_FIELD 8 ++ ++#define STV090x_Px_VTH56(__x) (0xf537 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH56 STV090x_Px_VTH56(1) ++#define STV090x_P2_VTH56 STV090x_Px_VTH56(2) ++#define STV090x_OFFST_Px_VTH56_FIELD 0 ++#define STV090x_WIDTH_Px_VTH56_FIELD 8 ++ ++#define STV090x_Px_VTH67(__x) (0xf538 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH67 STV090x_Px_VTH67(1) ++#define STV090x_P2_VTH67 STV090x_Px_VTH67(2) ++#define STV090x_OFFST_Px_VTH67_FIELD 0 ++#define STV090x_WIDTH_Px_VTH67_FIELD 8 ++ ++#define STV090x_Px_VTH78(__x) (0xf539 - (__x - 1) * 0x200) ++#define STV090x_P1_VTH78 STV090x_Px_VTH78(1) ++#define STV090x_P2_VTH78 STV090x_Px_VTH78(2) ++#define STV090x_OFFST_Px_VTH78_FIELD 0 ++#define STV090x_WIDTH_Px_VTH78_FIELD 8 ++ ++#define STV090x_Px_VITCURPUN(__x) (0xf53a - (__x - 1) * 0x200) ++#define STV090x_P1_VITCURPUN STV090x_Px_VITCURPUN(1) ++#define STV090x_P2_VITCURPUN STV090x_Px_VITCURPUN(2) ++#define STV090x_OFFST_Px_VIT_CURPUN_FIELD 0 ++#define STV090x_WIDTH_Px_VIT_CURPUN_FIELD 5 ++ ++#define STV090x_Px_VERROR(__x) (0xf53b - (__x - 1) * 0x200) ++#define STV090x_P1_VERROR STV090x_Px_VERROR(1) ++#define STV090x_P2_VERROR STV090x_Px_VERROR(2) ++#define STV090x_OFFST_Px_REGERR_VIT_FIELD 0 ++#define STV090x_WIDTH_Px_REGERR_VIT_FIELD 8 ++ ++#define STV090x_Px_PRVIT(__x) (0xf53c - (__x - 1) * 0x200) ++#define STV090x_P1_PRVIT STV090x_Px_PRVIT(1) ++#define STV090x_P2_PRVIT STV090x_Px_PRVIT(2) ++#define STV090x_OFFST_Px_DIS_VTHLOCK_FIELD 6 ++#define STV090x_WIDTH_Px_DIS_VTHLOCK_FIELD 1 ++#define STV090x_OFFST_Px_E7_8VIT_FIELD 5 ++#define STV090x_WIDTH_Px_E7_8VIT_FIELD 1 ++#define STV090x_OFFST_Px_E6_7VIT_FIELD 4 ++#define STV090x_WIDTH_Px_E6_7VIT_FIELD 1 ++#define STV090x_OFFST_Px_E5_6VIT_FIELD 3 ++#define STV090x_WIDTH_Px_E5_6VIT_FIELD 1 ++#define STV090x_OFFST_Px_E3_4VIT_FIELD 2 ++#define STV090x_WIDTH_Px_E3_4VIT_FIELD 1 ++#define STV090x_OFFST_Px_E2_3VIT_FIELD 1 ++#define STV090x_WIDTH_Px_E2_3VIT_FIELD 1 ++#define STV090x_OFFST_Px_E1_2VIT_FIELD 0 ++#define STV090x_WIDTH_Px_E1_2VIT_FIELD 1 ++ ++#define STV090x_Px_VAVSRVIT(__x) (0xf53d - (__x - 1) * 0x200) ++#define STV090x_P1_VAVSRVIT STV090x_Px_VAVSRVIT(1) ++#define STV090x_P2_VAVSRVIT STV090x_Px_VAVSRVIT(2) ++#define STV090x_OFFST_Px_SNVIT_FIELD 4 ++#define STV090x_WIDTH_Px_SNVIT_FIELD 2 ++#define STV090x_OFFST_Px_TOVVIT_FIELD 2 ++#define STV090x_WIDTH_Px_TOVVIT_FIELD 2 ++#define STV090x_OFFST_Px_HYPVIT_FIELD 0 ++#define STV090x_WIDTH_Px_HYPVIT_FIELD 2 ++ ++#define STV090x_Px_VSTATUSVIT(__x) (0xf53e - (__x - 1) * 0x200) ++#define STV090x_P1_VSTATUSVIT STV090x_Px_VSTATUSVIT(1) ++#define STV090x_P2_VSTATUSVIT STV090x_Px_VSTATUSVIT(2) ++#define STV090x_OFFST_Px_PRFVIT_FIELD 4 ++#define STV090x_WIDTH_Px_PRFVIT_FIELD 1 ++#define STV090x_OFFST_Px_LOCKEDVIT_FIELD 3 ++#define STV090x_WIDTH_Px_LOCKEDVIT_FIELD 1 ++ ++#define STV090x_Px_VTHINUSE(__x) (0xf53f - (__x - 1) * 0x200) ++#define STV090x_P1_VTHINUSE STV090x_Px_VTHINUSE(1) ++#define STV090x_P2_VTHINUSE STV090x_Px_VTHINUSE(2) ++#define STV090x_OFFST_Px_VIT_INUSE_FIELD 0 ++#define STV090x_WIDTH_Px_VIT_INUSE_FIELD 8 ++ ++#define STV090x_Px_KDIV12(__x) (0xf540 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV12 STV090x_Px_KDIV12(1) ++#define STV090x_P2_KDIV12 STV090x_Px_KDIV12(2) ++#define STV090x_OFFST_Px_K_DIVIDER_12_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_12_FIELD 7 ++ ++#define STV090x_Px_KDIV23(__x) (0xf541 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV23 STV090x_Px_KDIV23(1) ++#define STV090x_P2_KDIV23 STV090x_Px_KDIV23(2) ++#define STV090x_OFFST_Px_K_DIVIDER_23_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_23_FIELD 7 ++ ++#define STV090x_Px_KDIV34(__x) (0xf542 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV34 STV090x_Px_KDIV34(1) ++#define STV090x_P2_KDIV34 STV090x_Px_KDIV34(2) ++#define STV090x_OFFST_Px_K_DIVIDER_34_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_34_FIELD 7 ++ ++#define STV090x_Px_KDIV56(__x) (0xf543 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV56 STV090x_Px_KDIV56(1) ++#define STV090x_P2_KDIV56 STV090x_Px_KDIV56(2) ++#define STV090x_OFFST_Px_K_DIVIDER_56_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_56_FIELD 7 ++ ++#define STV090x_Px_KDIV67(__x) (0xf544 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV67 STV090x_Px_KDIV67(1) ++#define STV090x_P2_KDIV67 STV090x_Px_KDIV67(2) ++#define STV090x_OFFST_Px_K_DIVIDER_67_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_67_FIELD 7 ++ ++#define STV090x_Px_KDIV78(__x) (0xf545 - (__x - 1) * 0x200) ++#define STV090x_P1_KDIV78 STV090x_Px_KDIV78(1) ++#define STV090x_P2_KDIV78 STV090x_Px_KDIV78(2) ++#define STV090x_OFFST_Px_K_DIVIDER_78_FIELD 0 ++#define STV090x_WIDTH_Px_K_DIVIDER_78_FIELD 7 ++ ++#define STV090x_Px_PDELCTRL1(__x) (0xf550 - (__x - 1) * 0x200) ++#define STV090x_P1_PDELCTRL1 STV090x_Px_PDELCTRL1(1) ++#define STV090x_P2_PDELCTRL1 STV090x_Px_PDELCTRL1(2) ++#define STV090x_OFFST_Px_INV_MISMASK_FIELD 7 ++#define STV090x_WIDTH_Px_INV_MISMASK_FIELD 1 ++#define STV090x_OFFST_Px_FILTER_EN_FIELD 5 ++#define STV090x_WIDTH_Px_FILTER_EN_FIELD 1 ++#define STV090x_OFFST_Px_EN_MIS00_FIELD 1 ++#define STV090x_WIDTH_Px_EN_MIS00_FIELD 1 ++#define STV090x_OFFST_Px_ALGOSWRST_FIELD 0 ++#define STV090x_WIDTH_Px_ALGOSWRST_FIELD 1 ++ ++#define STV090x_Px_PDELCTRL2(__x) (0xf551 - (__x - 1) * 0x200) ++#define STV090x_P1_PDELCTRL2 STV090x_Px_PDELCTRL2(1) ++#define STV090x_P2_PDELCTRL2 STV090x_Px_PDELCTRL2(2) ++#define STV090x_OFFST_Px_FORCE_CONTINUOUS 7 ++#define STV090x_WIDTH_Px_FORCE_CONTINUOUS 1 ++#define STV090x_OFFST_Px_RESET_UPKO_COUNT 6 ++#define STV090x_WIDTH_Px_RESET_UPKO_COUNT 1 ++#define STV090x_OFFST_Px_USER_PKTDELIN_NB 5 ++#define STV090x_WIDTH_Px_USER_PKTDELIN_NB 1 ++#define STV090x_OFFST_Px_FORCE_LOCKED 4 ++#define STV090x_WIDTH_Px_FORCE_LOCKED 1 ++#define STV090x_OFFST_Px_DATA_UNBBSCRAM 3 ++#define STV090x_WIDTH_Px_DATA_UNBBSCRAM 1 ++#define STV090x_OFFST_Px_FORCE_LONGPACKET 2 ++#define STV090x_WIDTH_Px_FORCE_LONGPACKET 1 ++#define STV090x_OFFST_Px_FRAME_MODE_FIELD 1 ++#define STV090x_WIDTH_Px_FRAME_MODE_FIELD 1 ++ ++#define STV090x_Px_HYSTTHRESH(__x) (0xf554 - (__x - 1) * 0x200) ++#define STV090x_P1_HYSTTHRESH STV090x_Px_HYSTTHRESH(1) ++#define STV090x_P2_HYSTTHRESH STV090x_Px_HYSTTHRESH(2) ++#define STV090x_OFFST_Px_UNLCK_THRESH_FIELD 4 ++#define STV090x_WIDTH_Px_UNLCK_THRESH_FIELD 4 ++#define STV090x_OFFST_Px_DELIN_LCK_THRESH_FIELD 0 ++#define STV090x_WIDTH_Px_DELIN_LCK_THRESH_FIELD 4 ++ ++#define STV090x_Px_ISIENTRY(__x) (0xf55e - (__x - 1) * 0x200) ++#define STV090x_P1_ISIENTRY STV090x_Px_ISIENTRY(1) ++#define STV090x_P2_ISIENTRY STV090x_Px_ISIENTRY(2) ++#define STV090x_OFFST_Px_ISI_ENTRY_FIELD 0 ++#define STV090x_WIDTH_Px_ISI_ENTRY_FIELD 8 ++ ++#define STV090x_Px_ISIBITENA(__x) (0xf55f - (__x - 1) * 0x200) ++#define STV090x_P1_ISIBITENA STV090x_Px_ISIBITENA(1) ++#define STV090x_P2_ISIBITENA STV090x_Px_ISIBITENA(2) ++#define STV090x_OFFST_Px_ISI_BIT_EN_FIELD 0 ++#define STV090x_WIDTH_Px_ISI_BIT_EN_FIELD 8 ++ ++#define STV090x_Px_MATSTRy(__x, __y) (0xf561 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_MATSTR0 STV090x_Px_MATSTRy(1, 0) ++#define STV090x_P1_MATSTR1 STV090x_Px_MATSTRy(1, 1) ++#define STV090x_P2_MATSTR0 STV090x_Px_MATSTRy(2, 0) ++#define STV090x_P2_MATSTR1 STV090x_Px_MATSTRy(2, 1) ++#define STV090x_OFFST_Px_MATYPE_CURRENT_FIELD 0 ++#define STV090x_WIDTH_Px_MATYPE_CURRENT_FIELD 8 ++ ++#define STV090x_Px_UPLSTRy(__x, __y) (0xf563 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_UPLSTR0 STV090x_Px_UPLSTRy(1, 0) ++#define STV090x_P1_UPLSTR1 STV090x_Px_UPLSTRy(1, 1) ++#define STV090x_P2_UPLSTR0 STV090x_Px_UPLSTRy(2, 0) ++#define STV090x_P2_UPLSTR1 STV090x_Px_UPLSTRy(2, 1) ++#define STV090x_OFFST_Px_UPL_CURRENT_FIELD 0 ++#define STV090x_WIDTH_Px_UPL_CURRENT_FIELD 8 ++ ++#define STV090x_Px_DFLSTRy(__x, __y) (0xf565 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_DFLSTR0 STV090x_Px_DFLSTRy(1, 0) ++#define STV090x_P1_DFLSTR1 STV090x_Px_DFLSTRy(1, 1) ++#define STV090x_P2_DFLSTR0 STV090x_Px_DFLSTRy(2, 0) ++#define STV090x_P2_DFLSTR1 STV090x_Px_DFLSTRy(2, 1) ++#define STV090x_OFFST_Px_DFL_CURRENT_FIELD 0 ++#define STV090x_WIDTH_Px_DFL_CURRENT_FIELD 8 ++ ++#define STV090x_Px_SYNCSTR(__x) (0xf566 - (__x - 1) * 0x200) ++#define STV090x_P1_SYNCSTR STV090x_Px_SYNCSTR(1) ++#define STV090x_P2_SYNCSTR STV090x_Px_SYNCSTR(2) ++#define STV090x_OFFST_Px_SYNC_CURRENT_FIELD 0 ++#define STV090x_WIDTH_Px_SYNC_CURRENT_FIELD 8 ++ ++#define STV090x_Px_SYNCDSTRy(__x, __y) (0xf568 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_SYNCDSTR0 STV090x_Px_SYNCDSTRy(1, 0) ++#define STV090x_P1_SYNCDSTR1 STV090x_Px_SYNCDSTRy(1, 1) ++#define STV090x_P2_SYNCDSTR0 STV090x_Px_SYNCDSTRy(2, 0) ++#define STV090x_P2_SYNCDSTR1 STV090x_Px_SYNCDSTRy(2, 1) ++#define STV090x_OFFST_Px_SYNCD_CURRENT_FIELD 0 ++#define STV090x_WIDTH_Px_SYNCD_CURRENT_FIELD 8 ++ ++#define STV090x_Px_PDELSTATUS1(__x) (0xf569 - (__x - 1) * 0x200) ++#define STV090x_P1_PDELSTATUS1 STV090x_Px_PDELSTATUS1(1) ++#define STV090x_P2_PDELSTATUS1 STV090x_Px_PDELSTATUS1(2) ++#define STV090x_OFFST_Px_PKTDELIN_LOCK_FIELD 1 ++#define STV090x_WIDTH_Px_PKTDELIN_LOCK_FIELD 1 ++#define STV090x_OFFST_Px_FIRST_LOCK_FIELD 0 ++#define STV090x_WIDTH_Px_FIRST_LOCK_FIELD 1 ++ ++#define STV090x_Px_PDELSTATUS2(__x) (0xf56a - (__x - 1) * 0x200) ++#define STV090x_P1_PDELSTATUS2 STV090x_Px_PDELSTATUS2(1) ++#define STV090x_P2_PDELSTATUS2 STV090x_Px_PDELSTATUS2(2) ++#define STV090x_OFFST_Px_FRAME_MODCOD_FIELD 2 ++#define STV090x_WIDTH_Px_FRAME_MODCOD_FIELD 5 ++#define STV090x_OFFST_Px_FRAME_TYPE_FIELD 0 ++#define STV090x_WIDTH_Px_FRAME_TYPE_FIELD 2 ++ ++#define STV090x_Px_BBFCRCKO1(__x) (0xf56b - (__x - 1) * 0x200) ++#define STV090x_P1_BBFCRCKO1 STV090x_Px_BBFCRCKO1(1) ++#define STV090x_P2_BBFCRCKO1 STV090x_Px_BBFCRCKO1(2) ++#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 ++#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 ++ ++#define STV090x_Px_BBFCRCKO0(__x) (0xf56c - (__x - 1) * 0x200) ++#define STV090x_P1_BBFCRCKO0 STV090x_Px_BBFCRCKO0(1) ++#define STV090x_P2_BBFCRCKO0 STV090x_Px_BBFCRCKO0(2) ++#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 ++#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 ++ ++#define STV090x_Px_UPCRCKO1(__x) (0xf56d - (__x - 1) * 0x200) ++#define STV090x_P1_UPCRCKO1 STV090x_Px_UPCRCKO1(1) ++#define STV090x_P2_UPCRCKO1 STV090x_Px_UPCRCKO1(2) ++#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 ++#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 ++ ++#define STV090x_Px_UPCRCKO0(__x) (0xf56e - (__x - 1) * 0x200) ++#define STV090x_P1_UPCRCKO0 STV090x_Px_UPCRCKO0(1) ++#define STV090x_P2_UPCRCKO0 STV090x_Px_UPCRCKO0(2) ++#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 ++#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 ++ ++#define STV090x_NBITER_NFx(__x) (0xFA03 + (__x - 4) * 0x1) ++#define STV090x_NBITER_NF4 STV090x_NBITER_NFx(4) ++#define STV090x_NBITER_NF5 STV090x_NBITER_NFx(5) ++#define STV090x_NBITER_NF6 STV090x_NBITER_NFx(6) ++#define STV090x_NBITER_NF7 STV090x_NBITER_NFx(7) ++#define STV090x_NBITER_NF8 STV090x_NBITER_NFx(8) ++#define STV090x_NBITER_NF9 STV090x_NBITER_NFx(9) ++#define STV090x_NBITER_NF10 STV090x_NBITER_NFx(10) ++#define STV090x_NBITER_NF11 STV090x_NBITER_NFx(11) ++#define STV090x_NBITER_NF12 STV090x_NBITER_NFx(12) ++#define STV090x_NBITER_NF13 STV090x_NBITER_NFx(13) ++#define STV090x_NBITER_NF14 STV090x_NBITER_NFx(14) ++#define STV090x_NBITER_NF15 STV090x_NBITER_NFx(15) ++#define STV090x_NBITER_NF16 STV090x_NBITER_NFx(16) ++#define STV090x_NBITER_NF17 STV090x_NBITER_NFx(17) ++ ++#define STV090x_NBITERNOERR 0xFA3F ++#define STV090x_OFFST_NBITER_STOP_CRIT_FIELD 0 ++#define STV090x_WIDTH_NBITER_STOP_CRIT_FIELD 4 ++ ++#define STV090x_GAINLLR_NFx(__x) (0xFA43 + (__x - 4) * 0x1) ++#define STV090x_GAINLLR_NF4 STV090x_GAINLLR_NFx(4) ++#define STV090x_OFFST_GAINLLR_NF_QP_1_2_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_1_2_FIELD 7 ++ ++#define STV090x_GAINLLR_NF5 STV090x_GAINLLR_NFx(5) ++#define STV090x_OFFST_GAINLLR_NF_QP_3_5_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_3_5_FIELD 7 ++ ++#define STV090x_GAINLLR_NF6 STV090x_GAINLLR_NFx(6) ++#define STV090x_OFFST_GAINLLR_NF_QP_2_3_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_2_3_FIELD 7 ++ ++#define STV090x_GAINLLR_NF7 STV090x_GAINLLR_NFx(7) ++#define STV090x_OFFST_GAINLLR_NF_QP_3_4_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_3_4_FIELD 7 ++ ++#define STV090x_GAINLLR_NF8 STV090x_GAINLLR_NFx(8) ++#define STV090x_OFFST_GAINLLR_NF_QP_4_5_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_4_5_FIELD 7 ++ ++#define STV090x_GAINLLR_NF9 STV090x_GAINLLR_NFx(9) ++#define STV090x_OFFST_GAINLLR_NF_QP_5_6_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_5_6_FIELD 7 ++ ++#define STV090x_GAINLLR_NF10 STV090x_GAINLLR_NFx(10) ++#define STV090x_OFFST_GAINLLR_NF_QP_8_9_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_8_9_FIELD 7 ++ ++#define STV090x_GAINLLR_NF11 STV090x_GAINLLR_NFx(11) ++#define STV090x_OFFST_GAINLLR_NF_QP_9_10_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_QP_9_10_FIELD 7 ++ ++#define STV090x_GAINLLR_NF12 STV090x_GAINLLR_NFx(12) ++#define STV090x_OFFST_GAINLLR_NF_8P_3_5_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_3_5_FIELD 7 ++ ++#define STV090x_GAINLLR_NF13 STV090x_GAINLLR_NFx(13) ++#define STV090x_OFFST_GAINLLR_NF_8P_2_3_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_2_3_FIELD 7 ++ ++#define STV090x_GAINLLR_NF14 STV090x_GAINLLR_NFx(14) ++#define STV090x_OFFST_GAINLLR_NF_8P_3_4_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_3_4_FIELD 7 ++ ++#define STV090x_GAINLLR_NF15 STV090x_GAINLLR_NFx(15) ++#define STV090x_OFFST_GAINLLR_NF_8P_5_6_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_5_6_FIELD 7 ++ ++#define STV090x_GAINLLR_NF16 STV090x_GAINLLR_NFx(16) ++#define STV090x_OFFST_GAINLLR_NF_8P_8_9_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_8_9_FIELD 7 ++ ++#define STV090x_GAINLLR_NF17 STV090x_GAINLLR_NFx(17) ++#define STV090x_OFFST_GAINLLR_NF_8P_9_10_FIELD 0 ++#define STV090x_WIDTH_GAINLLR_NF_8P_9_10_FIELD 7 ++ ++#define STV090x_GENCFG 0xFA86 ++#define STV090x_OFFST_BROADCAST_FIELD 4 ++#define STV090x_WIDTH_BROADCAST_FIELD 1 ++#define STV090x_OFFST_PRIORITY_FIELD 1 ++#define STV090x_WIDTH_PRIORITY_FIELD 1 ++#define STV090x_OFFST_DDEMOD_FIELD 0 ++#define STV090x_WIDTH_DDEMOD_FIELD 1 ++ ++#define STV090x_LDPCERRx(__x) (0xFA97 - (__x * 0x1)) ++#define STV090x_LDPCERR0 STV090x_LDPCERRx(0) ++#define STV090x_LDPCERR1 STV090x_LDPCERRx(1) ++#define STV090x_OFFST_Px_LDPC_ERRORS_COUNTER_FIELD 0 ++#define STV090x_WIDTH_Px_LDPC_ERRORS_COUNTER_FIELD 8 ++ ++#define STV090x_BCHERR 0xFA98 ++#define STV090x_OFFST_Px_ERRORFLAG_FIELD 4 ++#define STV090x_WIDTH_Px_ERRORFLAG_FIELD 1 ++#define STV090x_OFFST_Px_BCH_ERRORS_COUNTER_FIELD 0 ++#define STV090x_WIDTH_Px_BCH_ERRORS_COUNTER_FIELD 4 ++ ++#define STV090x_Px_TSSTATEM(__x) (0xF570 - (__x - 1) * 0x200) ++#define STV090x_P1_TSSTATEM STV090x_Px_TSSTATEM(1) ++#define STV090x_P2_TSSTATEM STV090x_Px_TSSTATEM(2) ++#define STV090x_OFFST_Px_TSDIL_ON_FIELD 7 ++#define STV090x_WIDTH_Px_TSDIL_ON_FIELD 1 ++#define STV090x_OFFST_Px_TSRS_ON_FIELD 5 ++#define STV090x_WIDTH_Px_TSRS_ON_FIELD 1 ++ ++#define STV090x_Px_TSCFGH(__x) (0xF572 - (__x - 1) * 0x200) ++#define STV090x_P1_TSCFGH STV090x_Px_TSCFGH(1) ++#define STV090x_P2_TSCFGH STV090x_Px_TSCFGH(2) ++#define STV090x_OFFST_Px_TSFIFO_DVBCI_FIELD 7 ++#define STV090x_WIDTH_Px_TSFIFO_DVBCI_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_SERIAL_FIELD 6 ++#define STV090x_WIDTH_Px_TSFIFO_SERIAL_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_TEIUPDATE_FIELD 5 ++#define STV090x_WIDTH_Px_TSFIFO_TEIUPDATE_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_DUTY50_FIELD 4 ++#define STV090x_WIDTH_Px_TSFIFO_DUTY50_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_HSGNLOUT_FIELD 3 ++#define STV090x_WIDTH_Px_TSFIFO_HSGNLOUT_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_ERRORMODE_FIELD 1 ++#define STV090x_WIDTH_Px_TSFIFO_ERRORMODE_FIELD 2 ++#define STV090x_OFFST_Px_RST_HWARE_FIELD 0 ++#define STV090x_WIDTH_Px_RST_HWARE_FIELD 1 ++ ++#define STV090x_Px_TSCFGM(__x) (0xF573 - (__x - 1) * 0x200) ++#define STV090x_P1_TSCFGM STV090x_Px_TSCFGM(1) ++#define STV090x_P2_TSCFGM STV090x_Px_TSCFGM(2) ++#define STV090x_OFFST_Px_TSFIFO_MANSPEED_FIELD 6 ++#define STV090x_WIDTH_Px_TSFIFO_MANSPEED_FIELD 2 ++#define STV090x_OFFST_Px_TSFIFO_PERMDATA_FIELD 5 ++#define STV090x_WIDTH_Px_TSFIFO_PERMDATA_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_INVDATA_FIELD 0 ++#define STV090x_WIDTH_Px_TSFIFO_INVDATA_FIELD 1 ++ ++#define STV090x_Px_TSCFGL(__x) (0xF574 - (__x - 1) * 0x200) ++#define STV090x_P1_TSCFGL STV090x_Px_TSCFGL(1) ++#define STV090x_P2_TSCFGL STV090x_Px_TSCFGL(2) ++#define STV090x_OFFST_Px_TSFIFO_BCLKDEL1CK_FIELD 6 ++#define STV090x_WIDTH_Px_TSFIFO_BCLKDEL1CK_FIELD 2 ++#define STV090x_OFFST_Px_BCHERROR_MODE_FIELD 4 ++#define STV090x_WIDTH_Px_BCHERROR_MODE_FIELD 2 ++#define STV090x_OFFST_Px_TSFIFO_NSGNL2DATA_FIELD 3 ++#define STV090x_WIDTH_Px_TSFIFO_NSGNL2DATA_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_EMBINDVB_FIELD 2 ++#define STV090x_WIDTH_Px_TSFIFO_EMBINDVB_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_DPUNACT_FIELD 1 ++#define STV090x_WIDTH_Px_TSFIFO_DPUNACT_FIELD 1 ++ ++#define STV090x_Px_TSINSDELH(__x) (0xF576 - (__x - 1) * 0x200) ++#define STV090x_P1_TSINSDELH STV090x_Px_TSINSDELH(1) ++#define STV090x_P2_TSINSDELH STV090x_Px_TSINSDELH(2) ++#define STV090x_OFFST_Px_TSDEL_SYNCBYTE_FIELD 7 ++#define STV090x_WIDTH_Px_TSDEL_SYNCBYTE_FIELD 1 ++#define STV090x_OFFST_Px_TSDEL_XXHEADER_FIELD 6 ++#define STV090x_WIDTH_Px_TSDEL_XXHEADER_FIELD 1 ++ ++#define STV090x_Px_TSSPEED(__x) (0xF580 - (__x - 1) * 0x200) ++#define STV090x_P1_TSSPEED STV090x_Px_TSSPEED(1) ++#define STV090x_P2_TSSPEED STV090x_Px_TSSPEED(2) ++#define STV090x_OFFST_Px_TSFIFO_OUTSPEED_FIELD 0 ++#define STV090x_WIDTH_Px_TSFIFO_OUTSPEED_FIELD 8 ++ ++#define STV090x_Px_TSSTATUS(__x) (0xF581 - (__x - 1) * 0x200) ++#define STV090x_P1_TSSTATUS STV090x_Px_TSSTATUS(1) ++#define STV090x_P2_TSSTATUS STV090x_Px_TSSTATUS(2) ++#define STV090x_OFFST_Px_TSFIFO_LINEOK_FIELD 7 ++#define STV090x_WIDTH_Px_TSFIFO_LINEOK_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_ERROR_FIELD 6 ++#define STV090x_WIDTH_Px_TSFIFO_ERROR_FIELD 1 ++ ++#define STV090x_Px_TSSTATUS2(__x) (0xF582 - (__x - 1) * 0x200) ++#define STV090x_P1_TSSTATUS2 STV090x_Px_TSSTATUS2(1) ++#define STV090x_P2_TSSTATUS2 STV090x_Px_TSSTATUS2(2) ++#define STV090x_OFFST_Px_TSFIFO_DEMODSEL_FIELD 7 ++#define STV090x_WIDTH_Px_TSFIFO_DEMODSEL_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFOSPEED_STORE_FIELD 6 ++#define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1 ++#define STV090x_OFFST_Px_DILXX_RESET_FIELD 5 ++#define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1 ++#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4 ++#define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1 ++#define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1 ++#define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1 ++ ++#define STV090x_Px_TSBITRATEy(__x, __y) (0xF584 - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_TSBITRATE0 STV090x_Px_TSBITRATEy(1, 0) ++#define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1) ++#define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0) ++#define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1) ++#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0 ++#define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8 ++ ++#define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCTRL1 STV090x_Px_ERRCTRL1(1) ++#define STV090x_P2_ERRCTRL1 STV090x_Px_ERRCTRL1(2) ++#define STV090x_OFFST_Px_ERR_SOURCE_FIELD 4 ++#define STV090x_WIDTH_Px_ERR_SOURCE_FIELD 4 ++#define STV090x_OFFST_Px_NUM_EVENT_FIELD 0 ++#define STV090x_WIDTH_Px_NUM_EVENT_FIELD 3 ++ ++#define STV090x_Px_ERRCNT12(__x) (0xF599 - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT12 STV090x_Px_ERRCNT12(1) ++#define STV090x_P2_ERRCNT12 STV090x_Px_ERRCNT12(2) ++#define STV090x_OFFST_Px_ERRCNT1_OLDVALUE_FIELD 7 ++#define STV090x_WIDTH_Px_ERRCNT1_OLDVALUE_FIELD 1 ++#define STV090x_OFFST_Px_ERR_CNT12_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT12_FIELD 7 ++ ++#define STV090x_Px_ERRCNT11(__x) (0xF59A - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT11 STV090x_Px_ERRCNT11(1) ++#define STV090x_P2_ERRCNT11 STV090x_Px_ERRCNT11(2) ++#define STV090x_OFFST_Px_ERR_CNT11_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT11_FIELD 8 ++ ++#define STV090x_Px_ERRCNT10(__x) (0xF59B - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT10 STV090x_Px_ERRCNT10(1) ++#define STV090x_P2_ERRCNT10 STV090x_Px_ERRCNT10(2) ++#define STV090x_OFFST_Px_ERR_CNT10_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT10_FIELD 8 ++ ++#define STV090x_Px_ERRCTRL2(__x) (0xF59C - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCTRL2 STV090x_Px_ERRCTRL2(1) ++#define STV090x_P2_ERRCTRL2 STV090x_Px_ERRCTRL2(2) ++#define STV090x_OFFST_Px_ERR_SOURCE2_FIELD 4 ++#define STV090x_WIDTH_Px_ERR_SOURCE2_FIELD 4 ++#define STV090x_OFFST_Px_NUM_EVENT2_FIELD 0 ++#define STV090x_WIDTH_Px_NUM_EVENT2_FIELD 3 ++ ++#define STV090x_Px_ERRCNT22(__x) (0xF59D - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT22 STV090x_Px_ERRCNT22(1) ++#define STV090x_P2_ERRCNT22 STV090x_Px_ERRCNT22(2) ++#define STV090x_OFFST_Px_ERRCNT2_OLDVALUE_FIELD 7 ++#define STV090x_WIDTH_Px_ERRCNT2_OLDVALUE_FIELD 1 ++#define STV090x_OFFST_Px_ERR_CNT2_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT2_FIELD 7 ++ ++#define STV090x_Px_ERRCNT21(__x) (0xF59E - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT21 STV090x_Px_ERRCNT21(1) ++#define STV090x_P2_ERRCNT21 STV090x_Px_ERRCNT21(2) ++#define STV090x_OFFST_Px_ERR_CNT21_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT21_FIELD 8 ++ ++#define STV090x_Px_ERRCNT20(__x) (0xF59F - (__x - 1) * 0x200) ++#define STV090x_P1_ERRCNT20 STV090x_Px_ERRCNT20(1) ++#define STV090x_P2_ERRCNT20 STV090x_Px_ERRCNT20(2) ++#define STV090x_OFFST_Px_ERR_CNT20_FIELD 0 ++#define STV090x_WIDTH_Px_ERR_CNT20_FIELD 8 ++ ++#define STV090x_Px_FECSPY(__x) (0xF5A0 - (__x - 1) * 0x200) ++#define STV090x_P1_FECSPY STV090x_Px_FECSPY(1) ++#define STV090x_P2_FECSPY STV090x_Px_FECSPY(2) ++#define STV090x_OFFST_Px_SPY_ENABLE_FIELD 7 ++#define STV090x_WIDTH_Px_SPY_ENABLE_FIELD 1 ++#define STV090x_OFFST_Px_BERMETER_DATAMAODE_FIELD 2 ++#define STV090x_WIDTH_Px_BERMETER_DATAMAODE_FIELD 2 ++ ++#define STV090x_Px_FSPYCFG(__x) (0xF5A1 - (__x - 1) * 0x200) ++#define STV090x_P1_FSPYCFG STV090x_Px_FSPYCFG(1) ++#define STV090x_P2_FSPYCFG STV090x_Px_FSPYCFG(2) ++#define STV090x_OFFST_Px_RST_ON_ERROR_FIELD 5 ++#define STV090x_WIDTH_Px_RST_ON_ERROR_FIELD 1 ++#define STV090x_OFFST_Px_ONE_SHOT_FIELD 4 ++#define STV090x_WIDTH_Px_ONE_SHOT_FIELD 1 ++#define STV090x_OFFST_Px_I2C_MODE_FIELD 2 ++#define STV090x_WIDTH_Px_I2C_MODE_FIELD 2 ++ ++#define STV090x_Px_FSPYDATA(__x) (0xF5A2 - (__x - 1) * 0x200) ++#define STV090x_P1_FSPYDATA STV090x_Px_FSPYDATA(1) ++#define STV090x_P2_FSPYDATA STV090x_Px_FSPYDATA(2) ++#define STV090x_OFFST_Px_SPY_STUFFING_FIELD 7 ++#define STV090x_WIDTH_Px_SPY_STUFFING_FIELD 1 ++#define STV090x_OFFST_Px_SPY_CNULLPKT_FIELD 5 ++#define STV090x_WIDTH_Px_SPY_CNULLPKT_FIELD 1 ++#define STV090x_OFFST_Px_SPY_OUTDATA_MODE_FIELD 0 ++#define STV090x_WIDTH_Px_SPY_OUTDATA_MODE_FIELD 5 ++ ++#define STV090x_Px_FSPYOUT(__x) (0xF5A3 - (__x - 1) * 0x200) ++#define STV090x_P1_FSPYOUT STV090x_Px_FSPYOUT(1) ++#define STV090x_P2_FSPYOUT STV090x_Px_FSPYOUT(2) ++#define STV090x_OFFST_Px_FSPY_DIRECT_FIELD 7 ++#define STV090x_WIDTH_Px_FSPY_DIRECT_FIELD 1 ++#define STV090x_OFFST_Px_STUFF_MODE_FIELD 0 ++#define STV090x_WIDTH_Px_STUFF_MODE_FIELD 3 ++ ++#define STV090x_Px_FSTATUS(__x) (0xF5A4 - (__x - 1) * 0x200) ++#define STV090x_P1_FSTATUS STV090x_Px_FSTATUS(1) ++#define STV090x_P2_FSTATUS STV090x_Px_FSTATUS(2) ++#define STV090x_OFFST_Px_SPY_ENDSIM_FIELD 7 ++#define STV090x_WIDTH_Px_SPY_ENDSIM_FIELD 1 ++#define STV090x_OFFST_Px_VALID_SIM_FIELD 6 ++#define STV090x_WIDTH_Px_VALID_SIM_FIELD 1 ++#define STV090x_OFFST_Px_FOUND_SIGNAL_FIELD 5 ++#define STV090x_WIDTH_Px_FOUND_SIGNAL_FIELD 1 ++#define STV090x_OFFST_Px_DSS_SYNCBYTE_FIELD 4 ++#define STV090x_WIDTH_Px_DSS_SYNCBYTE_FIELD 1 ++#define STV090x_OFFST_Px_RESULT_STATE_FIELD 0 ++#define STV090x_WIDTH_Px_RESULT_STATE_FIELD 4 ++ ++#define STV090x_Px_FBERCPT4(__x) (0xF5A8 - (__x - 1) * 0x200) ++#define STV090x_P1_FBERCPT4 STV090x_Px_FBERCPT4(1) ++#define STV090x_P2_FBERCPT4 STV090x_Px_FBERCPT4(2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 ++ ++#define STV090x_Px_FBERCPT3(__x) (0xF5A9 - (__x - 1) * 0x200) ++#define STV090x_P1_FBERCPT3 STV090x_Px_FBERCPT3(1) ++#define STV090x_P2_FBERCPT3 STV090x_Px_FBERCPT3(2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 ++ ++#define STV090x_Px_FBERCPT2(__x) (0xF5AA - (__x - 1) * 0x200) ++#define STV090x_P1_FBERCPT2 STV090x_Px_FBERCPT2(1) ++#define STV090x_P2_FBERCPT2 STV090x_Px_FBERCPT2(2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 ++ ++#define STV090x_Px_FBERCPT1(__x) (0xF5AB - (__x - 1) * 0x200) ++#define STV090x_P1_FBERCPT1 STV090x_Px_FBERCPT1(1) ++#define STV090x_P2_FBERCPT1 STV090x_Px_FBERCPT1(2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 ++ ++#define STV090x_Px_FBERCPT0(__x) (0xF5AC - (__x - 1) * 0x200) ++#define STV090x_P1_FBERCPT0 STV090x_Px_FBERCPT0(1) ++#define STV090x_P2_FBERCPT0 STV090x_Px_FBERCPT0(2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 ++ ++#define STV090x_Px_FBERERRy(__x, __y) (0xF5AF - (__x - 1) * 0x200 - __y * 0x1) ++#define STV090x_P1_FBERERR0 STV090x_Px_FBERERRy(1, 0) ++#define STV090x_P1_FBERERR1 STV090x_Px_FBERERRy(1, 1) ++#define STV090x_P1_FBERERR2 STV090x_Px_FBERERRy(1, 2) ++#define STV090x_P2_FBERERR0 STV090x_Px_FBERERRy(2, 0) ++#define STV090x_P2_FBERERR1 STV090x_Px_FBERERRy(2, 1) ++#define STV090x_P2_FBERERR2 STV090x_Px_FBERERRy(2, 2) ++#define STV090x_OFFST_Px_FBERMETER_CPT_ERR_FIELD 0 ++#define STV090x_WIDTH_Px_FBERMETER_CPT_ERR_FIELD 8 ++ ++#define STV090x_Px_FSPYBER(__x) (0xF5B2 - (__x - 1) * 0x200) ++#define STV090x_P1_FSPYBER STV090x_Px_FSPYBER(1) ++#define STV090x_P2_FSPYBER STV090x_Px_FSPYBER(2) ++#define STV090x_OFFST_Px_FSPYBER_SYNCBYTE_FIELD 4 ++#define STV090x_WIDTH_Px_FSPYBER_SYNCBYTE_FIELD 1 ++#define STV090x_OFFST_Px_FSPYBER_UNSYNC_FIELD 3 ++#define STV090x_WIDTH_Px_FSPYBER_UNSYNC_FIELD 1 ++#define STV090x_OFFST_Px_FSPYBER_CTIME_FIELD 0 ++#define STV090x_WIDTH_Px_FSPYBER_CTIME_FIELD 3 ++ ++#define STV090x_RCCFGH 0xf600 ++ ++#define STV090x_TSGENERAL 0xF630 ++#define STV090x_OFFST_Px_MUXSTREAM_OUT_FIELD 3 ++#define STV090x_WIDTH_Px_MUXSTREAM_OUT_FIELD 1 ++#define STV090x_OFFST_Px_TSFIFO_PERMPARAL_FIELD 1 ++#define STV090x_WIDTH_Px_TSFIFO_PERMPARAL_FIELD 2 ++ ++#define STV090x_TSGENERAL1X 0xf670 ++#define STV090x_CFGEXT 0xfa80 ++ ++#define STV090x_TSTRES0 0xFF11 ++#define STV090x_OFFST_FRESFEC_FIELD 7 ++#define STV090x_WIDTH_FRESFEC_FIELD 1 ++ ++#define STV090x_Px_TSTDISRX(__x) (0xFF67 - (__x - 1) * 0x2) ++#define STV090x_P1_TSTDISRX STV090x_Px_TSTDISRX(1) ++#define STV090x_P2_TSTDISRX STV090x_Px_TSTDISRX(2) ++#define STV090x_OFFST_Px_TSTDISRX_SELECT_FIELD 3 ++#define STV090x_WIDTH_Px_TSTDISRX_SELECT_FIELD 1 ++ ++#endif /* __STV090x_REG_H */ +diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c +new file mode 100644 +index 0000000..20b5fa9 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110.c +@@ -0,0 +1,451 @@ ++/* ++ * stv6110.c ++ * ++ * Driver for ST STV6110 satellite tuner IC. ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "stv6110.h" ++ ++static int debug; ++ ++struct stv6110_priv { ++ int i2c_address; ++ struct i2c_adapter *i2c; ++ ++ u32 mclk; ++ u8 clk_div; ++ u8 gain; ++ u8 regs[8]; ++}; ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++static s32 abssub(s32 a, s32 b) ++{ ++ if (a > b) ++ return a - b; ++ else ++ return b - a; ++}; ++ ++static int stv6110_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[], ++ int start, int len) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ int rc; ++ u8 cmdbuf[len + 1]; ++ struct i2c_msg msg = { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = cmdbuf, ++ .len = len + 1 ++ }; ++ ++ dprintk("%s\n", __func__); ++ ++ if (start + len > 8) ++ return -EINVAL; ++ ++ memcpy(&cmdbuf[1], buf, len); ++ cmdbuf[0] = start; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ rc = i2c_transfer(priv->i2c, &msg, 1); ++ if (rc != 1) ++ dprintk("%s: i2c error\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], ++ int start, int len) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ int rc; ++ u8 reg[] = { start }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = reg, ++ .len = 1, ++ }, { ++ .addr = priv->i2c_address, ++ .flags = I2C_M_RD, ++ .buf = regs, ++ .len = len, ++ }, ++ }; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ rc = i2c_transfer(priv->i2c, msg, 2); ++ if (rc != 2) ++ dprintk("%s: i2c error\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ memcpy(&priv->regs[start], regs, len); ++ ++ return 0; ++} ++ ++static int stv6110_read_reg(struct dvb_frontend *fe, int start) ++{ ++ u8 buf[] = { 0 }; ++ stv6110_read_regs(fe, buf, start, 1); ++ ++ return buf[0]; ++} ++ ++static int stv6110_sleep(struct dvb_frontend *fe) ++{ ++ u8 reg[] = { 0 }; ++ stv6110_write_regs(fe, reg, 0, 1); ++ ++ return 0; ++} ++ ++static u32 carrier_width(u32 symbol_rate, fe_rolloff_t rolloff) ++{ ++ u32 rlf; ++ ++ switch (rolloff) { ++ case ROLLOFF_20: ++ rlf = 20; ++ break; ++ case ROLLOFF_25: ++ rlf = 25; ++ break; ++ default: ++ rlf = 35; ++ break; ++ } ++ ++ return symbol_rate + ((symbol_rate * rlf) / 100); ++} ++ ++static int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ u8 r8, ret = 0x04; ++ int i; ++ ++ if ((bandwidth / 2) > 36000000) /*BW/2 max=31+5=36 mhz for r8=31*/ ++ r8 = 31; ++ else if ((bandwidth / 2) < 5000000) /* BW/2 min=5Mhz for F=0 */ ++ r8 = 0; ++ else /*if 5 < BW/2 < 36*/ ++ r8 = (bandwidth / 2) / 1000000 - 5; ++ ++ /* ctrl3, RCCLKOFF = 0 Activate the calibration Clock */ ++ /* ctrl3, CF = r8 Set the LPF value */ ++ priv->regs[RSTV6110_CTRL3] &= ~((1 << 6) | 0x1f); ++ priv->regs[RSTV6110_CTRL3] |= (r8 & 0x1f); ++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); ++ /* stat1, CALRCSTRT = 1 Start LPF auto calibration*/ ++ priv->regs[RSTV6110_STAT1] |= 0x02; ++ stv6110_write_regs(fe, &priv->regs[RSTV6110_STAT1], RSTV6110_STAT1, 1); ++ ++ i = 0; ++ /* Wait for CALRCSTRT == 0 */ ++ while ((i < 10) && (ret != 0)) { ++ ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x02); ++ mdelay(1); /* wait for LPF auto calibration */ ++ i++; ++ } ++ ++ /* RCCLKOFF = 1 calibration done, desactivate the calibration Clock */ ++ priv->regs[RSTV6110_CTRL3] |= (1 << 6); ++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); ++ return 0; ++} ++ ++static int stv6110_init(struct dvb_frontend *fe) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ u8 buf0[] = { 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; ++ ++ memcpy(priv->regs, buf0, 8); ++ /* K = (Reference / 1000000) - 16 */ ++ priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); ++ priv->regs[RSTV6110_CTRL1] |= ++ ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); ++ ++ /* divisor value for the output clock */ ++ priv->regs[RSTV6110_CTRL2] &= ~0xc0; ++ priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); ++ ++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); ++ msleep(1); ++ stv6110_set_bandwidth(fe, 72000000); ++ ++ return 0; ++} ++ ++static int stv6110_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ u32 nbsteps, divider, psd2, freq; ++ u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; ++ ++ stv6110_read_regs(fe, regs, 0, 8); ++ /*N*/ ++ divider = (priv->regs[RSTV6110_TUNING2] & 0x0f) << 8; ++ divider += priv->regs[RSTV6110_TUNING1]; ++ ++ /*R*/ ++ nbsteps = (priv->regs[RSTV6110_TUNING2] >> 6) & 3; ++ /*p*/ ++ psd2 = (priv->regs[RSTV6110_TUNING2] >> 4) & 1; ++ ++ freq = divider * (priv->mclk / 1000); ++ freq /= (1 << (nbsteps + psd2)); ++ freq /= 4; ++ ++ *frequency = freq; ++ ++ return 0; ++} ++ ++static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 ret = 0x04; ++ u32 divider, ref, p, presc, i, result_freq, vco_freq; ++ s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; ++ s32 srate; ++ ++ dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, ++ frequency, priv->mclk); ++ ++ /* K = (Reference / 1000000) - 16 */ ++ priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); ++ priv->regs[RSTV6110_CTRL1] |= ++ ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); ++ ++ /* BB_GAIN = db/2 */ ++ if (fe->ops.set_property && fe->ops.get_property) { ++ srate = c->symbol_rate; ++ dprintk("%s: Get Frontend parameters: srate=%d\n", ++ __func__, srate); ++ } else ++ srate = 15000000; ++ ++ priv->regs[RSTV6110_CTRL2] &= ~0x0f; ++ priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); ++ ++ if (frequency <= 1023000) { ++ p = 1; ++ presc = 0; ++ } else if (frequency <= 1300000) { ++ p = 1; ++ presc = 1; ++ } else if (frequency <= 2046000) { ++ p = 0; ++ presc = 0; ++ } else { ++ p = 0; ++ presc = 1; ++ } ++ /* DIV4SEL = p*/ ++ priv->regs[RSTV6110_TUNING2] &= ~(1 << 4); ++ priv->regs[RSTV6110_TUNING2] |= (p << 4); ++ ++ /* PRESC32ON = presc */ ++ priv->regs[RSTV6110_TUNING2] &= ~(1 << 5); ++ priv->regs[RSTV6110_TUNING2] |= (presc << 5); ++ ++ p_val = (int)(1 << (p + 1)) * 10;/* P = 2 or P = 4 */ ++ for (r_div = 0; r_div <= 3; r_div++) { ++ p_calc = (priv->mclk / 100000); ++ p_calc /= (1 << (r_div + 1)); ++ if ((abssub(p_calc, p_val)) < (abssub(p_calc_opt, p_val))) ++ r_div_opt = r_div; ++ ++ p_calc_opt = (priv->mclk / 100000); ++ p_calc_opt /= (1 << (r_div_opt + 1)); ++ } ++ ++ ref = priv->mclk / ((1 << (r_div_opt + 1)) * (1 << (p + 1))); ++ divider = (((frequency * 1000) + (ref >> 1)) / ref); ++ ++ /* RDIV = r_div_opt */ ++ priv->regs[RSTV6110_TUNING2] &= ~(3 << 6); ++ priv->regs[RSTV6110_TUNING2] |= (((r_div_opt) & 3) << 6); ++ ++ /* NDIV_MSB = MSB(divider) */ ++ priv->regs[RSTV6110_TUNING2] &= ~0x0f; ++ priv->regs[RSTV6110_TUNING2] |= (((divider) >> 8) & 0x0f); ++ ++ /* NDIV_LSB, LSB(divider) */ ++ priv->regs[RSTV6110_TUNING1] = (divider & 0xff); ++ ++ /* CALVCOSTRT = 1 VCO Auto Calibration */ ++ priv->regs[RSTV6110_STAT1] |= 0x04; ++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], ++ RSTV6110_CTRL1, 8); ++ ++ i = 0; ++ /* Wait for CALVCOSTRT == 0 */ ++ while ((i < 10) && (ret != 0)) { ++ ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x04); ++ msleep(1); /* wait for VCO auto calibration */ ++ i++; ++ } ++ ++ ret = stv6110_read_reg(fe, RSTV6110_STAT1); ++ stv6110_get_frequency(fe, &result_freq); ++ ++ vco_freq = divider * ((priv->mclk / 1000) / ((1 << (r_div_opt + 1)))); ++ dprintk("%s, stat1=%x, lo_freq=%d kHz, vco_frec=%d kHz\n", __func__, ++ ret, result_freq, vco_freq); ++ ++ return 0; ++} ++ ++static int stv6110_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 bandwidth = carrier_width(c->symbol_rate, c->rolloff); ++ ++ stv6110_set_frequency(fe, c->frequency); ++ stv6110_set_bandwidth(fe, bandwidth); ++ ++ return 0; ++} ++ ++static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct stv6110_priv *priv = fe->tuner_priv; ++ u8 r8 = 0; ++ u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; ++ stv6110_read_regs(fe, regs, 0, 8); ++ ++ /* CF */ ++ r8 = priv->regs[RSTV6110_CTRL3] & 0x1f; ++ *bandwidth = (r8 + 5) * 2000000;/* x2 for ZIF tuner BW/2 = F+5 Mhz */ ++ ++ return 0; ++} ++ ++static struct dvb_tuner_ops stv6110_tuner_ops = { ++ .info = { ++ .name = "ST STV6110", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 1000, ++ }, ++ .init = stv6110_init, ++ .release = stv6110_release, ++ .sleep = stv6110_sleep, ++ .set_params = stv6110_set_params, ++ .get_frequency = stv6110_get_frequency, ++ .set_frequency = stv6110_set_frequency, ++ .get_bandwidth = stv6110_get_bandwidth, ++ .set_bandwidth = stv6110_set_bandwidth, ++ ++}; ++ ++struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, ++ const struct stv6110_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv6110_priv *priv = NULL; ++ u8 reg0[] = { 0x00, 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = config->i2c_address, ++ .flags = 0, ++ .buf = reg0, ++ .len = 9 ++ } ++ }; ++ int ret; ++ ++ /* divisor value for the output clock */ ++ reg0[2] &= ~0xc0; ++ reg0[2] |= (config->clk_div << 6); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(i2c, msg, 1); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ if (ret != 1) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct stv6110_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->i2c_address = config->i2c_address; ++ priv->i2c = i2c; ++ priv->mclk = config->mclk; ++ priv->clk_div = config->clk_div; ++ priv->gain = config->gain; ++ ++ memcpy(&priv->regs, ®0[1], 8); ++ ++ memcpy(&fe->ops.tuner_ops, &stv6110_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = priv; ++ printk(KERN_INFO "STV6110 attached on addr=%x!\n", priv->i2c_address); ++ ++ return fe; ++} ++EXPORT_SYMBOL(stv6110_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("ST STV6110 driver"); ++MODULE_AUTHOR("Igor M. Liplianin"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stv6110.h b/drivers/media/dvb-frontends/stv6110.h +new file mode 100644 +index 0000000..fe71bba +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110.h +@@ -0,0 +1,63 @@ ++/* ++ * stv6110.h ++ * ++ * Driver for ST STV6110 satellite tuner IC. ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __DVB_STV6110_H__ ++#define __DVB_STV6110_H__ ++ ++#include ++#include "dvb_frontend.h" ++ ++/* registers */ ++#define RSTV6110_CTRL1 0 ++#define RSTV6110_CTRL2 1 ++#define RSTV6110_TUNING1 2 ++#define RSTV6110_TUNING2 3 ++#define RSTV6110_CTRL3 4 ++#define RSTV6110_STAT1 5 ++#define RSTV6110_STAT2 6 ++#define RSTV6110_STAT3 7 ++ ++struct stv6110_config { ++ u8 i2c_address; ++ u32 mclk; ++ u8 gain; ++ u8 clk_div; /* divisor value for the output clock */ ++}; ++ ++#if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \ ++ && defined(MODULE)) ++extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, ++ const struct stv6110_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, ++ const struct stv6110_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c +new file mode 100644 +index 0000000..f36cab1 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110x.c +@@ -0,0 +1,405 @@ ++/* ++ STV6110(A) Silicon tuner driver ++ ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "stv6110x_reg.h" ++#include "stv6110x.h" ++#include "stv6110x_priv.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "Set Verbosity level"); ++ ++static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) ++{ ++ int ret; ++ const struct stv6110x_config *config = stv6110x->config; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .addr = config->addr, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = config->addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(stv6110x->i2c, msg, 2); ++ if (ret != 2) { ++ dprintk(FE_ERROR, 1, "I/O Error"); ++ return -EREMOTEIO; ++ } ++ *data = b1[0]; ++ ++ return 0; ++} ++ ++static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len) ++{ ++ int ret; ++ const struct stv6110x_config *config = stv6110x->config; ++ u8 buf[len + 1]; ++ struct i2c_msg msg = { ++ .addr = config->addr, ++ .flags = 0, ++ .buf = buf, ++ .len = len + 1 ++ }; ++ ++ if (start + len > 8) ++ return -EINVAL; ++ ++ buf[0] = start; ++ memcpy(&buf[1], data, len); ++ ++ ret = i2c_transfer(stv6110x->i2c, &msg, 1); ++ if (ret != 1) { ++ dprintk(FE_ERROR, 1, "I/O Error"); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) ++{ ++ return stv6110x_write_regs(stv6110x, reg, &data, 1); ++} ++ ++static int stv6110x_init(struct dvb_frontend *fe) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ int ret; ++ ++ ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, ++ ARRAY_SIZE(stv6110x->regs)); ++ if (ret < 0) { ++ dprintk(FE_ERROR, 1, "Initialization failed"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ u32 rDiv, divider; ++ s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; ++ u8 i; ++ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); ++ ++ if (frequency <= 1023000) { ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); ++ pVal = 40; ++ } else if (frequency <= 1300000) { ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); ++ pVal = 40; ++ } else if (frequency <= 2046000) { ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); ++ pVal = 20; ++ } else { ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); ++ pVal = 20; ++ } ++ ++ for (rDiv = 0; rDiv <= 3; rDiv++) { ++ pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv); ++ ++ if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal)))) ++ rDivOpt = rDiv; ++ ++ pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt); ++ } ++ ++ divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; ++ divider = (divider + 5) / 10; ++ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider)); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider)); ++ ++ /* VCO Auto calibration */ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1); ++ ++ stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); ++ stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]); ++ stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]); ++ stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); ++ ++ for (i = 0; i < TRIALS; i++) { ++ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); ++ if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1])) ++ break; ++ msleep(1); ++ } ++ ++ return 0; ++} ++ ++static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]); ++ stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]); ++ ++ *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]), ++ STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz; ++ ++ *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) + ++ STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1]))); ++ ++ *frequency >>= 2; ++ ++ return 0; ++} ++ ++static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ u32 halfbw; ++ u8 i; ++ ++ halfbw = bandwidth >> 1; ++ ++ if (halfbw > 36000000) ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */ ++ else if (halfbw < 5000000) ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */ ++ else ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */ ++ ++ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */ ++ ++ stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); ++ stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); ++ ++ for (i = 0; i < TRIALS; i++) { ++ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); ++ if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1])) ++ break; ++ msleep(1); ++ } ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */ ++ stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); ++ ++ return 0; ++} ++ ++static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]); ++ *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000; ++ ++ return 0; ++} ++ ++static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ /* setup divider */ ++ switch (refclock) { ++ default: ++ case 1: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); ++ break; ++ case 2: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); ++ break; ++ case 4: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); ++ break; ++ case 8: ++ case 0: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); ++ break; ++ } ++ stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); ++ ++ return 0; ++} ++ ++static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]); ++ *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]); ++ ++ return 0; ++} ++ ++static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2); ++ stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); ++ ++ return 0; ++} ++ ++static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ int ret; ++ ++ switch (mode) { ++ case TUNER_SLEEP: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0); ++ break; ++ ++ case TUNER_WAKE: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1); ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1); ++ break; ++ } ++ ++ ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); ++ if (ret < 0) { ++ dprintk(FE_ERROR, 1, "I/O Error"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int stv6110x_sleep(struct dvb_frontend *fe) ++{ ++ if (fe->tuner_priv) ++ return stv6110x_set_mode(fe, TUNER_SLEEP); ++ ++ return 0; ++} ++ ++static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); ++ ++ if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1])) ++ *status = TUNER_PHASELOCKED; ++ else ++ *status = 0; ++ ++ return 0; ++} ++ ++ ++static int stv6110x_release(struct dvb_frontend *fe) ++{ ++ struct stv6110x_state *stv6110x = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(stv6110x); ++ ++ return 0; ++} ++ ++static struct dvb_tuner_ops stv6110x_ops = { ++ .info = { ++ .name = "STV6110(A) Silicon Tuner", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 0, ++ }, ++ .release = stv6110x_release ++}; ++ ++static struct stv6110x_devctl stv6110x_ctl = { ++ .tuner_init = stv6110x_init, ++ .tuner_sleep = stv6110x_sleep, ++ .tuner_set_mode = stv6110x_set_mode, ++ .tuner_set_frequency = stv6110x_set_frequency, ++ .tuner_get_frequency = stv6110x_get_frequency, ++ .tuner_set_bandwidth = stv6110x_set_bandwidth, ++ .tuner_get_bandwidth = stv6110x_get_bandwidth, ++ .tuner_set_bbgain = stv6110x_set_bbgain, ++ .tuner_get_bbgain = stv6110x_get_bbgain, ++ .tuner_set_refclk = stv6110x_set_refclock, ++ .tuner_get_status = stv6110x_get_status, ++}; ++ ++struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, ++ const struct stv6110x_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct stv6110x_state *stv6110x; ++ u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; ++ ++ stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); ++ if (!stv6110x) ++ return NULL; ++ ++ stv6110x->i2c = i2c; ++ stv6110x->config = config; ++ stv6110x->devctl = &stv6110x_ctl; ++ memcpy(stv6110x->regs, default_regs, 8); ++ ++ /* setup divider */ ++ switch (stv6110x->config->clk_div) { ++ default: ++ case 1: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); ++ break; ++ case 2: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); ++ break; ++ case 4: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); ++ break; ++ case 8: ++ case 0: ++ STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); ++ break; ++ } ++ ++ fe->tuner_priv = stv6110x; ++ fe->ops.tuner_ops = stv6110x_ops; ++ ++ printk(KERN_INFO "%s: Attaching STV6110x\n", __func__); ++ return stv6110x->devctl; ++} ++EXPORT_SYMBOL(stv6110x_attach); ++ ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("STV6110x Silicon tuner"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/stv6110x.h b/drivers/media/dvb-frontends/stv6110x.h +new file mode 100644 +index 0000000..4751675 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110x.h +@@ -0,0 +1,73 @@ ++/* ++ STV6110(A) Silicon tuner driver ++ ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV6110x_H ++#define __STV6110x_H ++ ++struct stv6110x_config { ++ u8 addr; ++ u32 refclk; ++ u8 clk_div; /* divisor value for the output clock */ ++}; ++ ++enum tuner_mode { ++ TUNER_SLEEP = 1, ++ TUNER_WAKE, ++}; ++ ++enum tuner_status { ++ TUNER_PHASELOCKED = 1, ++}; ++ ++struct stv6110x_devctl { ++ int (*tuner_init) (struct dvb_frontend *fe); ++ int (*tuner_sleep) (struct dvb_frontend *fe); ++ int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); ++ int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); ++ int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); ++ int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth); ++ int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth); ++ int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain); ++ int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); ++ int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); ++ int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); ++}; ++ ++ ++#if defined(CONFIG_DVB_STV6110x) || (defined(CONFIG_DVB_STV6110x_MODULE) && defined(MODULE)) ++ ++extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, ++ const struct stv6110x_config *config, ++ struct i2c_adapter *i2c); ++ ++#else ++static inline struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, ++ const struct stv6110x_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif /* CONFIG_DVB_STV6110x */ ++ ++#endif /* __STV6110x_H */ +diff --git a/drivers/media/dvb-frontends/stv6110x_priv.h b/drivers/media/dvb-frontends/stv6110x_priv.h +new file mode 100644 +index 0000000..0ec936a +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110x_priv.h +@@ -0,0 +1,76 @@ ++/* ++ STV6110(A) Silicon tuner driver ++ ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV6110x_PRIV_H ++#define __STV6110x_PRIV_H ++ ++#define FE_ERROR 0 ++#define FE_NOTICE 1 ++#define FE_INFO 2 ++#define FE_DEBUG 3 ++#define FE_DEBUGREG 4 ++ ++#define dprintk(__y, __z, format, arg...) do { \ ++ if (__z) { \ ++ if ((verbose > FE_ERROR) && (verbose > __y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_NOTICE) && (verbose > __y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_INFO) && (verbose > __y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((verbose > FE_DEBUG) && (verbose > __y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (verbose > __y) \ ++ printk(format, ##arg); \ ++ } \ ++} while (0) ++ ++ ++#define STV6110x_SETFIELD(mask, bitf, val) \ ++ (mask = (mask & (~(((1 << STV6110x_WIDTH_##bitf) - 1) << \ ++ STV6110x_OFFST_##bitf))) | \ ++ (val << STV6110x_OFFST_##bitf)) ++ ++#define STV6110x_GETFIELD(bitf, val) \ ++ ((val >> STV6110x_OFFST_##bitf) & \ ++ ((1 << STV6110x_WIDTH_##bitf) - 1)) ++ ++#define MAKEWORD16(a, b) (((a) << 8) | (b)) ++ ++#define LSB(x) ((x & 0xff)) ++#define MSB(y) ((y >> 8) & 0xff) ++ ++#define TRIALS 10 ++#define R_DIV(__div) (1 << (__div + 1)) ++#define REFCLOCK_kHz (stv6110x->config->refclk / 1000) ++#define REFCLOCK_MHz (stv6110x->config->refclk / 1000000) ++ ++struct stv6110x_state { ++ struct i2c_adapter *i2c; ++ const struct stv6110x_config *config; ++ u8 regs[8]; ++ ++ struct stv6110x_devctl *devctl; ++}; ++ ++#endif /* __STV6110x_PRIV_H */ +diff --git a/drivers/media/dvb-frontends/stv6110x_reg.h b/drivers/media/dvb-frontends/stv6110x_reg.h +new file mode 100644 +index 0000000..93e5c70 +--- /dev/null ++++ b/drivers/media/dvb-frontends/stv6110x_reg.h +@@ -0,0 +1,82 @@ ++/* ++ STV6110(A) Silicon tuner driver ++ ++ Copyright (C) Manu Abraham ++ ++ Copyright (C) ST Microelectronics ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __STV6110x_REG_H ++#define __STV6110x_REG_H ++ ++#define STV6110x_CTRL1 0x00 ++#define STV6110x_OFFST_CTRL1_K 3 ++#define STV6110x_WIDTH_CTRL1_K 5 ++#define STV6110x_OFFST_CTRL1_LPT 2 ++#define STV6110x_WIDTH_CTRL1_LPT 1 ++#define STV6110x_OFFST_CTRL1_RX 1 ++#define STV6110x_WIDTH_CTRL1_RX 1 ++#define STV6110x_OFFST_CTRL1_SYN 0 ++#define STV6110x_WIDTH_CTRL1_SYN 1 ++ ++#define STV6110x_CTRL2 0x01 ++#define STV6110x_OFFST_CTRL2_CO_DIV 6 ++#define STV6110x_WIDTH_CTRL2_CO_DIV 2 ++#define STV6110x_OFFST_CTRL2_RSVD 5 ++#define STV6110x_WIDTH_CTRL2_RSVD 1 ++#define STV6110x_OFFST_CTRL2_REFOUT_SEL 4 ++#define STV6110x_WIDTH_CTRL2_REFOUT_SEL 1 ++#define STV6110x_OFFST_CTRL2_BBGAIN 0 ++#define STV6110x_WIDTH_CTRL2_BBGAIN 4 ++ ++#define STV6110x_TNG0 0x02 ++#define STV6110x_OFFST_TNG0_N_DIV_7_0 0 ++#define STV6110x_WIDTH_TNG0_N_DIV_7_0 8 ++ ++#define STV6110x_TNG1 0x03 ++#define STV6110x_OFFST_TNG1_R_DIV 6 ++#define STV6110x_WIDTH_TNG1_R_DIV 2 ++#define STV6110x_OFFST_TNG1_PRESC32_ON 5 ++#define STV6110x_WIDTH_TNG1_PRESC32_ON 1 ++#define STV6110x_OFFST_TNG1_DIV4SEL 4 ++#define STV6110x_WIDTH_TNG1_DIV4SEL 1 ++#define STV6110x_OFFST_TNG1_N_DIV_11_8 0 ++#define STV6110x_WIDTH_TNG1_N_DIV_11_8 4 ++ ++ ++#define STV6110x_CTRL3 0x04 ++#define STV6110x_OFFST_CTRL3_DCLOOP_OFF 7 ++#define STV6110x_WIDTH_CTRL3_DCLOOP_OFF 1 ++#define STV6110x_OFFST_CTRL3_RCCLK_OFF 6 ++#define STV6110x_WIDTH_CTRL3_RCCLK_OFF 1 ++#define STV6110x_OFFST_CTRL3_ICP 5 ++#define STV6110x_WIDTH_CTRL3_ICP 1 ++#define STV6110x_OFFST_CTRL3_CF 0 ++#define STV6110x_WIDTH_CTRL3_CF 5 ++ ++#define STV6110x_STAT1 0x05 ++#define STV6110x_OFFST_STAT1_CALVCO_STRT 2 ++#define STV6110x_WIDTH_STAT1_CALVCO_STRT 1 ++#define STV6110x_OFFST_STAT1_CALRC_STRT 1 ++#define STV6110x_WIDTH_STAT1_CALRC_STRT 1 ++#define STV6110x_OFFST_STAT1_LOCK 0 ++#define STV6110x_WIDTH_STAT1_LOCK 1 ++ ++#define STV6110x_STAT2 0x06 ++#define STV6110x_STAT3 0x07 ++ ++#endif /* __STV6110x_REG_H */ +diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c +new file mode 100644 +index 0000000..1bff7f4 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10021.c +@@ -0,0 +1,528 @@ ++/* ++ TDA10021 - Single Chip Cable Channel Receiver driver module ++ used on the Siemens DVB-C cards ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ Copyright (C) 2004 Markus Schulz ++ Support for TDA10021 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda1002x.h" ++ ++ ++struct tda10021_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct tda1002x_config* config; ++ struct dvb_frontend frontend; ++ ++ u8 pwm; ++ u8 reg0; ++}; ++ ++ ++#if 0 ++#define dprintk(x...) printk(x) ++#else ++#define dprintk(x...) ++#endif ++ ++static int verbose; ++ ++#define XIN 57840000UL ++ ++#define FIN (XIN >> 4) ++ ++static int tda10021_inittab_size = 0x40; ++static u8 tda10021_inittab[0x40]= ++{ ++ 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, ++ 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, ++ 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, ++ 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, ++ 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, ++ 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, ++ 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, ++ 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, ++}; ++ ++static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ int ret; ++ ++ ret = i2c_transfer (state->i2c, &msg, 1); ++ if (ret != 1) ++ printk("DVB: TDA10021(%d): %s, writereg error " ++ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", ++ state->frontend.dvb->num, __func__, reg, data, ret); ++ ++ msleep(10); ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) ++{ ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ int ret; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ // Don't print an error message if the id is read. ++ if (ret != 2 && reg != 0x1a) ++ printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", ++ __func__, ret); ++ return b1[0]; ++} ++ ++//get access to tuner ++static int lock_tuner(struct tda10021_state* state) ++{ ++ u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; ++ struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; ++ ++ if(i2c_transfer(state->i2c, &msg, 1) != 1) ++ { ++ printk("tda10021: lock tuner fails\n"); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++//release access from tuner ++static int unlock_tuner(struct tda10021_state* state) ++{ ++ u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; ++ struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; ++ ++ if(i2c_transfer(state->i2c, &msg_post, 1) != 1) ++ { ++ printk("tda10021: unlock tuner fails\n"); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, ++ fe_spectral_inversion_t inversion) ++{ ++ reg0 |= state->reg0 & 0x63; ++ ++ if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) ++ reg0 &= ~0x20; ++ else ++ reg0 |= 0x20; ++ ++ _tda10021_writereg (state, 0x00, reg0 & 0xfe); ++ _tda10021_writereg (state, 0x00, reg0 | 0x01); ++ ++ state->reg0 = reg0; ++ return 0; ++} ++ ++static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) ++{ ++ s32 BDR; ++ s32 BDRI; ++ s16 SFIL=0; ++ u16 NDEC = 0; ++ u32 tmp, ratio; ++ ++ if (symbolrate > XIN/2) ++ symbolrate = XIN/2; ++ if (symbolrate < 500000) ++ symbolrate = 500000; ++ ++ if (symbolrate < XIN/16) NDEC = 1; ++ if (symbolrate < XIN/32) NDEC = 2; ++ if (symbolrate < XIN/64) NDEC = 3; ++ ++ if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; ++ if (symbolrate < (u32)(XIN/16)) SFIL = 0; ++ if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; ++ if (symbolrate < (u32)(XIN/32)) SFIL = 0; ++ if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; ++ if (symbolrate < (u32)(XIN/64)) SFIL = 0; ++ if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; ++ ++ symbolrate <<= NDEC; ++ ratio = (symbolrate << 4) / FIN; ++ tmp = ((symbolrate << 4) % FIN) << 8; ++ ratio = (ratio << 8) + tmp / FIN; ++ tmp = (tmp % FIN) << 8; ++ ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN); ++ ++ BDR = ratio; ++ BDRI = (((XIN << 5) / symbolrate) + 1) / 2; ++ ++ if (BDRI > 0xFF) ++ BDRI = 0xFF; ++ ++ SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; ++ ++ NDEC = (NDEC << 6) | tda10021_inittab[0x03]; ++ ++ _tda10021_writereg (state, 0x03, NDEC); ++ _tda10021_writereg (state, 0x0a, BDR&0xff); ++ _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); ++ _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); ++ ++ _tda10021_writereg (state, 0x0d, BDRI); ++ _tda10021_writereg (state, 0x0e, SFIL); ++ ++ return 0; ++} ++ ++static int tda10021_init (struct dvb_frontend *fe) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ int i; ++ ++ dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); ++ ++ //_tda10021_writereg (fe, 0, 0); ++ ++ for (i=0; ipwm); ++ ++ //Comment by markus ++ //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) ++ //0x2A[4] == BYPPLL -> Power down mode (default 1) ++ //0x2A[5] == LCK -> PLL Lock Flag ++ //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) ++ ++ //Activate PLL ++ _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); ++ return 0; ++} ++ ++struct qam_params { ++ u8 conf, agcref, lthr, mseth, aref; ++}; ++ ++static int tda10021_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 delsys = c->delivery_system; ++ unsigned qam = c->modulation; ++ bool is_annex_c; ++ u32 reg0x3d; ++ struct tda10021_state* state = fe->demodulator_priv; ++ static const struct qam_params qam_params[] = { ++ /* Modulation Conf AGCref LTHR MSETH AREF */ ++ [QPSK] = { 0x14, 0x78, 0x78, 0x8c, 0x96 }, ++ [QAM_16] = { 0x00, 0x8c, 0x87, 0xa2, 0x91 }, ++ [QAM_32] = { 0x04, 0x8c, 0x64, 0x74, 0x96 }, ++ [QAM_64] = { 0x08, 0x6a, 0x46, 0x43, 0x6a }, ++ [QAM_128] = { 0x0c, 0x78, 0x36, 0x34, 0x7e }, ++ [QAM_256] = { 0x10, 0x5c, 0x26, 0x23, 0x6b }, ++ }; ++ ++ switch (delsys) { ++ case SYS_DVBC_ANNEX_A: ++ is_annex_c = false; ++ break; ++ case SYS_DVBC_ANNEX_C: ++ is_annex_c = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * gcc optimizes the code bellow the same way as it would code: ++ * "if (qam > 5) return -EINVAL;" ++ * Yet, the code is clearer, as it shows what QAM standards are ++ * supported by the driver, and avoids the usage of magic numbers on ++ * it. ++ */ ++ switch (qam) { ++ case QPSK: ++ case QAM_16: ++ case QAM_32: ++ case QAM_64: ++ case QAM_128: ++ case QAM_256: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF) ++ return -EINVAL; ++ ++ /*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/ ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ tda10021_set_symbolrate(state, c->symbol_rate); ++ _tda10021_writereg(state, 0x34, state->pwm); ++ ++ _tda10021_writereg(state, 0x01, qam_params[qam].agcref); ++ _tda10021_writereg(state, 0x05, qam_params[qam].lthr); ++ _tda10021_writereg(state, 0x08, qam_params[qam].mseth); ++ _tda10021_writereg(state, 0x09, qam_params[qam].aref); ++ ++ /* ++ * Bit 0 == 0 means roll-off = 0.15 (Annex A) ++ * == 1 means roll-off = 0.13 (Annex C) ++ */ ++ reg0x3d = tda10021_readreg (state, 0x3d); ++ if (is_annex_c) ++ _tda10021_writereg (state, 0x3d, 0x01 | reg0x3d); ++ else ++ _tda10021_writereg (state, 0x3d, 0xfe & reg0x3d); ++ tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion); ++ ++ return 0; ++} ++ ++static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ int sync; ++ ++ *status = 0; ++ //0x11[0] == EQALGO -> Equalizer algorithms state ++ //0x11[1] == CARLOCK -> Carrier locked ++ //0x11[2] == FSYNC -> Frame synchronisation ++ //0x11[3] == FEL -> Front End locked ++ //0x11[6] == NODVB -> DVB Mode Information ++ sync = tda10021_readreg (state, 0x11); ++ ++ if (sync & 2) ++ *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; ++ ++ if (sync & 4) ++ *status |= FE_HAS_SYNC|FE_HAS_VITERBI; ++ ++ if (sync & 8) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ u32 _ber = tda10021_readreg(state, 0x14) | ++ (tda10021_readreg(state, 0x15) << 8) | ++ ((tda10021_readreg(state, 0x16) & 0x0f) << 16); ++ _tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0) ++ | (tda10021_inittab[0x10] & 0xc0)); ++ *ber = 10 * _ber; ++ ++ return 0; ++} ++ ++static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ u8 config = tda10021_readreg(state, 0x02); ++ u8 gain = tda10021_readreg(state, 0x17); ++ if (config & 0x02) ++ /* the agc value is inverted */ ++ gain = ~gain; ++ *strength = (gain << 8) | gain; ++ ++ return 0; ++} ++ ++static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ u8 quality = ~tda10021_readreg(state, 0x18); ++ *snr = (quality << 8) | quality; ++ ++ return 0; ++} ++ ++static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; ++ if (*ucblocks == 0x7f) ++ *ucblocks = 0xffffffff; ++ ++ /* reset uncorrected block counter */ ++ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); ++ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); ++ ++ return 0; ++} ++ ++static int tda10021_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda10021_state* state = fe->demodulator_priv; ++ int sync; ++ s8 afc = 0; ++ ++ sync = tda10021_readreg(state, 0x11); ++ afc = tda10021_readreg(state, 0x19); ++ if (verbose) { ++ /* AFC only valid when carrier has been recovered */ ++ printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : ++ "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", ++ state->frontend.dvb->num, afc, ++ -((s32)p->symbol_rate * afc) >> 10); ++ } ++ ++ p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; ++ p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; ++ ++ p->fec_inner = FEC_NONE; ++ p->frequency = ((p->frequency + 31250) / 62500) * 62500; ++ ++ if (sync & 2) ++ p->frequency -= ((s32)p->symbol_rate * afc) >> 10; ++ ++ return 0; ++} ++ ++static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ lock_tuner(state); ++ } else { ++ unlock_tuner(state); ++ } ++ return 0; ++} ++ ++static int tda10021_sleep(struct dvb_frontend* fe) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ ++ _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ ++ _tda10021_writereg (state, 0x00, 0x80); /* standby */ ++ ++ return 0; ++} ++ ++static void tda10021_release(struct dvb_frontend* fe) ++{ ++ struct tda10021_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops tda10021_ops; ++ ++struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, ++ struct i2c_adapter* i2c, ++ u8 pwm) ++{ ++ struct tda10021_state* state = NULL; ++ u8 id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->pwm = pwm; ++ state->reg0 = tda10021_inittab[0]; ++ ++ /* check if the demod is there */ ++ id = tda10021_readreg(state, 0x1a); ++ if ((id & 0xf0) != 0x70) goto error; ++ ++ /* Don't claim TDA10023 */ ++ if (id == 0x7d) ++ goto error; ++ ++ printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", ++ state->config->demod_address, id); ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops tda10021_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, ++ .info = { ++ .name = "Philips TDA10021 DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ ++ .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ ++ #if 0 ++ .frequency_tolerance = ???, ++ .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ ++ #endif ++ .caps = 0x400 | //FE_CAN_QAM_4 ++ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = tda10021_release, ++ ++ .init = tda10021_init, ++ .sleep = tda10021_sleep, ++ .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, ++ ++ .set_frontend = tda10021_set_parameters, ++ .get_frontend = tda10021_get_frontend, ++ ++ .read_status = tda10021_read_status, ++ .read_ber = tda10021_read_ber, ++ .read_signal_strength = tda10021_read_signal_strength, ++ .read_snr = tda10021_read_snr, ++ .read_ucblocks = tda10021_read_ucblocks, ++}; ++ ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); ++ ++MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); ++MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(tda10021_attach); +diff --git a/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c +new file mode 100644 +index 0000000..ca1e0d5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10023.c +@@ -0,0 +1,610 @@ ++/* ++ TDA10023 - DVB-C decoder ++ (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card) ++ ++ Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de) ++ Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com) ++ ++ Remotely based on tda10021.c ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ Copyright (C) 2004 Markus Schulz ++ Support for TDA10021 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dvb_frontend.h" ++#include "tda1002x.h" ++ ++#define REG0_INIT_VAL 0x23 ++ ++struct tda10023_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct tda10023_config *config; ++ struct dvb_frontend frontend; ++ ++ u8 pwm; ++ u8 reg0; ++ ++ /* clock settings */ ++ u32 xtal; ++ u8 pll_m; ++ u8 pll_p; ++ u8 pll_n; ++ u32 sysclk; ++}; ++ ++#define dprintk(x...) ++ ++static int verbose; ++ ++static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) ++{ ++ u8 b0 [] = { reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ int ret; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ if (ret != 2) { ++ int num = state->frontend.dvb ? state->frontend.dvb->num : -1; ++ printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error " ++ "(reg == 0x%02x, ret == %i)\n", ++ num, __func__, reg, ret); ++ } ++ return b1[0]; ++} ++ ++static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) ++{ ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ int ret; ++ ++ ret = i2c_transfer (state->i2c, &msg, 1); ++ if (ret != 1) { ++ int num = state->frontend.dvb ? state->frontend.dvb->num : -1; ++ printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error " ++ "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", ++ num, __func__, reg, data, ret); ++ } ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++ ++static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data) ++{ ++ if (mask==0xff) ++ return tda10023_writereg(state, reg, data); ++ else { ++ u8 val; ++ val=tda10023_readreg(state,reg); ++ val&=~mask; ++ val|=(data&mask); ++ return tda10023_writereg(state, reg, val); ++ } ++} ++ ++static void tda10023_writetab(struct tda10023_state* state, u8* tab) ++{ ++ u8 r,m,v; ++ while (1) { ++ r=*tab++; ++ m=*tab++; ++ v=*tab++; ++ if (r==0xff) { ++ if (m==0xff) ++ break; ++ else ++ msleep(m); ++ } ++ else ++ tda10023_writebit(state,r,m,v); ++ } ++} ++ ++//get access to tuner ++static int lock_tuner(struct tda10023_state* state) ++{ ++ u8 buf[2] = { 0x0f, 0xc0 }; ++ struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; ++ ++ if(i2c_transfer(state->i2c, &msg, 1) != 1) ++ { ++ printk("tda10023: lock tuner fails\n"); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++//release access from tuner ++static int unlock_tuner(struct tda10023_state* state) ++{ ++ u8 buf[2] = { 0x0f, 0x40 }; ++ struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; ++ ++ if(i2c_transfer(state->i2c, &msg_post, 1) != 1) ++ { ++ printk("tda10023: unlock tuner fails\n"); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0) ++{ ++ reg0 |= state->reg0 & 0x63; ++ ++ tda10023_writereg (state, 0x00, reg0 & 0xfe); ++ tda10023_writereg (state, 0x00, reg0 | 0x01); ++ ++ state->reg0 = reg0; ++ return 0; ++} ++ ++static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) ++{ ++ s32 BDR; ++ s32 BDRI; ++ s16 SFIL=0; ++ u16 NDEC = 0; ++ ++ /* avoid floating point operations multiplying syscloc and divider ++ by 10 */ ++ u32 sysclk_x_10 = state->sysclk * 10; ++ ++ if (sr < (u32)(sysclk_x_10/984)) { ++ NDEC=3; ++ SFIL=1; ++ } else if (sr < (u32)(sysclk_x_10/640)) { ++ NDEC=3; ++ SFIL=0; ++ } else if (sr < (u32)(sysclk_x_10/492)) { ++ NDEC=2; ++ SFIL=1; ++ } else if (sr < (u32)(sysclk_x_10/320)) { ++ NDEC=2; ++ SFIL=0; ++ } else if (sr < (u32)(sysclk_x_10/246)) { ++ NDEC=1; ++ SFIL=1; ++ } else if (sr < (u32)(sysclk_x_10/160)) { ++ NDEC=1; ++ SFIL=0; ++ } else if (sr < (u32)(sysclk_x_10/123)) { ++ NDEC=0; ++ SFIL=1; ++ } ++ ++ BDRI = (state->sysclk)*16; ++ BDRI>>=NDEC; ++ BDRI +=sr/2; ++ BDRI /=sr; ++ ++ if (BDRI>255) ++ BDRI=255; ++ ++ { ++ u64 BDRX; ++ ++ BDRX=1<<(24+NDEC); ++ BDRX*=sr; ++ do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */ ++ ++ BDR=(s32)BDRX; ++ } ++ dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n", ++ sr, BDR, BDRI, NDEC); ++ tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); ++ tda10023_writereg (state, 0x0a, BDR&255); ++ tda10023_writereg (state, 0x0b, (BDR>>8)&255); ++ tda10023_writereg (state, 0x0c, (BDR>>16)&31); ++ tda10023_writereg (state, 0x0d, BDRI); ++ tda10023_writereg (state, 0x3d, (SFIL<<7)); ++ return 0; ++} ++ ++static int tda10023_init (struct dvb_frontend *fe) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ u8 tda10023_inittab[] = { ++/* reg mask val */ ++/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */ ++/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ ++/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */ ++/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ ++ /* PLL1 */ ++/* 012 */ 0x28, 0xff, (state->pll_m-1), ++ /* PLL2 */ ++/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1), ++ /* GPR FSAMPLING=1 */ ++/* 018 */ 0x00, 0xff, REG0_INIT_VAL, ++/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */ ++/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ ++/* 027 */ 0x1f, 0xff, 0x00, /* RESET */ ++/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ ++/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */ ++/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */ ++ ++/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */ ++/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */ ++/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 ++ PPWMTUN=0 PPWMIF=0 */ ++/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */ ++/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */ ++/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */ ++/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */ ++/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */ ++/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */ ++/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */ ++/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */ ++/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */ ++/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */ ++/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */ ++/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */ ++/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */ ++/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */ ++/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */ ++/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /* ++ INTP1 POCLKP=1 FEL=1 MFS=0 */ ++/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */ ++/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */ ++/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */ ++/* 105 */ 0xc4, 0xff, 0x00, ++/* 108 */ 0xc3, 0x30, 0x00, ++/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */ ++/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */ ++/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */ ++/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ ++/* 123 */ 0xff, 0xff, 0xff ++}; ++ dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num); ++ ++ /* override default values if set in config */ ++ if (state->config->deltaf) { ++ tda10023_inittab[80] = (state->config->deltaf & 0xff); ++ tda10023_inittab[83] = (state->config->deltaf >> 8); ++ } ++ ++ if (state->config->output_mode) ++ tda10023_inittab[95] = state->config->output_mode; ++ ++ tda10023_writetab(state, tda10023_inittab); ++ ++ return 0; ++} ++ ++struct qam_params { ++ u8 qam, lockthr, mseth, aref, agcrefnyq, eragnyq_thd; ++}; ++ ++static int tda10023_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 delsys = c->delivery_system; ++ unsigned qam = c->modulation; ++ bool is_annex_c; ++ struct tda10023_state* state = fe->demodulator_priv; ++ static const struct qam_params qam_params[] = { ++ /* Modulation QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD */ ++ [QPSK] = { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, ++ [QAM_16] = { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, ++ [QAM_32] = { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, ++ [QAM_64] = { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, ++ [QAM_128] = { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, ++ [QAM_256] = { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, ++ }; ++ ++ switch (delsys) { ++ case SYS_DVBC_ANNEX_A: ++ is_annex_c = false; ++ break; ++ case SYS_DVBC_ANNEX_C: ++ is_annex_c = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * gcc optimizes the code bellow the same way as it would code: ++ * "if (qam > 5) return -EINVAL;" ++ * Yet, the code is clearer, as it shows what QAM standards are ++ * supported by the driver, and avoids the usage of magic numbers on ++ * it. ++ */ ++ switch (qam) { ++ case QPSK: ++ case QAM_16: ++ case QAM_32: ++ case QAM_64: ++ case QAM_128: ++ case QAM_256: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ tda10023_set_symbolrate(state, c->symbol_rate); ++ tda10023_writereg(state, 0x05, qam_params[qam].lockthr); ++ tda10023_writereg(state, 0x08, qam_params[qam].mseth); ++ tda10023_writereg(state, 0x09, qam_params[qam].aref); ++ tda10023_writereg(state, 0xb4, qam_params[qam].agcrefnyq); ++ tda10023_writereg(state, 0xb6, qam_params[qam].eragnyq_thd); ++#if 0 ++ tda10023_writereg(state, 0x04, (c->inversion ? 0x12 : 0x32)); ++ tda10023_writebit(state, 0x04, 0x60, (c->inversion ? 0 : 0x20)); ++#endif ++ tda10023_writebit(state, 0x04, 0x40, 0x40); ++ ++ if (is_annex_c) ++ tda10023_writebit(state, 0x3d, 0xfc, 0x03); ++ else ++ tda10023_writebit(state, 0x3d, 0xfc, 0x02); ++ ++ tda10023_setup_reg0(state, qam_params[qam].qam); ++ ++ return 0; ++} ++ ++static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ int sync; ++ ++ *status = 0; ++ ++ //0x11[1] == CARLOCK -> Carrier locked ++ //0x11[2] == FSYNC -> Frame synchronisation ++ //0x11[3] == FEL -> Front End locked ++ //0x11[6] == NODVB -> DVB Mode Information ++ sync = tda10023_readreg (state, 0x11); ++ ++ if (sync & 2) ++ *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; ++ ++ if (sync & 4) ++ *status |= FE_HAS_SYNC|FE_HAS_VITERBI; ++ ++ if (sync & 8) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ u8 a,b,c; ++ a=tda10023_readreg(state, 0x14); ++ b=tda10023_readreg(state, 0x15); ++ c=tda10023_readreg(state, 0x16)&0xf; ++ tda10023_writebit (state, 0x10, 0xc0, 0x00); ++ ++ *ber = a | (b<<8)| (c<<16); ++ return 0; ++} ++ ++static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ u8 ifgain=tda10023_readreg(state, 0x2f); ++ ++ u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16; ++ // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90 ++ if (gain>0x90) ++ gain=gain+2*(gain-0x90); ++ if (gain>255) ++ gain=255; ++ ++ *strength = (gain<<8)|gain; ++ return 0; ++} ++ ++static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ ++ u8 quality = ~tda10023_readreg(state, 0x18); ++ *snr = (quality << 8) | quality; ++ return 0; ++} ++ ++static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ u8 a,b,c,d; ++ a= tda10023_readreg (state, 0x74); ++ b= tda10023_readreg (state, 0x75); ++ c= tda10023_readreg (state, 0x76); ++ d= tda10023_readreg (state, 0x77); ++ *ucblocks = a | (b<<8)|(c<<16)|(d<<24); ++ ++ tda10023_writebit (state, 0x10, 0x20,0x00); ++ tda10023_writebit (state, 0x10, 0x20,0x20); ++ tda10023_writebit (state, 0x13, 0x01, 0x00); ++ ++ return 0; ++} ++ ++static int tda10023_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda10023_state* state = fe->demodulator_priv; ++ int sync,inv; ++ s8 afc = 0; ++ ++ sync = tda10023_readreg(state, 0x11); ++ afc = tda10023_readreg(state, 0x19); ++ inv = tda10023_readreg(state, 0x04); ++ ++ if (verbose) { ++ /* AFC only valid when carrier has been recovered */ ++ printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" : ++ "DVB: TDA10023(%d): [AFC (%d) %dHz]\n", ++ state->frontend.dvb->num, afc, ++ -((s32)p->symbol_rate * afc) >> 10); ++ } ++ ++ p->inversion = (inv&0x20?0:1); ++ p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; ++ ++ p->fec_inner = FEC_NONE; ++ p->frequency = ((p->frequency + 31250) / 62500) * 62500; ++ ++ if (sync & 2) ++ p->frequency -= ((s32)p->symbol_rate * afc) >> 10; ++ ++ return 0; ++} ++ ++static int tda10023_sleep(struct dvb_frontend* fe) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ ++ tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */ ++ tda10023_writereg (state, 0x00, 0x80); /* standby */ ++ ++ return 0; ++} ++ ++static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ lock_tuner(state); ++ } else { ++ unlock_tuner(state); ++ } ++ return 0; ++} ++ ++static void tda10023_release(struct dvb_frontend* fe) ++{ ++ struct tda10023_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops tda10023_ops; ++ ++struct dvb_frontend *tda10023_attach(const struct tda10023_config *config, ++ struct i2c_adapter *i2c, ++ u8 pwm) ++{ ++ struct tda10023_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* wakeup if in standby */ ++ tda10023_writereg (state, 0x00, 0x33); ++ /* check if the demod is there */ ++ if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); ++ state->pwm = pwm; ++ state->reg0 = REG0_INIT_VAL; ++ if (state->config->xtal) { ++ state->xtal = state->config->xtal; ++ state->pll_m = state->config->pll_m; ++ state->pll_p = state->config->pll_p; ++ state->pll_n = state->config->pll_n; ++ } else { ++ /* set default values if not defined in config */ ++ state->xtal = 28920000; ++ state->pll_m = 8; ++ state->pll_p = 4; ++ state->pll_n = 1; ++ } ++ ++ /* calc sysclk */ ++ state->sysclk = (state->xtal * state->pll_m / \ ++ (state->pll_n * state->pll_p)); ++ ++ state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64; ++ state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4; ++ ++ dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n", ++ __func__, state->xtal, state->pll_m, state->pll_p, ++ state->pll_n); ++ ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops tda10023_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, ++ .info = { ++ .name = "Philips TDA10023 DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .symbol_rate_min = 0, /* set in tda10023_attach */ ++ .symbol_rate_max = 0, /* set in tda10023_attach */ ++ .caps = 0x400 | //FE_CAN_QAM_4 ++ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | FE_CAN_QAM_256 | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = tda10023_release, ++ ++ .init = tda10023_init, ++ .sleep = tda10023_sleep, ++ .i2c_gate_ctrl = tda10023_i2c_gate_ctrl, ++ ++ .set_frontend = tda10023_set_parameters, ++ .get_frontend = tda10023_get_frontend, ++ .read_status = tda10023_read_status, ++ .read_ber = tda10023_read_ber, ++ .read_signal_strength = tda10023_read_signal_strength, ++ .read_snr = tda10023_read_snr, ++ .read_ucblocks = tda10023_read_ucblocks, ++}; ++ ++ ++MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); ++MODULE_AUTHOR("Georg Acher, Hartmut Birr"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(tda10023_attach); +diff --git a/drivers/media/dvb-frontends/tda1002x.h b/drivers/media/dvb-frontends/tda1002x.h +new file mode 100644 +index 0000000..04d1941 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda1002x.h +@@ -0,0 +1,87 @@ ++/* ++ TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module ++ used on the the Siemens DVB-C cards ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ Copyright (C) 2004 Markus Schulz ++ Support for TDA10021 ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef TDA1002x_H ++#define TDA1002x_H ++ ++#include ++ ++struct tda1002x_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ u8 invert; ++}; ++ ++enum tda10023_output_mode { ++ TDA10023_OUTPUT_MODE_PARALLEL_A = 0xe0, ++ TDA10023_OUTPUT_MODE_PARALLEL_B = 0xa1, ++ TDA10023_OUTPUT_MODE_PARALLEL_C = 0xa0, ++ TDA10023_OUTPUT_MODE_SERIAL, /* TODO: not implemented */ ++}; ++ ++struct tda10023_config { ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ u8 invert; ++ ++ /* clock settings */ ++ u32 xtal; /* defaults: 28920000 */ ++ u8 pll_m; /* defaults: 8 */ ++ u8 pll_p; /* defaults: 4 */ ++ u8 pll_n; /* defaults: 1 */ ++ ++ /* MPEG2 TS output mode */ ++ u8 output_mode; ++ ++ /* input freq offset + baseband conversion type */ ++ u16 deltaf; ++}; ++ ++#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, ++ struct i2c_adapter* i2c, u8 pwm); ++#else ++static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, ++ struct i2c_adapter* i2c, u8 pwm) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TDA10021 ++ ++#if defined(CONFIG_DVB_TDA10023) || \ ++ (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *tda10023_attach( ++ const struct tda10023_config *config, ++ struct i2c_adapter *i2c, u8 pwm); ++#else ++static inline struct dvb_frontend *tda10023_attach( ++ const struct tda10023_config *config, ++ struct i2c_adapter *i2c, u8 pwm) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TDA10023 ++ ++#endif // TDA1002x_H +diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c +new file mode 100644 +index 0000000..71fb632 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10048.c +@@ -0,0 +1,1191 @@ ++/* ++ NXP TDA10048HN DVB OFDM demodulator driver ++ ++ Copyright (C) 2009 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "tda10048.h" ++ ++#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" ++#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878 ++ ++/* Register name definitions */ ++#define TDA10048_IDENTITY 0x00 ++#define TDA10048_VERSION 0x01 ++#define TDA10048_DSP_CODE_CPT 0x0C ++#define TDA10048_DSP_CODE_IN 0x0E ++#define TDA10048_IN_CONF1 0x10 ++#define TDA10048_IN_CONF2 0x11 ++#define TDA10048_IN_CONF3 0x12 ++#define TDA10048_OUT_CONF1 0x14 ++#define TDA10048_OUT_CONF2 0x15 ++#define TDA10048_OUT_CONF3 0x16 ++#define TDA10048_AUTO 0x18 ++#define TDA10048_SYNC_STATUS 0x1A ++#define TDA10048_CONF_C4_1 0x1E ++#define TDA10048_CONF_C4_2 0x1F ++#define TDA10048_CODE_IN_RAM 0x20 ++#define TDA10048_CHANNEL_INFO1_R 0x22 ++#define TDA10048_CHANNEL_INFO2_R 0x23 ++#define TDA10048_CHANNEL_INFO1 0x24 ++#define TDA10048_CHANNEL_INFO2 0x25 ++#define TDA10048_TIME_ERROR_R 0x26 ++#define TDA10048_TIME_ERROR 0x27 ++#define TDA10048_FREQ_ERROR_LSB_R 0x28 ++#define TDA10048_FREQ_ERROR_MSB_R 0x29 ++#define TDA10048_FREQ_ERROR_LSB 0x2A ++#define TDA10048_FREQ_ERROR_MSB 0x2B ++#define TDA10048_IT_SEL 0x30 ++#define TDA10048_IT_STAT 0x32 ++#define TDA10048_DSP_AD_LSB 0x3C ++#define TDA10048_DSP_AD_MSB 0x3D ++#define TDA10048_DSP_REG_LSB 0x3E ++#define TDA10048_DSP_REG_MSB 0x3F ++#define TDA10048_CONF_TRISTATE1 0x44 ++#define TDA10048_CONF_TRISTATE2 0x45 ++#define TDA10048_CONF_POLARITY 0x46 ++#define TDA10048_GPIO_SP_DS0 0x48 ++#define TDA10048_GPIO_SP_DS1 0x49 ++#define TDA10048_GPIO_SP_DS2 0x4A ++#define TDA10048_GPIO_SP_DS3 0x4B ++#define TDA10048_GPIO_OUT_SEL 0x4C ++#define TDA10048_GPIO_SELECT 0x4D ++#define TDA10048_IC_MODE 0x4E ++#define TDA10048_CONF_XO 0x50 ++#define TDA10048_CONF_PLL1 0x51 ++#define TDA10048_CONF_PLL2 0x52 ++#define TDA10048_CONF_PLL3 0x53 ++#define TDA10048_CONF_ADC 0x54 ++#define TDA10048_CONF_ADC_2 0x55 ++#define TDA10048_CONF_C1_1 0x60 ++#define TDA10048_CONF_C1_3 0x62 ++#define TDA10048_AGC_CONF 0x70 ++#define TDA10048_AGC_THRESHOLD_LSB 0x72 ++#define TDA10048_AGC_THRESHOLD_MSB 0x73 ++#define TDA10048_AGC_RENORM 0x74 ++#define TDA10048_AGC_GAINS 0x76 ++#define TDA10048_AGC_TUN_MIN 0x78 ++#define TDA10048_AGC_TUN_MAX 0x79 ++#define TDA10048_AGC_IF_MIN 0x7A ++#define TDA10048_AGC_IF_MAX 0x7B ++#define TDA10048_AGC_TUN_LEVEL 0x7E ++#define TDA10048_AGC_IF_LEVEL 0x7F ++#define TDA10048_DIG_AGC_LEVEL 0x81 ++#define TDA10048_FREQ_PHY2_LSB 0x86 ++#define TDA10048_FREQ_PHY2_MSB 0x87 ++#define TDA10048_TIME_INVWREF_LSB 0x88 ++#define TDA10048_TIME_INVWREF_MSB 0x89 ++#define TDA10048_TIME_WREF_LSB 0x8A ++#define TDA10048_TIME_WREF_MID1 0x8B ++#define TDA10048_TIME_WREF_MID2 0x8C ++#define TDA10048_TIME_WREF_MSB 0x8D ++#define TDA10048_NP_OUT 0xA2 ++#define TDA10048_CELL_ID_LSB 0xA4 ++#define TDA10048_CELL_ID_MSB 0xA5 ++#define TDA10048_EXTTPS_ODD 0xAA ++#define TDA10048_EXTTPS_EVEN 0xAB ++#define TDA10048_TPS_LENGTH 0xAC ++#define TDA10048_FREE_REG_1 0xB2 ++#define TDA10048_FREE_REG_2 0xB3 ++#define TDA10048_CONF_C3_1 0xC0 ++#define TDA10048_CVBER_CTRL 0xC2 ++#define TDA10048_CBER_NMAX_LSB 0xC4 ++#define TDA10048_CBER_NMAX_MSB 0xC5 ++#define TDA10048_CBER_LSB 0xC6 ++#define TDA10048_CBER_MSB 0xC7 ++#define TDA10048_VBER_LSB 0xC8 ++#define TDA10048_VBER_MID 0xC9 ++#define TDA10048_VBER_MSB 0xCA ++#define TDA10048_CVBER_LUT 0xCC ++#define TDA10048_UNCOR_CTRL 0xCD ++#define TDA10048_UNCOR_CPT_LSB 0xCE ++#define TDA10048_UNCOR_CPT_MSB 0xCF ++#define TDA10048_SOFT_IT_C3 0xD6 ++#define TDA10048_CONF_TS2 0xE0 ++#define TDA10048_CONF_TS1 0xE1 ++ ++static unsigned int debug; ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (debug >= level)\ ++ printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ ++ } while (0) ++ ++struct tda10048_state { ++ ++ struct i2c_adapter *i2c; ++ ++ /* We'll cache and update the attach config settings */ ++ struct tda10048_config config; ++ struct dvb_frontend frontend; ++ ++ int fwloaded; ++ ++ u32 freq_if_hz; ++ u32 xtal_hz; ++ u32 pll_mfactor; ++ u32 pll_nfactor; ++ u32 pll_pfactor; ++ u32 sample_freq; ++ ++ u32 bandwidth; ++}; ++ ++static struct init_tab { ++ u8 reg; ++ u16 data; ++} init_tab[] = { ++ { TDA10048_CONF_PLL1, 0x08 }, ++ { TDA10048_CONF_ADC_2, 0x00 }, ++ { TDA10048_CONF_C4_1, 0x00 }, ++ { TDA10048_CONF_PLL1, 0x0f }, ++ { TDA10048_CONF_PLL2, 0x0a }, ++ { TDA10048_CONF_PLL3, 0x43 }, ++ { TDA10048_FREQ_PHY2_LSB, 0x02 }, ++ { TDA10048_FREQ_PHY2_MSB, 0x0a }, ++ { TDA10048_TIME_WREF_LSB, 0xbd }, ++ { TDA10048_TIME_WREF_MID1, 0xe4 }, ++ { TDA10048_TIME_WREF_MID2, 0xa8 }, ++ { TDA10048_TIME_WREF_MSB, 0x02 }, ++ { TDA10048_TIME_INVWREF_LSB, 0x04 }, ++ { TDA10048_TIME_INVWREF_MSB, 0x06 }, ++ { TDA10048_CONF_C4_1, 0x00 }, ++ { TDA10048_CONF_C1_1, 0xa8 }, ++ { TDA10048_AGC_CONF, 0x16 }, ++ { TDA10048_CONF_C1_3, 0x0b }, ++ { TDA10048_AGC_TUN_MIN, 0x00 }, ++ { TDA10048_AGC_TUN_MAX, 0xff }, ++ { TDA10048_AGC_IF_MIN, 0x00 }, ++ { TDA10048_AGC_IF_MAX, 0xff }, ++ { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, ++ { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, ++ { TDA10048_CVBER_CTRL, 0x38 }, ++ { TDA10048_AGC_GAINS, 0x12 }, ++ { TDA10048_CONF_XO, 0x00 }, ++ { TDA10048_CONF_TS1, 0x07 }, ++ { TDA10048_IC_MODE, 0x00 }, ++ { TDA10048_CONF_TS2, 0xc0 }, ++ { TDA10048_CONF_TRISTATE1, 0x21 }, ++ { TDA10048_CONF_TRISTATE2, 0x00 }, ++ { TDA10048_CONF_POLARITY, 0x00 }, ++ { TDA10048_CONF_C4_2, 0x04 }, ++ { TDA10048_CONF_ADC, 0x60 }, ++ { TDA10048_CONF_ADC_2, 0x10 }, ++ { TDA10048_CONF_ADC, 0x60 }, ++ { TDA10048_CONF_ADC_2, 0x00 }, ++ { TDA10048_CONF_C1_1, 0xa8 }, ++ { TDA10048_UNCOR_CTRL, 0x00 }, ++ { TDA10048_CONF_C4_2, 0x04 }, ++}; ++ ++static struct pll_tab { ++ u32 clk_freq_khz; ++ u32 if_freq_khz; ++} pll_tab[] = { ++ { TDA10048_CLK_4000, TDA10048_IF_36130 }, ++ { TDA10048_CLK_16000, TDA10048_IF_3300 }, ++ { TDA10048_CLK_16000, TDA10048_IF_3500 }, ++ { TDA10048_CLK_16000, TDA10048_IF_3800 }, ++ { TDA10048_CLK_16000, TDA10048_IF_4000 }, ++ { TDA10048_CLK_16000, TDA10048_IF_4300 }, ++ { TDA10048_CLK_16000, TDA10048_IF_4500 }, ++ { TDA10048_CLK_16000, TDA10048_IF_5000 }, ++ { TDA10048_CLK_16000, TDA10048_IF_36130 }, ++}; ++ ++static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) ++{ ++ struct tda10048_config *config = &state->config; ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { ++ .addr = config->demod_address, ++ .flags = 0, .buf = buf, .len = 2 }; ++ ++ dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk("%s: writereg error (ret == %i)\n", __func__, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static u8 tda10048_readreg(struct tda10048_state *state, u8 reg) ++{ ++ struct tda10048_config *config = &state->config; ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { .addr = config->demod_address, ++ .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = config->demod_address, ++ .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk(KERN_ERR "%s: readreg error (ret == %i)\n", ++ __func__, ret); ++ ++ return b1[0]; ++} ++ ++static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, ++ const u8 *data, u16 len) ++{ ++ struct tda10048_config *config = &state->config; ++ int ret = -EREMOTEIO; ++ struct i2c_msg msg; ++ u8 *buf; ++ ++ dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); ++ ++ buf = kmalloc(len + 1, GFP_KERNEL); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ *buf = reg; ++ memcpy(buf + 1, data, len); ++ ++ msg.addr = config->demod_address; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = len + 1; ++ ++ dprintk(2, "%s(): write len = %d\n", ++ __func__, msg.len); ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ if (ret != 1) { ++ printk(KERN_ERR "%s(): writereg error err %i\n", ++ __func__, ret); ++ ret = -EREMOTEIO; ++ } ++ ++error: ++ kfree(buf); ++ ++ return ret; ++} ++ ++static int tda10048_set_phy2(struct dvb_frontend *fe, u32 sample_freq_hz, ++ u32 if_hz) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u64 t; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (sample_freq_hz == 0) ++ return -EINVAL; ++ ++ if (if_hz < (sample_freq_hz / 2)) { ++ /* PHY2 = (if2/fs) * 2^15 */ ++ t = if_hz; ++ t *= 10; ++ t *= 32768; ++ do_div(t, sample_freq_hz); ++ t += 5; ++ do_div(t, 10); ++ } else { ++ /* PHY2 = ((IF1-fs)/fs) * 2^15 */ ++ t = sample_freq_hz - if_hz; ++ t *= 10; ++ t *= 32768; ++ do_div(t, sample_freq_hz); ++ t += 5; ++ do_div(t, 10); ++ t = ~t + 1; ++ } ++ ++ tda10048_writereg(state, TDA10048_FREQ_PHY2_LSB, (u8)t); ++ tda10048_writereg(state, TDA10048_FREQ_PHY2_MSB, (u8)(t >> 8)); ++ ++ return 0; ++} ++ ++static int tda10048_set_wref(struct dvb_frontend *fe, u32 sample_freq_hz, ++ u32 bw) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u64 t, z; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (sample_freq_hz == 0) ++ return -EINVAL; ++ ++ /* WREF = (B / (7 * fs)) * 2^31 */ ++ t = bw * 10; ++ /* avoid warning: this decimal constant is unsigned only in ISO C90 */ ++ /* t *= 2147483648 on 32bit platforms */ ++ t *= (2048 * 1024); ++ t *= 1024; ++ z = 7 * sample_freq_hz; ++ do_div(t, z); ++ t += 5; ++ do_div(t, 10); ++ ++ tda10048_writereg(state, TDA10048_TIME_WREF_LSB, (u8)t); ++ tda10048_writereg(state, TDA10048_TIME_WREF_MID1, (u8)(t >> 8)); ++ tda10048_writereg(state, TDA10048_TIME_WREF_MID2, (u8)(t >> 16)); ++ tda10048_writereg(state, TDA10048_TIME_WREF_MSB, (u8)(t >> 24)); ++ ++ return 0; ++} ++ ++static int tda10048_set_invwref(struct dvb_frontend *fe, u32 sample_freq_hz, ++ u32 bw) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u64 t; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (sample_freq_hz == 0) ++ return -EINVAL; ++ ++ /* INVWREF = ((7 * fs) / B) * 2^5 */ ++ t = sample_freq_hz; ++ t *= 7; ++ t *= 32; ++ t *= 10; ++ do_div(t, bw); ++ t += 5; ++ do_div(t, 10); ++ ++ tda10048_writereg(state, TDA10048_TIME_INVWREF_LSB, (u8)t); ++ tda10048_writereg(state, TDA10048_TIME_INVWREF_MSB, (u8)(t >> 8)); ++ ++ return 0; ++} ++ ++static int tda10048_set_bandwidth(struct dvb_frontend *fe, ++ u32 bw) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ dprintk(1, "%s(bw=%d)\n", __func__, bw); ++ ++ /* Bandwidth setting may need to be adjusted */ ++ switch (bw) { ++ case 6000000: ++ case 7000000: ++ case 8000000: ++ tda10048_set_wref(fe, state->sample_freq, bw); ++ tda10048_set_invwref(fe, state->sample_freq, bw); ++ break; ++ default: ++ printk(KERN_ERR "%s() invalid bandwidth\n", __func__); ++ return -EINVAL; ++ } ++ ++ state->bandwidth = bw; ++ ++ return 0; ++} ++ ++static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ struct tda10048_config *config = &state->config; ++ int i; ++ u32 if_freq_khz; ++ ++ dprintk(1, "%s(bw = %d)\n", __func__, bw); ++ ++ /* based on target bandwidth and clk we calculate pll factors */ ++ switch (bw) { ++ case 6000000: ++ if_freq_khz = config->dtv6_if_freq_khz; ++ break; ++ case 7000000: ++ if_freq_khz = config->dtv7_if_freq_khz; ++ break; ++ case 8000000: ++ if_freq_khz = config->dtv8_if_freq_khz; ++ break; ++ default: ++ printk(KERN_ERR "%s() no default\n", __func__); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(pll_tab); i++) { ++ if ((pll_tab[i].clk_freq_khz == config->clk_freq_khz) && ++ (pll_tab[i].if_freq_khz == if_freq_khz)) { ++ ++ state->freq_if_hz = pll_tab[i].if_freq_khz * 1000; ++ state->xtal_hz = pll_tab[i].clk_freq_khz * 1000; ++ break; ++ } ++ } ++ if (i == ARRAY_SIZE(pll_tab)) { ++ printk(KERN_ERR "%s() Incorrect attach settings\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ dprintk(1, "- freq_if_hz = %d\n", state->freq_if_hz); ++ dprintk(1, "- xtal_hz = %d\n", state->xtal_hz); ++ dprintk(1, "- pll_mfactor = %d\n", state->pll_mfactor); ++ dprintk(1, "- pll_nfactor = %d\n", state->pll_nfactor); ++ dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); ++ ++ /* Calculate the sample frequency */ ++ state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45); ++ state->sample_freq /= (state->pll_nfactor + 1); ++ state->sample_freq /= (state->pll_pfactor + 4); ++ dprintk(1, "- sample_freq = %d\n", state->sample_freq); ++ ++ /* Update the I/F */ ++ tda10048_set_phy2(fe, state->sample_freq, state->freq_if_hz); ++ ++ return 0; ++} ++ ++static int tda10048_firmware_upload(struct dvb_frontend *fe) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ struct tda10048_config *config = &state->config; ++ const struct firmware *fw; ++ int ret; ++ int pos = 0; ++ int cnt; ++ u8 wlen = config->fwbulkwritelen; ++ ++ if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) ++ wlen = TDA10048_BULKWRITE_200; ++ ++ /* request the firmware, this will block and timeout */ ++ printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", ++ __func__, ++ TDA10048_DEFAULT_FIRMWARE); ++ ++ ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, ++ state->i2c->dev.parent); ++ if (ret) { ++ printk(KERN_ERR "%s: Upload failed. (file not found?)\n", ++ __func__); ++ return -EIO; ++ } else { ++ printk(KERN_INFO "%s: firmware read %Zu bytes.\n", ++ __func__, ++ fw->size); ++ ret = 0; ++ } ++ ++ if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { ++ printk(KERN_ERR "%s: firmware incorrect size\n", __func__); ++ ret = -EIO; ++ } else { ++ printk(KERN_INFO "%s: firmware uploading\n", __func__); ++ ++ /* Soft reset */ ++ tda10048_writereg(state, TDA10048_CONF_TRISTATE1, ++ tda10048_readreg(state, TDA10048_CONF_TRISTATE1) ++ & 0xfe); ++ tda10048_writereg(state, TDA10048_CONF_TRISTATE1, ++ tda10048_readreg(state, TDA10048_CONF_TRISTATE1) ++ | 0x01); ++ ++ /* Put the demod into host download mode */ ++ tda10048_writereg(state, TDA10048_CONF_C4_1, ++ tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); ++ ++ /* Boot the DSP */ ++ tda10048_writereg(state, TDA10048_CONF_C4_1, ++ tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); ++ ++ /* Prepare for download */ ++ tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); ++ ++ /* Download the firmware payload */ ++ while (pos < fw->size) { ++ ++ if ((fw->size - pos) > wlen) ++ cnt = wlen; ++ else ++ cnt = fw->size - pos; ++ ++ tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, ++ &fw->data[pos], cnt); ++ ++ pos += cnt; ++ } ++ ++ ret = -EIO; ++ /* Wait up to 250ms for the DSP to boot */ ++ for (cnt = 0; cnt < 250 ; cnt += 10) { ++ ++ msleep(10); ++ ++ if (tda10048_readreg(state, TDA10048_SYNC_STATUS) ++ & 0x40) { ++ ret = 0; ++ break; ++ } ++ } ++ } ++ ++ release_firmware(fw); ++ ++ if (ret == 0) { ++ printk(KERN_INFO "%s: firmware uploaded\n", __func__); ++ state->fwloaded = 1; ++ } else ++ printk(KERN_ERR "%s: firmware upload failed\n", __func__); ++ ++ return ret; ++} ++ ++static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "%s(%d)\n", __func__, inversion); ++ ++ if (inversion == TDA10048_INVERSION_ON) ++ tda10048_writereg(state, TDA10048_CONF_C1_1, ++ tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); ++ else ++ tda10048_writereg(state, TDA10048_CONF_C1_1, ++ tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); ++ ++ return 0; ++} ++ ++/* Retrieve the demod settings */ ++static int tda10048_get_tps(struct tda10048_state *state, ++ struct dtv_frontend_properties *p) ++{ ++ u8 val; ++ ++ /* Make sure the TPS regs are valid */ ++ if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) ++ return -EAGAIN; ++ ++ val = tda10048_readreg(state, TDA10048_OUT_CONF2); ++ switch ((val & 0x60) >> 5) { ++ case 0: ++ p->modulation = QPSK; ++ break; ++ case 1: ++ p->modulation = QAM_16; ++ break; ++ case 2: ++ p->modulation = QAM_64; ++ break; ++ } ++ switch ((val & 0x18) >> 3) { ++ case 0: ++ p->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ p->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ p->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ p->hierarchy = HIERARCHY_4; ++ break; ++ } ++ switch (val & 0x07) { ++ case 0: ++ p->code_rate_HP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_HP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_HP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_HP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_HP = FEC_7_8; ++ break; ++ } ++ ++ val = tda10048_readreg(state, TDA10048_OUT_CONF3); ++ switch (val & 0x07) { ++ case 0: ++ p->code_rate_LP = FEC_1_2; ++ break; ++ case 1: ++ p->code_rate_LP = FEC_2_3; ++ break; ++ case 2: ++ p->code_rate_LP = FEC_3_4; ++ break; ++ case 3: ++ p->code_rate_LP = FEC_5_6; ++ break; ++ case 4: ++ p->code_rate_LP = FEC_7_8; ++ break; ++ } ++ ++ val = tda10048_readreg(state, TDA10048_OUT_CONF1); ++ switch ((val & 0x0c) >> 2) { ++ case 0: ++ p->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ p->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ p->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ p->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ switch (val & 0x03) { ++ case 0: ++ p->transmission_mode = TRANSMISSION_MODE_2K; ++ break; ++ case 1: ++ p->transmission_mode = TRANSMISSION_MODE_8K; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tda10048_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ struct tda10048_config *config = &state->config; ++ dprintk(1, "%s(%d)\n", __func__, enable); ++ ++ if (config->disable_gate_access) ++ return 0; ++ ++ if (enable) ++ return tda10048_writereg(state, TDA10048_CONF_C4_1, ++ tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x02); ++ else ++ return tda10048_writereg(state, TDA10048_CONF_C4_1, ++ tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xfd); ++} ++ ++static int tda10048_output_mode(struct dvb_frontend *fe, int serial) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ dprintk(1, "%s(%d)\n", __func__, serial); ++ ++ /* Ensure pins are out of tri-state */ ++ tda10048_writereg(state, TDA10048_CONF_TRISTATE1, 0x21); ++ tda10048_writereg(state, TDA10048_CONF_TRISTATE2, 0x00); ++ ++ if (serial) { ++ tda10048_writereg(state, TDA10048_IC_MODE, 0x80 | 0x20); ++ tda10048_writereg(state, TDA10048_CONF_TS2, 0xc0); ++ } else { ++ tda10048_writereg(state, TDA10048_IC_MODE, 0x00); ++ tda10048_writereg(state, TDA10048_CONF_TS2, 0x01); ++ } ++ ++ return 0; ++} ++ ++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ ++/* TODO: Support manual tuning with specific params */ ++static int tda10048_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda10048_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "%s(frequency=%d)\n", __func__, p->frequency); ++ ++ /* Update the I/F pll's if the bandwidth changes */ ++ if (p->bandwidth_hz != state->bandwidth) { ++ tda10048_set_if(fe, p->bandwidth_hz); ++ tda10048_set_bandwidth(fe, p->bandwidth_hz); ++ } ++ ++ if (fe->ops.tuner_ops.set_params) { ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ fe->ops.tuner_ops.set_params(fe); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* Enable demod TPS auto detection and begin acquisition */ ++ tda10048_writereg(state, TDA10048_AUTO, 0x57); ++ /* trigger cber and vber acquisition */ ++ tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x3B); ++ ++ return 0; ++} ++ ++/* Establish sane defaults and load firmware. */ ++static int tda10048_init(struct dvb_frontend *fe) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ struct tda10048_config *config = &state->config; ++ int ret = 0, i; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* PLL */ ++ init_tab[4].data = (u8)(state->pll_mfactor); ++ init_tab[5].data = (u8)(state->pll_nfactor) | 0x40; ++ ++ /* Apply register defaults */ ++ for (i = 0; i < ARRAY_SIZE(init_tab); i++) ++ tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); ++ ++ if (state->fwloaded == 0) ++ ret = tda10048_firmware_upload(fe); ++ ++ /* Set either serial or parallel */ ++ tda10048_output_mode(fe, config->output_mode); ++ ++ /* Set inversion */ ++ tda10048_set_inversion(fe, config->inversion); ++ ++ /* Establish default RF values */ ++ tda10048_set_if(fe, 8000000); ++ tda10048_set_bandwidth(fe, 8000000); ++ ++ /* Ensure we leave the gate closed */ ++ tda10048_i2c_gate_ctrl(fe, 0); ++ ++ return ret; ++} ++ ++static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u8 reg; ++ ++ *status = 0; ++ ++ reg = tda10048_readreg(state, TDA10048_SYNC_STATUS); ++ ++ dprintk(1, "%s() status =0x%02x\n", __func__, reg); ++ ++ if (reg & 0x02) ++ *status |= FE_HAS_CARRIER; ++ ++ if (reg & 0x04) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (reg & 0x08) { ++ *status |= FE_HAS_LOCK; ++ *status |= FE_HAS_VITERBI; ++ *status |= FE_HAS_SYNC; ++ } ++ ++ return 0; ++} ++ ++static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ static u32 cber_current; ++ u32 cber_nmax; ++ u64 cber_tmp; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* update cber on interrupt */ ++ if (tda10048_readreg(state, TDA10048_SOFT_IT_C3) & 0x01) { ++ cber_tmp = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | ++ tda10048_readreg(state, TDA10048_CBER_LSB); ++ cber_nmax = tda10048_readreg(state, TDA10048_CBER_NMAX_MSB) << 8 | ++ tda10048_readreg(state, TDA10048_CBER_NMAX_LSB); ++ cber_tmp *= 100000000; ++ cber_tmp *= 2; ++ cber_tmp = div_u64(cber_tmp, (cber_nmax * 32) + 1); ++ cber_current = (u32)cber_tmp; ++ /* retrigger cber acquisition */ ++ tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x39); ++ } ++ /* actual cber is (*ber)/1e8 */ ++ *ber = cber_current; ++ ++ return 0; ++} ++ ++static int tda10048_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u8 v; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ *signal_strength = 65535; ++ ++ v = tda10048_readreg(state, TDA10048_NP_OUT); ++ if (v > 0) ++ *signal_strength -= (v << 8) | v; ++ ++ return 0; ++} ++ ++/* SNR lookup table */ ++static struct snr_tab { ++ u8 val; ++ u8 data; ++} snr_tab[] = { ++ { 0, 0 }, ++ { 1, 246 }, ++ { 2, 215 }, ++ { 3, 198 }, ++ { 4, 185 }, ++ { 5, 176 }, ++ { 6, 168 }, ++ { 7, 161 }, ++ { 8, 155 }, ++ { 9, 150 }, ++ { 10, 146 }, ++ { 11, 141 }, ++ { 12, 138 }, ++ { 13, 134 }, ++ { 14, 131 }, ++ { 15, 128 }, ++ { 16, 125 }, ++ { 17, 122 }, ++ { 18, 120 }, ++ { 19, 118 }, ++ { 20, 115 }, ++ { 21, 113 }, ++ { 22, 111 }, ++ { 23, 109 }, ++ { 24, 107 }, ++ { 25, 106 }, ++ { 26, 104 }, ++ { 27, 102 }, ++ { 28, 101 }, ++ { 29, 99 }, ++ { 30, 98 }, ++ { 31, 96 }, ++ { 32, 95 }, ++ { 33, 94 }, ++ { 34, 92 }, ++ { 35, 91 }, ++ { 36, 90 }, ++ { 37, 89 }, ++ { 38, 88 }, ++ { 39, 86 }, ++ { 40, 85 }, ++ { 41, 84 }, ++ { 42, 83 }, ++ { 43, 82 }, ++ { 44, 81 }, ++ { 45, 80 }, ++ { 46, 79 }, ++ { 47, 78 }, ++ { 48, 77 }, ++ { 49, 76 }, ++ { 50, 76 }, ++ { 51, 75 }, ++ { 52, 74 }, ++ { 53, 73 }, ++ { 54, 72 }, ++ { 56, 71 }, ++ { 57, 70 }, ++ { 58, 69 }, ++ { 60, 68 }, ++ { 61, 67 }, ++ { 63, 66 }, ++ { 64, 65 }, ++ { 66, 64 }, ++ { 67, 63 }, ++ { 68, 62 }, ++ { 69, 62 }, ++ { 70, 61 }, ++ { 72, 60 }, ++ { 74, 59 }, ++ { 75, 58 }, ++ { 77, 57 }, ++ { 79, 56 }, ++ { 81, 55 }, ++ { 83, 54 }, ++ { 85, 53 }, ++ { 87, 52 }, ++ { 89, 51 }, ++ { 91, 50 }, ++ { 93, 49 }, ++ { 95, 48 }, ++ { 97, 47 }, ++ { 100, 46 }, ++ { 102, 45 }, ++ { 104, 44 }, ++ { 107, 43 }, ++ { 109, 42 }, ++ { 112, 41 }, ++ { 114, 40 }, ++ { 117, 39 }, ++ { 120, 38 }, ++ { 123, 37 }, ++ { 125, 36 }, ++ { 128, 35 }, ++ { 131, 34 }, ++ { 134, 33 }, ++ { 138, 32 }, ++ { 141, 31 }, ++ { 144, 30 }, ++ { 147, 29 }, ++ { 151, 28 }, ++ { 154, 27 }, ++ { 158, 26 }, ++ { 162, 25 }, ++ { 165, 24 }, ++ { 169, 23 }, ++ { 173, 22 }, ++ { 177, 21 }, ++ { 181, 20 }, ++ { 186, 19 }, ++ { 190, 18 }, ++ { 194, 17 }, ++ { 199, 16 }, ++ { 204, 15 }, ++ { 208, 14 }, ++ { 213, 13 }, ++ { 218, 12 }, ++ { 223, 11 }, ++ { 229, 10 }, ++ { 234, 9 }, ++ { 239, 8 }, ++ { 245, 7 }, ++ { 251, 6 }, ++ { 255, 5 }, ++}; ++ ++static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ u8 v; ++ int i, ret = -EINVAL; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ v = tda10048_readreg(state, TDA10048_NP_OUT); ++ for (i = 0; i < ARRAY_SIZE(snr_tab); i++) { ++ if (v <= snr_tab[i].val) { ++ *snr = snr_tab[i].data; ++ ret = 0; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | ++ tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); ++ /* clear the uncorrected TS packets counter when saturated */ ++ if (*ucblocks == 0xFFFF) ++ tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x80); ++ ++ return 0; ++} ++ ++static int tda10048_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda10048_state *state = fe->demodulator_priv; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ p->inversion = tda10048_readreg(state, TDA10048_CONF_C1_1) ++ & 0x20 ? INVERSION_ON : INVERSION_OFF; ++ ++ return tda10048_get_tps(state, p); ++} ++ ++static int tda10048_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *tune) ++{ ++ tune->min_delay_ms = 1000; ++ return 0; ++} ++ ++static void tda10048_release(struct dvb_frontend *fe) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ dprintk(1, "%s()\n", __func__); ++ kfree(state); ++} ++ ++static void tda10048_establish_defaults(struct dvb_frontend *fe) ++{ ++ struct tda10048_state *state = fe->demodulator_priv; ++ struct tda10048_config *config = &state->config; ++ ++ /* Validate/default the config */ ++ if (config->dtv6_if_freq_khz == 0) { ++ config->dtv6_if_freq_khz = TDA10048_IF_4300; ++ printk(KERN_WARNING "%s() tda10048_config.dtv6_if_freq_khz " ++ "is not set (defaulting to %d)\n", ++ __func__, ++ config->dtv6_if_freq_khz); ++ } ++ ++ if (config->dtv7_if_freq_khz == 0) { ++ config->dtv7_if_freq_khz = TDA10048_IF_4300; ++ printk(KERN_WARNING "%s() tda10048_config.dtv7_if_freq_khz " ++ "is not set (defaulting to %d)\n", ++ __func__, ++ config->dtv7_if_freq_khz); ++ } ++ ++ if (config->dtv8_if_freq_khz == 0) { ++ config->dtv8_if_freq_khz = TDA10048_IF_4300; ++ printk(KERN_WARNING "%s() tda10048_config.dtv8_if_freq_khz " ++ "is not set (defaulting to %d)\n", ++ __func__, ++ config->dtv8_if_freq_khz); ++ } ++ ++ if (config->clk_freq_khz == 0) { ++ config->clk_freq_khz = TDA10048_CLK_16000; ++ printk(KERN_WARNING "%s() tda10048_config.clk_freq_khz " ++ "is not set (defaulting to %d)\n", ++ __func__, ++ config->clk_freq_khz); ++ } ++} ++ ++static struct dvb_frontend_ops tda10048_ops; ++ ++struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct tda10048_state *state = NULL; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state and clone the config */ ++ memcpy(&state->config, config, sizeof(*config)); ++ state->i2c = i2c; ++ state->fwloaded = config->no_firmware; ++ state->bandwidth = 8000000; ++ ++ /* check if the demod is present */ ++ if (tda10048_readreg(state, TDA10048_IDENTITY) != 0x048) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10048_ops, ++ sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ /* set pll */ ++ if (config->set_pll) { ++ state->pll_mfactor = config->pll_m; ++ state->pll_nfactor = config->pll_n; ++ state->pll_pfactor = config->pll_p; ++ } else { ++ state->pll_mfactor = 10; ++ state->pll_nfactor = 3; ++ state->pll_pfactor = 0; ++ } ++ ++ /* Establish any defaults the the user didn't pass */ ++ tda10048_establish_defaults(&state->frontend); ++ ++ /* Set the xtal and freq defaults */ ++ if (tda10048_set_if(&state->frontend, 8000000) != 0) ++ goto error; ++ ++ /* Default bandwidth */ ++ if (tda10048_set_bandwidth(&state->frontend, 8000000) != 0) ++ goto error; ++ ++ /* Leave the gate closed */ ++ tda10048_i2c_gate_ctrl(&state->frontend, 0); ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(tda10048_attach); ++ ++static struct dvb_frontend_ops tda10048_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "NXP TDA10048HN DVB-T", ++ .frequency_min = 177000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166666, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER ++ }, ++ ++ .release = tda10048_release, ++ .init = tda10048_init, ++ .i2c_gate_ctrl = tda10048_i2c_gate_ctrl, ++ .set_frontend = tda10048_set_frontend, ++ .get_frontend = tda10048_get_frontend, ++ .get_tune_settings = tda10048_get_tune_settings, ++ .read_status = tda10048_read_status, ++ .read_ber = tda10048_read_ber, ++ .read_signal_strength = tda10048_read_signal_strength, ++ .read_snr = tda10048_read_snr, ++ .read_ucblocks = tda10048_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable verbose debug messages"); ++ ++MODULE_DESCRIPTION("NXP TDA10048HN DVB-T Demodulator driver"); ++MODULE_AUTHOR("Steven Toth"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tda10048.h b/drivers/media/dvb-frontends/tda10048.h +new file mode 100644 +index 0000000..fb2ef5a +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10048.h +@@ -0,0 +1,90 @@ ++/* ++ NXP TDA10048HN DVB OFDM demodulator driver ++ ++ Copyright (C) 2009 Steven Toth ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef TDA10048_H ++#define TDA10048_H ++ ++#include ++#include ++ ++struct tda10048_config { ++ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* serial/parallel output */ ++#define TDA10048_PARALLEL_OUTPUT 0 ++#define TDA10048_SERIAL_OUTPUT 1 ++ u8 output_mode; ++ ++#define TDA10048_BULKWRITE_200 200 ++#define TDA10048_BULKWRITE_50 50 ++ u8 fwbulkwritelen; ++ ++ /* Spectral Inversion */ ++#define TDA10048_INVERSION_OFF 0 ++#define TDA10048_INVERSION_ON 1 ++ u8 inversion; ++ ++#define TDA10048_IF_3300 3300 ++#define TDA10048_IF_3500 3500 ++#define TDA10048_IF_3800 3800 ++#define TDA10048_IF_4000 4000 ++#define TDA10048_IF_4300 4300 ++#define TDA10048_IF_4500 4500 ++#define TDA10048_IF_4750 4750 ++#define TDA10048_IF_5000 5000 ++#define TDA10048_IF_36130 36130 ++ u16 dtv6_if_freq_khz; ++ u16 dtv7_if_freq_khz; ++ u16 dtv8_if_freq_khz; ++ ++#define TDA10048_CLK_4000 4000 ++#define TDA10048_CLK_16000 16000 ++ u16 clk_freq_khz; ++ ++ /* Disable I2C gate access */ ++ u8 disable_gate_access; ++ ++ bool no_firmware; ++ ++ bool set_pll; ++ u8 pll_m; ++ u8 pll_p; ++ u8 pll_n; ++}; ++ ++#if defined(CONFIG_DVB_TDA10048) || \ ++ (defined(CONFIG_DVB_TDA10048_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *tda10048_attach( ++ const struct tda10048_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *tda10048_attach( ++ const struct tda10048_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_TDA10048 */ ++ ++#endif /* TDA10048_H */ +diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c +new file mode 100644 +index 0000000..a2631be +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda1004x.c +@@ -0,0 +1,1385 @@ ++ /* ++ Driver for Philips tda1004xh OFDM Demodulator ++ ++ (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++/* ++ * This driver needs external firmware. Please use the commands ++ * "/Documentation/dvb/get_dvb_firmware tda10045", ++ * "/Documentation/dvb/get_dvb_firmware tda10046" to ++ * download/extract them, and then copy them to /usr/lib/hotplug/firmware ++ * or /lib/firmware (depending on configuration of firmware hotplug). ++ */ ++#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw" ++#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda1004x.h" ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "tda1004x: " args); \ ++ } while (0) ++ ++#define TDA1004X_CHIPID 0x00 ++#define TDA1004X_AUTO 0x01 ++#define TDA1004X_IN_CONF1 0x02 ++#define TDA1004X_IN_CONF2 0x03 ++#define TDA1004X_OUT_CONF1 0x04 ++#define TDA1004X_OUT_CONF2 0x05 ++#define TDA1004X_STATUS_CD 0x06 ++#define TDA1004X_CONFC4 0x07 ++#define TDA1004X_DSSPARE2 0x0C ++#define TDA10045H_CODE_IN 0x0D ++#define TDA10045H_FWPAGE 0x0E ++#define TDA1004X_SCAN_CPT 0x10 ++#define TDA1004X_DSP_CMD 0x11 ++#define TDA1004X_DSP_ARG 0x12 ++#define TDA1004X_DSP_DATA1 0x13 ++#define TDA1004X_DSP_DATA2 0x14 ++#define TDA1004X_CONFADC1 0x15 ++#define TDA1004X_CONFC1 0x16 ++#define TDA10045H_S_AGC 0x1a ++#define TDA10046H_AGC_TUN_LEVEL 0x1a ++#define TDA1004X_SNR 0x1c ++#define TDA1004X_CONF_TS1 0x1e ++#define TDA1004X_CONF_TS2 0x1f ++#define TDA1004X_CBER_RESET 0x20 ++#define TDA1004X_CBER_MSB 0x21 ++#define TDA1004X_CBER_LSB 0x22 ++#define TDA1004X_CVBER_LUT 0x23 ++#define TDA1004X_VBER_MSB 0x24 ++#define TDA1004X_VBER_MID 0x25 ++#define TDA1004X_VBER_LSB 0x26 ++#define TDA1004X_UNCOR 0x27 ++ ++#define TDA10045H_CONFPLL_P 0x2D ++#define TDA10045H_CONFPLL_M_MSB 0x2E ++#define TDA10045H_CONFPLL_M_LSB 0x2F ++#define TDA10045H_CONFPLL_N 0x30 ++ ++#define TDA10046H_CONFPLL1 0x2D ++#define TDA10046H_CONFPLL2 0x2F ++#define TDA10046H_CONFPLL3 0x30 ++#define TDA10046H_TIME_WREF1 0x31 ++#define TDA10046H_TIME_WREF2 0x32 ++#define TDA10046H_TIME_WREF3 0x33 ++#define TDA10046H_TIME_WREF4 0x34 ++#define TDA10046H_TIME_WREF5 0x35 ++ ++#define TDA10045H_UNSURW_MSB 0x31 ++#define TDA10045H_UNSURW_LSB 0x32 ++#define TDA10045H_WREF_MSB 0x33 ++#define TDA10045H_WREF_MID 0x34 ++#define TDA10045H_WREF_LSB 0x35 ++#define TDA10045H_MUXOUT 0x36 ++#define TDA1004X_CONFADC2 0x37 ++ ++#define TDA10045H_IOFFSET 0x38 ++ ++#define TDA10046H_CONF_TRISTATE1 0x3B ++#define TDA10046H_CONF_TRISTATE2 0x3C ++#define TDA10046H_CONF_POLARITY 0x3D ++#define TDA10046H_FREQ_OFFSET 0x3E ++#define TDA10046H_GPIO_OUT_SEL 0x41 ++#define TDA10046H_GPIO_SELECT 0x42 ++#define TDA10046H_AGC_CONF 0x43 ++#define TDA10046H_AGC_THR 0x44 ++#define TDA10046H_AGC_RENORM 0x45 ++#define TDA10046H_AGC_GAINS 0x46 ++#define TDA10046H_AGC_TUN_MIN 0x47 ++#define TDA10046H_AGC_TUN_MAX 0x48 ++#define TDA10046H_AGC_IF_MIN 0x49 ++#define TDA10046H_AGC_IF_MAX 0x4A ++ ++#define TDA10046H_FREQ_PHY2_MSB 0x4D ++#define TDA10046H_FREQ_PHY2_LSB 0x4E ++ ++#define TDA10046H_CVBER_CTRL 0x4F ++#define TDA10046H_AGC_IF_LEVEL 0x52 ++#define TDA10046H_CODE_CPT 0x57 ++#define TDA10046H_CODE_IN 0x58 ++ ++ ++static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data) ++{ ++ int ret; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; ++ ++ dprintk("%s: reg=0x%x, data=0x%x\n", __func__, reg, data); ++ ++ msg.addr = state->config->demod_address; ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", ++ __func__, reg, data, ret); ++ ++ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, ++ reg, data, ret); ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int tda1004x_read_byte(struct tda1004x_state *state, int reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, ++ { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; ++ ++ dprintk("%s: reg=0x%x\n", __func__, reg); ++ ++ msg[0].addr = state->config->demod_address; ++ msg[1].addr = state->config->demod_address; ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ++ ret); ++ return -EINVAL; ++ } ++ ++ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, ++ reg, b1[0], ret); ++ return b1[0]; ++} ++ ++static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data) ++{ ++ int val; ++ dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __func__, reg, ++ mask, data); ++ ++ // read a byte and check ++ val = tda1004x_read_byte(state, reg); ++ if (val < 0) ++ return val; ++ ++ // mask if off ++ val = val & ~mask; ++ val |= data & 0xff; ++ ++ // write it out again ++ return tda1004x_write_byteI(state, reg, val); ++} ++ ++static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len) ++{ ++ int i; ++ int result; ++ ++ dprintk("%s: reg=0x%x, len=0x%x\n", __func__, reg, len); ++ ++ result = 0; ++ for (i = 0; i < len; i++) { ++ result = tda1004x_write_byteI(state, reg + i, buf[i]); ++ if (result != 0) ++ break; ++ } ++ ++ return result; ++} ++ ++static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state) ++{ ++ int result; ++ dprintk("%s\n", __func__); ++ ++ result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2); ++ msleep(20); ++ return result; ++} ++ ++static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state) ++{ ++ dprintk("%s\n", __func__); ++ ++ return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0); ++} ++ ++static int tda10045h_set_bandwidth(struct tda1004x_state *state, ++ u32 bandwidth) ++{ ++ static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f }; ++ static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb }; ++ static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 }; ++ ++ switch (bandwidth) { ++ case 6000000: ++ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); ++ break; ++ ++ case 7000000: ++ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); ++ break; ++ ++ case 8000000: ++ tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0); ++ ++ return 0; ++} ++ ++static int tda10046h_set_bandwidth(struct tda1004x_state *state, ++ u32 bandwidth) ++{ ++ static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 }; ++ static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f }; ++ static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d }; ++ ++ static u8 bandwidth_6mhz_48M[] = { 0x70, 0x02, 0x49, 0x24, 0x92 }; ++ static u8 bandwidth_7mhz_48M[] = { 0x60, 0x02, 0xaa, 0xaa, 0xab }; ++ static u8 bandwidth_8mhz_48M[] = { 0x54, 0x03, 0x0c, 0x30, 0xc3 }; ++ int tda10046_clk53m; ++ ++ if ((state->config->if_freq == TDA10046_FREQ_045) || ++ (state->config->if_freq == TDA10046_FREQ_052)) ++ tda10046_clk53m = 0; ++ else ++ tda10046_clk53m = 1; ++ switch (bandwidth) { ++ case 6000000: ++ if (tda10046_clk53m) ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_53M, ++ sizeof(bandwidth_6mhz_53M)); ++ else ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_48M, ++ sizeof(bandwidth_6mhz_48M)); ++ if (state->config->if_freq == TDA10046_FREQ_045) { ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xab); ++ } ++ break; ++ ++ case 7000000: ++ if (tda10046_clk53m) ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_53M, ++ sizeof(bandwidth_7mhz_53M)); ++ else ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_48M, ++ sizeof(bandwidth_7mhz_48M)); ++ if (state->config->if_freq == TDA10046_FREQ_045) { ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); ++ } ++ break; ++ ++ case 8000000: ++ if (tda10046_clk53m) ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_53M, ++ sizeof(bandwidth_8mhz_53M)); ++ else ++ tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_48M, ++ sizeof(bandwidth_8mhz_48M)); ++ if (state->config->if_freq == TDA10046_FREQ_045) { ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x55); ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int tda1004x_do_upload(struct tda1004x_state *state, ++ const unsigned char *mem, unsigned int len, ++ u8 dspCodeCounterReg, u8 dspCodeInReg) ++{ ++ u8 buf[65]; ++ struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 }; ++ int tx_size; ++ int pos = 0; ++ ++ /* clear code counter */ ++ tda1004x_write_byteI(state, dspCodeCounterReg, 0); ++ fw_msg.addr = state->config->demod_address; ++ ++ i2c_lock_adapter(state->i2c); ++ buf[0] = dspCodeInReg; ++ while (pos != len) { ++ // work out how much to send this time ++ tx_size = len - pos; ++ if (tx_size > 0x10) ++ tx_size = 0x10; ++ ++ // send the chunk ++ memcpy(buf + 1, mem + pos, tx_size); ++ fw_msg.len = tx_size + 1; ++ if (__i2c_transfer(state->i2c, &fw_msg, 1) != 1) { ++ printk(KERN_ERR "tda1004x: Error during firmware upload\n"); ++ i2c_unlock_adapter(state->i2c); ++ return -EIO; ++ } ++ pos += tx_size; ++ ++ dprintk("%s: fw_pos=0x%x\n", __func__, pos); ++ } ++ i2c_unlock_adapter(state->i2c); ++ ++ /* give the DSP a chance to settle 03/10/05 Hac */ ++ msleep(100); ++ ++ return 0; ++} ++ ++static int tda1004x_check_upload_ok(struct tda1004x_state *state) ++{ ++ u8 data1, data2; ++ unsigned long timeout; ++ ++ if (state->demod_type == TDA1004X_DEMOD_TDA10046) { ++ timeout = jiffies + 2 * HZ; ++ while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) { ++ if (time_after(jiffies, timeout)) { ++ printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n"); ++ break; ++ } ++ msleep(1); ++ } ++ } else ++ msleep(100); ++ ++ // check upload was OK ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP ++ tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67); ++ ++ data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1); ++ data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2); ++ if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) { ++ printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2); ++ return -EIO; ++ } ++ printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2); ++ return 0; ++} ++ ++static int tda10045_fwupload(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int ret; ++ const struct firmware *fw; ++ ++ /* don't re-upload unless necessary */ ++ if (tda1004x_check_upload_ok(state) == 0) ++ return 0; ++ ++ /* request the firmware, this will block until someone uploads it */ ++ printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE); ++ ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); ++ if (ret) { ++ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); ++ return ret; ++ } ++ ++ /* reset chip */ ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); ++ msleep(10); ++ ++ /* set parameters */ ++ tda10045h_set_bandwidth(state, 8000000); ++ ++ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN); ++ release_firmware(fw); ++ if (ret) ++ return ret; ++ printk(KERN_INFO "tda1004x: firmware upload complete\n"); ++ ++ /* wait for DSP to initialise */ ++ /* DSPREADY doesn't seem to work on the TDA10045H */ ++ msleep(100); ++ ++ return tda1004x_check_upload_ok(state); ++} ++ ++static void tda10046_init_plls(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tda10046_clk53m; ++ ++ if ((state->config->if_freq == TDA10046_FREQ_045) || ++ (state->config->if_freq == TDA10046_FREQ_052)) ++ tda10046_clk53m = 0; ++ else ++ tda10046_clk53m = 1; ++ ++ tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0); ++ if(tda10046_clk53m) { ++ printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n"); ++ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8 ++ } else { ++ printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n"); ++ tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3 ++ } ++ if (state->config->xtal_freq == TDA10046_XTAL_4M ) { ++ dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __func__); ++ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 ++ } else { ++ dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __func__); ++ tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3 ++ } ++ if(tda10046_clk53m) ++ tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x67); ++ else ++ tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x72); ++ /* Note clock frequency is handled implicitly */ ++ switch (state->config->if_freq) { ++ case TDA10046_FREQ_045: ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); ++ break; ++ case TDA10046_FREQ_052: ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xc7); ++ break; ++ case TDA10046_FREQ_3617: ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x59); ++ break; ++ case TDA10046_FREQ_3613: ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); ++ tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x3f); ++ break; ++ } ++ tda10046h_set_bandwidth(state, 8000000); /* default bandwidth 8 MHz */ ++ /* let the PLLs settle */ ++ msleep(120); ++} ++ ++static int tda10046_fwupload(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int ret, confc4; ++ const struct firmware *fw; ++ ++ /* reset + wake up chip */ ++ if (state->config->xtal_freq == TDA10046_XTAL_4M) { ++ confc4 = 0; ++ } else { ++ dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__); ++ confc4 = 0x80; ++ } ++ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); ++ ++ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); ++ /* set GPIO 1 and 3 */ ++ if (state->config->gpio_config != TDA10046_GPTRI) { ++ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33); ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f); ++ } ++ /* let the clocks recover from sleep */ ++ msleep(10); ++ ++ /* The PLLs need to be reprogrammed after sleep */ ++ tda10046_init_plls(fe); ++ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0); ++ ++ /* don't re-upload unless necessary */ ++ if (tda1004x_check_upload_ok(state) == 0) ++ return 0; ++ ++ /* ++ For i2c normal work, we need to slow down the bus speed. ++ However, the slow down breaks the eeprom firmware load. ++ So, use normal speed for eeprom booting and then restore the ++ i2c speed after that. Tested with MSI TV @nyware A/D board, ++ that comes with firmware version 29 inside their eeprom. ++ ++ It should also be noticed that no other I2C transfer should ++ be in course while booting from eeprom, otherwise, tda10046 ++ goes into an instable state. So, proper locking are needed ++ at the i2c bus master. ++ */ ++ printk(KERN_INFO "tda1004x: trying to boot from eeprom\n"); ++ tda1004x_write_byteI(state, TDA1004X_CONFC4, 4); ++ msleep(300); ++ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); ++ ++ /* Checks if eeprom firmware went without troubles */ ++ if (tda1004x_check_upload_ok(state) == 0) ++ return 0; ++ ++ /* eeprom firmware didn't work. Load one manually. */ ++ ++ if (state->config->request_firmware != NULL) { ++ /* request the firmware, this will block until someone uploads it */ ++ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n"); ++ ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); ++ if (ret) { ++ /* remain compatible to old bug: try to load with tda10045 image name */ ++ ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); ++ if (ret) { ++ printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); ++ return ret; ++ } else { ++ printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n", ++ TDA10046_DEFAULT_FIRMWARE); ++ } ++ } ++ } else { ++ printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n"); ++ return -EIO; ++ } ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST ++ ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); ++ release_firmware(fw); ++ return tda1004x_check_upload_ok(state); ++} ++ ++static int tda1004x_encode_fec(int fec) ++{ ++ // convert known FEC values ++ switch (fec) { ++ case FEC_1_2: ++ return 0; ++ case FEC_2_3: ++ return 1; ++ case FEC_3_4: ++ return 2; ++ case FEC_5_6: ++ return 3; ++ case FEC_7_8: ++ return 4; ++ } ++ ++ // unsupported ++ return -EINVAL; ++} ++ ++static int tda1004x_decode_fec(int tdafec) ++{ ++ // convert known FEC values ++ switch (tdafec) { ++ case 0: ++ return FEC_1_2; ++ case 1: ++ return FEC_2_3; ++ case 2: ++ return FEC_3_4; ++ case 3: ++ return FEC_5_6; ++ case 4: ++ return FEC_7_8; ++ } ++ ++ // unsupported ++ return -1; ++} ++ ++static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ ++ if (len != 2) ++ return -EINVAL; ++ ++ return tda1004x_write_byteI(state, buf[0], buf[1]); ++} ++ ++static int tda10045_init(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ if (tda10045_fwupload(fe)) { ++ printk("tda1004x: firmware upload failed\n"); ++ return -EIO; ++ } ++ ++ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC ++ ++ // tda setup ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer ++ tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream ++ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal ++ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer ++ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset ++ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset ++ tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface ++ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface ++ tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits ++ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity ++ tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e); ++ ++ tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); ++ ++ return 0; ++} ++ ++static int tda10046_init(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ dprintk("%s\n", __func__); ++ ++ if (tda10046_fwupload(fe)) { ++ printk("tda1004x: firmware upload failed\n"); ++ return -EIO; ++ } ++ ++ // tda setup ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer ++ tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream ++ tda1004x_write_byteI(state, TDA1004X_CONFC1, 0x88); // enable pulse killer ++ ++ switch (state->config->agc_config) { ++ case TDA10046_AGC_DEFAULT: ++ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities ++ break; ++ case TDA10046_AGC_IFO_AUTO_NEG: ++ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities ++ break; ++ case TDA10046_AGC_IFO_AUTO_POS: ++ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities ++ break; ++ case TDA10046_AGC_TDA827X: ++ tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup ++ tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold ++ tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities ++ break; ++ } ++ if (state->config->ts_mode == 0) { ++ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40); ++ tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); ++ } else { ++ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80); ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10, ++ state->config->invert_oclk << 4); ++ } ++ tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); ++ tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on ++ tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } ++ tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values ++ tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } ++ tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // } ++ tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 0x12); // IF gain 2, TUN gain 1 ++ tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits ++ tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config ++ tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config ++ // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes ++ ++ return 0; ++} ++ ++static int tda1004x_set_fe(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tmp; ++ int inversion; ++ ++ dprintk("%s\n", __func__); ++ ++ if (state->demod_type == TDA1004X_DEMOD_TDA10046) { ++ // setup auto offset ++ tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0); ++ ++ // disable agc_conf[2] ++ tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0); ++ } ++ ++ // set frequency ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ // Hardcoded to use auto as much as possible on the TDA10045 as it ++ // is very unreliable if AUTO mode is _not_ used. ++ if (state->demod_type == TDA1004X_DEMOD_TDA10045) { ++ fe_params->code_rate_HP = FEC_AUTO; ++ fe_params->guard_interval = GUARD_INTERVAL_AUTO; ++ fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; ++ } ++ ++ // Set standard params.. or put them to auto ++ if ((fe_params->code_rate_HP == FEC_AUTO) || ++ (fe_params->code_rate_LP == FEC_AUTO) || ++ (fe_params->modulation == QAM_AUTO) || ++ (fe_params->hierarchy == HIERARCHY_AUTO)) { ++ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); /* turn off modulation bits */ ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits ++ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits ++ } else { ++ tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto ++ ++ // set HP FEC ++ tmp = tda1004x_encode_fec(fe_params->code_rate_HP); ++ if (tmp < 0) ++ return tmp; ++ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp); ++ ++ // set LP FEC ++ tmp = tda1004x_encode_fec(fe_params->code_rate_LP); ++ if (tmp < 0) ++ return tmp; ++ tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3); ++ ++ /* set modulation */ ++ switch (fe_params->modulation) { ++ case QPSK: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0); ++ break; ++ ++ case QAM_16: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1); ++ break; ++ ++ case QAM_64: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // set hierarchy ++ switch (fe_params->hierarchy) { ++ case HIERARCHY_NONE: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5); ++ break; ++ ++ case HIERARCHY_1: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5); ++ break; ++ ++ case HIERARCHY_2: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5); ++ break; ++ ++ case HIERARCHY_4: ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ // set bandwidth ++ switch (state->demod_type) { ++ case TDA1004X_DEMOD_TDA10045: ++ tda10045h_set_bandwidth(state, fe_params->bandwidth_hz); ++ break; ++ ++ case TDA1004X_DEMOD_TDA10046: ++ tda10046h_set_bandwidth(state, fe_params->bandwidth_hz); ++ break; ++ } ++ ++ // set inversion ++ inversion = fe_params->inversion; ++ if (state->config->invert) ++ inversion = inversion ? INVERSION_OFF : INVERSION_ON; ++ switch (inversion) { ++ case INVERSION_OFF: ++ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0); ++ break; ++ ++ case INVERSION_ON: ++ tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // set guard interval ++ switch (fe_params->guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); ++ break; ++ ++ case GUARD_INTERVAL_1_16: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); ++ break; ++ ++ case GUARD_INTERVAL_1_8: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); ++ break; ++ ++ case GUARD_INTERVAL_1_4: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); ++ break; ++ ++ case GUARD_INTERVAL_AUTO: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // set transmission mode ++ switch (fe_params->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4); ++ break; ++ ++ case TRANSMISSION_MODE_8K: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4); ++ break; ++ ++ case TRANSMISSION_MODE_AUTO: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4); ++ tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // start the lock ++ switch (state->demod_type) { ++ case TDA1004X_DEMOD_TDA10045: ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); ++ break; ++ ++ case TDA1004X_DEMOD_TDA10046: ++ tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40); ++ msleep(1); ++ tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 1); ++ break; ++ } ++ ++ msleep(10); ++ ++ return 0; ++} ++ ++static int tda1004x_get_fe(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; ++ struct tda1004x_state* state = fe->demodulator_priv; ++ ++ dprintk("%s\n", __func__); ++ ++ // inversion status ++ fe_params->inversion = INVERSION_OFF; ++ if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) ++ fe_params->inversion = INVERSION_ON; ++ if (state->config->invert) ++ fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; ++ ++ // bandwidth ++ switch (state->demod_type) { ++ case TDA1004X_DEMOD_TDA10045: ++ switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) { ++ case 0x14: ++ fe_params->bandwidth_hz = 8000000; ++ break; ++ case 0xdb: ++ fe_params->bandwidth_hz = 7000000; ++ break; ++ case 0x4f: ++ fe_params->bandwidth_hz = 6000000; ++ break; ++ } ++ break; ++ case TDA1004X_DEMOD_TDA10046: ++ switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) { ++ case 0x5c: ++ case 0x54: ++ fe_params->bandwidth_hz = 8000000; ++ break; ++ case 0x6a: ++ case 0x60: ++ fe_params->bandwidth_hz = 7000000; ++ break; ++ case 0x7b: ++ case 0x70: ++ fe_params->bandwidth_hz = 6000000; ++ break; ++ } ++ break; ++ } ++ ++ // FEC ++ fe_params->code_rate_HP = ++ tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7); ++ fe_params->code_rate_LP = ++ tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7); ++ ++ /* modulation */ ++ switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) { ++ case 0: ++ fe_params->modulation = QPSK; ++ break; ++ case 1: ++ fe_params->modulation = QAM_16; ++ break; ++ case 2: ++ fe_params->modulation = QAM_64; ++ break; ++ } ++ ++ // transmission mode ++ fe_params->transmission_mode = TRANSMISSION_MODE_2K; ++ if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) ++ fe_params->transmission_mode = TRANSMISSION_MODE_8K; ++ ++ // guard interval ++ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { ++ case 0: ++ fe_params->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ fe_params->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ fe_params->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ fe_params->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ } ++ ++ // hierarchy ++ switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { ++ case 0: ++ fe_params->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ fe_params->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ fe_params->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ fe_params->hierarchy = HIERARCHY_4; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int status; ++ int cber; ++ int vber; ++ ++ dprintk("%s\n", __func__); ++ ++ // read status ++ status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); ++ if (status == -1) ++ return -EIO; ++ ++ // decode ++ *fe_status = 0; ++ if (status & 4) ++ *fe_status |= FE_HAS_SIGNAL; ++ if (status & 2) ++ *fe_status |= FE_HAS_CARRIER; ++ if (status & 8) ++ *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi ++ // is getting anything valid ++ if (!(*fe_status & FE_HAS_VITERBI)) { ++ // read the CBER ++ cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB); ++ if (cber == -1) ++ return -EIO; ++ status = tda1004x_read_byte(state, TDA1004X_CBER_MSB); ++ if (status == -1) ++ return -EIO; ++ cber |= (status << 8); ++ // The address 0x20 should be read to cope with a TDA10046 bug ++ tda1004x_read_byte(state, TDA1004X_CBER_RESET); ++ ++ if (cber != 65535) ++ *fe_status |= FE_HAS_VITERBI; ++ } ++ ++ // if we DO have some valid VITERBI output, but don't already have SYNC ++ // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. ++ if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { ++ // read the VBER ++ vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB); ++ if (vber == -1) ++ return -EIO; ++ status = tda1004x_read_byte(state, TDA1004X_VBER_MID); ++ if (status == -1) ++ return -EIO; ++ vber |= (status << 8); ++ status = tda1004x_read_byte(state, TDA1004X_VBER_MSB); ++ if (status == -1) ++ return -EIO; ++ vber |= (status & 0x0f) << 16; ++ // The CVBER_LUT should be read to cope with TDA10046 hardware bug ++ tda1004x_read_byte(state, TDA1004X_CVBER_LUT); ++ ++ // if RS has passed some valid TS packets, then we must be ++ // getting some SYNC bytes ++ if (vber < 16632) ++ *fe_status |= FE_HAS_SYNC; ++ } ++ ++ // success ++ dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); ++ return 0; ++} ++ ++static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tmp; ++ int reg = 0; ++ ++ dprintk("%s\n", __func__); ++ ++ // determine the register to use ++ switch (state->demod_type) { ++ case TDA1004X_DEMOD_TDA10045: ++ reg = TDA10045H_S_AGC; ++ break; ++ ++ case TDA1004X_DEMOD_TDA10046: ++ reg = TDA10046H_AGC_IF_LEVEL; ++ break; ++ } ++ ++ // read it ++ tmp = tda1004x_read_byte(state, reg); ++ if (tmp < 0) ++ return -EIO; ++ ++ *signal = (tmp << 8) | tmp; ++ dprintk("%s: signal=0x%x\n", __func__, *signal); ++ return 0; ++} ++ ++static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tmp; ++ ++ dprintk("%s\n", __func__); ++ ++ // read it ++ tmp = tda1004x_read_byte(state, TDA1004X_SNR); ++ if (tmp < 0) ++ return -EIO; ++ tmp = 255 - tmp; ++ ++ *snr = ((tmp << 8) | tmp); ++ dprintk("%s: snr=0x%x\n", __func__, *snr); ++ return 0; ++} ++ ++static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tmp; ++ int tmp2; ++ int counter; ++ ++ dprintk("%s\n", __func__); ++ ++ // read the UCBLOCKS and reset ++ counter = 0; ++ tmp = tda1004x_read_byte(state, TDA1004X_UNCOR); ++ if (tmp < 0) ++ return -EIO; ++ tmp &= 0x7f; ++ while (counter++ < 5) { ++ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); ++ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); ++ tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); ++ ++ tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR); ++ if (tmp2 < 0) ++ return -EIO; ++ tmp2 &= 0x7f; ++ if ((tmp2 < tmp) || (tmp2 == 0)) ++ break; ++ } ++ ++ if (tmp != 0x7f) ++ *ucblocks = tmp; ++ else ++ *ucblocks = 0xffffffff; ++ ++ dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); ++ return 0; ++} ++ ++static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int tmp; ++ ++ dprintk("%s\n", __func__); ++ ++ // read it in ++ tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB); ++ if (tmp < 0) ++ return -EIO; ++ *ber = tmp << 1; ++ tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB); ++ if (tmp < 0) ++ return -EIO; ++ *ber |= (tmp << 9); ++ // The address 0x20 should be read to cope with a TDA10046 bug ++ tda1004x_read_byte(state, TDA1004X_CBER_RESET); ++ ++ dprintk("%s: ber=0x%x\n", __func__, *ber); ++ return 0; ++} ++ ++static int tda1004x_sleep(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ int gpio_conf; ++ ++ switch (state->demod_type) { ++ case TDA1004X_DEMOD_TDA10045: ++ tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10); ++ break; ++ ++ case TDA1004X_DEMOD_TDA10046: ++ /* set outputs to tristate */ ++ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); ++ /* invert GPIO 1 and 3 if desired*/ ++ gpio_conf = state->config->gpio_config; ++ if (gpio_conf >= TDA10046_GP00_I) ++ tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, ++ (gpio_conf & 0x0f) ^ 0x0a); ++ ++ tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0); ++ tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct tda1004x_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return tda1004x_enable_tuner_i2c(state); ++ } else { ++ return tda1004x_disable_tuner_i2c(state); ++ } ++} ++ ++static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ fesettings->min_delay_ms = 800; ++ /* Drift compensation makes no sense for DVB-T */ ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void tda1004x_release(struct dvb_frontend* fe) ++{ ++ struct tda1004x_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops tda10045_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Philips TDA10045H DVB-T", ++ .frequency_min = 51000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166667, ++ .caps = ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO ++ }, ++ ++ .release = tda1004x_release, ++ ++ .init = tda10045_init, ++ .sleep = tda1004x_sleep, ++ .write = tda1004x_write, ++ .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, ++ ++ .set_frontend = tda1004x_set_fe, ++ .get_frontend = tda1004x_get_fe, ++ .get_tune_settings = tda1004x_get_tune_settings, ++ ++ .read_status = tda1004x_read_status, ++ .read_ber = tda1004x_read_ber, ++ .read_signal_strength = tda1004x_read_signal_strength, ++ .read_snr = tda1004x_read_snr, ++ .read_ucblocks = tda1004x_read_ucblocks, ++}; ++ ++struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct tda1004x_state *state; ++ int id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); ++ if (!state) { ++ printk(KERN_ERR "Can't allocate memory for tda10045 state\n"); ++ return NULL; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->demod_type = TDA1004X_DEMOD_TDA10045; ++ ++ /* check if the demod is there */ ++ id = tda1004x_read_byte(state, TDA1004X_CHIPID); ++ if (id < 0) { ++ printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); ++ kfree(state); ++ return NULL; ++ } ++ ++ if (id != 0x25) { ++ printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); ++ kfree(state); ++ return NULL; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++static struct dvb_frontend_ops tda10046_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Philips TDA10046H DVB-T", ++ .frequency_min = 51000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166667, ++ .caps = ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO ++ }, ++ ++ .release = tda1004x_release, ++ ++ .init = tda10046_init, ++ .sleep = tda1004x_sleep, ++ .write = tda1004x_write, ++ .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, ++ ++ .set_frontend = tda1004x_set_fe, ++ .get_frontend = tda1004x_get_fe, ++ .get_tune_settings = tda1004x_get_tune_settings, ++ ++ .read_status = tda1004x_read_status, ++ .read_ber = tda1004x_read_ber, ++ .read_signal_strength = tda1004x_read_signal_strength, ++ .read_snr = tda1004x_read_snr, ++ .read_ucblocks = tda1004x_read_ucblocks, ++}; ++ ++struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct tda1004x_state *state; ++ int id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); ++ if (!state) { ++ printk(KERN_ERR "Can't allocate memory for tda10046 state\n"); ++ return NULL; ++ } ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->demod_type = TDA1004X_DEMOD_TDA10046; ++ ++ /* check if the demod is there */ ++ id = tda1004x_read_byte(state, TDA1004X_CHIPID); ++ if (id < 0) { ++ printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); ++ kfree(state); ++ return NULL; ++ } ++ if (id != 0x46) { ++ printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); ++ kfree(state); ++ return NULL; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); ++MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(tda10045_attach); ++EXPORT_SYMBOL(tda10046_attach); +diff --git a/drivers/media/dvb-frontends/tda1004x.h b/drivers/media/dvb-frontends/tda1004x.h +new file mode 100644 +index 0000000..4e27ffb +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda1004x.h +@@ -0,0 +1,149 @@ ++ /* ++ Driver for Philips tda1004xh OFDM Frontend ++ ++ (c) 2004 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#ifndef TDA1004X_H ++#define TDA1004X_H ++ ++#include ++#include ++ ++enum tda10046_xtal { ++ TDA10046_XTAL_4M, ++ TDA10046_XTAL_16M, ++}; ++ ++enum tda10046_agc { ++ TDA10046_AGC_DEFAULT, /* original configuration */ ++ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ ++ TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ ++ TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ ++}; ++ ++/* Many (hybrid) boards use GPIO 1 and 3 ++ GPIO1 analog - dvb switch ++ GPIO3 firmware eeprom address switch ++*/ ++enum tda10046_gpio { ++ TDA10046_GPTRI = 0x00, /* All GPIOs tristate */ ++ TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */ ++ TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */ ++ TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */ ++ TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */ ++ TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/ ++ TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */ ++ TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */ ++ TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */ ++}; ++ ++enum tda10046_if { ++ TDA10046_FREQ_3617, /* original config, 36,166 MHZ */ ++ TDA10046_FREQ_3613, /* 36,13 MHZ */ ++ TDA10046_FREQ_045, /* low IF, 4.0, 4.5, or 5.0 MHZ */ ++ TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */ ++}; ++ ++enum tda10046_tsout { ++ TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */ ++ TDA10046_TS_SERIAL = 0x01, /* serial transport stream */ ++}; ++ ++struct tda1004x_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* does the "inversion" need inverted? */ ++ u8 invert; ++ ++ /* Does the OCLK signal need inverted? */ ++ u8 invert_oclk; ++ ++ /* parallel or serial transport stream */ ++ enum tda10046_tsout ts_mode; ++ ++ /* Xtal frequency, 4 or 16MHz*/ ++ enum tda10046_xtal xtal_freq; ++ ++ /* IF frequency */ ++ enum tda10046_if if_freq; ++ ++ /* AGC configuration */ ++ enum tda10046_agc agc_config; ++ ++ /* setting of GPIO1 and 3 */ ++ enum tda10046_gpio gpio_config; ++ ++ /* slave address and configuration of the tuner */ ++ u8 tuner_address; ++ u8 antenna_switch; ++ ++ /* if the board uses another I2c Bridge (tda8290), its address */ ++ u8 i2c_gate; ++ ++ /* request firmware for device */ ++ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); ++}; ++ ++enum tda1004x_demod { ++ TDA1004X_DEMOD_TDA10045, ++ TDA1004X_DEMOD_TDA10046, ++}; ++ ++struct tda1004x_state { ++ struct i2c_adapter* i2c; ++ const struct tda1004x_config* config; ++ struct dvb_frontend frontend; ++ ++ /* private demod data */ ++ enum tda1004x_demod demod_type; ++}; ++ ++#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c); ++ ++extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TDA1004X ++ ++static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { ++ int r = 0; ++ u8 buf[] = {reg, val}; ++ if (fe->ops.write) ++ r = fe->ops.write(fe, buf, 2); ++ return r; ++} ++ ++#endif // TDA1004X_H +diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c +new file mode 100644 +index 0000000..2521f7e +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10071.c +@@ -0,0 +1,1301 @@ ++/* ++ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "tda10071_priv.h" ++ ++static struct dvb_frontend_ops tda10071_ops; ++ ++/* write multiple registers */ ++static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, ++ int len) ++{ ++ int ret; ++ u8 buf[len+1]; ++ struct i2c_msg msg[1] = { ++ { ++ .addr = priv->cfg.demod_i2c_addr, ++ .flags = 0, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = reg; ++ memcpy(&buf[1], val, len); ++ ++ ret = i2c_transfer(priv->i2c, msg, 1); ++ if (ret == 1) { ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* read multiple registers */ ++static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, ++ int len) ++{ ++ int ret; ++ u8 buf[len]; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = priv->cfg.demod_i2c_addr, ++ .flags = 0, ++ .len = 1, ++ .buf = ®, ++ }, { ++ .addr = priv->cfg.demod_i2c_addr, ++ .flags = I2C_M_RD, ++ .len = sizeof(buf), ++ .buf = buf, ++ } ++ }; ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ if (ret == 2) { ++ memcpy(val, buf, len); ++ ret = 0; ++ } else { ++ dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ ++ "len=%d\n", KBUILD_MODNAME, ret, reg, len); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* write single register */ ++static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val) ++{ ++ return tda10071_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register */ ++static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val) ++{ ++ return tda10071_rd_regs(priv, reg, val, 1); ++} ++ ++/* write single register with mask */ ++static int tda10071_wr_reg_mask(struct tda10071_priv *priv, ++ u8 reg, u8 val, u8 mask) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = tda10071_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return tda10071_wr_regs(priv, reg, &val, 1); ++} ++ ++/* read single register with mask */ ++static int tda10071_rd_reg_mask(struct tda10071_priv *priv, ++ u8 reg, u8 *val, u8 mask) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ ret = tda10071_rd_regs(priv, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ tmp &= mask; ++ ++ /* find position of the first bit */ ++ for (i = 0; i < 8; i++) { ++ if ((mask >> i) & 0x01) ++ break; ++ } ++ *val = tmp >> i; ++ ++ return 0; ++} ++ ++/* execute firmware command */ ++static int tda10071_cmd_execute(struct tda10071_priv *priv, ++ struct tda10071_cmd *cmd) ++{ ++ int ret, i; ++ u8 tmp; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ /* write cmd and args for firmware */ ++ ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len); ++ if (ret) ++ goto error; ++ ++ /* start cmd execution */ ++ ret = tda10071_wr_reg(priv, 0x1f, 1); ++ if (ret) ++ goto error; ++ ++ /* wait cmd execution terminate */ ++ for (i = 1000, tmp = 1; i && tmp; i--) { ++ ret = tda10071_rd_reg(priv, 0x1f, &tmp); ++ if (ret) ++ goto error; ++ ++ usleep_range(200, 5000); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) { ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_set_tone(struct dvb_frontend *fe, ++ fe_sec_tone_mode_t fe_sec_tone_mode) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret; ++ u8 tone; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: tone_mode=%d\n", __func__, ++ fe_sec_tone_mode); ++ ++ switch (fe_sec_tone_mode) { ++ case SEC_TONE_ON: ++ tone = 1; ++ break; ++ case SEC_TONE_OFF: ++ tone = 0; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_tone_mode\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ cmd.args[0] = CMD_LNB_PCB_CONFIG; ++ cmd.args[1] = 0; ++ cmd.args[2] = 0x00; ++ cmd.args[3] = 0x00; ++ cmd.args[4] = tone; ++ cmd.len = 5; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t fe_sec_voltage) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret; ++ u8 voltage; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: voltage=%d\n", __func__, fe_sec_voltage); ++ ++ switch (fe_sec_voltage) { ++ case SEC_VOLTAGE_13: ++ voltage = 0; ++ break; ++ case SEC_VOLTAGE_18: ++ voltage = 1; ++ break; ++ case SEC_VOLTAGE_OFF: ++ voltage = 0; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_voltage\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ cmd.args[0] = CMD_LNB_SET_DC_LEVEL; ++ cmd.args[1] = 0; ++ cmd.args[2] = voltage; ++ cmd.len = 3; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *diseqc_cmd) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i; ++ u8 tmp; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: msg_len=%d\n", __func__, ++ diseqc_cmd->msg_len); ++ ++ if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 6) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* wait LNB TX */ ++ for (i = 500, tmp = 0; i && !tmp; i--) { ++ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); ++ if (ret) ++ goto error; ++ ++ usleep_range(10000, 20000); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) { ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_LNB_SEND_DISEQC; ++ cmd.args[1] = 0; ++ cmd.args[2] = 0; ++ cmd.args[3] = 0; ++ cmd.args[4] = 2; ++ cmd.args[5] = 0; ++ cmd.args[6] = diseqc_cmd->msg_len; ++ memcpy(&cmd.args[7], diseqc_cmd->msg, diseqc_cmd->msg_len); ++ cmd.len = 7 + diseqc_cmd->msg_len; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, ++ struct dvb_diseqc_slave_reply *reply) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i; ++ u8 tmp; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s:\n", __func__); ++ ++ /* wait LNB RX */ ++ for (i = 500, tmp = 0; i && !tmp; i--) { ++ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02); ++ if (ret) ++ goto error; ++ ++ usleep_range(10000, 20000); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) { ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ /* reply len */ ++ ret = tda10071_rd_reg(priv, 0x46, &tmp); ++ if (ret) ++ goto error; ++ ++ reply->msg_len = tmp & 0x1f; /* [4:0] */ ++ if (reply->msg_len > sizeof(reply->msg)) ++ reply->msg_len = sizeof(reply->msg); /* truncate API max */ ++ ++ /* read reply */ ++ cmd.args[0] = CMD_LNB_UPDATE_REPLY; ++ cmd.args[1] = 0; ++ cmd.len = 2; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t fe_sec_mini_cmd) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i; ++ u8 tmp, burst; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: fe_sec_mini_cmd=%d\n", __func__, ++ fe_sec_mini_cmd); ++ ++ switch (fe_sec_mini_cmd) { ++ case SEC_MINI_A: ++ burst = 0; ++ break; ++ case SEC_MINI_B: ++ burst = 1; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid fe_sec_mini_cmd\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* wait LNB TX */ ++ for (i = 500, tmp = 0; i && !tmp; i--) { ++ ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); ++ if (ret) ++ goto error; ++ ++ usleep_range(10000, 20000); ++ } ++ ++ dev_dbg(&priv->i2c->dev, "%s: loop=%d\n", __func__, i); ++ ++ if (i == 0) { ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_LNB_SEND_TONEBURST; ++ cmd.args[1] = 0; ++ cmd.args[2] = burst; ++ cmd.len = 3; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 tmp; ++ ++ *status = 0; ++ ++ if (!priv->warm) { ++ ret = 0; ++ goto error; ++ } ++ ++ ret = tda10071_rd_reg(priv, 0x39, &tmp); ++ if (ret) ++ goto error; ++ ++ if (tmp & 0x01) /* tuner PLL */ ++ *status |= FE_HAS_SIGNAL; ++ if (tmp & 0x02) /* demod PLL */ ++ *status |= FE_HAS_CARRIER; ++ if (tmp & 0x04) /* viterbi or LDPC*/ ++ *status |= FE_HAS_VITERBI; ++ if (tmp & 0x08) /* RS or BCH */ ++ *status |= FE_HAS_SYNC | FE_HAS_LOCK; ++ ++ priv->fe_status = *status; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ int ret; ++ u8 buf[2]; ++ ++ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { ++ *snr = 0; ++ ret = 0; ++ goto error; ++ } ++ ++ ret = tda10071_rd_regs(priv, 0x3a, buf, 2); ++ if (ret) ++ goto error; ++ ++ /* Es/No dBx10 */ ++ *snr = buf[0] << 8 | buf[1]; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret; ++ u8 tmp; ++ ++ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { ++ *strength = 0; ++ ret = 0; ++ goto error; ++ } ++ ++ cmd.args[0] = CMD_GET_AGCACC; ++ cmd.args[1] = 0; ++ cmd.len = 2; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ /* input power estimate dBm */ ++ ret = tda10071_rd_reg(priv, 0x50, &tmp); ++ if (ret) ++ goto error; ++ ++ if (tmp < 181) ++ tmp = 181; /* -75 dBm */ ++ else if (tmp > 236) ++ tmp = 236; /* -20 dBm */ ++ ++ /* scale value to 0x0000-0xffff */ ++ *strength = (tmp-181) * 0xffff / (236-181); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i, len; ++ u8 tmp, reg, buf[8]; ++ ++ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { ++ *ber = priv->ber = 0; ++ ret = 0; ++ goto error; ++ } ++ ++ switch (priv->delivery_system) { ++ case SYS_DVBS: ++ reg = 0x4c; ++ len = 8; ++ i = 1; ++ break; ++ case SYS_DVBS2: ++ reg = 0x4d; ++ len = 4; ++ i = 0; ++ break; ++ default: ++ *ber = priv->ber = 0; ++ return 0; ++ } ++ ++ ret = tda10071_rd_reg(priv, reg, &tmp); ++ if (ret) ++ goto error; ++ ++ if (priv->meas_count[i] == tmp) { ++ dev_dbg(&priv->i2c->dev, "%s: meas not ready=%02x\n", __func__, ++ tmp); ++ *ber = priv->ber; ++ return 0; ++ } else { ++ priv->meas_count[i] = tmp; ++ } ++ ++ cmd.args[0] = CMD_BER_UPDATE_COUNTERS; ++ cmd.args[1] = 0; ++ cmd.args[2] = i; ++ cmd.len = 3; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_rd_regs(priv, cmd.len, buf, len); ++ if (ret) ++ goto error; ++ ++ if (priv->delivery_system == SYS_DVBS) { ++ *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; ++ priv->ucb += (buf[4] << 8) | buf[5]; ++ } else { ++ *ber = (buf[0] << 8) | buf[1]; ++ } ++ priv->ber = *ber; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ int ret = 0; ++ ++ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { ++ *ucblocks = 0; ++ goto error; ++ } ++ ++ /* UCB is updated when BER is read. Assume BER is read anyway. */ ++ ++ *ucblocks = priv->ucb; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_set_frontend(struct dvb_frontend *fe) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i; ++ u8 mode, rolloff, pilot, inversion, div; ++ ++ dev_dbg(&priv->i2c->dev, "%s: delivery_system=%d modulation=%d " \ ++ "frequency=%d symbol_rate=%d inversion=%d pilot=%d " \ ++ "rolloff=%d\n", __func__, c->delivery_system, c->modulation, ++ c->frequency, c->symbol_rate, c->inversion, c->pilot, ++ c->rolloff); ++ ++ priv->delivery_system = SYS_UNDEFINED; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ switch (c->inversion) { ++ case INVERSION_OFF: ++ inversion = 1; ++ break; ++ case INVERSION_ON: ++ inversion = 0; ++ break; ++ case INVERSION_AUTO: ++ /* 2 = auto; try first on then off ++ * 3 = auto; try first off then on */ ++ inversion = 3; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid inversion\n", __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ switch (c->delivery_system) { ++ case SYS_DVBS: ++ rolloff = 0; ++ pilot = 2; ++ break; ++ case SYS_DVBS2: ++ switch (c->rolloff) { ++ case ROLLOFF_20: ++ rolloff = 2; ++ break; ++ case ROLLOFF_25: ++ rolloff = 1; ++ break; ++ case ROLLOFF_35: ++ rolloff = 0; ++ break; ++ case ROLLOFF_AUTO: ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid rolloff\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ switch (c->pilot) { ++ case PILOT_OFF: ++ pilot = 0; ++ break; ++ case PILOT_ON: ++ pilot = 1; ++ break; ++ case PILOT_AUTO: ++ pilot = 2; ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid pilot\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ break; ++ default: ++ dev_dbg(&priv->i2c->dev, "%s: invalid delivery_system\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { ++ if (c->delivery_system == TDA10071_MODCOD[i].delivery_system && ++ c->modulation == TDA10071_MODCOD[i].modulation && ++ c->fec_inner == TDA10071_MODCOD[i].fec) { ++ mode = TDA10071_MODCOD[i].val; ++ dev_dbg(&priv->i2c->dev, "%s: mode found=%02x\n", ++ __func__, mode); ++ break; ++ } ++ } ++ ++ if (mode == 0xff) { ++ dev_dbg(&priv->i2c->dev, "%s: invalid parameter combination\n", ++ __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ if (c->symbol_rate <= 5000000) ++ div = 14; ++ else ++ div = 4; ++ ++ ret = tda10071_wr_reg(priv, 0x81, div); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_wr_reg(priv, 0xe3, div); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_CHANGE_CHANNEL; ++ cmd.args[1] = 0; ++ cmd.args[2] = mode; ++ cmd.args[3] = (c->frequency >> 16) & 0xff; ++ cmd.args[4] = (c->frequency >> 8) & 0xff; ++ cmd.args[5] = (c->frequency >> 0) & 0xff; ++ cmd.args[6] = ((c->symbol_rate / 1000) >> 8) & 0xff; ++ cmd.args[7] = ((c->symbol_rate / 1000) >> 0) & 0xff; ++ cmd.args[8] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; ++ cmd.args[9] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; ++ cmd.args[10] = rolloff; ++ cmd.args[11] = inversion; ++ cmd.args[12] = pilot; ++ cmd.args[13] = 0x00; ++ cmd.args[14] = 0x00; ++ cmd.len = 15; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ priv->delivery_system = c->delivery_system; ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_get_frontend(struct dvb_frontend *fe) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ int ret, i; ++ u8 buf[5], tmp; ++ ++ if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ ret = tda10071_rd_regs(priv, 0x30, buf, 5); ++ if (ret) ++ goto error; ++ ++ tmp = buf[0] & 0x3f; ++ for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { ++ if (tmp == TDA10071_MODCOD[i].val) { ++ c->modulation = TDA10071_MODCOD[i].modulation; ++ c->fec_inner = TDA10071_MODCOD[i].fec; ++ c->delivery_system = TDA10071_MODCOD[i].delivery_system; ++ } ++ } ++ ++ switch ((buf[1] >> 0) & 0x01) { ++ case 0: ++ c->inversion = INVERSION_OFF; ++ break; ++ case 1: ++ c->inversion = INVERSION_ON; ++ break; ++ } ++ ++ switch ((buf[1] >> 7) & 0x01) { ++ case 0: ++ c->pilot = PILOT_OFF; ++ break; ++ case 1: ++ c->pilot = PILOT_ON; ++ break; ++ } ++ ++ c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); ++ ++ ret = tda10071_rd_regs(priv, 0x52, buf, 3); ++ if (ret) ++ goto error; ++ ++ c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0); ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_init(struct dvb_frontend *fe) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i, len, remaining, fw_size; ++ const struct firmware *fw; ++ u8 *fw_file = TDA10071_FIRMWARE; ++ u8 tmp, buf[4]; ++ struct tda10071_reg_val_mask tab[] = { ++ { 0xcd, 0x00, 0x07 }, ++ { 0x80, 0x00, 0x02 }, ++ { 0xcd, 0x00, 0xc0 }, ++ { 0xce, 0x00, 0x1b }, ++ { 0x9d, 0x00, 0x01 }, ++ { 0x9d, 0x00, 0x02 }, ++ { 0x9e, 0x00, 0x01 }, ++ { 0x87, 0x00, 0x80 }, ++ { 0xce, 0x00, 0x08 }, ++ { 0xce, 0x00, 0x10 }, ++ }; ++ struct tda10071_reg_val_mask tab2[] = { ++ { 0xf1, 0x70, 0xff }, ++ { 0x88, priv->cfg.pll_multiplier, 0x3f }, ++ { 0x89, 0x00, 0x10 }, ++ { 0x89, 0x10, 0x10 }, ++ { 0xc0, 0x01, 0x01 }, ++ { 0xc0, 0x00, 0x01 }, ++ { 0xe0, 0xff, 0xff }, ++ { 0xe0, 0x00, 0xff }, ++ { 0x96, 0x1e, 0x7e }, ++ { 0x8b, 0x08, 0x08 }, ++ { 0x8b, 0x00, 0x08 }, ++ { 0x8f, 0x1a, 0x7e }, ++ { 0x8c, 0x68, 0xff }, ++ { 0x8d, 0x08, 0xff }, ++ { 0x8e, 0x4c, 0xff }, ++ { 0x8f, 0x01, 0x01 }, ++ { 0x8b, 0x04, 0x04 }, ++ { 0x8b, 0x00, 0x04 }, ++ { 0x87, 0x05, 0x07 }, ++ { 0x80, 0x00, 0x20 }, ++ { 0xc8, 0x01, 0xff }, ++ { 0xb4, 0x47, 0xff }, ++ { 0xb5, 0x9c, 0xff }, ++ { 0xb6, 0x7d, 0xff }, ++ { 0xba, 0x00, 0x03 }, ++ { 0xb7, 0x47, 0xff }, ++ { 0xb8, 0x9c, 0xff }, ++ { 0xb9, 0x7d, 0xff }, ++ { 0xba, 0x00, 0x0c }, ++ { 0xc8, 0x00, 0xff }, ++ { 0xcd, 0x00, 0x04 }, ++ { 0xcd, 0x00, 0x20 }, ++ { 0xe8, 0x02, 0xff }, ++ { 0xcf, 0x20, 0xff }, ++ { 0x9b, 0xd7, 0xff }, ++ { 0x9a, 0x01, 0x03 }, ++ { 0xa8, 0x05, 0x0f }, ++ { 0xa8, 0x65, 0xf0 }, ++ { 0xa6, 0xa0, 0xf0 }, ++ { 0x9d, 0x50, 0xfc }, ++ { 0x9e, 0x20, 0xe0 }, ++ { 0xa3, 0x1c, 0x7c }, ++ { 0xd5, 0x03, 0x03 }, ++ }; ++ ++ /* firmware status */ ++ ret = tda10071_rd_reg(priv, 0x51, &tmp); ++ if (ret) ++ goto error; ++ ++ if (!tmp) { ++ /* warm state - wake up device from sleep */ ++ priv->warm = 1; ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = tda10071_wr_reg_mask(priv, tab[i].reg, ++ tab[i].val, tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ ++ cmd.args[0] = CMD_SET_SLEEP_MODE; ++ cmd.args[1] = 0; ++ cmd.args[2] = 0; ++ cmd.len = 3; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ } else { ++ /* cold state - try to download firmware */ ++ priv->warm = 0; ++ ++ /* request the firmware, this will block and timeout */ ++ ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); ++ if (ret) { ++ dev_err(&priv->i2c->dev, "%s: did not find the " \ ++ "firmware file. (%s) Please see " \ ++ "linux/Documentation/dvb/ for more " \ ++ "details on firmware-problems. (%d)\n", ++ KBUILD_MODNAME, fw_file, ret); ++ goto error; ++ } ++ ++ /* init */ ++ for (i = 0; i < ARRAY_SIZE(tab2); i++) { ++ ret = tda10071_wr_reg_mask(priv, tab2[i].reg, ++ tab2[i].val, tab2[i].mask); ++ if (ret) ++ goto error_release_firmware; ++ } ++ ++ /* download firmware */ ++ ret = tda10071_wr_reg(priv, 0xe0, 0x7f); ++ if (ret) ++ goto error_release_firmware; ++ ++ ret = tda10071_wr_reg(priv, 0xf7, 0x81); ++ if (ret) ++ goto error_release_firmware; ++ ++ ret = tda10071_wr_reg(priv, 0xf8, 0x00); ++ if (ret) ++ goto error_release_firmware; ++ ++ ret = tda10071_wr_reg(priv, 0xf9, 0x00); ++ if (ret) ++ goto error_release_firmware; ++ ++ dev_info(&priv->i2c->dev, "%s: found a '%s' in cold state, " \ ++ "will try to load a firmware\n", KBUILD_MODNAME, ++ tda10071_ops.info.name); ++ dev_info(&priv->i2c->dev, "%s: downloading firmware from " \ ++ "file '%s'\n", KBUILD_MODNAME, fw_file); ++ ++ /* do not download last byte */ ++ fw_size = fw->size - 1; ++ ++ for (remaining = fw_size; remaining > 0; ++ remaining -= (priv->cfg.i2c_wr_max - 1)) { ++ len = remaining; ++ if (len > (priv->cfg.i2c_wr_max - 1)) ++ len = (priv->cfg.i2c_wr_max - 1); ++ ++ ret = tda10071_wr_regs(priv, 0xfa, ++ (u8 *) &fw->data[fw_size - remaining], len); ++ if (ret) { ++ dev_err(&priv->i2c->dev, "%s: firmware " \ ++ "download failed=%d\n", ++ KBUILD_MODNAME, ret); ++ if (ret) ++ goto error_release_firmware; ++ } ++ } ++ release_firmware(fw); ++ ++ ret = tda10071_wr_reg(priv, 0xf7, 0x0c); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_wr_reg(priv, 0xe0, 0x00); ++ if (ret) ++ goto error; ++ ++ /* wait firmware start */ ++ msleep(250); ++ ++ /* firmware status */ ++ ret = tda10071_rd_reg(priv, 0x51, &tmp); ++ if (ret) ++ goto error; ++ ++ if (tmp) { ++ dev_info(&priv->i2c->dev, "%s: firmware did not run\n", ++ KBUILD_MODNAME); ++ ret = -EFAULT; ++ goto error; ++ } else { ++ priv->warm = 1; ++ } ++ ++ cmd.args[0] = CMD_GET_FW_VERSION; ++ cmd.len = 1; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_rd_regs(priv, cmd.len, buf, 4); ++ if (ret) ++ goto error; ++ ++ dev_info(&priv->i2c->dev, "%s: firmware version %d.%d.%d.%d\n", ++ KBUILD_MODNAME, buf[0], buf[1], buf[2], buf[3]); ++ dev_info(&priv->i2c->dev, "%s: found a '%s' in warm state\n", ++ KBUILD_MODNAME, tda10071_ops.info.name); ++ ++ ret = tda10071_rd_regs(priv, 0x81, buf, 2); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_DEMOD_INIT; ++ cmd.args[1] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; ++ cmd.args[2] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; ++ cmd.args[3] = buf[0]; ++ cmd.args[4] = buf[1]; ++ cmd.args[5] = priv->cfg.pll_multiplier; ++ cmd.args[6] = priv->cfg.spec_inv; ++ cmd.args[7] = 0x00; ++ cmd.len = 8; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_TUNER_INIT; ++ cmd.args[1] = 0x00; ++ cmd.args[2] = 0x00; ++ cmd.args[3] = 0x00; ++ cmd.args[4] = 0x00; ++ cmd.args[5] = (priv->cfg.tuner_i2c_addr) ? priv->cfg.tuner_i2c_addr : 0x14; ++ cmd.args[6] = 0x00; ++ cmd.args[7] = 0x03; ++ cmd.args[8] = 0x02; ++ cmd.args[9] = 0x02; ++ cmd.args[10] = 0x00; ++ cmd.args[11] = 0x00; ++ cmd.args[12] = 0x00; ++ cmd.args[13] = 0x00; ++ cmd.args[14] = 0x00; ++ cmd.len = 15; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_MPEG_CONFIG; ++ cmd.args[1] = 0; ++ cmd.args[2] = priv->cfg.ts_mode; ++ cmd.args[3] = 0x00; ++ cmd.args[4] = 0x04; ++ cmd.args[5] = 0x00; ++ cmd.len = 6; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_LNB_CONFIG; ++ cmd.args[1] = 0; ++ cmd.args[2] = 150; ++ cmd.args[3] = 3; ++ cmd.args[4] = 22; ++ cmd.args[5] = 1; ++ cmd.args[6] = 1; ++ cmd.args[7] = 30; ++ cmd.args[8] = 30; ++ cmd.args[9] = 30; ++ cmd.args[10] = 30; ++ cmd.len = 11; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ cmd.args[0] = CMD_BER_CONTROL; ++ cmd.args[1] = 0; ++ cmd.args[2] = 14; ++ cmd.args[3] = 14; ++ cmd.len = 4; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error_release_firmware: ++ release_firmware(fw); ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_sleep(struct dvb_frontend *fe) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ struct tda10071_cmd cmd; ++ int ret, i; ++ struct tda10071_reg_val_mask tab[] = { ++ { 0xcd, 0x07, 0x07 }, ++ { 0x80, 0x02, 0x02 }, ++ { 0xcd, 0xc0, 0xc0 }, ++ { 0xce, 0x1b, 0x1b }, ++ { 0x9d, 0x01, 0x01 }, ++ { 0x9d, 0x02, 0x02 }, ++ { 0x9e, 0x01, 0x01 }, ++ { 0x87, 0x80, 0x80 }, ++ { 0xce, 0x08, 0x08 }, ++ { 0xce, 0x10, 0x10 }, ++ }; ++ ++ if (!priv->warm) { ++ ret = -EFAULT; ++ goto error; ++ } ++ ++ cmd.args[0] = CMD_SET_SLEEP_MODE; ++ cmd.args[1] = 0; ++ cmd.args[2] = 1; ++ cmd.len = 3; ++ ret = tda10071_cmd_execute(priv, &cmd); ++ if (ret) ++ goto error; ++ ++ for (i = 0; i < ARRAY_SIZE(tab); i++) { ++ ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val, ++ tab[i].mask); ++ if (ret) ++ goto error; ++ } ++ ++ return ret; ++error: ++ dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); ++ return ret; ++} ++ ++static int tda10071_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings *s) ++{ ++ s->min_delay_ms = 8000; ++ s->step_size = 0; ++ s->max_drift = 0; ++ ++ return 0; ++} ++ ++static void tda10071_release(struct dvb_frontend *fe) ++{ ++ struct tda10071_priv *priv = fe->demodulator_priv; ++ kfree(priv); ++} ++ ++struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, ++ struct i2c_adapter *i2c) ++{ ++ int ret; ++ struct tda10071_priv *priv = NULL; ++ u8 tmp; ++ ++ /* allocate memory for the internal priv */ ++ priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL); ++ if (priv == NULL) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ /* make sure demod i2c address is specified */ ++ if (!config->demod_i2c_addr) { ++ dev_dbg(&i2c->dev, "%s: invalid demod i2c address!\n", __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* make sure tuner i2c address is specified */ ++ if (!config->tuner_i2c_addr) { ++ dev_dbg(&i2c->dev, "%s: invalid tuner i2c address!\n", __func__); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* setup the priv */ ++ priv->i2c = i2c; ++ memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); ++ ++ /* chip ID */ ++ ret = tda10071_rd_reg(priv, 0xff, &tmp); ++ if (ret || tmp != 0x0f) ++ goto error; ++ ++ /* chip type */ ++ ret = tda10071_rd_reg(priv, 0xdd, &tmp); ++ if (ret || tmp != 0x00) ++ goto error; ++ ++ /* chip version */ ++ ret = tda10071_rd_reg(priv, 0xfe, &tmp); ++ if (ret || tmp != 0x01) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); ++ priv->fe.demodulator_priv = priv; ++ ++ return &priv->fe; ++error: ++ dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL(tda10071_attach); ++ ++static struct dvb_frontend_ops tda10071_ops = { ++ .delsys = { SYS_DVBS, SYS_DVBS2 }, ++ .info = { ++ .name = "NXP TDA10071", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_tolerance = 5000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | ++ FE_CAN_FEC_5_6 | ++ FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_8_9 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | ++ FE_CAN_RECOVER | ++ FE_CAN_2G_MODULATION ++ }, ++ ++ .release = tda10071_release, ++ ++ .get_tune_settings = tda10071_get_tune_settings, ++ ++ .init = tda10071_init, ++ .sleep = tda10071_sleep, ++ ++ .set_frontend = tda10071_set_frontend, ++ .get_frontend = tda10071_get_frontend, ++ ++ .read_status = tda10071_read_status, ++ .read_snr = tda10071_read_snr, ++ .read_signal_strength = tda10071_read_signal_strength, ++ .read_ber = tda10071_read_ber, ++ .read_ucblocks = tda10071_read_ucblocks, ++ ++ .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd, ++ .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply, ++ .diseqc_send_burst = tda10071_diseqc_send_burst, ++ ++ .set_tone = tda10071_set_tone, ++ .set_voltage = tda10071_set_voltage, ++}; ++ ++MODULE_AUTHOR("Antti Palosaari "); ++MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver"); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(TDA10071_FIRMWARE); +diff --git a/drivers/media/dvb-frontends/tda10071.h b/drivers/media/dvb-frontends/tda10071.h +new file mode 100644 +index 0000000..bff1c38 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10071.h +@@ -0,0 +1,87 @@ ++/* ++ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef TDA10071_H ++#define TDA10071_H ++ ++#include ++ ++struct tda10071_config { ++ /* Demodulator I2C address. ++ * Default: none, must set ++ * Values: 0x55, ++ */ ++ u8 demod_i2c_addr; ++ ++ /* Tuner I2C address. ++ * Default: none, must set ++ * Values: 0x14, 0x54, ... ++ */ ++ u8 tuner_i2c_addr; ++ ++ /* Max bytes I2C provider can write at once. ++ * Note: Buffer is taken from the stack currently! ++ * Default: none, must set ++ * Values: ++ */ ++ u16 i2c_wr_max; ++ ++ /* TS output mode. ++ * Default: TDA10071_TS_SERIAL ++ * Values: ++ */ ++#define TDA10071_TS_SERIAL 0 ++#define TDA10071_TS_PARALLEL 1 ++ u8 ts_mode; ++ ++ /* Input spectrum inversion. ++ * Default: 0 ++ * Values: 0, 1 ++ */ ++ bool spec_inv; ++ ++ /* Xtal frequency Hz ++ * Default: none, must set ++ * Values: ++ */ ++ u32 xtal; ++ ++ /* PLL multiplier. ++ * Default: none, must set ++ * Values: ++ */ ++ u8 pll_multiplier; ++}; ++ ++ ++#if defined(CONFIG_DVB_TDA10071) || \ ++ (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *tda10071_attach( ++ const struct tda10071_config *config, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *tda10071_attach( ++ const struct tda10071_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* TDA10071_H */ +diff --git a/drivers/media/dvb-frontends/tda10071_priv.h b/drivers/media/dvb-frontends/tda10071_priv.h +new file mode 100644 +index 0000000..4baf14b +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10071_priv.h +@@ -0,0 +1,109 @@ ++/* ++ * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver ++ * ++ * Copyright (C) 2011 Antti Palosaari ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef TDA10071_PRIV ++#define TDA10071_PRIV ++ ++#include "dvb_frontend.h" ++#include "tda10071.h" ++#include ++ ++struct tda10071_priv { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend fe; ++ struct tda10071_config cfg; ++ ++ u8 meas_count[2]; ++ u32 ber; ++ u32 ucb; ++ fe_status_t fe_status; ++ fe_delivery_system_t delivery_system; ++ bool warm; /* FW running */ ++}; ++ ++static struct tda10071_modcod { ++ fe_delivery_system_t delivery_system; ++ fe_modulation_t modulation; ++ fe_code_rate_t fec; ++ u8 val; ++} TDA10071_MODCOD[] = { ++ /* NBC-QPSK */ ++ { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 }, ++ { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, ++ { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, ++ { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, ++ { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, ++ { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, ++ { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, ++ { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, ++ { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, ++ /* 8PSK */ ++ { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, ++ { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, ++ { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, ++ { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, ++ { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, ++ { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, ++ /* QPSK */ ++ { SYS_DVBS, QPSK, FEC_AUTO, 0x2d }, ++ { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, ++ { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, ++ { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, ++ { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, ++ { SYS_DVBS, QPSK, FEC_7_8, 0x32 }, ++}; ++ ++struct tda10071_reg_val_mask { ++ u8 reg; ++ u8 val; ++ u8 mask; ++}; ++ ++/* firmware filename */ ++#define TDA10071_FIRMWARE "dvb-fe-tda10071.fw" ++ ++/* firmware commands */ ++#define CMD_DEMOD_INIT 0x10 ++#define CMD_CHANGE_CHANNEL 0x11 ++#define CMD_MPEG_CONFIG 0x13 ++#define CMD_TUNER_INIT 0x15 ++#define CMD_GET_AGCACC 0x1a ++ ++#define CMD_LNB_CONFIG 0x20 ++#define CMD_LNB_SEND_DISEQC 0x21 ++#define CMD_LNB_SET_DC_LEVEL 0x22 ++#define CMD_LNB_PCB_CONFIG 0x23 ++#define CMD_LNB_SEND_TONEBURST 0x24 ++#define CMD_LNB_UPDATE_REPLY 0x25 ++ ++#define CMD_GET_FW_VERSION 0x35 ++#define CMD_SET_SLEEP_MODE 0x36 ++#define CMD_BER_CONTROL 0x3e ++#define CMD_BER_UPDATE_COUNTERS 0x3f ++ ++/* firmare command struct */ ++#define TDA10071_ARGLEN 30 ++struct tda10071_cmd { ++ u8 args[TDA10071_ARGLEN]; ++ u8 len; ++}; ++ ++ ++#endif /* TDA10071_PRIV */ +diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c +new file mode 100644 +index 0000000..fcfe2e0 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10086.c +@@ -0,0 +1,777 @@ ++ /* ++ Driver for Philips tda10086 DVBS Demodulator ++ ++ (c) 2006 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda10086.h" ++ ++#define SACLK 96000000 ++ ++struct tda10086_state { ++ struct i2c_adapter* i2c; ++ const struct tda10086_config* config; ++ struct dvb_frontend frontend; ++ ++ /* private demod data */ ++ u32 frequency; ++ u32 symbol_rate; ++ bool has_lock; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "tda10086: " args); \ ++ } while (0) ++ ++static int tda10086_write_byte(struct tda10086_state *state, int reg, int data) ++{ ++ int ret; ++ u8 b0[] = { reg, data }; ++ struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 }; ++ ++ msg.addr = state->config->demod_address; ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", ++ __func__, reg, data, ret); ++ ++ return (ret != 1) ? ret : 0; ++} ++ ++static int tda10086_read_byte(struct tda10086_state *state, int reg) ++{ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, ++ { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; ++ ++ msg[0].addr = state->config->demod_address; ++ msg[1].addr = state->config->demod_address; ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ++ ret); ++ return ret; ++ } ++ ++ return b1[0]; ++} ++ ++static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data) ++{ ++ int val; ++ ++ /* read a byte and check */ ++ val = tda10086_read_byte(state, reg); ++ if (val < 0) ++ return val; ++ ++ /* mask if off */ ++ val = val & ~mask; ++ val |= data & 0xff; ++ ++ /* write it out again */ ++ return tda10086_write_byte(state, reg, val); ++} ++ ++static int tda10086_init(struct dvb_frontend* fe) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 t22k_off = 0x80; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (state->config->diseqc_tone) ++ t22k_off = 0; ++ /* reset */ ++ tda10086_write_byte(state, 0x00, 0x00); ++ msleep(10); ++ ++ /* misc setup */ ++ tda10086_write_byte(state, 0x01, 0x94); ++ tda10086_write_byte(state, 0x02, 0x35); /* NOTE: TT drivers appear to disable CSWP */ ++ tda10086_write_byte(state, 0x03, 0xe4); ++ tda10086_write_byte(state, 0x04, 0x43); ++ tda10086_write_byte(state, 0x0c, 0x0c); ++ tda10086_write_byte(state, 0x1b, 0xb0); /* noise threshold */ ++ tda10086_write_byte(state, 0x20, 0x89); /* misc */ ++ tda10086_write_byte(state, 0x30, 0x04); /* acquisition period length */ ++ tda10086_write_byte(state, 0x32, 0x00); /* irq off */ ++ tda10086_write_byte(state, 0x31, 0x56); /* setup AFC */ ++ ++ /* setup PLL (this assumes SACLK = 96MHz) */ ++ tda10086_write_byte(state, 0x55, 0x2c); /* misc PLL setup */ ++ if (state->config->xtal_freq == TDA10086_XTAL_16M) { ++ tda10086_write_byte(state, 0x3a, 0x0b); /* M=12 */ ++ tda10086_write_byte(state, 0x3b, 0x01); /* P=2 */ ++ } else { ++ tda10086_write_byte(state, 0x3a, 0x17); /* M=24 */ ++ tda10086_write_byte(state, 0x3b, 0x00); /* P=1 */ ++ } ++ tda10086_write_mask(state, 0x55, 0x20, 0x00); /* powerup PLL */ ++ ++ /* setup TS interface */ ++ tda10086_write_byte(state, 0x11, 0x81); ++ tda10086_write_byte(state, 0x12, 0x81); ++ tda10086_write_byte(state, 0x19, 0x40); /* parallel mode A + MSBFIRST */ ++ tda10086_write_byte(state, 0x56, 0x80); /* powerdown WPLL - unused in the mode we use */ ++ tda10086_write_byte(state, 0x57, 0x08); /* bypass WPLL - unused in the mode we use */ ++ tda10086_write_byte(state, 0x10, 0x2a); ++ ++ /* setup ADC */ ++ tda10086_write_byte(state, 0x58, 0x61); /* ADC setup */ ++ tda10086_write_mask(state, 0x58, 0x01, 0x00); /* powerup ADC */ ++ ++ /* setup AGC */ ++ tda10086_write_byte(state, 0x05, 0x0B); ++ tda10086_write_byte(state, 0x37, 0x63); ++ tda10086_write_byte(state, 0x3f, 0x0a); /* NOTE: flydvb varies it */ ++ tda10086_write_byte(state, 0x40, 0x64); ++ tda10086_write_byte(state, 0x41, 0x4f); ++ tda10086_write_byte(state, 0x42, 0x43); ++ ++ /* setup viterbi */ ++ tda10086_write_byte(state, 0x1a, 0x11); /* VBER 10^6, DVB, QPSK */ ++ ++ /* setup carrier recovery */ ++ tda10086_write_byte(state, 0x3d, 0x80); ++ ++ /* setup SEC */ ++ tda10086_write_byte(state, 0x36, t22k_off); /* all SEC off, 22k tone */ ++ tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); ++ tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); ++ ++ return 0; ++} ++ ++static void tda10086_diseqc_wait(struct tda10086_state *state) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(200); ++ while (!(tda10086_read_byte(state, 0x50) & 0x01)) { ++ if(time_after(jiffies, timeout)) { ++ printk("%s: diseqc queue not ready, command may be lost.\n", __func__); ++ break; ++ } ++ msleep(10); ++ } ++} ++ ++static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 t22k_off = 0x80; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (state->config->diseqc_tone) ++ t22k_off = 0; ++ ++ switch (tone) { ++ case SEC_TONE_OFF: ++ tda10086_write_byte(state, 0x36, t22k_off); ++ break; ++ ++ case SEC_TONE_ON: ++ tda10086_write_byte(state, 0x36, 0x01 + t22k_off); ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tda10086_send_master_cmd (struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ int i; ++ u8 oldval; ++ u8 t22k_off = 0x80; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (state->config->diseqc_tone) ++ t22k_off = 0; ++ ++ if (cmd->msg_len > 6) ++ return -EINVAL; ++ oldval = tda10086_read_byte(state, 0x36); ++ ++ for(i=0; i< cmd->msg_len; i++) { ++ tda10086_write_byte(state, 0x48+i, cmd->msg[i]); ++ } ++ tda10086_write_byte(state, 0x36, (0x08 + t22k_off) ++ | ((cmd->msg_len - 1) << 4)); ++ ++ tda10086_diseqc_wait(state); ++ ++ tda10086_write_byte(state, 0x36, oldval); ++ ++ return 0; ++} ++ ++static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 oldval = tda10086_read_byte(state, 0x36); ++ u8 t22k_off = 0x80; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (state->config->diseqc_tone) ++ t22k_off = 0; ++ ++ switch(minicmd) { ++ case SEC_MINI_A: ++ tda10086_write_byte(state, 0x36, 0x04 + t22k_off); ++ break; ++ ++ case SEC_MINI_B: ++ tda10086_write_byte(state, 0x36, 0x06 + t22k_off); ++ break; ++ } ++ ++ tda10086_diseqc_wait(state); ++ ++ tda10086_write_byte(state, 0x36, oldval); ++ ++ return 0; ++} ++ ++static int tda10086_set_inversion(struct tda10086_state *state, ++ struct dtv_frontend_properties *fe_params) ++{ ++ u8 invval = 0x80; ++ ++ dprintk ("%s %i %i\n", __func__, fe_params->inversion, state->config->invert); ++ ++ switch(fe_params->inversion) { ++ case INVERSION_OFF: ++ if (state->config->invert) ++ invval = 0x40; ++ break; ++ case INVERSION_ON: ++ if (!state->config->invert) ++ invval = 0x40; ++ break; ++ case INVERSION_AUTO: ++ invval = 0x00; ++ break; ++ } ++ tda10086_write_mask(state, 0x0c, 0xc0, invval); ++ ++ return 0; ++} ++ ++static int tda10086_set_symbol_rate(struct tda10086_state *state, ++ struct dtv_frontend_properties *fe_params) ++{ ++ u8 dfn = 0; ++ u8 afs = 0; ++ u8 byp = 0; ++ u8 reg37 = 0x43; ++ u8 reg42 = 0x43; ++ u64 big; ++ u32 tmp; ++ u32 bdr; ++ u32 bdri; ++ u32 symbol_rate = fe_params->symbol_rate; ++ ++ dprintk ("%s %i\n", __func__, symbol_rate); ++ ++ /* setup the decimation and anti-aliasing filters.. */ ++ if (symbol_rate < (u32) (SACLK * 0.0137)) { ++ dfn=4; ++ afs=1; ++ } else if (symbol_rate < (u32) (SACLK * 0.0208)) { ++ dfn=4; ++ afs=0; ++ } else if (symbol_rate < (u32) (SACLK * 0.0270)) { ++ dfn=3; ++ afs=1; ++ } else if (symbol_rate < (u32) (SACLK * 0.0416)) { ++ dfn=3; ++ afs=0; ++ } else if (symbol_rate < (u32) (SACLK * 0.0550)) { ++ dfn=2; ++ afs=1; ++ } else if (symbol_rate < (u32) (SACLK * 0.0833)) { ++ dfn=2; ++ afs=0; ++ } else if (symbol_rate < (u32) (SACLK * 0.1100)) { ++ dfn=1; ++ afs=1; ++ } else if (symbol_rate < (u32) (SACLK * 0.1666)) { ++ dfn=1; ++ afs=0; ++ } else if (symbol_rate < (u32) (SACLK * 0.2200)) { ++ dfn=0; ++ afs=1; ++ } else if (symbol_rate < (u32) (SACLK * 0.3333)) { ++ dfn=0; ++ afs=0; ++ } else { ++ reg37 = 0x63; ++ reg42 = 0x4f; ++ byp=1; ++ } ++ ++ /* calculate BDR */ ++ big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<> 8); ++ tda10086_write_byte(state, 0x08, bdr >> 16); ++ tda10086_write_byte(state, 0x09, bdri); ++ tda10086_write_byte(state, 0x37, reg37); ++ tda10086_write_byte(state, 0x42, reg42); ++ ++ return 0; ++} ++ ++static int tda10086_set_fec(struct tda10086_state *state, ++ struct dtv_frontend_properties *fe_params) ++{ ++ u8 fecval; ++ ++ dprintk("%s %i\n", __func__, fe_params->fec_inner); ++ ++ switch (fe_params->fec_inner) { ++ case FEC_1_2: ++ fecval = 0x00; ++ break; ++ case FEC_2_3: ++ fecval = 0x01; ++ break; ++ case FEC_3_4: ++ fecval = 0x02; ++ break; ++ case FEC_4_5: ++ fecval = 0x03; ++ break; ++ case FEC_5_6: ++ fecval = 0x04; ++ break; ++ case FEC_6_7: ++ fecval = 0x05; ++ break; ++ case FEC_7_8: ++ fecval = 0x06; ++ break; ++ case FEC_8_9: ++ fecval = 0x07; ++ break; ++ case FEC_AUTO: ++ fecval = 0x08; ++ break; ++ default: ++ return -1; ++ } ++ tda10086_write_byte(state, 0x0d, fecval); ++ ++ return 0; ++} ++ ++static int tda10086_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; ++ struct tda10086_state *state = fe->demodulator_priv; ++ int ret; ++ u32 freq = 0; ++ int freqoff; ++ ++ dprintk ("%s\n", __func__); ++ ++ /* modify parameters for tuning */ ++ tda10086_write_byte(state, 0x02, 0x35); ++ state->has_lock = false; ++ ++ /* set params */ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ if (fe->ops.tuner_ops.get_frequency) ++ fe->ops.tuner_ops.get_frequency(fe, &freq); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ /* calcluate the frequency offset (in *Hz* not kHz) */ ++ freqoff = fe_params->frequency - freq; ++ freqoff = ((1<<16) * freqoff) / (SACLK/1000); ++ tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f)); ++ tda10086_write_byte(state, 0x3e, freqoff); ++ ++ if ((ret = tda10086_set_inversion(state, fe_params)) < 0) ++ return ret; ++ if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0) ++ return ret; ++ if ((ret = tda10086_set_fec(state, fe_params)) < 0) ++ return ret; ++ ++ /* soft reset + disable TS output until lock */ ++ tda10086_write_mask(state, 0x10, 0x40, 0x40); ++ tda10086_write_mask(state, 0x00, 0x01, 0x00); ++ ++ state->symbol_rate = fe_params->symbol_rate; ++ state->frequency = fe_params->frequency; ++ return 0; ++} ++ ++static int tda10086_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 val; ++ int tmp; ++ u64 tmp64; ++ ++ dprintk ("%s\n", __func__); ++ ++ /* check for invalid symbol rate */ ++ if (fe_params->symbol_rate < 500000) ++ return -EINVAL; ++ ++ /* calculate the updated frequency (note: we convert from Hz->kHz) */ ++ tmp64 = tda10086_read_byte(state, 0x52); ++ tmp64 |= (tda10086_read_byte(state, 0x51) << 8); ++ if (tmp64 & 0x8000) ++ tmp64 |= 0xffffffffffff0000ULL; ++ tmp64 = (tmp64 * (SACLK/1000ULL)); ++ do_div(tmp64, (1ULL<<15) * (1ULL<<1)); ++ fe_params->frequency = (int) state->frequency + (int) tmp64; ++ ++ /* the inversion */ ++ val = tda10086_read_byte(state, 0x0c); ++ if (val & 0x80) { ++ switch(val & 0x40) { ++ case 0x00: ++ fe_params->inversion = INVERSION_OFF; ++ if (state->config->invert) ++ fe_params->inversion = INVERSION_ON; ++ break; ++ default: ++ fe_params->inversion = INVERSION_ON; ++ if (state->config->invert) ++ fe_params->inversion = INVERSION_OFF; ++ break; ++ } ++ } else { ++ tda10086_read_byte(state, 0x0f); ++ switch(val & 0x02) { ++ case 0x00: ++ fe_params->inversion = INVERSION_OFF; ++ if (state->config->invert) ++ fe_params->inversion = INVERSION_ON; ++ break; ++ default: ++ fe_params->inversion = INVERSION_ON; ++ if (state->config->invert) ++ fe_params->inversion = INVERSION_OFF; ++ break; ++ } ++ } ++ ++ /* calculate the updated symbol rate */ ++ tmp = tda10086_read_byte(state, 0x1d); ++ if (tmp & 0x80) ++ tmp |= 0xffffff00; ++ tmp = (tmp * 480 * (1<<1)) / 128; ++ tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000); ++ fe_params->symbol_rate = state->symbol_rate + tmp; ++ ++ /* the FEC */ ++ val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4; ++ switch(val) { ++ case 0x00: ++ fe_params->fec_inner = FEC_1_2; ++ break; ++ case 0x01: ++ fe_params->fec_inner = FEC_2_3; ++ break; ++ case 0x02: ++ fe_params->fec_inner = FEC_3_4; ++ break; ++ case 0x03: ++ fe_params->fec_inner = FEC_4_5; ++ break; ++ case 0x04: ++ fe_params->fec_inner = FEC_5_6; ++ break; ++ case 0x05: ++ fe_params->fec_inner = FEC_6_7; ++ break; ++ case 0x06: ++ fe_params->fec_inner = FEC_7_8; ++ break; ++ case 0x07: ++ fe_params->fec_inner = FEC_8_9; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 val; ++ ++ dprintk ("%s\n", __func__); ++ ++ val = tda10086_read_byte(state, 0x0e); ++ *fe_status = 0; ++ if (val & 0x01) ++ *fe_status |= FE_HAS_SIGNAL; ++ if (val & 0x02) ++ *fe_status |= FE_HAS_CARRIER; ++ if (val & 0x04) ++ *fe_status |= FE_HAS_VITERBI; ++ if (val & 0x08) ++ *fe_status |= FE_HAS_SYNC; ++ if (val & 0x10) { ++ *fe_status |= FE_HAS_LOCK; ++ if (!state->has_lock) { ++ state->has_lock = true; ++ /* modify parameters for stable reception */ ++ tda10086_write_byte(state, 0x02, 0x00); ++ } ++ } ++ ++ return 0; ++} ++ ++static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 _str; ++ ++ dprintk ("%s\n", __func__); ++ ++ _str = 0xff - tda10086_read_byte(state, 0x43); ++ *signal = (_str << 8) | _str; ++ ++ return 0; ++} ++ ++static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ u8 _snr; ++ ++ dprintk ("%s\n", __func__); ++ ++ _snr = 0xff - tda10086_read_byte(state, 0x1c); ++ *snr = (_snr << 8) | _snr; ++ ++ return 0; ++} ++ ++static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ /* read it */ ++ *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f; ++ ++ /* reset counter */ ++ tda10086_write_byte(state, 0x18, 0x00); ++ tda10086_write_byte(state, 0x18, 0x80); ++ ++ return 0; ++} ++ ++static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ /* read it */ ++ *ber = 0; ++ *ber |= tda10086_read_byte(state, 0x15); ++ *ber |= tda10086_read_byte(state, 0x16) << 8; ++ *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16; ++ ++ return 0; ++} ++ ++static int tda10086_sleep(struct dvb_frontend* fe) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ tda10086_write_mask(state, 0x00, 0x08, 0x08); ++ ++ return 0; ++} ++ ++static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct tda10086_state* state = fe->demodulator_priv; ++ ++ dprintk ("%s\n", __func__); ++ ++ if (enable) { ++ tda10086_write_mask(state, 0x00, 0x10, 0x10); ++ } else { ++ tda10086_write_mask(state, 0x00, 0x10, 0x00); ++ } ++ ++ return 0; ++} ++ ++static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ if (p->symbol_rate > 20000000) { ++ fesettings->min_delay_ms = 50; ++ fesettings->step_size = 2000; ++ fesettings->max_drift = 8000; ++ } else if (p->symbol_rate > 12000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 1500; ++ fesettings->max_drift = 9000; ++ } else if (p->symbol_rate > 8000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 1000; ++ fesettings->max_drift = 8000; ++ } else if (p->symbol_rate > 4000000) { ++ fesettings->min_delay_ms = 100; ++ fesettings->step_size = 500; ++ fesettings->max_drift = 7000; ++ } else if (p->symbol_rate > 2000000) { ++ fesettings->min_delay_ms = 200; ++ fesettings->step_size = p->symbol_rate / 8000; ++ fesettings->max_drift = 14 * fesettings->step_size; ++ } else { ++ fesettings->min_delay_ms = 200; ++ fesettings->step_size = p->symbol_rate / 8000; ++ fesettings->max_drift = 18 * fesettings->step_size; ++ } ++ ++ return 0; ++} ++ ++static void tda10086_release(struct dvb_frontend* fe) ++{ ++ struct tda10086_state *state = fe->demodulator_priv; ++ tda10086_sleep(fe); ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops tda10086_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Philips TDA10086 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .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_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK ++ }, ++ ++ .release = tda10086_release, ++ ++ .init = tda10086_init, ++ .sleep = tda10086_sleep, ++ .i2c_gate_ctrl = tda10086_i2c_gate_ctrl, ++ ++ .set_frontend = tda10086_set_frontend, ++ .get_frontend = tda10086_get_frontend, ++ .get_tune_settings = tda10086_get_tune_settings, ++ ++ .read_status = tda10086_read_status, ++ .read_ber = tda10086_read_ber, ++ .read_signal_strength = tda10086_read_signal_strength, ++ .read_snr = tda10086_read_snr, ++ .read_ucblocks = tda10086_read_ucblocks, ++ ++ .diseqc_send_master_cmd = tda10086_send_master_cmd, ++ .diseqc_send_burst = tda10086_send_burst, ++ .set_tone = tda10086_set_tone, ++}; ++ ++struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct tda10086_state *state; ++ ++ dprintk ("%s\n", __func__); ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if (tda10086_read_byte(state, 0x1e) != 0xe1) { ++ kfree(state); ++ return NULL; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++} ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator"); ++MODULE_AUTHOR("Andrew de Quincey"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(tda10086_attach); +diff --git a/drivers/media/dvb-frontends/tda10086.h b/drivers/media/dvb-frontends/tda10086.h +new file mode 100644 +index 0000000..61148c5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda10086.h +@@ -0,0 +1,61 @@ ++ /* ++ Driver for Philips tda10086 DVBS Frontend ++ ++ (c) 2006 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#ifndef TDA10086_H ++#define TDA10086_H ++ ++#include ++#include ++ ++enum tda10086_xtal { ++ TDA10086_XTAL_16M, ++ TDA10086_XTAL_4M ++}; ++ ++struct tda10086_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* does the "inversion" need inverted? */ ++ u8 invert; ++ ++ /* do we need the diseqc signal with carrier? */ ++ u8 diseqc_tone; ++ ++ /* frequency of the reference xtal */ ++ enum tda10086_xtal xtal_freq; ++}; ++ ++#if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_TDA10086 */ ++ ++#endif /* TDA10086_H */ +diff --git a/drivers/media/dvb-frontends/tda18271c2dd.c b/drivers/media/dvb-frontends/tda18271c2dd.c +new file mode 100644 +index 0000000..d281f77 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda18271c2dd.c +@@ -0,0 +1,1247 @@ ++/* ++ * tda18271c2dd: Driver for the TDA18271C2 tuner ++ * ++ * Copyright (C) 2010 Digital Devices GmbH ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda18271c2dd.h" ++ ++struct SStandardParam { ++ s32 m_IFFrequency; ++ u32 m_BandWidth; ++ u8 m_EP3_4_0; ++ u8 m_EB22; ++}; ++ ++struct SMap { ++ u32 m_Frequency; ++ u8 m_Param; ++}; ++ ++struct SMapI { ++ u32 m_Frequency; ++ s32 m_Param; ++}; ++ ++struct SMap2 { ++ u32 m_Frequency; ++ u8 m_Param1; ++ u8 m_Param2; ++}; ++ ++struct SRFBandMap { ++ u32 m_RF_max; ++ u32 m_RF1_Default; ++ u32 m_RF2_Default; ++ u32 m_RF3_Default; ++}; ++ ++enum ERegister { ++ ID = 0, ++ TM, ++ PL, ++ EP1, EP2, EP3, EP4, EP5, ++ CPD, CD1, CD2, CD3, ++ MPD, MD1, MD2, MD3, ++ EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10, ++ EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20, ++ EB21, EB22, EB23, ++ NUM_REGS ++}; ++ ++struct tda_state { ++ struct i2c_adapter *i2c; ++ u8 adr; ++ ++ u32 m_Frequency; ++ u32 IF; ++ ++ u8 m_IFLevelAnalog; ++ u8 m_IFLevelDigital; ++ u8 m_IFLevelDVBC; ++ u8 m_IFLevelDVBT; ++ ++ u8 m_EP4; ++ u8 m_EP3_Standby; ++ ++ bool m_bMaster; ++ ++ s32 m_SettlingTime; ++ ++ u8 m_Regs[NUM_REGS]; ++ ++ /* Tracking filter settings for band 0..6 */ ++ u32 m_RF1[7]; ++ s32 m_RF_A1[7]; ++ s32 m_RF_B1[7]; ++ u32 m_RF2[7]; ++ s32 m_RF_A2[7]; ++ s32 m_RF_B2[7]; ++ u32 m_RF3[7]; ++ ++ u8 m_TMValue_RFCal; /* Calibration temperatur */ ++ ++ bool m_bFMInput; /* true to use Pin 8 for FM Radio */ ++ ++}; ++ ++static int PowerScan(struct tda_state *state, ++ u8 RFBand, u32 RF_in, ++ u32 *pRF_Out, bool *pbcal); ++ ++static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len) ++{ ++ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, ++ .buf = data, .len = len} }; ++ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; ++} ++ ++static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) ++{ ++ struct i2c_msg msg = {.addr = adr, .flags = 0, ++ .buf = data, .len = len}; ++ ++ if (i2c_transfer(adap, &msg, 1) != 1) { ++ printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr); ++ return -1; ++ } ++ return 0; ++} ++ ++static int WriteRegs(struct tda_state *state, ++ u8 SubAddr, u8 *Regs, u16 nRegs) ++{ ++ u8 data[nRegs+1]; ++ ++ data[0] = SubAddr; ++ memcpy(data + 1, Regs, nRegs); ++ return i2c_write(state->i2c, state->adr, data, nRegs+1); ++} ++ ++static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg) ++{ ++ u8 msg[2] = {SubAddr, Reg}; ++ ++ return i2c_write(state->i2c, state->adr, msg, 2); ++} ++ ++static int Read(struct tda_state *state, u8 * Regs) ++{ ++ return i2c_readn(state->i2c, state->adr, Regs, 16); ++} ++ ++static int ReadExtented(struct tda_state *state, u8 * Regs) ++{ ++ return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS); ++} ++ ++static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo) ++{ ++ return WriteRegs(state, RegFrom, ++ &state->m_Regs[RegFrom], RegTo-RegFrom+1); ++} ++static int UpdateReg(struct tda_state *state, u8 Reg) ++{ ++ return WriteReg(state, Reg, state->m_Regs[Reg]); ++} ++ ++#include "tda18271c2dd_maps.h" ++ ++static void reset(struct tda_state *state) ++{ ++ u32 ulIFLevelAnalog = 0; ++ u32 ulIFLevelDigital = 2; ++ u32 ulIFLevelDVBC = 7; ++ u32 ulIFLevelDVBT = 6; ++ u32 ulXTOut = 0; ++ u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */ ++ u32 ulSlave = 0; ++ u32 ulFMInput = 0; ++ u32 ulSettlingTime = 100; ++ ++ state->m_Frequency = 0; ++ state->m_SettlingTime = 100; ++ state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2; ++ state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2; ++ state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2; ++ state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2; ++ ++ state->m_EP4 = 0x20; ++ if (ulXTOut != 0) ++ state->m_EP4 |= 0x40; ++ ++ state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F; ++ state->m_bMaster = (ulSlave == 0); ++ ++ state->m_SettlingTime = ulSettlingTime; ++ ++ state->m_bFMInput = (ulFMInput == 2); ++} ++ ++static bool SearchMap1(struct SMap Map[], ++ u32 Frequency, u8 *pParam) ++{ ++ int i = 0; ++ ++ while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency)) ++ i += 1; ++ if (Map[i].m_Frequency == 0) ++ return false; ++ *pParam = Map[i].m_Param; ++ return true; ++} ++ ++static bool SearchMap2(struct SMapI Map[], ++ u32 Frequency, s32 *pParam) ++{ ++ int i = 0; ++ ++ while ((Map[i].m_Frequency != 0) && ++ (Frequency > Map[i].m_Frequency)) ++ i += 1; ++ if (Map[i].m_Frequency == 0) ++ return false; ++ *pParam = Map[i].m_Param; ++ return true; ++} ++ ++static bool SearchMap3(struct SMap2 Map[], u32 Frequency, ++ u8 *pParam1, u8 *pParam2) ++{ ++ int i = 0; ++ ++ while ((Map[i].m_Frequency != 0) && ++ (Frequency > Map[i].m_Frequency)) ++ i += 1; ++ if (Map[i].m_Frequency == 0) ++ return false; ++ *pParam1 = Map[i].m_Param1; ++ *pParam2 = Map[i].m_Param2; ++ return true; ++} ++ ++static bool SearchMap4(struct SRFBandMap Map[], ++ u32 Frequency, u8 *pRFBand) ++{ ++ int i = 0; ++ ++ while (i < 7 && (Frequency > Map[i].m_RF_max)) ++ i += 1; ++ if (i == 7) ++ return false; ++ *pRFBand = i; ++ return true; ++} ++ ++static int ThermometerRead(struct tda_state *state, u8 *pTM_Value) ++{ ++ int status = 0; ++ ++ do { ++ u8 Regs[16]; ++ state->m_Regs[TM] |= 0x10; ++ status = UpdateReg(state, TM); ++ if (status < 0) ++ break; ++ status = Read(state, Regs); ++ if (status < 0) ++ break; ++ if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) || ++ ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) { ++ state->m_Regs[TM] ^= 0x20; ++ status = UpdateReg(state, TM); ++ if (status < 0) ++ break; ++ msleep(10); ++ status = Read(state, Regs); ++ if (status < 0) ++ break; ++ } ++ *pTM_Value = (Regs[TM] & 0x20) ++ ? m_Thermometer_Map_2[Regs[TM] & 0x0F] ++ : m_Thermometer_Map_1[Regs[TM] & 0x0F] ; ++ state->m_Regs[TM] &= ~0x10; /* Thermometer off */ ++ status = UpdateReg(state, TM); ++ if (status < 0) ++ break; ++ state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */ ++ status = UpdateReg(state, EP4); ++ if (status < 0) ++ break; ++ } while (0); ++ ++ return status; ++} ++ ++static int StandBy(struct tda_state *state) ++{ ++ int status = 0; ++ do { ++ state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */ ++ status = UpdateReg(state, EB12); ++ if (status < 0) ++ break; ++ state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */ ++ status = UpdateReg(state, EB18); ++ if (status < 0) ++ break; ++ state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */ ++ state->m_Regs[EP3] = state->m_EP3_Standby; ++ status = UpdateReg(state, EP3); ++ if (status < 0) ++ break; ++ state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */ ++ status = UpdateRegs(state, EB21, EB23); ++ if (status < 0) ++ break; ++ } while (0); ++ return status; ++} ++ ++static int CalcMainPLL(struct tda_state *state, u32 freq) ++{ ++ ++ u8 PostDiv; ++ u8 Div; ++ u64 OscFreq; ++ u32 MainDiv; ++ ++ if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div)) ++ return -EINVAL; ++ ++ OscFreq = (u64) freq * (u64) Div; ++ OscFreq *= (u64) 16384; ++ do_div(OscFreq, (u64)16000000); ++ MainDiv = OscFreq; ++ ++ state->m_Regs[MPD] = PostDiv & 0x77; ++ state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F); ++ state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF); ++ state->m_Regs[MD3] = (MainDiv & 0xFF); ++ ++ return UpdateRegs(state, MPD, MD3); ++} ++ ++static int CalcCalPLL(struct tda_state *state, u32 freq) ++{ ++ u8 PostDiv; ++ u8 Div; ++ u64 OscFreq; ++ u32 CalDiv; ++ ++ if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div)) ++ return -EINVAL; ++ ++ OscFreq = (u64)freq * (u64)Div; ++ /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ ++ OscFreq *= (u64)16384; ++ do_div(OscFreq, (u64)16000000); ++ CalDiv = OscFreq; ++ ++ state->m_Regs[CPD] = PostDiv; ++ state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF); ++ state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF); ++ state->m_Regs[CD3] = (CalDiv & 0xFF); ++ ++ return UpdateRegs(state, CPD, CD3); ++} ++ ++static int CalibrateRF(struct tda_state *state, ++ u8 RFBand, u32 freq, s32 *pCprog) ++{ ++ int status = 0; ++ u8 Regs[NUM_REGS]; ++ do { ++ u8 BP_Filter = 0; ++ u8 GainTaper = 0; ++ u8 RFC_K = 0; ++ u8 RFC_M = 0; ++ ++ state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */ ++ status = UpdateReg(state, EP4); ++ if (status < 0) ++ break; ++ state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */ ++ status = UpdateReg(state, EB18); ++ if (status < 0) ++ break; ++ ++ /* Switching off LT (as datasheet says) causes calibration on C1 to fail */ ++ /* (Readout of Cprog is allways 255) */ ++ if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */ ++ state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */ ++ ++ if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) && ++ SearchMap1(m_GainTaper_Map, freq, &GainTaper) && ++ SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M))) ++ return -EINVAL; ++ ++ state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter; ++ state->m_Regs[EP2] = (RFBand << 5) | GainTaper; ++ ++ state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2); ++ ++ status = UpdateRegs(state, EP1, EP3); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EB13); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */ ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */ ++ status = UpdateReg(state, EB7); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */ ++ status = UpdateReg(state, EB14); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */ ++ status = UpdateReg(state, EB20); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */ ++ status = UpdateRegs(state, EP4, EP5); ++ if (status < 0) ++ break; ++ ++ status = CalcCalPLL(state, freq); ++ if (status < 0) ++ break; ++ status = CalcMainPLL(state, freq + 1000000); ++ if (status < 0) ++ break; ++ ++ msleep(5); ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */ ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */ ++ status = UpdateReg(state, EB7); ++ if (status < 0) ++ break; ++ msleep(10); ++ ++ state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */ ++ status = UpdateReg(state, EB20); ++ if (status < 0) ++ break; ++ msleep(60); ++ ++ state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */ ++ state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */ ++ state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */ ++ status = UpdateReg(state, EB18); ++ if (status < 0) ++ break; ++ status = UpdateRegs(state, EP3, EP4); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ ++ status = ReadExtented(state, Regs); ++ if (status < 0) ++ break; ++ ++ *pCprog = Regs[EB14]; ++ ++ } while (0); ++ return status; ++} ++ ++static int RFTrackingFiltersInit(struct tda_state *state, ++ u8 RFBand) ++{ ++ int status = 0; ++ ++ u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default; ++ u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default; ++ u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default; ++ bool bcal = false; ++ ++ s32 Cprog_cal1 = 0; ++ s32 Cprog_table1 = 0; ++ s32 Cprog_cal2 = 0; ++ s32 Cprog_table2 = 0; ++ s32 Cprog_cal3 = 0; ++ s32 Cprog_table3 = 0; ++ ++ state->m_RF_A1[RFBand] = 0; ++ state->m_RF_B1[RFBand] = 0; ++ state->m_RF_A2[RFBand] = 0; ++ state->m_RF_B2[RFBand] = 0; ++ ++ do { ++ status = PowerScan(state, RFBand, RF1, &RF1, &bcal); ++ if (status < 0) ++ break; ++ if (bcal) { ++ status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1); ++ if (status < 0) ++ break; ++ } ++ SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1); ++ if (!bcal) ++ Cprog_cal1 = Cprog_table1; ++ state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1; ++ /* state->m_RF_A1[RF_Band] = ???? */ ++ ++ if (RF2 == 0) ++ break; ++ ++ status = PowerScan(state, RFBand, RF2, &RF2, &bcal); ++ if (status < 0) ++ break; ++ if (bcal) { ++ status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2); ++ if (status < 0) ++ break; ++ } ++ SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2); ++ if (!bcal) ++ Cprog_cal2 = Cprog_table2; ++ ++ state->m_RF_A1[RFBand] = ++ (Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) / ++ ((s32)(RF2) - (s32)(RF1)); ++ ++ if (RF3 == 0) ++ break; ++ ++ status = PowerScan(state, RFBand, RF3, &RF3, &bcal); ++ if (status < 0) ++ break; ++ if (bcal) { ++ status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3); ++ if (status < 0) ++ break; ++ } ++ SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3); ++ if (!bcal) ++ Cprog_cal3 = Cprog_table3; ++ state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2)); ++ state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2; ++ ++ } while (0); ++ ++ state->m_RF1[RFBand] = RF1; ++ state->m_RF2[RFBand] = RF2; ++ state->m_RF3[RFBand] = RF3; ++ ++#if 0 ++ printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__, ++ RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2, ++ state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3); ++#endif ++ ++ return status; ++} ++ ++static int PowerScan(struct tda_state *state, ++ u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal) ++{ ++ int status = 0; ++ do { ++ u8 Gain_Taper = 0; ++ s32 RFC_Cprog = 0; ++ u8 CID_Target = 0; ++ u8 CountLimit = 0; ++ u32 freq_MainPLL; ++ u8 Regs[NUM_REGS]; ++ u8 CID_Gain; ++ s32 Count = 0; ++ int sign = 1; ++ bool wait = false; ++ ++ if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) && ++ SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) && ++ SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) { ++ ++ printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__); ++ return -EINVAL; ++ } ++ ++ state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper; ++ state->m_Regs[EB14] = (RFC_Cprog); ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EB14); ++ if (status < 0) ++ break; ++ ++ freq_MainPLL = RF_in + 1000000; ++ status = CalcMainPLL(state, freq_MainPLL); ++ if (status < 0) ++ break; ++ msleep(5); ++ state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */ ++ status = UpdateReg(state, EP4); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP2); /* Launch power measurement */ ++ if (status < 0) ++ break; ++ status = ReadExtented(state, Regs); ++ if (status < 0) ++ break; ++ CID_Gain = Regs[EB10] & 0x3F; ++ state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workarround in CalibrateRF) */ ++ ++ *pRF_Out = RF_in; ++ ++ while (CID_Gain < CID_Target) { ++ freq_MainPLL = RF_in + sign * Count + 1000000; ++ status = CalcMainPLL(state, freq_MainPLL); ++ if (status < 0) ++ break; ++ msleep(wait ? 5 : 1); ++ wait = false; ++ status = UpdateReg(state, EP2); /* Launch power measurement */ ++ if (status < 0) ++ break; ++ status = ReadExtented(state, Regs); ++ if (status < 0) ++ break; ++ CID_Gain = Regs[EB10] & 0x3F; ++ Count += 200000; ++ ++ if (Count < CountLimit * 100000) ++ continue; ++ if (sign < 0) ++ break; ++ ++ sign = -sign; ++ Count = 200000; ++ wait = true; ++ } ++ status = status; ++ if (status < 0) ++ break; ++ if (CID_Gain >= CID_Target) { ++ *pbcal = true; ++ *pRF_Out = freq_MainPLL - 1000000; ++ } else ++ *pbcal = false; ++ } while (0); ++ ++ return status; ++} ++ ++static int PowerScanInit(struct tda_state *state) ++{ ++ int status = 0; ++ do { ++ state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12; ++ state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */ ++ status = UpdateRegs(state, EP3, EP4); ++ if (status < 0) ++ break; ++ state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */ ++ status = UpdateReg(state, EB18); ++ if (status < 0) ++ break; ++ state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */ ++ state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ ++ status = UpdateRegs(state, EB21, EB23); ++ if (status < 0) ++ break; ++ } while (0); ++ return status; ++} ++ ++static int CalcRFFilterCurve(struct tda_state *state) ++{ ++ int status = 0; ++ do { ++ msleep(200); /* Temperature stabilisation */ ++ status = PowerScanInit(state); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 0); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 1); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 2); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 3); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 4); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 5); ++ if (status < 0) ++ break; ++ status = RFTrackingFiltersInit(state, 6); ++ if (status < 0) ++ break; ++ status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */ ++ if (status < 0) ++ break; ++ } while (0); ++ ++ return status; ++} ++ ++static int FixedContentsI2CUpdate(struct tda_state *state) ++{ ++ static u8 InitRegs[] = { ++ 0x08, 0x80, 0xC6, ++ 0xDF, 0x16, 0x60, 0x80, ++ 0x80, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, ++ 0xFC, 0x01, 0x84, 0x41, ++ 0x01, 0x84, 0x40, 0x07, ++ 0x00, 0x00, 0x96, 0x3F, ++ 0xC1, 0x00, 0x8F, 0x00, ++ 0x00, 0x8C, 0x00, 0x20, ++ 0xB3, 0x48, 0xB0, ++ }; ++ int status = 0; ++ memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1); ++ do { ++ status = UpdateRegs(state, TM, EB23); ++ if (status < 0) ++ break; ++ ++ /* AGC1 gain setup */ ++ state->m_Regs[EB17] = 0x00; ++ status = UpdateReg(state, EB17); ++ if (status < 0) ++ break; ++ state->m_Regs[EB17] = 0x03; ++ status = UpdateReg(state, EB17); ++ if (status < 0) ++ break; ++ state->m_Regs[EB17] = 0x43; ++ status = UpdateReg(state, EB17); ++ if (status < 0) ++ break; ++ state->m_Regs[EB17] = 0x4C; ++ status = UpdateReg(state, EB17); ++ if (status < 0) ++ break; ++ ++ /* IRC Cal Low band */ ++ state->m_Regs[EP3] = 0x1F; ++ state->m_Regs[EP4] = 0x66; ++ state->m_Regs[EP5] = 0x81; ++ state->m_Regs[CPD] = 0xCC; ++ state->m_Regs[CD1] = 0x6C; ++ state->m_Regs[CD2] = 0x00; ++ state->m_Regs[CD3] = 0x00; ++ state->m_Regs[MPD] = 0xC5; ++ state->m_Regs[MD1] = 0x77; ++ state->m_Regs[MD2] = 0x08; ++ state->m_Regs[MD3] = 0x00; ++ status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */ ++ if (status < 0) ++ break; ++ ++#if 0 ++ state->m_Regs[EB4] = 0x61; /* missing in sw */ ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++ msleep(1); ++ state->m_Regs[EB4] = 0x41; ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++#endif ++ ++ msleep(5); ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ msleep(5); ++ ++ state->m_Regs[EP5] = 0x85; ++ state->m_Regs[CPD] = 0xCB; ++ state->m_Regs[CD1] = 0x66; ++ state->m_Regs[CD2] = 0x70; ++ status = UpdateRegs(state, EP3, CD3); ++ if (status < 0) ++ break; ++ msleep(5); ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ msleep(30); ++ ++ /* IRC Cal mid band */ ++ state->m_Regs[EP5] = 0x82; ++ state->m_Regs[CPD] = 0xA8; ++ state->m_Regs[CD2] = 0x00; ++ state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */ ++ state->m_Regs[MD1] = 0x73; ++ state->m_Regs[MD2] = 0x1A; ++ status = UpdateRegs(state, EP3, MD3); ++ if (status < 0) ++ break; ++ ++ msleep(5); ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ msleep(5); ++ ++ state->m_Regs[EP5] = 0x86; ++ state->m_Regs[CPD] = 0xA8; ++ state->m_Regs[CD1] = 0x66; ++ state->m_Regs[CD2] = 0xA0; ++ status = UpdateRegs(state, EP3, CD3); ++ if (status < 0) ++ break; ++ msleep(5); ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ msleep(30); ++ ++ /* IRC Cal high band */ ++ state->m_Regs[EP5] = 0x83; ++ state->m_Regs[CPD] = 0x98; ++ state->m_Regs[CD1] = 0x65; ++ state->m_Regs[CD2] = 0x00; ++ state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */ ++ state->m_Regs[MD1] = 0x71; ++ state->m_Regs[MD2] = 0xCD; ++ status = UpdateRegs(state, EP3, MD3); ++ if (status < 0) ++ break; ++ msleep(5); ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ msleep(5); ++ state->m_Regs[EP5] = 0x87; ++ state->m_Regs[CD1] = 0x65; ++ state->m_Regs[CD2] = 0x50; ++ status = UpdateRegs(state, EP3, CD3); ++ if (status < 0) ++ break; ++ msleep(5); ++ status = UpdateReg(state, EP2); ++ if (status < 0) ++ break; ++ msleep(30); ++ ++ /* Back to normal */ ++ state->m_Regs[EP4] = 0x64; ++ status = UpdateReg(state, EP4); ++ if (status < 0) ++ break; ++ status = UpdateReg(state, EP1); ++ if (status < 0) ++ break; ++ ++ } while (0); ++ return status; ++} ++ ++static int InitCal(struct tda_state *state) ++{ ++ int status = 0; ++ ++ do { ++ status = FixedContentsI2CUpdate(state); ++ if (status < 0) ++ break; ++ status = CalcRFFilterCurve(state); ++ if (status < 0) ++ break; ++ status = StandBy(state); ++ if (status < 0) ++ break; ++ /* m_bInitDone = true; */ ++ } while (0); ++ return status; ++}; ++ ++static int RFTrackingFiltersCorrection(struct tda_state *state, ++ u32 Frequency) ++{ ++ int status = 0; ++ s32 Cprog_table; ++ u8 RFBand; ++ u8 dCoverdT; ++ ++ if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) || ++ !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) || ++ !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT)) ++ ++ return -EINVAL; ++ ++ do { ++ u8 TMValue_Current; ++ u32 RF1 = state->m_RF1[RFBand]; ++ u32 RF2 = state->m_RF1[RFBand]; ++ u32 RF3 = state->m_RF1[RFBand]; ++ s32 RF_A1 = state->m_RF_A1[RFBand]; ++ s32 RF_B1 = state->m_RF_B1[RFBand]; ++ s32 RF_A2 = state->m_RF_A2[RFBand]; ++ s32 RF_B2 = state->m_RF_B2[RFBand]; ++ s32 Capprox = 0; ++ int TComp; ++ ++ state->m_Regs[EP3] &= ~0xE0; /* Power up */ ++ status = UpdateReg(state, EP3); ++ if (status < 0) ++ break; ++ ++ status = ThermometerRead(state, &TMValue_Current); ++ if (status < 0) ++ break; ++ ++ if (RF3 == 0 || Frequency < RF2) ++ Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table; ++ else ++ Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table; ++ ++ TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000; ++ ++ Capprox += TComp; ++ ++ if (Capprox < 0) ++ Capprox = 0; ++ else if (Capprox > 255) ++ Capprox = 255; ++ ++ ++ /* TODO Temperature compensation. There is defenitely a scale factor */ ++ /* missing in the datasheet, so leave it out for now. */ ++ state->m_Regs[EB14] = Capprox; ++ ++ status = UpdateReg(state, EB14); ++ if (status < 0) ++ break; ++ ++ } while (0); ++ return status; ++} ++ ++static int ChannelConfiguration(struct tda_state *state, ++ u32 Frequency, int Standard) ++{ ++ ++ s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency; ++ int status = 0; ++ ++ u8 BP_Filter = 0; ++ u8 RF_Band = 0; ++ u8 GainTaper = 0; ++ u8 IR_Meas = 0; ++ ++ state->IF = IntermediateFrequency; ++ /* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */ ++ /* get values from tables */ ++ ++ if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) && ++ SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) && ++ SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) && ++ SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) { ++ ++ printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__); ++ return -EINVAL; ++ } ++ ++ do { ++ state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0; ++ state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */ ++ ++ /* m_EP4 default for XToutOn, CAL_Mode (0) */ ++ state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog); ++ /* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */ ++ if (Standard <= HF_AnalogMax) ++ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog; ++ else if (Standard <= HF_ATSC) ++ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT; ++ else if (Standard <= HF_DVBC) ++ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC; ++ else ++ state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; ++ ++ if ((Standard == HF_FM_Radio) && state->m_bFMInput) ++ state->m_Regs[EP4] |= 80; ++ ++ state->m_Regs[MPD] &= ~0x80; ++ if (Standard > HF_AnalogMax) ++ state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */ ++ ++ state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22; ++ ++ /* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */ ++ if (Standard == HF_FM_Radio) ++ state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ ++ else ++ state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */ ++ ++ status = UpdateRegs(state, EB22, EB23); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */ ++ state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas; ++ state->m_Regs[EP2] = (RF_Band << 5) | GainTaper; ++ ++ state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) | ++ (state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */ ++ /* AGC1_always_master = 0 */ ++ /* AGC_firstn = 0 */ ++ status = UpdateReg(state, EB1); ++ if (status < 0) ++ break; ++ ++ if (state->m_bMaster) { ++ status = CalcMainPLL(state, Frequency + IntermediateFrequency); ++ if (status < 0) ++ break; ++ status = UpdateRegs(state, TM, EP5); ++ if (status < 0) ++ break; ++ state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */ ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++ msleep(1); ++ state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */ ++ status = UpdateReg(state, EB4); ++ if (status < 0) ++ break; ++ } else { ++ u8 PostDiv = 0; ++ u8 Div; ++ status = CalcCalPLL(state, Frequency + IntermediateFrequency); ++ if (status < 0) ++ break; ++ ++ SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div); ++ state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77); ++ status = UpdateReg(state, MPD); ++ if (status < 0) ++ break; ++ status = UpdateRegs(state, TM, EP5); ++ if (status < 0) ++ break; ++ ++ state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */ ++ status = UpdateReg(state, EB7); ++ if (status < 0) ++ break; ++ msleep(1); ++ state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */ ++ status = UpdateReg(state, EB7); ++ if (status < 0) ++ break; ++ } ++ msleep(20); ++ if (Standard != HF_FM_Radio) ++ state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */ ++ status = UpdateReg(state, EP3); ++ if (status < 0) ++ break; ++ ++ } while (0); ++ return status; ++} ++ ++static int sleep(struct dvb_frontend *fe) ++{ ++ struct tda_state *state = fe->tuner_priv; ++ ++ StandBy(state); ++ return 0; ++} ++ ++static int init(struct dvb_frontend *fe) ++{ ++ return 0; ++} ++ ++static int release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++ ++static int set_params(struct dvb_frontend *fe) ++{ ++ struct tda_state *state = fe->tuner_priv; ++ int status = 0; ++ int Standard; ++ u32 bw = fe->dtv_property_cache.bandwidth_hz; ++ u32 delsys = fe->dtv_property_cache.delivery_system; ++ ++ state->m_Frequency = fe->dtv_property_cache.frequency; ++ ++ switch (delsys) { ++ case SYS_DVBT: ++ case SYS_DVBT2: ++ switch (bw) { ++ case 6000000: ++ Standard = HF_DVBT_6MHZ; ++ break; ++ case 7000000: ++ Standard = HF_DVBT_7MHZ; ++ break; ++ case 8000000: ++ Standard = HF_DVBT_8MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ case SYS_DVBC_ANNEX_A: ++ case SYS_DVBC_ANNEX_C: ++ if (bw <= 6000000) ++ Standard = HF_DVBC_6MHZ; ++ else if (bw <= 7000000) ++ Standard = HF_DVBC_7MHZ; ++ else ++ Standard = HF_DVBC_8MHZ; ++ break; ++ default: ++ return -EINVAL; ++ } ++ do { ++ status = RFTrackingFiltersCorrection(state, state->m_Frequency); ++ if (status < 0) ++ break; ++ status = ChannelConfiguration(state, state->m_Frequency, ++ Standard); ++ if (status < 0) ++ break; ++ ++ msleep(state->m_SettlingTime); /* Allow AGC's to settle down */ ++ } while (0); ++ return status; ++} ++ ++#if 0 ++static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc) ++{ ++ if (IFAgc < 500) { ++ /* Scale this from 0 to 50000 */ ++ *pSignalStrength = IFAgc * 100; ++ } else { ++ /* Scale range 500-1500 to 50000-80000 */ ++ *pSignalStrength = 50000 + (IFAgc - 500) * 30; ++ } ++ ++ return 0; ++} ++#endif ++ ++static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct tda_state *state = fe->tuner_priv; ++ ++ *frequency = state->IF; ++ return 0; ++} ++ ++static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ /* struct tda_state *state = fe->tuner_priv; */ ++ /* *bandwidth = priv->bandwidth; */ ++ return 0; ++} ++ ++ ++static struct dvb_tuner_ops tuner_ops = { ++ .info = { ++ .name = "NXP TDA18271C2D", ++ .frequency_min = 47125000, ++ .frequency_max = 865000000, ++ .frequency_step = 62500 ++ }, ++ .init = init, ++ .sleep = sleep, ++ .set_params = set_params, ++ .release = release, ++ .get_if_frequency = get_if_frequency, ++ .get_bandwidth = get_bandwidth, ++}; ++ ++struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 adr) ++{ ++ struct tda_state *state; ++ ++ state = kzalloc(sizeof(struct tda_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ fe->tuner_priv = state; ++ state->adr = adr; ++ state->i2c = i2c; ++ memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); ++ reset(state); ++ InitCal(state); ++ ++ return fe; ++} ++EXPORT_SYMBOL_GPL(tda18271c2dd_attach); ++ ++MODULE_DESCRIPTION("TDA18271C2 driver"); ++MODULE_AUTHOR("DD"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tda18271c2dd.h b/drivers/media/dvb-frontends/tda18271c2dd.h +new file mode 100644 +index 0000000..1389c74 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda18271c2dd.h +@@ -0,0 +1,16 @@ ++#ifndef _TDA18271C2DD_H_ ++#define _TDA18271C2DD_H_ ++#if defined(CONFIG_DVB_TDA18271C2DD) || (defined(CONFIG_DVB_TDA18271C2DD_MODULE) \ ++ && defined(MODULE)) ++struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 adr); ++#else ++static inline struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, ++ struct i2c_adapter *i2c, u8 adr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif +diff --git a/drivers/media/dvb-frontends/tda18271c2dd_maps.h b/drivers/media/dvb-frontends/tda18271c2dd_maps.h +new file mode 100644 +index 0000000..b87661b +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda18271c2dd_maps.h +@@ -0,0 +1,814 @@ ++enum HF_S { ++ HF_None = 0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio, ++ HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ, ++ HF_DVBT, HF_ATSC, HF_DVBC_6MHZ, HF_DVBC_7MHZ, ++ HF_DVBC_8MHZ, HF_DVBC ++}; ++ ++struct SStandardParam m_StandardTable[] = { ++ { 0, 0, 0x00, 0x00 }, /* HF_None */ ++ { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */ ++ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */ ++ { 7100000, 8000000, 0x1E, 0x2C }, /* HF_G, */ ++ { 7250000, 8000000, 0x1E, 0x2C }, /* HF_I, */ ++ { 6900000, 8000000, 0x1E, 0x2C }, /* HF_L, */ ++ { 1250000, 8000000, 0x1E, 0x2C }, /* HF_L1, */ ++ { 5400000, 6000000, 0x1C, 0x2C }, /* HF_MN, */ ++ { 1250000, 500000, 0x18, 0x2C }, /* HF_FM_Radio, */ ++ { 0, 0, 0x00, 0x00 }, /* HF_AnalogMax (Unused) */ ++ { 3300000, 6000000, 0x1C, 0x58 }, /* HF_DVBT_6MHZ */ ++ { 3500000, 7000000, 0x1C, 0x37 }, /* HF_DVBT_7MHZ */ ++ { 4000000, 8000000, 0x1D, 0x37 }, /* HF_DVBT_8MHZ */ ++ { 0, 0, 0x00, 0x00 }, /* HF_DVBT (Unused) */ ++ { 5000000, 6000000, 0x1C, 0x37 }, /* HF_ATSC (center = 3.25 MHz) */ ++ { 4000000, 6000000, 0x1D, 0x58 }, /* HF_DVBC_6MHZ (Chicago) */ ++ { 4500000, 7000000, 0x1E, 0x37 }, /* HF_DVBC_7MHZ (not documented by NXP) */ ++ { 5000000, 8000000, 0x1F, 0x37 }, /* HF_DVBC_8MHZ */ ++ { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */ ++}; ++ ++struct SMap m_BP_Filter_Map[] = { ++ { 62000000, 0x00 }, ++ { 84000000, 0x01 }, ++ { 100000000, 0x02 }, ++ { 140000000, 0x03 }, ++ { 170000000, 0x04 }, ++ { 180000000, 0x05 }, ++ { 865000000, 0x06 }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++static struct SMapI m_RF_Cal_Map[] = { ++ { 41000000, 0x0F }, ++ { 43000000, 0x1C }, ++ { 45000000, 0x2F }, ++ { 46000000, 0x39 }, ++ { 47000000, 0x40 }, ++ { 47900000, 0x50 }, ++ { 49100000, 0x16 }, ++ { 50000000, 0x18 }, ++ { 51000000, 0x20 }, ++ { 53000000, 0x28 }, ++ { 55000000, 0x2B }, ++ { 56000000, 0x32 }, ++ { 57000000, 0x35 }, ++ { 58000000, 0x3E }, ++ { 59000000, 0x43 }, ++ { 60000000, 0x4E }, ++ { 61100000, 0x55 }, ++ { 63000000, 0x0F }, ++ { 64000000, 0x11 }, ++ { 65000000, 0x12 }, ++ { 66000000, 0x15 }, ++ { 67000000, 0x16 }, ++ { 68000000, 0x17 }, ++ { 70000000, 0x19 }, ++ { 71000000, 0x1C }, ++ { 72000000, 0x1D }, ++ { 73000000, 0x1F }, ++ { 74000000, 0x20 }, ++ { 75000000, 0x21 }, ++ { 76000000, 0x24 }, ++ { 77000000, 0x25 }, ++ { 78000000, 0x27 }, ++ { 80000000, 0x28 }, ++ { 81000000, 0x29 }, ++ { 82000000, 0x2D }, ++ { 83000000, 0x2E }, ++ { 84000000, 0x2F }, ++ { 85000000, 0x31 }, ++ { 86000000, 0x33 }, ++ { 87000000, 0x34 }, ++ { 88000000, 0x35 }, ++ { 89000000, 0x37 }, ++ { 90000000, 0x38 }, ++ { 91000000, 0x39 }, ++ { 93000000, 0x3C }, ++ { 94000000, 0x3E }, ++ { 95000000, 0x3F }, ++ { 96000000, 0x40 }, ++ { 97000000, 0x42 }, ++ { 99000000, 0x45 }, ++ { 100000000, 0x46 }, ++ { 102000000, 0x48 }, ++ { 103000000, 0x4A }, ++ { 105000000, 0x4D }, ++ { 106000000, 0x4E }, ++ { 107000000, 0x50 }, ++ { 108000000, 0x51 }, ++ { 110000000, 0x54 }, ++ { 111000000, 0x56 }, ++ { 112000000, 0x57 }, ++ { 113000000, 0x58 }, ++ { 114000000, 0x59 }, ++ { 115000000, 0x5C }, ++ { 116000000, 0x5D }, ++ { 117000000, 0x5F }, ++ { 119000000, 0x60 }, ++ { 120000000, 0x64 }, ++ { 121000000, 0x65 }, ++ { 122000000, 0x66 }, ++ { 123000000, 0x68 }, ++ { 124000000, 0x69 }, ++ { 125000000, 0x6C }, ++ { 126000000, 0x6D }, ++ { 127000000, 0x6E }, ++ { 128000000, 0x70 }, ++ { 129000000, 0x71 }, ++ { 130000000, 0x75 }, ++ { 131000000, 0x77 }, ++ { 132000000, 0x78 }, ++ { 133000000, 0x7B }, ++ { 134000000, 0x7E }, ++ { 135000000, 0x81 }, ++ { 136000000, 0x82 }, ++ { 137000000, 0x87 }, ++ { 138000000, 0x88 }, ++ { 139000000, 0x8D }, ++ { 140000000, 0x8E }, ++ { 141000000, 0x91 }, ++ { 142000000, 0x95 }, ++ { 143000000, 0x9A }, ++ { 144000000, 0x9D }, ++ { 145000000, 0xA1 }, ++ { 146000000, 0xA2 }, ++ { 147000000, 0xA4 }, ++ { 148000000, 0xA9 }, ++ { 149000000, 0xAE }, ++ { 150000000, 0xB0 }, ++ { 151000000, 0xB1 }, ++ { 152000000, 0xB7 }, ++ { 152600000, 0xBD }, ++ { 154000000, 0x20 }, ++ { 155000000, 0x22 }, ++ { 156000000, 0x24 }, ++ { 157000000, 0x25 }, ++ { 158000000, 0x27 }, ++ { 159000000, 0x29 }, ++ { 160000000, 0x2C }, ++ { 161000000, 0x2D }, ++ { 163000000, 0x2E }, ++ { 164000000, 0x2F }, ++ { 164700000, 0x30 }, ++ { 166000000, 0x11 }, ++ { 167000000, 0x12 }, ++ { 168000000, 0x13 }, ++ { 169000000, 0x14 }, ++ { 170000000, 0x15 }, ++ { 172000000, 0x16 }, ++ { 173000000, 0x17 }, ++ { 174000000, 0x18 }, ++ { 175000000, 0x1A }, ++ { 176000000, 0x1B }, ++ { 178000000, 0x1D }, ++ { 179000000, 0x1E }, ++ { 180000000, 0x1F }, ++ { 181000000, 0x20 }, ++ { 182000000, 0x21 }, ++ { 183000000, 0x22 }, ++ { 184000000, 0x24 }, ++ { 185000000, 0x25 }, ++ { 186000000, 0x26 }, ++ { 187000000, 0x27 }, ++ { 188000000, 0x29 }, ++ { 189000000, 0x2A }, ++ { 190000000, 0x2C }, ++ { 191000000, 0x2D }, ++ { 192000000, 0x2E }, ++ { 193000000, 0x2F }, ++ { 194000000, 0x30 }, ++ { 195000000, 0x33 }, ++ { 196000000, 0x35 }, ++ { 198000000, 0x36 }, ++ { 200000000, 0x38 }, ++ { 201000000, 0x3C }, ++ { 202000000, 0x3D }, ++ { 203500000, 0x3E }, ++ { 206000000, 0x0E }, ++ { 208000000, 0x0F }, ++ { 212000000, 0x10 }, ++ { 216000000, 0x11 }, ++ { 217000000, 0x12 }, ++ { 218000000, 0x13 }, ++ { 220000000, 0x14 }, ++ { 222000000, 0x15 }, ++ { 225000000, 0x16 }, ++ { 228000000, 0x17 }, ++ { 231000000, 0x18 }, ++ { 234000000, 0x19 }, ++ { 235000000, 0x1A }, ++ { 236000000, 0x1B }, ++ { 237000000, 0x1C }, ++ { 240000000, 0x1D }, ++ { 242000000, 0x1E }, ++ { 244000000, 0x1F }, ++ { 247000000, 0x20 }, ++ { 249000000, 0x21 }, ++ { 252000000, 0x22 }, ++ { 253000000, 0x23 }, ++ { 254000000, 0x24 }, ++ { 256000000, 0x25 }, ++ { 259000000, 0x26 }, ++ { 262000000, 0x27 }, ++ { 264000000, 0x28 }, ++ { 267000000, 0x29 }, ++ { 269000000, 0x2A }, ++ { 271000000, 0x2B }, ++ { 273000000, 0x2C }, ++ { 275000000, 0x2D }, ++ { 277000000, 0x2E }, ++ { 279000000, 0x2F }, ++ { 282000000, 0x30 }, ++ { 284000000, 0x31 }, ++ { 286000000, 0x32 }, ++ { 287000000, 0x33 }, ++ { 290000000, 0x34 }, ++ { 293000000, 0x35 }, ++ { 295000000, 0x36 }, ++ { 297000000, 0x37 }, ++ { 300000000, 0x38 }, ++ { 303000000, 0x39 }, ++ { 305000000, 0x3A }, ++ { 306000000, 0x3B }, ++ { 307000000, 0x3C }, ++ { 310000000, 0x3D }, ++ { 312000000, 0x3E }, ++ { 315000000, 0x3F }, ++ { 318000000, 0x40 }, ++ { 320000000, 0x41 }, ++ { 323000000, 0x42 }, ++ { 324000000, 0x43 }, ++ { 325000000, 0x44 }, ++ { 327000000, 0x45 }, ++ { 331000000, 0x46 }, ++ { 334000000, 0x47 }, ++ { 337000000, 0x48 }, ++ { 339000000, 0x49 }, ++ { 340000000, 0x4A }, ++ { 341000000, 0x4B }, ++ { 343000000, 0x4C }, ++ { 345000000, 0x4D }, ++ { 349000000, 0x4E }, ++ { 352000000, 0x4F }, ++ { 353000000, 0x50 }, ++ { 355000000, 0x51 }, ++ { 357000000, 0x52 }, ++ { 359000000, 0x53 }, ++ { 361000000, 0x54 }, ++ { 362000000, 0x55 }, ++ { 364000000, 0x56 }, ++ { 368000000, 0x57 }, ++ { 370000000, 0x58 }, ++ { 372000000, 0x59 }, ++ { 375000000, 0x5A }, ++ { 376000000, 0x5B }, ++ { 377000000, 0x5C }, ++ { 379000000, 0x5D }, ++ { 382000000, 0x5E }, ++ { 384000000, 0x5F }, ++ { 385000000, 0x60 }, ++ { 386000000, 0x61 }, ++ { 388000000, 0x62 }, ++ { 390000000, 0x63 }, ++ { 393000000, 0x64 }, ++ { 394000000, 0x65 }, ++ { 396000000, 0x66 }, ++ { 397000000, 0x67 }, ++ { 398000000, 0x68 }, ++ { 400000000, 0x69 }, ++ { 402000000, 0x6A }, ++ { 403000000, 0x6B }, ++ { 407000000, 0x6C }, ++ { 408000000, 0x6D }, ++ { 409000000, 0x6E }, ++ { 410000000, 0x6F }, ++ { 411000000, 0x70 }, ++ { 412000000, 0x71 }, ++ { 413000000, 0x72 }, ++ { 414000000, 0x73 }, ++ { 417000000, 0x74 }, ++ { 418000000, 0x75 }, ++ { 420000000, 0x76 }, ++ { 422000000, 0x77 }, ++ { 423000000, 0x78 }, ++ { 424000000, 0x79 }, ++ { 427000000, 0x7A }, ++ { 428000000, 0x7B }, ++ { 429000000, 0x7D }, ++ { 432000000, 0x7F }, ++ { 434000000, 0x80 }, ++ { 435000000, 0x81 }, ++ { 436000000, 0x83 }, ++ { 437000000, 0x84 }, ++ { 438000000, 0x85 }, ++ { 439000000, 0x86 }, ++ { 440000000, 0x87 }, ++ { 441000000, 0x88 }, ++ { 442000000, 0x89 }, ++ { 445000000, 0x8A }, ++ { 446000000, 0x8B }, ++ { 447000000, 0x8C }, ++ { 448000000, 0x8E }, ++ { 449000000, 0x8F }, ++ { 450000000, 0x90 }, ++ { 452000000, 0x91 }, ++ { 453000000, 0x93 }, ++ { 454000000, 0x94 }, ++ { 456000000, 0x96 }, ++ { 457800000, 0x98 }, ++ { 461000000, 0x11 }, ++ { 468000000, 0x12 }, ++ { 472000000, 0x13 }, ++ { 473000000, 0x14 }, ++ { 474000000, 0x15 }, ++ { 481000000, 0x16 }, ++ { 486000000, 0x17 }, ++ { 491000000, 0x18 }, ++ { 498000000, 0x19 }, ++ { 499000000, 0x1A }, ++ { 501000000, 0x1B }, ++ { 506000000, 0x1C }, ++ { 511000000, 0x1D }, ++ { 516000000, 0x1E }, ++ { 520000000, 0x1F }, ++ { 521000000, 0x20 }, ++ { 525000000, 0x21 }, ++ { 529000000, 0x22 }, ++ { 533000000, 0x23 }, ++ { 539000000, 0x24 }, ++ { 541000000, 0x25 }, ++ { 547000000, 0x26 }, ++ { 549000000, 0x27 }, ++ { 551000000, 0x28 }, ++ { 556000000, 0x29 }, ++ { 561000000, 0x2A }, ++ { 563000000, 0x2B }, ++ { 565000000, 0x2C }, ++ { 569000000, 0x2D }, ++ { 571000000, 0x2E }, ++ { 577000000, 0x2F }, ++ { 580000000, 0x30 }, ++ { 582000000, 0x31 }, ++ { 584000000, 0x32 }, ++ { 588000000, 0x33 }, ++ { 591000000, 0x34 }, ++ { 596000000, 0x35 }, ++ { 598000000, 0x36 }, ++ { 603000000, 0x37 }, ++ { 604000000, 0x38 }, ++ { 606000000, 0x39 }, ++ { 612000000, 0x3A }, ++ { 615000000, 0x3B }, ++ { 617000000, 0x3C }, ++ { 621000000, 0x3D }, ++ { 622000000, 0x3E }, ++ { 625000000, 0x3F }, ++ { 632000000, 0x40 }, ++ { 633000000, 0x41 }, ++ { 634000000, 0x42 }, ++ { 642000000, 0x43 }, ++ { 643000000, 0x44 }, ++ { 647000000, 0x45 }, ++ { 650000000, 0x46 }, ++ { 652000000, 0x47 }, ++ { 657000000, 0x48 }, ++ { 661000000, 0x49 }, ++ { 662000000, 0x4A }, ++ { 665000000, 0x4B }, ++ { 667000000, 0x4C }, ++ { 670000000, 0x4D }, ++ { 673000000, 0x4E }, ++ { 676000000, 0x4F }, ++ { 677000000, 0x50 }, ++ { 681000000, 0x51 }, ++ { 683000000, 0x52 }, ++ { 686000000, 0x53 }, ++ { 688000000, 0x54 }, ++ { 689000000, 0x55 }, ++ { 691000000, 0x56 }, ++ { 695000000, 0x57 }, ++ { 698000000, 0x58 }, ++ { 703000000, 0x59 }, ++ { 704000000, 0x5A }, ++ { 705000000, 0x5B }, ++ { 707000000, 0x5C }, ++ { 710000000, 0x5D }, ++ { 712000000, 0x5E }, ++ { 717000000, 0x5F }, ++ { 718000000, 0x60 }, ++ { 721000000, 0x61 }, ++ { 722000000, 0x62 }, ++ { 723000000, 0x63 }, ++ { 725000000, 0x64 }, ++ { 727000000, 0x65 }, ++ { 730000000, 0x66 }, ++ { 732000000, 0x67 }, ++ { 735000000, 0x68 }, ++ { 740000000, 0x69 }, ++ { 741000000, 0x6A }, ++ { 742000000, 0x6B }, ++ { 743000000, 0x6C }, ++ { 745000000, 0x6D }, ++ { 747000000, 0x6E }, ++ { 748000000, 0x6F }, ++ { 750000000, 0x70 }, ++ { 752000000, 0x71 }, ++ { 754000000, 0x72 }, ++ { 757000000, 0x73 }, ++ { 758000000, 0x74 }, ++ { 760000000, 0x75 }, ++ { 763000000, 0x76 }, ++ { 764000000, 0x77 }, ++ { 766000000, 0x78 }, ++ { 767000000, 0x79 }, ++ { 768000000, 0x7A }, ++ { 773000000, 0x7B }, ++ { 774000000, 0x7C }, ++ { 776000000, 0x7D }, ++ { 777000000, 0x7E }, ++ { 778000000, 0x7F }, ++ { 779000000, 0x80 }, ++ { 781000000, 0x81 }, ++ { 783000000, 0x82 }, ++ { 784000000, 0x83 }, ++ { 785000000, 0x84 }, ++ { 786000000, 0x85 }, ++ { 793000000, 0x86 }, ++ { 794000000, 0x87 }, ++ { 795000000, 0x88 }, ++ { 797000000, 0x89 }, ++ { 799000000, 0x8A }, ++ { 801000000, 0x8B }, ++ { 802000000, 0x8C }, ++ { 803000000, 0x8D }, ++ { 804000000, 0x8E }, ++ { 810000000, 0x90 }, ++ { 811000000, 0x91 }, ++ { 812000000, 0x92 }, ++ { 814000000, 0x93 }, ++ { 816000000, 0x94 }, ++ { 817000000, 0x96 }, ++ { 818000000, 0x97 }, ++ { 820000000, 0x98 }, ++ { 821000000, 0x99 }, ++ { 822000000, 0x9A }, ++ { 828000000, 0x9B }, ++ { 829000000, 0x9D }, ++ { 830000000, 0x9F }, ++ { 831000000, 0xA0 }, ++ { 833000000, 0xA1 }, ++ { 835000000, 0xA2 }, ++ { 836000000, 0xA3 }, ++ { 837000000, 0xA4 }, ++ { 838000000, 0xA6 }, ++ { 840000000, 0xA8 }, ++ { 842000000, 0xA9 }, ++ { 845000000, 0xAA }, ++ { 846000000, 0xAB }, ++ { 847000000, 0xAD }, ++ { 848000000, 0xAE }, ++ { 852000000, 0xAF }, ++ { 853000000, 0xB0 }, ++ { 858000000, 0xB1 }, ++ { 860000000, 0xB2 }, ++ { 861000000, 0xB3 }, ++ { 862000000, 0xB4 }, ++ { 863000000, 0xB6 }, ++ { 864000000, 0xB8 }, ++ { 865000000, 0xB9 }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++ ++static struct SMap2 m_KM_Map[] = { ++ { 47900000, 3, 2 }, ++ { 61100000, 3, 1 }, ++ { 350000000, 3, 0 }, ++ { 720000000, 2, 1 }, ++ { 865000000, 3, 3 }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++static struct SMap2 m_Main_PLL_Map[] = { ++ { 33125000, 0x57, 0xF0 }, ++ { 35500000, 0x56, 0xE0 }, ++ { 38188000, 0x55, 0xD0 }, ++ { 41375000, 0x54, 0xC0 }, ++ { 45125000, 0x53, 0xB0 }, ++ { 49688000, 0x52, 0xA0 }, ++ { 55188000, 0x51, 0x90 }, ++ { 62125000, 0x50, 0x80 }, ++ { 66250000, 0x47, 0x78 }, ++ { 71000000, 0x46, 0x70 }, ++ { 76375000, 0x45, 0x68 }, ++ { 82750000, 0x44, 0x60 }, ++ { 90250000, 0x43, 0x58 }, ++ { 99375000, 0x42, 0x50 }, ++ { 110375000, 0x41, 0x48 }, ++ { 124250000, 0x40, 0x40 }, ++ { 132500000, 0x37, 0x3C }, ++ { 142000000, 0x36, 0x38 }, ++ { 152750000, 0x35, 0x34 }, ++ { 165500000, 0x34, 0x30 }, ++ { 180500000, 0x33, 0x2C }, ++ { 198750000, 0x32, 0x28 }, ++ { 220750000, 0x31, 0x24 }, ++ { 248500000, 0x30, 0x20 }, ++ { 265000000, 0x27, 0x1E }, ++ { 284000000, 0x26, 0x1C }, ++ { 305500000, 0x25, 0x1A }, ++ { 331000000, 0x24, 0x18 }, ++ { 361000000, 0x23, 0x16 }, ++ { 397500000, 0x22, 0x14 }, ++ { 441500000, 0x21, 0x12 }, ++ { 497000000, 0x20, 0x10 }, ++ { 530000000, 0x17, 0x0F }, ++ { 568000000, 0x16, 0x0E }, ++ { 611000000, 0x15, 0x0D }, ++ { 662000000, 0x14, 0x0C }, ++ { 722000000, 0x13, 0x0B }, ++ { 795000000, 0x12, 0x0A }, ++ { 883000000, 0x11, 0x09 }, ++ { 994000000, 0x10, 0x08 }, ++ { 0, 0x00, 0x00 }, /* Table End */ ++}; ++ ++static struct SMap2 m_Cal_PLL_Map[] = { ++ { 33813000, 0xDD, 0xD0 }, ++ { 36625000, 0xDC, 0xC0 }, ++ { 39938000, 0xDB, 0xB0 }, ++ { 43938000, 0xDA, 0xA0 }, ++ { 48813000, 0xD9, 0x90 }, ++ { 54938000, 0xD8, 0x80 }, ++ { 62813000, 0xD3, 0x70 }, ++ { 67625000, 0xCD, 0x68 }, ++ { 73250000, 0xCC, 0x60 }, ++ { 79875000, 0xCB, 0x58 }, ++ { 87875000, 0xCA, 0x50 }, ++ { 97625000, 0xC9, 0x48 }, ++ { 109875000, 0xC8, 0x40 }, ++ { 125625000, 0xC3, 0x38 }, ++ { 135250000, 0xBD, 0x34 }, ++ { 146500000, 0xBC, 0x30 }, ++ { 159750000, 0xBB, 0x2C }, ++ { 175750000, 0xBA, 0x28 }, ++ { 195250000, 0xB9, 0x24 }, ++ { 219750000, 0xB8, 0x20 }, ++ { 251250000, 0xB3, 0x1C }, ++ { 270500000, 0xAD, 0x1A }, ++ { 293000000, 0xAC, 0x18 }, ++ { 319500000, 0xAB, 0x16 }, ++ { 351500000, 0xAA, 0x14 }, ++ { 390500000, 0xA9, 0x12 }, ++ { 439500000, 0xA8, 0x10 }, ++ { 502500000, 0xA3, 0x0E }, ++ { 541000000, 0x9D, 0x0D }, ++ { 586000000, 0x9C, 0x0C }, ++ { 639000000, 0x9B, 0x0B }, ++ { 703000000, 0x9A, 0x0A }, ++ { 781000000, 0x99, 0x09 }, ++ { 879000000, 0x98, 0x08 }, ++ { 0, 0x00, 0x00 }, /* Table End */ ++}; ++ ++static struct SMap m_GainTaper_Map[] = { ++ { 45400000, 0x1F }, ++ { 45800000, 0x1E }, ++ { 46200000, 0x1D }, ++ { 46700000, 0x1C }, ++ { 47100000, 0x1B }, ++ { 47500000, 0x1A }, ++ { 47900000, 0x19 }, ++ { 49600000, 0x17 }, ++ { 51200000, 0x16 }, ++ { 52900000, 0x15 }, ++ { 54500000, 0x14 }, ++ { 56200000, 0x13 }, ++ { 57800000, 0x12 }, ++ { 59500000, 0x11 }, ++ { 61100000, 0x10 }, ++ { 67600000, 0x0D }, ++ { 74200000, 0x0C }, ++ { 80700000, 0x0B }, ++ { 87200000, 0x0A }, ++ { 93800000, 0x09 }, ++ { 100300000, 0x08 }, ++ { 106900000, 0x07 }, ++ { 113400000, 0x06 }, ++ { 119900000, 0x05 }, ++ { 126500000, 0x04 }, ++ { 133000000, 0x03 }, ++ { 139500000, 0x02 }, ++ { 146100000, 0x01 }, ++ { 152600000, 0x00 }, ++ { 154300000, 0x1F }, ++ { 156100000, 0x1E }, ++ { 157800000, 0x1D }, ++ { 159500000, 0x1C }, ++ { 161200000, 0x1B }, ++ { 163000000, 0x1A }, ++ { 164700000, 0x19 }, ++ { 170200000, 0x17 }, ++ { 175800000, 0x16 }, ++ { 181300000, 0x15 }, ++ { 186900000, 0x14 }, ++ { 192400000, 0x13 }, ++ { 198000000, 0x12 }, ++ { 203500000, 0x11 }, ++ { 216200000, 0x14 }, ++ { 228900000, 0x13 }, ++ { 241600000, 0x12 }, ++ { 254400000, 0x11 }, ++ { 267100000, 0x10 }, ++ { 279800000, 0x0F }, ++ { 292500000, 0x0E }, ++ { 305200000, 0x0D }, ++ { 317900000, 0x0C }, ++ { 330700000, 0x0B }, ++ { 343400000, 0x0A }, ++ { 356100000, 0x09 }, ++ { 368800000, 0x08 }, ++ { 381500000, 0x07 }, ++ { 394200000, 0x06 }, ++ { 406900000, 0x05 }, ++ { 419700000, 0x04 }, ++ { 432400000, 0x03 }, ++ { 445100000, 0x02 }, ++ { 457800000, 0x01 }, ++ { 476300000, 0x19 }, ++ { 494800000, 0x18 }, ++ { 513300000, 0x17 }, ++ { 531800000, 0x16 }, ++ { 550300000, 0x15 }, ++ { 568900000, 0x14 }, ++ { 587400000, 0x13 }, ++ { 605900000, 0x12 }, ++ { 624400000, 0x11 }, ++ { 642900000, 0x10 }, ++ { 661400000, 0x0F }, ++ { 679900000, 0x0E }, ++ { 698400000, 0x0D }, ++ { 716900000, 0x0C }, ++ { 735400000, 0x0B }, ++ { 753900000, 0x0A }, ++ { 772500000, 0x09 }, ++ { 791000000, 0x08 }, ++ { 809500000, 0x07 }, ++ { 828000000, 0x06 }, ++ { 846500000, 0x05 }, ++ { 865000000, 0x04 }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++static struct SMap m_RF_Cal_DC_Over_DT_Map[] = { ++ { 47900000, 0x00 }, ++ { 55000000, 0x00 }, ++ { 61100000, 0x0A }, ++ { 64000000, 0x0A }, ++ { 82000000, 0x14 }, ++ { 84000000, 0x19 }, ++ { 119000000, 0x1C }, ++ { 124000000, 0x20 }, ++ { 129000000, 0x2A }, ++ { 134000000, 0x32 }, ++ { 139000000, 0x39 }, ++ { 144000000, 0x3E }, ++ { 149000000, 0x3F }, ++ { 152600000, 0x40 }, ++ { 154000000, 0x40 }, ++ { 164700000, 0x41 }, ++ { 203500000, 0x32 }, ++ { 353000000, 0x19 }, ++ { 356000000, 0x1A }, ++ { 359000000, 0x1B }, ++ { 363000000, 0x1C }, ++ { 366000000, 0x1D }, ++ { 369000000, 0x1E }, ++ { 373000000, 0x1F }, ++ { 376000000, 0x20 }, ++ { 379000000, 0x21 }, ++ { 383000000, 0x22 }, ++ { 386000000, 0x23 }, ++ { 389000000, 0x24 }, ++ { 393000000, 0x25 }, ++ { 396000000, 0x26 }, ++ { 399000000, 0x27 }, ++ { 402000000, 0x28 }, ++ { 404000000, 0x29 }, ++ { 407000000, 0x2A }, ++ { 409000000, 0x2B }, ++ { 412000000, 0x2C }, ++ { 414000000, 0x2D }, ++ { 417000000, 0x2E }, ++ { 419000000, 0x2F }, ++ { 422000000, 0x30 }, ++ { 424000000, 0x31 }, ++ { 427000000, 0x32 }, ++ { 429000000, 0x33 }, ++ { 432000000, 0x34 }, ++ { 434000000, 0x35 }, ++ { 437000000, 0x36 }, ++ { 439000000, 0x37 }, ++ { 442000000, 0x38 }, ++ { 444000000, 0x39 }, ++ { 447000000, 0x3A }, ++ { 449000000, 0x3B }, ++ { 457800000, 0x3C }, ++ { 465000000, 0x0F }, ++ { 477000000, 0x12 }, ++ { 483000000, 0x14 }, ++ { 502000000, 0x19 }, ++ { 508000000, 0x1B }, ++ { 519000000, 0x1C }, ++ { 522000000, 0x1D }, ++ { 524000000, 0x1E }, ++ { 534000000, 0x1F }, ++ { 549000000, 0x20 }, ++ { 554000000, 0x22 }, ++ { 584000000, 0x24 }, ++ { 589000000, 0x26 }, ++ { 658000000, 0x27 }, ++ { 664000000, 0x2C }, ++ { 669000000, 0x2D }, ++ { 699000000, 0x2E }, ++ { 704000000, 0x30 }, ++ { 709000000, 0x31 }, ++ { 714000000, 0x32 }, ++ { 724000000, 0x33 }, ++ { 729000000, 0x36 }, ++ { 739000000, 0x38 }, ++ { 744000000, 0x39 }, ++ { 749000000, 0x3B }, ++ { 754000000, 0x3C }, ++ { 759000000, 0x3D }, ++ { 764000000, 0x3E }, ++ { 769000000, 0x3F }, ++ { 774000000, 0x40 }, ++ { 779000000, 0x41 }, ++ { 784000000, 0x43 }, ++ { 789000000, 0x46 }, ++ { 794000000, 0x48 }, ++ { 799000000, 0x4B }, ++ { 804000000, 0x4F }, ++ { 809000000, 0x54 }, ++ { 814000000, 0x59 }, ++ { 819000000, 0x5D }, ++ { 824000000, 0x61 }, ++ { 829000000, 0x68 }, ++ { 834000000, 0x6E }, ++ { 839000000, 0x75 }, ++ { 844000000, 0x7E }, ++ { 849000000, 0x82 }, ++ { 854000000, 0x84 }, ++ { 859000000, 0x8F }, ++ { 865000000, 0x9A }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++ ++static struct SMap m_IR_Meas_Map[] = { ++ { 200000000, 0x05 }, ++ { 400000000, 0x06 }, ++ { 865000000, 0x07 }, ++ { 0, 0x00 }, /* Table End */ ++}; ++ ++static struct SMap2 m_CID_Target_Map[] = { ++ { 46000000, 0x04, 18 }, ++ { 52200000, 0x0A, 15 }, ++ { 70100000, 0x01, 40 }, ++ { 136800000, 0x18, 40 }, ++ { 156700000, 0x18, 40 }, ++ { 186250000, 0x0A, 40 }, ++ { 230000000, 0x0A, 40 }, ++ { 345000000, 0x18, 40 }, ++ { 426000000, 0x0E, 40 }, ++ { 489500000, 0x1E, 40 }, ++ { 697500000, 0x32, 40 }, ++ { 842000000, 0x3A, 40 }, ++ { 0, 0x00, 0 }, /* Table End */ ++}; ++ ++static struct SRFBandMap m_RF_Band_Map[7] = { ++ { 47900000, 46000000, 0, 0}, ++ { 61100000, 52200000, 0, 0}, ++ { 152600000, 70100000, 136800000, 0}, ++ { 164700000, 156700000, 0, 0}, ++ { 203500000, 186250000, 0, 0}, ++ { 457800000, 230000000, 345000000, 426000000}, ++ { 865000000, 489500000, 697500000, 842000000}, ++}; ++ ++u8 m_Thermometer_Map_1[16] = { ++ 60, 62, 66, 64, ++ 74, 72, 68, 70, ++ 90, 88, 84, 86, ++ 76, 78, 82, 80, ++}; ++ ++u8 m_Thermometer_Map_2[16] = { ++ 92, 94, 98, 96, ++ 106, 104, 100, 102, ++ 122, 120, 116, 118, ++ 108, 110, 114, 112, ++}; +diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c +new file mode 100644 +index 0000000..63cc123 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda665x.c +@@ -0,0 +1,254 @@ ++/* ++ TDA665x tuner driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda665x.h" ++ ++struct tda665x_state { ++ struct dvb_frontend *fe; ++ struct i2c_adapter *i2c; ++ const struct tda665x_config *config; ++ ++ u32 frequency; ++ u32 bandwidth; ++}; ++ ++static int tda665x_read(struct tda665x_state *state, u8 *buf) ++{ ++ const struct tda665x_config *config = state->config; ++ int err = 0; ++ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD, .buf = buf, .len = 2 }; ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) ++ goto exit; ++ ++ return err; ++exit: ++ printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); ++ return err; ++} ++ ++static int tda665x_write(struct tda665x_state *state, u8 *buf, u8 length) ++{ ++ const struct tda665x_config *config = state->config; ++ int err = 0; ++ struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = length }; ++ ++ err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) ++ goto exit; ++ ++ return err; ++exit: ++ printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); ++ return err; ++} ++ ++static int tda665x_get_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *tstate) ++{ ++ struct tda665x_state *state = fe->tuner_priv; ++ int err = 0; ++ ++ switch (param) { ++ case DVBFE_TUNER_FREQUENCY: ++ tstate->frequency = state->frequency; ++ break; ++ case DVBFE_TUNER_BANDWIDTH: ++ break; ++ default: ++ printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param); ++ err = -EINVAL; ++ break; ++ } ++ ++ return err; ++} ++ ++static int tda665x_get_status(struct dvb_frontend *fe, u32 *status) ++{ ++ struct tda665x_state *state = fe->tuner_priv; ++ u8 result = 0; ++ int err = 0; ++ ++ *status = 0; ++ ++ err = tda665x_read(state, &result); ++ if (err < 0) ++ goto exit; ++ ++ if ((result >> 6) & 0x01) { ++ printk(KERN_DEBUG "%s: Tuner Phase Locked\n", __func__); ++ *status = 1; ++ } ++ ++ return err; ++exit: ++ printk(KERN_ERR "%s: I/O Error\n", __func__); ++ return err; ++} ++ ++static int tda665x_set_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *tstate) ++{ ++ struct tda665x_state *state = fe->tuner_priv; ++ const struct tda665x_config *config = state->config; ++ u32 frequency, status = 0; ++ u8 buf[4]; ++ int err = 0; ++ ++ if (param & DVBFE_TUNER_FREQUENCY) { ++ ++ frequency = tstate->frequency; ++ if ((frequency < config->frequency_max) || (frequency > config->frequency_min)) { ++ printk(KERN_ERR "%s: Frequency beyond limits, frequency=%d\n", __func__, frequency); ++ return -EINVAL; ++ } ++ ++ frequency += config->frequency_offst; ++ frequency *= config->ref_multiplier; ++ frequency += config->ref_divider >> 1; ++ frequency /= config->ref_divider; ++ ++ buf[0] = (u8) ((frequency & 0x7f00) >> 8); ++ buf[1] = (u8) (frequency & 0x00ff) >> 0; ++ buf[2] = 0x80 | 0x40 | 0x02; ++ buf[3] = 0x00; ++ ++ /* restore frequency */ ++ frequency = tstate->frequency; ++ ++ if (frequency < 153000000) { ++ /* VHF-L */ ++ buf[3] |= 0x01; /* fc, Low Band, 47 - 153 MHz */ ++ if (frequency < 68000000) ++ buf[3] |= 0x40; /* 83uA */ ++ if (frequency < 1040000000) ++ buf[3] |= 0x60; /* 122uA */ ++ if (frequency < 1250000000) ++ buf[3] |= 0x80; /* 163uA */ ++ else ++ buf[3] |= 0xa0; /* 254uA */ ++ } else if (frequency < 438000000) { ++ /* VHF-H */ ++ buf[3] |= 0x02; /* fc, Mid Band, 153 - 438 MHz */ ++ if (frequency < 230000000) ++ buf[3] |= 0x40; ++ if (frequency < 300000000) ++ buf[3] |= 0x60; ++ else ++ buf[3] |= 0x80; ++ } else { ++ /* UHF */ ++ buf[3] |= 0x04; /* fc, High Band, 438 - 862 MHz */ ++ if (frequency < 470000000) ++ buf[3] |= 0x60; ++ if (frequency < 526000000) ++ buf[3] |= 0x80; ++ else ++ buf[3] |= 0xa0; ++ } ++ ++ /* Set params */ ++ err = tda665x_write(state, buf, 5); ++ if (err < 0) ++ goto exit; ++ ++ /* sleep for some time */ ++ printk(KERN_DEBUG "%s: Waiting to Phase LOCK\n", __func__); ++ msleep(20); ++ /* check status */ ++ err = tda665x_get_status(fe, &status); ++ if (err < 0) ++ goto exit; ++ ++ if (status == 1) { ++ printk(KERN_DEBUG "%s: Tuner Phase locked: status=%d\n", __func__, status); ++ state->frequency = frequency; /* cache successful state */ ++ } else { ++ printk(KERN_ERR "%s: No Phase lock: status=%d\n", __func__, status); ++ } ++ } else { ++ printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param); ++ return -EINVAL; ++ } ++ ++ return 0; ++exit: ++ printk(KERN_ERR "%s: I/O Error\n", __func__); ++ return err; ++} ++ ++static int tda665x_release(struct dvb_frontend *fe) ++{ ++ struct tda665x_state *state = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(state); ++ return 0; ++} ++ ++static struct dvb_tuner_ops tda665x_ops = { ++ ++ .set_state = tda665x_set_state, ++ .get_state = tda665x_get_state, ++ .get_status = tda665x_get_status, ++ .release = tda665x_release ++}; ++ ++struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, ++ const struct tda665x_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct tda665x_state *state = NULL; ++ struct dvb_tuner_info *info; ++ ++ state = kzalloc(sizeof(struct tda665x_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->config = config; ++ state->i2c = i2c; ++ state->fe = fe; ++ fe->tuner_priv = state; ++ fe->ops.tuner_ops = tda665x_ops; ++ info = &fe->ops.tuner_ops.info; ++ ++ memcpy(info->name, config->name, sizeof(config->name)); ++ info->frequency_min = config->frequency_min; ++ info->frequency_max = config->frequency_max; ++ info->frequency_step = config->frequency_offst; ++ ++ printk(KERN_DEBUG "%s: Attaching TDA665x (%s) tuner\n", __func__, info->name); ++ ++ return fe; ++} ++EXPORT_SYMBOL(tda665x_attach); ++ ++MODULE_DESCRIPTION("TDA665x driver"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tda665x.h b/drivers/media/dvb-frontends/tda665x.h +new file mode 100644 +index 0000000..ec7927a +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda665x.h +@@ -0,0 +1,52 @@ ++/* ++ TDA665x tuner driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __TDA665x_H ++#define __TDA665x_H ++ ++struct tda665x_config { ++ char name[128]; ++ ++ u8 addr; ++ u32 frequency_min; ++ u32 frequency_max; ++ u32 frequency_offst; ++ u32 ref_multiplier; ++ u32 ref_divider; ++}; ++ ++#if defined(CONFIG_DVB_TDA665x) || (defined(CONFIG_DVB_TDA665x_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, ++ const struct tda665x_config *config, ++ struct i2c_adapter *i2c); ++ ++#else ++ ++static inline struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, ++ const struct tda665x_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif /* CONFIG_DVB_TDA665x */ ++ ++#endif /* __TDA665x_H */ +diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c +new file mode 100644 +index 0000000..9d08350 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda8083.c +@@ -0,0 +1,487 @@ ++/* ++ Driver for Philips TDA8083 based QPSK Demodulator ++ ++ Copyright (C) 2001 Convergence Integrated Media GmbH ++ ++ written by Ralph Metzler ++ ++ adoption to the new DVB frontend API and diagnostic ioctl's ++ by Holger Waechtler ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "tda8083.h" ++ ++ ++struct tda8083_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct tda8083_config* config; ++ struct dvb_frontend frontend; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "tda8083: " args); \ ++ } while (0) ++ ++ ++static u8 tda8083_init_tab [] = { ++ 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, ++ 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, ++ 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, ++ 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, ++ 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00 ++}; ++ ++ ++static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) ++{ ++ int ret; ++ u8 buf [] = { reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ dprintk ("%s: writereg error (reg %02x, ret == %i)\n", ++ __func__, reg, ret); ++ ++ return (ret != 1) ? -1 : 0; ++} ++ ++static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) ++{ ++ int ret; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ dprintk ("%s: readreg error (reg %02x, ret == %i)\n", ++ __func__, reg1, ret); ++ ++ return ret == 2 ? 0 : -1; ++} ++ ++static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) ++{ ++ u8 val; ++ ++ tda8083_readregs (state, reg, &val, 1); ++ ++ return val; ++} ++ ++static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion) ++{ ++ /* XXX FIXME: implement other modes than FEC_AUTO */ ++ if (inversion == INVERSION_AUTO) ++ return 0; ++ ++ return -EINVAL; ++} ++ ++static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) ++{ ++ if (fec == FEC_AUTO) ++ return tda8083_writereg (state, 0x07, 0xff); ++ ++ if (fec >= FEC_1_2 && fec <= FEC_8_9) ++ return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec)); ++ ++ return -EINVAL; ++} ++ ++static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state) ++{ ++ u8 index; ++ static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, ++ FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; ++ ++ index = tda8083_readreg(state, 0x0e) & 0x07; ++ ++ return fec_tab [index]; ++} ++ ++static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) ++{ ++ u32 ratio; ++ u32 tmp; ++ u8 filter; ++ ++ if (srate > 32000000) ++ srate = 32000000; ++ if (srate < 500000) ++ srate = 500000; ++ ++ filter = 0; ++ if (srate < 24000000) ++ filter = 2; ++ if (srate < 16000000) ++ filter = 3; ++ ++ tmp = 31250 << 16; ++ ratio = tmp / srate; ++ ++ tmp = (tmp % srate) << 8; ++ ratio = (ratio << 8) + tmp / srate; ++ ++ tmp = (tmp % srate) << 8; ++ ratio = (ratio << 8) + tmp / srate; ++ ++ dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); ++ ++ tda8083_writereg (state, 0x05, filter); ++ tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff); ++ tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff); ++ tda8083_writereg (state, 0x04, (ratio ) & 0xff); ++ ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 1; ++} ++ ++static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) ++{ ++ unsigned long start = jiffies; ++ ++ while (jiffies - start < timeout && ++ !(tda8083_readreg(state, 0x02) & 0x80)) ++ { ++ msleep(50); ++ } ++} ++ ++static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) ++{ ++ tda8083_writereg (state, 0x26, 0xf1); ++ ++ switch (tone) { ++ case SEC_TONE_OFF: ++ return tda8083_writereg (state, 0x29, 0x00); ++ case SEC_TONE_ON: ++ return tda8083_writereg (state, 0x29, 0x80); ++ default: ++ return -EINVAL; ++ }; ++} ++ ++static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage) ++{ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ return tda8083_writereg (state, 0x20, 0x00); ++ case SEC_VOLTAGE_18: ++ return tda8083_writereg (state, 0x20, 0x11); ++ default: ++ return -EINVAL; ++ }; ++} ++ ++static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst) ++{ ++ switch (burst) { ++ case SEC_MINI_A: ++ tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */ ++ break; ++ case SEC_MINI_B: ++ tda8083_writereg (state, 0x29, (7 << 2)); /* send B */ ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ tda8083_wait_diseqc_fifo (state, 100); ++ ++ return 0; ++} ++ ++static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd *m) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ int i; ++ ++ tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ ++ ++ for (i=0; imsg_len; i++) ++ tda8083_writereg (state, 0x23 + i, m->msg[i]); ++ ++ tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ ++ ++ tda8083_wait_diseqc_fifo (state, 100); ++ ++ return 0; ++} ++ ++static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ u8 signal = ~tda8083_readreg (state, 0x01); ++ u8 sync = tda8083_readreg (state, 0x02); ++ ++ *status = 0; ++ ++ if (signal > 10) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 0x01) ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 0x02) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 0x10) ++ *status |= FE_HAS_SYNC; ++ ++ if (sync & 0x20) /* frontend can not lock */ ++ *status |= FE_TIMEDOUT; ++ ++ if ((sync & 0x1f) == 0x1f) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ int ret; ++ u8 buf[3]; ++ ++ if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf)))) ++ return ret; ++ ++ *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; ++ ++ return 0; ++} ++ ++static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ u8 signal = ~tda8083_readreg (state, 0x01); ++ *strength = (signal << 8) | signal; ++ ++ return 0; ++} ++ ++static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ u8 _snr = tda8083_readreg (state, 0x08); ++ *snr = (_snr << 8) | _snr; ++ ++ return 0; ++} ++ ++static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ *ucblocks = tda8083_readreg(state, 0x0f); ++ if (*ucblocks == 0xff) ++ *ucblocks = 0xffffffff; ++ ++ return 0; ++} ++ ++static int tda8083_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ tda8083_set_inversion (state, p->inversion); ++ tda8083_set_fec(state, p->fec_inner); ++ tda8083_set_symbolrate(state, p->symbol_rate); ++ ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 0; ++} ++ ++static int tda8083_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ /* FIXME: get symbolrate & frequency offset...*/ ++ /*p->frequency = ???;*/ ++ p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ? ++ INVERSION_ON : INVERSION_OFF; ++ p->fec_inner = tda8083_get_fec(state); ++ /*p->symbol_rate = tda8083_get_symbolrate (state);*/ ++ ++ return 0; ++} ++ ++static int tda8083_sleep(struct dvb_frontend* fe) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ tda8083_writereg (state, 0x00, 0x02); ++ return 0; ++} ++ ++static int tda8083_init(struct dvb_frontend* fe) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ int i; ++ ++ for (i=0; i<44; i++) ++ tda8083_writereg (state, i, tda8083_init_tab[i]); ++ ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 0; ++} ++ ++static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ tda8083_send_diseqc_burst (state, burst); ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 0; ++} ++ ++static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ tda8083_set_tone (state, tone); ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 0; ++} ++ ++static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ ++ tda8083_set_voltage (state, voltage); ++ tda8083_writereg (state, 0x00, 0x3c); ++ tda8083_writereg (state, 0x00, 0x04); ++ ++ return 0; ++} ++ ++static void tda8083_release(struct dvb_frontend* fe) ++{ ++ struct tda8083_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops tda8083_ops; ++ ++struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct tda8083_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ ++ /* check if the demod is there */ ++ if ((tda8083_readreg(state, 0x00)) != 0x05) goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops tda8083_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "Philips TDA8083 DVB-S", ++ .frequency_min = 920000, /* TDA8060 */ ++ .frequency_max = 2200000, /* TDA8060 */ ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ /* .frequency_tolerance = ???,*/ ++ .symbol_rate_min = 12000000, ++ .symbol_rate_max = 30000000, ++ /* .symbol_rate_tolerance = ???,*/ ++ .caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | ++ FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | ++ FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_MUTE_TS ++ }, ++ ++ .release = tda8083_release, ++ ++ .init = tda8083_init, ++ .sleep = tda8083_sleep, ++ ++ .set_frontend = tda8083_set_frontend, ++ .get_frontend = tda8083_get_frontend, ++ ++ .read_status = tda8083_read_status, ++ .read_signal_strength = tda8083_read_signal_strength, ++ .read_snr = tda8083_read_snr, ++ .read_ber = tda8083_read_ber, ++ .read_ucblocks = tda8083_read_ucblocks, ++ ++ .diseqc_send_master_cmd = tda8083_send_diseqc_msg, ++ .diseqc_send_burst = tda8083_diseqc_send_burst, ++ .set_tone = tda8083_diseqc_set_tone, ++ .set_voltage = tda8083_diseqc_set_voltage, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); ++MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(tda8083_attach); +diff --git a/drivers/media/dvb-frontends/tda8083.h b/drivers/media/dvb-frontends/tda8083.h +new file mode 100644 +index 0000000..5a03c14 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda8083.h +@@ -0,0 +1,50 @@ ++/* ++ Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend ++ ++ Copyright (C) 2001 Convergence Integrated Media GmbH ++ ++ written by Ralph Metzler ++ ++ adoption to the new DVB frontend API and diagnostic ioctl's ++ by Holger Waechtler ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef TDA8083_H ++#define TDA8083_H ++ ++#include ++ ++struct tda8083_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++}; ++ ++#if defined(CONFIG_DVB_TDA8083) || (defined(CONFIG_DVB_TDA8083_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TDA8083 ++ ++#endif // TDA8083_H +diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c +new file mode 100644 +index 0000000..19c4888 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda8261.c +@@ -0,0 +1,230 @@ ++/* ++ TDA8261 8PSK/QPSK tuner driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "tda8261.h" ++ ++struct tda8261_state { ++ struct dvb_frontend *fe; ++ struct i2c_adapter *i2c; ++ const struct tda8261_config *config; ++ ++ /* state cache */ ++ u32 frequency; ++ u32 bandwidth; ++}; ++ ++static int tda8261_read(struct tda8261_state *state, u8 *buf) ++{ ++ const struct tda8261_config *config = state->config; ++ int err = 0; ++ struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 }; ++ ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) ++ pr_err("%s: read error, err=%d\n", __func__, err); ++ ++ return err; ++} ++ ++static int tda8261_write(struct tda8261_state *state, u8 *buf) ++{ ++ const struct tda8261_config *config = state->config; ++ int err = 0; ++ struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 }; ++ ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) ++ pr_err("%s: write error, err=%d\n", __func__, err); ++ ++ return err; ++} ++ ++static int tda8261_get_status(struct dvb_frontend *fe, u32 *status) ++{ ++ struct tda8261_state *state = fe->tuner_priv; ++ u8 result = 0; ++ int err = 0; ++ ++ *status = 0; ++ ++ if ((err = tda8261_read(state, &result)) < 0) { ++ pr_err("%s: I/O Error\n", __func__); ++ return err; ++ } ++ if ((result >> 6) & 0x01) { ++ pr_debug("%s: Tuner Phase Locked\n", __func__); ++ *status = 1; ++ } ++ ++ return err; ++} ++ ++static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */ ++static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 }; ++ ++static int tda8261_get_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *tstate) ++{ ++ struct tda8261_state *state = fe->tuner_priv; ++ int err = 0; ++ ++ switch (param) { ++ case DVBFE_TUNER_FREQUENCY: ++ tstate->frequency = state->frequency; ++ break; ++ case DVBFE_TUNER_BANDWIDTH: ++ tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */ ++ break; ++ default: ++ pr_err("%s: Unknown parameter (param=%d)\n", __func__, param); ++ err = -EINVAL; ++ break; ++ } ++ ++ return err; ++} ++ ++static int tda8261_set_state(struct dvb_frontend *fe, ++ enum tuner_param param, ++ struct tuner_state *tstate) ++{ ++ struct tda8261_state *state = fe->tuner_priv; ++ const struct tda8261_config *config = state->config; ++ u32 frequency, N, status = 0; ++ u8 buf[4]; ++ int err = 0; ++ ++ if (param & DVBFE_TUNER_FREQUENCY) { ++ /** ++ * N = Max VCO Frequency / Channel Spacing ++ * Max VCO Frequency = VCO frequency + (channel spacing - 1) ++ * (to account for half channel spacing on either side) ++ */ ++ frequency = tstate->frequency; ++ if ((frequency < 950000) || (frequency > 2150000)) { ++ pr_warn("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency); ++ return -EINVAL; ++ } ++ N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size]; ++ pr_debug("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n", ++ __func__, config->step_size, div_tab[config->step_size], N, N); ++ ++ buf[0] = (N >> 8) & 0xff; ++ buf[1] = N & 0xff; ++ buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1); ++ ++ if (frequency < 1450000) ++ buf[3] = 0x00; ++ else if (frequency < 2000000) ++ buf[3] = 0x40; ++ else if (frequency < 2150000) ++ buf[3] = 0x80; ++ ++ /* Set params */ ++ if ((err = tda8261_write(state, buf)) < 0) { ++ pr_err("%s: I/O Error\n", __func__); ++ return err; ++ } ++ /* sleep for some time */ ++ pr_debug("%s: Waiting to Phase LOCK\n", __func__); ++ msleep(20); ++ /* check status */ ++ if ((err = tda8261_get_status(fe, &status)) < 0) { ++ pr_err("%s: I/O Error\n", __func__); ++ return err; ++ } ++ if (status == 1) { ++ pr_debug("%s: Tuner Phase locked: status=%d\n", __func__, status); ++ state->frequency = frequency; /* cache successful state */ ++ } else { ++ pr_debug("%s: No Phase lock: status=%d\n", __func__, status); ++ } ++ } else { ++ pr_err("%s: Unknown parameter (param=%d)\n", __func__, param); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int tda8261_release(struct dvb_frontend *fe) ++{ ++ struct tda8261_state *state = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(state); ++ return 0; ++} ++ ++static struct dvb_tuner_ops tda8261_ops = { ++ ++ .info = { ++ .name = "TDA8261", ++// .tuner_name = NULL, ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 0 ++ }, ++ ++ .set_state = tda8261_set_state, ++ .get_state = tda8261_get_state, ++ .get_status = tda8261_get_status, ++ .release = tda8261_release ++}; ++ ++struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, ++ const struct tda8261_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct tda8261_state *state = NULL; ++ ++ if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL) ++ goto exit; ++ ++ state->config = config; ++ state->i2c = i2c; ++ state->fe = fe; ++ fe->tuner_priv = state; ++ fe->ops.tuner_ops = tda8261_ops; ++ ++ fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; ++// fe->ops.tuner_ops.tuner_name = &config->buf; ++ ++// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n", ++// __func__, fe->ops.tuner_ops.tuner_name); ++ pr_info("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); ++ ++ return fe; ++ ++exit: ++ kfree(state); ++ return NULL; ++} ++ ++EXPORT_SYMBOL(tda8261_attach); ++ ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tda8261.h b/drivers/media/dvb-frontends/tda8261.h +new file mode 100644 +index 0000000..006e453 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda8261.h +@@ -0,0 +1,55 @@ ++/* ++ TDA8261 8PSK/QPSK tuner driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __TDA8261_H ++#define __TDA8261_H ++ ++enum tda8261_step { ++ TDA8261_STEP_2000 = 0, /* 2000 kHz */ ++ TDA8261_STEP_1000, /* 1000 kHz */ ++ TDA8261_STEP_500, /* 500 kHz */ ++ TDA8261_STEP_250, /* 250 kHz */ ++ TDA8261_STEP_125 /* 125 kHz */ ++}; ++ ++struct tda8261_config { ++// u8 buf[16]; ++ u8 addr; ++ enum tda8261_step step_size; ++}; ++ ++#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, ++ const struct tda8261_config *config, ++ struct i2c_adapter *i2c); ++ ++#else ++ ++static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, ++ const struct tda8261_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++ ++#endif //CONFIG_DVB_TDA8261 ++ ++#endif// __TDA8261_H +diff --git a/drivers/media/dvb-frontends/tda8261_cfg.h b/drivers/media/dvb-frontends/tda8261_cfg.h +new file mode 100644 +index 0000000..4671074 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda8261_cfg.h +@@ -0,0 +1,84 @@ ++/* ++ TDA8261 8PSK/QPSK tuner driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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. ++*/ ++ ++static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ *frequency = t_state.frequency; ++ printk("%s: Frequency=%d\n", __func__, t_state.frequency); ++ } ++ return 0; ++} ++ ++static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) ++{ ++ struct dvb_frontend_ops *frontend_ops = NULL; ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ t_state.frequency = frequency; ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->set_state) { ++ if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ } ++ printk("%s: Frequency=%d\n", __func__, t_state.frequency); ++ return 0; ++} ++ ++static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) ++{ ++ struct dvb_frontend_ops *frontend_ops = &fe->ops; ++ struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; ++ struct tuner_state t_state; ++ int err = 0; ++ ++ if (&fe->ops) ++ frontend_ops = &fe->ops; ++ if (&frontend_ops->tuner_ops) ++ tuner_ops = &frontend_ops->tuner_ops; ++ if (tuner_ops->get_state) { ++ if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { ++ printk("%s: Invalid parameter\n", __func__); ++ return err; ++ } ++ *bandwidth = t_state.bandwidth; ++ printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); ++ } ++ return 0; ++} +diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c +new file mode 100644 +index 0000000..04bbcc2 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda826x.c +@@ -0,0 +1,188 @@ ++ /* ++ Driver for Philips tda8262/tda8263 DVBS Silicon tuners ++ ++ (c) 2006 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "tda826x.h" ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "tda826x: " args); \ ++ } while (0) ++ ++struct tda826x_priv { ++ /* i2c details */ ++ int i2c_address; ++ struct i2c_adapter *i2c; ++ u8 has_loopthrough:1; ++ u32 frequency; ++}; ++ ++static int tda826x_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int tda826x_sleep(struct dvb_frontend *fe) ++{ ++ struct tda826x_priv *priv = fe->tuner_priv; ++ int ret; ++ u8 buf [] = { 0x00, 0x8d }; ++ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 }; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (!priv->has_loopthrough) ++ buf[1] = 0xad; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { ++ dprintk("%s: i2c error\n", __func__); ++ } ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return (ret == 1) ? 0 : ret; ++} ++ ++static int tda826x_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct tda826x_priv *priv = fe->tuner_priv; ++ int ret; ++ u32 div; ++ u32 ksyms; ++ u32 bandwidth; ++ u8 buf [11]; ++ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; ++ ++ dprintk("%s:\n", __func__); ++ ++ div = (p->frequency + (1000-1)) / 1000; ++ ++ /* BW = ((1 + RO) * SR/2 + 5) * 1.3 [SR in MSPS, BW in MHz] */ ++ /* with R0 = 0.35 and some transformations: */ ++ ksyms = p->symbol_rate / 1000; ++ bandwidth = (878 * ksyms + 6500000) / 1000000 + 1; ++ if (bandwidth < 5) ++ bandwidth = 5; ++ else if (bandwidth > 36) ++ bandwidth = 36; ++ ++ buf[0] = 0x00; // subaddress ++ buf[1] = 0x09; // powerdown RSSI + the magic value 1 ++ if (!priv->has_loopthrough) ++ buf[1] |= 0x20; // power down loopthrough if not needed ++ buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO ++ buf[3] = div >> 7; ++ buf[4] = div << 1; ++ buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */ ++ buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation ++ buf[7] = 0x83; // charge pumps at high, tests off ++ buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. ++ buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL ++ buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { ++ dprintk("%s: i2c error\n", __func__); ++ } ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ priv->frequency = div * 1000; ++ ++ return (ret == 1) ? 0 : ret; ++} ++ ++static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct tda826x_priv *priv = fe->tuner_priv; ++ *frequency = priv->frequency; ++ return 0; ++} ++ ++static struct dvb_tuner_ops tda826x_tuner_ops = { ++ .info = { ++ .name = "Philips TDA826X", ++ .frequency_min = 950000, ++ .frequency_max = 2175000 ++ }, ++ .release = tda826x_release, ++ .sleep = tda826x_sleep, ++ .set_params = tda826x_set_params, ++ .get_frequency = tda826x_get_frequency, ++}; ++ ++struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough) ++{ ++ struct tda826x_priv *priv = NULL; ++ u8 b1 [] = { 0, 0 }; ++ struct i2c_msg msg[2] = { ++ { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, ++ { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } ++ }; ++ int ret; ++ ++ dprintk("%s:\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = i2c_transfer (i2c, msg, 2); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ if (ret != 2) ++ return NULL; ++ if (!(b1[1] & 0x80)) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->i2c_address = addr; ++ priv->i2c = i2c; ++ priv->has_loopthrough = has_loopthrough; ++ ++ memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops)); ++ ++ fe->tuner_priv = priv; ++ ++ return fe; ++} ++EXPORT_SYMBOL(tda826x_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++MODULE_DESCRIPTION("DVB TDA826x driver"); ++MODULE_AUTHOR("Andrew de Quincey"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tda826x.h b/drivers/media/dvb-frontends/tda826x.h +new file mode 100644 +index 0000000..89e9792 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tda826x.h +@@ -0,0 +1,53 @@ ++ /* ++ Driver for Philips tda8262/tda8263 DVBS Silicon tuners ++ ++ (c) 2006 Andrew de Quincey ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++ */ ++ ++#ifndef __DVB_TDA826X_H__ ++#define __DVB_TDA826X_H__ ++ ++#include ++#include "dvb_frontend.h" ++ ++/** ++ * Attach a tda826x tuner to the supplied frontend structure. ++ * ++ * @param fe Frontend to attach to. ++ * @param addr i2c address of the tuner. ++ * @param i2c i2c adapter to use. ++ * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector. ++ * @return FE pointer on success, NULL on failure. ++ */ ++#if defined(CONFIG_DVB_TDA826X) || (defined(CONFIG_DVB_TDA826X_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr, ++ struct i2c_adapter *i2c, ++ int has_loopthrough); ++#else ++static inline struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, ++ int addr, ++ struct i2c_adapter *i2c, ++ int has_loopthrough) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TDA826X ++ ++#endif // __DVB_TDA826X_H__ +diff --git a/drivers/media/dvb-frontends/tdhd1.h b/drivers/media/dvb-frontends/tdhd1.h +new file mode 100644 +index 0000000..1775098 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tdhd1.h +@@ -0,0 +1,74 @@ ++/* ++ * tdhd1.h - ALPS TDHD1-204A tuner support ++ * ++ * Copyright (C) 2008 Oliver Endriss ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * The project's page is at http://www.linuxtv.org ++ */ ++ ++#ifndef TDHD1_H ++#define TDHD1_H ++ ++#include "tda1004x.h" ++ ++static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name); ++ ++static struct tda1004x_config alps_tdhd1_204a_config = { ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .request_firmware = alps_tdhd1_204_request_firmware ++}; ++ ++static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct i2c_adapter *i2c = fe->tuner_priv; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ u32 div; ++ ++ div = (p->frequency + 36166666) / 166666; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x85; ++ ++ if (p->frequency >= 174000000 && p->frequency <= 230000000) ++ data[3] = 0x02; ++ else if (p->frequency >= 470000000 && p->frequency <= 823000000) ++ data[3] = 0x0C; ++ else if (p->frequency > 823000000 && p->frequency <= 862000000) ++ data[3] = 0x8C; ++ else ++ return -EINVAL; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(i2c, &msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++#endif /* TDHD1_H */ +diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c +new file mode 100644 +index 0000000..ad7ad85 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ts2020.c +@@ -0,0 +1,373 @@ ++/* ++ Montage Technology TS2020 - Silicon Tuner driver ++ Copyright (C) 2009-2012 Konstantin Dimitrov ++ ++ Copyright (C) 2009-2012 TurboSight.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 "dvb_frontend.h" ++#include "ts2020.h" ++ ++#define TS2020_XTAL_FREQ 27000 /* in kHz */ ++#define FREQ_OFFSET_LOW_SYM_RATE 3000 ++ ++struct ts2020_priv { ++ /* i2c details */ ++ int i2c_address; ++ struct i2c_adapter *i2c; ++ u8 clk_out_div; ++ u32 frequency; ++}; ++ ++static int ts2020_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int ts2020_writereg(struct dvb_frontend *fe, int reg, int data) ++{ ++ struct ts2020_priv *priv = fe->tuner_priv; ++ u8 buf[] = { reg, data }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ } ++ }; ++ int err; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ err = i2c_transfer(priv->i2c, msg, 1); ++ if (err != 1) { ++ printk(KERN_ERR ++ "%s: writereg error(err == %i, reg == 0x%02x, value == 0x%02x)\n", ++ __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int ts2020_readreg(struct dvb_frontend *fe, u8 reg) ++{ ++ struct ts2020_priv *priv = fe->tuner_priv; ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = b0, ++ .len = 1 ++ }, { ++ .addr = priv->i2c_address, ++ .flags = I2C_M_RD, ++ .buf = b1, ++ .len = 1 ++ } ++ }; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(priv->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return b1[0]; ++} ++ ++static int ts2020_sleep(struct dvb_frontend *fe) ++{ ++ struct ts2020_priv *priv = fe->tuner_priv; ++ int ret; ++ u8 buf[] = { 10, 0 }; ++ struct i2c_msg msg = { ++ .addr = priv->i2c_address, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ ret = i2c_transfer(priv->i2c, &msg, 1); ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c error\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return (ret == 1) ? 0 : ret; ++} ++ ++static int ts2020_init(struct dvb_frontend *fe) ++{ ++ struct ts2020_priv *priv = fe->tuner_priv; ++ ++ ts2020_writereg(fe, 0x42, 0x73); ++ ts2020_writereg(fe, 0x05, priv->clk_out_div); ++ ts2020_writereg(fe, 0x20, 0x27); ++ ts2020_writereg(fe, 0x07, 0x02); ++ ts2020_writereg(fe, 0x11, 0xff); ++ ts2020_writereg(fe, 0x60, 0xf9); ++ ts2020_writereg(fe, 0x08, 0x01); ++ ts2020_writereg(fe, 0x00, 0x41); ++ ++ return 0; ++} ++ ++static int ts2020_tuner_gate_ctrl(struct dvb_frontend *fe, u8 offset) ++{ ++ int ret; ++ ret = ts2020_writereg(fe, 0x51, 0x1f - offset); ++ ret |= ts2020_writereg(fe, 0x51, 0x1f); ++ ret |= ts2020_writereg(fe, 0x50, offset); ++ ret |= ts2020_writereg(fe, 0x50, 0x00); ++ msleep(20); ++ return ret; ++} ++ ++static int ts2020_set_tuner_rf(struct dvb_frontend *fe) ++{ ++ int reg; ++ ++ reg = ts2020_readreg(fe, 0x3d); ++ reg &= 0x7f; ++ if (reg < 0x16) ++ reg = 0xa1; ++ else if (reg == 0x16) ++ reg = 0x99; ++ else ++ reg = 0xf9; ++ ++ ts2020_writereg(fe, 0x60, reg); ++ reg = ts2020_tuner_gate_ctrl(fe, 0x08); ++ ++ return reg; ++} ++ ++static int ts2020_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct ts2020_priv *priv = fe->tuner_priv; ++ int ret; ++ u32 frequency = c->frequency; ++ s32 offset_khz; ++ u32 symbol_rate = (c->symbol_rate / 1000); ++ u32 f3db, gdiv28; ++ u16 value, ndiv, lpf_coeff; ++ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf; ++ u8 lo = 0x01, div4 = 0x0; ++ ++ /* Calculate frequency divider */ ++ if (frequency < 1060000) { ++ lo |= 0x10; ++ div4 = 0x1; ++ ndiv = (frequency * 14 * 4) / TS2020_XTAL_FREQ; ++ } else ++ ndiv = (frequency * 14 * 2) / TS2020_XTAL_FREQ; ++ ndiv = ndiv + ndiv % 2; ++ ndiv = ndiv - 1024; ++ ++ ret = ts2020_writereg(fe, 0x10, 0x80 | lo); ++ ++ /* Set frequency divider */ ++ ret |= ts2020_writereg(fe, 0x01, (ndiv >> 8) & 0xf); ++ ret |= ts2020_writereg(fe, 0x02, ndiv & 0xff); ++ ++ ret |= ts2020_writereg(fe, 0x03, 0x06); ++ ret |= ts2020_tuner_gate_ctrl(fe, 0x10); ++ if (ret < 0) ++ return -ENODEV; ++ ++ /* Tuner Frequency Range */ ++ ret = ts2020_writereg(fe, 0x10, lo); ++ ++ ret |= ts2020_tuner_gate_ctrl(fe, 0x08); ++ ++ /* Tuner RF */ ++ ret |= ts2020_set_tuner_rf(fe); ++ ++ gdiv28 = (TS2020_XTAL_FREQ / 1000 * 1694 + 500) / 1000; ++ ret |= ts2020_writereg(fe, 0x04, gdiv28 & 0xff); ++ ret |= ts2020_tuner_gate_ctrl(fe, 0x04); ++ if (ret < 0) ++ return -ENODEV; ++ ++ value = ts2020_readreg(fe, 0x26); ++ ++ f3db = (symbol_rate * 135) / 200 + 2000; ++ f3db += FREQ_OFFSET_LOW_SYM_RATE; ++ if (f3db < 7000) ++ f3db = 7000; ++ if (f3db > 40000) ++ f3db = 40000; ++ ++ gdiv28 = gdiv28 * 207 / (value * 2 + 151); ++ mlpf_max = gdiv28 * 135 / 100; ++ mlpf_min = gdiv28 * 78 / 100; ++ if (mlpf_max > 63) ++ mlpf_max = 63; ++ ++ lpf_coeff = 2766; ++ ++ nlpf = (f3db * gdiv28 * 2 / lpf_coeff / ++ (TS2020_XTAL_FREQ / 1000) + 1) / 2; ++ if (nlpf > 23) ++ nlpf = 23; ++ if (nlpf < 1) ++ nlpf = 1; ++ ++ lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) ++ * lpf_coeff * 2 / f3db + 1) / 2; ++ ++ if (lpf_mxdiv < mlpf_min) { ++ nlpf++; ++ lpf_mxdiv = (nlpf * (TS2020_XTAL_FREQ / 1000) ++ * lpf_coeff * 2 / f3db + 1) / 2; ++ } ++ ++ if (lpf_mxdiv > mlpf_max) ++ lpf_mxdiv = mlpf_max; ++ ++ ret = ts2020_writereg(fe, 0x04, lpf_mxdiv); ++ ret |= ts2020_writereg(fe, 0x06, nlpf); ++ ++ ret |= ts2020_tuner_gate_ctrl(fe, 0x04); ++ ++ ret |= ts2020_tuner_gate_ctrl(fe, 0x01); ++ ++ msleep(80); ++ /* calculate offset assuming 96000kHz*/ ++ offset_khz = (ndiv - ndiv % 2 + 1024) * TS2020_XTAL_FREQ ++ / (6 + 8) / (div4 + 1) / 2; ++ ++ priv->frequency = offset_khz; ++ ++ return (ret < 0) ? -EINVAL : 0; ++} ++ ++static int ts2020_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct ts2020_priv *priv = fe->tuner_priv; ++ *frequency = priv->frequency; ++ return 0; ++} ++ ++/* read TS2020 signal strength */ ++static int ts2020_read_signal_strength(struct dvb_frontend *fe, ++ u16 *signal_strength) ++{ ++ u16 sig_reading, sig_strength; ++ u8 rfgain, bbgain; ++ ++ rfgain = ts2020_readreg(fe, 0x3d) & 0x1f; ++ bbgain = ts2020_readreg(fe, 0x21) & 0x1f; ++ ++ if (rfgain > 15) ++ rfgain = 15; ++ if (bbgain > 13) ++ bbgain = 13; ++ ++ sig_reading = rfgain * 2 + bbgain * 3; ++ ++ sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; ++ ++ /* cook the value to be suitable for szap-s2 human readable output */ ++ *signal_strength = sig_strength * 1000; ++ ++ return 0; ++} ++ ++static struct dvb_tuner_ops ts2020_tuner_ops = { ++ .info = { ++ .name = "TS2020", ++ .frequency_min = 950000, ++ .frequency_max = 2150000 ++ }, ++ .init = ts2020_init, ++ .release = ts2020_release, ++ .sleep = ts2020_sleep, ++ .set_params = ts2020_set_params, ++ .get_frequency = ts2020_get_frequency, ++ .get_rf_strength = ts2020_read_signal_strength, ++}; ++ ++struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, ++ const struct ts2020_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct ts2020_priv *priv = NULL; ++ u8 buf; ++ ++ priv = kzalloc(sizeof(struct ts2020_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->i2c_address = config->tuner_address; ++ priv->i2c = i2c; ++ priv->clk_out_div = config->clk_out_div; ++ fe->tuner_priv = priv; ++ ++ /* Wake Up the tuner */ ++ if ((0x03 & ts2020_readreg(fe, 0x00)) == 0x00) { ++ ts2020_writereg(fe, 0x00, 0x01); ++ msleep(2); ++ } ++ ++ ts2020_writereg(fe, 0x00, 0x03); ++ msleep(2); ++ ++ /* Check the tuner version */ ++ buf = ts2020_readreg(fe, 0x00); ++ if ((buf == 0x01) || (buf == 0x41) || (buf == 0x81)) ++ printk(KERN_INFO "%s: Find tuner TS2020!\n", __func__); ++ else { ++ printk(KERN_ERR "%s: Read tuner reg[0] = %d\n", __func__, buf); ++ kfree(priv); ++ return NULL; ++ } ++ ++ memcpy(&fe->ops.tuner_ops, &ts2020_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ ++ return fe; ++} ++EXPORT_SYMBOL(ts2020_attach); ++ ++MODULE_AUTHOR("Konstantin Dimitrov "); ++MODULE_DESCRIPTION("Montage Technology TS2020 - Silicon tuner driver module"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/ts2020.h b/drivers/media/dvb-frontends/ts2020.h +new file mode 100644 +index 0000000..c7e64af +--- /dev/null ++++ b/drivers/media/dvb-frontends/ts2020.h +@@ -0,0 +1,50 @@ ++/* ++ Montage Technology TS2020 - Silicon Tuner driver ++ Copyright (C) 2009-2012 Konstantin Dimitrov ++ ++ Copyright (C) 2009-2012 TurboSight.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. ++ */ ++ ++#ifndef TS2020_H ++#define TS2020_H ++ ++#include ++ ++struct ts2020_config { ++ u8 tuner_address; ++ u8 clk_out_div; ++}; ++ ++#if defined(CONFIG_DVB_TS2020) || \ ++ (defined(CONFIG_DVB_TS2020_MODULE) && defined(MODULE)) ++ ++extern struct dvb_frontend *ts2020_attach( ++ struct dvb_frontend *fe, ++ const struct ts2020_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *ts2020_attach( ++ struct dvb_frontend *fe, ++ const struct ts2020_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* TS2020_H */ +diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c +new file mode 100644 +index 0000000..029384d +--- /dev/null ++++ b/drivers/media/dvb-frontends/tua6100.c +@@ -0,0 +1,206 @@ ++/** ++ * Driver for Infineon tua6100 pll. ++ * ++ * (c) 2006 Andrew de Quincey ++ * ++ * Based on code found in budget-av.c, which has the following: ++ * Compiled from various sources by Michael Hunold ++ * ++ * CI interface support (c) 2004 Olivier Gournet & ++ * Andrew de Quincey ++ * ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "tua6100.h" ++ ++struct tua6100_priv { ++ /* i2c details */ ++ int i2c_address; ++ struct i2c_adapter *i2c; ++ u32 frequency; ++}; ++ ++static int tua6100_release(struct dvb_frontend *fe) ++{ ++ kfree(fe->tuner_priv); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static int tua6100_sleep(struct dvb_frontend *fe) ++{ ++ struct tua6100_priv *priv = fe->tuner_priv; ++ int ret; ++ u8 reg0[] = { 0x00, 0x00 }; ++ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { ++ printk("%s: i2c error\n", __func__); ++ } ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return (ret == 1) ? 0 : ret; ++} ++ ++static int tua6100_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct tua6100_priv *priv = fe->tuner_priv; ++ u32 div; ++ u32 prediv; ++ u8 reg0[] = { 0x00, 0x00 }; ++ u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 }; ++ u8 reg2[] = { 0x02, 0x00, 0x00 }; ++ struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; ++ struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 }; ++ struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 }; ++ ++#define _R 4 ++#define _P 32 ++#define _ri 4000000 ++ ++ // setup register 0 ++ if (c->frequency < 2000000) ++ reg0[1] = 0x03; ++ else ++ reg0[1] = 0x07; ++ ++ // setup register 1 ++ if (c->frequency < 1630000) ++ reg1[1] = 0x2c; ++ else ++ reg1[1] = 0x0c; ++ ++ if (_P == 64) ++ reg1[1] |= 0x40; ++ if (c->frequency >= 1525000) ++ reg1[1] |= 0x80; ++ ++ // register 2 ++ reg2[1] = (_R >> 8) & 0x03; ++ reg2[2] = _R; ++ if (c->frequency < 1455000) ++ reg2[1] |= 0x1c; ++ else if (c->frequency < 1630000) ++ reg2[1] |= 0x0c; ++ else ++ reg2[1] |= 0x1c; ++ ++ /* ++ * The N divisor ratio (note: c->frequency is in kHz, but we ++ * need it in Hz) ++ */ ++ prediv = (c->frequency * _R) / (_ri / 1000); ++ div = prediv / _P; ++ reg1[1] |= (div >> 9) & 0x03; ++ reg1[2] = div >> 1; ++ reg1[3] = (div << 7); ++ priv->frequency = ((div * _P) * (_ri / 1000)) / _R; ++ ++ // Finally, calculate and store the value for A ++ reg1[3] |= (prediv - (div*_P)) & 0x7f; ++ ++#undef _R ++#undef _P ++#undef _ri ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(priv->i2c, &msg0, 1) != 1) ++ return -EIO; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(priv->i2c, &msg2, 1) != 1) ++ return -EIO; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(priv->i2c, &msg1, 1) != 1) ++ return -EIO; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct tua6100_priv *priv = fe->tuner_priv; ++ *frequency = priv->frequency; ++ return 0; ++} ++ ++static struct dvb_tuner_ops tua6100_tuner_ops = { ++ .info = { ++ .name = "Infineon TUA6100", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_step = 1000, ++ }, ++ .release = tua6100_release, ++ .sleep = tua6100_sleep, ++ .set_params = tua6100_set_params, ++ .get_frequency = tua6100_get_frequency, ++}; ++ ++struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) ++{ ++ struct tua6100_priv *priv = NULL; ++ u8 b1 [] = { 0x80 }; ++ u8 b2 [] = { 0x00 }; ++ struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 }, ++ { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } }; ++ int ret; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = i2c_transfer (i2c, msg, 2); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ if (ret != 2) ++ return NULL; ++ ++ priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL); ++ if (priv == NULL) ++ return NULL; ++ ++ priv->i2c_address = addr; ++ priv->i2c = i2c; ++ ++ memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = priv; ++ return fe; ++} ++EXPORT_SYMBOL(tua6100_attach); ++ ++MODULE_DESCRIPTION("DVB tua6100 driver"); ++MODULE_AUTHOR("Andrew de Quincey"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/tua6100.h b/drivers/media/dvb-frontends/tua6100.h +new file mode 100644 +index 0000000..f83dbd5 +--- /dev/null ++++ b/drivers/media/dvb-frontends/tua6100.h +@@ -0,0 +1,47 @@ ++/** ++ * Driver for Infineon tua6100 PLL. ++ * ++ * (c) 2006 Andrew de Quincey ++ * ++ * Based on code found in budget-av.c, which has the following: ++ * Compiled from various sources by Michael Hunold ++ * ++ * CI interface support (c) 2004 Olivier Gournet & ++ * Andrew de Quincey ++ * ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __DVB_TUA6100_H__ ++#define __DVB_TUA6100_H__ ++ ++#include ++#include "dvb_frontend.h" ++ ++#if defined(CONFIG_DVB_TUA6100) || (defined(CONFIG_DVB_TUA6100_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_TUA6100 ++ ++#endif +diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c +new file mode 100644 +index 0000000..bb42b56 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ves1820.c +@@ -0,0 +1,447 @@ ++/* ++ VES1820 - Single Chip Cable Channel Receiver driver module ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "ves1820.h" ++ ++ ++ ++struct ves1820_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct ves1820_config* config; ++ struct dvb_frontend frontend; ++ ++ /* private demodulator data */ ++ u8 reg0; ++ u8 pwm; ++}; ++ ++ ++static int verbose; ++ ++static u8 ves1820_inittab[] = { ++ 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A, ++ 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, ++ 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, ++ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x40 ++}; ++ ++static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data) ++{ ++ u8 buf[] = { 0x00, reg, data }; ++ struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 }; ++ int ret; ++ ++ ret = i2c_transfer(state->i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk("ves1820: %s(): writereg error (reg == 0x%02x, " ++ "val == 0x%02x, ret == %i)\n", __func__, reg, data, ret); ++ ++ return (ret != 1) ? -EREMOTEIO : 0; ++} ++ ++static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) ++{ ++ u8 b0[] = { 0x00, reg }; ++ u8 b1[] = { 0 }; ++ struct i2c_msg msg[] = { ++ {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2}, ++ {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} ++ }; ++ int ret; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) ++ printk("ves1820: %s(): readreg error (reg == 0x%02x, " ++ "ret == %i)\n", __func__, reg, ret); ++ ++ return b1[0]; ++} ++ ++static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion) ++{ ++ reg0 |= state->reg0 & 0x62; ++ ++ if (INVERSION_ON == inversion) { ++ if (!state->config->invert) reg0 |= 0x20; ++ else reg0 &= ~0x20; ++ } else if (INVERSION_OFF == inversion) { ++ if (!state->config->invert) reg0 &= ~0x20; ++ else reg0 |= 0x20; ++ } ++ ++ ves1820_writereg(state, 0x00, reg0 & 0xfe); ++ ves1820_writereg(state, 0x00, reg0 | 0x01); ++ ++ state->reg0 = reg0; ++ ++ return 0; ++} ++ ++static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) ++{ ++ s32 BDR; ++ s32 BDRI; ++ s16 SFIL = 0; ++ u16 NDEC = 0; ++ u32 ratio; ++ u32 fin; ++ u32 tmp; ++ u64 fptmp; ++ u64 fpxin; ++ ++ if (symbolrate > state->config->xin / 2) ++ symbolrate = state->config->xin / 2; ++ ++ if (symbolrate < 500000) ++ symbolrate = 500000; ++ ++ if (symbolrate < state->config->xin / 16) ++ NDEC = 1; ++ if (symbolrate < state->config->xin / 32) ++ NDEC = 2; ++ if (symbolrate < state->config->xin / 64) ++ NDEC = 3; ++ ++ /* yeuch! */ ++ fpxin = state->config->xin * 10; ++ fptmp = fpxin; do_div(fptmp, 123); ++ if (symbolrate < fptmp) ++ SFIL = 1; ++ fptmp = fpxin; do_div(fptmp, 160); ++ if (symbolrate < fptmp) ++ SFIL = 0; ++ fptmp = fpxin; do_div(fptmp, 246); ++ if (symbolrate < fptmp) ++ SFIL = 1; ++ fptmp = fpxin; do_div(fptmp, 320); ++ if (symbolrate < fptmp) ++ SFIL = 0; ++ fptmp = fpxin; do_div(fptmp, 492); ++ if (symbolrate < fptmp) ++ SFIL = 1; ++ fptmp = fpxin; do_div(fptmp, 640); ++ if (symbolrate < fptmp) ++ SFIL = 0; ++ fptmp = fpxin; do_div(fptmp, 984); ++ if (symbolrate < fptmp) ++ SFIL = 1; ++ ++ fin = state->config->xin >> 4; ++ symbolrate <<= NDEC; ++ ratio = (symbolrate << 4) / fin; ++ tmp = ((symbolrate << 4) % fin) << 8; ++ ratio = (ratio << 8) + tmp / fin; ++ tmp = (tmp % fin) << 8; ++ ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin); ++ ++ BDR = ratio; ++ BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; ++ ++ if (BDRI > 0xFF) ++ BDRI = 0xFF; ++ ++ SFIL = (SFIL << 4) | ves1820_inittab[0x0E]; ++ ++ NDEC = (NDEC << 6) | ves1820_inittab[0x03]; ++ ++ ves1820_writereg(state, 0x03, NDEC); ++ ves1820_writereg(state, 0x0a, BDR & 0xff); ++ ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff); ++ ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f); ++ ++ ves1820_writereg(state, 0x0d, BDRI); ++ ves1820_writereg(state, 0x0e, SFIL); ++ ++ return 0; ++} ++ ++static int ves1820_init(struct dvb_frontend* fe) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ int i; ++ ++ ves1820_writereg(state, 0, 0); ++ ++ for (i = 0; i < sizeof(ves1820_inittab); i++) ++ ves1820_writereg(state, i, ves1820_inittab[i]); ++ if (state->config->selagc) ++ ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08); ++ ++ ves1820_writereg(state, 0x34, state->pwm); ++ ++ return 0; ++} ++ ++static int ves1820_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct ves1820_state* state = fe->demodulator_priv; ++ static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 }; ++ static const u8 reg0x01[] = { 140, 140, 106, 100, 92 }; ++ static const u8 reg0x05[] = { 135, 100, 70, 54, 38 }; ++ static const u8 reg0x08[] = { 162, 116, 67, 52, 35 }; ++ static const u8 reg0x09[] = { 145, 150, 106, 126, 107 }; ++ int real_qam = p->modulation - QAM_16; ++ ++ if (real_qam < 0 || real_qam > 4) ++ return -EINVAL; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ++ ves1820_set_symbolrate(state, p->symbol_rate); ++ ves1820_writereg(state, 0x34, state->pwm); ++ ++ ves1820_writereg(state, 0x01, reg0x01[real_qam]); ++ ves1820_writereg(state, 0x05, reg0x05[real_qam]); ++ ves1820_writereg(state, 0x08, reg0x08[real_qam]); ++ ves1820_writereg(state, 0x09, reg0x09[real_qam]); ++ ++ ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion); ++ ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0)); ++ return 0; ++} ++ ++static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ int sync; ++ ++ *status = 0; ++ sync = ves1820_readreg(state, 0x11); ++ ++ if (sync & 1) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 2) ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 2) /* XXX FIXME! */ ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 4) ++ *status |= FE_HAS_SYNC; ++ ++ if (sync & 8) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ ++ u32 _ber = ves1820_readreg(state, 0x14) | ++ (ves1820_readreg(state, 0x15) << 8) | ++ ((ves1820_readreg(state, 0x16) & 0x0f) << 16); ++ *ber = 10 * _ber; ++ ++ return 0; ++} ++ ++static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ ++ u8 gain = ves1820_readreg(state, 0x17); ++ *strength = (gain << 8) | gain; ++ ++ return 0; ++} ++ ++static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ ++ u8 quality = ~ves1820_readreg(state, 0x18); ++ *snr = (quality << 8) | quality; ++ ++ return 0; ++} ++ ++static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ ++ *ucblocks = ves1820_readreg(state, 0x13) & 0x7f; ++ if (*ucblocks == 0x7f) ++ *ucblocks = 0xffffffff; ++ ++ /* reset uncorrected block counter */ ++ ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf); ++ ves1820_writereg(state, 0x10, ves1820_inittab[0x10]); ++ ++ return 0; ++} ++ ++static int ves1820_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct ves1820_state* state = fe->demodulator_priv; ++ int sync; ++ s8 afc = 0; ++ ++ sync = ves1820_readreg(state, 0x11); ++ afc = ves1820_readreg(state, 0x19); ++ if (verbose) { ++ /* AFC only valid when carrier has been recovered */ ++ printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" : ++ "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10); ++ } ++ ++ if (!state->config->invert) { ++ p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF; ++ } else { ++ p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF; ++ } ++ ++ p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; ++ ++ p->fec_inner = FEC_NONE; ++ ++ p->frequency = ((p->frequency + 31250) / 62500) * 62500; ++ if (sync & 2) ++ p->frequency -= ((s32) p->symbol_rate * afc) >> 10; ++ ++ return 0; ++} ++ ++static int ves1820_sleep(struct dvb_frontend* fe) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ ++ ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ ++ ves1820_writereg(state, 0x00, 0x80); /* standby */ ++ ++ return 0; ++} ++ ++static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) ++{ ++ ++ fesettings->min_delay_ms = 200; ++ fesettings->step_size = 0; ++ fesettings->max_drift = 0; ++ return 0; ++} ++ ++static void ves1820_release(struct dvb_frontend* fe) ++{ ++ struct ves1820_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops ves1820_ops; ++ ++struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, ++ struct i2c_adapter* i2c, ++ u8 pwm) ++{ ++ struct ves1820_state* state = NULL; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->reg0 = ves1820_inittab[0]; ++ state->config = config; ++ state->i2c = i2c; ++ state->pwm = pwm; ++ ++ /* check if the demod is there */ ++ if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70) ++ goto error; ++ ++ if (verbose) ++ printk("ves1820: pwm=0x%02x\n", state->pwm); ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */ ++ state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */ ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops ves1820_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A }, ++ .info = { ++ .name = "VLSI VES1820 DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .caps = FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | ++ FE_CAN_FEC_AUTO ++ }, ++ ++ .release = ves1820_release, ++ ++ .init = ves1820_init, ++ .sleep = ves1820_sleep, ++ ++ .set_frontend = ves1820_set_parameters, ++ .get_frontend = ves1820_get_frontend, ++ .get_tune_settings = ves1820_get_tune_settings, ++ ++ .read_status = ves1820_read_status, ++ .read_ber = ves1820_read_ber, ++ .read_signal_strength = ves1820_read_signal_strength, ++ .read_snr = ves1820_read_snr, ++ .read_ucblocks = ves1820_read_ucblocks, ++}; ++ ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); ++ ++MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); ++MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(ves1820_attach); +diff --git a/drivers/media/dvb-frontends/ves1820.h b/drivers/media/dvb-frontends/ves1820.h +new file mode 100644 +index 0000000..e902ed6 +--- /dev/null ++++ b/drivers/media/dvb-frontends/ves1820.h +@@ -0,0 +1,56 @@ ++/* ++ VES1820 - Single Chip Cable Channel Receiver driver module ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef VES1820_H ++#define VES1820_H ++ ++#include ++ ++#define VES1820_SELAGC_PWM 0 ++#define VES1820_SELAGC_SIGNAMPERR 1 ++ ++struct ves1820_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* value of XIN to use */ ++ u32 xin; ++ ++ /* does inversion need inverted? */ ++ u8 invert:1; ++ ++ /* SELAGC control */ ++ u8 selagc:1; ++}; ++ ++#if defined(CONFIG_DVB_VES1820) || (defined(CONFIG_DVB_VES1820_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, ++ struct i2c_adapter* i2c, u8 pwm); ++#else ++static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, ++ struct i2c_adapter* i2c, u8 pwm) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_VES1820 ++ ++#endif // VES1820_H +diff --git a/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c +new file mode 100644 +index 0000000..9c17eac +--- /dev/null ++++ b/drivers/media/dvb-frontends/ves1x93.c +@@ -0,0 +1,553 @@ ++/* ++ Driver for VES1893 and VES1993 QPSK Demodulators ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> ++ Copyright (C) 2002 Dennis Noermann ++ Copyright (C) 2002-2003 Andreas Oberritter ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "ves1x93.h" ++ ++ ++struct ves1x93_state { ++ struct i2c_adapter* i2c; ++ /* configuration settings */ ++ const struct ves1x93_config* config; ++ struct dvb_frontend frontend; ++ ++ /* previous uncorrected block counter */ ++ fe_spectral_inversion_t inversion; ++ u8 *init_1x93_tab; ++ u8 *init_1x93_wtab; ++ u8 tab_size; ++ u8 demod_type; ++ u32 frequency; ++}; ++ ++static int debug; ++#define dprintk if (debug) printk ++ ++#define DEMOD_VES1893 0 ++#define DEMOD_VES1993 1 ++ ++static u8 init_1893_tab [] = { ++ 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4, ++ 0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00, ++ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00, ++ 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x55, 0x00, 0x00, 0x7f, 0x00 ++}; ++ ++static u8 init_1993_tab [] = { ++ 0x00, 0x9c, 0x35, 0x80, 0x6a, 0x09, 0x72, 0x8c, ++ 0x09, 0x6b, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, ++ 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x80, 0x40, 0x21, 0xb0, 0x00, 0x00, 0x00, 0x10, ++ 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, ++ 0x00, 0x00, 0x0e, 0x80, 0x00 ++}; ++ ++static u8 init_1893_wtab[] = ++{ ++ 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, ++ 0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1, ++ 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, ++ 1,1,1,0,1,1 ++}; ++ ++static u8 init_1993_wtab[] = ++{ ++ 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, ++ 0,1,0,0,0,0,0,0, 1,1,1,1,0,0,0,1, ++ 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, ++ 1,1,1,0,1,1,1,1, 1,1,1,1,1 ++}; ++ ++static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data) ++{ ++ u8 buf [] = { 0x00, reg, data }; ++ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 }; ++ int err; ++ ++ if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { ++ dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg) ++{ ++ int ret; ++ u8 b0 [] = { 0x00, reg }; ++ u8 b1 [] = { 0 }; ++ struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, ++ { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer (state->i2c, msg, 2); ++ ++ if (ret != 2) return ret; ++ ++ return b1[0]; ++} ++ ++static int ves1x93_clr_bit (struct ves1x93_state* state) ++{ ++ msleep(10); ++ ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe); ++ ves1x93_writereg (state, 0, state->init_1x93_tab[0]); ++ msleep(50); ++ return 0; ++} ++ ++static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion) ++{ ++ u8 val; ++ ++ /* ++ * inversion on/off are interchanged because i and q seem to ++ * be swapped on the hardware ++ */ ++ ++ switch (inversion) { ++ case INVERSION_OFF: ++ val = 0xc0; ++ break; ++ case INVERSION_ON: ++ val = 0x80; ++ break; ++ case INVERSION_AUTO: ++ val = 0x00; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); ++} ++ ++static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) ++{ ++ if (fec == FEC_AUTO) ++ return ves1x93_writereg (state, 0x0d, 0x08); ++ else if (fec < FEC_1_2 || fec > FEC_8_9) ++ return -EINVAL; ++ else ++ return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); ++} ++ ++static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state) ++{ ++ return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); ++} ++ ++static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate) ++{ ++ u32 BDR; ++ u32 ratio; ++ u8 ADCONF, FCONF, FNR, AGCR; ++ u32 BDRI; ++ u32 tmp; ++ u32 FIN; ++ ++ dprintk("%s: srate == %d\n", __func__, (unsigned int) srate); ++ ++ if (srate > state->config->xin/2) ++ srate = state->config->xin/2; ++ ++ if (srate < 500000) ++ srate = 500000; ++ ++#define MUL (1UL<<26) ++ ++ FIN = (state->config->xin + 6000) >> 4; ++ ++ tmp = srate << 6; ++ ratio = tmp / FIN; ++ ++ tmp = (tmp % FIN) << 8; ++ ratio = (ratio << 8) + tmp / FIN; ++ ++ tmp = (tmp % FIN) << 8; ++ ratio = (ratio << 8) + tmp / FIN; ++ ++ FNR = 0xff; ++ ++ if (ratio < MUL/3) FNR = 0; ++ if (ratio < (MUL*11)/50) FNR = 1; ++ if (ratio < MUL/6) FNR = 2; ++ if (ratio < MUL/9) FNR = 3; ++ if (ratio < MUL/12) FNR = 4; ++ if (ratio < (MUL*11)/200) FNR = 5; ++ if (ratio < MUL/24) FNR = 6; ++ if (ratio < (MUL*27)/1000) FNR = 7; ++ if (ratio < MUL/48) FNR = 8; ++ if (ratio < (MUL*137)/10000) FNR = 9; ++ ++ if (FNR == 0xff) { ++ ADCONF = 0x89; ++ FCONF = 0x80; ++ FNR = 0; ++ } else { ++ ADCONF = 0x81; ++ FCONF = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5); ++ /*FCONF = 0x80 | ((FNR & 0x01) << 5) | (((FNR > 1) & 0x03) << 3) | ((FNR >> 1) & 0x07);*/ ++ } ++ ++ BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1; ++ BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1; ++ ++ dprintk("FNR= %d\n", FNR); ++ dprintk("ratio= %08x\n", (unsigned int) ratio); ++ dprintk("BDR= %08x\n", (unsigned int) BDR); ++ dprintk("BDRI= %02x\n", (unsigned int) BDRI); ++ ++ if (BDRI > 0xff) ++ BDRI = 0xff; ++ ++ ves1x93_writereg (state, 0x06, 0xff & BDR); ++ ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8)); ++ ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16)); ++ ++ ves1x93_writereg (state, 0x09, BDRI); ++ ves1x93_writereg (state, 0x20, ADCONF); ++ ves1x93_writereg (state, 0x21, FCONF); ++ ++ AGCR = state->init_1x93_tab[0x05]; ++ if (state->config->invert_pwm) ++ AGCR |= 0x20; ++ ++ if (srate < 6000000) ++ AGCR |= 0x80; ++ else ++ AGCR &= ~0x80; ++ ++ ves1x93_writereg (state, 0x05, AGCR); ++ ++ /* ves1993 hates this, will lose lock */ ++ if (state->demod_type != DEMOD_VES1993) ++ ves1x93_clr_bit (state); ++ ++ return 0; ++} ++ ++static int ves1x93_init (struct dvb_frontend* fe) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ int i; ++ int val; ++ ++ dprintk("%s: init chip\n", __func__); ++ ++ for (i = 0; i < state->tab_size; i++) { ++ if (state->init_1x93_wtab[i]) { ++ val = state->init_1x93_tab[i]; ++ ++ if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */ ++ ves1x93_writereg (state, i, val); ++ } ++ } ++ ++ return 0; ++} ++ ++static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ return ves1x93_writereg (state, 0x1f, 0x20); ++ case SEC_VOLTAGE_18: ++ return ves1x93_writereg (state, 0x1f, 0x30); ++ case SEC_VOLTAGE_OFF: ++ return ves1x93_writereg (state, 0x1f, 0x00); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ u8 sync = ves1x93_readreg (state, 0x0e); ++ ++ /* ++ * The ves1893 sometimes returns sync values that make no sense, ++ * because, e.g., the SIGNAL bit is 0, while some of the higher ++ * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?). ++ * Tests showed that the VITERBI and SYNC bits are returned ++ * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong. ++ * If such a case occurs, we read the value again, until we get a ++ * valid value. ++ */ ++ int maxtry = 10; /* just for safety - let's not get stuck here */ ++ while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) { ++ msleep(10); ++ sync = ves1x93_readreg (state, 0x0e); ++ } ++ ++ *status = 0; ++ ++ if (sync & 1) ++ *status |= FE_HAS_SIGNAL; ++ ++ if (sync & 2) ++ *status |= FE_HAS_CARRIER; ++ ++ if (sync & 4) ++ *status |= FE_HAS_VITERBI; ++ ++ if (sync & 8) ++ *status |= FE_HAS_SYNC; ++ ++ if ((sync & 0x1f) == 0x1f) ++ *status |= FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ *ber = ves1x93_readreg (state, 0x15); ++ *ber |= (ves1x93_readreg (state, 0x16) << 8); ++ *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16); ++ *ber *= 10; ++ ++ return 0; ++} ++ ++static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ u8 signal = ~ves1x93_readreg (state, 0x0b); ++ *strength = (signal << 8) | signal; ++ ++ return 0; ++} ++ ++static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ u8 _snr = ~ves1x93_readreg (state, 0x1c); ++ *snr = (_snr << 8) | _snr; ++ ++ return 0; ++} ++ ++static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f; ++ ++ if (*ucblocks == 0x7f) ++ *ucblocks = 0xffffffff; /* counter overflow... */ ++ ++ ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */ ++ ves1x93_writereg (state, 0x18, 0x80); /* dto. */ ++ ++ return 0; ++} ++ ++static int ves1x93_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ ves1x93_set_inversion (state, p->inversion); ++ ves1x93_set_fec(state, p->fec_inner); ++ ves1x93_set_symbolrate(state, p->symbol_rate); ++ state->inversion = p->inversion; ++ state->frequency = p->frequency; ++ ++ return 0; ++} ++ ++static int ves1x93_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct ves1x93_state* state = fe->demodulator_priv; ++ int afc; ++ ++ afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2; ++ afc = (afc * (int)(p->symbol_rate/1000/8))/16; ++ ++ p->frequency = state->frequency - afc; ++ ++ /* ++ * inversion indicator is only valid ++ * if auto inversion was used ++ */ ++ if (state->inversion == INVERSION_AUTO) ++ p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ? ++ INVERSION_OFF : INVERSION_ON; ++ p->fec_inner = ves1x93_get_fec(state); ++ /* XXX FIXME: timing offset !! */ ++ ++ return 0; ++} ++ ++static int ves1x93_sleep(struct dvb_frontend* fe) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ return ves1x93_writereg (state, 0x00, 0x08); ++} ++ ++static void ves1x93_release(struct dvb_frontend* fe) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static int ves1x93_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct ves1x93_state* state = fe->demodulator_priv; ++ ++ if (enable) { ++ return ves1x93_writereg(state, 0x00, 0x11); ++ } else { ++ return ves1x93_writereg(state, 0x00, 0x01); ++ } ++} ++ ++static struct dvb_frontend_ops ves1x93_ops; ++ ++struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, ++ struct i2c_adapter* i2c) ++{ ++ struct ves1x93_state* state = NULL; ++ u8 identity; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); ++ if (state == NULL) goto error; ++ ++ /* setup the state */ ++ state->config = config; ++ state->i2c = i2c; ++ state->inversion = INVERSION_OFF; ++ ++ /* check if the demod is there + identify it */ ++ identity = ves1x93_readreg(state, 0x1e); ++ switch (identity) { ++ case 0xdc: /* VES1893A rev1 */ ++ printk("ves1x93: Detected ves1893a rev1\n"); ++ state->demod_type = DEMOD_VES1893; ++ state->init_1x93_tab = init_1893_tab; ++ state->init_1x93_wtab = init_1893_wtab; ++ state->tab_size = sizeof(init_1893_tab); ++ break; ++ ++ case 0xdd: /* VES1893A rev2 */ ++ printk("ves1x93: Detected ves1893a rev2\n"); ++ state->demod_type = DEMOD_VES1893; ++ state->init_1x93_tab = init_1893_tab; ++ state->init_1x93_wtab = init_1893_wtab; ++ state->tab_size = sizeof(init_1893_tab); ++ break; ++ ++ case 0xde: /* VES1993 */ ++ printk("ves1x93: Detected ves1993\n"); ++ state->demod_type = DEMOD_VES1993; ++ state->init_1x93_tab = init_1993_tab; ++ state->init_1x93_wtab = init_1993_wtab; ++ state->tab_size = sizeof(init_1993_tab); ++ break; ++ ++ default: ++ goto error; ++ } ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ return &state->frontend; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops ves1x93_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "VLSI VES1x93 DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 125, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 29500, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ /* .symbol_rate_tolerance = ???,*/ ++ .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 ++ }, ++ ++ .release = ves1x93_release, ++ ++ .init = ves1x93_init, ++ .sleep = ves1x93_sleep, ++ .i2c_gate_ctrl = ves1x93_i2c_gate_ctrl, ++ ++ .set_frontend = ves1x93_set_frontend, ++ .get_frontend = ves1x93_get_frontend, ++ ++ .read_status = ves1x93_read_status, ++ .read_ber = ves1x93_read_ber, ++ .read_signal_strength = ves1x93_read_signal_strength, ++ .read_snr = ves1x93_read_snr, ++ .read_ucblocks = ves1x93_read_ucblocks, ++ ++ .set_voltage = ves1x93_set_voltage, ++}; ++ ++module_param(debug, int, 0644); ++ ++MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); ++MODULE_AUTHOR("Ralph Metzler"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(ves1x93_attach); +diff --git a/drivers/media/dvb-frontends/ves1x93.h b/drivers/media/dvb-frontends/ves1x93.h +new file mode 100644 +index 0000000..8a5a49e +--- /dev/null ++++ b/drivers/media/dvb-frontends/ves1x93.h +@@ -0,0 +1,55 @@ ++/* ++ Driver for VES1893 and VES1993 QPSK Demodulators ++ ++ Copyright (C) 1999 Convergence Integrated Media GmbH ++ Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> ++ Copyright (C) 2002 Dennis Noermann ++ Copyright (C) 2002-2003 Andreas Oberritter ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef VES1X93_H ++#define VES1X93_H ++ ++#include ++ ++struct ves1x93_config ++{ ++ /* the demodulator's i2c address */ ++ u8 demod_address; ++ ++ /* value of XIN to use */ ++ u32 xin; ++ ++ /* should PWM be inverted? */ ++ u8 invert_pwm:1; ++}; ++ ++#if defined(CONFIG_DVB_VES1X93) || (defined(CONFIG_DVB_VES1X93_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, ++ struct i2c_adapter* i2c); ++#else ++static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, ++ struct i2c_adapter* i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif // CONFIG_DVB_VES1X93 ++ ++#endif // VES1X93_H +diff --git a/drivers/media/dvb-frontends/z0194a.h b/drivers/media/dvb-frontends/z0194a.h +new file mode 100644 +index 0000000..96d86d6 +--- /dev/null ++++ b/drivers/media/dvb-frontends/z0194a.h +@@ -0,0 +1,85 @@ ++/* z0194a.h Sharp z0194a tuner support ++* ++* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) ++* ++* This program is free software; you can redistribute it and/or modify it ++* under the terms of the GNU General Public License as published by the ++* Free Software Foundation, version 2. ++* ++* see Documentation/dvb/README.dvb-usb for more information ++*/ ++ ++#ifndef Z0194A ++#define Z0194A ++ ++static int sharp_z0194a_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 sharp_z0194a_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ ++ 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ ++ 0x06, 0x40, /* DAC not used, set to high impendance mode */ ++ 0x07, 0x00, /* DAC LSB */ ++ 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ ++ 0x09, 0x00, /* FIFO */ ++ 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ ++ 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ ++ 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ ++ 0x10, 0x3f, /* AGC2 0x3d */ ++ 0x11, 0x84, ++ 0x12, 0xb9, ++ 0x15, 0xc9, /* lock detector threshold */ ++ 0x16, 0x00, ++ 0x17, 0x00, ++ 0x18, 0x00, ++ 0x19, 0x00, ++ 0x1a, 0x00, ++ 0x1f, 0x50, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, /* out imp: normal out type: parallel FEC mode:0 */ ++ 0x29, 0x1e, /* 1/2 threshold */ ++ 0x2a, 0x14, /* 2/3 threshold */ ++ 0x2b, 0x0f, /* 3/4 threshold */ ++ 0x2c, 0x09, /* 5/6 threshold */ ++ 0x2d, 0x05, /* 7/8 threshold */ ++ 0x2e, 0x01, ++ 0x31, 0x1f, /* test all FECs */ ++ 0x32, 0x19, /* viterbi and synchro search */ ++ 0x33, 0xfc, /* rs control */ ++ 0x34, 0x93, /* error control */ ++ 0x0f, 0x52, ++ 0xff, 0xff ++}; ++ ++#endif +diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c +new file mode 100644 +index 0000000..0903d46 +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10036.c +@@ -0,0 +1,520 @@ ++/** ++ * Driver for Zarlink zl10036 DVB-S silicon tuner ++ * ++ * Copyright (C) 2006 Tino Reichardt ++ * Copyright (C) 2007-2009 Matthias Schwarzott ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License Version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ ** ++ * The data sheet for this tuner can be found at: ++ * http://www.mcmilk.de/projects/dvb-card/datasheets/ZL10036.pdf ++ * ++ * This one is working: (at my Avermedia DVB-S Pro) ++ * - zl10036 (40pin, FTA) ++ * ++ * A driver for zl10038 should be very similar. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "zl10036.h" ++ ++static int zl10036_debug; ++#define dprintk(level, args...) \ ++ do { if (zl10036_debug & level) printk(KERN_DEBUG "zl10036: " args); \ ++ } while (0) ++ ++#define deb_info(args...) dprintk(0x01, args) ++#define deb_i2c(args...) dprintk(0x02, args) ++ ++struct zl10036_state { ++ struct i2c_adapter *i2c; ++ const struct zl10036_config *config; ++ u32 frequency; ++ u8 br, bf; ++}; ++ ++ ++/* This driver assumes the tuner is driven by a 10.111MHz Cristal */ ++#define _XTAL 10111 ++ ++/* Some of the possible dividers: ++ * 64, (write 0x05 to reg), freq step size 158kHz ++ * 10, (write 0x0a to reg), freq step size 1.011kHz (used here) ++ * 5, (write 0x09 to reg), freq step size 2.022kHz ++ */ ++ ++#define _RDIV 10 ++#define _RDIV_REG 0x0a ++#define _FR (_XTAL/_RDIV) ++ ++#define STATUS_POR 0x80 /* Power on Reset */ ++#define STATUS_FL 0x40 /* Frequency & Phase Lock */ ++ ++/* read/write for zl10036 and zl10038 */ ++ ++static int zl10036_read_status_reg(struct zl10036_state *state) ++{ ++ u8 status; ++ struct i2c_msg msg[1] = { ++ { .addr = state->config->tuner_address, .flags = I2C_M_RD, ++ .buf = &status, .len = sizeof(status) }, ++ }; ++ ++ if (i2c_transfer(state->i2c, msg, 1) != 1) { ++ printk(KERN_ERR "%s: i2c read failed at addr=%02x\n", ++ __func__, state->config->tuner_address); ++ return -EIO; ++ } ++ ++ deb_i2c("R(status): %02x [FL=%d]\n", status, ++ (status & STATUS_FL) ? 1 : 0); ++ if (status & STATUS_POR) ++ deb_info("%s: Power-On-Reset bit enabled - " ++ "need to initialize the tuner\n", __func__); ++ ++ return status; ++} ++ ++static int zl10036_write(struct zl10036_state *state, u8 buf[], u8 count) ++{ ++ struct i2c_msg msg[1] = { ++ { .addr = state->config->tuner_address, .flags = 0, ++ .buf = buf, .len = count }, ++ }; ++ u8 reg = 0; ++ int ret; ++ ++ if (zl10036_debug & 0x02) { ++ /* every 8bit-value satisifes this! ++ * so only check for debug log */ ++ if ((buf[0] & 0x80) == 0x00) ++ reg = 2; ++ else if ((buf[0] & 0xc0) == 0x80) ++ reg = 4; ++ else if ((buf[0] & 0xf0) == 0xc0) ++ reg = 6; ++ else if ((buf[0] & 0xf0) == 0xd0) ++ reg = 8; ++ else if ((buf[0] & 0xf0) == 0xe0) ++ reg = 10; ++ else if ((buf[0] & 0xf0) == 0xf0) ++ reg = 12; ++ ++ deb_i2c("W(%d):", reg); ++ { ++ int i; ++ for (i = 0; i < count; i++) ++ printk(KERN_CONT " %02x", buf[i]); ++ printk(KERN_CONT "\n"); ++ } ++ } ++ ++ ret = i2c_transfer(state->i2c, msg, 1); ++ if (ret != 1) { ++ printk(KERN_ERR "%s: i2c error, ret=%d\n", __func__, ret); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int zl10036_release(struct dvb_frontend *fe) ++{ ++ struct zl10036_state *state = fe->tuner_priv; ++ ++ fe->tuner_priv = NULL; ++ kfree(state); ++ ++ return 0; ++} ++ ++static int zl10036_sleep(struct dvb_frontend *fe) ++{ ++ struct zl10036_state *state = fe->tuner_priv; ++ u8 buf[] = { 0xf0, 0x80 }; /* regs 12/13 */ ++ int ret; ++ ++ deb_info("%s\n", __func__); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ ++ ++ ret = zl10036_write(state, buf, sizeof(buf)); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ ++ ++ return ret; ++} ++ ++/** ++ * register map of the ZL10036/ZL10038 ++ * ++ * reg[default] content ++ * 2[0x00]: 0 | N14 | N13 | N12 | N11 | N10 | N9 | N8 ++ * 3[0x00]: N7 | N6 | N5 | N4 | N3 | N2 | N1 | N0 ++ * 4[0x80]: 1 | 0 | RFG | BA1 | BA0 | BG1 | BG0 | LEN ++ * 5[0x00]: P0 | C1 | C0 | R4 | R3 | R2 | R1 | R0 ++ * 6[0xc0]: 1 | 1 | 0 | 0 | RSD | 0 | 0 | 0 ++ * 7[0x20]: P1 | BF6 | BF5 | BF4 | BF3 | BF2 | BF1 | 0 ++ * 8[0xdb]: 1 | 1 | 0 | 1 | 0 | CC | 1 | 1 ++ * 9[0x30]: VSD | V2 | V1 | V0 | S3 | S2 | S1 | S0 ++ * 10[0xe1]: 1 | 1 | 1 | 0 | 0 | LS2 | LS1 | LS0 ++ * 11[0xf5]: WS | WH2 | WH1 | WH0 | WL2 | WL1 | WL0 | WRE ++ * 12[0xf0]: 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 ++ * 13[0x28]: PD | BR4 | BR3 | BR2 | BR1 | BR0 | CLR | TL ++ */ ++ ++static int zl10036_set_frequency(struct zl10036_state *state, u32 frequency) ++{ ++ u8 buf[2]; ++ u32 div, foffset; ++ ++ div = (frequency + _FR/2) / _FR; ++ state->frequency = div * _FR; ++ ++ foffset = frequency - state->frequency; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = (div >> 0) & 0xff; ++ ++ deb_info("%s: ftodo=%u fpriv=%u ferr=%d div=%u\n", __func__, ++ frequency, state->frequency, foffset, div); ++ ++ return zl10036_write(state, buf, sizeof(buf)); ++} ++ ++static int zl10036_set_bandwidth(struct zl10036_state *state, u32 fbw) ++{ ++ /* fbw is measured in kHz */ ++ u8 br, bf; ++ int ret; ++ u8 buf_bf[] = { ++ 0xc0, 0x00, /* 6/7: rsd=0 bf=0 */ ++ }; ++ u8 buf_br[] = { ++ 0xf0, 0x00, /* 12/13: br=0xa clr=0 tl=0*/ ++ }; ++ u8 zl10036_rsd_off[] = { 0xc8 }; /* set RSD=1 */ ++ ++ /* ensure correct values */ ++ if (fbw > 35000) ++ fbw = 35000; ++ if (fbw < 8000) ++ fbw = 8000; ++ ++#define _BR_MAXIMUM (_XTAL/575) /* _XTAL / 575kHz = 17 */ ++ ++ /* <= 28,82 MHz */ ++ if (fbw <= 28820) { ++ br = _BR_MAXIMUM; ++ } else { ++ /** ++ * f(bw)=34,6MHz f(xtal)=10.111MHz ++ * br = (10111/34600) * 63 * 1/K = 14; ++ */ ++ br = ((_XTAL * 21 * 1000) / (fbw * 419)); ++ } ++ ++ /* ensure correct values */ ++ if (br < 4) ++ br = 4; ++ if (br > _BR_MAXIMUM) ++ br = _BR_MAXIMUM; ++ ++ /* ++ * k = 1.257 ++ * bf = fbw/_XTAL * br * k - 1 */ ++ ++ bf = (fbw * br * 1257) / (_XTAL * 1000) - 1; ++ ++ /* ensure correct values */ ++ if (bf > 62) ++ bf = 62; ++ ++ buf_bf[1] = (bf << 1) & 0x7e; ++ buf_br[1] = (br << 2) & 0x7c; ++ deb_info("%s: BW=%d br=%u bf=%u\n", __func__, fbw, br, bf); ++ ++ if (br != state->br) { ++ ret = zl10036_write(state, buf_br, sizeof(buf_br)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (bf != state->bf) { ++ ret = zl10036_write(state, buf_bf, sizeof(buf_bf)); ++ if (ret < 0) ++ return ret; ++ ++ /* time = br/(32* fxtal) */ ++ /* minimal sleep time to be calculated ++ * maximum br is 63 -> max time = 2 /10 MHz = 2e-7 */ ++ msleep(1); ++ ++ ret = zl10036_write(state, zl10036_rsd_off, ++ sizeof(zl10036_rsd_off)); ++ if (ret < 0) ++ return ret; ++ } ++ ++ state->br = br; ++ state->bf = bf; ++ ++ return 0; ++} ++ ++static int zl10036_set_gain_params(struct zl10036_state *state, ++ int c) ++{ ++ u8 buf[2]; ++ u8 rfg, ba, bg; ++ ++ /* default values */ ++ rfg = 0; /* enable when using an lna */ ++ ba = 1; ++ bg = 1; ++ ++ /* reg 4 */ ++ buf[0] = 0x80 | ((rfg << 5) & 0x20) ++ | ((ba << 3) & 0x18) | ((bg << 1) & 0x06); ++ ++ if (!state->config->rf_loop_enable) ++ buf[0] |= 0x01; ++ ++ /* P0=0 */ ++ buf[1] = _RDIV_REG | ((c << 5) & 0x60); ++ ++ deb_info("%s: c=%u rfg=%u ba=%u bg=%u\n", __func__, c, rfg, ba, bg); ++ return zl10036_write(state, buf, sizeof(buf)); ++} ++ ++static int zl10036_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct zl10036_state *state = fe->tuner_priv; ++ int ret = 0; ++ u32 frequency = p->frequency; ++ u32 fbw; ++ int i; ++ u8 c; ++ ++ /* ensure correct values ++ * maybe redundant as core already checks this */ ++ if ((frequency < fe->ops.info.frequency_min) ++ || (frequency > fe->ops.info.frequency_max)) ++ return -EINVAL; ++ ++ /** ++ * alpha = 1.35 for dvb-s ++ * fBW = (alpha*symbolrate)/(2*0.8) ++ * 1.35 / (2*0.8) = 27 / 32 ++ */ ++ fbw = (27 * p->symbol_rate) / 32; ++ ++ /* scale to kHz */ ++ fbw /= 1000; ++ ++ /* Add safe margin of 3MHz */ ++ fbw += 3000; ++ ++ /* setting the charge pump - guessed values */ ++ if (frequency < 950000) ++ return -EINVAL; ++ else if (frequency < 1250000) ++ c = 0; ++ else if (frequency < 1750000) ++ c = 1; ++ else if (frequency < 2175000) ++ c = 2; ++ else ++ return -EINVAL; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ ++ ++ ret = zl10036_set_gain_params(state, c); ++ if (ret < 0) ++ goto error; ++ ++ ret = zl10036_set_frequency(state, p->frequency); ++ if (ret < 0) ++ goto error; ++ ++ ret = zl10036_set_bandwidth(state, fbw); ++ if (ret < 0) ++ goto error; ++ ++ /* wait for tuner lock - no idea if this is really needed */ ++ for (i = 0; i < 20; i++) { ++ ret = zl10036_read_status_reg(state); ++ if (ret < 0) ++ goto error; ++ ++ /* check Frequency & Phase Lock Bit */ ++ if (ret & STATUS_FL) ++ break; ++ ++ msleep(10); ++ } ++ ++error: ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ ++ ++ return ret; ++} ++ ++static int zl10036_get_frequency(struct dvb_frontend *fe, u32 *frequency) ++{ ++ struct zl10036_state *state = fe->tuner_priv; ++ ++ *frequency = state->frequency; ++ ++ return 0; ++} ++ ++static int zl10036_init_regs(struct zl10036_state *state) ++{ ++ int ret; ++ int i; ++ ++ /* could also be one block from reg 2 to 13 and additional 10/11 */ ++ u8 zl10036_init_tab[][2] = { ++ { 0x04, 0x00 }, /* 2/3: div=0x400 - arbitrary value */ ++ { 0x8b, _RDIV_REG }, /* 4/5: rfg=0 ba=1 bg=1 len=? */ ++ /* p0=0 c=0 r=_RDIV_REG */ ++ { 0xc0, 0x20 }, /* 6/7: rsd=0 bf=0x10 */ ++ { 0xd3, 0x40 }, /* 8/9: from datasheet */ ++ { 0xe3, 0x5b }, /* 10/11: lock window level */ ++ { 0xf0, 0x28 }, /* 12/13: br=0xa clr=0 tl=0*/ ++ { 0xe3, 0xf9 }, /* 10/11: unlock window level */ ++ }; ++ ++ /* invalid values to trigger writing */ ++ state->br = 0xff; ++ state->bf = 0xff; ++ ++ if (!state->config->rf_loop_enable) ++ zl10036_init_tab[1][0] |= 0x01; ++ ++ deb_info("%s\n", __func__); ++ ++ for (i = 0; i < ARRAY_SIZE(zl10036_init_tab); i++) { ++ ret = zl10036_write(state, zl10036_init_tab[i], 2); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int zl10036_init(struct dvb_frontend *fe) ++{ ++ struct zl10036_state *state = fe->tuner_priv; ++ int ret = 0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ ++ ++ ret = zl10036_read_status_reg(state); ++ if (ret < 0) ++ return ret; ++ ++ /* Only init if Power-on-Reset bit is set? */ ++ ret = zl10036_init_regs(state); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ ++ ++ return ret; ++} ++ ++static struct dvb_tuner_ops zl10036_tuner_ops = { ++ .info = { ++ .name = "Zarlink ZL10036", ++ .frequency_min = 950000, ++ .frequency_max = 2175000 ++ }, ++ .init = zl10036_init, ++ .release = zl10036_release, ++ .sleep = zl10036_sleep, ++ .set_params = zl10036_set_params, ++ .get_frequency = zl10036_get_frequency, ++}; ++ ++struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, ++ const struct zl10036_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct zl10036_state *state; ++ int ret; ++ ++ if (!config) { ++ printk(KERN_ERR "%s: no config specified", __func__); ++ return NULL; ++ } ++ ++ state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->config = config; ++ state->i2c = i2c; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ ++ ++ ret = zl10036_read_status_reg(state); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: No zl10036 found\n", __func__); ++ goto error; ++ } ++ ++ ret = zl10036_init_regs(state); ++ if (ret < 0) { ++ printk(KERN_ERR "%s: tuner initialization failed\n", ++ __func__); ++ goto error; ++ } ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ ++ ++ fe->tuner_priv = state; ++ ++ memcpy(&fe->ops.tuner_ops, &zl10036_tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ printk(KERN_INFO "%s: tuner initialization (%s addr=0x%02x) ok\n", ++ __func__, fe->ops.tuner_ops.info.name, config->tuner_address); ++ ++ return fe; ++ ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(zl10036_attach); ++ ++module_param_named(debug, zl10036_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++MODULE_DESCRIPTION("DVB ZL10036 driver"); ++MODULE_AUTHOR("Tino Reichardt"); ++MODULE_AUTHOR("Matthias Schwarzott"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/zl10036.h b/drivers/media/dvb-frontends/zl10036.h +new file mode 100644 +index 0000000..d84b8f8 +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10036.h +@@ -0,0 +1,53 @@ ++/** ++ * Driver for Zarlink ZL10036 DVB-S silicon tuner ++ * ++ * Copyright (C) 2006 Tino Reichardt ++ * Copyright (C) 2007-2009 Matthias Schwarzott ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License Version 2, as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef DVB_ZL10036_H ++#define DVB_ZL10036_H ++ ++#include ++#include "dvb_frontend.h" ++ ++/** ++ * Attach a zl10036 tuner to the supplied frontend structure. ++ * ++ * @param fe Frontend to attach to. ++ * @param config zl10036_config structure ++ * @return FE pointer on success, NULL on failure. ++ */ ++ ++struct zl10036_config { ++ u8 tuner_address; ++ int rf_loop_enable; ++}; ++ ++#if defined(CONFIG_DVB_ZL10036) || \ ++ (defined(CONFIG_DVB_ZL10036_MODULE) && defined(MODULE)) ++extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, ++ const struct zl10036_config *config, struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, ++ const struct zl10036_config *config, struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif ++ ++#endif /* DVB_ZL10036_H */ +diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c +new file mode 100644 +index 0000000..eff9c5f +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10039.c +@@ -0,0 +1,307 @@ ++/* ++ * Driver for Zarlink ZL10039 DVB-S tuner ++ * ++ * Copyright 2007 Jan D. Louw ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "zl10039.h" ++ ++static int debug; ++ ++#define dprintk(args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++enum zl10039_model_id { ++ ID_ZL10039 = 1 ++}; ++ ++struct zl10039_state { ++ struct i2c_adapter *i2c; ++ u8 i2c_addr; ++ u8 id; ++}; ++ ++enum zl10039_reg_addr { ++ PLL0 = 0, ++ PLL1, ++ PLL2, ++ PLL3, ++ RFFE, ++ BASE0, ++ BASE1, ++ BASE2, ++ LO0, ++ LO1, ++ LO2, ++ LO3, ++ LO4, ++ LO5, ++ LO6, ++ GENERAL ++}; ++ ++static int zl10039_read(const struct zl10039_state *state, ++ const enum zl10039_reg_addr reg, u8 *buf, ++ const size_t count) ++{ ++ u8 regbuf[] = { reg }; ++ struct i2c_msg msg[] = { ++ {/* Write register address */ ++ .addr = state->i2c_addr, ++ .flags = 0, ++ .buf = regbuf, ++ .len = 1, ++ }, {/* Read count bytes */ ++ .addr = state->i2c_addr, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = count, ++ }, ++ }; ++ ++ dprintk("%s\n", __func__); ++ ++ if (i2c_transfer(state->i2c, msg, 2) != 2) { ++ dprintk("%s: i2c read error\n", __func__); ++ return -EREMOTEIO; ++ } ++ ++ return 0; /* Success */ ++} ++ ++static int zl10039_write(struct zl10039_state *state, ++ const enum zl10039_reg_addr reg, const u8 *src, ++ const size_t count) ++{ ++ u8 buf[count + 1]; ++ struct i2c_msg msg = { ++ .addr = state->i2c_addr, ++ .flags = 0, ++ .buf = buf, ++ .len = count + 1, ++ }; ++ ++ dprintk("%s\n", __func__); ++ /* Write register address and data in one go */ ++ buf[0] = reg; ++ memcpy(&buf[1], src, count); ++ if (i2c_transfer(state->i2c, &msg, 1) != 1) { ++ dprintk("%s: i2c write error\n", __func__); ++ return -EREMOTEIO; ++ } ++ ++ return 0; /* Success */ ++} ++ ++static inline int zl10039_readreg(struct zl10039_state *state, ++ const enum zl10039_reg_addr reg, u8 *val) ++{ ++ return zl10039_read(state, reg, val, 1); ++} ++ ++static inline int zl10039_writereg(struct zl10039_state *state, ++ const enum zl10039_reg_addr reg, ++ const u8 val) ++{ ++ return zl10039_write(state, reg, &val, 1); ++} ++ ++static int zl10039_init(struct dvb_frontend *fe) ++{ ++ struct zl10039_state *state = fe->tuner_priv; ++ int ret; ++ ++ dprintk("%s\n", __func__); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ /* Reset logic */ ++ ret = zl10039_writereg(state, GENERAL, 0x40); ++ if (ret < 0) { ++ dprintk("Note: i2c write error normal when resetting the " ++ "tuner\n"); ++ } ++ /* Wake up */ ++ ret = zl10039_writereg(state, GENERAL, 0x01); ++ if (ret < 0) { ++ dprintk("Tuner power up failed\n"); ++ return ret; ++ } ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int zl10039_sleep(struct dvb_frontend *fe) ++{ ++ struct zl10039_state *state = fe->tuner_priv; ++ int ret; ++ ++ dprintk("%s\n", __func__); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = zl10039_writereg(state, GENERAL, 0x80); ++ if (ret < 0) { ++ dprintk("Tuner sleep failed\n"); ++ return ret; ++ } ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ return 0; ++} ++ ++static int zl10039_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct zl10039_state *state = fe->tuner_priv; ++ u8 buf[6]; ++ u8 bf; ++ u32 fbw; ++ u32 div; ++ int ret; ++ ++ dprintk("%s\n", __func__); ++ dprintk("Set frequency = %d, symbol rate = %d\n", ++ c->frequency, c->symbol_rate); ++ ++ /* Assumed 10.111 MHz crystal oscillator */ ++ /* Cancelled num/den 80 to prevent overflow */ ++ div = (c->frequency * 1000) / 126387; ++ fbw = (c->symbol_rate * 27) / 32000; ++ /* Cancelled num/den 10 to prevent overflow */ ++ bf = ((fbw * 5088) / 1011100) - 1; ++ ++ /*PLL divider*/ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = (div >> 0) & 0xff; ++ /*Reference divider*/ ++ /* Select reference ratio of 80 */ ++ buf[2] = 0x1D; ++ /*PLL test modes*/ ++ buf[3] = 0x40; ++ /*RF Control register*/ ++ buf[4] = 0x6E; /* Bypass enable */ ++ /*Baseband filter cutoff */ ++ buf[5] = bf; ++ ++ /* Open i2c gate */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ /* BR = 10, Enable filter adjustment */ ++ ret = zl10039_writereg(state, BASE1, 0x0A); ++ if (ret < 0) ++ goto error; ++ /* Write new config values */ ++ ret = zl10039_write(state, PLL0, buf, sizeof(buf)); ++ if (ret < 0) ++ goto error; ++ /* BR = 10, Disable filter adjustment */ ++ ret = zl10039_writereg(state, BASE1, 0x6A); ++ if (ret < 0) ++ goto error; ++ ++ /* Close i2c gate */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ return 0; ++error: ++ dprintk("Error setting tuner\n"); ++ return ret; ++} ++ ++static int zl10039_release(struct dvb_frontend *fe) ++{ ++ struct zl10039_state *state = fe->tuner_priv; ++ ++ dprintk("%s\n", __func__); ++ kfree(state); ++ fe->tuner_priv = NULL; ++ return 0; ++} ++ ++static struct dvb_tuner_ops zl10039_ops = { ++ .release = zl10039_release, ++ .init = zl10039_init, ++ .sleep = zl10039_sleep, ++ .set_params = zl10039_set_params, ++}; ++ ++struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, ++ u8 i2c_addr, struct i2c_adapter *i2c) ++{ ++ struct zl10039_state *state = NULL; ++ ++ dprintk("%s\n", __func__); ++ state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ state->i2c = i2c; ++ state->i2c_addr = i2c_addr; ++ ++ /* Open i2c gate */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ /* check if this is a valid tuner */ ++ if (zl10039_readreg(state, GENERAL, &state->id) < 0) { ++ /* Close i2c gate */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ goto error; ++ } ++ /* Close i2c gate */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ state->id = state->id & 0x0f; ++ switch (state->id) { ++ case ID_ZL10039: ++ strcpy(fe->ops.tuner_ops.info.name, ++ "Zarlink ZL10039 DVB-S tuner"); ++ break; ++ default: ++ dprintk("Chip ID=%x does not match a known type\n", state->id); ++ goto error; ++ } ++ ++ memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops)); ++ fe->tuner_priv = state; ++ dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr); ++ return fe; ++error: ++ kfree(state); ++ return NULL; ++} ++EXPORT_SYMBOL(zl10039_attach); ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver"); ++MODULE_AUTHOR("Jan D. Louw "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb-frontends/zl10039.h b/drivers/media/dvb-frontends/zl10039.h +new file mode 100644 +index 0000000..5eee7ea +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10039.h +@@ -0,0 +1,40 @@ ++/* ++ Driver for Zarlink ZL10039 DVB-S tuner ++ ++ Copyright (C) 2007 Jan D. Louw ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef ZL10039_H ++#define ZL10039_H ++ ++#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \ ++ && defined(MODULE)) ++struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, ++ u8 i2c_addr, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, ++ u8 i2c_addr, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_ZL10039 */ ++ ++#endif /* ZL10039_H */ +diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c +new file mode 100644 +index 0000000..82946cd +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10353.c +@@ -0,0 +1,684 @@ ++/* ++ * Driver for Zarlink DVB-T ZL10353 demodulator ++ * ++ * Copyright (C) 2006, 2007 Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_frontend.h" ++#include "zl10353_priv.h" ++#include "zl10353.h" ++ ++struct zl10353_state { ++ struct i2c_adapter *i2c; ++ struct dvb_frontend frontend; ++ ++ struct zl10353_config config; ++ ++ u32 bandwidth; ++ u32 ucblocks; ++ u32 frequency; ++}; ++ ++static int debug; ++#define dprintk(args...) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG "zl10353: " args); \ ++ } while (0) ++ ++static int debug_regs; ++ ++static int zl10353_single_write(struct dvb_frontend *fe, u8 reg, u8 val) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u8 buf[2] = { reg, val }; ++ struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, ++ .buf = buf, .len = 2 }; ++ int err = i2c_transfer(state->i2c, &msg, 1); ++ if (err != 1) { ++ printk("zl10353: write to reg %x failed (err = %d)!\n", reg, err); ++ return err; ++ } ++ return 0; ++} ++ ++static int zl10353_write(struct dvb_frontend *fe, const u8 ibuf[], int ilen) ++{ ++ int err, i; ++ for (i = 0; i < ilen - 1; i++) ++ if ((err = zl10353_single_write(fe, ibuf[0] + i, ibuf[i + 1]))) ++ return err; ++ ++ return 0; ++} ++ ++static int zl10353_read_register(struct zl10353_state *state, u8 reg) ++{ ++ int ret; ++ u8 b0[1] = { reg }; ++ u8 b1[1] = { 0 }; ++ struct i2c_msg msg[2] = { { .addr = state->config.demod_address, ++ .flags = 0, ++ .buf = b0, .len = 1 }, ++ { .addr = state->config.demod_address, ++ .flags = I2C_M_RD, ++ .buf = b1, .len = 1 } }; ++ ++ ret = i2c_transfer(state->i2c, msg, 2); ++ ++ if (ret != 2) { ++ printk("%s: readreg error (reg=%d, ret==%i)\n", ++ __func__, reg, ret); ++ return ret; ++ } ++ ++ return b1[0]; ++} ++ ++static void zl10353_dump_regs(struct dvb_frontend *fe) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ int ret; ++ u8 reg; ++ ++ /* Dump all registers. */ ++ for (reg = 0; ; reg++) { ++ if (reg % 16 == 0) { ++ if (reg) ++ printk(KERN_CONT "\n"); ++ printk(KERN_DEBUG "%02x:", reg); ++ } ++ ret = zl10353_read_register(state, reg); ++ if (ret >= 0) ++ printk(KERN_CONT " %02x", (u8)ret); ++ else ++ printk(KERN_CONT " --"); ++ if (reg == 0xff) ++ break; ++ } ++ printk(KERN_CONT "\n"); ++} ++ ++static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, ++ u32 bandwidth, ++ u16 *nominal_rate) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u32 adc_clock = 450560; /* 45.056 MHz */ ++ u64 value; ++ u8 bw = bandwidth / 1000000; ++ ++ if (state->config.adc_clock) ++ adc_clock = state->config.adc_clock; ++ ++ value = (u64)10 * (1 << 23) / 7 * 125; ++ value = (bw * value) + adc_clock / 2; ++ do_div(value, adc_clock); ++ *nominal_rate = value; ++ ++ dprintk("%s: bw %d, adc_clock %d => 0x%x\n", ++ __func__, bw, adc_clock, *nominal_rate); ++} ++ ++static void zl10353_calc_input_freq(struct dvb_frontend *fe, ++ u16 *input_freq) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u32 adc_clock = 450560; /* 45.056 MHz */ ++ int if2 = 361667; /* 36.1667 MHz */ ++ int ife; ++ u64 value; ++ ++ if (state->config.adc_clock) ++ adc_clock = state->config.adc_clock; ++ if (state->config.if2) ++ if2 = state->config.if2; ++ ++ if (adc_clock >= if2 * 2) ++ ife = if2; ++ else { ++ ife = adc_clock - (if2 % adc_clock); ++ if (ife > adc_clock / 2) ++ ife = adc_clock - ife; ++ } ++ value = (u64)65536 * ife + adc_clock / 2; ++ do_div(value, adc_clock); ++ *input_freq = -value; ++ ++ dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", ++ __func__, if2, ife, adc_clock, -(int)value, *input_freq); ++} ++ ++static int zl10353_sleep(struct dvb_frontend *fe) ++{ ++ static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 }; ++ ++ zl10353_write(fe, zl10353_softdown, sizeof(zl10353_softdown)); ++ return 0; ++} ++ ++static int zl10353_set_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct zl10353_state *state = fe->demodulator_priv; ++ u16 nominal_rate, input_freq; ++ u8 pllbuf[6] = { 0x67 }, acq_ctl = 0; ++ u16 tps = 0; ++ ++ state->frequency = c->frequency; ++ ++ zl10353_single_write(fe, RESET, 0x80); ++ udelay(200); ++ zl10353_single_write(fe, 0xEA, 0x01); ++ udelay(200); ++ zl10353_single_write(fe, 0xEA, 0x00); ++ ++ zl10353_single_write(fe, AGC_TARGET, 0x28); ++ ++ if (c->transmission_mode != TRANSMISSION_MODE_AUTO) ++ acq_ctl |= (1 << 0); ++ if (c->guard_interval != GUARD_INTERVAL_AUTO) ++ acq_ctl |= (1 << 1); ++ zl10353_single_write(fe, ACQ_CTL, acq_ctl); ++ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ /* These are extrapolated from the 7 and 8MHz values */ ++ zl10353_single_write(fe, MCLK_RATIO, 0x97); ++ zl10353_single_write(fe, 0x64, 0x34); ++ zl10353_single_write(fe, 0xcc, 0xdd); ++ break; ++ case 7000000: ++ zl10353_single_write(fe, MCLK_RATIO, 0x86); ++ zl10353_single_write(fe, 0x64, 0x35); ++ zl10353_single_write(fe, 0xcc, 0x73); ++ break; ++ default: ++ c->bandwidth_hz = 8000000; ++ /* fall though */ ++ case 8000000: ++ zl10353_single_write(fe, MCLK_RATIO, 0x75); ++ zl10353_single_write(fe, 0x64, 0x36); ++ zl10353_single_write(fe, 0xcc, 0x73); ++ } ++ ++ zl10353_calc_nominal_rate(fe, c->bandwidth_hz, &nominal_rate); ++ zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate)); ++ zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate)); ++ state->bandwidth = c->bandwidth_hz; ++ ++ zl10353_calc_input_freq(fe, &input_freq); ++ zl10353_single_write(fe, INPUT_FREQ_1, msb(input_freq)); ++ zl10353_single_write(fe, INPUT_FREQ_0, lsb(input_freq)); ++ ++ /* Hint at TPS settings */ ++ switch (c->code_rate_HP) { ++ case FEC_2_3: ++ tps |= (1 << 7); ++ break; ++ case FEC_3_4: ++ tps |= (2 << 7); ++ break; ++ case FEC_5_6: ++ tps |= (3 << 7); ++ break; ++ case FEC_7_8: ++ tps |= (4 << 7); ++ break; ++ case FEC_1_2: ++ case FEC_AUTO: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (c->code_rate_LP) { ++ case FEC_2_3: ++ tps |= (1 << 4); ++ break; ++ case FEC_3_4: ++ tps |= (2 << 4); ++ break; ++ case FEC_5_6: ++ tps |= (3 << 4); ++ break; ++ case FEC_7_8: ++ tps |= (4 << 4); ++ break; ++ case FEC_1_2: ++ case FEC_AUTO: ++ break; ++ case FEC_NONE: ++ if (c->hierarchy == HIERARCHY_AUTO || ++ c->hierarchy == HIERARCHY_NONE) ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (c->modulation) { ++ case QPSK: ++ break; ++ case QAM_AUTO: ++ case QAM_16: ++ tps |= (1 << 13); ++ break; ++ case QAM_64: ++ tps |= (2 << 13); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (c->transmission_mode) { ++ case TRANSMISSION_MODE_2K: ++ case TRANSMISSION_MODE_AUTO: ++ break; ++ case TRANSMISSION_MODE_8K: ++ tps |= (1 << 0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (c->guard_interval) { ++ case GUARD_INTERVAL_1_32: ++ case GUARD_INTERVAL_AUTO: ++ break; ++ case GUARD_INTERVAL_1_16: ++ tps |= (1 << 2); ++ break; ++ case GUARD_INTERVAL_1_8: ++ tps |= (2 << 2); ++ break; ++ case GUARD_INTERVAL_1_4: ++ tps |= (3 << 2); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (c->hierarchy) { ++ case HIERARCHY_AUTO: ++ case HIERARCHY_NONE: ++ break; ++ case HIERARCHY_1: ++ tps |= (1 << 10); ++ break; ++ case HIERARCHY_2: ++ tps |= (2 << 10); ++ break; ++ case HIERARCHY_4: ++ tps |= (3 << 10); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ zl10353_single_write(fe, TPS_GIVEN_1, msb(tps)); ++ zl10353_single_write(fe, TPS_GIVEN_0, lsb(tps)); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ ++ /* ++ * If there is no tuner attached to the secondary I2C bus, we call ++ * set_params to program a potential tuner attached somewhere else. ++ * Otherwise, we update the PLL registers via calc_regs. ++ */ ++ if (state->config.no_tuner) { ++ if (fe->ops.tuner_ops.set_params) { ++ fe->ops.tuner_ops.set_params(fe); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ } else if (fe->ops.tuner_ops.calc_regs) { ++ fe->ops.tuner_ops.calc_regs(fe, pllbuf + 1, 5); ++ pllbuf[1] <<= 1; ++ zl10353_write(fe, pllbuf, sizeof(pllbuf)); ++ } ++ ++ zl10353_single_write(fe, 0x5F, 0x13); ++ ++ /* If no attached tuner or invalid PLL registers, just start the FSM. */ ++ if (state->config.no_tuner || fe->ops.tuner_ops.calc_regs == NULL) ++ zl10353_single_write(fe, FSM_GO, 0x01); ++ else ++ zl10353_single_write(fe, TUNER_GO, 0x01); ++ ++ return 0; ++} ++ ++static int zl10353_get_parameters(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct zl10353_state *state = fe->demodulator_priv; ++ int s6, s9; ++ u16 tps; ++ static const u8 tps_fec_to_api[8] = { ++ FEC_1_2, ++ FEC_2_3, ++ FEC_3_4, ++ FEC_5_6, ++ FEC_7_8, ++ FEC_AUTO, ++ FEC_AUTO, ++ FEC_AUTO ++ }; ++ ++ s6 = zl10353_read_register(state, STATUS_6); ++ s9 = zl10353_read_register(state, STATUS_9); ++ if (s6 < 0 || s9 < 0) ++ return -EREMOTEIO; ++ if ((s6 & (1 << 5)) == 0 || (s9 & (1 << 4)) == 0) ++ return -EINVAL; /* no FE or TPS lock */ ++ ++ tps = zl10353_read_register(state, TPS_RECEIVED_1) << 8 | ++ zl10353_read_register(state, TPS_RECEIVED_0); ++ ++ c->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; ++ c->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; ++ ++ switch ((tps >> 13) & 3) { ++ case 0: ++ c->modulation = QPSK; ++ break; ++ case 1: ++ c->modulation = QAM_16; ++ break; ++ case 2: ++ c->modulation = QAM_64; ++ break; ++ default: ++ c->modulation = QAM_AUTO; ++ break; ++ } ++ ++ c->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : ++ TRANSMISSION_MODE_2K; ++ ++ switch ((tps >> 2) & 3) { ++ case 0: ++ c->guard_interval = GUARD_INTERVAL_1_32; ++ break; ++ case 1: ++ c->guard_interval = GUARD_INTERVAL_1_16; ++ break; ++ case 2: ++ c->guard_interval = GUARD_INTERVAL_1_8; ++ break; ++ case 3: ++ c->guard_interval = GUARD_INTERVAL_1_4; ++ break; ++ default: ++ c->guard_interval = GUARD_INTERVAL_AUTO; ++ break; ++ } ++ ++ switch ((tps >> 10) & 7) { ++ case 0: ++ c->hierarchy = HIERARCHY_NONE; ++ break; ++ case 1: ++ c->hierarchy = HIERARCHY_1; ++ break; ++ case 2: ++ c->hierarchy = HIERARCHY_2; ++ break; ++ case 3: ++ c->hierarchy = HIERARCHY_4; ++ break; ++ default: ++ c->hierarchy = HIERARCHY_AUTO; ++ break; ++ } ++ ++ c->frequency = state->frequency; ++ c->bandwidth_hz = state->bandwidth; ++ c->inversion = INVERSION_AUTO; ++ ++ return 0; ++} ++ ++static int zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ int s6, s7, s8; ++ ++ if ((s6 = zl10353_read_register(state, STATUS_6)) < 0) ++ return -EREMOTEIO; ++ if ((s7 = zl10353_read_register(state, STATUS_7)) < 0) ++ return -EREMOTEIO; ++ if ((s8 = zl10353_read_register(state, STATUS_8)) < 0) ++ return -EREMOTEIO; ++ ++ *status = 0; ++ if (s6 & (1 << 2)) ++ *status |= FE_HAS_CARRIER; ++ if (s6 & (1 << 1)) ++ *status |= FE_HAS_VITERBI; ++ if (s6 & (1 << 5)) ++ *status |= FE_HAS_LOCK; ++ if (s7 & (1 << 4)) ++ *status |= FE_HAS_SYNC; ++ if (s8 & (1 << 6)) ++ *status |= FE_HAS_SIGNAL; ++ ++ if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != ++ (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) ++ *status &= ~FE_HAS_LOCK; ++ ++ return 0; ++} ++ ++static int zl10353_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ ++ *ber = zl10353_read_register(state, RS_ERR_CNT_2) << 16 | ++ zl10353_read_register(state, RS_ERR_CNT_1) << 8 | ++ zl10353_read_register(state, RS_ERR_CNT_0); ++ ++ return 0; ++} ++ ++static int zl10353_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ ++ u16 signal = zl10353_read_register(state, AGC_GAIN_1) << 10 | ++ zl10353_read_register(state, AGC_GAIN_0) << 2 | 3; ++ ++ *strength = ~signal; ++ ++ return 0; ++} ++ ++static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u8 _snr; ++ ++ if (debug_regs) ++ zl10353_dump_regs(fe); ++ ++ _snr = zl10353_read_register(state, SNR); ++ *snr = 10 * _snr / 8; ++ ++ return 0; ++} ++ ++static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u32 ubl = 0; ++ ++ ubl = zl10353_read_register(state, RS_UBC_1) << 8 | ++ zl10353_read_register(state, RS_UBC_0); ++ ++ state->ucblocks += ubl; ++ *ucblocks = state->ucblocks; ++ ++ return 0; ++} ++ ++static int zl10353_get_tune_settings(struct dvb_frontend *fe, ++ struct dvb_frontend_tune_settings ++ *fe_tune_settings) ++{ ++ fe_tune_settings->min_delay_ms = 1000; ++ fe_tune_settings->step_size = 0; ++ fe_tune_settings->max_drift = 0; ++ ++ return 0; ++} ++ ++static int zl10353_init(struct dvb_frontend *fe) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F }; ++ ++ if (debug_regs) ++ zl10353_dump_regs(fe); ++ if (state->config.parallel_ts) ++ zl10353_reset_attach[2] &= ~0x20; ++ if (state->config.clock_ctl_1) ++ zl10353_reset_attach[3] = state->config.clock_ctl_1; ++ if (state->config.pll_0) ++ zl10353_reset_attach[4] = state->config.pll_0; ++ ++ /* Do a "hard" reset if not already done */ ++ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || ++ zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { ++ zl10353_write(fe, zl10353_reset_attach, ++ sizeof(zl10353_reset_attach)); ++ if (debug_regs) ++ zl10353_dump_regs(fe); ++ } ++ ++ return 0; ++} ++ ++static int zl10353_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ u8 val = 0x0a; ++ ++ if (state->config.disable_i2c_gate_ctrl) { ++ /* No tuner attached to the internal I2C bus */ ++ /* If set enable I2C bridge, the main I2C bus stopped hardly */ ++ return 0; ++ } ++ ++ if (enable) ++ val |= 0x10; ++ ++ return zl10353_single_write(fe, 0x62, val); ++} ++ ++static void zl10353_release(struct dvb_frontend *fe) ++{ ++ struct zl10353_state *state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops zl10353_ops; ++ ++struct dvb_frontend *zl10353_attach(const struct zl10353_config *config, ++ struct i2c_adapter *i2c) ++{ ++ struct zl10353_state *state = NULL; ++ int id; ++ ++ /* allocate memory for the internal state */ ++ state = kzalloc(sizeof(struct zl10353_state), GFP_KERNEL); ++ if (state == NULL) ++ goto error; ++ ++ /* setup the state */ ++ state->i2c = i2c; ++ memcpy(&state->config, config, sizeof(struct zl10353_config)); ++ ++ /* check if the demod is there */ ++ id = zl10353_read_register(state, CHIP_ID); ++ if ((id != ID_ZL10353) && (id != ID_CE6230) && (id != ID_CE6231)) ++ goto error; ++ ++ /* create dvb_frontend */ ++ memcpy(&state->frontend.ops, &zl10353_ops, sizeof(struct dvb_frontend_ops)); ++ state->frontend.demodulator_priv = state; ++ ++ return &state->frontend; ++error: ++ kfree(state); ++ return NULL; ++} ++ ++static struct dvb_frontend_ops zl10353_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "Zarlink ZL10353 DVB-T", ++ .frequency_min = 174000000, ++ .frequency_max = 862000000, ++ .frequency_stepsize = 166667, ++ .frequency_tolerance = 0, ++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | ++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | ++ FE_CAN_FEC_AUTO | ++ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | ++ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | ++ FE_CAN_MUTE_TS ++ }, ++ ++ .release = zl10353_release, ++ ++ .init = zl10353_init, ++ .sleep = zl10353_sleep, ++ .i2c_gate_ctrl = zl10353_i2c_gate_ctrl, ++ .write = zl10353_write, ++ ++ .set_frontend = zl10353_set_parameters, ++ .get_frontend = zl10353_get_parameters, ++ .get_tune_settings = zl10353_get_tune_settings, ++ ++ .read_status = zl10353_read_status, ++ .read_ber = zl10353_read_ber, ++ .read_signal_strength = zl10353_read_signal_strength, ++ .read_snr = zl10353_read_snr, ++ .read_ucblocks = zl10353_read_ucblocks, ++}; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); ++ ++module_param(debug_regs, int, 0644); ++MODULE_PARM_DESC(debug_regs, "Turn on/off frontend register dumps (default:off)."); ++ ++MODULE_DESCRIPTION("Zarlink ZL10353 DVB-T demodulator driver"); ++MODULE_AUTHOR("Chris Pascoe"); ++MODULE_LICENSE("GPL"); ++ ++EXPORT_SYMBOL(zl10353_attach); +diff --git a/drivers/media/dvb-frontends/zl10353.h b/drivers/media/dvb-frontends/zl10353.h +new file mode 100644 +index 0000000..6e3ca9e +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10353.h +@@ -0,0 +1,62 @@ ++/* ++ * Driver for Zarlink DVB-T ZL10353 demodulator ++ * ++ * Copyright (C) 2006, 2007 Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= ++ */ ++ ++#ifndef ZL10353_H ++#define ZL10353_H ++ ++#include ++ ++struct zl10353_config ++{ ++ /* demodulator's I2C address */ ++ u8 demod_address; ++ ++ /* frequencies in units of 0.1kHz */ ++ int adc_clock; /* default: 450560 (45.056 MHz) */ ++ int if2; /* default: 361667 (36.1667 MHz) */ ++ ++ /* set if no pll is connected to the secondary i2c bus */ ++ int no_tuner; ++ ++ /* set if parallel ts output is required */ ++ int parallel_ts; ++ ++ /* set if i2c_gate_ctrl disable is required */ ++ u8 disable_i2c_gate_ctrl:1; ++ ++ /* clock control registers (0x51-0x54) */ ++ u8 clock_ctl_1; /* default: 0x46 */ ++ u8 pll_0; /* default: 0x15 */ ++}; ++ ++#if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE)) ++extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, ++ struct i2c_adapter *i2c); ++#else ++static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, ++ struct i2c_adapter *i2c) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return NULL; ++} ++#endif /* CONFIG_DVB_ZL10353 */ ++ ++#endif /* ZL10353_H */ +diff --git a/drivers/media/dvb-frontends/zl10353_priv.h b/drivers/media/dvb-frontends/zl10353_priv.h +new file mode 100644 +index 0000000..e0dd1d3 +--- /dev/null ++++ b/drivers/media/dvb-frontends/zl10353_priv.h +@@ -0,0 +1,79 @@ ++/* ++ * Driver for Zarlink DVB-T ZL10353 demodulator ++ * ++ * Copyright (C) 2006, 2007 Christopher Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _ZL10353_PRIV_ ++#define _ZL10353_PRIV_ ++ ++#define ID_ZL10353 0x14 /* Zarlink ZL10353 */ ++#define ID_CE6230 0x18 /* Intel CE6230 */ ++#define ID_CE6231 0x19 /* Intel CE6231 */ ++ ++#define msb(x) (((x) >> 8) & 0xff) ++#define lsb(x) ((x) & 0xff) ++ ++enum zl10353_reg_addr { ++ INTERRUPT_0 = 0x00, ++ INTERRUPT_1 = 0x01, ++ INTERRUPT_2 = 0x02, ++ INTERRUPT_3 = 0x03, ++ INTERRUPT_4 = 0x04, ++ INTERRUPT_5 = 0x05, ++ STATUS_6 = 0x06, ++ STATUS_7 = 0x07, ++ STATUS_8 = 0x08, ++ STATUS_9 = 0x09, ++ AGC_GAIN_1 = 0x0A, ++ AGC_GAIN_0 = 0x0B, ++ SNR = 0x10, ++ RS_ERR_CNT_2 = 0x11, ++ RS_ERR_CNT_1 = 0x12, ++ RS_ERR_CNT_0 = 0x13, ++ RS_UBC_1 = 0x14, ++ RS_UBC_0 = 0x15, ++ TPS_RECEIVED_1 = 0x1D, ++ TPS_RECEIVED_0 = 0x1E, ++ TPS_CURRENT_1 = 0x1F, ++ TPS_CURRENT_0 = 0x20, ++ CLOCK_CTL_0 = 0x51, ++ CLOCK_CTL_1 = 0x52, ++ PLL_0 = 0x53, ++ PLL_1 = 0x54, ++ RESET = 0x55, ++ AGC_TARGET = 0x56, ++ MCLK_RATIO = 0x5C, ++ ACQ_CTL = 0x5E, ++ TRL_NOMINAL_RATE_1 = 0x65, ++ TRL_NOMINAL_RATE_0 = 0x66, ++ INPUT_FREQ_1 = 0x6C, ++ INPUT_FREQ_0 = 0x6D, ++ TPS_GIVEN_1 = 0x6E, ++ TPS_GIVEN_0 = 0x6F, ++ TUNER_GO = 0x70, ++ FSM_GO = 0x71, ++ CHIP_ID = 0x7F, ++ CHAN_STEP_1 = 0xE4, ++ CHAN_STEP_0 = 0xE5, ++ OFDM_LOCK_TIME = 0xE7, ++ FEC_LOCK_TIME = 0xE8, ++ ACQ_DELAY = 0xE9, ++}; ++ ++#endif /* _ZL10353_PRIV_ */ +diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig +deleted file mode 100644 +index f6e40b3..0000000 +--- a/drivers/media/dvb/Kconfig ++++ /dev/null +@@ -1,91 +0,0 @@ +-# +-# DVB device configuration +-# +- +-config DVB_MAX_ADAPTERS +- int "maximum number of DVB/ATSC adapters" +- depends on DVB_CORE +- default 8 +- range 1 255 +- help +- Maximum number of DVB/ATSC adapters. Increasing this number +- increases the memory consumption of the DVB subsystem even +- if a much lower number of DVB/ATSC adapters is present. +- Only values in the range 4-32 are tested. +- +- If you are unsure about this, use the default value 8 +- +-config DVB_DYNAMIC_MINORS +- bool "Dynamic DVB minor allocation" +- depends on DVB_CORE +- default n +- help +- If you say Y here, the DVB subsystem will use dynamic minor +- allocation for any device that uses the DVB major number. +- This means that you can have more than 4 of a single type +- of device (like demuxes and frontends) per adapter, but udev +- will be required to manage the device nodes. +- +- If you are unsure about this, say N here. +- +-menuconfig DVB_CAPTURE_DRIVERS +- bool "DVB/ATSC adapters" +- depends on DVB_CORE +- default y +- ---help--- +- Say Y to select Digital TV adapters +- +-if DVB_CAPTURE_DRIVERS && DVB_CORE +- +-comment "Supported SAA7146 based PCI Adapters" +- depends on DVB_CORE && PCI && I2C +-source "drivers/media/dvb/ttpci/Kconfig" +- +-comment "Supported USB Adapters" +- depends on DVB_CORE && USB && I2C +-source "drivers/media/dvb/dvb-usb/Kconfig" +-source "drivers/media/dvb/ttusb-budget/Kconfig" +-source "drivers/media/dvb/ttusb-dec/Kconfig" +-source "drivers/media/dvb/siano/Kconfig" +- +-comment "Supported FlexCopII (B2C2) Adapters" +- depends on DVB_CORE && (PCI || USB) && I2C +-source "drivers/media/dvb/b2c2/Kconfig" +- +-comment "Supported BT878 Adapters" +- depends on DVB_CORE && PCI && I2C +-source "drivers/media/dvb/bt8xx/Kconfig" +- +-comment "Supported Pluto2 Adapters" +- depends on DVB_CORE && PCI && I2C +-source "drivers/media/dvb/pluto2/Kconfig" +- +-comment "Supported SDMC DM1105 Adapters" +- depends on DVB_CORE && PCI && I2C +-source "drivers/media/dvb/dm1105/Kconfig" +- +-comment "Supported FireWire (IEEE 1394) Adapters" +- depends on DVB_CORE && FIREWIRE +-source "drivers/media/dvb/firewire/Kconfig" +- +-comment "Supported Earthsoft PT1 Adapters" +- depends on DVB_CORE && PCI && I2C +-source "drivers/media/dvb/pt1/Kconfig" +- +-comment "Supported Mantis Adapters" +- depends on DVB_CORE && PCI && I2C +- source "drivers/media/dvb/mantis/Kconfig" +- +-comment "Supported nGene Adapters" +- depends on DVB_CORE && PCI && I2C +- source "drivers/media/dvb/ngene/Kconfig" +- +-comment "Supported ddbridge ('Octopus') Adapters" +- depends on DVB_CORE && PCI && I2C +- source "drivers/media/dvb/ddbridge/Kconfig" +- +-comment "Supported DVB Frontends" +- depends on DVB_CORE +-source "drivers/media/dvb/frontends/Kconfig" +- +-endif # DVB_CAPTURE_DRIVERS +diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile +deleted file mode 100644 +index b2cefe6..0000000 +--- a/drivers/media/dvb/Makefile ++++ /dev/null +@@ -1,21 +0,0 @@ +-# +-# Makefile for the kernel multimedia device drivers. +-# +- +-obj-y := dvb-core/ \ +- frontends/ \ +- ttpci/ \ +- ttusb-dec/ \ +- ttusb-budget/ \ +- b2c2/ \ +- bt8xx/ \ +- dvb-usb/ \ +- pluto2/ \ +- siano/ \ +- dm1105/ \ +- pt1/ \ +- mantis/ \ +- ngene/ \ +- ddbridge/ +- +-obj-$(CONFIG_DVB_FIREDTV) += firewire/ +diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig +deleted file mode 100644 +index 9e57814..0000000 +--- a/drivers/media/dvb/b2c2/Kconfig ++++ /dev/null +@@ -1,45 +0,0 @@ +-config DVB_B2C2_FLEXCOP +- tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" +- depends on DVB_CORE && I2C +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- select DVB_MT312 if !DVB_FE_CUSTOMISE +- select DVB_NXT200X if !DVB_FE_CUSTOMISE +- select DVB_STV0297 if !DVB_FE_CUSTOMISE +- select DVB_BCM3510 if !DVB_FE_CUSTOMISE +- select DVB_LGDT330X if !DVB_FE_CUSTOMISE +- select DVB_S5H1420 if !DVB_FE_CUSTOMISE +- select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE +- select DVB_ISL6421 if !DVB_FE_CUSTOMISE +- select DVB_CX24123 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE +- select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE +- help +- Support for the digital TV receiver chip made by B2C2 Inc. included in +- Technisats PCI cards and USB boxes. +- +- Say Y if you own such a device and want to use it. +- +-config DVB_B2C2_FLEXCOP_PCI +- tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" +- depends on DVB_B2C2_FLEXCOP && PCI && I2C +- help +- Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. +- +- Say Y if you own such a device and want to use it. +- +-config DVB_B2C2_FLEXCOP_USB +- tristate "Technisat/B2C2 Air/Sky/Cable2PC USB" +- depends on DVB_B2C2_FLEXCOP && USB && I2C +- help +- Support for the Air/Sky/Cable2PC USB1.1 box (DVB/ATSC) by Technisat/B2C2, +- +- Say Y if you own such a device and want to use it. +- +-config DVB_B2C2_FLEXCOP_DEBUG +- bool "Enable debug for the B2C2 FlexCop drivers" +- depends on DVB_B2C2_FLEXCOP +- help +- Say Y if you want to enable the module option to control debug messages +- of all B2C2 FlexCop drivers. +diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile +deleted file mode 100644 +index 3d04a8d..0000000 +--- a/drivers/media/dvb/b2c2/Makefile ++++ /dev/null +@@ -1,16 +0,0 @@ +-b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \ +- flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o +-obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o +- +-ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),) +-b2c2-flexcop-objs += flexcop-dma.o +-endif +- +-b2c2-flexcop-pci-objs = flexcop-pci.o +-obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o +- +-b2c2-flexcop-usb-objs = flexcop-usb.o +-obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +-ccflags-y += -Idrivers/media/common/tuners/ +diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h +deleted file mode 100644 +index 437912e..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-common.h ++++ /dev/null +@@ -1,185 +0,0 @@ +-/* +- * 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 +-#include +-#include +- +-#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/dvb/b2c2/flexcop-dma.c b/drivers/media/dvb/b2c2/flexcop-dma.c +deleted file mode 100644 +index 2881e0d..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-dma.c ++++ /dev/null +@@ -1,172 +0,0 @@ +-/* +- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III +- * flexcop-dma.c - configuring and controlling the DMA of the FlexCop +- * see flexcop.c for copyright information +- */ +-#include "flexcop.h" +- +-int flexcop_dma_allocate(struct pci_dev *pdev, +- struct flexcop_dma *dma, u32 size) +-{ +- u8 *tcpu; +- dma_addr_t tdma = 0; +- +- if (size % 2) { +- err("dma buffersize has to be even."); +- return -EINVAL; +- } +- +- if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) { +- dma->pdev = pdev; +- dma->cpu_addr0 = tcpu; +- dma->dma_addr0 = tdma; +- dma->cpu_addr1 = tcpu + size/2; +- dma->dma_addr1 = tdma + size/2; +- dma->size = size/2; +- return 0; +- } +- return -ENOMEM; +-} +-EXPORT_SYMBOL(flexcop_dma_allocate); +- +-void flexcop_dma_free(struct flexcop_dma *dma) +-{ +- pci_free_consistent(dma->pdev, dma->size*2, +- dma->cpu_addr0, dma->dma_addr0); +- memset(dma,0,sizeof(struct flexcop_dma)); +-} +-EXPORT_SYMBOL(flexcop_dma_free); +- +-int flexcop_dma_config(struct flexcop_device *fc, +- struct flexcop_dma *dma, +- flexcop_dma_index_t dma_idx) +-{ +- flexcop_ibi_value v0x0,v0x4,v0xc; +- v0x0.raw = v0x4.raw = v0xc.raw = 0; +- +- v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2; +- v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2; +- v0x4.dma_0x4_write.dma_addr_size = dma->size / 4; +- +- if ((dma_idx & FC_DMA_1) == dma_idx) { +- fc->write_ibi_reg(fc,dma1_000,v0x0); +- fc->write_ibi_reg(fc,dma1_004,v0x4); +- fc->write_ibi_reg(fc,dma1_00c,v0xc); +- } else if ((dma_idx & FC_DMA_2) == dma_idx) { +- fc->write_ibi_reg(fc,dma2_010,v0x0); +- fc->write_ibi_reg(fc,dma2_014,v0x4); +- fc->write_ibi_reg(fc,dma2_01c,v0xc); +- } else { +- err("either DMA1 or DMA2 can be configured within one " +- "flexcop_dma_config call."); +- return -EINVAL; +- } +- +- return 0; +-} +-EXPORT_SYMBOL(flexcop_dma_config); +- +-/* start the DMA transfers, but not the DMA IRQs */ +-int flexcop_dma_xfer_control(struct flexcop_device *fc, +- flexcop_dma_index_t dma_idx, +- flexcop_dma_addr_index_t index, +- int onoff) +-{ +- flexcop_ibi_value v0x0,v0xc; +- flexcop_ibi_register r0x0,r0xc; +- +- if ((dma_idx & FC_DMA_1) == dma_idx) { +- r0x0 = dma1_000; +- r0xc = dma1_00c; +- } else if ((dma_idx & FC_DMA_2) == dma_idx) { +- r0x0 = dma2_010; +- r0xc = dma2_01c; +- } else { +- err("either transfer DMA1 or DMA2 can be started within one " +- "flexcop_dma_xfer_control call."); +- return -EINVAL; +- } +- +- v0x0 = fc->read_ibi_reg(fc,r0x0); +- v0xc = fc->read_ibi_reg(fc,r0xc); +- +- deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); +- deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); +- +- if (index & FC_DMA_SUBADDR_0) +- v0x0.dma_0x0.dma_0start = onoff; +- +- if (index & FC_DMA_SUBADDR_1) +- v0xc.dma_0xc.dma_1start = onoff; +- +- fc->write_ibi_reg(fc,r0x0,v0x0); +- fc->write_ibi_reg(fc,r0xc,v0xc); +- +- deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); +- deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); +- return 0; +-} +-EXPORT_SYMBOL(flexcop_dma_xfer_control); +- +-static int flexcop_dma_remap(struct flexcop_device *fc, +- flexcop_dma_index_t dma_idx, +- int onoff) +-{ +- flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c; +- flexcop_ibi_value v = fc->read_ibi_reg(fc,r); +- deb_info("%s\n",__func__); +- v.dma_0xc.remap_enable = onoff; +- fc->write_ibi_reg(fc,r,v); +- return 0; +-} +- +-int flexcop_dma_control_size_irq(struct flexcop_device *fc, +- flexcop_dma_index_t no, +- int onoff) +-{ +- flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); +- +- if (no & FC_DMA_1) +- v.ctrl_208.DMA1_IRQ_Enable_sig = onoff; +- +- if (no & FC_DMA_2) +- v.ctrl_208.DMA2_IRQ_Enable_sig = onoff; +- +- fc->write_ibi_reg(fc,ctrl_208,v); +- return 0; +-} +-EXPORT_SYMBOL(flexcop_dma_control_size_irq); +- +-int flexcop_dma_control_timer_irq(struct flexcop_device *fc, +- flexcop_dma_index_t no, +- int onoff) +-{ +- flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); +- +- if (no & FC_DMA_1) +- v.ctrl_208.DMA1_Timer_Enable_sig = onoff; +- +- if (no & FC_DMA_2) +- v.ctrl_208.DMA2_Timer_Enable_sig = onoff; +- +- fc->write_ibi_reg(fc,ctrl_208,v); +- return 0; +-} +-EXPORT_SYMBOL(flexcop_dma_control_timer_irq); +- +-/* 1 cycles = 1.97 msec */ +-int flexcop_dma_config_timer(struct flexcop_device *fc, +- flexcop_dma_index_t dma_idx, u8 cycles) +-{ +- flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014; +- flexcop_ibi_value v = fc->read_ibi_reg(fc,r); +- +- flexcop_dma_remap(fc,dma_idx,0); +- +- deb_info("%s\n",__func__); +- v.dma_0x4_write.dmatimer = cycles; +- fc->write_ibi_reg(fc,r,v); +- return 0; +-} +-EXPORT_SYMBOL(flexcop_dma_config_timer); +- +diff --git a/drivers/media/dvb/b2c2/flexcop-eeprom.c b/drivers/media/dvb/b2c2/flexcop-eeprom.c +deleted file mode 100644 +index a25373a..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-eeprom.c ++++ /dev/null +@@ -1,147 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +deleted file mode 100644 +index 850a6c6..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c ++++ /dev/null +@@ -1,678 +0,0 @@ +-/* +- * 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 +-#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)) { +- 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)) { +- 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 request_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/dvb/b2c2/flexcop-hw-filter.c b/drivers/media/dvb/b2c2/flexcop-hw-filter.c +deleted file mode 100644 +index 77e4547..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-hw-filter.c ++++ /dev/null +@@ -1,232 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c +deleted file mode 100644 +index 965d5eb..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-i2c.c ++++ /dev/null +@@ -1,288 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-misc.c b/drivers/media/dvb/b2c2/flexcop-misc.c +deleted file mode 100644 +index f06f3a9..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-misc.c ++++ /dev/null +@@ -1,86 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c +deleted file mode 100644 +index 44f8fb5..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-pci.c ++++ /dev/null +@@ -1,450 +0,0 @@ +-/* +- * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III +- * flexcop-pci.c - covers the PCI part including DMA transfers +- * see flexcop.c for copyright information +- */ +- +-#define FC_LOG_PREFIX "flexcop-pci" +-#include "flexcop-common.h" +- +-static int enable_pid_filtering = 1; +-module_param(enable_pid_filtering, int, 0444); +-MODULE_PARM_DESC(enable_pid_filtering, +- "enable hardware pid filtering: supported values: 0 (fullts), 1"); +- +-static int irq_chk_intv = 100; +-module_param(irq_chk_intv, int, 0644); +-MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog."); +- +-#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +-#define dprintk(level,args...) \ +- do { if ((debug & level)) printk(args); } while (0) +-#define DEBSTATUS "" +-#else +-#define dprintk(level,args...) +-#define DEBSTATUS " (debugging is not enabled)" +-#endif +- +-#define deb_info(args...) dprintk(0x01, args) +-#define deb_reg(args...) dprintk(0x02, args) +-#define deb_ts(args...) dprintk(0x04, args) +-#define deb_irq(args...) dprintk(0x08, args) +-#define deb_chk(args...) dprintk(0x10, args) +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))." +- DEBSTATUS); +- +-#define DRIVER_VERSION "0.1" +-#define DRIVER_NAME "flexcop-pci" +-#define DRIVER_AUTHOR "Patrick Boettcher " +- +-struct flexcop_pci { +- struct pci_dev *pdev; +- +-#define FC_PCI_INIT 0x01 +-#define FC_PCI_DMA_INIT 0x02 +- int init_state; +- +- void __iomem *io_mem; +- u32 irq; +- /* buffersize (at least for DMA1, need to be % 188 == 0, +- * this logic is required */ +-#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188) +-#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188) +- struct flexcop_dma dma[2]; +- +- int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */ +- u32 last_dma1_cur_pos; +- /* position of the pointer last time the timer/packet irq occurred */ +- int count; +- int count_prev; +- int stream_problem; +- +- spinlock_t irq_lock; +- unsigned long last_irq; +- +- struct delayed_work irq_check_work; +- struct flexcop_device *fc_dev; +-}; +- +-static int lastwreg, lastwval, lastrreg, lastrval; +- +-static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc, +- flexcop_ibi_register r) +-{ +- struct flexcop_pci *fc_pci = fc->bus_specific; +- flexcop_ibi_value v; +- v.raw = readl(fc_pci->io_mem + r); +- +- if (lastrreg != r || lastrval != v.raw) { +- lastrreg = r; lastrval = v.raw; +- deb_reg("new rd: %3x: %08x\n", r, v.raw); +- } +- +- return v; +-} +- +-static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, +- flexcop_ibi_register r, flexcop_ibi_value v) +-{ +- struct flexcop_pci *fc_pci = fc->bus_specific; +- +- if (lastwreg != r || lastwval != v.raw) { +- lastwreg = r; lastwval = v.raw; +- deb_reg("new wr: %3x: %08x\n", r, v.raw); +- } +- +- writel(v.raw, fc_pci->io_mem + r); +- return 0; +-} +- +-static void flexcop_pci_irq_check_work(struct work_struct *work) +-{ +- struct flexcop_pci *fc_pci = +- container_of(work, struct flexcop_pci, irq_check_work.work); +- struct flexcop_device *fc = fc_pci->fc_dev; +- +- if (fc->feedcount) { +- +- if (fc_pci->count == fc_pci->count_prev) { +- deb_chk("no IRQ since the last check\n"); +- if (fc_pci->stream_problem++ == 3) { +- struct dvb_demux_feed *feed; +- deb_info("flexcop-pci: stream problem, resetting pid filter\n"); +- +- spin_lock_irq(&fc->demux.lock); +- list_for_each_entry(feed, &fc->demux.feed_list, +- list_head) { +- flexcop_pid_feed_control(fc, feed, 0); +- } +- +- list_for_each_entry(feed, &fc->demux.feed_list, +- list_head) { +- flexcop_pid_feed_control(fc, feed, 1); +- } +- spin_unlock_irq(&fc->demux.lock); +- +- fc_pci->stream_problem = 0; +- } +- } else { +- fc_pci->stream_problem = 0; +- fc_pci->count_prev = fc_pci->count; +- } +- } +- +- schedule_delayed_work(&fc_pci->irq_check_work, +- msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); +-} +- +-/* When PID filtering is turned on, we use the timer IRQ, because small amounts +- * of data need to be passed to the user space instantly as well. When PID +- * filtering is turned off, we use the page-change-IRQ */ +-static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) +-{ +- struct flexcop_pci *fc_pci = dev_id; +- struct flexcop_device *fc = fc_pci->fc_dev; +- unsigned long flags; +- flexcop_ibi_value v; +- irqreturn_t ret = IRQ_HANDLED; +- +- spin_lock_irqsave(&fc_pci->irq_lock, flags); +- v = fc->read_ibi_reg(fc, irq_20c); +- +- /* errors */ +- if (v.irq_20c.Data_receiver_error) +- deb_chk("data receiver error\n"); +- if (v.irq_20c.Continuity_error_flag) +- deb_chk("Contunuity error flag is set\n"); +- if (v.irq_20c.LLC_SNAP_FLAG_set) +- deb_chk("LLC_SNAP_FLAG_set is set\n"); +- if (v.irq_20c.Transport_Error) +- deb_chk("Transport error\n"); +- +- if ((fc_pci->count % 1000) == 0) +- deb_chk("%d valid irq took place so far\n", fc_pci->count); +- +- if (v.irq_20c.DMA1_IRQ_Status == 1) { +- if (fc_pci->active_dma1_addr == 0) +- flexcop_pass_dmx_packets(fc_pci->fc_dev, +- fc_pci->dma[0].cpu_addr0, +- fc_pci->dma[0].size / 188); +- else +- flexcop_pass_dmx_packets(fc_pci->fc_dev, +- fc_pci->dma[0].cpu_addr1, +- fc_pci->dma[0].size / 188); +- +- deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr); +- fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr; +- /* for the timer IRQ we only can use buffer dmx feeding, because we don't have +- * complete TS packets when reading from the DMA memory */ +- } else if (v.irq_20c.DMA1_Timer_Status == 1) { +- dma_addr_t cur_addr = +- fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2; +- u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0; +- +- deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, " +- "last_cur_pos: %08x ", +- jiffies_to_usecs(jiffies - fc_pci->last_irq), +- v.raw, (unsigned long long)cur_addr, cur_pos, +- fc_pci->last_dma1_cur_pos); +- fc_pci->last_irq = jiffies; +- +- /* buffer end was reached, restarted from the beginning +- * pass the data from last_cur_pos to the buffer end to the demux +- */ +- if (cur_pos < fc_pci->last_dma1_cur_pos) { +- deb_irq(" end was reached: passing %d bytes ", +- (fc_pci->dma[0].size*2 - 1) - +- fc_pci->last_dma1_cur_pos); +- flexcop_pass_dmx_data(fc_pci->fc_dev, +- fc_pci->dma[0].cpu_addr0 + +- fc_pci->last_dma1_cur_pos, +- (fc_pci->dma[0].size*2) - +- fc_pci->last_dma1_cur_pos); +- fc_pci->last_dma1_cur_pos = 0; +- } +- +- if (cur_pos > fc_pci->last_dma1_cur_pos) { +- deb_irq(" passing %d bytes ", +- cur_pos - fc_pci->last_dma1_cur_pos); +- flexcop_pass_dmx_data(fc_pci->fc_dev, +- fc_pci->dma[0].cpu_addr0 + +- fc_pci->last_dma1_cur_pos, +- cur_pos - fc_pci->last_dma1_cur_pos); +- } +- deb_irq("\n"); +- +- fc_pci->last_dma1_cur_pos = cur_pos; +- fc_pci->count++; +- } else { +- deb_irq("isr for flexcop called, " +- "apparently without reason (%08x)\n", v.raw); +- ret = IRQ_NONE; +- } +- +- spin_unlock_irqrestore(&fc_pci->irq_lock, flags); +- return ret; +-} +- +-static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff) +-{ +- struct flexcop_pci *fc_pci = fc->bus_specific; +- if (onoff) { +- flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1); +- flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2); +- flexcop_dma_config_timer(fc, FC_DMA_1, 0); +- flexcop_dma_xfer_control(fc, FC_DMA_1, +- FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1); +- deb_irq("DMA xfer enabled\n"); +- +- fc_pci->last_dma1_cur_pos = 0; +- flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1); +- deb_irq("IRQ enabled\n"); +- fc_pci->count_prev = fc_pci->count; +- } else { +- flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0); +- deb_irq("IRQ disabled\n"); +- +- flexcop_dma_xfer_control(fc, FC_DMA_1, +- FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0); +- deb_irq("DMA xfer disabled\n"); +- } +- return 0; +-} +- +-static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci) +-{ +- int ret; +- ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0], +- FC_DEFAULT_DMA1_BUFSIZE); +- if (ret != 0) +- return ret; +- +- ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1], +- FC_DEFAULT_DMA2_BUFSIZE); +- if (ret != 0) { +- flexcop_dma_free(&fc_pci->dma[0]); +- return ret; +- } +- +- flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA | +- FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1); +- flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO | +- FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2); +- fc_pci->init_state |= FC_PCI_DMA_INIT; +- return ret; +-} +- +-static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci) +-{ +- if (fc_pci->init_state & FC_PCI_DMA_INIT) { +- flexcop_dma_free(&fc_pci->dma[0]); +- flexcop_dma_free(&fc_pci->dma[1]); +- } +- fc_pci->init_state &= ~FC_PCI_DMA_INIT; +-} +- +-static int flexcop_pci_init(struct flexcop_pci *fc_pci) +-{ +- int ret; +- +- info("card revision %x", fc_pci->pdev->revision); +- +- if ((ret = pci_enable_device(fc_pci->pdev)) != 0) +- return ret; +- pci_set_master(fc_pci->pdev); +- +- if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0) +- goto err_pci_disable_device; +- +- fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800); +- +- if (!fc_pci->io_mem) { +- err("cannot map io memory\n"); +- ret = -EIO; +- goto err_pci_release_regions; +- } +- +- pci_set_drvdata(fc_pci->pdev, fc_pci); +- spin_lock_init(&fc_pci->irq_lock); +- if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, +- IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) +- goto err_pci_iounmap; +- +- fc_pci->init_state |= FC_PCI_INIT; +- return ret; +- +-err_pci_iounmap: +- pci_iounmap(fc_pci->pdev, fc_pci->io_mem); +- pci_set_drvdata(fc_pci->pdev, NULL); +-err_pci_release_regions: +- pci_release_regions(fc_pci->pdev); +-err_pci_disable_device: +- pci_disable_device(fc_pci->pdev); +- return ret; +-} +- +-static void flexcop_pci_exit(struct flexcop_pci *fc_pci) +-{ +- if (fc_pci->init_state & FC_PCI_INIT) { +- free_irq(fc_pci->pdev->irq, fc_pci); +- pci_iounmap(fc_pci->pdev, fc_pci->io_mem); +- pci_set_drvdata(fc_pci->pdev, NULL); +- pci_release_regions(fc_pci->pdev); +- pci_disable_device(fc_pci->pdev); +- } +- fc_pci->init_state &= ~FC_PCI_INIT; +-} +- +-static int flexcop_pci_probe(struct pci_dev *pdev, +- const struct pci_device_id *ent) +-{ +- struct flexcop_device *fc; +- struct flexcop_pci *fc_pci; +- int ret = -ENOMEM; +- +- if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) { +- err("out of memory\n"); +- return -ENOMEM; +- } +- +- /* general flexcop init */ +- fc_pci = fc->bus_specific; +- fc_pci->fc_dev = fc; +- +- fc->read_ibi_reg = flexcop_pci_read_ibi_reg; +- fc->write_ibi_reg = flexcop_pci_write_ibi_reg; +- fc->i2c_request = flexcop_i2c_request; +- fc->get_mac_addr = flexcop_eeprom_check_mac_addr; +- fc->stream_control = flexcop_pci_stream_control; +- +- if (enable_pid_filtering) +- info("will use the HW PID filter."); +- else +- info("will pass the complete TS to the demuxer."); +- +- fc->pid_filtering = enable_pid_filtering; +- fc->bus_type = FC_PCI; +- fc->dev = &pdev->dev; +- fc->owner = THIS_MODULE; +- +- /* bus specific part */ +- fc_pci->pdev = pdev; +- if ((ret = flexcop_pci_init(fc_pci)) != 0) +- goto err_kfree; +- +- /* init flexcop */ +- if ((ret = flexcop_device_initialize(fc)) != 0) +- goto err_pci_exit; +- +- /* init dma */ +- if ((ret = flexcop_pci_dma_init(fc_pci)) != 0) +- goto err_fc_exit; +- +- INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work); +- +- if (irq_chk_intv > 0) +- schedule_delayed_work(&fc_pci->irq_check_work, +- msecs_to_jiffies(irq_chk_intv < 100 ? +- 100 : +- irq_chk_intv)); +- return ret; +- +-err_fc_exit: +- flexcop_device_exit(fc); +-err_pci_exit: +- flexcop_pci_exit(fc_pci); +-err_kfree: +- flexcop_device_kfree(fc); +- return ret; +-} +- +-/* in theory every _exit function should be called exactly two times, +- * here and in the bail-out-part of the _init-function +- */ +-static void flexcop_pci_remove(struct pci_dev *pdev) +-{ +- struct flexcop_pci *fc_pci = pci_get_drvdata(pdev); +- +- if (irq_chk_intv > 0) +- cancel_delayed_work(&fc_pci->irq_check_work); +- +- flexcop_pci_dma_exit(fc_pci); +- flexcop_device_exit(fc_pci->fc_dev); +- flexcop_pci_exit(fc_pci); +- flexcop_device_kfree(fc_pci->fc_dev); +-} +- +-static struct pci_device_id flexcop_pci_tbl[] = { +- { PCI_DEVICE(0x13d0, 0x2103) }, +- { }, +-}; +- +-MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl); +- +-static struct pci_driver flexcop_pci_driver = { +- .name = "b2c2_flexcop_pci", +- .id_table = flexcop_pci_tbl, +- .probe = flexcop_pci_probe, +- .remove = flexcop_pci_remove, +-}; +- +-static int __init flexcop_pci_module_init(void) +-{ +- return pci_register_driver(&flexcop_pci_driver); +-} +- +-static void __exit flexcop_pci_module_exit(void) +-{ +- pci_unregister_driver(&flexcop_pci_driver); +-} +- +-module_init(flexcop_pci_module_init); +-module_exit(flexcop_pci_module_exit); +- +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_DESCRIPTION(DRIVER_NAME); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/dvb/b2c2/flexcop-reg.h +deleted file mode 100644 +index dc4528d..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-reg.h ++++ /dev/null +@@ -1,166 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-sram.c b/drivers/media/dvb/b2c2/flexcop-sram.c +deleted file mode 100644 +index f2199e4..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-sram.c ++++ /dev/null +@@ -1,363 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c +deleted file mode 100644 +index 26c666d..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-usb.c ++++ /dev/null +@@ -1,590 +0,0 @@ +-/* +- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III +- * flexcop-usb.c - covers the USB part +- * see flexcop.c for copyright information +- */ +-#define FC_LOG_PREFIX "flexcop_usb" +-#include "flexcop-usb.h" +-#include "flexcop-common.h" +- +-/* Version information */ +-#define DRIVER_VERSION "0.1" +-#define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV USB Driver" +-#define DRIVER_AUTHOR "Patrick Boettcher " +- +-/* debug */ +-#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG +-#define dprintk(level,args...) \ +- do { if ((debug & level)) printk(args); } while (0) +- +-#define debug_dump(b, l, method) do {\ +- int i; \ +- for (i = 0; i < l; i++) \ +- method("%02x ", b[i]); \ +- method("\n"); \ +-} while (0) +- +-#define DEBSTATUS "" +-#else +-#define dprintk(level, args...) +-#define debug_dump(b, l, method) +-#define DEBSTATUS " (debugging is not enabled)" +-#endif +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2," +- "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS); +-#undef DEBSTATUS +- +-#define deb_info(args...) dprintk(0x01, args) +-#define deb_ts(args...) dprintk(0x02, args) +-#define deb_ctrl(args...) dprintk(0x04, args) +-#define deb_i2c(args...) dprintk(0x08, args) +-#define deb_v8(args...) dprintk(0x10, args) +- +-/* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits +- * in the IBI address, to make the V8 code simpler. +- * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used) +- * in general: 0000 0HHH 000L LL00 +- * IBI ADDRESS FORMAT: RHHH BLLL +- * +- * where R is the read(1)/write(0) bit, B is the busy bit +- * and HHH and LLL are the two sets of three bits from the PCI address. +- */ +-#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \ +- (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70)) +-#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \ +- (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4)) +- +-/* +- * DKT 020228 +- * - forget about this VENDOR_BUFFER_SIZE, read and write register +- * deal with DWORD or 4 bytes, that should be should from now on +- * - from now on, we don't support anything older than firm 1.00 +- * I eliminated the write register as a 2 trip of writing hi word and lo word +- * and force this to write only 4 bytes at a time. +- * NOTE: this should work with all the firmware from 1.00 and newer +- */ +-static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI, u32 *val, u8 read) +-{ +- struct flexcop_usb *fc_usb = fc->bus_specific; +- u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG; +- u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR; +- u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | +- (read ? 0x80 : 0); +- +- int len = usb_control_msg(fc_usb->udev, +- read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT, +- request, +- request_type, /* 0xc0 read or 0x40 write */ +- wAddress, +- 0, +- val, +- sizeof(u32), +- B2C2_WAIT_FOR_OPERATION_RDW * HZ); +- +- if (len != sizeof(u32)) { +- err("error while %s dword from %d (%d).", read ? "reading" : +- "writing", wAddress, wRegOffsPCI); +- return -EIO; +- } +- return 0; +-} +-/* +- * DKT 010817 - add support for V8 memory read/write and flash update +- */ +-static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb, +- flexcop_usb_request_t req, u8 page, u16 wAddress, +- u8 *pbBuffer, u32 buflen) +-{ +- u8 request_type = USB_TYPE_VENDOR; +- u16 wIndex; +- int nWaitTime, pipe, len; +- wIndex = page << 8; +- +- switch (req) { +- case B2C2_USB_READ_V8_MEM: +- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ; +- request_type |= USB_DIR_IN; +- pipe = B2C2_USB_CTRL_PIPE_IN; +- break; +- case B2C2_USB_WRITE_V8_MEM: +- wIndex |= pbBuffer[0]; +- request_type |= USB_DIR_OUT; +- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE; +- pipe = B2C2_USB_CTRL_PIPE_OUT; +- break; +- case B2C2_USB_FLASH_BLOCK: +- request_type |= USB_DIR_OUT; +- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH; +- pipe = B2C2_USB_CTRL_PIPE_OUT; +- break; +- default: +- deb_info("unsupported request for v8_mem_req %x.\n", req); +- return -EINVAL; +- } +- deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req, +- wAddress, wIndex, buflen); +- +- len = usb_control_msg(fc_usb->udev, pipe, +- req, +- request_type, +- wAddress, +- wIndex, +- pbBuffer, +- buflen, +- nWaitTime * HZ); +- +- debug_dump(pbBuffer, len, deb_v8); +- return len == buflen ? 0 : -EIO; +-} +- +-#define bytes_left_to_read_on_page(paddr,buflen) \ +- ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \ +- ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK))) +- +-static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb, +- flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start, +- u32 addr, int extended, u8 *buf, u32 len) +-{ +- int i,ret = 0; +- u16 wMax; +- u32 pagechunk = 0; +- +- switch(req) { +- case B2C2_USB_READ_V8_MEM: +- wMax = USB_MEM_READ_MAX; +- break; +- case B2C2_USB_WRITE_V8_MEM: +- wMax = USB_MEM_WRITE_MAX; +- break; +- case B2C2_USB_FLASH_BLOCK: +- wMax = USB_FLASH_MAX; +- break; +- default: +- return -EINVAL; +- break; +- } +- for (i = 0; i < len;) { +- pagechunk = +- wMax < bytes_left_to_read_on_page(addr, len) ? +- wMax : +- bytes_left_to_read_on_page(addr, len); +- deb_info("%x\n", +- (addr & V8_MEMORY_PAGE_MASK) | +- (V8_MEMORY_EXTENDED*extended)); +- +- ret = flexcop_usb_v8_memory_req(fc_usb, req, +- page_start + (addr / V8_MEMORY_PAGE_SIZE), +- (addr & V8_MEMORY_PAGE_MASK) | +- (V8_MEMORY_EXTENDED*extended), +- &buf[i], pagechunk); +- +- if (ret < 0) +- return ret; +- addr += pagechunk; +- len -= pagechunk; +- } +- return 0; +-} +- +-static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended) +-{ +- return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM, +- V8_MEMORY_PAGE_FLASH, 0x1f010, 1, +- fc->dvb_adapter.proposed_mac, 6); +-} +- +-#if 0 +-static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set, +- flexcop_usb_utility_function_t func, u8 extra, u16 wIndex, +- u16 buflen, u8 *pvBuffer) +-{ +- u16 wValue; +- u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR; +- int nWaitTime = 2, +- pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len; +- wValue = (func << 8) | extra; +- +- len = usb_control_msg(fc_usb->udev,pipe, +- B2C2_USB_UTILITY, +- request_type, +- wValue, +- wIndex, +- pvBuffer, +- buflen, +- nWaitTime * HZ); +- return len == buflen ? 0 : -EIO; +-} +-#endif +- +-/* usb i2c stuff */ +-static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c, +- flexcop_usb_request_t req, flexcop_usb_i2c_function_t func, +- u8 chipaddr, u8 addr, u8 *buf, u8 buflen) +-{ +- struct flexcop_usb *fc_usb = i2c->fc->bus_specific; +- u16 wValue, wIndex; +- int nWaitTime,pipe,len; +- u8 request_type = USB_TYPE_VENDOR; +- +- switch (func) { +- case USB_FUNC_I2C_WRITE: +- case USB_FUNC_I2C_MULTIWRITE: +- case USB_FUNC_I2C_REPEATWRITE: +- /* DKT 020208 - add this to support special case of DiSEqC */ +- case USB_FUNC_I2C_CHECKWRITE: +- pipe = B2C2_USB_CTRL_PIPE_OUT; +- nWaitTime = 2; +- request_type |= USB_DIR_OUT; +- break; +- case USB_FUNC_I2C_READ: +- case USB_FUNC_I2C_REPEATREAD: +- pipe = B2C2_USB_CTRL_PIPE_IN; +- nWaitTime = 2; +- request_type |= USB_DIR_IN; +- break; +- default: +- deb_info("unsupported function for i2c_req %x\n", func); +- return -EINVAL; +- } +- wValue = (func << 8) | (i2c->port << 4); +- wIndex = (chipaddr << 8 ) | addr; +- +- deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n", +- func, request_type, req, +- wValue & 0xff, wValue >> 8, +- wIndex & 0xff, wIndex >> 8); +- +- len = usb_control_msg(fc_usb->udev,pipe, +- req, +- request_type, +- wValue, +- wIndex, +- buf, +- buflen, +- nWaitTime * HZ); +- return len == buflen ? 0 : -EREMOTEIO; +-} +- +-/* actual bus specific access functions, +- make sure prototype are/will be equal to pci */ +-static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, +- flexcop_ibi_register reg) +-{ +- flexcop_ibi_value val; +- val.raw = 0; +- flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1); +- return val; +-} +- +-static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, +- flexcop_ibi_register reg, flexcop_ibi_value val) +-{ +- return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0); +-} +- +-static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c, +- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len) +-{ +- if (op == FC_READ) +- return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, +- USB_FUNC_I2C_READ, chipaddr, addr, buf, len); +- else +- return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST, +- USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len); +-} +- +-static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, +- u8 *buffer, int buffer_length) +-{ +- u8 *b; +- int l; +- +- deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", +- fc_usb->tmp_buffer_length, buffer_length); +- +- if (fc_usb->tmp_buffer_length > 0) { +- memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, +- buffer_length); +- fc_usb->tmp_buffer_length += buffer_length; +- b = fc_usb->tmp_buffer; +- l = fc_usb->tmp_buffer_length; +- } else { +- b=buffer; +- l=buffer_length; +- } +- +- while (l >= 190) { +- if (*b == 0xff) { +- switch (*(b+1) & 0x03) { +- case 0x01: /* media packet */ +- if (*(b+2) == 0x47) +- flexcop_pass_dmx_packets( +- fc_usb->fc_dev, b+2, 1); +- else +- deb_ts( +- "not ts packet %02x %02x %02x %02x \n", +- *(b+2), *(b+3), +- *(b+4), *(b+5)); +- b += 190; +- l -= 190; +- break; +- default: +- deb_ts("wrong packet type\n"); +- l = 0; +- break; +- } +- } else { +- deb_ts("wrong header\n"); +- l = 0; +- } +- } +- +- if (l>0) +- memcpy(fc_usb->tmp_buffer, b, l); +- fc_usb->tmp_buffer_length = l; +-} +- +-static void flexcop_usb_urb_complete(struct urb *urb) +-{ +- struct flexcop_usb *fc_usb = urb->context; +- int i; +- +- if (urb->actual_length > 0) +- deb_ts("urb completed, bufsize: %d actlen; %d\n", +- urb->transfer_buffer_length, urb->actual_length); +- +- for (i = 0; i < urb->number_of_packets; i++) { +- if (urb->iso_frame_desc[i].status < 0) { +- err("iso frame descriptor %d has an error: %d\n", i, +- urb->iso_frame_desc[i].status); +- } else +- if (urb->iso_frame_desc[i].actual_length > 0) { +- deb_ts("passed %d bytes to the demux\n", +- urb->iso_frame_desc[i].actual_length); +- +- flexcop_usb_process_frame(fc_usb, +- urb->transfer_buffer + +- urb->iso_frame_desc[i].offset, +- urb->iso_frame_desc[i].actual_length); +- } +- urb->iso_frame_desc[i].status = 0; +- urb->iso_frame_desc[i].actual_length = 0; +- } +- usb_submit_urb(urb,GFP_ATOMIC); +-} +- +-static int flexcop_usb_stream_control(struct flexcop_device *fc, int onoff) +-{ +- /* submit/kill iso packets */ +- return 0; +-} +- +-static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb) +-{ +- int i; +- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) +- if (fc_usb->iso_urb[i] != NULL) { +- deb_ts("unlinking/killing urb no. %d\n",i); +- usb_kill_urb(fc_usb->iso_urb[i]); +- usb_free_urb(fc_usb->iso_urb[i]); +- } +- +- if (fc_usb->iso_buffer != NULL) +- pci_free_consistent(NULL, +- fc_usb->buffer_size, fc_usb->iso_buffer, +- fc_usb->dma_addr); +-} +- +-static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb) +-{ +- u16 frame_size = le16_to_cpu( +- fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize); +- int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * +- frame_size, i, j, ret; +- int buffer_offset = 0; +- +- deb_ts("creating %d iso-urbs with %d frames " +- "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB, +- B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize); +- +- fc_usb->iso_buffer = pci_alloc_consistent(NULL, +- bufsize, &fc_usb->dma_addr); +- if (fc_usb->iso_buffer == NULL) +- return -ENOMEM; +- +- memset(fc_usb->iso_buffer, 0, bufsize); +- fc_usb->buffer_size = bufsize; +- +- /* creating iso urbs */ +- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { +- fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO, +- GFP_ATOMIC); +- if (fc_usb->iso_urb[i] == NULL) { +- ret = -ENOMEM; +- goto urb_error; +- } +- } +- +- /* initialising and submitting iso urbs */ +- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) { +- int frame_offset = 0; +- struct urb *urb = fc_usb->iso_urb[i]; +- deb_ts("initializing and submitting urb no. %d " +- "(buf_offset: %d).\n", i, buffer_offset); +- +- urb->dev = fc_usb->udev; +- urb->context = fc_usb; +- urb->complete = flexcop_usb_urb_complete; +- urb->pipe = B2C2_USB_DATA_PIPE; +- urb->transfer_flags = URB_ISO_ASAP; +- urb->interval = 1; +- urb->number_of_packets = B2C2_USB_FRAMES_PER_ISO; +- urb->transfer_buffer_length = frame_size * B2C2_USB_FRAMES_PER_ISO; +- urb->transfer_buffer = fc_usb->iso_buffer + buffer_offset; +- +- buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO; +- for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) { +- deb_ts("urb no: %d, frame: %d, frame_offset: %d\n", +- i, j, frame_offset); +- urb->iso_frame_desc[j].offset = frame_offset; +- urb->iso_frame_desc[j].length = frame_size; +- frame_offset += frame_size; +- } +- +- if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) { +- err("submitting urb %d failed with %d.", i, ret); +- goto urb_error; +- } +- deb_ts("submitted urb no. %d.\n",i); +- } +- +- /* SRAM */ +- flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA | +- FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, +- FC_SRAM_DEST_TARGET_WAN_USB); +- flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS); +- flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1); +- return 0; +- +-urb_error: +- flexcop_usb_transfer_exit(fc_usb); +- return ret; +-} +- +-static int flexcop_usb_init(struct flexcop_usb *fc_usb) +-{ +- /* use the alternate setting with the larges buffer */ +- usb_set_interface(fc_usb->udev,0,1); +- switch (fc_usb->udev->speed) { +- case USB_SPEED_LOW: +- err("cannot handle USB speed because it is too slow."); +- return -ENODEV; +- break; +- case USB_SPEED_FULL: +- info("running at FULL speed."); +- break; +- case USB_SPEED_HIGH: +- info("running at HIGH speed."); +- break; +- case USB_SPEED_UNKNOWN: /* fall through */ +- default: +- err("cannot handle USB speed because it is unknown."); +- return -ENODEV; +- } +- usb_set_intfdata(fc_usb->uintf, fc_usb); +- return 0; +-} +- +-static void flexcop_usb_exit(struct flexcop_usb *fc_usb) +-{ +- usb_set_intfdata(fc_usb->uintf, NULL); +-} +- +-static int flexcop_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- struct flexcop_usb *fc_usb = NULL; +- struct flexcop_device *fc = NULL; +- int ret; +- +- if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_usb))) == NULL) { +- err("out of memory\n"); +- return -ENOMEM; +- } +- +- /* general flexcop init */ +- fc_usb = fc->bus_specific; +- fc_usb->fc_dev = fc; +- +- fc->read_ibi_reg = flexcop_usb_read_ibi_reg; +- fc->write_ibi_reg = flexcop_usb_write_ibi_reg; +- fc->i2c_request = flexcop_usb_i2c_request; +- fc->get_mac_addr = flexcop_usb_get_mac_addr; +- +- fc->stream_control = flexcop_usb_stream_control; +- +- fc->pid_filtering = 1; +- fc->bus_type = FC_USB; +- +- fc->dev = &udev->dev; +- fc->owner = THIS_MODULE; +- +- /* bus specific part */ +- fc_usb->udev = udev; +- fc_usb->uintf = intf; +- if ((ret = flexcop_usb_init(fc_usb)) != 0) +- goto err_kfree; +- +- /* init flexcop */ +- if ((ret = flexcop_device_initialize(fc)) != 0) +- goto err_usb_exit; +- +- /* xfer init */ +- if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0) +- goto err_fc_exit; +- +- info("%s successfully initialized and connected.", DRIVER_NAME); +- return 0; +- +-err_fc_exit: +- flexcop_device_exit(fc); +-err_usb_exit: +- flexcop_usb_exit(fc_usb); +-err_kfree: +- flexcop_device_kfree(fc); +- return ret; +-} +- +-static void flexcop_usb_disconnect(struct usb_interface *intf) +-{ +- struct flexcop_usb *fc_usb = usb_get_intfdata(intf); +- flexcop_usb_transfer_exit(fc_usb); +- flexcop_device_exit(fc_usb->fc_dev); +- flexcop_usb_exit(fc_usb); +- flexcop_device_kfree(fc_usb->fc_dev); +- info("%s successfully deinitialized and disconnected.", DRIVER_NAME); +-} +- +-static struct usb_device_id flexcop_usb_table [] = { +- { USB_DEVICE(0x0af7, 0x0101) }, +- { } +-}; +-MODULE_DEVICE_TABLE (usb, flexcop_usb_table); +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver flexcop_usb_driver = { +- .name = "b2c2_flexcop_usb", +- .probe = flexcop_usb_probe, +- .disconnect = flexcop_usb_disconnect, +- .id_table = flexcop_usb_table, +-}; +- +-module_usb_driver(flexcop_usb_driver); +- +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_DESCRIPTION(DRIVER_NAME); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/b2c2/flexcop-usb.h b/drivers/media/dvb/b2c2/flexcop-usb.h +deleted file mode 100644 +index 92529a9..0000000 +--- a/drivers/media/dvb/b2c2/flexcop-usb.h ++++ /dev/null +@@ -1,111 +0,0 @@ +-/* +- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III +- * flexcop-usb.h - header file for the USB part +- * see flexcop.c for copyright information +- */ +-#ifndef __FLEXCOP_USB_H_INCLUDED__ +-#define __FLEXCOP_USB_H_INCLUDED__ +- +-#include +- +-/* transfer parameters */ +-#define B2C2_USB_FRAMES_PER_ISO 4 +-#define B2C2_USB_NUM_ISO_URB 4 +- +-#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0) +-#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0) +-#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81) +- +-struct flexcop_usb { +- struct usb_device *udev; +- struct usb_interface *uintf; +- +- u8 *iso_buffer; +- int buffer_size; +- dma_addr_t dma_addr; +- +- struct urb *iso_urb[B2C2_USB_NUM_ISO_URB]; +- struct flexcop_device *fc_dev; +- +- u8 tmp_buffer[1023+190]; +- int tmp_buffer_length; +-}; +- +-#if 0 +-/* request types TODO What is its use?*/ +-typedef enum { +- +-} flexcop_usb_request_type_t; +-#endif +- +-/* request */ +-typedef enum { +- B2C2_USB_WRITE_V8_MEM = 0x04, +- B2C2_USB_READ_V8_MEM = 0x05, +- B2C2_USB_READ_REG = 0x08, +- B2C2_USB_WRITE_REG = 0x0A, +- B2C2_USB_WRITEREGHI = 0x0B, +- B2C2_USB_FLASH_BLOCK = 0x10, +- B2C2_USB_I2C_REQUEST = 0x11, +- B2C2_USB_UTILITY = 0x12, +-} flexcop_usb_request_t; +- +-/* function definition for I2C_REQUEST */ +-typedef enum { +- USB_FUNC_I2C_WRITE = 0x01, +- USB_FUNC_I2C_MULTIWRITE = 0x02, +- USB_FUNC_I2C_READ = 0x03, +- USB_FUNC_I2C_REPEATWRITE = 0x04, +- USB_FUNC_GET_DESCRIPTOR = 0x05, +- USB_FUNC_I2C_REPEATREAD = 0x06, +- /* DKT 020208 - add this to support special case of DiSEqC */ +- USB_FUNC_I2C_CHECKWRITE = 0x07, +- USB_FUNC_I2C_CHECKRESULT = 0x08, +-} flexcop_usb_i2c_function_t; +- +-/* function definition for UTILITY request 0x12 +- * DKT 020304 - new utility function */ +-typedef enum { +- UTILITY_SET_FILTER = 0x01, +- UTILITY_DATA_ENABLE = 0x02, +- UTILITY_FLEX_MULTIWRITE = 0x03, +- UTILITY_SET_BUFFER_SIZE = 0x04, +- UTILITY_FLEX_OPERATOR = 0x05, +- UTILITY_FLEX_RESET300_START = 0x06, +- UTILITY_FLEX_RESET300_STOP = 0x07, +- UTILITY_FLEX_RESET300 = 0x08, +- UTILITY_SET_ISO_SIZE = 0x09, +- UTILITY_DATA_RESET = 0x0A, +- UTILITY_GET_DATA_STATUS = 0x10, +- UTILITY_GET_V8_REG = 0x11, +- /* DKT 020326 - add function for v1.14 */ +- UTILITY_SRAM_WRITE = 0x12, +- UTILITY_SRAM_READ = 0x13, +- UTILITY_SRAM_TESTFILL = 0x14, +- UTILITY_SRAM_TESTSET = 0x15, +- UTILITY_SRAM_TESTVERIFY = 0x16, +-} flexcop_usb_utility_function_t; +- +-#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ) +-#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ) +-#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ) +- +-#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ) +-#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ) +-#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ) +- +-typedef enum { +- V8_MEMORY_PAGE_DVB_CI = 0x20, +- V8_MEMORY_PAGE_DVB_DS = 0x40, +- V8_MEMORY_PAGE_MULTI2 = 0x60, +- V8_MEMORY_PAGE_FLASH = 0x80 +-} flexcop_usb_mem_page_t; +- +-#define V8_MEMORY_EXTENDED (1 << 15) +-#define USB_MEM_READ_MAX 32 +-#define USB_MEM_WRITE_MAX 1 +-#define USB_FLASH_MAX 8 +-#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */ +-#define V8_MEMORY_PAGE_MASK 0x7FFF +- +-#endif +diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c +deleted file mode 100644 +index b1e8c99..0000000 +--- a/drivers/media/dvb/b2c2/flexcop.c ++++ /dev/null +@@ -1,324 +0,0 @@ +-/* +- * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III +- * flexcop.c - main module part +- * Copyright (C) 2004-9 Patrick Boettcher +- * 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 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/dvb/b2c2/flexcop.h b/drivers/media/dvb/b2c2/flexcop.h +deleted file mode 100644 +index 897b10c..0000000 +--- a/drivers/media/dvb/b2c2/flexcop.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-/* +- * 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/dvb/b2c2/flexcop_ibi_value_be.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h +deleted file mode 100644 +index 8f64bdb..0000000 +--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h ++++ /dev/null +@@ -1,455 +0,0 @@ +-/* 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/dvb/b2c2/flexcop_ibi_value_le.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h +deleted file mode 100644 +index c75830d..0000000 +--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h ++++ /dev/null +@@ -1,455 +0,0 @@ +-/* 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/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig +deleted file mode 100644 +index 8668e63..0000000 +--- a/drivers/media/dvb/bt8xx/Kconfig ++++ /dev/null +@@ -1,22 +0,0 @@ +-config DVB_BT8XX +- tristate "BT8xx based PCI cards" +- depends on DVB_CORE && PCI && I2C && VIDEO_BT848 +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- select DVB_SP887X if !DVB_FE_CUSTOMISE +- select DVB_NXT6000 if !DVB_FE_CUSTOMISE +- select DVB_CX24110 if !DVB_FE_CUSTOMISE +- select DVB_OR51211 if !DVB_FE_CUSTOMISE +- select DVB_LGDT330X if !DVB_FE_CUSTOMISE +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE +- help +- Support for PCI cards based on the Bt8xx PCI bridge. Examples are +- the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, +- the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and +- some AVerMedia cards. +- +- Since these cards have no MPEG decoder onboard, they transmit +- only compressed MPEG data over the PCI bus, so you need +- an external software decoder to watch TV on your computer. +- +- Say Y if you own such a device and want to use it. +diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile +deleted file mode 100644 +index 0713b3a..0000000 +--- a/drivers/media/dvb/bt8xx/Makefile ++++ /dev/null +@@ -1,6 +0,0 @@ +-obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core +-ccflags-y += -Idrivers/media/dvb/frontends +-ccflags-y += -Idrivers/media/video/bt8xx +-ccflags-y += -Idrivers/media/common/tuners +diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c +deleted file mode 100644 +index b34fa95..0000000 +--- a/drivers/media/dvb/bt8xx/bt878.c ++++ /dev/null +@@ -1,609 +0,0 @@ +-/* +- * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card +- * +- * Copyright (C) 2002 Peter Hettkamp +- * +- * large parts based on the bttv driver +- * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de) +- * & Marcus Metzler (mocm@metzlerbros.de) +- * (c) 1999,2000 Gerd Knorr +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "bt878.h" +-#include "dst_priv.h" +- +- +-/**************************************/ +-/* Miscellaneous utility definitions */ +-/**************************************/ +- +-static unsigned int bt878_verbose = 1; +-static unsigned int bt878_debug; +- +-module_param_named(verbose, bt878_verbose, int, 0444); +-MODULE_PARM_DESC(verbose, +- "verbose startup messages, default is 1 (yes)"); +-module_param_named(debug, bt878_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off)."); +- +-int bt878_num; +-struct bt878 bt878[BT878_MAX]; +- +-EXPORT_SYMBOL(bt878_num); +-EXPORT_SYMBOL(bt878); +- +-#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr))) +-#define btread(adr) bmtread(bt->bt878_mem+(adr)) +- +-#define btand(dat,adr) btwrite((dat) & btread(adr), adr) +-#define btor(dat,adr) btwrite((dat) | btread(adr), adr) +-#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) +- +-#if defined(dprintk) +-#undef dprintk +-#endif +-#define dprintk(fmt, arg...) \ +- do { \ +- if (bt878_debug) \ +- printk(KERN_DEBUG fmt, ##arg); \ +- } while (0) +- +-static void bt878_mem_free(struct bt878 *bt) +-{ +- if (bt->buf_cpu) { +- pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu, +- bt->buf_dma); +- bt->buf_cpu = NULL; +- } +- +- if (bt->risc_cpu) { +- pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu, +- bt->risc_dma); +- bt->risc_cpu = NULL; +- } +-} +- +-static int bt878_mem_alloc(struct bt878 *bt) +-{ +- if (!bt->buf_cpu) { +- bt->buf_size = 128 * 1024; +- +- bt->buf_cpu = +- pci_alloc_consistent(bt->dev, bt->buf_size, +- &bt->buf_dma); +- +- if (!bt->buf_cpu) +- return -ENOMEM; +- +- memset(bt->buf_cpu, 0, bt->buf_size); +- } +- +- if (!bt->risc_cpu) { +- bt->risc_size = PAGE_SIZE; +- bt->risc_cpu = +- pci_alloc_consistent(bt->dev, bt->risc_size, +- &bt->risc_dma); +- +- if (!bt->risc_cpu) { +- bt878_mem_free(bt); +- return -ENOMEM; +- } +- +- memset(bt->risc_cpu, 0, bt->risc_size); +- } +- +- return 0; +-} +- +-/* RISC instructions */ +-#define RISC_WRITE (0x01 << 28) +-#define RISC_JUMP (0x07 << 28) +-#define RISC_SYNC (0x08 << 28) +- +-/* RISC bits */ +-#define RISC_WR_SOL (1 << 27) +-#define RISC_WR_EOL (1 << 26) +-#define RISC_IRQ (1 << 24) +-#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16)) +-#define RISC_SYNC_RESYNC (1 << 15) +-#define RISC_SYNC_FM1 0x06 +-#define RISC_SYNC_VRO 0x0C +- +-#define RISC_FLUSH() bt->risc_pos = 0 +-#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr) +- +-static int bt878_make_risc(struct bt878 *bt) +-{ +- bt->block_bytes = bt->buf_size >> 4; +- bt->block_count = 1 << 4; +- bt->line_bytes = bt->block_bytes; +- bt->line_count = bt->block_count; +- +- while (bt->line_bytes > 4095) { +- bt->line_bytes >>= 1; +- bt->line_count <<= 1; +- } +- +- if (bt->line_count > 255) { +- printk(KERN_ERR "bt878: buffer size error!\n"); +- return -EINVAL; +- } +- return 0; +-} +- +- +-static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin) +-{ +- u32 buf_pos = 0; +- u32 line; +- +- RISC_FLUSH(); +- RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin); +- RISC_INSTR(0); +- +- dprintk("bt878: risc len lines %u, bytes per line %u\n", +- bt->line_count, bt->line_bytes); +- for (line = 0; line < bt->line_count; line++) { +- // At the beginning of every block we issue an IRQ with previous (finished) block number set +- if (!(buf_pos % bt->block_bytes)) +- RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | +- RISC_IRQ | +- RISC_STATUS(((buf_pos / +- bt->block_bytes) + +- (bt->block_count - +- 1)) % +- bt->block_count) | bt-> +- line_bytes); +- else +- RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | +- bt->line_bytes); +- RISC_INSTR(bt->buf_dma + buf_pos); +- buf_pos += bt->line_bytes; +- } +- +- RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO); +- RISC_INSTR(0); +- +- RISC_INSTR(RISC_JUMP); +- RISC_INSTR(bt->risc_dma); +- +- btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN); +-} +- +-/*****************************/ +-/* Start/Stop grabbing funcs */ +-/*****************************/ +- +-void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, +- u32 irq_err_ignore) +-{ +- u32 int_mask; +- +- dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg); +- /* complete the writing of the risc dma program now we have +- * the card specifics +- */ +- bt878_risc_program(bt, op_sync_orin); +- controlreg &= ~0x1f; +- controlreg |= 0x1b; +- +- btwrite(bt->risc_dma, BT878_ARISC_START); +- +- /* original int mask had : +- * 6 2 8 4 0 +- * 1111 1111 1000 0000 0000 +- * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI +- * Hacked for DST to: +- * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI +- */ +- int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT | +- BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT | +- BT878_AFBUS | BT878_ARISCI; +- +- +- /* ignore pesky bits */ +- int_mask &= ~irq_err_ignore; +- +- btwrite(int_mask, BT878_AINT_MASK); +- btwrite(controlreg, BT878_AGPIO_DMA_CTL); +-} +- +-void bt878_stop(struct bt878 *bt) +-{ +- u32 stat; +- int i = 0; +- +- dprintk("bt878 debug: bt878_stop\n"); +- +- btwrite(0, BT878_AINT_MASK); +- btand(~0x13, BT878_AGPIO_DMA_CTL); +- +- do { +- stat = btread(BT878_AINT_STAT); +- if (!(stat & BT878_ARISC_EN)) +- break; +- i++; +- } while (i < 500); +- +- dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n", +- bt->nr, i, stat); +-} +- +-EXPORT_SYMBOL(bt878_start); +-EXPORT_SYMBOL(bt878_stop); +- +-/*****************************/ +-/* Interrupt service routine */ +-/*****************************/ +- +-static irqreturn_t bt878_irq(int irq, void *dev_id) +-{ +- u32 stat, astat, mask; +- int count; +- struct bt878 *bt; +- +- bt = (struct bt878 *) dev_id; +- +- count = 0; +- while (1) { +- stat = btread(BT878_AINT_STAT); +- mask = btread(BT878_AINT_MASK); +- if (!(astat = (stat & mask))) +- return IRQ_NONE; /* this interrupt is not for me */ +-/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */ +- btwrite(astat, BT878_AINT_STAT); /* try to clear interrupt condition */ +- +- +- if (astat & (BT878_ASCERR | BT878_AOCERR)) { +- if (bt878_verbose) { +- printk(KERN_INFO +- "bt878(%d): irq%s%s risc_pc=%08x\n", +- bt->nr, +- (astat & BT878_ASCERR) ? " SCERR" : +- "", +- (astat & BT878_AOCERR) ? " OCERR" : +- "", btread(BT878_ARISC_PC)); +- } +- } +- if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) { +- if (bt878_verbose) { +- printk(KERN_INFO +- "bt878(%d): irq%s%s%s risc_pc=%08x\n", +- bt->nr, +- (astat & BT878_APABORT) ? " PABORT" : +- "", +- (astat & BT878_ARIPERR) ? " RIPERR" : +- "", +- (astat & BT878_APPERR) ? " PPERR" : +- "", btread(BT878_ARISC_PC)); +- } +- } +- if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) { +- if (bt878_verbose) { +- printk(KERN_INFO +- "bt878(%d): irq%s%s%s risc_pc=%08x\n", +- bt->nr, +- (astat & BT878_AFDSR) ? " FDSR" : "", +- (astat & BT878_AFTRGT) ? " FTRGT" : +- "", +- (astat & BT878_AFBUS) ? " FBUS" : "", +- btread(BT878_ARISC_PC)); +- } +- } +- if (astat & BT878_ARISCI) { +- bt->finished_block = (stat & BT878_ARISCS) >> 28; +- tasklet_schedule(&bt->tasklet); +- break; +- } +- count++; +- if (count > 20) { +- btwrite(0, BT878_AINT_MASK); +- printk(KERN_ERR +- "bt878(%d): IRQ lockup, cleared int mask\n", +- bt->nr); +- break; +- } +- } +- return IRQ_HANDLED; +-} +- +-int +-bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp) +-{ +- int retval; +- +- retval = 0; +- if (mutex_lock_interruptible(&bt->gpio_lock)) +- return -ERESTARTSYS; +- /* special gpio signal */ +- switch (cmd) { +- case DST_IG_ENABLE: +- // dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable); +- retval = bttv_gpio_enable(bt->bttv_nr, +- mp->enb.mask, +- mp->enb.enable); +- break; +- case DST_IG_WRITE: +- // dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals); +- retval = bttv_write_gpio(bt->bttv_nr, +- mp->outp.mask, +- mp->outp.highvals); +- +- break; +- case DST_IG_READ: +- /* read */ +- retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value); +- // dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value); +- break; +- case DST_IG_TS: +- /* Set packet size */ +- bt->TS_Size = mp->psize; +- break; +- +- default: +- retval = -EINVAL; +- break; +- } +- mutex_unlock(&bt->gpio_lock); +- return retval; +-} +- +-EXPORT_SYMBOL(bt878_device_control); +- +-#define BROOKTREE_878_DEVICE(vend, dev, name) \ +- { \ +- .vendor = PCI_VENDOR_ID_BROOKTREE, \ +- .device = PCI_DEVICE_ID_BROOKTREE_878, \ +- .subvendor = (vend), .subdevice = (dev), \ +- .driver_data = (unsigned long) name \ +- } +- +-static struct pci_device_id bt878_pci_tbl[] __devinitdata = { +- BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"), +- BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"), +- BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"), +- BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"), +- BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"), +- BROOKTREE_878_DEVICE(0x270f, 0xfc00, +- "ChainTech digitop DST-1000 DVB-S"), +- BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"), +- BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"), +- BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"), +- BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"), +- BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"), +- BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"), +- { } +-}; +- +-MODULE_DEVICE_TABLE(pci, bt878_pci_tbl); +- +-static const char * __devinit card_name(const struct pci_device_id *id) +-{ +- return id->driver_data ? (const char *)id->driver_data : "Unknown"; +-} +- +-/***********************/ +-/* PCI device handling */ +-/***********************/ +- +-static int __devinit bt878_probe(struct pci_dev *dev, +- const struct pci_device_id *pci_id) +-{ +- int result = 0; +- unsigned char lat; +- struct bt878 *bt; +-#if defined(__powerpc__) +- unsigned int cmd; +-#endif +- unsigned int cardid; +- +- printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", +- bt878_num); +- if (bt878_num >= BT878_MAX) { +- printk(KERN_ERR "bt878: Too many devices inserted\n"); +- result = -ENOMEM; +- goto fail0; +- } +- if (pci_enable_device(dev)) +- return -EIO; +- +- cardid = dev->subsystem_device << 16; +- cardid |= dev->subsystem_vendor; +- +- printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n", +- __func__, cardid, card_name(pci_id)); +- +- bt = &bt878[bt878_num]; +- bt->dev = dev; +- bt->nr = bt878_num; +- bt->shutdown = 0; +- +- bt->id = dev->device; +- bt->irq = dev->irq; +- bt->bt878_adr = pci_resource_start(dev, 0); +- if (!request_mem_region(pci_resource_start(dev, 0), +- pci_resource_len(dev, 0), "bt878")) { +- result = -EBUSY; +- goto fail0; +- } +- +- bt->revision = dev->revision; +- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); +- +- +- printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ", +- bt878_num, bt->id, bt->revision, dev->bus->number, +- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); +- printk("irq: %d, latency: %d, memory: 0x%lx\n", +- bt->irq, lat, bt->bt878_adr); +- +- +-#if defined(__powerpc__) +- /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ +- /* response on cards with no firmware is not enabled by OF */ +- pci_read_config_dword(dev, PCI_COMMAND, &cmd); +- cmd = (cmd | PCI_COMMAND_MEMORY); +- pci_write_config_dword(dev, PCI_COMMAND, cmd); +-#endif +- +-#ifdef __sparc__ +- bt->bt878_mem = (unsigned char *) bt->bt878_adr; +-#else +- bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000); +-#endif +- +- /* clear interrupt mask */ +- btwrite(0, BT848_INT_MASK); +- +- result = request_irq(bt->irq, bt878_irq, +- IRQF_SHARED | IRQF_DISABLED, "bt878", +- (void *) bt); +- if (result == -EINVAL) { +- printk(KERN_ERR "bt878(%d): Bad irq number or handler\n", +- bt878_num); +- goto fail1; +- } +- if (result == -EBUSY) { +- printk(KERN_ERR +- "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n", +- bt878_num, bt->irq); +- goto fail1; +- } +- if (result < 0) +- goto fail1; +- +- pci_set_master(dev); +- pci_set_drvdata(dev, bt); +- +- if ((result = bt878_mem_alloc(bt))) { +- printk(KERN_ERR "bt878: failed to allocate memory!\n"); +- goto fail2; +- } +- +- bt878_make_risc(bt); +- btwrite(0, BT878_AINT_MASK); +- bt878_num++; +- +- return 0; +- +- fail2: +- free_irq(bt->irq, bt); +- fail1: +- release_mem_region(pci_resource_start(bt->dev, 0), +- pci_resource_len(bt->dev, 0)); +- fail0: +- pci_disable_device(dev); +- return result; +-} +- +-static void __devexit bt878_remove(struct pci_dev *pci_dev) +-{ +- u8 command; +- struct bt878 *bt = pci_get_drvdata(pci_dev); +- +- if (bt878_verbose) +- printk(KERN_INFO "bt878(%d): unloading\n", bt->nr); +- +- /* turn off all capturing, DMA and IRQs */ +- btand(~0x13, BT878_AGPIO_DMA_CTL); +- +- /* first disable interrupts before unmapping the memory! */ +- btwrite(0, BT878_AINT_MASK); +- btwrite(~0U, BT878_AINT_STAT); +- +- /* disable PCI bus-mastering */ +- pci_read_config_byte(bt->dev, PCI_COMMAND, &command); +- /* Should this be &=~ ?? */ +- command &= ~PCI_COMMAND_MASTER; +- pci_write_config_byte(bt->dev, PCI_COMMAND, command); +- +- free_irq(bt->irq, bt); +- printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem); +- if (bt->bt878_mem) +- iounmap(bt->bt878_mem); +- +- release_mem_region(pci_resource_start(bt->dev, 0), +- pci_resource_len(bt->dev, 0)); +- /* wake up any waiting processes +- because shutdown flag is set, no new processes (in this queue) +- are expected +- */ +- bt->shutdown = 1; +- bt878_mem_free(bt); +- +- pci_set_drvdata(pci_dev, NULL); +- pci_disable_device(pci_dev); +- return; +-} +- +-static struct pci_driver bt878_pci_driver = { +- .name = "bt878", +- .id_table = bt878_pci_tbl, +- .probe = bt878_probe, +- .remove = __devexit_p(bt878_remove), +-}; +- +-/*******************************/ +-/* Module management functions */ +-/*******************************/ +- +-static int __init bt878_init_module(void) +-{ +- bt878_num = 0; +- +- printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n", +- (BT878_VERSION_CODE >> 16) & 0xff, +- (BT878_VERSION_CODE >> 8) & 0xff, +- BT878_VERSION_CODE & 0xff); +- +- return pci_register_driver(&bt878_pci_driver); +-} +- +-static void __exit bt878_cleanup_module(void) +-{ +- pci_unregister_driver(&bt878_pci_driver); +-} +- +-module_init(bt878_init_module); +-module_exit(bt878_cleanup_module); +- +-MODULE_LICENSE("GPL"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/bt8xx/bt878.h b/drivers/media/dvb/bt8xx/bt878.h +deleted file mode 100644 +index d19b592..0000000 +--- a/drivers/media/dvb/bt8xx/bt878.h ++++ /dev/null +@@ -1,159 +0,0 @@ +-/* +- bt878.h - Bt878 audio module (register offsets) +- +- Copyright (C) 2002 Peter Hettkamp +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef _BT878_H_ +-#define _BT878_H_ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "bt848.h" +-#include "bttv.h" +- +-#define BT878_VERSION_CODE 0x000000 +- +-#define BT878_AINT_STAT 0x100 +-#define BT878_ARISCS (0xf<<28) +-#define BT878_ARISC_EN (1<<27) +-#define BT878_ASCERR (1<<19) +-#define BT878_AOCERR (1<<18) +-#define BT878_APABORT (1<<17) +-#define BT878_ARIPERR (1<<16) +-#define BT878_APPERR (1<<15) +-#define BT878_AFDSR (1<<14) +-#define BT878_AFTRGT (1<<13) +-#define BT878_AFBUS (1<<12) +-#define BT878_ARISCI (1<<11) +-#define BT878_AOFLOW (1<<3) +- +-#define BT878_AINT_MASK 0x104 +- +-#define BT878_AGPIO_DMA_CTL 0x10c +-#define BT878_A_GAIN (0xf<<28) +-#define BT878_A_G2X (1<<27) +-#define BT878_A_PWRDN (1<<26) +-#define BT878_A_SEL (3<<24) +-#define BT878_DA_SCE (1<<23) +-#define BT878_DA_LRI (1<<22) +-#define BT878_DA_MLB (1<<21) +-#define BT878_DA_LRD (0x1f<<16) +-#define BT878_DA_DPM (1<<15) +-#define BT878_DA_SBR (1<<14) +-#define BT878_DA_ES2 (1<<13) +-#define BT878_DA_LMT (1<<12) +-#define BT878_DA_SDR (0xf<<8) +-#define BT878_DA_IOM (3<<6) +-#define BT878_DA_APP (1<<5) +-#define BT878_ACAP_EN (1<<4) +-#define BT878_PKTP (3<<2) +-#define BT878_RISC_EN (1<<1) +-#define BT878_FIFO_EN 1 +- +-#define BT878_APACK_LEN 0x110 +-#define BT878_AFP_LEN (0xff<<16) +-#define BT878_ALP_LEN 0xfff +- +-#define BT878_ARISC_START 0x114 +- +-#define BT878_ARISC_PC 0x120 +- +-/* BT878 FUNCTION 0 REGISTERS */ +-#define BT878_GPIO_DMA_CTL 0x10c +- +-/* Interrupt register */ +-#define BT878_INT_STAT 0x100 +-#define BT878_INT_MASK 0x104 +-#define BT878_I2CRACK (1<<25) +-#define BT878_I2CDONE (1<<8) +- +-#define BT878_MAX 4 +- +-#define BT878_RISC_SYNC_MASK (1 << 15) +- +- +-#define BTTV_BOARD_UNKNOWN 0x00 +-#define BTTV_BOARD_PINNACLESAT 0x5e +-#define BTTV_BOARD_NEBULA_DIGITV 0x68 +-#define BTTV_BOARD_PC_HDTV 0x70 +-#define BTTV_BOARD_TWINHAN_DST 0x71 +-#define BTTV_BOARD_AVDVBT_771 0x7b +-#define BTTV_BOARD_AVDVBT_761 0x7c +-#define BTTV_BOARD_DVICO_DVBT_LITE 0x80 +-#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87 +- +-extern int bt878_num; +- +-struct bt878 { +- struct mutex gpio_lock; +- unsigned int nr; +- unsigned int bttv_nr; +- struct i2c_adapter *adapter; +- struct pci_dev *dev; +- unsigned int id; +- unsigned int TS_Size; +- unsigned char revision; +- unsigned int irq; +- unsigned long bt878_adr; +- volatile void __iomem *bt878_mem; /* function 1 */ +- +- volatile u32 finished_block; +- volatile u32 last_block; +- u32 block_count; +- u32 block_bytes; +- u32 line_bytes; +- u32 line_count; +- +- u32 buf_size; +- u8 *buf_cpu; +- dma_addr_t buf_dma; +- +- u32 risc_size; +- __le32 *risc_cpu; +- dma_addr_t risc_dma; +- u32 risc_pos; +- +- struct tasklet_struct tasklet; +- int shutdown; +-}; +- +-extern struct bt878 bt878[BT878_MAX]; +- +-void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, +- u32 irq_err_ignore); +-void bt878_stop(struct bt878 *bt); +- +-#if defined(__powerpc__) /* big-endian */ +-static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val) +-{ +- st_le32(addr, val); +- eieio(); +-} +- +-#define bmtwrite(dat,adr) io_st_le32((adr),(dat)) +-#define bmtread(adr) ld_le32((adr)) +-#else +-#define bmtwrite(dat,adr) writel((dat), (adr)) +-#define bmtread(adr) readl(adr) +-#endif +- +-#endif +diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c +deleted file mode 100644 +index 430b3eb..0000000 +--- a/drivers/media/dvb/bt8xx/dst.c ++++ /dev/null +@@ -1,1873 +0,0 @@ +-/* +- Frontend/Card driver for TwinHan DST Frontend +- Copyright (C) 2003 Jamie Honan +- Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "dst_priv.h" +-#include "dst_common.h" +- +-static unsigned int verbose = 1; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); +- +-static unsigned int dst_addons; +-module_param(dst_addons, int, 0644); +-MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)"); +- +-static unsigned int dst_algo; +-module_param(dst_algo, int, 0644); +-MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)"); +- +-#define HAS_LOCK 1 +-#define ATTEMPT_TUNE 2 +-#define HAS_POWER 4 +- +-#define DST_ERROR 0 +-#define DST_NOTICE 1 +-#define DST_INFO 2 +-#define DST_DEBUG 3 +- +-#define dprintk(x, y, z, format, arg...) do { \ +- if (z) { \ +- if ((x > DST_ERROR) && (x > y)) \ +- printk(KERN_ERR "dst(%d) %s: " format "\n", \ +- state->bt->nr, __func__ , ##arg); \ +- else if ((x > DST_NOTICE) && (x > y)) \ +- printk(KERN_NOTICE "dst(%d) %s: " format "\n", \ +- state->bt->nr, __func__ , ##arg); \ +- else if ((x > DST_INFO) && (x > y)) \ +- printk(KERN_INFO "dst(%d) %s: " format "\n", \ +- state->bt->nr, __func__ , ##arg); \ +- else if ((x > DST_DEBUG) && (x > y)) \ +- printk(KERN_DEBUG "dst(%d) %s: " format "\n", \ +- state->bt->nr, __func__ , ##arg); \ +- } else { \ +- if (x > y) \ +- printk(format, ##arg); \ +- } \ +-} while(0) +- +-static int dst_command(struct dst_state *state, u8 *data, u8 len); +- +-static void dst_packsize(struct dst_state *state, int psize) +-{ +- union dst_gpio_packet bits; +- +- bits.psize = psize; +- bt878_device_control(state->bt, DST_IG_TS, &bits); +-} +- +-static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, +- u32 outhigh, int delay) +-{ +- union dst_gpio_packet enb; +- union dst_gpio_packet bits; +- int err; +- +- enb.enb.mask = mask; +- enb.enb.enable = enbb; +- +- dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh); +- if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) { +- dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb); +- return -EREMOTEIO; +- } +- udelay(1000); +- /* because complete disabling means no output, no need to do output packet */ +- if (enbb == 0) +- return 0; +- if (delay) +- msleep(10); +- bits.outp.mask = enbb; +- bits.outp.highvals = outhigh; +- if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) { +- dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int dst_gpio_inb(struct dst_state *state, u8 *result) +-{ +- union dst_gpio_packet rd_packet; +- int err; +- +- *result = 0; +- if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err); +- return -EREMOTEIO; +- } +- *result = (u8) rd_packet.rd.value; +- +- return 0; +-} +- +-int rdc_reset_state(struct dst_state *state) +-{ +- dprintk(verbose, DST_INFO, 1, "Resetting state machine"); +- if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- return -1; +- } +- msleep(10); +- if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- msleep(10); +- return -1; +- } +- +- return 0; +-} +-EXPORT_SYMBOL(rdc_reset_state); +- +-static int rdc_8820_reset(struct dst_state *state) +-{ +- dprintk(verbose, DST_DEBUG, 1, "Resetting DST"); +- if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- return -1; +- } +- udelay(1000); +- if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- return -1; +- } +- +- return 0; +-} +- +-static int dst_pio_enable(struct dst_state *state) +-{ +- if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- return -1; +- } +- udelay(1000); +- +- return 0; +-} +- +-int dst_pio_disable(struct dst_state *state) +-{ +- if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); +- return -1; +- } +- if (state->type_flags & DST_TYPE_HAS_FW_1) +- udelay(1000); +- +- return 0; +-} +-EXPORT_SYMBOL(dst_pio_disable); +- +-int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode) +-{ +- u8 reply; +- int i; +- +- for (i = 0; i < 200; i++) { +- if (dst_gpio_inb(state, &reply) < 0) { +- dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !"); +- return -1; +- } +- if ((reply & RDC_8820_PIO_0_ENABLE) == 0) { +- dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i); +- return 1; +- } +- msleep(10); +- } +- dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i); +- +- return 0; +-} +-EXPORT_SYMBOL(dst_wait_dst_ready); +- +-int dst_error_recovery(struct dst_state *state) +-{ +- dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors."); +- dst_pio_disable(state); +- msleep(10); +- dst_pio_enable(state); +- msleep(10); +- +- return 0; +-} +-EXPORT_SYMBOL(dst_error_recovery); +- +-int dst_error_bailout(struct dst_state *state) +-{ +- dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error."); +- rdc_8820_reset(state); +- dst_pio_disable(state); +- msleep(10); +- +- return 0; +-} +-EXPORT_SYMBOL(dst_error_bailout); +- +-int dst_comm_init(struct dst_state *state) +-{ +- dprintk(verbose, DST_INFO, 1, "Initializing DST."); +- if ((dst_pio_enable(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed"); +- return -1; +- } +- if ((rdc_reset_state(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed."); +- return -1; +- } +- if (state->type_flags & DST_TYPE_HAS_FW_1) +- msleep(100); +- else +- msleep(5); +- +- return 0; +-} +-EXPORT_SYMBOL(dst_comm_init); +- +-int write_dst(struct dst_state *state, u8 *data, u8 len) +-{ +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = data, +- .len = len +- }; +- +- int err; +- u8 cnt, i; +- +- dprintk(verbose, DST_NOTICE, 0, "writing [ "); +- for (i = 0; i < len; i++) +- dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]); +- dprintk(verbose, DST_NOTICE, 0, "]\n"); +- +- for (cnt = 0; cnt < 2; cnt++) { +- if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { +- dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]); +- dst_error_recovery(state); +- continue; +- } else +- break; +- } +- if (cnt >= 2) { +- dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); +- dst_error_bailout(state); +- +- return -1; +- } +- +- return 0; +-} +-EXPORT_SYMBOL(write_dst); +- +-int read_dst(struct dst_state *state, u8 *ret, u8 len) +-{ +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = ret, +- .len = len +- }; +- +- int err; +- int cnt; +- +- for (cnt = 0; cnt < 2; cnt++) { +- if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { +- dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]); +- dst_error_recovery(state); +- continue; +- } else +- break; +- } +- if (cnt >= 2) { +- dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); +- dst_error_bailout(state); +- +- return -1; +- } +- dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]); +- for (err = 1; err < len; err++) +- dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]); +- if (err > 1) +- dprintk(verbose, DST_DEBUG, 0, "\n"); +- +- return 0; +-} +-EXPORT_SYMBOL(read_dst); +- +-static int dst_set_polarization(struct dst_state *state) +-{ +- switch (state->voltage) { +- case SEC_VOLTAGE_13: /* Vertical */ +- dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]"); +- state->tx_tuna[8] &= ~0x40; +- break; +- case SEC_VOLTAGE_18: /* Horizontal */ +- dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]"); +- state->tx_tuna[8] |= 0x40; +- break; +- case SEC_VOLTAGE_OFF: +- break; +- } +- +- return 0; +-} +- +-static int dst_set_freq(struct dst_state *state, u32 freq) +-{ +- state->frequency = freq; +- dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq); +- +- if (state->dst_type == DST_TYPE_IS_SAT) { +- freq = freq / 1000; +- if (freq < 950 || freq > 2150) +- return -EINVAL; +- state->tx_tuna[2] = (freq >> 8); +- state->tx_tuna[3] = (u8) freq; +- state->tx_tuna[4] = 0x01; +- state->tx_tuna[8] &= ~0x04; +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { +- if (freq < 1531) +- state->tx_tuna[8] |= 0x04; +- } +- } else if (state->dst_type == DST_TYPE_IS_TERR) { +- freq = freq / 1000; +- if (freq < 137000 || freq > 858000) +- return -EINVAL; +- state->tx_tuna[2] = (freq >> 16) & 0xff; +- state->tx_tuna[3] = (freq >> 8) & 0xff; +- state->tx_tuna[4] = (u8) freq; +- } else if (state->dst_type == DST_TYPE_IS_CABLE) { +- freq = freq / 1000; +- state->tx_tuna[2] = (freq >> 16) & 0xff; +- state->tx_tuna[3] = (freq >> 8) & 0xff; +- state->tx_tuna[4] = (u8) freq; +- } else if (state->dst_type == DST_TYPE_IS_ATSC) { +- freq = freq / 1000; +- if (freq < 51000 || freq > 858000) +- return -EINVAL; +- state->tx_tuna[2] = (freq >> 16) & 0xff; +- state->tx_tuna[3] = (freq >> 8) & 0xff; +- state->tx_tuna[4] = (u8) freq; +- state->tx_tuna[5] = 0x00; /* ATSC */ +- state->tx_tuna[6] = 0x00; +- if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG) +- state->tx_tuna[7] = 0x00; /* Digital */ +- } else +- return -EINVAL; +- +- return 0; +-} +- +-static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth) +-{ +- state->bandwidth = bandwidth; +- +- if (state->dst_type != DST_TYPE_IS_TERR) +- return -EOPNOTSUPP; +- +- switch (bandwidth) { +- case 6000000: +- if (state->dst_hw_cap & DST_TYPE_HAS_CA) +- state->tx_tuna[7] = 0x06; +- else { +- state->tx_tuna[6] = 0x06; +- state->tx_tuna[7] = 0x00; +- } +- break; +- case 7000000: +- if (state->dst_hw_cap & DST_TYPE_HAS_CA) +- state->tx_tuna[7] = 0x07; +- else { +- state->tx_tuna[6] = 0x07; +- state->tx_tuna[7] = 0x00; +- } +- break; +- case 8000000: +- if (state->dst_hw_cap & DST_TYPE_HAS_CA) +- state->tx_tuna[7] = 0x08; +- else { +- state->tx_tuna[6] = 0x08; +- state->tx_tuna[7] = 0x00; +- } +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion) +-{ +- state->inversion = inversion; +- switch (inversion) { +- case INVERSION_OFF: /* Inversion = Normal */ +- state->tx_tuna[8] &= ~0x80; +- break; +- case INVERSION_ON: +- state->tx_tuna[8] |= 0x80; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec) +-{ +- state->fec = fec; +- return 0; +-} +- +-static fe_code_rate_t dst_get_fec(struct dst_state *state) +-{ +- return state->fec; +-} +- +-static int dst_set_symbolrate(struct dst_state *state, u32 srate) +-{ +- u32 symcalc; +- u64 sval; +- +- state->symbol_rate = srate; +- if (state->dst_type == DST_TYPE_IS_TERR) { +- return -EOPNOTSUPP; +- } +- dprintk(verbose, DST_INFO, 1, "set symrate %u", srate); +- srate /= 1000; +- if (state->dst_type == DST_TYPE_IS_SAT) { +- if (state->type_flags & DST_TYPE_HAS_SYMDIV) { +- sval = srate; +- sval <<= 20; +- do_div(sval, 88000); +- symcalc = (u32) sval; +- dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc); +- state->tx_tuna[5] = (u8) (symcalc >> 12); +- state->tx_tuna[6] = (u8) (symcalc >> 4); +- state->tx_tuna[7] = (u8) (symcalc << 4); +- } else { +- state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f; +- state->tx_tuna[6] = (u8) (srate >> 8); +- state->tx_tuna[7] = (u8) srate; +- } +- state->tx_tuna[8] &= ~0x20; +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { +- if (srate > 8000) +- state->tx_tuna[8] |= 0x20; +- } +- } else if (state->dst_type == DST_TYPE_IS_CABLE) { +- dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name); +- if (!strncmp(state->fw_name, "DCTNEW", 6)) { +- state->tx_tuna[5] = (u8) (srate >> 8); +- state->tx_tuna[6] = (u8) srate; +- state->tx_tuna[7] = 0x00; +- } else if (!strncmp(state->fw_name, "DCT-CI", 6)) { +- state->tx_tuna[5] = 0x00; +- state->tx_tuna[6] = (u8) (srate >> 8); +- state->tx_tuna[7] = (u8) srate; +- } +- } +- return 0; +-} +- +-static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation) +-{ +- if (state->dst_type != DST_TYPE_IS_CABLE) +- return -EOPNOTSUPP; +- +- state->modulation = modulation; +- switch (modulation) { +- case QAM_16: +- state->tx_tuna[8] = 0x10; +- break; +- case QAM_32: +- state->tx_tuna[8] = 0x20; +- break; +- case QAM_64: +- state->tx_tuna[8] = 0x40; +- break; +- case QAM_128: +- state->tx_tuna[8] = 0x80; +- break; +- case QAM_256: +- if (!strncmp(state->fw_name, "DCTNEW", 6)) +- state->tx_tuna[8] = 0xff; +- else if (!strncmp(state->fw_name, "DCT-CI", 6)) +- state->tx_tuna[8] = 0x00; +- break; +- case QPSK: +- case QAM_AUTO: +- case VSB_8: +- case VSB_16: +- default: +- return -EINVAL; +- +- } +- +- return 0; +-} +- +-static fe_modulation_t dst_get_modulation(struct dst_state *state) +-{ +- return state->modulation; +-} +- +- +-u8 dst_check_sum(u8 *buf, u32 len) +-{ +- u32 i; +- u8 val = 0; +- if (!len) +- return 0; +- for (i = 0; i < len; i++) { +- val += buf[i]; +- } +- return ((~val) + 1); +-} +-EXPORT_SYMBOL(dst_check_sum); +- +-static void dst_type_flags_print(struct dst_state *state) +-{ +- u32 type_flags = state->type_flags; +- +- dprintk(verbose, DST_ERROR, 0, "DST type flags :"); +- if (type_flags & DST_TYPE_HAS_TS188) +- dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188); +- if (type_flags & DST_TYPE_HAS_NEWTUNE_2) +- dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2); +- if (type_flags & DST_TYPE_HAS_TS204) +- dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204); +- if (type_flags & DST_TYPE_HAS_VLF) +- dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF); +- if (type_flags & DST_TYPE_HAS_SYMDIV) +- dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV); +- if (type_flags & DST_TYPE_HAS_FW_1) +- dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1); +- if (type_flags & DST_TYPE_HAS_FW_2) +- dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2); +- if (type_flags & DST_TYPE_HAS_FW_3) +- dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3); +- dprintk(verbose, DST_ERROR, 0, "\n"); +-} +- +- +-static int dst_type_print(struct dst_state *state, u8 type) +-{ +- char *otype; +- switch (type) { +- case DST_TYPE_IS_SAT: +- otype = "satellite"; +- break; +- +- case DST_TYPE_IS_TERR: +- otype = "terrestrial"; +- break; +- +- case DST_TYPE_IS_CABLE: +- otype = "cable"; +- break; +- +- case DST_TYPE_IS_ATSC: +- otype = "atsc"; +- break; +- +- default: +- dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type); +- return -EINVAL; +- } +- dprintk(verbose, DST_INFO, 1, "DST type: %s", otype); +- +- return 0; +-} +- +-static struct tuner_types tuner_list[] = { +- { +- .tuner_type = TUNER_TYPE_L64724, +- .tuner_name = "L 64724", +- .board_name = "UNKNOWN", +- .fw_name = "UNKNOWN" +- }, +- +- { +- .tuner_type = TUNER_TYPE_STV0299, +- .tuner_name = "STV 0299", +- .board_name = "VP1020", +- .fw_name = "DST-MOT" +- }, +- +- { +- .tuner_type = TUNER_TYPE_STV0299, +- .tuner_name = "STV 0299", +- .board_name = "VP1020", +- .fw_name = "DST-03T" +- }, +- +- { +- .tuner_type = TUNER_TYPE_MB86A15, +- .tuner_name = "MB 86A15", +- .board_name = "VP1022", +- .fw_name = "DST-03T" +- }, +- +- { +- .tuner_type = TUNER_TYPE_MB86A15, +- .tuner_name = "MB 86A15", +- .board_name = "VP1025", +- .fw_name = "DST-03T" +- }, +- +- { +- .tuner_type = TUNER_TYPE_STV0299, +- .tuner_name = "STV 0299", +- .board_name = "VP1030", +- .fw_name = "DST-CI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_STV0299, +- .tuner_name = "STV 0299", +- .board_name = "VP1030", +- .fw_name = "DSTMCI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP2021", +- .fw_name = "DCTNEW" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP2030", +- .fw_name = "DCT-CI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP2031", +- .fw_name = "DCT-CI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP2040", +- .fw_name = "DCT-CI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP3020", +- .fw_name = "DTTFTA" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP3021", +- .fw_name = "DTTFTA" +- }, +- +- { +- .tuner_type = TUNER_TYPE_TDA10046, +- .tuner_name = "TDA10046", +- .board_name = "VP3040", +- .fw_name = "DTT-CI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_UNKNOWN, +- .tuner_name = "UNKNOWN", +- .board_name = "VP3051", +- .fw_name = "DTTNXT" +- }, +- +- { +- .tuner_type = TUNER_TYPE_NXT200x, +- .tuner_name = "NXT200x", +- .board_name = "VP3220", +- .fw_name = "ATSCDI" +- }, +- +- { +- .tuner_type = TUNER_TYPE_NXT200x, +- .tuner_name = "NXT200x", +- .board_name = "VP3250", +- .fw_name = "ATSCAD" +- }, +-}; +- +-/* +- Known cards list +- Satellite +- ------------------- +- 200103A +- VP-1020 DST-MOT LG(old), TS=188 +- +- VP-1020 DST-03T LG(new), TS=204 +- VP-1022 DST-03T LG(new), TS=204 +- VP-1025 DST-03T LG(new), TS=204 +- +- VP-1030 DSTMCI, LG(new), TS=188 +- VP-1032 DSTMCI, LG(new), TS=188 +- +- Cable +- ------------------- +- VP-2030 DCT-CI, Samsung, TS=204 +- VP-2021 DCT-CI, Unknown, TS=204 +- VP-2031 DCT-CI, Philips, TS=188 +- VP-2040 DCT-CI, Philips, TS=188, with CA daughter board +- VP-2040 DCT-CI, Philips, TS=204, without CA daughter board +- +- Terrestrial +- ------------------- +- VP-3050 DTTNXT TS=188 +- VP-3040 DTT-CI, Philips, TS=188 +- VP-3040 DTT-CI, Philips, TS=204 +- +- ATSC +- ------------------- +- VP-3220 ATSCDI, TS=188 +- VP-3250 ATSCAD, TS=188 +- +-*/ +- +-static struct dst_types dst_tlist[] = { +- { +- .device_id = "200103A", +- .offset = 0, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS, +- .dst_feature = 0, +- .tuner_type = 0 +- }, /* obsolete */ +- +- { +- .device_id = "DST-020", +- .offset = 0, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, +- .dst_feature = 0, +- .tuner_type = 0 +- }, /* obsolete */ +- +- { +- .device_id = "DST-030", +- .offset = 0, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, +- .dst_feature = 0, +- .tuner_type = 0 +- }, /* obsolete */ +- +- { +- .device_id = "DST-03T", +- .offset = 0, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2, +- .dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5 +- | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO, +- .tuner_type = TUNER_TYPE_MULTI +- }, +- +- { +- .device_id = "DST-MOT", +- .offset = 0, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, +- .dst_feature = 0, +- .tuner_type = 0 +- }, /* obsolete */ +- +- { +- .device_id = "DST-CI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1, +- .dst_feature = DST_TYPE_HAS_CA, +- .tuner_type = 0 +- }, /* An OEM board */ +- +- { +- .device_id = "DSTMCI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF, +- .dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 +- | DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC, +- .tuner_type = TUNER_TYPE_MULTI +- }, +- +- { +- .device_id = "DSTFCI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_SAT, +- .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, +- .dst_feature = 0, +- .tuner_type = 0 +- }, /* unknown to vendor */ +- +- { +- .device_id = "DCT-CI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_CABLE, +- .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF, +- .dst_feature = DST_TYPE_HAS_CA, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "DCTNEW", +- .offset = 1, +- .dst_type = DST_TYPE_IS_CABLE, +- .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE, +- .dst_feature = 0, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "DTT-CI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_TERR, +- .type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF, +- .dst_feature = DST_TYPE_HAS_CA, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "DTTDIG", +- .offset = 1, +- .dst_type = DST_TYPE_IS_TERR, +- .type_flags = DST_TYPE_HAS_FW_2, +- .dst_feature = 0, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "DTTNXT", +- .offset = 1, +- .dst_type = DST_TYPE_IS_TERR, +- .type_flags = DST_TYPE_HAS_FW_2, +- .dst_feature = DST_TYPE_HAS_ANALOG, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "ATSCDI", +- .offset = 1, +- .dst_type = DST_TYPE_IS_ATSC, +- .type_flags = DST_TYPE_HAS_FW_2, +- .dst_feature = 0, +- .tuner_type = 0 +- }, +- +- { +- .device_id = "ATSCAD", +- .offset = 1, +- .dst_type = DST_TYPE_IS_ATSC, +- .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD, +- .dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG, +- .tuner_type = 0 +- }, +- +- { } +- +-}; +- +-static int dst_get_mac(struct dst_state *state) +-{ +- u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- get_mac[7] = dst_check_sum(get_mac, 7); +- if (dst_command(state, get_mac, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Unsupported Command"); +- return -1; +- } +- memset(&state->mac_address, '\0', 8); +- memcpy(&state->mac_address, &state->rxbuffer, 6); +- dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address); +- +- return 0; +-} +- +-static int dst_fw_ver(struct dst_state *state) +-{ +- u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- get_ver[7] = dst_check_sum(get_ver, 7); +- if (dst_command(state, get_ver, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Unsupported Command"); +- return -1; +- } +- memcpy(&state->fw_version, &state->rxbuffer, 8); +- dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x", +- state->fw_version[0] >> 4, state->fw_version[0] & 0x0f, +- state->fw_version[1], +- state->fw_version[5], state->fw_version[6], +- state->fw_version[4], state->fw_version[3], state->fw_version[2]); +- +- return 0; +-} +- +-static int dst_card_type(struct dst_state *state) +-{ +- int j; +- struct tuner_types *p_tuner_list = NULL; +- +- u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- get_type[7] = dst_check_sum(get_type, 7); +- if (dst_command(state, get_type, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Unsupported Command"); +- return -1; +- } +- memset(&state->card_info, '\0', 8); +- memcpy(&state->card_info, &state->rxbuffer, 7); +- dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]); +- +- for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { +- if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) { +- state->tuner_type = p_tuner_list->tuner_type; +- dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]", +- p_tuner_list->tuner_name, p_tuner_list->tuner_type); +- } +- } +- +- return 0; +-} +- +-static int dst_get_vendor(struct dst_state *state) +-{ +- u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- get_vendor[7] = dst_check_sum(get_vendor, 7); +- if (dst_command(state, get_vendor, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Unsupported Command"); +- return -1; +- } +- memset(&state->vendor, '\0', 8); +- memcpy(&state->vendor, &state->rxbuffer, 7); +- dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]); +- +- return 0; +-} +- +-static void debug_dst_buffer(struct dst_state *state) +-{ +- int i; +- +- if (verbose > 2) { +- printk("%s: [", __func__); +- for (i = 0; i < 8; i++) +- printk(" %02x", state->rxbuffer[i]); +- printk("]\n"); +- } +-} +- +-static int dst_check_stv0299(struct dst_state *state) +-{ +- u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- check_stv0299[7] = dst_check_sum(check_stv0299, 7); +- if (dst_command(state, check_stv0299, 8) < 0) { +- dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed"); +- return -1; +- } +- debug_dst_buffer(state); +- +- if (memcmp(&check_stv0299, &state->rxbuffer, 8)) { +- dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM"); +- state->tuner_type = TUNER_TYPE_STV0299; +- return 0; +- } +- +- return -1; +-} +- +-static int dst_check_mb86a15(struct dst_state *state) +-{ +- u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- check_mb86a15[7] = dst_check_sum(check_mb86a15, 7); +- if (dst_command(state, check_mb86a15, 8) < 0) { +- dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed"); +- return -1; +- } +- debug_dst_buffer(state); +- +- if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) { +- dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM"); +- state->tuner_type = TUNER_TYPE_MB86A15; +- return 0; +- } +- +- return -1; +-} +- +-static int dst_get_tuner_info(struct dst_state *state) +-{ +- u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +- +- get_tuner_1[7] = dst_check_sum(get_tuner_1, 7); +- get_tuner_2[7] = dst_check_sum(get_tuner_2, 7); +- dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE"); +- if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { +- if (dst_command(state, get_tuner_1, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported"); +- goto force; +- } +- } else { +- if (dst_command(state, get_tuner_2, 8) < 0) { +- dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported"); +- goto force; +- } +- } +- memcpy(&state->board_info, &state->rxbuffer, 8); +- if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { +- dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); +- } +- if (state->board_info[0] == 0xbc) { +- if (state->dst_type != DST_TYPE_IS_ATSC) +- state->type_flags |= DST_TYPE_HAS_TS188; +- else +- state->type_flags |= DST_TYPE_HAS_NEWTUNE_2; +- +- if (state->board_info[1] == 0x01) { +- state->dst_hw_cap |= DST_TYPE_HAS_DBOARD; +- dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard"); +- } +- } +- +- return 0; +-force: +- if (!strncmp(state->fw_name, "DCT-CI", 6)) { +- state->type_flags |= DST_TYPE_HAS_TS204; +- dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name); +- } +- +- return -1; +-} +- +-static int dst_get_device_id(struct dst_state *state) +-{ +- u8 reply; +- +- int i, j; +- struct dst_types *p_dst_type = NULL; +- struct tuner_types *p_tuner_list = NULL; +- +- u8 use_dst_type = 0; +- u32 use_type_flags = 0; +- +- static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +- +- state->tuner_type = 0; +- device_type[7] = dst_check_sum(device_type, 7); +- +- if (write_dst(state, device_type, FIXED_COMM)) +- return -1; /* Write failed */ +- if ((dst_pio_disable(state)) < 0) +- return -1; +- if (read_dst(state, &reply, GET_ACK)) +- return -1; /* Read failure */ +- if (reply != ACK) { +- dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply); +- return -1; /* Unack'd write */ +- } +- if (!dst_wait_dst_ready(state, DEVICE_INIT)) +- return -1; /* DST not ready yet */ +- if (read_dst(state, state->rxbuffer, FIXED_COMM)) +- return -1; +- +- dst_pio_disable(state); +- if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { +- dprintk(verbose, DST_INFO, 1, "Checksum failure!"); +- return -1; /* Checksum failure */ +- } +- state->rxbuffer[7] = '\0'; +- +- for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) { +- if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) { +- use_type_flags = p_dst_type->type_flags; +- use_dst_type = p_dst_type->dst_type; +- +- /* Card capabilities */ +- state->dst_hw_cap = p_dst_type->dst_feature; +- dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id); +- strncpy(&state->fw_name[0], p_dst_type->device_id, 6); +- /* Multiple tuners */ +- if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) { +- switch (use_dst_type) { +- case DST_TYPE_IS_SAT: +- /* STV0299 check */ +- if (dst_check_stv0299(state) < 0) { +- dprintk(verbose, DST_ERROR, 1, "Unsupported"); +- state->tuner_type = TUNER_TYPE_MB86A15; +- } +- break; +- default: +- break; +- } +- if (dst_check_mb86a15(state) < 0) +- dprintk(verbose, DST_ERROR, 1, "Unsupported"); +- /* Single tuner */ +- } else { +- state->tuner_type = p_dst_type->tuner_type; +- } +- for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { +- if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) && +- p_tuner_list->tuner_type == state->tuner_type) { +- dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]", +- p_dst_type->device_id, p_tuner_list->tuner_name); +- } +- } +- break; +- } +- } +- +- if (i >= ARRAY_SIZE(dst_tlist)) { +- dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]); +- dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in"); +- use_dst_type = DST_TYPE_IS_SAT; +- use_type_flags = DST_TYPE_HAS_SYMDIV; +- } +- dst_type_print(state, use_dst_type); +- state->type_flags = use_type_flags; +- state->dst_type = use_dst_type; +- dst_type_flags_print(state); +- +- return 0; +-} +- +-static int dst_probe(struct dst_state *state) +-{ +- mutex_init(&state->dst_mutex); +- if (dst_addons & DST_TYPE_HAS_CA) { +- if ((rdc_8820_reset(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed."); +- return -1; +- } +- msleep(4000); +- } else { +- msleep(100); +- } +- if ((dst_comm_init(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed."); +- return -1; +- } +- msleep(100); +- if (dst_get_device_id(state) < 0) { +- dprintk(verbose, DST_ERROR, 1, "unknown device."); +- return -1; +- } +- if (dst_get_mac(state) < 0) { +- dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command"); +- } +- if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) { +- if (dst_get_tuner_info(state) < 0) +- dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command"); +- } +- if (state->type_flags & DST_TYPE_HAS_TS204) { +- dst_packsize(state, 204); +- } +- if (state->type_flags & DST_TYPE_HAS_FW_BUILD) { +- if (dst_fw_ver(state) < 0) { +- dprintk(verbose, DST_INFO, 1, "FW: Unsupported command"); +- return 0; +- } +- if (dst_card_type(state) < 0) { +- dprintk(verbose, DST_INFO, 1, "Card: Unsupported command"); +- return 0; +- } +- if (dst_get_vendor(state) < 0) { +- dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command"); +- return 0; +- } +- } +- +- return 0; +-} +- +-static int dst_command(struct dst_state *state, u8 *data, u8 len) +-{ +- u8 reply; +- +- mutex_lock(&state->dst_mutex); +- if ((dst_comm_init(state)) < 0) { +- dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed."); +- goto error; +- } +- if (write_dst(state, data, len)) { +- dprintk(verbose, DST_INFO, 1, "Trying to recover.. "); +- if ((dst_error_recovery(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "Recovery Failed."); +- goto error; +- } +- goto error; +- } +- if ((dst_pio_disable(state)) < 0) { +- dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed."); +- goto error; +- } +- if (state->type_flags & DST_TYPE_HAS_FW_1) +- mdelay(3); +- if (read_dst(state, &reply, GET_ACK)) { +- dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); +- if ((dst_error_recovery(state)) < 0) { +- dprintk(verbose, DST_INFO, 1, "Recovery Failed."); +- goto error; +- } +- goto error; +- } +- if (reply != ACK) { +- dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply); +- goto error; +- } +- if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) +- goto error; +- if (state->type_flags & DST_TYPE_HAS_FW_1) +- mdelay(3); +- else +- udelay(2000); +- if (!dst_wait_dst_ready(state, NO_DELAY)) +- goto error; +- if (read_dst(state, state->rxbuffer, FIXED_COMM)) { +- dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); +- if ((dst_error_recovery(state)) < 0) { +- dprintk(verbose, DST_INFO, 1, "Recovery failed."); +- goto error; +- } +- goto error; +- } +- if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { +- dprintk(verbose, DST_INFO, 1, "checksum failure"); +- goto error; +- } +- mutex_unlock(&state->dst_mutex); +- return 0; +- +-error: +- mutex_unlock(&state->dst_mutex); +- return -EIO; +- +-} +- +-static int dst_get_signal(struct dst_state *state) +-{ +- int retval; +- u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; +- //dprintk("%s: Getting Signal strength and other parameters\n", __func__); +- if ((state->diseq_flags & ATTEMPT_TUNE) == 0) { +- state->decode_lock = state->decode_strength = state->decode_snr = 0; +- return 0; +- } +- if (0 == (state->diseq_flags & HAS_LOCK)) { +- state->decode_lock = state->decode_strength = state->decode_snr = 0; +- return 0; +- } +- if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) { +- retval = dst_command(state, get_signal, 8); +- if (retval < 0) +- return retval; +- if (state->dst_type == DST_TYPE_IS_SAT) { +- state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0; +- state->decode_strength = state->rxbuffer[5] << 8; +- state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; +- } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) { +- state->decode_lock = (state->rxbuffer[1]) ? 1 : 0; +- state->decode_strength = state->rxbuffer[4] << 8; +- state->decode_snr = state->rxbuffer[3] << 8; +- } else if (state->dst_type == DST_TYPE_IS_ATSC) { +- state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0; +- state->decode_strength = state->rxbuffer[4] << 8; +- state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; +- } +- state->cur_jiff = jiffies; +- } +- return 0; +-} +- +-static int dst_tone_power_cmd(struct dst_state *state) +-{ +- u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; +- +- if (state->dst_type != DST_TYPE_IS_SAT) +- return -EOPNOTSUPP; +- paket[4] = state->tx_tuna[4]; +- paket[2] = state->tx_tuna[2]; +- paket[3] = state->tx_tuna[3]; +- paket[7] = dst_check_sum (paket, 7); +- return dst_command(state, paket, 8); +-} +- +-static int dst_get_tuna(struct dst_state *state) +-{ +- int retval; +- +- if ((state->diseq_flags & ATTEMPT_TUNE) == 0) +- return 0; +- state->diseq_flags &= ~(HAS_LOCK); +- if (!dst_wait_dst_ready(state, NO_DELAY)) +- return -EIO; +- if ((state->type_flags & DST_TYPE_HAS_VLF) && +- !(state->dst_type == DST_TYPE_IS_ATSC)) +- +- retval = read_dst(state, state->rx_tuna, 10); +- else +- retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM); +- if (retval < 0) { +- dprintk(verbose, DST_DEBUG, 1, "read not successful"); +- return retval; +- } +- if ((state->type_flags & DST_TYPE_HAS_VLF) && +- !(state->dst_type == DST_TYPE_IS_ATSC)) { +- +- if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { +- dprintk(verbose, DST_INFO, 1, "checksum failure ? "); +- return -EIO; +- } +- } else { +- if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) { +- dprintk(verbose, DST_INFO, 1, "checksum failure? "); +- return -EIO; +- } +- } +- if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0) +- return 0; +- if (state->dst_type == DST_TYPE_IS_SAT) { +- state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3]; +- } else { +- state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4]; +- } +- state->decode_freq = state->decode_freq * 1000; +- state->decode_lock = 1; +- state->diseq_flags |= HAS_LOCK; +- +- return 1; +-} +- +-static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); +- +-static int dst_write_tuna(struct dvb_frontend *fe) +-{ +- struct dst_state *state = fe->demodulator_priv; +- int retval; +- u8 reply; +- +- dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags); +- state->decode_freq = 0; +- state->decode_lock = state->decode_strength = state->decode_snr = 0; +- if (state->dst_type == DST_TYPE_IS_SAT) { +- if (!(state->diseq_flags & HAS_POWER)) +- dst_set_voltage(fe, SEC_VOLTAGE_13); +- } +- state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); +- mutex_lock(&state->dst_mutex); +- if ((dst_comm_init(state)) < 0) { +- dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed."); +- goto error; +- } +-// if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { +- if ((state->type_flags & DST_TYPE_HAS_VLF) && +- (!(state->dst_type == DST_TYPE_IS_ATSC))) { +- +- state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9); +- retval = write_dst(state, &state->tx_tuna[0], 10); +- } else { +- state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7); +- retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM); +- } +- if (retval < 0) { +- dst_pio_disable(state); +- dprintk(verbose, DST_DEBUG, 1, "write not successful"); +- goto werr; +- } +- if ((dst_pio_disable(state)) < 0) { +- dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !"); +- goto error; +- } +- if ((read_dst(state, &reply, GET_ACK) < 0)) { +- dprintk(verbose, DST_DEBUG, 1, "read verify not successful."); +- goto error; +- } +- if (reply != ACK) { +- dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply); +- goto error; +- } +- state->diseq_flags |= ATTEMPT_TUNE; +- retval = dst_get_tuna(state); +-werr: +- mutex_unlock(&state->dst_mutex); +- return retval; +- +-error: +- mutex_unlock(&state->dst_mutex); +- return -EIO; +-} +- +-/* +- * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 +- * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 +- * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 +- * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 +- * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 +- * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 +- * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 +- * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec +- * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 +- * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 +- * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 +- */ +- +-static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +-{ +- struct dst_state *state = fe->demodulator_priv; +- u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; +- +- if (state->dst_type != DST_TYPE_IS_SAT) +- return -EOPNOTSUPP; +- if (cmd->msg_len > 0 && cmd->msg_len < 5) +- memcpy(&paket[3], cmd->msg, cmd->msg_len); +- else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5) +- memcpy(&paket[2], cmd->msg, cmd->msg_len); +- else +- return -EINVAL; +- paket[7] = dst_check_sum(&paket[0], 7); +- return dst_command(state, paket, 8); +-} +- +-static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- int need_cmd, retval = 0; +- struct dst_state *state = fe->demodulator_priv; +- +- state->voltage = voltage; +- if (state->dst_type != DST_TYPE_IS_SAT) +- return -EOPNOTSUPP; +- +- need_cmd = 0; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- case SEC_VOLTAGE_18: +- if ((state->diseq_flags & HAS_POWER) == 0) +- need_cmd = 1; +- state->diseq_flags |= HAS_POWER; +- state->tx_tuna[4] = 0x01; +- break; +- case SEC_VOLTAGE_OFF: +- need_cmd = 1; +- state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); +- state->tx_tuna[4] = 0x00; +- break; +- default: +- return -EINVAL; +- } +- +- if (need_cmd) +- retval = dst_tone_power_cmd(state); +- +- return retval; +-} +- +-static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- state->tone = tone; +- if (state->dst_type != DST_TYPE_IS_SAT) +- return -EOPNOTSUPP; +- +- switch (tone) { +- case SEC_TONE_OFF: +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) +- state->tx_tuna[2] = 0x00; +- else +- state->tx_tuna[2] = 0xff; +- break; +- +- case SEC_TONE_ON: +- state->tx_tuna[2] = 0x02; +- break; +- default: +- return -EINVAL; +- } +- return dst_tone_power_cmd(state); +-} +- +-static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- if (state->dst_type != DST_TYPE_IS_SAT) +- return -EOPNOTSUPP; +- state->minicmd = minicmd; +- switch (minicmd) { +- case SEC_MINI_A: +- state->tx_tuna[3] = 0x02; +- break; +- case SEC_MINI_B: +- state->tx_tuna[3] = 0xff; +- break; +- } +- return dst_tone_power_cmd(state); +-} +- +- +-static int dst_init(struct dvb_frontend *fe) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 }; +- static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 }; +- static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; +- static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; +- static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; +- static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; +- static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; +- +- state->inversion = INVERSION_OFF; +- state->voltage = SEC_VOLTAGE_13; +- state->tone = SEC_TONE_OFF; +- state->diseq_flags = 0; +- state->k22 = 0x02; +- state->bandwidth = 7000000; +- state->cur_jiff = jiffies; +- if (state->dst_type == DST_TYPE_IS_SAT) +- memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204)); +- else if (state->dst_type == DST_TYPE_IS_TERR) +- memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204)); +- else if (state->dst_type == DST_TYPE_IS_CABLE) +- memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204)); +- else if (state->dst_type == DST_TYPE_IS_ATSC) +- memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner)); +- +- return 0; +-} +- +-static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- *status = 0; +- if (state->diseq_flags & HAS_LOCK) { +-// dst_get_signal(state); // don't require(?) to ask MCU +- if (state->decode_lock) +- *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; +- } +- +- return 0; +-} +- +-static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- int retval = dst_get_signal(state); +- *strength = state->decode_strength; +- +- return retval; +-} +- +-static int dst_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct dst_state *state = fe->demodulator_priv; +- +- int retval = dst_get_signal(state); +- *snr = state->decode_snr; +- +- return retval; +-} +- +-static int dst_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- int retval = -EINVAL; +- struct dst_state *state = fe->demodulator_priv; +- +- if (p != NULL) { +- retval = dst_set_freq(state, p->frequency); +- if(retval != 0) +- return retval; +- dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); +- +- if (state->dst_type == DST_TYPE_IS_SAT) { +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) +- dst_set_inversion(state, p->inversion); +- dst_set_fec(state, p->fec_inner); +- dst_set_symbolrate(state, p->symbol_rate); +- dst_set_polarization(state); +- dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); +- +- } else if (state->dst_type == DST_TYPE_IS_TERR) +- dst_set_bandwidth(state, p->bandwidth_hz); +- else if (state->dst_type == DST_TYPE_IS_CABLE) { +- dst_set_fec(state, p->fec_inner); +- dst_set_symbolrate(state, p->symbol_rate); +- dst_set_modulation(state, p->modulation); +- } +- retval = dst_write_tuna(fe); +- } +- +- return retval; +-} +- +-static int dst_tune_frontend(struct dvb_frontend* fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status) +-{ +- struct dst_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- if (re_tune) { +- dst_set_freq(state, p->frequency); +- dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); +- +- if (state->dst_type == DST_TYPE_IS_SAT) { +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) +- dst_set_inversion(state, p->inversion); +- dst_set_fec(state, p->fec_inner); +- dst_set_symbolrate(state, p->symbol_rate); +- dst_set_polarization(state); +- dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); +- +- } else if (state->dst_type == DST_TYPE_IS_TERR) +- dst_set_bandwidth(state, p->bandwidth_hz); +- else if (state->dst_type == DST_TYPE_IS_CABLE) { +- dst_set_fec(state, p->fec_inner); +- dst_set_symbolrate(state, p->symbol_rate); +- dst_set_modulation(state, p->modulation); +- } +- dst_write_tuna(fe); +- } +- +- if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) +- dst_read_status(fe, status); +- +- *delay = HZ/10; +- return 0; +-} +- +-static int dst_get_tuning_algo(struct dvb_frontend *fe) +-{ +- return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW; +-} +- +-static int dst_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dst_state *state = fe->demodulator_priv; +- +- p->frequency = state->decode_freq; +- if (state->dst_type == DST_TYPE_IS_SAT) { +- if (state->type_flags & DST_TYPE_HAS_OBS_REGS) +- p->inversion = state->inversion; +- p->symbol_rate = state->symbol_rate; +- p->fec_inner = dst_get_fec(state); +- } else if (state->dst_type == DST_TYPE_IS_TERR) { +- p->bandwidth_hz = state->bandwidth; +- } else if (state->dst_type == DST_TYPE_IS_CABLE) { +- p->symbol_rate = state->symbol_rate; +- p->fec_inner = dst_get_fec(state); +- p->modulation = dst_get_modulation(state); +- } +- +- return 0; +-} +- +-static void dst_release(struct dvb_frontend *fe) +-{ +- struct dst_state *state = fe->demodulator_priv; +- if (state->dst_ca) { +- dvb_unregister_device(state->dst_ca); +-#ifdef CONFIG_MEDIA_ATTACH +- symbol_put(dst_ca_attach); +-#endif +- } +- kfree(state); +-} +- +-static struct dvb_frontend_ops dst_dvbt_ops; +-static struct dvb_frontend_ops dst_dvbs_ops; +-static struct dvb_frontend_ops dst_dvbc_ops; +-static struct dvb_frontend_ops dst_atsc_ops; +- +-struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter) +-{ +- /* check if the ASIC is there */ +- if (dst_probe(state) < 0) { +- kfree(state); +- return NULL; +- } +- /* determine settings based on type */ +- /* create dvb_frontend */ +- switch (state->dst_type) { +- case DST_TYPE_IS_TERR: +- memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops)); +- break; +- case DST_TYPE_IS_CABLE: +- memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops)); +- break; +- case DST_TYPE_IS_SAT: +- memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops)); +- break; +- case DST_TYPE_IS_ATSC: +- memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops)); +- break; +- default: +- dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist."); +- kfree(state); +- return NULL; +- } +- state->frontend.demodulator_priv = state; +- +- return state; /* Manu (DST is a card not a frontend) */ +-} +- +-EXPORT_SYMBOL(dst_attach); +- +-static struct dvb_frontend_ops dst_dvbt_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DST DVB-T", +- .frequency_min = 137000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166667, +- .caps = FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | +- FE_CAN_QAM_256 | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO +- }, +- +- .release = dst_release, +- .init = dst_init, +- .tune = dst_tune_frontend, +- .set_frontend = dst_set_frontend, +- .get_frontend = dst_get_frontend, +- .get_frontend_algo = dst_get_tuning_algo, +- .read_status = dst_read_status, +- .read_signal_strength = dst_read_signal_strength, +- .read_snr = dst_read_snr, +-}; +- +-static struct dvb_frontend_ops dst_dvbs_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "DST DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1000, /* kHz for QPSK frontends */ +- .frequency_tolerance = 29500, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- /* . symbol_rate_tolerance = ???,*/ +- .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK +- }, +- +- .release = dst_release, +- .init = dst_init, +- .tune = dst_tune_frontend, +- .set_frontend = dst_set_frontend, +- .get_frontend = dst_get_frontend, +- .get_frontend_algo = dst_get_tuning_algo, +- .read_status = dst_read_status, +- .read_signal_strength = dst_read_signal_strength, +- .read_snr = dst_read_snr, +- .diseqc_send_burst = dst_send_burst, +- .diseqc_send_master_cmd = dst_set_diseqc, +- .set_voltage = dst_set_voltage, +- .set_tone = dst_set_tone, +-}; +- +-static struct dvb_frontend_ops dst_dvbc_ops = { +- .delsys = { SYS_DVBC_ANNEX_A }, +- .info = { +- .name = "DST DVB-C", +- .frequency_stepsize = 62500, +- .frequency_min = 51000000, +- .frequency_max = 858000000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | +- FE_CAN_QAM_256 +- }, +- +- .release = dst_release, +- .init = dst_init, +- .tune = dst_tune_frontend, +- .set_frontend = dst_set_frontend, +- .get_frontend = dst_get_frontend, +- .get_frontend_algo = dst_get_tuning_algo, +- .read_status = dst_read_status, +- .read_signal_strength = dst_read_signal_strength, +- .read_snr = dst_read_snr, +-}; +- +-static struct dvb_frontend_ops dst_atsc_ops = { +- .delsys = { SYS_ATSC }, +- .info = { +- .name = "DST ATSC", +- .frequency_stepsize = 62500, +- .frequency_min = 510000000, +- .frequency_max = 858000000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- +- .release = dst_release, +- .init = dst_init, +- .tune = dst_tune_frontend, +- .set_frontend = dst_set_frontend, +- .get_frontend = dst_get_frontend, +- .get_frontend_algo = dst_get_tuning_algo, +- .read_status = dst_read_status, +- .read_signal_strength = dst_read_signal_strength, +- .read_snr = dst_read_snr, +-}; +- +-MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver"); +-MODULE_AUTHOR("Jamie Honan, Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c +deleted file mode 100644 +index 48e48e8..0000000 +--- a/drivers/media/dvb/bt8xx/dst_ca.c ++++ /dev/null +@@ -1,727 +0,0 @@ +-/* +- CA-driver for TwinHan DST Frontend/Card +- +- Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvbdev.h" +-#include "dvb_frontend.h" +-#include "dst_ca.h" +-#include "dst_common.h" +- +-#define DST_CA_ERROR 0 +-#define DST_CA_NOTICE 1 +-#define DST_CA_INFO 2 +-#define DST_CA_DEBUG 3 +- +-#define dprintk(x, y, z, format, arg...) do { \ +- if (z) { \ +- if ((x > DST_CA_ERROR) && (x > y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > DST_CA_NOTICE) && (x > y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > DST_CA_INFO) && (x > y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > DST_CA_DEBUG) && (x > y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (x > y) \ +- printk(format, ## arg); \ +- } \ +-} while(0) +- +- +-static DEFINE_MUTEX(dst_ca_mutex); +-static unsigned int verbose = 5; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); +- +-/* Need some more work */ +-static int ca_set_slot_descr(void) +-{ +- /* We could make this more graceful ? */ +- return -EOPNOTSUPP; +-} +- +-/* Need some more work */ +-static int ca_set_pid(void) +-{ +- /* We could make this more graceful ? */ +- return -EOPNOTSUPP; +-} +- +-static void put_command_and_length(u8 *data, int command, int length) +-{ +- data[0] = (command >> 16) & 0xff; +- data[1] = (command >> 8) & 0xff; +- data[2] = command & 0xff; +- data[3] = length; +-} +- +-static void put_checksum(u8 *check_string, int length) +-{ +- dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum."); +- dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length); +- check_string[length] = dst_check_sum (check_string, length); +- dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]); +-} +- +-static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read) +-{ +- u8 reply; +- +- mutex_lock(&state->dst_mutex); +- dst_comm_init(state); +- msleep(65); +- +- if (write_dst(state, data, len)) { +- dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover"); +- dst_error_recovery(state); +- goto error; +- } +- if ((dst_pio_disable(state)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed."); +- goto error; +- } +- if (read_dst(state, &reply, GET_ACK) < 0) { +- dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); +- dst_error_recovery(state); +- goto error; +- } +- if (read) { +- if (! dst_wait_dst_ready(state, LONG_DELAY)) { +- dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready"); +- goto error; +- } +- if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */ +- dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); +- dst_error_recovery(state); +- goto error; +- } +- } +- mutex_unlock(&state->dst_mutex); +- return 0; +- +-error: +- mutex_unlock(&state->dst_mutex); +- return -EIO; +-} +- +- +-static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read) +-{ +- u8 dst_ca_comm_err = 0; +- +- while (dst_ca_comm_err < RETRIES) { +- dprintk(verbose, DST_CA_NOTICE, 1, " Put Command"); +- if (dst_ci_command(state, data, ca_string, len, read)) { // If error +- dst_error_recovery(state); +- dst_ca_comm_err++; // work required here. +- } else { +- break; +- } +- } +- +- if(dst_ca_comm_err == RETRIES) +- return -1; +- +- return 0; +-} +- +- +- +-static int ca_get_app_info(struct dst_state *state) +-{ +- int length, str_length; +- static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff}; +- +- put_checksum(&command[0], command[0]); +- if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); +- return -1; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); +- dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================"); +- dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]", +- state->messages[7], (state->messages[8] << 8) | state->messages[9], +- (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12])); +- dprintk(verbose, DST_CA_INFO, 1, " =================================================================================================="); +- +- // Transform dst message to correct application_info message +- length = state->messages[5]; +- str_length = length - 6; +- if (str_length < 0) { +- str_length = 0; +- dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering."); +- } +- +- // First, the command and length fields +- put_command_and_length(&state->messages[0], CA_APP_INFO, length); +- +- // Copy application_type, application_manufacturer and manufacturer_code +- memcpy(&state->messages[4], &state->messages[7], 5); +- +- // Set string length and copy string +- state->messages[9] = str_length; +- memcpy(&state->messages[10], &state->messages[12], str_length); +- +- return 0; +-} +- +-static int ca_get_ca_info(struct dst_state *state) +-{ +- int srcPtr, dstPtr, i, num_ids; +- static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff}; +- const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7; +- +- put_checksum(&slot_command[0], slot_command[0]); +- if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); +- return -1; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); +- +- // Print raw data +- dprintk(verbose, DST_CA_INFO, 0, " DST data = ["); +- for (i = 0; i < state->messages[0] + 1; i++) { +- dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]); +- } +- dprintk(verbose, DST_CA_INFO, 0, "]\n"); +- +- // Set the command and length of the output +- num_ids = state->messages[in_num_ids_pos]; +- if (num_ids >= 100) { +- num_ids = 100; +- dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering."); +- } +- put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2); +- +- dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = ["); +- srcPtr = in_system_id_pos; +- dstPtr = out_system_id_pos; +- for(i = 0; i < num_ids; i++) { +- dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]); +- // Append to output +- state->messages[dstPtr + 0] = state->messages[srcPtr + 0]; +- state->messages[dstPtr + 1] = state->messages[srcPtr + 1]; +- srcPtr += 2; +- dstPtr += 2; +- } +- dprintk(verbose, DST_CA_INFO, 0, "]\n"); +- +- return 0; +-} +- +-static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg) +-{ +- int i; +- u8 slot_cap[256]; +- static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff}; +- +- put_checksum(&slot_command[0], slot_command[0]); +- if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); +- return -1; +- } +- dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !"); +- +- /* Will implement the rest soon */ +- +- dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]); +- dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); +- for (i = 0; i < slot_cap[0] + 1; i++) +- dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]); +- dprintk(verbose, DST_CA_INFO, 0, "\n"); +- +- p_ca_caps->slot_num = 1; +- p_ca_caps->slot_type = 1; +- p_ca_caps->descr_num = slot_cap[7]; +- p_ca_caps->descr_type = 1; +- +- if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps))) +- return -EFAULT; +- +- return 0; +-} +- +-/* Need some more work */ +-static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +-{ +- return -EOPNOTSUPP; +-} +- +- +-static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg) +-{ +- int i; +- static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; +- +- u8 *slot_info = state->messages; +- +- put_checksum(&slot_command[0], 7); +- if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); +- return -1; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); +- +- /* Will implement the rest soon */ +- +- dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]); +- dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); +- for (i = 0; i < 8; i++) +- dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]); +- dprintk(verbose, DST_CA_INFO, 0, "\n"); +- +- if (slot_info[4] & 0x80) { +- p_ca_slot_info->flags = CA_CI_MODULE_PRESENT; +- p_ca_slot_info->num = 1; +- p_ca_slot_info->type = CA_CI; +- } else if (slot_info[4] & 0x40) { +- p_ca_slot_info->flags = CA_CI_MODULE_READY; +- p_ca_slot_info->num = 1; +- p_ca_slot_info->type = CA_CI; +- } else +- p_ca_slot_info->flags = 0; +- +- if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info))) +- return -EFAULT; +- +- return 0; +-} +- +- +-static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +-{ +- u8 i = 0; +- u32 command = 0; +- +- if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) +- return -EFAULT; +- +- if (p_ca_message->msg) { +- dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%02x %02x %02x]", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]); +- +- for (i = 0; i < 3; i++) { +- command = command | p_ca_message->msg[i]; +- if (i < 2) +- command = command << 8; +- } +- dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); +- +- switch (command) { +- case CA_APP_INFO: +- memcpy(p_ca_message->msg, state->messages, 128); +- if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) +- return -EFAULT; +- break; +- case CA_INFO: +- memcpy(p_ca_message->msg, state->messages, 128); +- if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) +- return -EFAULT; +- break; +- } +- } +- +- return 0; +-} +- +-static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length) +-{ +- if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) { +- hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */ +- hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */ +- } else { +- if (length > 247) { +- dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !"); +- return -1; +- } +- hw_buffer->msg[0] = (length & 0xff) + 7; +- hw_buffer->msg[1] = 0x40; +- hw_buffer->msg[2] = 0x03; +- hw_buffer->msg[3] = 0x00; +- hw_buffer->msg[4] = 0x03; +- hw_buffer->msg[5] = length & 0xff; +- hw_buffer->msg[6] = 0x00; +- +- /* +- * Need to compute length for EN50221 section 8.3.2, for the time being +- * assuming 8.3.2 is not applicable +- */ +- memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length); +- } +- +- return 0; +-} +- +-static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply) +-{ +- if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed."); +- dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST."); +- rdc_reset_state(state); +- return -1; +- } +- dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success."); +- +- return 0; +-} +- +-static u32 asn_1_decode(u8 *asn_1_array) +-{ +- u8 length_field = 0, word_count = 0, count = 0; +- u32 length = 0; +- +- length_field = asn_1_array[0]; +- dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field); +- if (length_field < 0x80) { +- length = length_field & 0x7f; +- dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length); +- } else { +- word_count = length_field & 0x7f; +- for (count = 0; count < word_count; count++) { +- length = length << 8; +- length += asn_1_array[count + 1]; +- dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length); +- } +- } +- return length; +-} +- +-static int debug_string(u8 *msg, u32 length, u32 offset) +-{ +- u32 i; +- +- dprintk(verbose, DST_CA_DEBUG, 0, " String=[ "); +- for (i = offset; i < length; i++) +- dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]); +- dprintk(verbose, DST_CA_DEBUG, 0, "]\n"); +- +- return 0; +-} +- +- +-static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query) +-{ +- u32 length = 0; +- u8 tag_length = 8; +- +- length = asn_1_decode(&p_ca_message->msg[3]); +- dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length); +- debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */ +- +- memset(hw_buffer->msg, '\0', length); +- handle_dst_tag(state, p_ca_message, hw_buffer, length); +- put_checksum(hw_buffer->msg, hw_buffer->msg[0]); +- +- debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */ +- write_to_8820(state, hw_buffer, (length + tag_length), reply); +- +- return 0; +-} +- +- +-/* Board supports CA PMT reply ? */ +-static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer) +-{ +- int ca_pmt_reply_test = 0; +- +- /* Do test board */ +- /* Not there yet but soon */ +- +- /* CA PMT Reply capable */ +- if (ca_pmt_reply_test) { +- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); +- return -1; +- } +- +- /* Process CA PMT Reply */ +- /* will implement soon */ +- dprintk(verbose, DST_CA_ERROR, 1, " Not there yet"); +- } +- /* CA PMT Reply not capable */ +- if (!ca_pmt_reply_test) { +- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); +- return -1; +- } +- dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !"); +- /* put a dummy message */ +- +- } +- return 0; +-} +- +-static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) +-{ +- int i = 0; +- unsigned int ca_message_header_len; +- +- u32 command = 0; +- struct ca_msg *hw_buffer; +- int result = 0; +- +- if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) { +- dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); +- return -ENOMEM; +- } +- dprintk(verbose, DST_CA_DEBUG, 1, " "); +- +- if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) { +- result = -EFAULT; +- goto free_mem_and_exit; +- } +- +- +- if (p_ca_message->msg) { +- ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */ +- /* EN50221 tag */ +- command = 0; +- +- for (i = 0; i < 3; i++) { +- command = command | p_ca_message->msg[i]; +- if (i < 2) +- command = command << 8; +- } +- dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); +- +- switch (command) { +- case CA_PMT: +- dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); +- if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); +- break; +- case CA_PMT_REPLY: +- dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); +- /* Have to handle the 2 basic types of cards here */ +- if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); +- break; +- case CA_APP_INFO_ENQUIRY: // only for debugging +- dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); +- +- if ((ca_get_app_info(state)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); +- break; +- case CA_INFO_ENQUIRY: +- dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); +- +- if ((ca_get_ca_info(state)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); +- break; +- } +- } +-free_mem_and_exit: +- kfree (hw_buffer); +- +- return result; +-} +- +-static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg) +-{ +- struct dvb_device *dvbdev; +- struct dst_state *state; +- struct ca_slot_info *p_ca_slot_info; +- struct ca_caps *p_ca_caps; +- struct ca_msg *p_ca_message; +- void __user *arg = (void __user *)ioctl_arg; +- int result = 0; +- +- mutex_lock(&dst_ca_mutex); +- dvbdev = file->private_data; +- state = (struct dst_state *)dvbdev->priv; +- p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); +- p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); +- p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL); +- if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) { +- dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); +- result = -ENOMEM; +- goto free_mem_and_exit; +- } +- +- /* We have now only the standard ioctl's, the driver is upposed to handle internals. */ +- switch (cmd) { +- case CA_SEND_MSG: +- dprintk(verbose, DST_CA_INFO, 1, " Sending message"); +- if ((ca_send_message(state, p_ca_message, arg)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- break; +- case CA_GET_MSG: +- dprintk(verbose, DST_CA_INFO, 1, " Getting message"); +- if ((ca_get_message(state, p_ca_message, arg)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !"); +- break; +- case CA_RESET: +- dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST"); +- dst_error_bailout(state); +- msleep(4000); +- break; +- case CA_GET_SLOT_INFO: +- dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info"); +- if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !"); +- break; +- case CA_GET_CAP: +- dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities"); +- if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !"); +- break; +- case CA_GET_DESCR_INFO: +- dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description"); +- if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !"); +- break; +- case CA_SET_DESCR: +- dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler"); +- if ((ca_set_slot_descr()) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !"); +- break; +- case CA_SET_PID: +- dprintk(verbose, DST_CA_INFO, 1, " Setting PID"); +- if ((ca_set_pid()) < 0) { +- dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !"); +- result = -1; +- goto free_mem_and_exit; +- } +- dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); +- default: +- result = -EOPNOTSUPP; +- }; +- free_mem_and_exit: +- kfree (p_ca_message); +- kfree (p_ca_slot_info); +- kfree (p_ca_caps); +- +- mutex_unlock(&dst_ca_mutex); +- return result; +-} +- +-static int dst_ca_open(struct inode *inode, struct file *file) +-{ +- dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); +- try_module_get(THIS_MODULE); +- +- return 0; +-} +- +-static int dst_ca_release(struct inode *inode, struct file *file) +-{ +- dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); +- module_put(THIS_MODULE); +- +- return 0; +-} +- +-static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) +-{ +- ssize_t bytes_read = 0; +- +- dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); +- +- return bytes_read; +-} +- +-static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) +-{ +- dprintk(verbose, DST_CA_DEBUG, 1, " Device write."); +- +- return 0; +-} +- +-static const struct file_operations dst_ca_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = dst_ca_ioctl, +- .open = dst_ca_open, +- .release = dst_ca_release, +- .read = dst_ca_read, +- .write = dst_ca_write, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_ca = { +- .priv = NULL, +- .users = 1, +- .readers = 1, +- .writers = 1, +- .fops = &dst_ca_fops +-}; +- +-struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) +-{ +- struct dvb_device *dvbdev; +- +- dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); +- if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { +- dst->dst_ca = dvbdev; +- return dst->dst_ca; +- } +- +- return NULL; +-} +- +-EXPORT_SYMBOL(dst_ca_attach); +- +-MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/bt8xx/dst_ca.h b/drivers/media/dvb/bt8xx/dst_ca.h +deleted file mode 100644 +index 59cd0dd..0000000 +--- a/drivers/media/dvb/bt8xx/dst_ca.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- CA-driver for TwinHan DST Frontend/Card +- +- Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. +-*/ +- +-#ifndef _DST_CA_H_ +-#define _DST_CA_H_ +- +-#define RETRIES 5 +- +- +-#define CA_APP_INFO_ENQUIRY 0x9f8020 +-#define CA_APP_INFO 0x9f8021 +-#define CA_ENTER_MENU 0x9f8022 +-#define CA_INFO_ENQUIRY 0x9f8030 +-#define CA_INFO 0x9f8031 +-#define CA_PMT 0x9f8032 +-#define CA_PMT_REPLY 0x9f8033 +- +-#define CA_CLOSE_MMI 0x9f8800 +-#define CA_DISPLAY_CONTROL 0x9f8801 +-#define CA_DISPLAY_REPLY 0x9f8802 +-#define CA_TEXT_LAST 0x9f8803 +-#define CA_TEXT_MORE 0x9f8804 +-#define CA_KEYPAD_CONTROL 0x9f8805 +-#define CA_KEYPRESS 0x9f8806 +- +-#define CA_ENQUIRY 0x9f8807 +-#define CA_ANSWER 0x9f8808 +-#define CA_MENU_LAST 0x9f8809 +-#define CA_MENU_MORE 0x9f880a +-#define CA_MENU_ANSWER 0x9f880b +-#define CA_LIST_LAST 0x9f880c +-#define CA_LIST_MORE 0x9f880d +- +- +-struct dst_ca_private { +- struct dst_state *dst; +- struct dvb_device *dvbdev; +-}; +- +- +-#endif +diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h +deleted file mode 100644 +index d70d98f..0000000 +--- a/drivers/media/dvb/bt8xx/dst_common.h ++++ /dev/null +@@ -1,182 +0,0 @@ +-/* +- Frontend-driver for TwinHan DST Frontend +- +- Copyright (C) 2003 Jamie Honan +- Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. +-*/ +- +-#ifndef DST_COMMON_H +-#define DST_COMMON_H +- +-#include +-#include +-#include +-#include "bt878.h" +- +-#include "dst_ca.h" +- +- +-#define NO_DELAY 0 +-#define LONG_DELAY 1 +-#define DEVICE_INIT 2 +- +-#define DELAY 1 +- +-#define DST_TYPE_IS_SAT 0 +-#define DST_TYPE_IS_TERR 1 +-#define DST_TYPE_IS_CABLE 2 +-#define DST_TYPE_IS_ATSC 3 +- +-#define DST_TYPE_HAS_TS188 1 +-#define DST_TYPE_HAS_TS204 2 +-#define DST_TYPE_HAS_SYMDIV 4 +-#define DST_TYPE_HAS_FW_1 8 +-#define DST_TYPE_HAS_FW_2 16 +-#define DST_TYPE_HAS_FW_3 32 +-#define DST_TYPE_HAS_FW_BUILD 64 +-#define DST_TYPE_HAS_OBS_REGS 128 +-#define DST_TYPE_HAS_INC_COUNT 256 +-#define DST_TYPE_HAS_MULTI_FE 512 +-#define DST_TYPE_HAS_NEWTUNE_2 1024 +-#define DST_TYPE_HAS_DBOARD 2048 +-#define DST_TYPE_HAS_VLF 4096 +- +-/* Card capability list */ +- +-#define DST_TYPE_HAS_MAC 1 +-#define DST_TYPE_HAS_DISEQC3 2 +-#define DST_TYPE_HAS_DISEQC4 4 +-#define DST_TYPE_HAS_DISEQC5 8 +-#define DST_TYPE_HAS_MOTO 16 +-#define DST_TYPE_HAS_CA 32 +-#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */ +-#define DST_TYPE_HAS_SESSION 128 +- +-#define TUNER_TYPE_MULTI 1 +-#define TUNER_TYPE_UNKNOWN 2 +-/* DVB-S */ +-#define TUNER_TYPE_L64724 4 +-#define TUNER_TYPE_STV0299 8 +-#define TUNER_TYPE_MB86A15 16 +- +-/* DVB-T */ +-#define TUNER_TYPE_TDA10046 32 +- +-/* ATSC */ +-#define TUNER_TYPE_NXT200x 64 +- +- +-#define RDC_8820_PIO_0_DISABLE 0 +-#define RDC_8820_PIO_0_ENABLE 1 +-#define RDC_8820_INT 2 +-#define RDC_8820_RESET 4 +- +-/* DST Communication */ +-#define GET_REPLY 1 +-#define NO_REPLY 0 +- +-#define GET_ACK 1 +-#define FIXED_COMM 8 +- +-#define ACK 0xff +- +-struct dst_state { +- +- struct i2c_adapter* i2c; +- +- struct bt878* bt; +- +- /* configuration settings */ +- const struct dst_config* config; +- +- struct dvb_frontend frontend; +- +- /* private ASIC data */ +- u8 tx_tuna[10]; +- u8 rx_tuna[10]; +- u8 rxbuffer[10]; +- u8 diseq_flags; +- u8 dst_type; +- u32 type_flags; +- u32 frequency; /* intermediate frequency in kHz for QPSK */ +- fe_spectral_inversion_t inversion; +- u32 symbol_rate; /* symbol rate in Symbols per second */ +- fe_code_rate_t fec; +- fe_sec_voltage_t voltage; +- fe_sec_tone_mode_t tone; +- u32 decode_freq; +- u8 decode_lock; +- u16 decode_strength; +- u16 decode_snr; +- unsigned long cur_jiff; +- u8 k22; +- u32 bandwidth; +- u32 dst_hw_cap; +- u8 dst_fw_version; +- fe_sec_mini_cmd_t minicmd; +- fe_modulation_t modulation; +- u8 messages[256]; +- u8 mac_address[8]; +- u8 fw_version[8]; +- u8 card_info[8]; +- u8 vendor[8]; +- u8 board_info[8]; +- u32 tuner_type; +- char *tuner_name; +- struct mutex dst_mutex; +- u8 fw_name[8]; +- struct dvb_device *dst_ca; +-}; +- +-struct tuner_types { +- u32 tuner_type; +- char *tuner_name; +- char *board_name; +- char *fw_name; +-}; +- +-struct dst_types { +- char *device_id; +- int offset; +- u8 dst_type; +- u32 type_flags; +- u32 dst_feature; +- u32 tuner_type; +-}; +- +-struct dst_config +-{ +- /* the ASIC i2c address */ +- u8 demod_address; +-}; +- +-int rdc_reset_state(struct dst_state *state); +- +-int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode); +-int dst_pio_disable(struct dst_state *state); +-int dst_error_recovery(struct dst_state* state); +-int dst_error_bailout(struct dst_state *state); +-int dst_comm_init(struct dst_state* state); +- +-int write_dst(struct dst_state *state, u8 * data, u8 len); +-int read_dst(struct dst_state *state, u8 * ret, u8 len); +-u8 dst_check_sum(u8 * buf, u32 len); +-struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter); +-struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); +- +- +-#endif // DST_COMMON_H +diff --git a/drivers/media/dvb/bt8xx/dst_priv.h b/drivers/media/dvb/bt8xx/dst_priv.h +deleted file mode 100644 +index 3974a4c..0000000 +--- a/drivers/media/dvb/bt8xx/dst_priv.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-/* +- * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend +- * +- * Copyright (C) 2003 Jamie Honan +- */ +- +-struct dst_gpio_enable { +- u32 mask; +- u32 enable; +-}; +- +-struct dst_gpio_output { +- u32 mask; +- u32 highvals; +-}; +- +-struct dst_gpio_read { +- unsigned long value; +-}; +- +-union dst_gpio_packet { +- struct dst_gpio_enable enb; +- struct dst_gpio_output outp; +- struct dst_gpio_read rd; +- int psize; +-}; +- +-#define DST_IG_ENABLE 0 +-#define DST_IG_WRITE 1 +-#define DST_IG_READ 2 +-#define DST_IG_TS 3 +- +-struct bt878; +- +-int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp); +diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c +deleted file mode 100644 +index 81fab9a..0000000 +--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c ++++ /dev/null +@@ -1,975 +0,0 @@ +-/* +- * Bt8xx based DVB adapter driver +- * +- * Copyright (C) 2002,2003 Florian Schirmer +- * +- * 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) "dvb_bt8xx: " fmt +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb-bt8xx.h" +-#include "bt878.h" +- +-static int debug; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define dprintk( args... ) \ +- do { \ +- if (debug) printk(KERN_DEBUG args); \ +- } while (0) +- +-#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ +- +-static void dvb_bt8xx_task(unsigned long data) +-{ +- struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; +- +- //printk("%d ", card->bt->finished_block); +- +- while (card->bt->last_block != card->bt->finished_block) { +- (card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) +- (&card->demux, +- &card->bt->buf_cpu[card->bt->last_block * +- card->bt->block_bytes], +- card->bt->block_bytes); +- card->bt->last_block = (card->bt->last_block + 1) % +- card->bt->block_count; +- } +-} +- +-static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux*dvbdmx = dvbdmxfeed->demux; +- struct dvb_bt8xx_card *card = dvbdmx->priv; +- int rc; +- +- dprintk("dvb_bt8xx: start_feed\n"); +- +- if (!dvbdmx->dmx.frontend) +- return -EINVAL; +- +- mutex_lock(&card->lock); +- card->nfeeds++; +- rc = card->nfeeds; +- if (card->nfeeds == 1) +- bt878_start(card->bt, card->gpio_mode, +- card->op_sync_orin, card->irq_err_ignore); +- mutex_unlock(&card->lock); +- return rc; +-} +- +-static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct dvb_bt8xx_card *card = dvbdmx->priv; +- +- dprintk("dvb_bt8xx: stop_feed\n"); +- +- if (!dvbdmx->dmx.frontend) +- return -EINVAL; +- +- mutex_lock(&card->lock); +- card->nfeeds--; +- if (card->nfeeds == 0) +- bt878_stop(card->bt); +- mutex_unlock(&card->lock); +- +- return 0; +-} +- +-static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev) +-{ +- if ((adev->subsystem_vendor == bdev->subsystem_vendor) && +- (adev->subsystem_device == bdev->subsystem_device) && +- (adev->bus->number == bdev->bus->number) && +- (PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn))) +- return 1; +- return 0; +-} +- +-static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev) +-{ +- unsigned int card_nr; +- +- /* Hmm, n squared. Hope n is small */ +- for (card_nr = 0; card_nr < bt878_num; card_nr++) +- if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev)) +- return &bt878[card_nr]; +- return NULL; +-} +- +-static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) +-{ +- static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 }; +- static u8 mt352_reset [] = { 0x50, 0x80 }; +- static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; +- static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 }; +- static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 }; +- 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_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); +- mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); +- +- return 0; +-} +- +-static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 div; +- unsigned char bs = 0; +- unsigned char cp = 0; +- +- if (buf_len < 5) +- return -EINVAL; +- +- div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; +- +- if (c->frequency < 542000000) +- cp = 0xb4; +- else if (c->frequency < 771000000) +- cp = 0xbc; +- else +- cp = 0xf4; +- +- if (c->frequency == 0) +- bs = 0x03; +- else if (c->frequency < 443250000) +- bs = 0x02; +- else +- bs = 0x08; +- +- pllbuf[0] = 0x60; +- pllbuf[1] = div >> 8; +- pllbuf[2] = div & 0xff; +- pllbuf[3] = cp; +- pllbuf[4] = bs; +- +- return 5; +-} +- +-static struct mt352_config thomson_dtt7579_config = { +- .demod_address = 0x0f, +- .demod_init = thomson_dtt7579_demod_init, +-}; +- +-static struct zl10353_config thomson_dtt7579_zl10353_config = { +- .demod_address = 0x0f, +-}; +- +-static int cx24108_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 freq = c->frequency; +- int i, a, n, pump; +- u32 band, pll; +- u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000, +- 1576000,1718000,1856000,2036000,2150000}; +- u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000, +- 0x00102000,0x00104000,0x00108000,0x00110000, +- 0x00120000,0x00140000}; +- +- #define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ +- dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq); +- +- /* This is really the bit driving the tuner chip cx24108 */ +- +- if (freq<950000) +- freq = 950000; /* kHz */ +- else if (freq>2150000) +- freq = 2150000; /* satellite IF is 950..2150MHz */ +- +- /* decide which VCO to use for the input frequency */ +- for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); +- dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq); +- band=bandsel[i]; +- /* the gain values must be set by SetSymbolrate */ +- /* compute the pll divider needed, from Conexant data sheet, +- resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4, +- depending on the divider bit. It is set to /4 on the 2 lowest +- bands */ +- n=((i<=2?2:1)*freq*10L)/(XTAL/100); +- a=n%32; n/=32; if(a==0) n--; +- pump=(freq<(osci[i-1]+osci[i])/2); +- pll=0xf8000000| +- ((pump?1:2)<<(14+11))| +- ((n&0x1ff)<<(5+11))| +- ((a&0x1f)<<11); +- /* everything is shifted left 11 bits to left-align the bits in the +- 32bit word. Output to the tuner goes MSB-aligned, after all */ +- dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a); +- cx24110_pll_write(fe,band); +- /* set vga and vca to their widest-band settings, as a precaution. +- SetSymbolrate might not be called to set this up */ +- cx24110_pll_write(fe,0x500c0000); +- cx24110_pll_write(fe,0x83f1f800); +- cx24110_pll_write(fe,pll); +- //writereg(client,0x56,0x7f); +- +- return 0; +-} +- +-static int pinnsat_tuner_init(struct dvb_frontend* fe) +-{ +- struct dvb_bt8xx_card *card = fe->dvb->priv; +- +- bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */ +- bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */ +- +- return 0; +-} +- +-static int pinnsat_tuner_sleep(struct dvb_frontend* fe) +-{ +- struct dvb_bt8xx_card *card = fe->dvb->priv; +- +- bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */ +- +- return 0; +-} +- +-static struct cx24110_config pctvsat_config = { +- .demod_address = 0x55, +-}; +- +-static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; +- u8 cfg, cpump, band_select; +- u8 data[4]; +- u32 div; +- struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (36000000 + c->frequency + 83333) / 166666; +- cfg = 0x88; +- +- if (c->frequency < 175000000) +- cpump = 2; +- else if (c->frequency < 390000000) +- cpump = 1; +- else if (c->frequency < 470000000) +- cpump = 2; +- else if (c->frequency < 750000000) +- cpump = 2; +- else +- cpump = 3; +- +- if (c->frequency < 175000000) +- band_select = 0x0e; +- else if (c->frequency < 470000000) +- band_select = 0x05; +- else +- band_select = 0x03; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = ((div >> 10) & 0x60) | cfg; +- data[3] = (cpump << 6) | band_select; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- i2c_transfer(card->i2c_adapter, &msg, 1); +- return (div * 166666 - 36000000); +-} +- +-static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +-{ +- struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; +- +- return request_firmware(fw, name, &bt->bt->dev->dev); +-} +- +-static struct sp887x_config microtune_mt7202dtf_config = { +- .demod_address = 0x70, +- .request_firmware = microtune_mt7202dtf_request_firmware, +-}; +- +-static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) +-{ +- static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; +- static u8 mt352_reset [] = { 0x50, 0x80 }; +- static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; +- static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, +- 0x00, 0xFF, 0x00, 0x40, 0x40 }; +- static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; +- 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)); +- udelay(2000); +- mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra)); +- mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); +- +- return 0; +-} +- +-static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 div; +- unsigned char bs = 0; +- unsigned char cp = 0; +- +- if (buf_len < 5) return -EINVAL; +- +- div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; +- +- if (c->frequency < 150000000) +- cp = 0xB4; +- else if (c->frequency < 173000000) +- cp = 0xBC; +- else if (c->frequency < 250000000) +- cp = 0xB4; +- else if (c->frequency < 400000000) +- cp = 0xBC; +- else if (c->frequency < 420000000) +- cp = 0xF4; +- else if (c->frequency < 470000000) +- cp = 0xFC; +- else if (c->frequency < 600000000) +- cp = 0xBC; +- else if (c->frequency < 730000000) +- cp = 0xF4; +- else +- cp = 0xFC; +- +- if (c->frequency < 150000000) +- bs = 0x01; +- else if (c->frequency < 173000000) +- bs = 0x01; +- else if (c->frequency < 250000000) +- bs = 0x02; +- else if (c->frequency < 400000000) +- bs = 0x02; +- else if (c->frequency < 420000000) +- bs = 0x02; +- else if (c->frequency < 470000000) +- bs = 0x02; +- else if (c->frequency < 600000000) +- bs = 0x08; +- else if (c->frequency < 730000000) +- bs = 0x08; +- else +- bs = 0x08; +- +- pllbuf[0] = 0x61; +- pllbuf[1] = div >> 8; +- pllbuf[2] = div & 0xff; +- pllbuf[3] = cp; +- pllbuf[4] = bs; +- +- return 5; +-} +- +-static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { +- .demod_address = 0x0f, +- .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, +-}; +- +-static struct dst_config dst_config = { +- .demod_address = 0x55, +-}; +- +-static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +-{ +- struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; +- +- return request_firmware(fw, name, &bt->bt->dev->dev); +-} +- +-static void or51211_setmode(struct dvb_frontend * fe, int mode) +-{ +- struct dvb_bt8xx_card *bt = fe->dvb->priv; +- bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */ +- msleep(20); +-} +- +-static void or51211_reset(struct dvb_frontend * fe) +-{ +- struct dvb_bt8xx_card *bt = fe->dvb->priv; +- +- /* RESET DEVICE +- * reset is controlled by GPIO-0 +- * when set to 0 causes reset and when to 1 for normal op +- * must remain reset for 128 clock cycles on a 50Mhz clock +- * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4 +- * We assume that the reset has be held low long enough or we +- * have been reset by a power on. When the driver is unloaded +- * reset set to 0 so if reloaded we have been reset. +- */ +- /* reset & PRM1,2&4 are outputs */ +- int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F); +- if (ret != 0) +- printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret); +- bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */ +- msleep(20); +- /* Now set for normal operation */ +- bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001); +- /* wait for operation to begin */ +- msleep(500); +-} +- +-static void or51211_sleep(struct dvb_frontend * fe) +-{ +- struct dvb_bt8xx_card *bt = fe->dvb->priv; +- bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000); +-} +- +-static struct or51211_config or51211_config = { +- .demod_address = 0x15, +- .request_firmware = or51211_request_firmware, +- .setmode = or51211_setmode, +- .reset = or51211_reset, +- .sleep = or51211_sleep, +-}; +- +-static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; +- u8 buf[4]; +- u32 div; +- struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) }; +- +- div = (c->frequency + 36166667) / 166667; +- +- buf[0] = (div >> 8) & 0x7F; +- buf[1] = div & 0xFF; +- buf[2] = 0x85; +- if ((c->frequency >= 47000000) && (c->frequency < 153000000)) +- buf[3] = 0x01; +- else if ((c->frequency >= 153000000) && (c->frequency < 430000000)) +- buf[3] = 0x02; +- else if ((c->frequency >= 430000000) && (c->frequency < 824000000)) +- buf[3] = 0x0C; +- else if ((c->frequency >= 824000000) && (c->frequency < 863000000)) +- buf[3] = 0x8C; +- else +- return -EINVAL; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- i2c_transfer(card->i2c_adapter, &msg, 1); +- return 0; +-} +- +-static struct nxt6000_config vp3021_alps_tded4_config = { +- .demod_address = 0x0a, +- .clock_inversion = 1, +-}; +- +-static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) +-{ +- static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; +- static u8 mt352_reset [] = { 0x50, 0x80 }; +- static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; +- static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 }; +- 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 int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) +-{ +- u32 div; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- if (buf_len < 5) +- return -EINVAL; +- +- div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; +- +- pllbuf[0] = 0x61; +- pllbuf[1] = (div >> 8) & 0x7F; +- pllbuf[2] = div & 0xFF; +- pllbuf[3] = 0x85; +- +- dprintk("frequency %u, div %u\n", c->frequency, div); +- +- if (c->frequency < 470000000) +- pllbuf[4] = 0x02; +- else if (c->frequency > 823000000) +- pllbuf[4] = 0x88; +- else +- pllbuf[4] = 0x08; +- +- if (c->bandwidth_hz == 8000000) +- pllbuf[4] |= 0x04; +- +- return 5; +-} +- +-static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) +-{ +- /* +- * Reset the frontend, must be called before trying +- * to initialise the MT352 or mt352_attach +- * will fail. Same goes for the nxt6000 frontend. +- * +- */ +- +- int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08); +- if (ret != 0) +- printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret); +- +- /* Pulse the reset line */ +- bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ +- bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */ +- msleep(100); +- +- bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ +-} +- +-static struct mt352_config digitv_alps_tded4_config = { +- .demod_address = 0x0a, +- .demod_init = digitv_alps_tded4_demod_init, +-}; +- +-static struct lgdt330x_config tdvs_tua6034_config = { +- .demod_address = 0x0e, +- .demod_chip = LGDT3303, +- .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ +-}; +- +-static void lgdt330x_reset(struct dvb_bt8xx_card *bt) +-{ +- /* Set pin 27 of the lgdt3303 chip high to reset the frontend */ +- +- /* Pulse the reset line */ +- bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ +- bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */ +- msleep(100); +- +- bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ +- msleep(100); +-} +- +-static void frontend_init(struct dvb_bt8xx_card *card, u32 type) +-{ +- struct dst_state* state = NULL; +- +- switch(type) { +- case BTTV_BOARD_DVICO_DVBT_LITE: +- card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter); +- +- if (card->fe == NULL) +- card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config, +- card->i2c_adapter); +- +- if (card->fe != NULL) { +- card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; +- card->fe->ops.info.frequency_min = 174000000; +- card->fe->ops.info.frequency_max = 862000000; +- } +- break; +- +- case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: +- lgdt330x_reset(card); +- card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); +- if (card->fe != NULL) { +- dvb_attach(simple_tuner_attach, card->fe, +- card->i2c_adapter, 0x61, +- TUNER_LG_TDVS_H06XF); +- dprintk ("dvb_bt8xx: lgdt330x detected\n"); +- } +- break; +- +- case BTTV_BOARD_NEBULA_DIGITV: +- /* +- * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK); +- * this would be a cleaner solution than trying each frontend in turn. +- */ +- +- /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */ +- digitv_alps_tded4_reset(card); +- card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter); +- if (card->fe != NULL) { +- card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; +- dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); +- break; +- } +- +- /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */ +- digitv_alps_tded4_reset(card); +- card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter); +- +- if (card->fe != NULL) { +- card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; +- dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); +- } +- break; +- +- case BTTV_BOARD_AVDVBT_761: +- card->fe = dvb_attach(sp887x_attach, µtune_mt7202dtf_config, card->i2c_adapter); +- if (card->fe) { +- card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; +- } +- break; +- +- case BTTV_BOARD_AVDVBT_771: +- card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); +- if (card->fe != NULL) { +- card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; +- card->fe->ops.info.frequency_min = 174000000; +- card->fe->ops.info.frequency_max = 862000000; +- } +- break; +- +- case BTTV_BOARD_TWINHAN_DST: +- /* DST is not a frontend driver !!! */ +- state = kmalloc(sizeof (struct dst_state), GFP_KERNEL); +- if (!state) { +- pr_err("No memory\n"); +- break; +- } +- /* Setup the Card */ +- state->config = &dst_config; +- state->i2c = card->i2c_adapter; +- state->bt = card->bt; +- state->dst_ca = NULL; +- /* DST is not a frontend, attaching the ASIC */ +- if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { +- pr_err("%s: Could not find a Twinhan DST\n", __func__); +- break; +- } +- /* Attach other DST peripherals if any */ +- /* Conditional Access device */ +- card->fe = &state->frontend; +- if (state->dst_hw_cap & DST_TYPE_HAS_CA) +- dvb_attach(dst_ca_attach, state, &card->dvb_adapter); +- break; +- +- case BTTV_BOARD_PINNACLESAT: +- card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter); +- if (card->fe) { +- card->fe->ops.tuner_ops.init = pinnsat_tuner_init; +- card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep; +- card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params; +- } +- break; +- +- case BTTV_BOARD_PC_HDTV: +- card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); +- if (card->fe != NULL) +- dvb_attach(simple_tuner_attach, card->fe, +- card->i2c_adapter, 0x61, +- TUNER_PHILIPS_FCV1236D); +- break; +- } +- +- if (card->fe == NULL) +- pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- card->bt->dev->vendor, +- card->bt->dev->device, +- card->bt->dev->subsystem_vendor, +- card->bt->dev->subsystem_device); +- else +- if (dvb_register_frontend(&card->dvb_adapter, card->fe)) { +- pr_err("Frontend registration failed!\n"); +- dvb_frontend_detach(card->fe); +- card->fe = NULL; +- } +-} +- +-static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) +-{ +- int result; +- +- result = dvb_register_adapter(&card->dvb_adapter, card->card_name, +- THIS_MODULE, &card->bt->dev->dev, +- adapter_nr); +- if (result < 0) { +- pr_err("dvb_register_adapter failed (errno = %d)\n", result); +- return result; +- } +- card->dvb_adapter.priv = card; +- +- card->bt->adapter = card->i2c_adapter; +- +- memset(&card->demux, 0, sizeof(struct dvb_demux)); +- +- card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; +- +- card->demux.priv = card; +- card->demux.filternum = 256; +- card->demux.feednum = 256; +- card->demux.start_feed = dvb_bt8xx_start_feed; +- card->demux.stop_feed = dvb_bt8xx_stop_feed; +- card->demux.write_to_decoder = NULL; +- +- result = dvb_dmx_init(&card->demux); +- if (result < 0) { +- pr_err("dvb_dmx_init failed (errno = %d)\n", result); +- goto err_unregister_adaptor; +- } +- +- card->dmxdev.filternum = 256; +- card->dmxdev.demux = &card->demux.dmx; +- card->dmxdev.capabilities = 0; +- +- result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter); +- if (result < 0) { +- pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); +- goto err_dmx_release; +- } +- +- card->fe_hw.source = DMX_FRONTEND_0; +- +- result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw); +- if (result < 0) { +- pr_err("dvb_dmx_init failed (errno = %d)\n", result); +- goto err_dmxdev_release; +- } +- +- card->fe_mem.source = DMX_MEMORY_FE; +- +- result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem); +- if (result < 0) { +- pr_err("dvb_dmx_init failed (errno = %d)\n", result); +- goto err_remove_hw_frontend; +- } +- +- result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw); +- if (result < 0) { +- pr_err("dvb_dmx_init failed (errno = %d)\n", result); +- goto err_remove_mem_frontend; +- } +- +- result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx); +- if (result < 0) { +- pr_err("dvb_net_init failed (errno = %d)\n", result); +- goto err_disconnect_frontend; +- } +- +- tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); +- +- frontend_init(card, type); +- +- return 0; +- +-err_disconnect_frontend: +- card->demux.dmx.disconnect_frontend(&card->demux.dmx); +-err_remove_mem_frontend: +- card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); +-err_remove_hw_frontend: +- card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); +-err_dmxdev_release: +- dvb_dmxdev_release(&card->dmxdev); +-err_dmx_release: +- dvb_dmx_release(&card->demux); +-err_unregister_adaptor: +- dvb_unregister_adapter(&card->dvb_adapter); +- return result; +-} +- +-static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub) +-{ +- struct dvb_bt8xx_card *card; +- struct pci_dev* bttv_pci_dev; +- int ret; +- +- if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) +- return -ENOMEM; +- +- mutex_init(&card->lock); +- card->bttv_nr = sub->core->nr; +- strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name)); +- card->i2c_adapter = &sub->core->i2c_adap; +- +- switch(sub->core->type) { +- case BTTV_BOARD_PINNACLESAT: +- card->gpio_mode = 0x0400c060; +- /* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR, +- BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */ +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- break; +- +- case BTTV_BOARD_DVICO_DVBT_LITE: +- card->gpio_mode = 0x0400C060; +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- /* 26, 15, 14, 6, 5 +- * A_PWRDN DA_DPM DA_SBR DA_IOM_DA +- * DA_APP(parallel) */ +- break; +- +- case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: +- card->gpio_mode = 0x0400c060; +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- break; +- +- case BTTV_BOARD_NEBULA_DIGITV: +- case BTTV_BOARD_AVDVBT_761: +- card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5); +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- /* A_PWRDN DA_SBR DA_APP (high speed serial) */ +- break; +- +- case BTTV_BOARD_AVDVBT_771: //case 0x07711461: +- card->gpio_mode = 0x0400402B; +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/ +- break; +- +- case BTTV_BOARD_TWINHAN_DST: +- card->gpio_mode = 0x2204f2c; +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR | +- BT878_APPERR | BT878_AFBUS; +- /* 25,21,14,11,10,9,8,3,2 then +- * 0x33 = 5,4,1,0 +- * A_SEL=SML, DA_MLB, DA_SBR, +- * DA_SDR=f, fifo trigger = 32 DWORDS +- * IOM = 0 == audio A/D +- * DPM = 0 == digital audio mode +- * == async data parallel port +- * then 0x33 (13 is set by start_capture) +- * DA_APP = async data parallel port, +- * ACAP_EN = 1, +- * RISC+FIFO ENABLE */ +- break; +- +- case BTTV_BOARD_PC_HDTV: +- card->gpio_mode = 0x0100EC7B; +- card->op_sync_orin = BT878_RISC_SYNC_MASK; +- card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; +- break; +- +- default: +- pr_err("Unknown bttv card type: %d\n", sub->core->type); +- kfree(card); +- return -ENODEV; +- } +- +- dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name); +- +- if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) { +- pr_err("no pci device for card %d\n", card->bttv_nr); +- kfree(card); +- return -ENODEV; +- } +- +- if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) { +- pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr); +- pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n"); +- +- kfree(card); +- return -ENODEV; +- } +- +- mutex_init(&card->bt->gpio_lock); +- card->bt->bttv_nr = sub->core->nr; +- +- if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) { +- kfree(card); +- return ret; +- } +- +- dev_set_drvdata(&sub->dev, card); +- return 0; +-} +- +-static void dvb_bt8xx_remove(struct bttv_sub_device *sub) +-{ +- struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev); +- +- dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); +- +- bt878_stop(card->bt); +- tasklet_kill(&card->bt->tasklet); +- dvb_net_release(&card->dvbnet); +- card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); +- card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); +- dvb_dmxdev_release(&card->dmxdev); +- dvb_dmx_release(&card->demux); +- if (card->fe) { +- dvb_unregister_frontend(card->fe); +- dvb_frontend_detach(card->fe); +- } +- dvb_unregister_adapter(&card->dvb_adapter); +- +- kfree(card); +-} +- +-static struct bttv_sub_driver driver = { +- .drv = { +- .name = "dvb-bt8xx", +- }, +- .probe = dvb_bt8xx_probe, +- .remove = dvb_bt8xx_remove, +- /* FIXME: +- * .shutdown = dvb_bt8xx_shutdown, +- * .suspend = dvb_bt8xx_suspend, +- * .resume = dvb_bt8xx_resume, +- */ +-}; +- +-static int __init dvb_bt8xx_init(void) +-{ +- return bttv_sub_register(&driver, "dvb"); +-} +- +-static void __exit dvb_bt8xx_exit(void) +-{ +- bttv_sub_unregister(&driver); +-} +- +-module_init(dvb_bt8xx_init); +-module_exit(dvb_bt8xx_exit); +- +-MODULE_DESCRIPTION("Bt8xx based DVB adapter driver"); +-MODULE_AUTHOR("Florian Schirmer "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h +deleted file mode 100644 +index 4499ed2..0000000 +--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h ++++ /dev/null +@@ -1,63 +0,0 @@ +-/* +- * Bt8xx based DVB adapter driver +- * +- * Copyright (C) 2002,2003 Florian Schirmer +- * Copyright (C) 2002 Peter Hettkamp +- * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef DVB_BT8XX_H +-#define DVB_BT8XX_H +- +-#include +-#include +-#include "dvbdev.h" +-#include "dvb_net.h" +-#include "bttv.h" +-#include "mt352.h" +-#include "sp887x.h" +-#include "dst_common.h" +-#include "nxt6000.h" +-#include "cx24110.h" +-#include "or51211.h" +-#include "lgdt330x.h" +-#include "zl10353.h" +-#include "tuner-simple.h" +- +-struct dvb_bt8xx_card { +- struct mutex lock; +- int nfeeds; +- char card_name[32]; +- struct dvb_adapter dvb_adapter; +- struct bt878 *bt; +- unsigned int bttv_nr; +- struct dvb_demux demux; +- struct dmxdev dmxdev; +- struct dmx_frontend fe_hw; +- struct dmx_frontend fe_mem; +- u32 gpio_mode; +- u32 op_sync_orin; +- u32 irq_err_ignore; +- struct i2c_adapter *i2c_adapter; +- struct dvb_net dvbnet; +- +- struct dvb_frontend* fe; +-}; +- +-#endif /* DVB_BT8XX_H */ +diff --git a/drivers/media/dvb/ddbridge/Kconfig b/drivers/media/dvb/ddbridge/Kconfig +deleted file mode 100644 +index d099e1a..0000000 +--- a/drivers/media/dvb/ddbridge/Kconfig ++++ /dev/null +@@ -1,18 +0,0 @@ +-config DVB_DDBRIDGE +- tristate "Digital Devices bridge support" +- depends on DVB_CORE && PCI && I2C +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_STV6110x if !DVB_FE_CUSTOMISE +- select DVB_STV090x if !DVB_FE_CUSTOMISE +- select DVB_DRXK if !DVB_FE_CUSTOMISE +- select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE +- ---help--- +- Support for cards with the Digital Devices PCI express bridge: +- - Octopus PCIe Bridge +- - Octopus mini PCIe Bridge +- - Octopus LE +- - DuoFlex S2 Octopus +- - DuoFlex CT Octopus +- - cineS2(v6) +- +- Say Y if you own such a card and want to use it. +diff --git a/drivers/media/dvb/ddbridge/Makefile b/drivers/media/dvb/ddbridge/Makefile +deleted file mode 100644 +index 38019ba..0000000 +--- a/drivers/media/dvb/ddbridge/Makefile ++++ /dev/null +@@ -1,14 +0,0 @@ +-# +-# Makefile for the ddbridge device driver +-# +- +-ddbridge-objs := ddbridge-core.o +- +-obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ +-ccflags-y += -Idrivers/media/dvb/frontends/ +-ccflags-y += -Idrivers/media/common/tuners/ +- +-# For the staging CI driver cxd2099 +-ccflags-y += -Idrivers/staging/media/cxd2099/ +diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c +deleted file mode 100644 +index ce4f858..0000000 +--- a/drivers/media/dvb/ddbridge/ddbridge-core.c ++++ /dev/null +@@ -1,1722 +0,0 @@ +-/* +- * ddbridge.c: Digital Devices PCIe bridge driver +- * +- * Copyright (C) 2010-2011 Digital Devices GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "ddbridge.h" +- +-#include "ddbridge-regs.h" +- +-#include "tda18271c2dd.h" +-#include "stv6110x.h" +-#include "stv090x.h" +-#include "lnbh24.h" +-#include "drxk.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-/* MSI had problems with lost interrupts, fixed but needs testing */ +-#undef CONFIG_PCI_MSI +- +-/******************************************************************************/ +- +-static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +-{ +- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1 } }; +- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +-} +- +-static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val) +-{ +- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, +- .buf = ®, .len = 1 }, +- {.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1 } }; +- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +-} +- +-static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, +- u16 reg, u8 *val) +-{ +- u8 msg[2] = {reg>>8, reg&0xff}; +- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, +- .buf = msg, .len = 2}, +- {.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1} }; +- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +-} +- +-static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) +-{ +- struct ddb *dev = i2c->dev; +- int stat; +- u32 val; +- +- i2c->done = 0; +- ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND); +- stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ); +- if (stat <= 0) { +- printk(KERN_ERR "I2C timeout\n"); +- { /* MSI debugging*/ +- u32 istat = ddbreadl(INTERRUPT_STATUS); +- printk(KERN_ERR "IRS %08x\n", istat); +- ddbwritel(istat, INTERRUPT_ACK); +- } +- return -EIO; +- } +- val = ddbreadl(i2c->regs+I2C_COMMAND); +- if (val & 0x70000) +- return -EIO; +- return 0; +-} +- +-static int ddb_i2c_master_xfer(struct i2c_adapter *adapter, +- struct i2c_msg msg[], int num) +-{ +- struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); +- struct ddb *dev = i2c->dev; +- u8 addr = 0; +- +- if (num) +- addr = msg[0].addr; +- +- if (num == 2 && msg[1].flags & I2C_M_RD && +- !(msg[0].flags & I2C_M_RD)) { +- memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf, +- msg[0].buf, msg[0].len); +- ddbwritel(msg[0].len|(msg[1].len << 16), +- i2c->regs+I2C_TASKLENGTH); +- if (!ddb_i2c_cmd(i2c, addr, 1)) { +- memcpy_fromio(msg[1].buf, +- dev->regs + I2C_TASKMEM_BASE + i2c->rbuf, +- msg[1].len); +- return num; +- } +- } +- +- if (num == 1 && !(msg[0].flags & I2C_M_RD)) { +- ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len); +- ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH); +- if (!ddb_i2c_cmd(i2c, addr, 2)) +- return num; +- } +- if (num == 1 && (msg[0].flags & I2C_M_RD)) { +- ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH); +- if (!ddb_i2c_cmd(i2c, addr, 3)) { +- ddbcpyfrom(msg[0].buf, +- I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len); +- return num; +- } +- } +- return -EIO; +-} +- +- +-static u32 ddb_i2c_functionality(struct i2c_adapter *adap) +-{ +- return I2C_FUNC_SMBUS_EMUL; +-} +- +-struct i2c_algorithm ddb_i2c_algo = { +- .master_xfer = ddb_i2c_master_xfer, +- .functionality = ddb_i2c_functionality, +-}; +- +-static void ddb_i2c_release(struct ddb *dev) +-{ +- int i; +- struct ddb_i2c *i2c; +- struct i2c_adapter *adap; +- +- for (i = 0; i < dev->info->port_num; i++) { +- i2c = &dev->i2c[i]; +- adap = &i2c->adap; +- i2c_del_adapter(adap); +- } +-} +- +-static int ddb_i2c_init(struct ddb *dev) +-{ +- int i, j, stat = 0; +- struct ddb_i2c *i2c; +- struct i2c_adapter *adap; +- +- for (i = 0; i < dev->info->port_num; i++) { +- i2c = &dev->i2c[i]; +- i2c->dev = dev; +- i2c->nr = i; +- i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4); +- i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8); +- i2c->regs = 0x80 + i * 0x20; +- ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING); +- ddbwritel((i2c->rbuf << 16) | i2c->wbuf, +- i2c->regs + I2C_TASKADDRESS); +- init_waitqueue_head(&i2c->wq); +- +- adap = &i2c->adap; +- i2c_set_adapdata(adap, i2c); +-#ifdef I2C_ADAP_CLASS_TV_DIGITAL +- adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG; +-#else +-#ifdef I2C_CLASS_TV_ANALOG +- adap->class = I2C_CLASS_TV_ANALOG; +-#endif +-#endif +- strcpy(adap->name, "ddbridge"); +- adap->algo = &ddb_i2c_algo; +- adap->algo_data = (void *)i2c; +- adap->dev.parent = &dev->pdev->dev; +- stat = i2c_add_adapter(adap); +- if (stat) +- break; +- } +- if (stat) +- for (j = 0; j < i; j++) { +- i2c = &dev->i2c[j]; +- adap = &i2c->adap; +- i2c_del_adapter(adap); +- } +- return stat; +-} +- +- +-/******************************************************************************/ +-/******************************************************************************/ +-/******************************************************************************/ +- +-#if 0 +-static void set_table(struct ddb *dev, u32 off, +- dma_addr_t *pbuf, u32 num) +-{ +- u32 i, base; +- u64 mem; +- +- base = DMA_BASE_ADDRESS_TABLE + off; +- for (i = 0; i < num; i++) { +- mem = pbuf[i]; +- ddbwritel(mem & 0xffffffff, base + i * 8); +- ddbwritel(mem >> 32, base + i * 8 + 4); +- } +-} +-#endif +- +-static void ddb_address_table(struct ddb *dev) +-{ +- u32 i, j, base; +- u64 mem; +- dma_addr_t *pbuf; +- +- for (i = 0; i < dev->info->port_num * 2; i++) { +- base = DMA_BASE_ADDRESS_TABLE + i * 0x100; +- pbuf = dev->input[i].pbuf; +- for (j = 0; j < dev->input[i].dma_buf_num; j++) { +- mem = pbuf[j]; +- ddbwritel(mem & 0xffffffff, base + j * 8); +- ddbwritel(mem >> 32, base + j * 8 + 4); +- } +- } +- for (i = 0; i < dev->info->port_num; i++) { +- base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100; +- pbuf = dev->output[i].pbuf; +- for (j = 0; j < dev->output[i].dma_buf_num; j++) { +- mem = pbuf[j]; +- ddbwritel(mem & 0xffffffff, base + j * 8); +- ddbwritel(mem >> 32, base + j * 8 + 4); +- } +- } +-} +- +-static void io_free(struct pci_dev *pdev, u8 **vbuf, +- dma_addr_t *pbuf, u32 size, int num) +-{ +- int i; +- +- for (i = 0; i < num; i++) { +- if (vbuf[i]) { +- pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); +- vbuf[i] = 0; +- } +- } +-} +- +-static int io_alloc(struct pci_dev *pdev, u8 **vbuf, +- dma_addr_t *pbuf, u32 size, int num) +-{ +- int i; +- +- for (i = 0; i < num; i++) { +- vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]); +- if (!vbuf[i]) +- return -ENOMEM; +- } +- return 0; +-} +- +-static int ddb_buffers_alloc(struct ddb *dev) +-{ +- int i; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- switch (port->class) { +- case DDB_PORT_TUNER: +- if (io_alloc(dev->pdev, port->input[0]->vbuf, +- port->input[0]->pbuf, +- port->input[0]->dma_buf_size, +- port->input[0]->dma_buf_num) < 0) +- return -1; +- if (io_alloc(dev->pdev, port->input[1]->vbuf, +- port->input[1]->pbuf, +- port->input[1]->dma_buf_size, +- port->input[1]->dma_buf_num) < 0) +- return -1; +- break; +- case DDB_PORT_CI: +- if (io_alloc(dev->pdev, port->input[0]->vbuf, +- port->input[0]->pbuf, +- port->input[0]->dma_buf_size, +- port->input[0]->dma_buf_num) < 0) +- return -1; +- if (io_alloc(dev->pdev, port->output->vbuf, +- port->output->pbuf, +- port->output->dma_buf_size, +- port->output->dma_buf_num) < 0) +- return -1; +- break; +- default: +- break; +- } +- } +- ddb_address_table(dev); +- return 0; +-} +- +-static void ddb_buffers_free(struct ddb *dev) +-{ +- int i; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- io_free(dev->pdev, port->input[0]->vbuf, +- port->input[0]->pbuf, +- port->input[0]->dma_buf_size, +- port->input[0]->dma_buf_num); +- io_free(dev->pdev, port->input[1]->vbuf, +- port->input[1]->pbuf, +- port->input[1]->dma_buf_size, +- port->input[1]->dma_buf_num); +- io_free(dev->pdev, port->output->vbuf, +- port->output->pbuf, +- port->output->dma_buf_size, +- port->output->dma_buf_num); +- } +-} +- +-static void ddb_input_start(struct ddb_input *input) +-{ +- struct ddb *dev = input->port->dev; +- +- spin_lock_irq(&input->lock); +- input->cbuf = 0; +- input->coff = 0; +- +- /* reset */ +- ddbwritel(0, TS_INPUT_CONTROL(input->nr)); +- ddbwritel(2, TS_INPUT_CONTROL(input->nr)); +- ddbwritel(0, TS_INPUT_CONTROL(input->nr)); +- +- ddbwritel((1 << 16) | +- (input->dma_buf_num << 11) | +- (input->dma_buf_size >> 7), +- DMA_BUFFER_SIZE(input->nr)); +- ddbwritel(0, DMA_BUFFER_ACK(input->nr)); +- +- ddbwritel(1, DMA_BASE_WRITE); +- ddbwritel(3, DMA_BUFFER_CONTROL(input->nr)); +- ddbwritel(9, TS_INPUT_CONTROL(input->nr)); +- input->running = 1; +- spin_unlock_irq(&input->lock); +-} +- +-static void ddb_input_stop(struct ddb_input *input) +-{ +- struct ddb *dev = input->port->dev; +- +- spin_lock_irq(&input->lock); +- ddbwritel(0, TS_INPUT_CONTROL(input->nr)); +- ddbwritel(0, DMA_BUFFER_CONTROL(input->nr)); +- input->running = 0; +- spin_unlock_irq(&input->lock); +-} +- +-static void ddb_output_start(struct ddb_output *output) +-{ +- struct ddb *dev = output->port->dev; +- +- spin_lock_irq(&output->lock); +- output->cbuf = 0; +- output->coff = 0; +- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); +- ddbwritel(2, TS_OUTPUT_CONTROL(output->nr)); +- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); +- ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr)); +- ddbwritel((1 << 16) | +- (output->dma_buf_num << 11) | +- (output->dma_buf_size >> 7), +- DMA_BUFFER_SIZE(output->nr + 8)); +- ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8)); +- +- ddbwritel(1, DMA_BASE_READ); +- ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8)); +- /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */ +- ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr)); +- output->running = 1; +- spin_unlock_irq(&output->lock); +-} +- +-static void ddb_output_stop(struct ddb_output *output) +-{ +- struct ddb *dev = output->port->dev; +- +- spin_lock_irq(&output->lock); +- ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); +- ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8)); +- output->running = 0; +- spin_unlock_irq(&output->lock); +-} +- +-static u32 ddb_output_free(struct ddb_output *output) +-{ +- u32 idx, off, stat = output->stat; +- s32 diff; +- +- idx = (stat >> 11) & 0x1f; +- off = (stat & 0x7ff) << 7; +- +- if (output->cbuf != idx) { +- if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && +- (output->dma_buf_size - output->coff <= 188)) +- return 0; +- return 188; +- } +- diff = off - output->coff; +- if (diff <= 0 || diff > 188) +- return 188; +- return 0; +-} +- +-static ssize_t ddb_output_write(struct ddb_output *output, +- const u8 *buf, size_t count) +-{ +- struct ddb *dev = output->port->dev; +- u32 idx, off, stat = output->stat; +- u32 left = count, len; +- +- idx = (stat >> 11) & 0x1f; +- off = (stat & 0x7ff) << 7; +- +- while (left) { +- len = output->dma_buf_size - output->coff; +- if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && +- (off == 0)) { +- if (len <= 188) +- break; +- len -= 188; +- } +- if (output->cbuf == idx) { +- if (off > output->coff) { +-#if 1 +- len = off - output->coff; +- len -= (len % 188); +- if (len <= 188) +- +-#endif +- break; +- len -= 188; +- } +- } +- if (len > left) +- len = left; +- if (copy_from_user(output->vbuf[output->cbuf] + output->coff, +- buf, len)) +- return -EIO; +- left -= len; +- buf += len; +- output->coff += len; +- if (output->coff == output->dma_buf_size) { +- output->coff = 0; +- output->cbuf = ((output->cbuf + 1) % output->dma_buf_num); +- } +- ddbwritel((output->cbuf << 11) | (output->coff >> 7), +- DMA_BUFFER_ACK(output->nr + 8)); +- } +- return count - left; +-} +- +-static u32 ddb_input_avail(struct ddb_input *input) +-{ +- struct ddb *dev = input->port->dev; +- u32 idx, off, stat = input->stat; +- u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr)); +- +- idx = (stat >> 11) & 0x1f; +- off = (stat & 0x7ff) << 7; +- +- if (ctrl & 4) { +- printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl); +- ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr)); +- return 0; +- } +- if (input->cbuf != idx) +- return 188; +- return 0; +-} +- +-static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) +-{ +- struct ddb *dev = input->port->dev; +- u32 left = count; +- u32 idx, free, stat = input->stat; +- int ret; +- +- idx = (stat >> 11) & 0x1f; +- +- while (left) { +- if (input->cbuf == idx) +- return count - left; +- free = input->dma_buf_size - input->coff; +- if (free > left) +- free = left; +- ret = copy_to_user(buf, input->vbuf[input->cbuf] + +- input->coff, free); +- if (ret) +- return -EFAULT; +- input->coff += free; +- if (input->coff == input->dma_buf_size) { +- input->coff = 0; +- input->cbuf = (input->cbuf+1) % input->dma_buf_num; +- } +- left -= free; +- ddbwritel((input->cbuf << 11) | (input->coff >> 7), +- DMA_BUFFER_ACK(input->nr)); +- } +- return count; +-} +- +-/******************************************************************************/ +-/******************************************************************************/ +-/******************************************************************************/ +- +-#if 0 +-static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe) +-{ +- int i; +- +- for (i = 0; i < dev->info->port_num * 2; i++) { +- if (dev->input[i].fe == fe) +- return &dev->input[i]; +- } +- return NULL; +-} +-#endif +- +-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct ddb_input *input = fe->sec_priv; +- struct ddb_port *port = input->port; +- int status; +- +- if (enable) { +- mutex_lock(&port->i2c_gate_lock); +- status = input->gate_ctrl(fe, 1); +- } else { +- status = input->gate_ctrl(fe, 0); +- mutex_unlock(&port->i2c_gate_lock); +- } +- return status; +-} +- +-static int demod_attach_drxk(struct ddb_input *input) +-{ +- struct i2c_adapter *i2c = &input->port->i2c->adap; +- struct dvb_frontend *fe; +- struct drxk_config config; +- +- memset(&config, 0, sizeof(config)); +- config.adr = 0x29 + (input->nr & 1); +- +- fe = input->fe = dvb_attach(drxk_attach, &config, i2c); +- if (!input->fe) { +- printk(KERN_ERR "No DRXK found!\n"); +- return -ENODEV; +- } +- fe->sec_priv = input; +- input->gate_ctrl = fe->ops.i2c_gate_ctrl; +- fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; +- return 0; +-} +- +-static int tuner_attach_tda18271(struct ddb_input *input) +-{ +- struct i2c_adapter *i2c = &input->port->i2c->adap; +- struct dvb_frontend *fe; +- +- if (input->fe->ops.i2c_gate_ctrl) +- input->fe->ops.i2c_gate_ctrl(input->fe, 1); +- fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60); +- if (!fe) { +- printk(KERN_ERR "No TDA18271 found!\n"); +- return -ENODEV; +- } +- if (input->fe->ops.i2c_gate_ctrl) +- input->fe->ops.i2c_gate_ctrl(input->fe, 0); +- return 0; +-} +- +-/******************************************************************************/ +-/******************************************************************************/ +-/******************************************************************************/ +- +-static struct stv090x_config stv0900 = { +- .device = STV0900, +- .demod_mode = STV090x_DUAL, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 27000000, +- .address = 0x69, +- +- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- +- .repeater_level = STV090x_RPTLEVEL_16, +- +- .adc1_range = STV090x_ADC_1Vpp, +- .adc2_range = STV090x_ADC_1Vpp, +- +- .diseqc_envelope_mode = true, +-}; +- +-static struct stv090x_config stv0900_aa = { +- .device = STV0900, +- .demod_mode = STV090x_DUAL, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 27000000, +- .address = 0x68, +- +- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- +- .repeater_level = STV090x_RPTLEVEL_16, +- +- .adc1_range = STV090x_ADC_1Vpp, +- .adc2_range = STV090x_ADC_1Vpp, +- +- .diseqc_envelope_mode = true, +-}; +- +-static struct stv6110x_config stv6110a = { +- .addr = 0x60, +- .refclk = 27000000, +- .clk_div = 1, +-}; +- +-static struct stv6110x_config stv6110b = { +- .addr = 0x63, +- .refclk = 27000000, +- .clk_div = 1, +-}; +- +-static int demod_attach_stv0900(struct ddb_input *input, int type) +-{ +- struct i2c_adapter *i2c = &input->port->i2c->adap; +- struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; +- +- input->fe = dvb_attach(stv090x_attach, feconf, i2c, +- (input->nr & 1) ? STV090x_DEMODULATOR_1 +- : STV090x_DEMODULATOR_0); +- if (!input->fe) { +- printk(KERN_ERR "No STV0900 found!\n"); +- return -ENODEV; +- } +- if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0, +- 0, (input->nr & 1) ? +- (0x09 - type) : (0x0b - type))) { +- printk(KERN_ERR "No LNBH24 found!\n"); +- return -ENODEV; +- } +- return 0; +-} +- +-static int tuner_attach_stv6110(struct ddb_input *input, int type) +-{ +- struct i2c_adapter *i2c = &input->port->i2c->adap; +- struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; +- struct stv6110x_config *tunerconf = (input->nr & 1) ? +- &stv6110b : &stv6110a; +- struct stv6110x_devctl *ctl; +- +- ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c); +- if (!ctl) { +- printk(KERN_ERR "No STV6110X found!\n"); +- return -ENODEV; +- } +- printk(KERN_INFO "attach tuner input %d adr %02x\n", +- input->nr, tunerconf->addr); +- +- feconf->tuner_init = ctl->tuner_init; +- feconf->tuner_sleep = ctl->tuner_sleep; +- feconf->tuner_set_mode = ctl->tuner_set_mode; +- feconf->tuner_set_frequency = ctl->tuner_set_frequency; +- feconf->tuner_get_frequency = ctl->tuner_get_frequency; +- feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; +- feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; +- feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; +- feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; +- feconf->tuner_set_refclk = ctl->tuner_set_refclk; +- feconf->tuner_get_status = ctl->tuner_get_status; +- +- return 0; +-} +- +-static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, +- int (*start_feed)(struct dvb_demux_feed *), +- int (*stop_feed)(struct dvb_demux_feed *), +- void *priv) +-{ +- dvbdemux->priv = priv; +- +- dvbdemux->filternum = 256; +- dvbdemux->feednum = 256; +- dvbdemux->start_feed = start_feed; +- dvbdemux->stop_feed = stop_feed; +- dvbdemux->write_to_decoder = NULL; +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | +- DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING); +- return dvb_dmx_init(dvbdemux); +-} +- +-static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, +- struct dvb_demux *dvbdemux, +- struct dmx_frontend *hw_frontend, +- struct dmx_frontend *mem_frontend, +- struct dvb_adapter *dvb_adapter) +-{ +- int ret; +- +- dmxdev->filternum = 256; +- dmxdev->demux = &dvbdemux->dmx; +- dmxdev->capabilities = 0; +- ret = dvb_dmxdev_init(dmxdev, dvb_adapter); +- if (ret < 0) +- return ret; +- +- hw_frontend->source = DMX_FRONTEND_0; +- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); +- mem_frontend->source = DMX_MEMORY_FE; +- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); +- return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +-} +- +-static int start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct ddb_input *input = dvbdmx->priv; +- +- if (!input->users) +- ddb_input_start(input); +- +- return ++input->users; +-} +- +-static int stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct ddb_input *input = dvbdmx->priv; +- +- if (--input->users) +- return input->users; +- +- ddb_input_stop(input); +- return 0; +-} +- +- +-static void dvb_input_detach(struct ddb_input *input) +-{ +- struct dvb_adapter *adap = &input->adap; +- struct dvb_demux *dvbdemux = &input->demux; +- +- switch (input->attached) { +- case 5: +- if (input->fe2) +- dvb_unregister_frontend(input->fe2); +- if (input->fe) { +- dvb_unregister_frontend(input->fe); +- dvb_frontend_detach(input->fe); +- input->fe = NULL; +- } +- case 4: +- dvb_net_release(&input->dvbnet); +- +- case 3: +- dvbdemux->dmx.close(&dvbdemux->dmx); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, +- &input->hw_frontend); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, +- &input->mem_frontend); +- dvb_dmxdev_release(&input->dmxdev); +- +- case 2: +- dvb_dmx_release(&input->demux); +- +- case 1: +- dvb_unregister_adapter(adap); +- } +- input->attached = 0; +-} +- +-static int dvb_input_attach(struct ddb_input *input) +-{ +- int ret; +- struct ddb_port *port = input->port; +- struct dvb_adapter *adap = &input->adap; +- struct dvb_demux *dvbdemux = &input->demux; +- +- ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE, +- &input->port->dev->pdev->dev, +- adapter_nr); +- if (ret < 0) { +- printk(KERN_ERR "ddbridge: Could not register adapter." +- "Check if you enabled enough adapters in dvb-core!\n"); +- return ret; +- } +- input->attached = 1; +- +- ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", +- start_feed, +- stop_feed, input); +- if (ret < 0) +- return ret; +- input->attached = 2; +- +- ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux, +- &input->hw_frontend, +- &input->mem_frontend, adap); +- if (ret < 0) +- return ret; +- input->attached = 3; +- +- ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux); +- if (ret < 0) +- return ret; +- input->attached = 4; +- +- input->fe = 0; +- switch (port->type) { +- case DDB_TUNER_DVBS_ST: +- if (demod_attach_stv0900(input, 0) < 0) +- return -ENODEV; +- if (tuner_attach_stv6110(input, 0) < 0) +- return -ENODEV; +- if (input->fe) { +- if (dvb_register_frontend(adap, input->fe) < 0) +- return -ENODEV; +- } +- break; +- case DDB_TUNER_DVBS_ST_AA: +- if (demod_attach_stv0900(input, 1) < 0) +- return -ENODEV; +- if (tuner_attach_stv6110(input, 1) < 0) +- return -ENODEV; +- if (input->fe) { +- if (dvb_register_frontend(adap, input->fe) < 0) +- return -ENODEV; +- } +- break; +- case DDB_TUNER_DVBCT_TR: +- if (demod_attach_drxk(input) < 0) +- return -ENODEV; +- if (tuner_attach_tda18271(input) < 0) +- return -ENODEV; +- if (input->fe) { +- if (dvb_register_frontend(adap, input->fe) < 0) +- return -ENODEV; +- } +- if (input->fe2) { +- if (dvb_register_frontend(adap, input->fe2) < 0) +- return -ENODEV; +- input->fe2->tuner_priv = input->fe->tuner_priv; +- memcpy(&input->fe2->ops.tuner_ops, +- &input->fe->ops.tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- } +- break; +- } +- input->attached = 5; +- return 0; +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +- +-static ssize_t ts_write(struct file *file, const char *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct ddb_output *output = dvbdev->priv; +- size_t left = count; +- int stat; +- +- while (left) { +- if (ddb_output_free(output) < 188) { +- if (file->f_flags & O_NONBLOCK) +- break; +- if (wait_event_interruptible( +- output->wq, ddb_output_free(output) >= 188) < 0) +- break; +- } +- stat = ddb_output_write(output, buf, left); +- if (stat < 0) +- break; +- buf += stat; +- left -= stat; +- } +- return (left == count) ? -EAGAIN : (count - left); +-} +- +-static ssize_t ts_read(struct file *file, char *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct ddb_output *output = dvbdev->priv; +- struct ddb_input *input = output->port->input[0]; +- int left, read; +- +- count -= count % 188; +- left = count; +- while (left) { +- if (ddb_input_avail(input) < 188) { +- if (file->f_flags & O_NONBLOCK) +- break; +- if (wait_event_interruptible( +- input->wq, ddb_input_avail(input) >= 188) < 0) +- break; +- } +- read = ddb_input_read(input, buf, left); +- if (read < 0) +- return read; +- left -= read; +- buf += read; +- } +- return (left == count) ? -EAGAIN : (count - left); +-} +- +-static unsigned int ts_poll(struct file *file, poll_table *wait) +-{ +- /* +- struct dvb_device *dvbdev = file->private_data; +- struct ddb_output *output = dvbdev->priv; +- struct ddb_input *input = output->port->input[0]; +- */ +- unsigned int mask = 0; +- +-#if 0 +- if (data_avail_to_read) +- mask |= POLLIN | POLLRDNORM; +- if (data_avail_to_write) +- mask |= POLLOUT | POLLWRNORM; +- +- poll_wait(file, &read_queue, wait); +- poll_wait(file, &write_queue, wait); +-#endif +- return mask; +-} +- +-static const struct file_operations ci_fops = { +- .owner = THIS_MODULE, +- .read = ts_read, +- .write = ts_write, +- .open = dvb_generic_open, +- .release = dvb_generic_release, +- .poll = ts_poll, +- .mmap = 0, +-}; +- +-static struct dvb_device dvbdev_ci = { +- .priv = 0, +- .readers = -1, +- .writers = -1, +- .users = -1, +- .fops = &ci_fops, +-}; +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static void input_tasklet(unsigned long data) +-{ +- struct ddb_input *input = (struct ddb_input *) data; +- struct ddb *dev = input->port->dev; +- +- spin_lock(&input->lock); +- if (!input->running) { +- spin_unlock(&input->lock); +- return; +- } +- input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); +- +- if (input->port->class == DDB_PORT_TUNER) { +- if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr))) +- printk(KERN_ERR "Overflow input %d\n", input->nr); +- while (input->cbuf != ((input->stat >> 11) & 0x1f) +- || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) { +- dvb_dmx_swfilter_packets(&input->demux, +- input->vbuf[input->cbuf], +- input->dma_buf_size / 188); +- +- input->cbuf = (input->cbuf + 1) % input->dma_buf_num; +- ddbwritel((input->cbuf << 11), +- DMA_BUFFER_ACK(input->nr)); +- input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); +- } +- } +- if (input->port->class == DDB_PORT_CI) +- wake_up(&input->wq); +- spin_unlock(&input->lock); +-} +- +-static void output_tasklet(unsigned long data) +-{ +- struct ddb_output *output = (struct ddb_output *) data; +- struct ddb *dev = output->port->dev; +- +- spin_lock(&output->lock); +- if (!output->running) { +- spin_unlock(&output->lock); +- return; +- } +- output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8)); +- wake_up(&output->wq); +- spin_unlock(&output->lock); +-} +- +- +-struct cxd2099_cfg cxd_cfg = { +- .bitrate = 62000, +- .adr = 0x40, +- .polarity = 1, +- .clock_mode = 1, +-}; +- +-static int ddb_ci_attach(struct ddb_port *port) +-{ +- int ret; +- +- ret = dvb_register_adapter(&port->output->adap, +- "DDBridge", +- THIS_MODULE, +- &port->dev->pdev->dev, +- adapter_nr); +- if (ret < 0) +- return ret; +- port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap); +- if (!port->en) { +- dvb_unregister_adapter(&port->output->adap); +- return -ENODEV; +- } +- ddb_input_start(port->input[0]); +- ddb_output_start(port->output); +- dvb_ca_en50221_init(&port->output->adap, +- port->en, 0, 1); +- ret = dvb_register_device(&port->output->adap, &port->output->dev, +- &dvbdev_ci, (void *) port->output, +- DVB_DEVICE_SEC); +- return ret; +-} +- +-static int ddb_port_attach(struct ddb_port *port) +-{ +- int ret = 0; +- +- switch (port->class) { +- case DDB_PORT_TUNER: +- ret = dvb_input_attach(port->input[0]); +- if (ret < 0) +- break; +- ret = dvb_input_attach(port->input[1]); +- break; +- case DDB_PORT_CI: +- ret = ddb_ci_attach(port); +- break; +- default: +- break; +- } +- if (ret < 0) +- printk(KERN_ERR "port_attach on port %d failed\n", port->nr); +- return ret; +-} +- +-static int ddb_ports_attach(struct ddb *dev) +-{ +- int i, ret = 0; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- ret = ddb_port_attach(port); +- if (ret < 0) +- break; +- } +- return ret; +-} +- +-static void ddb_ports_detach(struct ddb *dev) +-{ +- int i; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- switch (port->class) { +- case DDB_PORT_TUNER: +- dvb_input_detach(port->input[0]); +- dvb_input_detach(port->input[1]); +- break; +- case DDB_PORT_CI: +- if (port->output->dev) +- dvb_unregister_device(port->output->dev); +- if (port->en) { +- ddb_input_stop(port->input[0]); +- ddb_output_stop(port->output); +- dvb_ca_en50221_release(port->en); +- kfree(port->en); +- port->en = 0; +- dvb_unregister_adapter(&port->output->adap); +- } +- break; +- } +- } +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +- +-static int port_has_ci(struct ddb_port *port) +-{ +- u8 val; +- return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1; +-} +- +-static int port_has_stv0900(struct ddb_port *port) +-{ +- u8 val; +- if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0) +- return 0; +- return 1; +-} +- +-static int port_has_stv0900_aa(struct ddb_port *port) +-{ +- u8 val; +- if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0) +- return 0; +- return 1; +-} +- +-static int port_has_drxks(struct ddb_port *port) +-{ +- u8 val; +- if (i2c_read(&port->i2c->adap, 0x29, &val) < 0) +- return 0; +- if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0) +- return 0; +- return 1; +-} +- +-static void ddb_port_probe(struct ddb_port *port) +-{ +- struct ddb *dev = port->dev; +- char *modname = "NO MODULE"; +- +- port->class = DDB_PORT_NONE; +- +- if (port_has_ci(port)) { +- modname = "CI"; +- port->class = DDB_PORT_CI; +- ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); +- } else if (port_has_stv0900(port)) { +- modname = "DUAL DVB-S2"; +- port->class = DDB_PORT_TUNER; +- port->type = DDB_TUNER_DVBS_ST; +- ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); +- } else if (port_has_stv0900_aa(port)) { +- modname = "DUAL DVB-S2"; +- port->class = DDB_PORT_TUNER; +- port->type = DDB_TUNER_DVBS_ST_AA; +- ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); +- } else if (port_has_drxks(port)) { +- modname = "DUAL DVB-C/T"; +- port->class = DDB_PORT_TUNER; +- port->type = DDB_TUNER_DVBCT_TR; +- ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); +- } +- printk(KERN_INFO "Port %d (TAB %d): %s\n", +- port->nr, port->nr+1, modname); +-} +- +-static void ddb_input_init(struct ddb_port *port, int nr) +-{ +- struct ddb *dev = port->dev; +- struct ddb_input *input = &dev->input[nr]; +- +- input->nr = nr; +- input->port = port; +- input->dma_buf_num = INPUT_DMA_BUFS; +- input->dma_buf_size = INPUT_DMA_SIZE; +- ddbwritel(0, TS_INPUT_CONTROL(nr)); +- ddbwritel(2, TS_INPUT_CONTROL(nr)); +- ddbwritel(0, TS_INPUT_CONTROL(nr)); +- ddbwritel(0, DMA_BUFFER_ACK(nr)); +- tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input); +- spin_lock_init(&input->lock); +- init_waitqueue_head(&input->wq); +-} +- +-static void ddb_output_init(struct ddb_port *port, int nr) +-{ +- struct ddb *dev = port->dev; +- struct ddb_output *output = &dev->output[nr]; +- output->nr = nr; +- output->port = port; +- output->dma_buf_num = OUTPUT_DMA_BUFS; +- output->dma_buf_size = OUTPUT_DMA_SIZE; +- +- ddbwritel(0, TS_OUTPUT_CONTROL(nr)); +- ddbwritel(2, TS_OUTPUT_CONTROL(nr)); +- ddbwritel(0, TS_OUTPUT_CONTROL(nr)); +- tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output); +- init_waitqueue_head(&output->wq); +-} +- +-static void ddb_ports_init(struct ddb *dev) +-{ +- int i; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- port->dev = dev; +- port->nr = i; +- port->i2c = &dev->i2c[i]; +- port->input[0] = &dev->input[2 * i]; +- port->input[1] = &dev->input[2 * i + 1]; +- port->output = &dev->output[i]; +- +- mutex_init(&port->i2c_gate_lock); +- ddb_port_probe(port); +- ddb_input_init(port, 2 * i); +- ddb_input_init(port, 2 * i + 1); +- ddb_output_init(port, i); +- } +-} +- +-static void ddb_ports_release(struct ddb *dev) +-{ +- int i; +- struct ddb_port *port; +- +- for (i = 0; i < dev->info->port_num; i++) { +- port = &dev->port[i]; +- port->dev = dev; +- tasklet_kill(&port->input[0]->tasklet); +- tasklet_kill(&port->input[1]->tasklet); +- tasklet_kill(&port->output->tasklet); +- } +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static void irq_handle_i2c(struct ddb *dev, int n) +-{ +- struct ddb_i2c *i2c = &dev->i2c[n]; +- +- i2c->done = 1; +- wake_up(&i2c->wq); +-} +- +-static irqreturn_t irq_handler(int irq, void *dev_id) +-{ +- struct ddb *dev = (struct ddb *) dev_id; +- u32 s = ddbreadl(INTERRUPT_STATUS); +- +- if (!s) +- return IRQ_NONE; +- +- do { +- ddbwritel(s, INTERRUPT_ACK); +- +- if (s & 0x00000001) +- irq_handle_i2c(dev, 0); +- if (s & 0x00000002) +- irq_handle_i2c(dev, 1); +- if (s & 0x00000004) +- irq_handle_i2c(dev, 2); +- if (s & 0x00000008) +- irq_handle_i2c(dev, 3); +- +- if (s & 0x00000100) +- tasklet_schedule(&dev->input[0].tasklet); +- if (s & 0x00000200) +- tasklet_schedule(&dev->input[1].tasklet); +- if (s & 0x00000400) +- tasklet_schedule(&dev->input[2].tasklet); +- if (s & 0x00000800) +- tasklet_schedule(&dev->input[3].tasklet); +- if (s & 0x00001000) +- tasklet_schedule(&dev->input[4].tasklet); +- if (s & 0x00002000) +- tasklet_schedule(&dev->input[5].tasklet); +- if (s & 0x00004000) +- tasklet_schedule(&dev->input[6].tasklet); +- if (s & 0x00008000) +- tasklet_schedule(&dev->input[7].tasklet); +- +- if (s & 0x00010000) +- tasklet_schedule(&dev->output[0].tasklet); +- if (s & 0x00020000) +- tasklet_schedule(&dev->output[1].tasklet); +- if (s & 0x00040000) +- tasklet_schedule(&dev->output[2].tasklet); +- if (s & 0x00080000) +- tasklet_schedule(&dev->output[3].tasklet); +- +- /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */ +- } while ((s = ddbreadl(INTERRUPT_STATUS))); +- +- return IRQ_HANDLED; +-} +- +-/******************************************************************************/ +-/******************************************************************************/ +-/******************************************************************************/ +- +-static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) +-{ +- u32 data, shift; +- +- if (wlen > 4) +- ddbwritel(1, SPI_CONTROL); +- while (wlen > 4) { +- /* FIXME: check for big-endian */ +- data = swab32(*(u32 *)wbuf); +- wbuf += 4; +- wlen -= 4; +- ddbwritel(data, SPI_DATA); +- while (ddbreadl(SPI_CONTROL) & 0x0004) +- ; +- } +- +- if (rlen) +- ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); +- else +- ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); +- +- data = 0; +- shift = ((4 - wlen) * 8); +- while (wlen) { +- data <<= 8; +- data |= *wbuf; +- wlen--; +- wbuf++; +- } +- if (shift) +- data <<= shift; +- ddbwritel(data, SPI_DATA); +- while (ddbreadl(SPI_CONTROL) & 0x0004) +- ; +- +- if (!rlen) { +- ddbwritel(0, SPI_CONTROL); +- return 0; +- } +- if (rlen > 4) +- ddbwritel(1, SPI_CONTROL); +- +- while (rlen > 4) { +- ddbwritel(0xffffffff, SPI_DATA); +- while (ddbreadl(SPI_CONTROL) & 0x0004) +- ; +- data = ddbreadl(SPI_DATA); +- *(u32 *) rbuf = swab32(data); +- rbuf += 4; +- rlen -= 4; +- } +- ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL); +- ddbwritel(0xffffffff, SPI_DATA); +- while (ddbreadl(SPI_CONTROL) & 0x0004) +- ; +- +- data = ddbreadl(SPI_DATA); +- ddbwritel(0, SPI_CONTROL); +- +- if (rlen < 4) +- data <<= ((4 - rlen) * 8); +- +- while (rlen > 0) { +- *rbuf = ((data >> 24) & 0xff); +- data <<= 8; +- rbuf++; +- rlen--; +- } +- return 0; +-} +- +-#define DDB_MAGIC 'd' +- +-struct ddb_flashio { +- __u8 *write_buf; +- __u32 write_len; +- __u8 *read_buf; +- __u32 read_len; +-}; +- +-#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio) +- +-#define DDB_NAME "ddbridge" +- +-static u32 ddb_num; +-static struct ddb *ddbs[32]; +-static struct class *ddb_class; +-static int ddb_major; +- +-static int ddb_open(struct inode *inode, struct file *file) +-{ +- struct ddb *dev = ddbs[iminor(inode)]; +- +- file->private_data = dev; +- return 0; +-} +- +-static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +-{ +- struct ddb *dev = file->private_data; +- void *parg = (void *)arg; +- int res; +- +- switch (cmd) { +- case IOCTL_DDB_FLASHIO: +- { +- struct ddb_flashio fio; +- u8 *rbuf, *wbuf; +- +- if (copy_from_user(&fio, parg, sizeof(fio))) +- return -EFAULT; +- +- if (fio.write_len > 1028 || fio.read_len > 1028) +- return -EINVAL; +- if (fio.write_len + fio.read_len > 1028) +- return -EINVAL; +- +- wbuf = &dev->iobuf[0]; +- rbuf = wbuf + fio.write_len; +- +- if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) +- return -EFAULT; +- res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len); +- if (res) +- return res; +- if (copy_to_user(fio.read_buf, rbuf, fio.read_len)) +- return -EFAULT; +- break; +- } +- default: +- return -ENOTTY; +- } +- return 0; +-} +- +-static const struct file_operations ddb_fops = { +- .unlocked_ioctl = ddb_ioctl, +- .open = ddb_open, +-}; +- +-static char *ddb_devnode(struct device *device, umode_t *mode) +-{ +- struct ddb *dev = dev_get_drvdata(device); +- +- return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr); +-} +- +-static int ddb_class_create(void) +-{ +- ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops); +- if (ddb_major < 0) +- return ddb_major; +- +- ddb_class = class_create(THIS_MODULE, DDB_NAME); +- if (IS_ERR(ddb_class)) { +- unregister_chrdev(ddb_major, DDB_NAME); +- return -1; +- } +- ddb_class->devnode = ddb_devnode; +- return 0; +-} +- +-static void ddb_class_destroy(void) +-{ +- class_destroy(ddb_class); +- unregister_chrdev(ddb_major, DDB_NAME); +-} +- +-static int ddb_device_create(struct ddb *dev) +-{ +- dev->nr = ddb_num++; +- dev->ddb_dev = device_create(ddb_class, NULL, +- MKDEV(ddb_major, dev->nr), +- dev, "ddbridge%d", dev->nr); +- ddbs[dev->nr] = dev; +- if (IS_ERR(dev->ddb_dev)) +- return -1; +- return 0; +-} +- +-static void ddb_device_destroy(struct ddb *dev) +-{ +- ddb_num--; +- if (IS_ERR(dev->ddb_dev)) +- return; +- device_destroy(ddb_class, MKDEV(ddb_major, 0)); +-} +- +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static void ddb_unmap(struct ddb *dev) +-{ +- if (dev->regs) +- iounmap(dev->regs); +- vfree(dev); +-} +- +- +-static void __devexit ddb_remove(struct pci_dev *pdev) +-{ +- struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev); +- +- ddb_ports_detach(dev); +- ddb_i2c_release(dev); +- +- ddbwritel(0, INTERRUPT_ENABLE); +- free_irq(dev->pdev->irq, dev); +-#ifdef CONFIG_PCI_MSI +- if (dev->msi) +- pci_disable_msi(dev->pdev); +-#endif +- ddb_ports_release(dev); +- ddb_buffers_free(dev); +- ddb_device_destroy(dev); +- +- ddb_unmap(dev); +- pci_set_drvdata(pdev, 0); +- pci_disable_device(pdev); +-} +- +- +-static int __devinit ddb_probe(struct pci_dev *pdev, +- const struct pci_device_id *id) +-{ +- struct ddb *dev; +- int stat = 0; +- int irq_flag = IRQF_SHARED; +- +- if (pci_enable_device(pdev) < 0) +- return -ENODEV; +- +- dev = vmalloc(sizeof(struct ddb)); +- if (dev == NULL) +- return -ENOMEM; +- memset(dev, 0, sizeof(struct ddb)); +- +- dev->pdev = pdev; +- pci_set_drvdata(pdev, dev); +- dev->info = (struct ddb_info *) id->driver_data; +- printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name); +- +- dev->regs = ioremap(pci_resource_start(dev->pdev, 0), +- pci_resource_len(dev->pdev, 0)); +- if (!dev->regs) { +- stat = -ENOMEM; +- goto fail; +- } +- printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); +- +-#ifdef CONFIG_PCI_MSI +- if (pci_msi_enabled()) +- stat = pci_enable_msi(dev->pdev); +- if (stat) { +- printk(KERN_INFO ": MSI not available.\n"); +- } else { +- irq_flag = 0; +- dev->msi = 1; +- } +-#endif +- stat = request_irq(dev->pdev->irq, irq_handler, +- irq_flag, "DDBridge", (void *) dev); +- if (stat < 0) +- goto fail1; +- ddbwritel(0, DMA_BASE_WRITE); +- ddbwritel(0, DMA_BASE_READ); +- ddbwritel(0xffffffff, INTERRUPT_ACK); +- ddbwritel(0xfff0f, INTERRUPT_ENABLE); +- ddbwritel(0, MSI1_ENABLE); +- +- if (ddb_i2c_init(dev) < 0) +- goto fail1; +- ddb_ports_init(dev); +- if (ddb_buffers_alloc(dev) < 0) { +- printk(KERN_INFO ": Could not allocate buffer memory\n"); +- goto fail2; +- } +- if (ddb_ports_attach(dev) < 0) +- goto fail3; +- ddb_device_create(dev); +- return 0; +- +-fail3: +- ddb_ports_detach(dev); +- printk(KERN_ERR "fail3\n"); +- ddb_ports_release(dev); +-fail2: +- printk(KERN_ERR "fail2\n"); +- ddb_buffers_free(dev); +-fail1: +- printk(KERN_ERR "fail1\n"); +- if (dev->msi) +- pci_disable_msi(dev->pdev); +- free_irq(dev->pdev->irq, dev); +-fail: +- printk(KERN_ERR "fail\n"); +- ddb_unmap(dev); +- pci_set_drvdata(pdev, 0); +- pci_disable_device(pdev); +- return -1; +-} +- +-/******************************************************************************/ +-/******************************************************************************/ +-/******************************************************************************/ +- +-static struct ddb_info ddb_none = { +- .type = DDB_NONE, +- .name = "Digital Devices PCIe bridge", +-}; +- +-static struct ddb_info ddb_octopus = { +- .type = DDB_OCTOPUS, +- .name = "Digital Devices Octopus DVB adapter", +- .port_num = 4, +-}; +- +-static struct ddb_info ddb_octopus_le = { +- .type = DDB_OCTOPUS, +- .name = "Digital Devices Octopus LE DVB adapter", +- .port_num = 2, +-}; +- +-static struct ddb_info ddb_v6 = { +- .type = DDB_OCTOPUS, +- .name = "Digital Devices Cine S2 V6 DVB adapter", +- .port_num = 3, +-}; +- +-#define DDVID 0xdd01 /* Digital Devices Vendor ID */ +- +-#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \ +- .vendor = _vend, .device = _dev, \ +- .subvendor = _subvend, .subdevice = _subdev, \ +- .driver_data = (unsigned long)&_driverdata } +- +-static const struct pci_device_id ddb_id_tbl[] __devinitdata = { +- DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus), +- DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus), +- DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le), +- DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus), +- DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6), +- /* in case sub-ids got deleted in flash */ +- DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none), +- {0} +-}; +-MODULE_DEVICE_TABLE(pci, ddb_id_tbl); +- +- +-static struct pci_driver ddb_pci_driver = { +- .name = "DDBridge", +- .id_table = ddb_id_tbl, +- .probe = ddb_probe, +- .remove = ddb_remove, +-}; +- +-static __init int module_init_ddbridge(void) +-{ +- printk(KERN_INFO "Digital Devices PCIE bridge driver, " +- "Copyright (C) 2010-11 Digital Devices GmbH\n"); +- if (ddb_class_create()) +- return -1; +- return pci_register_driver(&ddb_pci_driver); +-} +- +-static __exit void module_exit_ddbridge(void) +-{ +- pci_unregister_driver(&ddb_pci_driver); +- ddb_class_destroy(); +-} +- +-module_init(module_init_ddbridge); +-module_exit(module_exit_ddbridge); +- +-MODULE_DESCRIPTION("Digital Devices PCIe Bridge"); +-MODULE_AUTHOR("Ralph Metzler"); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.5"); +diff --git a/drivers/media/dvb/ddbridge/ddbridge-regs.h b/drivers/media/dvb/ddbridge/ddbridge-regs.h +deleted file mode 100644 +index a3ccb31..0000000 +--- a/drivers/media/dvb/ddbridge/ddbridge-regs.h ++++ /dev/null +@@ -1,151 +0,0 @@ +-/* +- * ddbridge-regs.h: Digital Devices PCIe bridge driver +- * +- * Copyright (C) 2010-2011 Digital Devices GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */ +- +-/* Register Definitions */ +- +-#define CUR_REGISTERMAP_VERSION 0x10000 +- +-#define HARDWARE_VERSION 0x00 +-#define REGISTERMAP_VERSION 0x04 +- +-/* ------------------------------------------------------------------------- */ +-/* SPI Controller */ +- +-#define SPI_CONTROL 0x10 +-#define SPI_DATA 0x14 +- +-/* ------------------------------------------------------------------------- */ +- +-/* Interrupt controller */ +-/* How many MSI's are available depends on HW (Min 2 max 8) */ +-/* How many are usable also depends on Host platform */ +- +-#define INTERRUPT_BASE (0x40) +- +-#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00) +-#define MSI0_ENABLE (INTERRUPT_BASE + 0x00) +-#define MSI1_ENABLE (INTERRUPT_BASE + 0x04) +-#define MSI2_ENABLE (INTERRUPT_BASE + 0x08) +-#define MSI3_ENABLE (INTERRUPT_BASE + 0x0C) +-#define MSI4_ENABLE (INTERRUPT_BASE + 0x10) +-#define MSI5_ENABLE (INTERRUPT_BASE + 0x14) +-#define MSI6_ENABLE (INTERRUPT_BASE + 0x18) +-#define MSI7_ENABLE (INTERRUPT_BASE + 0x1C) +- +-#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20) +-#define INTERRUPT_ACK (INTERRUPT_BASE + 0x20) +- +-#define INTMASK_I2C1 (0x00000001) +-#define INTMASK_I2C2 (0x00000002) +-#define INTMASK_I2C3 (0x00000004) +-#define INTMASK_I2C4 (0x00000008) +- +-#define INTMASK_CIRQ1 (0x00000010) +-#define INTMASK_CIRQ2 (0x00000020) +-#define INTMASK_CIRQ3 (0x00000040) +-#define INTMASK_CIRQ4 (0x00000080) +- +-#define INTMASK_TSINPUT1 (0x00000100) +-#define INTMASK_TSINPUT2 (0x00000200) +-#define INTMASK_TSINPUT3 (0x00000400) +-#define INTMASK_TSINPUT4 (0x00000800) +-#define INTMASK_TSINPUT5 (0x00001000) +-#define INTMASK_TSINPUT6 (0x00002000) +-#define INTMASK_TSINPUT7 (0x00004000) +-#define INTMASK_TSINPUT8 (0x00008000) +- +-#define INTMASK_TSOUTPUT1 (0x00010000) +-#define INTMASK_TSOUTPUT2 (0x00020000) +-#define INTMASK_TSOUTPUT3 (0x00040000) +-#define INTMASK_TSOUTPUT4 (0x00080000) +- +-/* ------------------------------------------------------------------------- */ +-/* I2C Master Controller */ +- +-#define I2C_BASE (0x80) /* Byte offset */ +- +-#define I2C_COMMAND (0x00) +-#define I2C_TIMING (0x04) +-#define I2C_TASKLENGTH (0x08) /* High read, low write */ +-#define I2C_TASKADDRESS (0x0C) /* High read, low write */ +- +-#define I2C_MONITOR (0x1C) +- +-#define I2C_BASE_1 (I2C_BASE + 0x00) +-#define I2C_BASE_2 (I2C_BASE + 0x20) +-#define I2C_BASE_3 (I2C_BASE + 0x40) +-#define I2C_BASE_4 (I2C_BASE + 0x60) +- +-#define I2C_BASE_N(i) (I2C_BASE + (i) * 0x20) +- +-#define I2C_TASKMEM_BASE (0x1000) /* Byte offset */ +-#define I2C_TASKMEM_SIZE (0x1000) +- +-#define I2C_SPEED_400 (0x04030404) +-#define I2C_SPEED_200 (0x09080909) +-#define I2C_SPEED_154 (0x0C0B0C0C) +-#define I2C_SPEED_100 (0x13121313) +-#define I2C_SPEED_77 (0x19181919) +-#define I2C_SPEED_50 (0x27262727) +- +- +-/* ------------------------------------------------------------------------- */ +-/* DMA Controller */ +- +-#define DMA_BASE_WRITE (0x100) +-#define DMA_BASE_READ (0x140) +- +-#define DMA_CONTROL (0x00) /* 64 */ +-#define DMA_ERROR (0x04) /* 65 ( only read instance ) */ +- +-#define DMA_DIAG_CONTROL (0x1C) /* 71 */ +-#define DMA_DIAG_PACKETCOUNTER_LOW (0x20) /* 72 */ +-#define DMA_DIAG_PACKETCOUNTER_HIGH (0x24) /* 73 */ +-#define DMA_DIAG_TIMECOUNTER_LOW (0x28) /* 74 */ +-#define DMA_DIAG_TIMECOUNTER_HIGH (0x2C) /* 75 */ +-#define DMA_DIAG_RECHECKCOUNTER (0x30) /* 76 ( Split completions on read ) */ +-#define DMA_DIAG_WAITTIMEOUTINIT (0x34) /* 77 */ +-#define DMA_DIAG_WAITOVERFLOWCOUNTER (0x38) /* 78 */ +-#define DMA_DIAG_WAITCOUNTER (0x3C) /* 79 */ +- +-/* ------------------------------------------------------------------------- */ +-/* DMA Buffer */ +- +-#define TS_INPUT_BASE (0x200) +-#define TS_INPUT_CONTROL(i) (TS_INPUT_BASE + (i) * 16 + 0x00) +- +-#define TS_OUTPUT_BASE (0x280) +-#define TS_OUTPUT_CONTROL(i) (TS_OUTPUT_BASE + (i) * 16 + 0x00) +- +-#define DMA_BUFFER_BASE (0x300) +- +-#define DMA_BUFFER_CONTROL(i) (DMA_BUFFER_BASE + (i) * 16 + 0x00) +-#define DMA_BUFFER_ACK(i) (DMA_BUFFER_BASE + (i) * 16 + 0x04) +-#define DMA_BUFFER_CURRENT(i) (DMA_BUFFER_BASE + (i) * 16 + 0x08) +-#define DMA_BUFFER_SIZE(i) (DMA_BUFFER_BASE + (i) * 16 + 0x0c) +- +-#define DMA_BASE_ADDRESS_TABLE (0x2000) +-#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512) +- +diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h +deleted file mode 100644 +index 6d14893..0000000 +--- a/drivers/media/dvb/ddbridge/ddbridge.h ++++ /dev/null +@@ -1,187 +0,0 @@ +-/* +- * ddbridge.h: Digital Devices PCIe bridge driver +- * +- * Copyright (C) 2010-2011 Digital Devices GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#ifndef _DDBRIDGE_H_ +-#define _DDBRIDGE_H_ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_ringbuffer.h" +-#include "dvb_ca_en50221.h" +-#include "dvb_net.h" +-#include "cxd2099.h" +- +-#define DDB_MAX_I2C 4 +-#define DDB_MAX_PORT 4 +-#define DDB_MAX_INPUT 8 +-#define DDB_MAX_OUTPUT 4 +- +-struct ddb_info { +- int type; +-#define DDB_NONE 0 +-#define DDB_OCTOPUS 1 +- char *name; +- int port_num; +- u32 port_type[DDB_MAX_PORT]; +-}; +- +-/* DMA_SIZE MUST be divisible by 188 and 128 !!! */ +- +-#define INPUT_DMA_MAX_BUFS 32 /* hardware table limit */ +-#define INPUT_DMA_BUFS 8 +-#define INPUT_DMA_SIZE (128*47*21) +- +-#define OUTPUT_DMA_MAX_BUFS 32 +-#define OUTPUT_DMA_BUFS 8 +-#define OUTPUT_DMA_SIZE (128*47*21) +- +-struct ddb; +-struct ddb_port; +- +-struct ddb_input { +- struct ddb_port *port; +- u32 nr; +- int attached; +- +- dma_addr_t pbuf[INPUT_DMA_MAX_BUFS]; +- u8 *vbuf[INPUT_DMA_MAX_BUFS]; +- u32 dma_buf_num; +- u32 dma_buf_size; +- +- struct tasklet_struct tasklet; +- spinlock_t lock; +- wait_queue_head_t wq; +- int running; +- u32 stat; +- u32 cbuf; +- u32 coff; +- +- struct dvb_adapter adap; +- struct dvb_device *dev; +- struct dvb_frontend *fe; +- struct dvb_frontend *fe2; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- struct dvb_net dvbnet; +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- int users; +- int (*gate_ctrl)(struct dvb_frontend *, int); +-}; +- +-struct ddb_output { +- struct ddb_port *port; +- u32 nr; +- dma_addr_t pbuf[OUTPUT_DMA_MAX_BUFS]; +- u8 *vbuf[OUTPUT_DMA_MAX_BUFS]; +- u32 dma_buf_num; +- u32 dma_buf_size; +- struct tasklet_struct tasklet; +- spinlock_t lock; +- wait_queue_head_t wq; +- int running; +- u32 stat; +- u32 cbuf; +- u32 coff; +- +- struct dvb_adapter adap; +- struct dvb_device *dev; +-}; +- +-struct ddb_i2c { +- struct ddb *dev; +- u32 nr; +- struct i2c_adapter adap; +- struct i2c_adapter adap2; +- u32 regs; +- u32 rbuf; +- u32 wbuf; +- int done; +- wait_queue_head_t wq; +-}; +- +-struct ddb_port { +- struct ddb *dev; +- u32 nr; +- struct ddb_i2c *i2c; +- struct mutex i2c_gate_lock; +- u32 class; +-#define DDB_PORT_NONE 0 +-#define DDB_PORT_CI 1 +-#define DDB_PORT_TUNER 2 +- u32 type; +-#define DDB_TUNER_NONE 0 +-#define DDB_TUNER_DVBS_ST 1 +-#define DDB_TUNER_DVBS_ST_AA 2 +-#define DDB_TUNER_DVBCT_TR 16 +-#define DDB_TUNER_DVBCT_ST 17 +- u32 adr; +- +- struct ddb_input *input[2]; +- struct ddb_output *output; +- struct dvb_ca_en50221 *en; +-}; +- +-struct ddb { +- struct pci_dev *pdev; +- unsigned char *regs; +- struct ddb_port port[DDB_MAX_PORT]; +- struct ddb_i2c i2c[DDB_MAX_I2C]; +- struct ddb_input input[DDB_MAX_INPUT]; +- struct ddb_output output[DDB_MAX_OUTPUT]; +- +- struct device *ddb_dev; +- int nr; +- u8 iobuf[1028]; +- +- struct ddb_info *info; +- int msi; +-}; +- +-/****************************************************************************/ +- +-#define ddbwritel(_val, _adr) writel((_val), \ +- (char *) (dev->regs+(_adr))) +-#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) +-#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ +- (dev->regs+(_adr)), (_src), (_count)) +-#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ +- (dev->regs+(_adr)), (_count)) +- +-/****************************************************************************/ +- +-#endif +diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig +deleted file mode 100644 +index f3de0a4..0000000 +--- a/drivers/media/dvb/dm1105/Kconfig ++++ /dev/null +@@ -1,20 +0,0 @@ +-config DVB_DM1105 +- tristate "SDMC DM1105 based PCI cards" +- depends on DVB_CORE && PCI && I2C +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_STV0288 if !DVB_FE_CUSTOMISE +- select DVB_STB6000 if !DVB_FE_CUSTOMISE +- select DVB_CX24116 if !DVB_FE_CUSTOMISE +- select DVB_SI21XX if !DVB_FE_CUSTOMISE +- select DVB_DS3000 if !DVB_FE_CUSTOMISE +- depends on RC_CORE +- help +- Support for cards based on the SDMC DM1105 PCI chip like +- DvbWorld 2002 +- +- Since these cards have no MPEG decoder onboard, they transmit +- only compressed MPEG data over the PCI bus, so you need +- an external software decoder to watch TV on your computer. +- +- Say Y or M if you own such a device and want to use it. +diff --git a/drivers/media/dvb/dm1105/Makefile b/drivers/media/dvb/dm1105/Makefile +deleted file mode 100644 +index 95a008b..0000000 +--- a/drivers/media/dvb/dm1105/Makefile ++++ /dev/null +@@ -1,3 +0,0 @@ +-obj-$(CONFIG_DVB_DM1105) += dm1105.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends +diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c +deleted file mode 100644 +index a609b3a..0000000 +--- a/drivers/media/dvb/dm1105/dm1105.c ++++ /dev/null +@@ -1,1248 +0,0 @@ +-/* +- * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip +- * +- * Copyright (C) 2008 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "demux.h" +-#include "dmxdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +-#include "dvbdev.h" +-#include "dvb-pll.h" +- +-#include "stv0299.h" +-#include "stv0288.h" +-#include "stb6000.h" +-#include "si21xx.h" +-#include "cx24116.h" +-#include "z0194a.h" +-#include "ds3000.h" +- +-#define MODULE_NAME "dm1105" +- +-#define UNSET (-1U) +- +-#define DM1105_BOARD_NOAUTO UNSET +-#define DM1105_BOARD_UNKNOWN 0 +-#define DM1105_BOARD_DVBWORLD_2002 1 +-#define DM1105_BOARD_DVBWORLD_2004 2 +-#define DM1105_BOARD_AXESS_DM05 3 +-#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 +- +-/* ----------------------------------------------- */ +-/* +- * PCI ID's +- */ +-#ifndef PCI_VENDOR_ID_TRIGEM +-#define PCI_VENDOR_ID_TRIGEM 0x109f +-#endif +-#ifndef PCI_VENDOR_ID_AXESS +-#define PCI_VENDOR_ID_AXESS 0x195d +-#endif +-#ifndef PCI_DEVICE_ID_DM1105 +-#define PCI_DEVICE_ID_DM1105 0x036f +-#endif +-#ifndef PCI_DEVICE_ID_DW2002 +-#define PCI_DEVICE_ID_DW2002 0x2002 +-#endif +-#ifndef PCI_DEVICE_ID_DW2004 +-#define PCI_DEVICE_ID_DW2004 0x2004 +-#endif +-#ifndef PCI_DEVICE_ID_DM05 +-#define PCI_DEVICE_ID_DM05 0x1105 +-#endif +-/* ----------------------------------------------- */ +-/* sdmc dm1105 registers */ +- +-/* TS Control */ +-#define DM1105_TSCTR 0x00 +-#define DM1105_DTALENTH 0x04 +- +-/* GPIO Interface */ +-#define DM1105_GPIOVAL 0x08 +-#define DM1105_GPIOCTR 0x0c +- +-/* PID serial number */ +-#define DM1105_PIDN 0x10 +- +-/* Odd-even secret key select */ +-#define DM1105_CWSEL 0x14 +- +-/* Host Command Interface */ +-#define DM1105_HOST_CTR 0x18 +-#define DM1105_HOST_AD 0x1c +- +-/* PCI Interface */ +-#define DM1105_CR 0x30 +-#define DM1105_RST 0x34 +-#define DM1105_STADR 0x38 +-#define DM1105_RLEN 0x3c +-#define DM1105_WRP 0x40 +-#define DM1105_INTCNT 0x44 +-#define DM1105_INTMAK 0x48 +-#define DM1105_INTSTS 0x4c +- +-/* CW Value */ +-#define DM1105_ODD 0x50 +-#define DM1105_EVEN 0x58 +- +-/* PID Value */ +-#define DM1105_PID 0x60 +- +-/* IR Control */ +-#define DM1105_IRCTR 0x64 +-#define DM1105_IRMODE 0x68 +-#define DM1105_SYSTEMCODE 0x6c +-#define DM1105_IRCODE 0x70 +- +-/* Unknown Values */ +-#define DM1105_ENCRYPT 0x74 +-#define DM1105_VER 0x7c +- +-/* I2C Interface */ +-#define DM1105_I2CCTR 0x80 +-#define DM1105_I2CSTS 0x81 +-#define DM1105_I2CDAT 0x82 +-#define DM1105_I2C_RA 0x83 +-/* ----------------------------------------------- */ +-/* Interrupt Mask Bits */ +- +-#define INTMAK_TSIRQM 0x01 +-#define INTMAK_HIRQM 0x04 +-#define INTMAK_IRM 0x08 +-#define INTMAK_ALLMASK (INTMAK_TSIRQM | \ +- INTMAK_HIRQM | \ +- INTMAK_IRM) +-#define INTMAK_NONEMASK 0x00 +- +-/* Interrupt Status Bits */ +-#define INTSTS_TSIRQ 0x01 +-#define INTSTS_HIRQ 0x04 +-#define INTSTS_IR 0x08 +- +-/* IR Control Bits */ +-#define DM1105_IR_EN 0x01 +-#define DM1105_SYS_CHK 0x02 +-#define DM1105_REP_FLG 0x08 +- +-/* EEPROM addr */ +-#define IIC_24C01_addr 0xa0 +-/* Max board count */ +-#define DM1105_MAX 0x04 +- +-#define DRIVER_NAME "dm1105" +-#define DM1105_I2C_GPIO_NAME "dm1105-gpio" +- +-#define DM1105_DMA_PACKETS 47 +-#define DM1105_DMA_PACKET_LENGTH (128*4) +-#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) +- +-/* */ +-#define GPIO08 (1 << 8) +-#define GPIO13 (1 << 13) +-#define GPIO14 (1 << 14) +-#define GPIO15 (1 << 15) +-#define GPIO16 (1 << 16) +-#define GPIO17 (1 << 17) +-#define GPIO_ALL 0x03ffff +- +-/* GPIO's for LNB power control */ +-#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +-#define DM1105_LNB_OFF GPIO17 +-#define DM1105_LNB_13V (GPIO16 | GPIO08) +-#define DM1105_LNB_18V GPIO08 +- +-/* GPIO's for LNB power control for Axess DM05 */ +-#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) +-#define DM05_LNB_OFF GPIO17/* actually 13v */ +-#define DM05_LNB_13V GPIO17 +-#define DM05_LNB_18V (GPIO17 | GPIO16) +- +-/* GPIO's for LNB power control for unbranded with I2C on GPIO */ +-#define UNBR_LNB_MASK (GPIO17 | GPIO16) +-#define UNBR_LNB_OFF 0 +-#define UNBR_LNB_13V GPIO17 +-#define UNBR_LNB_18V (GPIO17 | GPIO16) +- +-static unsigned int card[] = {[0 ... 3] = UNSET }; +-module_param_array(card, int, NULL, 0444); +-MODULE_PARM_DESC(card, "card type"); +- +-static int ir_debug; +-module_param(ir_debug, int, 0644); +-MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +- +-static unsigned int dm1105_devcount; +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct dm1105_board { +- char *name; +- struct { +- u32 mask, off, v13, v18; +- } lnb; +- u32 gpio_scl, gpio_sda; +-}; +- +-struct dm1105_subid { +- u16 subvendor; +- u16 subdevice; +- u32 card; +-}; +- +-static const struct dm1105_board dm1105_boards[] = { +- [DM1105_BOARD_UNKNOWN] = { +- .name = "UNKNOWN/GENERIC", +- .lnb = { +- .mask = DM1105_LNB_MASK, +- .off = DM1105_LNB_OFF, +- .v13 = DM1105_LNB_13V, +- .v18 = DM1105_LNB_18V, +- }, +- }, +- [DM1105_BOARD_DVBWORLD_2002] = { +- .name = "DVBWorld PCI 2002", +- .lnb = { +- .mask = DM1105_LNB_MASK, +- .off = DM1105_LNB_OFF, +- .v13 = DM1105_LNB_13V, +- .v18 = DM1105_LNB_18V, +- }, +- }, +- [DM1105_BOARD_DVBWORLD_2004] = { +- .name = "DVBWorld PCI 2004", +- .lnb = { +- .mask = DM1105_LNB_MASK, +- .off = DM1105_LNB_OFF, +- .v13 = DM1105_LNB_13V, +- .v18 = DM1105_LNB_18V, +- }, +- }, +- [DM1105_BOARD_AXESS_DM05] = { +- .name = "Axess/EasyTv DM05", +- .lnb = { +- .mask = DM05_LNB_MASK, +- .off = DM05_LNB_OFF, +- .v13 = DM05_LNB_13V, +- .v18 = DM05_LNB_18V, +- }, +- }, +- [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { +- .name = "Unbranded DM1105 with i2c on GPIOs", +- .lnb = { +- .mask = UNBR_LNB_MASK, +- .off = UNBR_LNB_OFF, +- .v13 = UNBR_LNB_13V, +- .v18 = UNBR_LNB_18V, +- }, +- .gpio_scl = GPIO14, +- .gpio_sda = GPIO13, +- }, +-}; +- +-static const struct dm1105_subid dm1105_subids[] = { +- { +- .subvendor = 0x0000, +- .subdevice = 0x2002, +- .card = DM1105_BOARD_DVBWORLD_2002, +- }, { +- .subvendor = 0x0001, +- .subdevice = 0x2002, +- .card = DM1105_BOARD_DVBWORLD_2002, +- }, { +- .subvendor = 0x0000, +- .subdevice = 0x2004, +- .card = DM1105_BOARD_DVBWORLD_2004, +- }, { +- .subvendor = 0x0001, +- .subdevice = 0x2004, +- .card = DM1105_BOARD_DVBWORLD_2004, +- }, { +- .subvendor = 0x195d, +- .subdevice = 0x1105, +- .card = DM1105_BOARD_AXESS_DM05, +- }, +-}; +- +-static void dm1105_card_list(struct pci_dev *pci) +-{ +- int i; +- +- if (0 == pci->subsystem_vendor && +- 0 == pci->subsystem_device) { +- printk(KERN_ERR +- "dm1105: Your board has no valid PCI Subsystem ID\n" +- "dm1105: and thus can't be autodetected\n" +- "dm1105: Please pass card= insmod option to\n" +- "dm1105: workaround that. Redirect complaints to\n" +- "dm1105: the vendor of the TV card. Best regards,\n" +- "dm1105: -- tux\n"); +- } else { +- printk(KERN_ERR +- "dm1105: Your board isn't known (yet) to the driver.\n" +- "dm1105: You can try to pick one of the existing\n" +- "dm1105: card configs via card= insmod option.\n" +- "dm1105: Updating to the latest version might help\n" +- "dm1105: as well.\n"); +- } +- printk(KERN_ERR "Here is a list of valid choices for the card= " +- "insmod option:\n"); +- for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) +- printk(KERN_ERR "dm1105: card=%d -> %s\n", +- i, dm1105_boards[i].name); +-} +- +-/* infrared remote control */ +-struct infrared { +- struct rc_dev *dev; +- char input_phys[32]; +- struct work_struct work; +- u32 ir_command; +-}; +- +-struct dm1105_dev { +- /* pci */ +- struct pci_dev *pdev; +- u8 __iomem *io_mem; +- +- /* ir */ +- struct infrared ir; +- +- /* dvb */ +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- struct dmxdev dmxdev; +- struct dvb_adapter dvb_adapter; +- struct dvb_demux demux; +- struct dvb_frontend *fe; +- struct dvb_net dvbnet; +- unsigned int full_ts_users; +- unsigned int boardnr; +- int nr; +- +- /* i2c */ +- struct i2c_adapter i2c_adap; +- struct i2c_adapter i2c_bb_adap; +- struct i2c_algo_bit_data i2c_bit; +- +- /* irq */ +- struct work_struct work; +- struct workqueue_struct *wq; +- char wqn[16]; +- +- /* dma */ +- dma_addr_t dma_addr; +- unsigned char *ts_buf; +- u32 wrp; +- u32 nextwrp; +- u32 buffer_size; +- unsigned int PacketErrorCount; +- unsigned int dmarst; +- spinlock_t lock; +-}; +- +-#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg])) +- +-#define dm_readb(reg) inb(dm_io_mem(reg)) +-#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg))) +- +-#define dm_readw(reg) inw(dm_io_mem(reg)) +-#define dm_writew(reg, value) outw((value), (dm_io_mem(reg))) +- +-#define dm_readl(reg) inl(dm_io_mem(reg)) +-#define dm_writel(reg, value) outl((value), (dm_io_mem(reg))) +- +-#define dm_andorl(reg, mask, value) \ +- outl((inl(dm_io_mem(reg)) & ~(mask)) |\ +- ((value) & (mask)), (dm_io_mem(reg))) +- +-#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) +-#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) +- +-/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, +- so we can use only 3 GPIO's from GPIO15 to GPIO17. +- Here I don't check whether HOST is enebled as it is not implemented yet. +- */ +-static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) +-{ +- if (mask & 0xfffc0000) +- printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); +- +- if (mask & 0x0003ffff) +- dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); +- +-} +- +-static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) +-{ +- if (mask & 0xfffc0000) +- printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); +- +- if (mask & 0x0003ffff) +- dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); +- +-} +- +-static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) +-{ +- if (mask & 0xfffc0000) +- printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); +- +- if (mask & 0x0003ffff) +- dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); +- +-} +- +-static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) +-{ +- if (mask & 0xfffc0000) +- printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); +- +- if (mask & 0x0003ffff) +- return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; +- +- return 0; +-} +- +-static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) +-{ +- if (mask & 0xfffc0000) +- printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); +- +- if ((mask & 0x0003ffff) && asoutput) +- dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); +- else if ((mask & 0x0003ffff) && !asoutput) +- dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); +- +-} +- +-static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) +-{ +- if (state) +- dm1105_gpio_enable(dev, line, 0); +- else { +- dm1105_gpio_enable(dev, line, 1); +- dm1105_gpio_clear(dev, line); +- } +-} +- +-static void dm1105_setsda(void *data, int state) +-{ +- struct dm1105_dev *dev = data; +- +- dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); +-} +- +-static void dm1105_setscl(void *data, int state) +-{ +- struct dm1105_dev *dev = data; +- +- dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); +-} +- +-static int dm1105_getsda(void *data) +-{ +- struct dm1105_dev *dev = data; +- +- return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) +- ? 1 : 0; +-} +- +-static int dm1105_getscl(void *data) +-{ +- struct dm1105_dev *dev = data; +- +- return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) +- ? 1 : 0; +-} +- +-static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, +- struct i2c_msg *msgs, int num) +-{ +- struct dm1105_dev *dev ; +- +- int addr, rc, i, j, k, len, byte, data; +- u8 status; +- +- dev = i2c_adap->algo_data; +- for (i = 0; i < num; i++) { +- dm_writeb(DM1105_I2CCTR, 0x00); +- if (msgs[i].flags & I2C_M_RD) { +- /* read bytes */ +- addr = msgs[i].addr << 1; +- addr |= 1; +- dm_writeb(DM1105_I2CDAT, addr); +- for (byte = 0; byte < msgs[i].len; byte++) +- dm_writeb(DM1105_I2CDAT + byte + 1, 0); +- +- dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); +- for (j = 0; j < 55; j++) { +- mdelay(10); +- status = dm_readb(DM1105_I2CSTS); +- if ((status & 0xc0) == 0x40) +- break; +- } +- if (j >= 55) +- return -1; +- +- for (byte = 0; byte < msgs[i].len; byte++) { +- rc = dm_readb(DM1105_I2CDAT + byte + 1); +- if (rc < 0) +- goto err; +- msgs[i].buf[byte] = rc; +- } +- } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { +- /* prepaired for cx24116 firmware */ +- /* Write in small blocks */ +- len = msgs[i].len - 1; +- k = 1; +- do { +- dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); +- dm_writeb(DM1105_I2CDAT + 1, 0xf7); +- for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { +- data = msgs[i].buf[k + byte]; +- dm_writeb(DM1105_I2CDAT + byte + 2, data); +- } +- dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len)); +- for (j = 0; j < 25; j++) { +- mdelay(10); +- status = dm_readb(DM1105_I2CSTS); +- if ((status & 0xc0) == 0x40) +- break; +- } +- +- if (j >= 25) +- return -1; +- +- k += 48; +- len -= 48; +- } while (len > 0); +- } else { +- /* write bytes */ +- dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); +- for (byte = 0; byte < msgs[i].len; byte++) { +- data = msgs[i].buf[byte]; +- dm_writeb(DM1105_I2CDAT + byte + 1, data); +- } +- dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); +- for (j = 0; j < 25; j++) { +- mdelay(10); +- status = dm_readb(DM1105_I2CSTS); +- if ((status & 0xc0) == 0x40) +- break; +- } +- +- if (j >= 25) +- return -1; +- } +- } +- return num; +- err: +- return rc; +-} +- +-static u32 functionality(struct i2c_adapter *adap) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dm1105_algo = { +- .master_xfer = dm1105_i2c_xfer, +- .functionality = functionality, +-}; +- +-static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed) +-{ +- return container_of(feed->demux, struct dm1105_dev, demux); +-} +- +-static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) +-{ +- return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); +-} +- +-static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); +- +- dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); +- if (voltage == SEC_VOLTAGE_18) +- dm1105_gpio_andor(dev, +- dm1105_boards[dev->boardnr].lnb.mask, +- dm1105_boards[dev->boardnr].lnb.v18); +- else if (voltage == SEC_VOLTAGE_13) +- dm1105_gpio_andor(dev, +- dm1105_boards[dev->boardnr].lnb.mask, +- dm1105_boards[dev->boardnr].lnb.v13); +- else +- dm1105_gpio_andor(dev, +- dm1105_boards[dev->boardnr].lnb.mask, +- dm1105_boards[dev->boardnr].lnb.off); +- +- return 0; +-} +- +-static void dm1105_set_dma_addr(struct dm1105_dev *dev) +-{ +- dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); +-} +- +-static int __devinit dm1105_dma_map(struct dm1105_dev *dev) +-{ +- dev->ts_buf = pci_alloc_consistent(dev->pdev, +- 6 * DM1105_DMA_BYTES, +- &dev->dma_addr); +- +- return !dev->ts_buf; +-} +- +-static void dm1105_dma_unmap(struct dm1105_dev *dev) +-{ +- pci_free_consistent(dev->pdev, +- 6 * DM1105_DMA_BYTES, +- dev->ts_buf, +- dev->dma_addr); +-} +- +-static void dm1105_enable_irqs(struct dm1105_dev *dev) +-{ +- dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK); +- dm_writeb(DM1105_CR, 1); +-} +- +-static void dm1105_disable_irqs(struct dm1105_dev *dev) +-{ +- dm_writeb(DM1105_INTMAK, INTMAK_IRM); +- dm_writeb(DM1105_CR, 0); +-} +- +-static int dm1105_start_feed(struct dvb_demux_feed *f) +-{ +- struct dm1105_dev *dev = feed_to_dm1105_dev(f); +- +- if (dev->full_ts_users++ == 0) +- dm1105_enable_irqs(dev); +- +- return 0; +-} +- +-static int dm1105_stop_feed(struct dvb_demux_feed *f) +-{ +- struct dm1105_dev *dev = feed_to_dm1105_dev(f); +- +- if (--dev->full_ts_users == 0) +- dm1105_disable_irqs(dev); +- +- return 0; +-} +- +-/* ir work handler */ +-static void dm1105_emit_key(struct work_struct *work) +-{ +- struct infrared *ir = container_of(work, struct infrared, work); +- u32 ircom = ir->ir_command; +- u8 data; +- +- if (ir_debug) +- printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom); +- +- data = (ircom >> 8) & 0x7f; +- +- rc_keydown(ir->dev, data, 0); +-} +- +-/* work handler */ +-static void dm1105_dmx_buffer(struct work_struct *work) +-{ +- struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work); +- unsigned int nbpackets; +- u32 oldwrp = dev->wrp; +- u32 nextwrp = dev->nextwrp; +- +- if (!((dev->ts_buf[oldwrp] == 0x47) && +- (dev->ts_buf[oldwrp + 188] == 0x47) && +- (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) { +- dev->PacketErrorCount++; +- /* bad packet found */ +- if ((dev->PacketErrorCount >= 2) && +- (dev->dmarst == 0)) { +- dm_writeb(DM1105_RST, 1); +- dev->wrp = 0; +- dev->PacketErrorCount = 0; +- dev->dmarst = 0; +- return; +- } +- } +- +- if (nextwrp < oldwrp) { +- memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp); +- nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188; +- } else +- nbpackets = (nextwrp - oldwrp) / 188; +- +- dev->wrp = nextwrp; +- dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets); +-} +- +-static irqreturn_t dm1105_irq(int irq, void *dev_id) +-{ +- struct dm1105_dev *dev = dev_id; +- +- /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ +- unsigned int intsts = dm_readb(DM1105_INTSTS); +- dm_writeb(DM1105_INTSTS, intsts); +- +- switch (intsts) { +- case INTSTS_TSIRQ: +- case (INTSTS_TSIRQ | INTSTS_IR): +- dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); +- queue_work(dev->wq, &dev->work); +- break; +- case INTSTS_IR: +- dev->ir.ir_command = dm_readl(DM1105_IRCODE); +- schedule_work(&dev->ir.work); +- break; +- } +- +- return IRQ_HANDLED; +-} +- +-int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) +-{ +- struct rc_dev *dev; +- int err = -ENOMEM; +- +- dev = rc_allocate_device(); +- if (!dev) +- return -ENOMEM; +- +- snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), +- "pci-%s/ir0", pci_name(dm1105->pdev)); +- +- dev->driver_name = MODULE_NAME; +- dev->map_name = RC_MAP_DM1105_NEC; +- dev->driver_type = RC_DRIVER_SCANCODE; +- dev->input_name = "DVB on-card IR receiver"; +- dev->input_phys = dm1105->ir.input_phys; +- dev->input_id.bustype = BUS_PCI; +- dev->input_id.version = 1; +- if (dm1105->pdev->subsystem_vendor) { +- dev->input_id.vendor = dm1105->pdev->subsystem_vendor; +- dev->input_id.product = dm1105->pdev->subsystem_device; +- } else { +- dev->input_id.vendor = dm1105->pdev->vendor; +- dev->input_id.product = dm1105->pdev->device; +- } +- dev->dev.parent = &dm1105->pdev->dev; +- +- INIT_WORK(&dm1105->ir.work, dm1105_emit_key); +- +- err = rc_register_device(dev); +- if (err < 0) { +- rc_free_device(dev); +- return err; +- } +- +- dm1105->ir.dev = dev; +- return 0; +-} +- +-void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) +-{ +- rc_unregister_device(dm1105->ir.dev); +-} +- +-static int __devinit dm1105_hw_init(struct dm1105_dev *dev) +-{ +- dm1105_disable_irqs(dev); +- +- dm_writeb(DM1105_HOST_CTR, 0); +- +- /*DATALEN 188,*/ +- dm_writeb(DM1105_DTALENTH, 188); +- /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ +- dm_writew(DM1105_TSCTR, 0xc10a); +- +- /* map DMA and set address */ +- dm1105_dma_map(dev); +- dm1105_set_dma_addr(dev); +- /* big buffer */ +- dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); +- dm_writeb(DM1105_INTCNT, 47); +- +- /* IR NEC mode enable */ +- dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK)); +- dm_writeb(DM1105_IRMODE, 0); +- dm_writew(DM1105_SYSTEMCODE, 0); +- +- return 0; +-} +- +-static void dm1105_hw_exit(struct dm1105_dev *dev) +-{ +- dm1105_disable_irqs(dev); +- +- /* IR disable */ +- dm_writeb(DM1105_IRCTR, 0); +- dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK); +- +- dm1105_dma_unmap(dev); +-} +- +-static struct stv0299_config sharp_z0194a_config = { +- .demod_address = 0x68, +- .inittab = sharp_z0194a_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 100, +- .set_symbol_rate = sharp_z0194a_set_symbol_rate, +-}; +- +-static struct stv0288_config earda_config = { +- .demod_address = 0x68, +- .min_delay_ms = 100, +-}; +- +-static struct si21xx_config serit_config = { +- .demod_address = 0x68, +- .min_delay_ms = 100, +- +-}; +- +-static struct cx24116_config serit_sp2633_config = { +- .demod_address = 0x55, +-}; +- +-static struct ds3000_config dvbworld_ds3000_config = { +- .demod_address = 0x68, +-}; +- +-static int __devinit frontend_init(struct dm1105_dev *dev) +-{ +- int ret; +- +- switch (dev->boardnr) { +- case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: +- dm1105_gpio_enable(dev, GPIO15, 1); +- dm1105_gpio_clear(dev, GPIO15); +- msleep(100); +- dm1105_gpio_set(dev, GPIO15); +- msleep(200); +- dev->fe = dvb_attach( +- stv0299_attach, &sharp_z0194a_config, +- &dev->i2c_bb_adap); +- if (dev->fe) { +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- dvb_attach(dvb_pll_attach, dev->fe, 0x60, +- &dev->i2c_bb_adap, DVB_PLL_OPERA1); +- break; +- } +- +- dev->fe = dvb_attach( +- stv0288_attach, &earda_config, +- &dev->i2c_bb_adap); +- if (dev->fe) { +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- dvb_attach(stb6000_attach, dev->fe, 0x61, +- &dev->i2c_bb_adap); +- break; +- } +- +- dev->fe = dvb_attach( +- si21xx_attach, &serit_config, +- &dev->i2c_bb_adap); +- if (dev->fe) +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- break; +- case DM1105_BOARD_DVBWORLD_2004: +- dev->fe = dvb_attach( +- cx24116_attach, &serit_sp2633_config, +- &dev->i2c_adap); +- if (dev->fe) { +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- break; +- } +- +- dev->fe = dvb_attach( +- ds3000_attach, &dvbworld_ds3000_config, +- &dev->i2c_adap); +- if (dev->fe) +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- +- break; +- case DM1105_BOARD_DVBWORLD_2002: +- case DM1105_BOARD_AXESS_DM05: +- default: +- dev->fe = dvb_attach( +- stv0299_attach, &sharp_z0194a_config, +- &dev->i2c_adap); +- if (dev->fe) { +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- dvb_attach(dvb_pll_attach, dev->fe, 0x60, +- &dev->i2c_adap, DVB_PLL_OPERA1); +- break; +- } +- +- dev->fe = dvb_attach( +- stv0288_attach, &earda_config, +- &dev->i2c_adap); +- if (dev->fe) { +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- dvb_attach(stb6000_attach, dev->fe, 0x61, +- &dev->i2c_adap); +- break; +- } +- +- dev->fe = dvb_attach( +- si21xx_attach, &serit_config, +- &dev->i2c_adap); +- if (dev->fe) +- dev->fe->ops.set_voltage = dm1105_set_voltage; +- +- } +- +- if (!dev->fe) { +- dev_err(&dev->pdev->dev, "could not attach frontend\n"); +- return -ENODEV; +- } +- +- ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe); +- if (ret < 0) { +- if (dev->fe->ops.release) +- dev->fe->ops.release(dev->fe); +- dev->fe = NULL; +- return ret; +- } +- +- return 0; +-} +- +-static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac) +-{ +- static u8 command[1] = { 0x28 }; +- +- struct i2c_msg msg[] = { +- { +- .addr = IIC_24C01_addr >> 1, +- .flags = 0, +- .buf = command, +- .len = 1 +- }, { +- .addr = IIC_24C01_addr >> 1, +- .flags = I2C_M_RD, +- .buf = mac, +- .len = 6 +- }, +- }; +- +- dm1105_i2c_xfer(&dev->i2c_adap, msg , 2); +- dev_info(&dev->pdev->dev, "MAC %pM\n", mac); +-} +- +-static int __devinit dm1105_probe(struct pci_dev *pdev, +- const struct pci_device_id *ent) +-{ +- struct dm1105_dev *dev; +- struct dvb_adapter *dvb_adapter; +- struct dvb_demux *dvbdemux; +- struct dmx_demux *dmx; +- int ret = -ENOMEM; +- int i; +- +- dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); +- if (!dev) +- return -ENOMEM; +- +- /* board config */ +- dev->nr = dm1105_devcount; +- dev->boardnr = UNSET; +- if (card[dev->nr] < ARRAY_SIZE(dm1105_boards)) +- dev->boardnr = card[dev->nr]; +- for (i = 0; UNSET == dev->boardnr && +- i < ARRAY_SIZE(dm1105_subids); i++) +- if (pdev->subsystem_vendor == +- dm1105_subids[i].subvendor && +- pdev->subsystem_device == +- dm1105_subids[i].subdevice) +- dev->boardnr = dm1105_subids[i].card; +- +- if (UNSET == dev->boardnr) { +- dev->boardnr = DM1105_BOARD_UNKNOWN; +- dm1105_card_list(pdev); +- } +- +- dm1105_devcount++; +- dev->pdev = pdev; +- dev->buffer_size = 5 * DM1105_DMA_BYTES; +- dev->PacketErrorCount = 0; +- dev->dmarst = 0; +- +- ret = pci_enable_device(pdev); +- if (ret < 0) +- goto err_kfree; +- +- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); +- if (ret < 0) +- goto err_pci_disable_device; +- +- pci_set_master(pdev); +- +- ret = pci_request_regions(pdev, DRIVER_NAME); +- if (ret < 0) +- goto err_pci_disable_device; +- +- dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); +- if (!dev->io_mem) { +- ret = -EIO; +- goto err_pci_release_regions; +- } +- +- spin_lock_init(&dev->lock); +- pci_set_drvdata(pdev, dev); +- +- ret = dm1105_hw_init(dev); +- if (ret < 0) +- goto err_pci_iounmap; +- +- /* i2c */ +- i2c_set_adapdata(&dev->i2c_adap, dev); +- strcpy(dev->i2c_adap.name, DRIVER_NAME); +- dev->i2c_adap.owner = THIS_MODULE; +- dev->i2c_adap.dev.parent = &pdev->dev; +- dev->i2c_adap.algo = &dm1105_algo; +- dev->i2c_adap.algo_data = dev; +- ret = i2c_add_adapter(&dev->i2c_adap); +- +- if (ret < 0) +- goto err_dm1105_hw_exit; +- +- i2c_set_adapdata(&dev->i2c_bb_adap, dev); +- strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); +- dev->i2c_bb_adap.owner = THIS_MODULE; +- dev->i2c_bb_adap.dev.parent = &pdev->dev; +- dev->i2c_bb_adap.algo_data = &dev->i2c_bit; +- dev->i2c_bit.data = dev; +- dev->i2c_bit.setsda = dm1105_setsda; +- dev->i2c_bit.setscl = dm1105_setscl; +- dev->i2c_bit.getsda = dm1105_getsda; +- dev->i2c_bit.getscl = dm1105_getscl; +- dev->i2c_bit.udelay = 10; +- dev->i2c_bit.timeout = 10; +- +- /* Raise SCL and SDA */ +- dm1105_setsda(dev, 1); +- dm1105_setscl(dev, 1); +- +- ret = i2c_bit_add_bus(&dev->i2c_bb_adap); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- /* dvb */ +- ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, +- THIS_MODULE, &pdev->dev, adapter_nr); +- if (ret < 0) +- goto err_i2c_del_adapters; +- +- dvb_adapter = &dev->dvb_adapter; +- +- dm1105_read_mac(dev, dvb_adapter->proposed_mac); +- +- dvbdemux = &dev->demux; +- dvbdemux->filternum = 256; +- dvbdemux->feednum = 256; +- dvbdemux->start_feed = dm1105_start_feed; +- dvbdemux->stop_feed = dm1105_stop_feed; +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | +- DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); +- ret = dvb_dmx_init(dvbdemux); +- if (ret < 0) +- goto err_dvb_unregister_adapter; +- +- dmx = &dvbdemux->dmx; +- dev->dmxdev.filternum = 256; +- dev->dmxdev.demux = dmx; +- dev->dmxdev.capabilities = 0; +- +- ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter); +- if (ret < 0) +- goto err_dvb_dmx_release; +- +- dev->hw_frontend.source = DMX_FRONTEND_0; +- +- ret = dmx->add_frontend(dmx, &dev->hw_frontend); +- if (ret < 0) +- goto err_dvb_dmxdev_release; +- +- dev->mem_frontend.source = DMX_MEMORY_FE; +- +- ret = dmx->add_frontend(dmx, &dev->mem_frontend); +- if (ret < 0) +- goto err_remove_hw_frontend; +- +- ret = dmx->connect_frontend(dmx, &dev->hw_frontend); +- if (ret < 0) +- goto err_remove_mem_frontend; +- +- ret = dvb_net_init(dvb_adapter, &dev->dvbnet, dmx); +- if (ret < 0) +- goto err_disconnect_frontend; +- +- ret = frontend_init(dev); +- if (ret < 0) +- goto err_dvb_net; +- +- dm1105_ir_init(dev); +- +- INIT_WORK(&dev->work, dm1105_dmx_buffer); +- sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); +- dev->wq = create_singlethread_workqueue(dev->wqn); +- if (!dev->wq) +- goto err_dvb_net; +- +- ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED, +- DRIVER_NAME, dev); +- if (ret < 0) +- goto err_workqueue; +- +- return 0; +- +-err_workqueue: +- destroy_workqueue(dev->wq); +-err_dvb_net: +- dvb_net_release(&dev->dvbnet); +-err_disconnect_frontend: +- dmx->disconnect_frontend(dmx); +-err_remove_mem_frontend: +- dmx->remove_frontend(dmx, &dev->mem_frontend); +-err_remove_hw_frontend: +- dmx->remove_frontend(dmx, &dev->hw_frontend); +-err_dvb_dmxdev_release: +- dvb_dmxdev_release(&dev->dmxdev); +-err_dvb_dmx_release: +- dvb_dmx_release(dvbdemux); +-err_dvb_unregister_adapter: +- dvb_unregister_adapter(dvb_adapter); +-err_i2c_del_adapters: +- i2c_del_adapter(&dev->i2c_bb_adap); +-err_i2c_del_adapter: +- i2c_del_adapter(&dev->i2c_adap); +-err_dm1105_hw_exit: +- dm1105_hw_exit(dev); +-err_pci_iounmap: +- pci_iounmap(pdev, dev->io_mem); +-err_pci_release_regions: +- pci_release_regions(pdev); +-err_pci_disable_device: +- pci_disable_device(pdev); +-err_kfree: +- pci_set_drvdata(pdev, NULL); +- kfree(dev); +- return ret; +-} +- +-static void __devexit dm1105_remove(struct pci_dev *pdev) +-{ +- struct dm1105_dev *dev = pci_get_drvdata(pdev); +- struct dvb_adapter *dvb_adapter = &dev->dvb_adapter; +- struct dvb_demux *dvbdemux = &dev->demux; +- struct dmx_demux *dmx = &dvbdemux->dmx; +- +- dm1105_ir_exit(dev); +- dmx->close(dmx); +- dvb_net_release(&dev->dvbnet); +- if (dev->fe) +- dvb_unregister_frontend(dev->fe); +- +- dmx->disconnect_frontend(dmx); +- dmx->remove_frontend(dmx, &dev->mem_frontend); +- dmx->remove_frontend(dmx, &dev->hw_frontend); +- dvb_dmxdev_release(&dev->dmxdev); +- dvb_dmx_release(dvbdemux); +- dvb_unregister_adapter(dvb_adapter); +- if (&dev->i2c_adap) +- i2c_del_adapter(&dev->i2c_adap); +- +- dm1105_hw_exit(dev); +- synchronize_irq(pdev->irq); +- free_irq(pdev->irq, dev); +- pci_iounmap(pdev, dev->io_mem); +- pci_release_regions(pdev); +- pci_disable_device(pdev); +- pci_set_drvdata(pdev, NULL); +- dm1105_devcount--; +- kfree(dev); +-} +- +-static struct pci_device_id dm1105_id_table[] __devinitdata = { +- { +- .vendor = PCI_VENDOR_ID_TRIGEM, +- .device = PCI_DEVICE_ID_DM1105, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- }, { +- .vendor = PCI_VENDOR_ID_AXESS, +- .device = PCI_DEVICE_ID_DM05, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- }, { +- /* empty */ +- }, +-}; +- +-MODULE_DEVICE_TABLE(pci, dm1105_id_table); +- +-static struct pci_driver dm1105_driver = { +- .name = DRIVER_NAME, +- .id_table = dm1105_id_table, +- .probe = dm1105_probe, +- .remove = __devexit_p(dm1105_remove), +-}; +- +-static int __init dm1105_init(void) +-{ +- return pci_register_driver(&dm1105_driver); +-} +- +-static void __exit dm1105_exit(void) +-{ +- pci_unregister_driver(&dm1105_driver); +-} +- +-module_init(dm1105_init); +-module_exit(dm1105_exit); +- +-MODULE_AUTHOR("Igor M. Liplianin "); +-MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-core/Makefile b/drivers/media/dvb/dvb-core/Makefile +deleted file mode 100644 +index 8f22bcd..0000000 +--- a/drivers/media/dvb/dvb-core/Makefile ++++ /dev/null +@@ -1,11 +0,0 @@ +-# +-# Makefile for the kernel DVB device drivers. +-# +- +-dvb-net-$(CONFIG_DVB_NET) := dvb_net.o +- +-dvb-core-objs := dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \ +- dvb_ca_en50221.o dvb_frontend.o \ +- $(dvb-net-y) dvb_ringbuffer.o dvb_math.o +- +-obj-$(CONFIG_DVB_CORE) += dvb-core.o +diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h +deleted file mode 100644 +index eb91fd8..0000000 +--- a/drivers/media/dvb/dvb-core/demux.h ++++ /dev/null +@@ -1,280 +0,0 @@ +-/* +- * demux.h +- * +- * Copyright (c) 2002 Convergence GmbH +- * +- * based on code: +- * Copyright (c) 2000 Nokia Research Center +- * Tampere, FINLAND +- * +- * 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. +- * +- */ +- +-#ifndef __DEMUX_H +-#define __DEMUX_H +- +-#include +-#include +-#include +-#include +-#include +- +-/*--------------------------------------------------------------------------*/ +-/* Common definitions */ +-/*--------------------------------------------------------------------------*/ +- +-/* +- * DMX_MAX_FILTER_SIZE: Maximum length (in bytes) of a section/PES filter. +- */ +- +-#ifndef DMX_MAX_FILTER_SIZE +-#define DMX_MAX_FILTER_SIZE 18 +-#endif +- +-/* +- * DMX_MAX_SECFEED_SIZE: Maximum length (in bytes) of a private section feed filter. +- */ +- +-#ifndef DMX_MAX_SECTION_SIZE +-#define DMX_MAX_SECTION_SIZE 4096 +-#endif +-#ifndef DMX_MAX_SECFEED_SIZE +-#define DMX_MAX_SECFEED_SIZE (DMX_MAX_SECTION_SIZE + 188) +-#endif +- +- +-/* +- * enum dmx_success: Success codes for the Demux Callback API. +- */ +- +-enum dmx_success { +- DMX_OK = 0, /* Received Ok */ +- DMX_LENGTH_ERROR, /* Incorrect length */ +- DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */ +- DMX_CRC_ERROR, /* Incorrect CRC */ +- DMX_FRAME_ERROR, /* Frame alignment error */ +- DMX_FIFO_ERROR, /* Receiver FIFO overrun */ +- DMX_MISSED_ERROR /* Receiver missed packet */ +-} ; +- +-/*--------------------------------------------------------------------------*/ +-/* TS packet reception */ +-/*--------------------------------------------------------------------------*/ +- +-/* TS filter type for set() */ +- +-#define TS_PACKET 1 /* send TS packets (188 bytes) to callback (default) */ +-#define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS +- payload (<=184 bytes per packet) to callback */ +-#define TS_DECODER 4 /* send stream to built-in decoder (if present) */ +-#define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to +- the demux device, not to the dvr device */ +- +-/* PES type for filters which write to built-in decoder */ +-/* these should be kept identical to the types in dmx.h */ +- +-enum dmx_ts_pes +-{ /* also send packets to decoder (if it exists) */ +- DMX_TS_PES_AUDIO0, +- DMX_TS_PES_VIDEO0, +- DMX_TS_PES_TELETEXT0, +- DMX_TS_PES_SUBTITLE0, +- DMX_TS_PES_PCR0, +- +- DMX_TS_PES_AUDIO1, +- DMX_TS_PES_VIDEO1, +- DMX_TS_PES_TELETEXT1, +- DMX_TS_PES_SUBTITLE1, +- DMX_TS_PES_PCR1, +- +- DMX_TS_PES_AUDIO2, +- DMX_TS_PES_VIDEO2, +- DMX_TS_PES_TELETEXT2, +- DMX_TS_PES_SUBTITLE2, +- DMX_TS_PES_PCR2, +- +- DMX_TS_PES_AUDIO3, +- DMX_TS_PES_VIDEO3, +- DMX_TS_PES_TELETEXT3, +- DMX_TS_PES_SUBTITLE3, +- DMX_TS_PES_PCR3, +- +- DMX_TS_PES_OTHER +-}; +- +-#define DMX_TS_PES_AUDIO DMX_TS_PES_AUDIO0 +-#define DMX_TS_PES_VIDEO DMX_TS_PES_VIDEO0 +-#define DMX_TS_PES_TELETEXT DMX_TS_PES_TELETEXT0 +-#define DMX_TS_PES_SUBTITLE DMX_TS_PES_SUBTITLE0 +-#define DMX_TS_PES_PCR DMX_TS_PES_PCR0 +- +- +-struct dmx_ts_feed { +- int is_filtering; /* Set to non-zero when filtering in progress */ +- struct dmx_demux *parent; /* Back-pointer */ +- void *priv; /* Pointer to private data of the API client */ +- int (*set) (struct dmx_ts_feed *feed, +- u16 pid, +- int type, +- enum dmx_ts_pes pes_type, +- size_t circular_buffer_size, +- struct timespec timeout); +- int (*start_filtering) (struct dmx_ts_feed* feed); +- int (*stop_filtering) (struct dmx_ts_feed* feed); +-}; +- +-/*--------------------------------------------------------------------------*/ +-/* Section reception */ +-/*--------------------------------------------------------------------------*/ +- +-struct dmx_section_filter { +- u8 filter_value [DMX_MAX_FILTER_SIZE]; +- u8 filter_mask [DMX_MAX_FILTER_SIZE]; +- u8 filter_mode [DMX_MAX_FILTER_SIZE]; +- struct dmx_section_feed* parent; /* Back-pointer */ +- void* priv; /* Pointer to private data of the API client */ +-}; +- +-struct dmx_section_feed { +- int is_filtering; /* Set to non-zero when filtering in progress */ +- struct dmx_demux* parent; /* Back-pointer */ +- void* priv; /* Pointer to private data of the API client */ +- +- int check_crc; +- u32 crc_val; +- +- u8 *secbuf; +- u8 secbuf_base[DMX_MAX_SECFEED_SIZE]; +- u16 secbufp, seclen, tsfeedp; +- +- int (*set) (struct dmx_section_feed* feed, +- u16 pid, +- size_t circular_buffer_size, +- int check_crc); +- int (*allocate_filter) (struct dmx_section_feed* feed, +- struct dmx_section_filter** filter); +- int (*release_filter) (struct dmx_section_feed* feed, +- struct dmx_section_filter* filter); +- int (*start_filtering) (struct dmx_section_feed* feed); +- int (*stop_filtering) (struct dmx_section_feed* feed); +-}; +- +-/*--------------------------------------------------------------------------*/ +-/* Callback functions */ +-/*--------------------------------------------------------------------------*/ +- +-typedef int (*dmx_ts_cb) ( const u8 * buffer1, +- size_t buffer1_length, +- const u8 * buffer2, +- size_t buffer2_length, +- struct dmx_ts_feed* source, +- enum dmx_success success); +- +-typedef int (*dmx_section_cb) ( const u8 * buffer1, +- size_t buffer1_len, +- const u8 * buffer2, +- size_t buffer2_len, +- struct dmx_section_filter * source, +- enum dmx_success success); +- +-/*--------------------------------------------------------------------------*/ +-/* DVB Front-End */ +-/*--------------------------------------------------------------------------*/ +- +-enum dmx_frontend_source { +- DMX_MEMORY_FE, +- DMX_FRONTEND_0, +- DMX_FRONTEND_1, +- DMX_FRONTEND_2, +- DMX_FRONTEND_3, +- DMX_STREAM_0, /* external stream input, e.g. LVDS */ +- DMX_STREAM_1, +- DMX_STREAM_2, +- DMX_STREAM_3 +-}; +- +-struct dmx_frontend { +- struct list_head connectivity_list; /* List of front-ends that can +- be connected to a particular +- demux */ +- enum dmx_frontend_source source; +-}; +- +-/*--------------------------------------------------------------------------*/ +-/* MPEG-2 TS Demux */ +-/*--------------------------------------------------------------------------*/ +- +-/* +- * Flags OR'ed in the capabilities field of struct dmx_demux. +- */ +- +-#define DMX_TS_FILTERING 1 +-#define DMX_PES_FILTERING 2 +-#define DMX_SECTION_FILTERING 4 +-#define DMX_MEMORY_BASED_FILTERING 8 /* write() available */ +-#define DMX_CRC_CHECKING 16 +-#define DMX_TS_DESCRAMBLING 32 +- +-/* +- * Demux resource type identifier. +-*/ +- +-/* +- * DMX_FE_ENTRY(): Casts elements in the list of registered +- * front-ends from the generic type struct list_head +- * to the type * struct dmx_frontend +- *. +-*/ +- +-#define DMX_FE_ENTRY(list) list_entry(list, struct dmx_frontend, connectivity_list) +- +-struct dmx_demux { +- u32 capabilities; /* Bitfield of capability flags */ +- struct dmx_frontend* frontend; /* Front-end connected to the demux */ +- void* priv; /* Pointer to private data of the API client */ +- int (*open) (struct dmx_demux* demux); +- int (*close) (struct dmx_demux* demux); +- int (*write) (struct dmx_demux* demux, const char __user *buf, size_t count); +- int (*allocate_ts_feed) (struct dmx_demux* demux, +- struct dmx_ts_feed** feed, +- dmx_ts_cb callback); +- int (*release_ts_feed) (struct dmx_demux* demux, +- struct dmx_ts_feed* feed); +- int (*allocate_section_feed) (struct dmx_demux* demux, +- struct dmx_section_feed** feed, +- dmx_section_cb callback); +- int (*release_section_feed) (struct dmx_demux* demux, +- struct dmx_section_feed* feed); +- int (*add_frontend) (struct dmx_demux* demux, +- struct dmx_frontend* frontend); +- int (*remove_frontend) (struct dmx_demux* demux, +- struct dmx_frontend* frontend); +- struct list_head* (*get_frontends) (struct dmx_demux* demux); +- int (*connect_frontend) (struct dmx_demux* demux, +- struct dmx_frontend* frontend); +- int (*disconnect_frontend) (struct dmx_demux* demux); +- +- int (*get_pes_pids) (struct dmx_demux* demux, u16 *pids); +- +- int (*get_caps) (struct dmx_demux* demux, struct dmx_caps *caps); +- +- int (*set_source) (struct dmx_demux* demux, const dmx_source_t *src); +- +- int (*get_stc) (struct dmx_demux* demux, unsigned int num, +- u64 *stc, unsigned int *base); +-}; +- +-#endif /* #ifndef __DEMUX_H */ +diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c +deleted file mode 100644 +index caca6e5..0000000 +--- a/drivers/media/dvb/dvb-core/dmxdev.c ++++ /dev/null +@@ -1,1299 +0,0 @@ +-/* +- * dmxdev.c - DVB demultiplexer device +- * +- * Copyright (C) 2000 Ralph Metzler & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dmxdev.h" +- +-static int debug; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define dprintk if (debug) printk +- +-static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf, +- const u8 *src, size_t len) +-{ +- ssize_t free; +- +- if (!len) +- return 0; +- if (!buf->data) +- return 0; +- +- free = dvb_ringbuffer_free(buf); +- if (len > free) { +- dprintk("dmxdev: buffer overflow\n"); +- return -EOVERFLOW; +- } +- +- return dvb_ringbuffer_write(buf, src, len); +-} +- +-static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, +- int non_blocking, char __user *buf, +- size_t count, loff_t *ppos) +-{ +- size_t todo; +- ssize_t avail; +- ssize_t ret = 0; +- +- if (!src->data) +- return 0; +- +- if (src->error) { +- ret = src->error; +- dvb_ringbuffer_flush(src); +- return ret; +- } +- +- for (todo = count; todo > 0; todo -= ret) { +- if (non_blocking && dvb_ringbuffer_empty(src)) { +- ret = -EWOULDBLOCK; +- break; +- } +- +- +- ret = wait_event_interruptible(src->queue, +- !dvb_ringbuffer_empty(src) || +- (src->error != 0) || +- (src->do_wait != 1)); +- if (src->do_wait != 1) +- ret = -EINTR; +- +- if (ret < 0) +- break; +- +- if (src->error) { +- ret = src->error; +- dvb_ringbuffer_flush(src); +- break; +- } +- +- avail = dvb_ringbuffer_avail(src); +- if (avail > todo) +- avail = todo; +- +- ret = dvb_ringbuffer_read_user(src, buf, avail); +- if (ret < 0) +- break; +- +- buf += ret; +- } +- +- return (count - todo) ? (count - todo) : ret; +-} +- +-static struct dmx_frontend *get_fe(struct dmx_demux *demux, int type) +-{ +- struct list_head *head, *pos; +- +- head = demux->get_frontends(demux); +- if (!head) +- return NULL; +- list_for_each(pos, head) +- if (DMX_FE_ENTRY(pos)->source == type) +- return DMX_FE_ENTRY(pos); +- +- return NULL; +-} +- +-static int dvb_dvr_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- struct dmx_frontend *front; +- +- dprintk("function : %s\n", __func__); +- +- if (mutex_lock_interruptible(&dmxdev->mutex)) +- return -ERESTARTSYS; +- +- if (dmxdev->exit) { +- mutex_unlock(&dmxdev->mutex); +- return -ENODEV; +- } +- +- if ((file->f_flags & O_ACCMODE) == O_RDWR) { +- if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) { +- mutex_unlock(&dmxdev->mutex); +- return -EOPNOTSUPP; +- } +- } +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- void *mem; +- if (!dvbdev->readers) { +- mutex_unlock(&dmxdev->mutex); +- return -EBUSY; +- } +- mem = vmalloc(DVR_BUFFER_SIZE); +- if (!mem) { +- mutex_unlock(&dmxdev->mutex); +- return -ENOMEM; +- } +- dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); +- dvbdev->readers--; +- } +- +- if ((file->f_flags & O_ACCMODE) == O_WRONLY) { +- dmxdev->dvr_orig_fe = dmxdev->demux->frontend; +- +- if (!dmxdev->demux->write) { +- mutex_unlock(&dmxdev->mutex); +- return -EOPNOTSUPP; +- } +- +- front = get_fe(dmxdev->demux, DMX_MEMORY_FE); +- +- if (!front) { +- mutex_unlock(&dmxdev->mutex); +- return -EINVAL; +- } +- dmxdev->demux->disconnect_frontend(dmxdev->demux); +- dmxdev->demux->connect_frontend(dmxdev->demux, front); +- } +- dvbdev->users++; +- mutex_unlock(&dmxdev->mutex); +- return 0; +-} +- +-static int dvb_dvr_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- +- mutex_lock(&dmxdev->mutex); +- +- if ((file->f_flags & O_ACCMODE) == O_WRONLY) { +- dmxdev->demux->disconnect_frontend(dmxdev->demux); +- dmxdev->demux->connect_frontend(dmxdev->demux, +- dmxdev->dvr_orig_fe); +- } +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- dvbdev->readers++; +- if (dmxdev->dvr_buffer.data) { +- void *mem = dmxdev->dvr_buffer.data; +- mb(); +- spin_lock_irq(&dmxdev->lock); +- dmxdev->dvr_buffer.data = NULL; +- spin_unlock_irq(&dmxdev->lock); +- vfree(mem); +- } +- } +- /* TODO */ +- dvbdev->users--; +- if (dvbdev->users == 1 && dmxdev->exit == 1) { +- fops_put(file->f_op); +- file->f_op = NULL; +- mutex_unlock(&dmxdev->mutex); +- wake_up(&dvbdev->wait_queue); +- } else +- mutex_unlock(&dmxdev->mutex); +- +- return 0; +-} +- +-static ssize_t dvb_dvr_write(struct file *file, const char __user *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- int ret; +- +- if (!dmxdev->demux->write) +- return -EOPNOTSUPP; +- if ((file->f_flags & O_ACCMODE) != O_WRONLY) +- return -EINVAL; +- if (mutex_lock_interruptible(&dmxdev->mutex)) +- return -ERESTARTSYS; +- +- if (dmxdev->exit) { +- mutex_unlock(&dmxdev->mutex); +- return -ENODEV; +- } +- ret = dmxdev->demux->write(dmxdev->demux, buf, count); +- mutex_unlock(&dmxdev->mutex); +- return ret; +-} +- +-static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, +- loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- +- if (dmxdev->exit) +- return -ENODEV; +- +- return dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, +- file->f_flags & O_NONBLOCK, +- buf, count, ppos); +-} +- +-static int dvb_dvr_set_buffer_size(struct dmxdev *dmxdev, +- unsigned long size) +-{ +- struct dvb_ringbuffer *buf = &dmxdev->dvr_buffer; +- void *newmem; +- void *oldmem; +- +- dprintk("function : %s\n", __func__); +- +- if (buf->size == size) +- return 0; +- if (!size) +- return -EINVAL; +- +- newmem = vmalloc(size); +- if (!newmem) +- return -ENOMEM; +- +- oldmem = buf->data; +- +- spin_lock_irq(&dmxdev->lock); +- buf->data = newmem; +- buf->size = size; +- +- /* reset and not flush in case the buffer shrinks */ +- dvb_ringbuffer_reset(buf); +- spin_unlock_irq(&dmxdev->lock); +- +- vfree(oldmem); +- +- return 0; +-} +- +-static inline void dvb_dmxdev_filter_state_set(struct dmxdev_filter +- *dmxdevfilter, int state) +-{ +- spin_lock_irq(&dmxdevfilter->dev->lock); +- dmxdevfilter->state = state; +- spin_unlock_irq(&dmxdevfilter->dev->lock); +-} +- +-static int dvb_dmxdev_set_buffer_size(struct dmxdev_filter *dmxdevfilter, +- unsigned long size) +-{ +- struct dvb_ringbuffer *buf = &dmxdevfilter->buffer; +- void *newmem; +- void *oldmem; +- +- if (buf->size == size) +- return 0; +- if (!size) +- return -EINVAL; +- if (dmxdevfilter->state >= DMXDEV_STATE_GO) +- return -EBUSY; +- +- newmem = vmalloc(size); +- if (!newmem) +- return -ENOMEM; +- +- oldmem = buf->data; +- +- spin_lock_irq(&dmxdevfilter->dev->lock); +- buf->data = newmem; +- buf->size = size; +- +- /* reset and not flush in case the buffer shrinks */ +- dvb_ringbuffer_reset(buf); +- spin_unlock_irq(&dmxdevfilter->dev->lock); +- +- vfree(oldmem); +- +- return 0; +-} +- +-static void dvb_dmxdev_filter_timeout(unsigned long data) +-{ +- struct dmxdev_filter *dmxdevfilter = (struct dmxdev_filter *)data; +- +- dmxdevfilter->buffer.error = -ETIMEDOUT; +- spin_lock_irq(&dmxdevfilter->dev->lock); +- dmxdevfilter->state = DMXDEV_STATE_TIMEDOUT; +- spin_unlock_irq(&dmxdevfilter->dev->lock); +- wake_up(&dmxdevfilter->buffer.queue); +-} +- +-static void dvb_dmxdev_filter_timer(struct dmxdev_filter *dmxdevfilter) +-{ +- struct dmx_sct_filter_params *para = &dmxdevfilter->params.sec; +- +- del_timer(&dmxdevfilter->timer); +- if (para->timeout) { +- dmxdevfilter->timer.function = dvb_dmxdev_filter_timeout; +- dmxdevfilter->timer.data = (unsigned long)dmxdevfilter; +- dmxdevfilter->timer.expires = +- jiffies + 1 + (HZ / 2 + HZ * para->timeout) / 1000; +- add_timer(&dmxdevfilter->timer); +- } +-} +- +-static int dvb_dmxdev_section_callback(const u8 *buffer1, size_t buffer1_len, +- const u8 *buffer2, size_t buffer2_len, +- struct dmx_section_filter *filter, +- enum dmx_success success) +-{ +- struct dmxdev_filter *dmxdevfilter = filter->priv; +- int ret; +- +- if (dmxdevfilter->buffer.error) { +- wake_up(&dmxdevfilter->buffer.queue); +- return 0; +- } +- spin_lock(&dmxdevfilter->dev->lock); +- if (dmxdevfilter->state != DMXDEV_STATE_GO) { +- spin_unlock(&dmxdevfilter->dev->lock); +- return 0; +- } +- del_timer(&dmxdevfilter->timer); +- dprintk("dmxdev: section callback %02x %02x %02x %02x %02x %02x\n", +- buffer1[0], buffer1[1], +- buffer1[2], buffer1[3], buffer1[4], buffer1[5]); +- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer1, +- buffer1_len); +- if (ret == buffer1_len) { +- ret = dvb_dmxdev_buffer_write(&dmxdevfilter->buffer, buffer2, +- buffer2_len); +- } +- if (ret < 0) { +- dvb_ringbuffer_flush(&dmxdevfilter->buffer); +- dmxdevfilter->buffer.error = ret; +- } +- if (dmxdevfilter->params.sec.flags & DMX_ONESHOT) +- dmxdevfilter->state = DMXDEV_STATE_DONE; +- spin_unlock(&dmxdevfilter->dev->lock); +- wake_up(&dmxdevfilter->buffer.queue); +- return 0; +-} +- +-static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, +- const u8 *buffer2, size_t buffer2_len, +- struct dmx_ts_feed *feed, +- enum dmx_success success) +-{ +- struct dmxdev_filter *dmxdevfilter = feed->priv; +- struct dvb_ringbuffer *buffer; +- int ret; +- +- spin_lock(&dmxdevfilter->dev->lock); +- if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER) { +- spin_unlock(&dmxdevfilter->dev->lock); +- return 0; +- } +- +- if (dmxdevfilter->params.pes.output == DMX_OUT_TAP +- || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) +- buffer = &dmxdevfilter->buffer; +- else +- buffer = &dmxdevfilter->dev->dvr_buffer; +- if (buffer->error) { +- spin_unlock(&dmxdevfilter->dev->lock); +- wake_up(&buffer->queue); +- return 0; +- } +- ret = dvb_dmxdev_buffer_write(buffer, buffer1, buffer1_len); +- if (ret == buffer1_len) +- ret = dvb_dmxdev_buffer_write(buffer, buffer2, buffer2_len); +- if (ret < 0) { +- dvb_ringbuffer_flush(buffer); +- buffer->error = ret; +- } +- spin_unlock(&dmxdevfilter->dev->lock); +- wake_up(&buffer->queue); +- return 0; +-} +- +-/* stop feed but only mark the specified filter as stopped (state set) */ +-static int dvb_dmxdev_feed_stop(struct dmxdev_filter *dmxdevfilter) +-{ +- struct dmxdev_feed *feed; +- +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); +- +- switch (dmxdevfilter->type) { +- case DMXDEV_TYPE_SEC: +- del_timer(&dmxdevfilter->timer); +- dmxdevfilter->feed.sec->stop_filtering(dmxdevfilter->feed.sec); +- break; +- case DMXDEV_TYPE_PES: +- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) +- feed->ts->stop_filtering(feed->ts); +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-/* start feed associated with the specified filter */ +-static int dvb_dmxdev_feed_start(struct dmxdev_filter *filter) +-{ +- struct dmxdev_feed *feed; +- int ret; +- +- dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); +- +- switch (filter->type) { +- case DMXDEV_TYPE_SEC: +- return filter->feed.sec->start_filtering(filter->feed.sec); +- case DMXDEV_TYPE_PES: +- list_for_each_entry(feed, &filter->feed.ts, next) { +- ret = feed->ts->start_filtering(feed->ts); +- if (ret < 0) { +- dvb_dmxdev_feed_stop(filter); +- return ret; +- } +- } +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-/* restart section feed if it has filters left associated with it, +- otherwise release the feed */ +-static int dvb_dmxdev_feed_restart(struct dmxdev_filter *filter) +-{ +- int i; +- struct dmxdev *dmxdev = filter->dev; +- u16 pid = filter->params.sec.pid; +- +- for (i = 0; i < dmxdev->filternum; i++) +- if (dmxdev->filter[i].state >= DMXDEV_STATE_GO && +- dmxdev->filter[i].type == DMXDEV_TYPE_SEC && +- dmxdev->filter[i].params.sec.pid == pid) { +- dvb_dmxdev_feed_start(&dmxdev->filter[i]); +- return 0; +- } +- +- filter->dev->demux->release_section_feed(dmxdev->demux, +- filter->feed.sec); +- +- return 0; +-} +- +-static int dvb_dmxdev_filter_stop(struct dmxdev_filter *dmxdevfilter) +-{ +- struct dmxdev_feed *feed; +- struct dmx_demux *demux; +- +- if (dmxdevfilter->state < DMXDEV_STATE_GO) +- return 0; +- +- switch (dmxdevfilter->type) { +- case DMXDEV_TYPE_SEC: +- if (!dmxdevfilter->feed.sec) +- break; +- dvb_dmxdev_feed_stop(dmxdevfilter); +- if (dmxdevfilter->filter.sec) +- dmxdevfilter->feed.sec-> +- release_filter(dmxdevfilter->feed.sec, +- dmxdevfilter->filter.sec); +- dvb_dmxdev_feed_restart(dmxdevfilter); +- dmxdevfilter->feed.sec = NULL; +- break; +- case DMXDEV_TYPE_PES: +- dvb_dmxdev_feed_stop(dmxdevfilter); +- demux = dmxdevfilter->dev->demux; +- list_for_each_entry(feed, &dmxdevfilter->feed.ts, next) { +- demux->release_ts_feed(demux, feed->ts); +- feed->ts = NULL; +- } +- break; +- default: +- if (dmxdevfilter->state == DMXDEV_STATE_ALLOCATED) +- return 0; +- return -EINVAL; +- } +- +- dvb_ringbuffer_flush(&dmxdevfilter->buffer); +- return 0; +-} +- +-static void dvb_dmxdev_delete_pids(struct dmxdev_filter *dmxdevfilter) +-{ +- struct dmxdev_feed *feed, *tmp; +- +- /* delete all PIDs */ +- list_for_each_entry_safe(feed, tmp, &dmxdevfilter->feed.ts, next) { +- list_del(&feed->next); +- kfree(feed); +- } +- +- BUG_ON(!list_empty(&dmxdevfilter->feed.ts)); +-} +- +-static inline int dvb_dmxdev_filter_reset(struct dmxdev_filter *dmxdevfilter) +-{ +- if (dmxdevfilter->state < DMXDEV_STATE_SET) +- return 0; +- +- if (dmxdevfilter->type == DMXDEV_TYPE_PES) +- dvb_dmxdev_delete_pids(dmxdevfilter); +- +- dmxdevfilter->type = DMXDEV_TYPE_NONE; +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); +- return 0; +-} +- +-static int dvb_dmxdev_start_feed(struct dmxdev *dmxdev, +- struct dmxdev_filter *filter, +- struct dmxdev_feed *feed) +-{ +- struct timespec timeout = { 0 }; +- struct dmx_pes_filter_params *para = &filter->params.pes; +- dmx_output_t otype; +- int ret; +- int ts_type; +- dmx_pes_type_t ts_pes; +- struct dmx_ts_feed *tsfeed; +- +- feed->ts = NULL; +- otype = para->output; +- +- ts_pes = para->pes_type; +- +- if (ts_pes < DMX_PES_OTHER) +- ts_type = TS_DECODER; +- else +- ts_type = 0; +- +- if (otype == DMX_OUT_TS_TAP) +- ts_type |= TS_PACKET; +- else if (otype == DMX_OUT_TSDEMUX_TAP) +- ts_type |= TS_PACKET | TS_DEMUX; +- else if (otype == DMX_OUT_TAP) +- ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; +- +- ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, &feed->ts, +- dvb_dmxdev_ts_callback); +- if (ret < 0) +- return ret; +- +- tsfeed = feed->ts; +- tsfeed->priv = filter; +- +- ret = tsfeed->set(tsfeed, feed->pid, ts_type, ts_pes, 32768, timeout); +- if (ret < 0) { +- dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); +- return ret; +- } +- +- ret = tsfeed->start_filtering(tsfeed); +- if (ret < 0) { +- dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed); +- return ret; +- } +- +- return 0; +-} +- +-static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) +-{ +- struct dmxdev *dmxdev = filter->dev; +- struct dmxdev_feed *feed; +- void *mem; +- int ret, i; +- +- if (filter->state < DMXDEV_STATE_SET) +- return -EINVAL; +- +- if (filter->state >= DMXDEV_STATE_GO) +- dvb_dmxdev_filter_stop(filter); +- +- if (!filter->buffer.data) { +- mem = vmalloc(filter->buffer.size); +- if (!mem) +- return -ENOMEM; +- spin_lock_irq(&filter->dev->lock); +- filter->buffer.data = mem; +- spin_unlock_irq(&filter->dev->lock); +- } +- +- dvb_ringbuffer_flush(&filter->buffer); +- +- switch (filter->type) { +- case DMXDEV_TYPE_SEC: +- { +- struct dmx_sct_filter_params *para = &filter->params.sec; +- struct dmx_section_filter **secfilter = &filter->filter.sec; +- struct dmx_section_feed **secfeed = &filter->feed.sec; +- +- *secfilter = NULL; +- *secfeed = NULL; +- +- +- /* find active filter/feed with same PID */ +- for (i = 0; i < dmxdev->filternum; i++) { +- if (dmxdev->filter[i].state >= DMXDEV_STATE_GO && +- dmxdev->filter[i].type == DMXDEV_TYPE_SEC && +- dmxdev->filter[i].params.sec.pid == para->pid) { +- *secfeed = dmxdev->filter[i].feed.sec; +- break; +- } +- } +- +- /* if no feed found, try to allocate new one */ +- if (!*secfeed) { +- ret = dmxdev->demux->allocate_section_feed(dmxdev->demux, +- secfeed, +- dvb_dmxdev_section_callback); +- if (ret < 0) { +- printk("DVB (%s): could not alloc feed\n", +- __func__); +- return ret; +- } +- +- ret = (*secfeed)->set(*secfeed, para->pid, 32768, +- (para->flags & DMX_CHECK_CRC) ? 1 : 0); +- if (ret < 0) { +- printk("DVB (%s): could not set feed\n", +- __func__); +- dvb_dmxdev_feed_restart(filter); +- return ret; +- } +- } else { +- dvb_dmxdev_feed_stop(filter); +- } +- +- ret = (*secfeed)->allocate_filter(*secfeed, secfilter); +- if (ret < 0) { +- dvb_dmxdev_feed_restart(filter); +- filter->feed.sec->start_filtering(*secfeed); +- dprintk("could not get filter\n"); +- return ret; +- } +- +- (*secfilter)->priv = filter; +- +- memcpy(&((*secfilter)->filter_value[3]), +- &(para->filter.filter[1]), DMX_FILTER_SIZE - 1); +- memcpy(&(*secfilter)->filter_mask[3], +- ¶->filter.mask[1], DMX_FILTER_SIZE - 1); +- memcpy(&(*secfilter)->filter_mode[3], +- ¶->filter.mode[1], DMX_FILTER_SIZE - 1); +- +- (*secfilter)->filter_value[0] = para->filter.filter[0]; +- (*secfilter)->filter_mask[0] = para->filter.mask[0]; +- (*secfilter)->filter_mode[0] = para->filter.mode[0]; +- (*secfilter)->filter_mask[1] = 0; +- (*secfilter)->filter_mask[2] = 0; +- +- filter->todo = 0; +- +- ret = filter->feed.sec->start_filtering(filter->feed.sec); +- if (ret < 0) +- return ret; +- +- dvb_dmxdev_filter_timer(filter); +- break; +- } +- case DMXDEV_TYPE_PES: +- list_for_each_entry(feed, &filter->feed.ts, next) { +- ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); +- if (ret < 0) { +- dvb_dmxdev_filter_stop(filter); +- return ret; +- } +- } +- break; +- default: +- return -EINVAL; +- } +- +- dvb_dmxdev_filter_state_set(filter, DMXDEV_STATE_GO); +- return 0; +-} +- +-static int dvb_demux_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- int i; +- struct dmxdev_filter *dmxdevfilter; +- +- if (!dmxdev->filter) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&dmxdev->mutex)) +- return -ERESTARTSYS; +- +- for (i = 0; i < dmxdev->filternum; i++) +- if (dmxdev->filter[i].state == DMXDEV_STATE_FREE) +- break; +- +- if (i == dmxdev->filternum) { +- mutex_unlock(&dmxdev->mutex); +- return -EMFILE; +- } +- +- dmxdevfilter = &dmxdev->filter[i]; +- mutex_init(&dmxdevfilter->mutex); +- file->private_data = dmxdevfilter; +- +- dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192); +- dmxdevfilter->type = DMXDEV_TYPE_NONE; +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED); +- init_timer(&dmxdevfilter->timer); +- +- dvbdev->users++; +- +- mutex_unlock(&dmxdev->mutex); +- return 0; +-} +- +-static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, +- struct dmxdev_filter *dmxdevfilter) +-{ +- mutex_lock(&dmxdev->mutex); +- mutex_lock(&dmxdevfilter->mutex); +- +- dvb_dmxdev_filter_stop(dmxdevfilter); +- dvb_dmxdev_filter_reset(dmxdevfilter); +- +- if (dmxdevfilter->buffer.data) { +- void *mem = dmxdevfilter->buffer.data; +- +- spin_lock_irq(&dmxdev->lock); +- dmxdevfilter->buffer.data = NULL; +- spin_unlock_irq(&dmxdev->lock); +- vfree(mem); +- } +- +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE); +- wake_up(&dmxdevfilter->buffer.queue); +- mutex_unlock(&dmxdevfilter->mutex); +- mutex_unlock(&dmxdev->mutex); +- return 0; +-} +- +-static inline void invert_mode(dmx_filter_t *filter) +-{ +- int i; +- +- for (i = 0; i < DMX_FILTER_SIZE; i++) +- filter->mode[i] ^= 0xff; +-} +- +-static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, +- struct dmxdev_filter *filter, u16 pid) +-{ +- struct dmxdev_feed *feed; +- +- if ((filter->type != DMXDEV_TYPE_PES) || +- (filter->state < DMXDEV_STATE_SET)) +- return -EINVAL; +- +- /* only TS packet filters may have multiple PIDs */ +- if ((filter->params.pes.output != DMX_OUT_TSDEMUX_TAP) && +- (!list_empty(&filter->feed.ts))) +- return -EINVAL; +- +- feed = kzalloc(sizeof(struct dmxdev_feed), GFP_KERNEL); +- if (feed == NULL) +- return -ENOMEM; +- +- feed->pid = pid; +- list_add(&feed->next, &filter->feed.ts); +- +- if (filter->state >= DMXDEV_STATE_GO) +- return dvb_dmxdev_start_feed(dmxdev, filter, feed); +- +- return 0; +-} +- +-static int dvb_dmxdev_remove_pid(struct dmxdev *dmxdev, +- struct dmxdev_filter *filter, u16 pid) +-{ +- struct dmxdev_feed *feed, *tmp; +- +- if ((filter->type != DMXDEV_TYPE_PES) || +- (filter->state < DMXDEV_STATE_SET)) +- return -EINVAL; +- +- list_for_each_entry_safe(feed, tmp, &filter->feed.ts, next) { +- if ((feed->pid == pid) && (feed->ts != NULL)) { +- feed->ts->stop_filtering(feed->ts); +- filter->dev->demux->release_ts_feed(filter->dev->demux, +- feed->ts); +- list_del(&feed->next); +- kfree(feed); +- } +- } +- +- return 0; +-} +- +-static int dvb_dmxdev_filter_set(struct dmxdev *dmxdev, +- struct dmxdev_filter *dmxdevfilter, +- struct dmx_sct_filter_params *params) +-{ +- dprintk("function : %s\n", __func__); +- +- dvb_dmxdev_filter_stop(dmxdevfilter); +- +- dmxdevfilter->type = DMXDEV_TYPE_SEC; +- memcpy(&dmxdevfilter->params.sec, +- params, sizeof(struct dmx_sct_filter_params)); +- invert_mode(&dmxdevfilter->params.sec.filter); +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); +- +- if (params->flags & DMX_IMMEDIATE_START) +- return dvb_dmxdev_filter_start(dmxdevfilter); +- +- return 0; +-} +- +-static int dvb_dmxdev_pes_filter_set(struct dmxdev *dmxdev, +- struct dmxdev_filter *dmxdevfilter, +- struct dmx_pes_filter_params *params) +-{ +- int ret; +- +- dvb_dmxdev_filter_stop(dmxdevfilter); +- dvb_dmxdev_filter_reset(dmxdevfilter); +- +- if (params->pes_type > DMX_PES_OTHER || params->pes_type < 0) +- return -EINVAL; +- +- dmxdevfilter->type = DMXDEV_TYPE_PES; +- memcpy(&dmxdevfilter->params, params, +- sizeof(struct dmx_pes_filter_params)); +- INIT_LIST_HEAD(&dmxdevfilter->feed.ts); +- +- dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_SET); +- +- ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, +- dmxdevfilter->params.pes.pid); +- if (ret < 0) +- return ret; +- +- if (params->flags & DMX_IMMEDIATE_START) +- return dvb_dmxdev_filter_start(dmxdevfilter); +- +- return 0; +-} +- +-static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil, +- struct file *file, char __user *buf, +- size_t count, loff_t *ppos) +-{ +- int result, hcount; +- int done = 0; +- +- if (dfil->todo <= 0) { +- hcount = 3 + dfil->todo; +- if (hcount > count) +- hcount = count; +- result = dvb_dmxdev_buffer_read(&dfil->buffer, +- file->f_flags & O_NONBLOCK, +- buf, hcount, ppos); +- if (result < 0) { +- dfil->todo = 0; +- return result; +- } +- if (copy_from_user(dfil->secheader - dfil->todo, buf, result)) +- return -EFAULT; +- buf += result; +- done = result; +- count -= result; +- dfil->todo -= result; +- if (dfil->todo > -3) +- return done; +- dfil->todo = ((dfil->secheader[1] << 8) | dfil->secheader[2]) & 0xfff; +- if (!count) +- return done; +- } +- if (count > dfil->todo) +- count = dfil->todo; +- result = dvb_dmxdev_buffer_read(&dfil->buffer, +- file->f_flags & O_NONBLOCK, +- buf, count, ppos); +- if (result < 0) +- return result; +- dfil->todo -= result; +- return (result + done); +-} +- +-static ssize_t +-dvb_demux_read(struct file *file, char __user *buf, size_t count, +- loff_t *ppos) +-{ +- struct dmxdev_filter *dmxdevfilter = file->private_data; +- int ret; +- +- if (mutex_lock_interruptible(&dmxdevfilter->mutex)) +- return -ERESTARTSYS; +- +- if (dmxdevfilter->type == DMXDEV_TYPE_SEC) +- ret = dvb_dmxdev_read_sec(dmxdevfilter, file, buf, count, ppos); +- else +- ret = dvb_dmxdev_buffer_read(&dmxdevfilter->buffer, +- file->f_flags & O_NONBLOCK, +- buf, count, ppos); +- +- mutex_unlock(&dmxdevfilter->mutex); +- return ret; +-} +- +-static int dvb_demux_lock_filter(struct dmxdev_filter *dmxdevfilter) +-{ +- int ret; +- +- dmxdevfilter->buffer.do_wait = 0; +- +- if (waitqueue_active(&dmxdevfilter->buffer.queue)) +- wake_up(&dmxdevfilter->buffer.queue); +- +- ret = mutex_lock_interruptible(&dmxdevfilter->mutex); +- +- dmxdevfilter->buffer.do_wait = 1; +- +- return ret; +-} +- +-static int dvb_demux_do_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dmxdev_filter *dmxdevfilter = file->private_data; +- struct dmxdev *dmxdev = dmxdevfilter->dev; +- unsigned long arg = (unsigned long)parg; +- int ret = 0; +- +- if (mutex_lock_interruptible(&dmxdev->mutex)) +- return -ERESTARTSYS; +- +- switch (cmd) { +- case DMX_START: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- mutex_unlock(&dmxdev->mutex); +- return -ERESTARTSYS; +- } +- if (dmxdevfilter->state < DMXDEV_STATE_SET) +- ret = -EINVAL; +- else +- ret = dvb_dmxdev_filter_start(dmxdevfilter); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_STOP: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- mutex_unlock(&dmxdev->mutex); +- return -ERESTARTSYS; +- } +- ret = dvb_dmxdev_filter_stop(dmxdevfilter); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_SET_FILTER: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- mutex_unlock(&dmxdev->mutex); +- return -ERESTARTSYS; +- } +- ret = dvb_dmxdev_filter_set(dmxdev, dmxdevfilter, parg); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_SET_PES_FILTER: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- mutex_unlock(&dmxdev->mutex); +- return -ERESTARTSYS; +- } +- ret = dvb_dmxdev_pes_filter_set(dmxdev, dmxdevfilter, parg); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_SET_BUFFER_SIZE: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- mutex_unlock(&dmxdev->mutex); +- return -ERESTARTSYS; +- } +- ret = dvb_dmxdev_set_buffer_size(dmxdevfilter, arg); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_GET_PES_PIDS: +- if (!dmxdev->demux->get_pes_pids) { +- ret = -EINVAL; +- break; +- } +- dmxdev->demux->get_pes_pids(dmxdev->demux, parg); +- break; +- +- case DMX_GET_CAPS: +- if (!dmxdev->demux->get_caps) { +- ret = -EINVAL; +- break; +- } +- ret = dmxdev->demux->get_caps(dmxdev->demux, parg); +- break; +- +- case DMX_SET_SOURCE: +- if (!dmxdev->demux->set_source) { +- ret = -EINVAL; +- break; +- } +- ret = dmxdev->demux->set_source(dmxdev->demux, parg); +- break; +- +- case DMX_GET_STC: +- if (!dmxdev->demux->get_stc) { +- ret = -EINVAL; +- break; +- } +- ret = dmxdev->demux->get_stc(dmxdev->demux, +- ((struct dmx_stc *)parg)->num, +- &((struct dmx_stc *)parg)->stc, +- &((struct dmx_stc *)parg)->base); +- break; +- +- case DMX_ADD_PID: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- ret = -ERESTARTSYS; +- break; +- } +- ret = dvb_dmxdev_add_pid(dmxdev, dmxdevfilter, *(u16 *)parg); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- case DMX_REMOVE_PID: +- if (dvb_demux_lock_filter(dmxdevfilter)) { +- ret = -ERESTARTSYS; +- break; +- } +- ret = dvb_dmxdev_remove_pid(dmxdev, dmxdevfilter, *(u16 *)parg); +- mutex_unlock(&dmxdevfilter->mutex); +- break; +- +- default: +- ret = -EINVAL; +- break; +- } +- mutex_unlock(&dmxdev->mutex); +- return ret; +-} +- +-static long dvb_demux_ioctl(struct file *file, unsigned int cmd, +- unsigned long arg) +-{ +- return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl); +-} +- +-static unsigned int dvb_demux_poll(struct file *file, poll_table *wait) +-{ +- struct dmxdev_filter *dmxdevfilter = file->private_data; +- unsigned int mask = 0; +- +- if (!dmxdevfilter) +- return -EINVAL; +- +- poll_wait(file, &dmxdevfilter->buffer.queue, wait); +- +- if (dmxdevfilter->state != DMXDEV_STATE_GO && +- dmxdevfilter->state != DMXDEV_STATE_DONE && +- dmxdevfilter->state != DMXDEV_STATE_TIMEDOUT) +- return 0; +- +- if (dmxdevfilter->buffer.error) +- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); +- +- if (!dvb_ringbuffer_empty(&dmxdevfilter->buffer)) +- mask |= (POLLIN | POLLRDNORM | POLLPRI); +- +- return mask; +-} +- +-static int dvb_demux_release(struct inode *inode, struct file *file) +-{ +- struct dmxdev_filter *dmxdevfilter = file->private_data; +- struct dmxdev *dmxdev = dmxdevfilter->dev; +- +- int ret; +- +- ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); +- +- mutex_lock(&dmxdev->mutex); +- dmxdev->dvbdev->users--; +- if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) { +- fops_put(file->f_op); +- file->f_op = NULL; +- mutex_unlock(&dmxdev->mutex); +- wake_up(&dmxdev->dvbdev->wait_queue); +- } else +- mutex_unlock(&dmxdev->mutex); +- +- return ret; +-} +- +-static const struct file_operations dvb_demux_fops = { +- .owner = THIS_MODULE, +- .read = dvb_demux_read, +- .unlocked_ioctl = dvb_demux_ioctl, +- .open = dvb_demux_open, +- .release = dvb_demux_release, +- .poll = dvb_demux_poll, +- .llseek = default_llseek, +-}; +- +-static struct dvb_device dvbdev_demux = { +- .priv = NULL, +- .users = 1, +- .writers = 1, +- .fops = &dvb_demux_fops +-}; +- +-static int dvb_dvr_do_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- unsigned long arg = (unsigned long)parg; +- int ret; +- +- if (mutex_lock_interruptible(&dmxdev->mutex)) +- return -ERESTARTSYS; +- +- switch (cmd) { +- case DMX_SET_BUFFER_SIZE: +- ret = dvb_dvr_set_buffer_size(dmxdev, arg); +- break; +- +- default: +- ret = -EINVAL; +- break; +- } +- mutex_unlock(&dmxdev->mutex); +- return ret; +-} +- +-static long dvb_dvr_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl); +-} +- +-static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dmxdev *dmxdev = dvbdev->priv; +- unsigned int mask = 0; +- +- dprintk("function : %s\n", __func__); +- +- poll_wait(file, &dmxdev->dvr_buffer.queue, wait); +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- if (dmxdev->dvr_buffer.error) +- mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR); +- +- if (!dvb_ringbuffer_empty(&dmxdev->dvr_buffer)) +- mask |= (POLLIN | POLLRDNORM | POLLPRI); +- } else +- mask |= (POLLOUT | POLLWRNORM | POLLPRI); +- +- return mask; +-} +- +-static const struct file_operations dvb_dvr_fops = { +- .owner = THIS_MODULE, +- .read = dvb_dvr_read, +- .write = dvb_dvr_write, +- .unlocked_ioctl = dvb_dvr_ioctl, +- .open = dvb_dvr_open, +- .release = dvb_dvr_release, +- .poll = dvb_dvr_poll, +- .llseek = default_llseek, +-}; +- +-static struct dvb_device dvbdev_dvr = { +- .priv = NULL, +- .readers = 1, +- .users = 1, +- .fops = &dvb_dvr_fops +-}; +- +-int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter) +-{ +- int i; +- +- if (dmxdev->demux->open(dmxdev->demux) < 0) +- return -EUSERS; +- +- dmxdev->filter = vmalloc(dmxdev->filternum * sizeof(struct dmxdev_filter)); +- if (!dmxdev->filter) +- return -ENOMEM; +- +- mutex_init(&dmxdev->mutex); +- spin_lock_init(&dmxdev->lock); +- for (i = 0; i < dmxdev->filternum; i++) { +- dmxdev->filter[i].dev = dmxdev; +- dmxdev->filter[i].buffer.data = NULL; +- dvb_dmxdev_filter_state_set(&dmxdev->filter[i], +- DMXDEV_STATE_FREE); +- } +- +- dvb_register_device(dvb_adapter, &dmxdev->dvbdev, &dvbdev_demux, dmxdev, +- DVB_DEVICE_DEMUX); +- dvb_register_device(dvb_adapter, &dmxdev->dvr_dvbdev, &dvbdev_dvr, +- dmxdev, DVB_DEVICE_DVR); +- +- dvb_ringbuffer_init(&dmxdev->dvr_buffer, NULL, 8192); +- +- return 0; +-} +- +-EXPORT_SYMBOL(dvb_dmxdev_init); +- +-void dvb_dmxdev_release(struct dmxdev *dmxdev) +-{ +- dmxdev->exit=1; +- if (dmxdev->dvbdev->users > 1) { +- wait_event(dmxdev->dvbdev->wait_queue, +- dmxdev->dvbdev->users==1); +- } +- if (dmxdev->dvr_dvbdev->users > 1) { +- wait_event(dmxdev->dvr_dvbdev->wait_queue, +- dmxdev->dvr_dvbdev->users==1); +- } +- +- dvb_unregister_device(dmxdev->dvbdev); +- dvb_unregister_device(dmxdev->dvr_dvbdev); +- +- vfree(dmxdev->filter); +- dmxdev->filter = NULL; +- dmxdev->demux->close(dmxdev->demux); +-} +- +-EXPORT_SYMBOL(dvb_dmxdev_release); +diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h +deleted file mode 100644 +index 02ebe28..0000000 +--- a/drivers/media/dvb/dvb-core/dmxdev.h ++++ /dev/null +@@ -1,118 +0,0 @@ +-/* +- * dmxdev.h +- * +- * Copyright (C) 2000 Ralph Metzler & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * 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. +- * +- */ +- +-#ifndef _DMXDEV_H_ +-#define _DMXDEV_H_ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "dvbdev.h" +-#include "demux.h" +-#include "dvb_ringbuffer.h" +- +-enum dmxdev_type { +- DMXDEV_TYPE_NONE, +- DMXDEV_TYPE_SEC, +- DMXDEV_TYPE_PES, +-}; +- +-enum dmxdev_state { +- DMXDEV_STATE_FREE, +- DMXDEV_STATE_ALLOCATED, +- DMXDEV_STATE_SET, +- DMXDEV_STATE_GO, +- DMXDEV_STATE_DONE, +- DMXDEV_STATE_TIMEDOUT +-}; +- +-struct dmxdev_feed { +- u16 pid; +- struct dmx_ts_feed *ts; +- struct list_head next; +-}; +- +-struct dmxdev_filter { +- union { +- struct dmx_section_filter *sec; +- } filter; +- +- union { +- /* list of TS and PES feeds (struct dmxdev_feed) */ +- struct list_head ts; +- struct dmx_section_feed *sec; +- } feed; +- +- union { +- struct dmx_sct_filter_params sec; +- struct dmx_pes_filter_params pes; +- } params; +- +- enum dmxdev_type type; +- enum dmxdev_state state; +- struct dmxdev *dev; +- struct dvb_ringbuffer buffer; +- +- struct mutex mutex; +- +- /* only for sections */ +- struct timer_list timer; +- int todo; +- u8 secheader[3]; +-}; +- +- +-struct dmxdev { +- struct dvb_device *dvbdev; +- struct dvb_device *dvr_dvbdev; +- +- struct dmxdev_filter *filter; +- struct dmx_demux *demux; +- +- int filternum; +- int capabilities; +- +- unsigned int exit:1; +-#define DMXDEV_CAP_DUPLEX 1 +- struct dmx_frontend *dvr_orig_fe; +- +- struct dvb_ringbuffer dvr_buffer; +-#define DVR_BUFFER_SIZE (10*188*1024) +- +- struct mutex mutex; +- spinlock_t lock; +-}; +- +- +-int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *); +-void dvb_dmxdev_release(struct dmxdev *dmxdev); +- +-#endif /* _DMXDEV_H_ */ +diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +deleted file mode 100644 +index 9be65a3..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c ++++ /dev/null +@@ -1,1753 +0,0 @@ +-/* +- * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces +- * +- * Copyright (C) 2004 Andrew de Quincey +- * +- * Parts of this file were based on sources as follows: +- * +- * Copyright (C) 2003 Ralph Metzler +- * +- * based on code: +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_ca_en50221.h" +-#include "dvb_ringbuffer.h" +- +-static int dvb_ca_en50221_debug; +- +-module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644); +-MODULE_PARM_DESC(cam_debug, "enable verbose debug messages"); +- +-#define dprintk if (dvb_ca_en50221_debug) printk +- +-#define INIT_TIMEOUT_SECS 10 +- +-#define HOST_LINK_BUF_SIZE 0x200 +- +-#define RX_BUFFER_SIZE 65535 +- +-#define MAX_RX_PACKETS_PER_ITERATION 10 +- +-#define CTRLIF_DATA 0 +-#define CTRLIF_COMMAND 1 +-#define CTRLIF_STATUS 1 +-#define CTRLIF_SIZE_LOW 2 +-#define CTRLIF_SIZE_HIGH 3 +- +-#define CMDREG_HC 1 /* Host control */ +-#define CMDREG_SW 2 /* Size write */ +-#define CMDREG_SR 4 /* Size read */ +-#define CMDREG_RS 8 /* Reset interface */ +-#define CMDREG_FRIE 0x40 /* Enable FR interrupt */ +-#define CMDREG_DAIE 0x80 /* Enable DA interrupt */ +-#define IRQEN (CMDREG_DAIE) +- +-#define STATUSREG_RE 1 /* read error */ +-#define STATUSREG_WE 2 /* write error */ +-#define STATUSREG_FR 0x40 /* module free */ +-#define STATUSREG_DA 0x80 /* data available */ +-#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */ +- +- +-#define DVB_CA_SLOTSTATE_NONE 0 +-#define DVB_CA_SLOTSTATE_UNINITIALISED 1 +-#define DVB_CA_SLOTSTATE_RUNNING 2 +-#define DVB_CA_SLOTSTATE_INVALID 3 +-#define DVB_CA_SLOTSTATE_WAITREADY 4 +-#define DVB_CA_SLOTSTATE_VALIDATE 5 +-#define DVB_CA_SLOTSTATE_WAITFR 6 +-#define DVB_CA_SLOTSTATE_LINKINIT 7 +- +- +-/* Information on a CA slot */ +-struct dvb_ca_slot { +- +- /* current state of the CAM */ +- int slot_state; +- +- /* mutex used for serializing access to one CI slot */ +- struct mutex slot_lock; +- +- /* Number of CAMCHANGES that have occurred since last processing */ +- atomic_t camchange_count; +- +- /* Type of last CAMCHANGE */ +- int camchange_type; +- +- /* base address of CAM config */ +- u32 config_base; +- +- /* value to write into Config Control register */ +- u8 config_option; +- +- /* if 1, the CAM supports DA IRQs */ +- u8 da_irq_supported:1; +- +- /* size of the buffer to use when talking to the CAM */ +- int link_buf_size; +- +- /* buffer for incoming packets */ +- struct dvb_ringbuffer rx_buffer; +- +- /* timer used during various states of the slot */ +- unsigned long timeout; +-}; +- +-/* Private CA-interface information */ +-struct dvb_ca_private { +- +- /* pointer back to the public data structure */ +- struct dvb_ca_en50221 *pub; +- +- /* the DVB device */ +- struct dvb_device *dvbdev; +- +- /* Flags describing the interface (DVB_CA_FLAG_*) */ +- u32 flags; +- +- /* number of slots supported by this CA interface */ +- unsigned int slot_count; +- +- /* information on each slot */ +- struct dvb_ca_slot *slot_info; +- +- /* wait queues for read() and write() operations */ +- wait_queue_head_t wait_queue; +- +- /* PID of the monitoring thread */ +- struct task_struct *thread; +- +- /* Flag indicating if the CA device is open */ +- unsigned int open:1; +- +- /* Flag indicating the thread should wake up now */ +- unsigned int wakeup:1; +- +- /* Delay the main thread should use */ +- unsigned long delay; +- +- /* Slot to start looking for data to read from in the next user-space read operation */ +- int next_read_slot; +-}; +- +-static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); +-static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); +-static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount); +- +- +-/** +- * Safely find needle in haystack. +- * +- * @param haystack Buffer to look in. +- * @param hlen Number of bytes in haystack. +- * @param needle Buffer to find. +- * @param nlen Number of bytes in needle. +- * @return Pointer into haystack needle was found at, or NULL if not found. +- */ +-static char *findstr(char * haystack, int hlen, char * needle, int nlen) +-{ +- int i; +- +- if (hlen < nlen) +- return NULL; +- +- for (i = 0; i <= hlen - nlen; i++) { +- if (!strncmp(haystack + i, needle, nlen)) +- return haystack + i; +- } +- +- return NULL; +-} +- +- +- +-/* ******************************************************************************** */ +-/* EN50221 physical interface functions */ +- +- +-/** +- * Check CAM status. +- */ +-static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot) +-{ +- int slot_status; +- int cam_present_now; +- int cam_changed; +- +- /* IRQ mode */ +- if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) { +- return (atomic_read(&ca->slot_info[slot].camchange_count) != 0); +- } +- +- /* poll mode */ +- slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open); +- +- cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0; +- cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0; +- if (!cam_changed) { +- int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE); +- cam_changed = (cam_present_now != cam_present_old); +- } +- +- if (cam_changed) { +- if (!cam_present_now) { +- ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; +- } else { +- ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED; +- } +- atomic_set(&ca->slot_info[slot].camchange_count, 1); +- } else { +- if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) && +- (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) { +- // move to validate state if reset is completed +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE; +- } +- } +- +- return cam_changed; +-} +- +- +-/** +- * Wait for flags to become set on the STATUS register on a CAM interface, +- * checking for errors and timeout. +- * +- * @param ca CA instance. +- * @param slot Slot on interface. +- * @param waitfor Flags to wait for. +- * @param timeout_ms Timeout in milliseconds. +- * +- * @return 0 on success, nonzero on error. +- */ +-static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, +- u8 waitfor, int timeout_hz) +-{ +- unsigned long timeout; +- unsigned long start; +- +- dprintk("%s\n", __func__); +- +- /* loop until timeout elapsed */ +- start = jiffies; +- timeout = jiffies + timeout_hz; +- while (1) { +- /* read the status and check for error */ +- int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); +- if (res < 0) +- return -EIO; +- +- /* if we got the flags, it was successful! */ +- if (res & waitfor) { +- dprintk("%s succeeded timeout:%lu\n", __func__, jiffies - start); +- return 0; +- } +- +- /* check for timeout */ +- if (time_after(jiffies, timeout)) { +- break; +- } +- +- /* wait for a bit */ +- msleep(1); +- } +- +- dprintk("%s failed timeout:%lu\n", __func__, jiffies - start); +- +- /* if we get here, we've timed out */ +- return -ETIMEDOUT; +-} +- +- +-/** +- * Initialise the link layer connection to a CAM. +- * +- * @param ca CA instance. +- * @param slot Slot id. +- * +- * @return 0 on success, nonzero on failure. +- */ +-static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) +-{ +- int ret; +- int buf_size; +- u8 buf[2]; +- +- dprintk("%s\n", __func__); +- +- /* we'll be determining these during this function */ +- ca->slot_info[slot].da_irq_supported = 0; +- +- /* set the host link buffer size temporarily. it will be overwritten with the +- * real negotiated size later. */ +- ca->slot_info[slot].link_buf_size = 2; +- +- /* read the buffer size from the CAM */ +- if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) +- return ret; +- if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0) +- return ret; +- if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) +- return -EIO; +- if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) +- return ret; +- +- /* store it, and choose the minimum of our buffer and the CAM's buffer size */ +- buf_size = (buf[0] << 8) | buf[1]; +- if (buf_size > HOST_LINK_BUF_SIZE) +- buf_size = HOST_LINK_BUF_SIZE; +- ca->slot_info[slot].link_buf_size = buf_size; +- buf[0] = buf_size >> 8; +- buf[1] = buf_size & 0xff; +- dprintk("Chosen link buffer size of %i\n", buf_size); +- +- /* write the buffer size to the CAM */ +- if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) +- return ret; +- if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0) +- return ret; +- if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) +- return -EIO; +- if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) +- return ret; +- +- /* success */ +- return 0; +-} +- +-/** +- * Read a tuple from attribute memory. +- * +- * @param ca CA instance. +- * @param slot Slot id. +- * @param address Address to read from. Updated. +- * @param tupleType Tuple id byte. Updated. +- * @param tupleLength Tuple length. Updated. +- * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. +- * +- * @return 0 on success, nonzero on error. +- */ +-static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, +- int *address, int *tupleType, int *tupleLength, u8 * tuple) +-{ +- int i; +- int _tupleType; +- int _tupleLength; +- int _address = *address; +- +- /* grab the next tuple length and type */ +- if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) +- return _tupleType; +- if (_tupleType == 0xff) { +- dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType); +- *address += 2; +- *tupleType = _tupleType; +- *tupleLength = 0; +- return 0; +- } +- if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0) +- return _tupleLength; +- _address += 4; +- +- dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength); +- +- /* read in the whole tuple */ +- for (i = 0; i < _tupleLength; i++) { +- tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2)); +- dprintk(" 0x%02x: 0x%02x %c\n", +- i, tuple[i] & 0xff, +- ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); +- } +- _address += (_tupleLength * 2); +- +- // success +- *tupleType = _tupleType; +- *tupleLength = _tupleLength; +- *address = _address; +- return 0; +-} +- +- +-/** +- * Parse attribute memory of a CAM module, extracting Config register, and checking +- * it is a DVB CAM module. +- * +- * @param ca CA instance. +- * @param slot Slot id. +- * +- * @return 0 on success, <0 on failure. +- */ +-static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot) +-{ +- int address = 0; +- int tupleLength; +- int tupleType; +- u8 tuple[257]; +- char *dvb_str; +- int rasz; +- int status; +- int got_cftableentry = 0; +- int end_chain = 0; +- int i; +- u16 manfid = 0; +- u16 devid = 0; +- +- +- // CISTPL_DEVICE_0A +- if ((status = +- dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) +- return status; +- if (tupleType != 0x1D) +- return -EINVAL; +- +- +- +- // CISTPL_DEVICE_0C +- if ((status = +- dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) +- return status; +- if (tupleType != 0x1C) +- return -EINVAL; +- +- +- +- // CISTPL_VERS_1 +- if ((status = +- dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) +- return status; +- if (tupleType != 0x15) +- return -EINVAL; +- +- +- +- // CISTPL_MANFID +- if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, +- &tupleLength, tuple)) < 0) +- return status; +- if (tupleType != 0x20) +- return -EINVAL; +- if (tupleLength != 4) +- return -EINVAL; +- manfid = (tuple[1] << 8) | tuple[0]; +- devid = (tuple[3] << 8) | tuple[2]; +- +- +- +- // CISTPL_CONFIG +- if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, +- &tupleLength, tuple)) < 0) +- return status; +- if (tupleType != 0x1A) +- return -EINVAL; +- if (tupleLength < 3) +- return -EINVAL; +- +- /* extract the configbase */ +- rasz = tuple[0] & 3; +- if (tupleLength < (3 + rasz + 14)) +- return -EINVAL; +- ca->slot_info[slot].config_base = 0; +- for (i = 0; i < rasz + 1; i++) { +- ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i)); +- } +- +- /* check it contains the correct DVB string */ +- dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8); +- if (dvb_str == NULL) +- return -EINVAL; +- if (tupleLength < ((dvb_str - (char *) tuple) + 12)) +- return -EINVAL; +- +- /* is it a version we support? */ +- if (strncmp(dvb_str + 8, "1.00", 4)) { +- printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n", +- ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]); +- return -EINVAL; +- } +- +- /* process the CFTABLE_ENTRY tuples, and any after those */ +- while ((!end_chain) && (address < 0x1000)) { +- if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, +- &tupleLength, tuple)) < 0) +- return status; +- switch (tupleType) { +- case 0x1B: // CISTPL_CFTABLE_ENTRY +- if (tupleLength < (2 + 11 + 17)) +- break; +- +- /* if we've already parsed one, just use it */ +- if (got_cftableentry) +- break; +- +- /* get the config option */ +- ca->slot_info[slot].config_option = tuple[0] & 0x3f; +- +- /* OK, check it contains the correct strings */ +- if ((findstr((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) || +- (findstr((char *)tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) +- break; +- +- got_cftableentry = 1; +- break; +- +- case 0x14: // CISTPL_NO_LINK +- break; +- +- case 0xFF: // CISTPL_END +- end_chain = 1; +- break; +- +- default: /* Unknown tuple type - just skip this tuple and move to the next one */ +- dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, +- tupleLength); +- break; +- } +- } +- +- if ((address > 0x1000) || (!got_cftableentry)) +- return -EINVAL; +- +- dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n", +- manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); +- +- // success! +- return 0; +-} +- +- +-/** +- * Set CAM's configoption correctly. +- * +- * @param ca CA instance. +- * @param slot Slot containing the CAM. +- */ +-static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot) +-{ +- int configoption; +- +- dprintk("%s\n", __func__); +- +- /* set the config option */ +- ca->pub->write_attribute_mem(ca->pub, slot, +- ca->slot_info[slot].config_base, +- ca->slot_info[slot].config_option); +- +- /* check it */ +- configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base); +- dprintk("Set configoption 0x%x, read configoption 0x%x\n", +- ca->slot_info[slot].config_option, configoption & 0x3f); +- +- /* fine! */ +- return 0; +- +-} +- +- +-/** +- * This function talks to an EN50221 CAM control interface. It reads a buffer of +- * data from the CAM. The data can either be stored in a supplied buffer, or +- * automatically be added to the slot's rx_buffer. +- * +- * @param ca CA instance. +- * @param slot Slot to read from. +- * @param ebuf If non-NULL, the data will be written to this buffer. If NULL, +- * the data will be added into the buffering system as a normal fragment. +- * @param ecount Size of ebuf. Ignored if ebuf is NULL. +- * +- * @return Number of bytes read, or < 0 on error +- */ +-static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount) +-{ +- int bytes_read; +- int status; +- u8 buf[HOST_LINK_BUF_SIZE]; +- int i; +- +- dprintk("%s\n", __func__); +- +- /* check if we have space for a link buf in the rx_buffer */ +- if (ebuf == NULL) { +- int buf_free; +- +- if (ca->slot_info[slot].rx_buffer.data == NULL) { +- status = -EIO; +- goto exit; +- } +- buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer); +- +- if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) { +- status = -EAGAIN; +- goto exit; +- } +- } +- +- /* check if there is data available */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) +- goto exit; +- if (!(status & STATUSREG_DA)) { +- /* no data */ +- status = 0; +- goto exit; +- } +- +- /* read the amount of data */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) +- goto exit; +- bytes_read = status << 8; +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) +- goto exit; +- bytes_read |= status; +- +- /* check it will fit */ +- if (ebuf == NULL) { +- if (bytes_read > ca->slot_info[slot].link_buf_size) { +- printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", +- ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; +- status = -EIO; +- goto exit; +- } +- if (bytes_read < 2) { +- printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; +- status = -EIO; +- goto exit; +- } +- } else { +- if (bytes_read > ecount) { +- printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", +- ca->dvbdev->adapter->num); +- status = -EIO; +- goto exit; +- } +- } +- +- /* fill the buffer */ +- for (i = 0; i < bytes_read; i++) { +- /* read byte and check */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) +- goto exit; +- +- /* OK, store it in the buffer */ +- buf[i] = status; +- } +- +- /* check for read error (RE should now be 0) */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) +- goto exit; +- if (status & STATUSREG_RE) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; +- status = -EIO; +- goto exit; +- } +- +- /* OK, add it to the receive buffer, or copy into external buffer if supplied */ +- if (ebuf == NULL) { +- if (ca->slot_info[slot].rx_buffer.data == NULL) { +- status = -EIO; +- goto exit; +- } +- dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read); +- } else { +- memcpy(ebuf, buf, bytes_read); +- } +- +- dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, +- buf[0], (buf[1] & 0x80) == 0, bytes_read); +- +- /* wake up readers when a last_fragment is received */ +- if ((buf[1] & 0x80) == 0x00) { +- wake_up_interruptible(&ca->wait_queue); +- } +- status = bytes_read; +- +-exit: +- return status; +-} +- +- +-/** +- * This function talks to an EN50221 CAM control interface. It writes a buffer of data +- * to a CAM. +- * +- * @param ca CA instance. +- * @param slot Slot to write to. +- * @param ebuf The data in this buffer is treated as a complete link-level packet to +- * be written. +- * @param count Size of ebuf. +- * +- * @return Number of bytes written, or < 0 on error. +- */ +-static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write) +-{ +- int status; +- int i; +- +- dprintk("%s\n", __func__); +- +- +- /* sanity check */ +- if (bytes_write > ca->slot_info[slot].link_buf_size) +- return -EINVAL; +- +- /* it is possible we are dealing with a single buffer implementation, +- thus if there is data available for read or if there is even a read +- already in progress, we do nothing but awake the kernel thread to +- process the data if necessary. */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) +- goto exitnowrite; +- if (status & (STATUSREG_DA | STATUSREG_RE)) { +- if (status & STATUSREG_DA) +- dvb_ca_en50221_thread_wakeup(ca); +- +- status = -EAGAIN; +- goto exitnowrite; +- } +- +- /* OK, set HC bit */ +- if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, +- IRQEN | CMDREG_HC)) != 0) +- goto exit; +- +- /* check if interface is still free */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) +- goto exit; +- if (!(status & STATUSREG_FR)) { +- /* it wasn't free => try again later */ +- status = -EAGAIN; +- goto exit; +- } +- +- /* send the amount of data */ +- if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) +- goto exit; +- if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, +- bytes_write & 0xff)) != 0) +- goto exit; +- +- /* send the buffer */ +- for (i = 0; i < bytes_write; i++) { +- if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) +- goto exit; +- } +- +- /* check for write error (WE should now be 0) */ +- if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) +- goto exit; +- if (status & STATUSREG_WE) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; +- status = -EIO; +- goto exit; +- } +- status = bytes_write; +- +- dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, +- buf[0], (buf[1] & 0x80) == 0, bytes_write); +- +-exit: +- ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); +- +-exitnowrite: +- return status; +-} +-EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq); +- +- +- +-/* ******************************************************************************** */ +-/* EN50221 higher level functions */ +- +- +-/** +- * A CAM has been removed => shut it down. +- * +- * @param ca CA instance. +- * @param slot Slot to shut down. +- */ +-static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot) +-{ +- dprintk("%s\n", __func__); +- +- ca->pub->slot_shutdown(ca->pub, slot); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; +- +- /* need to wake up all processes to check if they're now +- trying to write to a defunct CAM */ +- wake_up_interruptible(&ca->wait_queue); +- +- dprintk("Slot %i shutdown\n", slot); +- +- /* success */ +- return 0; +-} +-EXPORT_SYMBOL(dvb_ca_en50221_camready_irq); +- +- +-/** +- * A CAMCHANGE IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- * @param change_type One of the DVB_CA_CAMCHANGE_* values. +- */ +-void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type) +-{ +- struct dvb_ca_private *ca = pubca->private; +- +- dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type); +- +- switch (change_type) { +- case DVB_CA_EN50221_CAMCHANGE_REMOVED: +- case DVB_CA_EN50221_CAMCHANGE_INSERTED: +- break; +- +- default: +- return; +- } +- +- ca->slot_info[slot].camchange_type = change_type; +- atomic_inc(&ca->slot_info[slot].camchange_count); +- dvb_ca_en50221_thread_wakeup(ca); +-} +-EXPORT_SYMBOL(dvb_ca_en50221_frda_irq); +- +- +-/** +- * A CAMREADY IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- */ +-void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot) +-{ +- struct dvb_ca_private *ca = pubca->private; +- +- dprintk("CAMREADY IRQ slot:%i\n", slot); +- +- if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE; +- dvb_ca_en50221_thread_wakeup(ca); +- } +-} +- +- +-/** +- * An FR or DA IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- */ +-void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot) +-{ +- struct dvb_ca_private *ca = pubca->private; +- int flags; +- +- dprintk("FR/DA IRQ slot:%i\n", slot); +- +- switch (ca->slot_info[slot].slot_state) { +- case DVB_CA_SLOTSTATE_LINKINIT: +- flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS); +- if (flags & STATUSREG_DA) { +- dprintk("CAM supports DA IRQ\n"); +- ca->slot_info[slot].da_irq_supported = 1; +- } +- break; +- +- case DVB_CA_SLOTSTATE_RUNNING: +- if (ca->open) +- dvb_ca_en50221_thread_wakeup(ca); +- break; +- } +-} +- +- +- +-/* ******************************************************************************** */ +-/* EN50221 thread functions */ +- +-/** +- * Wake up the DVB CA thread +- * +- * @param ca CA instance. +- */ +-static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca) +-{ +- +- dprintk("%s\n", __func__); +- +- ca->wakeup = 1; +- mb(); +- wake_up_process(ca->thread); +-} +- +-/** +- * Update the delay used by the thread. +- * +- * @param ca CA instance. +- */ +-static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca) +-{ +- int delay; +- int curdelay = 100000000; +- int slot; +- +- /* Beware of too high polling frequency, because one polling +- * call might take several hundred milliseconds until timeout! +- */ +- for (slot = 0; slot < ca->slot_count; slot++) { +- switch (ca->slot_info[slot].slot_state) { +- default: +- case DVB_CA_SLOTSTATE_NONE: +- delay = HZ * 60; /* 60s */ +- if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) +- delay = HZ * 5; /* 5s */ +- break; +- case DVB_CA_SLOTSTATE_INVALID: +- delay = HZ * 60; /* 60s */ +- if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) +- delay = HZ / 10; /* 100ms */ +- break; +- +- case DVB_CA_SLOTSTATE_UNINITIALISED: +- case DVB_CA_SLOTSTATE_WAITREADY: +- case DVB_CA_SLOTSTATE_VALIDATE: +- case DVB_CA_SLOTSTATE_WAITFR: +- case DVB_CA_SLOTSTATE_LINKINIT: +- delay = HZ / 10; /* 100ms */ +- break; +- +- case DVB_CA_SLOTSTATE_RUNNING: +- delay = HZ * 60; /* 60s */ +- if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) +- delay = HZ / 10; /* 100ms */ +- if (ca->open) { +- if ((!ca->slot_info[slot].da_irq_supported) || +- (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) +- delay = HZ / 10; /* 100ms */ +- } +- break; +- } +- +- if (delay < curdelay) +- curdelay = delay; +- } +- +- ca->delay = curdelay; +-} +- +- +- +-/** +- * Kernel thread which monitors CA slots for CAM changes, and performs data transfers. +- */ +-static int dvb_ca_en50221_thread(void *data) +-{ +- struct dvb_ca_private *ca = data; +- int slot; +- int flags; +- int status; +- int pktcount; +- void *rxbuf; +- +- dprintk("%s\n", __func__); +- +- /* choose the correct initial delay */ +- dvb_ca_en50221_thread_update_delay(ca); +- +- /* main loop */ +- while (!kthread_should_stop()) { +- /* sleep for a bit */ +- if (!ca->wakeup) { +- set_current_state(TASK_INTERRUPTIBLE); +- schedule_timeout(ca->delay); +- if (kthread_should_stop()) +- return 0; +- } +- ca->wakeup = 0; +- +- /* go through all the slots processing them */ +- for (slot = 0; slot < ca->slot_count; slot++) { +- +- mutex_lock(&ca->slot_info[slot].slot_lock); +- +- // check the cam status + deal with CAMCHANGEs +- while (dvb_ca_en50221_check_camstatus(ca, slot)) { +- /* clear down an old CI slot if necessary */ +- if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) +- dvb_ca_en50221_slot_shutdown(ca, slot); +- +- /* if a CAM is NOW present, initialise it */ +- if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED; +- } +- +- /* we've handled one CAMCHANGE */ +- dvb_ca_en50221_thread_update_delay(ca); +- atomic_dec(&ca->slot_info[slot].camchange_count); +- } +- +- // CAM state machine +- switch (ca->slot_info[slot].slot_state) { +- case DVB_CA_SLOTSTATE_NONE: +- case DVB_CA_SLOTSTATE_INVALID: +- // no action needed +- break; +- +- case DVB_CA_SLOTSTATE_UNINITIALISED: +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY; +- ca->pub->slot_reset(ca->pub, slot); +- ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ); +- break; +- +- case DVB_CA_SLOTSTATE_WAITREADY: +- if (time_after(jiffies, ca->slot_info[slot].timeout)) { +- printk("dvb_ca adaptor %d: PC card did not respond :(\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- // no other action needed; will automatically change state when ready +- break; +- +- case DVB_CA_SLOTSTATE_VALIDATE: +- if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { +- /* we need this extra check for annoying interfaces like the budget-av */ +- if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && +- (ca->pub->poll_slot_status)) { +- status = ca->pub->poll_slot_status(ca->pub, slot, 0); +- if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- } +- +- printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- if (dvb_ca_en50221_set_configoption(ca, slot) != 0) { +- printk("dvb_ca adapter %d: Unable to initialise CAM :(\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- if (ca->pub->write_cam_control(ca->pub, slot, +- CTRLIF_COMMAND, CMDREG_RS) != 0) { +- printk("dvb_ca adapter %d: Unable to reset CAM IF\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- dprintk("DVB CAM validated successfully\n"); +- +- ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR; +- ca->wakeup = 1; +- break; +- +- case DVB_CA_SLOTSTATE_WAITFR: +- if (time_after(jiffies, ca->slot_info[slot].timeout)) { +- printk("dvb_ca adapter %d: DVB CAM did not respond :(\n", +- ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- +- flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); +- if (flags & STATUSREG_FR) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; +- ca->wakeup = 1; +- } +- break; +- +- case DVB_CA_SLOTSTATE_LINKINIT: +- if (dvb_ca_en50221_link_init(ca, slot) != 0) { +- /* we need this extra check for annoying interfaces like the budget-av */ +- if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) && +- (ca->pub->poll_slot_status)) { +- status = ca->pub->poll_slot_status(ca->pub, slot, 0); +- if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) { +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- } +- +- printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- +- if (ca->slot_info[slot].rx_buffer.data == NULL) { +- rxbuf = vmalloc(RX_BUFFER_SIZE); +- if (rxbuf == NULL) { +- printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; +- dvb_ca_en50221_thread_update_delay(ca); +- break; +- } +- dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE); +- } +- +- ca->pub->slot_ts_enable(ca->pub, slot); +- ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING; +- dvb_ca_en50221_thread_update_delay(ca); +- printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num); +- break; +- +- case DVB_CA_SLOTSTATE_RUNNING: +- if (!ca->open) +- break; +- +- // poll slots for data +- pktcount = 0; +- while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) { +- if (!ca->open) +- break; +- +- /* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */ +- if (dvb_ca_en50221_check_camstatus(ca, slot)) { +- // we dont want to sleep on the next iteration so we can handle the cam change +- ca->wakeup = 1; +- break; +- } +- +- /* check if we've hit our limit this time */ +- if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) { +- // dont sleep; there is likely to be more data to read +- ca->wakeup = 1; +- break; +- } +- } +- break; +- } +- +- mutex_unlock(&ca->slot_info[slot].slot_lock); +- } +- } +- +- return 0; +-} +- +- +- +-/* ******************************************************************************** */ +-/* EN50221 IO interface functions */ +- +-/** +- * Real ioctl implementation. +- * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them. +- * +- * @param inode Inode concerned. +- * @param file File concerned. +- * @param cmd IOCTL command. +- * @param arg Associated argument. +- * +- * @return 0 on success, <0 on error. +- */ +-static int dvb_ca_en50221_io_do_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- int err = 0; +- int slot; +- +- dprintk("%s\n", __func__); +- +- switch (cmd) { +- case CA_RESET: +- for (slot = 0; slot < ca->slot_count; slot++) { +- mutex_lock(&ca->slot_info[slot].slot_lock); +- if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) { +- dvb_ca_en50221_slot_shutdown(ca, slot); +- if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) +- dvb_ca_en50221_camchange_irq(ca->pub, +- slot, +- DVB_CA_EN50221_CAMCHANGE_INSERTED); +- } +- mutex_unlock(&ca->slot_info[slot].slot_lock); +- } +- ca->next_read_slot = 0; +- dvb_ca_en50221_thread_wakeup(ca); +- break; +- +- case CA_GET_CAP: { +- struct ca_caps *caps = parg; +- +- caps->slot_num = ca->slot_count; +- caps->slot_type = CA_CI_LINK; +- caps->descr_num = 0; +- caps->descr_type = 0; +- break; +- } +- +- case CA_GET_SLOT_INFO: { +- struct ca_slot_info *info = parg; +- +- if ((info->num > ca->slot_count) || (info->num < 0)) +- return -EINVAL; +- +- info->type = CA_CI_LINK; +- info->flags = 0; +- if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) +- && (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) { +- info->flags = CA_CI_MODULE_PRESENT; +- } +- if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) { +- info->flags |= CA_CI_MODULE_READY; +- } +- break; +- } +- +- default: +- err = -EINVAL; +- break; +- } +- +- return err; +-} +- +- +-/** +- * Wrapper for ioctl implementation. +- * +- * @param inode Inode concerned. +- * @param file File concerned. +- * @param cmd IOCTL command. +- * @param arg Associated argument. +- * +- * @return 0 on success, <0 on error. +- */ +-static long dvb_ca_en50221_io_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl); +-} +- +- +-/** +- * Implementation of write() syscall. +- * +- * @param file File structure. +- * @param buf Source buffer. +- * @param count Size of source buffer. +- * @param ppos Position in file (ignored). +- * +- * @return Number of bytes read, or <0 on error. +- */ +-static ssize_t dvb_ca_en50221_io_write(struct file *file, +- const char __user * buf, size_t count, loff_t * ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- u8 slot, connection_id; +- int status; +- u8 fragbuf[HOST_LINK_BUF_SIZE]; +- int fragpos = 0; +- int fraglen; +- unsigned long timeout; +- int written; +- +- dprintk("%s\n", __func__); +- +- /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ +- if (count < 2) +- return -EINVAL; +- +- /* extract slot & connection id */ +- if (copy_from_user(&slot, buf, 1)) +- return -EFAULT; +- if (copy_from_user(&connection_id, buf + 1, 1)) +- return -EFAULT; +- buf += 2; +- count -= 2; +- +- /* check if the slot is actually running */ +- if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) +- return -EINVAL; +- +- /* fragment the packets & store in the buffer */ +- while (fragpos < count) { +- fraglen = ca->slot_info[slot].link_buf_size - 2; +- if (fraglen < 0) +- break; +- if (fraglen > HOST_LINK_BUF_SIZE - 2) +- fraglen = HOST_LINK_BUF_SIZE - 2; +- if ((count - fragpos) < fraglen) +- fraglen = count - fragpos; +- +- fragbuf[0] = connection_id; +- fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00; +- status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen); +- if (status) { +- status = -EFAULT; +- goto exit; +- } +- +- timeout = jiffies + HZ / 2; +- written = 0; +- while (!time_after(jiffies, timeout)) { +- /* check the CAM hasn't been removed/reset in the meantime */ +- if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) { +- status = -EIO; +- goto exit; +- } +- +- mutex_lock(&ca->slot_info[slot].slot_lock); +- status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2); +- mutex_unlock(&ca->slot_info[slot].slot_lock); +- if (status == (fraglen + 2)) { +- written = 1; +- break; +- } +- if (status != -EAGAIN) +- goto exit; +- +- msleep(1); +- } +- if (!written) { +- status = -EIO; +- goto exit; +- } +- +- fragpos += fraglen; +- } +- status = count + 2; +- +-exit: +- return status; +-} +- +- +-/** +- * Condition for waking up in dvb_ca_en50221_io_read_condition +- */ +-static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, +- int *result, int *_slot) +-{ +- int slot; +- int slot_count = 0; +- int idx; +- size_t fraglen; +- int connection_id = -1; +- int found = 0; +- u8 hdr[2]; +- +- slot = ca->next_read_slot; +- while ((slot_count < ca->slot_count) && (!found)) { +- if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) +- goto nextslot; +- +- if (ca->slot_info[slot].rx_buffer.data == NULL) { +- return 0; +- } +- +- idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); +- while (idx != -1) { +- dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); +- if (connection_id == -1) +- connection_id = hdr[0]; +- if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) { +- *_slot = slot; +- found = 1; +- break; +- } +- +- idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); +- } +- +-nextslot: +- slot = (slot + 1) % ca->slot_count; +- slot_count++; +- } +- +- ca->next_read_slot = slot; +- return found; +-} +- +- +-/** +- * Implementation of read() syscall. +- * +- * @param file File structure. +- * @param buf Destination buffer. +- * @param count Size of destination buffer. +- * @param ppos Position in file (ignored). +- * +- * @return Number of bytes read, or <0 on error. +- */ +-static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, +- size_t count, loff_t * ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- int status; +- int result = 0; +- u8 hdr[2]; +- int slot; +- int connection_id = -1; +- size_t idx, idx2; +- int last_fragment = 0; +- size_t fraglen; +- int pktlen; +- int dispose = 0; +- +- dprintk("%s\n", __func__); +- +- /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ +- if (count < 2) +- return -EINVAL; +- +- /* wait for some data */ +- if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) { +- +- /* if we're in nonblocking mode, exit immediately */ +- if (file->f_flags & O_NONBLOCK) +- return -EWOULDBLOCK; +- +- /* wait for some data */ +- status = wait_event_interruptible(ca->wait_queue, +- dvb_ca_en50221_io_read_condition +- (ca, &result, &slot)); +- } +- if ((status < 0) || (result < 0)) { +- if (result) +- return result; +- return status; +- } +- +- idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); +- pktlen = 2; +- do { +- if (idx == -1) { +- printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num); +- status = -EIO; +- goto exit; +- } +- +- dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2); +- if (connection_id == -1) +- connection_id = hdr[0]; +- if (hdr[0] == connection_id) { +- if (pktlen < count) { +- if ((pktlen + fraglen - 2) > count) { +- fraglen = count - pktlen; +- } else { +- fraglen -= 2; +- } +- +- if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2, +- buf + pktlen, fraglen)) < 0) { +- goto exit; +- } +- pktlen += fraglen; +- } +- +- if ((hdr[1] & 0x80) == 0) +- last_fragment = 1; +- dispose = 1; +- } +- +- idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); +- if (dispose) +- dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx); +- idx = idx2; +- dispose = 0; +- } while (!last_fragment); +- +- hdr[0] = slot; +- hdr[1] = connection_id; +- status = copy_to_user(buf, hdr, 2); +- if (status) { +- status = -EFAULT; +- goto exit; +- } +- status = pktlen; +- +-exit: +- return status; +-} +- +- +-/** +- * Implementation of file open syscall. +- * +- * @param inode Inode concerned. +- * @param file File concerned. +- * +- * @return 0 on success, <0 on failure. +- */ +-static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- int err; +- int i; +- +- dprintk("%s\n", __func__); +- +- if (!try_module_get(ca->pub->owner)) +- return -EIO; +- +- err = dvb_generic_open(inode, file); +- if (err < 0) { +- module_put(ca->pub->owner); +- return err; +- } +- +- for (i = 0; i < ca->slot_count; i++) { +- +- if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) { +- if (ca->slot_info[i].rx_buffer.data != NULL) { +- /* it is safe to call this here without locks because +- * ca->open == 0. Data is not read in this case */ +- dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer); +- } +- } +- } +- +- ca->open = 1; +- dvb_ca_en50221_thread_update_delay(ca); +- dvb_ca_en50221_thread_wakeup(ca); +- +- return 0; +-} +- +- +-/** +- * Implementation of file close syscall. +- * +- * @param inode Inode concerned. +- * @param file File concerned. +- * +- * @return 0 on success, <0 on failure. +- */ +-static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- int err; +- +- dprintk("%s\n", __func__); +- +- /* mark the CA device as closed */ +- ca->open = 0; +- dvb_ca_en50221_thread_update_delay(ca); +- +- err = dvb_generic_release(inode, file); +- +- module_put(ca->pub->owner); +- +- return err; +-} +- +- +-/** +- * Implementation of poll() syscall. +- * +- * @param file File concerned. +- * @param wait poll wait table. +- * +- * @return Standard poll mask. +- */ +-static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_ca_private *ca = dvbdev->priv; +- unsigned int mask = 0; +- int slot; +- int result = 0; +- +- dprintk("%s\n", __func__); +- +- if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { +- mask |= POLLIN; +- } +- +- /* if there is something, return now */ +- if (mask) +- return mask; +- +- /* wait for something to happen */ +- poll_wait(file, &ca->wait_queue, wait); +- +- if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { +- mask |= POLLIN; +- } +- +- return mask; +-} +-EXPORT_SYMBOL(dvb_ca_en50221_init); +- +- +-static const struct file_operations dvb_ca_fops = { +- .owner = THIS_MODULE, +- .read = dvb_ca_en50221_io_read, +- .write = dvb_ca_en50221_io_write, +- .unlocked_ioctl = dvb_ca_en50221_io_ioctl, +- .open = dvb_ca_en50221_io_open, +- .release = dvb_ca_en50221_io_release, +- .poll = dvb_ca_en50221_io_poll, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_ca = { +- .priv = NULL, +- .users = 1, +- .readers = 1, +- .writers = 1, +- .fops = &dvb_ca_fops, +-}; +- +- +-/* ******************************************************************************** */ +-/* Initialisation/shutdown functions */ +- +- +-/** +- * Initialise a new DVB CA EN50221 interface device. +- * +- * @param dvb_adapter DVB adapter to attach the new CA device to. +- * @param ca The dvb_ca instance. +- * @param flags Flags describing the CA device (DVB_CA_FLAG_*). +- * @param slot_count Number of slots supported. +- * +- * @return 0 on success, nonzero on failure +- */ +-int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, +- struct dvb_ca_en50221 *pubca, int flags, int slot_count) +-{ +- int ret; +- struct dvb_ca_private *ca = NULL; +- int i; +- +- dprintk("%s\n", __func__); +- +- if (slot_count < 1) +- return -EINVAL; +- +- /* initialise the system data */ +- if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) { +- ret = -ENOMEM; +- goto error; +- } +- ca->pub = pubca; +- ca->flags = flags; +- ca->slot_count = slot_count; +- if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) { +- ret = -ENOMEM; +- goto error; +- } +- init_waitqueue_head(&ca->wait_queue); +- ca->open = 0; +- ca->wakeup = 0; +- ca->next_read_slot = 0; +- pubca->private = ca; +- +- /* register the DVB device */ +- ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA); +- if (ret) +- goto error; +- +- /* now initialise each slot */ +- for (i = 0; i < slot_count; i++) { +- memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot)); +- ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE; +- atomic_set(&ca->slot_info[i].camchange_count, 0); +- ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; +- mutex_init(&ca->slot_info[i].slot_lock); +- } +- +- if (signal_pending(current)) { +- ret = -EINTR; +- goto error; +- } +- mb(); +- +- /* create a kthread for monitoring this CA device */ +- ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i", +- ca->dvbdev->adapter->num, ca->dvbdev->id); +- if (IS_ERR(ca->thread)) { +- ret = PTR_ERR(ca->thread); +- printk("dvb_ca_init: failed to start kernel_thread (%d)\n", +- ret); +- goto error; +- } +- return 0; +- +-error: +- if (ca != NULL) { +- if (ca->dvbdev != NULL) +- dvb_unregister_device(ca->dvbdev); +- kfree(ca->slot_info); +- kfree(ca); +- } +- pubca->private = NULL; +- return ret; +-} +-EXPORT_SYMBOL(dvb_ca_en50221_release); +- +- +- +-/** +- * Release a DVB CA EN50221 interface device. +- * +- * @param ca_dev The dvb_device_t instance for the CA device. +- * @param ca The associated dvb_ca instance. +- */ +-void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca) +-{ +- struct dvb_ca_private *ca = pubca->private; +- int i; +- +- dprintk("%s\n", __func__); +- +- /* shutdown the thread if there was one */ +- kthread_stop(ca->thread); +- +- for (i = 0; i < ca->slot_count; i++) { +- dvb_ca_en50221_slot_shutdown(ca, i); +- vfree(ca->slot_info[i].rx_buffer.data); +- } +- kfree(ca->slot_info); +- dvb_unregister_device(ca->dvbdev); +- kfree(ca); +- pubca->private = NULL; +-} +diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb/dvb-core/dvb_ca_en50221.h +deleted file mode 100644 +index 7df2e14..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.h ++++ /dev/null +@@ -1,136 +0,0 @@ +-/* +- * dvb_ca.h: generic DVB functions for EN50221 CA interfaces +- * +- * Copyright (C) 2004 Andrew de Quincey +- * +- * 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. +- */ +- +-#ifndef _DVB_CA_EN50221_H_ +-#define _DVB_CA_EN50221_H_ +- +-#include +-#include +- +-#include "dvbdev.h" +- +-#define DVB_CA_EN50221_POLL_CAM_PRESENT 1 +-#define DVB_CA_EN50221_POLL_CAM_CHANGED 2 +-#define DVB_CA_EN50221_POLL_CAM_READY 4 +- +-#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1 +-#define DVB_CA_EN50221_FLAG_IRQ_FR 2 +-#define DVB_CA_EN50221_FLAG_IRQ_DA 4 +- +-#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0 +-#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1 +- +- +- +-/* Structure describing a CA interface */ +-struct dvb_ca_en50221 { +- +- /* the module owning this structure */ +- struct module* owner; +- +- /* NOTE: the read_*, write_* and poll_slot_status functions will be +- * called for different slots concurrently and need to use locks where +- * and if appropriate. There will be no concurrent access to one slot. +- */ +- +- /* functions for accessing attribute memory on the CAM */ +- int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address); +- int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value); +- +- /* functions for accessing the control interface on the CAM */ +- int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address); +- int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value); +- +- /* Functions for controlling slots */ +- int (*slot_reset)(struct dvb_ca_en50221* ca, int slot); +- int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot); +- int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot); +- +- /* +- * Poll slot status. +- * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set +- */ +- int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot, int open); +- +- /* private data, used by caller */ +- void* data; +- +- /* Opaque data used by the dvb_ca core. Do not modify! */ +- void* private; +-}; +- +- +- +- +-/* ******************************************************************************** */ +-/* Functions for reporting IRQ events */ +- +-/** +- * A CAMCHANGE IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- * @param change_type One of the DVB_CA_CAMCHANGE_* values +- */ +-void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type); +- +-/** +- * A CAMREADY IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- */ +-void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot); +- +-/** +- * An FR or a DA IRQ has occurred. +- * +- * @param ca CA instance. +- * @param slot Slot concerned. +- */ +-void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot); +- +- +- +-/* ******************************************************************************** */ +-/* Initialisation/shutdown functions */ +- +-/** +- * Initialise a new DVB CA device. +- * +- * @param dvb_adapter DVB adapter to attach the new CA device to. +- * @param ca The dvb_ca instance. +- * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*). +- * @param slot_count Number of slots supported. +- * +- * @return 0 on success, nonzero on failure +- */ +-extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count); +- +-/** +- * Release a DVB CA device. +- * +- * @param ca The associated dvb_ca instance. +- */ +-extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca); +- +- +- +-#endif +diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c +deleted file mode 100644 +index faa3671..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_demux.c ++++ /dev/null +@@ -1,1299 +0,0 @@ +-/* +- * dvb_demux.c - DVB kernel demux API +- * +- * Copyright (C) 2000-2001 Ralph Metzler +- * & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_demux.h" +- +-#define NOBUFS +-/* +-** #define DVB_DEMUX_SECTION_LOSS_LOG to monitor payload loss in the syslog +-*/ +-// #define DVB_DEMUX_SECTION_LOSS_LOG +- +-static int dvb_demux_tscheck; +-module_param(dvb_demux_tscheck, int, 0644); +-MODULE_PARM_DESC(dvb_demux_tscheck, +- "enable transport stream continuity and TEI check"); +- +-static int dvb_demux_speedcheck; +-module_param(dvb_demux_speedcheck, int, 0644); +-MODULE_PARM_DESC(dvb_demux_speedcheck, +- "enable transport stream speed check"); +- +-#define dprintk_tscheck(x...) do { \ +- if (dvb_demux_tscheck && printk_ratelimit()) \ +- printk(x); \ +- } while (0) +- +-/****************************************************************************** +- * static inlined helper functions +- ******************************************************************************/ +- +-static inline u16 section_length(const u8 *buf) +-{ +- return 3 + ((buf[1] & 0x0f) << 8) + buf[2]; +-} +- +-static inline u16 ts_pid(const u8 *buf) +-{ +- return ((buf[1] & 0x1f) << 8) + buf[2]; +-} +- +-static inline u8 payload(const u8 *tsp) +-{ +- if (!(tsp[3] & 0x10)) // no payload? +- return 0; +- +- if (tsp[3] & 0x20) { // adaptation field? +- if (tsp[4] > 183) // corrupted data? +- return 0; +- else +- return 184 - 1 - tsp[4]; +- } +- +- return 184; +-} +- +-static u32 dvb_dmx_crc32(struct dvb_demux_feed *f, const u8 *src, size_t len) +-{ +- return (f->feed.sec.crc_val = crc32_be(f->feed.sec.crc_val, src, len)); +-} +- +-static void dvb_dmx_memcopy(struct dvb_demux_feed *f, u8 *d, const u8 *s, +- size_t len) +-{ +- memcpy(d, s, len); +-} +- +-/****************************************************************************** +- * Software filter functions +- ******************************************************************************/ +- +-static inline int dvb_dmx_swfilter_payload(struct dvb_demux_feed *feed, +- const u8 *buf) +-{ +- int count = payload(buf); +- int p; +- //int ccok; +- //u8 cc; +- +- if (count == 0) +- return -1; +- +- p = 188 - count; +- +- /* +- cc = buf[3] & 0x0f; +- ccok = ((feed->cc + 1) & 0x0f) == cc; +- feed->cc = cc; +- if (!ccok) +- printk("missed packet!\n"); +- */ +- +- if (buf[1] & 0x40) // PUSI ? +- feed->peslen = 0xfffa; +- +- feed->peslen += count; +- +- return feed->cb.ts(&buf[p], count, NULL, 0, &feed->feed.ts, DMX_OK); +-} +- +-static int dvb_dmx_swfilter_sectionfilter(struct dvb_demux_feed *feed, +- struct dvb_demux_filter *f) +-{ +- u8 neq = 0; +- int i; +- +- for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { +- u8 xor = f->filter.filter_value[i] ^ feed->feed.sec.secbuf[i]; +- +- if (f->maskandmode[i] & xor) +- return 0; +- +- neq |= f->maskandnotmode[i] & xor; +- } +- +- if (f->doneq && !neq) +- return 0; +- +- return feed->cb.sec(feed->feed.sec.secbuf, feed->feed.sec.seclen, +- NULL, 0, &f->filter, DMX_OK); +-} +- +-static inline int dvb_dmx_swfilter_section_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct dvb_demux_filter *f = feed->filter; +- struct dmx_section_feed *sec = &feed->feed.sec; +- int section_syntax_indicator; +- +- if (!sec->is_filtering) +- return 0; +- +- if (!f) +- return 0; +- +- if (sec->check_crc) { +- section_syntax_indicator = ((sec->secbuf[1] & 0x80) != 0); +- if (section_syntax_indicator && +- demux->check_crc32(feed, sec->secbuf, sec->seclen)) +- return -1; +- } +- +- do { +- if (dvb_dmx_swfilter_sectionfilter(feed, f) < 0) +- return -1; +- } while ((f = f->next) && sec->is_filtering); +- +- sec->seclen = 0; +- +- return 0; +-} +- +-static void dvb_dmx_swfilter_section_new(struct dvb_demux_feed *feed) +-{ +- struct dmx_section_feed *sec = &feed->feed.sec; +- +-#ifdef DVB_DEMUX_SECTION_LOSS_LOG +- if (sec->secbufp < sec->tsfeedp) { +- int i, n = sec->tsfeedp - sec->secbufp; +- +- /* +- * Section padding is done with 0xff bytes entirely. +- * Due to speed reasons, we won't check all of them +- * but just first and last. +- */ +- if (sec->secbuf[0] != 0xff || sec->secbuf[n - 1] != 0xff) { +- printk("dvb_demux.c section ts padding loss: %d/%d\n", +- n, sec->tsfeedp); +- printk("dvb_demux.c pad data:"); +- for (i = 0; i < n; i++) +- printk(" %02x", sec->secbuf[i]); +- printk("\n"); +- } +- } +-#endif +- +- sec->tsfeedp = sec->secbufp = sec->seclen = 0; +- sec->secbuf = sec->secbuf_base; +-} +- +-/* +- * Losless Section Demux 1.4.1 by Emard +- * Valsecchi Patrick: +- * - middle of section A (no PUSI) +- * - end of section A and start of section B +- * (with PUSI pointing to the start of the second section) +- * +- * In this case, without feed->pusi_seen you'll receive a garbage section +- * consisting of the end of section A. Basically because tsfeedp +- * is incemented and the use=0 condition is not raised +- * when the second packet arrives. +- * +- * Fix: +- * when demux is started, let feed->pusi_seen = 0 to +- * prevent initial feeding of garbage from the end of +- * previous section. When you for the first time see PUSI=1 +- * then set feed->pusi_seen = 1 +- */ +-static int dvb_dmx_swfilter_section_copy_dump(struct dvb_demux_feed *feed, +- const u8 *buf, u8 len) +-{ +- struct dvb_demux *demux = feed->demux; +- struct dmx_section_feed *sec = &feed->feed.sec; +- u16 limit, seclen, n; +- +- if (sec->tsfeedp >= DMX_MAX_SECFEED_SIZE) +- return 0; +- +- if (sec->tsfeedp + len > DMX_MAX_SECFEED_SIZE) { +-#ifdef DVB_DEMUX_SECTION_LOSS_LOG +- printk("dvb_demux.c section buffer full loss: %d/%d\n", +- sec->tsfeedp + len - DMX_MAX_SECFEED_SIZE, +- DMX_MAX_SECFEED_SIZE); +-#endif +- len = DMX_MAX_SECFEED_SIZE - sec->tsfeedp; +- } +- +- if (len <= 0) +- return 0; +- +- demux->memcopy(feed, sec->secbuf_base + sec->tsfeedp, buf, len); +- sec->tsfeedp += len; +- +- /* +- * Dump all the sections we can find in the data (Emard) +- */ +- limit = sec->tsfeedp; +- if (limit > DMX_MAX_SECFEED_SIZE) +- return -1; /* internal error should never happen */ +- +- /* to be sure always set secbuf */ +- sec->secbuf = sec->secbuf_base + sec->secbufp; +- +- for (n = 0; sec->secbufp + 2 < limit; n++) { +- seclen = section_length(sec->secbuf); +- if (seclen <= 0 || seclen > DMX_MAX_SECTION_SIZE +- || seclen + sec->secbufp > limit) +- return 0; +- sec->seclen = seclen; +- sec->crc_val = ~0; +- /* dump [secbuf .. secbuf+seclen) */ +- if (feed->pusi_seen) +- dvb_dmx_swfilter_section_feed(feed); +-#ifdef DVB_DEMUX_SECTION_LOSS_LOG +- else +- printk("dvb_demux.c pusi not seen, discarding section data\n"); +-#endif +- sec->secbufp += seclen; /* secbufp and secbuf moving together is */ +- sec->secbuf += seclen; /* redundant but saves pointer arithmetic */ +- } +- +- return 0; +-} +- +-static int dvb_dmx_swfilter_section_packet(struct dvb_demux_feed *feed, +- const u8 *buf) +-{ +- u8 p, count; +- int ccok, dc_i = 0; +- u8 cc; +- +- count = payload(buf); +- +- if (count == 0) /* count == 0 if no payload or out of range */ +- return -1; +- +- p = 188 - count; /* payload start */ +- +- cc = buf[3] & 0x0f; +- ccok = ((feed->cc + 1) & 0x0f) == cc; +- feed->cc = cc; +- +- if (buf[3] & 0x20) { +- /* adaption field present, check for discontinuity_indicator */ +- if ((buf[4] > 0) && (buf[5] & 0x80)) +- dc_i = 1; +- } +- +- if (!ccok || dc_i) { +-#ifdef DVB_DEMUX_SECTION_LOSS_LOG +- printk("dvb_demux.c discontinuity detected %d bytes lost\n", +- count); +- /* +- * those bytes under sume circumstances will again be reported +- * in the following dvb_dmx_swfilter_section_new +- */ +-#endif +- /* +- * Discontinuity detected. Reset pusi_seen = 0 to +- * stop feeding of suspicious data until next PUSI=1 arrives +- */ +- feed->pusi_seen = 0; +- dvb_dmx_swfilter_section_new(feed); +- } +- +- if (buf[1] & 0x40) { +- /* PUSI=1 (is set), section boundary is here */ +- if (count > 1 && buf[p] < count) { +- const u8 *before = &buf[p + 1]; +- u8 before_len = buf[p]; +- const u8 *after = &before[before_len]; +- u8 after_len = count - 1 - before_len; +- +- dvb_dmx_swfilter_section_copy_dump(feed, before, +- before_len); +- /* before start of new section, set pusi_seen = 1 */ +- feed->pusi_seen = 1; +- dvb_dmx_swfilter_section_new(feed); +- dvb_dmx_swfilter_section_copy_dump(feed, after, +- after_len); +- } +-#ifdef DVB_DEMUX_SECTION_LOSS_LOG +- else if (count > 0) +- printk("dvb_demux.c PUSI=1 but %d bytes lost\n", count); +-#endif +- } else { +- /* PUSI=0 (is not set), no section boundary */ +- dvb_dmx_swfilter_section_copy_dump(feed, &buf[p], count); +- } +- +- return 0; +-} +- +-static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, +- const u8 *buf) +-{ +- switch (feed->type) { +- case DMX_TYPE_TS: +- if (!feed->feed.ts.is_filtering) +- break; +- if (feed->ts_type & TS_PACKET) { +- if (feed->ts_type & TS_PAYLOAD_ONLY) +- dvb_dmx_swfilter_payload(feed, buf); +- else +- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, +- DMX_OK); +- } +- if (feed->ts_type & TS_DECODER) +- if (feed->demux->write_to_decoder) +- feed->demux->write_to_decoder(feed, buf, 188); +- break; +- +- case DMX_TYPE_SEC: +- if (!feed->feed.sec.is_filtering) +- break; +- if (dvb_dmx_swfilter_section_packet(feed, buf) < 0) +- feed->feed.sec.seclen = feed->feed.sec.secbufp = 0; +- break; +- +- default: +- break; +- } +-} +- +-#define DVR_FEED(f) \ +- (((f)->type == DMX_TYPE_TS) && \ +- ((f)->feed.ts.is_filtering) && \ +- (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET)) +- +-static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) +-{ +- struct dvb_demux_feed *feed; +- u16 pid = ts_pid(buf); +- int dvr_done = 0; +- +- if (dvb_demux_speedcheck) { +- struct timespec cur_time, delta_time; +- u64 speed_bytes, speed_timedelta; +- +- demux->speed_pkts_cnt++; +- +- /* show speed every SPEED_PKTS_INTERVAL packets */ +- if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { +- cur_time = current_kernel_time(); +- +- if (demux->speed_last_time.tv_sec != 0 && +- demux->speed_last_time.tv_nsec != 0) { +- delta_time = timespec_sub(cur_time, +- demux->speed_last_time); +- speed_bytes = (u64)demux->speed_pkts_cnt +- * 188 * 8; +- /* convert to 1024 basis */ +- speed_bytes = 1000 * div64_u64(speed_bytes, +- 1024); +- speed_timedelta = +- (u64)timespec_to_ns(&delta_time); +- speed_timedelta = div64_u64(speed_timedelta, +- 1000000); /* nsec -> usec */ +- printk(KERN_INFO "TS speed %llu Kbits/sec \n", +- div64_u64(speed_bytes, +- speed_timedelta)); +- }; +- +- demux->speed_last_time = cur_time; +- demux->speed_pkts_cnt = 0; +- }; +- }; +- +- if (demux->cnt_storage && dvb_demux_tscheck) { +- /* check pkt counter */ +- if (pid < MAX_PID) { +- if (buf[1] & 0x80) +- dprintk_tscheck("TEI detected. " +- "PID=0x%x data1=0x%x\n", +- pid, buf[1]); +- +- if ((buf[3] & 0xf) != demux->cnt_storage[pid]) +- dprintk_tscheck("TS packet counter mismatch. " +- "PID=0x%x expected 0x%x " +- "got 0x%x\n", +- pid, demux->cnt_storage[pid], +- buf[3] & 0xf); +- +- demux->cnt_storage[pid] = ((buf[3] & 0xf) + 1)&0xf; +- }; +- /* end check */ +- }; +- +- list_for_each_entry(feed, &demux->feed_list, list_head) { +- if ((feed->pid != pid) && (feed->pid != 0x2000)) +- continue; +- +- /* copy each packet only once to the dvr device, even +- * if a PID is in multiple filters (e.g. video + PCR) */ +- if ((DVR_FEED(feed)) && (dvr_done++)) +- continue; +- +- if (feed->pid == pid) +- dvb_dmx_swfilter_packet_type(feed, buf); +- else if (feed->pid == 0x2000) +- feed->cb.ts(buf, 188, NULL, 0, &feed->feed.ts, DMX_OK); +- } +-} +- +-void dvb_dmx_swfilter_packets(struct dvb_demux *demux, const u8 *buf, +- size_t count) +-{ +- spin_lock(&demux->lock); +- +- while (count--) { +- if (buf[0] == 0x47) +- dvb_dmx_swfilter_packet(demux, buf); +- buf += 188; +- } +- +- spin_unlock(&demux->lock); +-} +- +-EXPORT_SYMBOL(dvb_dmx_swfilter_packets); +- +-static inline int find_next_packet(const u8 *buf, int pos, size_t count, +- const int pktsize) +-{ +- int start = pos, lost; +- +- while (pos < count) { +- if (buf[pos] == 0x47 || +- (pktsize == 204 && buf[pos] == 0xB8)) +- break; +- pos++; +- } +- +- lost = pos - start; +- if (lost) { +- /* This garbage is part of a valid packet? */ +- int backtrack = pos - pktsize; +- if (backtrack >= 0 && (buf[backtrack] == 0x47 || +- (pktsize == 204 && buf[backtrack] == 0xB8))) +- return backtrack; +- } +- +- return pos; +-} +- +-/* Filter all pktsize= 188 or 204 sized packets and skip garbage. */ +-static inline void _dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, +- size_t count, const int pktsize) +-{ +- int p = 0, i, j; +- const u8 *q; +- +- spin_lock(&demux->lock); +- +- if (demux->tsbufp) { /* tsbuf[0] is now 0x47. */ +- i = demux->tsbufp; +- j = pktsize - i; +- if (count < j) { +- memcpy(&demux->tsbuf[i], buf, count); +- demux->tsbufp += count; +- goto bailout; +- } +- memcpy(&demux->tsbuf[i], buf, j); +- if (demux->tsbuf[0] == 0x47) /* double check */ +- dvb_dmx_swfilter_packet(demux, demux->tsbuf); +- demux->tsbufp = 0; +- p += j; +- } +- +- while (1) { +- p = find_next_packet(buf, p, count, pktsize); +- if (p >= count) +- break; +- if (count - p < pktsize) +- break; +- +- q = &buf[p]; +- +- if (pktsize == 204 && (*q == 0xB8)) { +- memcpy(demux->tsbuf, q, 188); +- demux->tsbuf[0] = 0x47; +- q = demux->tsbuf; +- } +- dvb_dmx_swfilter_packet(demux, q); +- p += pktsize; +- } +- +- i = count - p; +- if (i) { +- memcpy(demux->tsbuf, &buf[p], i); +- demux->tsbufp = i; +- if (pktsize == 204 && demux->tsbuf[0] == 0xB8) +- demux->tsbuf[0] = 0x47; +- } +- +-bailout: +- spin_unlock(&demux->lock); +-} +- +-void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count) +-{ +- _dvb_dmx_swfilter(demux, buf, count, 188); +-} +-EXPORT_SYMBOL(dvb_dmx_swfilter); +- +-void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) +-{ +- _dvb_dmx_swfilter(demux, buf, count, 204); +-} +-EXPORT_SYMBOL(dvb_dmx_swfilter_204); +- +-static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) +-{ +- int i; +- +- for (i = 0; i < demux->filternum; i++) +- if (demux->filter[i].state == DMX_STATE_FREE) +- break; +- +- if (i == demux->filternum) +- return NULL; +- +- demux->filter[i].state = DMX_STATE_ALLOCATED; +- +- return &demux->filter[i]; +-} +- +-static struct dvb_demux_feed *dvb_dmx_feed_alloc(struct dvb_demux *demux) +-{ +- int i; +- +- for (i = 0; i < demux->feednum; i++) +- if (demux->feed[i].state == DMX_STATE_FREE) +- break; +- +- if (i == demux->feednum) +- return NULL; +- +- demux->feed[i].state = DMX_STATE_ALLOCATED; +- +- return &demux->feed[i]; +-} +- +-static int dvb_demux_feed_find(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux_feed *entry; +- +- list_for_each_entry(entry, &feed->demux->feed_list, list_head) +- if (entry == feed) +- return 1; +- +- return 0; +-} +- +-static void dvb_demux_feed_add(struct dvb_demux_feed *feed) +-{ +- spin_lock_irq(&feed->demux->lock); +- if (dvb_demux_feed_find(feed)) { +- printk(KERN_ERR "%s: feed already in list (type=%x state=%x pid=%x)\n", +- __func__, feed->type, feed->state, feed->pid); +- goto out; +- } +- +- list_add(&feed->list_head, &feed->demux->feed_list); +-out: +- spin_unlock_irq(&feed->demux->lock); +-} +- +-static void dvb_demux_feed_del(struct dvb_demux_feed *feed) +-{ +- spin_lock_irq(&feed->demux->lock); +- if (!(dvb_demux_feed_find(feed))) { +- printk(KERN_ERR "%s: feed not in list (type=%x state=%x pid=%x)\n", +- __func__, feed->type, feed->state, feed->pid); +- goto out; +- } +- +- list_del(&feed->list_head); +-out: +- spin_unlock_irq(&feed->demux->lock); +-} +- +-static int dmx_ts_feed_set(struct dmx_ts_feed *ts_feed, u16 pid, int ts_type, +- enum dmx_ts_pes pes_type, +- size_t circular_buffer_size, struct timespec timeout) +-{ +- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; +- struct dvb_demux *demux = feed->demux; +- +- if (pid > DMX_MAX_PID) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&demux->mutex)) +- return -ERESTARTSYS; +- +- if (ts_type & TS_DECODER) { +- if (pes_type >= DMX_TS_PES_OTHER) { +- mutex_unlock(&demux->mutex); +- return -EINVAL; +- } +- +- if (demux->pesfilter[pes_type] && +- demux->pesfilter[pes_type] != feed) { +- mutex_unlock(&demux->mutex); +- return -EINVAL; +- } +- +- demux->pesfilter[pes_type] = feed; +- demux->pids[pes_type] = pid; +- } +- +- dvb_demux_feed_add(feed); +- +- feed->pid = pid; +- feed->buffer_size = circular_buffer_size; +- feed->timeout = timeout; +- feed->ts_type = ts_type; +- feed->pes_type = pes_type; +- +- if (feed->buffer_size) { +-#ifdef NOBUFS +- feed->buffer = NULL; +-#else +- feed->buffer = vmalloc(feed->buffer_size); +- if (!feed->buffer) { +- mutex_unlock(&demux->mutex); +- return -ENOMEM; +- } +-#endif +- } +- +- feed->state = DMX_STATE_READY; +- mutex_unlock(&demux->mutex); +- +- return 0; +-} +- +-static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed) +-{ +- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; +- struct dvb_demux *demux = feed->demux; +- int ret; +- +- if (mutex_lock_interruptible(&demux->mutex)) +- return -ERESTARTSYS; +- +- if (feed->state != DMX_STATE_READY || feed->type != DMX_TYPE_TS) { +- mutex_unlock(&demux->mutex); +- return -EINVAL; +- } +- +- if (!demux->start_feed) { +- mutex_unlock(&demux->mutex); +- return -ENODEV; +- } +- +- if ((ret = demux->start_feed(feed)) < 0) { +- mutex_unlock(&demux->mutex); +- return ret; +- } +- +- spin_lock_irq(&demux->lock); +- ts_feed->is_filtering = 1; +- feed->state = DMX_STATE_GO; +- spin_unlock_irq(&demux->lock); +- mutex_unlock(&demux->mutex); +- +- return 0; +-} +- +-static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed) +-{ +- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; +- struct dvb_demux *demux = feed->demux; +- int ret; +- +- mutex_lock(&demux->mutex); +- +- if (feed->state < DMX_STATE_GO) { +- mutex_unlock(&demux->mutex); +- return -EINVAL; +- } +- +- if (!demux->stop_feed) { +- mutex_unlock(&demux->mutex); +- return -ENODEV; +- } +- +- ret = demux->stop_feed(feed); +- +- spin_lock_irq(&demux->lock); +- ts_feed->is_filtering = 0; +- feed->state = DMX_STATE_ALLOCATED; +- spin_unlock_irq(&demux->lock); +- mutex_unlock(&demux->mutex); +- +- return ret; +-} +- +-static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx, +- struct dmx_ts_feed **ts_feed, +- dmx_ts_cb callback) +-{ +- struct dvb_demux *demux = (struct dvb_demux *)dmx; +- struct dvb_demux_feed *feed; +- +- if (mutex_lock_interruptible(&demux->mutex)) +- return -ERESTARTSYS; +- +- if (!(feed = dvb_dmx_feed_alloc(demux))) { +- mutex_unlock(&demux->mutex); +- return -EBUSY; +- } +- +- feed->type = DMX_TYPE_TS; +- feed->cb.ts = callback; +- feed->demux = demux; +- feed->pid = 0xffff; +- feed->peslen = 0xfffa; +- feed->buffer = NULL; +- +- (*ts_feed) = &feed->feed.ts; +- (*ts_feed)->parent = dmx; +- (*ts_feed)->priv = NULL; +- (*ts_feed)->is_filtering = 0; +- (*ts_feed)->start_filtering = dmx_ts_feed_start_filtering; +- (*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering; +- (*ts_feed)->set = dmx_ts_feed_set; +- +- if (!(feed->filter = dvb_dmx_filter_alloc(demux))) { +- feed->state = DMX_STATE_FREE; +- mutex_unlock(&demux->mutex); +- return -EBUSY; +- } +- +- feed->filter->type = DMX_TYPE_TS; +- feed->filter->feed = feed; +- feed->filter->state = DMX_STATE_READY; +- +- mutex_unlock(&demux->mutex); +- +- return 0; +-} +- +-static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, +- struct dmx_ts_feed *ts_feed) +-{ +- struct dvb_demux *demux = (struct dvb_demux *)dmx; +- struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; +- +- mutex_lock(&demux->mutex); +- +- if (feed->state == DMX_STATE_FREE) { +- mutex_unlock(&demux->mutex); +- return -EINVAL; +- } +-#ifndef NOBUFS +- vfree(feed->buffer); +- feed->buffer = NULL; +-#endif +- +- feed->state = DMX_STATE_FREE; +- feed->filter->state = DMX_STATE_FREE; +- +- dvb_demux_feed_del(feed); +- +- feed->pid = 0xffff; +- +- if (feed->ts_type & TS_DECODER && feed->pes_type < DMX_TS_PES_OTHER) +- demux->pesfilter[feed->pes_type] = NULL; +- +- mutex_unlock(&demux->mutex); +- return 0; +-} +- +-/****************************************************************************** +- * dmx_section_feed API calls +- ******************************************************************************/ +- +-static int dmx_section_feed_allocate_filter(struct dmx_section_feed *feed, +- struct dmx_section_filter **filter) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdemux = dvbdmxfeed->demux; +- struct dvb_demux_filter *dvbdmxfilter; +- +- if (mutex_lock_interruptible(&dvbdemux->mutex)) +- return -ERESTARTSYS; +- +- dvbdmxfilter = dvb_dmx_filter_alloc(dvbdemux); +- if (!dvbdmxfilter) { +- mutex_unlock(&dvbdemux->mutex); +- return -EBUSY; +- } +- +- spin_lock_irq(&dvbdemux->lock); +- *filter = &dvbdmxfilter->filter; +- (*filter)->parent = feed; +- (*filter)->priv = NULL; +- dvbdmxfilter->feed = dvbdmxfeed; +- dvbdmxfilter->type = DMX_TYPE_SEC; +- dvbdmxfilter->state = DMX_STATE_READY; +- dvbdmxfilter->next = dvbdmxfeed->filter; +- dvbdmxfeed->filter = dvbdmxfilter; +- spin_unlock_irq(&dvbdemux->lock); +- +- mutex_unlock(&dvbdemux->mutex); +- return 0; +-} +- +-static int dmx_section_feed_set(struct dmx_section_feed *feed, +- u16 pid, size_t circular_buffer_size, +- int check_crc) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- +- if (pid > 0x1fff) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&dvbdmx->mutex)) +- return -ERESTARTSYS; +- +- dvb_demux_feed_add(dvbdmxfeed); +- +- dvbdmxfeed->pid = pid; +- dvbdmxfeed->buffer_size = circular_buffer_size; +- dvbdmxfeed->feed.sec.check_crc = check_crc; +- +-#ifdef NOBUFS +- dvbdmxfeed->buffer = NULL; +-#else +- dvbdmxfeed->buffer = vmalloc(dvbdmxfeed->buffer_size); +- if (!dvbdmxfeed->buffer) { +- mutex_unlock(&dvbdmx->mutex); +- return -ENOMEM; +- } +-#endif +- +- dvbdmxfeed->state = DMX_STATE_READY; +- mutex_unlock(&dvbdmx->mutex); +- return 0; +-} +- +-static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed) +-{ +- int i; +- struct dvb_demux_filter *f; +- struct dmx_section_filter *sf; +- u8 mask, mode, doneq; +- +- if (!(f = dvbdmxfeed->filter)) +- return; +- do { +- sf = &f->filter; +- doneq = 0; +- for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { +- mode = sf->filter_mode[i]; +- mask = sf->filter_mask[i]; +- f->maskandmode[i] = mask & mode; +- doneq |= f->maskandnotmode[i] = mask & ~mode; +- } +- f->doneq = doneq ? 1 : 0; +- } while ((f = f->next)); +-} +- +-static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- int ret; +- +- if (mutex_lock_interruptible(&dvbdmx->mutex)) +- return -ERESTARTSYS; +- +- if (feed->is_filtering) { +- mutex_unlock(&dvbdmx->mutex); +- return -EBUSY; +- } +- +- if (!dvbdmxfeed->filter) { +- mutex_unlock(&dvbdmx->mutex); +- return -EINVAL; +- } +- +- dvbdmxfeed->feed.sec.tsfeedp = 0; +- dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; +- dvbdmxfeed->feed.sec.secbufp = 0; +- dvbdmxfeed->feed.sec.seclen = 0; +- +- if (!dvbdmx->start_feed) { +- mutex_unlock(&dvbdmx->mutex); +- return -ENODEV; +- } +- +- prepare_secfilters(dvbdmxfeed); +- +- if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) { +- mutex_unlock(&dvbdmx->mutex); +- return ret; +- } +- +- spin_lock_irq(&dvbdmx->lock); +- feed->is_filtering = 1; +- dvbdmxfeed->state = DMX_STATE_GO; +- spin_unlock_irq(&dvbdmx->lock); +- +- mutex_unlock(&dvbdmx->mutex); +- return 0; +-} +- +-static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- int ret; +- +- mutex_lock(&dvbdmx->mutex); +- +- if (!dvbdmx->stop_feed) { +- mutex_unlock(&dvbdmx->mutex); +- return -ENODEV; +- } +- +- ret = dvbdmx->stop_feed(dvbdmxfeed); +- +- spin_lock_irq(&dvbdmx->lock); +- dvbdmxfeed->state = DMX_STATE_READY; +- feed->is_filtering = 0; +- spin_unlock_irq(&dvbdmx->lock); +- +- mutex_unlock(&dvbdmx->mutex); +- return ret; +-} +- +-static int dmx_section_feed_release_filter(struct dmx_section_feed *feed, +- struct dmx_section_filter *filter) +-{ +- struct dvb_demux_filter *dvbdmxfilter = (struct dvb_demux_filter *)filter, *f; +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- +- mutex_lock(&dvbdmx->mutex); +- +- if (dvbdmxfilter->feed != dvbdmxfeed) { +- mutex_unlock(&dvbdmx->mutex); +- return -EINVAL; +- } +- +- if (feed->is_filtering) +- feed->stop_filtering(feed); +- +- spin_lock_irq(&dvbdmx->lock); +- f = dvbdmxfeed->filter; +- +- if (f == dvbdmxfilter) { +- dvbdmxfeed->filter = dvbdmxfilter->next; +- } else { +- while (f->next != dvbdmxfilter) +- f = f->next; +- f->next = f->next->next; +- } +- +- dvbdmxfilter->state = DMX_STATE_FREE; +- spin_unlock_irq(&dvbdmx->lock); +- mutex_unlock(&dvbdmx->mutex); +- return 0; +-} +- +-static int dvbdmx_allocate_section_feed(struct dmx_demux *demux, +- struct dmx_section_feed **feed, +- dmx_section_cb callback) +-{ +- struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; +- struct dvb_demux_feed *dvbdmxfeed; +- +- if (mutex_lock_interruptible(&dvbdmx->mutex)) +- return -ERESTARTSYS; +- +- if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) { +- mutex_unlock(&dvbdmx->mutex); +- return -EBUSY; +- } +- +- dvbdmxfeed->type = DMX_TYPE_SEC; +- dvbdmxfeed->cb.sec = callback; +- dvbdmxfeed->demux = dvbdmx; +- dvbdmxfeed->pid = 0xffff; +- dvbdmxfeed->feed.sec.secbuf = dvbdmxfeed->feed.sec.secbuf_base; +- dvbdmxfeed->feed.sec.secbufp = dvbdmxfeed->feed.sec.seclen = 0; +- dvbdmxfeed->feed.sec.tsfeedp = 0; +- dvbdmxfeed->filter = NULL; +- dvbdmxfeed->buffer = NULL; +- +- (*feed) = &dvbdmxfeed->feed.sec; +- (*feed)->is_filtering = 0; +- (*feed)->parent = demux; +- (*feed)->priv = NULL; +- +- (*feed)->set = dmx_section_feed_set; +- (*feed)->allocate_filter = dmx_section_feed_allocate_filter; +- (*feed)->start_filtering = dmx_section_feed_start_filtering; +- (*feed)->stop_filtering = dmx_section_feed_stop_filtering; +- (*feed)->release_filter = dmx_section_feed_release_filter; +- +- mutex_unlock(&dvbdmx->mutex); +- return 0; +-} +- +-static int dvbdmx_release_section_feed(struct dmx_demux *demux, +- struct dmx_section_feed *feed) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; +- struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; +- +- mutex_lock(&dvbdmx->mutex); +- +- if (dvbdmxfeed->state == DMX_STATE_FREE) { +- mutex_unlock(&dvbdmx->mutex); +- return -EINVAL; +- } +-#ifndef NOBUFS +- vfree(dvbdmxfeed->buffer); +- dvbdmxfeed->buffer = NULL; +-#endif +- dvbdmxfeed->state = DMX_STATE_FREE; +- +- dvb_demux_feed_del(dvbdmxfeed); +- +- dvbdmxfeed->pid = 0xffff; +- +- mutex_unlock(&dvbdmx->mutex); +- return 0; +-} +- +-/****************************************************************************** +- * dvb_demux kernel data API calls +- ******************************************************************************/ +- +-static int dvbdmx_open(struct dmx_demux *demux) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- if (dvbdemux->users >= MAX_DVB_DEMUX_USERS) +- return -EUSERS; +- +- dvbdemux->users++; +- return 0; +-} +- +-static int dvbdmx_close(struct dmx_demux *demux) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- if (dvbdemux->users == 0) +- return -ENODEV; +- +- dvbdemux->users--; +- //FIXME: release any unneeded resources if users==0 +- return 0; +-} +- +-static int dvbdmx_write(struct dmx_demux *demux, const char __user *buf, size_t count) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- void *p; +- +- if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE)) +- return -EINVAL; +- +- p = memdup_user(buf, count); +- if (IS_ERR(p)) +- return PTR_ERR(p); +- if (mutex_lock_interruptible(&dvbdemux->mutex)) { +- kfree(p); +- return -ERESTARTSYS; +- } +- dvb_dmx_swfilter(dvbdemux, p, count); +- kfree(p); +- mutex_unlock(&dvbdemux->mutex); +- +- if (signal_pending(current)) +- return -EINTR; +- return count; +-} +- +-static int dvbdmx_add_frontend(struct dmx_demux *demux, +- struct dmx_frontend *frontend) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- struct list_head *head = &dvbdemux->frontend_list; +- +- list_add(&(frontend->connectivity_list), head); +- +- return 0; +-} +- +-static int dvbdmx_remove_frontend(struct dmx_demux *demux, +- struct dmx_frontend *frontend) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- struct list_head *pos, *n, *head = &dvbdemux->frontend_list; +- +- list_for_each_safe(pos, n, head) { +- if (DMX_FE_ENTRY(pos) == frontend) { +- list_del(pos); +- return 0; +- } +- } +- +- return -ENODEV; +-} +- +-static struct list_head *dvbdmx_get_frontends(struct dmx_demux *demux) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- if (list_empty(&dvbdemux->frontend_list)) +- return NULL; +- +- return &dvbdemux->frontend_list; +-} +- +-static int dvbdmx_connect_frontend(struct dmx_demux *demux, +- struct dmx_frontend *frontend) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- if (demux->frontend) +- return -EINVAL; +- +- mutex_lock(&dvbdemux->mutex); +- +- demux->frontend = frontend; +- mutex_unlock(&dvbdemux->mutex); +- return 0; +-} +- +-static int dvbdmx_disconnect_frontend(struct dmx_demux *demux) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- mutex_lock(&dvbdemux->mutex); +- +- demux->frontend = NULL; +- mutex_unlock(&dvbdemux->mutex); +- return 0; +-} +- +-static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids) +-{ +- struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; +- +- memcpy(pids, dvbdemux->pids, 5 * sizeof(u16)); +- return 0; +-} +- +-int dvb_dmx_init(struct dvb_demux *dvbdemux) +-{ +- int i; +- struct dmx_demux *dmx = &dvbdemux->dmx; +- +- dvbdemux->cnt_storage = NULL; +- dvbdemux->users = 0; +- dvbdemux->filter = vmalloc(dvbdemux->filternum * sizeof(struct dvb_demux_filter)); +- +- if (!dvbdemux->filter) +- return -ENOMEM; +- +- dvbdemux->feed = vmalloc(dvbdemux->feednum * sizeof(struct dvb_demux_feed)); +- if (!dvbdemux->feed) { +- vfree(dvbdemux->filter); +- dvbdemux->filter = NULL; +- return -ENOMEM; +- } +- for (i = 0; i < dvbdemux->filternum; i++) { +- dvbdemux->filter[i].state = DMX_STATE_FREE; +- dvbdemux->filter[i].index = i; +- } +- for (i = 0; i < dvbdemux->feednum; i++) { +- dvbdemux->feed[i].state = DMX_STATE_FREE; +- dvbdemux->feed[i].index = i; +- } +- +- dvbdemux->cnt_storage = vmalloc(MAX_PID + 1); +- if (!dvbdemux->cnt_storage) +- printk(KERN_WARNING "Couldn't allocate memory for TS/TEI check. Disabling it\n"); +- +- INIT_LIST_HEAD(&dvbdemux->frontend_list); +- +- for (i = 0; i < DMX_TS_PES_OTHER; i++) { +- dvbdemux->pesfilter[i] = NULL; +- dvbdemux->pids[i] = 0xffff; +- } +- +- INIT_LIST_HEAD(&dvbdemux->feed_list); +- +- dvbdemux->playing = 0; +- dvbdemux->recording = 0; +- dvbdemux->tsbufp = 0; +- +- if (!dvbdemux->check_crc32) +- dvbdemux->check_crc32 = dvb_dmx_crc32; +- +- if (!dvbdemux->memcopy) +- dvbdemux->memcopy = dvb_dmx_memcopy; +- +- dmx->frontend = NULL; +- dmx->priv = dvbdemux; +- dmx->open = dvbdmx_open; +- dmx->close = dvbdmx_close; +- dmx->write = dvbdmx_write; +- dmx->allocate_ts_feed = dvbdmx_allocate_ts_feed; +- dmx->release_ts_feed = dvbdmx_release_ts_feed; +- dmx->allocate_section_feed = dvbdmx_allocate_section_feed; +- dmx->release_section_feed = dvbdmx_release_section_feed; +- +- dmx->add_frontend = dvbdmx_add_frontend; +- dmx->remove_frontend = dvbdmx_remove_frontend; +- dmx->get_frontends = dvbdmx_get_frontends; +- dmx->connect_frontend = dvbdmx_connect_frontend; +- dmx->disconnect_frontend = dvbdmx_disconnect_frontend; +- dmx->get_pes_pids = dvbdmx_get_pes_pids; +- +- mutex_init(&dvbdemux->mutex); +- spin_lock_init(&dvbdemux->lock); +- +- return 0; +-} +- +-EXPORT_SYMBOL(dvb_dmx_init); +- +-void dvb_dmx_release(struct dvb_demux *dvbdemux) +-{ +- vfree(dvbdemux->cnt_storage); +- vfree(dvbdemux->filter); +- vfree(dvbdemux->feed); +-} +- +-EXPORT_SYMBOL(dvb_dmx_release); +diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h +deleted file mode 100644 +index a7d876f..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_demux.h ++++ /dev/null +@@ -1,149 +0,0 @@ +-/* +- * dvb_demux.h: DVB kernel demux API +- * +- * Copyright (C) 2000-2001 Marcus Metzler & Ralph Metzler +- * for convergence integrated media GmbH +- * +- * 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. +- * +- */ +- +-#ifndef _DVB_DEMUX_H_ +-#define _DVB_DEMUX_H_ +- +-#include +-#include +-#include +-#include +- +-#include "demux.h" +- +-#define DMX_TYPE_TS 0 +-#define DMX_TYPE_SEC 1 +-#define DMX_TYPE_PES 2 +- +-#define DMX_STATE_FREE 0 +-#define DMX_STATE_ALLOCATED 1 +-#define DMX_STATE_SET 2 +-#define DMX_STATE_READY 3 +-#define DMX_STATE_GO 4 +- +-#define DVB_DEMUX_MASK_MAX 18 +- +-#define MAX_PID 0x1fff +- +-#define SPEED_PKTS_INTERVAL 50000 +- +-struct dvb_demux_filter { +- struct dmx_section_filter filter; +- u8 maskandmode[DMX_MAX_FILTER_SIZE]; +- u8 maskandnotmode[DMX_MAX_FILTER_SIZE]; +- int doneq; +- +- struct dvb_demux_filter *next; +- struct dvb_demux_feed *feed; +- int index; +- int state; +- int type; +- +- u16 hw_handle; +- struct timer_list timer; +-}; +- +-#define DMX_FEED_ENTRY(pos) list_entry(pos, struct dvb_demux_feed, list_head) +- +-struct dvb_demux_feed { +- union { +- struct dmx_ts_feed ts; +- struct dmx_section_feed sec; +- } feed; +- +- union { +- dmx_ts_cb ts; +- dmx_section_cb sec; +- } cb; +- +- struct dvb_demux *demux; +- void *priv; +- int type; +- int state; +- u16 pid; +- u8 *buffer; +- int buffer_size; +- +- struct timespec timeout; +- struct dvb_demux_filter *filter; +- +- int ts_type; +- enum dmx_ts_pes pes_type; +- +- int cc; +- int pusi_seen; /* prevents feeding of garbage from previous section */ +- +- u16 peslen; +- +- struct list_head list_head; +- unsigned int index; /* a unique index for each feed (can be used as hardware pid filter index) */ +-}; +- +-struct dvb_demux { +- struct dmx_demux dmx; +- void *priv; +- int filternum; +- int feednum; +- int (*start_feed)(struct dvb_demux_feed *feed); +- int (*stop_feed)(struct dvb_demux_feed *feed); +- int (*write_to_decoder)(struct dvb_demux_feed *feed, +- const u8 *buf, size_t len); +- u32 (*check_crc32)(struct dvb_demux_feed *feed, +- const u8 *buf, size_t len); +- void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst, +- const u8 *src, size_t len); +- +- int users; +-#define MAX_DVB_DEMUX_USERS 10 +- struct dvb_demux_filter *filter; +- struct dvb_demux_feed *feed; +- +- struct list_head frontend_list; +- +- struct dvb_demux_feed *pesfilter[DMX_TS_PES_OTHER]; +- u16 pids[DMX_TS_PES_OTHER]; +- int playing; +- int recording; +- +-#define DMX_MAX_PID 0x2000 +- struct list_head feed_list; +- u8 tsbuf[204]; +- int tsbufp; +- +- struct mutex mutex; +- spinlock_t lock; +- +- uint8_t *cnt_storage; /* for TS continuity check */ +- +- struct timespec speed_last_time; /* for TS speed check */ +- uint32_t speed_pkts_cnt; /* for TS speed check */ +-}; +- +-int dvb_dmx_init(struct dvb_demux *dvbdemux); +-void dvb_dmx_release(struct dvb_demux *dvbdemux); +-void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, +- size_t count); +-void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count); +-void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, +- size_t count); +- +-#endif /* _DVB_DEMUX_H_ */ +diff --git a/drivers/media/dvb/dvb-core/dvb_filter.c b/drivers/media/dvb/dvb-core/dvb_filter.c +deleted file mode 100644 +index 772003f..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_filter.c ++++ /dev/null +@@ -1,603 +0,0 @@ +-#include +-#include +-#include +-#include "dvb_filter.h" +- +-#if 0 +-static unsigned int bitrates[3][16] = +-{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0}, +- {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,0}, +- {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,0}}; +-#endif +- +-static u32 freq[4] = {480, 441, 320, 0}; +- +-static unsigned int ac3_bitrates[32] = +- {32,40,48,56,64,80,96,112,128,160,192,224,256,320,384,448,512,576,640, +- 0,0,0,0,0,0,0,0,0,0,0,0,0}; +- +-static u32 ac3_frames[3][32] = +- {{64,80,96,112,128,160,192,224,256,320,384,448,512,640,768,896,1024, +- 1152,1280,0,0,0,0,0,0,0,0,0,0,0,0,0}, +- {69,87,104,121,139,174,208,243,278,348,417,487,557,696,835,975,1114, +- 1253,1393,0,0,0,0,0,0,0,0,0,0,0,0,0}, +- {96,120,144,168,192,240,288,336,384,480,576,672,768,960,1152,1344, +- 1536,1728,1920,0,0,0,0,0,0,0,0,0,0,0,0,0}}; +- +- +- +-#if 0 +-static void setup_ts2pes(ipack *pa, ipack *pv, u16 *pida, u16 *pidv, +- void (*pes_write)(u8 *buf, int count, void *data), +- void *priv) +-{ +- dvb_filter_ipack_init(pa, IPACKS, pes_write); +- dvb_filter_ipack_init(pv, IPACKS, pes_write); +- pa->pid = pida; +- pv->pid = pidv; +- pa->data = priv; +- pv->data = priv; +-} +-#endif +- +-#if 0 +-static void ts_to_pes(ipack *p, u8 *buf) // don't need count (=188) +-{ +- u8 off = 0; +- +- if (!buf || !p ){ +- printk("NULL POINTER IDIOT\n"); +- return; +- } +- if (buf[1]&PAY_START) { +- if (p->plength == MMAX_PLENGTH-6 && p->found>6){ +- p->plength = p->found-6; +- p->found = 0; +- send_ipack(p); +- dvb_filter_ipack_reset(p); +- } +- } +- if (buf[3] & ADAPT_FIELD) { // adaptation field? +- off = buf[4] + 1; +- if (off+4 > 187) return; +- } +- dvb_filter_instant_repack(buf+4+off, TS_SIZE-4-off, p); +-} +-#endif +- +-#if 0 +-/* needs 5 byte input, returns picture coding type*/ +-static int read_picture_header(u8 *headr, struct mpg_picture *pic, int field, int pr) +-{ +- u8 pct; +- +- if (pr) printk( "Pic header: "); +- pic->temporal_reference[field] = (( headr[0] << 2 ) | +- (headr[1] & 0x03) )& 0x03ff; +- if (pr) printk( " temp ref: 0x%04x", pic->temporal_reference[field]); +- +- pct = ( headr[1] >> 2 ) & 0x07; +- pic->picture_coding_type[field] = pct; +- if (pr) { +- switch(pct){ +- case I_FRAME: +- printk( " I-FRAME"); +- break; +- case B_FRAME: +- printk( " B-FRAME"); +- break; +- case P_FRAME: +- printk( " P-FRAME"); +- break; +- } +- } +- +- +- pic->vinfo.vbv_delay = (( headr[1] >> 5 ) | ( headr[2] << 3) | +- ( (headr[3] & 0x1F) << 11) ) & 0xffff; +- +- if (pr) printk( " vbv delay: 0x%04x", pic->vinfo.vbv_delay); +- +- pic->picture_header_parameter = ( headr[3] & 0xe0 ) | +- ((headr[4] & 0x80) >> 3); +- +- if ( pct == B_FRAME ){ +- pic->picture_header_parameter |= ( headr[4] >> 3 ) & 0x0f; +- } +- if (pr) printk( " pic head param: 0x%x", +- pic->picture_header_parameter); +- +- return pct; +-} +-#endif +- +-#if 0 +-/* needs 4 byte input */ +-static int read_gop_header(u8 *headr, struct mpg_picture *pic, int pr) +-{ +- if (pr) printk("GOP header: "); +- +- pic->time_code = (( headr[0] << 17 ) | ( headr[1] << 9) | +- ( headr[2] << 1 ) | (headr[3] &0x01)) & 0x1ffffff; +- +- if (pr) printk(" time: %d:%d.%d ", (headr[0]>>2)& 0x1F, +- ((headr[0]<<4)& 0x30)| ((headr[1]>>4)& 0x0F), +- ((headr[1]<<3)& 0x38)| ((headr[2]>>5)& 0x0F)); +- +- if ( ( headr[3] & 0x40 ) != 0 ){ +- pic->closed_gop = 1; +- } else { +- pic->closed_gop = 0; +- } +- if (pr) printk("closed: %d", pic->closed_gop); +- +- if ( ( headr[3] & 0x20 ) != 0 ){ +- pic->broken_link = 1; +- } else { +- pic->broken_link = 0; +- } +- if (pr) printk(" broken: %d\n", pic->broken_link); +- +- return 0; +-} +-#endif +- +-#if 0 +-/* needs 8 byte input */ +-static int read_sequence_header(u8 *headr, struct dvb_video_info *vi, int pr) +-{ +- int sw; +- int form = -1; +- +- if (pr) printk("Reading sequence header\n"); +- +- vi->horizontal_size = ((headr[1] &0xF0) >> 4) | (headr[0] << 4); +- vi->vertical_size = ((headr[1] &0x0F) << 8) | (headr[2]); +- +- sw = (int)((headr[3]&0xF0) >> 4) ; +- +- switch( sw ){ +- case 1: +- if (pr) +- printk("Videostream: ASPECT: 1:1"); +- vi->aspect_ratio = 100; +- break; +- case 2: +- if (pr) +- printk("Videostream: ASPECT: 4:3"); +- vi->aspect_ratio = 133; +- break; +- case 3: +- if (pr) +- printk("Videostream: ASPECT: 16:9"); +- vi->aspect_ratio = 177; +- break; +- case 4: +- if (pr) +- printk("Videostream: ASPECT: 2.21:1"); +- vi->aspect_ratio = 221; +- break; +- +- case 5 ... 15: +- if (pr) +- printk("Videostream: ASPECT: reserved"); +- vi->aspect_ratio = 0; +- break; +- +- default: +- vi->aspect_ratio = 0; +- return -1; +- } +- +- if (pr) +- printk(" Size = %dx%d",vi->horizontal_size,vi->vertical_size); +- +- sw = (int)(headr[3]&0x0F); +- +- switch ( sw ) { +- case 1: +- if (pr) +- printk(" FRate: 23.976 fps"); +- vi->framerate = 23976; +- form = -1; +- break; +- case 2: +- if (pr) +- printk(" FRate: 24 fps"); +- vi->framerate = 24000; +- form = -1; +- break; +- case 3: +- if (pr) +- printk(" FRate: 25 fps"); +- vi->framerate = 25000; +- form = VIDEO_MODE_PAL; +- break; +- case 4: +- if (pr) +- printk(" FRate: 29.97 fps"); +- vi->framerate = 29970; +- form = VIDEO_MODE_NTSC; +- break; +- case 5: +- if (pr) +- printk(" FRate: 30 fps"); +- vi->framerate = 30000; +- form = VIDEO_MODE_NTSC; +- break; +- case 6: +- if (pr) +- printk(" FRate: 50 fps"); +- vi->framerate = 50000; +- form = VIDEO_MODE_PAL; +- break; +- case 7: +- if (pr) +- printk(" FRate: 60 fps"); +- vi->framerate = 60000; +- form = VIDEO_MODE_NTSC; +- break; +- } +- +- vi->bit_rate = (headr[4] << 10) | (headr[5] << 2) | (headr[6] & 0x03); +- +- vi->vbv_buffer_size +- = (( headr[6] & 0xF8) >> 3 ) | (( headr[7] & 0x1F )<< 5); +- +- if (pr){ +- printk(" BRate: %d Mbit/s",4*(vi->bit_rate)/10000); +- printk(" vbvbuffer %d",16*1024*(vi->vbv_buffer_size)); +- printk("\n"); +- } +- +- vi->video_format = form; +- +- return 0; +-} +-#endif +- +- +-#if 0 +-static int get_vinfo(u8 *mbuf, int count, struct dvb_video_info *vi, int pr) +-{ +- u8 *headr; +- int found = 0; +- int c = 0; +- +- while (found < 4 && c+4 < count){ +- u8 *b; +- +- b = mbuf+c; +- if ( b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01 +- && b[3] == 0xb3) found = 4; +- else { +- c++; +- } +- } +- +- if (! found) return -1; +- c += 4; +- if (c+12 >= count) return -1; +- headr = mbuf+c; +- if (read_sequence_header(headr, vi, pr) < 0) return -1; +- vi->off = c-4; +- return 0; +-} +-#endif +- +- +-#if 0 +-static int get_ainfo(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) +-{ +- u8 *headr; +- int found = 0; +- int c = 0; +- int fr = 0; +- +- while (found < 2 && c < count){ +- u8 b[2]; +- memcpy( b, mbuf+c, 2); +- +- if ( b[0] == 0xff && (b[1] & 0xf8) == 0xf8) +- found = 2; +- else { +- c++; +- } +- } +- +- if (!found) return -1; +- +- if (c+3 >= count) return -1; +- headr = mbuf+c; +- +- ai->layer = (headr[1] & 0x06) >> 1; +- +- if (pr) +- printk("Audiostream: Layer: %d", 4-ai->layer); +- +- +- ai->bit_rate = bitrates[(3-ai->layer)][(headr[2] >> 4 )]*1000; +- +- if (pr){ +- if (ai->bit_rate == 0) +- printk(" Bit rate: free"); +- else if (ai->bit_rate == 0xf) +- printk(" BRate: reserved"); +- else +- printk(" BRate: %d kb/s", ai->bit_rate/1000); +- } +- +- fr = (headr[2] & 0x0c ) >> 2; +- ai->frequency = freq[fr]*100; +- if (pr){ +- if (ai->frequency == 3) +- printk(" Freq: reserved\n"); +- else +- printk(" Freq: %d kHz\n",ai->frequency); +- +- } +- ai->off = c; +- return 0; +-} +-#endif +- +- +-int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr) +-{ +- u8 *headr; +- int found = 0; +- int c = 0; +- u8 frame = 0; +- int fr = 0; +- +- while ( !found && c < count){ +- u8 *b = mbuf+c; +- +- if ( b[0] == 0x0b && b[1] == 0x77 ) +- found = 1; +- else { +- c++; +- } +- } +- +- if (!found) return -1; +- if (pr) +- printk("Audiostream: AC3"); +- +- ai->off = c; +- if (c+5 >= count) return -1; +- +- ai->layer = 0; // 0 for AC3 +- headr = mbuf+c+2; +- +- frame = (headr[2]&0x3f); +- ai->bit_rate = ac3_bitrates[frame >> 1]*1000; +- +- if (pr) +- printk(" BRate: %d kb/s", (int) ai->bit_rate/1000); +- +- ai->frequency = (headr[2] & 0xc0 ) >> 6; +- fr = (headr[2] & 0xc0 ) >> 6; +- ai->frequency = freq[fr]*100; +- if (pr) printk (" Freq: %d Hz\n", (int) ai->frequency); +- +- +- ai->framesize = ac3_frames[fr][frame >> 1]; +- if ((frame & 1) && (fr == 1)) ai->framesize++; +- ai->framesize = ai->framesize << 1; +- if (pr) printk (" Framesize %d\n",(int) ai->framesize); +- +- +- return 0; +-} +-EXPORT_SYMBOL(dvb_filter_get_ac3info); +- +- +-#if 0 +-static u8 *skip_pes_header(u8 **bufp) +-{ +- u8 *inbuf = *bufp; +- u8 *buf = inbuf; +- u8 *pts = NULL; +- int skip = 0; +- +- static const int mpeg1_skip_table[16] = { +- 1, 0xffff, 5, 10, 0xffff, 0xffff, 0xffff, 0xffff, +- 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +- }; +- +- +- if ((inbuf[6] & 0xc0) == 0x80){ /* mpeg2 */ +- if (buf[7] & PTS_ONLY) +- pts = buf+9; +- else pts = NULL; +- buf = inbuf + 9 + inbuf[8]; +- } else { /* mpeg1 */ +- for (buf = inbuf + 6; *buf == 0xff; buf++) +- if (buf == inbuf + 6 + 16) { +- break; +- } +- if ((*buf & 0xc0) == 0x40) +- buf += 2; +- skip = mpeg1_skip_table [*buf >> 4]; +- if (skip == 5 || skip == 10) pts = buf; +- else pts = NULL; +- +- buf += mpeg1_skip_table [*buf >> 4]; +- } +- +- *bufp = buf; +- return pts; +-} +-#endif +- +-#if 0 +-static void initialize_quant_matrix( u32 *matrix ) +-{ +- int i; +- +- matrix[0] = 0x08101013; +- matrix[1] = 0x10131616; +- matrix[2] = 0x16161616; +- matrix[3] = 0x1a181a1b; +- matrix[4] = 0x1b1b1a1a; +- matrix[5] = 0x1a1a1b1b; +- matrix[6] = 0x1b1d1d1d; +- matrix[7] = 0x2222221d; +- matrix[8] = 0x1d1d1b1b; +- matrix[9] = 0x1d1d2020; +- matrix[10] = 0x22222526; +- matrix[11] = 0x25232322; +- matrix[12] = 0x23262628; +- matrix[13] = 0x28283030; +- matrix[14] = 0x2e2e3838; +- matrix[15] = 0x3a454553; +- +- for ( i = 16 ; i < 32 ; i++ ) +- matrix[i] = 0x10101010; +-} +-#endif +- +-#if 0 +-static void initialize_mpg_picture(struct mpg_picture *pic) +-{ +- int i; +- +- /* set MPEG1 */ +- pic->mpeg1_flag = 1; +- pic->profile_and_level = 0x4A ; /* MP@LL */ +- pic->progressive_sequence = 1; +- pic->low_delay = 0; +- +- pic->sequence_display_extension_flag = 0; +- for ( i = 0 ; i < 4 ; i++ ){ +- pic->frame_centre_horizontal_offset[i] = 0; +- pic->frame_centre_vertical_offset[i] = 0; +- } +- pic->last_frame_centre_horizontal_offset = 0; +- pic->last_frame_centre_vertical_offset = 0; +- +- pic->picture_display_extension_flag[0] = 0; +- pic->picture_display_extension_flag[1] = 0; +- pic->sequence_header_flag = 0; +- pic->gop_flag = 0; +- pic->sequence_end_flag = 0; +-} +-#endif +- +-#if 0 +-static void mpg_set_picture_parameter( int32_t field_type, struct mpg_picture *pic ) +-{ +- int16_t last_h_offset; +- int16_t last_v_offset; +- +- int16_t *p_h_offset; +- int16_t *p_v_offset; +- +- if ( pic->mpeg1_flag ){ +- pic->picture_structure[field_type] = VIDEO_FRAME_PICTURE; +- pic->top_field_first = 0; +- pic->repeat_first_field = 0; +- pic->progressive_frame = 1; +- pic->picture_coding_parameter = 0x000010; +- } +- +- /* Reset flag */ +- pic->picture_display_extension_flag[field_type] = 0; +- +- last_h_offset = pic->last_frame_centre_horizontal_offset; +- last_v_offset = pic->last_frame_centre_vertical_offset; +- if ( field_type == FIRST_FIELD ){ +- p_h_offset = pic->frame_centre_horizontal_offset; +- p_v_offset = pic->frame_centre_vertical_offset; +- *p_h_offset = last_h_offset; +- *(p_h_offset + 1) = last_h_offset; +- *(p_h_offset + 2) = last_h_offset; +- *p_v_offset = last_v_offset; +- *(p_v_offset + 1) = last_v_offset; +- *(p_v_offset + 2) = last_v_offset; +- } else { +- pic->frame_centre_horizontal_offset[3] = last_h_offset; +- pic->frame_centre_vertical_offset[3] = last_v_offset; +- } +-} +-#endif +- +-#if 0 +-static void init_mpg_picture( struct mpg_picture *pic, int chan, int32_t field_type) +-{ +- pic->picture_header = 0; +- pic->sequence_header_data +- = ( INIT_HORIZONTAL_SIZE << 20 ) +- | ( INIT_VERTICAL_SIZE << 8 ) +- | ( INIT_ASPECT_RATIO << 4 ) +- | ( INIT_FRAME_RATE ); +- pic->mpeg1_flag = 0; +- pic->vinfo.horizontal_size +- = INIT_DISP_HORIZONTAL_SIZE; +- pic->vinfo.vertical_size +- = INIT_DISP_VERTICAL_SIZE; +- pic->picture_display_extension_flag[field_type] +- = 0; +- pic->pts_flag[field_type] = 0; +- +- pic->sequence_gop_header = 0; +- pic->picture_header = 0; +- pic->sequence_header_flag = 0; +- pic->gop_flag = 0; +- pic->sequence_end_flag = 0; +- pic->sequence_display_extension_flag = 0; +- pic->last_frame_centre_horizontal_offset = 0; +- pic->last_frame_centre_vertical_offset = 0; +- pic->channel = chan; +-} +-#endif +- +-void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, +- dvb_filter_pes2ts_cb_t *cb, void *priv) +-{ +- unsigned char *buf=p2ts->buf; +- +- buf[0]=0x47; +- buf[1]=(pid>>8); +- buf[2]=pid&0xff; +- p2ts->cc=0; +- p2ts->cb=cb; +- p2ts->priv=priv; +-} +-EXPORT_SYMBOL(dvb_filter_pes2ts_init); +- +-int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, +- int len, int payload_start) +-{ +- unsigned char *buf=p2ts->buf; +- int ret=0, rest; +- +- //len=6+((pes[4]<<8)|pes[5]); +- +- if (payload_start) +- buf[1]|=0x40; +- else +- buf[1]&=~0x40; +- while (len>=184) { +- buf[3]=0x10|((p2ts->cc++)&0x0f); +- memcpy(buf+4, pes, 184); +- if ((ret=p2ts->cb(p2ts->priv, buf))) +- return ret; +- len-=184; pes+=184; +- buf[1]&=~0x40; +- } +- if (!len) +- return 0; +- buf[3]=0x30|((p2ts->cc++)&0x0f); +- rest=183-len; +- if (rest) { +- buf[5]=0x00; +- if (rest-1) +- memset(buf+6, 0xff, rest-1); +- } +- buf[4]=rest; +- memcpy(buf+5+rest, pes, len); +- return p2ts->cb(p2ts->priv, buf); +-} +-EXPORT_SYMBOL(dvb_filter_pes2ts); +diff --git a/drivers/media/dvb/dvb-core/dvb_filter.h b/drivers/media/dvb/dvb-core/dvb_filter.h +deleted file mode 100644 +index 375e3be..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_filter.h ++++ /dev/null +@@ -1,246 +0,0 @@ +-/* +- * dvb_filter.h +- * +- * Copyright (C) 2003 Convergence GmbH +- * +- * 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. +- */ +- +-#ifndef _DVB_FILTER_H_ +-#define _DVB_FILTER_H_ +- +-#include +- +-#include "demux.h" +- +-typedef int (dvb_filter_pes2ts_cb_t) (void *, unsigned char *); +- +-struct dvb_filter_pes2ts { +- unsigned char buf[188]; +- unsigned char cc; +- dvb_filter_pes2ts_cb_t *cb; +- void *priv; +-}; +- +-void dvb_filter_pes2ts_init(struct dvb_filter_pes2ts *p2ts, unsigned short pid, +- dvb_filter_pes2ts_cb_t *cb, void *priv); +- +-int dvb_filter_pes2ts(struct dvb_filter_pes2ts *p2ts, unsigned char *pes, +- int len, int payload_start); +- +- +-#define PROG_STREAM_MAP 0xBC +-#define PRIVATE_STREAM1 0xBD +-#define PADDING_STREAM 0xBE +-#define PRIVATE_STREAM2 0xBF +-#define AUDIO_STREAM_S 0xC0 +-#define AUDIO_STREAM_E 0xDF +-#define VIDEO_STREAM_S 0xE0 +-#define VIDEO_STREAM_E 0xEF +-#define ECM_STREAM 0xF0 +-#define EMM_STREAM 0xF1 +-#define DSM_CC_STREAM 0xF2 +-#define ISO13522_STREAM 0xF3 +-#define PROG_STREAM_DIR 0xFF +- +-#define DVB_PICTURE_START 0x00 +-#define DVB_USER_START 0xb2 +-#define DVB_SEQUENCE_HEADER 0xb3 +-#define DVB_SEQUENCE_ERROR 0xb4 +-#define DVB_EXTENSION_START 0xb5 +-#define DVB_SEQUENCE_END 0xb7 +-#define DVB_GOP_START 0xb8 +-#define DVB_EXCEPT_SLICE 0xb0 +- +-#define SEQUENCE_EXTENSION 0x01 +-#define SEQUENCE_DISPLAY_EXTENSION 0x02 +-#define PICTURE_CODING_EXTENSION 0x08 +-#define QUANT_MATRIX_EXTENSION 0x03 +-#define PICTURE_DISPLAY_EXTENSION 0x07 +- +-#define I_FRAME 0x01 +-#define B_FRAME 0x02 +-#define P_FRAME 0x03 +- +-/* Initialize sequence_data */ +-#define INIT_HORIZONTAL_SIZE 720 +-#define INIT_VERTICAL_SIZE 576 +-#define INIT_ASPECT_RATIO 0x02 +-#define INIT_FRAME_RATE 0x03 +-#define INIT_DISP_HORIZONTAL_SIZE 540 +-#define INIT_DISP_VERTICAL_SIZE 576 +- +- +-//flags2 +-#define PTS_DTS_FLAGS 0xC0 +-#define ESCR_FLAG 0x20 +-#define ES_RATE_FLAG 0x10 +-#define DSM_TRICK_FLAG 0x08 +-#define ADD_CPY_FLAG 0x04 +-#define PES_CRC_FLAG 0x02 +-#define PES_EXT_FLAG 0x01 +- +-//pts_dts flags +-#define PTS_ONLY 0x80 +-#define PTS_DTS 0xC0 +- +-#define TS_SIZE 188 +-#define TRANS_ERROR 0x80 +-#define PAY_START 0x40 +-#define TRANS_PRIO 0x20 +-#define PID_MASK_HI 0x1F +-//flags +-#define TRANS_SCRMBL1 0x80 +-#define TRANS_SCRMBL2 0x40 +-#define ADAPT_FIELD 0x20 +-#define PAYLOAD 0x10 +-#define COUNT_MASK 0x0F +- +-// adaptation flags +-#define DISCON_IND 0x80 +-#define RAND_ACC_IND 0x40 +-#define ES_PRI_IND 0x20 +-#define PCR_FLAG 0x10 +-#define OPCR_FLAG 0x08 +-#define SPLICE_FLAG 0x04 +-#define TRANS_PRIV 0x02 +-#define ADAP_EXT_FLAG 0x01 +- +-// adaptation extension flags +-#define LTW_FLAG 0x80 +-#define PIECE_RATE 0x40 +-#define SEAM_SPLICE 0x20 +- +- +-#define MAX_PLENGTH 0xFFFF +-#define MMAX_PLENGTH (256*MAX_PLENGTH) +- +-#ifndef IPACKS +-#define IPACKS 2048 +-#endif +- +-struct ipack { +- int size; +- int found; +- u8 *buf; +- u8 cid; +- u32 plength; +- u8 plen[2]; +- u8 flag1; +- u8 flag2; +- u8 hlength; +- u8 pts[5]; +- u16 *pid; +- int mpeg; +- u8 check; +- int which; +- int done; +- void *data; +- void (*func)(u8 *buf, int size, void *priv); +- int count; +- int repack_subids; +-}; +- +-struct dvb_video_info { +- u32 horizontal_size; +- u32 vertical_size; +- u32 aspect_ratio; +- u32 framerate; +- u32 video_format; +- u32 bit_rate; +- u32 comp_bit_rate; +- u32 vbv_buffer_size; +- s16 vbv_delay; +- u32 CSPF; +- u32 off; +-}; +- +-#define OFF_SIZE 4 +-#define FIRST_FIELD 0 +-#define SECOND_FIELD 1 +-#define VIDEO_FRAME_PICTURE 0x03 +- +-struct mpg_picture { +- int channel; +- struct dvb_video_info vinfo; +- u32 *sequence_gop_header; +- u32 *picture_header; +- s32 time_code; +- int low_delay; +- int closed_gop; +- int broken_link; +- int sequence_header_flag; +- int gop_flag; +- int sequence_end_flag; +- +- u8 profile_and_level; +- s32 picture_coding_parameter; +- u32 matrix[32]; +- s8 matrix_change_flag; +- +- u8 picture_header_parameter; +- /* bit 0 - 2: bwd f code +- bit 3 : fpb vector +- bit 4 - 6: fwd f code +- bit 7 : fpf vector */ +- +- int mpeg1_flag; +- int progressive_sequence; +- int sequence_display_extension_flag; +- u32 sequence_header_data; +- s16 last_frame_centre_horizontal_offset; +- s16 last_frame_centre_vertical_offset; +- +- u32 pts[2]; /* [0] 1st field, [1] 2nd field */ +- int top_field_first; +- int repeat_first_field; +- int progressive_frame; +- int bank; +- int forward_bank; +- int backward_bank; +- int compress; +- s16 frame_centre_horizontal_offset[OFF_SIZE]; +- /* [0-2] 1st field, [3] 2nd field */ +- s16 frame_centre_vertical_offset[OFF_SIZE]; +- /* [0-2] 1st field, [3] 2nd field */ +- s16 temporal_reference[2]; +- /* [0] 1st field, [1] 2nd field */ +- +- s8 picture_coding_type[2]; +- /* [0] 1st field, [1] 2nd field */ +- s8 picture_structure[2]; +- /* [0] 1st field, [1] 2nd field */ +- s8 picture_display_extension_flag[2]; +- /* [0] 1st field, [1] 2nd field */ +- /* picture_display_extenion() 0:no 1:exit*/ +- s8 pts_flag[2]; +- /* [0] 1st field, [1] 2nd field */ +-}; +- +-struct dvb_audio_info { +- int layer; +- u32 bit_rate; +- u32 frequency; +- u32 mode; +- u32 mode_extension ; +- u32 emphasis; +- u32 framesize; +- u32 off; +-}; +- +-int dvb_filter_get_ac3info(u8 *mbuf, int count, struct dvb_audio_info *ai, int pr); +- +- +-#endif +diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c +deleted file mode 100644 +index bf94380..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_frontend.c ++++ /dev/null +@@ -1,2481 +0,0 @@ +-/* +- * dvb_frontend.c: DVB frontend tuning interface/thread +- * +- * +- * Copyright (C) 1999-2001 Ralph Metzler +- * Marcus Metzler +- * Holger Waechtler +- * for convergence integrated media GmbH +- * +- * Copyright (C) 2004 Andrew de Quincey (tuning thread cleanup) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-/* Enables DVBv3 compatibility bits at the headers */ +-#define __DVB_CORE__ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "dvbdev.h" +-#include +- +-static int dvb_frontend_debug; +-static int dvb_shutdown_timeout; +-static int dvb_force_auto_inversion; +-static int dvb_override_tune_delay; +-static int dvb_powerdown_on_sleep = 1; +-static int dvb_mfe_wait_time = 5; +- +-module_param_named(frontend_debug, dvb_frontend_debug, int, 0644); +-MODULE_PARM_DESC(frontend_debug, "Turn on/off frontend core debugging (default:off)."); +-module_param(dvb_shutdown_timeout, int, 0644); +-MODULE_PARM_DESC(dvb_shutdown_timeout, "wait seconds after close() before suspending hardware"); +-module_param(dvb_force_auto_inversion, int, 0644); +-MODULE_PARM_DESC(dvb_force_auto_inversion, "0: normal (default), 1: INVERSION_AUTO forced always"); +-module_param(dvb_override_tune_delay, int, 0644); +-MODULE_PARM_DESC(dvb_override_tune_delay, "0: normal (default), >0 => delay in milliseconds to wait for lock after a tune attempt"); +-module_param(dvb_powerdown_on_sleep, int, 0644); +-MODULE_PARM_DESC(dvb_powerdown_on_sleep, "0: do not power down, 1: turn LNB voltage off on sleep (default)"); +-module_param(dvb_mfe_wait_time, int, 0644); +-MODULE_PARM_DESC(dvb_mfe_wait_time, "Wait up to seconds on open() for multi-frontend to become available (default:5 seconds)"); +- +-#define dprintk if (dvb_frontend_debug) printk +- +-#define FESTATE_IDLE 1 +-#define FESTATE_RETUNE 2 +-#define FESTATE_TUNING_FAST 4 +-#define FESTATE_TUNING_SLOW 8 +-#define FESTATE_TUNED 16 +-#define FESTATE_ZIGZAG_FAST 32 +-#define FESTATE_ZIGZAG_SLOW 64 +-#define FESTATE_DISEQC 128 +-#define FESTATE_ERROR 256 +-#define FESTATE_WAITFORLOCK (FESTATE_TUNING_FAST | FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW | FESTATE_DISEQC) +-#define FESTATE_SEARCHING_FAST (FESTATE_TUNING_FAST | FESTATE_ZIGZAG_FAST) +-#define FESTATE_SEARCHING_SLOW (FESTATE_TUNING_SLOW | FESTATE_ZIGZAG_SLOW) +-#define FESTATE_LOSTLOCK (FESTATE_ZIGZAG_FAST | FESTATE_ZIGZAG_SLOW) +- +-#define FE_ALGO_HW 1 +-/* +- * FESTATE_IDLE. No tuning parameters have been supplied and the loop is idling. +- * FESTATE_RETUNE. Parameters have been supplied, but we have not yet performed the first tune. +- * FESTATE_TUNING_FAST. Tuning parameters have been supplied and fast zigzag scan is in progress. +- * FESTATE_TUNING_SLOW. Tuning parameters have been supplied. Fast zigzag failed, so we're trying again, but slower. +- * FESTATE_TUNED. The frontend has successfully locked on. +- * FESTATE_ZIGZAG_FAST. The lock has been lost, and a fast zigzag has been initiated to try and regain it. +- * FESTATE_ZIGZAG_SLOW. The lock has been lost. Fast zigzag has been failed, so we're trying again, but slower. +- * FESTATE_DISEQC. A DISEQC command has just been issued. +- * FESTATE_WAITFORLOCK. When we're waiting for a lock. +- * FESTATE_SEARCHING_FAST. When we're searching for a signal using a fast zigzag scan. +- * FESTATE_SEARCHING_SLOW. When we're searching for a signal using a slow zigzag scan. +- * FESTATE_LOSTLOCK. When the lock has been lost, and we're searching it again. +- */ +- +-#define DVB_FE_NO_EXIT 0 +-#define DVB_FE_NORMAL_EXIT 1 +-#define DVB_FE_DEVICE_REMOVED 2 +- +-static DEFINE_MUTEX(frontend_mutex); +- +-struct dvb_frontend_private { +- +- /* thread/frontend values */ +- struct dvb_device *dvbdev; +- struct dvb_frontend_parameters parameters_out; +- struct dvb_fe_events events; +- struct semaphore sem; +- struct list_head list_head; +- wait_queue_head_t wait_queue; +- struct task_struct *thread; +- unsigned long release_jiffies; +- unsigned int exit; +- unsigned int wakeup; +- fe_status_t status; +- unsigned long tune_mode_flags; +- unsigned int delay; +- unsigned int reinitialise; +- int tone; +- int voltage; +- +- /* swzigzag values */ +- unsigned int state; +- unsigned int bending; +- int lnb_drift; +- unsigned int inversion; +- unsigned int auto_step; +- unsigned int auto_sub_step; +- unsigned int started_auto_step; +- unsigned int min_delay; +- unsigned int max_drift; +- unsigned int step_size; +- int quality; +- unsigned int check_wrapped; +- enum dvbfe_search algo_status; +-}; +- +-static void dvb_frontend_wakeup(struct dvb_frontend *fe); +-static int dtv_get_frontend(struct dvb_frontend *fe, +- struct dvb_frontend_parameters *p_out); +-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe, +- struct dvb_frontend_parameters *p); +- +-static bool has_get_frontend(struct dvb_frontend *fe) +-{ +- return fe->ops.get_frontend; +-} +- +-/* +- * Due to DVBv3 API calls, a delivery system should be mapped into one of +- * the 4 DVBv3 delivery systems (FE_QPSK, FE_QAM, FE_OFDM or FE_ATSC), +- * otherwise, a DVBv3 call will fail. +- */ +-enum dvbv3_emulation_type { +- DVBV3_UNKNOWN, +- DVBV3_QPSK, +- DVBV3_QAM, +- DVBV3_OFDM, +- DVBV3_ATSC, +-}; +- +-static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system) +-{ +- switch (delivery_system) { +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- return DVBV3_QAM; +- case SYS_DVBS: +- case SYS_DVBS2: +- case SYS_TURBO: +- case SYS_ISDBS: +- case SYS_DSS: +- return DVBV3_QPSK; +- case SYS_DVBT: +- case SYS_DVBT2: +- case SYS_ISDBT: +- case SYS_DMBTH: +- return DVBV3_OFDM; +- case SYS_ATSC: +- case SYS_DVBC_ANNEX_B: +- return DVBV3_ATSC; +- case SYS_UNDEFINED: +- case SYS_ISDBC: +- case SYS_DVBH: +- case SYS_DAB: +- case SYS_ATSCMH: +- default: +- /* +- * Doesn't know how to emulate those types and/or +- * there's no frontend driver from this type yet +- * with some emulation code, so, we're not sure yet how +- * to handle them, or they're not compatible with a DVBv3 call. +- */ +- return DVBV3_UNKNOWN; +- } +-} +- +-static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dvb_fe_events *events = &fepriv->events; +- struct dvb_frontend_event *e; +- int wp; +- +- dprintk ("%s\n", __func__); +- +- if ((status & FE_HAS_LOCK) && has_get_frontend(fe)) +- dtv_get_frontend(fe, &fepriv->parameters_out); +- +- mutex_lock(&events->mtx); +- +- wp = (events->eventw + 1) % MAX_EVENT; +- if (wp == events->eventr) { +- events->overflow = 1; +- events->eventr = (events->eventr + 1) % MAX_EVENT; +- } +- +- e = &events->events[events->eventw]; +- e->status = status; +- e->parameters = fepriv->parameters_out; +- +- events->eventw = wp; +- +- mutex_unlock(&events->mtx); +- +- wake_up_interruptible (&events->wait_queue); +-} +- +-static int dvb_frontend_get_event(struct dvb_frontend *fe, +- struct dvb_frontend_event *event, int flags) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dvb_fe_events *events = &fepriv->events; +- +- dprintk ("%s\n", __func__); +- +- if (events->overflow) { +- events->overflow = 0; +- return -EOVERFLOW; +- } +- +- if (events->eventw == events->eventr) { +- int ret; +- +- if (flags & O_NONBLOCK) +- return -EWOULDBLOCK; +- +- up(&fepriv->sem); +- +- ret = wait_event_interruptible (events->wait_queue, +- events->eventw != events->eventr); +- +- if (down_interruptible (&fepriv->sem)) +- return -ERESTARTSYS; +- +- if (ret < 0) +- return ret; +- } +- +- mutex_lock(&events->mtx); +- *event = events->events[events->eventr]; +- events->eventr = (events->eventr + 1) % MAX_EVENT; +- mutex_unlock(&events->mtx); +- +- return 0; +-} +- +-static void dvb_frontend_clear_events(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dvb_fe_events *events = &fepriv->events; +- +- mutex_lock(&events->mtx); +- events->eventr = events->eventw; +- mutex_unlock(&events->mtx); +-} +- +-static void dvb_frontend_init(struct dvb_frontend *fe) +-{ +- dprintk ("DVB: initialising adapter %i frontend %i (%s)...\n", +- fe->dvb->num, +- fe->id, +- fe->ops.info.name); +- +- if (fe->ops.init) +- fe->ops.init(fe); +- if (fe->ops.tuner_ops.init) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.init(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +-} +- +-void dvb_frontend_reinitialise(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- fepriv->reinitialise = 1; +- dvb_frontend_wakeup(fe); +-} +-EXPORT_SYMBOL(dvb_frontend_reinitialise); +- +-static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked) +-{ +- int q2; +- +- dprintk ("%s\n", __func__); +- +- if (locked) +- (fepriv->quality) = (fepriv->quality * 220 + 36*256) / 256; +- else +- (fepriv->quality) = (fepriv->quality * 220 + 0) / 256; +- +- q2 = fepriv->quality - 128; +- q2 *= q2; +- +- fepriv->delay = fepriv->min_delay + q2 * HZ / (128*128); +-} +- +-/** +- * Performs automatic twiddling of frontend parameters. +- * +- * @param fe The frontend concerned. +- * @param check_wrapped Checks if an iteration has completed. DO NOT SET ON THE FIRST ATTEMPT +- * @returns Number of complete iterations that have been performed. +- */ +-static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wrapped) +-{ +- int autoinversion; +- int ready = 0; +- int fe_set_err = 0; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp; +- int original_inversion = c->inversion; +- u32 original_frequency = c->frequency; +- +- /* are we using autoinversion? */ +- autoinversion = ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) && +- (c->inversion == INVERSION_AUTO)); +- +- /* setup parameters correctly */ +- while(!ready) { +- /* calculate the lnb_drift */ +- fepriv->lnb_drift = fepriv->auto_step * fepriv->step_size; +- +- /* wrap the auto_step if we've exceeded the maximum drift */ +- if (fepriv->lnb_drift > fepriv->max_drift) { +- fepriv->auto_step = 0; +- fepriv->auto_sub_step = 0; +- fepriv->lnb_drift = 0; +- } +- +- /* perform inversion and +/- zigzag */ +- switch(fepriv->auto_sub_step) { +- case 0: +- /* try with the current inversion and current drift setting */ +- ready = 1; +- break; +- +- case 1: +- if (!autoinversion) break; +- +- fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF; +- ready = 1; +- break; +- +- case 2: +- if (fepriv->lnb_drift == 0) break; +- +- fepriv->lnb_drift = -fepriv->lnb_drift; +- ready = 1; +- break; +- +- case 3: +- if (fepriv->lnb_drift == 0) break; +- if (!autoinversion) break; +- +- fepriv->inversion = (fepriv->inversion == INVERSION_OFF) ? INVERSION_ON : INVERSION_OFF; +- fepriv->lnb_drift = -fepriv->lnb_drift; +- ready = 1; +- break; +- +- default: +- fepriv->auto_step++; +- fepriv->auto_sub_step = -1; /* it'll be incremented to 0 in a moment */ +- break; +- } +- +- if (!ready) fepriv->auto_sub_step++; +- } +- +- /* if this attempt would hit where we started, indicate a complete +- * iteration has occurred */ +- if ((fepriv->auto_step == fepriv->started_auto_step) && +- (fepriv->auto_sub_step == 0) && check_wrapped) { +- return 1; +- } +- +- dprintk("%s: drift:%i inversion:%i auto_step:%i " +- "auto_sub_step:%i started_auto_step:%i\n", +- __func__, fepriv->lnb_drift, fepriv->inversion, +- fepriv->auto_step, fepriv->auto_sub_step, fepriv->started_auto_step); +- +- /* set the frontend itself */ +- c->frequency += fepriv->lnb_drift; +- if (autoinversion) +- c->inversion = fepriv->inversion; +- tmp = *c; +- if (fe->ops.set_frontend) +- fe_set_err = fe->ops.set_frontend(fe); +- *c = tmp; +- if (fe_set_err < 0) { +- fepriv->state = FESTATE_ERROR; +- return fe_set_err; +- } +- +- c->frequency = original_frequency; +- c->inversion = original_inversion; +- +- fepriv->auto_sub_step++; +- return 0; +-} +- +-static void dvb_frontend_swzigzag(struct dvb_frontend *fe) +-{ +- fe_status_t s = 0; +- int retval = 0; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache, tmp; +- +- /* if we've got no parameters, just keep idling */ +- if (fepriv->state & FESTATE_IDLE) { +- fepriv->delay = 3*HZ; +- fepriv->quality = 0; +- return; +- } +- +- /* in SCAN mode, we just set the frontend when asked and leave it alone */ +- if (fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT) { +- if (fepriv->state & FESTATE_RETUNE) { +- tmp = *c; +- if (fe->ops.set_frontend) +- retval = fe->ops.set_frontend(fe); +- *c = tmp; +- if (retval < 0) +- fepriv->state = FESTATE_ERROR; +- else +- fepriv->state = FESTATE_TUNED; +- } +- fepriv->delay = 3*HZ; +- fepriv->quality = 0; +- return; +- } +- +- /* get the frontend status */ +- if (fepriv->state & FESTATE_RETUNE) { +- s = 0; +- } else { +- if (fe->ops.read_status) +- fe->ops.read_status(fe, &s); +- if (s != fepriv->status) { +- dvb_frontend_add_event(fe, s); +- fepriv->status = s; +- } +- } +- +- /* if we're not tuned, and we have a lock, move to the TUNED state */ +- if ((fepriv->state & FESTATE_WAITFORLOCK) && (s & FE_HAS_LOCK)) { +- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); +- fepriv->state = FESTATE_TUNED; +- +- /* if we're tuned, then we have determined the correct inversion */ +- if ((!(fe->ops.info.caps & FE_CAN_INVERSION_AUTO)) && +- (c->inversion == INVERSION_AUTO)) { +- c->inversion = fepriv->inversion; +- } +- return; +- } +- +- /* if we are tuned already, check we're still locked */ +- if (fepriv->state & FESTATE_TUNED) { +- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); +- +- /* we're tuned, and the lock is still good... */ +- if (s & FE_HAS_LOCK) { +- return; +- } else { /* if we _WERE_ tuned, but now don't have a lock */ +- fepriv->state = FESTATE_ZIGZAG_FAST; +- fepriv->started_auto_step = fepriv->auto_step; +- fepriv->check_wrapped = 0; +- } +- } +- +- /* don't actually do anything if we're in the LOSTLOCK state, +- * the frontend is set to FE_CAN_RECOVER, and the max_drift is 0 */ +- if ((fepriv->state & FESTATE_LOSTLOCK) && +- (fe->ops.info.caps & FE_CAN_RECOVER) && (fepriv->max_drift == 0)) { +- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); +- return; +- } +- +- /* don't do anything if we're in the DISEQC state, since this +- * might be someone with a motorized dish controlled by DISEQC. +- * If its actually a re-tune, there will be a SET_FRONTEND soon enough. */ +- if (fepriv->state & FESTATE_DISEQC) { +- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); +- return; +- } +- +- /* if we're in the RETUNE state, set everything up for a brand +- * new scan, keeping the current inversion setting, as the next +- * tune is _very_ likely to require the same */ +- if (fepriv->state & FESTATE_RETUNE) { +- fepriv->lnb_drift = 0; +- fepriv->auto_step = 0; +- fepriv->auto_sub_step = 0; +- fepriv->started_auto_step = 0; +- fepriv->check_wrapped = 0; +- } +- +- /* fast zigzag. */ +- if ((fepriv->state & FESTATE_SEARCHING_FAST) || (fepriv->state & FESTATE_RETUNE)) { +- fepriv->delay = fepriv->min_delay; +- +- /* perform a tune */ +- retval = dvb_frontend_swzigzag_autotune(fe, +- fepriv->check_wrapped); +- if (retval < 0) { +- return; +- } else if (retval) { +- /* OK, if we've run out of trials at the fast speed. +- * Drop back to slow for the _next_ attempt */ +- fepriv->state = FESTATE_SEARCHING_SLOW; +- fepriv->started_auto_step = fepriv->auto_step; +- return; +- } +- fepriv->check_wrapped = 1; +- +- /* if we've just retuned, enter the ZIGZAG_FAST state. +- * This ensures we cannot return from an +- * FE_SET_FRONTEND ioctl before the first frontend tune +- * occurs */ +- if (fepriv->state & FESTATE_RETUNE) { +- fepriv->state = FESTATE_TUNING_FAST; +- } +- } +- +- /* slow zigzag */ +- if (fepriv->state & FESTATE_SEARCHING_SLOW) { +- dvb_frontend_swzigzag_update_delay(fepriv, s & FE_HAS_LOCK); +- +- /* Note: don't bother checking for wrapping; we stay in this +- * state until we get a lock */ +- dvb_frontend_swzigzag_autotune(fe, 0); +- } +-} +- +-static int dvb_frontend_is_exiting(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- if (fepriv->exit != DVB_FE_NO_EXIT) +- return 1; +- +- if (fepriv->dvbdev->writers == 1) +- if (time_after_eq(jiffies, fepriv->release_jiffies + +- dvb_shutdown_timeout * HZ)) +- return 1; +- +- return 0; +-} +- +-static int dvb_frontend_should_wakeup(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- if (fepriv->wakeup) { +- fepriv->wakeup = 0; +- return 1; +- } +- return dvb_frontend_is_exiting(fe); +-} +- +-static void dvb_frontend_wakeup(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- fepriv->wakeup = 1; +- wake_up_interruptible(&fepriv->wait_queue); +-} +- +-static int dvb_frontend_thread(void *data) +-{ +- struct dvb_frontend *fe = data; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- fe_status_t s; +- enum dvbfe_algo algo; +- +- bool re_tune = false; +- +- dprintk("%s\n", __func__); +- +- fepriv->check_wrapped = 0; +- fepriv->quality = 0; +- fepriv->delay = 3*HZ; +- fepriv->status = 0; +- fepriv->wakeup = 0; +- fepriv->reinitialise = 0; +- +- dvb_frontend_init(fe); +- +- set_freezable(); +- while (1) { +- up(&fepriv->sem); /* is locked when we enter the thread... */ +-restart: +- wait_event_interruptible_timeout(fepriv->wait_queue, +- dvb_frontend_should_wakeup(fe) || kthread_should_stop() +- || freezing(current), +- fepriv->delay); +- +- if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { +- /* got signal or quitting */ +- fepriv->exit = DVB_FE_NORMAL_EXIT; +- break; +- } +- +- if (try_to_freeze()) +- goto restart; +- +- if (down_interruptible(&fepriv->sem)) +- break; +- +- if (fepriv->reinitialise) { +- dvb_frontend_init(fe); +- if (fe->ops.set_tone && fepriv->tone != -1) +- fe->ops.set_tone(fe, fepriv->tone); +- if (fe->ops.set_voltage && fepriv->voltage != -1) +- fe->ops.set_voltage(fe, fepriv->voltage); +- fepriv->reinitialise = 0; +- } +- +- /* do an iteration of the tuning loop */ +- if (fe->ops.get_frontend_algo) { +- algo = fe->ops.get_frontend_algo(fe); +- switch (algo) { +- case DVBFE_ALGO_HW: +- dprintk("%s: Frontend ALGO = DVBFE_ALGO_HW\n", __func__); +- +- if (fepriv->state & FESTATE_RETUNE) { +- dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__); +- re_tune = true; +- fepriv->state = FESTATE_TUNED; +- } else { +- re_tune = false; +- } +- +- if (fe->ops.tune) +- fe->ops.tune(fe, re_tune, fepriv->tune_mode_flags, &fepriv->delay, &s); +- +- if (s != fepriv->status && !(fepriv->tune_mode_flags & FE_TUNE_MODE_ONESHOT)) { +- dprintk("%s: state changed, adding current state\n", __func__); +- dvb_frontend_add_event(fe, s); +- fepriv->status = s; +- } +- break; +- case DVBFE_ALGO_SW: +- dprintk("%s: Frontend ALGO = DVBFE_ALGO_SW\n", __func__); +- dvb_frontend_swzigzag(fe); +- break; +- case DVBFE_ALGO_CUSTOM: +- dprintk("%s: Frontend ALGO = DVBFE_ALGO_CUSTOM, state=%d\n", __func__, fepriv->state); +- if (fepriv->state & FESTATE_RETUNE) { +- dprintk("%s: Retune requested, FESTAT_RETUNE\n", __func__); +- fepriv->state = FESTATE_TUNED; +- } +- /* Case where we are going to search for a carrier +- * User asked us to retune again for some reason, possibly +- * requesting a search with a new set of parameters +- */ +- if (fepriv->algo_status & DVBFE_ALGO_SEARCH_AGAIN) { +- if (fe->ops.search) { +- fepriv->algo_status = fe->ops.search(fe); +- /* We did do a search as was requested, the flags are +- * now unset as well and has the flags wrt to search. +- */ +- } else { +- fepriv->algo_status &= ~DVBFE_ALGO_SEARCH_AGAIN; +- } +- } +- /* Track the carrier if the search was successful */ +- if (fepriv->algo_status != DVBFE_ALGO_SEARCH_SUCCESS) { +- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; +- fepriv->delay = HZ / 2; +- } +- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); +- fe->ops.read_status(fe, &s); +- if (s != fepriv->status) { +- dvb_frontend_add_event(fe, s); /* update event list */ +- fepriv->status = s; +- if (!(s & FE_HAS_LOCK)) { +- fepriv->delay = HZ / 10; +- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; +- } else { +- fepriv->delay = 60 * HZ; +- } +- } +- break; +- default: +- dprintk("%s: UNDEFINED ALGO !\n", __func__); +- break; +- } +- } else { +- dvb_frontend_swzigzag(fe); +- } +- } +- +- if (dvb_powerdown_on_sleep) { +- if (fe->ops.set_voltage) +- fe->ops.set_voltage(fe, SEC_VOLTAGE_OFF); +- if (fe->ops.tuner_ops.sleep) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.sleep(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (fe->ops.sleep) +- fe->ops.sleep(fe); +- } +- +- fepriv->thread = NULL; +- if (kthread_should_stop()) +- fepriv->exit = DVB_FE_DEVICE_REMOVED; +- else +- fepriv->exit = DVB_FE_NO_EXIT; +- mb(); +- +- dvb_frontend_wakeup(fe); +- return 0; +-} +- +-static void dvb_frontend_stop(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- dprintk ("%s\n", __func__); +- +- fepriv->exit = DVB_FE_NORMAL_EXIT; +- mb(); +- +- if (!fepriv->thread) +- return; +- +- kthread_stop(fepriv->thread); +- +- sema_init(&fepriv->sem, 1); +- fepriv->state = FESTATE_IDLE; +- +- /* paranoia check in case a signal arrived */ +- if (fepriv->thread) +- printk("dvb_frontend_stop: warning: thread %p won't exit\n", +- fepriv->thread); +-} +- +-s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime) +-{ +- return ((curtime.tv_usec < lasttime.tv_usec) ? +- 1000000 - lasttime.tv_usec + curtime.tv_usec : +- curtime.tv_usec - lasttime.tv_usec); +-} +-EXPORT_SYMBOL(timeval_usec_diff); +- +-static inline void timeval_usec_add(struct timeval *curtime, u32 add_usec) +-{ +- curtime->tv_usec += add_usec; +- if (curtime->tv_usec >= 1000000) { +- curtime->tv_usec -= 1000000; +- curtime->tv_sec++; +- } +-} +- +-/* +- * Sleep until gettimeofday() > waketime + add_usec +- * This needs to be as precise as possible, but as the delay is +- * usually between 2ms and 32ms, it is done using a scheduled msleep +- * followed by usleep (normally a busy-wait loop) for the remainder +- */ +-void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec) +-{ +- struct timeval lasttime; +- s32 delta, newdelta; +- +- timeval_usec_add(waketime, add_usec); +- +- do_gettimeofday(&lasttime); +- delta = timeval_usec_diff(lasttime, *waketime); +- if (delta > 2500) { +- msleep((delta - 1500) / 1000); +- do_gettimeofday(&lasttime); +- newdelta = timeval_usec_diff(lasttime, *waketime); +- delta = (newdelta > delta) ? 0 : newdelta; +- } +- if (delta > 0) +- udelay(delta); +-} +-EXPORT_SYMBOL(dvb_frontend_sleep_until); +- +-static int dvb_frontend_start(struct dvb_frontend *fe) +-{ +- int ret; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct task_struct *fe_thread; +- +- dprintk ("%s\n", __func__); +- +- if (fepriv->thread) { +- if (fepriv->exit == DVB_FE_NO_EXIT) +- return 0; +- else +- dvb_frontend_stop (fe); +- } +- +- if (signal_pending(current)) +- return -EINTR; +- if (down_interruptible (&fepriv->sem)) +- return -EINTR; +- +- fepriv->state = FESTATE_IDLE; +- fepriv->exit = DVB_FE_NO_EXIT; +- fepriv->thread = NULL; +- mb(); +- +- fe_thread = kthread_run(dvb_frontend_thread, fe, +- "kdvb-ad-%i-fe-%i", fe->dvb->num,fe->id); +- if (IS_ERR(fe_thread)) { +- ret = PTR_ERR(fe_thread); +- printk("dvb_frontend_start: failed to start kthread (%d)\n", ret); +- up(&fepriv->sem); +- return ret; +- } +- fepriv->thread = fe_thread; +- return 0; +-} +- +-static void dvb_frontend_get_frequency_limits(struct dvb_frontend *fe, +- u32 *freq_min, u32 *freq_max) +-{ +- *freq_min = max(fe->ops.info.frequency_min, fe->ops.tuner_ops.info.frequency_min); +- +- if (fe->ops.info.frequency_max == 0) +- *freq_max = fe->ops.tuner_ops.info.frequency_max; +- else if (fe->ops.tuner_ops.info.frequency_max == 0) +- *freq_max = fe->ops.info.frequency_max; +- else +- *freq_max = min(fe->ops.info.frequency_max, fe->ops.tuner_ops.info.frequency_max); +- +- if (*freq_min == 0 || *freq_max == 0) +- printk(KERN_WARNING "DVB: adapter %i frontend %u frequency limits undefined - fix the driver\n", +- fe->dvb->num,fe->id); +-} +- +-static int dvb_frontend_check_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 freq_min; +- u32 freq_max; +- +- /* range check: frequency */ +- dvb_frontend_get_frequency_limits(fe, &freq_min, &freq_max); +- if ((freq_min && c->frequency < freq_min) || +- (freq_max && c->frequency > freq_max)) { +- printk(KERN_WARNING "DVB: adapter %i frontend %i frequency %u out of range (%u..%u)\n", +- fe->dvb->num, fe->id, c->frequency, freq_min, freq_max); +- return -EINVAL; +- } +- +- /* range check: symbol rate */ +- switch (c->delivery_system) { +- case SYS_DVBS: +- case SYS_DVBS2: +- case SYS_TURBO: +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- if ((fe->ops.info.symbol_rate_min && +- c->symbol_rate < fe->ops.info.symbol_rate_min) || +- (fe->ops.info.symbol_rate_max && +- c->symbol_rate > fe->ops.info.symbol_rate_max)) { +- printk(KERN_WARNING "DVB: adapter %i frontend %i symbol rate %u out of range (%u..%u)\n", +- fe->dvb->num, fe->id, c->symbol_rate, +- fe->ops.info.symbol_rate_min, +- fe->ops.info.symbol_rate_max); +- return -EINVAL; +- } +- default: +- break; +- } +- +- return 0; +-} +- +-static int dvb_frontend_clear_cache(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int i; +- u32 delsys; +- +- delsys = c->delivery_system; +- memset(c, 0, sizeof(struct dtv_frontend_properties)); +- c->delivery_system = delsys; +- +- c->state = DTV_CLEAR; +- +- dprintk("%s() Clearing cache for delivery system %d\n", __func__, +- c->delivery_system); +- +- c->transmission_mode = TRANSMISSION_MODE_AUTO; +- c->bandwidth_hz = 0; /* AUTO */ +- c->guard_interval = GUARD_INTERVAL_AUTO; +- c->hierarchy = HIERARCHY_AUTO; +- c->symbol_rate = 0; +- c->code_rate_HP = FEC_AUTO; +- c->code_rate_LP = FEC_AUTO; +- c->fec_inner = FEC_AUTO; +- c->rolloff = ROLLOFF_AUTO; +- c->voltage = SEC_VOLTAGE_OFF; +- c->sectone = SEC_TONE_OFF; +- c->pilot = PILOT_AUTO; +- +- c->isdbt_partial_reception = 0; +- c->isdbt_sb_mode = 0; +- c->isdbt_sb_subchannel = 0; +- c->isdbt_sb_segment_idx = 0; +- c->isdbt_sb_segment_count = 0; +- c->isdbt_layer_enabled = 0; +- for (i = 0; i < 3; i++) { +- c->layer[i].fec = FEC_AUTO; +- c->layer[i].modulation = QAM_AUTO; +- c->layer[i].interleaving = 0; +- c->layer[i].segment_count = 0; +- } +- +- c->isdbs_ts_id = 0; +- c->dvbt2_plp_id = 0; +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- case SYS_DVBS2: +- case SYS_TURBO: +- c->modulation = QPSK; /* implied for DVB-S in legacy API */ +- c->rolloff = ROLLOFF_35;/* implied for DVB-S */ +- break; +- case SYS_ATSC: +- c->modulation = VSB_8; +- break; +- default: +- c->modulation = QAM_AUTO; +- break; +- } +- +- return 0; +-} +- +-#define _DTV_CMD(n, s, b) \ +-[n] = { \ +- .name = #n, \ +- .cmd = n, \ +- .set = s,\ +- .buffer = b \ +-} +- +-static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { +- _DTV_CMD(DTV_TUNE, 1, 0), +- _DTV_CMD(DTV_CLEAR, 1, 0), +- +- /* Set */ +- _DTV_CMD(DTV_FREQUENCY, 1, 0), +- _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0), +- _DTV_CMD(DTV_MODULATION, 1, 0), +- _DTV_CMD(DTV_INVERSION, 1, 0), +- _DTV_CMD(DTV_DISEQC_MASTER, 1, 1), +- _DTV_CMD(DTV_SYMBOL_RATE, 1, 0), +- _DTV_CMD(DTV_INNER_FEC, 1, 0), +- _DTV_CMD(DTV_VOLTAGE, 1, 0), +- _DTV_CMD(DTV_TONE, 1, 0), +- _DTV_CMD(DTV_PILOT, 1, 0), +- _DTV_CMD(DTV_ROLLOFF, 1, 0), +- _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0), +- _DTV_CMD(DTV_HIERARCHY, 1, 0), +- _DTV_CMD(DTV_CODE_RATE_HP, 1, 0), +- _DTV_CMD(DTV_CODE_RATE_LP, 1, 0), +- _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0), +- _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0), +- +- _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), +- _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), +- _DTV_CMD(DTV_ISDBT_SB_SUBCHANNEL_ID, 1, 0), +- _DTV_CMD(DTV_ISDBT_SB_SEGMENT_IDX, 1, 0), +- _DTV_CMD(DTV_ISDBT_SB_SEGMENT_COUNT, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYER_ENABLED, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERA_FEC, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERA_MODULATION, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERA_SEGMENT_COUNT, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERA_TIME_INTERLEAVING, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERB_FEC, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERB_MODULATION, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERB_SEGMENT_COUNT, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERB_TIME_INTERLEAVING, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERC_FEC, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERC_MODULATION, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERC_SEGMENT_COUNT, 1, 0), +- _DTV_CMD(DTV_ISDBT_LAYERC_TIME_INTERLEAVING, 1, 0), +- +- _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), +- _DTV_CMD(DTV_DVBT2_PLP_ID, 1, 0), +- +- /* Get */ +- _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1), +- _DTV_CMD(DTV_API_VERSION, 0, 0), +- _DTV_CMD(DTV_CODE_RATE_HP, 0, 0), +- _DTV_CMD(DTV_CODE_RATE_LP, 0, 0), +- _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0), +- _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0), +- _DTV_CMD(DTV_HIERARCHY, 0, 0), +- +- _DTV_CMD(DTV_ENUM_DELSYS, 0, 0), +-}; +- +-static void dtv_property_dump(struct dtv_property *tvp) +-{ +- int i; +- +- if (tvp->cmd <= 0 || tvp->cmd > DTV_MAX_COMMAND) { +- printk(KERN_WARNING "%s: tvp.cmd = 0x%08x undefined\n", +- __func__, tvp->cmd); +- return; +- } +- +- dprintk("%s() tvp.cmd = 0x%08x (%s)\n" +- ,__func__ +- ,tvp->cmd +- ,dtv_cmds[ tvp->cmd ].name); +- +- if(dtv_cmds[ tvp->cmd ].buffer) { +- +- dprintk("%s() tvp.u.buffer.len = 0x%02x\n" +- ,__func__ +- ,tvp->u.buffer.len); +- +- for(i = 0; i < tvp->u.buffer.len; i++) +- dprintk("%s() tvp.u.buffer.data[0x%02x] = 0x%02x\n" +- ,__func__ +- ,i +- ,tvp->u.buffer.data[i]); +- +- } else +- dprintk("%s() tvp.u.data = 0x%08x\n", __func__, tvp->u.data); +-} +- +-/* Synchronise the legacy tuning parameters into the cache, so that demodulator +- * drivers can use a single set_frontend tuning function, regardless of whether +- * it's being used for the legacy or new API, reducing code and complexity. +- */ +-static int dtv_property_cache_sync(struct dvb_frontend *fe, +- struct dtv_frontend_properties *c, +- const struct dvb_frontend_parameters *p) +-{ +- c->frequency = p->frequency; +- c->inversion = p->inversion; +- +- switch (dvbv3_type(c->delivery_system)) { +- case DVBV3_QPSK: +- dprintk("%s() Preparing QPSK req\n", __func__); +- c->symbol_rate = p->u.qpsk.symbol_rate; +- c->fec_inner = p->u.qpsk.fec_inner; +- break; +- case DVBV3_QAM: +- dprintk("%s() Preparing QAM req\n", __func__); +- c->symbol_rate = p->u.qam.symbol_rate; +- c->fec_inner = p->u.qam.fec_inner; +- c->modulation = p->u.qam.modulation; +- break; +- case DVBV3_OFDM: +- dprintk("%s() Preparing OFDM req\n", __func__); +- switch (p->u.ofdm.bandwidth) { +- case BANDWIDTH_10_MHZ: +- c->bandwidth_hz = 10000000; +- break; +- case BANDWIDTH_8_MHZ: +- c->bandwidth_hz = 8000000; +- break; +- case BANDWIDTH_7_MHZ: +- c->bandwidth_hz = 7000000; +- break; +- case BANDWIDTH_6_MHZ: +- c->bandwidth_hz = 6000000; +- break; +- case BANDWIDTH_5_MHZ: +- c->bandwidth_hz = 5000000; +- break; +- case BANDWIDTH_1_712_MHZ: +- c->bandwidth_hz = 1712000; +- break; +- case BANDWIDTH_AUTO: +- c->bandwidth_hz = 0; +- } +- +- c->code_rate_HP = p->u.ofdm.code_rate_HP; +- c->code_rate_LP = p->u.ofdm.code_rate_LP; +- c->modulation = p->u.ofdm.constellation; +- c->transmission_mode = p->u.ofdm.transmission_mode; +- c->guard_interval = p->u.ofdm.guard_interval; +- c->hierarchy = p->u.ofdm.hierarchy_information; +- break; +- case DVBV3_ATSC: +- dprintk("%s() Preparing ATSC req\n", __func__); +- c->modulation = p->u.vsb.modulation; +- if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) +- c->delivery_system = SYS_ATSC; +- else +- c->delivery_system = SYS_DVBC_ANNEX_B; +- break; +- case DVBV3_UNKNOWN: +- printk(KERN_ERR +- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", +- __func__, c->delivery_system); +- return -EINVAL; +- } +- +- return 0; +-} +- +-/* Ensure the cached values are set correctly in the frontend +- * legacy tuning structures, for the advanced tuning API. +- */ +-static int dtv_property_legacy_params_sync(struct dvb_frontend *fe, +- struct dvb_frontend_parameters *p) +-{ +- const struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- p->frequency = c->frequency; +- p->inversion = c->inversion; +- +- switch (dvbv3_type(c->delivery_system)) { +- case DVBV3_UNKNOWN: +- printk(KERN_ERR +- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", +- __func__, c->delivery_system); +- return -EINVAL; +- case DVBV3_QPSK: +- dprintk("%s() Preparing QPSK req\n", __func__); +- p->u.qpsk.symbol_rate = c->symbol_rate; +- p->u.qpsk.fec_inner = c->fec_inner; +- break; +- case DVBV3_QAM: +- dprintk("%s() Preparing QAM req\n", __func__); +- p->u.qam.symbol_rate = c->symbol_rate; +- p->u.qam.fec_inner = c->fec_inner; +- p->u.qam.modulation = c->modulation; +- break; +- case DVBV3_OFDM: +- dprintk("%s() Preparing OFDM req\n", __func__); +- +- switch (c->bandwidth_hz) { +- case 10000000: +- p->u.ofdm.bandwidth = BANDWIDTH_10_MHZ; +- break; +- case 8000000: +- p->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; +- break; +- case 7000000: +- p->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; +- break; +- case 6000000: +- p->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; +- break; +- case 5000000: +- p->u.ofdm.bandwidth = BANDWIDTH_5_MHZ; +- break; +- case 1712000: +- p->u.ofdm.bandwidth = BANDWIDTH_1_712_MHZ; +- break; +- case 0: +- default: +- p->u.ofdm.bandwidth = BANDWIDTH_AUTO; +- } +- p->u.ofdm.code_rate_HP = c->code_rate_HP; +- p->u.ofdm.code_rate_LP = c->code_rate_LP; +- p->u.ofdm.constellation = c->modulation; +- p->u.ofdm.transmission_mode = c->transmission_mode; +- p->u.ofdm.guard_interval = c->guard_interval; +- p->u.ofdm.hierarchy_information = c->hierarchy; +- break; +- case DVBV3_ATSC: +- dprintk("%s() Preparing VSB req\n", __func__); +- p->u.vsb.modulation = c->modulation; +- break; +- } +- return 0; +-} +- +-/** +- * dtv_get_frontend - calls a callback for retrieving DTV parameters +- * @fe: struct dvb_frontend pointer +- * @c: struct dtv_frontend_properties pointer (DVBv5 cache) +- * @p_out struct dvb_frontend_parameters pointer (DVBv3 FE struct) +- * +- * This routine calls either the DVBv3 or DVBv5 get_frontend call. +- * If c is not null, it will update the DVBv5 cache struct pointed by it. +- * If p_out is not null, it will update the DVBv3 params pointed by it. +- */ +-static int dtv_get_frontend(struct dvb_frontend *fe, +- struct dvb_frontend_parameters *p_out) +-{ +- int r; +- +- if (fe->ops.get_frontend) { +- r = fe->ops.get_frontend(fe); +- if (unlikely(r < 0)) +- return r; +- if (p_out) +- dtv_property_legacy_params_sync(fe, p_out); +- return 0; +- } +- +- /* As everything is in cache, get_frontend fops are always supported */ +- return 0; +-} +- +-static int dvb_frontend_ioctl_legacy(struct file *file, +- unsigned int cmd, void *parg); +-static int dvb_frontend_ioctl_properties(struct file *file, +- unsigned int cmd, void *parg); +- +-static int dtv_property_process_get(struct dvb_frontend *fe, +- const struct dtv_frontend_properties *c, +- struct dtv_property *tvp, +- struct file *file) +-{ +- int r, ncaps; +- +- switch(tvp->cmd) { +- case DTV_ENUM_DELSYS: +- ncaps = 0; +- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { +- tvp->u.buffer.data[ncaps] = fe->ops.delsys[ncaps]; +- ncaps++; +- } +- tvp->u.buffer.len = ncaps; +- break; +- case DTV_FREQUENCY: +- tvp->u.data = c->frequency; +- break; +- case DTV_MODULATION: +- tvp->u.data = c->modulation; +- break; +- case DTV_BANDWIDTH_HZ: +- tvp->u.data = c->bandwidth_hz; +- break; +- case DTV_INVERSION: +- tvp->u.data = c->inversion; +- break; +- case DTV_SYMBOL_RATE: +- tvp->u.data = c->symbol_rate; +- break; +- case DTV_INNER_FEC: +- tvp->u.data = c->fec_inner; +- break; +- case DTV_PILOT: +- tvp->u.data = c->pilot; +- break; +- case DTV_ROLLOFF: +- tvp->u.data = c->rolloff; +- break; +- case DTV_DELIVERY_SYSTEM: +- tvp->u.data = c->delivery_system; +- break; +- case DTV_VOLTAGE: +- tvp->u.data = c->voltage; +- break; +- case DTV_TONE: +- tvp->u.data = c->sectone; +- break; +- case DTV_API_VERSION: +- tvp->u.data = (DVB_API_VERSION << 8) | DVB_API_VERSION_MINOR; +- break; +- case DTV_CODE_RATE_HP: +- tvp->u.data = c->code_rate_HP; +- break; +- case DTV_CODE_RATE_LP: +- tvp->u.data = c->code_rate_LP; +- break; +- case DTV_GUARD_INTERVAL: +- tvp->u.data = c->guard_interval; +- break; +- case DTV_TRANSMISSION_MODE: +- tvp->u.data = c->transmission_mode; +- break; +- case DTV_HIERARCHY: +- tvp->u.data = c->hierarchy; +- break; +- +- /* ISDB-T Support here */ +- case DTV_ISDBT_PARTIAL_RECEPTION: +- tvp->u.data = c->isdbt_partial_reception; +- break; +- case DTV_ISDBT_SOUND_BROADCASTING: +- tvp->u.data = c->isdbt_sb_mode; +- break; +- case DTV_ISDBT_SB_SUBCHANNEL_ID: +- tvp->u.data = c->isdbt_sb_subchannel; +- break; +- case DTV_ISDBT_SB_SEGMENT_IDX: +- tvp->u.data = c->isdbt_sb_segment_idx; +- break; +- case DTV_ISDBT_SB_SEGMENT_COUNT: +- tvp->u.data = c->isdbt_sb_segment_count; +- break; +- case DTV_ISDBT_LAYER_ENABLED: +- tvp->u.data = c->isdbt_layer_enabled; +- break; +- case DTV_ISDBT_LAYERA_FEC: +- tvp->u.data = c->layer[0].fec; +- break; +- case DTV_ISDBT_LAYERA_MODULATION: +- tvp->u.data = c->layer[0].modulation; +- break; +- case DTV_ISDBT_LAYERA_SEGMENT_COUNT: +- tvp->u.data = c->layer[0].segment_count; +- break; +- case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: +- tvp->u.data = c->layer[0].interleaving; +- break; +- case DTV_ISDBT_LAYERB_FEC: +- tvp->u.data = c->layer[1].fec; +- break; +- case DTV_ISDBT_LAYERB_MODULATION: +- tvp->u.data = c->layer[1].modulation; +- break; +- case DTV_ISDBT_LAYERB_SEGMENT_COUNT: +- tvp->u.data = c->layer[1].segment_count; +- break; +- case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: +- tvp->u.data = c->layer[1].interleaving; +- break; +- case DTV_ISDBT_LAYERC_FEC: +- tvp->u.data = c->layer[2].fec; +- break; +- case DTV_ISDBT_LAYERC_MODULATION: +- tvp->u.data = c->layer[2].modulation; +- break; +- case DTV_ISDBT_LAYERC_SEGMENT_COUNT: +- tvp->u.data = c->layer[2].segment_count; +- break; +- case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: +- tvp->u.data = c->layer[2].interleaving; +- break; +- case DTV_ISDBS_TS_ID: +- tvp->u.data = c->isdbs_ts_id; +- break; +- case DTV_DVBT2_PLP_ID: +- tvp->u.data = c->dvbt2_plp_id; +- break; +- default: +- return -EINVAL; +- } +- +- /* Allow the frontend to override outgoing properties */ +- if (fe->ops.get_property) { +- r = fe->ops.get_property(fe, tvp); +- if (r < 0) +- return r; +- } +- +- dtv_property_dump(tvp); +- +- return 0; +-} +- +-static int dtv_set_frontend(struct dvb_frontend *fe); +- +-static bool is_dvbv3_delsys(u32 delsys) +-{ +- bool status; +- +- status = (delsys == SYS_DVBT) || (delsys == SYS_DVBC_ANNEX_A) || +- (delsys == SYS_DVBS) || (delsys == SYS_ATSC); +- +- return status; +-} +- +-static int set_delivery_system(struct dvb_frontend *fe, u32 desired_system) +-{ +- int ncaps, i; +- u32 delsys = SYS_UNDEFINED; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- enum dvbv3_emulation_type type; +- +- /* +- * It was reported that some old DVBv5 applications were +- * filling delivery_system with SYS_UNDEFINED. If this happens, +- * assume that the application wants to use the first supported +- * delivery system. +- */ +- if (c->delivery_system == SYS_UNDEFINED) +- c->delivery_system = fe->ops.delsys[0]; +- +- if (desired_system == SYS_UNDEFINED) { +- /* +- * A DVBv3 call doesn't know what's the desired system. +- * Also, DVBv3 applications don't know that ops.info->type +- * could be changed, and they simply dies when it doesn't +- * match. +- * So, don't change the current delivery system, as it +- * may be trying to do the wrong thing, like setting an +- * ISDB-T frontend as DVB-T. Instead, find the closest +- * DVBv3 system that matches the delivery system. +- */ +- if (is_dvbv3_delsys(c->delivery_system)) { +- dprintk("%s() Using delivery system to %d\n", +- __func__, c->delivery_system); +- return 0; +- } +- type = dvbv3_type(c->delivery_system); +- switch (type) { +- case DVBV3_QPSK: +- desired_system = SYS_DVBS; +- break; +- case DVBV3_QAM: +- desired_system = SYS_DVBC_ANNEX_A; +- break; +- case DVBV3_ATSC: +- desired_system = SYS_ATSC; +- break; +- case DVBV3_OFDM: +- desired_system = SYS_DVBT; +- break; +- default: +- dprintk("%s(): This frontend doesn't support DVBv3 calls\n", +- __func__); +- return -EINVAL; +- } +- /* +- * Get a delivery system that is compatible with DVBv3 +- * NOTE: in order for this to work with softwares like Kaffeine that +- * uses a DVBv5 call for DVB-S2 and a DVBv3 call to go back to +- * DVB-S, drivers that support both should put the SYS_DVBS entry +- * before the SYS_DVBS2, otherwise it won't switch back to DVB-S. +- * The real fix is that userspace applications should not use DVBv3 +- * and not trust on calling FE_SET_FRONTEND to switch the delivery +- * system. +- */ +- ncaps = 0; +- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { +- if (fe->ops.delsys[ncaps] == desired_system) { +- delsys = desired_system; +- break; +- } +- ncaps++; +- } +- if (delsys == SYS_UNDEFINED) { +- dprintk("%s() Couldn't find a delivery system that matches %d\n", +- __func__, desired_system); +- } +- } else { +- /* +- * This is a DVBv5 call. So, it likely knows the supported +- * delivery systems. +- */ +- +- /* Check if the desired delivery system is supported */ +- ncaps = 0; +- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { +- if (fe->ops.delsys[ncaps] == desired_system) { +- c->delivery_system = desired_system; +- dprintk("%s() Changing delivery system to %d\n", +- __func__, desired_system); +- return 0; +- } +- ncaps++; +- } +- type = dvbv3_type(desired_system); +- +- /* +- * The delivery system is not supported. See if it can be +- * emulated. +- * The emulation only works if the desired system is one of the +- * DVBv3 delivery systems +- */ +- if (!is_dvbv3_delsys(desired_system)) { +- dprintk("%s() can't use a DVBv3 FE_SET_FRONTEND call on this frontend\n", +- __func__); +- return -EINVAL; +- } +- +- /* +- * Get the last non-DVBv3 delivery system that has the same type +- * of the desired system +- */ +- ncaps = 0; +- while (fe->ops.delsys[ncaps] && ncaps < MAX_DELSYS) { +- if ((dvbv3_type(fe->ops.delsys[ncaps]) == type) && +- !is_dvbv3_delsys(fe->ops.delsys[ncaps])) +- delsys = fe->ops.delsys[ncaps]; +- ncaps++; +- } +- /* There's nothing compatible with the desired delivery system */ +- if (delsys == SYS_UNDEFINED) { +- dprintk("%s() Incompatible DVBv3 FE_SET_FRONTEND call for this frontend\n", +- __func__); +- return -EINVAL; +- } +- } +- +- c->delivery_system = delsys; +- +- /* +- * The DVBv3 or DVBv5 call is requesting a different system. So, +- * emulation is needed. +- * +- * Emulate newer delivery systems like ISDBT, DVBT and DMBTH +- * for older DVBv5 applications. The emulation will try to use +- * the auto mode for most things, and will assume that the desired +- * delivery system is the last one at the ops.delsys[] array +- */ +- dprintk("%s() Using delivery system %d emulated as if it were a %d\n", +- __func__, delsys, desired_system); +- +- /* +- * For now, handles ISDB-T calls. More code may be needed here for the +- * other emulated stuff +- */ +- if (type == DVBV3_OFDM) { +- if (c->delivery_system == SYS_ISDBT) { +- dprintk("%s() Using defaults for SYS_ISDBT\n", +- __func__); +- if (!c->bandwidth_hz) +- c->bandwidth_hz = 6000000; +- +- c->isdbt_partial_reception = 0; +- c->isdbt_sb_mode = 0; +- c->isdbt_sb_subchannel = 0; +- c->isdbt_sb_segment_idx = 0; +- c->isdbt_sb_segment_count = 0; +- c->isdbt_layer_enabled = 0; +- for (i = 0; i < 3; i++) { +- c->layer[i].fec = FEC_AUTO; +- c->layer[i].modulation = QAM_AUTO; +- c->layer[i].interleaving = 0; +- c->layer[i].segment_count = 0; +- } +- } +- } +- dprintk("change delivery system on cache to %d\n", c->delivery_system); +- +- return 0; +-} +- +-static int dtv_property_process_set(struct dvb_frontend *fe, +- struct dtv_property *tvp, +- struct file *file) +-{ +- int r = 0; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- /* Allow the frontend to validate incoming properties */ +- if (fe->ops.set_property) { +- r = fe->ops.set_property(fe, tvp); +- if (r < 0) +- return r; +- } +- +- switch(tvp->cmd) { +- case DTV_CLEAR: +- /* +- * Reset a cache of data specific to the frontend here. This does +- * not effect hardware. +- */ +- dvb_frontend_clear_cache(fe); +- break; +- case DTV_TUNE: +- /* interpret the cache of data, build either a traditional frontend +- * tunerequest so we can pass validation in the FE_SET_FRONTEND +- * ioctl. +- */ +- c->state = tvp->cmd; +- dprintk("%s() Finalised property cache\n", __func__); +- +- r = dtv_set_frontend(fe); +- break; +- case DTV_FREQUENCY: +- c->frequency = tvp->u.data; +- break; +- case DTV_MODULATION: +- c->modulation = tvp->u.data; +- break; +- case DTV_BANDWIDTH_HZ: +- c->bandwidth_hz = tvp->u.data; +- break; +- case DTV_INVERSION: +- c->inversion = tvp->u.data; +- break; +- case DTV_SYMBOL_RATE: +- c->symbol_rate = tvp->u.data; +- break; +- case DTV_INNER_FEC: +- c->fec_inner = tvp->u.data; +- break; +- case DTV_PILOT: +- c->pilot = tvp->u.data; +- break; +- case DTV_ROLLOFF: +- c->rolloff = tvp->u.data; +- break; +- case DTV_DELIVERY_SYSTEM: +- r = set_delivery_system(fe, tvp->u.data); +- break; +- case DTV_VOLTAGE: +- c->voltage = tvp->u.data; +- r = dvb_frontend_ioctl_legacy(file, FE_SET_VOLTAGE, +- (void *)c->voltage); +- break; +- case DTV_TONE: +- c->sectone = tvp->u.data; +- r = dvb_frontend_ioctl_legacy(file, FE_SET_TONE, +- (void *)c->sectone); +- break; +- case DTV_CODE_RATE_HP: +- c->code_rate_HP = tvp->u.data; +- break; +- case DTV_CODE_RATE_LP: +- c->code_rate_LP = tvp->u.data; +- break; +- case DTV_GUARD_INTERVAL: +- c->guard_interval = tvp->u.data; +- break; +- case DTV_TRANSMISSION_MODE: +- c->transmission_mode = tvp->u.data; +- break; +- case DTV_HIERARCHY: +- c->hierarchy = tvp->u.data; +- break; +- +- /* ISDB-T Support here */ +- case DTV_ISDBT_PARTIAL_RECEPTION: +- c->isdbt_partial_reception = tvp->u.data; +- break; +- case DTV_ISDBT_SOUND_BROADCASTING: +- c->isdbt_sb_mode = tvp->u.data; +- break; +- case DTV_ISDBT_SB_SUBCHANNEL_ID: +- c->isdbt_sb_subchannel = tvp->u.data; +- break; +- case DTV_ISDBT_SB_SEGMENT_IDX: +- c->isdbt_sb_segment_idx = tvp->u.data; +- break; +- case DTV_ISDBT_SB_SEGMENT_COUNT: +- c->isdbt_sb_segment_count = tvp->u.data; +- break; +- case DTV_ISDBT_LAYER_ENABLED: +- c->isdbt_layer_enabled = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERA_FEC: +- c->layer[0].fec = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERA_MODULATION: +- c->layer[0].modulation = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERA_SEGMENT_COUNT: +- c->layer[0].segment_count = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERA_TIME_INTERLEAVING: +- c->layer[0].interleaving = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERB_FEC: +- c->layer[1].fec = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERB_MODULATION: +- c->layer[1].modulation = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERB_SEGMENT_COUNT: +- c->layer[1].segment_count = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERB_TIME_INTERLEAVING: +- c->layer[1].interleaving = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERC_FEC: +- c->layer[2].fec = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERC_MODULATION: +- c->layer[2].modulation = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERC_SEGMENT_COUNT: +- c->layer[2].segment_count = tvp->u.data; +- break; +- case DTV_ISDBT_LAYERC_TIME_INTERLEAVING: +- c->layer[2].interleaving = tvp->u.data; +- break; +- case DTV_ISDBS_TS_ID: +- c->isdbs_ts_id = tvp->u.data; +- break; +- case DTV_DVBT2_PLP_ID: +- c->dvbt2_plp_id = tvp->u.data; +- break; +- default: +- return -EINVAL; +- } +- +- return r; +-} +- +-static int dvb_frontend_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- int err = -EOPNOTSUPP; +- +- dprintk("%s (%d)\n", __func__, _IOC_NR(cmd)); +- +- if (fepriv->exit != DVB_FE_NO_EXIT) +- return -ENODEV; +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY && +- (_IOC_DIR(cmd) != _IOC_READ || cmd == FE_GET_EVENT || +- cmd == FE_DISEQC_RECV_SLAVE_REPLY)) +- return -EPERM; +- +- if (down_interruptible (&fepriv->sem)) +- return -ERESTARTSYS; +- +- if ((cmd == FE_SET_PROPERTY) || (cmd == FE_GET_PROPERTY)) +- err = dvb_frontend_ioctl_properties(file, cmd, parg); +- else { +- c->state = DTV_UNDEFINED; +- err = dvb_frontend_ioctl_legacy(file, cmd, parg); +- } +- +- up(&fepriv->sem); +- return err; +-} +- +-static int dvb_frontend_ioctl_properties(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int err = 0; +- +- struct dtv_properties *tvps = NULL; +- struct dtv_property *tvp = NULL; +- int i; +- +- dprintk("%s\n", __func__); +- +- if(cmd == FE_SET_PROPERTY) { +- tvps = (struct dtv_properties __user *)parg; +- +- dprintk("%s() properties.num = %d\n", __func__, tvps->num); +- dprintk("%s() properties.props = %p\n", __func__, tvps->props); +- +- /* Put an arbitrary limit on the number of messages that can +- * be sent at once */ +- if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) +- return -EINVAL; +- +- tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); +- if (!tvp) { +- err = -ENOMEM; +- goto out; +- } +- +- if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { +- err = -EFAULT; +- goto out; +- } +- +- for (i = 0; i < tvps->num; i++) { +- err = dtv_property_process_set(fe, tvp + i, file); +- if (err < 0) +- goto out; +- (tvp + i)->result = err; +- } +- +- if (c->state == DTV_TUNE) +- dprintk("%s() Property cache is full, tuning\n", __func__); +- +- } else +- if(cmd == FE_GET_PROPERTY) { +- tvps = (struct dtv_properties __user *)parg; +- +- dprintk("%s() properties.num = %d\n", __func__, tvps->num); +- dprintk("%s() properties.props = %p\n", __func__, tvps->props); +- +- /* Put an arbitrary limit on the number of messages that can +- * be sent at once */ +- if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) +- return -EINVAL; +- +- tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); +- if (!tvp) { +- err = -ENOMEM; +- goto out; +- } +- +- if (copy_from_user(tvp, tvps->props, tvps->num * sizeof(struct dtv_property))) { +- err = -EFAULT; +- goto out; +- } +- +- /* +- * Fills the cache out struct with the cache contents, plus +- * the data retrieved from get_frontend, if the frontend +- * is not idle. Otherwise, returns the cached content +- */ +- if (fepriv->state != FESTATE_IDLE) { +- err = dtv_get_frontend(fe, NULL); +- if (err < 0) +- goto out; +- } +- for (i = 0; i < tvps->num; i++) { +- err = dtv_property_process_get(fe, c, tvp + i, file); +- if (err < 0) +- goto out; +- (tvp + i)->result = err; +- } +- +- if (copy_to_user(tvps->props, tvp, tvps->num * sizeof(struct dtv_property))) { +- err = -EFAULT; +- goto out; +- } +- +- } else +- err = -EOPNOTSUPP; +- +-out: +- kfree(tvp); +- return err; +-} +- +-static int dtv_set_frontend(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_frontend_tune_settings fetunesettings; +- u32 rolloff = 0; +- +- if (dvb_frontend_check_parameters(fe) < 0) +- return -EINVAL; +- +- /* +- * Initialize output parameters to match the values given by +- * the user. FE_SET_FRONTEND triggers an initial frontend event +- * with status = 0, which copies output parameters to userspace. +- */ +- dtv_property_legacy_params_sync(fe, &fepriv->parameters_out); +- +- /* +- * Be sure that the bandwidth will be filled for all +- * non-satellite systems, as tuners need to know what +- * low pass/Nyquist half filter should be applied, in +- * order to avoid inter-channel noise. +- * +- * ISDB-T and DVB-T/T2 already sets bandwidth. +- * ATSC and DVB-C don't set, so, the core should fill it. +- * +- * On DVB-C Annex A and C, the bandwidth is a function of +- * the roll-off and symbol rate. Annex B defines different +- * roll-off factors depending on the modulation. Fortunately, +- * Annex B is only used with 6MHz, so there's no need to +- * calculate it. +- * +- * While not officially supported, a side effect of handling it at +- * the cache level is that a program could retrieve the bandwidth +- * via DTV_BANDWIDTH_HZ, which may be useful for test programs. +- */ +- switch (c->delivery_system) { +- case SYS_ATSC: +- case SYS_DVBC_ANNEX_B: +- c->bandwidth_hz = 6000000; +- break; +- case SYS_DVBC_ANNEX_A: +- rolloff = 115; +- break; +- case SYS_DVBC_ANNEX_C: +- rolloff = 113; +- break; +- default: +- break; +- } +- if (rolloff) +- c->bandwidth_hz = (c->symbol_rate * rolloff) / 100; +- +- /* force auto frequency inversion if requested */ +- if (dvb_force_auto_inversion) +- c->inversion = INVERSION_AUTO; +- +- /* +- * without hierarchical coding code_rate_LP is irrelevant, +- * so we tolerate the otherwise invalid FEC_NONE setting +- */ +- if (c->hierarchy == HIERARCHY_NONE && c->code_rate_LP == FEC_NONE) +- c->code_rate_LP = FEC_AUTO; +- +- /* get frontend-specific tuning settings */ +- memset(&fetunesettings, 0, sizeof(struct dvb_frontend_tune_settings)); +- if (fe->ops.get_tune_settings && (fe->ops.get_tune_settings(fe, &fetunesettings) == 0)) { +- fepriv->min_delay = (fetunesettings.min_delay_ms * HZ) / 1000; +- fepriv->max_drift = fetunesettings.max_drift; +- fepriv->step_size = fetunesettings.step_size; +- } else { +- /* default values */ +- switch (c->delivery_system) { +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- fepriv->min_delay = HZ / 20; +- fepriv->step_size = c->symbol_rate / 16000; +- fepriv->max_drift = c->symbol_rate / 2000; +- break; +- case SYS_DVBT: +- case SYS_DVBT2: +- case SYS_ISDBT: +- case SYS_DMBTH: +- fepriv->min_delay = HZ / 20; +- fepriv->step_size = fe->ops.info.frequency_stepsize * 2; +- fepriv->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; +- break; +- default: +- /* +- * FIXME: This sounds wrong! if freqency_stepsize is +- * defined by the frontend, why not use it??? +- */ +- fepriv->min_delay = HZ / 20; +- fepriv->step_size = 0; /* no zigzag */ +- fepriv->max_drift = 0; +- break; +- } +- } +- if (dvb_override_tune_delay > 0) +- fepriv->min_delay = (dvb_override_tune_delay * HZ) / 1000; +- +- fepriv->state = FESTATE_RETUNE; +- +- /* Request the search algorithm to search */ +- fepriv->algo_status |= DVBFE_ALGO_SEARCH_AGAIN; +- +- dvb_frontend_clear_events(fe); +- dvb_frontend_add_event(fe, 0); +- dvb_frontend_wakeup(fe); +- fepriv->status = 0; +- +- return 0; +-} +- +- +-static int dvb_frontend_ioctl_legacy(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int cb_err, err = -EOPNOTSUPP; +- +- if (fe->dvb->fe_ioctl_override) { +- cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, +- DVB_FE_IOCTL_PRE); +- if (cb_err < 0) +- return cb_err; +- if (cb_err > 0) +- return 0; +- /* fe_ioctl_override returning 0 allows +- * dvb-core to continue handling the ioctl */ +- } +- +- switch (cmd) { +- case FE_GET_INFO: { +- struct dvb_frontend_info* info = parg; +- +- memcpy(info, &fe->ops.info, sizeof(struct dvb_frontend_info)); +- dvb_frontend_get_frequency_limits(fe, &info->frequency_min, &info->frequency_max); +- +- /* +- * Associate the 4 delivery systems supported by DVBv3 +- * API with their DVBv5 counterpart. For the other standards, +- * use the closest type, assuming that it would hopefully +- * work with a DVBv3 application. +- * It should be noticed that, on multi-frontend devices with +- * different types (terrestrial and cable, for example), +- * a pure DVBv3 application won't be able to use all delivery +- * systems. Yet, changing the DVBv5 cache to the other delivery +- * system should be enough for making it work. +- */ +- switch (dvbv3_type(c->delivery_system)) { +- case DVBV3_QPSK: +- info->type = FE_QPSK; +- break; +- case DVBV3_ATSC: +- info->type = FE_ATSC; +- break; +- case DVBV3_QAM: +- info->type = FE_QAM; +- break; +- case DVBV3_OFDM: +- info->type = FE_OFDM; +- break; +- default: +- printk(KERN_ERR +- "%s: doesn't know how to handle a DVBv3 call to delivery system %i\n", +- __func__, c->delivery_system); +- fe->ops.info.type = FE_OFDM; +- } +- dprintk("current delivery system on cache: %d, V3 type: %d\n", +- c->delivery_system, fe->ops.info.type); +- +- /* Force the CAN_INVERSION_AUTO bit on. If the frontend doesn't +- * do it, it is done for it. */ +- info->caps |= FE_CAN_INVERSION_AUTO; +- err = 0; +- break; +- } +- +- case FE_READ_STATUS: { +- fe_status_t* status = parg; +- +- /* if retune was requested but hasn't occurred yet, prevent +- * that user get signal state from previous tuning */ +- if (fepriv->state == FESTATE_RETUNE || +- fepriv->state == FESTATE_ERROR) { +- err=0; +- *status = 0; +- break; +- } +- +- if (fe->ops.read_status) +- err = fe->ops.read_status(fe, status); +- break; +- } +- case FE_READ_BER: +- if (fe->ops.read_ber) +- err = fe->ops.read_ber(fe, (__u32*) parg); +- break; +- +- case FE_READ_SIGNAL_STRENGTH: +- if (fe->ops.read_signal_strength) +- err = fe->ops.read_signal_strength(fe, (__u16*) parg); +- break; +- +- case FE_READ_SNR: +- if (fe->ops.read_snr) +- err = fe->ops.read_snr(fe, (__u16*) parg); +- break; +- +- case FE_READ_UNCORRECTED_BLOCKS: +- if (fe->ops.read_ucblocks) +- err = fe->ops.read_ucblocks(fe, (__u32*) parg); +- break; +- +- +- case FE_DISEQC_RESET_OVERLOAD: +- if (fe->ops.diseqc_reset_overload) { +- err = fe->ops.diseqc_reset_overload(fe); +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_DISEQC_SEND_MASTER_CMD: +- if (fe->ops.diseqc_send_master_cmd) { +- err = fe->ops.diseqc_send_master_cmd(fe, (struct dvb_diseqc_master_cmd*) parg); +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_DISEQC_SEND_BURST: +- if (fe->ops.diseqc_send_burst) { +- err = fe->ops.diseqc_send_burst(fe, (fe_sec_mini_cmd_t) parg); +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_SET_TONE: +- if (fe->ops.set_tone) { +- err = fe->ops.set_tone(fe, (fe_sec_tone_mode_t) parg); +- fepriv->tone = (fe_sec_tone_mode_t) parg; +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_SET_VOLTAGE: +- if (fe->ops.set_voltage) { +- err = fe->ops.set_voltage(fe, (fe_sec_voltage_t) parg); +- fepriv->voltage = (fe_sec_voltage_t) parg; +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_DISHNETWORK_SEND_LEGACY_CMD: +- if (fe->ops.dishnetwork_send_legacy_command) { +- err = fe->ops.dishnetwork_send_legacy_command(fe, (unsigned long) parg); +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } else if (fe->ops.set_voltage) { +- /* +- * NOTE: This is a fallback condition. Some frontends +- * (stv0299 for instance) take longer than 8msec to +- * respond to a set_voltage command. Those switches +- * need custom routines to switch properly. For all +- * other frontends, the following should work ok. +- * Dish network legacy switches (as used by Dish500) +- * are controlled by sending 9-bit command words +- * spaced 8msec apart. +- * the actual command word is switch/port dependent +- * so it is up to the userspace application to send +- * the right command. +- * The command must always start with a '0' after +- * initialization, so parg is 8 bits and does not +- * include the initialization or start bit +- */ +- unsigned long swcmd = ((unsigned long) parg) << 1; +- struct timeval nexttime; +- struct timeval tv[10]; +- int i; +- u8 last = 1; +- if (dvb_frontend_debug) +- printk("%s switch command: 0x%04lx\n", __func__, swcmd); +- do_gettimeofday(&nexttime); +- if (dvb_frontend_debug) +- memcpy(&tv[0], &nexttime, sizeof(struct timeval)); +- /* before sending a command, initialize by sending +- * a 32ms 18V to the switch +- */ +- fe->ops.set_voltage(fe, SEC_VOLTAGE_18); +- dvb_frontend_sleep_until(&nexttime, 32000); +- +- for (i = 0; i < 9; i++) { +- if (dvb_frontend_debug) +- do_gettimeofday(&tv[i + 1]); +- if ((swcmd & 0x01) != last) { +- /* set voltage to (last ? 13V : 18V) */ +- fe->ops.set_voltage(fe, (last) ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18); +- last = (last) ? 0 : 1; +- } +- swcmd = swcmd >> 1; +- if (i != 8) +- dvb_frontend_sleep_until(&nexttime, 8000); +- } +- if (dvb_frontend_debug) { +- printk("%s(%d): switch delay (should be 32k followed by all 8k\n", +- __func__, fe->dvb->num); +- for (i = 1; i < 10; i++) +- printk("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); +- } +- err = 0; +- fepriv->state = FESTATE_DISEQC; +- fepriv->status = 0; +- } +- break; +- +- case FE_DISEQC_RECV_SLAVE_REPLY: +- if (fe->ops.diseqc_recv_slave_reply) +- err = fe->ops.diseqc_recv_slave_reply(fe, (struct dvb_diseqc_slave_reply*) parg); +- break; +- +- case FE_ENABLE_HIGH_LNB_VOLTAGE: +- if (fe->ops.enable_high_lnb_voltage) +- err = fe->ops.enable_high_lnb_voltage(fe, (long) parg); +- break; +- +- case FE_SET_FRONTEND: +- err = set_delivery_system(fe, SYS_UNDEFINED); +- if (err) +- break; +- +- err = dtv_property_cache_sync(fe, c, parg); +- if (err) +- break; +- err = dtv_set_frontend(fe); +- break; +- case FE_GET_EVENT: +- err = dvb_frontend_get_event (fe, parg, file->f_flags); +- break; +- +- case FE_GET_FRONTEND: +- err = dtv_get_frontend(fe, parg); +- break; +- +- case FE_SET_FRONTEND_TUNE_MODE: +- fepriv->tune_mode_flags = (unsigned long) parg; +- err = 0; +- break; +- }; +- +- if (fe->dvb->fe_ioctl_override) { +- cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, +- DVB_FE_IOCTL_POST); +- if (cb_err < 0) +- return cb_err; +- } +- +- return err; +-} +- +- +-static unsigned int dvb_frontend_poll(struct file *file, struct poll_table_struct *wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- +- dprintk ("%s\n", __func__); +- +- poll_wait (file, &fepriv->events.wait_queue, wait); +- +- if (fepriv->events.eventw != fepriv->events.eventr) +- return (POLLIN | POLLRDNORM | POLLPRI); +- +- return 0; +-} +- +-static int dvb_frontend_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- struct dvb_adapter *adapter = fe->dvb; +- int ret; +- +- dprintk ("%s\n", __func__); +- if (fepriv->exit == DVB_FE_DEVICE_REMOVED) +- return -ENODEV; +- +- if (adapter->mfe_shared) { +- mutex_lock (&adapter->mfe_lock); +- +- if (adapter->mfe_dvbdev == NULL) +- adapter->mfe_dvbdev = dvbdev; +- +- else if (adapter->mfe_dvbdev != dvbdev) { +- struct dvb_device +- *mfedev = adapter->mfe_dvbdev; +- struct dvb_frontend +- *mfe = mfedev->priv; +- struct dvb_frontend_private +- *mfepriv = mfe->frontend_priv; +- int mferetry = (dvb_mfe_wait_time << 1); +- +- mutex_unlock (&adapter->mfe_lock); +- while (mferetry-- && (mfedev->users != -1 || +- mfepriv->thread != NULL)) { +- if(msleep_interruptible(500)) { +- if(signal_pending(current)) +- return -EINTR; +- } +- } +- +- mutex_lock (&adapter->mfe_lock); +- if(adapter->mfe_dvbdev != dvbdev) { +- mfedev = adapter->mfe_dvbdev; +- mfe = mfedev->priv; +- mfepriv = mfe->frontend_priv; +- if (mfedev->users != -1 || +- mfepriv->thread != NULL) { +- mutex_unlock (&adapter->mfe_lock); +- return -EBUSY; +- } +- adapter->mfe_dvbdev = dvbdev; +- } +- } +- } +- +- if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) { +- if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0) +- goto err0; +- +- /* If we took control of the bus, we need to force +- reinitialization. This is because many ts_bus_ctrl() +- functions strobe the RESET pin on the demod, and if the +- frontend thread already exists then the dvb_init() routine +- won't get called (which is what usually does initial +- register configuration). */ +- fepriv->reinitialise = 1; +- } +- +- if ((ret = dvb_generic_open (inode, file)) < 0) +- goto err1; +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) { +- /* normal tune mode when opened R/W */ +- fepriv->tune_mode_flags &= ~FE_TUNE_MODE_ONESHOT; +- fepriv->tone = -1; +- fepriv->voltage = -1; +- +- ret = dvb_frontend_start (fe); +- if (ret) +- goto err2; +- +- /* empty event queue */ +- fepriv->events.eventr = fepriv->events.eventw = 0; +- } +- +- if (adapter->mfe_shared) +- mutex_unlock (&adapter->mfe_lock); +- return ret; +- +-err2: +- dvb_generic_release(inode, file); +-err1: +- if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) +- fe->ops.ts_bus_ctrl(fe, 0); +-err0: +- if (adapter->mfe_shared) +- mutex_unlock (&adapter->mfe_lock); +- return ret; +-} +- +-static int dvb_frontend_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_frontend *fe = dvbdev->priv; +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- int ret; +- +- dprintk ("%s\n", __func__); +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) { +- fepriv->release_jiffies = jiffies; +- mb(); +- } +- +- ret = dvb_generic_release (inode, file); +- +- if (dvbdev->users == -1) { +- wake_up(&fepriv->wait_queue); +- if (fepriv->exit != DVB_FE_NO_EXIT) { +- fops_put(file->f_op); +- file->f_op = NULL; +- wake_up(&dvbdev->wait_queue); +- } +- if (fe->ops.ts_bus_ctrl) +- fe->ops.ts_bus_ctrl(fe, 0); +- } +- +- return ret; +-} +- +-static const struct file_operations dvb_frontend_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = dvb_generic_ioctl, +- .poll = dvb_frontend_poll, +- .open = dvb_frontend_open, +- .release = dvb_frontend_release, +- .llseek = noop_llseek, +-}; +- +-int dvb_register_frontend(struct dvb_adapter* dvb, +- struct dvb_frontend* fe) +-{ +- struct dvb_frontend_private *fepriv; +- static const struct dvb_device dvbdev_template = { +- .users = ~0, +- .writers = 1, +- .readers = (~0)-1, +- .fops = &dvb_frontend_fops, +- .kernel_ioctl = dvb_frontend_ioctl +- }; +- +- dprintk ("%s\n", __func__); +- +- if (mutex_lock_interruptible(&frontend_mutex)) +- return -ERESTARTSYS; +- +- fe->frontend_priv = kzalloc(sizeof(struct dvb_frontend_private), GFP_KERNEL); +- if (fe->frontend_priv == NULL) { +- mutex_unlock(&frontend_mutex); +- return -ENOMEM; +- } +- fepriv = fe->frontend_priv; +- +- sema_init(&fepriv->sem, 1); +- init_waitqueue_head (&fepriv->wait_queue); +- init_waitqueue_head (&fepriv->events.wait_queue); +- mutex_init(&fepriv->events.mtx); +- fe->dvb = dvb; +- fepriv->inversion = INVERSION_OFF; +- +- printk ("DVB: registering adapter %i frontend %i (%s)...\n", +- fe->dvb->num, +- fe->id, +- fe->ops.info.name); +- +- dvb_register_device (fe->dvb, &fepriv->dvbdev, &dvbdev_template, +- fe, DVB_DEVICE_FRONTEND); +- +- /* +- * Initialize the cache to the proper values according with the +- * first supported delivery system (ops->delsys[0]) +- */ +- +- fe->dtv_property_cache.delivery_system = fe->ops.delsys[0]; +- dvb_frontend_clear_cache(fe); +- +- mutex_unlock(&frontend_mutex); +- return 0; +-} +-EXPORT_SYMBOL(dvb_register_frontend); +- +-int dvb_unregister_frontend(struct dvb_frontend* fe) +-{ +- struct dvb_frontend_private *fepriv = fe->frontend_priv; +- dprintk ("%s\n", __func__); +- +- mutex_lock(&frontend_mutex); +- dvb_frontend_stop (fe); +- mutex_unlock(&frontend_mutex); +- +- if (fepriv->dvbdev->users < -1) +- wait_event(fepriv->dvbdev->wait_queue, +- fepriv->dvbdev->users==-1); +- +- mutex_lock(&frontend_mutex); +- dvb_unregister_device (fepriv->dvbdev); +- +- /* fe is invalid now */ +- kfree(fepriv); +- mutex_unlock(&frontend_mutex); +- return 0; +-} +-EXPORT_SYMBOL(dvb_unregister_frontend); +- +-#ifdef CONFIG_MEDIA_ATTACH +-void dvb_frontend_detach(struct dvb_frontend* fe) +-{ +- void *ptr; +- +- if (fe->ops.release_sec) { +- fe->ops.release_sec(fe); +- symbol_put_addr(fe->ops.release_sec); +- } +- if (fe->ops.tuner_ops.release) { +- fe->ops.tuner_ops.release(fe); +- symbol_put_addr(fe->ops.tuner_ops.release); +- } +- if (fe->ops.analog_ops.release) { +- fe->ops.analog_ops.release(fe); +- symbol_put_addr(fe->ops.analog_ops.release); +- } +- ptr = (void*)fe->ops.release; +- if (ptr) { +- fe->ops.release(fe); +- symbol_put_addr(ptr); +- } +-} +-#else +-void dvb_frontend_detach(struct dvb_frontend* fe) +-{ +- if (fe->ops.release_sec) +- fe->ops.release_sec(fe); +- if (fe->ops.tuner_ops.release) +- fe->ops.tuner_ops.release(fe); +- if (fe->ops.analog_ops.release) +- fe->ops.analog_ops.release(fe); +- if (fe->ops.release) +- fe->ops.release(fe); +-} +-#endif +-EXPORT_SYMBOL(dvb_frontend_detach); +diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h +deleted file mode 100644 +index d63a821..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_frontend.h ++++ /dev/null +@@ -1,404 +0,0 @@ +-/* +- * dvb_frontend.h +- * +- * Copyright (C) 2001 convergence integrated media GmbH +- * Copyright (C) 2004 convergence GmbH +- * +- * Written by Ralph Metzler +- * Overhauled by Holger Waechtler +- * Kernel I2C stuff by Michael Hunold +- * +- * 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. +- * +- */ +- +-#ifndef _DVB_FRONTEND_H_ +-#define _DVB_FRONTEND_H_ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "dvbdev.h" +- +-/* +- * Maximum number of Delivery systems per frontend. It +- * should be smaller or equal to 32 +- */ +-#define MAX_DELSYS 8 +- +-struct dvb_frontend_tune_settings { +- int min_delay_ms; +- int step_size; +- int max_drift; +-}; +- +-struct dvb_frontend; +- +-struct dvb_tuner_info { +- char name[128]; +- +- u32 frequency_min; +- u32 frequency_max; +- u32 frequency_step; +- +- u32 bandwidth_min; +- u32 bandwidth_max; +- u32 bandwidth_step; +-}; +- +-struct analog_parameters { +- unsigned int frequency; +- unsigned int mode; +- unsigned int audmode; +- u64 std; +-}; +- +-enum dvbfe_modcod { +- DVBFE_MODCOD_DUMMY_PLFRAME = 0, +- DVBFE_MODCOD_QPSK_1_4, +- DVBFE_MODCOD_QPSK_1_3, +- DVBFE_MODCOD_QPSK_2_5, +- DVBFE_MODCOD_QPSK_1_2, +- DVBFE_MODCOD_QPSK_3_5, +- DVBFE_MODCOD_QPSK_2_3, +- DVBFE_MODCOD_QPSK_3_4, +- DVBFE_MODCOD_QPSK_4_5, +- DVBFE_MODCOD_QPSK_5_6, +- DVBFE_MODCOD_QPSK_8_9, +- DVBFE_MODCOD_QPSK_9_10, +- DVBFE_MODCOD_8PSK_3_5, +- DVBFE_MODCOD_8PSK_2_3, +- DVBFE_MODCOD_8PSK_3_4, +- DVBFE_MODCOD_8PSK_5_6, +- DVBFE_MODCOD_8PSK_8_9, +- DVBFE_MODCOD_8PSK_9_10, +- DVBFE_MODCOD_16APSK_2_3, +- DVBFE_MODCOD_16APSK_3_4, +- DVBFE_MODCOD_16APSK_4_5, +- DVBFE_MODCOD_16APSK_5_6, +- DVBFE_MODCOD_16APSK_8_9, +- DVBFE_MODCOD_16APSK_9_10, +- DVBFE_MODCOD_32APSK_3_4, +- DVBFE_MODCOD_32APSK_4_5, +- DVBFE_MODCOD_32APSK_5_6, +- DVBFE_MODCOD_32APSK_8_9, +- DVBFE_MODCOD_32APSK_9_10, +- DVBFE_MODCOD_RESERVED_1, +- DVBFE_MODCOD_BPSK_1_3, +- DVBFE_MODCOD_BPSK_1_4, +- DVBFE_MODCOD_RESERVED_2 +-}; +- +-enum tuner_param { +- DVBFE_TUNER_FREQUENCY = (1 << 0), +- DVBFE_TUNER_TUNERSTEP = (1 << 1), +- DVBFE_TUNER_IFFREQ = (1 << 2), +- DVBFE_TUNER_BANDWIDTH = (1 << 3), +- DVBFE_TUNER_REFCLOCK = (1 << 4), +- DVBFE_TUNER_IQSENSE = (1 << 5), +- DVBFE_TUNER_DUMMY = (1 << 31) +-}; +- +-/* +- * ALGO_HW: (Hardware Algorithm) +- * ---------------------------------------------------------------- +- * Devices that support this algorithm do everything in hardware +- * and no software support is needed to handle them. +- * Requesting these devices to LOCK is the only thing required, +- * device is supposed to do everything in the hardware. +- * +- * ALGO_SW: (Software Algorithm) +- * ---------------------------------------------------------------- +- * These are dumb devices, that require software to do everything +- * +- * ALGO_CUSTOM: (Customizable Agorithm) +- * ---------------------------------------------------------------- +- * Devices having this algorithm can be customized to have specific +- * algorithms in the frontend driver, rather than simply doing a +- * software zig-zag. In this case the zigzag maybe hardware assisted +- * or it maybe completely done in hardware. In all cases, usage of +- * this algorithm, in conjunction with the search and track +- * callbacks, utilizes the driver specific algorithm. +- * +- * ALGO_RECOVERY: (Recovery Algorithm) +- * ---------------------------------------------------------------- +- * These devices have AUTO recovery capabilities from LOCK failure +- */ +-enum dvbfe_algo { +- DVBFE_ALGO_HW = (1 << 0), +- DVBFE_ALGO_SW = (1 << 1), +- DVBFE_ALGO_CUSTOM = (1 << 2), +- DVBFE_ALGO_RECOVERY = (1 << 31) +-}; +- +-struct tuner_state { +- u32 frequency; +- u32 tunerstep; +- u32 ifreq; +- u32 bandwidth; +- u32 iqsense; +- u32 refclock; +-}; +- +-/* +- * search callback possible return status +- * +- * DVBFE_ALGO_SEARCH_SUCCESS +- * The frontend search algorithm completed and returned successfully +- * +- * DVBFE_ALGO_SEARCH_ASLEEP +- * The frontend search algorithm is sleeping +- * +- * DVBFE_ALGO_SEARCH_FAILED +- * The frontend search for a signal failed +- * +- * DVBFE_ALGO_SEARCH_INVALID +- * The frontend search algorith was probably supplied with invalid +- * parameters and the search is an invalid one +- * +- * DVBFE_ALGO_SEARCH_ERROR +- * The frontend search algorithm failed due to some error +- * +- * DVBFE_ALGO_SEARCH_AGAIN +- * The frontend search algorithm was requested to search again +- */ +-enum dvbfe_search { +- DVBFE_ALGO_SEARCH_SUCCESS = (1 << 0), +- DVBFE_ALGO_SEARCH_ASLEEP = (1 << 1), +- DVBFE_ALGO_SEARCH_FAILED = (1 << 2), +- DVBFE_ALGO_SEARCH_INVALID = (1 << 3), +- DVBFE_ALGO_SEARCH_AGAIN = (1 << 4), +- DVBFE_ALGO_SEARCH_ERROR = (1 << 31), +-}; +- +- +-struct dvb_tuner_ops { +- +- struct dvb_tuner_info info; +- +- int (*release)(struct dvb_frontend *fe); +- int (*init)(struct dvb_frontend *fe); +- int (*sleep)(struct dvb_frontend *fe); +- +- /** This is for simple PLLs - set all parameters in one go. */ +- int (*set_params)(struct dvb_frontend *fe); +- int (*set_analog_params)(struct dvb_frontend *fe, struct analog_parameters *p); +- +- /** This is support for demods like the mt352 - fills out the supplied buffer with what to write. */ +- int (*calc_regs)(struct dvb_frontend *fe, u8 *buf, int buf_len); +- +- /** This is to allow setting tuner-specific configs */ +- int (*set_config)(struct dvb_frontend *fe, void *priv_cfg); +- +- int (*get_frequency)(struct dvb_frontend *fe, u32 *frequency); +- int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); +- int (*get_if_frequency)(struct dvb_frontend *fe, u32 *frequency); +- +-#define TUNER_STATUS_LOCKED 1 +-#define TUNER_STATUS_STEREO 2 +- int (*get_status)(struct dvb_frontend *fe, u32 *status); +- int (*get_rf_strength)(struct dvb_frontend *fe, u16 *strength); +- +- /** These are provided separately from set_params in order to facilitate silicon +- * tuners which require sophisticated tuning loops, controlling each parameter separately. */ +- int (*set_frequency)(struct dvb_frontend *fe, u32 frequency); +- int (*set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); +- +- /* +- * These are provided separately from set_params in order to facilitate silicon +- * tuners which require sophisticated tuning loops, controlling each parameter separately. +- */ +- int (*set_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); +- int (*get_state)(struct dvb_frontend *fe, enum tuner_param param, struct tuner_state *state); +-}; +- +-struct analog_demod_info { +- char *name; +-}; +- +-struct analog_demod_ops { +- +- struct analog_demod_info info; +- +- void (*set_params)(struct dvb_frontend *fe, +- struct analog_parameters *params); +- int (*has_signal)(struct dvb_frontend *fe); +- int (*get_afc)(struct dvb_frontend *fe); +- void (*tuner_status)(struct dvb_frontend *fe); +- void (*standby)(struct dvb_frontend *fe); +- void (*release)(struct dvb_frontend *fe); +- int (*i2c_gate_ctrl)(struct dvb_frontend *fe, int enable); +- +- /** This is to allow setting tuner-specific configuration */ +- int (*set_config)(struct dvb_frontend *fe, void *priv_cfg); +-}; +- +-struct dtv_frontend_properties; +- +-struct dvb_frontend_ops { +- +- struct dvb_frontend_info info; +- +- u8 delsys[MAX_DELSYS]; +- +- void (*release)(struct dvb_frontend* fe); +- void (*release_sec)(struct dvb_frontend* fe); +- +- int (*init)(struct dvb_frontend* fe); +- int (*sleep)(struct dvb_frontend* fe); +- +- int (*write)(struct dvb_frontend* fe, const u8 buf[], int len); +- +- /* if this is set, it overrides the default swzigzag */ +- int (*tune)(struct dvb_frontend* fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status); +- /* get frontend tuning algorithm from the module */ +- enum dvbfe_algo (*get_frontend_algo)(struct dvb_frontend *fe); +- +- /* these two are only used for the swzigzag code */ +- int (*set_frontend)(struct dvb_frontend *fe); +- int (*get_tune_settings)(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* settings); +- +- int (*get_frontend)(struct dvb_frontend *fe); +- +- int (*read_status)(struct dvb_frontend* fe, fe_status_t* status); +- int (*read_ber)(struct dvb_frontend* fe, u32* ber); +- int (*read_signal_strength)(struct dvb_frontend* fe, u16* strength); +- int (*read_snr)(struct dvb_frontend* fe, u16* snr); +- int (*read_ucblocks)(struct dvb_frontend* fe, u32* ucblocks); +- +- int (*diseqc_reset_overload)(struct dvb_frontend* fe); +- int (*diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); +- int (*diseqc_recv_slave_reply)(struct dvb_frontend* fe, struct dvb_diseqc_slave_reply* reply); +- int (*diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); +- int (*set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); +- int (*set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); +- int (*enable_high_lnb_voltage)(struct dvb_frontend* fe, long arg); +- int (*dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); +- int (*i2c_gate_ctrl)(struct dvb_frontend* fe, int enable); +- int (*ts_bus_ctrl)(struct dvb_frontend* fe, int acquire); +- +- /* These callbacks are for devices that implement their own +- * tuning algorithms, rather than a simple swzigzag +- */ +- enum dvbfe_search (*search)(struct dvb_frontend *fe); +- +- struct dvb_tuner_ops tuner_ops; +- struct analog_demod_ops analog_ops; +- +- int (*set_property)(struct dvb_frontend* fe, struct dtv_property* tvp); +- int (*get_property)(struct dvb_frontend* fe, struct dtv_property* tvp); +-}; +- +-#ifdef __DVB_CORE__ +-#define MAX_EVENT 8 +- +-struct dvb_fe_events { +- struct dvb_frontend_event events[MAX_EVENT]; +- int eventw; +- int eventr; +- int overflow; +- wait_queue_head_t wait_queue; +- struct mutex mtx; +-}; +-#endif +- +-struct dtv_frontend_properties { +- +- /* Cache State */ +- u32 state; +- +- u32 frequency; +- fe_modulation_t modulation; +- +- fe_sec_voltage_t voltage; +- fe_sec_tone_mode_t sectone; +- fe_spectral_inversion_t inversion; +- fe_code_rate_t fec_inner; +- fe_transmit_mode_t transmission_mode; +- u32 bandwidth_hz; /* 0 = AUTO */ +- fe_guard_interval_t guard_interval; +- fe_hierarchy_t hierarchy; +- u32 symbol_rate; +- fe_code_rate_t code_rate_HP; +- fe_code_rate_t code_rate_LP; +- +- fe_pilot_t pilot; +- fe_rolloff_t rolloff; +- +- fe_delivery_system_t delivery_system; +- +- /* ISDB-T specifics */ +- u8 isdbt_partial_reception; +- u8 isdbt_sb_mode; +- u8 isdbt_sb_subchannel; +- u32 isdbt_sb_segment_idx; +- u32 isdbt_sb_segment_count; +- u8 isdbt_layer_enabled; +- struct { +- u8 segment_count; +- fe_code_rate_t fec; +- fe_modulation_t modulation; +- u8 interleaving; +- } layer[3]; +- +- /* ISDB-T specifics */ +- u32 isdbs_ts_id; +- +- /* DVB-T2 specifics */ +- u32 dvbt2_plp_id; +-}; +- +-struct dvb_frontend { +- struct dvb_frontend_ops ops; +- struct dvb_adapter *dvb; +- void *demodulator_priv; +- void *tuner_priv; +- void *frontend_priv; +- void *sec_priv; +- void *analog_demod_priv; +- struct dtv_frontend_properties dtv_property_cache; +-#define DVB_FRONTEND_COMPONENT_TUNER 0 +-#define DVB_FRONTEND_COMPONENT_DEMOD 1 +- int (*callback)(void *adapter_priv, int component, int cmd, int arg); +- int id; +-}; +- +-extern int dvb_register_frontend(struct dvb_adapter *dvb, +- struct dvb_frontend *fe); +- +-extern int dvb_unregister_frontend(struct dvb_frontend *fe); +- +-extern void dvb_frontend_detach(struct dvb_frontend *fe); +- +-extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); +- +-extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); +-extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); +- +-#endif +diff --git a/drivers/media/dvb/dvb-core/dvb_math.c b/drivers/media/dvb/dvb-core/dvb_math.c +deleted file mode 100644 +index beb7c93..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_math.c ++++ /dev/null +@@ -1,145 +0,0 @@ +-/* +- * dvb-math provides some complex fixed-point math +- * operations shared between the dvb related stuff +- * +- * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) +- * +- * This library 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 Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with this library; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +- +-#include +-#include +-#include +-#include +-#include "dvb_math.h" +- +-static const unsigned short logtable[256] = { +- 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, +- 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, +- 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6, +- 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37, +- 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f, +- 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41, +- 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1, +- 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142, +- 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68, +- 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355, +- 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c, +- 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490, +- 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3, +- 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507, +- 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe, +- 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca, +- 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c, +- 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7, +- 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c, +- 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c, +- 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a, +- 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065, +- 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730, +- 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc, +- 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469, +- 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9, +- 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c, +- 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765, +- 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83, +- 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387, +- 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973, +- 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47 +-}; +- +-unsigned int intlog2(u32 value) +-{ +- /** +- * returns: log2(value) * 2^24 +- * wrong result if value = 0 (log2(0) is undefined) +- */ +- unsigned int msb; +- unsigned int logentry; +- unsigned int significand; +- unsigned int interpolation; +- +- if (unlikely(value == 0)) { +- WARN_ON(1); +- return 0; +- } +- +- /* first detect the msb (count begins at 0) */ +- msb = fls(value) - 1; +- +- /** +- * now we use a logtable after the following method: +- * +- * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24 +- * where x = msb and therefore 1 <= y < 2 +- * first y is determined by shifting the value left +- * so that msb is bit 31 +- * 0x00231f56 -> 0x8C7D5800 +- * the result is y * 2^31 -> "significand" +- * then the highest 9 bits are used for a table lookup +- * the highest bit is discarded because it's always set +- * the highest nine bits in our example are 100011000 +- * so we would use the entry 0x18 +- */ +- significand = value << (31 - msb); +- logentry = (significand >> 23) & 0xff; +- +- /** +- * last step we do is interpolation because of the +- * limitations of the log table the error is that part of +- * the significand which isn't used for lookup then we +- * compute the ratio between the error and the next table entry +- * and interpolate it between the log table entry used and the +- * next one the biggest error possible is 0x7fffff +- * (in our example it's 0x7D5800) +- * needed value for next table entry is 0x800000 +- * so the interpolation is +- * (error / 0x800000) * (logtable_next - logtable_current) +- * in the implementation the division is moved to the end for +- * better accuracy there is also an overflow correction if +- * logtable_next is 256 +- */ +- interpolation = ((significand & 0x7fffff) * +- ((logtable[(logentry + 1) & 0xff] - +- logtable[logentry]) & 0xffff)) >> 15; +- +- /* now we return the result */ +- return ((msb << 24) + (logtable[logentry] << 8) + interpolation); +-} +-EXPORT_SYMBOL(intlog2); +- +-unsigned int intlog10(u32 value) +-{ +- /** +- * returns: log10(value) * 2^24 +- * wrong result if value = 0 (log10(0) is undefined) +- */ +- u64 log; +- +- if (unlikely(value == 0)) { +- WARN_ON(1); +- return 0; +- } +- +- log = intlog2(value); +- +- /** +- * we use the following method: +- * log10(x) = log2(x) * log10(2) +- */ +- +- return (log * 646456993) >> 31; +-} +-EXPORT_SYMBOL(intlog10); +diff --git a/drivers/media/dvb/dvb-core/dvb_math.h b/drivers/media/dvb/dvb-core/dvb_math.h +deleted file mode 100644 +index aecc867..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_math.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- * dvb-math provides some complex fixed-point math +- * operations shared between the dvb related stuff +- * +- * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com) +- * +- * This library 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 Lesser General Public License for more details. +- * +- * You should have received a copy of the GNU Lesser General Public +- * License along with this library; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +- +-#ifndef __DVB_MATH_H +-#define __DVB_MATH_H +- +-#include +- +-/** +- * computes log2 of a value; the result is shifted left by 24 bits +- * +- * to use rational values you can use the following method: +- * intlog2(value) = intlog2(value * 2^x) - x * 2^24 +- * +- * example: intlog2(8) will give 3 << 24 = 3 * 2^24 +- * example: intlog2(9) will give 3 << 24 + ... = 3.16... * 2^24 +- * example: intlog2(1.5) = intlog2(3) - 2^24 = 0.584... * 2^24 +- * +- * @param value The value (must be != 0) +- * @return log2(value) * 2^24 +- */ +-extern unsigned int intlog2(u32 value); +- +-/** +- * computes log10 of a value; the result is shifted left by 24 bits +- * +- * to use rational values you can use the following method: +- * intlog10(value) = intlog10(value * 10^x) - x * 2^24 +- * +- * example: intlog10(1000) will give 3 << 24 = 3 * 2^24 +- * due to the implementation intlog10(1000) might be not exactly 3 * 2^24 +- * +- * look at intlog2 for similar examples +- * +- * @param value The value (must be != 0) +- * @return log10(value) * 2^24 +- */ +-extern unsigned int intlog10(u32 value); +- +-#endif +diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c +deleted file mode 100644 +index 8766ce8..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_net.c ++++ /dev/null +@@ -1,1516 +0,0 @@ +-/* +- * dvb_net.c +- * +- * Copyright (C) 2001 Convergence integrated media GmbH +- * Ralph Metzler +- * Copyright (C) 2002 Ralph Metzler +- * +- * ULE Decapsulation code: +- * Copyright (C) 2003, 2004 gcs - Global Communication & Services GmbH. +- * and Department of Scientific Computing +- * Paris Lodron University of Salzburg. +- * Hilmar Linder +- * and Wolfram Stering +- * +- * ULE Decaps according to RFC 4326. +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-/* +- * ULE ChangeLog: +- * Feb 2004: hl/ws v1: Implementing draft-fair-ipdvb-ule-01.txt +- * +- * Dec 2004: hl/ws v2: Implementing draft-ietf-ipdvb-ule-03.txt: +- * ULE Extension header handling. +- * Bugreports by Moritz Vieth and Hanno Tersteegen, +- * Fraunhofer Institute for Open Communication Systems +- * Competence Center for Advanced Satellite Communications. +- * Bugfixes and robustness improvements. +- * Filtering on dest MAC addresses, if present (D-Bit = 0) +- * ULE_DEBUG compile-time option. +- * Apr 2006: cp v3: Bugfixes and compliency with RFC 4326 (ULE) by +- * Christian Praehauser , +- * Paris Lodron University of Salzburg. +- */ +- +-/* +- * FIXME / TODO (dvb_net.c): +- * +- * Unloading does not work for 2.6.9 kernels: a refcount doesn't go to zero. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_demux.h" +-#include "dvb_net.h" +- +-static int dvb_net_debug; +-module_param(dvb_net_debug, int, 0444); +-MODULE_PARM_DESC(dvb_net_debug, "enable debug messages"); +- +-#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0) +- +- +-static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) +-{ +- unsigned int j; +- for (j = 0; j < cnt; j++) +- c = crc32_be( c, iov[j].iov_base, iov[j].iov_len ); +- return c; +-} +- +- +-#define DVB_NET_MULTICAST_MAX 10 +- +-#undef ULE_DEBUG +- +-#ifdef ULE_DEBUG +- +-#define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" +-#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5] +- +-#define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) +- +-static void hexdump( const unsigned char *buf, unsigned short len ) +-{ +- char str[80], octet[10]; +- int ofs, i, l; +- +- for (ofs = 0; ofs < len; ofs += 16) { +- sprintf( str, "%03d: ", ofs ); +- +- for (i = 0; i < 16; i++) { +- if ((i + ofs) < len) +- sprintf( octet, "%02x ", buf[ofs + i] ); +- else +- strcpy( octet, " " ); +- +- strcat( str, octet ); +- } +- strcat( str, " " ); +- l = strlen( str ); +- +- for (i = 0; (i < 16) && ((i + ofs) < len); i++) +- str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.'; +- +- str[l] = '\0'; +- printk( KERN_WARNING "%s\n", str ); +- } +-} +- +-#endif +- +-struct dvb_net_priv { +- int in_use; +- u16 pid; +- struct net_device *net; +- struct dvb_net *host; +- struct dmx_demux *demux; +- struct dmx_section_feed *secfeed; +- struct dmx_section_filter *secfilter; +- struct dmx_ts_feed *tsfeed; +- int multi_num; +- struct dmx_section_filter *multi_secfilter[DVB_NET_MULTICAST_MAX]; +- unsigned char multi_macs[DVB_NET_MULTICAST_MAX][6]; +- int rx_mode; +-#define RX_MODE_UNI 0 +-#define RX_MODE_MULTI 1 +-#define RX_MODE_ALL_MULTI 2 +-#define RX_MODE_PROMISC 3 +- struct work_struct set_multicast_list_wq; +- struct work_struct restart_net_feed_wq; +- unsigned char feedtype; /* Either FEED_TYPE_ or FEED_TYPE_ULE */ +- int need_pusi; /* Set to 1, if synchronization on PUSI required. */ +- unsigned char tscc; /* TS continuity counter after sync on PUSI. */ +- struct sk_buff *ule_skb; /* ULE SNDU decodes into this buffer. */ +- unsigned char *ule_next_hdr; /* Pointer into skb to next ULE extension header. */ +- unsigned short ule_sndu_len; /* ULE SNDU length in bytes, w/o D-Bit. */ +- unsigned short ule_sndu_type; /* ULE SNDU type field, complete. */ +- unsigned char ule_sndu_type_1; /* ULE SNDU type field, if split across 2 TS cells. */ +- unsigned char ule_dbit; /* Whether the DestMAC address present +- * or not (bit is set). */ +- unsigned char ule_bridged; /* Whether the ULE_BRIDGED extension header was found. */ +- int ule_sndu_remain; /* Nr. of bytes still required for current ULE SNDU. */ +- unsigned long ts_count; /* Current ts cell counter. */ +- struct mutex mutex; +-}; +- +- +-/** +- * Determine the packet's protocol ID. The rule here is that we +- * assume 802.3 if the type field is short enough to be a length. +- * This is normal practice and works for any 'now in use' protocol. +- * +- * stolen from eth.c out of the linux kernel, hacked for dvb-device +- * by Michael Holzt +- */ +-static __be16 dvb_net_eth_type_trans(struct sk_buff *skb, +- struct net_device *dev) +-{ +- struct ethhdr *eth; +- unsigned char *rawp; +- +- skb_reset_mac_header(skb); +- skb_pull(skb,dev->hard_header_len); +- eth = eth_hdr(skb); +- +- if (*eth->h_dest & 1) { +- if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0) +- skb->pkt_type=PACKET_BROADCAST; +- else +- skb->pkt_type=PACKET_MULTICAST; +- } +- +- if (ntohs(eth->h_proto) >= 1536) +- return eth->h_proto; +- +- rawp = skb->data; +- +- /** +- * This is a magic hack to spot IPX packets. Older Novell breaks +- * the protocol design and runs IPX over 802.3 without an 802.2 LLC +- * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This +- * won't work for fault tolerant netware but does for the rest. +- */ +- if (*(unsigned short *)rawp == 0xFFFF) +- return htons(ETH_P_802_3); +- +- /** +- * Real 802.2 LLC +- */ +- return htons(ETH_P_802_2); +-} +- +-#define TS_SZ 188 +-#define TS_SYNC 0x47 +-#define TS_TEI 0x80 +-#define TS_SC 0xC0 +-#define TS_PUSI 0x40 +-#define TS_AF_A 0x20 +-#define TS_AF_D 0x10 +- +-/* ULE Extension Header handlers. */ +- +-#define ULE_TEST 0 +-#define ULE_BRIDGED 1 +- +-#define ULE_OPTEXTHDR_PADDING 0 +- +-static int ule_test_sndu( struct dvb_net_priv *p ) +-{ +- return -1; +-} +- +-static int ule_bridged_sndu( struct dvb_net_priv *p ) +-{ +- struct ethhdr *hdr = (struct ethhdr*) p->ule_next_hdr; +- if(ntohs(hdr->h_proto) < 1536) { +- int framelen = p->ule_sndu_len - ((p->ule_next_hdr+sizeof(struct ethhdr)) - p->ule_skb->data); +- /* A frame Type < 1536 for a bridged frame, introduces a LLC Length field. */ +- if(framelen != ntohs(hdr->h_proto)) { +- return -1; +- } +- } +- /* Note: +- * From RFC4326: +- * "A bridged SNDU is a Mandatory Extension Header of Type 1. +- * It must be the final (or only) extension header specified in the header chain of a SNDU." +- * The 'ule_bridged' flag will cause the extension header processing loop to terminate. +- */ +- p->ule_bridged = 1; +- return 0; +-} +- +-static int ule_exthdr_padding(struct dvb_net_priv *p) +-{ +- return 0; +-} +- +-/** Handle ULE extension headers. +- * Function is called after a successful CRC32 verification of an ULE SNDU to complete its decoding. +- * Returns: >= 0: nr. of bytes consumed by next extension header +- * -1: Mandatory extension header that is not recognized or TEST SNDU; discard. +- */ +-static int handle_one_ule_extension( struct dvb_net_priv *p ) +-{ +- /* Table of mandatory extension header handlers. The header type is the index. */ +- static int (*ule_mandatory_ext_handlers[255])( struct dvb_net_priv *p ) = +- { [0] = ule_test_sndu, [1] = ule_bridged_sndu, [2] = NULL, }; +- +- /* Table of optional extension header handlers. The header type is the index. */ +- static int (*ule_optional_ext_handlers[255])( struct dvb_net_priv *p ) = +- { [0] = ule_exthdr_padding, [1] = NULL, }; +- +- int ext_len = 0; +- unsigned char hlen = (p->ule_sndu_type & 0x0700) >> 8; +- unsigned char htype = p->ule_sndu_type & 0x00FF; +- +- /* Discriminate mandatory and optional extension headers. */ +- if (hlen == 0) { +- /* Mandatory extension header */ +- if (ule_mandatory_ext_handlers[htype]) { +- ext_len = ule_mandatory_ext_handlers[htype]( p ); +- if(ext_len >= 0) { +- p->ule_next_hdr += ext_len; +- if (!p->ule_bridged) { +- p->ule_sndu_type = ntohs(*(__be16 *)p->ule_next_hdr); +- p->ule_next_hdr += 2; +- } else { +- p->ule_sndu_type = ntohs(*(__be16 *)(p->ule_next_hdr + ((p->ule_dbit ? 2 : 3) * ETH_ALEN))); +- /* This assures the extension handling loop will terminate. */ +- } +- } +- // else: extension handler failed or SNDU should be discarded +- } else +- ext_len = -1; /* SNDU has to be discarded. */ +- } else { +- /* Optional extension header. Calculate the length. */ +- ext_len = hlen << 1; +- /* Process the optional extension header according to its type. */ +- if (ule_optional_ext_handlers[htype]) +- (void)ule_optional_ext_handlers[htype]( p ); +- p->ule_next_hdr += ext_len; +- p->ule_sndu_type = ntohs( *(__be16 *)(p->ule_next_hdr-2) ); +- /* +- * note: the length of the next header type is included in the +- * length of THIS optional extension header +- */ +- } +- +- return ext_len; +-} +- +-static int handle_ule_extensions( struct dvb_net_priv *p ) +-{ +- int total_ext_len = 0, l; +- +- p->ule_next_hdr = p->ule_skb->data; +- do { +- l = handle_one_ule_extension( p ); +- if (l < 0) +- return l; /* Stop extension header processing and discard SNDU. */ +- total_ext_len += l; +-#ifdef ULE_DEBUG +- dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, " +- "l=%i, total_ext_len=%i\n", p->ule_next_hdr, +- (int) p->ule_sndu_type, l, total_ext_len); +-#endif +- +- } while (p->ule_sndu_type < 1536); +- +- return total_ext_len; +-} +- +- +-/** Prepare for a new ULE SNDU: reset the decoder state. */ +-static inline void reset_ule( struct dvb_net_priv *p ) +-{ +- p->ule_skb = NULL; +- p->ule_next_hdr = NULL; +- p->ule_sndu_len = 0; +- p->ule_sndu_type = 0; +- p->ule_sndu_type_1 = 0; +- p->ule_sndu_remain = 0; +- p->ule_dbit = 0xFF; +- p->ule_bridged = 0; +-} +- +-/** +- * Decode ULE SNDUs according to draft-ietf-ipdvb-ule-03.txt from a sequence of +- * TS cells of a single PID. +- */ +-static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- unsigned long skipped = 0L; +- const u8 *ts, *ts_end, *from_where = NULL; +- u8 ts_remain = 0, how_much = 0, new_ts = 1; +- struct ethhdr *ethh = NULL; +- bool error = false; +- +-#ifdef ULE_DEBUG +- /* The code inside ULE_DEBUG keeps a history of the last 100 TS cells processed. */ +- static unsigned char ule_hist[100*TS_SZ]; +- static unsigned char *ule_where = ule_hist, ule_dump; +-#endif +- +- /* For all TS cells in current buffer. +- * Appearently, we are called for every single TS cell. +- */ +- for (ts = buf, ts_end = buf + buf_len; ts < ts_end; /* no default incr. */ ) { +- +- if (new_ts) { +- /* We are about to process a new TS cell. */ +- +-#ifdef ULE_DEBUG +- if (ule_where >= &ule_hist[100*TS_SZ]) ule_where = ule_hist; +- memcpy( ule_where, ts, TS_SZ ); +- if (ule_dump) { +- hexdump( ule_where, TS_SZ ); +- ule_dump = 0; +- } +- ule_where += TS_SZ; +-#endif +- +- /* Check TS error conditions: sync_byte, transport_error_indicator, scrambling_control . */ +- if ((ts[0] != TS_SYNC) || (ts[1] & TS_TEI) || ((ts[3] & TS_SC) != 0)) { +- printk(KERN_WARNING "%lu: Invalid TS cell: SYNC %#x, TEI %u, SC %#x.\n", +- priv->ts_count, ts[0], ts[1] & TS_TEI >> 7, ts[3] & 0xC0 >> 6); +- +- /* Drop partly decoded SNDU, reset state, resync on PUSI. */ +- if (priv->ule_skb) { +- dev_kfree_skb( priv->ule_skb ); +- /* Prepare for next SNDU. */ +- dev->stats.rx_errors++; +- dev->stats.rx_frame_errors++; +- } +- reset_ule(priv); +- priv->need_pusi = 1; +- +- /* Continue with next TS cell. */ +- ts += TS_SZ; +- priv->ts_count++; +- continue; +- } +- +- ts_remain = 184; +- from_where = ts + 4; +- } +- /* Synchronize on PUSI, if required. */ +- if (priv->need_pusi) { +- if (ts[1] & TS_PUSI) { +- /* Find beginning of first ULE SNDU in current TS cell. */ +- /* Synchronize continuity counter. */ +- priv->tscc = ts[3] & 0x0F; +- /* There is a pointer field here. */ +- if (ts[4] > ts_remain) { +- printk(KERN_ERR "%lu: Invalid ULE packet " +- "(pointer field %d)\n", priv->ts_count, ts[4]); +- ts += TS_SZ; +- priv->ts_count++; +- continue; +- } +- /* Skip to destination of pointer field. */ +- from_where = &ts[5] + ts[4]; +- ts_remain -= 1 + ts[4]; +- skipped = 0; +- } else { +- skipped++; +- ts += TS_SZ; +- priv->ts_count++; +- continue; +- } +- } +- +- if (new_ts) { +- /* Check continuity counter. */ +- if ((ts[3] & 0x0F) == priv->tscc) +- priv->tscc = (priv->tscc + 1) & 0x0F; +- else { +- /* TS discontinuity handling: */ +- printk(KERN_WARNING "%lu: TS discontinuity: got %#x, " +- "expected %#x.\n", priv->ts_count, ts[3] & 0x0F, priv->tscc); +- /* Drop partly decoded SNDU, reset state, resync on PUSI. */ +- if (priv->ule_skb) { +- dev_kfree_skb( priv->ule_skb ); +- /* Prepare for next SNDU. */ +- // reset_ule(priv); moved to below. +- dev->stats.rx_errors++; +- dev->stats.rx_frame_errors++; +- } +- reset_ule(priv); +- /* skip to next PUSI. */ +- priv->need_pusi = 1; +- continue; +- } +- /* If we still have an incomplete payload, but PUSI is +- * set; some TS cells are missing. +- * This is only possible here, if we missed exactly 16 TS +- * cells (continuity counter wrap). */ +- if (ts[1] & TS_PUSI) { +- if (! priv->need_pusi) { +- if (!(*from_where < (ts_remain-1)) || *from_where != priv->ule_sndu_remain) { +- /* Pointer field is invalid. Drop this TS cell and any started ULE SNDU. */ +- printk(KERN_WARNING "%lu: Invalid pointer " +- "field: %u.\n", priv->ts_count, *from_where); +- +- /* Drop partly decoded SNDU, reset state, resync on PUSI. */ +- if (priv->ule_skb) { +- error = true; +- dev_kfree_skb(priv->ule_skb); +- } +- +- if (error || priv->ule_sndu_remain) { +- dev->stats.rx_errors++; +- dev->stats.rx_frame_errors++; +- error = false; +- } +- +- reset_ule(priv); +- priv->need_pusi = 1; +- continue; +- } +- /* Skip pointer field (we're processing a +- * packed payload). */ +- from_where += 1; +- ts_remain -= 1; +- } else +- priv->need_pusi = 0; +- +- if (priv->ule_sndu_remain > 183) { +- /* Current SNDU lacks more data than there could be available in the +- * current TS cell. */ +- dev->stats.rx_errors++; +- dev->stats.rx_length_errors++; +- printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but " +- "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n", +- priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain); +- dev_kfree_skb(priv->ule_skb); +- /* Prepare for next SNDU. */ +- reset_ule(priv); +- /* Resync: go to where pointer field points to: start of next ULE SNDU. */ +- from_where += ts[4]; +- ts_remain -= ts[4]; +- } +- } +- } +- +- /* Check if new payload needs to be started. */ +- if (priv->ule_skb == NULL) { +- /* Start a new payload with skb. +- * Find ULE header. It is only guaranteed that the +- * length field (2 bytes) is contained in the current +- * TS. +- * Check ts_remain has to be >= 2 here. */ +- if (ts_remain < 2) { +- printk(KERN_WARNING "Invalid payload packing: only %d " +- "bytes left in TS. Resyncing.\n", ts_remain); +- priv->ule_sndu_len = 0; +- priv->need_pusi = 1; +- ts += TS_SZ; +- continue; +- } +- +- if (! priv->ule_sndu_len) { +- /* Got at least two bytes, thus extrace the SNDU length. */ +- priv->ule_sndu_len = from_where[0] << 8 | from_where[1]; +- if (priv->ule_sndu_len & 0x8000) { +- /* D-Bit is set: no dest mac present. */ +- priv->ule_sndu_len &= 0x7FFF; +- priv->ule_dbit = 1; +- } else +- priv->ule_dbit = 0; +- +- if (priv->ule_sndu_len < 5) { +- printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. " +- "Resyncing.\n", priv->ts_count, priv->ule_sndu_len); +- dev->stats.rx_errors++; +- dev->stats.rx_length_errors++; +- priv->ule_sndu_len = 0; +- priv->need_pusi = 1; +- new_ts = 1; +- ts += TS_SZ; +- priv->ts_count++; +- continue; +- } +- ts_remain -= 2; /* consume the 2 bytes SNDU length. */ +- from_where += 2; +- } +- +- priv->ule_sndu_remain = priv->ule_sndu_len + 2; +- /* +- * State of current TS: +- * ts_remain (remaining bytes in the current TS cell) +- * 0 ule_type is not available now, we need the next TS cell +- * 1 the first byte of the ule_type is present +- * >=2 full ULE header present, maybe some payload data as well. +- */ +- switch (ts_remain) { +- case 1: +- priv->ule_sndu_remain--; +- priv->ule_sndu_type = from_where[0] << 8; +- priv->ule_sndu_type_1 = 1; /* first byte of ule_type is set. */ +- ts_remain -= 1; from_where += 1; +- /* Continue w/ next TS. */ +- case 0: +- new_ts = 1; +- ts += TS_SZ; +- priv->ts_count++; +- continue; +- +- default: /* complete ULE header is present in current TS. */ +- /* Extract ULE type field. */ +- if (priv->ule_sndu_type_1) { +- priv->ule_sndu_type_1 = 0; +- priv->ule_sndu_type |= from_where[0]; +- from_where += 1; /* points to payload start. */ +- ts_remain -= 1; +- } else { +- /* Complete type is present in new TS. */ +- priv->ule_sndu_type = from_where[0] << 8 | from_where[1]; +- from_where += 2; /* points to payload start. */ +- ts_remain -= 2; +- } +- break; +- } +- +- /* Allocate the skb (decoder target buffer) with the correct size, as follows: +- * prepare for the largest case: bridged SNDU with MAC address (dbit = 0). */ +- priv->ule_skb = dev_alloc_skb( priv->ule_sndu_len + ETH_HLEN + ETH_ALEN ); +- if (priv->ule_skb == NULL) { +- printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", +- dev->name); +- dev->stats.rx_dropped++; +- return; +- } +- +- /* This includes the CRC32 _and_ dest mac, if !dbit. */ +- priv->ule_sndu_remain = priv->ule_sndu_len; +- priv->ule_skb->dev = dev; +- /* Leave space for Ethernet or bridged SNDU header (eth hdr plus one MAC addr). */ +- skb_reserve( priv->ule_skb, ETH_HLEN + ETH_ALEN ); +- } +- +- /* Copy data into our current skb. */ +- how_much = min(priv->ule_sndu_remain, (int)ts_remain); +- memcpy(skb_put(priv->ule_skb, how_much), from_where, how_much); +- priv->ule_sndu_remain -= how_much; +- ts_remain -= how_much; +- from_where += how_much; +- +- /* Check for complete payload. */ +- if (priv->ule_sndu_remain <= 0) { +- /* Check CRC32, we've got it in our skb already. */ +- __be16 ulen = htons(priv->ule_sndu_len); +- __be16 utype = htons(priv->ule_sndu_type); +- const u8 *tail; +- struct kvec iov[3] = { +- { &ulen, sizeof ulen }, +- { &utype, sizeof utype }, +- { priv->ule_skb->data, priv->ule_skb->len - 4 } +- }; +- u32 ule_crc = ~0L, expected_crc; +- if (priv->ule_dbit) { +- /* Set D-bit for CRC32 verification, +- * if it was set originally. */ +- ulen |= htons(0x8000); +- } +- +- ule_crc = iov_crc32(ule_crc, iov, 3); +- tail = skb_tail_pointer(priv->ule_skb); +- expected_crc = *(tail - 4) << 24 | +- *(tail - 3) << 16 | +- *(tail - 2) << 8 | +- *(tail - 1); +- if (ule_crc != expected_crc) { +- printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n", +- priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0); +- +-#ifdef ULE_DEBUG +- hexdump( iov[0].iov_base, iov[0].iov_len ); +- hexdump( iov[1].iov_base, iov[1].iov_len ); +- hexdump( iov[2].iov_base, iov[2].iov_len ); +- +- if (ule_where == ule_hist) { +- hexdump( &ule_hist[98*TS_SZ], TS_SZ ); +- hexdump( &ule_hist[99*TS_SZ], TS_SZ ); +- } else if (ule_where == &ule_hist[TS_SZ]) { +- hexdump( &ule_hist[99*TS_SZ], TS_SZ ); +- hexdump( ule_hist, TS_SZ ); +- } else { +- hexdump( ule_where - TS_SZ - TS_SZ, TS_SZ ); +- hexdump( ule_where - TS_SZ, TS_SZ ); +- } +- ule_dump = 1; +-#endif +- +- dev->stats.rx_errors++; +- dev->stats.rx_crc_errors++; +- dev_kfree_skb(priv->ule_skb); +- } else { +- /* CRC32 verified OK. */ +- u8 dest_addr[ETH_ALEN]; +- static const u8 bc_addr[ETH_ALEN] = +- { [ 0 ... ETH_ALEN-1] = 0xff }; +- +- /* CRC32 was OK. Remove it from skb. */ +- priv->ule_skb->tail -= 4; +- priv->ule_skb->len -= 4; +- +- if (!priv->ule_dbit) { +- /* +- * The destination MAC address is the +- * next data in the skb. It comes +- * before any extension headers. +- * +- * Check if the payload of this SNDU +- * should be passed up the stack. +- */ +- register int drop = 0; +- if (priv->rx_mode != RX_MODE_PROMISC) { +- if (priv->ule_skb->data[0] & 0x01) { +- /* multicast or broadcast */ +- if (memcmp(priv->ule_skb->data, bc_addr, ETH_ALEN)) { +- /* multicast */ +- if (priv->rx_mode == RX_MODE_MULTI) { +- int i; +- for(i = 0; i < priv->multi_num && memcmp(priv->ule_skb->data, priv->multi_macs[i], ETH_ALEN); i++) +- ; +- if (i == priv->multi_num) +- drop = 1; +- } else if (priv->rx_mode != RX_MODE_ALL_MULTI) +- drop = 1; /* no broadcast; */ +- /* else: all multicast mode: accept all multicast packets */ +- } +- /* else: broadcast */ +- } +- else if (memcmp(priv->ule_skb->data, dev->dev_addr, ETH_ALEN)) +- drop = 1; +- /* else: destination address matches the MAC address of our receiver device */ +- } +- /* else: promiscuous mode; pass everything up the stack */ +- +- if (drop) { +-#ifdef ULE_DEBUG +- dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n", +- MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr)); +-#endif +- dev_kfree_skb(priv->ule_skb); +- goto sndu_done; +- } +- else +- { +- skb_copy_from_linear_data(priv->ule_skb, +- dest_addr, +- ETH_ALEN); +- skb_pull(priv->ule_skb, ETH_ALEN); +- } +- } +- +- /* Handle ULE Extension Headers. */ +- if (priv->ule_sndu_type < 1536) { +- /* There is an extension header. Handle it accordingly. */ +- int l = handle_ule_extensions(priv); +- if (l < 0) { +- /* Mandatory extension header unknown or TEST SNDU. Drop it. */ +- // printk( KERN_WARNING "Dropping SNDU, extension headers.\n" ); +- dev_kfree_skb(priv->ule_skb); +- goto sndu_done; +- } +- skb_pull(priv->ule_skb, l); +- } +- +- /* +- * Construct/assure correct ethernet header. +- * Note: in bridged mode (priv->ule_bridged != +- * 0) we already have the (original) ethernet +- * header at the start of the payload (after +- * optional dest. address and any extension +- * headers). +- */ +- +- if (!priv->ule_bridged) { +- skb_push(priv->ule_skb, ETH_HLEN); +- ethh = (struct ethhdr *)priv->ule_skb->data; +- if (!priv->ule_dbit) { +- /* dest_addr buffer is only valid if priv->ule_dbit == 0 */ +- memcpy(ethh->h_dest, dest_addr, ETH_ALEN); +- memset(ethh->h_source, 0, ETH_ALEN); +- } +- else /* zeroize source and dest */ +- memset( ethh, 0, ETH_ALEN*2 ); +- +- ethh->h_proto = htons(priv->ule_sndu_type); +- } +- /* else: skb is in correct state; nothing to do. */ +- priv->ule_bridged = 0; +- +- /* Stuff into kernel's protocol stack. */ +- priv->ule_skb->protocol = dvb_net_eth_type_trans(priv->ule_skb, dev); +- /* If D-bit is set (i.e. destination MAC address not present), +- * receive the packet anyhow. */ +- /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST) +- priv->ule_skb->pkt_type = PACKET_HOST; */ +- dev->stats.rx_packets++; +- dev->stats.rx_bytes += priv->ule_skb->len; +- netif_rx(priv->ule_skb); +- } +- sndu_done: +- /* Prepare for next SNDU. */ +- reset_ule(priv); +- } +- +- /* More data in current TS (look at the bytes following the CRC32)? */ +- if (ts_remain >= 2 && *((unsigned short *)from_where) != 0xFFFF) { +- /* Next ULE SNDU starts right there. */ +- new_ts = 0; +- priv->ule_skb = NULL; +- priv->ule_sndu_type_1 = 0; +- priv->ule_sndu_len = 0; +- // printk(KERN_WARNING "More data in current TS: [%#x %#x %#x %#x]\n", +- // *(from_where + 0), *(from_where + 1), +- // *(from_where + 2), *(from_where + 3)); +- // printk(KERN_WARNING "ts @ %p, stopped @ %p:\n", ts, from_where + 0); +- // hexdump(ts, 188); +- } else { +- new_ts = 1; +- ts += TS_SZ; +- priv->ts_count++; +- if (priv->ule_skb == NULL) { +- priv->need_pusi = 1; +- priv->ule_sndu_type_1 = 0; +- priv->ule_sndu_len = 0; +- } +- } +- } /* for all available TS cells */ +-} +- +-static int dvb_net_ts_callback(const u8 *buffer1, size_t buffer1_len, +- const u8 *buffer2, size_t buffer2_len, +- struct dmx_ts_feed *feed, enum dmx_success success) +-{ +- struct net_device *dev = feed->priv; +- +- if (buffer2) +- printk(KERN_WARNING "buffer2 not NULL: %p.\n", buffer2); +- if (buffer1_len > 32768) +- printk(KERN_WARNING "length > 32k: %zu.\n", buffer1_len); +- /* printk("TS callback: %u bytes, %u TS cells @ %p.\n", +- buffer1_len, buffer1_len / TS_SZ, buffer1); */ +- dvb_net_ule(dev, buffer1, buffer1_len); +- return 0; +-} +- +- +-static void dvb_net_sec(struct net_device *dev, +- const u8 *pkt, int pkt_len) +-{ +- u8 *eth; +- struct sk_buff *skb; +- struct net_device_stats *stats = &dev->stats; +- int snap = 0; +- +- /* note: pkt_len includes a 32bit checksum */ +- if (pkt_len < 16) { +- printk("%s: IP/MPE packet length = %d too small.\n", +- dev->name, pkt_len); +- stats->rx_errors++; +- stats->rx_length_errors++; +- return; +- } +-/* it seems some ISPs manage to screw up here, so we have to +- * relax the error checks... */ +-#if 0 +- if ((pkt[5] & 0xfd) != 0xc1) { +- /* drop scrambled or broken packets */ +-#else +- if ((pkt[5] & 0x3c) != 0x00) { +- /* drop scrambled */ +-#endif +- stats->rx_errors++; +- stats->rx_crc_errors++; +- return; +- } +- if (pkt[5] & 0x02) { +- /* handle LLC/SNAP, see rfc-1042 */ +- if (pkt_len < 24 || memcmp(&pkt[12], "\xaa\xaa\x03\0\0\0", 6)) { +- stats->rx_dropped++; +- return; +- } +- snap = 8; +- } +- if (pkt[7]) { +- /* FIXME: assemble datagram from multiple sections */ +- stats->rx_errors++; +- stats->rx_frame_errors++; +- return; +- } +- +- /* we have 14 byte ethernet header (ip header follows); +- * 12 byte MPE header; 4 byte checksum; + 2 byte alignment, 8 byte LLC/SNAP +- */ +- if (!(skb = dev_alloc_skb(pkt_len - 4 - 12 + 14 + 2 - snap))) { +- //printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", dev->name); +- stats->rx_dropped++; +- return; +- } +- skb_reserve(skb, 2); /* longword align L3 header */ +- skb->dev = dev; +- +- /* copy L3 payload */ +- eth = (u8 *) skb_put(skb, pkt_len - 12 - 4 + 14 - snap); +- memcpy(eth + 14, pkt + 12 + snap, pkt_len - 12 - 4 - snap); +- +- /* create ethernet header: */ +- eth[0]=pkt[0x0b]; +- eth[1]=pkt[0x0a]; +- eth[2]=pkt[0x09]; +- eth[3]=pkt[0x08]; +- eth[4]=pkt[0x04]; +- eth[5]=pkt[0x03]; +- +- eth[6]=eth[7]=eth[8]=eth[9]=eth[10]=eth[11]=0; +- +- if (snap) { +- eth[12] = pkt[18]; +- eth[13] = pkt[19]; +- } else { +- /* protocol numbers are from rfc-1700 or +- * http://www.iana.org/assignments/ethernet-numbers +- */ +- if (pkt[12] >> 4 == 6) { /* version field from IP header */ +- eth[12] = 0x86; /* IPv6 */ +- eth[13] = 0xdd; +- } else { +- eth[12] = 0x08; /* IPv4 */ +- eth[13] = 0x00; +- } +- } +- +- skb->protocol = dvb_net_eth_type_trans(skb, dev); +- +- stats->rx_packets++; +- stats->rx_bytes+=skb->len; +- netif_rx(skb); +-} +- +-static int dvb_net_sec_callback(const u8 *buffer1, size_t buffer1_len, +- const u8 *buffer2, size_t buffer2_len, +- struct dmx_section_filter *filter, +- enum dmx_success success) +-{ +- struct net_device *dev = filter->priv; +- +- /** +- * we rely on the DVB API definition where exactly one complete +- * section is delivered in buffer1 +- */ +- dvb_net_sec (dev, buffer1, buffer1_len); +- return 0; +-} +- +-static int dvb_net_tx(struct sk_buff *skb, struct net_device *dev) +-{ +- dev_kfree_skb(skb); +- return NETDEV_TX_OK; +-} +- +-static u8 mask_normal[6]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +-static u8 mask_allmulti[6]={0xff, 0xff, 0xff, 0x00, 0x00, 0x00}; +-static u8 mac_allmulti[6]={0x01, 0x00, 0x5e, 0x00, 0x00, 0x00}; +-static u8 mask_promisc[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +- +-static int dvb_net_filter_sec_set(struct net_device *dev, +- struct dmx_section_filter **secfilter, +- u8 *mac, u8 *mac_mask) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- int ret; +- +- *secfilter=NULL; +- ret = priv->secfeed->allocate_filter(priv->secfeed, secfilter); +- if (ret<0) { +- printk("%s: could not get filter\n", dev->name); +- return ret; +- } +- +- (*secfilter)->priv=(void *) dev; +- +- memset((*secfilter)->filter_value, 0x00, DMX_MAX_FILTER_SIZE); +- memset((*secfilter)->filter_mask, 0x00, DMX_MAX_FILTER_SIZE); +- memset((*secfilter)->filter_mode, 0xff, DMX_MAX_FILTER_SIZE); +- +- (*secfilter)->filter_value[0]=0x3e; +- (*secfilter)->filter_value[3]=mac[5]; +- (*secfilter)->filter_value[4]=mac[4]; +- (*secfilter)->filter_value[8]=mac[3]; +- (*secfilter)->filter_value[9]=mac[2]; +- (*secfilter)->filter_value[10]=mac[1]; +- (*secfilter)->filter_value[11]=mac[0]; +- +- (*secfilter)->filter_mask[0] = 0xff; +- (*secfilter)->filter_mask[3] = mac_mask[5]; +- (*secfilter)->filter_mask[4] = mac_mask[4]; +- (*secfilter)->filter_mask[8] = mac_mask[3]; +- (*secfilter)->filter_mask[9] = mac_mask[2]; +- (*secfilter)->filter_mask[10] = mac_mask[1]; +- (*secfilter)->filter_mask[11]=mac_mask[0]; +- +- dprintk("%s: filter mac=%pM\n", dev->name, mac); +- dprintk("%s: filter mask=%pM\n", dev->name, mac_mask); +- +- return 0; +-} +- +-static int dvb_net_feed_start(struct net_device *dev) +-{ +- int ret = 0, i; +- struct dvb_net_priv *priv = netdev_priv(dev); +- struct dmx_demux *demux = priv->demux; +- unsigned char *mac = (unsigned char *) dev->dev_addr; +- +- dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode); +- mutex_lock(&priv->mutex); +- if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0]) +- printk("%s: BUG %d\n", __func__, __LINE__); +- +- priv->secfeed=NULL; +- priv->secfilter=NULL; +- priv->tsfeed = NULL; +- +- if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { +- dprintk("%s: alloc secfeed\n", __func__); +- ret=demux->allocate_section_feed(demux, &priv->secfeed, +- dvb_net_sec_callback); +- if (ret<0) { +- printk("%s: could not allocate section feed\n", dev->name); +- goto error; +- } +- +- ret = priv->secfeed->set(priv->secfeed, priv->pid, 32768, 1); +- +- if (ret<0) { +- printk("%s: could not set section feed\n", dev->name); +- priv->demux->release_section_feed(priv->demux, priv->secfeed); +- priv->secfeed=NULL; +- goto error; +- } +- +- if (priv->rx_mode != RX_MODE_PROMISC) { +- dprintk("%s: set secfilter\n", __func__); +- dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal); +- } +- +- switch (priv->rx_mode) { +- case RX_MODE_MULTI: +- for (i = 0; i < priv->multi_num; i++) { +- dprintk("%s: set multi_secfilter[%d]\n", __func__, i); +- dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i], +- priv->multi_macs[i], mask_normal); +- } +- break; +- case RX_MODE_ALL_MULTI: +- priv->multi_num=1; +- dprintk("%s: set multi_secfilter[0]\n", __func__); +- dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0], +- mac_allmulti, mask_allmulti); +- break; +- case RX_MODE_PROMISC: +- priv->multi_num=0; +- dprintk("%s: set secfilter\n", __func__); +- dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc); +- break; +- } +- +- dprintk("%s: start filtering\n", __func__); +- priv->secfeed->start_filtering(priv->secfeed); +- } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { +- struct timespec timeout = { 0, 10000000 }; // 10 msec +- +- /* we have payloads encapsulated in TS */ +- dprintk("%s: alloc tsfeed\n", __func__); +- ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback); +- if (ret < 0) { +- printk("%s: could not allocate ts feed\n", dev->name); +- goto error; +- } +- +- /* Set netdevice pointer for ts decaps callback. */ +- priv->tsfeed->priv = (void *)dev; +- ret = priv->tsfeed->set(priv->tsfeed, +- priv->pid, /* pid */ +- TS_PACKET, /* type */ +- DMX_TS_PES_OTHER, /* pes type */ +- 32768, /* circular buffer size */ +- timeout /* timeout */ +- ); +- +- if (ret < 0) { +- printk("%s: could not set ts feed\n", dev->name); +- priv->demux->release_ts_feed(priv->demux, priv->tsfeed); +- priv->tsfeed = NULL; +- goto error; +- } +- +- dprintk("%s: start filtering\n", __func__); +- priv->tsfeed->start_filtering(priv->tsfeed); +- } else +- ret = -EINVAL; +- +-error: +- mutex_unlock(&priv->mutex); +- return ret; +-} +- +-static int dvb_net_feed_stop(struct net_device *dev) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- int i, ret = 0; +- +- dprintk("%s\n", __func__); +- mutex_lock(&priv->mutex); +- if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { +- if (priv->secfeed) { +- if (priv->secfeed->is_filtering) { +- dprintk("%s: stop secfeed\n", __func__); +- priv->secfeed->stop_filtering(priv->secfeed); +- } +- +- if (priv->secfilter) { +- dprintk("%s: release secfilter\n", __func__); +- priv->secfeed->release_filter(priv->secfeed, +- priv->secfilter); +- priv->secfilter=NULL; +- } +- +- for (i=0; imulti_num; i++) { +- if (priv->multi_secfilter[i]) { +- dprintk("%s: release multi_filter[%d]\n", +- __func__, i); +- priv->secfeed->release_filter(priv->secfeed, +- priv->multi_secfilter[i]); +- priv->multi_secfilter[i] = NULL; +- } +- } +- +- priv->demux->release_section_feed(priv->demux, priv->secfeed); +- priv->secfeed = NULL; +- } else +- printk("%s: no feed to stop\n", dev->name); +- } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { +- if (priv->tsfeed) { +- if (priv->tsfeed->is_filtering) { +- dprintk("%s: stop tsfeed\n", __func__); +- priv->tsfeed->stop_filtering(priv->tsfeed); +- } +- priv->demux->release_ts_feed(priv->demux, priv->tsfeed); +- priv->tsfeed = NULL; +- } +- else +- printk("%s: no ts feed to stop\n", dev->name); +- } else +- ret = -EINVAL; +- mutex_unlock(&priv->mutex); +- return ret; +-} +- +- +-static int dvb_set_mc_filter(struct net_device *dev, unsigned char *addr) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- +- if (priv->multi_num == DVB_NET_MULTICAST_MAX) +- return -ENOMEM; +- +- memcpy(priv->multi_macs[priv->multi_num], addr, ETH_ALEN); +- +- priv->multi_num++; +- return 0; +-} +- +- +-static void wq_set_multicast_list (struct work_struct *work) +-{ +- struct dvb_net_priv *priv = +- container_of(work, struct dvb_net_priv, set_multicast_list_wq); +- struct net_device *dev = priv->net; +- +- dvb_net_feed_stop(dev); +- priv->rx_mode = RX_MODE_UNI; +- netif_addr_lock_bh(dev); +- +- if (dev->flags & IFF_PROMISC) { +- dprintk("%s: promiscuous mode\n", dev->name); +- priv->rx_mode = RX_MODE_PROMISC; +- } else if ((dev->flags & IFF_ALLMULTI)) { +- dprintk("%s: allmulti mode\n", dev->name); +- priv->rx_mode = RX_MODE_ALL_MULTI; +- } else if (!netdev_mc_empty(dev)) { +- struct netdev_hw_addr *ha; +- +- dprintk("%s: set_mc_list, %d entries\n", +- dev->name, netdev_mc_count(dev)); +- +- priv->rx_mode = RX_MODE_MULTI; +- priv->multi_num = 0; +- +- netdev_for_each_mc_addr(ha, dev) +- dvb_set_mc_filter(dev, ha->addr); +- } +- +- netif_addr_unlock_bh(dev); +- dvb_net_feed_start(dev); +-} +- +- +-static void dvb_net_set_multicast_list (struct net_device *dev) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- schedule_work(&priv->set_multicast_list_wq); +-} +- +- +-static void wq_restart_net_feed (struct work_struct *work) +-{ +- struct dvb_net_priv *priv = +- container_of(work, struct dvb_net_priv, restart_net_feed_wq); +- struct net_device *dev = priv->net; +- +- if (netif_running(dev)) { +- dvb_net_feed_stop(dev); +- dvb_net_feed_start(dev); +- } +-} +- +- +-static int dvb_net_set_mac (struct net_device *dev, void *p) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- struct sockaddr *addr=p; +- +- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); +- +- if (netif_running(dev)) +- schedule_work(&priv->restart_net_feed_wq); +- +- return 0; +-} +- +- +-static int dvb_net_open(struct net_device *dev) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- +- priv->in_use++; +- dvb_net_feed_start(dev); +- return 0; +-} +- +- +-static int dvb_net_stop(struct net_device *dev) +-{ +- struct dvb_net_priv *priv = netdev_priv(dev); +- +- priv->in_use--; +- return dvb_net_feed_stop(dev); +-} +- +-static const struct header_ops dvb_header_ops = { +- .create = eth_header, +- .parse = eth_header_parse, +- .rebuild = eth_rebuild_header, +-}; +- +- +-static const struct net_device_ops dvb_netdev_ops = { +- .ndo_open = dvb_net_open, +- .ndo_stop = dvb_net_stop, +- .ndo_start_xmit = dvb_net_tx, +- .ndo_set_rx_mode = dvb_net_set_multicast_list, +- .ndo_set_mac_address = dvb_net_set_mac, +- .ndo_change_mtu = eth_change_mtu, +- .ndo_validate_addr = eth_validate_addr, +-}; +- +-static void dvb_net_setup(struct net_device *dev) +-{ +- ether_setup(dev); +- +- dev->header_ops = &dvb_header_ops; +- dev->netdev_ops = &dvb_netdev_ops; +- dev->mtu = 4096; +- +- dev->flags |= IFF_NOARP; +-} +- +-static int get_if(struct dvb_net *dvbnet) +-{ +- int i; +- +- for (i=0; istate[i]) +- break; +- +- if (i == DVB_NET_DEVICES_MAX) +- return -1; +- +- dvbnet->state[i]=1; +- return i; +-} +- +-static int dvb_net_add_if(struct dvb_net *dvbnet, u16 pid, u8 feedtype) +-{ +- struct net_device *net; +- struct dvb_net_priv *priv; +- int result; +- int if_num; +- +- if (feedtype != DVB_NET_FEEDTYPE_MPE && feedtype != DVB_NET_FEEDTYPE_ULE) +- return -EINVAL; +- if ((if_num = get_if(dvbnet)) < 0) +- return -EINVAL; +- +- net = alloc_netdev(sizeof(struct dvb_net_priv), "dvb", dvb_net_setup); +- if (!net) +- return -ENOMEM; +- +- if (dvbnet->dvbdev->id) +- snprintf(net->name, IFNAMSIZ, "dvb%d%u%d", +- dvbnet->dvbdev->adapter->num, dvbnet->dvbdev->id, if_num); +- else +- /* compatibility fix to keep dvb0_0 format */ +- snprintf(net->name, IFNAMSIZ, "dvb%d_%d", +- dvbnet->dvbdev->adapter->num, if_num); +- +- net->addr_len = 6; +- memcpy(net->dev_addr, dvbnet->dvbdev->adapter->proposed_mac, 6); +- +- dvbnet->device[if_num] = net; +- +- priv = netdev_priv(net); +- priv->net = net; +- priv->demux = dvbnet->demux; +- priv->pid = pid; +- priv->rx_mode = RX_MODE_UNI; +- priv->need_pusi = 1; +- priv->tscc = 0; +- priv->feedtype = feedtype; +- reset_ule(priv); +- +- INIT_WORK(&priv->set_multicast_list_wq, wq_set_multicast_list); +- INIT_WORK(&priv->restart_net_feed_wq, wq_restart_net_feed); +- mutex_init(&priv->mutex); +- +- net->base_addr = pid; +- +- if ((result = register_netdev(net)) < 0) { +- dvbnet->device[if_num] = NULL; +- free_netdev(net); +- return result; +- } +- printk("dvb_net: created network interface %s\n", net->name); +- +- return if_num; +-} +- +-static int dvb_net_remove_if(struct dvb_net *dvbnet, unsigned long num) +-{ +- struct net_device *net = dvbnet->device[num]; +- struct dvb_net_priv *priv; +- +- if (!dvbnet->state[num]) +- return -EINVAL; +- priv = netdev_priv(net); +- if (priv->in_use) +- return -EBUSY; +- +- dvb_net_stop(net); +- flush_work_sync(&priv->set_multicast_list_wq); +- flush_work_sync(&priv->restart_net_feed_wq); +- printk("dvb_net: removed network interface %s\n", net->name); +- unregister_netdev(net); +- dvbnet->state[num]=0; +- dvbnet->device[num] = NULL; +- free_netdev(net); +- +- return 0; +-} +- +-static int dvb_net_do_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_net *dvbnet = dvbdev->priv; +- +- if (((file->f_flags&O_ACCMODE)==O_RDONLY)) +- return -EPERM; +- +- switch (cmd) { +- case NET_ADD_IF: +- { +- struct dvb_net_if *dvbnetif = parg; +- int result; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- +- if (!try_module_get(dvbdev->adapter->module)) +- return -EPERM; +- +- result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); +- if (result<0) { +- module_put(dvbdev->adapter->module); +- return result; +- } +- dvbnetif->if_num=result; +- break; +- } +- case NET_GET_IF: +- { +- struct net_device *netdev; +- struct dvb_net_priv *priv_data; +- struct dvb_net_if *dvbnetif = parg; +- +- if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || +- !dvbnet->state[dvbnetif->if_num]) +- return -EINVAL; +- +- netdev = dvbnet->device[dvbnetif->if_num]; +- +- priv_data = netdev_priv(netdev); +- dvbnetif->pid=priv_data->pid; +- dvbnetif->feedtype=priv_data->feedtype; +- break; +- } +- case NET_REMOVE_IF: +- { +- int ret; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) +- return -EINVAL; +- ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); +- if (!ret) +- module_put(dvbdev->adapter->module); +- return ret; +- } +- +- /* binary compatibility cruft */ +- case __NET_ADD_IF_OLD: +- { +- struct __dvb_net_if_old *dvbnetif = parg; +- int result; +- +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- +- if (!try_module_get(dvbdev->adapter->module)) +- return -EPERM; +- +- result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); +- if (result<0) { +- module_put(dvbdev->adapter->module); +- return result; +- } +- dvbnetif->if_num=result; +- break; +- } +- case __NET_GET_IF_OLD: +- { +- struct net_device *netdev; +- struct dvb_net_priv *priv_data; +- struct __dvb_net_if_old *dvbnetif = parg; +- +- if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || +- !dvbnet->state[dvbnetif->if_num]) +- return -EINVAL; +- +- netdev = dvbnet->device[dvbnetif->if_num]; +- +- priv_data = netdev_priv(netdev); +- dvbnetif->pid=priv_data->pid; +- break; +- } +- default: +- return -ENOTTY; +- } +- return 0; +-} +- +-static long dvb_net_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl); +-} +- +-static int dvb_net_close(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct dvb_net *dvbnet = dvbdev->priv; +- +- dvb_generic_release(inode, file); +- +- if(dvbdev->users == 1 && dvbnet->exit == 1) { +- fops_put(file->f_op); +- file->f_op = NULL; +- wake_up(&dvbdev->wait_queue); +- } +- return 0; +-} +- +- +-static const struct file_operations dvb_net_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = dvb_net_ioctl, +- .open = dvb_generic_open, +- .release = dvb_net_close, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_net = { +- .priv = NULL, +- .users = 1, +- .writers = 1, +- .fops = &dvb_net_fops, +-}; +- +- +-void dvb_net_release (struct dvb_net *dvbnet) +-{ +- int i; +- +- dvbnet->exit = 1; +- if (dvbnet->dvbdev->users < 1) +- wait_event(dvbnet->dvbdev->wait_queue, +- dvbnet->dvbdev->users==1); +- +- dvb_unregister_device(dvbnet->dvbdev); +- +- for (i=0; istate[i]) +- continue; +- dvb_net_remove_if(dvbnet, i); +- } +-} +-EXPORT_SYMBOL(dvb_net_release); +- +- +-int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, +- struct dmx_demux *dmx) +-{ +- int i; +- +- dvbnet->demux = dmx; +- +- for (i=0; istate[i] = 0; +- +- return dvb_register_device(adap, &dvbnet->dvbdev, &dvbdev_net, +- dvbnet, DVB_DEVICE_NET); +-} +-EXPORT_SYMBOL(dvb_net_init); +diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h +deleted file mode 100644 +index 1e53acd..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_net.h ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* +- * dvb_net.h +- * +- * Copyright (C) 2001 Ralph Metzler for convergence integrated media GmbH +- * +- * 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. +- * +- */ +- +-#ifndef _DVB_NET_H_ +-#define _DVB_NET_H_ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvbdev.h" +- +-#define DVB_NET_DEVICES_MAX 10 +- +-#ifdef CONFIG_DVB_NET +- +-struct dvb_net { +- struct dvb_device *dvbdev; +- struct net_device *device[DVB_NET_DEVICES_MAX]; +- int state[DVB_NET_DEVICES_MAX]; +- unsigned int exit:1; +- struct dmx_demux *demux; +-}; +- +-void dvb_net_release(struct dvb_net *); +-int dvb_net_init(struct dvb_adapter *, struct dvb_net *, struct dmx_demux *); +- +-#else +- +-struct dvb_net { +- struct dvb_device *dvbdev; +-}; +- +-static inline void dvb_net_release(struct dvb_net *dvbnet) +-{ +-} +- +-static inline int dvb_net_init(struct dvb_adapter *adap, +- struct dvb_net *dvbnet, struct dmx_demux *dmx) +-{ +- return 0; +-} +- +-#endif /* ifdef CONFIG_DVB_NET */ +- +-#endif +diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c +deleted file mode 100755 +index 9f744c2..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c ++++ /dev/null +@@ -1,300 +0,0 @@ +-/* +- * +- * dvb_ringbuffer.c: ring buffer implementation for the dvb driver +- * +- * Copyright (C) 2003 Oliver Endriss +- * Copyright (C) 2004 Andrew de Quincey +- * +- * based on code originally found in av7110.c & dvb_ci.c: +- * Copyright (C) 1999-2003 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * 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 Lesser 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 +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_ringbuffer.h" +- +-#define PKT_READY 0 +-#define PKT_DISPOSED 1 +- +- +-void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len) +-{ +- rbuf->pread=rbuf->pwrite=0; +- rbuf->data=data; +- rbuf->size=len; +- rbuf->error=0; +- rbuf->do_wait=1; +- +- init_waitqueue_head(&rbuf->queue); +- +- spin_lock_init(&(rbuf->lock)); +-} +- +- +- +-int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf) +-{ +- return (rbuf->pread==rbuf->pwrite); +-} +- +- +- +-ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf) +-{ +- ssize_t free; +- +- free = rbuf->pread - rbuf->pwrite; +- if (free <= 0) +- free += rbuf->size; +- return free-1; +-} +- +- +- +-ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf) +-{ +- ssize_t avail; +- +- avail = rbuf->pwrite - rbuf->pread; +- if (avail < 0) +- avail += rbuf->size; +- return avail; +-} +- +- +- +-void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) +-{ +- rbuf->pread = rbuf->pwrite; +- rbuf->error = 0; +-} +-EXPORT_SYMBOL(dvb_ringbuffer_flush); +- +-void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) +-{ +- rbuf->pread = rbuf->pwrite = 0; +- rbuf->error = 0; +-} +- +-void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&rbuf->lock, flags); +- dvb_ringbuffer_flush(rbuf); +- spin_unlock_irqrestore(&rbuf->lock, flags); +- +- wake_up(&rbuf->queue); +-} +- +-ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, u8 __user *buf, size_t len) +-{ +- size_t todo = len; +- size_t split; +- +- split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; +- if (split > 0) { +- if (copy_to_user(buf, rbuf->data+rbuf->pread, split)) +- return -EFAULT; +- buf += split; +- todo -= split; +- rbuf->pread = 0; +- } +- if (copy_to_user(buf, rbuf->data+rbuf->pread, todo)) +- return -EFAULT; +- +- rbuf->pread = (rbuf->pread + todo) % rbuf->size; +- +- return len; +-} +- +-void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len) +-{ +- size_t todo = len; +- size_t split; +- +- split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0; +- if (split > 0) { +- memcpy(buf, rbuf->data+rbuf->pread, split); +- buf += split; +- todo -= split; +- rbuf->pread = 0; +- } +- memcpy(buf, rbuf->data+rbuf->pread, todo); +- +- rbuf->pread = (rbuf->pread + todo) % rbuf->size; +-} +- +- +-ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, size_t len) +-{ +- size_t todo = len; +- size_t split; +- +- split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0; +- +- if (split > 0) { +- memcpy(rbuf->data+rbuf->pwrite, buf, split); +- buf += split; +- todo -= split; +- rbuf->pwrite = 0; +- } +- memcpy(rbuf->data+rbuf->pwrite, buf, todo); +- rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size; +- +- return len; +-} +- +-ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len) +-{ +- int status; +- ssize_t oldpwrite = rbuf->pwrite; +- +- DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8); +- DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff); +- DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY); +- status = dvb_ringbuffer_write(rbuf, buf, len); +- +- if (status < 0) rbuf->pwrite = oldpwrite; +- return status; +-} +- +-ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, +- int offset, u8 __user *buf, size_t len) +-{ +- size_t todo; +- size_t split; +- size_t pktlen; +- +- pktlen = rbuf->data[idx] << 8; +- pktlen |= rbuf->data[(idx + 1) % rbuf->size]; +- if (offset > pktlen) return -EINVAL; +- if ((offset + len) > pktlen) len = pktlen - offset; +- +- idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; +- todo = len; +- split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; +- if (split > 0) { +- if (copy_to_user(buf, rbuf->data+idx, split)) +- return -EFAULT; +- buf += split; +- todo -= split; +- idx = 0; +- } +- if (copy_to_user(buf, rbuf->data+idx, todo)) +- return -EFAULT; +- +- return len; +-} +- +-ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, +- int offset, u8* buf, size_t len) +-{ +- size_t todo; +- size_t split; +- size_t pktlen; +- +- pktlen = rbuf->data[idx] << 8; +- pktlen |= rbuf->data[(idx + 1) % rbuf->size]; +- if (offset > pktlen) return -EINVAL; +- if ((offset + len) > pktlen) len = pktlen - offset; +- +- idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size; +- todo = len; +- split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0; +- if (split > 0) { +- memcpy(buf, rbuf->data+idx, split); +- buf += split; +- todo -= split; +- idx = 0; +- } +- memcpy(buf, rbuf->data+idx, todo); +- return len; +-} +- +-void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx) +-{ +- size_t pktlen; +- +- rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED; +- +- // clean up disposed packets +- while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) { +- if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) { +- pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8; +- pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1); +- DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE); +- } else { +- // first packet is not disposed, so we stop cleaning now +- break; +- } +- } +-} +- +-ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen) +-{ +- int consumed; +- int curpktlen; +- int curpktstatus; +- +- if (idx == -1) { +- idx = rbuf->pread; +- } else { +- curpktlen = rbuf->data[idx] << 8; +- curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; +- idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; +- } +- +- consumed = (idx - rbuf->pread) % rbuf->size; +- +- while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) { +- +- curpktlen = rbuf->data[idx] << 8; +- curpktlen |= rbuf->data[(idx + 1) % rbuf->size]; +- curpktstatus = rbuf->data[(idx + 2) % rbuf->size]; +- +- if (curpktstatus == PKT_READY) { +- *pktlen = curpktlen; +- return idx; +- } +- +- consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE; +- idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size; +- } +- +- // no packets available +- return -1; +-} +- +- +- +-EXPORT_SYMBOL(dvb_ringbuffer_init); +-EXPORT_SYMBOL(dvb_ringbuffer_empty); +-EXPORT_SYMBOL(dvb_ringbuffer_free); +-EXPORT_SYMBOL(dvb_ringbuffer_avail); +-EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup); +-EXPORT_SYMBOL(dvb_ringbuffer_read_user); +-EXPORT_SYMBOL(dvb_ringbuffer_read); +-EXPORT_SYMBOL(dvb_ringbuffer_write); +diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/drivers/media/dvb/dvb-core/dvb_ringbuffer.h +deleted file mode 100755 +index 6951dd3..0000000 +--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.h ++++ /dev/null +@@ -1,187 +0,0 @@ +-/* +- * +- * dvb_ringbuffer.h: ring buffer implementation for the dvb driver +- * +- * Copyright (C) 2003 Oliver Endriss +- * Copyright (C) 2004 Andrew de Quincey +- * +- * based on code originally found in av7110.c & dvb_ci.c: +- * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * 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 Lesser 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. +- */ +- +-#ifndef _DVB_RINGBUFFER_H_ +-#define _DVB_RINGBUFFER_H_ +- +-#include +-#include +- +-struct dvb_ringbuffer { +- u8 *data; +- ssize_t size; +- ssize_t pread; +- ssize_t pwrite; +- int error; +- +- wait_queue_head_t queue; +- spinlock_t lock; +- int do_wait; +-}; +- +-#define DVB_RINGBUFFER_PKTHDRSIZE 3 +- +- +-/* +-** Notes: +-** ------ +-** (1) For performance reasons read and write routines don't check buffer sizes +-** and/or number of bytes free/available. This has to be done before these +-** routines are called. For example: +-** +-** *** write bytes *** +-** free = dvb_ringbuffer_free(rbuf); +-** if (free >= buflen) +-** count = dvb_ringbuffer_write(rbuf, buffer, buflen); +-** else +-** ... +-** +-** *** read min. 1000, max. bytes *** +-** avail = dvb_ringbuffer_avail(rbuf); +-** if (avail >= 1000) +-** count = dvb_ringbuffer_read(rbuf, buffer, min(avail, bufsize)); +-** else +-** ... +-** +-** (2) If there is exactly one reader and one writer, there is no need +-** to lock read or write operations. +-** Two or more readers must be locked against each other. +-** Flushing the buffer counts as a read operation. +-** Resetting the buffer counts as a read and write operation. +-** Two or more writers must be locked against each other. +-*/ +- +-/* initialize ring buffer, lock and queue */ +-extern void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len); +- +-/* test whether buffer is empty */ +-extern int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf); +- +-/* return the number of free bytes in the buffer */ +-extern ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf); +- +-/* return the number of bytes waiting in the buffer */ +-extern ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf); +- +- +-/* +-** Reset the read and write pointers to zero and flush the buffer +-** This counts as a read and write operation +-*/ +-extern void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf); +- +- +-/* read routines & macros */ +-/* ---------------------- */ +-/* flush buffer */ +-extern void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf); +- +-/* flush buffer protected by spinlock and wake-up waiting task(s) */ +-extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf); +- +-/* peek at byte in the buffer */ +-#define DVB_RINGBUFFER_PEEK(rbuf,offs) \ +- (rbuf)->data[((rbuf)->pread+(offs))%(rbuf)->size] +- +-/* advance read ptr by bytes */ +-#define DVB_RINGBUFFER_SKIP(rbuf,num) \ +- (rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size +- +-/* +-** read bytes from ring buffer into +-** specifies whether resides in user space +-** returns number of bytes transferred or -EFAULT +-*/ +-extern ssize_t dvb_ringbuffer_read_user(struct dvb_ringbuffer *rbuf, +- u8 __user *buf, size_t len); +-extern void dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, +- u8 *buf, size_t len); +- +- +-/* write routines & macros */ +-/* ----------------------- */ +-/* write single byte to ring buffer */ +-#define DVB_RINGBUFFER_WRITE_BYTE(rbuf,byte) \ +- { (rbuf)->data[(rbuf)->pwrite]=(byte); \ +- (rbuf)->pwrite=((rbuf)->pwrite+1)%(rbuf)->size; } +-/* +-** write bytes to ring buffer +-** specifies whether resides in user space +-** returns number of bytes transferred or -EFAULT +-*/ +-extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf, +- size_t len); +- +- +-/** +- * Write a packet into the ringbuffer. +- * +- * Ringbuffer to write to. +- * Buffer to write. +- * Length of buffer (currently limited to 65535 bytes max). +- * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL. +- */ +-extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, +- size_t len); +- +-/** +- * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this +- * does NOT update the read pointer in the ringbuffer. You must use +- * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required. +- * +- * Ringbuffer concerned. +- * Packet index as returned by dvb_ringbuffer_pkt_next(). +- * Offset into packet to read from. +- * Destination buffer for data. +- * Size of destination buffer. +- * Set to 1 if is in userspace. +- * returns Number of bytes read, or -EFAULT. +- */ +-extern ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx, +- int offset, u8 __user *buf, size_t len); +-extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx, +- int offset, u8 *buf, size_t len); +- +-/** +- * Dispose of a packet in the ring buffer. +- * +- * Ring buffer concerned. +- * Packet index as returned by dvb_ringbuffer_pkt_next(). +- */ +-extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx); +- +-/** +- * Get the index of the next packet in a ringbuffer. +- * +- * Ringbuffer concerned. +- * Previous packet index, or -1 to return the first packet index. +- * On success, will be updated to contain the length of the packet in bytes. +- * returns Packet index (if >=0), or -1 if no packets available. +- */ +-extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen); +- +- +-#endif /* _DVB_RINGBUFFER_H_ */ +diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c +deleted file mode 100644 +index 00a6732..0000000 +--- a/drivers/media/dvb/dvb-core/dvbdev.c ++++ /dev/null +@@ -1,506 +0,0 @@ +-/* +- * dvbdev.c +- * +- * Copyright (C) 2000 Ralph Metzler +- * & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvbdev.h" +- +-static DEFINE_MUTEX(dvbdev_mutex); +-static int dvbdev_debug; +- +-module_param(dvbdev_debug, int, 0644); +-MODULE_PARM_DESC(dvbdev_debug, "Turn on/off device debugging (default:off)."); +- +-#define dprintk if (dvbdev_debug) printk +- +-static LIST_HEAD(dvb_adapter_list); +-static DEFINE_MUTEX(dvbdev_register_lock); +- +-static const char * const dnames[] = { +- "video", "audio", "sec", "frontend", "demux", "dvr", "ca", +- "net", "osd" +-}; +- +-#ifdef CONFIG_DVB_DYNAMIC_MINORS +-#define MAX_DVB_MINORS 256 +-#define DVB_MAX_IDS MAX_DVB_MINORS +-#else +-#define DVB_MAX_IDS 4 +-#define nums2minor(num,type,id) ((num << 6) | (id << 4) | type) +-#define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64) +-#endif +- +-static struct class *dvb_class; +- +-static struct dvb_device *dvb_minors[MAX_DVB_MINORS]; +-static DECLARE_RWSEM(minor_rwsem); +- +-static int dvb_device_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev; +- +- mutex_lock(&dvbdev_mutex); +- down_read(&minor_rwsem); +- dvbdev = dvb_minors[iminor(inode)]; +- +- if (dvbdev && dvbdev->fops) { +- int err = 0; +- const struct file_operations *old_fops; +- +- file->private_data = dvbdev; +- old_fops = file->f_op; +- file->f_op = fops_get(dvbdev->fops); +- if (file->f_op == NULL) { +- file->f_op = old_fops; +- goto fail; +- } +- if(file->f_op->open) +- err = file->f_op->open(inode,file); +- if (err) { +- fops_put(file->f_op); +- file->f_op = fops_get(old_fops); +- } +- fops_put(old_fops); +- up_read(&minor_rwsem); +- mutex_unlock(&dvbdev_mutex); +- return err; +- } +-fail: +- up_read(&minor_rwsem); +- mutex_unlock(&dvbdev_mutex); +- return -ENODEV; +-} +- +- +-static const struct file_operations dvb_device_fops = +-{ +- .owner = THIS_MODULE, +- .open = dvb_device_open, +- .llseek = noop_llseek, +-}; +- +-static struct cdev dvb_device_cdev; +- +-int dvb_generic_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- +- if (!dvbdev) +- return -ENODEV; +- +- if (!dvbdev->users) +- return -EBUSY; +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- if (!dvbdev->readers) +- return -EBUSY; +- dvbdev->readers--; +- } else { +- if (!dvbdev->writers) +- return -EBUSY; +- dvbdev->writers--; +- } +- +- dvbdev->users--; +- return 0; +-} +-EXPORT_SYMBOL(dvb_generic_open); +- +- +-int dvb_generic_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- +- if (!dvbdev) +- return -ENODEV; +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- dvbdev->readers++; +- } else { +- dvbdev->writers++; +- } +- +- dvbdev->users++; +- return 0; +-} +-EXPORT_SYMBOL(dvb_generic_release); +- +- +-long dvb_generic_ioctl(struct file *file, +- unsigned int cmd, unsigned long arg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- +- if (!dvbdev) +- return -ENODEV; +- +- if (!dvbdev->kernel_ioctl) +- return -EINVAL; +- +- return dvb_usercopy(file, cmd, arg, dvbdev->kernel_ioctl); +-} +-EXPORT_SYMBOL(dvb_generic_ioctl); +- +- +-static int dvbdev_get_free_id (struct dvb_adapter *adap, int type) +-{ +- u32 id = 0; +- +- while (id < DVB_MAX_IDS) { +- struct dvb_device *dev; +- list_for_each_entry(dev, &adap->device_list, list_head) +- if (dev->type == type && dev->id == id) +- goto skip; +- return id; +-skip: +- id++; +- } +- return -ENFILE; +-} +- +- +-int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, +- const struct dvb_device *template, void *priv, int type) +-{ +- struct dvb_device *dvbdev; +- struct file_operations *dvbdevfops; +- struct device *clsdev; +- int minor; +- int id; +- +- mutex_lock(&dvbdev_register_lock); +- +- if ((id = dvbdev_get_free_id (adap, type)) < 0){ +- mutex_unlock(&dvbdev_register_lock); +- *pdvbdev = NULL; +- printk(KERN_ERR "%s: couldn't find free device id\n", __func__); +- return -ENFILE; +- } +- +- *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); +- +- if (!dvbdev){ +- mutex_unlock(&dvbdev_register_lock); +- return -ENOMEM; +- } +- +- dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); +- +- if (!dvbdevfops){ +- kfree (dvbdev); +- mutex_unlock(&dvbdev_register_lock); +- return -ENOMEM; +- } +- +- memcpy(dvbdev, template, sizeof(struct dvb_device)); +- dvbdev->type = type; +- dvbdev->id = id; +- dvbdev->adapter = adap; +- dvbdev->priv = priv; +- dvbdev->fops = dvbdevfops; +- init_waitqueue_head (&dvbdev->wait_queue); +- +- memcpy(dvbdevfops, template->fops, sizeof(struct file_operations)); +- dvbdevfops->owner = adap->module; +- +- list_add_tail (&dvbdev->list_head, &adap->device_list); +- +- down_write(&minor_rwsem); +-#ifdef CONFIG_DVB_DYNAMIC_MINORS +- for (minor = 0; minor < MAX_DVB_MINORS; minor++) +- if (dvb_minors[minor] == NULL) +- break; +- +- if (minor == MAX_DVB_MINORS) { +- kfree(dvbdevfops); +- kfree(dvbdev); +- mutex_unlock(&dvbdev_register_lock); +- return -EINVAL; +- } +-#else +- minor = nums2minor(adap->num, type, id); +-#endif +- +- dvbdev->minor = minor; +- dvb_minors[minor] = dvbdev; +- up_write(&minor_rwsem); +- +- mutex_unlock(&dvbdev_register_lock); +- +- clsdev = device_create(dvb_class, adap->device, +- MKDEV(DVB_MAJOR, minor), +- dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id); +- if (IS_ERR(clsdev)) { +- printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n", +- __func__, adap->num, dnames[type], id, PTR_ERR(clsdev)); +- return PTR_ERR(clsdev); +- } +- +- dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", +- adap->num, dnames[type], id, minor, minor); +- +- return 0; +-} +-EXPORT_SYMBOL(dvb_register_device); +- +- +-void dvb_unregister_device(struct dvb_device *dvbdev) +-{ +- if (!dvbdev) +- return; +- +- down_write(&minor_rwsem); +- dvb_minors[dvbdev->minor] = NULL; +- up_write(&minor_rwsem); +- +- device_destroy(dvb_class, MKDEV(DVB_MAJOR, dvbdev->minor)); +- +- list_del (&dvbdev->list_head); +- kfree (dvbdev->fops); +- kfree (dvbdev); +-} +-EXPORT_SYMBOL(dvb_unregister_device); +- +-static int dvbdev_check_free_adapter_num(int num) +-{ +- struct list_head *entry; +- list_for_each(entry, &dvb_adapter_list) { +- struct dvb_adapter *adap; +- adap = list_entry(entry, struct dvb_adapter, list_head); +- if (adap->num == num) +- return 0; +- } +- return 1; +-} +- +-static int dvbdev_get_free_adapter_num (void) +-{ +- int num = 0; +- +- while (num < DVB_MAX_ADAPTERS) { +- if (dvbdev_check_free_adapter_num(num)) +- return num; +- num++; +- } +- +- return -ENFILE; +-} +- +- +-int dvb_register_adapter(struct dvb_adapter *adap, const char *name, +- struct module *module, struct device *device, +- short *adapter_nums) +-{ +- int i, num; +- +- mutex_lock(&dvbdev_register_lock); +- +- for (i = 0; i < DVB_MAX_ADAPTERS; ++i) { +- num = adapter_nums[i]; +- if (num >= 0 && num < DVB_MAX_ADAPTERS) { +- /* use the one the driver asked for */ +- if (dvbdev_check_free_adapter_num(num)) +- break; +- } else { +- num = dvbdev_get_free_adapter_num(); +- break; +- } +- num = -1; +- } +- +- if (num < 0) { +- mutex_unlock(&dvbdev_register_lock); +- return -ENFILE; +- } +- +- memset (adap, 0, sizeof(struct dvb_adapter)); +- INIT_LIST_HEAD (&adap->device_list); +- +- printk(KERN_INFO "DVB: registering new adapter (%s)\n", name); +- +- adap->num = num; +- adap->name = name; +- adap->module = module; +- adap->device = device; +- adap->mfe_shared = 0; +- adap->mfe_dvbdev = NULL; +- mutex_init (&adap->mfe_lock); +- +- list_add_tail (&adap->list_head, &dvb_adapter_list); +- +- mutex_unlock(&dvbdev_register_lock); +- +- return num; +-} +-EXPORT_SYMBOL(dvb_register_adapter); +- +- +-int dvb_unregister_adapter(struct dvb_adapter *adap) +-{ +- mutex_lock(&dvbdev_register_lock); +- list_del (&adap->list_head); +- mutex_unlock(&dvbdev_register_lock); +- return 0; +-} +-EXPORT_SYMBOL(dvb_unregister_adapter); +- +-/* if the miracle happens and "generic_usercopy()" is included into +- the kernel, then this can vanish. please don't make the mistake and +- define this as video_usercopy(). this will introduce a dependecy +- to the v4l "videodev.o" module, which is unnecessary for some +- cards (ie. the budget dvb-cards don't need the v4l module...) */ +-int dvb_usercopy(struct file *file, +- unsigned int cmd, unsigned long arg, +- int (*func)(struct file *file, +- unsigned int cmd, void *arg)) +-{ +- char sbuf[128]; +- void *mbuf = NULL; +- void *parg = NULL; +- int err = -EINVAL; +- +- /* Copy arguments into temp kernel buffer */ +- switch (_IOC_DIR(cmd)) { +- case _IOC_NONE: +- /* +- * For this command, the pointer is actually an integer +- * argument. +- */ +- parg = (void *) arg; +- break; +- case _IOC_READ: /* some v4l ioctls are marked wrong ... */ +- case _IOC_WRITE: +- case (_IOC_WRITE | _IOC_READ): +- if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { +- parg = sbuf; +- } else { +- /* too big to allocate from stack */ +- mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL); +- if (NULL == mbuf) +- return -ENOMEM; +- parg = mbuf; +- } +- +- err = -EFAULT; +- if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) +- goto out; +- break; +- } +- +- /* call driver */ +- mutex_lock(&dvbdev_mutex); +- if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) +- err = -EINVAL; +- mutex_unlock(&dvbdev_mutex); +- +- if (err < 0) +- goto out; +- +- /* Copy results into user buffer */ +- switch (_IOC_DIR(cmd)) +- { +- case _IOC_READ: +- case (_IOC_WRITE | _IOC_READ): +- if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) +- err = -EFAULT; +- break; +- } +- +-out: +- kfree(mbuf); +- return err; +-} +- +-static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) +-{ +- struct dvb_device *dvbdev = dev_get_drvdata(dev); +- +- add_uevent_var(env, "DVB_ADAPTER_NUM=%d", dvbdev->adapter->num); +- add_uevent_var(env, "DVB_DEVICE_TYPE=%s", dnames[dvbdev->type]); +- add_uevent_var(env, "DVB_DEVICE_NUM=%d", dvbdev->id); +- return 0; +-} +- +-static char *dvb_devnode(struct device *dev, umode_t *mode) +-{ +- struct dvb_device *dvbdev = dev_get_drvdata(dev); +- +- return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d", +- dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id); +-} +- +- +-static int __init init_dvbdev(void) +-{ +- int retval; +- dev_t dev = MKDEV(DVB_MAJOR, 0); +- +- if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) { +- printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR); +- return retval; +- } +- +- cdev_init(&dvb_device_cdev, &dvb_device_fops); +- if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) { +- printk(KERN_ERR "dvb-core: unable register character device\n"); +- goto error; +- } +- +- dvb_class = class_create(THIS_MODULE, "dvb"); +- if (IS_ERR(dvb_class)) { +- retval = PTR_ERR(dvb_class); +- goto error; +- } +- dvb_class->dev_uevent = dvb_uevent; +- dvb_class->devnode = dvb_devnode; +- return 0; +- +-error: +- cdev_del(&dvb_device_cdev); +- unregister_chrdev_region(dev, MAX_DVB_MINORS); +- return retval; +-} +- +- +-static void __exit exit_dvbdev(void) +-{ +- class_destroy(dvb_class); +- cdev_del(&dvb_device_cdev); +- unregister_chrdev_region(MKDEV(DVB_MAJOR, 0), MAX_DVB_MINORS); +-} +- +-subsys_initcall(init_dvbdev); +-module_exit(exit_dvbdev); +- +-MODULE_DESCRIPTION("DVB Core Driver"); +-MODULE_AUTHOR("Marcus Metzler, Ralph Metzler, Holger Waechtler"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h +deleted file mode 100644 +index fcc6ae9..0000000 +--- a/drivers/media/dvb/dvb-core/dvbdev.h ++++ /dev/null +@@ -1,172 +0,0 @@ +-/* +- * dvbdev.h +- * +- * Copyright (C) 2000 Ralph Metzler & Marcus Metzler +- * for convergence integrated media GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Lesser 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. +- * +- */ +- +-#ifndef _DVBDEV_H_ +-#define _DVBDEV_H_ +- +-#include +-#include +-#include +-#include +- +-#define DVB_MAJOR 212 +- +-#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0 +- #define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS +-#else +- #define DVB_MAX_ADAPTERS 8 +-#endif +- +-#define DVB_UNSET (-1) +- +-#define DVB_DEVICE_VIDEO 0 +-#define DVB_DEVICE_AUDIO 1 +-#define DVB_DEVICE_SEC 2 +-#define DVB_DEVICE_FRONTEND 3 +-#define DVB_DEVICE_DEMUX 4 +-#define DVB_DEVICE_DVR 5 +-#define DVB_DEVICE_CA 6 +-#define DVB_DEVICE_NET 7 +-#define DVB_DEVICE_OSD 8 +- +-#define DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr) \ +- static short adapter_nr[] = \ +- {[0 ... (DVB_MAX_ADAPTERS - 1)] = DVB_UNSET }; \ +- module_param_array(adapter_nr, short, NULL, 0444); \ +- MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers") +- +-struct dvb_frontend; +- +-struct dvb_adapter { +- int num; +- struct list_head list_head; +- struct list_head device_list; +- const char *name; +- u8 proposed_mac [6]; +- void* priv; +- +- struct device *device; +- +- struct module *module; +- +- int mfe_shared; /* indicates mutually exclusive frontends */ +- struct dvb_device *mfe_dvbdev; /* frontend device in use */ +- struct mutex mfe_lock; /* access lock for thread creation */ +- +- /* Allow the adapter/bridge driver to perform an action before and/or +- * after the core handles an ioctl: +- * +- * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled. +- * DVB_FE_IOCTL_POST indicates that the ioctl has been handled. +- * +- * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg: +- * +- * return 0 to allow dvb-core to handle the ioctl. +- * return a positive int to prevent dvb-core from handling the ioctl, +- * and exit without error. +- * return a negative int to prevent dvb-core from handling the ioctl, +- * and return that value as an error. +- * +- * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg: +- * +- * return 0 to allow the dvb_frontend ioctl handler to exit normally. +- * return a negative int to cause the dvb_frontend ioctl handler to +- * return that value as an error. +- */ +-#define DVB_FE_IOCTL_PRE 0 +-#define DVB_FE_IOCTL_POST 1 +- int (*fe_ioctl_override)(struct dvb_frontend *fe, +- unsigned int cmd, void *parg, +- unsigned int stage); +-}; +- +- +-struct dvb_device { +- struct list_head list_head; +- const struct file_operations *fops; +- struct dvb_adapter *adapter; +- int type; +- int minor; +- u32 id; +- +- /* in theory, 'users' can vanish now, +- but I don't want to change too much now... */ +- int readers; +- int writers; +- int users; +- +- wait_queue_head_t wait_queue; +- /* don't really need those !? -- FIXME: use video_usercopy */ +- int (*kernel_ioctl)(struct file *file, unsigned int cmd, void *arg); +- +- void *priv; +-}; +- +- +-extern int dvb_register_adapter(struct dvb_adapter *adap, const char *name, +- struct module *module, struct device *device, +- short *adapter_nums); +-extern int dvb_unregister_adapter (struct dvb_adapter *adap); +- +-extern int dvb_register_device (struct dvb_adapter *adap, +- struct dvb_device **pdvbdev, +- const struct dvb_device *template, +- void *priv, +- int type); +- +-extern void dvb_unregister_device (struct dvb_device *dvbdev); +- +-extern int dvb_generic_open (struct inode *inode, struct file *file); +-extern int dvb_generic_release (struct inode *inode, struct file *file); +-extern long dvb_generic_ioctl (struct file *file, +- unsigned int cmd, unsigned long arg); +- +-/* we don't mess with video_usercopy() any more, +-we simply define out own dvb_usercopy(), which will hopefully become +-generic_usercopy() someday... */ +- +-extern int dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg, +- int (*func)(struct file *file, unsigned int cmd, void *arg)); +- +-/** generic DVB attach function. */ +-#ifdef CONFIG_MEDIA_ATTACH +-#define dvb_attach(FUNCTION, ARGS...) ({ \ +- void *__r = NULL; \ +- typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ +- if (__a) { \ +- __r = (void *) __a(ARGS); \ +- if (__r == NULL) \ +- symbol_put(FUNCTION); \ +- } else { \ +- printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \ +- } \ +- __r; \ +-}) +- +-#else +-#define dvb_attach(FUNCTION, ARGS...) ({ \ +- FUNCTION(ARGS); \ +-}) +- +-#endif +- +-#endif /* #ifndef _DVBDEV_H_ */ +diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig +deleted file mode 100644 +index 9f203c6..0000000 +--- a/drivers/media/dvb/dvb-usb/Kconfig ++++ /dev/null +@@ -1,405 +0,0 @@ +-config DVB_USB +- tristate "Support for various USB DVB devices" +- depends on DVB_CORE && USB && I2C && RC_CORE +- help +- By enabling this you will be able to choose the various supported +- USB1.1 and USB2.0 DVB devices. +- +- Almost every USB device needs a firmware, please look into +- . +- +- For a complete list of supported USB devices see the LinuxTV DVB Wiki: +- +- +- Say Y if you own a USB DVB device. +- +-config DVB_USB_DEBUG +- bool "Enable extended debug support for all DVB-USB devices" +- depends on DVB_USB +- help +- Say Y if you want to enable debugging. See modinfo dvb-usb (and the +- appropriate drivers) for debug levels. +- +-config DVB_USB_A800 +- tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" +- depends on DVB_USB +- select DVB_DIB3000MC +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. +- +-config DVB_USB_DIBUSB_MB +- tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_DIB3000MB +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- help +- Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by +- DiBcom () equipped with a DiB3000M-B demodulator. +- +- For an up-to-date list of devices supported by this driver, have a look +- on the Linux-DVB Wiki at www.linuxtv.org. +- +- Say Y if you own such a device and want to use it. You should build it as +- a module. +- +-config DVB_USB_DIBUSB_MB_FAULTY +- bool "Support faulty USB IDs" +- depends on DVB_USB_DIBUSB_MB +- help +- Support for faulty USB IDs due to an invalid EEPROM on some Artec devices. +- +-config DVB_USB_DIBUSB_MC +- tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)" +- depends on DVB_USB +- select DVB_DIB3000MC +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- help +- Support for USB2.0 DVB-T receivers based on reference designs made by +- DiBcom () equipped with a DiB3000M-C/P demodulator. +- +- For an up-to-date list of devices supported by this driver, have a look +- on the Linux-DVB Wiki at www.linuxtv.org. +- +- Say Y if you own such a device and want to use it. You should build it as +- a module. +- +-config DVB_USB_DIB0700 +- tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" +- depends on DVB_USB +- select DVB_DIB7000P if !DVB_FE_CUSTOMISE +- select DVB_DIB7000M if !DVB_FE_CUSTOMISE +- select DVB_DIB8000 if !DVB_FE_CUSTOMISE +- select DVB_DIB3000MC if !DVB_FE_CUSTOMISE +- select DVB_S5H1411 if !DVB_FE_CUSTOMISE +- select DVB_LGDT3305 if !DVB_FE_CUSTOMISE +- select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE +- select DVB_TUNER_DIB0090 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC4000 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE +- help +- Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The +- USB bridge is also present in devices having the DiB7700 DVB-T-USB +- silicon. This chip can be found in devices offered by Hauppauge, +- Avermedia and other big and small companies. +- +- For an up-to-date list of devices supported by this driver, have a look +- on the LinuxTV Wiki at www.linuxtv.org. +- +- Say Y if you own such a device and want to use it. You should build it as +- a module. +- +-config DVB_USB_UMT_010 +- tristate "HanfTek UMT-010 DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_DIB3000MC +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. +- +-config DVB_USB_CXUSB +- tristate "Conexant USB2.0 hybrid reference design support" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_CX22702 if !DVB_FE_CUSTOMISE +- select DVB_LGDT330X if !DVB_FE_CUSTOMISE +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select DVB_DIB7000P if !DVB_FE_CUSTOMISE +- select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE +- select DVB_ATBM8830 if !DVB_FE_CUSTOMISE +- select DVB_LGS8GXX if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Conexant USB2.0 hybrid reference design. +- Currently, only DVB and ATSC modes are supported, analog mode +- shall be added in the future. Devices that require this module: +- +- Medion MD95700 hybrid USB2.0 device. +- DViCO FusionHDTV (Bluebird) USB2.0 devices +- +-config DVB_USB_M920X +- tristate "Uli m920x DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- select DVB_TDA1004X if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. +- Currently, only devices with a product id of +- "DTV USB MINI" (in cold state) are supported. +- Firmware required. +- +-config DVB_USB_GL861 +- tristate "Genesys Logic GL861 USB2.0 support" +- depends on DVB_USB +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 +- receiver with USB ID 0db0:5581. +- +-config DVB_USB_AU6610 +- tristate "Alcor Micro AU6610 USB2.0 support" +- depends on DVB_USB +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. +- +-config DVB_USB_DIGITV +- tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_NXT6000 if !DVB_FE_CUSTOMISE +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver. +- +-config DVB_USB_VP7045 +- tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support" +- depends on DVB_USB +- help +- Say Y here to support the +- +- TwinhanDTV Alpha (stick) (VP-7045), +- TwinhanDTV MagicBox II (VP-7046), +- DigitalNow TinyUSB 2 DVB-t, +- DigitalRise USB 2.0 Ter (Beetle) and +- TYPHOON DVB-T USB DRIVE +- +- DVB-T USB2.0 receivers. +- +-config DVB_USB_VP702X +- tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support" +- depends on DVB_USB +- help +- Say Y here to support the +- +- TwinhanDTV StarBox, +- DigitalRise USB Starbox and +- TYPHOON DVB-S USB 2.0 BOX +- +- DVB-S USB2.0 receivers. +- +-config DVB_USB_GP8PSK +- tristate "GENPIX 8PSK->USB module support" +- depends on DVB_USB +- help +- Say Y here to support the +- GENPIX 8psk module +- +- DVB-S USB2.0 receivers. +- +-config DVB_USB_NOVA_T_USB2 +- tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_DIB3000MC +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. +- +-config DVB_USB_TTUSB2 +- tristate "Pinnacle 400e DVB-S USB2.0 support" +- depends on DVB_USB +- select DVB_TDA10086 if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_TDA826X if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The +- firmware protocol used by this module is similar to the one used by the +- old ttusb-driver - that's why the module is called dvb-usb-ttusb2. +- +-config DVB_USB_DTT200U +- tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)" +- depends on DVB_USB +- help +- Say Y here to support the WideView/Yakumo/Hama/Typhoon/Yuan DVB-T USB2.0 receiver. +- +- The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan). +- +- The WT-220U and its clones are pen-sized. +- +-config DVB_USB_OPERA1 +- tristate "Opera1 DVB-S USB2.0 receiver" +- depends on DVB_USB +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_PLL if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the Opera DVB-S USB2.0 receiver. +- +-config DVB_USB_AF9005 +- tristate "Afatech AF9005 DVB-T USB1.1 support" +- depends on DVB_USB && EXPERIMENTAL +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver +- and the TerraTec Cinergy T USB XE (Rev.1) +- +-config DVB_USB_AF9005_REMOTE +- tristate "Afatech AF9005 default remote control support" +- depends on DVB_USB_AF9005 +- help +- Say Y here to support the default remote control decoding for the +- Afatech AF9005 based receiver. +- +-config DVB_USB_PCTV452E +- tristate "Pinnacle PCTV HDTV Pro USB device/TT Connect S2-3600" +- depends on DVB_USB +- select TTPCI_EEPROM +- select DVB_LNBP22 if !DVB_FE_CUSTOMISE +- select DVB_STB0899 if !DVB_FE_CUSTOMISE +- select DVB_STB6100 if !DVB_FE_CUSTOMISE +- help +- Support for external USB adapter designed by Pinnacle, +- shipped under the brand name 'PCTV HDTV Pro USB'. +- Also supports TT Connect S2-3600/3650 cards. +- Say Y if you own such a device and want to use it. +- +-config DVB_USB_DW2102 +- tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_STV0288 if !DVB_FE_CUSTOMISE +- select DVB_STB6000 if !DVB_FE_CUSTOMISE +- select DVB_CX24116 if !DVB_FE_CUSTOMISE +- select DVB_SI21XX if !DVB_FE_CUSTOMISE +- select DVB_TDA10023 if !DVB_FE_CUSTOMISE +- select DVB_MT312 if !DVB_FE_CUSTOMISE +- select DVB_ZL10039 if !DVB_FE_CUSTOMISE +- select DVB_DS3000 if !DVB_FE_CUSTOMISE +- select DVB_STB6100 if !DVB_FE_CUSTOMISE +- select DVB_STV6110 if !DVB_FE_CUSTOMISE +- select DVB_STV0900 if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the DvbWorld, TeVii, Prof DVB-S/S2 USB2.0 +- receivers. +- +-config DVB_USB_CINERGY_T2 +- tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver" +- depends on DVB_USB +- help +- Support for "TerraTec CinergyT2" USB2.0 Highspeed DVB Receivers +- +- Say Y if you own such a device and want to use it. +- +-config DVB_USB_ANYSEE +- tristate "Anysee DVB-T/C USB2.0 support" +- depends on DVB_USB +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_MT352 if !DVB_FE_CUSTOMISE +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select DVB_TDA10023 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_TDA18212 if !MEDIA_TUNER_CUSTOMISE +- select DVB_CX24116 if !DVB_FE_CUSTOMISE +- select DVB_STV0900 if !DVB_FE_CUSTOMISE +- select DVB_STV6110 if !DVB_FE_CUSTOMISE +- select DVB_ISL6423 if !DVB_FE_CUSTOMISE +- select DVB_CXD2820R if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the Anysee E30, Anysee E30 Plus or +- Anysee E30 C Plus DVB USB2.0 receiver. +- +-config DVB_USB_DTV5100 +- tristate "AME DTV-5100 USB2.0 DVB-T support" +- depends on DVB_USB +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver. +- +-config DVB_USB_AF9015 +- tristate "Afatech AF9015 DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_AF9013 +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE +- select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver +- +-config DVB_USB_CE6230 +- tristate "Intel CE6230 DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_ZL10353 +- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver +- +-config DVB_USB_FRIIO +- tristate "Friio ISDB-T USB2.0 Receiver support" +- depends on DVB_USB +- help +- Say Y here to support the Japanese DTV receiver Friio. +- +-config DVB_USB_EC168 +- tristate "E3C EC168 DVB-T USB2.0 support" +- depends on DVB_USB +- select DVB_EC100 +- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE +- help +- Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. +- +-config DVB_USB_AZ6027 +- tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" +- depends on DVB_USB +- select DVB_STB0899 if !DVB_FE_CUSTOMISE +- select DVB_STB6100 if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the AZ6027 device +- +-config DVB_USB_LME2510 +- tristate "LME DM04/QQBOX DVB-S USB2.0 support" +- depends on DVB_USB +- select DVB_TDA10086 if !DVB_FE_CUSTOMISE +- select DVB_TDA826X if !DVB_FE_CUSTOMISE +- select DVB_STV0288 if !DVB_FE_CUSTOMISE +- select DVB_IX2505V if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_PLL if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 . +- +-config DVB_USB_TECHNISAT_USB2 +- tristate "Technisat DVB-S/S2 USB2.0 support" +- depends on DVB_USB +- select DVB_STV090x if !DVB_FE_CUSTOMISE +- select DVB_STV6110x if !DVB_FE_CUSTOMISE +- help +- Say Y here to support the Technisat USB2 DVB-S/S2 device +- +-config DVB_USB_IT913X +- tristate "it913x driver" +- depends on DVB_USB +- select DVB_IT913X_FE +- help +- Say Y here to support the it913x device +- +-config DVB_USB_MXL111SF +- tristate "MxL111SF DTV USB2.0 support" +- depends on DVB_USB +- select DVB_LGDT3305 if !DVB_FE_CUSTOMISE +- select VIDEO_TVEEPROM +- help +- Say Y here to support the MxL111SF USB2.0 DTV receiver. +diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile +deleted file mode 100644 +index 26c8b9e..0000000 +--- a/drivers/media/dvb/dvb-usb/Makefile ++++ /dev/null +@@ -1,112 +0,0 @@ +-dvb-usb-objs = dvb-usb-firmware.o dvb-usb-init.o dvb-usb-urb.o dvb-usb-i2c.o dvb-usb-dvb.o dvb-usb-remote.o usb-urb.o +-obj-$(CONFIG_DVB_USB) += dvb-usb.o +- +-dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o +-obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o +- +-dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o +-obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o +- +-dvb-usb-gp8psk-objs = gp8psk.o gp8psk-fe.o +-obj-$(CONFIG_DVB_USB_GP8PSK) += dvb-usb-gp8psk.o +- +-dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o +-obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o +- +-dvb-usb-dibusb-common-objs = dibusb-common.o +- +-dvb-usb-a800-objs = a800.o +-obj-$(CONFIG_DVB_USB_A800) += dvb-usb-dibusb-common.o dvb-usb-a800.o +- +-dvb-usb-dibusb-mb-objs = dibusb-mb.o +-obj-$(CONFIG_DVB_USB_DIBUSB_MB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mb.o +- +-dvb-usb-dibusb-mc-objs = dibusb-mc.o +-obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o +- +-dvb-usb-nova-t-usb2-objs = nova-t-usb2.o +-obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o +- +-dvb-usb-umt-010-objs = umt-010.o +-obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o +- +-dvb-usb-m920x-objs = m920x.o +-obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o +- +-dvb-usb-gl861-objs = gl861.o +-obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o +- +-dvb-usb-au6610-objs = au6610.o +-obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o +- +-dvb-usb-digitv-objs = digitv.o +-obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o +- +-dvb-usb-cxusb-objs = cxusb.o +-obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o +- +-dvb-usb-ttusb2-objs = ttusb2.o +-obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o +- +-dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o +-obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o +- +-dvb-usb-opera-objs = opera1.o +-obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o +- +- +-dvb-usb-af9005-objs = af9005.o af9005-fe.o +-obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o +- +-dvb-usb-af9005-remote-objs = af9005-remote.o +-obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o +- +-dvb-usb-anysee-objs = anysee.o +-obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o +- +-dvb-usb-pctv452e-objs = pctv452e.o +-obj-$(CONFIG_DVB_USB_PCTV452E) += dvb-usb-pctv452e.o +- +-dvb-usb-dw2102-objs = dw2102.o +-obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o +- +-dvb-usb-dtv5100-objs = dtv5100.o +-obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o +- +-dvb-usb-af9015-objs = af9015.o +-obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o +- +-dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o +-obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o +- +-dvb-usb-ce6230-objs = ce6230.o +-obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o +- +-dvb-usb-friio-objs = friio.o friio-fe.o +-obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o +- +-dvb-usb-ec168-objs = ec168.o +-obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o +- +-dvb-usb-az6027-objs = az6027.o +-obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o +- +-dvb-usb-lmedm04-objs = lmedm04.o +-obj-$(CONFIG_DVB_USB_LME2510) += dvb-usb-lmedm04.o +- +-dvb-usb-technisat-usb2-objs = technisat-usb2.o +-obj-$(CONFIG_DVB_USB_TECHNISAT_USB2) += dvb-usb-technisat-usb2.o +- +-dvb-usb-it913x-objs := it913x.o +-obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o +- +-dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o +-obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o +-obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o +-obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +-# due to tuner-xc3028 +-ccflags-y += -Idrivers/media/common/tuners +-EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci +- +diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c +deleted file mode 100644 +index 8d7fef8..0000000 +--- a/drivers/media/dvb/dvb-usb/a800.c ++++ /dev/null +@@ -1,191 +0,0 @@ +-/* DVB USB framework compliant Linux driver for the AVerMedia AverTV DVB-T +- * USB2.0 (A800) DVB-T receiver. +- * +- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * Thanks to +- * - AVerMedia who kindly provided information and +- * - Glen Harris who suffered from my mistakes during development. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (rc=1 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_rc(args...) dprintk(debug,0x01,args) +- +-static int a800_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- /* do nothing for the AVerMedia */ +- return 0; +-} +- +-/* assure to put cold to 0 for iManufacturer == 1 */ +-static int a800_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, int *cold) +-{ +- *cold = udev->descriptor.iManufacturer != 1; +- return 0; +-} +- +-static struct rc_map_table rc_map_a800_table[] = { +- { 0x0201, KEY_MODE }, /* SOURCE */ +- { 0x0200, KEY_POWER2 }, /* POWER */ +- { 0x0205, KEY_1 }, /* 1 */ +- { 0x0206, KEY_2 }, /* 2 */ +- { 0x0207, KEY_3 }, /* 3 */ +- { 0x0209, KEY_4 }, /* 4 */ +- { 0x020a, KEY_5 }, /* 5 */ +- { 0x020b, KEY_6 }, /* 6 */ +- { 0x020d, KEY_7 }, /* 7 */ +- { 0x020e, KEY_8 }, /* 8 */ +- { 0x020f, KEY_9 }, /* 9 */ +- { 0x0212, KEY_LEFT }, /* L / DISPLAY */ +- { 0x0211, KEY_0 }, /* 0 */ +- { 0x0213, KEY_RIGHT }, /* R / CH RTN */ +- { 0x0217, KEY_CAMERA }, /* SNAP SHOT */ +- { 0x0210, KEY_LAST }, /* 16-CH PREV */ +- { 0x021e, KEY_VOLUMEDOWN }, /* VOL DOWN */ +- { 0x020c, KEY_ZOOM }, /* FULL SCREEN */ +- { 0x021f, KEY_VOLUMEUP }, /* VOL UP */ +- { 0x0214, KEY_MUTE }, /* MUTE */ +- { 0x0208, KEY_AUDIO }, /* AUDIO */ +- { 0x0219, KEY_RECORD }, /* RECORD */ +- { 0x0218, KEY_PLAY }, /* PLAY */ +- { 0x021b, KEY_STOP }, /* STOP */ +- { 0x021a, KEY_PLAYPAUSE }, /* TIMESHIFT / PAUSE */ +- { 0x021d, KEY_BACK }, /* << / RED */ +- { 0x021c, KEY_FORWARD }, /* >> / YELLOW */ +- { 0x0203, KEY_TEXT }, /* TELETEXT */ +- { 0x0204, KEY_EPG }, /* EPG */ +- { 0x0215, KEY_MENU }, /* MENU */ +- +- { 0x0303, KEY_CHANNELUP }, /* CH UP */ +- { 0x0302, KEY_CHANNELDOWN }, /* CH DOWN */ +- { 0x0301, KEY_FIRST }, /* |<< / GREEN */ +- { 0x0300, KEY_LAST }, /* >>| / BLUE */ +- +-}; +- +-static int a800_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- int ret; +- u8 *key = kmalloc(5, GFP_KERNEL); +- if (!key) +- return -ENOMEM; +- +- if (usb_control_msg(d->udev,usb_rcvctrlpipe(d->udev,0), +- 0x04, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, key, 5, +- 2000) != 5) { +- ret = -ENODEV; +- goto out; +- } +- +- /* call the universal NEC remote processor, to find out the key's state and event */ +- dvb_usb_nec_rc_key_to_event(d,key,event,state); +- if (key[0] != 0) +- deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); +- ret = 0; +-out: +- kfree(key); +- return ret; +-} +- +-/* USB Driver stuff */ +-static struct dvb_usb_device_properties a800_properties; +- +-static int a800_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &a800_properties, +- THIS_MODULE, NULL, adapter_nr); +-} +- +-/* do not change the order of the ID table */ +-static struct usb_device_id a800_table [] = { +-/* 00 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_COLD) }, +-/* 01 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_DVBT_USB2_WARM) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, a800_table); +- +-static struct dvb_usb_device_properties a800_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-avertv-a800-02.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- +- .frontend_attach = dibusb_dib3000mc_frontend_attach, +- .tuner_attach = dibusb_dib3000mc_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- }, +- }, +- +- .power_ctrl = a800_power_ctrl, +- .identify_state = a800_identify_state, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_a800_table, +- .rc_map_size = ARRAY_SIZE(rc_map_a800_table), +- .rc_query = a800_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- .num_device_descs = 1, +- .devices = { +- { "AVerMedia AverTV DVB-T USB 2.0 (A800)", +- { &a800_table[0], NULL }, +- { &a800_table[1], NULL }, +- }, +- } +-}; +- +-static struct usb_driver a800_driver = { +- .name = "dvb_usb_a800", +- .probe = a800_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = a800_table, +-}; +- +-module_usb_driver(a800_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("AVerMedia AverTV DVB-T USB 2.0 (A800)"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/dvb/dvb-usb/af9005-fe.c +deleted file mode 100644 +index 740f3f4..0000000 +--- a/drivers/media/dvb/dvb-usb/af9005-fe.c ++++ /dev/null +@@ -1,1487 +0,0 @@ +-/* Frontend part of the Linux driver for the Afatech 9005 +- * USB1.1 DVB-T receiver. +- * +- * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) +- * +- * Thanks to Afatech who kindly provided information. +- * +- * 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. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "af9005.h" +-#include "af9005-script.h" +-#include "mt2060.h" +-#include "qt1010.h" +-#include +- +-struct af9005_fe_state { +- struct dvb_usb_device *d; +- fe_status_t stat; +- +- /* retraining parameters */ +- u32 original_fcw; +- u16 original_rf_top; +- u16 original_if_top; +- u16 original_if_min; +- u16 original_aci0_if_top; +- u16 original_aci1_if_top; +- u16 original_aci0_if_min; +- u8 original_if_unplug_th; +- u8 original_rf_unplug_th; +- u8 original_dtop_if_unplug_th; +- u8 original_dtop_rf_unplug_th; +- +- /* statistics */ +- u32 pre_vit_error_count; +- u32 pre_vit_bit_count; +- u32 ber; +- u32 post_vit_error_count; +- u32 post_vit_bit_count; +- u32 unc; +- u16 abort_count; +- +- int opened; +- int strong; +- unsigned long next_status_check; +- struct dvb_frontend frontend; +-}; +- +-static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi, +- u16 reglo, u8 pos, u8 len, u16 value) +-{ +- int ret; +- +- if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff)))) +- return ret; +- return af9005_write_register_bits(d, reghi, pos, len, +- (u8) ((value & 0x300) >> 8)); +-} +- +-static int af9005_read_word_agc(struct dvb_usb_device *d, u16 reghi, +- u16 reglo, u8 pos, u8 len, u16 * value) +-{ +- int ret; +- u8 temp0, temp1; +- +- if ((ret = af9005_read_ofdm_register(d, reglo, &temp0))) +- return ret; +- if ((ret = af9005_read_ofdm_register(d, reghi, &temp1))) +- return ret; +- switch (pos) { +- case 0: +- *value = ((u16) (temp1 & 0x03) << 8) + (u16) temp0; +- break; +- case 2: +- *value = ((u16) (temp1 & 0x0C) << 6) + (u16) temp0; +- break; +- case 4: +- *value = ((u16) (temp1 & 0x30) << 4) + (u16) temp0; +- break; +- case 6: +- *value = ((u16) (temp1 & 0xC0) << 2) + (u16) temp0; +- break; +- default: +- err("invalid pos in read word agc"); +- return -EINVAL; +- } +- return 0; +- +-} +- +-static int af9005_is_fecmon_available(struct dvb_frontend *fe, int *available) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 temp; +- +- *available = false; +- +- ret = af9005_read_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, +- fec_vtb_rsd_mon_en_pos, +- fec_vtb_rsd_mon_en_len, &temp); +- if (ret) +- return ret; +- if (temp & 1) { +- ret = +- af9005_read_register_bits(state->d, +- xd_p_reg_ofsm_read_rbc_en, +- reg_ofsm_read_rbc_en_pos, +- reg_ofsm_read_rbc_en_len, &temp); +- if (ret) +- return ret; +- if ((temp & 1) == 0) +- *available = true; +- +- } +- return 0; +-} +- +-static int af9005_get_post_vit_err_cw_count(struct dvb_frontend *fe, +- u32 * post_err_count, +- u32 * post_cw_count, +- u16 * abort_count) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- u32 err_count; +- u32 cw_count; +- u8 temp, temp0, temp1, temp2; +- u16 loc_abort_count; +- +- *post_err_count = 0; +- *post_cw_count = 0; +- +- /* check if error bit count is ready */ +- ret = +- af9005_read_register_bits(state->d, xd_r_fec_rsd_ber_rdy, +- fec_rsd_ber_rdy_pos, fec_rsd_ber_rdy_len, +- &temp); +- if (ret) +- return ret; +- if (!temp) { +- deb_info("rsd counter not ready\n"); +- return 100; +- } +- /* get abort count */ +- ret = +- af9005_read_ofdm_register(state->d, +- xd_r_fec_rsd_abort_packet_cnt_7_0, +- &temp0); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, +- xd_r_fec_rsd_abort_packet_cnt_15_8, +- &temp1); +- if (ret) +- return ret; +- loc_abort_count = ((u16) temp1 << 8) + temp0; +- +- /* get error count */ +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_7_0, +- &temp0); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_15_8, +- &temp1); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_23_16, +- &temp2); +- if (ret) +- return ret; +- err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; +- *post_err_count = err_count - (u32) loc_abort_count *8 * 8; +- +- /* get RSD packet number */ +- ret = +- af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, +- &temp0); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, +- &temp1); +- if (ret) +- return ret; +- cw_count = ((u32) temp1 << 8) + temp0; +- if (cw_count == 0) { +- err("wrong RSD packet count"); +- return -EIO; +- } +- deb_info("POST abort count %d err count %d rsd packets %d\n", +- loc_abort_count, err_count, cw_count); +- *post_cw_count = cw_count - (u32) loc_abort_count; +- *abort_count = loc_abort_count; +- return 0; +- +-} +- +-static int af9005_get_post_vit_ber(struct dvb_frontend *fe, +- u32 * post_err_count, u32 * post_cw_count, +- u16 * abort_count) +-{ +- u32 loc_cw_count = 0, loc_err_count; +- u16 loc_abort_count = 0; +- int ret; +- +- ret = +- af9005_get_post_vit_err_cw_count(fe, &loc_err_count, &loc_cw_count, +- &loc_abort_count); +- if (ret) +- return ret; +- *post_err_count = loc_err_count; +- *post_cw_count = loc_cw_count * 204 * 8; +- *abort_count = loc_abort_count; +- +- return 0; +-} +- +-static int af9005_get_pre_vit_err_bit_count(struct dvb_frontend *fe, +- u32 * pre_err_count, +- u32 * pre_bit_count) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- u8 temp, temp0, temp1, temp2; +- u32 super_frame_count, x, bits; +- int ret; +- +- ret = +- af9005_read_register_bits(state->d, xd_r_fec_vtb_ber_rdy, +- fec_vtb_ber_rdy_pos, fec_vtb_ber_rdy_len, +- &temp); +- if (ret) +- return ret; +- if (!temp) { +- deb_info("viterbi counter not ready\n"); +- return 101; /* ERR_APO_VTB_COUNTER_NOT_READY; */ +- } +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_7_0, +- &temp0); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_15_8, +- &temp1); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_23_16, +- &temp2); +- if (ret) +- return ret; +- *pre_err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; +- +- ret = +- af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, +- &temp0); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, +- &temp1); +- if (ret) +- return ret; +- super_frame_count = ((u32) temp1 << 8) + temp0; +- if (super_frame_count == 0) { +- deb_info("super frame count 0\n"); +- return 102; +- } +- +- /* read fft mode */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, +- reg_tpsd_txmod_pos, reg_tpsd_txmod_len, +- &temp); +- if (ret) +- return ret; +- if (temp == 0) { +- /* 2K */ +- x = 1512; +- } else if (temp == 1) { +- /* 8k */ +- x = 6048; +- } else { +- err("Invalid fft mode"); +- return -EINVAL; +- } +- +- /* read modulation mode */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, +- reg_tpsd_const_pos, reg_tpsd_const_len, +- &temp); +- if (ret) +- return ret; +- switch (temp) { +- case 0: /* QPSK */ +- bits = 2; +- break; +- case 1: /* QAM_16 */ +- bits = 4; +- break; +- case 2: /* QAM_64 */ +- bits = 6; +- break; +- default: +- err("invalid modulation mode"); +- return -EINVAL; +- } +- *pre_bit_count = super_frame_count * 68 * 4 * x * bits; +- deb_info("PRE err count %d frame count %d bit count %d\n", +- *pre_err_count, super_frame_count, *pre_bit_count); +- return 0; +-} +- +-static int af9005_reset_pre_viterbi(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- +- /* set super frame count to 1 */ +- ret = +- af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, +- 1 & 0xff); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, +- 1 >> 8); +- if (ret) +- return ret; +- /* reset pre viterbi error count */ +- ret = +- af9005_write_register_bits(state->d, xd_p_fec_vtb_ber_rst, +- fec_vtb_ber_rst_pos, fec_vtb_ber_rst_len, +- 1); +- +- return ret; +-} +- +-static int af9005_reset_post_viterbi(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- +- /* set packet unit */ +- ret = +- af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, +- 10000 & 0xff); +- if (ret) +- return ret; +- ret = +- af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, +- 10000 >> 8); +- if (ret) +- return ret; +- /* reset post viterbi error count */ +- ret = +- af9005_write_register_bits(state->d, xd_p_fec_rsd_ber_rst, +- fec_rsd_ber_rst_pos, fec_rsd_ber_rst_len, +- 1); +- +- return ret; +-} +- +-static int af9005_get_statistic(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret, fecavailable; +- u64 numerator, denominator; +- +- deb_info("GET STATISTIC\n"); +- ret = af9005_is_fecmon_available(fe, &fecavailable); +- if (ret) +- return ret; +- if (!fecavailable) { +- deb_info("fecmon not available\n"); +- return 0; +- } +- +- ret = af9005_get_pre_vit_err_bit_count(fe, &state->pre_vit_error_count, +- &state->pre_vit_bit_count); +- if (ret == 0) { +- af9005_reset_pre_viterbi(fe); +- if (state->pre_vit_bit_count > 0) { +- /* according to v 0.0.4 of the dvb api ber should be a multiple +- of 10E-9 so we have to multiply the error count by +- 10E9=1000000000 */ +- numerator = +- (u64) state->pre_vit_error_count * (u64) 1000000000; +- denominator = (u64) state->pre_vit_bit_count; +- state->ber = do_div(numerator, denominator); +- } else { +- state->ber = 0xffffffff; +- } +- } +- +- ret = af9005_get_post_vit_ber(fe, &state->post_vit_error_count, +- &state->post_vit_bit_count, +- &state->abort_count); +- if (ret == 0) { +- ret = af9005_reset_post_viterbi(fe); +- state->unc += state->abort_count; +- if (ret) +- return ret; +- } +- return 0; +-} +- +-static int af9005_fe_refresh_state(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- if (time_after(jiffies, state->next_status_check)) { +- deb_info("REFRESH STATE\n"); +- +- /* statistics */ +- if (af9005_get_statistic(fe)) +- err("get_statistic_failed"); +- state->next_status_check = jiffies + 250 * HZ / 1000; +- } +- return 0; +-} +- +-static int af9005_fe_read_status(struct dvb_frontend *fe, fe_status_t * stat) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- u8 temp; +- int ret; +- +- if (fe->ops.tuner_ops.release == NULL) +- return -ENODEV; +- +- *stat = 0; +- ret = af9005_read_register_bits(state->d, xd_p_agc_lock, +- agc_lock_pos, agc_lock_len, &temp); +- if (ret) +- return ret; +- if (temp) +- *stat |= FE_HAS_SIGNAL; +- +- ret = af9005_read_register_bits(state->d, xd_p_fd_tpsd_lock, +- fd_tpsd_lock_pos, fd_tpsd_lock_len, +- &temp); +- if (ret) +- return ret; +- if (temp) +- *stat |= FE_HAS_CARRIER; +- +- ret = af9005_read_register_bits(state->d, +- xd_r_mp2if_sync_byte_locked, +- mp2if_sync_byte_locked_pos, +- mp2if_sync_byte_locked_pos, &temp); +- if (ret) +- return ret; +- if (temp) +- *stat |= FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_LOCK; +- if (state->opened) +- af9005_led_control(state->d, *stat & FE_HAS_LOCK); +- +- ret = +- af9005_read_register_bits(state->d, xd_p_reg_strong_sginal_detected, +- reg_strong_sginal_detected_pos, +- reg_strong_sginal_detected_len, &temp); +- if (ret) +- return ret; +- if (temp != state->strong) { +- deb_info("adjust for strong signal %d\n", temp); +- state->strong = temp; +- } +- return 0; +-} +- +-static int af9005_fe_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- if (fe->ops.tuner_ops.release == NULL) +- return -ENODEV; +- af9005_fe_refresh_state(fe); +- *ber = state->ber; +- return 0; +-} +- +-static int af9005_fe_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- if (fe->ops.tuner_ops.release == NULL) +- return -ENODEV; +- af9005_fe_refresh_state(fe); +- *unc = state->unc; +- return 0; +-} +- +-static int af9005_fe_read_signal_strength(struct dvb_frontend *fe, +- u16 * strength) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 if_gain, rf_gain; +- +- if (fe->ops.tuner_ops.release == NULL) +- return -ENODEV; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_reg_aagc_rf_gain, +- &rf_gain); +- if (ret) +- return ret; +- ret = +- af9005_read_ofdm_register(state->d, xd_r_reg_aagc_if_gain, +- &if_gain); +- if (ret) +- return ret; +- /* this value has no real meaning, but i don't have the tables that relate +- the rf and if gain with the dbm, so I just scale the value */ +- *strength = (512 - rf_gain - if_gain) << 7; +- return 0; +-} +- +-static int af9005_fe_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- /* the snr can be derived from the ber and the modulation +- but I don't think this kind of complex calculations belong +- in the driver. I may be wrong.... */ +- return -ENOSYS; +-} +- +-static int af9005_fe_program_cfoe(struct dvb_usb_device *d, u32 bw) +-{ +- u8 temp0, temp1, temp2, temp3, buf[4]; +- int ret; +- u32 NS_coeff1_2048Nu; +- u32 NS_coeff1_8191Nu; +- u32 NS_coeff1_8192Nu; +- u32 NS_coeff1_8193Nu; +- u32 NS_coeff2_2k; +- u32 NS_coeff2_8k; +- +- switch (bw) { +- case 6000000: +- NS_coeff1_2048Nu = 0x2ADB6DC; +- NS_coeff1_8191Nu = 0xAB7313; +- NS_coeff1_8192Nu = 0xAB6DB7; +- NS_coeff1_8193Nu = 0xAB685C; +- NS_coeff2_2k = 0x156DB6E; +- NS_coeff2_8k = 0x55B6DC; +- break; +- +- case 7000000: +- NS_coeff1_2048Nu = 0x3200001; +- NS_coeff1_8191Nu = 0xC80640; +- NS_coeff1_8192Nu = 0xC80000; +- NS_coeff1_8193Nu = 0xC7F9C0; +- NS_coeff2_2k = 0x1900000; +- NS_coeff2_8k = 0x640000; +- break; +- +- case 8000000: +- NS_coeff1_2048Nu = 0x3924926; +- NS_coeff1_8191Nu = 0xE4996E; +- NS_coeff1_8192Nu = 0xE49249; +- NS_coeff1_8193Nu = 0xE48B25; +- NS_coeff2_2k = 0x1C92493; +- NS_coeff2_8k = 0x724925; +- break; +- default: +- err("Invalid bandwidth %d.", bw); +- return -EINVAL; +- } +- +- /* +- * write NS_coeff1_2048Nu +- */ +- +- temp0 = (u8) (NS_coeff1_2048Nu & 0x000000FF); +- temp1 = (u8) ((NS_coeff1_2048Nu & 0x0000FF00) >> 8); +- temp2 = (u8) ((NS_coeff1_2048Nu & 0x00FF0000) >> 16); +- temp3 = (u8) ((NS_coeff1_2048Nu & 0x03000000) >> 24); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- /* cfoe_NS_2k_coeff1_25_24 */ +- ret = af9005_write_ofdm_register(d, 0xAE00, buf[0]); +- if (ret) +- return ret; +- +- /* cfoe_NS_2k_coeff1_23_16 */ +- ret = af9005_write_ofdm_register(d, 0xAE01, buf[1]); +- if (ret) +- return ret; +- +- /* cfoe_NS_2k_coeff1_15_8 */ +- ret = af9005_write_ofdm_register(d, 0xAE02, buf[2]); +- if (ret) +- return ret; +- +- /* cfoe_NS_2k_coeff1_7_0 */ +- ret = af9005_write_ofdm_register(d, 0xAE03, buf[3]); +- if (ret) +- return ret; +- +- /* +- * write NS_coeff2_2k +- */ +- +- temp0 = (u8) ((NS_coeff2_2k & 0x0000003F)); +- temp1 = (u8) ((NS_coeff2_2k & 0x00003FC0) >> 6); +- temp2 = (u8) ((NS_coeff2_2k & 0x003FC000) >> 14); +- temp3 = (u8) ((NS_coeff2_2k & 0x01C00000) >> 22); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- ret = af9005_write_ofdm_register(d, 0xAE04, buf[0]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE05, buf[1]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE06, buf[2]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE07, buf[3]); +- if (ret) +- return ret; +- +- /* +- * write NS_coeff1_8191Nu +- */ +- +- temp0 = (u8) ((NS_coeff1_8191Nu & 0x000000FF)); +- temp1 = (u8) ((NS_coeff1_8191Nu & 0x0000FF00) >> 8); +- temp2 = (u8) ((NS_coeff1_8191Nu & 0x00FFC000) >> 16); +- temp3 = (u8) ((NS_coeff1_8191Nu & 0x03000000) >> 24); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- ret = af9005_write_ofdm_register(d, 0xAE08, buf[0]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE09, buf[1]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE0A, buf[2]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE0B, buf[3]); +- if (ret) +- return ret; +- +- /* +- * write NS_coeff1_8192Nu +- */ +- +- temp0 = (u8) (NS_coeff1_8192Nu & 0x000000FF); +- temp1 = (u8) ((NS_coeff1_8192Nu & 0x0000FF00) >> 8); +- temp2 = (u8) ((NS_coeff1_8192Nu & 0x00FFC000) >> 16); +- temp3 = (u8) ((NS_coeff1_8192Nu & 0x03000000) >> 24); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- ret = af9005_write_ofdm_register(d, 0xAE0C, buf[0]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE0D, buf[1]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE0E, buf[2]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE0F, buf[3]); +- if (ret) +- return ret; +- +- /* +- * write NS_coeff1_8193Nu +- */ +- +- temp0 = (u8) ((NS_coeff1_8193Nu & 0x000000FF)); +- temp1 = (u8) ((NS_coeff1_8193Nu & 0x0000FF00) >> 8); +- temp2 = (u8) ((NS_coeff1_8193Nu & 0x00FFC000) >> 16); +- temp3 = (u8) ((NS_coeff1_8193Nu & 0x03000000) >> 24); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- ret = af9005_write_ofdm_register(d, 0xAE10, buf[0]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE11, buf[1]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE12, buf[2]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE13, buf[3]); +- if (ret) +- return ret; +- +- /* +- * write NS_coeff2_8k +- */ +- +- temp0 = (u8) ((NS_coeff2_8k & 0x0000003F)); +- temp1 = (u8) ((NS_coeff2_8k & 0x00003FC0) >> 6); +- temp2 = (u8) ((NS_coeff2_8k & 0x003FC000) >> 14); +- temp3 = (u8) ((NS_coeff2_8k & 0x01C00000) >> 22); +- +- /* big endian to make 8051 happy */ +- buf[0] = temp3; +- buf[1] = temp2; +- buf[2] = temp1; +- buf[3] = temp0; +- +- ret = af9005_write_ofdm_register(d, 0xAE14, buf[0]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE15, buf[1]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE16, buf[2]); +- if (ret) +- return ret; +- +- ret = af9005_write_ofdm_register(d, 0xAE17, buf[3]); +- return ret; +- +-} +- +-static int af9005_fe_select_bw(struct dvb_usb_device *d, u32 bw) +-{ +- u8 temp; +- switch (bw) { +- case 6000000: +- temp = 0; +- break; +- case 7000000: +- temp = 1; +- break; +- case 8000000: +- temp = 2; +- break; +- default: +- err("Invalid bandwidth %d.", bw); +- return -EINVAL; +- } +- return af9005_write_register_bits(d, xd_g_reg_bw, reg_bw_pos, +- reg_bw_len, temp); +-} +- +-static int af9005_fe_power(struct dvb_frontend *fe, int on) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- u8 temp = on; +- int ret; +- deb_info("power %s tuner\n", on ? "on" : "off"); +- ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); +- return ret; +-} +- +-static struct mt2060_config af9005_mt2060_config = { +- 0xC0 +-}; +- +-static struct qt1010_config af9005_qt1010_config = { +- 0xC4 +-}; +- +-static int af9005_fe_init(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- int ret, i, scriptlen; +- u8 temp, temp0 = 0, temp1 = 0, temp2 = 0; +- u8 buf[2]; +- u16 if1; +- +- deb_info("in af9005_fe_init\n"); +- +- /* reset */ +- deb_info("reset\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst_en, +- 4, 1, 0x01))) +- return ret; +- if ((ret = af9005_write_ofdm_register(state->d, APO_REG_RESET, 0))) +- return ret; +- /* clear ofdm reset */ +- deb_info("clear ofdm reset\n"); +- for (i = 0; i < 150; i++) { +- if ((ret = +- af9005_read_ofdm_register(state->d, +- xd_I2C_reg_ofdm_rst, &temp))) +- return ret; +- if (temp & (regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos)) +- break; +- msleep(10); +- } +- if (i == 150) +- return -ETIMEDOUT; +- +- /*FIXME in the dump +- write B200 A9 +- write xd_g_reg_ofsm_clk 7 +- read eepr c6 (2) +- read eepr c7 (2) +- misc ctrl 3 -> 1 +- read eepr ca (6) +- write xd_g_reg_ofsm_clk 0 +- write B200 a1 +- */ +- ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa9); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x07); +- if (ret) +- return ret; +- temp = 0x01; +- ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x00); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa1); +- if (ret) +- return ret; +- +- temp = regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos; +- if ((ret = +- af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, +- reg_ofdm_rst_pos, reg_ofdm_rst_len, 1))) +- return ret; +- ret = af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, +- reg_ofdm_rst_pos, reg_ofdm_rst_len, 0); +- +- if (ret) +- return ret; +- /* don't know what register aefc is, but this is what the windows driver does */ +- ret = af9005_write_ofdm_register(state->d, 0xaefc, 0); +- if (ret) +- return ret; +- +- /* set stand alone chip */ +- deb_info("set stand alone chip\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_dca_stand_alone, +- reg_dca_stand_alone_pos, +- reg_dca_stand_alone_len, 1))) +- return ret; +- +- /* set dca upper & lower chip */ +- deb_info("set dca upper & lower chip\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_dca_upper_chip, +- reg_dca_upper_chip_pos, +- reg_dca_upper_chip_len, 0))) +- return ret; +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_dca_lower_chip, +- reg_dca_lower_chip_pos, +- reg_dca_lower_chip_len, 0))) +- return ret; +- +- /* set 2wire master clock to 0x14 (for 60KHz) */ +- deb_info("set 2wire master clock to 0x14 (for 60KHz)\n"); +- if ((ret = +- af9005_write_ofdm_register(state->d, xd_I2C_i2c_m_period, 0x14))) +- return ret; +- +- /* clear dca enable chip */ +- deb_info("clear dca enable chip\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_dca_en, +- reg_dca_en_pos, reg_dca_en_len, 0))) +- return ret; +- /* FIXME these are register bits, but I don't know which ones */ +- ret = af9005_write_ofdm_register(state->d, 0xa16c, 1); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, 0xa3c1, 0); +- if (ret) +- return ret; +- +- /* init other parameters: program cfoe and select bandwidth */ +- deb_info("program cfoe\n"); +- ret = af9005_fe_program_cfoe(state->d, 6000000); +- if (ret) +- return ret; +- /* set read-update bit for modulation */ +- deb_info("set read-update bit for modulation\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_feq_read_update, +- reg_feq_read_update_pos, +- reg_feq_read_update_len, 1))) +- return ret; +- +- /* sample code has a set MPEG TS code here +- but sniffing reveals that it doesn't do it */ +- +- /* set read-update bit to 1 for DCA modulation */ +- deb_info("set read-update bit 1 for DCA modulation\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_reg_dca_read_update, +- reg_dca_read_update_pos, +- reg_dca_read_update_len, 1))) +- return ret; +- +- /* enable fec monitor */ +- deb_info("enable fec monitor\n"); +- if ((ret = +- af9005_write_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, +- fec_vtb_rsd_mon_en_pos, +- fec_vtb_rsd_mon_en_len, 1))) +- return ret; +- +- /* FIXME should be register bits, I don't know which ones */ +- ret = af9005_write_ofdm_register(state->d, 0xa601, 0); +- +- /* set api_retrain_never_freeze */ +- deb_info("set api_retrain_never_freeze\n"); +- if ((ret = af9005_write_ofdm_register(state->d, 0xaefb, 0x01))) +- return ret; +- +- /* load init script */ +- deb_info("load init script\n"); +- scriptlen = sizeof(script) / sizeof(RegDesc); +- for (i = 0; i < scriptlen; i++) { +- if ((ret = +- af9005_write_register_bits(state->d, script[i].reg, +- script[i].pos, +- script[i].len, script[i].val))) +- return ret; +- /* save 3 bytes of original fcw */ +- if (script[i].reg == 0xae18) +- temp2 = script[i].val; +- if (script[i].reg == 0xae19) +- temp1 = script[i].val; +- if (script[i].reg == 0xae1a) +- temp0 = script[i].val; +- +- /* save original unplug threshold */ +- if (script[i].reg == xd_p_reg_unplug_th) +- state->original_if_unplug_th = script[i].val; +- if (script[i].reg == xd_p_reg_unplug_rf_gain_th) +- state->original_rf_unplug_th = script[i].val; +- if (script[i].reg == xd_p_reg_unplug_dtop_if_gain_th) +- state->original_dtop_if_unplug_th = script[i].val; +- if (script[i].reg == xd_p_reg_unplug_dtop_rf_gain_th) +- state->original_dtop_rf_unplug_th = script[i].val; +- +- } +- state->original_fcw = +- ((u32) temp2 << 16) + ((u32) temp1 << 8) + (u32) temp0; +- +- +- /* save original TOPs */ +- deb_info("save original TOPs\n"); +- +- /* RF TOP */ +- ret = +- af9005_read_word_agc(state->d, +- xd_p_reg_aagc_rf_top_numerator_9_8, +- xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, +- &state->original_rf_top); +- if (ret) +- return ret; +- +- /* IF TOP */ +- ret = +- af9005_read_word_agc(state->d, +- xd_p_reg_aagc_if_top_numerator_9_8, +- xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, +- &state->original_if_top); +- if (ret) +- return ret; +- +- /* ACI 0 IF TOP */ +- ret = +- af9005_read_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, +- &state->original_aci0_if_top); +- if (ret) +- return ret; +- +- /* ACI 1 IF TOP */ +- ret = +- af9005_read_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, +- &state->original_aci1_if_top); +- if (ret) +- return ret; +- +- /* attach tuner and init */ +- if (fe->ops.tuner_ops.release == NULL) { +- /* read tuner and board id from eeprom */ +- ret = af9005_read_eeprom(adap->dev, 0xc6, buf, 2); +- if (ret) { +- err("Impossible to read EEPROM\n"); +- return ret; +- } +- deb_info("Tuner id %d, board id %d\n", buf[0], buf[1]); +- switch (buf[0]) { +- case 2: /* MT2060 */ +- /* read if1 from eeprom */ +- ret = af9005_read_eeprom(adap->dev, 0xc8, buf, 2); +- if (ret) { +- err("Impossible to read EEPROM\n"); +- return ret; +- } +- if1 = (u16) (buf[0] << 8) + buf[1]; +- if (dvb_attach(mt2060_attach, fe, &adap->dev->i2c_adap, +- &af9005_mt2060_config, if1) == NULL) { +- deb_info("MT2060 attach failed\n"); +- return -ENODEV; +- } +- break; +- case 3: /* QT1010 */ +- case 9: /* QT1010B */ +- if (dvb_attach(qt1010_attach, fe, &adap->dev->i2c_adap, +- &af9005_qt1010_config) ==NULL) { +- deb_info("QT1010 attach failed\n"); +- return -ENODEV; +- } +- break; +- default: +- err("Unsupported tuner type %d", buf[0]); +- return -ENODEV; +- } +- ret = fe->ops.tuner_ops.init(fe); +- if (ret) +- return ret; +- } +- +- deb_info("profit!\n"); +- return 0; +-} +- +-static int af9005_fe_sleep(struct dvb_frontend *fe) +-{ +- return af9005_fe_power(fe, 0); +-} +- +-static int af9005_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +-{ +- struct af9005_fe_state *state = fe->demodulator_priv; +- +- if (acquire) { +- state->opened++; +- } else { +- +- state->opened--; +- if (!state->opened) +- af9005_led_control(state->d, 0); +- } +- return 0; +-} +- +-static int af9005_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 temp, temp0, temp1, temp2; +- +- deb_info("af9005_fe_set_frontend freq %d bw %d\n", fep->frequency, +- fep->bandwidth_hz); +- if (fe->ops.tuner_ops.release == NULL) { +- err("Tuner not attached"); +- return -ENODEV; +- } +- +- deb_info("turn off led\n"); +- /* not in the log */ +- ret = af9005_led_control(state->d, 0); +- if (ret) +- return ret; +- /* not sure about the bits */ +- ret = af9005_write_register_bits(state->d, XD_MP2IF_MISC, 2, 1, 0); +- if (ret) +- return ret; +- +- /* set FCW to default value */ +- deb_info("set FCW to default value\n"); +- temp0 = (u8) (state->original_fcw & 0x000000ff); +- temp1 = (u8) ((state->original_fcw & 0x0000ff00) >> 8); +- temp2 = (u8) ((state->original_fcw & 0x00ff0000) >> 16); +- ret = af9005_write_ofdm_register(state->d, 0xae1a, temp0); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, 0xae19, temp1); +- if (ret) +- return ret; +- ret = af9005_write_ofdm_register(state->d, 0xae18, temp2); +- if (ret) +- return ret; +- +- /* restore original TOPs */ +- deb_info("restore original TOPs\n"); +- ret = +- af9005_write_word_agc(state->d, +- xd_p_reg_aagc_rf_top_numerator_9_8, +- xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, +- state->original_rf_top); +- if (ret) +- return ret; +- ret = +- af9005_write_word_agc(state->d, +- xd_p_reg_aagc_if_top_numerator_9_8, +- xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, +- state->original_if_top); +- if (ret) +- return ret; +- ret = +- af9005_write_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, +- state->original_aci0_if_top); +- if (ret) +- return ret; +- ret = +- af9005_write_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, +- state->original_aci1_if_top); +- if (ret) +- return ret; +- +- /* select bandwidth */ +- deb_info("select bandwidth"); +- ret = af9005_fe_select_bw(state->d, fep->bandwidth_hz); +- if (ret) +- return ret; +- ret = af9005_fe_program_cfoe(state->d, fep->bandwidth_hz); +- if (ret) +- return ret; +- +- /* clear easy mode flag */ +- deb_info("clear easy mode flag\n"); +- ret = af9005_write_ofdm_register(state->d, 0xaefd, 0); +- if (ret) +- return ret; +- +- /* set unplug threshold to original value */ +- deb_info("set unplug threshold to original value\n"); +- ret = +- af9005_write_ofdm_register(state->d, xd_p_reg_unplug_th, +- state->original_if_unplug_th); +- if (ret) +- return ret; +- /* set tuner */ +- deb_info("set tuner\n"); +- ret = fe->ops.tuner_ops.set_params(fe); +- if (ret) +- return ret; +- +- /* trigger ofsm */ +- deb_info("trigger ofsm\n"); +- temp = 0; +- ret = af9005_write_tuner_registers(state->d, 0xffff, &temp, 1); +- if (ret) +- return ret; +- +- /* clear retrain and freeze flag */ +- deb_info("clear retrain and freeze flag\n"); +- ret = +- af9005_write_register_bits(state->d, +- xd_p_reg_api_retrain_request, +- reg_api_retrain_request_pos, 2, 0); +- if (ret) +- return ret; +- +- /* reset pre viterbi and post viterbi registers and statistics */ +- af9005_reset_pre_viterbi(fe); +- af9005_reset_post_viterbi(fe); +- state->pre_vit_error_count = 0; +- state->pre_vit_bit_count = 0; +- state->ber = 0; +- state->post_vit_error_count = 0; +- /* state->unc = 0; commented out since it should be ever increasing */ +- state->abort_count = 0; +- +- state->next_status_check = jiffies; +- state->strong = -1; +- +- return 0; +-} +- +-static int af9005_fe_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct af9005_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 temp; +- +- /* mode */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, +- reg_tpsd_const_pos, reg_tpsd_const_len, +- &temp); +- if (ret) +- return ret; +- deb_info("===== fe_get_frontend_legacy = =============\n"); +- deb_info("CONSTELLATION "); +- switch (temp) { +- case 0: +- fep->modulation = QPSK; +- deb_info("QPSK\n"); +- break; +- case 1: +- fep->modulation = QAM_16; +- deb_info("QAM_16\n"); +- break; +- case 2: +- fep->modulation = QAM_64; +- deb_info("QAM_64\n"); +- break; +- } +- +- /* tps hierarchy and alpha value */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_hier, +- reg_tpsd_hier_pos, reg_tpsd_hier_len, +- &temp); +- if (ret) +- return ret; +- deb_info("HIERARCHY "); +- switch (temp) { +- case 0: +- fep->hierarchy = HIERARCHY_NONE; +- deb_info("NONE\n"); +- break; +- case 1: +- fep->hierarchy = HIERARCHY_1; +- deb_info("1\n"); +- break; +- case 2: +- fep->hierarchy = HIERARCHY_2; +- deb_info("2\n"); +- break; +- case 3: +- fep->hierarchy = HIERARCHY_4; +- deb_info("4\n"); +- break; +- } +- +- /* high/low priority */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_dec_pri, +- reg_dec_pri_pos, reg_dec_pri_len, &temp); +- if (ret) +- return ret; +- /* if temp is set = high priority */ +- deb_info("PRIORITY %s\n", temp ? "high" : "low"); +- +- /* high coderate */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_hpcr, +- reg_tpsd_hpcr_pos, reg_tpsd_hpcr_len, +- &temp); +- if (ret) +- return ret; +- deb_info("CODERATE HP "); +- switch (temp) { +- case 0: +- fep->code_rate_HP = FEC_1_2; +- deb_info("FEC_1_2\n"); +- break; +- case 1: +- fep->code_rate_HP = FEC_2_3; +- deb_info("FEC_2_3\n"); +- break; +- case 2: +- fep->code_rate_HP = FEC_3_4; +- deb_info("FEC_3_4\n"); +- break; +- case 3: +- fep->code_rate_HP = FEC_5_6; +- deb_info("FEC_5_6\n"); +- break; +- case 4: +- fep->code_rate_HP = FEC_7_8; +- deb_info("FEC_7_8\n"); +- break; +- } +- +- /* low coderate */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_lpcr, +- reg_tpsd_lpcr_pos, reg_tpsd_lpcr_len, +- &temp); +- if (ret) +- return ret; +- deb_info("CODERATE LP "); +- switch (temp) { +- case 0: +- fep->code_rate_LP = FEC_1_2; +- deb_info("FEC_1_2\n"); +- break; +- case 1: +- fep->code_rate_LP = FEC_2_3; +- deb_info("FEC_2_3\n"); +- break; +- case 2: +- fep->code_rate_LP = FEC_3_4; +- deb_info("FEC_3_4\n"); +- break; +- case 3: +- fep->code_rate_LP = FEC_5_6; +- deb_info("FEC_5_6\n"); +- break; +- case 4: +- fep->code_rate_LP = FEC_7_8; +- deb_info("FEC_7_8\n"); +- break; +- } +- +- /* guard interval */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_gi, +- reg_tpsd_gi_pos, reg_tpsd_gi_len, &temp); +- if (ret) +- return ret; +- deb_info("GUARD INTERVAL "); +- switch (temp) { +- case 0: +- fep->guard_interval = GUARD_INTERVAL_1_32; +- deb_info("1_32\n"); +- break; +- case 1: +- fep->guard_interval = GUARD_INTERVAL_1_16; +- deb_info("1_16\n"); +- break; +- case 2: +- fep->guard_interval = GUARD_INTERVAL_1_8; +- deb_info("1_8\n"); +- break; +- case 3: +- fep->guard_interval = GUARD_INTERVAL_1_4; +- deb_info("1_4\n"); +- break; +- } +- +- /* fft */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, +- reg_tpsd_txmod_pos, reg_tpsd_txmod_len, +- &temp); +- if (ret) +- return ret; +- deb_info("TRANSMISSION MODE "); +- switch (temp) { +- case 0: +- fep->transmission_mode = TRANSMISSION_MODE_2K; +- deb_info("2K\n"); +- break; +- case 1: +- fep->transmission_mode = TRANSMISSION_MODE_8K; +- deb_info("8K\n"); +- break; +- } +- +- /* bandwidth */ +- ret = +- af9005_read_register_bits(state->d, xd_g_reg_bw, reg_bw_pos, +- reg_bw_len, &temp); +- deb_info("BANDWIDTH "); +- switch (temp) { +- case 0: +- fep->bandwidth_hz = 6000000; +- deb_info("6\n"); +- break; +- case 1: +- fep->bandwidth_hz = 7000000; +- deb_info("7\n"); +- break; +- case 2: +- fep->bandwidth_hz = 8000000; +- deb_info("8\n"); +- break; +- } +- return 0; +-} +- +-static void af9005_fe_release(struct dvb_frontend *fe) +-{ +- struct af9005_fe_state *state = +- (struct af9005_fe_state *)fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops af9005_fe_ops; +- +-struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d) +-{ +- struct af9005_fe_state *state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct af9005_fe_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- deb_info("attaching frontend af9005\n"); +- +- state->d = d; +- state->opened = 0; +- +- memcpy(&state->frontend.ops, &af9005_fe_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +- error: +- return NULL; +-} +- +-static struct dvb_frontend_ops af9005_fe_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "AF9005 USB DVB-T", +- .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 = af9005_fe_release, +- +- .init = af9005_fe_init, +- .sleep = af9005_fe_sleep, +- .ts_bus_ctrl = af9005_ts_bus_ctrl, +- +- .set_frontend = af9005_fe_set_frontend, +- .get_frontend = af9005_fe_get_frontend, +- +- .read_status = af9005_fe_read_status, +- .read_ber = af9005_fe_read_ber, +- .read_signal_strength = af9005_fe_read_signal_strength, +- .read_snr = af9005_fe_read_snr, +- .read_ucblocks = af9005_fe_read_unc_blocks, +-}; +diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c +deleted file mode 100644 +index 7e3961d..0000000 +--- a/drivers/media/dvb/dvb-usb/af9005-remote.c ++++ /dev/null +@@ -1,157 +0,0 @@ +-/* DVB USB compliant Linux driver for the Afatech 9005 +- * USB1.1 DVB-T receiver. +- * +- * Standard remote decode function +- * +- * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) +- * +- * Thanks to Afatech who kindly provided information. +- * +- * 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. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "af9005.h" +-/* debug */ +-static int dvb_usb_af9005_remote_debug; +-module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "enable (1) or disable (0) debug messages." +- DVB_USB_DEBUG_STATUS); +- +-#define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args) +- +-struct rc_map_table rc_map_af9005_table[] = { +- +- {0x01b7, KEY_POWER}, +- {0x01a7, KEY_VOLUMEUP}, +- {0x0187, KEY_CHANNELUP}, +- {0x017f, KEY_MUTE}, +- {0x01bf, KEY_VOLUMEDOWN}, +- {0x013f, KEY_CHANNELDOWN}, +- {0x01df, KEY_1}, +- {0x015f, KEY_2}, +- {0x019f, KEY_3}, +- {0x011f, KEY_4}, +- {0x01ef, KEY_5}, +- {0x016f, KEY_6}, +- {0x01af, KEY_7}, +- {0x0127, KEY_8}, +- {0x0107, KEY_9}, +- {0x01cf, KEY_ZOOM}, +- {0x014f, KEY_0}, +- {0x018f, KEY_GOTO}, /* marked jump on the remote */ +- +- {0x00bd, KEY_POWER}, +- {0x007d, KEY_VOLUMEUP}, +- {0x00fd, KEY_CHANNELUP}, +- {0x009d, KEY_MUTE}, +- {0x005d, KEY_VOLUMEDOWN}, +- {0x00dd, KEY_CHANNELDOWN}, +- {0x00ad, KEY_1}, +- {0x006d, KEY_2}, +- {0x00ed, KEY_3}, +- {0x008d, KEY_4}, +- {0x004d, KEY_5}, +- {0x00cd, KEY_6}, +- {0x00b5, KEY_7}, +- {0x0075, KEY_8}, +- {0x00f5, KEY_9}, +- {0x0095, KEY_ZOOM}, +- {0x0055, KEY_0}, +- {0x00d5, KEY_GOTO}, /* marked jump on the remote */ +-}; +- +-int rc_map_af9005_table_size = ARRAY_SIZE(rc_map_af9005_table); +- +-static int repeatable_keys[] = { +- KEY_VOLUMEUP, +- KEY_VOLUMEDOWN, +- KEY_CHANNELUP, +- KEY_CHANNELDOWN +-}; +- +-int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, +- int *state) +-{ +- u16 mark, space; +- u32 result; +- u8 cust, dat, invdat; +- int i; +- +- if (len >= 6) { +- mark = (u16) (data[0] << 8) + data[1]; +- space = (u16) (data[2] << 8) + data[3]; +- if (space * 3 < mark) { +- for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) { +- if (d->last_event == repeatable_keys[i]) { +- *state = REMOTE_KEY_REPEAT; +- *event = d->last_event; +- deb_decode("repeat key, event %x\n", +- *event); +- return 0; +- } +- } +- deb_decode("repeated key ignored (non repeatable)\n"); +- return 0; +- } else if (len >= 33 * 4) { /*32 bits + start code */ +- result = 0; +- for (i = 4; i < 4 + 32 * 4; i += 4) { +- result <<= 1; +- mark = (u16) (data[i] << 8) + data[i + 1]; +- mark >>= 1; +- space = (u16) (data[i + 2] << 8) + data[i + 3]; +- space >>= 1; +- if (mark * 2 > space) +- result += 1; +- } +- deb_decode("key pressed, raw value %x\n", result); +- if ((result & 0xff000000) != 0xfe000000) { +- deb_decode +- ("doesn't start with 0xfe, ignored\n"); +- return 0; +- } +- cust = (result >> 16) & 0xff; +- dat = (result >> 8) & 0xff; +- invdat = (~result) & 0xff; +- if (dat != invdat) { +- deb_decode("code != inverted code\n"); +- return 0; +- } +- for (i = 0; i < rc_map_af9005_table_size; i++) { +- if (rc5_custom(&rc_map_af9005_table[i]) == cust +- && rc5_data(&rc_map_af9005_table[i]) == dat) { +- *event = rc_map_af9005_table[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- deb_decode +- ("key pressed, event %x\n", *event); +- return 0; +- } +- } +- deb_decode("not found in table\n"); +- } +- } +- return 0; +-} +- +-EXPORT_SYMBOL(rc_map_af9005_table); +-EXPORT_SYMBOL(rc_map_af9005_table_size); +-EXPORT_SYMBOL(af9005_rc_decode); +- +-MODULE_AUTHOR("Luca Olivetti "); +-MODULE_DESCRIPTION +- ("Standard remote control decoder for Afatech 9005 DVB-T USB1.1 stick"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/af9005-script.h b/drivers/media/dvb/dvb-usb/af9005-script.h +deleted file mode 100644 +index 4d69045..0000000 +--- a/drivers/media/dvb/dvb-usb/af9005-script.h ++++ /dev/null +@@ -1,203 +0,0 @@ +-/* +-File automatically generated by createinit.py using data +-extracted from AF05BDA.sys (windows driver): +- +-dd if=AF05BDA.sys of=initsequence bs=1 skip=88316 count=1110 +-python createinit.py > af9005-script.h +- +-*/ +- +-typedef struct { +- u16 reg; +- u8 pos; +- u8 len; +- u8 val; +-} RegDesc; +- +-static RegDesc script[] = { +- {0xa180, 0x0, 0x8, 0xa}, +- {0xa181, 0x0, 0x8, 0xd7}, +- {0xa182, 0x0, 0x8, 0xa3}, +- {0xa0a0, 0x0, 0x8, 0x0}, +- {0xa0a1, 0x0, 0x5, 0x0}, +- {0xa0a1, 0x5, 0x1, 0x1}, +- {0xa0c0, 0x0, 0x4, 0x1}, +- {0xa20e, 0x4, 0x4, 0xa}, +- {0xa20f, 0x0, 0x8, 0x40}, +- {0xa210, 0x0, 0x8, 0x8}, +- {0xa32a, 0x0, 0x4, 0xa}, +- {0xa32c, 0x0, 0x8, 0x20}, +- {0xa32b, 0x0, 0x8, 0x15}, +- {0xa1a0, 0x1, 0x1, 0x1}, +- {0xa000, 0x0, 0x1, 0x1}, +- {0xa000, 0x1, 0x1, 0x0}, +- {0xa001, 0x1, 0x1, 0x1}, +- {0xa001, 0x0, 0x1, 0x0}, +- {0xa001, 0x5, 0x1, 0x0}, +- {0xa00e, 0x0, 0x5, 0x10}, +- {0xa00f, 0x0, 0x3, 0x4}, +- {0xa00f, 0x3, 0x3, 0x5}, +- {0xa010, 0x0, 0x3, 0x4}, +- {0xa010, 0x3, 0x3, 0x5}, +- {0xa016, 0x4, 0x4, 0x3}, +- {0xa01f, 0x0, 0x6, 0xa}, +- {0xa020, 0x0, 0x6, 0xa}, +- {0xa2bc, 0x0, 0x1, 0x1}, +- {0xa2bc, 0x5, 0x1, 0x1}, +- {0xa015, 0x0, 0x8, 0x50}, +- {0xa016, 0x0, 0x1, 0x0}, +- {0xa02a, 0x0, 0x8, 0x50}, +- {0xa029, 0x0, 0x8, 0x4b}, +- {0xa614, 0x0, 0x8, 0x46}, +- {0xa002, 0x0, 0x5, 0x19}, +- {0xa003, 0x0, 0x5, 0x1a}, +- {0xa004, 0x0, 0x5, 0x19}, +- {0xa005, 0x0, 0x5, 0x1a}, +- {0xa008, 0x0, 0x8, 0x69}, +- {0xa009, 0x0, 0x2, 0x2}, +- {0xae1b, 0x0, 0x8, 0x69}, +- {0xae1c, 0x0, 0x8, 0x2}, +- {0xae1d, 0x0, 0x8, 0x2a}, +- {0xa022, 0x0, 0x8, 0xaa}, +- {0xa006, 0x0, 0x8, 0xc8}, +- {0xa007, 0x0, 0x2, 0x0}, +- {0xa00c, 0x0, 0x8, 0xba}, +- {0xa00d, 0x0, 0x2, 0x2}, +- {0xa608, 0x0, 0x8, 0xba}, +- {0xa60e, 0x0, 0x2, 0x2}, +- {0xa609, 0x0, 0x8, 0x80}, +- {0xa60e, 0x2, 0x2, 0x3}, +- {0xa00a, 0x0, 0x8, 0xb6}, +- {0xa00b, 0x0, 0x2, 0x0}, +- {0xa011, 0x0, 0x8, 0xb9}, +- {0xa012, 0x0, 0x2, 0x0}, +- {0xa013, 0x0, 0x8, 0xbd}, +- {0xa014, 0x0, 0x2, 0x2}, +- {0xa366, 0x0, 0x1, 0x1}, +- {0xa2bc, 0x3, 0x1, 0x0}, +- {0xa2bd, 0x0, 0x8, 0xa}, +- {0xa2be, 0x0, 0x8, 0x14}, +- {0xa2bf, 0x0, 0x8, 0x8}, +- {0xa60a, 0x0, 0x8, 0xbd}, +- {0xa60e, 0x4, 0x2, 0x2}, +- {0xa60b, 0x0, 0x8, 0x86}, +- {0xa60e, 0x6, 0x2, 0x3}, +- {0xa001, 0x2, 0x2, 0x1}, +- {0xa1c7, 0x0, 0x8, 0xf5}, +- {0xa03d, 0x0, 0x8, 0xb1}, +- {0xa616, 0x0, 0x8, 0xff}, +- {0xa617, 0x0, 0x8, 0xad}, +- {0xa618, 0x0, 0x8, 0xad}, +- {0xa61e, 0x3, 0x1, 0x1}, +- {0xae1a, 0x0, 0x8, 0x0}, +- {0xae19, 0x0, 0x8, 0xc8}, +- {0xae18, 0x0, 0x8, 0x61}, +- {0xa140, 0x0, 0x8, 0x0}, +- {0xa141, 0x0, 0x8, 0xc8}, +- {0xa142, 0x0, 0x7, 0x61}, +- {0xa023, 0x0, 0x8, 0xff}, +- {0xa021, 0x0, 0x8, 0xad}, +- {0xa026, 0x0, 0x1, 0x0}, +- {0xa024, 0x0, 0x8, 0xff}, +- {0xa025, 0x0, 0x8, 0xff}, +- {0xa1c8, 0x0, 0x8, 0xf}, +- {0xa2bc, 0x1, 0x1, 0x0}, +- {0xa60c, 0x0, 0x4, 0x5}, +- {0xa60c, 0x4, 0x4, 0x6}, +- {0xa60d, 0x0, 0x8, 0xa}, +- {0xa371, 0x0, 0x1, 0x1}, +- {0xa366, 0x1, 0x3, 0x7}, +- {0xa338, 0x0, 0x8, 0x10}, +- {0xa339, 0x0, 0x6, 0x7}, +- {0xa33a, 0x0, 0x6, 0x1f}, +- {0xa33b, 0x0, 0x8, 0xf6}, +- {0xa33c, 0x3, 0x5, 0x4}, +- {0xa33d, 0x4, 0x4, 0x0}, +- {0xa33d, 0x1, 0x1, 0x1}, +- {0xa33d, 0x2, 0x1, 0x1}, +- {0xa33d, 0x3, 0x1, 0x1}, +- {0xa16d, 0x0, 0x4, 0xf}, +- {0xa161, 0x0, 0x5, 0x5}, +- {0xa162, 0x0, 0x4, 0x5}, +- {0xa165, 0x0, 0x8, 0xff}, +- {0xa166, 0x0, 0x8, 0x9c}, +- {0xa2c3, 0x0, 0x4, 0x5}, +- {0xa61a, 0x0, 0x6, 0xf}, +- {0xb200, 0x0, 0x8, 0xa1}, +- {0xb201, 0x0, 0x8, 0x7}, +- {0xa093, 0x0, 0x1, 0x0}, +- {0xa093, 0x1, 0x5, 0xf}, +- {0xa094, 0x0, 0x8, 0xff}, +- {0xa095, 0x0, 0x8, 0xf}, +- {0xa080, 0x2, 0x5, 0x3}, +- {0xa081, 0x0, 0x4, 0x0}, +- {0xa081, 0x4, 0x4, 0x9}, +- {0xa082, 0x0, 0x5, 0x1f}, +- {0xa08d, 0x0, 0x8, 0x1}, +- {0xa083, 0x0, 0x8, 0x32}, +- {0xa084, 0x0, 0x1, 0x0}, +- {0xa08e, 0x0, 0x8, 0x3}, +- {0xa085, 0x0, 0x8, 0x32}, +- {0xa086, 0x0, 0x3, 0x0}, +- {0xa087, 0x0, 0x8, 0x6e}, +- {0xa088, 0x0, 0x5, 0x15}, +- {0xa089, 0x0, 0x8, 0x0}, +- {0xa08a, 0x0, 0x5, 0x19}, +- {0xa08b, 0x0, 0x8, 0x92}, +- {0xa08c, 0x0, 0x5, 0x1c}, +- {0xa120, 0x0, 0x8, 0x0}, +- {0xa121, 0x0, 0x5, 0x10}, +- {0xa122, 0x0, 0x8, 0x0}, +- {0xa123, 0x0, 0x7, 0x40}, +- {0xa123, 0x7, 0x1, 0x0}, +- {0xa124, 0x0, 0x8, 0x13}, +- {0xa125, 0x0, 0x7, 0x10}, +- {0xa1c0, 0x0, 0x8, 0x0}, +- {0xa1c1, 0x0, 0x5, 0x4}, +- {0xa1c2, 0x0, 0x8, 0x0}, +- {0xa1c3, 0x0, 0x5, 0x10}, +- {0xa1c3, 0x5, 0x3, 0x0}, +- {0xa1c4, 0x0, 0x6, 0x0}, +- {0xa1c5, 0x0, 0x7, 0x10}, +- {0xa100, 0x0, 0x8, 0x0}, +- {0xa101, 0x0, 0x5, 0x10}, +- {0xa102, 0x0, 0x8, 0x0}, +- {0xa103, 0x0, 0x7, 0x40}, +- {0xa103, 0x7, 0x1, 0x0}, +- {0xa104, 0x0, 0x8, 0x18}, +- {0xa105, 0x0, 0x7, 0xa}, +- {0xa106, 0x0, 0x8, 0x20}, +- {0xa107, 0x0, 0x8, 0x40}, +- {0xa108, 0x0, 0x4, 0x0}, +- {0xa38c, 0x0, 0x8, 0xfc}, +- {0xa38d, 0x0, 0x8, 0x0}, +- {0xa38e, 0x0, 0x8, 0x7e}, +- {0xa38f, 0x0, 0x8, 0x0}, +- {0xa390, 0x0, 0x8, 0x2f}, +- {0xa60f, 0x5, 0x1, 0x1}, +- {0xa170, 0x0, 0x8, 0xdc}, +- {0xa171, 0x0, 0x2, 0x0}, +- {0xa2ae, 0x0, 0x1, 0x1}, +- {0xa2ae, 0x1, 0x1, 0x1}, +- {0xa392, 0x0, 0x1, 0x1}, +- {0xa391, 0x2, 0x1, 0x0}, +- {0xabc1, 0x0, 0x8, 0xff}, +- {0xabc2, 0x0, 0x8, 0x0}, +- {0xabc8, 0x0, 0x8, 0x8}, +- {0xabca, 0x0, 0x8, 0x10}, +- {0xabcb, 0x0, 0x1, 0x0}, +- {0xabc3, 0x5, 0x3, 0x7}, +- {0xabc0, 0x6, 0x1, 0x0}, +- {0xabc0, 0x4, 0x2, 0x0}, +- {0xa344, 0x4, 0x4, 0x1}, +- {0xabc0, 0x7, 0x1, 0x1}, +- {0xabc0, 0x2, 0x1, 0x1}, +- {0xa345, 0x0, 0x8, 0x66}, +- {0xa346, 0x0, 0x8, 0x66}, +- {0xa347, 0x0, 0x4, 0x0}, +- {0xa343, 0x0, 0x4, 0xa}, +- {0xa347, 0x4, 0x4, 0x2}, +- {0xa348, 0x0, 0x4, 0xc}, +- {0xa348, 0x4, 0x4, 0x7}, +- {0xa349, 0x0, 0x6, 0x2}, +-}; +diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c +deleted file mode 100644 +index af176b6..0000000 +--- a/drivers/media/dvb/dvb-usb/af9005.c ++++ /dev/null +@@ -1,1117 +0,0 @@ +-/* DVB USB compliant Linux driver for the Afatech 9005 +- * USB1.1 DVB-T receiver. +- * +- * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) +- * +- * Thanks to Afatech who kindly provided information. +- * +- * 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. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "af9005.h" +- +-/* debug */ +-int dvb_usb_af9005_debug; +-module_param_named(debug, dvb_usb_af9005_debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." +- DVB_USB_DEBUG_STATUS); +-/* enable obnoxious led */ +-bool dvb_usb_af9005_led = 1; +-module_param_named(led, dvb_usb_af9005_led, bool, 0644); +-MODULE_PARM_DESC(led, "enable led (default: 1)."); +- +-/* eeprom dump */ +-static int dvb_usb_af9005_dump_eeprom; +-module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0); +-MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-/* remote control decoder */ +-static int (*rc_decode) (struct dvb_usb_device *d, u8 *data, int len, +- u32 *event, int *state); +-static void *rc_keys; +-static int *rc_keys_size; +- +-u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; +- +-struct af9005_device_state { +- u8 sequence; +- int led_state; +-}; +- +-static int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, +- int readwrite, int type, u8 * values, int len) +-{ +- struct af9005_device_state *st = d->priv; +- u8 obuf[16] = { 0 }; +- u8 ibuf[17] = { 0 }; +- u8 command; +- int i; +- int ret; +- +- if (len < 1) { +- err("generic read/write, less than 1 byte. Makes no sense."); +- return -EINVAL; +- } +- if (len > 8) { +- err("generic read/write, more than 8 bytes. Not supported."); +- return -EINVAL; +- } +- +- obuf[0] = 14; /* rest of buffer length low */ +- obuf[1] = 0; /* rest of buffer length high */ +- +- obuf[2] = AF9005_REGISTER_RW; /* register operation */ +- obuf[3] = 12; /* rest of buffer length */ +- +- obuf[4] = st->sequence++; /* sequence number */ +- +- obuf[5] = (u8) (reg >> 8); /* register address */ +- obuf[6] = (u8) (reg & 0xff); +- +- if (type == AF9005_OFDM_REG) { +- command = AF9005_CMD_OFDM_REG; +- } else { +- command = AF9005_CMD_TUNER; +- } +- +- if (len > 1) +- command |= +- AF9005_CMD_BURST | AF9005_CMD_AUTOINC | (len - 1) << 3; +- command |= readwrite; +- if (readwrite == AF9005_CMD_WRITE) +- for (i = 0; i < len; i++) +- obuf[8 + i] = values[i]; +- else if (type == AF9005_TUNER_REG) +- /* read command for tuner, the first byte contains the i2c address */ +- obuf[8] = values[0]; +- obuf[7] = command; +- +- ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); +- if (ret) +- return ret; +- +- /* sanity check */ +- if (ibuf[2] != AF9005_REGISTER_RW_ACK) { +- err("generic read/write, wrong reply code."); +- return -EIO; +- } +- if (ibuf[3] != 0x0d) { +- err("generic read/write, wrong length in reply."); +- return -EIO; +- } +- if (ibuf[4] != obuf[4]) { +- err("generic read/write, wrong sequence in reply."); +- return -EIO; +- } +- /* +- Windows driver doesn't check these fields, in fact sometimes +- the register in the reply is different that what has been sent +- +- if (ibuf[5] != obuf[5] || ibuf[6] != obuf[6]) { +- err("generic read/write, wrong register in reply."); +- return -EIO; +- } +- if (ibuf[7] != command) { +- err("generic read/write wrong command in reply."); +- return -EIO; +- } +- */ +- if (ibuf[16] != 0x01) { +- err("generic read/write wrong status code in reply."); +- return -EIO; +- } +- if (readwrite == AF9005_CMD_READ) +- for (i = 0; i < len; i++) +- values[i] = ibuf[8 + i]; +- +- return 0; +- +-} +- +-int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 * value) +-{ +- int ret; +- deb_reg("read register %x ", reg); +- ret = af9005_generic_read_write(d, reg, +- AF9005_CMD_READ, AF9005_OFDM_REG, +- value, 1); +- if (ret) +- deb_reg("failed\n"); +- else +- deb_reg("value %x\n", *value); +- return ret; +-} +- +-int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len) +-{ +- int ret; +- deb_reg("read %d registers %x ", len, reg); +- ret = af9005_generic_read_write(d, reg, +- AF9005_CMD_READ, AF9005_OFDM_REG, +- values, len); +- if (ret) +- deb_reg("failed\n"); +- else +- debug_dump(values, len, deb_reg); +- return ret; +-} +- +-int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 value) +-{ +- int ret; +- u8 temp = value; +- deb_reg("write register %x value %x ", reg, value); +- ret = af9005_generic_read_write(d, reg, +- AF9005_CMD_WRITE, AF9005_OFDM_REG, +- &temp, 1); +- if (ret) +- deb_reg("failed\n"); +- else +- deb_reg("ok\n"); +- return ret; +-} +- +-int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len) +-{ +- int ret; +- deb_reg("write %d registers %x values ", len, reg); +- debug_dump(values, len, deb_reg); +- +- ret = af9005_generic_read_write(d, reg, +- AF9005_CMD_WRITE, AF9005_OFDM_REG, +- values, len); +- if (ret) +- deb_reg("failed\n"); +- else +- deb_reg("ok\n"); +- return ret; +-} +- +-int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, +- u8 len, u8 * value) +-{ +- u8 temp; +- int ret; +- deb_reg("read bits %x %x %x", reg, pos, len); +- ret = af9005_read_ofdm_register(d, reg, &temp); +- if (ret) { +- deb_reg(" failed\n"); +- return ret; +- } +- *value = (temp >> pos) & regmask[len - 1]; +- deb_reg(" value %x\n", *value); +- return 0; +- +-} +- +-int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, +- u8 len, u8 value) +-{ +- u8 temp, mask; +- int ret; +- deb_reg("write bits %x %x %x value %x\n", reg, pos, len, value); +- if (pos == 0 && len == 8) +- return af9005_write_ofdm_register(d, reg, value); +- ret = af9005_read_ofdm_register(d, reg, &temp); +- if (ret) +- return ret; +- mask = regmask[len - 1] << pos; +- temp = (temp & ~mask) | ((value << pos) & mask); +- return af9005_write_ofdm_register(d, reg, temp); +- +-} +- +-static int af9005_usb_read_tuner_registers(struct dvb_usb_device *d, +- u16 reg, u8 * values, int len) +-{ +- return af9005_generic_read_write(d, reg, +- AF9005_CMD_READ, AF9005_TUNER_REG, +- values, len); +-} +- +-static int af9005_usb_write_tuner_registers(struct dvb_usb_device *d, +- u16 reg, u8 * values, int len) +-{ +- return af9005_generic_read_write(d, reg, +- AF9005_CMD_WRITE, +- AF9005_TUNER_REG, values, len); +-} +- +-int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len) +-{ +- /* don't let the name of this function mislead you: it's just used +- as an interface from the firmware to the i2c bus. The actual +- i2c addresses are contained in the data */ +- int ret, i, done = 0, fail = 0; +- u8 temp; +- ret = af9005_usb_write_tuner_registers(d, reg, values, len); +- if (ret) +- return ret; +- if (reg != 0xffff) { +- /* check if write done (0xa40d bit 1) or fail (0xa40d bit 2) */ +- for (i = 0; i < 200; i++) { +- ret = +- af9005_read_ofdm_register(d, +- xd_I2C_i2c_m_status_wdat_done, +- &temp); +- if (ret) +- return ret; +- done = temp & (regmask[i2c_m_status_wdat_done_len - 1] +- << i2c_m_status_wdat_done_pos); +- if (done) +- break; +- fail = temp & (regmask[i2c_m_status_wdat_fail_len - 1] +- << i2c_m_status_wdat_fail_pos); +- if (fail) +- break; +- msleep(50); +- } +- if (i == 200) +- return -ETIMEDOUT; +- if (fail) { +- /* clear write fail bit */ +- af9005_write_register_bits(d, +- xd_I2C_i2c_m_status_wdat_fail, +- i2c_m_status_wdat_fail_pos, +- i2c_m_status_wdat_fail_len, +- 1); +- return -EIO; +- } +- /* clear write done bit */ +- ret = +- af9005_write_register_bits(d, +- xd_I2C_i2c_m_status_wdat_fail, +- i2c_m_status_wdat_done_pos, +- i2c_m_status_wdat_done_len, 1); +- if (ret) +- return ret; +- } +- return 0; +-} +- +-int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 addr, +- u8 * values, int len) +-{ +- /* don't let the name of this function mislead you: it's just used +- as an interface from the firmware to the i2c bus. The actual +- i2c addresses are contained in the data */ +- int ret, i; +- u8 temp, buf[2]; +- +- buf[0] = addr; /* tuner i2c address */ +- buf[1] = values[0]; /* tuner register */ +- +- values[0] = addr + 0x01; /* i2c read address */ +- +- if (reg == APO_REG_I2C_RW_SILICON_TUNER) { +- /* write tuner i2c address to tuner, 0c00c0 undocumented, found by sniffing */ +- ret = af9005_write_tuner_registers(d, 0x00c0, buf, 2); +- if (ret) +- return ret; +- } +- +- /* send read command to ofsm */ +- ret = af9005_usb_read_tuner_registers(d, reg, values, 1); +- if (ret) +- return ret; +- +- /* check if read done */ +- for (i = 0; i < 200; i++) { +- ret = af9005_read_ofdm_register(d, 0xa408, &temp); +- if (ret) +- return ret; +- if (temp & 0x01) +- break; +- msleep(50); +- } +- if (i == 200) +- return -ETIMEDOUT; +- +- /* clear read done bit (by writing 1) */ +- ret = af9005_write_ofdm_register(d, xd_I2C_i2c_m_data8, 1); +- if (ret) +- return ret; +- +- /* get read data (available from 0xa400) */ +- for (i = 0; i < len; i++) { +- ret = af9005_read_ofdm_register(d, 0xa400 + i, &temp); +- if (ret) +- return ret; +- values[i] = temp; +- } +- return 0; +-} +- +-static int af9005_i2c_write(struct dvb_usb_device *d, u8 i2caddr, u8 reg, +- u8 * data, int len) +-{ +- int ret, i; +- u8 buf[3]; +- deb_i2c("i2c_write i2caddr %x, reg %x, len %d data ", i2caddr, +- reg, len); +- debug_dump(data, len, deb_i2c); +- +- for (i = 0; i < len; i++) { +- buf[0] = i2caddr; +- buf[1] = reg + (u8) i; +- buf[2] = data[i]; +- ret = +- af9005_write_tuner_registers(d, +- APO_REG_I2C_RW_SILICON_TUNER, +- buf, 3); +- if (ret) { +- deb_i2c("i2c_write failed\n"); +- return ret; +- } +- } +- deb_i2c("i2c_write ok\n"); +- return 0; +-} +- +-static int af9005_i2c_read(struct dvb_usb_device *d, u8 i2caddr, u8 reg, +- u8 * data, int len) +-{ +- int ret, i; +- u8 temp; +- deb_i2c("i2c_read i2caddr %x, reg %x, len %d\n ", i2caddr, reg, len); +- for (i = 0; i < len; i++) { +- temp = reg + i; +- ret = +- af9005_read_tuner_registers(d, +- APO_REG_I2C_RW_SILICON_TUNER, +- i2caddr, &temp, 1); +- if (ret) { +- deb_i2c("i2c_read failed\n"); +- return ret; +- } +- data[i] = temp; +- } +- deb_i2c("i2c data read: "); +- debug_dump(data, len, deb_i2c); +- return 0; +-} +- +-static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- /* only implements what the mt2060 module does, don't know how +- to make it really generic */ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret; +- u8 reg, addr; +- u8 *value; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- if (num > 2) +- warn("more than 2 i2c messages at a time is not handled yet. TODO."); +- +- if (num == 2) { +- /* reads a single register */ +- reg = *msg[0].buf; +- addr = msg[0].addr; +- value = msg[1].buf; +- ret = af9005_i2c_read(d, addr, reg, value, 1); +- if (ret == 0) +- ret = 2; +- } else { +- /* write one or more registers */ +- reg = msg[0].buf[0]; +- addr = msg[0].addr; +- value = &msg[0].buf[1]; +- ret = af9005_i2c_write(d, addr, reg, value, msg[0].len - 1); +- if (ret == 0) +- ret = 1; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return ret; +-} +- +-static u32 af9005_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm af9005_i2c_algo = { +- .master_xfer = af9005_i2c_xfer, +- .functionality = af9005_i2c_func, +-}; +- +-int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, +- int wlen, u8 * rbuf, int rlen) +-{ +- struct af9005_device_state *st = d->priv; +- +- int ret, i, packet_len; +- u8 buf[64]; +- u8 ibuf[64]; +- +- if (wlen < 0) { +- err("send command, wlen less than 0 bytes. Makes no sense."); +- return -EINVAL; +- } +- if (wlen > 54) { +- err("send command, wlen more than 54 bytes. Not supported."); +- return -EINVAL; +- } +- if (rlen > 54) { +- err("send command, rlen more than 54 bytes. Not supported."); +- return -EINVAL; +- } +- packet_len = wlen + 5; +- buf[0] = (u8) (packet_len & 0xff); +- buf[1] = (u8) ((packet_len & 0xff00) >> 8); +- +- buf[2] = 0x26; /* packet type */ +- buf[3] = wlen + 3; +- buf[4] = st->sequence++; +- buf[5] = command; +- buf[6] = wlen; +- for (i = 0; i < wlen; i++) +- buf[7 + i] = wbuf[i]; +- ret = dvb_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); +- if (ret) +- return ret; +- if (ibuf[2] != 0x27) { +- err("send command, wrong reply code."); +- return -EIO; +- } +- if (ibuf[4] != buf[4]) { +- err("send command, wrong sequence in reply."); +- return -EIO; +- } +- if (ibuf[5] != 0x01) { +- err("send command, wrong status code in reply."); +- return -EIO; +- } +- if (ibuf[6] != rlen) { +- err("send command, invalid data length in reply."); +- return -EIO; +- } +- for (i = 0; i < rlen; i++) +- rbuf[i] = ibuf[i + 7]; +- return 0; +-} +- +-int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, +- int len) +-{ +- struct af9005_device_state *st = d->priv; +- u8 obuf[16], ibuf[14]; +- int ret, i; +- +- memset(obuf, 0, sizeof(obuf)); +- memset(ibuf, 0, sizeof(ibuf)); +- +- obuf[0] = 14; /* length of rest of packet low */ +- obuf[1] = 0; /* length of rest of packer high */ +- +- obuf[2] = 0x2a; /* read/write eeprom */ +- +- obuf[3] = 12; /* size */ +- +- obuf[4] = st->sequence++; +- +- obuf[5] = 0; /* read */ +- +- obuf[6] = len; +- obuf[7] = address; +- ret = dvb_usb_generic_rw(d, obuf, 16, ibuf, 14, 0); +- if (ret) +- return ret; +- if (ibuf[2] != 0x2b) { +- err("Read eeprom, invalid reply code"); +- return -EIO; +- } +- if (ibuf[3] != 10) { +- err("Read eeprom, invalid reply length"); +- return -EIO; +- } +- if (ibuf[4] != obuf[4]) { +- err("Read eeprom, wrong sequence in reply "); +- return -EIO; +- } +- if (ibuf[5] != 1) { +- err("Read eeprom, wrong status in reply "); +- return -EIO; +- } +- for (i = 0; i < len; i++) { +- values[i] = ibuf[6 + i]; +- } +- return 0; +-} +- +-static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply) +-{ +- u8 buf[FW_BULKOUT_SIZE + 2]; +- u16 checksum; +- int act_len, i, ret; +- memset(buf, 0, sizeof(buf)); +- buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); +- buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); +- switch (type) { +- case FW_CONFIG: +- buf[2] = 0x11; +- buf[3] = 0x04; +- buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ +- buf[5] = 0x03; +- checksum = buf[4] + buf[5]; +- buf[6] = (u8) ((checksum >> 8) & 0xff); +- buf[7] = (u8) (checksum & 0xff); +- break; +- case FW_CONFIRM: +- buf[2] = 0x11; +- buf[3] = 0x04; +- buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ +- buf[5] = 0x01; +- checksum = buf[4] + buf[5]; +- buf[6] = (u8) ((checksum >> 8) & 0xff); +- buf[7] = (u8) (checksum & 0xff); +- break; +- case FW_BOOT: +- buf[2] = 0x10; +- buf[3] = 0x08; +- buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ +- buf[5] = 0x97; +- buf[6] = 0xaa; +- buf[7] = 0x55; +- buf[8] = 0xa5; +- buf[9] = 0x5a; +- checksum = 0; +- for (i = 4; i <= 9; i++) +- checksum += buf[i]; +- buf[10] = (u8) ((checksum >> 8) & 0xff); +- buf[11] = (u8) (checksum & 0xff); +- break; +- default: +- err("boot packet invalid boot packet type"); +- return -EINVAL; +- } +- deb_fw(">>> "); +- debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); +- +- ret = usb_bulk_msg(udev, +- usb_sndbulkpipe(udev, 0x02), +- buf, FW_BULKOUT_SIZE + 2, &act_len, 2000); +- if (ret) +- err("boot packet bulk message failed: %d (%d/%d)", ret, +- FW_BULKOUT_SIZE + 2, act_len); +- else +- ret = act_len != FW_BULKOUT_SIZE + 2 ? -1 : 0; +- if (ret) +- return ret; +- memset(buf, 0, 9); +- ret = usb_bulk_msg(udev, +- usb_rcvbulkpipe(udev, 0x01), buf, 9, &act_len, 2000); +- if (ret) { +- err("boot packet recv bulk message failed: %d", ret); +- return ret; +- } +- deb_fw("<<< "); +- debug_dump(buf, act_len, deb_fw); +- checksum = 0; +- switch (type) { +- case FW_CONFIG: +- if (buf[2] != 0x11) { +- err("boot bad config header."); +- return -EIO; +- } +- if (buf[3] != 0x05) { +- err("boot bad config size."); +- return -EIO; +- } +- if (buf[4] != 0x00) { +- err("boot bad config sequence."); +- return -EIO; +- } +- if (buf[5] != 0x04) { +- err("boot bad config subtype."); +- return -EIO; +- } +- for (i = 4; i <= 6; i++) +- checksum += buf[i]; +- if (buf[7] * 256 + buf[8] != checksum) { +- err("boot bad config checksum."); +- return -EIO; +- } +- *reply = buf[6]; +- break; +- case FW_CONFIRM: +- if (buf[2] != 0x11) { +- err("boot bad confirm header."); +- return -EIO; +- } +- if (buf[3] != 0x05) { +- err("boot bad confirm size."); +- return -EIO; +- } +- if (buf[4] != 0x00) { +- err("boot bad confirm sequence."); +- return -EIO; +- } +- if (buf[5] != 0x02) { +- err("boot bad confirm subtype."); +- return -EIO; +- } +- for (i = 4; i <= 6; i++) +- checksum += buf[i]; +- if (buf[7] * 256 + buf[8] != checksum) { +- err("boot bad confirm checksum."); +- return -EIO; +- } +- *reply = buf[6]; +- break; +- case FW_BOOT: +- if (buf[2] != 0x10) { +- err("boot bad boot header."); +- return -EIO; +- } +- if (buf[3] != 0x05) { +- err("boot bad boot size."); +- return -EIO; +- } +- if (buf[4] != 0x00) { +- err("boot bad boot sequence."); +- return -EIO; +- } +- if (buf[5] != 0x01) { +- err("boot bad boot pattern 01."); +- return -EIO; +- } +- if (buf[6] != 0x10) { +- err("boot bad boot pattern 10."); +- return -EIO; +- } +- for (i = 4; i <= 6; i++) +- checksum += buf[i]; +- if (buf[7] * 256 + buf[8] != checksum) { +- err("boot bad boot checksum."); +- return -EIO; +- } +- break; +- +- } +- +- return 0; +-} +- +-static int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) +-{ +- int i, packets, ret, act_len; +- +- u8 buf[FW_BULKOUT_SIZE + 2]; +- u8 reply; +- +- ret = af9005_boot_packet(udev, FW_CONFIG, &reply); +- if (ret) +- return ret; +- if (reply != 0x01) { +- err("before downloading firmware, FW_CONFIG expected 0x01, received 0x%x", reply); +- return -EIO; +- } +- packets = fw->size / FW_BULKOUT_SIZE; +- buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); +- buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); +- for (i = 0; i < packets; i++) { +- memcpy(&buf[2], fw->data + i * FW_BULKOUT_SIZE, +- FW_BULKOUT_SIZE); +- deb_fw(">>> "); +- debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); +- ret = usb_bulk_msg(udev, +- usb_sndbulkpipe(udev, 0x02), +- buf, FW_BULKOUT_SIZE + 2, &act_len, 1000); +- if (ret) { +- err("firmware download failed at packet %d with code %d", i, ret); +- return ret; +- } +- } +- ret = af9005_boot_packet(udev, FW_CONFIRM, &reply); +- if (ret) +- return ret; +- if (reply != (u8) (packets & 0xff)) { +- err("after downloading firmware, FW_CONFIRM expected 0x%x, received 0x%x", packets & 0xff, reply); +- return -EIO; +- } +- ret = af9005_boot_packet(udev, FW_BOOT, &reply); +- if (ret) +- return ret; +- ret = af9005_boot_packet(udev, FW_CONFIG, &reply); +- if (ret) +- return ret; +- if (reply != 0x02) { +- err("after downloading firmware, FW_CONFIG expected 0x02, received 0x%x", reply); +- return -EIO; +- } +- +- return 0; +- +-} +- +-int af9005_led_control(struct dvb_usb_device *d, int onoff) +-{ +- struct af9005_device_state *st = d->priv; +- int temp, ret; +- +- if (onoff && dvb_usb_af9005_led) +- temp = 1; +- else +- temp = 0; +- if (st->led_state != temp) { +- ret = +- af9005_write_register_bits(d, xd_p_reg_top_locken1, +- reg_top_locken1_pos, +- reg_top_locken1_len, temp); +- if (ret) +- return ret; +- ret = +- af9005_write_register_bits(d, xd_p_reg_top_lock1, +- reg_top_lock1_pos, +- reg_top_lock1_len, temp); +- if (ret) +- return ret; +- st->led_state = temp; +- } +- return 0; +-} +- +-static int af9005_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- u8 buf[8]; +- int i; +- +- /* without these calls the first commands after downloading +- the firmware fail. I put these calls here to simulate +- what it is done in dvb-usb-init.c. +- */ +- struct usb_device *udev = adap->dev->udev; +- usb_clear_halt(udev, usb_sndbulkpipe(udev, 2)); +- usb_clear_halt(udev, usb_rcvbulkpipe(udev, 1)); +- if (dvb_usb_af9005_dump_eeprom) { +- printk("EEPROM DUMP\n"); +- for (i = 0; i < 255; i += 8) { +- af9005_read_eeprom(adap->dev, i, buf, 8); +- printk("ADDR %x ", i); +- debug_dump(buf, 8, printk); +- } +- } +- adap->fe_adap[0].fe = af9005_fe_attach(adap->dev); +- return 0; +-} +- +-static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) +-{ +- struct af9005_device_state *st = d->priv; +- int ret, len; +- +- u8 obuf[5]; +- u8 ibuf[256]; +- +- *state = REMOTE_NO_KEY_PRESSED; +- if (rc_decode == NULL) { +- /* it shouldn't never come here */ +- return 0; +- } +- /* deb_info("rc_query\n"); */ +- obuf[0] = 3; /* rest of packet length low */ +- obuf[1] = 0; /* rest of packet lentgh high */ +- obuf[2] = 0x40; /* read remote */ +- obuf[3] = 1; /* rest of packet length */ +- obuf[4] = st->sequence++; /* sequence number */ +- ret = dvb_usb_generic_rw(d, obuf, 5, ibuf, 256, 0); +- if (ret) { +- err("rc query failed"); +- return ret; +- } +- if (ibuf[2] != 0x41) { +- err("rc query bad header."); +- return -EIO; +- } +- if (ibuf[4] != obuf[4]) { +- err("rc query bad sequence."); +- return -EIO; +- } +- len = ibuf[5]; +- if (len > 246) { +- err("rc query invalid length"); +- return -EIO; +- } +- if (len > 0) { +- deb_rc("rc data (%d) ", len); +- debug_dump((ibuf + 6), len, deb_rc); +- ret = rc_decode(d, &ibuf[6], len, event, state); +- if (ret) { +- err("rc_decode failed"); +- return ret; +- } else { +- deb_rc("rc_decode state %x event %x\n", *state, *event); +- if (*state == REMOTE_KEY_REPEAT) +- *event = d->last_event; +- } +- } +- return 0; +-} +- +-static int af9005_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- +- return 0; +-} +- +-static int af9005_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret; +- deb_info("pid filter control onoff %d\n", onoff); +- if (onoff) { +- ret = +- af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); +- if (ret) +- return ret; +- ret = +- af9005_write_register_bits(adap->dev, +- XD_MP2IF_DMX_CTRL, 1, 1, 1); +- if (ret) +- return ret; +- ret = +- af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); +- } else +- ret = +- af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 0); +- if (ret) +- return ret; +- deb_info("pid filter control ok\n"); +- return 0; +-} +- +-static int af9005_pid_filter(struct dvb_usb_adapter *adap, int index, +- u16 pid, int onoff) +-{ +- u8 cmd = index & 0x1f; +- int ret; +- deb_info("set pid filter, index %d, pid %x, onoff %d\n", index, +- pid, onoff); +- if (onoff) { +- /* cannot use it as pid_filter_ctrl since it has to be done +- before setting the first pid */ +- if (adap->feedcount == 1) { +- deb_info("first pid set, enable pid table\n"); +- ret = af9005_pid_filter_control(adap, onoff); +- if (ret) +- return ret; +- } +- ret = +- af9005_write_ofdm_register(adap->dev, +- XD_MP2IF_PID_DATA_L, +- (u8) (pid & 0xff)); +- if (ret) +- return ret; +- ret = +- af9005_write_ofdm_register(adap->dev, +- XD_MP2IF_PID_DATA_H, +- (u8) (pid >> 8)); +- if (ret) +- return ret; +- cmd |= 0x20 | 0x40; +- } else { +- if (adap->feedcount == 0) { +- deb_info("last pid unset, disable pid table\n"); +- ret = af9005_pid_filter_control(adap, onoff); +- if (ret) +- return ret; +- } +- } +- ret = af9005_write_ofdm_register(adap->dev, XD_MP2IF_PID_IDX, cmd); +- if (ret) +- return ret; +- deb_info("set pid ok\n"); +- return 0; +-} +- +-static int af9005_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- int ret; +- u8 reply; +- ret = af9005_boot_packet(udev, FW_CONFIG, &reply); +- if (ret) +- return ret; +- deb_info("result of FW_CONFIG in identify state %d\n", reply); +- if (reply == 0x01) +- *cold = 1; +- else if (reply == 0x02) +- *cold = 0; +- else +- return -EIO; +- deb_info("Identify state cold = %d\n", *cold); +- return 0; +-} +- +-static struct dvb_usb_device_properties af9005_properties; +- +-static int af9005_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &af9005_properties, +- THIS_MODULE, NULL, adapter_nr); +-} +- +-enum af9005_usb_table_entry { +- AFATECH_AF9005, +- TERRATEC_AF9005, +- ANSONIC_AF9005, +-}; +- +-static struct usb_device_id af9005_usb_table[] = { +- [AFATECH_AF9005] = {USB_DEVICE(USB_VID_AFATECH, +- USB_PID_AFATECH_AF9005)}, +- [TERRATEC_AF9005] = {USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_T_USB_XE)}, +- [ANSONIC_AF9005] = {USB_DEVICE(USB_VID_ANSONIC, +- USB_PID_ANSONIC_DVBT_USB)}, +- { } +-}; +- +-MODULE_DEVICE_TABLE(usb, af9005_usb_table); +- +-static struct dvb_usb_device_properties af9005_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "af9005.fw", +- .download_firmware = af9005_download_firmware, +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct af9005_device_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = +- DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = af9005_pid_filter, +- /* .pid_filter_ctrl = af9005_pid_filter_control, */ +- .frontend_attach = af9005_frontend_attach, +- /* .tuner_attach = af9005_tuner_attach, */ +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x04, +- .u = { +- .bulk = { +- .buffersize = 4096, /* actual size seen is 3948 */ +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = af9005_power_ctrl, +- .identify_state = af9005_identify_state, +- +- .i2c_algo = &af9005_i2c_algo, +- +- .rc.legacy = { +- .rc_interval = 200, +- .rc_map_table = NULL, +- .rc_map_size = 0, +- .rc_query = af9005_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 2, +- .generic_bulk_ctrl_endpoint_response = 1, +- +- .num_device_descs = 3, +- .devices = { +- {.name = "Afatech DVB-T USB1.1 stick", +- .cold_ids = {&af9005_usb_table[AFATECH_AF9005], NULL}, +- .warm_ids = {NULL}, +- }, +- {.name = "TerraTec Cinergy T USB XE", +- .cold_ids = {&af9005_usb_table[TERRATEC_AF9005], NULL}, +- .warm_ids = {NULL}, +- }, +- {.name = "Ansonic DVB-T USB1.1 stick", +- .cold_ids = {&af9005_usb_table[ANSONIC_AF9005], NULL}, +- .warm_ids = {NULL}, +- }, +- {NULL}, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver af9005_usb_driver = { +- .name = "dvb_usb_af9005", +- .probe = af9005_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = af9005_usb_table, +-}; +- +-/* module stuff */ +-static int __init af9005_usb_module_init(void) +-{ +- int result; +- if ((result = usb_register(&af9005_usb_driver))) { +- err("usb_register failed. (%d)", result); +- return result; +- } +- rc_decode = symbol_request(af9005_rc_decode); +- rc_keys = symbol_request(rc_map_af9005_table); +- rc_keys_size = symbol_request(rc_map_af9005_table_size); +- if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { +- err("af9005_rc_decode function not found, disabling remote"); +- af9005_properties.rc.legacy.rc_query = NULL; +- } else { +- af9005_properties.rc.legacy.rc_map_table = rc_keys; +- af9005_properties.rc.legacy.rc_map_size = *rc_keys_size; +- } +- +- return 0; +-} +- +-static void __exit af9005_usb_module_exit(void) +-{ +- /* release rc decode symbols */ +- if (rc_decode != NULL) +- symbol_put(af9005_rc_decode); +- if (rc_keys != NULL) +- symbol_put(rc_map_af9005_table); +- if (rc_keys_size != NULL) +- symbol_put(rc_map_af9005_table_size); +- /* deregister this driver from the USB subsystem */ +- usb_deregister(&af9005_usb_driver); +-} +- +-module_init(af9005_usb_module_init); +-module_exit(af9005_usb_module_exit); +- +-MODULE_AUTHOR("Luca Olivetti "); +-MODULE_DESCRIPTION("Driver for Afatech 9005 DVB-T USB1.1 stick"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/af9005.h b/drivers/media/dvb/dvb-usb/af9005.h +deleted file mode 100644 +index 6a2bf3d..0000000 +--- a/drivers/media/dvb/dvb-usb/af9005.h ++++ /dev/null +@@ -1,3496 +0,0 @@ +-/* Common header-file of the Linux driver for the Afatech 9005 +- * USB1.1 DVB-T receiver. +- * +- * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) +- * +- * Thanks to Afatech who kindly provided information. +- * +- * 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. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_AF9005_H_ +-#define _DVB_USB_AF9005_H_ +- +-#define DVB_USB_LOG_PREFIX "af9005" +-#include "dvb-usb.h" +- +-extern int dvb_usb_af9005_debug; +-#define deb_info(args...) dprintk(dvb_usb_af9005_debug,0x01,args) +-#define deb_xfer(args...) dprintk(dvb_usb_af9005_debug,0x02,args) +-#define deb_rc(args...) dprintk(dvb_usb_af9005_debug,0x04,args) +-#define deb_reg(args...) dprintk(dvb_usb_af9005_debug,0x08,args) +-#define deb_i2c(args...) dprintk(dvb_usb_af9005_debug,0x10,args) +-#define deb_fw(args...) dprintk(dvb_usb_af9005_debug,0x20,args) +- +-extern bool dvb_usb_af9005_led; +- +-/* firmware */ +-#define FW_BULKOUT_SIZE 250 +-enum { +- FW_CONFIG, +- FW_CONFIRM, +- FW_BOOT +-}; +- +-/* af9005 commands */ +-#define AF9005_OFDM_REG 0 +-#define AF9005_TUNER_REG 1 +- +-#define AF9005_REGISTER_RW 0x20 +-#define AF9005_REGISTER_RW_ACK 0x21 +- +-#define AF9005_CMD_OFDM_REG 0x00 +-#define AF9005_CMD_TUNER 0x80 +-#define AF9005_CMD_BURST 0x02 +-#define AF9005_CMD_AUTOINC 0x04 +-#define AF9005_CMD_READ 0x00 +-#define AF9005_CMD_WRITE 0x01 +- +-/* af9005 registers */ +-#define APO_REG_RESET 0xAEFF +- +-#define APO_REG_I2C_RW_CAN_TUNER 0xF000 +-#define APO_REG_I2C_RW_SILICON_TUNER 0xF001 +-#define APO_REG_GPIO_RW_SILICON_TUNER 0xFFFE /* also for OFSM */ +-#define APO_REG_TRIGGER_OFSM 0xFFFF /* also for OFSM */ +- +-/*********************************************************************** +- * Apollo Registers from VLSI * +- ***********************************************************************/ +-#define xd_p_reg_aagc_inverted_agc 0xA000 +-#define reg_aagc_inverted_agc_pos 0 +-#define reg_aagc_inverted_agc_len 1 +-#define reg_aagc_inverted_agc_lsb 0 +-#define xd_p_reg_aagc_sign_only 0xA000 +-#define reg_aagc_sign_only_pos 1 +-#define reg_aagc_sign_only_len 1 +-#define reg_aagc_sign_only_lsb 0 +-#define xd_p_reg_aagc_slow_adc_en 0xA000 +-#define reg_aagc_slow_adc_en_pos 2 +-#define reg_aagc_slow_adc_en_len 1 +-#define reg_aagc_slow_adc_en_lsb 0 +-#define xd_p_reg_aagc_slow_adc_scale 0xA000 +-#define reg_aagc_slow_adc_scale_pos 3 +-#define reg_aagc_slow_adc_scale_len 5 +-#define reg_aagc_slow_adc_scale_lsb 0 +-#define xd_p_reg_aagc_check_slow_adc_lock 0xA001 +-#define reg_aagc_check_slow_adc_lock_pos 0 +-#define reg_aagc_check_slow_adc_lock_len 1 +-#define reg_aagc_check_slow_adc_lock_lsb 0 +-#define xd_p_reg_aagc_init_control 0xA001 +-#define reg_aagc_init_control_pos 1 +-#define reg_aagc_init_control_len 1 +-#define reg_aagc_init_control_lsb 0 +-#define xd_p_reg_aagc_total_gain_sel 0xA001 +-#define reg_aagc_total_gain_sel_pos 2 +-#define reg_aagc_total_gain_sel_len 2 +-#define reg_aagc_total_gain_sel_lsb 0 +-#define xd_p_reg_aagc_out_inv 0xA001 +-#define reg_aagc_out_inv_pos 5 +-#define reg_aagc_out_inv_len 1 +-#define reg_aagc_out_inv_lsb 0 +-#define xd_p_reg_aagc_int_en 0xA001 +-#define reg_aagc_int_en_pos 6 +-#define reg_aagc_int_en_len 1 +-#define reg_aagc_int_en_lsb 0 +-#define xd_p_reg_aagc_lock_change_flag 0xA001 +-#define reg_aagc_lock_change_flag_pos 7 +-#define reg_aagc_lock_change_flag_len 1 +-#define reg_aagc_lock_change_flag_lsb 0 +-#define xd_p_reg_aagc_rf_loop_bw_scale_acquire 0xA002 +-#define reg_aagc_rf_loop_bw_scale_acquire_pos 0 +-#define reg_aagc_rf_loop_bw_scale_acquire_len 5 +-#define reg_aagc_rf_loop_bw_scale_acquire_lsb 0 +-#define xd_p_reg_aagc_rf_loop_bw_scale_track 0xA003 +-#define reg_aagc_rf_loop_bw_scale_track_pos 0 +-#define reg_aagc_rf_loop_bw_scale_track_len 5 +-#define reg_aagc_rf_loop_bw_scale_track_lsb 0 +-#define xd_p_reg_aagc_if_loop_bw_scale_acquire 0xA004 +-#define reg_aagc_if_loop_bw_scale_acquire_pos 0 +-#define reg_aagc_if_loop_bw_scale_acquire_len 5 +-#define reg_aagc_if_loop_bw_scale_acquire_lsb 0 +-#define xd_p_reg_aagc_if_loop_bw_scale_track 0xA005 +-#define reg_aagc_if_loop_bw_scale_track_pos 0 +-#define reg_aagc_if_loop_bw_scale_track_len 5 +-#define reg_aagc_if_loop_bw_scale_track_lsb 0 +-#define xd_p_reg_aagc_max_rf_agc_7_0 0xA006 +-#define reg_aagc_max_rf_agc_7_0_pos 0 +-#define reg_aagc_max_rf_agc_7_0_len 8 +-#define reg_aagc_max_rf_agc_7_0_lsb 0 +-#define xd_p_reg_aagc_max_rf_agc_9_8 0xA007 +-#define reg_aagc_max_rf_agc_9_8_pos 0 +-#define reg_aagc_max_rf_agc_9_8_len 2 +-#define reg_aagc_max_rf_agc_9_8_lsb 8 +-#define xd_p_reg_aagc_min_rf_agc_7_0 0xA008 +-#define reg_aagc_min_rf_agc_7_0_pos 0 +-#define reg_aagc_min_rf_agc_7_0_len 8 +-#define reg_aagc_min_rf_agc_7_0_lsb 0 +-#define xd_p_reg_aagc_min_rf_agc_9_8 0xA009 +-#define reg_aagc_min_rf_agc_9_8_pos 0 +-#define reg_aagc_min_rf_agc_9_8_len 2 +-#define reg_aagc_min_rf_agc_9_8_lsb 8 +-#define xd_p_reg_aagc_max_if_agc_7_0 0xA00A +-#define reg_aagc_max_if_agc_7_0_pos 0 +-#define reg_aagc_max_if_agc_7_0_len 8 +-#define reg_aagc_max_if_agc_7_0_lsb 0 +-#define xd_p_reg_aagc_max_if_agc_9_8 0xA00B +-#define reg_aagc_max_if_agc_9_8_pos 0 +-#define reg_aagc_max_if_agc_9_8_len 2 +-#define reg_aagc_max_if_agc_9_8_lsb 8 +-#define xd_p_reg_aagc_min_if_agc_7_0 0xA00C +-#define reg_aagc_min_if_agc_7_0_pos 0 +-#define reg_aagc_min_if_agc_7_0_len 8 +-#define reg_aagc_min_if_agc_7_0_lsb 0 +-#define xd_p_reg_aagc_min_if_agc_9_8 0xA00D +-#define reg_aagc_min_if_agc_9_8_pos 0 +-#define reg_aagc_min_if_agc_9_8_len 2 +-#define reg_aagc_min_if_agc_9_8_lsb 8 +-#define xd_p_reg_aagc_lock_sample_scale 0xA00E +-#define reg_aagc_lock_sample_scale_pos 0 +-#define reg_aagc_lock_sample_scale_len 5 +-#define reg_aagc_lock_sample_scale_lsb 0 +-#define xd_p_reg_aagc_rf_agc_lock_scale_acquire 0xA00F +-#define reg_aagc_rf_agc_lock_scale_acquire_pos 0 +-#define reg_aagc_rf_agc_lock_scale_acquire_len 3 +-#define reg_aagc_rf_agc_lock_scale_acquire_lsb 0 +-#define xd_p_reg_aagc_rf_agc_lock_scale_track 0xA00F +-#define reg_aagc_rf_agc_lock_scale_track_pos 3 +-#define reg_aagc_rf_agc_lock_scale_track_len 3 +-#define reg_aagc_rf_agc_lock_scale_track_lsb 0 +-#define xd_p_reg_aagc_if_agc_lock_scale_acquire 0xA010 +-#define reg_aagc_if_agc_lock_scale_acquire_pos 0 +-#define reg_aagc_if_agc_lock_scale_acquire_len 3 +-#define reg_aagc_if_agc_lock_scale_acquire_lsb 0 +-#define xd_p_reg_aagc_if_agc_lock_scale_track 0xA010 +-#define reg_aagc_if_agc_lock_scale_track_pos 3 +-#define reg_aagc_if_agc_lock_scale_track_len 3 +-#define reg_aagc_if_agc_lock_scale_track_lsb 0 +-#define xd_p_reg_aagc_rf_top_numerator_7_0 0xA011 +-#define reg_aagc_rf_top_numerator_7_0_pos 0 +-#define reg_aagc_rf_top_numerator_7_0_len 8 +-#define reg_aagc_rf_top_numerator_7_0_lsb 0 +-#define xd_p_reg_aagc_rf_top_numerator_9_8 0xA012 +-#define reg_aagc_rf_top_numerator_9_8_pos 0 +-#define reg_aagc_rf_top_numerator_9_8_len 2 +-#define reg_aagc_rf_top_numerator_9_8_lsb 8 +-#define xd_p_reg_aagc_if_top_numerator_7_0 0xA013 +-#define reg_aagc_if_top_numerator_7_0_pos 0 +-#define reg_aagc_if_top_numerator_7_0_len 8 +-#define reg_aagc_if_top_numerator_7_0_lsb 0 +-#define xd_p_reg_aagc_if_top_numerator_9_8 0xA014 +-#define reg_aagc_if_top_numerator_9_8_pos 0 +-#define reg_aagc_if_top_numerator_9_8_len 2 +-#define reg_aagc_if_top_numerator_9_8_lsb 8 +-#define xd_p_reg_aagc_adc_out_desired_7_0 0xA015 +-#define reg_aagc_adc_out_desired_7_0_pos 0 +-#define reg_aagc_adc_out_desired_7_0_len 8 +-#define reg_aagc_adc_out_desired_7_0_lsb 0 +-#define xd_p_reg_aagc_adc_out_desired_8 0xA016 +-#define reg_aagc_adc_out_desired_8_pos 0 +-#define reg_aagc_adc_out_desired_8_len 1 +-#define reg_aagc_adc_out_desired_8_lsb 0 +-#define xd_p_reg_aagc_fixed_gain 0xA016 +-#define reg_aagc_fixed_gain_pos 3 +-#define reg_aagc_fixed_gain_len 1 +-#define reg_aagc_fixed_gain_lsb 0 +-#define xd_p_reg_aagc_lock_count_th 0xA016 +-#define reg_aagc_lock_count_th_pos 4 +-#define reg_aagc_lock_count_th_len 4 +-#define reg_aagc_lock_count_th_lsb 0 +-#define xd_p_reg_aagc_fixed_rf_agc_control_7_0 0xA017 +-#define reg_aagc_fixed_rf_agc_control_7_0_pos 0 +-#define reg_aagc_fixed_rf_agc_control_7_0_len 8 +-#define reg_aagc_fixed_rf_agc_control_7_0_lsb 0 +-#define xd_p_reg_aagc_fixed_rf_agc_control_15_8 0xA018 +-#define reg_aagc_fixed_rf_agc_control_15_8_pos 0 +-#define reg_aagc_fixed_rf_agc_control_15_8_len 8 +-#define reg_aagc_fixed_rf_agc_control_15_8_lsb 8 +-#define xd_p_reg_aagc_fixed_rf_agc_control_23_16 0xA019 +-#define reg_aagc_fixed_rf_agc_control_23_16_pos 0 +-#define reg_aagc_fixed_rf_agc_control_23_16_len 8 +-#define reg_aagc_fixed_rf_agc_control_23_16_lsb 16 +-#define xd_p_reg_aagc_fixed_rf_agc_control_30_24 0xA01A +-#define reg_aagc_fixed_rf_agc_control_30_24_pos 0 +-#define reg_aagc_fixed_rf_agc_control_30_24_len 7 +-#define reg_aagc_fixed_rf_agc_control_30_24_lsb 24 +-#define xd_p_reg_aagc_fixed_if_agc_control_7_0 0xA01B +-#define reg_aagc_fixed_if_agc_control_7_0_pos 0 +-#define reg_aagc_fixed_if_agc_control_7_0_len 8 +-#define reg_aagc_fixed_if_agc_control_7_0_lsb 0 +-#define xd_p_reg_aagc_fixed_if_agc_control_15_8 0xA01C +-#define reg_aagc_fixed_if_agc_control_15_8_pos 0 +-#define reg_aagc_fixed_if_agc_control_15_8_len 8 +-#define reg_aagc_fixed_if_agc_control_15_8_lsb 8 +-#define xd_p_reg_aagc_fixed_if_agc_control_23_16 0xA01D +-#define reg_aagc_fixed_if_agc_control_23_16_pos 0 +-#define reg_aagc_fixed_if_agc_control_23_16_len 8 +-#define reg_aagc_fixed_if_agc_control_23_16_lsb 16 +-#define xd_p_reg_aagc_fixed_if_agc_control_30_24 0xA01E +-#define reg_aagc_fixed_if_agc_control_30_24_pos 0 +-#define reg_aagc_fixed_if_agc_control_30_24_len 7 +-#define reg_aagc_fixed_if_agc_control_30_24_lsb 24 +-#define xd_p_reg_aagc_rf_agc_unlock_numerator 0xA01F +-#define reg_aagc_rf_agc_unlock_numerator_pos 0 +-#define reg_aagc_rf_agc_unlock_numerator_len 6 +-#define reg_aagc_rf_agc_unlock_numerator_lsb 0 +-#define xd_p_reg_aagc_if_agc_unlock_numerator 0xA020 +-#define reg_aagc_if_agc_unlock_numerator_pos 0 +-#define reg_aagc_if_agc_unlock_numerator_len 6 +-#define reg_aagc_if_agc_unlock_numerator_lsb 0 +-#define xd_p_reg_unplug_th 0xA021 +-#define reg_unplug_th_pos 0 +-#define reg_unplug_th_len 8 +-#define reg_aagc_rf_x0_lsb 0 +-#define xd_p_reg_weak_signal_rfagc_thr 0xA022 +-#define reg_weak_signal_rfagc_thr_pos 0 +-#define reg_weak_signal_rfagc_thr_len 8 +-#define reg_weak_signal_rfagc_thr_lsb 0 +-#define xd_p_reg_unplug_rf_gain_th 0xA023 +-#define reg_unplug_rf_gain_th_pos 0 +-#define reg_unplug_rf_gain_th_len 8 +-#define reg_unplug_rf_gain_th_lsb 0 +-#define xd_p_reg_unplug_dtop_rf_gain_th 0xA024 +-#define reg_unplug_dtop_rf_gain_th_pos 0 +-#define reg_unplug_dtop_rf_gain_th_len 8 +-#define reg_unplug_dtop_rf_gain_th_lsb 0 +-#define xd_p_reg_unplug_dtop_if_gain_th 0xA025 +-#define reg_unplug_dtop_if_gain_th_pos 0 +-#define reg_unplug_dtop_if_gain_th_len 8 +-#define reg_unplug_dtop_if_gain_th_lsb 0 +-#define xd_p_reg_top_recover_at_unplug_en 0xA026 +-#define reg_top_recover_at_unplug_en_pos 0 +-#define reg_top_recover_at_unplug_en_len 1 +-#define reg_top_recover_at_unplug_en_lsb 0 +-#define xd_p_reg_aagc_rf_x6 0xA027 +-#define reg_aagc_rf_x6_pos 0 +-#define reg_aagc_rf_x6_len 8 +-#define reg_aagc_rf_x6_lsb 0 +-#define xd_p_reg_aagc_rf_x7 0xA028 +-#define reg_aagc_rf_x7_pos 0 +-#define reg_aagc_rf_x7_len 8 +-#define reg_aagc_rf_x7_lsb 0 +-#define xd_p_reg_aagc_rf_x8 0xA029 +-#define reg_aagc_rf_x8_pos 0 +-#define reg_aagc_rf_x8_len 8 +-#define reg_aagc_rf_x8_lsb 0 +-#define xd_p_reg_aagc_rf_x9 0xA02A +-#define reg_aagc_rf_x9_pos 0 +-#define reg_aagc_rf_x9_len 8 +-#define reg_aagc_rf_x9_lsb 0 +-#define xd_p_reg_aagc_rf_x10 0xA02B +-#define reg_aagc_rf_x10_pos 0 +-#define reg_aagc_rf_x10_len 8 +-#define reg_aagc_rf_x10_lsb 0 +-#define xd_p_reg_aagc_rf_x11 0xA02C +-#define reg_aagc_rf_x11_pos 0 +-#define reg_aagc_rf_x11_len 8 +-#define reg_aagc_rf_x11_lsb 0 +-#define xd_p_reg_aagc_rf_x12 0xA02D +-#define reg_aagc_rf_x12_pos 0 +-#define reg_aagc_rf_x12_len 8 +-#define reg_aagc_rf_x12_lsb 0 +-#define xd_p_reg_aagc_rf_x13 0xA02E +-#define reg_aagc_rf_x13_pos 0 +-#define reg_aagc_rf_x13_len 8 +-#define reg_aagc_rf_x13_lsb 0 +-#define xd_p_reg_aagc_if_x0 0xA02F +-#define reg_aagc_if_x0_pos 0 +-#define reg_aagc_if_x0_len 8 +-#define reg_aagc_if_x0_lsb 0 +-#define xd_p_reg_aagc_if_x1 0xA030 +-#define reg_aagc_if_x1_pos 0 +-#define reg_aagc_if_x1_len 8 +-#define reg_aagc_if_x1_lsb 0 +-#define xd_p_reg_aagc_if_x2 0xA031 +-#define reg_aagc_if_x2_pos 0 +-#define reg_aagc_if_x2_len 8 +-#define reg_aagc_if_x2_lsb 0 +-#define xd_p_reg_aagc_if_x3 0xA032 +-#define reg_aagc_if_x3_pos 0 +-#define reg_aagc_if_x3_len 8 +-#define reg_aagc_if_x3_lsb 0 +-#define xd_p_reg_aagc_if_x4 0xA033 +-#define reg_aagc_if_x4_pos 0 +-#define reg_aagc_if_x4_len 8 +-#define reg_aagc_if_x4_lsb 0 +-#define xd_p_reg_aagc_if_x5 0xA034 +-#define reg_aagc_if_x5_pos 0 +-#define reg_aagc_if_x5_len 8 +-#define reg_aagc_if_x5_lsb 0 +-#define xd_p_reg_aagc_if_x6 0xA035 +-#define reg_aagc_if_x6_pos 0 +-#define reg_aagc_if_x6_len 8 +-#define reg_aagc_if_x6_lsb 0 +-#define xd_p_reg_aagc_if_x7 0xA036 +-#define reg_aagc_if_x7_pos 0 +-#define reg_aagc_if_x7_len 8 +-#define reg_aagc_if_x7_lsb 0 +-#define xd_p_reg_aagc_if_x8 0xA037 +-#define reg_aagc_if_x8_pos 0 +-#define reg_aagc_if_x8_len 8 +-#define reg_aagc_if_x8_lsb 0 +-#define xd_p_reg_aagc_if_x9 0xA038 +-#define reg_aagc_if_x9_pos 0 +-#define reg_aagc_if_x9_len 8 +-#define reg_aagc_if_x9_lsb 0 +-#define xd_p_reg_aagc_if_x10 0xA039 +-#define reg_aagc_if_x10_pos 0 +-#define reg_aagc_if_x10_len 8 +-#define reg_aagc_if_x10_lsb 0 +-#define xd_p_reg_aagc_if_x11 0xA03A +-#define reg_aagc_if_x11_pos 0 +-#define reg_aagc_if_x11_len 8 +-#define reg_aagc_if_x11_lsb 0 +-#define xd_p_reg_aagc_if_x12 0xA03B +-#define reg_aagc_if_x12_pos 0 +-#define reg_aagc_if_x12_len 8 +-#define reg_aagc_if_x12_lsb 0 +-#define xd_p_reg_aagc_if_x13 0xA03C +-#define reg_aagc_if_x13_pos 0 +-#define reg_aagc_if_x13_len 8 +-#define reg_aagc_if_x13_lsb 0 +-#define xd_p_reg_aagc_min_rf_ctl_8bit_for_dca 0xA03D +-#define reg_aagc_min_rf_ctl_8bit_for_dca_pos 0 +-#define reg_aagc_min_rf_ctl_8bit_for_dca_len 8 +-#define reg_aagc_min_rf_ctl_8bit_for_dca_lsb 0 +-#define xd_p_reg_aagc_min_if_ctl_8bit_for_dca 0xA03E +-#define reg_aagc_min_if_ctl_8bit_for_dca_pos 0 +-#define reg_aagc_min_if_ctl_8bit_for_dca_len 8 +-#define reg_aagc_min_if_ctl_8bit_for_dca_lsb 0 +-#define xd_r_reg_aagc_total_gain_7_0 0xA070 +-#define reg_aagc_total_gain_7_0_pos 0 +-#define reg_aagc_total_gain_7_0_len 8 +-#define reg_aagc_total_gain_7_0_lsb 0 +-#define xd_r_reg_aagc_total_gain_15_8 0xA071 +-#define reg_aagc_total_gain_15_8_pos 0 +-#define reg_aagc_total_gain_15_8_len 8 +-#define reg_aagc_total_gain_15_8_lsb 8 +-#define xd_p_reg_aagc_in_sat_cnt_7_0 0xA074 +-#define reg_aagc_in_sat_cnt_7_0_pos 0 +-#define reg_aagc_in_sat_cnt_7_0_len 8 +-#define reg_aagc_in_sat_cnt_7_0_lsb 0 +-#define xd_p_reg_aagc_in_sat_cnt_15_8 0xA075 +-#define reg_aagc_in_sat_cnt_15_8_pos 0 +-#define reg_aagc_in_sat_cnt_15_8_len 8 +-#define reg_aagc_in_sat_cnt_15_8_lsb 8 +-#define xd_p_reg_aagc_in_sat_cnt_23_16 0xA076 +-#define reg_aagc_in_sat_cnt_23_16_pos 0 +-#define reg_aagc_in_sat_cnt_23_16_len 8 +-#define reg_aagc_in_sat_cnt_23_16_lsb 16 +-#define xd_p_reg_aagc_in_sat_cnt_31_24 0xA077 +-#define reg_aagc_in_sat_cnt_31_24_pos 0 +-#define reg_aagc_in_sat_cnt_31_24_len 8 +-#define reg_aagc_in_sat_cnt_31_24_lsb 24 +-#define xd_r_reg_aagc_digital_rf_volt_7_0 0xA078 +-#define reg_aagc_digital_rf_volt_7_0_pos 0 +-#define reg_aagc_digital_rf_volt_7_0_len 8 +-#define reg_aagc_digital_rf_volt_7_0_lsb 0 +-#define xd_r_reg_aagc_digital_rf_volt_9_8 0xA079 +-#define reg_aagc_digital_rf_volt_9_8_pos 0 +-#define reg_aagc_digital_rf_volt_9_8_len 2 +-#define reg_aagc_digital_rf_volt_9_8_lsb 8 +-#define xd_r_reg_aagc_digital_if_volt_7_0 0xA07A +-#define reg_aagc_digital_if_volt_7_0_pos 0 +-#define reg_aagc_digital_if_volt_7_0_len 8 +-#define reg_aagc_digital_if_volt_7_0_lsb 0 +-#define xd_r_reg_aagc_digital_if_volt_9_8 0xA07B +-#define reg_aagc_digital_if_volt_9_8_pos 0 +-#define reg_aagc_digital_if_volt_9_8_len 2 +-#define reg_aagc_digital_if_volt_9_8_lsb 8 +-#define xd_r_reg_aagc_rf_gain 0xA07C +-#define reg_aagc_rf_gain_pos 0 +-#define reg_aagc_rf_gain_len 8 +-#define reg_aagc_rf_gain_lsb 0 +-#define xd_r_reg_aagc_if_gain 0xA07D +-#define reg_aagc_if_gain_pos 0 +-#define reg_aagc_if_gain_len 8 +-#define reg_aagc_if_gain_lsb 0 +-#define xd_p_tinr_imp_indicator 0xA080 +-#define tinr_imp_indicator_pos 0 +-#define tinr_imp_indicator_len 2 +-#define tinr_imp_indicator_lsb 0 +-#define xd_p_reg_tinr_fifo_size 0xA080 +-#define reg_tinr_fifo_size_pos 2 +-#define reg_tinr_fifo_size_len 5 +-#define reg_tinr_fifo_size_lsb 0 +-#define xd_p_reg_tinr_saturation_cnt_th 0xA081 +-#define reg_tinr_saturation_cnt_th_pos 0 +-#define reg_tinr_saturation_cnt_th_len 4 +-#define reg_tinr_saturation_cnt_th_lsb 0 +-#define xd_p_reg_tinr_saturation_th_3_0 0xA081 +-#define reg_tinr_saturation_th_3_0_pos 4 +-#define reg_tinr_saturation_th_3_0_len 4 +-#define reg_tinr_saturation_th_3_0_lsb 0 +-#define xd_p_reg_tinr_saturation_th_8_4 0xA082 +-#define reg_tinr_saturation_th_8_4_pos 0 +-#define reg_tinr_saturation_th_8_4_len 5 +-#define reg_tinr_saturation_th_8_4_lsb 4 +-#define xd_p_reg_tinr_imp_duration_th_2k_7_0 0xA083 +-#define reg_tinr_imp_duration_th_2k_7_0_pos 0 +-#define reg_tinr_imp_duration_th_2k_7_0_len 8 +-#define reg_tinr_imp_duration_th_2k_7_0_lsb 0 +-#define xd_p_reg_tinr_imp_duration_th_2k_8 0xA084 +-#define reg_tinr_imp_duration_th_2k_8_pos 0 +-#define reg_tinr_imp_duration_th_2k_8_len 1 +-#define reg_tinr_imp_duration_th_2k_8_lsb 0 +-#define xd_p_reg_tinr_imp_duration_th_8k_7_0 0xA085 +-#define reg_tinr_imp_duration_th_8k_7_0_pos 0 +-#define reg_tinr_imp_duration_th_8k_7_0_len 8 +-#define reg_tinr_imp_duration_th_8k_7_0_lsb 0 +-#define xd_p_reg_tinr_imp_duration_th_8k_10_8 0xA086 +-#define reg_tinr_imp_duration_th_8k_10_8_pos 0 +-#define reg_tinr_imp_duration_th_8k_10_8_len 3 +-#define reg_tinr_imp_duration_th_8k_10_8_lsb 8 +-#define xd_p_reg_tinr_freq_ratio_6m_7_0 0xA087 +-#define reg_tinr_freq_ratio_6m_7_0_pos 0 +-#define reg_tinr_freq_ratio_6m_7_0_len 8 +-#define reg_tinr_freq_ratio_6m_7_0_lsb 0 +-#define xd_p_reg_tinr_freq_ratio_6m_12_8 0xA088 +-#define reg_tinr_freq_ratio_6m_12_8_pos 0 +-#define reg_tinr_freq_ratio_6m_12_8_len 5 +-#define reg_tinr_freq_ratio_6m_12_8_lsb 8 +-#define xd_p_reg_tinr_freq_ratio_7m_7_0 0xA089 +-#define reg_tinr_freq_ratio_7m_7_0_pos 0 +-#define reg_tinr_freq_ratio_7m_7_0_len 8 +-#define reg_tinr_freq_ratio_7m_7_0_lsb 0 +-#define xd_p_reg_tinr_freq_ratio_7m_12_8 0xA08A +-#define reg_tinr_freq_ratio_7m_12_8_pos 0 +-#define reg_tinr_freq_ratio_7m_12_8_len 5 +-#define reg_tinr_freq_ratio_7m_12_8_lsb 8 +-#define xd_p_reg_tinr_freq_ratio_8m_7_0 0xA08B +-#define reg_tinr_freq_ratio_8m_7_0_pos 0 +-#define reg_tinr_freq_ratio_8m_7_0_len 8 +-#define reg_tinr_freq_ratio_8m_7_0_lsb 0 +-#define xd_p_reg_tinr_freq_ratio_8m_12_8 0xA08C +-#define reg_tinr_freq_ratio_8m_12_8_pos 0 +-#define reg_tinr_freq_ratio_8m_12_8_len 5 +-#define reg_tinr_freq_ratio_8m_12_8_lsb 8 +-#define xd_p_reg_tinr_imp_duration_th_low_2k 0xA08D +-#define reg_tinr_imp_duration_th_low_2k_pos 0 +-#define reg_tinr_imp_duration_th_low_2k_len 8 +-#define reg_tinr_imp_duration_th_low_2k_lsb 0 +-#define xd_p_reg_tinr_imp_duration_th_low_8k 0xA08E +-#define reg_tinr_imp_duration_th_low_8k_pos 0 +-#define reg_tinr_imp_duration_th_low_8k_len 8 +-#define reg_tinr_imp_duration_th_low_8k_lsb 0 +-#define xd_r_reg_tinr_counter_7_0 0xA090 +-#define reg_tinr_counter_7_0_pos 0 +-#define reg_tinr_counter_7_0_len 8 +-#define reg_tinr_counter_7_0_lsb 0 +-#define xd_r_reg_tinr_counter_15_8 0xA091 +-#define reg_tinr_counter_15_8_pos 0 +-#define reg_tinr_counter_15_8_len 8 +-#define reg_tinr_counter_15_8_lsb 8 +-#define xd_p_reg_tinr_adative_tinr_en 0xA093 +-#define reg_tinr_adative_tinr_en_pos 0 +-#define reg_tinr_adative_tinr_en_len 1 +-#define reg_tinr_adative_tinr_en_lsb 0 +-#define xd_p_reg_tinr_peak_fifo_size 0xA093 +-#define reg_tinr_peak_fifo_size_pos 1 +-#define reg_tinr_peak_fifo_size_len 5 +-#define reg_tinr_peak_fifo_size_lsb 0 +-#define xd_p_reg_tinr_counter_rst 0xA093 +-#define reg_tinr_counter_rst_pos 6 +-#define reg_tinr_counter_rst_len 1 +-#define reg_tinr_counter_rst_lsb 0 +-#define xd_p_reg_tinr_search_period_7_0 0xA094 +-#define reg_tinr_search_period_7_0_pos 0 +-#define reg_tinr_search_period_7_0_len 8 +-#define reg_tinr_search_period_7_0_lsb 0 +-#define xd_p_reg_tinr_search_period_15_8 0xA095 +-#define reg_tinr_search_period_15_8_pos 0 +-#define reg_tinr_search_period_15_8_len 8 +-#define reg_tinr_search_period_15_8_lsb 8 +-#define xd_p_reg_ccifs_fcw_7_0 0xA0A0 +-#define reg_ccifs_fcw_7_0_pos 0 +-#define reg_ccifs_fcw_7_0_len 8 +-#define reg_ccifs_fcw_7_0_lsb 0 +-#define xd_p_reg_ccifs_fcw_12_8 0xA0A1 +-#define reg_ccifs_fcw_12_8_pos 0 +-#define reg_ccifs_fcw_12_8_len 5 +-#define reg_ccifs_fcw_12_8_lsb 8 +-#define xd_p_reg_ccifs_spec_inv 0xA0A1 +-#define reg_ccifs_spec_inv_pos 5 +-#define reg_ccifs_spec_inv_len 1 +-#define reg_ccifs_spec_inv_lsb 0 +-#define xd_p_reg_gp_trigger 0xA0A2 +-#define reg_gp_trigger_pos 0 +-#define reg_gp_trigger_len 1 +-#define reg_gp_trigger_lsb 0 +-#define xd_p_reg_trigger_sel 0xA0A2 +-#define reg_trigger_sel_pos 1 +-#define reg_trigger_sel_len 2 +-#define reg_trigger_sel_lsb 0 +-#define xd_p_reg_debug_ofdm 0xA0A2 +-#define reg_debug_ofdm_pos 3 +-#define reg_debug_ofdm_len 2 +-#define reg_debug_ofdm_lsb 0 +-#define xd_p_reg_trigger_module_sel 0xA0A3 +-#define reg_trigger_module_sel_pos 0 +-#define reg_trigger_module_sel_len 6 +-#define reg_trigger_module_sel_lsb 0 +-#define xd_p_reg_trigger_set_sel 0xA0A4 +-#define reg_trigger_set_sel_pos 0 +-#define reg_trigger_set_sel_len 6 +-#define reg_trigger_set_sel_lsb 0 +-#define xd_p_reg_fw_int_mask_n 0xA0A4 +-#define reg_fw_int_mask_n_pos 6 +-#define reg_fw_int_mask_n_len 1 +-#define reg_fw_int_mask_n_lsb 0 +-#define xd_p_reg_debug_group 0xA0A5 +-#define reg_debug_group_pos 0 +-#define reg_debug_group_len 4 +-#define reg_debug_group_lsb 0 +-#define xd_p_reg_odbg_clk_sel 0xA0A5 +-#define reg_odbg_clk_sel_pos 4 +-#define reg_odbg_clk_sel_len 2 +-#define reg_odbg_clk_sel_lsb 0 +-#define xd_p_reg_ccif_sc 0xA0C0 +-#define reg_ccif_sc_pos 0 +-#define reg_ccif_sc_len 4 +-#define reg_ccif_sc_lsb 0 +-#define xd_r_reg_ccif_saturate 0xA0C1 +-#define reg_ccif_saturate_pos 0 +-#define reg_ccif_saturate_len 2 +-#define reg_ccif_saturate_lsb 0 +-#define xd_r_reg_antif_saturate 0xA0C1 +-#define reg_antif_saturate_pos 2 +-#define reg_antif_saturate_len 4 +-#define reg_antif_saturate_lsb 0 +-#define xd_r_reg_acif_saturate 0xA0C2 +-#define reg_acif_saturate_pos 0 +-#define reg_acif_saturate_len 8 +-#define reg_acif_saturate_lsb 0 +-#define xd_p_reg_tmr_timer0_threshold_7_0 0xA0C8 +-#define reg_tmr_timer0_threshold_7_0_pos 0 +-#define reg_tmr_timer0_threshold_7_0_len 8 +-#define reg_tmr_timer0_threshold_7_0_lsb 0 +-#define xd_p_reg_tmr_timer0_threshold_15_8 0xA0C9 +-#define reg_tmr_timer0_threshold_15_8_pos 0 +-#define reg_tmr_timer0_threshold_15_8_len 8 +-#define reg_tmr_timer0_threshold_15_8_lsb 8 +-#define xd_p_reg_tmr_timer0_enable 0xA0CA +-#define reg_tmr_timer0_enable_pos 0 +-#define reg_tmr_timer0_enable_len 1 +-#define reg_tmr_timer0_enable_lsb 0 +-#define xd_p_reg_tmr_timer0_clk_sel 0xA0CA +-#define reg_tmr_timer0_clk_sel_pos 1 +-#define reg_tmr_timer0_clk_sel_len 1 +-#define reg_tmr_timer0_clk_sel_lsb 0 +-#define xd_p_reg_tmr_timer0_int 0xA0CA +-#define reg_tmr_timer0_int_pos 2 +-#define reg_tmr_timer0_int_len 1 +-#define reg_tmr_timer0_int_lsb 0 +-#define xd_p_reg_tmr_timer0_rst 0xA0CA +-#define reg_tmr_timer0_rst_pos 3 +-#define reg_tmr_timer0_rst_len 1 +-#define reg_tmr_timer0_rst_lsb 0 +-#define xd_r_reg_tmr_timer0_count_7_0 0xA0CB +-#define reg_tmr_timer0_count_7_0_pos 0 +-#define reg_tmr_timer0_count_7_0_len 8 +-#define reg_tmr_timer0_count_7_0_lsb 0 +-#define xd_r_reg_tmr_timer0_count_15_8 0xA0CC +-#define reg_tmr_timer0_count_15_8_pos 0 +-#define reg_tmr_timer0_count_15_8_len 8 +-#define reg_tmr_timer0_count_15_8_lsb 8 +-#define xd_p_reg_suspend 0xA0CD +-#define reg_suspend_pos 0 +-#define reg_suspend_len 1 +-#define reg_suspend_lsb 0 +-#define xd_p_reg_suspend_rdy 0xA0CD +-#define reg_suspend_rdy_pos 1 +-#define reg_suspend_rdy_len 1 +-#define reg_suspend_rdy_lsb 0 +-#define xd_p_reg_resume 0xA0CD +-#define reg_resume_pos 2 +-#define reg_resume_len 1 +-#define reg_resume_lsb 0 +-#define xd_p_reg_resume_rdy 0xA0CD +-#define reg_resume_rdy_pos 3 +-#define reg_resume_rdy_len 1 +-#define reg_resume_rdy_lsb 0 +-#define xd_p_reg_fmf 0xA0CE +-#define reg_fmf_pos 0 +-#define reg_fmf_len 8 +-#define reg_fmf_lsb 0 +-#define xd_p_ccid_accumulate_num_2k_7_0 0xA100 +-#define ccid_accumulate_num_2k_7_0_pos 0 +-#define ccid_accumulate_num_2k_7_0_len 8 +-#define ccid_accumulate_num_2k_7_0_lsb 0 +-#define xd_p_ccid_accumulate_num_2k_12_8 0xA101 +-#define ccid_accumulate_num_2k_12_8_pos 0 +-#define ccid_accumulate_num_2k_12_8_len 5 +-#define ccid_accumulate_num_2k_12_8_lsb 8 +-#define xd_p_ccid_accumulate_num_8k_7_0 0xA102 +-#define ccid_accumulate_num_8k_7_0_pos 0 +-#define ccid_accumulate_num_8k_7_0_len 8 +-#define ccid_accumulate_num_8k_7_0_lsb 0 +-#define xd_p_ccid_accumulate_num_8k_14_8 0xA103 +-#define ccid_accumulate_num_8k_14_8_pos 0 +-#define ccid_accumulate_num_8k_14_8_len 7 +-#define ccid_accumulate_num_8k_14_8_lsb 8 +-#define xd_p_ccid_desired_level_0 0xA103 +-#define ccid_desired_level_0_pos 7 +-#define ccid_desired_level_0_len 1 +-#define ccid_desired_level_0_lsb 0 +-#define xd_p_ccid_desired_level_8_1 0xA104 +-#define ccid_desired_level_8_1_pos 0 +-#define ccid_desired_level_8_1_len 8 +-#define ccid_desired_level_8_1_lsb 1 +-#define xd_p_ccid_apply_delay 0xA105 +-#define ccid_apply_delay_pos 0 +-#define ccid_apply_delay_len 7 +-#define ccid_apply_delay_lsb 0 +-#define xd_p_ccid_CCID_Threshold1 0xA106 +-#define ccid_CCID_Threshold1_pos 0 +-#define ccid_CCID_Threshold1_len 8 +-#define ccid_CCID_Threshold1_lsb 0 +-#define xd_p_ccid_CCID_Threshold2 0xA107 +-#define ccid_CCID_Threshold2_pos 0 +-#define ccid_CCID_Threshold2_len 8 +-#define ccid_CCID_Threshold2_lsb 0 +-#define xd_p_reg_ccid_gain_scale 0xA108 +-#define reg_ccid_gain_scale_pos 0 +-#define reg_ccid_gain_scale_len 4 +-#define reg_ccid_gain_scale_lsb 0 +-#define xd_p_reg_ccid2_passband_gain_set 0xA108 +-#define reg_ccid2_passband_gain_set_pos 4 +-#define reg_ccid2_passband_gain_set_len 4 +-#define reg_ccid2_passband_gain_set_lsb 0 +-#define xd_r_ccid_multiplier_7_0 0xA109 +-#define ccid_multiplier_7_0_pos 0 +-#define ccid_multiplier_7_0_len 8 +-#define ccid_multiplier_7_0_lsb 0 +-#define xd_r_ccid_multiplier_15_8 0xA10A +-#define ccid_multiplier_15_8_pos 0 +-#define ccid_multiplier_15_8_len 8 +-#define ccid_multiplier_15_8_lsb 8 +-#define xd_r_ccid_right_shift_bits 0xA10B +-#define ccid_right_shift_bits_pos 0 +-#define ccid_right_shift_bits_len 4 +-#define ccid_right_shift_bits_lsb 0 +-#define xd_r_reg_ccid_sx_7_0 0xA10C +-#define reg_ccid_sx_7_0_pos 0 +-#define reg_ccid_sx_7_0_len 8 +-#define reg_ccid_sx_7_0_lsb 0 +-#define xd_r_reg_ccid_sx_15_8 0xA10D +-#define reg_ccid_sx_15_8_pos 0 +-#define reg_ccid_sx_15_8_len 8 +-#define reg_ccid_sx_15_8_lsb 8 +-#define xd_r_reg_ccid_sx_21_16 0xA10E +-#define reg_ccid_sx_21_16_pos 0 +-#define reg_ccid_sx_21_16_len 6 +-#define reg_ccid_sx_21_16_lsb 16 +-#define xd_r_reg_ccid_sy_7_0 0xA110 +-#define reg_ccid_sy_7_0_pos 0 +-#define reg_ccid_sy_7_0_len 8 +-#define reg_ccid_sy_7_0_lsb 0 +-#define xd_r_reg_ccid_sy_15_8 0xA111 +-#define reg_ccid_sy_15_8_pos 0 +-#define reg_ccid_sy_15_8_len 8 +-#define reg_ccid_sy_15_8_lsb 8 +-#define xd_r_reg_ccid_sy_23_16 0xA112 +-#define reg_ccid_sy_23_16_pos 0 +-#define reg_ccid_sy_23_16_len 8 +-#define reg_ccid_sy_23_16_lsb 16 +-#define xd_r_reg_ccid2_sz_7_0 0xA114 +-#define reg_ccid2_sz_7_0_pos 0 +-#define reg_ccid2_sz_7_0_len 8 +-#define reg_ccid2_sz_7_0_lsb 0 +-#define xd_r_reg_ccid2_sz_15_8 0xA115 +-#define reg_ccid2_sz_15_8_pos 0 +-#define reg_ccid2_sz_15_8_len 8 +-#define reg_ccid2_sz_15_8_lsb 8 +-#define xd_r_reg_ccid2_sz_23_16 0xA116 +-#define reg_ccid2_sz_23_16_pos 0 +-#define reg_ccid2_sz_23_16_len 8 +-#define reg_ccid2_sz_23_16_lsb 16 +-#define xd_r_reg_ccid2_sz_25_24 0xA117 +-#define reg_ccid2_sz_25_24_pos 0 +-#define reg_ccid2_sz_25_24_len 2 +-#define reg_ccid2_sz_25_24_lsb 24 +-#define xd_r_reg_ccid2_sy_7_0 0xA118 +-#define reg_ccid2_sy_7_0_pos 0 +-#define reg_ccid2_sy_7_0_len 8 +-#define reg_ccid2_sy_7_0_lsb 0 +-#define xd_r_reg_ccid2_sy_15_8 0xA119 +-#define reg_ccid2_sy_15_8_pos 0 +-#define reg_ccid2_sy_15_8_len 8 +-#define reg_ccid2_sy_15_8_lsb 8 +-#define xd_r_reg_ccid2_sy_23_16 0xA11A +-#define reg_ccid2_sy_23_16_pos 0 +-#define reg_ccid2_sy_23_16_len 8 +-#define reg_ccid2_sy_23_16_lsb 16 +-#define xd_r_reg_ccid2_sy_25_24 0xA11B +-#define reg_ccid2_sy_25_24_pos 0 +-#define reg_ccid2_sy_25_24_len 2 +-#define reg_ccid2_sy_25_24_lsb 24 +-#define xd_p_dagc1_accumulate_num_2k_7_0 0xA120 +-#define dagc1_accumulate_num_2k_7_0_pos 0 +-#define dagc1_accumulate_num_2k_7_0_len 8 +-#define dagc1_accumulate_num_2k_7_0_lsb 0 +-#define xd_p_dagc1_accumulate_num_2k_12_8 0xA121 +-#define dagc1_accumulate_num_2k_12_8_pos 0 +-#define dagc1_accumulate_num_2k_12_8_len 5 +-#define dagc1_accumulate_num_2k_12_8_lsb 8 +-#define xd_p_dagc1_accumulate_num_8k_7_0 0xA122 +-#define dagc1_accumulate_num_8k_7_0_pos 0 +-#define dagc1_accumulate_num_8k_7_0_len 8 +-#define dagc1_accumulate_num_8k_7_0_lsb 0 +-#define xd_p_dagc1_accumulate_num_8k_14_8 0xA123 +-#define dagc1_accumulate_num_8k_14_8_pos 0 +-#define dagc1_accumulate_num_8k_14_8_len 7 +-#define dagc1_accumulate_num_8k_14_8_lsb 8 +-#define xd_p_dagc1_desired_level_0 0xA123 +-#define dagc1_desired_level_0_pos 7 +-#define dagc1_desired_level_0_len 1 +-#define dagc1_desired_level_0_lsb 0 +-#define xd_p_dagc1_desired_level_8_1 0xA124 +-#define dagc1_desired_level_8_1_pos 0 +-#define dagc1_desired_level_8_1_len 8 +-#define dagc1_desired_level_8_1_lsb 1 +-#define xd_p_dagc1_apply_delay 0xA125 +-#define dagc1_apply_delay_pos 0 +-#define dagc1_apply_delay_len 7 +-#define dagc1_apply_delay_lsb 0 +-#define xd_p_dagc1_bypass_scale_ctl 0xA126 +-#define dagc1_bypass_scale_ctl_pos 0 +-#define dagc1_bypass_scale_ctl_len 2 +-#define dagc1_bypass_scale_ctl_lsb 0 +-#define xd_p_reg_dagc1_in_sat_cnt_7_0 0xA127 +-#define reg_dagc1_in_sat_cnt_7_0_pos 0 +-#define reg_dagc1_in_sat_cnt_7_0_len 8 +-#define reg_dagc1_in_sat_cnt_7_0_lsb 0 +-#define xd_p_reg_dagc1_in_sat_cnt_15_8 0xA128 +-#define reg_dagc1_in_sat_cnt_15_8_pos 0 +-#define reg_dagc1_in_sat_cnt_15_8_len 8 +-#define reg_dagc1_in_sat_cnt_15_8_lsb 8 +-#define xd_p_reg_dagc1_in_sat_cnt_23_16 0xA129 +-#define reg_dagc1_in_sat_cnt_23_16_pos 0 +-#define reg_dagc1_in_sat_cnt_23_16_len 8 +-#define reg_dagc1_in_sat_cnt_23_16_lsb 16 +-#define xd_p_reg_dagc1_in_sat_cnt_31_24 0xA12A +-#define reg_dagc1_in_sat_cnt_31_24_pos 0 +-#define reg_dagc1_in_sat_cnt_31_24_len 8 +-#define reg_dagc1_in_sat_cnt_31_24_lsb 24 +-#define xd_p_reg_dagc1_out_sat_cnt_7_0 0xA12B +-#define reg_dagc1_out_sat_cnt_7_0_pos 0 +-#define reg_dagc1_out_sat_cnt_7_0_len 8 +-#define reg_dagc1_out_sat_cnt_7_0_lsb 0 +-#define xd_p_reg_dagc1_out_sat_cnt_15_8 0xA12C +-#define reg_dagc1_out_sat_cnt_15_8_pos 0 +-#define reg_dagc1_out_sat_cnt_15_8_len 8 +-#define reg_dagc1_out_sat_cnt_15_8_lsb 8 +-#define xd_p_reg_dagc1_out_sat_cnt_23_16 0xA12D +-#define reg_dagc1_out_sat_cnt_23_16_pos 0 +-#define reg_dagc1_out_sat_cnt_23_16_len 8 +-#define reg_dagc1_out_sat_cnt_23_16_lsb 16 +-#define xd_p_reg_dagc1_out_sat_cnt_31_24 0xA12E +-#define reg_dagc1_out_sat_cnt_31_24_pos 0 +-#define reg_dagc1_out_sat_cnt_31_24_len 8 +-#define reg_dagc1_out_sat_cnt_31_24_lsb 24 +-#define xd_r_dagc1_multiplier_7_0 0xA136 +-#define dagc1_multiplier_7_0_pos 0 +-#define dagc1_multiplier_7_0_len 8 +-#define dagc1_multiplier_7_0_lsb 0 +-#define xd_r_dagc1_multiplier_15_8 0xA137 +-#define dagc1_multiplier_15_8_pos 0 +-#define dagc1_multiplier_15_8_len 8 +-#define dagc1_multiplier_15_8_lsb 8 +-#define xd_r_dagc1_right_shift_bits 0xA138 +-#define dagc1_right_shift_bits_pos 0 +-#define dagc1_right_shift_bits_len 4 +-#define dagc1_right_shift_bits_lsb 0 +-#define xd_p_reg_bfs_fcw_7_0 0xA140 +-#define reg_bfs_fcw_7_0_pos 0 +-#define reg_bfs_fcw_7_0_len 8 +-#define reg_bfs_fcw_7_0_lsb 0 +-#define xd_p_reg_bfs_fcw_15_8 0xA141 +-#define reg_bfs_fcw_15_8_pos 0 +-#define reg_bfs_fcw_15_8_len 8 +-#define reg_bfs_fcw_15_8_lsb 8 +-#define xd_p_reg_bfs_fcw_22_16 0xA142 +-#define reg_bfs_fcw_22_16_pos 0 +-#define reg_bfs_fcw_22_16_len 7 +-#define reg_bfs_fcw_22_16_lsb 16 +-#define xd_p_reg_antif_sf_7_0 0xA144 +-#define reg_antif_sf_7_0_pos 0 +-#define reg_antif_sf_7_0_len 8 +-#define reg_antif_sf_7_0_lsb 0 +-#define xd_p_reg_antif_sf_11_8 0xA145 +-#define reg_antif_sf_11_8_pos 0 +-#define reg_antif_sf_11_8_len 4 +-#define reg_antif_sf_11_8_lsb 8 +-#define xd_r_bfs_fcw_q_7_0 0xA150 +-#define bfs_fcw_q_7_0_pos 0 +-#define bfs_fcw_q_7_0_len 8 +-#define bfs_fcw_q_7_0_lsb 0 +-#define xd_r_bfs_fcw_q_15_8 0xA151 +-#define bfs_fcw_q_15_8_pos 0 +-#define bfs_fcw_q_15_8_len 8 +-#define bfs_fcw_q_15_8_lsb 8 +-#define xd_r_bfs_fcw_q_22_16 0xA152 +-#define bfs_fcw_q_22_16_pos 0 +-#define bfs_fcw_q_22_16_len 7 +-#define bfs_fcw_q_22_16_lsb 16 +-#define xd_p_reg_dca_enu 0xA160 +-#define reg_dca_enu_pos 0 +-#define reg_dca_enu_len 1 +-#define reg_dca_enu_lsb 0 +-#define xd_p_reg_dca_enl 0xA160 +-#define reg_dca_enl_pos 1 +-#define reg_dca_enl_len 1 +-#define reg_dca_enl_lsb 0 +-#define xd_p_reg_dca_lower_chip 0xA160 +-#define reg_dca_lower_chip_pos 2 +-#define reg_dca_lower_chip_len 1 +-#define reg_dca_lower_chip_lsb 0 +-#define xd_p_reg_dca_upper_chip 0xA160 +-#define reg_dca_upper_chip_pos 3 +-#define reg_dca_upper_chip_len 1 +-#define reg_dca_upper_chip_lsb 0 +-#define xd_p_reg_dca_platch 0xA160 +-#define reg_dca_platch_pos 4 +-#define reg_dca_platch_len 1 +-#define reg_dca_platch_lsb 0 +-#define xd_p_reg_dca_th 0xA161 +-#define reg_dca_th_pos 0 +-#define reg_dca_th_len 5 +-#define reg_dca_th_lsb 0 +-#define xd_p_reg_dca_scale 0xA162 +-#define reg_dca_scale_pos 0 +-#define reg_dca_scale_len 4 +-#define reg_dca_scale_lsb 0 +-#define xd_p_reg_dca_tone_7_0 0xA163 +-#define reg_dca_tone_7_0_pos 0 +-#define reg_dca_tone_7_0_len 8 +-#define reg_dca_tone_7_0_lsb 0 +-#define xd_p_reg_dca_tone_12_8 0xA164 +-#define reg_dca_tone_12_8_pos 0 +-#define reg_dca_tone_12_8_len 5 +-#define reg_dca_tone_12_8_lsb 8 +-#define xd_p_reg_dca_time_7_0 0xA165 +-#define reg_dca_time_7_0_pos 0 +-#define reg_dca_time_7_0_len 8 +-#define reg_dca_time_7_0_lsb 0 +-#define xd_p_reg_dca_time_15_8 0xA166 +-#define reg_dca_time_15_8_pos 0 +-#define reg_dca_time_15_8_len 8 +-#define reg_dca_time_15_8_lsb 8 +-#define xd_r_dcasm 0xA167 +-#define dcasm_pos 0 +-#define dcasm_len 3 +-#define dcasm_lsb 0 +-#define xd_p_reg_qnt_valuew_7_0 0xA168 +-#define reg_qnt_valuew_7_0_pos 0 +-#define reg_qnt_valuew_7_0_len 8 +-#define reg_qnt_valuew_7_0_lsb 0 +-#define xd_p_reg_qnt_valuew_10_8 0xA169 +-#define reg_qnt_valuew_10_8_pos 0 +-#define reg_qnt_valuew_10_8_len 3 +-#define reg_qnt_valuew_10_8_lsb 8 +-#define xd_p_dca_sbx_gain_diff_7_0 0xA16A +-#define dca_sbx_gain_diff_7_0_pos 0 +-#define dca_sbx_gain_diff_7_0_len 8 +-#define dca_sbx_gain_diff_7_0_lsb 0 +-#define xd_p_dca_sbx_gain_diff_9_8 0xA16B +-#define dca_sbx_gain_diff_9_8_pos 0 +-#define dca_sbx_gain_diff_9_8_len 2 +-#define dca_sbx_gain_diff_9_8_lsb 8 +-#define xd_p_reg_dca_stand_alone 0xA16C +-#define reg_dca_stand_alone_pos 0 +-#define reg_dca_stand_alone_len 1 +-#define reg_dca_stand_alone_lsb 0 +-#define xd_p_reg_dca_upper_out_en 0xA16C +-#define reg_dca_upper_out_en_pos 1 +-#define reg_dca_upper_out_en_len 1 +-#define reg_dca_upper_out_en_lsb 0 +-#define xd_p_reg_dca_rc_en 0xA16C +-#define reg_dca_rc_en_pos 2 +-#define reg_dca_rc_en_len 1 +-#define reg_dca_rc_en_lsb 0 +-#define xd_p_reg_dca_retrain_send 0xA16C +-#define reg_dca_retrain_send_pos 3 +-#define reg_dca_retrain_send_len 1 +-#define reg_dca_retrain_send_lsb 0 +-#define xd_p_reg_dca_retrain_rec 0xA16C +-#define reg_dca_retrain_rec_pos 4 +-#define reg_dca_retrain_rec_len 1 +-#define reg_dca_retrain_rec_lsb 0 +-#define xd_p_reg_dca_api_tpsrdy 0xA16C +-#define reg_dca_api_tpsrdy_pos 5 +-#define reg_dca_api_tpsrdy_len 1 +-#define reg_dca_api_tpsrdy_lsb 0 +-#define xd_p_reg_dca_symbol_gap 0xA16D +-#define reg_dca_symbol_gap_pos 0 +-#define reg_dca_symbol_gap_len 4 +-#define reg_dca_symbol_gap_lsb 0 +-#define xd_p_reg_qnt_nfvaluew_7_0 0xA16E +-#define reg_qnt_nfvaluew_7_0_pos 0 +-#define reg_qnt_nfvaluew_7_0_len 8 +-#define reg_qnt_nfvaluew_7_0_lsb 0 +-#define xd_p_reg_qnt_nfvaluew_10_8 0xA16F +-#define reg_qnt_nfvaluew_10_8_pos 0 +-#define reg_qnt_nfvaluew_10_8_len 3 +-#define reg_qnt_nfvaluew_10_8_lsb 8 +-#define xd_p_reg_qnt_flatness_thr_7_0 0xA170 +-#define reg_qnt_flatness_thr_7_0_pos 0 +-#define reg_qnt_flatness_thr_7_0_len 8 +-#define reg_qnt_flatness_thr_7_0_lsb 0 +-#define xd_p_reg_qnt_flatness_thr_9_8 0xA171 +-#define reg_qnt_flatness_thr_9_8_pos 0 +-#define reg_qnt_flatness_thr_9_8_len 2 +-#define reg_qnt_flatness_thr_9_8_lsb 8 +-#define xd_p_reg_dca_tone_idx_5_0 0xA171 +-#define reg_dca_tone_idx_5_0_pos 2 +-#define reg_dca_tone_idx_5_0_len 6 +-#define reg_dca_tone_idx_5_0_lsb 0 +-#define xd_p_reg_dca_tone_idx_12_6 0xA172 +-#define reg_dca_tone_idx_12_6_pos 0 +-#define reg_dca_tone_idx_12_6_len 7 +-#define reg_dca_tone_idx_12_6_lsb 6 +-#define xd_p_reg_dca_data_vld 0xA173 +-#define reg_dca_data_vld_pos 0 +-#define reg_dca_data_vld_len 1 +-#define reg_dca_data_vld_lsb 0 +-#define xd_p_reg_dca_read_update 0xA173 +-#define reg_dca_read_update_pos 1 +-#define reg_dca_read_update_len 1 +-#define reg_dca_read_update_lsb 0 +-#define xd_r_reg_dca_data_re_5_0 0xA173 +-#define reg_dca_data_re_5_0_pos 2 +-#define reg_dca_data_re_5_0_len 6 +-#define reg_dca_data_re_5_0_lsb 0 +-#define xd_r_reg_dca_data_re_10_6 0xA174 +-#define reg_dca_data_re_10_6_pos 0 +-#define reg_dca_data_re_10_6_len 5 +-#define reg_dca_data_re_10_6_lsb 6 +-#define xd_r_reg_dca_data_im_7_0 0xA175 +-#define reg_dca_data_im_7_0_pos 0 +-#define reg_dca_data_im_7_0_len 8 +-#define reg_dca_data_im_7_0_lsb 0 +-#define xd_r_reg_dca_data_im_10_8 0xA176 +-#define reg_dca_data_im_10_8_pos 0 +-#define reg_dca_data_im_10_8_len 3 +-#define reg_dca_data_im_10_8_lsb 8 +-#define xd_r_reg_dca_data_h2_7_0 0xA178 +-#define reg_dca_data_h2_7_0_pos 0 +-#define reg_dca_data_h2_7_0_len 8 +-#define reg_dca_data_h2_7_0_lsb 0 +-#define xd_r_reg_dca_data_h2_9_8 0xA179 +-#define reg_dca_data_h2_9_8_pos 0 +-#define reg_dca_data_h2_9_8_len 2 +-#define reg_dca_data_h2_9_8_lsb 8 +-#define xd_p_reg_f_adc_7_0 0xA180 +-#define reg_f_adc_7_0_pos 0 +-#define reg_f_adc_7_0_len 8 +-#define reg_f_adc_7_0_lsb 0 +-#define xd_p_reg_f_adc_15_8 0xA181 +-#define reg_f_adc_15_8_pos 0 +-#define reg_f_adc_15_8_len 8 +-#define reg_f_adc_15_8_lsb 8 +-#define xd_p_reg_f_adc_23_16 0xA182 +-#define reg_f_adc_23_16_pos 0 +-#define reg_f_adc_23_16_len 8 +-#define reg_f_adc_23_16_lsb 16 +-#define xd_r_intp_mu_7_0 0xA190 +-#define intp_mu_7_0_pos 0 +-#define intp_mu_7_0_len 8 +-#define intp_mu_7_0_lsb 0 +-#define xd_r_intp_mu_15_8 0xA191 +-#define intp_mu_15_8_pos 0 +-#define intp_mu_15_8_len 8 +-#define intp_mu_15_8_lsb 8 +-#define xd_r_intp_mu_19_16 0xA192 +-#define intp_mu_19_16_pos 0 +-#define intp_mu_19_16_len 4 +-#define intp_mu_19_16_lsb 16 +-#define xd_p_reg_agc_rst 0xA1A0 +-#define reg_agc_rst_pos 0 +-#define reg_agc_rst_len 1 +-#define reg_agc_rst_lsb 0 +-#define xd_p_rf_agc_en 0xA1A0 +-#define rf_agc_en_pos 1 +-#define rf_agc_en_len 1 +-#define rf_agc_en_lsb 0 +-#define xd_p_rf_agc_dis 0xA1A0 +-#define rf_agc_dis_pos 2 +-#define rf_agc_dis_len 1 +-#define rf_agc_dis_lsb 0 +-#define xd_p_if_agc_rst 0xA1A0 +-#define if_agc_rst_pos 3 +-#define if_agc_rst_len 1 +-#define if_agc_rst_lsb 0 +-#define xd_p_if_agc_en 0xA1A0 +-#define if_agc_en_pos 4 +-#define if_agc_en_len 1 +-#define if_agc_en_lsb 0 +-#define xd_p_if_agc_dis 0xA1A0 +-#define if_agc_dis_pos 5 +-#define if_agc_dis_len 1 +-#define if_agc_dis_lsb 0 +-#define xd_p_agc_lock 0xA1A0 +-#define agc_lock_pos 6 +-#define agc_lock_len 1 +-#define agc_lock_lsb 0 +-#define xd_p_reg_tinr_rst 0xA1A1 +-#define reg_tinr_rst_pos 0 +-#define reg_tinr_rst_len 1 +-#define reg_tinr_rst_lsb 0 +-#define xd_p_reg_tinr_en 0xA1A1 +-#define reg_tinr_en_pos 1 +-#define reg_tinr_en_len 1 +-#define reg_tinr_en_lsb 0 +-#define xd_p_reg_ccifs_en 0xA1A2 +-#define reg_ccifs_en_pos 0 +-#define reg_ccifs_en_len 1 +-#define reg_ccifs_en_lsb 0 +-#define xd_p_reg_ccifs_dis 0xA1A2 +-#define reg_ccifs_dis_pos 1 +-#define reg_ccifs_dis_len 1 +-#define reg_ccifs_dis_lsb 0 +-#define xd_p_reg_ccifs_rst 0xA1A2 +-#define reg_ccifs_rst_pos 2 +-#define reg_ccifs_rst_len 1 +-#define reg_ccifs_rst_lsb 0 +-#define xd_p_reg_ccifs_byp 0xA1A2 +-#define reg_ccifs_byp_pos 3 +-#define reg_ccifs_byp_len 1 +-#define reg_ccifs_byp_lsb 0 +-#define xd_p_reg_ccif_en 0xA1A3 +-#define reg_ccif_en_pos 0 +-#define reg_ccif_en_len 1 +-#define reg_ccif_en_lsb 0 +-#define xd_p_reg_ccif_dis 0xA1A3 +-#define reg_ccif_dis_pos 1 +-#define reg_ccif_dis_len 1 +-#define reg_ccif_dis_lsb 0 +-#define xd_p_reg_ccif_rst 0xA1A3 +-#define reg_ccif_rst_pos 2 +-#define reg_ccif_rst_len 1 +-#define reg_ccif_rst_lsb 0 +-#define xd_p_reg_ccif_byp 0xA1A3 +-#define reg_ccif_byp_pos 3 +-#define reg_ccif_byp_len 1 +-#define reg_ccif_byp_lsb 0 +-#define xd_p_dagc1_rst 0xA1A4 +-#define dagc1_rst_pos 0 +-#define dagc1_rst_len 1 +-#define dagc1_rst_lsb 0 +-#define xd_p_dagc1_en 0xA1A4 +-#define dagc1_en_pos 1 +-#define dagc1_en_len 1 +-#define dagc1_en_lsb 0 +-#define xd_p_dagc1_mode 0xA1A4 +-#define dagc1_mode_pos 2 +-#define dagc1_mode_len 2 +-#define dagc1_mode_lsb 0 +-#define xd_p_dagc1_done 0xA1A4 +-#define dagc1_done_pos 4 +-#define dagc1_done_len 1 +-#define dagc1_done_lsb 0 +-#define xd_p_ccid_rst 0xA1A5 +-#define ccid_rst_pos 0 +-#define ccid_rst_len 1 +-#define ccid_rst_lsb 0 +-#define xd_p_ccid_en 0xA1A5 +-#define ccid_en_pos 1 +-#define ccid_en_len 1 +-#define ccid_en_lsb 0 +-#define xd_p_ccid_mode 0xA1A5 +-#define ccid_mode_pos 2 +-#define ccid_mode_len 2 +-#define ccid_mode_lsb 0 +-#define xd_p_ccid_done 0xA1A5 +-#define ccid_done_pos 4 +-#define ccid_done_len 1 +-#define ccid_done_lsb 0 +-#define xd_r_ccid_deted 0xA1A5 +-#define ccid_deted_pos 5 +-#define ccid_deted_len 1 +-#define ccid_deted_lsb 0 +-#define xd_p_ccid2_en 0xA1A5 +-#define ccid2_en_pos 6 +-#define ccid2_en_len 1 +-#define ccid2_en_lsb 0 +-#define xd_p_ccid2_done 0xA1A5 +-#define ccid2_done_pos 7 +-#define ccid2_done_len 1 +-#define ccid2_done_lsb 0 +-#define xd_p_reg_bfs_en 0xA1A6 +-#define reg_bfs_en_pos 0 +-#define reg_bfs_en_len 1 +-#define reg_bfs_en_lsb 0 +-#define xd_p_reg_bfs_dis 0xA1A6 +-#define reg_bfs_dis_pos 1 +-#define reg_bfs_dis_len 1 +-#define reg_bfs_dis_lsb 0 +-#define xd_p_reg_bfs_rst 0xA1A6 +-#define reg_bfs_rst_pos 2 +-#define reg_bfs_rst_len 1 +-#define reg_bfs_rst_lsb 0 +-#define xd_p_reg_bfs_byp 0xA1A6 +-#define reg_bfs_byp_pos 3 +-#define reg_bfs_byp_len 1 +-#define reg_bfs_byp_lsb 0 +-#define xd_p_reg_antif_en 0xA1A7 +-#define reg_antif_en_pos 0 +-#define reg_antif_en_len 1 +-#define reg_antif_en_lsb 0 +-#define xd_p_reg_antif_dis 0xA1A7 +-#define reg_antif_dis_pos 1 +-#define reg_antif_dis_len 1 +-#define reg_antif_dis_lsb 0 +-#define xd_p_reg_antif_rst 0xA1A7 +-#define reg_antif_rst_pos 2 +-#define reg_antif_rst_len 1 +-#define reg_antif_rst_lsb 0 +-#define xd_p_reg_antif_byp 0xA1A7 +-#define reg_antif_byp_pos 3 +-#define reg_antif_byp_len 1 +-#define reg_antif_byp_lsb 0 +-#define xd_p_intp_en 0xA1A8 +-#define intp_en_pos 0 +-#define intp_en_len 1 +-#define intp_en_lsb 0 +-#define xd_p_intp_dis 0xA1A8 +-#define intp_dis_pos 1 +-#define intp_dis_len 1 +-#define intp_dis_lsb 0 +-#define xd_p_intp_rst 0xA1A8 +-#define intp_rst_pos 2 +-#define intp_rst_len 1 +-#define intp_rst_lsb 0 +-#define xd_p_intp_byp 0xA1A8 +-#define intp_byp_pos 3 +-#define intp_byp_len 1 +-#define intp_byp_lsb 0 +-#define xd_p_reg_acif_en 0xA1A9 +-#define reg_acif_en_pos 0 +-#define reg_acif_en_len 1 +-#define reg_acif_en_lsb 0 +-#define xd_p_reg_acif_dis 0xA1A9 +-#define reg_acif_dis_pos 1 +-#define reg_acif_dis_len 1 +-#define reg_acif_dis_lsb 0 +-#define xd_p_reg_acif_rst 0xA1A9 +-#define reg_acif_rst_pos 2 +-#define reg_acif_rst_len 1 +-#define reg_acif_rst_lsb 0 +-#define xd_p_reg_acif_byp 0xA1A9 +-#define reg_acif_byp_pos 3 +-#define reg_acif_byp_len 1 +-#define reg_acif_byp_lsb 0 +-#define xd_p_reg_acif_sync_mode 0xA1A9 +-#define reg_acif_sync_mode_pos 4 +-#define reg_acif_sync_mode_len 1 +-#define reg_acif_sync_mode_lsb 0 +-#define xd_p_dagc2_rst 0xA1AA +-#define dagc2_rst_pos 0 +-#define dagc2_rst_len 1 +-#define dagc2_rst_lsb 0 +-#define xd_p_dagc2_en 0xA1AA +-#define dagc2_en_pos 1 +-#define dagc2_en_len 1 +-#define dagc2_en_lsb 0 +-#define xd_p_dagc2_mode 0xA1AA +-#define dagc2_mode_pos 2 +-#define dagc2_mode_len 2 +-#define dagc2_mode_lsb 0 +-#define xd_p_dagc2_done 0xA1AA +-#define dagc2_done_pos 4 +-#define dagc2_done_len 1 +-#define dagc2_done_lsb 0 +-#define xd_p_reg_dca_en 0xA1AB +-#define reg_dca_en_pos 0 +-#define reg_dca_en_len 1 +-#define reg_dca_en_lsb 0 +-#define xd_p_dagc2_accumulate_num_2k_7_0 0xA1C0 +-#define dagc2_accumulate_num_2k_7_0_pos 0 +-#define dagc2_accumulate_num_2k_7_0_len 8 +-#define dagc2_accumulate_num_2k_7_0_lsb 0 +-#define xd_p_dagc2_accumulate_num_2k_12_8 0xA1C1 +-#define dagc2_accumulate_num_2k_12_8_pos 0 +-#define dagc2_accumulate_num_2k_12_8_len 5 +-#define dagc2_accumulate_num_2k_12_8_lsb 8 +-#define xd_p_dagc2_accumulate_num_8k_7_0 0xA1C2 +-#define dagc2_accumulate_num_8k_7_0_pos 0 +-#define dagc2_accumulate_num_8k_7_0_len 8 +-#define dagc2_accumulate_num_8k_7_0_lsb 0 +-#define xd_p_dagc2_accumulate_num_8k_12_8 0xA1C3 +-#define dagc2_accumulate_num_8k_12_8_pos 0 +-#define dagc2_accumulate_num_8k_12_8_len 5 +-#define dagc2_accumulate_num_8k_12_8_lsb 8 +-#define xd_p_dagc2_desired_level_2_0 0xA1C3 +-#define dagc2_desired_level_2_0_pos 5 +-#define dagc2_desired_level_2_0_len 3 +-#define dagc2_desired_level_2_0_lsb 0 +-#define xd_p_dagc2_desired_level_8_3 0xA1C4 +-#define dagc2_desired_level_8_3_pos 0 +-#define dagc2_desired_level_8_3_len 6 +-#define dagc2_desired_level_8_3_lsb 3 +-#define xd_p_dagc2_apply_delay 0xA1C5 +-#define dagc2_apply_delay_pos 0 +-#define dagc2_apply_delay_len 7 +-#define dagc2_apply_delay_lsb 0 +-#define xd_p_dagc2_bypass_scale_ctl 0xA1C6 +-#define dagc2_bypass_scale_ctl_pos 0 +-#define dagc2_bypass_scale_ctl_len 3 +-#define dagc2_bypass_scale_ctl_lsb 0 +-#define xd_p_dagc2_programmable_shift1 0xA1C7 +-#define dagc2_programmable_shift1_pos 0 +-#define dagc2_programmable_shift1_len 8 +-#define dagc2_programmable_shift1_lsb 0 +-#define xd_p_dagc2_programmable_shift2 0xA1C8 +-#define dagc2_programmable_shift2_pos 0 +-#define dagc2_programmable_shift2_len 8 +-#define dagc2_programmable_shift2_lsb 0 +-#define xd_p_reg_dagc2_in_sat_cnt_7_0 0xA1C9 +-#define reg_dagc2_in_sat_cnt_7_0_pos 0 +-#define reg_dagc2_in_sat_cnt_7_0_len 8 +-#define reg_dagc2_in_sat_cnt_7_0_lsb 0 +-#define xd_p_reg_dagc2_in_sat_cnt_15_8 0xA1CA +-#define reg_dagc2_in_sat_cnt_15_8_pos 0 +-#define reg_dagc2_in_sat_cnt_15_8_len 8 +-#define reg_dagc2_in_sat_cnt_15_8_lsb 8 +-#define xd_p_reg_dagc2_in_sat_cnt_23_16 0xA1CB +-#define reg_dagc2_in_sat_cnt_23_16_pos 0 +-#define reg_dagc2_in_sat_cnt_23_16_len 8 +-#define reg_dagc2_in_sat_cnt_23_16_lsb 16 +-#define xd_p_reg_dagc2_in_sat_cnt_31_24 0xA1CC +-#define reg_dagc2_in_sat_cnt_31_24_pos 0 +-#define reg_dagc2_in_sat_cnt_31_24_len 8 +-#define reg_dagc2_in_sat_cnt_31_24_lsb 24 +-#define xd_p_reg_dagc2_out_sat_cnt_7_0 0xA1CD +-#define reg_dagc2_out_sat_cnt_7_0_pos 0 +-#define reg_dagc2_out_sat_cnt_7_0_len 8 +-#define reg_dagc2_out_sat_cnt_7_0_lsb 0 +-#define xd_p_reg_dagc2_out_sat_cnt_15_8 0xA1CE +-#define reg_dagc2_out_sat_cnt_15_8_pos 0 +-#define reg_dagc2_out_sat_cnt_15_8_len 8 +-#define reg_dagc2_out_sat_cnt_15_8_lsb 8 +-#define xd_p_reg_dagc2_out_sat_cnt_23_16 0xA1CF +-#define reg_dagc2_out_sat_cnt_23_16_pos 0 +-#define reg_dagc2_out_sat_cnt_23_16_len 8 +-#define reg_dagc2_out_sat_cnt_23_16_lsb 16 +-#define xd_p_reg_dagc2_out_sat_cnt_31_24 0xA1D0 +-#define reg_dagc2_out_sat_cnt_31_24_pos 0 +-#define reg_dagc2_out_sat_cnt_31_24_len 8 +-#define reg_dagc2_out_sat_cnt_31_24_lsb 24 +-#define xd_r_dagc2_multiplier_7_0 0xA1D6 +-#define dagc2_multiplier_7_0_pos 0 +-#define dagc2_multiplier_7_0_len 8 +-#define dagc2_multiplier_7_0_lsb 0 +-#define xd_r_dagc2_multiplier_15_8 0xA1D7 +-#define dagc2_multiplier_15_8_pos 0 +-#define dagc2_multiplier_15_8_len 8 +-#define dagc2_multiplier_15_8_lsb 8 +-#define xd_r_dagc2_right_shift_bits 0xA1D8 +-#define dagc2_right_shift_bits_pos 0 +-#define dagc2_right_shift_bits_len 4 +-#define dagc2_right_shift_bits_lsb 0 +-#define xd_p_cfoe_NS_coeff1_7_0 0xA200 +-#define cfoe_NS_coeff1_7_0_pos 0 +-#define cfoe_NS_coeff1_7_0_len 8 +-#define cfoe_NS_coeff1_7_0_lsb 0 +-#define xd_p_cfoe_NS_coeff1_15_8 0xA201 +-#define cfoe_NS_coeff1_15_8_pos 0 +-#define cfoe_NS_coeff1_15_8_len 8 +-#define cfoe_NS_coeff1_15_8_lsb 8 +-#define xd_p_cfoe_NS_coeff1_23_16 0xA202 +-#define cfoe_NS_coeff1_23_16_pos 0 +-#define cfoe_NS_coeff1_23_16_len 8 +-#define cfoe_NS_coeff1_23_16_lsb 16 +-#define xd_p_cfoe_NS_coeff1_25_24 0xA203 +-#define cfoe_NS_coeff1_25_24_pos 0 +-#define cfoe_NS_coeff1_25_24_len 2 +-#define cfoe_NS_coeff1_25_24_lsb 24 +-#define xd_p_cfoe_NS_coeff2_5_0 0xA203 +-#define cfoe_NS_coeff2_5_0_pos 2 +-#define cfoe_NS_coeff2_5_0_len 6 +-#define cfoe_NS_coeff2_5_0_lsb 0 +-#define xd_p_cfoe_NS_coeff2_13_6 0xA204 +-#define cfoe_NS_coeff2_13_6_pos 0 +-#define cfoe_NS_coeff2_13_6_len 8 +-#define cfoe_NS_coeff2_13_6_lsb 6 +-#define xd_p_cfoe_NS_coeff2_21_14 0xA205 +-#define cfoe_NS_coeff2_21_14_pos 0 +-#define cfoe_NS_coeff2_21_14_len 8 +-#define cfoe_NS_coeff2_21_14_lsb 14 +-#define xd_p_cfoe_NS_coeff2_24_22 0xA206 +-#define cfoe_NS_coeff2_24_22_pos 0 +-#define cfoe_NS_coeff2_24_22_len 3 +-#define cfoe_NS_coeff2_24_22_lsb 22 +-#define xd_p_cfoe_lf_c1_4_0 0xA206 +-#define cfoe_lf_c1_4_0_pos 3 +-#define cfoe_lf_c1_4_0_len 5 +-#define cfoe_lf_c1_4_0_lsb 0 +-#define xd_p_cfoe_lf_c1_12_5 0xA207 +-#define cfoe_lf_c1_12_5_pos 0 +-#define cfoe_lf_c1_12_5_len 8 +-#define cfoe_lf_c1_12_5_lsb 5 +-#define xd_p_cfoe_lf_c1_20_13 0xA208 +-#define cfoe_lf_c1_20_13_pos 0 +-#define cfoe_lf_c1_20_13_len 8 +-#define cfoe_lf_c1_20_13_lsb 13 +-#define xd_p_cfoe_lf_c1_25_21 0xA209 +-#define cfoe_lf_c1_25_21_pos 0 +-#define cfoe_lf_c1_25_21_len 5 +-#define cfoe_lf_c1_25_21_lsb 21 +-#define xd_p_cfoe_lf_c2_2_0 0xA209 +-#define cfoe_lf_c2_2_0_pos 5 +-#define cfoe_lf_c2_2_0_len 3 +-#define cfoe_lf_c2_2_0_lsb 0 +-#define xd_p_cfoe_lf_c2_10_3 0xA20A +-#define cfoe_lf_c2_10_3_pos 0 +-#define cfoe_lf_c2_10_3_len 8 +-#define cfoe_lf_c2_10_3_lsb 3 +-#define xd_p_cfoe_lf_c2_18_11 0xA20B +-#define cfoe_lf_c2_18_11_pos 0 +-#define cfoe_lf_c2_18_11_len 8 +-#define cfoe_lf_c2_18_11_lsb 11 +-#define xd_p_cfoe_lf_c2_25_19 0xA20C +-#define cfoe_lf_c2_25_19_pos 0 +-#define cfoe_lf_c2_25_19_len 7 +-#define cfoe_lf_c2_25_19_lsb 19 +-#define xd_p_cfoe_ifod_7_0 0xA20D +-#define cfoe_ifod_7_0_pos 0 +-#define cfoe_ifod_7_0_len 8 +-#define cfoe_ifod_7_0_lsb 0 +-#define xd_p_cfoe_ifod_10_8 0xA20E +-#define cfoe_ifod_10_8_pos 0 +-#define cfoe_ifod_10_8_len 3 +-#define cfoe_ifod_10_8_lsb 8 +-#define xd_p_cfoe_Divg_ctr_th 0xA20E +-#define cfoe_Divg_ctr_th_pos 4 +-#define cfoe_Divg_ctr_th_len 4 +-#define cfoe_Divg_ctr_th_lsb 0 +-#define xd_p_cfoe_FOT_divg_th 0xA20F +-#define cfoe_FOT_divg_th_pos 0 +-#define cfoe_FOT_divg_th_len 8 +-#define cfoe_FOT_divg_th_lsb 0 +-#define xd_p_cfoe_FOT_cnvg_th 0xA210 +-#define cfoe_FOT_cnvg_th_pos 0 +-#define cfoe_FOT_cnvg_th_len 8 +-#define cfoe_FOT_cnvg_th_lsb 0 +-#define xd_p_reg_cfoe_offset_7_0 0xA211 +-#define reg_cfoe_offset_7_0_pos 0 +-#define reg_cfoe_offset_7_0_len 8 +-#define reg_cfoe_offset_7_0_lsb 0 +-#define xd_p_reg_cfoe_offset_9_8 0xA212 +-#define reg_cfoe_offset_9_8_pos 0 +-#define reg_cfoe_offset_9_8_len 2 +-#define reg_cfoe_offset_9_8_lsb 8 +-#define xd_p_reg_cfoe_ifoe_sign_corr 0xA212 +-#define reg_cfoe_ifoe_sign_corr_pos 2 +-#define reg_cfoe_ifoe_sign_corr_len 1 +-#define reg_cfoe_ifoe_sign_corr_lsb 0 +-#define xd_r_cfoe_fot_LF_output_7_0 0xA218 +-#define cfoe_fot_LF_output_7_0_pos 0 +-#define cfoe_fot_LF_output_7_0_len 8 +-#define cfoe_fot_LF_output_7_0_lsb 0 +-#define xd_r_cfoe_fot_LF_output_15_8 0xA219 +-#define cfoe_fot_LF_output_15_8_pos 0 +-#define cfoe_fot_LF_output_15_8_len 8 +-#define cfoe_fot_LF_output_15_8_lsb 8 +-#define xd_r_cfoe_ifo_metric_7_0 0xA21A +-#define cfoe_ifo_metric_7_0_pos 0 +-#define cfoe_ifo_metric_7_0_len 8 +-#define cfoe_ifo_metric_7_0_lsb 0 +-#define xd_r_cfoe_ifo_metric_15_8 0xA21B +-#define cfoe_ifo_metric_15_8_pos 0 +-#define cfoe_ifo_metric_15_8_len 8 +-#define cfoe_ifo_metric_15_8_lsb 8 +-#define xd_r_cfoe_ifo_metric_23_16 0xA21C +-#define cfoe_ifo_metric_23_16_pos 0 +-#define cfoe_ifo_metric_23_16_len 8 +-#define cfoe_ifo_metric_23_16_lsb 16 +-#define xd_p_ste_Nu 0xA220 +-#define ste_Nu_pos 0 +-#define ste_Nu_len 2 +-#define ste_Nu_lsb 0 +-#define xd_p_ste_GI 0xA220 +-#define ste_GI_pos 2 +-#define ste_GI_len 3 +-#define ste_GI_lsb 0 +-#define xd_p_ste_symbol_num 0xA221 +-#define ste_symbol_num_pos 0 +-#define ste_symbol_num_len 2 +-#define ste_symbol_num_lsb 0 +-#define xd_p_ste_sample_num 0xA221 +-#define ste_sample_num_pos 2 +-#define ste_sample_num_len 2 +-#define ste_sample_num_lsb 0 +-#define xd_p_reg_ste_buf_en 0xA221 +-#define reg_ste_buf_en_pos 7 +-#define reg_ste_buf_en_len 1 +-#define reg_ste_buf_en_lsb 0 +-#define xd_p_ste_FFT_offset_7_0 0xA222 +-#define ste_FFT_offset_7_0_pos 0 +-#define ste_FFT_offset_7_0_len 8 +-#define ste_FFT_offset_7_0_lsb 0 +-#define xd_p_ste_FFT_offset_11_8 0xA223 +-#define ste_FFT_offset_11_8_pos 0 +-#define ste_FFT_offset_11_8_len 4 +-#define ste_FFT_offset_11_8_lsb 8 +-#define xd_p_reg_ste_tstmod 0xA223 +-#define reg_ste_tstmod_pos 5 +-#define reg_ste_tstmod_len 1 +-#define reg_ste_tstmod_lsb 0 +-#define xd_p_ste_adv_start_7_0 0xA224 +-#define ste_adv_start_7_0_pos 0 +-#define ste_adv_start_7_0_len 8 +-#define ste_adv_start_7_0_lsb 0 +-#define xd_p_ste_adv_start_10_8 0xA225 +-#define ste_adv_start_10_8_pos 0 +-#define ste_adv_start_10_8_len 3 +-#define ste_adv_start_10_8_lsb 8 +-#define xd_p_ste_adv_stop 0xA226 +-#define ste_adv_stop_pos 0 +-#define ste_adv_stop_len 8 +-#define ste_adv_stop_lsb 0 +-#define xd_r_ste_P_value_7_0 0xA228 +-#define ste_P_value_7_0_pos 0 +-#define ste_P_value_7_0_len 8 +-#define ste_P_value_7_0_lsb 0 +-#define xd_r_ste_P_value_10_8 0xA229 +-#define ste_P_value_10_8_pos 0 +-#define ste_P_value_10_8_len 3 +-#define ste_P_value_10_8_lsb 8 +-#define xd_r_ste_M_value_7_0 0xA22A +-#define ste_M_value_7_0_pos 0 +-#define ste_M_value_7_0_len 8 +-#define ste_M_value_7_0_lsb 0 +-#define xd_r_ste_M_value_10_8 0xA22B +-#define ste_M_value_10_8_pos 0 +-#define ste_M_value_10_8_len 3 +-#define ste_M_value_10_8_lsb 8 +-#define xd_r_ste_H1 0xA22C +-#define ste_H1_pos 0 +-#define ste_H1_len 7 +-#define ste_H1_lsb 0 +-#define xd_r_ste_H2 0xA22D +-#define ste_H2_pos 0 +-#define ste_H2_len 7 +-#define ste_H2_lsb 0 +-#define xd_r_ste_H3 0xA22E +-#define ste_H3_pos 0 +-#define ste_H3_len 7 +-#define ste_H3_lsb 0 +-#define xd_r_ste_H4 0xA22F +-#define ste_H4_pos 0 +-#define ste_H4_len 7 +-#define ste_H4_lsb 0 +-#define xd_r_ste_Corr_value_I_7_0 0xA230 +-#define ste_Corr_value_I_7_0_pos 0 +-#define ste_Corr_value_I_7_0_len 8 +-#define ste_Corr_value_I_7_0_lsb 0 +-#define xd_r_ste_Corr_value_I_15_8 0xA231 +-#define ste_Corr_value_I_15_8_pos 0 +-#define ste_Corr_value_I_15_8_len 8 +-#define ste_Corr_value_I_15_8_lsb 8 +-#define xd_r_ste_Corr_value_I_23_16 0xA232 +-#define ste_Corr_value_I_23_16_pos 0 +-#define ste_Corr_value_I_23_16_len 8 +-#define ste_Corr_value_I_23_16_lsb 16 +-#define xd_r_ste_Corr_value_I_27_24 0xA233 +-#define ste_Corr_value_I_27_24_pos 0 +-#define ste_Corr_value_I_27_24_len 4 +-#define ste_Corr_value_I_27_24_lsb 24 +-#define xd_r_ste_Corr_value_Q_7_0 0xA234 +-#define ste_Corr_value_Q_7_0_pos 0 +-#define ste_Corr_value_Q_7_0_len 8 +-#define ste_Corr_value_Q_7_0_lsb 0 +-#define xd_r_ste_Corr_value_Q_15_8 0xA235 +-#define ste_Corr_value_Q_15_8_pos 0 +-#define ste_Corr_value_Q_15_8_len 8 +-#define ste_Corr_value_Q_15_8_lsb 8 +-#define xd_r_ste_Corr_value_Q_23_16 0xA236 +-#define ste_Corr_value_Q_23_16_pos 0 +-#define ste_Corr_value_Q_23_16_len 8 +-#define ste_Corr_value_Q_23_16_lsb 16 +-#define xd_r_ste_Corr_value_Q_27_24 0xA237 +-#define ste_Corr_value_Q_27_24_pos 0 +-#define ste_Corr_value_Q_27_24_len 4 +-#define ste_Corr_value_Q_27_24_lsb 24 +-#define xd_r_ste_J_num_7_0 0xA238 +-#define ste_J_num_7_0_pos 0 +-#define ste_J_num_7_0_len 8 +-#define ste_J_num_7_0_lsb 0 +-#define xd_r_ste_J_num_15_8 0xA239 +-#define ste_J_num_15_8_pos 0 +-#define ste_J_num_15_8_len 8 +-#define ste_J_num_15_8_lsb 8 +-#define xd_r_ste_J_num_23_16 0xA23A +-#define ste_J_num_23_16_pos 0 +-#define ste_J_num_23_16_len 8 +-#define ste_J_num_23_16_lsb 16 +-#define xd_r_ste_J_num_31_24 0xA23B +-#define ste_J_num_31_24_pos 0 +-#define ste_J_num_31_24_len 8 +-#define ste_J_num_31_24_lsb 24 +-#define xd_r_ste_J_den_7_0 0xA23C +-#define ste_J_den_7_0_pos 0 +-#define ste_J_den_7_0_len 8 +-#define ste_J_den_7_0_lsb 0 +-#define xd_r_ste_J_den_15_8 0xA23D +-#define ste_J_den_15_8_pos 0 +-#define ste_J_den_15_8_len 8 +-#define ste_J_den_15_8_lsb 8 +-#define xd_r_ste_J_den_18_16 0xA23E +-#define ste_J_den_18_16_pos 0 +-#define ste_J_den_18_16_len 3 +-#define ste_J_den_18_16_lsb 16 +-#define xd_r_ste_Beacon_Indicator 0xA23E +-#define ste_Beacon_Indicator_pos 4 +-#define ste_Beacon_Indicator_len 1 +-#define ste_Beacon_Indicator_lsb 0 +-#define xd_r_tpsd_Frame_Num 0xA250 +-#define tpsd_Frame_Num_pos 0 +-#define tpsd_Frame_Num_len 2 +-#define tpsd_Frame_Num_lsb 0 +-#define xd_r_tpsd_Constel 0xA250 +-#define tpsd_Constel_pos 2 +-#define tpsd_Constel_len 2 +-#define tpsd_Constel_lsb 0 +-#define xd_r_tpsd_GI 0xA250 +-#define tpsd_GI_pos 4 +-#define tpsd_GI_len 2 +-#define tpsd_GI_lsb 0 +-#define xd_r_tpsd_Mode 0xA250 +-#define tpsd_Mode_pos 6 +-#define tpsd_Mode_len 2 +-#define tpsd_Mode_lsb 0 +-#define xd_r_tpsd_CR_HP 0xA251 +-#define tpsd_CR_HP_pos 0 +-#define tpsd_CR_HP_len 3 +-#define tpsd_CR_HP_lsb 0 +-#define xd_r_tpsd_CR_LP 0xA251 +-#define tpsd_CR_LP_pos 3 +-#define tpsd_CR_LP_len 3 +-#define tpsd_CR_LP_lsb 0 +-#define xd_r_tpsd_Hie 0xA252 +-#define tpsd_Hie_pos 0 +-#define tpsd_Hie_len 3 +-#define tpsd_Hie_lsb 0 +-#define xd_r_tpsd_Res_Bits 0xA252 +-#define tpsd_Res_Bits_pos 3 +-#define tpsd_Res_Bits_len 5 +-#define tpsd_Res_Bits_lsb 0 +-#define xd_r_tpsd_Res_Bits_0 0xA253 +-#define tpsd_Res_Bits_0_pos 0 +-#define tpsd_Res_Bits_0_len 1 +-#define tpsd_Res_Bits_0_lsb 0 +-#define xd_r_tpsd_LengthInd 0xA253 +-#define tpsd_LengthInd_pos 1 +-#define tpsd_LengthInd_len 6 +-#define tpsd_LengthInd_lsb 0 +-#define xd_r_tpsd_Cell_Id_7_0 0xA254 +-#define tpsd_Cell_Id_7_0_pos 0 +-#define tpsd_Cell_Id_7_0_len 8 +-#define tpsd_Cell_Id_7_0_lsb 0 +-#define xd_r_tpsd_Cell_Id_15_8 0xA255 +-#define tpsd_Cell_Id_15_8_pos 0 +-#define tpsd_Cell_Id_15_8_len 8 +-#define tpsd_Cell_Id_15_8_lsb 0 +-#define xd_p_reg_fft_mask_tone0_7_0 0xA260 +-#define reg_fft_mask_tone0_7_0_pos 0 +-#define reg_fft_mask_tone0_7_0_len 8 +-#define reg_fft_mask_tone0_7_0_lsb 0 +-#define xd_p_reg_fft_mask_tone0_12_8 0xA261 +-#define reg_fft_mask_tone0_12_8_pos 0 +-#define reg_fft_mask_tone0_12_8_len 5 +-#define reg_fft_mask_tone0_12_8_lsb 8 +-#define xd_p_reg_fft_mask_tone1_7_0 0xA262 +-#define reg_fft_mask_tone1_7_0_pos 0 +-#define reg_fft_mask_tone1_7_0_len 8 +-#define reg_fft_mask_tone1_7_0_lsb 0 +-#define xd_p_reg_fft_mask_tone1_12_8 0xA263 +-#define reg_fft_mask_tone1_12_8_pos 0 +-#define reg_fft_mask_tone1_12_8_len 5 +-#define reg_fft_mask_tone1_12_8_lsb 8 +-#define xd_p_reg_fft_mask_tone2_7_0 0xA264 +-#define reg_fft_mask_tone2_7_0_pos 0 +-#define reg_fft_mask_tone2_7_0_len 8 +-#define reg_fft_mask_tone2_7_0_lsb 0 +-#define xd_p_reg_fft_mask_tone2_12_8 0xA265 +-#define reg_fft_mask_tone2_12_8_pos 0 +-#define reg_fft_mask_tone2_12_8_len 5 +-#define reg_fft_mask_tone2_12_8_lsb 8 +-#define xd_p_reg_fft_mask_tone3_7_0 0xA266 +-#define reg_fft_mask_tone3_7_0_pos 0 +-#define reg_fft_mask_tone3_7_0_len 8 +-#define reg_fft_mask_tone3_7_0_lsb 0 +-#define xd_p_reg_fft_mask_tone3_12_8 0xA267 +-#define reg_fft_mask_tone3_12_8_pos 0 +-#define reg_fft_mask_tone3_12_8_len 5 +-#define reg_fft_mask_tone3_12_8_lsb 8 +-#define xd_p_reg_fft_mask_from0_7_0 0xA268 +-#define reg_fft_mask_from0_7_0_pos 0 +-#define reg_fft_mask_from0_7_0_len 8 +-#define reg_fft_mask_from0_7_0_lsb 0 +-#define xd_p_reg_fft_mask_from0_12_8 0xA269 +-#define reg_fft_mask_from0_12_8_pos 0 +-#define reg_fft_mask_from0_12_8_len 5 +-#define reg_fft_mask_from0_12_8_lsb 8 +-#define xd_p_reg_fft_mask_to0_7_0 0xA26A +-#define reg_fft_mask_to0_7_0_pos 0 +-#define reg_fft_mask_to0_7_0_len 8 +-#define reg_fft_mask_to0_7_0_lsb 0 +-#define xd_p_reg_fft_mask_to0_12_8 0xA26B +-#define reg_fft_mask_to0_12_8_pos 0 +-#define reg_fft_mask_to0_12_8_len 5 +-#define reg_fft_mask_to0_12_8_lsb 8 +-#define xd_p_reg_fft_mask_from1_7_0 0xA26C +-#define reg_fft_mask_from1_7_0_pos 0 +-#define reg_fft_mask_from1_7_0_len 8 +-#define reg_fft_mask_from1_7_0_lsb 0 +-#define xd_p_reg_fft_mask_from1_12_8 0xA26D +-#define reg_fft_mask_from1_12_8_pos 0 +-#define reg_fft_mask_from1_12_8_len 5 +-#define reg_fft_mask_from1_12_8_lsb 8 +-#define xd_p_reg_fft_mask_to1_7_0 0xA26E +-#define reg_fft_mask_to1_7_0_pos 0 +-#define reg_fft_mask_to1_7_0_len 8 +-#define reg_fft_mask_to1_7_0_lsb 0 +-#define xd_p_reg_fft_mask_to1_12_8 0xA26F +-#define reg_fft_mask_to1_12_8_pos 0 +-#define reg_fft_mask_to1_12_8_len 5 +-#define reg_fft_mask_to1_12_8_lsb 8 +-#define xd_p_reg_cge_idx0_7_0 0xA280 +-#define reg_cge_idx0_7_0_pos 0 +-#define reg_cge_idx0_7_0_len 8 +-#define reg_cge_idx0_7_0_lsb 0 +-#define xd_p_reg_cge_idx0_12_8 0xA281 +-#define reg_cge_idx0_12_8_pos 0 +-#define reg_cge_idx0_12_8_len 5 +-#define reg_cge_idx0_12_8_lsb 8 +-#define xd_p_reg_cge_idx1_7_0 0xA282 +-#define reg_cge_idx1_7_0_pos 0 +-#define reg_cge_idx1_7_0_len 8 +-#define reg_cge_idx1_7_0_lsb 0 +-#define xd_p_reg_cge_idx1_12_8 0xA283 +-#define reg_cge_idx1_12_8_pos 0 +-#define reg_cge_idx1_12_8_len 5 +-#define reg_cge_idx1_12_8_lsb 8 +-#define xd_p_reg_cge_idx2_7_0 0xA284 +-#define reg_cge_idx2_7_0_pos 0 +-#define reg_cge_idx2_7_0_len 8 +-#define reg_cge_idx2_7_0_lsb 0 +-#define xd_p_reg_cge_idx2_12_8 0xA285 +-#define reg_cge_idx2_12_8_pos 0 +-#define reg_cge_idx2_12_8_len 5 +-#define reg_cge_idx2_12_8_lsb 8 +-#define xd_p_reg_cge_idx3_7_0 0xA286 +-#define reg_cge_idx3_7_0_pos 0 +-#define reg_cge_idx3_7_0_len 8 +-#define reg_cge_idx3_7_0_lsb 0 +-#define xd_p_reg_cge_idx3_12_8 0xA287 +-#define reg_cge_idx3_12_8_pos 0 +-#define reg_cge_idx3_12_8_len 5 +-#define reg_cge_idx3_12_8_lsb 8 +-#define xd_p_reg_cge_idx4_7_0 0xA288 +-#define reg_cge_idx4_7_0_pos 0 +-#define reg_cge_idx4_7_0_len 8 +-#define reg_cge_idx4_7_0_lsb 0 +-#define xd_p_reg_cge_idx4_12_8 0xA289 +-#define reg_cge_idx4_12_8_pos 0 +-#define reg_cge_idx4_12_8_len 5 +-#define reg_cge_idx4_12_8_lsb 8 +-#define xd_p_reg_cge_idx5_7_0 0xA28A +-#define reg_cge_idx5_7_0_pos 0 +-#define reg_cge_idx5_7_0_len 8 +-#define reg_cge_idx5_7_0_lsb 0 +-#define xd_p_reg_cge_idx5_12_8 0xA28B +-#define reg_cge_idx5_12_8_pos 0 +-#define reg_cge_idx5_12_8_len 5 +-#define reg_cge_idx5_12_8_lsb 8 +-#define xd_p_reg_cge_idx6_7_0 0xA28C +-#define reg_cge_idx6_7_0_pos 0 +-#define reg_cge_idx6_7_0_len 8 +-#define reg_cge_idx6_7_0_lsb 0 +-#define xd_p_reg_cge_idx6_12_8 0xA28D +-#define reg_cge_idx6_12_8_pos 0 +-#define reg_cge_idx6_12_8_len 5 +-#define reg_cge_idx6_12_8_lsb 8 +-#define xd_p_reg_cge_idx7_7_0 0xA28E +-#define reg_cge_idx7_7_0_pos 0 +-#define reg_cge_idx7_7_0_len 8 +-#define reg_cge_idx7_7_0_lsb 0 +-#define xd_p_reg_cge_idx7_12_8 0xA28F +-#define reg_cge_idx7_12_8_pos 0 +-#define reg_cge_idx7_12_8_len 5 +-#define reg_cge_idx7_12_8_lsb 8 +-#define xd_p_reg_cge_idx8_7_0 0xA290 +-#define reg_cge_idx8_7_0_pos 0 +-#define reg_cge_idx8_7_0_len 8 +-#define reg_cge_idx8_7_0_lsb 0 +-#define xd_p_reg_cge_idx8_12_8 0xA291 +-#define reg_cge_idx8_12_8_pos 0 +-#define reg_cge_idx8_12_8_len 5 +-#define reg_cge_idx8_12_8_lsb 8 +-#define xd_p_reg_cge_idx9_7_0 0xA292 +-#define reg_cge_idx9_7_0_pos 0 +-#define reg_cge_idx9_7_0_len 8 +-#define reg_cge_idx9_7_0_lsb 0 +-#define xd_p_reg_cge_idx9_12_8 0xA293 +-#define reg_cge_idx9_12_8_pos 0 +-#define reg_cge_idx9_12_8_len 5 +-#define reg_cge_idx9_12_8_lsb 8 +-#define xd_p_reg_cge_idx10_7_0 0xA294 +-#define reg_cge_idx10_7_0_pos 0 +-#define reg_cge_idx10_7_0_len 8 +-#define reg_cge_idx10_7_0_lsb 0 +-#define xd_p_reg_cge_idx10_12_8 0xA295 +-#define reg_cge_idx10_12_8_pos 0 +-#define reg_cge_idx10_12_8_len 5 +-#define reg_cge_idx10_12_8_lsb 8 +-#define xd_p_reg_cge_idx11_7_0 0xA296 +-#define reg_cge_idx11_7_0_pos 0 +-#define reg_cge_idx11_7_0_len 8 +-#define reg_cge_idx11_7_0_lsb 0 +-#define xd_p_reg_cge_idx11_12_8 0xA297 +-#define reg_cge_idx11_12_8_pos 0 +-#define reg_cge_idx11_12_8_len 5 +-#define reg_cge_idx11_12_8_lsb 8 +-#define xd_p_reg_cge_idx12_7_0 0xA298 +-#define reg_cge_idx12_7_0_pos 0 +-#define reg_cge_idx12_7_0_len 8 +-#define reg_cge_idx12_7_0_lsb 0 +-#define xd_p_reg_cge_idx12_12_8 0xA299 +-#define reg_cge_idx12_12_8_pos 0 +-#define reg_cge_idx12_12_8_len 5 +-#define reg_cge_idx12_12_8_lsb 8 +-#define xd_p_reg_cge_idx13_7_0 0xA29A +-#define reg_cge_idx13_7_0_pos 0 +-#define reg_cge_idx13_7_0_len 8 +-#define reg_cge_idx13_7_0_lsb 0 +-#define xd_p_reg_cge_idx13_12_8 0xA29B +-#define reg_cge_idx13_12_8_pos 0 +-#define reg_cge_idx13_12_8_len 5 +-#define reg_cge_idx13_12_8_lsb 8 +-#define xd_p_reg_cge_idx14_7_0 0xA29C +-#define reg_cge_idx14_7_0_pos 0 +-#define reg_cge_idx14_7_0_len 8 +-#define reg_cge_idx14_7_0_lsb 0 +-#define xd_p_reg_cge_idx14_12_8 0xA29D +-#define reg_cge_idx14_12_8_pos 0 +-#define reg_cge_idx14_12_8_len 5 +-#define reg_cge_idx14_12_8_lsb 8 +-#define xd_p_reg_cge_idx15_7_0 0xA29E +-#define reg_cge_idx15_7_0_pos 0 +-#define reg_cge_idx15_7_0_len 8 +-#define reg_cge_idx15_7_0_lsb 0 +-#define xd_p_reg_cge_idx15_12_8 0xA29F +-#define reg_cge_idx15_12_8_pos 0 +-#define reg_cge_idx15_12_8_len 5 +-#define reg_cge_idx15_12_8_lsb 8 +-#define xd_r_reg_fft_crc 0xA2A8 +-#define reg_fft_crc_pos 0 +-#define reg_fft_crc_len 8 +-#define reg_fft_crc_lsb 0 +-#define xd_p_fd_fft_shift_max 0xA2A9 +-#define fd_fft_shift_max_pos 0 +-#define fd_fft_shift_max_len 4 +-#define fd_fft_shift_max_lsb 0 +-#define xd_r_fd_fft_shift 0xA2A9 +-#define fd_fft_shift_pos 4 +-#define fd_fft_shift_len 4 +-#define fd_fft_shift_lsb 0 +-#define xd_r_fd_fft_frame_num 0xA2AA +-#define fd_fft_frame_num_pos 0 +-#define fd_fft_frame_num_len 2 +-#define fd_fft_frame_num_lsb 0 +-#define xd_r_fd_fft_symbol_count 0xA2AB +-#define fd_fft_symbol_count_pos 0 +-#define fd_fft_symbol_count_len 7 +-#define fd_fft_symbol_count_lsb 0 +-#define xd_r_reg_fft_idx_max_7_0 0xA2AC +-#define reg_fft_idx_max_7_0_pos 0 +-#define reg_fft_idx_max_7_0_len 8 +-#define reg_fft_idx_max_7_0_lsb 0 +-#define xd_r_reg_fft_idx_max_12_8 0xA2AD +-#define reg_fft_idx_max_12_8_pos 0 +-#define reg_fft_idx_max_12_8_len 5 +-#define reg_fft_idx_max_12_8_lsb 8 +-#define xd_p_reg_cge_program 0xA2AE +-#define reg_cge_program_pos 0 +-#define reg_cge_program_len 1 +-#define reg_cge_program_lsb 0 +-#define xd_p_reg_cge_fixed 0xA2AE +-#define reg_cge_fixed_pos 1 +-#define reg_cge_fixed_len 1 +-#define reg_cge_fixed_lsb 0 +-#define xd_p_reg_fft_rotate_en 0xA2AE +-#define reg_fft_rotate_en_pos 2 +-#define reg_fft_rotate_en_len 1 +-#define reg_fft_rotate_en_lsb 0 +-#define xd_p_reg_fft_rotate_base_4_0 0xA2AE +-#define reg_fft_rotate_base_4_0_pos 3 +-#define reg_fft_rotate_base_4_0_len 5 +-#define reg_fft_rotate_base_4_0_lsb 0 +-#define xd_p_reg_fft_rotate_base_12_5 0xA2AF +-#define reg_fft_rotate_base_12_5_pos 0 +-#define reg_fft_rotate_base_12_5_len 8 +-#define reg_fft_rotate_base_12_5_lsb 5 +-#define xd_p_reg_gp_trigger_fd 0xA2B8 +-#define reg_gp_trigger_fd_pos 0 +-#define reg_gp_trigger_fd_len 1 +-#define reg_gp_trigger_fd_lsb 0 +-#define xd_p_reg_trigger_sel_fd 0xA2B8 +-#define reg_trigger_sel_fd_pos 1 +-#define reg_trigger_sel_fd_len 2 +-#define reg_trigger_sel_fd_lsb 0 +-#define xd_p_reg_trigger_module_sel_fd 0xA2B9 +-#define reg_trigger_module_sel_fd_pos 0 +-#define reg_trigger_module_sel_fd_len 6 +-#define reg_trigger_module_sel_fd_lsb 0 +-#define xd_p_reg_trigger_set_sel_fd 0xA2BA +-#define reg_trigger_set_sel_fd_pos 0 +-#define reg_trigger_set_sel_fd_len 6 +-#define reg_trigger_set_sel_fd_lsb 0 +-#define xd_p_reg_fd_noname_7_0 0xA2BC +-#define reg_fd_noname_7_0_pos 0 +-#define reg_fd_noname_7_0_len 8 +-#define reg_fd_noname_7_0_lsb 0 +-#define xd_p_reg_fd_noname_15_8 0xA2BD +-#define reg_fd_noname_15_8_pos 0 +-#define reg_fd_noname_15_8_len 8 +-#define reg_fd_noname_15_8_lsb 8 +-#define xd_p_reg_fd_noname_23_16 0xA2BE +-#define reg_fd_noname_23_16_pos 0 +-#define reg_fd_noname_23_16_len 8 +-#define reg_fd_noname_23_16_lsb 16 +-#define xd_p_reg_fd_noname_31_24 0xA2BF +-#define reg_fd_noname_31_24_pos 0 +-#define reg_fd_noname_31_24_len 8 +-#define reg_fd_noname_31_24_lsb 24 +-#define xd_r_fd_fpcc_cp_corr_signn 0xA2C0 +-#define fd_fpcc_cp_corr_signn_pos 0 +-#define fd_fpcc_cp_corr_signn_len 8 +-#define fd_fpcc_cp_corr_signn_lsb 0 +-#define xd_p_reg_feq_s1 0xA2C1 +-#define reg_feq_s1_pos 0 +-#define reg_feq_s1_len 5 +-#define reg_feq_s1_lsb 0 +-#define xd_p_fd_fpcc_cp_corr_tone_th 0xA2C2 +-#define fd_fpcc_cp_corr_tone_th_pos 0 +-#define fd_fpcc_cp_corr_tone_th_len 6 +-#define fd_fpcc_cp_corr_tone_th_lsb 0 +-#define xd_p_fd_fpcc_cp_corr_symbol_log_th 0xA2C3 +-#define fd_fpcc_cp_corr_symbol_log_th_pos 0 +-#define fd_fpcc_cp_corr_symbol_log_th_len 4 +-#define fd_fpcc_cp_corr_symbol_log_th_lsb 0 +-#define xd_p_fd_fpcc_cp_corr_int 0xA2C4 +-#define fd_fpcc_cp_corr_int_pos 0 +-#define fd_fpcc_cp_corr_int_len 1 +-#define fd_fpcc_cp_corr_int_lsb 0 +-#define xd_p_reg_sfoe_ns_7_0 0xA320 +-#define reg_sfoe_ns_7_0_pos 0 +-#define reg_sfoe_ns_7_0_len 8 +-#define reg_sfoe_ns_7_0_lsb 0 +-#define xd_p_reg_sfoe_ns_14_8 0xA321 +-#define reg_sfoe_ns_14_8_pos 0 +-#define reg_sfoe_ns_14_8_len 7 +-#define reg_sfoe_ns_14_8_lsb 8 +-#define xd_p_reg_sfoe_c1_7_0 0xA322 +-#define reg_sfoe_c1_7_0_pos 0 +-#define reg_sfoe_c1_7_0_len 8 +-#define reg_sfoe_c1_7_0_lsb 0 +-#define xd_p_reg_sfoe_c1_15_8 0xA323 +-#define reg_sfoe_c1_15_8_pos 0 +-#define reg_sfoe_c1_15_8_len 8 +-#define reg_sfoe_c1_15_8_lsb 8 +-#define xd_p_reg_sfoe_c1_17_16 0xA324 +-#define reg_sfoe_c1_17_16_pos 0 +-#define reg_sfoe_c1_17_16_len 2 +-#define reg_sfoe_c1_17_16_lsb 16 +-#define xd_p_reg_sfoe_c2_7_0 0xA325 +-#define reg_sfoe_c2_7_0_pos 0 +-#define reg_sfoe_c2_7_0_len 8 +-#define reg_sfoe_c2_7_0_lsb 0 +-#define xd_p_reg_sfoe_c2_15_8 0xA326 +-#define reg_sfoe_c2_15_8_pos 0 +-#define reg_sfoe_c2_15_8_len 8 +-#define reg_sfoe_c2_15_8_lsb 8 +-#define xd_p_reg_sfoe_c2_17_16 0xA327 +-#define reg_sfoe_c2_17_16_pos 0 +-#define reg_sfoe_c2_17_16_len 2 +-#define reg_sfoe_c2_17_16_lsb 16 +-#define xd_r_reg_sfoe_out_9_2 0xA328 +-#define reg_sfoe_out_9_2_pos 0 +-#define reg_sfoe_out_9_2_len 8 +-#define reg_sfoe_out_9_2_lsb 0 +-#define xd_r_reg_sfoe_out_1_0 0xA329 +-#define reg_sfoe_out_1_0_pos 0 +-#define reg_sfoe_out_1_0_len 2 +-#define reg_sfoe_out_1_0_lsb 0 +-#define xd_p_reg_sfoe_lm_counter_th 0xA32A +-#define reg_sfoe_lm_counter_th_pos 0 +-#define reg_sfoe_lm_counter_th_len 4 +-#define reg_sfoe_lm_counter_th_lsb 0 +-#define xd_p_reg_sfoe_convg_th 0xA32B +-#define reg_sfoe_convg_th_pos 0 +-#define reg_sfoe_convg_th_len 8 +-#define reg_sfoe_convg_th_lsb 0 +-#define xd_p_reg_sfoe_divg_th 0xA32C +-#define reg_sfoe_divg_th_pos 0 +-#define reg_sfoe_divg_th_len 8 +-#define reg_sfoe_divg_th_lsb 0 +-#define xd_p_fd_tpsd_en 0xA330 +-#define fd_tpsd_en_pos 0 +-#define fd_tpsd_en_len 1 +-#define fd_tpsd_en_lsb 0 +-#define xd_p_fd_tpsd_dis 0xA330 +-#define fd_tpsd_dis_pos 1 +-#define fd_tpsd_dis_len 1 +-#define fd_tpsd_dis_lsb 0 +-#define xd_p_fd_tpsd_rst 0xA330 +-#define fd_tpsd_rst_pos 2 +-#define fd_tpsd_rst_len 1 +-#define fd_tpsd_rst_lsb 0 +-#define xd_p_fd_tpsd_lock 0xA330 +-#define fd_tpsd_lock_pos 3 +-#define fd_tpsd_lock_len 1 +-#define fd_tpsd_lock_lsb 0 +-#define xd_r_fd_tpsd_s19 0xA330 +-#define fd_tpsd_s19_pos 4 +-#define fd_tpsd_s19_len 1 +-#define fd_tpsd_s19_lsb 0 +-#define xd_r_fd_tpsd_s17 0xA330 +-#define fd_tpsd_s17_pos 5 +-#define fd_tpsd_s17_len 1 +-#define fd_tpsd_s17_lsb 0 +-#define xd_p_fd_sfr_ste_en 0xA331 +-#define fd_sfr_ste_en_pos 0 +-#define fd_sfr_ste_en_len 1 +-#define fd_sfr_ste_en_lsb 0 +-#define xd_p_fd_sfr_ste_dis 0xA331 +-#define fd_sfr_ste_dis_pos 1 +-#define fd_sfr_ste_dis_len 1 +-#define fd_sfr_ste_dis_lsb 0 +-#define xd_p_fd_sfr_ste_rst 0xA331 +-#define fd_sfr_ste_rst_pos 2 +-#define fd_sfr_ste_rst_len 1 +-#define fd_sfr_ste_rst_lsb 0 +-#define xd_p_fd_sfr_ste_mode 0xA331 +-#define fd_sfr_ste_mode_pos 3 +-#define fd_sfr_ste_mode_len 1 +-#define fd_sfr_ste_mode_lsb 0 +-#define xd_p_fd_sfr_ste_done 0xA331 +-#define fd_sfr_ste_done_pos 4 +-#define fd_sfr_ste_done_len 1 +-#define fd_sfr_ste_done_lsb 0 +-#define xd_p_reg_cfoe_ffoe_en 0xA332 +-#define reg_cfoe_ffoe_en_pos 0 +-#define reg_cfoe_ffoe_en_len 1 +-#define reg_cfoe_ffoe_en_lsb 0 +-#define xd_p_reg_cfoe_ffoe_dis 0xA332 +-#define reg_cfoe_ffoe_dis_pos 1 +-#define reg_cfoe_ffoe_dis_len 1 +-#define reg_cfoe_ffoe_dis_lsb 0 +-#define xd_p_reg_cfoe_ffoe_rst 0xA332 +-#define reg_cfoe_ffoe_rst_pos 2 +-#define reg_cfoe_ffoe_rst_len 1 +-#define reg_cfoe_ffoe_rst_lsb 0 +-#define xd_p_reg_cfoe_ifoe_en 0xA332 +-#define reg_cfoe_ifoe_en_pos 3 +-#define reg_cfoe_ifoe_en_len 1 +-#define reg_cfoe_ifoe_en_lsb 0 +-#define xd_p_reg_cfoe_ifoe_dis 0xA332 +-#define reg_cfoe_ifoe_dis_pos 4 +-#define reg_cfoe_ifoe_dis_len 1 +-#define reg_cfoe_ifoe_dis_lsb 0 +-#define xd_p_reg_cfoe_ifoe_rst 0xA332 +-#define reg_cfoe_ifoe_rst_pos 5 +-#define reg_cfoe_ifoe_rst_len 1 +-#define reg_cfoe_ifoe_rst_lsb 0 +-#define xd_p_reg_cfoe_fot_en 0xA332 +-#define reg_cfoe_fot_en_pos 6 +-#define reg_cfoe_fot_en_len 1 +-#define reg_cfoe_fot_en_lsb 0 +-#define xd_p_reg_cfoe_fot_lm_en 0xA332 +-#define reg_cfoe_fot_lm_en_pos 7 +-#define reg_cfoe_fot_lm_en_len 1 +-#define reg_cfoe_fot_lm_en_lsb 0 +-#define xd_p_reg_cfoe_fot_rst 0xA333 +-#define reg_cfoe_fot_rst_pos 0 +-#define reg_cfoe_fot_rst_len 1 +-#define reg_cfoe_fot_rst_lsb 0 +-#define xd_r_fd_cfoe_ffoe_done 0xA333 +-#define fd_cfoe_ffoe_done_pos 1 +-#define fd_cfoe_ffoe_done_len 1 +-#define fd_cfoe_ffoe_done_lsb 0 +-#define xd_p_fd_cfoe_metric_vld 0xA333 +-#define fd_cfoe_metric_vld_pos 2 +-#define fd_cfoe_metric_vld_len 1 +-#define fd_cfoe_metric_vld_lsb 0 +-#define xd_p_reg_cfoe_ifod_vld 0xA333 +-#define reg_cfoe_ifod_vld_pos 3 +-#define reg_cfoe_ifod_vld_len 1 +-#define reg_cfoe_ifod_vld_lsb 0 +-#define xd_r_fd_cfoe_ifoe_done 0xA333 +-#define fd_cfoe_ifoe_done_pos 4 +-#define fd_cfoe_ifoe_done_len 1 +-#define fd_cfoe_ifoe_done_lsb 0 +-#define xd_r_fd_cfoe_fot_valid 0xA333 +-#define fd_cfoe_fot_valid_pos 5 +-#define fd_cfoe_fot_valid_len 1 +-#define fd_cfoe_fot_valid_lsb 0 +-#define xd_p_reg_cfoe_divg_int 0xA333 +-#define reg_cfoe_divg_int_pos 6 +-#define reg_cfoe_divg_int_len 1 +-#define reg_cfoe_divg_int_lsb 0 +-#define xd_r_reg_cfoe_divg_flag 0xA333 +-#define reg_cfoe_divg_flag_pos 7 +-#define reg_cfoe_divg_flag_len 1 +-#define reg_cfoe_divg_flag_lsb 0 +-#define xd_p_reg_sfoe_en 0xA334 +-#define reg_sfoe_en_pos 0 +-#define reg_sfoe_en_len 1 +-#define reg_sfoe_en_lsb 0 +-#define xd_p_reg_sfoe_dis 0xA334 +-#define reg_sfoe_dis_pos 1 +-#define reg_sfoe_dis_len 1 +-#define reg_sfoe_dis_lsb 0 +-#define xd_p_reg_sfoe_rst 0xA334 +-#define reg_sfoe_rst_pos 2 +-#define reg_sfoe_rst_len 1 +-#define reg_sfoe_rst_lsb 0 +-#define xd_p_reg_sfoe_vld_int 0xA334 +-#define reg_sfoe_vld_int_pos 3 +-#define reg_sfoe_vld_int_len 1 +-#define reg_sfoe_vld_int_lsb 0 +-#define xd_p_reg_sfoe_lm_en 0xA334 +-#define reg_sfoe_lm_en_pos 4 +-#define reg_sfoe_lm_en_len 1 +-#define reg_sfoe_lm_en_lsb 0 +-#define xd_p_reg_sfoe_divg_int 0xA334 +-#define reg_sfoe_divg_int_pos 5 +-#define reg_sfoe_divg_int_len 1 +-#define reg_sfoe_divg_int_lsb 0 +-#define xd_r_reg_sfoe_divg_flag 0xA334 +-#define reg_sfoe_divg_flag_pos 6 +-#define reg_sfoe_divg_flag_len 1 +-#define reg_sfoe_divg_flag_lsb 0 +-#define xd_p_reg_fft_rst 0xA335 +-#define reg_fft_rst_pos 0 +-#define reg_fft_rst_len 1 +-#define reg_fft_rst_lsb 0 +-#define xd_p_reg_fft_fast_beacon 0xA335 +-#define reg_fft_fast_beacon_pos 1 +-#define reg_fft_fast_beacon_len 1 +-#define reg_fft_fast_beacon_lsb 0 +-#define xd_p_reg_fft_fast_valid 0xA335 +-#define reg_fft_fast_valid_pos 2 +-#define reg_fft_fast_valid_len 1 +-#define reg_fft_fast_valid_lsb 0 +-#define xd_p_reg_fft_mask_en 0xA335 +-#define reg_fft_mask_en_pos 3 +-#define reg_fft_mask_en_len 1 +-#define reg_fft_mask_en_lsb 0 +-#define xd_p_reg_fft_crc_en 0xA335 +-#define reg_fft_crc_en_pos 4 +-#define reg_fft_crc_en_len 1 +-#define reg_fft_crc_en_lsb 0 +-#define xd_p_reg_finr_en 0xA336 +-#define reg_finr_en_pos 0 +-#define reg_finr_en_len 1 +-#define reg_finr_en_lsb 0 +-#define xd_p_fd_fste_en 0xA337 +-#define fd_fste_en_pos 1 +-#define fd_fste_en_len 1 +-#define fd_fste_en_lsb 0 +-#define xd_p_fd_sqi_tps_level_shift 0xA338 +-#define fd_sqi_tps_level_shift_pos 0 +-#define fd_sqi_tps_level_shift_len 8 +-#define fd_sqi_tps_level_shift_lsb 0 +-#define xd_p_fd_pilot_ma_len 0xA339 +-#define fd_pilot_ma_len_pos 0 +-#define fd_pilot_ma_len_len 6 +-#define fd_pilot_ma_len_lsb 0 +-#define xd_p_fd_tps_ma_len 0xA33A +-#define fd_tps_ma_len_pos 0 +-#define fd_tps_ma_len_len 6 +-#define fd_tps_ma_len_lsb 0 +-#define xd_p_fd_sqi_s3 0xA33B +-#define fd_sqi_s3_pos 0 +-#define fd_sqi_s3_len 8 +-#define fd_sqi_s3_lsb 0 +-#define xd_p_fd_sqi_dummy_reg_0 0xA33C +-#define fd_sqi_dummy_reg_0_pos 0 +-#define fd_sqi_dummy_reg_0_len 1 +-#define fd_sqi_dummy_reg_0_lsb 0 +-#define xd_p_fd_sqi_debug_sel 0xA33C +-#define fd_sqi_debug_sel_pos 1 +-#define fd_sqi_debug_sel_len 2 +-#define fd_sqi_debug_sel_lsb 0 +-#define xd_p_fd_sqi_s2 0xA33C +-#define fd_sqi_s2_pos 3 +-#define fd_sqi_s2_len 5 +-#define fd_sqi_s2_lsb 0 +-#define xd_p_fd_sqi_dummy_reg_1 0xA33D +-#define fd_sqi_dummy_reg_1_pos 0 +-#define fd_sqi_dummy_reg_1_len 1 +-#define fd_sqi_dummy_reg_1_lsb 0 +-#define xd_p_fd_inr_ignore 0xA33D +-#define fd_inr_ignore_pos 1 +-#define fd_inr_ignore_len 1 +-#define fd_inr_ignore_lsb 0 +-#define xd_p_fd_pilot_ignore 0xA33D +-#define fd_pilot_ignore_pos 2 +-#define fd_pilot_ignore_len 1 +-#define fd_pilot_ignore_lsb 0 +-#define xd_p_fd_etps_ignore 0xA33D +-#define fd_etps_ignore_pos 3 +-#define fd_etps_ignore_len 1 +-#define fd_etps_ignore_lsb 0 +-#define xd_p_fd_sqi_s1 0xA33D +-#define fd_sqi_s1_pos 4 +-#define fd_sqi_s1_len 4 +-#define fd_sqi_s1_lsb 0 +-#define xd_p_reg_fste_ehw_7_0 0xA33E +-#define reg_fste_ehw_7_0_pos 0 +-#define reg_fste_ehw_7_0_len 8 +-#define reg_fste_ehw_7_0_lsb 0 +-#define xd_p_reg_fste_ehw_9_8 0xA33F +-#define reg_fste_ehw_9_8_pos 0 +-#define reg_fste_ehw_9_8_len 2 +-#define reg_fste_ehw_9_8_lsb 8 +-#define xd_p_reg_fste_i_adj_vld 0xA33F +-#define reg_fste_i_adj_vld_pos 2 +-#define reg_fste_i_adj_vld_len 1 +-#define reg_fste_i_adj_vld_lsb 0 +-#define xd_p_reg_fste_phase_ini_7_0 0xA340 +-#define reg_fste_phase_ini_7_0_pos 0 +-#define reg_fste_phase_ini_7_0_len 8 +-#define reg_fste_phase_ini_7_0_lsb 0 +-#define xd_p_reg_fste_phase_ini_11_8 0xA341 +-#define reg_fste_phase_ini_11_8_pos 0 +-#define reg_fste_phase_ini_11_8_len 4 +-#define reg_fste_phase_ini_11_8_lsb 8 +-#define xd_p_reg_fste_phase_inc_3_0 0xA341 +-#define reg_fste_phase_inc_3_0_pos 4 +-#define reg_fste_phase_inc_3_0_len 4 +-#define reg_fste_phase_inc_3_0_lsb 0 +-#define xd_p_reg_fste_phase_inc_11_4 0xA342 +-#define reg_fste_phase_inc_11_4_pos 0 +-#define reg_fste_phase_inc_11_4_len 8 +-#define reg_fste_phase_inc_11_4_lsb 4 +-#define xd_p_reg_fste_acum_cost_cnt_max 0xA343 +-#define reg_fste_acum_cost_cnt_max_pos 0 +-#define reg_fste_acum_cost_cnt_max_len 4 +-#define reg_fste_acum_cost_cnt_max_lsb 0 +-#define xd_p_reg_fste_step_size_std 0xA343 +-#define reg_fste_step_size_std_pos 4 +-#define reg_fste_step_size_std_len 4 +-#define reg_fste_step_size_std_lsb 0 +-#define xd_p_reg_fste_step_size_max 0xA344 +-#define reg_fste_step_size_max_pos 0 +-#define reg_fste_step_size_max_len 4 +-#define reg_fste_step_size_max_lsb 0 +-#define xd_p_reg_fste_step_size_min 0xA344 +-#define reg_fste_step_size_min_pos 4 +-#define reg_fste_step_size_min_len 4 +-#define reg_fste_step_size_min_lsb 0 +-#define xd_p_reg_fste_frac_step_size_7_0 0xA345 +-#define reg_fste_frac_step_size_7_0_pos 0 +-#define reg_fste_frac_step_size_7_0_len 8 +-#define reg_fste_frac_step_size_7_0_lsb 0 +-#define xd_p_reg_fste_frac_step_size_15_8 0xA346 +-#define reg_fste_frac_step_size_15_8_pos 0 +-#define reg_fste_frac_step_size_15_8_len 8 +-#define reg_fste_frac_step_size_15_8_lsb 8 +-#define xd_p_reg_fste_frac_step_size_19_16 0xA347 +-#define reg_fste_frac_step_size_19_16_pos 0 +-#define reg_fste_frac_step_size_19_16_len 4 +-#define reg_fste_frac_step_size_19_16_lsb 16 +-#define xd_p_reg_fste_rpd_dir_cnt_max 0xA347 +-#define reg_fste_rpd_dir_cnt_max_pos 4 +-#define reg_fste_rpd_dir_cnt_max_len 4 +-#define reg_fste_rpd_dir_cnt_max_lsb 0 +-#define xd_p_reg_fste_ehs 0xA348 +-#define reg_fste_ehs_pos 0 +-#define reg_fste_ehs_len 4 +-#define reg_fste_ehs_lsb 0 +-#define xd_p_reg_fste_frac_cost_cnt_max_3_0 0xA348 +-#define reg_fste_frac_cost_cnt_max_3_0_pos 4 +-#define reg_fste_frac_cost_cnt_max_3_0_len 4 +-#define reg_fste_frac_cost_cnt_max_3_0_lsb 0 +-#define xd_p_reg_fste_frac_cost_cnt_max_9_4 0xA349 +-#define reg_fste_frac_cost_cnt_max_9_4_pos 0 +-#define reg_fste_frac_cost_cnt_max_9_4_len 6 +-#define reg_fste_frac_cost_cnt_max_9_4_lsb 4 +-#define xd_p_reg_fste_w0_7_0 0xA34A +-#define reg_fste_w0_7_0_pos 0 +-#define reg_fste_w0_7_0_len 8 +-#define reg_fste_w0_7_0_lsb 0 +-#define xd_p_reg_fste_w0_11_8 0xA34B +-#define reg_fste_w0_11_8_pos 0 +-#define reg_fste_w0_11_8_len 4 +-#define reg_fste_w0_11_8_lsb 8 +-#define xd_p_reg_fste_w1_3_0 0xA34B +-#define reg_fste_w1_3_0_pos 4 +-#define reg_fste_w1_3_0_len 4 +-#define reg_fste_w1_3_0_lsb 0 +-#define xd_p_reg_fste_w1_11_4 0xA34C +-#define reg_fste_w1_11_4_pos 0 +-#define reg_fste_w1_11_4_len 8 +-#define reg_fste_w1_11_4_lsb 4 +-#define xd_p_reg_fste_w2_7_0 0xA34D +-#define reg_fste_w2_7_0_pos 0 +-#define reg_fste_w2_7_0_len 8 +-#define reg_fste_w2_7_0_lsb 0 +-#define xd_p_reg_fste_w2_11_8 0xA34E +-#define reg_fste_w2_11_8_pos 0 +-#define reg_fste_w2_11_8_len 4 +-#define reg_fste_w2_11_8_lsb 8 +-#define xd_p_reg_fste_w3_3_0 0xA34E +-#define reg_fste_w3_3_0_pos 4 +-#define reg_fste_w3_3_0_len 4 +-#define reg_fste_w3_3_0_lsb 0 +-#define xd_p_reg_fste_w3_11_4 0xA34F +-#define reg_fste_w3_11_4_pos 0 +-#define reg_fste_w3_11_4_len 8 +-#define reg_fste_w3_11_4_lsb 4 +-#define xd_p_reg_fste_w4_7_0 0xA350 +-#define reg_fste_w4_7_0_pos 0 +-#define reg_fste_w4_7_0_len 8 +-#define reg_fste_w4_7_0_lsb 0 +-#define xd_p_reg_fste_w4_11_8 0xA351 +-#define reg_fste_w4_11_8_pos 0 +-#define reg_fste_w4_11_8_len 4 +-#define reg_fste_w4_11_8_lsb 8 +-#define xd_p_reg_fste_w5_3_0 0xA351 +-#define reg_fste_w5_3_0_pos 4 +-#define reg_fste_w5_3_0_len 4 +-#define reg_fste_w5_3_0_lsb 0 +-#define xd_p_reg_fste_w5_11_4 0xA352 +-#define reg_fste_w5_11_4_pos 0 +-#define reg_fste_w5_11_4_len 8 +-#define reg_fste_w5_11_4_lsb 4 +-#define xd_p_reg_fste_w6_7_0 0xA353 +-#define reg_fste_w6_7_0_pos 0 +-#define reg_fste_w6_7_0_len 8 +-#define reg_fste_w6_7_0_lsb 0 +-#define xd_p_reg_fste_w6_11_8 0xA354 +-#define reg_fste_w6_11_8_pos 0 +-#define reg_fste_w6_11_8_len 4 +-#define reg_fste_w6_11_8_lsb 8 +-#define xd_p_reg_fste_w7_3_0 0xA354 +-#define reg_fste_w7_3_0_pos 4 +-#define reg_fste_w7_3_0_len 4 +-#define reg_fste_w7_3_0_lsb 0 +-#define xd_p_reg_fste_w7_11_4 0xA355 +-#define reg_fste_w7_11_4_pos 0 +-#define reg_fste_w7_11_4_len 8 +-#define reg_fste_w7_11_4_lsb 4 +-#define xd_p_reg_fste_w8_7_0 0xA356 +-#define reg_fste_w8_7_0_pos 0 +-#define reg_fste_w8_7_0_len 8 +-#define reg_fste_w8_7_0_lsb 0 +-#define xd_p_reg_fste_w8_11_8 0xA357 +-#define reg_fste_w8_11_8_pos 0 +-#define reg_fste_w8_11_8_len 4 +-#define reg_fste_w8_11_8_lsb 8 +-#define xd_p_reg_fste_w9_3_0 0xA357 +-#define reg_fste_w9_3_0_pos 4 +-#define reg_fste_w9_3_0_len 4 +-#define reg_fste_w9_3_0_lsb 0 +-#define xd_p_reg_fste_w9_11_4 0xA358 +-#define reg_fste_w9_11_4_pos 0 +-#define reg_fste_w9_11_4_len 8 +-#define reg_fste_w9_11_4_lsb 4 +-#define xd_p_reg_fste_wa_7_0 0xA359 +-#define reg_fste_wa_7_0_pos 0 +-#define reg_fste_wa_7_0_len 8 +-#define reg_fste_wa_7_0_lsb 0 +-#define xd_p_reg_fste_wa_11_8 0xA35A +-#define reg_fste_wa_11_8_pos 0 +-#define reg_fste_wa_11_8_len 4 +-#define reg_fste_wa_11_8_lsb 8 +-#define xd_p_reg_fste_wb_3_0 0xA35A +-#define reg_fste_wb_3_0_pos 4 +-#define reg_fste_wb_3_0_len 4 +-#define reg_fste_wb_3_0_lsb 0 +-#define xd_p_reg_fste_wb_11_4 0xA35B +-#define reg_fste_wb_11_4_pos 0 +-#define reg_fste_wb_11_4_len 8 +-#define reg_fste_wb_11_4_lsb 4 +-#define xd_r_fd_fste_i_adj 0xA35C +-#define fd_fste_i_adj_pos 0 +-#define fd_fste_i_adj_len 5 +-#define fd_fste_i_adj_lsb 0 +-#define xd_r_fd_fste_f_adj_7_0 0xA35D +-#define fd_fste_f_adj_7_0_pos 0 +-#define fd_fste_f_adj_7_0_len 8 +-#define fd_fste_f_adj_7_0_lsb 0 +-#define xd_r_fd_fste_f_adj_15_8 0xA35E +-#define fd_fste_f_adj_15_8_pos 0 +-#define fd_fste_f_adj_15_8_len 8 +-#define fd_fste_f_adj_15_8_lsb 8 +-#define xd_r_fd_fste_f_adj_19_16 0xA35F +-#define fd_fste_f_adj_19_16_pos 0 +-#define fd_fste_f_adj_19_16_len 4 +-#define fd_fste_f_adj_19_16_lsb 16 +-#define xd_p_reg_feq_Leak_Bypass 0xA366 +-#define reg_feq_Leak_Bypass_pos 0 +-#define reg_feq_Leak_Bypass_len 1 +-#define reg_feq_Leak_Bypass_lsb 0 +-#define xd_p_reg_feq_Leak_Mneg1 0xA366 +-#define reg_feq_Leak_Mneg1_pos 1 +-#define reg_feq_Leak_Mneg1_len 3 +-#define reg_feq_Leak_Mneg1_lsb 0 +-#define xd_p_reg_feq_Leak_B_ShiftQ 0xA366 +-#define reg_feq_Leak_B_ShiftQ_pos 4 +-#define reg_feq_Leak_B_ShiftQ_len 4 +-#define reg_feq_Leak_B_ShiftQ_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float0 0xA367 +-#define reg_feq_Leak_B_Float0_pos 0 +-#define reg_feq_Leak_B_Float0_len 8 +-#define reg_feq_Leak_B_Float0_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float1 0xA368 +-#define reg_feq_Leak_B_Float1_pos 0 +-#define reg_feq_Leak_B_Float1_len 8 +-#define reg_feq_Leak_B_Float1_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float2 0xA369 +-#define reg_feq_Leak_B_Float2_pos 0 +-#define reg_feq_Leak_B_Float2_len 8 +-#define reg_feq_Leak_B_Float2_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float3 0xA36A +-#define reg_feq_Leak_B_Float3_pos 0 +-#define reg_feq_Leak_B_Float3_len 8 +-#define reg_feq_Leak_B_Float3_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float4 0xA36B +-#define reg_feq_Leak_B_Float4_pos 0 +-#define reg_feq_Leak_B_Float4_len 8 +-#define reg_feq_Leak_B_Float4_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float5 0xA36C +-#define reg_feq_Leak_B_Float5_pos 0 +-#define reg_feq_Leak_B_Float5_len 8 +-#define reg_feq_Leak_B_Float5_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float6 0xA36D +-#define reg_feq_Leak_B_Float6_pos 0 +-#define reg_feq_Leak_B_Float6_len 8 +-#define reg_feq_Leak_B_Float6_lsb 0 +-#define xd_p_reg_feq_Leak_B_Float7 0xA36E +-#define reg_feq_Leak_B_Float7_pos 0 +-#define reg_feq_Leak_B_Float7_len 8 +-#define reg_feq_Leak_B_Float7_lsb 0 +-#define xd_r_reg_feq_data_h2_7_0 0xA36F +-#define reg_feq_data_h2_7_0_pos 0 +-#define reg_feq_data_h2_7_0_len 8 +-#define reg_feq_data_h2_7_0_lsb 0 +-#define xd_r_reg_feq_data_h2_9_8 0xA370 +-#define reg_feq_data_h2_9_8_pos 0 +-#define reg_feq_data_h2_9_8_len 2 +-#define reg_feq_data_h2_9_8_lsb 8 +-#define xd_p_reg_feq_leak_use_slice_tps 0xA371 +-#define reg_feq_leak_use_slice_tps_pos 0 +-#define reg_feq_leak_use_slice_tps_len 1 +-#define reg_feq_leak_use_slice_tps_lsb 0 +-#define xd_p_reg_feq_read_update 0xA371 +-#define reg_feq_read_update_pos 1 +-#define reg_feq_read_update_len 1 +-#define reg_feq_read_update_lsb 0 +-#define xd_p_reg_feq_data_vld 0xA371 +-#define reg_feq_data_vld_pos 2 +-#define reg_feq_data_vld_len 1 +-#define reg_feq_data_vld_lsb 0 +-#define xd_p_reg_feq_tone_idx_4_0 0xA371 +-#define reg_feq_tone_idx_4_0_pos 3 +-#define reg_feq_tone_idx_4_0_len 5 +-#define reg_feq_tone_idx_4_0_lsb 0 +-#define xd_p_reg_feq_tone_idx_12_5 0xA372 +-#define reg_feq_tone_idx_12_5_pos 0 +-#define reg_feq_tone_idx_12_5_len 8 +-#define reg_feq_tone_idx_12_5_lsb 5 +-#define xd_r_reg_feq_data_re_7_0 0xA373 +-#define reg_feq_data_re_7_0_pos 0 +-#define reg_feq_data_re_7_0_len 8 +-#define reg_feq_data_re_7_0_lsb 0 +-#define xd_r_reg_feq_data_re_10_8 0xA374 +-#define reg_feq_data_re_10_8_pos 0 +-#define reg_feq_data_re_10_8_len 3 +-#define reg_feq_data_re_10_8_lsb 8 +-#define xd_r_reg_feq_data_im_7_0 0xA375 +-#define reg_feq_data_im_7_0_pos 0 +-#define reg_feq_data_im_7_0_len 8 +-#define reg_feq_data_im_7_0_lsb 0 +-#define xd_r_reg_feq_data_im_10_8 0xA376 +-#define reg_feq_data_im_10_8_pos 0 +-#define reg_feq_data_im_10_8_len 3 +-#define reg_feq_data_im_10_8_lsb 8 +-#define xd_r_reg_feq_y_re 0xA377 +-#define reg_feq_y_re_pos 0 +-#define reg_feq_y_re_len 8 +-#define reg_feq_y_re_lsb 0 +-#define xd_r_reg_feq_y_im 0xA378 +-#define reg_feq_y_im_pos 0 +-#define reg_feq_y_im_len 8 +-#define reg_feq_y_im_lsb 0 +-#define xd_r_reg_feq_h_re_7_0 0xA379 +-#define reg_feq_h_re_7_0_pos 0 +-#define reg_feq_h_re_7_0_len 8 +-#define reg_feq_h_re_7_0_lsb 0 +-#define xd_r_reg_feq_h_re_8 0xA37A +-#define reg_feq_h_re_8_pos 0 +-#define reg_feq_h_re_8_len 1 +-#define reg_feq_h_re_8_lsb 0 +-#define xd_r_reg_feq_h_im_7_0 0xA37B +-#define reg_feq_h_im_7_0_pos 0 +-#define reg_feq_h_im_7_0_len 8 +-#define reg_feq_h_im_7_0_lsb 0 +-#define xd_r_reg_feq_h_im_8 0xA37C +-#define reg_feq_h_im_8_pos 0 +-#define reg_feq_h_im_8_len 1 +-#define reg_feq_h_im_8_lsb 0 +-#define xd_p_fec_super_frm_unit_7_0 0xA380 +-#define fec_super_frm_unit_7_0_pos 0 +-#define fec_super_frm_unit_7_0_len 8 +-#define fec_super_frm_unit_7_0_lsb 0 +-#define xd_p_fec_super_frm_unit_15_8 0xA381 +-#define fec_super_frm_unit_15_8_pos 0 +-#define fec_super_frm_unit_15_8_len 8 +-#define fec_super_frm_unit_15_8_lsb 8 +-#define xd_r_fec_vtb_err_bit_cnt_7_0 0xA382 +-#define fec_vtb_err_bit_cnt_7_0_pos 0 +-#define fec_vtb_err_bit_cnt_7_0_len 8 +-#define fec_vtb_err_bit_cnt_7_0_lsb 0 +-#define xd_r_fec_vtb_err_bit_cnt_15_8 0xA383 +-#define fec_vtb_err_bit_cnt_15_8_pos 0 +-#define fec_vtb_err_bit_cnt_15_8_len 8 +-#define fec_vtb_err_bit_cnt_15_8_lsb 8 +-#define xd_r_fec_vtb_err_bit_cnt_23_16 0xA384 +-#define fec_vtb_err_bit_cnt_23_16_pos 0 +-#define fec_vtb_err_bit_cnt_23_16_len 8 +-#define fec_vtb_err_bit_cnt_23_16_lsb 16 +-#define xd_p_fec_rsd_packet_unit_7_0 0xA385 +-#define fec_rsd_packet_unit_7_0_pos 0 +-#define fec_rsd_packet_unit_7_0_len 8 +-#define fec_rsd_packet_unit_7_0_lsb 0 +-#define xd_p_fec_rsd_packet_unit_15_8 0xA386 +-#define fec_rsd_packet_unit_15_8_pos 0 +-#define fec_rsd_packet_unit_15_8_len 8 +-#define fec_rsd_packet_unit_15_8_lsb 8 +-#define xd_r_fec_rsd_bit_err_cnt_7_0 0xA387 +-#define fec_rsd_bit_err_cnt_7_0_pos 0 +-#define fec_rsd_bit_err_cnt_7_0_len 8 +-#define fec_rsd_bit_err_cnt_7_0_lsb 0 +-#define xd_r_fec_rsd_bit_err_cnt_15_8 0xA388 +-#define fec_rsd_bit_err_cnt_15_8_pos 0 +-#define fec_rsd_bit_err_cnt_15_8_len 8 +-#define fec_rsd_bit_err_cnt_15_8_lsb 8 +-#define xd_r_fec_rsd_bit_err_cnt_23_16 0xA389 +-#define fec_rsd_bit_err_cnt_23_16_pos 0 +-#define fec_rsd_bit_err_cnt_23_16_len 8 +-#define fec_rsd_bit_err_cnt_23_16_lsb 16 +-#define xd_r_fec_rsd_abort_packet_cnt_7_0 0xA38A +-#define fec_rsd_abort_packet_cnt_7_0_pos 0 +-#define fec_rsd_abort_packet_cnt_7_0_len 8 +-#define fec_rsd_abort_packet_cnt_7_0_lsb 0 +-#define xd_r_fec_rsd_abort_packet_cnt_15_8 0xA38B +-#define fec_rsd_abort_packet_cnt_15_8_pos 0 +-#define fec_rsd_abort_packet_cnt_15_8_len 8 +-#define fec_rsd_abort_packet_cnt_15_8_lsb 8 +-#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_7_0 0xA38C +-#define fec_RSD_PKT_NUM_PER_UNIT_7_0_pos 0 +-#define fec_RSD_PKT_NUM_PER_UNIT_7_0_len 8 +-#define fec_RSD_PKT_NUM_PER_UNIT_7_0_lsb 0 +-#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_15_8 0xA38D +-#define fec_RSD_PKT_NUM_PER_UNIT_15_8_pos 0 +-#define fec_RSD_PKT_NUM_PER_UNIT_15_8_len 8 +-#define fec_RSD_PKT_NUM_PER_UNIT_15_8_lsb 8 +-#define xd_p_fec_RS_TH_1_7_0 0xA38E +-#define fec_RS_TH_1_7_0_pos 0 +-#define fec_RS_TH_1_7_0_len 8 +-#define fec_RS_TH_1_7_0_lsb 0 +-#define xd_p_fec_RS_TH_1_15_8 0xA38F +-#define fec_RS_TH_1_15_8_pos 0 +-#define fec_RS_TH_1_15_8_len 8 +-#define fec_RS_TH_1_15_8_lsb 8 +-#define xd_p_fec_RS_TH_2 0xA390 +-#define fec_RS_TH_2_pos 0 +-#define fec_RS_TH_2_len 8 +-#define fec_RS_TH_2_lsb 0 +-#define xd_p_fec_mon_en 0xA391 +-#define fec_mon_en_pos 0 +-#define fec_mon_en_len 1 +-#define fec_mon_en_lsb 0 +-#define xd_p_reg_b8to47 0xA391 +-#define reg_b8to47_pos 1 +-#define reg_b8to47_len 1 +-#define reg_b8to47_lsb 0 +-#define xd_p_reg_rsd_sync_rep 0xA391 +-#define reg_rsd_sync_rep_pos 2 +-#define reg_rsd_sync_rep_len 1 +-#define reg_rsd_sync_rep_lsb 0 +-#define xd_p_fec_rsd_retrain_rst 0xA391 +-#define fec_rsd_retrain_rst_pos 3 +-#define fec_rsd_retrain_rst_len 1 +-#define fec_rsd_retrain_rst_lsb 0 +-#define xd_r_fec_rsd_ber_rdy 0xA391 +-#define fec_rsd_ber_rdy_pos 4 +-#define fec_rsd_ber_rdy_len 1 +-#define fec_rsd_ber_rdy_lsb 0 +-#define xd_p_fec_rsd_ber_rst 0xA391 +-#define fec_rsd_ber_rst_pos 5 +-#define fec_rsd_ber_rst_len 1 +-#define fec_rsd_ber_rst_lsb 0 +-#define xd_r_fec_vtb_ber_rdy 0xA391 +-#define fec_vtb_ber_rdy_pos 6 +-#define fec_vtb_ber_rdy_len 1 +-#define fec_vtb_ber_rdy_lsb 0 +-#define xd_p_fec_vtb_ber_rst 0xA391 +-#define fec_vtb_ber_rst_pos 7 +-#define fec_vtb_ber_rst_len 1 +-#define fec_vtb_ber_rst_lsb 0 +-#define xd_p_reg_vtb_clk40en 0xA392 +-#define reg_vtb_clk40en_pos 0 +-#define reg_vtb_clk40en_len 1 +-#define reg_vtb_clk40en_lsb 0 +-#define xd_p_fec_vtb_rsd_mon_en 0xA392 +-#define fec_vtb_rsd_mon_en_pos 1 +-#define fec_vtb_rsd_mon_en_len 1 +-#define fec_vtb_rsd_mon_en_lsb 0 +-#define xd_p_reg_fec_data_en 0xA392 +-#define reg_fec_data_en_pos 2 +-#define reg_fec_data_en_len 1 +-#define reg_fec_data_en_lsb 0 +-#define xd_p_fec_dummy_reg_2 0xA392 +-#define fec_dummy_reg_2_pos 3 +-#define fec_dummy_reg_2_len 3 +-#define fec_dummy_reg_2_lsb 0 +-#define xd_p_reg_sync_chk 0xA392 +-#define reg_sync_chk_pos 6 +-#define reg_sync_chk_len 1 +-#define reg_sync_chk_lsb 0 +-#define xd_p_fec_rsd_bypass 0xA392 +-#define fec_rsd_bypass_pos 7 +-#define fec_rsd_bypass_len 1 +-#define fec_rsd_bypass_lsb 0 +-#define xd_p_fec_sw_rst 0xA393 +-#define fec_sw_rst_pos 0 +-#define fec_sw_rst_len 1 +-#define fec_sw_rst_lsb 0 +-#define xd_r_fec_vtb_pm_crc 0xA394 +-#define fec_vtb_pm_crc_pos 0 +-#define fec_vtb_pm_crc_len 8 +-#define fec_vtb_pm_crc_lsb 0 +-#define xd_r_fec_vtb_tb_7_crc 0xA395 +-#define fec_vtb_tb_7_crc_pos 0 +-#define fec_vtb_tb_7_crc_len 8 +-#define fec_vtb_tb_7_crc_lsb 0 +-#define xd_r_fec_vtb_tb_6_crc 0xA396 +-#define fec_vtb_tb_6_crc_pos 0 +-#define fec_vtb_tb_6_crc_len 8 +-#define fec_vtb_tb_6_crc_lsb 0 +-#define xd_r_fec_vtb_tb_5_crc 0xA397 +-#define fec_vtb_tb_5_crc_pos 0 +-#define fec_vtb_tb_5_crc_len 8 +-#define fec_vtb_tb_5_crc_lsb 0 +-#define xd_r_fec_vtb_tb_4_crc 0xA398 +-#define fec_vtb_tb_4_crc_pos 0 +-#define fec_vtb_tb_4_crc_len 8 +-#define fec_vtb_tb_4_crc_lsb 0 +-#define xd_r_fec_vtb_tb_3_crc 0xA399 +-#define fec_vtb_tb_3_crc_pos 0 +-#define fec_vtb_tb_3_crc_len 8 +-#define fec_vtb_tb_3_crc_lsb 0 +-#define xd_r_fec_vtb_tb_2_crc 0xA39A +-#define fec_vtb_tb_2_crc_pos 0 +-#define fec_vtb_tb_2_crc_len 8 +-#define fec_vtb_tb_2_crc_lsb 0 +-#define xd_r_fec_vtb_tb_1_crc 0xA39B +-#define fec_vtb_tb_1_crc_pos 0 +-#define fec_vtb_tb_1_crc_len 8 +-#define fec_vtb_tb_1_crc_lsb 0 +-#define xd_r_fec_vtb_tb_0_crc 0xA39C +-#define fec_vtb_tb_0_crc_pos 0 +-#define fec_vtb_tb_0_crc_len 8 +-#define fec_vtb_tb_0_crc_lsb 0 +-#define xd_r_fec_rsd_bank0_crc 0xA39D +-#define fec_rsd_bank0_crc_pos 0 +-#define fec_rsd_bank0_crc_len 8 +-#define fec_rsd_bank0_crc_lsb 0 +-#define xd_r_fec_rsd_bank1_crc 0xA39E +-#define fec_rsd_bank1_crc_pos 0 +-#define fec_rsd_bank1_crc_len 8 +-#define fec_rsd_bank1_crc_lsb 0 +-#define xd_r_fec_idi_vtb_crc 0xA39F +-#define fec_idi_vtb_crc_pos 0 +-#define fec_idi_vtb_crc_len 8 +-#define fec_idi_vtb_crc_lsb 0 +-#define xd_g_reg_tpsd_txmod 0xA3C0 +-#define reg_tpsd_txmod_pos 0 +-#define reg_tpsd_txmod_len 2 +-#define reg_tpsd_txmod_lsb 0 +-#define xd_g_reg_tpsd_gi 0xA3C0 +-#define reg_tpsd_gi_pos 2 +-#define reg_tpsd_gi_len 2 +-#define reg_tpsd_gi_lsb 0 +-#define xd_g_reg_tpsd_hier 0xA3C0 +-#define reg_tpsd_hier_pos 4 +-#define reg_tpsd_hier_len 3 +-#define reg_tpsd_hier_lsb 0 +-#define xd_g_reg_bw 0xA3C1 +-#define reg_bw_pos 2 +-#define reg_bw_len 2 +-#define reg_bw_lsb 0 +-#define xd_g_reg_dec_pri 0xA3C1 +-#define reg_dec_pri_pos 4 +-#define reg_dec_pri_len 1 +-#define reg_dec_pri_lsb 0 +-#define xd_g_reg_tpsd_const 0xA3C1 +-#define reg_tpsd_const_pos 6 +-#define reg_tpsd_const_len 2 +-#define reg_tpsd_const_lsb 0 +-#define xd_g_reg_tpsd_hpcr 0xA3C2 +-#define reg_tpsd_hpcr_pos 0 +-#define reg_tpsd_hpcr_len 3 +-#define reg_tpsd_hpcr_lsb 0 +-#define xd_g_reg_tpsd_lpcr 0xA3C2 +-#define reg_tpsd_lpcr_pos 3 +-#define reg_tpsd_lpcr_len 3 +-#define reg_tpsd_lpcr_lsb 0 +-#define xd_g_reg_ofsm_clk 0xA3D0 +-#define reg_ofsm_clk_pos 0 +-#define reg_ofsm_clk_len 3 +-#define reg_ofsm_clk_lsb 0 +-#define xd_g_reg_fclk_cfg 0xA3D1 +-#define reg_fclk_cfg_pos 0 +-#define reg_fclk_cfg_len 1 +-#define reg_fclk_cfg_lsb 0 +-#define xd_g_reg_fclk_idi 0xA3D1 +-#define reg_fclk_idi_pos 1 +-#define reg_fclk_idi_len 1 +-#define reg_fclk_idi_lsb 0 +-#define xd_g_reg_fclk_odi 0xA3D1 +-#define reg_fclk_odi_pos 2 +-#define reg_fclk_odi_len 1 +-#define reg_fclk_odi_lsb 0 +-#define xd_g_reg_fclk_rsd 0xA3D1 +-#define reg_fclk_rsd_pos 3 +-#define reg_fclk_rsd_len 1 +-#define reg_fclk_rsd_lsb 0 +-#define xd_g_reg_fclk_vtb 0xA3D1 +-#define reg_fclk_vtb_pos 4 +-#define reg_fclk_vtb_len 1 +-#define reg_fclk_vtb_lsb 0 +-#define xd_g_reg_fclk_cste 0xA3D1 +-#define reg_fclk_cste_pos 5 +-#define reg_fclk_cste_len 1 +-#define reg_fclk_cste_lsb 0 +-#define xd_g_reg_fclk_mp2if 0xA3D1 +-#define reg_fclk_mp2if_pos 6 +-#define reg_fclk_mp2if_len 1 +-#define reg_fclk_mp2if_lsb 0 +-#define xd_I2C_i2c_m_slave_addr 0xA400 +-#define i2c_m_slave_addr_pos 0 +-#define i2c_m_slave_addr_len 8 +-#define i2c_m_slave_addr_lsb 0 +-#define xd_I2C_i2c_m_data1 0xA401 +-#define i2c_m_data1_pos 0 +-#define i2c_m_data1_len 8 +-#define i2c_m_data1_lsb 0 +-#define xd_I2C_i2c_m_data2 0xA402 +-#define i2c_m_data2_pos 0 +-#define i2c_m_data2_len 8 +-#define i2c_m_data2_lsb 0 +-#define xd_I2C_i2c_m_data3 0xA403 +-#define i2c_m_data3_pos 0 +-#define i2c_m_data3_len 8 +-#define i2c_m_data3_lsb 0 +-#define xd_I2C_i2c_m_data4 0xA404 +-#define i2c_m_data4_pos 0 +-#define i2c_m_data4_len 8 +-#define i2c_m_data4_lsb 0 +-#define xd_I2C_i2c_m_data5 0xA405 +-#define i2c_m_data5_pos 0 +-#define i2c_m_data5_len 8 +-#define i2c_m_data5_lsb 0 +-#define xd_I2C_i2c_m_data6 0xA406 +-#define i2c_m_data6_pos 0 +-#define i2c_m_data6_len 8 +-#define i2c_m_data6_lsb 0 +-#define xd_I2C_i2c_m_data7 0xA407 +-#define i2c_m_data7_pos 0 +-#define i2c_m_data7_len 8 +-#define i2c_m_data7_lsb 0 +-#define xd_I2C_i2c_m_data8 0xA408 +-#define i2c_m_data8_pos 0 +-#define i2c_m_data8_len 8 +-#define i2c_m_data8_lsb 0 +-#define xd_I2C_i2c_m_data9 0xA409 +-#define i2c_m_data9_pos 0 +-#define i2c_m_data9_len 8 +-#define i2c_m_data9_lsb 0 +-#define xd_I2C_i2c_m_data10 0xA40A +-#define i2c_m_data10_pos 0 +-#define i2c_m_data10_len 8 +-#define i2c_m_data10_lsb 0 +-#define xd_I2C_i2c_m_data11 0xA40B +-#define i2c_m_data11_pos 0 +-#define i2c_m_data11_len 8 +-#define i2c_m_data11_lsb 0 +-#define xd_I2C_i2c_m_cmd_rw 0xA40C +-#define i2c_m_cmd_rw_pos 0 +-#define i2c_m_cmd_rw_len 1 +-#define i2c_m_cmd_rw_lsb 0 +-#define xd_I2C_i2c_m_cmd_rwlen 0xA40C +-#define i2c_m_cmd_rwlen_pos 3 +-#define i2c_m_cmd_rwlen_len 4 +-#define i2c_m_cmd_rwlen_lsb 0 +-#define xd_I2C_i2c_m_status_cmd_exe 0xA40D +-#define i2c_m_status_cmd_exe_pos 0 +-#define i2c_m_status_cmd_exe_len 1 +-#define i2c_m_status_cmd_exe_lsb 0 +-#define xd_I2C_i2c_m_status_wdat_done 0xA40D +-#define i2c_m_status_wdat_done_pos 1 +-#define i2c_m_status_wdat_done_len 1 +-#define i2c_m_status_wdat_done_lsb 0 +-#define xd_I2C_i2c_m_status_wdat_fail 0xA40D +-#define i2c_m_status_wdat_fail_pos 2 +-#define i2c_m_status_wdat_fail_len 1 +-#define i2c_m_status_wdat_fail_lsb 0 +-#define xd_I2C_i2c_m_period 0xA40E +-#define i2c_m_period_pos 0 +-#define i2c_m_period_len 8 +-#define i2c_m_period_lsb 0 +-#define xd_I2C_i2c_m_reg_msb_lsb 0xA40F +-#define i2c_m_reg_msb_lsb_pos 0 +-#define i2c_m_reg_msb_lsb_len 1 +-#define i2c_m_reg_msb_lsb_lsb 0 +-#define xd_I2C_reg_ofdm_rst 0xA40F +-#define reg_ofdm_rst_pos 1 +-#define reg_ofdm_rst_len 1 +-#define reg_ofdm_rst_lsb 0 +-#define xd_I2C_reg_sample_period_on_tuner 0xA40F +-#define reg_sample_period_on_tuner_pos 2 +-#define reg_sample_period_on_tuner_len 1 +-#define reg_sample_period_on_tuner_lsb 0 +-#define xd_I2C_reg_rst_i2c 0xA40F +-#define reg_rst_i2c_pos 3 +-#define reg_rst_i2c_len 1 +-#define reg_rst_i2c_lsb 0 +-#define xd_I2C_reg_ofdm_rst_en 0xA40F +-#define reg_ofdm_rst_en_pos 4 +-#define reg_ofdm_rst_en_len 1 +-#define reg_ofdm_rst_en_lsb 0 +-#define xd_I2C_reg_tuner_sda_sync_on 0xA40F +-#define reg_tuner_sda_sync_on_pos 5 +-#define reg_tuner_sda_sync_on_len 1 +-#define reg_tuner_sda_sync_on_lsb 0 +-#define xd_p_mp2if_data_access_disable_ofsm 0xA500 +-#define mp2if_data_access_disable_ofsm_pos 0 +-#define mp2if_data_access_disable_ofsm_len 1 +-#define mp2if_data_access_disable_ofsm_lsb 0 +-#define xd_p_reg_mp2_sw_rst_ofsm 0xA500 +-#define reg_mp2_sw_rst_ofsm_pos 1 +-#define reg_mp2_sw_rst_ofsm_len 1 +-#define reg_mp2_sw_rst_ofsm_lsb 0 +-#define xd_p_reg_mp2if_clk_en_ofsm 0xA500 +-#define reg_mp2if_clk_en_ofsm_pos 2 +-#define reg_mp2if_clk_en_ofsm_len 1 +-#define reg_mp2if_clk_en_ofsm_lsb 0 +-#define xd_r_mp2if_sync_byte_locked 0xA500 +-#define mp2if_sync_byte_locked_pos 3 +-#define mp2if_sync_byte_locked_len 1 +-#define mp2if_sync_byte_locked_lsb 0 +-#define xd_r_mp2if_ts_not_188 0xA500 +-#define mp2if_ts_not_188_pos 4 +-#define mp2if_ts_not_188_len 1 +-#define mp2if_ts_not_188_lsb 0 +-#define xd_r_mp2if_psb_empty 0xA500 +-#define mp2if_psb_empty_pos 5 +-#define mp2if_psb_empty_len 1 +-#define mp2if_psb_empty_lsb 0 +-#define xd_r_mp2if_psb_overflow 0xA500 +-#define mp2if_psb_overflow_pos 6 +-#define mp2if_psb_overflow_len 1 +-#define mp2if_psb_overflow_lsb 0 +-#define xd_p_mp2if_keep_sf_sync_byte_ofsm 0xA500 +-#define mp2if_keep_sf_sync_byte_ofsm_pos 7 +-#define mp2if_keep_sf_sync_byte_ofsm_len 1 +-#define mp2if_keep_sf_sync_byte_ofsm_lsb 0 +-#define xd_r_mp2if_psb_mp2if_num_pkt 0xA501 +-#define mp2if_psb_mp2if_num_pkt_pos 0 +-#define mp2if_psb_mp2if_num_pkt_len 6 +-#define mp2if_psb_mp2if_num_pkt_lsb 0 +-#define xd_p_reg_mpeg_full_speed_ofsm 0xA501 +-#define reg_mpeg_full_speed_ofsm_pos 6 +-#define reg_mpeg_full_speed_ofsm_len 1 +-#define reg_mpeg_full_speed_ofsm_lsb 0 +-#define xd_p_mp2if_mpeg_ser_mode_ofsm 0xA501 +-#define mp2if_mpeg_ser_mode_ofsm_pos 7 +-#define mp2if_mpeg_ser_mode_ofsm_len 1 +-#define mp2if_mpeg_ser_mode_ofsm_lsb 0 +-#define xd_p_reg_sw_mon51 0xA600 +-#define reg_sw_mon51_pos 0 +-#define reg_sw_mon51_len 8 +-#define reg_sw_mon51_lsb 0 +-#define xd_p_reg_top_pcsel 0xA601 +-#define reg_top_pcsel_pos 0 +-#define reg_top_pcsel_len 1 +-#define reg_top_pcsel_lsb 0 +-#define xd_p_reg_top_rs232 0xA601 +-#define reg_top_rs232_pos 1 +-#define reg_top_rs232_len 1 +-#define reg_top_rs232_lsb 0 +-#define xd_p_reg_top_pcout 0xA601 +-#define reg_top_pcout_pos 2 +-#define reg_top_pcout_len 1 +-#define reg_top_pcout_lsb 0 +-#define xd_p_reg_top_debug 0xA601 +-#define reg_top_debug_pos 3 +-#define reg_top_debug_len 1 +-#define reg_top_debug_lsb 0 +-#define xd_p_reg_top_adcdly 0xA601 +-#define reg_top_adcdly_pos 4 +-#define reg_top_adcdly_len 2 +-#define reg_top_adcdly_lsb 0 +-#define xd_p_reg_top_pwrdw 0xA601 +-#define reg_top_pwrdw_pos 6 +-#define reg_top_pwrdw_len 1 +-#define reg_top_pwrdw_lsb 0 +-#define xd_p_reg_top_pwrdw_inv 0xA601 +-#define reg_top_pwrdw_inv_pos 7 +-#define reg_top_pwrdw_inv_len 1 +-#define reg_top_pwrdw_inv_lsb 0 +-#define xd_p_reg_top_int_inv 0xA602 +-#define reg_top_int_inv_pos 0 +-#define reg_top_int_inv_len 1 +-#define reg_top_int_inv_lsb 0 +-#define xd_p_reg_top_dio_sel 0xA602 +-#define reg_top_dio_sel_pos 1 +-#define reg_top_dio_sel_len 1 +-#define reg_top_dio_sel_lsb 0 +-#define xd_p_reg_top_gpioon0 0xA603 +-#define reg_top_gpioon0_pos 0 +-#define reg_top_gpioon0_len 1 +-#define reg_top_gpioon0_lsb 0 +-#define xd_p_reg_top_gpioon1 0xA603 +-#define reg_top_gpioon1_pos 1 +-#define reg_top_gpioon1_len 1 +-#define reg_top_gpioon1_lsb 0 +-#define xd_p_reg_top_gpioon2 0xA603 +-#define reg_top_gpioon2_pos 2 +-#define reg_top_gpioon2_len 1 +-#define reg_top_gpioon2_lsb 0 +-#define xd_p_reg_top_gpioon3 0xA603 +-#define reg_top_gpioon3_pos 3 +-#define reg_top_gpioon3_len 1 +-#define reg_top_gpioon3_lsb 0 +-#define xd_p_reg_top_lockon1 0xA603 +-#define reg_top_lockon1_pos 4 +-#define reg_top_lockon1_len 1 +-#define reg_top_lockon1_lsb 0 +-#define xd_p_reg_top_lockon2 0xA603 +-#define reg_top_lockon2_pos 5 +-#define reg_top_lockon2_len 1 +-#define reg_top_lockon2_lsb 0 +-#define xd_p_reg_top_gpioo0 0xA604 +-#define reg_top_gpioo0_pos 0 +-#define reg_top_gpioo0_len 1 +-#define reg_top_gpioo0_lsb 0 +-#define xd_p_reg_top_gpioo1 0xA604 +-#define reg_top_gpioo1_pos 1 +-#define reg_top_gpioo1_len 1 +-#define reg_top_gpioo1_lsb 0 +-#define xd_p_reg_top_gpioo2 0xA604 +-#define reg_top_gpioo2_pos 2 +-#define reg_top_gpioo2_len 1 +-#define reg_top_gpioo2_lsb 0 +-#define xd_p_reg_top_gpioo3 0xA604 +-#define reg_top_gpioo3_pos 3 +-#define reg_top_gpioo3_len 1 +-#define reg_top_gpioo3_lsb 0 +-#define xd_p_reg_top_lock1 0xA604 +-#define reg_top_lock1_pos 4 +-#define reg_top_lock1_len 1 +-#define reg_top_lock1_lsb 0 +-#define xd_p_reg_top_lock2 0xA604 +-#define reg_top_lock2_pos 5 +-#define reg_top_lock2_len 1 +-#define reg_top_lock2_lsb 0 +-#define xd_p_reg_top_gpioen0 0xA605 +-#define reg_top_gpioen0_pos 0 +-#define reg_top_gpioen0_len 1 +-#define reg_top_gpioen0_lsb 0 +-#define xd_p_reg_top_gpioen1 0xA605 +-#define reg_top_gpioen1_pos 1 +-#define reg_top_gpioen1_len 1 +-#define reg_top_gpioen1_lsb 0 +-#define xd_p_reg_top_gpioen2 0xA605 +-#define reg_top_gpioen2_pos 2 +-#define reg_top_gpioen2_len 1 +-#define reg_top_gpioen2_lsb 0 +-#define xd_p_reg_top_gpioen3 0xA605 +-#define reg_top_gpioen3_pos 3 +-#define reg_top_gpioen3_len 1 +-#define reg_top_gpioen3_lsb 0 +-#define xd_p_reg_top_locken1 0xA605 +-#define reg_top_locken1_pos 4 +-#define reg_top_locken1_len 1 +-#define reg_top_locken1_lsb 0 +-#define xd_p_reg_top_locken2 0xA605 +-#define reg_top_locken2_pos 5 +-#define reg_top_locken2_len 1 +-#define reg_top_locken2_lsb 0 +-#define xd_r_reg_top_gpioi0 0xA606 +-#define reg_top_gpioi0_pos 0 +-#define reg_top_gpioi0_len 1 +-#define reg_top_gpioi0_lsb 0 +-#define xd_r_reg_top_gpioi1 0xA606 +-#define reg_top_gpioi1_pos 1 +-#define reg_top_gpioi1_len 1 +-#define reg_top_gpioi1_lsb 0 +-#define xd_r_reg_top_gpioi2 0xA606 +-#define reg_top_gpioi2_pos 2 +-#define reg_top_gpioi2_len 1 +-#define reg_top_gpioi2_lsb 0 +-#define xd_r_reg_top_gpioi3 0xA606 +-#define reg_top_gpioi3_pos 3 +-#define reg_top_gpioi3_len 1 +-#define reg_top_gpioi3_lsb 0 +-#define xd_r_reg_top_locki1 0xA606 +-#define reg_top_locki1_pos 4 +-#define reg_top_locki1_len 1 +-#define reg_top_locki1_lsb 0 +-#define xd_r_reg_top_locki2 0xA606 +-#define reg_top_locki2_pos 5 +-#define reg_top_locki2_len 1 +-#define reg_top_locki2_lsb 0 +-#define xd_p_reg_dummy_7_0 0xA608 +-#define reg_dummy_7_0_pos 0 +-#define reg_dummy_7_0_len 8 +-#define reg_dummy_7_0_lsb 0 +-#define xd_p_reg_dummy_15_8 0xA609 +-#define reg_dummy_15_8_pos 0 +-#define reg_dummy_15_8_len 8 +-#define reg_dummy_15_8_lsb 8 +-#define xd_p_reg_dummy_23_16 0xA60A +-#define reg_dummy_23_16_pos 0 +-#define reg_dummy_23_16_len 8 +-#define reg_dummy_23_16_lsb 16 +-#define xd_p_reg_dummy_31_24 0xA60B +-#define reg_dummy_31_24_pos 0 +-#define reg_dummy_31_24_len 8 +-#define reg_dummy_31_24_lsb 24 +-#define xd_p_reg_dummy_39_32 0xA60C +-#define reg_dummy_39_32_pos 0 +-#define reg_dummy_39_32_len 8 +-#define reg_dummy_39_32_lsb 32 +-#define xd_p_reg_dummy_47_40 0xA60D +-#define reg_dummy_47_40_pos 0 +-#define reg_dummy_47_40_len 8 +-#define reg_dummy_47_40_lsb 40 +-#define xd_p_reg_dummy_55_48 0xA60E +-#define reg_dummy_55_48_pos 0 +-#define reg_dummy_55_48_len 8 +-#define reg_dummy_55_48_lsb 48 +-#define xd_p_reg_dummy_63_56 0xA60F +-#define reg_dummy_63_56_pos 0 +-#define reg_dummy_63_56_len 8 +-#define reg_dummy_63_56_lsb 56 +-#define xd_p_reg_dummy_71_64 0xA610 +-#define reg_dummy_71_64_pos 0 +-#define reg_dummy_71_64_len 8 +-#define reg_dummy_71_64_lsb 64 +-#define xd_p_reg_dummy_79_72 0xA611 +-#define reg_dummy_79_72_pos 0 +-#define reg_dummy_79_72_len 8 +-#define reg_dummy_79_72_lsb 72 +-#define xd_p_reg_dummy_87_80 0xA612 +-#define reg_dummy_87_80_pos 0 +-#define reg_dummy_87_80_len 8 +-#define reg_dummy_87_80_lsb 80 +-#define xd_p_reg_dummy_95_88 0xA613 +-#define reg_dummy_95_88_pos 0 +-#define reg_dummy_95_88_len 8 +-#define reg_dummy_95_88_lsb 88 +-#define xd_p_reg_dummy_103_96 0xA614 +-#define reg_dummy_103_96_pos 0 +-#define reg_dummy_103_96_len 8 +-#define reg_dummy_103_96_lsb 96 +- +-#define xd_p_reg_unplug_flag 0xA615 +-#define reg_unplug_flag_pos 0 +-#define reg_unplug_flag_len 1 +-#define reg_unplug_flag_lsb 104 +- +-#define xd_p_reg_api_dca_stes_request 0xA615 +-#define reg_api_dca_stes_request_pos 1 +-#define reg_api_dca_stes_request_len 1 +-#define reg_api_dca_stes_request_lsb 0 +- +-#define xd_p_reg_back_to_dca_flag 0xA615 +-#define reg_back_to_dca_flag_pos 2 +-#define reg_back_to_dca_flag_len 1 +-#define reg_back_to_dca_flag_lsb 106 +- +-#define xd_p_reg_api_retrain_request 0xA615 +-#define reg_api_retrain_request_pos 3 +-#define reg_api_retrain_request_len 1 +-#define reg_api_retrain_request_lsb 0 +- +-#define xd_p_reg_Dyn_Top_Try_flag 0xA615 +-#define reg_Dyn_Top_Try_flag_pos 3 +-#define reg_Dyn_Top_Try_flag_len 1 +-#define reg_Dyn_Top_Try_flag_lsb 107 +- +-#define xd_p_reg_API_retrain_freeze_flag 0xA615 +-#define reg_API_retrain_freeze_flag_pos 4 +-#define reg_API_retrain_freeze_flag_len 1 +-#define reg_API_retrain_freeze_flag_lsb 108 +- +-#define xd_p_reg_dummy_111_104 0xA615 +-#define reg_dummy_111_104_pos 0 +-#define reg_dummy_111_104_len 8 +-#define reg_dummy_111_104_lsb 104 +-#define xd_p_reg_dummy_119_112 0xA616 +-#define reg_dummy_119_112_pos 0 +-#define reg_dummy_119_112_len 8 +-#define reg_dummy_119_112_lsb 112 +-#define xd_p_reg_dummy_127_120 0xA617 +-#define reg_dummy_127_120_pos 0 +-#define reg_dummy_127_120_len 8 +-#define reg_dummy_127_120_lsb 120 +-#define xd_p_reg_dummy_135_128 0xA618 +-#define reg_dummy_135_128_pos 0 +-#define reg_dummy_135_128_len 8 +-#define reg_dummy_135_128_lsb 128 +- +-#define xd_p_reg_dummy_143_136 0xA619 +-#define reg_dummy_143_136_pos 0 +-#define reg_dummy_143_136_len 8 +-#define reg_dummy_143_136_lsb 136 +- +-#define xd_p_reg_CCIR_dis 0xA619 +-#define reg_CCIR_dis_pos 0 +-#define reg_CCIR_dis_len 1 +-#define reg_CCIR_dis_lsb 0 +- +-#define xd_p_reg_dummy_151_144 0xA61A +-#define reg_dummy_151_144_pos 0 +-#define reg_dummy_151_144_len 8 +-#define reg_dummy_151_144_lsb 144 +- +-#define xd_p_reg_dummy_159_152 0xA61B +-#define reg_dummy_159_152_pos 0 +-#define reg_dummy_159_152_len 8 +-#define reg_dummy_159_152_lsb 152 +- +-#define xd_p_reg_dummy_167_160 0xA61C +-#define reg_dummy_167_160_pos 0 +-#define reg_dummy_167_160_len 8 +-#define reg_dummy_167_160_lsb 160 +- +-#define xd_p_reg_dummy_175_168 0xA61D +-#define reg_dummy_175_168_pos 0 +-#define reg_dummy_175_168_len 8 +-#define reg_dummy_175_168_lsb 168 +- +-#define xd_p_reg_dummy_183_176 0xA61E +-#define reg_dummy_183_176_pos 0 +-#define reg_dummy_183_176_len 8 +-#define reg_dummy_183_176_lsb 176 +- +-#define xd_p_reg_ofsm_read_rbc_en 0xA61E +-#define reg_ofsm_read_rbc_en_pos 2 +-#define reg_ofsm_read_rbc_en_len 1 +-#define reg_ofsm_read_rbc_en_lsb 0 +- +-#define xd_p_reg_ce_filter_selection_dis 0xA61E +-#define reg_ce_filter_selection_dis_pos 1 +-#define reg_ce_filter_selection_dis_len 1 +-#define reg_ce_filter_selection_dis_lsb 0 +- +-#define xd_p_reg_OFSM_version_control_7_0 0xA611 +-#define reg_OFSM_version_control_7_0_pos 0 +-#define reg_OFSM_version_control_7_0_len 8 +-#define reg_OFSM_version_control_7_0_lsb 0 +- +-#define xd_p_reg_OFSM_version_control_15_8 0xA61F +-#define reg_OFSM_version_control_15_8_pos 0 +-#define reg_OFSM_version_control_15_8_len 8 +-#define reg_OFSM_version_control_15_8_lsb 0 +- +-#define xd_p_reg_OFSM_version_control_23_16 0xA620 +-#define reg_OFSM_version_control_23_16_pos 0 +-#define reg_OFSM_version_control_23_16_len 8 +-#define reg_OFSM_version_control_23_16_lsb 0 +- +-#define xd_p_reg_dummy_191_184 0xA61F +-#define reg_dummy_191_184_pos 0 +-#define reg_dummy_191_184_len 8 +-#define reg_dummy_191_184_lsb 184 +- +-#define xd_p_reg_dummy_199_192 0xA620 +-#define reg_dummy_199_192_pos 0 +-#define reg_dummy_199_192_len 8 +-#define reg_dummy_199_192_lsb 192 +- +-#define xd_p_reg_ce_en 0xABC0 +-#define reg_ce_en_pos 0 +-#define reg_ce_en_len 1 +-#define reg_ce_en_lsb 0 +-#define xd_p_reg_ce_fctrl_en 0xABC0 +-#define reg_ce_fctrl_en_pos 1 +-#define reg_ce_fctrl_en_len 1 +-#define reg_ce_fctrl_en_lsb 0 +-#define xd_p_reg_ce_fste_tdi 0xABC0 +-#define reg_ce_fste_tdi_pos 2 +-#define reg_ce_fste_tdi_len 1 +-#define reg_ce_fste_tdi_lsb 0 +-#define xd_p_reg_ce_dynamic 0xABC0 +-#define reg_ce_dynamic_pos 3 +-#define reg_ce_dynamic_len 1 +-#define reg_ce_dynamic_lsb 0 +-#define xd_p_reg_ce_conf 0xABC0 +-#define reg_ce_conf_pos 4 +-#define reg_ce_conf_len 2 +-#define reg_ce_conf_lsb 0 +-#define xd_p_reg_ce_dyn12 0xABC0 +-#define reg_ce_dyn12_pos 6 +-#define reg_ce_dyn12_len 1 +-#define reg_ce_dyn12_lsb 0 +-#define xd_p_reg_ce_derot_en 0xABC0 +-#define reg_ce_derot_en_pos 7 +-#define reg_ce_derot_en_len 1 +-#define reg_ce_derot_en_lsb 0 +-#define xd_p_reg_ce_dynamic_th_7_0 0xABC1 +-#define reg_ce_dynamic_th_7_0_pos 0 +-#define reg_ce_dynamic_th_7_0_len 8 +-#define reg_ce_dynamic_th_7_0_lsb 0 +-#define xd_p_reg_ce_dynamic_th_15_8 0xABC2 +-#define reg_ce_dynamic_th_15_8_pos 0 +-#define reg_ce_dynamic_th_15_8_len 8 +-#define reg_ce_dynamic_th_15_8_lsb 8 +-#define xd_p_reg_ce_s1 0xABC3 +-#define reg_ce_s1_pos 0 +-#define reg_ce_s1_len 5 +-#define reg_ce_s1_lsb 0 +-#define xd_p_reg_ce_var_forced_value 0xABC3 +-#define reg_ce_var_forced_value_pos 5 +-#define reg_ce_var_forced_value_len 3 +-#define reg_ce_var_forced_value_lsb 0 +-#define xd_p_reg_ce_data_im_7_0 0xABC4 +-#define reg_ce_data_im_7_0_pos 0 +-#define reg_ce_data_im_7_0_len 8 +-#define reg_ce_data_im_7_0_lsb 0 +-#define xd_p_reg_ce_data_im_8 0xABC5 +-#define reg_ce_data_im_8_pos 0 +-#define reg_ce_data_im_8_len 1 +-#define reg_ce_data_im_8_lsb 0 +-#define xd_p_reg_ce_data_re_6_0 0xABC5 +-#define reg_ce_data_re_6_0_pos 1 +-#define reg_ce_data_re_6_0_len 7 +-#define reg_ce_data_re_6_0_lsb 0 +-#define xd_p_reg_ce_data_re_8_7 0xABC6 +-#define reg_ce_data_re_8_7_pos 0 +-#define reg_ce_data_re_8_7_len 2 +-#define reg_ce_data_re_8_7_lsb 7 +-#define xd_p_reg_ce_tone_5_0 0xABC6 +-#define reg_ce_tone_5_0_pos 2 +-#define reg_ce_tone_5_0_len 6 +-#define reg_ce_tone_5_0_lsb 0 +-#define xd_p_reg_ce_tone_12_6 0xABC7 +-#define reg_ce_tone_12_6_pos 0 +-#define reg_ce_tone_12_6_len 7 +-#define reg_ce_tone_12_6_lsb 6 +-#define xd_p_reg_ce_centroid_drift_th 0xABC8 +-#define reg_ce_centroid_drift_th_pos 0 +-#define reg_ce_centroid_drift_th_len 8 +-#define reg_ce_centroid_drift_th_lsb 0 +-#define xd_p_reg_ce_centroid_count_max 0xABC9 +-#define reg_ce_centroid_count_max_pos 0 +-#define reg_ce_centroid_count_max_len 4 +-#define reg_ce_centroid_count_max_lsb 0 +-#define xd_p_reg_ce_centroid_bias_inc_7_0 0xABCA +-#define reg_ce_centroid_bias_inc_7_0_pos 0 +-#define reg_ce_centroid_bias_inc_7_0_len 8 +-#define reg_ce_centroid_bias_inc_7_0_lsb 0 +-#define xd_p_reg_ce_centroid_bias_inc_8 0xABCB +-#define reg_ce_centroid_bias_inc_8_pos 0 +-#define reg_ce_centroid_bias_inc_8_len 1 +-#define reg_ce_centroid_bias_inc_8_lsb 0 +-#define xd_p_reg_ce_var_th0_7_0 0xABCC +-#define reg_ce_var_th0_7_0_pos 0 +-#define reg_ce_var_th0_7_0_len 8 +-#define reg_ce_var_th0_7_0_lsb 0 +-#define xd_p_reg_ce_var_th0_15_8 0xABCD +-#define reg_ce_var_th0_15_8_pos 0 +-#define reg_ce_var_th0_15_8_len 8 +-#define reg_ce_var_th0_15_8_lsb 8 +-#define xd_p_reg_ce_var_th1_7_0 0xABCE +-#define reg_ce_var_th1_7_0_pos 0 +-#define reg_ce_var_th1_7_0_len 8 +-#define reg_ce_var_th1_7_0_lsb 0 +-#define xd_p_reg_ce_var_th1_15_8 0xABCF +-#define reg_ce_var_th1_15_8_pos 0 +-#define reg_ce_var_th1_15_8_len 8 +-#define reg_ce_var_th1_15_8_lsb 8 +-#define xd_p_reg_ce_var_th2_7_0 0xABD0 +-#define reg_ce_var_th2_7_0_pos 0 +-#define reg_ce_var_th2_7_0_len 8 +-#define reg_ce_var_th2_7_0_lsb 0 +-#define xd_p_reg_ce_var_th2_15_8 0xABD1 +-#define reg_ce_var_th2_15_8_pos 0 +-#define reg_ce_var_th2_15_8_len 8 +-#define reg_ce_var_th2_15_8_lsb 8 +-#define xd_p_reg_ce_var_th3_7_0 0xABD2 +-#define reg_ce_var_th3_7_0_pos 0 +-#define reg_ce_var_th3_7_0_len 8 +-#define reg_ce_var_th3_7_0_lsb 0 +-#define xd_p_reg_ce_var_th3_15_8 0xABD3 +-#define reg_ce_var_th3_15_8_pos 0 +-#define reg_ce_var_th3_15_8_len 8 +-#define reg_ce_var_th3_15_8_lsb 8 +-#define xd_p_reg_ce_var_th4_7_0 0xABD4 +-#define reg_ce_var_th4_7_0_pos 0 +-#define reg_ce_var_th4_7_0_len 8 +-#define reg_ce_var_th4_7_0_lsb 0 +-#define xd_p_reg_ce_var_th4_15_8 0xABD5 +-#define reg_ce_var_th4_15_8_pos 0 +-#define reg_ce_var_th4_15_8_len 8 +-#define reg_ce_var_th4_15_8_lsb 8 +-#define xd_p_reg_ce_var_th5_7_0 0xABD6 +-#define reg_ce_var_th5_7_0_pos 0 +-#define reg_ce_var_th5_7_0_len 8 +-#define reg_ce_var_th5_7_0_lsb 0 +-#define xd_p_reg_ce_var_th5_15_8 0xABD7 +-#define reg_ce_var_th5_15_8_pos 0 +-#define reg_ce_var_th5_15_8_len 8 +-#define reg_ce_var_th5_15_8_lsb 8 +-#define xd_p_reg_ce_var_th6_7_0 0xABD8 +-#define reg_ce_var_th6_7_0_pos 0 +-#define reg_ce_var_th6_7_0_len 8 +-#define reg_ce_var_th6_7_0_lsb 0 +-#define xd_p_reg_ce_var_th6_15_8 0xABD9 +-#define reg_ce_var_th6_15_8_pos 0 +-#define reg_ce_var_th6_15_8_len 8 +-#define reg_ce_var_th6_15_8_lsb 8 +-#define xd_p_reg_ce_fctrl_reset 0xABDA +-#define reg_ce_fctrl_reset_pos 0 +-#define reg_ce_fctrl_reset_len 1 +-#define reg_ce_fctrl_reset_lsb 0 +-#define xd_p_reg_ce_cent_auto_clr_en 0xABDA +-#define reg_ce_cent_auto_clr_en_pos 1 +-#define reg_ce_cent_auto_clr_en_len 1 +-#define reg_ce_cent_auto_clr_en_lsb 0 +-#define xd_p_reg_ce_fctrl_auto_reset_en 0xABDA +-#define reg_ce_fctrl_auto_reset_en_pos 2 +-#define reg_ce_fctrl_auto_reset_en_len 1 +-#define reg_ce_fctrl_auto_reset_en_lsb 0 +-#define xd_p_reg_ce_var_forced_en 0xABDA +-#define reg_ce_var_forced_en_pos 3 +-#define reg_ce_var_forced_en_len 1 +-#define reg_ce_var_forced_en_lsb 0 +-#define xd_p_reg_ce_cent_forced_en 0xABDA +-#define reg_ce_cent_forced_en_pos 4 +-#define reg_ce_cent_forced_en_len 1 +-#define reg_ce_cent_forced_en_lsb 0 +-#define xd_p_reg_ce_var_max 0xABDA +-#define reg_ce_var_max_pos 5 +-#define reg_ce_var_max_len 3 +-#define reg_ce_var_max_lsb 0 +-#define xd_p_reg_ce_cent_forced_value_7_0 0xABDB +-#define reg_ce_cent_forced_value_7_0_pos 0 +-#define reg_ce_cent_forced_value_7_0_len 8 +-#define reg_ce_cent_forced_value_7_0_lsb 0 +-#define xd_p_reg_ce_cent_forced_value_11_8 0xABDC +-#define reg_ce_cent_forced_value_11_8_pos 0 +-#define reg_ce_cent_forced_value_11_8_len 4 +-#define reg_ce_cent_forced_value_11_8_lsb 8 +-#define xd_p_reg_ce_fctrl_rd 0xABDD +-#define reg_ce_fctrl_rd_pos 0 +-#define reg_ce_fctrl_rd_len 1 +-#define reg_ce_fctrl_rd_lsb 0 +-#define xd_p_reg_ce_centroid_max_6_0 0xABDD +-#define reg_ce_centroid_max_6_0_pos 1 +-#define reg_ce_centroid_max_6_0_len 7 +-#define reg_ce_centroid_max_6_0_lsb 0 +-#define xd_p_reg_ce_centroid_max_11_7 0xABDE +-#define reg_ce_centroid_max_11_7_pos 0 +-#define reg_ce_centroid_max_11_7_len 5 +-#define reg_ce_centroid_max_11_7_lsb 7 +-#define xd_p_reg_ce_var 0xABDF +-#define reg_ce_var_pos 0 +-#define reg_ce_var_len 3 +-#define reg_ce_var_lsb 0 +-#define xd_p_reg_ce_fctrl_rdy 0xABDF +-#define reg_ce_fctrl_rdy_pos 3 +-#define reg_ce_fctrl_rdy_len 1 +-#define reg_ce_fctrl_rdy_lsb 0 +-#define xd_p_reg_ce_centroid_out_3_0 0xABDF +-#define reg_ce_centroid_out_3_0_pos 4 +-#define reg_ce_centroid_out_3_0_len 4 +-#define reg_ce_centroid_out_3_0_lsb 0 +-#define xd_p_reg_ce_centroid_out_11_4 0xABE0 +-#define reg_ce_centroid_out_11_4_pos 0 +-#define reg_ce_centroid_out_11_4_len 8 +-#define reg_ce_centroid_out_11_4_lsb 4 +-#define xd_p_reg_ce_bias_7_0 0xABE1 +-#define reg_ce_bias_7_0_pos 0 +-#define reg_ce_bias_7_0_len 8 +-#define reg_ce_bias_7_0_lsb 0 +-#define xd_p_reg_ce_bias_11_8 0xABE2 +-#define reg_ce_bias_11_8_pos 0 +-#define reg_ce_bias_11_8_len 4 +-#define reg_ce_bias_11_8_lsb 8 +-#define xd_p_reg_ce_m1_3_0 0xABE2 +-#define reg_ce_m1_3_0_pos 4 +-#define reg_ce_m1_3_0_len 4 +-#define reg_ce_m1_3_0_lsb 0 +-#define xd_p_reg_ce_m1_11_4 0xABE3 +-#define reg_ce_m1_11_4_pos 0 +-#define reg_ce_m1_11_4_len 8 +-#define reg_ce_m1_11_4_lsb 4 +-#define xd_p_reg_ce_rh0_7_0 0xABE4 +-#define reg_ce_rh0_7_0_pos 0 +-#define reg_ce_rh0_7_0_len 8 +-#define reg_ce_rh0_7_0_lsb 0 +-#define xd_p_reg_ce_rh0_15_8 0xABE5 +-#define reg_ce_rh0_15_8_pos 0 +-#define reg_ce_rh0_15_8_len 8 +-#define reg_ce_rh0_15_8_lsb 8 +-#define xd_p_reg_ce_rh0_23_16 0xABE6 +-#define reg_ce_rh0_23_16_pos 0 +-#define reg_ce_rh0_23_16_len 8 +-#define reg_ce_rh0_23_16_lsb 16 +-#define xd_p_reg_ce_rh0_31_24 0xABE7 +-#define reg_ce_rh0_31_24_pos 0 +-#define reg_ce_rh0_31_24_len 8 +-#define reg_ce_rh0_31_24_lsb 24 +-#define xd_p_reg_ce_rh3_real_7_0 0xABE8 +-#define reg_ce_rh3_real_7_0_pos 0 +-#define reg_ce_rh3_real_7_0_len 8 +-#define reg_ce_rh3_real_7_0_lsb 0 +-#define xd_p_reg_ce_rh3_real_15_8 0xABE9 +-#define reg_ce_rh3_real_15_8_pos 0 +-#define reg_ce_rh3_real_15_8_len 8 +-#define reg_ce_rh3_real_15_8_lsb 8 +-#define xd_p_reg_ce_rh3_real_23_16 0xABEA +-#define reg_ce_rh3_real_23_16_pos 0 +-#define reg_ce_rh3_real_23_16_len 8 +-#define reg_ce_rh3_real_23_16_lsb 16 +-#define xd_p_reg_ce_rh3_real_31_24 0xABEB +-#define reg_ce_rh3_real_31_24_pos 0 +-#define reg_ce_rh3_real_31_24_len 8 +-#define reg_ce_rh3_real_31_24_lsb 24 +-#define xd_p_reg_ce_rh3_imag_7_0 0xABEC +-#define reg_ce_rh3_imag_7_0_pos 0 +-#define reg_ce_rh3_imag_7_0_len 8 +-#define reg_ce_rh3_imag_7_0_lsb 0 +-#define xd_p_reg_ce_rh3_imag_15_8 0xABED +-#define reg_ce_rh3_imag_15_8_pos 0 +-#define reg_ce_rh3_imag_15_8_len 8 +-#define reg_ce_rh3_imag_15_8_lsb 8 +-#define xd_p_reg_ce_rh3_imag_23_16 0xABEE +-#define reg_ce_rh3_imag_23_16_pos 0 +-#define reg_ce_rh3_imag_23_16_len 8 +-#define reg_ce_rh3_imag_23_16_lsb 16 +-#define xd_p_reg_ce_rh3_imag_31_24 0xABEF +-#define reg_ce_rh3_imag_31_24_pos 0 +-#define reg_ce_rh3_imag_31_24_len 8 +-#define reg_ce_rh3_imag_31_24_lsb 24 +-#define xd_p_reg_feq_fix_eh2_7_0 0xABF0 +-#define reg_feq_fix_eh2_7_0_pos 0 +-#define reg_feq_fix_eh2_7_0_len 8 +-#define reg_feq_fix_eh2_7_0_lsb 0 +-#define xd_p_reg_feq_fix_eh2_15_8 0xABF1 +-#define reg_feq_fix_eh2_15_8_pos 0 +-#define reg_feq_fix_eh2_15_8_len 8 +-#define reg_feq_fix_eh2_15_8_lsb 8 +-#define xd_p_reg_feq_fix_eh2_23_16 0xABF2 +-#define reg_feq_fix_eh2_23_16_pos 0 +-#define reg_feq_fix_eh2_23_16_len 8 +-#define reg_feq_fix_eh2_23_16_lsb 16 +-#define xd_p_reg_feq_fix_eh2_31_24 0xABF3 +-#define reg_feq_fix_eh2_31_24_pos 0 +-#define reg_feq_fix_eh2_31_24_len 8 +-#define reg_feq_fix_eh2_31_24_lsb 24 +-#define xd_p_reg_ce_m2_central_7_0 0xABF4 +-#define reg_ce_m2_central_7_0_pos 0 +-#define reg_ce_m2_central_7_0_len 8 +-#define reg_ce_m2_central_7_0_lsb 0 +-#define xd_p_reg_ce_m2_central_15_8 0xABF5 +-#define reg_ce_m2_central_15_8_pos 0 +-#define reg_ce_m2_central_15_8_len 8 +-#define reg_ce_m2_central_15_8_lsb 8 +-#define xd_p_reg_ce_fftshift 0xABF6 +-#define reg_ce_fftshift_pos 0 +-#define reg_ce_fftshift_len 4 +-#define reg_ce_fftshift_lsb 0 +-#define xd_p_reg_ce_fftshift1 0xABF6 +-#define reg_ce_fftshift1_pos 4 +-#define reg_ce_fftshift1_len 4 +-#define reg_ce_fftshift1_lsb 0 +-#define xd_p_reg_ce_fftshift2 0xABF7 +-#define reg_ce_fftshift2_pos 0 +-#define reg_ce_fftshift2_len 4 +-#define reg_ce_fftshift2_lsb 0 +-#define xd_p_reg_ce_top_mobile 0xABF7 +-#define reg_ce_top_mobile_pos 4 +-#define reg_ce_top_mobile_len 1 +-#define reg_ce_top_mobile_lsb 0 +-#define xd_p_reg_strong_sginal_detected 0xA2BC +-#define reg_strong_sginal_detected_pos 2 +-#define reg_strong_sginal_detected_len 1 +-#define reg_strong_sginal_detected_lsb 0 +- +-#define XD_MP2IF_BASE 0xB000 +-#define XD_MP2IF_CSR (0x00 + XD_MP2IF_BASE) +-#define XD_MP2IF_DMX_CTRL (0x03 + XD_MP2IF_BASE) +-#define XD_MP2IF_PID_IDX (0x04 + XD_MP2IF_BASE) +-#define XD_MP2IF_PID_DATA_L (0x05 + XD_MP2IF_BASE) +-#define XD_MP2IF_PID_DATA_H (0x06 + XD_MP2IF_BASE) +-#define XD_MP2IF_MISC (0x07 + XD_MP2IF_BASE) +- +-extern struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d); +-extern int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, +- u8 * value); +-extern int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len); +-extern int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, +- u8 value); +-extern int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len); +-extern int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, +- u8 addr, u8 * values, int len); +-extern int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, +- u8 * values, int len); +-extern int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, +- u8 pos, u8 len, u8 * value); +-extern int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, +- u8 pos, u8 len, u8 value); +-extern int af9005_send_command(struct dvb_usb_device *d, u8 command, +- u8 * wbuf, int wlen, u8 * rbuf, int rlen); +-extern int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, +- u8 * values, int len); +-extern int af9005_tuner_attach(struct dvb_usb_adapter *adap); +-extern int af9005_led_control(struct dvb_usb_device *d, int onoff); +- +-extern u8 regmask[8]; +- +-/* remote control decoder */ +-extern int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, +- u32 * event, int *state); +-extern struct rc_map_table rc_map_af9005_table[]; +-extern int rc_map_af9005_table_size; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c +deleted file mode 100644 +index 282a43d..0000000 +--- a/drivers/media/dvb/dvb-usb/af9015.c ++++ /dev/null +@@ -1,1974 +0,0 @@ +-/* +- * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver +- * +- * Copyright (C) 2007 Antti Palosaari +- * +- * Thanks to Afatech who kindly provided information. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +- +-#include "af9015.h" +-#include "af9013.h" +-#include "mt2060.h" +-#include "qt1010.h" +-#include "tda18271.h" +-#include "mxl5005s.h" +-#include "mc44s803.h" +-#include "tda18218.h" +-#include "mxl5007t.h" +- +-static int dvb_usb_af9015_debug; +-module_param_named(debug, dvb_usb_af9015_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-static int dvb_usb_af9015_remote; +-module_param_named(remote, dvb_usb_af9015_remote, int, 0644); +-MODULE_PARM_DESC(remote, "select remote"); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static DEFINE_MUTEX(af9015_usb_mutex); +- +-static struct af9015_config af9015_config; +-static struct dvb_usb_device_properties af9015_properties[3]; +-static int af9015_properties_count = ARRAY_SIZE(af9015_properties); +- +-static struct af9013_config af9015_af9013_config[] = { +- { +- .i2c_addr = AF9015_I2C_DEMOD, +- .ts_mode = AF9013_TS_USB, +- .api_version = { 0, 1, 9, 0 }, +- .gpio[0] = AF9013_GPIO_HI, +- .gpio[3] = AF9013_GPIO_TUNER_ON, +- +- }, { +- .ts_mode = AF9013_TS_SERIAL, +- .api_version = { 0, 1, 9, 0 }, +- .gpio[0] = AF9013_GPIO_TUNER_ON, +- .gpio[1] = AF9013_GPIO_LO, +- } +-}; +- +-static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) +-{ +-#define BUF_LEN 63 +-#define REQ_HDR_LEN 8 /* send header size */ +-#define ACK_HDR_LEN 2 /* rece header size */ +- int act_len, ret; +- u8 buf[BUF_LEN]; +- u8 write = 1; +- u8 msg_len = REQ_HDR_LEN; +- static u8 seq; /* packet sequence number */ +- +- if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) +- return -EAGAIN; +- +- buf[0] = req->cmd; +- buf[1] = seq++; +- buf[2] = req->i2c_addr; +- buf[3] = req->addr >> 8; +- buf[4] = req->addr & 0xff; +- buf[5] = req->mbox; +- buf[6] = req->addr_len; +- buf[7] = req->data_len; +- +- switch (req->cmd) { +- case GET_CONFIG: +- case READ_MEMORY: +- case RECONNECT_USB: +- write = 0; +- break; +- case READ_I2C: +- write = 0; +- buf[2] |= 0x01; /* set I2C direction */ +- case WRITE_I2C: +- buf[0] = READ_WRITE_I2C; +- break; +- case WRITE_MEMORY: +- if (((req->addr & 0xff00) == 0xff00) || +- ((req->addr & 0xff00) == 0xae00)) +- buf[0] = WRITE_VIRTUAL_MEMORY; +- case WRITE_VIRTUAL_MEMORY: +- case COPY_FIRMWARE: +- case DOWNLOAD_FIRMWARE: +- case BOOT: +- break; +- default: +- err("unknown command:%d", req->cmd); +- ret = -1; +- goto error_unlock; +- } +- +- /* buffer overflow check */ +- if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || +- (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { +- err("too much data; cmd:%d len:%d", req->cmd, req->data_len); +- ret = -EINVAL; +- goto error_unlock; +- } +- +- /* write requested */ +- if (write) { +- memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len); +- msg_len += req->data_len; +- } +- +- deb_xfer(">>> "); +- debug_dump(buf, msg_len, deb_xfer); +- +- /* send req */ +- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, +- &act_len, AF9015_USB_TIMEOUT); +- if (ret) +- err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); +- else +- if (act_len != msg_len) +- ret = -1; /* all data is not send */ +- if (ret) +- goto error_unlock; +- +- /* no ack for those packets */ +- if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) +- goto exit_unlock; +- +- /* write receives seq + status = 2 bytes +- read receives seq + status + data = 2 + N bytes */ +- msg_len = ACK_HDR_LEN; +- if (!write) +- msg_len += req->data_len; +- +- ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, +- &act_len, AF9015_USB_TIMEOUT); +- if (ret) { +- err("recv bulk message failed:%d", ret); +- ret = -1; +- goto error_unlock; +- } +- +- deb_xfer("<<< "); +- debug_dump(buf, act_len, deb_xfer); +- +- /* check status */ +- if (buf[1]) { +- err("command failed:%d", buf[1]); +- ret = -1; +- goto error_unlock; +- } +- +- /* read request, copy returned data to return buf */ +- if (!write) +- memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); +- +-error_unlock: +-exit_unlock: +- mutex_unlock(&af9015_usb_mutex); +- +- return ret; +-} +- +-static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) +-{ +- return af9015_rw_udev(d->udev, req); +-} +- +-static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val, +- u8 len) +-{ +- struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, +- val}; +- return af9015_ctrl_msg(d, &req); +-} +- +-static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val) +-{ +- return af9015_write_regs(d, addr, &val, 1); +-} +- +-static int af9015_read_regs(struct dvb_usb_device *d, u16 addr, u8 *val, u8 len) +-{ +- struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len, +- val}; +- return af9015_ctrl_msg(d, &req); +-} +- +-static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val) +-{ +- return af9015_read_regs(d, addr, val, 1); +-} +- +-static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, +- u8 val) +-{ +- struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val}; +- +- if (addr == af9015_af9013_config[0].i2c_addr || +- addr == af9015_af9013_config[1].i2c_addr) +- req.addr_len = 3; +- +- return af9015_ctrl_msg(d, &req); +-} +- +-static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg, +- u8 *val) +-{ +- struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val}; +- +- if (addr == af9015_af9013_config[0].i2c_addr || +- addr == af9015_af9013_config[1].i2c_addr) +- req.addr_len = 3; +- +- return af9015_ctrl_msg(d, &req); +-} +- +-static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0, i = 0; +- u16 addr; +- u8 uninitialized_var(mbox), addr_len; +- struct req_t req; +- +-/* TODO: implement bus lock +- +-The bus lock is needed because there is two tuners both using same I2C-address. +-Due to that the only way to select correct tuner is use demodulator I2C-gate. +- +-................................................ +-. AF9015 includes integrated AF9013 demodulator. +-. ____________ ____________ . ____________ +-.| uC | | demod | . | tuner | +-.|------------| |------------| . |------------| +-.| AF9015 | | AF9013/5 | . | MXL5003 | +-.| |--+----I2C-------|-----/ -----|-.-----I2C-------| | +-.| | | | addr 0x38 | . | addr 0xc6 | +-.|____________| | |____________| . |____________| +-.................|.............................. +- | ____________ ____________ +- | | demod | | tuner | +- | |------------| |------------| +- | | AF9013 | | MXL5003 | +- +----I2C-------|-----/ -----|-------I2C-------| | +- | addr 0x3a | | addr 0xc6 | +- |____________| |____________| +-*/ +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- while (i < num) { +- if (msg[i].addr == af9015_af9013_config[0].i2c_addr || +- msg[i].addr == af9015_af9013_config[1].i2c_addr) { +- addr = msg[i].buf[0] << 8; +- addr += msg[i].buf[1]; +- mbox = msg[i].buf[2]; +- addr_len = 3; +- } else { +- addr = msg[i].buf[0]; +- addr_len = 1; +- /* mbox is don't care in that case */ +- } +- +- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { +- if (msg[i].len > 3 || msg[i+1].len > 61) { +- ret = -EOPNOTSUPP; +- goto error; +- } +- if (msg[i].addr == af9015_af9013_config[0].i2c_addr) +- req.cmd = READ_MEMORY; +- else +- req.cmd = READ_I2C; +- req.i2c_addr = msg[i].addr; +- req.addr = addr; +- req.mbox = mbox; +- req.addr_len = addr_len; +- req.data_len = msg[i+1].len; +- req.data = &msg[i+1].buf[0]; +- ret = af9015_ctrl_msg(d, &req); +- i += 2; +- } else if (msg[i].flags & I2C_M_RD) { +- if (msg[i].len > 61) { +- ret = -EOPNOTSUPP; +- goto error; +- } +- if (msg[i].addr == +- af9015_af9013_config[0].i2c_addr) { +- ret = -EINVAL; +- goto error; +- } +- req.cmd = READ_I2C; +- req.i2c_addr = msg[i].addr; +- req.addr = addr; +- req.mbox = mbox; +- req.addr_len = addr_len; +- req.data_len = msg[i].len; +- req.data = &msg[i].buf[0]; +- ret = af9015_ctrl_msg(d, &req); +- i += 1; +- } else { +- if (msg[i].len > 21) { +- ret = -EOPNOTSUPP; +- goto error; +- } +- if (msg[i].addr == af9015_af9013_config[0].i2c_addr) +- req.cmd = WRITE_MEMORY; +- else +- req.cmd = WRITE_I2C; +- req.i2c_addr = msg[i].addr; +- req.addr = addr; +- req.mbox = mbox; +- req.addr_len = addr_len; +- req.data_len = msg[i].len-addr_len; +- req.data = &msg[i].buf[addr_len]; +- ret = af9015_ctrl_msg(d, &req); +- i += 1; +- } +- if (ret) +- goto error; +- +- } +- ret = i; +- +-error: +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-static u32 af9015_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm af9015_i2c_algo = { +- .master_xfer = af9015_i2c_xfer, +- .functionality = af9015_i2c_func, +-}; +- +-static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op) +-{ +- int ret; +- u8 val, mask = 0x01; +- +- ret = af9015_read_reg(d, addr, &val); +- if (ret) +- return ret; +- +- mask <<= bit; +- if (op) { +- /* set bit */ +- val |= mask; +- } else { +- /* clear bit */ +- mask ^= 0xff; +- val &= mask; +- } +- +- return af9015_write_reg(d, addr, val); +-} +- +-static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +-{ +- return af9015_do_reg_bit(d, addr, bit, 1); +-} +- +-static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit) +-{ +- return af9015_do_reg_bit(d, addr, bit, 0); +-} +- +-static int af9015_init_endpoint(struct dvb_usb_device *d) +-{ +- int ret; +- u16 frame_size; +- u8 packet_size; +- deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); +- +- /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. +- We use smaller - about 1/4 from the original, 5 and 87. */ +-#define TS_PACKET_SIZE 188 +- +-#define TS_USB20_PACKET_COUNT 87 +-#define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) +- +-#define TS_USB11_PACKET_COUNT 5 +-#define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) +- +-#define TS_USB20_MAX_PACKET_SIZE 512 +-#define TS_USB11_MAX_PACKET_SIZE 64 +- +- if (d->udev->speed == USB_SPEED_FULL) { +- frame_size = TS_USB11_FRAME_SIZE/4; +- packet_size = TS_USB11_MAX_PACKET_SIZE/4; +- } else { +- frame_size = TS_USB20_FRAME_SIZE/4; +- packet_size = TS_USB20_MAX_PACKET_SIZE/4; +- } +- +- ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */ +- if (ret) +- goto error; +- ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */ +- if (ret) +- goto error; +- ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */ +- if (ret) +- goto error; +- ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */ +- if (ret) +- goto error; +- ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */ +- if (ret) +- goto error; +- if (af9015_config.dual_mode) { +- ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */ +- if (ret) +- goto error; +- } +- ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */ +- if (ret) +- goto error; +- if (af9015_config.dual_mode) { +- ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */ +- if (ret) +- goto error; +- } +- /* EP4 xfer length */ +- ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff); +- if (ret) +- goto error; +- ret = af9015_write_reg(d, 0xdd89, frame_size >> 8); +- if (ret) +- goto error; +- /* EP5 xfer length */ +- ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff); +- if (ret) +- goto error; +- ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8); +- if (ret) +- goto error; +- ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */ +- if (ret) +- goto error; +- ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */ +- if (ret) +- goto error; +- ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */ +- if (ret) +- goto error; +- if (af9015_config.dual_mode) { +- ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */ +- if (ret) +- goto error; +- } +- +- /* enable / disable mp2if2 */ +- if (af9015_config.dual_mode) +- ret = af9015_set_reg_bit(d, 0xd50b, 0); +- else +- ret = af9015_clear_reg_bit(d, 0xd50b, 0); +- +-error: +- if (ret) +- err("endpoint init failed:%d", ret); +- return ret; +-} +- +-static int af9015_copy_firmware(struct dvb_usb_device *d) +-{ +- int ret; +- u8 fw_params[4]; +- u8 val, i; +- struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params), +- fw_params }; +- deb_info("%s:\n", __func__); +- +- fw_params[0] = af9015_config.firmware_size >> 8; +- fw_params[1] = af9015_config.firmware_size & 0xff; +- fw_params[2] = af9015_config.firmware_checksum >> 8; +- fw_params[3] = af9015_config.firmware_checksum & 0xff; +- +- /* wait 2nd demodulator ready */ +- msleep(100); +- +- ret = af9015_read_reg_i2c(d, +- af9015_af9013_config[1].i2c_addr, 0x98be, &val); +- if (ret) +- goto error; +- else +- deb_info("%s: firmware status:%02x\n", __func__, val); +- +- if (val == 0x0c) /* fw is running, no need for download */ +- goto exit; +- +- /* set I2C master clock to fast (to speed up firmware copy) */ +- ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */ +- if (ret) +- goto error; +- +- msleep(50); +- +- /* copy firmware */ +- ret = af9015_ctrl_msg(d, &req); +- if (ret) +- err("firmware copy cmd failed:%d", ret); +- deb_info("%s: firmware copy done\n", __func__); +- +- /* set I2C master clock back to normal */ +- ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */ +- if (ret) +- goto error; +- +- /* request boot firmware */ +- ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].i2c_addr, +- 0xe205, 1); +- deb_info("%s: firmware boot cmd status:%d\n", __func__, ret); +- if (ret) +- goto error; +- +- for (i = 0; i < 15; i++) { +- msleep(100); +- +- /* check firmware status */ +- ret = af9015_read_reg_i2c(d, +- af9015_af9013_config[1].i2c_addr, 0x98be, &val); +- deb_info("%s: firmware status cmd status:%d fw status:%02x\n", +- __func__, ret, val); +- if (ret) +- goto error; +- +- if (val == 0x0c || val == 0x04) /* success or fail */ +- break; +- } +- +- if (val == 0x04) { +- err("firmware did not run"); +- ret = -1; +- } else if (val != 0x0c) { +- err("firmware boot timeout"); +- ret = -1; +- } +- +-error: +-exit: +- return ret; +-} +- +-/* hash (and dump) eeprom */ +-static int af9015_eeprom_hash(struct usb_device *udev) +-{ +- static const unsigned int eeprom_size = 256; +- unsigned int reg; +- int ret; +- u8 val, *eeprom; +- struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; +- +- eeprom = kmalloc(eeprom_size, GFP_KERNEL); +- if (eeprom == NULL) +- return -ENOMEM; +- +- for (reg = 0; reg < eeprom_size; reg++) { +- req.addr = reg; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto free; +- eeprom[reg] = val; +- } +- +- if (dvb_usb_af9015_debug & 0x01) +- print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom, +- eeprom_size); +- +- BUG_ON(eeprom_size % 4); +- +- af9015_config.eeprom_sum = 0; +- for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) { +- af9015_config.eeprom_sum *= GOLDEN_RATIO_PRIME_32; +- af9015_config.eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]); +- } +- +- deb_info("%s: eeprom sum=%.8x\n", __func__, af9015_config.eeprom_sum); +- +- ret = 0; +-free: +- kfree(eeprom); +- return ret; +-} +- +-static int af9015_init(struct dvb_usb_device *d) +-{ +- int ret; +- deb_info("%s:\n", __func__); +- +- /* init RC canary */ +- ret = af9015_write_reg(d, 0x98e9, 0xff); +- if (ret) +- goto error; +- +- ret = af9015_init_endpoint(d); +- if (ret) +- goto error; +- +-error: +- return ret; +-} +- +-static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret; +- deb_info("%s: onoff:%d\n", __func__, onoff); +- +- if (onoff) +- ret = af9015_set_reg_bit(adap->dev, 0xd503, 0); +- else +- ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0); +- +- return ret; +-} +- +-static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, +- int onoff) +-{ +- int ret; +- u8 idx; +- +- deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n", +- __func__, index, pid, onoff); +- +- ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff)); +- if (ret) +- goto error; +- +- ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8)); +- if (ret) +- goto error; +- +- idx = ((index & 0x1f) | (1 << 5)); +- ret = af9015_write_reg(adap->dev, 0xd504, idx); +- +-error: +- return ret; +-} +- +-static int af9015_download_firmware(struct usb_device *udev, +- const struct firmware *fw) +-{ +- int i, len, remaining, ret; +- struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL}; +- u16 checksum = 0; +- +- deb_info("%s:\n", __func__); +- +- /* calc checksum */ +- for (i = 0; i < fw->size; i++) +- checksum += fw->data[i]; +- +- af9015_config.firmware_size = fw->size; +- af9015_config.firmware_checksum = checksum; +- +- #define FW_ADDR 0x5100 /* firmware start address */ +- #define LEN_MAX 55 /* max packet size */ +- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { +- len = remaining; +- if (len > LEN_MAX) +- len = LEN_MAX; +- +- req.data_len = len; +- req.data = (u8 *) &fw->data[fw->size - remaining]; +- req.addr = FW_ADDR + fw->size - remaining; +- +- ret = af9015_rw_udev(udev, &req); +- if (ret) { +- err("firmware download failed:%d", ret); +- goto error; +- } +- } +- +- /* firmware loaded, request boot */ +- req.cmd = BOOT; +- ret = af9015_rw_udev(udev, &req); +- if (ret) { +- err("firmware boot failed:%d", ret); +- goto error; +- } +- +-error: +- return ret; +-} +- +-struct af9015_rc_setup { +- unsigned int id; +- char *rc_codes; +-}; +- +-static char *af9015_rc_setup_match(unsigned int id, +- const struct af9015_rc_setup *table) +-{ +- for (; table->rc_codes; table++) +- if (table->id == id) +- return table->rc_codes; +- return NULL; +-} +- +-static const struct af9015_rc_setup af9015_rc_setup_modparam[] = { +- { AF9015_REMOTE_A_LINK_DTU_M, RC_MAP_ALINK_DTU_M }, +- { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, RC_MAP_MSI_DIGIVOX_II }, +- { AF9015_REMOTE_MYGICTV_U718, RC_MAP_TOTAL_MEDIA_IN_HAND }, +- { AF9015_REMOTE_DIGITTRADE_DVB_T, RC_MAP_DIGITTRADE }, +- { AF9015_REMOTE_AVERMEDIA_KS, RC_MAP_AVERMEDIA_RM_KS }, +- { } +-}; +- +-static const struct af9015_rc_setup af9015_rc_setup_hashes[] = { +- { 0xb8feb708, RC_MAP_MSI_DIGIVOX_II }, +- { 0xa3703d00, RC_MAP_ALINK_DTU_M }, +- { 0x9b7dc64e, RC_MAP_TOTAL_MEDIA_IN_HAND }, /* MYGICTV U718 */ +- { 0x5d49e3db, RC_MAP_DIGITTRADE }, /* LC-Power LC-USB-DVBT */ +- { } +-}; +- +-static const struct af9015_rc_setup af9015_rc_setup_usbids[] = { +- { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_RC, +- RC_MAP_TERRATEC_SLIM_2 }, +- { (USB_VID_TERRATEC << 16) | USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC, +- RC_MAP_TERRATEC_SLIM }, +- { (USB_VID_VISIONPLUS << 16) | USB_PID_AZUREWAVE_AD_TU700, +- RC_MAP_AZUREWAVE_AD_TU700 }, +- { (USB_VID_VISIONPLUS << 16) | USB_PID_TINYTWIN, +- RC_MAP_AZUREWAVE_AD_TU700 }, +- { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGI_VOX_MINI_III, +- RC_MAP_MSI_DIGIVOX_III }, +- { (USB_VID_MSI_2 << 16) | USB_PID_MSI_DIGIVOX_DUO, +- RC_MAP_MSI_DIGIVOX_III }, +- { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV_DONGLE_GOLD, +- RC_MAP_LEADTEK_Y04G0051 }, +- { (USB_VID_LEADTEK << 16) | USB_PID_WINFAST_DTV2000DS, +- RC_MAP_LEADTEK_Y04G0051 }, +- { (USB_VID_AVERMEDIA << 16) | USB_PID_AVERMEDIA_VOLAR_X, +- RC_MAP_AVERMEDIA_M135A }, +- { (USB_VID_AFATECH << 16) | USB_PID_TREKSTOR_DVBT, +- RC_MAP_TREKSTOR }, +- { (USB_VID_KWORLD_2 << 16) | USB_PID_TINYTWIN_2, +- RC_MAP_DIGITALNOW_TINYTWIN }, +- { (USB_VID_GTEK << 16) | USB_PID_TINYTWIN_3, +- RC_MAP_DIGITALNOW_TINYTWIN }, +- { (USB_VID_KWORLD_2 << 16) | USB_PID_SVEON_STV22, +- RC_MAP_MSI_DIGIVOX_III }, +- { } +-}; +- +-static void af9015_set_remote_config(struct usb_device *udev, +- struct dvb_usb_device_properties *props) +-{ +- u16 vid = le16_to_cpu(udev->descriptor.idVendor); +- u16 pid = le16_to_cpu(udev->descriptor.idProduct); +- +- /* try to load remote based module param */ +- props->rc.core.rc_codes = af9015_rc_setup_match( +- dvb_usb_af9015_remote, af9015_rc_setup_modparam); +- +- /* try to load remote based eeprom hash */ +- if (!props->rc.core.rc_codes) +- props->rc.core.rc_codes = af9015_rc_setup_match( +- af9015_config.eeprom_sum, af9015_rc_setup_hashes); +- +- /* try to load remote based USB ID */ +- if (!props->rc.core.rc_codes) +- props->rc.core.rc_codes = af9015_rc_setup_match( +- (vid << 16) + pid, af9015_rc_setup_usbids); +- +- /* try to load remote based USB iManufacturer string */ +- if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) { +- /* Check USB manufacturer and product strings and try +- to determine correct remote in case of chip vendor +- reference IDs are used. +- DO NOT ADD ANYTHING NEW HERE. Use hashes instead. */ +- char manufacturer[10]; +- memset(manufacturer, 0, sizeof(manufacturer)); +- usb_string(udev, udev->descriptor.iManufacturer, +- manufacturer, sizeof(manufacturer)); +- if (!strcmp("MSI", manufacturer)) { +- /* iManufacturer 1 MSI +- iProduct 2 MSI K-VOX */ +- props->rc.core.rc_codes = af9015_rc_setup_match( +- AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, +- af9015_rc_setup_modparam); +- } +- } +- +- /* finally load "empty" just for leaving IR receiver enabled */ +- if (!props->rc.core.rc_codes) +- props->rc.core.rc_codes = RC_MAP_EMPTY; +- +- return; +-} +- +-static int af9015_read_config(struct usb_device *udev) +-{ +- int ret; +- u8 val, i, offset = 0; +- struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; +- +- /* IR remote controller */ +- req.addr = AF9015_EEPROM_IR_MODE; +- /* first message will timeout often due to possible hw bug */ +- for (i = 0; i < 4; i++) { +- ret = af9015_rw_udev(udev, &req); +- if (!ret) +- break; +- } +- if (ret) +- goto error; +- +- ret = af9015_eeprom_hash(udev); +- if (ret) +- goto error; +- +- deb_info("%s: IR mode=%d\n", __func__, val); +- for (i = 0; i < af9015_properties_count; i++) { +- if (val == AF9015_IR_MODE_DISABLED) +- af9015_properties[i].rc.core.rc_codes = NULL; +- else +- af9015_set_remote_config(udev, &af9015_properties[i]); +- } +- +- /* TS mode - one or two receivers */ +- req.addr = AF9015_EEPROM_TS_MODE; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- af9015_config.dual_mode = val; +- deb_info("%s: TS mode=%d\n", __func__, af9015_config.dual_mode); +- +- /* Set adapter0 buffer size according to USB port speed, adapter1 buffer +- size can be static because it is enabled only USB2.0 */ +- for (i = 0; i < af9015_properties_count; i++) { +- /* USB1.1 set smaller buffersize and disable 2nd adapter */ +- if (udev->speed == USB_SPEED_FULL) { +- af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize +- = TS_USB11_FRAME_SIZE; +- /* disable 2nd adapter because we don't have +- PID-filters */ +- af9015_config.dual_mode = 0; +- } else { +- af9015_properties[i].adapter[0].fe[0].stream.u.bulk.buffersize +- = TS_USB20_FRAME_SIZE; +- } +- } +- +- if (af9015_config.dual_mode) { +- /* read 2nd demodulator I2C address */ +- req.addr = AF9015_EEPROM_DEMOD2_I2C; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- af9015_af9013_config[1].i2c_addr = val; +- +- /* enable 2nd adapter */ +- for (i = 0; i < af9015_properties_count; i++) +- af9015_properties[i].num_adapters = 2; +- +- } else { +- /* disable 2nd adapter */ +- for (i = 0; i < af9015_properties_count; i++) +- af9015_properties[i].num_adapters = 1; +- } +- +- for (i = 0; i < af9015_properties[0].num_adapters; i++) { +- if (i == 1) +- offset = AF9015_EEPROM_OFFSET; +- /* xtal */ +- req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- switch (val) { +- case 0: +- af9015_af9013_config[i].clock = 28800000; +- break; +- case 1: +- af9015_af9013_config[i].clock = 20480000; +- break; +- case 2: +- af9015_af9013_config[i].clock = 28000000; +- break; +- case 3: +- af9015_af9013_config[i].clock = 25000000; +- break; +- }; +- deb_info("%s: [%d] xtal=%d set clock=%d\n", __func__, i, +- val, af9015_af9013_config[i].clock); +- +- /* IF frequency */ +- req.addr = AF9015_EEPROM_IF1H + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- af9015_af9013_config[i].if_frequency = val << 8; +- +- req.addr = AF9015_EEPROM_IF1L + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- af9015_af9013_config[i].if_frequency += val; +- af9015_af9013_config[i].if_frequency *= 1000; +- deb_info("%s: [%d] IF frequency=%d\n", __func__, i, +- af9015_af9013_config[0].if_frequency); +- +- /* MT2060 IF1 */ +- req.addr = AF9015_EEPROM_MT2060_IF1H + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- af9015_config.mt2060_if1[i] = val << 8; +- req.addr = AF9015_EEPROM_MT2060_IF1L + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- af9015_config.mt2060_if1[i] += val; +- deb_info("%s: [%d] MT2060 IF1=%d\n", __func__, i, +- af9015_config.mt2060_if1[i]); +- +- /* tuner */ +- req.addr = AF9015_EEPROM_TUNER_ID1 + offset; +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- goto error; +- switch (val) { +- case AF9013_TUNER_ENV77H11D5: +- case AF9013_TUNER_MT2060: +- case AF9013_TUNER_QT1010: +- case AF9013_TUNER_UNKNOWN: +- case AF9013_TUNER_MT2060_2: +- case AF9013_TUNER_TDA18271: +- case AF9013_TUNER_QT1010A: +- case AF9013_TUNER_TDA18218: +- af9015_af9013_config[i].spec_inv = 1; +- break; +- case AF9013_TUNER_MXL5003D: +- case AF9013_TUNER_MXL5005D: +- case AF9013_TUNER_MXL5005R: +- case AF9013_TUNER_MXL5007T: +- af9015_af9013_config[i].spec_inv = 0; +- break; +- case AF9013_TUNER_MC44S803: +- af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO; +- af9015_af9013_config[i].spec_inv = 1; +- break; +- default: +- warn("tuner id=%d not supported, please report!", val); +- return -ENODEV; +- }; +- +- af9015_af9013_config[i].tuner = val; +- deb_info("%s: [%d] tuner id=%d\n", __func__, i, val); +- } +- +-error: +- if (ret) +- err("eeprom read failed=%d", ret); +- +- /* AverMedia AVerTV Volar Black HD (A850) device have bad EEPROM +- content :-( Override some wrong values here. Ditto for the +- AVerTV Red HD+ (A850T) device. */ +- if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_AVERMEDIA && +- ((le16_to_cpu(udev->descriptor.idProduct) == +- USB_PID_AVERMEDIA_A850) || +- (le16_to_cpu(udev->descriptor.idProduct) == +- USB_PID_AVERMEDIA_A850T))) { +- deb_info("%s: AverMedia A850: overriding config\n", __func__); +- /* disable dual mode */ +- af9015_config.dual_mode = 0; +- /* disable 2nd adapter */ +- for (i = 0; i < af9015_properties_count; i++) +- af9015_properties[i].num_adapters = 1; +- +- /* set correct IF */ +- af9015_af9013_config[0].if_frequency = 4570000; +- } +- +- return ret; +-} +- +-static int af9015_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- int ret; +- u8 reply; +- struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply}; +- +- ret = af9015_rw_udev(udev, &req); +- if (ret) +- return ret; +- +- deb_info("%s: reply:%02x\n", __func__, reply); +- if (reply == 0x02) +- *cold = 0; +- else +- *cold = 1; +- +- return ret; +-} +- +-static int af9015_rc_query(struct dvb_usb_device *d) +-{ +- struct af9015_state *priv = d->priv; +- int ret; +- u8 buf[17]; +- +- /* read registers needed to detect remote controller code */ +- ret = af9015_read_regs(d, 0x98d9, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- /* If any of these are non-zero, assume invalid data */ +- if (buf[1] || buf[2] || buf[3]) +- return ret; +- +- /* Check for repeat of previous code */ +- if ((priv->rc_repeat != buf[6] || buf[0]) && +- !memcmp(&buf[12], priv->rc_last, 4)) { +- deb_rc("%s: key repeated\n", __func__); +- rc_keydown(d->rc_dev, priv->rc_keycode, 0); +- priv->rc_repeat = buf[6]; +- return ret; +- } +- +- /* Only process key if canary killed */ +- if (buf[16] != 0xff && buf[0] != 0x01) { +- deb_rc("%s: key pressed %02x %02x %02x %02x\n", __func__, +- buf[12], buf[13], buf[14], buf[15]); +- +- /* Reset the canary */ +- ret = af9015_write_reg(d, 0x98e9, 0xff); +- if (ret) +- goto error; +- +- /* Remember this key */ +- memcpy(priv->rc_last, &buf[12], 4); +- if (buf[14] == (u8) ~buf[15]) { +- if (buf[12] == (u8) ~buf[13]) { +- /* NEC */ +- priv->rc_keycode = buf[12] << 8 | buf[14]; +- } else { +- /* NEC extended*/ +- priv->rc_keycode = buf[12] << 16 | +- buf[13] << 8 | buf[14]; +- } +- } else { +- /* 32 bit NEC */ +- priv->rc_keycode = buf[12] << 24 | buf[13] << 16 | +- buf[14] << 8 | buf[15]; +- } +- rc_keydown(d->rc_dev, priv->rc_keycode, 0); +- } else { +- deb_rc("%s: no key press\n", __func__); +- /* Invalidate last keypress */ +- /* Not really needed, but helps with debug */ +- priv->rc_last[2] = priv->rc_last[3]; +- } +- +- priv->rc_repeat = buf[6]; +- +-error: +- if (ret) +- err("%s: failed:%d", __func__, ret); +- +- return ret; +-} +- +-/* override demod callbacks for resource locking */ +-static int af9015_af9013_set_frontend(struct dvb_frontend *fe) +-{ +- int ret; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct af9015_state *priv = adap->dev->priv; +- +- if (mutex_lock_interruptible(&adap->dev->usb_mutex)) +- return -EAGAIN; +- +- ret = priv->set_frontend[adap->id](fe); +- +- mutex_unlock(&adap->dev->usb_mutex); +- +- return ret; +-} +- +-/* override demod callbacks for resource locking */ +-static int af9015_af9013_read_status(struct dvb_frontend *fe, +- fe_status_t *status) +-{ +- int ret; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct af9015_state *priv = adap->dev->priv; +- +- if (mutex_lock_interruptible(&adap->dev->usb_mutex)) +- return -EAGAIN; +- +- ret = priv->read_status[adap->id](fe, status); +- +- mutex_unlock(&adap->dev->usb_mutex); +- +- return ret; +-} +- +-/* override demod callbacks for resource locking */ +-static int af9015_af9013_init(struct dvb_frontend *fe) +-{ +- int ret; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct af9015_state *priv = adap->dev->priv; +- +- if (mutex_lock_interruptible(&adap->dev->usb_mutex)) +- return -EAGAIN; +- +- ret = priv->init[adap->id](fe); +- +- mutex_unlock(&adap->dev->usb_mutex); +- +- return ret; +-} +- +-/* override demod callbacks for resource locking */ +-static int af9015_af9013_sleep(struct dvb_frontend *fe) +-{ +- int ret; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct af9015_state *priv = adap->dev->priv; +- +- if (mutex_lock_interruptible(&adap->dev->usb_mutex)) +- return -EAGAIN; +- +- ret = priv->sleep[adap->id](fe); +- +- mutex_unlock(&adap->dev->usb_mutex); +- +- return ret; +-} +- +-static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- int ret; +- struct af9015_state *state = adap->dev->priv; +- +- if (adap->id == 1) { +- /* copy firmware to 2nd demodulator */ +- if (af9015_config.dual_mode) { +- ret = af9015_copy_firmware(adap->dev); +- if (ret) { +- err("firmware copy to 2nd frontend " \ +- "failed, will disable it"); +- af9015_config.dual_mode = 0; +- return -ENODEV; +- } +- } else { +- return -ENODEV; +- } +- } +- +- /* attach demodulator */ +- adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], +- &adap->dev->i2c_adap); +- +- /* +- * AF9015 firmware does not like if it gets interrupted by I2C adapter +- * request on some critical phases. During normal operation I2C adapter +- * is used only 2nd demodulator and tuner on dual tuner devices. +- * Override demodulator callbacks and use mutex for limit access to +- * those "critical" paths to keep AF9015 happy. +- * Note: we abuse unused usb_mutex here. +- */ +- if (adap->fe_adap[0].fe) { +- state->set_frontend[adap->id] = +- adap->fe_adap[0].fe->ops.set_frontend; +- adap->fe_adap[0].fe->ops.set_frontend = +- af9015_af9013_set_frontend; +- +- state->read_status[adap->id] = +- adap->fe_adap[0].fe->ops.read_status; +- adap->fe_adap[0].fe->ops.read_status = +- af9015_af9013_read_status; +- +- state->init[adap->id] = adap->fe_adap[0].fe->ops.init; +- adap->fe_adap[0].fe->ops.init = af9015_af9013_init; +- +- state->sleep[adap->id] = adap->fe_adap[0].fe->ops.sleep; +- adap->fe_adap[0].fe->ops.sleep = af9015_af9013_sleep; +- } +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static struct mt2060_config af9015_mt2060_config = { +- .i2c_address = 0xc0, +- .clock_out = 0, +-}; +- +-static struct qt1010_config af9015_qt1010_config = { +- .i2c_address = 0xc4, +-}; +- +-static struct tda18271_config af9015_tda18271_config = { +- .gate = TDA18271_GATE_DIGITAL, +- .small_i2c = TDA18271_16_BYTE_CHUNK_INIT, +-}; +- +-static struct mxl5005s_config af9015_mxl5003_config = { +- .i2c_address = 0xc6, +- .if_freq = IF_FREQ_4570000HZ, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_DEFAULT, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-static struct mxl5005s_config af9015_mxl5005_config = { +- .i2c_address = 0xc6, +- .if_freq = IF_FREQ_4570000HZ, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_OFF, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-static struct mc44s803_config af9015_mc44s803_config = { +- .i2c_address = 0xc0, +- .dig_out = 1, +-}; +- +-static struct tda18218_config af9015_tda18218_config = { +- .i2c_address = 0xc0, +- .i2c_wr_max = 21, /* max wr bytes AF9015 I2C adap can handle at once */ +-}; +- +-static struct mxl5007t_config af9015_mxl5007t_config = { +- .xtal_freq_hz = MxL_XTAL_24_MHZ, +- .if_freq_hz = MxL_IF_4_57_MHZ, +-}; +- +-static int af9015_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- int ret; +- deb_info("%s:\n", __func__); +- +- switch (af9015_af9013_config[adap->id].tuner) { +- case AF9013_TUNER_MT2060: +- case AF9013_TUNER_MT2060_2: +- ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &af9015_mt2060_config, +- af9015_config.mt2060_if1[adap->id]) +- == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_QT1010: +- case AF9013_TUNER_QT1010A: +- ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &af9015_qt1010_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_TDA18271: +- ret = dvb_attach(tda18271_attach, adap->fe_adap[0].fe, 0xc0, +- &adap->dev->i2c_adap, +- &af9015_tda18271_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_TDA18218: +- ret = dvb_attach(tda18218_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, +- &af9015_tda18218_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_MXL5003D: +- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, +- &af9015_mxl5003_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_MXL5005D: +- case AF9013_TUNER_MXL5005R: +- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, +- &af9015_mxl5005_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_ENV77H11D5: +- ret = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0xc0, +- &adap->dev->i2c_adap, +- DVB_PLL_TDA665X) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_MC44S803: +- ret = dvb_attach(mc44s803_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, +- &af9015_mc44s803_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_MXL5007T: +- ret = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, +- 0xc0, &af9015_mxl5007t_config) == NULL ? -ENODEV : 0; +- break; +- case AF9013_TUNER_UNKNOWN: +- default: +- ret = -ENODEV; +- err("Unknown tuner id:%d", +- af9015_af9013_config[adap->id].tuner); +- } +- return ret; +-} +- +-enum af9015_usb_table_entry { +- AFATECH_9015, +- AFATECH_9016, +- WINFAST_DTV_GOLD, +- PINNACLE_PCTV_71E, +- KWORLD_PLUSTV_399U, +- TINYTWIN, +- AZUREWAVE_TU700, +- TERRATEC_AF9015, +- KWORLD_PLUSTV_PC160, +- AVERTV_VOLAR_X, +- XTENSIONS_380U, +- MSI_DIGIVOX_DUO, +- AVERTV_VOLAR_X_REV2, +- TELESTAR_STARSTICK_2, +- AVERMEDIA_A309_USB, +- MSI_DIGIVOX_MINI_III, +- KWORLD_E396, +- KWORLD_E39B, +- KWORLD_E395, +- TREKSTOR_DVBT, +- AVERTV_A850, +- AVERTV_A805, +- CONCEPTRONIC_CTVDIGRCU, +- KWORLD_MC810, +- GENIUS_TVGO_DVB_T03, +- KWORLD_399U_2, +- KWORLD_PC160_T, +- SVEON_STV20, +- TINYTWIN_2, +- WINFAST_DTV2000DS, +- KWORLD_UB383_T, +- KWORLD_E39A, +- AVERMEDIA_A815M, +- CINERGY_T_STICK_RC, +- CINERGY_T_DUAL_RC, +- AVERTV_A850T, +- TINYTWIN_3, +- SVEON_STV22, +-}; +- +-static struct usb_device_id af9015_usb_table[] = { +- [AFATECH_9015] = +- {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, +- [AFATECH_9016] = +- {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, +- [WINFAST_DTV_GOLD] = +- {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, +- [PINNACLE_PCTV_71E] = +- {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, +- [KWORLD_PLUSTV_399U] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, +- [TINYTWIN] = {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)}, +- [AZUREWAVE_TU700] = +- {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)}, +- [TERRATEC_AF9015] = {USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)}, +- [KWORLD_PLUSTV_PC160] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, +- [AVERTV_VOLAR_X] = +- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, +- [XTENSIONS_380U] = +- {USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, +- [MSI_DIGIVOX_DUO] = +- {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, +- [AVERTV_VOLAR_X_REV2] = +- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, +- [TELESTAR_STARSTICK_2] = +- {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, +- [AVERMEDIA_A309_USB] = +- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, +- [MSI_DIGIVOX_MINI_III] = +- {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, +- [KWORLD_E396] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, +- [KWORLD_E39B] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)}, +- [KWORLD_E395] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)}, +- [TREKSTOR_DVBT] = {USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)}, +- [AVERTV_A850] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)}, +- [AVERTV_A805] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)}, +- [CONCEPTRONIC_CTVDIGRCU] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, +- [KWORLD_MC810] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, +- [GENIUS_TVGO_DVB_T03] = +- {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, +- [KWORLD_399U_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, +- [KWORLD_PC160_T] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, +- [SVEON_STV20] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, +- [TINYTWIN_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, +- [WINFAST_DTV2000DS] = +- {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, +- [KWORLD_UB383_T] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, +- [KWORLD_E39A] = +- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, +- [AVERMEDIA_A815M] = +- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, +- [CINERGY_T_STICK_RC] = {USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_T_STICK_RC)}, +- [CINERGY_T_DUAL_RC] = {USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)}, +- [AVERTV_A850T] = +- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)}, +- [TINYTWIN_3] = {USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)}, +- [SVEON_STV22] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)}, +- { } +-}; +-MODULE_DEVICE_TABLE(usb, af9015_usb_table); +- +-#define AF9015_RC_INTERVAL 500 +-static struct dvb_usb_device_properties af9015_properties[] = { +- { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = af9015_download_firmware, +- .firmware = "dvb-usb-af9015.fw", +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct af9015_state), +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 32, +- .pid_filter = af9015_pid_filter, +- .pid_filter_ctrl = af9015_pid_filter_ctrl, +- +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x84, +- }, +- }}, +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x85, +- .u = { +- .bulk = { +- .buffersize = +- TS_USB20_FRAME_SIZE, +- } +- } +- }, +- }}, +- } +- }, +- +- .identify_state = af9015_identify_state, +- +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "af9015", +- .rc_query = af9015_rc_query, +- .rc_interval = AF9015_RC_INTERVAL, +- .allowed_protos = RC_TYPE_NEC, +- }, +- +- .i2c_algo = &af9015_i2c_algo, +- +- .num_device_descs = 12, /* check max from dvb-usb.h */ +- .devices = { +- { +- .name = "Afatech AF9015 DVB-T USB2.0 stick", +- .cold_ids = { +- &af9015_usb_table[AFATECH_9015], +- &af9015_usb_table[AFATECH_9016], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Leadtek WinFast DTV Dongle Gold", +- .cold_ids = { +- &af9015_usb_table[WINFAST_DTV_GOLD], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Pinnacle PCTV 71e", +- .cold_ids = { +- &af9015_usb_table[PINNACLE_PCTV_71E], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld PlusTV Dual DVB-T Stick " \ +- "(DVB-T 399U)", +- .cold_ids = { +- &af9015_usb_table[KWORLD_PLUSTV_399U], +- &af9015_usb_table[KWORLD_399U_2], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "DigitalNow TinyTwin DVB-T Receiver", +- .cold_ids = { +- &af9015_usb_table[TINYTWIN], +- &af9015_usb_table[TINYTWIN_2], +- &af9015_usb_table[TINYTWIN_3], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "TwinHan AzureWave AD-TU700(704J)", +- .cold_ids = { +- &af9015_usb_table[AZUREWAVE_TU700], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "TerraTec Cinergy T USB XE", +- .cold_ids = { +- &af9015_usb_table[TERRATEC_AF9015], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld PlusTV Dual DVB-T PCI " \ +- "(DVB-T PC160-2T)", +- .cold_ids = { +- &af9015_usb_table[KWORLD_PLUSTV_PC160], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "AVerMedia AVerTV DVB-T Volar X", +- .cold_ids = { +- &af9015_usb_table[AVERTV_VOLAR_X], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "TerraTec Cinergy T Stick RC", +- .cold_ids = { +- &af9015_usb_table[CINERGY_T_STICK_RC], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "TerraTec Cinergy T Stick Dual RC", +- .cold_ids = { +- &af9015_usb_table[CINERGY_T_DUAL_RC], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "AverMedia AVerTV Red HD+ (A850T)", +- .cold_ids = { +- &af9015_usb_table[AVERTV_A850T], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- } +- }, { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = af9015_download_firmware, +- .firmware = "dvb-usb-af9015.fw", +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct af9015_state), +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 32, +- .pid_filter = af9015_pid_filter, +- .pid_filter_ctrl = af9015_pid_filter_ctrl, +- +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x84, +- }, +- }}, +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x85, +- .u = { +- .bulk = { +- .buffersize = +- TS_USB20_FRAME_SIZE, +- } +- } +- }, +- }}, +- } +- }, +- +- .identify_state = af9015_identify_state, +- +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "af9015", +- .rc_query = af9015_rc_query, +- .rc_interval = AF9015_RC_INTERVAL, +- .allowed_protos = RC_TYPE_NEC, +- }, +- +- .i2c_algo = &af9015_i2c_algo, +- +- .num_device_descs = 10, /* check max from dvb-usb.h */ +- .devices = { +- { +- .name = "Xtensions XD-380", +- .cold_ids = { +- &af9015_usb_table[XTENSIONS_380U], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "MSI DIGIVOX Duo", +- .cold_ids = { +- &af9015_usb_table[MSI_DIGIVOX_DUO], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Fujitsu-Siemens Slim Mobile USB DVB-T", +- .cold_ids = { +- &af9015_usb_table[AVERTV_VOLAR_X_REV2], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Telestar Starstick 2", +- .cold_ids = { +- &af9015_usb_table[TELESTAR_STARSTICK_2], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "AVerMedia A309", +- .cold_ids = { +- &af9015_usb_table[AVERMEDIA_A309_USB], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "MSI Digi VOX mini III", +- .cold_ids = { +- &af9015_usb_table[MSI_DIGIVOX_MINI_III], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld USB DVB-T TV Stick II " \ +- "(VS-DVB-T 395U)", +- .cold_ids = { +- &af9015_usb_table[KWORLD_E396], +- &af9015_usb_table[KWORLD_E39B], +- &af9015_usb_table[KWORLD_E395], +- &af9015_usb_table[KWORLD_E39A], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "TrekStor DVB-T USB Stick", +- .cold_ids = { +- &af9015_usb_table[TREKSTOR_DVBT], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "AverMedia AVerTV Volar Black HD " \ +- "(A850)", +- .cold_ids = { +- &af9015_usb_table[AVERTV_A850], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Sveon STV22 Dual USB DVB-T Tuner HDTV", +- .cold_ids = { +- &af9015_usb_table[SVEON_STV22], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- } +- }, { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = af9015_download_firmware, +- .firmware = "dvb-usb-af9015.fw", +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct af9015_state), +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 32, +- .pid_filter = af9015_pid_filter, +- .pid_filter_ctrl = af9015_pid_filter_ctrl, +- +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x84, +- }, +- }}, +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = +- af9015_af9013_frontend_attach, +- .tuner_attach = af9015_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x85, +- .u = { +- .bulk = { +- .buffersize = +- TS_USB20_FRAME_SIZE, +- } +- } +- }, +- }}, +- } +- }, +- +- .identify_state = af9015_identify_state, +- +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "af9015", +- .rc_query = af9015_rc_query, +- .rc_interval = AF9015_RC_INTERVAL, +- .allowed_protos = RC_TYPE_NEC, +- }, +- +- .i2c_algo = &af9015_i2c_algo, +- +- .num_device_descs = 9, /* check max from dvb-usb.h */ +- .devices = { +- { +- .name = "AverMedia AVerTV Volar GPS 805 (A805)", +- .cold_ids = { +- &af9015_usb_table[AVERTV_A805], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Conceptronic USB2.0 DVB-T CTVDIGRCU " \ +- "V3.0", +- .cold_ids = { +- &af9015_usb_table[CONCEPTRONIC_CTVDIGRCU], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld Digial MC-810", +- .cold_ids = { +- &af9015_usb_table[KWORLD_MC810], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Genius TVGo DVB-T03", +- .cold_ids = { +- &af9015_usb_table[GENIUS_TVGO_DVB_T03], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld PlusTV DVB-T PCI Pro Card " \ +- "(DVB-T PC160-T)", +- .cold_ids = { +- &af9015_usb_table[KWORLD_PC160_T], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Sveon STV20 Tuner USB DVB-T HDTV", +- .cold_ids = { +- &af9015_usb_table[SVEON_STV20], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "Leadtek WinFast DTV2000DS", +- .cold_ids = { +- &af9015_usb_table[WINFAST_DTV2000DS], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "KWorld USB DVB-T Stick Mobile " \ +- "(UB383-T)", +- .cold_ids = { +- &af9015_usb_table[KWORLD_UB383_T], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- { +- .name = "AverMedia AVerTV Volar M (A815Mac)", +- .cold_ids = { +- &af9015_usb_table[AVERMEDIA_A815M], +- NULL +- }, +- .warm_ids = {NULL}, +- }, +- } +- }, +-}; +- +-static int af9015_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int ret = 0; +- struct dvb_usb_device *d = NULL; +- struct usb_device *udev = interface_to_usbdev(intf); +- u8 i; +- +- deb_info("%s: interface:%d\n", __func__, +- intf->cur_altsetting->desc.bInterfaceNumber); +- +- /* interface 0 is used by DVB-T receiver and +- interface 1 is for remote controller (HID) */ +- if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { +- ret = af9015_read_config(udev); +- if (ret) +- return ret; +- +- for (i = 0; i < af9015_properties_count; i++) { +- ret = dvb_usb_device_init(intf, &af9015_properties[i], +- THIS_MODULE, &d, adapter_nr); +- if (!ret) +- break; +- if (ret != -ENODEV) +- return ret; +- } +- if (ret) +- return ret; +- +- if (d) +- ret = af9015_init(d); +- } +- +- return ret; +-} +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver af9015_usb_driver = { +- .name = "dvb_usb_af9015", +- .probe = af9015_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = af9015_usb_table, +-}; +- +-module_usb_driver(af9015_usb_driver); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h +deleted file mode 100644 +index f619063..0000000 +--- a/drivers/media/dvb/dvb-usb/af9015.h ++++ /dev/null +@@ -1,130 +0,0 @@ +-/* +- * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver +- * +- * Copyright (C) 2007 Antti Palosaari +- * +- * Thanks to Afatech who kindly provided information. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef _DVB_USB_AF9015_H_ +-#define _DVB_USB_AF9015_H_ +- +-#define DVB_USB_LOG_PREFIX "af9015" +-#include "dvb-usb.h" +- +-#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args) +-#define deb_rc(args...) dprintk(dvb_usb_af9015_debug, 0x02, args) +-#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args) +-#define deb_reg(args...) dprintk(dvb_usb_af9015_debug, 0x08, args) +-#define deb_i2c(args...) dprintk(dvb_usb_af9015_debug, 0x10, args) +-#define deb_fw(args...) dprintk(dvb_usb_af9015_debug, 0x20, args) +- +-#define AF9015_I2C_EEPROM 0xa0 +-#define AF9015_I2C_DEMOD 0x38 +-#define AF9015_USB_TIMEOUT 2000 +- +-/* EEPROM locations */ +-#define AF9015_EEPROM_IR_MODE 0x18 +-#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34 +-#define AF9015_EEPROM_TS_MODE 0x31 +-#define AF9015_EEPROM_DEMOD2_I2C 0x32 +- +-#define AF9015_EEPROM_SAW_BW1 0x35 +-#define AF9015_EEPROM_XTAL_TYPE1 0x36 +-#define AF9015_EEPROM_SPEC_INV1 0x37 +-#define AF9015_EEPROM_IF1L 0x38 +-#define AF9015_EEPROM_IF1H 0x39 +-#define AF9015_EEPROM_MT2060_IF1L 0x3a +-#define AF9015_EEPROM_MT2060_IF1H 0x3b +-#define AF9015_EEPROM_TUNER_ID1 0x3c +- +-#define AF9015_EEPROM_SAW_BW2 0x45 +-#define AF9015_EEPROM_XTAL_TYPE2 0x46 +-#define AF9015_EEPROM_SPEC_INV2 0x47 +-#define AF9015_EEPROM_IF2L 0x48 +-#define AF9015_EEPROM_IF2H 0x49 +-#define AF9015_EEPROM_MT2060_IF2L 0x4a +-#define AF9015_EEPROM_MT2060_IF2H 0x4b +-#define AF9015_EEPROM_TUNER_ID2 0x4c +- +-#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1) +- +-struct req_t { +- u8 cmd; /* [0] */ +- /* seq */ /* [1] */ +- u8 i2c_addr; /* [2] */ +- u16 addr; /* [3|4] */ +- u8 mbox; /* [5] */ +- u8 addr_len; /* [6] */ +- u8 data_len; /* [7] */ +- u8 *data; +-}; +- +-enum af9015_cmd { +- GET_CONFIG = 0x10, +- DOWNLOAD_FIRMWARE = 0x11, +- BOOT = 0x13, +- READ_MEMORY = 0x20, +- WRITE_MEMORY = 0x21, +- READ_WRITE_I2C = 0x22, +- COPY_FIRMWARE = 0x23, +- RECONNECT_USB = 0x5a, +- WRITE_VIRTUAL_MEMORY = 0x26, +- GET_IR_CODE = 0x27, +- READ_I2C, +- WRITE_I2C, +-}; +- +-enum af9015_ir_mode { +- AF9015_IR_MODE_DISABLED = 0, +- AF9015_IR_MODE_HID, +- AF9015_IR_MODE_RLC, +- AF9015_IR_MODE_RC6, +- AF9015_IR_MODE_POLLING, /* just guess */ +-}; +- +-struct af9015_state { +- u8 rc_repeat; +- u32 rc_keycode; +- u8 rc_last[4]; +- +- /* for demod callback override */ +- int (*set_frontend[2]) (struct dvb_frontend *fe); +- int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status); +- int (*init[2]) (struct dvb_frontend *fe); +- int (*sleep[2]) (struct dvb_frontend *fe); +-}; +- +-struct af9015_config { +- u8 dual_mode:1; +- u16 mt2060_if1[2]; +- u16 firmware_size; +- u16 firmware_checksum; +- u32 eeprom_sum; +-}; +- +-enum af9015_remote { +- AF9015_REMOTE_NONE = 0, +-/* 1 */ AF9015_REMOTE_A_LINK_DTU_M, +- AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, +- AF9015_REMOTE_MYGICTV_U718, +- AF9015_REMOTE_DIGITTRADE_DVB_T, +-/* 5 */ AF9015_REMOTE_AVERMEDIA_KS, +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c +deleted file mode 100644 +index cf0c318..0000000 +--- a/drivers/media/dvb/dvb-usb/anysee.c ++++ /dev/null +@@ -1,1380 +0,0 @@ +-/* +- * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver +- * +- * Copyright (C) 2007 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * TODO: +- * - add smart card reader support for Conditional Access (CA) +- * +- * Card reader in Anysee is nothing more than ISO 7816 card reader. +- * There is no hardware CAM in any Anysee device sold. +- * In my understanding it should be implemented by making own module +- * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This +- * module registers serial interface that can be used to communicate +- * with any ISO 7816 smart card. +- * +- * Any help according to implement serial smart card reader support +- * is highly welcome! +- */ +- +-#include "anysee.h" +-#include "tda1002x.h" +-#include "mt352.h" +-#include "mt352_priv.h" +-#include "zl10353.h" +-#include "tda18212.h" +-#include "cx24116.h" +-#include "stv0900.h" +-#include "stv6110.h" +-#include "isl6423.h" +-#include "cxd2820r.h" +- +-/* debug */ +-static int dvb_usb_anysee_debug; +-module_param_named(debug, dvb_usb_anysee_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-static int dvb_usb_anysee_delsys; +-module_param_named(delsys, dvb_usb_anysee_delsys, int, 0644); +-MODULE_PARM_DESC(delsys, "select delivery mode (0=DVB-C, 1=DVB-T)"); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static DEFINE_MUTEX(anysee_usb_mutex); +- +-static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen, +- u8 *rbuf, u8 rlen) +-{ +- struct anysee_state *state = d->priv; +- int act_len, ret; +- u8 buf[64]; +- +- memcpy(&buf[0], sbuf, slen); +- buf[60] = state->seq++; +- +- if (mutex_lock_interruptible(&anysee_usb_mutex) < 0) +- return -EAGAIN; +- +- deb_xfer(">>> "); +- debug_dump(buf, slen, deb_xfer); +- +- /* We need receive one message more after dvb_usb_generic_rw due +- to weird transaction flow, which is 1 x send + 2 x receive. */ +- ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0); +- if (!ret) { +- /* receive 2nd answer */ +- ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, +- d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf), +- &act_len, 2000); +- if (ret) +- err("%s: recv bulk message failed: %d", __func__, ret); +- else { +- deb_xfer("<<< "); +- debug_dump(buf, rlen, deb_xfer); +- +- if (buf[63] != 0x4f) +- deb_info("%s: cmd failed\n", __func__); +- } +- } +- +- /* read request, copy returned data to return buf */ +- if (!ret && rbuf && rlen) +- memcpy(rbuf, buf, rlen); +- +- mutex_unlock(&anysee_usb_mutex); +- +- return ret; +-} +- +-static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +-{ +- u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01}; +- int ret; +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1); +- deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val); +- return ret; +-} +- +-static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val) +-{ +- u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val}; +- deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val); +- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +-} +- +-/* write single register with mask */ +-static int anysee_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, +- u8 mask) +-{ +- int ret; +- u8 tmp; +- +- /* no need for read if whole reg is written */ +- if (mask != 0xff) { +- ret = anysee_read_reg(d, reg, &tmp); +- if (ret) +- return ret; +- +- val &= mask; +- tmp &= ~mask; +- val |= tmp; +- } +- +- return anysee_write_reg(d, reg, val); +-} +- +-/* read single register with mask */ +-static int anysee_rd_reg_mask(struct dvb_usb_device *d, u16 reg, u8 *val, +- u8 mask) +-{ +- int ret, i; +- u8 tmp; +- +- ret = anysee_read_reg(d, reg, &tmp); +- if (ret) +- return ret; +- +- tmp &= mask; +- +- /* find position of the first bit */ +- for (i = 0; i < 8; i++) { +- if ((mask >> i) & 0x01) +- break; +- } +- *val = tmp >> i; +- +- return 0; +-} +- +-static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id) +-{ +- u8 buf[] = {CMD_GET_HW_INFO}; +- return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3); +-} +- +-static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00}; +- deb_info("%s: onoff:%02x\n", __func__, onoff); +- return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0); +-} +- +-static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval) +-{ +- u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval}; +- deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval); +- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +-} +- +-static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff) +-{ +- u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff}; +- deb_info("%s: onoff:%02x\n", __func__, onoff); +- return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +-} +- +-/* I2C */ +-static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0, inc, i = 0; +- u8 buf[52]; /* 4 + 48 (I2C WR USB command header + I2C WR max) */ +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- while (i < num) { +- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { +- if (msg[i].len > 2 || msg[i+1].len > 60) { +- ret = -EOPNOTSUPP; +- break; +- } +- buf[0] = CMD_I2C_READ; +- buf[1] = (msg[i].addr << 1) | 0x01; +- buf[2] = msg[i].buf[0]; +- buf[3] = msg[i].buf[1]; +- buf[4] = msg[i].len-1; +- buf[5] = msg[i+1].len; +- ret = anysee_ctrl_msg(d, buf, 6, msg[i+1].buf, +- msg[i+1].len); +- inc = 2; +- } else { +- if (msg[i].len > 48) { +- ret = -EOPNOTSUPP; +- break; +- } +- buf[0] = CMD_I2C_WRITE; +- buf[1] = (msg[i].addr << 1); +- buf[2] = msg[i].len; +- buf[3] = 0x01; +- memcpy(&buf[4], msg[i].buf, msg[i].len); +- ret = anysee_ctrl_msg(d, buf, 4 + msg[i].len, NULL, 0); +- inc = 1; +- } +- if (ret) +- break; +- +- i += inc; +- } +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret ? ret : i; +-} +- +-static u32 anysee_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm anysee_i2c_algo = { +- .master_xfer = anysee_master_xfer, +- .functionality = anysee_i2c_func, +-}; +- +-static int anysee_mt352_demod_init(struct dvb_frontend *fe) +-{ +- static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x28 }; +- static u8 reset[] = { RESET, 0x80 }; +- static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 }; +- static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0x20 }; +- static u8 gpp_ctl_cfg[] = { GPP_CTL, 0x33 }; +- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; +- +- mt352_write(fe, clock_config, sizeof(clock_config)); +- udelay(200); +- mt352_write(fe, reset, sizeof(reset)); +- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); +- +- mt352_write(fe, agc_cfg, sizeof(agc_cfg)); +- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); +- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); +- +- return 0; +-} +- +-/* Callbacks for DVB USB */ +-static struct tda10023_config anysee_tda10023_config = { +- .demod_address = (0x1a >> 1), +- .invert = 0, +- .xtal = 16000000, +- .pll_m = 11, +- .pll_p = 3, +- .pll_n = 1, +- .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_C, +- .deltaf = 0xfeeb, +-}; +- +-static struct mt352_config anysee_mt352_config = { +- .demod_address = (0x1e >> 1), +- .demod_init = anysee_mt352_demod_init, +-}; +- +-static struct zl10353_config anysee_zl10353_config = { +- .demod_address = (0x1e >> 1), +- .parallel_ts = 1, +-}; +- +-static struct zl10353_config anysee_zl10353_tda18212_config2 = { +- .demod_address = (0x1e >> 1), +- .parallel_ts = 1, +- .disable_i2c_gate_ctrl = 1, +- .no_tuner = 1, +- .if2 = 41500, +-}; +- +-static struct zl10353_config anysee_zl10353_tda18212_config = { +- .demod_address = (0x18 >> 1), +- .parallel_ts = 1, +- .disable_i2c_gate_ctrl = 1, +- .no_tuner = 1, +- .if2 = 41500, +-}; +- +-static struct tda10023_config anysee_tda10023_tda18212_config = { +- .demod_address = (0x1a >> 1), +- .xtal = 16000000, +- .pll_m = 12, +- .pll_p = 3, +- .pll_n = 1, +- .output_mode = TDA10023_OUTPUT_MODE_PARALLEL_B, +- .deltaf = 0xba02, +-}; +- +-static struct tda18212_config anysee_tda18212_config = { +- .i2c_address = (0xc0 >> 1), +- .if_dvbt_6 = 4150, +- .if_dvbt_7 = 4150, +- .if_dvbt_8 = 4150, +- .if_dvbc = 5000, +-}; +- +-static struct tda18212_config anysee_tda18212_config2 = { +- .i2c_address = 0x60 /* (0xc0 >> 1) */, +- .if_dvbt_6 = 3550, +- .if_dvbt_7 = 3700, +- .if_dvbt_8 = 4150, +- .if_dvbt2_6 = 3250, +- .if_dvbt2_7 = 4000, +- .if_dvbt2_8 = 4000, +- .if_dvbc = 5000, +-}; +- +-static struct cx24116_config anysee_cx24116_config = { +- .demod_address = (0xaa >> 1), +- .mpg_clk_pos_pol = 0x00, +- .i2c_wr_max = 48, +-}; +- +-static struct stv0900_config anysee_stv0900_config = { +- .demod_address = (0xd0 >> 1), +- .demod_mode = 0, +- .xtal = 8000000, +- .clkmode = 3, +- .diseqc_mode = 2, +- .tun1_maddress = 0, +- .tun1_adc = 1, /* 1 Vpp */ +- .path1_mode = 3, +-}; +- +-static struct stv6110_config anysee_stv6110_config = { +- .i2c_address = (0xc0 >> 1), +- .mclk = 16000000, +- .clk_div = 1, +-}; +- +-static struct isl6423_config anysee_isl6423_config = { +- .current_max = SEC_CURRENT_800m, +- .curlim = SEC_CURRENT_LIM_OFF, +- .mod_extern = 1, +- .addr = (0x10 >> 1), +-}; +- +-static struct cxd2820r_config anysee_cxd2820r_config = { +- .i2c_address = 0x6d, /* (0xda >> 1) */ +- .ts_mode = 0x38, +-}; +- +-/* +- * New USB device strings: Mfr=1, Product=2, SerialNumber=0 +- * Manufacturer: AMT.CO.KR +- * +- * E30 VID=04b4 PID=861f HW=2 FW=2.1 Product=???????? +- * PCB: ? +- * parts: DNOS404ZH102A(MT352, DTT7579(?)) +- * +- * E30 VID=04b4 PID=861f HW=2 FW=2.1 "anysee-T(LP)" +- * PCB: PCB 507T (rev1.61) +- * parts: DNOS404ZH103A(ZL10353, DTT7579(?)) +- * OEA=0a OEB=00 OEC=00 OED=ff OEE=00 +- * IOA=45 IOB=ff IOC=00 IOD=ff IOE=00 +- * +- * E30 Plus VID=04b4 PID=861f HW=6 FW=1.0 "anysee" +- * PCB: 507CD (rev1.1) +- * parts: DNOS404ZH103A(ZL10353, DTT7579(?)), CST56I01 +- * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe +- * IOA=4f IOB=ff IOC=00 IOD=06 IOE=01 +- * IOD[0] ZL10353 1=enabled +- * IOA[7] TS 0=enabled +- * tuner is not behind ZL10353 I2C-gate (no care if gate disabled or not) +- * +- * E30 C Plus VID=04b4 PID=861f HW=10 FW=1.0 "anysee-DC(LP)" +- * PCB: 507DC (rev0.2) +- * parts: TDA10023, DTOS403IH102B TM, CST56I01 +- * OEA=80 OEB=00 OEC=00 OED=ff OEE=fe +- * IOA=4f IOB=ff IOC=00 IOD=26 IOE=01 +- * IOD[0] TDA10023 1=enabled +- * +- * E30 S2 Plus VID=04b4 PID=861f HW=11 FW=0.1 "anysee-S2(LP)" +- * PCB: 507SI (rev2.1) +- * parts: BS2N10WCC01(CX24116, CX24118), ISL6423, TDA8024 +- * OEA=80 OEB=00 OEC=ff OED=ff OEE=fe +- * IOA=4d IOB=ff IOC=00 IOD=26 IOE=01 +- * IOD[0] CX24116 1=enabled +- * +- * E30 C Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)" +- * PCB: 507FA (rev0.4) +- * parts: TDA10023, DTOS403IH102B TM, TDA8024 +- * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff +- * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0 +- * IOD[5] TDA10023 1=enabled +- * IOE[0] tuner 1=enabled +- * +- * E30 Combo Plus VID=1c73 PID=861f HW=15 FW=1.2 "anysee-FA(LP)" +- * PCB: 507FA (rev1.1) +- * parts: ZL10353, TDA10023, DTOS403IH102B TM, TDA8024 +- * OEA=80 OEB=00 OEC=ff OED=ff OEE=ff +- * IOA=4d IOB=ff IOC=00 IOD=00 IOE=c0 +- * DVB-C: +- * IOD[5] TDA10023 1=enabled +- * IOE[0] tuner 1=enabled +- * DVB-T: +- * IOD[0] ZL10353 1=enabled +- * IOE[0] tuner 0=enabled +- * tuner is behind ZL10353 I2C-gate +- * +- * E7 TC VID=1c73 PID=861f HW=18 FW=0.7 AMTCI=0.5 "anysee-E7TC(LP)" +- * PCB: 508TC (rev0.6) +- * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212) +- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff +- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 +- * IOA[7] TS 1=enabled +- * IOE[4] TDA18212 1=enabled +- * DVB-C: +- * IOD[6] ZL10353 0=disabled +- * IOD[5] TDA10023 1=enabled +- * IOE[0] IF 1=enabled +- * DVB-T: +- * IOD[5] TDA10023 0=disabled +- * IOD[6] ZL10353 1=enabled +- * IOE[0] IF 0=enabled +- * +- * E7 S2 VID=1c73 PID=861f HW=19 FW=0.4 AMTCI=0.5 "anysee-E7S2(LP)" +- * PCB: 508S2 (rev0.7) +- * parts: DNBU10512IST(STV0903, STV6110), ISL6423 +- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff +- * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4 +- * IOA[7] TS 1=enabled +- * IOE[5] STV0903 1=enabled +- * +- * E7 T2C VID=1c73 PID=861f HW=20 FW=0.1 AMTCI=0.5 "anysee-E7T2C(LP)" +- * PCB: 508T2C (rev0.3) +- * parts: DNOQ44QCH106A(CXD2820R, TDA18212), TDA8024 +- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff +- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 +- * IOA[7] TS 1=enabled +- * IOE[5] CXD2820R 1=enabled +- * +- * E7 PTC VID=1c73 PID=861f HW=21 FW=0.1 AMTCI=?? "anysee-E7PTC(LP)" +- * PCB: 508PTC (rev0.5) +- * parts: ZL10353, TDA10023, DNOD44CDH086A(TDA18212) +- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff +- * IOA=4d IOB=00 IOC=cc IOD=48 IOE=e4 +- * IOA[7] TS 1=enabled +- * IOE[4] TDA18212 1=enabled +- * DVB-C: +- * IOD[6] ZL10353 0=disabled +- * IOD[5] TDA10023 1=enabled +- * IOE[0] IF 1=enabled +- * DVB-T: +- * IOD[5] TDA10023 0=disabled +- * IOD[6] ZL10353 1=enabled +- * IOE[0] IF 0=enabled +- * +- * E7 PS2 VID=1c73 PID=861f HW=22 FW=0.1 AMTCI=?? "anysee-E7PS2(LP)" +- * PCB: 508PS2 (rev0.4) +- * parts: DNBU10512IST(STV0903, STV6110), ISL6423 +- * OEA=80 OEB=00 OEC=03 OED=f7 OEE=ff +- * IOA=4d IOB=00 IOC=c4 IOD=08 IOE=e4 +- * IOA[7] TS 1=enabled +- * IOE[5] STV0903 1=enabled +- */ +- +- +-/* external I2C gate used for DNOD44CDH086A(TDA18212) tuner module */ +-static int anysee_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- /* enable / disable tuner access on IOE[4] */ +- return anysee_wr_reg_mask(adap->dev, REG_IOE, (enable << 4), 0x10); +-} +- +-static int anysee_frontend_ctrl(struct dvb_frontend *fe, int onoff) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct anysee_state *state = adap->dev->priv; +- int ret; +- +- deb_info("%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); +- +- /* no frontend sleep control */ +- if (onoff == 0) +- return 0; +- +- switch (state->hw) { +- case ANYSEE_HW_507FA: /* 15 */ +- /* E30 Combo Plus */ +- /* E30 C Plus */ +- +- if ((fe->id ^ dvb_usb_anysee_delsys) == 0) { +- /* disable DVB-T demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0), +- 0x01); +- if (ret) +- goto error; +- +- /* enable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable DVB-C tuner on IOE[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), +- 0x01); +- if (ret) +- goto error; +- } else { +- /* disable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable DVB-T demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), +- 0x01); +- if (ret) +- goto error; +- +- /* enable DVB-T tuner on IOE[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), +- 0x01); +- if (ret) +- goto error; +- } +- +- break; +- case ANYSEE_HW_508TC: /* 18 */ +- case ANYSEE_HW_508PTC: /* 21 */ +- /* E7 TC */ +- /* E7 PTC */ +- +- if ((fe->id ^ dvb_usb_anysee_delsys) == 0) { +- /* disable DVB-T demod on IOD[6] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6), +- 0x40); +- if (ret) +- goto error; +- +- /* enable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable IF route on IOE[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 0), +- 0x01); +- if (ret) +- goto error; +- } else { +- /* disable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable DVB-T demod on IOD[6] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6), +- 0x40); +- if (ret) +- goto error; +- +- /* enable IF route on IOE[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 0), +- 0x01); +- if (ret) +- goto error; +- } +- +- break; +- default: +- ret = 0; +- } +- +-error: +- return ret; +-} +- +-static int anysee_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- int ret; +- struct anysee_state *state = adap->dev->priv; +- u8 hw_info[3]; +- u8 tmp; +- struct i2c_msg msg[2] = { +- { +- .addr = anysee_tda18212_config.i2c_address, +- .flags = 0, +- .len = 1, +- .buf = "\x00", +- }, { +- .addr = anysee_tda18212_config.i2c_address, +- .flags = I2C_M_RD, +- .len = 1, +- .buf = &tmp, +- } +- }; +- +- /* detect hardware only once */ +- if (adap->fe_adap[0].fe == NULL) { +- /* Check which hardware we have. +- * We must do this call two times to get reliable values +- * (hw/fw bug). +- */ +- ret = anysee_get_hw_info(adap->dev, hw_info); +- if (ret) +- goto error; +- +- ret = anysee_get_hw_info(adap->dev, hw_info); +- if (ret) +- goto error; +- +- /* Meaning of these info bytes are guessed. */ +- info("firmware version:%d.%d hardware id:%d", +- hw_info[1], hw_info[2], hw_info[0]); +- +- state->hw = hw_info[0]; +- } +- +- /* set current frondend ID for devices having two frondends */ +- if (adap->fe_adap[0].fe) +- state->fe_id++; +- +- switch (state->hw) { +- case ANYSEE_HW_507T: /* 2 */ +- /* E30 */ +- +- if (state->fe_id) +- break; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, +- &anysee_mt352_config, &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe) +- break; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, +- &anysee_zl10353_config, &adap->dev->i2c_adap); +- +- break; +- case ANYSEE_HW_507CD: /* 6 */ +- /* E30 Plus */ +- +- if (state->fe_id) +- break; +- +- /* enable DVB-T demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); +- if (ret) +- goto error; +- +- /* enable transport stream on IOA[7] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOA, (0 << 7), 0x80); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, +- &anysee_zl10353_config, &adap->dev->i2c_adap); +- +- break; +- case ANYSEE_HW_507DC: /* 10 */ +- /* E30 C Plus */ +- +- if (state->fe_id) +- break; +- +- /* enable DVB-C demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(tda10023_attach, +- &anysee_tda10023_config, &adap->dev->i2c_adap, 0x48); +- +- break; +- case ANYSEE_HW_507SI: /* 11 */ +- /* E30 S2 Plus */ +- +- if (state->fe_id) +- break; +- +- /* enable DVB-S/S2 demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), 0x01); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(cx24116_attach, +- &anysee_cx24116_config, &adap->dev->i2c_adap); +- +- break; +- case ANYSEE_HW_507FA: /* 15 */ +- /* E30 Combo Plus */ +- /* E30 C Plus */ +- +- /* enable tuner on IOE[4] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 4), 0x10); +- if (ret) +- goto error; +- +- /* probe TDA18212 */ +- tmp = 0; +- ret = i2c_transfer(&adap->dev->i2c_adap, msg, 2); +- if (ret == 2 && tmp == 0xc7) +- deb_info("%s: TDA18212 found\n", __func__); +- else +- tmp = 0; +- +- /* disable tuner on IOE[4] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (0 << 4), 0x10); +- if (ret) +- goto error; +- +- if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) { +- /* disable DVB-T demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 0), +- 0x01); +- if (ret) +- goto error; +- +- /* enable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* attach demod */ +- if (tmp == 0xc7) { +- /* TDA18212 config */ +- adap->fe_adap[state->fe_id].fe = dvb_attach( +- tda10023_attach, +- &anysee_tda10023_tda18212_config, +- &adap->dev->i2c_adap, 0x48); +- } else { +- /* PLL config */ +- adap->fe_adap[state->fe_id].fe = dvb_attach( +- tda10023_attach, +- &anysee_tda10023_config, +- &adap->dev->i2c_adap, 0x48); +- } +- } else { +- /* disable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable DVB-T demod on IOD[0] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 0), +- 0x01); +- if (ret) +- goto error; +- +- /* attach demod */ +- if (tmp == 0xc7) { +- /* TDA18212 config */ +- adap->fe_adap[state->fe_id].fe = dvb_attach( +- zl10353_attach, +- &anysee_zl10353_tda18212_config2, +- &adap->dev->i2c_adap); +- } else { +- /* PLL config */ +- adap->fe_adap[state->fe_id].fe = dvb_attach( +- zl10353_attach, +- &anysee_zl10353_config, +- &adap->dev->i2c_adap); +- } +- } +- +- /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ +- if (tmp == 0xc7) { +- if (adap->fe_adap[state->fe_id].fe) +- adap->fe_adap[state->fe_id].fe->ops.i2c_gate_ctrl = +- anysee_i2c_gate_ctrl; +- } +- +- break; +- case ANYSEE_HW_508TC: /* 18 */ +- case ANYSEE_HW_508PTC: /* 21 */ +- /* E7 TC */ +- /* E7 PTC */ +- +- if ((state->fe_id ^ dvb_usb_anysee_delsys) == 0) { +- /* disable DVB-T demod on IOD[6] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 6), +- 0x40); +- if (ret) +- goto error; +- +- /* enable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[state->fe_id].fe = +- dvb_attach(tda10023_attach, +- &anysee_tda10023_tda18212_config, +- &adap->dev->i2c_adap, 0x48); +- } else { +- /* disable DVB-C demod on IOD[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (0 << 5), +- 0x20); +- if (ret) +- goto error; +- +- /* enable DVB-T demod on IOD[6] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOD, (1 << 6), +- 0x40); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[state->fe_id].fe = +- dvb_attach(zl10353_attach, +- &anysee_zl10353_tda18212_config, +- &adap->dev->i2c_adap); +- } +- +- /* I2C gate for DNOD44CDH086A(TDA18212) tuner module */ +- if (adap->fe_adap[state->fe_id].fe) +- adap->fe_adap[state->fe_id].fe->ops.i2c_gate_ctrl = +- anysee_i2c_gate_ctrl; +- +- state->has_ci = true; +- +- break; +- case ANYSEE_HW_508S2: /* 19 */ +- case ANYSEE_HW_508PS2: /* 22 */ +- /* E7 S2 */ +- /* E7 PS2 */ +- +- if (state->fe_id) +- break; +- +- /* enable DVB-S/S2 demod on IOE[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 5), 0x20); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[0].fe = dvb_attach(stv0900_attach, +- &anysee_stv0900_config, &adap->dev->i2c_adap, 0); +- +- state->has_ci = true; +- +- break; +- case ANYSEE_HW_508T2C: /* 20 */ +- /* E7 T2C */ +- +- if (state->fe_id) +- break; +- +- /* enable DVB-T/T2/C demod on IOE[5] */ +- ret = anysee_wr_reg_mask(adap->dev, REG_IOE, (1 << 5), 0x20); +- if (ret) +- goto error; +- +- /* attach demod */ +- adap->fe_adap[state->fe_id].fe = dvb_attach(cxd2820r_attach, +- &anysee_cxd2820r_config, &adap->dev->i2c_adap); +- +- state->has_ci = true; +- +- break; +- } +- +- if (!adap->fe_adap[0].fe) { +- /* we have no frontend :-( */ +- ret = -ENODEV; +- err("Unsupported Anysee version. " \ +- "Please report the ."); +- } +-error: +- return ret; +-} +- +-static int anysee_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct anysee_state *state = adap->dev->priv; +- struct dvb_frontend *fe; +- int ret; +- deb_info("%s: fe=%d\n", __func__, state->fe_id); +- +- switch (state->hw) { +- case ANYSEE_HW_507T: /* 2 */ +- /* E30 */ +- +- /* attach tuner */ +- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, +- (0xc2 >> 1), NULL, DVB_PLL_THOMSON_DTT7579); +- +- break; +- case ANYSEE_HW_507CD: /* 6 */ +- /* E30 Plus */ +- +- /* attach tuner */ +- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, +- (0xc2 >> 1), &adap->dev->i2c_adap, +- DVB_PLL_THOMSON_DTT7579); +- +- break; +- case ANYSEE_HW_507DC: /* 10 */ +- /* E30 C Plus */ +- +- /* attach tuner */ +- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, +- (0xc0 >> 1), &adap->dev->i2c_adap, +- DVB_PLL_SAMSUNG_DTOS403IH102A); +- +- break; +- case ANYSEE_HW_507SI: /* 11 */ +- /* E30 S2 Plus */ +- +- /* attach LNB controller */ +- fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, &anysee_isl6423_config); +- +- break; +- case ANYSEE_HW_507FA: /* 15 */ +- /* E30 Combo Plus */ +- /* E30 C Plus */ +- +- /* Try first attach TDA18212 silicon tuner on IOE[4], if that +- * fails attach old simple PLL. */ +- +- /* attach tuner */ +- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe, +- &adap->dev->i2c_adap, &anysee_tda18212_config); +- if (fe) +- break; +- +- /* attach tuner */ +- fe = dvb_attach(dvb_pll_attach, adap->fe_adap[state->fe_id].fe, +- (0xc0 >> 1), &adap->dev->i2c_adap, +- DVB_PLL_SAMSUNG_DTOS403IH102A); +- +- break; +- case ANYSEE_HW_508TC: /* 18 */ +- case ANYSEE_HW_508PTC: /* 21 */ +- /* E7 TC */ +- /* E7 PTC */ +- +- /* attach tuner */ +- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe, +- &adap->dev->i2c_adap, &anysee_tda18212_config); +- +- break; +- case ANYSEE_HW_508S2: /* 19 */ +- case ANYSEE_HW_508PS2: /* 22 */ +- /* E7 S2 */ +- /* E7 PS2 */ +- +- /* attach tuner */ +- fe = dvb_attach(stv6110_attach, adap->fe_adap[0].fe, +- &anysee_stv6110_config, &adap->dev->i2c_adap); +- +- if (fe) { +- /* attach LNB controller */ +- fe = dvb_attach(isl6423_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, &anysee_isl6423_config); +- } +- +- break; +- +- case ANYSEE_HW_508T2C: /* 20 */ +- /* E7 T2C */ +- +- /* attach tuner */ +- fe = dvb_attach(tda18212_attach, adap->fe_adap[state->fe_id].fe, +- &adap->dev->i2c_adap, &anysee_tda18212_config2); +- +- break; +- default: +- fe = NULL; +- } +- +- if (fe) +- ret = 0; +- else +- ret = -ENODEV; +- +- return ret; +-} +- +-static int anysee_rc_query(struct dvb_usb_device *d) +-{ +- u8 buf[] = {CMD_GET_IR_CODE}; +- u8 ircode[2]; +- int ret; +- +- /* Remote controller is basic NEC using address byte 0x08. +- Anysee device RC query returns only two bytes, status and code, +- address byte is dropped. Also it does not return any value for +- NEC RCs having address byte other than 0x08. Due to that, we +- cannot use that device as standard NEC receiver. +- It could be possible make hack which reads whole code directly +- from device memory... */ +- +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), ircode, sizeof(ircode)); +- if (ret) +- return ret; +- +- if (ircode[0]) { +- deb_rc("%s: key pressed %02x\n", __func__, ircode[1]); +- rc_keydown(d->rc_dev, 0x08 << 8 | ircode[1], 0); +- } +- +- return 0; +-} +- +-static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot, +- int addr) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- u8 buf[] = {CMD_CI, 0x02, 0x40 | addr >> 8, addr & 0xff, 0x00, 1}; +- u8 val; +- +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1); +- if (ret) +- return ret; +- +- return val; +-} +- +-static int anysee_ci_write_attribute_mem(struct dvb_ca_en50221 *ci, int slot, +- int addr, u8 val) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- u8 buf[] = {CMD_CI, 0x03, 0x40 | addr >> 8, addr & 0xff, 0x00, 1, val}; +- +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int anysee_ci_read_cam_control(struct dvb_ca_en50221 *ci, int slot, +- u8 addr) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- u8 buf[] = {CMD_CI, 0x04, 0x40, addr, 0x00, 1}; +- u8 val; +- +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), &val, 1); +- if (ret) +- return ret; +- +- return val; +-} +- +-static int anysee_ci_write_cam_control(struct dvb_ca_en50221 *ci, int slot, +- u8 addr, u8 val) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- u8 buf[] = {CMD_CI, 0x05, 0x40, addr, 0x00, 1, val}; +- +- ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int anysee_ci_slot_reset(struct dvb_ca_en50221 *ci, int slot) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- struct anysee_state *state = d->priv; +- +- state->ci_cam_ready = jiffies + msecs_to_jiffies(1000); +- +- ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80); +- if (ret) +- return ret; +- +- msleep(300); +- +- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int anysee_ci_slot_shutdown(struct dvb_ca_en50221 *ci, int slot) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- +- ret = anysee_wr_reg_mask(d, REG_IOA, (0 << 7), 0x80); +- if (ret) +- return ret; +- +- msleep(30); +- +- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int anysee_ci_slot_ts_enable(struct dvb_ca_en50221 *ci, int slot) +-{ +- struct dvb_usb_device *d = ci->data; +- int ret; +- +- ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 1), 0x02); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static int anysee_ci_poll_slot_status(struct dvb_ca_en50221 *ci, int slot, +- int open) +-{ +- struct dvb_usb_device *d = ci->data; +- struct anysee_state *state = d->priv; +- int ret; +- u8 tmp; +- +- ret = anysee_rd_reg_mask(d, REG_IOC, &tmp, 0x40); +- if (ret) +- return ret; +- +- if (tmp == 0) { +- ret = DVB_CA_EN50221_POLL_CAM_PRESENT; +- if (time_after(jiffies, state->ci_cam_ready)) +- ret |= DVB_CA_EN50221_POLL_CAM_READY; +- } +- +- return ret; +-} +- +-static int anysee_ci_init(struct dvb_usb_device *d) +-{ +- struct anysee_state *state = d->priv; +- int ret; +- +- state->ci.owner = THIS_MODULE; +- state->ci.read_attribute_mem = anysee_ci_read_attribute_mem; +- state->ci.write_attribute_mem = anysee_ci_write_attribute_mem; +- state->ci.read_cam_control = anysee_ci_read_cam_control; +- state->ci.write_cam_control = anysee_ci_write_cam_control; +- state->ci.slot_reset = anysee_ci_slot_reset; +- state->ci.slot_shutdown = anysee_ci_slot_shutdown; +- state->ci.slot_ts_enable = anysee_ci_slot_ts_enable; +- state->ci.poll_slot_status = anysee_ci_poll_slot_status; +- state->ci.data = d; +- +- ret = anysee_wr_reg_mask(d, REG_IOA, (1 << 7), 0x80); +- if (ret) +- return ret; +- +- ret = anysee_wr_reg_mask(d, REG_IOD, (0 << 2)|(0 << 1)|(0 << 0), 0x07); +- if (ret) +- return ret; +- +- ret = anysee_wr_reg_mask(d, REG_IOD, (1 << 2)|(1 << 1)|(1 << 0), 0x07); +- if (ret) +- return ret; +- +- ret = dvb_ca_en50221_init(&d->adapter[0].dvb_adap, &state->ci, 0, 1); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static void anysee_ci_release(struct dvb_usb_device *d) +-{ +- struct anysee_state *state = d->priv; +- +- /* detach CI */ +- if (state->has_ci) +- dvb_ca_en50221_release(&state->ci); +- +- return; +-} +- +-static int anysee_init(struct dvb_usb_device *d) +-{ +- struct anysee_state *state = d->priv; +- int ret; +- +- /* LED light */ +- ret = anysee_led_ctrl(d, 0x01, 0x03); +- if (ret) +- return ret; +- +- /* enable IR */ +- ret = anysee_ir_ctrl(d, 1); +- if (ret) +- return ret; +- +- /* attach CI */ +- if (state->has_ci) { +- ret = anysee_ci_init(d); +- if (ret) { +- state->has_ci = false; +- return ret; +- } +- } +- +- return 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties anysee_properties; +- +-static int anysee_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- struct usb_host_interface *alt; +- int ret; +- +- /* There is one interface with two alternate settings. +- Alternate setting 0 is for bulk transfer. +- Alternate setting 1 is for isochronous transfer. +- We use bulk transfer (alternate setting 0). */ +- if (intf->num_altsetting < 1) +- return -ENODEV; +- +- /* +- * Anysee is always warm (its USB-bridge, Cypress FX2, uploads +- * firmware from eeprom). If dvb_usb_device_init() succeeds that +- * means d is a valid pointer. +- */ +- ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d, +- adapter_nr); +- if (ret) +- return ret; +- +- alt = usb_altnum_to_altsetting(intf, 0); +- if (alt == NULL) { +- deb_info("%s: no alt found!\n", __func__); +- return -ENODEV; +- } +- +- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, +- alt->desc.bAlternateSetting); +- if (ret) +- return ret; +- +- return anysee_init(d); +-} +- +-static void anysee_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- +- anysee_ci_release(d); +- dvb_usb_device_exit(intf); +- +- return; +-} +- +-static struct usb_device_id anysee_table[] = { +- { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) }, +- { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, anysee_table); +- +-static struct dvb_usb_device_properties anysee_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = sizeof(struct anysee_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 2, +- .frontend_ctrl = anysee_frontend_ctrl, +- .fe = { { +- .streaming_ctrl = anysee_streaming_ctrl, +- .frontend_attach = anysee_frontend_attach, +- .tuner_attach = anysee_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = (16*512), +- } +- } +- }, +- }, { +- .streaming_ctrl = anysee_streaming_ctrl, +- .frontend_attach = anysee_frontend_attach, +- .tuner_attach = anysee_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = (16*512), +- } +- } +- }, +- } }, +- } +- }, +- +- .rc.core = { +- .rc_codes = RC_MAP_ANYSEE, +- .protocol = RC_TYPE_OTHER, +- .module_name = "anysee", +- .rc_query = anysee_rc_query, +- .rc_interval = 250, /* windows driver uses 500ms */ +- }, +- +- .i2c_algo = &anysee_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 1, +- +- .num_device_descs = 1, +- .devices = { +- { +- .name = "Anysee DVB USB2.0", +- .cold_ids = {NULL}, +- .warm_ids = {&anysee_table[0], +- &anysee_table[1], NULL}, +- }, +- } +-}; +- +-static struct usb_driver anysee_driver = { +- .name = "dvb_usb_anysee", +- .probe = anysee_probe, +- .disconnect = anysee_disconnect, +- .id_table = anysee_table, +-}; +- +-module_usb_driver(anysee_driver); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h +deleted file mode 100644 +index 8ac8794..0000000 +--- a/drivers/media/dvb/dvb-usb/anysee.h ++++ /dev/null +@@ -1,332 +0,0 @@ +-/* +- * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver +- * +- * Copyright (C) 2007 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * TODO: +- * - add smart card reader support for Conditional Access (CA) +- * +- * Card reader in Anysee is nothing more than ISO 7816 card reader. +- * There is no hardware CAM in any Anysee device sold. +- * In my understanding it should be implemented by making own module +- * for ISO 7816 card reader, like dvb_ca_en50221 is implemented. This +- * module registers serial interface that can be used to communicate +- * with any ISO 7816 smart card. +- * +- * Any help according to implement serial smart card reader support +- * is highly welcome! +- */ +- +-#ifndef _DVB_USB_ANYSEE_H_ +-#define _DVB_USB_ANYSEE_H_ +- +-#define DVB_USB_LOG_PREFIX "anysee" +-#include "dvb-usb.h" +-#include "dvb_ca_en50221.h" +- +-#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args) +-#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args) +-#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args) +-#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args) +-#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args) +-#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args) +- +-enum cmd { +- CMD_I2C_READ = 0x33, +- CMD_I2C_WRITE = 0x31, +- CMD_REG_READ = 0xb0, +- CMD_REG_WRITE = 0xb1, +- CMD_STREAMING_CTRL = 0x12, +- CMD_LED_AND_IR_CTRL = 0x16, +- CMD_GET_IR_CODE = 0x41, +- CMD_GET_HW_INFO = 0x19, +- CMD_SMARTCARD = 0x34, +- CMD_CI = 0x37, +-}; +- +-struct anysee_state { +- u8 hw; /* PCB ID */ +- u8 seq; +- u8 fe_id:1; /* frondend ID */ +- u8 has_ci:1; +- struct dvb_ca_en50221 ci; +- unsigned long ci_cam_ready; /* jiffies */ +-}; +- +-#define ANYSEE_HW_507T 2 /* E30 */ +-#define ANYSEE_HW_507CD 6 /* E30 Plus */ +-#define ANYSEE_HW_507DC 10 /* E30 C Plus */ +-#define ANYSEE_HW_507SI 11 /* E30 S2 Plus */ +-#define ANYSEE_HW_507FA 15 /* E30 Combo Plus / E30 C Plus */ +-#define ANYSEE_HW_508TC 18 /* E7 TC */ +-#define ANYSEE_HW_508S2 19 /* E7 S2 */ +-#define ANYSEE_HW_508T2C 20 /* E7 T2C */ +-#define ANYSEE_HW_508PTC 21 /* E7 PTC Plus */ +-#define ANYSEE_HW_508PS2 22 /* E7 PS2 Plus */ +- +-#define REG_IOA 0x80 /* Port A (bit addressable) */ +-#define REG_IOB 0x90 /* Port B (bit addressable) */ +-#define REG_IOC 0xa0 /* Port C (bit addressable) */ +-#define REG_IOD 0xb0 /* Port D (bit addressable) */ +-#define REG_IOE 0xb1 /* Port E (NOT bit addressable) */ +-#define REG_OEA 0xb2 /* Port A Output Enable */ +-#define REG_OEB 0xb3 /* Port B Output Enable */ +-#define REG_OEC 0xb4 /* Port C Output Enable */ +-#define REG_OED 0xb5 /* Port D Output Enable */ +-#define REG_OEE 0xb6 /* Port E Output Enable */ +- +-#endif +- +-/*************************************************************************** +- * USB API description (reverse engineered) +- *************************************************************************** +- +-Transaction flow: +-================= +-BULK[00001] >>> REQUEST PACKET 64 bytes +-BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY) +-BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY) +- +-General reply packet(s) are always used if not own reply defined. +- +-============================================================================ +-| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY) +-============================================================================ +-| 00 | reply data (if any) from previous transaction +-| | Just same reply packet as returned during previous transaction. +-| | Needed only if reply is missed in previous transaction. +-| | Just skip normally. +----------------------------------------------------------------------------- +-| 01-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY) +-============================================================================ +-| 00 | reply data (if any) +----------------------------------------------------------------------------- +-| 01-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | I2C WRITE REQUEST PACKET +-============================================================================ +-| 00 | 0x31 I2C write command +----------------------------------------------------------------------------- +-| 01 | i2c address +----------------------------------------------------------------------------- +-| 02 | data length +-| | 0x02 (for typical I2C reg / val pair) +----------------------------------------------------------------------------- +-| 03 | 0x01 +----------------------------------------------------------------------------- +-| 04- | data +----------------------------------------------------------------------------- +-| -59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | I2C READ REQUEST PACKET +-============================================================================ +-| 00 | 0x33 I2C read command +----------------------------------------------------------------------------- +-| 01 | i2c address + 1 +----------------------------------------------------------------------------- +-| 02 | register +----------------------------------------------------------------------------- +-| 03 | 0x00 +----------------------------------------------------------------------------- +-| 04 | 0x00 +----------------------------------------------------------------------------- +-| 05 | data length +----------------------------------------------------------------------------- +-| 06-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET +-============================================================================ +-| 00 | 0xb1 register write command +----------------------------------------------------------------------------- +-| 01-02 | register +----------------------------------------------------------------------------- +-| 03 | 0x01 +----------------------------------------------------------------------------- +-| 04 | value +----------------------------------------------------------------------------- +-| 05-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET +-============================================================================ +-| 00 | 0xb0 register read command +----------------------------------------------------------------------------- +-| 01-02 | register +----------------------------------------------------------------------------- +-| 03 | 0x01 +----------------------------------------------------------------------------- +-| 04-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | LED CONTROL REQUEST PACKET +-============================================================================ +-| 00 | 0x16 LED and IR control command +----------------------------------------------------------------------------- +-| 01 | 0x01 (LED) +----------------------------------------------------------------------------- +-| 03 | 0x00 blink +-| | 0x01 lights continuously +----------------------------------------------------------------------------- +-| 04 | blink interval +-| | 0x00 fastest (looks like LED lights continuously) +-| | 0xff slowest +----------------------------------------------------------------------------- +-| 05-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | IR CONTROL REQUEST PACKET +-============================================================================ +-| 00 | 0x16 LED and IR control command +----------------------------------------------------------------------------- +-| 01 | 0x02 (IR) +----------------------------------------------------------------------------- +-| 03 | 0x00 IR disabled +-| | 0x01 IR enabled +----------------------------------------------------------------------------- +-| 04-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | STREAMING CONTROL REQUEST PACKET +-============================================================================ +-| 00 | 0x12 streaming control command +----------------------------------------------------------------------------- +-| 01 | 0x00 streaming disabled +-| | 0x01 streaming enabled +----------------------------------------------------------------------------- +-| 02 | 0x00 +----------------------------------------------------------------------------- +-| 03-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | REMOTE CONTROL REQUEST PACKET +-============================================================================ +-| 00 | 0x41 remote control command +----------------------------------------------------------------------------- +-| 01-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | REMOTE CONTROL REPLY PACKET +-============================================================================ +-| 00 | 0x00 code not received +-| | 0x01 code received +----------------------------------------------------------------------------- +-| 01 | remote control code +----------------------------------------------------------------------------- +-| 02-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | GET HARDWARE INFO REQUEST PACKET +-============================================================================ +-| 00 | 0x19 get hardware info command +----------------------------------------------------------------------------- +-| 01-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | GET HARDWARE INFO REPLY PACKET +-============================================================================ +-| 00 | hardware id +----------------------------------------------------------------------------- +-| 01-02 | firmware version +----------------------------------------------------------------------------- +-| 03-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-============================================================================ +-| 00-63 | SMART CARD READER PACKET +-============================================================================ +-| 00 | 0x34 smart card reader command +----------------------------------------------------------------------------- +-| xx | +----------------------------------------------------------------------------- +-| xx-59 | don't care +----------------------------------------------------------------------------- +-| 60 | packet sequence number +----------------------------------------------------------------------------- +-| 61-63 | don't care +----------------------------------------------------------------------------- +- +-*/ +diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c +deleted file mode 100644 +index 16210c0..0000000 +--- a/drivers/media/dvb/dvb-usb/au6610.c ++++ /dev/null +@@ -1,252 +0,0 @@ +-/* +- * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. +- * +- * Copyright (C) 2006 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "au6610.h" +-#include "zl10353.h" +-#include "qt1010.h" +- +-/* debug */ +-static int dvb_usb_au6610_debug; +-module_param_named(debug, dvb_usb_au6610_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- int ret; +- u16 index; +- u8 *usb_buf; +- +- /* +- * allocate enough for all known requests, +- * read returns 5 and write 6 bytes +- */ +- usb_buf = kmalloc(6, GFP_KERNEL); +- if (!usb_buf) +- return -ENOMEM; +- +- switch (wlen) { +- case 1: +- index = wbuf[0] << 8; +- break; +- case 2: +- index = wbuf[0] << 8; +- index += wbuf[1]; +- break; +- default: +- warn("wlen = %x, aborting.", wlen); +- ret = -EINVAL; +- goto error; +- } +- +- ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, +- USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, +- usb_buf, 6, AU6610_USB_TIMEOUT); +- if (ret < 0) +- goto error; +- +- switch (operation) { +- case AU6610_REQ_I2C_READ: +- case AU6610_REQ_USB_READ: +- /* requested value is always 5th byte in buffer */ +- rbuf[0] = usb_buf[4]; +- } +-error: +- kfree(usb_buf); +- return ret; +-} +- +-static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u8 request; +- u8 wo = (rbuf == NULL || rlen == 0); /* write-only */ +- +- if (wo) { +- request = AU6610_REQ_I2C_WRITE; +- } else { /* rw */ +- request = AU6610_REQ_I2C_READ; +- } +- +- return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen); +-} +- +- +-/* I2C */ +-static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { +- if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, msg[i+1].buf, +- msg[i+1].len) < 0) +- break; +- i++; +- } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, NULL, 0) < 0) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +- +-static u32 au6610_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm au6610_i2c_algo = { +- .master_xfer = au6610_i2c_xfer, +- .functionality = au6610_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static struct zl10353_config au6610_zl10353_config = { +- .demod_address = 0x0f, +- .no_tuner = 1, +- .parallel_ts = 1, +-}; +- +-static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- return 0; +-} +- +-static struct qt1010_config au6610_qt1010_config = { +- .i2c_address = 0x62 +-}; +- +-static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- return dvb_attach(qt1010_attach, +- adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &au6610_qt1010_config) == NULL ? -ENODEV : 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties au6610_properties; +- +-static int au6610_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- struct usb_host_interface *alt; +- int ret; +- +- if (intf->num_altsetting < AU6610_ALTSETTING_COUNT) +- return -ENODEV; +- +- ret = dvb_usb_device_init(intf, &au6610_properties, THIS_MODULE, &d, +- adapter_nr); +- if (ret == 0) { +- alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING); +- +- if (alt == NULL) { +- deb_info("%s: no alt found!\n", __func__); +- return -ENODEV; +- } +- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, +- alt->desc.bAlternateSetting); +- } +- +- return ret; +-} +- +-static struct usb_device_id au6610_table [] = { +- { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, au6610_table); +- +-static struct dvb_usb_device_properties au6610_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = au6610_zl10353_frontend_attach, +- .tuner_attach = au6610_qt1010_tuner_attach, +- +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x82, +- .u = { +- .isoc = { +- .framesperurb = 40, +- .framesize = 942, +- .interval = 1, +- } +- } +- }, +- }}, +- } +- }, +- +- .i2c_algo = &au6610_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { +- .name = "Sigmatek DVB-110 DVB-T USB2.0", +- .cold_ids = {NULL}, +- .warm_ids = {&au6610_table[0], NULL}, +- }, +- } +-}; +- +-static struct usb_driver au6610_driver = { +- .name = "dvb_usb_au6610", +- .probe = au6610_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = au6610_table, +-}; +- +-module_usb_driver(au6610_driver); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0"); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/au6610.h b/drivers/media/dvb/dvb-usb/au6610.h +deleted file mode 100644 +index 7849abe..0000000 +--- a/drivers/media/dvb/dvb-usb/au6610.h ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* +- * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0. +- * +- * Copyright (C) 2006 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _DVB_USB_AU6610_H_ +-#define _DVB_USB_AU6610_H_ +- +-#define DVB_USB_LOG_PREFIX "au6610" +-#include "dvb-usb.h" +- +-#define deb_info(args...) dprintk(dvb_usb_au6610_debug, 0x01, args) +- +-#define AU6610_REQ_I2C_WRITE 0x14 +-#define AU6610_REQ_I2C_READ 0x13 +-#define AU6610_REQ_USB_WRITE 0x16 +-#define AU6610_REQ_USB_READ 0x15 +- +-#define AU6610_USB_TIMEOUT 1000 +- +-#define AU6610_ALTSETTING_COUNT 6 +-#define AU6610_ALTSETTING 5 +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c +deleted file mode 100644 +index 5e45ae6..0000000 +--- a/drivers/media/dvb/dvb-usb/az6027.c ++++ /dev/null +@@ -1,1182 +0,0 @@ +-/* DVB USB compliant Linux driver for the AZUREWAVE DVB-S/S2 USB2.0 (AZ6027) +- * receiver. +- * +- * Copyright (C) 2009 Adams.Xu +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "az6027.h" +- +-#include "stb0899_drv.h" +-#include "stb0899_reg.h" +-#include "stb0899_cfg.h" +- +-#include "stb6100.h" +-#include "stb6100_cfg.h" +-#include "dvb_ca_en50221.h" +- +-int dvb_usb_az6027_debug; +-module_param_named(debug, dvb_usb_az6027_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct az6027_device_state { +- struct dvb_ca_en50221 ca; +- struct mutex ca_mutex; +- u8 power_state; +-}; +- +-static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { +- +- /* 0x0000000b, SYSREG */ +- { STB0899_DEV_ID , 0x30 }, +- { STB0899_DISCNTRL1 , 0x32 }, +- { STB0899_DISCNTRL2 , 0x80 }, +- { STB0899_DISRX_ST0 , 0x04 }, +- { STB0899_DISRX_ST1 , 0x00 }, +- { STB0899_DISPARITY , 0x00 }, +- { STB0899_DISSTATUS , 0x20 }, +- { STB0899_DISF22 , 0x99 }, +- { STB0899_DISF22RX , 0xa8 }, +- /* SYSREG ? */ +- { STB0899_ACRPRESC , 0x11 }, +- { STB0899_ACRDIV1 , 0x0a }, +- { STB0899_ACRDIV2 , 0x05 }, +- { STB0899_DACR1 , 0x00 }, +- { STB0899_DACR2 , 0x00 }, +- { STB0899_OUTCFG , 0x00 }, +- { STB0899_MODECFG , 0x00 }, +- { STB0899_IRQSTATUS_3 , 0xfe }, +- { STB0899_IRQSTATUS_2 , 0x03 }, +- { STB0899_IRQSTATUS_1 , 0x7c }, +- { STB0899_IRQSTATUS_0 , 0xf4 }, +- { STB0899_IRQMSK_3 , 0xf3 }, +- { STB0899_IRQMSK_2 , 0xfc }, +- { STB0899_IRQMSK_1 , 0xff }, +- { STB0899_IRQMSK_0 , 0xff }, +- { STB0899_IRQCFG , 0x00 }, +- { STB0899_I2CCFG , 0x88 }, +- { STB0899_I2CRPT , 0x58 }, +- { STB0899_IOPVALUE5 , 0x00 }, +- { STB0899_IOPVALUE4 , 0x33 }, +- { STB0899_IOPVALUE3 , 0x6d }, +- { STB0899_IOPVALUE2 , 0x90 }, +- { STB0899_IOPVALUE1 , 0x60 }, +- { STB0899_IOPVALUE0 , 0x00 }, +- { STB0899_GPIO00CFG , 0x82 }, +- { STB0899_GPIO01CFG , 0x82 }, +- { STB0899_GPIO02CFG , 0x82 }, +- { STB0899_GPIO03CFG , 0x82 }, +- { STB0899_GPIO04CFG , 0x82 }, +- { STB0899_GPIO05CFG , 0x82 }, +- { STB0899_GPIO06CFG , 0x82 }, +- { STB0899_GPIO07CFG , 0x82 }, +- { STB0899_GPIO08CFG , 0x82 }, +- { STB0899_GPIO09CFG , 0x82 }, +- { STB0899_GPIO10CFG , 0x82 }, +- { STB0899_GPIO11CFG , 0x82 }, +- { STB0899_GPIO12CFG , 0x82 }, +- { STB0899_GPIO13CFG , 0x82 }, +- { STB0899_GPIO14CFG , 0x82 }, +- { STB0899_GPIO15CFG , 0x82 }, +- { STB0899_GPIO16CFG , 0x82 }, +- { STB0899_GPIO17CFG , 0x82 }, +- { STB0899_GPIO18CFG , 0x82 }, +- { STB0899_GPIO19CFG , 0x82 }, +- { STB0899_GPIO20CFG , 0x82 }, +- { STB0899_SDATCFG , 0xb8 }, +- { STB0899_SCLTCFG , 0xba }, +- { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ +- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ +- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ +- { STB0899_DIRCLKCFG , 0x82 }, +- { STB0899_CLKOUT27CFG , 0x7e }, +- { STB0899_STDBYCFG , 0x82 }, +- { STB0899_CS0CFG , 0x82 }, +- { STB0899_CS1CFG , 0x82 }, +- { STB0899_DISEQCOCFG , 0x20 }, +- { STB0899_GPIO32CFG , 0x82 }, +- { STB0899_GPIO33CFG , 0x82 }, +- { STB0899_GPIO34CFG , 0x82 }, +- { STB0899_GPIO35CFG , 0x82 }, +- { STB0899_GPIO36CFG , 0x82 }, +- { STB0899_GPIO37CFG , 0x82 }, +- { STB0899_GPIO38CFG , 0x82 }, +- { STB0899_GPIO39CFG , 0x82 }, +- { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ +- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ +- { STB0899_FILTCTRL , 0x00 }, +- { STB0899_SYSCTRL , 0x01 }, +- { STB0899_STOPCLK1 , 0x20 }, +- { STB0899_STOPCLK2 , 0x00 }, +- { STB0899_INTBUFSTATUS , 0x00 }, +- { STB0899_INTBUFCTRL , 0x0a }, +- { 0xffff , 0xff }, +-}; +- +-static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { +- { STB0899_DEMOD , 0x00 }, +- { STB0899_RCOMPC , 0xc9 }, +- { STB0899_AGC1CN , 0x01 }, +- { STB0899_AGC1REF , 0x10 }, +- { STB0899_RTC , 0x23 }, +- { STB0899_TMGCFG , 0x4e }, +- { STB0899_AGC2REF , 0x34 }, +- { STB0899_TLSR , 0x84 }, +- { STB0899_CFD , 0xf7 }, +- { STB0899_ACLC , 0x87 }, +- { STB0899_BCLC , 0x94 }, +- { STB0899_EQON , 0x41 }, +- { STB0899_LDT , 0xf1 }, +- { STB0899_LDT2 , 0xe3 }, +- { STB0899_EQUALREF , 0xb4 }, +- { STB0899_TMGRAMP , 0x10 }, +- { STB0899_TMGTHD , 0x30 }, +- { STB0899_IDCCOMP , 0xfd }, +- { STB0899_QDCCOMP , 0xff }, +- { STB0899_POWERI , 0x0c }, +- { STB0899_POWERQ , 0x0f }, +- { STB0899_RCOMP , 0x6c }, +- { STB0899_AGCIQIN , 0x80 }, +- { STB0899_AGC2I1 , 0x06 }, +- { STB0899_AGC2I2 , 0x00 }, +- { STB0899_TLIR , 0x30 }, +- { STB0899_RTF , 0x7f }, +- { STB0899_DSTATUS , 0x00 }, +- { STB0899_LDI , 0xbc }, +- { STB0899_CFRM , 0xea }, +- { STB0899_CFRL , 0x31 }, +- { STB0899_NIRM , 0x2b }, +- { STB0899_NIRL , 0x80 }, +- { STB0899_ISYMB , 0x1d }, +- { STB0899_QSYMB , 0xa6 }, +- { STB0899_SFRH , 0x2f }, +- { STB0899_SFRM , 0x68 }, +- { STB0899_SFRL , 0x40 }, +- { STB0899_SFRUPH , 0x2f }, +- { STB0899_SFRUPM , 0x68 }, +- { STB0899_SFRUPL , 0x40 }, +- { STB0899_EQUAI1 , 0x02 }, +- { STB0899_EQUAQ1 , 0xff }, +- { STB0899_EQUAI2 , 0x04 }, +- { STB0899_EQUAQ2 , 0x05 }, +- { STB0899_EQUAI3 , 0x02 }, +- { STB0899_EQUAQ3 , 0xfd }, +- { STB0899_EQUAI4 , 0x03 }, +- { STB0899_EQUAQ4 , 0x07 }, +- { STB0899_EQUAI5 , 0x08 }, +- { STB0899_EQUAQ5 , 0xf5 }, +- { STB0899_DSTATUS2 , 0x00 }, +- { STB0899_VSTATUS , 0x00 }, +- { STB0899_VERROR , 0x86 }, +- { STB0899_IQSWAP , 0x2a }, +- { STB0899_ECNT1M , 0x00 }, +- { STB0899_ECNT1L , 0x00 }, +- { STB0899_ECNT2M , 0x00 }, +- { STB0899_ECNT2L , 0x00 }, +- { STB0899_ECNT3M , 0x0a }, +- { STB0899_ECNT3L , 0xad }, +- { STB0899_FECAUTO1 , 0x06 }, +- { STB0899_FECM , 0x01 }, +- { STB0899_VTH12 , 0xb0 }, +- { STB0899_VTH23 , 0x7a }, +- { STB0899_VTH34 , 0x58 }, +- { STB0899_VTH56 , 0x38 }, +- { STB0899_VTH67 , 0x34 }, +- { STB0899_VTH78 , 0x24 }, +- { STB0899_PRVIT , 0xff }, +- { STB0899_VITSYNC , 0x19 }, +- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ +- { STB0899_TSULC , 0x42 }, +- { STB0899_RSLLC , 0x41 }, +- { STB0899_TSLPL , 0x12 }, +- { STB0899_TSCFGH , 0x0c }, +- { STB0899_TSCFGM , 0x00 }, +- { STB0899_TSCFGL , 0x00 }, +- { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ +- { STB0899_RSSYNCDEL , 0x00 }, +- { STB0899_TSINHDELH , 0x02 }, +- { STB0899_TSINHDELM , 0x00 }, +- { STB0899_TSINHDELL , 0x00 }, +- { STB0899_TSLLSTKM , 0x1b }, +- { STB0899_TSLLSTKL , 0xb3 }, +- { STB0899_TSULSTKM , 0x00 }, +- { STB0899_TSULSTKL , 0x00 }, +- { STB0899_PCKLENUL , 0xbc }, +- { STB0899_PCKLENLL , 0xcc }, +- { STB0899_RSPCKLEN , 0xbd }, +- { STB0899_TSSTATUS , 0x90 }, +- { STB0899_ERRCTRL1 , 0xb6 }, +- { STB0899_ERRCTRL2 , 0x95 }, +- { STB0899_ERRCTRL3 , 0x8d }, +- { STB0899_DMONMSK1 , 0x27 }, +- { STB0899_DMONMSK0 , 0x03 }, +- { STB0899_DEMAPVIT , 0x5c }, +- { STB0899_PLPARM , 0x19 }, +- { STB0899_PDELCTRL , 0x48 }, +- { STB0899_PDELCTRL2 , 0x00 }, +- { STB0899_BBHCTRL1 , 0x00 }, +- { STB0899_BBHCTRL2 , 0x00 }, +- { STB0899_HYSTTHRESH , 0x77 }, +- { STB0899_MATCSTM , 0x00 }, +- { STB0899_MATCSTL , 0x00 }, +- { STB0899_UPLCSTM , 0x00 }, +- { STB0899_UPLCSTL , 0x00 }, +- { STB0899_DFLCSTM , 0x00 }, +- { STB0899_DFLCSTL , 0x00 }, +- { STB0899_SYNCCST , 0x00 }, +- { STB0899_SYNCDCSTM , 0x00 }, +- { STB0899_SYNCDCSTL , 0x00 }, +- { STB0899_ISI_ENTRY , 0x00 }, +- { STB0899_ISI_BIT_EN , 0x00 }, +- { STB0899_MATSTRM , 0xf0 }, +- { STB0899_MATSTRL , 0x02 }, +- { STB0899_UPLSTRM , 0x45 }, +- { STB0899_UPLSTRL , 0x60 }, +- { STB0899_DFLSTRM , 0xe3 }, +- { STB0899_DFLSTRL , 0x00 }, +- { STB0899_SYNCSTR , 0x47 }, +- { STB0899_SYNCDSTRM , 0x05 }, +- { STB0899_SYNCDSTRL , 0x18 }, +- { STB0899_CFGPDELSTATUS1 , 0x19 }, +- { STB0899_CFGPDELSTATUS2 , 0x2b }, +- { STB0899_BBFERRORM , 0x00 }, +- { STB0899_BBFERRORL , 0x01 }, +- { STB0899_UPKTERRORM , 0x00 }, +- { STB0899_UPKTERRORL , 0x00 }, +- { 0xffff , 0xff }, +-}; +- +- +- +-struct stb0899_config az6027_stb0899_config = { +- .init_dev = az6027_stb0899_s1_init_1, +- .init_s2_demod = stb0899_s2_init_2, +- .init_s1_demod = az6027_stb0899_s1_init_3, +- .init_s2_fec = stb0899_s2_init_4, +- .init_tst = stb0899_s1_init_5, +- +- .demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */ +- +- .xtal_freq = 27000000, +- .inversion = IQ_SWAP_ON, /* 1 */ +- +- .lo_clk = 76500000, +- .hi_clk = 99000000, +- +- .esno_ave = STB0899_DVBS2_ESNO_AVE, +- .esno_quant = STB0899_DVBS2_ESNO_QUANT, +- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, +- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, +- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, +- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, +- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, +- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, +- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, +- +- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, +- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, +- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, +- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, +- +- .tuner_get_frequency = stb6100_get_frequency, +- .tuner_set_frequency = stb6100_set_frequency, +- .tuner_set_bandwidth = stb6100_set_bandwidth, +- .tuner_get_bandwidth = stb6100_get_bandwidth, +- .tuner_set_rfsiggain = NULL, +-}; +- +-struct stb6100_config az6027_stb6100_config = { +- .tuner_address = 0xc0, +- .refclock = 27000000, +-}; +- +- +-/* check for mutex FIXME */ +-int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +-{ +- int ret = -1; +- if (mutex_lock_interruptible(&d->usb_mutex)) +- return -EAGAIN; +- +- ret = usb_control_msg(d->udev, +- usb_rcvctrlpipe(d->udev, 0), +- req, +- USB_TYPE_VENDOR | USB_DIR_IN, +- value, +- index, +- b, +- blen, +- 2000); +- +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EIO; +- } else +- ret = 0; +- +- deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); +- debug_dump(b, blen, deb_xfer); +- +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +- +-static int az6027_usb_out_op(struct dvb_usb_device *d, +- u8 req, +- u16 value, +- u16 index, +- u8 *b, +- int blen) +-{ +- int ret; +- +- deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); +- debug_dump(b, blen, deb_xfer); +- +- if (mutex_lock_interruptible(&d->usb_mutex)) +- return -EAGAIN; +- +- ret = usb_control_msg(d->udev, +- usb_sndctrlpipe(d->udev, 0), +- req, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- value, +- index, +- b, +- blen, +- 2000); +- +- if (ret != blen) { +- warn("usb out operation failed. (%d)", ret); +- mutex_unlock(&d->usb_mutex); +- return -EIO; +- } else{ +- mutex_unlock(&d->usb_mutex); +- return 0; +- } +-} +- +-static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- deb_info("%s %d", __func__, onoff); +- +- req = 0xBC; +- value = onoff; +- index = 0; +- blen = 0; +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- warn("usb out operation failed. (%d)", ret); +- +- return ret; +-} +- +-/* keys for the enclosed remote control */ +-static struct rc_map_table rc_map_az6027_table[] = { +- { 0x01, KEY_1 }, +- { 0x02, KEY_2 }, +-}; +- +-/* remote control stuff (does not work with my box) */ +-static int az6027_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- return 0; +-} +- +-/* +-int az6027_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 v = onoff; +- return az6027_usb_out_op(d,0xBC,v,3,NULL,1); +-} +-*/ +- +-static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, +- int slot, +- int address) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- u8 *b; +- +- if (slot != 0) +- return -EINVAL; +- +- b = kmalloc(12, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- mutex_lock(&state->ca_mutex); +- +- req = 0xC1; +- value = address; +- index = 0; +- blen = 1; +- +- ret = az6027_usb_in_op(d, req, value, index, b, blen); +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EINVAL; +- } else { +- ret = b[0]; +- } +- +- mutex_unlock(&state->ca_mutex); +- kfree(b); +- return ret; +-} +- +-static int az6027_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, +- int slot, +- int address, +- u8 value) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret; +- u8 req; +- u16 value1; +- u16 index; +- int blen; +- +- deb_info("%s %d", __func__, slot); +- if (slot != 0) +- return -EINVAL; +- +- mutex_lock(&state->ca_mutex); +- req = 0xC2; +- value1 = address; +- index = value; +- blen = 0; +- +- ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); +- if (ret != 0) +- warn("usb out operation failed. (%d)", ret); +- +- mutex_unlock(&state->ca_mutex); +- return ret; +-} +- +-static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, +- int slot, +- u8 address) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- u8 *b; +- +- if (slot != 0) +- return -EINVAL; +- +- b = kmalloc(12, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- mutex_lock(&state->ca_mutex); +- +- req = 0xC3; +- value = address; +- index = 0; +- blen = 2; +- +- ret = az6027_usb_in_op(d, req, value, index, b, blen); +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EINVAL; +- } else { +- if (b[0] == 0) +- warn("Read CI IO error"); +- +- ret = b[1]; +- deb_info("read cam data = %x from 0x%x", b[1], value); +- } +- +- mutex_unlock(&state->ca_mutex); +- kfree(b); +- return ret; +-} +- +-static int az6027_ci_write_cam_control(struct dvb_ca_en50221 *ca, +- int slot, +- u8 address, +- u8 value) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret; +- u8 req; +- u16 value1; +- u16 index; +- int blen; +- +- if (slot != 0) +- return -EINVAL; +- +- mutex_lock(&state->ca_mutex); +- req = 0xC4; +- value1 = address; +- index = value; +- blen = 0; +- +- ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); +- if (ret != 0) { +- warn("usb out operation failed. (%d)", ret); +- goto failed; +- } +- +-failed: +- mutex_unlock(&state->ca_mutex); +- return ret; +-} +- +-static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- u8 *b; +- +- b = kmalloc(12, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- req = 0xC8; +- value = 0; +- index = 0; +- blen = 1; +- +- ret = az6027_usb_in_op(d, req, value, index, b, blen); +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EIO; +- } else{ +- ret = b[0]; +- } +- kfree(b); +- return ret; +-} +- +-static int az6027_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret, i; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- mutex_lock(&state->ca_mutex); +- +- req = 0xC6; +- value = 1; +- index = 0; +- blen = 0; +- +- ret = az6027_usb_out_op(d, req, value, index, NULL, blen); +- if (ret != 0) { +- warn("usb out operation failed. (%d)", ret); +- goto failed; +- } +- +- msleep(500); +- req = 0xC6; +- value = 0; +- index = 0; +- blen = 0; +- +- ret = az6027_usb_out_op(d, req, value, index, NULL, blen); +- if (ret != 0) { +- warn("usb out operation failed. (%d)", ret); +- goto failed; +- } +- +- for (i = 0; i < 15; i++) { +- msleep(100); +- +- if (CI_CamReady(ca, slot)) { +- deb_info("CAM Ready"); +- break; +- } +- } +- msleep(5000); +- +-failed: +- mutex_unlock(&state->ca_mutex); +- return ret; +-} +- +-static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +-{ +- return 0; +-} +- +-static int az6027_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- deb_info("%s", __func__); +- mutex_lock(&state->ca_mutex); +- req = 0xC7; +- value = 1; +- index = 0; +- blen = 0; +- +- ret = az6027_usb_out_op(d, req, value, index, NULL, blen); +- if (ret != 0) { +- warn("usb out operation failed. (%d)", ret); +- goto failed; +- } +- +-failed: +- mutex_unlock(&state->ca_mutex); +- return ret; +-} +- +-static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- u8 *b; +- +- b = kmalloc(12, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- mutex_lock(&state->ca_mutex); +- +- req = 0xC5; +- value = 0; +- index = 0; +- blen = 1; +- +- ret = az6027_usb_in_op(d, req, value, index, b, blen); +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EIO; +- } else +- ret = 0; +- +- if (!ret && b[0] == 1) { +- ret = DVB_CA_EN50221_POLL_CAM_PRESENT | +- DVB_CA_EN50221_POLL_CAM_READY; +- } +- +- mutex_unlock(&state->ca_mutex); +- kfree(b); +- return ret; +-} +- +- +-static void az6027_ci_uninit(struct dvb_usb_device *d) +-{ +- struct az6027_device_state *state; +- +- deb_info("%s", __func__); +- +- if (NULL == d) +- return; +- +- state = (struct az6027_device_state *)d->priv; +- if (NULL == state) +- return; +- +- if (NULL == state->ca.data) +- return; +- +- dvb_ca_en50221_release(&state->ca); +- +- memset(&state->ca, 0, sizeof(state->ca)); +-} +- +- +-static int az6027_ci_init(struct dvb_usb_adapter *a) +-{ +- struct dvb_usb_device *d = a->dev; +- struct az6027_device_state *state = (struct az6027_device_state *)d->priv; +- int ret; +- +- deb_info("%s", __func__); +- +- mutex_init(&state->ca_mutex); +- +- state->ca.owner = THIS_MODULE; +- state->ca.read_attribute_mem = az6027_ci_read_attribute_mem; +- state->ca.write_attribute_mem = az6027_ci_write_attribute_mem; +- state->ca.read_cam_control = az6027_ci_read_cam_control; +- state->ca.write_cam_control = az6027_ci_write_cam_control; +- state->ca.slot_reset = az6027_ci_slot_reset; +- state->ca.slot_shutdown = az6027_ci_slot_shutdown; +- state->ca.slot_ts_enable = az6027_ci_slot_ts_enable; +- state->ca.poll_slot_status = az6027_ci_poll_slot_status; +- state->ca.data = d; +- +- ret = dvb_ca_en50221_init(&a->dvb_adap, +- &state->ca, +- 0, /* flags */ +- 1);/* n_slots */ +- if (ret != 0) { +- err("Cannot initialize CI: Error %d.", ret); +- memset(&state->ca, 0, sizeof(state->ca)); +- return ret; +- } +- +- deb_info("CI initialized."); +- +- return 0; +-} +- +-/* +-static int az6027_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) +-{ +- az6027_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6); +- return 0; +-} +-*/ +- +-static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- +- u8 buf; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- struct i2c_msg i2c_msg = { +- .addr = 0x99, +- .flags = 0, +- .buf = &buf, +- .len = 1 +- }; +- +- /* +- * 2 --18v +- * 1 --13v +- * 0 --off +- */ +- switch (voltage) { +- case SEC_VOLTAGE_13: +- buf = 1; +- i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); +- break; +- +- case SEC_VOLTAGE_18: +- buf = 2; +- i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); +- break; +- +- case SEC_VOLTAGE_OFF: +- buf = 0; +- i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); +- break; +- +- default: +- return -EINVAL; +- } +- return 0; +-} +- +- +-static int az6027_frontend_poweron(struct dvb_usb_adapter *adap) +-{ +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- req = 0xBC; +- value = 1; /* power on */ +- index = 3; +- blen = 0; +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- return -EIO; +- +- return 0; +-} +-static int az6027_frontend_reset(struct dvb_usb_adapter *adap) +-{ +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- /* reset demodulator */ +- req = 0xC0; +- value = 1; /* high */ +- index = 3; +- blen = 0; +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- return -EIO; +- +- req = 0xC0; +- value = 0; /* low */ +- index = 3; +- blen = 0; +- msleep_interruptible(200); +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- return -EIO; +- +- msleep_interruptible(200); +- +- req = 0xC0; +- value = 1; /*high */ +- index = 3; +- blen = 0; +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- return -EIO; +- +- msleep_interruptible(200); +- return 0; +-} +- +-static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret; +- u8 req; +- u16 value; +- u16 index; +- int blen; +- +- /* TS passthrough */ +- req = 0xC7; +- value = onoff; +- index = 0; +- blen = 0; +- +- ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); +- if (ret != 0) +- return -EIO; +- +- return 0; +-} +- +-static int az6027_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- +- az6027_frontend_poweron(adap); +- az6027_frontend_reset(adap); +- +- deb_info("adap = %p, dev = %p\n", adap, adap->dev); +- adap->fe_adap[0].fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap); +- +- if (adap->fe_adap[0].fe) { +- deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address); +- if (stb6100_attach(adap->fe_adap[0].fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) { +- deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address); +- adap->fe_adap[0].fe->ops.set_voltage = az6027_set_voltage; +- az6027_ci_init(adap); +- } else { +- adap->fe_adap[0].fe = NULL; +- } +- } else +- warn("no front-end attached\n"); +- +- az6027_frontend_tsbypass(adap, 0); +- +- return 0; +-} +- +-static struct dvb_usb_device_properties az6027_properties; +- +-static void az6027_usb_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- az6027_ci_uninit(d); +- dvb_usb_device_exit(intf); +-} +- +- +-static int az6027_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, +- &az6027_properties, +- THIS_MODULE, +- NULL, +- adapter_nr); +-} +- +-/* I2C */ +-static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i = 0, j = 0, len = 0; +- u16 index; +- u16 value; +- int length; +- u8 req; +- u8 *data; +- +- data = kmalloc(256, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) { +- kfree(data); +- return -EAGAIN; +- } +- +- if (num > 2) +- warn("more than 2 i2c messages at a time is not handled yet. TODO."); +- +- for (i = 0; i < num; i++) { +- +- if (msg[i].addr == 0x99) { +- req = 0xBE; +- index = 0; +- value = msg[i].buf[0] & 0x00ff; +- length = 1; +- az6027_usb_out_op(d, req, value, index, data, length); +- } +- +- if (msg[i].addr == 0xd0) { +- /* write/read request */ +- if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { +- req = 0xB9; +- index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); +- value = msg[i].addr + (msg[i].len << 8); +- length = msg[i + 1].len + 6; +- az6027_usb_in_op(d, req, value, index, data, length); +- len = msg[i + 1].len; +- for (j = 0; j < len; j++) +- msg[i + 1].buf[j] = data[j + 5]; +- +- i++; +- } else { +- +- /* demod 16bit addr */ +- req = 0xBD; +- index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); +- value = msg[i].addr + (2 << 8); +- length = msg[i].len - 2; +- len = msg[i].len - 2; +- for (j = 0; j < len; j++) +- data[j] = msg[i].buf[j + 2]; +- az6027_usb_out_op(d, req, value, index, data, length); +- } +- } +- +- if (msg[i].addr == 0xc0) { +- if (msg[i].flags & I2C_M_RD) { +- +- req = 0xB9; +- index = 0x0; +- value = msg[i].addr; +- length = msg[i].len + 6; +- az6027_usb_in_op(d, req, value, index, data, length); +- len = msg[i].len; +- for (j = 0; j < len; j++) +- msg[i].buf[j] = data[j + 5]; +- +- } else { +- +- req = 0xBD; +- index = msg[i].buf[0] & 0x00FF; +- value = msg[i].addr + (1 << 8); +- length = msg[i].len - 1; +- len = msg[i].len - 1; +- +- for (j = 0; j < len; j++) +- data[j] = msg[i].buf[j + 1]; +- +- az6027_usb_out_op(d, req, value, index, data, length); +- } +- } +- } +- mutex_unlock(&d->i2c_mutex); +- kfree(data); +- +- return i; +-} +- +- +-static u32 az6027_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm az6027_i2c_algo = { +- .master_xfer = az6027_i2c_xfer, +- .functionality = az6027_i2c_func, +-}; +- +-int az6027_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- u8 *b; +- s16 ret; +- +- b = kmalloc(16, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- ret = usb_control_msg(udev, +- usb_rcvctrlpipe(udev, 0), +- 0xb7, +- USB_TYPE_VENDOR | USB_DIR_IN, +- 6, +- 0, +- b, +- 6, +- USB_CTRL_GET_TIMEOUT); +- +- *cold = ret <= 0; +- kfree(b); +- deb_info("cold: %d\n", *cold); +- return 0; +-} +- +- +-static struct usb_device_id az6027_usb_table[] = { +- { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_AZ6027) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V1) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI_V2) }, +- { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V1) }, +- { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI_V2) }, +- { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_SAT) }, +- { }, +-}; +- +-MODULE_DEVICE_TABLE(usb, az6027_usb_table); +- +-static struct dvb_usb_device_properties az6027_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-az6027-03.fw", +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct az6027_device_state), +- .identify_state = az6027_identify_state, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = az6027_streaming_ctrl, +- .frontend_attach = az6027_frontend_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +-/* +- .power_ctrl = az6027_power_ctrl, +- .read_mac_address = az6027_read_mac_addr, +- */ +- .rc.legacy = { +- .rc_map_table = rc_map_az6027_table, +- .rc_map_size = ARRAY_SIZE(rc_map_az6027_table), +- .rc_interval = 400, +- .rc_query = az6027_rc_query, +- }, +- +- .i2c_algo = &az6027_i2c_algo, +- +- .num_device_descs = 6, +- .devices = { +- { +- .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", +- .cold_ids = { &az6027_usb_table[0], NULL }, +- .warm_ids = { NULL }, +- }, { +- .name = "TERRATEC S7", +- .cold_ids = { &az6027_usb_table[1], NULL }, +- .warm_ids = { NULL }, +- }, { +- .name = "TERRATEC S7 MKII", +- .cold_ids = { &az6027_usb_table[2], NULL }, +- .warm_ids = { NULL }, +- }, { +- .name = "Technisat SkyStar USB 2 HD CI", +- .cold_ids = { &az6027_usb_table[3], NULL }, +- .warm_ids = { NULL }, +- }, { +- .name = "Technisat SkyStar USB 2 HD CI", +- .cold_ids = { &az6027_usb_table[4], NULL }, +- .warm_ids = { NULL }, +- }, { +- .name = "Elgato EyeTV Sat", +- .cold_ids = { &az6027_usb_table[5], NULL }, +- .warm_ids = { NULL }, +- }, +- { NULL }, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver az6027_usb_driver = { +- .name = "dvb_usb_az6027", +- .probe = az6027_usb_probe, +- .disconnect = az6027_usb_disconnect, +- .id_table = az6027_usb_table, +-}; +- +-module_usb_driver(az6027_usb_driver); +- +-MODULE_AUTHOR("Adams Xu "); +-MODULE_DESCRIPTION("Driver for AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/az6027.h b/drivers/media/dvb/dvb-usb/az6027.h +deleted file mode 100644 +index f3afe17..0000000 +--- a/drivers/media/dvb/dvb-usb/az6027.h ++++ /dev/null +@@ -1,14 +0,0 @@ +-#ifndef _DVB_USB_VP6027_H_ +-#define _DVB_USB_VP6027_H_ +- +-#define DVB_USB_LOG_PREFIX "az6027" +-#include "dvb-usb.h" +- +- +-extern int dvb_usb_az6027_debug; +-#define deb_info(args...) dprintk(dvb_usb_az6027_debug, 0x01, args) +-#define deb_xfer(args...) dprintk(dvb_usb_az6027_debug, 0x02, args) +-#define deb_rc(args...) dprintk(dvb_usb_az6027_debug, 0x04, args) +-#define deb_fe(args...) dprintk(dvb_usb_az6027_debug, 0x08, args) +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c +deleted file mode 100644 +index fa63725..0000000 +--- a/drivers/media/dvb/dvb-usb/ce6230.c ++++ /dev/null +@@ -1,324 +0,0 @@ +-/* +- * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include "ce6230.h" +-#include "zl10353.h" +-#include "mxl5005s.h" +- +-/* debug */ +-static int dvb_usb_ce6230_debug; +-module_param_named(debug, dvb_usb_ce6230_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static struct zl10353_config ce6230_zl10353_config; +- +-static int ce6230_rw_udev(struct usb_device *udev, struct req_t *req) +-{ +- int ret; +- unsigned int pipe; +- u8 request; +- u8 requesttype; +- u16 value; +- u16 index; +- u8 *buf; +- +- request = req->cmd; +- value = req->value; +- index = req->index; +- +- switch (req->cmd) { +- case I2C_READ: +- case DEMOD_READ: +- case REG_READ: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); +- break; +- case I2C_WRITE: +- case DEMOD_WRITE: +- case REG_WRITE: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); +- break; +- default: +- err("unknown command:%02x", req->cmd); +- ret = -EPERM; +- goto error; +- } +- +- buf = kmalloc(req->data_len, GFP_KERNEL); +- if (!buf) { +- ret = -ENOMEM; +- goto error; +- } +- +- if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { +- /* write */ +- memcpy(buf, req->data, req->data_len); +- pipe = usb_sndctrlpipe(udev, 0); +- } else { +- /* read */ +- pipe = usb_rcvctrlpipe(udev, 0); +- } +- +- msleep(1); /* avoid I2C errors */ +- +- ret = usb_control_msg(udev, pipe, request, requesttype, value, index, +- buf, req->data_len, CE6230_USB_TIMEOUT); +- +- ce6230_debug_dump(request, requesttype, value, index, buf, +- req->data_len, deb_xfer); +- +- if (ret < 0) +- deb_info("%s: usb_control_msg failed:%d\n", __func__, ret); +- else +- ret = 0; +- +- /* read request, copy returned data to return buf */ +- if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) +- memcpy(req->data, buf, req->data_len); +- +- kfree(buf); +-error: +- return ret; +-} +- +-static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct req_t *req) +-{ +- return ce6230_rw_udev(d->udev, req); +-} +- +-/* I2C */ +-static int ce6230_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i = 0; +- struct req_t req; +- int ret = 0; +- memset(&req, 0, sizeof(req)); +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- while (i < num) { +- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { +- if (msg[i].addr == +- ce6230_zl10353_config.demod_address) { +- req.cmd = DEMOD_READ; +- req.value = msg[i].addr >> 1; +- req.index = msg[i].buf[0]; +- req.data_len = msg[i+1].len; +- req.data = &msg[i+1].buf[0]; +- ret = ce6230_ctrl_msg(d, &req); +- } else { +- err("i2c read not implemented"); +- ret = -EPERM; +- } +- i += 2; +- } else { +- if (msg[i].addr == +- ce6230_zl10353_config.demod_address) { +- req.cmd = DEMOD_WRITE; +- req.value = msg[i].addr >> 1; +- req.index = msg[i].buf[0]; +- req.data_len = msg[i].len-1; +- req.data = &msg[i].buf[1]; +- ret = ce6230_ctrl_msg(d, &req); +- } else { +- req.cmd = I2C_WRITE; +- req.value = 0x2000 + (msg[i].addr >> 1); +- req.index = 0x0000; +- req.data_len = msg[i].len; +- req.data = &msg[i].buf[0]; +- ret = ce6230_ctrl_msg(d, &req); +- } +- i += 1; +- } +- if (ret) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return ret ? ret : i; +-} +- +-static u32 ce6230_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm ce6230_i2c_algo = { +- .master_xfer = ce6230_i2c_xfer, +- .functionality = ce6230_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static struct zl10353_config ce6230_zl10353_config = { +- .demod_address = 0x1e, +- .adc_clock = 450000, +- .if2 = 45700, +- .no_tuner = 1, +- .parallel_ts = 1, +- .clock_ctl_1 = 0x34, +- .pll_0 = 0x0e, +-}; +- +-static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- deb_info("%s:\n", __func__); +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- return 0; +-} +- +-static struct mxl5005s_config ce6230_mxl5003s_config = { +- .i2c_address = 0xc6, +- .if_freq = IF_FREQ_4570000HZ, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_DEFAULT, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- int ret; +- deb_info("%s:\n", __func__); +- ret = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0; +- return ret; +-} +- +-static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- int ret; +- deb_info("%s: onoff:%d\n", __func__, onoff); +- +- /* InterfaceNumber 1 / AlternateSetting 0 idle +- InterfaceNumber 1 / AlternateSetting 1 streaming */ +- ret = usb_set_interface(d->udev, 1, onoff); +- if (ret) +- err("usb_set_interface failed with error:%d", ret); +- +- return ret; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties ce6230_properties; +- +-static int ce6230_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int ret = 0; +- struct dvb_usb_device *d = NULL; +- +- deb_info("%s: interface:%d\n", __func__, +- intf->cur_altsetting->desc.bInterfaceNumber); +- +- if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { +- ret = dvb_usb_device_init(intf, &ce6230_properties, THIS_MODULE, +- &d, adapter_nr); +- if (ret) +- err("init failed with error:%d\n", ret); +- } +- +- return ret; +-} +- +-static struct usb_device_id ce6230_table[] = { +- { USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500) }, +- { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A310) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, ce6230_table); +- +-static struct dvb_usb_device_properties ce6230_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .no_reconnect = 1, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = ce6230_zl10353_frontend_attach, +- .tuner_attach = ce6230_mxl5003s_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = (16*512), +- } +- } +- }, +- }}, +- } +- }, +- +- .power_ctrl = ce6230_power_ctrl, +- +- .i2c_algo = &ce6230_i2c_algo, +- +- .num_device_descs = 2, +- .devices = { +- { +- .name = "Intel CE9500 reference design", +- .cold_ids = {NULL}, +- .warm_ids = {&ce6230_table[0], NULL}, +- }, +- { +- .name = "AVerMedia A310 USB 2.0 DVB-T tuner", +- .cold_ids = {NULL}, +- .warm_ids = {&ce6230_table[1], NULL}, +- }, +- } +-}; +- +-static struct usb_driver ce6230_driver = { +- .name = "dvb_usb_ce6230", +- .probe = ce6230_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = ce6230_table, +-}; +- +-module_usb_driver(ce6230_driver); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Driver for Intel CE6230 DVB-T USB2.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/ce6230.h b/drivers/media/dvb/dvb-usb/ce6230.h +deleted file mode 100644 +index 97c4248..0000000 +--- a/drivers/media/dvb/dvb-usb/ce6230.h ++++ /dev/null +@@ -1,69 +0,0 @@ +-/* +- * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef _DVB_USB_CE6230_H_ +-#define _DVB_USB_CE6230_H_ +- +-#define DVB_USB_LOG_PREFIX "ce6230" +-#include "dvb-usb.h" +- +-#define deb_info(args...) dprintk(dvb_usb_ce6230_debug, 0x01, args) +-#define deb_rc(args...) dprintk(dvb_usb_ce6230_debug, 0x02, args) +-#define deb_xfer(args...) dprintk(dvb_usb_ce6230_debug, 0x04, args) +-#define deb_reg(args...) dprintk(dvb_usb_ce6230_debug, 0x08, args) +-#define deb_i2c(args...) dprintk(dvb_usb_ce6230_debug, 0x10, args) +-#define deb_fw(args...) dprintk(dvb_usb_ce6230_debug, 0x20, args) +- +-#define ce6230_debug_dump(r, t, v, i, b, l, func) { \ +- int loop_; \ +- func("%02x %02x %02x %02x %02x %02x %02x %02x", \ +- t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \ +- if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ +- func(" >>> "); \ +- else \ +- func(" <<< "); \ +- for (loop_ = 0; loop_ < l; loop_++) \ +- func("%02x ", b[loop_]); \ +- func("\n");\ +-} +- +-#define CE6230_USB_TIMEOUT 1000 +- +-struct req_t { +- u8 cmd; /* [1] */ +- u16 value; /* [2|3] */ +- u16 index; /* [4|5] */ +- u16 data_len; /* [6|7] */ +- u8 *data; +-}; +- +-enum ce6230_cmd { +- CONFIG_READ = 0xd0, /* rd 0 (unclear) */ +- UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */ +- I2C_READ = 0xd9, /* rd 9 (unclear) */ +- I2C_WRITE = 0xca, /* wr a */ +- DEMOD_READ = 0xdb, /* rd b */ +- DEMOD_WRITE = 0xcc, /* wr c */ +- REG_READ = 0xde, /* rd e */ +- REG_WRITE = 0xcf, /* wr f */ +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-core.c b/drivers/media/dvb/dvb-usb/cinergyT2-core.c +deleted file mode 100644 +index 0a98548..0000000 +--- a/drivers/media/dvb/dvb-usb/cinergyT2-core.c ++++ /dev/null +@@ -1,254 +0,0 @@ +-/* +- * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. +- * +- * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) +- * +- * Based on the dvb-usb-framework code and the +- * original Terratec Cinergy T2 driver by: +- * +- * Copyright (C) 2004 Daniel Mack and +- * Holger Waechtler +- * +- * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf +- * +- * 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 "cinergyT2.h" +- +- +-/* debug */ +-int dvb_usb_cinergyt2_debug; +- +-module_param_named(debug, dvb_usb_cinergyt2_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info, xfer=2, rc=4 " +- "(or-able))."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct cinergyt2_state { +- u8 rc_counter; +-}; +- +-/* We are missing a release hook with usb_device data */ +-static struct dvb_usb_device *cinergyt2_usb_device; +- +-static struct dvb_usb_device_properties cinergyt2_properties; +- +-static int cinergyt2_streaming_ctrl(struct dvb_usb_adapter *adap, int enable) +-{ +- char buf[] = { CINERGYT2_EP1_CONTROL_STREAM_TRANSFER, enable ? 1 : 0 }; +- char result[64]; +- return dvb_usb_generic_rw(adap->dev, buf, sizeof(buf), result, +- sizeof(result), 0); +-} +- +-static int cinergyt2_power_ctrl(struct dvb_usb_device *d, int enable) +-{ +- char buf[] = { CINERGYT2_EP1_SLEEP_MODE, enable ? 0 : 1 }; +- char state[3]; +- return dvb_usb_generic_rw(d, buf, sizeof(buf), state, sizeof(state), 0); +-} +- +-static int cinergyt2_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- char query[] = { CINERGYT2_EP1_GET_FIRMWARE_VERSION }; +- char state[3]; +- int ret; +- +- adap->fe_adap[0].fe = cinergyt2_fe_attach(adap->dev); +- +- ret = dvb_usb_generic_rw(adap->dev, query, sizeof(query), state, +- sizeof(state), 0); +- if (ret < 0) { +- deb_rc("cinergyt2_power_ctrl() Failed to retrieve sleep " +- "state info\n"); +- } +- +- /* Copy this pointer as we are gonna need it in the release phase */ +- cinergyt2_usb_device = adap->dev; +- +- return 0; +-} +- +-static struct rc_map_table rc_map_cinergyt2_table[] = { +- { 0x0401, KEY_POWER }, +- { 0x0402, KEY_1 }, +- { 0x0403, KEY_2 }, +- { 0x0404, KEY_3 }, +- { 0x0405, KEY_4 }, +- { 0x0406, KEY_5 }, +- { 0x0407, KEY_6 }, +- { 0x0408, KEY_7 }, +- { 0x0409, KEY_8 }, +- { 0x040a, KEY_9 }, +- { 0x040c, KEY_0 }, +- { 0x040b, KEY_VIDEO }, +- { 0x040d, KEY_REFRESH }, +- { 0x040e, KEY_SELECT }, +- { 0x040f, KEY_EPG }, +- { 0x0410, KEY_UP }, +- { 0x0414, KEY_DOWN }, +- { 0x0411, KEY_LEFT }, +- { 0x0413, KEY_RIGHT }, +- { 0x0412, KEY_OK }, +- { 0x0415, KEY_TEXT }, +- { 0x0416, KEY_INFO }, +- { 0x0417, KEY_RED }, +- { 0x0418, KEY_GREEN }, +- { 0x0419, KEY_YELLOW }, +- { 0x041a, KEY_BLUE }, +- { 0x041c, KEY_VOLUMEUP }, +- { 0x041e, KEY_VOLUMEDOWN }, +- { 0x041d, KEY_MUTE }, +- { 0x041b, KEY_CHANNELUP }, +- { 0x041f, KEY_CHANNELDOWN }, +- { 0x0440, KEY_PAUSE }, +- { 0x044c, KEY_PLAY }, +- { 0x0458, KEY_RECORD }, +- { 0x0454, KEY_PREVIOUS }, +- { 0x0448, KEY_STOP }, +- { 0x045c, KEY_NEXT } +-}; +- +-/* Number of keypresses to ignore before detect repeating */ +-#define RC_REPEAT_DELAY 3 +- +-static int repeatable_keys[] = { +- KEY_UP, +- KEY_DOWN, +- KEY_LEFT, +- KEY_RIGHT, +- KEY_VOLUMEUP, +- KEY_VOLUMEDOWN, +- KEY_CHANNELUP, +- KEY_CHANNELDOWN +-}; +- +-static int cinergyt2_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- struct cinergyt2_state *st = d->priv; +- u8 key[5] = {0, 0, 0, 0, 0}, cmd = CINERGYT2_EP1_GET_RC_EVENTS; +- int i; +- +- *state = REMOTE_NO_KEY_PRESSED; +- +- dvb_usb_generic_rw(d, &cmd, 1, key, sizeof(key), 0); +- if (key[4] == 0xff) { +- /* key repeat */ +- st->rc_counter++; +- if (st->rc_counter > RC_REPEAT_DELAY) { +- for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) { +- if (d->last_event == repeatable_keys[i]) { +- *state = REMOTE_KEY_REPEAT; +- *event = d->last_event; +- deb_rc("repeat key, event %x\n", +- *event); +- return 0; +- } +- } +- deb_rc("repeated key (non repeatable)\n"); +- } +- return 0; +- } +- +- /* hack to pass checksum on the custom field */ +- key[2] = ~key[1]; +- dvb_usb_nec_rc_key_to_event(d, key, event, state); +- if (key[0] != 0) { +- if (*event != d->last_event) +- st->rc_counter = 0; +- +- deb_rc("key: %x %x %x %x %x\n", +- key[0], key[1], key[2], key[3], key[4]); +- } +- return 0; +-} +- +-static int cinergyt2_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &cinergyt2_properties, +- THIS_MODULE, NULL, adapter_nr); +-} +- +- +-static struct usb_device_id cinergyt2_usb_table[] = { +- { USB_DEVICE(USB_VID_TERRATEC, 0x0038) }, +- { 0 } +-}; +- +-MODULE_DEVICE_TABLE(usb, cinergyt2_usb_table); +- +-static struct dvb_usb_device_properties cinergyt2_properties = { +- .size_of_priv = sizeof(struct cinergyt2_state), +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cinergyt2_streaming_ctrl, +- .frontend_attach = cinergyt2_frontend_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }, +- }}, +- } +- }, +- +- .power_ctrl = cinergyt2_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = 50, +- .rc_map_table = rc_map_cinergyt2_table, +- .rc_map_size = ARRAY_SIZE(rc_map_cinergyt2_table), +- .rc_query = cinergyt2_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 1, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver", +- .cold_ids = {NULL}, +- .warm_ids = { &cinergyt2_usb_table[0], NULL }, +- }, +- { NULL }, +- } +-}; +- +- +-static struct usb_driver cinergyt2_driver = { +- .name = "cinergyT2", +- .probe = cinergyt2_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = cinergyt2_usb_table +-}; +- +-module_usb_driver(cinergyt2_driver); +- +-MODULE_DESCRIPTION("Terratec Cinergy T2 DVB-T driver"); +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Tomi Orava"); +diff --git a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c b/drivers/media/dvb/dvb-usb/cinergyT2-fe.c +deleted file mode 100644 +index 1efc028..0000000 +--- a/drivers/media/dvb/dvb-usb/cinergyT2-fe.c ++++ /dev/null +@@ -1,356 +0,0 @@ +-/* +- * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. +- * +- * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) +- * +- * Based on the dvb-usb-framework code and the +- * original Terratec Cinergy T2 driver by: +- * +- * Copyright (C) 2004 Daniel Mack and +- * Holger Waechtler +- * +- * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf +- * +- * 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 "cinergyT2.h" +- +- +-/** +- * convert linux-dvb frontend parameter set into TPS. +- * See ETSI ETS-300744, section 4.6.2, table 9 for details. +- * +- * This function is probably reusable and may better get placed in a support +- * library. +- * +- * We replace errornous fields by default TPS fields (the ones with value 0). +- */ +- +-static uint16_t compute_tps(struct dtv_frontend_properties *op) +-{ +- uint16_t tps = 0; +- +- switch (op->code_rate_HP) { +- case FEC_2_3: +- tps |= (1 << 7); +- break; +- case FEC_3_4: +- tps |= (2 << 7); +- break; +- case FEC_5_6: +- tps |= (3 << 7); +- break; +- case FEC_7_8: +- tps |= (4 << 7); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- default: +- /* tps |= (0 << 7) */; +- } +- +- switch (op->code_rate_LP) { +- case FEC_2_3: +- tps |= (1 << 4); +- break; +- case FEC_3_4: +- tps |= (2 << 4); +- break; +- case FEC_5_6: +- tps |= (3 << 4); +- break; +- case FEC_7_8: +- tps |= (4 << 4); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- default: +- /* tps |= (0 << 4) */; +- } +- +- switch (op->modulation) { +- case QAM_16: +- tps |= (1 << 13); +- break; +- case QAM_64: +- tps |= (2 << 13); +- break; +- case QPSK: +- default: +- /* tps |= (0 << 13) */; +- } +- +- switch (op->transmission_mode) { +- case TRANSMISSION_MODE_8K: +- tps |= (1 << 0); +- break; +- case TRANSMISSION_MODE_2K: +- default: +- /* tps |= (0 << 0) */; +- } +- +- switch (op->guard_interval) { +- case GUARD_INTERVAL_1_16: +- tps |= (1 << 2); +- break; +- case GUARD_INTERVAL_1_8: +- tps |= (2 << 2); +- break; +- case GUARD_INTERVAL_1_4: +- tps |= (3 << 2); +- break; +- case GUARD_INTERVAL_1_32: +- default: +- /* tps |= (0 << 2) */; +- } +- +- switch (op->hierarchy) { +- case HIERARCHY_1: +- tps |= (1 << 10); +- break; +- case HIERARCHY_2: +- tps |= (2 << 10); +- break; +- case HIERARCHY_4: +- tps |= (3 << 10); +- break; +- case HIERARCHY_NONE: +- default: +- /* tps |= (0 << 10) */; +- } +- +- return tps; +-} +- +-struct cinergyt2_fe_state { +- struct dvb_frontend fe; +- struct dvb_usb_device *d; +-}; +- +-static int cinergyt2_fe_read_status(struct dvb_frontend *fe, +- fe_status_t *status) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_get_status_msg result; +- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; +- int ret; +- +- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&result, +- sizeof(result), 0); +- if (ret < 0) +- return ret; +- +- *status = 0; +- +- if (0xffff - le16_to_cpu(result.gain) > 30) +- *status |= FE_HAS_SIGNAL; +- if (result.lock_bits & (1 << 6)) +- *status |= FE_HAS_LOCK; +- if (result.lock_bits & (1 << 5)) +- *status |= FE_HAS_SYNC; +- if (result.lock_bits & (1 << 4)) +- *status |= FE_HAS_CARRIER; +- if (result.lock_bits & (1 << 1)) +- *status |= FE_HAS_VITERBI; +- +- if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != +- (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) +- *status &= ~FE_HAS_LOCK; +- +- return 0; +-} +- +-static int cinergyt2_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_get_status_msg status; +- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; +- int ret; +- +- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, +- sizeof(status), 0); +- if (ret < 0) +- return ret; +- +- *ber = le32_to_cpu(status.viterbi_error_rate); +- return 0; +-} +- +-static int cinergyt2_fe_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_get_status_msg status; +- u8 cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; +- int ret; +- +- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (u8 *)&status, +- sizeof(status), 0); +- if (ret < 0) { +- err("cinergyt2_fe_read_unc_blocks() Failed! (Error=%d)\n", +- ret); +- return ret; +- } +- *unc = le32_to_cpu(status.uncorrected_block_count); +- return 0; +-} +- +-static int cinergyt2_fe_read_signal_strength(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_get_status_msg status; +- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; +- int ret; +- +- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, +- sizeof(status), 0); +- if (ret < 0) { +- err("cinergyt2_fe_read_signal_strength() Failed!" +- " (Error=%d)\n", ret); +- return ret; +- } +- *strength = (0xffff - le16_to_cpu(status.gain)); +- return 0; +-} +- +-static int cinergyt2_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_get_status_msg status; +- char cmd[] = { CINERGYT2_EP1_GET_TUNER_STATUS }; +- int ret; +- +- ret = dvb_usb_generic_rw(state->d, cmd, sizeof(cmd), (char *)&status, +- sizeof(status), 0); +- if (ret < 0) { +- err("cinergyt2_fe_read_snr() Failed! (Error=%d)\n", ret); +- return ret; +- } +- *snr = (status.snr << 8) | status.snr; +- return 0; +-} +- +-static int cinergyt2_fe_init(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int cinergyt2_fe_sleep(struct dvb_frontend *fe) +-{ +- deb_info("cinergyt2_fe_sleep() Called\n"); +- return 0; +-} +- +-static int cinergyt2_fe_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 800; +- return 0; +-} +- +-static int cinergyt2_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- struct dvbt_set_parameters_msg param; +- char result[2]; +- int err; +- +- param.cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; +- param.tps = cpu_to_le16(compute_tps(fep)); +- param.freq = cpu_to_le32(fep->frequency / 1000); +- param.flags = 0; +- +- switch (fep->bandwidth_hz) { +- default: +- case 8000000: +- param.bandwidth = 8; +- break; +- case 7000000: +- param.bandwidth = 7; +- break; +- case 6000000: +- param.bandwidth = 6; +- break; +- } +- +- err = dvb_usb_generic_rw(state->d, +- (char *)¶m, sizeof(param), +- result, sizeof(result), 0); +- if (err < 0) +- err("cinergyt2_fe_set_frontend() Failed! err=%d\n", err); +- +- return (err < 0) ? err : 0; +-} +- +-static void cinergyt2_fe_release(struct dvb_frontend *fe) +-{ +- struct cinergyt2_fe_state *state = fe->demodulator_priv; +- if (state != NULL) +- kfree(state); +-} +- +-static struct dvb_frontend_ops cinergyt2_fe_ops; +- +-struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d) +-{ +- struct cinergyt2_fe_state *s = kzalloc(sizeof( +- struct cinergyt2_fe_state), GFP_KERNEL); +- if (s == NULL) +- return NULL; +- +- s->d = d; +- memcpy(&s->fe.ops, &cinergyt2_fe_ops, sizeof(struct dvb_frontend_ops)); +- s->fe.demodulator_priv = s; +- return &s->fe; +-} +- +- +-static struct dvb_frontend_ops cinergyt2_fe_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = DRIVER_NAME, +- .frequency_min = 174000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 166667, +- .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_HIERARCHY_AUTO +- | FE_CAN_RECOVER +- | FE_CAN_MUTE_TS +- }, +- +- .release = cinergyt2_fe_release, +- +- .init = cinergyt2_fe_init, +- .sleep = cinergyt2_fe_sleep, +- +- .set_frontend = cinergyt2_fe_set_frontend, +- .get_tune_settings = cinergyt2_fe_get_tune_settings, +- +- .read_status = cinergyt2_fe_read_status, +- .read_ber = cinergyt2_fe_read_ber, +- .read_signal_strength = cinergyt2_fe_read_signal_strength, +- .read_snr = cinergyt2_fe_read_snr, +- .read_ucblocks = cinergyt2_fe_read_unc_blocks, +-}; +diff --git a/drivers/media/dvb/dvb-usb/cinergyT2.h b/drivers/media/dvb/dvb-usb/cinergyT2.h +deleted file mode 100644 +index 84efe03..0000000 +--- a/drivers/media/dvb/dvb-usb/cinergyT2.h ++++ /dev/null +@@ -1,95 +0,0 @@ +-/* +- * TerraTec Cinergy T2/qanu USB2 DVB-T adapter. +- * +- * Copyright (C) 2007 Tomi Orava (tomimo@ncircle.nullnet.fi) +- * +- * Based on the dvb-usb-framework code and the +- * original Terratec Cinergy T2 driver by: +- * +- * Copyright (C) 2004 Daniel Mack and +- * Holger Waechtler +- * +- * Protocol Spec published on http://qanu.de/specs/terratec_cinergyT2.pdf +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef _DVB_USB_CINERGYT2_H_ +-#define _DVB_USB_CINERGYT2_H_ +- +-#include +- +-#define DVB_USB_LOG_PREFIX "cinergyT2" +-#include "dvb-usb.h" +- +-#define DRIVER_NAME "TerraTec/qanu USB2.0 Highspeed DVB-T Receiver" +- +-extern int dvb_usb_cinergyt2_debug; +- +-#define deb_info(args...) dprintk(dvb_usb_cinergyt2_debug, 0x001, args) +-#define deb_xfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x002, args) +-#define deb_pll(args...) dprintk(dvb_usb_cinergyt2_debug, 0x004, args) +-#define deb_ts(args...) dprintk(dvb_usb_cinergyt2_debug, 0x008, args) +-#define deb_err(args...) dprintk(dvb_usb_cinergyt2_debug, 0x010, args) +-#define deb_rc(args...) dprintk(dvb_usb_cinergyt2_debug, 0x020, args) +-#define deb_fw(args...) dprintk(dvb_usb_cinergyt2_debug, 0x040, args) +-#define deb_mem(args...) dprintk(dvb_usb_cinergyt2_debug, 0x080, args) +-#define deb_uxfer(args...) dprintk(dvb_usb_cinergyt2_debug, 0x100, args) +- +- +- +-enum cinergyt2_ep1_cmd { +- CINERGYT2_EP1_PID_TABLE_RESET = 0x01, +- CINERGYT2_EP1_PID_SETUP = 0x02, +- CINERGYT2_EP1_CONTROL_STREAM_TRANSFER = 0x03, +- CINERGYT2_EP1_SET_TUNER_PARAMETERS = 0x04, +- CINERGYT2_EP1_GET_TUNER_STATUS = 0x05, +- CINERGYT2_EP1_START_SCAN = 0x06, +- CINERGYT2_EP1_CONTINUE_SCAN = 0x07, +- CINERGYT2_EP1_GET_RC_EVENTS = 0x08, +- CINERGYT2_EP1_SLEEP_MODE = 0x09, +- CINERGYT2_EP1_GET_FIRMWARE_VERSION = 0x0A +-}; +- +- +-struct dvbt_get_status_msg { +- uint32_t freq; +- uint8_t bandwidth; +- uint16_t tps; +- uint8_t flags; +- __le16 gain; +- uint8_t snr; +- __le32 viterbi_error_rate; +- uint32_t rs_error_rate; +- __le32 uncorrected_block_count; +- uint8_t lock_bits; +- uint8_t prev_lock_bits; +-} __attribute__((packed)); +- +- +-struct dvbt_set_parameters_msg { +- uint8_t cmd; +- __le32 freq; +- uint8_t bandwidth; +- __le16 tps; +- uint8_t flags; +-} __attribute__((packed)); +- +- +-extern struct dvb_frontend *cinergyt2_fe_attach(struct dvb_usb_device *d); +- +-#endif /* _DVB_USB_CINERGYT2_H_ */ +- +diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c +deleted file mode 100644 +index 3940bb0..0000000 +--- a/drivers/media/dvb/dvb-usb/cxusb.c ++++ /dev/null +@@ -1,2043 +0,0 @@ +-/* DVB USB compliant linux driver for Conexant USB reference design. +- * +- * The Conexant reference design I saw on their website was only for analogue +- * capturing (using the cx25842). The box I took to write this driver (reverse +- * engineered) is the one labeled Medion MD95700. In addition to the cx25842 +- * for analogue capturing it also has a cx22702 DVB-T demodulator on the main +- * board. Besides it has a atiremote (X10) and a USB2.0 hub onboard. +- * +- * Maybe it is a little bit premature to call this driver cxusb, but I assume +- * the USB protocol is identical or at least inherited from the reference +- * design, so it can be reused for the "analogue-only" device (if it will +- * appear at all). +- * +- * TODO: Use the cx25840-driver for the analogue part +- * +- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) +- * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) +- * Copyright (C) 2006, 2007 Chris Pascoe (c.pascoe@itee.uq.edu.au) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include +-#include +-#include +- +-#include "cxusb.h" +- +-#include "cx22702.h" +-#include "lgdt330x.h" +-#include "mt352.h" +-#include "mt352_priv.h" +-#include "zl10353.h" +-#include "tuner-xc2028.h" +-#include "tuner-simple.h" +-#include "mxl5005s.h" +-#include "max2165.h" +-#include "dib7000p.h" +-#include "dib0070.h" +-#include "lgs8gxx.h" +-#include "atbm8830.h" +- +-/* debug */ +-static int dvb_usb_cxusb_debug; +-module_param_named(debug, dvb_usb_cxusb_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_info(args...) dprintk(dvb_usb_cxusb_debug, 0x03, args) +-#define deb_i2c(args...) dprintk(dvb_usb_cxusb_debug, 0x02, args) +- +-static int cxusb_ctrl_msg(struct dvb_usb_device *d, +- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- int wo = (rbuf == NULL || rlen == 0); /* write-only */ +- u8 sndbuf[1+wlen]; +- memset(sndbuf, 0, 1+wlen); +- +- sndbuf[0] = cmd; +- memcpy(&sndbuf[1], wbuf, wlen); +- if (wo) +- return dvb_usb_generic_write(d, sndbuf, 1+wlen); +- else +- return dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0); +-} +- +-/* GPIO */ +-static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) +-{ +- struct cxusb_state *st = d->priv; +- u8 o[2], i; +- +- if (st->gpio_write_state[GPIO_TUNER] == onoff) +- return; +- +- o[0] = GPIO_TUNER; +- o[1] = onoff; +- cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); +- +- if (i != 0x01) +- deb_info("gpio_write failed.\n"); +- +- st->gpio_write_state[GPIO_TUNER] = onoff; +-} +- +-static int cxusb_bluebird_gpio_rw(struct dvb_usb_device *d, u8 changemask, +- u8 newval) +-{ +- u8 o[2], gpio_state; +- int rc; +- +- o[0] = 0xff & ~changemask; /* mask of bits to keep */ +- o[1] = newval & changemask; /* new values for bits */ +- +- rc = cxusb_ctrl_msg(d, CMD_BLUEBIRD_GPIO_RW, o, 2, &gpio_state, 1); +- if (rc < 0 || (gpio_state & changemask) != (newval & changemask)) +- deb_info("bluebird_gpio_write failed.\n"); +- +- return rc < 0 ? rc : gpio_state; +-} +- +-static void cxusb_bluebird_gpio_pulse(struct dvb_usb_device *d, u8 pin, int low) +-{ +- cxusb_bluebird_gpio_rw(d, pin, low ? 0 : pin); +- msleep(5); +- cxusb_bluebird_gpio_rw(d, pin, low ? pin : 0); +-} +- +-static void cxusb_nano2_led(struct dvb_usb_device *d, int onoff) +-{ +- cxusb_bluebird_gpio_rw(d, 0x40, onoff ? 0 : 0x40); +-} +- +-static int cxusb_d680_dmb_gpio_tuner(struct dvb_usb_device *d, +- u8 addr, int onoff) +-{ +- u8 o[2] = {addr, onoff}; +- u8 i; +- int rc; +- +- rc = cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); +- +- if (rc < 0) +- return rc; +- if (i == 0x01) +- return 0; +- else { +- deb_info("gpio_write failed.\n"); +- return -EIO; +- } +-} +- +-/* I2C */ +-static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- +- if (d->udev->descriptor.idVendor == USB_VID_MEDION) +- switch (msg[i].addr) { +- case 0x63: +- cxusb_gpio_tuner(d, 0); +- break; +- default: +- cxusb_gpio_tuner(d, 1); +- break; +- } +- +- if (msg[i].flags & I2C_M_RD) { +- /* read only */ +- u8 obuf[3], ibuf[1+msg[i].len]; +- obuf[0] = 0; +- obuf[1] = msg[i].len; +- obuf[2] = msg[i].addr; +- if (cxusb_ctrl_msg(d, CMD_I2C_READ, +- obuf, 3, +- ibuf, 1+msg[i].len) < 0) { +- warn("i2c read failed"); +- break; +- } +- memcpy(msg[i].buf, &ibuf[1], msg[i].len); +- } else if (i+1 < num && (msg[i+1].flags & I2C_M_RD) && +- msg[i].addr == msg[i+1].addr) { +- /* write to then read from same address */ +- u8 obuf[3+msg[i].len], ibuf[1+msg[i+1].len]; +- obuf[0] = msg[i].len; +- obuf[1] = msg[i+1].len; +- obuf[2] = msg[i].addr; +- memcpy(&obuf[3], msg[i].buf, msg[i].len); +- +- if (cxusb_ctrl_msg(d, CMD_I2C_READ, +- obuf, 3+msg[i].len, +- ibuf, 1+msg[i+1].len) < 0) +- break; +- +- if (ibuf[0] != 0x08) +- deb_i2c("i2c read may have failed\n"); +- +- memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); +- +- i++; +- } else { +- /* write only */ +- u8 obuf[2+msg[i].len], ibuf; +- obuf[0] = msg[i].addr; +- obuf[1] = msg[i].len; +- memcpy(&obuf[2], msg[i].buf, msg[i].len); +- +- if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf, +- 2+msg[i].len, &ibuf,1) < 0) +- break; +- if (ibuf != 0x08) +- deb_i2c("i2c write may have failed\n"); +- } +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i == num ? num : -EREMOTEIO; +-} +- +-static u32 cxusb_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm cxusb_i2c_algo = { +- .master_xfer = cxusb_i2c_xfer, +- .functionality = cxusb_i2c_func, +-}; +- +-static int cxusb_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 b = 0; +- if (onoff) +- return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); +- else +- return cxusb_ctrl_msg(d, CMD_POWER_OFF, &b, 1, NULL, 0); +-} +- +-static int cxusb_aver_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- int ret; +- if (!onoff) +- return cxusb_ctrl_msg(d, CMD_POWER_OFF, NULL, 0, NULL, 0); +- if (d->state == DVB_USB_STATE_INIT && +- usb_set_interface(d->udev, 0, 0) < 0) +- err("set interface failed"); +- do {} while (!(ret = cxusb_ctrl_msg(d, CMD_POWER_ON, NULL, 0, NULL, 0)) && +- !(ret = cxusb_ctrl_msg(d, 0x15, NULL, 0, NULL, 0)) && +- !(ret = cxusb_ctrl_msg(d, 0x17, NULL, 0, NULL, 0)) && 0); +- if (!ret) { +- /* FIXME: We don't know why, but we need to configure the +- * lgdt3303 with the register settings below on resume */ +- int i; +- u8 buf, bufs[] = { +- 0x0e, 0x2, 0x00, 0x7f, +- 0x0e, 0x2, 0x02, 0xfe, +- 0x0e, 0x2, 0x02, 0x01, +- 0x0e, 0x2, 0x00, 0x03, +- 0x0e, 0x2, 0x0d, 0x40, +- 0x0e, 0x2, 0x0e, 0x87, +- 0x0e, 0x2, 0x0f, 0x8e, +- 0x0e, 0x2, 0x10, 0x01, +- 0x0e, 0x2, 0x14, 0xd7, +- 0x0e, 0x2, 0x47, 0x88, +- }; +- msleep(20); +- for (i = 0; i < sizeof(bufs)/sizeof(u8); i += 4/sizeof(u8)) { +- ret = cxusb_ctrl_msg(d, CMD_I2C_WRITE, +- bufs+i, 4, &buf, 1); +- if (ret) +- break; +- if (buf != 0x8) +- return -EREMOTEIO; +- } +- } +- return ret; +-} +- +-static int cxusb_bluebird_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 b = 0; +- if (onoff) +- return cxusb_ctrl_msg(d, CMD_POWER_ON, &b, 1, NULL, 0); +- else +- return 0; +-} +- +-static int cxusb_nano2_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- int rc = 0; +- +- rc = cxusb_power_ctrl(d, onoff); +- if (!onoff) +- cxusb_nano2_led(d, 0); +- +- return rc; +-} +- +-static int cxusb_d680_dmb_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- int ret; +- u8 b; +- ret = cxusb_power_ctrl(d, onoff); +- if (!onoff) +- return ret; +- +- msleep(128); +- cxusb_ctrl_msg(d, CMD_DIGITAL, NULL, 0, &b, 1); +- msleep(100); +- return ret; +-} +- +-static int cxusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- u8 buf[2] = { 0x03, 0x00 }; +- if (onoff) +- cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, buf, 2, NULL, 0); +- else +- cxusb_ctrl_msg(adap->dev, CMD_STREAMING_OFF, NULL, 0, NULL, 0); +- +- return 0; +-} +- +-static int cxusb_aver_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- if (onoff) +- cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_ON, NULL, 0, NULL, 0); +- else +- cxusb_ctrl_msg(adap->dev, CMD_AVER_STREAM_OFF, +- NULL, 0, NULL, 0); +- return 0; +-} +- +-static void cxusb_d680_dmb_drain_message(struct dvb_usb_device *d) +-{ +- int ep = d->props.generic_bulk_ctrl_endpoint; +- const int timeout = 100; +- const int junk_len = 32; +- u8 *junk; +- int rd_count; +- +- /* Discard remaining data in video pipe */ +- junk = kmalloc(junk_len, GFP_KERNEL); +- if (!junk) +- return; +- while (1) { +- if (usb_bulk_msg(d->udev, +- usb_rcvbulkpipe(d->udev, ep), +- junk, junk_len, &rd_count, timeout) < 0) +- break; +- if (!rd_count) +- break; +- } +- kfree(junk); +-} +- +-static void cxusb_d680_dmb_drain_video(struct dvb_usb_device *d) +-{ +- struct usb_data_stream_properties *p = &d->props.adapter[0].fe[0].stream; +- const int timeout = 100; +- const int junk_len = p->u.bulk.buffersize; +- u8 *junk; +- int rd_count; +- +- /* Discard remaining data in video pipe */ +- junk = kmalloc(junk_len, GFP_KERNEL); +- if (!junk) +- return; +- while (1) { +- if (usb_bulk_msg(d->udev, +- usb_rcvbulkpipe(d->udev, p->endpoint), +- junk, junk_len, &rd_count, timeout) < 0) +- break; +- if (!rd_count) +- break; +- } +- kfree(junk); +-} +- +-static int cxusb_d680_dmb_streaming_ctrl( +- struct dvb_usb_adapter *adap, int onoff) +-{ +- if (onoff) { +- u8 buf[2] = { 0x03, 0x00 }; +- cxusb_d680_dmb_drain_video(adap->dev); +- return cxusb_ctrl_msg(adap->dev, CMD_STREAMING_ON, +- buf, sizeof(buf), NULL, 0); +- } else { +- int ret = cxusb_ctrl_msg(adap->dev, +- CMD_STREAMING_OFF, NULL, 0, NULL, 0); +- return ret; +- } +-} +- +-static int cxusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- u8 ircode[4]; +- int i; +- +- cxusb_ctrl_msg(d, CMD_GET_IR_CODE, NULL, 0, ircode, 4); +- +- *event = 0; +- *state = REMOTE_NO_KEY_PRESSED; +- +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { +- if (rc5_custom(&keymap[i]) == ircode[2] && +- rc5_data(&keymap[i]) == ircode[3]) { +- *event = keymap[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- +- return 0; +- } +- } +- +- return 0; +-} +- +-static int cxusb_bluebird2_rc_query(struct dvb_usb_device *d, u32 *event, +- int *state) +-{ +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- u8 ircode[4]; +- int i; +- struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, +- .buf = ircode, .len = 4 }; +- +- *event = 0; +- *state = REMOTE_NO_KEY_PRESSED; +- +- if (cxusb_i2c_xfer(&d->i2c_adap, &msg, 1) != 1) +- return 0; +- +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { +- if (rc5_custom(&keymap[i]) == ircode[1] && +- rc5_data(&keymap[i]) == ircode[2]) { +- *event = keymap[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- +- return 0; +- } +- } +- +- return 0; +-} +- +-static int cxusb_d680_dmb_rc_query(struct dvb_usb_device *d, u32 *event, +- int *state) +-{ +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- u8 ircode[2]; +- int i; +- +- *event = 0; +- *state = REMOTE_NO_KEY_PRESSED; +- +- if (cxusb_ctrl_msg(d, 0x10, NULL, 0, ircode, 2) < 0) +- return 0; +- +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { +- if (rc5_custom(&keymap[i]) == ircode[0] && +- rc5_data(&keymap[i]) == ircode[1]) { +- *event = keymap[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- +- return 0; +- } +- } +- +- return 0; +-} +- +-static struct rc_map_table rc_map_dvico_mce_table[] = { +- { 0xfe02, KEY_TV }, +- { 0xfe0e, KEY_MP3 }, +- { 0xfe1a, KEY_DVD }, +- { 0xfe1e, KEY_FAVORITES }, +- { 0xfe16, KEY_SETUP }, +- { 0xfe46, KEY_POWER2 }, +- { 0xfe0a, KEY_EPG }, +- { 0xfe49, KEY_BACK }, +- { 0xfe4d, KEY_MENU }, +- { 0xfe51, KEY_UP }, +- { 0xfe5b, KEY_LEFT }, +- { 0xfe5f, KEY_RIGHT }, +- { 0xfe53, KEY_DOWN }, +- { 0xfe5e, KEY_OK }, +- { 0xfe59, KEY_INFO }, +- { 0xfe55, KEY_TAB }, +- { 0xfe0f, KEY_PREVIOUSSONG },/* Replay */ +- { 0xfe12, KEY_NEXTSONG }, /* Skip */ +- { 0xfe42, KEY_ENTER }, /* Windows/Start */ +- { 0xfe15, KEY_VOLUMEUP }, +- { 0xfe05, KEY_VOLUMEDOWN }, +- { 0xfe11, KEY_CHANNELUP }, +- { 0xfe09, KEY_CHANNELDOWN }, +- { 0xfe52, KEY_CAMERA }, +- { 0xfe5a, KEY_TUNER }, /* Live */ +- { 0xfe19, KEY_OPEN }, +- { 0xfe0b, KEY_1 }, +- { 0xfe17, KEY_2 }, +- { 0xfe1b, KEY_3 }, +- { 0xfe07, KEY_4 }, +- { 0xfe50, KEY_5 }, +- { 0xfe54, KEY_6 }, +- { 0xfe48, KEY_7 }, +- { 0xfe4c, KEY_8 }, +- { 0xfe58, KEY_9 }, +- { 0xfe13, KEY_ANGLE }, /* Aspect */ +- { 0xfe03, KEY_0 }, +- { 0xfe1f, KEY_ZOOM }, +- { 0xfe43, KEY_REWIND }, +- { 0xfe47, KEY_PLAYPAUSE }, +- { 0xfe4f, KEY_FASTFORWARD }, +- { 0xfe57, KEY_MUTE }, +- { 0xfe0d, KEY_STOP }, +- { 0xfe01, KEY_RECORD }, +- { 0xfe4e, KEY_POWER }, +-}; +- +-static struct rc_map_table rc_map_dvico_portable_table[] = { +- { 0xfc02, KEY_SETUP }, /* Profile */ +- { 0xfc43, KEY_POWER2 }, +- { 0xfc06, KEY_EPG }, +- { 0xfc5a, KEY_BACK }, +- { 0xfc05, KEY_MENU }, +- { 0xfc47, KEY_INFO }, +- { 0xfc01, KEY_TAB }, +- { 0xfc42, KEY_PREVIOUSSONG },/* Replay */ +- { 0xfc49, KEY_VOLUMEUP }, +- { 0xfc09, KEY_VOLUMEDOWN }, +- { 0xfc54, KEY_CHANNELUP }, +- { 0xfc0b, KEY_CHANNELDOWN }, +- { 0xfc16, KEY_CAMERA }, +- { 0xfc40, KEY_TUNER }, /* ATV/DTV */ +- { 0xfc45, KEY_OPEN }, +- { 0xfc19, KEY_1 }, +- { 0xfc18, KEY_2 }, +- { 0xfc1b, KEY_3 }, +- { 0xfc1a, KEY_4 }, +- { 0xfc58, KEY_5 }, +- { 0xfc59, KEY_6 }, +- { 0xfc15, KEY_7 }, +- { 0xfc14, KEY_8 }, +- { 0xfc17, KEY_9 }, +- { 0xfc44, KEY_ANGLE }, /* Aspect */ +- { 0xfc55, KEY_0 }, +- { 0xfc07, KEY_ZOOM }, +- { 0xfc0a, KEY_REWIND }, +- { 0xfc08, KEY_PLAYPAUSE }, +- { 0xfc4b, KEY_FASTFORWARD }, +- { 0xfc5b, KEY_MUTE }, +- { 0xfc04, KEY_STOP }, +- { 0xfc56, KEY_RECORD }, +- { 0xfc57, KEY_POWER }, +- { 0xfc41, KEY_UNKNOWN }, /* INPUT */ +- { 0xfc00, KEY_UNKNOWN }, /* HD */ +-}; +- +-static struct rc_map_table rc_map_d680_dmb_table[] = { +- { 0x0038, KEY_UNKNOWN }, /* TV/AV */ +- { 0x080c, KEY_ZOOM }, +- { 0x0800, KEY_0 }, +- { 0x0001, KEY_1 }, +- { 0x0802, KEY_2 }, +- { 0x0003, KEY_3 }, +- { 0x0804, KEY_4 }, +- { 0x0005, KEY_5 }, +- { 0x0806, KEY_6 }, +- { 0x0007, KEY_7 }, +- { 0x0808, KEY_8 }, +- { 0x0009, KEY_9 }, +- { 0x000a, KEY_MUTE }, +- { 0x0829, KEY_BACK }, +- { 0x0012, KEY_CHANNELUP }, +- { 0x0813, KEY_CHANNELDOWN }, +- { 0x002b, KEY_VOLUMEUP }, +- { 0x082c, KEY_VOLUMEDOWN }, +- { 0x0020, KEY_UP }, +- { 0x0821, KEY_DOWN }, +- { 0x0011, KEY_LEFT }, +- { 0x0810, KEY_RIGHT }, +- { 0x000d, KEY_OK }, +- { 0x081f, KEY_RECORD }, +- { 0x0017, KEY_PLAYPAUSE }, +- { 0x0816, KEY_PLAYPAUSE }, +- { 0x000b, KEY_STOP }, +- { 0x0827, KEY_FASTFORWARD }, +- { 0x0026, KEY_REWIND }, +- { 0x081e, KEY_UNKNOWN }, /* Time Shift */ +- { 0x000e, KEY_UNKNOWN }, /* Snapshot */ +- { 0x082d, KEY_UNKNOWN }, /* Mouse Cursor */ +- { 0x000f, KEY_UNKNOWN }, /* Minimize/Maximize */ +- { 0x0814, KEY_UNKNOWN }, /* Shuffle */ +- { 0x0025, KEY_POWER }, +-}; +- +-static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) +-{ +- static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; +- static u8 reset [] = { RESET, 0x80 }; +- static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; +- static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; +- static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; +- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; +- +- mt352_write(fe, clock_config, sizeof(clock_config)); +- udelay(200); +- mt352_write(fe, reset, sizeof(reset)); +- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); +- +- mt352_write(fe, agc_cfg, sizeof(agc_cfg)); +- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); +- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); +- +- return 0; +-} +- +-static int cxusb_mt352_demod_init(struct dvb_frontend* fe) +-{ /* used in both lgz201 and th7579 */ +- static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x29 }; +- static u8 reset [] = { RESET, 0x80 }; +- static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; +- static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; +- static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; +- static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; +- +- mt352_write(fe, clock_config, sizeof(clock_config)); +- udelay(200); +- mt352_write(fe, reset, sizeof(reset)); +- mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); +- +- mt352_write(fe, agc_cfg, sizeof(agc_cfg)); +- mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); +- mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); +- return 0; +-} +- +-static struct cx22702_config cxusb_cx22702_config = { +- .demod_address = 0x63, +- .output_mode = CX22702_PARALLEL_OUTPUT, +-}; +- +-static struct lgdt330x_config cxusb_lgdt3303_config = { +- .demod_address = 0x0e, +- .demod_chip = LGDT3303, +-}; +- +-static struct lgdt330x_config cxusb_aver_lgdt3303_config = { +- .demod_address = 0x0e, +- .demod_chip = LGDT3303, +- .clock_polarity_flip = 2, +-}; +- +-static struct mt352_config cxusb_dee1601_config = { +- .demod_address = 0x0f, +- .demod_init = cxusb_dee1601_demod_init, +-}; +- +-static struct zl10353_config cxusb_zl10353_dee1601_config = { +- .demod_address = 0x0f, +- .parallel_ts = 1, +-}; +- +-static struct mt352_config cxusb_mt352_config = { +- /* used in both lgz201 and th7579 */ +- .demod_address = 0x0f, +- .demod_init = cxusb_mt352_demod_init, +-}; +- +-static struct zl10353_config cxusb_zl10353_xc3028_config = { +- .demod_address = 0x0f, +- .if2 = 45600, +- .no_tuner = 1, +- .parallel_ts = 1, +-}; +- +-static struct zl10353_config cxusb_zl10353_xc3028_config_no_i2c_gate = { +- .demod_address = 0x0f, +- .if2 = 45600, +- .no_tuner = 1, +- .parallel_ts = 1, +- .disable_i2c_gate_ctrl = 1, +-}; +- +-static struct mt352_config cxusb_mt352_xc3028_config = { +- .demod_address = 0x0f, +- .if2 = 4560, +- .no_tuner = 1, +- .demod_init = cxusb_mt352_demod_init, +-}; +- +-/* FIXME: needs tweaking */ +-static struct mxl5005s_config aver_a868r_tuner = { +- .i2c_address = 0x63, +- .if_freq = 6000000UL, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_C, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-/* FIXME: needs tweaking */ +-static struct mxl5005s_config d680_dmb_tuner = { +- .i2c_address = 0x63, +- .if_freq = 36125000UL, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_C, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-static struct max2165_config mygica_d689_max2165_cfg = { +- .i2c_address = 0x60, +- .osc_clk = 20 +-}; +- +-/* Callbacks for DVB USB */ +-static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, 0x61, +- TUNER_PHILIPS_FMD1216ME_MK3); +- return 0; +-} +- +-static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, +- NULL, DVB_PLL_THOMSON_DTT7579); +- return 0; +-} +- +-static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_LG_Z201); +- return 0; +-} +- +-static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, +- NULL, DVB_PLL_THOMSON_DTT7579); +- return 0; +-} +- +-static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, 0x61, TUNER_LG_TDVS_H06XF); +- return 0; +-} +- +-static int dvico_bluebird_xc2028_callback(void *ptr, int component, +- int command, int arg) +-{ +- struct dvb_usb_adapter *adap = ptr; +- struct dvb_usb_device *d = adap->dev; +- +- switch (command) { +- case XC2028_TUNER_RESET: +- deb_info("%s: XC2028_TUNER_RESET %d\n", __func__, arg); +- cxusb_bluebird_gpio_pulse(d, 0x01, 1); +- break; +- case XC2028_RESET_CLK: +- deb_info("%s: XC2028_RESET_CLK %d\n", __func__, arg); +- break; +- default: +- deb_info("%s: unknown command %d, arg %d\n", __func__, +- command, arg); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int cxusb_dvico_xc3028_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_frontend *fe; +- struct xc2028_config cfg = { +- .i2c_adap = &adap->dev->i2c_adap, +- .i2c_addr = 0x61, +- }; +- static struct xc2028_ctrl ctl = { +- .fname = XC2028_DEFAULT_FIRMWARE, +- .max_len = 64, +- .demod = XC3028_FE_ZARLINK456, +- }; +- +- /* FIXME: generalize & move to common area */ +- adap->fe_adap[0].fe->callback = dvico_bluebird_xc2028_callback; +- +- fe = dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &cfg); +- if (fe == NULL || fe->ops.tuner_ops.set_config == NULL) +- return -EIO; +- +- fe->ops.tuner_ops.set_config(fe, &ctl); +- +- return 0; +-} +- +-static int cxusb_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, &aver_a868r_tuner); +- return 0; +-} +- +-static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_frontend *fe; +- fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, &d680_dmb_tuner); +- return (fe == NULL) ? -EIO : 0; +-} +- +-static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_frontend *fe; +- fe = dvb_attach(max2165_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); +- return (fe == NULL) ? -EIO : 0; +-} +- +-static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- u8 b; +- if (usb_set_interface(adap->dev->udev, 0, 6) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); +- +- adap->fe_adap[0].fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- return -EIO; +-} +- +-static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev, 0, 7) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, +- &cxusb_lgdt3303_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- return -EIO; +-} +- +-static int cxusb_aver_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = dvb_attach(lgdt330x_attach, &cxusb_aver_lgdt3303_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe != NULL) +- return 0; +- +- return -EIO; +-} +- +-static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- /* used in both lgz201 and th7579 */ +- if (usb_set_interface(adap->dev->udev, 0, 0) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_mt352_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- return -EIO; +-} +- +-static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev, 0, 0) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, +- &cxusb_zl10353_dee1601_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- return -EIO; +-} +- +-static int cxusb_dualdig4_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- u8 ircode[4]; +- int i; +- struct i2c_msg msg = { .addr = 0x6b, .flags = I2C_M_RD, +- .buf = ircode, .len = 4 }; +- +- if (usb_set_interface(adap->dev->udev, 0, 1) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- /* reset the tuner and demodulator */ +- cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0); +- cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); +- cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); +- +- adap->fe_adap[0].fe = +- dvb_attach(zl10353_attach, +- &cxusb_zl10353_xc3028_config_no_i2c_gate, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) == NULL) +- return -EIO; +- +- /* try to determine if there is no IR decoder on the I2C bus */ +- for (i = 0; adap->dev->props.rc.legacy.rc_map_table != NULL && i < 5; i++) { +- msleep(20); +- if (cxusb_i2c_xfer(&adap->dev->i2c_adap, &msg, 1) != 1) +- goto no_IR; +- if (ircode[0] == 0 && ircode[1] == 0) +- continue; +- if (ircode[2] + ircode[3] != 0xff) { +-no_IR: +- adap->dev->props.rc.legacy.rc_map_table = NULL; +- info("No IR receiver detected on this device."); +- break; +- } +- } +- +- return 0; +-} +- +-static struct dibx000_agc_config dib7070_agc_config = { +- .band_caps = BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, +- +- /* +- * P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, +- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 +- */ +- .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | +- (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- .inv_gain = 600, +- .time_stabiliz = 10, +- .alpha_level = 0, +- .thlock = 118, +- .wbd_inv = 0, +- .wbd_ref = 3530, +- .wbd_sel = 1, +- .wbd_alpha = 5, +- .agc1_max = 65535, +- .agc1_min = 0, +- .agc2_max = 65535, +- .agc2_min = 0, +- .agc1_pt1 = 0, +- .agc1_pt2 = 40, +- .agc1_pt3 = 183, +- .agc1_slope1 = 206, +- .agc1_slope2 = 255, +- .agc2_pt1 = 72, +- .agc2_pt2 = 152, +- .agc2_slope1 = 88, +- .agc2_slope2 = 90, +- .alpha_mant = 17, +- .alpha_exp = 27, +- .beta_mant = 23, +- .beta_exp = 51, +- .perform_agc_softsplit = 0, +-}; +- +-static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { +- .internal = 60000, +- .sampling = 15000, +- .pll_prediv = 1, +- .pll_ratio = 20, +- .pll_range = 3, +- .pll_reset = 1, +- .pll_bypass = 0, +- .enable_refdiv = 0, +- .bypclk_div = 0, +- .IO_CLK_en_core = 1, +- .ADClkSrc = 1, +- .modulo = 2, +- /* refsel, sel, freq_15k */ +- .sad_cfg = (3 << 14) | (1 << 12) | (524 << 0), +- .ifreq = (0 << 25) | 0, +- .timf = 20452225, +- .xtal_hz = 12000000, +-}; +- +-static struct dib7000p_config cxusb_dualdig4_rev2_config = { +- .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &dib7070_agc_config, +- .bw = &dib7070_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- .spur_protect = 1, +- +- .gpio_dir = 0xfcef, +- .gpio_val = 0x0110, +- +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +-}; +- +-static int cxusb_dualdig4_rev2_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev, 0, 1) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- &cxusb_dualdig4_rev2_config) < 0) { +- printk(KERN_WARNING "Unable to enumerate dib7000p\n"); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, +- &cxusb_dualdig4_rev2_config); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- return 0; +-} +- +-static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +-{ +- return dib7000p_set_gpio(fe, 8, 0, !onoff); +-} +- +-static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- return 0; +-} +- +-static struct dib0070_config dib7070p_dib0070_config = { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib7070_tuner_reset, +- .sleep = dib7070_tuner_sleep, +- .clock_khz = 12000, +-}; +- +-struct dib0700_adapter_state { +- int (*set_param_save) (struct dvb_frontend *); +-}; +- +-static int dib7070_set_param_override(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- +- u16 offset; +- u8 band = BAND_OF_FREQUENCY(p->frequency/1000); +- switch (band) { +- case BAND_VHF: offset = 950; break; +- default: +- case BAND_UHF: offset = 550; break; +- } +- +- dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); +- +- return state->set_param_save(fe); +-} +- +-static int cxusb_dualdig4_rev2_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = +- dib7000p_get_i2c_master(adap->fe_adap[0].fe, +- DIBX000_I2C_INTERFACE_TUNER, 1); +- +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, +- &dib7070p_dib0070_config) == NULL) +- return -ENODEV; +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; +- return 0; +-} +- +-static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev, 0, 1) < 0) +- err("set interface failed"); +- +- cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); +- +- /* reset the tuner and demodulator */ +- cxusb_bluebird_gpio_rw(adap->dev, 0x04, 0); +- cxusb_bluebird_gpio_pulse(adap->dev, 0x01, 1); +- cxusb_bluebird_gpio_pulse(adap->dev, 0x02, 1); +- +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, +- &cxusb_zl10353_xc3028_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, +- &cxusb_mt352_xc3028_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) +- return 0; +- +- return -EIO; +-} +- +-static struct lgs8gxx_config d680_lgs8gl5_cfg = { +- .prod = LGS8GXX_PROD_LGS8GL5, +- .demod_address = 0x19, +- .serial_ts = 0, +- .ts_clk_pol = 0, +- .ts_clk_gated = 1, +- .if_clk_freq = 30400, /* 30.4 MHz */ +- .if_freq = 5725, /* 5.725 MHz */ +- .if_neg_center = 0, +- .ext_adc = 0, +- .adc_signed = 0, +- .if_neg_edge = 0, +-}; +- +-static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *d = adap->dev; +- int n; +- +- /* Select required USB configuration */ +- if (usb_set_interface(d->udev, 0, 0) < 0) +- err("set interface failed"); +- +- /* Unblock all USB pipes */ +- usb_clear_halt(d->udev, +- usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- usb_clear_halt(d->udev, +- usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- usb_clear_halt(d->udev, +- usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); +- +- /* Drain USB pipes to avoid hang after reboot */ +- for (n = 0; n < 5; n++) { +- cxusb_d680_dmb_drain_message(d); +- cxusb_d680_dmb_drain_video(d); +- msleep(200); +- } +- +- /* Reset the tuner */ +- if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { +- err("clear tuner gpio failed"); +- return -EIO; +- } +- msleep(100); +- if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { +- err("set tuner gpio failed"); +- return -EIO; +- } +- msleep(100); +- +- /* Attach frontend */ +- adap->fe_adap[0].fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- return 0; +-} +- +-static struct atbm8830_config mygica_d689_atbm8830_cfg = { +- .prod = ATBM8830_PROD_8830, +- .demod_address = 0x40, +- .serial_ts = 0, +- .ts_sampling_edge = 1, +- .ts_clk_gated = 0, +- .osc_clk_freq = 30400, /* in kHz */ +- .if_freq = 0, /* zero IF */ +- .zif_swap_iq = 1, +- .agc_min = 0x2E, +- .agc_max = 0x90, +- .agc_hold_loop = 0, +-}; +- +-static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *d = adap->dev; +- +- /* Select required USB configuration */ +- if (usb_set_interface(d->udev, 0, 0) < 0) +- err("set interface failed"); +- +- /* Unblock all USB pipes */ +- usb_clear_halt(d->udev, +- usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- usb_clear_halt(d->udev, +- usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- usb_clear_halt(d->udev, +- usb_rcvbulkpipe(d->udev, d->props.adapter[0].fe[0].stream.endpoint)); +- +- +- /* Reset the tuner */ +- if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { +- err("clear tuner gpio failed"); +- return -EIO; +- } +- msleep(100); +- if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { +- err("set tuner gpio failed"); +- return -EIO; +- } +- msleep(100); +- +- /* Attach frontend */ +- adap->fe_adap[0].fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, +- &d->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- return 0; +-} +- +-/* +- * DViCO has shipped two devices with the same USB ID, but only one of them +- * needs a firmware download. Check the device class details to see if they +- * have non-default values to decide whether the device is actually cold or +- * not, and forget a match if it turns out we selected the wrong device. +- */ +-static int bluebird_fx2_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- int wascold = *cold; +- +- *cold = udev->descriptor.bDeviceClass == 0xff && +- udev->descriptor.bDeviceSubClass == 0xff && +- udev->descriptor.bDeviceProtocol == 0xff; +- +- if (*cold && !wascold) +- *desc = NULL; +- +- return 0; +-} +- +-/* +- * DViCO bluebird firmware needs the "warm" product ID to be patched into the +- * firmware file before download. +- */ +- +-static const int dvico_firmware_id_offsets[] = { 6638, 3204 }; +-static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, +- const struct firmware *fw) +-{ +- int pos; +- +- for (pos = 0; pos < ARRAY_SIZE(dvico_firmware_id_offsets); pos++) { +- int idoff = dvico_firmware_id_offsets[pos]; +- +- if (fw->size < idoff + 4) +- continue; +- +- if (fw->data[idoff] == (USB_VID_DVICO & 0xff) && +- fw->data[idoff + 1] == USB_VID_DVICO >> 8) { +- struct firmware new_fw; +- u8 *new_fw_data = vmalloc(fw->size); +- int ret; +- +- if (!new_fw_data) +- return -ENOMEM; +- +- memcpy(new_fw_data, fw->data, fw->size); +- new_fw.size = fw->size; +- new_fw.data = new_fw_data; +- +- new_fw_data[idoff + 2] = +- le16_to_cpu(udev->descriptor.idProduct) + 1; +- new_fw_data[idoff + 3] = +- le16_to_cpu(udev->descriptor.idProduct) >> 8; +- +- ret = usb_cypress_load_firmware(udev, &new_fw, +- CYPRESS_FX2); +- vfree(new_fw_data); +- return ret; +- } +- } +- +- return -EINVAL; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties cxusb_medion_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; +-static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; +-static struct dvb_usb_device_properties cxusb_aver_a868r_properties; +-static struct dvb_usb_device_properties cxusb_d680_dmb_properties; +-static struct dvb_usb_device_properties cxusb_mygica_d689_properties; +- +-static int cxusb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &cxusb_medion_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgh064f_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dee1601_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_lgz201_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dtt7579_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_dualdig4_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_bluebird_nano2_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, +- &cxusb_bluebird_nano2_needsfirmware_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_aver_a868r_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, +- &cxusb_bluebird_dualdig4_rev2_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0) +- return 0; +- +- return -EINVAL; +-} +- +-static struct usb_device_id cxusb_table [] = { +- { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM) }, +- { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, +- { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, +- { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, +- { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, +- {} /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, cxusb_table); +- +-static struct dvb_usb_device_properties cxusb_medion_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_cx22702_frontend_attach, +- .tuner_attach = cxusb_fmd1216me_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- .power_ctrl = cxusb_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Medion MD95700 (MDUSBTV-HYBRID)", +- { NULL }, +- { &cxusb_table[0], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-bluebird-01.fw", +- .download_firmware = bluebird_patch_dvico_firmware_download, +- /* use usb alt setting 0 for EP4 transfer (dvb-t), +- use usb alt setting 7 for EP2 transfer (atsc) */ +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_lgdt3303_frontend_attach, +- .tuner_attach = cxusb_lgh064f_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_bluebird_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_portable_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV5 USB Gold", +- { &cxusb_table[1], NULL }, +- { &cxusb_table[2], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-bluebird-01.fw", +- .download_firmware = bluebird_patch_dvico_firmware_download, +- /* use usb alt setting 0 for EP4 transfer (dvb-t), +- use usb alt setting 7 for EP2 transfer (atsc) */ +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_dee1601_frontend_attach, +- .tuner_attach = cxusb_dee1601_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x04, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_bluebird_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .rc.legacy = { +- .rc_interval = 150, +- .rc_map_table = rc_map_dvico_mce_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 3, +- .devices = { +- { "DViCO FusionHDTV DVB-T Dual USB", +- { &cxusb_table[3], NULL }, +- { &cxusb_table[4], NULL }, +- }, +- { "DigitalNow DVB-T Dual USB", +- { &cxusb_table[9], NULL }, +- { &cxusb_table[10], NULL }, +- }, +- { "DViCO FusionHDTV DVB-T Dual Digital 2", +- { &cxusb_table[11], NULL }, +- { &cxusb_table[12], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-bluebird-01.fw", +- .download_firmware = bluebird_patch_dvico_firmware_download, +- /* use usb alt setting 0 for EP4 transfer (dvb-t), +- use usb alt setting 7 for EP2 transfer (atsc) */ +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_mt352_frontend_attach, +- .tuner_attach = cxusb_lgz201_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x04, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- .power_ctrl = cxusb_bluebird_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_portable_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T USB (LGZ201)", +- { &cxusb_table[5], NULL }, +- { &cxusb_table[6], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-bluebird-01.fw", +- .download_firmware = bluebird_patch_dvico_firmware_download, +- /* use usb alt setting 0 for EP4 transfer (dvb-t), +- use usb alt setting 7 for EP2 transfer (atsc) */ +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_mt352_frontend_attach, +- .tuner_attach = cxusb_dtt7579_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x04, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- .power_ctrl = cxusb_bluebird_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_portable_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T USB (TH7579)", +- { &cxusb_table[7], NULL }, +- { &cxusb_table[8], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_dualdig4_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_dualdig4_frontend_attach, +- .tuner_attach = cxusb_dvico_xc3028_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_mce_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), +- .rc_query = cxusb_bluebird2_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T Dual Digital 4", +- { NULL }, +- { &cxusb_table[13], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .identify_state = bluebird_fx2_identify_state, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_nano2_frontend_attach, +- .tuner_attach = cxusb_dvico_xc3028_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_nano2_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_portable_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), +- .rc_query = cxusb_bluebird2_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T NANO2", +- { NULL }, +- { &cxusb_table[14], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-bluebird-02.fw", +- .download_firmware = bluebird_patch_dvico_firmware_download, +- .identify_state = bluebird_fx2_identify_state, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_nano2_frontend_attach, +- .tuner_attach = cxusb_dvico_xc3028_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_nano2_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_portable_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_portable_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T NANO2 w/o firmware", +- { &cxusb_table[14], NULL }, +- { &cxusb_table[15], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_aver_a868r_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_aver_streaming_ctrl, +- .frontend_attach = cxusb_aver_lgdt3303_frontend_attach, +- .tuner_attach = cxusb_mxl5003s_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x04, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- .power_ctrl = cxusb_aver_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "AVerMedia AVerTVHD Volar (A868R)", +- { NULL }, +- { &cxusb_table[16], NULL }, +- }, +- } +-}; +- +-static +-struct dvb_usb_device_properties cxusb_bluebird_dualdig4_rev2_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_streaming_ctrl, +- .frontend_attach = cxusb_dualdig4_rev2_frontend_attach, +- .tuner_attach = cxusb_dualdig4_rev2_tuner_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_bluebird_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_dvico_mce_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dvico_mce_table), +- .rc_query = cxusb_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DViCO FusionHDTV DVB-T Dual Digital 4 (rev 2)", +- { NULL }, +- { &cxusb_table[17], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, +- .frontend_attach = cxusb_d680_dmb_frontend_attach, +- .tuner_attach = cxusb_d680_dmb_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_d680_dmb_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_d680_dmb_table, +- .rc_map_size = ARRAY_SIZE(rc_map_d680_dmb_table), +- .rc_query = cxusb_d680_dmb_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { +- "Conexant DMB-TH Stick", +- { NULL }, +- { &cxusb_table[18], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct cxusb_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, +- .frontend_attach = cxusb_mygica_d689_frontend_attach, +- .tuner_attach = cxusb_mygica_d689_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- }, +- }, +- +- .power_ctrl = cxusb_d680_dmb_power_ctrl, +- +- .i2c_algo = &cxusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_d680_dmb_table, +- .rc_map_size = ARRAY_SIZE(rc_map_d680_dmb_table), +- .rc_query = cxusb_d680_dmb_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { +- "Mygica D689 DMB-TH", +- { NULL }, +- { &cxusb_table[19], NULL }, +- }, +- } +-}; +- +-static struct usb_driver cxusb_driver = { +- .name = "dvb_usb_cxusb", +- .probe = cxusb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = cxusb_table, +-}; +- +-module_usb_driver(cxusb_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_AUTHOR("Chris Pascoe "); +-MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); +-MODULE_VERSION("1.0-alpha"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/cxusb.h b/drivers/media/dvb/dvb-usb/cxusb.h +deleted file mode 100644 +index 1a51eaf..0000000 +--- a/drivers/media/dvb/dvb-usb/cxusb.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-#ifndef _DVB_USB_CXUSB_H_ +-#define _DVB_USB_CXUSB_H_ +- +-#define DVB_USB_LOG_PREFIX "cxusb" +-#include "dvb-usb.h" +- +-/* usb commands - some of it are guesses, don't have a reference yet */ +-#define CMD_BLUEBIRD_GPIO_RW 0x05 +- +-#define CMD_I2C_WRITE 0x08 +-#define CMD_I2C_READ 0x09 +- +-#define CMD_GPIO_READ 0x0d +-#define CMD_GPIO_WRITE 0x0e +-#define GPIO_TUNER 0x02 +- +-#define CMD_POWER_OFF 0xdc +-#define CMD_POWER_ON 0xde +- +-#define CMD_STREAMING_ON 0x36 +-#define CMD_STREAMING_OFF 0x37 +- +-#define CMD_AVER_STREAM_ON 0x18 +-#define CMD_AVER_STREAM_OFF 0x19 +- +-#define CMD_GET_IR_CODE 0x47 +- +-#define CMD_ANALOG 0x50 +-#define CMD_DIGITAL 0x51 +- +-struct cxusb_state { +- u8 gpio_write_state[3]; +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h +deleted file mode 100644 +index 7de125c..0000000 +--- a/drivers/media/dvb/dvb-usb/dib0700.h ++++ /dev/null +@@ -1,75 +0,0 @@ +-/* Linux driver for devices based on the DiBcom DiB0700 USB bridge +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * Copyright (C) 2005-6 DiBcom, SA +- */ +-#ifndef _DIB0700_H_ +-#define _DIB0700_H_ +- +-#define DVB_USB_LOG_PREFIX "dib0700" +-#include "dvb-usb.h" +- +-#include "dib07x0.h" +- +-extern int dvb_usb_dib0700_debug; +-#define deb_info(args...) dprintk(dvb_usb_dib0700_debug,0x01,args) +-#define deb_fw(args...) dprintk(dvb_usb_dib0700_debug,0x02,args) +-#define deb_fwdata(args...) dprintk(dvb_usb_dib0700_debug,0x04,args) +-#define deb_data(args...) dprintk(dvb_usb_dib0700_debug,0x08,args) +- +-#define REQUEST_SET_USB_XFER_LEN 0x0 /* valid only for firmware version */ +- /* higher than 1.21 */ +-#define REQUEST_I2C_READ 0x2 +-#define REQUEST_I2C_WRITE 0x3 +-#define REQUEST_POLL_RC 0x4 /* deprecated in firmware v1.20 */ +-#define REQUEST_JUMPRAM 0x8 +-#define REQUEST_SET_CLOCK 0xB +-#define REQUEST_SET_GPIO 0xC +-#define REQUEST_ENABLE_VIDEO 0xF +- // 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog) +- // 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1) +- // 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " ) +-#define REQUEST_SET_I2C_PARAM 0x10 +-#define REQUEST_SET_RC 0x11 +-#define REQUEST_NEW_I2C_READ 0x12 +-#define REQUEST_NEW_I2C_WRITE 0x13 +-#define REQUEST_GET_VERSION 0x15 +- +-struct dib0700_state { +- u8 channel_state; +- u16 mt2060_if1[2]; +- u8 rc_toggle; +- u8 rc_counter; +- u8 is_dib7000pc; +- u8 fw_use_new_i2c_api; +- u8 disable_streaming_master_mode; +- u32 fw_version; +- u32 nb_packet_buffer_size; +- int (*read_status)(struct dvb_frontend *, fe_status_t *); +- int (*sleep)(struct dvb_frontend* fe); +- u8 buf[255]; +-}; +- +-extern int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, +- u32 *romversion, u32 *ramversion, u32 *fwtype); +-extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); +-extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); +-extern int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen); +-extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); +-extern int dib0700_rc_setup(struct dvb_usb_device *d); +-extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); +-extern struct i2c_algorithm dib0700_i2c_algo; +-extern int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, int *cold); +-extern int dib0700_change_protocol(struct rc_dev *dev, u64 rc_type); +-extern int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz); +- +-extern int dib0700_device_count; +-extern int dvb_usb_dib0700_ir_proto; +-extern struct dvb_usb_device_properties dib0700_devices[]; +-extern struct usb_device_id dib0700_usb_id_table[]; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c +deleted file mode 100644 +index 070e82a..0000000 +--- a/drivers/media/dvb/dvb-usb/dib0700_core.c ++++ /dev/null +@@ -1,841 +0,0 @@ +-/* Linux driver for devices based on the DiBcom DiB0700 USB bridge +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * Copyright (C) 2005-6 DiBcom, SA +- */ +-#include "dib0700.h" +- +-/* debug */ +-int dvb_usb_dib0700_debug; +-module_param_named(debug,dvb_usb_dib0700_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=fw,4=fwdata,8=data (or-able))." DVB_USB_DEBUG_STATUS); +- +-static int nb_packet_buffer_size = 21; +-module_param(nb_packet_buffer_size, int, 0644); +-MODULE_PARM_DESC(nb_packet_buffer_size, +- "Set the dib0700 driver data buffer size. This parameter " +- "corresponds to the number of TS packets. The actual size of " +- "the data buffer corresponds to this parameter " +- "multiplied by 188 (default: 21)"); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +- +-int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, +- u32 *romversion, u32 *ramversion, u32 *fwtype) +-{ +- struct dib0700_state *st = d->priv; +- int ret; +- +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), +- REQUEST_GET_VERSION, +- USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, +- st->buf, 16, USB_CTRL_GET_TIMEOUT); +- if (hwversion != NULL) +- *hwversion = (st->buf[0] << 24) | (st->buf[1] << 16) | +- (st->buf[2] << 8) | st->buf[3]; +- if (romversion != NULL) +- *romversion = (st->buf[4] << 24) | (st->buf[5] << 16) | +- (st->buf[6] << 8) | st->buf[7]; +- if (ramversion != NULL) +- *ramversion = (st->buf[8] << 24) | (st->buf[9] << 16) | +- (st->buf[10] << 8) | st->buf[11]; +- if (fwtype != NULL) +- *fwtype = (st->buf[12] << 24) | (st->buf[13] << 16) | +- (st->buf[14] << 8) | st->buf[15]; +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +- +-/* expecting rx buffer: request data[0] data[1] ... data[2] */ +-static int dib0700_ctrl_wr(struct dvb_usb_device *d, u8 *tx, u8 txlen) +-{ +- int status; +- +- deb_data(">>> "); +- debug_dump(tx, txlen, deb_data); +- +- status = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev,0), +- tx[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, tx, txlen, +- USB_CTRL_GET_TIMEOUT); +- +- if (status != txlen) +- deb_data("ep 0 write error (status = %d, len: %d)\n",status,txlen); +- +- return status < 0 ? status : 0; +-} +- +-/* expecting tx buffer: request data[0] ... data[n] (n <= 4) */ +-int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +-{ +- u16 index, value; +- int status; +- +- if (txlen < 2) { +- err("tx buffer length is smaller than 2. Makes no sense."); +- return -EINVAL; +- } +- if (txlen > 4) { +- err("tx buffer length is larger than 4. Not supported."); +- return -EINVAL; +- } +- +- deb_data(">>> "); +- debug_dump(tx,txlen,deb_data); +- +- value = ((txlen - 2) << 8) | tx[1]; +- index = 0; +- if (txlen > 2) +- index |= (tx[2] << 8); +- if (txlen > 3) +- index |= tx[3]; +- +- status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0], +- USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen, +- USB_CTRL_GET_TIMEOUT); +- +- if (status < 0) +- deb_info("ep 0 read error (status = %d)\n",status); +- +- deb_data("<<< "); +- debug_dump(rx, rxlen, deb_data); +- +- return status; /* length in case of success */ +-} +- +-int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val) +-{ +- struct dib0700_state *st = d->priv; +- int ret; +- +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_SET_GPIO; +- st->buf[1] = gpio; +- st->buf[2] = ((gpio_dir & 0x01) << 7) | ((gpio_val & 0x01) << 6); +- +- ret = dib0700_ctrl_wr(d, st->buf, 3); +- +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +- +-static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) +-{ +- struct dib0700_state *st = d->priv; +- int ret; +- +- if (st->fw_version >= 0x10201) { +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_SET_USB_XFER_LEN; +- st->buf[1] = (nb_ts_packets >> 8) & 0xff; +- st->buf[2] = nb_ts_packets & 0xff; +- +- deb_info("set the USB xfer len to %i Ts packet\n", nb_ts_packets); +- +- ret = dib0700_ctrl_wr(d, st->buf, 3); +- mutex_unlock(&d->usb_mutex); +- } else { +- deb_info("this firmware does not allow to change the USB xfer len\n"); +- ret = -EIO; +- } +- +- return ret; +-} +- +-/* +- * I2C master xfer function (supported in 1.20 firmware) +- */ +-static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, +- int num) +-{ +- /* The new i2c firmware messages are more reliable and in particular +- properly support i2c read calls not preceded by a write */ +- +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct dib0700_state *st = d->priv; +- uint8_t bus_mode = 1; /* 0=eeprom bus, 1=frontend bus */ +- uint8_t gen_mode = 0; /* 0=master i2c, 1=gpio i2c */ +- uint8_t en_start = 0; +- uint8_t en_stop = 0; +- int result, i; +- +- /* Ensure nobody else hits the i2c bus while we're sending our +- sequence of messages, (such as the remote control thread) */ +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- if (i == 0) { +- /* First message in the transaction */ +- en_start = 1; +- } else if (!(msg[i].flags & I2C_M_NOSTART)) { +- /* Device supports repeated-start */ +- en_start = 1; +- } else { +- /* Not the first packet and device doesn't support +- repeated start */ +- en_start = 0; +- } +- if (i == (num - 1)) { +- /* Last message in the transaction */ +- en_stop = 1; +- } +- +- if (msg[i].flags & I2C_M_RD) { +- /* Read request */ +- u16 index, value; +- uint8_t i2c_dest; +- +- i2c_dest = (msg[i].addr << 1); +- value = ((en_start << 7) | (en_stop << 6) | +- (msg[i].len & 0x3F)) << 8 | i2c_dest; +- /* I2C ctrl + FE bus; */ +- index = ((gen_mode << 6) & 0xC0) | +- ((bus_mode << 4) & 0x30); +- +- result = usb_control_msg(d->udev, +- usb_rcvctrlpipe(d->udev, 0), +- REQUEST_NEW_I2C_READ, +- USB_TYPE_VENDOR | USB_DIR_IN, +- value, index, msg[i].buf, +- msg[i].len, +- USB_CTRL_GET_TIMEOUT); +- if (result < 0) { +- deb_info("i2c read error (status = %d)\n", result); +- break; +- } +- +- deb_data("<<< "); +- debug_dump(msg[i].buf, msg[i].len, deb_data); +- +- } else { +- /* Write request */ +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- st->buf[0] = REQUEST_NEW_I2C_WRITE; +- st->buf[1] = msg[i].addr << 1; +- st->buf[2] = (en_start << 7) | (en_stop << 6) | +- (msg[i].len & 0x3F); +- /* I2C ctrl + FE bus; */ +- st->buf[3] = ((gen_mode << 6) & 0xC0) | +- ((bus_mode << 4) & 0x30); +- /* The Actual i2c payload */ +- memcpy(&st->buf[4], msg[i].buf, msg[i].len); +- +- deb_data(">>> "); +- debug_dump(st->buf, msg[i].len + 4, deb_data); +- +- result = usb_control_msg(d->udev, +- usb_sndctrlpipe(d->udev, 0), +- REQUEST_NEW_I2C_WRITE, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- 0, 0, st->buf, msg[i].len + 4, +- USB_CTRL_GET_TIMEOUT); +- mutex_unlock(&d->usb_mutex); +- if (result < 0) { +- deb_info("i2c write error (status = %d)\n", result); +- break; +- } +- } +- } +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-/* +- * I2C master xfer function (pre-1.20 firmware) +- */ +-static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, +- struct i2c_msg *msg, int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct dib0700_state *st = d->priv; +- int i,len; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- for (i = 0; i < num; i++) { +- /* fill in the address */ +- st->buf[1] = msg[i].addr << 1; +- /* fill the buffer */ +- memcpy(&st->buf[2], msg[i].buf, msg[i].len); +- +- /* write/read request */ +- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { +- st->buf[0] = REQUEST_I2C_READ; +- st->buf[1] |= 1; +- +- /* special thing in the current firmware: when length is zero the read-failed */ +- len = dib0700_ctrl_rd(d, st->buf, msg[i].len + 2, +- msg[i+1].buf, msg[i+1].len); +- if (len <= 0) { +- deb_info("I2C read failed on address 0x%02x\n", +- msg[i].addr); +- break; +- } +- +- msg[i+1].len = len; +- +- i++; +- } else { +- st->buf[0] = REQUEST_I2C_WRITE; +- if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0) +- break; +- } +- } +- mutex_unlock(&d->usb_mutex); +- mutex_unlock(&d->i2c_mutex); +- +- return i; +-} +- +-static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct dib0700_state *st = d->priv; +- +- if (st->fw_use_new_i2c_api == 1) { +- /* User running at least fw 1.20 */ +- return dib0700_i2c_xfer_new(adap, msg, num); +- } else { +- /* Use legacy calls */ +- return dib0700_i2c_xfer_legacy(adap, msg, num); +- } +-} +- +-static u32 dib0700_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-struct i2c_algorithm dib0700_i2c_algo = { +- .master_xfer = dib0700_i2c_xfer, +- .functionality = dib0700_i2c_func, +-}; +- +-int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, int *cold) +-{ +- s16 ret; +- u8 *b; +- +- b = kmalloc(16, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- +- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +- REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT); +- +- deb_info("FW GET_VERSION length: %d\n",ret); +- +- *cold = ret <= 0; +- deb_info("cold: %d\n", *cold); +- +- kfree(b); +- return 0; +-} +- +-static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, +- u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv, +- u16 pll_loopdiv, u16 free_div, u16 dsuScaler) +-{ +- struct dib0700_state *st = d->priv; +- int ret; +- +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_SET_CLOCK; +- st->buf[1] = (en_pll << 7) | (pll_src << 6) | +- (pll_range << 5) | (clock_gpio3 << 4); +- st->buf[2] = (pll_prediv >> 8) & 0xff; /* MSB */ +- st->buf[3] = pll_prediv & 0xff; /* LSB */ +- st->buf[4] = (pll_loopdiv >> 8) & 0xff; /* MSB */ +- st->buf[5] = pll_loopdiv & 0xff; /* LSB */ +- st->buf[6] = (free_div >> 8) & 0xff; /* MSB */ +- st->buf[7] = free_div & 0xff; /* LSB */ +- st->buf[8] = (dsuScaler >> 8) & 0xff; /* MSB */ +- st->buf[9] = dsuScaler & 0xff; /* LSB */ +- +- ret = dib0700_ctrl_wr(d, st->buf, 10); +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) +-{ +- struct dib0700_state *st = d->priv; +- u16 divider; +- int ret; +- +- if (scl_kHz == 0) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_SET_I2C_PARAM; +- divider = (u16) (30000 / scl_kHz); +- st->buf[1] = 0; +- st->buf[2] = (u8) (divider >> 8); +- st->buf[3] = (u8) (divider & 0xff); +- divider = (u16) (72000 / scl_kHz); +- st->buf[4] = (u8) (divider >> 8); +- st->buf[5] = (u8) (divider & 0xff); +- divider = (u16) (72000 / scl_kHz); /* clock: 72MHz */ +- st->buf[6] = (u8) (divider >> 8); +- st->buf[7] = (u8) (divider & 0xff); +- +- deb_info("setting I2C speed: %04x %04x %04x (%d kHz).", +- (st->buf[2] << 8) | (st->buf[3]), (st->buf[4] << 8) | +- st->buf[5], (st->buf[6] << 8) | st->buf[7], scl_kHz); +- +- ret = dib0700_ctrl_wr(d, st->buf, 8); +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +- +-int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) +-{ +- switch (clk_MHz) { +- case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break; +- default: return -EINVAL; +- } +- return 0; +-} +- +-static int dib0700_jumpram(struct usb_device *udev, u32 address) +-{ +- int ret = 0, actlen; +- u8 *buf; +- +- buf = kmalloc(8, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; +- buf[0] = REQUEST_JUMPRAM; +- buf[1] = 0; +- buf[2] = 0; +- buf[3] = 0; +- buf[4] = (address >> 24) & 0xff; +- buf[5] = (address >> 16) & 0xff; +- buf[6] = (address >> 8) & 0xff; +- buf[7] = address & 0xff; +- +- if ((ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x01),buf,8,&actlen,1000)) < 0) { +- deb_fw("jumpram to 0x%x failed\n",address); +- goto out; +- } +- if (actlen != 8) { +- deb_fw("jumpram to 0x%x failed\n",address); +- ret = -EIO; +- goto out; +- } +-out: +- kfree(buf); +- return ret; +-} +- +-int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw) +-{ +- struct hexline hx; +- int pos = 0, ret, act_len, i, adap_num; +- u8 *buf; +- u32 fw_version; +- +- buf = kmalloc(260, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; +- +- while ((ret = dvb_usb_get_hexline(fw, &hx, &pos)) > 0) { +- deb_fwdata("writing to address 0x%08x (buffer: 0x%02x %02x)\n", +- hx.addr, hx.len, hx.chk); +- +- buf[0] = hx.len; +- buf[1] = (hx.addr >> 8) & 0xff; +- buf[2] = hx.addr & 0xff; +- buf[3] = hx.type; +- memcpy(&buf[4],hx.data,hx.len); +- buf[4+hx.len] = hx.chk; +- +- ret = usb_bulk_msg(udev, +- usb_sndbulkpipe(udev, 0x01), +- buf, +- hx.len + 5, +- &act_len, +- 1000); +- +- if (ret < 0) { +- err("firmware download failed at %d with %d",pos,ret); +- goto out; +- } +- } +- +- if (ret == 0) { +- /* start the firmware */ +- if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) { +- info("firmware started successfully."); +- msleep(500); +- } +- } else +- ret = -EIO; +- +- /* the number of ts packet has to be at least 1 */ +- if (nb_packet_buffer_size < 1) +- nb_packet_buffer_size = 1; +- +- /* get the fimware version */ +- usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +- REQUEST_GET_VERSION, +- USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, +- buf, 16, USB_CTRL_GET_TIMEOUT); +- fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11]; +- +- /* set the buffer size - DVB-USB is allocating URB buffers +- * only after the firwmare download was successful */ +- for (i = 0; i < dib0700_device_count; i++) { +- for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters; +- adap_num++) { +- if (fw_version >= 0x10201) { +- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 188*nb_packet_buffer_size; +- } else { +- /* for fw version older than 1.20.1, +- * the buffersize has to be n times 512 */ +- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = ((188*nb_packet_buffer_size+188/2)/512)*512; +- if (dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize < 512) +- dib0700_devices[i].adapter[adap_num].fe[0].stream.u.bulk.buffersize = 512; +- } +- } +- } +-out: +- kfree(buf); +- return ret; +-} +- +-int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct dib0700_state *st = adap->dev->priv; +- int ret; +- +- if ((onoff != 0) && (st->fw_version >= 0x10201)) { +- /* for firmware later than 1.20.1, +- * the USB xfer length can be set */ +- ret = dib0700_set_usb_xfer_len(adap->dev, +- st->nb_packet_buffer_size); +- if (ret < 0) { +- deb_info("can not set the USB xfer len\n"); +- return ret; +- } +- } +- +- if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_ENABLE_VIDEO; +- /* this bit gives a kind of command, +- * rather than enabling something or not */ +- st->buf[1] = (onoff << 4) | 0x00; +- +- if (st->disable_streaming_master_mode == 1) +- st->buf[2] = 0x00; +- else +- st->buf[2] = 0x01 << 4; /* Master mode */ +- +- st->buf[3] = 0x00; +- +- deb_info("modifying (%d) streaming state for %d\n", onoff, adap->id); +- +- st->channel_state &= ~0x3; +- if ((adap->fe_adap[0].stream.props.endpoint != 2) +- && (adap->fe_adap[0].stream.props.endpoint != 3)) { +- deb_info("the endpoint number (%i) is not correct, use the adapter id instead", adap->fe_adap[0].stream.props.endpoint); +- if (onoff) +- st->channel_state |= 1 << (adap->id); +- else +- st->channel_state |= 1 << ~(adap->id); +- } else { +- if (onoff) +- st->channel_state |= 1 << (adap->fe_adap[0].stream.props.endpoint-2); +- else +- st->channel_state |= 1 << (3-adap->fe_adap[0].stream.props.endpoint); +- } +- +- st->buf[2] |= st->channel_state; +- +- deb_info("data for streaming: %x %x\n", st->buf[1], st->buf[2]); +- +- ret = dib0700_ctrl_wr(adap->dev, st->buf, 4); +- mutex_unlock(&adap->dev->usb_mutex); +- +- return ret; +-} +- +-int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) +-{ +- struct dvb_usb_device *d = rc->priv; +- struct dib0700_state *st = d->priv; +- int new_proto, ret; +- +- if (mutex_lock_interruptible(&d->usb_mutex) < 0) { +- err("could not acquire lock"); +- return 0; +- } +- +- st->buf[0] = REQUEST_SET_RC; +- st->buf[1] = 0; +- st->buf[2] = 0; +- +- /* Set the IR mode */ +- if (rc_type == RC_TYPE_RC5) +- new_proto = 1; +- else if (rc_type == RC_TYPE_NEC) +- new_proto = 0; +- else if (rc_type == RC_TYPE_RC6) { +- if (st->fw_version < 0x10200) { +- ret = -EINVAL; +- goto out; +- } +- +- new_proto = 2; +- } else { +- ret = -EINVAL; +- goto out; +- } +- +- st->buf[1] = new_proto; +- +- ret = dib0700_ctrl_wr(d, st->buf, 3); +- if (ret < 0) { +- err("ir protocol setup failed"); +- goto out; +- } +- +- d->props.rc.core.protocol = rc_type; +- +-out: +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +- +-/* Number of keypresses to ignore before start repeating */ +-#define RC_REPEAT_DELAY_V1_20 10 +- +-/* This is the structure of the RC response packet starting in firmware 1.20 */ +-struct dib0700_rc_response { +- u8 report_id; +- u8 data_state; +- union { +- u16 system16; +- struct { +- u8 not_system; +- u8 system; +- }; +- }; +- u8 data; +- u8 not_data; +-}; +-#define RC_MSG_SIZE_V1_20 6 +- +-static void dib0700_rc_urb_completion(struct urb *purb) +-{ +- struct dvb_usb_device *d = purb->context; +- struct dib0700_rc_response *poll_reply; +- u32 uninitialized_var(keycode); +- u8 toggle; +- +- deb_info("%s()\n", __func__); +- if (d == NULL) +- return; +- +- if (d->rc_dev == NULL) { +- /* This will occur if disable_rc_polling=1 */ +- usb_free_urb(purb); +- return; +- } +- +- poll_reply = purb->transfer_buffer; +- +- if (purb->status < 0) { +- deb_info("discontinuing polling\n"); +- usb_free_urb(purb); +- return; +- } +- +- if (purb->actual_length != RC_MSG_SIZE_V1_20) { +- deb_info("malformed rc msg size=%d\n", purb->actual_length); +- goto resubmit; +- } +- +- deb_data("IR ID = %02X state = %02X System = %02X %02X Cmd = %02X %02X (len %d)\n", +- poll_reply->report_id, poll_reply->data_state, +- poll_reply->system, poll_reply->not_system, +- poll_reply->data, poll_reply->not_data, +- purb->actual_length); +- +- switch (d->props.rc.core.protocol) { +- case RC_TYPE_NEC: +- toggle = 0; +- +- /* NEC protocol sends repeat code as 0 0 0 FF */ +- if ((poll_reply->system == 0x00) && (poll_reply->data == 0x00) +- && (poll_reply->not_data == 0xff)) { +- poll_reply->data_state = 2; +- break; +- } +- +- if ((poll_reply->system ^ poll_reply->not_system) != 0xff) { +- deb_data("NEC extended protocol\n"); +- /* NEC extended code - 24 bits */ +- keycode = be16_to_cpu(poll_reply->system16) << 8 | poll_reply->data; +- } else { +- deb_data("NEC normal protocol\n"); +- /* normal NEC code - 16 bits */ +- keycode = poll_reply->system << 8 | poll_reply->data; +- } +- +- break; +- default: +- deb_data("RC5 protocol\n"); +- /* RC5 Protocol */ +- toggle = poll_reply->report_id; +- keycode = poll_reply->system << 8 | poll_reply->data; +- +- break; +- } +- +- if ((poll_reply->data + poll_reply->not_data) != 0xff) { +- /* Key failed integrity check */ +- err("key failed integrity check: %04x %02x %02x", +- poll_reply->system, +- poll_reply->data, poll_reply->not_data); +- goto resubmit; +- } +- +- rc_keydown(d->rc_dev, keycode, toggle); +- +-resubmit: +- /* Clean the buffer before we requeue */ +- memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20); +- +- /* Requeue URB */ +- usb_submit_urb(purb, GFP_ATOMIC); +-} +- +-int dib0700_rc_setup(struct dvb_usb_device *d) +-{ +- struct dib0700_state *st = d->priv; +- struct urb *purb; +- int ret; +- +- /* Poll-based. Don't initialize bulk mode */ +- if (st->fw_version < 0x10200) +- return 0; +- +- /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ +- purb = usb_alloc_urb(0, GFP_KERNEL); +- if (purb == NULL) { +- err("rc usb alloc urb failed\n"); +- return -ENOMEM; +- } +- +- purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL); +- if (purb->transfer_buffer == NULL) { +- err("rc kzalloc failed\n"); +- usb_free_urb(purb); +- return -ENOMEM; +- } +- +- purb->status = -EINPROGRESS; +- usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1), +- purb->transfer_buffer, RC_MSG_SIZE_V1_20, +- dib0700_rc_urb_completion, d); +- +- ret = usb_submit_urb(purb, GFP_ATOMIC); +- if (ret) +- err("rc submit urb failed\n"); +- +- return ret; +-} +- +-static int dib0700_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int i; +- struct dvb_usb_device *dev; +- +- for (i = 0; i < dib0700_device_count; i++) +- if (dvb_usb_device_init(intf, &dib0700_devices[i], THIS_MODULE, +- &dev, adapter_nr) == 0) { +- struct dib0700_state *st = dev->priv; +- u32 hwversion, romversion, fw_version, fwtype; +- +- dib0700_get_version(dev, &hwversion, &romversion, +- &fw_version, &fwtype); +- +- deb_info("Firmware version: %x, %d, 0x%x, %d\n", +- hwversion, romversion, fw_version, fwtype); +- +- st->fw_version = fw_version; +- st->nb_packet_buffer_size = (u32)nb_packet_buffer_size; +- +- /* Disable polling mode on newer firmwares */ +- if (st->fw_version >= 0x10200) +- dev->props.rc.core.bulk_mode = true; +- else +- dev->props.rc.core.bulk_mode = false; +- +- dib0700_rc_setup(dev); +- +- return 0; +- } +- +- return -ENODEV; +-} +- +-static struct usb_driver dib0700_driver = { +- .name = "dvb_usb_dib0700", +- .probe = dib0700_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dib0700_usb_id_table, +-}; +- +-module_usb_driver(dib0700_driver); +- +-MODULE_FIRMWARE("dvb-usb-dib0700-1.20.fw"); +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for devices based on DiBcom DiB0700 - USB bridge"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c +deleted file mode 100644 +index f9e966a..0000000 +--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c ++++ /dev/null +@@ -1,4808 +0,0 @@ +-/* Linux driver for devices based on the DiBcom DiB0700 USB bridge +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * Copyright (C) 2005-9 DiBcom, SA et al +- */ +-#include "dib0700.h" +- +-#include "dib3000mc.h" +-#include "dib7000m.h" +-#include "dib7000p.h" +-#include "dib8000.h" +-#include "dib9000.h" +-#include "mt2060.h" +-#include "mt2266.h" +-#include "tuner-xc2028.h" +-#include "xc5000.h" +-#include "xc4000.h" +-#include "s5h1411.h" +-#include "dib0070.h" +-#include "dib0090.h" +-#include "lgdt3305.h" +-#include "mxl5007t.h" +- +-static int force_lna_activation; +-module_param(force_lna_activation, int, 0644); +-MODULE_PARM_DESC(force_lna_activation, "force the activation of Low-Noise-Amplifyer(s) (LNA), " +- "if applicable for the device (default: 0=automatic/off)."); +- +-struct dib0700_adapter_state { +- int (*set_param_save) (struct dvb_frontend *); +- const struct firmware *frontend_firmware; +-}; +- +-/* Hauppauge Nova-T 500 (aka Bristol) +- * has a LNA on GPIO0 which is enabled by setting 1 */ +-static struct mt2060_config bristol_mt2060_config[2] = { +- { +- .i2c_address = 0x60, +- .clock_out = 3, +- }, { +- .i2c_address = 0x61, +- } +-}; +- +- +-static struct dibx000_agc_config bristol_dib3000p_mt2060_agc_config = { +- .band_caps = BAND_VHF | BAND_UHF, +- .setup = (1 << 8) | (5 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (2 << 0), +- +- .agc1_max = 42598, +- .agc1_min = 17694, +- .agc2_max = 45875, +- .agc2_min = 0, +- +- .agc1_pt1 = 0, +- .agc1_pt2 = 59, +- +- .agc1_slope1 = 0, +- .agc1_slope2 = 69, +- +- .agc2_pt1 = 0, +- .agc2_pt2 = 59, +- +- .agc2_slope1 = 111, +- .agc2_slope2 = 28, +-}; +- +-static struct dib3000mc_config bristol_dib3000mc_config[2] = { +- { .agc = &bristol_dib3000p_mt2060_agc_config, +- .max_time = 0x196, +- .ln_adc_level = 0x1cc7, +- .output_mpeg2_in_188_bytes = 1, +- }, +- { .agc = &bristol_dib3000p_mt2060_agc_config, +- .max_time = 0x196, +- .ln_adc_level = 0x1cc7, +- .output_mpeg2_in_188_bytes = 1, +- } +-}; +- +-static int bristol_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- if (adap->id == 0) { +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); +- +- if (force_lna_activation) +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- else +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0); +- +- if (dib3000mc_i2c_enumeration(&adap->dev->i2c_adap, 2, DEFAULT_DIB3000P_I2C_ADDRESS, bristol_dib3000mc_config) != 0) { +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); +- return -ENODEV; +- } +- } +- st->mt2060_if1[adap->id] = 1220; +- return (adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, +- (10 + adap->id) << 1, &bristol_dib3000mc_config[adap->id])) == NULL ? -ENODEV : 0; +-} +- +-static int eeprom_read(struct i2c_adapter *adap,u8 adrs,u8 *pval) +-{ +- struct i2c_msg msg[2] = { +- { .addr = 0x50, .flags = 0, .buf = &adrs, .len = 1 }, +- { .addr = 0x50, .flags = I2C_M_RD, .buf = pval, .len = 1 }, +- }; +- if (i2c_transfer(adap, msg, 2) != 2) return -EREMOTEIO; +- return 0; +-} +- +-static int bristol_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; +- struct i2c_adapter *tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); +- s8 a; +- int if1=1220; +- if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && +- adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_500_2)) { +- if (!eeprom_read(prim_i2c,0x59 + adap->id,&a)) if1=1220+a; +- } +- return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, +- &bristol_mt2060_config[adap->id], if1) == NULL ? +- -ENODEV : 0; +-} +- +-/* STK7700D: Pinnacle/Terratec/Hauppauge Dual DVB-T Diversity */ +- +-/* MT226x */ +-static struct dibx000_agc_config stk7700d_7000p_mt2266_agc_config[2] = { +- { +- BAND_UHF, +- +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- 1130, +- 21, +- +- 0, +- 118, +- +- 0, +- 3530, +- 1, +- 0, +- +- 65535, +- 33770, +- 65535, +- 23592, +- +- 0, +- 62, +- 255, +- 64, +- 64, +- 132, +- 192, +- 80, +- 80, +- +- 17, +- 27, +- 23, +- 51, +- +- 1, +- }, { +- BAND_VHF | BAND_LBAND, +- +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=1, P_agc_inv_pwm2=1, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (1 << 11) | (1 << 10) | (1 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), +- +- 2372, +- 21, +- +- 0, +- 118, +- +- 0, +- 3530, +- 1, +- 0, +- +- 65535, +- 0, +- 65535, +- 23592, +- +- 0, +- 128, +- 128, +- 128, +- 0, +- 128, +- 253, +- 81, +- 0, +- +- 17, +- 27, +- 23, +- 51, +- +- 1, +- } +-}; +- +-static struct dibx000_bandwidth_config stk7700d_mt2266_pll_config = { +- 60000, 30000, +- 1, 8, 3, 1, 0, +- 0, 0, 1, 1, 2, +- (3 << 14) | (1 << 12) | (524 << 0), +- 0, +- 20452225, +-}; +- +-static struct dib7000p_config stk7700d_dib7000p_mt2266_config[] = { +- { .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- +- .agc_config_count = 2, +- .agc = stk7700d_7000p_mt2266_agc_config, +- .bw = &stk7700d_mt2266_pll_config, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- }, +- { .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- +- .agc_config_count = 2, +- .agc = stk7700d_7000p_mt2266_agc_config, +- .bw = &stk7700d_mt2266_pll_config, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- } +-}; +- +-static struct mt2266_config stk7700d_mt2266_config[2] = { +- { .i2c_address = 0x60 +- }, +- { .i2c_address = 0x60 +- } +-}; +- +-static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (adap->id == 0) { +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- stk7700d_dib7000p_mt2266_config) +- != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); +- return -ENODEV; +- } +- } +- +- adap->fe_adap[0].fe = +- dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, +- 0x80 + (adap->id << 1), +- &stk7700d_dib7000p_mt2266_config[adap->id]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (adap->id == 0) { +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, +- stk7700d_dib7000p_mt2266_config) +- != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); +- return -ENODEV; +- } +- } +- +- adap->fe_adap[0].fe = +- dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, +- 0x80 + (adap->id << 1), +- &stk7700d_dib7000p_mt2266_config[adap->id]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int stk7700d_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *tun_i2c; +- tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- return dvb_attach(mt2266_attach, adap->fe_adap[0].fe, tun_i2c, +- &stk7700d_mt2266_config[adap->id]) == NULL ? -ENODEV : 0; +-} +- +-/* STK7700-PH: Digital/Analog Hybrid Tuner, e.h. Cinergy HT USB HE */ +-static struct dibx000_agc_config xc3028_agc_config = { +- BAND_VHF | BAND_UHF, /* band_caps */ +- +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, +- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | +- (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ +- +- 712, /* inv_gain */ +- 21, /* time_stabiliz */ +- +- 0, /* alpha_level */ +- 118, /* thlock */ +- +- 0, /* wbd_inv */ +- 2867, /* wbd_ref */ +- 0, /* wbd_sel */ +- 2, /* wbd_alpha */ +- +- 0, /* agc1_max */ +- 0, /* agc1_min */ +- 39718, /* agc2_max */ +- 9930, /* agc2_min */ +- 0, /* agc1_pt1 */ +- 0, /* agc1_pt2 */ +- 0, /* agc1_pt3 */ +- 0, /* agc1_slope1 */ +- 0, /* agc1_slope2 */ +- 0, /* agc2_pt1 */ +- 128, /* agc2_pt2 */ +- 29, /* agc2_slope1 */ +- 29, /* agc2_slope2 */ +- +- 17, /* alpha_mant */ +- 27, /* alpha_exp */ +- 23, /* beta_mant */ +- 51, /* beta_exp */ +- +- 1, /* perform_agc_softsplit */ +-}; +- +-/* PLL Configuration for COFDM BW_MHz = 8.00 with external clock = 30.00 */ +-static struct dibx000_bandwidth_config xc3028_bw_config = { +- 60000, 30000, /* internal, sampling */ +- 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ +- 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, +- modulo */ +- (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ +- (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ +- 20452225, /* timf */ +- 30000000, /* xtal_hz */ +-}; +- +-static struct dib7000p_config stk7700ph_dib7700_xc3028_config = { +- .output_mpeg2_in_188_bytes = 1, +- .tuner_is_baseband = 1, +- +- .agc_config_count = 1, +- .agc = &xc3028_agc_config, +- .bw = &xc3028_bw_config, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +-}; +- +-static int stk7700ph_xc3028_callback(void *ptr, int component, +- int command, int arg) +-{ +- struct dvb_usb_adapter *adap = ptr; +- +- switch (command) { +- case XC2028_TUNER_RESET: +- /* Send the tuner in then out of reset */ +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); msleep(10); +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- break; +- case XC2028_RESET_CLK: +- break; +- default: +- err("%s: unknown command %d, arg %d\n", __func__, +- command, arg); +- return -EINVAL; +- } +- return 0; +-} +- +-static struct xc2028_ctrl stk7700ph_xc3028_ctrl = { +- .fname = XC2028_DEFAULT_FIRMWARE, +- .max_len = 64, +- .demod = XC3028_FE_DIBCOM52, +-}; +- +-static struct xc2028_config stk7700ph_xc3028_config = { +- .i2c_addr = 0x61, +- .ctrl = &stk7700ph_xc3028_ctrl, +-}; +- +-static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct usb_device_descriptor *desc = &adap->dev->udev->descriptor; +- +- if (desc->idVendor == cpu_to_le16(USB_VID_PINNACLE) && +- desc->idProduct == cpu_to_le16(USB_PID_PINNACLE_EXPRESSCARD_320CX)) +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- else +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- msleep(10); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- &stk7700ph_dib7700_xc3028_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, +- &stk7700ph_dib7700_xc3028_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int stk7700ph_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *tun_i2c; +- +- tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, +- DIBX000_I2C_INTERFACE_TUNER, 1); +- +- stk7700ph_xc3028_config.i2c_adap = tun_i2c; +- +- /* FIXME: generalize & move to common area */ +- adap->fe_adap[0].fe->callback = stk7700ph_xc3028_callback; +- +- return dvb_attach(xc2028_attach, adap->fe_adap[0].fe, &stk7700ph_xc3028_config) +- == NULL ? -ENODEV : 0; +-} +- +-#define DEFAULT_RC_INTERVAL 50 +- +-static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; +- +-/* Number of keypresses to ignore before start repeating */ +-#define RC_REPEAT_DELAY 6 +- +-/* +- * This function is used only when firmware is < 1.20 version. Newer +- * firmwares use bulk mode, with functions implemented at dib0700_core, +- * at dib0700_rc_urb_completion() +- */ +-static int dib0700_rc_query_old_firmware(struct dvb_usb_device *d) +-{ +- u8 key[4]; +- u32 keycode; +- u8 toggle; +- int i; +- struct dib0700_state *st = d->priv; +- +- if (st->fw_version >= 0x10200) { +- /* For 1.20 firmware , We need to keep the RC polling +- callback so we can reuse the input device setup in +- dvb-usb-remote.c. However, the actual work is being done +- in the bulk URB completion handler. */ +- return 0; +- } +- +- i = dib0700_ctrl_rd(d, rc_request, 2, key, 4); +- if (i <= 0) { +- err("RC Query Failed"); +- return -1; +- } +- +- /* losing half of KEY_0 events from Philipps rc5 remotes.. */ +- if (key[0] == 0 && key[1] == 0 && key[2] == 0 && key[3] == 0) +- return 0; +- +- /* info("%d: %2X %2X %2X %2X",dvb_usb_dib0700_ir_proto,(int)key[3-2],(int)key[3-3],(int)key[3-1],(int)key[3]); */ +- +- dib0700_rc_setup(d); /* reset ir sensor data to prevent false events */ +- +- d->last_event = 0; +- switch (d->props.rc.core.protocol) { +- case RC_TYPE_NEC: +- /* NEC protocol sends repeat code as 0 0 0 FF */ +- if ((key[3-2] == 0x00) && (key[3-3] == 0x00) && +- (key[3] == 0xff)) +- keycode = d->last_event; +- else { +- keycode = key[3-2] << 8 | key[3-3]; +- d->last_event = keycode; +- } +- +- rc_keydown(d->rc_dev, keycode, 0); +- break; +- default: +- /* RC-5 protocol changes toggle bit on new keypress */ +- keycode = key[3-2] << 8 | key[3-3]; +- toggle = key[3-1]; +- rc_keydown(d->rc_dev, keycode, toggle); +- +- break; +- } +- return 0; +-} +- +-/* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ +-static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { +- BAND_UHF | BAND_VHF, +- +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), +- +- 712, +- 41, +- +- 0, +- 118, +- +- 0, +- 4095, +- 0, +- 0, +- +- 42598, +- 17694, +- 45875, +- 2621, +- 0, +- 76, +- 139, +- 52, +- 59, +- 107, +- 172, +- 57, +- 70, +- +- 21, +- 25, +- 28, +- 48, +- +- 1, +- { 0, +- 107, +- 51800, +- 24700 +- }, +-}; +- +-static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = { +- BAND_UHF | BAND_VHF, +- +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), +- +- 712, +- 41, +- +- 0, +- 118, +- +- 0, +- 4095, +- 0, +- 0, +- +- 42598, +- 16384, +- 42598, +- 0, +- +- 0, +- 137, +- 255, +- +- 0, +- 255, +- +- 0, +- 0, +- +- 0, +- 41, +- +- 15, +- 25, +- +- 28, +- 48, +- +- 0, +-}; +- +-static struct dibx000_bandwidth_config stk7700p_pll_config = { +- 60000, 30000, +- 1, 8, 3, 1, 0, +- 0, 0, 1, 1, 0, +- (3 << 14) | (1 << 12) | (524 << 0), +- 60258167, +- 20452225, +- 30000000, +-}; +- +-static struct dib7000m_config stk7700p_dib7000m_config = { +- .dvbt_mode = 1, +- .output_mpeg2_in_188_bytes = 1, +- .quartz_direct = 1, +- +- .agc_config_count = 1, +- .agc = &stk7700p_7000m_mt2060_agc_config, +- .bw = &stk7700p_pll_config, +- +- .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +-}; +- +-static struct dib7000p_config stk7700p_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &stk7700p_7000p_mt2060_agc_config, +- .bw = &stk7700p_pll_config, +- +- .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +-}; +- +-static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- /* unless there is no real power management in DVB - we leave the device on GPIO6 */ +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50); +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); +- dib0700_ctrl_clock(adap->dev, 72, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100); +- +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- st->mt2060_if1[0] = 1220; +- +- if (dib7000pc_detection(&adap->dev->i2c_adap)) { +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); +- st->is_dib7000pc = 1; +- } else +- adap->fe_adap[0].fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static struct mt2060_config stk7700p_mt2060_config = { +- 0x60 +-}; +- +-static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *prim_i2c = &adap->dev->i2c_adap; +- struct dib0700_state *st = adap->dev->priv; +- struct i2c_adapter *tun_i2c; +- s8 a; +- int if1=1220; +- if (adap->dev->udev->descriptor.idVendor == cpu_to_le16(USB_VID_HAUPPAUGE) && +- adap->dev->udev->descriptor.idProduct == cpu_to_le16(USB_PID_HAUPPAUGE_NOVA_T_STICK)) { +- if (!eeprom_read(prim_i2c,0x58,&a)) if1=1220+a; +- } +- if (st->is_dib7000pc) +- tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- else +- tun_i2c = dib7000m_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- +- return dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk7700p_mt2060_config, +- if1) == NULL ? -ENODEV : 0; +-} +- +-/* DIB7070 generic */ +-static struct dibx000_agc_config dib7070_agc_config = { +- BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- 600, +- 10, +- +- 0, +- 118, +- +- 0, +- 3530, +- 1, +- 5, +- +- 65535, +- 0, +- +- 65535, +- 0, +- +- 0, +- 40, +- 183, +- 206, +- 255, +- 72, +- 152, +- 88, +- 90, +- +- 17, +- 27, +- 23, +- 51, +- +- 0, +-}; +- +-static int dib7070_tuner_reset(struct dvb_frontend *fe, int onoff) +-{ +- deb_info("reset: %d", onoff); +- return dib7000p_set_gpio(fe, 8, 0, !onoff); +-} +- +-static int dib7070_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- deb_info("sleep: %d", onoff); +- return dib7000p_set_gpio(fe, 9, 0, onoff); +-} +- +-static struct dib0070_config dib7070p_dib0070_config[2] = { +- { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib7070_tuner_reset, +- .sleep = dib7070_tuner_sleep, +- .clock_khz = 12000, +- .clock_pad_drive = 4, +- .charge_pump = 2, +- }, { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib7070_tuner_reset, +- .sleep = dib7070_tuner_sleep, +- .clock_khz = 12000, +- .charge_pump = 2, +- } +-}; +- +-static struct dib0070_config dib7770p_dib0070_config = { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib7070_tuner_reset, +- .sleep = dib7070_tuner_sleep, +- .clock_khz = 12000, +- .clock_pad_drive = 0, +- .flip_chip = 1, +- .charge_pump = 2, +-}; +- +-static int dib7070_set_param_override(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- +- u16 offset; +- u8 band = BAND_OF_FREQUENCY(p->frequency/1000); +- switch (band) { +- case BAND_VHF: offset = 950; break; +- case BAND_UHF: +- default: offset = 550; break; +- } +- deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); +- dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); +- return state->set_param_save(fe); +-} +- +-static int dib7770_set_param_override(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- +- u16 offset; +- u8 band = BAND_OF_FREQUENCY(p->frequency/1000); +- switch (band) { +- case BAND_VHF: +- dib7000p_set_gpio(fe, 0, 0, 1); +- offset = 850; +- break; +- case BAND_UHF: +- default: +- dib7000p_set_gpio(fe, 0, 0, 0); +- offset = 250; +- break; +- } +- deb_info("WBD for DiB7000P: %d\n", offset + dib0070_wbd_offset(fe)); +- dib7000p_set_wbd_ref(fe, offset + dib0070_wbd_offset(fe)); +- return state->set_param_save(fe); +-} +- +-static int dib7770p_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, +- DIBX000_I2C_INTERFACE_TUNER, 1); +- +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, +- &dib7770p_dib0070_config) == NULL) +- return -ENODEV; +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7770_set_param_override; +- return 0; +-} +- +-static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- +- if (adap->id == 0) { +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[0]) == NULL) +- return -ENODEV; +- } else { +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, &dib7070p_dib0070_config[1]) == NULL) +- return -ENODEV; +- } +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7070_set_param_override; +- return 0; +-} +- +-static int stk7700p_pid_filter(struct dvb_usb_adapter *adapter, int index, +- u16 pid, int onoff) +-{ +- struct dib0700_state *st = adapter->dev->priv; +- if (st->is_dib7000pc) +- return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +- return dib7000m_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +-} +- +-static int stk7700p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +-{ +- struct dib0700_state *st = adapter->dev->priv; +- if (st->is_dib7000pc) +- return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +- return dib7000m_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +-} +- +-static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +-{ +- return dib7000p_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +-} +- +-static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +-{ +- return dib7000p_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +-} +- +-static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { +- 60000, 15000, +- 1, 20, 3, 1, 0, +- 0, 0, 1, 1, 2, +- (3 << 14) | (1 << 12) | (524 << 0), +- (0 << 25) | 0, +- 20452225, +- 12000000, +-}; +- +-static struct dib7000p_config dib7070p_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &dib7070_agc_config, +- .bw = &dib7070_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- .spur_protect = 1, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +-}; +- +-/* STK7070P */ +-static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct usb_device_descriptor *p = &adap->dev->udev->descriptor; +- if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && +- p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- else +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- &dib7070p_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, +- &dib7070p_dib7000p_config); +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-/* STK7770P */ +-static struct dib7000p_config dib7770p_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &dib7070_agc_config, +- .bw = &dib7070_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- .spur_protect = 1, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- .enable_current_mirror = 1, +- .disable_sample_and_hold = 0, +-}; +- +-static int stk7770p_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct usb_device_descriptor *p = &adap->dev->udev->descriptor; +- if (p->idVendor == cpu_to_le16(USB_VID_PINNACLE) && +- p->idProduct == cpu_to_le16(USB_PID_PINNACLE_PCTV72E)) +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- else +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- &dib7770p_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, +- &dib7770p_dib7000p_config); +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-/* DIB807x generic */ +-static struct dibx000_agc_config dib807x_agc_config[2] = { +- { +- BAND_VHF, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, +- * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, +- * P_agc_inv_pwm2=0,P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, +- * P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (7 << 11) | (0 << 10) | (0 << 9) | +- (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | +- (0 << 0), /* setup*/ +- +- 600, /* inv_gain*/ +- 10, /* time_stabiliz*/ +- +- 0, /* alpha_level*/ +- 118, /* thlock*/ +- +- 0, /* wbd_inv*/ +- 3530, /* wbd_ref*/ +- 1, /* wbd_sel*/ +- 5, /* wbd_alpha*/ +- +- 65535, /* agc1_max*/ +- 0, /* agc1_min*/ +- +- 65535, /* agc2_max*/ +- 0, /* agc2_min*/ +- +- 0, /* agc1_pt1*/ +- 40, /* agc1_pt2*/ +- 183, /* agc1_pt3*/ +- 206, /* agc1_slope1*/ +- 255, /* agc1_slope2*/ +- 72, /* agc2_pt1*/ +- 152, /* agc2_pt2*/ +- 88, /* agc2_slope1*/ +- 90, /* agc2_slope2*/ +- +- 17, /* alpha_mant*/ +- 27, /* alpha_exp*/ +- 23, /* beta_mant*/ +- 51, /* beta_exp*/ +- +- 0, /* perform_agc_softsplit*/ +- }, { +- BAND_UHF, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, +- * P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, +- * P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, +- * P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (1 << 11) | (0 << 10) | (0 << 9) | +- (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | +- (0 << 0), /* setup */ +- +- 600, /* inv_gain*/ +- 10, /* time_stabiliz*/ +- +- 0, /* alpha_level*/ +- 118, /* thlock*/ +- +- 0, /* wbd_inv*/ +- 3530, /* wbd_ref*/ +- 1, /* wbd_sel*/ +- 5, /* wbd_alpha*/ +- +- 65535, /* agc1_max*/ +- 0, /* agc1_min*/ +- +- 65535, /* agc2_max*/ +- 0, /* agc2_min*/ +- +- 0, /* agc1_pt1*/ +- 40, /* agc1_pt2*/ +- 183, /* agc1_pt3*/ +- 206, /* agc1_slope1*/ +- 255, /* agc1_slope2*/ +- 72, /* agc2_pt1*/ +- 152, /* agc2_pt2*/ +- 88, /* agc2_slope1*/ +- 90, /* agc2_slope2*/ +- +- 17, /* alpha_mant*/ +- 27, /* alpha_exp*/ +- 23, /* beta_mant*/ +- 51, /* beta_exp*/ +- +- 0, /* perform_agc_softsplit*/ +- } +-}; +- +-static struct dibx000_bandwidth_config dib807x_bw_config_12_mhz = { +- 60000, 15000, /* internal, sampling*/ +- 1, 20, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass*/ +- 0, 0, 1, 1, 2, /* misc: refdiv, bypclk_div, IO_CLK_en_core, +- ADClkSrc, modulo */ +- (3 << 14) | (1 << 12) | (599 << 0), /* sad_cfg: refsel, sel, freq_15k*/ +- (0 << 25) | 0, /* ifreq = 0.000000 MHz*/ +- 18179755, /* timf*/ +- 12000000, /* xtal_hz*/ +-}; +- +-static struct dib8000_config dib807x_dib8000_config[2] = { +- { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 2, +- .agc = dib807x_agc_config, +- .pll = &dib807x_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- +- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- .div_cfg = 1, +- .agc_control = &dib0070_ctrl_agc_filter, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .drives = 0x2d98, +- }, { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 2, +- .agc = dib807x_agc_config, +- .pll = &dib807x_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- +- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- .agc_control = &dib0070_ctrl_agc_filter, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .drives = 0x2d98, +- } +-}; +- +-static int dib80xx_tuner_reset(struct dvb_frontend *fe, int onoff) +-{ +- return dib8000_set_gpio(fe, 5, 0, !onoff); +-} +- +-static int dib80xx_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- return dib8000_set_gpio(fe, 0, 0, onoff); +-} +- +-static const struct dib0070_wbd_gain_cfg dib8070_wbd_gain_cfg[] = { +- { 240, 7}, +- { 0xffff, 6}, +-}; +- +-static struct dib0070_config dib807x_dib0070_config[2] = { +- { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib80xx_tuner_reset, +- .sleep = dib80xx_tuner_sleep, +- .clock_khz = 12000, +- .clock_pad_drive = 4, +- .vga_filter = 1, +- .force_crystal_mode = 1, +- .enable_third_order_filter = 1, +- .charge_pump = 0, +- .wbd_gain = dib8070_wbd_gain_cfg, +- .osc_buffer_state = 0, +- .freq_offset_khz_uhf = -100, +- .freq_offset_khz_vhf = -100, +- }, { +- .i2c_address = DEFAULT_DIB0070_I2C_ADDRESS, +- .reset = dib80xx_tuner_reset, +- .sleep = dib80xx_tuner_sleep, +- .clock_khz = 12000, +- .clock_pad_drive = 2, +- .vga_filter = 1, +- .force_crystal_mode = 1, +- .enable_third_order_filter = 1, +- .charge_pump = 0, +- .wbd_gain = dib8070_wbd_gain_cfg, +- .osc_buffer_state = 0, +- .freq_offset_khz_uhf = -25, +- .freq_offset_khz_vhf = -25, +- } +-}; +- +-static int dib807x_set_param_override(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- +- u16 offset = dib0070_wbd_offset(fe); +- u8 band = BAND_OF_FREQUENCY(p->frequency/1000); +- switch (band) { +- case BAND_VHF: +- offset += 750; +- break; +- case BAND_UHF: /* fall-thru wanted */ +- default: +- offset += 250; break; +- } +- deb_info("WBD for DiB8000: %d\n", offset); +- dib8000_set_wbd_ref(fe, offset); +- +- return state->set_param_save(fe); +-} +- +-static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, +- DIBX000_I2C_INTERFACE_TUNER, 1); +- +- if (adap->id == 0) { +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, +- &dib807x_dib0070_config[0]) == NULL) +- return -ENODEV; +- } else { +- if (dvb_attach(dib0070_attach, adap->fe_adap[0].fe, tun_i2c, +- &dib807x_dib0070_config[1]) == NULL) +- return -ENODEV; +- } +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib807x_set_param_override; +- return 0; +-} +- +-static int stk80xx_pid_filter(struct dvb_usb_adapter *adapter, int index, +- u16 pid, int onoff) +-{ +- return dib8000_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +-} +- +-static int stk80xx_pid_filter_ctrl(struct dvb_usb_adapter *adapter, +- int onoff) +-{ +- return dib8000_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +-} +- +-/* STK807x */ +-static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, +- 0x80, 0); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, +- &dib807x_dib8000_config[0]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-/* STK807xPVR */ +-static int stk807xpvr_frontend_attach0(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- msleep(30); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(500); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- /* initialize IC 0 */ +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x22, 0x80, 0); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, +- &dib807x_dib8000_config[0]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int stk807xpvr_frontend_attach1(struct dvb_usb_adapter *adap) +-{ +- /* initialize IC 1 */ +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x12, 0x82, 0); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, +- &dib807x_dib8000_config[1]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-/* STK8096GP */ +-static struct dibx000_agc_config dib8090_agc_config[2] = { +- { +- BAND_UHF | BAND_VHF | BAND_LBAND | BAND_SBAND, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, +- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- 787, +- 10, +- +- 0, +- 118, +- +- 0, +- 3530, +- 1, +- 5, +- +- 65535, +- 0, +- +- 65535, +- 0, +- +- 0, +- 32, +- 114, +- 143, +- 144, +- 114, +- 227, +- 116, +- 117, +- +- 28, +- 26, +- 31, +- 51, +- +- 0, +- }, +- { +- BAND_CBAND, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, +- * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- * P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ +- (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) +- | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- 787, +- 10, +- +- 0, +- 118, +- +- 0, +- 3530, +- 1, +- 5, +- +- 0, +- 0, +- +- 65535, +- 0, +- +- 0, +- 32, +- 114, +- 143, +- 144, +- 114, +- 227, +- 116, +- 117, +- +- 28, +- 26, +- 31, +- 51, +- +- 0, +- } +-}; +- +-static struct dibx000_bandwidth_config dib8090_pll_config_12mhz = { +- 54000, 13500, +- 1, 18, 3, 1, 0, +- 0, 0, 1, 1, 2, +- (3 << 14) | (1 << 12) | (599 << 0), +- (0 << 25) | 0, +- 20199727, +- 12000000, +-}; +- +-static int dib8090_get_adc_power(struct dvb_frontend *fe) +-{ +- return dib8000_get_adc_power(fe, 1); +-} +- +-static struct dib8000_config dib809x_dib8000_config[2] = { +- { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 2, +- .agc = dib8090_agc_config, +- .agc_control = dib0090_dcc_freq, +- .pll = &dib8090_pll_config_12mhz, +- .tuner_is_baseband = 1, +- +- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- .div_cfg = 0x31, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .drives = 0x2d98, +- .diversity_delay = 48, +- .refclksel = 3, +- }, { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 2, +- .agc = dib8090_agc_config, +- .agc_control = dib0090_dcc_freq, +- .pll = &dib8090_pll_config_12mhz, +- .tuner_is_baseband = 1, +- +- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- .div_cfg = 0x31, +- .output_mode = OUTMODE_DIVERSITY, +- .drives = 0x2d08, +- .diversity_delay = 1, +- .refclksel = 3, +- } +-}; +- +-static struct dib0090_wbd_slope dib8090_wbd_table[] = { +- /* max freq ; cold slope ; cold offset ; warm slope ; warm offset ; wbd gain */ +- { 120, 0, 500, 0, 500, 4 }, /* CBAND */ +- { 170, 0, 450, 0, 450, 4 }, /* CBAND */ +- { 380, 48, 373, 28, 259, 6 }, /* VHF */ +- { 860, 34, 700, 36, 616, 6 }, /* high UHF */ +- { 0xFFFF, 34, 700, 36, 616, 6 }, /* default */ +-}; +- +-static struct dib0090_config dib809x_dib0090_config = { +- .io.pll_bypass = 1, +- .io.pll_range = 1, +- .io.pll_prediv = 1, +- .io.pll_loopdiv = 20, +- .io.adc_clock_ratio = 8, +- .io.pll_int_loop_filt = 0, +- .io.clock_khz = 12000, +- .reset = dib80xx_tuner_reset, +- .sleep = dib80xx_tuner_sleep, +- .clkouttobamse = 1, +- .analog_output = 1, +- .i2c_address = DEFAULT_DIB0090_I2C_ADDRESS, +- .use_pwm_agc = 1, +- .clkoutdrive = 1, +- .get_adc_power = dib8090_get_adc_power, +- .freq_offset_khz_uhf = -63, +- .freq_offset_khz_vhf = -143, +- .wbd = dib8090_wbd_table, +- .fref_clock_ratio = 6, +-}; +- +-static int dib8096_set_param_override(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- u8 band = BAND_OF_FREQUENCY(p->frequency/1000); +- u16 target; +- int ret = 0; +- enum frontend_tune_state tune_state = CT_SHUTDOWN; +- u16 ltgain, rf_gain_limit; +- +- ret = state->set_param_save(fe); +- if (ret < 0) +- return ret; +- +- target = (dib0090_get_wbd_target(fe) * 8 * 18 / 33 + 1) / 2; +- dib8000_set_wbd_ref(fe, target); +- +- +- if (band == BAND_CBAND) { +- deb_info("tuning in CBAND - soft-AGC startup\n"); +- dib0090_set_tune_state(fe, CT_AGC_START); +- do { +- ret = dib0090_gain_control(fe); +- msleep(ret); +- tune_state = dib0090_get_tune_state(fe); +- if (tune_state == CT_AGC_STEP_0) +- dib8000_set_gpio(fe, 6, 0, 1); +- else if (tune_state == CT_AGC_STEP_1) { +- dib0090_get_current_gain(fe, NULL, NULL, &rf_gain_limit, <gain); +- if (rf_gain_limit == 0) +- dib8000_set_gpio(fe, 6, 0, 0); +- } +- } while (tune_state < CT_AGC_STOP); +- dib0090_pwm_gain_reset(fe); +- dib8000_pwm_agc_reset(fe); +- dib8000_set_tune_state(fe, CT_DEMOD_START); +- } else { +- deb_info("not tuning in CBAND - standard AGC startup\n"); +- dib0090_pwm_gain_reset(fe); +- } +- +- return 0; +-} +- +-static int dib809x_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) +- return -ENODEV; +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; +- return 0; +-} +- +-static int stk809x_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 18, 0x80, 0); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int nim8096md_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c; +- struct dvb_frontend *fe_slave = dib8000_get_slave_frontend(adap->fe_adap[0].fe, 1); +- +- if (fe_slave) { +- tun_i2c = dib8000_get_i2c_master(fe_slave, DIBX000_I2C_INTERFACE_TUNER, 1); +- if (dvb_attach(dib0090_register, fe_slave, tun_i2c, &dib809x_dib0090_config) == NULL) +- return -ENODEV; +- fe_slave->dvb = adap->fe_adap[0].fe->dvb; +- fe_slave->ops.tuner_ops.set_params = dib8096_set_param_override; +- } +- tun_i2c = dib8000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_TUNER, 1); +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &dib809x_dib0090_config) == NULL) +- return -ENODEV; +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096_set_param_override; +- +- return 0; +-} +- +-static int nim8096md_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_frontend *fe_slave; +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(1000); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, 0x80, 0); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x80, &dib809x_dib8000_config[0]); +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- fe_slave = dvb_attach(dib8000_attach, &adap->dev->i2c_adap, 0x82, &dib809x_dib8000_config[1]); +- dib8000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); +- +- return fe_slave == NULL ? -ENODEV : 0; +-} +- +-/* TFE8096P */ +-static struct dibx000_agc_config dib8096p_agc_config[2] = { +- { +- .band_caps = BAND_UHF, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, +- P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, +- P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, +- P_agc_write=0 */ +- .setup = (0 << 15) | (0 << 14) | (5 << 11) +- | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) +- | (0 << 4) | (5 << 1) | (0 << 0), +- +- .inv_gain = 684, +- .time_stabiliz = 10, +- +- .alpha_level = 0, +- .thlock = 118, +- +- .wbd_inv = 0, +- .wbd_ref = 1200, +- .wbd_sel = 3, +- .wbd_alpha = 5, +- +- .agc1_max = 65535, +- .agc1_min = 0, +- +- .agc2_max = 32767, +- .agc2_min = 0, +- +- .agc1_pt1 = 0, +- .agc1_pt2 = 0, +- .agc1_pt3 = 105, +- .agc1_slope1 = 0, +- .agc1_slope2 = 156, +- .agc2_pt1 = 105, +- .agc2_pt2 = 255, +- .agc2_slope1 = 54, +- .agc2_slope2 = 0, +- +- .alpha_mant = 28, +- .alpha_exp = 26, +- .beta_mant = 31, +- .beta_exp = 51, +- +- .perform_agc_softsplit = 0, +- } , { +- .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, +- P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, +- P_agc_inv_pwm2=0, P_agc_inh_dc_rv_est=0, +- P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, +- P_agc_write=0 */ +- .setup = (0 << 15) | (0 << 14) | (5 << 11) +- | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) +- | (0 << 4) | (5 << 1) | (0 << 0), +- +- .inv_gain = 732, +- .time_stabiliz = 10, +- +- .alpha_level = 0, +- .thlock = 118, +- +- .wbd_inv = 0, +- .wbd_ref = 1200, +- .wbd_sel = 3, +- .wbd_alpha = 5, +- +- .agc1_max = 65535, +- .agc1_min = 0, +- +- .agc2_max = 32767, +- .agc2_min = 0, +- +- .agc1_pt1 = 0, +- .agc1_pt2 = 0, +- .agc1_pt3 = 98, +- .agc1_slope1 = 0, +- .agc1_slope2 = 167, +- .agc2_pt1 = 98, +- .agc2_pt2 = 255, +- .agc2_slope1 = 52, +- .agc2_slope2 = 0, +- +- .alpha_mant = 28, +- .alpha_exp = 26, +- .beta_mant = 31, +- .beta_exp = 51, +- +- .perform_agc_softsplit = 0, +- } +-}; +- +-static struct dibx000_bandwidth_config dib8096p_clock_config_12_mhz = { +- 108000, 13500, +- 1, 9, 1, 0, 0, +- 0, 0, 0, 0, 2, +- (3 << 14) | (1 << 12) | (524 << 0), +- (0 << 25) | 0, +- 20199729, +- 12000000, +-}; +- +-static struct dib8000_config tfe8096p_dib8000_config = { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .update_lna = NULL, +- +- .agc_config_count = 2, +- .agc = dib8096p_agc_config, +- .pll = &dib8096p_clock_config_12_mhz, +- +- .gpio_dir = DIB8000_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB8000_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB8000_GPIO_DEFAULT_PWM_POS, +- +- .agc_control = NULL, +- .diversity_delay = 48, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .enMpegOutput = 1, +-}; +- +-static struct dib0090_wbd_slope dib8096p_wbd_table[] = { +- { 380, 81, 850, 64, 540, 4}, +- { 860, 51, 866, 21, 375, 4}, +- {1700, 0, 250, 0, 100, 6}, +- {2600, 0, 250, 0, 100, 6}, +- { 0xFFFF, 0, 0, 0, 0, 0}, +-}; +- +-static const struct dib0090_config tfe8096p_dib0090_config = { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib8096p_tuner_sleep, +- .sleep = dib8096p_tuner_sleep, +- +- .freq_offset_khz_uhf = -143, +- .freq_offset_khz_vhf = -143, +- +- .get_adc_power = dib8090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 1, +- +- .wbd = dib8096p_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +- .force_cband_input = 0, +-}; +- +-struct dibx090p_adc { +- u32 freq; /* RF freq MHz */ +- u32 timf; /* New Timf */ +- u32 pll_loopdiv; /* New prediv */ +- u32 pll_prediv; /* New loopdiv */ +-}; +- +-struct dibx090p_adc dib8090p_adc_tab[] = { +- { 50000, 17043521, 16, 3}, /* 64 MHz */ +- {878000, 20199729, 9, 1}, /* 60 MHz */ +- {0xffffffff, 0, 0, 0}, /* 60 MHz */ +-}; +- +-static int dib8096p_agc_startup(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- struct dibx000_bandwidth_config pll; +- u16 target; +- int better_sampling_freq = 0, ret; +- struct dibx090p_adc *adc_table = &dib8090p_adc_tab[0]; +- +- ret = state->set_param_save(fe); +- if (ret < 0) +- return ret; +- memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); +- +- dib0090_pwm_gain_reset(fe); +- /* dib0090_get_wbd_target is returning any possible +- temperature compensated wbd-target */ +- target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; +- dib8000_set_wbd_ref(fe, target); +- +- +- while (p->frequency / 1000 > adc_table->freq) { +- better_sampling_freq = 1; +- adc_table++; +- } +- +- if ((adc_table->freq != 0xffffffff) && better_sampling_freq) { +- pll.pll_ratio = adc_table->pll_loopdiv; +- pll.pll_prediv = adc_table->pll_prediv; +- dib8000_update_pll(fe, &pll); +- dib8000_ctrl_timf(fe, DEMOD_TIMF_SET, adc_table->timf); +- } +- return 0; +-} +- +-static int tfe8096p_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- dib8000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80, 1); +- +- adap->fe_adap[0].fe = dvb_attach(dib8000_attach, +- &adap->dev->i2c_adap, 0x80, &tfe8096p_dib8000_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int tfe8096p_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib8096p_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, +- &tfe8096p_dib0090_config) == NULL) +- return -ENODEV; +- +- dib8000_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib8096p_agc_startup; +- return 0; +-} +- +-/* STK9090M */ +-static int dib90x0_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +-{ +- return dib9000_fw_pid_filter(adapter->fe_adap[0].fe, index, pid, onoff); +-} +- +-static int dib90x0_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +-{ +- return dib9000_fw_pid_filter_ctrl(adapter->fe_adap[0].fe, onoff); +-} +- +-static int dib90x0_tuner_reset(struct dvb_frontend *fe, int onoff) +-{ +- return dib9000_set_gpio(fe, 5, 0, !onoff); +-} +- +-static int dib90x0_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- return dib9000_set_gpio(fe, 0, 0, onoff); +-} +- +-static int dib01x0_pmu_update(struct i2c_adapter *i2c, u16 *data, u8 len) +-{ +- u8 wb[4] = { 0xc >> 8, 0xc & 0xff, 0, 0 }; +- u8 rb[2]; +- struct i2c_msg msg[2] = { +- {.addr = 0x1e >> 1, .flags = 0, .buf = wb, .len = 2}, +- {.addr = 0x1e >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2}, +- }; +- u8 index_data; +- +- dibx000_i2c_set_speed(i2c, 250); +- +- if (i2c_transfer(i2c, msg, 2) != 2) +- return -EIO; +- +- switch (rb[0] << 8 | rb[1]) { +- case 0: +- deb_info("Found DiB0170 rev1: This version of DiB0170 is not supported any longer.\n"); +- return -EIO; +- case 1: +- deb_info("Found DiB0170 rev2"); +- break; +- case 2: +- deb_info("Found DiB0190 rev2"); +- break; +- default: +- deb_info("DiB01x0 not found"); +- return -EIO; +- } +- +- for (index_data = 0; index_data < len; index_data += 2) { +- wb[2] = (data[index_data + 1] >> 8) & 0xff; +- wb[3] = (data[index_data + 1]) & 0xff; +- +- if (data[index_data] == 0) { +- wb[0] = (data[index_data] >> 8) & 0xff; +- wb[1] = (data[index_data]) & 0xff; +- msg[0].len = 2; +- if (i2c_transfer(i2c, msg, 2) != 2) +- return -EIO; +- wb[2] |= rb[0]; +- wb[3] |= rb[1] & ~(3 << 4); +- } +- +- wb[0] = (data[index_data] >> 8)&0xff; +- wb[1] = (data[index_data])&0xff; +- msg[0].len = 4; +- if (i2c_transfer(i2c, &msg[0], 1) != 1) +- return -EIO; +- } +- return 0; +-} +- +-static struct dib9000_config stk9090m_config = { +- .output_mpeg2_in_188_bytes = 1, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .vcxo_timer = 279620, +- .timing_frequency = 20452225, +- .demod_clock_khz = 60000, +- .xtal_clock_khz = 30000, +- .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), +- .subband = { +- 2, +- { +- { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0008 } }, /* GPIO 3 to 1 for VHF */ +- { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0008, 0x0000, 0x0000 } }, /* GPIO 3 to 0 for UHF */ +- { 0 }, +- }, +- }, +- .gpio_function = { +- { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, +- { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, +- }, +-}; +- +-static struct dib9000_config nim9090md_config[2] = { +- { +- .output_mpeg2_in_188_bytes = 1, +- .output_mode = OUTMODE_MPEG2_FIFO, +- .vcxo_timer = 279620, +- .timing_frequency = 20452225, +- .demod_clock_khz = 60000, +- .xtal_clock_khz = 30000, +- .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), +- }, { +- .output_mpeg2_in_188_bytes = 1, +- .output_mode = OUTMODE_DIVERSITY, +- .vcxo_timer = 279620, +- .timing_frequency = 20452225, +- .demod_clock_khz = 60000, +- .xtal_clock_khz = 30000, +- .if_drives = (0 << 15) | (1 << 13) | (0 << 12) | (3 << 10) | (0 << 9) | (1 << 7) | (0 << 6) | (0 << 4) | (1 << 3) | (1 << 1) | (0), +- .subband = { +- 2, +- { +- { 240, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0006 } }, /* GPIO 1 and 2 to 1 for VHF */ +- { 890, { BOARD_GPIO_COMPONENT_DEMOD, BOARD_GPIO_FUNCTION_SUBBAND_GPIO, 0x0006, 0x0000, 0x0000 } }, /* GPIO 1 and 2 to 0 for UHF */ +- { 0 }, +- }, +- }, +- .gpio_function = { +- { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_ON, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = (0x10 & ~0x1) | 0x20 }, +- { .component = BOARD_GPIO_COMPONENT_DEMOD, .function = BOARD_GPIO_FUNCTION_COMPONENT_OFF, .mask = 0x10 | 0x21, .direction = 0 & ~0x21, .value = 0 | 0x21 }, +- }, +- } +-}; +- +-static struct dib0090_config dib9090_dib0090_config = { +- .io.pll_bypass = 0, +- .io.pll_range = 1, +- .io.pll_prediv = 1, +- .io.pll_loopdiv = 8, +- .io.adc_clock_ratio = 8, +- .io.pll_int_loop_filt = 0, +- .io.clock_khz = 30000, +- .reset = dib90x0_tuner_reset, +- .sleep = dib90x0_tuner_sleep, +- .clkouttobamse = 0, +- .analog_output = 0, +- .use_pwm_agc = 0, +- .clkoutdrive = 0, +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +-}; +- +-static struct dib0090_config nim9090md_dib0090_config[2] = { +- { +- .io.pll_bypass = 0, +- .io.pll_range = 1, +- .io.pll_prediv = 1, +- .io.pll_loopdiv = 8, +- .io.adc_clock_ratio = 8, +- .io.pll_int_loop_filt = 0, +- .io.clock_khz = 30000, +- .reset = dib90x0_tuner_reset, +- .sleep = dib90x0_tuner_sleep, +- .clkouttobamse = 1, +- .analog_output = 0, +- .use_pwm_agc = 0, +- .clkoutdrive = 0, +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +- }, { +- .io.pll_bypass = 0, +- .io.pll_range = 1, +- .io.pll_prediv = 1, +- .io.pll_loopdiv = 8, +- .io.adc_clock_ratio = 8, +- .io.pll_int_loop_filt = 0, +- .io.clock_khz = 30000, +- .reset = dib90x0_tuner_reset, +- .sleep = dib90x0_tuner_sleep, +- .clkouttobamse = 0, +- .analog_output = 0, +- .use_pwm_agc = 0, +- .clkoutdrive = 0, +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +- } +-}; +- +- +-static int stk9090m_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *state = adap->priv; +- struct dib0700_state *st = adap->dev->priv; +- u32 fw_version; +- +- /* Make use of the new i2c functions from FW 1.20 */ +- dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); +- if (fw_version >= 0x10200) +- st->fw_use_new_i2c_api = 1; +- dib0700_set_i2c_speed(adap->dev, 340); +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, 0x80); +- +- if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { +- deb_info("%s: Upload failed. (file not found?)\n", __func__); +- return -ENODEV; +- } else { +- deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); +- } +- stk9090m_config.microcode_B_fe_size = state->frontend_firmware->size; +- stk9090m_config.microcode_B_fe_buffer = state->frontend_firmware->data; +- +- adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &stk9090m_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int dib9090_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *state = adap->priv; +- struct i2c_adapter *i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); +- u16 data_dib190[10] = { +- 1, 0x1374, +- 2, 0x01a2, +- 7, 0x0020, +- 0, 0x00ef, +- 8, 0x0486, +- }; +- +- if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &dib9090_dib0090_config) == NULL) +- return -ENODEV; +- i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); +- if (dib01x0_pmu_update(i2c, data_dib190, 10) != 0) +- return -ENODEV; +- dib0700_set_i2c_speed(adap->dev, 1500); +- if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) +- return -ENODEV; +- release_firmware(state->frontend_firmware); +- return 0; +-} +- +-static int nim9090md_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *state = adap->priv; +- struct dib0700_state *st = adap->dev->priv; +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe_slave; +- u32 fw_version; +- +- /* Make use of the new i2c functions from FW 1.20 */ +- dib0700_get_version(adap->dev, NULL, NULL, &fw_version, NULL); +- if (fw_version >= 0x10200) +- st->fw_use_new_i2c_api = 1; +- dib0700_set_i2c_speed(adap->dev, 340); +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (request_firmware(&state->frontend_firmware, "dib9090.fw", &adap->dev->udev->dev)) { +- deb_info("%s: Upload failed. (file not found?)\n", __func__); +- return -EIO; +- } else { +- deb_info("%s: firmware read %Zu bytes.\n", __func__, state->frontend_firmware->size); +- } +- nim9090md_config[0].microcode_B_fe_size = state->frontend_firmware->size; +- nim9090md_config[0].microcode_B_fe_buffer = state->frontend_firmware->data; +- nim9090md_config[1].microcode_B_fe_size = state->frontend_firmware->size; +- nim9090md_config[1].microcode_B_fe_buffer = state->frontend_firmware->data; +- +- dib9000_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, 0x80); +- adap->fe_adap[0].fe = dvb_attach(dib9000_attach, &adap->dev->i2c_adap, 0x80, &nim9090md_config[0]); +- +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_3_4, 0); +- dib9000_i2c_enumeration(i2c, 1, 0x12, 0x82); +- +- fe_slave = dvb_attach(dib9000_attach, i2c, 0x82, &nim9090md_config[1]); +- dib9000_set_slave_frontend(adap->fe_adap[0].fe, fe_slave); +- +- return fe_slave == NULL ? -ENODEV : 0; +-} +- +-static int nim9090md_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *state = adap->priv; +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe_slave; +- u16 data_dib190[10] = { +- 1, 0x5374, +- 2, 0x01ae, +- 7, 0x0020, +- 0, 0x00ef, +- 8, 0x0406, +- }; +- i2c = dib9000_get_tuner_interface(adap->fe_adap[0].fe); +- if (dvb_attach(dib0090_fw_register, adap->fe_adap[0].fe, i2c, &nim9090md_dib0090_config[0]) == NULL) +- return -ENODEV; +- i2c = dib9000_get_i2c_master(adap->fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_1_2, 0); +- if (dib01x0_pmu_update(i2c, data_dib190, 10) < 0) +- return -ENODEV; +- +- dib0700_set_i2c_speed(adap->dev, 1500); +- if (dib9000_firmware_post_pll_init(adap->fe_adap[0].fe) < 0) +- return -ENODEV; +- +- fe_slave = dib9000_get_slave_frontend(adap->fe_adap[0].fe, 1); +- if (fe_slave != NULL) { +- i2c = dib9000_get_component_bus_interface(adap->fe_adap[0].fe); +- dib9000_set_i2c_adapter(fe_slave, i2c); +- +- i2c = dib9000_get_tuner_interface(fe_slave); +- if (dvb_attach(dib0090_fw_register, fe_slave, i2c, &nim9090md_dib0090_config[1]) == NULL) +- return -ENODEV; +- fe_slave->dvb = adap->fe_adap[0].fe->dvb; +- dib9000_fw_set_component_bus_speed(adap->fe_adap[0].fe, 1500); +- if (dib9000_firmware_post_pll_init(fe_slave) < 0) +- return -ENODEV; +- } +- release_firmware(state->frontend_firmware); +- +- return 0; +-} +- +-/* NIM7090 */ +-struct dib7090p_best_adc { +- u32 timf; +- u32 pll_loopdiv; +- u32 pll_prediv; +-}; +- +-static int dib7090p_get_best_sampling(struct dvb_frontend *fe , struct dib7090p_best_adc *adc) +-{ +- u8 spur = 0, prediv = 0, loopdiv = 0, min_prediv = 1, max_prediv = 1; +- +- u16 xtal = 12000; +- u32 fcp_min = 1900; /* PLL Minimum Frequency comparator KHz */ +- u32 fcp_max = 20000; /* PLL Maximum Frequency comparator KHz */ +- u32 fdem_max = 76000; +- u32 fdem_min = 69500; +- u32 fcp = 0, fs = 0, fdem = 0; +- u32 harmonic_id = 0; +- +- adc->pll_loopdiv = loopdiv; +- adc->pll_prediv = prediv; +- adc->timf = 0; +- +- deb_info("bandwidth = %d fdem_min =%d", fe->dtv_property_cache.bandwidth_hz, fdem_min); +- +- /* Find Min and Max prediv */ +- while ((xtal/max_prediv) >= fcp_min) +- max_prediv++; +- +- max_prediv--; +- min_prediv = max_prediv; +- while ((xtal/min_prediv) <= fcp_max) { +- min_prediv--; +- if (min_prediv == 1) +- break; +- } +- deb_info("MIN prediv = %d : MAX prediv = %d", min_prediv, max_prediv); +- +- min_prediv = 2; +- +- for (prediv = min_prediv ; prediv < max_prediv; prediv++) { +- fcp = xtal / prediv; +- if (fcp > fcp_min && fcp < fcp_max) { +- for (loopdiv = 1 ; loopdiv < 64 ; loopdiv++) { +- fdem = ((xtal/prediv) * loopdiv); +- fs = fdem / 4; +- /* test min/max system restrictions */ +- +- if ((fdem >= fdem_min) && (fdem <= fdem_max) && (fs >= fe->dtv_property_cache.bandwidth_hz/1000)) { +- spur = 0; +- /* test fs harmonics positions */ +- for (harmonic_id = (fe->dtv_property_cache.frequency / (1000*fs)) ; harmonic_id <= ((fe->dtv_property_cache.frequency / (1000*fs))+1) ; harmonic_id++) { +- if (((fs*harmonic_id) >= ((fe->dtv_property_cache.frequency/1000) - (fe->dtv_property_cache.bandwidth_hz/2000))) && ((fs*harmonic_id) <= ((fe->dtv_property_cache.frequency/1000) + (fe->dtv_property_cache.bandwidth_hz/2000)))) { +- spur = 1; +- break; +- } +- } +- +- if (!spur) { +- adc->pll_loopdiv = loopdiv; +- adc->pll_prediv = prediv; +- adc->timf = 2396745143UL/fdem*(1 << 9); +- adc->timf += ((2396745143UL%fdem) << 9)/fdem; +- deb_info("loopdiv=%i prediv=%i timf=%i", loopdiv, prediv, adc->timf); +- break; +- } +- } +- } +- } +- if (!spur) +- break; +- } +- +- +- if (adc->pll_loopdiv == 0 && adc->pll_prediv == 0) +- return -EINVAL; +- else +- return 0; +-} +- +-static int dib7090_agc_startup(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dib0700_adapter_state *state = adap->priv; +- struct dibx000_bandwidth_config pll; +- u16 target; +- struct dib7090p_best_adc adc; +- int ret; +- +- ret = state->set_param_save(fe); +- if (ret < 0) +- return ret; +- +- memset(&pll, 0, sizeof(struct dibx000_bandwidth_config)); +- dib0090_pwm_gain_reset(fe); +- target = (dib0090_get_wbd_target(fe) * 8 + 1) / 2; +- dib7000p_set_wbd_ref(fe, target); +- +- if (dib7090p_get_best_sampling(fe, &adc) == 0) { +- pll.pll_ratio = adc.pll_loopdiv; +- pll.pll_prediv = adc.pll_prediv; +- +- dib7000p_update_pll(fe, &pll); +- dib7000p_ctrl_timf(fe, DEMOD_TIMF_SET, adc.timf); +- } +- return 0; +-} +- +-static int dib7090_agc_restart(struct dvb_frontend *fe, u8 restart) +-{ +- deb_info("AGC restart callback: %d", restart); +- if (restart == 0) /* before AGC startup */ +- dib0090_set_dc_servo(fe, 1); +- return 0; +-} +- +-static int dib7090e_update_lna(struct dvb_frontend *fe, u16 agc_global) +-{ +- u16 agc1 = 0, agc2, wbd = 0, wbd_target, wbd_offset, threshold_agc1; +- s16 wbd_delta; +- +- if ((fe->dtv_property_cache.frequency) < 400000000) +- threshold_agc1 = 25000; +- else +- threshold_agc1 = 30000; +- +- wbd_target = (dib0090_get_wbd_target(fe)*8+1)/2; +- wbd_offset = dib0090_get_wbd_offset(fe); +- dib7000p_get_agc_values(fe, NULL, &agc1, &agc2, &wbd); +- wbd_delta = (s16)wbd - (((s16)wbd_offset+10)*4) ; +- +- deb_info("update lna, agc_global=%d agc1=%d agc2=%d", +- agc_global, agc1, agc2); +- deb_info("update lna, wbd=%d wbd target=%d wbd offset=%d wbd delta=%d", +- wbd, wbd_target, wbd_offset, wbd_delta); +- +- if ((agc1 < threshold_agc1) && (wbd_delta > 0)) { +- dib0090_set_switch(fe, 1, 1, 1); +- dib0090_set_vga(fe, 0); +- dib0090_update_rframp_7090(fe, 0); +- dib0090_update_tuning_table_7090(fe, 0); +- } else { +- dib0090_set_vga(fe, 1); +- dib0090_update_rframp_7090(fe, 1); +- dib0090_update_tuning_table_7090(fe, 1); +- dib0090_set_switch(fe, 0, 0, 0); +- } +- +- return 0; +-} +- +-static struct dib0090_wbd_slope dib7090_wbd_table[] = { +- { 380, 81, 850, 64, 540, 4}, +- { 860, 51, 866, 21, 375, 4}, +- {1700, 0, 250, 0, 100, 6}, +- {2600, 0, 250, 0, 100, 6}, +- { 0xFFFF, 0, 0, 0, 0, 0}, +-}; +- +-static struct dib0090_wbd_slope dib7090e_wbd_table[] = { +- { 380, 81, 850, 64, 540, 4}, +- { 700, 51, 866, 21, 320, 4}, +- { 860, 48, 666, 18, 330, 6}, +- {1700, 0, 250, 0, 100, 6}, +- {2600, 0, 250, 0, 100, 6}, +- { 0xFFFF, 0, 0, 0, 0, 0}, +-}; +- +-static struct dibx000_agc_config dib7090_agc_config[2] = { +- { +- .band_caps = BAND_UHF, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ +- .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- .inv_gain = 687, +- .time_stabiliz = 10, +- +- .alpha_level = 0, +- .thlock = 118, +- +- .wbd_inv = 0, +- .wbd_ref = 1200, +- .wbd_sel = 3, +- .wbd_alpha = 5, +- +- .agc1_max = 65535, +- .agc1_min = 0, +- +- .agc2_max = 65535, +- .agc2_min = 0, +- +- .agc1_pt1 = 0, +- .agc1_pt2 = 32, +- .agc1_pt3 = 114, +- .agc1_slope1 = 143, +- .agc1_slope2 = 144, +- .agc2_pt1 = 114, +- .agc2_pt2 = 227, +- .agc2_slope1 = 116, +- .agc2_slope2 = 117, +- +- .alpha_mant = 18, +- .alpha_exp = 0, +- .beta_mant = 20, +- .beta_exp = 59, +- +- .perform_agc_softsplit = 0, +- } , { +- .band_caps = BAND_FM | BAND_VHF | BAND_CBAND, +- /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=1, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, +- * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=5, P_agc_write=0 */ +- .setup = (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (5 << 1) | (0 << 0), +- +- .inv_gain = 732, +- .time_stabiliz = 10, +- +- .alpha_level = 0, +- .thlock = 118, +- +- .wbd_inv = 0, +- .wbd_ref = 1200, +- .wbd_sel = 3, +- .wbd_alpha = 5, +- +- .agc1_max = 65535, +- .agc1_min = 0, +- +- .agc2_max = 65535, +- .agc2_min = 0, +- +- .agc1_pt1 = 0, +- .agc1_pt2 = 0, +- .agc1_pt3 = 98, +- .agc1_slope1 = 0, +- .agc1_slope2 = 167, +- .agc2_pt1 = 98, +- .agc2_pt2 = 255, +- .agc2_slope1 = 104, +- .agc2_slope2 = 0, +- +- .alpha_mant = 18, +- .alpha_exp = 0, +- .beta_mant = 20, +- .beta_exp = 59, +- +- .perform_agc_softsplit = 0, +- } +-}; +- +-static struct dibx000_bandwidth_config dib7090_clock_config_12_mhz = { +- 60000, 15000, +- 1, 5, 0, 0, 0, +- 0, 0, 1, 1, 2, +- (3 << 14) | (1 << 12) | (524 << 0), +- (0 << 25) | 0, +- 20452225, +- 15000000, +-}; +- +-static struct dib7000p_config nim7090_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- .update_lna = NULL, +- +- .agc_config_count = 2, +- .agc = dib7090_agc_config, +- +- .bw = &dib7090_clock_config_12_mhz, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .pwm_freq_div = 0, +- +- .agc_control = dib7090_agc_restart, +- +- .spur_protect = 0, +- .disable_sample_and_hold = 0, +- .enable_current_mirror = 0, +- .diversity_delay = 0, +- +- .output_mode = OUTMODE_MPEG2_FIFO, +- .enMpegOutput = 1, +-}; +- +-static struct dib7000p_config tfe7090pvr_dib7000p_config[2] = { +- { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- .update_lna = NULL, +- +- .agc_config_count = 2, +- .agc = dib7090_agc_config, +- +- .bw = &dib7090_clock_config_12_mhz, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .pwm_freq_div = 0, +- +- .agc_control = dib7090_agc_restart, +- +- .spur_protect = 0, +- .disable_sample_and_hold = 0, +- .enable_current_mirror = 0, +- .diversity_delay = 0, +- +- .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, +- .default_i2c_addr = 0x90, +- .enMpegOutput = 1, +- }, { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- .update_lna = NULL, +- +- .agc_config_count = 2, +- .agc = dib7090_agc_config, +- +- .bw = &dib7090_clock_config_12_mhz, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .pwm_freq_div = 0, +- +- .agc_control = dib7090_agc_restart, +- +- .spur_protect = 0, +- .disable_sample_and_hold = 0, +- .enable_current_mirror = 0, +- .diversity_delay = 0, +- +- .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, +- .default_i2c_addr = 0x92, +- .enMpegOutput = 0, +- } +-}; +- +-static struct dib7000p_config tfe7090e_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- .update_lna = dib7090e_update_lna, +- +- .agc_config_count = 2, +- .agc = dib7090_agc_config, +- +- .bw = &dib7090_clock_config_12_mhz, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .pwm_freq_div = 0, +- +- .agc_control = dib7090_agc_restart, +- +- .spur_protect = 0, +- .disable_sample_and_hold = 0, +- .enable_current_mirror = 0, +- .diversity_delay = 0, +- +- .output_mode = OUTMODE_MPEG2_FIFO, +- .enMpegOutput = 1, +-}; +- +-static const struct dib0090_config nim7090_dib0090_config = { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib7090_tuner_sleep, +- .sleep = dib7090_tuner_sleep, +- +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +- +- .get_adc_power = dib7090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 0, +- +- .wbd = dib7090_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +-}; +- +-static const struct dib0090_config tfe7090e_dib0090_config = { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib7090_tuner_sleep, +- .sleep = dib7090_tuner_sleep, +- +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +- +- .get_adc_power = dib7090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 0, +- +- .wbd = dib7090e_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +- .force_cband_input = 1, +- .is_dib7090e = 1, +-}; +- +-static struct dib7000p_config tfe7790e_dib7000p_config = { +- .output_mpeg2_in_188_bytes = 1, +- .hostbus_diversity = 1, +- .tuner_is_baseband = 1, +- .update_lna = dib7090e_update_lna, +- +- .agc_config_count = 2, +- .agc = dib7090_agc_config, +- +- .bw = &dib7090_clock_config_12_mhz, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .pwm_freq_div = 0, +- +- .agc_control = dib7090_agc_restart, +- +- .spur_protect = 0, +- .disable_sample_and_hold = 0, +- .enable_current_mirror = 0, +- .diversity_delay = 0, +- +- .output_mode = OUTMODE_MPEG2_PAR_GATED_CLK, +- .enMpegOutput = 1, +-}; +- +-static const struct dib0090_config tfe7790e_dib0090_config = { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib7090_tuner_sleep, +- .sleep = dib7090_tuner_sleep, +- +- .freq_offset_khz_uhf = 0, +- .freq_offset_khz_vhf = 0, +- +- .get_adc_power = dib7090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 0, +- +- .wbd = dib7090e_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +- .force_cband_input = 1, +- .is_dib7090e = 1, +- .force_crystal_mode = 1, +-}; +- +-static const struct dib0090_config tfe7090pvr_dib0090_config[2] = { +- { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib7090_tuner_sleep, +- .sleep = dib7090_tuner_sleep, +- +- .freq_offset_khz_uhf = 50, +- .freq_offset_khz_vhf = 70, +- +- .get_adc_power = dib7090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 0, +- +- .wbd = dib7090_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +- }, { +- .io.clock_khz = 12000, +- .io.pll_bypass = 0, +- .io.pll_range = 0, +- .io.pll_prediv = 3, +- .io.pll_loopdiv = 6, +- .io.adc_clock_ratio = 0, +- .io.pll_int_loop_filt = 0, +- .reset = dib7090_tuner_sleep, +- .sleep = dib7090_tuner_sleep, +- +- .freq_offset_khz_uhf = -50, +- .freq_offset_khz_vhf = -70, +- +- .get_adc_power = dib7090_get_adc_power, +- +- .clkouttobamse = 1, +- .analog_output = 0, +- +- .wbd_vhf_offset = 0, +- .wbd_cband_offset = 0, +- .use_pwm_agc = 1, +- .clkoutdrive = 0, +- +- .fref_clock_ratio = 0, +- +- .wbd = dib7090_wbd_table, +- +- .ls_cfg_pad_drv = 0, +- .data_tx_drv = 0, +- .low_if = NULL, +- .in_soc = 1, +- } +-}; +- +-static int nim7090_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x10, &nim7090_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); +- return -ENODEV; +- } +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &nim7090_dib7000p_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int nim7090_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &nim7090_dib0090_config) == NULL) +- return -ENODEV; +- +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; +- return 0; +-} +- +-static int tfe7090pvr_frontend0_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- +- /* The TFE7090 requires the dib0700 to not be in master mode */ +- st->disable_streaming_master_mode = 1; +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- /* initialize IC 0 */ +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 0x20, &tfe7090pvr_dib7000p_config[0]) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); +- return -ENODEV; +- } +- +- dib0700_set_i2c_speed(adap->dev, 340); +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x90, &tfe7090pvr_dib7000p_config[0]); +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- dib7090_slave_reset(adap->fe_adap[0].fe); +- +- return 0; +-} +- +-static int tfe7090pvr_frontend1_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *i2c; +- +- if (adap->dev->adapter[0].fe_adap[0].fe == NULL) { +- err("the master dib7090 has to be initialized first"); +- return -ENODEV; /* the master device has not been initialized */ +- } +- +- i2c = dib7000p_get_i2c_master(adap->dev->adapter[0].fe_adap[0].fe, DIBX000_I2C_INTERFACE_GPIO_6_7, 1); +- if (dib7000p_i2c_enumeration(i2c, 1, 0x10, &tfe7090pvr_dib7000p_config[1]) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, i2c, 0x92, &tfe7090pvr_dib7000p_config[1]); +- dib0700_set_i2c_speed(adap->dev, 200); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int tfe7090pvr_tuner0_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[0]) == NULL) +- return -ENODEV; +- +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; +- return 0; +-} +- +-static int tfe7090pvr_tuner1_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = dib7090_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, &tfe7090pvr_dib0090_config[1]) == NULL) +- return -ENODEV; +- +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; +- return 0; +-} +- +-static int tfe7090e_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, +- 1, 0x10, &tfe7090e_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, +- 0x80, &tfe7090e_dib7000p_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int tfe7790e_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- +- /* The TFE7790E requires the dib0700 to not be in master mode */ +- st->disable_streaming_master_mode = 1; +- +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(20); +- dib0700_ctrl_clock(adap->dev, 72, 1); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(20); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, +- 1, 0x10, &tfe7790e_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, +- 0x80, &tfe7790e_dib7000p_config); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int tfe7790e_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = +- dib7090_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, +- &tfe7790e_dib0090_config) == NULL) +- return -ENODEV; +- +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; +- return 0; +-} +- +-static int tfe7090e_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_adapter_state *st = adap->priv; +- struct i2c_adapter *tun_i2c = +- dib7090_get_i2c_tuner(adap->fe_adap[0].fe); +- +- if (dvb_attach(dib0090_register, adap->fe_adap[0].fe, tun_i2c, +- &tfe7090e_dib0090_config) == NULL) +- return -ENODEV; +- +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- +- st->set_param_save = adap->fe_adap[0].fe->ops.tuner_ops.set_params; +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = dib7090_agc_startup; +- return 0; +-} +- +-/* STK7070PD */ +-static struct dib7000p_config stk7070pd_dib7000p_config[2] = { +- { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &dib7070_agc_config, +- .bw = &dib7070_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- .spur_protect = 1, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- }, { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &dib7070_agc_config, +- .bw = &dib7070_bw_config_12_mhz, +- .tuner_is_baseband = 1, +- .spur_protect = 1, +- +- .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, +- +- .hostbus_diversity = 1, +- } +-}; +- +-static void stk7070pd_init(struct dvb_usb_device *dev) +-{ +- dib0700_set_gpio(dev, GPIO6, GPIO_OUT, 1); +- msleep(10); +- dib0700_set_gpio(dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 0); +- +- dib0700_ctrl_clock(dev, 72, 1); +- +- msleep(10); +- dib0700_set_gpio(dev, GPIO10, GPIO_OUT, 1); +-} +- +-static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap) +-{ +- stk7070pd_init(adap->dev); +- +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, +- stk7070pd_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]); +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int stk7070pd_frontend_attach1(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x82, &stk7070pd_dib7000p_config[1]); +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int novatd_read_status_override(struct dvb_frontend *fe, +- fe_status_t *stat) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dvb_usb_device *dev = adap->dev; +- struct dib0700_state *state = dev->priv; +- int ret; +- +- ret = state->read_status(fe, stat); +- +- if (!ret) +- dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, +- !!(*stat & FE_HAS_LOCK)); +- +- return ret; +-} +- +-static int novatd_sleep_override(struct dvb_frontend* fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dvb_usb_device *dev = adap->dev; +- struct dib0700_state *state = dev->priv; +- +- /* turn off LED */ +- dib0700_set_gpio(dev, adap->id == 0 ? GPIO1 : GPIO0, GPIO_OUT, 0); +- +- return state->sleep(fe); +-} +- +-/** +- * novatd_frontend_attach - Nova-TD specific attach +- * +- * Nova-TD has GPIO0, 1 and 2 for LEDs. So do not fiddle with them except for +- * information purposes. +- */ +-static int novatd_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *dev = adap->dev; +- struct dib0700_state *st = dev->priv; +- +- if (adap->id == 0) { +- stk7070pd_init(dev); +- +- /* turn the power LED on, the other two off (just in case) */ +- dib0700_set_gpio(dev, GPIO0, GPIO_OUT, 0); +- dib0700_set_gpio(dev, GPIO1, GPIO_OUT, 0); +- dib0700_set_gpio(dev, GPIO2, GPIO_OUT, 1); +- +- if (dib7000p_i2c_enumeration(&dev->i2c_adap, 2, 18, +- stk7070pd_dib7000p_config) != 0) { +- err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", +- __func__); +- return -ENODEV; +- } +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &dev->i2c_adap, +- adap->id == 0 ? 0x80 : 0x82, +- &stk7070pd_dib7000p_config[adap->id]); +- +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- st->read_status = adap->fe_adap[0].fe->ops.read_status; +- adap->fe_adap[0].fe->ops.read_status = novatd_read_status_override; +- st->sleep = adap->fe_adap[0].fe->ops.sleep; +- adap->fe_adap[0].fe->ops.sleep = novatd_sleep_override; +- +- return 0; +-} +- +-/* S5H1411 */ +-static struct s5h1411_config pinnacle_801e_config = { +- .output_mode = S5H1411_PARALLEL_OUTPUT, +- .gpio = S5H1411_GPIO_OFF, +- .mpeg_timing = S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, +- .qam_if = S5H1411_IF_44000, +- .vsb_if = S5H1411_IF_44000, +- .inversion = S5H1411_INVERSION_OFF, +- .status_mode = S5H1411_DEMODLOCKING +-}; +- +-/* Pinnacle PCTV HD Pro 801e GPIOs map: +- GPIO0 - currently unknown +- GPIO1 - xc5000 tuner reset +- GPIO2 - CX25843 sleep +- GPIO3 - currently unknown +- GPIO4 - currently unknown +- GPIO6 - currently unknown +- GPIO7 - currently unknown +- GPIO9 - currently unknown +- GPIO10 - CX25843 reset +- */ +-static int s5h1411_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- +- /* Make use of the new i2c functions from FW 1.20 */ +- st->fw_use_new_i2c_api = 1; +- +- /* The s5h1411 requires the dib0700 to not be in master mode */ +- st->disable_streaming_master_mode = 1; +- +- /* All msleep values taken from Windows USB trace */ +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 0); +- dib0700_set_gpio(adap->dev, GPIO3, GPIO_OUT, 0); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(400); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(60); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(30); +- dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO4, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO7, GPIO_OUT, 1); +- dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 0); +- msleep(30); +- +- /* Put the CX25843 to sleep for now since we're in digital mode */ +- dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1); +- +- /* GPIOs are initialized, do the attach */ +- adap->fe_adap[0].fe = dvb_attach(s5h1411_attach, &pinnacle_801e_config, +- &adap->dev->i2c_adap); +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int dib0700_xc5000_tuner_callback(void *priv, int component, +- int command, int arg) +-{ +- struct dvb_usb_adapter *adap = priv; +- +- if (command == XC5000_TUNER_RESET) { +- /* Reset the tuner */ +- dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 0); +- msleep(10); +- dib0700_set_gpio(adap->dev, GPIO1, GPIO_OUT, 1); +- msleep(10); +- } else { +- err("xc5000: unknown tuner callback command: %d\n", command); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static struct xc5000_config s5h1411_xc5000_tunerconfig = { +- .i2c_address = 0x64, +- .if_khz = 5380, +-}; +- +-static int xc5000_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- /* FIXME: generalize & move to common area */ +- adap->fe_adap[0].fe->callback = dib0700_xc5000_tuner_callback; +- +- return dvb_attach(xc5000_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &s5h1411_xc5000_tunerconfig) +- == NULL ? -ENODEV : 0; +-} +- +-static int dib0700_xc4000_tuner_callback(void *priv, int component, +- int command, int arg) +-{ +- struct dvb_usb_adapter *adap = priv; +- +- if (command == XC4000_TUNER_RESET) { +- /* Reset the tuner */ +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 0); +- msleep(10); +- dib7000p_set_gpio(adap->fe_adap[0].fe, 8, 0, 1); +- } else { +- err("xc4000: unknown tuner callback command: %d\n", command); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static struct dibx000_agc_config stk7700p_7000p_xc4000_agc_config = { +- .band_caps = BAND_UHF | BAND_VHF, +- .setup = 0x64, +- .inv_gain = 0x02c8, +- .time_stabiliz = 0x15, +- .alpha_level = 0x00, +- .thlock = 0x76, +- .wbd_inv = 0x01, +- .wbd_ref = 0x0b33, +- .wbd_sel = 0x00, +- .wbd_alpha = 0x02, +- .agc1_max = 0x00, +- .agc1_min = 0x00, +- .agc2_max = 0x9b26, +- .agc2_min = 0x26ca, +- .agc1_pt1 = 0x00, +- .agc1_pt2 = 0x00, +- .agc1_pt3 = 0x00, +- .agc1_slope1 = 0x00, +- .agc1_slope2 = 0x00, +- .agc2_pt1 = 0x00, +- .agc2_pt2 = 0x80, +- .agc2_slope1 = 0x1d, +- .agc2_slope2 = 0x1d, +- .alpha_mant = 0x11, +- .alpha_exp = 0x1b, +- .beta_mant = 0x17, +- .beta_exp = 0x33, +- .perform_agc_softsplit = 0x00, +-}; +- +-static struct dibx000_bandwidth_config stk7700p_xc4000_pll_config = { +- 60000, 30000, /* internal, sampling */ +- 1, 8, 3, 1, 0, /* pll_cfg: prediv, ratio, range, reset, bypass */ +- 0, 0, 1, 1, 0, /* misc: refdiv, bypclk_div, IO_CLK_en_core, */ +- /* ADClkSrc, modulo */ +- (3 << 14) | (1 << 12) | 524, /* sad_cfg: refsel, sel, freq_15k */ +- 39370534, /* ifreq */ +- 20452225, /* timf */ +- 30000000 /* xtal */ +-}; +- +-/* FIXME: none of these inputs are validated yet */ +-static struct dib7000p_config pctv_340e_config = { +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_config_count = 1, +- .agc = &stk7700p_7000p_xc4000_agc_config, +- .bw = &stk7700p_xc4000_pll_config, +- +- .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, +- .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, +- .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +-}; +- +-/* PCTV 340e GPIOs map: +- dib0700: +- GPIO2 - CX25843 sleep +- GPIO3 - CS5340 reset +- GPIO5 - IRD +- GPIO6 - Power Supply +- GPIO8 - LNA (1=off 0=on) +- GPIO10 - CX25843 reset +- dib7000: +- GPIO8 - xc4000 reset +- */ +-static int pctv340e_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- +- /* Power Supply on */ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- msleep(50); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(100); /* Allow power supply to settle before probing */ +- +- /* cx25843 reset */ +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(1); /* cx25843 datasheet say 350us required */ +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- +- /* LNA off for now */ +- dib0700_set_gpio(adap->dev, GPIO8, GPIO_OUT, 1); +- +- /* Put the CX25843 to sleep for now since we're in digital mode */ +- dib0700_set_gpio(adap->dev, GPIO2, GPIO_OUT, 1); +- +- /* FIXME: not verified yet */ +- dib0700_ctrl_clock(adap->dev, 72, 1); +- +- msleep(500); +- +- if (dib7000pc_detection(&adap->dev->i2c_adap) == 0) { +- /* Demodulator not found for some reason? */ +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x12, +- &pctv_340e_config); +- st->is_dib7000pc = 1; +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static struct xc4000_config dib7000p_xc4000_tunerconfig = { +- .i2c_address = 0x61, +- .default_pm = 1, +- .dvb_amplitude = 0, +- .set_smoothedcvbs = 0, +- .if_khz = 5400 +-}; +- +-static int xc4000_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct i2c_adapter *tun_i2c; +- +- /* The xc4000 is not on the main i2c bus */ +- tun_i2c = dib7000p_get_i2c_master(adap->fe_adap[0].fe, +- DIBX000_I2C_INTERFACE_TUNER, 1); +- if (tun_i2c == NULL) { +- printk(KERN_ERR "Could not reach tuner i2c bus\n"); +- return 0; +- } +- +- /* Setup the reset callback */ +- adap->fe_adap[0].fe->callback = dib0700_xc4000_tuner_callback; +- +- return dvb_attach(xc4000_attach, adap->fe_adap[0].fe, tun_i2c, +- &dib7000p_xc4000_tunerconfig) +- == NULL ? -ENODEV : 0; +-} +- +-static struct lgdt3305_config hcw_lgdt3305_config = { +- .i2c_addr = 0x0e, +- .mpeg_mode = LGDT3305_MPEG_PARALLEL, +- .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, +- .tpvalid_polarity = LGDT3305_TP_VALID_LOW, +- .deny_i2c_rptr = 0, +- .spectral_inversion = 1, +- .qam_if_khz = 6000, +- .vsb_if_khz = 6000, +- .usref_8vsb = 0x0500, +-}; +- +-static struct mxl5007t_config hcw_mxl5007t_config = { +- .xtal_freq_hz = MxL_XTAL_25_MHZ, +- .if_freq_hz = MxL_IF_6_MHZ, +- .invert_if = 1, +-}; +- +-/* TIGER-ATSC map: +- GPIO0 - LNA_CTR (H: LNA power enabled, L: LNA power disabled) +- GPIO1 - ANT_SEL (H: VPA, L: MCX) +- GPIO4 - SCL2 +- GPIO6 - EN_TUNER +- GPIO7 - SDA2 +- GPIO10 - DEM_RST +- +- MXL is behind LG's i2c repeater. LG is on SCL2/SDA2 gpios on the DIB +- */ +-static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib0700_state *st = adap->dev->priv; +- +- /* Make use of the new i2c functions from FW 1.20 */ +- st->fw_use_new_i2c_api = 1; +- +- st->disable_streaming_master_mode = 1; +- +- /* fe power enable */ +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); +- msleep(30); +- dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); +- msleep(30); +- +- /* demod reset */ +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(30); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); +- msleep(30); +- dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); +- msleep(30); +- +- adap->fe_adap[0].fe = dvb_attach(lgdt3305_attach, +- &hcw_lgdt3305_config, +- &adap->dev->i2c_adap); +- +- return adap->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- return dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, 0x60, +- &hcw_mxl5007t_config) == NULL ? -ENODEV : 0; +-} +- +- +-/* DVB-USB and USB stuff follows */ +-struct usb_device_id dib0700_usb_id_table[] = { +-/* 0 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, +-/* 5 */ { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, +- { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) }, +- { USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) }, +- { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) }, +-/* 10 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV2000E) }, +- { USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700D) }, +-/* 15 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7070P) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV_DVB_T_FLASH) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7070PD) }, +- { USB_DEVICE(USB_VID_PINNACLE, +- USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T) }, +- { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500_PC) }, +-/* 20 */{ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_EXPRESS) }, +- { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U7000) }, +- { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14BR) }, +- { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000) }, +- { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3100) }, +-/* 25 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_3) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_MYTV_T) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_USB_XE) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_EXPRESSCARD_320CX) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV72E) }, +-/* 30 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73E) }, +- { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_EC372S) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_HT_EXPRESS) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS) }, +- { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P_2) }, +-/* 35 */{ USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_3) }, +- { USB_DEVICE(USB_VID_GIGABYTE, USB_PID_GIGABYTE_U8000) }, +- { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700PH) }, +- { USB_DEVICE(USB_VID_ASUS, USB_PID_ASUS_U3000H) }, +-/* 40 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV801E_SE) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_EXPRESS) }, +- { USB_DEVICE(USB_VID_TERRATEC, +- USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2) }, +- { USB_DEVICE(USB_VID_SONY, USB_PID_SONY_PLAYTV) }, +-/* 45 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_PD378S) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC) }, +- { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC_B210) }, +- { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_MC770) }, +- { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT) }, +-/* 50 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_Dlx) }, +- { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_H) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T3) }, +- { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_T5) }, +- { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D) }, +-/* 55 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_STK7700D_2) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73A) }, +- { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV73ESE) }, +- { USB_DEVICE(USB_VID_PCTV, USB_PID_PINNACLE_PCTV282E) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7770P) }, +-/* 60 */{ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_XXS_2) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, +- { USB_DEVICE_VER(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD, 0x000, 0x3f00) }, +- { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) }, +-/* 65 */{ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV73ESE) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV282E) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK8096GP) }, +- { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DIVERSITY) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090M) }, +-/* 70 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM8096MD) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM9090MD) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_NIM7090) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090PVR) }, +- { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2) }, +-/* 75 */{ USB_DEVICE(USB_VID_MEDION, USB_PID_CREATIX_CTX1921) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV340E_SE) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, +- { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, +-/* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, +- { 0 } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); +- +-#define DIB0700_DEFAULT_DEVICE_PROPERTIES \ +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ +- .usb_ctrl = DEVICE_SPECIFIC, \ +- .firmware = "dvb-usb-dib0700-1.20.fw", \ +- .download_firmware = dib0700_download_firmware, \ +- .no_reconnect = 1, \ +- .size_of_priv = sizeof(struct dib0700_state), \ +- .i2c_algo = &dib0700_i2c_algo, \ +- .identify_state = dib0700_identify_state +- +-#define DIB0700_DEFAULT_STREAMING_CONFIG(ep) \ +- .streaming_ctrl = dib0700_streaming_ctrl, \ +- .stream = { \ +- .type = USB_BULK, \ +- .count = 4, \ +- .endpoint = ep, \ +- .u = { \ +- .bulk = { \ +- .buffersize = 39480, \ +- } \ +- } \ +- } +- +-struct dvb_usb_device_properties dib0700_devices[] = { +- { +- DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk7700p_pid_filter, +- .pid_filter_ctrl = stk7700p_pid_filter_ctrl, +- .frontend_attach = stk7700p_frontend_attach, +- .tuner_attach = stk7700p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- }, +- }, +- +- .num_device_descs = 8, +- .devices = { +- { "DiBcom STK7700P reference design", +- { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, +- { NULL }, +- }, +- { "Hauppauge Nova-T Stick", +- { &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL }, +- { NULL }, +- }, +- { "AVerMedia AVerTV DVB-T Volar", +- { &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] }, +- { NULL }, +- }, +- { "Compro Videomate U500", +- { &dib0700_usb_id_table[6], &dib0700_usb_id_table[19] }, +- { NULL }, +- }, +- { "Uniwill STK7700P based (Hama and others)", +- { &dib0700_usb_id_table[7], NULL }, +- { NULL }, +- }, +- { "Leadtek Winfast DTV Dongle (STK7700P based)", +- { &dib0700_usb_id_table[8], &dib0700_usb_id_table[34] }, +- { NULL }, +- }, +- { "AVerMedia AVerTV DVB-T Express", +- { &dib0700_usb_id_table[20] }, +- { NULL }, +- }, +- { "Gigabyte U7000", +- { &dib0700_usb_id_table[21], NULL }, +- { NULL }, +- } +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = bristol_frontend_attach, +- .tuner_attach = bristol_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- }, { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = bristol_frontend_attach, +- .tuner_attach = bristol_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- } +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "Hauppauge Nova-T 500 Dual DVB-T", +- { &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7700d_frontend_attach, +- .tuner_attach = stk7700d_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- }, { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7700d_frontend_attach, +- .tuner_attach = stk7700d_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- } +- }, +- +- .num_device_descs = 5, +- .devices = { +- { "Pinnacle PCTV 2000e", +- { &dib0700_usb_id_table[11], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy DT XS Diversity", +- { &dib0700_usb_id_table[12], NULL }, +- { NULL }, +- }, +- { "Hauppauge Nova-TD Stick/Elgato Eye-TV Diversity", +- { &dib0700_usb_id_table[13], NULL }, +- { NULL }, +- }, +- { "DiBcom STK7700D reference design", +- { &dib0700_usb_id_table[14], NULL }, +- { NULL }, +- }, +- { "YUAN High-Tech DiBcom STK7700D", +- { &dib0700_usb_id_table[55], NULL }, +- { NULL }, +- }, +- +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7700P2_frontend_attach, +- .tuner_attach = stk7700d_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- }, +- }, +- +- .num_device_descs = 3, +- .devices = { +- { "ASUS My Cinema U3000 Mini DVBT Tuner", +- { &dib0700_usb_id_table[23], NULL }, +- { NULL }, +- }, +- { "Yuan EC372S", +- { &dib0700_usb_id_table[31], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy T Express", +- { &dib0700_usb_id_table[42], NULL }, +- { NULL }, +- } +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070p_frontend_attach, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 11, +- .devices = { +- { "DiBcom STK7070P reference design", +- { &dib0700_usb_id_table[15], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV DVB-T Flash Stick", +- { &dib0700_usb_id_table[16], NULL }, +- { NULL }, +- }, +- { "Artec T14BR DVB-T", +- { &dib0700_usb_id_table[22], NULL }, +- { NULL }, +- }, +- { "ASUS My Cinema U3100 Mini DVBT Tuner", +- { &dib0700_usb_id_table[24], NULL }, +- { NULL }, +- }, +- { "Hauppauge Nova-T Stick", +- { &dib0700_usb_id_table[25], NULL }, +- { NULL }, +- }, +- { "Hauppauge Nova-T MyTV.t", +- { &dib0700_usb_id_table[26], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV 72e", +- { &dib0700_usb_id_table[29], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV 73e", +- { &dib0700_usb_id_table[30], NULL }, +- { NULL }, +- }, +- { "Elgato EyeTV DTT", +- { &dib0700_usb_id_table[49], NULL }, +- { NULL }, +- }, +- { "Yuan PD378S", +- { &dib0700_usb_id_table[45], NULL }, +- { NULL }, +- }, +- { "Elgato EyeTV Dtt Dlx PD378S", +- { &dib0700_usb_id_table[50], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070p_frontend_attach, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 3, +- .devices = { +- { "Pinnacle PCTV 73A", +- { &dib0700_usb_id_table[56], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV 73e SE", +- { &dib0700_usb_id_table[57], &dib0700_usb_id_table[65], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV 282e", +- { &dib0700_usb_id_table[58], &dib0700_usb_id_table[66], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = novatd_frontend_attach, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- }, { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = novatd_frontend_attach, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- } +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "Hauppauge Nova-TD Stick (52009)", +- { &dib0700_usb_id_table[35], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070pd_frontend_attach0, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- }, { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070pd_frontend_attach1, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- } +- }, +- +- .num_device_descs = 5, +- .devices = { +- { "DiBcom STK7070PD reference design", +- { &dib0700_usb_id_table[17], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV Dual DVB-T Diversity Stick", +- { &dib0700_usb_id_table[18], NULL }, +- { NULL }, +- }, +- { "Hauppauge Nova-TD-500 (84xxx)", +- { &dib0700_usb_id_table[36], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy DT USB XS Diversity/ T5", +- { &dib0700_usb_id_table[43], +- &dib0700_usb_id_table[53], NULL}, +- { NULL }, +- }, +- { "Sony PlayTV", +- { &dib0700_usb_id_table[44], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070pd_frontend_attach0, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- }, { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7070pd_frontend_attach1, +- .tuner_attach = dib7070p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- .size_of_priv = sizeof(struct dib0700_adapter_state), +- } +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "Elgato EyeTV Diversity", +- { &dib0700_usb_id_table[68], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_NEC_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7700ph_frontend_attach, +- .tuner_attach = stk7700ph_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct +- dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 9, +- .devices = { +- { "Terratec Cinergy HT USB XE", +- { &dib0700_usb_id_table[27], NULL }, +- { NULL }, +- }, +- { "Pinnacle Expresscard 320cx", +- { &dib0700_usb_id_table[28], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy HT Express", +- { &dib0700_usb_id_table[32], NULL }, +- { NULL }, +- }, +- { "Gigabyte U8000-RH", +- { &dib0700_usb_id_table[37], NULL }, +- { NULL }, +- }, +- { "YUAN High-Tech STK7700PH", +- { &dib0700_usb_id_table[38], NULL }, +- { NULL }, +- }, +- { "Asus My Cinema-U3000Hybrid", +- { &dib0700_usb_id_table[39], NULL }, +- { NULL }, +- }, +- { "YUAN High-Tech MC770", +- { &dib0700_usb_id_table[48], NULL }, +- { NULL }, +- }, +- { "Leadtek WinFast DTV Dongle H", +- { &dib0700_usb_id_table[51], NULL }, +- { NULL }, +- }, +- { "YUAN High-Tech STK7700D", +- { &dib0700_usb_id_table[54], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = s5h1411_frontend_attach, +- .tuner_attach = xc5000_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct +- dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 2, +- .devices = { +- { "Pinnacle PCTV HD Pro USB Stick", +- { &dib0700_usb_id_table[40], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV HD USB Stick", +- { &dib0700_usb_id_table[41], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = lgdt3305_frontend_attach, +- .tuner_attach = mxl5007t_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct +- dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 2, +- .devices = { +- { "Hauppauge ATSC MiniCard (B200)", +- { &dib0700_usb_id_table[46], NULL }, +- { NULL }, +- }, +- { "Hauppauge ATSC MiniCard (B210)", +- { &dib0700_usb_id_table[47], NULL }, +- { NULL }, +- }, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = stk7770p_frontend_attach, +- .tuner_attach = dib7770p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 4, +- .devices = { +- { "DiBcom STK7770P reference design", +- { &dib0700_usb_id_table[59], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy T USB XXS (HD)/ T3", +- { &dib0700_usb_id_table[33], +- &dib0700_usb_id_table[52], +- &dib0700_usb_id_table[60], NULL}, +- { NULL }, +- }, +- { "TechniSat AirStar TeleStick 2", +- { &dib0700_usb_id_table[74], NULL }, +- { NULL }, +- }, +- { "Medion CTX1921 DVB-T USB", +- { &dib0700_usb_id_table[75], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = stk807x_frontend_attach, +- .tuner_attach = dib807x_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 3, +- .devices = { +- { "DiBcom STK807xP reference design", +- { &dib0700_usb_id_table[62], NULL }, +- { NULL }, +- }, +- { "Prolink Pixelview SBTVD", +- { &dib0700_usb_id_table[63], NULL }, +- { NULL }, +- }, +- { "EvolutePC TVWay+", +- { &dib0700_usb_id_table[64], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_NEC_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = stk807xpvr_frontend_attach0, +- .tuner_attach = dib807x_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = stk807xpvr_frontend_attach1, +- .tuner_attach = dib807x_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom STK807xPVR reference design", +- { &dib0700_usb_id_table[61], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = stk809x_frontend_attach, +- .tuner_attach = dib809x_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom STK8096GP reference design", +- { &dib0700_usb_id_table[67], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = dib90x0_pid_filter, +- .pid_filter_ctrl = dib90x0_pid_filter_ctrl, +- .frontend_attach = stk9090m_frontend_attach, +- .tuner_attach = dib9090_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom STK9090M reference design", +- { &dib0700_usb_id_table[69], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = nim8096md_frontend_attach, +- .tuner_attach = nim8096md_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom NIM8096MD reference design", +- { &dib0700_usb_id_table[70], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = dib90x0_pid_filter, +- .pid_filter_ctrl = dib90x0_pid_filter_ctrl, +- .frontend_attach = nim9090md_frontend_attach, +- .tuner_attach = nim9090md_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom NIM9090MD reference design", +- { &dib0700_usb_id_table[71], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = nim7090_frontend_attach, +- .tuner_attach = nim7090_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom NIM7090 reference design", +- { &dib0700_usb_id_table[72], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = tfe7090pvr_frontend0_attach, +- .tuner_attach = tfe7090pvr_tuner0_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = tfe7090pvr_frontend1_attach, +- .tuner_attach = tfe7090pvr_tuner1_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom TFE7090PVR reference design", +- { &dib0700_usb_id_table[73], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = pctv340e_frontend_attach, +- .tuner_attach = xc4000_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- }}, +- .size_of_priv = sizeof(struct +- dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 2, +- .devices = { +- { "Pinnacle PCTV 340e HD Pro USB Stick", +- { &dib0700_usb_id_table[76], NULL }, +- { NULL }, +- }, +- { "Pinnacle PCTV Hybrid Stick Solo", +- { &dib0700_usb_id_table[77], NULL }, +- { NULL }, +- }, +- }, +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = tfe7090e_frontend_attach, +- .tuner_attach = tfe7090e_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- } }, +- +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom TFE7090E reference design", +- { &dib0700_usb_id_table[78], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk70x0p_pid_filter, +- .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, +- .frontend_attach = tfe7790e_frontend_attach, +- .tuner_attach = tfe7790e_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x03), +- } }, +- +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom TFE7790E reference design", +- { &dib0700_usb_id_table[79], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .pid_filter = stk80xx_pid_filter, +- .pid_filter_ctrl = stk80xx_pid_filter_ctrl, +- .frontend_attach = tfe8096p_frontend_attach, +- .tuner_attach = tfe8096p_tuner_attach, +- +- DIB0700_DEFAULT_STREAMING_CONFIG(0x02), +- +- } }, +- +- .size_of_priv = +- sizeof(struct dib0700_adapter_state), +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "DiBcom TFE8096P reference design", +- { &dib0700_usb_id_table[80], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .module_name = "dib0700", +- .rc_query = dib0700_rc_query_old_firmware, +- .allowed_protos = RC_TYPE_RC5 | +- RC_TYPE_RC6 | +- RC_TYPE_NEC, +- .change_protocol = dib0700_change_protocol, +- }, +- }, +-}; +- +-int dib0700_device_count = ARRAY_SIZE(dib0700_devices); +diff --git a/drivers/media/dvb/dvb-usb/dib07x0.h b/drivers/media/dvb/dvb-usb/dib07x0.h +deleted file mode 100644 +index 7e62c10..0000000 +--- a/drivers/media/dvb/dvb-usb/dib07x0.h ++++ /dev/null +@@ -1,21 +0,0 @@ +-#ifndef _DIB07X0_H_ +-#define _DIB07X0_H_ +- +-enum dib07x0_gpios { +- GPIO0 = 0, +- GPIO1 = 2, +- GPIO2 = 3, +- GPIO3 = 4, +- GPIO4 = 5, +- GPIO5 = 6, +- GPIO6 = 8, +- GPIO7 = 10, +- GPIO8 = 11, +- GPIO9 = 14, +- GPIO10 = 15, +-}; +- +-#define GPIO_IN 0 +-#define GPIO_OUT 1 +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c +deleted file mode 100644 +index a76bbb2..0000000 +--- a/drivers/media/dvb/dvb-usb/dibusb-common.c ++++ /dev/null +@@ -1,479 +0,0 @@ +-/* Common methods for dibusb-based-receivers. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (|-able))." DVB_USB_DEBUG_STATUS); +-MODULE_LICENSE("GPL"); +- +-#define deb_info(args...) dprintk(debug,0x01,args) +- +-/* common stuff used by the different dibusb modules */ +-int dibusb_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- if (adap->priv != NULL) { +- struct dibusb_state *st = adap->priv; +- if (st->ops.fifo_ctrl != NULL) +- if (st->ops.fifo_ctrl(adap->fe_adap[0].fe, onoff)) { +- err("error while controlling the fifo of the demod."); +- return -ENODEV; +- } +- } +- return 0; +-} +-EXPORT_SYMBOL(dibusb_streaming_ctrl); +- +-int dibusb_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +-{ +- if (adap->priv != NULL) { +- struct dibusb_state *st = adap->priv; +- if (st->ops.pid_ctrl != NULL) +- st->ops.pid_ctrl(adap->fe_adap[0].fe, +- index, pid, onoff); +- } +- return 0; +-} +-EXPORT_SYMBOL(dibusb_pid_filter); +- +-int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- if (adap->priv != NULL) { +- struct dibusb_state *st = adap->priv; +- if (st->ops.pid_parse != NULL) +- if (st->ops.pid_parse(adap->fe_adap[0].fe, onoff) < 0) +- err("could not handle pid_parser"); +- } +- return 0; +-} +-EXPORT_SYMBOL(dibusb_pid_filter_ctrl); +- +-int dibusb_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 b[3]; +- int ret; +- b[0] = DIBUSB_REQ_SET_IOCTL; +- b[1] = DIBUSB_IOCTL_CMD_POWER_MODE; +- b[2] = onoff ? DIBUSB_IOCTL_POWER_WAKEUP : DIBUSB_IOCTL_POWER_SLEEP; +- ret = dvb_usb_generic_write(d,b,3); +- msleep(10); +- return ret; +-} +-EXPORT_SYMBOL(dibusb_power_ctrl); +- +-int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- u8 b[3] = { 0 }; +- int ret; +- +- if ((ret = dibusb_streaming_ctrl(adap,onoff)) < 0) +- return ret; +- +- if (onoff) { +- b[0] = DIBUSB_REQ_SET_STREAMING_MODE; +- b[1] = 0x00; +- if ((ret = dvb_usb_generic_write(adap->dev,b,2)) < 0) +- return ret; +- } +- +- b[0] = DIBUSB_REQ_SET_IOCTL; +- b[1] = onoff ? DIBUSB_IOCTL_CMD_ENABLE_STREAM : DIBUSB_IOCTL_CMD_DISABLE_STREAM; +- return dvb_usb_generic_write(adap->dev,b,3); +-} +-EXPORT_SYMBOL(dibusb2_0_streaming_ctrl); +- +-int dibusb2_0_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- if (onoff) { +- u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP }; +- return dvb_usb_generic_write(d,b,3); +- } else +- return 0; +-} +-EXPORT_SYMBOL(dibusb2_0_power_ctrl); +- +-static int dibusb_i2c_msg(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u8 sndbuf[wlen+4]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ +- /* write only ? */ +- int wo = (rbuf == NULL || rlen == 0), +- len = 2 + wlen + (wo ? 0 : 2); +- +- sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; +- sndbuf[1] = (addr << 1) | (wo ? 0 : 1); +- +- memcpy(&sndbuf[2],wbuf,wlen); +- +- if (!wo) { +- sndbuf[wlen+2] = (rlen >> 8) & 0xff; +- sndbuf[wlen+3] = rlen & 0xff; +- } +- +- return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0); +-} +- +-/* +- * I2C master xfer function +- */ +-static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i+1 < num && (msg[i].flags & I2C_M_RD) == 0 +- && (msg[i+1].flags & I2C_M_RD)) { +- if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len, +- msg[i+1].buf,msg[i+1].len) < 0) +- break; +- i++; +- } else if ((msg[i].flags & I2C_M_RD) == 0) { +- if (dibusb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) +- break; +- } else if (msg[i].addr != 0x50) { +- /* 0x50 is the address of the eeprom - we need to protect it +- * from dibusb's bad i2c implementation: reads without +- * writing the offset before are forbidden */ +- if (dibusb_i2c_msg(d, msg[i].addr, NULL, 0, msg[i].buf, msg[i].len) < 0) +- break; +- } +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 dibusb_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-struct i2c_algorithm dibusb_i2c_algo = { +- .master_xfer = dibusb_i2c_xfer, +- .functionality = dibusb_i2c_func, +-}; +-EXPORT_SYMBOL(dibusb_i2c_algo); +- +-int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val) +-{ +- u8 wbuf[1] = { offs }; +- return dibusb_i2c_msg(d, 0x50, wbuf, 1, val, 1); +-} +-EXPORT_SYMBOL(dibusb_read_eeprom_byte); +- +-/* 3000MC/P stuff */ +-// Config Adjacent channels Perf -cal22 +-static struct dibx000_agc_config dib3000p_mt2060_agc_config = { +- .band_caps = BAND_VHF | BAND_UHF, +- .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), +- +- .agc1_max = 48497, +- .agc1_min = 23593, +- .agc2_max = 46531, +- .agc2_min = 24904, +- +- .agc1_pt1 = 0x65, +- .agc1_pt2 = 0x69, +- +- .agc1_slope1 = 0x51, +- .agc1_slope2 = 0x27, +- +- .agc2_pt1 = 0, +- .agc2_pt2 = 0x33, +- +- .agc2_slope1 = 0x35, +- .agc2_slope2 = 0x37, +-}; +- +-static struct dib3000mc_config stk3000p_dib3000p_config = { +- &dib3000p_mt2060_agc_config, +- +- .max_time = 0x196, +- .ln_adc_level = 0x1cc7, +- +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_command1 = 1, +- .agc_command2 = 1, +-}; +- +-static struct dibx000_agc_config dib3000p_panasonic_agc_config = { +- .band_caps = BAND_VHF | BAND_UHF, +- .setup = (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0), +- +- .agc1_max = 56361, +- .agc1_min = 22282, +- .agc2_max = 47841, +- .agc2_min = 36045, +- +- .agc1_pt1 = 0x3b, +- .agc1_pt2 = 0x6b, +- +- .agc1_slope1 = 0x55, +- .agc1_slope2 = 0x1d, +- +- .agc2_pt1 = 0, +- .agc2_pt2 = 0x0a, +- +- .agc2_slope1 = 0x95, +- .agc2_slope2 = 0x1e, +-}; +- +-#if defined(CONFIG_DVB_DIB3000MC) || \ +- (defined(CONFIG_DVB_DIB3000MC_MODULE) && defined(MODULE)) +- +-static struct dib3000mc_config mod3000p_dib3000p_config = { +- &dib3000p_panasonic_agc_config, +- +- .max_time = 0x51, +- .ln_adc_level = 0x1cc7, +- +- .output_mpeg2_in_188_bytes = 1, +- +- .agc_command1 = 1, +- .agc_command2 = 1, +-}; +- +-int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && +- adap->dev->udev->descriptor.idProduct == +- USB_PID_LITEON_DVB_T_WARM) { +- msleep(1000); +- } +- +- adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, +- &adap->dev->i2c_adap, +- DEFAULT_DIB3000P_I2C_ADDRESS, +- &mod3000p_dib3000p_config); +- if ((adap->fe_adap[0].fe) == NULL) +- adap->fe_adap[0].fe = dvb_attach(dib3000mc_attach, +- &adap->dev->i2c_adap, +- DEFAULT_DIB3000MC_I2C_ADDRESS, +- &mod3000p_dib3000p_config); +- if ((adap->fe_adap[0].fe) != NULL) { +- if (adap->priv != NULL) { +- struct dibusb_state *st = adap->priv; +- st->ops.pid_parse = dib3000mc_pid_parse; +- st->ops.pid_ctrl = dib3000mc_pid_control; +- } +- return 0; +- } +- return -ENODEV; +-} +-EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach); +- +-static struct mt2060_config stk3000p_mt2060_config = { +- 0x60 +-}; +- +-int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dibusb_state *st = adap->priv; +- u8 a,b; +- u16 if1 = 1220; +- struct i2c_adapter *tun_i2c; +- +- // First IF calibration for Liteon Sticks +- if (adap->dev->udev->descriptor.idVendor == USB_VID_LITEON && +- adap->dev->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) { +- +- dibusb_read_eeprom_byte(adap->dev,0x7E,&a); +- dibusb_read_eeprom_byte(adap->dev,0x7F,&b); +- +- if (a == 0x00) +- if1 += b; +- else if (a == 0x80) +- if1 -= b; +- else +- warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b); +- +- } else if (adap->dev->udev->descriptor.idVendor == USB_VID_DIBCOM && +- adap->dev->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) { +- u8 desc; +- dibusb_read_eeprom_byte(adap->dev, 7, &desc); +- if (desc == 2) { +- a = 127; +- do { +- dibusb_read_eeprom_byte(adap->dev, a, &desc); +- a--; +- } while (a > 7 && (desc == 0xff || desc == 0x00)); +- if (desc & 0x80) +- if1 -= (0xff - desc); +- else +- if1 += desc; +- } +- } +- +- tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe_adap[0].fe, 1); +- if (dvb_attach(mt2060_attach, adap->fe_adap[0].fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { +- /* not found - use panasonic pll parameters */ +- if (dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) +- return -ENOMEM; +- } else { +- st->mt2060_present = 1; +- /* set the correct parameters for the dib3000p */ +- dib3000mc_set_config(adap->fe_adap[0].fe, &stk3000p_dib3000p_config); +- } +- return 0; +-} +-EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach); +-#endif +- +-/* +- * common remote control stuff +- */ +-struct rc_map_table rc_map_dibusb_table[] = { +- /* Key codes for the little Artec T1/Twinhan/HAMA/ remote. */ +- { 0x0016, KEY_POWER }, +- { 0x0010, KEY_MUTE }, +- { 0x0003, KEY_1 }, +- { 0x0001, KEY_2 }, +- { 0x0006, KEY_3 }, +- { 0x0009, KEY_4 }, +- { 0x001d, KEY_5 }, +- { 0x001f, KEY_6 }, +- { 0x000d, KEY_7 }, +- { 0x0019, KEY_8 }, +- { 0x001b, KEY_9 }, +- { 0x0015, KEY_0 }, +- { 0x0005, KEY_CHANNELUP }, +- { 0x0002, KEY_CHANNELDOWN }, +- { 0x001e, KEY_VOLUMEUP }, +- { 0x000a, KEY_VOLUMEDOWN }, +- { 0x0011, KEY_RECORD }, +- { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ +- { 0x0014, KEY_PLAY }, +- { 0x001a, KEY_STOP }, +- { 0x0040, KEY_REWIND }, +- { 0x0012, KEY_FASTFORWARD }, +- { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ +- { 0x004c, KEY_PAUSE }, +- { 0x004d, KEY_SCREEN }, /* Full screen mode. */ +- { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ +- /* additional keys TwinHan VisionPlus, the Artec seemingly not have */ +- { 0x000c, KEY_CANCEL }, /* Cancel */ +- { 0x001c, KEY_EPG }, /* EPG */ +- { 0x0000, KEY_TAB }, /* Tab */ +- { 0x0048, KEY_INFO }, /* Preview */ +- { 0x0004, KEY_LIST }, /* RecordList */ +- { 0x000f, KEY_TEXT }, /* Teletext */ +- /* Key codes for the KWorld/ADSTech/JetWay remote. */ +- { 0x8612, KEY_POWER }, +- { 0x860f, KEY_SELECT }, /* source */ +- { 0x860c, KEY_UNKNOWN }, /* scan */ +- { 0x860b, KEY_EPG }, +- { 0x8610, KEY_MUTE }, +- { 0x8601, KEY_1 }, +- { 0x8602, KEY_2 }, +- { 0x8603, KEY_3 }, +- { 0x8604, KEY_4 }, +- { 0x8605, KEY_5 }, +- { 0x8606, KEY_6 }, +- { 0x8607, KEY_7 }, +- { 0x8608, KEY_8 }, +- { 0x8609, KEY_9 }, +- { 0x860a, KEY_0 }, +- { 0x8618, KEY_ZOOM }, +- { 0x861c, KEY_UNKNOWN }, /* preview */ +- { 0x8613, KEY_UNKNOWN }, /* snap */ +- { 0x8600, KEY_UNDO }, +- { 0x861d, KEY_RECORD }, +- { 0x860d, KEY_STOP }, +- { 0x860e, KEY_PAUSE }, +- { 0x8616, KEY_PLAY }, +- { 0x8611, KEY_BACK }, +- { 0x8619, KEY_FORWARD }, +- { 0x8614, KEY_UNKNOWN }, /* pip */ +- { 0x8615, KEY_ESC }, +- { 0x861a, KEY_UP }, +- { 0x861e, KEY_DOWN }, +- { 0x861f, KEY_LEFT }, +- { 0x861b, KEY_RIGHT }, +- +- /* Key codes for the DiBcom MOD3000 remote. */ +- { 0x8000, KEY_MUTE }, +- { 0x8001, KEY_TEXT }, +- { 0x8002, KEY_HOME }, +- { 0x8003, KEY_POWER }, +- +- { 0x8004, KEY_RED }, +- { 0x8005, KEY_GREEN }, +- { 0x8006, KEY_YELLOW }, +- { 0x8007, KEY_BLUE }, +- +- { 0x8008, KEY_DVD }, +- { 0x8009, KEY_AUDIO }, +- { 0x800a, KEY_IMAGES }, /* Pictures */ +- { 0x800b, KEY_VIDEO }, +- +- { 0x800c, KEY_BACK }, +- { 0x800d, KEY_UP }, +- { 0x800e, KEY_RADIO }, +- { 0x800f, KEY_EPG }, +- +- { 0x8010, KEY_LEFT }, +- { 0x8011, KEY_OK }, +- { 0x8012, KEY_RIGHT }, +- { 0x8013, KEY_UNKNOWN }, /* SAP */ +- +- { 0x8014, KEY_TV }, +- { 0x8015, KEY_DOWN }, +- { 0x8016, KEY_MENU }, /* DVD Menu */ +- { 0x8017, KEY_LAST }, +- +- { 0x8018, KEY_RECORD }, +- { 0x8019, KEY_STOP }, +- { 0x801a, KEY_PAUSE }, +- { 0x801b, KEY_PLAY }, +- +- { 0x801c, KEY_PREVIOUS }, +- { 0x801d, KEY_REWIND }, +- { 0x801e, KEY_FASTFORWARD }, +- { 0x801f, KEY_NEXT}, +- +- { 0x8040, KEY_1 }, +- { 0x8041, KEY_2 }, +- { 0x8042, KEY_3 }, +- { 0x8043, KEY_CHANNELUP }, +- +- { 0x8044, KEY_4 }, +- { 0x8045, KEY_5 }, +- { 0x8046, KEY_6 }, +- { 0x8047, KEY_CHANNELDOWN }, +- +- { 0x8048, KEY_7 }, +- { 0x8049, KEY_8 }, +- { 0x804a, KEY_9 }, +- { 0x804b, KEY_VOLUMEUP }, +- +- { 0x804c, KEY_CLEAR }, +- { 0x804d, KEY_0 }, +- { 0x804e, KEY_ENTER }, +- { 0x804f, KEY_VOLUMEDOWN }, +-}; +-EXPORT_SYMBOL(rc_map_dibusb_table); +- +-int dibusb_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- u8 key[5],cmd = DIBUSB_REQ_POLL_REMOTE; +- dvb_usb_generic_rw(d,&cmd,1,key,5,0); +- dvb_usb_nec_rc_key_to_event(d,key,event,state); +- if (key[0] != 0) +- deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); +- return 0; +-} +-EXPORT_SYMBOL(dibusb_rc_query); +diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c +deleted file mode 100644 +index a4ac37e..0000000 +--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c ++++ /dev/null +@@ -1,471 +0,0 @@ +-/* DVB USB compliant linux driver for mobile DVB-T USB devices based on +- * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-B) +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * based on GPL code from DiBcom, which has +- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int dib3000mb_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dibusb_state *st = adap->priv; +- +- return st->ops.tuner_pass_ctrl(fe, enable, st->tuner_addr); +-} +- +-static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dib3000_config demod_cfg; +- struct dibusb_state *st = adap->priv; +- +- demod_cfg.demod_address = 0x8; +- +- adap->fe_adap[0].fe = dvb_attach(dib3000mb_attach, &demod_cfg, +- &adap->dev->i2c_adap, &st->ops); +- if ((adap->fe_adap[0].fe) == NULL) +- return -ENODEV; +- +- adap->fe_adap[0].fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl; +- +- return 0; +-} +- +-static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dibusb_state *st = adap->priv; +- +- st->tuner_addr = 0x61; +- +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, +- DVB_PLL_TUA6010XS); +- return 0; +-} +- +-static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct dibusb_state *st = adap->priv; +- +- st->tuner_addr = 0x60; +- +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, +- DVB_PLL_TDA665X); +- return 0; +-} +- +-/* Some of the Artec 1.1 device aren't equipped with the default tuner +- * (Thomson Cable), but with a Panasonic ENV77H11D5. This function figures +- * this out. */ +-static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap) +-{ +- u8 b[2] = { 0,0 }, b2[1]; +- int ret = 0; +- struct i2c_msg msg[2] = { +- { .flags = 0, .buf = b, .len = 2 }, +- { .flags = I2C_M_RD, .buf = b2, .len = 1 }, +- }; +- struct dibusb_state *st = adap->priv; +- +- /* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */ +- msg[0].addr = msg[1].addr = st->tuner_addr = 0x60; +- +- if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) +- adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1); +- +- if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) { +- err("tuner i2c write failed."); +- ret = -EREMOTEIO; +- } +- +- if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl) +- adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0); +- +- if (b2[0] == 0xfe) { +- info("This device has the Thomson Cable onboard. Which is default."); +- ret = dibusb_thomson_tuner_attach(adap); +- } else { +- info("This device has the Panasonic ENV77H11D5 onboard."); +- ret = dibusb_panasonic_tuner_attach(adap); +- } +- +- return ret; +-} +- +-/* USB Driver stuff */ +-static struct dvb_usb_device_properties dibusb1_1_properties; +-static struct dvb_usb_device_properties dibusb1_1_an2235_properties; +-static struct dvb_usb_device_properties dibusb2_0b_properties; +-static struct dvb_usb_device_properties artec_t1_usb2_properties; +- +-static int dibusb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &dibusb1_1_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &dibusb1_1_an2235_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &dibusb2_0b_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &artec_t1_usb2_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- +- return -EINVAL; +-} +- +-/* do not change the order of the ID table */ +-static struct usb_device_id dibusb_dib3000mb_table [] = { +-/* 00 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_COLD) }, +-/* 01 */ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_AVERMEDIA_DVBT_USB_WARM) }, +-/* 02 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_COLD) }, +-/* 03 */ { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_DVBU2000_WARM) }, +-/* 04 */ { USB_DEVICE(USB_VID_COMPRO_UNK, USB_PID_COMPRO_DVBU2000_UNK_COLD) }, +-/* 05 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_COLD) }, +-/* 06 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3000_WARM) }, +-/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_COLD) }, +-/* 08 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_KWORLD_VSTREAM_WARM) }, +-/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_COLD) }, +-/* 10 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB_WARM) }, +-/* 11 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_COLD) }, +-/* 12 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_DIBCOM_MOD3000_WARM) }, +-/* 13 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_COLD) }, +-/* 14 */ { USB_DEVICE(USB_VID_HYPER_PALTEK, USB_PID_UNK_HYPER_PALTEK_WARM) }, +-/* 15 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_COLD) }, +-/* 16 */ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7041_WARM) }, +-/* 17 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_COLD) }, +-/* 18 */ { USB_DEVICE(USB_VID_TWINHAN, USB_PID_TWINHAN_VP7041_WARM) }, +-/* 19 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_COLD) }, +-/* 20 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_WARM) }, +-/* 21 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_COLD) }, +-/* 22 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_AN2235_WARM) }, +-/* 23 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_COLD) }, +- +-/* device ID with default DIBUSB2_0-firmware and with the hacked firmware */ +-/* 24 */ { USB_DEVICE(USB_VID_ADSTECH, USB_PID_ADSTECH_USB2_WARM) }, +-/* 25 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_COLD) }, +-/* 26 */ { USB_DEVICE(USB_VID_KYE, USB_PID_KYE_DVB_T_WARM) }, +- +-/* 27 */ { USB_DEVICE(USB_VID_KWORLD, USB_PID_KWORLD_VSTREAM_COLD) }, +- +-/* 28 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, +-/* 29 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, +- +-/* +- * XXX: As Artec just 'forgot' to program the EEPROM on some Artec T1 devices +- * we don't catch these faulty IDs (namely 'Cypress FX1 USB controller') that +- * have been left on the device. If you don't have such a device but an Artec +- * device that's supposed to work with this driver but is not detected by it, +- * free to enable CONFIG_DVB_USB_DIBUSB_MB_FAULTY via your kernel config. +- */ +- +-#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY +-/* 30 */ { USB_DEVICE(USB_VID_ANCHOR, USB_PID_ULTIMA_TVBOX_ANCHOR_COLD) }, +-#endif +- +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, dibusb_dib3000mb_table); +- +-static struct dvb_usb_device_properties dibusb1_1_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_AN2135, +- +- .firmware = "dvb-usb-dibusb-5.0.0.11.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 16, +- +- .streaming_ctrl = dibusb_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mb_frontend_attach, +- .tuner_attach = dibusb_tuner_probe_and_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- +- .power_ctrl = dibusb_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_dibusb_table, +- .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ +- .rc_query = dibusb_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 9, +- .devices = { +- { "AVerMedia AverTV DVBT USB1.1", +- { &dibusb_dib3000mb_table[0], NULL }, +- { &dibusb_dib3000mb_table[1], NULL }, +- }, +- { "Compro Videomate DVB-U2000 - DVB-T USB1.1 (please confirm to linux-dvb)", +- { &dibusb_dib3000mb_table[2], &dibusb_dib3000mb_table[4], NULL}, +- { &dibusb_dib3000mb_table[3], NULL }, +- }, +- { "DiBcom USB1.1 DVB-T reference design (MOD3000)", +- { &dibusb_dib3000mb_table[5], NULL }, +- { &dibusb_dib3000mb_table[6], NULL }, +- }, +- { "KWorld V-Stream XPERT DTV - DVB-T USB1.1", +- { &dibusb_dib3000mb_table[7], NULL }, +- { &dibusb_dib3000mb_table[8], NULL }, +- }, +- { "Grandtec USB1.1 DVB-T", +- { &dibusb_dib3000mb_table[9], &dibusb_dib3000mb_table[11], NULL }, +- { &dibusb_dib3000mb_table[10], &dibusb_dib3000mb_table[12], NULL }, +- }, +- { "Unknown USB1.1 DVB-T device ???? please report the name to the author", +- { &dibusb_dib3000mb_table[13], NULL }, +- { &dibusb_dib3000mb_table[14], NULL }, +- }, +- { "TwinhanDTV USB-Ter USB1.1 / Magic Box I / HAMA USB1.1 DVB-T device", +- { &dibusb_dib3000mb_table[15], &dibusb_dib3000mb_table[17], NULL}, +- { &dibusb_dib3000mb_table[16], &dibusb_dib3000mb_table[18], NULL}, +- }, +- { "Artec T1 USB1.1 TVBOX with AN2135", +- { &dibusb_dib3000mb_table[19], NULL }, +- { &dibusb_dib3000mb_table[20], NULL }, +- }, +- { "VideoWalker DVB-T USB", +- { &dibusb_dib3000mb_table[25], NULL }, +- { &dibusb_dib3000mb_table[26], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = CYPRESS_AN2235, +- +- .firmware = "dvb-usb-dibusb-an2235-01.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER, +- .pid_filter_count = 16, +- +- .streaming_ctrl = dibusb_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mb_frontend_attach, +- .tuner_attach = dibusb_tuner_probe_and_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- }, +- }, +- .power_ctrl = dibusb_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_dibusb_table, +- .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ +- .rc_query = dibusb_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +-#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY +- .num_device_descs = 2, +-#else +- .num_device_descs = 1, +-#endif +- .devices = { +- { "Artec T1 USB1.1 TVBOX with AN2235", +- { &dibusb_dib3000mb_table[21], NULL }, +- { &dibusb_dib3000mb_table[22], NULL }, +- }, +-#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY +- { "Artec T1 USB1.1 TVBOX with AN2235 (faulty USB IDs)", +- { &dibusb_dib3000mb_table[30], NULL }, +- { NULL }, +- }, +- { NULL }, +-#endif +- } +-}; +- +-static struct dvb_usb_device_properties dibusb2_0b_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .firmware = "dvb-usb-adstech-usb2-02.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 16, +- +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mb_frontend_attach, +- .tuner_attach = dibusb_thomson_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- .power_ctrl = dibusb2_0_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_dibusb_table, +- .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ +- .rc_query = dibusb_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 2, +- .devices = { +- { "KWorld/ADSTech Instant DVB-T USB2.0", +- { &dibusb_dib3000mb_table[23], NULL }, +- { &dibusb_dib3000mb_table[24], NULL }, +- }, +- { "KWorld Xpert DVB-T USB2.0", +- { &dibusb_dib3000mb_table[27], NULL }, +- { NULL } +- }, +- { NULL }, +- } +-}; +- +-static struct dvb_usb_device_properties artec_t1_usb2_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .firmware = "dvb-usb-dibusb-6.0.0.8.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 16, +- +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mb_frontend_attach, +- .tuner_attach = dibusb_tuner_probe_and_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- .power_ctrl = dibusb2_0_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_dibusb_table, +- .rc_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */ +- .rc_query = dibusb_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Artec T1 USB2.0", +- { &dibusb_dib3000mb_table[28], NULL }, +- { &dibusb_dib3000mb_table[29], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct usb_driver dibusb_driver = { +- .name = "dvb_usb_dibusb_mb", +- .probe = dibusb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dibusb_dib3000mb_table, +-}; +- +-module_usb_driver(dibusb_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for DiBcom USB DVB-T devices (DiB3000M-B based)"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c +deleted file mode 100644 +index 9d1a59d..0000000 +--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c ++++ /dev/null +@@ -1,149 +0,0 @@ +-/* DVB USB compliant linux driver for mobile DVB-T USB devices based on +- * reference designs made by DiBcom (http://www.dibcom.fr/) (DiB3000M-C/P) +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * based on GPL code from DiBcom, which has +- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-/* USB Driver stuff */ +-static struct dvb_usb_device_properties dibusb_mc_properties; +- +-static int dibusb_mc_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &dibusb_mc_properties, THIS_MODULE, +- NULL, adapter_nr); +-} +- +-/* do not change the order of the ID table */ +-static struct usb_device_id dibusb_dib3000mc_table [] = { +-/* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) }, +-/* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) }, +-/* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) }, +-/* 03 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, // ( ? ) +-/* 04 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_COLD) }, +-/* 05 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_WARM) }, +-/* 06 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_COLD) }, +-/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_WARM) }, +-/* 08 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_COLD) }, +-/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_WARM) }, +-/* 10 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_COLD) }, +-/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) }, +-/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) }, +-/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) }, +-/* 14 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) }, +-/* 15 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table); +- +-static struct dvb_usb_device_properties dibusb_mc_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-dibusb-6.0.0.8.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mc_frontend_attach, +- .tuner_attach = dibusb_dib3000mc_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- .power_ctrl = dibusb2_0_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = DEFAULT_RC_INTERVAL, +- .rc_map_table = rc_map_dibusb_table, +- .rc_map_size = 111, /* FIXME */ +- .rc_query = dibusb_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 8, +- .devices = { +- { "DiBcom USB2.0 DVB-T reference design (MOD3000P)", +- { &dibusb_dib3000mc_table[0], NULL }, +- { &dibusb_dib3000mc_table[1], NULL }, +- }, +- { "Artec T1 USB2.0 TVBOX (please check the warm ID)", +- { &dibusb_dib3000mc_table[2], NULL }, +- { &dibusb_dib3000mc_table[3], NULL }, +- }, +- { "LITE-ON USB2.0 DVB-T Tuner", +- /* Also rebranded as Intuix S800, Toshiba */ +- { &dibusb_dib3000mc_table[4], NULL }, +- { &dibusb_dib3000mc_table[5], NULL }, +- }, +- { "MSI Digivox Mini SL", +- { &dibusb_dib3000mc_table[6], NULL }, +- { &dibusb_dib3000mc_table[7], NULL }, +- }, +- { "GRAND - USB2.0 DVB-T adapter", +- { &dibusb_dib3000mc_table[8], NULL }, +- { &dibusb_dib3000mc_table[9], NULL }, +- }, +- { "Artec T14 - USB2.0 DVB-T", +- { &dibusb_dib3000mc_table[10], NULL }, +- { &dibusb_dib3000mc_table[11], NULL }, +- }, +- { "Leadtek - USB2.0 Winfast DTV dongle", +- { &dibusb_dib3000mc_table[12], NULL }, +- { &dibusb_dib3000mc_table[13], NULL }, +- }, +- { "Humax/Coex DVB-T USB Stick 2.0 High Speed", +- { &dibusb_dib3000mc_table[14], NULL }, +- { &dibusb_dib3000mc_table[15], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct usb_driver dibusb_mc_driver = { +- .name = "dvb_usb_dibusb_mc", +- .probe = dibusb_mc_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dibusb_dib3000mc_table, +-}; +- +-module_usb_driver(dibusb_mc_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h +deleted file mode 100644 +index e47c321..0000000 +--- a/drivers/media/dvb/dvb-usb/dibusb.h ++++ /dev/null +@@ -1,131 +0,0 @@ +-/* Header file for all dibusb-based-receivers. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_DIBUSB_H_ +-#define _DVB_USB_DIBUSB_H_ +- +-#ifndef DVB_USB_LOG_PREFIX +- #define DVB_USB_LOG_PREFIX "dibusb" +-#endif +-#include "dvb-usb.h" +- +-#include "dib3000.h" +-#include "dib3000mc.h" +-#include "mt2060.h" +- +-/* +- * protocol of all dibusb related devices +- */ +- +-/* +- * bulk msg to/from endpoint 0x01 +- * +- * general structure: +- * request_byte parameter_bytes +- */ +- +-#define DIBUSB_REQ_START_READ 0x00 +-#define DIBUSB_REQ_START_DEMOD 0x01 +- +-/* +- * i2c read +- * bulk write: 0x02 ((7bit i2c_addr << 1) & 0x01) register_bytes length_word +- * bulk read: byte_buffer (length_word bytes) +- */ +-#define DIBUSB_REQ_I2C_READ 0x02 +- +-/* +- * i2c write +- * bulk write: 0x03 (7bit i2c_addr << 1) register_bytes value_bytes +- */ +-#define DIBUSB_REQ_I2C_WRITE 0x03 +- +-/* +- * polling the value of the remote control +- * bulk write: 0x04 +- * bulk read: byte_buffer (5 bytes) +- */ +-#define DIBUSB_REQ_POLL_REMOTE 0x04 +- +-/* additional status values for Hauppauge Remote Control Protocol */ +-#define DIBUSB_RC_HAUPPAUGE_KEY_PRESSED 0x01 +-#define DIBUSB_RC_HAUPPAUGE_KEY_EMPTY 0x03 +- +-/* streaming mode: +- * bulk write: 0x05 mode_byte +- * +- * mode_byte is mostly 0x00 +- */ +-#define DIBUSB_REQ_SET_STREAMING_MODE 0x05 +- +-/* interrupt the internal read loop, when blocking */ +-#define DIBUSB_REQ_INTR_READ 0x06 +- +-/* io control +- * 0x07 cmd_byte param_bytes +- * +- * param_bytes can be up to 32 bytes +- * +- * cmd_byte function parameter name +- * 0x00 power mode +- * 0x00 sleep +- * 0x01 wakeup +- * +- * 0x01 enable streaming +- * 0x02 disable streaming +- * +- * +- */ +-#define DIBUSB_REQ_SET_IOCTL 0x07 +- +-/* IOCTL commands */ +- +-/* change the power mode in firmware */ +-#define DIBUSB_IOCTL_CMD_POWER_MODE 0x00 +-#define DIBUSB_IOCTL_POWER_SLEEP 0x00 +-#define DIBUSB_IOCTL_POWER_WAKEUP 0x01 +- +-/* modify streaming of the FX2 */ +-#define DIBUSB_IOCTL_CMD_ENABLE_STREAM 0x01 +-#define DIBUSB_IOCTL_CMD_DISABLE_STREAM 0x02 +- +-struct dibusb_state { +- struct dib_fe_xfer_ops ops; +- int mt2060_present; +- u8 tuner_addr; +-}; +- +-struct dibusb_device_state { +- /* for RC5 remote control */ +- int old_toggle; +- int last_repeat_count; +-}; +- +-extern struct i2c_algorithm dibusb_i2c_algo; +- +-extern int dibusb_dib3000mc_frontend_attach(struct dvb_usb_adapter *); +-extern int dibusb_dib3000mc_tuner_attach (struct dvb_usb_adapter *); +- +-extern int dibusb_streaming_ctrl(struct dvb_usb_adapter *, int); +-extern int dibusb_pid_filter(struct dvb_usb_adapter *, int, u16, int); +-extern int dibusb_pid_filter_ctrl(struct dvb_usb_adapter *, int); +-extern int dibusb2_0_streaming_ctrl(struct dvb_usb_adapter *, int); +- +-extern int dibusb_power_ctrl(struct dvb_usb_device *, int); +-extern int dibusb2_0_power_ctrl(struct dvb_usb_device *, int); +- +-#define DEFAULT_RC_INTERVAL 150 +-//#define DEFAULT_RC_INTERVAL 100000 +- +-extern struct rc_map_table rc_map_dibusb_table[]; +-extern int dibusb_rc_query(struct dvb_usb_device *, u32 *, int *); +-extern int dibusb_read_eeprom_byte(struct dvb_usb_device *, u8, u8 *); +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c +deleted file mode 100644 +index ff34419..0000000 +--- a/drivers/media/dvb/dvb-usb/digitv.c ++++ /dev/null +@@ -1,354 +0,0 @@ +-/* DVB USB compliant linux driver for Nebula Electronics uDigiTV DVB-T USB2.0 +- * receiver +- * +- * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * partly based on the SDK published by Nebula Electronics +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "digitv.h" +- +-#include "mt352.h" +-#include "nxt6000.h" +- +-/* debug */ +-static int dvb_usb_digitv_debug; +-module_param_named(debug,dvb_usb_digitv_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args) +- +-static int digitv_ctrl_msg(struct dvb_usb_device *d, +- u8 cmd, u8 vv, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- int wo = (rbuf == NULL || rlen == 0); /* write-only */ +- u8 sndbuf[7],rcvbuf[7]; +- memset(sndbuf,0,7); memset(rcvbuf,0,7); +- +- sndbuf[0] = cmd; +- sndbuf[1] = vv; +- sndbuf[2] = wo ? wlen : rlen; +- +- if (wo) { +- memcpy(&sndbuf[3],wbuf,wlen); +- dvb_usb_generic_write(d,sndbuf,7); +- } else { +- dvb_usb_generic_rw(d,sndbuf,7,rcvbuf,7,10); +- memcpy(rbuf,&rcvbuf[3],rlen); +- } +- return 0; +-} +- +-/* I2C */ +-static int digitv_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- if (num > 2) +- warn("more than 2 i2c messages at a time is not handled yet. TODO."); +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { +- if (digitv_ctrl_msg(d, USB_READ_COFDM, msg[i].buf[0], NULL, 0, +- msg[i+1].buf,msg[i+1].len) < 0) +- break; +- i++; +- } else +- if (digitv_ctrl_msg(d,USB_WRITE_COFDM, msg[i].buf[0], +- &msg[i].buf[1],msg[i].len-1,NULL,0) < 0) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 digitv_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm digitv_i2c_algo = { +- .master_xfer = digitv_i2c_xfer, +- .functionality = digitv_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static int digitv_identify_state (struct usb_device *udev, struct +- dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, +- int *cold) +-{ +- *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; +- return 0; +-} +- +-static int digitv_mt352_demod_init(struct dvb_frontend *fe) +-{ +- static u8 reset_buf[] = { 0x89, 0x38, 0x8a, 0x2d, 0x50, 0x80 }; +- static u8 init_buf[] = { 0x68, 0xa0, 0x8e, 0x40, 0x53, 0x50, +- 0x67, 0x20, 0x7d, 0x01, 0x7c, 0x00, 0x7a, 0x00, +- 0x79, 0x20, 0x57, 0x05, 0x56, 0x31, 0x88, 0x0f, +- 0x75, 0x32 }; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(reset_buf); i += 2) +- mt352_write(fe, &reset_buf[i], 2); +- +- msleep(1); +- +- for (i = 0; i < ARRAY_SIZE(init_buf); i += 2) +- mt352_write(fe, &init_buf[i], 2); +- +- return 0; +-} +- +-static struct mt352_config digitv_mt352_config = { +- .demod_init = digitv_mt352_demod_init, +-}; +- +-static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- u8 b[5]; +- +- fe->ops.tuner_ops.calc_regs(fe, b, sizeof(b)); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- return digitv_ctrl_msg(adap->dev, USB_WRITE_TUNER, 0, &b[1], 4, NULL, 0); +-} +- +-static struct nxt6000_config digitv_nxt6000_config = { +- .clock_inversion = 1, +-}; +- +-static int digitv_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct digitv_state *st = adap->dev->priv; +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, &digitv_mt352_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) { +- st->is_nxt6000 = 0; +- return 0; +- } +- adap->fe_adap[0].fe = dvb_attach(nxt6000_attach, +- &digitv_nxt6000_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) != NULL) { +- st->is_nxt6000 = 1; +- return 0; +- } +- return -EIO; +-} +- +-static int digitv_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- struct digitv_state *st = adap->dev->priv; +- +- if (!dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, NULL, DVB_PLL_TDED4)) +- return -ENODEV; +- +- if (st->is_nxt6000) +- adap->fe_adap[0].fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; +- +- return 0; +-} +- +-static struct rc_map_table rc_map_digitv_table[] = { +- { 0x5f55, KEY_0 }, +- { 0x6f55, KEY_1 }, +- { 0x9f55, KEY_2 }, +- { 0xaf55, KEY_3 }, +- { 0x5f56, KEY_4 }, +- { 0x6f56, KEY_5 }, +- { 0x9f56, KEY_6 }, +- { 0xaf56, KEY_7 }, +- { 0x5f59, KEY_8 }, +- { 0x6f59, KEY_9 }, +- { 0x9f59, KEY_TV }, +- { 0xaf59, KEY_AUX }, +- { 0x5f5a, KEY_DVD }, +- { 0x6f5a, KEY_POWER }, +- { 0x9f5a, KEY_CAMERA }, /* labelled 'Picture' */ +- { 0xaf5a, KEY_AUDIO }, +- { 0x5f65, KEY_INFO }, +- { 0x6f65, KEY_F13 }, /* 16:9 */ +- { 0x9f65, KEY_F14 }, /* 14:9 */ +- { 0xaf65, KEY_EPG }, +- { 0x5f66, KEY_EXIT }, +- { 0x6f66, KEY_MENU }, +- { 0x9f66, KEY_UP }, +- { 0xaf66, KEY_DOWN }, +- { 0x5f69, KEY_LEFT }, +- { 0x6f69, KEY_RIGHT }, +- { 0x9f69, KEY_ENTER }, +- { 0xaf69, KEY_CHANNELUP }, +- { 0x5f6a, KEY_CHANNELDOWN }, +- { 0x6f6a, KEY_VOLUMEUP }, +- { 0x9f6a, KEY_VOLUMEDOWN }, +- { 0xaf6a, KEY_RED }, +- { 0x5f95, KEY_GREEN }, +- { 0x6f95, KEY_YELLOW }, +- { 0x9f95, KEY_BLUE }, +- { 0xaf95, KEY_SUBTITLE }, +- { 0x5f96, KEY_F15 }, /* AD */ +- { 0x6f96, KEY_TEXT }, +- { 0x9f96, KEY_MUTE }, +- { 0xaf96, KEY_REWIND }, +- { 0x5f99, KEY_STOP }, +- { 0x6f99, KEY_PLAY }, +- { 0x9f99, KEY_FASTFORWARD }, +- { 0xaf99, KEY_F16 }, /* chapter */ +- { 0x5f9a, KEY_PAUSE }, +- { 0x6f9a, KEY_PLAY }, +- { 0x9f9a, KEY_RECORD }, +- { 0xaf9a, KEY_F17 }, /* picture in picture */ +- { 0x5fa5, KEY_KPPLUS }, /* zoom in */ +- { 0x6fa5, KEY_KPMINUS }, /* zoom out */ +- { 0x9fa5, KEY_F18 }, /* capture */ +- { 0xafa5, KEY_F19 }, /* web */ +- { 0x5fa6, KEY_EMAIL }, +- { 0x6fa6, KEY_PHONE }, +- { 0x9fa6, KEY_PC }, +-}; +- +-static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- int i; +- u8 key[5]; +- u8 b[4] = { 0 }; +- +- *event = 0; +- *state = REMOTE_NO_KEY_PRESSED; +- +- digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4); +- +- /* Tell the device we've read the remote. Not sure how necessary +- this is, but the Nebula SDK does it. */ +- digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); +- +- /* if something is inside the buffer, simulate key press */ +- if (key[1] != 0) +- { +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { +- if (rc5_custom(&d->props.rc.legacy.rc_map_table[i]) == key[1] && +- rc5_data(&d->props.rc.legacy.rc_map_table[i]) == key[2]) { +- *event = d->props.rc.legacy.rc_map_table[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- return 0; +- } +- } +- } +- +- if (key[0] != 0) +- deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); +- return 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties digitv_properties; +- +-static int digitv_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- int ret = dvb_usb_device_init(intf, &digitv_properties, THIS_MODULE, &d, +- adapter_nr); +- if (ret == 0) { +- u8 b[4] = { 0 }; +- +- if (d != NULL) { /* do that only when the firmware is loaded */ +- b[0] = 1; +- digitv_ctrl_msg(d,USB_WRITE_REMOTE_TYPE,0,b,4,NULL,0); +- +- b[0] = 0; +- digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0); +- } +- } +- return ret; +-} +- +-static struct usb_device_id digitv_table [] = { +- { USB_DEVICE(USB_VID_ANCHOR, USB_PID_NEBULA_DIGITV) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, digitv_table); +- +-static struct dvb_usb_device_properties digitv_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-digitv-02.fw", +- +- .size_of_priv = sizeof(struct digitv_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = digitv_frontend_attach, +- .tuner_attach = digitv_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .identify_state = digitv_identify_state, +- +- .rc.legacy = { +- .rc_interval = 1000, +- .rc_map_table = rc_map_digitv_table, +- .rc_map_size = ARRAY_SIZE(rc_map_digitv_table), +- .rc_query = digitv_rc_query, +- }, +- +- .i2c_algo = &digitv_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Nebula Electronics uDigiTV DVB-T USB2.0)", +- { &digitv_table[0], NULL }, +- { NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct usb_driver digitv_driver = { +- .name = "dvb_usb_digitv", +- .probe = digitv_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = digitv_table, +-}; +- +-module_usb_driver(digitv_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for Nebula Electronics uDigiTV DVB-T USB2.0"); +-MODULE_VERSION("1.0-alpha"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/digitv.h b/drivers/media/dvb/dvb-usb/digitv.h +deleted file mode 100644 +index 908c09f..0000000 +--- a/drivers/media/dvb/dvb-usb/digitv.h ++++ /dev/null +@@ -1,66 +0,0 @@ +-#ifndef _DVB_USB_DIGITV_H_ +-#define _DVB_USB_DIGITV_H_ +- +-#define DVB_USB_LOG_PREFIX "digitv" +-#include "dvb-usb.h" +- +-struct digitv_state { +- int is_nxt6000; +-}; +- +-/* protocol (from usblogging and the SDK: +- * +- * Always 7 bytes bulk message(s) for controlling +- * +- * First byte describes the command. Reads are 2 consecutive transfer (as always). +- * +- * General structure: +- * +- * write or first message of a read: +- * VV B0 B1 B2 B3 +- * +- * second message of a read +- * VV R0 R1 R2 R3 +- * +- * whereas 0 < len <= 4 +- * +- * I2C address is stored somewhere inside the device. +- * +- * 0x01 read from EEPROM +- * VV = offset; B* = 0; R* = value(s) +- * +- * 0x02 read register of the COFDM +- * VV = register; B* = 0; R* = value(s) +- * +- * 0x05 write register of the COFDM +- * VV = register; B* = value(s); +- * +- * 0x06 write to the tuner (only for NXT6000) +- * VV = 0; B* = PLL data; len = 4; +- * +- * 0x03 read remote control +- * VV = 0; B* = 0; len = 4; R* = key +- * +- * 0x07 write to the remote (don't know why one should this, resetting ?) +- * VV = 0; B* = key; len = 4; +- * +- * 0x08 write remote type +- * VV = 0; B[0] = 0x01, len = 4 +- * +- * 0x09 write device init +- * TODO +- */ +-#define USB_READ_EEPROM 1 +- +-#define USB_READ_COFDM 2 +-#define USB_WRITE_COFDM 5 +- +-#define USB_WRITE_TUNER 6 +- +-#define USB_READ_REMOTE 3 +-#define USB_WRITE_REMOTE 7 +-#define USB_WRITE_REMOTE_TYPE 8 +- +-#define USB_DEV_INIT 9 +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dtt200u-fe.c b/drivers/media/dvb/dvb-usb/dtt200u-fe.c +deleted file mode 100644 +index 3d81daa..0000000 +--- a/drivers/media/dvb/dvb-usb/dtt200u-fe.c ++++ /dev/null +@@ -1,210 +0,0 @@ +-/* Frontend part of the Linux driver for the WideView/ Yakumo/ Hama/ +- * Typhoon/ Yuan DVB-T USB2.0 receiver. +- * +- * Copyright (C) 2005 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dtt200u.h" +- +-struct dtt200u_fe_state { +- struct dvb_usb_device *d; +- +- fe_status_t stat; +- +- struct dtv_frontend_properties fep; +- struct dvb_frontend frontend; +-}; +- +-static int dtt200u_fe_read_status(struct dvb_frontend* fe, fe_status_t *stat) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 st = GET_TUNE_STATUS, b[3]; +- +- dvb_usb_generic_rw(state->d,&st,1,b,3,0); +- +- switch (b[0]) { +- case 0x01: +- *stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- break; +- case 0x00: /* pending */ +- *stat = FE_TIMEDOUT; /* during set_frontend */ +- break; +- default: +- case 0x02: /* failed */ +- *stat = 0; +- break; +- } +- return 0; +-} +- +-static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 bw = GET_VIT_ERR_CNT,b[3]; +- dvb_usb_generic_rw(state->d,&bw,1,b,3,0); +- *ber = (b[0] << 16) | (b[1] << 8) | b[2]; +- return 0; +-} +- +-static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 bw = GET_RS_UNCOR_BLK_CNT,b[2]; +- +- dvb_usb_generic_rw(state->d,&bw,1,b,2,0); +- *unc = (b[0] << 8) | b[1]; +- return 0; +-} +- +-static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 bw = GET_AGC, b; +- dvb_usb_generic_rw(state->d,&bw,1,&b,1,0); +- *strength = (b << 8) | b; +- return 0; +-} +- +-static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 bw = GET_SNR,br; +- dvb_usb_generic_rw(state->d,&bw,1,&br,1,0); +- *snr = ~((br << 8) | br); +- return 0; +-} +- +-static int dtt200u_fe_init(struct dvb_frontend* fe) +-{ +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- u8 b = SET_INIT; +- return dvb_usb_generic_write(state->d,&b,1); +-} +- +-static int dtt200u_fe_sleep(struct dvb_frontend* fe) +-{ +- return dtt200u_fe_init(fe); +-} +- +-static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1500; +- tune->step_size = 0; +- tune->max_drift = 0; +- return 0; +-} +- +-static int dtt200u_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- int i; +- fe_status_t st; +- u16 freq = fep->frequency / 250000; +- u8 bwbuf[2] = { SET_BANDWIDTH, 0 },freqbuf[3] = { SET_RF_FREQ, 0, 0 }; +- +- switch (fep->bandwidth_hz) { +- case 8000000: +- bwbuf[1] = 8; +- break; +- case 7000000: +- bwbuf[1] = 7; +- break; +- case 6000000: +- bwbuf[1] = 6; +- break; +- default: +- return -EINVAL; +- } +- +- dvb_usb_generic_write(state->d,bwbuf,2); +- +- freqbuf[1] = freq & 0xff; +- freqbuf[2] = (freq >> 8) & 0xff; +- dvb_usb_generic_write(state->d,freqbuf,3); +- +- for (i = 0; i < 30; i++) { +- msleep(20); +- dtt200u_fe_read_status(fe, &st); +- if (st & FE_TIMEDOUT) +- continue; +- } +- +- return 0; +-} +- +-static int dtt200u_fe_get_frontend(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dtt200u_fe_state *state = fe->demodulator_priv; +- memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties)); +- return 0; +-} +- +-static void dtt200u_fe_release(struct dvb_frontend* fe) +-{ +- struct dtt200u_fe_state *state = (struct dtt200u_fe_state*) fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops dtt200u_fe_ops; +- +-struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d) +-{ +- struct dtt200u_fe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- deb_info("attaching frontend dtt200u\n"); +- +- state->d = d; +- +- memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +-error: +- return NULL; +-} +- +-static struct dvb_frontend_ops dtt200u_fe_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "WideView USB DVB-T", +- .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 = dtt200u_fe_release, +- +- .init = dtt200u_fe_init, +- .sleep = dtt200u_fe_sleep, +- +- .set_frontend = dtt200u_fe_set_frontend, +- .get_frontend = dtt200u_fe_get_frontend, +- .get_tune_settings = dtt200u_fe_get_tune_settings, +- +- .read_status = dtt200u_fe_read_status, +- .read_ber = dtt200u_fe_read_ber, +- .read_signal_strength = dtt200u_fe_read_signal_strength, +- .read_snr = dtt200u_fe_read_snr, +- .read_ucblocks = dtt200u_fe_read_unc_blocks, +-}; +diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c +deleted file mode 100644 +index 66f205c..0000000 +--- a/drivers/media/dvb/dvb-usb/dtt200u.c ++++ /dev/null +@@ -1,368 +0,0 @@ +-/* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/ +- * Typhoon/ Yuan/ Miglia DVB-T USB2.0 receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * Thanks to Steve Chang from WideView for providing support for the WT-220U. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dtt200u.h" +- +-/* debug */ +-int dvb_usb_dtt200u_debug; +-module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 b = SET_INIT; +- +- if (onoff) +- dvb_usb_generic_write(d,&b,2); +- +- return 0; +-} +- +-static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- u8 b_streaming[2] = { SET_STREAMING, onoff }; +- u8 b_rst_pid = RESET_PID_FILTER; +- +- dvb_usb_generic_write(adap->dev, b_streaming, 2); +- +- if (onoff == 0) +- dvb_usb_generic_write(adap->dev, &b_rst_pid, 1); +- return 0; +-} +- +-static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +-{ +- u8 b_pid[4]; +- pid = onoff ? pid : 0; +- +- b_pid[0] = SET_PID_FILTER; +- b_pid[1] = index; +- b_pid[2] = pid & 0xff; +- b_pid[3] = (pid >> 8) & 0x1f; +- +- return dvb_usb_generic_write(adap->dev, b_pid, 4); +-} +- +-/* remote control */ +-/* key list for the tiny remote control (Yakumo, don't know about the others) */ +-static struct rc_map_table rc_map_dtt200u_table[] = { +- { 0x8001, KEY_MUTE }, +- { 0x8002, KEY_CHANNELDOWN }, +- { 0x8003, KEY_VOLUMEDOWN }, +- { 0x8004, KEY_1 }, +- { 0x8005, KEY_2 }, +- { 0x8006, KEY_3 }, +- { 0x8007, KEY_4 }, +- { 0x8008, KEY_5 }, +- { 0x8009, KEY_6 }, +- { 0x800a, KEY_7 }, +- { 0x800c, KEY_ZOOM }, +- { 0x800d, KEY_0 }, +- { 0x800e, KEY_SELECT }, +- { 0x8012, KEY_POWER }, +- { 0x801a, KEY_CHANNELUP }, +- { 0x801b, KEY_8 }, +- { 0x801e, KEY_VOLUMEUP }, +- { 0x801f, KEY_9 }, +-}; +- +-static int dtt200u_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- u8 key[5],cmd = GET_RC_CODE; +- dvb_usb_generic_rw(d,&cmd,1,key,5,0); +- dvb_usb_nec_rc_key_to_event(d,key,event,state); +- if (key[0] != 0) +- deb_info("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]); +- return 0; +-} +- +-static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = dtt200u_fe_attach(adap->dev); +- return 0; +-} +- +-static struct dvb_usb_device_properties dtt200u_properties; +-static struct dvb_usb_device_properties wt220u_fc_properties; +-static struct dvb_usb_device_properties wt220u_properties; +-static struct dvb_usb_device_properties wt220u_zl0353_properties; +-static struct dvb_usb_device_properties wt220u_miglia_properties; +- +-static int dtt200u_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &dtt200u_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &wt220u_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &wt220u_fc_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- +- return -ENODEV; +-} +- +-static struct usb_device_id dtt200u_usb_table [] = { +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_COLD) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) }, +- { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) }, +- { USB_DEVICE(USB_VID_MIGLIA, USB_PID_WT220U_ZAP250_COLD) }, +- { 0 }, +-}; +-MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); +- +-static struct dvb_usb_device_properties dtt200u_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-dtt200u-01.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, +- .pid_filter_count = 15, +- +- .streaming_ctrl = dtt200u_streaming_ctrl, +- .pid_filter = dtt200u_pid_filter, +- .frontend_attach = dtt200u_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = dtt200u_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = 300, +- .rc_map_table = rc_map_dtt200u_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), +- .rc_query = dtt200u_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "WideView/Yuan/Yakumo/Hama/Typhoon DVB-T USB2.0 (WT-200U)", +- .cold_ids = { &dtt200u_usb_table[0], NULL }, +- .warm_ids = { &dtt200u_usb_table[1], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct dvb_usb_device_properties wt220u_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-wt220u-02.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, +- .pid_filter_count = 15, +- +- .streaming_ctrl = dtt200u_streaming_ctrl, +- .pid_filter = dtt200u_pid_filter, +- .frontend_attach = dtt200u_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = dtt200u_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = 300, +- .rc_map_table = rc_map_dtt200u_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), +- .rc_query = dtt200u_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", +- .cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL }, +- .warm_ids = { &dtt200u_usb_table[3], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct dvb_usb_device_properties wt220u_fc_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-wt220u-fc03.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, +- .pid_filter_count = 15, +- +- .streaming_ctrl = dtt200u_streaming_ctrl, +- .pid_filter = dtt200u_pid_filter, +- .frontend_attach = dtt200u_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = dtt200u_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = 300, +- .rc_map_table = rc_map_dtt200u_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), +- .rc_query = dtt200u_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)", +- .cold_ids = { &dtt200u_usb_table[6], NULL }, +- .warm_ids = { &dtt200u_usb_table[7], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct dvb_usb_device_properties wt220u_zl0353_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-wt220u-zl0353-01.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, +- .pid_filter_count = 15, +- +- .streaming_ctrl = dtt200u_streaming_ctrl, +- .pid_filter = dtt200u_pid_filter, +- .frontend_attach = dtt200u_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = dtt200u_power_ctrl, +- +- .rc.legacy = { +- .rc_interval = 300, +- .rc_map_table = rc_map_dtt200u_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dtt200u_table), +- .rc_query = dtt200u_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "WideView WT-220U PenType Receiver (based on ZL353)", +- .cold_ids = { &dtt200u_usb_table[4], NULL }, +- .warm_ids = { &dtt200u_usb_table[5], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct dvb_usb_device_properties wt220u_miglia_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-wt220u-miglia-01.fw", +- +- .num_adapters = 1, +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "WideView WT-220U PenType Receiver (Miglia)", +- .cold_ids = { &dtt200u_usb_table[9], NULL }, +- /* This device turns into WT220U_ZL0353_WARM when fw +- has been uploaded */ +- .warm_ids = { NULL }, +- }, +- { NULL }, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver dtt200u_usb_driver = { +- .name = "dvb_usb_dtt200u", +- .probe = dtt200u_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dtt200u_usb_table, +-}; +- +-module_usb_driver(dtt200u_usb_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dtt200u.h b/drivers/media/dvb/dvb-usb/dtt200u.h +deleted file mode 100644 +index 005b0a7..0000000 +--- a/drivers/media/dvb/dvb-usb/dtt200u.h ++++ /dev/null +@@ -1,57 +0,0 @@ +-/* Common header file of Linux driver for the WideView/ Yakumo/ Hama/ +- * Typhoon/ Yuan DVB-T USB2.0 receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_DTT200U_H_ +-#define _DVB_USB_DTT200U_H_ +- +-#define DVB_USB_LOG_PREFIX "dtt200u" +- +-#include "dvb-usb.h" +- +-extern int dvb_usb_dtt200u_debug; +-#define deb_info(args...) dprintk(dvb_usb_dtt200u_debug,0x01,args) +-#define deb_xfer(args...) dprintk(dvb_usb_dtt200u_debug,0x02,args) +- +-/* guessed protocol description (reverse engineered): +- * read +- * 00 - USB type 0x02 for usb2.0, 0x01 for usb1.1 +- * 88 - locking 2 bytes (0x80 0x40 == no signal, 0x89 0x20 == nice signal) +- */ +- +-#define GET_SPEED 0x00 +-#define GET_TUNE_STATUS 0x81 +-#define GET_RC_CODE 0x84 +-#define GET_CONFIGURATION 0x88 +-#define GET_AGC 0x89 +-#define GET_SNR 0x8a +-#define GET_VIT_ERR_CNT 0x8c +-#define GET_RS_ERR_CNT 0x8d +-#define GET_RS_UNCOR_BLK_CNT 0x8e +- +-/* write +- * 01 - init +- * 02 - frequency (divided by 250000) +- * 03 - bandwidth +- * 04 - pid table (index pid(7:0) pid(12:8)) +- * 05 - reset the pid table +- * 08 - transfer switch +- */ +- +-#define SET_INIT 0x01 +-#define SET_RF_FREQ 0x02 +-#define SET_BANDWIDTH 0x03 +-#define SET_PID_FILTER 0x04 +-#define RESET_PID_FILTER 0x05 +-#define SET_STREAMING 0x08 +- +-extern struct dvb_frontend * dtt200u_fe_attach(struct dvb_usb_device *d); +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dtv5100.c b/drivers/media/dvb/dvb-usb/dtv5100.c +deleted file mode 100644 +index 3d11df4..0000000 +--- a/drivers/media/dvb/dvb-usb/dtv5100.c ++++ /dev/null +@@ -1,224 +0,0 @@ +-/* +- * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T +- * +- * Copyright (C) 2008 Antoine Jacquet +- * http://royale.zerezo.com/dtv5100/ +- * +- * Inspired by gl861.c and au6610.c drivers +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +- +-#include "dtv5100.h" +-#include "zl10353.h" +-#include "qt1010.h" +- +-/* debug */ +-static int dvb_usb_dtv5100_debug; +-module_param_named(debug, dvb_usb_dtv5100_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u8 request; +- u8 type; +- u16 value; +- u16 index; +- +- switch (wlen) { +- case 1: +- /* write { reg }, read { value } */ +- request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_READ : +- DTV5100_TUNER_READ); +- type = USB_TYPE_VENDOR | USB_DIR_IN; +- value = 0; +- break; +- case 2: +- /* write { reg, value } */ +- request = (addr == DTV5100_DEMOD_ADDR ? DTV5100_DEMOD_WRITE : +- DTV5100_TUNER_WRITE); +- type = USB_TYPE_VENDOR | USB_DIR_OUT; +- value = wbuf[1]; +- break; +- default: +- warn("wlen = %x, aborting.", wlen); +- return -EINVAL; +- } +- index = (addr << 8) + wbuf[0]; +- +- msleep(1); /* avoid I2C errors */ +- return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), request, +- type, value, index, rbuf, rlen, +- DTV5100_USB_TIMEOUT); +-} +- +-/* I2C */ +-static int dtv5100_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { +- if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, msg[i+1].buf, +- msg[i+1].len) < 0) +- break; +- i++; +- } else if (dtv5100_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, NULL, 0) < 0) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 dtv5100_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dtv5100_i2c_algo = { +- .master_xfer = dtv5100_i2c_xfer, +- .functionality = dtv5100_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static struct zl10353_config dtv5100_zl10353_config = { +- .demod_address = DTV5100_DEMOD_ADDR, +- .no_tuner = 1, +- .parallel_ts = 1, +-}; +- +-static int dtv5100_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &dtv5100_zl10353_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- /* disable i2c gate, or it won't work... is this safe? */ +- adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; +- +- return 0; +-} +- +-static struct qt1010_config dtv5100_qt1010_config = { +- .i2c_address = DTV5100_TUNER_ADDR +-}; +- +-static int dtv5100_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- return dvb_attach(qt1010_attach, +- adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &dtv5100_qt1010_config) == NULL ? -ENODEV : 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties dtv5100_properties; +- +-static int dtv5100_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int i, ret; +- struct usb_device *udev = interface_to_usbdev(intf); +- +- /* initialize non qt1010/zl10353 part? */ +- for (i = 0; dtv5100_init[i].request; i++) { +- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +- dtv5100_init[i].request, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- dtv5100_init[i].value, +- dtv5100_init[i].index, NULL, 0, +- DTV5100_USB_TIMEOUT); +- if (ret) +- return ret; +- } +- +- ret = dvb_usb_device_init(intf, &dtv5100_properties, +- THIS_MODULE, NULL, adapter_nr); +- if (ret) +- return ret; +- +- return 0; +-} +- +-static struct usb_device_id dtv5100_table[] = { +- { USB_DEVICE(0x06be, 0xa232) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, dtv5100_table); +- +-static struct dvb_usb_device_properties dtv5100_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = dtv5100_frontend_attach, +- .tuner_attach = dtv5100_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } }, +- +- .i2c_algo = &dtv5100_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { +- .name = "AME DTV-5100 USB2.0 DVB-T", +- .cold_ids = { NULL }, +- .warm_ids = { &dtv5100_table[0], NULL }, +- }, +- } +-}; +- +-static struct usb_driver dtv5100_driver = { +- .name = "dvb_usb_dtv5100", +- .probe = dtv5100_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dtv5100_table, +-}; +- +-module_usb_driver(dtv5100_driver); +- +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dtv5100.h b/drivers/media/dvb/dvb-usb/dtv5100.h +deleted file mode 100644 +index 93e96e0..0000000 +--- a/drivers/media/dvb/dvb-usb/dtv5100.h ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- * DVB USB Linux driver for AME DTV-5100 USB2.0 DVB-T +- * +- * Copyright (C) 2008 Antoine Jacquet +- * http://royale.zerezo.com/dtv5100/ +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +- +-#ifndef _DVB_USB_DTV5100_H_ +-#define _DVB_USB_DTV5100_H_ +- +-#define DVB_USB_LOG_PREFIX "dtv5100" +-#include "dvb-usb.h" +- +-#define DTV5100_USB_TIMEOUT 500 +- +-#define DTV5100_DEMOD_ADDR 0x00 +-#define DTV5100_DEMOD_WRITE 0xc0 +-#define DTV5100_DEMOD_READ 0xc1 +- +-#define DTV5100_TUNER_ADDR 0xc4 +-#define DTV5100_TUNER_WRITE 0xc7 +-#define DTV5100_TUNER_READ 0xc8 +- +-#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +-#define DRIVER_DESC "AME DTV-5100 USB2.0 DVB-T" +- +-static struct { +- u8 request; +- u8 value; +- u16 index; +-} dtv5100_init[] = { +- { 0x000000c5, 0x00000000, 0x00000001 }, +- { 0x000000c5, 0x00000001, 0x00000001 }, +- { } /* Terminating entry */ +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-common.h b/drivers/media/dvb/dvb-usb/dvb-usb-common.h +deleted file mode 100644 +index 6b7b2a8..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-common.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* dvb-usb-common.h is part of the DVB USB library. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * see dvb-usb-init.c for copyright information. +- * +- * a header file containing prototypes and types for internal use of the dvb-usb-lib +- */ +-#ifndef _DVB_USB_COMMON_H_ +-#define _DVB_USB_COMMON_H_ +- +-#define DVB_USB_LOG_PREFIX "dvb-usb" +-#include "dvb-usb.h" +- +-extern int dvb_usb_debug; +-extern int dvb_usb_disable_rc_polling; +- +-#define deb_info(args...) dprintk(dvb_usb_debug,0x001,args) +-#define deb_xfer(args...) dprintk(dvb_usb_debug,0x002,args) +-#define deb_pll(args...) dprintk(dvb_usb_debug,0x004,args) +-#define deb_ts(args...) dprintk(dvb_usb_debug,0x008,args) +-#define deb_err(args...) dprintk(dvb_usb_debug,0x010,args) +-#define deb_rc(args...) dprintk(dvb_usb_debug,0x020,args) +-#define deb_fw(args...) dprintk(dvb_usb_debug,0x040,args) +-#define deb_mem(args...) dprintk(dvb_usb_debug,0x080,args) +-#define deb_uxfer(args...) dprintk(dvb_usb_debug,0x100,args) +- +-/* commonly used methods */ +-extern int dvb_usb_download_firmware(struct usb_device *, struct dvb_usb_device_properties *); +- +-extern int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff); +- +-extern int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props); +-extern int usb_urb_exit(struct usb_data_stream *stream); +-extern int usb_urb_submit(struct usb_data_stream *stream); +-extern int usb_urb_kill(struct usb_data_stream *stream); +- +-extern int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap); +-extern int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap); +- +-extern int dvb_usb_i2c_init(struct dvb_usb_device *); +-extern int dvb_usb_i2c_exit(struct dvb_usb_device *); +- +-extern int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, +- short *adapter_nums); +-extern int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap); +-extern int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap); +-extern int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap); +- +-extern int dvb_usb_remote_init(struct dvb_usb_device *); +-extern int dvb_usb_remote_exit(struct dvb_usb_device *); +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +deleted file mode 100644 +index ddf282f..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c ++++ /dev/null +@@ -1,289 +0,0 @@ +-/* dvb-usb-dvb.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 initializing and handling the +- * linux-dvb API. +- */ +-#include "dvb-usb-common.h" +- +-/* does the complete input transfer handling */ +-static int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +-{ +- struct dvb_usb_adapter *adap = dvbdmxfeed->demux->priv; +- int newfeedcount, ret; +- +- if (adap == NULL) +- return -ENODEV; +- +- if ((adap->active_fe < 0) || +- (adap->active_fe >= adap->num_frontends_initialized)) { +- return -EINVAL; +- } +- +- newfeedcount = adap->feedcount + (onoff ? 1 : -1); +- +- /* stop feed before setting a new pid if there will be no pid anymore */ +- if (newfeedcount == 0) { +- deb_ts("stop feeding\n"); +- usb_urb_kill(&adap->fe_adap[adap->active_fe].stream); +- +- if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { +- ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 0); +- if (ret < 0) { +- err("error while stopping stream."); +- return ret; +- } +- } +- } +- +- adap->feedcount = newfeedcount; +- +- /* activate the pid on the device specific pid_filter */ +- deb_ts("setting pid (%s): %5d %04x at index %d '%s'\n", +- adap->fe_adap[adap->active_fe].pid_filtering ? +- "yes" : "no", dvbdmxfeed->pid, dvbdmxfeed->pid, +- dvbdmxfeed->index, onoff ? "on" : "off"); +- if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && +- adap->fe_adap[adap->active_fe].pid_filtering && +- adap->props.fe[adap->active_fe].pid_filter != NULL) +- adap->props.fe[adap->active_fe].pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, onoff); +- +- /* start the feed if this was the first feed and there is still a feed +- * for reception. +- */ +- if (adap->feedcount == onoff && adap->feedcount > 0) { +- deb_ts("submitting all URBs\n"); +- usb_urb_submit(&adap->fe_adap[adap->active_fe].stream); +- +- deb_ts("controlling pid parser\n"); +- if (adap->props.fe[adap->active_fe].caps & DVB_USB_ADAP_HAS_PID_FILTER && +- adap->props.fe[adap->active_fe].caps & +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF && +- adap->props.fe[adap->active_fe].pid_filter_ctrl != NULL) { +- ret = adap->props.fe[adap->active_fe].pid_filter_ctrl(adap, +- adap->fe_adap[adap->active_fe].pid_filtering); +- if (ret < 0) { +- err("could not handle pid_parser"); +- return ret; +- } +- } +- deb_ts("start feeding\n"); +- if (adap->props.fe[adap->active_fe].streaming_ctrl != NULL) { +- ret = adap->props.fe[adap->active_fe].streaming_ctrl(adap, 1); +- if (ret < 0) { +- err("error while enabling fifo."); +- return ret; +- } +- } +- +- } +- return 0; +-} +- +-static int dvb_usb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- deb_ts("start pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid,dvbdmxfeed->type); +- return dvb_usb_ctrl_feed(dvbdmxfeed,1); +-} +- +-static int dvb_usb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- deb_ts("stop pid: 0x%04x, feedtype: %d\n", dvbdmxfeed->pid, dvbdmxfeed->type); +- return dvb_usb_ctrl_feed(dvbdmxfeed,0); +-} +- +-int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) +-{ +- int i; +- int ret = dvb_register_adapter(&adap->dvb_adap, adap->dev->desc->name, +- adap->dev->owner, &adap->dev->udev->dev, +- adapter_nums); +- +- if (ret < 0) { +- deb_info("dvb_register_adapter failed: error %d", ret); +- goto err; +- } +- adap->dvb_adap.priv = adap; +- adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override; +- +- if (adap->dev->props.read_mac_address) { +- if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0) +- info("MAC address: %pM",adap->dvb_adap.proposed_mac); +- else +- err("MAC address reading failed."); +- } +- +- +- adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; +- adap->demux.priv = adap; +- +- adap->demux.filternum = 0; +- for (i = 0; i < adap->props.num_frontends; i++) { +- if (adap->demux.filternum < adap->fe_adap[i].max_feed_count) +- adap->demux.filternum = adap->fe_adap[i].max_feed_count; +- } +- adap->demux.feednum = adap->demux.filternum; +- adap->demux.start_feed = dvb_usb_start_feed; +- adap->demux.stop_feed = dvb_usb_stop_feed; +- adap->demux.write_to_decoder = NULL; +- if ((ret = dvb_dmx_init(&adap->demux)) < 0) { +- err("dvb_dmx_init failed: error %d",ret); +- goto err_dmx; +- } +- +- adap->dmxdev.filternum = adap->demux.filternum; +- adap->dmxdev.demux = &adap->demux.dmx; +- adap->dmxdev.capabilities = 0; +- if ((ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap)) < 0) { +- err("dvb_dmxdev_init failed: error %d",ret); +- goto err_dmx_dev; +- } +- +- if ((ret = dvb_net_init(&adap->dvb_adap, &adap->dvb_net, +- &adap->demux.dmx)) < 0) { +- err("dvb_net_init failed: error %d",ret); +- goto err_net_init; +- } +- +- adap->state |= DVB_USB_ADAP_STATE_DVB; +- return 0; +- +-err_net_init: +- dvb_dmxdev_release(&adap->dmxdev); +-err_dmx_dev: +- dvb_dmx_release(&adap->demux); +-err_dmx: +- dvb_unregister_adapter(&adap->dvb_adap); +-err: +- return ret; +-} +- +-int dvb_usb_adapter_dvb_exit(struct dvb_usb_adapter *adap) +-{ +- if (adap->state & DVB_USB_ADAP_STATE_DVB) { +- deb_info("unregistering DVB part\n"); +- dvb_net_release(&adap->dvb_net); +- adap->demux.dmx.close(&adap->demux.dmx); +- dvb_dmxdev_release(&adap->dmxdev); +- dvb_dmx_release(&adap->demux); +- dvb_unregister_adapter(&adap->dvb_adap); +- adap->state &= ~DVB_USB_ADAP_STATE_DVB; +- } +- return 0; +-} +- +-static int dvb_usb_set_active_fe(struct dvb_frontend *fe, int onoff) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- int ret = (adap->props.frontend_ctrl) ? +- adap->props.frontend_ctrl(fe, onoff) : 0; +- +- if (ret < 0) { +- err("frontend_ctrl request failed"); +- return ret; +- } +- if (onoff) +- adap->active_fe = fe->id; +- +- return 0; +-} +- +-static int dvb_usb_fe_wakeup(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- dvb_usb_device_power_ctrl(adap->dev, 1); +- +- dvb_usb_set_active_fe(fe, 1); +- +- if (adap->fe_adap[fe->id].fe_init) +- adap->fe_adap[fe->id].fe_init(fe); +- +- return 0; +-} +- +-static int dvb_usb_fe_sleep(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- if (adap->fe_adap[fe->id].fe_sleep) +- adap->fe_adap[fe->id].fe_sleep(fe); +- +- dvb_usb_set_active_fe(fe, 0); +- +- return dvb_usb_device_power_ctrl(adap->dev, 0); +-} +- +-int dvb_usb_adapter_frontend_init(struct dvb_usb_adapter *adap) +-{ +- int ret, i; +- +- /* register all given adapter frontends */ +- for (i = 0; i < adap->props.num_frontends; i++) { +- +- if (adap->props.fe[i].frontend_attach == NULL) { +- err("strange: '%s' #%d,%d " +- "doesn't want to attach a frontend.", +- adap->dev->desc->name, adap->id, i); +- +- return 0; +- } +- +- ret = adap->props.fe[i].frontend_attach(adap); +- if (ret || adap->fe_adap[i].fe == NULL) { +- /* only print error when there is no FE at all */ +- if (i == 0) +- err("no frontend was attached by '%s'", +- adap->dev->desc->name); +- +- return 0; +- } +- +- adap->fe_adap[i].fe->id = i; +- +- /* re-assign sleep and wakeup functions */ +- adap->fe_adap[i].fe_init = adap->fe_adap[i].fe->ops.init; +- adap->fe_adap[i].fe->ops.init = dvb_usb_fe_wakeup; +- adap->fe_adap[i].fe_sleep = adap->fe_adap[i].fe->ops.sleep; +- adap->fe_adap[i].fe->ops.sleep = dvb_usb_fe_sleep; +- +- if (dvb_register_frontend(&adap->dvb_adap, adap->fe_adap[i].fe)) { +- err("Frontend %d registration failed.", i); +- dvb_frontend_detach(adap->fe_adap[i].fe); +- adap->fe_adap[i].fe = NULL; +- /* In error case, do not try register more FEs, +- * still leaving already registered FEs alive. */ +- if (i == 0) +- return -ENODEV; +- else +- return 0; +- } +- +- /* only attach the tuner if the demod is there */ +- if (adap->props.fe[i].tuner_attach != NULL) +- adap->props.fe[i].tuner_attach(adap); +- +- adap->num_frontends_initialized++; +- } +- +- return 0; +-} +- +-int dvb_usb_adapter_frontend_exit(struct dvb_usb_adapter *adap) +-{ +- int i = adap->num_frontends_initialized - 1; +- +- /* unregister all given adapter frontends */ +- for (; i >= 0; i--) { +- if (adap->fe_adap[i].fe != NULL) { +- dvb_unregister_frontend(adap->fe_adap[i].fe); +- dvb_frontend_detach(adap->fe_adap[i].fe); +- } +- } +- adap->num_frontends_initialized = 0; +- +- return 0; +-} +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c b/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c +deleted file mode 100644 +index 733a7ff..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c ++++ /dev/null +@@ -1,146 +0,0 @@ +-/* dvb-usb-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. +- * +- * FIXME: This part does actually not belong to dvb-usb, but to the usb-subsystem. +- */ +-#include "dvb-usb-common.h" +- +-#include +- +-struct usb_cypress_controller { +- int id; +- const char *name; /* name of the usb controller */ +- u16 cpu_cs_register; /* needs to be restarted, when the firmware has been downloaded. */ +-}; +- +-static struct usb_cypress_controller cypress[] = { +- { .id = DEVICE_SPECIFIC, .name = "Device specific", .cpu_cs_register = 0 }, +- { .id = CYPRESS_AN2135, .name = "Cypress AN2135", .cpu_cs_register = 0x7f92 }, +- { .id = CYPRESS_AN2235, .name = "Cypress AN2235", .cpu_cs_register = 0x7f92 }, +- { .id = CYPRESS_FX2, .name = "Cypress FX2", .cpu_cs_register = 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); +-} +- +-int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) +-{ +- struct hexline hx; +- u8 reset; +- int ret,pos=0; +- +- /* stop the CPU */ +- reset = 1; +- if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) +- err("could not stop the USB controller CPU."); +- +- while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) { +- deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk); +- ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len); +- +- if (ret != hx.len) { +- err("error while transferring firmware " +- "(transferred size: %d, block size: %d)", +- ret,hx.len); +- ret = -EINVAL; +- break; +- } +- } +- if (ret < 0) { +- err("firmware download failed at %d with %d",pos,ret); +- return ret; +- } +- +- if (ret == 0) { +- /* restart the CPU */ +- reset = 0; +- if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { +- err("could not restart the USB controller CPU."); +- ret = -EINVAL; +- } +- } else +- ret = -EIO; +- +- return ret; +-} +-EXPORT_SYMBOL(usb_cypress_load_firmware); +- +-int dvb_usb_download_firmware(struct usb_device *udev, struct dvb_usb_device_properties *props) +-{ +- int ret; +- const struct firmware *fw = NULL; +- +- if ((ret = request_firmware(&fw, props->firmware, &udev->dev)) != 0) { +- err("did not find the firmware file. (%s) " +- "Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)", +- props->firmware,ret); +- return ret; +- } +- +- info("downloading firmware from file '%s'",props->firmware); +- +- switch (props->usb_ctrl) { +- case CYPRESS_AN2135: +- case CYPRESS_AN2235: +- case CYPRESS_FX2: +- ret = usb_cypress_load_firmware(udev, fw, props->usb_ctrl); +- break; +- case DEVICE_SPECIFIC: +- if (props->download_firmware) +- ret = props->download_firmware(udev,fw); +- else { +- err("BUG: driver didn't specified a download_firmware-callback, although it claims to have a DEVICE_SPECIFIC one."); +- ret = -EINVAL; +- } +- break; +- default: +- ret = -EINVAL; +- break; +- } +- +- release_firmware(fw); +- return ret; +-} +- +-int dvb_usb_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); +-/* hx->len -= 2; +- data_offs += 2; */ +- } +- memcpy(hx->data,&b[data_offs],hx->len); +- hx->chk = b[hx->len + data_offs]; +- +- *pos += hx->len + 5; +- +- return *pos; +-} +-EXPORT_SYMBOL(dvb_usb_get_hexline); +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +deleted file mode 100644 +index 88e4a62..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c ++++ /dev/null +@@ -1,43 +0,0 @@ +-/* dvb-usb-i2c.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 (de-)initializing an I2C adapter. +- */ +-#include "dvb-usb-common.h" +- +-int dvb_usb_i2c_init(struct dvb_usb_device *d) +-{ +- int ret = 0; +- +- if (!(d->props.caps & DVB_USB_IS_AN_I2C_ADAPTER)) +- return 0; +- +- if (d->props.i2c_algo == NULL) { +- err("no i2c algorithm specified"); +- return -EINVAL; +- } +- +- strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name)); +- d->i2c_adap.algo = d->props.i2c_algo; +- d->i2c_adap.algo_data = NULL; +- d->i2c_adap.dev.parent = &d->udev->dev; +- +- i2c_set_adapdata(&d->i2c_adap, d); +- +- if ((ret = i2c_add_adapter(&d->i2c_adap)) < 0) +- err("could not add i2c adapter"); +- +- d->state |= DVB_USB_STATE_I2C; +- +- return ret; +-} +- +-int dvb_usb_i2c_exit(struct dvb_usb_device *d) +-{ +- if (d->state & DVB_USB_STATE_I2C) +- i2c_del_adapter(&d->i2c_adap); +- d->state &= ~DVB_USB_STATE_I2C; +- return 0; +-} +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +deleted file mode 100644 +index d390dda..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h ++++ /dev/null +@@ -1,338 +0,0 @@ +-/* dvb-usb-ids.h is part of the DVB USB library. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) see +- * dvb-usb-init.c for copyright information. +- * +- * a header file containing define's for the USB device supported by the +- * various drivers. +- */ +-#ifndef _DVB_USB_IDS_H_ +-#define _DVB_USB_IDS_H_ +- +-/* Vendor IDs */ +-#define USB_VID_ADSTECH 0x06e1 +-#define USB_VID_AFATECH 0x15a4 +-#define USB_VID_ALCOR_MICRO 0x058f +-#define USB_VID_ALINK 0x05e3 +-#define USB_VID_AMT 0x1c73 +-#define USB_VID_ANCHOR 0x0547 +-#define USB_VID_ANSONIC 0x10b9 +-#define USB_VID_ANUBIS_ELECTRONIC 0x10fd +-#define USB_VID_ASUS 0x0b05 +-#define USB_VID_AVERMEDIA 0x07ca +-#define USB_VID_COMPRO 0x185b +-#define USB_VID_COMPRO_UNK 0x145f +-#define USB_VID_CONEXANT 0x0572 +-#define USB_VID_CYPRESS 0x04b4 +-#define USB_VID_DIBCOM 0x10b8 +-#define USB_VID_DPOSH 0x1498 +-#define USB_VID_DVICO 0x0fe9 +-#define USB_VID_E3C 0x18b4 +-#define USB_VID_ELGATO 0x0fd9 +-#define USB_VID_EMPIA 0xeb1a +-#define USB_VID_GENPIX 0x09c0 +-#define USB_VID_GRANDTEC 0x5032 +-#define USB_VID_GTEK 0x1f4d +-#define USB_VID_HANFTEK 0x15f4 +-#define USB_VID_HAUPPAUGE 0x2040 +-#define USB_VID_HYPER_PALTEK 0x1025 +-#define USB_VID_INTEL 0x8086 +-#define USB_VID_ITETECH 0x048d +-#define USB_VID_KWORLD 0xeb2a +-#define USB_VID_KWORLD_2 0x1b80 +-#define USB_VID_KYE 0x0458 +-#define USB_VID_LEADTEK 0x0413 +-#define USB_VID_LITEON 0x04ca +-#define USB_VID_MEDION 0x1660 +-#define USB_VID_MIGLIA 0x18f3 +-#define USB_VID_MSI 0x0db0 +-#define USB_VID_MSI_2 0x1462 +-#define USB_VID_OPERA1 0x695c +-#define USB_VID_PINNACLE 0x2304 +-#define USB_VID_PCTV 0x2013 +-#define USB_VID_PIXELVIEW 0x1554 +-#define USB_VID_TECHNOTREND 0x0b48 +-#define USB_VID_TERRATEC 0x0ccd +-#define USB_VID_TELESTAR 0x10b9 +-#define USB_VID_VISIONPLUS 0x13d3 +-#define USB_VID_SONY 0x1415 +-#define USB_VID_TWINHAN 0x1822 +-#define USB_VID_ULTIMA_ELECTRONIC 0x05d8 +-#define USB_VID_UNIWILL 0x1584 +-#define USB_VID_WIDEVIEW 0x14aa +-#define USB_VID_GIGABYTE 0x1044 +-#define USB_VID_YUAN 0x1164 +-#define USB_VID_XTENSIONS 0x1ae7 +-#define USB_VID_HUMAX_COEX 0x10b9 +-#define USB_VID_774 0x7a69 +-#define USB_VID_EVOLUTEPC 0x1e59 +-#define USB_VID_AZUREWAVE 0x13d3 +-#define USB_VID_TECHNISAT 0x14f7 +- +-/* Product IDs */ +-#define USB_PID_ADSTECH_USB2_COLD 0xa333 +-#define USB_PID_ADSTECH_USB2_WARM 0xa334 +-#define USB_PID_AFATECH_AF9005 0x9020 +-#define USB_PID_AFATECH_AF9015_9015 0x9015 +-#define USB_PID_AFATECH_AF9015_9016 0x9016 +-#define USB_PID_TREKSTOR_DVBT 0x901b +-#define USB_VID_ALINK_DTU 0xf170 +-#define USB_PID_ANSONIC_DVBT_USB 0x6000 +-#define USB_PID_ANYSEE 0x861f +-#define USB_PID_AZUREWAVE_AD_TU700 0x3237 +-#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 +-#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 +-#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 +-#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801 +-#define USB_PID_COMPRO_DVBU2000_COLD 0xd000 +-#define USB_PID_COMPRO_DVBU2000_WARM 0xd001 +-#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c +-#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +-#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78 +-#define USB_PID_COMPRO_VIDEOMATE_U500_PC 0x1e80 +-#define USB_PID_CONCEPTRONIC_CTVDIGRCU 0xe397 +-#define USB_PID_CONEXANT_D680_DMB 0x86d6 +-#define USB_PID_CREATIX_CTX1921 0x1921 +-#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 +-#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 +-#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 +-#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9 +-#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 +-#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 +-#define USB_PID_DIBCOM_STK7700P 0x1e14 +-#define USB_PID_DIBCOM_STK7700P_PC 0x1e78 +-#define USB_PID_DIBCOM_STK7700D 0x1ef0 +-#define USB_PID_DIBCOM_STK7700_U7000 0x7001 +-#define USB_PID_DIBCOM_STK7070P 0x1ebc +-#define USB_PID_DIBCOM_STK7070PD 0x1ebe +-#define USB_PID_DIBCOM_STK807XP 0x1f90 +-#define USB_PID_DIBCOM_STK807XPVR 0x1f98 +-#define USB_PID_DIBCOM_STK8096GP 0x1fa0 +-#define USB_PID_DIBCOM_NIM8096MD 0x1fa8 +-#define USB_PID_DIBCOM_TFE8096P 0x1f9C +-#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +-#define USB_PID_DIBCOM_STK7770P 0x1e80 +-#define USB_PID_DIBCOM_NIM7090 0x1bb2 +-#define USB_PID_DIBCOM_TFE7090PVR 0x1bb4 +-#define USB_PID_DIBCOM_TFE7090E 0x1bb7 +-#define USB_PID_DIBCOM_TFE7790E 0x1e6e +-#define USB_PID_DIBCOM_NIM9090M 0x2383 +-#define USB_PID_DIBCOM_NIM9090MD 0x2384 +-#define USB_PID_DPOSH_M9206_COLD 0x9206 +-#define USB_PID_DPOSH_M9206_WARM 0xa090 +-#define USB_PID_E3C_EC168 0x1689 +-#define USB_PID_E3C_EC168_2 0xfffa +-#define USB_PID_E3C_EC168_3 0xfffb +-#define USB_PID_E3C_EC168_4 0x1001 +-#define USB_PID_E3C_EC168_5 0x1002 +-#define USB_PID_UNIWILL_STK7700P 0x6003 +-#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 +-#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 +-#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 +-#define USB_PID_INTEL_CE9500 0x9500 +-#define USB_PID_ITETECH_IT9135 0x9135 +-#define USB_PID_ITETECH_IT9135_9005 0x9005 +-#define USB_PID_ITETECH_IT9135_9006 0x9006 +-#define USB_PID_KWORLD_399U 0xe399 +-#define USB_PID_KWORLD_399U_2 0xe400 +-#define USB_PID_KWORLD_395U 0xe396 +-#define USB_PID_KWORLD_395U_2 0xe39b +-#define USB_PID_KWORLD_395U_3 0xe395 +-#define USB_PID_KWORLD_395U_4 0xe39a +-#define USB_PID_KWORLD_MC810 0xc810 +-#define USB_PID_KWORLD_PC160_2T 0xc160 +-#define USB_PID_KWORLD_PC160_T 0xc161 +-#define USB_PID_KWORLD_UB383_T 0xe383 +-#define USB_PID_KWORLD_UB499_2T_T09 0xe409 +-#define USB_PID_KWORLD_VSTREAM_COLD 0x17de +-#define USB_PID_KWORLD_VSTREAM_WARM 0x17df +-#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 +-#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 +-#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 +-#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 +-#define USB_PID_TWINHAN_VP7041_COLD 0x3201 +-#define USB_PID_TWINHAN_VP7041_WARM 0x3202 +-#define USB_PID_TWINHAN_VP7020_COLD 0x3203 +-#define USB_PID_TWINHAN_VP7020_WARM 0x3204 +-#define USB_PID_TWINHAN_VP7045_COLD 0x3205 +-#define USB_PID_TWINHAN_VP7045_WARM 0x3206 +-#define USB_PID_TWINHAN_VP7021_COLD 0x3207 +-#define USB_PID_TWINHAN_VP7021_WARM 0x3208 +-#define USB_PID_TINYTWIN 0x3226 +-#define USB_PID_TINYTWIN_2 0xe402 +-#define USB_PID_TINYTWIN_3 0x9016 +-#define USB_PID_DNTV_TINYUSB2_COLD 0x3223 +-#define USB_PID_DNTV_TINYUSB2_WARM 0x3224 +-#define USB_PID_ULTIMA_TVBOX_COLD 0x8105 +-#define USB_PID_ULTIMA_TVBOX_WARM 0x8106 +-#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107 +-#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108 +-#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235 +-#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109 +-#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a +-#define USB_PID_ARTEC_T14_COLD 0x810b +-#define USB_PID_ARTEC_T14_WARM 0x810c +-#define USB_PID_ARTEC_T14BR 0x810f +-#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613 +-#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002 +-#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e +-#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f +-#define USB_PID_HANFTEK_UMT_010_COLD 0x0001 +-#define USB_PID_HANFTEK_UMT_010_WARM 0x0015 +-#define USB_PID_DTT200U_COLD 0x0201 +-#define USB_PID_DTT200U_WARM 0x0301 +-#define USB_PID_WT220U_ZAP250_COLD 0x0220 +-#define USB_PID_WT220U_COLD 0x0222 +-#define USB_PID_WT220U_WARM 0x0221 +-#define USB_PID_WT220U_FC_COLD 0x0225 +-#define USB_PID_WT220U_FC_WARM 0x0226 +-#define USB_PID_WT220U_ZL0353_COLD 0x022a +-#define USB_PID_WT220U_ZL0353_WARM 0x022b +-#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 +-#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 +-#define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941 +-#define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 +-#define USB_PID_HAUPPAUGE_NOVA_T_500_3 0x8400 +-#define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 +-#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 +-#define USB_PID_HAUPPAUGE_NOVA_T_STICK_3 0x7070 +-#define USB_PID_HAUPPAUGE_MYTV_T 0x7080 +-#define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580 +-#define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200 +-#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200 +-#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210 +-#define USB_PID_AVERMEDIA_EXPRESS 0xb568 +-#define USB_PID_AVERMEDIA_VOLAR 0xa807 +-#define USB_PID_AVERMEDIA_VOLAR_2 0xb808 +-#define USB_PID_AVERMEDIA_VOLAR_A868R 0xa868 +-#define USB_PID_AVERMEDIA_MCE_USB_M038 0x1228 +-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R 0x0039 +-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC 0x1039 +-#define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT 0x2039 +-#define USB_PID_AVERMEDIA_VOLAR_X 0xa815 +-#define USB_PID_AVERMEDIA_VOLAR_X_2 0x8150 +-#define USB_PID_AVERMEDIA_A309 0xa309 +-#define USB_PID_AVERMEDIA_A310 0xa310 +-#define USB_PID_AVERMEDIA_A850 0x850a +-#define USB_PID_AVERMEDIA_A850T 0x850b +-#define USB_PID_AVERMEDIA_A805 0xa805 +-#define USB_PID_AVERMEDIA_A815M 0x815a +-#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 +-#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d +-#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a +-#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2 0x0081 +-#define USB_PID_TERRATEC_CINERGY_HT_USB_XE 0x0058 +-#define USB_PID_TERRATEC_CINERGY_HT_EXPRESS 0x0060 +-#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062 +-#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078 +-#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab +-#define USB_PID_TERRATEC_T3 0x10a0 +-#define USB_PID_TERRATEC_T5 0x10a1 +-#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e +-#define USB_PID_PINNACLE_PCTV2000E 0x022c +-#define USB_PID_PINNACLE_PCTV_DVB_T_FLASH 0x0228 +-#define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T 0x0229 +-#define USB_PID_PINNACLE_PCTV71E 0x022b +-#define USB_PID_PINNACLE_PCTV72E 0x0236 +-#define USB_PID_PINNACLE_PCTV73E 0x0237 +-#define USB_PID_PINNACLE_PCTV310E 0x3211 +-#define USB_PID_PINNACLE_PCTV801E 0x023a +-#define USB_PID_PINNACLE_PCTV801E_SE 0x023b +-#define USB_PID_PINNACLE_PCTV340E 0x023d +-#define USB_PID_PINNACLE_PCTV340E_SE 0x023e +-#define USB_PID_PINNACLE_PCTV73A 0x0243 +-#define USB_PID_PINNACLE_PCTV73ESE 0x0245 +-#define USB_PID_PINNACLE_PCTV74E 0x0246 +-#define USB_PID_PINNACLE_PCTV282E 0x0248 +-#define USB_PID_PIXELVIEW_SBTVD 0x5010 +-#define USB_PID_PCTV_200E 0x020e +-#define USB_PID_PCTV_400E 0x020f +-#define USB_PID_PCTV_450E 0x0222 +-#define USB_PID_PCTV_452E 0x021f +-#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007 +-#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a +-#define USB_PID_NEBULA_DIGITV 0x0201 +-#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 +-#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 +-#define USB_PID_DVICO_BLUEBIRD_LG064F_WARM 0xd501 +-#define USB_PID_DVICO_BLUEBIRD_LGZ201_COLD 0xdb00 +-#define USB_PID_DVICO_BLUEBIRD_LGZ201_WARM 0xdb01 +-#define USB_PID_DVICO_BLUEBIRD_TH7579_COLD 0xdb10 +-#define USB_PID_DVICO_BLUEBIRD_TH7579_WARM 0xdb11 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD 0xdb50 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_4 0xdb78 +-#define USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2 0xdb98 +-#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2 0xdb70 +-#define USB_PID_DVICO_BLUEBIRD_DVB_T_NANO_2_NFW_WARM 0xdb71 +-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 +-#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 +-#define USB_PID_MEDION_MD95700 0x0932 +-#define USB_PID_MSI_MEGASKY580 0x5580 +-#define USB_PID_MSI_MEGASKY580_55801 0x5581 +-#define USB_PID_KYE_DVB_T_COLD 0x701e +-#define USB_PID_KYE_DVB_T_WARM 0x701f +-#define USB_PID_LITEON_DVB_T_COLD 0xf000 +-#define USB_PID_LITEON_DVB_T_WARM 0xf001 +-#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360 +-#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361 +-#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6 +-#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 +-#define USB_PID_WINFAST_DTV2000DS 0x6a04 +-#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 +-#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 +-#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 +-#define USB_PID_WINFAST_DTV_DONGLE_H 0x60f6 +-#define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2 0x6f01 +-#define USB_PID_WINFAST_DTV_DONGLE_GOLD 0x6029 +-#define USB_PID_GENPIX_8PSK_REV_1_COLD 0x0200 +-#define USB_PID_GENPIX_8PSK_REV_1_WARM 0x0201 +-#define USB_PID_GENPIX_8PSK_REV_2 0x0202 +-#define USB_PID_GENPIX_SKYWALKER_1 0x0203 +-#define USB_PID_GENPIX_SKYWALKER_CW3K 0x0204 +-#define USB_PID_GENPIX_SKYWALKER_2 0x0206 +-#define USB_PID_SIGMATEK_DVB_110 0x6610 +-#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 +-#define USB_PID_MSI_DIGIVOX_DUO 0x8801 +-#define USB_PID_OPERA1_COLD 0x2830 +-#define USB_PID_OPERA1_WARM 0x3829 +-#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD 0x0514 +-#define USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM 0x0513 +-#define USB_PID_GIGABYTE_U7000 0x7001 +-#define USB_PID_GIGABYTE_U8000 0x7002 +-#define USB_PID_ASUS_U3000 0x171f +-#define USB_PID_ASUS_U3000H 0x1736 +-#define USB_PID_ASUS_U3100 0x173f +-#define USB_PID_YUAN_EC372S 0x1edc +-#define USB_PID_YUAN_STK7700PH 0x1f08 +-#define USB_PID_YUAN_PD378S 0x2edc +-#define USB_PID_YUAN_MC770 0x0871 +-#define USB_PID_YUAN_STK7700D 0x1efc +-#define USB_PID_YUAN_STK7700D_2 0x1e8c +-#define USB_PID_DW2102 0x2102 +-#define USB_PID_XTENSIONS_XD_380 0x0381 +-#define USB_PID_TELESTAR_STARSTICK_2 0x8000 +-#define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 +-#define USB_PID_SONY_PLAYTV 0x0003 +-#define USB_PID_MYGICA_D689 0xd811 +-#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 +-#define USB_PID_ELGATO_EYETV_DTT 0x0021 +-#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 +-#define USB_PID_ELGATO_EYETV_SAT 0x002a +-#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 +-#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 +-#define USB_PID_FRIIO_WHITE 0x0001 +-#define USB_PID_TVWAY_PLUS 0x0002 +-#define USB_PID_SVEON_STV20 0xe39d +-#define USB_PID_SVEON_STV22 0xe401 +-#define USB_PID_SVEON_STV22_IT9137 0xe411 +-#define USB_PID_AZUREWAVE_AZ6027 0x3275 +-#define USB_PID_TERRATEC_DVBS2CI_V1 0x10a4 +-#define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac +-#define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 +-#define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 +-#define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004 +-#define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c +deleted file mode 100644 +index 169196e..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c ++++ /dev/null +@@ -1,304 +0,0 @@ +-/* +- * DVB USB library - provides a generic interface for a DVB USB device driver. +- * +- * dvb-usb-init.c +- * +- * Copyright (C) 2004-6 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dvb-usb-common.h" +- +-/* debug */ +-int dvb_usb_debug; +-module_param_named(debug, dvb_usb_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64,mem=128,uxfer=256 (or-able))." DVB_USB_DEBUG_STATUS); +- +-int dvb_usb_disable_rc_polling; +-module_param_named(disable_rc_polling, dvb_usb_disable_rc_polling, int, 0644); +-MODULE_PARM_DESC(disable_rc_polling, "disable remote control polling (default: 0)."); +- +-static int dvb_usb_force_pid_filter_usage; +-module_param_named(force_pid_filter_usage, dvb_usb_force_pid_filter_usage, int, 0444); +-MODULE_PARM_DESC(force_pid_filter_usage, "force all dvb-usb-devices to use a PID filter, if any (default: 0)."); +- +-static int dvb_usb_adapter_init(struct dvb_usb_device *d, short *adapter_nrs) +-{ +- struct dvb_usb_adapter *adap; +- int ret, n, o; +- +- for (n = 0; n < d->props.num_adapters; n++) { +- adap = &d->adapter[n]; +- adap->dev = d; +- adap->id = n; +- +- memcpy(&adap->props, &d->props.adapter[n], sizeof(struct dvb_usb_adapter_properties)); +- +- for (o = 0; o < adap->props.num_frontends; o++) { +- struct dvb_usb_adapter_fe_properties *props = &adap->props.fe[o]; +- /* speed - when running at FULL speed we need a HW PID filter */ +- if (d->udev->speed == USB_SPEED_FULL && !(props->caps & DVB_USB_ADAP_HAS_PID_FILTER)) { +- err("This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)"); +- return -ENODEV; +- } +- +- if ((d->udev->speed == USB_SPEED_FULL && props->caps & DVB_USB_ADAP_HAS_PID_FILTER) || +- (props->caps & DVB_USB_ADAP_NEED_PID_FILTERING)) { +- info("will use the device's hardware PID filter (table count: %d).", props->pid_filter_count); +- adap->fe_adap[o].pid_filtering = 1; +- adap->fe_adap[o].max_feed_count = props->pid_filter_count; +- } else { +- info("will pass the complete MPEG2 transport stream to the software demuxer."); +- adap->fe_adap[o].pid_filtering = 0; +- adap->fe_adap[o].max_feed_count = 255; +- } +- +- if (!adap->fe_adap[o].pid_filtering && +- dvb_usb_force_pid_filter_usage && +- props->caps & DVB_USB_ADAP_HAS_PID_FILTER) { +- info("pid filter enabled by module option."); +- adap->fe_adap[o].pid_filtering = 1; +- adap->fe_adap[o].max_feed_count = props->pid_filter_count; +- } +- +- if (props->size_of_priv > 0) { +- adap->fe_adap[o].priv = kzalloc(props->size_of_priv, GFP_KERNEL); +- if (adap->fe_adap[o].priv == NULL) { +- err("no memory for priv for adapter %d fe %d.", n, o); +- return -ENOMEM; +- } +- } +- } +- +- if (adap->props.size_of_priv > 0) { +- adap->priv = kzalloc(adap->props.size_of_priv, GFP_KERNEL); +- if (adap->priv == NULL) { +- err("no memory for priv for adapter %d.", n); +- return -ENOMEM; +- } +- } +- +- if ((ret = dvb_usb_adapter_stream_init(adap)) || +- (ret = dvb_usb_adapter_dvb_init(adap, adapter_nrs)) || +- (ret = dvb_usb_adapter_frontend_init(adap))) { +- return ret; +- } +- +- /* use exclusive FE lock if there is multiple shared FEs */ +- if (adap->fe_adap[1].fe) +- adap->dvb_adap.mfe_shared = 1; +- +- d->num_adapters_initialized++; +- d->state |= DVB_USB_STATE_DVB; +- } +- +- /* +- * when reloading the driver w/o replugging the device +- * sometimes a timeout occures, this helps +- */ +- if (d->props.generic_bulk_ctrl_endpoint != 0) { +- usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); +- } +- +- return 0; +-} +- +-static int dvb_usb_adapter_exit(struct dvb_usb_device *d) +-{ +- int n; +- +- for (n = 0; n < d->num_adapters_initialized; n++) { +- dvb_usb_adapter_frontend_exit(&d->adapter[n]); +- dvb_usb_adapter_dvb_exit(&d->adapter[n]); +- dvb_usb_adapter_stream_exit(&d->adapter[n]); +- kfree(d->adapter[n].priv); +- } +- d->num_adapters_initialized = 0; +- d->state &= ~DVB_USB_STATE_DVB; +- return 0; +-} +- +- +-/* general initialization functions */ +-static int dvb_usb_exit(struct dvb_usb_device *d) +-{ +- deb_info("state before exiting everything: %x\n", d->state); +- dvb_usb_remote_exit(d); +- dvb_usb_adapter_exit(d); +- dvb_usb_i2c_exit(d); +- deb_info("state should be zero now: %x\n", d->state); +- d->state = DVB_USB_STATE_INIT; +- kfree(d->priv); +- kfree(d); +- return 0; +-} +- +-static int dvb_usb_init(struct dvb_usb_device *d, short *adapter_nums) +-{ +- int ret = 0; +- +- mutex_init(&d->usb_mutex); +- mutex_init(&d->i2c_mutex); +- +- d->state = DVB_USB_STATE_INIT; +- +- if (d->props.size_of_priv > 0) { +- d->priv = kzalloc(d->props.size_of_priv, GFP_KERNEL); +- if (d->priv == NULL) { +- err("no memory for priv in 'struct dvb_usb_device'"); +- return -ENOMEM; +- } +- } +- +- /* check the capabilities and set appropriate variables */ +- dvb_usb_device_power_ctrl(d, 1); +- +- if ((ret = dvb_usb_i2c_init(d)) || +- (ret = dvb_usb_adapter_init(d, adapter_nums))) { +- dvb_usb_exit(d); +- return ret; +- } +- +- if ((ret = dvb_usb_remote_init(d))) +- err("could not initialize remote control."); +- +- dvb_usb_device_power_ctrl(d, 0); +- +- return 0; +-} +- +-/* determine the name and the state of the just found USB device */ +-static struct dvb_usb_device_description *dvb_usb_find_device(struct usb_device *udev, struct dvb_usb_device_properties *props, int *cold) +-{ +- int i, j; +- struct dvb_usb_device_description *desc = NULL; +- +- *cold = -1; +- +- for (i = 0; i < props->num_device_descs; i++) { +- +- for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].cold_ids[j] != NULL; j++) { +- deb_info("check for cold %x %x\n", props->devices[i].cold_ids[j]->idVendor, props->devices[i].cold_ids[j]->idProduct); +- if (props->devices[i].cold_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && +- props->devices[i].cold_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { +- *cold = 1; +- desc = &props->devices[i]; +- break; +- } +- } +- +- if (desc != NULL) +- break; +- +- for (j = 0; j < DVB_USB_ID_MAX_NUM && props->devices[i].warm_ids[j] != NULL; j++) { +- deb_info("check for warm %x %x\n", props->devices[i].warm_ids[j]->idVendor, props->devices[i].warm_ids[j]->idProduct); +- if (props->devices[i].warm_ids[j]->idVendor == le16_to_cpu(udev->descriptor.idVendor) && +- props->devices[i].warm_ids[j]->idProduct == le16_to_cpu(udev->descriptor.idProduct)) { +- *cold = 0; +- desc = &props->devices[i]; +- break; +- } +- } +- } +- +- if (desc != NULL && props->identify_state != NULL) +- props->identify_state(udev, props, &desc, cold); +- +- return desc; +-} +- +-int dvb_usb_device_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- if (onoff) +- d->powered++; +- else +- d->powered--; +- +- if (d->powered == 0 || (onoff && d->powered == 1)) { /* when switching from 1 to 0 or from 0 to 1 */ +- deb_info("power control: %d\n", onoff); +- if (d->props.power_ctrl) +- return d->props.power_ctrl(d, onoff); +- } +- return 0; +-} +- +-/* +- * USB +- */ +-int dvb_usb_device_init(struct usb_interface *intf, +- struct dvb_usb_device_properties *props, +- struct module *owner, struct dvb_usb_device **du, +- short *adapter_nums) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- struct dvb_usb_device *d = NULL; +- struct dvb_usb_device_description *desc = NULL; +- +- int ret = -ENOMEM, cold = 0; +- +- if (du != NULL) +- *du = NULL; +- +- if ((desc = dvb_usb_find_device(udev, props, &cold)) == NULL) { +- deb_err("something went very wrong, device was not found in current device list - let's see what comes next.\n"); +- return -ENODEV; +- } +- +- if (cold) { +- info("found a '%s' in cold state, will try to load a firmware", desc->name); +- ret = dvb_usb_download_firmware(udev, props); +- if (!props->no_reconnect || ret != 0) +- return ret; +- } +- +- info("found a '%s' in warm state.", desc->name); +- d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL); +- if (d == NULL) { +- err("no memory for 'struct dvb_usb_device'"); +- return -ENOMEM; +- } +- +- d->udev = udev; +- memcpy(&d->props, props, sizeof(struct dvb_usb_device_properties)); +- d->desc = desc; +- d->owner = owner; +- +- usb_set_intfdata(intf, d); +- +- if (du != NULL) +- *du = d; +- +- ret = dvb_usb_init(d, adapter_nums); +- +- if (ret == 0) +- info("%s successfully initialized and connected.", desc->name); +- else +- info("%s error while loading driver (%d)", desc->name, ret); +- return ret; +-} +-EXPORT_SYMBOL(dvb_usb_device_init); +- +-void dvb_usb_device_exit(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- const char *name = "generic DVB-USB module"; +- +- usb_set_intfdata(intf, NULL); +- if (d != NULL && d->desc != NULL) { +- name = d->desc->name; +- dvb_usb_exit(d); +- } +- info("%s successfully deinitialized and disconnected.", name); +- +-} +-EXPORT_SYMBOL(dvb_usb_device_exit); +- +-MODULE_VERSION("1.0"); +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("A library module containing commonly used USB and DVB function USB DVB devices"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +deleted file mode 100644 +index 41bacff..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c ++++ /dev/null +@@ -1,391 +0,0 @@ +-/* dvb-usb-remote.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 initializing the input-device and for handling remote-control-queries. +- */ +-#include "dvb-usb-common.h" +-#include +- +-static unsigned int +-legacy_dvb_usb_get_keymap_index(const struct input_keymap_entry *ke, +- struct rc_map_table *keymap, +- unsigned int keymap_size) +-{ +- unsigned int index; +- unsigned int scancode; +- +- if (ke->flags & INPUT_KEYMAP_BY_INDEX) { +- index = ke->index; +- } else { +- if (input_scancode_to_scalar(ke, &scancode)) +- return keymap_size; +- +- /* See if we can match the raw key code. */ +- for (index = 0; index < keymap_size; index++) +- if (keymap[index].scancode == scancode) +- break; +- +- /* See if there is an unused hole in the map */ +- if (index >= keymap_size) { +- for (index = 0; index < keymap_size; index++) { +- if (keymap[index].keycode == KEY_RESERVED || +- keymap[index].keycode == KEY_UNKNOWN) { +- break; +- } +- } +- } +- } +- +- return index; +-} +- +-static int legacy_dvb_usb_getkeycode(struct input_dev *dev, +- struct input_keymap_entry *ke) +-{ +- struct dvb_usb_device *d = input_get_drvdata(dev); +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- unsigned int keymap_size = d->props.rc.legacy.rc_map_size; +- unsigned int index; +- +- index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size); +- if (index >= keymap_size) +- return -EINVAL; +- +- ke->keycode = keymap[index].keycode; +- if (ke->keycode == KEY_UNKNOWN) +- ke->keycode = KEY_RESERVED; +- ke->len = sizeof(keymap[index].scancode); +- memcpy(&ke->scancode, &keymap[index].scancode, ke->len); +- ke->index = index; +- +- return 0; +-} +- +-static int legacy_dvb_usb_setkeycode(struct input_dev *dev, +- const struct input_keymap_entry *ke, +- unsigned int *old_keycode) +-{ +- struct dvb_usb_device *d = input_get_drvdata(dev); +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- unsigned int keymap_size = d->props.rc.legacy.rc_map_size; +- unsigned int index; +- +- index = legacy_dvb_usb_get_keymap_index(ke, keymap, keymap_size); +- /* +- * FIXME: Currently, it is not possible to increase the size of +- * scancode table. For it to happen, one possibility +- * would be to allocate a table with key_map_size + 1, +- * copying data, appending the new key on it, and freeing +- * the old one - or maybe just allocating some spare space +- */ +- if (index >= keymap_size) +- return -EINVAL; +- +- *old_keycode = keymap[index].keycode; +- keymap->keycode = ke->keycode; +- __set_bit(ke->keycode, dev->keybit); +- +- if (*old_keycode != KEY_RESERVED) { +- __clear_bit(*old_keycode, dev->keybit); +- for (index = 0; index < keymap_size; index++) { +- if (keymap[index].keycode == *old_keycode) { +- __set_bit(*old_keycode, dev->keybit); +- break; +- } +- } +- } +- +- return 0; +-} +- +-/* Remote-control poll function - called every dib->rc_query_interval ms to see +- * whether the remote control has received anything. +- * +- * TODO: Fix the repeat rate of the input device. +- */ +-static void legacy_dvb_usb_read_remote_control(struct work_struct *work) +-{ +- struct dvb_usb_device *d = +- container_of(work, struct dvb_usb_device, rc_query_work.work); +- u32 event; +- int state; +- +- /* TODO: need a lock here. We can simply skip checking for the remote control +- if we're busy. */ +- +- /* when the parameter has been set to 1 via sysfs while the driver was running */ +- if (dvb_usb_disable_rc_polling) +- return; +- +- if (d->props.rc.legacy.rc_query(d,&event,&state)) { +- err("error while querying for an remote control event."); +- goto schedule; +- } +- +- +- switch (state) { +- case REMOTE_NO_KEY_PRESSED: +- break; +- case REMOTE_KEY_PRESSED: +- deb_rc("key pressed\n"); +- d->last_event = event; +- case REMOTE_KEY_REPEAT: +- deb_rc("key repeated\n"); +- input_event(d->input_dev, EV_KEY, event, 1); +- input_sync(d->input_dev); +- input_event(d->input_dev, EV_KEY, d->last_event, 0); +- input_sync(d->input_dev); +- break; +- default: +- break; +- } +- +-/* improved repeat handling ??? +- switch (state) { +- case REMOTE_NO_KEY_PRESSED: +- deb_rc("NO KEY PRESSED\n"); +- if (d->last_state != REMOTE_NO_KEY_PRESSED) { +- deb_rc("releasing event %d\n",d->last_event); +- input_event(d->rc_input_dev, EV_KEY, d->last_event, 0); +- input_sync(d->rc_input_dev); +- } +- d->last_state = REMOTE_NO_KEY_PRESSED; +- d->last_event = 0; +- break; +- case REMOTE_KEY_PRESSED: +- deb_rc("KEY PRESSED\n"); +- deb_rc("pressing event %d\n",event); +- +- input_event(d->rc_input_dev, EV_KEY, event, 1); +- input_sync(d->rc_input_dev); +- +- d->last_event = event; +- d->last_state = REMOTE_KEY_PRESSED; +- break; +- case REMOTE_KEY_REPEAT: +- deb_rc("KEY_REPEAT\n"); +- if (d->last_state != REMOTE_NO_KEY_PRESSED) { +- deb_rc("repeating event %d\n",d->last_event); +- input_event(d->rc_input_dev, EV_KEY, d->last_event, 2); +- input_sync(d->rc_input_dev); +- d->last_state = REMOTE_KEY_REPEAT; +- } +- default: +- break; +- } +-*/ +- +-schedule: +- schedule_delayed_work(&d->rc_query_work,msecs_to_jiffies(d->props.rc.legacy.rc_interval)); +-} +- +-static int legacy_dvb_usb_remote_init(struct dvb_usb_device *d) +-{ +- int i, err, rc_interval; +- struct input_dev *input_dev; +- +- input_dev = input_allocate_device(); +- if (!input_dev) +- return -ENOMEM; +- +- input_dev->evbit[0] = BIT_MASK(EV_KEY); +- input_dev->name = "IR-receiver inside an USB DVB receiver"; +- input_dev->phys = d->rc_phys; +- usb_to_input_id(d->udev, &input_dev->id); +- input_dev->dev.parent = &d->udev->dev; +- d->input_dev = input_dev; +- d->rc_dev = NULL; +- +- input_dev->getkeycode = legacy_dvb_usb_getkeycode; +- input_dev->setkeycode = legacy_dvb_usb_setkeycode; +- +- /* set the bits for the keys */ +- deb_rc("key map size: %d\n", d->props.rc.legacy.rc_map_size); +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) { +- deb_rc("setting bit for event %d item %d\n", +- d->props.rc.legacy.rc_map_table[i].keycode, i); +- set_bit(d->props.rc.legacy.rc_map_table[i].keycode, input_dev->keybit); +- } +- +- /* setting these two values to non-zero, we have to manage key repeats */ +- input_dev->rep[REP_PERIOD] = d->props.rc.legacy.rc_interval; +- input_dev->rep[REP_DELAY] = d->props.rc.legacy.rc_interval + 150; +- +- input_set_drvdata(input_dev, d); +- +- err = input_register_device(input_dev); +- if (err) +- input_free_device(input_dev); +- +- rc_interval = d->props.rc.legacy.rc_interval; +- +- INIT_DELAYED_WORK(&d->rc_query_work, legacy_dvb_usb_read_remote_control); +- +- info("schedule remote query interval to %d msecs.", rc_interval); +- schedule_delayed_work(&d->rc_query_work, +- msecs_to_jiffies(rc_interval)); +- +- d->state |= DVB_USB_STATE_REMOTE; +- +- return err; +-} +- +-/* Remote-control poll function - called every dib->rc_query_interval ms to see +- * whether the remote control has received anything. +- * +- * TODO: Fix the repeat rate of the input device. +- */ +-static void dvb_usb_read_remote_control(struct work_struct *work) +-{ +- struct dvb_usb_device *d = +- container_of(work, struct dvb_usb_device, rc_query_work.work); +- int err; +- +- /* TODO: need a lock here. We can simply skip checking for the remote control +- if we're busy. */ +- +- /* when the parameter has been set to 1 via sysfs while the +- * driver was running, or when bulk mode is enabled after IR init +- */ +- if (dvb_usb_disable_rc_polling || d->props.rc.core.bulk_mode) +- return; +- +- err = d->props.rc.core.rc_query(d); +- if (err) +- err("error %d while querying for an remote control event.", err); +- +- schedule_delayed_work(&d->rc_query_work, +- msecs_to_jiffies(d->props.rc.core.rc_interval)); +-} +- +-static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d) +-{ +- int err, rc_interval; +- struct rc_dev *dev; +- +- dev = rc_allocate_device(); +- if (!dev) +- return -ENOMEM; +- +- dev->driver_name = d->props.rc.core.module_name; +- dev->map_name = d->props.rc.core.rc_codes; +- dev->change_protocol = d->props.rc.core.change_protocol; +- dev->allowed_protos = d->props.rc.core.allowed_protos; +- dev->driver_type = d->props.rc.core.driver_type; +- usb_to_input_id(d->udev, &dev->input_id); +- dev->input_name = "IR-receiver inside an USB DVB receiver"; +- dev->input_phys = d->rc_phys; +- dev->dev.parent = &d->udev->dev; +- dev->priv = d; +- +- err = rc_register_device(dev); +- if (err < 0) { +- rc_free_device(dev); +- return err; +- } +- +- d->input_dev = NULL; +- d->rc_dev = dev; +- +- if (!d->props.rc.core.rc_query || d->props.rc.core.bulk_mode) +- return 0; +- +- /* Polling mode - initialize a work queue for handling it */ +- INIT_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); +- +- rc_interval = d->props.rc.core.rc_interval; +- +- info("schedule remote query interval to %d msecs.", rc_interval); +- schedule_delayed_work(&d->rc_query_work, +- msecs_to_jiffies(rc_interval)); +- +- return 0; +-} +- +-int dvb_usb_remote_init(struct dvb_usb_device *d) +-{ +- int err; +- +- if (dvb_usb_disable_rc_polling) +- return 0; +- +- if (d->props.rc.legacy.rc_map_table && d->props.rc.legacy.rc_query) +- d->props.rc.mode = DVB_RC_LEGACY; +- else if (d->props.rc.core.rc_codes) +- d->props.rc.mode = DVB_RC_CORE; +- else +- return 0; +- +- usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); +- strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); +- +- /* Start the remote-control polling. */ +- if (d->props.rc.legacy.rc_interval < 40) +- d->props.rc.legacy.rc_interval = 100; /* default */ +- +- if (d->props.rc.mode == DVB_RC_LEGACY) +- err = legacy_dvb_usb_remote_init(d); +- else +- err = rc_core_dvb_usb_remote_init(d); +- if (err) +- return err; +- +- d->state |= DVB_USB_STATE_REMOTE; +- +- return 0; +-} +- +-int dvb_usb_remote_exit(struct dvb_usb_device *d) +-{ +- if (d->state & DVB_USB_STATE_REMOTE) { +- cancel_delayed_work_sync(&d->rc_query_work); +- if (d->props.rc.mode == DVB_RC_LEGACY) +- input_unregister_device(d->input_dev); +- else +- rc_unregister_device(d->rc_dev); +- } +- d->state &= ~DVB_USB_STATE_REMOTE; +- return 0; +-} +- +-#define DVB_USB_RC_NEC_EMPTY 0x00 +-#define DVB_USB_RC_NEC_KEY_PRESSED 0x01 +-#define DVB_USB_RC_NEC_KEY_REPEATED 0x02 +-int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *d, +- u8 keybuf[5], u32 *event, int *state) +-{ +- int i; +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- *event = 0; +- *state = REMOTE_NO_KEY_PRESSED; +- switch (keybuf[0]) { +- case DVB_USB_RC_NEC_EMPTY: +- break; +- case DVB_USB_RC_NEC_KEY_PRESSED: +- if ((u8) ~keybuf[1] != keybuf[2] || +- (u8) ~keybuf[3] != keybuf[4]) { +- deb_err("remote control checksum failed.\n"); +- break; +- } +- /* See if we can match the raw key code. */ +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) +- if (rc5_custom(&keymap[i]) == keybuf[1] && +- rc5_data(&keymap[i]) == keybuf[3]) { +- *event = keymap[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- return 0; +- } +- deb_err("key mapping failed - no appropriate key found in keymapping\n"); +- break; +- case DVB_USB_RC_NEC_KEY_REPEATED: +- *state = REMOTE_KEY_REPEAT; +- break; +- default: +- deb_err("unknown type of remote status: %d\n",keybuf[0]); +- break; +- } +- return 0; +-} +-EXPORT_SYMBOL(dvb_usb_nec_rc_key_to_event); +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +deleted file mode 100644 +index 53a5c30..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c ++++ /dev/null +@@ -1,109 +0,0 @@ +-/* dvb-usb-urb.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 keeps functions for initializing and handling the +- * USB and URB stuff. +- */ +-#include "dvb-usb-common.h" +- +-int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, +- u16 rlen, int delay_ms) +-{ +- int actlen,ret = -ENOMEM; +- +- if (!d || wbuf == NULL || wlen == 0) +- return -EINVAL; +- +- if (d->props.generic_bulk_ctrl_endpoint == 0) { +- err("endpoint for generic control not specified."); +- return -EINVAL; +- } +- +- if ((ret = mutex_lock_interruptible(&d->usb_mutex))) +- return ret; +- +- deb_xfer(">>> "); +- debug_dump(wbuf,wlen,deb_xfer); +- +- ret = usb_bulk_msg(d->udev,usb_sndbulkpipe(d->udev, +- d->props.generic_bulk_ctrl_endpoint), wbuf,wlen,&actlen, +- 2000); +- +- if (ret) +- err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); +- else +- ret = actlen != wlen ? -1 : 0; +- +- /* an answer is expected, and no error before */ +- if (!ret && rbuf && rlen) { +- if (delay_ms) +- msleep(delay_ms); +- +- ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev, +- d->props.generic_bulk_ctrl_endpoint_response ? +- d->props.generic_bulk_ctrl_endpoint_response : +- d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, +- 2000); +- +- if (ret) +- err("recv bulk message failed: %d",ret); +- else { +- deb_xfer("<<< "); +- debug_dump(rbuf,actlen,deb_xfer); +- } +- } +- +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +-EXPORT_SYMBOL(dvb_usb_generic_rw); +- +-int dvb_usb_generic_write(struct dvb_usb_device *d, u8 *buf, u16 len) +-{ +- return dvb_usb_generic_rw(d,buf,len,NULL,0,0); +-} +-EXPORT_SYMBOL(dvb_usb_generic_write); +- +-static void dvb_usb_data_complete(struct usb_data_stream *stream, u8 *buffer, size_t length) +-{ +- struct dvb_usb_adapter *adap = stream->user_priv; +- if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) +- dvb_dmx_swfilter(&adap->demux, buffer, length); +-} +- +-static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer, size_t length) +-{ +- struct dvb_usb_adapter *adap = stream->user_priv; +- if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) +- dvb_dmx_swfilter_204(&adap->demux, buffer, length); +-} +- +-int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) +-{ +- int i, ret = 0; +- for (i = 0; i < adap->props.num_frontends; i++) { +- +- adap->fe_adap[i].stream.udev = adap->dev->udev; +- if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_204_BYTE_TS) +- adap->fe_adap[i].stream.complete = +- dvb_usb_data_complete_204; +- else +- adap->fe_adap[i].stream.complete = dvb_usb_data_complete; +- adap->fe_adap[i].stream.user_priv = adap; +- ret = usb_urb_init(&adap->fe_adap[i].stream, +- &adap->props.fe[i].stream); +- if (ret < 0) +- break; +- } +- return ret; +-} +- +-int dvb_usb_adapter_stream_exit(struct dvb_usb_adapter *adap) +-{ +- int i; +- for (i = 0; i < adap->props.num_frontends; i++) +- usb_urb_exit(&adap->fe_adap[i].stream); +- return 0; +-} +diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h +deleted file mode 100644 +index 6d7d13f..0000000 +--- a/drivers/media/dvb/dvb-usb/dvb-usb.h ++++ /dev/null +@@ -1,484 +0,0 @@ +-/* dvb-usb.h 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. +- * +- * the headerfile, all dvb-usb-drivers have to include. +- * +- * TODO: clean-up the structures for unused fields and update the comments +- */ +-#ifndef __DVB_USB_H__ +-#define __DVB_USB_H__ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "dvb_demux.h" +-#include "dvb_net.h" +-#include "dmxdev.h" +- +-#include "dvb-pll.h" +- +-#include "dvb-usb-ids.h" +- +-/* debug */ +-#ifdef CONFIG_DVB_USB_DEBUG +-#define dprintk(var,level,args...) \ +- do { if ((var & level)) { printk(args); } } while (0) +- +-#define debug_dump(b,l,func) {\ +- int loop_; \ +- for (loop_ = 0; loop_ < l; loop_++) func("%02x ", b[loop_]); \ +- func("\n");\ +-} +-#define DVB_USB_DEBUG_STATUS +-#else +-#define dprintk(args...) +-#define debug_dump(b,l,func) +- +-#define DVB_USB_DEBUG_STATUS " (debugging is not enabled)" +- +-#endif +- +-/* generic log methods - taken from usb.h */ +-#ifndef DVB_USB_LOG_PREFIX +- #define DVB_USB_LOG_PREFIX "dvb-usb (please define a log prefix)" +-#endif +- +-#undef err +-#define err(format, arg...) printk(KERN_ERR DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +-#undef info +-#define info(format, arg...) printk(KERN_INFO DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +-#undef warn +-#define warn(format, arg...) printk(KERN_WARNING DVB_USB_LOG_PREFIX ": " format "\n" , ## arg) +- +-/** +- * struct dvb_usb_device_description - name and its according USB IDs +- * @name: real name of the box, regardless which DVB USB device class is in use +- * @cold_ids: array of struct usb_device_id which describe the device in +- * pre-firmware state +- * @warm_ids: array of struct usb_device_id which describe the device in +- * post-firmware state +- * +- * Each DVB USB device class can have one or more actual devices, this struct +- * assigns a name to it. +- */ +-struct dvb_usb_device_description { +- const char *name; +- +-#define DVB_USB_ID_MAX_NUM 15 +- struct usb_device_id *cold_ids[DVB_USB_ID_MAX_NUM]; +- struct usb_device_id *warm_ids[DVB_USB_ID_MAX_NUM]; +-}; +- +-static inline u8 rc5_custom(struct rc_map_table *key) +-{ +- return (key->scancode >> 8) & 0xff; +-} +- +-static inline u8 rc5_data(struct rc_map_table *key) +-{ +- return key->scancode & 0xff; +-} +- +-static inline u16 rc5_scan(struct rc_map_table *key) +-{ +- return key->scancode & 0xffff; +-} +- +-struct dvb_usb_device; +-struct dvb_usb_adapter; +-struct usb_data_stream; +- +-/** +- * Properties of USB streaming - TODO this structure should be somewhere else +- * describes the kind of USB transfer used for data-streaming. +- * (BULK or ISOC) +- */ +-struct usb_data_stream_properties { +-#define USB_BULK 1 +-#define USB_ISOC 2 +- int type; +- int count; +- int endpoint; +- +- union { +- struct { +- int buffersize; /* per URB */ +- } bulk; +- struct { +- int framesperurb; +- int framesize; +- int interval; +- } isoc; +- } u; +-}; +- +-/** +- * struct dvb_usb_adapter_properties - properties of a dvb-usb-adapter. +- * A DVB-USB-Adapter is basically a dvb_adapter which is present on a USB-device. +- * @caps: capabilities of the DVB USB device. +- * @pid_filter_count: number of PID filter position in the optional hardware +- * PID-filter. +- * @num_frontends: number of frontends of the DVB USB adapter. +- * @frontend_ctrl: called to power on/off active frontend. +- * @streaming_ctrl: called to start and stop the MPEG2-TS streaming of the +- * device (not URB submitting/killing). +- * @pid_filter_ctrl: called to en/disable the PID filter, if any. +- * @pid_filter: called to set/unset a PID for filtering. +- * @frontend_attach: called to attach the possible frontends (fill fe-field +- * of struct dvb_usb_device). +- * @tuner_attach: called to attach the correct tuner and to fill pll_addr, +- * pll_desc and pll_init_buf of struct dvb_usb_device). +- * @stream: configuration of the USB streaming +- */ +-struct dvb_usb_adapter_fe_properties { +-#define DVB_USB_ADAP_HAS_PID_FILTER 0x01 +-#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 +-#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 +-#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08 +- int caps; +- int pid_filter_count; +- +- int (*streaming_ctrl) (struct dvb_usb_adapter *, int); +- int (*pid_filter_ctrl) (struct dvb_usb_adapter *, int); +- int (*pid_filter) (struct dvb_usb_adapter *, int, u16, int); +- +- int (*frontend_attach) (struct dvb_usb_adapter *); +- int (*tuner_attach) (struct dvb_usb_adapter *); +- +- struct usb_data_stream_properties stream; +- +- int size_of_priv; +-}; +- +-#define MAX_NO_OF_FE_PER_ADAP 2 +-struct dvb_usb_adapter_properties { +- int size_of_priv; +- +- int (*frontend_ctrl) (struct dvb_frontend *, int); +- int (*fe_ioctl_override) (struct dvb_frontend *, +- unsigned int, void *, unsigned int); +- +- int num_frontends; +- struct dvb_usb_adapter_fe_properties fe[MAX_NO_OF_FE_PER_ADAP]; +-}; +- +-/** +- * struct dvb_rc_legacy - old properties of remote controller +- * @rc_map_table: a hard-wired array of struct rc_map_table (NULL to disable +- * remote control handling). +- * @rc_map_size: number of items in @rc_map_table. +- * @rc_query: called to query an event event. +- * @rc_interval: time in ms between two queries. +- */ +-struct dvb_rc_legacy { +-/* remote control properties */ +-#define REMOTE_NO_KEY_PRESSED 0x00 +-#define REMOTE_KEY_PRESSED 0x01 +-#define REMOTE_KEY_REPEAT 0x02 +- struct rc_map_table *rc_map_table; +- int rc_map_size; +- int (*rc_query) (struct dvb_usb_device *, u32 *, int *); +- int rc_interval; +-}; +- +-/** +- * struct dvb_rc properties of remote controller, using rc-core +- * @rc_codes: name of rc codes table +- * @protocol: type of protocol(s) currently used by the driver +- * @allowed_protos: protocol(s) supported by the driver +- * @driver_type: Used to point if a device supports raw mode +- * @change_protocol: callback to change protocol +- * @rc_query: called to query an event event. +- * @rc_interval: time in ms between two queries. +- * @bulk_mode: device supports bulk mode for RC (disable polling mode) +- */ +-struct dvb_rc { +- char *rc_codes; +- u64 protocol; +- u64 allowed_protos; +- enum rc_driver_type driver_type; +- int (*change_protocol)(struct rc_dev *dev, u64 rc_type); +- char *module_name; +- int (*rc_query) (struct dvb_usb_device *d); +- int rc_interval; +- bool bulk_mode; /* uses bulk mode */ +-}; +- +-/** +- * enum dvb_usb_mode - Specifies if it is using a legacy driver or a new one +- * based on rc-core +- * This is initialized/used only inside dvb-usb-remote.c. +- * It shouldn't be set by the drivers. +- */ +-enum dvb_usb_mode { +- DVB_RC_LEGACY, +- DVB_RC_CORE, +-}; +- +-/** +- * struct dvb_usb_device_properties - properties of a dvb-usb-device +- * @usb_ctrl: which USB device-side controller is in use. Needed for firmware +- * download. +- * @firmware: name of the firmware file. +- * @download_firmware: called to download the firmware when the usb_ctrl is +- * DEVICE_SPECIFIC. +- * @no_reconnect: device doesn't do a reconnect after downloading the firmware, +- * so do the warm initialization right after it +- * +- * @size_of_priv: how many bytes shall be allocated for the private field +- * of struct dvb_usb_device. +- * +- * @power_ctrl: called to enable/disable power of the device. +- * @read_mac_address: called to read the MAC address of the device. +- * @identify_state: called to determine the state (cold or warm), when it +- * is not distinguishable by the USB IDs. +- * +- * @rc: remote controller properties +- * +- * @i2c_algo: i2c_algorithm if the device has I2CoverUSB. +- * +- * @generic_bulk_ctrl_endpoint: most of the DVB USB devices have a generic +- * endpoint which received control messages with bulk transfers. When this +- * is non-zero, one can use dvb_usb_generic_rw and dvb_usb_generic_write- +- * helper functions. +- * +- * @generic_bulk_ctrl_endpoint_response: some DVB USB devices use a separate +- * endpoint for responses to control messages sent with bulk transfers via +- * the generic_bulk_ctrl_endpoint. When this is non-zero, this will be used +- * instead of the generic_bulk_ctrl_endpoint when reading usb responses in +- * the dvb_usb_generic_rw helper function. +- * +- * @num_device_descs: number of struct dvb_usb_device_description in @devices +- * @devices: array of struct dvb_usb_device_description compatibles with these +- * properties. +- */ +-#define MAX_NO_OF_ADAPTER_PER_DEVICE 2 +-struct dvb_usb_device_properties { +- +-#define DVB_USB_IS_AN_I2C_ADAPTER 0x01 +- int caps; +- +-#define DEVICE_SPECIFIC 0 +-#define CYPRESS_AN2135 1 +-#define CYPRESS_AN2235 2 +-#define CYPRESS_FX2 3 +- int usb_ctrl; +- int (*download_firmware) (struct usb_device *, const struct firmware *); +- const char *firmware; +- int no_reconnect; +- +- int size_of_priv; +- +- int num_adapters; +- struct dvb_usb_adapter_properties adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; +- +- int (*power_ctrl) (struct dvb_usb_device *, int); +- int (*read_mac_address) (struct dvb_usb_device *, u8 []); +- int (*identify_state) (struct usb_device *, struct dvb_usb_device_properties *, +- struct dvb_usb_device_description **, int *); +- +- struct { +- enum dvb_usb_mode mode; /* Drivers shouldn't touch on it */ +- struct dvb_rc_legacy legacy; +- struct dvb_rc core; +- } rc; +- +- struct i2c_algorithm *i2c_algo; +- +- int generic_bulk_ctrl_endpoint; +- int generic_bulk_ctrl_endpoint_response; +- +- int num_device_descs; +- struct dvb_usb_device_description devices[12]; +-}; +- +-/** +- * struct usb_data_stream - generic object of an USB stream +- * @buf_num: number of buffer allocated. +- * @buf_size: size of each buffer in buf_list. +- * @buf_list: array containing all allocate buffers for streaming. +- * @dma_addr: list of dma_addr_t for each buffer in buf_list. +- * +- * @urbs_initialized: number of URBs initialized. +- * @urbs_submitted: number of URBs submitted. +- */ +-#define MAX_NO_URBS_FOR_DATA_STREAM 10 +-struct usb_data_stream { +- struct usb_device *udev; +- struct usb_data_stream_properties props; +- +-#define USB_STATE_INIT 0x00 +-#define USB_STATE_URB_BUF 0x01 +- int state; +- +- void (*complete) (struct usb_data_stream *, u8 *, size_t); +- +- struct urb *urb_list[MAX_NO_URBS_FOR_DATA_STREAM]; +- int buf_num; +- unsigned long buf_size; +- u8 *buf_list[MAX_NO_URBS_FOR_DATA_STREAM]; +- dma_addr_t dma_addr[MAX_NO_URBS_FOR_DATA_STREAM]; +- +- int urbs_initialized; +- int urbs_submitted; +- +- void *user_priv; +-}; +- +-/** +- * struct dvb_usb_adapter - a DVB adapter on a USB device +- * @id: index of this adapter (starting with 0). +- * +- * @feedcount: number of reqested feeds (used for streaming-activation) +- * @pid_filtering: is hardware pid_filtering used or not. +- * +- * @pll_addr: I2C address of the tuner for programming +- * @pll_init: array containing the initialization buffer +- * @pll_desc: pointer to the appropriate struct dvb_pll_desc +- * @tuner_pass_ctrl: called to (de)activate tuner passthru of the demod or the board +- * +- * @dvb_adap: device's dvb_adapter. +- * @dmxdev: device's dmxdev. +- * @demux: device's software demuxer. +- * @dvb_net: device's dvb_net interfaces. +- * @dvb_frontend: device's frontend. +- * @max_feed_count: how many feeds can be handled simultaneously by this +- * device +- * +- * @fe_init: rerouted frontend-init (wakeup) function. +- * @fe_sleep: rerouted frontend-sleep function. +- * +- * @stream: the usb data stream. +- */ +-struct dvb_usb_fe_adapter { +- struct dvb_frontend *fe; +- +- int (*fe_init) (struct dvb_frontend *); +- int (*fe_sleep) (struct dvb_frontend *); +- +- struct usb_data_stream stream; +- +- int pid_filtering; +- int max_feed_count; +- +- void *priv; +-}; +- +-struct dvb_usb_adapter { +- struct dvb_usb_device *dev; +- struct dvb_usb_adapter_properties props; +- +-#define DVB_USB_ADAP_STATE_INIT 0x000 +-#define DVB_USB_ADAP_STATE_DVB 0x001 +- int state; +- +- u8 id; +- +- int feedcount; +- +- /* dvb */ +- struct dvb_adapter dvb_adap; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- struct dvb_net dvb_net; +- +- struct dvb_usb_fe_adapter fe_adap[MAX_NO_OF_FE_PER_ADAP]; +- int active_fe; +- int num_frontends_initialized; +- +- void *priv; +-}; +- +-/** +- * struct dvb_usb_device - object of a DVB USB device +- * @props: copy of the struct dvb_usb_properties this device belongs to. +- * @desc: pointer to the device's struct dvb_usb_device_description. +- * @state: initialization and runtime state of the device. +- * +- * @powered: indicated whether the device is power or not. +- * Powered is in/decremented for each call to modify the state. +- * @udev: pointer to the device's struct usb_device. +- * +- * @usb_mutex: semaphore of USB control messages (reading needs two messages) +- * @i2c_mutex: semaphore for i2c-transfers +- * +- * @i2c_adap: device's i2c_adapter if it uses I2CoverUSB +- * +- * @rc_dev: rc device for the remote control (rc-core mode) +- * @input_dev: input device for the remote control (legacy mode) +- * @rc_query_work: struct work_struct frequent rc queries +- * @last_event: last triggered event +- * @last_state: last state (no, pressed, repeat) +- * @owner: owner of the dvb_adapter +- * @priv: private data of the actual driver (allocate by dvb-usb, size defined +- * in size_of_priv of dvb_usb_properties). +- */ +-struct dvb_usb_device { +- struct dvb_usb_device_properties props; +- struct dvb_usb_device_description *desc; +- +- struct usb_device *udev; +- +-#define DVB_USB_STATE_INIT 0x000 +-#define DVB_USB_STATE_I2C 0x001 +-#define DVB_USB_STATE_DVB 0x002 +-#define DVB_USB_STATE_REMOTE 0x004 +- int state; +- +- int powered; +- +- /* locking */ +- struct mutex usb_mutex; +- +- /* i2c */ +- struct mutex i2c_mutex; +- struct i2c_adapter i2c_adap; +- +- int num_adapters_initialized; +- struct dvb_usb_adapter adapter[MAX_NO_OF_ADAPTER_PER_DEVICE]; +- +- /* remote control */ +- struct rc_dev *rc_dev; +- struct input_dev *input_dev; +- char rc_phys[64]; +- struct delayed_work rc_query_work; +- u32 last_event; +- int last_state; +- +- struct module *owner; +- +- void *priv; +-}; +- +-extern int dvb_usb_device_init(struct usb_interface *, +- struct dvb_usb_device_properties *, +- struct module *, struct dvb_usb_device **, +- short *adapter_nums); +-extern void dvb_usb_device_exit(struct usb_interface *); +- +-/* the generic read/write method for device control */ +-extern int dvb_usb_generic_rw(struct dvb_usb_device *, u8 *, u16, u8 *, u16,int); +-extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); +- +-/* commonly used remote control parsing */ +-extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); +- +-/* commonly used firmware download types and function */ +-struct hexline { +- u8 len; +- u32 addr; +- u8 type; +- u8 data[255]; +- u8 chk; +-}; +-extern int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type); +-extern int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx, int *pos); +- +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c +deleted file mode 100644 +index 451c5a7..0000000 +--- a/drivers/media/dvb/dvb-usb/dw2102.c ++++ /dev/null +@@ -1,1955 +0,0 @@ +-/* DVB USB framework compliant Linux driver for the +- * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, +- * TeVii S600, S630, S650, S660, S480, +- * Prof 1100, 7500, +- * Geniatech SU3000 Cards +- * Copyright (C) 2008-2011 Igor M. Liplianin (liplianin@me.by) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the +- * Free Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dw2102.h" +-#include "si21xx.h" +-#include "stv0299.h" +-#include "z0194a.h" +-#include "stv0288.h" +-#include "stb6000.h" +-#include "eds1547.h" +-#include "cx24116.h" +-#include "tda1002x.h" +-#include "mt312.h" +-#include "zl10039.h" +-#include "ds3000.h" +-#include "stv0900.h" +-#include "stv6110.h" +-#include "stb6100.h" +-#include "stb6100_proc.h" +- +-#ifndef USB_PID_DW2102 +-#define USB_PID_DW2102 0x2102 +-#endif +- +-#ifndef USB_PID_DW2104 +-#define USB_PID_DW2104 0x2104 +-#endif +- +-#ifndef USB_PID_DW3101 +-#define USB_PID_DW3101 0x3101 +-#endif +- +-#ifndef USB_PID_CINERGY_S +-#define USB_PID_CINERGY_S 0x0064 +-#endif +- +-#ifndef USB_PID_TEVII_S630 +-#define USB_PID_TEVII_S630 0xd630 +-#endif +- +-#ifndef USB_PID_TEVII_S650 +-#define USB_PID_TEVII_S650 0xd650 +-#endif +- +-#ifndef USB_PID_TEVII_S660 +-#define USB_PID_TEVII_S660 0xd660 +-#endif +- +-#ifndef USB_PID_TEVII_S480_1 +-#define USB_PID_TEVII_S480_1 0xd481 +-#endif +- +-#ifndef USB_PID_TEVII_S480_2 +-#define USB_PID_TEVII_S480_2 0xd482 +-#endif +- +-#ifndef USB_PID_PROF_1100 +-#define USB_PID_PROF_1100 0xb012 +-#endif +- +-#define DW210X_READ_MSG 0 +-#define DW210X_WRITE_MSG 1 +- +-#define REG_1F_SYMBOLRATE_BYTE0 0x1f +-#define REG_20_SYMBOLRATE_BYTE1 0x20 +-#define REG_21_SYMBOLRATE_BYTE2 0x21 +-/* on my own*/ +-#define DW2102_VOLTAGE_CTRL (0x1800) +-#define SU3000_STREAM_CTRL (0x1900) +-#define DW2102_RC_QUERY (0x1a00) +-#define DW2102_LED_CTRL (0x1b00) +- +-#define err_str "did not find the firmware file. (%s) " \ +- "Please see linux/Documentation/dvb/ for more details " \ +- "on firmware-problems." +- +-struct rc_map_dvb_usb_table_table { +- struct rc_map_table *rc_keys; +- int rc_keys_size; +-}; +- +-struct su3000_state { +- u8 initialized; +-}; +- +-struct s6x0_state { +- int (*old_set_voltage)(struct dvb_frontend *f, fe_sec_voltage_t v); +-}; +- +-/* debug */ +-static int dvb_usb_dw2102_debug; +-module_param_named(debug, dvb_usb_dw2102_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info 2=xfer 4=rc(or-able))." +- DVB_USB_DEBUG_STATUS); +- +-/* keymaps */ +-static int ir_keymap; +-module_param_named(keymap, ir_keymap, int, 0644); +-MODULE_PARM_DESC(keymap, "set keymap 0=default 1=dvbworld 2=tevii 3=tbs ..." +- " 256=none"); +- +-/* demod probe */ +-static int demod_probe = 1; +-module_param_named(demod, demod_probe, int, 0644); +-MODULE_PARM_DESC(demod, "demod to probe (1=cx24116 2=stv0903+stv6110 " +- "4=stv0903+stb6100(or-able))."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int dw210x_op_rw(struct usb_device *dev, u8 request, u16 value, +- u16 index, u8 * data, u16 len, int flags) +-{ +- int ret; +- u8 *u8buf; +- unsigned int pipe = (flags == DW210X_READ_MSG) ? +- usb_rcvctrlpipe(dev, 0) : usb_sndctrlpipe(dev, 0); +- u8 request_type = (flags == DW210X_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; +- +- u8buf = kmalloc(len, GFP_KERNEL); +- if (!u8buf) +- return -ENOMEM; +- +- +- if (flags == DW210X_WRITE_MSG) +- memcpy(u8buf, data, len); +- ret = usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, +- value, index , u8buf, len, 2000); +- +- if (flags == DW210X_READ_MSG) +- memcpy(data, u8buf, len); +- +- kfree(u8buf); +- return ret; +-} +- +-/* I2C */ +-static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i = 0, ret = 0; +- u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; +- u16 value; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (num) { +- case 2: +- /* read stv0299 register */ +- value = msg[0].buf[0];/* register */ +- for (i = 0; i < msg[1].len; i++) { +- ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0, +- buf6, 2, DW210X_READ_MSG); +- msg[1].buf[i] = buf6[0]; +- } +- break; +- case 1: +- switch (msg[0].addr) { +- case 0x68: +- /* write to stv0299 register */ +- buf6[0] = 0x2a; +- buf6[1] = msg[0].buf[0]; +- buf6[2] = msg[0].buf[1]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- buf6, 3, DW210X_WRITE_MSG); +- break; +- case 0x60: +- if (msg[0].flags == 0) { +- /* write to tuner pll */ +- buf6[0] = 0x2c; +- buf6[1] = 5; +- buf6[2] = 0xc0; +- buf6[3] = msg[0].buf[0]; +- buf6[4] = msg[0].buf[1]; +- buf6[5] = msg[0].buf[2]; +- buf6[6] = msg[0].buf[3]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- buf6, 7, DW210X_WRITE_MSG); +- } else { +- /* read from tuner */ +- ret = dw210x_op_rw(d->udev, 0xb5, 0, 0, +- buf6, 1, DW210X_READ_MSG); +- msg[0].buf[0] = buf6[0]; +- } +- break; +- case (DW2102_RC_QUERY): +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- buf6, 2, DW210X_READ_MSG); +- msg[0].buf[0] = buf6[0]; +- msg[0].buf[1] = buf6[1]; +- break; +- case (DW2102_VOLTAGE_CTRL): +- buf6[0] = 0x30; +- buf6[1] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- buf6, 2, DW210X_WRITE_MSG); +- break; +- } +- +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, +- struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0; +- u8 buf6[] = {0, 0, 0, 0, 0, 0, 0}; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (num) { +- case 2: +- /* read si2109 register by number */ +- buf6[0] = msg[0].addr << 1; +- buf6[1] = msg[0].len; +- buf6[2] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- buf6, msg[0].len + 2, DW210X_WRITE_MSG); +- /* read si2109 register */ +- ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, +- buf6, msg[1].len + 2, DW210X_READ_MSG); +- memcpy(msg[1].buf, buf6 + 2, msg[1].len); +- +- break; +- case 1: +- switch (msg[0].addr) { +- case 0x68: +- /* write to si2109 register */ +- buf6[0] = msg[0].addr << 1; +- buf6[1] = msg[0].len; +- memcpy(buf6 + 2, msg[0].buf, msg[0].len); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, +- msg[0].len + 2, DW210X_WRITE_MSG); +- break; +- case(DW2102_RC_QUERY): +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- buf6, 2, DW210X_READ_MSG); +- msg[0].buf[0] = buf6[0]; +- msg[0].buf[1] = buf6[1]; +- break; +- case(DW2102_VOLTAGE_CTRL): +- buf6[0] = 0x30; +- buf6[1] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- buf6, 2, DW210X_WRITE_MSG); +- break; +- } +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (num) { +- case 2: { +- /* read */ +- /* first write first register number */ +- u8 ibuf[msg[1].len + 2], obuf[3]; +- obuf[0] = msg[0].addr << 1; +- obuf[1] = msg[0].len; +- obuf[2] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[0].len + 2, DW210X_WRITE_MSG); +- /* second read registers */ +- ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, +- ibuf, msg[1].len + 2, DW210X_READ_MSG); +- memcpy(msg[1].buf, ibuf + 2, msg[1].len); +- +- break; +- } +- case 1: +- switch (msg[0].addr) { +- case 0x68: { +- /* write to register */ +- u8 obuf[msg[0].len + 2]; +- obuf[0] = msg[0].addr << 1; +- obuf[1] = msg[0].len; +- memcpy(obuf + 2, msg[0].buf, msg[0].len); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[0].len + 2, DW210X_WRITE_MSG); +- break; +- } +- case 0x61: { +- /* write to tuner */ +- u8 obuf[msg[0].len + 2]; +- obuf[0] = msg[0].addr << 1; +- obuf[1] = msg[0].len; +- memcpy(obuf + 2, msg[0].buf, msg[0].len); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[0].len + 2, DW210X_WRITE_MSG); +- break; +- } +- case(DW2102_RC_QUERY): { +- u8 ibuf[2]; +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- ibuf, 2, DW210X_READ_MSG); +- memcpy(msg[0].buf, ibuf , 2); +- break; +- } +- case(DW2102_VOLTAGE_CTRL): { +- u8 obuf[2]; +- obuf[0] = 0x30; +- obuf[1] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- obuf, 2, DW210X_WRITE_MSG); +- break; +- } +- } +- +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0; +- int len, i, j; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (j = 0; j < num; j++) { +- switch (msg[j].addr) { +- case(DW2102_RC_QUERY): { +- u8 ibuf[2]; +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- ibuf, 2, DW210X_READ_MSG); +- memcpy(msg[j].buf, ibuf , 2); +- break; +- } +- case(DW2102_VOLTAGE_CTRL): { +- u8 obuf[2]; +- obuf[0] = 0x30; +- obuf[1] = msg[j].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, +- obuf, 2, DW210X_WRITE_MSG); +- break; +- } +- /*case 0x55: cx24116 +- case 0x6a: stv0903 +- case 0x68: ds3000, stv0903 +- case 0x60: ts2020, stv6110, stb6100 */ +- default: { +- if (msg[j].flags == I2C_M_RD) { +- /* read registers */ +- u8 ibuf[msg[j].len + 2]; +- ret = dw210x_op_rw(d->udev, 0xc3, +- (msg[j].addr << 1) + 1, 0, +- ibuf, msg[j].len + 2, +- DW210X_READ_MSG); +- memcpy(msg[j].buf, ibuf + 2, msg[j].len); +- mdelay(10); +- } else if (((msg[j].buf[0] == 0xb0) && +- (msg[j].addr == 0x68)) || +- ((msg[j].buf[0] == 0xf7) && +- (msg[j].addr == 0x55))) { +- /* write firmware */ +- u8 obuf[19]; +- obuf[0] = msg[j].addr << 1; +- obuf[1] = (msg[j].len > 15 ? 17 : msg[j].len); +- obuf[2] = msg[j].buf[0]; +- len = msg[j].len - 1; +- i = 1; +- do { +- memcpy(obuf + 3, msg[j].buf + i, +- (len > 16 ? 16 : len)); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, (len > 16 ? 16 : len) + 3, +- DW210X_WRITE_MSG); +- i += 16; +- len -= 16; +- } while (len > 0); +- } else { +- /* write registers */ +- u8 obuf[msg[j].len + 2]; +- obuf[0] = msg[j].addr << 1; +- obuf[1] = msg[j].len; +- memcpy(obuf + 2, msg[j].buf, msg[j].len); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[j].len + 2, +- DW210X_WRITE_MSG); +- } +- break; +- } +- } +- +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int ret = 0, i; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (num) { +- case 2: { +- /* read */ +- /* first write first register number */ +- u8 ibuf[msg[1].len + 2], obuf[3]; +- obuf[0] = msg[0].addr << 1; +- obuf[1] = msg[0].len; +- obuf[2] = msg[0].buf[0]; +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[0].len + 2, DW210X_WRITE_MSG); +- /* second read registers */ +- ret = dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, +- ibuf, msg[1].len + 2, DW210X_READ_MSG); +- memcpy(msg[1].buf, ibuf + 2, msg[1].len); +- +- break; +- } +- case 1: +- switch (msg[0].addr) { +- case 0x60: +- case 0x0c: { +- /* write to register */ +- u8 obuf[msg[0].len + 2]; +- obuf[0] = msg[0].addr << 1; +- obuf[1] = msg[0].len; +- memcpy(obuf + 2, msg[0].buf, msg[0].len); +- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, +- obuf, msg[0].len + 2, DW210X_WRITE_MSG); +- break; +- } +- case(DW2102_RC_QUERY): { +- u8 ibuf[2]; +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- ibuf, 2, DW210X_READ_MSG); +- memcpy(msg[0].buf, ibuf , 2); +- break; +- } +- } +- +- break; +- } +- +- for (i = 0; i < num; i++) { +- deb_xfer("%02x:%02x: %s ", i, msg[i].addr, +- msg[i].flags == 0 ? ">>>" : "<<<"); +- debug_dump(msg[i].buf, msg[i].len, deb_xfer); +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct usb_device *udev; +- int ret = 0; +- int len, i, j; +- +- if (!d) +- return -ENODEV; +- udev = d->udev; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (j = 0; j < num; j++) { +- switch (msg[j].addr) { +- case (DW2102_RC_QUERY): { +- u8 ibuf[5]; +- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, +- ibuf, 5, DW210X_READ_MSG); +- memcpy(msg[j].buf, ibuf + 3, 2); +- break; +- } +- case (DW2102_VOLTAGE_CTRL): { +- u8 obuf[2]; +- +- obuf[0] = 1; +- obuf[1] = msg[j].buf[1];/* off-on */ +- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, +- obuf, 2, DW210X_WRITE_MSG); +- obuf[0] = 3; +- obuf[1] = msg[j].buf[0];/* 13v-18v */ +- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, +- obuf, 2, DW210X_WRITE_MSG); +- break; +- } +- case (DW2102_LED_CTRL): { +- u8 obuf[2]; +- +- obuf[0] = 5; +- obuf[1] = msg[j].buf[0]; +- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, +- obuf, 2, DW210X_WRITE_MSG); +- break; +- } +- /*case 0x55: cx24116 +- case 0x6a: stv0903 +- case 0x68: ds3000, stv0903 +- case 0x60: ts2020, stv6110, stb6100 +- case 0xa0: eeprom */ +- default: { +- if (msg[j].flags == I2C_M_RD) { +- /* read registers */ +- u8 ibuf[msg[j].len]; +- ret = dw210x_op_rw(d->udev, 0x91, 0, 0, +- ibuf, msg[j].len, +- DW210X_READ_MSG); +- memcpy(msg[j].buf, ibuf, msg[j].len); +- break; +- } else if ((msg[j].buf[0] == 0xb0) && +- (msg[j].addr == 0x68)) { +- /* write firmware */ +- u8 obuf[19]; +- obuf[0] = (msg[j].len > 16 ? +- 18 : msg[j].len + 1); +- obuf[1] = msg[j].addr << 1; +- obuf[2] = msg[j].buf[0]; +- len = msg[j].len - 1; +- i = 1; +- do { +- memcpy(obuf + 3, msg[j].buf + i, +- (len > 16 ? 16 : len)); +- ret = dw210x_op_rw(d->udev, 0x80, 0, 0, +- obuf, (len > 16 ? 16 : len) + 3, +- DW210X_WRITE_MSG); +- i += 16; +- len -= 16; +- } while (len > 0); +- } else if (j < (num - 1)) { +- /* write register addr before read */ +- u8 obuf[msg[j].len + 2]; +- obuf[0] = msg[j + 1].len; +- obuf[1] = (msg[j].addr << 1); +- memcpy(obuf + 2, msg[j].buf, msg[j].len); +- ret = dw210x_op_rw(d->udev, +- udev->descriptor.idProduct == +- 0x7500 ? 0x92 : 0x90, 0, 0, +- obuf, msg[j].len + 2, +- DW210X_WRITE_MSG); +- break; +- } else { +- /* write registers */ +- u8 obuf[msg[j].len + 2]; +- obuf[0] = msg[j].len + 1; +- obuf[1] = (msg[j].addr << 1); +- memcpy(obuf + 2, msg[j].buf, msg[j].len); +- ret = dw210x_op_rw(d->udev, 0x80, 0, 0, +- obuf, msg[j].len + 2, +- DW210X_WRITE_MSG); +- break; +- } +- break; +- } +- } +- } +- +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static int su3000_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- u8 obuf[0x40], ibuf[0x40]; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (num) { +- case 1: +- switch (msg[0].addr) { +- case SU3000_STREAM_CTRL: +- obuf[0] = msg[0].buf[0] + 0x36; +- obuf[1] = 3; +- obuf[2] = 0; +- if (dvb_usb_generic_rw(d, obuf, 3, ibuf, 0, 0) < 0) +- err("i2c transfer failed."); +- break; +- case DW2102_RC_QUERY: +- obuf[0] = 0x10; +- if (dvb_usb_generic_rw(d, obuf, 1, ibuf, 2, 0) < 0) +- err("i2c transfer failed."); +- msg[0].buf[1] = ibuf[0]; +- msg[0].buf[0] = ibuf[1]; +- break; +- default: +- /* always i2c write*/ +- obuf[0] = 0x08; +- obuf[1] = msg[0].addr; +- obuf[2] = msg[0].len; +- +- memcpy(&obuf[3], msg[0].buf, msg[0].len); +- +- if (dvb_usb_generic_rw(d, obuf, msg[0].len + 3, +- ibuf, 1, 0) < 0) +- err("i2c transfer failed."); +- +- } +- break; +- case 2: +- /* always i2c read */ +- obuf[0] = 0x09; +- obuf[1] = msg[0].len; +- obuf[2] = msg[1].len; +- obuf[3] = msg[0].addr; +- memcpy(&obuf[4], msg[0].buf, msg[0].len); +- +- if (dvb_usb_generic_rw(d, obuf, msg[0].len + 4, +- ibuf, msg[1].len + 1, 0) < 0) +- err("i2c transfer failed."); +- +- memcpy(msg[1].buf, &ibuf[1], msg[1].len); +- break; +- default: +- warn("more than 2 i2c messages at a time is not handled yet."); +- break; +- } +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static u32 dw210x_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dw2102_i2c_algo = { +- .master_xfer = dw2102_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm dw2102_serit_i2c_algo = { +- .master_xfer = dw2102_serit_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm dw2102_earda_i2c_algo = { +- .master_xfer = dw2102_earda_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm dw2104_i2c_algo = { +- .master_xfer = dw2104_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm dw3101_i2c_algo = { +- .master_xfer = dw3101_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm s6x0_i2c_algo = { +- .master_xfer = s6x0_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static struct i2c_algorithm su3000_i2c_algo = { +- .master_xfer = su3000_i2c_transfer, +- .functionality = dw210x_i2c_func, +-}; +- +-static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +-{ +- int i; +- u8 ibuf[] = {0, 0}; +- u8 eeprom[256], eepromline[16]; +- +- for (i = 0; i < 256; i++) { +- if (dw210x_op_rw(d->udev, 0xb6, 0xa0 , i, ibuf, 2, DW210X_READ_MSG) < 0) { +- err("read eeprom failed."); +- return -1; +- } else { +- eepromline[i%16] = ibuf[0]; +- eeprom[i] = ibuf[0]; +- } +- if ((i % 16) == 15) { +- deb_xfer("%02x: ", i - 15); +- debug_dump(eepromline, 16, deb_xfer); +- } +- } +- +- memcpy(mac, eeprom + 8, 6); +- return 0; +-}; +- +-static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +-{ +- int i, ret; +- u8 ibuf[] = { 0 }, obuf[] = { 0 }; +- u8 eeprom[256], eepromline[16]; +- struct i2c_msg msg[] = { +- { +- .addr = 0xa0 >> 1, +- .flags = 0, +- .buf = obuf, +- .len = 1, +- }, { +- .addr = 0xa0 >> 1, +- .flags = I2C_M_RD, +- .buf = ibuf, +- .len = 1, +- } +- }; +- +- for (i = 0; i < 256; i++) { +- obuf[0] = i; +- ret = s6x0_i2c_transfer(&d->i2c_adap, msg, 2); +- if (ret != 2) { +- err("read eeprom failed."); +- return -1; +- } else { +- eepromline[i % 16] = ibuf[0]; +- eeprom[i] = ibuf[0]; +- } +- +- if ((i % 16) == 15) { +- deb_xfer("%02x: ", i - 15); +- debug_dump(eepromline, 16, deb_xfer); +- } +- } +- +- memcpy(mac, eeprom + 16, 6); +- return 0; +-}; +- +-static int su3000_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- static u8 command_start[] = {0x00}; +- static u8 command_stop[] = {0x01}; +- struct i2c_msg msg = { +- .addr = SU3000_STREAM_CTRL, +- .flags = 0, +- .buf = onoff ? command_start : command_stop, +- .len = 1 +- }; +- +- i2c_transfer(&adap->dev->i2c_adap, &msg, 1); +- +- return 0; +-} +- +-static int su3000_power_ctrl(struct dvb_usb_device *d, int i) +-{ +- struct su3000_state *state = (struct su3000_state *)d->priv; +- u8 obuf[] = {0xde, 0}; +- +- info("%s: %d, initialized %d\n", __func__, i, state->initialized); +- +- if (i && !state->initialized) { +- state->initialized = 1; +- /* reset board */ +- dvb_usb_generic_rw(d, obuf, 2, NULL, 0, 0); +- } +- +- return 0; +-} +- +-static int su3000_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +-{ +- int i; +- u8 obuf[] = { 0x1f, 0xf0 }; +- u8 ibuf[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = 0x51, +- .flags = 0, +- .buf = obuf, +- .len = 2, +- }, { +- .addr = 0x51, +- .flags = I2C_M_RD, +- .buf = ibuf, +- .len = 1, +- +- } +- }; +- +- for (i = 0; i < 6; i++) { +- obuf[1] = 0xf0 + i; +- if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) +- break; +- else +- mac[i] = ibuf[0]; +- +- debug_dump(mac, 6, printk); +- } +- +- return 0; +-} +- +-static int su3000_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- info("%s\n", __func__); +- +- *cold = 0; +- return 0; +-} +- +-static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- static u8 command_13v[] = {0x00, 0x01}; +- static u8 command_18v[] = {0x01, 0x01}; +- static u8 command_off[] = {0x00, 0x00}; +- struct i2c_msg msg = { +- .addr = DW2102_VOLTAGE_CTRL, +- .flags = 0, +- .buf = command_off, +- .len = 2, +- }; +- +- struct dvb_usb_adapter *udev_adap = +- (struct dvb_usb_adapter *)(fe->dvb->priv); +- if (voltage == SEC_VOLTAGE_18) +- msg.buf = command_18v; +- else if (voltage == SEC_VOLTAGE_13) +- msg.buf = command_13v; +- +- i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); +- +- return 0; +-} +- +-static int s660_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct dvb_usb_adapter *d = +- (struct dvb_usb_adapter *)(fe->dvb->priv); +- struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; +- +- dw210x_set_voltage(fe, voltage); +- if (st->old_set_voltage) +- st->old_set_voltage(fe, voltage); +- +- return 0; +-} +- +-static void dw210x_led_ctrl(struct dvb_frontend *fe, int offon) +-{ +- static u8 led_off[] = { 0 }; +- static u8 led_on[] = { 1 }; +- struct i2c_msg msg = { +- .addr = DW2102_LED_CTRL, +- .flags = 0, +- .buf = led_off, +- .len = 1 +- }; +- struct dvb_usb_adapter *udev_adap = +- (struct dvb_usb_adapter *)(fe->dvb->priv); +- +- if (offon) +- msg.buf = led_on; +- i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); +-} +- +-static struct stv0299_config sharp_z0194a_config = { +- .demod_address = 0x68, +- .inittab = sharp_z0194a_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 100, +- .set_symbol_rate = sharp_z0194a_set_symbol_rate, +-}; +- +-static struct cx24116_config dw2104_config = { +- .demod_address = 0x55, +- .mpg_clk_pos_pol = 0x01, +-}; +- +-static struct si21xx_config serit_sp1511lhb_config = { +- .demod_address = 0x68, +- .min_delay_ms = 100, +- +-}; +- +-static struct tda10023_config dw3101_tda10023_config = { +- .demod_address = 0x0c, +- .invert = 1, +-}; +- +-static struct mt312_config zl313_config = { +- .demod_address = 0x0e, +-}; +- +-static struct ds3000_config dw2104_ds3000_config = { +- .demod_address = 0x68, +-}; +- +-static struct stv0900_config dw2104a_stv0900_config = { +- .demod_address = 0x6a, +- .demod_mode = 0, +- .xtal = 27000000, +- .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ +- .diseqc_mode = 2,/* 2/3 PWM */ +- .tun1_maddress = 0,/* 0x60 */ +- .tun1_adc = 0,/* 2 Vpp */ +- .path1_mode = 3, +-}; +- +-static struct stb6100_config dw2104a_stb6100_config = { +- .tuner_address = 0x60, +- .refclock = 27000000, +-}; +- +-static struct stv0900_config dw2104_stv0900_config = { +- .demod_address = 0x68, +- .demod_mode = 0, +- .xtal = 8000000, +- .clkmode = 3, +- .diseqc_mode = 2, +- .tun1_maddress = 0, +- .tun1_adc = 1,/* 1 Vpp */ +- .path1_mode = 3, +-}; +- +-static struct stv6110_config dw2104_stv6110_config = { +- .i2c_address = 0x60, +- .mclk = 16000000, +- .clk_div = 1, +-}; +- +-static struct stv0900_config prof_7500_stv0900_config = { +- .demod_address = 0x6a, +- .demod_mode = 0, +- .xtal = 27000000, +- .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ +- .diseqc_mode = 2,/* 2/3 PWM */ +- .tun1_maddress = 0,/* 0x60 */ +- .tun1_adc = 0,/* 2 Vpp */ +- .path1_mode = 3, +- .tun1_type = 3, +- .set_lock_led = dw210x_led_ctrl, +-}; +- +-static struct ds3000_config su3000_ds3000_config = { +- .demod_address = 0x68, +- .ci_mode = 1, +-}; +- +-static int dw2104_frontend_attach(struct dvb_usb_adapter *d) +-{ +- struct dvb_tuner_ops *tuner_ops = NULL; +- +- if (demod_probe & 4) { +- d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104a_stv0900_config, +- &d->dev->i2c_adap, 0); +- if (d->fe_adap[0].fe != NULL) { +- if (dvb_attach(stb6100_attach, d->fe_adap[0].fe, +- &dw2104a_stb6100_config, +- &d->dev->i2c_adap)) { +- tuner_ops = &d->fe_adap[0].fe->ops.tuner_ops; +- tuner_ops->set_frequency = stb6100_set_freq; +- tuner_ops->get_frequency = stb6100_get_freq; +- tuner_ops->set_bandwidth = stb6100_set_bandw; +- tuner_ops->get_bandwidth = stb6100_get_bandw; +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached STV0900+STB6100!\n"); +- return 0; +- } +- } +- } +- +- if (demod_probe & 2) { +- d->fe_adap[0].fe = dvb_attach(stv0900_attach, &dw2104_stv0900_config, +- &d->dev->i2c_adap, 0); +- if (d->fe_adap[0].fe != NULL) { +- if (dvb_attach(stv6110_attach, d->fe_adap[0].fe, +- &dw2104_stv6110_config, +- &d->dev->i2c_adap)) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached STV0900+STV6110A!\n"); +- return 0; +- } +- } +- } +- +- if (demod_probe & 1) { +- d->fe_adap[0].fe = dvb_attach(cx24116_attach, &dw2104_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached cx24116!\n"); +- return 0; +- } +- } +- +- d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached DS3000!\n"); +- return 0; +- } +- +- return -EIO; +-} +- +-static struct dvb_usb_device_properties dw2102_properties; +-static struct dvb_usb_device_properties dw2104_properties; +-static struct dvb_usb_device_properties s6x0_properties; +- +-static int dw2102_frontend_attach(struct dvb_usb_adapter *d) +-{ +- if (dw2102_properties.i2c_algo == &dw2102_serit_i2c_algo) { +- /*dw2102_properties.adapter->tuner_attach = NULL;*/ +- d->fe_adap[0].fe = dvb_attach(si21xx_attach, &serit_sp1511lhb_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached si21xx!\n"); +- return 0; +- } +- } +- +- if (dw2102_properties.i2c_algo == &dw2102_earda_i2c_algo) { +- d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- if (dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, +- &d->dev->i2c_adap)) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached stv0288!\n"); +- return 0; +- } +- } +- } +- +- if (dw2102_properties.i2c_algo == &dw2102_i2c_algo) { +- /*dw2102_properties.adapter->tuner_attach = dw2102_tuner_attach;*/ +- d->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194a_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached stv0299!\n"); +- return 0; +- } +- } +- return -EIO; +-} +- +-static int dw3101_frontend_attach(struct dvb_usb_adapter *d) +-{ +- d->fe_adap[0].fe = dvb_attach(tda10023_attach, &dw3101_tda10023_config, +- &d->dev->i2c_adap, 0x48); +- if (d->fe_adap[0].fe != NULL) { +- info("Attached tda10023!\n"); +- return 0; +- } +- return -EIO; +-} +- +-static int zl100313_frontend_attach(struct dvb_usb_adapter *d) +-{ +- d->fe_adap[0].fe = dvb_attach(mt312_attach, &zl313_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe != NULL) { +- if (dvb_attach(zl10039_attach, d->fe_adap[0].fe, 0x60, +- &d->dev->i2c_adap)) { +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- info("Attached zl100313+zl10039!\n"); +- return 0; +- } +- } +- +- return -EIO; +-} +- +-static int stv0288_frontend_attach(struct dvb_usb_adapter *d) +-{ +- u8 obuf[] = {7, 1}; +- +- d->fe_adap[0].fe = dvb_attach(stv0288_attach, &earda_config, +- &d->dev->i2c_adap); +- +- if (d->fe_adap[0].fe == NULL) +- return -EIO; +- +- if (NULL == dvb_attach(stb6000_attach, d->fe_adap[0].fe, 0x61, &d->dev->i2c_adap)) +- return -EIO; +- +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- +- dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); +- +- info("Attached stv0288+stb6000!\n"); +- +- return 0; +- +-} +- +-static int ds3000_frontend_attach(struct dvb_usb_adapter *d) +-{ +- struct s6x0_state *st = (struct s6x0_state *)d->dev->priv; +- u8 obuf[] = {7, 1}; +- +- d->fe_adap[0].fe = dvb_attach(ds3000_attach, &dw2104_ds3000_config, +- &d->dev->i2c_adap); +- +- if (d->fe_adap[0].fe == NULL) +- return -EIO; +- +- st->old_set_voltage = d->fe_adap[0].fe->ops.set_voltage; +- d->fe_adap[0].fe->ops.set_voltage = s660_set_voltage; +- +- dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); +- +- info("Attached ds3000+ds2020!\n"); +- +- return 0; +-} +- +-static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) +-{ +- u8 obuf[] = {7, 1}; +- +- d->fe_adap[0].fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, +- &d->dev->i2c_adap, 0); +- if (d->fe_adap[0].fe == NULL) +- return -EIO; +- +- d->fe_adap[0].fe->ops.set_voltage = dw210x_set_voltage; +- +- dw210x_op_rw(d->dev->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); +- +- info("Attached STV0900+STB6100A!\n"); +- +- return 0; +-} +- +-static int su3000_frontend_attach(struct dvb_usb_adapter *d) +-{ +- u8 obuf[3] = { 0xe, 0x80, 0 }; +- u8 ibuf[] = { 0 }; +- +- if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) +- err("command 0x0e transfer failed."); +- +- obuf[0] = 0xe; +- obuf[1] = 0x83; +- obuf[2] = 0; +- +- if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) +- err("command 0x0e transfer failed."); +- +- obuf[0] = 0xe; +- obuf[1] = 0x83; +- obuf[2] = 1; +- +- if (dvb_usb_generic_rw(d->dev, obuf, 3, ibuf, 1, 0) < 0) +- err("command 0x0e transfer failed."); +- +- obuf[0] = 0x51; +- +- if (dvb_usb_generic_rw(d->dev, obuf, 1, ibuf, 1, 0) < 0) +- err("command 0x51 transfer failed."); +- +- d->fe_adap[0].fe = dvb_attach(ds3000_attach, &su3000_ds3000_config, +- &d->dev->i2c_adap); +- if (d->fe_adap[0].fe == NULL) +- return -EIO; +- +- info("Attached DS3000!\n"); +- +- return 0; +-} +- +-static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, +- &adap->dev->i2c_adap, DVB_PLL_OPERA1); +- return 0; +-} +- +-static int dw3101_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x60, +- &adap->dev->i2c_adap, DVB_PLL_TUA6034); +- +- return 0; +-} +- +-static struct rc_map_table rc_map_dw210x_table[] = { +- { 0xf80a, KEY_POWER2 }, /*power*/ +- { 0xf80c, KEY_MUTE }, /*mute*/ +- { 0xf811, KEY_1 }, +- { 0xf812, KEY_2 }, +- { 0xf813, KEY_3 }, +- { 0xf814, KEY_4 }, +- { 0xf815, KEY_5 }, +- { 0xf816, KEY_6 }, +- { 0xf817, KEY_7 }, +- { 0xf818, KEY_8 }, +- { 0xf819, KEY_9 }, +- { 0xf810, KEY_0 }, +- { 0xf81c, KEY_CHANNELUP }, /*ch+*/ +- { 0xf80f, KEY_CHANNELDOWN }, /*ch-*/ +- { 0xf81a, KEY_VOLUMEUP }, /*vol+*/ +- { 0xf80e, KEY_VOLUMEDOWN }, /*vol-*/ +- { 0xf804, KEY_RECORD }, /*rec*/ +- { 0xf809, KEY_FAVORITES }, /*fav*/ +- { 0xf808, KEY_REWIND }, /*rewind*/ +- { 0xf807, KEY_FASTFORWARD }, /*fast*/ +- { 0xf80b, KEY_PAUSE }, /*pause*/ +- { 0xf802, KEY_ESC }, /*cancel*/ +- { 0xf803, KEY_TAB }, /*tab*/ +- { 0xf800, KEY_UP }, /*up*/ +- { 0xf81f, KEY_OK }, /*ok*/ +- { 0xf801, KEY_DOWN }, /*down*/ +- { 0xf805, KEY_CAMERA }, /*cap*/ +- { 0xf806, KEY_STOP }, /*stop*/ +- { 0xf840, KEY_ZOOM }, /*full*/ +- { 0xf81e, KEY_TV }, /*tvmode*/ +- { 0xf81b, KEY_LAST }, /*recall*/ +-}; +- +-static struct rc_map_table rc_map_tevii_table[] = { +- { 0xf80a, KEY_POWER }, +- { 0xf80c, KEY_MUTE }, +- { 0xf811, KEY_1 }, +- { 0xf812, KEY_2 }, +- { 0xf813, KEY_3 }, +- { 0xf814, KEY_4 }, +- { 0xf815, KEY_5 }, +- { 0xf816, KEY_6 }, +- { 0xf817, KEY_7 }, +- { 0xf818, KEY_8 }, +- { 0xf819, KEY_9 }, +- { 0xf810, KEY_0 }, +- { 0xf81c, KEY_MENU }, +- { 0xf80f, KEY_VOLUMEDOWN }, +- { 0xf81a, KEY_LAST }, +- { 0xf80e, KEY_OPEN }, +- { 0xf804, KEY_RECORD }, +- { 0xf809, KEY_VOLUMEUP }, +- { 0xf808, KEY_CHANNELUP }, +- { 0xf807, KEY_PVR }, +- { 0xf80b, KEY_TIME }, +- { 0xf802, KEY_RIGHT }, +- { 0xf803, KEY_LEFT }, +- { 0xf800, KEY_UP }, +- { 0xf81f, KEY_OK }, +- { 0xf801, KEY_DOWN }, +- { 0xf805, KEY_TUNER }, +- { 0xf806, KEY_CHANNELDOWN }, +- { 0xf840, KEY_PLAYPAUSE }, +- { 0xf81e, KEY_REWIND }, +- { 0xf81b, KEY_FAVORITES }, +- { 0xf81d, KEY_BACK }, +- { 0xf84d, KEY_FASTFORWARD }, +- { 0xf844, KEY_EPG }, +- { 0xf84c, KEY_INFO }, +- { 0xf841, KEY_AB }, +- { 0xf843, KEY_AUDIO }, +- { 0xf845, KEY_SUBTITLE }, +- { 0xf84a, KEY_LIST }, +- { 0xf846, KEY_F1 }, +- { 0xf847, KEY_F2 }, +- { 0xf85e, KEY_F3 }, +- { 0xf85c, KEY_F4 }, +- { 0xf852, KEY_F5 }, +- { 0xf85a, KEY_F6 }, +- { 0xf856, KEY_MODE }, +- { 0xf858, KEY_SWITCHVIDEOMODE }, +-}; +- +-static struct rc_map_table rc_map_tbs_table[] = { +- { 0xf884, KEY_POWER }, +- { 0xf894, KEY_MUTE }, +- { 0xf887, KEY_1 }, +- { 0xf886, KEY_2 }, +- { 0xf885, KEY_3 }, +- { 0xf88b, KEY_4 }, +- { 0xf88a, KEY_5 }, +- { 0xf889, KEY_6 }, +- { 0xf88f, KEY_7 }, +- { 0xf88e, KEY_8 }, +- { 0xf88d, KEY_9 }, +- { 0xf892, KEY_0 }, +- { 0xf896, KEY_CHANNELUP }, +- { 0xf891, KEY_CHANNELDOWN }, +- { 0xf893, KEY_VOLUMEUP }, +- { 0xf88c, KEY_VOLUMEDOWN }, +- { 0xf883, KEY_RECORD }, +- { 0xf898, KEY_PAUSE }, +- { 0xf899, KEY_OK }, +- { 0xf89a, KEY_SHUFFLE }, +- { 0xf881, KEY_UP }, +- { 0xf890, KEY_LEFT }, +- { 0xf882, KEY_RIGHT }, +- { 0xf888, KEY_DOWN }, +- { 0xf895, KEY_FAVORITES }, +- { 0xf897, KEY_SUBTITLE }, +- { 0xf89d, KEY_ZOOM }, +- { 0xf89f, KEY_EXIT }, +- { 0xf89e, KEY_MENU }, +- { 0xf89c, KEY_EPG }, +- { 0xf880, KEY_PREVIOUS }, +- { 0xf89b, KEY_MODE } +-}; +- +-static struct rc_map_table rc_map_su3000_table[] = { +- { 0x25, KEY_POWER }, /* right-bottom Red */ +- { 0x0a, KEY_MUTE }, /* -/-- */ +- { 0x01, KEY_1 }, +- { 0x02, KEY_2 }, +- { 0x03, KEY_3 }, +- { 0x04, KEY_4 }, +- { 0x05, KEY_5 }, +- { 0x06, KEY_6 }, +- { 0x07, KEY_7 }, +- { 0x08, KEY_8 }, +- { 0x09, KEY_9 }, +- { 0x00, KEY_0 }, +- { 0x20, KEY_UP }, /* CH+ */ +- { 0x21, KEY_DOWN }, /* CH+ */ +- { 0x12, KEY_VOLUMEUP }, /* Brightness Up */ +- { 0x13, KEY_VOLUMEDOWN },/* Brightness Down */ +- { 0x1f, KEY_RECORD }, +- { 0x17, KEY_PLAY }, +- { 0x16, KEY_PAUSE }, +- { 0x0b, KEY_STOP }, +- { 0x27, KEY_FASTFORWARD },/* >> */ +- { 0x26, KEY_REWIND }, /* << */ +- { 0x0d, KEY_OK }, /* Mute */ +- { 0x11, KEY_LEFT }, /* VOL- */ +- { 0x10, KEY_RIGHT }, /* VOL+ */ +- { 0x29, KEY_BACK }, /* button under 9 */ +- { 0x2c, KEY_MENU }, /* TTX */ +- { 0x2b, KEY_EPG }, /* EPG */ +- { 0x1e, KEY_RED }, /* OSD */ +- { 0x0e, KEY_GREEN }, /* Window */ +- { 0x2d, KEY_YELLOW }, /* button under << */ +- { 0x0f, KEY_BLUE }, /* bottom yellow button */ +- { 0x14, KEY_AUDIO }, /* Snapshot */ +- { 0x38, KEY_TV }, /* TV/Radio */ +- { 0x0c, KEY_ESC } /* upper Red button */ +-}; +- +-static struct rc_map_dvb_usb_table_table keys_tables[] = { +- { rc_map_dw210x_table, ARRAY_SIZE(rc_map_dw210x_table) }, +- { rc_map_tevii_table, ARRAY_SIZE(rc_map_tevii_table) }, +- { rc_map_tbs_table, ARRAY_SIZE(rc_map_tbs_table) }, +- { rc_map_su3000_table, ARRAY_SIZE(rc_map_su3000_table) }, +-}; +- +-static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- struct rc_map_table *keymap = d->props.rc.legacy.rc_map_table; +- int keymap_size = d->props.rc.legacy.rc_map_size; +- u8 key[2]; +- struct i2c_msg msg = { +- .addr = DW2102_RC_QUERY, +- .flags = I2C_M_RD, +- .buf = key, +- .len = 2 +- }; +- int i; +- /* override keymap */ +- if ((ir_keymap > 0) && (ir_keymap <= ARRAY_SIZE(keys_tables))) { +- keymap = keys_tables[ir_keymap - 1].rc_keys ; +- keymap_size = keys_tables[ir_keymap - 1].rc_keys_size; +- } else if (ir_keymap > ARRAY_SIZE(keys_tables)) +- return 0; /* none */ +- +- *state = REMOTE_NO_KEY_PRESSED; +- if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) { +- for (i = 0; i < keymap_size ; i++) { +- if (rc5_data(&keymap[i]) == msg.buf[0]) { +- *state = REMOTE_KEY_PRESSED; +- *event = keymap[i].keycode; +- break; +- } +- +- } +- +- if ((*state) == REMOTE_KEY_PRESSED) +- deb_rc("%s: found rc key: %x, %x, event: %x\n", +- __func__, key[0], key[1], (*event)); +- else if (key[0] != 0xff) +- deb_rc("%s: unknown rc key: %x, %x\n", +- __func__, key[0], key[1]); +- +- } +- +- return 0; +-} +- +-enum dw2102_table_entry { +- CYPRESS_DW2102, +- CYPRESS_DW2101, +- CYPRESS_DW2104, +- TEVII_S650, +- TERRATEC_CINERGY_S, +- CYPRESS_DW3101, +- TEVII_S630, +- PROF_1100, +- TEVII_S660, +- PROF_7500, +- GENIATECH_SU3000, +- TERRATEC_CINERGY_S2, +- TEVII_S480_1, +- TEVII_S480_2, +- X3M_SPC1400HD, +-}; +- +-static struct usb_device_id dw2102_table[] = { +- [CYPRESS_DW2102] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2102)}, +- [CYPRESS_DW2101] = {USB_DEVICE(USB_VID_CYPRESS, 0x2101)}, +- [CYPRESS_DW2104] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW2104)}, +- [TEVII_S650] = {USB_DEVICE(0x9022, USB_PID_TEVII_S650)}, +- [TERRATEC_CINERGY_S] = {USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)}, +- [CYPRESS_DW3101] = {USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)}, +- [TEVII_S630] = {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, +- [PROF_1100] = {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, +- [TEVII_S660] = {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, +- [PROF_7500] = {USB_DEVICE(0x3034, 0x7500)}, +- [GENIATECH_SU3000] = {USB_DEVICE(0x1f4d, 0x3000)}, +- [TERRATEC_CINERGY_S2] = {USB_DEVICE(USB_VID_TERRATEC, 0x00a8)}, +- [TEVII_S480_1] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_1)}, +- [TEVII_S480_2] = {USB_DEVICE(0x9022, USB_PID_TEVII_S480_2)}, +- [X3M_SPC1400HD] = {USB_DEVICE(0x1f4d, 0x3100)}, +- { } +-}; +- +-MODULE_DEVICE_TABLE(usb, dw2102_table); +- +-static int dw2102_load_firmware(struct usb_device *dev, +- const struct firmware *frmwr) +-{ +- u8 *b, *p; +- int ret = 0, i; +- u8 reset; +- u8 reset16[] = {0, 0, 0, 0, 0, 0, 0}; +- const struct firmware *fw; +- const char *fw_2101 = "dvb-usb-dw2101.fw"; +- +- switch (dev->descriptor.idProduct) { +- case 0x2101: +- ret = request_firmware(&fw, fw_2101, &dev->dev); +- if (ret != 0) { +- err(err_str, fw_2101); +- return ret; +- } +- break; +- default: +- fw = frmwr; +- break; +- } +- info("start downloading DW210X firmware"); +- p = kmalloc(fw->size, GFP_KERNEL); +- reset = 1; +- /*stop the CPU*/ +- dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, DW210X_WRITE_MSG); +- dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, DW210X_WRITE_MSG); +- +- if (p != NULL) { +- memcpy(p, fw->data, fw->size); +- for (i = 0; i < fw->size; i += 0x40) { +- b = (u8 *) p + i; +- if (dw210x_op_rw(dev, 0xa0, i, 0, b , 0x40, +- DW210X_WRITE_MSG) != 0x40) { +- err("error while transferring firmware"); +- ret = -EINVAL; +- break; +- } +- } +- /* restart the CPU */ +- reset = 0; +- if (ret || dw210x_op_rw(dev, 0xa0, 0x7f92, 0, &reset, 1, +- DW210X_WRITE_MSG) != 1) { +- err("could not restart the USB controller CPU."); +- ret = -EINVAL; +- } +- if (ret || dw210x_op_rw(dev, 0xa0, 0xe600, 0, &reset, 1, +- DW210X_WRITE_MSG) != 1) { +- err("could not restart the USB controller CPU."); +- ret = -EINVAL; +- } +- /* init registers */ +- switch (dev->descriptor.idProduct) { +- case USB_PID_TEVII_S650: +- dw2104_properties.rc.legacy.rc_map_table = rc_map_tevii_table; +- dw2104_properties.rc.legacy.rc_map_size = +- ARRAY_SIZE(rc_map_tevii_table); +- case USB_PID_DW2104: +- reset = 1; +- dw210x_op_rw(dev, 0xc4, 0x0000, 0, &reset, 1, +- DW210X_WRITE_MSG); +- /* break omitted intentionally */ +- case USB_PID_DW3101: +- reset = 0; +- dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, +- DW210X_WRITE_MSG); +- break; +- case USB_PID_CINERGY_S: +- case USB_PID_DW2102: +- dw210x_op_rw(dev, 0xbf, 0x0040, 0, &reset, 0, +- DW210X_WRITE_MSG); +- dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, +- DW210X_READ_MSG); +- /* check STV0299 frontend */ +- dw210x_op_rw(dev, 0xb5, 0, 0, &reset16[0], 2, +- DW210X_READ_MSG); +- if ((reset16[0] == 0xa1) || (reset16[0] == 0x80)) { +- dw2102_properties.i2c_algo = &dw2102_i2c_algo; +- dw2102_properties.adapter->fe[0].tuner_attach = &dw2102_tuner_attach; +- break; +- } else { +- /* check STV0288 frontend */ +- reset16[0] = 0xd0; +- reset16[1] = 1; +- reset16[2] = 0; +- dw210x_op_rw(dev, 0xc2, 0, 0, &reset16[0], 3, +- DW210X_WRITE_MSG); +- dw210x_op_rw(dev, 0xc3, 0xd1, 0, &reset16[0], 3, +- DW210X_READ_MSG); +- if (reset16[2] == 0x11) { +- dw2102_properties.i2c_algo = &dw2102_earda_i2c_algo; +- break; +- } +- } +- case 0x2101: +- dw210x_op_rw(dev, 0xbc, 0x0030, 0, &reset16[0], 2, +- DW210X_READ_MSG); +- dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, +- DW210X_READ_MSG); +- dw210x_op_rw(dev, 0xba, 0x0000, 0, &reset16[0], 7, +- DW210X_READ_MSG); +- dw210x_op_rw(dev, 0xb9, 0x0000, 0, &reset16[0], 2, +- DW210X_READ_MSG); +- break; +- } +- +- msleep(100); +- kfree(p); +- } +- return ret; +-} +- +-static struct dvb_usb_device_properties dw2102_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-dw2102.fw", +- .no_reconnect = 1, +- +- .i2c_algo = &dw2102_serit_i2c_algo, +- +- .rc.legacy = { +- .rc_map_table = rc_map_dw210x_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), +- .rc_interval = 150, +- .rc_query = dw2102_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x81, +- /* parameter for the MPEG2-data transfer */ +- .num_adapters = 1, +- .download_firmware = dw2102_load_firmware, +- .read_mac_address = dw210x_read_mac_address, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = dw2102_frontend_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .num_device_descs = 3, +- .devices = { +- {"DVBWorld DVB-S 2102 USB2.0", +- {&dw2102_table[CYPRESS_DW2102], NULL}, +- {NULL}, +- }, +- {"DVBWorld DVB-S 2101 USB2.0", +- {&dw2102_table[CYPRESS_DW2101], NULL}, +- {NULL}, +- }, +- {"TerraTec Cinergy S USB", +- {&dw2102_table[TERRATEC_CINERGY_S], NULL}, +- {NULL}, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties dw2104_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-dw2104.fw", +- .no_reconnect = 1, +- +- .i2c_algo = &dw2104_i2c_algo, +- .rc.legacy = { +- .rc_map_table = rc_map_dw210x_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), +- .rc_interval = 150, +- .rc_query = dw2102_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x81, +- /* parameter for the MPEG2-data transfer */ +- .num_adapters = 1, +- .download_firmware = dw2102_load_firmware, +- .read_mac_address = dw210x_read_mac_address, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = dw2104_frontend_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .num_device_descs = 2, +- .devices = { +- { "DVBWorld DW2104 USB2.0", +- {&dw2102_table[CYPRESS_DW2104], NULL}, +- {NULL}, +- }, +- { "TeVii S650 USB2.0", +- {&dw2102_table[TEVII_S650], NULL}, +- {NULL}, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties dw3101_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-dw3101.fw", +- .no_reconnect = 1, +- +- .i2c_algo = &dw3101_i2c_algo, +- .rc.legacy = { +- .rc_map_table = rc_map_dw210x_table, +- .rc_map_size = ARRAY_SIZE(rc_map_dw210x_table), +- .rc_interval = 150, +- .rc_query = dw2102_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x81, +- /* parameter for the MPEG2-data transfer */ +- .num_adapters = 1, +- .download_firmware = dw2102_load_firmware, +- .read_mac_address = dw210x_read_mac_address, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = dw3101_frontend_attach, +- .tuner_attach = dw3101_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .num_device_descs = 1, +- .devices = { +- { "DVBWorld DVB-C 3101 USB2.0", +- {&dw2102_table[CYPRESS_DW3101], NULL}, +- {NULL}, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties s6x0_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .size_of_priv = sizeof(struct s6x0_state), +- .firmware = "dvb-usb-s630.fw", +- .no_reconnect = 1, +- +- .i2c_algo = &s6x0_i2c_algo, +- .rc.legacy = { +- .rc_map_table = rc_map_tevii_table, +- .rc_map_size = ARRAY_SIZE(rc_map_tevii_table), +- .rc_interval = 150, +- .rc_query = dw2102_rc_query, +- }, +- +- .generic_bulk_ctrl_endpoint = 0x81, +- .num_adapters = 1, +- .download_firmware = dw2102_load_firmware, +- .read_mac_address = s6x0_read_mac_address, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = zl100313_frontend_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .num_device_descs = 1, +- .devices = { +- {"TeVii S630 USB", +- {&dw2102_table[TEVII_S630], NULL}, +- {NULL}, +- }, +- } +-}; +- +-struct dvb_usb_device_properties *p1100; +-static struct dvb_usb_device_description d1100 = { +- "Prof 1100 USB ", +- {&dw2102_table[PROF_1100], NULL}, +- {NULL}, +-}; +- +-struct dvb_usb_device_properties *s660; +-static struct dvb_usb_device_description d660 = { +- "TeVii S660 USB", +- {&dw2102_table[TEVII_S660], NULL}, +- {NULL}, +-}; +- +-static struct dvb_usb_device_description d480_1 = { +- "TeVii S480.1 USB", +- {&dw2102_table[TEVII_S480_1], NULL}, +- {NULL}, +-}; +- +-static struct dvb_usb_device_description d480_2 = { +- "TeVii S480.2 USB", +- {&dw2102_table[TEVII_S480_2], NULL}, +- {NULL}, +-}; +- +-struct dvb_usb_device_properties *p7500; +-static struct dvb_usb_device_description d7500 = { +- "Prof 7500 USB DVB-S2", +- {&dw2102_table[PROF_7500], NULL}, +- {NULL}, +-}; +- +-static struct dvb_usb_device_properties su3000_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .size_of_priv = sizeof(struct su3000_state), +- .power_ctrl = su3000_power_ctrl, +- .num_adapters = 1, +- .identify_state = su3000_identify_state, +- .i2c_algo = &su3000_i2c_algo, +- +- .rc.legacy = { +- .rc_map_table = rc_map_su3000_table, +- .rc_map_size = ARRAY_SIZE(rc_map_su3000_table), +- .rc_interval = 150, +- .rc_query = dw2102_rc_query, +- }, +- +- .read_mac_address = su3000_read_mac_address, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = su3000_streaming_ctrl, +- .frontend_attach = su3000_frontend_attach, +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- } +- }}, +- } +- }, +- .num_device_descs = 3, +- .devices = { +- { "SU3000HD DVB-S USB2.0", +- { &dw2102_table[GENIATECH_SU3000], NULL }, +- { NULL }, +- }, +- { "Terratec Cinergy S2 USB HD", +- { &dw2102_table[TERRATEC_CINERGY_S2], NULL }, +- { NULL }, +- }, +- { "X3M TV SPC1400HD PCI", +- { &dw2102_table[X3M_SPC1400HD], NULL }, +- { NULL }, +- }, +- } +-}; +- +-static int dw2102_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- p1100 = kmemdup(&s6x0_properties, +- sizeof(struct dvb_usb_device_properties), GFP_KERNEL); +- if (!p1100) +- return -ENOMEM; +- /* copy default structure */ +- /* fill only different fields */ +- p1100->firmware = "dvb-usb-p1100.fw"; +- p1100->devices[0] = d1100; +- p1100->rc.legacy.rc_map_table = rc_map_tbs_table; +- p1100->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); +- p1100->adapter->fe[0].frontend_attach = stv0288_frontend_attach; +- +- s660 = kmemdup(&s6x0_properties, +- sizeof(struct dvb_usb_device_properties), GFP_KERNEL); +- if (!s660) { +- kfree(p1100); +- return -ENOMEM; +- } +- s660->firmware = "dvb-usb-s660.fw"; +- s660->num_device_descs = 3; +- s660->devices[0] = d660; +- s660->devices[1] = d480_1; +- s660->devices[2] = d480_2; +- s660->adapter->fe[0].frontend_attach = ds3000_frontend_attach; +- +- p7500 = kmemdup(&s6x0_properties, +- sizeof(struct dvb_usb_device_properties), GFP_KERNEL); +- if (!p7500) { +- kfree(p1100); +- kfree(s660); +- return -ENOMEM; +- } +- p7500->firmware = "dvb-usb-p7500.fw"; +- p7500->devices[0] = d7500; +- p7500->rc.legacy.rc_map_table = rc_map_tbs_table; +- p7500->rc.legacy.rc_map_size = ARRAY_SIZE(rc_map_tbs_table); +- p7500->adapter->fe[0].frontend_attach = prof_7500_frontend_attach; +- +- if (0 == dvb_usb_device_init(intf, &dw2102_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &dw2104_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &dw3101_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &s6x0_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, p1100, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, s660, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, p7500, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &su3000_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- +- return -ENODEV; +-} +- +-static struct usb_driver dw2102_driver = { +- .name = "dw2102", +- .probe = dw2102_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = dw2102_table, +-}; +- +-module_usb_driver(dw2102_driver); +- +-MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); +-MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," +- " DVB-C 3101 USB2.0," +- " TeVii S600, S630, S650, S660, S480," +- " Prof 1100, 7500 USB2.0," +- " Geniatech SU3000 devices"); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/dw2102.h b/drivers/media/dvb/dvb-usb/dw2102.h +deleted file mode 100644 +index 5cd0b0e..0000000 +--- a/drivers/media/dvb/dvb-usb/dw2102.h ++++ /dev/null +@@ -1,9 +0,0 @@ +-#ifndef _DW2102_H_ +-#define _DW2102_H_ +- +-#define DVB_USB_LOG_PREFIX "dw2102" +-#include "dvb-usb.h" +- +-#define deb_xfer(args...) dprintk(dvb_usb_dw2102_debug, 0x02, args) +-#define deb_rc(args...) dprintk(dvb_usb_dw2102_debug, 0x04, args) +-#endif +diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c +deleted file mode 100644 +index b4989ba..0000000 +--- a/drivers/media/dvb/dvb-usb/ec168.c ++++ /dev/null +@@ -1,435 +0,0 @@ +-/* +- * E3C EC168 DVB USB driver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include "ec168.h" +-#include "ec100.h" +-#include "mxl5005s.h" +- +-/* debug */ +-static int dvb_usb_ec168_debug; +-module_param_named(debug, dvb_usb_ec168_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static struct ec100_config ec168_ec100_config; +- +-static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req) +-{ +- int ret; +- unsigned int pipe; +- u8 request, requesttype; +- u8 *buf; +- +- +- +- switch (req->cmd) { +- case DOWNLOAD_FIRMWARE: +- case GPIO: +- case WRITE_I2C: +- case STREAMING_CTRL: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); +- request = req->cmd; +- break; +- case READ_I2C: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); +- request = req->cmd; +- break; +- case GET_CONFIG: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); +- request = CONFIG; +- break; +- case SET_CONFIG: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); +- request = CONFIG; +- break; +- case WRITE_DEMOD: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); +- request = DEMOD_RW; +- break; +- case READ_DEMOD: +- requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); +- request = DEMOD_RW; +- break; +- default: +- err("unknown command:%02x", req->cmd); +- ret = -EPERM; +- goto error; +- } +- +- buf = kmalloc(req->size, GFP_KERNEL); +- if (!buf) { +- ret = -ENOMEM; +- goto error; +- } +- +- if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { +- /* write */ +- memcpy(buf, req->data, req->size); +- pipe = usb_sndctrlpipe(udev, 0); +- } else { +- /* read */ +- pipe = usb_rcvctrlpipe(udev, 0); +- } +- +- msleep(1); /* avoid I2C errors */ +- +- ret = usb_control_msg(udev, pipe, request, requesttype, req->value, +- req->index, buf, req->size, EC168_USB_TIMEOUT); +- +- ec168_debug_dump(request, requesttype, req->value, req->index, buf, +- req->size, deb_xfer); +- +- if (ret < 0) +- goto err_dealloc; +- else +- ret = 0; +- +- /* read request, copy returned data to return buf */ +- if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) +- memcpy(req->data, buf, req->size); +- +- kfree(buf); +- return ret; +- +-err_dealloc: +- kfree(buf); +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) +-{ +- return ec168_rw_udev(d->udev, req); +-} +- +-/* I2C */ +-static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct ec168_req req; +- int i = 0; +- int ret; +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- while (i < num) { +- if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { +- if (msg[i].addr == ec168_ec100_config.demod_address) { +- req.cmd = READ_DEMOD; +- req.value = 0; +- req.index = 0xff00 + msg[i].buf[0]; /* reg */ +- req.size = msg[i+1].len; /* bytes to read */ +- req.data = &msg[i+1].buf[0]; +- ret = ec168_ctrl_msg(d, &req); +- i += 2; +- } else { +- err("I2C read not implemented"); +- ret = -ENOSYS; +- i += 2; +- } +- } else { +- if (msg[i].addr == ec168_ec100_config.demod_address) { +- req.cmd = WRITE_DEMOD; +- req.value = msg[i].buf[1]; /* val */ +- req.index = 0xff00 + msg[i].buf[0]; /* reg */ +- req.size = 0; +- req.data = NULL; +- ret = ec168_ctrl_msg(d, &req); +- i += 1; +- } else { +- req.cmd = WRITE_I2C; +- req.value = msg[i].buf[0]; /* val */ +- req.index = 0x0100 + msg[i].addr; /* I2C addr */ +- req.size = msg[i].len-1; +- req.data = &msg[i].buf[1]; +- ret = ec168_ctrl_msg(d, &req); +- i += 1; +- } +- } +- if (ret) +- goto error; +- +- } +- ret = i; +- +-error: +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +- +-static u32 ec168_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm ec168_i2c_algo = { +- .master_xfer = ec168_i2c_xfer, +- .functionality = ec168_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static struct ec100_config ec168_ec100_config = { +- .demod_address = 0xff, /* not real address, demod is integrated */ +-}; +- +-static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- deb_info("%s:\n", __func__); +- adap->fe_adap[0].fe = dvb_attach(ec100_attach, &ec168_ec100_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -ENODEV; +- +- return 0; +-} +- +-static struct mxl5005s_config ec168_mxl5003s_config = { +- .i2c_address = 0xc6, +- .if_freq = IF_FREQ_4570000HZ, +- .xtal_freq = CRYSTAL_FREQ_16000000HZ, +- .agc_mode = MXL_SINGLE_AGC, +- .tracking_filter = MXL_TF_OFF, +- .rssi_enable = MXL_RSSI_ENABLE, +- .cap_select = MXL_CAP_SEL_ENABLE, +- .div_out = MXL_DIV_OUT_4, +- .clock_out = MXL_CLOCK_OUT_DISABLE, +- .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, +- .top = MXL5005S_TOP_25P2, +- .mod_mode = MXL_DIGITAL_MODE, +- .if_mode = MXL_ZERO_IF, +- .AgcMasterByte = 0x00, +-}; +- +-static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- deb_info("%s:\n", __func__); +- return dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; +-} +- +-static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL}; +- deb_info("%s: onoff:%d\n", __func__, onoff); +- if (onoff) +- req.index = 0x0102; +- return ec168_ctrl_msg(adap->dev, &req); +-} +- +-static int ec168_download_firmware(struct usb_device *udev, +- const struct firmware *fw) +-{ +- int i, len, packets, remainder, ret; +- u16 addr = 0x0000; /* firmware start address */ +- struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL}; +- deb_info("%s:\n", __func__); +- +- #define FW_PACKET_MAX_DATA 2048 +- packets = fw->size / FW_PACKET_MAX_DATA; +- remainder = fw->size % FW_PACKET_MAX_DATA; +- len = FW_PACKET_MAX_DATA; +- for (i = 0; i <= packets; i++) { +- if (i == packets) /* set size of the last packet */ +- len = remainder; +- +- req.size = len; +- req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA); +- req.index = addr; +- addr += FW_PACKET_MAX_DATA; +- +- ret = ec168_rw_udev(udev, &req); +- if (ret) { +- err("firmware download failed:%d packet:%d", ret, i); +- goto error; +- } +- } +- req.size = 0; +- +- /* set "warm"? */ +- req.cmd = SET_CONFIG; +- req.value = 0; +- req.index = 0x0001; +- ret = ec168_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- /* really needed - no idea what does */ +- req.cmd = GPIO; +- req.value = 0; +- req.index = 0x0206; +- ret = ec168_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- /* activate tuner I2C? */ +- req.cmd = WRITE_I2C; +- req.value = 0; +- req.index = 0x00c6; +- ret = ec168_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec168_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, int *cold) +-{ +- int ret; +- u8 reply; +- struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply}; +- deb_info("%s:\n", __func__); +- +- ret = ec168_rw_udev(udev, &req); +- if (ret) +- goto error; +- +- deb_info("%s: reply:%02x\n", __func__, reply); +- +- if (reply == 0x01) +- *cold = 0; +- else +- *cold = 1; +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties ec168_properties; +- +-static int ec168_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int ret; +- deb_info("%s: interface:%d\n", __func__, +- intf->cur_altsetting->desc.bInterfaceNumber); +- +- ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL, +- adapter_nr); +- if (ret) +- goto error; +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-#define E3C_EC168_1689 0 +-#define E3C_EC168_FFFA 1 +-#define E3C_EC168_FFFB 2 +-#define E3C_EC168_1001 3 +-#define E3C_EC168_1002 4 +- +-static struct usb_device_id ec168_id[] = { +- [E3C_EC168_1689] = +- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)}, +- [E3C_EC168_FFFA] = +- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)}, +- [E3C_EC168_FFFB] = +- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)}, +- [E3C_EC168_1001] = +- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)}, +- [E3C_EC168_1002] = +- {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)}, +- {} /* terminating entry */ +-}; +- +-MODULE_DEVICE_TABLE(usb, ec168_id); +- +-static struct dvb_usb_device_properties ec168_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = ec168_download_firmware, +- .firmware = "dvb-usb-ec168.fw", +- .no_reconnect = 1, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = ec168_streaming_ctrl, +- .frontend_attach = ec168_ec100_frontend_attach, +- .tuner_attach = ec168_mxl5003s_tuner_attach, +- .stream = { +- .type = USB_BULK, +- .count = 6, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = (32*512), +- } +- } +- }, +- }}, +- } +- }, +- +- .identify_state = ec168_identify_state, +- +- .i2c_algo = &ec168_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { +- .name = "E3C EC168 DVB-T USB2.0 reference design", +- .cold_ids = { +- &ec168_id[E3C_EC168_1689], +- &ec168_id[E3C_EC168_FFFA], +- &ec168_id[E3C_EC168_FFFB], +- &ec168_id[E3C_EC168_1001], +- &ec168_id[E3C_EC168_1002], +- NULL}, +- .warm_ids = {NULL}, +- }, +- } +-}; +- +-static struct usb_driver ec168_driver = { +- .name = "dvb_usb_ec168", +- .probe = ec168_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = ec168_id, +-}; +- +-module_usb_driver(ec168_driver); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h +deleted file mode 100644 +index e7e0b83..0000000 +--- a/drivers/media/dvb/dvb-usb/ec168.h ++++ /dev/null +@@ -1,73 +0,0 @@ +-/* +- * E3C EC168 DVB USB driver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef EC168_H +-#define EC168_H +- +-#define DVB_USB_LOG_PREFIX "ec168" +-#include "dvb-usb.h" +- +-#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args) +-#define deb_rc(args...) dprintk(dvb_usb_ec168_debug, 0x02, args) +-#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args) +-#define deb_reg(args...) dprintk(dvb_usb_ec168_debug, 0x08, args) +-#define deb_i2c(args...) dprintk(dvb_usb_ec168_debug, 0x10, args) +-#define deb_fw(args...) dprintk(dvb_usb_ec168_debug, 0x20, args) +- +-#define ec168_debug_dump(r, t, v, i, b, l, func) { \ +- int loop_; \ +- func("%02x %02x %02x %02x %02x %02x %02x %02x", \ +- t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \ +- if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ +- func(" >>> "); \ +- else \ +- func(" <<< "); \ +- for (loop_ = 0; loop_ < l; loop_++) \ +- func("%02x ", b[loop_]); \ +- func("\n");\ +-} +- +-#define EC168_USB_TIMEOUT 1000 +- +-struct ec168_req { +- u8 cmd; /* [1] */ +- u16 value; /* [2|3] */ +- u16 index; /* [4|5] */ +- u16 size; /* [6|7] */ +- u8 *data; +-}; +- +-enum ec168_cmd { +- DOWNLOAD_FIRMWARE = 0x00, +- CONFIG = 0x01, +- DEMOD_RW = 0x03, +- GPIO = 0x04, +- STREAMING_CTRL = 0x10, +- READ_I2C = 0x20, +- WRITE_I2C = 0x21, +- HID_DOWNLOAD = 0x30, +- GET_CONFIG, +- SET_CONFIG, +- READ_DEMOD, +- WRITE_DEMOD, +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c +deleted file mode 100644 +index 90a70c6..0000000 +--- a/drivers/media/dvb/dvb-usb/friio-fe.c ++++ /dev/null +@@ -1,473 +0,0 @@ +-/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. +- * +- * Copyright (C) 2009 Akihiro Tsukada +- * +- * This module is based off the the gl861 and vp702x modules. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include +-#include +-#include +- +-#include "friio.h" +- +-struct jdvbt90502_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend frontend; +- struct jdvbt90502_config config; +-}; +- +-/* NOTE: TC90502 has 16bit register-address? */ +-/* register 0x0100 is used for reading PLL status, so reg is u16 here */ +-static int jdvbt90502_reg_read(struct jdvbt90502_state *state, +- const u16 reg, u8 *buf, const size_t count) +-{ +- int ret; +- u8 wbuf[3]; +- struct i2c_msg msg[2]; +- +- wbuf[0] = reg & 0xFF; +- wbuf[1] = 0; +- wbuf[2] = reg >> 8; +- +- msg[0].addr = state->config.demod_address; +- msg[0].flags = 0; +- msg[0].buf = wbuf; +- msg[0].len = sizeof(wbuf); +- +- msg[1].addr = msg[0].addr; +- msg[1].flags = I2C_M_RD; +- msg[1].buf = buf; +- msg[1].len = count; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) { +- deb_fe(" reg read failed.\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* currently 16bit register-address is not used, so reg is u8 here */ +-static int jdvbt90502_single_reg_write(struct jdvbt90502_state *state, +- const u8 reg, const u8 val) +-{ +- struct i2c_msg msg; +- u8 wbuf[2]; +- +- wbuf[0] = reg; +- wbuf[1] = val; +- +- msg.addr = state->config.demod_address; +- msg.flags = 0; +- msg.buf = wbuf; +- msg.len = sizeof(wbuf); +- +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- deb_fe(" reg write failed."); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int _jdvbt90502_write(struct dvb_frontend *fe, const u8 buf[], int len) +-{ +- struct jdvbt90502_state *state = fe->demodulator_priv; +- int err, i; +- for (i = 0; i < len - 1; i++) { +- err = jdvbt90502_single_reg_write(state, +- buf[0] + i, buf[i + 1]); +- if (err) +- return err; +- } +- +- return 0; +-} +- +-/* read pll status byte via the demodulator's I2C register */ +-/* note: Win box reads it by 8B block at the I2C addr 0x30 from reg:0x80 */ +-static int jdvbt90502_pll_read(struct jdvbt90502_state *state, u8 *result) +-{ +- int ret; +- +- /* +1 for reading */ +- u8 pll_addr_byte = (state->config.pll_address << 1) + 1; +- +- *result = 0; +- +- ret = jdvbt90502_single_reg_write(state, JDVBT90502_2ND_I2C_REG, +- pll_addr_byte); +- if (ret) +- goto error; +- +- ret = jdvbt90502_reg_read(state, 0x0100, result, 1); +- if (ret) +- goto error; +- +- deb_fe("PLL read val:%02x\n", *result); +- return 0; +- +-error: +- deb_fe("%s:ret == %d\n", __func__, ret); +- return -EREMOTEIO; +-} +- +- +-/* set pll frequency via the demodulator's I2C register */ +-static int jdvbt90502_pll_set_freq(struct jdvbt90502_state *state, u32 freq) +-{ +- int ret; +- int retry; +- u8 res1; +- u8 res2[9]; +- +- u8 pll_freq_cmd[PLL_CMD_LEN]; +- u8 pll_agc_cmd[PLL_CMD_LEN]; +- struct i2c_msg msg[2]; +- u32 f; +- +- deb_fe("%s: freq=%d, step=%d\n", __func__, freq, +- state->frontend.ops.info.frequency_stepsize); +- /* freq -> oscilator frequency conversion. */ +- /* freq: 473,000,000 + n*6,000,000 [+ 142857 (center freq. shift)] */ +- f = freq / state->frontend.ops.info.frequency_stepsize; +- /* add 399[1/7 MHZ] = 57MHz for the IF */ +- f += 399; +- /* add center frequency shift if necessary */ +- if (f % 7 == 0) +- f++; +- pll_freq_cmd[DEMOD_REDIRECT_REG] = JDVBT90502_2ND_I2C_REG; /* 0xFE */ +- pll_freq_cmd[ADDRESS_BYTE] = state->config.pll_address << 1; +- pll_freq_cmd[DIVIDER_BYTE1] = (f >> 8) & 0x7F; +- pll_freq_cmd[DIVIDER_BYTE2] = f & 0xFF; +- pll_freq_cmd[CONTROL_BYTE] = 0xB2; /* ref.divider:28, 4MHz/28=1/7MHz */ +- pll_freq_cmd[BANDSWITCH_BYTE] = 0x08; /* UHF band */ +- +- msg[0].addr = state->config.demod_address; +- msg[0].flags = 0; +- msg[0].buf = pll_freq_cmd; +- msg[0].len = sizeof(pll_freq_cmd); +- +- ret = i2c_transfer(state->i2c, &msg[0], 1); +- if (ret != 1) +- goto error; +- +- udelay(50); +- +- pll_agc_cmd[DEMOD_REDIRECT_REG] = pll_freq_cmd[DEMOD_REDIRECT_REG]; +- pll_agc_cmd[ADDRESS_BYTE] = pll_freq_cmd[ADDRESS_BYTE]; +- pll_agc_cmd[DIVIDER_BYTE1] = pll_freq_cmd[DIVIDER_BYTE1]; +- pll_agc_cmd[DIVIDER_BYTE2] = pll_freq_cmd[DIVIDER_BYTE2]; +- pll_agc_cmd[CONTROL_BYTE] = 0x9A; /* AGC_CTRL instead of BANDSWITCH */ +- pll_agc_cmd[AGC_CTRL_BYTE] = 0x50; +- /* AGC Time Constant 2s, AGC take-over point:103dBuV(lowest) */ +- +- msg[1].addr = msg[0].addr; +- msg[1].flags = 0; +- msg[1].buf = pll_agc_cmd; +- msg[1].len = sizeof(pll_agc_cmd); +- +- ret = i2c_transfer(state->i2c, &msg[1], 1); +- if (ret != 1) +- goto error; +- +- /* I don't know what these cmds are for, */ +- /* but the USB log on a windows box contains them */ +- ret = jdvbt90502_single_reg_write(state, 0x01, 0x40); +- ret |= jdvbt90502_single_reg_write(state, 0x01, 0x00); +- if (ret) +- goto error; +- udelay(100); +- +- /* wait for the demod to be ready? */ +-#define RETRY_COUNT 5 +- for (retry = 0; retry < RETRY_COUNT; retry++) { +- ret = jdvbt90502_reg_read(state, 0x0096, &res1, 1); +- if (ret) +- goto error; +- /* if (res1 != 0x00) goto error; */ +- ret = jdvbt90502_reg_read(state, 0x00B0, res2, sizeof(res2)); +- if (ret) +- goto error; +- if (res2[0] >= 0xA7) +- break; +- msleep(100); +- } +- if (retry >= RETRY_COUNT) { +- deb_fe("%s: FE does not get ready after freq setting.\n", +- __func__); +- return -EREMOTEIO; +- } +- +- return 0; +-error: +- deb_fe("%s:ret == %d\n", __func__, ret); +- return -EREMOTEIO; +-} +- +-static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) +-{ +- u8 result; +- int ret; +- +- *state = FE_HAS_SIGNAL; +- +- ret = jdvbt90502_pll_read(fe->demodulator_priv, &result); +- if (ret) { +- deb_fe("%s:ret == %d\n", __func__, ret); +- return -EREMOTEIO; +- } +- +- *state = FE_HAS_SIGNAL +- | FE_HAS_CARRIER +- | FE_HAS_VITERBI +- | FE_HAS_SYNC; +- +- if (result & PLL_STATUS_LOCKED) +- *state |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, +- u16 *strength) +-{ +- int ret; +- u8 rbuf[37]; +- +- *strength = 0; +- +- /* status register (incl. signal strength) : 0x89 */ +- /* TODO: read just the necessary registers [0x8B..0x8D]? */ +- ret = jdvbt90502_reg_read(fe->demodulator_priv, 0x0089, +- rbuf, sizeof(rbuf)); +- +- if (ret) { +- deb_fe("%s:ret == %d\n", __func__, ret); +- return -EREMOTEIO; +- } +- +- /* signal_strength: rbuf[2-4] (24bit BE), use lower 16bit for now. */ +- *strength = (rbuf[3] << 8) + rbuf[4]; +- if (rbuf[2]) +- *strength = 0xffff; +- +- return 0; +-} +- +- +-/* filter out un-supported properties to notify users */ +-static int jdvbt90502_set_property(struct dvb_frontend *fe, +- struct dtv_property *tvp) +-{ +- int r = 0; +- +- switch (tvp->cmd) { +- case DTV_DELIVERY_SYSTEM: +- if (tvp->u.data != SYS_ISDBT) +- r = -EINVAL; +- break; +- case DTV_CLEAR: +- case DTV_TUNE: +- case DTV_FREQUENCY: +- break; +- default: +- r = -EINVAL; +- } +- return r; +-} +- +-static int jdvbt90502_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- p->inversion = INVERSION_AUTO; +- p->bandwidth_hz = 6000000; +- p->code_rate_HP = FEC_AUTO; +- p->code_rate_LP = FEC_AUTO; +- p->modulation = QAM_64; +- p->transmission_mode = TRANSMISSION_MODE_AUTO; +- p->guard_interval = GUARD_INTERVAL_AUTO; +- p->hierarchy = HIERARCHY_AUTO; +- return 0; +-} +- +-static int jdvbt90502_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- /** +- * NOTE: ignore all the parameters except frequency. +- * others should be fixed to the proper value for ISDB-T, +- * but don't check here. +- */ +- +- struct jdvbt90502_state *state = fe->demodulator_priv; +- int ret; +- +- deb_fe("%s: Freq:%d\n", __func__, p->frequency); +- +- /* for recovery from DTV_CLEAN */ +- fe->dtv_property_cache.delivery_system = SYS_ISDBT; +- +- ret = jdvbt90502_pll_set_freq(state, p->frequency); +- if (ret) { +- deb_fe("%s:ret == %d\n", __func__, ret); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +- +-/** +- * (reg, val) commad list to initialize this module. +- * captured on a Windows box. +- */ +-static u8 init_code[][2] = { +- {0x01, 0x40}, +- {0x04, 0x38}, +- {0x05, 0x40}, +- {0x07, 0x40}, +- {0x0F, 0x4F}, +- {0x11, 0x21}, +- {0x12, 0x0B}, +- {0x13, 0x2F}, +- {0x14, 0x31}, +- {0x16, 0x02}, +- {0x21, 0xC4}, +- {0x22, 0x20}, +- {0x2C, 0x79}, +- {0x2D, 0x34}, +- {0x2F, 0x00}, +- {0x30, 0x28}, +- {0x31, 0x31}, +- {0x32, 0xDF}, +- {0x38, 0x01}, +- {0x39, 0x78}, +- {0x3B, 0x33}, +- {0x3C, 0x33}, +- {0x48, 0x90}, +- {0x51, 0x68}, +- {0x5E, 0x38}, +- {0x71, 0x00}, +- {0x72, 0x08}, +- {0x77, 0x00}, +- {0xC0, 0x21}, +- {0xC1, 0x10}, +- {0xE4, 0x1A}, +- {0xEA, 0x1F}, +- {0x77, 0x00}, +- {0x71, 0x00}, +- {0x71, 0x00}, +- {0x76, 0x0C}, +-}; +- +-static const int init_code_len = sizeof(init_code) / sizeof(u8[2]); +- +-static int jdvbt90502_init(struct dvb_frontend *fe) +-{ +- int i = -1; +- int ret; +- struct i2c_msg msg; +- +- struct jdvbt90502_state *state = fe->demodulator_priv; +- +- deb_fe("%s called.\n", __func__); +- +- msg.addr = state->config.demod_address; +- msg.flags = 0; +- msg.len = 2; +- for (i = 0; i < init_code_len; i++) { +- msg.buf = init_code[i]; +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) +- goto error; +- } +- fe->dtv_property_cache.delivery_system = SYS_ISDBT; +- msleep(100); +- +- return 0; +- +-error: +- deb_fe("%s: init_code[%d] failed. ret==%d\n", __func__, i, ret); +- return -EREMOTEIO; +-} +- +- +-static void jdvbt90502_release(struct dvb_frontend *fe) +-{ +- struct jdvbt90502_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +- +-static struct dvb_frontend_ops jdvbt90502_ops; +- +-struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d) +-{ +- struct jdvbt90502_state *state = NULL; +- +- deb_info("%s called.\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct jdvbt90502_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = &d->i2c_adap; +- memcpy(&state->config, &friio_fe_config, sizeof(friio_fe_config)); +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &jdvbt90502_ops, +- sizeof(jdvbt90502_ops)); +- state->frontend.demodulator_priv = state; +- +- if (jdvbt90502_init(&state->frontend) < 0) +- goto error; +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops jdvbt90502_ops = { +- .delsys = { SYS_ISDBT }, +- .info = { +- .name = "Comtech JDVBT90502 ISDB-T", +- .frequency_min = 473000000, /* UHF 13ch, center */ +- .frequency_max = 767142857, /* UHF 62ch, center */ +- .frequency_stepsize = JDVBT90502_PLL_CLK / JDVBT90502_PLL_DIVIDER, +- .frequency_tolerance = 0, +- +- /* NOTE: this driver ignores all parameters but frequency. */ +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = jdvbt90502_release, +- +- .init = jdvbt90502_init, +- .write = _jdvbt90502_write, +- +- .set_property = jdvbt90502_set_property, +- +- .set_frontend = jdvbt90502_set_frontend, +- .get_frontend = jdvbt90502_get_frontend, +- +- .read_status = jdvbt90502_read_status, +- .read_signal_strength = jdvbt90502_read_signal_strength, +-}; +diff --git a/drivers/media/dvb/dvb-usb/friio.c b/drivers/media/dvb/dvb-usb/friio.c +deleted file mode 100644 +index 474a17e..0000000 +--- a/drivers/media/dvb/dvb-usb/friio.c ++++ /dev/null +@@ -1,522 +0,0 @@ +-/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. +- * +- * Copyright (C) 2009 Akihiro Tsukada +- * +- * This module is based off the the gl861 and vp702x modules. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "friio.h" +- +-/* debug */ +-int dvb_usb_friio_debug; +-module_param_named(debug, dvb_usb_friio_debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "set debugging level (1=info,2=xfer,4=rc,8=fe (or-able))." +- DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-/** +- * Indirect I2C access to the PLL via FE. +- * whole I2C protocol data to the PLL is sent via the FE's I2C register. +- * This is done by a control msg to the FE with the I2C data accompanied, and +- * a specific USB request number is assigned for that purpose. +- * +- * this func sends wbuf[1..] to the I2C register wbuf[0] at addr (= at FE). +- * TODO: refoctored, smarter i2c functions. +- */ +-static int gl861_i2c_ctrlmsg_data(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u16 index = wbuf[0]; /* must be JDVBT90502_2ND_I2C_REG(=0xFE) */ +- u16 value = addr << (8 + 1); +- int wo = (rbuf == NULL || rlen == 0); /* write only */ +- u8 req, type; +- +- deb_xfer("write to PLL:0x%02x via FE reg:0x%02x, len:%d\n", +- wbuf[1], wbuf[0], wlen - 1); +- +- if (wo && wlen >= 2) { +- req = GL861_REQ_I2C_DATA_CTRL_WRITE; +- type = GL861_WRITE; +- udelay(20); +- return usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- req, type, value, index, +- &wbuf[1], wlen - 1, 2000); +- } +- +- deb_xfer("not supported ctrl-msg, aborting."); +- return -EINVAL; +-} +- +-/* normal I2C access (without extra data arguments). +- * write to the register wbuf[0] at I2C address addr with the value wbuf[1], +- * or read from the register wbuf[0]. +- * register address can be 16bit (wbuf[2]<<8 | wbuf[0]) if wlen==3 +- */ +-static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u16 index; +- u16 value = addr << (8 + 1); +- int wo = (rbuf == NULL || rlen == 0); /* write-only */ +- u8 req, type; +- unsigned int pipe; +- +- /* special case for the indirect I2C access to the PLL via FE, */ +- if (addr == friio_fe_config.demod_address && +- wbuf[0] == JDVBT90502_2ND_I2C_REG) +- return gl861_i2c_ctrlmsg_data(d, addr, wbuf, wlen, rbuf, rlen); +- +- if (wo) { +- req = GL861_REQ_I2C_WRITE; +- type = GL861_WRITE; +- pipe = usb_sndctrlpipe(d->udev, 0); +- } else { /* rw */ +- req = GL861_REQ_I2C_READ; +- type = GL861_READ; +- pipe = usb_rcvctrlpipe(d->udev, 0); +- } +- +- switch (wlen) { +- case 1: +- index = wbuf[0]; +- break; +- case 2: +- index = wbuf[0]; +- value = value + wbuf[1]; +- break; +- case 3: +- /* special case for 16bit register-address */ +- index = (wbuf[2] << 8) | wbuf[0]; +- value = value + wbuf[1]; +- break; +- default: +- deb_xfer("wlen = %x, aborting.", wlen); +- return -EINVAL; +- } +- msleep(1); +- return usb_control_msg(d->udev, pipe, req, type, +- value, index, rbuf, rlen, 2000); +-} +- +-/* I2C */ +-static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { +- if (gl861_i2c_msg(d, msg[i].addr, +- msg[i].buf, msg[i].len, +- msg[i + 1].buf, msg[i + 1].len) < 0) +- break; +- i++; +- } else +- if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, NULL, 0) < 0) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 gl861_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static int friio_ext_ctl(struct dvb_usb_adapter *adap, +- u32 sat_color, int lnb_on) +-{ +- int i; +- int ret; +- struct i2c_msg msg; +- u8 *buf; +- u32 mask; +- u8 lnb = (lnb_on) ? FRIIO_CTL_LNB : 0; +- +- buf = kmalloc(2, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; +- +- msg.addr = 0x00; +- msg.flags = 0; +- msg.len = 2; +- msg.buf = buf; +- +- buf[0] = 0x00; +- +- /* send 2bit header (&B10) */ +- buf[1] = lnb | FRIIO_CTL_LED | FRIIO_CTL_STROBE; +- ret = gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- buf[1] |= FRIIO_CTL_CLK; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- +- buf[1] = lnb | FRIIO_CTL_STROBE; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- buf[1] |= FRIIO_CTL_CLK; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- +- /* send 32bit(satur, R, G, B) data in serial */ +- mask = 1 << 31; +- for (i = 0; i < 32; i++) { +- buf[1] = lnb | FRIIO_CTL_STROBE; +- if (sat_color & mask) +- buf[1] |= FRIIO_CTL_LED; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- buf[1] |= FRIIO_CTL_CLK; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- mask >>= 1; +- } +- +- /* set the strobe off */ +- buf[1] = lnb; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- buf[1] |= FRIIO_CTL_CLK; +- ret += gl861_i2c_xfer(&adap->dev->i2c_adap, &msg, 1); +- +- kfree(buf); +- return (ret == 70); +-} +- +- +-static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); +- +-/* TODO: move these init cmds to the FE's init routine? */ +-static u8 streaming_init_cmds[][2] = { +- {0x33, 0x08}, +- {0x37, 0x40}, +- {0x3A, 0x1F}, +- {0x3B, 0xFF}, +- {0x3C, 0x1F}, +- {0x3D, 0xFF}, +- {0x38, 0x00}, +- {0x35, 0x00}, +- {0x39, 0x00}, +- {0x36, 0x00}, +-}; +-static int cmdlen = sizeof(streaming_init_cmds) / 2; +- +-/* +- * Command sequence in this init function is a replay +- * of the captured USB commands from the Windows proprietary driver. +- */ +-static int friio_initialize(struct dvb_usb_device *d) +-{ +- int ret; +- int i; +- int retry = 0; +- u8 *rbuf, *wbuf; +- +- deb_info("%s called.\n", __func__); +- +- wbuf = kmalloc(3, GFP_KERNEL); +- if (!wbuf) +- return -ENOMEM; +- +- rbuf = kmalloc(2, GFP_KERNEL); +- if (!rbuf) { +- kfree(wbuf); +- return -ENOMEM; +- } +- +- /* use gl861_i2c_msg instead of gl861_i2c_xfer(), */ +- /* because the i2c device is not set up yet. */ +- wbuf[0] = 0x11; +- wbuf[1] = 0x02; +- ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- msleep(2); +- +- wbuf[0] = 0x11; +- wbuf[1] = 0x00; +- ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- msleep(1); +- +- /* following msgs should be in the FE's init code? */ +- /* cmd sequence to identify the device type? (friio black/white) */ +- wbuf[0] = 0x03; +- wbuf[1] = 0x80; +- /* can't use gl861_i2c_cmd, as the register-addr is 16bit(0x0100) */ +- ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, +- 0x1200, 0x0100, wbuf, 2, 2000); +- if (ret < 0) +- goto error; +- +- msleep(2); +- wbuf[0] = 0x00; +- wbuf[2] = 0x01; /* reg.0x0100 */ +- wbuf[1] = 0x00; +- ret = gl861_i2c_msg(d, 0x12 >> 1, wbuf, 3, rbuf, 2); +- /* my Friio White returns 0xffff. */ +- if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) +- goto error; +- +- msleep(2); +- wbuf[0] = 0x03; +- wbuf[1] = 0x80; +- ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- GL861_REQ_I2C_DATA_CTRL_WRITE, GL861_WRITE, +- 0x9000, 0x0100, wbuf, 2, 2000); +- if (ret < 0) +- goto error; +- +- msleep(2); +- wbuf[0] = 0x00; +- wbuf[2] = 0x01; /* reg.0x0100 */ +- wbuf[1] = 0x00; +- ret = gl861_i2c_msg(d, 0x90 >> 1, wbuf, 3, rbuf, 2); +- /* my Friio White returns 0xffff again. */ +- if (ret < 0 || rbuf[0] != 0xff || rbuf[1] != 0xff) +- goto error; +- +- msleep(1); +- +-restart: +- /* ============ start DEMOD init cmds ================== */ +- /* read PLL status to clear the POR bit */ +- wbuf[0] = JDVBT90502_2ND_I2C_REG; +- wbuf[1] = (FRIIO_PLL_ADDR << 1) + 1; /* +1 for reading */ +- ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- +- msleep(5); +- /* note: DEMODULATOR has 16bit register-address. */ +- wbuf[0] = 0x00; +- wbuf[2] = 0x01; /* reg addr: 0x0100 */ +- wbuf[1] = 0x00; /* val: not used */ +- ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 3, rbuf, 1); +- if (ret < 0) +- goto error; +-/* +- msleep(1); +- wbuf[0] = 0x80; +- wbuf[1] = 0x00; +- ret = gl861_i2c_msg(d, FRIIO_DEMOD_ADDR, wbuf, 2, rbuf, 1); +- if (ret < 0) +- goto error; +- */ +- if (rbuf[0] & 0x80) { /* still in PowerOnReset state? */ +- if (++retry > 3) { +- deb_info("failed to get the correct" +- " FE demod status:0x%02x\n", rbuf[0]); +- goto error; +- } +- msleep(100); +- goto restart; +- } +- +- /* TODO: check return value in rbuf */ +- /* =========== end DEMOD init cmds ===================== */ +- msleep(1); +- +- wbuf[0] = 0x30; +- wbuf[1] = 0x04; +- ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- +- msleep(2); +- /* following 2 cmds unnecessary? */ +- wbuf[0] = 0x00; +- wbuf[1] = 0x01; +- ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- +- wbuf[0] = 0x06; +- wbuf[1] = 0x0F; +- ret = gl861_i2c_msg(d, 0x00, wbuf, 2, NULL, 0); +- if (ret < 0) +- goto error; +- +- /* some streaming ctl cmds (maybe) */ +- msleep(10); +- for (i = 0; i < cmdlen; i++) { +- ret = gl861_i2c_msg(d, 0x00, streaming_init_cmds[i], 2, +- NULL, 0); +- if (ret < 0) +- goto error; +- msleep(1); +- } +- msleep(20); +- +- /* change the LED color etc. */ +- ret = friio_streaming_ctrl(&d->adapter[0], 0); +- if (ret < 0) +- goto error; +- +- return 0; +- +-error: +- kfree(wbuf); +- kfree(rbuf); +- deb_info("%s:ret == %d\n", __func__, ret); +- return -EIO; +-} +- +-/* Callbacks for DVB USB */ +- +-static int friio_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret; +- +- deb_info("%s called.(%d)\n", __func__, onoff); +- +- /* set the LED color and saturation (and LNB on) */ +- if (onoff) +- ret = friio_ext_ctl(adap, 0x6400ff64, 1); +- else +- ret = friio_ext_ctl(adap, 0x96ff00ff, 1); +- +- if (ret != 1) { +- deb_info("%s failed to send cmdx. ret==%d\n", __func__, ret); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int friio_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- if (friio_initialize(adap->dev) < 0) +- return -EIO; +- +- adap->fe_adap[0].fe = jdvbt90502_attach(adap->dev); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- return 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties friio_properties; +- +-static int friio_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- struct usb_host_interface *alt; +- int ret; +- +- if (intf->num_altsetting < GL861_ALTSETTING_COUNT) +- return -ENODEV; +- +- alt = usb_altnum_to_altsetting(intf, FRIIO_BULK_ALTSETTING); +- if (alt == NULL) { +- deb_rc("not alt found!\n"); +- return -ENODEV; +- } +- ret = usb_set_interface(interface_to_usbdev(intf), +- alt->desc.bInterfaceNumber, +- alt->desc.bAlternateSetting); +- if (ret != 0) { +- deb_rc("failed to set alt-setting!\n"); +- return ret; +- } +- +- ret = dvb_usb_device_init(intf, &friio_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) +- friio_streaming_ctrl(&d->adapter[0], 1); +- +- return ret; +-} +- +- +-struct jdvbt90502_config friio_fe_config = { +- .demod_address = FRIIO_DEMOD_ADDR, +- .pll_address = FRIIO_PLL_ADDR, +-}; +- +-static struct i2c_algorithm gl861_i2c_algo = { +- .master_xfer = gl861_i2c_xfer, +- .functionality = gl861_i2c_func, +-}; +- +-static struct usb_device_id friio_table[] = { +- { USB_DEVICE(USB_VID_774, USB_PID_FRIIO_WHITE) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, friio_table); +- +- +-static struct dvb_usb_device_properties friio_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = { +- /* caps:0 => no pid filter, 188B TS packet */ +- /* GL861 has a HW pid filter, but no info available. */ +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = 0, +- +- .frontend_attach = friio_frontend_attach, +- .streaming_ctrl = friio_streaming_ctrl, +- +- .stream = { +- .type = USB_BULK, +- /* count <= MAX_NO_URBS_FOR_DATA_STREAM(10) */ +- .count = 8, +- .endpoint = 0x01, +- .u = { +- /* GL861 has 6KB buf inside */ +- .bulk = { +- .buffersize = 16384, +- } +- } +- }, +- }}, +- } +- }, +- .i2c_algo = &gl861_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { +- .name = "774 Friio ISDB-T USB2.0", +- .cold_ids = { NULL }, +- .warm_ids = { &friio_table[0], NULL }, +- }, +- } +-}; +- +-static struct usb_driver friio_driver = { +- .name = "dvb_usb_friio", +- .probe = friio_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = friio_table, +-}; +- +-module_usb_driver(friio_driver); +- +-MODULE_AUTHOR("Akihiro Tsukada "); +-MODULE_DESCRIPTION("Driver for Friio ISDB-T USB2.0 Receiver"); +-MODULE_VERSION("0.2"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/friio.h b/drivers/media/dvb/dvb-usb/friio.h +deleted file mode 100644 +index 0f461ca..0000000 +--- a/drivers/media/dvb/dvb-usb/friio.h ++++ /dev/null +@@ -1,99 +0,0 @@ +-/* DVB USB compliant Linux driver for the Friio USB2.0 ISDB-T receiver. +- * +- * Copyright (C) 2009 Akihiro Tsukada +- * +- * This module is based off the the gl861 and vp702x modules. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_FRIIO_H_ +-#define _DVB_USB_FRIIO_H_ +- +-/** +- * Friio Components +- * USB hub: AU4254 +- * USB controller(+ TS dmx & streaming): GL861 +- * Frontend: comtech JDVBT-90502 +- * (tuner PLL: tua6034, I2C addr:(0xC0 >> 1)) +- * (OFDM demodulator: TC90502, I2C addr:(0x30 >> 1)) +- * LED x3 (+LNB) control: PIC 16F676 +- * EEPROM: 24C08 +- * +- * (USB smart card reader: AU9522) +- * +- */ +- +-#define DVB_USB_LOG_PREFIX "friio" +-#include "dvb-usb.h" +- +-extern int dvb_usb_friio_debug; +-#define deb_info(args...) dprintk(dvb_usb_friio_debug, 0x01, args) +-#define deb_xfer(args...) dprintk(dvb_usb_friio_debug, 0x02, args) +-#define deb_rc(args...) dprintk(dvb_usb_friio_debug, 0x04, args) +-#define deb_fe(args...) dprintk(dvb_usb_friio_debug, 0x08, args) +- +-/* Vendor requests */ +-#define GL861_WRITE 0x40 +-#define GL861_READ 0xc0 +- +-/* command bytes */ +-#define GL861_REQ_I2C_WRITE 0x01 +-#define GL861_REQ_I2C_READ 0x02 +-/* For control msg with data argument */ +-/* Used for accessing the PLL on the secondary I2C bus of FE via GL861 */ +-#define GL861_REQ_I2C_DATA_CTRL_WRITE 0x03 +- +-#define GL861_ALTSETTING_COUNT 2 +-#define FRIIO_BULK_ALTSETTING 0 +-#define FRIIO_ISOC_ALTSETTING 1 +- +-/* LED & LNB control via PIC. */ +-/* basically, it's serial control with clock and strobe. */ +-/* write the below 4bit control data to the reg 0x00 at the I2C addr 0x00 */ +-/* when controlling the LEDs, 32bit(saturation, R, G, B) is sent on the bit3*/ +-#define FRIIO_CTL_LNB (1 << 0) +-#define FRIIO_CTL_STROBE (1 << 1) +-#define FRIIO_CTL_CLK (1 << 2) +-#define FRIIO_CTL_LED (1 << 3) +- +-/* Front End related */ +- +-#define FRIIO_DEMOD_ADDR (0x30 >> 1) +-#define FRIIO_PLL_ADDR (0xC0 >> 1) +- +-#define JDVBT90502_PLL_CLK 4000000 +-#define JDVBT90502_PLL_DIVIDER 28 +- +-#define JDVBT90502_2ND_I2C_REG 0xFE +- +-/* byte index for pll i2c command data structure*/ +-/* see datasheet for tua6034 */ +-#define DEMOD_REDIRECT_REG 0 +-#define ADDRESS_BYTE 1 +-#define DIVIDER_BYTE1 2 +-#define DIVIDER_BYTE2 3 +-#define CONTROL_BYTE 4 +-#define BANDSWITCH_BYTE 5 +-#define AGC_CTRL_BYTE 5 +-#define PLL_CMD_LEN 6 +- +-/* bit masks for PLL STATUS response */ +-#define PLL_STATUS_POR_MODE 0x80 /* 1: Power on Reset (test) Mode */ +-#define PLL_STATUS_LOCKED 0x40 /* 1: locked */ +-#define PLL_STATUS_AGC_ACTIVE 0x08 /* 1:active */ +-#define PLL_STATUS_TESTMODE 0x07 /* digital output level (5 level) */ +- /* 0.15Vcc step 0x00: < 0.15Vcc, ..., 0x04: >= 0.6Vcc (<= 1Vcc) */ +- +- +-struct jdvbt90502_config { +- u8 demod_address; /* i2c addr for demodulator IC */ +- u8 pll_address; /* PLL addr on the secondary i2c*/ +-}; +-extern struct jdvbt90502_config friio_fe_config; +- +-extern struct dvb_frontend *jdvbt90502_attach(struct dvb_usb_device *d); +-#endif +diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c +deleted file mode 100644 +index c1f5582..0000000 +--- a/drivers/media/dvb/dvb-usb/gl861.c ++++ /dev/null +@@ -1,217 +0,0 @@ +-/* DVB USB compliant linux driver for GL861 USB2.0 devices. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the +- * Free Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "gl861.h" +- +-#include "zl10353.h" +-#include "qt1010.h" +- +-/* debug */ +-static int dvb_usb_gl861_debug; +-module_param_named(debug, dvb_usb_gl861_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." +- DVB_USB_DEBUG_STATUS); +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, +- u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +-{ +- u16 index; +- u16 value = addr << (8 + 1); +- int wo = (rbuf == NULL || rlen == 0); /* write-only */ +- u8 req, type; +- +- if (wo) { +- req = GL861_REQ_I2C_WRITE; +- type = GL861_WRITE; +- } else { /* rw */ +- req = GL861_REQ_I2C_READ; +- type = GL861_READ; +- } +- +- switch (wlen) { +- case 1: +- index = wbuf[0]; +- break; +- case 2: +- index = wbuf[0]; +- value = value + wbuf[1]; +- break; +- default: +- warn("wlen = %x, aborting.", wlen); +- return -EINVAL; +- } +- +- msleep(1); /* avoid I2C errors */ +- +- return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, +- value, index, rbuf, rlen, 2000); +-} +- +-/* I2C */ +-static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i; +- +- if (num > 2) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- /* write/read request */ +- if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { +- if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) +- break; +- i++; +- } else +- if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, +- msg[i].len, NULL, 0) < 0) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 gl861_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm gl861_i2c_algo = { +- .master_xfer = gl861_i2c_xfer, +- .functionality = gl861_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static struct zl10353_config gl861_zl10353_config = { +- .demod_address = 0x0f, +- .no_tuner = 1, +- .parallel_ts = 1, +-}; +- +-static int gl861_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- +- adap->fe_adap[0].fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe == NULL) +- return -EIO; +- +- return 0; +-} +- +-static struct qt1010_config gl861_qt1010_config = { +- .i2c_address = 0x62 +-}; +- +-static int gl861_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- return dvb_attach(qt1010_attach, +- adap->fe_adap[0].fe, &adap->dev->i2c_adap, +- &gl861_qt1010_config) == NULL ? -ENODEV : 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties gl861_properties; +- +-static int gl861_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- struct usb_host_interface *alt; +- int ret; +- +- if (intf->num_altsetting < 2) +- return -ENODEV; +- +- ret = dvb_usb_device_init(intf, &gl861_properties, THIS_MODULE, &d, +- adapter_nr); +- if (ret == 0) { +- alt = usb_altnum_to_altsetting(intf, 0); +- +- if (alt == NULL) { +- deb_rc("not alt found!\n"); +- return -ENODEV; +- } +- +- ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, +- alt->desc.bAlternateSetting); +- } +- +- return ret; +-} +- +-static struct usb_device_id gl861_table [] = { +- { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) }, +- { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, gl861_table); +- +-static struct dvb_usb_device_properties gl861_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = 0, +- +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- +- .frontend_attach = gl861_frontend_attach, +- .tuner_attach = gl861_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x81, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }, +- }}, +- } }, +- .i2c_algo = &gl861_i2c_algo, +- +- .num_device_descs = 2, +- .devices = { +- { +- .name = "MSI Mega Sky 55801 DVB-T USB2.0", +- .cold_ids = { NULL }, +- .warm_ids = { &gl861_table[0], NULL }, +- }, +- { +- .name = "A-LINK DTU DVB-T USB2.0", +- .cold_ids = { NULL }, +- .warm_ids = { &gl861_table[1], NULL }, +- }, +- } +-}; +- +-static struct usb_driver gl861_driver = { +- .name = "dvb_usb_gl861", +- .probe = gl861_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = gl861_table, +-}; +- +-module_usb_driver(gl861_driver); +- +-MODULE_AUTHOR("Carl Lundqvist "); +-MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861"); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/gl861.h b/drivers/media/dvb/dvb-usb/gl861.h +deleted file mode 100644 +index c54855e..0000000 +--- a/drivers/media/dvb/dvb-usb/gl861.h ++++ /dev/null +@@ -1,15 +0,0 @@ +-#ifndef _DVB_USB_GL861_H_ +-#define _DVB_USB_GL861_H_ +- +-#define DVB_USB_LOG_PREFIX "gl861" +-#include "dvb-usb.h" +- +-#define deb_rc(args...) dprintk(dvb_usb_gl861_debug, 0x01, args) +- +-#define GL861_WRITE 0x40 +-#define GL861_READ 0xc0 +- +-#define GL861_REQ_I2C_WRITE 0x01 +-#define GL861_REQ_I2C_READ 0x02 +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/gp8psk-fe.c b/drivers/media/dvb/dvb-usb/gp8psk-fe.c +deleted file mode 100644 +index 67957dd..0000000 +--- a/drivers/media/dvb/dvb-usb/gp8psk-fe.c ++++ /dev/null +@@ -1,369 +0,0 @@ +-/* DVB USB compliant Linux driver for the +- * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module +- * +- * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) +- * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) +- * +- * Thanks to GENPIX for the sample code used to implement this module. +- * +- * This module is based off the vp7045 and vp702x modules +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "gp8psk.h" +- +-struct gp8psk_fe_state { +- struct dvb_frontend fe; +- struct dvb_usb_device *d; +- u8 lock; +- u16 snr; +- unsigned long next_status_check; +- unsigned long status_check_interval; +-}; +- +-static int gp8psk_tuned_to_DCII(struct dvb_frontend *fe) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- u8 status; +- gp8psk_usb_in_op(st->d, GET_8PSK_CONFIG, 0, 0, &status, 1); +- return status & bmDCtuned; +-} +- +-static int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode) +-{ +- struct gp8psk_fe_state *state = fe->demodulator_priv; +- return gp8psk_usb_out_op(state->d, SET_8PSK_CONFIG, mode, 0, NULL, 0); +-} +- +-static int gp8psk_fe_update_status(struct gp8psk_fe_state *st) +-{ +- u8 buf[6]; +- if (time_after(jiffies,st->next_status_check)) { +- gp8psk_usb_in_op(st->d, GET_SIGNAL_LOCK, 0,0,&st->lock,1); +- gp8psk_usb_in_op(st->d, GET_SIGNAL_STRENGTH, 0,0,buf,6); +- st->snr = (buf[1]) << 8 | buf[0]; +- st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; +- } +- return 0; +-} +- +-static int gp8psk_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- gp8psk_fe_update_status(st); +- +- if (st->lock) +- *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; +- else +- *status = 0; +- +- if (*status & FE_HAS_LOCK) +- st->status_check_interval = 1000; +- else +- st->status_check_interval = 100; +- return 0; +-} +- +-/* not supported by this Frontend */ +-static int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +-{ +- (void) fe; +- *ber = 0; +- return 0; +-} +- +-/* not supported by this Frontend */ +-static int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +-{ +- (void) fe; +- *unc = 0; +- return 0; +-} +- +-static int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- gp8psk_fe_update_status(st); +- /* snr is reported in dBu*256 */ +- *snr = st->snr; +- return 0; +-} +- +-static int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- gp8psk_fe_update_status(st); +- /* snr is reported in dBu*256 */ +- /* snr / 38.4 ~= 100% strength */ +- /* snr * 17 returns 100% strength as 65535 */ +- if (st->snr > 0xf00) +- *strength = 0xffff; +- else +- *strength = (st->snr << 4) + st->snr; /* snr*17 */ +- return 0; +-} +- +-static int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 800; +- return 0; +-} +- +-static int gp8psk_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct gp8psk_fe_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 cmd[10]; +- u32 freq = c->frequency * 1000; +- int gp_product_id = le16_to_cpu(state->d->udev->descriptor.idProduct); +- +- deb_fe("%s()\n", __func__); +- +- cmd[4] = freq & 0xff; +- cmd[5] = (freq >> 8) & 0xff; +- cmd[6] = (freq >> 16) & 0xff; +- cmd[7] = (freq >> 24) & 0xff; +- +- /* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */ +- if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8) +- c->delivery_system = SYS_TURBO; +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- if (c->modulation != QPSK) { +- deb_fe("%s: unsupported modulation selected (%d)\n", +- __func__, c->modulation); +- return -EOPNOTSUPP; +- } +- c->fec_inner = FEC_AUTO; +- break; +- case SYS_DVBS2: /* kept for backwards compatibility */ +- deb_fe("%s: DVB-S2 delivery system selected\n", __func__); +- break; +- case SYS_TURBO: +- deb_fe("%s: Turbo-FEC delivery system selected\n", __func__); +- break; +- +- default: +- deb_fe("%s: unsupported delivery system selected (%d)\n", +- __func__, c->delivery_system); +- return -EOPNOTSUPP; +- } +- +- cmd[0] = c->symbol_rate & 0xff; +- cmd[1] = (c->symbol_rate >> 8) & 0xff; +- cmd[2] = (c->symbol_rate >> 16) & 0xff; +- cmd[3] = (c->symbol_rate >> 24) & 0xff; +- switch (c->modulation) { +- case QPSK: +- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) +- if (gp8psk_tuned_to_DCII(fe)) +- gp8psk_bcm4500_reload(state->d); +- switch (c->fec_inner) { +- case FEC_1_2: +- cmd[9] = 0; break; +- case FEC_2_3: +- cmd[9] = 1; break; +- case FEC_3_4: +- cmd[9] = 2; break; +- case FEC_5_6: +- cmd[9] = 3; break; +- case FEC_7_8: +- cmd[9] = 4; break; +- case FEC_AUTO: +- cmd[9] = 5; break; +- default: +- cmd[9] = 5; break; +- } +- if (c->delivery_system == SYS_TURBO) +- cmd[8] = ADV_MOD_TURBO_QPSK; +- else +- cmd[8] = ADV_MOD_DVB_QPSK; +- break; +- case PSK_8: /* PSK_8 is for compatibility with DN */ +- cmd[8] = ADV_MOD_TURBO_8PSK; +- switch (c->fec_inner) { +- case FEC_2_3: +- cmd[9] = 0; break; +- case FEC_3_4: +- cmd[9] = 1; break; +- case FEC_3_5: +- cmd[9] = 2; break; +- case FEC_5_6: +- cmd[9] = 3; break; +- case FEC_8_9: +- cmd[9] = 4; break; +- default: +- cmd[9] = 0; break; +- } +- break; +- case QAM_16: /* QAM_16 is for compatibility with DN */ +- cmd[8] = ADV_MOD_TURBO_16QAM; +- cmd[9] = 0; +- break; +- default: /* Unknown modulation */ +- deb_fe("%s: unsupported modulation selected (%d)\n", +- __func__, c->modulation); +- return -EOPNOTSUPP; +- } +- +- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) +- gp8psk_set_tuner_mode(fe, 0); +- gp8psk_usb_out_op(state->d, TUNE_8PSK, 0, 0, cmd, 10); +- +- state->lock = 0; +- state->next_status_check = jiffies; +- state->status_check_interval = 200; +- +- return 0; +-} +- +-static int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- +- deb_fe("%s\n",__func__); +- +- if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, m->msg[0], 0, +- m->msg, m->msg_len)) { +- return -EINVAL; +- } +- return 0; +-} +- +-static int gp8psk_fe_send_diseqc_burst (struct dvb_frontend* fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct gp8psk_fe_state *st = fe->demodulator_priv; +- u8 cmd; +- +- deb_fe("%s\n",__func__); +- +- /* These commands are certainly wrong */ +- cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01; +- +- if (gp8psk_usb_out_op(st->d,SEND_DISEQC_COMMAND, cmd, 0, +- &cmd, 0)) { +- return -EINVAL; +- } +- return 0; +-} +- +-static int gp8psk_fe_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct gp8psk_fe_state* state = fe->demodulator_priv; +- +- if (gp8psk_usb_out_op(state->d,SET_22KHZ_TONE, +- (tone == SEC_TONE_ON), 0, NULL, 0)) { +- return -EINVAL; +- } +- return 0; +-} +- +-static int gp8psk_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct gp8psk_fe_state* state = fe->demodulator_priv; +- +- if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, +- voltage == SEC_VOLTAGE_18, 0, NULL, 0)) { +- return -EINVAL; +- } +- return 0; +-} +- +-static int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff) +-{ +- struct gp8psk_fe_state* state = fe->demodulator_priv; +- return gp8psk_usb_out_op(state->d, USE_EXTRA_VOLT, onoff, 0,NULL,0); +-} +- +-static int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd) +-{ +- struct gp8psk_fe_state* state = fe->demodulator_priv; +- u8 cmd = sw_cmd & 0x7f; +- +- if (gp8psk_usb_out_op(state->d,SET_DN_SWITCH, cmd, 0, +- NULL, 0)) { +- return -EINVAL; +- } +- if (gp8psk_usb_out_op(state->d,SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), +- 0, NULL, 0)) { +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void gp8psk_fe_release(struct dvb_frontend* fe) +-{ +- struct gp8psk_fe_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops gp8psk_fe_ops; +- +-struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d) +-{ +- struct gp8psk_fe_state *s = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL); +- if (s == NULL) +- goto error; +- +- s->d = d; +- memcpy(&s->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops)); +- s->fe.demodulator_priv = s; +- +- goto success; +-error: +- return NULL; +-success: +- return &s->fe; +-} +- +- +-static struct dvb_frontend_ops gp8psk_fe_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Genpix DVB-S", +- .frequency_min = 800000, +- .frequency_max = 2250000, +- .frequency_stepsize = 100, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, /* ppm */ +- .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_QAM_16 is for compatibility +- * (Myth incorrectly detects Turbo-QPSK as plain QAM-16) +- */ +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC +- }, +- +- .release = gp8psk_fe_release, +- +- .init = NULL, +- .sleep = NULL, +- +- .set_frontend = gp8psk_fe_set_frontend, +- +- .get_tune_settings = gp8psk_fe_get_tune_settings, +- +- .read_status = gp8psk_fe_read_status, +- .read_ber = gp8psk_fe_read_ber, +- .read_signal_strength = gp8psk_fe_read_signal_strength, +- .read_snr = gp8psk_fe_read_snr, +- .read_ucblocks = gp8psk_fe_read_unc_blocks, +- +- .diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg, +- .diseqc_send_burst = gp8psk_fe_send_diseqc_burst, +- .set_tone = gp8psk_fe_set_tone, +- .set_voltage = gp8psk_fe_set_voltage, +- .dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd, +- .enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage +-}; +diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c +deleted file mode 100644 +index 5d0384d..0000000 +--- a/drivers/media/dvb/dvb-usb/gp8psk.c ++++ /dev/null +@@ -1,328 +0,0 @@ +-/* DVB USB compliant Linux driver for the +- * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module +- * +- * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) +- * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com) +- * +- * Thanks to GENPIX for the sample code used to implement this module. +- * +- * This module is based off the vp7045 and vp702x modules +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "gp8psk.h" +- +-/* debug */ +-static char bcm4500_firmware[] = "dvb-usb-gp8psk-02.fw"; +-int dvb_usb_gp8psk_debug; +-module_param_named(debug,dvb_usb_gp8psk_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int gp8psk_get_fw_version(struct dvb_usb_device *d, u8 *fw_vers) +-{ +- return (gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6)); +-} +- +-static int gp8psk_get_fpga_version(struct dvb_usb_device *d, u8 *fpga_vers) +-{ +- return (gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, fpga_vers, 1)); +-} +- +-static void gp8psk_info(struct dvb_usb_device *d) +-{ +- u8 fpga_vers, fw_vers[6]; +- +- if (!gp8psk_get_fw_version(d, fw_vers)) +- info("FW Version = %i.%02i.%i (0x%x) Build %4i/%02i/%02i", +- fw_vers[2], fw_vers[1], fw_vers[0], GP8PSK_FW_VERS(fw_vers), +- 2000 + fw_vers[5], fw_vers[4], fw_vers[3]); +- else +- info("failed to get FW version"); +- +- if (!gp8psk_get_fpga_version(d, &fpga_vers)) +- info("FPGA Version = %i", fpga_vers); +- else +- info("failed to get FPGA version"); +-} +- +-int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +-{ +- int ret = 0,try = 0; +- +- if ((ret = mutex_lock_interruptible(&d->usb_mutex))) +- return ret; +- +- while (ret >= 0 && ret != blen && try < 3) { +- ret = usb_control_msg(d->udev, +- usb_rcvctrlpipe(d->udev,0), +- req, +- USB_TYPE_VENDOR | USB_DIR_IN, +- value,index,b,blen, +- 2000); +- deb_info("reading number %d (ret: %d)\n",try,ret); +- try++; +- } +- +- if (ret < 0 || ret != blen) { +- warn("usb in %d operation failed.", req); +- ret = -EIO; +- } else +- ret = 0; +- +- deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index); +- debug_dump(b,blen,deb_xfer); +- +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, +- u16 index, u8 *b, int blen) +-{ +- int ret; +- +- deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index); +- debug_dump(b,blen,deb_xfer); +- +- if ((ret = mutex_lock_interruptible(&d->usb_mutex))) +- return ret; +- +- if (usb_control_msg(d->udev, +- usb_sndctrlpipe(d->udev,0), +- req, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- value,index,b,blen, +- 2000) != blen) { +- warn("usb out operation failed."); +- ret = -EIO; +- } else +- ret = 0; +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d) +-{ +- int ret; +- const struct firmware *fw = NULL; +- const u8 *ptr; +- u8 *buf; +- if ((ret = request_firmware(&fw, bcm4500_firmware, +- &d->udev->dev)) != 0) { +- err("did not find the bcm4500 firmware file. (%s) " +- "Please see linux/Documentation/dvb/ for more details on firmware-problems. (%d)", +- bcm4500_firmware,ret); +- return ret; +- } +- +- ret = -EINVAL; +- +- if (gp8psk_usb_out_op(d, LOAD_BCM4500,1,0,NULL, 0)) +- goto out_rel_fw; +- +- info("downloading bcm4500 firmware from file '%s'",bcm4500_firmware); +- +- ptr = fw->data; +- buf = kmalloc(64, GFP_KERNEL | GFP_DMA); +- if (!buf) { +- ret = -ENOMEM; +- goto out_rel_fw; +- } +- +- while (ptr[0] != 0xff) { +- u16 buflen = ptr[0] + 4; +- if (ptr + buflen >= fw->data + fw->size) { +- err("failed to load bcm4500 firmware."); +- goto out_free; +- } +- memcpy(buf, ptr, buflen); +- if (dvb_usb_generic_write(d, buf, buflen)) { +- err("failed to load bcm4500 firmware."); +- goto out_free; +- } +- ptr += buflen; +- } +- +- ret = 0; +- +-out_free: +- kfree(buf); +-out_rel_fw: +- release_firmware(fw); +- +- return ret; +-} +- +-static int gp8psk_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 status, buf; +- int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); +- +- if (onoff) { +- gp8psk_usb_in_op(d, GET_8PSK_CONFIG,0,0,&status,1); +- if (! (status & bm8pskStarted)) { /* started */ +- if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K) +- gp8psk_usb_out_op(d, CW3K_INIT, 1, 0, NULL, 0); +- if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1)) +- return -EINVAL; +- gp8psk_info(d); +- } +- +- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) +- if (! (status & bm8pskFW_Loaded)) /* BCM4500 firmware loaded */ +- if(gp8psk_load_bcm4500fw(d)) +- return -EINVAL; +- +- if (! (status & bmIntersilOn)) /* LNB Power */ +- if (gp8psk_usb_in_op(d, START_INTERSIL, 1, 0, +- &buf, 1)) +- return -EINVAL; +- +- /* Set DVB mode to 1 */ +- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) +- if (gp8psk_usb_out_op(d, SET_DVB_MODE, 1, 0, NULL, 0)) +- return -EINVAL; +- /* Abort possible TS (if previous tune crashed) */ +- if (gp8psk_usb_out_op(d, ARM_TRANSFER, 0, 0, NULL, 0)) +- return -EINVAL; +- } else { +- /* Turn off LNB power */ +- if (gp8psk_usb_in_op(d, START_INTERSIL, 0, 0, &buf, 1)) +- return -EINVAL; +- /* Turn off 8psk power */ +- if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) +- return -EINVAL; +- if(gp_product_id == USB_PID_GENPIX_SKYWALKER_CW3K) +- gp8psk_usb_out_op(d, CW3K_INIT, 0, 0, NULL, 0); +- } +- return 0; +-} +- +-int gp8psk_bcm4500_reload(struct dvb_usb_device *d) +-{ +- u8 buf; +- int gp_product_id = le16_to_cpu(d->udev->descriptor.idProduct); +- /* Turn off 8psk power */ +- if (gp8psk_usb_in_op(d, BOOT_8PSK, 0, 0, &buf, 1)) +- return -EINVAL; +- /* Turn On 8psk power */ +- if (gp8psk_usb_in_op(d, BOOT_8PSK, 1, 0, &buf, 1)) +- return -EINVAL; +- /* load BCM4500 firmware */ +- if (gp_product_id == USB_PID_GENPIX_8PSK_REV_1_WARM) +- if (gp8psk_load_bcm4500fw(d)) +- return -EINVAL; +- return 0; +-} +- +-static int gp8psk_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- return gp8psk_usb_out_op(adap->dev, ARM_TRANSFER, onoff, 0 , NULL, 0); +-} +- +-static int gp8psk_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- adap->fe_adap[0].fe = gp8psk_fe_attach(adap->dev); +- return 0; +-} +- +-static struct dvb_usb_device_properties gp8psk_properties; +- +-static int gp8psk_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- int ret; +- struct usb_device *udev = interface_to_usbdev(intf); +- ret = dvb_usb_device_init(intf, &gp8psk_properties, +- THIS_MODULE, NULL, adapter_nr); +- if (ret == 0) { +- info("found Genpix USB device pID = %x (hex)", +- le16_to_cpu(udev->descriptor.idProduct)); +- } +- return ret; +-} +- +-static struct usb_device_id gp8psk_usb_table [] = { +- { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_COLD) }, +- { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_1_WARM) }, +- { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_8PSK_REV_2) }, +- { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_1) }, +- { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_2) }, +-/* { USB_DEVICE(USB_VID_GENPIX, USB_PID_GENPIX_SKYWALKER_CW3K) }, */ +- { 0 }, +-}; +-MODULE_DEVICE_TABLE(usb, gp8psk_usb_table); +- +-static struct dvb_usb_device_properties gp8psk_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-gp8psk-01.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = gp8psk_streaming_ctrl, +- .frontend_attach = gp8psk_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 8192, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = gp8psk_power_ctrl, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 4, +- .devices = { +- { .name = "Genpix 8PSK-to-USB2 Rev.1 DVB-S receiver", +- .cold_ids = { &gp8psk_usb_table[0], NULL }, +- .warm_ids = { &gp8psk_usb_table[1], NULL }, +- }, +- { .name = "Genpix 8PSK-to-USB2 Rev.2 DVB-S receiver", +- .cold_ids = { NULL }, +- .warm_ids = { &gp8psk_usb_table[2], NULL }, +- }, +- { .name = "Genpix SkyWalker-1 DVB-S receiver", +- .cold_ids = { NULL }, +- .warm_ids = { &gp8psk_usb_table[3], NULL }, +- }, +- { .name = "Genpix SkyWalker-2 DVB-S receiver", +- .cold_ids = { NULL }, +- .warm_ids = { &gp8psk_usb_table[4], NULL }, +- }, +- { NULL }, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver gp8psk_usb_driver = { +- .name = "dvb_usb_gp8psk", +- .probe = gp8psk_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = gp8psk_usb_table, +-}; +- +-module_usb_driver(gp8psk_usb_driver); +- +-MODULE_AUTHOR("Alan Nisota "); +-MODULE_DESCRIPTION("Driver for Genpix DVB-S"); +-MODULE_VERSION("1.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/gp8psk.h b/drivers/media/dvb/dvb-usb/gp8psk.h +deleted file mode 100644 +index ed32b9d..0000000 +--- a/drivers/media/dvb/dvb-usb/gp8psk.h ++++ /dev/null +@@ -1,100 +0,0 @@ +-/* DVB USB compliant Linux driver for the +- * - GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module +- * +- * Copyright (C) 2006 Alan Nisota (alannisota@gmail.com) +- * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com) +- * +- * Thanks to GENPIX for the sample code used to implement this module. +- * +- * This module is based off the vp7045 and vp702x modules +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_GP8PSK_H_ +-#define _DVB_USB_GP8PSK_H_ +- +-#define DVB_USB_LOG_PREFIX "gp8psk" +-#include "dvb-usb.h" +- +-extern int dvb_usb_gp8psk_debug; +-#define deb_info(args...) dprintk(dvb_usb_gp8psk_debug,0x01,args) +-#define deb_xfer(args...) dprintk(dvb_usb_gp8psk_debug,0x02,args) +-#define deb_rc(args...) dprintk(dvb_usb_gp8psk_debug,0x04,args) +-#define deb_fe(args...) dprintk(dvb_usb_gp8psk_debug,0x08,args) +- +-/* Twinhan Vendor requests */ +-#define TH_COMMAND_IN 0xC0 +-#define TH_COMMAND_OUT 0xC1 +- +-/* gp8psk commands */ +- +-#define GET_8PSK_CONFIG 0x80 /* in */ +-#define SET_8PSK_CONFIG 0x81 +-#define I2C_WRITE 0x83 +-#define I2C_READ 0x84 +-#define ARM_TRANSFER 0x85 +-#define TUNE_8PSK 0x86 +-#define GET_SIGNAL_STRENGTH 0x87 /* in */ +-#define LOAD_BCM4500 0x88 +-#define BOOT_8PSK 0x89 /* in */ +-#define START_INTERSIL 0x8A /* in */ +-#define SET_LNB_VOLTAGE 0x8B +-#define SET_22KHZ_TONE 0x8C +-#define SEND_DISEQC_COMMAND 0x8D +-#define SET_DVB_MODE 0x8E +-#define SET_DN_SWITCH 0x8F +-#define GET_SIGNAL_LOCK 0x90 /* in */ +-#define GET_FW_VERS 0x92 +-#define GET_SERIAL_NUMBER 0x93 /* in */ +-#define USE_EXTRA_VOLT 0x94 +-#define GET_FPGA_VERS 0x95 +-#define CW3K_INIT 0x9d +- +-/* PSK_configuration bits */ +-#define bm8pskStarted 0x01 +-#define bm8pskFW_Loaded 0x02 +-#define bmIntersilOn 0x04 +-#define bmDVBmode 0x08 +-#define bm22kHz 0x10 +-#define bmSEL18V 0x20 +-#define bmDCtuned 0x40 +-#define bmArmed 0x80 +- +-/* Satellite modulation modes */ +-#define ADV_MOD_DVB_QPSK 0 /* DVB-S QPSK */ +-#define ADV_MOD_TURBO_QPSK 1 /* Turbo QPSK */ +-#define ADV_MOD_TURBO_8PSK 2 /* Turbo 8PSK (also used for Trellis 8PSK) */ +-#define ADV_MOD_TURBO_16QAM 3 /* Turbo 16QAM (also used for Trellis 8PSK) */ +- +-#define ADV_MOD_DCII_C_QPSK 4 /* Digicipher II Combo */ +-#define ADV_MOD_DCII_I_QPSK 5 /* Digicipher II I-stream */ +-#define ADV_MOD_DCII_Q_QPSK 6 /* Digicipher II Q-stream */ +-#define ADV_MOD_DCII_C_OQPSK 7 /* Digicipher II offset QPSK */ +-#define ADV_MOD_DSS_QPSK 8 /* DSS (DIRECTV) QPSK */ +-#define ADV_MOD_DVB_BPSK 9 /* DVB-S BPSK */ +- +-#define GET_USB_SPEED 0x07 +- +-#define RESET_FX2 0x13 +- +-#define FW_VERSION_READ 0x0B +-#define VENDOR_STRING_READ 0x0C +-#define PRODUCT_STRING_READ 0x0D +-#define FW_BCD_VERSION_READ 0x14 +- +-/* firmware revision id's */ +-#define GP8PSK_FW_REV1 0x020604 +-#define GP8PSK_FW_REV2 0x020704 +-#define GP8PSK_FW_VERS(_fw_vers) ((_fw_vers)[2]<<0x10 | (_fw_vers)[1]<<0x08 | (_fw_vers)[0]) +- +-extern struct dvb_frontend * gp8psk_fe_attach(struct dvb_usb_device *d); +-extern int gp8psk_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); +-extern int gp8psk_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, +- u16 index, u8 *b, int blen); +-extern int gp8psk_bcm4500_reload(struct dvb_usb_device *d); +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c +deleted file mode 100644 +index 9f01cd7..0000000 +--- a/drivers/media/dvb/dvb-usb/it913x.c ++++ /dev/null +@@ -1,827 +0,0 @@ +-/* DVB USB compliant linux driver for IT9137 +- * +- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) +- * IT9137 (C) ITE Tech Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * see Documentation/dvb/it9137.txt for firmware information +- * +- */ +-#define DVB_USB_LOG_PREFIX "it913x" +- +-#include +-#include +-#include +- +-#include "dvb-usb.h" +-#include "it913x-fe.h" +- +-/* debug */ +-static int dvb_usb_it913x_debug; +-#define l_dprintk(var, level, args...) do { \ +- if ((var >= level)) \ +- printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \ +-} while (0) +- +-#define deb_info(level, args...) l_dprintk(dvb_usb_it913x_debug, level, args) +-#define debug_data_snipet(level, name, p) \ +- deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ +- *p, *(p+1), *(p+2), *(p+3), *(p+4), \ +- *(p+5), *(p+6), *(p+7)); +- +- +-module_param_named(debug, dvb_usb_it913x_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." +- DVB_USB_DEBUG_STATUS); +- +-static int pid_filter; +-module_param_named(pid, pid_filter, int, 0644); +-MODULE_PARM_DESC(pid, "set default 0=on 1=off"); +- +-static int dvb_usb_it913x_firmware; +-module_param_named(firmware, dvb_usb_it913x_firmware, int, 0644); +-MODULE_PARM_DESC(firmware, "set firmware 0=auto 1=IT9137 2=IT9135V1"); +- +- +-int cmd_counter; +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct it913x_state { +- u8 id; +- struct ite_config it913x_config; +-}; +- +-struct ite_config it913x_config; +- +-#define IT913X_RETRY 10 +-#define IT913X_SND_TIMEOUT 100 +-#define IT913X_RCV_TIMEOUT 200 +- +-static int it913x_bulk_write(struct usb_device *dev, +- u8 *snd, int len, u8 pipe) +-{ +- int ret, actual_l, i; +- +- for (i = 0; i < IT913X_RETRY; i++) { +- ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), +- snd, len , &actual_l, IT913X_SND_TIMEOUT); +- if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) +- break; +- } +- +- if (len != actual_l && ret == 0) +- ret = -EAGAIN; +- +- return ret; +-} +- +-static int it913x_bulk_read(struct usb_device *dev, +- u8 *rev, int len, u8 pipe) +-{ +- int ret, actual_l, i; +- +- for (i = 0; i < IT913X_RETRY; i++) { +- ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), +- rev, len , &actual_l, IT913X_RCV_TIMEOUT); +- if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) +- break; +- } +- +- if (len != actual_l && ret == 0) +- ret = -EAGAIN; +- +- return ret; +-} +- +-static u16 check_sum(u8 *p, u8 len) +-{ +- u16 sum = 0; +- u8 i = 1; +- while (i < len) +- sum += (i++ & 1) ? (*p++) << 8 : *p++; +- return ~sum; +-} +- +-static int it913x_usb_talk(struct usb_device *udev, u8 mode, u8 pro, +- u8 cmd, u32 reg, u8 addr, u8 *data, u8 len) +-{ +- int ret = 0, i, buf_size = 1; +- u8 *buff; +- u8 rlen; +- u16 chk_sum; +- +- buff = kzalloc(256, GFP_KERNEL); +- if (!buff) { +- info("USB Buffer Failed"); +- return -ENOMEM; +- } +- +- buff[buf_size++] = pro; +- buff[buf_size++] = cmd; +- buff[buf_size++] = cmd_counter; +- +- switch (mode) { +- case READ_LONG: +- case WRITE_LONG: +- buff[buf_size++] = len; +- buff[buf_size++] = 2; +- buff[buf_size++] = (reg >> 24); +- buff[buf_size++] = (reg >> 16) & 0xff; +- buff[buf_size++] = (reg >> 8) & 0xff; +- buff[buf_size++] = reg & 0xff; +- break; +- case READ_SHORT: +- buff[buf_size++] = addr; +- break; +- case WRITE_SHORT: +- buff[buf_size++] = len; +- buff[buf_size++] = addr; +- buff[buf_size++] = (reg >> 8) & 0xff; +- buff[buf_size++] = reg & 0xff; +- break; +- case READ_DATA: +- case WRITE_DATA: +- break; +- case WRITE_CMD: +- mode = 7; +- break; +- default: +- kfree(buff); +- return -EINVAL; +- } +- +- if (mode & 1) { +- for (i = 0; i < len ; i++) +- buff[buf_size++] = data[i]; +- } +- chk_sum = check_sum(&buff[1], buf_size); +- +- buff[buf_size++] = chk_sum >> 8; +- buff[0] = buf_size; +- buff[buf_size++] = (chk_sum & 0xff); +- +- ret = it913x_bulk_write(udev, buff, buf_size , 0x02); +- if (ret < 0) +- goto error; +- +- ret = it913x_bulk_read(udev, buff, (mode & 1) ? +- 5 : len + 5 , 0x01); +- if (ret < 0) +- goto error; +- +- rlen = (mode & 0x1) ? 0x1 : len; +- +- if (mode & 1) +- ret = buff[2]; +- else +- memcpy(data, &buff[3], rlen); +- +- cmd_counter++; +- +-error: kfree(buff); +- +- return ret; +-} +- +-static int it913x_io(struct usb_device *udev, u8 mode, u8 pro, +- u8 cmd, u32 reg, u8 addr, u8 *data, u8 len) +-{ +- int ret, i; +- +- for (i = 0; i < IT913X_RETRY; i++) { +- ret = it913x_usb_talk(udev, mode, pro, +- cmd, reg, addr, data, len); +- if (ret != -EAGAIN) +- break; +- } +- +- return ret; +-} +- +-static int it913x_wr_reg(struct usb_device *udev, u8 pro, u32 reg , u8 data) +-{ +- int ret; +- u8 b[1]; +- b[0] = data; +- ret = it913x_io(udev, WRITE_LONG, pro, +- CMD_DEMOD_WRITE, reg, 0, b, sizeof(b)); +- +- return ret; +-} +- +-static int it913x_read_reg(struct usb_device *udev, u32 reg) +-{ +- int ret; +- u8 data[1]; +- +- ret = it913x_io(udev, READ_LONG, DEV_0, +- CMD_DEMOD_READ, reg, 0, &data[0], 1); +- +- return (ret < 0) ? ret : data[0]; +-} +- +-static u32 it913x_query(struct usb_device *udev, u8 pro) +-{ +- int ret; +- u8 data[4]; +- ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ, +- 0x1222, 0, &data[0], 3); +- +- it913x_config.chip_ver = data[0]; +- it913x_config.chip_type = (u16)(data[2] << 8) + data[1]; +- +- info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver, +- it913x_config.chip_type); +- +- ret |= it913x_io(udev, READ_SHORT, pro, +- CMD_QUERYINFO, 0, 0x1, &data[0], 4); +- +- it913x_config.firmware = (data[0] << 24) + (data[1] << 16) + +- (data[2] << 8) + data[3]; +- +- return (ret < 0) ? 0 : it913x_config.firmware; +-} +- +-static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct usb_device *udev = adap->dev->udev; +- int ret; +- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; +- +- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) +- return -EAGAIN; +- deb_info(1, "PID_C (%02x)", onoff); +- +- ret = it913x_wr_reg(udev, pro, PID_EN, onoff); +- +- mutex_unlock(&adap->dev->i2c_mutex); +- return ret; +-} +- +-static int it913x_pid_filter(struct dvb_usb_adapter *adap, +- int index, u16 pid, int onoff) +-{ +- struct usb_device *udev = adap->dev->udev; +- int ret; +- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; +- +- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) +- return -EAGAIN; +- deb_info(1, "PID_F (%02x)", onoff); +- +- ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff)); +- +- ret |= it913x_wr_reg(udev, pro, PID_MSB, (u8)(pid >> 8)); +- +- ret |= it913x_wr_reg(udev, pro, PID_INX_EN, (u8)onoff); +- +- ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f)); +- +- mutex_unlock(&adap->dev->i2c_mutex); +- return 0; +-} +- +- +-static int it913x_return_status(struct usb_device *udev) +-{ +- u32 firm = 0; +- +- firm = it913x_query(udev, DEV_0); +- if (firm > 0) +- info("Firmware Version %d", firm); +- +- return (firm > 0) ? firm : 0; +-} +- +-static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- static u8 data[256]; +- int ret; +- u32 reg; +- u8 pro; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- debug_data_snipet(1, "Message out", msg[0].buf); +- deb_info(2, "num of messages %d address %02x", num, msg[0].addr); +- +- pro = (msg[0].addr & 0x2) ? DEV_0_DMOD : 0x0; +- pro |= (msg[0].addr & 0x20) ? DEV_1 : DEV_0; +- memcpy(data, msg[0].buf, msg[0].len); +- reg = (data[0] << 24) + (data[1] << 16) + +- (data[2] << 8) + data[3]; +- if (num == 2) { +- ret = it913x_io(d->udev, READ_LONG, pro, +- CMD_DEMOD_READ, reg, 0, data, msg[1].len); +- memcpy(msg[1].buf, data, msg[1].len); +- } else +- ret = it913x_io(d->udev, WRITE_LONG, pro, CMD_DEMOD_WRITE, +- reg, 0, &data[4], msg[0].len - 4); +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-static u32 it913x_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm it913x_i2c_algo = { +- .master_xfer = it913x_i2c_xfer, +- .functionality = it913x_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-#define IT913X_POLL 250 +-static int it913x_rc_query(struct dvb_usb_device *d) +-{ +- u8 ibuf[4]; +- int ret; +- u32 key; +- /* Avoid conflict with frontends*/ +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET, +- 0, 0, &ibuf[0], sizeof(ibuf)); +- +- if ((ibuf[2] + ibuf[3]) == 0xff) { +- key = ibuf[2]; +- key += ibuf[0] << 16; +- key += ibuf[1] << 8; +- deb_info(1, "NEC Extended Key =%08x", key); +- if (d->rc_dev != NULL) +- rc_keydown(d->rc_dev, key, 0); +- } +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-/* Firmware sets raw */ +-const char fw_it9135_v1[] = "dvb-usb-it9135-01.fw"; +-const char fw_it9135_v2[] = "dvb-usb-it9135-02.fw"; +-const char fw_it9137[] = "dvb-usb-it9137-01.fw"; +- +-static int ite_firmware_select(struct usb_device *udev, +- struct dvb_usb_device_properties *props) +-{ +- int sw; +- /* auto switch */ +- if (le16_to_cpu(udev->descriptor.idProduct) == +- USB_PID_ITETECH_IT9135) +- sw = IT9135_V1_FW; +- else if (le16_to_cpu(udev->descriptor.idProduct) == +- USB_PID_ITETECH_IT9135_9005) +- sw = IT9135_V1_FW; +- else if (le16_to_cpu(udev->descriptor.idProduct) == +- USB_PID_ITETECH_IT9135_9006) { +- sw = IT9135_V2_FW; +- if (it913x_config.tuner_id_0 == 0) +- it913x_config.tuner_id_0 = IT9135_60; +- } else +- sw = IT9137_FW; +- +- /* force switch */ +- if (dvb_usb_it913x_firmware != IT9135_AUTO) +- sw = dvb_usb_it913x_firmware; +- +- switch (sw) { +- case IT9135_V1_FW: +- it913x_config.firmware_ver = 1; +- it913x_config.adc_x2 = 1; +- props->firmware = fw_it9135_v1; +- break; +- case IT9135_V2_FW: +- it913x_config.firmware_ver = 1; +- it913x_config.adc_x2 = 1; +- props->firmware = fw_it9135_v2; +- break; +- case IT9137_FW: +- default: +- it913x_config.firmware_ver = 0; +- it913x_config.adc_x2 = 0; +- props->firmware = fw_it9137; +- } +- +- return 0; +-} +- +-#define TS_MPEG_PKT_SIZE 188 +-#define EP_LOW 21 +-#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE) +-#define EP_HIGH 348 +-#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE) +- +-static int it913x_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- int ret = 0, firm_no; +- u8 reg, remote; +- +- firm_no = it913x_return_status(udev); +- +- /* checnk for dual mode */ +- it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5); +- +- if (udev->speed != USB_SPEED_HIGH) { +- props->adapter[0].fe[0].pid_filter_count = 5; +- info("USB 1 low speed mode - connect to USB 2 port"); +- if (pid_filter > 0) +- pid_filter = 0; +- if (it913x_config.dual_mode) { +- it913x_config.dual_mode = 0; +- info("Dual mode not supported in USB 1"); +- } +- } else /* For replugging */ +- if(props->adapter[0].fe[0].pid_filter_count == 5) +- props->adapter[0].fe[0].pid_filter_count = 31; +- +- /* TODO different remotes */ +- remote = it913x_read_reg(udev, 0x49ac); /* Remote */ +- if (remote == 0) +- props->rc.core.rc_codes = NULL; +- +- /* TODO at the moment tuner_id is always assigned to 0x38 */ +- it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0); +- +- info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode +- , remote, it913x_config.tuner_id_0); +- +- /* Select Stream Buffer Size and pid filter option*/ +- if (pid_filter) { +- props->adapter[0].fe[0].stream.u.bulk.buffersize = +- TS_BUFFER_SIZE_MAX; +- props->adapter[0].fe[0].caps &= +- ~DVB_USB_ADAP_NEED_PID_FILTERING; +- } else +- props->adapter[0].fe[0].stream.u.bulk.buffersize = +- TS_BUFFER_SIZE_PID; +- +- if (it913x_config.dual_mode) { +- props->adapter[1].fe[0].stream.u.bulk.buffersize = +- props->adapter[0].fe[0].stream.u.bulk.buffersize; +- props->num_adapters = 2; +- if (pid_filter) +- props->adapter[1].fe[0].caps = +- props->adapter[0].fe[0].caps; +- } else +- props->num_adapters = 1; +- +- ret = ite_firmware_select(udev, props); +- +- if (firm_no > 0) { +- *cold = 0; +- return 0; +- } +- +- if (it913x_config.dual_mode) { +- it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0); +- ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1); +- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1); +- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1); +- msleep(50); +- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0); +- msleep(50); +- reg = it913x_read_reg(udev, GPIOH1_O); +- if (reg == 0) { +- ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1); +- ret |= it913x_return_status(udev); +- if (ret != 0) +- ret = it913x_wr_reg(udev, DEV_0, +- GPIOH1_O, 0x0); +- } +- } +- +- reg = it913x_read_reg(udev, IO_MUX_POWER_CLK); +- +- if (it913x_config.dual_mode) { +- ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR); +- if (it913x_config.firmware_ver == 1) +- ret |= it913x_wr_reg(udev, DEV_0, 0xcfff, 0x1); +- else +- ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1); +- } else { +- ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0); +- if (it913x_config.firmware_ver == 1) +- ret |= it913x_wr_reg(udev, DEV_0, 0xcfff, 0x0); +- else +- ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0); +- } +- +- *cold = 1; +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- int ret = 0; +- u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD; +- +- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) +- return -EAGAIN; +- deb_info(1, "STM (%02x)", onoff); +- +- if (!onoff) +- ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1); +- +- +- mutex_unlock(&adap->dev->i2c_mutex); +- +- return ret; +-} +- +-static int it913x_download_firmware(struct usb_device *udev, +- const struct firmware *fw) +-{ +- int ret = 0, i = 0, pos = 0; +- u8 packet_size, min_pkt; +- u8 *fw_data; +- +- ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100); +- +- info("FRM Starting Firmware Download"); +- +- /* Multi firmware loader */ +- /* This uses scatter write firmware headers */ +- /* The firmware must start with 03 XX 00 */ +- /* and be the extact firmware length */ +- +- if (it913x_config.chip_ver == 2) +- min_pkt = 0x11; +- else +- min_pkt = 0x19; +- +- while (i <= fw->size) { +- if (((fw->data[i] == 0x3) && (fw->data[i + 2] == 0x0)) +- || (i == fw->size)) { +- packet_size = i - pos; +- if ((packet_size > min_pkt) || (i == fw->size)) { +- fw_data = (u8 *)(fw->data + pos); +- pos += packet_size; +- if (packet_size > 0) +- ret |= it913x_io(udev, WRITE_DATA, +- DEV_0, CMD_SCATTER_WRITE, 0, +- 0, fw_data, packet_size); +- udelay(1000); +- } +- } +- i++; +- } +- +- ret |= it913x_io(udev, WRITE_CMD, DEV_0, CMD_BOOT, 0, 0, NULL, 0); +- +- msleep(100); +- +- if (ret < 0) +- info("FRM Firmware Download Failed (%04x)" , ret); +- else +- info("FRM Firmware Download Completed - Resetting Device"); +- +- ret |= it913x_return_status(udev); +- +- msleep(30); +- +- ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400); +- +- /* Tuner function */ +- if (it913x_config.dual_mode) +- ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0); +- else +- ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0x68); +- +- if ((it913x_config.chip_ver == 1) && +- (it913x_config.chip_type == 0x9135)) { +- ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0); +- ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0); +- if (it913x_config.dual_mode) { +- ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0); +- ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0); +- } +- } +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int it913x_name(struct dvb_usb_adapter *adap) +-{ +- const char *desc = adap->dev->desc->name; +- char *fe_name[] = {"_1", "_2", "_3", "_4"}; +- char *name = adap->fe_adap[0].fe->ops.info.name; +- +- strlcpy(name, desc, 128); +- strlcat(name, fe_name[adap->id], 128); +- +- return 0; +-} +- +-static int it913x_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct usb_device *udev = adap->dev->udev; +- struct it913x_state *st = adap->dev->priv; +- int ret = 0; +- u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); +- u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize / 4; +- u8 pkt_size = 0x80; +- +- if (adap->dev->udev->speed != USB_SPEED_HIGH) +- pkt_size = 0x10; +- +- it913x_config.adf = it913x_read_reg(udev, IO_MUX_POWER_CLK); +- +- if (adap->id == 0) +- memcpy(&st->it913x_config, &it913x_config, +- sizeof(struct ite_config)); +- +- adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach, +- &adap->dev->i2c_adap, adap_addr, &st->it913x_config); +- +- if (adap->id == 0 && adap->fe_adap[0].fe) { +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x1); +- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x0f); +- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_NAK, 0x1b); +- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x2f); +- ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_LSB, +- ep_size & 0xff); +- ret = it913x_wr_reg(udev, DEV_0, EP4_TX_LEN_MSB, ep_size >> 8); +- ret = it913x_wr_reg(udev, DEV_0, EP4_MAX_PKT, pkt_size); +- } else if (adap->id == 1 && adap->fe_adap[0].fe) { +- ret = it913x_wr_reg(udev, DEV_0, EP0_TX_EN, 0x6f); +- ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_LSB, +- ep_size & 0xff); +- ret = it913x_wr_reg(udev, DEV_0, EP5_TX_LEN_MSB, ep_size >> 8); +- ret = it913x_wr_reg(udev, DEV_0, EP5_MAX_PKT, pkt_size); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_EN, 0x1); +- ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_SERIAL, 0x1); +- ret = it913x_wr_reg(udev, DEV_1, TOP_HOSTB_SER_MODE, 0x1); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, TSIS_ENABLE, 0x1); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x0); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_SW_RST, 0x0); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF2_HALF_PSB, 0x0); +- ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2IF_STOP_EN, 0x1); +- ret = it913x_wr_reg(udev, DEV_1_DMOD, MPEG_FULL_SPEED, 0x0); +- ret = it913x_wr_reg(udev, DEV_1_DMOD, MP2IF_STOP_EN, 0x0); +- } else +- return -ENODEV; +- +- ret = it913x_name(adap); +- +- return ret; +-} +- +-/* DVB USB Driver */ +-static struct dvb_usb_device_properties it913x_properties; +- +-static int it913x_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- cmd_counter = 0; +- if (0 == dvb_usb_device_init(intf, &it913x_properties, +- THIS_MODULE, NULL, adapter_nr)) { +- info("DEV registering device driver"); +- return 0; +- } +- +- info("DEV it913x Error"); +- return -ENODEV; +- +-} +- +-static struct usb_device_id it913x_table[] = { +- { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) }, +- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) }, +- { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22_IT9137) }, +- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9005) }, +- { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135_9006) }, +- {} /* Terminating entry */ +-}; +- +-MODULE_DEVICE_TABLE(usb, it913x_table); +- +-static struct dvb_usb_device_properties it913x_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = it913x_download_firmware, +- .firmware = "dvb-usb-it9137-01.fw", +- .no_reconnect = 1, +- .size_of_priv = sizeof(struct it913x_state), +- .num_adapters = 2, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER| +- DVB_USB_ADAP_NEED_PID_FILTERING| +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .streaming_ctrl = it913x_streaming_ctrl, +- .pid_filter_count = 31, +- .pid_filter = it913x_pid_filter, +- .pid_filter_ctrl = it913x_pid_filter_ctrl, +- .frontend_attach = it913x_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x04, +- .u = {/* Keep Low if PID filter on */ +- .bulk = { +- .buffersize = +- TS_BUFFER_SIZE_PID, +- } +- } +- } +- }}, +- }, +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER| +- DVB_USB_ADAP_NEED_PID_FILTERING| +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .streaming_ctrl = it913x_streaming_ctrl, +- .pid_filter_count = 31, +- .pid_filter = it913x_pid_filter, +- .pid_filter_ctrl = it913x_pid_filter_ctrl, +- .frontend_attach = it913x_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 5, +- .endpoint = 0x05, +- .u = { +- .bulk = { +- .buffersize = +- TS_BUFFER_SIZE_PID, +- } +- } +- } +- }}, +- } +- }, +- .identify_state = it913x_identify_state, +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "it913x", +- .rc_query = it913x_rc_query, +- .rc_interval = IT913X_POLL, +- .allowed_protos = RC_TYPE_NEC, +- .rc_codes = RC_MAP_MSI_DIGIVOX_III, +- }, +- .i2c_algo = &it913x_i2c_algo, +- .num_device_descs = 5, +- .devices = { +- { "Kworld UB499-2T T09(IT9137)", +- { &it913x_table[0], NULL }, +- }, +- { "ITE 9135 Generic", +- { &it913x_table[1], NULL }, +- }, +- { "Sveon STV22 Dual DVB-T HDTV(IT9137)", +- { &it913x_table[2], NULL }, +- }, +- { "ITE 9135(9005) Generic", +- { &it913x_table[3], NULL }, +- }, +- { "ITE 9135(9006) Generic", +- { &it913x_table[4], NULL }, +- }, +- } +-}; +- +-static struct usb_driver it913x_driver = { +- .name = "it913x", +- .probe = it913x_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = it913x_table, +-}; +- +-module_usb_driver(it913x_driver); +- +-MODULE_AUTHOR("Malcolm Priestley "); +-MODULE_DESCRIPTION("it913x USB 2 Driver"); +-MODULE_VERSION("1.22"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c +deleted file mode 100644 +index b3fe05b..0000000 +--- a/drivers/media/dvb/dvb-usb/lmedm04.c ++++ /dev/null +@@ -1,1299 +0,0 @@ +-/* DVB USB compliant linux driver for +- * +- * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 +- * LME2510C + LG TDQY-P001F +- * LME2510C + BS2F7HZ0194 +- * LME2510 + LG TDQY-P001F +- * LME2510 + BS2F7HZ0194 +- * +- * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) +- * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) +- * +- * MV001F (LME2510+LGTDQY-P001F) +- * LG TDQY - P001F =(TDA8263 + TDA10086H) +- * +- * MVB0001F (LME2510C+LGTDQT-P001F) +- * +- * MV0194 (LME2510+SHARP:BS2F7HZ0194) +- * SHARP:BS2F7HZ0194 = (STV0299+IX2410) +- * +- * MVB0194 (LME2510C+SHARP0194) +- * +- * For firmware see Documentation/dvb/lmedm04.txt +- * +- * I2C addresses: +- * 0xd0 - STV0288 - Demodulator +- * 0xc0 - Sharp IX2505V - Tuner +- * -- +- * 0x1c - TDA10086 - Demodulator +- * 0xc0 - TDA8263 - Tuner +- * -- +- * 0xd0 - STV0299 - Demodulator +- * 0xc0 - IX2410 - Tuner +- * +- * +- * VID = 3344 PID LME2510=1122 LME2510C=1120 +- * +- * Copyright (C) 2010 Malcolm Priestley (tvboxspy@gmail.com) +- * LME2510(C)(C) Leaguerme (Shenzhen) MicroElectronics Co., Ltd. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * 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. +- * +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * +- * Known Issues : +- * LME2510: Non Intel USB chipsets fail to maintain High Speed on +- * Boot or Hot Plug. +- * +- * QQbox suffers from noise on LNB voltage. +- * +- * LME2510: SHARP:BS2F7HZ0194(MV0194) cannot cold reset and share system +- * with other tuners. After a cold reset streaming will not start. +- * +- */ +-#define DVB_USB_LOG_PREFIX "LME2510(C)" +-#include +-#include +-#include +- +-#include "dvb-usb.h" +-#include "lmedm04.h" +-#include "tda826x.h" +-#include "tda10086.h" +-#include "stv0288.h" +-#include "ix2505v.h" +-#include "stv0299.h" +-#include "dvb-pll.h" +-#include "z0194a.h" +- +- +- +-/* debug */ +-static int dvb_usb_lme2510_debug; +-#define l_dprintk(var, level, args...) do { \ +- if ((var >= level)) \ +- printk(KERN_DEBUG DVB_USB_LOG_PREFIX ": " args); \ +-} while (0) +- +-#define deb_info(level, args...) l_dprintk(dvb_usb_lme2510_debug, level, args) +-#define debug_data_snipet(level, name, p) \ +- deb_info(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ +- *p, *(p+1), *(p+2), *(p+3), *(p+4), \ +- *(p+5), *(p+6), *(p+7)); +- +- +-module_param_named(debug, dvb_usb_lme2510_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." +- DVB_USB_DEBUG_STATUS); +- +-static int dvb_usb_lme2510_firmware; +-module_param_named(firmware, dvb_usb_lme2510_firmware, int, 0644); +-MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG"); +- +-static int pid_filter; +-module_param_named(pid, pid_filter, int, 0644); +-MODULE_PARM_DESC(pid, "set default 0=on 1=off"); +- +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define TUNER_DEFAULT 0x0 +-#define TUNER_LG 0x1 +-#define TUNER_S7395 0x2 +-#define TUNER_S0194 0x3 +- +-struct lme2510_state { +- u8 id; +- u8 tuner_config; +- u8 signal_lock; +- u8 signal_level; +- u8 signal_sn; +- u8 time_key; +- u8 i2c_talk_onoff; +- u8 i2c_gate; +- u8 i2c_tuner_gate_w; +- u8 i2c_tuner_gate_r; +- u8 i2c_tuner_addr; +- u8 stream_on; +- u8 pid_size; +- void *buffer; +- struct urb *lme_urb; +- void *usb_buffer; +- +-}; +- +-static int lme2510_bulk_write(struct usb_device *dev, +- u8 *snd, int len, u8 pipe) +-{ +- int ret, actual_l; +- +- ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), +- snd, len , &actual_l, 100); +- return ret; +-} +- +-static int lme2510_bulk_read(struct usb_device *dev, +- u8 *rev, int len, u8 pipe) +-{ +- int ret, actual_l; +- +- ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), +- rev, len , &actual_l, 200); +- return ret; +-} +- +-static int lme2510_usb_talk(struct dvb_usb_device *d, +- u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- struct lme2510_state *st = d->priv; +- u8 *buff; +- int ret = 0; +- +- if (st->usb_buffer == NULL) { +- st->usb_buffer = kmalloc(64, GFP_KERNEL); +- if (st->usb_buffer == NULL) { +- info("MEM Error no memory"); +- return -ENOMEM; +- } +- } +- buff = st->usb_buffer; +- +- ret = mutex_lock_interruptible(&d->usb_mutex); +- +- if (ret < 0) +- return -EAGAIN; +- +- /* the read/write capped at 64 */ +- memcpy(buff, wbuf, (wlen < 64) ? wlen : 64); +- +- ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01)); +- +- ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01); +- +- msleep(10); +- +- ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01)); +- +- ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ? +- rlen : 64 , 0x01); +- +- if (rlen > 0) +- memcpy(rbuf, buff, rlen); +- +- mutex_unlock(&d->usb_mutex); +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int lme2510_stream_restart(struct dvb_usb_device *d) +-{ +- static u8 stream_on[] = LME_ST_ON_W; +- int ret; +- u8 rbuff[10]; +- /*Restart Stream Command*/ +- ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on), +- rbuff, sizeof(rbuff)); +- return ret; +-} +- +-static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) +-{ +- struct lme2510_state *st = d->priv; +- static u8 pid_buff[] = LME_ZERO_PID; +- static u8 rbuf[1]; +- u8 pid_no = index * 2; +- u8 pid_len = pid_no + 2; +- int ret = 0; +- deb_info(1, "PID Setting Pid %04x", pid_out); +- +- if (st->pid_size == 0) +- ret |= lme2510_stream_restart(d); +- +- pid_buff[2] = pid_no; +- pid_buff[3] = (u8)pid_out & 0xff; +- pid_buff[4] = pid_no + 1; +- pid_buff[5] = (u8)(pid_out >> 8); +- +- if (pid_len > st->pid_size) +- st->pid_size = pid_len; +- pid_buff[7] = 0x80 + st->pid_size; +- +- ret |= lme2510_usb_talk(d, pid_buff , +- sizeof(pid_buff) , rbuf, sizeof(rbuf)); +- +- if (st->stream_on) +- ret |= lme2510_stream_restart(d); +- +- return ret; +-} +- +-static void lme2510_int_response(struct urb *lme_urb) +-{ +- struct dvb_usb_adapter *adap = lme_urb->context; +- struct lme2510_state *st = adap->dev->priv; +- static u8 *ibuf, *rbuf; +- int i = 0, offset; +- u32 key; +- +- switch (lme_urb->status) { +- case 0: +- case -ETIMEDOUT: +- break; +- case -ECONNRESET: +- case -ENOENT: +- case -ESHUTDOWN: +- return; +- default: +- info("Error %x", lme_urb->status); +- break; +- } +- +- rbuf = (u8 *) lme_urb->transfer_buffer; +- +- offset = ((lme_urb->actual_length/8) > 4) +- ? 4 : (lme_urb->actual_length/8) ; +- +- for (i = 0; i < offset; ++i) { +- ibuf = (u8 *)&rbuf[i*8]; +- deb_info(5, "INT O/S C =%02x C/O=%02x Type =%02x%02x", +- offset, i, ibuf[0], ibuf[1]); +- +- switch (ibuf[0]) { +- case 0xaa: +- debug_data_snipet(1, "INT Remote data snipet", ibuf); +- if ((ibuf[4] + ibuf[5]) == 0xff) { +- key = ibuf[5]; +- key += (ibuf[3] > 0) +- ? (ibuf[3] ^ 0xff) << 8 : 0; +- key += (ibuf[2] ^ 0xff) << 16; +- deb_info(1, "INT Key =%08x", key); +- if (adap->dev->rc_dev != NULL) +- rc_keydown(adap->dev->rc_dev, key, 0); +- } +- break; +- case 0xbb: +- switch (st->tuner_config) { +- case TUNER_LG: +- if (ibuf[2] > 0) +- st->signal_lock = ibuf[2]; +- st->signal_level = ibuf[4]; +- st->signal_sn = ibuf[3]; +- st->time_key = ibuf[7]; +- break; +- case TUNER_S7395: +- case TUNER_S0194: +- /* Tweak for earlier firmware*/ +- if (ibuf[1] == 0x03) { +- if (ibuf[2] > 1) +- st->signal_lock = ibuf[2]; +- st->signal_level = ibuf[3]; +- st->signal_sn = ibuf[4]; +- } else { +- st->signal_level = ibuf[4]; +- st->signal_sn = ibuf[5]; +- st->signal_lock = +- (st->signal_lock & 0xf7) + +- ((ibuf[2] & 0x01) << 0x03); +- } +- break; +- default: +- break; +- } +- debug_data_snipet(5, "INT Remote data snipet in", ibuf); +- break; +- case 0xcc: +- debug_data_snipet(1, "INT Control data snipet", ibuf); +- break; +- default: +- debug_data_snipet(1, "INT Unknown data snipet", ibuf); +- break; +- } +- } +- usb_submit_urb(lme_urb, GFP_ATOMIC); +-} +- +-static int lme2510_int_read(struct dvb_usb_adapter *adap) +-{ +- struct lme2510_state *lme_int = adap->dev->priv; +- +- lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); +- +- if (lme_int->lme_urb == NULL) +- return -ENOMEM; +- +- lme_int->buffer = usb_alloc_coherent(adap->dev->udev, 128, GFP_ATOMIC, +- &lme_int->lme_urb->transfer_dma); +- +- if (lme_int->buffer == NULL) +- return -ENOMEM; +- +- usb_fill_int_urb(lme_int->lme_urb, +- adap->dev->udev, +- usb_rcvintpipe(adap->dev->udev, 0xa), +- lme_int->buffer, +- 128, +- lme2510_int_response, +- adap, +- 8); +- +- lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +- +- usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); +- info("INT Interrupt Service Started"); +- +- return 0; +-} +- +-static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct lme2510_state *st = adap->dev->priv; +- static u8 clear_pid_reg[] = LME_CLEAR_PID; +- static u8 rbuf[1]; +- int ret; +- +- deb_info(1, "PID Clearing Filter"); +- +- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex); +- if (ret < 0) +- return -EAGAIN; +- +- if (!onoff) +- ret |= lme2510_usb_talk(adap->dev, clear_pid_reg, +- sizeof(clear_pid_reg), rbuf, sizeof(rbuf)); +- +- st->pid_size = 0; +- +- mutex_unlock(&adap->dev->i2c_mutex); +- +- return 0; +-} +- +-static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, +- int onoff) +-{ +- int ret = 0; +- +- deb_info(3, "%s PID=%04x Index=%04x onoff=%02x", __func__, +- pid, index, onoff); +- +- if (onoff) { +- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex); +- if (ret < 0) +- return -EAGAIN; +- ret |= lme2510_enable_pid(adap->dev, index, pid); +- mutex_unlock(&adap->dev->i2c_mutex); +- } +- +- +- return ret; +-} +- +- +-static int lme2510_return_status(struct usb_device *dev) +-{ +- int ret = 0; +- u8 *data; +- +- data = kzalloc(10, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- ret |= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), +- 0x06, 0x80, 0x0302, 0x00, data, 0x0006, 200); +- info("Firmware Status: %x (%x)", ret , data[2]); +- +- ret = (ret < 0) ? -ENODEV : data[2]; +- kfree(data); +- return ret; +-} +- +-static int lme2510_msg(struct dvb_usb_device *d, +- u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- int ret = 0; +- struct lme2510_state *st = d->priv; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- if (st->i2c_talk_onoff == 1) { +- +- ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); +- +- switch (st->tuner_config) { +- case TUNER_LG: +- if (wbuf[2] == 0x1c) { +- if (wbuf[3] == 0x0e) { +- st->signal_lock = rbuf[1]; +- if ((st->stream_on & 1) && +- (st->signal_lock & 0x10)) { +- lme2510_stream_restart(d); +- st->i2c_talk_onoff = 0; +- } +- msleep(80); +- } +- } +- break; +- case TUNER_S7395: +- if (wbuf[2] == 0xd0) { +- if (wbuf[3] == 0x24) { +- st->signal_lock = rbuf[1]; +- if ((st->stream_on & 1) && +- (st->signal_lock & 0x8)) { +- lme2510_stream_restart(d); +- st->i2c_talk_onoff = 0; +- } +- } +- if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5)) +- msleep(5); +- } +- break; +- case TUNER_S0194: +- if (wbuf[2] == 0xd0) { +- if (wbuf[3] == 0x1b) { +- st->signal_lock = rbuf[1]; +- if ((st->stream_on & 1) && +- (st->signal_lock & 0x8)) { +- lme2510_stream_restart(d); +- st->i2c_talk_onoff = 0; +- } +- } +- } +- break; +- default: +- break; +- } +- } else { +- switch (st->tuner_config) { +- case TUNER_LG: +- switch (wbuf[3]) { +- case 0x0e: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_lock; +- break; +- case 0x43: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_level; +- break; +- case 0x1c: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_sn; +- break; +- case 0x15: +- case 0x16: +- case 0x17: +- case 0x18: +- rbuf[0] = 0x55; +- rbuf[1] = 0x00; +- break; +- default: +- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); +- st->i2c_talk_onoff = 1; +- break; +- } +- break; +- case TUNER_S7395: +- switch (wbuf[3]) { +- case 0x10: +- rbuf[0] = 0x55; +- rbuf[1] = (st->signal_level & 0x80) +- ? 0 : (st->signal_level * 2); +- break; +- case 0x2d: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_sn; +- break; +- case 0x24: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_lock; +- break; +- case 0x2e: +- case 0x26: +- case 0x27: +- rbuf[0] = 0x55; +- rbuf[1] = 0x00; +- break; +- default: +- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); +- st->i2c_talk_onoff = 1; +- break; +- } +- break; +- case TUNER_S0194: +- switch (wbuf[3]) { +- case 0x18: +- rbuf[0] = 0x55; +- rbuf[1] = (st->signal_level & 0x80) +- ? 0 : (st->signal_level * 2); +- break; +- case 0x24: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_sn; +- break; +- case 0x1b: +- rbuf[0] = 0x55; +- rbuf[1] = st->signal_lock; +- break; +- case 0x19: +- case 0x25: +- case 0x1e: +- case 0x1d: +- rbuf[0] = 0x55; +- rbuf[1] = 0x00; +- break; +- default: +- lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); +- st->i2c_talk_onoff = 1; +- break; +- } +- break; +- default: +- break; +- } +- +- deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)", +- wbuf[3], rbuf[1]); +- +- } +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +- +-static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct lme2510_state *st = d->priv; +- static u8 obuf[64], ibuf[64]; +- int i, read, read_o; +- u16 len; +- u8 gate = st->i2c_gate; +- +- if (gate == 0) +- gate = 5; +- +- if (num > 2) +- warn("more than 2 i2c messages" +- "at a time is not handled yet. TODO."); +- +- for (i = 0; i < num; i++) { +- read_o = 1 & (msg[i].flags & I2C_M_RD); +- read = i+1 < num && (msg[i+1].flags & I2C_M_RD); +- read |= read_o; +- gate = (msg[i].addr == st->i2c_tuner_addr) +- ? (read) ? st->i2c_tuner_gate_r +- : st->i2c_tuner_gate_w +- : st->i2c_gate; +- obuf[0] = gate | (read << 7); +- +- if (gate == 5) +- obuf[1] = (read) ? 2 : msg[i].len + 1; +- else +- obuf[1] = msg[i].len + read + 1; +- +- obuf[2] = msg[i].addr; +- if (read) { +- if (read_o) +- len = 3; +- else { +- memcpy(&obuf[3], msg[i].buf, msg[i].len); +- obuf[msg[i].len+3] = msg[i+1].len; +- len = msg[i].len+4; +- } +- } else { +- memcpy(&obuf[3], msg[i].buf, msg[i].len); +- len = msg[i].len+3; +- } +- +- if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) { +- deb_info(1, "i2c transfer failed."); +- return -EAGAIN; +- } +- +- if (read) { +- if (read_o) +- memcpy(msg[i].buf, &ibuf[1], msg[i].len); +- else { +- memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); +- i++; +- } +- } +- } +- return i; +-} +- +-static u32 lme2510_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm lme2510_i2c_algo = { +- .master_xfer = lme2510_i2c_xfer, +- .functionality = lme2510_i2c_func, +-}; +- +-/* Callbacks for DVB USB */ +-static int lme2510_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- if (pid_filter > 0) +- props->adapter[0].fe[0].caps &= +- ~DVB_USB_ADAP_NEED_PID_FILTERING; +- *cold = 0; +- return 0; +-} +- +-static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct lme2510_state *st = adap->dev->priv; +- static u8 clear_reg_3[] = LME_CLEAR_PID; +- static u8 rbuf[1]; +- int ret = 0, rlen = sizeof(rbuf); +- +- deb_info(1, "STM (%02x)", onoff); +- +- /* Streaming is started by FE_HAS_LOCK */ +- if (onoff == 1) +- st->stream_on = 1; +- else { +- deb_info(1, "STM Steam Off"); +- /* mutex is here only to avoid collision with I2C */ +- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) +- return -EAGAIN; +- +- ret = lme2510_usb_talk(adap->dev, clear_reg_3, +- sizeof(clear_reg_3), rbuf, rlen); +- st->stream_on = 0; +- st->i2c_talk_onoff = 1; +- +- mutex_unlock(&adap->dev->i2c_mutex); +- } +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static u8 check_sum(u8 *p, u8 len) +-{ +- u8 sum = 0; +- while (len--) +- sum += *p++; +- return sum; +-} +- +-static int lme2510_download_firmware(struct usb_device *dev, +- const struct firmware *fw) +-{ +- int ret = 0; +- u8 *data; +- u16 j, wlen, len_in, start, end; +- u8 packet_size, dlen, i; +- u8 *fw_data; +- +- packet_size = 0x31; +- len_in = 1; +- +- data = kzalloc(512, GFP_KERNEL); +- if (!data) { +- info("FRM Could not start Firmware Download (Buffer allocation failed)"); +- return -ENOMEM; +- } +- +- info("FRM Starting Firmware Download"); +- +- for (i = 1; i < 3; i++) { +- start = (i == 1) ? 0 : 512; +- end = (i == 1) ? 512 : fw->size; +- for (j = start; j < end; j += (packet_size+1)) { +- fw_data = (u8 *)(fw->data + j); +- if ((end - j) > packet_size) { +- data[0] = i; +- dlen = packet_size; +- } else { +- data[0] = i | 0x80; +- dlen = (u8)(end - j)-1; +- } +- data[1] = dlen; +- memcpy(&data[2], fw_data, dlen+1); +- wlen = (u8) dlen + 4; +- data[wlen-1] = check_sum(fw_data, dlen+1); +- deb_info(1, "Data S=%02x:E=%02x CS= %02x", data[3], +- data[dlen+2], data[dlen+3]); +- ret |= lme2510_bulk_write(dev, data, wlen, 1); +- ret |= lme2510_bulk_read(dev, data, len_in , 1); +- ret |= (data[0] == 0x88) ? 0 : -1; +- } +- } +- +- usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), +- 0x06, 0x80, 0x0200, 0x00, data, 0x0109, 1000); +- +- +- data[0] = 0x8a; +- len_in = 1; +- msleep(2000); +- ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Resetting*/ +- ret |= lme2510_bulk_read(dev, data, len_in, 1); +- msleep(400); +- +- if (ret < 0) +- info("FRM Firmware Download Failed (%04x)" , ret); +- else +- info("FRM Firmware Download Completed - Resetting Device"); +- +- kfree(data); +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static void lme_coldreset(struct usb_device *dev) +-{ +- int ret = 0, len_in; +- u8 data[512] = {0}; +- +- data[0] = 0x0a; +- len_in = 1; +- info("FRM Firmware Cold Reset"); +- ret |= lme2510_bulk_write(dev, data , len_in, 1); /*Cold Resetting*/ +- ret |= lme2510_bulk_read(dev, data, len_in, 1); +- +- return; +-} +- +-static int lme_firmware_switch(struct usb_device *udev, int cold) +-{ +- const struct firmware *fw = NULL; +- const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw"; +- const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw"; +- const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw"; +- const char fw_lg[] = "dvb-usb-lme2510-lg.fw"; +- const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw"; +- const char *fw_lme; +- int ret, cold_fw; +- +- cold = (cold > 0) ? (cold & 1) : 0; +- +- cold_fw = !cold; +- +- if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) { +- switch (dvb_usb_lme2510_firmware) { +- default: +- dvb_usb_lme2510_firmware = TUNER_S0194; +- case TUNER_S0194: +- fw_lme = fw_s0194; +- ret = request_firmware(&fw, fw_lme, &udev->dev); +- if (ret == 0) { +- cold = 0; +- break; +- } +- dvb_usb_lme2510_firmware = TUNER_LG; +- case TUNER_LG: +- fw_lme = fw_lg; +- ret = request_firmware(&fw, fw_lme, &udev->dev); +- if (ret == 0) +- break; +- info("FRM No Firmware Found - please install"); +- dvb_usb_lme2510_firmware = TUNER_DEFAULT; +- cold = 0; +- cold_fw = 0; +- break; +- } +- } else { +- switch (dvb_usb_lme2510_firmware) { +- default: +- dvb_usb_lme2510_firmware = TUNER_S7395; +- case TUNER_S7395: +- fw_lme = fw_c_s7395; +- ret = request_firmware(&fw, fw_lme, &udev->dev); +- if (ret == 0) { +- cold = 0; +- break; +- } +- dvb_usb_lme2510_firmware = TUNER_LG; +- case TUNER_LG: +- fw_lme = fw_c_lg; +- ret = request_firmware(&fw, fw_lme, &udev->dev); +- if (ret == 0) +- break; +- dvb_usb_lme2510_firmware = TUNER_S0194; +- case TUNER_S0194: +- fw_lme = fw_c_s0194; +- ret = request_firmware(&fw, fw_lme, &udev->dev); +- if (ret == 0) +- break; +- info("FRM No Firmware Found - please install"); +- dvb_usb_lme2510_firmware = TUNER_DEFAULT; +- cold = 0; +- cold_fw = 0; +- break; +- } +- } +- +- if (cold_fw) { +- info("FRM Loading %s file", fw_lme); +- ret = lme2510_download_firmware(udev, fw); +- } +- +- release_firmware(fw); +- +- if (cold) { +- info("FRM Changing to %s firmware", fw_lme); +- lme_coldreset(udev); +- return -ENODEV; +- } +- +- return ret; +-} +- +-static int lme2510_kill_urb(struct usb_data_stream *stream) +-{ +- int i; +- +- for (i = 0; i < stream->urbs_submitted; i++) { +- deb_info(3, "killing URB no. %d.", i); +- /* stop the URB */ +- usb_kill_urb(stream->urb_list[i]); +- } +- stream->urbs_submitted = 0; +- +- return 0; +-} +- +-static struct tda10086_config tda10086_config = { +- .demod_address = 0x1c, +- .invert = 0, +- .diseqc_tone = 1, +- .xtal_freq = TDA10086_XTAL_16M, +-}; +- +-static struct stv0288_config lme_config = { +- .demod_address = 0xd0, +- .min_delay_ms = 15, +- .inittab = s7395_inittab, +-}; +- +-static struct ix2505v_config lme_tuner = { +- .tuner_address = 0xc0, +- .min_delay_ms = 100, +- .tuner_gain = 0x0, +- .tuner_chargepump = 0x3, +-}; +- +-static struct stv0299_config sharp_z0194_config = { +- .demod_address = 0xd0, +- .inittab = sharp_z0194a_inittab, +- .mclk = 88000000UL, +- .invert = 0, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 100, +- .set_symbol_rate = sharp_z0194a_set_symbol_rate, +-}; +- +-static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- static u8 voltage_low[] = LME_VOLTAGE_L; +- static u8 voltage_high[] = LME_VOLTAGE_H; +- static u8 rbuf[1]; +- int ret = 0, len = 3, rlen = 1; +- +- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0) +- return -EAGAIN; +- +- switch (voltage) { +- case SEC_VOLTAGE_18: +- ret |= lme2510_usb_talk(adap->dev, +- voltage_high, len, rbuf, rlen); +- break; +- +- case SEC_VOLTAGE_OFF: +- case SEC_VOLTAGE_13: +- default: +- ret |= lme2510_usb_talk(adap->dev, +- voltage_low, len, rbuf, rlen); +- break; +- } +- +- mutex_unlock(&adap->dev->i2c_mutex); +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int lme_name(struct dvb_usb_adapter *adap) +-{ +- struct lme2510_state *st = adap->dev->priv; +- const char *desc = adap->dev->desc->name; +- char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395", +- " SHARP:BS2F7HZ0194"}; +- char *name = adap->fe_adap[0].fe->ops.info.name; +- +- strlcpy(name, desc, 128); +- strlcat(name, fe_name[st->tuner_config], 128); +- +- return 0; +-} +- +-static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct lme2510_state *st = adap->dev->priv; +- +- int ret = 0; +- +- st->i2c_talk_onoff = 1; +- +- st->i2c_gate = 4; +- adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, +- &adap->dev->i2c_adap); +- +- if (adap->fe_adap[0].fe) { +- info("TUN Found Frontend TDA10086"); +- st->i2c_tuner_gate_w = 4; +- st->i2c_tuner_gate_r = 4; +- st->i2c_tuner_addr = 0xc0; +- st->tuner_config = TUNER_LG; +- if (dvb_usb_lme2510_firmware != TUNER_LG) { +- dvb_usb_lme2510_firmware = TUNER_LG; +- ret = lme_firmware_switch(adap->dev->udev, 1); +- } +- goto end; +- } +- +- st->i2c_gate = 4; +- adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe) { +- info("FE Found Stv0299"); +- st->i2c_tuner_gate_w = 4; +- st->i2c_tuner_gate_r = 5; +- st->i2c_tuner_addr = 0xc0; +- st->tuner_config = TUNER_S0194; +- if (dvb_usb_lme2510_firmware != TUNER_S0194) { +- dvb_usb_lme2510_firmware = TUNER_S0194; +- ret = lme_firmware_switch(adap->dev->udev, 1); +- } +- goto end; +- } +- +- st->i2c_gate = 5; +- adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[0].fe) { +- info("FE Found Stv0288"); +- st->i2c_tuner_gate_w = 4; +- st->i2c_tuner_gate_r = 5; +- st->i2c_tuner_addr = 0xc0; +- st->tuner_config = TUNER_S7395; +- if (dvb_usb_lme2510_firmware != TUNER_S7395) { +- dvb_usb_lme2510_firmware = TUNER_S7395; +- ret = lme_firmware_switch(adap->dev->udev, 1); +- } +- } else { +- info("DM04 Not Supported"); +- return -ENODEV; +- } +- +- +-end: if (ret) { +- if (adap->fe_adap[0].fe) { +- dvb_frontend_detach(adap->fe_adap[0].fe); +- adap->fe_adap[0].fe = NULL; +- } +- adap->dev->props.rc.core.rc_codes = NULL; +- return -ENODEV; +- } +- +- adap->fe_adap[0].fe->ops.set_voltage = dm04_lme2510_set_voltage; +- ret = lme_name(adap); +- return ret; +-} +- +-static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap) +-{ +- struct lme2510_state *st = adap->dev->priv; +- char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"}; +- int ret = 0; +- +- switch (st->tuner_config) { +- case TUNER_LG: +- if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0xc0, +- &adap->dev->i2c_adap, 1)) +- ret = st->tuner_config; +- break; +- case TUNER_S7395: +- if (dvb_attach(ix2505v_attach , adap->fe_adap[0].fe, &lme_tuner, +- &adap->dev->i2c_adap)) +- ret = st->tuner_config; +- break; +- case TUNER_S0194: +- if (dvb_attach(dvb_pll_attach , adap->fe_adap[0].fe, 0xc0, +- &adap->dev->i2c_adap, DVB_PLL_OPERA1)) +- ret = st->tuner_config; +- break; +- default: +- break; +- } +- +- if (ret) +- info("TUN Found %s tuner", tun_msg[ret]); +- else { +- info("TUN No tuner found --- reseting device"); +- lme_coldreset(adap->dev->udev); +- return -ENODEV; +- } +- +- /* Start the Interrupt*/ +- ret = lme2510_int_read(adap); +- if (ret < 0) { +- info("INT Unable to start Interrupt Service"); +- return -ENODEV; +- } +- +- return ret; +-} +- +-static int lme2510_powerup(struct dvb_usb_device *d, int onoff) +-{ +- struct lme2510_state *st = d->priv; +- static u8 lnb_on[] = LNB_ON; +- static u8 lnb_off[] = LNB_OFF; +- static u8 rbuf[1]; +- int ret, len = 3, rlen = 1; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- if (onoff) +- ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen); +- else +- ret = lme2510_usb_talk(d, lnb_off, len, rbuf, rlen); +- +- st->i2c_talk_onoff = 1; +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties lme2510_properties; +-static struct dvb_usb_device_properties lme2510c_properties; +- +-static int lme2510_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- int ret = 0; +- +- usb_reset_configuration(udev); +- +- usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1); +- +- if (udev->speed != USB_SPEED_HIGH) { +- ret = usb_reset_device(udev); +- info("DEV Failed to connect in HIGH SPEED mode"); +- return -ENODEV; +- } +- +- if (lme2510_return_status(udev) == 0x44) { +- lme_firmware_switch(udev, 0); +- return -ENODEV; +- } +- +- if (0 == dvb_usb_device_init(intf, &lme2510_properties, +- THIS_MODULE, NULL, adapter_nr)) { +- info("DEV registering device driver"); +- return 0; +- } +- if (0 == dvb_usb_device_init(intf, &lme2510c_properties, +- THIS_MODULE, NULL, adapter_nr)) { +- info("DEV registering device driver"); +- return 0; +- } +- +- info("DEV lme2510 Error"); +- return -ENODEV; +- +-} +- +-static struct usb_device_id lme2510_table[] = { +- { USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */ +- { USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */ +- {} /* Terminating entry */ +-}; +- +-MODULE_DEVICE_TABLE(usb, lme2510_table); +- +-static struct dvb_usb_device_properties lme2510_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .size_of_priv = sizeof(struct lme2510_state), +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER| +- DVB_USB_ADAP_NEED_PID_FILTERING| +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .streaming_ctrl = lme2510_streaming_ctrl, +- .pid_filter_count = 15, +- .pid_filter = lme2510_pid_filter, +- .pid_filter_ctrl = lme2510_pid_filter_ctrl, +- .frontend_attach = dm04_lme2510_frontend_attach, +- .tuner_attach = dm04_lme2510_tuner, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- +- } +- } +- } +- }}, +- } +- }, +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "LME2510 Remote Control", +- .allowed_protos = RC_TYPE_NEC, +- .rc_codes = RC_MAP_LME2510, +- }, +- .power_ctrl = lme2510_powerup, +- .identify_state = lme2510_identify_state, +- .i2c_algo = &lme2510_i2c_algo, +- .generic_bulk_ctrl_endpoint = 0, +- .num_device_descs = 1, +- .devices = { +- { "DM04_LME2510_DVB-S", +- { &lme2510_table[0], NULL }, +- }, +- +- } +-}; +- +-static struct dvb_usb_device_properties lme2510c_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .size_of_priv = sizeof(struct lme2510_state), +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER| +- DVB_USB_ADAP_NEED_PID_FILTERING| +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .streaming_ctrl = lme2510_streaming_ctrl, +- .pid_filter_count = 15, +- .pid_filter = lme2510_pid_filter, +- .pid_filter_ctrl = lme2510_pid_filter_ctrl, +- .frontend_attach = dm04_lme2510_frontend_attach, +- .tuner_attach = dm04_lme2510_tuner, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x8, +- .u = { +- .bulk = { +- .buffersize = 4096, +- +- } +- } +- } +- }}, +- } +- }, +- .rc.core = { +- .protocol = RC_TYPE_NEC, +- .module_name = "LME2510 Remote Control", +- .allowed_protos = RC_TYPE_NEC, +- .rc_codes = RC_MAP_LME2510, +- }, +- .power_ctrl = lme2510_powerup, +- .identify_state = lme2510_identify_state, +- .i2c_algo = &lme2510_i2c_algo, +- .generic_bulk_ctrl_endpoint = 0, +- .num_device_descs = 1, +- .devices = { +- { "DM04_LME2510C_DVB-S", +- { &lme2510_table[1], NULL }, +- }, +- } +-}; +- +-static void *lme2510_exit_int(struct dvb_usb_device *d) +-{ +- struct lme2510_state *st = d->priv; +- struct dvb_usb_adapter *adap = &d->adapter[0]; +- void *buffer = NULL; +- +- if (adap != NULL) { +- lme2510_kill_urb(&adap->fe_adap[0].stream); +- adap->feedcount = 0; +- } +- +- if (st->usb_buffer != NULL) { +- st->i2c_talk_onoff = 1; +- st->signal_lock = 0; +- st->signal_level = 0; +- st->signal_sn = 0; +- buffer = st->usb_buffer; +- } +- +- if (st->lme_urb != NULL) { +- usb_kill_urb(st->lme_urb); +- usb_free_coherent(d->udev, 128, st->buffer, +- st->lme_urb->transfer_dma); +- info("Interrupt Service Stopped"); +- } +- +- return buffer; +-} +- +-static void lme2510_exit(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- void *usb_buffer; +- +- if (d != NULL) { +- usb_buffer = lme2510_exit_int(d); +- dvb_usb_device_exit(intf); +- if (usb_buffer != NULL) +- kfree(usb_buffer); +- } +-} +- +-static struct usb_driver lme2510_driver = { +- .name = "LME2510C_DVB-S", +- .probe = lme2510_probe, +- .disconnect = lme2510_exit, +- .id_table = lme2510_table, +-}; +- +-module_usb_driver(lme2510_driver); +- +-MODULE_AUTHOR("Malcolm Priestley "); +-MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0"); +-MODULE_VERSION("1.91"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h +deleted file mode 100644 +index ab21e2e..0000000 +--- a/drivers/media/dvb/dvb-usb/lmedm04.h ++++ /dev/null +@@ -1,174 +0,0 @@ +-/* DVB USB compliant linux driver for +- * +- * DM04/QQBOX DVB-S USB BOX LME2510C + SHARP:BS2F7HZ7395 +- * LME2510C + LG TDQY-P001F +- * LME2510 + LG TDQY-P001F +- * +- * MVB7395 (LME2510C+SHARP:BS2F7HZ7395) +- * SHARP:BS2F7HZ7395 = (STV0288+Sharp IX2505V) +- * +- * MVB001F (LME2510+LGTDQT-P001F) +- * LG TDQY - P001F =(TDA8263 + TDA10086H) +- * +- * MVB0001F (LME2510C+LGTDQT-P001F) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_LME2510_H_ +-#define _DVB_USB_LME2510_H_ +- +-/* Streamer & PID +- * +- * Note: These commands do not actually stop the streaming +- * but form some kind of packet filtering/stream count +- * or tuning related functions. +- * 06 XX +- * offset 1 = 00 Enable Streaming +- * +- * +- * PID +- * 03 XX XX ----> reg number ---> setting....20 XX +- * offset 1 = length +- * offset 2 = start of data +- * end byte -1 = 20 +- * end byte = clear pid always a0, other wise 9c, 9a ?? +- * +-*/ +-#define LME_ST_ON_W {0x06, 0x00} +-#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0} +-#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c} +- +-/* LNB Voltage +- * 07 XX XX +- * offset 1 = 01 +- * offset 2 = 00=Voltage low 01=Voltage high +- * +- * LNB Power +- * 03 01 XX +- * offset 2 = 00=ON 01=OFF +- */ +- +-#define LME_VOLTAGE_L {0x07, 0x01, 0x00} +-#define LME_VOLTAGE_H {0x07, 0x01, 0x01} +-#define LNB_ON {0x3a, 0x01, 0x00} +-#define LNB_OFF {0x3a, 0x01, 0x01} +- +-/* Initial stv0288 settings for 7395 Frontend */ +-static u8 s7395_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x20, +- 0x03, 0xa0, +- 0x04, 0xa0, +- 0x05, 0x12, +- 0x06, 0x00, +- 0x09, 0x00, +- 0x0a, 0x04, +- 0x0b, 0x00, +- 0x0c, 0x00, +- 0x0d, 0x00, +- 0x0e, 0xc1, +- 0x0f, 0x54, +- 0x11, 0x7a, +- 0x12, 0x03, +- 0x13, 0x48, +- 0x14, 0x84, +- 0x15, 0xc5, +- 0x16, 0xb8, +- 0x17, 0x9c, +- 0x18, 0x00, +- 0x19, 0xa6, +- 0x1a, 0x88, +- 0x1b, 0x8f, +- 0x1c, 0xf0, +- 0x20, 0x0b, +- 0x21, 0x54, +- 0x22, 0xff, +- 0x23, 0x01, +- 0x28, 0x46, +- 0x29, 0x66, +- 0x2a, 0x90, +- 0x2b, 0xfa, +- 0x2c, 0xd9, +- 0x30, 0x0, +- 0x31, 0x1e, +- 0x32, 0x14, +- 0x33, 0x0f, +- 0x34, 0x09, +- 0x35, 0x0c, +- 0x36, 0x05, +- 0x37, 0x2f, +- 0x38, 0x16, +- 0x39, 0xbd, +- 0x3a, 0x0, +- 0x3b, 0x13, +- 0x3c, 0x11, +- 0x3d, 0x30, +- 0x40, 0x63, +- 0x41, 0x04, +- 0x42, 0x20, +- 0x43, 0x00, +- 0x44, 0x00, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x47, 0x00, +- 0x4a, 0x00, +- 0x50, 0x10, +- 0x51, 0x36, +- 0x52, 0x21, +- 0x53, 0x94, +- 0x54, 0xb2, +- 0x55, 0x29, +- 0x56, 0x64, +- 0x57, 0x2b, +- 0x58, 0x54, +- 0x59, 0x86, +- 0x5a, 0x00, +- 0x5b, 0x9b, +- 0x5c, 0x08, +- 0x5d, 0x7f, +- 0x5e, 0xff, +- 0x5f, 0x8d, +- 0x70, 0x0, +- 0x71, 0x0, +- 0x72, 0x0, +- 0x74, 0x0, +- 0x75, 0x0, +- 0x76, 0x0, +- 0x81, 0x0, +- 0x82, 0x3f, +- 0x83, 0x3f, +- 0x84, 0x0, +- 0x85, 0x0, +- 0x88, 0x0, +- 0x89, 0x0, +- 0x8a, 0x0, +- 0x8b, 0x0, +- 0x8c, 0x0, +- 0x90, 0x0, +- 0x91, 0x0, +- 0x92, 0x0, +- 0x93, 0x0, +- 0x94, 0x1c, +- 0x97, 0x0, +- 0xa0, 0x48, +- 0xa1, 0x0, +- 0xb0, 0xb8, +- 0xb1, 0x3a, +- 0xb2, 0x10, +- 0xb3, 0x82, +- 0xb4, 0x80, +- 0xb5, 0x82, +- 0xb6, 0x82, +- 0xb7, 0x82, +- 0xb8, 0x20, +- 0xb9, 0x0, +- 0xf0, 0x0, +- 0xf1, 0x0, +- 0xf2, 0xc0, +- 0xff, 0xff, +-}; +-#endif +diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c +deleted file mode 100644 +index 288af29..0000000 +--- a/drivers/media/dvb/dvb-usb/m920x.c ++++ /dev/null +@@ -1,1099 +0,0 @@ +-/* DVB USB compliant linux driver for MSI Mega Sky 580 DVB-T USB2.0 receiver +- * +- * Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org) +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the +- * Free Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +- +-#include "m920x.h" +- +-#include "mt352.h" +-#include "mt352_priv.h" +-#include "qt1010.h" +-#include "tda1004x.h" +-#include "tda827x.h" +- +-#include +-#include "tuner-simple.h" +-#include +- +-/* debug */ +-static int dvb_usb_m920x_debug; +-module_param_named(debug,dvb_usb_m920x_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid); +- +-static inline int m920x_read(struct usb_device *udev, u8 request, u16 value, +- u16 index, void *data, int size) +-{ +- int ret; +- +- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +- request, USB_TYPE_VENDOR | USB_DIR_IN, +- value, index, data, size, 2000); +- if (ret < 0) { +- printk(KERN_INFO "m920x_read = error: %d\n", ret); +- return ret; +- } +- +- if (ret != size) { +- deb("m920x_read = no data\n"); +- return -EIO; +- } +- +- return 0; +-} +- +-static inline int m920x_write(struct usb_device *udev, u8 request, +- u16 value, u16 index) +-{ +- int ret; +- +- ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), +- request, USB_TYPE_VENDOR | USB_DIR_OUT, +- value, index, NULL, 0, 2000); +- +- return ret; +-} +- +-static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) +-{ +- int ret = 0, i, epi, flags = 0; +- int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; +- +- /* Remote controller init. */ +- if (d->props.rc.legacy.rc_query) { +- deb("Initialising remote control\n"); +- while (rc_seq->address) { +- if ((ret = m920x_write(d->udev, M9206_CORE, +- rc_seq->data, +- rc_seq->address)) != 0) { +- deb("Initialising remote control failed\n"); +- return ret; +- } +- +- rc_seq++; +- } +- +- deb("Initialising remote control success\n"); +- } +- +- for (i = 0; i < d->props.num_adapters; i++) +- flags |= d->adapter[i].props.fe[0].caps; +- +- /* Some devices(Dposh) might crash if we attempt touch at all. */ +- if (flags & DVB_USB_ADAP_HAS_PID_FILTER) { +- for (i = 0; i < d->props.num_adapters; i++) { +- epi = d->adapter[i].props.fe[0].stream.endpoint - 0x81; +- +- if (epi < 0 || epi >= M9206_MAX_ADAPTERS) { +- printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n"); +- return -EINVAL; +- } +- +- adap_enabled[epi] = 1; +- } +- +- for (i = 0; i < M9206_MAX_ADAPTERS; i++) { +- if (adap_enabled[i]) +- continue; +- +- if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x0)) != 0) +- return ret; +- +- if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x02f5)) != 0) +- return ret; +- } +- } +- +- return ret; +-} +- +-static int m920x_init_ep(struct usb_interface *intf) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- struct usb_host_interface *alt; +- +- if ((alt = usb_altnum_to_altsetting(intf, 1)) == NULL) { +- deb("No alt found!\n"); +- return -ENODEV; +- } +- +- return usb_set_interface(udev, alt->desc.bInterfaceNumber, +- alt->desc.bAlternateSetting); +-} +- +-static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- struct m920x_state *m = d->priv; +- int i, ret = 0; +- u8 *rc_state; +- +- rc_state = kmalloc(2, GFP_KERNEL); +- if (!rc_state) +- return -ENOMEM; +- +- if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) +- goto out; +- +- if ((ret = m920x_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) +- goto out; +- +- for (i = 0; i < d->props.rc.legacy.rc_map_size; i++) +- if (rc5_data(&d->props.rc.legacy.rc_map_table[i]) == rc_state[1]) { +- *event = d->props.rc.legacy.rc_map_table[i].keycode; +- +- switch(rc_state[0]) { +- case 0x80: +- *state = REMOTE_NO_KEY_PRESSED; +- goto out; +- +- case 0x88: /* framing error or "invalid code" */ +- case 0x99: +- case 0xc0: +- case 0xd8: +- *state = REMOTE_NO_KEY_PRESSED; +- m->rep_count = 0; +- goto out; +- +- case 0x93: +- case 0x92: +- case 0x83: /* pinnacle PCTV310e */ +- case 0x82: +- m->rep_count = 0; +- *state = REMOTE_KEY_PRESSED; +- goto out; +- +- case 0x91: +- case 0x81: /* pinnacle PCTV310e */ +- /* prevent immediate auto-repeat */ +- if (++m->rep_count > 2) +- *state = REMOTE_KEY_REPEAT; +- else +- *state = REMOTE_NO_KEY_PRESSED; +- goto out; +- +- default: +- deb("Unexpected rc state %02x\n", rc_state[0]); +- *state = REMOTE_NO_KEY_PRESSED; +- goto out; +- } +- } +- +- if (rc_state[1] != 0) +- deb("Unknown rc key %02x\n", rc_state[1]); +- +- *state = REMOTE_NO_KEY_PRESSED; +- +- out: +- kfree(rc_state); +- return ret; +-} +- +-/* I2C */ +-static int m920x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i, j; +- int ret = 0; +- +- if (!num) +- return -EINVAL; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- if (msg[i].flags & (I2C_M_NO_RD_ACK | I2C_M_IGNORE_NAK | I2C_M_TEN) || msg[i].len == 0) { +- /* For a 0 byte message, I think sending the address +- * to index 0x80|0x40 would be the correct thing to +- * do. However, zero byte messages are only used for +- * probing, and since we don't know how to get the +- * slave's ack, we can't probe. */ +- ret = -ENOTSUPP; +- goto unlock; +- } +- /* Send START & address/RW bit */ +- if (!(msg[i].flags & I2C_M_NOSTART)) { +- if ((ret = m920x_write(d->udev, M9206_I2C, +- (msg[i].addr << 1) | +- (msg[i].flags & I2C_M_RD ? 0x01 : 0), 0x80)) != 0) +- goto unlock; +- /* Should check for ack here, if we knew how. */ +- } +- if (msg[i].flags & I2C_M_RD) { +- for (j = 0; j < msg[i].len; j++) { +- /* Last byte of transaction? +- * Send STOP, otherwise send ACK. */ +- int stop = (i+1 == num && j+1 == msg[i].len) ? 0x40 : 0x01; +- +- if ((ret = m920x_read(d->udev, M9206_I2C, 0x0, +- 0x20 | stop, +- &msg[i].buf[j], 1)) != 0) +- goto unlock; +- } +- } else { +- for (j = 0; j < msg[i].len; j++) { +- /* Last byte of transaction? Then send STOP. */ +- int stop = (i+1 == num && j+1 == msg[i].len) ? 0x40 : 0x00; +- +- if ((ret = m920x_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0) +- goto unlock; +- /* Should check for ack here too. */ +- } +- } +- } +- ret = num; +- +- unlock: +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-static u32 m920x_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm m920x_i2c_algo = { +- .master_xfer = m920x_i2c_xfer, +- .functionality = m920x_i2c_func, +-}; +- +-/* pid filter */ +-static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid) +-{ +- int ret = 0; +- +- if (pid >= 0x8000) +- return -EINVAL; +- +- pid |= 0x8000; +- +- if ((ret = m920x_write(d->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0) +- return ret; +- +- if ((ret = m920x_write(d->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0) +- return ret; +- +- return ret; +-} +- +-static int m920x_update_filters(struct dvb_usb_adapter *adap) +-{ +- struct m920x_state *m = adap->dev->priv; +- int enabled = m->filtering_enabled[adap->id]; +- int i, ret = 0, filter = 0; +- int ep = adap->props.fe[0].stream.endpoint; +- +- for (i = 0; i < M9206_MAX_FILTERS; i++) +- if (m->filters[adap->id][i] == 8192) +- enabled = 0; +- +- /* Disable all filters */ +- if ((ret = m920x_set_filter(adap->dev, ep, 1, enabled)) != 0) +- return ret; +- +- for (i = 0; i < M9206_MAX_FILTERS; i++) +- if ((ret = m920x_set_filter(adap->dev, ep, i + 2, 0)) != 0) +- return ret; +- +- /* Set */ +- if (enabled) { +- for (i = 0; i < M9206_MAX_FILTERS; i++) { +- if (m->filters[adap->id][i] == 0) +- continue; +- +- if ((ret = m920x_set_filter(adap->dev, ep, filter + 2, m->filters[adap->id][i])) != 0) +- return ret; +- +- filter++; +- } +- } +- +- return ret; +-} +- +-static int m920x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct m920x_state *m = adap->dev->priv; +- +- m->filtering_enabled[adap->id] = onoff ? 1 : 0; +- +- return m920x_update_filters(adap); +-} +- +-static int m920x_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) +-{ +- struct m920x_state *m = adap->dev->priv; +- +- m->filters[adap->id][index] = onoff ? pid : 0; +- +- return m920x_update_filters(adap); +-} +- +-static int m920x_firmware_download(struct usb_device *udev, const struct firmware *fw) +-{ +- u16 value, index, size; +- u8 *read, *buff; +- int i, pass, ret = 0; +- +- buff = kmalloc(65536, GFP_KERNEL); +- if (buff == NULL) +- return -ENOMEM; +- +- read = kmalloc(4, GFP_KERNEL); +- if (!read) { +- kfree(buff); +- return -ENOMEM; +- } +- +- if ((ret = m920x_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0) +- goto done; +- deb("%x %x %x %x\n", read[0], read[1], read[2], read[3]); +- +- if ((ret = m920x_read(udev, M9206_FW, 0x0, 0x0, read, 1)) != 0) +- goto done; +- deb("%x\n", read[0]); +- +- for (pass = 0; pass < 2; pass++) { +- for (i = 0; i + (sizeof(u16) * 3) < fw->size;) { +- value = get_unaligned_le16(fw->data + i); +- i += sizeof(u16); +- +- index = get_unaligned_le16(fw->data + i); +- i += sizeof(u16); +- +- size = get_unaligned_le16(fw->data + i); +- i += sizeof(u16); +- +- if (pass == 1) { +- /* Will stall if using fw->data ... */ +- memcpy(buff, fw->data + i, size); +- +- ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), +- M9206_FW, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- value, index, buff, size, 20); +- if (ret != size) { +- deb("error while uploading fw!\n"); +- ret = -EIO; +- goto done; +- } +- msleep(3); +- } +- i += size; +- } +- if (i != fw->size) { +- deb("bad firmware file!\n"); +- ret = -EINVAL; +- goto done; +- } +- } +- +- msleep(36); +- +- /* m920x will disconnect itself from the bus after this. */ +- (void) m920x_write(udev, M9206_CORE, 0x01, M9206_FW_GO); +- deb("firmware uploaded!\n"); +- +- done: +- kfree(read); +- kfree(buff); +- +- return ret; +-} +- +-/* Callbacks for DVB USB */ +-static int m920x_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, +- int *cold) +-{ +- struct usb_host_interface *alt; +- +- alt = usb_altnum_to_altsetting(usb_ifnum_to_if(udev, 0), 1); +- *cold = (alt == NULL) ? 1 : 0; +- +- return 0; +-} +- +-/* demod configurations */ +-static int m920x_mt352_demod_init(struct dvb_frontend *fe) +-{ +- int ret; +- u8 config[] = { CONFIG, 0x3d }; +- u8 clock[] = { CLOCK_CTL, 0x30 }; +- u8 reset[] = { RESET, 0x80 }; +- u8 adc_ctl[] = { ADC_CTL_1, 0x40 }; +- u8 agc[] = { AGC_TARGET, 0x1c, 0x20 }; +- u8 sec_agc[] = { 0x69, 0x00, 0xff, 0xff, 0x40, 0xff, 0x00, 0x40, 0x40 }; +- u8 unk1[] = { 0x93, 0x1a }; +- u8 unk2[] = { 0xb5, 0x7a }; +- +- deb("Demod init!\n"); +- +- if ((ret = mt352_write(fe, config, ARRAY_SIZE(config))) != 0) +- return ret; +- if ((ret = mt352_write(fe, clock, ARRAY_SIZE(clock))) != 0) +- return ret; +- if ((ret = mt352_write(fe, reset, ARRAY_SIZE(reset))) != 0) +- return ret; +- if ((ret = mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl))) != 0) +- return ret; +- if ((ret = mt352_write(fe, agc, ARRAY_SIZE(agc))) != 0) +- return ret; +- if ((ret = mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc))) != 0) +- return ret; +- if ((ret = mt352_write(fe, unk1, ARRAY_SIZE(unk1))) != 0) +- return ret; +- if ((ret = mt352_write(fe, unk2, ARRAY_SIZE(unk2))) != 0) +- return ret; +- +- return 0; +-} +- +-static struct mt352_config m920x_mt352_config = { +- .demod_address = 0x0f, +- .no_tuner = 1, +- .demod_init = m920x_mt352_demod_init, +-}; +- +-static struct tda1004x_config m920x_tda10046_08_config = { +- .demod_address = 0x08, +- .invert = 0, +- .invert_oclk = 0, +- .ts_mode = TDA10046_TS_SERIAL, +- .xtal_freq = TDA10046_XTAL_16M, +- .if_freq = TDA10046_FREQ_045, +- .agc_config = TDA10046_AGC_TDA827X, +- .gpio_config = TDA10046_GPTRI, +- .request_firmware = NULL, +-}; +- +-static struct tda1004x_config m920x_tda10046_0b_config = { +- .demod_address = 0x0b, +- .invert = 0, +- .invert_oclk = 0, +- .ts_mode = TDA10046_TS_SERIAL, +- .xtal_freq = TDA10046_XTAL_16M, +- .if_freq = TDA10046_FREQ_045, +- .agc_config = TDA10046_AGC_TDA827X, +- .gpio_config = TDA10046_GPTRI, +- .request_firmware = NULL, /* uses firmware EEPROM */ +-}; +- +-/* tuner configurations */ +-static struct qt1010_config m920x_qt1010_config = { +- .i2c_address = 0x62 +-}; +- +-/* Callbacks for DVB USB */ +-static int m920x_mt352_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, +- &m920x_mt352_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) == NULL) +- return -EIO; +- +- return 0; +-} +- +-static int m920x_tda10046_08_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- adap->fe_adap[0].fe = dvb_attach(tda10046_attach, +- &m920x_tda10046_08_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) == NULL) +- return -EIO; +- +- return 0; +-} +- +-static int m920x_tda10046_0b_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- adap->fe_adap[0].fe = dvb_attach(tda10046_attach, +- &m920x_tda10046_0b_config, +- &adap->dev->i2c_adap); +- if ((adap->fe_adap[0].fe) == NULL) +- return -EIO; +- +- return 0; +-} +- +-static int m920x_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- if (dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, &m920x_qt1010_config) == NULL) +- return -ENODEV; +- +- return 0; +-} +- +-static int m920x_tda8275_60_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, NULL) == NULL) +- return -ENODEV; +- +- return 0; +-} +- +-static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- deb("%s\n",__func__); +- +- if (dvb_attach(tda827x_attach, adap->fe_adap[0].fe, 0x61, &adap->dev->i2c_adap, NULL) == NULL) +- return -ENODEV; +- +- return 0; +-} +- +-static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach(simple_tuner_attach, adap->fe_adap[0].fe, +- &adap->dev->i2c_adap, 0x61, +- TUNER_PHILIPS_FMD1216ME_MK3); +- return 0; +-} +- +-/* device-specific initialization */ +-static struct m920x_inits megasky_rc_init [] = { +- { M9206_RC_INIT2, 0xa8 }, +- { M9206_RC_INIT1, 0x51 }, +- { } /* terminating entry */ +-}; +- +-static struct m920x_inits tvwalkertwin_rc_init [] = { +- { M9206_RC_INIT2, 0x00 }, +- { M9206_RC_INIT1, 0xef }, +- { 0xff28, 0x00 }, +- { 0xff23, 0x00 }, +- { 0xff21, 0x30 }, +- { } /* terminating entry */ +-}; +- +-static struct m920x_inits pinnacle310e_init[] = { +- /* without these the tuner don't work */ +- { 0xff20, 0x9b }, +- { 0xff22, 0x70 }, +- +- /* rc settings */ +- { 0xff50, 0x80 }, +- { M9206_RC_INIT1, 0x00 }, +- { M9206_RC_INIT2, 0xff }, +- { } /* terminating entry */ +-}; +- +-/* ir keymaps */ +-static struct rc_map_table rc_map_megasky_table[] = { +- { 0x0012, KEY_POWER }, +- { 0x001e, KEY_CYCLEWINDOWS }, /* min/max */ +- { 0x0002, KEY_CHANNELUP }, +- { 0x0005, KEY_CHANNELDOWN }, +- { 0x0003, KEY_VOLUMEUP }, +- { 0x0006, KEY_VOLUMEDOWN }, +- { 0x0004, KEY_MUTE }, +- { 0x0007, KEY_OK }, /* TS */ +- { 0x0008, KEY_STOP }, +- { 0x0009, KEY_MENU }, /* swap */ +- { 0x000a, KEY_REWIND }, +- { 0x001b, KEY_PAUSE }, +- { 0x001f, KEY_FASTFORWARD }, +- { 0x000c, KEY_RECORD }, +- { 0x000d, KEY_CAMERA }, /* screenshot */ +- { 0x000e, KEY_COFFEE }, /* "MTS" */ +-}; +- +-static struct rc_map_table rc_map_tvwalkertwin_table[] = { +- { 0x0001, KEY_ZOOM }, /* Full Screen */ +- { 0x0002, KEY_CAMERA }, /* snapshot */ +- { 0x0003, KEY_MUTE }, +- { 0x0004, KEY_REWIND }, +- { 0x0005, KEY_PLAYPAUSE }, /* Play/Pause */ +- { 0x0006, KEY_FASTFORWARD }, +- { 0x0007, KEY_RECORD }, +- { 0x0008, KEY_STOP }, +- { 0x0009, KEY_TIME }, /* Timeshift */ +- { 0x000c, KEY_COFFEE }, /* Recall */ +- { 0x000e, KEY_CHANNELUP }, +- { 0x0012, KEY_POWER }, +- { 0x0015, KEY_MENU }, /* source */ +- { 0x0018, KEY_CYCLEWINDOWS }, /* TWIN PIP */ +- { 0x001a, KEY_CHANNELDOWN }, +- { 0x001b, KEY_VOLUMEDOWN }, +- { 0x001e, KEY_VOLUMEUP }, +-}; +- +-static struct rc_map_table rc_map_pinnacle310e_table[] = { +- { 0x16, KEY_POWER }, +- { 0x17, KEY_FAVORITES }, +- { 0x0f, KEY_TEXT }, +- { 0x48, KEY_PROGRAM }, /* preview */ +- { 0x1c, KEY_EPG }, +- { 0x04, KEY_LIST }, /* record list */ +- { 0x03, KEY_1 }, +- { 0x01, KEY_2 }, +- { 0x06, KEY_3 }, +- { 0x09, KEY_4 }, +- { 0x1d, KEY_5 }, +- { 0x1f, KEY_6 }, +- { 0x0d, KEY_7 }, +- { 0x19, KEY_8 }, +- { 0x1b, KEY_9 }, +- { 0x15, KEY_0 }, +- { 0x0c, KEY_CANCEL }, +- { 0x4a, KEY_CLEAR }, +- { 0x13, KEY_BACK }, +- { 0x00, KEY_TAB }, +- { 0x4b, KEY_UP }, +- { 0x4e, KEY_LEFT }, +- { 0x52, KEY_RIGHT }, +- { 0x51, KEY_DOWN }, +- { 0x4f, KEY_ENTER }, /* could also be KEY_OK */ +- { 0x1e, KEY_VOLUMEUP }, +- { 0x0a, KEY_VOLUMEDOWN }, +- { 0x05, KEY_CHANNELUP }, +- { 0x02, KEY_CHANNELDOWN }, +- { 0x11, KEY_RECORD }, +- { 0x14, KEY_PLAY }, +- { 0x4c, KEY_PAUSE }, +- { 0x1a, KEY_STOP }, +- { 0x40, KEY_REWIND }, +- { 0x12, KEY_FASTFORWARD }, +- { 0x41, KEY_PREVIOUSSONG }, /* Replay */ +- { 0x42, KEY_NEXTSONG }, /* Skip */ +- { 0x54, KEY_CAMERA }, /* Capture */ +-/* { 0x50, KEY_SAP }, */ /* Sap */ +- { 0x47, KEY_CYCLEWINDOWS }, /* Pip */ +- { 0x4d, KEY_SCREEN }, /* FullScreen */ +- { 0x08, KEY_SUBTITLE }, +- { 0x0e, KEY_MUTE }, +-/* { 0x49, KEY_LR }, */ /* L/R */ +- { 0x07, KEY_SLEEP }, /* Hibernate */ +- { 0x08, KEY_VIDEO }, /* A/V */ +- { 0x0e, KEY_MENU }, /* Recall */ +- { 0x45, KEY_ZOOMIN }, +- { 0x46, KEY_ZOOMOUT }, +- { 0x18, KEY_RED }, /* Red */ +- { 0x53, KEY_GREEN }, /* Green */ +- { 0x5e, KEY_YELLOW }, /* Yellow */ +- { 0x5f, KEY_BLUE }, /* Blue */ +-}; +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties megasky_properties; +-static struct dvb_usb_device_properties digivox_mini_ii_properties; +-static struct dvb_usb_device_properties tvwalkertwin_properties; +-static struct dvb_usb_device_properties dposh_properties; +-static struct dvb_usb_device_properties pinnacle_pctv310e_properties; +- +-static int m920x_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d = NULL; +- int ret; +- struct m920x_inits *rc_init_seq = NULL; +- int bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber; +- +- deb("Probing for m920x device at interface %d\n", bInterfaceNumber); +- +- if (bInterfaceNumber == 0) { +- /* Single-tuner device, or first interface on +- * multi-tuner device +- */ +- +- ret = dvb_usb_device_init(intf, &megasky_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) { +- rc_init_seq = megasky_rc_init; +- goto found; +- } +- +- ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) { +- /* No remote control, so no rc_init_seq */ +- goto found; +- } +- +- /* This configures both tuners on the TV Walker Twin */ +- ret = dvb_usb_device_init(intf, &tvwalkertwin_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) { +- rc_init_seq = tvwalkertwin_rc_init; +- goto found; +- } +- +- ret = dvb_usb_device_init(intf, &dposh_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) { +- /* Remote controller not supported yet. */ +- goto found; +- } +- +- ret = dvb_usb_device_init(intf, &pinnacle_pctv310e_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret == 0) { +- rc_init_seq = pinnacle310e_init; +- goto found; +- } +- +- return ret; +- } else { +- /* Another interface on a multi-tuner device */ +- +- /* The LifeView TV Walker Twin gets here, but struct +- * tvwalkertwin_properties already configured both +- * tuners, so there is nothing for us to do here +- */ +- } +- +- found: +- if ((ret = m920x_init_ep(intf)) < 0) +- return ret; +- +- if (d && (ret = m920x_init(d, rc_init_seq)) != 0) +- return ret; +- +- return ret; +-} +- +-static struct usb_device_id m920x_table [] = { +- { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) }, +- { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, +- USB_PID_MSI_DIGI_VOX_MINI_II) }, +- { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, +- USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD) }, +- { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, +- USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM) }, +- { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, +- { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, m920x_table); +- +-static struct dvb_usb_device_properties megasky_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-megasky-02.fw", +- .download_firmware = m920x_firmware_download, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_megasky_table, +- .rc_map_size = ARRAY_SIZE(rc_map_megasky_table), +- .rc_query = m920x_rc_query, +- }, +- +- .size_of_priv = sizeof(struct m920x_state), +- +- .identify_state = m920x_identify_state, +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 8, +- .pid_filter = m920x_pid_filter, +- .pid_filter_ctrl = m920x_pid_filter_ctrl, +- +- .frontend_attach = m920x_mt352_frontend_attach, +- .tuner_attach = m920x_qt1010_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x81, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }, +- }}, +- }}, +- .i2c_algo = &m920x_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { "MSI Mega Sky 580 DVB-T USB2.0", +- { &m920x_table[0], NULL }, +- { NULL }, +- } +- } +-}; +- +-static struct dvb_usb_device_properties digivox_mini_ii_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-digivox-02.fw", +- .download_firmware = m920x_firmware_download, +- +- .size_of_priv = sizeof(struct m920x_state), +- +- .identify_state = m920x_identify_state, +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 8, +- .pid_filter = m920x_pid_filter, +- .pid_filter_ctrl = m920x_pid_filter_ctrl, +- +- .frontend_attach = m920x_tda10046_08_frontend_attach, +- .tuner_attach = m920x_tda8275_60_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x81, +- .u = { +- .bulk = { +- .buffersize = 0x4000, +- } +- } +- }, +- }}, +- }}, +- .i2c_algo = &m920x_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { "MSI DIGI VOX mini II DVB-T USB2.0", +- { &m920x_table[1], NULL }, +- { NULL }, +- }, +- } +-}; +- +-/* LifeView TV Walker Twin support by Nick Andrew +- * +- * LifeView TV Walker Twin has 1 x M9206, 2 x TDA10046, 2 x TDA8275A +- * TDA10046 #0 is located at i2c address 0x08 +- * TDA10046 #1 is located at i2c address 0x0b +- * TDA8275A #0 is located at i2c address 0x60 +- * TDA8275A #1 is located at i2c address 0x61 +- */ +-static struct dvb_usb_device_properties tvwalkertwin_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-tvwalkert.fw", +- .download_firmware = m920x_firmware_download, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_tvwalkertwin_table, +- .rc_map_size = ARRAY_SIZE(rc_map_tvwalkertwin_table), +- .rc_query = m920x_rc_query, +- }, +- +- .size_of_priv = sizeof(struct m920x_state), +- +- .identify_state = m920x_identify_state, +- .num_adapters = 2, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 8, +- .pid_filter = m920x_pid_filter, +- .pid_filter_ctrl = m920x_pid_filter_ctrl, +- +- .frontend_attach = m920x_tda10046_08_frontend_attach, +- .tuner_attach = m920x_tda8275_60_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x81, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }}, +- }},{ +- .num_frontends = 1, +- .fe = {{ +- +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 8, +- .pid_filter = m920x_pid_filter, +- .pid_filter_ctrl = m920x_pid_filter_ctrl, +- +- .frontend_attach = m920x_tda10046_0b_frontend_attach, +- .tuner_attach = m920x_tda8275_61_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }}, +- }, +- }}, +- .i2c_algo = &m920x_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "LifeView TV Walker Twin DVB-T USB2.0", +- .cold_ids = { &m920x_table[2], NULL }, +- .warm_ids = { &m920x_table[3], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties dposh_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .firmware = "dvb-usb-dposh-01.fw", +- .download_firmware = m920x_firmware_download, +- +- .size_of_priv = sizeof(struct m920x_state), +- +- .identify_state = m920x_identify_state, +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- /* Hardware pid filters don't work with this device/firmware */ +- +- .frontend_attach = m920x_mt352_frontend_attach, +- .tuner_attach = m920x_qt1010_tuner_attach, +- +- .stream = { +- .type = USB_BULK, +- .count = 8, +- .endpoint = 0x81, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }, +- }}, +- }}, +- .i2c_algo = &m920x_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "Dposh DVB-T USB2.0", +- .cold_ids = { &m920x_table[4], NULL }, +- .warm_ids = { &m920x_table[5], NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = DEVICE_SPECIFIC, +- .download_firmware = NULL, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_pinnacle310e_table, +- .rc_map_size = ARRAY_SIZE(rc_map_pinnacle310e_table), +- .rc_query = m920x_rc_query, +- }, +- +- .size_of_priv = sizeof(struct m920x_state), +- +- .identify_state = m920x_identify_state, +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- +- .pid_filter_count = 8, +- .pid_filter = m920x_pid_filter, +- .pid_filter_ctrl = m920x_pid_filter_ctrl, +- +- .frontend_attach = m920x_mt352_frontend_attach, +- .tuner_attach = m920x_fmd1216me_tuner_attach, +- +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x84, +- .u = { +- .isoc = { +- .framesperurb = 128, +- .framesize = 564, +- .interval = 1, +- } +- } +- }, +- }}, +- } }, +- .i2c_algo = &m920x_i2c_algo, +- +- .num_device_descs = 1, +- .devices = { +- { "Pinnacle PCTV 310e", +- { &m920x_table[6], NULL }, +- { NULL }, +- } +- } +-}; +- +-static struct usb_driver m920x_driver = { +- .name = "dvb_usb_m920x", +- .probe = m920x_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = m920x_table, +-}; +- +-module_usb_driver(m920x_driver); +- +-MODULE_AUTHOR("Aapo Tahkola "); +-MODULE_DESCRIPTION("DVB Driver for ULI M920x"); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h +deleted file mode 100644 +index 3c06151..0000000 +--- a/drivers/media/dvb/dvb-usb/m920x.h ++++ /dev/null +@@ -1,77 +0,0 @@ +-#ifndef _DVB_USB_M920X_H_ +-#define _DVB_USB_M920X_H_ +- +-#define DVB_USB_LOG_PREFIX "m920x" +-#include "dvb-usb.h" +- +-#define deb(args...) dprintk(dvb_usb_m920x_debug,0x01,args) +- +-#define M9206_CORE 0x22 +-#define M9206_RC_STATE 0xff51 +-#define M9206_RC_KEY 0xff52 +-#define M9206_RC_INIT1 0xff54 +-#define M9206_RC_INIT2 0xff55 +-#define M9206_FW_GO 0xff69 +- +-#define M9206_I2C 0x23 +-#define M9206_FILTER 0x25 +-#define M9206_FW 0x30 +- +-#define M9206_MAX_FILTERS 8 +-#define M9206_MAX_ADAPTERS 4 +- +-/* +-sequences found in logs: +-[index value] +-0x80 write addr +-(0x00 out byte)* +-0x40 out byte +- +-0x80 write addr +-(0x00 out byte)* +-0x80 read addr +-(0x21 in byte)* +-0x60 in byte +- +-this sequence works: +-0x80 read addr +-(0x21 in byte)* +-0x60 in byte +- +-Guess at API of the I2C function: +-I2C operation is done one byte at a time with USB control messages. The +-index the messages is sent to is made up of a set of flags that control +-the I2C bus state: +-0x80: Send START condition. After a START condition, one would normally +- always send the 7-bit slave I2C address as the 7 MSB, followed by +- the read/write bit as the LSB. +-0x40: Send STOP condition. This should be set on the last byte of an +- I2C transaction. +-0x20: Read a byte from the slave. As opposed to writing a byte to the +- slave. The slave will normally not produce any data unless you +- set the R/W bit to 1 when sending the slave's address after the +- START condition. +-0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read, +- the master should send an ACK, that is pull SDA low during the 9th +- clock cycle, after every byte but the last. This flags only makes +- sense when bit 0x20 is set, indicating a read. +- +-What any other bits might mean, or how to get the slave's ACK/NACK +-response to a write, is unknown. +-*/ +- +-struct m920x_state { +- u16 filters[M9206_MAX_ADAPTERS][M9206_MAX_FILTERS]; +- int filtering_enabled[M9206_MAX_ADAPTERS]; +- int rep_count; +-}; +- +-/* Initialisation data for the m920x +- */ +- +-struct m920x_inits { +- u16 address; +- u8 data; +-}; +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c +deleted file mode 100644 +index d83df4b..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c ++++ /dev/null +@@ -1,612 +0,0 @@ +-/* +- * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "mxl111sf-demod.h" +-#include "mxl111sf-reg.h" +- +-/* debug */ +-static int mxl111sf_demod_debug; +-module_param_named(debug, mxl111sf_demod_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); +- +-#define mxl_dbg(fmt, arg...) \ +- if (mxl111sf_demod_debug) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-/* ------------------------------------------------------------------------ */ +- +-struct mxl111sf_demod_state { +- struct mxl111sf_state *mxl_state; +- +- struct mxl111sf_demod_config *cfg; +- +- struct dvb_frontend fe; +-}; +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state, +- u8 addr, u8 *data) +-{ +- return (state->cfg->read_reg) ? +- state->cfg->read_reg(state->mxl_state, addr, data) : +- -EINVAL; +-} +- +-static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state, +- u8 addr, u8 data) +-{ +- return (state->cfg->write_reg) ? +- state->cfg->write_reg(state->mxl_state, addr, data) : +- -EINVAL; +-} +- +-static +-int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +-{ +- return (state->cfg->program_regs) ? +- state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : +- -EINVAL; +-} +- +-/* ------------------------------------------------------------------------ */ +-/* TPS */ +- +-static +-int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, +- fe_code_rate_t *code_rate) +-{ +- u8 val; +- int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); +- /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */ +- if (mxl_fail(ret)) +- goto fail; +- +- switch (val & V6_CODE_RATE_TPS_MASK) { +- case 0: +- *code_rate = FEC_1_2; +- break; +- case 1: +- *code_rate = FEC_2_3; +- break; +- case 2: +- *code_rate = FEC_3_4; +- break; +- case 3: +- *code_rate = FEC_5_6; +- break; +- case 4: +- *code_rate = FEC_7_8; +- break; +- } +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_tps_modulation(struct mxl111sf_demod_state *state, +- fe_modulation_t *modulation) +-{ +- u8 val; +- int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); +- /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */ +- if (mxl_fail(ret)) +- goto fail; +- +- switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) { +- case 0: +- *modulation = QPSK; +- break; +- case 1: +- *modulation = QAM_16; +- break; +- case 2: +- *modulation = QAM_64; +- break; +- } +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, +- fe_transmit_mode_t *fft_mode) +-{ +- u8 val; +- int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); +- /* FFT Mode, 00:2K, 01:8K, 10:4K */ +- if (mxl_fail(ret)) +- goto fail; +- +- switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) { +- case 0: +- *fft_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- *fft_mode = TRANSMISSION_MODE_8K; +- break; +- case 2: +- *fft_mode = TRANSMISSION_MODE_4K; +- break; +- } +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, +- fe_guard_interval_t *guard) +-{ +- u8 val; +- int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); +- /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */ +- if (mxl_fail(ret)) +- goto fail; +- +- switch ((val & V6_PARAM_GI_MASK) >> 4) { +- case 0: +- *guard = GUARD_INTERVAL_1_32; +- break; +- case 1: +- *guard = GUARD_INTERVAL_1_16; +- break; +- case 2: +- *guard = GUARD_INTERVAL_1_8; +- break; +- case 3: +- *guard = GUARD_INTERVAL_1_4; +- break; +- } +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, +- fe_hierarchy_t *hierarchy) +-{ +- u8 val; +- int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); +- /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */ +- if (mxl_fail(ret)) +- goto fail; +- +- switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) { +- case 0: +- *hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- *hierarchy = HIERARCHY_1; +- break; +- case 2: +- *hierarchy = HIERARCHY_2; +- break; +- case 3: +- *hierarchy = HIERARCHY_4; +- break; +- } +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +-/* LOCKS */ +- +-static +-int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state, +- int *sync_lock) +-{ +- u8 val = 0; +- int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- *sync_lock = (val & SYNC_LOCK_MASK) >> 4; +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state, +- int *rs_lock) +-{ +- u8 val = 0; +- int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- *rs_lock = (val & RS_LOCK_DET_MASK) >> 3; +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state, +- int *tps_lock) +-{ +- u8 val = 0; +- int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6; +-fail: +- return ret; +-} +- +-static +-int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state, +- int *fec_lock) +-{ +- u8 val = 0; +- int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4; +-fail: +- return ret; +-} +- +-#if 0 +-static +-int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state, +- int *cp_lock) +-{ +- u8 val = 0; +- int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2; +-fail: +- return ret; +-} +-#endif +- +-static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state) +-{ +- return mxl111sf_demod_write_reg(state, 0x0e, 0xff); +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- int ret = 0; +- +- struct mxl111sf_reg_ctrl_info phy_pll_patch[] = { +- {0x00, 0xff, 0x01}, /* change page to 1 */ +- {0x40, 0xff, 0x05}, +- {0x40, 0xff, 0x01}, +- {0x41, 0xff, 0xca}, +- {0x41, 0xff, 0xc0}, +- {0x00, 0xff, 0x00}, /* change page to 0 */ +- {0, 0, 0} +- }; +- +- mxl_dbg("()"); +- +- if (fe->ops.tuner_ops.set_params) { +- ret = fe->ops.tuner_ops.set_params(fe); +- if (mxl_fail(ret)) +- goto fail; +- msleep(50); +- } +- ret = mxl111sf_demod_program_regs(state, phy_pll_patch); +- mxl_fail(ret); +- msleep(50); +- ret = mxl1x1sf_demod_reset_irq_status(state); +- mxl_fail(ret); +- msleep(100); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-#if 0 +-/* resets TS Packet error count */ +-/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */ +-static +-int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state) +-{ +- struct mxl111sf_reg_ctrl_info reset_per_count[] = { +- {0x20, 0x01, 0x01}, +- {0x20, 0x01, 0x00}, +- {0, 0, 0} +- }; +- return mxl111sf_demod_program_regs(state, reset_per_count); +-} +-#endif +- +-/* returns TS Packet error count */ +-/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */ +-static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- u32 fec_per_count, fec_per_scale; +- u8 val; +- int ret; +- +- *ucblocks = 0; +- +- /* FEC_PER_COUNT Register */ +- ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- +- fec_per_count = val; +- +- /* FEC_PER_SCALE Register */ +- ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- +- val &= V6_FEC_PER_SCALE_MASK; +- val *= 4; +- +- fec_per_scale = 1 << val; +- +- fec_per_count *= fec_per_scale; +- +- *ucblocks = fec_per_count; +-fail: +- return ret; +-} +- +-#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS +-/* FIXME: leaving this enabled breaks the build on some architectures, +- * and we shouldn't have any floating point math in the kernel, anyway. +- * +- * These macros need to be re-written, but it's harmless to simply +- * return zero for now. */ +-#define CALCULATE_BER(avg_errors, count) \ +- ((u32)(avg_errors * 4)/(count*64*188*8)) +-#define CALCULATE_SNR(data) \ +- ((u32)((10 * (u32)data / 64) - 2.5)) +-#else +-#define CALCULATE_BER(avg_errors, count) 0 +-#define CALCULATE_SNR(data) 0 +-#endif +- +-static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- u8 val1, val2, val3; +- int ret; +- +- *ber = 0; +- +- ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3); +- if (mxl_fail(ret)) +- goto fail; +- +- *ber = CALCULATE_BER((val1 | (val2 << 8)), val3); +-fail: +- return ret; +-} +- +-static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state, +- u16 *snr) +-{ +- u8 val1, val2; +- int ret; +- +- *snr = 0; +- +- ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2); +- if (mxl_fail(ret)) +- goto fail; +- +- *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8)); +-fail: +- return ret; +-} +- +-static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- +- int ret = mxl111sf_demod_calc_snr(state, snr); +- if (mxl_fail(ret)) +- goto fail; +- +- *snr /= 10; /* 0.1 dB */ +-fail: +- return ret; +-} +- +-static int mxl111sf_demod_read_status(struct dvb_frontend *fe, +- fe_status_t *status) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- int ret, locked, cr_lock, sync_lock, fec_lock; +- +- *status = 0; +- +- ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock); +- if (mxl_fail(ret)) +- goto fail; +- +- if (locked) +- *status |= FE_HAS_SIGNAL; +- if (cr_lock) +- *status |= FE_HAS_CARRIER; +- if (sync_lock) +- *status |= FE_HAS_SYNC; +- if (fec_lock) /* false positives? */ +- *status |= FE_HAS_VITERBI; +- +- if ((locked) && (cr_lock) && (sync_lock)) +- *status |= FE_HAS_LOCK; +-fail: +- return ret; +-} +- +-static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- fe_modulation_t modulation; +- u16 snr; +- +- mxl111sf_demod_calc_snr(state, &snr); +- mxl1x1sf_demod_get_tps_modulation(state, &modulation); +- +- switch (modulation) { +- case QPSK: +- *signal_strength = (snr >= 1300) ? +- min(65535, snr * 44) : snr * 38; +- break; +- case QAM_16: +- *signal_strength = (snr >= 1500) ? +- min(65535, snr * 38) : snr * 33; +- break; +- case QAM_64: +- *signal_strength = (snr >= 2000) ? +- min(65535, snr * 29) : snr * 25; +- break; +- default: +- *signal_strength = 0; +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- +- mxl_dbg("()"); +-#if 0 +- p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF; +-#endif +- if (fe->ops.tuner_ops.get_bandwidth) +- fe->ops.tuner_ops.get_bandwidth(fe, &p->bandwidth_hz); +- if (fe->ops.tuner_ops.get_frequency) +- fe->ops.tuner_ops.get_frequency(fe, &p->frequency); +- mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_HP); +- mxl1x1sf_demod_get_tps_code_rate(state, &p->code_rate_LP); +- mxl1x1sf_demod_get_tps_modulation(state, &p->modulation); +- mxl1x1sf_demod_get_tps_guard_fft_mode(state, +- &p->transmission_mode); +- mxl1x1sf_demod_get_tps_guard_interval(state, +- &p->guard_interval); +- mxl1x1sf_demod_get_tps_hierarchy(state, +- &p->hierarchy); +- +- return 0; +-} +- +-static +-int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void mxl111sf_demod_release(struct dvb_frontend *fe) +-{ +- struct mxl111sf_demod_state *state = fe->demodulator_priv; +- mxl_dbg("()"); +- kfree(state); +- fe->demodulator_priv = NULL; +-} +- +-static struct dvb_frontend_ops mxl111sf_demod_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "MaxLinear MxL111SF DVB-T demodulator", +- .frequency_min = 177000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_QAM_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER +- }, +- .release = mxl111sf_demod_release, +-#if 0 +- .init = mxl111sf_init, +- .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl, +-#endif +- .set_frontend = mxl111sf_demod_set_frontend, +- .get_frontend = mxl111sf_demod_get_frontend, +- .get_tune_settings = mxl111sf_demod_get_tune_settings, +- .read_status = mxl111sf_demod_read_status, +- .read_signal_strength = mxl111sf_demod_read_signal_strength, +- .read_ber = mxl111sf_demod_read_ber, +- .read_snr = mxl111sf_demod_read_snr, +- .read_ucblocks = mxl111sf_demod_read_ucblocks, +-}; +- +-struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, +- struct mxl111sf_demod_config *cfg) +-{ +- struct mxl111sf_demod_state *state = NULL; +- +- mxl_dbg("()"); +- +- state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- state->mxl_state = mxl_state; +- state->cfg = cfg; +- +- memcpy(&state->fe.ops, &mxl111sf_demod_ops, +- sizeof(struct dvb_frontend_ops)); +- +- state->fe.demodulator_priv = state; +- return &state->fe; +-} +-EXPORT_SYMBOL_GPL(mxl111sf_demod_attach); +- +-MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.1"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h +deleted file mode 100644 +index 432706a..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MXL111SF_DEMOD_H__ +-#define __MXL111SF_DEMOD_H__ +- +-#include "dvb_frontend.h" +-#include "mxl111sf.h" +- +-struct mxl111sf_demod_config { +- int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); +- int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); +- int (*program_regs)(struct mxl111sf_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +-}; +- +-#if defined(CONFIG_DVB_USB_MXL111SF) || \ +- (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +-extern +-struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, +- struct mxl111sf_demod_config *cfg); +-#else +-static inline +-struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, +- struct mxl111sf_demod_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_USB_MXL111SF */ +- +-#endif /* __MXL111SF_DEMOD_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c +deleted file mode 100644 +index e4121cb..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.c ++++ /dev/null +@@ -1,763 +0,0 @@ +-/* +- * mxl111sf-gpio.c - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "mxl111sf-gpio.h" +-#include "mxl111sf-i2c.h" +-#include "mxl111sf.h" +- +-/* ------------------------------------------------------------------------- */ +- +-#define MXL_GPIO_MUX_REG_0 0x84 +-#define MXL_GPIO_MUX_REG_1 0x89 +-#define MXL_GPIO_MUX_REG_2 0x82 +- +-#define MXL_GPIO_DIR_INPUT 0 +-#define MXL_GPIO_DIR_OUTPUT 1 +- +- +-static int mxl111sf_set_gpo_state(struct mxl111sf_state *state, u8 pin, u8 val) +-{ +- int ret; +- u8 tmp; +- +- mxl_debug_adv("(%d, %d)", pin, val); +- +- if ((pin > 0) && (pin < 8)) { +- ret = mxl111sf_read_reg(state, 0x19, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- tmp &= ~(1 << (pin - 1)); +- tmp |= (val << (pin - 1)); +- ret = mxl111sf_write_reg(state, 0x19, tmp); +- if (mxl_fail(ret)) +- goto fail; +- } else if (pin <= 10) { +- if (pin == 0) +- pin += 7; +- ret = mxl111sf_read_reg(state, 0x30, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- tmp &= ~(1 << (pin - 3)); +- tmp |= (val << (pin - 3)); +- ret = mxl111sf_write_reg(state, 0x30, tmp); +- if (mxl_fail(ret)) +- goto fail; +- } else +- ret = -EINVAL; +-fail: +- return ret; +-} +- +-static int mxl111sf_get_gpi_state(struct mxl111sf_state *state, u8 pin, u8 *val) +-{ +- int ret; +- u8 tmp; +- +- mxl_debug("(0x%02x)", pin); +- +- *val = 0; +- +- switch (pin) { +- case 0: +- case 1: +- case 2: +- case 3: +- ret = mxl111sf_read_reg(state, 0x23, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- *val = (tmp >> (pin + 4)) & 0x01; +- break; +- case 4: +- case 5: +- case 6: +- case 7: +- ret = mxl111sf_read_reg(state, 0x2f, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- *val = (tmp >> pin) & 0x01; +- break; +- case 8: +- case 9: +- case 10: +- ret = mxl111sf_read_reg(state, 0x22, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- *val = (tmp >> (pin - 3)) & 0x01; +- break; +- default: +- return -EINVAL; /* invalid pin */ +- } +-fail: +- return ret; +-} +- +-struct mxl_gpio_cfg { +- u8 pin; +- u8 dir; +- u8 val; +-}; +- +-static int mxl111sf_config_gpio_pins(struct mxl111sf_state *state, +- struct mxl_gpio_cfg *gpio_cfg) +-{ +- int ret; +- u8 tmp; +- +- mxl_debug_adv("(%d, %d)", gpio_cfg->pin, gpio_cfg->dir); +- +- switch (gpio_cfg->pin) { +- case 0: +- case 1: +- case 2: +- case 3: +- ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_0, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- tmp &= ~(1 << (gpio_cfg->pin + 4)); +- tmp |= (gpio_cfg->dir << (gpio_cfg->pin + 4)); +- ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_0, tmp); +- if (mxl_fail(ret)) +- goto fail; +- break; +- case 4: +- case 5: +- case 6: +- case 7: +- ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_1, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- tmp &= ~(1 << gpio_cfg->pin); +- tmp |= (gpio_cfg->dir << gpio_cfg->pin); +- ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_1, tmp); +- if (mxl_fail(ret)) +- goto fail; +- break; +- case 8: +- case 9: +- case 10: +- ret = mxl111sf_read_reg(state, MXL_GPIO_MUX_REG_2, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- tmp &= ~(1 << (gpio_cfg->pin - 3)); +- tmp |= (gpio_cfg->dir << (gpio_cfg->pin - 3)); +- ret = mxl111sf_write_reg(state, MXL_GPIO_MUX_REG_2, tmp); +- if (mxl_fail(ret)) +- goto fail; +- break; +- default: +- return -EINVAL; /* invalid pin */ +- } +- +- ret = (MXL_GPIO_DIR_OUTPUT == gpio_cfg->dir) ? +- mxl111sf_set_gpo_state(state, +- gpio_cfg->pin, gpio_cfg->val) : +- mxl111sf_get_gpi_state(state, +- gpio_cfg->pin, &gpio_cfg->val); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-static int mxl111sf_hw_do_set_gpio(struct mxl111sf_state *state, +- int gpio, int direction, int val) +-{ +- struct mxl_gpio_cfg gpio_config = { +- .pin = gpio, +- .dir = direction, +- .val = val, +- }; +- +- mxl_debug("(%d, %d, %d)", gpio, direction, val); +- +- return mxl111sf_config_gpio_pins(state, &gpio_config); +-} +- +-/* ------------------------------------------------------------------------- */ +- +-#define PIN_MUX_MPEG_MODE_MASK 0x40 /* 0x17 <6> */ +-#define PIN_MUX_MPEG_PAR_EN_MASK 0x01 /* 0x18 <0> */ +-#define PIN_MUX_MPEG_SER_EN_MASK 0x02 /* 0x18 <1> */ +-#define PIN_MUX_MPG_IN_MUX_MASK 0x80 /* 0x3D <7> */ +-#define PIN_MUX_BT656_ENABLE_MASK 0x04 /* 0x12 <2> */ +-#define PIN_MUX_I2S_ENABLE_MASK 0x40 /* 0x15 <6> */ +-#define PIN_MUX_SPI_MODE_MASK 0x10 /* 0x3D <4> */ +-#define PIN_MUX_MCLK_EN_CTRL_MASK 0x10 /* 0x82 <4> */ +-#define PIN_MUX_MPSYN_EN_CTRL_MASK 0x20 /* 0x82 <5> */ +-#define PIN_MUX_MDVAL_EN_CTRL_MASK 0x40 /* 0x82 <6> */ +-#define PIN_MUX_MPERR_EN_CTRL_MASK 0x80 /* 0x82 <7> */ +-#define PIN_MUX_MDAT_EN_0_MASK 0x10 /* 0x84 <4> */ +-#define PIN_MUX_MDAT_EN_1_MASK 0x20 /* 0x84 <5> */ +-#define PIN_MUX_MDAT_EN_2_MASK 0x40 /* 0x84 <6> */ +-#define PIN_MUX_MDAT_EN_3_MASK 0x80 /* 0x84 <7> */ +-#define PIN_MUX_MDAT_EN_4_MASK 0x10 /* 0x89 <4> */ +-#define PIN_MUX_MDAT_EN_5_MASK 0x20 /* 0x89 <5> */ +-#define PIN_MUX_MDAT_EN_6_MASK 0x40 /* 0x89 <6> */ +-#define PIN_MUX_MDAT_EN_7_MASK 0x80 /* 0x89 <7> */ +- +-int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, +- enum mxl111sf_mux_config pin_mux_config) +-{ +- u8 r12, r15, r17, r18, r3D, r82, r84, r89; +- int ret; +- +- mxl_debug("(%d)", pin_mux_config); +- +- ret = mxl111sf_read_reg(state, 0x17, &r17); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x18, &r18); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x12, &r12); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x15, &r15); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x82, &r82); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x84, &r84); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x89, &r89); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_read_reg(state, 0x3D, &r3D); +- if (mxl_fail(ret)) +- goto fail; +- +- switch (pin_mux_config) { +- case PIN_MUX_TS_OUT_PARALLEL: +- /* mpeg_mode = 1 */ +- r17 |= PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 1 */ +- r18 |= PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 1 */ +- r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 1 */ +- r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 1 */ +- r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 1 */ +- r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0xF */ +- r84 |= 0xF0; +- /* mdat_en_ctrl[7:4] = 0xF */ +- r89 |= 0xF0; +- break; +- case PIN_MUX_TS_OUT_SERIAL: +- /* mpeg_mode = 1 */ +- r17 |= PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 1 */ +- r18 |= PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 1 */ +- r82 |= PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 1 */ +- r82 |= PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 1 */ +- r82 |= PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 1 */ +- r82 |= PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0xF */ +- r84 |= 0xF0; +- /* mdat_en_ctrl[7:4] = 0xF */ +- r89 |= 0xF0; +- break; +- case PIN_MUX_GPIO_MODE: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_TS_SERIAL_IN_MODE_0: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 1 */ +- r18 |= PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_TS_SERIAL_IN_MODE_1: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 1 */ +- r18 |= PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 1 */ +- r3D |= PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_TS_SPI_IN_MODE_1: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 1 */ +- r18 |= PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 1 */ +- r3D |= PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 1 */ +- r15 |= PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 1 */ +- r3D |= PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_TS_SPI_IN_MODE_0: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 1 */ +- r18 |= PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 1 */ +- r15 |= PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 1 */ +- r3D |= PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_TS_PARALLEL_IN: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 1 */ +- r18 |= PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_BT656_I2S_MODE: +- /* mpeg_mode = 0 */ +- r17 &= ~PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 1 */ +- r12 |= PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 1 */ +- r15 |= PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- case PIN_MUX_DEFAULT: +- default: +- /* mpeg_mode = 1 */ +- r17 |= PIN_MUX_MPEG_MODE_MASK; +- /* mpeg_par_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_PAR_EN_MASK; +- /* mpeg_ser_en = 0 */ +- r18 &= ~PIN_MUX_MPEG_SER_EN_MASK; +- /* mpg_in_mux = 0 */ +- r3D &= ~PIN_MUX_MPG_IN_MUX_MASK; +- /* bt656_enable = 0 */ +- r12 &= ~PIN_MUX_BT656_ENABLE_MASK; +- /* i2s_enable = 0 */ +- r15 &= ~PIN_MUX_I2S_ENABLE_MASK; +- /* spi_mode = 0 */ +- r3D &= ~PIN_MUX_SPI_MODE_MASK; +- /* mclk_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MCLK_EN_CTRL_MASK; +- /* mperr_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPERR_EN_CTRL_MASK; +- /* mdval_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MDVAL_EN_CTRL_MASK; +- /* mpsyn_en_ctrl = 0 */ +- r82 &= ~PIN_MUX_MPSYN_EN_CTRL_MASK; +- /* mdat_en_ctrl[3:0] = 0x0 */ +- r84 &= 0x0F; +- /* mdat_en_ctrl[7:4] = 0x0 */ +- r89 &= 0x0F; +- break; +- } +- +- ret = mxl111sf_write_reg(state, 0x17, r17); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x18, r18); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x12, r12); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x15, r15); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x82, r82); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x84, r84); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x89, r89); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x3D, r3D); +- if (mxl_fail(ret)) +- goto fail; +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static int mxl111sf_hw_set_gpio(struct mxl111sf_state *state, int gpio, int val) +-{ +- return mxl111sf_hw_do_set_gpio(state, gpio, MXL_GPIO_DIR_OUTPUT, val); +-} +- +-static int mxl111sf_hw_gpio_initialize(struct mxl111sf_state *state) +-{ +- u8 gpioval = 0x07; /* write protect enabled, signal LEDs off */ +- int i, ret; +- +- mxl_debug("()"); +- +- for (i = 3; i < 8; i++) { +- ret = mxl111sf_hw_set_gpio(state, i, (gpioval >> i) & 0x01); +- if (mxl_fail(ret)) +- break; +- } +- +- return ret; +-} +- +-#define PCA9534_I2C_ADDR (0x40 >> 1) +-static int pca9534_set_gpio(struct mxl111sf_state *state, int gpio, int val) +-{ +- u8 w[2] = { 1, 0 }; +- u8 r = 0; +- struct i2c_msg msg[] = { +- { .addr = PCA9534_I2C_ADDR, +- .flags = 0, .buf = w, .len = 1 }, +- { .addr = PCA9534_I2C_ADDR, +- .flags = I2C_M_RD, .buf = &r, .len = 1 }, +- }; +- +- mxl_debug("(%d, %d)", gpio, val); +- +- /* read current GPIO levels from flip-flop */ +- i2c_transfer(&state->d->i2c_adap, msg, 2); +- +- /* prepare write buffer with current GPIO levels */ +- msg[0].len = 2; +-#if 0 +- w[0] = 1; +-#endif +- w[1] = r; +- +- /* clear the desired GPIO */ +- w[1] &= ~(1 << gpio); +- +- /* set the desired GPIO value */ +- w[1] |= ((val ? 1 : 0) << gpio); +- +- /* write new GPIO levels to flip-flop */ +- i2c_transfer(&state->d->i2c_adap, &msg[0], 1); +- +- return 0; +-} +- +-static int pca9534_init_port_expander(struct mxl111sf_state *state) +-{ +- u8 w[2] = { 1, 0x07 }; /* write protect enabled, signal LEDs off */ +- +- struct i2c_msg msg = { +- .addr = PCA9534_I2C_ADDR, +- .flags = 0, .buf = w, .len = 2 +- }; +- +- mxl_debug("()"); +- +- i2c_transfer(&state->d->i2c_adap, &msg, 1); +- +- /* configure all pins as outputs */ +- w[0] = 3; +- w[1] = 0; +- +- i2c_transfer(&state->d->i2c_adap, &msg, 1); +- +- return 0; +-} +- +-int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val) +-{ +- mxl_debug("(%d, %d)", gpio, val); +- +- switch (state->gpio_port_expander) { +- default: +- mxl_printk(KERN_ERR, +- "gpio_port_expander undefined, assuming PCA9534"); +- /* fall-thru */ +- case mxl111sf_PCA9534: +- return pca9534_set_gpio(state, gpio, val); +- case mxl111sf_gpio_hw: +- return mxl111sf_hw_set_gpio(state, gpio, val); +- } +-} +- +-static int mxl111sf_probe_port_expander(struct mxl111sf_state *state) +-{ +- int ret; +- u8 w = 1; +- u8 r = 0; +- struct i2c_msg msg[] = { +- { .flags = 0, .buf = &w, .len = 1 }, +- { .flags = I2C_M_RD, .buf = &r, .len = 1 }, +- }; +- +- mxl_debug("()"); +- +- msg[0].addr = 0x70 >> 1; +- msg[1].addr = 0x70 >> 1; +- +- /* read current GPIO levels from flip-flop */ +- ret = i2c_transfer(&state->d->i2c_adap, msg, 2); +- if (ret == 2) { +- state->port_expander_addr = msg[0].addr; +- state->gpio_port_expander = mxl111sf_PCA9534; +- mxl_debug("found port expander at 0x%02x", +- state->port_expander_addr); +- return 0; +- } +- +- msg[0].addr = 0x40 >> 1; +- msg[1].addr = 0x40 >> 1; +- +- ret = i2c_transfer(&state->d->i2c_adap, msg, 2); +- if (ret == 2) { +- state->port_expander_addr = msg[0].addr; +- state->gpio_port_expander = mxl111sf_PCA9534; +- mxl_debug("found port expander at 0x%02x", +- state->port_expander_addr); +- return 0; +- } +- state->port_expander_addr = 0xff; +- state->gpio_port_expander = mxl111sf_gpio_hw; +- mxl_debug("using hardware gpio"); +- return 0; +-} +- +-int mxl111sf_init_port_expander(struct mxl111sf_state *state) +-{ +- mxl_debug("()"); +- +- if (0x00 == state->port_expander_addr) +- mxl111sf_probe_port_expander(state); +- +- switch (state->gpio_port_expander) { +- default: +- mxl_printk(KERN_ERR, +- "gpio_port_expander undefined, assuming PCA9534"); +- /* fall-thru */ +- case mxl111sf_PCA9534: +- return pca9534_init_port_expander(state); +- case mxl111sf_gpio_hw: +- return mxl111sf_hw_gpio_initialize(state); +- } +-} +- +-/* ------------------------------------------------------------------------ */ +- +-int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode) +-{ +-/* GPO: +- * 3 - ATSC/MH# | 1 = ATSC transport, 0 = MH transport | default 0 +- * 4 - ATSC_RST## | 1 = ATSC enable, 0 = ATSC Reset | default 0 +- * 5 - ATSC_EN | 1 = ATSC power enable, 0 = ATSC power off | default 0 +- * 6 - MH_RESET# | 1 = MH enable, 0 = MH Reset | default 0 +- * 7 - MH_EN | 1 = MH power enable, 0 = MH power off | default 0 +- */ +- mxl_debug("(%d)", mode); +- +- switch (mode) { +- case MXL111SF_GPIO_MOD_MH: +- mxl111sf_set_gpio(state, 4, 0); +- mxl111sf_set_gpio(state, 5, 0); +- msleep(50); +- mxl111sf_set_gpio(state, 7, 1); +- msleep(50); +- mxl111sf_set_gpio(state, 6, 1); +- msleep(50); +- +- mxl111sf_set_gpio(state, 3, 0); +- break; +- case MXL111SF_GPIO_MOD_ATSC: +- mxl111sf_set_gpio(state, 6, 0); +- mxl111sf_set_gpio(state, 7, 0); +- msleep(50); +- mxl111sf_set_gpio(state, 5, 1); +- msleep(50); +- mxl111sf_set_gpio(state, 4, 1); +- msleep(50); +- mxl111sf_set_gpio(state, 3, 1); +- break; +- default: /* DVBT / STANDBY */ +- mxl111sf_init_port_expander(state); +- break; +- } +- return 0; +-} +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h b/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h +deleted file mode 100644 +index 0220f54..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-gpio.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- * mxl111sf-gpio.h - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _DVB_USB_MXL111SF_GPIO_H_ +-#define _DVB_USB_MXL111SF_GPIO_H_ +- +-#include "mxl111sf.h" +- +-int mxl111sf_set_gpio(struct mxl111sf_state *state, int gpio, int val); +-int mxl111sf_init_port_expander(struct mxl111sf_state *state); +- +-#define MXL111SF_GPIO_MOD_DVBT 0 +-#define MXL111SF_GPIO_MOD_MH 1 +-#define MXL111SF_GPIO_MOD_ATSC 2 +-int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode); +- +-enum mxl111sf_mux_config { +- PIN_MUX_DEFAULT = 0, +- PIN_MUX_TS_OUT_PARALLEL, +- PIN_MUX_TS_OUT_SERIAL, +- PIN_MUX_GPIO_MODE, +- PIN_MUX_TS_SERIAL_IN_MODE_0, +- PIN_MUX_TS_SERIAL_IN_MODE_1, +- PIN_MUX_TS_SPI_IN_MODE_0, +- PIN_MUX_TS_SPI_IN_MODE_1, +- PIN_MUX_TS_PARALLEL_IN, +- PIN_MUX_BT656_I2S_MODE, +-}; +- +-int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, +- enum mxl111sf_mux_config pin_mux_config); +- +-#endif /* _DVB_USB_MXL111SF_GPIO_H_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c +deleted file mode 100644 +index 3443455..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.c ++++ /dev/null +@@ -1,850 +0,0 @@ +-/* +- * mxl111sf-i2c.c - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "mxl111sf-i2c.h" +-#include "mxl111sf.h" +- +-/* SW-I2C ----------------------------------------------------------------- */ +- +-#define SW_I2C_ADDR 0x1a +-#define SW_I2C_EN 0x02 +-#define SW_SCL_OUT 0x04 +-#define SW_SDA_OUT 0x08 +-#define SW_SDA_IN 0x04 +- +-#define SW_I2C_BUSY_ADDR 0x2f +-#define SW_I2C_BUSY 0x02 +- +-static int mxl111sf_i2c_bitbang_sendbyte(struct mxl111sf_state *state, +- u8 byte) +-{ +- int i, ret; +- u8 data = 0; +- +- mxl_i2c("(0x%02x)", byte); +- +- ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); +- if (mxl_fail(ret)) +- goto fail; +- +- for (i = 0; i < 8; i++) { +- +- data = (byte & (0x80 >> i)) ? SW_SDA_OUT : 0; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | data); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | data | SW_SCL_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | data); +- if (mxl_fail(ret)) +- goto fail; +- } +- +- /* last bit was 0 so we need to release SDA */ +- if (!(byte & 1)) { +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- } +- +- /* CLK high for ACK readback */ +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); +- if (mxl_fail(ret)) +- goto fail; +- +- /* drop the CLK after getting ACK, SDA will go high right away */ +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- if (data & SW_SDA_IN) +- ret = -EIO; +-fail: +- return ret; +-} +- +-static int mxl111sf_i2c_bitbang_recvbyte(struct mxl111sf_state *state, +- u8 *pbyte) +-{ +- int i, ret; +- u8 byte = 0; +- u8 data = 0; +- +- mxl_i2c("()"); +- +- *pbyte = 0; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- for (i = 0; i < 8; i++) { +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | +- SW_SCL_OUT | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &data); +- if (mxl_fail(ret)) +- goto fail; +- +- if (data & SW_SDA_IN) +- byte |= (0x80 >> i); +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- } +- *pbyte = byte; +-fail: +- return ret; +-} +- +-static int mxl111sf_i2c_start(struct mxl111sf_state *state) +-{ +- int ret; +- +- mxl_i2c("()"); +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN); /* start */ +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-static int mxl111sf_i2c_stop(struct mxl111sf_state *state) +-{ +- int ret; +- +- mxl_i2c("()"); +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN); /* stop */ +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_SCL_OUT | SW_SDA_OUT); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-static int mxl111sf_i2c_ack(struct mxl111sf_state *state) +-{ +- int ret; +- u8 b = 0; +- +- mxl_i2c("()"); +- +- ret = mxl111sf_read_reg(state, SW_I2C_BUSY_ADDR, &b); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN); +- if (mxl_fail(ret)) +- goto fail; +- +- /* pull SDA low */ +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-static int mxl111sf_i2c_nack(struct mxl111sf_state *state) +-{ +- int ret; +- +- mxl_i2c("()"); +- +- /* SDA high to signal last byte read from slave */ +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SCL_OUT | SW_SDA_OUT); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, SW_I2C_ADDR, +- 0x10 | SW_I2C_EN | SW_SDA_OUT); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_i2c_sw_xfer_msg(struct mxl111sf_state *state, +- struct i2c_msg *msg) +-{ +- int i, ret; +- +- mxl_i2c("()"); +- +- if (msg->flags & I2C_M_RD) { +- +- ret = mxl111sf_i2c_start(state); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_i2c_bitbang_sendbyte(state, +- (msg->addr << 1) | 0x01); +- if (mxl_fail(ret)) { +- mxl111sf_i2c_stop(state); +- goto fail; +- } +- +- for (i = 0; i < msg->len; i++) { +- ret = mxl111sf_i2c_bitbang_recvbyte(state, +- &msg->buf[i]); +- if (mxl_fail(ret)) { +- mxl111sf_i2c_stop(state); +- goto fail; +- } +- +- if (i < msg->len - 1) +- mxl111sf_i2c_ack(state); +- } +- +- mxl111sf_i2c_nack(state); +- +- ret = mxl111sf_i2c_stop(state); +- if (mxl_fail(ret)) +- goto fail; +- +- } else { +- +- ret = mxl111sf_i2c_start(state); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_i2c_bitbang_sendbyte(state, +- (msg->addr << 1) & 0xfe); +- if (mxl_fail(ret)) { +- mxl111sf_i2c_stop(state); +- goto fail; +- } +- +- for (i = 0; i < msg->len; i++) { +- ret = mxl111sf_i2c_bitbang_sendbyte(state, +- msg->buf[i]); +- if (mxl_fail(ret)) { +- mxl111sf_i2c_stop(state); +- goto fail; +- } +- } +- +- /* FIXME: we only want to do this on the last transaction */ +- mxl111sf_i2c_stop(state); +- } +-fail: +- return ret; +-} +- +-/* HW-I2C ----------------------------------------------------------------- */ +- +-#define USB_WRITE_I2C_CMD 0x99 +-#define USB_READ_I2C_CMD 0xdd +-#define USB_END_I2C_CMD 0xfe +- +-#define USB_WRITE_I2C_CMD_LEN 26 +-#define USB_READ_I2C_CMD_LEN 24 +- +-#define I2C_MUX_REG 0x30 +-#define I2C_CONTROL_REG 0x00 +-#define I2C_SLAVE_ADDR_REG 0x08 +-#define I2C_DATA_REG 0x0c +-#define I2C_INT_STATUS_REG 0x10 +- +-static int mxl111sf_i2c_send_data(struct mxl111sf_state *state, +- u8 index, u8 *wdata) +-{ +- int ret = mxl111sf_ctrl_msg(state->d, wdata[0], +- &wdata[1], 25, NULL, 0); +- mxl_fail(ret); +- +- return ret; +-} +- +-static int mxl111sf_i2c_get_data(struct mxl111sf_state *state, +- u8 index, u8 *wdata, u8 *rdata) +-{ +- int ret = mxl111sf_ctrl_msg(state->d, wdata[0], +- &wdata[1], 25, rdata, 24); +- mxl_fail(ret); +- +- return ret; +-} +- +-static u8 mxl111sf_i2c_check_status(struct mxl111sf_state *state) +-{ +- u8 status = 0; +- u8 buf[26]; +- +- mxl_i2c_adv("()"); +- +- buf[0] = USB_READ_I2C_CMD; +- buf[1] = 0x00; +- +- buf[2] = I2C_INT_STATUS_REG; +- buf[3] = 0x00; +- buf[4] = 0x00; +- +- buf[5] = USB_END_I2C_CMD; +- +- mxl111sf_i2c_get_data(state, 0, buf, buf); +- +- if (buf[1] & 0x04) +- status = 1; +- +- return status; +-} +- +-static u8 mxl111sf_i2c_check_fifo(struct mxl111sf_state *state) +-{ +- u8 status = 0; +- u8 buf[26]; +- +- mxl_i2c("()"); +- +- buf[0] = USB_READ_I2C_CMD; +- buf[1] = 0x00; +- +- buf[2] = I2C_MUX_REG; +- buf[3] = 0x00; +- buf[4] = 0x00; +- +- buf[5] = I2C_INT_STATUS_REG; +- buf[6] = 0x00; +- buf[7] = 0x00; +- buf[8] = USB_END_I2C_CMD; +- +- mxl111sf_i2c_get_data(state, 0, buf, buf); +- +- if (0x08 == (buf[1] & 0x08)) +- status = 1; +- +- if ((buf[5] & 0x02) == 0x02) +- mxl_i2c("(buf[5] & 0x02) == 0x02"); /* FIXME */ +- +- return status; +-} +- +-static int mxl111sf_i2c_readagain(struct mxl111sf_state *state, +- u8 count, u8 *rbuf) +-{ +- u8 i2c_w_data[26]; +- u8 i2c_r_data[24]; +- u8 i = 0; +- u8 fifo_status = 0; +- int status = 0; +- +- mxl_i2c("read %d bytes", count); +- +- while ((fifo_status == 0) && (i++ < 5)) +- fifo_status = mxl111sf_i2c_check_fifo(state); +- +- i2c_w_data[0] = 0xDD; +- i2c_w_data[1] = 0x00; +- +- for (i = 2; i < 26; i++) +- i2c_w_data[i] = 0xFE; +- +- for (i = 0; i < count; i++) { +- i2c_w_data[2+(i*3)] = 0x0C; +- i2c_w_data[3+(i*3)] = 0x00; +- i2c_w_data[4+(i*3)] = 0x00; +- } +- +- mxl111sf_i2c_get_data(state, 0, i2c_w_data, i2c_r_data); +- +- /* Check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("error!"); +- } else { +- for (i = 0; i < count; i++) { +- rbuf[i] = i2c_r_data[(i*3)+1]; +- mxl_i2c("%02x\t %02x", +- i2c_r_data[(i*3)+1], +- i2c_r_data[(i*3)+2]); +- } +- +- status = 1; +- } +- +- return status; +-} +- +-#define HWI2C400 1 +-static int mxl111sf_i2c_hw_xfer_msg(struct mxl111sf_state *state, +- struct i2c_msg *msg) +-{ +- int i, k, ret = 0; +- u16 index = 0; +- u8 buf[26]; +- u8 i2c_r_data[24]; +- u16 block_len; +- u16 left_over_len; +- u8 rd_status[8]; +- u8 ret_status; +- u8 readbuff[26]; +- +- mxl_i2c("addr: 0x%02x, read buff len: %d, write buff len: %d", +- msg->addr, (msg->flags & I2C_M_RD) ? msg->len : 0, +- (!(msg->flags & I2C_M_RD)) ? msg->len : 0); +- +- for (index = 0; index < 26; index++) +- buf[index] = USB_END_I2C_CMD; +- +- /* command to indicate data payload is destined for I2C interface */ +- buf[0] = USB_WRITE_I2C_CMD; +- buf[1] = 0x00; +- +- /* enable I2C interface */ +- buf[2] = I2C_MUX_REG; +- buf[3] = 0x80; +- buf[4] = 0x00; +- +- /* enable I2C interface */ +- buf[5] = I2C_MUX_REG; +- buf[6] = 0x81; +- buf[7] = 0x00; +- +- /* set Timeout register on I2C interface */ +- buf[8] = 0x14; +- buf[9] = 0xff; +- buf[10] = 0x00; +-#if 0 +- /* enable Interrupts on I2C interface */ +- buf[8] = 0x24; +- buf[9] = 0xF7; +- buf[10] = 0x00; +-#endif +- buf[11] = 0x24; +- buf[12] = 0xF7; +- buf[13] = 0x00; +- +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* write data on I2C bus */ +- if (!(msg->flags & I2C_M_RD) && (msg->len > 0)) { +- mxl_i2c("%d\t%02x", msg->len, msg->buf[0]); +- +- /* control register on I2C interface to initialize I2C bus */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x5E; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- +- /* I2C Slave device Address */ +- buf[5] = I2C_SLAVE_ADDR_REG; +- buf[6] = (msg->addr); +- buf[7] = 0x00; +- buf[8] = USB_END_I2C_CMD; +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* check for slave device status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK writing slave address %02x", +- msg->addr); +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x4E; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- /* I2C interface can do I2C operations in block of 8 bytes of +- I2C data. calculation to figure out number of blocks of i2c +- data required to program */ +- block_len = (msg->len / 8); +- left_over_len = (msg->len % 8); +- index = 0; +- +- mxl_i2c("block_len %d, left_over_len %d", +- block_len, left_over_len); +- +- for (index = 0; index < block_len; index++) { +- for (i = 0; i < 8; i++) { +- /* write data on I2C interface */ +- buf[2+(i*3)] = I2C_DATA_REG; +- buf[3+(i*3)] = msg->buf[(index*8)+i]; +- buf[4+(i*3)] = 0x00; +- } +- +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK writing slave address %02x", +- msg->addr); +- +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x4E; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- } +- +- if (left_over_len) { +- for (k = 0; k < 26; k++) +- buf[k] = USB_END_I2C_CMD; +- +- buf[0] = 0x99; +- buf[1] = 0x00; +- +- for (i = 0; i < left_over_len; i++) { +- buf[2+(i*3)] = I2C_DATA_REG; +- buf[3+(i*3)] = msg->buf[(index*8)+i]; +- mxl_i2c("index = %d %d data %d", +- index, i, msg->buf[(index*8)+i]); +- buf[4+(i*3)] = 0x00; +- } +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK writing slave address %02x", +- msg->addr); +- +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x4E; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- } +- +- /* issue I2C STOP after write */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x4E; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- +- } +- +- /* read data from I2C bus */ +- if ((msg->flags & I2C_M_RD) && (msg->len > 0)) { +- mxl_i2c("read buf len %d", msg->len); +- +- /* command to indicate data payload is +- destined for I2C interface */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xDF; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- +- /* I2C xfer length */ +- buf[5] = 0x14; +- buf[6] = (msg->len & 0xFF); +- buf[7] = 0; +- +- /* I2C slave device Address */ +- buf[8] = I2C_SLAVE_ADDR_REG; +- buf[9] = msg->addr; +- buf[10] = 0x00; +- buf[11] = USB_END_I2C_CMD; +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK reading slave address %02x", +- msg->addr); +- +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xC7; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- /* I2C interface can do I2C operations in block of 8 bytes of +- I2C data. calculation to figure out number of blocks of +- i2c data required to program */ +- block_len = ((msg->len) / 8); +- left_over_len = ((msg->len) % 8); +- index = 0; +- +- mxl_i2c("block_len %d, left_over_len %d", +- block_len, left_over_len); +- +- /* command to read data from I2C interface */ +- buf[0] = USB_READ_I2C_CMD; +- buf[1] = 0x00; +- +- for (index = 0; index < block_len; index++) { +- /* setup I2C read request packet on I2C interface */ +- for (i = 0; i < 8; i++) { +- buf[2+(i*3)] = I2C_DATA_REG; +- buf[3+(i*3)] = 0x00; +- buf[4+(i*3)] = 0x00; +- } +- +- ret = mxl111sf_i2c_get_data(state, 0, buf, i2c_r_data); +- +- /* check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK reading slave address %02x", +- msg->addr); +- +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xC7; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- /* copy data from i2c data payload to read buffer */ +- for (i = 0; i < 8; i++) { +- rd_status[i] = i2c_r_data[(i*3)+2]; +- +- if (rd_status[i] == 0x04) { +- if (i < 7) { +- mxl_i2c("i2c fifo empty!" +- " @ %d", i); +- msg->buf[(index*8)+i] = +- i2c_r_data[(i*3)+1]; +- /* read again */ +- ret_status = +- mxl111sf_i2c_readagain( +- state, 8-(i+1), +- readbuff); +- if (ret_status == 1) { +- for (k = 0; +- k < 8-(i+1); +- k++) { +- +- msg->buf[(index*8)+(k+i+1)] = +- readbuff[k]; +- mxl_i2c("read data: %02x\t %02x", +- msg->buf[(index*8)+(k+i)], +- (index*8)+(k+i)); +- mxl_i2c("read data: %02x\t %02x", +- msg->buf[(index*8)+(k+i+1)], +- readbuff[k]); +- +- } +- goto stop_copy; +- } else { +- mxl_i2c("readagain " +- "ERROR!"); +- } +- } else { +- msg->buf[(index*8)+i] = +- i2c_r_data[(i*3)+1]; +- } +- } else { +- msg->buf[(index*8)+i] = +- i2c_r_data[(i*3)+1]; +- } +- } +-stop_copy: +- ; +- +- } +- +- if (left_over_len) { +- for (k = 0; k < 26; k++) +- buf[k] = USB_END_I2C_CMD; +- +- buf[0] = 0xDD; +- buf[1] = 0x00; +- +- for (i = 0; i < left_over_len; i++) { +- buf[2+(i*3)] = I2C_DATA_REG; +- buf[3+(i*3)] = 0x00; +- buf[4+(i*3)] = 0x00; +- } +- ret = mxl111sf_i2c_get_data(state, 0, buf, +- i2c_r_data); +- +- /* check for I2C NACK status */ +- if (mxl111sf_i2c_check_status(state) == 1) { +- mxl_i2c("NACK reading slave address %02x", +- msg->addr); +- +- /* if NACK, stop I2C bus and exit */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xC7; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- ret = -EIO; +- goto exit; +- } +- +- for (i = 0; i < left_over_len; i++) { +- msg->buf[(block_len*8)+i] = +- i2c_r_data[(i*3)+1]; +- mxl_i2c("read data: %02x\t %02x", +- i2c_r_data[(i*3)+1], +- i2c_r_data[(i*3)+2]); +- } +- } +- +- /* indicate I2C interface to issue NACK +- after next I2C read op */ +- buf[0] = USB_WRITE_I2C_CMD; +- buf[1] = 0x00; +- +- /* control register */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0x17; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- +- buf[5] = USB_END_I2C_CMD; +- ret = mxl111sf_i2c_send_data(state, 0, buf); +- +- /* control register */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xC7; +- buf[4] = (HWI2C400) ? 0x03 : 0x0D; +- +- } +-exit: +- /* STOP and disable I2C MUX */ +- buf[0] = USB_WRITE_I2C_CMD; +- buf[1] = 0x00; +- +- /* de-initilize I2C BUS */ +- buf[5] = USB_END_I2C_CMD; +- mxl111sf_i2c_send_data(state, 0, buf); +- +- /* Control Register */ +- buf[2] = I2C_CONTROL_REG; +- buf[3] = 0xDF; +- buf[4] = 0x03; +- +- /* disable I2C interface */ +- buf[5] = I2C_MUX_REG; +- buf[6] = 0x00; +- buf[7] = 0x00; +- +- /* de-initilize I2C BUS */ +- buf[8] = USB_END_I2C_CMD; +- mxl111sf_i2c_send_data(state, 0, buf); +- +- /* disable I2C interface */ +- buf[2] = I2C_MUX_REG; +- buf[3] = 0x81; +- buf[4] = 0x00; +- +- /* disable I2C interface */ +- buf[5] = I2C_MUX_REG; +- buf[6] = 0x00; +- buf[7] = 0x00; +- +- /* disable I2C interface */ +- buf[8] = I2C_MUX_REG; +- buf[9] = 0x00; +- buf[10] = 0x00; +- +- buf[11] = USB_END_I2C_CMD; +- mxl111sf_i2c_send_data(state, 0, buf); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-int mxl111sf_i2c_xfer(struct i2c_adapter *adap, +- struct i2c_msg msg[], int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- struct mxl111sf_state *state = d->priv; +- int hwi2c = (state->chip_rev > MXL111SF_V6); +- int i, ret; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- ret = (hwi2c) ? +- mxl111sf_i2c_hw_xfer_msg(state, &msg[i]) : +- mxl111sf_i2c_sw_xfer_msg(state, &msg[i]); +- if (mxl_fail(ret)) { +- mxl_debug_adv("failed with error %d on i2c " +- "transaction %d of %d, %sing %d bytes " +- "to/from 0x%02x", ret, i+1, num, +- (msg[i].flags & I2C_M_RD) ? +- "read" : "writ", +- msg[i].len, msg[i].addr); +- +- break; +- } +- } +- +- mutex_unlock(&d->i2c_mutex); +- +- return i == num ? num : -EREMOTEIO; +-} +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h b/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h +deleted file mode 100644 +index a57a45f..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-i2c.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-/* +- * mxl111sf-i2c.h - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _DVB_USB_MXL111SF_I2C_H_ +-#define _DVB_USB_MXL111SF_I2C_H_ +- +-#include +- +-int mxl111sf_i2c_xfer(struct i2c_adapter *adap, +- struct i2c_msg msg[], int num); +- +-#endif /* _DVB_USB_MXL111SF_I2C_H_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c b/drivers/media/dvb/dvb-usb/mxl111sf-phy.c +deleted file mode 100644 +index b741b3a..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-phy.c ++++ /dev/null +@@ -1,343 +0,0 @@ +-/* +- * mxl111sf-phy.c - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "mxl111sf-phy.h" +-#include "mxl111sf-reg.h" +- +-int mxl111sf_init_tuner_demod(struct mxl111sf_state *state) +-{ +- struct mxl111sf_reg_ctrl_info mxl_111_overwrite_default[] = { +- {0x07, 0xff, 0x0c}, +- {0x58, 0xff, 0x9d}, +- {0x09, 0xff, 0x00}, +- {0x06, 0xff, 0x06}, +- {0xc8, 0xff, 0x40}, /* ED_LE_WIN_OLD = 0 */ +- {0x8d, 0x01, 0x01}, /* NEGATE_Q */ +- {0x32, 0xff, 0xac}, /* DIG_RFREFSELECT = 12 */ +- {0x42, 0xff, 0x43}, /* DIG_REG_AMP = 4 */ +- {0x74, 0xff, 0xc4}, /* SSPUR_FS_PRIO = 4 */ +- {0x71, 0xff, 0xe6}, /* SPUR_ROT_PRIO_VAL = 1 */ +- {0x83, 0xff, 0x64}, /* INF_FILT1_THD_SC = 100 */ +- {0x85, 0xff, 0x64}, /* INF_FILT2_THD_SC = 100 */ +- {0x88, 0xff, 0xf0}, /* INF_THD = 240 */ +- {0x6f, 0xf0, 0xb0}, /* DFE_DLY = 11 */ +- {0x00, 0xff, 0x01}, /* Change to page 1 */ +- {0x81, 0xff, 0x11}, /* DSM_FERR_BYPASS = 1 */ +- {0xf4, 0xff, 0x07}, /* DIG_FREQ_CORR = 1 */ +- {0xd4, 0x1f, 0x0f}, /* SPUR_TEST_NOISE_TH = 15 */ +- {0xd6, 0xff, 0x0c}, /* SPUR_TEST_NOISE_PAPR = 12 */ +- {0x00, 0xff, 0x00}, /* Change to page 0 */ +- {0, 0, 0} +- }; +- +- mxl_debug("()"); +- +- return mxl111sf_ctrl_program_regs(state, mxl_111_overwrite_default); +-} +- +-int mxl1x1sf_soft_reset(struct mxl111sf_state *state) +-{ +- int ret; +- mxl_debug("()"); +- +- ret = mxl111sf_write_reg(state, 0xff, 0x00); /* AIC */ +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_write_reg(state, 0x02, 0x01); /* get out of reset */ +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode) +-{ +- int ret; +- +- mxl_debug("(%s)", MXL_SOC_MODE == mode ? +- "MXL_SOC_MODE" : "MXL_TUNER_MODE"); +- +- /* set device mode */ +- ret = mxl111sf_write_reg(state, 0x03, +- MXL_SOC_MODE == mode ? 0x01 : 0x00); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg_mask(state, +- 0x7d, 0x40, MXL_SOC_MODE == mode ? +- 0x00 : /* enable impulse noise filter, +- INF_BYP = 0 */ +- 0x40); /* disable impulse noise filter, +- INF_BYP = 1 */ +- if (mxl_fail(ret)) +- goto fail; +- +- state->device_mode = mode; +-fail: +- return ret; +-} +- +-/* power up tuner */ +-int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff) +-{ +- mxl_debug("(%d)", onoff); +- +- return mxl111sf_write_reg(state, 0x01, onoff ? 0x01 : 0x00); +-} +- +-int mxl111sf_disable_656_port(struct mxl111sf_state *state) +-{ +- mxl_debug("()"); +- +- return mxl111sf_write_reg_mask(state, 0x12, 0x04, 0x00); +-} +- +-int mxl111sf_enable_usb_output(struct mxl111sf_state *state) +-{ +- mxl_debug("()"); +- +- return mxl111sf_write_reg_mask(state, 0x17, 0x40, 0x00); +-} +- +-/* initialize TSIF as input port of MxL1X1SF for MPEG2 data transfer */ +-int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, +- unsigned int parallel_serial, +- unsigned int msb_lsb_1st, +- unsigned int clock_phase, +- unsigned int mpeg_valid_pol, +- unsigned int mpeg_sync_pol) +-{ +- int ret; +- u8 mode, tmp; +- +- mxl_debug("(%u,%u,%u,%u,%u)", parallel_serial, msb_lsb_1st, +- clock_phase, mpeg_valid_pol, mpeg_sync_pol); +- +- /* Enable PIN MUX */ +- ret = mxl111sf_write_reg(state, V6_PIN_MUX_MODE_REG, V6_ENABLE_PIN_MUX); +- mxl_fail(ret); +- +- /* Configure MPEG Clock phase */ +- mxl111sf_read_reg(state, V6_MPEG_IN_CLK_INV_REG, &mode); +- +- if (clock_phase == TSIF_NORMAL) +- mode &= ~V6_INVERTED_CLK_PHASE; +- else +- mode |= V6_INVERTED_CLK_PHASE; +- +- ret = mxl111sf_write_reg(state, V6_MPEG_IN_CLK_INV_REG, mode); +- mxl_fail(ret); +- +- /* Configure data input mode, MPEG Valid polarity, MPEG Sync polarity +- * Get current configuration */ +- ret = mxl111sf_read_reg(state, V6_MPEG_IN_CTRL_REG, &mode); +- mxl_fail(ret); +- +- /* Data Input mode */ +- if (parallel_serial == TSIF_INPUT_PARALLEL) { +- /* Disable serial mode */ +- mode &= ~V6_MPEG_IN_DATA_SERIAL; +- +- /* Enable Parallel mode */ +- mode |= V6_MPEG_IN_DATA_PARALLEL; +- } else { +- /* Disable Parallel mode */ +- mode &= ~V6_MPEG_IN_DATA_PARALLEL; +- +- /* Enable Serial Mode */ +- mode |= V6_MPEG_IN_DATA_SERIAL; +- +- /* If serial interface is chosen, configure +- MSB or LSB order in transmission */ +- ret = mxl111sf_read_reg(state, +- V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, +- &tmp); +- mxl_fail(ret); +- +- if (msb_lsb_1st == MPEG_SER_MSB_FIRST_ENABLED) +- tmp |= V6_MPEG_SER_MSB_FIRST; +- else +- tmp &= ~V6_MPEG_SER_MSB_FIRST; +- +- ret = mxl111sf_write_reg(state, +- V6_MPEG_INOUT_BIT_ORDER_CTRL_REG, +- tmp); +- mxl_fail(ret); +- } +- +- /* MPEG Sync polarity */ +- if (mpeg_sync_pol == TSIF_NORMAL) +- mode &= ~V6_INVERTED_MPEG_SYNC; +- else +- mode |= V6_INVERTED_MPEG_SYNC; +- +- /* MPEG Valid polarity */ +- if (mpeg_valid_pol == 0) +- mode &= ~V6_INVERTED_MPEG_VALID; +- else +- mode |= V6_INVERTED_MPEG_VALID; +- +- ret = mxl111sf_write_reg(state, V6_MPEG_IN_CTRL_REG, mode); +- mxl_fail(ret); +- +- return ret; +-} +- +-int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size) +-{ +- static struct mxl111sf_reg_ctrl_info init_i2s[] = { +- {0x1b, 0xff, 0x1e}, /* pin mux mode, Choose 656/I2S input */ +- {0x15, 0x60, 0x60}, /* Enable I2S */ +- {0x17, 0xe0, 0x20}, /* Input, MPEG MODE USB, +- Inverted 656 Clock, I2S_SOFT_RESET, +- 0 : Normal operation, 1 : Reset State */ +-#if 0 +- {0x12, 0x01, 0x00}, /* AUDIO_IRQ_CLR (Overflow Indicator) */ +-#endif +- {0x00, 0xff, 0x02}, /* Change to Control Page */ +- {0x26, 0x0d, 0x0d}, /* I2S_MODE & BT656_SRC_SEL for FPGA only */ +- {0x00, 0xff, 0x00}, +- {0, 0, 0} +- }; +- int ret; +- +- mxl_debug("(0x%02x)", sample_size); +- +- ret = mxl111sf_ctrl_program_regs(state, init_i2s); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, V6_I2S_NUM_SAMPLES_REG, sample_size); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-int mxl111sf_disable_i2s_port(struct mxl111sf_state *state) +-{ +- static struct mxl111sf_reg_ctrl_info disable_i2s[] = { +- {0x15, 0x40, 0x00}, +- {0, 0, 0} +- }; +- +- mxl_debug("()"); +- +- return mxl111sf_ctrl_program_regs(state, disable_i2s); +-} +- +-int mxl111sf_config_i2s(struct mxl111sf_state *state, +- u8 msb_start_pos, u8 data_width) +-{ +- int ret; +- u8 tmp; +- +- mxl_debug("(0x%02x, 0x%02x)", msb_start_pos, data_width); +- +- ret = mxl111sf_read_reg(state, V6_I2S_STREAM_START_BIT_REG, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- +- tmp &= 0xe0; +- tmp |= msb_start_pos; +- ret = mxl111sf_write_reg(state, V6_I2S_STREAM_START_BIT_REG, tmp); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_read_reg(state, V6_I2S_STREAM_END_BIT_REG, &tmp); +- if (mxl_fail(ret)) +- goto fail; +- +- tmp &= 0xe0; +- tmp |= data_width; +- ret = mxl111sf_write_reg(state, V6_I2S_STREAM_END_BIT_REG, tmp); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff) +-{ +- u8 val; +- int ret; +- +- mxl_debug("(%d)", onoff); +- +- ret = mxl111sf_write_reg(state, 0x00, 0x02); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_read_reg(state, V8_SPI_MODE_REG, &val); +- if (mxl_fail(ret)) +- goto fail; +- +- if (onoff) +- val |= 0x04; +- else +- val &= ~0x04; +- +- ret = mxl111sf_write_reg(state, V8_SPI_MODE_REG, val); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_write_reg(state, 0x00, 0x00); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-int mxl111sf_idac_config(struct mxl111sf_state *state, +- u8 control_mode, u8 current_setting, +- u8 current_value, u8 hysteresis_value) +-{ +- int ret; +- u8 val; +- /* current value will be set for both automatic & manual IDAC control */ +- val = current_value; +- +- if (control_mode == IDAC_MANUAL_CONTROL) { +- /* enable manual control of IDAC */ +- val |= IDAC_MANUAL_CONTROL_BIT_MASK; +- +- if (current_setting == IDAC_CURRENT_SINKING_ENABLE) +- /* enable current sinking in manual mode */ +- val |= IDAC_CURRENT_SINKING_BIT_MASK; +- else +- /* disable current sinking in manual mode */ +- val &= ~IDAC_CURRENT_SINKING_BIT_MASK; +- } else { +- /* disable manual control of IDAC */ +- val &= ~IDAC_MANUAL_CONTROL_BIT_MASK; +- +- /* set hysteresis value reg: 0x0B<5:0> */ +- ret = mxl111sf_write_reg(state, V6_IDAC_HYSTERESIS_REG, +- (hysteresis_value & 0x3F)); +- mxl_fail(ret); +- } +- +- ret = mxl111sf_write_reg(state, V6_IDAC_SETTINGS_REG, val); +- mxl_fail(ret); +- +- return ret; +-} +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h b/drivers/media/dvb/dvb-usb/mxl111sf-phy.h +deleted file mode 100644 +index f075607..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-phy.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * mxl111sf-phy.h - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _DVB_USB_MXL111SF_PHY_H_ +-#define _DVB_USB_MXL111SF_PHY_H_ +- +-#include "mxl111sf.h" +- +-int mxl1x1sf_soft_reset(struct mxl111sf_state *state); +-int mxl1x1sf_set_device_mode(struct mxl111sf_state *state, int mode); +-int mxl1x1sf_top_master_ctrl(struct mxl111sf_state *state, int onoff); +-int mxl111sf_disable_656_port(struct mxl111sf_state *state); +-int mxl111sf_init_tuner_demod(struct mxl111sf_state *state); +-int mxl111sf_enable_usb_output(struct mxl111sf_state *state); +-int mxl111sf_config_mpeg_in(struct mxl111sf_state *state, +- unsigned int parallel_serial, +- unsigned int msb_lsb_1st, +- unsigned int clock_phase, +- unsigned int mpeg_valid_pol, +- unsigned int mpeg_sync_pol); +-int mxl111sf_config_i2s(struct mxl111sf_state *state, +- u8 msb_start_pos, u8 data_width); +-int mxl111sf_init_i2s_port(struct mxl111sf_state *state, u8 sample_size); +-int mxl111sf_disable_i2s_port(struct mxl111sf_state *state); +-int mxl111sf_config_spi(struct mxl111sf_state *state, int onoff); +-int mxl111sf_idac_config(struct mxl111sf_state *state, +- u8 control_mode, u8 current_setting, +- u8 current_value, u8 hysteresis_value); +- +-#endif /* _DVB_USB_MXL111SF_PHY_H_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h b/drivers/media/dvb/dvb-usb/mxl111sf-reg.h +deleted file mode 100644 +index 17831b0..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-reg.h ++++ /dev/null +@@ -1,179 +0,0 @@ +-/* +- * mxl111sf-reg.h - driver for the MaxLinear MXL111SF +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _DVB_USB_MXL111SF_REG_H_ +-#define _DVB_USB_MXL111SF_REG_H_ +- +-#define CHIP_ID_REG 0xFC +-#define TOP_CHIP_REV_ID_REG 0xFA +- +-#define V6_SNR_RB_LSB_REG 0x27 +-#define V6_SNR_RB_MSB_REG 0x28 +- +-#define V6_N_ACCUMULATE_REG 0x11 +-#define V6_RS_AVG_ERRORS_LSB_REG 0x2C +-#define V6_RS_AVG_ERRORS_MSB_REG 0x2D +- +-#define V6_IRQ_STATUS_REG 0x24 +-#define IRQ_MASK_FEC_LOCK 0x10 +- +-#define V6_SYNC_LOCK_REG 0x28 +-#define SYNC_LOCK_MASK 0x10 +- +-#define V6_RS_LOCK_DET_REG 0x28 +-#define RS_LOCK_DET_MASK 0x08 +- +-#define V6_INITACQ_NODETECT_REG 0x20 +-#define V6_FORCE_NFFT_CPSIZE_REG 0x20 +- +-#define V6_CODE_RATE_TPS_REG 0x29 +-#define V6_CODE_RATE_TPS_MASK 0x07 +- +- +-#define V6_CP_LOCK_DET_REG 0x28 +-#define V6_CP_LOCK_DET_MASK 0x04 +- +-#define V6_TPS_HIERACHY_REG 0x29 +-#define V6_TPS_HIERARCHY_INFO_MASK 0x40 +- +-#define V6_MODORDER_TPS_REG 0x2A +-#define V6_PARAM_CONSTELLATION_MASK 0x30 +- +-#define V6_MODE_TPS_REG 0x2A +-#define V6_PARAM_FFT_MODE_MASK 0x0C +- +- +-#define V6_CP_TPS_REG 0x29 +-#define V6_PARAM_GI_MASK 0x30 +- +-#define V6_TPS_LOCK_REG 0x2A +-#define V6_PARAM_TPS_LOCK_MASK 0x40 +- +-#define V6_FEC_PER_COUNT_REG 0x2E +-#define V6_FEC_PER_SCALE_REG 0x2B +-#define V6_FEC_PER_SCALE_MASK 0x03 +-#define V6_FEC_PER_CLR_REG 0x20 +-#define V6_FEC_PER_CLR_MASK 0x01 +- +-#define V6_PIN_MUX_MODE_REG 0x1B +-#define V6_ENABLE_PIN_MUX 0x1E +- +-#define V6_I2S_NUM_SAMPLES_REG 0x16 +- +-#define V6_MPEG_IN_CLK_INV_REG 0x17 +-#define V6_MPEG_IN_CTRL_REG 0x18 +- +-#define V6_INVERTED_CLK_PHASE 0x20 +-#define V6_MPEG_IN_DATA_PARALLEL 0x01 +-#define V6_MPEG_IN_DATA_SERIAL 0x02 +- +-#define V6_INVERTED_MPEG_SYNC 0x04 +-#define V6_INVERTED_MPEG_VALID 0x08 +- +-#define TSIF_INPUT_PARALLEL 0 +-#define TSIF_INPUT_SERIAL 1 +-#define TSIF_NORMAL 0 +- +-#define V6_MPEG_INOUT_BIT_ORDER_CTRL_REG 0x19 +-#define V6_MPEG_SER_MSB_FIRST 0x80 +-#define MPEG_SER_MSB_FIRST_ENABLED 0x01 +- +-#define V6_656_I2S_BUFF_STATUS_REG 0x2F +-#define V6_656_OVERFLOW_MASK_BIT 0x08 +-#define V6_I2S_OVERFLOW_MASK_BIT 0x01 +- +-#define V6_I2S_STREAM_START_BIT_REG 0x14 +-#define V6_I2S_STREAM_END_BIT_REG 0x15 +-#define I2S_RIGHT_JUSTIFIED 0 +-#define I2S_LEFT_JUSTIFIED 1 +-#define I2S_DATA_FORMAT 2 +- +-#define V6_TUNER_LOOP_THRU_CONTROL_REG 0x09 +-#define V6_ENABLE_LOOP_THRU 0x01 +- +-#define TOTAL_NUM_IF_OUTPUT_FREQ 16 +- +-#define TUNER_NORMAL_IF_SPECTRUM 0x0 +-#define TUNER_INVERT_IF_SPECTRUM 0x10 +- +-#define V6_TUNER_IF_SEL_REG 0x06 +-#define V6_TUNER_IF_FCW_REG 0x3C +-#define V6_TUNER_IF_FCW_BYP_REG 0x3D +-#define V6_RF_LOCK_STATUS_REG 0x23 +- +-#define NUM_DIG_TV_CHANNEL 1000 +- +-#define V6_DIG_CLK_FREQ_SEL_REG 0x07 +-#define V6_REF_SYNTH_INT_REG 0x5C +-#define V6_REF_SYNTH_REMAIN_REG 0x58 +-#define V6_DIG_RFREFSELECT_REG 0x32 +-#define V6_XTAL_CLK_OUT_GAIN_REG 0x31 +-#define V6_TUNER_LOOP_THRU_CTRL_REG 0x09 +-#define V6_DIG_XTAL_ENABLE_REG 0x06 +-#define V6_DIG_XTAL_BIAS_REG 0x66 +-#define V6_XTAL_CAP_REG 0x08 +- +-#define V6_GPO_CTRL_REG 0x18 +-#define MXL_GPO_0 0x00 +-#define MXL_GPO_1 0x01 +-#define V6_GPO_0_MASK 0x10 +-#define V6_GPO_1_MASK 0x20 +- +-#define V6_111SF_GPO_CTRL_REG 0x19 +-#define MXL_111SF_GPO_1 0x00 +-#define MXL_111SF_GPO_2 0x01 +-#define MXL_111SF_GPO_3 0x02 +-#define MXL_111SF_GPO_4 0x03 +-#define MXL_111SF_GPO_5 0x04 +-#define MXL_111SF_GPO_6 0x05 +-#define MXL_111SF_GPO_7 0x06 +- +-#define MXL_111SF_GPO_0_MASK 0x01 +-#define MXL_111SF_GPO_1_MASK 0x02 +-#define MXL_111SF_GPO_2_MASK 0x04 +-#define MXL_111SF_GPO_3_MASK 0x08 +-#define MXL_111SF_GPO_4_MASK 0x10 +-#define MXL_111SF_GPO_5_MASK 0x20 +-#define MXL_111SF_GPO_6_MASK 0x40 +- +-#define V6_ATSC_CONFIG_REG 0x0A +- +-#define MXL_MODE_REG 0x03 +-#define START_TUNE_REG 0x1C +- +-#define V6_IDAC_HYSTERESIS_REG 0x0B +-#define V6_IDAC_SETTINGS_REG 0x0C +-#define IDAC_MANUAL_CONTROL 1 +-#define IDAC_CURRENT_SINKING_ENABLE 1 +-#define IDAC_MANUAL_CONTROL_BIT_MASK 0x80 +-#define IDAC_CURRENT_SINKING_BIT_MASK 0x40 +- +-#define V8_SPI_MODE_REG 0xE9 +- +-#define V6_DIG_RF_PWR_LSB_REG 0x46 +-#define V6_DIG_RF_PWR_MSB_REG 0x47 +- +-#endif /* _DVB_USB_MXL111SF_REG_H_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c +deleted file mode 100644 +index 72db6ee..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c ++++ /dev/null +@@ -1,524 +0,0 @@ +-/* +- * mxl111sf-tuner.c - driver for the MaxLinear MXL111SF CMOS tuner +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "mxl111sf-tuner.h" +-#include "mxl111sf-phy.h" +-#include "mxl111sf-reg.h" +- +-/* debug */ +-static int mxl111sf_tuner_debug; +-module_param_named(debug, mxl111sf_tuner_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); +- +-#define mxl_dbg(fmt, arg...) \ +- if (mxl111sf_tuner_debug) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-/* ------------------------------------------------------------------------ */ +- +-struct mxl111sf_tuner_state { +- struct mxl111sf_state *mxl_state; +- +- struct mxl111sf_tuner_config *cfg; +- +- enum mxl_if_freq if_freq; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-static int mxl111sf_tuner_read_reg(struct mxl111sf_tuner_state *state, +- u8 addr, u8 *data) +-{ +- return (state->cfg->read_reg) ? +- state->cfg->read_reg(state->mxl_state, addr, data) : +- -EINVAL; +-} +- +-static int mxl111sf_tuner_write_reg(struct mxl111sf_tuner_state *state, +- u8 addr, u8 data) +-{ +- return (state->cfg->write_reg) ? +- state->cfg->write_reg(state->mxl_state, addr, data) : +- -EINVAL; +-} +- +-static int mxl111sf_tuner_program_regs(struct mxl111sf_tuner_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +-{ +- return (state->cfg->program_regs) ? +- state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : +- -EINVAL; +-} +- +-static int mxl1x1sf_tuner_top_master_ctrl(struct mxl111sf_tuner_state *state, +- int onoff) +-{ +- return (state->cfg->top_master_ctrl) ? +- state->cfg->top_master_ctrl(state->mxl_state, onoff) : +- -EINVAL; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static struct mxl111sf_reg_ctrl_info mxl_phy_tune_rf[] = { +- {0x1d, 0x7f, 0x00}, /* channel bandwidth section 1/2/3, +- DIG_MODEINDEX, _A, _CSF, */ +- {0x1e, 0xff, 0x00}, /* channel frequency (lo and fractional) */ +- {0x1f, 0xff, 0x00}, /* channel frequency (hi for integer portion) */ +- {0, 0, 0} +-}; +- +-/* ------------------------------------------------------------------------ */ +- +-static struct mxl111sf_reg_ctrl_info *mxl111sf_calc_phy_tune_regs(u32 freq, +- u8 bw) +-{ +- u8 filt_bw; +- +- /* set channel bandwidth */ +- switch (bw) { +- case 0: /* ATSC */ +- filt_bw = 25; +- break; +- case 1: /* QAM */ +- filt_bw = 69; +- break; +- case 6: +- filt_bw = 21; +- break; +- case 7: +- filt_bw = 42; +- break; +- case 8: +- filt_bw = 63; +- break; +- default: +- err("%s: invalid bandwidth setting!", __func__); +- return NULL; +- } +- +- /* calculate RF channel */ +- freq /= 1000000; +- +- freq *= 64; +-#if 0 +- /* do round */ +- freq += 0.5; +-#endif +- /* set bandwidth */ +- mxl_phy_tune_rf[0].data = filt_bw; +- +- /* set RF */ +- mxl_phy_tune_rf[1].data = (freq & 0xff); +- mxl_phy_tune_rf[2].data = (freq >> 8) & 0xff; +- +- /* start tune */ +- return mxl_phy_tune_rf; +-} +- +-static int mxl1x1sf_tuner_set_if_output_freq(struct mxl111sf_tuner_state *state) +-{ +- int ret; +- u8 ctrl; +-#if 0 +- u16 iffcw; +- u32 if_freq; +-#endif +- mxl_dbg("(IF polarity = %d, IF freq = 0x%02x)", +- state->cfg->invert_spectrum, state->cfg->if_freq); +- +- /* set IF polarity */ +- ctrl = state->cfg->invert_spectrum; +- +- ctrl |= state->cfg->if_freq; +- +- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_SEL_REG, ctrl); +- if (mxl_fail(ret)) +- goto fail; +- +-#if 0 +- if_freq /= 1000000; +- +- /* do round */ +- if_freq += 0.5; +- +- if (MXL_IF_LO == state->cfg->if_freq) { +- ctrl = 0x08; +- iffcw = (u16)(if_freq / (108 * 4096)); +- } else if (MXL_IF_HI == state->cfg->if_freq) { +- ctrl = 0x08; +- iffcw = (u16)(if_freq / (216 * 4096)); +- } else { +- ctrl = 0; +- iffcw = 0; +- } +- +- ctrl |= (iffcw >> 8); +-#endif +- ret = mxl111sf_tuner_read_reg(state, V6_TUNER_IF_FCW_BYP_REG, &ctrl); +- if (mxl_fail(ret)) +- goto fail; +- +- ctrl &= 0xf0; +- ctrl |= 0x90; +- +- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_BYP_REG, ctrl); +- if (mxl_fail(ret)) +- goto fail; +- +-#if 0 +- ctrl = iffcw & 0x00ff; +-#endif +- ret = mxl111sf_tuner_write_reg(state, V6_TUNER_IF_FCW_REG, ctrl); +- if (mxl_fail(ret)) +- goto fail; +- +- state->if_freq = state->cfg->if_freq; +-fail: +- return ret; +-} +- +-static int mxl1x1sf_tune_rf(struct dvb_frontend *fe, u32 freq, u8 bw) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- static struct mxl111sf_reg_ctrl_info *reg_ctrl_array; +- int ret; +- u8 mxl_mode; +- +- mxl_dbg("(freq = %d, bw = 0x%x)", freq, bw); +- +- /* stop tune */ +- ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 0); +- if (mxl_fail(ret)) +- goto fail; +- +- /* check device mode */ +- ret = mxl111sf_tuner_read_reg(state, MXL_MODE_REG, &mxl_mode); +- if (mxl_fail(ret)) +- goto fail; +- +- /* Fill out registers for channel tune */ +- reg_ctrl_array = mxl111sf_calc_phy_tune_regs(freq, bw); +- if (!reg_ctrl_array) +- return -EINVAL; +- +- ret = mxl111sf_tuner_program_regs(state, reg_ctrl_array); +- if (mxl_fail(ret)) +- goto fail; +- +- if ((mxl_mode & MXL_DEV_MODE_MASK) == MXL_TUNER_MODE) { +- /* IF tuner mode only */ +- mxl1x1sf_tuner_top_master_ctrl(state, 0); +- mxl1x1sf_tuner_top_master_ctrl(state, 1); +- mxl1x1sf_tuner_set_if_output_freq(state); +- } +- +- ret = mxl111sf_tuner_write_reg(state, START_TUNE_REG, 1); +- if (mxl_fail(ret)) +- goto fail; +- +- if (state->cfg->ant_hunt) +- state->cfg->ant_hunt(fe); +-fail: +- return ret; +-} +- +-static int mxl1x1sf_tuner_get_lock_status(struct mxl111sf_tuner_state *state, +- int *rf_synth_lock, +- int *ref_synth_lock) +-{ +- int ret; +- u8 data; +- +- *rf_synth_lock = 0; +- *ref_synth_lock = 0; +- +- ret = mxl111sf_tuner_read_reg(state, V6_RF_LOCK_STATUS_REG, &data); +- if (mxl_fail(ret)) +- goto fail; +- +- *ref_synth_lock = ((data & 0x03) == 0x03) ? 1 : 0; +- *rf_synth_lock = ((data & 0x0c) == 0x0c) ? 1 : 0; +-fail: +- return ret; +-} +- +-#if 0 +-static int mxl1x1sf_tuner_loop_thru_ctrl(struct mxl111sf_tuner_state *state, +- int onoff) +-{ +- return mxl111sf_tuner_write_reg(state, V6_TUNER_LOOP_THRU_CTRL_REG, +- onoff ? 1 : 0); +-} +-#endif +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- int ret; +- u8 bw; +- +- mxl_dbg("()"); +- +- switch (delsys) { +- case SYS_ATSC: +- bw = 0; /* ATSC */ +- break; +- case SYS_DVBC_ANNEX_B: +- bw = 1; /* US CABLE */ +- break; +- case SYS_DVBT: +- switch (c->bandwidth_hz) { +- case 6000000: +- bw = 6; +- break; +- case 7000000: +- bw = 7; +- break; +- case 8000000: +- bw = 8; +- break; +- default: +- err("%s: bandwidth not set!", __func__); +- return -EINVAL; +- } +- break; +- default: +- err("%s: modulation type not supported!", __func__); +- return -EINVAL; +- } +- ret = mxl1x1sf_tune_rf(fe, c->frequency, bw); +- if (mxl_fail(ret)) +- goto fail; +- +- state->frequency = c->frequency; +- state->bandwidth = c->bandwidth_hz; +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-#if 0 +-static int mxl111sf_tuner_init(struct dvb_frontend *fe) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- int ret; +- +- /* wake from standby handled by usb driver */ +- +- return ret; +-} +- +-static int mxl111sf_tuner_sleep(struct dvb_frontend *fe) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- int ret; +- +- /* enter standby mode handled by usb driver */ +- +- return ret; +-} +-#endif +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_tuner_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- int rf_locked, ref_locked, ret; +- +- *status = 0; +- +- ret = mxl1x1sf_tuner_get_lock_status(state, &rf_locked, &ref_locked); +- if (mxl_fail(ret)) +- goto fail; +- mxl_info("%s%s", rf_locked ? "rf locked " : "", +- ref_locked ? "ref locked" : ""); +- +- if ((rf_locked) || (ref_locked)) +- *status |= TUNER_STATUS_LOCKED; +-fail: +- return ret; +-} +- +-static int mxl111sf_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- u8 val1, val2; +- int ret; +- +- *strength = 0; +- +- ret = mxl111sf_tuner_write_reg(state, 0x00, 0x02); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_LSB_REG, &val1); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_tuner_read_reg(state, V6_DIG_RF_PWR_MSB_REG, &val2); +- if (mxl_fail(ret)) +- goto fail; +- +- *strength = val1 | ((val2 & 0x07) << 8); +-fail: +- ret = mxl111sf_tuner_write_reg(state, 0x00, 0x00); +- mxl_fail(ret); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_tuner_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- *frequency = state->frequency; +- return 0; +-} +- +-static int mxl111sf_tuner_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- *bandwidth = state->bandwidth; +- return 0; +-} +- +-static int mxl111sf_tuner_get_if_frequency(struct dvb_frontend *fe, +- u32 *frequency) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- +- *frequency = 0; +- +- switch (state->if_freq) { +- case MXL_IF_4_0: /* 4.0 MHz */ +- *frequency = 4000000; +- break; +- case MXL_IF_4_5: /* 4.5 MHz */ +- *frequency = 4500000; +- break; +- case MXL_IF_4_57: /* 4.57 MHz */ +- *frequency = 4570000; +- break; +- case MXL_IF_5_0: /* 5.0 MHz */ +- *frequency = 5000000; +- break; +- case MXL_IF_5_38: /* 5.38 MHz */ +- *frequency = 5380000; +- break; +- case MXL_IF_6_0: /* 6.0 MHz */ +- *frequency = 6000000; +- break; +- case MXL_IF_6_28: /* 6.28 MHz */ +- *frequency = 6280000; +- break; +- case MXL_IF_7_2: /* 7.2 MHz */ +- *frequency = 7200000; +- break; +- case MXL_IF_35_25: /* 35.25 MHz */ +- *frequency = 35250000; +- break; +- case MXL_IF_36: /* 36 MHz */ +- *frequency = 36000000; +- break; +- case MXL_IF_36_15: /* 36.15 MHz */ +- *frequency = 36150000; +- break; +- case MXL_IF_44: /* 44 MHz */ +- *frequency = 44000000; +- break; +- } +- return 0; +-} +- +-static int mxl111sf_tuner_release(struct dvb_frontend *fe) +-{ +- struct mxl111sf_tuner_state *state = fe->tuner_priv; +- mxl_dbg("()"); +- kfree(state); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-/* ------------------------------------------------------------------------- */ +- +-static struct dvb_tuner_ops mxl111sf_tuner_tuner_ops = { +- .info = { +- .name = "MaxLinear MxL111SF", +-#if 0 +- .frequency_min = , +- .frequency_max = , +- .frequency_step = , +-#endif +- }, +-#if 0 +- .init = mxl111sf_tuner_init, +- .sleep = mxl111sf_tuner_sleep, +-#endif +- .set_params = mxl111sf_tuner_set_params, +- .get_status = mxl111sf_tuner_get_status, +- .get_rf_strength = mxl111sf_get_rf_strength, +- .get_frequency = mxl111sf_tuner_get_frequency, +- .get_bandwidth = mxl111sf_tuner_get_bandwidth, +- .get_if_frequency = mxl111sf_tuner_get_if_frequency, +- .release = mxl111sf_tuner_release, +-}; +- +-struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, +- struct mxl111sf_state *mxl_state, +- struct mxl111sf_tuner_config *cfg) +-{ +- struct mxl111sf_tuner_state *state = NULL; +- +- mxl_dbg("()"); +- +- state = kzalloc(sizeof(struct mxl111sf_tuner_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- state->mxl_state = mxl_state; +- state->cfg = cfg; +- +- memcpy(&fe->ops.tuner_ops, &mxl111sf_tuner_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = state; +- return fe; +-} +-EXPORT_SYMBOL_GPL(mxl111sf_tuner_attach); +- +-MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.1"); +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h +deleted file mode 100644 +index ff33396..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.h ++++ /dev/null +@@ -1,89 +0,0 @@ +-/* +- * mxl111sf-tuner.h - driver for the MaxLinear MXL111SF CMOS tuner +- * +- * Copyright (C) 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __MXL111SF_TUNER_H__ +-#define __MXL111SF_TUNER_H__ +- +-#include "dvb_frontend.h" +- +-#include "mxl111sf.h" +- +-enum mxl_if_freq { +-#if 0 +- MXL_IF_LO = 0x00, /* other IF < 9MHz */ +-#endif +- MXL_IF_4_0 = 0x01, /* 4.0 MHz */ +- MXL_IF_4_5 = 0x02, /* 4.5 MHz */ +- MXL_IF_4_57 = 0x03, /* 4.57 MHz */ +- MXL_IF_5_0 = 0x04, /* 5.0 MHz */ +- MXL_IF_5_38 = 0x05, /* 5.38 MHz */ +- MXL_IF_6_0 = 0x06, /* 6.0 MHz */ +- MXL_IF_6_28 = 0x07, /* 6.28 MHz */ +- MXL_IF_7_2 = 0x08, /* 7.2 MHz */ +- MXL_IF_35_25 = 0x09, /* 35.25 MHz */ +- MXL_IF_36 = 0x0a, /* 36 MHz */ +- MXL_IF_36_15 = 0x0b, /* 36.15 MHz */ +- MXL_IF_44 = 0x0c, /* 44 MHz */ +-#if 0 +- MXL_IF_HI = 0x0f, /* other IF > 35 MHz and < 45 MHz */ +-#endif +-}; +- +-struct mxl111sf_tuner_config { +- enum mxl_if_freq if_freq; +- unsigned int invert_spectrum:1; +- +- int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); +- int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); +- int (*program_regs)(struct mxl111sf_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +- int (*top_master_ctrl)(struct mxl111sf_state *state, int onoff); +- int (*ant_hunt)(struct dvb_frontend *fe); +-}; +- +-/* ------------------------------------------------------------------------ */ +- +-#if defined(CONFIG_DVB_USB_MXL111SF) || \ +- (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +-extern +-struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, +- struct mxl111sf_state *mxl_state, +- struct mxl111sf_tuner_config *cfg); +-#else +-static inline +-struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, +- struct mxl111sf_state *mxl_state +- struct mxl111sf_tuner_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* __MXL111SF_TUNER_H__ */ +- +-/* +- * Overrides for Emacs so that we follow Linus's tabbing style. +- * --------------------------------------------------------------------------- +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +- +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c +deleted file mode 100644 +index 81305de..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf.c ++++ /dev/null +@@ -1,1059 +0,0 @@ +-/* +- * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +- +-#include +-#include +- +-#include "mxl111sf.h" +-#include "mxl111sf-reg.h" +-#include "mxl111sf-phy.h" +-#include "mxl111sf-i2c.h" +-#include "mxl111sf-gpio.h" +- +-#include "mxl111sf-demod.h" +-#include "mxl111sf-tuner.h" +- +-#include "lgdt3305.h" +- +-int dvb_usb_mxl111sf_debug; +-module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level " +- "(1=info, 2=xfer, 4=i2c, 8=reg, 16=adv (or-able))."); +- +-int dvb_usb_mxl111sf_isoc; +-module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); +-MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); +- +-#define ANT_PATH_AUTO 0 +-#define ANT_PATH_EXTERNAL 1 +-#define ANT_PATH_INTERNAL 2 +- +-int dvb_usb_mxl111sf_rfswitch = +-#if 0 +- ANT_PATH_AUTO; +-#else +- ANT_PATH_EXTERNAL; +-#endif +- +-module_param_named(rfswitch, dvb_usb_mxl111sf_rfswitch, int, 0644); +-MODULE_PARM_DESC(rfswitch, "force rf switch position (0=auto, 1=ext, 2=int)."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_info(args...) dprintk(dvb_usb_mxl111sf_debug, 0x13, args) +-#define deb_reg(args...) dprintk(dvb_usb_mxl111sf_debug, 0x08, args) +-#define deb_adv(args...) dprintk(dvb_usb_mxl111sf_debug, MXL_ADV_DBG, args) +- +-int mxl111sf_ctrl_msg(struct dvb_usb_device *d, +- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- int wo = (rbuf == NULL || rlen == 0); /* write-only */ +- int ret; +- u8 sndbuf[1+wlen]; +- +- deb_adv("%s(wlen = %d, rlen = %d)\n", __func__, wlen, rlen); +- +- memset(sndbuf, 0, 1+wlen); +- +- sndbuf[0] = cmd; +- memcpy(&sndbuf[1], wbuf, wlen); +- +- ret = (wo) ? dvb_usb_generic_write(d, sndbuf, 1+wlen) : +- dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0); +- mxl_fail(ret); +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-#define MXL_CMD_REG_READ 0xaa +-#define MXL_CMD_REG_WRITE 0x55 +- +-int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data) +-{ +- u8 buf[2]; +- int ret; +- +- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_READ, &addr, 1, buf, 2); +- if (mxl_fail(ret)) { +- mxl_debug("error reading reg: 0x%02x", addr); +- goto fail; +- } +- +- if (buf[0] == addr) +- *data = buf[1]; +- else { +- err("invalid response reading reg: 0x%02x != 0x%02x, 0x%02x", +- addr, buf[0], buf[1]); +- ret = -EINVAL; +- } +- +- deb_reg("R: (0x%02x, 0x%02x)\n", addr, *data); +-fail: +- return ret; +-} +- +-int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data) +-{ +- u8 buf[] = { addr, data }; +- int ret; +- +- deb_reg("W: (0x%02x, 0x%02x)\n", addr, data); +- +- ret = mxl111sf_ctrl_msg(state->d, MXL_CMD_REG_WRITE, buf, 2, NULL, 0); +- if (mxl_fail(ret)) +- err("error writing reg: 0x%02x, val: 0x%02x", addr, data); +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-int mxl111sf_write_reg_mask(struct mxl111sf_state *state, +- u8 addr, u8 mask, u8 data) +-{ +- int ret; +- u8 val; +- +- if (mask != 0xff) { +- ret = mxl111sf_read_reg(state, addr, &val); +-#if 1 +- /* dont know why this usually errors out on the first try */ +- if (mxl_fail(ret)) +- err("error writing addr: 0x%02x, mask: 0x%02x, " +- "data: 0x%02x, retrying...", addr, mask, data); +- +- ret = mxl111sf_read_reg(state, addr, &val); +-#endif +- if (mxl_fail(ret)) +- goto fail; +- } +- val &= ~mask; +- val |= data; +- +- ret = mxl111sf_write_reg(state, addr, val); +- mxl_fail(ret); +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +-{ +- int i, ret = 0; +- +- for (i = 0; ctrl_reg_info[i].addr | +- ctrl_reg_info[i].mask | +- ctrl_reg_info[i].data; i++) { +- +- ret = mxl111sf_write_reg_mask(state, +- ctrl_reg_info[i].addr, +- ctrl_reg_info[i].mask, +- ctrl_reg_info[i].data); +- if (mxl_fail(ret)) { +- err("failed on reg #%d (0x%02x)", i, +- ctrl_reg_info[i].addr); +- break; +- } +- } +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl1x1sf_get_chip_info(struct mxl111sf_state *state) +-{ +- int ret; +- u8 id, ver; +- char *mxl_chip, *mxl_rev; +- +- if ((state->chip_id) && (state->chip_ver)) +- return 0; +- +- ret = mxl111sf_read_reg(state, CHIP_ID_REG, &id); +- if (mxl_fail(ret)) +- goto fail; +- state->chip_id = id; +- +- ret = mxl111sf_read_reg(state, TOP_CHIP_REV_ID_REG, &ver); +- if (mxl_fail(ret)) +- goto fail; +- state->chip_ver = ver; +- +- switch (id) { +- case 0x61: +- mxl_chip = "MxL101SF"; +- break; +- case 0x63: +- mxl_chip = "MxL111SF"; +- break; +- default: +- mxl_chip = "UNKNOWN MxL1X1"; +- break; +- } +- switch (ver) { +- case 0x36: +- state->chip_rev = MXL111SF_V6; +- mxl_rev = "v6"; +- break; +- case 0x08: +- state->chip_rev = MXL111SF_V8_100; +- mxl_rev = "v8_100"; +- break; +- case 0x18: +- state->chip_rev = MXL111SF_V8_200; +- mxl_rev = "v8_200"; +- break; +- default: +- state->chip_rev = 0; +- mxl_rev = "UNKNOWN REVISION"; +- break; +- } +- info("%s detected, %s (0x%x)", mxl_chip, mxl_rev, ver); +-fail: +- return ret; +-} +- +-#define get_chip_info(state) \ +-({ \ +- int ___ret; \ +- ___ret = mxl1x1sf_get_chip_info(state); \ +- if (mxl_fail(___ret)) { \ +- mxl_debug("failed to get chip info" \ +- " on first probe attempt"); \ +- ___ret = mxl1x1sf_get_chip_info(state); \ +- if (mxl_fail(___ret)) \ +- err("failed to get chip info during probe"); \ +- else \ +- mxl_debug("probe needed a retry " \ +- "in order to succeed."); \ +- } \ +- ___ret; \ +-}) +- +-/* ------------------------------------------------------------------------ */ +- +-static int mxl111sf_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- /* power control depends on which adapter is being woken: +- * save this for init, instead, via mxl111sf_adap_fe_init */ +- return 0; +-} +- +-static int mxl111sf_adap_fe_init(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv; +- +- int err; +- +- /* exit if we didnt initialize the driver yet */ +- if (!state->chip_id) { +- mxl_debug("driver not yet initialized, exit."); +- goto fail; +- } +- +- deb_info("%s()\n", __func__); +- +- mutex_lock(&state->fe_lock); +- +- state->alt_mode = adap_state->alt_mode; +- +- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) +- err("set interface failed"); +- +- err = mxl1x1sf_soft_reset(state); +- mxl_fail(err); +- err = mxl111sf_init_tuner_demod(state); +- mxl_fail(err); +- err = mxl1x1sf_set_device_mode(state, adap_state->device_mode); +- +- mxl_fail(err); +- mxl111sf_enable_usb_output(state); +- mxl_fail(err); +- mxl1x1sf_top_master_ctrl(state, 1); +- mxl_fail(err); +- +- if ((MXL111SF_GPIO_MOD_DVBT != adap_state->gpio_mode) && +- (state->chip_rev > MXL111SF_V6)) { +- mxl111sf_config_pin_mux_modes(state, +- PIN_MUX_TS_SPI_IN_MODE_1); +- mxl_fail(err); +- } +- err = mxl111sf_init_port_expander(state); +- if (!mxl_fail(err)) { +- state->gpio_mode = adap_state->gpio_mode; +- err = mxl111sf_gpio_mode_switch(state, state->gpio_mode); +- mxl_fail(err); +-#if 0 +- err = fe->ops.init(fe); +-#endif +- msleep(100); /* add short delay after enabling +- * the demod before touching it */ +- } +- +- return (adap_state->fe_init) ? adap_state->fe_init(fe) : 0; +-fail: +- return -ENODEV; +-} +- +-static int mxl111sf_adap_fe_sleep(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe->id].priv; +- int err; +- +- /* exit if we didnt initialize the driver yet */ +- if (!state->chip_id) { +- mxl_debug("driver not yet initialized, exit."); +- goto fail; +- } +- +- deb_info("%s()\n", __func__); +- +- err = (adap_state->fe_sleep) ? adap_state->fe_sleep(fe) : 0; +- +- mutex_unlock(&state->fe_lock); +- +- return err; +-fail: +- return -ENODEV; +-} +- +- +-static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv; +- int ret = 0; +- u8 tmp; +- +- deb_info("%s(%d)\n", __func__, onoff); +- +- if (onoff) { +- ret = mxl111sf_enable_usb_output(state); +- mxl_fail(ret); +- ret = mxl111sf_config_mpeg_in(state, 1, 1, +- adap_state->ep6_clockphase, +- 0, 0); +- mxl_fail(ret); +-#if 0 +- } else { +- ret = mxl111sf_disable_656_port(state); +- mxl_fail(ret); +-#endif +- } +- +- return ret; +-} +- +-static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- int ret = 0; +- +- deb_info("%s(%d)\n", __func__, onoff); +- +- if (onoff) { +- ret = mxl111sf_enable_usb_output(state); +- mxl_fail(ret); +- } +- +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static struct lgdt3305_config hauppauge_lgdt3305_config = { +- .i2c_addr = 0xb2 >> 1, +- .mpeg_mode = LGDT3305_MPEG_SERIAL, +- .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, +- .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, +- .deny_i2c_rptr = 1, +- .spectral_inversion = 0, +- .qam_if_khz = 6000, +- .vsb_if_khz = 6000, +-}; +- +-static int mxl111sf_lgdt3305_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- int fe_id = adap->num_frontends_initialized; +- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; +- int ret; +- +- deb_adv("%s()\n", __func__); +- +- /* save a pointer to the dvb_usb_device in device state */ +- state->d = d; +- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; +- state->alt_mode = adap_state->alt_mode; +- +- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) +- err("set interface failed"); +- +- state->gpio_mode = MXL111SF_GPIO_MOD_ATSC; +- adap_state->gpio_mode = state->gpio_mode; +- adap_state->device_mode = MXL_TUNER_MODE; +- adap_state->ep6_clockphase = 1; +- +- ret = mxl1x1sf_soft_reset(state); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_init_tuner_demod(state); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_enable_usb_output(state); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl1x1sf_top_master_ctrl(state, 1); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_init_port_expander(state); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); +- if (mxl_fail(ret)) +- goto fail; +- +- adap->fe_adap[fe_id].fe = dvb_attach(lgdt3305_attach, +- &hauppauge_lgdt3305_config, +- &adap->dev->i2c_adap); +- if (adap->fe_adap[fe_id].fe) { +- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; +- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; +- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; +- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; +- return 0; +- } +- ret = -EIO; +-fail: +- return ret; +-} +- +-static struct mxl111sf_demod_config mxl_demod_config = { +- .read_reg = mxl111sf_read_reg, +- .write_reg = mxl111sf_write_reg, +- .program_regs = mxl111sf_ctrl_program_regs, +-}; +- +-static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- int fe_id = adap->num_frontends_initialized; +- struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; +- int ret; +- +- deb_adv("%s()\n", __func__); +- +- /* save a pointer to the dvb_usb_device in device state */ +- state->d = d; +- adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2; +- state->alt_mode = adap_state->alt_mode; +- +- if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) +- err("set interface failed"); +- +- state->gpio_mode = MXL111SF_GPIO_MOD_DVBT; +- adap_state->gpio_mode = state->gpio_mode; +- adap_state->device_mode = MXL_SOC_MODE; +- adap_state->ep6_clockphase = 1; +- +- ret = mxl1x1sf_soft_reset(state); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl111sf_init_tuner_demod(state); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); +- if (mxl_fail(ret)) +- goto fail; +- +- ret = mxl111sf_enable_usb_output(state); +- if (mxl_fail(ret)) +- goto fail; +- ret = mxl1x1sf_top_master_ctrl(state, 1); +- if (mxl_fail(ret)) +- goto fail; +- +- /* dont care if this fails */ +- mxl111sf_init_port_expander(state); +- +- adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state, +- &mxl_demod_config); +- if (adap->fe_adap[fe_id].fe) { +- adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; +- adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; +- adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; +- adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; +- return 0; +- } +- ret = -EIO; +-fail: +- return ret; +-} +- +-static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state, +- int antpath) +-{ +- return mxl111sf_idac_config(state, 1, 1, +- (antpath == ANT_PATH_INTERNAL) ? +- 0x3f : 0x00, 0); +-} +- +-#define DbgAntHunt(x, pwr0, pwr1, pwr2, pwr3) \ +- err("%s(%d) FINAL input set to %s rxPwr:%d|%d|%d|%d\n", \ +- __func__, __LINE__, \ +- (ANT_PATH_EXTERNAL == x) ? "EXTERNAL" : "INTERNAL", \ +- pwr0, pwr1, pwr2, pwr3) +- +-#define ANT_HUNT_SLEEP 90 +-#define ANT_EXT_TWEAK 0 +- +-static int mxl111sf_ant_hunt(struct dvb_frontend *fe) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- +- int antctrl = dvb_usb_mxl111sf_rfswitch; +- +- u16 rxPwrA, rxPwr0, rxPwr1, rxPwr2; +- +- /* FIXME: must force EXTERNAL for QAM - done elsewhere */ +- mxl111sf_set_ant_path(state, antctrl == ANT_PATH_AUTO ? +- ANT_PATH_EXTERNAL : antctrl); +- +- if (antctrl == ANT_PATH_AUTO) { +-#if 0 +- msleep(ANT_HUNT_SLEEP); +-#endif +- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwrA); +- +- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); +- msleep(ANT_HUNT_SLEEP); +- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr0); +- +- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); +- msleep(ANT_HUNT_SLEEP); +- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr1); +- +- mxl111sf_set_ant_path(state, ANT_PATH_INTERNAL); +- msleep(ANT_HUNT_SLEEP); +- fe->ops.tuner_ops.get_rf_strength(fe, &rxPwr2); +- +- if (rxPwr1+ANT_EXT_TWEAK >= rxPwr2) { +- /* return with EXTERNAL enabled */ +- mxl111sf_set_ant_path(state, ANT_PATH_EXTERNAL); +- DbgAntHunt(ANT_PATH_EXTERNAL, rxPwrA, +- rxPwr0, rxPwr1, rxPwr2); +- } else { +- /* return with INTERNAL enabled */ +- DbgAntHunt(ANT_PATH_INTERNAL, rxPwrA, +- rxPwr0, rxPwr1, rxPwr2); +- } +- } +- return 0; +-} +- +-static struct mxl111sf_tuner_config mxl_tuner_config = { +- .if_freq = MXL_IF_6_0, /* applies to external IF output, only */ +- .invert_spectrum = 0, +- .read_reg = mxl111sf_read_reg, +- .write_reg = mxl111sf_write_reg, +- .program_regs = mxl111sf_ctrl_program_regs, +- .top_master_ctrl = mxl1x1sf_top_master_ctrl, +- .ant_hunt = mxl111sf_ant_hunt, +-}; +- +-static int mxl111sf_attach_tuner(struct dvb_usb_adapter *adap) +-{ +- struct dvb_usb_device *d = adap->dev; +- struct mxl111sf_state *state = d->priv; +- int fe_id = adap->num_frontends_initialized; +- +- deb_adv("%s()\n", __func__); +- +- if (NULL != dvb_attach(mxl111sf_tuner_attach, +- adap->fe_adap[fe_id].fe, state, +- &mxl_tuner_config)) +- return 0; +- +- return -EIO; +-} +- +-static int mxl111sf_fe_ioctl_override(struct dvb_frontend *fe, +- unsigned int cmd, void *parg, +- unsigned int stage) +-{ +- int err = 0; +- +- switch (stage) { +- case DVB_FE_IOCTL_PRE: +- +- switch (cmd) { +- case FE_READ_SIGNAL_STRENGTH: +- err = fe->ops.tuner_ops.get_rf_strength(fe, parg); +- /* If no error occurs, prevent dvb-core from handling +- * this IOCTL, otherwise return the error */ +- if (0 == err) +- err = 1; +- break; +- } +- break; +- +- case DVB_FE_IOCTL_POST: +- /* no post-ioctl handling required */ +- break; +- } +- return err; +-}; +- +-static u32 mxl111sf_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-struct i2c_algorithm mxl111sf_i2c_algo = { +- .master_xfer = mxl111sf_i2c_xfer, +- .functionality = mxl111sf_i2c_func, +-#ifdef NEED_ALGO_CONTROL +- .algo_control = dummy_algo_control, +-#endif +-}; +- +-static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties; +-static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties; +-static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties; +-static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties; +- +-static int mxl111sf_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d = NULL; +- +- deb_adv("%s()\n", __func__); +- +- if (((dvb_usb_mxl111sf_isoc) && +- (0 == dvb_usb_device_init(intf, +- &mxl111sf_dvbt_isoc_properties, +- THIS_MODULE, &d, adapter_nr) || +- 0 == dvb_usb_device_init(intf, +- &mxl111sf_atsc_isoc_properties, +- THIS_MODULE, &d, adapter_nr))) || +- 0 == dvb_usb_device_init(intf, +- &mxl111sf_dvbt_bulk_properties, +- THIS_MODULE, &d, adapter_nr) || +- 0 == dvb_usb_device_init(intf, +- &mxl111sf_atsc_bulk_properties, +- THIS_MODULE, &d, adapter_nr) || 0) { +- +- struct mxl111sf_state *state = d->priv; +- static u8 eeprom[256]; +- struct i2c_client c; +- int ret; +- +- ret = get_chip_info(state); +- if (mxl_fail(ret)) +- err("failed to get chip info during probe"); +- +- mutex_init(&state->fe_lock); +- +- if (state->chip_rev > MXL111SF_V6) +- mxl111sf_config_pin_mux_modes(state, +- PIN_MUX_TS_SPI_IN_MODE_1); +- +- c.adapter = &d->i2c_adap; +- c.addr = 0xa0 >> 1; +- +- ret = tveeprom_read(&c, eeprom, sizeof(eeprom)); +- if (mxl_fail(ret)) +- return 0; +- tveeprom_hauppauge_analog(&c, &state->tv, +- (0x84 == eeprom[0xa0]) ? +- eeprom + 0xa0 : eeprom + 0x80); +-#if 0 +- switch (state->tv.model) { +- case 117001: +- case 126001: +- case 138001: +- break; +- default: +- printk(KERN_WARNING "%s: warning: " +- "unknown hauppauge model #%d\n", +- __func__, state->tv.model); +- } +-#endif +- return 0; +- } +- err("Your device is not yet supported by this driver. " +- "See kernellabs.com for more info"); +- return -EINVAL; +-} +- +-static struct usb_device_id mxl111sf_table[] = { +-/* 0 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc600) }, /* ATSC+ IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc601) }, /* ATSC */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc602) }, /* + */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc603) }, /* ATSC+ */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc604) }, /* DVBT */ +-/* 5 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc609) }, /* ATSC IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60a) }, /* + IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60b) }, /* ATSC+ IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc60c) }, /* DVBT IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc653) }, /* ATSC+ */ +-/*10 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc65b) }, /* ATSC+ IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb700) }, /* ATSC+ sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb701) }, /* ATSC sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb702) }, /* + sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb703) }, /* ATSC+ sw */ +-/*15 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb704) }, /* DVBT sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb753) }, /* ATSC+ sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb763) }, /* ATSC+ no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb764) }, /* DVBT no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd853) }, /* ATSC+ sw */ +-/*20 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd854) }, /* DVBT sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd863) }, /* ATSC+ no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd864) }, /* DVBT no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d3) }, /* ATSC+ sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8d4) }, /* DVBT sw */ +-/*25 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e3) }, /* ATSC+ no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8e4) }, /* DVBT no */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xd8ff) }, /* ATSC+ */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc612) }, /* + */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc613) }, /* ATSC+ */ +-/*30 */ { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61a) }, /* + IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xc61b) }, /* ATSC+ IR */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb757) }, /* ATSC+DVBT sw */ +- { USB_DEVICE(USB_VID_HAUPPAUGE, 0xb767) }, /* ATSC+DVBT no */ +- {} /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, mxl111sf_table); +- +- +-#define MXL111SF_EP4_BULK_STREAMING_CONFIG \ +- .size_of_priv = sizeof(struct mxl111sf_adap_state), \ +- .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ +- .stream = { \ +- .type = USB_BULK, \ +- .count = 5, \ +- .endpoint = 0x04, \ +- .u = { \ +- .bulk = { \ +- .buffersize = 8192, \ +- } \ +- } \ +- } +- +-/* FIXME: works for v6 but not v8 silicon */ +-#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \ +- .size_of_priv = sizeof(struct mxl111sf_adap_state), \ +- .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ +- .stream = { \ +- .type = USB_ISOC, \ +- .count = 5, \ +- .endpoint = 0x04, \ +- .u = { \ +- .isoc = { \ +- .framesperurb = 96, \ +- /* FIXME: v6 SILICON: */ \ +- .framesize = 564, \ +- .interval = 1, \ +- } \ +- } \ +- } +- +-#define MXL111SF_EP6_BULK_STREAMING_CONFIG \ +- .size_of_priv = sizeof(struct mxl111sf_adap_state), \ +- .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ +- .stream = { \ +- .type = USB_BULK, \ +- .count = 5, \ +- .endpoint = 0x06, \ +- .u = { \ +- .bulk = { \ +- .buffersize = 8192, \ +- } \ +- } \ +- } +- +-/* FIXME */ +-#define MXL111SF_EP6_ISOC_STREAMING_CONFIG \ +- .size_of_priv = sizeof(struct mxl111sf_adap_state), \ +- .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ +- .stream = { \ +- .type = USB_ISOC, \ +- .count = 5, \ +- .endpoint = 0x06, \ +- .u = { \ +- .isoc = { \ +- .framesperurb = 24, \ +- .framesize = 3072, \ +- .interval = 1, \ +- } \ +- } \ +- } +- +-#define MXL111SF_DEFAULT_DEVICE_PROPERTIES \ +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, \ +- .usb_ctrl = DEVICE_SPECIFIC, \ +- /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), \ +- EP6 BULK transfer (atsc/qam), \ +- use usb alt setting 2 for EP4 BULK transfer (dvb-t), \ +- EP6 ISOC transfer (atsc/qam), \ +- */ \ +- .power_ctrl = mxl111sf_power_ctrl, \ +- .i2c_algo = &mxl111sf_i2c_algo, \ +- .generic_bulk_ctrl_endpoint = MXL_EP2_REG_WRITE, \ +- .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \ +- .size_of_priv = sizeof(struct mxl111sf_state) +- +-static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { +- MXL111SF_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .fe_ioctl_override = mxl111sf_fe_ioctl_override, +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = mxl111sf_attach_demod, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP4_BULK_STREAMING_CONFIG, +- } }, +- }, +- }, +- .num_device_descs = 4, +- .devices = { +- { "Hauppauge 126xxx DVBT (bulk)", +- { NULL }, +- { &mxl111sf_table[4], &mxl111sf_table[8], +- NULL }, +- }, +- { "Hauppauge 117xxx DVBT (bulk)", +- { NULL }, +- { &mxl111sf_table[15], &mxl111sf_table[18], +- NULL }, +- }, +- { "Hauppauge 138xxx DVBT (bulk)", +- { NULL }, +- { &mxl111sf_table[20], &mxl111sf_table[22], +- &mxl111sf_table[24], &mxl111sf_table[26], +- NULL }, +- }, +- { "Hauppauge 126xxx (tp-bulk)", +- { NULL }, +- { &mxl111sf_table[28], &mxl111sf_table[30], +- NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { +- MXL111SF_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .fe_ioctl_override = mxl111sf_fe_ioctl_override, +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = mxl111sf_attach_demod, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP4_ISOC_STREAMING_CONFIG, +- } }, +- }, +- }, +- .num_device_descs = 4, +- .devices = { +- { "Hauppauge 126xxx DVBT (isoc)", +- { NULL }, +- { &mxl111sf_table[4], &mxl111sf_table[8], +- NULL }, +- }, +- { "Hauppauge 117xxx DVBT (isoc)", +- { NULL }, +- { &mxl111sf_table[15], &mxl111sf_table[18], +- NULL }, +- }, +- { "Hauppauge 138xxx DVBT (isoc)", +- { NULL }, +- { &mxl111sf_table[20], &mxl111sf_table[22], +- &mxl111sf_table[24], &mxl111sf_table[26], +- NULL }, +- }, +- { "Hauppauge 126xxx (tp-isoc)", +- { NULL }, +- { &mxl111sf_table[28], &mxl111sf_table[30], +- NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { +- MXL111SF_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .fe_ioctl_override = mxl111sf_fe_ioctl_override, +- .num_frontends = 2, +- .fe = {{ +- .frontend_attach = mxl111sf_lgdt3305_frontend_attach, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP6_BULK_STREAMING_CONFIG, +- }, +- { +- .frontend_attach = mxl111sf_attach_demod, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP4_BULK_STREAMING_CONFIG, +- }}, +- }, +- }, +- .num_device_descs = 6, +- .devices = { +- { "Hauppauge 126xxx ATSC (bulk)", +- { NULL }, +- { &mxl111sf_table[1], &mxl111sf_table[5], +- NULL }, +- }, +- { "Hauppauge 117xxx ATSC (bulk)", +- { NULL }, +- { &mxl111sf_table[12], +- NULL }, +- }, +- { "Hauppauge 126xxx ATSC+ (bulk)", +- { NULL }, +- { &mxl111sf_table[0], &mxl111sf_table[3], +- &mxl111sf_table[7], &mxl111sf_table[9], +- &mxl111sf_table[10], NULL }, +- }, +- { "Hauppauge 117xxx ATSC+ (bulk)", +- { NULL }, +- { &mxl111sf_table[11], &mxl111sf_table[14], +- &mxl111sf_table[16], &mxl111sf_table[17], +- &mxl111sf_table[32], &mxl111sf_table[33], +- NULL }, +- }, +- { "Hauppauge Mercury (tp-bulk)", +- { NULL }, +- { &mxl111sf_table[19], &mxl111sf_table[21], +- &mxl111sf_table[23], &mxl111sf_table[25], +- &mxl111sf_table[27], NULL }, +- }, +- { "Hauppauge WinTV-Aero-M", +- { NULL }, +- { &mxl111sf_table[29], &mxl111sf_table[31], +- NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { +- MXL111SF_DEFAULT_DEVICE_PROPERTIES, +- +- .num_adapters = 1, +- .adapter = { +- { +- .fe_ioctl_override = mxl111sf_fe_ioctl_override, +- .num_frontends = 2, +- .fe = {{ +- .frontend_attach = mxl111sf_lgdt3305_frontend_attach, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP6_ISOC_STREAMING_CONFIG, +- }, +- { +- .frontend_attach = mxl111sf_attach_demod, +- .tuner_attach = mxl111sf_attach_tuner, +- +- MXL111SF_EP4_ISOC_STREAMING_CONFIG, +- }}, +- }, +- }, +- .num_device_descs = 6, +- .devices = { +- { "Hauppauge 126xxx ATSC (isoc)", +- { NULL }, +- { &mxl111sf_table[1], &mxl111sf_table[5], +- NULL }, +- }, +- { "Hauppauge 117xxx ATSC (isoc)", +- { NULL }, +- { &mxl111sf_table[12], +- NULL }, +- }, +- { "Hauppauge 126xxx ATSC+ (isoc)", +- { NULL }, +- { &mxl111sf_table[0], &mxl111sf_table[3], +- &mxl111sf_table[7], &mxl111sf_table[9], +- &mxl111sf_table[10], NULL }, +- }, +- { "Hauppauge 117xxx ATSC+ (isoc)", +- { NULL }, +- { &mxl111sf_table[11], &mxl111sf_table[14], +- &mxl111sf_table[16], &mxl111sf_table[17], +- &mxl111sf_table[32], &mxl111sf_table[33], +- NULL }, +- }, +- { "Hauppauge Mercury (tp-isoc)", +- { NULL }, +- { &mxl111sf_table[19], &mxl111sf_table[21], +- &mxl111sf_table[23], &mxl111sf_table[25], +- &mxl111sf_table[27], NULL }, +- }, +- { "Hauppauge WinTV-Aero-M (tp-isoc)", +- { NULL }, +- { &mxl111sf_table[29], &mxl111sf_table[31], +- NULL }, +- }, +- } +-}; +- +-static struct usb_driver mxl111sf_driver = { +- .name = "dvb_usb_mxl111sf", +- .probe = mxl111sf_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = mxl111sf_table, +-}; +- +-module_usb_driver(mxl111sf_driver); +- +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h +deleted file mode 100644 +index 364d89f..0000000 +--- a/drivers/media/dvb/dvb-usb/mxl111sf.h ++++ /dev/null +@@ -1,158 +0,0 @@ +-/* +- * Copyright (C) 2010 Michael Krufky (mkrufky@kernellabs.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +- +-#ifndef _DVB_USB_MXL111SF_H_ +-#define _DVB_USB_MXL111SF_H_ +- +-#ifdef DVB_USB_LOG_PREFIX +-#undef DVB_USB_LOG_PREFIX +-#endif +-#define DVB_USB_LOG_PREFIX "mxl111sf" +-#include "dvb-usb.h" +-#include +- +-#define MXL_EP1_REG_READ 1 +-#define MXL_EP2_REG_WRITE 2 +-#define MXL_EP3_INTERRUPT 3 +-#define MXL_EP4_MPEG2 4 +-#define MXL_EP5_I2S 5 +-#define MXL_EP6_656 6 +-#define MXL_EP6_MPEG2 6 +- +-#ifdef USING_ENUM_mxl111sf_current_mode +-enum mxl111sf_current_mode { +- mxl_mode_dvbt = MXL_EP4_MPEG2, +- mxl_mode_mh = MXL_EP5_I2S, +- mxl_mode_atsc = MXL_EP6_MPEG2, +-}; +-#endif +- +-enum mxl111sf_gpio_port_expander { +- mxl111sf_gpio_hw, +- mxl111sf_PCA9534, +-}; +- +-struct mxl111sf_state { +- struct dvb_usb_device *d; +- +- enum mxl111sf_gpio_port_expander gpio_port_expander; +- u8 port_expander_addr; +- +- u8 chip_id; +- u8 chip_ver; +-#define MXL111SF_V6 1 +-#define MXL111SF_V8_100 2 +-#define MXL111SF_V8_200 3 +- u8 chip_rev; +- +-#ifdef USING_ENUM_mxl111sf_current_mode +- enum mxl111sf_current_mode current_mode; +-#endif +- +-#define MXL_TUNER_MODE 0 +-#define MXL_SOC_MODE 1 +-#define MXL_DEV_MODE_MASK 0x01 +-#if 1 +- int device_mode; +-#endif +- /* use usb alt setting 1 for EP4 ISOC transfer (dvb-t), +- EP5 BULK transfer (atsc-mh), +- EP6 BULK transfer (atsc/qam), +- use usb alt setting 2 for EP4 BULK transfer (dvb-t), +- EP5 ISOC transfer (atsc-mh), +- EP6 ISOC transfer (atsc/qam), +- */ +- int alt_mode; +- int gpio_mode; +- struct tveeprom tv; +- +- struct mutex fe_lock; +-}; +- +-struct mxl111sf_adap_state { +- int alt_mode; +- int gpio_mode; +- int device_mode; +- int ep6_clockphase; +- int (*fe_init)(struct dvb_frontend *); +- int (*fe_sleep)(struct dvb_frontend *); +-}; +- +-int mxl111sf_read_reg(struct mxl111sf_state *state, u8 addr, u8 *data); +-int mxl111sf_write_reg(struct mxl111sf_state *state, u8 addr, u8 data); +- +-struct mxl111sf_reg_ctrl_info { +- u8 addr; +- u8 mask; +- u8 data; +-}; +- +-int mxl111sf_write_reg_mask(struct mxl111sf_state *state, +- u8 addr, u8 mask, u8 data); +-int mxl111sf_ctrl_program_regs(struct mxl111sf_state *state, +- struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +- +-/* needed for hardware i2c functions in mxl111sf-i2c.c: +- * mxl111sf_i2c_send_data / mxl111sf_i2c_get_data */ +-int mxl111sf_ctrl_msg(struct dvb_usb_device *d, +- u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen); +- +-#define mxl_printk(kern, fmt, arg...) \ +- printk(kern "%s: " fmt "\n", __func__, ##arg) +- +-#define mxl_info(fmt, arg...) \ +- mxl_printk(KERN_INFO, fmt, ##arg) +- +-extern int dvb_usb_mxl111sf_debug; +-#define mxl_debug(fmt, arg...) \ +- if (dvb_usb_mxl111sf_debug) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-#define MXL_I2C_DBG 0x04 +-#define MXL_ADV_DBG 0x10 +-#define mxl_debug_adv(fmt, arg...) \ +- if (dvb_usb_mxl111sf_debug & MXL_ADV_DBG) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-#define mxl_i2c(fmt, arg...) \ +- if (dvb_usb_mxl111sf_debug & MXL_I2C_DBG) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-#define mxl_i2c_adv(fmt, arg...) \ +- if ((dvb_usb_mxl111sf_debug & (MXL_I2C_DBG | MXL_ADV_DBG)) == \ +- (MXL_I2C_DBG | MXL_ADV_DBG)) \ +- mxl_printk(KERN_DEBUG, fmt, ##arg) +- +-/* The following allows the mxl_fail() macro defined below to work +- * in externel modules, such as mxl111sf-tuner.ko, even though +- * dvb_usb_mxl111sf_debug is not defined within those modules */ +-#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__)) +-#define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG +-#else +-#define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug +-#endif +- +-#define mxl_fail(ret) \ +-({ \ +- int __ret; \ +- __ret = (ret < 0); \ +- if ((__ret) && (MXL_ADV_DEBUG_ENABLED & MXL_ADV_DBG)) \ +- mxl_printk(KERN_ERR, "error %d on line %d", \ +- ret, __LINE__); \ +- __ret; \ +-}) +- +-#endif /* _DVB_USB_MXL111SF_H_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c +deleted file mode 100644 +index 6c55384..0000000 +--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c ++++ /dev/null +@@ -1,233 +0,0 @@ +-/* DVB USB framework compliant Linux driver for the Hauppauge WinTV-NOVA-T usb2 +- * DVB-T receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=rc,2=eeprom (|-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_rc(args...) dprintk(debug,0x01,args) +-#define deb_ee(args...) dprintk(debug,0x02,args) +- +-/* Hauppauge NOVA-T USB2 keys */ +-static struct rc_map_table rc_map_haupp_table[] = { +- { 0x1e00, KEY_0 }, +- { 0x1e01, KEY_1 }, +- { 0x1e02, KEY_2 }, +- { 0x1e03, KEY_3 }, +- { 0x1e04, KEY_4 }, +- { 0x1e05, KEY_5 }, +- { 0x1e06, KEY_6 }, +- { 0x1e07, KEY_7 }, +- { 0x1e08, KEY_8 }, +- { 0x1e09, KEY_9 }, +- { 0x1e0a, KEY_KPASTERISK }, +- { 0x1e0b, KEY_RED }, +- { 0x1e0c, KEY_RADIO }, +- { 0x1e0d, KEY_MENU }, +- { 0x1e0e, KEY_GRAVE }, /* # */ +- { 0x1e0f, KEY_MUTE }, +- { 0x1e10, KEY_VOLUMEUP }, +- { 0x1e11, KEY_VOLUMEDOWN }, +- { 0x1e12, KEY_CHANNEL }, +- { 0x1e14, KEY_UP }, +- { 0x1e15, KEY_DOWN }, +- { 0x1e16, KEY_LEFT }, +- { 0x1e17, KEY_RIGHT }, +- { 0x1e18, KEY_VIDEO }, +- { 0x1e19, KEY_AUDIO }, +- { 0x1e1a, KEY_IMAGES }, +- { 0x1e1b, KEY_EPG }, +- { 0x1e1c, KEY_TV }, +- { 0x1e1e, KEY_NEXT }, +- { 0x1e1f, KEY_BACK }, +- { 0x1e20, KEY_CHANNELUP }, +- { 0x1e21, KEY_CHANNELDOWN }, +- { 0x1e24, KEY_LAST }, /* Skip backwards */ +- { 0x1e25, KEY_OK }, +- { 0x1e29, KEY_BLUE}, +- { 0x1e2e, KEY_GREEN }, +- { 0x1e30, KEY_PAUSE }, +- { 0x1e32, KEY_REWIND }, +- { 0x1e34, KEY_FASTFORWARD }, +- { 0x1e35, KEY_PLAY }, +- { 0x1e36, KEY_STOP }, +- { 0x1e37, KEY_RECORD }, +- { 0x1e38, KEY_YELLOW }, +- { 0x1e3b, KEY_GOTO }, +- { 0x1e3d, KEY_POWER }, +-}; +- +-/* Firmware bug? sometimes, when a new key is pressed, the previous pressed key +- * is delivered. No workaround yet, maybe a new firmware. +- */ +-static int nova_t_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- u8 key[5],cmd[2] = { DIBUSB_REQ_POLL_REMOTE, 0x35 }, data,toggle,custom; +- u16 raw; +- int i; +- struct dibusb_device_state *st = d->priv; +- +- dvb_usb_generic_rw(d,cmd,2,key,5,0); +- +- *state = REMOTE_NO_KEY_PRESSED; +- switch (key[0]) { +- case DIBUSB_RC_HAUPPAUGE_KEY_PRESSED: +- raw = ((key[1] << 8) | key[2]) >> 3; +- toggle = !!(raw & 0x800); +- data = raw & 0x3f; +- custom = (raw >> 6) & 0x1f; +- +- deb_rc("raw key code 0x%02x, 0x%02x, 0x%02x to c: %02x d: %02x toggle: %d\n",key[1],key[2],key[3],custom,data,toggle); +- +- for (i = 0; i < ARRAY_SIZE(rc_map_haupp_table); i++) { +- if (rc5_data(&rc_map_haupp_table[i]) == data && +- rc5_custom(&rc_map_haupp_table[i]) == custom) { +- +- deb_rc("c: %x, d: %x\n", rc5_data(&rc_map_haupp_table[i]), +- rc5_custom(&rc_map_haupp_table[i])); +- +- *event = rc_map_haupp_table[i].keycode; +- *state = REMOTE_KEY_PRESSED; +- if (st->old_toggle == toggle) { +- if (st->last_repeat_count++ < 2) +- *state = REMOTE_NO_KEY_PRESSED; +- } else { +- st->last_repeat_count = 0; +- st->old_toggle = toggle; +- } +- break; +- } +- } +- +- break; +- case DIBUSB_RC_HAUPPAUGE_KEY_EMPTY: +- default: +- break; +- } +- +- return 0; +-} +- +-static int nova_t_read_mac_address (struct dvb_usb_device *d, u8 mac[6]) +-{ +- int i; +- u8 b; +- +- mac[0] = 0x00; +- mac[1] = 0x0d; +- mac[2] = 0xfe; +- +- /* this is a complete guess, but works for my box */ +- for (i = 136; i < 139; i++) { +- dibusb_read_eeprom_byte(d,i, &b); +- +- mac[5 - (i - 136)] = b; +- } +- +- return 0; +-} +- +-/* USB Driver stuff */ +-static struct dvb_usb_device_properties nova_t_properties; +- +-static int nova_t_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &nova_t_properties, +- THIS_MODULE, NULL, adapter_nr); +-} +- +-/* do not change the order of the ID table */ +-static struct usb_device_id nova_t_table [] = { +-/* 00 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_COLD) }, +-/* 01 */ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_WINTV_NOVA_T_USB2_WARM) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE(usb, nova_t_table); +- +-static struct dvb_usb_device_properties nova_t_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-nova-t-usb2-02.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter_count = 32, +- +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .pid_filter = dibusb_pid_filter, +- .pid_filter_ctrl = dibusb_pid_filter_ctrl, +- .frontend_attach = dibusb_dib3000mc_frontend_attach, +- .tuner_attach = dibusb_dib3000mc_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- .size_of_priv = sizeof(struct dibusb_device_state), +- +- .power_ctrl = dibusb2_0_power_ctrl, +- .read_mac_address = nova_t_read_mac_address, +- +- .rc.legacy = { +- .rc_interval = 100, +- .rc_map_table = rc_map_haupp_table, +- .rc_map_size = ARRAY_SIZE(rc_map_haupp_table), +- .rc_query = nova_t_rc_query, +- }, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Hauppauge WinTV-NOVA-T usb2", +- { &nova_t_table[0], NULL }, +- { &nova_t_table[1], NULL }, +- }, +- { NULL }, +- } +-}; +- +-static struct usb_driver nova_t_driver = { +- .name = "dvb_usb_nova_t_usb2", +- .probe = nova_t_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = nova_t_table, +-}; +- +-module_usb_driver(nova_t_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Hauppauge WinTV-NOVA-T usb2"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c +deleted file mode 100644 +index c8a9504..0000000 +--- a/drivers/media/dvb/dvb-usb/opera1.c ++++ /dev/null +@@ -1,583 +0,0 @@ +-/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card +-* +-* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) +-* Copyright (C) 2006 Marco Gittler (g.marco@freenet.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, version 2. +-* +-* see Documentation/dvb/README.dvb-usb for more information +-*/ +- +-#define DVB_USB_LOG_PREFIX "opera" +- +-#include "dvb-usb.h" +-#include "stv0299.h" +- +-#define OPERA_READ_MSG 0 +-#define OPERA_WRITE_MSG 1 +-#define OPERA_I2C_TUNER 0xd1 +- +-#define READ_FX2_REG_REQ 0xba +-#define READ_MAC_ADDR 0x08 +-#define OPERA_WRITE_FX2 0xbb +-#define OPERA_TUNER_REQ 0xb1 +-#define REG_1F_SYMBOLRATE_BYTE0 0x1f +-#define REG_20_SYMBOLRATE_BYTE1 0x20 +-#define REG_21_SYMBOLRATE_BYTE2 0x21 +- +-#define ADDR_B600_VOLTAGE_13V (0x02) +-#define ADDR_B601_VOLTAGE_18V (0x03) +-#define ADDR_B1A6_STREAM_CTRL (0x04) +-#define ADDR_B880_READ_REMOTE (0x05) +- +-struct opera1_state { +- u32 last_key_pressed; +-}; +-struct rc_map_opera_table { +- u32 keycode; +- u32 event; +-}; +- +-static int dvb_usb_opera1_debug; +-module_param_named(debug, dvb_usb_opera1_debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." +- DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +- +-static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, +- u8 * data, u16 len, int flags) +-{ +- int ret; +- u8 tmp; +- u8 *buf; +- unsigned int pipe = (flags == OPERA_READ_MSG) ? +- usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); +- u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; +- +- buf = kmalloc(len, GFP_KERNEL); +- if (!buf) +- return -ENOMEM; +- +- if (flags == OPERA_WRITE_MSG) +- memcpy(buf, data, len); +- ret = usb_control_msg(dev, pipe, request, +- request_type | USB_TYPE_VENDOR, value, 0x0, +- buf, len, 2000); +- +- if (request == OPERA_TUNER_REQ) { +- tmp = buf[0]; +- if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), +- OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, +- 0x01, 0x0, buf, 1, 2000) < 1 || buf[0] != 0x08) { +- ret = 0; +- goto out; +- } +- buf[0] = tmp; +- } +- if (flags == OPERA_READ_MSG) +- memcpy(data, buf, len); +-out: +- kfree(buf); +- return ret; +-} +- +-/* I2C */ +- +-static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, +- u8 * buf, u16 len) +-{ +- int ret = 0; +- u8 request; +- u16 value; +- +- if (!dev) { +- info("no usb_device"); +- return -EINVAL; +- } +- if (mutex_lock_interruptible(&dev->usb_mutex) < 0) +- return -EAGAIN; +- +- switch (addr>>1){ +- case ADDR_B600_VOLTAGE_13V: +- request=0xb6; +- value=0x00; +- break; +- case ADDR_B601_VOLTAGE_18V: +- request=0xb6; +- value=0x01; +- break; +- case ADDR_B1A6_STREAM_CTRL: +- request=0xb1; +- value=0xa6; +- break; +- case ADDR_B880_READ_REMOTE: +- request=0xb8; +- value=0x80; +- break; +- default: +- request=0xb1; +- value=addr; +- } +- ret = opera1_xilinx_rw(dev->udev, request, +- value, buf, len, +- addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); +- +- mutex_unlock(&dev->usb_mutex); +- return ret; +-} +- +-static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- int i = 0, tmp = 0; +- +- if (!d) +- return -ENODEV; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- if ((tmp = opera1_usb_i2c_msgxfer(d, +- (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), +- msg[i].buf, +- msg[i].len +- )) != msg[i].len) { +- break; +- } +- if (dvb_usb_opera1_debug & 0x10) +- info("sending i2c mesage %d %d", tmp, msg[i].len); +- } +- mutex_unlock(&d->i2c_mutex); +- return num; +-} +- +-static u32 opera1_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm opera1_i2c_algo = { +- .master_xfer = opera1_i2c_xfer, +- .functionality = opera1_i2c_func, +-}; +- +-static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- static u8 command_13v[1]={0x00}; +- static u8 command_18v[1]={0x01}; +- struct i2c_msg msg[] = { +- {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, +- }; +- struct dvb_usb_adapter *udev_adap = +- (struct dvb_usb_adapter *)(fe->dvb->priv); +- if (voltage == SEC_VOLTAGE_18) { +- msg[0].addr = ADDR_B601_VOLTAGE_18V; +- msg[0].buf = command_18v; +- } +- i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); +- return 0; +-} +- +-static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, +- u32 ratio) +-{ +- stv0299_writereg(fe, 0x13, 0x98); +- stv0299_writereg(fe, 0x14, 0x95); +- stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff); +- stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff); +- stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0); +- return 0; +- +-} +-static u8 opera1_inittab[] = { +- 0x00, 0xa1, +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, +- 0x05, 0x05, +- 0x06, 0x02, +- 0x07, 0x00, +- 0x0b, 0x00, +- 0x0c, 0x01, +- 0x0d, 0x81, +- 0x0e, 0x44, +- 0x0f, 0x19, +- 0x10, 0x3f, +- 0x11, 0x84, +- 0x12, 0xda, +- 0x13, 0x98, +- 0x14, 0x95, +- 0x15, 0xc9, +- 0x16, 0xeb, +- 0x17, 0x00, +- 0x18, 0x19, +- 0x19, 0x8b, +- 0x1a, 0x00, +- 0x1b, 0x82, +- 0x1c, 0x7f, +- 0x1d, 0x00, +- 0x1e, 0x00, +- REG_1F_SYMBOLRATE_BYTE0, 0x06, +- REG_20_SYMBOLRATE_BYTE1, 0x50, +- REG_21_SYMBOLRATE_BYTE2, 0x10, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x24, 0x37, +- 0x25, 0xbc, +- 0x26, 0x00, +- 0x27, 0x00, +- 0x28, 0x00, +- 0x29, 0x1e, +- 0x2a, 0x14, +- 0x2b, 0x1f, +- 0x2c, 0x09, +- 0x2d, 0x0a, +- 0x2e, 0x00, +- 0x2f, 0x00, +- 0x30, 0x00, +- 0x31, 0x1f, +- 0x32, 0x19, +- 0x33, 0xfc, +- 0x34, 0x13, +- 0xff, 0xff, +-}; +- +-static struct stv0299_config opera1_stv0299_config = { +- .demod_address = 0xd0>>1, +- .min_delay_ms = 100, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_0, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .inittab = opera1_inittab, +- .set_symbol_rate = opera1_stv0299_set_symbol_rate, +-}; +- +-static int opera1_frontend_attach(struct dvb_usb_adapter *d) +-{ +- d->fe_adap[0].fe = dvb_attach(stv0299_attach, &opera1_stv0299_config, +- &d->dev->i2c_adap); +- if ((d->fe_adap[0].fe) != NULL) { +- d->fe_adap[0].fe->ops.set_voltage = opera1_set_voltage; +- return 0; +- } +- info("not attached stv0299"); +- return -EIO; +-} +- +-static int opera1_tuner_attach(struct dvb_usb_adapter *adap) +-{ +- dvb_attach( +- dvb_pll_attach, adap->fe_adap[0].fe, 0xc0>>1, +- &adap->dev->i2c_adap, DVB_PLL_OPERA1 +- ); +- return 0; +-} +- +-static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 val = onoff ? 0x01 : 0x00; +- +- if (dvb_usb_opera1_debug) +- info("power %s", onoff ? "on" : "off"); +- return opera1_xilinx_rw(d->udev, 0xb7, val, +- &val, 1, OPERA_WRITE_MSG); +-} +- +-static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- static u8 buf_start[2] = { 0xff, 0x03 }; +- static u8 buf_stop[2] = { 0xff, 0x00 }; +- struct i2c_msg start_tuner[] = { +- {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, +- }; +- if (dvb_usb_opera1_debug) +- info("streaming %s", onoff ? "on" : "off"); +- i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1); +- return 0; +-} +- +-static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, +- int onoff) +-{ +- u8 b_pid[3]; +- struct i2c_msg msg[] = { +- {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, +- }; +- if (dvb_usb_opera1_debug) +- info("pidfilter index: %d pid: %d %s", index, pid, +- onoff ? "on" : "off"); +- b_pid[0] = (2 * index) + 4; +- b_pid[1] = onoff ? (pid & 0xff) : (0x00); +- b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); +- i2c_transfer(&adap->dev->i2c_adap, msg, 1); +- return 0; +-} +- +-static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +-{ +- int u = 0x04; +- u8 b_pid[3]; +- struct i2c_msg msg[] = { +- {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, +- }; +- if (dvb_usb_opera1_debug) +- info("%s hw-pidfilter", onoff ? "enable" : "disable"); +- for (; u < 0x7e; u += 2) { +- b_pid[0] = u; +- b_pid[1] = 0; +- b_pid[2] = 0x80; +- i2c_transfer(&adap->dev->i2c_adap, msg, 1); +- } +- return 0; +-} +- +-static struct rc_map_table rc_map_opera1_table[] = { +- {0x5fa0, KEY_1}, +- {0x51af, KEY_2}, +- {0x5da2, KEY_3}, +- {0x41be, KEY_4}, +- {0x0bf5, KEY_5}, +- {0x43bd, KEY_6}, +- {0x47b8, KEY_7}, +- {0x49b6, KEY_8}, +- {0x05fa, KEY_9}, +- {0x45ba, KEY_0}, +- {0x09f6, KEY_CHANNELUP}, /*chanup */ +- {0x1be5, KEY_CHANNELDOWN}, /*chandown */ +- {0x5da3, KEY_VOLUMEDOWN}, /*voldown */ +- {0x5fa1, KEY_VOLUMEUP}, /*volup */ +- {0x07f8, KEY_SPACE}, /*tab */ +- {0x1fe1, KEY_OK}, /*play ok */ +- {0x1be4, KEY_ZOOM}, /*zoom */ +- {0x59a6, KEY_MUTE}, /*mute */ +- {0x5ba5, KEY_RADIO}, /*tv/f */ +- {0x19e7, KEY_RECORD}, /*rec */ +- {0x01fe, KEY_STOP}, /*Stop */ +- {0x03fd, KEY_PAUSE}, /*pause */ +- {0x03fc, KEY_SCREEN}, /*<- -> */ +- {0x07f9, KEY_CAMERA}, /*capture */ +- {0x47b9, KEY_ESC}, /*exit */ +- {0x43bc, KEY_POWER2}, /*power */ +-}; +- +-static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) +-{ +- struct opera1_state *opst = dev->priv; +- u8 rcbuffer[32]; +- const u16 startmarker1 = 0x10ed; +- const u16 startmarker2 = 0x11ec; +- struct i2c_msg read_remote[] = { +- {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, +- }; +- int i = 0; +- u32 send_key = 0; +- +- if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) { +- for (i = 0; i < 32; i++) { +- if (rcbuffer[i]) +- send_key |= 1; +- if (i < 31) +- send_key = send_key << 1; +- } +- if (send_key & 0x8000) +- send_key = (send_key << 1) | (send_key >> 15 & 0x01); +- +- if (send_key == 0xffff && opst->last_key_pressed != 0) { +- *state = REMOTE_KEY_REPEAT; +- *event = opst->last_key_pressed; +- return 0; +- } +- for (; send_key != 0;) { +- if (send_key >> 16 == startmarker2) { +- break; +- } else if (send_key >> 16 == startmarker1) { +- send_key = +- (send_key & 0xfffeffff) | (startmarker1 << 16); +- break; +- } else +- send_key >>= 1; +- } +- +- if (send_key == 0) +- return 0; +- +- send_key = (send_key & 0xffff) | 0x0100; +- +- for (i = 0; i < ARRAY_SIZE(rc_map_opera1_table); i++) { +- if (rc5_scan(&rc_map_opera1_table[i]) == (send_key & 0xffff)) { +- *state = REMOTE_KEY_PRESSED; +- *event = rc_map_opera1_table[i].keycode; +- opst->last_key_pressed = +- rc_map_opera1_table[i].keycode; +- break; +- } +- opst->last_key_pressed = 0; +- } +- } else +- *state = REMOTE_NO_KEY_PRESSED; +- return 0; +-} +- +-static struct usb_device_id opera1_table[] = { +- {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)}, +- {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)}, +- {} +-}; +- +-MODULE_DEVICE_TABLE(usb, opera1_table); +- +-static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +-{ +- u8 command[] = { READ_MAC_ADDR }; +- opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); +- opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); +- return 0; +-} +-static int opera1_xilinx_load_firmware(struct usb_device *dev, +- const char *filename) +-{ +- const struct firmware *fw = NULL; +- u8 *b, *p; +- int ret = 0, i,fpgasize=40; +- u8 testval; +- info("start downloading fpga firmware %s",filename); +- +- if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { +- err("did not find the firmware file. (%s) " +- "Please see linux/Documentation/dvb/ for more details on firmware-problems.", +- filename); +- return ret; +- } else { +- p = kmalloc(fw->size, GFP_KERNEL); +- opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG); +- if (p != NULL && testval != 0x67) { +- +- u8 reset = 0, fpga_command = 0; +- memcpy(p, fw->data, fw->size); +- /* clear fpga ? */ +- opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, +- OPERA_WRITE_MSG); +- for (i = 0; i < fw->size;) { +- if ( (fw->size - i) size-i; +- } +- b = (u8 *) p + i; +- if (opera1_xilinx_rw +- (dev, OPERA_WRITE_FX2, 0x0, b , fpgasize, +- OPERA_WRITE_MSG) != fpgasize +- ) { +- err("error while transferring firmware"); +- ret = -EINVAL; +- break; +- } +- i = i + fpgasize; +- } +- /* restart the CPU */ +- if (ret || opera1_xilinx_rw +- (dev, 0xa0, 0xe600, &reset, 1, +- OPERA_WRITE_MSG) != 1) { +- err("could not restart the USB controller CPU."); +- ret = -EINVAL; +- } +- } +- } +- kfree(p); +- release_firmware(fw); +- return ret; +-} +- +-static struct dvb_usb_device_properties opera1_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-opera-01.fw", +- .size_of_priv = sizeof(struct opera1_state), +- +- .power_ctrl = opera1_power_ctrl, +- .i2c_algo = &opera1_i2c_algo, +- +- .rc.legacy = { +- .rc_map_table = rc_map_opera1_table, +- .rc_map_size = ARRAY_SIZE(rc_map_opera1_table), +- .rc_interval = 200, +- .rc_query = opera1_rc_query, +- }, +- .read_mac_address = opera1_read_mac_address, +- .generic_bulk_ctrl_endpoint = 0x00, +- /* parameter for the MPEG2-data transfer */ +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = opera1_frontend_attach, +- .streaming_ctrl = opera1_streaming_ctrl, +- .tuner_attach = opera1_tuner_attach, +- .caps = +- DVB_USB_ADAP_HAS_PID_FILTER | +- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, +- .pid_filter = opera1_pid_filter, +- .pid_filter_ctrl = opera1_pid_filter_control, +- .pid_filter_count = 252, +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x82, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .num_device_descs = 1, +- .devices = { +- {"Opera1 DVB-S USB2.0", +- {&opera1_table[0], NULL}, +- {&opera1_table[1], NULL}, +- }, +- } +-}; +- +-static int opera1_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- +- if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && +- udev->descriptor.idVendor == USB_VID_OPERA1 && +- opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0 +- ) { +- return -EINVAL; +- } +- +- if (0 != dvb_usb_device_init(intf, &opera1_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return -EINVAL; +- return 0; +-} +- +-static struct usb_driver opera1_driver = { +- .name = "opera1", +- .probe = opera1_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = opera1_table, +-}; +- +-module_usb_driver(opera1_driver); +- +-MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org"); +-MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de"); +-MODULE_DESCRIPTION("Driver for Opera1 DVB-S device"); +-MODULE_VERSION("0.1"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/pctv452e.c b/drivers/media/dvb/dvb-usb/pctv452e.c +deleted file mode 100644 +index f526eb0..0000000 +--- a/drivers/media/dvb/dvb-usb/pctv452e.c ++++ /dev/null +@@ -1,1064 +0,0 @@ +-/* +- * PCTV 452e DVB driver +- * +- * Copyright (c) 2006-2008 Dominik Kuhlen +- * +- * TT connect S2-3650-CI Common Interface support, MAC readout +- * Copyright (C) 2008 Michael H. Schimek +- * +- * 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. +- */ +- +-/* dvb usb framework */ +-#define DVB_USB_LOG_PREFIX "pctv452e" +-#include "dvb-usb.h" +- +-/* Demodulator */ +-#include "stb0899_drv.h" +-#include "stb0899_reg.h" +-#include "stb0899_cfg.h" +-/* Tuner */ +-#include "stb6100.h" +-#include "stb6100_cfg.h" +-/* FE Power */ +-#include "lnbp22.h" +- +-#include "dvb_ca_en50221.h" +-#include "ttpci-eeprom.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define ISOC_INTERFACE_ALTERNATIVE 3 +- +-#define SYNC_BYTE_OUT 0xaa +-#define SYNC_BYTE_IN 0x55 +- +-/* guessed: (copied from ttusb-budget) */ +-#define PCTV_CMD_RESET 0x15 +-/* command to poll IR receiver */ +-#define PCTV_CMD_IR 0x1b +-/* command to send I2C */ +-#define PCTV_CMD_I2C 0x31 +- +-#define I2C_ADDR_STB0899 (0xd0 >> 1) +-#define I2C_ADDR_STB6100 (0xc0 >> 1) +-#define I2C_ADDR_LNBP22 (0x10 >> 1) +-#define I2C_ADDR_24C16 (0xa0 >> 1) +-#define I2C_ADDR_24C64 (0xa2 >> 1) +- +- +-/* pctv452e sends us this amount of data for each issued usb-command */ +-#define PCTV_ANSWER_LEN 64 +-/* Wait up to 1000ms for device */ +-#define PCTV_TIMEOUT 1000 +- +- +-#define PCTV_LED_GPIO STB0899_GPIO01 +-#define PCTV_LED_GREEN 0x82 +-#define PCTV_LED_ORANGE 0x02 +- +-#define ci_dbg(format, arg...) \ +-do { \ +- if (0) \ +- printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ +- ": " format "\n" , ## arg); \ +-} while (0) +- +-enum { +- TT3650_CMD_CI_TEST = 0x40, +- TT3650_CMD_CI_RD_CTRL, +- TT3650_CMD_CI_WR_CTRL, +- TT3650_CMD_CI_RD_ATTR, +- TT3650_CMD_CI_WR_ATTR, +- TT3650_CMD_CI_RESET, +- TT3650_CMD_CI_SET_VIDEO_PORT +-}; +- +- +-static struct stb0899_postproc pctv45e_postproc[] = { +- { PCTV_LED_GPIO, STB0899_GPIOPULLUP }, +- { 0, 0 } +-}; +- +-/* +- * stores all private variables for communication with the PCTV452e DVB-S2 +- */ +-struct pctv452e_state { +- struct dvb_ca_en50221 ca; +- struct mutex ca_mutex; +- +- u8 c; /* transaction counter, wraps around... */ +- u8 initialized; /* set to 1 if 0x15 has been sent */ +- u16 last_rc_key; +-}; +- +-static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, +- unsigned int write_len, unsigned int read_len) +-{ +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- u8 buf[64]; +- u8 id; +- unsigned int rlen; +- int ret; +- +- BUG_ON(NULL == data && 0 != (write_len | read_len)); +- BUG_ON(write_len > 64 - 4); +- BUG_ON(read_len > 64 - 4); +- +- id = state->c++; +- +- buf[0] = SYNC_BYTE_OUT; +- buf[1] = id; +- buf[2] = cmd; +- buf[3] = write_len; +- +- memcpy(buf + 4, data, write_len); +- +- rlen = (read_len > 0) ? 64 : 0; +- ret = dvb_usb_generic_rw(d, buf, 4 + write_len, +- buf, rlen, /* delay_ms */ 0); +- if (0 != ret) +- goto failed; +- +- ret = -EIO; +- if (SYNC_BYTE_IN != buf[0] || id != buf[1]) +- goto failed; +- +- memcpy(data, buf + 4, read_len); +- +- return 0; +- +-failed: +- err("CI error %d; %02X %02X %02X -> %02X %02X %02X.", +- ret, SYNC_BYTE_OUT, id, cmd, buf[0], buf[1], buf[2]); +- +- return ret; +-} +- +-static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, +- u8 cmd, u8 *data, unsigned int write_len, +- unsigned int read_len) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- int ret; +- +- mutex_lock(&state->ca_mutex); +- ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); +- mutex_unlock(&state->ca_mutex); +- +- return ret; +-} +- +-static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, +- int slot, int address) +-{ +- u8 buf[3]; +- int ret; +- +- if (0 != slot) +- return -EINVAL; +- +- buf[0] = (address >> 8) & 0x0F; +- buf[1] = address; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); +- +- ci_dbg("%s %04x -> %d 0x%02x", +- __func__, address, ret, buf[2]); +- +- if (ret < 0) +- return ret; +- +- return buf[2]; +-} +- +-static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, +- int slot, int address, u8 value) +-{ +- u8 buf[3]; +- +- ci_dbg("%s %d 0x%04x 0x%02x", +- __func__, slot, address, value); +- +- if (0 != slot) +- return -EINVAL; +- +- buf[0] = (address >> 8) & 0x0F; +- buf[1] = address; +- buf[2] = value; +- +- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +-} +- +-static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, +- int slot, +- u8 address) +-{ +- u8 buf[2]; +- int ret; +- +- if (0 != slot) +- return -EINVAL; +- +- buf[0] = address & 3; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); +- +- ci_dbg("%s 0x%02x -> %d 0x%02x", +- __func__, address, ret, buf[1]); +- +- if (ret < 0) +- return ret; +- +- return buf[1]; +-} +- +-static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, +- int slot, +- u8 address, +- u8 value) +-{ +- u8 buf[2]; +- +- ci_dbg("%s %d 0x%02x 0x%02x", +- __func__, slot, address, value); +- +- if (0 != slot) +- return -EINVAL; +- +- buf[0] = address; +- buf[1] = value; +- +- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +-} +- +-static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, +- int slot, +- int enable) +-{ +- u8 buf[1]; +- int ret; +- +- ci_dbg("%s %d %d", __func__, slot, enable); +- +- if (0 != slot) +- return -EINVAL; +- +- enable = !!enable; +- buf[0] = enable; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); +- if (ret < 0) +- return ret; +- +- if (enable != buf[0]) { +- err("CI not %sabled.", enable ? "en" : "dis"); +- return -EIO; +- } +- +- return 0; +-} +- +-static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +-{ +- return tt3650_ci_set_video_port(ca, slot, /* enable */ 0); +-} +- +-static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +-{ +- return tt3650_ci_set_video_port(ca, slot, /* enable */ 1); +-} +- +-static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- u8 buf[1]; +- int ret; +- +- ci_dbg("%s %d", __func__, slot); +- +- if (0 != slot) +- return -EINVAL; +- +- buf[0] = 0; +- +- mutex_lock(&state->ca_mutex); +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); +- if (0 != ret) +- goto failed; +- +- msleep(500); +- +- buf[0] = 1; +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); +- if (0 != ret) +- goto failed; +- +- msleep(500); +- +- buf[0] = 0; /* FTA */ +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); +- +- failed: +- mutex_unlock(&state->ca_mutex); +- +- return ret; +-} +- +-static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, +- int slot, +- int open) +-{ +- u8 buf[1]; +- int ret; +- +- if (0 != slot) +- return -EINVAL; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); +- if (0 != ret) +- return ret; +- +- if (1 == buf[0]) +- return DVB_CA_EN50221_POLL_CAM_PRESENT | +- DVB_CA_EN50221_POLL_CAM_READY; +- +- return 0; +- +-} +- +-static void tt3650_ci_uninit(struct dvb_usb_device *d) +-{ +- struct pctv452e_state *state; +- +- ci_dbg("%s", __func__); +- +- if (NULL == d) +- return; +- +- state = (struct pctv452e_state *)d->priv; +- if (NULL == state) +- return; +- +- if (NULL == state->ca.data) +- return; +- +- /* Error ignored. */ +- tt3650_ci_set_video_port(&state->ca, /* slot */ 0, /* enable */ 0); +- +- dvb_ca_en50221_release(&state->ca); +- +- memset(&state->ca, 0, sizeof(state->ca)); +-} +- +-static int tt3650_ci_init(struct dvb_usb_adapter *a) +-{ +- struct dvb_usb_device *d = a->dev; +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- int ret; +- +- ci_dbg("%s", __func__); +- +- mutex_init(&state->ca_mutex); +- +- state->ca.owner = THIS_MODULE; +- state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; +- state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; +- state->ca.read_cam_control = tt3650_ci_read_cam_control; +- state->ca.write_cam_control = tt3650_ci_write_cam_control; +- state->ca.slot_reset = tt3650_ci_slot_reset; +- state->ca.slot_shutdown = tt3650_ci_slot_shutdown; +- state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; +- state->ca.poll_slot_status = tt3650_ci_poll_slot_status; +- state->ca.data = d; +- +- ret = dvb_ca_en50221_init(&a->dvb_adap, +- &state->ca, +- /* flags */ 0, +- /* n_slots */ 1); +- if (0 != ret) { +- err("Cannot initialize CI: Error %d.", ret); +- memset(&state->ca, 0, sizeof(state->ca)); +- return ret; +- } +- +- info("CI initialized."); +- +- return 0; +-} +- +-#define CMD_BUFFER_SIZE 0x28 +-static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr, +- const u8 *snd_buf, u8 snd_len, +- u8 *rcv_buf, u8 rcv_len) +-{ +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- u8 buf[64]; +- u8 id; +- int ret; +- +- id = state->c++; +- +- ret = -EINVAL; +- if (snd_len > 64 - 7 || rcv_len > 64 - 7) +- goto failed; +- +- buf[0] = SYNC_BYTE_OUT; +- buf[1] = id; +- buf[2] = PCTV_CMD_I2C; +- buf[3] = snd_len + 3; +- buf[4] = addr << 1; +- buf[5] = snd_len; +- buf[6] = rcv_len; +- +- memcpy(buf + 7, snd_buf, snd_len); +- +- ret = dvb_usb_generic_rw(d, buf, 7 + snd_len, +- buf, /* rcv_len */ 64, +- /* delay_ms */ 0); +- if (ret < 0) +- goto failed; +- +- /* TT USB protocol error. */ +- ret = -EIO; +- if (SYNC_BYTE_IN != buf[0] || id != buf[1]) +- goto failed; +- +- /* I2C device didn't respond as expected. */ +- ret = -EREMOTEIO; +- if (buf[5] < snd_len || buf[6] < rcv_len) +- goto failed; +- +- memcpy(rcv_buf, buf + 7, rcv_len); +- +- return rcv_len; +- +-failed: +- err("I2C error %d; %02X %02X %02X %02X %02X -> " +- "%02X %02X %02X %02X %02X.", +- ret, SYNC_BYTE_OUT, id, addr << 1, snd_len, rcv_len, +- buf[0], buf[1], buf[4], buf[5], buf[6]); +- +- return ret; +-} +- +-static int pctv452e_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msg, +- int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adapter); +- int i; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; +- int ret; +- +- if (msg[i].flags & I2C_M_RD) { +- addr = msg[i].addr; +- snd_buf = NULL; +- snd_len = 0; +- rcv_buf = msg[i].buf; +- rcv_len = msg[i].len; +- } else { +- addr = msg[i].addr; +- snd_buf = msg[i].buf; +- snd_len = msg[i].len; +- rcv_buf = NULL; +- rcv_len = 0; +- } +- +- ret = pctv452e_i2c_msg(d, addr, snd_buf, snd_len, rcv_buf, +- rcv_len); +- if (ret < rcv_len) +- break; +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 pctv452e_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static int pctv452e_power_ctrl(struct dvb_usb_device *d, int i) +-{ +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- u8 b0[] = { 0xaa, 0, PCTV_CMD_RESET, 1, 0 }; +- u8 rx[PCTV_ANSWER_LEN]; +- int ret; +- +- info("%s: %d\n", __func__, i); +- +- if (!i) +- return 0; +- +- if (state->initialized) +- return 0; +- +- /* hmm where shoud this should go? */ +- ret = usb_set_interface(d->udev, 0, ISOC_INTERFACE_ALTERNATIVE); +- if (ret != 0) +- info("%s: Warning set interface returned: %d\n", +- __func__, ret); +- +- /* this is a one-time initialization, dont know where to put */ +- b0[1] = state->c++; +- /* reset board */ +- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); +- if (ret) +- return ret; +- +- b0[1] = state->c++; +- b0[4] = 1; +- /* reset board (again?) */ +- ret = dvb_usb_generic_rw(d, b0, sizeof(b0), rx, PCTV_ANSWER_LEN, 0); +- if (ret) +- return ret; +- +- state->initialized = 1; +- +- return 0; +-} +- +-static int pctv452e_rc_query(struct dvb_usb_device *d) +-{ +- struct pctv452e_state *state = (struct pctv452e_state *)d->priv; +- u8 b[CMD_BUFFER_SIZE]; +- u8 rx[PCTV_ANSWER_LEN]; +- int ret, i; +- u8 id = state->c++; +- +- /* prepare command header */ +- b[0] = SYNC_BYTE_OUT; +- b[1] = id; +- b[2] = PCTV_CMD_IR; +- b[3] = 0; +- +- /* send ir request */ +- ret = dvb_usb_generic_rw(d, b, 4, rx, PCTV_ANSWER_LEN, 0); +- if (ret != 0) +- return ret; +- +- if (debug > 3) { +- info("%s: read: %2d: %02x %02x %02x: ", __func__, +- ret, rx[0], rx[1], rx[2]); +- for (i = 0; (i < rx[3]) && ((i+3) < PCTV_ANSWER_LEN); i++) +- info(" %02x", rx[i+3]); +- +- info("\n"); +- } +- +- if ((rx[3] == 9) && (rx[12] & 0x01)) { +- /* got a "press" event */ +- state->last_rc_key = (rx[7] << 8) | rx[6]; +- if (debug > 2) +- info("%s: cmd=0x%02x sys=0x%02x\n", +- __func__, rx[6], rx[7]); +- +- rc_keydown(d->rc_dev, state->last_rc_key, 0); +- } else if (state->last_rc_key) { +- rc_keyup(d->rc_dev); +- state->last_rc_key = 0; +- } +- +- return 0; +-} +- +-static int pctv452e_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +-{ +- const u8 mem_addr[] = { 0x1f, 0xcc }; +- u8 encoded_mac[20]; +- int ret; +- +- ret = -EAGAIN; +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- goto failed; +- +- ret = pctv452e_i2c_msg(d, I2C_ADDR_24C16, +- mem_addr + 1, /* snd_len */ 1, +- encoded_mac, /* rcv_len */ 20); +- if (-EREMOTEIO == ret) +- /* Caution! A 24C16 interprets 0xA2 0x1F 0xCC as a +- byte write if /WC is low. */ +- ret = pctv452e_i2c_msg(d, I2C_ADDR_24C64, +- mem_addr, 2, +- encoded_mac, 20); +- +- mutex_unlock(&d->i2c_mutex); +- +- if (20 != ret) +- goto failed; +- +- ret = ttpci_eeprom_decode_mac(mac, encoded_mac); +- if (0 != ret) +- goto failed; +- +- return 0; +- +-failed: +- memset(mac, 0, 6); +- +- return ret; +-} +- +-static const struct stb0899_s1_reg pctv452e_init_dev[] = { +- { STB0899_DISCNTRL1, 0x26 }, +- { STB0899_DISCNTRL2, 0x80 }, +- { STB0899_DISRX_ST0, 0x04 }, +- { STB0899_DISRX_ST1, 0x20 }, +- { STB0899_DISPARITY, 0x00 }, +- { STB0899_DISFIFO, 0x00 }, +- { STB0899_DISF22, 0x99 }, +- { STB0899_DISF22RX, 0x85 }, /* 0xa8 */ +- { STB0899_ACRPRESC, 0x11 }, +- { STB0899_ACRDIV1, 0x0a }, +- { STB0899_ACRDIV2, 0x05 }, +- { STB0899_DACR1 , 0x00 }, +- { STB0899_DACR2 , 0x00 }, +- { STB0899_OUTCFG, 0x00 }, +- { STB0899_MODECFG, 0x00 }, /* Inversion */ +- { STB0899_IRQMSK_3, 0xf3 }, +- { STB0899_IRQMSK_2, 0xfc }, +- { STB0899_IRQMSK_1, 0xff }, +- { STB0899_IRQMSK_0, 0xff }, +- { STB0899_I2CCFG, 0x88 }, +- { STB0899_I2CRPT, 0x58 }, +- { STB0899_GPIO00CFG, 0x82 }, +- { STB0899_GPIO01CFG, 0x82 }, /* LED: 0x02 green, 0x82 orange */ +- { STB0899_GPIO02CFG, 0x82 }, +- { STB0899_GPIO03CFG, 0x82 }, +- { STB0899_GPIO04CFG, 0x82 }, +- { STB0899_GPIO05CFG, 0x82 }, +- { STB0899_GPIO06CFG, 0x82 }, +- { STB0899_GPIO07CFG, 0x82 }, +- { STB0899_GPIO08CFG, 0x82 }, +- { STB0899_GPIO09CFG, 0x82 }, +- { STB0899_GPIO10CFG, 0x82 }, +- { STB0899_GPIO11CFG, 0x82 }, +- { STB0899_GPIO12CFG, 0x82 }, +- { STB0899_GPIO13CFG, 0x82 }, +- { STB0899_GPIO14CFG, 0x82 }, +- { STB0899_GPIO15CFG, 0x82 }, +- { STB0899_GPIO16CFG, 0x82 }, +- { STB0899_GPIO17CFG, 0x82 }, +- { STB0899_GPIO18CFG, 0x82 }, +- { STB0899_GPIO19CFG, 0x82 }, +- { STB0899_GPIO20CFG, 0x82 }, +- { STB0899_SDATCFG, 0xb8 }, +- { STB0899_SCLTCFG, 0xba }, +- { STB0899_AGCRFCFG, 0x1c }, /* 0x11 DVB-S; 0x1c DVB-S2 (1c, rjkm) */ +- { STB0899_GPIO22, 0x82 }, +- { STB0899_GPIO21, 0x91 }, +- { STB0899_DIRCLKCFG, 0x82 }, +- { STB0899_CLKOUT27CFG, 0x7e }, +- { STB0899_STDBYCFG, 0x82 }, +- { STB0899_CS0CFG, 0x82 }, +- { STB0899_CS1CFG, 0x82 }, +- { STB0899_DISEQCOCFG, 0x20 }, +- { STB0899_NCOARSE, 0x15 }, /* 0x15 27Mhz, F/3 198MHz, F/6 108MHz */ +- { STB0899_SYNTCTRL, 0x00 }, /* 0x00 CLKI, 0x02 XTALI */ +- { STB0899_FILTCTRL, 0x00 }, +- { STB0899_SYSCTRL, 0x00 }, +- { STB0899_STOPCLK1, 0x20 }, /* orig: 0x00 budget-ci: 0x20 */ +- { STB0899_STOPCLK2, 0x00 }, +- { STB0899_INTBUFCTRL, 0x0a }, +- { STB0899_AGC2I1, 0x00 }, +- { STB0899_AGC2I2, 0x00 }, +- { STB0899_AGCIQIN, 0x00 }, +- { STB0899_TSTRES, 0x40 }, /* rjkm */ +- { 0xffff, 0xff }, +-}; +- +-static const struct stb0899_s1_reg pctv452e_init_s1_demod[] = { +- { STB0899_DEMOD, 0x00 }, +- { STB0899_RCOMPC, 0xc9 }, +- { STB0899_AGC1CN, 0x01 }, +- { STB0899_AGC1REF, 0x10 }, +- { STB0899_RTC, 0x23 }, +- { STB0899_TMGCFG, 0x4e }, +- { STB0899_AGC2REF, 0x34 }, +- { STB0899_TLSR, 0x84 }, +- { STB0899_CFD, 0xf7 }, +- { STB0899_ACLC, 0x87 }, +- { STB0899_BCLC, 0x94 }, +- { STB0899_EQON, 0x41 }, +- { STB0899_LDT, 0xf1 }, +- { STB0899_LDT2, 0xe3 }, +- { STB0899_EQUALREF, 0xb4 }, +- { STB0899_TMGRAMP, 0x10 }, +- { STB0899_TMGTHD, 0x30 }, +- { STB0899_IDCCOMP, 0xfd }, +- { STB0899_QDCCOMP, 0xff }, +- { STB0899_POWERI, 0x0c }, +- { STB0899_POWERQ, 0x0f }, +- { STB0899_RCOMP, 0x6c }, +- { STB0899_AGCIQIN, 0x80 }, +- { STB0899_AGC2I1, 0x06 }, +- { STB0899_AGC2I2, 0x00 }, +- { STB0899_TLIR, 0x30 }, +- { STB0899_RTF, 0x7f }, +- { STB0899_DSTATUS, 0x00 }, +- { STB0899_LDI, 0xbc }, +- { STB0899_CFRM, 0xea }, +- { STB0899_CFRL, 0x31 }, +- { STB0899_NIRM, 0x2b }, +- { STB0899_NIRL, 0x80 }, +- { STB0899_ISYMB, 0x1d }, +- { STB0899_QSYMB, 0xa6 }, +- { STB0899_SFRH, 0x2f }, +- { STB0899_SFRM, 0x68 }, +- { STB0899_SFRL, 0x40 }, +- { STB0899_SFRUPH, 0x2f }, +- { STB0899_SFRUPM, 0x68 }, +- { STB0899_SFRUPL, 0x40 }, +- { STB0899_EQUAI1, 0x02 }, +- { STB0899_EQUAQ1, 0xff }, +- { STB0899_EQUAI2, 0x04 }, +- { STB0899_EQUAQ2, 0x05 }, +- { STB0899_EQUAI3, 0x02 }, +- { STB0899_EQUAQ3, 0xfd }, +- { STB0899_EQUAI4, 0x03 }, +- { STB0899_EQUAQ4, 0x07 }, +- { STB0899_EQUAI5, 0x08 }, +- { STB0899_EQUAQ5, 0xf5 }, +- { STB0899_DSTATUS2, 0x00 }, +- { STB0899_VSTATUS, 0x00 }, +- { STB0899_VERROR, 0x86 }, +- { STB0899_IQSWAP, 0x2a }, +- { STB0899_ECNT1M, 0x00 }, +- { STB0899_ECNT1L, 0x00 }, +- { STB0899_ECNT2M, 0x00 }, +- { STB0899_ECNT2L, 0x00 }, +- { STB0899_ECNT3M, 0x0a }, +- { STB0899_ECNT3L, 0xad }, +- { STB0899_FECAUTO1, 0x06 }, +- { STB0899_FECM, 0x01 }, +- { STB0899_VTH12, 0xb0 }, +- { STB0899_VTH23, 0x7a }, +- { STB0899_VTH34, 0x58 }, +- { STB0899_VTH56, 0x38 }, +- { STB0899_VTH67, 0x34 }, +- { STB0899_VTH78, 0x24 }, +- { STB0899_PRVIT, 0xff }, +- { STB0899_VITSYNC, 0x19 }, +- { STB0899_RSULC, 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ +- { STB0899_TSULC, 0x42 }, +- { STB0899_RSLLC, 0x41 }, +- { STB0899_TSLPL, 0x12 }, +- { STB0899_TSCFGH, 0x0c }, +- { STB0899_TSCFGM, 0x00 }, +- { STB0899_TSCFGL, 0x00 }, +- { STB0899_TSOUT, 0x69 }, /* 0x0d for CAM */ +- { STB0899_RSSYNCDEL, 0x00 }, +- { STB0899_TSINHDELH, 0x02 }, +- { STB0899_TSINHDELM, 0x00 }, +- { STB0899_TSINHDELL, 0x00 }, +- { STB0899_TSLLSTKM, 0x1b }, +- { STB0899_TSLLSTKL, 0xb3 }, +- { STB0899_TSULSTKM, 0x00 }, +- { STB0899_TSULSTKL, 0x00 }, +- { STB0899_PCKLENUL, 0xbc }, +- { STB0899_PCKLENLL, 0xcc }, +- { STB0899_RSPCKLEN, 0xbd }, +- { STB0899_TSSTATUS, 0x90 }, +- { STB0899_ERRCTRL1, 0xb6 }, +- { STB0899_ERRCTRL2, 0x95 }, +- { STB0899_ERRCTRL3, 0x8d }, +- { STB0899_DMONMSK1, 0x27 }, +- { STB0899_DMONMSK0, 0x03 }, +- { STB0899_DEMAPVIT, 0x5c }, +- { STB0899_PLPARM, 0x19 }, +- { STB0899_PDELCTRL, 0x48 }, +- { STB0899_PDELCTRL2, 0x00 }, +- { STB0899_BBHCTRL1, 0x00 }, +- { STB0899_BBHCTRL2, 0x00 }, +- { STB0899_HYSTTHRESH, 0x77 }, +- { STB0899_MATCSTM, 0x00 }, +- { STB0899_MATCSTL, 0x00 }, +- { STB0899_UPLCSTM, 0x00 }, +- { STB0899_UPLCSTL, 0x00 }, +- { STB0899_DFLCSTM, 0x00 }, +- { STB0899_DFLCSTL, 0x00 }, +- { STB0899_SYNCCST, 0x00 }, +- { STB0899_SYNCDCSTM, 0x00 }, +- { STB0899_SYNCDCSTL, 0x00 }, +- { STB0899_ISI_ENTRY, 0x00 }, +- { STB0899_ISI_BIT_EN, 0x00 }, +- { STB0899_MATSTRM, 0xf0 }, +- { STB0899_MATSTRL, 0x02 }, +- { STB0899_UPLSTRM, 0x45 }, +- { STB0899_UPLSTRL, 0x60 }, +- { STB0899_DFLSTRM, 0xe3 }, +- { STB0899_DFLSTRL, 0x00 }, +- { STB0899_SYNCSTR, 0x47 }, +- { STB0899_SYNCDSTRM, 0x05 }, +- { STB0899_SYNCDSTRL, 0x18 }, +- { STB0899_CFGPDELSTATUS1, 0x19 }, +- { STB0899_CFGPDELSTATUS2, 0x2b }, +- { STB0899_BBFERRORM, 0x00 }, +- { STB0899_BBFERRORL, 0x01 }, +- { STB0899_UPKTERRORM, 0x00 }, +- { STB0899_UPKTERRORL, 0x00 }, +- { 0xffff, 0xff }, +-}; +- +-static struct stb0899_config stb0899_config = { +- .init_dev = pctv452e_init_dev, +- .init_s2_demod = stb0899_s2_init_2, +- .init_s1_demod = pctv452e_init_s1_demod, +- .init_s2_fec = stb0899_s2_init_4, +- .init_tst = stb0899_s1_init_5, +- +- .demod_address = I2C_ADDR_STB0899, /* I2C Address */ +- .block_sync_mode = STB0899_SYNC_FORCED, /* ? */ +- +- .xtal_freq = 27000000, /* Assume Hz ? */ +- .inversion = IQ_SWAP_ON, /* ? */ +- +- .lo_clk = 76500000, +- .hi_clk = 99000000, +- +- .ts_output_mode = 0, /* Use parallel mode */ +- .clock_polarity = 0, +- .data_clk_parity = 0, +- .fec_mode = 0, +- +- .esno_ave = STB0899_DVBS2_ESNO_AVE, +- .esno_quant = STB0899_DVBS2_ESNO_QUANT, +- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, +- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, +- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, +- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, +- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, +- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, +- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, +- +- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, +- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, +- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, +- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, +- +- .tuner_get_frequency = stb6100_get_frequency, +- .tuner_set_frequency = stb6100_set_frequency, +- .tuner_set_bandwidth = stb6100_set_bandwidth, +- .tuner_get_bandwidth = stb6100_get_bandwidth, +- .tuner_set_rfsiggain = NULL, +- +- /* helper for switching LED green/orange */ +- .postproc = pctv45e_postproc +-}; +- +-static struct stb6100_config stb6100_config = { +- .tuner_address = I2C_ADDR_STB6100, +- .refclock = 27000000 +-}; +- +- +-static struct i2c_algorithm pctv452e_i2c_algo = { +- .master_xfer = pctv452e_i2c_xfer, +- .functionality = pctv452e_i2c_func +-}; +- +-static int pctv452e_frontend_attach(struct dvb_usb_adapter *a) +-{ +- struct usb_device_id *id; +- +- a->fe_adap[0].fe = dvb_attach(stb0899_attach, &stb0899_config, +- &a->dev->i2c_adap); +- if (!a->fe_adap[0].fe) +- return -ENODEV; +- if ((dvb_attach(lnbp22_attach, a->fe_adap[0].fe, +- &a->dev->i2c_adap)) == 0) +- err("Cannot attach lnbp22\n"); +- +- id = a->dev->desc->warm_ids[0]; +- if (USB_VID_TECHNOTREND == id->idVendor +- && USB_PID_TECHNOTREND_CONNECT_S2_3650_CI == id->idProduct) +- /* Error ignored. */ +- tt3650_ci_init(a); +- +- return 0; +-} +- +-static int pctv452e_tuner_attach(struct dvb_usb_adapter *a) +-{ +- if (!a->fe_adap[0].fe) +- return -ENODEV; +- if (dvb_attach(stb6100_attach, a->fe_adap[0].fe, &stb6100_config, +- &a->dev->i2c_adap) == 0) { +- err("%s failed\n", __func__); +- return -ENODEV; +- } +- +- return 0; +-} +- +-static struct usb_device_id pctv452e_usb_table[] = { +- {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_452E)}, +- {USB_DEVICE(USB_VID_TECHNOTREND, USB_PID_TECHNOTREND_CONNECT_S2_3600)}, +- {USB_DEVICE(USB_VID_TECHNOTREND, +- USB_PID_TECHNOTREND_CONNECT_S2_3650_CI)}, +- {} +-}; +-MODULE_DEVICE_TABLE(usb, pctv452e_usb_table); +- +-static struct dvb_usb_device_properties pctv452e_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = sizeof(struct pctv452e_state), +- +- .power_ctrl = pctv452e_power_ctrl, +- +- .rc.core = { +- .rc_codes = RC_MAP_DIB0700_RC5_TABLE, +- .allowed_protos = RC_TYPE_UNKNOWN, +- .rc_query = pctv452e_rc_query, +- .rc_interval = 100, +- }, +- +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = pctv452e_frontend_attach, +- .tuner_attach = pctv452e_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 4, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1 +- } +- } +- }, +- } }, +- } }, +- +- .i2c_algo = &pctv452e_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 1, /* allow generice rw function */ +- +- .num_device_descs = 1, +- .devices = { +- { .name = "PCTV HDTV USB", +- .cold_ids = { NULL, NULL }, /* this is a warm only device */ +- .warm_ids = { &pctv452e_usb_table[0], NULL } +- }, +- { 0 }, +- } +-}; +- +-static struct dvb_usb_device_properties tt_connect_s2_3600_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, /* more ? */ +- .usb_ctrl = DEVICE_SPECIFIC, +- +- .size_of_priv = sizeof(struct pctv452e_state), +- +- .power_ctrl = pctv452e_power_ctrl, +- .read_mac_address = pctv452e_read_mac_address, +- +- .rc.core = { +- .rc_codes = RC_MAP_TT_1500, +- .allowed_protos = RC_TYPE_UNKNOWN, +- .rc_query = pctv452e_rc_query, +- .rc_interval = 100, +- }, +- +- .num_adapters = 1, +- .adapter = {{ +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = pctv452e_frontend_attach, +- .tuner_attach = pctv452e_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1 +- } +- } +- }, +- +- } }, +- } }, +- +- .i2c_algo = &pctv452e_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 1, /* allow generic rw function*/ +- +- .num_device_descs = 2, +- .devices = { +- { .name = "Technotrend TT Connect S2-3600", +- .cold_ids = { NULL, NULL }, /* this is a warm only device */ +- .warm_ids = { &pctv452e_usb_table[1], NULL } +- }, +- { .name = "Technotrend TT Connect S2-3650-CI", +- .cold_ids = { NULL, NULL }, +- .warm_ids = { &pctv452e_usb_table[2], NULL } +- }, +- { 0 }, +- } +-}; +- +-static void pctv452e_usb_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- +- tt3650_ci_uninit(d); +- dvb_usb_device_exit(intf); +-} +- +-static int pctv452e_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &pctv452e_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &tt_connect_s2_3600_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- +- return -ENODEV; +-} +- +-static struct usb_driver pctv452e_usb_driver = { +- .name = "pctv452e", +- .probe = pctv452e_usb_probe, +- .disconnect = pctv452e_usb_disconnect, +- .id_table = pctv452e_usb_table, +-}; +- +-module_usb_driver(pctv452e_usb_driver); +- +-MODULE_AUTHOR("Dominik Kuhlen "); +-MODULE_AUTHOR("Andre Weidemann "); +-MODULE_AUTHOR("Michael H. Schimek "); +-MODULE_DESCRIPTION("Pinnacle PCTV HDTV USB DVB / TT connect S2-3600 Driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/technisat-usb2.c b/drivers/media/dvb/dvb-usb/technisat-usb2.c +deleted file mode 100644 +index acefaa8..0000000 +--- a/drivers/media/dvb/dvb-usb/technisat-usb2.c ++++ /dev/null +@@ -1,789 +0,0 @@ +-/* +- * Linux driver for Technisat DVB-S/S2 USB 2.0 device +- * +- * Copyright (C) 2010 Patrick Boettcher, +- * Kernel Labs Inc. PO Box 745, St James, NY 11780 +- * +- * Development was sponsored by Technisat Digital UK Limited, whose +- * registered office is Witan Gate House 500 - 600 Witan Gate West, +- * Milton Keynes, MK9 1SH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of the +- * License, or (at your option) any later version. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * THIS PROGRAM IS PROVIDED "AS IS" AND BOTH THE COPYRIGHT HOLDER AND +- * TECHNISAT DIGITAL UK LTD DISCLAIM ALL WARRANTIES WITH REGARD TO +- * THIS PROGRAM INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY OR +- * FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COPYRIGHT HOLDER +- * NOR TECHNISAT DIGITAL UK LIMITED SHALL BE LIABLE FOR ANY SPECIAL, +- * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER +- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +- * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS PROGRAM. See the +- * GNU General Public License for more details. +- */ +- +-#define DVB_USB_LOG_PREFIX "technisat-usb2" +-#include "dvb-usb.h" +- +-#include "stv6110x.h" +-#include "stv090x.h" +- +-/* module parameters */ +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, +- "set debugging level (bit-mask: 1=info,2=eeprom,4=i2c,8=rc)." \ +- DVB_USB_DEBUG_STATUS); +- +-/* disables all LED control command and +- * also does not start the signal polling thread */ +-static int disable_led_control; +-module_param(disable_led_control, int, 0444); +-MODULE_PARM_DESC(disable_led_control, +- "disable LED control of the device " +- "(default: 0 - LED control is active)."); +- +-/* device private data */ +-struct technisat_usb2_state { +- struct dvb_usb_device *dev; +- struct delayed_work green_led_work; +- u8 power_state; +- +- u16 last_scan_code; +-}; +- +-/* debug print helpers */ +-#define deb_info(args...) dprintk(debug, 0x01, args) +-#define deb_eeprom(args...) dprintk(debug, 0x02, args) +-#define deb_i2c(args...) dprintk(debug, 0x04, args) +-#define deb_rc(args...) dprintk(debug, 0x08, args) +- +-/* vendor requests */ +-#define SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST 0xB3 +-#define SET_FRONT_END_RESET_VENDOR_REQUEST 0xB4 +-#define GET_VERSION_INFO_VENDOR_REQUEST 0xB5 +-#define SET_GREEN_LED_VENDOR_REQUEST 0xB6 +-#define SET_RED_LED_VENDOR_REQUEST 0xB7 +-#define GET_IR_DATA_VENDOR_REQUEST 0xB8 +-#define SET_LED_TIMER_DIVIDER_VENDOR_REQUEST 0xB9 +-#define SET_USB_REENUMERATION 0xBA +- +-/* i2c-access methods */ +-#define I2C_SPEED_100KHZ_BIT 0x40 +- +-#define I2C_STATUS_NAK 7 +-#define I2C_STATUS_OK 8 +- +-static int technisat_usb2_i2c_access(struct usb_device *udev, +- u8 device_addr, u8 *tx, u8 txlen, u8 *rx, u8 rxlen) +-{ +- u8 b[64]; +- int ret, actual_length; +- +- deb_i2c("i2c-access: %02x, tx: ", device_addr); +- debug_dump(tx, txlen, deb_i2c); +- deb_i2c(" "); +- +- if (txlen > 62) { +- err("i2c TX buffer can't exceed 62 bytes (dev 0x%02x)", +- device_addr); +- txlen = 62; +- } +- if (rxlen > 62) { +- err("i2c RX buffer can't exceed 62 bytes (dev 0x%02x)", +- device_addr); +- txlen = 62; +- } +- +- b[0] = I2C_SPEED_100KHZ_BIT; +- b[1] = device_addr << 1; +- +- if (rx != NULL) { +- b[0] |= rxlen; +- b[1] |= 1; +- } +- +- memcpy(&b[2], tx, txlen); +- ret = usb_bulk_msg(udev, +- usb_sndbulkpipe(udev, 0x01), +- b, 2 + txlen, +- NULL, 1000); +- +- if (ret < 0) { +- err("i2c-error: out failed %02x = %d", device_addr, ret); +- return -ENODEV; +- } +- +- ret = usb_bulk_msg(udev, +- usb_rcvbulkpipe(udev, 0x01), +- b, 64, &actual_length, 1000); +- if (ret < 0) { +- err("i2c-error: in failed %02x = %d", device_addr, ret); +- return -ENODEV; +- } +- +- if (b[0] != I2C_STATUS_OK) { +- err("i2c-error: %02x = %d", device_addr, b[0]); +- /* handle tuner-i2c-nak */ +- if (!(b[0] == I2C_STATUS_NAK && +- device_addr == 0x60 +- /* && device_is_technisat_usb2 */)) +- return -ENODEV; +- } +- +- deb_i2c("status: %d, ", b[0]); +- +- if (rx != NULL) { +- memcpy(rx, &b[2], rxlen); +- +- deb_i2c("rx (%d): ", rxlen); +- debug_dump(rx, rxlen, deb_i2c); +- } +- +- deb_i2c("\n"); +- +- return 0; +-} +- +-static int technisat_usb2_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, +- int num) +-{ +- int ret = 0, i; +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- +- /* Ensure nobody else hits the i2c bus while we're sending our +- sequence of messages, (such as the remote control thread) */ +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- for (i = 0; i < num; i++) { +- if (i+1 < num && msg[i+1].flags & I2C_M_RD) { +- ret = technisat_usb2_i2c_access(d->udev, msg[i+1].addr, +- msg[i].buf, msg[i].len, +- msg[i+1].buf, msg[i+1].len); +- if (ret != 0) +- break; +- i++; +- } else { +- ret = technisat_usb2_i2c_access(d->udev, msg[i].addr, +- msg[i].buf, msg[i].len, +- NULL, 0); +- if (ret != 0) +- break; +- } +- } +- +- if (ret == 0) +- ret = i; +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-static u32 technisat_usb2_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm technisat_usb2_i2c_algo = { +- .master_xfer = technisat_usb2_i2c_xfer, +- .functionality = technisat_usb2_i2c_func, +-}; +- +-#if 0 +-static void technisat_usb2_frontend_reset(struct usb_device *udev) +-{ +- usb_control_msg(udev, usb_sndctrlpipe(udev, 0), +- SET_FRONT_END_RESET_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- 10, 0, +- NULL, 0, 500); +-} +-#endif +- +-/* LED control */ +-enum technisat_usb2_led_state { +- LED_OFF, +- LED_BLINK, +- LED_ON, +- LED_UNDEFINED +-}; +- +-static int technisat_usb2_set_led(struct dvb_usb_device *d, int red, enum technisat_usb2_led_state state) +-{ +- int ret; +- +- u8 led[8] = { +- red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, +- 0 +- }; +- +- if (disable_led_control && state != LED_OFF) +- return 0; +- +- switch (state) { +- case LED_ON: +- led[1] = 0x82; +- break; +- case LED_BLINK: +- led[1] = 0x82; +- if (red) { +- led[2] = 0x02; +- led[3] = 10; +- led[4] = 10; +- } else { +- led[2] = 0xff; +- led[3] = 50; +- led[4] = 50; +- } +- led[5] = 1; +- break; +- +- default: +- case LED_OFF: +- led[1] = 0x80; +- break; +- } +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- red ? SET_RED_LED_VENDOR_REQUEST : SET_GREEN_LED_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- 0, 0, +- led, sizeof(led), 500); +- +- mutex_unlock(&d->i2c_mutex); +- return ret; +-} +- +-static int technisat_usb2_set_led_timer(struct dvb_usb_device *d, u8 red, u8 green) +-{ +- int ret; +- u8 b = 0; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- SET_LED_TIMER_DIVIDER_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- (red << 8) | green, 0, +- &b, 1, 500); +- +- mutex_unlock(&d->i2c_mutex); +- +- return ret; +-} +- +-static void technisat_usb2_green_led_control(struct work_struct *work) +-{ +- struct technisat_usb2_state *state = +- container_of(work, struct technisat_usb2_state, green_led_work.work); +- struct dvb_frontend *fe = state->dev->adapter[0].fe_adap[0].fe; +- +- if (state->power_state == 0) +- goto schedule; +- +- if (fe != NULL) { +- enum fe_status status; +- +- if (fe->ops.read_status(fe, &status) != 0) +- goto schedule; +- +- if (status & FE_HAS_LOCK) { +- u32 ber; +- +- if (fe->ops.read_ber(fe, &ber) != 0) +- goto schedule; +- +- if (ber > 1000) +- technisat_usb2_set_led(state->dev, 0, LED_BLINK); +- else +- technisat_usb2_set_led(state->dev, 0, LED_ON); +- } else +- technisat_usb2_set_led(state->dev, 0, LED_OFF); +- } +- +-schedule: +- schedule_delayed_work(&state->green_led_work, +- msecs_to_jiffies(500)); +-} +- +-/* method to find out whether the firmware has to be downloaded or not */ +-static int technisat_usb2_identify_state(struct usb_device *udev, +- struct dvb_usb_device_properties *props, +- struct dvb_usb_device_description **desc, int *cold) +-{ +- int ret; +- u8 version[3]; +- +- /* first select the interface */ +- if (usb_set_interface(udev, 0, 1) != 0) +- err("could not set alternate setting to 0"); +- else +- info("set alternate setting"); +- +- *cold = 0; /* by default do not download a firmware - just in case something is wrong */ +- +- ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), +- GET_VERSION_INFO_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_IN, +- 0, 0, +- version, sizeof(version), 500); +- +- if (ret < 0) +- *cold = 1; +- else { +- info("firmware version: %d.%d", version[1], version[2]); +- *cold = 0; +- } +- +- return 0; +-} +- +-/* power control */ +-static int technisat_usb2_power_ctrl(struct dvb_usb_device *d, int level) +-{ +- struct technisat_usb2_state *state = d->priv; +- +- state->power_state = level; +- +- if (disable_led_control) +- return 0; +- +- /* green led is turned off in any case - will be turned on when tuning */ +- technisat_usb2_set_led(d, 0, LED_OFF); +- /* red led is turned on all the time */ +- technisat_usb2_set_led(d, 1, LED_ON); +- return 0; +-} +- +-/* mac address reading - from the eeprom */ +-#if 0 +-static void technisat_usb2_eeprom_dump(struct dvb_usb_device *d) +-{ +- u8 reg; +- u8 b[16]; +- int i, j; +- +- /* full EEPROM dump */ +- for (j = 0; j < 256 * 4; j += 16) { +- reg = j; +- if (technisat_usb2_i2c_access(d->udev, 0x50 + j / 256, ®, 1, b, 16) != 0) +- break; +- +- deb_eeprom("EEPROM: %01x%02x: ", j / 256, reg); +- for (i = 0; i < 16; i++) +- deb_eeprom("%02x ", b[i]); +- deb_eeprom("\n"); +- } +-} +-#endif +- +-static u8 technisat_usb2_calc_lrc(const u8 *b, u16 length) +-{ +- u8 lrc = 0; +- while (--length) +- lrc ^= *b++; +- return lrc; +-} +- +-static int technisat_usb2_eeprom_lrc_read(struct dvb_usb_device *d, +- u16 offset, u8 *b, u16 length, u8 tries) +-{ +- u8 bo = offset & 0xff; +- struct i2c_msg msg[] = { +- { +- .addr = 0x50 | ((offset >> 8) & 0x3), +- .buf = &bo, +- .len = 1 +- }, { +- .addr = 0x50 | ((offset >> 8) & 0x3), +- .flags = I2C_M_RD, +- .buf = b, +- .len = length +- } +- }; +- +- while (tries--) { +- int status; +- +- if (i2c_transfer(&d->i2c_adap, msg, 2) != 2) +- break; +- +- status = +- technisat_usb2_calc_lrc(b, length - 1) == b[length - 1]; +- +- if (status) +- return 0; +- } +- +- return -EREMOTEIO; +-} +- +-#define EEPROM_MAC_START 0x3f8 +-#define EEPROM_MAC_TOTAL 8 +-static int technisat_usb2_read_mac_address(struct dvb_usb_device *d, +- u8 mac[]) +-{ +- u8 buf[EEPROM_MAC_TOTAL]; +- +- if (technisat_usb2_eeprom_lrc_read(d, EEPROM_MAC_START, +- buf, EEPROM_MAC_TOTAL, 4) != 0) +- return -ENODEV; +- +- memcpy(mac, buf, 6); +- return 0; +-} +- +-/* frontend attach */ +-static int technisat_usb2_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- int i; +- u8 gpio[3] = { 0 }; /* 0 = 2, 1 = 3, 2 = 4 */ +- +- gpio[2] = 1; /* high - voltage ? */ +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- gpio[0] = 1; +- break; +- case SEC_VOLTAGE_18: +- gpio[0] = 1; +- gpio[1] = 1; +- break; +- default: +- case SEC_VOLTAGE_OFF: +- break; +- } +- +- for (i = 0; i < 3; i++) +- if (stv090x_set_gpio(fe, i+2, 0, gpio[i], 0) != 0) +- return -EREMOTEIO; +- return 0; +-} +- +-static struct stv090x_config technisat_usb2_stv090x_config = { +- .device = STV0903, +- .demod_mode = STV090x_SINGLE, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 8000000, +- .address = 0x68, +- +- .ts1_mode = STV090x_TSMODE_DVBCI, +- .ts1_clk = 13400000, +- .ts1_tei = 1, +- +- .repeater_level = STV090x_RPTLEVEL_64, +- +- .tuner_bbgain = 6, +-}; +- +-static struct stv6110x_config technisat_usb2_stv6110x_config = { +- .addr = 0x60, +- .refclk = 16000000, +- .clk_div = 2, +-}; +- +-static int technisat_usb2_frontend_attach(struct dvb_usb_adapter *a) +-{ +- struct usb_device *udev = a->dev->udev; +- int ret; +- +- a->fe_adap[0].fe = dvb_attach(stv090x_attach, &technisat_usb2_stv090x_config, +- &a->dev->i2c_adap, STV090x_DEMODULATOR_0); +- +- if (a->fe_adap[0].fe) { +- struct stv6110x_devctl *ctl; +- +- ctl = dvb_attach(stv6110x_attach, +- a->fe_adap[0].fe, +- &technisat_usb2_stv6110x_config, +- &a->dev->i2c_adap); +- +- if (ctl) { +- technisat_usb2_stv090x_config.tuner_init = ctl->tuner_init; +- technisat_usb2_stv090x_config.tuner_sleep = ctl->tuner_sleep; +- technisat_usb2_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; +- technisat_usb2_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; +- technisat_usb2_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; +- technisat_usb2_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; +- technisat_usb2_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; +- technisat_usb2_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; +- technisat_usb2_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; +- technisat_usb2_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; +- technisat_usb2_stv090x_config.tuner_get_status = ctl->tuner_get_status; +- +- /* call the init function once to initialize +- tuner's clock output divider and demod's +- master clock */ +- if (a->fe_adap[0].fe->ops.init) +- a->fe_adap[0].fe->ops.init(a->fe_adap[0].fe); +- +- if (mutex_lock_interruptible(&a->dev->i2c_mutex) < 0) +- return -EAGAIN; +- +- ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), +- SET_IFCLK_TO_EXTERNAL_TSCLK_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- 0, 0, +- NULL, 0, 500); +- mutex_unlock(&a->dev->i2c_mutex); +- +- if (ret != 0) +- err("could not set IF_CLK to external"); +- +- a->fe_adap[0].fe->ops.set_voltage = technisat_usb2_set_voltage; +- +- /* if everything was successful assign a nice name to the frontend */ +- strlcpy(a->fe_adap[0].fe->ops.info.name, a->dev->desc->name, +- sizeof(a->fe_adap[0].fe->ops.info.name)); +- } else { +- dvb_frontend_detach(a->fe_adap[0].fe); +- a->fe_adap[0].fe = NULL; +- } +- } +- +- technisat_usb2_set_led_timer(a->dev, 1, 1); +- +- return a->fe_adap[0].fe == NULL ? -ENODEV : 0; +-} +- +-/* Remote control */ +- +-/* the device is giving providing raw IR-signals to the host mapping +- * it only to one remote control is just the default implementation +- */ +-#define NOMINAL_IR_BIT_TRANSITION_TIME_US 889 +-#define NOMINAL_IR_BIT_TIME_US (2 * NOMINAL_IR_BIT_TRANSITION_TIME_US) +- +-#define FIRMWARE_CLOCK_TICK 83333 +-#define FIRMWARE_CLOCK_DIVISOR 256 +- +-#define IR_PERCENT_TOLERANCE 15 +- +-#define NOMINAL_IR_BIT_TRANSITION_TICKS ((NOMINAL_IR_BIT_TRANSITION_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +-#define NOMINAL_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICKS / FIRMWARE_CLOCK_DIVISOR) +- +-#define NOMINAL_IR_BIT_TIME_TICKS ((NOMINAL_IR_BIT_TIME_US * 1000 * 1000) / FIRMWARE_CLOCK_TICK) +-#define NOMINAL_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICKS / FIRMWARE_CLOCK_DIVISOR) +- +-#define MINIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT - ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +-#define MAXIMUM_IR_BIT_TRANSITION_TICK_COUNT (NOMINAL_IR_BIT_TRANSITION_TICK_COUNT + ((NOMINAL_IR_BIT_TRANSITION_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +- +-#define MINIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT - ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +-#define MAXIMUM_IR_BIT_TIME_TICK_COUNT (NOMINAL_IR_BIT_TIME_TICK_COUNT + ((NOMINAL_IR_BIT_TIME_TICK_COUNT * IR_PERCENT_TOLERANCE) / 100)) +- +-static int technisat_usb2_get_ir(struct dvb_usb_device *d) +-{ +- u8 buf[62], *b; +- int ret; +- struct ir_raw_event ev; +- +- buf[0] = GET_IR_DATA_VENDOR_REQUEST; +- buf[1] = 0x08; +- buf[2] = 0x8f; +- buf[3] = MINIMUM_IR_BIT_TRANSITION_TICK_COUNT; +- buf[4] = MAXIMUM_IR_BIT_TIME_TICK_COUNT; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- ret = usb_control_msg(d->udev, usb_sndctrlpipe(d->udev, 0), +- GET_IR_DATA_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- 0, 0, +- buf, 5, 500); +- if (ret < 0) +- goto unlock; +- +- buf[1] = 0; +- buf[2] = 0; +- ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), +- GET_IR_DATA_VENDOR_REQUEST, +- USB_TYPE_VENDOR | USB_DIR_IN, +- 0x8080, 0, +- buf, sizeof(buf), 500); +- +-unlock: +- mutex_unlock(&d->i2c_mutex); +- +- if (ret < 0) +- return ret; +- +- if (ret == 1) +- return 0; /* no key pressed */ +- +- /* decoding */ +- b = buf+1; +- +-#if 0 +- deb_rc("RC: %d ", ret); +- debug_dump(b, ret, deb_rc); +-#endif +- +- ev.pulse = 0; +- while (1) { +- ev.pulse = !ev.pulse; +- ev.duration = (*b * FIRMWARE_CLOCK_DIVISOR * FIRMWARE_CLOCK_TICK) / 1000; +- ir_raw_event_store(d->rc_dev, &ev); +- +- b++; +- if (*b == 0xff) { +- ev.pulse = 0; +- ev.duration = 888888*2; +- ir_raw_event_store(d->rc_dev, &ev); +- break; +- } +- } +- +- ir_raw_event_handle(d->rc_dev); +- +- return 1; +-} +- +-static int technisat_usb2_rc_query(struct dvb_usb_device *d) +-{ +- int ret = technisat_usb2_get_ir(d); +- +- if (ret < 0) +- return ret; +- +- if (ret == 0) +- return 0; +- +- if (!disable_led_control) +- technisat_usb2_set_led(d, 1, LED_BLINK); +- +- return 0; +-} +- +-/* DVB-USB and USB stuff follows */ +-static struct usb_device_id technisat_usb2_id_table[] = { +- { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_DVB_S2) }, +- { 0 } /* Terminating entry */ +-}; +- +-/* device description */ +-static struct dvb_usb_device_properties technisat_usb2_devices = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .identify_state = technisat_usb2_identify_state, +- .firmware = "dvb-usb-SkyStar_USB_HD_FW_v17_63.HEX.fw", +- +- .size_of_priv = sizeof(struct technisat_usb2_state), +- +- .i2c_algo = &technisat_usb2_i2c_algo, +- +- .power_ctrl = technisat_usb2_power_ctrl, +- .read_mac_address = technisat_usb2_read_mac_address, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = technisat_usb2_frontend_attach, +- +- .stream = { +- .type = USB_ISOC, +- .count = 8, +- .endpoint = 0x2, +- .u = { +- .isoc = { +- .framesperurb = 32, +- .framesize = 2048, +- .interval = 3, +- } +- } +- }, +- }}, +- .size_of_priv = 0, +- }, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { "Technisat SkyStar USB HD (DVB-S/S2)", +- { &technisat_usb2_id_table[0], NULL }, +- { NULL }, +- }, +- }, +- +- .rc.core = { +- .rc_interval = 100, +- .rc_codes = RC_MAP_TECHNISAT_USB2, +- .module_name = "technisat-usb2", +- .rc_query = technisat_usb2_rc_query, +- .allowed_protos = RC_TYPE_ALL, +- .driver_type = RC_DRIVER_IR_RAW, +- } +-}; +- +-static int technisat_usb2_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *dev; +- +- if (dvb_usb_device_init(intf, &technisat_usb2_devices, THIS_MODULE, +- &dev, adapter_nr) != 0) +- return -ENODEV; +- +- if (dev) { +- struct technisat_usb2_state *state = dev->priv; +- state->dev = dev; +- +- if (!disable_led_control) { +- INIT_DELAYED_WORK(&state->green_led_work, +- technisat_usb2_green_led_control); +- schedule_delayed_work(&state->green_led_work, +- msecs_to_jiffies(500)); +- } +- } +- +- return 0; +-} +- +-static void technisat_usb2_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *dev = usb_get_intfdata(intf); +- +- /* work and stuff was only created when the device is is hot-state */ +- if (dev != NULL) { +- struct technisat_usb2_state *state = dev->priv; +- if (state != NULL) +- cancel_delayed_work_sync(&state->green_led_work); +- } +- +- dvb_usb_device_exit(intf); +-} +- +-static struct usb_driver technisat_usb2_driver = { +- .name = "dvb_usb_technisat_usb2", +- .probe = technisat_usb2_probe, +- .disconnect = technisat_usb2_disconnect, +- .id_table = technisat_usb2_id_table, +-}; +- +-module_usb_driver(technisat_usb2_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for Technisat DVB-S/S2 USB 2.0 device"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c +deleted file mode 100644 +index e53a106..0000000 +--- a/drivers/media/dvb/dvb-usb/ttusb2.c ++++ /dev/null +@@ -1,820 +0,0 @@ +-/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones +- * (e.g. Pinnacle 400e DVB-S USB2.0). +- * +- * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes. +- * +- * TDA8263 + TDA10086 +- * +- * I2C addresses: +- * 0x08 - LNBP21PD - LNB power supply +- * 0x0e - TDA10086 - Demodulator +- * 0x50 - FX2 eeprom +- * 0x60 - TDA8263 - Tuner +- * 0x78 ??? +- * +- * Copyright (c) 2002 Holger Waechtler +- * Copyright (c) 2003 Felix Domke +- * Copyright (C) 2005-6 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#define DVB_USB_LOG_PREFIX "ttusb2" +-#include "dvb-usb.h" +- +-#include "ttusb2.h" +- +-#include "tda826x.h" +-#include "tda10086.h" +-#include "tda1002x.h" +-#include "tda10048.h" +-#include "tda827x.h" +-#include "lnbp21.h" +-/* CA */ +-#include "dvb_ca_en50221.h" +- +-/* debug */ +-static int dvb_usb_ttusb2_debug; +-#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args) +-module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); +-static int dvb_usb_ttusb2_debug_ci; +-module_param_named(debug_ci,dvb_usb_ttusb2_debug_ci, int, 0644); +-MODULE_PARM_DESC(debug_ci, "set debugging ci." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define ci_dbg(format, arg...) \ +-do { \ +- if (dvb_usb_ttusb2_debug_ci) \ +- printk(KERN_DEBUG DVB_USB_LOG_PREFIX \ +- ": %s " format "\n" , __func__, ## arg); \ +-} while (0) +- +-enum { +- TT3650_CMD_CI_TEST = 0x40, +- TT3650_CMD_CI_RD_CTRL, +- TT3650_CMD_CI_WR_CTRL, +- TT3650_CMD_CI_RD_ATTR, +- TT3650_CMD_CI_WR_ATTR, +- TT3650_CMD_CI_RESET, +- TT3650_CMD_CI_SET_VIDEO_PORT +-}; +- +-struct ttusb2_state { +- struct dvb_ca_en50221 ca; +- struct mutex ca_mutex; +- u8 id; +- u16 last_rc_key; +-}; +- +-static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, +- u8 *wbuf, int wlen, u8 *rbuf, int rlen) +-{ +- struct ttusb2_state *st = d->priv; +- u8 *s, *r = NULL; +- int ret = 0; +- +- s = kzalloc(wlen+4, GFP_KERNEL); +- if (!s) +- return -ENOMEM; +- +- r = kzalloc(64, GFP_KERNEL); +- if (!r) { +- kfree(s); +- return -ENOMEM; +- } +- +- s[0] = 0xaa; +- s[1] = ++st->id; +- s[2] = cmd; +- s[3] = wlen; +- memcpy(&s[4],wbuf,wlen); +- +- ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0); +- +- if (ret != 0 || +- r[0] != 0x55 || +- r[1] != s[1] || +- r[2] != cmd || +- (rlen > 0 && r[3] != rlen)) { +- warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]); +- kfree(s); +- kfree(r); +- return -EIO; +- } +- +- if (rlen > 0) +- memcpy(rbuf, &r[4], rlen); +- +- kfree(s); +- kfree(r); +- +- return 0; +-} +- +-/* ci */ +-static int tt3650_ci_msg(struct dvb_usb_device *d, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +-{ +- int ret; +- u8 rx[60];/* (64 -4) */ +- ret = ttusb2_msg(d, cmd, data, write_len, rx, read_len); +- if (!ret) +- memcpy(data, rx, read_len); +- return ret; +-} +- +-static int tt3650_ci_msg_locked(struct dvb_ca_en50221 *ca, u8 cmd, u8 *data, unsigned int write_len, unsigned int read_len) +-{ +- struct dvb_usb_device *d = ca->data; +- struct ttusb2_state *state = d->priv; +- int ret; +- +- mutex_lock(&state->ca_mutex); +- ret = tt3650_ci_msg(d, cmd, data, write_len, read_len); +- mutex_unlock(&state->ca_mutex); +- +- return ret; +-} +- +-static int tt3650_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +-{ +- u8 buf[3]; +- int ret = 0; +- +- if (slot) +- return -EINVAL; +- +- buf[0] = (address >> 8) & 0x0F; +- buf[1] = address; +- +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_ATTR, buf, 2, 3); +- +- ci_dbg("%04x -> %d 0x%02x", address, ret, buf[2]); +- +- if (ret < 0) +- return ret; +- +- return buf[2]; +-} +- +-static int tt3650_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +-{ +- u8 buf[3]; +- +- ci_dbg("%d 0x%04x 0x%02x", slot, address, value); +- +- if (slot) +- return -EINVAL; +- +- buf[0] = (address >> 8) & 0x0F; +- buf[1] = address; +- buf[2] = value; +- +- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_ATTR, buf, 3, 3); +-} +- +-static int tt3650_ci_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +-{ +- u8 buf[2]; +- int ret; +- +- if (slot) +- return -EINVAL; +- +- buf[0] = address & 3; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_RD_CTRL, buf, 1, 2); +- +- ci_dbg("0x%02x -> %d 0x%02x", address, ret, buf[1]); +- +- if (ret < 0) +- return ret; +- +- return buf[1]; +-} +- +-static int tt3650_ci_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +-{ +- u8 buf[2]; +- +- ci_dbg("%d 0x%02x 0x%02x", slot, address, value); +- +- if (slot) +- return -EINVAL; +- +- buf[0] = address; +- buf[1] = value; +- +- return tt3650_ci_msg_locked(ca, TT3650_CMD_CI_WR_CTRL, buf, 2, 2); +-} +- +-static int tt3650_ci_set_video_port(struct dvb_ca_en50221 *ca, int slot, int enable) +-{ +- u8 buf[1]; +- int ret; +- +- ci_dbg("%d %d", slot, enable); +- +- if (slot) +- return -EINVAL; +- +- buf[0] = enable; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); +- if (ret < 0) +- return ret; +- +- if (enable != buf[0]) { +- err("CI not %sabled.", enable ? "en" : "dis"); +- return -EIO; +- } +- +- return 0; +-} +- +-static int tt3650_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +-{ +- return tt3650_ci_set_video_port(ca, slot, 0); +-} +- +-static int tt3650_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +-{ +- return tt3650_ci_set_video_port(ca, slot, 1); +-} +- +-static int tt3650_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct dvb_usb_device *d = ca->data; +- struct ttusb2_state *state = d->priv; +- u8 buf[1]; +- int ret; +- +- ci_dbg("%d", slot); +- +- if (slot) +- return -EINVAL; +- +- buf[0] = 0; +- +- mutex_lock(&state->ca_mutex); +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); +- if (ret) +- goto failed; +- +- msleep(500); +- +- buf[0] = 1; +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_RESET, buf, 1, 1); +- if (ret) +- goto failed; +- +- msleep(500); +- +- buf[0] = 0; /* FTA */ +- +- ret = tt3650_ci_msg(d, TT3650_CMD_CI_SET_VIDEO_PORT, buf, 1, 1); +- +- msleep(1100); +- +- failed: +- mutex_unlock(&state->ca_mutex); +- +- return ret; +-} +- +-static int tt3650_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +-{ +- u8 buf[1]; +- int ret; +- +- if (slot) +- return -EINVAL; +- +- ret = tt3650_ci_msg_locked(ca, TT3650_CMD_CI_TEST, buf, 0, 1); +- if (ret) +- return ret; +- +- if (1 == buf[0]) { +- return DVB_CA_EN50221_POLL_CAM_PRESENT | +- DVB_CA_EN50221_POLL_CAM_READY; +- } +- return 0; +-} +- +-static void tt3650_ci_uninit(struct dvb_usb_device *d) +-{ +- struct ttusb2_state *state; +- +- ci_dbg(""); +- +- if (NULL == d) +- return; +- +- state = d->priv; +- if (NULL == state) +- return; +- +- if (NULL == state->ca.data) +- return; +- +- dvb_ca_en50221_release(&state->ca); +- +- memset(&state->ca, 0, sizeof(state->ca)); +-} +- +-static int tt3650_ci_init(struct dvb_usb_adapter *a) +-{ +- struct dvb_usb_device *d = a->dev; +- struct ttusb2_state *state = d->priv; +- int ret; +- +- ci_dbg(""); +- +- mutex_init(&state->ca_mutex); +- +- state->ca.owner = THIS_MODULE; +- state->ca.read_attribute_mem = tt3650_ci_read_attribute_mem; +- state->ca.write_attribute_mem = tt3650_ci_write_attribute_mem; +- state->ca.read_cam_control = tt3650_ci_read_cam_control; +- state->ca.write_cam_control = tt3650_ci_write_cam_control; +- state->ca.slot_reset = tt3650_ci_slot_reset; +- state->ca.slot_shutdown = tt3650_ci_slot_shutdown; +- state->ca.slot_ts_enable = tt3650_ci_slot_ts_enable; +- state->ca.poll_slot_status = tt3650_ci_poll_slot_status; +- state->ca.data = d; +- +- ret = dvb_ca_en50221_init(&a->dvb_adap, +- &state->ca, +- /* flags */ 0, +- /* n_slots */ 1); +- if (ret) { +- err("Cannot initialize CI: Error %d.", ret); +- memset(&state->ca, 0, sizeof(state->ca)); +- return ret; +- } +- +- info("CI initialized."); +- +- return 0; +-} +- +-static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +-{ +- struct dvb_usb_device *d = i2c_get_adapdata(adap); +- static u8 obuf[60], ibuf[60]; +- int i, write_read, read; +- +- if (mutex_lock_interruptible(&d->i2c_mutex) < 0) +- return -EAGAIN; +- +- if (num > 2) +- warn("more than 2 i2c messages at a time is not handled yet. TODO."); +- +- for (i = 0; i < num; i++) { +- write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD); +- read = msg[i].flags & I2C_M_RD; +- +- obuf[0] = (msg[i].addr << 1) | (write_read | read); +- if (read) +- obuf[1] = 0; +- else +- obuf[1] = msg[i].len; +- +- /* read request */ +- if (write_read) +- obuf[2] = msg[i+1].len; +- else if (read) +- obuf[2] = msg[i].len; +- else +- obuf[2] = 0; +- +- memcpy(&obuf[3], msg[i].buf, msg[i].len); +- +- if (ttusb2_msg(d, CMD_I2C_XFER, obuf, obuf[1]+3, ibuf, obuf[2] + 3) < 0) { +- err("i2c transfer failed."); +- break; +- } +- +- if (write_read) { +- memcpy(msg[i+1].buf, &ibuf[3], msg[i+1].len); +- i++; +- } else if (read) +- memcpy(msg[i].buf, &ibuf[3], msg[i].len); +- } +- +- mutex_unlock(&d->i2c_mutex); +- return i; +-} +- +-static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm ttusb2_i2c_algo = { +- .master_xfer = ttusb2_i2c_xfer, +- .functionality = ttusb2_i2c_func, +-}; +- +-/* command to poll IR receiver (copied from pctv452e.c) */ +-#define CMD_GET_IR_CODE 0x1b +- +-/* IR */ +-static int tt3650_rc_query(struct dvb_usb_device *d) +-{ +- int ret; +- u8 rx[9]; /* A CMD_GET_IR_CODE reply is 9 bytes long */ +- struct ttusb2_state *st = d->priv; +- ret = ttusb2_msg(d, CMD_GET_IR_CODE, NULL, 0, rx, sizeof(rx)); +- if (ret != 0) +- return ret; +- +- if (rx[8] & 0x01) { +- /* got a "press" event */ +- st->last_rc_key = (rx[3] << 8) | rx[2]; +- deb_info("%s: cmd=0x%02x sys=0x%02x\n", __func__, rx[2], rx[3]); +- rc_keydown(d->rc_dev, st->last_rc_key, 0); +- } else if (st->last_rc_key) { +- rc_keyup(d->rc_dev); +- st->last_rc_key = 0; +- } +- +- return 0; +-} +- +- +-/* Callbacks for DVB USB */ +-static int ttusb2_identify_state (struct usb_device *udev, struct +- dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, +- int *cold) +-{ +- *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; +- return 0; +-} +- +-static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 b = onoff; +- ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0); +- return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0); +-} +- +- +-static struct tda10086_config tda10086_config = { +- .demod_address = 0x0e, +- .invert = 0, +- .diseqc_tone = 1, +- .xtal_freq = TDA10086_XTAL_16M, +-}; +- +-static struct tda10023_config tda10023_config = { +- .demod_address = 0x0c, +- .invert = 0, +- .xtal = 16000000, +- .pll_m = 11, +- .pll_p = 3, +- .pll_n = 1, +- .deltaf = 0xa511, +-}; +- +-static struct tda10048_config tda10048_config = { +- .demod_address = 0x10 >> 1, +- .output_mode = TDA10048_PARALLEL_OUTPUT, +- .inversion = TDA10048_INVERSION_ON, +- .dtv6_if_freq_khz = TDA10048_IF_4000, +- .dtv7_if_freq_khz = TDA10048_IF_4500, +- .dtv8_if_freq_khz = TDA10048_IF_5000, +- .clk_freq_khz = TDA10048_CLK_16000, +- .no_firmware = 1, +- .set_pll = true , +- .pll_m = 5, +- .pll_n = 3, +- .pll_p = 0, +-}; +- +-static struct tda827x_config tda827x_config = { +- .config = 0, +-}; +- +-static int ttusb2_frontend_tda10086_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev,0,3) < 0) +- err("set interface to alts=3 failed"); +- +- if ((adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { +- deb_info("TDA10086 attach failed\n"); +- return -ENODEV; +- } +- +- return 0; +-} +- +-static int ttusb2_ct3650_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct dvb_usb_adapter *adap = fe->dvb->priv; +- +- return adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, enable); +-} +- +-static int ttusb2_frontend_tda10023_attach(struct dvb_usb_adapter *adap) +-{ +- if (usb_set_interface(adap->dev->udev, 0, 3) < 0) +- err("set interface to alts=3 failed"); +- +- if (adap->fe_adap[0].fe == NULL) { +- /* FE 0 DVB-C */ +- adap->fe_adap[0].fe = dvb_attach(tda10023_attach, +- &tda10023_config, &adap->dev->i2c_adap, 0x48); +- +- if (adap->fe_adap[0].fe == NULL) { +- deb_info("TDA10023 attach failed\n"); +- return -ENODEV; +- } +- tt3650_ci_init(adap); +- } else { +- adap->fe_adap[1].fe = dvb_attach(tda10048_attach, +- &tda10048_config, &adap->dev->i2c_adap); +- +- if (adap->fe_adap[1].fe == NULL) { +- deb_info("TDA10048 attach failed\n"); +- return -ENODEV; +- } +- +- /* tuner is behind TDA10023 I2C-gate */ +- adap->fe_adap[1].fe->ops.i2c_gate_ctrl = ttusb2_ct3650_i2c_gate_ctrl; +- +- } +- +- return 0; +-} +- +-static int ttusb2_tuner_tda827x_attach(struct dvb_usb_adapter *adap) +-{ +- struct dvb_frontend *fe; +- +- /* MFE: select correct FE to attach tuner since that's called twice */ +- if (adap->fe_adap[1].fe == NULL) +- fe = adap->fe_adap[0].fe; +- else +- fe = adap->fe_adap[1].fe; +- +- /* attach tuner */ +- if (dvb_attach(tda827x_attach, fe, 0x61, &adap->dev->i2c_adap, &tda827x_config) == NULL) { +- printk(KERN_ERR "%s: No tda827x found!\n", __func__); +- return -ENODEV; +- } +- return 0; +-} +- +-static int ttusb2_tuner_tda826x_attach(struct dvb_usb_adapter *adap) +-{ +- if (dvb_attach(tda826x_attach, adap->fe_adap[0].fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { +- deb_info("TDA8263 attach failed\n"); +- return -ENODEV; +- } +- +- if (dvb_attach(lnbp21_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, 0, 0) == NULL) { +- deb_info("LNBP21 attach failed\n"); +- return -ENODEV; +- } +- return 0; +-} +- +-/* DVB USB Driver stuff */ +-static struct dvb_usb_device_properties ttusb2_properties; +-static struct dvb_usb_device_properties ttusb2_properties_s2400; +-static struct dvb_usb_device_properties ttusb2_properties_ct3650; +- +-static void ttusb2_usb_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- +- tt3650_ci_uninit(d); +- dvb_usb_device_exit(intf); +-} +- +-static int ttusb2_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &ttusb2_properties, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &ttusb2_properties_s2400, +- THIS_MODULE, NULL, adapter_nr) || +- 0 == dvb_usb_device_init(intf, &ttusb2_properties_ct3650, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- return -ENODEV; +-} +- +-static struct usb_device_id ttusb2_table [] = { +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, +- { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) }, +- { USB_DEVICE(USB_VID_TECHNOTREND, +- USB_PID_TECHNOTREND_CONNECT_S2400) }, +- { USB_DEVICE(USB_VID_TECHNOTREND, +- USB_PID_TECHNOTREND_CONNECT_CT3650) }, +- {} /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, ttusb2_table); +- +-static struct dvb_usb_device_properties ttusb2_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-pctv-400e-01.fw", +- +- .size_of_priv = sizeof(struct ttusb2_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = NULL, // ttusb2_streaming_ctrl, +- +- .frontend_attach = ttusb2_frontend_tda10086_attach, +- .tuner_attach = ttusb2_tuner_tda826x_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1, +- } +- } +- } +- }}, +- } +- }, +- +- .power_ctrl = ttusb2_power_ctrl, +- .identify_state = ttusb2_identify_state, +- +- .i2c_algo = &ttusb2_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 2, +- .devices = { +- { "Pinnacle 400e DVB-S USB2.0", +- { &ttusb2_table[0], NULL }, +- { NULL }, +- }, +- { "Pinnacle 450e DVB-S USB2.0", +- { &ttusb2_table[1], NULL }, +- { NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties ttusb2_properties_s2400 = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-tt-s2400-01.fw", +- +- .size_of_priv = sizeof(struct ttusb2_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = NULL, +- +- .frontend_attach = ttusb2_frontend_tda10086_attach, +- .tuner_attach = ttusb2_tuner_tda826x_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1, +- } +- } +- } +- }}, +- } +- }, +- +- .power_ctrl = ttusb2_power_ctrl, +- .identify_state = ttusb2_identify_state, +- +- .i2c_algo = &ttusb2_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Technotrend TT-connect S-2400", +- { &ttusb2_table[2], NULL }, +- { NULL }, +- }, +- } +-}; +- +-static struct dvb_usb_device_properties ttusb2_properties_ct3650 = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- +- .size_of_priv = sizeof(struct ttusb2_state), +- +- .rc.core = { +- .rc_interval = 150, /* Less than IR_KEYPRESS_TIMEOUT */ +- .rc_codes = RC_MAP_TT_1500, +- .rc_query = tt3650_rc_query, +- .allowed_protos = RC_TYPE_UNKNOWN, +- }, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 2, +- .fe = {{ +- .streaming_ctrl = NULL, +- +- .frontend_attach = ttusb2_frontend_tda10023_attach, +- .tuner_attach = ttusb2_tuner_tda827x_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1, +- } +- } +- } +- }, { +- .streaming_ctrl = NULL, +- +- .frontend_attach = ttusb2_frontend_tda10023_attach, +- .tuner_attach = ttusb2_tuner_tda827x_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_ISOC, +- .count = 5, +- .endpoint = 0x02, +- .u = { +- .isoc = { +- .framesperurb = 4, +- .framesize = 940, +- .interval = 1, +- } +- } +- } +- }}, +- }, +- }, +- +- .power_ctrl = ttusb2_power_ctrl, +- .identify_state = ttusb2_identify_state, +- +- .i2c_algo = &ttusb2_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Technotrend TT-connect CT-3650", +- .warm_ids = { &ttusb2_table[3], NULL }, +- }, +- } +-}; +- +-static struct usb_driver ttusb2_driver = { +- .name = "dvb_usb_ttusb2", +- .probe = ttusb2_probe, +- .disconnect = ttusb2_usb_disconnect, +- .id_table = ttusb2_table, +-}; +- +-module_usb_driver(ttusb2_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/dvb/dvb-usb/ttusb2.h +deleted file mode 100644 +index 52a63af..0000000 +--- a/drivers/media/dvb/dvb-usb/ttusb2.h ++++ /dev/null +@@ -1,70 +0,0 @@ +-/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones +- * (e.g. Pinnacle 400e DVB-S USB2.0). +- * +- * Copyright (c) 2002 Holger Waechtler +- * Copyright (c) 2003 Felix Domke +- * Copyright (C) 2005-6 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_TTUSB2_H_ +-#define _DVB_USB_TTUSB2_H_ +- +-/* TTUSB protocol +- * +- * always to messages (out/in) +- * out message: +- * 0xaa +- * +- * in message (complete block is always 0x40 bytes long) +- * 0x55 +- * +- * id is incremented for each transaction +- */ +- +-#define CMD_DSP_DOWNLOAD 0x13 +-/* out data: [28] +- * last block must be empty */ +- +-#define CMD_DSP_BOOT 0x14 +-/* out data: nothing */ +- +-#define CMD_POWER 0x15 +-/* out data: */ +- +-#define CMD_LNB 0x16 +-/* out data: <18V=0,13V=1> */ +- +-#define CMD_GET_VERSION 0x17 +-/* in data: [5] */ +- +-#define CMD_DISEQC 0x18 +-/* out data: [cmdlen] */ +- +-#define CMD_PID_ENABLE 0x22 +-/* out data: */ +- +-#define CMD_PID_DISABLE 0x23 +-/* out data: */ +- +-#define CMD_FILTER_ENABLE 0x24 +-/* out data: [12] [12] */ +- +-#define CMD_FILTER_DISABLE 0x25 +-/* out data: */ +- +-#define CMD_GET_DSP_VERSION 0x26 +-/* in data: [28] */ +- +-#define CMD_I2C_XFER 0x31 +-/* out data: [sndlen] +- * in data: [rcvlen] */ +- +-#define CMD_I2C_BITRATE 0x32 +-/* out data: */ +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c +deleted file mode 100644 +index 9b04229..0000000 +--- a/drivers/media/dvb/dvb-usb/umt-010.c ++++ /dev/null +@@ -1,151 +0,0 @@ +-/* DVB USB framework compliant Linux driver for the HanfTek UMT-010 USB2.0 +- * DVB-T receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "dibusb.h" +- +-#include "mt352.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static int umt_mt352_demod_init(struct dvb_frontend *fe) +-{ +- static u8 mt352_clock_config[] = { 0x89, 0xb8, 0x2d }; +- static u8 mt352_reset[] = { 0x50, 0x80 }; +- static u8 mt352_mclk_ratio[] = { 0x8b, 0x00 }; +- static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 }; +- static u8 mt352_agc_cfg[] = { 0x67, 0x10, 0xa0 }; +- +- static u8 mt352_sec_agc_cfg1[] = { 0x6a, 0xff }; +- static u8 mt352_sec_agc_cfg2[] = { 0x6d, 0xff }; +- static u8 mt352_sec_agc_cfg3[] = { 0x70, 0x40 }; +- static u8 mt352_sec_agc_cfg4[] = { 0x7b, 0x03 }; +- static u8 mt352_sec_agc_cfg5[] = { 0x7d, 0x0f }; +- +- static u8 mt352_acq_ctl[] = { 0x53, 0x50 }; +- static u8 mt352_input_freq_1[] = { 0x56, 0x31, 0x06 }; +- +- mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config)); +- udelay(2000); +- mt352_write(fe, mt352_reset, sizeof(mt352_reset)); +- mt352_write(fe, mt352_mclk_ratio, sizeof(mt352_mclk_ratio)); +- +- 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_sec_agc_cfg1, sizeof(mt352_sec_agc_cfg1)); +- mt352_write(fe, mt352_sec_agc_cfg2, sizeof(mt352_sec_agc_cfg2)); +- mt352_write(fe, mt352_sec_agc_cfg3, sizeof(mt352_sec_agc_cfg3)); +- mt352_write(fe, mt352_sec_agc_cfg4, sizeof(mt352_sec_agc_cfg4)); +- mt352_write(fe, mt352_sec_agc_cfg5, sizeof(mt352_sec_agc_cfg5)); +- +- mt352_write(fe, mt352_acq_ctl, sizeof(mt352_acq_ctl)); +- mt352_write(fe, mt352_input_freq_1, sizeof(mt352_input_freq_1)); +- +- return 0; +-} +- +-static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- struct mt352_config umt_config; +- +- memset(&umt_config,0,sizeof(struct mt352_config)); +- umt_config.demod_init = umt_mt352_demod_init; +- umt_config.demod_address = 0xf; +- +- adap->fe_adap[0].fe = dvb_attach(mt352_attach, &umt_config, &adap->dev->i2c_adap); +- +- return 0; +-} +- +-static int umt_tuner_attach (struct dvb_usb_adapter *adap) +-{ +- dvb_attach(dvb_pll_attach, adap->fe_adap[0].fe, 0x61, NULL, DVB_PLL_TUA6034); +- return 0; +-} +- +-/* USB Driver stuff */ +-static struct dvb_usb_device_properties umt_properties; +- +-static int umt_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- if (0 == dvb_usb_device_init(intf, &umt_properties, +- THIS_MODULE, NULL, adapter_nr)) +- return 0; +- return -EINVAL; +-} +- +-/* do not change the order of the ID table */ +-static struct usb_device_id umt_table [] = { +-/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, +-/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, +- { } /* Terminating entry */ +-}; +-MODULE_DEVICE_TABLE (usb, umt_table); +- +-static struct dvb_usb_device_properties umt_properties = { +- .caps = DVB_USB_IS_AN_I2C_ADAPTER, +- +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-umt-010-02.fw", +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .streaming_ctrl = dibusb2_0_streaming_ctrl, +- .frontend_attach = umt_mt352_frontend_attach, +- .tuner_attach = umt_tuner_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = MAX_NO_URBS_FOR_DATA_STREAM, +- .endpoint = 0x06, +- .u = { +- .bulk = { +- .buffersize = 512, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct dibusb_state), +- } +- }, +- .power_ctrl = dibusb_power_ctrl, +- +- .i2c_algo = &dibusb_i2c_algo, +- +- .generic_bulk_ctrl_endpoint = 0x01, +- +- .num_device_descs = 1, +- .devices = { +- { "Hanftek UMT-010 DVB-T USB2.0", +- { &umt_table[0], NULL }, +- { &umt_table[1], NULL }, +- }, +- } +-}; +- +-static struct usb_driver umt_driver = { +- .name = "dvb_usb_umt_010", +- .probe = umt_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = umt_table, +-}; +- +-module_usb_driver(umt_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for HanfTek UMT 010 USB2.0 DVB-T device"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/usb-urb.c b/drivers/media/dvb/dvb-usb/usb-urb.c +deleted file mode 100644 +index d62ee0f..0000000 +--- a/drivers/media/dvb/dvb-usb/usb-urb.c ++++ /dev/null +@@ -1,254 +0,0 @@ +-/* usb-urb.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 keeps functions for initializing and handling the +- * BULK and ISOC USB data transfers in a generic way. +- * Can be used for DVB-only and also, that's the plan, for +- * Hybrid USB devices (analog and DVB). +- */ +-#include "dvb-usb-common.h" +- +-/* URB stuff for streaming */ +-static void usb_urb_complete(struct urb *urb) +-{ +- struct usb_data_stream *stream = urb->context; +- int ptype = usb_pipetype(urb->pipe); +- int i; +- u8 *b; +- +- deb_uxfer("'%s' urb completed. status: %d, length: %d/%d, pack_num: %d, errors: %d\n", +- ptype == PIPE_ISOCHRONOUS ? "isoc" : "bulk", +- urb->status,urb->actual_length,urb->transfer_buffer_length, +- urb->number_of_packets,urb->error_count); +- +- switch (urb->status) { +- case 0: /* success */ +- case -ETIMEDOUT: /* NAK */ +- break; +- case -ECONNRESET: /* kill */ +- case -ENOENT: +- case -ESHUTDOWN: +- return; +- default: /* error */ +- deb_ts("urb completition error %d.\n", urb->status); +- break; +- } +- +- b = (u8 *) urb->transfer_buffer; +- switch (ptype) { +- case PIPE_ISOCHRONOUS: +- for (i = 0; i < urb->number_of_packets; i++) { +- +- if (urb->iso_frame_desc[i].status != 0) +- deb_ts("iso frame descriptor has an error: %d\n",urb->iso_frame_desc[i].status); +- else if (urb->iso_frame_desc[i].actual_length > 0) +- stream->complete(stream, b + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); +- +- urb->iso_frame_desc[i].status = 0; +- urb->iso_frame_desc[i].actual_length = 0; +- } +- debug_dump(b,20,deb_uxfer); +- break; +- case PIPE_BULK: +- if (urb->actual_length > 0) +- stream->complete(stream, b, urb->actual_length); +- break; +- default: +- err("unknown endpoint type in completition handler."); +- return; +- } +- usb_submit_urb(urb,GFP_ATOMIC); +-} +- +-int usb_urb_kill(struct usb_data_stream *stream) +-{ +- int i; +- for (i = 0; i < stream->urbs_submitted; i++) { +- deb_ts("killing URB no. %d.\n",i); +- +- /* stop the URB */ +- usb_kill_urb(stream->urb_list[i]); +- } +- stream->urbs_submitted = 0; +- return 0; +-} +- +-int usb_urb_submit(struct usb_data_stream *stream) +-{ +- int i,ret; +- for (i = 0; i < stream->urbs_initialized; i++) { +- deb_ts("submitting URB no. %d\n",i); +- if ((ret = usb_submit_urb(stream->urb_list[i],GFP_ATOMIC))) { +- err("could not submit URB no. %d - get them all back",i); +- usb_urb_kill(stream); +- return ret; +- } +- stream->urbs_submitted++; +- } +- return 0; +-} +- +-static int usb_free_stream_buffers(struct usb_data_stream *stream) +-{ +- if (stream->state & USB_STATE_URB_BUF) { +- while (stream->buf_num) { +- stream->buf_num--; +- deb_mem("freeing buffer %d\n",stream->buf_num); +- usb_free_coherent(stream->udev, stream->buf_size, +- stream->buf_list[stream->buf_num], +- stream->dma_addr[stream->buf_num]); +- } +- } +- +- stream->state &= ~USB_STATE_URB_BUF; +- +- return 0; +-} +- +-static int usb_allocate_stream_buffers(struct usb_data_stream *stream, int num, unsigned long size) +-{ +- stream->buf_num = 0; +- stream->buf_size = size; +- +- deb_mem("all in all I will use %lu bytes for streaming\n",num*size); +- +- for (stream->buf_num = 0; stream->buf_num < num; stream->buf_num++) { +- deb_mem("allocating buffer %d\n",stream->buf_num); +- if (( stream->buf_list[stream->buf_num] = +- usb_alloc_coherent(stream->udev, size, GFP_ATOMIC, +- &stream->dma_addr[stream->buf_num]) ) == NULL) { +- deb_mem("not enough memory for urb-buffer allocation.\n"); +- usb_free_stream_buffers(stream); +- return -ENOMEM; +- } +- deb_mem("buffer %d: %p (dma: %Lu)\n", +- stream->buf_num, +-stream->buf_list[stream->buf_num], (long long)stream->dma_addr[stream->buf_num]); +- memset(stream->buf_list[stream->buf_num],0,size); +- stream->state |= USB_STATE_URB_BUF; +- } +- deb_mem("allocation successful\n"); +- +- return 0; +-} +- +-static int usb_bulk_urb_init(struct usb_data_stream *stream) +-{ +- int i, j; +- +- if ((i = usb_allocate_stream_buffers(stream,stream->props.count, +- stream->props.u.bulk.buffersize)) < 0) +- return i; +- +- /* allocate the URBs */ +- for (i = 0; i < stream->props.count; i++) { +- stream->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); +- if (!stream->urb_list[i]) { +- deb_mem("not enough memory for urb_alloc_urb!.\n"); +- for (j = 0; j < i; j++) +- usb_free_urb(stream->urb_list[j]); +- return -ENOMEM; +- } +- usb_fill_bulk_urb( stream->urb_list[i], stream->udev, +- usb_rcvbulkpipe(stream->udev,stream->props.endpoint), +- stream->buf_list[i], +- stream->props.u.bulk.buffersize, +- usb_urb_complete, stream); +- +- stream->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; +- stream->urb_list[i]->transfer_dma = stream->dma_addr[i]; +- stream->urbs_initialized++; +- } +- return 0; +-} +- +-static int usb_isoc_urb_init(struct usb_data_stream *stream) +-{ +- int i,j; +- +- if ((i = usb_allocate_stream_buffers(stream,stream->props.count, +- stream->props.u.isoc.framesize*stream->props.u.isoc.framesperurb)) < 0) +- return i; +- +- /* allocate the URBs */ +- for (i = 0; i < stream->props.count; i++) { +- struct urb *urb; +- int frame_offset = 0; +- +- stream->urb_list[i] = usb_alloc_urb(stream->props.u.isoc.framesperurb, GFP_ATOMIC); +- if (!stream->urb_list[i]) { +- deb_mem("not enough memory for urb_alloc_urb!\n"); +- for (j = 0; j < i; j++) +- usb_free_urb(stream->urb_list[j]); +- return -ENOMEM; +- } +- +- urb = stream->urb_list[i]; +- +- urb->dev = stream->udev; +- urb->context = stream; +- urb->complete = usb_urb_complete; +- urb->pipe = usb_rcvisocpipe(stream->udev,stream->props.endpoint); +- urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; +- urb->interval = stream->props.u.isoc.interval; +- urb->number_of_packets = stream->props.u.isoc.framesperurb; +- urb->transfer_buffer_length = stream->buf_size; +- urb->transfer_buffer = stream->buf_list[i]; +- urb->transfer_dma = stream->dma_addr[i]; +- +- for (j = 0; j < stream->props.u.isoc.framesperurb; j++) { +- urb->iso_frame_desc[j].offset = frame_offset; +- urb->iso_frame_desc[j].length = stream->props.u.isoc.framesize; +- frame_offset += stream->props.u.isoc.framesize; +- } +- +- stream->urbs_initialized++; +- } +- return 0; +-} +- +-int usb_urb_init(struct usb_data_stream *stream, struct usb_data_stream_properties *props) +-{ +- if (stream == NULL || props == NULL) +- return -EINVAL; +- +- memcpy(&stream->props, props, sizeof(*props)); +- +- usb_clear_halt(stream->udev,usb_rcvbulkpipe(stream->udev,stream->props.endpoint)); +- +- if (stream->complete == NULL) { +- err("there is no data callback - this doesn't make sense."); +- return -EINVAL; +- } +- +- switch (stream->props.type) { +- case USB_BULK: +- return usb_bulk_urb_init(stream); +- case USB_ISOC: +- return usb_isoc_urb_init(stream); +- default: +- err("unknown URB-type for data transfer."); +- return -EINVAL; +- } +-} +- +-int usb_urb_exit(struct usb_data_stream *stream) +-{ +- int i; +- +- usb_urb_kill(stream); +- +- for (i = 0; i < stream->urbs_initialized; i++) { +- if (stream->urb_list[i] != NULL) { +- deb_mem("freeing URB no. %d.\n",i); +- /* free the URBs */ +- usb_free_urb(stream->urb_list[i]); +- } +- } +- stream->urbs_initialized = 0; +- +- usb_free_stream_buffers(stream); +- return 0; +-} +diff --git a/drivers/media/dvb/dvb-usb/vp702x-fe.c b/drivers/media/dvb/dvb-usb/vp702x-fe.c +deleted file mode 100644 +index 5eab468..0000000 +--- a/drivers/media/dvb/dvb-usb/vp702x-fe.c ++++ /dev/null +@@ -1,379 +0,0 @@ +-/* DVB frontend part of the Linux driver for the TwinhanDTV StarBox USB2.0 +- * DVB-S receiver. +- * +- * Copyright (C) 2005 Ralph Metzler +- * Metzler Brothers Systementwicklung GbR +- * +- * Copyright (C) 2005 Patrick Boettcher +- * +- * Thanks to Twinhan who kindly provided hardware and information. +- * +- * This file can be removed soon, after the DST-driver is rewritten to provice +- * the frontend-controlling separately. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * +- */ +-#include "vp702x.h" +- +-struct vp702x_fe_state { +- struct dvb_frontend fe; +- struct dvb_usb_device *d; +- +- struct dvb_frontend_ops ops; +- +- fe_sec_voltage_t voltage; +- fe_sec_tone_mode_t tone_mode; +- +- u8 lnb_buf[8]; +- +- u8 lock; +- u8 sig; +- u8 snr; +- +- unsigned long next_status_check; +- unsigned long status_check_interval; +-}; +- +-static int vp702x_fe_refresh_state(struct vp702x_fe_state *st) +-{ +- struct vp702x_device_state *dst = st->d->priv; +- u8 *buf; +- +- if (time_after(jiffies, st->next_status_check)) { +- mutex_lock(&dst->buf_mutex); +- buf = dst->buf; +- +- vp702x_usb_in_op(st->d, READ_STATUS, 0, 0, buf, 10); +- st->lock = buf[4]; +- +- vp702x_usb_in_op(st->d, READ_TUNER_REG_REQ, 0x11, 0, buf, 1); +- st->snr = buf[0]; +- +- vp702x_usb_in_op(st->d, READ_TUNER_REG_REQ, 0x15, 0, buf, 1); +- st->sig = buf[0]; +- +- mutex_unlock(&dst->buf_mutex); +- st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; +- } +- return 0; +-} +- +-static u8 vp702x_chksum(u8 *buf,int f, int count) +-{ +- u8 s = 0; +- int i; +- for (i = f; i < f+count; i++) +- s += buf[i]; +- return ~s+1; +-} +- +-static int vp702x_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- vp702x_fe_refresh_state(st); +- deb_fe("%s\n",__func__); +- +- if (st->lock == 0) +- *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER; +- else +- *status = 0; +- +- if (*status & FE_HAS_LOCK) +- st->status_check_interval = 1000; +- else +- st->status_check_interval = 250; +- return 0; +-} +- +-/* not supported by this Frontend */ +-static int vp702x_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- vp702x_fe_refresh_state(st); +- *ber = 0; +- return 0; +-} +- +-/* not supported by this Frontend */ +-static int vp702x_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- vp702x_fe_refresh_state(st); +- *unc = 0; +- return 0; +-} +- +-static int vp702x_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- vp702x_fe_refresh_state(st); +- +- *strength = (st->sig << 8) | st->sig; +- return 0; +-} +- +-static int vp702x_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- u8 _snr; +- struct vp702x_fe_state *st = fe->demodulator_priv; +- vp702x_fe_refresh_state(st); +- +- _snr = (st->snr & 0x1f) * 0xff / 0x1f; +- *snr = (_snr << 8) | _snr; +- return 0; +-} +- +-static int vp702x_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- deb_fe("%s\n",__func__); +- tune->min_delay_ms = 2000; +- return 0; +-} +- +-static int vp702x_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct vp702x_fe_state *st = fe->demodulator_priv; +- struct vp702x_device_state *dst = st->d->priv; +- u32 freq = fep->frequency/1000; +- /*CalFrequency*/ +-/* u16 frequencyRef[16] = { 2, 4, 8, 16, 32, 64, 128, 256, 24, 5, 10, 20, 40, 80, 160, 320 }; */ +- u64 sr; +- u8 *cmd; +- +- mutex_lock(&dst->buf_mutex); +- +- cmd = dst->buf; +- memset(cmd, 0, 10); +- +- cmd[0] = (freq >> 8) & 0x7f; +- cmd[1] = freq & 0xff; +- cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */ +- +- sr = (u64) (fep->symbol_rate/1000) << 20; +- do_div(sr,88000); +- cmd[3] = (sr >> 12) & 0xff; +- cmd[4] = (sr >> 4) & 0xff; +- cmd[5] = (sr << 4) & 0xf0; +- +- deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n", +- fep->frequency, freq, freq, fep->symbol_rate, +- (unsigned long) sr, (unsigned long) sr); +- +-/* if (fep->inversion == INVERSION_ON) +- cmd[6] |= 0x80; */ +- +- if (st->voltage == SEC_VOLTAGE_18) +- cmd[6] |= 0x40; +- +-/* if (fep->symbol_rate > 8000000) +- cmd[6] |= 0x20; +- +- if (fep->frequency < 1531000) +- cmd[6] |= 0x04; +- +- if (st->tone_mode == SEC_TONE_ON) +- cmd[6] |= 0x01;*/ +- +- cmd[7] = vp702x_chksum(cmd,0,7); +- +- st->status_check_interval = 250; +- st->next_status_check = jiffies; +- +- vp702x_usb_inout_op(st->d, cmd, 8, cmd, 10, 100); +- +- if (cmd[2] == 0 && cmd[3] == 0) +- deb_fe("tuning failed.\n"); +- else +- deb_fe("tuning succeeded.\n"); +- +- mutex_unlock(&dst->buf_mutex); +- +- return 0; +-} +- +-static int vp702x_fe_init(struct dvb_frontend *fe) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- deb_fe("%s\n",__func__); +- vp702x_usb_in_op(st->d, RESET_TUNER, 0, 0, NULL, 0); +- return 0; +-} +- +-static int vp702x_fe_sleep(struct dvb_frontend *fe) +-{ +- deb_fe("%s\n",__func__); +- return 0; +-} +- +-static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- u8 *cmd; +- struct vp702x_fe_state *st = fe->demodulator_priv; +- struct vp702x_device_state *dst = st->d->priv; +- +- deb_fe("%s\n",__func__); +- +- if (m->msg_len > 4) +- return -EINVAL; +- +- mutex_lock(&dst->buf_mutex); +- +- cmd = dst->buf; +- cmd[1] = SET_DISEQC_CMD; +- cmd[2] = m->msg_len; +- memcpy(&cmd[3], m->msg, m->msg_len); +- cmd[7] = vp702x_chksum(cmd, 0, 7); +- +- vp702x_usb_inout_op(st->d, cmd, 8, cmd, 10, 100); +- +- if (cmd[2] == 0 && cmd[3] == 0) +- deb_fe("diseqc cmd failed.\n"); +- else +- deb_fe("diseqc cmd succeeded.\n"); +- +- mutex_unlock(&dst->buf_mutex); +- +- return 0; +-} +- +-static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +-{ +- deb_fe("%s\n",__func__); +- return 0; +-} +- +-static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- struct vp702x_device_state *dst = st->d->priv; +- u8 *buf; +- +- deb_fe("%s\n",__func__); +- +- st->tone_mode = tone; +- +- if (tone == SEC_TONE_ON) +- st->lnb_buf[2] = 0x02; +- else +- st->lnb_buf[2] = 0x00; +- +- st->lnb_buf[7] = vp702x_chksum(st->lnb_buf, 0, 7); +- +- mutex_lock(&dst->buf_mutex); +- +- buf = dst->buf; +- memcpy(buf, st->lnb_buf, 8); +- +- vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100); +- if (buf[2] == 0 && buf[3] == 0) +- deb_fe("set_tone cmd failed.\n"); +- else +- deb_fe("set_tone cmd succeeded.\n"); +- +- mutex_unlock(&dst->buf_mutex); +- +- return 0; +-} +- +-static int vp702x_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t +- voltage) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- struct vp702x_device_state *dst = st->d->priv; +- u8 *buf; +- deb_fe("%s\n",__func__); +- +- st->voltage = voltage; +- +- if (voltage != SEC_VOLTAGE_OFF) +- st->lnb_buf[4] = 0x01; +- else +- st->lnb_buf[4] = 0x00; +- +- st->lnb_buf[7] = vp702x_chksum(st->lnb_buf, 0, 7); +- +- mutex_lock(&dst->buf_mutex); +- +- buf = dst->buf; +- memcpy(buf, st->lnb_buf, 8); +- +- vp702x_usb_inout_op(st->d, buf, 8, buf, 10, 100); +- if (buf[2] == 0 && buf[3] == 0) +- deb_fe("set_voltage cmd failed.\n"); +- else +- deb_fe("set_voltage cmd succeeded.\n"); +- +- mutex_unlock(&dst->buf_mutex); +- return 0; +-} +- +-static void vp702x_fe_release(struct dvb_frontend* fe) +-{ +- struct vp702x_fe_state *st = fe->demodulator_priv; +- kfree(st); +-} +- +-static struct dvb_frontend_ops vp702x_fe_ops; +- +-struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d) +-{ +- struct vp702x_fe_state *s = kzalloc(sizeof(struct vp702x_fe_state), GFP_KERNEL); +- if (s == NULL) +- goto error; +- +- s->d = d; +- +- memcpy(&s->fe.ops,&vp702x_fe_ops,sizeof(struct dvb_frontend_ops)); +- s->fe.demodulator_priv = s; +- +- s->lnb_buf[1] = SET_LNB_POWER; +- s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */ +- +- return &s->fe; +-error: +- return NULL; +-} +- +- +-static struct dvb_frontend_ops vp702x_fe_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1000, /* kHz for QPSK frontends */ +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, /* ppm */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_QPSK | +- FE_CAN_FEC_AUTO +- }, +- .release = vp702x_fe_release, +- +- .init = vp702x_fe_init, +- .sleep = vp702x_fe_sleep, +- +- .set_frontend = vp702x_fe_set_frontend, +- .get_tune_settings = vp702x_fe_get_tune_settings, +- +- .read_status = vp702x_fe_read_status, +- .read_ber = vp702x_fe_read_ber, +- .read_signal_strength = vp702x_fe_read_signal_strength, +- .read_snr = vp702x_fe_read_snr, +- .read_ucblocks = vp702x_fe_read_unc_blocks, +- +- .diseqc_send_master_cmd = vp702x_fe_send_diseqc_msg, +- .diseqc_send_burst = vp702x_fe_send_diseqc_burst, +- .set_tone = vp702x_fe_set_tone, +- .set_voltage = vp702x_fe_set_voltage, +-}; +diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c +deleted file mode 100644 +index 07c673a..0000000 +--- a/drivers/media/dvb/dvb-usb/vp702x.c ++++ /dev/null +@@ -1,444 +0,0 @@ +-/* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S +- * receiver. +- * +- * Copyright (C) 2005 Ralph Metzler +- * Metzler Brothers Systementwicklung GbR +- * +- * Copyright (C) 2005 Patrick Boettcher +- * +- * Thanks to Twinhan who kindly provided hardware and information. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "vp702x.h" +-#include +- +-/* debug */ +-int dvb_usb_vp702x_debug; +-module_param_named(debug,dvb_usb_vp702x_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct vp702x_adapter_state { +- int pid_filter_count; +- int pid_filter_can_bypass; +- u8 pid_filter_state; +-}; +- +-static int vp702x_usb_in_op_unlocked(struct dvb_usb_device *d, u8 req, +- u16 value, u16 index, u8 *b, int blen) +-{ +- int ret; +- +- ret = usb_control_msg(d->udev, +- usb_rcvctrlpipe(d->udev, 0), +- req, +- USB_TYPE_VENDOR | USB_DIR_IN, +- value, index, b, blen, +- 2000); +- +- if (ret < 0) { +- warn("usb in operation failed. (%d)", ret); +- ret = -EIO; +- } else +- ret = 0; +- +- +- deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index); +- debug_dump(b,blen,deb_xfer); +- +- return ret; +-} +- +-int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, +- u16 index, u8 *b, int blen) +-{ +- int ret; +- +- mutex_lock(&d->usb_mutex); +- ret = vp702x_usb_in_op_unlocked(d, req, value, index, b, blen); +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-int vp702x_usb_out_op_unlocked(struct dvb_usb_device *d, u8 req, u16 value, +- u16 index, u8 *b, int blen) +-{ +- int ret; +- deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ",req,value,index); +- debug_dump(b,blen,deb_xfer); +- +- if ((ret = usb_control_msg(d->udev, +- usb_sndctrlpipe(d->udev,0), +- req, +- USB_TYPE_VENDOR | USB_DIR_OUT, +- value,index,b,blen, +- 2000)) != blen) { +- warn("usb out operation failed. (%d)",ret); +- return -EIO; +- } else +- return 0; +-} +- +-int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, +- u16 index, u8 *b, int blen) +-{ +- int ret; +- +- mutex_lock(&d->usb_mutex); +- ret = vp702x_usb_out_op_unlocked(d, req, value, index, b, blen); +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec) +-{ +- int ret; +- +- if ((ret = mutex_lock_interruptible(&d->usb_mutex))) +- return ret; +- +- ret = vp702x_usb_out_op_unlocked(d, REQUEST_OUT, 0, 0, o, olen); +- msleep(msec); +- ret = vp702x_usb_in_op_unlocked(d, REQUEST_IN, 0, 0, i, ilen); +- +- mutex_unlock(&d->usb_mutex); +- return ret; +-} +- +-static int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, +- int olen, u8 *i, int ilen, int msec) +-{ +- struct vp702x_device_state *st = d->priv; +- int ret = 0; +- u8 *buf; +- int buflen = max(olen + 2, ilen + 1); +- +- ret = mutex_lock_interruptible(&st->buf_mutex); +- if (ret < 0) +- return ret; +- +- if (buflen > st->buf_len) { +- buf = kmalloc(buflen, GFP_KERNEL); +- if (!buf) { +- mutex_unlock(&st->buf_mutex); +- return -ENOMEM; +- } +- info("successfully reallocated a bigger buffer"); +- kfree(st->buf); +- st->buf = buf; +- st->buf_len = buflen; +- } else { +- buf = st->buf; +- } +- +- buf[0] = 0x00; +- buf[1] = cmd; +- memcpy(&buf[2], o, olen); +- +- ret = vp702x_usb_inout_op(d, buf, olen+2, buf, ilen+1, msec); +- +- if (ret == 0) +- memcpy(i, &buf[1], ilen); +- mutex_unlock(&st->buf_mutex); +- +- return ret; +-} +- +-static int vp702x_set_pld_mode(struct dvb_usb_adapter *adap, u8 bypass) +-{ +- int ret; +- struct vp702x_device_state *st = adap->dev->priv; +- u8 *buf; +- +- mutex_lock(&st->buf_mutex); +- +- buf = st->buf; +- memset(buf, 0, 16); +- +- ret = vp702x_usb_in_op(adap->dev, 0xe0, (bypass << 8) | 0x0e, +- 0, buf, 16); +- mutex_unlock(&st->buf_mutex); +- return ret; +-} +- +-static int vp702x_set_pld_state(struct dvb_usb_adapter *adap, u8 state) +-{ +- int ret; +- struct vp702x_device_state *st = adap->dev->priv; +- u8 *buf; +- +- mutex_lock(&st->buf_mutex); +- +- buf = st->buf; +- memset(buf, 0, 16); +- ret = vp702x_usb_in_op(adap->dev, 0xe0, (state << 8) | 0x0f, +- 0, buf, 16); +- +- mutex_unlock(&st->buf_mutex); +- +- return ret; +-} +- +-static int vp702x_set_pid(struct dvb_usb_adapter *adap, u16 pid, u8 id, int onoff) +-{ +- struct vp702x_adapter_state *st = adap->priv; +- struct vp702x_device_state *dst = adap->dev->priv; +- u8 *buf; +- +- if (onoff) +- st->pid_filter_state |= (1 << id); +- else { +- st->pid_filter_state &= ~(1 << id); +- pid = 0xffff; +- } +- +- id = 0x10 + id*2; +- +- vp702x_set_pld_state(adap, st->pid_filter_state); +- +- mutex_lock(&dst->buf_mutex); +- +- buf = dst->buf; +- memset(buf, 0, 16); +- vp702x_usb_in_op(adap->dev, 0xe0, (((pid >> 8) & 0xff) << 8) | (id), 0, buf, 16); +- vp702x_usb_in_op(adap->dev, 0xe0, (((pid ) & 0xff) << 8) | (id+1), 0, buf, 16); +- +- mutex_unlock(&dst->buf_mutex); +- +- return 0; +-} +- +- +-static int vp702x_init_pid_filter(struct dvb_usb_adapter *adap) +-{ +- struct vp702x_adapter_state *st = adap->priv; +- struct vp702x_device_state *dst = adap->dev->priv; +- int i; +- u8 *b; +- +- st->pid_filter_count = 8; +- st->pid_filter_can_bypass = 1; +- st->pid_filter_state = 0x00; +- +- vp702x_set_pld_mode(adap, 1); /* bypass */ +- +- for (i = 0; i < st->pid_filter_count; i++) +- vp702x_set_pid(adap, 0xffff, i, 1); +- +- mutex_lock(&dst->buf_mutex); +- b = dst->buf; +- memset(b, 0, 10); +- vp702x_usb_in_op(adap->dev, 0xb5, 3, 0, b, 10); +- vp702x_usb_in_op(adap->dev, 0xb5, 0, 0, b, 10); +- vp702x_usb_in_op(adap->dev, 0xb5, 1, 0, b, 10); +- mutex_unlock(&dst->buf_mutex); +- /*vp702x_set_pld_mode(d, 0); // filter */ +- +- return 0; +-} +- +-static int vp702x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +-{ +- return 0; +-} +- +-/* keys for the enclosed remote control */ +-static struct rc_map_table rc_map_vp702x_table[] = { +- { 0x0001, KEY_1 }, +- { 0x0002, KEY_2 }, +-}; +- +-/* remote control stuff (does not work with my box) */ +-static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- u8 *key; +- int i; +- +-/* remove the following return to enabled remote querying */ +- return 0; +- +- key = kmalloc(10, GFP_KERNEL); +- if (!key) +- return -ENOMEM; +- +- vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10); +- +- deb_rc("remote query key: %x %d\n",key[1],key[1]); +- +- if (key[1] == 0x44) { +- *state = REMOTE_NO_KEY_PRESSED; +- kfree(key); +- return 0; +- } +- +- for (i = 0; i < ARRAY_SIZE(rc_map_vp702x_table); i++) +- if (rc5_custom(&rc_map_vp702x_table[i]) == key[1]) { +- *state = REMOTE_KEY_PRESSED; +- *event = rc_map_vp702x_table[i].keycode; +- break; +- } +- kfree(key); +- return 0; +-} +- +- +-static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +-{ +- u8 i, *buf; +- struct vp702x_device_state *st = d->priv; +- +- mutex_lock(&st->buf_mutex); +- buf = st->buf; +- for (i = 6; i < 12; i++) +- vp702x_usb_in_op(d, READ_EEPROM_REQ, i, 1, &buf[i - 6], 1); +- +- memcpy(mac, buf, 6); +- mutex_unlock(&st->buf_mutex); +- return 0; +-} +- +-static int vp702x_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- u8 buf[10] = { 0 }; +- +- vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 0, 7, NULL, 0); +- +- if (vp702x_usb_inout_cmd(adap->dev, GET_SYSTEM_STRING, NULL, 0, +- buf, 10, 10)) +- return -EIO; +- +- buf[9] = '\0'; +- info("system string: %s",&buf[1]); +- +- vp702x_init_pid_filter(adap); +- +- adap->fe_adap[0].fe = vp702x_fe_attach(adap->dev); +- vp702x_usb_out_op(adap->dev, SET_TUNER_POWER_REQ, 1, 7, NULL, 0); +- +- return 0; +-} +- +-static struct dvb_usb_device_properties vp702x_properties; +- +-static int vp702x_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct dvb_usb_device *d; +- struct vp702x_device_state *st; +- int ret; +- +- ret = dvb_usb_device_init(intf, &vp702x_properties, +- THIS_MODULE, &d, adapter_nr); +- if (ret) +- goto out; +- +- st = d->priv; +- st->buf_len = 16; +- st->buf = kmalloc(st->buf_len, GFP_KERNEL); +- if (!st->buf) { +- ret = -ENOMEM; +- dvb_usb_device_exit(intf); +- goto out; +- } +- mutex_init(&st->buf_mutex); +- +-out: +- return ret; +- +-} +- +-static void vp702x_usb_disconnect(struct usb_interface *intf) +-{ +- struct dvb_usb_device *d = usb_get_intfdata(intf); +- struct vp702x_device_state *st = d->priv; +- mutex_lock(&st->buf_mutex); +- kfree(st->buf); +- mutex_unlock(&st->buf_mutex); +- dvb_usb_device_exit(intf); +-} +- +-static struct usb_device_id vp702x_usb_table [] = { +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) }, +-// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) }, +-// { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) }, +- { 0 }, +-}; +-MODULE_DEVICE_TABLE(usb, vp702x_usb_table); +- +-static struct dvb_usb_device_properties vp702x_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-vp702x-02.fw", +- .no_reconnect = 1, +- +- .size_of_priv = sizeof(struct vp702x_device_state), +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, +- +- .streaming_ctrl = vp702x_streaming_ctrl, +- .frontend_attach = vp702x_frontend_attach, +- +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 10, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- .size_of_priv = sizeof(struct vp702x_adapter_state), +- } +- }, +- .read_mac_address = vp702x_read_mac_addr, +- +- .rc.legacy = { +- .rc_map_table = rc_map_vp702x_table, +- .rc_map_size = ARRAY_SIZE(rc_map_vp702x_table), +- .rc_interval = 400, +- .rc_query = vp702x_rc_query, +- }, +- +- .num_device_descs = 1, +- .devices = { +- { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)", +- .cold_ids = { &vp702x_usb_table[0], NULL }, +- .warm_ids = { NULL }, +- }, +-/* { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)", +- .cold_ids = { &vp702x_usb_table[2], NULL }, +- .warm_ids = { &vp702x_usb_table[3], NULL }, +- }, +-*/ { NULL }, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver vp702x_usb_driver = { +- .name = "dvb_usb_vp702x", +- .probe = vp702x_usb_probe, +- .disconnect = vp702x_usb_disconnect, +- .id_table = vp702x_usb_table, +-}; +- +-module_usb_driver(vp702x_usb_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/vp702x.h b/drivers/media/dvb/dvb-usb/vp702x.h +deleted file mode 100644 +index 20b9005..0000000 +--- a/drivers/media/dvb/dvb-usb/vp702x.h ++++ /dev/null +@@ -1,113 +0,0 @@ +-#ifndef _DVB_USB_VP7021_H_ +-#define _DVB_USB_VP7021_H_ +- +-#define DVB_USB_LOG_PREFIX "vp702x" +-#include "dvb-usb.h" +- +-extern int dvb_usb_vp702x_debug; +-#define deb_info(args...) dprintk(dvb_usb_vp702x_debug,0x01,args) +-#define deb_xfer(args...) dprintk(dvb_usb_vp702x_debug,0x02,args) +-#define deb_rc(args...) dprintk(dvb_usb_vp702x_debug,0x04,args) +-#define deb_fe(args...) dprintk(dvb_usb_vp702x_debug,0x08,args) +- +-/* commands are read and written with USB control messages */ +- +-/* consecutive read/write operation */ +-#define REQUEST_OUT 0xB2 +-#define REQUEST_IN 0xB3 +- +-/* the out-buffer of these consecutive operations contain sub-commands when b[0] = 0 +- * request: 0xB2; i: 0; v: 0; b[0] = 0, b[1] = subcmd, additional buffer +- * the returning buffer looks as follows +- * request: 0xB3; i: 0; v: 0; b[0] = 0xB3, additional buffer */ +- +-#define GET_TUNER_STATUS 0x05 +-/* additional in buffer: +- * 0 1 2 3 4 5 6 7 8 +- * N/A N/A 0x05 signal-quality N/A N/A signal-strength lock==0 N/A */ +- +-#define GET_SYSTEM_STRING 0x06 +-/* additional in buffer: +- * 0 1 2 3 4 5 6 7 8 +- * N/A 'U' 'S' 'B' '7' '0' '2' 'X' N/A */ +- +-#define SET_DISEQC_CMD 0x08 +-/* additional out buffer: +- * 0 1 2 3 4 +- * len X1 X2 X3 X4 +- * additional in buffer: +- * 0 1 2 +- * N/A 0 0 b[1] == b[2] == 0 -> success, failure otherwise */ +- +-#define SET_LNB_POWER 0x09 +-/* additional out buffer: +- * 0 1 2 +- * 0x00 0xff 1 = on, 0 = off +- * additional in buffer: +- * 0 1 2 +- * N/A 0 0 b[1] == b[2] == 0 -> success failure otherwise */ +- +-#define GET_MAC_ADDRESS 0x0A +-/* #define GET_MAC_ADDRESS 0x0B */ +-/* additional in buffer: +- * 0 1 2 3 4 5 6 7 8 +- * N/A N/A 0x0A or 0x0B MAC0 MAC1 MAC2 MAC3 MAC4 MAC5 */ +- +-#define SET_PID_FILTER 0x11 +-/* additional in buffer: +- * 0 1 ... 14 15 16 +- * PID0_MSB PID0_LSB ... PID7_MSB PID7_LSB PID_active (bits) */ +- +-/* request: 0xB2; i: 0; v: 0; +- * b[0] != 0 -> tune and lock a channel +- * 0 1 2 3 4 5 6 7 +- * freq0 freq1 divstep srate0 srate1 srate2 flag chksum +- */ +- +-/* one direction requests */ +-#define READ_REMOTE_REQ 0xB4 +-/* IN i: 0; v: 0; b[0] == request, b[1] == key */ +- +-#define READ_PID_NUMBER_REQ 0xB5 +-/* IN i: 0; v: 0; b[0] == request, b[1] == 0, b[2] = pid number */ +- +-#define WRITE_EEPROM_REQ 0xB6 +-/* OUT i: offset; v: value to write; no extra buffer */ +- +-#define READ_EEPROM_REQ 0xB7 +-/* IN i: bufferlen; v: offset; buffer with bufferlen bytes */ +- +-#define READ_STATUS 0xB8 +-/* IN i: 0; v: 0; bufferlen 10 */ +- +-#define READ_TUNER_REG_REQ 0xB9 +-/* IN i: 0; v: register; b[0] = value */ +- +-#define READ_FX2_REG_REQ 0xBA +-/* IN i: offset; v: 0; b[0] = value */ +- +-#define WRITE_FX2_REG_REQ 0xBB +-/* OUT i: offset; v: value to write; 1 byte extra buffer */ +- +-#define SET_TUNER_POWER_REQ 0xBC +-/* IN i: 0 = power off, 1 = power on */ +- +-#define WRITE_TUNER_REG_REQ 0xBD +-/* IN i: register, v: value to write, no extra buffer */ +- +-#define RESET_TUNER 0xBE +-/* IN i: 0, v: 0, no extra buffer */ +- +-struct vp702x_device_state { +- struct mutex buf_mutex; +- int buf_len; +- u8 *buf; +-}; +- +- +-extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d); +- +-extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec); +-extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen); +- +-#endif +diff --git a/drivers/media/dvb/dvb-usb/vp7045-fe.c b/drivers/media/dvb/dvb-usb/vp7045-fe.c +deleted file mode 100644 +index b8825b1..0000000 +--- a/drivers/media/dvb/dvb-usb/vp7045-fe.c ++++ /dev/null +@@ -1,190 +0,0 @@ +-/* DVB frontend part of the Linux driver for TwinhanDTV Alpha/MagicBoxII USB2.0 +- * DVB-T receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * Thanks to Twinhan who kindly provided hardware and information. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * +- */ +-#include "vp7045.h" +- +-/* It is a Zarlink MT352 within a Samsung Tuner (DNOS404ZH102A) - 040929 - AAT +- * +- * Programming is hidden inside the firmware, so set_frontend is very easy. +- * Even though there is a Firmware command that one can use to access the demod +- * via its registers. This is used for status information. +- */ +- +-struct vp7045_fe_state { +- struct dvb_frontend fe; +- struct dvb_usb_device *d; +-}; +- +-static int vp7045_fe_read_status(struct dvb_frontend* fe, fe_status_t *status) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- u8 s0 = vp7045_read_reg(state->d,0x00), +- s1 = vp7045_read_reg(state->d,0x01), +- s3 = vp7045_read_reg(state->d,0x03); +- +- *status = 0; +- if (s0 & (1 << 4)) +- *status |= FE_HAS_CARRIER; +- if (s0 & (1 << 1)) +- *status |= FE_HAS_VITERBI; +- if (s0 & (1 << 5)) +- *status |= FE_HAS_LOCK; +- if (s1 & (1 << 1)) +- *status |= FE_HAS_SYNC; +- if (s3 & (1 << 6)) +- *status |= FE_HAS_SIGNAL; +- +- if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != +- (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) +- *status &= ~FE_HAS_LOCK; +- +- return 0; +-} +- +-static int vp7045_fe_read_ber(struct dvb_frontend* fe, u32 *ber) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- *ber = (vp7045_read_reg(state->d, 0x0D) << 16) | +- (vp7045_read_reg(state->d, 0x0E) << 8) | +- vp7045_read_reg(state->d, 0x0F); +- return 0; +-} +- +-static int vp7045_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- *unc = (vp7045_read_reg(state->d, 0x10) << 8) | +- vp7045_read_reg(state->d, 0x11); +- return 0; +-} +- +-static int vp7045_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- u16 signal = (vp7045_read_reg(state->d, 0x14) << 8) | +- vp7045_read_reg(state->d, 0x15); +- +- *strength = ~signal; +- return 0; +-} +- +-static int vp7045_fe_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- u8 _snr = vp7045_read_reg(state->d, 0x09); +- *snr = (_snr << 8) | _snr; +- return 0; +-} +- +-static int vp7045_fe_init(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int vp7045_fe_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int vp7045_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 800; +- return 0; +-} +- +-static int vp7045_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct vp7045_fe_state *state = fe->demodulator_priv; +- u8 buf[5]; +- u32 freq = fep->frequency / 1000; +- +- buf[0] = (freq >> 16) & 0xff; +- buf[1] = (freq >> 8) & 0xff; +- buf[2] = freq & 0xff; +- buf[3] = 0; +- +- switch (fep->bandwidth_hz) { +- case 8000000: +- buf[4] = 8; +- break; +- case 7000000: +- buf[4] = 7; +- break; +- case 6000000: +- buf[4] = 6; +- break; +- default: +- return -EINVAL; +- } +- +- vp7045_usb_op(state->d,LOCK_TUNER_COMMAND,buf,5,NULL,0,200); +- return 0; +-} +- +-static void vp7045_fe_release(struct dvb_frontend* fe) +-{ +- struct vp7045_fe_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops vp7045_fe_ops; +- +-struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d) +-{ +- struct vp7045_fe_state *s = kzalloc(sizeof(struct vp7045_fe_state), GFP_KERNEL); +- if (s == NULL) +- goto error; +- +- s->d = d; +- memcpy(&s->fe.ops, &vp7045_fe_ops, sizeof(struct dvb_frontend_ops)); +- s->fe.demodulator_priv = s; +- +- return &s->fe; +-error: +- return NULL; +-} +- +- +-static struct dvb_frontend_ops vp7045_fe_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Twinhan VP7045/46 USB DVB-T", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 1000, +- .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 = vp7045_fe_release, +- +- .init = vp7045_fe_init, +- .sleep = vp7045_fe_sleep, +- +- .set_frontend = vp7045_fe_set_frontend, +- .get_tune_settings = vp7045_fe_get_tune_settings, +- +- .read_status = vp7045_fe_read_status, +- .read_ber = vp7045_fe_read_ber, +- .read_signal_strength = vp7045_fe_read_signal_strength, +- .read_snr = vp7045_fe_read_snr, +- .read_ucblocks = vp7045_fe_read_unc_blocks, +-}; +diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c +deleted file mode 100644 +index d750724..0000000 +--- a/drivers/media/dvb/dvb-usb/vp7045.c ++++ /dev/null +@@ -1,302 +0,0 @@ +-/* DVB USB compliant Linux driver for the +- * - TwinhanDTV Alpha/MagicBoxII USB2.0 DVB-T receiver +- * - DigitalNow TinyUSB2 DVB-t receiver +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * Thanks to Twinhan who kindly provided hardware and information. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#include "vp7045.h" +- +-/* debug */ +-static int dvb_usb_vp7045_debug; +-module_param_named(debug,dvb_usb_vp7045_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define deb_info(args...) dprintk(dvb_usb_vp7045_debug,0x01,args) +-#define deb_xfer(args...) dprintk(dvb_usb_vp7045_debug,0x02,args) +-#define deb_rc(args...) dprintk(dvb_usb_vp7045_debug,0x04,args) +- +-int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen, int msec) +-{ +- int ret = 0; +- u8 *buf = d->priv; +- +- buf[0] = cmd; +- +- if (outlen > 19) +- outlen = 19; +- +- if (inlen > 11) +- inlen = 11; +- +- ret = mutex_lock_interruptible(&d->usb_mutex); +- if (ret) +- return ret; +- +- if (out != NULL && outlen > 0) +- memcpy(&buf[1], out, outlen); +- +- deb_xfer("out buffer: "); +- debug_dump(buf, outlen+1, deb_xfer); +- +- +- if (usb_control_msg(d->udev, +- usb_sndctrlpipe(d->udev,0), +- TH_COMMAND_OUT, USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, +- buf, 20, 2000) != 20) { +- err("USB control message 'out' went wrong."); +- ret = -EIO; +- goto unlock; +- } +- +- msleep(msec); +- +- if (usb_control_msg(d->udev, +- usb_rcvctrlpipe(d->udev,0), +- TH_COMMAND_IN, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, +- buf, 12, 2000) != 12) { +- err("USB control message 'in' went wrong."); +- ret = -EIO; +- goto unlock; +- } +- +- deb_xfer("in buffer: "); +- debug_dump(buf, 12, deb_xfer); +- +- if (in != NULL && inlen > 0) +- memcpy(in, &buf[1], inlen); +- +-unlock: +- mutex_unlock(&d->usb_mutex); +- +- return ret; +-} +- +-u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg) +-{ +- u8 obuf[2] = { 0 },v; +- obuf[1] = reg; +- +- vp7045_usb_op(d,TUNER_REG_READ,obuf,2,&v,1,30); +- +- return v; +-} +- +-static int vp7045_power_ctrl(struct dvb_usb_device *d, int onoff) +-{ +- u8 v = onoff; +- return vp7045_usb_op(d,SET_TUNER_POWER,&v,1,NULL,0,150); +-} +- +-/* remote control stuff */ +- +-/* The keymapping struct. Somehow this should be loaded to the driver, but +- * currently it is hardcoded. */ +-static struct rc_map_table rc_map_vp7045_table[] = { +- { 0x0016, KEY_POWER }, +- { 0x0010, KEY_MUTE }, +- { 0x0003, KEY_1 }, +- { 0x0001, KEY_2 }, +- { 0x0006, KEY_3 }, +- { 0x0009, KEY_4 }, +- { 0x001d, KEY_5 }, +- { 0x001f, KEY_6 }, +- { 0x000d, KEY_7 }, +- { 0x0019, KEY_8 }, +- { 0x001b, KEY_9 }, +- { 0x0015, KEY_0 }, +- { 0x0005, KEY_CHANNELUP }, +- { 0x0002, KEY_CHANNELDOWN }, +- { 0x001e, KEY_VOLUMEUP }, +- { 0x000a, KEY_VOLUMEDOWN }, +- { 0x0011, KEY_RECORD }, +- { 0x0017, KEY_FAVORITES }, /* Heart symbol - Channel list. */ +- { 0x0014, KEY_PLAY }, +- { 0x001a, KEY_STOP }, +- { 0x0040, KEY_REWIND }, +- { 0x0012, KEY_FASTFORWARD }, +- { 0x000e, KEY_PREVIOUS }, /* Recall - Previous channel. */ +- { 0x004c, KEY_PAUSE }, +- { 0x004d, KEY_SCREEN }, /* Full screen mode. */ +- { 0x0054, KEY_AUDIO }, /* MTS - Switch to secondary audio. */ +- { 0x000c, KEY_CANCEL }, /* Cancel */ +- { 0x001c, KEY_EPG }, /* EPG */ +- { 0x0000, KEY_TAB }, /* Tab */ +- { 0x0048, KEY_INFO }, /* Preview */ +- { 0x0004, KEY_LIST }, /* RecordList */ +- { 0x000f, KEY_TEXT }, /* Teletext */ +- { 0x0041, KEY_PREVIOUSSONG }, +- { 0x0042, KEY_NEXTSONG }, +- { 0x004b, KEY_UP }, +- { 0x0051, KEY_DOWN }, +- { 0x004e, KEY_LEFT }, +- { 0x0052, KEY_RIGHT }, +- { 0x004f, KEY_ENTER }, +- { 0x0013, KEY_CANCEL }, +- { 0x004a, KEY_CLEAR }, +- { 0x0054, KEY_PRINT }, /* Capture */ +- { 0x0043, KEY_SUBTITLE }, /* Subtitle/CC */ +- { 0x0008, KEY_VIDEO }, /* A/V */ +- { 0x0007, KEY_SLEEP }, /* Hibernate */ +- { 0x0045, KEY_ZOOM }, /* Zoom+ */ +- { 0x0018, KEY_RED}, +- { 0x0053, KEY_GREEN}, +- { 0x005e, KEY_YELLOW}, +- { 0x005f, KEY_BLUE} +-}; +- +-static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +-{ +- u8 key; +- int i; +- vp7045_usb_op(d,RC_VAL_READ,NULL,0,&key,1,20); +- +- deb_rc("remote query key: %x %d\n",key,key); +- +- if (key == 0x44) { +- *state = REMOTE_NO_KEY_PRESSED; +- return 0; +- } +- +- for (i = 0; i < ARRAY_SIZE(rc_map_vp7045_table); i++) +- if (rc5_data(&rc_map_vp7045_table[i]) == key) { +- *state = REMOTE_KEY_PRESSED; +- *event = rc_map_vp7045_table[i].keycode; +- break; +- } +- return 0; +-} +- +-static int vp7045_read_eeprom(struct dvb_usb_device *d,u8 *buf, int len, int offset) +-{ +- int i = 0; +- u8 v,br[2]; +- for (i=0; i < len; i++) { +- v = offset + i; +- vp7045_usb_op(d,GET_EE_VALUE,&v,1,br,2,5); +- buf[i] = br[1]; +- } +- deb_info("VP7045 EEPROM read (offs: %d, len: %d) : ",offset, i); +- debug_dump(buf,i,deb_info); +- return 0; +-} +- +-static int vp7045_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +-{ +- return vp7045_read_eeprom(d,mac, 6, MAC_0_ADDR); +-} +- +-static int vp7045_frontend_attach(struct dvb_usb_adapter *adap) +-{ +- u8 buf[255] = { 0 }; +- +- vp7045_usb_op(adap->dev,VENDOR_STRING_READ,NULL,0,buf,20,0); +- buf[10] = '\0'; +- deb_info("firmware says: %s ",buf); +- +- vp7045_usb_op(adap->dev,PRODUCT_STRING_READ,NULL,0,buf,20,0); +- buf[10] = '\0'; +- deb_info("%s ",buf); +- +- vp7045_usb_op(adap->dev,FW_VERSION_READ,NULL,0,buf,20,0); +- buf[10] = '\0'; +- deb_info("v%s\n",buf); +- +-/* Dump the EEPROM */ +-/* vp7045_read_eeprom(d,buf, 255, FX2_ID_ADDR); */ +- +- adap->fe_adap[0].fe = vp7045_fe_attach(adap->dev); +- +- return 0; +-} +- +-static struct dvb_usb_device_properties vp7045_properties; +- +-static int vp7045_usb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- return dvb_usb_device_init(intf, &vp7045_properties, +- THIS_MODULE, NULL, adapter_nr); +-} +- +-static struct usb_device_id vp7045_usb_table [] = { +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_COLD) }, +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7045_WARM) }, +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_COLD) }, +- { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_DNTV_TINYUSB2_WARM) }, +- { 0 }, +-}; +-MODULE_DEVICE_TABLE(usb, vp7045_usb_table); +- +-static struct dvb_usb_device_properties vp7045_properties = { +- .usb_ctrl = CYPRESS_FX2, +- .firmware = "dvb-usb-vp7045-01.fw", +- .size_of_priv = 20, +- +- .num_adapters = 1, +- .adapter = { +- { +- .num_frontends = 1, +- .fe = {{ +- .frontend_attach = vp7045_frontend_attach, +- /* parameter for the MPEG2-data transfer */ +- .stream = { +- .type = USB_BULK, +- .count = 7, +- .endpoint = 0x02, +- .u = { +- .bulk = { +- .buffersize = 4096, +- } +- } +- }, +- }}, +- } +- }, +- .power_ctrl = vp7045_power_ctrl, +- .read_mac_address = vp7045_read_mac_addr, +- +- .rc.legacy = { +- .rc_interval = 400, +- .rc_map_table = rc_map_vp7045_table, +- .rc_map_size = ARRAY_SIZE(rc_map_vp7045_table), +- .rc_query = vp7045_rc_query, +- }, +- +- .num_device_descs = 2, +- .devices = { +- { .name = "Twinhan USB2.0 DVB-T receiver (TwinhanDTV Alpha/MagicBox II)", +- .cold_ids = { &vp7045_usb_table[0], NULL }, +- .warm_ids = { &vp7045_usb_table[1], NULL }, +- }, +- { .name = "DigitalNow TinyUSB 2 DVB-t Receiver", +- .cold_ids = { &vp7045_usb_table[2], NULL }, +- .warm_ids = { &vp7045_usb_table[3], NULL }, +- }, +- { NULL }, +- } +-}; +- +-/* usb specific object needed to register this driver with the usb subsystem */ +-static struct usb_driver vp7045_usb_driver = { +- .name = "dvb_usb_vp7045", +- .probe = vp7045_usb_probe, +- .disconnect = dvb_usb_device_exit, +- .id_table = vp7045_usb_table, +-}; +- +-module_usb_driver(vp7045_usb_driver); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for Twinhan MagicBox/Alpha and DNTV tinyUSB2 DVB-T USB2.0"); +-MODULE_VERSION("1.0"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/dvb-usb/vp7045.h b/drivers/media/dvb/dvb-usb/vp7045.h +deleted file mode 100644 +index cf5ec46..0000000 +--- a/drivers/media/dvb/dvb-usb/vp7045.h ++++ /dev/null +@@ -1,70 +0,0 @@ +-/* Common header-file of the Linux driver for the TwinhanDTV Alpha/MagicBoxII +- * USB2.0 DVB-T receiver. +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * Thanks to Twinhan who kindly provided hardware and information. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation, version 2. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- */ +-#ifndef _DVB_USB_VP7045_H_ +-#define _DVB_USB_VP7045_H_ +- +-#define DVB_USB_LOG_PREFIX "vp7045" +-#include "dvb-usb.h" +- +-/* vp7045 commands */ +- +-/* Twinhan Vendor requests */ +-#define TH_COMMAND_IN 0xC0 +-#define TH_COMMAND_OUT 0xC1 +- +-/* command bytes */ +-#define TUNER_REG_READ 0x03 +-#define TUNER_REG_WRITE 0x04 +- +-#define RC_VAL_READ 0x05 +- #define RC_NO_KEY 0x44 +- +-#define SET_TUNER_POWER 0x06 +-#define CHECK_TUNER_POWER 0x12 +- #define Tuner_Power_ON 1 +- #define Tuner_Power_OFF 0 +- +-#define GET_USB_SPEED 0x07 +- +-#define LOCK_TUNER_COMMAND 0x09 +- +-#define TUNER_SIGNAL_READ 0x0A +- +-/* FX2 eeprom */ +-#define SET_EE_VALUE 0x10 +-#define GET_EE_VALUE 0x11 +- #define FX2_ID_ADDR 0x00 +- #define VID_MSB_ADDR 0x02 +- #define VID_LSB_ADDR 0x01 +- #define PID_MSB_ADDR 0x04 +- #define PID_LSB_ADDR 0x03 +- #define MAC_0_ADDR 0x07 +- #define MAC_1_ADDR 0x08 +- #define MAC_2_ADDR 0x09 +- #define MAC_3_ADDR 0x0a +- #define MAC_4_ADDR 0x0b +- #define MAC_5_ADDR 0x0c +- +-#define RESET_FX2 0x13 +- +-#define FW_VERSION_READ 0x0B +-#define VENDOR_STRING_READ 0x0C +-#define PRODUCT_STRING_READ 0x0D +-#define FW_BCD_VERSION_READ 0x14 +- +-extern struct dvb_frontend * vp7045_fe_attach(struct dvb_usb_device *d); +-extern int vp7045_usb_op(struct dvb_usb_device *d, u8 cmd, u8 *out, int outlen, u8 *in, int inlen,int msec); +-extern u8 vp7045_read_reg(struct dvb_usb_device *d, u8 reg); +- +-#endif +diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig +deleted file mode 100644 +index f3e9448..0000000 +--- a/drivers/media/dvb/firewire/Kconfig ++++ /dev/null +@@ -1,19 +0,0 @@ +-config DVB_FIREDTV +- tristate "FireDTV and FloppyDTV" +- depends on DVB_CORE && FIREWIRE +- help +- Support for DVB receivers from Digital Everywhere +- which are connected via IEEE 1394 (FireWire). +- +- These devices don't have an MPEG decoder built in, +- so you need an external software decoder to watch TV. +- +- To compile this driver as a module, say M here: +- the module will be called firedtv. +- +-if DVB_FIREDTV +- +-config DVB_FIREDTV_INPUT +- def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) +- +-endif # DVB_FIREDTV +diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile +deleted file mode 100644 +index 357b3aa..0000000 +--- a/drivers/media/dvb/firewire/Makefile ++++ /dev/null +@@ -1,6 +0,0 @@ +-obj-$(CONFIG_DVB_FIREDTV) += firedtv.o +- +-firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o +-firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core +diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c +deleted file mode 100644 +index d1a1a13..0000000 +--- a/drivers/media/dvb/firewire/firedtv-avc.c ++++ /dev/null +@@ -1,1457 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * Copyright (C) 2008 Ben Backx +- * Copyright (C) 2008 Henrik Kurelid +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "firedtv.h" +- +-#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL +- +-#define AVC_CTYPE_CONTROL 0x0 +-#define AVC_CTYPE_STATUS 0x1 +-#define AVC_CTYPE_NOTIFY 0x3 +- +-#define AVC_RESPONSE_ACCEPTED 0x9 +-#define AVC_RESPONSE_STABLE 0xc +-#define AVC_RESPONSE_CHANGED 0xd +-#define AVC_RESPONSE_INTERIM 0xf +- +-#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3) +-#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3) +- +-#define AVC_OPCODE_VENDOR 0x00 +-#define AVC_OPCODE_READ_DESCRIPTOR 0x09 +-#define AVC_OPCODE_DSIT 0xc8 +-#define AVC_OPCODE_DSD 0xcb +- +-#define DESCRIPTOR_TUNER_STATUS 0x80 +-#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 +- +-#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */ +-#define SFE_VENDOR_DE_COMPANYID_1 0x12 +-#define SFE_VENDOR_DE_COMPANYID_2 0x87 +- +-#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a +-#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 +-#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */ +- +-#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 +-#define SFE_VENDOR_OPCODE_HOST2CA 0x56 +-#define SFE_VENDOR_OPCODE_CA2HOST 0x57 +-#define SFE_VENDOR_OPCODE_CISTATUS 0x59 +-#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */ +- +-#define SFE_VENDOR_TAG_CA_RESET 0x00 +-#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 +-#define SFE_VENDOR_TAG_CA_PMT 0x02 +-#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 +-#define SFE_VENDOR_TAG_CA_MMI 0x05 +-#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 +- +-#define EN50221_LIST_MANAGEMENT_ONLY 0x03 +-#define EN50221_TAG_APP_INFO 0x9f8021 +-#define EN50221_TAG_CA_INFO 0x9f8031 +- +-struct avc_command_frame { +- u8 ctype; +- u8 subunit; +- u8 opcode; +- u8 operand[509]; +-}; +- +-struct avc_response_frame { +- u8 response; +- u8 subunit; +- u8 opcode; +- u8 operand[509]; +-}; +- +-#define LAST_OPERAND (509 - 1) +- +-static inline void clear_operands(struct avc_command_frame *c, int from, int to) +-{ +- memset(&c->operand[from], 0, to - from + 1); +-} +- +-static void pad_operands(struct avc_command_frame *c, int from) +-{ +- int to = ALIGN(from, 4); +- +- if (from <= to && to <= LAST_OPERAND) +- clear_operands(c, from, to); +-} +- +-#define AVC_DEBUG_READ_DESCRIPTOR 0x0001 +-#define AVC_DEBUG_DSIT 0x0002 +-#define AVC_DEBUG_DSD 0x0004 +-#define AVC_DEBUG_REGISTER_REMOTE_CONTROL 0x0008 +-#define AVC_DEBUG_LNB_CONTROL 0x0010 +-#define AVC_DEBUG_TUNE_QPSK 0x0020 +-#define AVC_DEBUG_TUNE_QPSK2 0x0040 +-#define AVC_DEBUG_HOST2CA 0x0080 +-#define AVC_DEBUG_CA2HOST 0x0100 +-#define AVC_DEBUG_APPLICATION_PMT 0x4000 +-#define AVC_DEBUG_FCP_PAYLOADS 0x8000 +- +-static int avc_debug; +-module_param_named(debug, avc_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Verbose logging (none = 0" +- ", FCP subactions" +- ": READ DESCRIPTOR = " __stringify(AVC_DEBUG_READ_DESCRIPTOR) +- ", DSIT = " __stringify(AVC_DEBUG_DSIT) +- ", REGISTER_REMOTE_CONTROL = " __stringify(AVC_DEBUG_REGISTER_REMOTE_CONTROL) +- ", LNB CONTROL = " __stringify(AVC_DEBUG_LNB_CONTROL) +- ", TUNE QPSK = " __stringify(AVC_DEBUG_TUNE_QPSK) +- ", TUNE QPSK2 = " __stringify(AVC_DEBUG_TUNE_QPSK2) +- ", HOST2CA = " __stringify(AVC_DEBUG_HOST2CA) +- ", CA2HOST = " __stringify(AVC_DEBUG_CA2HOST) +- "; Application sent PMT = " __stringify(AVC_DEBUG_APPLICATION_PMT) +- ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) +- ", or a combination, or all = -1)"); +- +-/* +- * This is a workaround since there is no vendor specific command to retrieve +- * ca_info using AVC. If this parameter is not used, ca_system_id will be +- * filled with application_manufacturer from ca_app_info. +- * Digital Everywhere have said that adding ca_info is on their TODO list. +- */ +-static unsigned int num_fake_ca_system_ids; +-static int fake_ca_system_ids[4] = { -1, -1, -1, -1 }; +-module_param_array(fake_ca_system_ids, int, &num_fake_ca_system_ids, 0644); +-MODULE_PARM_DESC(fake_ca_system_ids, "If your CAM application manufacturer " +- "does not have the same ca_system_id as your CAS, you can " +- "override what ca_system_ids are presented to the " +- "application by setting this field to an array of ids."); +- +-static const char *debug_fcp_ctype(unsigned int ctype) +-{ +- static const char *ctypes[] = { +- [0x0] = "CONTROL", [0x1] = "STATUS", +- [0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY", +- [0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED", +- [0x9] = "ACCEPTED", [0xa] = "REJECTED", +- [0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE", +- [0xd] = "CHANGED", [0xf] = "INTERIM", +- }; +- const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL; +- +- return ret ? ret : "?"; +-} +- +-static const char *debug_fcp_opcode(unsigned int opcode, +- const u8 *data, int length) +-{ +- switch (opcode) { +- case AVC_OPCODE_VENDOR: +- break; +- case AVC_OPCODE_READ_DESCRIPTOR: +- return avc_debug & AVC_DEBUG_READ_DESCRIPTOR ? +- "ReadDescriptor" : NULL; +- case AVC_OPCODE_DSIT: +- return avc_debug & AVC_DEBUG_DSIT ? +- "DirectSelectInfo.Type" : NULL; +- case AVC_OPCODE_DSD: +- return avc_debug & AVC_DEBUG_DSD ? "DirectSelectData" : NULL; +- default: +- return "Unknown"; +- } +- +- if (length < 7 || +- data[3] != SFE_VENDOR_DE_COMPANYID_0 || +- data[4] != SFE_VENDOR_DE_COMPANYID_1 || +- data[5] != SFE_VENDOR_DE_COMPANYID_2) +- return "Vendor/Unknown"; +- +- switch (data[6]) { +- case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: +- return avc_debug & AVC_DEBUG_REGISTER_REMOTE_CONTROL ? +- "RegisterRC" : NULL; +- case SFE_VENDOR_OPCODE_LNB_CONTROL: +- return avc_debug & AVC_DEBUG_LNB_CONTROL ? "LNBControl" : NULL; +- case SFE_VENDOR_OPCODE_TUNE_QPSK: +- return avc_debug & AVC_DEBUG_TUNE_QPSK ? "TuneQPSK" : NULL; +- case SFE_VENDOR_OPCODE_TUNE_QPSK2: +- return avc_debug & AVC_DEBUG_TUNE_QPSK2 ? "TuneQPSK2" : NULL; +- case SFE_VENDOR_OPCODE_HOST2CA: +- return avc_debug & AVC_DEBUG_HOST2CA ? "Host2CA" : NULL; +- case SFE_VENDOR_OPCODE_CA2HOST: +- return avc_debug & AVC_DEBUG_CA2HOST ? "CA2Host" : NULL; +- } +- return "Vendor/Unknown"; +-} +- +-static void debug_fcp(const u8 *data, int length) +-{ +- unsigned int subunit_type, subunit_id, opcode; +- const char *op, *prefix; +- +- prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; +- subunit_type = data[1] >> 3; +- subunit_id = data[1] & 7; +- opcode = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; +- op = debug_fcp_opcode(opcode, data, length); +- +- if (op) { +- printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", +- prefix, subunit_type, subunit_id, length, +- debug_fcp_ctype(data[0]), op); +- if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) +- print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, +- 16, 1, data, length, false); +- } +-} +- +-static void debug_pmt(char *msg, int length) +-{ +- printk(KERN_INFO "APP PMT -> l=%d\n", length); +- print_hex_dump(KERN_INFO, "APP PMT -> ", DUMP_PREFIX_NONE, +- 16, 1, msg, length, false); +-} +- +-static int avc_write(struct firedtv *fdtv) +-{ +- int err, retry; +- +- fdtv->avc_reply_received = false; +- +- for (retry = 0; retry < 6; retry++) { +- if (unlikely(avc_debug)) +- debug_fcp(fdtv->avc_data, fdtv->avc_data_length); +- +- err = fdtv_write(fdtv, FCP_COMMAND_REGISTER, +- fdtv->avc_data, fdtv->avc_data_length); +- if (err) { +- dev_err(fdtv->device, "FCP command write failed\n"); +- +- return err; +- } +- +- /* +- * AV/C specs say that answers should be sent within 150 ms. +- * Time out after 200 ms. +- */ +- if (wait_event_timeout(fdtv->avc_wait, +- fdtv->avc_reply_received, +- msecs_to_jiffies(200)) != 0) +- return 0; +- } +- dev_err(fdtv->device, "FCP response timed out\n"); +- +- return -ETIMEDOUT; +-} +- +-static bool is_register_rc(struct avc_response_frame *r) +-{ +- return r->opcode == AVC_OPCODE_VENDOR && +- r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && +- r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && +- r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && +- r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; +-} +- +-int avc_recv(struct firedtv *fdtv, void *data, size_t length) +-{ +- struct avc_response_frame *r = data; +- +- if (unlikely(avc_debug)) +- debug_fcp(data, length); +- +- if (length >= 8 && is_register_rc(r)) { +- switch (r->response) { +- case AVC_RESPONSE_CHANGED: +- fdtv_handle_rc(fdtv, r->operand[4] << 8 | r->operand[5]); +- schedule_work(&fdtv->remote_ctrl_work); +- break; +- case AVC_RESPONSE_INTERIM: +- if (is_register_rc((void *)fdtv->avc_data)) +- goto wake; +- break; +- default: +- dev_info(fdtv->device, +- "remote control result = %d\n", r->response); +- } +- return 0; +- } +- +- if (fdtv->avc_reply_received) { +- dev_err(fdtv->device, "out-of-order AVC response, ignored\n"); +- return -EIO; +- } +- +- memcpy(fdtv->avc_data, data, length); +- fdtv->avc_data_length = length; +-wake: +- fdtv->avc_reply_received = true; +- wake_up(&fdtv->avc_wait); +- +- return 0; +-} +- +-static int add_pid_filter(struct firedtv *fdtv, u8 *operand) +-{ +- int i, n, pos = 1; +- +- for (i = 0, n = 0; i < 16; i++) { +- if (test_bit(i, &fdtv->channel_active)) { +- operand[pos++] = 0x13; /* flowfunction relay */ +- operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ +- operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f; +- operand[pos++] = fdtv->channel_pid[i] & 0xff; +- operand[pos++] = 0x00; /* tableID */ +- operand[pos++] = 0x00; /* filter_length */ +- n++; +- } +- } +- operand[0] = n; +- +- return pos; +-} +- +-/* +- * tuning command for setting the relative LNB frequency +- * (not supported by the AVC standard) +- */ +-static int avc_tuner_tuneqpsk(struct firedtv *fdtv, +- struct dtv_frontend_properties *p) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- if (fdtv->type == FIREDTV_DVB_S2) +- c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK2; +- else +- c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; +- +- c->operand[4] = (p->frequency >> 24) & 0xff; +- c->operand[5] = (p->frequency >> 16) & 0xff; +- c->operand[6] = (p->frequency >> 8) & 0xff; +- c->operand[7] = p->frequency & 0xff; +- +- c->operand[8] = ((p->symbol_rate / 1000) >> 8) & 0xff; +- c->operand[9] = (p->symbol_rate / 1000) & 0xff; +- +- switch (p->fec_inner) { +- case FEC_1_2: c->operand[10] = 0x1; break; +- case FEC_2_3: c->operand[10] = 0x2; break; +- case FEC_3_4: c->operand[10] = 0x3; break; +- case FEC_5_6: c->operand[10] = 0x4; break; +- case FEC_7_8: c->operand[10] = 0x5; break; +- case FEC_4_5: +- case FEC_8_9: +- case FEC_AUTO: +- default: c->operand[10] = 0x0; +- } +- +- if (fdtv->voltage == 0xff) +- c->operand[11] = 0xff; +- else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ +- c->operand[11] = 0; +- else +- c->operand[11] = 1; +- +- if (fdtv->tone == 0xff) +- c->operand[12] = 0xff; +- else if (fdtv->tone == SEC_TONE_ON) /* band */ +- c->operand[12] = 1; +- else +- c->operand[12] = 0; +- +- if (fdtv->type == FIREDTV_DVB_S2) { +- if (fdtv->fe.dtv_property_cache.delivery_system == SYS_DVBS2) { +- switch (fdtv->fe.dtv_property_cache.modulation) { +- case QAM_16: c->operand[13] = 0x1; break; +- case QPSK: c->operand[13] = 0x2; break; +- case PSK_8: c->operand[13] = 0x3; break; +- default: c->operand[13] = 0x2; break; +- } +- switch (fdtv->fe.dtv_property_cache.rolloff) { +- case ROLLOFF_35: c->operand[14] = 0x2; break; +- case ROLLOFF_20: c->operand[14] = 0x0; break; +- case ROLLOFF_25: c->operand[14] = 0x1; break; +- case ROLLOFF_AUTO: +- default: c->operand[14] = 0x2; break; +- /* case ROLLOFF_NONE: c->operand[14] = 0xff; break; */ +- } +- switch (fdtv->fe.dtv_property_cache.pilot) { +- case PILOT_AUTO: c->operand[15] = 0x0; break; +- case PILOT_OFF: c->operand[15] = 0x0; break; +- case PILOT_ON: c->operand[15] = 0x1; break; +- } +- } else { +- c->operand[13] = 0x1; /* auto modulation */ +- c->operand[14] = 0xff; /* disable rolloff */ +- c->operand[15] = 0xff; /* disable pilot */ +- } +- return 16; +- } else { +- return 13; +- } +-} +- +-static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv, +- struct dtv_frontend_properties *p) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- +- c->opcode = AVC_OPCODE_DSD; +- +- c->operand[0] = 0; /* source plug */ +- c->operand[1] = 0xd2; /* subfunction replace */ +- c->operand[2] = 0x20; /* system id = DVB */ +- c->operand[3] = 0x00; /* antenna number */ +- c->operand[4] = 0x11; /* system_specific_multiplex selection_length */ +- +- /* multiplex_valid_flags, high byte */ +- c->operand[5] = 0 << 7 /* reserved */ +- | 0 << 6 /* Polarisation */ +- | 0 << 5 /* Orbital_Pos */ +- | 1 << 4 /* Frequency */ +- | 1 << 3 /* Symbol_Rate */ +- | 0 << 2 /* FEC_outer */ +- | (p->fec_inner != FEC_AUTO ? 1 << 1 : 0) +- | (p->modulation != QAM_AUTO ? 1 << 0 : 0); +- +- /* multiplex_valid_flags, low byte */ +- c->operand[6] = 0 << 7 /* NetworkID */ +- | 0 << 0 /* reserved */ ; +- +- c->operand[7] = 0x00; +- c->operand[8] = 0x00; +- c->operand[9] = 0x00; +- c->operand[10] = 0x00; +- +- c->operand[11] = (((p->frequency / 4000) >> 16) & 0xff) | (2 << 6); +- c->operand[12] = ((p->frequency / 4000) >> 8) & 0xff; +- c->operand[13] = (p->frequency / 4000) & 0xff; +- c->operand[14] = ((p->symbol_rate / 1000) >> 12) & 0xff; +- c->operand[15] = ((p->symbol_rate / 1000) >> 4) & 0xff; +- c->operand[16] = ((p->symbol_rate / 1000) << 4) & 0xf0; +- c->operand[17] = 0x00; +- +- switch (p->fec_inner) { +- case FEC_1_2: c->operand[18] = 0x1; break; +- case FEC_2_3: c->operand[18] = 0x2; break; +- case FEC_3_4: c->operand[18] = 0x3; break; +- case FEC_5_6: c->operand[18] = 0x4; break; +- case FEC_7_8: c->operand[18] = 0x5; break; +- case FEC_8_9: c->operand[18] = 0x6; break; +- case FEC_4_5: c->operand[18] = 0x8; break; +- case FEC_AUTO: +- default: c->operand[18] = 0x0; +- } +- +- switch (p->modulation) { +- case QAM_16: c->operand[19] = 0x08; break; +- case QAM_32: c->operand[19] = 0x10; break; +- case QAM_64: c->operand[19] = 0x18; break; +- case QAM_128: c->operand[19] = 0x20; break; +- case QAM_256: c->operand[19] = 0x28; break; +- case QAM_AUTO: +- default: c->operand[19] = 0x00; +- } +- +- c->operand[20] = 0x00; +- c->operand[21] = 0x00; +- +- return 22 + add_pid_filter(fdtv, &c->operand[22]); +-} +- +-static int avc_tuner_dsd_dvb_t(struct firedtv *fdtv, +- struct dtv_frontend_properties *p) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- +- c->opcode = AVC_OPCODE_DSD; +- +- c->operand[0] = 0; /* source plug */ +- c->operand[1] = 0xd2; /* subfunction replace */ +- c->operand[2] = 0x20; /* system id = DVB */ +- c->operand[3] = 0x00; /* antenna number */ +- c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */ +- +- /* multiplex_valid_flags, high byte */ +- c->operand[5] = +- 0 << 7 /* reserved */ +- | 1 << 6 /* CenterFrequency */ +- | (p->bandwidth_hz != 0 ? 1 << 5 : 0) +- | (p->modulation != QAM_AUTO ? 1 << 4 : 0) +- | (p->hierarchy != HIERARCHY_AUTO ? 1 << 3 : 0) +- | (p->code_rate_HP != FEC_AUTO ? 1 << 2 : 0) +- | (p->code_rate_LP != FEC_AUTO ? 1 << 1 : 0) +- | (p->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0); +- +- /* multiplex_valid_flags, low byte */ +- c->operand[6] = +- 0 << 7 /* NetworkID */ +- | (p->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0) +- | 0 << 5 /* OtherFrequencyFlag */ +- | 0 << 0 /* reserved */ ; +- +- c->operand[7] = 0x0; +- c->operand[8] = (p->frequency / 10) >> 24; +- c->operand[9] = ((p->frequency / 10) >> 16) & 0xff; +- c->operand[10] = ((p->frequency / 10) >> 8) & 0xff; +- c->operand[11] = (p->frequency / 10) & 0xff; +- +- switch (p->bandwidth_hz) { +- case 7000000: c->operand[12] = 0x20; break; +- case 8000000: +- case 6000000: /* not defined by AVC spec */ +- case 0: +- default: c->operand[12] = 0x00; +- } +- +- switch (p->modulation) { +- case QAM_16: c->operand[13] = 1 << 6; break; +- case QAM_64: c->operand[13] = 2 << 6; break; +- case QPSK: +- default: c->operand[13] = 0x00; +- } +- +- switch (p->hierarchy) { +- case HIERARCHY_1: c->operand[13] |= 1 << 3; break; +- case HIERARCHY_2: c->operand[13] |= 2 << 3; break; +- case HIERARCHY_4: c->operand[13] |= 3 << 3; break; +- case HIERARCHY_AUTO: +- case HIERARCHY_NONE: +- default: break; +- } +- +- switch (p->code_rate_HP) { +- case FEC_2_3: c->operand[13] |= 1; break; +- case FEC_3_4: c->operand[13] |= 2; break; +- case FEC_5_6: c->operand[13] |= 3; break; +- case FEC_7_8: c->operand[13] |= 4; break; +- case FEC_1_2: +- default: break; +- } +- +- switch (p->code_rate_LP) { +- case FEC_2_3: c->operand[14] = 1 << 5; break; +- case FEC_3_4: c->operand[14] = 2 << 5; break; +- case FEC_5_6: c->operand[14] = 3 << 5; break; +- case FEC_7_8: c->operand[14] = 4 << 5; break; +- case FEC_1_2: +- default: c->operand[14] = 0x00; break; +- } +- +- switch (p->guard_interval) { +- case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break; +- case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break; +- case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break; +- case GUARD_INTERVAL_1_32: +- case GUARD_INTERVAL_AUTO: +- default: break; +- } +- +- switch (p->transmission_mode) { +- case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break; +- case TRANSMISSION_MODE_2K: +- case TRANSMISSION_MODE_AUTO: +- default: break; +- } +- +- c->operand[15] = 0x00; /* network_ID[0] */ +- c->operand[16] = 0x00; /* network_ID[1] */ +- +- return 17 + add_pid_filter(fdtv, &c->operand[17]); +-} +- +-int avc_tuner_dsd(struct firedtv *fdtv, +- struct dtv_frontend_properties *p) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int pos, ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- +- switch (fdtv->type) { +- case FIREDTV_DVB_S: +- case FIREDTV_DVB_S2: pos = avc_tuner_tuneqpsk(fdtv, p); break; +- case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, p); break; +- case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, p); break; +- default: +- BUG(); +- } +- pad_operands(c, pos); +- +- fdtv->avc_data_length = ALIGN(3 + pos, 4); +- ret = avc_write(fdtv); +-#if 0 +- /* +- * FIXME: +- * u8 *status was an out-parameter of avc_tuner_dsd, unused by caller. +- * Check for AVC_RESPONSE_ACCEPTED here instead? +- */ +- if (status) +- *status = r->operand[2]; +-#endif +- mutex_unlock(&fdtv->avc_mutex); +- +- if (ret == 0) +- msleep(500); +- +- return ret; +-} +- +-int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret, pos, k; +- +- if (pidc > 16 && pidc != 0xff) +- return -EINVAL; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_DSD; +- +- c->operand[0] = 0; /* source plug */ +- c->operand[1] = 0xd2; /* subfunction replace */ +- c->operand[2] = 0x20; /* system id = DVB */ +- c->operand[3] = 0x00; /* antenna number */ +- c->operand[4] = 0x00; /* system_specific_multiplex selection_length */ +- c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */ +- +- pos = 6; +- if (pidc != 0xff) +- for (k = 0; k < pidc; k++) { +- c->operand[pos++] = 0x13; /* flowfunction relay */ +- c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ +- c->operand[pos++] = (pid[k] >> 8) & 0x1f; +- c->operand[pos++] = pid[k] & 0xff; +- c->operand[pos++] = 0x00; /* tableID */ +- c->operand[pos++] = 0x00; /* filter_length */ +- } +- pad_operands(c, pos); +- +- fdtv->avc_data_length = ALIGN(3 + pos, 4); +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- if (ret == 0) +- msleep(50); +- +- return ret; +-} +- +-int avc_tuner_get_ts(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret, sl; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_DSIT; +- +- sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11; +- +- c->operand[0] = 0; /* source plug */ +- c->operand[1] = 0xd2; /* subfunction replace */ +- c->operand[2] = 0xff; /* status */ +- c->operand[3] = 0x20; /* system id = DVB */ +- c->operand[4] = 0x00; /* antenna number */ +- c->operand[5] = 0x0; /* system_specific_search_flags */ +- c->operand[6] = sl; /* system_specific_multiplex selection_length */ +- /* +- * operand[7]: valid_flags[0] +- * operand[8]: valid_flags[1] +- * operand[7 + sl]: nr_of_dsit_sel_specs (always 0) +- */ +- clear_operands(c, 7, 24); +- +- fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- if (ret == 0) +- msleep(250); +- +- return ret; +-} +- +-int avc_identify_subunit(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_READ_DESCRIPTOR; +- +- c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER; +- c->operand[1] = 0xff; +- c->operand[2] = 0x00; +- c->operand[3] = 0x00; /* length highbyte */ +- c->operand[4] = 0x08; /* length lowbyte */ +- c->operand[5] = 0x00; /* offset highbyte */ +- c->operand[6] = 0x0d; /* offset lowbyte */ +- clear_operands(c, 7, 8); /* padding */ +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- if ((r->response != AVC_RESPONSE_STABLE && +- r->response != AVC_RESPONSE_ACCEPTED) || +- (r->operand[3] << 8) + r->operand[4] != 8) { +- dev_err(fdtv->device, "cannot read subunit identifier\n"); +- ret = -EINVAL; +- } +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-#define SIZEOF_ANTENNA_INPUT_INFO 22 +- +-int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int length, ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_READ_DESCRIPTOR; +- +- c->operand[0] = DESCRIPTOR_TUNER_STATUS; +- c->operand[1] = 0xff; /* read_result_status */ +- /* +- * operand[2]: reserved +- * operand[3]: SIZEOF_ANTENNA_INPUT_INFO >> 8 +- * operand[4]: SIZEOF_ANTENNA_INPUT_INFO & 0xff +- */ +- clear_operands(c, 2, 31); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- if (r->response != AVC_RESPONSE_STABLE && +- r->response != AVC_RESPONSE_ACCEPTED) { +- dev_err(fdtv->device, "cannot read tuner status\n"); +- ret = -EINVAL; +- goto out; +- } +- +- length = r->operand[9]; +- if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { +- dev_err(fdtv->device, "got invalid tuner status\n"); +- ret = -EINVAL; +- goto out; +- } +- +- stat->active_system = r->operand[10]; +- stat->searching = r->operand[11] >> 7 & 1; +- stat->moving = r->operand[11] >> 6 & 1; +- stat->no_rf = r->operand[11] >> 5 & 1; +- stat->input = r->operand[12] >> 7 & 1; +- stat->selected_antenna = r->operand[12] & 0x7f; +- stat->ber = r->operand[13] << 24 | +- r->operand[14] << 16 | +- r->operand[15] << 8 | +- r->operand[16]; +- stat->signal_strength = r->operand[17]; +- stat->raster_frequency = r->operand[18] >> 6 & 2; +- stat->rf_frequency = (r->operand[18] & 0x3f) << 16 | +- r->operand[19] << 8 | +- r->operand[20]; +- stat->man_dep_info_length = r->operand[21]; +- stat->front_end_error = r->operand[22] >> 4 & 1; +- stat->antenna_error = r->operand[22] >> 3 & 1; +- stat->front_end_power_status = r->operand[22] >> 1 & 1; +- stat->power_supply = r->operand[22] & 1; +- stat->carrier_noise_ratio = r->operand[23] << 8 | +- r->operand[24]; +- stat->power_supply_voltage = r->operand[27]; +- stat->antenna_voltage = r->operand[28]; +- stat->firewire_bus_voltage = r->operand[29]; +- stat->ca_mmi = r->operand[30] & 1; +- stat->ca_pmt_reply = r->operand[31] >> 7 & 1; +- stat->ca_date_time_request = r->operand[31] >> 6 & 1; +- stat->ca_application_info = r->operand[31] >> 5 & 1; +- stat->ca_module_present_status = r->operand[31] >> 4 & 1; +- stat->ca_dvb_flag = r->operand[31] >> 3 & 1; +- stat->ca_error_flag = r->operand[31] >> 2 & 1; +- stat->ca_initialization_status = r->operand[31] >> 1 & 1; +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, +- char conttone, char nrdiseq, +- struct dvb_diseqc_master_cmd *diseqcmd) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int pos, j, k, ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; +- c->operand[4] = voltage; +- c->operand[5] = nrdiseq; +- +- pos = 6; +- for (j = 0; j < nrdiseq; j++) { +- c->operand[pos++] = diseqcmd[j].msg_len; +- +- for (k = 0; k < diseqcmd[j].msg_len; k++) +- c->operand[pos++] = diseqcmd[j].msg[k]; +- } +- c->operand[pos++] = burst; +- c->operand[pos++] = conttone; +- pad_operands(c, pos); +- +- fdtv->avc_data_length = ALIGN(3 + pos, 4); +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- if (r->response != AVC_RESPONSE_ACCEPTED) { +- dev_err(fdtv->device, "LNB control failed\n"); +- ret = -EINVAL; +- } +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_register_remote_control(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_NOTIFY; +- c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; +- c->operand[4] = 0; /* padding */ +- +- fdtv->avc_data_length = 8; +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-void avc_remote_ctrl_work(struct work_struct *work) +-{ +- struct firedtv *fdtv = +- container_of(work, struct firedtv, remote_ctrl_work); +- +- /* Should it be rescheduled in failure cases? */ +- avc_register_remote_control(fdtv); +-} +- +-#if 0 /* FIXME: unused */ +-int avc_tuner_host2ca(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ +- clear_operands(c, 6, 8); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +-#endif +- +-static int get_ca_object_pos(struct avc_response_frame *r) +-{ +- int length = 1; +- +- /* Check length of length field */ +- if (r->operand[7] & 0x80) +- length = (r->operand[7] & 0x7f) + 1; +- return length + 7; +-} +- +-static int get_ca_object_length(struct avc_response_frame *r) +-{ +-#if 0 /* FIXME: unused */ +- int size = 0; +- int i; +- +- if (r->operand[7] & 0x80) +- for (i = 0; i < (r->operand[7] & 0x7f); i++) { +- size <<= 8; +- size += r->operand[8 + i]; +- } +-#endif +- return r->operand[7]; +-} +- +-int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int pos, ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_STATUS; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ +- clear_operands(c, 6, LAST_OPERAND); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- /* FIXME: check response code and validate response data */ +- +- pos = get_ca_object_pos(r); +- app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff; +- app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff; +- app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff; +- app_info[3] = 6 + r->operand[pos + 4]; +- app_info[4] = 0x01; +- memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); +- *len = app_info[3] + 4; +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int i, pos, ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_STATUS; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ +- clear_operands(c, 6, LAST_OPERAND); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- /* FIXME: check response code and validate response data */ +- +- pos = get_ca_object_pos(r); +- app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; +- app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff; +- app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff; +- if (num_fake_ca_system_ids == 0) { +- app_info[3] = 2; +- app_info[4] = r->operand[pos + 0]; +- app_info[5] = r->operand[pos + 1]; +- } else { +- app_info[3] = num_fake_ca_system_ids * 2; +- for (i = 0; i < num_fake_ca_system_ids; i++) { +- app_info[4 + i * 2] = +- (fake_ca_system_ids[i] >> 8) & 0xff; +- app_info[5 + i * 2] = fake_ca_system_ids[i] & 0xff; +- } +- } +- *len = app_info[3] + 4; +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_reset(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */ +- c->operand[6] = 0; /* more/last */ +- c->operand[7] = 1; /* length */ +- c->operand[8] = 0; /* force hardware reset */ +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int list_management; +- int program_info_length; +- int pmt_cmd_id; +- int read_pos; +- int write_pos; +- int es_info_length; +- int crc32_csum; +- int ret; +- +- if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT)) +- debug_pmt(msg, length); +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_CONTROL; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) { +- dev_info(fdtv->device, "forcing list_management to ONLY\n"); +- msg[0] = EN50221_LIST_MANAGEMENT_ONLY; +- } +- /* We take the cmd_id from the programme level only! */ +- list_management = msg[0]; +- program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; +- if (program_info_length > 0) +- program_info_length--; /* Remove pmt_cmd_id */ +- pmt_cmd_id = msg[6]; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ +- c->operand[6] = 0; /* more/last */ +- /* Use three bytes for length field in case length > 127 */ +- c->operand[10] = list_management; +- c->operand[11] = 0x01; /* pmt_cmd=OK_descramble */ +- +- /* TS program map table */ +- +- c->operand[12] = 0x02; /* Table id=2 */ +- c->operand[13] = 0x80; /* Section syntax + length */ +- +- c->operand[15] = msg[1]; /* Program number */ +- c->operand[16] = msg[2]; +- c->operand[17] = msg[3]; /* Version number and current/next */ +- c->operand[18] = 0x00; /* Section number=0 */ +- c->operand[19] = 0x00; /* Last section number=0 */ +- c->operand[20] = 0x1f; /* PCR_PID=1FFF */ +- c->operand[21] = 0xff; +- c->operand[22] = (program_info_length >> 8); /* Program info length */ +- c->operand[23] = (program_info_length & 0xff); +- +- /* CA descriptors at programme level */ +- read_pos = 6; +- write_pos = 24; +- if (program_info_length > 0) { +- pmt_cmd_id = msg[read_pos++]; +- if (pmt_cmd_id != 1 && pmt_cmd_id != 4) +- dev_err(fdtv->device, +- "invalid pmt_cmd_id %d\n", pmt_cmd_id); +- +- memcpy(&c->operand[write_pos], &msg[read_pos], +- program_info_length); +- read_pos += program_info_length; +- write_pos += program_info_length; +- } +- while (read_pos < length) { +- c->operand[write_pos++] = msg[read_pos++]; +- c->operand[write_pos++] = msg[read_pos++]; +- c->operand[write_pos++] = msg[read_pos++]; +- es_info_length = +- ((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1]; +- read_pos += 2; +- if (es_info_length > 0) +- es_info_length--; /* Remove pmt_cmd_id */ +- c->operand[write_pos++] = es_info_length >> 8; +- c->operand[write_pos++] = es_info_length & 0xff; +- if (es_info_length > 0) { +- pmt_cmd_id = msg[read_pos++]; +- if (pmt_cmd_id != 1 && pmt_cmd_id != 4) +- dev_err(fdtv->device, "invalid pmt_cmd_id %d " +- "at stream level\n", pmt_cmd_id); +- +- memcpy(&c->operand[write_pos], &msg[read_pos], +- es_info_length); +- read_pos += es_info_length; +- write_pos += es_info_length; +- } +- } +- write_pos += 4; /* CRC */ +- +- c->operand[7] = 0x82; +- c->operand[8] = (write_pos - 10) >> 8; +- c->operand[9] = (write_pos - 10) & 0xff; +- c->operand[14] = write_pos - 15; +- +- crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); +- c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; +- c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; +- c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; +- c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; +- pad_operands(c, write_pos); +- +- fdtv->avc_data_length = ALIGN(3 + write_pos, 4); +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- if (r->response != AVC_RESPONSE_ACCEPTED) { +- dev_err(fdtv->device, +- "CA PMT failed with response 0x%x\n", r->response); +- ret = -EACCES; +- } +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_STATUS; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ +- clear_operands(c, 6, LAST_OPERAND); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- /* FIXME: check response code and validate response data */ +- +- *interval = r->operand[get_ca_object_pos(r)]; +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_enter_menu(struct firedtv *fdtv) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_STATUS; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; +- clear_operands(c, 6, 8); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- +- /* FIXME: check response code? */ +- +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) +-{ +- struct avc_command_frame *c = (void *)fdtv->avc_data; +- struct avc_response_frame *r = (void *)fdtv->avc_data; +- int ret; +- +- mutex_lock(&fdtv->avc_mutex); +- +- c->ctype = AVC_CTYPE_STATUS; +- c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; +- c->opcode = AVC_OPCODE_VENDOR; +- +- c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; +- c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; +- c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; +- c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; +- c->operand[4] = 0; /* slot */ +- c->operand[5] = SFE_VENDOR_TAG_CA_MMI; +- clear_operands(c, 6, LAST_OPERAND); +- +- fdtv->avc_data_length = 12; +- ret = avc_write(fdtv); +- if (ret < 0) +- goto out; +- +- /* FIXME: check response code and validate response data */ +- +- *len = get_ca_object_length(r); +- memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); +-out: +- mutex_unlock(&fdtv->avc_mutex); +- +- return ret; +-} +- +-#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL +- +-static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) +-{ +- int ret; +- +- ret = fdtv_read(fdtv, addr, data); +- if (ret < 0) +- dev_err(fdtv->device, "CMP: read I/O error\n"); +- +- return ret; +-} +- +-static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) +-{ +- int ret; +- +- ret = fdtv_lock(fdtv, addr, data); +- if (ret < 0) +- dev_err(fdtv->device, "CMP: lock I/O error\n"); +- +- return ret; +-} +- +-static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) +-{ +- return (be32_to_cpu(opcr) >> shift) & mask; +-} +- +-static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) +-{ +- *opcr &= ~cpu_to_be32(mask << shift); +- *opcr |= cpu_to_be32((value & mask) << shift); +-} +- +-#define get_opcr_online(v) get_opcr((v), 0x1, 31) +-#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) +-#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) +- +-#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) +-#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) +-#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) +-#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) +- +-int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) +-{ +- __be32 old_opcr, opcr[2]; +- u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); +- int attempts = 0; +- int ret; +- +- ret = cmp_read(fdtv, opcr_address, opcr); +- if (ret < 0) +- return ret; +- +-repeat: +- if (!get_opcr_online(*opcr)) { +- dev_err(fdtv->device, "CMP: output offline\n"); +- return -EBUSY; +- } +- +- old_opcr = *opcr; +- +- if (get_opcr_p2p_connections(*opcr)) { +- if (get_opcr_channel(*opcr) != channel) { +- dev_err(fdtv->device, "CMP: cannot change channel\n"); +- return -EBUSY; +- } +- dev_info(fdtv->device, "CMP: overlaying connection\n"); +- +- /* We don't allocate isochronous resources. */ +- } else { +- set_opcr_channel(opcr, channel); +- set_opcr_data_rate(opcr, 2); /* S400 */ +- +- /* FIXME: this is for the worst case - optimize */ +- set_opcr_overhead_id(opcr, 0); +- +- /* FIXME: allocate isochronous channel and bandwidth at IRM */ +- } +- +- set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1); +- +- opcr[1] = *opcr; +- opcr[0] = old_opcr; +- +- ret = cmp_lock(fdtv, opcr_address, opcr); +- if (ret < 0) +- return ret; +- +- if (old_opcr != *opcr) { +- /* +- * FIXME: if old_opcr.P2P_Connections > 0, +- * deallocate isochronous channel and bandwidth at IRM +- */ +- +- if (++attempts < 6) /* arbitrary limit */ +- goto repeat; +- return -EBUSY; +- } +- +- return 0; +-} +- +-void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) +-{ +- __be32 old_opcr, opcr[2]; +- u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); +- int attempts = 0; +- +- if (cmp_read(fdtv, opcr_address, opcr) < 0) +- return; +- +-repeat: +- if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) || +- get_opcr_channel(*opcr) != channel) { +- dev_err(fdtv->device, "CMP: no connection to break\n"); +- return; +- } +- +- old_opcr = *opcr; +- set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1); +- +- opcr[1] = *opcr; +- opcr[0] = old_opcr; +- +- if (cmp_lock(fdtv, opcr_address, opcr) < 0) +- return; +- +- if (old_opcr != *opcr) { +- /* +- * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last +- * owner, deallocate isochronous channel and bandwidth at IRM +- * if (...) +- * fdtv->backend->dealloc_resources(fdtv, channel, bw); +- */ +- +- if (++attempts < 6) /* arbitrary limit */ +- goto repeat; +- } +-} +diff --git a/drivers/media/dvb/firewire/firedtv-ci.c b/drivers/media/dvb/firewire/firedtv-ci.c +deleted file mode 100644 +index e5ebdbf..0000000 +--- a/drivers/media/dvb/firewire/firedtv-ci.c ++++ /dev/null +@@ -1,258 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * Copyright (C) 2008 Henrik Kurelid +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +- +-#include +- +-#include "firedtv.h" +- +-#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 +-#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 +-#define EN50221_TAG_CA_PMT 0x9f8032 +-#define EN50221_TAG_ENTER_MENU 0x9f8022 +- +-static int fdtv_ca_ready(struct firedtv_tuner_status *stat) +-{ +- return stat->ca_initialization_status == 1 && +- stat->ca_error_flag == 0 && +- stat->ca_dvb_flag == 1 && +- stat->ca_module_present_status == 1; +-} +- +-static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) +-{ +- int flags = 0; +- +- if (stat->ca_module_present_status == 1) +- flags |= CA_CI_MODULE_PRESENT; +- if (stat->ca_initialization_status == 1 && +- stat->ca_error_flag == 0 && +- stat->ca_dvb_flag == 1) +- flags |= CA_CI_MODULE_READY; +- return flags; +-} +- +-static int fdtv_ca_get_caps(void *arg) +-{ +- struct ca_caps *cap = arg; +- +- cap->slot_num = 1; +- cap->slot_type = CA_CI; +- cap->descr_num = 1; +- cap->descr_type = CA_ECD; +- return 0; +-} +- +-static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) +-{ +- struct firedtv_tuner_status stat; +- struct ca_slot_info *slot = arg; +- int err; +- +- err = avc_tuner_status(fdtv, &stat); +- if (err) +- return err; +- +- if (slot->num != 0) +- return -EACCES; +- +- slot->type = CA_CI; +- slot->flags = fdtv_get_ca_flags(&stat); +- return 0; +-} +- +-static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) +-{ +- struct ca_msg *reply = arg; +- +- return avc_ca_app_info(fdtv, reply->msg, &reply->length); +-} +- +-static int fdtv_ca_info(struct firedtv *fdtv, void *arg) +-{ +- struct ca_msg *reply = arg; +- +- return avc_ca_info(fdtv, reply->msg, &reply->length); +-} +- +-static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) +-{ +- struct ca_msg *reply = arg; +- +- return avc_ca_get_mmi(fdtv, reply->msg, &reply->length); +-} +- +-static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) +-{ +- struct firedtv_tuner_status stat; +- int err; +- +- switch (fdtv->ca_last_command) { +- case EN50221_TAG_APP_INFO_ENQUIRY: +- err = fdtv_ca_app_info(fdtv, arg); +- break; +- case EN50221_TAG_CA_INFO_ENQUIRY: +- err = fdtv_ca_info(fdtv, arg); +- break; +- default: +- err = avc_tuner_status(fdtv, &stat); +- if (err) +- break; +- if (stat.ca_mmi == 1) +- err = fdtv_ca_get_mmi(fdtv, arg); +- else { +- dev_info(fdtv->device, "unhandled CA message 0x%08x\n", +- fdtv->ca_last_command); +- err = -EACCES; +- } +- } +- fdtv->ca_last_command = 0; +- return err; +-} +- +-static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) +-{ +- struct ca_msg *msg = arg; +- int data_pos; +- int data_length; +- int i; +- +- data_pos = 4; +- if (msg->msg[3] & 0x80) { +- data_length = 0; +- for (i = 0; i < (msg->msg[3] & 0x7f); i++) +- data_length = (data_length << 8) + msg->msg[data_pos++]; +- } else { +- data_length = msg->msg[3]; +- } +- +- return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); +-} +- +-static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) +-{ +- struct ca_msg *msg = arg; +- int err; +- +- /* Do we need a semaphore for this? */ +- fdtv->ca_last_command = +- (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; +- switch (fdtv->ca_last_command) { +- case EN50221_TAG_CA_PMT: +- err = fdtv_ca_pmt(fdtv, arg); +- break; +- case EN50221_TAG_APP_INFO_ENQUIRY: +- /* handled in ca_get_msg */ +- err = 0; +- break; +- case EN50221_TAG_CA_INFO_ENQUIRY: +- /* handled in ca_get_msg */ +- err = 0; +- break; +- case EN50221_TAG_ENTER_MENU: +- err = avc_ca_enter_menu(fdtv); +- break; +- default: +- dev_err(fdtv->device, "unhandled CA message 0x%08x\n", +- fdtv->ca_last_command); +- err = -EACCES; +- } +- return err; +-} +- +-static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct firedtv *fdtv = dvbdev->priv; +- struct firedtv_tuner_status stat; +- int err; +- +- switch (cmd) { +- case CA_RESET: +- err = avc_ca_reset(fdtv); +- break; +- case CA_GET_CAP: +- err = fdtv_ca_get_caps(arg); +- break; +- case CA_GET_SLOT_INFO: +- err = fdtv_ca_get_slot_info(fdtv, arg); +- break; +- case CA_GET_MSG: +- err = fdtv_ca_get_msg(fdtv, arg); +- break; +- case CA_SEND_MSG: +- err = fdtv_ca_send_msg(fdtv, arg); +- break; +- default: +- dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); +- err = -EOPNOTSUPP; +- } +- +- /* FIXME Is this necessary? */ +- avc_tuner_status(fdtv, &stat); +- +- return err; +-} +- +-static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) +-{ +- return POLLIN; +-} +- +-static const struct file_operations fdtv_ca_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = dvb_generic_ioctl, +- .open = dvb_generic_open, +- .release = dvb_generic_release, +- .poll = fdtv_ca_io_poll, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device fdtv_ca = { +- .users = 1, +- .readers = 1, +- .writers = 1, +- .fops = &fdtv_ca_fops, +- .kernel_ioctl = fdtv_ca_ioctl, +-}; +- +-int fdtv_ca_register(struct firedtv *fdtv) +-{ +- struct firedtv_tuner_status stat; +- int err; +- +- if (avc_tuner_status(fdtv, &stat)) +- return -EINVAL; +- +- if (!fdtv_ca_ready(&stat)) +- return -EFAULT; +- +- err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, +- &fdtv_ca, fdtv, DVB_DEVICE_CA); +- +- if (stat.ca_application_info == 0) +- dev_err(fdtv->device, "CaApplicationInfo is not set\n"); +- if (stat.ca_date_time_request == 1) +- avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); +- +- return err; +-} +- +-void fdtv_ca_release(struct firedtv *fdtv) +-{ +- if (fdtv->cadev) +- dvb_unregister_device(fdtv->cadev); +-} +diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c +deleted file mode 100644 +index eb7496e..0000000 +--- a/drivers/media/dvb/firewire/firedtv-dvb.c ++++ /dev/null +@@ -1,248 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * Copyright (C) 2008 Henrik Kurelid +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include "firedtv.h" +- +-static int alloc_channel(struct firedtv *fdtv) +-{ +- int i; +- +- for (i = 0; i < 16; i++) +- if (!__test_and_set_bit(i, &fdtv->channel_active)) +- break; +- return i; +-} +- +-static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) +-{ +- int i, n; +- +- for (i = 0, n = 0; i < 16; i++) +- if (test_bit(i, &fdtv->channel_active)) +- pid[n++] = fdtv->channel_pid[i]; +- *pidc = n; +-} +- +-static inline void dealloc_channel(struct firedtv *fdtv, int i) +-{ +- __clear_bit(i, &fdtv->channel_active); +-} +- +-int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct firedtv *fdtv = dvbdmxfeed->demux->priv; +- int pidc, c, ret; +- u16 pids[16]; +- +- switch (dvbdmxfeed->type) { +- case DMX_TYPE_TS: +- case DMX_TYPE_SEC: +- break; +- default: +- dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", +- dvbdmxfeed->type); +- return -EINVAL; +- } +- +- if (mutex_lock_interruptible(&fdtv->demux_mutex)) +- return -EINTR; +- +- if (dvbdmxfeed->type == DMX_TYPE_TS) { +- switch (dvbdmxfeed->pes_type) { +- case DMX_TS_PES_VIDEO: +- case DMX_TS_PES_AUDIO: +- case DMX_TS_PES_TELETEXT: +- case DMX_TS_PES_PCR: +- case DMX_TS_PES_OTHER: +- c = alloc_channel(fdtv); +- break; +- default: +- dev_err(fdtv->device, +- "can't start dmx feed: invalid pes type %u\n", +- dvbdmxfeed->pes_type); +- ret = -EINVAL; +- goto out; +- } +- } else { +- c = alloc_channel(fdtv); +- } +- +- if (c > 15) { +- dev_err(fdtv->device, "can't start dmx feed: busy\n"); +- ret = -EBUSY; +- goto out; +- } +- +- dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; +- fdtv->channel_pid[c] = dvbdmxfeed->pid; +- collect_channels(fdtv, &pidc, pids); +- +- if (dvbdmxfeed->pid == 8192) { +- ret = avc_tuner_get_ts(fdtv); +- if (ret) { +- dealloc_channel(fdtv, c); +- dev_err(fdtv->device, "can't get TS\n"); +- goto out; +- } +- } else { +- ret = avc_tuner_set_pids(fdtv, pidc, pids); +- if (ret) { +- dealloc_channel(fdtv, c); +- dev_err(fdtv->device, "can't set PIDs\n"); +- goto out; +- } +- } +-out: +- mutex_unlock(&fdtv->demux_mutex); +- +- return ret; +-} +- +-int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *demux = dvbdmxfeed->demux; +- struct firedtv *fdtv = demux->priv; +- int pidc, c, ret; +- u16 pids[16]; +- +- if (dvbdmxfeed->type == DMX_TYPE_TS && +- !((dvbdmxfeed->ts_type & TS_PACKET) && +- (demux->dmx.frontend->source != DMX_MEMORY_FE))) { +- +- if (dvbdmxfeed->ts_type & TS_DECODER) { +- if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || +- !demux->pesfilter[dvbdmxfeed->pes_type]) +- return -EINVAL; +- +- demux->pids[dvbdmxfeed->pes_type] |= 0x8000; +- demux->pesfilter[dvbdmxfeed->pes_type] = NULL; +- } +- +- if (!(dvbdmxfeed->ts_type & TS_DECODER && +- dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) +- return 0; +- } +- +- if (mutex_lock_interruptible(&fdtv->demux_mutex)) +- return -EINTR; +- +- c = (unsigned long)dvbdmxfeed->priv; +- dealloc_channel(fdtv, c); +- collect_channels(fdtv, &pidc, pids); +- +- ret = avc_tuner_set_pids(fdtv, pidc, pids); +- +- mutex_unlock(&fdtv->demux_mutex); +- +- return ret; +-} +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-int fdtv_dvb_register(struct firedtv *fdtv, const char *name) +-{ +- int err; +- +- err = dvb_register_adapter(&fdtv->adapter, name, +- THIS_MODULE, fdtv->device, adapter_nr); +- if (err < 0) +- goto fail_log; +- +- /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ +- fdtv->demux.dmx.capabilities = 0; +- +- fdtv->demux.priv = fdtv; +- fdtv->demux.filternum = 16; +- fdtv->demux.feednum = 16; +- fdtv->demux.start_feed = fdtv_start_feed; +- fdtv->demux.stop_feed = fdtv_stop_feed; +- fdtv->demux.write_to_decoder = NULL; +- +- err = dvb_dmx_init(&fdtv->demux); +- if (err) +- goto fail_unreg_adapter; +- +- fdtv->dmxdev.filternum = 16; +- fdtv->dmxdev.demux = &fdtv->demux.dmx; +- fdtv->dmxdev.capabilities = 0; +- +- err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); +- if (err) +- goto fail_dmx_release; +- +- fdtv->frontend.source = DMX_FRONTEND_0; +- +- err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); +- if (err) +- goto fail_dmxdev_release; +- +- err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, +- &fdtv->frontend); +- if (err) +- goto fail_rem_frontend; +- +- err = dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); +- if (err) +- goto fail_disconnect_frontend; +- +- fdtv_frontend_init(fdtv, name); +- err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); +- if (err) +- goto fail_net_release; +- +- err = fdtv_ca_register(fdtv); +- if (err) +- dev_info(fdtv->device, +- "Conditional Access Module not enabled\n"); +- return 0; +- +-fail_net_release: +- dvb_net_release(&fdtv->dvbnet); +-fail_disconnect_frontend: +- fdtv->demux.dmx.close(&fdtv->demux.dmx); +-fail_rem_frontend: +- fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); +-fail_dmxdev_release: +- dvb_dmxdev_release(&fdtv->dmxdev); +-fail_dmx_release: +- dvb_dmx_release(&fdtv->demux); +-fail_unreg_adapter: +- dvb_unregister_adapter(&fdtv->adapter); +-fail_log: +- dev_err(fdtv->device, "DVB initialization failed\n"); +- return err; +-} +- +-void fdtv_dvb_unregister(struct firedtv *fdtv) +-{ +- fdtv_ca_release(fdtv); +- dvb_unregister_frontend(&fdtv->fe); +- dvb_net_release(&fdtv->dvbnet); +- fdtv->demux.dmx.close(&fdtv->demux.dmx); +- fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); +- dvb_dmxdev_release(&fdtv->dmxdev); +- dvb_dmx_release(&fdtv->demux); +- dvb_unregister_adapter(&fdtv->adapter); +-} +diff --git a/drivers/media/dvb/firewire/firedtv-fe.c b/drivers/media/dvb/firewire/firedtv-fe.c +deleted file mode 100644 +index 6fe9793..0000000 +--- a/drivers/media/dvb/firewire/firedtv-fe.c ++++ /dev/null +@@ -1,254 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * Copyright (C) 2008 Henrik Kurelid +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "firedtv.h" +- +-static int fdtv_dvb_init(struct dvb_frontend *fe) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- int err; +- +- /* FIXME - allocate free channel at IRM */ +- fdtv->isochannel = fdtv->adapter.num; +- +- err = cmp_establish_pp_connection(fdtv, fdtv->subunit, +- fdtv->isochannel); +- if (err) { +- dev_err(fdtv->device, +- "could not establish point to point connection\n"); +- return err; +- } +- +- return fdtv_start_iso(fdtv); +-} +- +-static int fdtv_sleep(struct dvb_frontend *fe) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- +- fdtv_stop_iso(fdtv); +- cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); +- fdtv->isochannel = -1; +- return 0; +-} +- +-#define LNBCONTROL_DONTCARE 0xff +- +-static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *cmd) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- +- return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, +- LNBCONTROL_DONTCARE, 1, cmd); +-} +- +-static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t minicmd) +-{ +- return 0; +-} +- +-static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- +- fdtv->tone = tone; +- return 0; +-} +- +-static int fdtv_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- +- fdtv->voltage = voltage; +- return 0; +-} +- +-static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- struct firedtv_tuner_status stat; +- +- if (avc_tuner_status(fdtv, &stat)) +- return -EINVAL; +- +- if (stat.no_rf) +- *status = 0; +- else +- *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | +- FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +-} +- +-static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- struct firedtv_tuner_status stat; +- +- if (avc_tuner_status(fdtv, &stat)) +- return -EINVAL; +- +- *ber = stat.ber; +- return 0; +-} +- +-static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- struct firedtv_tuner_status stat; +- +- if (avc_tuner_status(fdtv, &stat)) +- return -EINVAL; +- +- *strength = stat.signal_strength << 8; +- return 0; +-} +- +-static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct firedtv *fdtv = fe->sec_priv; +- struct firedtv_tuner_status stat; +- +- if (avc_tuner_status(fdtv, &stat)) +- return -EINVAL; +- +- /* C/N[dB] = -10 * log10(snr / 65535) */ +- *snr = stat.carrier_noise_ratio * 257; +- return 0; +-} +- +-static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- return -EOPNOTSUPP; +-} +- +-static int fdtv_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct firedtv *fdtv = fe->sec_priv; +- +- return avc_tuner_dsd(fdtv, p); +-} +- +-void fdtv_frontend_init(struct firedtv *fdtv, const char *name) +-{ +- struct dvb_frontend_ops *ops = &fdtv->fe.ops; +- struct dvb_frontend_info *fi = &ops->info; +- +- ops->init = fdtv_dvb_init; +- ops->sleep = fdtv_sleep; +- +- ops->set_frontend = fdtv_set_frontend; +- +- ops->read_status = fdtv_read_status; +- ops->read_ber = fdtv_read_ber; +- ops->read_signal_strength = fdtv_read_signal_strength; +- ops->read_snr = fdtv_read_snr; +- ops->read_ucblocks = fdtv_read_uncorrected_blocks; +- +- ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd; +- ops->diseqc_send_burst = fdtv_diseqc_send_burst; +- ops->set_tone = fdtv_set_tone; +- ops->set_voltage = fdtv_set_voltage; +- +- switch (fdtv->type) { +- case FIREDTV_DVB_S: +- ops->delsys[0] = SYS_DVBS; +- +- fi->frequency_min = 950000; +- fi->frequency_max = 2150000; +- fi->frequency_stepsize = 125; +- fi->symbol_rate_min = 1000000; +- fi->symbol_rate_max = 40000000; +- +- fi->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; +- break; +- +- case FIREDTV_DVB_S2: +- ops->delsys[0] = SYS_DVBS; +- ops->delsys[1] = SYS_DVBS2; +- +- fi->frequency_min = 950000; +- fi->frequency_max = 2150000; +- fi->frequency_stepsize = 125; +- fi->symbol_rate_min = 1000000; +- fi->symbol_rate_max = 40000000; +- +- fi->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_2G_MODULATION; +- break; +- +- case FIREDTV_DVB_C: +- ops->delsys[0] = SYS_DVBC_ANNEX_A; +- +- fi->frequency_min = 47000000; +- fi->frequency_max = 866000000; +- fi->frequency_stepsize = 62500; +- fi->symbol_rate_min = 870000; +- fi->symbol_rate_max = 6900000; +- +- fi->caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | +- FE_CAN_QAM_256 | +- FE_CAN_QAM_AUTO; +- break; +- +- case FIREDTV_DVB_T: +- ops->delsys[0] = SYS_DVBT; +- +- fi->frequency_min = 49000000; +- fi->frequency_max = 861000000; +- fi->frequency_stepsize = 62500; +- +- fi->caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_2_3 | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO; +- break; +- +- default: +- dev_err(fdtv->device, "no frontend for model type %d\n", +- fdtv->type); +- } +- strcpy(fi->name, name); +- +- fdtv->fe.dvb = &fdtv->adapter; +- fdtv->fe.sec_priv = fdtv; +-} +diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c +deleted file mode 100644 +index 864b627..0000000 +--- a/drivers/media/dvb/firewire/firedtv-fw.c ++++ /dev/null +@@ -1,430 +0,0 @@ +-/* +- * FireDTV driver -- firewire I/O backend +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +- +-#include +- +-#include "firedtv.h" +- +-static LIST_HEAD(node_list); +-static DEFINE_SPINLOCK(node_list_lock); +- +-static inline struct fw_device *device_of(struct firedtv *fdtv) +-{ +- return fw_device(fdtv->device->parent); +-} +- +-static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, +- int tcode) +-{ +- struct fw_device *device = device_of(fdtv); +- int rcode, generation = device->generation; +- +- smp_rmb(); /* node_id vs. generation */ +- +- rcode = fw_run_transaction(device->card, tcode, device->node_id, +- generation, device->max_speed, addr, data, len); +- +- return rcode != RCODE_COMPLETE ? -EIO : 0; +-} +- +-int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data) +-{ +- return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); +-} +- +-int fdtv_read(struct firedtv *fdtv, u64 addr, void *data) +-{ +- return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); +-} +- +-int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +-{ +- return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); +-} +- +-#define ISO_HEADER_SIZE 4 +-#define CIP_HEADER_SIZE 8 +-#define MPEG2_TS_HEADER_SIZE 4 +-#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) +- +-#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */ +-#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE) +-#define N_PACKETS 64 /* buffer size */ +-#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) +-#define IRQ_INTERVAL 16 +- +-struct fdtv_ir_context { +- struct fw_iso_context *context; +- struct fw_iso_buffer buffer; +- int interrupt_packet; +- int current_packet; +- char *pages[N_PAGES]; +-}; +- +-static int queue_iso(struct fdtv_ir_context *ctx, int index) +-{ +- struct fw_iso_packet p; +- +- p.payload_length = MAX_PACKET_SIZE; +- p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1)); +- p.skip = 0; +- p.header_length = ISO_HEADER_SIZE; +- +- return fw_iso_context_queue(ctx->context, &p, &ctx->buffer, +- index * MAX_PACKET_SIZE); +-} +- +-static void handle_iso(struct fw_iso_context *context, u32 cycle, +- size_t header_length, void *header, void *data) +-{ +- struct firedtv *fdtv = data; +- struct fdtv_ir_context *ctx = fdtv->ir_context; +- __be32 *h, *h_end; +- int length, err, i = ctx->current_packet; +- char *p, *p_end; +- +- for (h = header, h_end = h + header_length / 4; h < h_end; h++) { +- length = be32_to_cpup(h) >> 16; +- if (unlikely(length > MAX_PACKET_SIZE)) { +- dev_err(fdtv->device, "length = %d\n", length); +- length = MAX_PACKET_SIZE; +- } +- +- p = ctx->pages[i / PACKETS_PER_PAGE] +- + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE; +- p_end = p + length; +- +- for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end; +- p += MPEG2_TS_SOURCE_PACKET_SIZE) +- dvb_dmx_swfilter_packets(&fdtv->demux, p, 1); +- +- err = queue_iso(ctx, i); +- if (unlikely(err)) +- dev_err(fdtv->device, "requeue failed\n"); +- +- i = (i + 1) & (N_PACKETS - 1); +- } +- fw_iso_context_queue_flush(ctx->context); +- ctx->current_packet = i; +-} +- +-int fdtv_start_iso(struct firedtv *fdtv) +-{ +- struct fdtv_ir_context *ctx; +- struct fw_device *device = device_of(fdtv); +- int i, err; +- +- ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); +- if (!ctx) +- return -ENOMEM; +- +- ctx->context = fw_iso_context_create(device->card, +- FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel, +- device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv); +- if (IS_ERR(ctx->context)) { +- err = PTR_ERR(ctx->context); +- goto fail_free; +- } +- +- err = fw_iso_buffer_init(&ctx->buffer, device->card, +- N_PAGES, DMA_FROM_DEVICE); +- if (err) +- goto fail_context_destroy; +- +- ctx->interrupt_packet = 0; +- ctx->current_packet = 0; +- +- for (i = 0; i < N_PAGES; i++) +- ctx->pages[i] = page_address(ctx->buffer.pages[i]); +- +- for (i = 0; i < N_PACKETS; i++) { +- err = queue_iso(ctx, i); +- if (err) +- goto fail; +- } +- +- err = fw_iso_context_start(ctx->context, -1, 0, +- FW_ISO_CONTEXT_MATCH_ALL_TAGS); +- if (err) +- goto fail; +- +- fdtv->ir_context = ctx; +- +- return 0; +-fail: +- fw_iso_buffer_destroy(&ctx->buffer, device->card); +-fail_context_destroy: +- fw_iso_context_destroy(ctx->context); +-fail_free: +- kfree(ctx); +- +- return err; +-} +- +-void fdtv_stop_iso(struct firedtv *fdtv) +-{ +- struct fdtv_ir_context *ctx = fdtv->ir_context; +- +- fw_iso_context_stop(ctx->context); +- fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); +- fw_iso_context_destroy(ctx->context); +- kfree(ctx); +-} +- +-static void handle_fcp(struct fw_card *card, struct fw_request *request, +- int tcode, int destination, int source, int generation, +- unsigned long long offset, void *payload, size_t length, +- void *callback_data) +-{ +- struct firedtv *f, *fdtv = NULL; +- struct fw_device *device; +- unsigned long flags; +- int su; +- +- if (length < 2 || (((u8 *)payload)[0] & 0xf0) != 0) +- return; +- +- su = ((u8 *)payload)[1] & 0x7; +- +- spin_lock_irqsave(&node_list_lock, flags); +- list_for_each_entry(f, &node_list, list) { +- device = device_of(f); +- if (device->generation != generation) +- continue; +- +- smp_rmb(); /* node_id vs. generation */ +- +- if (device->card == card && +- device->node_id == source && +- (f->subunit == su || (f->subunit == 0 && su == 0x7))) { +- fdtv = f; +- break; +- } +- } +- spin_unlock_irqrestore(&node_list_lock, flags); +- +- if (fdtv) +- avc_recv(fdtv, payload, length); +-} +- +-static struct fw_address_handler fcp_handler = { +- .length = CSR_FCP_END - CSR_FCP_RESPONSE, +- .address_callback = handle_fcp, +-}; +- +-static const struct fw_address_region fcp_region = { +- .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, +- .end = CSR_REGISTER_BASE + CSR_FCP_END, +-}; +- +-static const char * const model_names[] = { +- [FIREDTV_UNKNOWN] = "unknown type", +- [FIREDTV_DVB_S] = "FireDTV S/CI", +- [FIREDTV_DVB_C] = "FireDTV C/CI", +- [FIREDTV_DVB_T] = "FireDTV T/CI", +- [FIREDTV_DVB_S2] = "FireDTV S2 ", +-}; +- +-/* Adjust the template string if models with longer names appear. */ +-#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") +- +-static int node_probe(struct device *dev) +-{ +- struct firedtv *fdtv; +- char name[MAX_MODEL_NAME_LEN]; +- int name_len, i, err; +- +- fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); +- if (!fdtv) +- return -ENOMEM; +- +- dev_set_drvdata(dev, fdtv); +- fdtv->device = dev; +- fdtv->isochannel = -1; +- fdtv->voltage = 0xff; +- fdtv->tone = 0xff; +- +- mutex_init(&fdtv->avc_mutex); +- init_waitqueue_head(&fdtv->avc_wait); +- mutex_init(&fdtv->demux_mutex); +- INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); +- +- name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, +- name, sizeof(name)); +- for (i = ARRAY_SIZE(model_names); --i; ) +- if (strlen(model_names[i]) <= name_len && +- strncmp(name, model_names[i], name_len) == 0) +- break; +- fdtv->type = i; +- +- err = fdtv_register_rc(fdtv, dev); +- if (err) +- goto fail_free; +- +- spin_lock_irq(&node_list_lock); +- list_add_tail(&fdtv->list, &node_list); +- spin_unlock_irq(&node_list_lock); +- +- err = avc_identify_subunit(fdtv); +- if (err) +- goto fail; +- +- err = fdtv_dvb_register(fdtv, model_names[fdtv->type]); +- if (err) +- goto fail; +- +- avc_register_remote_control(fdtv); +- +- return 0; +-fail: +- spin_lock_irq(&node_list_lock); +- list_del(&fdtv->list); +- spin_unlock_irq(&node_list_lock); +- fdtv_unregister_rc(fdtv); +-fail_free: +- kfree(fdtv); +- +- return err; +-} +- +-static int node_remove(struct device *dev) +-{ +- struct firedtv *fdtv = dev_get_drvdata(dev); +- +- fdtv_dvb_unregister(fdtv); +- +- spin_lock_irq(&node_list_lock); +- list_del(&fdtv->list); +- spin_unlock_irq(&node_list_lock); +- +- fdtv_unregister_rc(fdtv); +- +- kfree(fdtv); +- return 0; +-} +- +-static void node_update(struct fw_unit *unit) +-{ +- struct firedtv *fdtv = dev_get_drvdata(&unit->device); +- +- if (fdtv->isochannel >= 0) +- cmp_establish_pp_connection(fdtv, fdtv->subunit, +- fdtv->isochannel); +-} +- +-#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ +- IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) +- +-#define DIGITAL_EVERYWHERE_OUI 0x001287 +-#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d +-#define AVC_SW_VERSION_ENTRY 0x010001 +- +-static const struct ieee1394_device_id fdtv_id_table[] = { +- { +- /* FloppyDTV S/CI and FloppyDTV S2 */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000024, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, { +- /* FloppyDTV T/CI */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000025, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, { +- /* FloppyDTV C/CI */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000026, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, { +- /* FireDTV S/CI and FloppyDTV S2 */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000034, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, { +- /* FireDTV T/CI */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000035, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, { +- /* FireDTV C/CI */ +- .match_flags = MATCH_FLAGS, +- .vendor_id = DIGITAL_EVERYWHERE_OUI, +- .model_id = 0x000036, +- .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, +- .version = AVC_SW_VERSION_ENTRY, +- }, {} +-}; +-MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); +- +-static struct fw_driver fdtv_driver = { +- .driver = { +- .owner = THIS_MODULE, +- .name = "firedtv", +- .bus = &fw_bus_type, +- .probe = node_probe, +- .remove = node_remove, +- }, +- .update = node_update, +- .id_table = fdtv_id_table, +-}; +- +-static int __init fdtv_init(void) +-{ +- int ret; +- +- ret = fw_core_add_address_handler(&fcp_handler, &fcp_region); +- if (ret < 0) +- return ret; +- +- ret = driver_register(&fdtv_driver.driver); +- if (ret < 0) +- fw_core_remove_address_handler(&fcp_handler); +- +- return ret; +-} +- +-static void __exit fdtv_exit(void) +-{ +- driver_unregister(&fdtv_driver.driver); +- fw_core_remove_address_handler(&fcp_handler); +-} +- +-module_init(fdtv_init); +-module_exit(fdtv_exit); +- +-MODULE_AUTHOR("Andreas Monitzer "); +-MODULE_AUTHOR("Ben Backx "); +-MODULE_DESCRIPTION("FireDTV DVB Driver"); +-MODULE_LICENSE("GPL"); +-MODULE_SUPPORTED_DEVICE("FireDTV DVB"); +diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c +deleted file mode 100644 +index f82d4a9..0000000 +--- a/drivers/media/dvb/firewire/firedtv-rc.c ++++ /dev/null +@@ -1,196 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "firedtv.h" +- +-/* fixed table with older keycodes, geared towards MythTV */ +-static const u16 oldtable[] = { +- +- /* code from device: 0x4501...0x451f */ +- +- KEY_ESC, +- KEY_F9, +- KEY_1, +- KEY_2, +- KEY_3, +- KEY_4, +- KEY_5, +- KEY_6, +- KEY_7, +- KEY_8, +- KEY_9, +- KEY_I, +- KEY_0, +- KEY_ENTER, +- KEY_RED, +- KEY_UP, +- KEY_GREEN, +- KEY_F10, +- KEY_SPACE, +- KEY_F11, +- KEY_YELLOW, +- KEY_DOWN, +- KEY_BLUE, +- KEY_Z, +- KEY_P, +- KEY_PAGEDOWN, +- KEY_LEFT, +- KEY_W, +- KEY_RIGHT, +- KEY_P, +- KEY_M, +- +- /* code from device: 0x4540...0x4542 */ +- +- KEY_R, +- KEY_V, +- KEY_C, +-}; +- +-/* user-modifiable table for a remote as sold in 2008 */ +-static const u16 keytable[] = { +- +- /* code from device: 0x0300...0x031f */ +- +- [0x00] = KEY_POWER, +- [0x01] = KEY_SLEEP, +- [0x02] = KEY_STOP, +- [0x03] = KEY_OK, +- [0x04] = KEY_RIGHT, +- [0x05] = KEY_1, +- [0x06] = KEY_2, +- [0x07] = KEY_3, +- [0x08] = KEY_LEFT, +- [0x09] = KEY_4, +- [0x0a] = KEY_5, +- [0x0b] = KEY_6, +- [0x0c] = KEY_UP, +- [0x0d] = KEY_7, +- [0x0e] = KEY_8, +- [0x0f] = KEY_9, +- [0x10] = KEY_DOWN, +- [0x11] = KEY_TITLE, /* "OSD" - fixme */ +- [0x12] = KEY_0, +- [0x13] = KEY_F20, /* "16:9" - fixme */ +- [0x14] = KEY_SCREEN, /* "FULL" - fixme */ +- [0x15] = KEY_MUTE, +- [0x16] = KEY_SUBTITLE, +- [0x17] = KEY_RECORD, +- [0x18] = KEY_TEXT, +- [0x19] = KEY_AUDIO, +- [0x1a] = KEY_RED, +- [0x1b] = KEY_PREVIOUS, +- [0x1c] = KEY_REWIND, +- [0x1d] = KEY_PLAYPAUSE, +- [0x1e] = KEY_NEXT, +- [0x1f] = KEY_VOLUMEUP, +- +- /* code from device: 0x0340...0x0354 */ +- +- [0x20] = KEY_CHANNELUP, +- [0x21] = KEY_F21, /* "4:3" - fixme */ +- [0x22] = KEY_TV, +- [0x23] = KEY_DVD, +- [0x24] = KEY_VCR, +- [0x25] = KEY_AUX, +- [0x26] = KEY_GREEN, +- [0x27] = KEY_YELLOW, +- [0x28] = KEY_BLUE, +- [0x29] = KEY_CHANNEL, /* "CH.LIST" */ +- [0x2a] = KEY_VENDOR, /* "CI" - fixme */ +- [0x2b] = KEY_VOLUMEDOWN, +- [0x2c] = KEY_CHANNELDOWN, +- [0x2d] = KEY_LAST, +- [0x2e] = KEY_INFO, +- [0x2f] = KEY_FORWARD, +- [0x30] = KEY_LIST, +- [0x31] = KEY_FAVORITES, +- [0x32] = KEY_MENU, +- [0x33] = KEY_EPG, +- [0x34] = KEY_EXIT, +-}; +- +-int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) +-{ +- struct input_dev *idev; +- int i, err; +- +- idev = input_allocate_device(); +- if (!idev) +- return -ENOMEM; +- +- fdtv->remote_ctrl_dev = idev; +- idev->name = "FireDTV remote control"; +- idev->dev.parent = dev; +- idev->evbit[0] = BIT_MASK(EV_KEY); +- idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); +- if (!idev->keycode) { +- err = -ENOMEM; +- goto fail; +- } +- idev->keycodesize = sizeof(keytable[0]); +- idev->keycodemax = ARRAY_SIZE(keytable); +- +- for (i = 0; i < ARRAY_SIZE(keytable); i++) +- set_bit(keytable[i], idev->keybit); +- +- err = input_register_device(idev); +- if (err) +- goto fail_free_keymap; +- +- return 0; +- +-fail_free_keymap: +- kfree(idev->keycode); +-fail: +- input_free_device(idev); +- return err; +-} +- +-void fdtv_unregister_rc(struct firedtv *fdtv) +-{ +- cancel_work_sync(&fdtv->remote_ctrl_work); +- kfree(fdtv->remote_ctrl_dev->keycode); +- input_unregister_device(fdtv->remote_ctrl_dev); +-} +- +-void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) +-{ +- struct input_dev *idev = fdtv->remote_ctrl_dev; +- u16 *keycode = idev->keycode; +- +- if (code >= 0x0300 && code <= 0x031f) +- code = keycode[code - 0x0300]; +- else if (code >= 0x0340 && code <= 0x0354) +- code = keycode[code - 0x0320]; +- else if (code >= 0x4501 && code <= 0x451f) +- code = oldtable[code - 0x4501]; +- else if (code >= 0x4540 && code <= 0x4542) +- code = oldtable[code - 0x4521]; +- else { +- printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " +- "from remote control\n", code); +- return; +- } +- +- input_report_key(idev, code, 1); +- input_sync(idev); +- input_report_key(idev, code, 0); +- input_sync(idev); +-} +diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h +deleted file mode 100644 +index 4fdcd8c..0000000 +--- a/drivers/media/dvb/firewire/firedtv.h ++++ /dev/null +@@ -1,168 +0,0 @@ +-/* +- * FireDTV driver (formerly known as FireSAT) +- * +- * Copyright (C) 2004 Andreas Monitzer +- * Copyright (C) 2008 Henrik Kurelid +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +- +-#ifndef _FIREDTV_H +-#define _FIREDTV_H +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-struct firedtv_tuner_status { +- unsigned active_system:8; +- unsigned searching:1; +- unsigned moving:1; +- unsigned no_rf:1; +- unsigned input:1; +- unsigned selected_antenna:7; +- unsigned ber:32; +- unsigned signal_strength:8; +- unsigned raster_frequency:2; +- unsigned rf_frequency:22; +- unsigned man_dep_info_length:8; +- unsigned front_end_error:1; +- unsigned antenna_error:1; +- unsigned front_end_power_status:1; +- unsigned power_supply:1; +- unsigned carrier_noise_ratio:16; +- unsigned power_supply_voltage:8; +- unsigned antenna_voltage:8; +- unsigned firewire_bus_voltage:8; +- unsigned ca_mmi:1; +- unsigned ca_pmt_reply:1; +- unsigned ca_date_time_request:1; +- unsigned ca_application_info:1; +- unsigned ca_module_present_status:1; +- unsigned ca_dvb_flag:1; +- unsigned ca_error_flag:1; +- unsigned ca_initialization_status:1; +-}; +- +-enum model_type { +- FIREDTV_UNKNOWN = 0, +- FIREDTV_DVB_S = 1, +- FIREDTV_DVB_C = 2, +- FIREDTV_DVB_T = 3, +- FIREDTV_DVB_S2 = 4, +-}; +- +-struct device; +-struct input_dev; +-struct fdtv_ir_context; +- +-struct firedtv { +- struct device *device; +- struct list_head list; +- +- struct dvb_adapter adapter; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- struct dmx_frontend frontend; +- struct dvb_net dvbnet; +- struct dvb_frontend fe; +- +- struct dvb_device *cadev; +- int ca_last_command; +- int ca_time_interval; +- +- struct mutex avc_mutex; +- wait_queue_head_t avc_wait; +- bool avc_reply_received; +- struct work_struct remote_ctrl_work; +- struct input_dev *remote_ctrl_dev; +- +- enum model_type type; +- char subunit; +- char isochannel; +- struct fdtv_ir_context *ir_context; +- +- fe_sec_voltage_t voltage; +- fe_sec_tone_mode_t tone; +- +- struct mutex demux_mutex; +- unsigned long channel_active; +- u16 channel_pid[16]; +- +- int avc_data_length; +- u8 avc_data[512]; +-}; +- +-/* firedtv-avc.c */ +-int avc_recv(struct firedtv *fdtv, void *data, size_t length); +-int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); +-struct dtv_frontend_properties; +-int avc_tuner_dsd(struct firedtv *fdtv, struct dtv_frontend_properties *params); +-int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); +-int avc_tuner_get_ts(struct firedtv *fdtv); +-int avc_identify_subunit(struct firedtv *fdtv); +-struct dvb_diseqc_master_cmd; +-int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, +- char conttone, char nrdiseq, +- struct dvb_diseqc_master_cmd *diseqcmd); +-void avc_remote_ctrl_work(struct work_struct *work); +-int avc_register_remote_control(struct firedtv *fdtv); +-int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +-int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); +-int avc_ca_reset(struct firedtv *fdtv); +-int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); +-int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); +-int avc_ca_enter_menu(struct firedtv *fdtv); +-int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); +-int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); +-void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); +- +-/* firedtv-ci.c */ +-int fdtv_ca_register(struct firedtv *fdtv); +-void fdtv_ca_release(struct firedtv *fdtv); +- +-/* firedtv-dvb.c */ +-int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); +-int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +-int fdtv_dvb_register(struct firedtv *fdtv, const char *name); +-void fdtv_dvb_unregister(struct firedtv *fdtv); +- +-/* firedtv-fe.c */ +-void fdtv_frontend_init(struct firedtv *fdtv, const char *name); +- +-/* firedtv-fw.c */ +-int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data); +-int fdtv_read(struct firedtv *fdtv, u64 addr, void *data); +-int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len); +-int fdtv_start_iso(struct firedtv *fdtv); +-void fdtv_stop_iso(struct firedtv *fdtv); +- +-/* firedtv-rc.c */ +-#ifdef CONFIG_DVB_FIREDTV_INPUT +-int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); +-void fdtv_unregister_rc(struct firedtv *fdtv); +-void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); +-#else +-static inline int fdtv_register_rc(struct firedtv *fdtv, +- struct device *dev) { return 0; } +-static inline void fdtv_unregister_rc(struct firedtv *fdtv) {} +-static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {} +-#endif +- +-#endif /* _FIREDTV_H */ +diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig +deleted file mode 100644 +index ebb5ed7..0000000 +--- a/drivers/media/dvb/frontends/Kconfig ++++ /dev/null +@@ -1,706 +0,0 @@ +-config DVB_FE_CUSTOMISE +- bool "Customise the frontend modules to build" +- depends on DVB_CORE +- default y if EXPERT +- help +- This allows the user to select/deselect frontend drivers for their +- hardware from the build. +- +- Use this option with care as deselecting frontends which are in fact +- necessary will result in DVB devices which cannot be tuned due to lack +- of driver support. +- +- If unsure say N. +- +-menu "Customise DVB Frontends" +- visible if DVB_FE_CUSTOMISE +- +-comment "Multistandard (satellite) frontends" +- depends on DVB_CORE +- +-config DVB_STB0899 +- tristate "STB0899 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S/S2/DSS Multistandard demodulator. Say Y when you want +- to support this demodulator based frontends +- +-config DVB_STB6100 +- tristate "STB6100 based tuners" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A Silicon tuner from ST used in conjunction with the STB0899 +- demodulator. Say Y when you want to support this tuner. +- +-config DVB_STV090x +- tristate "STV0900/STV0903(A/B) based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- DVB-S/S2/DSS Multistandard Professional/Broadcast demodulators. +- Say Y when you want to support these frontends. +- +-config DVB_STV6110x +- tristate "STV6110/(A) based tuners" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A Silicon tuner that supports DVB-S and DVB-S2 modes +- +-comment "Multistandard (cable + terrestrial) frontends" +- depends on DVB_CORE +- +-config DVB_DRXK +- tristate "Micronas DRXK based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Micronas DRX-K DVB-C/T demodulator. +- +- Say Y when you want to support this frontend. +- +-config DVB_TDA18271C2DD +- tristate "NXP TDA18271C2 silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- NXP TDA18271 silicon tuner. +- +- Say Y when you want to support this tuner. +- +-comment "DVB-S (satellite) frontends" +- depends on DVB_CORE +- +-config DVB_CX24110 +- tristate "Conexant CX24110 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_CX24123 +- tristate "Conexant CX24123 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_MT312 +- tristate "Zarlink VP310/MT312/ZL10313 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_ZL10036 +- tristate "Zarlink ZL10036 silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_ZL10039 +- tristate "Zarlink ZL10039 silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_S5H1420 +- tristate "Samsung S5H1420 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_STV0288 +- tristate "ST STV0288 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_STB6000 +- tristate "ST STB6000 silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S silicon tuner module. Say Y when you want to support this tuner. +- +-config DVB_STV0299 +- tristate "ST STV0299 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_STV6110 +- tristate "ST STV6110 silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S silicon tuner module. Say Y when you want to support this tuner. +- +-config DVB_STV0900 +- tristate "ST STV0900 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S/S2 demodulator. Say Y when you want to support this frontend. +- +-config DVB_TDA8083 +- tristate "Philips TDA8083 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA10086 +- tristate "Philips TDA10086 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA8261 +- tristate "Philips TDA8261 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_VES1X93 +- tristate "VLSI VES1893 or VES1993 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_TUNER_ITD1000 +- tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_TUNER_CX24113 +- tristate "Conexant CX24113/CX24128 tuner for DVB-S/DSS" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +- +-config DVB_TDA826X +- tristate "Philips TDA826X silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S silicon tuner module. Say Y when you want to support this tuner. +- +-config DVB_TUA6100 +- tristate "Infineon TUA6100 PLL" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S PLL chip. +- +-config DVB_CX24116 +- tristate "Conexant CX24116 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S/S2 tuner module. Say Y when you want to support this frontend. +- +-config DVB_SI21XX +- tristate "Silicon Labs SI21XX based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_DS3000 +- tristate "Montage Tehnology DS3000 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S/S2 tuner module. Say Y when you want to support this frontend. +- +-config DVB_MB86A16 +- tristate "Fujitsu MB86A16 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S/DSS Direct Conversion reveiver. +- Say Y when you want to support this frontend. +- +-config DVB_TDA10071 +- tristate "NXP TDA10071" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Say Y when you want to support this frontend. +- +-comment "DVB-T (terrestrial) frontends" +- depends on DVB_CORE +- +-config DVB_SP8870 +- tristate "Spase sp8870 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +- This driver needs external firmware. Please use the command +- "/Documentation/dvb/get_dvb_firmware sp8870" to +- download/extract it, and then copy it to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +-config DVB_SP887X +- tristate "Spase sp887x based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +- This driver needs external firmware. Please use the command +- "/Documentation/dvb/get_dvb_firmware sp887x" to +- download/extract it, and then copy it to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +-config DVB_CX22700 +- tristate "Conexant CX22700 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_CX22702 +- tristate "Conexant cx22702 demodulator (OFDM)" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_S5H1432 +- tristate "Samsung s5h1432 demodulator (OFDM)" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_DRXD +- tristate "Micronas DRXD driver" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +- Note: this driver was based on vendor driver reference code (released +- under the GPL) as opposed to the existing drx397xd driver, which +- was written via reverse engineering. +- +-config DVB_L64781 +- tristate "LSI L64781" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA1004X +- tristate "Philips TDA10045H/TDA10046H based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +- This driver needs external firmware. Please use the commands +- "/Documentation/dvb/get_dvb_firmware tda10045", +- "/Documentation/dvb/get_dvb_firmware tda10046" to +- download/extract them, and then copy them to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +-config DVB_NXT6000 +- tristate "NxtWave Communications NXT6000 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_MT352 +- tristate "Zarlink MT352 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_ZL10353 +- tristate "Zarlink ZL10353 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_DIB3000MB +- tristate "DiBcom 3000M-B" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Designed for mobile usage. Say Y when you want +- to support this frontend. +- +-config DVB_DIB3000MC +- tristate "DiBcom 3000P/M-C" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Designed for mobile usage. Say Y when you want +- to support this frontend. +- +-config DVB_DIB7000M +- tristate "DiBcom 7000MA/MB/PA/PB/MC" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Designed for mobile usage. Say Y when you want +- to support this frontend. +- +-config DVB_DIB7000P +- tristate "DiBcom 7000PC" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Designed for mobile usage. Say Y when you want +- to support this frontend. +- +-config DVB_DIB9000 +- tristate "DiBcom 9000" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Designed for mobile usage. Say Y when you want +- to support this frontend. +- +-config DVB_TDA10048 +- tristate "Philips TDA10048HN based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. Say Y when you want to support this frontend. +- +-config DVB_AF9013 +- tristate "Afatech AF9013 demodulator" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Say Y when you want to support this frontend. +- +-config DVB_EC100 +- tristate "E3C EC100" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Say Y when you want to support this frontend. +- +-config DVB_HD29L2 +- tristate "HDIC HD29L2" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Say Y when you want to support this frontend. +- +-config DVB_STV0367 +- tristate "ST STV0367 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T/C tuner module. Say Y when you want to support this frontend. +- +-config DVB_CXD2820R +- tristate "Sony CXD2820R" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Say Y when you want to support this frontend. +- +-comment "DVB-C (cable) frontends" +- depends on DVB_CORE +- +-config DVB_VES1820 +- tristate "VLSI VES1820 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-C tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA10021 +- tristate "Philips TDA10021 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-C tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA10023 +- tristate "Philips TDA10023 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-C tuner module. Say Y when you want to support this frontend. +- +-config DVB_STV0297 +- tristate "ST STV0297 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-C tuner module. Say Y when you want to support this frontend. +- +-comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends" +- depends on DVB_CORE +- +-config DVB_NXT200X +- tristate "NxtWave Communications NXT2002/NXT2004 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +- This driver needs external firmware. Please use the commands +- "/Documentation/dvb/get_dvb_firmware nxt2002" and +- "/Documentation/dvb/get_dvb_firmware nxt2004" to +- download/extract them, and then copy them to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +-config DVB_OR51211 +- tristate "Oren OR51211 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB tuner module. Say Y when you want to support this frontend. +- +- This driver needs external firmware. Please use the command +- "/Documentation/dvb/get_dvb_firmware or51211" to +- download it, and then copy it to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +-config DVB_OR51132 +- tristate "Oren OR51132 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +- This driver needs external firmware. Please use the commands +- "/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or +- "/Documentation/dvb/get_dvb_firmware or51132_qam" to +- download firmwares for 8VSB and QAM64/256, respectively. Copy them to +- /usr/lib/hotplug/firmware or /lib/firmware (depending on +- configuration of firmware hotplug). +- +-config DVB_BCM3510 +- tristate "Broadcom BCM3510" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to +- support this frontend. +- +-config DVB_LGDT330X +- tristate "LG Electronics LGDT3302/LGDT3303 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +-config DVB_LGDT3305 +- tristate "LG Electronics LGDT3304 and LGDT3305 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +-config DVB_S5H1409 +- tristate "Samsung S5H1409 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +-config DVB_AU8522 +- tristate "Auvitek AU8522 based" +- depends on DVB_CORE && I2C && VIDEO_V4L2 +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +-config DVB_S5H1411 +- tristate "Samsung S5H1411 based" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want +- to support this frontend. +- +-comment "ISDB-T (terrestrial) frontends" +- depends on DVB_CORE +- +-config DVB_S921 +- tristate "Sharp S921 frontend" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- AN ISDB-T DQPSK, QPSK, 16QAM and 64QAM 1seg tuner module. +- Say Y when you want to support this frontend. +- +-config DVB_DIB8000 +- tristate "DiBcom 8000MB/MC" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A driver for DiBcom's DiB8000 ISDB-T/ISDB-Tsb demodulator. +- Say Y when you want to support this frontend. +- +-config DVB_MB86A20S +- tristate "Fujitsu mb86a20s" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A driver for Fujitsu mb86a20s ISDB-T/ISDB-Tsb demodulator. +- Say Y when you want to support this frontend. +- +-comment "Digital terrestrial only tuners/PLL" +- depends on DVB_CORE +- +-config DVB_PLL +- tristate "Generic I2C PLL based tuners" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- This module drives a number of tuners based on PLL chips with a +- common I2C interface. Say Y when you want to support these tuners. +- +-config DVB_TUNER_DIB0070 +- tristate "DiBcom DiB0070 silicon base-band tuner" +- depends on I2C +- default m if DVB_FE_CUSTOMISE +- help +- A driver for the silicon baseband tuner DiB0070 from DiBcom. +- This device is only used inside a SiP called together with a +- demodulator for now. +- +-config DVB_TUNER_DIB0090 +- tristate "DiBcom DiB0090 silicon base-band tuner" +- depends on I2C +- default m if DVB_FE_CUSTOMISE +- help +- A driver for the silicon baseband tuner DiB0090 from DiBcom. +- This device is only used inside a SiP called together with a +- demodulator for now. +- +-comment "SEC control devices for DVB-S" +- depends on DVB_CORE +- +-config DVB_LNBP21 +- tristate "LNBP21/LNBH24 SEC controllers" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An SEC control chips. +- +-config DVB_LNBP22 +- tristate "LNBP22 SEC controllers" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- LNB power supply and control voltage +- regulator chip with step-up converter +- and I2C interface. +- Say Y when you want to support this chip. +- +-config DVB_ISL6405 +- tristate "ISL6405 SEC controller" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An SEC control chip. +- +-config DVB_ISL6421 +- tristate "ISL6421 SEC controller" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- An SEC control chip. +- +-config DVB_ISL6423 +- tristate "ISL6423 SEC controller" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A SEC controller chip from Intersil +- +-config DVB_A8293 +- tristate "Allegro A8293" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- +-config DVB_LGS8GL5 +- tristate "Silicon Legend LGS-8GL5 demodulator (OFDM)" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DMB-TH tuner module. Say Y when you want to support this frontend. +- +-config DVB_LGS8GXX +- tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" +- depends on DVB_CORE && I2C +- select FW_LOADER +- default m if DVB_FE_CUSTOMISE +- help +- A DMB-TH tuner module. Say Y when you want to support this frontend. +- +-config DVB_ATBM8830 +- tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DMB-TH tuner module. Say Y when you want to support this frontend. +- +-config DVB_TDA665x +- tristate "TDA665x tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- Support for tuner modules based on Philips TDA6650/TDA6651 chips. +- Say Y when you want to support this chip. +- +- Currently supported tuners: +- * Panasonic ENV57H12D5 (ET-50DT) +- +-config DVB_IX2505V +- tristate "Sharp IX2505V silicon tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-S tuner module. Say Y when you want to support this frontend. +- +-config DVB_IT913X_FE +- tristate "it913x frontend and it9137 tuner" +- depends on DVB_CORE && I2C +- default m if DVB_FE_CUSTOMISE +- help +- A DVB-T tuner module. +- Say Y when you want to support this frontend. +- +-comment "Tools to develop new frontends" +- +-config DVB_DUMMY_FE +- tristate "Dummy frontend driver" +- default n +-endmenu +diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile +deleted file mode 100644 +index 00a2063..0000000 +--- a/drivers/media/dvb/frontends/Makefile ++++ /dev/null +@@ -1,99 +0,0 @@ +-# +-# Makefile for the kernel DVB frontend device drivers. +-# +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ +-ccflags-y += -Idrivers/media/common/tuners/ +- +-stb0899-objs = stb0899_drv.o stb0899_algo.o +-stv0900-objs = stv0900_core.o stv0900_sw.o +-au8522-objs = au8522_dig.o au8522_decoder.o +-drxd-objs = drxd_firm.o drxd_hard.o +-cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o +-drxk-objs := drxk_hard.o +- +-obj-$(CONFIG_DVB_PLL) += dvb-pll.o +-obj-$(CONFIG_DVB_STV0299) += stv0299.o +-obj-$(CONFIG_DVB_STB0899) += stb0899.o +-obj-$(CONFIG_DVB_STB6100) += stb6100.o +-obj-$(CONFIG_DVB_SP8870) += sp8870.o +-obj-$(CONFIG_DVB_CX22700) += cx22700.o +-obj-$(CONFIG_DVB_S5H1432) += s5h1432.o +-obj-$(CONFIG_DVB_CX24110) += cx24110.o +-obj-$(CONFIG_DVB_TDA8083) += tda8083.o +-obj-$(CONFIG_DVB_L64781) += l64781.o +-obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o +-obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o +-obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o +-obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o +-obj-$(CONFIG_DVB_DIB8000) += dib8000.o dibx000_common.o +-obj-$(CONFIG_DVB_DIB9000) += dib9000.o dibx000_common.o +-obj-$(CONFIG_DVB_MT312) += mt312.o +-obj-$(CONFIG_DVB_VES1820) += ves1820.o +-obj-$(CONFIG_DVB_VES1X93) += ves1x93.o +-obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o +-obj-$(CONFIG_DVB_SP887X) += sp887x.o +-obj-$(CONFIG_DVB_NXT6000) += nxt6000.o +-obj-$(CONFIG_DVB_MT352) += mt352.o +-obj-$(CONFIG_DVB_ZL10036) += zl10036.o +-obj-$(CONFIG_DVB_ZL10039) += zl10039.o +-obj-$(CONFIG_DVB_ZL10353) += zl10353.o +-obj-$(CONFIG_DVB_CX22702) += cx22702.o +-obj-$(CONFIG_DVB_DRXD) += drxd.o +-obj-$(CONFIG_DVB_TDA10021) += tda10021.o +-obj-$(CONFIG_DVB_TDA10023) += tda10023.o +-obj-$(CONFIG_DVB_STV0297) += stv0297.o +-obj-$(CONFIG_DVB_NXT200X) += nxt200x.o +-obj-$(CONFIG_DVB_OR51211) += or51211.o +-obj-$(CONFIG_DVB_OR51132) += or51132.o +-obj-$(CONFIG_DVB_BCM3510) += bcm3510.o +-obj-$(CONFIG_DVB_S5H1420) += s5h1420.o +-obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o +-obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o +-obj-$(CONFIG_DVB_CX24123) += cx24123.o +-obj-$(CONFIG_DVB_LNBP21) += lnbp21.o +-obj-$(CONFIG_DVB_LNBP22) += lnbp22.o +-obj-$(CONFIG_DVB_ISL6405) += isl6405.o +-obj-$(CONFIG_DVB_ISL6421) += isl6421.o +-obj-$(CONFIG_DVB_TDA10086) += tda10086.o +-obj-$(CONFIG_DVB_TDA826X) += tda826x.o +-obj-$(CONFIG_DVB_TDA8261) += tda8261.o +-obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o +-obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o +-obj-$(CONFIG_DVB_TUA6100) += tua6100.o +-obj-$(CONFIG_DVB_S5H1409) += s5h1409.o +-obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o +-obj-$(CONFIG_DVB_AU8522) += au8522.o +-obj-$(CONFIG_DVB_TDA10048) += tda10048.o +-obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o +-obj-$(CONFIG_DVB_S5H1411) += s5h1411.o +-obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +-obj-$(CONFIG_DVB_TDA665x) += tda665x.o +-obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o +-obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o +-obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o +-obj-$(CONFIG_DVB_AF9013) += af9013.o +-obj-$(CONFIG_DVB_CX24116) += cx24116.o +-obj-$(CONFIG_DVB_SI21XX) += si21xx.o +-obj-$(CONFIG_DVB_STV0288) += stv0288.o +-obj-$(CONFIG_DVB_STB6000) += stb6000.o +-obj-$(CONFIG_DVB_S921) += s921.o +-obj-$(CONFIG_DVB_STV6110) += stv6110.o +-obj-$(CONFIG_DVB_STV0900) += stv0900.o +-obj-$(CONFIG_DVB_STV090x) += stv090x.o +-obj-$(CONFIG_DVB_STV6110x) += stv6110x.o +-obj-$(CONFIG_DVB_ISL6423) += isl6423.o +-obj-$(CONFIG_DVB_EC100) += ec100.o +-obj-$(CONFIG_DVB_HD29L2) += hd29l2.o +-obj-$(CONFIG_DVB_DS3000) += ds3000.o +-obj-$(CONFIG_DVB_MB86A16) += mb86a16.o +-obj-$(CONFIG_DVB_MB86A20S) += mb86a20s.o +-obj-$(CONFIG_DVB_IX2505V) += ix2505v.o +-obj-$(CONFIG_DVB_STV0367) += stv0367.o +-obj-$(CONFIG_DVB_CXD2820R) += cxd2820r.o +-obj-$(CONFIG_DVB_DRXK) += drxk.o +-obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o +-obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o +-obj-$(CONFIG_DVB_A8293) += a8293.o +-obj-$(CONFIG_DVB_TDA10071) += tda10071.o +- +diff --git a/drivers/media/dvb/frontends/a8293.c b/drivers/media/dvb/frontends/a8293.c +deleted file mode 100644 +index bb56497..0000000 +--- a/drivers/media/dvb/frontends/a8293.c ++++ /dev/null +@@ -1,184 +0,0 @@ +-/* +- * Allegro A8293 SEC driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#include "dvb_frontend.h" +-#include "a8293.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define LOG_PREFIX "a8293" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (debug) \ +- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +- +-struct a8293_priv { +- struct i2c_adapter *i2c; +- const struct a8293_config *cfg; +- u8 reg[2]; +-}; +- +-static int a8293_i2c(struct a8293_priv *priv, u8 *val, int len, bool rd) +-{ +- int ret; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->cfg->i2c_addr, +- .len = len, +- .buf = val, +- } +- }; +- +- if (rd) +- msg[0].flags = I2C_M_RD; +- else +- msg[0].flags = 0; +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c failed=%d rd=%d", ret, rd); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-static int a8293_wr(struct a8293_priv *priv, u8 *val, int len) +-{ +- return a8293_i2c(priv, val, len, 0); +-} +- +-static int a8293_rd(struct a8293_priv *priv, u8 *val, int len) +-{ +- return a8293_i2c(priv, val, len, 1); +-} +- +-static int a8293_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t fe_sec_voltage) +-{ +- struct a8293_priv *priv = fe->sec_priv; +- int ret; +- +- dbg("%s: fe_sec_voltage=%d", __func__, fe_sec_voltage); +- +- switch (fe_sec_voltage) { +- case SEC_VOLTAGE_OFF: +- /* ENB=0 */ +- priv->reg[0] = 0x10; +- break; +- case SEC_VOLTAGE_13: +- /* VSEL0=1, VSEL1=0, VSEL2=0, VSEL3=0, ENB=1*/ +- priv->reg[0] = 0x31; +- break; +- case SEC_VOLTAGE_18: +- /* VSEL0=0, VSEL1=0, VSEL2=0, VSEL3=1, ENB=1*/ +- priv->reg[0] = 0x38; +- break; +- default: +- ret = -EINVAL; +- goto err; +- }; +- +- ret = a8293_wr(priv, &priv->reg[0], 1); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static void a8293_release_sec(struct dvb_frontend *fe) +-{ +- dbg("%s:", __func__); +- +- a8293_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, const struct a8293_config *cfg) +-{ +- int ret; +- struct a8293_priv *priv = NULL; +- u8 buf[2]; +- +- /* allocate memory for the internal priv */ +- priv = kzalloc(sizeof(struct a8293_priv), GFP_KERNEL); +- if (priv == NULL) { +- ret = -ENOMEM; +- goto err; +- } +- +- /* setup the priv */ +- priv->i2c = i2c; +- priv->cfg = cfg; +- fe->sec_priv = priv; +- +- /* check if the SEC is there */ +- ret = a8293_rd(priv, buf, 2); +- if (ret) +- goto err; +- +- /* ENB=0 */ +- priv->reg[0] = 0x10; +- ret = a8293_wr(priv, &priv->reg[1], 1); +- if (ret) +- goto err; +- +- /* TMODE=0, TGATE=1 */ +- priv->reg[1] = 0x82; +- ret = a8293_wr(priv, &priv->reg[1], 1); +- if (ret) +- goto err; +- +- info("Allegro A8293 SEC attached."); +- +- fe->ops.release_sec = a8293_release_sec; +- +- /* override frontend ops */ +- fe->ops.set_voltage = a8293_set_voltage; +- +- return fe; +-err: +- dbg("%s: failed=%d", __func__, ret); +- kfree(priv); +- return NULL; +-} +-EXPORT_SYMBOL(a8293_attach); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Allegro A8293 SEC driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/a8293.h b/drivers/media/dvb/frontends/a8293.h +deleted file mode 100644 +index ed29e55..0000000 +--- a/drivers/media/dvb/frontends/a8293.h ++++ /dev/null +@@ -1,41 +0,0 @@ +-/* +- * Allegro A8293 SEC driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#ifndef A8293_H +-#define A8293_H +- +-struct a8293_config { +- u8 i2c_addr; +-}; +- +-#if defined(CONFIG_DVB_A8293) || \ +- (defined(CONFIG_DVB_A8293_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, const struct a8293_config *cfg); +-#else +-static inline struct dvb_frontend *a8293_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, const struct a8293_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* A8293_H */ +diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c +deleted file mode 100644 +index 6bcbcf5..0000000 +--- a/drivers/media/dvb/frontends/af9013.c ++++ /dev/null +@@ -1,1527 +0,0 @@ +-/* +- * Afatech AF9013 demodulator driver +- * +- * Copyright (C) 2007 Antti Palosaari +- * Copyright (C) 2011 Antti Palosaari +- * +- * Thanks to Afatech who kindly provided information. +- * +- * 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 "af9013_priv.h" +- +-int af9013_debug; +-module_param_named(debug, af9013_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-struct af9013_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend fe; +- struct af9013_config config; +- +- /* tuner/demod RF and IF AGC limits used for signal strength calc */ +- u8 signal_strength_en, rf_50, rf_80, if_50, if_80; +- u16 signal_strength; +- u32 ber; +- u32 ucblocks; +- u16 snr; +- u32 bandwidth_hz; +- fe_status_t fe_status; +- unsigned long set_frontend_jiffies; +- unsigned long read_status_jiffies; +- bool first_tune; +- bool i2c_gate_state; +- unsigned int statistics_step:3; +- struct delayed_work statistics_work; +-}; +- +-/* write multiple registers */ +-static int af9013_wr_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg, +- const u8 *val, int len) +-{ +- int ret; +- u8 buf[3+len]; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->config.i2c_addr, +- .flags = 0, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- buf[0] = (reg >> 8) & 0xff; +- buf[1] = (reg >> 0) & 0xff; +- buf[2] = mbox; +- memcpy(&buf[3], val, len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c wr failed=%d reg=%04x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* read multiple registers */ +-static int af9013_rd_regs_i2c(struct af9013_state *priv, u8 mbox, u16 reg, +- u8 *val, int len) +-{ +- int ret; +- u8 buf[3]; +- struct i2c_msg msg[2] = { +- { +- .addr = priv->config.i2c_addr, +- .flags = 0, +- .len = 3, +- .buf = buf, +- }, { +- .addr = priv->config.i2c_addr, +- .flags = I2C_M_RD, +- .len = len, +- .buf = val, +- } +- }; +- +- buf[0] = (reg >> 8) & 0xff; +- buf[1] = (reg >> 0) & 0xff; +- buf[2] = mbox; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- ret = 0; +- } else { +- warn("i2c rd failed=%d reg=%04x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* write multiple registers */ +-static int af9013_wr_regs(struct af9013_state *priv, u16 reg, const u8 *val, +- int len) +-{ +- int ret, i; +- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(1 << 0); +- +- if ((priv->config.ts_mode == AF9013_TS_USB) && +- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) { +- mbox |= ((len - 1) << 2); +- ret = af9013_wr_regs_i2c(priv, mbox, reg, val, len); +- } else { +- for (i = 0; i < len; i++) { +- ret = af9013_wr_regs_i2c(priv, mbox, reg+i, val+i, 1); +- if (ret) +- goto err; +- } +- } +- +-err: +- return 0; +-} +- +-/* read multiple registers */ +-static int af9013_rd_regs(struct af9013_state *priv, u16 reg, u8 *val, int len) +-{ +- int ret, i; +- u8 mbox = (0 << 7)|(0 << 6)|(1 << 1)|(0 << 0); +- +- if ((priv->config.ts_mode == AF9013_TS_USB) && +- ((reg & 0xff00) != 0xff00) && ((reg & 0xff00) != 0xae00)) { +- mbox |= ((len - 1) << 2); +- ret = af9013_rd_regs_i2c(priv, mbox, reg, val, len); +- } else { +- for (i = 0; i < len; i++) { +- ret = af9013_rd_regs_i2c(priv, mbox, reg+i, val+i, 1); +- if (ret) +- goto err; +- } +- } +- +-err: +- return 0; +-} +- +-/* write single register */ +-static int af9013_wr_reg(struct af9013_state *priv, u16 reg, u8 val) +-{ +- return af9013_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +-static int af9013_rd_reg(struct af9013_state *priv, u16 reg, u8 *val) +-{ +- return af9013_rd_regs(priv, reg, val, 1); +-} +- +-static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val, +- u8 len) +-{ +- u8 mbox = (1 << 7)|(1 << 6)|((len - 1) << 2)|(1 << 1)|(1 << 0); +- return af9013_wr_regs_i2c(state, mbox, reg, val, len); +-} +- +-static int af9013_wr_reg_bits(struct af9013_state *state, u16 reg, int pos, +- int len, u8 val) +-{ +- int ret; +- u8 tmp, mask; +- +- /* no need for read if whole reg is written */ +- if (len != 8) { +- ret = af9013_rd_reg(state, reg, &tmp); +- if (ret) +- return ret; +- +- mask = (0xff >> (8 - len)) << pos; +- val <<= pos; +- tmp &= ~mask; +- val |= tmp; +- } +- +- return af9013_wr_reg(state, reg, val); +-} +- +-static int af9013_rd_reg_bits(struct af9013_state *state, u16 reg, int pos, +- int len, u8 *val) +-{ +- int ret; +- u8 tmp; +- +- ret = af9013_rd_reg(state, reg, &tmp); +- if (ret) +- return ret; +- +- *val = (tmp >> pos); +- *val &= (0xff >> (8 - len)); +- +- return 0; +-} +- +-static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval) +-{ +- int ret; +- u8 pos; +- u16 addr; +- +- dbg("%s: gpio=%d gpioval=%02x", __func__, gpio, gpioval); +- +- /* +- * GPIO0 & GPIO1 0xd735 +- * GPIO2 & GPIO3 0xd736 +- */ +- +- switch (gpio) { +- case 0: +- case 1: +- addr = 0xd735; +- break; +- case 2: +- case 3: +- addr = 0xd736; +- break; +- +- default: +- err("invalid gpio:%d\n", gpio); +- ret = -EINVAL; +- goto err; +- }; +- +- switch (gpio) { +- case 0: +- case 2: +- pos = 0; +- break; +- case 1: +- case 3: +- default: +- pos = 4; +- break; +- }; +- +- ret = af9013_wr_reg_bits(state, addr, pos, 4, gpioval); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static u32 af913_div(u32 a, u32 b, u32 x) +-{ +- u32 r = 0, c = 0, i; +- +- dbg("%s: a=%d b=%d x=%d", __func__, a, b, x); +- +- if (a > b) { +- c = a / b; +- a = a - c * b; +- } +- +- for (i = 0; i < x; i++) { +- if (a >= b) { +- r += 1; +- a -= b; +- } +- a <<= 1; +- r <<= 1; +- } +- r = (c << (u32)x) + r; +- +- dbg("%s: a=%d b=%d x=%d r=%x", __func__, a, b, x, r); +- return r; +-} +- +-static int af9013_power_ctrl(struct af9013_state *state, u8 onoff) +-{ +- int ret, i; +- u8 tmp; +- +- dbg("%s: onoff=%d", __func__, onoff); +- +- /* enable reset */ +- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 1); +- if (ret) +- goto err; +- +- /* start reset mechanism */ +- ret = af9013_wr_reg(state, 0xaeff, 1); +- if (ret) +- goto err; +- +- /* wait reset performs */ +- for (i = 0; i < 150; i++) { +- ret = af9013_rd_reg_bits(state, 0xd417, 1, 1, &tmp); +- if (ret) +- goto err; +- +- if (tmp) +- break; /* reset done */ +- +- usleep_range(5000, 25000); +- } +- +- if (!tmp) +- return -ETIMEDOUT; +- +- if (onoff) { +- /* clear reset */ +- ret = af9013_wr_reg_bits(state, 0xd417, 1, 1, 0); +- if (ret) +- goto err; +- +- /* disable reset */ +- ret = af9013_wr_reg_bits(state, 0xd417, 4, 1, 0); +- +- /* power on */ +- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 0); +- } else { +- /* power off */ +- ret = af9013_wr_reg_bits(state, 0xd73a, 3, 1, 1); +- } +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_statistics_ber_unc_start(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- +- dbg("%s", __func__); +- +- /* reset and start BER counter */ +- ret = af9013_wr_reg_bits(state, 0xd391, 4, 1, 1); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_statistics_ber_unc_result(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[5]; +- +- dbg("%s", __func__); +- +- /* check if error bit count is ready */ +- ret = af9013_rd_reg_bits(state, 0xd391, 4, 1, &buf[0]); +- if (ret) +- goto err; +- +- if (!buf[0]) { +- dbg("%s: not ready", __func__); +- return 0; +- } +- +- ret = af9013_rd_regs(state, 0xd387, buf, 5); +- if (ret) +- goto err; +- +- state->ber = (buf[2] << 16) | (buf[1] << 8) | buf[0]; +- state->ucblocks += (buf[4] << 8) | buf[3]; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_statistics_snr_start(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- +- dbg("%s", __func__); +- +- /* start SNR meas */ +- ret = af9013_wr_reg_bits(state, 0xd2e1, 3, 1, 1); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_statistics_snr_result(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret, i, len; +- u8 buf[3], tmp; +- u32 snr_val; +- const struct af9013_snr *uninitialized_var(snr_lut); +- +- dbg("%s", __func__); +- +- /* check if SNR ready */ +- ret = af9013_rd_reg_bits(state, 0xd2e1, 3, 1, &tmp); +- if (ret) +- goto err; +- +- if (!tmp) { +- dbg("%s: not ready", __func__); +- return 0; +- } +- +- /* read value */ +- ret = af9013_rd_regs(state, 0xd2e3, buf, 3); +- if (ret) +- goto err; +- +- snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; +- +- /* read current modulation */ +- ret = af9013_rd_reg(state, 0xd3c1, &tmp); +- if (ret) +- goto err; +- +- switch ((tmp >> 6) & 3) { +- case 0: +- len = ARRAY_SIZE(qpsk_snr_lut); +- snr_lut = qpsk_snr_lut; +- break; +- case 1: +- len = ARRAY_SIZE(qam16_snr_lut); +- snr_lut = qam16_snr_lut; +- break; +- case 2: +- len = ARRAY_SIZE(qam64_snr_lut); +- snr_lut = qam64_snr_lut; +- break; +- default: +- goto err; +- break; +- } +- +- for (i = 0; i < len; i++) { +- tmp = snr_lut[i].snr; +- +- if (snr_val < snr_lut[i].val) +- break; +- } +- state->snr = tmp * 10; /* dB/10 */ +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_statistics_signal_strength(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret = 0; +- u8 buf[2], rf_gain, if_gain; +- int signal_strength; +- +- dbg("%s", __func__); +- +- if (!state->signal_strength_en) +- return 0; +- +- ret = af9013_rd_regs(state, 0xd07c, buf, 2); +- if (ret) +- goto err; +- +- rf_gain = buf[0]; +- if_gain = buf[1]; +- +- signal_strength = (0xffff / \ +- (9 * (state->rf_50 + state->if_50) - \ +- 11 * (state->rf_80 + state->if_80))) * \ +- (10 * (rf_gain + if_gain) - \ +- 11 * (state->rf_80 + state->if_80)); +- if (signal_strength < 0) +- signal_strength = 0; +- else if (signal_strength > 0xffff) +- signal_strength = 0xffff; +- +- state->signal_strength = signal_strength; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static void af9013_statistics_work(struct work_struct *work) +-{ +- int ret; +- struct af9013_state *state = container_of(work, +- struct af9013_state, statistics_work.work); +- unsigned int next_msec; +- +- /* update only signal strength when demod is not locked */ +- if (!(state->fe_status & FE_HAS_LOCK)) { +- state->statistics_step = 0; +- state->ber = 0; +- state->snr = 0; +- } +- +- switch (state->statistics_step) { +- default: +- state->statistics_step = 0; +- case 0: +- ret = af9013_statistics_signal_strength(&state->fe); +- state->statistics_step++; +- next_msec = 300; +- break; +- case 1: +- ret = af9013_statistics_snr_start(&state->fe); +- state->statistics_step++; +- next_msec = 200; +- break; +- case 2: +- ret = af9013_statistics_ber_unc_start(&state->fe); +- state->statistics_step++; +- next_msec = 1000; +- break; +- case 3: +- ret = af9013_statistics_snr_result(&state->fe); +- state->statistics_step++; +- next_msec = 400; +- break; +- case 4: +- ret = af9013_statistics_ber_unc_result(&state->fe); +- state->statistics_step++; +- next_msec = 100; +- break; +- } +- +- schedule_delayed_work(&state->statistics_work, +- msecs_to_jiffies(next_msec)); +- +- return; +-} +- +-static int af9013_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- fesettings->min_delay_ms = 800; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- +- return 0; +-} +- +-static int af9013_set_frontend(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i, sampling_freq; +- bool auto_mode, spec_inv; +- u8 buf[6]; +- u32 if_frequency, freq_cw; +- +- dbg("%s: frequency=%d bandwidth_hz=%d", __func__, +- c->frequency, c->bandwidth_hz); +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- /* program CFOE coefficients */ +- if (c->bandwidth_hz != state->bandwidth_hz) { +- for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { +- if (coeff_lut[i].clock == state->config.clock && +- coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { +- break; +- } +- } +- +- ret = af9013_wr_regs(state, 0xae00, coeff_lut[i].val, +- sizeof(coeff_lut[i].val)); +- } +- +- /* program frequency control */ +- if (c->bandwidth_hz != state->bandwidth_hz || state->first_tune) { +- /* get used IF frequency */ +- if (fe->ops.tuner_ops.get_if_frequency) +- fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); +- else +- if_frequency = state->config.if_frequency; +- +- sampling_freq = if_frequency; +- +- while (sampling_freq > (state->config.clock / 2)) +- sampling_freq -= state->config.clock; +- +- if (sampling_freq < 0) { +- sampling_freq *= -1; +- spec_inv = state->config.spec_inv; +- } else { +- spec_inv = !state->config.spec_inv; +- } +- +- freq_cw = af913_div(sampling_freq, state->config.clock, 23); +- +- if (spec_inv) +- freq_cw = 0x800000 - freq_cw; +- +- buf[0] = (freq_cw >> 0) & 0xff; +- buf[1] = (freq_cw >> 8) & 0xff; +- buf[2] = (freq_cw >> 16) & 0x7f; +- +- freq_cw = 0x800000 - freq_cw; +- +- buf[3] = (freq_cw >> 0) & 0xff; +- buf[4] = (freq_cw >> 8) & 0xff; +- buf[5] = (freq_cw >> 16) & 0x7f; +- +- ret = af9013_wr_regs(state, 0xd140, buf, 3); +- if (ret) +- goto err; +- +- ret = af9013_wr_regs(state, 0x9be7, buf, 6); +- if (ret) +- goto err; +- } +- +- /* clear TPS lock flag */ +- ret = af9013_wr_reg_bits(state, 0xd330, 3, 1, 1); +- if (ret) +- goto err; +- +- /* clear MPEG2 lock flag */ +- ret = af9013_wr_reg_bits(state, 0xd507, 6, 1, 0); +- if (ret) +- goto err; +- +- /* empty channel function */ +- ret = af9013_wr_reg_bits(state, 0x9bfe, 0, 1, 0); +- if (ret) +- goto err; +- +- /* empty DVB-T channel function */ +- ret = af9013_wr_reg_bits(state, 0x9bc2, 0, 1, 0); +- if (ret) +- goto err; +- +- /* transmission parameters */ +- auto_mode = false; +- memset(buf, 0, 3); +- +- switch (c->transmission_mode) { +- case TRANSMISSION_MODE_AUTO: +- auto_mode = 1; +- break; +- case TRANSMISSION_MODE_2K: +- break; +- case TRANSMISSION_MODE_8K: +- buf[0] |= (1 << 0); +- break; +- default: +- dbg("%s: invalid transmission_mode", __func__); +- auto_mode = 1; +- } +- +- switch (c->guard_interval) { +- case GUARD_INTERVAL_AUTO: +- auto_mode = 1; +- break; +- case GUARD_INTERVAL_1_32: +- break; +- case GUARD_INTERVAL_1_16: +- buf[0] |= (1 << 2); +- break; +- case GUARD_INTERVAL_1_8: +- buf[0] |= (2 << 2); +- break; +- case GUARD_INTERVAL_1_4: +- buf[0] |= (3 << 2); +- break; +- default: +- dbg("%s: invalid guard_interval", __func__); +- auto_mode = 1; +- } +- +- switch (c->hierarchy) { +- case HIERARCHY_AUTO: +- auto_mode = 1; +- break; +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- buf[0] |= (1 << 4); +- break; +- case HIERARCHY_2: +- buf[0] |= (2 << 4); +- break; +- case HIERARCHY_4: +- buf[0] |= (3 << 4); +- break; +- default: +- dbg("%s: invalid hierarchy", __func__); +- auto_mode = 1; +- }; +- +- switch (c->modulation) { +- case QAM_AUTO: +- auto_mode = 1; +- break; +- case QPSK: +- break; +- case QAM_16: +- buf[1] |= (1 << 6); +- break; +- case QAM_64: +- buf[1] |= (2 << 6); +- break; +- default: +- dbg("%s: invalid modulation", __func__); +- auto_mode = 1; +- } +- +- /* Use HP. How and which case we can switch to LP? */ +- buf[1] |= (1 << 4); +- +- switch (c->code_rate_HP) { +- case FEC_AUTO: +- auto_mode = 1; +- break; +- case FEC_1_2: +- break; +- case FEC_2_3: +- buf[2] |= (1 << 0); +- break; +- case FEC_3_4: +- buf[2] |= (2 << 0); +- break; +- case FEC_5_6: +- buf[2] |= (3 << 0); +- break; +- case FEC_7_8: +- buf[2] |= (4 << 0); +- break; +- default: +- dbg("%s: invalid code_rate_HP", __func__); +- auto_mode = 1; +- } +- +- switch (c->code_rate_LP) { +- case FEC_AUTO: +- auto_mode = 1; +- break; +- case FEC_1_2: +- break; +- case FEC_2_3: +- buf[2] |= (1 << 3); +- break; +- case FEC_3_4: +- buf[2] |= (2 << 3); +- break; +- case FEC_5_6: +- buf[2] |= (3 << 3); +- break; +- case FEC_7_8: +- buf[2] |= (4 << 3); +- break; +- case FEC_NONE: +- break; +- default: +- dbg("%s: invalid code_rate_LP", __func__); +- auto_mode = 1; +- } +- +- switch (c->bandwidth_hz) { +- case 6000000: +- break; +- case 7000000: +- buf[1] |= (1 << 2); +- break; +- case 8000000: +- buf[1] |= (2 << 2); +- break; +- default: +- dbg("%s: invalid bandwidth_hz", __func__); +- ret = -EINVAL; +- goto err; +- } +- +- ret = af9013_wr_regs(state, 0xd3c0, buf, 3); +- if (ret) +- goto err; +- +- if (auto_mode) { +- /* clear easy mode flag */ +- ret = af9013_wr_reg(state, 0xaefd, 0); +- if (ret) +- goto err; +- +- dbg("%s: auto params", __func__); +- } else { +- /* set easy mode flag */ +- ret = af9013_wr_reg(state, 0xaefd, 1); +- if (ret) +- goto err; +- +- ret = af9013_wr_reg(state, 0xaefe, 0); +- if (ret) +- goto err; +- +- dbg("%s: manual params", __func__); +- } +- +- /* tune */ +- ret = af9013_wr_reg(state, 0xffff, 0); +- if (ret) +- goto err; +- +- state->bandwidth_hz = c->bandwidth_hz; +- state->set_frontend_jiffies = jiffies; +- state->first_tune = false; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[3]; +- +- dbg("%s", __func__); +- +- ret = af9013_rd_regs(state, 0xd3c0, buf, 3); +- if (ret) +- goto err; +- +- switch ((buf[1] >> 6) & 3) { +- case 0: +- c->modulation = QPSK; +- break; +- case 1: +- c->modulation = QAM_16; +- break; +- case 2: +- c->modulation = QAM_64; +- break; +- } +- +- switch ((buf[0] >> 0) & 3) { +- case 0: +- c->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- c->transmission_mode = TRANSMISSION_MODE_8K; +- } +- +- switch ((buf[0] >> 2) & 3) { +- case 0: +- c->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- c->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- c->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- c->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- switch ((buf[0] >> 4) & 7) { +- case 0: +- c->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- c->hierarchy = HIERARCHY_1; +- break; +- case 2: +- c->hierarchy = HIERARCHY_2; +- break; +- case 3: +- c->hierarchy = HIERARCHY_4; +- break; +- } +- +- switch ((buf[2] >> 0) & 7) { +- case 0: +- c->code_rate_HP = FEC_1_2; +- break; +- case 1: +- c->code_rate_HP = FEC_2_3; +- break; +- case 2: +- c->code_rate_HP = FEC_3_4; +- break; +- case 3: +- c->code_rate_HP = FEC_5_6; +- break; +- case 4: +- c->code_rate_HP = FEC_7_8; +- break; +- } +- +- switch ((buf[2] >> 3) & 7) { +- case 0: +- c->code_rate_LP = FEC_1_2; +- break; +- case 1: +- c->code_rate_LP = FEC_2_3; +- break; +- case 2: +- c->code_rate_LP = FEC_3_4; +- break; +- case 3: +- c->code_rate_LP = FEC_5_6; +- break; +- case 4: +- c->code_rate_LP = FEC_7_8; +- break; +- } +- +- switch ((buf[1] >> 2) & 3) { +- case 0: +- c->bandwidth_hz = 6000000; +- break; +- case 1: +- c->bandwidth_hz = 7000000; +- break; +- case 2: +- c->bandwidth_hz = 8000000; +- break; +- } +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- u8 tmp; +- +- /* +- * Return status from the cache if it is younger than 2000ms with the +- * exception of last tune is done during 4000ms. +- */ +- if (time_is_after_jiffies( +- state->read_status_jiffies + msecs_to_jiffies(2000)) && +- time_is_before_jiffies( +- state->set_frontend_jiffies + msecs_to_jiffies(4000)) +- ) { +- *status = state->fe_status; +- return 0; +- } else { +- *status = 0; +- } +- +- /* MPEG2 lock */ +- ret = af9013_rd_reg_bits(state, 0xd507, 6, 1, &tmp); +- if (ret) +- goto err; +- +- if (tmp) +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | +- FE_HAS_SYNC | FE_HAS_LOCK; +- +- if (!*status) { +- /* TPS lock */ +- ret = af9013_rd_reg_bits(state, 0xd330, 3, 1, &tmp); +- if (ret) +- goto err; +- +- if (tmp) +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI; +- } +- +- state->fe_status = *status; +- state->read_status_jiffies = jiffies; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- *snr = state->snr; +- return 0; +-} +- +-static int af9013_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- *strength = state->signal_strength; +- return 0; +-} +- +-static int af9013_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- *ber = state->ber; +- return 0; +-} +- +-static int af9013_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- *ucblocks = state->ucblocks; +- return 0; +-} +- +-static int af9013_init(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret, i, len; +- u8 buf[3], tmp; +- u32 adc_cw; +- const struct af9013_reg_bit *init; +- +- dbg("%s", __func__); +- +- /* power on */ +- ret = af9013_power_ctrl(state, 1); +- if (ret) +- goto err; +- +- /* enable ADC */ +- ret = af9013_wr_reg(state, 0xd73a, 0xa4); +- if (ret) +- goto err; +- +- /* write API version to firmware */ +- ret = af9013_wr_regs(state, 0x9bf2, state->config.api_version, 4); +- if (ret) +- goto err; +- +- /* program ADC control */ +- switch (state->config.clock) { +- case 28800000: /* 28.800 MHz */ +- tmp = 0; +- break; +- case 20480000: /* 20.480 MHz */ +- tmp = 1; +- break; +- case 28000000: /* 28.000 MHz */ +- tmp = 2; +- break; +- case 25000000: /* 25.000 MHz */ +- tmp = 3; +- break; +- default: +- err("invalid clock"); +- return -EINVAL; +- } +- +- adc_cw = af913_div(state->config.clock, 1000000ul, 19); +- buf[0] = (adc_cw >> 0) & 0xff; +- buf[1] = (adc_cw >> 8) & 0xff; +- buf[2] = (adc_cw >> 16) & 0xff; +- +- ret = af9013_wr_regs(state, 0xd180, buf, 3); +- if (ret) +- goto err; +- +- ret = af9013_wr_reg_bits(state, 0x9bd2, 0, 4, tmp); +- if (ret) +- goto err; +- +- /* set I2C master clock */ +- ret = af9013_wr_reg(state, 0xd416, 0x14); +- if (ret) +- goto err; +- +- /* set 16 embx */ +- ret = af9013_wr_reg_bits(state, 0xd700, 1, 1, 1); +- if (ret) +- goto err; +- +- /* set no trigger */ +- ret = af9013_wr_reg_bits(state, 0xd700, 2, 1, 0); +- if (ret) +- goto err; +- +- /* set read-update bit for constellation */ +- ret = af9013_wr_reg_bits(state, 0xd371, 1, 1, 1); +- if (ret) +- goto err; +- +- /* settings for mp2if */ +- if (state->config.ts_mode == AF9013_TS_USB) { +- /* AF9015 split PSB to 1.5k + 0.5k */ +- ret = af9013_wr_reg_bits(state, 0xd50b, 2, 1, 1); +- if (ret) +- goto err; +- } else { +- /* AF9013 change the output bit to data7 */ +- ret = af9013_wr_reg_bits(state, 0xd500, 3, 1, 1); +- if (ret) +- goto err; +- +- /* AF9013 set mpeg to full speed */ +- ret = af9013_wr_reg_bits(state, 0xd502, 4, 1, 1); +- if (ret) +- goto err; +- } +- +- ret = af9013_wr_reg_bits(state, 0xd520, 4, 1, 1); +- if (ret) +- goto err; +- +- /* load OFSM settings */ +- dbg("%s: load ofsm settings", __func__); +- len = ARRAY_SIZE(ofsm_init); +- init = ofsm_init; +- for (i = 0; i < len; i++) { +- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos, +- init[i].len, init[i].val); +- if (ret) +- goto err; +- } +- +- /* load tuner specific settings */ +- dbg("%s: load tuner specific settings", __func__); +- switch (state->config.tuner) { +- case AF9013_TUNER_MXL5003D: +- len = ARRAY_SIZE(tuner_init_mxl5003d); +- init = tuner_init_mxl5003d; +- break; +- case AF9013_TUNER_MXL5005D: +- case AF9013_TUNER_MXL5005R: +- case AF9013_TUNER_MXL5007T: +- len = ARRAY_SIZE(tuner_init_mxl5005); +- init = tuner_init_mxl5005; +- break; +- case AF9013_TUNER_ENV77H11D5: +- len = ARRAY_SIZE(tuner_init_env77h11d5); +- init = tuner_init_env77h11d5; +- break; +- case AF9013_TUNER_MT2060: +- len = ARRAY_SIZE(tuner_init_mt2060); +- init = tuner_init_mt2060; +- break; +- case AF9013_TUNER_MC44S803: +- len = ARRAY_SIZE(tuner_init_mc44s803); +- init = tuner_init_mc44s803; +- break; +- case AF9013_TUNER_QT1010: +- case AF9013_TUNER_QT1010A: +- len = ARRAY_SIZE(tuner_init_qt1010); +- init = tuner_init_qt1010; +- break; +- case AF9013_TUNER_MT2060_2: +- len = ARRAY_SIZE(tuner_init_mt2060_2); +- init = tuner_init_mt2060_2; +- break; +- case AF9013_TUNER_TDA18271: +- case AF9013_TUNER_TDA18218: +- len = ARRAY_SIZE(tuner_init_tda18271); +- init = tuner_init_tda18271; +- break; +- case AF9013_TUNER_UNKNOWN: +- default: +- len = ARRAY_SIZE(tuner_init_unknown); +- init = tuner_init_unknown; +- break; +- } +- +- for (i = 0; i < len; i++) { +- ret = af9013_wr_reg_bits(state, init[i].addr, init[i].pos, +- init[i].len, init[i].val); +- if (ret) +- goto err; +- } +- +- /* TS mode */ +- ret = af9013_wr_reg_bits(state, 0xd500, 1, 2, state->config.ts_mode); +- if (ret) +- goto err; +- +- /* enable lock led */ +- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 1); +- if (ret) +- goto err; +- +- /* check if we support signal strength */ +- if (!state->signal_strength_en) { +- ret = af9013_rd_reg_bits(state, 0x9bee, 0, 1, +- &state->signal_strength_en); +- if (ret) +- goto err; +- } +- +- /* read values needed for signal strength calculation */ +- if (state->signal_strength_en && !state->rf_50) { +- ret = af9013_rd_reg(state, 0x9bbd, &state->rf_50); +- if (ret) +- goto err; +- +- ret = af9013_rd_reg(state, 0x9bd0, &state->rf_80); +- if (ret) +- goto err; +- +- ret = af9013_rd_reg(state, 0x9be2, &state->if_50); +- if (ret) +- goto err; +- +- ret = af9013_rd_reg(state, 0x9be4, &state->if_80); +- if (ret) +- goto err; +- } +- +- /* SNR */ +- ret = af9013_wr_reg(state, 0xd2e2, 1); +- if (ret) +- goto err; +- +- /* BER / UCB */ +- buf[0] = (10000 >> 0) & 0xff; +- buf[1] = (10000 >> 8) & 0xff; +- ret = af9013_wr_regs(state, 0xd385, buf, 2); +- if (ret) +- goto err; +- +- /* enable FEC monitor */ +- ret = af9013_wr_reg_bits(state, 0xd392, 1, 1, 1); +- if (ret) +- goto err; +- +- state->first_tune = true; +- schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(400)); +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_sleep(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- int ret; +- +- dbg("%s", __func__); +- +- /* stop statistics polling */ +- cancel_delayed_work_sync(&state->statistics_work); +- +- /* disable lock led */ +- ret = af9013_wr_reg_bits(state, 0xd730, 0, 1, 0); +- if (ret) +- goto err; +- +- /* power off */ +- ret = af9013_power_ctrl(state, 0); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int af9013_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- int ret; +- struct af9013_state *state = fe->demodulator_priv; +- +- dbg("%s: enable=%d", __func__, enable); +- +- /* gate already open or close */ +- if (state->i2c_gate_state == enable) +- return 0; +- +- if (state->config.ts_mode == AF9013_TS_USB) +- ret = af9013_wr_reg_bits(state, 0xd417, 3, 1, enable); +- else +- ret = af9013_wr_reg_bits(state, 0xd607, 2, 1, enable); +- if (ret) +- goto err; +- +- state->i2c_gate_state = enable; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static void af9013_release(struct dvb_frontend *fe) +-{ +- struct af9013_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops af9013_ops; +- +-static int af9013_download_firmware(struct af9013_state *state) +-{ +- int i, len, remaining, ret; +- const struct firmware *fw; +- u16 checksum = 0; +- u8 val; +- u8 fw_params[4]; +- u8 *fw_file = AF9013_DEFAULT_FIRMWARE; +- +- msleep(100); +- /* check whether firmware is already running */ +- ret = af9013_rd_reg(state, 0x98be, &val); +- if (ret) +- goto err; +- else +- dbg("%s: firmware status=%02x", __func__, val); +- +- if (val == 0x0c) /* fw is running, no need for download */ +- goto exit; +- +- info("found a '%s' in cold state, will try to load a firmware", +- af9013_ops.info.name); +- +- /* request the firmware, this will block and timeout */ +- ret = request_firmware(&fw, fw_file, state->i2c->dev.parent); +- if (ret) { +- err("did not find the firmware file. (%s) " +- "Please see linux/Documentation/dvb/ for more details" \ +- " on firmware-problems. (%d)", +- fw_file, ret); +- goto err; +- } +- +- info("downloading firmware from file '%s'", fw_file); +- +- /* calc checksum */ +- for (i = 0; i < fw->size; i++) +- checksum += fw->data[i]; +- +- fw_params[0] = checksum >> 8; +- fw_params[1] = checksum & 0xff; +- fw_params[2] = fw->size >> 8; +- fw_params[3] = fw->size & 0xff; +- +- /* write fw checksum & size */ +- ret = af9013_write_ofsm_regs(state, 0x50fc, +- fw_params, sizeof(fw_params)); +- if (ret) +- goto err_release; +- +- #define FW_ADDR 0x5100 /* firmware start address */ +- #define LEN_MAX 16 /* max packet size */ +- for (remaining = fw->size; remaining > 0; remaining -= LEN_MAX) { +- len = remaining; +- if (len > LEN_MAX) +- len = LEN_MAX; +- +- ret = af9013_write_ofsm_regs(state, +- FW_ADDR + fw->size - remaining, +- (u8 *) &fw->data[fw->size - remaining], len); +- if (ret) { +- err("firmware download failed:%d", ret); +- goto err_release; +- } +- } +- +- /* request boot firmware */ +- ret = af9013_wr_reg(state, 0xe205, 1); +- if (ret) +- goto err_release; +- +- for (i = 0; i < 15; i++) { +- msleep(100); +- +- /* check firmware status */ +- ret = af9013_rd_reg(state, 0x98be, &val); +- if (ret) +- goto err_release; +- +- dbg("%s: firmware status=%02x", __func__, val); +- +- if (val == 0x0c || val == 0x04) /* success or fail */ +- break; +- } +- +- if (val == 0x04) { +- err("firmware did not run"); +- ret = -ENODEV; +- } else if (val != 0x0c) { +- err("firmware boot timeout"); +- ret = -ENODEV; +- } +- +-err_release: +- release_firmware(fw); +-err: +-exit: +- if (!ret) +- info("found a '%s' in warm state.", af9013_ops.info.name); +- return ret; +-} +- +-struct dvb_frontend *af9013_attach(const struct af9013_config *config, +- struct i2c_adapter *i2c) +-{ +- int ret; +- struct af9013_state *state = NULL; +- u8 buf[4], i; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct af9013_state), GFP_KERNEL); +- if (state == NULL) +- goto err; +- +- /* setup the state */ +- state->i2c = i2c; +- memcpy(&state->config, config, sizeof(struct af9013_config)); +- +- /* download firmware */ +- if (state->config.ts_mode != AF9013_TS_USB) { +- ret = af9013_download_firmware(state); +- if (ret) +- goto err; +- } +- +- /* firmware version */ +- ret = af9013_rd_regs(state, 0x5103, buf, 4); +- if (ret) +- goto err; +- +- info("firmware version %d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3]); +- +- /* set GPIOs */ +- for (i = 0; i < sizeof(state->config.gpio); i++) { +- ret = af9013_set_gpio(state, i, state->config.gpio[i]); +- if (ret) +- goto err; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->fe.ops, &af9013_ops, +- sizeof(struct dvb_frontend_ops)); +- state->fe.demodulator_priv = state; +- +- INIT_DELAYED_WORK(&state->statistics_work, af9013_statistics_work); +- +- return &state->fe; +-err: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(af9013_attach); +- +-static struct dvb_frontend_ops af9013_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Afatech AF9013", +- .frequency_min = 174000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 250000, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_1_2 | +- FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | +- FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | +- FE_CAN_RECOVER | +- FE_CAN_MUTE_TS +- }, +- +- .release = af9013_release, +- +- .init = af9013_init, +- .sleep = af9013_sleep, +- +- .get_tune_settings = af9013_get_tune_settings, +- .set_frontend = af9013_set_frontend, +- .get_frontend = af9013_get_frontend, +- +- .read_status = af9013_read_status, +- .read_snr = af9013_read_snr, +- .read_signal_strength = af9013_read_signal_strength, +- .read_ber = af9013_read_ber, +- .read_ucblocks = af9013_read_ucblocks, +- +- .i2c_gate_ctrl = af9013_i2c_gate_ctrl, +-}; +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Afatech AF9013 DVB-T demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h +deleted file mode 100644 +index b973fc5..0000000 +--- a/drivers/media/dvb/frontends/af9013.h ++++ /dev/null +@@ -1,118 +0,0 @@ +-/* +- * Afatech AF9013 demodulator driver +- * +- * Copyright (C) 2007 Antti Palosaari +- * Copyright (C) 2011 Antti Palosaari +- * +- * Thanks to Afatech who kindly provided information. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef AF9013_H +-#define AF9013_H +- +-#include +- +-/* AF9013/5 GPIOs (mostly guessed) +- demod#1-gpio#0 - set demod#2 i2c-addr for dual devices +- demod#1-gpio#1 - xtal setting (?) +- demod#1-gpio#3 - tuner#1 +- demod#2-gpio#0 - tuner#2 +- demod#2-gpio#1 - xtal setting (?) +-*/ +- +-struct af9013_config { +- /* +- * I2C address +- */ +- u8 i2c_addr; +- +- /* +- * clock +- * 20480000, 25000000, 28000000, 28800000 +- */ +- u32 clock; +- +- /* +- * tuner +- */ +-#define AF9013_TUNER_MXL5003D 3 /* MaxLinear */ +-#define AF9013_TUNER_MXL5005D 13 /* MaxLinear */ +-#define AF9013_TUNER_MXL5005R 30 /* MaxLinear */ +-#define AF9013_TUNER_ENV77H11D5 129 /* Panasonic */ +-#define AF9013_TUNER_MT2060 130 /* Microtune */ +-#define AF9013_TUNER_MC44S803 133 /* Freescale */ +-#define AF9013_TUNER_QT1010 134 /* Quantek */ +-#define AF9013_TUNER_UNKNOWN 140 /* for can tuners ? */ +-#define AF9013_TUNER_MT2060_2 147 /* Microtune */ +-#define AF9013_TUNER_TDA18271 156 /* NXP */ +-#define AF9013_TUNER_QT1010A 162 /* Quantek */ +-#define AF9013_TUNER_MXL5007T 177 /* MaxLinear */ +-#define AF9013_TUNER_TDA18218 179 /* NXP */ +- u8 tuner; +- +- /* +- * IF frequency +- */ +- u32 if_frequency; +- +- /* +- * TS settings +- */ +-#define AF9013_TS_USB 0 +-#define AF9013_TS_PARALLEL 1 +-#define AF9013_TS_SERIAL 2 +- u8 ts_mode:2; +- +- /* +- * input spectrum inversion +- */ +- bool spec_inv; +- +- /* +- * firmware API version +- */ +- u8 api_version[4]; +- +- /* +- * GPIOs +- */ +-#define AF9013_GPIO_ON (1 << 0) +-#define AF9013_GPIO_EN (1 << 1) +-#define AF9013_GPIO_O (1 << 2) +-#define AF9013_GPIO_I (1 << 3) +-#define AF9013_GPIO_LO (AF9013_GPIO_ON|AF9013_GPIO_EN) +-#define AF9013_GPIO_HI (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) +-#define AF9013_GPIO_TUNER_ON (AF9013_GPIO_ON|AF9013_GPIO_EN) +-#define AF9013_GPIO_TUNER_OFF (AF9013_GPIO_ON|AF9013_GPIO_EN|AF9013_GPIO_O) +- u8 gpio[4]; +-}; +- +-#if defined(CONFIG_DVB_AF9013) || \ +- (defined(CONFIG_DVB_AF9013_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *af9013_attach(const struct af9013_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *af9013_attach( +-const struct af9013_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_AF9013 */ +- +-#endif /* AF9013_H */ +diff --git a/drivers/media/dvb/frontends/af9013_priv.h b/drivers/media/dvb/frontends/af9013_priv.h +deleted file mode 100644 +index fa848af..0000000 +--- a/drivers/media/dvb/frontends/af9013_priv.h ++++ /dev/null +@@ -1,922 +0,0 @@ +-/* +- * Afatech AF9013 demodulator driver +- * +- * Copyright (C) 2007 Antti Palosaari +- * Copyright (C) 2011 Antti Palosaari +- * +- * Thanks to Afatech who kindly provided information. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef AF9013_PRIV_H +-#define AF9013_PRIV_H +- +-#include "dvb_frontend.h" +-#include "af9013.h" +-#include +- +-#define LOG_PREFIX "af9013" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (af9013_debug) \ +- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-#define AF9013_DEFAULT_FIRMWARE "dvb-fe-af9013.fw" +- +-struct af9013_reg_bit { +- u16 addr; +- u8 pos:4; +- u8 len:4; +- u8 val; +-}; +- +-struct af9013_snr { +- u32 val; +- u8 snr; +-}; +- +-struct af9013_coeff { +- u32 clock; +- u32 bandwidth_hz; +- u8 val[24]; +-}; +- +-/* pre-calculated coeff lookup table */ +-static const struct af9013_coeff coeff_lut[] = { +- /* 28.800 MHz */ +- { 28800000, 8000000, { 0x02, 0x8a, 0x28, 0xa3, 0x05, 0x14, +- 0x51, 0x11, 0x00, 0xa2, 0x8f, 0x3d, 0x00, 0xa2, 0x8a, +- 0x29, 0x00, 0xa2, 0x85, 0x14, 0x01, 0x45, 0x14, 0x14 } }, +- { 28800000, 7000000, { 0x02, 0x38, 0xe3, 0x8e, 0x04, 0x71, +- 0xc7, 0x07, 0x00, 0x8e, 0x3d, 0x55, 0x00, 0x8e, 0x38, +- 0xe4, 0x00, 0x8e, 0x34, 0x72, 0x01, 0x1c, 0x71, 0x32 } }, +- { 28800000, 6000000, { 0x01, 0xe7, 0x9e, 0x7a, 0x03, 0xcf, +- 0x3c, 0x3d, 0x00, 0x79, 0xeb, 0x6e, 0x00, 0x79, 0xe7, +- 0x9e, 0x00, 0x79, 0xe3, 0xcf, 0x00, 0xf3, 0xcf, 0x0f } }, +- /* 20.480 MHz */ +- { 20480000, 8000000, { 0x03, 0x92, 0x49, 0x26, 0x07, 0x24, +- 0x92, 0x13, 0x00, 0xe4, 0x99, 0x6e, 0x00, 0xe4, 0x92, +- 0x49, 0x00, 0xe4, 0x8b, 0x25, 0x01, 0xc9, 0x24, 0x25 } }, +- { 20480000, 7000000, { 0x03, 0x20, 0x00, 0x01, 0x06, 0x40, +- 0x00, 0x00, 0x00, 0xc8, 0x06, 0x40, 0x00, 0xc8, 0x00, +- 0x00, 0x00, 0xc7, 0xf9, 0xc0, 0x01, 0x90, 0x00, 0x00 } }, +- { 20480000, 6000000, { 0x02, 0xad, 0xb6, 0xdc, 0x05, 0x5b, +- 0x6d, 0x2e, 0x00, 0xab, 0x73, 0x13, 0x00, 0xab, 0x6d, +- 0xb7, 0x00, 0xab, 0x68, 0x5c, 0x01, 0x56, 0xdb, 0x1c } }, +- /* 28.000 MHz */ +- { 28000000, 8000000, { 0x02, 0x9c, 0xbc, 0x15, 0x05, 0x39, +- 0x78, 0x0a, 0x00, 0xa7, 0x34, 0x3f, 0x00, 0xa7, 0x2f, +- 0x05, 0x00, 0xa7, 0x29, 0xcc, 0x01, 0x4e, 0x5e, 0x03 } }, +- { 28000000, 7000000, { 0x02, 0x49, 0x24, 0x92, 0x04, 0x92, +- 0x49, 0x09, 0x00, 0x92, 0x4d, 0xb7, 0x00, 0x92, 0x49, +- 0x25, 0x00, 0x92, 0x44, 0x92, 0x01, 0x24, 0x92, 0x12 } }, +- { 28000000, 6000000, { 0x01, 0xf5, 0x8d, 0x10, 0x03, 0xeb, +- 0x1a, 0x08, 0x00, 0x7d, 0x67, 0x2f, 0x00, 0x7d, 0x63, +- 0x44, 0x00, 0x7d, 0x5f, 0x59, 0x00, 0xfa, 0xc6, 0x22 } }, +- /* 25.000 MHz */ +- { 25000000, 8000000, { 0x02, 0xec, 0xfb, 0x9d, 0x05, 0xd9, +- 0xf7, 0x0e, 0x00, 0xbb, 0x44, 0xc1, 0x00, 0xbb, 0x3e, +- 0xe7, 0x00, 0xbb, 0x39, 0x0d, 0x01, 0x76, 0x7d, 0x34 } }, +- { 25000000, 7000000, { 0x02, 0x8f, 0x5c, 0x29, 0x05, 0x1e, +- 0xb8, 0x14, 0x00, 0xa3, 0xdc, 0x29, 0x00, 0xa3, 0xd7, +- 0x0a, 0x00, 0xa3, 0xd1, 0xec, 0x01, 0x47, 0xae, 0x05 } }, +- { 25000000, 6000000, { 0x02, 0x31, 0xbc, 0xb5, 0x04, 0x63, +- 0x79, 0x1b, 0x00, 0x8c, 0x73, 0x91, 0x00, 0x8c, 0x6f, +- 0x2d, 0x00, 0x8c, 0x6a, 0xca, 0x01, 0x18, 0xde, 0x17 } }, +-}; +- +-/* QPSK SNR lookup table */ +-static const struct af9013_snr qpsk_snr_lut[] = { +- { 0x000000, 0 }, +- { 0x0b4771, 0 }, +- { 0x0c1aed, 1 }, +- { 0x0d0d27, 2 }, +- { 0x0e4d19, 3 }, +- { 0x0e5da8, 4 }, +- { 0x107097, 5 }, +- { 0x116975, 6 }, +- { 0x1252d9, 7 }, +- { 0x131fa4, 8 }, +- { 0x13d5e1, 9 }, +- { 0x148e53, 10 }, +- { 0x15358b, 11 }, +- { 0x15dd29, 12 }, +- { 0x168112, 13 }, +- { 0x170b61, 14 }, +- { 0xffffff, 15 }, +-}; +- +-/* QAM16 SNR lookup table */ +-static const struct af9013_snr qam16_snr_lut[] = { +- { 0x000000, 0 }, +- { 0x05eb62, 5 }, +- { 0x05fecf, 6 }, +- { 0x060b80, 7 }, +- { 0x062501, 8 }, +- { 0x064865, 9 }, +- { 0x069604, 10 }, +- { 0x06f356, 11 }, +- { 0x07706a, 12 }, +- { 0x0804d3, 13 }, +- { 0x089d1a, 14 }, +- { 0x093e3d, 15 }, +- { 0x09e35d, 16 }, +- { 0x0a7c3c, 17 }, +- { 0x0afaf8, 18 }, +- { 0x0b719d, 19 }, +- { 0xffffff, 20 }, +-}; +- +-/* QAM64 SNR lookup table */ +-static const struct af9013_snr qam64_snr_lut[] = { +- { 0x000000, 0 }, +- { 0x03109b, 12 }, +- { 0x0310d4, 13 }, +- { 0x031920, 14 }, +- { 0x0322d0, 15 }, +- { 0x0339fc, 16 }, +- { 0x0364a1, 17 }, +- { 0x038bcc, 18 }, +- { 0x03c7d3, 19 }, +- { 0x0408cc, 20 }, +- { 0x043bed, 21 }, +- { 0x048061, 22 }, +- { 0x04be95, 23 }, +- { 0x04fa7d, 24 }, +- { 0x052405, 25 }, +- { 0x05570d, 26 }, +- { 0xffffff, 27 }, +-}; +- +-static const struct af9013_reg_bit ofsm_init[] = { +- { 0xd73a, 0, 8, 0xa1 }, +- { 0xd73b, 0, 8, 0x1f }, +- { 0xd73c, 4, 4, 0x0a }, +- { 0xd732, 3, 1, 0x00 }, +- { 0xd731, 4, 2, 0x03 }, +- { 0xd73d, 7, 1, 0x01 }, +- { 0xd740, 0, 1, 0x00 }, +- { 0xd740, 1, 1, 0x00 }, +- { 0xd740, 2, 1, 0x00 }, +- { 0xd740, 3, 1, 0x01 }, +- { 0xd3c1, 4, 1, 0x01 }, +- { 0x9124, 0, 8, 0x58 }, +- { 0x9125, 0, 2, 0x02 }, +- { 0xd3a2, 0, 8, 0x00 }, +- { 0xd3a3, 0, 8, 0x04 }, +- { 0xd305, 0, 8, 0x32 }, +- { 0xd306, 0, 8, 0x10 }, +- { 0xd304, 0, 8, 0x04 }, +- { 0x9112, 0, 1, 0x01 }, +- { 0x911d, 0, 1, 0x01 }, +- { 0x911a, 0, 1, 0x01 }, +- { 0x911b, 0, 1, 0x01 }, +- { 0x9bce, 0, 4, 0x02 }, +- { 0x9116, 0, 1, 0x01 }, +- { 0x9122, 0, 8, 0xd0 }, +- { 0xd2e0, 0, 8, 0xd0 }, +- { 0xd2e9, 0, 4, 0x0d }, +- { 0xd38c, 0, 8, 0xfc }, +- { 0xd38d, 0, 8, 0x00 }, +- { 0xd38e, 0, 8, 0x7e }, +- { 0xd38f, 0, 8, 0x00 }, +- { 0xd390, 0, 8, 0x2f }, +- { 0xd145, 4, 1, 0x01 }, +- { 0xd1a9, 4, 1, 0x01 }, +- { 0xd158, 5, 3, 0x01 }, +- { 0xd159, 0, 6, 0x06 }, +- { 0xd167, 0, 8, 0x00 }, +- { 0xd168, 0, 4, 0x07 }, +- { 0xd1c3, 5, 3, 0x00 }, +- { 0xd1c4, 0, 6, 0x00 }, +- { 0xd1c5, 0, 7, 0x10 }, +- { 0xd1c6, 0, 3, 0x02 }, +- { 0xd080, 2, 5, 0x03 }, +- { 0xd081, 4, 4, 0x09 }, +- { 0xd098, 4, 4, 0x0f }, +- { 0xd098, 0, 4, 0x03 }, +- { 0xdbc0, 4, 1, 0x01 }, +- { 0xdbc7, 0, 8, 0x08 }, +- { 0xdbc8, 4, 4, 0x00 }, +- { 0xdbc9, 0, 5, 0x01 }, +- { 0xd280, 0, 8, 0xe0 }, +- { 0xd281, 0, 8, 0xff }, +- { 0xd282, 0, 8, 0xff }, +- { 0xd283, 0, 8, 0xc3 }, +- { 0xd284, 0, 8, 0xff }, +- { 0xd285, 0, 4, 0x01 }, +- { 0xd0f0, 0, 7, 0x1a }, +- { 0xd0f1, 4, 1, 0x01 }, +- { 0xd0f2, 0, 8, 0x0c }, +- { 0xd101, 5, 3, 0x06 }, +- { 0xd103, 0, 4, 0x08 }, +- { 0xd0f8, 0, 7, 0x20 }, +- { 0xd111, 5, 1, 0x00 }, +- { 0xd111, 6, 1, 0x00 }, +- { 0x910b, 0, 8, 0x0a }, +- { 0x9115, 0, 8, 0x02 }, +- { 0x910c, 0, 8, 0x02 }, +- { 0x910d, 0, 8, 0x08 }, +- { 0x910e, 0, 8, 0x0a }, +- { 0x9bf6, 0, 8, 0x06 }, +- { 0x9bf8, 0, 8, 0x02 }, +- { 0x9bf7, 0, 8, 0x05 }, +- { 0x9bf9, 0, 8, 0x0f }, +- { 0x9bfc, 0, 8, 0x13 }, +- { 0x9bd3, 0, 8, 0xff }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +-}; +- +-/* Panasonic ENV77H11D5 tuner init +- AF9013_TUNER_ENV77H11D5 = 129 */ +-static const struct af9013_reg_bit tuner_init_env77h11d5[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x03 }, +- { 0x9bbe, 0, 8, 0x01 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x00 }, +- { 0x9be3, 0, 8, 0x00 }, +- { 0xd015, 0, 8, 0x50 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0xdf }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x44 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0xeb }, +- { 0xd00d, 0, 2, 0x02 }, +- { 0xd00a, 0, 8, 0xf4 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bba, 0, 8, 0xf9 }, +- { 0x9bc3, 0, 8, 0xdf }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0xeb }, +- { 0x9bc6, 0, 8, 0x02 }, +- { 0x9bc9, 0, 8, 0x52 }, +- { 0xd011, 0, 8, 0x3c }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0xf7 }, +- { 0xd014, 0, 2, 0x02 }, +- { 0xd040, 0, 8, 0x0b }, +- { 0xd041, 0, 2, 0x02 }, +- { 0xd042, 0, 8, 0x4d }, +- { 0xd043, 0, 2, 0x00 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +-}; +- +-/* Microtune MT2060 tuner init +- AF9013_TUNER_MT2060 = 130 */ +-static const struct af9013_reg_bit tuner_init_mt2060[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x07 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x00 }, +- { 0x9be3, 0, 8, 0x00 }, +- { 0x9bbe, 0, 1, 0x00 }, +- { 0x9bcc, 0, 1, 0x00 }, +- { 0x9bb9, 0, 8, 0x75 }, +- { 0x9bcd, 0, 8, 0x24 }, +- { 0x9bff, 0, 8, 0x30 }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0x0f }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x32 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0x36 }, +- { 0xd00d, 0, 2, 0x03 }, +- { 0xd00a, 0, 8, 0x35 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x07 }, +- { 0x9bc8, 0, 8, 0x90 }, +- { 0x9bc3, 0, 8, 0x0f }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x36 }, +- { 0x9bc6, 0, 8, 0x03 }, +- { 0x9bba, 0, 8, 0xc9 }, +- { 0x9bc9, 0, 8, 0x79 }, +- { 0xd011, 0, 8, 0x10 }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0x45 }, +- { 0xd014, 0, 2, 0x03 }, +- { 0xd040, 0, 8, 0x98 }, +- { 0xd041, 0, 2, 0x00 }, +- { 0xd042, 0, 8, 0xcf }, +- { 0xd043, 0, 2, 0x03 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +- { 0x9bd0, 0, 8, 0xcc }, +- { 0x9be4, 0, 8, 0xa0 }, +- { 0x9bbd, 0, 8, 0x8e }, +- { 0x9be2, 0, 8, 0x4d }, +- { 0x9bee, 0, 1, 0x01 }, +-}; +- +-/* Microtune MT2060 tuner init +- AF9013_TUNER_MT2060_2 = 147 */ +-static const struct af9013_reg_bit tuner_init_mt2060_2[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x06 }, +- { 0x9bbe, 0, 8, 0x01 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0x0f }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x32 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0x36 }, +- { 0xd00d, 0, 2, 0x03 }, +- { 0xd00a, 0, 8, 0x35 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x07 }, +- { 0x9bc8, 0, 8, 0x90 }, +- { 0x9bc3, 0, 8, 0x0f }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x36 }, +- { 0x9bc6, 0, 8, 0x03 }, +- { 0x9bba, 0, 8, 0xc9 }, +- { 0x9bc9, 0, 8, 0x79 }, +- { 0xd011, 0, 8, 0x10 }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0x45 }, +- { 0xd014, 0, 2, 0x03 }, +- { 0xd040, 0, 8, 0x98 }, +- { 0xd041, 0, 2, 0x00 }, +- { 0xd042, 0, 8, 0xcf }, +- { 0xd043, 0, 2, 0x03 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 8, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x96 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0xd045, 7, 1, 0x00 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +-}; +- +-/* MaxLinear MXL5003 tuner init +- AF9013_TUNER_MXL5003D = 3 */ +-static const struct af9013_reg_bit tuner_init_mxl5003d[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x09 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x00 }, +- { 0x9be3, 0, 8, 0x00 }, +- { 0x9bfc, 0, 8, 0x0f }, +- { 0x9bf6, 0, 8, 0x01 }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0xd015, 0, 8, 0x33 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x40 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0x0f }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x6c }, +- { 0xd007, 0, 2, 0x00 }, +- { 0xd00c, 0, 8, 0x3d }, +- { 0xd00d, 0, 2, 0x00 }, +- { 0xd00a, 0, 8, 0x45 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x07 }, +- { 0x9bc8, 0, 8, 0x52 }, +- { 0x9bc3, 0, 8, 0x0f }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x3d }, +- { 0x9bc6, 0, 8, 0x00 }, +- { 0x9bba, 0, 8, 0xa2 }, +- { 0x9bc9, 0, 8, 0xa0 }, +- { 0xd011, 0, 8, 0x56 }, +- { 0xd012, 0, 2, 0x00 }, +- { 0xd013, 0, 8, 0x50 }, +- { 0xd014, 0, 2, 0x00 }, +- { 0xd040, 0, 8, 0x56 }, +- { 0xd041, 0, 2, 0x00 }, +- { 0xd042, 0, 8, 0x50 }, +- { 0xd043, 0, 2, 0x00 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 8, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +-}; +- +-/* MaxLinear MXL5005S & MXL5007T tuner init +- AF9013_TUNER_MXL5005D = 13 +- AF9013_TUNER_MXL5005R = 30 +- AF9013_TUNER_MXL5007T = 177 */ +-static const struct af9013_reg_bit tuner_init_mxl5005[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x07 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x01 }, +- { 0x9be3, 0, 8, 0x01 }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +- { 0x9bb9, 0, 8, 0x00 }, +- { 0x9bcd, 0, 8, 0x28 }, +- { 0x9bff, 0, 8, 0x24 }, +- { 0xd015, 0, 8, 0x40 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x40 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0x0f }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x73 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0xfa }, +- { 0xd00d, 0, 2, 0x01 }, +- { 0xd00a, 0, 8, 0xff }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x23 }, +- { 0x9bc8, 0, 8, 0x55 }, +- { 0x9bc3, 0, 8, 0x01 }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0xfa }, +- { 0x9bc6, 0, 8, 0x01 }, +- { 0x9bba, 0, 8, 0xff }, +- { 0x9bc9, 0, 8, 0xff }, +- { 0x9bd3, 0, 8, 0x95 }, +- { 0xd011, 0, 8, 0x70 }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0xfb }, +- { 0xd014, 0, 2, 0x01 }, +- { 0xd040, 0, 8, 0x70 }, +- { 0xd041, 0, 2, 0x01 }, +- { 0xd042, 0, 8, 0xfb }, +- { 0xd043, 0, 2, 0x01 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +- { 0x9bd0, 0, 8, 0x93 }, +- { 0x9be4, 0, 8, 0xfe }, +- { 0x9bbd, 0, 8, 0x63 }, +- { 0x9be2, 0, 8, 0xfe }, +- { 0x9bee, 0, 1, 0x01 }, +-}; +- +-/* Quantek QT1010 tuner init +- AF9013_TUNER_QT1010 = 134 +- AF9013_TUNER_QT1010A = 162 */ +-static const struct af9013_reg_bit tuner_init_qt1010[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x09 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x01 }, +- { 0x9be3, 0, 8, 0x01 }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +- { 0x9bb9, 0, 8, 0x00 }, +- { 0x9bcd, 0, 8, 0x28 }, +- { 0x9bff, 0, 8, 0x20 }, +- { 0xd008, 0, 8, 0x0f }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x99 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0x0f }, +- { 0xd00d, 0, 2, 0x02 }, +- { 0xd00a, 0, 8, 0x50 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x00 }, +- { 0x9bc8, 0, 8, 0x00 }, +- { 0x9bc3, 0, 8, 0x0f }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x0f }, +- { 0x9bc6, 0, 8, 0x02 }, +- { 0x9bba, 0, 8, 0xc5 }, +- { 0x9bc9, 0, 8, 0xff }, +- { 0xd011, 0, 8, 0x58 }, +- { 0xd012, 0, 2, 0x02 }, +- { 0xd013, 0, 8, 0x89 }, +- { 0xd014, 0, 2, 0x01 }, +- { 0xd040, 0, 8, 0x58 }, +- { 0xd041, 0, 2, 0x02 }, +- { 0xd042, 0, 8, 0x89 }, +- { 0xd043, 0, 2, 0x01 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +- { 0x9bd0, 0, 8, 0xcd }, +- { 0x9be4, 0, 8, 0xbb }, +- { 0x9bbd, 0, 8, 0x93 }, +- { 0x9be2, 0, 8, 0x80 }, +- { 0x9bee, 0, 1, 0x01 }, +-}; +- +-/* Freescale MC44S803 tuner init +- AF9013_TUNER_MC44S803 = 133 */ +-static const struct af9013_reg_bit tuner_init_mc44s803[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x06 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x00 }, +- { 0x9be3, 0, 8, 0x00 }, +- { 0x9bf6, 0, 8, 0x01 }, +- { 0x9bf8, 0, 8, 0x02 }, +- { 0x9bf9, 0, 8, 0x02 }, +- { 0x9bfc, 0, 8, 0x1f }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +- { 0x9bb9, 0, 8, 0x00 }, +- { 0x9bcd, 0, 8, 0x24 }, +- { 0x9bff, 0, 8, 0x24 }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0x01 }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x7b }, +- { 0xd007, 0, 2, 0x00 }, +- { 0xd00c, 0, 8, 0x7c }, +- { 0xd00d, 0, 2, 0x02 }, +- { 0xd00a, 0, 8, 0xfe }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bc7, 0, 8, 0x08 }, +- { 0x9bc8, 0, 8, 0x9a }, +- { 0x9bc3, 0, 8, 0x01 }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x7c }, +- { 0x9bc6, 0, 8, 0x02 }, +- { 0x9bba, 0, 8, 0xfc }, +- { 0x9bc9, 0, 8, 0xaa }, +- { 0xd011, 0, 8, 0x6b }, +- { 0xd012, 0, 2, 0x00 }, +- { 0xd013, 0, 8, 0x88 }, +- { 0xd014, 0, 2, 0x02 }, +- { 0xd040, 0, 8, 0x6b }, +- { 0xd041, 0, 2, 0x00 }, +- { 0xd042, 0, 8, 0x7c }, +- { 0xd043, 0, 2, 0x02 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +- { 0x9bd0, 0, 8, 0x9e }, +- { 0x9be4, 0, 8, 0xff }, +- { 0x9bbd, 0, 8, 0x9e }, +- { 0x9be2, 0, 8, 0x25 }, +- { 0x9bee, 0, 1, 0x01 }, +- { 0xd73b, 3, 1, 0x00 }, +-}; +- +-/* unknown, probably for tin can tuner, tuner init +- AF9013_TUNER_UNKNOWN = 140 */ +-static const struct af9013_reg_bit tuner_init_unknown[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x02 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x01 }, +- { 0x9be3, 0, 8, 0x01 }, +- { 0xd1a0, 1, 1, 0x00 }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +- { 0x9bb9, 0, 8, 0x00 }, +- { 0x9bcd, 0, 8, 0x18 }, +- { 0x9bff, 0, 8, 0x2c }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0xdf }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x44 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0x00 }, +- { 0xd00d, 0, 2, 0x02 }, +- { 0xd00a, 0, 8, 0xf6 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bba, 0, 8, 0xf9 }, +- { 0x9bc8, 0, 8, 0xaa }, +- { 0x9bc3, 0, 8, 0xdf }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x00 }, +- { 0x9bc6, 0, 8, 0x02 }, +- { 0x9bc9, 0, 8, 0xf0 }, +- { 0xd011, 0, 8, 0x3c }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0xf7 }, +- { 0xd014, 0, 2, 0x02 }, +- { 0xd040, 0, 8, 0x0b }, +- { 0xd041, 0, 2, 0x02 }, +- { 0xd042, 0, 8, 0x4d }, +- { 0xd043, 0, 2, 0x00 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +-}; +- +-/* NXP TDA18271 & TDA18218 tuner init +- AF9013_TUNER_TDA18271 = 156 +- AF9013_TUNER_TDA18218 = 179 */ +-static const struct af9013_reg_bit tuner_init_tda18271[] = { +- { 0x9bd5, 0, 8, 0x01 }, +- { 0x9bd6, 0, 8, 0x04 }, +- { 0xd1a0, 1, 1, 0x01 }, +- { 0xd000, 0, 1, 0x01 }, +- { 0xd000, 1, 1, 0x00 }, +- { 0xd001, 1, 1, 0x01 }, +- { 0xd001, 0, 1, 0x00 }, +- { 0xd001, 5, 1, 0x00 }, +- { 0xd002, 0, 5, 0x19 }, +- { 0xd003, 0, 5, 0x1a }, +- { 0xd004, 0, 5, 0x19 }, +- { 0xd005, 0, 5, 0x1a }, +- { 0xd00e, 0, 5, 0x10 }, +- { 0xd00f, 0, 3, 0x04 }, +- { 0xd00f, 3, 3, 0x05 }, +- { 0xd010, 0, 3, 0x04 }, +- { 0xd010, 3, 3, 0x05 }, +- { 0xd016, 4, 4, 0x03 }, +- { 0xd01f, 0, 6, 0x0a }, +- { 0xd020, 0, 6, 0x0a }, +- { 0x9bda, 0, 8, 0x01 }, +- { 0x9be3, 0, 8, 0x01 }, +- { 0xd1a0, 1, 1, 0x00 }, +- { 0x9bbe, 0, 1, 0x01 }, +- { 0x9bcc, 0, 1, 0x01 }, +- { 0x9bb9, 0, 8, 0x00 }, +- { 0x9bcd, 0, 8, 0x18 }, +- { 0x9bff, 0, 8, 0x2c }, +- { 0xd015, 0, 8, 0x46 }, +- { 0xd016, 0, 1, 0x00 }, +- { 0xd044, 0, 8, 0x46 }, +- { 0xd045, 0, 1, 0x00 }, +- { 0xd008, 0, 8, 0xdf }, +- { 0xd009, 0, 2, 0x02 }, +- { 0xd006, 0, 8, 0x44 }, +- { 0xd007, 0, 2, 0x01 }, +- { 0xd00c, 0, 8, 0x00 }, +- { 0xd00d, 0, 2, 0x02 }, +- { 0xd00a, 0, 8, 0xf6 }, +- { 0xd00b, 0, 2, 0x01 }, +- { 0x9bba, 0, 8, 0xf9 }, +- { 0x9bc8, 0, 8, 0xaa }, +- { 0x9bc3, 0, 8, 0xdf }, +- { 0x9bc4, 0, 8, 0x02 }, +- { 0x9bc5, 0, 8, 0x00 }, +- { 0x9bc6, 0, 8, 0x02 }, +- { 0x9bc9, 0, 8, 0xf0 }, +- { 0xd011, 0, 8, 0x3c }, +- { 0xd012, 0, 2, 0x01 }, +- { 0xd013, 0, 8, 0xf7 }, +- { 0xd014, 0, 2, 0x02 }, +- { 0xd040, 0, 8, 0x0b }, +- { 0xd041, 0, 2, 0x02 }, +- { 0xd042, 0, 8, 0x4d }, +- { 0xd043, 0, 2, 0x00 }, +- { 0xd045, 1, 1, 0x00 }, +- { 0x9bcf, 0, 1, 0x01 }, +- { 0xd045, 2, 1, 0x01 }, +- { 0xd04f, 0, 8, 0x9a }, +- { 0xd050, 0, 1, 0x01 }, +- { 0xd051, 0, 8, 0x5a }, +- { 0xd052, 0, 1, 0x01 }, +- { 0xd053, 0, 8, 0x50 }, +- { 0xd054, 0, 8, 0x46 }, +- { 0x9bd7, 0, 8, 0x0a }, +- { 0x9bd8, 0, 8, 0x14 }, +- { 0x9bd9, 0, 8, 0x08 }, +- { 0x9bd0, 0, 8, 0xa8 }, +- { 0x9be4, 0, 8, 0x7f }, +- { 0x9bbd, 0, 8, 0xa8 }, +- { 0x9be2, 0, 8, 0x20 }, +- { 0x9bee, 0, 1, 0x01 }, +-}; +- +-#endif /* AF9013_PRIV_H */ +diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c +deleted file mode 100644 +index a2261ea..0000000 +--- a/drivers/media/dvb/frontends/atbm8830.c ++++ /dev/null +@@ -1,508 +0,0 @@ +-/* +- * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator +- * ATBM8830, ATBM8831 +- * +- * Copyright (C) 2009 David T.L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include "dvb_frontend.h" +- +-#include "atbm8830.h" +-#include "atbm8830_priv.h" +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "atbm8830: " args); \ +- } while (0) +- +-static int debug; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) +-{ +- int ret = 0; +- u8 dev_addr; +- u8 buf1[] = { reg >> 8, reg & 0xFF }; +- u8 buf2[] = { data }; +- struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; +- struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; +- +- dev_addr = priv->config->demod_address; +- msg1.addr = dev_addr; +- msg2.addr = dev_addr; +- +- if (debug >= 2) +- dprintk("%s: reg=0x%04X, data=0x%02X\n", __func__, reg, data); +- +- ret = i2c_transfer(priv->i2c, &msg1, 1); +- if (ret != 1) +- return -EIO; +- +- ret = i2c_transfer(priv->i2c, &msg2, 1); +- return (ret != 1) ? -EIO : 0; +-} +- +-static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) +-{ +- int ret; +- u8 dev_addr; +- +- u8 buf1[] = { reg >> 8, reg & 0xFF }; +- u8 buf2[] = { 0 }; +- struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; +- struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; +- +- dev_addr = priv->config->demod_address; +- msg1.addr = dev_addr; +- msg2.addr = dev_addr; +- +- ret = i2c_transfer(priv->i2c, &msg1, 1); +- if (ret != 1) { +- dprintk("%s: error reg=0x%04x, ret=%i\n", __func__, reg, ret); +- return -EIO; +- } +- +- ret = i2c_transfer(priv->i2c, &msg2, 1); +- if (ret != 1) +- return -EIO; +- +- *p_data = buf2[0]; +- if (debug >= 2) +- dprintk("%s: reg=0x%04X, data=0x%02X\n", +- __func__, reg, buf2[0]); +- +- return 0; +-} +- +-/* Lock register latch so that multi-register read is atomic */ +-static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) +-{ +- return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); +-} +- +-static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +-{ +- u32 val; +- u64 t; +- +- /* 0x100000 * freq / 30.4MHz */ +- t = (u64)0x100000 * freq; +- do_div(t, 30400); +- val = t; +- +- atbm8830_write_reg(priv, REG_OSC_CLK, val); +- atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); +- atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); +- +- return 0; +-} +- +-static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +-{ +- +- u32 fs = priv->config->osc_clk_freq; +- u64 t; +- u32 val; +- u8 dat; +- +- if (freq != 0) { +- /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ +- t = (u64) 2 * 31416 * (freq - fs); +- t <<= 22; +- do_div(t, fs); +- do_div(t, 1000); +- val = t; +- +- atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); +- atbm8830_write_reg(priv, REG_IF_FREQ, val); +- atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); +- atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); +- +- atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); +- dat &= 0xFC; +- atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); +- } else { +- /* Zero IF */ +- atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); +- +- atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); +- dat &= 0xFC; +- dat |= 0x02; +- atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); +- +- if (priv->config->zif_swap_iq) +- atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); +- else +- atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); +- } +- +- return 0; +-} +- +-static int is_locked(struct atbm_state *priv, u8 *locked) +-{ +- u8 status; +- +- atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); +- +- if (locked != NULL) +- *locked = (status == 1); +- return 0; +-} +- +-static int set_agc_config(struct atbm_state *priv, +- u8 min, u8 max, u8 hold_loop) +-{ +- /* no effect if both min and max are zero */ +- if (!min && !max) +- return 0; +- +- atbm8830_write_reg(priv, REG_AGC_MIN, min); +- atbm8830_write_reg(priv, REG_AGC_MAX, max); +- atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); +- +- return 0; +-} +- +-static int set_static_channel_mode(struct atbm_state *priv) +-{ +- int i; +- +- for (i = 0; i < 5; i++) +- atbm8830_write_reg(priv, 0x099B + i, 0x08); +- +- atbm8830_write_reg(priv, 0x095B, 0x7F); +- atbm8830_write_reg(priv, 0x09CB, 0x01); +- atbm8830_write_reg(priv, 0x09CC, 0x7F); +- atbm8830_write_reg(priv, 0x09CD, 0x7F); +- atbm8830_write_reg(priv, 0x0E01, 0x20); +- +- /* For single carrier */ +- atbm8830_write_reg(priv, 0x0B03, 0x0A); +- atbm8830_write_reg(priv, 0x0935, 0x10); +- atbm8830_write_reg(priv, 0x0936, 0x08); +- atbm8830_write_reg(priv, 0x093E, 0x08); +- atbm8830_write_reg(priv, 0x096E, 0x06); +- +- /* frame_count_max0 */ +- atbm8830_write_reg(priv, 0x0B09, 0x00); +- /* frame_count_max1 */ +- atbm8830_write_reg(priv, 0x0B0A, 0x08); +- +- return 0; +-} +- +-static int set_ts_config(struct atbm_state *priv) +-{ +- const struct atbm8830_config *cfg = priv->config; +- +- /*Set parallel/serial ts mode*/ +- atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); +- atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); +- /*Set ts sampling edge*/ +- atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, +- cfg->ts_sampling_edge ? 1 : 0); +- /*Set ts clock freerun*/ +- atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, +- cfg->ts_clk_gated ? 0 : 1); +- +- return 0; +-} +- +-static int atbm8830_init(struct dvb_frontend *fe) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- const struct atbm8830_config *cfg = priv->config; +- +- /*Set oscillator frequency*/ +- set_osc_freq(priv, cfg->osc_clk_freq); +- +- /*Set IF frequency*/ +- set_if_freq(priv, cfg->if_freq); +- +- /*Set AGC Config*/ +- set_agc_config(priv, cfg->agc_min, cfg->agc_max, +- cfg->agc_hold_loop); +- +- /*Set static channel mode*/ +- set_static_channel_mode(priv); +- +- set_ts_config(priv); +- /*Turn off DSP reset*/ +- atbm8830_write_reg(priv, 0x000A, 0); +- +- /*SW version test*/ +- atbm8830_write_reg(priv, 0x020C, 11); +- +- /* Run */ +- atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); +- +- return 0; +-} +- +- +-static void atbm8830_release(struct dvb_frontend *fe) +-{ +- struct atbm_state *state = fe->demodulator_priv; +- dprintk("%s\n", __func__); +- +- kfree(state); +-} +- +-static int atbm8830_set_fe(struct dvb_frontend *fe) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- int i; +- u8 locked = 0; +- dprintk("%s\n", __func__); +- +- /* set frequency */ +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* start auto lock */ +- for (i = 0; i < 10; i++) { +- mdelay(100); +- dprintk("Try %d\n", i); +- is_locked(priv, &locked); +- if (locked != 0) { +- dprintk("ATBM8830 locked!\n"); +- break; +- } +- } +- +- return 0; +-} +- +-static int atbm8830_get_fe(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- dprintk("%s\n", __func__); +- +- /* TODO: get real readings from device */ +- /* inversion status */ +- c->inversion = INVERSION_OFF; +- +- /* bandwidth */ +- c->bandwidth_hz = 8000000; +- +- c->code_rate_HP = FEC_AUTO; +- c->code_rate_LP = FEC_AUTO; +- +- c->modulation = QAM_AUTO; +- +- /* transmission mode */ +- c->transmission_mode = TRANSMISSION_MODE_AUTO; +- +- /* guard interval */ +- c->guard_interval = GUARD_INTERVAL_AUTO; +- +- /* hierarchy */ +- c->hierarchy = HIERARCHY_NONE; +- +- return 0; +-} +- +-static int atbm8830_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- fesettings->min_delay_ms = 0; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- u8 locked = 0; +- u8 agc_locked = 0; +- +- dprintk("%s\n", __func__); +- *fe_status = 0; +- +- is_locked(priv, &locked); +- if (locked) { +- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- } +- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); +- +- atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); +- dprintk("AGC Lock: %d\n", agc_locked); +- +- return 0; +-} +- +-static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- u32 frame_err; +- u8 t; +- +- dprintk("%s\n", __func__); +- +- atbm8830_reglatch_lock(priv, 1); +- +- atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); +- frame_err = t & 0x7F; +- frame_err <<= 8; +- atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); +- frame_err |= t; +- +- atbm8830_reglatch_lock(priv, 0); +- +- *ber = frame_err * 100 / 32767; +- +- dprintk("%s: ber=0x%x\n", __func__, *ber); +- return 0; +-} +- +-static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- u32 pwm; +- u8 t; +- +- dprintk("%s\n", __func__); +- atbm8830_reglatch_lock(priv, 1); +- +- atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); +- pwm = t & 0x03; +- pwm <<= 8; +- atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); +- pwm |= t; +- +- atbm8830_reglatch_lock(priv, 0); +- +- dprintk("AGC PWM = 0x%02X\n", pwm); +- pwm = 0x400 - pwm; +- +- *signal = pwm * 0x10000 / 0x400; +- +- return 0; +-} +- +-static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- dprintk("%s\n", __func__); +- *snr = 0; +- return 0; +-} +- +-static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- dprintk("%s\n", __func__); +- *ucblocks = 0; +- return 0; +-} +- +-static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct atbm_state *priv = fe->demodulator_priv; +- +- return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); +-} +- +-static struct dvb_frontend_ops atbm8830_ops = { +- .delsys = { SYS_DMBTH }, +- .info = { +- .name = "AltoBeam ATBM8830/8831 DMB-TH", +- .frequency_min = 474000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 10000, +- .caps = +- FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO +- }, +- +- .release = atbm8830_release, +- +- .init = atbm8830_init, +- .sleep = NULL, +- .write = NULL, +- .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, +- +- .set_frontend = atbm8830_set_fe, +- .get_frontend = atbm8830_get_fe, +- .get_tune_settings = atbm8830_get_tune_settings, +- +- .read_status = atbm8830_read_status, +- .read_ber = atbm8830_read_ber, +- .read_signal_strength = atbm8830_read_signal_strength, +- .read_snr = atbm8830_read_snr, +- .read_ucblocks = atbm8830_read_ucblocks, +-}; +- +-struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, +- struct i2c_adapter *i2c) +-{ +- struct atbm_state *priv = NULL; +- u8 data = 0; +- +- dprintk("%s()\n", __func__); +- +- if (config == NULL || i2c == NULL) +- return NULL; +- +- priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); +- if (priv == NULL) +- goto error_out; +- +- priv->config = config; +- priv->i2c = i2c; +- +- /* check if the demod is there */ +- if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { +- dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", +- __func__, priv->config->demod_address); +- goto error_out; +- } +- dprintk("atbm8830 chip id: 0x%02X\n", data); +- +- memcpy(&priv->frontend.ops, &atbm8830_ops, +- sizeof(struct dvb_frontend_ops)); +- priv->frontend.demodulator_priv = priv; +- +- atbm8830_init(&priv->frontend); +- +- atbm8830_i2c_gate_ctrl(&priv->frontend, 1); +- +- return &priv->frontend; +- +-error_out: +- dprintk("%s() error_out\n", __func__); +- kfree(priv); +- return NULL; +- +-} +-EXPORT_SYMBOL(atbm8830_attach); +- +-MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); +-MODULE_AUTHOR("David T. L. Wong "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h +deleted file mode 100644 +index 0242733..0000000 +--- a/drivers/media/dvb/frontends/atbm8830.h ++++ /dev/null +@@ -1,76 +0,0 @@ +-/* +- * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator +- * ATBM8830, ATBM8831 +- * +- * Copyright (C) 2009 David T.L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __ATBM8830_H__ +-#define __ATBM8830_H__ +- +-#include +-#include +- +-#define ATBM8830_PROD_8830 0 +-#define ATBM8830_PROD_8831 1 +- +-struct atbm8830_config { +- +- /* product type */ +- u8 prod; +- +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* parallel or serial transport stream */ +- u8 serial_ts; +- +- /* transport stream clock output only when receiving valid stream */ +- u8 ts_clk_gated; +- +- /* Decoder sample TS data at rising edge of clock */ +- u8 ts_sampling_edge; +- +- /* Oscillator clock frequency */ +- u32 osc_clk_freq; /* in kHz */ +- +- /* IF frequency */ +- u32 if_freq; /* in kHz */ +- +- /* Swap I/Q for zero IF */ +- u8 zif_swap_iq; +- +- /* Tuner AGC settings */ +- u8 agc_min; +- u8 agc_max; +- u8 agc_hold_loop; +-}; +- +-#if defined(CONFIG_DVB_ATBM8830) || \ +- (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline +-struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, +- struct i2c_adapter *i2c) { +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_ATBM8830 */ +- +-#endif /* __ATBM8830_H__ */ +diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h +deleted file mode 100644 +index d460058..0000000 +--- a/drivers/media/dvb/frontends/atbm8830_priv.h ++++ /dev/null +@@ -1,75 +0,0 @@ +-/* +- * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator +- * ATBM8830, ATBM8831 +- * +- * Copyright (C) 2009 David T.L. Wong +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __ATBM8830_PRIV_H +-#define __ATBM8830_PRIV_H +- +-struct atbm_state { +- struct i2c_adapter *i2c; +- /* configuration settings */ +- const struct atbm8830_config *config; +- struct dvb_frontend frontend; +-}; +- +-#define REG_CHIP_ID 0x0000 +-#define REG_TUNER_BASEBAND 0x0001 +-#define REG_DEMOD_RUN 0x0004 +-#define REG_DSP_RESET 0x0005 +-#define REG_RAM_RESET 0x0006 +-#define REG_ADC_RESET 0x0007 +-#define REG_TSPORT_RESET 0x0008 +-#define REG_BLKERR_POL 0x000C +-#define REG_I2C_GATE 0x0103 +-#define REG_TS_SAMPLE_EDGE 0x0301 +-#define REG_TS_PKT_LEN_204 0x0302 +-#define REG_TS_PKT_LEN_AUTO 0x0303 +-#define REG_TS_SERIAL 0x0305 +-#define REG_TS_CLK_FREERUN 0x0306 +-#define REG_TS_VALID_MODE 0x0307 +-#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ +- +-#define REG_TS_ERRBIT_USE 0x030C +-#define REG_LOCK_STATUS 0x030D +-#define REG_ADC_CONFIG 0x0602 +-#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ +-#define REG_DETECTED_PN_MODE 0x082D +-#define REG_READ_LATCH 0x084D +-#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ +-#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ +-#define REG_BYPASS_CCI 0x0A06 +-#define REG_ANALOG_LUMA_DETECTED 0x0A25 +-#define REG_ANALOG_AUDIO_DETECTED 0x0A26 +-#define REG_ANALOG_CHROMA_DETECTED 0x0A39 +-#define REG_FRAME_ERR_CNT 0x0B04 +-#define REG_USE_EXT_ADC 0x0C00 +-#define REG_SWAP_I_Q 0x0C01 +-#define REG_TPS_MANUAL 0x0D01 +-#define REG_TPS_CONFIG 0x0D02 +-#define REG_BYPASS_DEINTERLEAVER 0x0E00 +-#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ +-#define REG_AGC_MIN 0x1020 +-#define REG_AGC_MAX 0x1023 +-#define REG_AGC_LOCK 0x1027 +-#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ +-#define REG_AGC_HOLD_LOOP 0x1031 +- +-#endif +- +diff --git a/drivers/media/dvb/frontends/au8522.h b/drivers/media/dvb/frontends/au8522.h +deleted file mode 100644 +index 565dcf3..0000000 +--- a/drivers/media/dvb/frontends/au8522.h ++++ /dev/null +@@ -1,98 +0,0 @@ +-/* +- Auvitek AU8522 QAM/8VSB demodulator driver +- +- Copyright (C) 2008 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef __AU8522_H__ +-#define __AU8522_H__ +- +-#include +- +-enum au8522_if_freq { +- AU8522_IF_6MHZ = 0, +- AU8522_IF_4MHZ, +- AU8522_IF_3_25MHZ, +-}; +- +-struct au8522_led_config { +- u16 vsb8_strong; +- u16 qam64_strong; +- u16 qam256_strong; +- +- u16 gpio_output; +- /* unset hi bits, set low bits */ +- u16 gpio_output_enable; +- u16 gpio_output_disable; +- +- u16 gpio_leds; +- u8 *led_states; +- unsigned int num_led_states; +-}; +- +-struct au8522_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* Return lock status based on tuner lock, or demod lock */ +-#define AU8522_TUNERLOCKING 0 +-#define AU8522_DEMODLOCKING 1 +- u8 status_mode; +- +- struct au8522_led_config *led_cfg; +- +- enum au8522_if_freq vsb_if; +- enum au8522_if_freq qam_if; +-}; +- +-#if defined(CONFIG_DVB_AU8522) || \ +- (defined(CONFIG_DVB_AU8522_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *au8522_attach(const struct au8522_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline +-struct dvb_frontend *au8522_attach(const struct au8522_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_AU8522 */ +- +-/* Other modes may need to be added later */ +-enum au8522_video_input { +- AU8522_COMPOSITE_CH1 = 1, +- AU8522_COMPOSITE_CH2, +- AU8522_COMPOSITE_CH3, +- AU8522_COMPOSITE_CH4, +- AU8522_COMPOSITE_CH4_SIF, +- AU8522_SVIDEO_CH13, +- AU8522_SVIDEO_CH24, +-}; +- +-enum au8522_audio_input { +- AU8522_AUDIO_NONE, +- AU8522_AUDIO_SIF, +-}; +- +-#endif /* __AU8522_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c +deleted file mode 100644 +index 2b248c1..0000000 +--- a/drivers/media/dvb/frontends/au8522_decoder.c ++++ /dev/null +@@ -1,853 +0,0 @@ +-/* +- * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder +- * +- * Copyright (C) 2009 Devin Heitmueller +- * Copyright (C) 2005-2008 Auvitek International, Ltd. +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * As published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA. +- */ +- +-/* Developer notes: +- * +- * VBI support is not yet working +- * Enough is implemented here for CVBS and S-Video inputs, but the actual +- * analog demodulator code isn't implemented (not needed for xc5000 since it +- * has its own demodulator and outputs CVBS) +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "au8522.h" +-#include "au8522_priv.h" +- +-MODULE_AUTHOR("Devin Heitmueller"); +-MODULE_LICENSE("GPL"); +- +-static int au8522_analog_debug; +- +- +-module_param_named(analog_debug, au8522_analog_debug, int, 0644); +- +-MODULE_PARM_DESC(analog_debug, +- "Analog debugging messages [0=Off (default) 1=On]"); +- +-struct au8522_register_config { +- u16 reg_name; +- u8 reg_val[8]; +-}; +- +- +-/* Video Decoder Filter Coefficients +- The values are as follows from left to right +- 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13" +-*/ +-static const struct au8522_register_config filter_coef[] = { +- {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} }, +- {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} }, +- {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} }, +- {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} }, +- {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} }, +- {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} }, +- {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} }, +- {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} }, +- {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} }, +- {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} }, +- {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} }, +- {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} }, +- {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} }, +- {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} }, +- {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} }, +- {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} }, +- {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} }, +- {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} }, +- {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} }, +- {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} }, +- {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} }, +- {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} }, +- +-}; +-#define NUM_FILTER_COEF (sizeof(filter_coef)\ +- / sizeof(struct au8522_register_config)) +- +- +-/* Registers 0x060b through 0x0652 are the LP Filter coefficients +- The values are as follows from left to right +- 0="SIF" 1="ATVRF/ATVRF13" +- Note: the "ATVRF/ATVRF13" mode has never been tested +-*/ +-static const struct au8522_register_config lpfilter_coef[] = { +- {0x060b, {0x21, 0x0b} }, +- {0x060c, {0xad, 0xad} }, +- {0x060d, {0x70, 0xf0} }, +- {0x060e, {0xea, 0xe9} }, +- {0x060f, {0xdd, 0xdd} }, +- {0x0610, {0x08, 0x64} }, +- {0x0611, {0x60, 0x60} }, +- {0x0612, {0xf8, 0xb2} }, +- {0x0613, {0x01, 0x02} }, +- {0x0614, {0xe4, 0xb4} }, +- {0x0615, {0x19, 0x02} }, +- {0x0616, {0xae, 0x2e} }, +- {0x0617, {0xee, 0xc5} }, +- {0x0618, {0x56, 0x56} }, +- {0x0619, {0x30, 0x58} }, +- {0x061a, {0xf9, 0xf8} }, +- {0x061b, {0x24, 0x64} }, +- {0x061c, {0x07, 0x07} }, +- {0x061d, {0x30, 0x30} }, +- {0x061e, {0xa9, 0xed} }, +- {0x061f, {0x09, 0x0b} }, +- {0x0620, {0x42, 0xc2} }, +- {0x0621, {0x1d, 0x2a} }, +- {0x0622, {0xd6, 0x56} }, +- {0x0623, {0x95, 0x8b} }, +- {0x0624, {0x2b, 0x2b} }, +- {0x0625, {0x30, 0x24} }, +- {0x0626, {0x3e, 0x3e} }, +- {0x0627, {0x62, 0xe2} }, +- {0x0628, {0xe9, 0xf5} }, +- {0x0629, {0x99, 0x19} }, +- {0x062a, {0xd4, 0x11} }, +- {0x062b, {0x03, 0x04} }, +- {0x062c, {0xb5, 0x85} }, +- {0x062d, {0x1e, 0x20} }, +- {0x062e, {0x2a, 0xea} }, +- {0x062f, {0xd7, 0xd2} }, +- {0x0630, {0x15, 0x15} }, +- {0x0631, {0xa3, 0xa9} }, +- {0x0632, {0x1f, 0x1f} }, +- {0x0633, {0xf9, 0xd1} }, +- {0x0634, {0xc0, 0xc3} }, +- {0x0635, {0x4d, 0x8d} }, +- {0x0636, {0x21, 0x31} }, +- {0x0637, {0x83, 0x83} }, +- {0x0638, {0x08, 0x8c} }, +- {0x0639, {0x19, 0x19} }, +- {0x063a, {0x45, 0xa5} }, +- {0x063b, {0xef, 0xec} }, +- {0x063c, {0x8a, 0x8a} }, +- {0x063d, {0xf4, 0xf6} }, +- {0x063e, {0x8f, 0x8f} }, +- {0x063f, {0x44, 0x0c} }, +- {0x0640, {0xef, 0xf0} }, +- {0x0641, {0x66, 0x66} }, +- {0x0642, {0xcc, 0xd2} }, +- {0x0643, {0x41, 0x41} }, +- {0x0644, {0x63, 0x93} }, +- {0x0645, {0x8e, 0x8e} }, +- {0x0646, {0xa2, 0x42} }, +- {0x0647, {0x7b, 0x7b} }, +- {0x0648, {0x04, 0x04} }, +- {0x0649, {0x00, 0x00} }, +- {0x064a, {0x40, 0x40} }, +- {0x064b, {0x8c, 0x98} }, +- {0x064c, {0x00, 0x00} }, +- {0x064d, {0x63, 0xc3} }, +- {0x064e, {0x04, 0x04} }, +- {0x064f, {0x20, 0x20} }, +- {0x0650, {0x00, 0x00} }, +- {0x0651, {0x40, 0x40} }, +- {0x0652, {0x01, 0x01} }, +-}; +-#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\ +- / sizeof(struct au8522_register_config)) +- +-static inline struct au8522_state *to_state(struct v4l2_subdev *sd) +-{ +- return container_of(sd, struct au8522_state, sd); +-} +- +-static void setup_vbi(struct au8522_state *state, int aud_input) +-{ +- int i; +- +- /* These are set to zero regardless of what mode we're in */ +- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H, +- 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H, +- 0x00); +- au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H, +- 0x00); +- +- /* Setup the VBI registers */ +- for (i = 0x30; i < 0x60; i++) +- au8522_writereg(state, i, 0x40); +- +- /* For some reason, every register is 0x40 except register 0x44 +- (confirmed via the HVR-950q USB capture) */ +- au8522_writereg(state, 0x44, 0x60); +- +- /* Enable VBI (we always do this regardless of whether the user is +- viewing closed caption info) */ +- au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, +- AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON); +- +-} +- +-static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) +-{ +- int i; +- int filter_coef_type; +- +- /* Provide reasonable defaults for picture tuning values */ +- au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07); +- au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed); +- state->brightness = 0xed - 128; +- au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79); +- state->contrast = 0x79; +- au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); +- au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); +- state->saturation = 0x80; +- au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); +- au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); +- state->hue = 0x00; +- +- /* Other decoder registers */ +- au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); +- +- if (input_mode == 0x23) { +- /* S-Video input mapping */ +- au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04); +- } else { +- /* All other modes (CVBS/ATVRF etc.) */ +- au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00); +- } +- +- au8522_writereg(state, AU8522_TVDEC_PGA_REG012H, +- AU8522_TVDEC_PGA_REG012H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H, +- AU8522_TVDEC_COMB_MODE_REG015H_CVBS); +- au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H, +- AU8522_TVDED_DBG_MODE_REG060H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H, +- AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13); +- au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H, +- AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13); +- au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H, +- AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H, +- AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H, +- AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H, +- AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H, +- AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H, +- AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H, +- AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH, +- AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH, +- AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS); +- if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || +- input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { +- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, +- AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO); +- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, +- AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO); +- } else { +- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH, +- AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH, +- AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS); +- } +- au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH, +- AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH, +- AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H, +- AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS); +- au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS); +- au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H, +- AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS); +- au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS); +- au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H, +- AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H, +- AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H, +- AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS); +- au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH, +- AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH, +- AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS); +- au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H, +- AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS); +- au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H, +- AU8522_TOREGAAGC_REG0E5H_CVBS); +- au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS); +- +- setup_vbi(state, 0); +- +- if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 || +- input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) { +- /* Despite what the table says, for the HVR-950q we still need +- to be in CVBS mode for the S-Video input (reason unknown). */ +- /* filter_coef_type = 3; */ +- filter_coef_type = 5; +- } else { +- filter_coef_type = 5; +- } +- +- /* Load the Video Decoder Filter Coefficients */ +- for (i = 0; i < NUM_FILTER_COEF; i++) { +- au8522_writereg(state, filter_coef[i].reg_name, +- filter_coef[i].reg_val[filter_coef_type]); +- } +- +- /* It's not clear what these registers are for, but they are always +- set to the same value regardless of what mode we're in */ +- au8522_writereg(state, AU8522_REG42EH, 0x87); +- au8522_writereg(state, AU8522_REG42FH, 0xa2); +- au8522_writereg(state, AU8522_REG430H, 0xbf); +- au8522_writereg(state, AU8522_REG431H, 0xcb); +- au8522_writereg(state, AU8522_REG432H, 0xa1); +- au8522_writereg(state, AU8522_REG433H, 0x41); +- au8522_writereg(state, AU8522_REG434H, 0x88); +- au8522_writereg(state, AU8522_REG435H, 0xc2); +- au8522_writereg(state, AU8522_REG436H, 0x3c); +-} +- +-static void au8522_setup_cvbs_mode(struct au8522_state *state) +-{ +- /* here we're going to try the pre-programmed route */ +- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, +- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); +- +- /* PGA in automatic mode */ +- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); +- +- /* Enable clamping control */ +- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); +- +- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, +- AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); +- +- setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1); +- +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +-} +- +-static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state) +-{ +- /* here we're going to try the pre-programmed route */ +- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, +- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS); +- +- /* It's not clear why we have to have the PGA in automatic mode while +- enabling clamp control, but it's what Windows does */ +- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); +- +- /* Enable clamping control */ +- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e); +- +- /* Disable automatic PGA (since the CVBS is coming from the tuner) */ +- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10); +- +- /* Set input mode to CVBS on channel 4 with SIF audio input enabled */ +- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, +- AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); +- +- setup_decoder_defaults(state, +- AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF); +- +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +-} +- +-static void au8522_setup_svideo_mode(struct au8522_state *state) +-{ +- au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H, +- AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO); +- +- /* Set input to Y on Channe1, C on Channel 3 */ +- au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H, +- AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); +- +- /* PGA in automatic mode */ +- au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00); +- +- /* Enable clamping control */ +- au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00); +- +- setup_decoder_defaults(state, +- AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13); +- +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +-} +- +-/* ----------------------------------------------------------------------- */ +- +-static void disable_audio_input(struct au8522_state *state) +-{ +- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); +- +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04); +- au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02); +- +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO); +-} +- +-/* 0=disable, 1=SIF */ +-static void set_audio_input(struct au8522_state *state, int aud_input) +-{ +- int i; +- +- /* Note that this function needs to be used in conjunction with setting +- the input routing via register 0x81 */ +- +- if (aud_input == AU8522_AUDIO_NONE) { +- disable_audio_input(state); +- return; +- } +- +- if (aud_input != AU8522_AUDIO_SIF) { +- /* The caller asked for a mode we don't currently support */ +- printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n", +- aud_input); +- return; +- } +- +- /* Load the Audio Decoder Filter Coefficients */ +- for (i = 0; i < NUM_LPFILTER_COEF; i++) { +- au8522_writereg(state, lpfilter_coef[i].reg_name, +- lpfilter_coef[i].reg_val[0]); +- } +- +- /* Setup audio */ +- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00); +- au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80); +- au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84); +- msleep(150); +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00); +- msleep(1); +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d); +- msleep(50); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff); +- msleep(80); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F); +- au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F); +- au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO); +- au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82); +- msleep(70); +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09); +- au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03); +- au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2); +-} +- +-/* ----------------------------------------------------------------------- */ +- +-static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +-{ +- struct au8522_state *state = to_state(sd); +- +- switch (ctrl->id) { +- case V4L2_CID_BRIGHTNESS: +- state->brightness = ctrl->value; +- au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, +- ctrl->value - 128); +- break; +- case V4L2_CID_CONTRAST: +- state->contrast = ctrl->value; +- au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, +- ctrl->value); +- break; +- case V4L2_CID_SATURATION: +- state->saturation = ctrl->value; +- au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, +- ctrl->value); +- au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, +- ctrl->value); +- break; +- case V4L2_CID_HUE: +- state->hue = ctrl->value; +- au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, +- ctrl->value >> 8); +- au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, +- ctrl->value & 0xFF); +- break; +- case V4L2_CID_AUDIO_VOLUME: +- case V4L2_CID_AUDIO_BASS: +- case V4L2_CID_AUDIO_TREBLE: +- case V4L2_CID_AUDIO_BALANCE: +- case V4L2_CID_AUDIO_MUTE: +- /* Not yet implemented */ +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +-{ +- struct au8522_state *state = to_state(sd); +- +- /* Note that we are using values cached in the state structure instead +- of reading the registers due to issues with i2c reads not working +- properly/consistently yet on the HVR-950q */ +- +- switch (ctrl->id) { +- case V4L2_CID_BRIGHTNESS: +- ctrl->value = state->brightness; +- break; +- case V4L2_CID_CONTRAST: +- ctrl->value = state->contrast; +- break; +- case V4L2_CID_SATURATION: +- ctrl->value = state->saturation; +- break; +- case V4L2_CID_HUE: +- ctrl->value = state->hue; +- break; +- case V4L2_CID_AUDIO_VOLUME: +- case V4L2_CID_AUDIO_BASS: +- case V4L2_CID_AUDIO_TREBLE: +- case V4L2_CID_AUDIO_BALANCE: +- case V4L2_CID_AUDIO_MUTE: +- /* Not yet supported */ +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-/* ----------------------------------------------------------------------- */ +- +-#ifdef CONFIG_VIDEO_ADV_DEBUG +-static int au8522_g_register(struct v4l2_subdev *sd, +- struct v4l2_dbg_register *reg) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(sd); +- struct au8522_state *state = to_state(sd); +- +- if (!v4l2_chip_match_i2c_client(client, ®->match)) +- return -EINVAL; +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- reg->val = au8522_readreg(state, reg->reg & 0xffff); +- return 0; +-} +- +-static int au8522_s_register(struct v4l2_subdev *sd, +- struct v4l2_dbg_register *reg) +-{ +- struct i2c_client *client = v4l2_get_subdevdata(sd); +- struct au8522_state *state = to_state(sd); +- +- if (!v4l2_chip_match_i2c_client(client, ®->match)) +- return -EINVAL; +- if (!capable(CAP_SYS_ADMIN)) +- return -EPERM; +- au8522_writereg(state, reg->reg, reg->val & 0xff); +- return 0; +-} +-#endif +- +-static int au8522_s_stream(struct v4l2_subdev *sd, int enable) +-{ +- struct au8522_state *state = to_state(sd); +- +- if (enable) { +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- 0x01); +- msleep(1); +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS); +- } else { +- /* This does not completely power down the device +- (it only reduces it from around 140ma to 80ma) */ +- au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, +- 1 << 5); +- } +- return 0; +-} +- +-static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +-{ +- switch (qc->id) { +- case V4L2_CID_CONTRAST: +- return v4l2_ctrl_query_fill(qc, 0, 255, 1, +- AU8522_TVDEC_CONTRAST_REG00BH_CVBS); +- case V4L2_CID_BRIGHTNESS: +- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 109); +- case V4L2_CID_SATURATION: +- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); +- case V4L2_CID_HUE: +- return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0); +- default: +- break; +- } +- +- qc->type = 0; +- return -EINVAL; +-} +- +-static int au8522_reset(struct v4l2_subdev *sd, u32 val) +-{ +- struct au8522_state *state = to_state(sd); +- +- state->operational_mode = AU8522_ANALOG_MODE; +- +- /* Clear out any state associated with the digital side of the +- chip, so that when it gets powered back up it won't think +- that it is already tuned */ +- state->current_frequency = 0; +- +- au8522_writereg(state, 0xa4, 1 << 5); +- +- return 0; +-} +- +-static int au8522_s_video_routing(struct v4l2_subdev *sd, +- u32 input, u32 output, u32 config) +-{ +- struct au8522_state *state = to_state(sd); +- +- au8522_reset(sd, 0); +- +- /* Jam open the i2c gate to the tuner. We do this here to handle the +- case where the user went into digital mode (causing the gate to be +- closed), and then came back to analog mode */ +- au8522_writereg(state, 0x106, 1); +- +- if (input == AU8522_COMPOSITE_CH1) { +- au8522_setup_cvbs_mode(state); +- } else if (input == AU8522_SVIDEO_CH13) { +- au8522_setup_svideo_mode(state); +- } else if (input == AU8522_COMPOSITE_CH4_SIF) { +- au8522_setup_cvbs_tuner_mode(state); +- } else { +- printk(KERN_ERR "au8522 mode not currently supported\n"); +- return -EINVAL; +- } +- return 0; +-} +- +-static int au8522_s_audio_routing(struct v4l2_subdev *sd, +- u32 input, u32 output, u32 config) +-{ +- struct au8522_state *state = to_state(sd); +- set_audio_input(state, input); +- return 0; +-} +- +-static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +-{ +- int val = 0; +- struct au8522_state *state = to_state(sd); +- u8 lock_status; +- +- /* Interrogate the decoder to see if we are getting a real signal */ +- lock_status = au8522_readreg(state, 0x00); +- if (lock_status == 0xa2) +- vt->signal = 0xffff; +- else +- vt->signal = 0x00; +- +- vt->capability |= +- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | +- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; +- +- val = V4L2_TUNER_SUB_MONO; +- vt->rxsubchans = val; +- vt->audmode = V4L2_TUNER_MODE_STEREO; +- return 0; +-} +- +-static int au8522_g_chip_ident(struct v4l2_subdev *sd, +- struct v4l2_dbg_chip_ident *chip) +-{ +- struct au8522_state *state = to_state(sd); +- struct i2c_client *client = v4l2_get_subdevdata(sd); +- +- return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +-} +- +-static int au8522_log_status(struct v4l2_subdev *sd) +-{ +- /* FIXME: Add some status info here */ +- return 0; +-} +- +-/* ----------------------------------------------------------------------- */ +- +-static const struct v4l2_subdev_core_ops au8522_core_ops = { +- .log_status = au8522_log_status, +- .g_chip_ident = au8522_g_chip_ident, +- .g_ctrl = au8522_g_ctrl, +- .s_ctrl = au8522_s_ctrl, +- .queryctrl = au8522_queryctrl, +- .reset = au8522_reset, +-#ifdef CONFIG_VIDEO_ADV_DEBUG +- .g_register = au8522_g_register, +- .s_register = au8522_s_register, +-#endif +-}; +- +-static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = { +- .g_tuner = au8522_g_tuner, +-}; +- +-static const struct v4l2_subdev_audio_ops au8522_audio_ops = { +- .s_routing = au8522_s_audio_routing, +-}; +- +-static const struct v4l2_subdev_video_ops au8522_video_ops = { +- .s_routing = au8522_s_video_routing, +- .s_stream = au8522_s_stream, +-}; +- +-static const struct v4l2_subdev_ops au8522_ops = { +- .core = &au8522_core_ops, +- .tuner = &au8522_tuner_ops, +- .audio = &au8522_audio_ops, +- .video = &au8522_video_ops, +-}; +- +-/* ----------------------------------------------------------------------- */ +- +-static int au8522_probe(struct i2c_client *client, +- const struct i2c_device_id *did) +-{ +- struct au8522_state *state; +- struct v4l2_subdev *sd; +- int instance; +- struct au8522_config *demod_config; +- +- /* Check if the adapter supports the needed features */ +- if (!i2c_check_functionality(client->adapter, +- I2C_FUNC_SMBUS_BYTE_DATA)) { +- return -EIO; +- } +- +- /* allocate memory for the internal state */ +- instance = au8522_get_state(&state, client->adapter, client->addr); +- switch (instance) { +- case 0: +- printk(KERN_ERR "au8522_decoder allocation failed\n"); +- return -EIO; +- case 1: +- /* new demod instance */ +- printk(KERN_INFO "au8522_decoder creating new instance...\n"); +- break; +- default: +- /* existing demod instance */ +- printk(KERN_INFO "au8522_decoder attach existing instance.\n"); +- break; +- } +- +- demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL); +- if (demod_config == NULL) { +- if (instance == 1) +- kfree(state); +- return -ENOMEM; +- } +- demod_config->demod_address = 0x8e >> 1; +- +- state->config = demod_config; +- state->i2c = client->adapter; +- +- sd = &state->sd; +- v4l2_i2c_subdev_init(sd, client, &au8522_ops); +- +- state->c = client; +- state->vid_input = AU8522_COMPOSITE_CH1; +- state->aud_input = AU8522_AUDIO_NONE; +- state->id = 8522; +- state->rev = 0; +- +- /* Jam open the i2c gate to the tuner */ +- au8522_writereg(state, 0x106, 1); +- +- return 0; +-} +- +-static int au8522_remove(struct i2c_client *client) +-{ +- struct v4l2_subdev *sd = i2c_get_clientdata(client); +- v4l2_device_unregister_subdev(sd); +- au8522_release_state(to_state(sd)); +- return 0; +-} +- +-static const struct i2c_device_id au8522_id[] = { +- {"au8522", 0}, +- {} +-}; +- +-MODULE_DEVICE_TABLE(i2c, au8522_id); +- +-static struct i2c_driver au8522_driver = { +- .driver = { +- .owner = THIS_MODULE, +- .name = "au8522", +- }, +- .probe = au8522_probe, +- .remove = au8522_remove, +- .id_table = au8522_id, +-}; +- +-static __init int init_au8522(void) +-{ +- return i2c_add_driver(&au8522_driver); +-} +- +-static __exit void exit_au8522(void) +-{ +- i2c_del_driver(&au8522_driver); +-} +- +-module_init(init_au8522); +-module_exit(exit_au8522); +diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c +deleted file mode 100644 +index 25f6509..0000000 +--- a/drivers/media/dvb/frontends/au8522_dig.c ++++ /dev/null +@@ -1,1041 +0,0 @@ +-/* +- Auvitek AU8522 QAM/8VSB demodulator driver +- +- Copyright (C) 2008 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "au8522.h" +-#include "au8522_priv.h" +- +-static int debug; +- +-/* Despite the name "hybrid_tuner", the framework works just as well for +- hybrid demodulators as well... */ +-static LIST_HEAD(hybrid_tuner_instance_list); +-static DEFINE_MUTEX(au8522_list_mutex); +- +-#define dprintk(arg...)\ +- do { if (debug)\ +- printk(arg);\ +- } while (0) +- +-/* 16 bit registers, 8 bit values */ +-int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; +- +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = 0, .buf = buf, .len = 3 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-u8 au8522_readreg(struct au8522_state *state, u16 reg) +-{ +- int ret; +- u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; +- u8 b1[] = { 0 }; +- +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, +- .buf = b0, .len = 2 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, +- .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk(KERN_ERR "%s: readreg error (ret == %i)\n", +- __func__, ret); +- return b1[0]; +-} +- +-static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- if (state->operational_mode == AU8522_ANALOG_MODE) { +- /* We're being asked to manage the gate even though we're +- not in digital mode. This can occur if we get switched +- over to analog mode before the dvb_frontend kernel thread +- has completely shutdown */ +- return 0; +- } +- +- if (enable) +- return au8522_writereg(state, 0x106, 1); +- else +- return au8522_writereg(state, 0x106, 0); +-} +- +-struct mse2snr_tab { +- u16 val; +- u16 data; +-}; +- +-/* VSB SNR lookup table */ +-static struct mse2snr_tab vsb_mse2snr_tab[] = { +- { 0, 270 }, +- { 2, 250 }, +- { 3, 240 }, +- { 5, 230 }, +- { 7, 220 }, +- { 9, 210 }, +- { 12, 200 }, +- { 13, 195 }, +- { 15, 190 }, +- { 17, 185 }, +- { 19, 180 }, +- { 21, 175 }, +- { 24, 170 }, +- { 27, 165 }, +- { 31, 160 }, +- { 32, 158 }, +- { 33, 156 }, +- { 36, 152 }, +- { 37, 150 }, +- { 39, 148 }, +- { 40, 146 }, +- { 41, 144 }, +- { 43, 142 }, +- { 44, 140 }, +- { 48, 135 }, +- { 50, 130 }, +- { 43, 142 }, +- { 53, 125 }, +- { 56, 120 }, +- { 256, 115 }, +-}; +- +-/* QAM64 SNR lookup table */ +-static struct mse2snr_tab qam64_mse2snr_tab[] = { +- { 15, 0 }, +- { 16, 290 }, +- { 17, 288 }, +- { 18, 286 }, +- { 19, 284 }, +- { 20, 282 }, +- { 21, 281 }, +- { 22, 279 }, +- { 23, 277 }, +- { 24, 275 }, +- { 25, 273 }, +- { 26, 271 }, +- { 27, 269 }, +- { 28, 268 }, +- { 29, 266 }, +- { 30, 264 }, +- { 31, 262 }, +- { 32, 260 }, +- { 33, 259 }, +- { 34, 258 }, +- { 35, 256 }, +- { 36, 255 }, +- { 37, 254 }, +- { 38, 252 }, +- { 39, 251 }, +- { 40, 250 }, +- { 41, 249 }, +- { 42, 248 }, +- { 43, 246 }, +- { 44, 245 }, +- { 45, 244 }, +- { 46, 242 }, +- { 47, 241 }, +- { 48, 240 }, +- { 50, 239 }, +- { 51, 238 }, +- { 53, 237 }, +- { 54, 236 }, +- { 56, 235 }, +- { 57, 234 }, +- { 59, 233 }, +- { 60, 232 }, +- { 62, 231 }, +- { 63, 230 }, +- { 65, 229 }, +- { 67, 228 }, +- { 68, 227 }, +- { 70, 226 }, +- { 71, 225 }, +- { 73, 224 }, +- { 74, 223 }, +- { 76, 222 }, +- { 78, 221 }, +- { 80, 220 }, +- { 82, 219 }, +- { 85, 218 }, +- { 88, 217 }, +- { 90, 216 }, +- { 92, 215 }, +- { 93, 214 }, +- { 94, 212 }, +- { 95, 211 }, +- { 97, 210 }, +- { 99, 209 }, +- { 101, 208 }, +- { 102, 207 }, +- { 104, 206 }, +- { 107, 205 }, +- { 111, 204 }, +- { 114, 203 }, +- { 118, 202 }, +- { 122, 201 }, +- { 125, 200 }, +- { 128, 199 }, +- { 130, 198 }, +- { 132, 197 }, +- { 256, 190 }, +-}; +- +-/* QAM256 SNR lookup table */ +-static struct mse2snr_tab qam256_mse2snr_tab[] = { +- { 16, 0 }, +- { 17, 400 }, +- { 18, 398 }, +- { 19, 396 }, +- { 20, 394 }, +- { 21, 392 }, +- { 22, 390 }, +- { 23, 388 }, +- { 24, 386 }, +- { 25, 384 }, +- { 26, 382 }, +- { 27, 380 }, +- { 28, 379 }, +- { 29, 378 }, +- { 30, 377 }, +- { 31, 376 }, +- { 32, 375 }, +- { 33, 374 }, +- { 34, 373 }, +- { 35, 372 }, +- { 36, 371 }, +- { 37, 370 }, +- { 38, 362 }, +- { 39, 354 }, +- { 40, 346 }, +- { 41, 338 }, +- { 42, 330 }, +- { 43, 328 }, +- { 44, 326 }, +- { 45, 324 }, +- { 46, 322 }, +- { 47, 320 }, +- { 48, 319 }, +- { 49, 318 }, +- { 50, 317 }, +- { 51, 316 }, +- { 52, 315 }, +- { 53, 314 }, +- { 54, 313 }, +- { 55, 312 }, +- { 56, 311 }, +- { 57, 310 }, +- { 58, 308 }, +- { 59, 306 }, +- { 60, 304 }, +- { 61, 302 }, +- { 62, 300 }, +- { 63, 298 }, +- { 65, 295 }, +- { 68, 294 }, +- { 70, 293 }, +- { 73, 292 }, +- { 76, 291 }, +- { 78, 290 }, +- { 79, 289 }, +- { 81, 288 }, +- { 82, 287 }, +- { 83, 286 }, +- { 84, 285 }, +- { 85, 284 }, +- { 86, 283 }, +- { 88, 282 }, +- { 89, 281 }, +- { 256, 280 }, +-}; +- +-static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse, +- u16 *snr) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < sz; i++) { +- if (mse < tab[i].val) { +- *snr = tab[i].data; +- ret = 0; +- break; +- } +- } +- dprintk("%s() snr=%d\n", __func__, *snr); +- return ret; +-} +- +-static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- u8 r0b5, r0b6, r0b7; +- char *ifmhz; +- +- switch (if_freq) { +- case AU8522_IF_3_25MHZ: +- ifmhz = "3.25"; +- r0b5 = 0x00; +- r0b6 = 0x3d; +- r0b7 = 0xa0; +- break; +- case AU8522_IF_4MHZ: +- ifmhz = "4.00"; +- r0b5 = 0x00; +- r0b6 = 0x4b; +- r0b7 = 0xd9; +- break; +- case AU8522_IF_6MHZ: +- ifmhz = "6.00"; +- r0b5 = 0xfb; +- r0b6 = 0x8e; +- r0b7 = 0x39; +- break; +- default: +- dprintk("%s() IF Frequency not supported\n", __func__); +- return -EINVAL; +- } +- dprintk("%s() %s MHz\n", __func__, ifmhz); +- au8522_writereg(state, 0x80b5, r0b5); +- au8522_writereg(state, 0x80b6, r0b6); +- au8522_writereg(state, 0x80b7, r0b7); +- +- return 0; +-} +- +-/* VSB Modulation table */ +-static struct { +- u16 reg; +- u16 data; +-} VSB_mod_tab[] = { +- { 0x8090, 0x84 }, +- { 0x4092, 0x11 }, +- { 0x2005, 0x00 }, +- { 0x8091, 0x80 }, +- { 0x80a3, 0x0c }, +- { 0x80a4, 0xe8 }, +- { 0x8081, 0xc4 }, +- { 0x80a5, 0x40 }, +- { 0x80a7, 0x40 }, +- { 0x80a6, 0x67 }, +- { 0x8262, 0x20 }, +- { 0x821c, 0x30 }, +- { 0x80d8, 0x1a }, +- { 0x8227, 0xa0 }, +- { 0x8121, 0xff }, +- { 0x80a8, 0xf0 }, +- { 0x80a9, 0x05 }, +- { 0x80aa, 0x77 }, +- { 0x80ab, 0xf0 }, +- { 0x80ac, 0x05 }, +- { 0x80ad, 0x77 }, +- { 0x80ae, 0x41 }, +- { 0x80af, 0x66 }, +- { 0x821b, 0xcc }, +- { 0x821d, 0x80 }, +- { 0x80a4, 0xe8 }, +- { 0x8231, 0x13 }, +-}; +- +-/* QAM64 Modulation table */ +-static struct { +- u16 reg; +- u16 data; +-} QAM64_mod_tab[] = { +- { 0x00a3, 0x09 }, +- { 0x00a4, 0x00 }, +- { 0x0081, 0xc4 }, +- { 0x00a5, 0x40 }, +- { 0x00aa, 0x77 }, +- { 0x00ad, 0x77 }, +- { 0x00a6, 0x67 }, +- { 0x0262, 0x20 }, +- { 0x021c, 0x30 }, +- { 0x00b8, 0x3e }, +- { 0x00b9, 0xf0 }, +- { 0x00ba, 0x01 }, +- { 0x00bb, 0x18 }, +- { 0x00bc, 0x50 }, +- { 0x00bd, 0x00 }, +- { 0x00be, 0xea }, +- { 0x00bf, 0xef }, +- { 0x00c0, 0xfc }, +- { 0x00c1, 0xbd }, +- { 0x00c2, 0x1f }, +- { 0x00c3, 0xfc }, +- { 0x00c4, 0xdd }, +- { 0x00c5, 0xaf }, +- { 0x00c6, 0x00 }, +- { 0x00c7, 0x38 }, +- { 0x00c8, 0x30 }, +- { 0x00c9, 0x05 }, +- { 0x00ca, 0x4a }, +- { 0x00cb, 0xd0 }, +- { 0x00cc, 0x01 }, +- { 0x00cd, 0xd9 }, +- { 0x00ce, 0x6f }, +- { 0x00cf, 0xf9 }, +- { 0x00d0, 0x70 }, +- { 0x00d1, 0xdf }, +- { 0x00d2, 0xf7 }, +- { 0x00d3, 0xc2 }, +- { 0x00d4, 0xdf }, +- { 0x00d5, 0x02 }, +- { 0x00d6, 0x9a }, +- { 0x00d7, 0xd0 }, +- { 0x0250, 0x0d }, +- { 0x0251, 0xcd }, +- { 0x0252, 0xe0 }, +- { 0x0253, 0x05 }, +- { 0x0254, 0xa7 }, +- { 0x0255, 0xff }, +- { 0x0256, 0xed }, +- { 0x0257, 0x5b }, +- { 0x0258, 0xae }, +- { 0x0259, 0xe6 }, +- { 0x025a, 0x3d }, +- { 0x025b, 0x0f }, +- { 0x025c, 0x0d }, +- { 0x025d, 0xea }, +- { 0x025e, 0xf2 }, +- { 0x025f, 0x51 }, +- { 0x0260, 0xf5 }, +- { 0x0261, 0x06 }, +- { 0x021a, 0x00 }, +- { 0x0546, 0x40 }, +- { 0x0210, 0xc7 }, +- { 0x0211, 0xaa }, +- { 0x0212, 0xab }, +- { 0x0213, 0x02 }, +- { 0x0502, 0x00 }, +- { 0x0121, 0x04 }, +- { 0x0122, 0x04 }, +- { 0x052e, 0x10 }, +- { 0x00a4, 0xca }, +- { 0x00a7, 0x40 }, +- { 0x0526, 0x01 }, +-}; +- +-/* QAM256 Modulation table */ +-static struct { +- u16 reg; +- u16 data; +-} QAM256_mod_tab[] = { +- { 0x80a3, 0x09 }, +- { 0x80a4, 0x00 }, +- { 0x8081, 0xc4 }, +- { 0x80a5, 0x40 }, +- { 0x80aa, 0x77 }, +- { 0x80ad, 0x77 }, +- { 0x80a6, 0x67 }, +- { 0x8262, 0x20 }, +- { 0x821c, 0x30 }, +- { 0x80b8, 0x3e }, +- { 0x80b9, 0xf0 }, +- { 0x80ba, 0x01 }, +- { 0x80bb, 0x18 }, +- { 0x80bc, 0x50 }, +- { 0x80bd, 0x00 }, +- { 0x80be, 0xea }, +- { 0x80bf, 0xef }, +- { 0x80c0, 0xfc }, +- { 0x80c1, 0xbd }, +- { 0x80c2, 0x1f }, +- { 0x80c3, 0xfc }, +- { 0x80c4, 0xdd }, +- { 0x80c5, 0xaf }, +- { 0x80c6, 0x00 }, +- { 0x80c7, 0x38 }, +- { 0x80c8, 0x30 }, +- { 0x80c9, 0x05 }, +- { 0x80ca, 0x4a }, +- { 0x80cb, 0xd0 }, +- { 0x80cc, 0x01 }, +- { 0x80cd, 0xd9 }, +- { 0x80ce, 0x6f }, +- { 0x80cf, 0xf9 }, +- { 0x80d0, 0x70 }, +- { 0x80d1, 0xdf }, +- { 0x80d2, 0xf7 }, +- { 0x80d3, 0xc2 }, +- { 0x80d4, 0xdf }, +- { 0x80d5, 0x02 }, +- { 0x80d6, 0x9a }, +- { 0x80d7, 0xd0 }, +- { 0x8250, 0x0d }, +- { 0x8251, 0xcd }, +- { 0x8252, 0xe0 }, +- { 0x8253, 0x05 }, +- { 0x8254, 0xa7 }, +- { 0x8255, 0xff }, +- { 0x8256, 0xed }, +- { 0x8257, 0x5b }, +- { 0x8258, 0xae }, +- { 0x8259, 0xe6 }, +- { 0x825a, 0x3d }, +- { 0x825b, 0x0f }, +- { 0x825c, 0x0d }, +- { 0x825d, 0xea }, +- { 0x825e, 0xf2 }, +- { 0x825f, 0x51 }, +- { 0x8260, 0xf5 }, +- { 0x8261, 0x06 }, +- { 0x821a, 0x00 }, +- { 0x8546, 0x40 }, +- { 0x8210, 0x26 }, +- { 0x8211, 0xf6 }, +- { 0x8212, 0x84 }, +- { 0x8213, 0x02 }, +- { 0x8502, 0x01 }, +- { 0x8121, 0x04 }, +- { 0x8122, 0x04 }, +- { 0x852e, 0x10 }, +- { 0x80a4, 0xca }, +- { 0x80a7, 0x40 }, +- { 0x8526, 0x01 }, +-}; +- +-static int au8522_enable_modulation(struct dvb_frontend *fe, +- fe_modulation_t m) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- int i; +- +- dprintk("%s(0x%08x)\n", __func__, m); +- +- switch (m) { +- case VSB_8: +- dprintk("%s() VSB_8\n", __func__); +- for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++) +- au8522_writereg(state, +- VSB_mod_tab[i].reg, +- VSB_mod_tab[i].data); +- au8522_set_if(fe, state->config->vsb_if); +- break; +- case QAM_64: +- dprintk("%s() QAM 64\n", __func__); +- for (i = 0; i < ARRAY_SIZE(QAM64_mod_tab); i++) +- au8522_writereg(state, +- QAM64_mod_tab[i].reg, +- QAM64_mod_tab[i].data); +- au8522_set_if(fe, state->config->qam_if); +- break; +- case QAM_256: +- dprintk("%s() QAM 256\n", __func__); +- for (i = 0; i < ARRAY_SIZE(QAM256_mod_tab); i++) +- au8522_writereg(state, +- QAM256_mod_tab[i].reg, +- QAM256_mod_tab[i].data); +- au8522_set_if(fe, state->config->qam_if); +- break; +- default: +- dprintk("%s() Invalid modulation\n", __func__); +- return -EINVAL; +- } +- +- state->current_modulation = m; +- +- return 0; +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-static int au8522_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct au8522_state *state = fe->demodulator_priv; +- int ret = -EINVAL; +- +- dprintk("%s(frequency=%d)\n", __func__, c->frequency); +- +- if ((state->current_frequency == c->frequency) && +- (state->current_modulation == c->modulation)) +- return 0; +- +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- if (ret < 0) +- return ret; +- +- /* Allow the tuner to settle */ +- msleep(100); +- +- au8522_enable_modulation(fe, c->modulation); +- +- state->current_frequency = c->frequency; +- +- return 0; +-} +- +-/* Reset the demod hardware and reset all of the configuration registers +- to a default state. */ +-int au8522_init(struct dvb_frontend *fe) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- dprintk("%s()\n", __func__); +- +- state->operational_mode = AU8522_DIGITAL_MODE; +- +- /* Clear out any state associated with the digital side of the +- chip, so that when it gets powered back up it won't think +- that it is already tuned */ +- state->current_frequency = 0; +- +- au8522_writereg(state, 0xa4, 1 << 5); +- +- au8522_i2c_gate_ctrl(fe, 1); +- +- return 0; +-} +- +-static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) +-{ +- struct au8522_led_config *led_config = state->config->led_cfg; +- u8 val; +- +- /* bail out if we can't control an LED */ +- if (!led_config || !led_config->gpio_output || +- !led_config->gpio_output_enable || !led_config->gpio_output_disable) +- return 0; +- +- val = au8522_readreg(state, 0x4000 | +- (led_config->gpio_output & ~0xc000)); +- if (onoff) { +- /* enable GPIO output */ +- val &= ~((led_config->gpio_output_enable >> 8) & 0xff); +- val |= (led_config->gpio_output_enable & 0xff); +- } else { +- /* disable GPIO output */ +- val &= ~((led_config->gpio_output_disable >> 8) & 0xff); +- val |= (led_config->gpio_output_disable & 0xff); +- } +- return au8522_writereg(state, 0x8000 | +- (led_config->gpio_output & ~0xc000), val); +-} +- +-/* led = 0 | off +- * led = 1 | signal ok +- * led = 2 | signal strong +- * led < 0 | only light led if leds are currently off +- */ +-static int au8522_led_ctrl(struct au8522_state *state, int led) +-{ +- struct au8522_led_config *led_config = state->config->led_cfg; +- int i, ret = 0; +- +- /* bail out if we can't control an LED */ +- if (!led_config || !led_config->gpio_leds || +- !led_config->num_led_states || !led_config->led_states) +- return 0; +- +- if (led < 0) { +- /* if LED is already lit, then leave it as-is */ +- if (state->led_state) +- return 0; +- else +- led *= -1; +- } +- +- /* toggle LED if changing state */ +- if (state->led_state != led) { +- u8 val; +- +- dprintk("%s: %d\n", __func__, led); +- +- au8522_led_gpio_enable(state, 1); +- +- val = au8522_readreg(state, 0x4000 | +- (led_config->gpio_leds & ~0xc000)); +- +- /* start with all leds off */ +- for (i = 0; i < led_config->num_led_states; i++) +- val &= ~led_config->led_states[i]; +- +- /* set selected LED state */ +- if (led < led_config->num_led_states) +- val |= led_config->led_states[led]; +- else if (led_config->num_led_states) +- val |= +- led_config->led_states[led_config->num_led_states - 1]; +- +- ret = au8522_writereg(state, 0x8000 | +- (led_config->gpio_leds & ~0xc000), val); +- if (ret < 0) +- return ret; +- +- state->led_state = led; +- +- if (led == 0) +- au8522_led_gpio_enable(state, 0); +- } +- +- return 0; +-} +- +-int au8522_sleep(struct dvb_frontend *fe) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- dprintk("%s()\n", __func__); +- +- /* Only power down if the digital side is currently using the chip */ +- if (state->operational_mode == AU8522_ANALOG_MODE) { +- /* We're not in one of the expected power modes, which means +- that the DVB thread is probably telling us to go to sleep +- even though the analog frontend has already started using +- the chip. So ignore the request */ +- return 0; +- } +- +- /* turn off led */ +- au8522_led_ctrl(state, 0); +- +- /* Power down the chip */ +- au8522_writereg(state, 0xa4, 1 << 5); +- +- state->current_frequency = 0; +- +- return 0; +-} +- +-static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- u8 reg; +- u32 tuner_status = 0; +- +- *status = 0; +- +- if (state->current_modulation == VSB_8) { +- dprintk("%s() Checking VSB_8\n", __func__); +- reg = au8522_readreg(state, 0x4088); +- if ((reg & 0x03) == 0x03) +- *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI; +- } else { +- dprintk("%s() Checking QAM\n", __func__); +- reg = au8522_readreg(state, 0x4541); +- if (reg & 0x80) +- *status |= FE_HAS_VITERBI; +- if (reg & 0x20) +- *status |= FE_HAS_LOCK | FE_HAS_SYNC; +- } +- +- switch (state->config->status_mode) { +- case AU8522_DEMODLOCKING: +- dprintk("%s() DEMODLOCKING\n", __func__); +- if (*status & FE_HAS_VITERBI) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- case AU8522_TUNERLOCKING: +- /* Get the tuner status */ +- dprintk("%s() TUNERLOCKING\n", __func__); +- if (fe->ops.tuner_ops.get_status) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- fe->ops.tuner_ops.get_status(fe, &tuner_status); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (tuner_status) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- } +- state->fe_status = *status; +- +- if (*status & FE_HAS_LOCK) +- /* turn on LED, if it isn't on already */ +- au8522_led_ctrl(state, -1); +- else +- /* turn off LED */ +- au8522_led_ctrl(state, 0); +- +- dprintk("%s() status 0x%08x\n", __func__, *status); +- +- return 0; +-} +- +-static int au8522_led_status(struct au8522_state *state, const u16 *snr) +-{ +- struct au8522_led_config *led_config = state->config->led_cfg; +- int led; +- u16 strong; +- +- /* bail out if we can't control an LED */ +- if (!led_config) +- return 0; +- +- if (0 == (state->fe_status & FE_HAS_LOCK)) +- return au8522_led_ctrl(state, 0); +- else if (state->current_modulation == QAM_256) +- strong = led_config->qam256_strong; +- else if (state->current_modulation == QAM_64) +- strong = led_config->qam64_strong; +- else /* (state->current_modulation == VSB_8) */ +- strong = led_config->vsb8_strong; +- +- if (*snr >= strong) +- led = 2; +- else +- led = 1; +- +- if ((state->led_state) && +- (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10)) +- /* snr didn't change enough to bother +- * changing the color of the led */ +- return 0; +- +- return au8522_led_ctrl(state, led); +-} +- +-static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- int ret = -EINVAL; +- +- dprintk("%s()\n", __func__); +- +- if (state->current_modulation == QAM_256) +- ret = au8522_mse2snr_lookup(qam256_mse2snr_tab, +- ARRAY_SIZE(qam256_mse2snr_tab), +- au8522_readreg(state, 0x4522), +- snr); +- else if (state->current_modulation == QAM_64) +- ret = au8522_mse2snr_lookup(qam64_mse2snr_tab, +- ARRAY_SIZE(qam64_mse2snr_tab), +- au8522_readreg(state, 0x4522), +- snr); +- else /* VSB_8 */ +- ret = au8522_mse2snr_lookup(vsb_mse2snr_tab, +- ARRAY_SIZE(vsb_mse2snr_tab), +- au8522_readreg(state, 0x4311), +- snr); +- +- if (state->config->led_cfg) +- au8522_led_status(state, snr); +- +- return ret; +-} +- +-static int au8522_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- /* borrowed from lgdt330x.c +- * +- * Calculate strength from SNR up to 35dB +- * Even though the SNR can go higher than 35dB, +- * there is some comfort factor in having a range of +- * strong signals that can show at 100% +- */ +- u16 snr; +- u32 tmp; +- int ret = au8522_read_snr(fe, &snr); +- +- *signal_strength = 0; +- +- if (0 == ret) { +- /* The following calculation method was chosen +- * purely for the sake of code re-use from the +- * other demod drivers that use this method */ +- +- /* Convert from SNR in dB * 10 to 8.24 fixed-point */ +- tmp = (snr * ((1 << 24) / 10)); +- +- /* Convert from 8.24 fixed-point to +- * scale the range 0 - 35*2^24 into 0 - 65535*/ +- if (tmp >= 8960 * 0x10000) +- *signal_strength = 0xffff; +- else +- *signal_strength = tmp / 8960; +- } +- +- return ret; +-} +- +-static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- +- if (state->current_modulation == VSB_8) +- *ucblocks = au8522_readreg(state, 0x4087); +- else +- *ucblocks = au8522_readreg(state, 0x4543); +- +- return 0; +-} +- +-static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- return au8522_read_ucblocks(fe, ber); +-} +- +-static int au8522_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct au8522_state *state = fe->demodulator_priv; +- +- c->frequency = state->current_frequency; +- c->modulation = state->current_modulation; +- +- return 0; +-} +- +-static int au8522_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static struct dvb_frontend_ops au8522_ops; +- +-int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, +- u8 client_address) +-{ +- int ret; +- +- mutex_lock(&au8522_list_mutex); +- ret = hybrid_tuner_request_state(struct au8522_state, (*state), +- hybrid_tuner_instance_list, +- i2c, client_address, "au8522"); +- mutex_unlock(&au8522_list_mutex); +- +- return ret; +-} +- +-void au8522_release_state(struct au8522_state *state) +-{ +- mutex_lock(&au8522_list_mutex); +- if (state != NULL) +- hybrid_tuner_release_state(state); +- mutex_unlock(&au8522_list_mutex); +-} +- +- +-static void au8522_release(struct dvb_frontend *fe) +-{ +- struct au8522_state *state = fe->demodulator_priv; +- au8522_release_state(state); +-} +- +-struct dvb_frontend *au8522_attach(const struct au8522_config *config, +- struct i2c_adapter *i2c) +-{ +- struct au8522_state *state = NULL; +- int instance; +- +- /* allocate memory for the internal state */ +- instance = au8522_get_state(&state, i2c, config->demod_address); +- switch (instance) { +- case 0: +- dprintk("%s state allocation failed\n", __func__); +- break; +- case 1: +- /* new demod instance */ +- dprintk("%s using new instance\n", __func__); +- break; +- default: +- /* existing demod instance */ +- dprintk("%s using existing instance\n", __func__); +- break; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->operational_mode = AU8522_DIGITAL_MODE; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &au8522_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- if (au8522_init(&state->frontend) != 0) { +- printk(KERN_ERR "%s: Failed to initialize correctly\n", +- __func__); +- goto error; +- } +- +- /* Note: Leaving the I2C gate open here. */ +- au8522_i2c_gate_ctrl(&state->frontend, 1); +- +- return &state->frontend; +- +-error: +- au8522_release_state(state); +- return NULL; +-} +-EXPORT_SYMBOL(au8522_attach); +- +-static struct dvb_frontend_ops au8522_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Auvitek AU8522 QAM/8VSB Frontend", +- .frequency_min = 54000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- +- .init = au8522_init, +- .sleep = au8522_sleep, +- .i2c_gate_ctrl = au8522_i2c_gate_ctrl, +- .set_frontend = au8522_set_frontend, +- .get_frontend = au8522_get_frontend, +- .get_tune_settings = au8522_get_tune_settings, +- .read_status = au8522_read_status, +- .read_ber = au8522_read_ber, +- .read_signal_strength = au8522_read_signal_strength, +- .read_snr = au8522_read_snr, +- .read_ucblocks = au8522_read_ucblocks, +- .release = au8522_release, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h +deleted file mode 100644 +index 751e17d..0000000 +--- a/drivers/media/dvb/frontends/au8522_priv.h ++++ /dev/null +@@ -1,421 +0,0 @@ +-/* +- Auvitek AU8522 QAM/8VSB demodulator driver +- +- Copyright (C) 2008 Steven Toth +- Copyright (C) 2008 Devin Heitmueller +- Copyright (C) 2005-2008 Auvitek International, Ltd. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "au8522.h" +-#include "tuner-i2c.h" +- +-#define AU8522_ANALOG_MODE 0 +-#define AU8522_DIGITAL_MODE 1 +- +-struct au8522_state { +- struct i2c_client *c; +- struct i2c_adapter *i2c; +- +- u8 operational_mode; +- +- /* Used for sharing of the state between analog and digital mode */ +- struct tuner_i2c_props i2c_props; +- struct list_head hybrid_tuner_instance_list; +- +- /* configuration settings */ +- const struct au8522_config *config; +- +- struct dvb_frontend frontend; +- +- u32 current_frequency; +- fe_modulation_t current_modulation; +- +- u32 fe_status; +- unsigned int led_state; +- +- /* Analog settings */ +- struct v4l2_subdev sd; +- v4l2_std_id std; +- int vid_input; +- int aud_input; +- u32 id; +- u32 rev; +- u8 brightness; +- u8 contrast; +- u8 saturation; +- s16 hue; +-}; +- +-/* These are routines shared by both the VSB/QAM demodulator and the analog +- decoder */ +-int au8522_writereg(struct au8522_state *state, u16 reg, u8 data); +-u8 au8522_readreg(struct au8522_state *state, u16 reg); +-int au8522_init(struct dvb_frontend *fe); +-int au8522_sleep(struct dvb_frontend *fe); +- +-int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, +- u8 client_address); +-void au8522_release_state(struct au8522_state *state); +- +-/* REGISTERS */ +-#define AU8522_INPUT_CONTROL_REG081H 0x081 +-#define AU8522_PGA_CONTROL_REG082H 0x082 +-#define AU8522_CLAMPING_CONTROL_REG083H 0x083 +- +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3 +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5 +-#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6 +-#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7 +-#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8 +-#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9 +-#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA +-#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB +-#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC +-#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD +-#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE +-#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF +- +-/* Receiver registers */ +-#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0 +-#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1 +-#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2 +-#define AU8522_TOREGAGC1_REG0B3H 0x0B3 +-#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4 +-#define AU8522_FRMREGBBH_REG0B5H 0x0B5 +-#define AU8522_FRMREGBBM_REG0B6H 0x0B6 +-#define AU8522_FRMREGBBL_REG0B7H 0x0B7 +-/* 0xB8 TO 0xD7 are the filter coefficients */ +-#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8 +-#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9 +-#define AU8522_TOREGAGC2_REG0DAH 0x0DA +-#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB +-#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC +-#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD +-#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE +-#define AU8522_TOREGFREQ_REG0DFH 0x0DF +- +-#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB +-#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC +-#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED +- +-#define AU8522_CHIP_MODE_REG0FEH 0x0FE +- +-/* I2C bus control registers */ +-#define AU8522_I2C_CONTROL_REG0_REG090H 0x090 +-#define AU8522_I2C_CONTROL_REG1_REG091H 0x091 +-#define AU8522_I2C_STATUS_REG092H 0x092 +-#define AU8522_I2C_WR_DATA0_REG093H 0x093 +-#define AU8522_I2C_WR_DATA1_REG094H 0x094 +-#define AU8522_I2C_WR_DATA2_REG095H 0x095 +-#define AU8522_I2C_WR_DATA3_REG096H 0x096 +-#define AU8522_I2C_WR_DATA4_REG097H 0x097 +-#define AU8522_I2C_WR_DATA5_REG098H 0x098 +-#define AU8522_I2C_WR_DATA6_REG099H 0x099 +-#define AU8522_I2C_WR_DATA7_REG09AH 0x09A +-#define AU8522_I2C_RD_DATA0_REG09BH 0x09B +-#define AU8522_I2C_RD_DATA1_REG09CH 0x09C +-#define AU8522_I2C_RD_DATA2_REG09DH 0x09D +-#define AU8522_I2C_RD_DATA3_REG09EH 0x09E +-#define AU8522_I2C_RD_DATA4_REG09FH 0x09F +-#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0 +-#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1 +-#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2 +- +-#define AU8522_ENA_USB_REG101H 0x101 +- +-#define AU8522_I2S_CTRL_0_REG110H 0x110 +-#define AU8522_I2S_CTRL_1_REG111H 0x111 +-#define AU8522_I2S_CTRL_2_REG112H 0x112 +- +-#define AU8522_FRMREGFFECONTROL_REG121H 0x121 +-#define AU8522_FRMREGDFECONTROL_REG122H 0x122 +- +-#define AU8522_CARRFREQOFFSET0_REG201H 0x201 +-#define AU8522_CARRFREQOFFSET1_REG202H 0x202 +- +-#define AU8522_DECIMATION_GAIN_REG21AH 0x21A +-#define AU8522_FRMREGIFSLP_REG21BH 0x21B +-#define AU8522_FRMREGTHRDL2_REG21CH 0x21C +-#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D +-#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E +-#define AU8522_FRMREGPLLMODE_REG21FH 0x21F +-#define AU8522_FRMREGCSTHRD_REG220H 0x220 +-#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221 +-#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222 +-#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223 +-#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224 +-#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225 +-#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226 +-#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227 +-#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228 +- +-/* Analog TV Decoder */ +-#define AU8522_TVDEC_STATUS_REG000H 0x000 +-#define AU8522_TVDEC_INT_STATUS_REG001H 0x001 +-#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002 +-#define AU8522_TVDEC_SHARPNESSREG009H 0x009 +-#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A +-#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B +-#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C +-#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D +-#define AU8522_TVDEC_HUE_H_REG00EH 0x00E +-#define AU8522_TVDEC_HUE_L_REG00FH 0x00F +-#define AU8522_TVDEC_INT_MASK_REG010H 0x010 +-#define AU8522_VIDEO_MODE_REG011H 0x011 +-#define AU8522_TVDEC_PGA_REG012H 0x012 +-#define AU8522_TVDEC_COMB_MODE_REG015H 0x015 +-#define AU8522_REG016H 0x016 +-#define AU8522_TVDED_DBG_MODE_REG060H 0x060 +-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061 +-#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062 +-#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063 +-#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064 +-#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065 +-#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066 +-#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067 +-#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068 +-#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069 +-#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A +-#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B +-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C +-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D +-#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E +-#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F +-#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070 +-#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073 +-#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077 +-#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078 +-#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079 +-#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A +-#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B +-#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E +-#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F +- +-#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4 +-#define AU8522_TOREGAAGC_REG0E5H 0x0E5 +- +-#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401 +-#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402 +-#define AU8522_FILTER_COEF_R410 0x410 +-#define AU8522_FILTER_COEF_R411 0x411 +-#define AU8522_FILTER_COEF_R412 0x412 +-#define AU8522_FILTER_COEF_R413 0x413 +-#define AU8522_FILTER_COEF_R414 0x414 +-#define AU8522_FILTER_COEF_R415 0x415 +-#define AU8522_FILTER_COEF_R416 0x416 +-#define AU8522_FILTER_COEF_R417 0x417 +-#define AU8522_FILTER_COEF_R418 0x418 +-#define AU8522_FILTER_COEF_R419 0x419 +-#define AU8522_FILTER_COEF_R41A 0x41A +-#define AU8522_FILTER_COEF_R41B 0x41B +-#define AU8522_FILTER_COEF_R41C 0x41C +-#define AU8522_FILTER_COEF_R41D 0x41D +-#define AU8522_FILTER_COEF_R41E 0x41E +-#define AU8522_FILTER_COEF_R41F 0x41F +-#define AU8522_FILTER_COEF_R420 0x420 +-#define AU8522_FILTER_COEF_R421 0x421 +-#define AU8522_FILTER_COEF_R422 0x422 +-#define AU8522_FILTER_COEF_R423 0x423 +-#define AU8522_FILTER_COEF_R424 0x424 +-#define AU8522_FILTER_COEF_R425 0x425 +-#define AU8522_FILTER_COEF_R426 0x426 +-#define AU8522_FILTER_COEF_R427 0x427 +-#define AU8522_FILTER_COEF_R428 0x428 +-#define AU8522_FILTER_COEF_R429 0x429 +-#define AU8522_FILTER_COEF_R42A 0x42A +-#define AU8522_FILTER_COEF_R42B 0x42B +-#define AU8522_FILTER_COEF_R42C 0x42C +-#define AU8522_FILTER_COEF_R42D 0x42D +- +-/* VBI Control Registers */ +-#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004 +-#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005 +-#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006 +-#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007 +-#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017 +-#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018 +-#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019 +-#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A +-#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B +-#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C +-#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E +-#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F +-#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020 +-#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021 +-#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022 +-#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023 +- +-#define AU8522_REG071H 0x071 +-#define AU8522_REG072H 0x072 +-#define AU8522_REG074H 0x074 +-#define AU8522_REG075H 0x075 +- +-/* Digital Demodulator Registers */ +-#define AU8522_FRAME_COUNT0_REG084H 0x084 +-#define AU8522_RS_STATUS_G0_REG085H 0x085 +-#define AU8522_RS_STATUS_B0_REG086H 0x086 +-#define AU8522_RS_STATUS_E_REG087H 0x087 +-#define AU8522_DEMODULATION_STATUS_REG088H 0x088 +-#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6 +-#define AU8522_TSPORT_CONTROL_REG10BH 0x10B +-#define AU8522_TSTHES_REG10CH 0x10C +-#define AU8522_FRMREGDFEKEEP_REG301H 0x301 +-#define AU8522_DFE_AVERAGE_REG302H 0x302 +-#define AU8522_FRMREGEQLERRWIN_REG303H 0x303 +-#define AU8522_FRMREGFFEKEEP_REG304H 0x304 +-#define AU8522_FRMREGDFECONTROL1_REG305H 0x305 +-#define AU8522_FRMREGEQLERRLOW_REG306H 0x306 +- +-#define AU8522_REG42EH 0x42E +-#define AU8522_REG42FH 0x42F +-#define AU8522_REG430H 0x430 +-#define AU8522_REG431H 0x431 +-#define AU8522_REG432H 0x432 +-#define AU8522_REG433H 0x433 +-#define AU8522_REG434H 0x434 +-#define AU8522_REG435H 0x435 +-#define AU8522_REG436H 0x436 +- +-/* GPIO Registers */ +-#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0 +-#define AU8522_GPIO_STATUS_REG0E1H 0x0E1 +-#define AU8522_GPIO_DATA_REG0E2H 0x0E2 +- +-/* Audio Control Registers */ +-#define AU8522_AUDIOAGC_REG0EEH 0x0EE +-#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0 +-#define AU8522_AUDIO_MODE_REG0F1H 0x0F1 +-#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2 +-#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3 +-#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4 +-#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7 +-#define AU8522_REG0F9H 0x0F9 +- +-#define AU8522_AUDIOAGC2_REG605H 0x605 +-#define AU8522_AUDIOFREQ_REG606H 0x606 +- +- +-/**************************************************************/ +- +-#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4 +-#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4 +-#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4 +-#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4 +-#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68 +-#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28 +-/* CH1 AS Y,CH3 AS C */ +-#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23 +-/* CH2 AS Y,CH4 AS C */ +-#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20 +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09 +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09 +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12 +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A +-#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02 +- +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00 +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8 +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD +-#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD +- +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01 +-#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01 +- +-/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */ +-#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79 +-#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80 +-#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80 +-#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00 +-#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00 +-#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F +-#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00 +-#define AU8522_REG016H_CVBS 0x00 +-#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00 +-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS 0x0B +-#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13 0x03 +-#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13 0x00 +-#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19 +-#define AU8522_REG0F9H_AUDIO 0x20 +-#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7 +-#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A +-#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32 +-#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19 +-#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23 +-#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41 +-#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A +-#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32 +-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34 +-#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_SVIDEO 0x2a +-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05 +-#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_SVIDEO 0x15 +-#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E +-#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F +-#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80 +-#define AU8522_REG071H_CVBS 0x18 +-#define AU8522_REG072H_CVBS 0x30 +-#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0 +-#define AU8522_REG074H_CVBS 0x80 +-#define AU8522_REG075H_CVBS 0xF0 +-#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB +-#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04 +-#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00 +-#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00 +-#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE +-#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE +-#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00 +-#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40 +- +-/* Enables Closed captioning */ +-#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21 +diff --git a/drivers/media/dvb/frontends/bcm3510.c b/drivers/media/dvb/frontends/bcm3510.c +deleted file mode 100644 +index 033cd7a..0000000 +--- a/drivers/media/dvb/frontends/bcm3510.c ++++ /dev/null +@@ -1,856 +0,0 @@ +-/* +- * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) +- * +- * Copyright (C) 2001-5, B2C2 inc. +- * +- * GPL/Linux driver written by Patrick Boettcher +- * +- * This driver is "hard-coded" to be used with the 1st generation of +- * Technisat/B2C2's Air2PC ATSC PCI/USB cards/boxes. The pll-programming +- * (Panasonic CT10S) is located here, which is actually wrong. Unless there is +- * another device with a BCM3510, this is no problem. +- * +- * The driver works also with QAM64 DVB-C, but had an unreasonable high +- * UNC. (Tested with the Air2PC ATSC 1st generation) +- * +- * You'll need a firmware for this driver in order to get it running. It is +- * called "dvb-fe-bcm3510-01.fw". +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation; either version 2 of the License, or (at your option) +- * any later version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +- * more details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program; if not, write to the Free Software Foundation, Inc., 675 Mass +- * Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "bcm3510.h" +-#include "bcm3510_priv.h" +- +-struct bcm3510_state { +- +- struct i2c_adapter* i2c; +- const struct bcm3510_config* config; +- struct dvb_frontend frontend; +- +- /* demodulator private data */ +- struct mutex hab_mutex; +- u8 firmware_loaded:1; +- +- unsigned long next_status_check; +- unsigned long status_check_interval; +- struct bcm3510_hab_cmd_status1 status1; +- struct bcm3510_hab_cmd_status2 status2; +-}; +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c (|-able))."); +- +-#define dprintk(level,x...) if (level & debug) printk(x) +-#define dbufout(b,l,m) {\ +- int i; \ +- for (i = 0; i < l; i++) \ +- m("%02x ",b[i]); \ +-} +-#define deb_info(args...) dprintk(0x01,args) +-#define deb_i2c(args...) dprintk(0x02,args) +-#define deb_hab(args...) dprintk(0x04,args) +- +-/* transfer functions */ +-static int bcm3510_writebytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +-{ +- u8 b[256]; +- int err; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = len + 1 }; +- +- b[0] = reg; +- memcpy(&b[1],buf,len); +- +- deb_i2c("i2c wr %02x: ",reg); +- dbufout(buf,len,deb_i2c); +- deb_i2c("\n"); +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- +- deb_info("%s: i2c write error (addr %02x, reg %02x, err == %i)\n", +- __func__, state->config->demod_address, reg, err); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int bcm3510_readbytes (struct bcm3510_state *state, u8 reg, u8 *buf, u8 len) +-{ +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } +- }; +- int err; +- +- memset(buf,0,len); +- +- if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { +- deb_info("%s: i2c read error (addr %02x, reg %02x, err == %i)\n", +- __func__, state->config->demod_address, reg, err); +- return -EREMOTEIO; +- } +- deb_i2c("i2c rd %02x: ",reg); +- dbufout(buf,len,deb_i2c); +- deb_i2c("\n"); +- +- return 0; +-} +- +-static int bcm3510_writeB(struct bcm3510_state *state, u8 reg, bcm3510_register_value v) +-{ +- return bcm3510_writebytes(state,reg,&v.raw,1); +-} +- +-static int bcm3510_readB(struct bcm3510_state *state, u8 reg, bcm3510_register_value *v) +-{ +- return bcm3510_readbytes(state,reg,&v->raw,1); +-} +- +-/* Host Access Buffer transfers */ +-static int bcm3510_hab_get_response(struct bcm3510_state *st, u8 *buf, int len) +-{ +- bcm3510_register_value v; +- int ret,i; +- +- v.HABADR_a6.HABADR = 0; +- if ((ret = bcm3510_writeB(st,0xa6,v)) < 0) +- return ret; +- +- for (i = 0; i < len; i++) { +- if ((ret = bcm3510_readB(st,0xa7,&v)) < 0) +- return ret; +- buf[i] = v.HABDATA_a7; +- } +- return 0; +-} +- +-static int bcm3510_hab_send_request(struct bcm3510_state *st, u8 *buf, int len) +-{ +- bcm3510_register_value v,hab; +- int ret,i; +- unsigned long t; +- +-/* Check if any previous HAB request still needs to be serviced by the +- * Acquisition Processor before sending new request */ +- if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) +- return ret; +- if (v.HABSTAT_a8.HABR) { +- deb_info("HAB is running already - clearing it.\n"); +- v.HABSTAT_a8.HABR = 0; +- bcm3510_writeB(st,0xa8,v); +-// return -EBUSY; +- } +- +-/* Send the start HAB Address (automatically incremented after write of +- * HABDATA) and write the HAB Data */ +- hab.HABADR_a6.HABADR = 0; +- if ((ret = bcm3510_writeB(st,0xa6,hab)) < 0) +- return ret; +- +- for (i = 0; i < len; i++) { +- hab.HABDATA_a7 = buf[i]; +- if ((ret = bcm3510_writeB(st,0xa7,hab)) < 0) +- return ret; +- } +- +-/* Set the HABR bit to indicate AP request in progress (LBHABR allows HABR to +- * be written) */ +- v.raw = 0; v.HABSTAT_a8.HABR = 1; v.HABSTAT_a8.LDHABR = 1; +- if ((ret = bcm3510_writeB(st,0xa8,v)) < 0) +- return ret; +- +-/* Polling method: Wait until the AP finishes processing the HAB request */ +- t = jiffies + 1*HZ; +- while (time_before(jiffies, t)) { +- deb_info("waiting for HAB to complete\n"); +- msleep(10); +- if ((ret = bcm3510_readB(st,0xa8,&v)) < 0) +- return ret; +- +- if (!v.HABSTAT_a8.HABR) +- return 0; +- } +- +- deb_info("send_request execution timed out.\n"); +- return -ETIMEDOUT; +-} +- +-static int bcm3510_do_hab_cmd(struct bcm3510_state *st, u8 cmd, u8 msgid, u8 *obuf, u8 olen, u8 *ibuf, u8 ilen) +-{ +- u8 ob[olen+2],ib[ilen+2]; +- int ret = 0; +- +- ob[0] = cmd; +- ob[1] = msgid; +- memcpy(&ob[2],obuf,olen); +- +- deb_hab("hab snd: "); +- dbufout(ob,olen+2,deb_hab); +- deb_hab("\n"); +- +- if (mutex_lock_interruptible(&st->hab_mutex) < 0) +- return -EAGAIN; +- +- if ((ret = bcm3510_hab_send_request(st, ob, olen+2)) < 0 || +- (ret = bcm3510_hab_get_response(st, ib, ilen+2)) < 0) +- goto error; +- +- deb_hab("hab get: "); +- dbufout(ib,ilen+2,deb_hab); +- deb_hab("\n"); +- +- memcpy(ibuf,&ib[2],ilen); +-error: +- mutex_unlock(&st->hab_mutex); +- return ret; +-} +- +-#if 0 +-/* not needed, we use a semaphore to prevent HAB races */ +-static int bcm3510_is_ap_ready(struct bcm3510_state *st) +-{ +- bcm3510_register_value ap,hab; +- int ret; +- +- if ((ret = bcm3510_readB(st,0xa8,&hab)) < 0 || +- (ret = bcm3510_readB(st,0xa2,&ap) < 0)) +- return ret; +- +- if (ap.APSTAT1_a2.RESET || ap.APSTAT1_a2.IDLE || ap.APSTAT1_a2.STOP || hab.HABSTAT_a8.HABR) { +- deb_info("AP is busy\n"); +- return -EBUSY; +- } +- +- return 0; +-} +-#endif +- +-static int bcm3510_bert_reset(struct bcm3510_state *st) +-{ +- bcm3510_register_value b; +- int ret; +- +- if ((ret = bcm3510_readB(st,0xfa,&b)) < 0) +- return ret; +- +- b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); +- b.BERCTL_fa.RESYNC = 1; bcm3510_writeB(st,0xfa,b); +- b.BERCTL_fa.RESYNC = 0; bcm3510_writeB(st,0xfa,b); +- b.BERCTL_fa.CNTCTL = 1; b.BERCTL_fa.BITCNT = 1; bcm3510_writeB(st,0xfa,b); +- +- /* clear residual bit counter TODO */ +- return 0; +-} +- +-static int bcm3510_refresh_state(struct bcm3510_state *st) +-{ +- if (time_after(jiffies,st->next_status_check)) { +- bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS1, NULL,0, (u8 *)&st->status1, sizeof(st->status1)); +- bcm3510_do_hab_cmd(st, CMD_STATUS, MSGID_STATUS2, NULL,0, (u8 *)&st->status2, sizeof(st->status2)); +- st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000; +- } +- return 0; +-} +- +-static int bcm3510_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- bcm3510_refresh_state(st); +- +- *status = 0; +- if (st->status1.STATUS1.RECEIVER_LOCK) +- *status |= FE_HAS_LOCK | FE_HAS_SYNC; +- +- if (st->status1.STATUS1.FEC_LOCK) +- *status |= FE_HAS_VITERBI; +- +- if (st->status1.STATUS1.OUT_PLL_LOCK) +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; +- +- if (*status & FE_HAS_LOCK) +- st->status_check_interval = 1500; +- else /* more frequently checks if no lock has been achieved yet */ +- st->status_check_interval = 500; +- +- deb_info("real_status: %02x\n",*status); +- return 0; +-} +- +-static int bcm3510_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- bcm3510_refresh_state(st); +- +- *ber = (st->status2.LDBER0 << 16) | (st->status2.LDBER1 << 8) | st->status2.LDBER2; +- return 0; +-} +- +-static int bcm3510_read_unc(struct dvb_frontend* fe, u32* unc) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- bcm3510_refresh_state(st); +- *unc = (st->status2.LDUERC0 << 8) | st->status2.LDUERC1; +- return 0; +-} +- +-static int bcm3510_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- s32 t; +- +- bcm3510_refresh_state(st); +- t = st->status2.SIGNAL; +- +- if (t > 190) +- t = 190; +- if (t < 90) +- t = 90; +- +- t -= 90; +- t = t * 0xff / 100; +- /* normalize if necessary */ +- *strength = (t << 8) | t; +- return 0; +-} +- +-static int bcm3510_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- bcm3510_refresh_state(st); +- +- *snr = st->status1.SNR_EST0*1000 + ((st->status1.SNR_EST1*1000) >> 8); +- return 0; +-} +- +-/* tuner frontend programming */ +-static int bcm3510_tuner_cmd(struct bcm3510_state* st,u8 bc, u16 n, u8 a) +-{ +- struct bcm3510_hab_cmd_tune c; +- memset(&c,0,sizeof(struct bcm3510_hab_cmd_tune)); +- +-/* I2C Mode disabled, set 16 control / Data pairs */ +- c.length = 0x10; +- c.clock_width = 0; +-/* CS1, CS0, DATA, CLK bits control the tuner RF_AGC_SEL pin is set to +- * logic high (as Configuration) */ +- c.misc = 0x10; +-/* Set duration of the initial state of TUNCTL = 3.34 micro Sec */ +- c.TUNCTL_state = 0x40; +- +-/* PRESCALER DIVIDE RATIO | BC1_2_3_4; (band switch), 1stosc REFERENCE COUNTER REF_S12 and REF_S11 */ +- c.ctl_dat[0].ctrl.size = BITS_8; +- c.ctl_dat[0].data = 0x80 | bc; +- +-/* Control DATA pin, 1stosc REFERENCE COUNTER REF_S10 to REF_S3 */ +- c.ctl_dat[1].ctrl.size = BITS_8; +- c.ctl_dat[1].data = 4; +- +-/* set CONTROL BIT 1 to 1, 1stosc REFERENCE COUNTER REF_S2 to REF_S1 */ +- c.ctl_dat[2].ctrl.size = BITS_3; +- c.ctl_dat[2].data = 0x20; +- +-/* control CS0 pin, pulse byte ? */ +- c.ctl_dat[3].ctrl.size = BITS_3; +- c.ctl_dat[3].ctrl.clk_off = 1; +- c.ctl_dat[3].ctrl.cs0 = 1; +- c.ctl_dat[3].data = 0x40; +- +-/* PGM_S18 to PGM_S11 */ +- c.ctl_dat[4].ctrl.size = BITS_8; +- c.ctl_dat[4].data = n >> 3; +- +-/* PGM_S10 to PGM_S8, SWL_S7 to SWL_S3 */ +- c.ctl_dat[5].ctrl.size = BITS_8; +- c.ctl_dat[5].data = ((n & 0x7) << 5) | (a >> 2); +- +-/* SWL_S2 and SWL_S1, set CONTROL BIT 2 to 0 */ +- c.ctl_dat[6].ctrl.size = BITS_3; +- c.ctl_dat[6].data = (a << 6) & 0xdf; +- +-/* control CS0 pin, pulse byte ? */ +- c.ctl_dat[7].ctrl.size = BITS_3; +- c.ctl_dat[7].ctrl.clk_off = 1; +- c.ctl_dat[7].ctrl.cs0 = 1; +- c.ctl_dat[7].data = 0x40; +- +-/* PRESCALER DIVIDE RATIO, 2ndosc REFERENCE COUNTER REF_S12 and REF_S11 */ +- c.ctl_dat[8].ctrl.size = BITS_8; +- c.ctl_dat[8].data = 0x80; +- +-/* 2ndosc REFERENCE COUNTER REF_S10 to REF_S3 */ +- c.ctl_dat[9].ctrl.size = BITS_8; +- c.ctl_dat[9].data = 0x10; +- +-/* set CONTROL BIT 1 to 1, 2ndosc REFERENCE COUNTER REF_S2 to REF_S1 */ +- c.ctl_dat[10].ctrl.size = BITS_3; +- c.ctl_dat[10].data = 0x20; +- +-/* pulse byte */ +- c.ctl_dat[11].ctrl.size = BITS_3; +- c.ctl_dat[11].ctrl.clk_off = 1; +- c.ctl_dat[11].ctrl.cs1 = 1; +- c.ctl_dat[11].data = 0x40; +- +-/* PGM_S18 to PGM_S11 */ +- c.ctl_dat[12].ctrl.size = BITS_8; +- c.ctl_dat[12].data = 0x2a; +- +-/* PGM_S10 to PGM_S8 and SWL_S7 to SWL_S3 */ +- c.ctl_dat[13].ctrl.size = BITS_8; +- c.ctl_dat[13].data = 0x8e; +- +-/* SWL_S2 and SWL_S1 and set CONTROL BIT 2 to 0 */ +- c.ctl_dat[14].ctrl.size = BITS_3; +- c.ctl_dat[14].data = 0; +- +-/* Pulse Byte */ +- c.ctl_dat[15].ctrl.size = BITS_3; +- c.ctl_dat[15].ctrl.clk_off = 1; +- c.ctl_dat[15].ctrl.cs1 = 1; +- c.ctl_dat[15].data = 0x40; +- +- return bcm3510_do_hab_cmd(st,CMD_TUNE, MSGID_TUNE,(u8 *) &c,sizeof(c), NULL, 0); +-} +- +-static int bcm3510_set_freq(struct bcm3510_state* st,u32 freq) +-{ +- u8 bc,a; +- u16 n; +- s32 YIntercept,Tfvco1; +- +- freq /= 1000; +- +- deb_info("%dkHz:",freq); +- /* set Band Switch */ +- if (freq <= 168000) +- bc = 0x1c; +- else if (freq <= 378000) +- bc = 0x2c; +- else +- bc = 0x30; +- +- if (freq >= 470000) { +- freq -= 470001; +- YIntercept = 18805; +- } else if (freq >= 90000) { +- freq -= 90001; +- YIntercept = 15005; +- } else if (freq >= 76000){ +- freq -= 76001; +- YIntercept = 14865; +- } else { +- freq -= 54001; +- YIntercept = 14645; +- } +- +- Tfvco1 = (((freq/6000)*60 + YIntercept)*4)/10; +- +- n = Tfvco1 >> 6; +- a = Tfvco1 & 0x3f; +- +- deb_info(" BC1_2_3_4: %x, N: %x A: %x\n", bc, n, a); +- if (n >= 16 && n <= 2047) +- return bcm3510_tuner_cmd(st,bc,n,a); +- +- return -EINVAL; +-} +- +-static int bcm3510_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct bcm3510_state* st = fe->demodulator_priv; +- struct bcm3510_hab_cmd_ext_acquire cmd; +- struct bcm3510_hab_cmd_bert_control bert; +- int ret; +- +- memset(&cmd,0,sizeof(cmd)); +- switch (c->modulation) { +- case QAM_256: +- cmd.ACQUIRE0.MODE = 0x1; +- cmd.ACQUIRE1.SYM_RATE = 0x1; +- cmd.ACQUIRE1.IF_FREQ = 0x1; +- break; +- case QAM_64: +- cmd.ACQUIRE0.MODE = 0x2; +- cmd.ACQUIRE1.SYM_RATE = 0x2; +- cmd.ACQUIRE1.IF_FREQ = 0x1; +- break; +-#if 0 +- case QAM_256: +- cmd.ACQUIRE0.MODE = 0x3; +- break; +- case QAM_128: +- cmd.ACQUIRE0.MODE = 0x4; +- break; +- case QAM_64: +- cmd.ACQUIRE0.MODE = 0x5; +- break; +- case QAM_32: +- cmd.ACQUIRE0.MODE = 0x6; +- break; +- case QAM_16: +- cmd.ACQUIRE0.MODE = 0x7; +- break; +-#endif +- case VSB_8: +- cmd.ACQUIRE0.MODE = 0x8; +- cmd.ACQUIRE1.SYM_RATE = 0x0; +- cmd.ACQUIRE1.IF_FREQ = 0x0; +- break; +- case VSB_16: +- cmd.ACQUIRE0.MODE = 0x9; +- cmd.ACQUIRE1.SYM_RATE = 0x0; +- cmd.ACQUIRE1.IF_FREQ = 0x0; +- default: +- return -EINVAL; +- }; +- cmd.ACQUIRE0.OFFSET = 0; +- cmd.ACQUIRE0.NTSCSWEEP = 1; +- cmd.ACQUIRE0.FA = 1; +- cmd.ACQUIRE0.BW = 0; +- +-/* if (enableOffset) { +- cmd.IF_OFFSET0 = xx; +- cmd.IF_OFFSET1 = xx; +- +- cmd.SYM_OFFSET0 = xx; +- cmd.SYM_OFFSET1 = xx; +- if (enableNtscSweep) { +- cmd.NTSC_OFFSET0; +- cmd.NTSC_OFFSET1; +- } +- } */ +- bcm3510_do_hab_cmd(st, CMD_ACQUIRE, MSGID_EXT_TUNER_ACQUIRE, (u8 *) &cmd, sizeof(cmd), NULL, 0); +- +-/* doing it with different MSGIDs, data book and source differs */ +- bert.BE = 0; +- bert.unused = 0; +- bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_CONTROL, (u8 *) &bert, sizeof(bert), NULL, 0); +- bcm3510_do_hab_cmd(st, CMD_STATE_CONTROL, MSGID_BERT_SET, (u8 *) &bert, sizeof(bert), NULL, 0); +- +- bcm3510_bert_reset(st); +- +- ret = bcm3510_set_freq(st, c->frequency); +- if (ret < 0) +- return ret; +- +- memset(&st->status1,0,sizeof(st->status1)); +- memset(&st->status2,0,sizeof(st->status2)); +- st->status_check_interval = 500; +- +-/* Give the AP some time */ +- msleep(200); +- +- return 0; +-} +- +-static int bcm3510_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int bcm3510_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) +-{ +- s->min_delay_ms = 1000; +- s->step_size = 0; +- s->max_drift = 0; +- return 0; +-} +- +-static void bcm3510_release(struct dvb_frontend* fe) +-{ +- struct bcm3510_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-/* firmware download: +- * firmware file is build up like this: +- * 16bit addr, 16bit length, 8byte of length +- */ +-#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw" +- +-static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b, +- u16 len) +-{ +- int ret = 0,i; +- bcm3510_register_value vH, vL,vD; +- +- vH.MADRH_a9 = addr >> 8; +- vL.MADRL_aa = addr; +- if ((ret = bcm3510_writeB(st,0xa9,vH)) < 0) return ret; +- if ((ret = bcm3510_writeB(st,0xaa,vL)) < 0) return ret; +- +- for (i = 0; i < len; i++) { +- vD.MDATA_ab = b[i]; +- if ((ret = bcm3510_writeB(st,0xab,vD)) < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int bcm3510_download_firmware(struct dvb_frontend* fe) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- const struct firmware *fw; +- u16 addr,len; +- const u8 *b; +- int ret,i; +- +- deb_info("requesting firmware\n"); +- if ((ret = st->config->request_firmware(fe, &fw, BCM3510_DEFAULT_FIRMWARE)) < 0) { +- err("could not load firmware (%s): %d",BCM3510_DEFAULT_FIRMWARE,ret); +- return ret; +- } +- deb_info("got firmware: %zd\n",fw->size); +- +- b = fw->data; +- for (i = 0; i < fw->size;) { +- addr = le16_to_cpu( *( (u16 *)&b[i] ) ); +- len = le16_to_cpu( *( (u16 *)&b[i+2] ) ); +- deb_info("firmware chunk, addr: 0x%04x, len: 0x%04x, total length: 0x%04zx\n",addr,len,fw->size); +- if ((ret = bcm3510_write_ram(st,addr,&b[i+4],len)) < 0) { +- err("firmware download failed: %d\n",ret); +- return ret; +- } +- i += 4 + len; +- } +- release_firmware(fw); +- deb_info("firmware download successfully completed\n"); +- return 0; +-} +- +-static int bcm3510_check_firmware_version(struct bcm3510_state *st) +-{ +- struct bcm3510_hab_cmd_get_version_info ver; +- bcm3510_do_hab_cmd(st,CMD_GET_VERSION_INFO,MSGID_GET_VERSION_INFO,NULL,0,(u8*)&ver,sizeof(ver)); +- +- deb_info("Version information: 0x%02x 0x%02x 0x%02x 0x%02x\n", +- ver.microcode_version, ver.script_version, ver.config_version, ver.demod_version); +- +- if (ver.script_version == BCM3510_DEF_SCRIPT_VERSION && +- ver.config_version == BCM3510_DEF_CONFIG_VERSION && +- ver.demod_version == BCM3510_DEF_DEMOD_VERSION) +- return 0; +- +- deb_info("version check failed\n"); +- return -ENODEV; +-} +- +-/* (un)resetting the AP */ +-static int bcm3510_reset(struct bcm3510_state *st) +-{ +- int ret; +- unsigned long t; +- bcm3510_register_value v; +- +- bcm3510_readB(st,0xa0,&v); v.HCTL1_a0.RESET = 1; +- if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) +- return ret; +- +- t = jiffies + 3*HZ; +- while (time_before(jiffies, t)) { +- msleep(10); +- if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) +- return ret; +- +- if (v.APSTAT1_a2.RESET) +- return 0; +- } +- deb_info("reset timed out\n"); +- return -ETIMEDOUT; +-} +- +-static int bcm3510_clear_reset(struct bcm3510_state *st) +-{ +- bcm3510_register_value v; +- int ret; +- unsigned long t; +- +- v.raw = 0; +- if ((ret = bcm3510_writeB(st,0xa0,v)) < 0) +- return ret; +- +- t = jiffies + 3*HZ; +- while (time_before(jiffies, t)) { +- msleep(10); +- if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) +- return ret; +- +- /* verify that reset is cleared */ +- if (!v.APSTAT1_a2.RESET) +- return 0; +- } +- deb_info("reset clear timed out\n"); +- return -ETIMEDOUT; +-} +- +-static int bcm3510_init_cold(struct bcm3510_state *st) +-{ +- int ret; +- bcm3510_register_value v; +- +- /* read Acquisation Processor status register and check it is not in RUN mode */ +- if ((ret = bcm3510_readB(st,0xa2,&v)) < 0) +- return ret; +- if (v.APSTAT1_a2.RUN) { +- deb_info("AP is already running - firmware already loaded.\n"); +- return 0; +- } +- +- deb_info("reset?\n"); +- if ((ret = bcm3510_reset(st)) < 0) +- return ret; +- +- deb_info("tristate?\n"); +- /* tri-state */ +- v.TSTCTL_2e.CTL = 0; +- if ((ret = bcm3510_writeB(st,0x2e,v)) < 0) +- return ret; +- +- deb_info("firmware?\n"); +- if ((ret = bcm3510_download_firmware(&st->frontend)) < 0 || +- (ret = bcm3510_clear_reset(st)) < 0) +- return ret; +- +- /* anything left here to Let the acquisition processor begin execution at program counter 0000 ??? */ +- +- return 0; +-} +- +-static int bcm3510_init(struct dvb_frontend* fe) +-{ +- struct bcm3510_state* st = fe->demodulator_priv; +- bcm3510_register_value j; +- struct bcm3510_hab_cmd_set_agc c; +- int ret; +- +- if ((ret = bcm3510_readB(st,0xca,&j)) < 0) +- return ret; +- +- deb_info("JDEC: %02x\n",j.raw); +- +- switch (j.JDEC_ca.JDEC) { +- case JDEC_WAIT_AT_RAM: +- deb_info("attempting to download firmware\n"); +- if ((ret = bcm3510_init_cold(st)) < 0) +- return ret; +- case JDEC_EEPROM_LOAD_WAIT: /* fall-through is wanted */ +- deb_info("firmware is loaded\n"); +- bcm3510_check_firmware_version(st); +- break; +- default: +- return -ENODEV; +- } +- +- memset(&c,0,1); +- c.SEL = 1; +- bcm3510_do_hab_cmd(st,CMD_AUTO_PARAM,MSGID_SET_RF_AGC_SEL,(u8 *)&c,sizeof(c),NULL,0); +- +- return 0; +-} +- +- +-static struct dvb_frontend_ops bcm3510_ops; +- +-struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, +- struct i2c_adapter *i2c) +-{ +- struct bcm3510_state* state = NULL; +- int ret; +- bcm3510_register_value v; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct bcm3510_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- +- state->config = config; +- state->i2c = i2c; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &bcm3510_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- mutex_init(&state->hab_mutex); +- +- if ((ret = bcm3510_readB(state,0xe0,&v)) < 0) +- goto error; +- +- deb_info("Revision: 0x%1x, Layer: 0x%1x.\n",v.REVID_e0.REV,v.REVID_e0.LAYER); +- +- if ((v.REVID_e0.REV != 0x1 && v.REVID_e0.LAYER != 0xb) && /* cold */ +- (v.REVID_e0.REV != 0x8 && v.REVID_e0.LAYER != 0x0)) /* warm */ +- goto error; +- +- info("Revision: 0x%1x, Layer: 0x%1x.",v.REVID_e0.REV,v.REVID_e0.LAYER); +- +- bcm3510_reset(state); +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(bcm3510_attach); +- +-static struct dvb_frontend_ops bcm3510_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Broadcom BCM3510 VSB/QAM frontend", +- .frequency_min = 54000000, +- .frequency_max = 803000000, +- /* stepsize is just a guess */ +- .frequency_stepsize = 0, +- .caps = +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_8VSB | FE_CAN_16VSB | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | FE_CAN_QAM_256 +- }, +- +- .release = bcm3510_release, +- +- .init = bcm3510_init, +- .sleep = bcm3510_sleep, +- +- .set_frontend = bcm3510_set_frontend, +- .get_tune_settings = bcm3510_get_tune_settings, +- +- .read_status = bcm3510_read_status, +- .read_ber = bcm3510_read_ber, +- .read_signal_strength = bcm3510_read_signal_strength, +- .read_snr = bcm3510_read_snr, +- .read_ucblocks = bcm3510_read_unc, +-}; +- +-MODULE_DESCRIPTION("Broadcom BCM3510 ATSC (8VSB/16VSB & ITU J83 AnnexB FEC QAM64/256) demodulator driver"); +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb/frontends/bcm3510.h +deleted file mode 100644 +index f4575c0..0000000 +--- a/drivers/media/dvb/frontends/bcm3510.h ++++ /dev/null +@@ -1,49 +0,0 @@ +-/* +- * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) +- * +- * Copyright (C) 2001-5, B2C2 inc. +- * +- * GPL/Linux driver written by Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +-#ifndef BCM3510_H +-#define BCM3510_H +- +-#include +-#include +- +-struct bcm3510_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* request firmware for device */ +- int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +-}; +- +-#if defined(CONFIG_DVB_BCM3510) || (defined(CONFIG_DVB_BCM3510_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_BCM3510 +- +-#endif +diff --git a/drivers/media/dvb/frontends/bcm3510_priv.h b/drivers/media/dvb/frontends/bcm3510_priv.h +deleted file mode 100644 +index 3bb1bc2..0000000 +--- a/drivers/media/dvb/frontends/bcm3510_priv.h ++++ /dev/null +@@ -1,460 +0,0 @@ +-/* +- * Support for the Broadcom BCM3510 ATSC demodulator (1st generation Air2PC) +- * +- * Copyright (C) 2001-5, B2C2 inc. +- * +- * GPL/Linux driver written by Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +-#ifndef __BCM3510_PRIV_H__ +-#define __BCM3510_PRIV_H__ +- +-#define PACKED __attribute__((packed)) +- +-#undef err +-#define err(format, arg...) printk(KERN_ERR "bcm3510: " format "\n" , ## arg) +-#undef info +-#define info(format, arg...) printk(KERN_INFO "bcm3510: " format "\n" , ## arg) +-#undef warn +-#define warn(format, arg...) printk(KERN_WARNING "bcm3510: " format "\n" , ## arg) +- +- +-#define PANASONIC_FIRST_IF_BASE_IN_KHz 1407500 +-#define BCM3510_SYMBOL_RATE 5381000 +- +-typedef union { +- u8 raw; +- +- struct { +- u8 CTL :8; +- } TSTCTL_2e; +- +- u8 LDCERC_4e; +- u8 LDUERC_4f; +- u8 LD_BER0_65; +- u8 LD_BER1_66; +- u8 LD_BER2_67; +- u8 LD_BER3_68; +- +- struct { +- u8 RESET :1; +- u8 IDLE :1; +- u8 STOP :1; +- u8 HIRQ0 :1; +- u8 HIRQ1 :1; +- u8 na0 :1; +- u8 HABAV :1; +- u8 na1 :1; +- } HCTL1_a0; +- +- struct { +- u8 na0 :1; +- u8 IDLMSK :1; +- u8 STMSK :1; +- u8 I0MSK :1; +- u8 I1MSK :1; +- u8 na1 :1; +- u8 HABMSK :1; +- u8 na2 :1; +- } HCTLMSK_a1; +- +- struct { +- u8 RESET :1; +- u8 IDLE :1; +- u8 STOP :1; +- u8 RUN :1; +- u8 HABAV :1; +- u8 MEMAV :1; +- u8 ALDONE :1; +- u8 REIRQ :1; +- } APSTAT1_a2; +- +- struct { +- u8 RSTMSK :1; +- u8 IMSK :1; +- u8 SMSK :1; +- u8 RMSK :1; +- u8 HABMSK :1; +- u8 MAVMSK :1; +- u8 ALDMSK :1; +- u8 REMSK :1; +- } APMSK1_a3; +- +- u8 APSTAT2_a4; +- u8 APMSK2_a5; +- +- struct { +- u8 HABADR :7; +- u8 na :1; +- } HABADR_a6; +- +- u8 HABDATA_a7; +- +- struct { +- u8 HABR :1; +- u8 LDHABR :1; +- u8 APMSK :1; +- u8 HMSK :1; +- u8 LDMSK :1; +- u8 na :3; +- } HABSTAT_a8; +- +- u8 MADRH_a9; +- u8 MADRL_aa; +- u8 MDATA_ab; +- +- struct { +-#define JDEC_WAIT_AT_RAM 0x7 +-#define JDEC_EEPROM_LOAD_WAIT 0x4 +- u8 JDEC :3; +- u8 na :5; +- } JDEC_ca; +- +- struct { +- u8 REV :4; +- u8 LAYER :4; +- } REVID_e0; +- +- struct { +- u8 unk0 :1; +- u8 CNTCTL :1; +- u8 BITCNT :1; +- u8 unk1 :1; +- u8 RESYNC :1; +- u8 unk2 :3; +- } BERCTL_fa; +- +- struct { +- u8 CSEL0 :1; +- u8 CLKED0 :1; +- u8 CSEL1 :1; +- u8 CLKED1 :1; +- u8 CLKLEV :1; +- u8 SPIVAR :1; +- u8 na :2; +- } TUNSET_fc; +- +- struct { +- u8 CLK :1; +- u8 DATA :1; +- u8 CS0 :1; +- u8 CS1 :1; +- u8 AGCSEL :1; +- u8 na0 :1; +- u8 TUNSEL :1; +- u8 na1 :1; +- } TUNCTL_fd; +- +- u8 TUNSEL0_fe; +- u8 TUNSEL1_ff; +- +-} bcm3510_register_value; +- +-/* HAB commands */ +- +-/* version */ +-#define CMD_GET_VERSION_INFO 0x3D +-#define MSGID_GET_VERSION_INFO 0x15 +-struct bcm3510_hab_cmd_get_version_info { +- u8 microcode_version; +- u8 script_version; +- u8 config_version; +- u8 demod_version; +-} PACKED; +- +-#define BCM3510_DEF_MICROCODE_VERSION 0x0E +-#define BCM3510_DEF_SCRIPT_VERSION 0x06 +-#define BCM3510_DEF_CONFIG_VERSION 0x01 +-#define BCM3510_DEF_DEMOD_VERSION 0xB1 +- +-/* acquire */ +-#define CMD_ACQUIRE 0x38 +- +-#define MSGID_EXT_TUNER_ACQUIRE 0x0A +-struct bcm3510_hab_cmd_ext_acquire { +- struct { +- u8 MODE :4; +- u8 BW :1; +- u8 FA :1; +- u8 NTSCSWEEP :1; +- u8 OFFSET :1; +- } PACKED ACQUIRE0; /* control_byte */ +- +- struct { +- u8 IF_FREQ :3; +- u8 zero0 :1; +- u8 SYM_RATE :3; +- u8 zero1 :1; +- } PACKED ACQUIRE1; /* sym_if */ +- +- u8 IF_OFFSET0; /* IF_Offset_10hz */ +- u8 IF_OFFSET1; +- u8 SYM_OFFSET0; /* SymbolRateOffset */ +- u8 SYM_OFFSET1; +- u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ +- u8 NTSC_OFFSET1; +-} PACKED; +- +-#define MSGID_INT_TUNER_ACQUIRE 0x0B +-struct bcm3510_hab_cmd_int_acquire { +- struct { +- u8 MODE :4; +- u8 BW :1; +- u8 FA :1; +- u8 NTSCSWEEP :1; +- u8 OFFSET :1; +- } PACKED ACQUIRE0; /* control_byte */ +- +- struct { +- u8 IF_FREQ :3; +- u8 zero0 :1; +- u8 SYM_RATE :3; +- u8 zero1 :1; +- } PACKED ACQUIRE1; /* sym_if */ +- +- u8 TUNER_FREQ0; +- u8 TUNER_FREQ1; +- u8 TUNER_FREQ2; +- u8 TUNER_FREQ3; +- u8 IF_OFFSET0; /* IF_Offset_10hz */ +- u8 IF_OFFSET1; +- u8 SYM_OFFSET0; /* SymbolRateOffset */ +- u8 SYM_OFFSET1; +- u8 NTSC_OFFSET0; /* NTSC_Offset_10hz */ +- u8 NTSC_OFFSET1; +-} PACKED; +- +-/* modes */ +-#define BCM3510_QAM16 = 0x01 +-#define BCM3510_QAM32 = 0x02 +-#define BCM3510_QAM64 = 0x03 +-#define BCM3510_QAM128 = 0x04 +-#define BCM3510_QAM256 = 0x05 +-#define BCM3510_8VSB = 0x0B +-#define BCM3510_16VSB = 0x0D +- +-/* IF_FREQS */ +-#define BCM3510_IF_TERRESTRIAL 0x0 +-#define BCM3510_IF_CABLE 0x1 +-#define BCM3510_IF_USE_CMD 0x7 +- +-/* SYM_RATE */ +-#define BCM3510_SR_8VSB 0x0 /* 5381119 s/sec */ +-#define BCM3510_SR_256QAM 0x1 /* 5360537 s/sec */ +-#define BCM3510_SR_16QAM 0x2 /* 5056971 s/sec */ +-#define BCM3510_SR_MISC 0x3 /* 5000000 s/sec */ +-#define BCM3510_SR_USE_CMD 0x7 +- +-/* special symbol rate */ +-#define CMD_SET_VALUE_NOT_LISTED 0x2d +-#define MSGID_SET_SYMBOL_RATE_NOT_LISTED 0x0c +-struct bcm3510_hab_cmd_set_sr_not_listed { +- u8 HOST_SYM_RATE0; +- u8 HOST_SYM_RATE1; +- u8 HOST_SYM_RATE2; +- u8 HOST_SYM_RATE3; +-} PACKED; +- +-/* special IF */ +-#define MSGID_SET_IF_FREQ_NOT_LISTED 0x0d +-struct bcm3510_hab_cmd_set_if_freq_not_listed { +- u8 HOST_IF_FREQ0; +- u8 HOST_IF_FREQ1; +- u8 HOST_IF_FREQ2; +- u8 HOST_IF_FREQ3; +-} PACKED; +- +-/* auto reacquire */ +-#define CMD_AUTO_PARAM 0x2a +-#define MSGID_AUTO_REACQUIRE 0x0e +-struct bcm3510_hab_cmd_auto_reacquire { +- u8 ACQ :1; /* on/off*/ +- u8 unused :7; +-} PACKED; +- +-#define MSGID_SET_RF_AGC_SEL 0x12 +-struct bcm3510_hab_cmd_set_agc { +- u8 LVL :1; +- u8 unused :6; +- u8 SEL :1; +-} PACKED; +- +-#define MSGID_SET_AUTO_INVERSION 0x14 +-struct bcm3510_hab_cmd_auto_inversion { +- u8 AI :1; +- u8 unused :7; +-} PACKED; +- +- +-/* bert control */ +-#define CMD_STATE_CONTROL 0x12 +-#define MSGID_BERT_CONTROL 0x0e +-#define MSGID_BERT_SET 0xfa +-struct bcm3510_hab_cmd_bert_control { +- u8 BE :1; +- u8 unused :7; +-} PACKED; +- +-#define MSGID_TRI_STATE 0x2e +-struct bcm3510_hab_cmd_tri_state { +- u8 RE :1; /* a/d ram port pins */ +- u8 PE :1; /* baud clock pin */ +- u8 AC :1; /* a/d clock pin */ +- u8 BE :1; /* baud clock pin */ +- u8 unused :4; +-} PACKED; +- +- +-/* tune */ +-#define CMD_TUNE 0x38 +-#define MSGID_TUNE 0x16 +-struct bcm3510_hab_cmd_tune_ctrl_data_pair { +- struct { +-#define BITS_8 0x07 +-#define BITS_7 0x06 +-#define BITS_6 0x05 +-#define BITS_5 0x04 +-#define BITS_4 0x03 +-#define BITS_3 0x02 +-#define BITS_2 0x01 +-#define BITS_1 0x00 +- u8 size :3; +- u8 unk :2; +- u8 clk_off :1; +- u8 cs0 :1; +- u8 cs1 :1; +- +- } PACKED ctrl; +- +- u8 data; +-} PACKED; +- +-struct bcm3510_hab_cmd_tune { +- u8 length; +- u8 clock_width; +- u8 misc; +- u8 TUNCTL_state; +- +- struct bcm3510_hab_cmd_tune_ctrl_data_pair ctl_dat[16]; +-} PACKED; +- +-#define CMD_STATUS 0x38 +-#define MSGID_STATUS1 0x08 +-struct bcm3510_hab_cmd_status1 { +- struct { +- u8 EQ_MODE :4; +- u8 reserved :2; +- u8 QRE :1; /* if QSE and the spectrum is inversed */ +- u8 QSE :1; /* automatic spectral inversion */ +- } PACKED STATUS0; +- +- struct { +- u8 RECEIVER_LOCK :1; +- u8 FEC_LOCK :1; +- u8 OUT_PLL_LOCK :1; +- u8 reserved :5; +- } PACKED STATUS1; +- +- struct { +- u8 reserved :2; +- u8 BW :1; +- u8 NTE :1; /* NTSC filter sweep enabled */ +- u8 AQI :1; /* currently acquiring */ +- u8 FA :1; /* fast acquisition */ +- u8 ARI :1; /* auto reacquire */ +- u8 TI :1; /* programming the tuner */ +- } PACKED STATUS2; +- u8 STATUS3; +- u8 SNR_EST0; +- u8 SNR_EST1; +- u8 TUNER_FREQ0; +- u8 TUNER_FREQ1; +- u8 TUNER_FREQ2; +- u8 TUNER_FREQ3; +- u8 SYM_RATE0; +- u8 SYM_RATE1; +- u8 SYM_RATE2; +- u8 SYM_RATE3; +- u8 SYM_OFFSET0; +- u8 SYM_OFFSET1; +- u8 SYM_ERROR0; +- u8 SYM_ERROR1; +- u8 IF_FREQ0; +- u8 IF_FREQ1; +- u8 IF_FREQ2; +- u8 IF_FREQ3; +- u8 IF_OFFSET0; +- u8 IF_OFFSET1; +- u8 IF_ERROR0; +- u8 IF_ERROR1; +- u8 NTSC_FILTER0; +- u8 NTSC_FILTER1; +- u8 NTSC_FILTER2; +- u8 NTSC_FILTER3; +- u8 NTSC_OFFSET0; +- u8 NTSC_OFFSET1; +- u8 NTSC_ERROR0; +- u8 NTSC_ERROR1; +- u8 INT_AGC_LEVEL0; +- u8 INT_AGC_LEVEL1; +- u8 EXT_AGC_LEVEL0; +- u8 EXT_AGC_LEVEL1; +-} PACKED; +- +-#define MSGID_STATUS2 0x14 +-struct bcm3510_hab_cmd_status2 { +- struct { +- u8 EQ_MODE :4; +- u8 reserved :2; +- u8 QRE :1; +- u8 QSR :1; +- } PACKED STATUS0; +- struct { +- u8 RL :1; +- u8 FL :1; +- u8 OL :1; +- u8 reserved :5; +- } PACKED STATUS1; +- u8 SYMBOL_RATE0; +- u8 SYMBOL_RATE1; +- u8 SYMBOL_RATE2; +- u8 SYMBOL_RATE3; +- u8 LDCERC0; +- u8 LDCERC1; +- u8 LDCERC2; +- u8 LDCERC3; +- u8 LDUERC0; +- u8 LDUERC1; +- u8 LDUERC2; +- u8 LDUERC3; +- u8 LDBER0; +- u8 LDBER1; +- u8 LDBER2; +- u8 LDBER3; +- struct { +- u8 MODE_TYPE :4; /* acquire mode 0 */ +- u8 reservd :4; +- } MODE_TYPE; +- u8 SNR_EST0; +- u8 SNR_EST1; +- u8 SIGNAL; +-} PACKED; +- +-#define CMD_SET_RF_BW_NOT_LISTED 0x3f +-#define MSGID_SET_RF_BW_NOT_LISTED 0x11 +-/* TODO */ +- +-#endif +diff --git a/drivers/media/dvb/frontends/bsbe1-d01a.h b/drivers/media/dvb/frontends/bsbe1-d01a.h +deleted file mode 100644 +index 7ed3c42..0000000 +--- a/drivers/media/dvb/frontends/bsbe1-d01a.h ++++ /dev/null +@@ -1,146 +0,0 @@ +-/* +- * bsbe1-d01a.h - ALPS BSBE1-D01A tuner support +- * +- * Copyright (C) 2011 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef BSBE1_D01A_H +-#define BSBE1_D01A_H +- +-#include "stb6000.h" +-#include "stv0288.h" +- +-static u8 stv0288_bsbe1_d01a_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x20, +- 0x09, 0x0, +- 0x0a, 0x4, +- 0x0b, 0x0, +- 0x0c, 0x0, +- 0x0d, 0x0, +- 0x0e, 0xd4, +- 0x0f, 0x30, +- 0x11, 0x80, +- 0x12, 0x03, +- 0x13, 0x48, +- 0x14, 0x84, +- 0x15, 0x45, +- 0x16, 0xb7, +- 0x17, 0x9c, +- 0x18, 0x0, +- 0x19, 0xa6, +- 0x1a, 0x88, +- 0x1b, 0x8f, +- 0x1c, 0xf0, +- 0x20, 0x0b, +- 0x21, 0x54, +- 0x22, 0x0, +- 0x23, 0x0, +- 0x2b, 0xff, +- 0x2c, 0xf7, +- 0x30, 0x0, +- 0x31, 0x1e, +- 0x32, 0x14, +- 0x33, 0x0f, +- 0x34, 0x09, +- 0x35, 0x0c, +- 0x36, 0x05, +- 0x37, 0x2f, +- 0x38, 0x16, +- 0x39, 0xbd, +- 0x3a, 0x03, +- 0x3b, 0x13, +- 0x3c, 0x11, +- 0x3d, 0x30, +- 0x40, 0x63, +- 0x41, 0x04, +- 0x42, 0x60, +- 0x43, 0x00, +- 0x44, 0x00, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x47, 0x00, +- 0x4a, 0x00, +- 0x50, 0x10, +- 0x51, 0x36, +- 0x52, 0x09, +- 0x53, 0x94, +- 0x54, 0x62, +- 0x55, 0x29, +- 0x56, 0x64, +- 0x57, 0x2b, +- 0x58, 0x54, +- 0x59, 0x86, +- 0x5a, 0x0, +- 0x5b, 0x9b, +- 0x5c, 0x08, +- 0x5d, 0x7f, +- 0x5e, 0x0, +- 0x5f, 0xff, +- 0x70, 0x0, +- 0x71, 0x0, +- 0x72, 0x0, +- 0x74, 0x0, +- 0x75, 0x0, +- 0x76, 0x0, +- 0x81, 0x0, +- 0x82, 0x3f, +- 0x83, 0x3f, +- 0x84, 0x0, +- 0x85, 0x0, +- 0x88, 0x0, +- 0x89, 0x0, +- 0x8a, 0x0, +- 0x8b, 0x0, +- 0x8c, 0x0, +- 0x90, 0x0, +- 0x91, 0x0, +- 0x92, 0x0, +- 0x93, 0x0, +- 0x94, 0x1c, +- 0x97, 0x0, +- 0xa0, 0x48, +- 0xa1, 0x0, +- 0xb0, 0xb8, +- 0xb1, 0x3a, +- 0xb2, 0x10, +- 0xb3, 0x82, +- 0xb4, 0x80, +- 0xb5, 0x82, +- 0xb6, 0x82, +- 0xb7, 0x82, +- 0xb8, 0x20, +- 0xb9, 0x0, +- 0xf0, 0x0, +- 0xf1, 0x0, +- 0xf2, 0xc0, +- 0xff, 0xff, +-}; +- +-static struct stv0288_config stv0288_bsbe1_d01a_config = { +- .demod_address = 0x68, +- .min_delay_ms = 100, +- .inittab = stv0288_bsbe1_d01a_inittab, +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/bsbe1.h b/drivers/media/dvb/frontends/bsbe1.h +deleted file mode 100644 +index 53e4d0d..0000000 +--- a/drivers/media/dvb/frontends/bsbe1.h ++++ /dev/null +@@ -1,106 +0,0 @@ +-/* +- * bsbe1.h - ALPS BSBE1 tuner support +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef BSBE1_H +-#define BSBE1_H +- +-static u8 alps_bsbe1_inittab[] = { +- 0x01, 0x15, /* XTAL = 4MHz, VCO = 352 MHz */ +- 0x02, 0x30, /* MCLK = 88 MHz */ +- 0x03, 0x00, /* ACR output 0 */ +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x05, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x00, /* DAC output 0 */ +- 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1/OP0 normal, val = 1 (LNB power on) */ +- 0x0d, 0x82, /* DC offset compensation = on, beta_agc1 = 2 */ +- 0x0f, 0x92, /* AGC1R */ +- 0x10, 0x34, /* AGC2O */ +- 0x11, 0x84, /* TLSR */ +- 0x12, 0xb9, /* CFD */ +- 0x15, 0xc9, /* lock detector threshold */ +- 0x28, 0x00, /* out imp: normal, type: parallel, FEC mode: QPSK */ +- 0x33, 0xfc, /* RS control */ +- 0x34, 0x93, /* count viterbi bit errors per 2E18 bytes */ +- 0xff, 0xff +-}; +- +- +-static int alps_bsbe1_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 int alps_bsbe1_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- int ret; +- u8 data[4]; +- u32 div; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- struct i2c_adapter *i2c = fe->tuner_priv; +- +- if ((p->frequency < 950000) || (p->frequency > 2150000)) +- return -EINVAL; +- +- div = p->frequency / 1000; +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x80 | ((div & 0x18000) >> 10) | 0x1; +- data[3] = 0xe0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer(i2c, &msg, 1); +- return (ret != 1) ? -EIO : 0; +-} +- +-static struct stv0299_config alps_bsbe1_config = { +- .demod_address = 0x68, +- .inittab = alps_bsbe1_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .min_delay_ms = 100, +- .set_symbol_rate = alps_bsbe1_set_symbol_rate, +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/bsru6.h b/drivers/media/dvb/frontends/bsru6.h +deleted file mode 100644 +index c2a578e..0000000 +--- a/drivers/media/dvb/frontends/bsru6.h ++++ /dev/null +@@ -1,143 +0,0 @@ +-/* +- * bsru6.h - ALPS BSRU6 tuner support (moved from budget-ci.c) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef BSRU6_H +-#define BSRU6_H +- +-static u8 alps_bsru6_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x40, /* DAC not used, set to high impendance mode */ +- 0x07, 0x00, /* DAC LSB */ +- 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ +- 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ +- 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ +- 0x10, 0x3f, // AGC2 0x3d +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, // lock detector threshold +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 +- 0x29, 0x1e, // 1/2 threshold +- 0x2a, 0x14, // 2/3 threshold +- 0x2b, 0x0f, // 3/4 threshold +- 0x2c, 0x09, // 5/6 threshold +- 0x2d, 0x05, // 7/8 threshold +- 0x2e, 0x01, +- 0x31, 0x1f, // test all FECs +- 0x32, 0x19, // viterbi and synchro search +- 0x33, 0xfc, // rs control +- 0x34, 0x93, // error control +- 0x0f, 0x52, +- 0xff, 0xff +-}; +- +-static int alps_bsru6_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 int alps_bsru6_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- u8 buf[4]; +- u32 div; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; +- struct i2c_adapter *i2c = fe->tuner_priv; +- +- if ((p->frequency < 950000) || (p->frequency > 2150000)) +- return -EINVAL; +- +- div = (p->frequency + (125 - 1)) / 125; /* round correctly */ +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; +- buf[3] = 0xC4; +- +- if (p->frequency > 1530000) +- buf[3] = 0xc0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(i2c, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct stv0299_config alps_bsru6_config = { +- .demod_address = 0x68, +- .inittab = alps_bsru6_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 100, +- .set_symbol_rate = alps_bsru6_set_symbol_rate, +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c +deleted file mode 100644 +index f2a90f9..0000000 +--- a/drivers/media/dvb/frontends/cx22700.c ++++ /dev/null +@@ -1,443 +0,0 @@ +-/* +- Conexant cx22700 DVB OFDM demodulator driver +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- Holger Waechtler +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "cx22700.h" +- +- +-struct cx22700_state { +- +- struct i2c_adapter* i2c; +- +- const struct cx22700_config* config; +- +- struct dvb_frontend frontend; +-}; +- +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "cx22700: " args); \ +- } while (0) +- +-static u8 init_tab [] = { +- 0x04, 0x10, +- 0x05, 0x09, +- 0x06, 0x00, +- 0x08, 0x04, +- 0x09, 0x00, +- 0x0a, 0x01, +- 0x15, 0x40, +- 0x16, 0x10, +- 0x17, 0x87, +- 0x18, 0x17, +- 0x1a, 0x10, +- 0x25, 0x04, +- 0x2e, 0x00, +- 0x39, 0x00, +- 0x3a, 0x04, +- 0x45, 0x08, +- 0x46, 0x02, +- 0x47, 0x05, +-}; +- +- +-static int cx22700_writereg (struct cx22700_state* state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf [] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- +- dprintk ("%s\n", __func__); +- +- ret = i2c_transfer (state->i2c, &msg, 1); +- +- if (ret != 1) +- printk("%s: writereg error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", +- __func__, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static int cx22700_readreg (struct cx22700_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- dprintk ("%s\n", __func__); +- +- ret = i2c_transfer (state->i2c, msg, 2); +- +- if (ret != 2) return -EIO; +- +- return b1[0]; +-} +- +-static int cx22700_set_inversion (struct cx22700_state* state, int inversion) +-{ +- u8 val; +- +- dprintk ("%s\n", __func__); +- +- switch (inversion) { +- case INVERSION_AUTO: +- return -EOPNOTSUPP; +- case INVERSION_ON: +- val = cx22700_readreg (state, 0x09); +- return cx22700_writereg (state, 0x09, val | 0x01); +- case INVERSION_OFF: +- val = cx22700_readreg (state, 0x09); +- return cx22700_writereg (state, 0x09, val & 0xfe); +- default: +- return -EINVAL; +- } +-} +- +-static int cx22700_set_tps(struct cx22700_state *state, +- struct dtv_frontend_properties *p) +-{ +- static const u8 qam_tab [4] = { 0, 1, 0, 2 }; +- static const u8 fec_tab [6] = { 0, 1, 2, 0, 3, 4 }; +- u8 val; +- +- dprintk ("%s\n", __func__); +- +- if (p->code_rate_HP < FEC_1_2 || p->code_rate_HP > FEC_7_8) +- return -EINVAL; +- +- if (p->code_rate_LP < FEC_1_2 || p->code_rate_LP > FEC_7_8) +- return -EINVAL; +- +- if (p->code_rate_HP == FEC_4_5 || p->code_rate_LP == FEC_4_5) +- return -EINVAL; +- +- if (p->guard_interval < GUARD_INTERVAL_1_32 || +- p->guard_interval > GUARD_INTERVAL_1_4) +- return -EINVAL; +- +- if (p->transmission_mode != TRANSMISSION_MODE_2K && +- p->transmission_mode != TRANSMISSION_MODE_8K) +- return -EINVAL; +- +- if (p->modulation != QPSK && +- p->modulation != QAM_16 && +- p->modulation != QAM_64) +- return -EINVAL; +- +- if (p->hierarchy < HIERARCHY_NONE || +- p->hierarchy > HIERARCHY_4) +- return -EINVAL; +- +- if (p->bandwidth_hz > 8000000 || p->bandwidth_hz < 6000000) +- return -EINVAL; +- +- if (p->bandwidth_hz == 7000000) +- cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 | 0x10)); +- else +- cx22700_writereg (state, 0x09, cx22700_readreg (state, 0x09 & ~0x10)); +- +- val = qam_tab[p->modulation - QPSK]; +- val |= p->hierarchy - HIERARCHY_NONE; +- +- cx22700_writereg (state, 0x04, val); +- +- val = fec_tab[p->code_rate_HP - FEC_1_2] << 3; +- val |= fec_tab[p->code_rate_LP - FEC_1_2]; +- +- cx22700_writereg (state, 0x05, val); +- +- val = (p->guard_interval - GUARD_INTERVAL_1_32) << 2; +- val |= p->transmission_mode - TRANSMISSION_MODE_2K; +- +- cx22700_writereg (state, 0x06, val); +- +- cx22700_writereg (state, 0x08, 0x04 | 0x02); /* use user tps parameters */ +- cx22700_writereg (state, 0x08, 0x04); /* restart acquisition */ +- +- return 0; +-} +- +-static int cx22700_get_tps(struct cx22700_state *state, +- struct dtv_frontend_properties *p) +-{ +- static const fe_modulation_t qam_tab [3] = { QPSK, QAM_16, QAM_64 }; +- static const fe_code_rate_t fec_tab [5] = { FEC_1_2, FEC_2_3, FEC_3_4, +- FEC_5_6, FEC_7_8 }; +- u8 val; +- +- dprintk ("%s\n", __func__); +- +- if (!(cx22700_readreg(state, 0x07) & 0x20)) /* tps valid? */ +- return -EAGAIN; +- +- val = cx22700_readreg (state, 0x01); +- +- if ((val & 0x7) > 4) +- p->hierarchy = HIERARCHY_AUTO; +- else +- p->hierarchy = HIERARCHY_NONE + (val & 0x7); +- +- if (((val >> 3) & 0x3) > 2) +- p->modulation = QAM_AUTO; +- else +- p->modulation = qam_tab[(val >> 3) & 0x3]; +- +- val = cx22700_readreg (state, 0x02); +- +- if (((val >> 3) & 0x07) > 4) +- p->code_rate_HP = FEC_AUTO; +- else +- p->code_rate_HP = fec_tab[(val >> 3) & 0x07]; +- +- if ((val & 0x07) > 4) +- p->code_rate_LP = FEC_AUTO; +- else +- p->code_rate_LP = fec_tab[val & 0x07]; +- +- val = cx22700_readreg (state, 0x03); +- +- p->guard_interval = GUARD_INTERVAL_1_32 + ((val >> 6) & 0x3); +- p->transmission_mode = TRANSMISSION_MODE_2K + ((val >> 5) & 0x1); +- +- return 0; +-} +- +-static int cx22700_init (struct dvb_frontend* fe) +- +-{ struct cx22700_state* state = fe->demodulator_priv; +- int i; +- +- dprintk("cx22700_init: init chip\n"); +- +- cx22700_writereg (state, 0x00, 0x02); /* soft reset */ +- cx22700_writereg (state, 0x00, 0x00); +- +- msleep(10); +- +- for (i=0; idemodulator_priv; +- +- u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) +- | (cx22700_readreg (state, 0x0e) << 1); +- u8 sync = cx22700_readreg (state, 0x07); +- +- *status = 0; +- +- if (rs_ber < 0xff00) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 0x20) +- *status |= FE_HAS_CARRIER; +- +- if (sync & 0x10) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 0x10) +- *status |= FE_HAS_SYNC; +- +- if (*status == 0x0f) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int cx22700_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- +- *ber = cx22700_readreg (state, 0x0c) & 0x7f; +- cx22700_writereg (state, 0x0c, 0x00); +- +- return 0; +-} +- +-static int cx22700_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- +- u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) +- | (cx22700_readreg (state, 0x0e) << 1); +- *signal_strength = ~rs_ber; +- +- return 0; +-} +- +-static int cx22700_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- +- u16 rs_ber = (cx22700_readreg (state, 0x0d) << 9) +- | (cx22700_readreg (state, 0x0e) << 1); +- *snr = ~rs_ber; +- +- return 0; +-} +- +-static int cx22700_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- +- *ucblocks = cx22700_readreg (state, 0x0f); +- cx22700_writereg (state, 0x0f, 0x00); +- +- return 0; +-} +- +-static int cx22700_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct cx22700_state* state = fe->demodulator_priv; +- +- cx22700_writereg (state, 0x00, 0x02); /* XXX CHECKME: soft reset*/ +- cx22700_writereg (state, 0x00, 0x00); +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- cx22700_set_inversion(state, c->inversion); +- cx22700_set_tps(state, c); +- cx22700_writereg (state, 0x37, 0x01); /* PAL loop filter off */ +- cx22700_writereg (state, 0x00, 0x01); /* restart acquire */ +- +- return 0; +-} +- +-static int cx22700_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct cx22700_state* state = fe->demodulator_priv; +- u8 reg09 = cx22700_readreg (state, 0x09); +- +- c->inversion = reg09 & 0x1 ? INVERSION_ON : INVERSION_OFF; +- return cx22700_get_tps(state, c); +-} +- +-static int cx22700_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- +- if (enable) { +- return cx22700_writereg(state, 0x0a, 0x00); +- } else { +- return cx22700_writereg(state, 0x0a, 0x01); +- } +-} +- +-static int cx22700_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 150; +- fesettings->step_size = 166667; +- fesettings->max_drift = 166667*2; +- return 0; +-} +- +-static void cx22700_release(struct dvb_frontend* fe) +-{ +- struct cx22700_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops cx22700_ops; +- +-struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, +- struct i2c_adapter* i2c) +-{ +- struct cx22700_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if (cx22700_readreg(state, 0x07) < 0) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &cx22700_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops cx22700_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Conexant CX22700 DVB-T", +- .frequency_min = 470000000, +- .frequency_max = 860000000, +- .frequency_stepsize = 166667, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_RECOVER +- }, +- +- .release = cx22700_release, +- +- .init = cx22700_init, +- .i2c_gate_ctrl = cx22700_i2c_gate_ctrl, +- +- .set_frontend = cx22700_set_frontend, +- .get_frontend = cx22700_get_frontend, +- .get_tune_settings = cx22700_get_tune_settings, +- +- .read_status = cx22700_read_status, +- .read_ber = cx22700_read_ber, +- .read_signal_strength = cx22700_read_signal_strength, +- .read_snr = cx22700_read_snr, +- .read_ucblocks = cx22700_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Holger Waechtler"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(cx22700_attach); +diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h +deleted file mode 100644 +index 4757a93..0000000 +--- a/drivers/media/dvb/frontends/cx22700.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- Conexant CX22700 DVB OFDM demodulator driver +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- Holger Waechtler +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef CX22700_H +-#define CX22700_H +- +-#include +- +-struct cx22700_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-#if defined(CONFIG_DVB_CX22700) || (defined(CONFIG_DVB_CX22700_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_CX22700 +- +-#endif // CX22700_H +diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c +deleted file mode 100644 +index faba824..0000000 +--- a/drivers/media/dvb/frontends/cx22702.c ++++ /dev/null +@@ -1,637 +0,0 @@ +-/* +- Conexant 22702 DVB OFDM demodulator driver +- +- based on: +- Alps TDMB7 DVB OFDM demodulator driver +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- Holger Waechtler +- +- Copyright (C) 2004 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "cx22702.h" +- +-struct cx22702_state { +- +- struct i2c_adapter *i2c; +- +- /* configuration settings */ +- const struct cx22702_config *config; +- +- struct dvb_frontend frontend; +- +- /* previous uncorrected block counter */ +- u8 prevUCBlocks; +-}; +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-#define dprintk if (debug) printk +- +-/* Register values to initialise the demod */ +-static const u8 init_tab[] = { +- 0x00, 0x00, /* Stop acquisition */ +- 0x0B, 0x06, +- 0x09, 0x01, +- 0x0D, 0x41, +- 0x16, 0x32, +- 0x20, 0x0A, +- 0x21, 0x17, +- 0x24, 0x3e, +- 0x26, 0xff, +- 0x27, 0x10, +- 0x28, 0x00, +- 0x29, 0x00, +- 0x2a, 0x10, +- 0x2b, 0x00, +- 0x2c, 0x10, +- 0x2d, 0x00, +- 0x48, 0xd4, +- 0x49, 0x56, +- 0x6b, 0x1e, +- 0xc8, 0x02, +- 0xf9, 0x00, +- 0xfa, 0x00, +- 0xfb, 0x00, +- 0xfc, 0x00, +- 0xfd, 0x00, +-}; +- +-static int cx22702_writereg(struct cx22702_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = state->config->demod_address, .flags = 0, +- .buf = buf, .len = 2 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (unlikely(ret != 1)) { +- printk(KERN_ERR +- "%s: error (reg == 0x%02x, val == 0x%02x, ret == %i)\n", +- __func__, reg, data, ret); +- return -1; +- } +- +- return 0; +-} +- +-static u8 cx22702_readreg(struct cx22702_state *state, u8 reg) +-{ +- int ret; +- u8 data; +- +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, +- .buf = ®, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, +- .buf = &data, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (unlikely(ret != 2)) { +- printk(KERN_ERR "%s: error (reg == 0x%02x, ret == %i)\n", +- __func__, reg, ret); +- return 0; +- } +- +- return data; +-} +- +-static int cx22702_set_inversion(struct cx22702_state *state, int inversion) +-{ +- u8 val; +- +- val = cx22702_readreg(state, 0x0C); +- switch (inversion) { +- case INVERSION_AUTO: +- return -EOPNOTSUPP; +- case INVERSION_ON: +- val |= 0x01; +- break; +- case INVERSION_OFF: +- val &= 0xfe; +- break; +- default: +- return -EINVAL; +- } +- return cx22702_writereg(state, 0x0C, val); +-} +- +-/* Retrieve the demod settings */ +-static int cx22702_get_tps(struct cx22702_state *state, +- struct dtv_frontend_properties *p) +-{ +- u8 val; +- +- /* Make sure the TPS regs are valid */ +- if (!(cx22702_readreg(state, 0x0A) & 0x20)) +- return -EAGAIN; +- +- val = cx22702_readreg(state, 0x01); +- switch ((val & 0x18) >> 3) { +- case 0: +- p->modulation = QPSK; +- break; +- case 1: +- p->modulation = QAM_16; +- break; +- case 2: +- p->modulation = QAM_64; +- break; +- } +- switch (val & 0x07) { +- case 0: +- p->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- p->hierarchy = HIERARCHY_1; +- break; +- case 2: +- p->hierarchy = HIERARCHY_2; +- break; +- case 3: +- p->hierarchy = HIERARCHY_4; +- break; +- } +- +- +- val = cx22702_readreg(state, 0x02); +- switch ((val & 0x38) >> 3) { +- case 0: +- p->code_rate_HP = FEC_1_2; +- break; +- case 1: +- p->code_rate_HP = FEC_2_3; +- break; +- case 2: +- p->code_rate_HP = FEC_3_4; +- break; +- case 3: +- p->code_rate_HP = FEC_5_6; +- break; +- case 4: +- p->code_rate_HP = FEC_7_8; +- break; +- } +- switch (val & 0x07) { +- case 0: +- p->code_rate_LP = FEC_1_2; +- break; +- case 1: +- p->code_rate_LP = FEC_2_3; +- break; +- case 2: +- p->code_rate_LP = FEC_3_4; +- break; +- case 3: +- p->code_rate_LP = FEC_5_6; +- break; +- case 4: +- p->code_rate_LP = FEC_7_8; +- break; +- } +- +- val = cx22702_readreg(state, 0x03); +- switch ((val & 0x0c) >> 2) { +- case 0: +- p->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- p->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- p->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- p->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- switch (val & 0x03) { +- case 0: +- p->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- p->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- } +- +- return 0; +-} +- +-static int cx22702_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- u8 val; +- +- dprintk("%s(%d)\n", __func__, enable); +- val = cx22702_readreg(state, 0x0D); +- if (enable) +- val &= 0xfe; +- else +- val |= 0x01; +- return cx22702_writereg(state, 0x0D, val); +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-static int cx22702_set_tps(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- u8 val; +- struct cx22702_state *state = fe->demodulator_priv; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* set inversion */ +- cx22702_set_inversion(state, p->inversion); +- +- /* set bandwidth */ +- val = cx22702_readreg(state, 0x0C) & 0xcf; +- switch (p->bandwidth_hz) { +- case 6000000: +- val |= 0x20; +- break; +- case 7000000: +- val |= 0x10; +- break; +- case 8000000: +- break; +- default: +- dprintk("%s: invalid bandwidth\n", __func__); +- return -EINVAL; +- } +- cx22702_writereg(state, 0x0C, val); +- +- p->code_rate_LP = FEC_AUTO; /* temp hack as manual not working */ +- +- /* use auto configuration? */ +- if ((p->hierarchy == HIERARCHY_AUTO) || +- (p->modulation == QAM_AUTO) || +- (p->code_rate_HP == FEC_AUTO) || +- (p->code_rate_LP == FEC_AUTO) || +- (p->guard_interval == GUARD_INTERVAL_AUTO) || +- (p->transmission_mode == TRANSMISSION_MODE_AUTO)) { +- +- /* TPS Source - use hardware driven values */ +- cx22702_writereg(state, 0x06, 0x10); +- cx22702_writereg(state, 0x07, 0x9); +- cx22702_writereg(state, 0x08, 0xC1); +- cx22702_writereg(state, 0x0B, cx22702_readreg(state, 0x0B) +- & 0xfc); +- cx22702_writereg(state, 0x0C, +- (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); +- cx22702_writereg(state, 0x00, 0x01); /* Begin acquisition */ +- dprintk("%s: Autodetecting\n", __func__); +- return 0; +- } +- +- /* manually programmed values */ +- switch (p->modulation) { /* mask 0x18 */ +- case QPSK: +- val = 0x00; +- break; +- case QAM_16: +- val = 0x08; +- break; +- case QAM_64: +- val = 0x10; +- break; +- default: +- dprintk("%s: invalid modulation\n", __func__); +- return -EINVAL; +- } +- switch (p->hierarchy) { /* mask 0x07 */ +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- val |= 0x01; +- break; +- case HIERARCHY_2: +- val |= 0x02; +- break; +- case HIERARCHY_4: +- val |= 0x03; +- break; +- default: +- dprintk("%s: invalid hierarchy\n", __func__); +- return -EINVAL; +- } +- cx22702_writereg(state, 0x06, val); +- +- switch (p->code_rate_HP) { /* mask 0x38 */ +- case FEC_NONE: +- case FEC_1_2: +- val = 0x00; +- break; +- case FEC_2_3: +- val = 0x08; +- break; +- case FEC_3_4: +- val = 0x10; +- break; +- case FEC_5_6: +- val = 0x18; +- break; +- case FEC_7_8: +- val = 0x20; +- break; +- default: +- dprintk("%s: invalid code_rate_HP\n", __func__); +- return -EINVAL; +- } +- switch (p->code_rate_LP) { /* mask 0x07 */ +- case FEC_NONE: +- case FEC_1_2: +- break; +- case FEC_2_3: +- val |= 0x01; +- break; +- case FEC_3_4: +- val |= 0x02; +- break; +- case FEC_5_6: +- val |= 0x03; +- break; +- case FEC_7_8: +- val |= 0x04; +- break; +- default: +- dprintk("%s: invalid code_rate_LP\n", __func__); +- return -EINVAL; +- } +- cx22702_writereg(state, 0x07, val); +- +- switch (p->guard_interval) { /* mask 0x0c */ +- case GUARD_INTERVAL_1_32: +- val = 0x00; +- break; +- case GUARD_INTERVAL_1_16: +- val = 0x04; +- break; +- case GUARD_INTERVAL_1_8: +- val = 0x08; +- break; +- case GUARD_INTERVAL_1_4: +- val = 0x0c; +- break; +- default: +- dprintk("%s: invalid guard_interval\n", __func__); +- return -EINVAL; +- } +- switch (p->transmission_mode) { /* mask 0x03 */ +- case TRANSMISSION_MODE_2K: +- break; +- case TRANSMISSION_MODE_8K: +- val |= 0x1; +- break; +- default: +- dprintk("%s: invalid transmission_mode\n", __func__); +- return -EINVAL; +- } +- cx22702_writereg(state, 0x08, val); +- cx22702_writereg(state, 0x0B, +- (cx22702_readreg(state, 0x0B) & 0xfc) | 0x02); +- cx22702_writereg(state, 0x0C, +- (cx22702_readreg(state, 0x0C) & 0xBF) | 0x40); +- +- /* Begin channel acquisition */ +- cx22702_writereg(state, 0x00, 0x01); +- +- return 0; +-} +- +-/* Reset the demod hardware and reset all of the configuration registers +- to a default state. */ +-static int cx22702_init(struct dvb_frontend *fe) +-{ +- int i; +- struct cx22702_state *state = fe->demodulator_priv; +- +- cx22702_writereg(state, 0x00, 0x02); +- +- msleep(10); +- +- for (i = 0; i < ARRAY_SIZE(init_tab); i += 2) +- cx22702_writereg(state, init_tab[i], init_tab[i + 1]); +- +- cx22702_writereg(state, 0xf8, (state->config->output_mode << 1) +- & 0x02); +- +- cx22702_i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int cx22702_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- u8 reg0A; +- u8 reg23; +- +- *status = 0; +- +- reg0A = cx22702_readreg(state, 0x0A); +- reg23 = cx22702_readreg(state, 0x23); +- +- dprintk("%s: status demod=0x%02x agc=0x%02x\n" +- , __func__, reg0A, reg23); +- +- if (reg0A & 0x10) { +- *status |= FE_HAS_LOCK; +- *status |= FE_HAS_VITERBI; +- *status |= FE_HAS_SYNC; +- } +- +- if (reg0A & 0x20) +- *status |= FE_HAS_CARRIER; +- +- if (reg23 < 0xf0) +- *status |= FE_HAS_SIGNAL; +- +- return 0; +-} +- +-static int cx22702_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- +- if (cx22702_readreg(state, 0xE4) & 0x02) { +- /* Realtime statistics */ +- *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 +- | (cx22702_readreg(state, 0xDF) & 0x7F); +- } else { +- /* Averagtine statistics */ +- *ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 +- | cx22702_readreg(state, 0xDF); +- } +- +- return 0; +-} +- +-static int cx22702_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- +- u16 rs_ber; +- rs_ber = cx22702_readreg(state, 0x23); +- *signal_strength = (rs_ber << 8) | rs_ber; +- +- return 0; +-} +- +-static int cx22702_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- +- u16 rs_ber; +- if (cx22702_readreg(state, 0xE4) & 0x02) { +- /* Realtime statistics */ +- rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 7 +- | (cx22702_readreg(state, 0xDF) & 0x7F); +- } else { +- /* Averagine statistics */ +- rs_ber = (cx22702_readreg(state, 0xDE) & 0x7F) << 8 +- | cx22702_readreg(state, 0xDF); +- } +- *snr = ~rs_ber; +- +- return 0; +-} +- +-static int cx22702_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- +- u8 _ucblocks; +- +- /* RS Uncorrectable Packet Count then reset */ +- _ucblocks = cx22702_readreg(state, 0xE3); +- if (state->prevUCBlocks < _ucblocks) +- *ucblocks = (_ucblocks - state->prevUCBlocks); +- else +- *ucblocks = state->prevUCBlocks - _ucblocks; +- state->prevUCBlocks = _ucblocks; +- +- return 0; +-} +- +-static int cx22702_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct cx22702_state *state = fe->demodulator_priv; +- +- u8 reg0C = cx22702_readreg(state, 0x0C); +- +- c->inversion = reg0C & 0x1 ? INVERSION_ON : INVERSION_OFF; +- return cx22702_get_tps(state, c); +-} +- +-static int cx22702_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void cx22702_release(struct dvb_frontend *fe) +-{ +- struct cx22702_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static const struct dvb_frontend_ops cx22702_ops; +- +-struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, +- struct i2c_adapter *i2c) +-{ +- struct cx22702_state *state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if (cx22702_readreg(state, 0x1f) != 0x3) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &cx22702_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(cx22702_attach); +- +-static const struct dvb_frontend_ops cx22702_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Conexant CX22702 DVB-T", +- .frequency_min = 177000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER +- }, +- +- .release = cx22702_release, +- +- .init = cx22702_init, +- .i2c_gate_ctrl = cx22702_i2c_gate_ctrl, +- +- .set_frontend = cx22702_set_tps, +- .get_frontend = cx22702_get_frontend, +- .get_tune_settings = cx22702_get_tune_settings, +- +- .read_status = cx22702_read_status, +- .read_ber = cx22702_read_ber, +- .read_signal_strength = cx22702_read_signal_strength, +- .read_snr = cx22702_read_snr, +- .read_ucblocks = cx22702_read_ucblocks, +-}; +- +-MODULE_DESCRIPTION("Conexant CX22702 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h +deleted file mode 100644 +index f154e1f..0000000 +--- a/drivers/media/dvb/frontends/cx22702.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- Conexant 22702 DVB OFDM demodulator driver +- +- based on: +- Alps TDMB7 DVB OFDM demodulator driver +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- Holger Waechtler +- +- Copyright (C) 2004 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef CX22702_H +-#define CX22702_H +- +-#include +- +-struct cx22702_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* serial/parallel output */ +-#define CX22702_PARALLEL_OUTPUT 0 +-#define CX22702_SERIAL_OUTPUT 1 +- u8 output_mode; +-}; +- +-#if defined(CONFIG_DVB_CX22702) || (defined(CONFIG_DVB_CX22702_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *cx22702_attach( +- const struct cx22702_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *cx22702_attach( +- const struct cx22702_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c +deleted file mode 100644 +index 5101f10..0000000 +--- a/drivers/media/dvb/frontends/cx24110.c ++++ /dev/null +@@ -1,667 +0,0 @@ +-/* +- cx24110 - Single Chip Satellite Channel Receiver driver module +- +- Copyright (C) 2002 Peter Hettkamp based on +- work +- Copyright (C) 1999 Convergence Integrated Media GmbH +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "cx24110.h" +- +- +-struct cx24110_state { +- +- struct i2c_adapter* i2c; +- +- const struct cx24110_config* config; +- +- struct dvb_frontend frontend; +- +- u32 lastber; +- u32 lastbler; +- u32 lastesn0; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "cx24110: " args); \ +- } while (0) +- +-static struct {u8 reg; u8 data;} cx24110_regdata[]= +- /* Comments beginning with @ denote this value should +- be the default */ +- {{0x09,0x01}, /* SoftResetAll */ +- {0x09,0x00}, /* release reset */ +- {0x01,0xe8}, /* MSB of code rate 27.5MS/s */ +- {0x02,0x17}, /* middle byte " */ +- {0x03,0x29}, /* LSB " */ +- {0x05,0x03}, /* @ DVB mode, standard code rate 3/4 */ +- {0x06,0xa5}, /* @ PLL 60MHz */ +- {0x07,0x01}, /* @ Fclk, i.e. sampling clock, 60MHz */ +- {0x0a,0x00}, /* @ partial chip disables, do not set */ +- {0x0b,0x01}, /* set output clock in gapped mode, start signal low +- active for first byte */ +- {0x0c,0x11}, /* no parity bytes, large hold time, serial data out */ +- {0x0d,0x6f}, /* @ RS Sync/Unsync thresholds */ +- {0x10,0x40}, /* chip doc is misleading here: write bit 6 as 1 +- to avoid starting the BER counter. Reset the +- CRC test bit. Finite counting selected */ +- {0x15,0xff}, /* @ size of the limited time window for RS BER +- estimation. It is *256 RS blocks, this +- gives approx. 2.6 sec at 27.5MS/s, rate 3/4 */ +- {0x16,0x00}, /* @ enable all RS output ports */ +- {0x17,0x04}, /* @ time window allowed for the RS to sync */ +- {0x18,0xae}, /* @ allow all standard DVB code rates to be scanned +- for automatically */ +- /* leave the current code rate and normalization +- registers as they are after reset... */ +- {0x21,0x10}, /* @ during AutoAcq, search each viterbi setting +- only once */ +- {0x23,0x18}, /* @ size of the limited time window for Viterbi BER +- estimation. It is *65536 channel bits, i.e. +- approx. 38ms at 27.5MS/s, rate 3/4 */ +- {0x24,0x24}, /* do not trigger Viterbi CRC test. Finite count window */ +- /* leave front-end AGC parameters at default values */ +- /* leave decimation AGC parameters at default values */ +- {0x35,0x40}, /* disable all interrupts. They are not connected anyway */ +- {0x36,0xff}, /* clear all interrupt pending flags */ +- {0x37,0x00}, /* @ fully enable AutoAcqq state machine */ +- {0x38,0x07}, /* @ enable fade recovery, but not autostart AutoAcq */ +- /* leave the equalizer parameters on their default values */ +- /* leave the final AGC parameters on their default values */ +- {0x41,0x00}, /* @ MSB of front-end derotator frequency */ +- {0x42,0x00}, /* @ middle bytes " */ +- {0x43,0x00}, /* @ LSB " */ +- /* leave the carrier tracking loop parameters on default */ +- /* leave the bit timing loop parameters at default */ +- {0x56,0x4d}, /* set the filtune voltage to 2.7V, as recommended by */ +- /* the cx24108 data sheet for symbol rates above 15MS/s */ +- {0x57,0x00}, /* @ Filter sigma delta enabled, positive */ +- {0x61,0x95}, /* GPIO pins 1-4 have special function */ +- {0x62,0x05}, /* GPIO pin 5 has special function, pin 6 is GPIO */ +- {0x63,0x00}, /* All GPIO pins use CMOS output characteristics */ +- {0x64,0x20}, /* GPIO 6 is input, all others are outputs */ +- {0x6d,0x30}, /* tuner auto mode clock freq 62kHz */ +- {0x70,0x15}, /* use auto mode, tuner word is 21 bits long */ +- {0x73,0x00}, /* @ disable several demod bypasses */ +- {0x74,0x00}, /* @ " */ +- {0x75,0x00} /* @ " */ +- /* the remaining registers are for SEC */ +- }; +- +- +-static int cx24110_writereg (struct cx24110_state* state, int reg, int data) +-{ +- u8 buf [] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- int err; +- +- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { +- dprintk ("%s: writereg error (err == %i, reg == 0x%02x," +- " data == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int cx24110_readreg (struct cx24110_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) return ret; +- +- return b1[0]; +-} +- +-static int cx24110_set_inversion (struct cx24110_state* state, fe_spectral_inversion_t inversion) +-{ +-/* fixme (low): error handling */ +- +- switch (inversion) { +- case INVERSION_OFF: +- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); +- /* AcqSpectrInvDis on. No idea why someone should want this */ +- cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)&0xf7); +- /* Initial value 0 at start of acq */ +- cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)&0xef); +- /* current value 0 */ +- /* The cx24110 manual tells us this reg is read-only. +- But what the heck... set it ayways */ +- break; +- case INVERSION_ON: +- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x1); +- /* AcqSpectrInvDis on. No idea why someone should want this */ +- cx24110_writereg(state,0x5,cx24110_readreg(state,0x5)|0x08); +- /* Initial value 1 at start of acq */ +- cx24110_writereg(state,0x22,cx24110_readreg(state,0x22)|0x10); +- /* current value 1 */ +- break; +- case INVERSION_AUTO: +- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xfe); +- /* AcqSpectrInvDis off. Leave initial & current states as is */ +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int cx24110_set_fec (struct cx24110_state* state, fe_code_rate_t fec) +-{ +-/* fixme (low): error handling */ +- +- static const int rate[]={-1,1,2,3,5,7,-1}; +- static const int g1[]={-1,0x01,0x02,0x05,0x15,0x45,-1}; +- static const int g2[]={-1,0x01,0x03,0x06,0x1a,0x7a,-1}; +- +- /* Well, the AutoAcq engine of the cx24106 and 24110 automatically +- searches all enabled viterbi rates, and can handle non-standard +- rates as well. */ +- +- if (fec>FEC_AUTO) +- fec=FEC_AUTO; +- +- if (fec==FEC_AUTO) { /* (re-)establish AutoAcq behaviour */ +- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)&0xdf); +- /* clear AcqVitDis bit */ +- cx24110_writereg(state,0x18,0xae); +- /* allow all DVB standard code rates */ +- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|0x3); +- /* set nominal Viterbi rate 3/4 */ +- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|0x3); +- /* set current Viterbi rate 3/4 */ +- cx24110_writereg(state,0x1a,0x05); cx24110_writereg(state,0x1b,0x06); +- /* set the puncture registers for code rate 3/4 */ +- return 0; +- } else { +- cx24110_writereg(state,0x37,cx24110_readreg(state,0x37)|0x20); +- /* set AcqVitDis bit */ +- if(rate[fec]>0) { +- cx24110_writereg(state,0x05,(cx24110_readreg(state,0x05)&0xf0)|rate[fec]); +- /* set nominal Viterbi rate */ +- cx24110_writereg(state,0x22,(cx24110_readreg(state,0x22)&0xf0)|rate[fec]); +- /* set current Viterbi rate */ +- cx24110_writereg(state,0x1a,g1[fec]); +- cx24110_writereg(state,0x1b,g2[fec]); +- /* not sure if this is the right way: I always used AutoAcq mode */ +- } else +- return -EOPNOTSUPP; +-/* fixme (low): which is the correct return code? */ +- }; +- return 0; +-} +- +-static fe_code_rate_t cx24110_get_fec (struct cx24110_state* state) +-{ +- int i; +- +- i=cx24110_readreg(state,0x22)&0x0f; +- if(!(i&0x08)) { +- return FEC_1_2 + i - 1; +- } else { +-/* fixme (low): a special code rate has been selected. In theory, we need to +- return a denominator value, a numerator value, and a pair of puncture +- maps to correctly describe this mode. But this should never happen in +- practice, because it cannot be set by cx24110_get_fec. */ +- return FEC_NONE; +- } +-} +- +-static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) +-{ +-/* fixme (low): add error handling */ +- u32 ratio; +- u32 tmp, fclk, BDRI; +- +- static const u32 bands[]={5000000UL,15000000UL,90999000UL/2}; +- int i; +- +- dprintk("cx24110 debug: entering %s(%d)\n",__func__,srate); +- if (srate>90999000UL/2) +- srate=90999000UL/2; +- if (srate<500000) +- srate=500000; +- +- for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++) +- ; +- /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, +- and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, +- R06[3:0] PLLphaseDetGain */ +- tmp=cx24110_readreg(state,0x07)&0xfc; +- if(srate<90999000UL/4) { /* sample rate 45MHz*/ +- cx24110_writereg(state,0x07,tmp); +- cx24110_writereg(state,0x06,0x78); +- fclk=90999000UL/2; +- } else if(srate<60666000UL/2) { /* sample rate 60MHz */ +- cx24110_writereg(state,0x07,tmp|0x1); +- cx24110_writereg(state,0x06,0xa5); +- fclk=60666000UL; +- } else if(srate<80888000UL/2) { /* sample rate 80MHz */ +- cx24110_writereg(state,0x07,tmp|0x2); +- cx24110_writereg(state,0x06,0x87); +- fclk=80888000UL; +- } else { /* sample rate 90MHz */ +- cx24110_writereg(state,0x07,tmp|0x3); +- cx24110_writereg(state,0x06,0x78); +- fclk=90999000UL; +- }; +- dprintk("cx24110 debug: fclk %d Hz\n",fclk); +- /* we need to divide two integers with approx. 27 bits in 32 bit +- arithmetic giving a 25 bit result */ +- /* the maximum dividend is 90999000/2, 0x02b6446c, this number is +- also the most complex divisor. Hence, the dividend has, +- assuming 32bit unsigned arithmetic, 6 clear bits on top, the +- divisor 2 unused bits at the bottom. Also, the quotient is +- always less than 1/2. Borrowed from VES1893.c, of course */ +- +- tmp=srate<<6; +- BDRI=fclk>>2; +- ratio=(tmp/BDRI); +- +- tmp=(tmp%BDRI)<<8; +- ratio=(ratio<<8)+(tmp/BDRI); +- +- tmp=(tmp%BDRI)<<8; +- ratio=(ratio<<8)+(tmp/BDRI); +- +- tmp=(tmp%BDRI)<<1; +- ratio=(ratio<<1)+(tmp/BDRI); +- +- dprintk("srate= %d (range %d, up to %d)\n", srate,i,bands[i]); +- dprintk("fclk = %d\n", fclk); +- dprintk("ratio= %08x\n", ratio); +- +- cx24110_writereg(state, 0x1, (ratio>>16)&0xff); +- cx24110_writereg(state, 0x2, (ratio>>8)&0xff); +- cx24110_writereg(state, 0x3, (ratio)&0xff); +- +- return 0; +- +-} +- +-static int _cx24110_pll_write (struct dvb_frontend* fe, const u8 buf[], int len) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- if (len != 3) +- return -EINVAL; +- +-/* tuner data is 21 bits long, must be left-aligned in data */ +-/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */ +-/* FIXME (low): add error handling, avoid infinite loops if HW fails... */ +- +- cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */ +- cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */ +- +- /* if the auto tuner writer is still busy, clear it out */ +- while (cx24110_readreg(state,0x6d)&0x80) +- cx24110_writereg(state,0x72,0); +- +- /* write the topmost 8 bits */ +- cx24110_writereg(state,0x72,buf[0]); +- +- /* wait for the send to be completed */ +- while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) +- ; +- +- /* send another 8 bytes */ +- cx24110_writereg(state,0x72,buf[1]); +- while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) +- ; +- +- /* and the topmost 5 bits of this byte */ +- cx24110_writereg(state,0x72,buf[2]); +- while ((cx24110_readreg(state,0x6d)&0xc0)==0x80) +- ; +- +- /* now strobe the enable line once */ +- cx24110_writereg(state,0x6d,0x32); +- cx24110_writereg(state,0x6d,0x30); +- +- return 0; +-} +- +-static int cx24110_initfe(struct dvb_frontend* fe) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +-/* fixme (low): error handling */ +- int i; +- +- dprintk("%s: init chip\n", __func__); +- +- for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { +- cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); +- }; +- +- return 0; +-} +- +-static int cx24110_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0xc0); +- case SEC_VOLTAGE_18: +- return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&0x3b)|0x40); +- default: +- return -EINVAL; +- }; +-} +- +-static int cx24110_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +-{ +- int rv, bit; +- struct cx24110_state *state = fe->demodulator_priv; +- unsigned long timeout; +- +- if (burst == SEC_MINI_A) +- bit = 0x00; +- else if (burst == SEC_MINI_B) +- bit = 0x08; +- else +- return -EINVAL; +- +- rv = cx24110_readreg(state, 0x77); +- if (!(rv & 0x04)) +- cx24110_writereg(state, 0x77, rv | 0x04); +- +- rv = cx24110_readreg(state, 0x76); +- cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40 | bit)); +- timeout = jiffies + msecs_to_jiffies(100); +- while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) +- ; /* wait for LNB ready */ +- +- return 0; +-} +- +-static int cx24110_send_diseqc_msg(struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd *cmd) +-{ +- int i, rv; +- struct cx24110_state *state = fe->demodulator_priv; +- unsigned long timeout; +- +- if (cmd->msg_len < 3 || cmd->msg_len > 6) +- return -EINVAL; /* not implemented */ +- +- for (i = 0; i < cmd->msg_len; i++) +- cx24110_writereg(state, 0x79 + i, cmd->msg[i]); +- +- rv = cx24110_readreg(state, 0x77); +- if (rv & 0x04) { +- cx24110_writereg(state, 0x77, rv & ~0x04); +- msleep(30); /* reportedly fixes switching problems */ +- } +- +- rv = cx24110_readreg(state, 0x76); +- +- cx24110_writereg(state, 0x76, ((rv & 0x90) | 0x40) | ((cmd->msg_len-3) & 3)); +- timeout = jiffies + msecs_to_jiffies(100); +- while (!time_after(jiffies, timeout) && !(cx24110_readreg(state, 0x76) & 0x40)) +- ; /* wait for LNB ready */ +- +- return 0; +-} +- +-static int cx24110_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- int sync = cx24110_readreg (state, 0x55); +- +- *status = 0; +- +- if (sync & 0x10) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 0x08) +- *status |= FE_HAS_CARRIER; +- +- sync = cx24110_readreg (state, 0x08); +- +- if (sync & 0x40) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 0x20) +- *status |= FE_HAS_SYNC; +- +- if ((sync & 0x60) == 0x60) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int cx24110_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- /* fixme (maybe): value range is 16 bit. Scale? */ +- if(cx24110_readreg(state,0x24)&0x10) { +- /* the Viterbi error counter has finished one counting window */ +- cx24110_writereg(state,0x24,0x04); /* select the ber reg */ +- state->lastber=cx24110_readreg(state,0x25)| +- (cx24110_readreg(state,0x26)<<8); +- cx24110_writereg(state,0x24,0x04); /* start new count window */ +- cx24110_writereg(state,0x24,0x14); +- } +- *ber = state->lastber; +- +- return 0; +-} +- +-static int cx24110_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +-/* no provision in hardware. Read the frontend AGC accumulator. No idea how to scale this, but I know it is 2s complement */ +- u8 signal = cx24110_readreg (state, 0x27)+128; +- *signal_strength = (signal << 8) | signal; +- +- return 0; +-} +- +-static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- /* no provision in hardware. Can be computed from the Es/N0 estimator, but I don't know how. */ +- if(cx24110_readreg(state,0x6a)&0x80) { +- /* the Es/N0 error counter has finished one counting window */ +- state->lastesn0=cx24110_readreg(state,0x69)| +- (cx24110_readreg(state,0x68)<<8); +- cx24110_writereg(state,0x6a,0x84); /* start new count window */ +- } +- *snr = state->lastesn0; +- +- return 0; +-} +- +-static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- u32 lastbyer; +- +- if(cx24110_readreg(state,0x10)&0x40) { +- /* the RS error counter has finished one counting window */ +- cx24110_writereg(state,0x10,0x60); /* select the byer reg */ +- lastbyer=cx24110_readreg(state,0x12)| +- (cx24110_readreg(state,0x13)<<8)| +- (cx24110_readreg(state,0x14)<<16); +- cx24110_writereg(state,0x10,0x70); /* select the bler reg */ +- state->lastbler=cx24110_readreg(state,0x12)| +- (cx24110_readreg(state,0x13)<<8)| +- (cx24110_readreg(state,0x14)<<16); +- cx24110_writereg(state,0x10,0x20); /* start new count window */ +- } +- *ucblocks = state->lastbler; +- +- return 0; +-} +- +-static int cx24110_set_frontend(struct dvb_frontend *fe) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- cx24110_set_inversion(state, p->inversion); +- cx24110_set_fec(state, p->fec_inner); +- cx24110_set_symbolrate(state, p->symbol_rate); +- cx24110_writereg(state,0x04,0x05); /* start acquisition */ +- +- return 0; +-} +- +-static int cx24110_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct cx24110_state *state = fe->demodulator_priv; +- s32 afc; unsigned sclk; +- +-/* cannot read back tuner settings (freq). Need to have some private storage */ +- +- sclk = cx24110_readreg (state, 0x07) & 0x03; +-/* ok, real AFC (FEDR) freq. is afc/2^24*fsamp, fsamp=45/60/80/90MHz. +- * Need 64 bit arithmetic. Is thiss possible in the kernel? */ +- if (sclk==0) sclk=90999000L/2L; +- else if (sclk==1) sclk=60666000L; +- else if (sclk==2) sclk=80888000L; +- else sclk=90999000L; +- sclk>>=8; +- afc = sclk*(cx24110_readreg (state, 0x44)&0x1f)+ +- ((sclk*cx24110_readreg (state, 0x45))>>8)+ +- ((sclk*cx24110_readreg (state, 0x46))>>16); +- +- p->frequency += afc; +- p->inversion = (cx24110_readreg (state, 0x22) & 0x10) ? +- INVERSION_ON : INVERSION_OFF; +- p->fec_inner = cx24110_get_fec(state); +- +- return 0; +-} +- +-static int cx24110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct cx24110_state *state = fe->demodulator_priv; +- +- return cx24110_writereg(state,0x76,(cx24110_readreg(state,0x76)&~0x10)|(((tone==SEC_TONE_ON))?0x10:0)); +-} +- +-static void cx24110_release(struct dvb_frontend* fe) +-{ +- struct cx24110_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops cx24110_ops; +- +-struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, +- struct i2c_adapter* i2c) +-{ +- struct cx24110_state* state = NULL; +- int ret; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->lastber = 0; +- state->lastbler = 0; +- state->lastesn0 = 0; +- +- /* check if the demod is there */ +- ret = cx24110_readreg(state, 0x00); +- if ((ret != 0x5a) && (ret != 0x69)) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &cx24110_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops cx24110_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Conexant CX24110 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1011, /* kHz for QPSK frontends */ +- .frequency_tolerance = 29500, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .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_RECOVER +- }, +- +- .release = cx24110_release, +- +- .init = cx24110_initfe, +- .write = _cx24110_pll_write, +- .set_frontend = cx24110_set_frontend, +- .get_frontend = cx24110_get_frontend, +- .read_status = cx24110_read_status, +- .read_ber = cx24110_read_ber, +- .read_signal_strength = cx24110_read_signal_strength, +- .read_snr = cx24110_read_snr, +- .read_ucblocks = cx24110_read_ucblocks, +- +- .diseqc_send_master_cmd = cx24110_send_diseqc_msg, +- .set_tone = cx24110_set_tone, +- .set_voltage = cx24110_set_voltage, +- .diseqc_send_burst = cx24110_diseqc_send_burst, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); +-MODULE_AUTHOR("Peter Hettkamp"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(cx24110_attach); +diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h +deleted file mode 100644 +index fdcceee..0000000 +--- a/drivers/media/dvb/frontends/cx24110.h ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- cx24110 - Single Chip Satellite Channel Receiver driver module +- +- Copyright (C) 2002 Peter Hettkamp based on +- work +- Copyright (C) 1999 Convergence Integrated Media GmbH +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef CX24110_H +-#define CX24110_H +- +-#include +- +-struct cx24110_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) +-{ +- u8 buf[] = { +- (u8)((val >> 24) & 0xff), +- (u8)((val >> 16) & 0xff), +- (u8)((val >> 8) & 0xff) +- }; +- +- if (fe->ops.write) +- return fe->ops.write(fe, buf, 3); +- return 0; +-} +- +-#if defined(CONFIG_DVB_CX24110) || (defined(CONFIG_DVB_CX24110_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_CX24110 +- +-#endif // CX24110_H +diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c +deleted file mode 100644 +index 3883c3b..0000000 +--- a/drivers/media/dvb/frontends/cx24113.c ++++ /dev/null +@@ -1,618 +0,0 @@ +-/* +- * Driver for Conexant CX24113/CX24128 Tuner (Satellite) +- * +- * Copyright (C) 2007-8 Patrick Boettcher +- * +- * Developed for BBTI / Technisat +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "cx24113.h" +- +-static int debug; +- +-#define cx_info(args...) do { printk(KERN_INFO "CX24113: " args); } while (0) +-#define cx_err(args...) do { printk(KERN_ERR "CX24113: " args); } while (0) +- +-#define dprintk(args...) \ +- do { \ +- if (debug) { \ +- printk(KERN_DEBUG "CX24113: %s: ", __func__); \ +- printk(args); \ +- } \ +- } while (0) +- +-struct cx24113_state { +- struct i2c_adapter *i2c; +- const struct cx24113_config *config; +- +-#define REV_CX24113 0x23 +- u8 rev; +- u8 ver; +- +- u8 icp_mode:1; +- +-#define ICP_LEVEL1 0 +-#define ICP_LEVEL2 1 +-#define ICP_LEVEL3 2 +-#define ICP_LEVEL4 3 +- u8 icp_man:2; +- u8 icp_auto_low:2; +- u8 icp_auto_mlow:2; +- u8 icp_auto_mhi:2; +- u8 icp_auto_hi:2; +- u8 icp_dig; +- +-#define LNA_MIN_GAIN 0 +-#define LNA_MID_GAIN 1 +-#define LNA_MAX_GAIN 2 +- u8 lna_gain:2; +- +- u8 acp_on:1; +- +- u8 vco_mode:2; +- u8 vco_shift:1; +-#define VCOBANDSEL_6 0x80 +-#define VCOBANDSEL_5 0x01 +-#define VCOBANDSEL_4 0x02 +-#define VCOBANDSEL_3 0x04 +-#define VCOBANDSEL_2 0x08 +-#define VCOBANDSEL_1 0x10 +- u8 vco_band; +- +-#define VCODIV4 4 +-#define VCODIV2 2 +- u8 vcodiv; +- +- u8 bs_delay:4; +- u16 bs_freqcnt:13; +- u16 bs_rdiv; +- u8 prescaler_mode:1; +- +- u8 rfvga_bias_ctrl; +- +- s16 tuner_gain_thres; +- u8 gain_level; +- +- u32 frequency; +- +- u8 refdiv; +- +- u8 Fwindow_enabled; +-}; +- +-static int cx24113_writereg(struct cx24113_state *state, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->i2c_addr, +- .flags = 0, .buf = buf, .len = 2 }; +- int err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk(KERN_DEBUG "%s: writereg error(err == %i, reg == 0x%02x," +- " data == 0x%02x)\n", __func__, err, reg, data); +- return err; +- } +- +- return 0; +-} +- +-static int cx24113_readreg(struct cx24113_state *state, u8 reg) +-{ +- int ret; +- u8 b; +- struct i2c_msg msg[] = { +- { .addr = state->config->i2c_addr, +- .flags = 0, .buf = ®, .len = 1 }, +- { .addr = state->config->i2c_addr, +- .flags = I2C_M_RD, .buf = &b, .len = 1 } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk(KERN_DEBUG "%s: reg=0x%x (error=%d)\n", +- __func__, reg, ret); +- return ret; +- } +- +- return b; +-} +- +-static void cx24113_set_parameters(struct cx24113_state *state) +-{ +- u8 r; +- +- r = cx24113_readreg(state, 0x10) & 0x82; +- r |= state->icp_mode; +- r |= state->icp_man << 4; +- r |= state->icp_dig << 2; +- r |= state->prescaler_mode << 5; +- cx24113_writereg(state, 0x10, r); +- +- r = (state->icp_auto_low << 0) | (state->icp_auto_mlow << 2) +- | (state->icp_auto_mhi << 4) | (state->icp_auto_hi << 6); +- cx24113_writereg(state, 0x11, r); +- +- if (state->rev == REV_CX24113) { +- r = cx24113_readreg(state, 0x20) & 0xec; +- r |= state->lna_gain; +- r |= state->rfvga_bias_ctrl << 4; +- cx24113_writereg(state, 0x20, r); +- } +- +- r = cx24113_readreg(state, 0x12) & 0x03; +- r |= state->acp_on << 2; +- r |= state->bs_delay << 4; +- cx24113_writereg(state, 0x12, r); +- +- r = cx24113_readreg(state, 0x18) & 0x40; +- r |= state->vco_shift; +- if (state->vco_band == VCOBANDSEL_6) +- r |= (1 << 7); +- else +- r |= (state->vco_band << 1); +- cx24113_writereg(state, 0x18, r); +- +- r = cx24113_readreg(state, 0x14) & 0x20; +- r |= (state->vco_mode << 6) | ((state->bs_freqcnt >> 8) & 0x1f); +- cx24113_writereg(state, 0x14, r); +- cx24113_writereg(state, 0x15, (state->bs_freqcnt & 0xff)); +- +- cx24113_writereg(state, 0x16, (state->bs_rdiv >> 4) & 0xff); +- r = (cx24113_readreg(state, 0x17) & 0x0f) | +- ((state->bs_rdiv & 0x0f) << 4); +- cx24113_writereg(state, 0x17, r); +-} +- +-#define VGA_0 0x00 +-#define VGA_1 0x04 +-#define VGA_2 0x02 +-#define VGA_3 0x06 +-#define VGA_4 0x01 +-#define VGA_5 0x05 +-#define VGA_6 0x03 +-#define VGA_7 0x07 +- +-#define RFVGA_0 0x00 +-#define RFVGA_1 0x01 +-#define RFVGA_2 0x02 +-#define RFVGA_3 0x03 +- +-static int cx24113_set_gain_settings(struct cx24113_state *state, +- s16 power_estimation) +-{ +- u8 ampout = cx24113_readreg(state, 0x1d) & 0xf0, +- vga = cx24113_readreg(state, 0x1f) & 0x3f, +- rfvga = cx24113_readreg(state, 0x20) & 0xf3; +- u8 gain_level = power_estimation >= state->tuner_gain_thres; +- +- dprintk("power estimation: %d, thres: %d, gain_level: %d/%d\n", +- power_estimation, state->tuner_gain_thres, +- state->gain_level, gain_level); +- +- if (gain_level == state->gain_level) +- return 0; /* nothing to be done */ +- +- ampout |= 0xf; +- +- if (gain_level) { +- rfvga |= RFVGA_0 << 2; +- vga |= (VGA_7 << 3) | VGA_7; +- } else { +- rfvga |= RFVGA_2 << 2; +- vga |= (VGA_6 << 3) | VGA_2; +- } +- state->gain_level = gain_level; +- +- cx24113_writereg(state, 0x1d, ampout); +- cx24113_writereg(state, 0x1f, vga); +- cx24113_writereg(state, 0x20, rfvga); +- +- return 1; /* did something */ +-} +- +-static int cx24113_set_Fref(struct cx24113_state *state, u8 high) +-{ +- u8 xtal = cx24113_readreg(state, 0x02); +- if (state->rev == 0x43 && state->vcodiv == VCODIV4) +- high = 1; +- +- xtal &= ~0x2; +- if (high) +- xtal |= high << 1; +- return cx24113_writereg(state, 0x02, xtal); +-} +- +-static int cx24113_enable(struct cx24113_state *state, u8 enable) +-{ +- u8 r21 = (cx24113_readreg(state, 0x21) & 0xc0) | enable; +- if (state->rev == REV_CX24113) +- r21 |= (1 << 1); +- return cx24113_writereg(state, 0x21, r21); +-} +- +-static int cx24113_set_bandwidth(struct cx24113_state *state, u32 bandwidth_khz) +-{ +- u8 r; +- +- if (bandwidth_khz <= 19000) +- r = 0x03 << 6; +- else if (bandwidth_khz <= 25000) +- r = 0x02 << 6; +- else +- r = 0x01 << 6; +- +- dprintk("bandwidth to be set: %d\n", bandwidth_khz); +- bandwidth_khz *= 10; +- bandwidth_khz -= 10000; +- bandwidth_khz /= 1000; +- bandwidth_khz += 5; +- bandwidth_khz /= 10; +- +- dprintk("bandwidth: %d %d\n", r >> 6, bandwidth_khz); +- +- r |= bandwidth_khz & 0x3f; +- +- return cx24113_writereg(state, 0x1e, r); +-} +- +-static int cx24113_set_clk_inversion(struct cx24113_state *state, u8 on) +-{ +- u8 r = (cx24113_readreg(state, 0x10) & 0x7f) | ((on & 0x1) << 7); +- return cx24113_writereg(state, 0x10, r); +-} +- +-static int cx24113_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct cx24113_state *state = fe->tuner_priv; +- u8 r = (cx24113_readreg(state, 0x10) & 0x02) >> 1; +- if (r) +- *status |= TUNER_STATUS_LOCKED; +- dprintk("PLL locked: %d\n", r); +- return 0; +-} +- +-static u8 cx24113_set_ref_div(struct cx24113_state *state, u8 refdiv) +-{ +- if (state->rev == 0x43 && state->vcodiv == VCODIV4) +- refdiv = 2; +- return state->refdiv = refdiv; +-} +- +-static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f) +-{ +- s32 N; +- s64 F; +- u64 dividend; +- u8 R, r; +- u8 vcodiv; +- u8 factor; +- s32 freq_hz = state->frequency * 1000; +- +- if (state->config->xtal_khz < 20000) +- factor = 1; +- else +- factor = 2; +- +- if (state->rev == REV_CX24113) { +- if (state->frequency >= 1100000) +- vcodiv = VCODIV2; +- else +- vcodiv = VCODIV4; +- } else { +- if (state->frequency >= 1165000) +- vcodiv = VCODIV2; +- else +- vcodiv = VCODIV4; +- } +- state->vcodiv = vcodiv; +- +- dprintk("calculating N/F for %dHz with vcodiv %d\n", freq_hz, vcodiv); +- R = 0; +- do { +- R = cx24113_set_ref_div(state, R + 1); +- +- /* calculate tuner PLL settings: */ +- N = (freq_hz / 100 * vcodiv) * R; +- N /= (state->config->xtal_khz) * factor * 2; +- N += 5; /* For round up. */ +- N /= 10; +- N -= 32; +- } while (N < 6 && R < 3); +- +- if (N < 6) { +- cx_err("strange frequency: N < 6\n"); +- return; +- } +- F = freq_hz; +- F *= (u64) (R * vcodiv * 262144); +- dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R); +- /* do_div needs an u64 as first argument */ +- dividend = F; +- do_div(dividend, state->config->xtal_khz * 1000 * factor * 2); +- F = dividend; +- dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R); +- F -= (N + 32) * 262144; +- +- dprintk("3 N: %d, F: %lld, R: %d\n", N, (long long)F, R); +- +- if (state->Fwindow_enabled) { +- if (F > (262144 / 2 - 1638)) +- F = 262144 / 2 - 1638; +- if (F < (-262144 / 2 + 1638)) +- F = -262144 / 2 + 1638; +- if ((F < 3277 && F > 0) || (F > -3277 && F < 0)) { +- F = 0; +- r = cx24113_readreg(state, 0x10); +- cx24113_writereg(state, 0x10, r | (1 << 6)); +- } +- } +- dprintk("4 N: %d, F: %lld, R: %d\n", N, (long long)F, R); +- +- *n = (u16) N; +- *f = (s32) F; +-} +- +- +-static void cx24113_set_nfr(struct cx24113_state *state, u16 n, s32 f, u8 r) +-{ +- u8 reg; +- cx24113_writereg(state, 0x19, (n >> 1) & 0xff); +- +- reg = ((n & 0x1) << 7) | ((f >> 11) & 0x7f); +- cx24113_writereg(state, 0x1a, reg); +- +- cx24113_writereg(state, 0x1b, (f >> 3) & 0xff); +- +- reg = cx24113_readreg(state, 0x1c) & 0x1f; +- cx24113_writereg(state, 0x1c, reg | ((f & 0x7) << 5)); +- +- cx24113_set_Fref(state, r - 1); +-} +- +-static int cx24113_set_frequency(struct cx24113_state *state, u32 frequency) +-{ +- u8 r = 1; /* or 2 */ +- u16 n = 6; +- s32 f = 0; +- +- r = cx24113_readreg(state, 0x14); +- cx24113_writereg(state, 0x14, r & 0x3f); +- +- r = cx24113_readreg(state, 0x10); +- cx24113_writereg(state, 0x10, r & 0xbf); +- +- state->frequency = frequency; +- +- dprintk("tuning to frequency: %d\n", frequency); +- +- cx24113_calc_pll_nf(state, &n, &f); +- cx24113_set_nfr(state, n, f, state->refdiv); +- +- r = cx24113_readreg(state, 0x18) & 0xbf; +- if (state->vcodiv != VCODIV2) +- r |= 1 << 6; +- cx24113_writereg(state, 0x18, r); +- +- /* The need for this sleep is not clear. But helps in some cases */ +- msleep(5); +- +- r = cx24113_readreg(state, 0x1c) & 0xef; +- cx24113_writereg(state, 0x1c, r | (1 << 4)); +- return 0; +-} +- +-static int cx24113_init(struct dvb_frontend *fe) +-{ +- struct cx24113_state *state = fe->tuner_priv; +- int ret; +- +- state->tuner_gain_thres = -50; +- state->gain_level = 255; /* to force a gain-setting initialization */ +- state->icp_mode = 0; +- +- if (state->config->xtal_khz < 11000) { +- state->icp_auto_hi = ICP_LEVEL4; +- state->icp_auto_mhi = ICP_LEVEL4; +- state->icp_auto_mlow = ICP_LEVEL3; +- state->icp_auto_low = ICP_LEVEL3; +- } else { +- state->icp_auto_hi = ICP_LEVEL4; +- state->icp_auto_mhi = ICP_LEVEL4; +- state->icp_auto_mlow = ICP_LEVEL3; +- state->icp_auto_low = ICP_LEVEL2; +- } +- +- state->icp_dig = ICP_LEVEL3; +- state->icp_man = ICP_LEVEL1; +- state->acp_on = 1; +- state->vco_mode = 0; +- state->vco_shift = 0; +- state->vco_band = VCOBANDSEL_1; +- state->bs_delay = 8; +- state->bs_freqcnt = 0x0fff; +- state->bs_rdiv = 0x0fff; +- state->prescaler_mode = 0; +- state->lna_gain = LNA_MAX_GAIN; +- state->rfvga_bias_ctrl = 1; +- state->Fwindow_enabled = 1; +- +- cx24113_set_Fref(state, 0); +- cx24113_enable(state, 0x3d); +- cx24113_set_parameters(state); +- +- cx24113_set_gain_settings(state, -30); +- +- cx24113_set_bandwidth(state, 18025); +- cx24113_set_clk_inversion(state, 1); +- +- if (state->config->xtal_khz >= 40000) +- ret = cx24113_writereg(state, 0x02, +- (cx24113_readreg(state, 0x02) & 0xfb) | (1 << 2)); +- else +- ret = cx24113_writereg(state, 0x02, +- (cx24113_readreg(state, 0x02) & 0xfb) | (0 << 2)); +- +- return ret; +-} +- +-static int cx24113_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct cx24113_state *state = fe->tuner_priv; +- /* for a ROLL-OFF factor of 0.35, 0.2: 600, 0.25: 625 */ +- u32 roll_off = 675; +- u32 bw; +- +- bw = ((c->symbol_rate/100) * roll_off) / 1000; +- bw += (10000000/100) + 5; +- bw /= 10; +- bw += 1000; +- cx24113_set_bandwidth(state, bw); +- +- cx24113_set_frequency(state, c->frequency); +- msleep(5); +- return cx24113_get_status(fe, &bw); +-} +- +-static s8 cx24113_agc_table[2][10] = { +- {-54, -41, -35, -30, -25, -21, -16, -10, -6, -2}, +- {-39, -35, -30, -25, -19, -15, -11, -5, 1, 9}, +-}; +- +-void cx24113_agc_callback(struct dvb_frontend *fe) +-{ +- struct cx24113_state *state = fe->tuner_priv; +- s16 s, i; +- if (!fe->ops.read_signal_strength) +- return; +- +- do { +- /* this only works with the current CX24123 implementation */ +- fe->ops.read_signal_strength(fe, (u16 *) &s); +- s >>= 8; +- dprintk("signal strength: %d\n", s); +- for (i = 0; i < sizeof(cx24113_agc_table[0]); i++) +- if (cx24113_agc_table[state->gain_level][i] > s) +- break; +- s = -25 - i*5; +- } while (cx24113_set_gain_settings(state, s)); +-} +-EXPORT_SYMBOL(cx24113_agc_callback); +- +-static int cx24113_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct cx24113_state *state = fe->tuner_priv; +- *frequency = state->frequency; +- return 0; +-} +- +-static int cx24113_release(struct dvb_frontend *fe) +-{ +- struct cx24113_state *state = fe->tuner_priv; +- dprintk("\n"); +- fe->tuner_priv = NULL; +- kfree(state); +- return 0; +-} +- +-static const struct dvb_tuner_ops cx24113_tuner_ops = { +- .info = { +- .name = "Conexant CX24113", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 125, +- }, +- +- .release = cx24113_release, +- +- .init = cx24113_init, +- +- .set_params = cx24113_set_params, +- .get_frequency = cx24113_get_frequency, +- .get_status = cx24113_get_status, +-}; +- +-struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, +- const struct cx24113_config *config, struct i2c_adapter *i2c) +-{ +- /* allocate memory for the internal state */ +- struct cx24113_state *state = +- kzalloc(sizeof(struct cx24113_state), GFP_KERNEL); +- int rc; +- if (state == NULL) { +- cx_err("Unable to kzalloc\n"); +- goto error; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- cx_info("trying to detect myself\n"); +- +- /* making a dummy read, because of some expected troubles +- * after power on */ +- cx24113_readreg(state, 0x00); +- +- rc = cx24113_readreg(state, 0x00); +- if (rc < 0) { +- cx_info("CX24113 not found.\n"); +- goto error; +- } +- state->rev = rc; +- +- switch (rc) { +- case 0x43: +- cx_info("detected CX24113 variant\n"); +- break; +- case REV_CX24113: +- cx_info("successfully detected\n"); +- break; +- default: +- cx_err("unsupported device id: %x\n", state->rev); +- goto error; +- } +- state->ver = cx24113_readreg(state, 0x01); +- cx_info("version: %x\n", state->ver); +- +- /* create dvb_frontend */ +- memcpy(&fe->ops.tuner_ops, &cx24113_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- fe->tuner_priv = state; +- return fe; +- +-error: +- kfree(state); +- +- return NULL; +-} +-EXPORT_SYMBOL(cx24113_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("DVB Frontend module for Conexant CX24113/CX24128hardware"); +-MODULE_LICENSE("GPL"); +- +diff --git a/drivers/media/dvb/frontends/cx24113.h b/drivers/media/dvb/frontends/cx24113.h +deleted file mode 100644 +index 01eb7b9..0000000 +--- a/drivers/media/dvb/frontends/cx24113.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * Driver for Conexant CX24113/CX24128 Tuner (Satellite) +- * +- * Copyright (C) 2007-8 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef CX24113_H +-#define CX24113_H +- +-struct dvb_frontend; +- +-struct cx24113_config { +- u8 i2c_addr; /* 0x14 or 0x54 */ +- +- u32 xtal_khz; +-}; +- +-#if defined(CONFIG_DVB_TUNER_CX24113) || \ +- (defined(CONFIG_DVB_TUNER_CX24113_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *cx24113_attach(struct dvb_frontend *, +- const struct cx24113_config *config, struct i2c_adapter *i2c); +- +-extern void cx24113_agc_callback(struct dvb_frontend *fe); +-#else +-static inline struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, +- const struct cx24113_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline void cx24113_agc_callback(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +-#endif +- +-#endif /* CX24113_H */ +diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c +deleted file mode 100644 +index b488791..0000000 +--- a/drivers/media/dvb/frontends/cx24116.c ++++ /dev/null +@@ -1,1508 +0,0 @@ +-/* +- Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver +- +- Copyright (C) 2006-2008 Steven Toth +- Copyright (C) 2006-2007 Georg Acher +- Copyright (C) 2007-2008 Darron Broad +- March 2007 +- Fixed some bugs. +- Added diseqc support. +- Added corrected signal strength support. +- August 2007 +- Sync with legacy version. +- Some clean ups. +- Copyright (C) 2008 Igor Liplianin +- September, 9th 2008 +- Fixed locking on high symbol rates (>30000). +- Implement MPEG initialization parameter. +- January, 17th 2009 +- Fill set_voltage with actually control voltage code. +- Correct set tone to not affect voltage. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "cx24116.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_INFO "cx24116: " args); \ +- } while (0) +- +-#define CX24116_DEFAULT_FIRMWARE "dvb-fe-cx24116.fw" +-#define CX24116_SEARCH_RANGE_KHZ 5000 +- +-/* known registers */ +-#define CX24116_REG_COMMAND (0x00) /* command args 0x00..0x1e */ +-#define CX24116_REG_EXECUTE (0x1f) /* execute command */ +-#define CX24116_REG_MAILBOX (0x96) /* FW or multipurpose mailbox? */ +-#define CX24116_REG_RESET (0x20) /* reset status > 0 */ +-#define CX24116_REG_SIGNAL (0x9e) /* signal low */ +-#define CX24116_REG_SSTATUS (0x9d) /* signal high / status */ +-#define CX24116_REG_QUALITY8 (0xa3) +-#define CX24116_REG_QSTATUS (0xbc) +-#define CX24116_REG_QUALITY0 (0xd5) +-#define CX24116_REG_BER0 (0xc9) +-#define CX24116_REG_BER8 (0xc8) +-#define CX24116_REG_BER16 (0xc7) +-#define CX24116_REG_BER24 (0xc6) +-#define CX24116_REG_UCB0 (0xcb) +-#define CX24116_REG_UCB8 (0xca) +-#define CX24116_REG_CLKDIV (0xf3) +-#define CX24116_REG_RATEDIV (0xf9) +- +-/* configured fec (not tuned) or actual FEC (tuned) 1=1/2 2=2/3 etc */ +-#define CX24116_REG_FECSTATUS (0x9c) +- +-/* FECSTATUS bits */ +-/* mask to determine configured fec (not tuned) or actual fec (tuned) */ +-#define CX24116_FEC_FECMASK (0x1f) +- +-/* Select DVB-S demodulator, else DVB-S2 */ +-#define CX24116_FEC_DVBS (0x20) +-#define CX24116_FEC_UNKNOWN (0x40) /* Unknown/unused */ +- +-/* Pilot mode requested when tuning else always reset when tuned */ +-#define CX24116_FEC_PILOT (0x80) +- +-/* arg buffer size */ +-#define CX24116_ARGLEN (0x1e) +- +-/* rolloff */ +-#define CX24116_ROLLOFF_020 (0x00) +-#define CX24116_ROLLOFF_025 (0x01) +-#define CX24116_ROLLOFF_035 (0x02) +- +-/* pilot bit */ +-#define CX24116_PILOT_OFF (0x00) +-#define CX24116_PILOT_ON (0x40) +- +-/* signal status */ +-#define CX24116_HAS_SIGNAL (0x01) +-#define CX24116_HAS_CARRIER (0x02) +-#define CX24116_HAS_VITERBI (0x04) +-#define CX24116_HAS_SYNCLOCK (0x08) +-#define CX24116_HAS_UNKNOWN1 (0x10) +-#define CX24116_HAS_UNKNOWN2 (0x20) +-#define CX24116_STATUS_MASK (0x0f) +-#define CX24116_SIGNAL_MASK (0xc0) +- +-#define CX24116_DISEQC_TONEOFF (0) /* toneburst never sent */ +-#define CX24116_DISEQC_TONECACHE (1) /* toneburst cached */ +-#define CX24116_DISEQC_MESGCACHE (2) /* message cached */ +- +-/* arg offset for DiSEqC */ +-#define CX24116_DISEQC_BURST (1) +-#define CX24116_DISEQC_ARG2_2 (2) /* unknown value=2 */ +-#define CX24116_DISEQC_ARG3_0 (3) /* unknown value=0 */ +-#define CX24116_DISEQC_ARG4_0 (4) /* unknown value=0 */ +-#define CX24116_DISEQC_MSGLEN (5) +-#define CX24116_DISEQC_MSGOFS (6) +- +-/* DiSEqC burst */ +-#define CX24116_DISEQC_MINI_A (0) +-#define CX24116_DISEQC_MINI_B (1) +- +-/* DiSEqC tone burst */ +-static int toneburst = 1; +-module_param(toneburst, int, 0644); +-MODULE_PARM_DESC(toneburst, "DiSEqC toneburst 0=OFF, 1=TONE CACHE, "\ +- "2=MESSAGE CACHE (default:1)"); +- +-/* SNR measurements */ +-static int esno_snr; +-module_param(esno_snr, int, 0644); +-MODULE_PARM_DESC(esno_snr, "SNR return units, 0=PERCENTAGE 0-100, "\ +- "1=ESNO(db * 10) (default:0)"); +- +-enum cmds { +- CMD_SET_VCO = 0x10, +- CMD_TUNEREQUEST = 0x11, +- CMD_MPEGCONFIG = 0x13, +- CMD_TUNERINIT = 0x14, +- CMD_BANDWIDTH = 0x15, +- CMD_GETAGC = 0x19, +- CMD_LNBCONFIG = 0x20, +- CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */ +- CMD_LNBDCLEVEL = 0x22, +- CMD_SET_TONE = 0x23, +- CMD_UPDFWVERS = 0x35, +- CMD_TUNERSLEEP = 0x36, +- CMD_AGCCONTROL = 0x3b, /* Unknown */ +-}; +- +-/* The Demod/Tuner can't easily provide these, we cache them */ +-struct cx24116_tuning { +- u32 frequency; +- u32 symbol_rate; +- fe_spectral_inversion_t inversion; +- fe_code_rate_t fec; +- +- fe_delivery_system_t delsys; +- fe_modulation_t modulation; +- fe_pilot_t pilot; +- fe_rolloff_t rolloff; +- +- /* Demod values */ +- u8 fec_val; +- u8 fec_mask; +- u8 inversion_val; +- u8 pilot_val; +- u8 rolloff_val; +-}; +- +-/* Basic commands that are sent to the firmware */ +-struct cx24116_cmd { +- u8 len; +- u8 args[CX24116_ARGLEN]; +-}; +- +-struct cx24116_state { +- struct i2c_adapter *i2c; +- const struct cx24116_config *config; +- +- struct dvb_frontend frontend; +- +- struct cx24116_tuning dcur; +- struct cx24116_tuning dnxt; +- +- u8 skip_fw_load; +- u8 burst; +- struct cx24116_cmd dsec_cmd; +-}; +- +-static int cx24116_writereg(struct cx24116_state *state, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = 0, .buf = buf, .len = 2 }; +- int err; +- +- if (debug > 1) +- printk("cx24116: %s: write reg 0x%02x, value 0x%02x\n", +- __func__, reg, data); +- +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," +- " value == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-/* Bulk byte writes to a single I2C address, for 32k firmware load */ +-static int cx24116_writeregN(struct cx24116_state *state, int reg, +- const u8 *data, u16 len) +-{ +- int ret = -EREMOTEIO; +- struct i2c_msg msg; +- u8 *buf; +- +- buf = kmalloc(len + 1, GFP_KERNEL); +- if (buf == NULL) { +- printk("Unable to kmalloc\n"); +- ret = -ENOMEM; +- goto error; +- } +- +- *(buf) = reg; +- memcpy(buf + 1, data, len); +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.buf = buf; +- msg.len = len + 1; +- +- if (debug > 1) +- printk(KERN_INFO "cx24116: %s: write regN 0x%02x, len = %d\n", +- __func__, reg, len); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) { +- printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x\n", +- __func__, ret, reg); +- ret = -EREMOTEIO; +- } +- +-error: +- kfree(buf); +- +- return ret; +-} +- +-static int cx24116_readreg(struct cx24116_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, +- .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, +- .buf = b1, .len = 1 } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk(KERN_ERR "%s: reg=0x%x (error=%d)\n", +- __func__, reg, ret); +- return ret; +- } +- +- if (debug > 1) +- printk(KERN_INFO "cx24116: read reg 0x%02x, value 0x%02x\n", +- reg, b1[0]); +- +- return b1[0]; +-} +- +-static int cx24116_set_inversion(struct cx24116_state *state, +- fe_spectral_inversion_t inversion) +-{ +- dprintk("%s(%d)\n", __func__, inversion); +- +- switch (inversion) { +- case INVERSION_OFF: +- state->dnxt.inversion_val = 0x00; +- break; +- case INVERSION_ON: +- state->dnxt.inversion_val = 0x04; +- break; +- case INVERSION_AUTO: +- state->dnxt.inversion_val = 0x0C; +- break; +- default: +- return -EINVAL; +- } +- +- state->dnxt.inversion = inversion; +- +- return 0; +-} +- +-/* +- * modfec (modulation and FEC) +- * =========================== +- * +- * MOD FEC mask/val standard +- * ---- -------- ----------- -------- +- * QPSK FEC_1_2 0x02 0x02+X DVB-S +- * QPSK FEC_2_3 0x04 0x02+X DVB-S +- * QPSK FEC_3_4 0x08 0x02+X DVB-S +- * QPSK FEC_4_5 0x10 0x02+X DVB-S (?) +- * QPSK FEC_5_6 0x20 0x02+X DVB-S +- * QPSK FEC_6_7 0x40 0x02+X DVB-S +- * QPSK FEC_7_8 0x80 0x02+X DVB-S +- * QPSK FEC_8_9 0x01 0x02+X DVB-S (?) (NOT SUPPORTED?) +- * QPSK AUTO 0xff 0x02+X DVB-S +- * +- * For DVB-S high byte probably represents FEC +- * and low byte selects the modulator. The high +- * byte is search range mask. Bit 5 may turn +- * on DVB-S and remaining bits represent some +- * kind of calibration (how/what i do not know). +- * +- * Eg.(2/3) szap "Zone Horror" +- * +- * mask/val = 0x04, 0x20 +- * status 1f | signal c3c0 | snr a333 | ber 00000098 | unc 0 | FE_HAS_LOCK +- * +- * mask/val = 0x04, 0x30 +- * status 1f | signal c3c0 | snr a333 | ber 00000000 | unc 0 | FE_HAS_LOCK +- * +- * After tuning FECSTATUS contains actual FEC +- * in use numbered 1 through to 8 for 1/2 .. 2/3 etc +- * +- * NBC=NOT/NON BACKWARD COMPATIBLE WITH DVB-S (DVB-S2 only) +- * +- * NBC-QPSK FEC_1_2 0x00, 0x04 DVB-S2 +- * NBC-QPSK FEC_3_5 0x00, 0x05 DVB-S2 +- * NBC-QPSK FEC_2_3 0x00, 0x06 DVB-S2 +- * NBC-QPSK FEC_3_4 0x00, 0x07 DVB-S2 +- * NBC-QPSK FEC_4_5 0x00, 0x08 DVB-S2 +- * NBC-QPSK FEC_5_6 0x00, 0x09 DVB-S2 +- * NBC-QPSK FEC_8_9 0x00, 0x0a DVB-S2 +- * NBC-QPSK FEC_9_10 0x00, 0x0b DVB-S2 +- * +- * NBC-8PSK FEC_3_5 0x00, 0x0c DVB-S2 +- * NBC-8PSK FEC_2_3 0x00, 0x0d DVB-S2 +- * NBC-8PSK FEC_3_4 0x00, 0x0e DVB-S2 +- * NBC-8PSK FEC_5_6 0x00, 0x0f DVB-S2 +- * NBC-8PSK FEC_8_9 0x00, 0x10 DVB-S2 +- * NBC-8PSK FEC_9_10 0x00, 0x11 DVB-S2 +- * +- * For DVB-S2 low bytes selects both modulator +- * and FEC. High byte is meaningless here. To +- * set pilot, bit 6 (0x40) is set. When inspecting +- * FECSTATUS bit 7 (0x80) represents the pilot +- * selection whilst not tuned. When tuned, actual FEC +- * in use is found in FECSTATUS as per above. Pilot +- * value is reset. +- */ +- +-/* A table of modulation, fec and configuration bytes for the demod. +- * Not all S2 mmodulation schemes are support and not all rates with +- * a scheme are support. Especially, no auto detect when in S2 mode. +- */ +-static struct cx24116_modfec { +- fe_delivery_system_t delivery_system; +- fe_modulation_t modulation; +- fe_code_rate_t fec; +- u8 mask; /* In DVBS mode this is used to autodetect */ +- u8 val; /* Passed to the firmware to indicate mode selection */ +-} CX24116_MODFEC_MODES[] = { +- /* QPSK. For unknown rates we set hardware to auto detect 0xfe 0x30 */ +- +- /*mod fec mask val */ +- { SYS_DVBS, QPSK, FEC_NONE, 0xfe, 0x30 }, +- { SYS_DVBS, QPSK, FEC_1_2, 0x02, 0x2e }, /* 00000010 00101110 */ +- { SYS_DVBS, QPSK, FEC_2_3, 0x04, 0x2f }, /* 00000100 00101111 */ +- { SYS_DVBS, QPSK, FEC_3_4, 0x08, 0x30 }, /* 00001000 00110000 */ +- { SYS_DVBS, QPSK, FEC_4_5, 0xfe, 0x30 }, /* 000?0000 ? */ +- { SYS_DVBS, QPSK, FEC_5_6, 0x20, 0x31 }, /* 00100000 00110001 */ +- { SYS_DVBS, QPSK, FEC_6_7, 0xfe, 0x30 }, /* 0?000000 ? */ +- { SYS_DVBS, QPSK, FEC_7_8, 0x80, 0x32 }, /* 10000000 00110010 */ +- { SYS_DVBS, QPSK, FEC_8_9, 0xfe, 0x30 }, /* 0000000? ? */ +- { SYS_DVBS, QPSK, FEC_AUTO, 0xfe, 0x30 }, +- /* NBC-QPSK */ +- { SYS_DVBS2, QPSK, FEC_1_2, 0x00, 0x04 }, +- { SYS_DVBS2, QPSK, FEC_3_5, 0x00, 0x05 }, +- { SYS_DVBS2, QPSK, FEC_2_3, 0x00, 0x06 }, +- { SYS_DVBS2, QPSK, FEC_3_4, 0x00, 0x07 }, +- { SYS_DVBS2, QPSK, FEC_4_5, 0x00, 0x08 }, +- { SYS_DVBS2, QPSK, FEC_5_6, 0x00, 0x09 }, +- { SYS_DVBS2, QPSK, FEC_8_9, 0x00, 0x0a }, +- { SYS_DVBS2, QPSK, FEC_9_10, 0x00, 0x0b }, +- /* 8PSK */ +- { SYS_DVBS2, PSK_8, FEC_3_5, 0x00, 0x0c }, +- { SYS_DVBS2, PSK_8, FEC_2_3, 0x00, 0x0d }, +- { SYS_DVBS2, PSK_8, FEC_3_4, 0x00, 0x0e }, +- { SYS_DVBS2, PSK_8, FEC_5_6, 0x00, 0x0f }, +- { SYS_DVBS2, PSK_8, FEC_8_9, 0x00, 0x10 }, +- { SYS_DVBS2, PSK_8, FEC_9_10, 0x00, 0x11 }, +- /* +- * `val' can be found in the FECSTATUS register when tuning. +- * FECSTATUS will give the actual FEC in use if tuning was successful. +- */ +-}; +- +-static int cx24116_lookup_fecmod(struct cx24116_state *state, +- fe_delivery_system_t d, fe_modulation_t m, fe_code_rate_t f) +-{ +- int i, ret = -EOPNOTSUPP; +- +- dprintk("%s(0x%02x,0x%02x)\n", __func__, m, f); +- +- for (i = 0; i < ARRAY_SIZE(CX24116_MODFEC_MODES); i++) { +- if ((d == CX24116_MODFEC_MODES[i].delivery_system) && +- (m == CX24116_MODFEC_MODES[i].modulation) && +- (f == CX24116_MODFEC_MODES[i].fec)) { +- ret = i; +- break; +- } +- } +- +- return ret; +-} +- +-static int cx24116_set_fec(struct cx24116_state *state, +- fe_delivery_system_t delsys, fe_modulation_t mod, fe_code_rate_t fec) +-{ +- int ret = 0; +- +- dprintk("%s(0x%02x,0x%02x)\n", __func__, mod, fec); +- +- ret = cx24116_lookup_fecmod(state, delsys, mod, fec); +- +- if (ret < 0) +- return ret; +- +- state->dnxt.fec = fec; +- state->dnxt.fec_val = CX24116_MODFEC_MODES[ret].val; +- state->dnxt.fec_mask = CX24116_MODFEC_MODES[ret].mask; +- dprintk("%s() mask/val = 0x%02x/0x%02x\n", __func__, +- state->dnxt.fec_mask, state->dnxt.fec_val); +- +- return 0; +-} +- +-static int cx24116_set_symbolrate(struct cx24116_state *state, u32 rate) +-{ +- dprintk("%s(%d)\n", __func__, rate); +- +- /* check if symbol rate is within limits */ +- if ((rate > state->frontend.ops.info.symbol_rate_max) || +- (rate < state->frontend.ops.info.symbol_rate_min)) { +- dprintk("%s() unsupported symbol_rate = %d\n", __func__, rate); +- return -EOPNOTSUPP; +- } +- +- state->dnxt.symbol_rate = rate; +- dprintk("%s() symbol_rate = %d\n", __func__, rate); +- +- return 0; +-} +- +-static int cx24116_load_firmware(struct dvb_frontend *fe, +- const struct firmware *fw); +- +-static int cx24116_firmware_ondemand(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- const struct firmware *fw; +- int ret = 0; +- +- dprintk("%s()\n", __func__); +- +- if (cx24116_readreg(state, 0x20) > 0) { +- +- if (state->skip_fw_load) +- return 0; +- +- /* Load firmware */ +- /* request the firmware, this will block until loaded */ +- printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", +- __func__, CX24116_DEFAULT_FIRMWARE); +- ret = request_firmware(&fw, CX24116_DEFAULT_FIRMWARE, +- state->i2c->dev.parent); +- printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", +- __func__); +- if (ret) { +- printk(KERN_ERR "%s: No firmware uploaded " +- "(timeout or file not found?)\n", __func__); +- return ret; +- } +- +- /* Make sure we don't recurse back through here +- * during loading */ +- state->skip_fw_load = 1; +- +- ret = cx24116_load_firmware(fe, fw); +- if (ret) +- printk(KERN_ERR "%s: Writing firmware to device failed\n", +- __func__); +- +- release_firmware(fw); +- +- printk(KERN_INFO "%s: Firmware upload %s\n", __func__, +- ret == 0 ? "complete" : "failed"); +- +- /* Ensure firmware is always loaded if required */ +- state->skip_fw_load = 0; +- } +- +- return ret; +-} +- +-/* Take a basic firmware command structure, format it +- * and forward it for processing +- */ +-static int cx24116_cmd_execute(struct dvb_frontend *fe, struct cx24116_cmd *cmd) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- int i, ret; +- +- dprintk("%s()\n", __func__); +- +- /* Load the firmware if required */ +- ret = cx24116_firmware_ondemand(fe); +- if (ret != 0) { +- printk(KERN_ERR "%s(): Unable initialise the firmware\n", +- __func__); +- return ret; +- } +- +- /* Write the command */ +- for (i = 0; i < cmd->len ; i++) { +- dprintk("%s: 0x%02x == 0x%02x\n", __func__, i, cmd->args[i]); +- cx24116_writereg(state, i, cmd->args[i]); +- } +- +- /* Start execution and wait for cmd to terminate */ +- cx24116_writereg(state, CX24116_REG_EXECUTE, 0x01); +- while (cx24116_readreg(state, CX24116_REG_EXECUTE)) { +- msleep(10); +- if (i++ > 64) { +- /* Avoid looping forever if the firmware does +- not respond */ +- printk(KERN_WARNING "%s() Firmware not responding\n", +- __func__); +- return -EREMOTEIO; +- } +- } +- return 0; +-} +- +-static int cx24116_load_firmware(struct dvb_frontend *fe, +- const struct firmware *fw) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct cx24116_cmd cmd; +- int i, ret, len, max, remaining; +- unsigned char vers[4]; +- +- dprintk("%s\n", __func__); +- dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", +- fw->size, +- fw->data[0], +- fw->data[1], +- fw->data[fw->size-2], +- fw->data[fw->size-1]); +- +- /* Toggle 88x SRST pin to reset demod */ +- if (state->config->reset_device) +- state->config->reset_device(fe); +- +- /* Begin the firmware load process */ +- /* Prepare the demod, load the firmware, cleanup after load */ +- +- /* Init PLL */ +- cx24116_writereg(state, 0xE5, 0x00); +- cx24116_writereg(state, 0xF1, 0x08); +- cx24116_writereg(state, 0xF2, 0x13); +- +- /* Start PLL */ +- cx24116_writereg(state, 0xe0, 0x03); +- cx24116_writereg(state, 0xe0, 0x00); +- +- /* Unknown */ +- cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); +- cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); +- +- /* Unknown */ +- cx24116_writereg(state, 0xF0, 0x03); +- cx24116_writereg(state, 0xF4, 0x81); +- cx24116_writereg(state, 0xF5, 0x00); +- cx24116_writereg(state, 0xF6, 0x00); +- +- /* Split firmware to the max I2C write len and write. +- * Writes whole firmware as one write when i2c_wr_max is set to 0. */ +- if (state->config->i2c_wr_max) +- max = state->config->i2c_wr_max; +- else +- max = INT_MAX; /* enough for 32k firmware */ +- +- for (remaining = fw->size; remaining > 0; remaining -= max - 1) { +- len = remaining; +- if (len > max - 1) +- len = max - 1; +- +- cx24116_writeregN(state, 0xF7, &fw->data[fw->size - remaining], +- len); +- } +- +- cx24116_writereg(state, 0xF4, 0x10); +- cx24116_writereg(state, 0xF0, 0x00); +- cx24116_writereg(state, 0xF8, 0x06); +- +- /* Firmware CMD 10: VCO config */ +- cmd.args[0x00] = CMD_SET_VCO; +- cmd.args[0x01] = 0x05; +- cmd.args[0x02] = 0xdc; +- cmd.args[0x03] = 0xda; +- cmd.args[0x04] = 0xae; +- cmd.args[0x05] = 0xaa; +- cmd.args[0x06] = 0x04; +- cmd.args[0x07] = 0x9d; +- cmd.args[0x08] = 0xfc; +- cmd.args[0x09] = 0x06; +- cmd.len = 0x0a; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- cx24116_writereg(state, CX24116_REG_SSTATUS, 0x00); +- +- /* Firmware CMD 14: Tuner config */ +- cmd.args[0x00] = CMD_TUNERINIT; +- cmd.args[0x01] = 0x00; +- cmd.args[0x02] = 0x00; +- cmd.len = 0x03; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- cx24116_writereg(state, 0xe5, 0x00); +- +- /* Firmware CMD 13: MPEG config */ +- cmd.args[0x00] = CMD_MPEGCONFIG; +- cmd.args[0x01] = 0x01; +- cmd.args[0x02] = 0x75; +- cmd.args[0x03] = 0x00; +- if (state->config->mpg_clk_pos_pol) +- cmd.args[0x04] = state->config->mpg_clk_pos_pol; +- else +- cmd.args[0x04] = 0x02; +- cmd.args[0x05] = 0x00; +- cmd.len = 0x06; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- /* Firmware CMD 35: Get firmware version */ +- cmd.args[0x00] = CMD_UPDFWVERS; +- cmd.len = 0x02; +- for (i = 0; i < 4; i++) { +- cmd.args[0x01] = i; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- vers[i] = cx24116_readreg(state, CX24116_REG_MAILBOX); +- } +- printk(KERN_INFO "%s: FW version %i.%i.%i.%i\n", __func__, +- vers[0], vers[1], vers[2], vers[3]); +- +- return 0; +-} +- +-static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- +- int lock = cx24116_readreg(state, CX24116_REG_SSTATUS) & +- CX24116_STATUS_MASK; +- +- dprintk("%s: status = 0x%02x\n", __func__, lock); +- +- *status = 0; +- +- if (lock & CX24116_HAS_SIGNAL) +- *status |= FE_HAS_SIGNAL; +- if (lock & CX24116_HAS_CARRIER) +- *status |= FE_HAS_CARRIER; +- if (lock & CX24116_HAS_VITERBI) +- *status |= FE_HAS_VITERBI; +- if (lock & CX24116_HAS_SYNCLOCK) +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- +- return 0; +-} +- +-static int cx24116_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- *ber = (cx24116_readreg(state, CX24116_REG_BER24) << 24) | +- (cx24116_readreg(state, CX24116_REG_BER16) << 16) | +- (cx24116_readreg(state, CX24116_REG_BER8) << 8) | +- cx24116_readreg(state, CX24116_REG_BER0); +- +- return 0; +-} +- +-/* TODO Determine function and scale appropriately */ +-static int cx24116_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct cx24116_cmd cmd; +- int ret; +- u16 sig_reading; +- +- dprintk("%s()\n", __func__); +- +- /* Firmware CMD 19: Get AGC */ +- cmd.args[0x00] = CMD_GETAGC; +- cmd.len = 0x01; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- sig_reading = +- (cx24116_readreg(state, +- CX24116_REG_SSTATUS) & CX24116_SIGNAL_MASK) | +- (cx24116_readreg(state, CX24116_REG_SIGNAL) << 6); +- *signal_strength = 0 - sig_reading; +- +- dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", +- __func__, sig_reading, *signal_strength); +- +- return 0; +-} +- +-/* SNR (0..100)% = (sig & 0xf0) * 10 + (sig & 0x0f) * 10 / 16 */ +-static int cx24116_read_snr_pct(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- u8 snr_reading; +- static const u32 snr_tab[] = { /* 10 x Table (rounded up) */ +- 0x00000, 0x0199A, 0x03333, 0x04ccD, 0x06667, +- 0x08000, 0x0999A, 0x0b333, 0x0cccD, 0x0e667, +- 0x10000, 0x1199A, 0x13333, 0x14ccD, 0x16667, +- 0x18000 }; +- +- dprintk("%s()\n", __func__); +- +- snr_reading = cx24116_readreg(state, CX24116_REG_QUALITY0); +- +- if (snr_reading >= 0xa0 /* 100% */) +- *snr = 0xffff; +- else +- *snr = snr_tab[(snr_reading & 0xf0) >> 4] + +- (snr_tab[(snr_reading & 0x0f)] >> 4); +- +- dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, +- snr_reading, *snr); +- +- return 0; +-} +- +-/* The reelbox patches show the value in the registers represents +- * ESNO, from 0->30db (values 0->300). We provide this value by +- * default. +- */ +-static int cx24116_read_snr_esno(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- *snr = cx24116_readreg(state, CX24116_REG_QUALITY8) << 8 | +- cx24116_readreg(state, CX24116_REG_QUALITY0); +- +- dprintk("%s: raw 0x%04x\n", __func__, *snr); +- +- return 0; +-} +- +-static int cx24116_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- if (esno_snr == 1) +- return cx24116_read_snr_esno(fe, snr); +- else +- return cx24116_read_snr_pct(fe, snr); +-} +- +-static int cx24116_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- *ucblocks = (cx24116_readreg(state, CX24116_REG_UCB8) << 8) | +- cx24116_readreg(state, CX24116_REG_UCB0); +- +- return 0; +-} +- +-/* Overwrite the current tuning params, we are about to tune */ +-static void cx24116_clone_params(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); +-} +- +-/* Wait for LNB */ +-static int cx24116_wait_for_lnb(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- int i; +- +- dprintk("%s() qstatus = 0x%02x\n", __func__, +- cx24116_readreg(state, CX24116_REG_QSTATUS)); +- +- /* Wait for up to 300 ms */ +- for (i = 0; i < 30 ; i++) { +- if (cx24116_readreg(state, CX24116_REG_QSTATUS) & 0x20) +- return 0; +- msleep(10); +- } +- +- dprintk("%s(): LNB not ready\n", __func__); +- +- return -ETIMEDOUT; /* -EBUSY ? */ +-} +- +-static int cx24116_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- struct cx24116_cmd cmd; +- int ret; +- +- dprintk("%s: %s\n", __func__, +- voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : +- voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); +- +- /* Wait for LNB ready */ +- ret = cx24116_wait_for_lnb(fe); +- if (ret != 0) +- return ret; +- +- /* Wait for voltage/min repeat delay */ +- msleep(100); +- +- cmd.args[0x00] = CMD_LNBDCLEVEL; +- cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00); +- cmd.len = 0x02; +- +- /* Min delay time before DiSEqC send */ +- msleep(15); +- +- return cx24116_cmd_execute(fe, &cmd); +-} +- +-static int cx24116_set_tone(struct dvb_frontend *fe, +- fe_sec_tone_mode_t tone) +-{ +- struct cx24116_cmd cmd; +- int ret; +- +- dprintk("%s(%d)\n", __func__, tone); +- if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { +- printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); +- return -EINVAL; +- } +- +- /* Wait for LNB ready */ +- ret = cx24116_wait_for_lnb(fe); +- if (ret != 0) +- return ret; +- +- /* Min delay time after DiSEqC send */ +- msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ +- +- /* Now we set the tone */ +- cmd.args[0x00] = CMD_SET_TONE; +- cmd.args[0x01] = 0x00; +- cmd.args[0x02] = 0x00; +- +- switch (tone) { +- case SEC_TONE_ON: +- dprintk("%s: setting tone on\n", __func__); +- cmd.args[0x03] = 0x01; +- break; +- case SEC_TONE_OFF: +- dprintk("%s: setting tone off\n", __func__); +- cmd.args[0x03] = 0x00; +- break; +- } +- cmd.len = 0x04; +- +- /* Min delay time before DiSEqC send */ +- msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */ +- +- return cx24116_cmd_execute(fe, &cmd); +-} +- +-/* Initialise DiSEqC */ +-static int cx24116_diseqc_init(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct cx24116_cmd cmd; +- int ret; +- +- /* Firmware CMD 20: LNB/DiSEqC config */ +- cmd.args[0x00] = CMD_LNBCONFIG; +- cmd.args[0x01] = 0x00; +- cmd.args[0x02] = 0x10; +- cmd.args[0x03] = 0x00; +- cmd.args[0x04] = 0x8f; +- cmd.args[0x05] = 0x28; +- cmd.args[0x06] = (toneburst == CX24116_DISEQC_TONEOFF) ? 0x00 : 0x01; +- cmd.args[0x07] = 0x01; +- cmd.len = 0x08; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- /* Prepare a DiSEqC command */ +- state->dsec_cmd.args[0x00] = CMD_LNBSEND; +- +- /* DiSEqC burst */ +- state->dsec_cmd.args[CX24116_DISEQC_BURST] = CX24116_DISEQC_MINI_A; +- +- /* Unknown */ +- state->dsec_cmd.args[CX24116_DISEQC_ARG2_2] = 0x02; +- state->dsec_cmd.args[CX24116_DISEQC_ARG3_0] = 0x00; +- /* Continuation flag? */ +- state->dsec_cmd.args[CX24116_DISEQC_ARG4_0] = 0x00; +- +- /* DiSEqC message length */ +- state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = 0x00; +- +- /* Command length */ +- state->dsec_cmd.len = CX24116_DISEQC_MSGOFS; +- +- return 0; +-} +- +-/* Send DiSEqC message with derived burst (hack) || previous burst */ +-static int cx24116_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *d) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- int i, ret; +- +- /* Dump DiSEqC message */ +- if (debug) { +- printk(KERN_INFO "cx24116: %s(", __func__); +- for (i = 0 ; i < d->msg_len ;) { +- printk(KERN_INFO "0x%02x", d->msg[i]); +- if (++i < d->msg_len) +- printk(KERN_INFO ", "); +- } +- printk(") toneburst=%d\n", toneburst); +- } +- +- /* Validate length */ +- if (d->msg_len > (CX24116_ARGLEN - CX24116_DISEQC_MSGOFS)) +- return -EINVAL; +- +- /* DiSEqC message */ +- for (i = 0; i < d->msg_len; i++) +- state->dsec_cmd.args[CX24116_DISEQC_MSGOFS + i] = d->msg[i]; +- +- /* DiSEqC message length */ +- state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] = d->msg_len; +- +- /* Command length */ +- state->dsec_cmd.len = CX24116_DISEQC_MSGOFS + +- state->dsec_cmd.args[CX24116_DISEQC_MSGLEN]; +- +- /* DiSEqC toneburst */ +- if (toneburst == CX24116_DISEQC_MESGCACHE) +- /* Message is cached */ +- return 0; +- +- else if (toneburst == CX24116_DISEQC_TONEOFF) +- /* Message is sent without burst */ +- state->dsec_cmd.args[CX24116_DISEQC_BURST] = 0; +- +- else if (toneburst == CX24116_DISEQC_TONECACHE) { +- /* +- * Message is sent with derived else cached burst +- * +- * WRITE PORT GROUP COMMAND 38 +- * +- * 0/A/A: E0 10 38 F0..F3 +- * 1/B/B: E0 10 38 F4..F7 +- * 2/C/A: E0 10 38 F8..FB +- * 3/D/B: E0 10 38 FC..FF +- * +- * databyte[3]= 8421:8421 +- * ABCD:WXYZ +- * CLR :SET +- * +- * WX= PORT SELECT 0..3 (X=TONEBURST) +- * Y = VOLTAGE (0=13V, 1=18V) +- * Z = BAND (0=LOW, 1=HIGH(22K)) +- */ +- if (d->msg_len >= 4 && d->msg[2] == 0x38) +- state->dsec_cmd.args[CX24116_DISEQC_BURST] = +- ((d->msg[3] & 4) >> 2); +- if (debug) +- dprintk("%s burst=%d\n", __func__, +- state->dsec_cmd.args[CX24116_DISEQC_BURST]); +- } +- +- /* Wait for LNB ready */ +- ret = cx24116_wait_for_lnb(fe); +- if (ret != 0) +- return ret; +- +- /* Wait for voltage/min repeat delay */ +- msleep(100); +- +- /* Command */ +- ret = cx24116_cmd_execute(fe, &state->dsec_cmd); +- if (ret != 0) +- return ret; +- /* +- * Wait for send +- * +- * Eutelsat spec: +- * >15ms delay + (XXX determine if FW does this, see set_tone) +- * 13.5ms per byte + +- * >15ms delay + +- * 12.5ms burst + +- * >15ms delay (XXX determine if FW does this, see set_tone) +- */ +- msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + +- ((toneburst == CX24116_DISEQC_TONEOFF) ? 30 : 60)); +- +- return 0; +-} +- +-/* Send DiSEqC burst */ +-static int cx24116_diseqc_send_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- int ret; +- +- dprintk("%s(%d) toneburst=%d\n", __func__, burst, toneburst); +- +- /* DiSEqC burst */ +- if (burst == SEC_MINI_A) +- state->dsec_cmd.args[CX24116_DISEQC_BURST] = +- CX24116_DISEQC_MINI_A; +- else if (burst == SEC_MINI_B) +- state->dsec_cmd.args[CX24116_DISEQC_BURST] = +- CX24116_DISEQC_MINI_B; +- else +- return -EINVAL; +- +- /* DiSEqC toneburst */ +- if (toneburst != CX24116_DISEQC_MESGCACHE) +- /* Burst is cached */ +- return 0; +- +- /* Burst is to be sent with cached message */ +- +- /* Wait for LNB ready */ +- ret = cx24116_wait_for_lnb(fe); +- if (ret != 0) +- return ret; +- +- /* Wait for voltage/min repeat delay */ +- msleep(100); +- +- /* Command */ +- ret = cx24116_cmd_execute(fe, &state->dsec_cmd); +- if (ret != 0) +- return ret; +- +- /* +- * Wait for send +- * +- * Eutelsat spec: +- * >15ms delay + (XXX determine if FW does this, see set_tone) +- * 13.5ms per byte + +- * >15ms delay + +- * 12.5ms burst + +- * >15ms delay (XXX determine if FW does this, see set_tone) +- */ +- msleep((state->dsec_cmd.args[CX24116_DISEQC_MSGLEN] << 4) + 60); +- +- return 0; +-} +- +-static void cx24116_release(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- dprintk("%s\n", __func__); +- kfree(state); +-} +- +-static struct dvb_frontend_ops cx24116_ops; +- +-struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, +- struct i2c_adapter *i2c) +-{ +- struct cx24116_state *state = NULL; +- int ret; +- +- dprintk("%s\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL); +- if (state == NULL) +- goto error1; +- +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is present */ +- ret = (cx24116_readreg(state, 0xFF) << 8) | +- cx24116_readreg(state, 0xFE); +- if (ret != 0x0501) { +- printk(KERN_INFO "Invalid probe, probably not a CX24116 device\n"); +- goto error2; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &cx24116_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error2: kfree(state); +-error1: return NULL; +-} +-EXPORT_SYMBOL(cx24116_attach); +- +-/* +- * Initialise or wake up device +- * +- * Power config will reset and load initial firmware if required +- */ +-static int cx24116_initfe(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct cx24116_cmd cmd; +- int ret; +- +- dprintk("%s()\n", __func__); +- +- /* Power on */ +- cx24116_writereg(state, 0xe0, 0); +- cx24116_writereg(state, 0xe1, 0); +- cx24116_writereg(state, 0xea, 0); +- +- /* Firmware CMD 36: Power config */ +- cmd.args[0x00] = CMD_TUNERSLEEP; +- cmd.args[0x01] = 0; +- cmd.len = 0x02; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- ret = cx24116_diseqc_init(fe); +- if (ret != 0) +- return ret; +- +- /* HVR-4000 needs this */ +- return cx24116_set_voltage(fe, SEC_VOLTAGE_13); +-} +- +-/* +- * Put device to sleep +- */ +-static int cx24116_sleep(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct cx24116_cmd cmd; +- int ret; +- +- dprintk("%s()\n", __func__); +- +- /* Firmware CMD 36: Power config */ +- cmd.args[0x00] = CMD_TUNERSLEEP; +- cmd.args[0x01] = 1; +- cmd.len = 0x02; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- /* Power off (Shutdown clocks) */ +- cx24116_writereg(state, 0xea, 0xff); +- cx24116_writereg(state, 0xe1, 1); +- cx24116_writereg(state, 0xe0, 1); +- +- return 0; +-} +- +-/* dvb-core told us to tune, the tv property cache will be complete, +- * it's safe for is to pull values and use them for tuning purposes. +- */ +-static int cx24116_set_frontend(struct dvb_frontend *fe) +-{ +- struct cx24116_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct cx24116_cmd cmd; +- fe_status_t tunerstat; +- int i, status, ret, retune = 1; +- +- dprintk("%s()\n", __func__); +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- dprintk("%s: DVB-S delivery system selected\n", __func__); +- +- /* Only QPSK is supported for DVB-S */ +- if (c->modulation != QPSK) { +- dprintk("%s: unsupported modulation selected (%d)\n", +- __func__, c->modulation); +- return -EOPNOTSUPP; +- } +- +- /* Pilot doesn't exist in DVB-S, turn bit off */ +- state->dnxt.pilot_val = CX24116_PILOT_OFF; +- +- /* DVB-S only supports 0.35 */ +- if (c->rolloff != ROLLOFF_35) { +- dprintk("%s: unsupported rolloff selected (%d)\n", +- __func__, c->rolloff); +- return -EOPNOTSUPP; +- } +- state->dnxt.rolloff_val = CX24116_ROLLOFF_035; +- break; +- +- case SYS_DVBS2: +- dprintk("%s: DVB-S2 delivery system selected\n", __func__); +- +- /* +- * NBC 8PSK/QPSK with DVB-S is supported for DVB-S2, +- * but not hardware auto detection +- */ +- if (c->modulation != PSK_8 && c->modulation != QPSK) { +- dprintk("%s: unsupported modulation selected (%d)\n", +- __func__, c->modulation); +- return -EOPNOTSUPP; +- } +- +- switch (c->pilot) { +- case PILOT_AUTO: /* Not supported but emulated */ +- state->dnxt.pilot_val = (c->modulation == QPSK) +- ? CX24116_PILOT_OFF : CX24116_PILOT_ON; +- retune++; +- break; +- case PILOT_OFF: +- state->dnxt.pilot_val = CX24116_PILOT_OFF; +- break; +- case PILOT_ON: +- state->dnxt.pilot_val = CX24116_PILOT_ON; +- break; +- default: +- dprintk("%s: unsupported pilot mode selected (%d)\n", +- __func__, c->pilot); +- return -EOPNOTSUPP; +- } +- +- switch (c->rolloff) { +- case ROLLOFF_20: +- state->dnxt.rolloff_val = CX24116_ROLLOFF_020; +- break; +- case ROLLOFF_25: +- state->dnxt.rolloff_val = CX24116_ROLLOFF_025; +- break; +- case ROLLOFF_35: +- state->dnxt.rolloff_val = CX24116_ROLLOFF_035; +- break; +- case ROLLOFF_AUTO: /* Rolloff must be explicit */ +- default: +- dprintk("%s: unsupported rolloff selected (%d)\n", +- __func__, c->rolloff); +- return -EOPNOTSUPP; +- } +- break; +- +- default: +- dprintk("%s: unsupported delivery system selected (%d)\n", +- __func__, c->delivery_system); +- return -EOPNOTSUPP; +- } +- state->dnxt.delsys = c->delivery_system; +- state->dnxt.modulation = c->modulation; +- state->dnxt.frequency = c->frequency; +- state->dnxt.pilot = c->pilot; +- state->dnxt.rolloff = c->rolloff; +- +- ret = cx24116_set_inversion(state, c->inversion); +- if (ret != 0) +- return ret; +- +- /* FEC_NONE/AUTO for DVB-S2 is not supported and detected here */ +- ret = cx24116_set_fec(state, c->delivery_system, c->modulation, c->fec_inner); +- if (ret != 0) +- return ret; +- +- ret = cx24116_set_symbolrate(state, c->symbol_rate); +- if (ret != 0) +- return ret; +- +- /* discard the 'current' tuning parameters and prepare to tune */ +- cx24116_clone_params(fe); +- +- dprintk("%s: delsys = %d\n", __func__, state->dcur.delsys); +- dprintk("%s: modulation = %d\n", __func__, state->dcur.modulation); +- dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); +- dprintk("%s: pilot = %d (val = 0x%02x)\n", __func__, +- state->dcur.pilot, state->dcur.pilot_val); +- dprintk("%s: retune = %d\n", __func__, retune); +- dprintk("%s: rolloff = %d (val = 0x%02x)\n", __func__, +- state->dcur.rolloff, state->dcur.rolloff_val); +- dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); +- dprintk("%s: FEC = %d (mask/val = 0x%02x/0x%02x)\n", __func__, +- state->dcur.fec, state->dcur.fec_mask, state->dcur.fec_val); +- dprintk("%s: Inversion = %d (val = 0x%02x)\n", __func__, +- state->dcur.inversion, state->dcur.inversion_val); +- +- /* This is also done in advise/acquire on HVR4000 but not on LITE */ +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- +- /* Set/Reset B/W */ +- cmd.args[0x00] = CMD_BANDWIDTH; +- cmd.args[0x01] = 0x01; +- cmd.len = 0x02; +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- return ret; +- +- /* Prepare a tune request */ +- cmd.args[0x00] = CMD_TUNEREQUEST; +- +- /* Frequency */ +- cmd.args[0x01] = (state->dcur.frequency & 0xff0000) >> 16; +- cmd.args[0x02] = (state->dcur.frequency & 0x00ff00) >> 8; +- cmd.args[0x03] = (state->dcur.frequency & 0x0000ff); +- +- /* Symbol Rate */ +- cmd.args[0x04] = ((state->dcur.symbol_rate / 1000) & 0xff00) >> 8; +- cmd.args[0x05] = ((state->dcur.symbol_rate / 1000) & 0x00ff); +- +- /* Automatic Inversion */ +- cmd.args[0x06] = state->dcur.inversion_val; +- +- /* Modulation / FEC / Pilot */ +- cmd.args[0x07] = state->dcur.fec_val | state->dcur.pilot_val; +- +- cmd.args[0x08] = CX24116_SEARCH_RANGE_KHZ >> 8; +- cmd.args[0x09] = CX24116_SEARCH_RANGE_KHZ & 0xff; +- cmd.args[0x0a] = 0x00; +- cmd.args[0x0b] = 0x00; +- cmd.args[0x0c] = state->dcur.rolloff_val; +- cmd.args[0x0d] = state->dcur.fec_mask; +- +- if (state->dcur.symbol_rate > 30000000) { +- cmd.args[0x0e] = 0x04; +- cmd.args[0x0f] = 0x00; +- cmd.args[0x10] = 0x01; +- cmd.args[0x11] = 0x77; +- cmd.args[0x12] = 0x36; +- cx24116_writereg(state, CX24116_REG_CLKDIV, 0x44); +- cx24116_writereg(state, CX24116_REG_RATEDIV, 0x01); +- } else { +- cmd.args[0x0e] = 0x06; +- cmd.args[0x0f] = 0x00; +- cmd.args[0x10] = 0x00; +- cmd.args[0x11] = 0xFA; +- cmd.args[0x12] = 0x24; +- cx24116_writereg(state, CX24116_REG_CLKDIV, 0x46); +- cx24116_writereg(state, CX24116_REG_RATEDIV, 0x00); +- } +- +- cmd.len = 0x13; +- +- /* We need to support pilot and non-pilot tuning in the +- * driver automatically. This is a workaround for because +- * the demod does not support autodetect. +- */ +- do { +- /* Reset status register */ +- status = cx24116_readreg(state, CX24116_REG_SSTATUS) +- & CX24116_SIGNAL_MASK; +- cx24116_writereg(state, CX24116_REG_SSTATUS, status); +- +- /* Tune */ +- ret = cx24116_cmd_execute(fe, &cmd); +- if (ret != 0) +- break; +- +- /* +- * Wait for up to 500 ms before retrying +- * +- * If we are able to tune then generally it occurs within 100ms. +- * If it takes longer, try a different toneburst setting. +- */ +- for (i = 0; i < 50 ; i++) { +- cx24116_read_status(fe, &tunerstat); +- status = tunerstat & (FE_HAS_SIGNAL | FE_HAS_SYNC); +- if (status == (FE_HAS_SIGNAL | FE_HAS_SYNC)) { +- dprintk("%s: Tuned\n", __func__); +- goto tuned; +- } +- msleep(10); +- } +- +- dprintk("%s: Not tuned\n", __func__); +- +- /* Toggle pilot bit when in auto-pilot */ +- if (state->dcur.pilot == PILOT_AUTO) +- cmd.args[0x07] ^= CX24116_PILOT_ON; +- } while (--retune); +- +-tuned: /* Set/Reset B/W */ +- cmd.args[0x00] = CMD_BANDWIDTH; +- cmd.args[0x01] = 0x00; +- cmd.len = 0x02; +- return cx24116_cmd_execute(fe, &cmd); +-} +- +-static int cx24116_tune(struct dvb_frontend *fe, bool re_tune, +- unsigned int mode_flags, unsigned int *delay, fe_status_t *status) +-{ +- /* +- * It is safe to discard "params" here, as the DVB core will sync +- * fe->dtv_property_cache with fepriv->parameters_in, where the +- * DVBv3 params are stored. The only practical usage for it indicate +- * that re-tuning is needed, e. g. (fepriv->state & FESTATE_RETUNE) is +- * true. +- */ +- +- *delay = HZ / 5; +- if (re_tune) { +- int ret = cx24116_set_frontend(fe); +- if (ret) +- return ret; +- } +- return cx24116_read_status(fe, status); +-} +- +-static int cx24116_get_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_HW; +-} +- +-static struct dvb_frontend_ops cx24116_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2 }, +- .info = { +- .name = "Conexant CX24116/CX24118", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1011, /* kHz for QPSK frontends */ +- .frequency_tolerance = 5000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_2G_MODULATION | +- FE_CAN_QPSK | FE_CAN_RECOVER +- }, +- +- .release = cx24116_release, +- +- .init = cx24116_initfe, +- .sleep = cx24116_sleep, +- .read_status = cx24116_read_status, +- .read_ber = cx24116_read_ber, +- .read_signal_strength = cx24116_read_signal_strength, +- .read_snr = cx24116_read_snr, +- .read_ucblocks = cx24116_read_ucblocks, +- .set_tone = cx24116_set_tone, +- .set_voltage = cx24116_set_voltage, +- .diseqc_send_master_cmd = cx24116_send_diseqc_msg, +- .diseqc_send_burst = cx24116_diseqc_send_burst, +- .get_frontend_algo = cx24116_get_algo, +- .tune = cx24116_tune, +- +- .set_frontend = cx24116_set_frontend, +-}; +- +-MODULE_DESCRIPTION("DVB Frontend module for Conexant cx24116/cx24118 hardware"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +- +diff --git a/drivers/media/dvb/frontends/cx24116.h b/drivers/media/dvb/frontends/cx24116.h +deleted file mode 100644 +index 7d90ab9..0000000 +--- a/drivers/media/dvb/frontends/cx24116.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- Conexant cx24116/cx24118 - DVBS/S2 Satellite demod/tuner driver +- +- Copyright (C) 2006 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef CX24116_H +-#define CX24116_H +- +-#include +- +-struct cx24116_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* Need to set device param for start_dma */ +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +- +- /* Need to reset device during firmware loading */ +- int (*reset_device)(struct dvb_frontend *fe); +- +- /* Need to set MPEG parameters */ +- u8 mpg_clk_pos_pol:0x02; +- +- /* max bytes I2C provider can write at once */ +- u16 i2c_wr_max; +-}; +- +-#if defined(CONFIG_DVB_CX24116) || \ +- (defined(CONFIG_DVB_CX24116_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *cx24116_attach( +- const struct cx24116_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *cx24116_attach( +- const struct cx24116_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* CX24116_H */ +diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c +deleted file mode 100644 +index 7e28b4e..0000000 +--- a/drivers/media/dvb/frontends/cx24123.c ++++ /dev/null +@@ -1,1165 +0,0 @@ +-/* +- * Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver +- * +- * Copyright (C) 2005 Steven Toth +- * +- * Support for KWorld DVB-S 100 by Vadim Catana +- * +- * Support for CX24123/CX24113-NIM by Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "cx24123.h" +- +-#define XTAL 10111000 +- +-static int force_band; +-module_param(force_band, int, 0644); +-MODULE_PARM_DESC(force_band, "Force a specific band select "\ +- "(1-9, default:off)."); +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-#define info(args...) do { printk(KERN_INFO "CX24123: " args); } while (0) +-#define err(args...) do { printk(KERN_ERR "CX24123: " args); } while (0) +- +-#define dprintk(args...) \ +- do { \ +- if (debug) { \ +- printk(KERN_DEBUG "CX24123: %s: ", __func__); \ +- printk(args); \ +- } \ +- } while (0) +- +-struct cx24123_state { +- struct i2c_adapter *i2c; +- const struct cx24123_config *config; +- +- struct dvb_frontend frontend; +- +- /* Some PLL specifics for tuning */ +- u32 VCAarg; +- u32 VGAarg; +- u32 bandselectarg; +- u32 pllarg; +- u32 FILTune; +- +- struct i2c_adapter tuner_i2c_adapter; +- +- u8 demod_rev; +- +- /* The Demod/Tuner can't easily provide these, we cache them */ +- u32 currentfreq; +- u32 currentsymbolrate; +-}; +- +-/* Various tuner defaults need to be established for a given symbol rate Sps */ +-static struct cx24123_AGC_val { +- u32 symbolrate_low; +- u32 symbolrate_high; +- u32 VCAprogdata; +- u32 VGAprogdata; +- u32 FILTune; +-} cx24123_AGC_vals[] = +-{ +- { +- .symbolrate_low = 1000000, +- .symbolrate_high = 4999999, +- /* the specs recommend other values for VGA offsets, +- but tests show they are wrong */ +- .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, +- .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x07, +- .FILTune = 0x27f /* 0.41 V */ +- }, +- { +- .symbolrate_low = 5000000, +- .symbolrate_high = 14999999, +- .VGAprogdata = (1 << 19) | (0x180 << 9) | 0x1e0, +- .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x1f, +- .FILTune = 0x317 /* 0.90 V */ +- }, +- { +- .symbolrate_low = 15000000, +- .symbolrate_high = 45000000, +- .VGAprogdata = (1 << 19) | (0x100 << 9) | 0x180, +- .VCAprogdata = (2 << 19) | (0x07 << 9) | 0x3f, +- .FILTune = 0x145 /* 2.70 V */ +- }, +-}; +- +-/* +- * Various tuner defaults need to be established for a given frequency kHz. +- * fixme: The bounds on the bands do not match the doc in real life. +- * fixme: Some of them have been moved, other might need adjustment. +- */ +-static struct cx24123_bandselect_val { +- u32 freq_low; +- u32 freq_high; +- u32 VCOdivider; +- u32 progdata; +-} cx24123_bandselect_vals[] = +-{ +- /* band 1 */ +- { +- .freq_low = 950000, +- .freq_high = 1074999, +- .VCOdivider = 4, +- .progdata = (0 << 19) | (0 << 9) | 0x40, +- }, +- +- /* band 2 */ +- { +- .freq_low = 1075000, +- .freq_high = 1177999, +- .VCOdivider = 4, +- .progdata = (0 << 19) | (0 << 9) | 0x80, +- }, +- +- /* band 3 */ +- { +- .freq_low = 1178000, +- .freq_high = 1295999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x01, +- }, +- +- /* band 4 */ +- { +- .freq_low = 1296000, +- .freq_high = 1431999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x02, +- }, +- +- /* band 5 */ +- { +- .freq_low = 1432000, +- .freq_high = 1575999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x04, +- }, +- +- /* band 6 */ +- { +- .freq_low = 1576000, +- .freq_high = 1717999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x08, +- }, +- +- /* band 7 */ +- { +- .freq_low = 1718000, +- .freq_high = 1855999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x10, +- }, +- +- /* band 8 */ +- { +- .freq_low = 1856000, +- .freq_high = 2035999, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x20, +- }, +- +- /* band 9 */ +- { +- .freq_low = 2036000, +- .freq_high = 2150000, +- .VCOdivider = 2, +- .progdata = (0 << 19) | (1 << 9) | 0x40, +- }, +-}; +- +-static struct { +- u8 reg; +- u8 data; +-} cx24123_regdata[] = +-{ +- {0x00, 0x03}, /* Reset system */ +- {0x00, 0x00}, /* Clear reset */ +- {0x03, 0x07}, /* QPSK, DVB, Auto Acquisition (default) */ +- {0x04, 0x10}, /* MPEG */ +- {0x05, 0x04}, /* MPEG */ +- {0x06, 0x31}, /* MPEG (default) */ +- {0x0b, 0x00}, /* Freq search start point (default) */ +- {0x0c, 0x00}, /* Demodulator sample gain (default) */ +- {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */ +- {0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */ +- {0x0f, 0xfe}, /* FEC search mask (all supported codes) */ +- {0x10, 0x01}, /* Default search inversion, no repeat (default) */ +- {0x16, 0x00}, /* Enable reading of frequency */ +- {0x17, 0x01}, /* Enable EsNO Ready Counter */ +- {0x1c, 0x80}, /* Enable error counter */ +- {0x20, 0x00}, /* Tuner burst clock rate = 500KHz */ +- {0x21, 0x15}, /* Tuner burst mode, word length = 0x15 */ +- {0x28, 0x00}, /* Enable FILTERV with positive pol., DiSEqC 2.x off */ +- {0x29, 0x00}, /* DiSEqC LNB_DC off */ +- {0x2a, 0xb0}, /* DiSEqC Parameters (default) */ +- {0x2b, 0x73}, /* DiSEqC Tone Frequency (default) */ +- {0x2c, 0x00}, /* DiSEqC Message (0x2c - 0x31) */ +- {0x2d, 0x00}, +- {0x2e, 0x00}, +- {0x2f, 0x00}, +- {0x30, 0x00}, +- {0x31, 0x00}, +- {0x32, 0x8c}, /* DiSEqC Parameters (default) */ +- {0x33, 0x00}, /* Interrupts off (0x33 - 0x34) */ +- {0x34, 0x00}, +- {0x35, 0x03}, /* DiSEqC Tone Amplitude (default) */ +- {0x36, 0x02}, /* DiSEqC Parameters (default) */ +- {0x37, 0x3a}, /* DiSEqC Parameters (default) */ +- {0x3a, 0x00}, /* Enable AGC accumulator (for signal strength) */ +- {0x44, 0x00}, /* Constellation (default) */ +- {0x45, 0x00}, /* Symbol count (default) */ +- {0x46, 0x0d}, /* Symbol rate estimator on (default) */ +- {0x56, 0xc1}, /* Error Counter = Viterbi BER */ +- {0x57, 0xff}, /* Error Counter Window (default) */ +- {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */ +- {0x67, 0x83}, /* Non-DCII symbol clock */ +-}; +- +-static int cx24123_i2c_writereg(struct cx24123_state *state, +- u8 i2c_addr, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 +- }; +- int err; +- +- /* printk(KERN_DEBUG "wr(%02x): %02x %02x\n", i2c_addr, reg, data); */ +- +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk("%s: writereg error(err == %i, reg == 0x%02x," +- " data == 0x%02x)\n", __func__, err, reg, data); +- return err; +- } +- +- return 0; +-} +- +-static int cx24123_i2c_readreg(struct cx24123_state *state, u8 i2c_addr, u8 reg) +-{ +- int ret; +- u8 b = 0; +- struct i2c_msg msg[] = { +- { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &b, .len = 1 } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- err("%s: reg=0x%x (error=%d)\n", __func__, reg, ret); +- return ret; +- } +- +- /* printk(KERN_DEBUG "rd(%02x): %02x %02x\n", i2c_addr, reg, b); */ +- +- return b; +-} +- +-#define cx24123_readreg(state, reg) \ +- cx24123_i2c_readreg(state, state->config->demod_address, reg) +-#define cx24123_writereg(state, reg, val) \ +- cx24123_i2c_writereg(state, state->config->demod_address, reg, val) +- +-static int cx24123_set_inversion(struct cx24123_state *state, +- fe_spectral_inversion_t inversion) +-{ +- u8 nom_reg = cx24123_readreg(state, 0x0e); +- u8 auto_reg = cx24123_readreg(state, 0x10); +- +- switch (inversion) { +- case INVERSION_OFF: +- dprintk("inversion off\n"); +- cx24123_writereg(state, 0x0e, nom_reg & ~0x80); +- cx24123_writereg(state, 0x10, auto_reg | 0x80); +- break; +- case INVERSION_ON: +- dprintk("inversion on\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x80); +- cx24123_writereg(state, 0x10, auto_reg | 0x80); +- break; +- case INVERSION_AUTO: +- dprintk("inversion auto\n"); +- cx24123_writereg(state, 0x10, auto_reg & ~0x80); +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int cx24123_get_inversion(struct cx24123_state *state, +- fe_spectral_inversion_t *inversion) +-{ +- u8 val; +- +- val = cx24123_readreg(state, 0x1b) >> 7; +- +- if (val == 0) { +- dprintk("read inversion off\n"); +- *inversion = INVERSION_OFF; +- } else { +- dprintk("read inversion on\n"); +- *inversion = INVERSION_ON; +- } +- +- return 0; +-} +- +-static int cx24123_set_fec(struct cx24123_state *state, fe_code_rate_t fec) +-{ +- u8 nom_reg = cx24123_readreg(state, 0x0e) & ~0x07; +- +- if ((fec < FEC_NONE) || (fec > FEC_AUTO)) +- fec = FEC_AUTO; +- +- /* Set the soft decision threshold */ +- if (fec == FEC_1_2) +- cx24123_writereg(state, 0x43, +- cx24123_readreg(state, 0x43) | 0x01); +- else +- cx24123_writereg(state, 0x43, +- cx24123_readreg(state, 0x43) & ~0x01); +- +- switch (fec) { +- case FEC_1_2: +- dprintk("set FEC to 1/2\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x01); +- cx24123_writereg(state, 0x0f, 0x02); +- break; +- case FEC_2_3: +- dprintk("set FEC to 2/3\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x02); +- cx24123_writereg(state, 0x0f, 0x04); +- break; +- case FEC_3_4: +- dprintk("set FEC to 3/4\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x03); +- cx24123_writereg(state, 0x0f, 0x08); +- break; +- case FEC_4_5: +- dprintk("set FEC to 4/5\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x04); +- cx24123_writereg(state, 0x0f, 0x10); +- break; +- case FEC_5_6: +- dprintk("set FEC to 5/6\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x05); +- cx24123_writereg(state, 0x0f, 0x20); +- break; +- case FEC_6_7: +- dprintk("set FEC to 6/7\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x06); +- cx24123_writereg(state, 0x0f, 0x40); +- break; +- case FEC_7_8: +- dprintk("set FEC to 7/8\n"); +- cx24123_writereg(state, 0x0e, nom_reg | 0x07); +- cx24123_writereg(state, 0x0f, 0x80); +- break; +- case FEC_AUTO: +- dprintk("set FEC to auto\n"); +- cx24123_writereg(state, 0x0f, 0xfe); +- break; +- default: +- return -EOPNOTSUPP; +- } +- +- return 0; +-} +- +-static int cx24123_get_fec(struct cx24123_state *state, fe_code_rate_t *fec) +-{ +- int ret; +- +- ret = cx24123_readreg(state, 0x1b); +- if (ret < 0) +- return ret; +- ret = ret & 0x07; +- +- switch (ret) { +- case 1: +- *fec = FEC_1_2; +- break; +- case 2: +- *fec = FEC_2_3; +- break; +- case 3: +- *fec = FEC_3_4; +- break; +- case 4: +- *fec = FEC_4_5; +- break; +- case 5: +- *fec = FEC_5_6; +- break; +- case 6: +- *fec = FEC_6_7; +- break; +- case 7: +- *fec = FEC_7_8; +- break; +- default: +- /* this can happen when there's no lock */ +- *fec = FEC_NONE; +- } +- +- return 0; +-} +- +-/* Approximation of closest integer of log2(a/b). It actually gives the +- lowest integer i such that 2^i >= round(a/b) */ +-static u32 cx24123_int_log2(u32 a, u32 b) +-{ +- u32 exp, nearest = 0; +- u32 div = a / b; +- if (a % b >= b / 2) +- ++div; +- if (div < (1 << 31)) { +- for (exp = 1; div > exp; nearest++) +- exp += exp; +- } +- return nearest; +-} +- +-static int cx24123_set_symbolrate(struct cx24123_state *state, u32 srate) +-{ +- u32 tmp, sample_rate, ratio, sample_gain; +- u8 pll_mult; +- +- /* check if symbol rate is within limits */ +- if ((srate > state->frontend.ops.info.symbol_rate_max) || +- (srate < state->frontend.ops.info.symbol_rate_min)) +- return -EOPNOTSUPP; +- +- /* choose the sampling rate high enough for the required operation, +- while optimizing the power consumed by the demodulator */ +- if (srate < (XTAL*2)/2) +- pll_mult = 2; +- else if (srate < (XTAL*3)/2) +- pll_mult = 3; +- else if (srate < (XTAL*4)/2) +- pll_mult = 4; +- else if (srate < (XTAL*5)/2) +- pll_mult = 5; +- else if (srate < (XTAL*6)/2) +- pll_mult = 6; +- else if (srate < (XTAL*7)/2) +- pll_mult = 7; +- else if (srate < (XTAL*8)/2) +- pll_mult = 8; +- else +- pll_mult = 9; +- +- +- sample_rate = pll_mult * XTAL; +- +- /* +- SYSSymbolRate[21:0] = (srate << 23) / sample_rate +- +- We have to use 32 bit unsigned arithmetic without precision loss. +- The maximum srate is 45000000 or 0x02AEA540. This number has +- only 6 clear bits on top, hence we can shift it left only 6 bits +- at a time. Borrowed from cx24110.c +- */ +- +- tmp = srate << 6; +- ratio = tmp / sample_rate; +- +- tmp = (tmp % sample_rate) << 6; +- ratio = (ratio << 6) + (tmp / sample_rate); +- +- tmp = (tmp % sample_rate) << 6; +- ratio = (ratio << 6) + (tmp / sample_rate); +- +- tmp = (tmp % sample_rate) << 5; +- ratio = (ratio << 5) + (tmp / sample_rate); +- +- +- cx24123_writereg(state, 0x01, pll_mult * 6); +- +- cx24123_writereg(state, 0x08, (ratio >> 16) & 0x3f); +- cx24123_writereg(state, 0x09, (ratio >> 8) & 0xff); +- cx24123_writereg(state, 0x0a, ratio & 0xff); +- +- /* also set the demodulator sample gain */ +- sample_gain = cx24123_int_log2(sample_rate, srate); +- tmp = cx24123_readreg(state, 0x0c) & ~0xe0; +- cx24123_writereg(state, 0x0c, tmp | sample_gain << 5); +- +- dprintk("srate=%d, ratio=0x%08x, sample_rate=%i sample_gain=%d\n", +- srate, ratio, sample_rate, sample_gain); +- +- return 0; +-} +- +-/* +- * Based on the required frequency and symbolrate, the tuner AGC has +- * to be configured and the correct band selected. +- * Calculate those values. +- */ +-static int cx24123_pll_calculate(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct cx24123_state *state = fe->demodulator_priv; +- u32 ndiv = 0, adiv = 0, vco_div = 0; +- int i = 0; +- int pump = 2; +- int band = 0; +- int num_bands = ARRAY_SIZE(cx24123_bandselect_vals); +- struct cx24123_bandselect_val *bsv = NULL; +- struct cx24123_AGC_val *agcv = NULL; +- +- /* Defaults for low freq, low rate */ +- state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; +- state->VGAarg = cx24123_AGC_vals[0].VGAprogdata; +- state->bandselectarg = cx24123_bandselect_vals[0].progdata; +- vco_div = cx24123_bandselect_vals[0].VCOdivider; +- +- /* For the given symbol rate, determine the VCA, VGA and +- * FILTUNE programming bits */ +- for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) { +- agcv = &cx24123_AGC_vals[i]; +- if ((agcv->symbolrate_low <= p->symbol_rate) && +- (agcv->symbolrate_high >= p->symbol_rate)) { +- state->VCAarg = agcv->VCAprogdata; +- state->VGAarg = agcv->VGAprogdata; +- state->FILTune = agcv->FILTune; +- } +- } +- +- /* determine the band to use */ +- if (force_band < 1 || force_band > num_bands) { +- for (i = 0; i < num_bands; i++) { +- bsv = &cx24123_bandselect_vals[i]; +- if ((bsv->freq_low <= p->frequency) && +- (bsv->freq_high >= p->frequency)) +- band = i; +- } +- } else +- band = force_band - 1; +- +- state->bandselectarg = cx24123_bandselect_vals[band].progdata; +- vco_div = cx24123_bandselect_vals[band].VCOdivider; +- +- /* determine the charge pump current */ +- if (p->frequency < (cx24123_bandselect_vals[band].freq_low + +- cx24123_bandselect_vals[band].freq_high) / 2) +- pump = 0x01; +- else +- pump = 0x02; +- +- /* Determine the N/A dividers for the requested lband freq (in kHz). */ +- /* Note: the reference divider R=10, frequency is in KHz, +- * XTAL is in Hz */ +- ndiv = (((p->frequency * vco_div * 10) / +- (2 * XTAL / 1000)) / 32) & 0x1ff; +- adiv = (((p->frequency * vco_div * 10) / +- (2 * XTAL / 1000)) % 32) & 0x1f; +- +- if (adiv == 0 && ndiv > 0) +- ndiv--; +- +- /* control bits 11, refdiv 11, charge pump polarity 1, +- * charge pump current, ndiv, adiv */ +- state->pllarg = (3 << 19) | (3 << 17) | (1 << 16) | +- (pump << 14) | (ndiv << 5) | adiv; +- +- return 0; +-} +- +-/* +- * Tuner data is 21 bits long, must be left-aligned in data. +- * Tuner cx24109 is written through a dedicated 3wire interface +- * on the demod chip. +- */ +-static int cx24123_pll_writereg(struct dvb_frontend *fe, u32 data) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- unsigned long timeout; +- +- dprintk("pll writereg called, data=0x%08x\n", data); +- +- /* align the 21 bytes into to bit23 boundary */ +- data = data << 3; +- +- /* Reset the demod pll word length to 0x15 bits */ +- cx24123_writereg(state, 0x21, 0x15); +- +- /* write the msb 8 bits, wait for the send to be completed */ +- timeout = jiffies + msecs_to_jiffies(40); +- cx24123_writereg(state, 0x22, (data >> 16) & 0xff); +- while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { +- if (time_after(jiffies, timeout)) { +- err("%s: demodulator is not responding, "\ +- "possibly hung, aborting.\n", __func__); +- return -EREMOTEIO; +- } +- msleep(10); +- } +- +- /* send another 8 bytes, wait for the send to be completed */ +- timeout = jiffies + msecs_to_jiffies(40); +- cx24123_writereg(state, 0x22, (data >> 8) & 0xff); +- while ((cx24123_readreg(state, 0x20) & 0x40) == 0) { +- if (time_after(jiffies, timeout)) { +- err("%s: demodulator is not responding, "\ +- "possibly hung, aborting.\n", __func__); +- return -EREMOTEIO; +- } +- msleep(10); +- } +- +- /* send the lower 5 bits of this byte, padded with 3 LBB, +- * wait for the send to be completed */ +- timeout = jiffies + msecs_to_jiffies(40); +- cx24123_writereg(state, 0x22, (data) & 0xff); +- while ((cx24123_readreg(state, 0x20) & 0x80)) { +- if (time_after(jiffies, timeout)) { +- err("%s: demodulator is not responding," \ +- "possibly hung, aborting.\n", __func__); +- return -EREMOTEIO; +- } +- msleep(10); +- } +- +- /* Trigger the demod to configure the tuner */ +- cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) | 2); +- cx24123_writereg(state, 0x20, cx24123_readreg(state, 0x20) & 0xfd); +- +- return 0; +-} +- +-static int cx24123_pll_tune(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct cx24123_state *state = fe->demodulator_priv; +- u8 val; +- +- dprintk("frequency=%i\n", p->frequency); +- +- if (cx24123_pll_calculate(fe) != 0) { +- err("%s: cx24123_pll_calcutate failed\n", __func__); +- return -EINVAL; +- } +- +- /* Write the new VCO/VGA */ +- cx24123_pll_writereg(fe, state->VCAarg); +- cx24123_pll_writereg(fe, state->VGAarg); +- +- /* Write the new bandselect and pll args */ +- cx24123_pll_writereg(fe, state->bandselectarg); +- cx24123_pll_writereg(fe, state->pllarg); +- +- /* set the FILTUNE voltage */ +- val = cx24123_readreg(state, 0x28) & ~0x3; +- cx24123_writereg(state, 0x27, state->FILTune >> 2); +- cx24123_writereg(state, 0x28, val | (state->FILTune & 0x3)); +- +- dprintk("pll tune VCA=%d, band=%d, pll=%d\n", state->VCAarg, +- state->bandselectarg, state->pllarg); +- +- return 0; +-} +- +- +-/* +- * 0x23: +- * [7:7] = BTI enabled +- * [6:6] = I2C repeater enabled +- * [5:5] = I2C repeater start +- * [0:0] = BTI start +- */ +- +-/* mode == 1 -> i2c-repeater, 0 -> bti */ +-static int cx24123_repeater_mode(struct cx24123_state *state, u8 mode, u8 start) +-{ +- u8 r = cx24123_readreg(state, 0x23) & 0x1e; +- if (mode) +- r |= (1 << 6) | (start << 5); +- else +- r |= (1 << 7) | (start); +- return cx24123_writereg(state, 0x23, r); +-} +- +-static int cx24123_initfe(struct dvb_frontend *fe) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- int i; +- +- dprintk("init frontend\n"); +- +- /* Configure the demod to a good set of defaults */ +- for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++) +- cx24123_writereg(state, cx24123_regdata[i].reg, +- cx24123_regdata[i].data); +- +- /* Set the LNB polarity */ +- if (state->config->lnb_polarity) +- cx24123_writereg(state, 0x32, +- cx24123_readreg(state, 0x32) | 0x02); +- +- if (state->config->dont_use_pll) +- cx24123_repeater_mode(state, 1, 0); +- +- return 0; +-} +- +-static int cx24123_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- u8 val; +- +- val = cx24123_readreg(state, 0x29) & ~0x40; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- dprintk("setting voltage 13V\n"); +- return cx24123_writereg(state, 0x29, val & 0x7f); +- case SEC_VOLTAGE_18: +- dprintk("setting voltage 18V\n"); +- return cx24123_writereg(state, 0x29, val | 0x80); +- case SEC_VOLTAGE_OFF: +- /* already handled in cx88-dvb */ +- return 0; +- default: +- return -EINVAL; +- }; +- +- return 0; +-} +- +-/* wait for diseqc queue to become ready (or timeout) */ +-static void cx24123_wait_for_diseqc(struct cx24123_state *state) +-{ +- unsigned long timeout = jiffies + msecs_to_jiffies(200); +- while (!(cx24123_readreg(state, 0x29) & 0x40)) { +- if (time_after(jiffies, timeout)) { +- err("%s: diseqc queue not ready, " \ +- "command may be lost.\n", __func__); +- break; +- } +- msleep(10); +- } +-} +- +-static int cx24123_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *cmd) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- int i, val, tone; +- +- dprintk("\n"); +- +- /* stop continuous tone if enabled */ +- tone = cx24123_readreg(state, 0x29); +- if (tone & 0x10) +- cx24123_writereg(state, 0x29, tone & ~0x50); +- +- /* wait for diseqc queue ready */ +- cx24123_wait_for_diseqc(state); +- +- /* select tone mode */ +- cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); +- +- for (i = 0; i < cmd->msg_len; i++) +- cx24123_writereg(state, 0x2C + i, cmd->msg[i]); +- +- val = cx24123_readreg(state, 0x29); +- cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40) | +- ((cmd->msg_len-3) & 3)); +- +- /* wait for diseqc message to finish sending */ +- cx24123_wait_for_diseqc(state); +- +- /* restart continuous tone if enabled */ +- if (tone & 0x10) +- cx24123_writereg(state, 0x29, tone & ~0x40); +- +- return 0; +-} +- +-static int cx24123_diseqc_send_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- int val, tone; +- +- dprintk("\n"); +- +- /* stop continuous tone if enabled */ +- tone = cx24123_readreg(state, 0x29); +- if (tone & 0x10) +- cx24123_writereg(state, 0x29, tone & ~0x50); +- +- /* wait for diseqc queue ready */ +- cx24123_wait_for_diseqc(state); +- +- /* select tone mode */ +- cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) | 0x4); +- msleep(30); +- val = cx24123_readreg(state, 0x29); +- if (burst == SEC_MINI_A) +- cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x00)); +- else if (burst == SEC_MINI_B) +- cx24123_writereg(state, 0x29, ((val & 0x90) | 0x40 | 0x08)); +- else +- return -EINVAL; +- +- cx24123_wait_for_diseqc(state); +- cx24123_writereg(state, 0x2a, cx24123_readreg(state, 0x2a) & 0xfb); +- +- /* restart continuous tone if enabled */ +- if (tone & 0x10) +- cx24123_writereg(state, 0x29, tone & ~0x40); +- +- return 0; +-} +- +-static int cx24123_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- int sync = cx24123_readreg(state, 0x14); +- +- *status = 0; +- if (state->config->dont_use_pll) { +- u32 tun_status = 0; +- if (fe->ops.tuner_ops.get_status) +- fe->ops.tuner_ops.get_status(fe, &tun_status); +- if (tun_status & TUNER_STATUS_LOCKED) +- *status |= FE_HAS_SIGNAL; +- } else { +- int lock = cx24123_readreg(state, 0x20); +- if (lock & 0x01) +- *status |= FE_HAS_SIGNAL; +- } +- +- if (sync & 0x02) +- *status |= FE_HAS_CARRIER; /* Phase locked */ +- if (sync & 0x04) +- *status |= FE_HAS_VITERBI; +- +- /* Reed-Solomon Status */ +- if (sync & 0x08) +- *status |= FE_HAS_SYNC; +- if (sync & 0x80) +- *status |= FE_HAS_LOCK; /*Full Sync */ +- +- return 0; +-} +- +-/* +- * Configured to return the measurement of errors in blocks, +- * because no UCBLOCKS value is available, so this value doubles up +- * to satisfy both measurements. +- */ +-static int cx24123_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- +- /* The true bit error rate is this value divided by +- the window size (set as 256 * 255) */ +- *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) | +- (cx24123_readreg(state, 0x1d) << 8 | +- cx24123_readreg(state, 0x1e)); +- +- dprintk("BER = %d\n", *ber); +- +- return 0; +-} +- +-static int cx24123_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- +- /* larger = better */ +- *signal_strength = cx24123_readreg(state, 0x3b) << 8; +- +- dprintk("Signal strength = %d\n", *signal_strength); +- +- return 0; +-} +- +-static int cx24123_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- +- /* Inverted raw Es/N0 count, totally bogus but better than the +- BER threshold. */ +- *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) | +- (u16)cx24123_readreg(state, 0x19)); +- +- dprintk("read S/N index = %d\n", *snr); +- +- return 0; +-} +- +-static int cx24123_set_frontend(struct dvb_frontend *fe) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- dprintk("\n"); +- +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- +- state->currentfreq = p->frequency; +- state->currentsymbolrate = p->symbol_rate; +- +- cx24123_set_inversion(state, p->inversion); +- cx24123_set_fec(state, p->fec_inner); +- cx24123_set_symbolrate(state, p->symbol_rate); +- +- if (!state->config->dont_use_pll) +- cx24123_pll_tune(fe); +- else if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- else +- err("it seems I don't have a tuner..."); +- +- /* Enable automatic acquisition and reset cycle */ +- cx24123_writereg(state, 0x03, (cx24123_readreg(state, 0x03) | 0x07)); +- cx24123_writereg(state, 0x00, 0x10); +- cx24123_writereg(state, 0x00, 0); +- +- if (state->config->agc_callback) +- state->config->agc_callback(fe); +- +- return 0; +-} +- +-static int cx24123_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct cx24123_state *state = fe->demodulator_priv; +- +- dprintk("\n"); +- +- if (cx24123_get_inversion(state, &p->inversion) != 0) { +- err("%s: Failed to get inversion status\n", __func__); +- return -EREMOTEIO; +- } +- if (cx24123_get_fec(state, &p->fec_inner) != 0) { +- err("%s: Failed to get fec status\n", __func__); +- return -EREMOTEIO; +- } +- p->frequency = state->currentfreq; +- p->symbol_rate = state->currentsymbolrate; +- +- return 0; +-} +- +-static int cx24123_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- u8 val; +- +- /* wait for diseqc queue ready */ +- cx24123_wait_for_diseqc(state); +- +- val = cx24123_readreg(state, 0x29) & ~0x40; +- +- switch (tone) { +- case SEC_TONE_ON: +- dprintk("setting tone on\n"); +- return cx24123_writereg(state, 0x29, val | 0x10); +- case SEC_TONE_OFF: +- dprintk("setting tone off\n"); +- return cx24123_writereg(state, 0x29, val & 0xef); +- default: +- err("CASE reached default with tone=%d\n", tone); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int cx24123_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status) +-{ +- int retval = 0; +- +- if (re_tune) +- retval = cx24123_set_frontend(fe); +- +- if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) +- cx24123_read_status(fe, status); +- *delay = HZ/10; +- +- return retval; +-} +- +-static int cx24123_get_algo(struct dvb_frontend *fe) +-{ +- return 1; /* FE_ALGO_HW */ +-} +- +-static void cx24123_release(struct dvb_frontend *fe) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- dprintk("\n"); +- i2c_del_adapter(&state->tuner_i2c_adapter); +- kfree(state); +-} +- +-static int cx24123_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct cx24123_state *state = i2c_get_adapdata(i2c_adap); +- /* this repeater closes after the first stop */ +- cx24123_repeater_mode(state, 1, 1); +- return i2c_transfer(state->i2c, msg, num); +-} +- +-static u32 cx24123_tuner_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm cx24123_tuner_i2c_algo = { +- .master_xfer = cx24123_tuner_i2c_tuner_xfer, +- .functionality = cx24123_tuner_i2c_func, +-}; +- +-struct i2c_adapter * +- cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- struct cx24123_state *state = fe->demodulator_priv; +- return &state->tuner_i2c_adapter; +-} +-EXPORT_SYMBOL(cx24123_get_tuner_i2c_adapter); +- +-static struct dvb_frontend_ops cx24123_ops; +- +-struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, +- struct i2c_adapter *i2c) +-{ +- /* allocate memory for the internal state */ +- struct cx24123_state *state = +- kzalloc(sizeof(struct cx24123_state), GFP_KERNEL); +- +- dprintk("\n"); +- if (state == NULL) { +- err("Unable to kzalloc\n"); +- goto error; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- state->demod_rev = cx24123_readreg(state, 0x00); +- switch (state->demod_rev) { +- case 0xe1: +- info("detected CX24123C\n"); +- break; +- case 0xd1: +- info("detected CX24123\n"); +- break; +- default: +- err("wrong demod revision: %x\n", state->demod_rev); +- goto error; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &cx24123_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- /* create tuner i2c adapter */ +- if (config->dont_use_pll) +- cx24123_repeater_mode(state, 1, 0); +- +- strlcpy(state->tuner_i2c_adapter.name, "CX24123 tuner I2C bus", +- sizeof(state->tuner_i2c_adapter.name)); +- state->tuner_i2c_adapter.algo = &cx24123_tuner_i2c_algo; +- state->tuner_i2c_adapter.algo_data = NULL; +- i2c_set_adapdata(&state->tuner_i2c_adapter, state); +- if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { +- err("tuner i2c bus could not be initialized\n"); +- goto error; +- } +- +- return &state->frontend; +- +-error: +- kfree(state); +- +- return NULL; +-} +-EXPORT_SYMBOL(cx24123_attach); +- +-static struct dvb_frontend_ops cx24123_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Conexant CX24123/CX24109", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1011, /* kHz for QPSK frontends */ +- .frequency_tolerance = 5000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_RECOVER +- }, +- +- .release = cx24123_release, +- +- .init = cx24123_initfe, +- .set_frontend = cx24123_set_frontend, +- .get_frontend = cx24123_get_frontend, +- .read_status = cx24123_read_status, +- .read_ber = cx24123_read_ber, +- .read_signal_strength = cx24123_read_signal_strength, +- .read_snr = cx24123_read_snr, +- .diseqc_send_master_cmd = cx24123_send_diseqc_msg, +- .diseqc_send_burst = cx24123_diseqc_send_burst, +- .set_tone = cx24123_set_tone, +- .set_voltage = cx24123_set_voltage, +- .tune = cx24123_tune, +- .get_frontend_algo = cx24123_get_algo, +-}; +- +-MODULE_DESCRIPTION("DVB Frontend module for Conexant " \ +- "CX24123/CX24109/CX24113 hardware"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +- +diff --git a/drivers/media/dvb/frontends/cx24123.h b/drivers/media/dvb/frontends/cx24123.h +deleted file mode 100644 +index 51ae866..0000000 +--- a/drivers/media/dvb/frontends/cx24123.h ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- Conexant cx24123/cx24109 - DVB QPSK Satellite demod/tuner driver +- +- Copyright (C) 2005 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef CX24123_H +-#define CX24123_H +- +-#include +- +-struct cx24123_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* Need to set device param for start_dma */ +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +- +- /* 0 = LNB voltage normal, 1 = LNB voltage inverted */ +- int lnb_polarity; +- +- /* this device has another tuner */ +- u8 dont_use_pll; +- void (*agc_callback) (struct dvb_frontend *); +-}; +- +-#if defined(CONFIG_DVB_CX24123) || (defined(CONFIG_DVB_CX24123_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, +- struct i2c_adapter *i2c); +-extern struct i2c_adapter *cx24123_get_tuner_i2c_adapter(struct dvb_frontend *); +-#else +-static inline struct dvb_frontend *cx24123_attach( +- const struct cx24123_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static struct i2c_adapter * +- cx24123_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* CX24123_H */ +diff --git a/drivers/media/dvb/frontends/cxd2820r.h b/drivers/media/dvb/frontends/cxd2820r.h +deleted file mode 100644 +index 5aa306e..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r.h ++++ /dev/null +@@ -1,94 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#ifndef CXD2820R_H +-#define CXD2820R_H +- +-#include +- +-#define CXD2820R_GPIO_D (0 << 0) /* disable */ +-#define CXD2820R_GPIO_E (1 << 0) /* enable */ +-#define CXD2820R_GPIO_O (0 << 1) /* output */ +-#define CXD2820R_GPIO_I (1 << 1) /* input */ +-#define CXD2820R_GPIO_L (0 << 2) /* output low */ +-#define CXD2820R_GPIO_H (1 << 2) /* output high */ +- +-#define CXD2820R_TS_SERIAL 0x08 +-#define CXD2820R_TS_SERIAL_MSB 0x28 +-#define CXD2820R_TS_PARALLEL 0x30 +-#define CXD2820R_TS_PARALLEL_MSB 0x70 +- +-struct cxd2820r_config { +- /* Demodulator I2C address. +- * Driver determines DVB-C slave I2C address automatically from master +- * address. +- * Default: none, must set +- * Values: 0x6c, 0x6d +- */ +- u8 i2c_address; +- +- /* TS output mode. +- * Default: none, must set. +- * Values: +- */ +- u8 ts_mode; +- +- /* IF AGC polarity. +- * Default: 0 +- * Values: 0, 1 +- */ +- bool if_agc_polarity; +- +- /* Spectrum inversion. +- * Default: 0 +- * Values: 0, 1 +- */ +- bool spec_inv; +- +- /* GPIOs for all used modes. +- * Default: none, disabled +- * Values: +- */ +- u8 gpio_dvbt[3]; +- u8 gpio_dvbt2[3]; +- u8 gpio_dvbc[3]; +-}; +- +- +-#if defined(CONFIG_DVB_CXD2820R) || \ +- (defined(CONFIG_DVB_CXD2820R_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *cxd2820r_attach( +- const struct cxd2820r_config *config, +- struct i2c_adapter *i2c +-); +-#else +-static inline struct dvb_frontend *cxd2820r_attach( +- const struct cxd2820r_config *config, +- struct i2c_adapter *i2c +-) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif +- +-#endif /* CXD2820R_H */ +diff --git a/drivers/media/dvb/frontends/cxd2820r_c.c b/drivers/media/dvb/frontends/cxd2820r_c.c +deleted file mode 100644 +index 9454049..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r_c.c ++++ /dev/null +@@ -1,346 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#include "cxd2820r_priv.h" +- +-int cxd2820r_set_frontend_c(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i; +- u8 buf[2]; +- u32 if_freq; +- u16 if_ctl; +- u64 num; +- struct reg_val_mask tab[] = { +- { 0x00080, 0x01, 0xff }, +- { 0x00081, 0x05, 0xff }, +- { 0x00085, 0x07, 0xff }, +- { 0x00088, 0x01, 0xff }, +- +- { 0x00082, 0x20, 0x60 }, +- { 0x1016a, 0x48, 0xff }, +- { 0x100a5, 0x00, 0x01 }, +- { 0x10020, 0x06, 0x07 }, +- { 0x10059, 0x50, 0xff }, +- { 0x10087, 0x0c, 0x3c }, +- { 0x1008b, 0x07, 0xff }, +- { 0x1001f, priv->cfg.if_agc_polarity << 7, 0x80 }, +- { 0x10070, priv->cfg.ts_mode, 0xff }, +- }; +- +- dbg("%s: RF=%d SR=%d", __func__, c->frequency, c->symbol_rate); +- +- /* update GPIOs */ +- ret = cxd2820r_gpio(fe); +- if (ret) +- goto error; +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- if (priv->delivery_system != SYS_DVBC_ANNEX_A) { +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, +- tab[i].val, tab[i].mask); +- if (ret) +- goto error; +- } +- } +- +- priv->delivery_system = SYS_DVBC_ANNEX_A; +- priv->ber_running = 0; /* tune stops BER counter */ +- +- /* program IF frequency */ +- if (fe->ops.tuner_ops.get_if_frequency) { +- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); +- if (ret) +- goto error; +- } else +- if_freq = 0; +- +- dbg("%s: if_freq=%d", __func__, if_freq); +- +- num = if_freq / 1000; /* Hz => kHz */ +- num *= 0x4000; +- if_ctl = cxd2820r_div_u64_round_closest(num, 41000); +- buf[0] = (if_ctl >> 8) & 0x3f; +- buf[1] = (if_ctl >> 0) & 0xff; +- +- ret = cxd2820r_wr_regs(priv, 0x10042, buf, 2); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_get_frontend_c(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret; +- u8 buf[2]; +- +- ret = cxd2820r_rd_regs(priv, 0x1001a, buf, 2); +- if (ret) +- goto error; +- +- c->symbol_rate = 2500 * ((buf[0] & 0x0f) << 8 | buf[1]); +- +- ret = cxd2820r_rd_reg(priv, 0x10019, &buf[0]); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 0) & 0x03) { +- case 0: +- c->modulation = QAM_16; +- break; +- case 1: +- c->modulation = QAM_32; +- break; +- case 2: +- c->modulation = QAM_64; +- break; +- case 3: +- c->modulation = QAM_128; +- break; +- case 4: +- c->modulation = QAM_256; +- break; +- } +- +- switch ((buf[0] >> 7) & 0x01) { +- case 0: +- c->inversion = INVERSION_OFF; +- break; +- case 1: +- c->inversion = INVERSION_ON; +- break; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[3], start_ber = 0; +- *ber = 0; +- +- if (priv->ber_running) { +- ret = cxd2820r_rd_regs(priv, 0x10076, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { +- *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; +- start_ber = 1; +- } +- } else { +- priv->ber_running = 1; +- start_ber = 1; +- } +- +- if (start_ber) { +- /* (re)start BER */ +- ret = cxd2820r_wr_reg(priv, 0x10079, 0x01); +- if (ret) +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- u16 tmp; +- +- ret = cxd2820r_rd_regs(priv, 0x10049, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- tmp = (buf[0] & 0x03) << 8 | buf[1]; +- tmp = (~tmp & 0x03ff); +- +- if (tmp == 512) +- /* ~no signal */ +- tmp = 0; +- else if (tmp > 350) +- tmp = 350; +- +- /* scale value to 0x0000-0xffff */ +- *strength = tmp * 0xffff / (350-0); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 tmp; +- unsigned int A, B; +- /* report SNR in dB * 10 */ +- +- ret = cxd2820r_rd_reg(priv, 0x10019, &tmp); +- if (ret) +- goto error; +- +- if (((tmp >> 0) & 0x03) % 2) { +- A = 875; +- B = 650; +- } else { +- A = 950; +- B = 760; +- } +- +- ret = cxd2820r_rd_reg(priv, 0x1004d, &tmp); +- if (ret) +- goto error; +- +- #define CXD2820R_LOG2_E_24 24204406 /* log2(e) << 24 */ +- if (tmp) +- *snr = A * (intlog2(B / tmp) >> 5) / (CXD2820R_LOG2_E_24 >> 5) +- / 10; +- else +- *snr = 0; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- /* no way to read ? */ +- return 0; +-} +- +-int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- *status = 0; +- +- ret = cxd2820r_rd_regs(priv, 0x10088, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- if (((buf[0] >> 0) & 0x01) == 1) { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC; +- +- if (((buf[1] >> 3) & 0x01) == 1) { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- } +- } +- +- dbg("%s: lock=%02x %02x", __func__, buf[0], buf[1]); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_init_c(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- +- ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_sleep_c(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret, i; +- struct reg_val_mask tab[] = { +- { 0x000ff, 0x1f, 0xff }, +- { 0x00085, 0x00, 0xff }, +- { 0x00088, 0x01, 0xff }, +- { 0x00081, 0x00, 0xff }, +- { 0x00080, 0x00, 0xff }, +- }; +- +- dbg("%s", __func__); +- +- priv->delivery_system = SYS_UNDEFINED; +- +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, +- tab[i].mask); +- if (ret) +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s) +-{ +- s->min_delay_ms = 500; +- s->step_size = 0; /* no zigzag */ +- s->max_drift = 0; +- +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c +deleted file mode 100644 +index 5c7c2aa..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r_core.c ++++ /dev/null +@@ -1,646 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#include "cxd2820r_priv.h" +- +-int cxd2820r_debug; +-module_param_named(debug, cxd2820r_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-/* write multiple registers */ +-static int cxd2820r_wr_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, +- u8 *val, int len) +-{ +- int ret; +- u8 buf[len+1]; +- struct i2c_msg msg[1] = { +- { +- .addr = i2c, +- .flags = 0, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- buf[0] = reg; +- memcpy(&buf[1], val, len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* read multiple registers */ +-static int cxd2820r_rd_regs_i2c(struct cxd2820r_priv *priv, u8 i2c, u8 reg, +- u8 *val, int len) +-{ +- int ret; +- u8 buf[len]; +- struct i2c_msg msg[2] = { +- { +- .addr = i2c, +- .flags = 0, +- .len = 1, +- .buf = ®, +- }, { +- .addr = i2c, +- .flags = I2C_M_RD, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- memcpy(val, buf, len); +- ret = 0; +- } else { +- warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* write multiple registers */ +-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, +- int len) +-{ +- int ret; +- u8 i2c_addr; +- u8 reg = (reginfo >> 0) & 0xff; +- u8 bank = (reginfo >> 8) & 0xff; +- u8 i2c = (reginfo >> 16) & 0x01; +- +- /* select I2C */ +- if (i2c) +- i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ +- else +- i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ +- +- /* switch bank if needed */ +- if (bank != priv->bank[i2c]) { +- ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); +- if (ret) +- return ret; +- priv->bank[i2c] = bank; +- } +- return cxd2820r_wr_regs_i2c(priv, i2c_addr, reg, val, len); +-} +- +-/* read multiple registers */ +-int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, +- int len) +-{ +- int ret; +- u8 i2c_addr; +- u8 reg = (reginfo >> 0) & 0xff; +- u8 bank = (reginfo >> 8) & 0xff; +- u8 i2c = (reginfo >> 16) & 0x01; +- +- /* select I2C */ +- if (i2c) +- i2c_addr = priv->cfg.i2c_address | (1 << 1); /* DVB-C */ +- else +- i2c_addr = priv->cfg.i2c_address; /* DVB-T/T2 */ +- +- /* switch bank if needed */ +- if (bank != priv->bank[i2c]) { +- ret = cxd2820r_wr_regs_i2c(priv, i2c_addr, 0x00, &bank, 1); +- if (ret) +- return ret; +- priv->bank[i2c] = bank; +- } +- return cxd2820r_rd_regs_i2c(priv, i2c_addr, reg, val, len); +-} +- +-/* write single register */ +-int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val) +-{ +- return cxd2820r_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +-int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val) +-{ +- return cxd2820r_rd_regs(priv, reg, val, 1); +-} +- +-/* write single register with mask */ +-int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, +- u8 mask) +-{ +- int ret; +- u8 tmp; +- +- /* no need for read if whole reg is written */ +- if (mask != 0xff) { +- ret = cxd2820r_rd_reg(priv, reg, &tmp); +- if (ret) +- return ret; +- +- val &= mask; +- tmp &= ~mask; +- val |= tmp; +- } +- +- return cxd2820r_wr_reg(priv, reg, val); +-} +- +-int cxd2820r_gpio(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret, i; +- u8 *gpio, tmp0, tmp1; +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- gpio = priv->cfg.gpio_dvbt; +- break; +- case SYS_DVBT2: +- gpio = priv->cfg.gpio_dvbt2; +- break; +- case SYS_DVBC_ANNEX_AC: +- gpio = priv->cfg.gpio_dvbc; +- break; +- default: +- ret = -EINVAL; +- goto error; +- } +- +- /* update GPIOs only when needed */ +- if (!memcmp(gpio, priv->gpio, sizeof(priv->gpio))) +- return 0; +- +- tmp0 = 0x00; +- tmp1 = 0x00; +- for (i = 0; i < sizeof(priv->gpio); i++) { +- /* enable / disable */ +- if (gpio[i] & CXD2820R_GPIO_E) +- tmp0 |= (2 << 6) >> (2 * i); +- else +- tmp0 |= (1 << 6) >> (2 * i); +- +- /* input / output */ +- if (gpio[i] & CXD2820R_GPIO_I) +- tmp1 |= (1 << (3 + i)); +- else +- tmp1 |= (0 << (3 + i)); +- +- /* high / low */ +- if (gpio[i] & CXD2820R_GPIO_H) +- tmp1 |= (1 << (0 + i)); +- else +- tmp1 |= (0 << (0 + i)); +- +- dbg("%s: GPIO i=%d %02x %02x", __func__, i, tmp0, tmp1); +- } +- +- dbg("%s: wr gpio=%02x %02x", __func__, tmp0, tmp1); +- +- /* write bits [7:2] */ +- ret = cxd2820r_wr_reg_mask(priv, 0x00089, tmp0, 0xfc); +- if (ret) +- goto error; +- +- /* write bits [5:0] */ +- ret = cxd2820r_wr_reg_mask(priv, 0x0008e, tmp1, 0x3f); +- if (ret) +- goto error; +- +- memcpy(priv->gpio, gpio, sizeof(priv->gpio)); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-/* 64 bit div with round closest, like DIV_ROUND_CLOSEST but 64 bit */ +-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor) +-{ +- return div_u64(dividend + (divisor / 2), divisor); +-} +- +-static int cxd2820r_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (c->delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_init_t(fe); +- if (ret < 0) +- goto err; +- ret = cxd2820r_set_frontend_t(fe); +- if (ret < 0) +- goto err; +- break; +- case SYS_DVBT2: +- ret = cxd2820r_init_t(fe); +- if (ret < 0) +- goto err; +- ret = cxd2820r_set_frontend_t2(fe); +- if (ret < 0) +- goto err; +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_init_c(fe); +- if (ret < 0) +- goto err; +- ret = cxd2820r_set_frontend_c(fe); +- if (ret < 0) +- goto err; +- break; +- default: +- dbg("%s: error state=%d", __func__, fe->dtv_property_cache.delivery_system); +- ret = -EINVAL; +- break; +- } +-err: +- return ret; +-} +-static int cxd2820r_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_read_status_t(fe, status); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_read_status_t2(fe, status); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_read_status_c(fe, status); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_get_frontend(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- +- if (priv->delivery_system == SYS_UNDEFINED) +- return 0; +- +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_get_frontend_t(fe); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_get_frontend_t2(fe); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_get_frontend_c(fe); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_read_ber_t(fe, ber); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_read_ber_t2(fe, ber); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_read_ber_c(fe, ber); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_read_signal_strength_t(fe, strength); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_read_signal_strength_t2(fe, strength); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_read_signal_strength_c(fe, strength); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_read_snr_t(fe, snr); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_read_snr_t2(fe, snr); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_read_snr_c(fe, snr); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_read_ucblocks_t(fe, ucblocks); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_read_ucblocks_t2(fe, ucblocks); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_read_ucblocks_c(fe, ucblocks); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_init(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int cxd2820r_sleep(struct dvb_frontend *fe) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_sleep_t(fe); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_sleep_t2(fe); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_sleep_c(fe); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static int cxd2820r_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s) +-{ +- int ret; +- +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- switch (fe->dtv_property_cache.delivery_system) { +- case SYS_DVBT: +- ret = cxd2820r_get_tune_settings_t(fe, s); +- break; +- case SYS_DVBT2: +- ret = cxd2820r_get_tune_settings_t2(fe, s); +- break; +- case SYS_DVBC_ANNEX_A: +- ret = cxd2820r_get_tune_settings_c(fe, s); +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- +-static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i; +- fe_status_t status = 0; +- dbg("%s: delsys=%d", __func__, fe->dtv_property_cache.delivery_system); +- +- /* switch between DVB-T and DVB-T2 when tune fails */ +- if (priv->last_tune_failed) { +- if (priv->delivery_system == SYS_DVBT) { +- ret = cxd2820r_sleep_t(fe); +- if (ret) +- goto error; +- +- c->delivery_system = SYS_DVBT2; +- } else if (priv->delivery_system == SYS_DVBT2) { +- ret = cxd2820r_sleep_t2(fe); +- if (ret) +- goto error; +- +- c->delivery_system = SYS_DVBT; +- } +- } +- +- /* set frontend */ +- ret = cxd2820r_set_frontend(fe); +- if (ret) +- goto error; +- +- +- /* frontend lock wait loop count */ +- switch (priv->delivery_system) { +- case SYS_DVBT: +- case SYS_DVBC_ANNEX_A: +- i = 20; +- break; +- case SYS_DVBT2: +- i = 40; +- break; +- case SYS_UNDEFINED: +- default: +- i = 0; +- break; +- } +- +- /* wait frontend lock */ +- for (; i > 0; i--) { +- dbg("%s: LOOP=%d", __func__, i); +- msleep(50); +- ret = cxd2820r_read_status(fe, &status); +- if (ret) +- goto error; +- +- if (status & FE_HAS_SIGNAL) +- break; +- } +- +- /* check if we have a valid signal */ +- if (status) { +- priv->last_tune_failed = 0; +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } else { +- priv->last_tune_failed = 1; +- return DVBFE_ALGO_SEARCH_AGAIN; +- } +- +-error: +- dbg("%s: failed:%d", __func__, ret); +- return DVBFE_ALGO_SEARCH_ERROR; +-} +- +-static int cxd2820r_get_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-static void cxd2820r_release(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- dbg("%s", __func__); +- +- kfree(priv); +- return; +-} +- +-static int cxd2820r_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- dbg("%s: %d", __func__, enable); +- +- /* Bit 0 of reg 0xdb in bank 0x00 controls I2C repeater */ +- return cxd2820r_wr_reg_mask(priv, 0xdb, enable ? 1 : 0, 0x1); +-} +- +-static const struct dvb_frontend_ops cxd2820r_ops = { +- .delsys = { SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A }, +- /* default: DVB-T/T2 */ +- .info = { +- .name = "Sony CXD2820R", +- +- .caps = FE_CAN_FEC_1_2 | +- FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | +- FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | +- FE_CAN_QAM_256 | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | +- FE_CAN_MUTE_TS | +- FE_CAN_2G_MODULATION +- }, +- +- .release = cxd2820r_release, +- .init = cxd2820r_init, +- .sleep = cxd2820r_sleep, +- +- .get_tune_settings = cxd2820r_get_tune_settings, +- .i2c_gate_ctrl = cxd2820r_i2c_gate_ctrl, +- +- .get_frontend = cxd2820r_get_frontend, +- +- .get_frontend_algo = cxd2820r_get_frontend_algo, +- .search = cxd2820r_search, +- +- .read_status = cxd2820r_read_status, +- .read_snr = cxd2820r_read_snr, +- .read_ber = cxd2820r_read_ber, +- .read_ucblocks = cxd2820r_read_ucblocks, +- .read_signal_strength = cxd2820r_read_signal_strength, +-}; +- +-struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *cfg, +- struct i2c_adapter *i2c) +-{ +- struct cxd2820r_priv *priv = NULL; +- int ret; +- u8 tmp; +- +- priv = kzalloc(sizeof (struct cxd2820r_priv), GFP_KERNEL); +- if (!priv) +- goto error; +- +- priv->i2c = i2c; +- memcpy(&priv->cfg, cfg, sizeof (struct cxd2820r_config)); +- +- priv->bank[0] = priv->bank[1] = 0xff; +- ret = cxd2820r_rd_reg(priv, 0x000fd, &tmp); +- dbg("%s: chip id=%02x", __func__, tmp); +- if (ret || tmp != 0xe1) +- goto error; +- +- memcpy(&priv->fe.ops, &cxd2820r_ops, sizeof (struct dvb_frontend_ops)); +- priv->fe.demodulator_priv = priv; +- return &priv->fe; +-error: +- kfree(priv); +- return NULL; +-} +-EXPORT_SYMBOL(cxd2820r_attach); +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("Sony CXD2820R demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/cxd2820r_priv.h b/drivers/media/dvb/frontends/cxd2820r_priv.h +deleted file mode 100644 +index 9a9822c..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r_priv.h ++++ /dev/null +@@ -1,156 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#ifndef CXD2820R_PRIV_H +-#define CXD2820R_PRIV_H +- +-#include +-#include "dvb_frontend.h" +-#include "dvb_math.h" +-#include "cxd2820r.h" +- +-#define LOG_PREFIX "cxd2820r" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (cxd2820r_debug) \ +- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-struct reg_val_mask { +- u32 reg; +- u8 val; +- u8 mask; +-}; +- +-struct cxd2820r_priv { +- struct i2c_adapter *i2c; +- struct dvb_frontend fe; +- struct cxd2820r_config cfg; +- +- bool ber_running; +- +- u8 bank[2]; +- u8 gpio[3]; +- +- fe_delivery_system_t delivery_system; +- bool last_tune_failed; /* for switch between T and T2 tune */ +-}; +- +-/* cxd2820r_core.c */ +- +-extern int cxd2820r_debug; +- +-int cxd2820r_gpio(struct dvb_frontend *fe); +- +-int cxd2820r_wr_reg_mask(struct cxd2820r_priv *priv, u32 reg, u8 val, +- u8 mask); +- +-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, +- int len); +- +-u32 cxd2820r_div_u64_round_closest(u64 dividend, u32 divisor); +- +-int cxd2820r_wr_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, +- int len); +- +-int cxd2820r_rd_regs(struct cxd2820r_priv *priv, u32 reginfo, u8 *val, +- int len); +- +-int cxd2820r_wr_reg(struct cxd2820r_priv *priv, u32 reg, u8 val); +- +-int cxd2820r_rd_reg(struct cxd2820r_priv *priv, u32 reg, u8 *val); +- +-/* cxd2820r_c.c */ +- +-int cxd2820r_get_frontend_c(struct dvb_frontend *fe); +- +-int cxd2820r_set_frontend_c(struct dvb_frontend *fe); +- +-int cxd2820r_read_status_c(struct dvb_frontend *fe, fe_status_t *status); +- +-int cxd2820r_read_ber_c(struct dvb_frontend *fe, u32 *ber); +- +-int cxd2820r_read_signal_strength_c(struct dvb_frontend *fe, u16 *strength); +- +-int cxd2820r_read_snr_c(struct dvb_frontend *fe, u16 *snr); +- +-int cxd2820r_read_ucblocks_c(struct dvb_frontend *fe, u32 *ucblocks); +- +-int cxd2820r_init_c(struct dvb_frontend *fe); +- +-int cxd2820r_sleep_c(struct dvb_frontend *fe); +- +-int cxd2820r_get_tune_settings_c(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s); +- +-/* cxd2820r_t.c */ +- +-int cxd2820r_get_frontend_t(struct dvb_frontend *fe); +- +-int cxd2820r_set_frontend_t(struct dvb_frontend *fe); +- +-int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status); +- +-int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber); +- +-int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, u16 *strength); +- +-int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr); +- +-int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks); +- +-int cxd2820r_init_t(struct dvb_frontend *fe); +- +-int cxd2820r_sleep_t(struct dvb_frontend *fe); +- +-int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s); +- +-/* cxd2820r_t2.c */ +- +-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe); +- +-int cxd2820r_set_frontend_t2(struct dvb_frontend *fe); +- +-int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status); +- +-int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber); +- +-int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, u16 *strength); +- +-int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr); +- +-int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks); +- +-int cxd2820r_init_t2(struct dvb_frontend *fe); +- +-int cxd2820r_sleep_t2(struct dvb_frontend *fe); +- +-int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s); +- +-#endif /* CXD2820R_PRIV_H */ +diff --git a/drivers/media/dvb/frontends/cxd2820r_t.c b/drivers/media/dvb/frontends/cxd2820r_t.c +deleted file mode 100644 +index 1a02623..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r_t.c ++++ /dev/null +@@ -1,453 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#include "cxd2820r_priv.h" +- +-int cxd2820r_set_frontend_t(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i, bw_i; +- u32 if_freq, if_ctl; +- u64 num; +- u8 buf[3], bw_param; +- u8 bw_params1[][5] = { +- { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ +- { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ +- { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ +- }; +- u8 bw_params2[][2] = { +- { 0x1f, 0xdc }, /* 6 MHz */ +- { 0x12, 0xf8 }, /* 7 MHz */ +- { 0x01, 0xe0 }, /* 8 MHz */ +- }; +- struct reg_val_mask tab[] = { +- { 0x00080, 0x00, 0xff }, +- { 0x00081, 0x03, 0xff }, +- { 0x00085, 0x07, 0xff }, +- { 0x00088, 0x01, 0xff }, +- +- { 0x00070, priv->cfg.ts_mode, 0xff }, +- { 0x000cb, priv->cfg.if_agc_polarity << 6, 0x40 }, +- { 0x000a5, 0x00, 0x01 }, +- { 0x00082, 0x20, 0x60 }, +- { 0x000c2, 0xc3, 0xff }, +- { 0x0016a, 0x50, 0xff }, +- { 0x00427, 0x41, 0xff }, +- }; +- +- dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz); +- +- switch (c->bandwidth_hz) { +- case 6000000: +- bw_i = 0; +- bw_param = 2; +- break; +- case 7000000: +- bw_i = 1; +- bw_param = 1; +- break; +- case 8000000: +- bw_i = 2; +- bw_param = 0; +- break; +- default: +- return -EINVAL; +- } +- +- /* update GPIOs */ +- ret = cxd2820r_gpio(fe); +- if (ret) +- goto error; +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- if (priv->delivery_system != SYS_DVBT) { +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, +- tab[i].val, tab[i].mask); +- if (ret) +- goto error; +- } +- } +- +- priv->delivery_system = SYS_DVBT; +- priv->ber_running = 0; /* tune stops BER counter */ +- +- /* program IF frequency */ +- if (fe->ops.tuner_ops.get_if_frequency) { +- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); +- if (ret) +- goto error; +- } else +- if_freq = 0; +- +- dbg("%s: if_freq=%d", __func__, if_freq); +- +- num = if_freq / 1000; /* Hz => kHz */ +- num *= 0x1000000; +- if_ctl = cxd2820r_div_u64_round_closest(num, 41000); +- buf[0] = ((if_ctl >> 16) & 0xff); +- buf[1] = ((if_ctl >> 8) & 0xff); +- buf[2] = ((if_ctl >> 0) & 0xff); +- +- ret = cxd2820r_wr_regs(priv, 0x000b6, buf, 3); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_regs(priv, 0x0009f, bw_params1[bw_i], 5); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg_mask(priv, 0x000d7, bw_param << 6, 0xc0); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_regs(priv, 0x000d9, bw_params2[bw_i], 2); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_get_frontend_t(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret; +- u8 buf[2]; +- +- ret = cxd2820r_rd_regs(priv, 0x0002f, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 6) & 0x03) { +- case 0: +- c->modulation = QPSK; +- break; +- case 1: +- c->modulation = QAM_16; +- break; +- case 2: +- c->modulation = QAM_64; +- break; +- } +- +- switch ((buf[1] >> 1) & 0x03) { +- case 0: +- c->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- c->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- } +- +- switch ((buf[1] >> 3) & 0x03) { +- case 0: +- c->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- c->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- c->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- c->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- switch ((buf[0] >> 3) & 0x07) { +- case 0: +- c->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- c->hierarchy = HIERARCHY_1; +- break; +- case 2: +- c->hierarchy = HIERARCHY_2; +- break; +- case 3: +- c->hierarchy = HIERARCHY_4; +- break; +- } +- +- switch ((buf[0] >> 0) & 0x07) { +- case 0: +- c->code_rate_HP = FEC_1_2; +- break; +- case 1: +- c->code_rate_HP = FEC_2_3; +- break; +- case 2: +- c->code_rate_HP = FEC_3_4; +- break; +- case 3: +- c->code_rate_HP = FEC_5_6; +- break; +- case 4: +- c->code_rate_HP = FEC_7_8; +- break; +- } +- +- switch ((buf[1] >> 5) & 0x07) { +- case 0: +- c->code_rate_LP = FEC_1_2; +- break; +- case 1: +- c->code_rate_LP = FEC_2_3; +- break; +- case 2: +- c->code_rate_LP = FEC_3_4; +- break; +- case 3: +- c->code_rate_LP = FEC_5_6; +- break; +- case 4: +- c->code_rate_LP = FEC_7_8; +- break; +- } +- +- ret = cxd2820r_rd_reg(priv, 0x007c6, &buf[0]); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 0) & 0x01) { +- case 0: +- c->inversion = INVERSION_OFF; +- break; +- case 1: +- c->inversion = INVERSION_ON; +- break; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ber_t(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[3], start_ber = 0; +- *ber = 0; +- +- if (priv->ber_running) { +- ret = cxd2820r_rd_regs(priv, 0x00076, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- if ((buf[2] >> 7) & 0x01 || (buf[2] >> 4) & 0x01) { +- *ber = (buf[2] & 0x0f) << 16 | buf[1] << 8 | buf[0]; +- start_ber = 1; +- } +- } else { +- priv->ber_running = 1; +- start_ber = 1; +- } +- +- if (start_ber) { +- /* (re)start BER */ +- ret = cxd2820r_wr_reg(priv, 0x00079, 0x01); +- if (ret) +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_signal_strength_t(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- u16 tmp; +- +- ret = cxd2820r_rd_regs(priv, 0x00026, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- tmp = (buf[0] & 0x0f) << 8 | buf[1]; +- tmp = ~tmp & 0x0fff; +- +- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ +- *strength = tmp * 0xffff / 0x0fff; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_snr_t(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- u16 tmp; +- /* report SNR in dB * 10 */ +- +- ret = cxd2820r_rd_regs(priv, 0x00028, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- tmp = (buf[0] & 0x1f) << 8 | buf[1]; +- #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ +- if (tmp) +- *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) +- / 100); +- else +- *snr = 0; +- +- dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ucblocks_t(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- /* no way to read ? */ +- return 0; +-} +- +-int cxd2820r_read_status_t(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[4]; +- *status = 0; +- +- ret = cxd2820r_rd_reg(priv, 0x00010, &buf[0]); +- if (ret) +- goto error; +- +- if ((buf[0] & 0x07) == 6) { +- ret = cxd2820r_rd_reg(priv, 0x00073, &buf[1]); +- if (ret) +- goto error; +- +- if (((buf[1] >> 3) & 0x01) == 1) { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- } else { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC; +- } +- } else { +- ret = cxd2820r_rd_reg(priv, 0x00014, &buf[2]); +- if (ret) +- goto error; +- +- if ((buf[2] & 0x0f) >= 4) { +- ret = cxd2820r_rd_reg(priv, 0x00a14, &buf[3]); +- if (ret) +- goto error; +- +- if (((buf[3] >> 4) & 0x01) == 1) +- *status |= FE_HAS_SIGNAL; +- } +- } +- +- dbg("%s: lock=%02x %02x %02x %02x", __func__, +- buf[0], buf[1], buf[2], buf[3]); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_init_t(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- +- ret = cxd2820r_wr_reg(priv, 0x00085, 0x07); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_sleep_t(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret, i; +- struct reg_val_mask tab[] = { +- { 0x000ff, 0x1f, 0xff }, +- { 0x00085, 0x00, 0xff }, +- { 0x00088, 0x01, 0xff }, +- { 0x00081, 0x00, 0xff }, +- { 0x00080, 0x00, 0xff }, +- }; +- +- dbg("%s", __func__); +- +- priv->delivery_system = SYS_UNDEFINED; +- +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, +- tab[i].mask); +- if (ret) +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_get_tune_settings_t(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s) +-{ +- s->min_delay_ms = 500; +- s->step_size = fe->ops.info.frequency_stepsize * 2; +- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; +- +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/cxd2820r_t2.c b/drivers/media/dvb/frontends/cxd2820r_t2.c +deleted file mode 100644 +index 3a5759e..0000000 +--- a/drivers/media/dvb/frontends/cxd2820r_t2.c ++++ /dev/null +@@ -1,426 +0,0 @@ +-/* +- * Sony CXD2820R demodulator driver +- * +- * Copyright (C) 2010 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +- +-#include "cxd2820r_priv.h" +- +-int cxd2820r_set_frontend_t2(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i, bw_i; +- u32 if_freq, if_ctl; +- u64 num; +- u8 buf[3], bw_param; +- u8 bw_params1[][5] = { +- { 0x1c, 0xb3, 0x33, 0x33, 0x33 }, /* 5 MHz */ +- { 0x17, 0xea, 0xaa, 0xaa, 0xaa }, /* 6 MHz */ +- { 0x14, 0x80, 0x00, 0x00, 0x00 }, /* 7 MHz */ +- { 0x11, 0xf0, 0x00, 0x00, 0x00 }, /* 8 MHz */ +- }; +- struct reg_val_mask tab[] = { +- { 0x00080, 0x02, 0xff }, +- { 0x00081, 0x20, 0xff }, +- { 0x00085, 0x07, 0xff }, +- { 0x00088, 0x01, 0xff }, +- { 0x02069, 0x01, 0xff }, +- +- { 0x0207f, 0x2a, 0xff }, +- { 0x02082, 0x0a, 0xff }, +- { 0x02083, 0x0a, 0xff }, +- { 0x020cb, priv->cfg.if_agc_polarity << 6, 0x40 }, +- { 0x02070, priv->cfg.ts_mode, 0xff }, +- { 0x020b5, priv->cfg.spec_inv << 4, 0x10 }, +- { 0x02567, 0x07, 0x0f }, +- { 0x02569, 0x03, 0x03 }, +- { 0x02595, 0x1a, 0xff }, +- { 0x02596, 0x50, 0xff }, +- { 0x02a8c, 0x00, 0xff }, +- { 0x02a8d, 0x34, 0xff }, +- { 0x02a45, 0x06, 0x07 }, +- { 0x03f10, 0x0d, 0xff }, +- { 0x03f11, 0x02, 0xff }, +- { 0x03f12, 0x01, 0xff }, +- { 0x03f23, 0x2c, 0xff }, +- { 0x03f51, 0x13, 0xff }, +- { 0x03f52, 0x01, 0xff }, +- { 0x03f53, 0x00, 0xff }, +- { 0x027e6, 0x14, 0xff }, +- { 0x02786, 0x02, 0x07 }, +- { 0x02787, 0x40, 0xe0 }, +- { 0x027ef, 0x10, 0x18 }, +- }; +- +- dbg("%s: RF=%d BW=%d", __func__, c->frequency, c->bandwidth_hz); +- +- switch (c->bandwidth_hz) { +- case 5000000: +- bw_i = 0; +- bw_param = 3; +- break; +- case 6000000: +- bw_i = 1; +- bw_param = 2; +- break; +- case 7000000: +- bw_i = 2; +- bw_param = 1; +- break; +- case 8000000: +- bw_i = 3; +- bw_param = 0; +- break; +- default: +- return -EINVAL; +- } +- +- /* update GPIOs */ +- ret = cxd2820r_gpio(fe); +- if (ret) +- goto error; +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- if (priv->delivery_system != SYS_DVBT2) { +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, +- tab[i].val, tab[i].mask); +- if (ret) +- goto error; +- } +- } +- +- priv->delivery_system = SYS_DVBT2; +- +- /* program IF frequency */ +- if (fe->ops.tuner_ops.get_if_frequency) { +- ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); +- if (ret) +- goto error; +- } else +- if_freq = 0; +- +- dbg("%s: if_freq=%d", __func__, if_freq); +- +- num = if_freq / 1000; /* Hz => kHz */ +- num *= 0x1000000; +- if_ctl = cxd2820r_div_u64_round_closest(num, 41000); +- buf[0] = ((if_ctl >> 16) & 0xff); +- buf[1] = ((if_ctl >> 8) & 0xff); +- buf[2] = ((if_ctl >> 0) & 0xff); +- +- ret = cxd2820r_wr_regs(priv, 0x020b6, buf, 3); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_regs(priv, 0x0209f, bw_params1[bw_i], 5); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg_mask(priv, 0x020d7, bw_param << 6, 0xc0); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000ff, 0x08); +- if (ret) +- goto error; +- +- ret = cxd2820r_wr_reg(priv, 0x000fe, 0x01); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +- +-} +- +-int cxd2820r_get_frontend_t2(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret; +- u8 buf[2]; +- +- ret = cxd2820r_rd_regs(priv, 0x0205c, buf, 2); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 0) & 0x07) { +- case 0: +- c->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- c->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- case 2: +- c->transmission_mode = TRANSMISSION_MODE_4K; +- break; +- case 3: +- c->transmission_mode = TRANSMISSION_MODE_1K; +- break; +- case 4: +- c->transmission_mode = TRANSMISSION_MODE_16K; +- break; +- case 5: +- c->transmission_mode = TRANSMISSION_MODE_32K; +- break; +- } +- +- switch ((buf[1] >> 4) & 0x07) { +- case 0: +- c->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- c->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- c->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- c->guard_interval = GUARD_INTERVAL_1_4; +- break; +- case 4: +- c->guard_interval = GUARD_INTERVAL_1_128; +- break; +- case 5: +- c->guard_interval = GUARD_INTERVAL_19_128; +- break; +- case 6: +- c->guard_interval = GUARD_INTERVAL_19_256; +- break; +- } +- +- ret = cxd2820r_rd_regs(priv, 0x0225b, buf, 2); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 0) & 0x07) { +- case 0: +- c->fec_inner = FEC_1_2; +- break; +- case 1: +- c->fec_inner = FEC_3_5; +- break; +- case 2: +- c->fec_inner = FEC_2_3; +- break; +- case 3: +- c->fec_inner = FEC_3_4; +- break; +- case 4: +- c->fec_inner = FEC_4_5; +- break; +- case 5: +- c->fec_inner = FEC_5_6; +- break; +- } +- +- switch ((buf[1] >> 0) & 0x07) { +- case 0: +- c->modulation = QPSK; +- break; +- case 1: +- c->modulation = QAM_16; +- break; +- case 2: +- c->modulation = QAM_64; +- break; +- case 3: +- c->modulation = QAM_256; +- break; +- } +- +- ret = cxd2820r_rd_reg(priv, 0x020b5, &buf[0]); +- if (ret) +- goto error; +- +- switch ((buf[0] >> 4) & 0x01) { +- case 0: +- c->inversion = INVERSION_OFF; +- break; +- case 1: +- c->inversion = INVERSION_ON; +- break; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_status_t2(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[1]; +- *status = 0; +- +- ret = cxd2820r_rd_reg(priv, 0x02010 , &buf[0]); +- if (ret) +- goto error; +- +- if ((buf[0] & 0x07) == 6) { +- if (((buf[0] >> 5) & 0x01) == 1) { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- } else { +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC; +- } +- } +- +- dbg("%s: lock=%02x", __func__, buf[0]); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ber_t2(struct dvb_frontend *fe, u32 *ber) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[4]; +- unsigned int errbits; +- *ber = 0; +- /* FIXME: correct calculation */ +- +- ret = cxd2820r_rd_regs(priv, 0x02039, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- if ((buf[0] >> 4) & 0x01) { +- errbits = (buf[0] & 0x0f) << 24 | buf[1] << 16 | +- buf[2] << 8 | buf[3]; +- +- if (errbits) +- *ber = errbits * 64 / 16588800; +- } +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_signal_strength_t2(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- u16 tmp; +- +- ret = cxd2820r_rd_regs(priv, 0x02026, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- tmp = (buf[0] & 0x0f) << 8 | buf[1]; +- tmp = ~tmp & 0x0fff; +- +- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ +- *strength = tmp * 0xffff / 0x0fff; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_snr_t2(struct dvb_frontend *fe, u16 *snr) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- u16 tmp; +- /* report SNR in dB * 10 */ +- +- ret = cxd2820r_rd_regs(priv, 0x02028, buf, sizeof(buf)); +- if (ret) +- goto error; +- +- tmp = (buf[0] & 0x0f) << 8 | buf[1]; +- #define CXD2820R_LOG10_8_24 15151336 /* log10(8) << 24 */ +- if (tmp) +- *snr = (intlog10(tmp) - CXD2820R_LOG10_8_24) / ((1 << 24) +- / 100); +- else +- *snr = 0; +- +- dbg("%s: dBx10=%d val=%04x", __func__, *snr, tmp); +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_read_ucblocks_t2(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- /* no way to read ? */ +- return 0; +-} +- +-int cxd2820r_sleep_t2(struct dvb_frontend *fe) +-{ +- struct cxd2820r_priv *priv = fe->demodulator_priv; +- int ret, i; +- struct reg_val_mask tab[] = { +- { 0x000ff, 0x1f, 0xff }, +- { 0x00085, 0x00, 0xff }, +- { 0x00088, 0x01, 0xff }, +- { 0x02069, 0x00, 0xff }, +- { 0x00081, 0x00, 0xff }, +- { 0x00080, 0x00, 0xff }, +- }; +- +- dbg("%s", __func__); +- +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = cxd2820r_wr_reg_mask(priv, tab[i].reg, tab[i].val, +- tab[i].mask); +- if (ret) +- goto error; +- } +- +- priv->delivery_system = SYS_UNDEFINED; +- +- return ret; +-error: +- dbg("%s: failed:%d", __func__, ret); +- return ret; +-} +- +-int cxd2820r_get_tune_settings_t2(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s) +-{ +- s->min_delay_ms = 1500; +- s->step_size = fe->ops.info.frequency_stepsize * 2; +- s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; +- +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/dib0070.c b/drivers/media/dvb/frontends/dib0070.c +deleted file mode 100644 +index 3b024bf..0000000 +--- a/drivers/media/dvb/frontends/dib0070.c ++++ /dev/null +@@ -1,780 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. +- * +- * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of the +- * License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, but +- * WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * +- * This code is more or less generated from another driver, please +- * excuse some codingstyle oddities. +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "dib0070.h" +-#include "dibx000_common.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { \ +- if (debug) { \ +- printk(KERN_DEBUG "DiB0070: "); \ +- printk(args); \ +- printk("\n"); \ +- } \ +-} while (0) +- +-#define DIB0070_P1D 0x00 +-#define DIB0070_P1F 0x01 +-#define DIB0070_P1G 0x03 +-#define DIB0070S_P1A 0x02 +- +-struct dib0070_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe; +- const struct dib0070_config *cfg; +- u16 wbd_ff_offset; +- u8 revision; +- +- enum frontend_tune_state tune_state; +- u32 current_rf; +- +- /* for the captrim binary search */ +- s8 step; +- u16 adc_diff; +- +- s8 captrim; +- s8 fcaptrim; +- u16 lo4; +- +- const struct dib0070_tuning *current_tune_table_index; +- const struct dib0070_lna_match *lna_match; +- +- u8 wbd_gain_current; +- u16 wbd_offset_3_3[2]; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[3]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +-}; +- +-static u16 dib0070_read_reg(struct dib0070_state *state, u8 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = reg; +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->cfg->i2c_address; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 1; +- state->msg[1].addr = state->cfg->i2c_address; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = state->i2c_read_buffer; +- state->msg[1].len = 2; +- +- if (i2c_transfer(state->i2c, state->msg, 2) != 2) { +- printk(KERN_WARNING "DiB0070 I2C read failed\n"); +- ret = 0; +- } else +- ret = (state->i2c_read_buffer[0] << 8) +- | state->i2c_read_buffer[1]; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static int dib0070_write_reg(struct dib0070_state *state, u8 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- state->i2c_write_buffer[0] = reg; +- state->i2c_write_buffer[1] = val >> 8; +- state->i2c_write_buffer[2] = val & 0xff; +- +- memset(state->msg, 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->cfg->i2c_address; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 3; +- +- if (i2c_transfer(state->i2c, state->msg, 1) != 1) { +- printk(KERN_WARNING "DiB0070 I2C write failed\n"); +- ret = -EREMOTEIO; +- } else +- ret = 0; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-#define HARD_RESET(state) do { \ +- state->cfg->sleep(state->fe, 0); \ +- if (state->cfg->reset) { \ +- state->cfg->reset(state->fe,1); msleep(10); \ +- state->cfg->reset(state->fe,0); msleep(10); \ +- } \ +-} while (0) +- +-static int dib0070_set_bandwidth(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- u16 tmp = dib0070_read_reg(state, 0x02) & 0x3fff; +- +- if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 7000) +- tmp |= (0 << 14); +- else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 6000) +- tmp |= (1 << 14); +- else if (state->fe->dtv_property_cache.bandwidth_hz/1000 > 5000) +- tmp |= (2 << 14); +- else +- tmp |= (3 << 14); +- +- dib0070_write_reg(state, 0x02, tmp); +- +- /* sharpen the BB filter in ISDB-T to have higher immunity to adjacent channels */ +- if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) { +- u16 value = dib0070_read_reg(state, 0x17); +- +- dib0070_write_reg(state, 0x17, value & 0xfffc); +- tmp = dib0070_read_reg(state, 0x01) & 0x01ff; +- dib0070_write_reg(state, 0x01, tmp | (60 << 9)); +- +- dib0070_write_reg(state, 0x17, value); +- } +- return 0; +-} +- +-static int dib0070_captrim(struct dib0070_state *state, enum frontend_tune_state *tune_state) +-{ +- int8_t step_sign; +- u16 adc; +- int ret = 0; +- +- if (*tune_state == CT_TUNER_STEP_0) { +- +- dib0070_write_reg(state, 0x0f, 0xed10); +- dib0070_write_reg(state, 0x17, 0x0034); +- +- dib0070_write_reg(state, 0x18, 0x0032); +- state->step = state->captrim = state->fcaptrim = 64; +- state->adc_diff = 3000; +- ret = 20; +- +- *tune_state = CT_TUNER_STEP_1; +- } else if (*tune_state == CT_TUNER_STEP_1) { +- state->step /= 2; +- dib0070_write_reg(state, 0x14, state->lo4 | state->captrim); +- ret = 15; +- +- *tune_state = CT_TUNER_STEP_2; +- } else if (*tune_state == CT_TUNER_STEP_2) { +- +- adc = dib0070_read_reg(state, 0x19); +- +- dprintk("CAPTRIM=%hd; ADC = %hd (ADC) & %dmV", state->captrim, adc, (u32) adc*(u32)1800/(u32)1024); +- +- if (adc >= 400) { +- adc -= 400; +- step_sign = -1; +- } else { +- adc = 400 - adc; +- step_sign = 1; +- } +- +- if (adc < state->adc_diff) { +- dprintk("CAPTRIM=%hd is closer to target (%hd/%hd)", state->captrim, adc, state->adc_diff); +- state->adc_diff = adc; +- state->fcaptrim = state->captrim; +- +- +- +- } +- state->captrim += (step_sign * state->step); +- +- if (state->step >= 1) +- *tune_state = CT_TUNER_STEP_1; +- else +- *tune_state = CT_TUNER_STEP_3; +- +- } else if (*tune_state == CT_TUNER_STEP_3) { +- dib0070_write_reg(state, 0x14, state->lo4 | state->fcaptrim); +- dib0070_write_reg(state, 0x18, 0x07ff); +- *tune_state = CT_TUNER_STEP_4; +- } +- +- return ret; +-} +- +-static int dib0070_set_ctrl_lo5(struct dvb_frontend *fe, u8 vco_bias_trim, u8 hf_div_trim, u8 cp_current, u8 third_order_filt) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- u16 lo5 = (third_order_filt << 14) | (0 << 13) | (1 << 12) | (3 << 9) | (cp_current << 6) | (hf_div_trim << 3) | (vco_bias_trim << 0); +- dprintk("CTRL_LO5: 0x%x", lo5); +- return dib0070_write_reg(state, 0x15, lo5); +-} +- +-void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- +- if (open) { +- dib0070_write_reg(state, 0x1b, 0xff00); +- dib0070_write_reg(state, 0x1a, 0x0000); +- } else { +- dib0070_write_reg(state, 0x1b, 0x4112); +- if (state->cfg->vga_filter != 0) { +- dib0070_write_reg(state, 0x1a, state->cfg->vga_filter); +- dprintk("vga filter register is set to %x", state->cfg->vga_filter); +- } else +- dib0070_write_reg(state, 0x1a, 0x0009); +- } +-} +- +-EXPORT_SYMBOL(dib0070_ctrl_agc_filter); +-struct dib0070_tuning { +- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ +- u8 switch_trim; +- u8 vco_band; +- u8 hfdiv; +- u8 vco_multi; +- u8 presc; +- u8 wbdmux; +- u16 tuner_enable; +-}; +- +-struct dib0070_lna_match { +- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ +- u8 lna_band; +-}; +- +-static const struct dib0070_tuning dib0070s_tuning_table[] = { +- { 570000, 2, 1, 3, 6, 6, 2, 0x4000 | 0x0800 }, /* UHF */ +- { 700000, 2, 0, 2, 4, 2, 2, 0x4000 | 0x0800 }, +- { 863999, 2, 1, 2, 4, 2, 2, 0x4000 | 0x0800 }, +- { 1500000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND */ +- { 1600000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, +- { 2000000, 0, 1, 1, 2, 2, 4, 0x2000 | 0x0400 }, +- { 0xffffffff, 0, 0, 8, 1, 2, 1, 0x8000 | 0x1000 }, /* SBAND */ +-}; +- +-static const struct dib0070_tuning dib0070_tuning_table[] = { +- { 115000, 1, 0, 7, 24, 2, 1, 0x8000 | 0x1000 }, /* FM below 92MHz cannot be tuned */ +- { 179500, 1, 0, 3, 16, 2, 1, 0x8000 | 0x1000 }, /* VHF */ +- { 189999, 1, 1, 3, 16, 2, 1, 0x8000 | 0x1000 }, +- { 250000, 1, 0, 6, 12, 2, 1, 0x8000 | 0x1000 }, +- { 569999, 2, 1, 5, 6, 2, 2, 0x4000 | 0x0800 }, /* UHF */ +- { 699999, 2, 0, 1, 4, 2, 2, 0x4000 | 0x0800 }, +- { 863999, 2, 1, 1, 4, 2, 2, 0x4000 | 0x0800 }, +- { 0xffffffff, 0, 1, 0, 2, 2, 4, 0x2000 | 0x0400 }, /* LBAND or everything higher than UHF */ +-}; +- +-static const struct dib0070_lna_match dib0070_lna_flip_chip[] = { +- { 180000, 0 }, /* VHF */ +- { 188000, 1 }, +- { 196400, 2 }, +- { 250000, 3 }, +- { 550000, 0 }, /* UHF */ +- { 590000, 1 }, +- { 666000, 3 }, +- { 864000, 5 }, +- { 1500000, 0 }, /* LBAND or everything higher than UHF */ +- { 1600000, 1 }, +- { 2000000, 3 }, +- { 0xffffffff, 7 }, +-}; +- +-static const struct dib0070_lna_match dib0070_lna[] = { +- { 180000, 0 }, /* VHF */ +- { 188000, 1 }, +- { 196400, 2 }, +- { 250000, 3 }, +- { 550000, 2 }, /* UHF */ +- { 650000, 3 }, +- { 750000, 5 }, +- { 850000, 6 }, +- { 864000, 7 }, +- { 1500000, 0 }, /* LBAND or everything higher than UHF */ +- { 1600000, 1 }, +- { 2000000, 3 }, +- { 0xffffffff, 7 }, +-}; +- +-#define LPF 100 +-static int dib0070_tune_digital(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- +- const struct dib0070_tuning *tune; +- const struct dib0070_lna_match *lna_match; +- +- enum frontend_tune_state *tune_state = &state->tune_state; +- int ret = 10; /* 1ms is the default delay most of the time */ +- +- u8 band = (u8)BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency/1000); +- u32 freq = fe->dtv_property_cache.frequency/1000 + (band == BAND_VHF ? state->cfg->freq_offset_khz_vhf : state->cfg->freq_offset_khz_uhf); +- +-#ifdef CONFIG_SYS_ISDBT +- if (state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1) +- if (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) +- && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) +- || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) +- && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == (state->fe->dtv_property_cache.isdbt_sb_segment_count / 2))) +- || (((state->fe->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) +- && (state->fe->dtv_property_cache.isdbt_sb_segment_idx == ((state->fe->dtv_property_cache.isdbt_sb_segment_count / 2) + 1)))) +- freq += 850; +-#endif +- if (state->current_rf != freq) { +- +- switch (state->revision) { +- case DIB0070S_P1A: +- tune = dib0070s_tuning_table; +- lna_match = dib0070_lna; +- break; +- default: +- tune = dib0070_tuning_table; +- if (state->cfg->flip_chip) +- lna_match = dib0070_lna_flip_chip; +- else +- lna_match = dib0070_lna; +- break; +- } +- while (freq > tune->max_freq) /* find the right one */ +- tune++; +- while (freq > lna_match->max_freq) /* find the right one */ +- lna_match++; +- +- state->current_tune_table_index = tune; +- state->lna_match = lna_match; +- } +- +- if (*tune_state == CT_TUNER_START) { +- dprintk("Tuning for Band: %hd (%d kHz)", band, freq); +- if (state->current_rf != freq) { +- u8 REFDIV; +- u32 FBDiv, Rest, FREF, VCOF_kHz; +- u8 Den; +- +- state->current_rf = freq; +- state->lo4 = (state->current_tune_table_index->vco_band << 11) | (state->current_tune_table_index->hfdiv << 7); +- +- +- dib0070_write_reg(state, 0x17, 0x30); +- +- +- VCOF_kHz = state->current_tune_table_index->vco_multi * freq * 2; +- +- switch (band) { +- case BAND_VHF: +- REFDIV = (u8) ((state->cfg->clock_khz + 9999) / 10000); +- break; +- case BAND_FM: +- REFDIV = (u8) ((state->cfg->clock_khz) / 1000); +- break; +- default: +- REFDIV = (u8) (state->cfg->clock_khz / 10000); +- break; +- } +- FREF = state->cfg->clock_khz / REFDIV; +- +- +- +- switch (state->revision) { +- case DIB0070S_P1A: +- FBDiv = (VCOF_kHz / state->current_tune_table_index->presc / FREF); +- Rest = (VCOF_kHz / state->current_tune_table_index->presc) - FBDiv * FREF; +- break; +- +- case DIB0070_P1G: +- case DIB0070_P1F: +- default: +- FBDiv = (freq / (FREF / 2)); +- Rest = 2 * freq - FBDiv * FREF; +- break; +- } +- +- if (Rest < LPF) +- Rest = 0; +- else if (Rest < 2 * LPF) +- Rest = 2 * LPF; +- else if (Rest > (FREF - LPF)) { +- Rest = 0; +- FBDiv += 1; +- } else if (Rest > (FREF - 2 * LPF)) +- Rest = FREF - 2 * LPF; +- Rest = (Rest * 6528) / (FREF / 10); +- +- Den = 1; +- if (Rest > 0) { +- state->lo4 |= (1 << 14) | (1 << 12); +- Den = 255; +- } +- +- +- dib0070_write_reg(state, 0x11, (u16)FBDiv); +- dib0070_write_reg(state, 0x12, (Den << 8) | REFDIV); +- dib0070_write_reg(state, 0x13, (u16) Rest); +- +- if (state->revision == DIB0070S_P1A) { +- +- if (band == BAND_SBAND) { +- dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); +- dib0070_write_reg(state, 0x1d, 0xFFFF); +- } else +- dib0070_set_ctrl_lo5(fe, 5, 4, 3, 1); +- } +- +- dib0070_write_reg(state, 0x20, +- 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001 | state->current_tune_table_index->tuner_enable); +- +- dprintk("REFDIV: %hd, FREF: %d", REFDIV, FREF); +- dprintk("FBDIV: %d, Rest: %d", FBDiv, Rest); +- dprintk("Num: %hd, Den: %hd, SD: %hd", (u16) Rest, Den, (state->lo4 >> 12) & 0x1); +- dprintk("HFDIV code: %hd", state->current_tune_table_index->hfdiv); +- dprintk("VCO = %hd", state->current_tune_table_index->vco_band); +- dprintk("VCOF: ((%hd*%d) << 1))", state->current_tune_table_index->vco_multi, freq); +- +- *tune_state = CT_TUNER_STEP_0; +- } else { /* we are already tuned to this frequency - the configuration is correct */ +- ret = 50; /* wakeup time */ +- *tune_state = CT_TUNER_STEP_5; +- } +- } else if ((*tune_state > CT_TUNER_START) && (*tune_state < CT_TUNER_STEP_4)) { +- +- ret = dib0070_captrim(state, tune_state); +- +- } else if (*tune_state == CT_TUNER_STEP_4) { +- const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; +- if (tmp != NULL) { +- while (freq/1000 > tmp->freq) /* find the right one */ +- tmp++; +- dib0070_write_reg(state, 0x0f, +- (0 << 15) | (1 << 14) | (3 << 12) +- | (tmp->wbd_gain_val << 9) | (0 << 8) | (1 << 7) +- | (state->current_tune_table_index->wbdmux << 0)); +- state->wbd_gain_current = tmp->wbd_gain_val; +- } else { +- dib0070_write_reg(state, 0x0f, +- (0 << 15) | (1 << 14) | (3 << 12) | (6 << 9) | (0 << 8) | (1 << 7) | (state->current_tune_table_index-> +- wbdmux << 0)); +- state->wbd_gain_current = 6; +- } +- +- dib0070_write_reg(state, 0x06, 0x3fff); +- dib0070_write_reg(state, 0x07, +- (state->current_tune_table_index->switch_trim << 11) | (7 << 8) | (state->lna_match->lna_band << 3) | (3 << 0)); +- dib0070_write_reg(state, 0x08, (state->lna_match->lna_band << 10) | (3 << 7) | (127)); +- dib0070_write_reg(state, 0x0d, 0x0d80); +- +- +- dib0070_write_reg(state, 0x18, 0x07ff); +- dib0070_write_reg(state, 0x17, 0x0033); +- +- +- *tune_state = CT_TUNER_STEP_5; +- } else if (*tune_state == CT_TUNER_STEP_5) { +- dib0070_set_bandwidth(fe); +- *tune_state = CT_TUNER_STOP; +- } else { +- ret = FE_CALLBACK_TIME_NEVER; /* tuner finished, time to call again infinite */ +- } +- return ret; +-} +- +- +-static int dib0070_tune(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- uint32_t ret; +- +- state->tune_state = CT_TUNER_START; +- +- do { +- ret = dib0070_tune_digital(fe); +- if (ret != FE_CALLBACK_TIME_NEVER) +- msleep(ret/10); +- else +- break; +- } while (state->tune_state != CT_TUNER_STOP); +- +- return 0; +-} +- +-static int dib0070_wakeup(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- if (state->cfg->sleep) +- state->cfg->sleep(fe, 0); +- return 0; +-} +- +-static int dib0070_sleep(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- if (state->cfg->sleep) +- state->cfg->sleep(fe, 1); +- return 0; +-} +- +-u8 dib0070_get_rf_output(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- return (dib0070_read_reg(state, 0x07) >> 11) & 0x3; +-} +-EXPORT_SYMBOL(dib0070_get_rf_output); +- +-int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- u16 rxrf2 = dib0070_read_reg(state, 0x07) & 0xfe7ff; +- if (no > 3) +- no = 3; +- if (no < 1) +- no = 1; +- return dib0070_write_reg(state, 0x07, rxrf2 | (no << 11)); +-} +-EXPORT_SYMBOL(dib0070_set_rf_output); +- +-static const u16 dib0070_p1f_defaults[] = +- +-{ +- 7, 0x02, +- 0x0008, +- 0x0000, +- 0x0000, +- 0x0000, +- 0x0000, +- 0x0002, +- 0x0100, +- +- 3, 0x0d, +- 0x0d80, +- 0x0001, +- 0x0000, +- +- 4, 0x11, +- 0x0000, +- 0x0103, +- 0x0000, +- 0x0000, +- +- 3, 0x16, +- 0x0004 | 0x0040, +- 0x0030, +- 0x07ff, +- +- 6, 0x1b, +- 0x4112, +- 0xff00, +- 0xc07f, +- 0x0000, +- 0x0180, +- 0x4000 | 0x0800 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001, +- +- 0, +-}; +- +-static u16 dib0070_read_wbd_offset(struct dib0070_state *state, u8 gain) +-{ +- u16 tuner_en = dib0070_read_reg(state, 0x20); +- u16 offset; +- +- dib0070_write_reg(state, 0x18, 0x07ff); +- dib0070_write_reg(state, 0x20, 0x0800 | 0x4000 | 0x0040 | 0x0020 | 0x0010 | 0x0008 | 0x0002 | 0x0001); +- dib0070_write_reg(state, 0x0f, (1 << 14) | (2 << 12) | (gain << 9) | (1 << 8) | (1 << 7) | (0 << 0)); +- msleep(9); +- offset = dib0070_read_reg(state, 0x19); +- dib0070_write_reg(state, 0x20, tuner_en); +- return offset; +-} +- +-static void dib0070_wbd_offset_calibration(struct dib0070_state *state) +-{ +- u8 gain; +- for (gain = 6; gain < 8; gain++) { +- state->wbd_offset_3_3[gain - 6] = ((dib0070_read_wbd_offset(state, gain) * 8 * 18 / 33 + 1) / 2); +- dprintk("Gain: %d, WBDOffset (3.3V) = %hd", gain, state->wbd_offset_3_3[gain-6]); +- } +-} +- +-u16 dib0070_wbd_offset(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- const struct dib0070_wbd_gain_cfg *tmp = state->cfg->wbd_gain; +- u32 freq = fe->dtv_property_cache.frequency/1000; +- +- if (tmp != NULL) { +- while (freq/1000 > tmp->freq) /* find the right one */ +- tmp++; +- state->wbd_gain_current = tmp->wbd_gain_val; +- } else +- state->wbd_gain_current = 6; +- +- return state->wbd_offset_3_3[state->wbd_gain_current - 6]; +-} +-EXPORT_SYMBOL(dib0070_wbd_offset); +- +-#define pgm_read_word(w) (*w) +-static int dib0070_reset(struct dvb_frontend *fe) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- u16 l, r, *n; +- +- HARD_RESET(state); +- +- +-#ifndef FORCE_SBAND_TUNER +- if ((dib0070_read_reg(state, 0x22) >> 9) & 0x1) +- state->revision = (dib0070_read_reg(state, 0x1f) >> 8) & 0xff; +- else +-#else +-#warning forcing SBAND +-#endif +- state->revision = DIB0070S_P1A; +- +- /* P1F or not */ +- dprintk("Revision: %x", state->revision); +- +- if (state->revision == DIB0070_P1D) { +- dprintk("Error: this driver is not to be used meant for P1D or earlier"); +- return -EINVAL; +- } +- +- n = (u16 *) dib0070_p1f_defaults; +- l = pgm_read_word(n++); +- while (l) { +- r = pgm_read_word(n++); +- do { +- dib0070_write_reg(state, (u8)r, pgm_read_word(n++)); +- r++; +- } while (--l); +- l = pgm_read_word(n++); +- } +- +- if (state->cfg->force_crystal_mode != 0) +- r = state->cfg->force_crystal_mode; +- else if (state->cfg->clock_khz >= 24000) +- r = 1; +- else +- r = 2; +- +- +- r |= state->cfg->osc_buffer_state << 3; +- +- dib0070_write_reg(state, 0x10, r); +- dib0070_write_reg(state, 0x1f, (1 << 8) | ((state->cfg->clock_pad_drive & 0xf) << 5)); +- +- if (state->cfg->invert_iq) { +- r = dib0070_read_reg(state, 0x02) & 0xffdf; +- dib0070_write_reg(state, 0x02, r | (1 << 5)); +- } +- +- if (state->revision == DIB0070S_P1A) +- dib0070_set_ctrl_lo5(fe, 2, 4, 3, 0); +- else +- dib0070_set_ctrl_lo5(fe, 5, 4, state->cfg->charge_pump, state->cfg->enable_third_order_filter); +- +- dib0070_write_reg(state, 0x01, (54 << 9) | 0xc8); +- +- dib0070_wbd_offset_calibration(state); +- +- return 0; +-} +- +-static int dib0070_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct dib0070_state *state = fe->tuner_priv; +- +- *frequency = 1000 * state->current_rf; +- return 0; +-} +- +-static int dib0070_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops dib0070_ops = { +- .info = { +- .name = "DiBcom DiB0070", +- .frequency_min = 45000000, +- .frequency_max = 860000000, +- .frequency_step = 1000, +- }, +- .release = dib0070_release, +- +- .init = dib0070_wakeup, +- .sleep = dib0070_sleep, +- .set_params = dib0070_tune, +- +- .get_frequency = dib0070_get_frequency, +-// .get_bandwidth = dib0070_get_bandwidth +-}; +- +-struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +-{ +- struct dib0070_state *state = kzalloc(sizeof(struct dib0070_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- state->cfg = cfg; +- state->i2c = i2c; +- state->fe = fe; +- mutex_init(&state->i2c_buffer_lock); +- fe->tuner_priv = state; +- +- if (dib0070_reset(fe) != 0) +- goto free_mem; +- +- printk(KERN_INFO "DiB0070: successfully identified\n"); +- memcpy(&fe->ops.tuner_ops, &dib0070_ops, sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = state; +- return fe; +- +-free_mem: +- kfree(state); +- fe->tuner_priv = NULL; +- return NULL; +-} +-EXPORT_SYMBOL(dib0070_attach); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h +deleted file mode 100644 +index 45c31fa..0000000 +--- a/drivers/media/dvb/frontends/dib0070.h ++++ /dev/null +@@ -1,76 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB0070 base-band RF Tuner. +- * +- * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#ifndef DIB0070_H +-#define DIB0070_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-#define DEFAULT_DIB0070_I2C_ADDRESS 0x60 +- +-struct dib0070_wbd_gain_cfg { +- u16 freq; +- u16 wbd_gain_val; +-}; +- +-struct dib0070_config { +- u8 i2c_address; +- +- /* tuner pins controlled externally */ +- int (*reset) (struct dvb_frontend *, int); +- int (*sleep) (struct dvb_frontend *, int); +- +- /* offset in kHz */ +- int freq_offset_khz_uhf; +- int freq_offset_khz_vhf; +- +- u8 osc_buffer_state; /* 0= normal, 1= tri-state */ +- u32 clock_khz; +- u8 clock_pad_drive; /* (Drive + 1) * 2mA */ +- +- u8 invert_iq; /* invert Q - in case I or Q is inverted on the board */ +- +- u8 force_crystal_mode; /* if == 0 -> decision is made in the driver default: <24 -> 2, >=24 -> 1 */ +- +- u8 flip_chip; +- u8 enable_third_order_filter; +- u8 charge_pump; +- +- const struct dib0070_wbd_gain_cfg *wbd_gain; +- +- u8 vga_filter; +-}; +- +-#if defined(CONFIG_DVB_TUNER_DIB0070) || (defined(CONFIG_DVB_TUNER_DIB0070_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg); +-extern u16 dib0070_wbd_offset(struct dvb_frontend *); +-extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, u8 open); +-extern u8 dib0070_get_rf_output(struct dvb_frontend *fe); +-extern int dib0070_set_rf_output(struct dvb_frontend *fe, u8 no); +-#else +-static inline struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0070_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +- +-static inline void dib0070_ctrl_agc_filter(struct dvb_frontend *fe, u8 open) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c +deleted file mode 100644 +index 224d81e..0000000 +--- a/drivers/media/dvb/frontends/dib0090.c ++++ /dev/null +@@ -1,2686 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. +- * +- * Copyright (C) 2005-9 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of the +- * License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, but +- * WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * +- * This code is more or less generated from another driver, please +- * excuse some codingstyle oddities. +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "dib0090.h" +-#include "dibx000_common.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { \ +- if (debug) { \ +- printk(KERN_DEBUG "DiB0090: "); \ +- printk(args); \ +- printk("\n"); \ +- } \ +-} while (0) +- +-#define CONFIG_SYS_DVBT +-#define CONFIG_SYS_ISDBT +-#define CONFIG_BAND_CBAND +-#define CONFIG_BAND_VHF +-#define CONFIG_BAND_UHF +-#define CONFIG_DIB0090_USE_PWM_AGC +- +-#define EN_LNA0 0x8000 +-#define EN_LNA1 0x4000 +-#define EN_LNA2 0x2000 +-#define EN_LNA3 0x1000 +-#define EN_MIX0 0x0800 +-#define EN_MIX1 0x0400 +-#define EN_MIX2 0x0200 +-#define EN_MIX3 0x0100 +-#define EN_IQADC 0x0040 +-#define EN_PLL 0x0020 +-#define EN_TX 0x0010 +-#define EN_BB 0x0008 +-#define EN_LO 0x0004 +-#define EN_BIAS 0x0001 +- +-#define EN_IQANA 0x0002 +-#define EN_DIGCLK 0x0080 /* not in the 0x24 reg, only in 0x1b */ +-#define EN_CRYSTAL 0x0002 +- +-#define EN_UHF 0x22E9 +-#define EN_VHF 0x44E9 +-#define EN_LBD 0x11E9 +-#define EN_SBD 0x44E9 +-#define EN_CAB 0x88E9 +- +-/* Calibration defines */ +-#define DC_CAL 0x1 +-#define WBD_CAL 0x2 +-#define TEMP_CAL 0x4 +-#define CAPTRIM_CAL 0x8 +- +-#define KROSUS_PLL_LOCKED 0x800 +-#define KROSUS 0x2 +- +-/* Use those defines to identify SOC version */ +-#define SOC 0x02 +-#define SOC_7090_P1G_11R1 0x82 +-#define SOC_7090_P1G_21R1 0x8a +-#define SOC_8090_P1G_11R1 0x86 +-#define SOC_8090_P1G_21R1 0x8e +- +-/* else use thos ones to check */ +-#define P1A_B 0x0 +-#define P1C 0x1 +-#define P1D_E_F 0x3 +-#define P1G 0x7 +-#define P1G_21R2 0xf +- +-#define MP001 0x1 /* Single 9090/8096 */ +-#define MP005 0x4 /* Single Sband */ +-#define MP008 0x6 /* Dual diversity VHF-UHF-LBAND */ +-#define MP009 0x7 /* Dual diversity 29098 CBAND-UHF-LBAND-SBAND */ +- +-#define pgm_read_word(w) (*w) +- +-struct dc_calibration; +- +-struct dib0090_tuning { +- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ +- u8 switch_trim; +- u8 lna_tune; +- u16 lna_bias; +- u16 v2i; +- u16 mix; +- u16 load; +- u16 tuner_enable; +-}; +- +-struct dib0090_pll { +- u32 max_freq; /* for every frequency less than or equal to that field: this information is correct */ +- u8 vco_band; +- u8 hfdiv_code; +- u8 hfdiv; +- u8 topresc; +-}; +- +-struct dib0090_identity { +- u8 version; +- u8 product; +- u8 p1g; +- u8 in_soc; +-}; +- +-struct dib0090_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe; +- const struct dib0090_config *config; +- +- u8 current_band; +- enum frontend_tune_state tune_state; +- u32 current_rf; +- +- u16 wbd_offset; +- s16 wbd_target; /* in dB */ +- +- s16 rf_gain_limit; /* take-over-point: where to split between bb and rf gain */ +- s16 current_gain; /* keeps the currently programmed gain */ +- u8 agc_step; /* new binary search */ +- +- u16 gain[2]; /* for channel monitoring */ +- +- const u16 *rf_ramp; +- const u16 *bb_ramp; +- +- /* for the software AGC ramps */ +- u16 bb_1_def; +- u16 rf_lt_def; +- u16 gain_reg[4]; +- +- /* for the captrim/dc-offset search */ +- s8 step; +- s16 adc_diff; +- s16 min_adc_diff; +- +- s8 captrim; +- s8 fcaptrim; +- +- const struct dc_calibration *dc; +- u16 bb6, bb7; +- +- const struct dib0090_tuning *current_tune_table_index; +- const struct dib0090_pll *current_pll_table_index; +- +- u8 tuner_is_tuned; +- u8 agc_freeze; +- +- struct dib0090_identity identity; +- +- u32 rf_request; +- u8 current_standard; +- +- u8 calibrate; +- u32 rest; +- u16 bias; +- s16 temperature; +- +- u8 wbd_calibration_gain; +- const struct dib0090_wbd_slope *current_wbd_table; +- u16 wbdmux; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[3]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +-}; +- +-struct dib0090_fw_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe; +- struct dib0090_identity identity; +- const struct dib0090_config *config; +- +- /* for the I2C transfer */ +- struct i2c_msg msg; +- u8 i2c_write_buffer[2]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +-}; +- +-static u16 dib0090_read_reg(struct dib0090_state *state, u8 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = reg; +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->config->i2c_address; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 1; +- state->msg[1].addr = state->config->i2c_address; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = state->i2c_read_buffer; +- state->msg[1].len = 2; +- +- if (i2c_transfer(state->i2c, state->msg, 2) != 2) { +- printk(KERN_WARNING "DiB0090 I2C read failed\n"); +- ret = 0; +- } else +- ret = (state->i2c_read_buffer[0] << 8) +- | state->i2c_read_buffer[1]; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static int dib0090_write_reg(struct dib0090_state *state, u32 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- state->i2c_write_buffer[0] = reg & 0xff; +- state->i2c_write_buffer[1] = val >> 8; +- state->i2c_write_buffer[2] = val & 0xff; +- +- memset(state->msg, 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->config->i2c_address; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 3; +- +- if (i2c_transfer(state->i2c, state->msg, 1) != 1) { +- printk(KERN_WARNING "DiB0090 I2C write failed\n"); +- ret = -EREMOTEIO; +- } else +- ret = 0; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static u16 dib0090_fw_read_reg(struct dib0090_fw_state *state, u8 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = reg; +- +- memset(&state->msg, 0, sizeof(struct i2c_msg)); +- state->msg.addr = reg; +- state->msg.flags = I2C_M_RD; +- state->msg.buf = state->i2c_read_buffer; +- state->msg.len = 2; +- if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { +- printk(KERN_WARNING "DiB0090 I2C read failed\n"); +- ret = 0; +- } else +- ret = (state->i2c_read_buffer[0] << 8) +- | state->i2c_read_buffer[1]; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static int dib0090_fw_write_reg(struct dib0090_fw_state *state, u8 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- state->i2c_write_buffer[0] = val >> 8; +- state->i2c_write_buffer[1] = val & 0xff; +- +- memset(&state->msg, 0, sizeof(struct i2c_msg)); +- state->msg.addr = reg; +- state->msg.flags = 0; +- state->msg.buf = state->i2c_write_buffer; +- state->msg.len = 2; +- if (i2c_transfer(state->i2c, &state->msg, 1) != 1) { +- printk(KERN_WARNING "DiB0090 I2C write failed\n"); +- ret = -EREMOTEIO; +- } else +- ret = 0; +- +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-#define HARD_RESET(state) do { if (cfg->reset) { if (cfg->sleep) cfg->sleep(fe, 0); msleep(10); cfg->reset(fe, 1); msleep(10); cfg->reset(fe, 0); msleep(10); } } while (0) +-#define ADC_TARGET -220 +-#define GAIN_ALPHA 5 +-#define WBD_ALPHA 6 +-#define LPF 100 +-static void dib0090_write_regs(struct dib0090_state *state, u8 r, const u16 * b, u8 c) +-{ +- do { +- dib0090_write_reg(state, r++, *b++); +- } while (--c); +-} +- +-static int dib0090_identify(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- u16 v; +- struct dib0090_identity *identity = &state->identity; +- +- v = dib0090_read_reg(state, 0x1a); +- +- identity->p1g = 0; +- identity->in_soc = 0; +- +- dprintk("Tuner identification (Version = 0x%04x)", v); +- +- /* without PLL lock info */ +- v &= ~KROSUS_PLL_LOCKED; +- +- identity->version = v & 0xff; +- identity->product = (v >> 8) & 0xf; +- +- if (identity->product != KROSUS) +- goto identification_error; +- +- if ((identity->version & 0x3) == SOC) { +- identity->in_soc = 1; +- switch (identity->version) { +- case SOC_8090_P1G_11R1: +- dprintk("SOC 8090 P1-G11R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_8090_P1G_21R1: +- dprintk("SOC 8090 P1-G21R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_7090_P1G_11R1: +- dprintk("SOC 7090 P1-G11R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_7090_P1G_21R1: +- dprintk("SOC 7090 P1-G21R1 Has been detected"); +- identity->p1g = 1; +- break; +- default: +- goto identification_error; +- } +- } else { +- switch ((identity->version >> 5) & 0x7) { +- case MP001: +- dprintk("MP001 : 9090/8096"); +- break; +- case MP005: +- dprintk("MP005 : Single Sband"); +- break; +- case MP008: +- dprintk("MP008 : diversity VHF-UHF-LBAND"); +- break; +- case MP009: +- dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); +- break; +- default: +- goto identification_error; +- } +- +- switch (identity->version & 0x1f) { +- case P1G_21R2: +- dprintk("P1G_21R2 detected"); +- identity->p1g = 1; +- break; +- case P1G: +- dprintk("P1G detected"); +- identity->p1g = 1; +- break; +- case P1D_E_F: +- dprintk("P1D/E/F detected"); +- break; +- case P1C: +- dprintk("P1C detected"); +- break; +- case P1A_B: +- dprintk("P1-A/B detected: driver is deactivated - not available"); +- goto identification_error; +- break; +- default: +- goto identification_error; +- } +- } +- +- return 0; +- +-identification_error: +- return -EIO; +-} +- +-static int dib0090_fw_identify(struct dvb_frontend *fe) +-{ +- struct dib0090_fw_state *state = fe->tuner_priv; +- struct dib0090_identity *identity = &state->identity; +- +- u16 v = dib0090_fw_read_reg(state, 0x1a); +- identity->p1g = 0; +- identity->in_soc = 0; +- +- dprintk("FE: Tuner identification (Version = 0x%04x)", v); +- +- /* without PLL lock info */ +- v &= ~KROSUS_PLL_LOCKED; +- +- identity->version = v & 0xff; +- identity->product = (v >> 8) & 0xf; +- +- if (identity->product != KROSUS) +- goto identification_error; +- +- if ((identity->version & 0x3) == SOC) { +- identity->in_soc = 1; +- switch (identity->version) { +- case SOC_8090_P1G_11R1: +- dprintk("SOC 8090 P1-G11R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_8090_P1G_21R1: +- dprintk("SOC 8090 P1-G21R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_7090_P1G_11R1: +- dprintk("SOC 7090 P1-G11R1 Has been detected"); +- identity->p1g = 1; +- break; +- case SOC_7090_P1G_21R1: +- dprintk("SOC 7090 P1-G21R1 Has been detected"); +- identity->p1g = 1; +- break; +- default: +- goto identification_error; +- } +- } else { +- switch ((identity->version >> 5) & 0x7) { +- case MP001: +- dprintk("MP001 : 9090/8096"); +- break; +- case MP005: +- dprintk("MP005 : Single Sband"); +- break; +- case MP008: +- dprintk("MP008 : diversity VHF-UHF-LBAND"); +- break; +- case MP009: +- dprintk("MP009 : diversity 29098 CBAND-UHF-LBAND-SBAND"); +- break; +- default: +- goto identification_error; +- } +- +- switch (identity->version & 0x1f) { +- case P1G_21R2: +- dprintk("P1G_21R2 detected"); +- identity->p1g = 1; +- break; +- case P1G: +- dprintk("P1G detected"); +- identity->p1g = 1; +- break; +- case P1D_E_F: +- dprintk("P1D/E/F detected"); +- break; +- case P1C: +- dprintk("P1C detected"); +- break; +- case P1A_B: +- dprintk("P1-A/B detected: driver is deactivated - not available"); +- goto identification_error; +- break; +- default: +- goto identification_error; +- } +- } +- +- return 0; +- +-identification_error: +- return -EIO;; +-} +- +-static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- u16 PllCfg, i, v; +- +- HARD_RESET(state); +- +- dib0090_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); +- dib0090_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ +- +- if (!cfg->in_soc) { +- /* adcClkOutRatio=8->7, release reset */ +- dib0090_write_reg(state, 0x20, ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (0 << 4) | 0); +- if (cfg->clkoutdrive != 0) +- dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) +- | (cfg->clkoutdrive << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); +- else +- dib0090_write_reg(state, 0x23, (0 << 15) | ((!cfg->analog_output) << 14) | (2 << 10) | (1 << 9) | (0 << 8) +- | (7 << 5) | (cfg->clkouttobamse << 4) | (0 << 2) | (0)); +- } +- +- /* Read Pll current config * */ +- PllCfg = dib0090_read_reg(state, 0x21); +- +- /** Reconfigure PLL if current setting is different from default setting **/ +- if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && (!cfg->in_soc) +- && !cfg->io.pll_bypass) { +- +- /* Set Bypass mode */ +- PllCfg |= (1 << 15); +- dib0090_write_reg(state, 0x21, PllCfg); +- +- /* Set Reset Pll */ +- PllCfg &= ~(1 << 13); +- dib0090_write_reg(state, 0x21, PllCfg); +- +- /*** Set new Pll configuration in bypass and reset state ***/ +- PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); +- dib0090_write_reg(state, 0x21, PllCfg); +- +- /* Remove Reset Pll */ +- PllCfg |= (1 << 13); +- dib0090_write_reg(state, 0x21, PllCfg); +- +- /*** Wait for PLL lock ***/ +- i = 100; +- do { +- v = !!(dib0090_read_reg(state, 0x1a) & 0x800); +- if (v) +- break; +- } while (--i); +- +- if (i == 0) { +- dprintk("Pll: Unable to lock Pll"); +- return; +- } +- +- /* Finally Remove Bypass mode */ +- PllCfg &= ~(1 << 15); +- dib0090_write_reg(state, 0x21, PllCfg); +- } +- +- if (cfg->io.pll_bypass) { +- PllCfg |= (cfg->io.pll_bypass << 15); +- dib0090_write_reg(state, 0x21, PllCfg); +- } +-} +- +-static int dib0090_fw_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg) +-{ +- struct dib0090_fw_state *state = fe->tuner_priv; +- u16 PllCfg; +- u16 v; +- int i; +- +- dprintk("fw reset digital"); +- HARD_RESET(state); +- +- dib0090_fw_write_reg(state, 0x24, EN_PLL | EN_CRYSTAL); +- dib0090_fw_write_reg(state, 0x1b, EN_DIGCLK | EN_PLL | EN_CRYSTAL); /* PLL, DIG_CLK and CRYSTAL remain */ +- +- dib0090_fw_write_reg(state, 0x20, +- ((cfg->io.adc_clock_ratio - 1) << 11) | (0 << 10) | (1 << 9) | (1 << 8) | (cfg->data_tx_drv << 4) | cfg->ls_cfg_pad_drv); +- +- v = (0 << 15) | ((!cfg->analog_output) << 14) | (1 << 9) | (0 << 8) | (cfg->clkouttobamse << 4) | (0 << 2) | (0); +- if (cfg->clkoutdrive != 0) +- v |= cfg->clkoutdrive << 5; +- else +- v |= 7 << 5; +- +- v |= 2 << 10; +- dib0090_fw_write_reg(state, 0x23, v); +- +- /* Read Pll current config * */ +- PllCfg = dib0090_fw_read_reg(state, 0x21); +- +- /** Reconfigure PLL if current setting is different from default setting **/ +- if ((PllCfg & 0x1FFF) != ((cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv)) && !cfg->io.pll_bypass) { +- +- /* Set Bypass mode */ +- PllCfg |= (1 << 15); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- +- /* Set Reset Pll */ +- PllCfg &= ~(1 << 13); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- +- /*** Set new Pll configuration in bypass and reset state ***/ +- PllCfg = (1 << 15) | (0 << 13) | (cfg->io.pll_range << 12) | (cfg->io.pll_loopdiv << 6) | (cfg->io.pll_prediv); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- +- /* Remove Reset Pll */ +- PllCfg |= (1 << 13); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- +- /*** Wait for PLL lock ***/ +- i = 100; +- do { +- v = !!(dib0090_fw_read_reg(state, 0x1a) & 0x800); +- if (v) +- break; +- } while (--i); +- +- if (i == 0) { +- dprintk("Pll: Unable to lock Pll"); +- return -EIO; +- } +- +- /* Finally Remove Bypass mode */ +- PllCfg &= ~(1 << 15); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- } +- +- if (cfg->io.pll_bypass) { +- PllCfg |= (cfg->io.pll_bypass << 15); +- dib0090_fw_write_reg(state, 0x21, PllCfg); +- } +- +- return dib0090_fw_identify(fe); +-} +- +-static int dib0090_wakeup(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- if (state->config->sleep) +- state->config->sleep(fe, 0); +- +- /* enable dataTX in case we have been restarted in the wrong moment */ +- dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); +- return 0; +-} +- +-static int dib0090_sleep(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- if (state->config->sleep) +- state->config->sleep(fe, 1); +- return 0; +-} +- +-void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- if (fast) +- dib0090_write_reg(state, 0x04, 0); +- else +- dib0090_write_reg(state, 0x04, 1); +-} +- +-EXPORT_SYMBOL(dib0090_dcc_freq); +- +-static const u16 bb_ramp_pwm_normal_socs[] = { +- 550, /* max BB gain in 10th of dB */ +- (1 << 9) | 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> BB_RAMP2 */ +- 440, +- (4 << 9) | 0, /* BB_RAMP3 = 26dB */ +- (0 << 9) | 208, /* BB_RAMP4 */ +- (4 << 9) | 208, /* BB_RAMP5 = 29dB */ +- (0 << 9) | 440, /* BB_RAMP6 */ +-}; +- +-static const u16 rf_ramp_pwm_cband_7090[] = { +- 280, /* max RF gain in 10th of dB */ +- 18, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ +- 504, /* ramp_max = maximum X used on the ramp */ +- (29 << 10) | 364, /* RF_RAMP5, LNA 1 = 8dB */ +- (0 << 10) | 504, /* RF_RAMP6, LNA 1 */ +- (60 << 10) | 228, /* RF_RAMP7, LNA 2 = 7.7dB */ +- (0 << 10) | 364, /* RF_RAMP8, LNA 2 */ +- (34 << 10) | 109, /* GAIN_4_1, LNA 3 = 6.8dB */ +- (0 << 10) | 228, /* GAIN_4_2, LNA 3 */ +- (37 << 10) | 0, /* RF_RAMP3, LNA 4 = 6.2dB */ +- (0 << 10) | 109, /* RF_RAMP4, LNA 4 */ +-}; +- +-static const uint16_t rf_ramp_pwm_cband_7090e_sensitivity[] = { +- 186, +- 40, +- 746, +- (10 << 10) | 345, +- (0 << 10) | 746, +- (0 << 10) | 0, +- (0 << 10) | 0, +- (28 << 10) | 200, +- (0 << 10) | 345, +- (20 << 10) | 0, +- (0 << 10) | 200, +-}; +- +-static const uint16_t rf_ramp_pwm_cband_7090e_aci[] = { +- 86, +- 40, +- 345, +- (0 << 10) | 0, +- (0 << 10) | 0, +- (0 << 10) | 0, +- (0 << 10) | 0, +- (28 << 10) | 200, +- (0 << 10) | 345, +- (20 << 10) | 0, +- (0 << 10) | 200, +-}; +- +-static const u16 rf_ramp_pwm_cband_8090[] = { +- 345, /* max RF gain in 10th of dB */ +- 29, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ +- 1000, /* ramp_max = maximum X used on the ramp */ +- (35 << 10) | 772, /* RF_RAMP3, LNA 1 = 8dB */ +- (0 << 10) | 1000, /* RF_RAMP4, LNA 1 */ +- (58 << 10) | 496, /* RF_RAMP5, LNA 2 = 9.5dB */ +- (0 << 10) | 772, /* RF_RAMP6, LNA 2 */ +- (27 << 10) | 200, /* RF_RAMP7, LNA 3 = 10.5dB */ +- (0 << 10) | 496, /* RF_RAMP8, LNA 3 */ +- (40 << 10) | 0, /* GAIN_4_1, LNA 4 = 7dB */ +- (0 << 10) | 200, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 rf_ramp_pwm_uhf_7090[] = { +- 407, /* max RF gain in 10th of dB */ +- 13, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ +- 529, /* ramp_max = maximum X used on the ramp */ +- (23 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ +- (0 << 10) | 176, /* RF_RAMP4, LNA 1 */ +- (63 << 10) | 400, /* RF_RAMP5, LNA 2 = 8dB */ +- (0 << 10) | 529, /* RF_RAMP6, LNA 2 */ +- (48 << 10) | 316, /* RF_RAMP7, LNA 3 = 6.8dB */ +- (0 << 10) | 400, /* RF_RAMP8, LNA 3 */ +- (29 << 10) | 176, /* GAIN_4_1, LNA 4 = 11.5dB */ +- (0 << 10) | 316, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 rf_ramp_pwm_uhf_8090[] = { +- 388, /* max RF gain in 10th of dB */ +- 26, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> RF_RAMP2 */ +- 1008, /* ramp_max = maximum X used on the ramp */ +- (11 << 10) | 0, /* RF_RAMP3, LNA 1 = 14.7dB */ +- (0 << 10) | 369, /* RF_RAMP4, LNA 1 */ +- (41 << 10) | 809, /* RF_RAMP5, LNA 2 = 8dB */ +- (0 << 10) | 1008, /* RF_RAMP6, LNA 2 */ +- (27 << 10) | 659, /* RF_RAMP7, LNA 3 = 6dB */ +- (0 << 10) | 809, /* RF_RAMP8, LNA 3 */ +- (14 << 10) | 369, /* GAIN_4_1, LNA 4 = 11.5dB */ +- (0 << 10) | 659, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 rf_ramp_pwm_cband[] = { +- 0, /* max RF gain in 10th of dB */ +- 0, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ +- 0, /* ramp_max = maximum X used on the ramp */ +- (0 << 10) | 0, /* 0x2c, LNA 1 = 0dB */ +- (0 << 10) | 0, /* 0x2d, LNA 1 */ +- (0 << 10) | 0, /* 0x2e, LNA 2 = 0dB */ +- (0 << 10) | 0, /* 0x2f, LNA 2 */ +- (0 << 10) | 0, /* 0x30, LNA 3 = 0dB */ +- (0 << 10) | 0, /* 0x31, LNA 3 */ +- (0 << 10) | 0, /* GAIN_4_1, LNA 4 = 0dB */ +- (0 << 10) | 0, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 rf_ramp_vhf[] = { +- 412, /* max RF gain in 10th of dB */ +- 132, 307, 127, /* LNA1, 13.2dB */ +- 105, 412, 255, /* LNA2, 10.5dB */ +- 50, 50, 127, /* LNA3, 5dB */ +- 125, 175, 127, /* LNA4, 12.5dB */ +- 0, 0, 127, /* CBAND, 0dB */ +-}; +- +-static const u16 rf_ramp_uhf[] = { +- 412, /* max RF gain in 10th of dB */ +- 132, 307, 127, /* LNA1 : total gain = 13.2dB, point on the ramp where this amp is full gain, value to write to get full gain */ +- 105, 412, 255, /* LNA2 : 10.5 dB */ +- 50, 50, 127, /* LNA3 : 5.0 dB */ +- 125, 175, 127, /* LNA4 : 12.5 dB */ +- 0, 0, 127, /* CBAND : 0.0 dB */ +-}; +- +-static const u16 rf_ramp_cband_broadmatching[] = /* for p1G only */ +-{ +- 314, /* Calibrated at 200MHz order has been changed g4-g3-g2-g1 */ +- 84, 314, 127, /* LNA1 */ +- 80, 230, 255, /* LNA2 */ +- 80, 150, 127, /* LNA3 It was measured 12dB, do not lock if 120 */ +- 70, 70, 127, /* LNA4 */ +- 0, 0, 127, /* CBAND */ +-}; +- +-static const u16 rf_ramp_cband[] = { +- 332, /* max RF gain in 10th of dB */ +- 132, 252, 127, /* LNA1, dB */ +- 80, 332, 255, /* LNA2, dB */ +- 0, 0, 127, /* LNA3, dB */ +- 0, 0, 127, /* LNA4, dB */ +- 120, 120, 127, /* LT1 CBAND */ +-}; +- +-static const u16 rf_ramp_pwm_vhf[] = { +- 404, /* max RF gain in 10th of dB */ +- 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ +- 1011, /* ramp_max = maximum X used on the ramp */ +- (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ +- (0 << 10) | 756, /* 0x2d, LNA 1 */ +- (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ +- (0 << 10) | 1011, /* 0x2f, LNA 2 */ +- (16 << 10) | 290, /* 0x30, LNA 3 = 5dB */ +- (0 << 10) | 417, /* 0x31, LNA 3 */ +- (7 << 10) | 0, /* GAIN_4_1, LNA 4 = 12.5dB */ +- (0 << 10) | 290, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 rf_ramp_pwm_uhf[] = { +- 404, /* max RF gain in 10th of dB */ +- 25, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x2b */ +- 1011, /* ramp_max = maximum X used on the ramp */ +- (6 << 10) | 417, /* 0x2c, LNA 1 = 13.2dB */ +- (0 << 10) | 756, /* 0x2d, LNA 1 */ +- (16 << 10) | 756, /* 0x2e, LNA 2 = 10.5dB */ +- (0 << 10) | 1011, /* 0x2f, LNA 2 */ +- (16 << 10) | 0, /* 0x30, LNA 3 = 5dB */ +- (0 << 10) | 127, /* 0x31, LNA 3 */ +- (7 << 10) | 127, /* GAIN_4_1, LNA 4 = 12.5dB */ +- (0 << 10) | 417, /* GAIN_4_2, LNA 4 */ +-}; +- +-static const u16 bb_ramp_boost[] = { +- 550, /* max BB gain in 10th of dB */ +- 260, 260, 26, /* BB1, 26dB */ +- 290, 550, 29, /* BB2, 29dB */ +-}; +- +-static const u16 bb_ramp_pwm_normal[] = { +- 500, /* max RF gain in 10th of dB */ +- 8, /* ramp_slope = 1dB of gain -> clock_ticks_per_db = clk_khz / ramp_slope -> 0x34 */ +- 400, +- (2 << 9) | 0, /* 0x35 = 21dB */ +- (0 << 9) | 168, /* 0x36 */ +- (2 << 9) | 168, /* 0x37 = 29dB */ +- (0 << 9) | 400, /* 0x38 */ +-}; +- +-struct slope { +- s16 range; +- s16 slope; +-}; +-static u16 slopes_to_scale(const struct slope *slopes, u8 num, s16 val) +-{ +- u8 i; +- u16 rest; +- u16 ret = 0; +- for (i = 0; i < num; i++) { +- if (val > slopes[i].range) +- rest = slopes[i].range; +- else +- rest = val; +- ret += (rest * slopes[i].slope) / slopes[i].range; +- val -= rest; +- } +- return ret; +-} +- +-static const struct slope dib0090_wbd_slopes[3] = { +- {66, 120}, /* -64,-52: offset - 65 */ +- {600, 170}, /* -52,-35: 65 - 665 */ +- {170, 250}, /* -45,-10: 665 - 835 */ +-}; +- +-static s16 dib0090_wbd_to_db(struct dib0090_state *state, u16 wbd) +-{ +- wbd &= 0x3ff; +- if (wbd < state->wbd_offset) +- wbd = 0; +- else +- wbd -= state->wbd_offset; +- /* -64dB is the floor */ +- return -640 + (s16) slopes_to_scale(dib0090_wbd_slopes, ARRAY_SIZE(dib0090_wbd_slopes), wbd); +-} +- +-static void dib0090_wbd_target(struct dib0090_state *state, u32 rf) +-{ +- u16 offset = 250; +- +- /* TODO : DAB digital N+/-1 interferer perfs : offset = 10 */ +- +- if (state->current_band == BAND_VHF) +- offset = 650; +-#ifndef FIRMWARE_FIREFLY +- if (state->current_band == BAND_VHF) +- offset = state->config->wbd_vhf_offset; +- if (state->current_band == BAND_CBAND) +- offset = state->config->wbd_cband_offset; +-#endif +- +- state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + offset); +- dprintk("wbd-target: %d dB", (u32) state->wbd_target); +-} +- +-static const int gain_reg_addr[4] = { +- 0x08, 0x0a, 0x0f, 0x01 +-}; +- +-static void dib0090_gain_apply(struct dib0090_state *state, s16 gain_delta, s16 top_delta, u8 force) +-{ +- u16 rf, bb, ref; +- u16 i, v, gain_reg[4] = { 0 }, gain; +- const u16 *g; +- +- if (top_delta < -511) +- top_delta = -511; +- if (top_delta > 511) +- top_delta = 511; +- +- if (force) { +- top_delta *= (1 << WBD_ALPHA); +- gain_delta *= (1 << GAIN_ALPHA); +- } +- +- if (top_delta >= ((s16) (state->rf_ramp[0] << WBD_ALPHA) - state->rf_gain_limit)) /* overflow */ +- state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; +- else +- state->rf_gain_limit += top_delta; +- +- if (state->rf_gain_limit < 0) /*underflow */ +- state->rf_gain_limit = 0; +- +- /* use gain as a temporary variable and correct current_gain */ +- gain = ((state->rf_gain_limit >> WBD_ALPHA) + state->bb_ramp[0]) << GAIN_ALPHA; +- if (gain_delta >= ((s16) gain - state->current_gain)) /* overflow */ +- state->current_gain = gain; +- else +- state->current_gain += gain_delta; +- /* cannot be less than 0 (only if gain_delta is less than 0 we can have current_gain < 0) */ +- if (state->current_gain < 0) +- state->current_gain = 0; +- +- /* now split total gain to rf and bb gain */ +- gain = state->current_gain >> GAIN_ALPHA; +- +- /* requested gain is bigger than rf gain limit - ACI/WBD adjustment */ +- if (gain > (state->rf_gain_limit >> WBD_ALPHA)) { +- rf = state->rf_gain_limit >> WBD_ALPHA; +- bb = gain - rf; +- if (bb > state->bb_ramp[0]) +- bb = state->bb_ramp[0]; +- } else { /* high signal level -> all gains put on RF */ +- rf = gain; +- bb = 0; +- } +- +- state->gain[0] = rf; +- state->gain[1] = bb; +- +- /* software ramp */ +- /* Start with RF gains */ +- g = state->rf_ramp + 1; /* point on RF LNA1 max gain */ +- ref = rf; +- for (i = 0; i < 7; i++) { /* Go over all amplifiers => 5RF amps + 2 BB amps = 7 amps */ +- if (g[0] == 0 || ref < (g[1] - g[0])) /* if total gain of the current amp is null or this amp is not concerned because it starts to work from an higher gain value */ +- v = 0; /* force the gain to write for the current amp to be null */ +- else if (ref >= g[1]) /* Gain to set is higher than the high working point of this amp */ +- v = g[2]; /* force this amp to be full gain */ +- else /* compute the value to set to this amp because we are somewhere in his range */ +- v = ((ref - (g[1] - g[0])) * g[2]) / g[0]; +- +- if (i == 0) /* LNA 1 reg mapping */ +- gain_reg[0] = v; +- else if (i == 1) /* LNA 2 reg mapping */ +- gain_reg[0] |= v << 7; +- else if (i == 2) /* LNA 3 reg mapping */ +- gain_reg[1] = v; +- else if (i == 3) /* LNA 4 reg mapping */ +- gain_reg[1] |= v << 7; +- else if (i == 4) /* CBAND LNA reg mapping */ +- gain_reg[2] = v | state->rf_lt_def; +- else if (i == 5) /* BB gain 1 reg mapping */ +- gain_reg[3] = v << 3; +- else if (i == 6) /* BB gain 2 reg mapping */ +- gain_reg[3] |= v << 8; +- +- g += 3; /* go to next gain bloc */ +- +- /* When RF is finished, start with BB */ +- if (i == 4) { +- g = state->bb_ramp + 1; /* point on BB gain 1 max gain */ +- ref = bb; +- } +- } +- gain_reg[3] |= state->bb_1_def; +- gain_reg[3] |= ((bb % 10) * 100) / 125; +- +-#ifdef DEBUG_AGC +- dprintk("GA CALC: DB: %3d(rf) + %3d(bb) = %3d gain_reg[0]=%04x gain_reg[1]=%04x gain_reg[2]=%04x gain_reg[0]=%04x", rf, bb, rf + bb, +- gain_reg[0], gain_reg[1], gain_reg[2], gain_reg[3]); +-#endif +- +- /* Write the amplifier regs */ +- for (i = 0; i < 4; i++) { +- v = gain_reg[i]; +- if (force || state->gain_reg[i] != v) { +- state->gain_reg[i] = v; +- dib0090_write_reg(state, gain_reg_addr[i], v); +- } +- } +-} +- +-static void dib0090_set_boost(struct dib0090_state *state, int onoff) +-{ +- state->bb_1_def &= 0xdfff; +- state->bb_1_def |= onoff << 13; +-} +- +-static void dib0090_set_rframp(struct dib0090_state *state, const u16 * cfg) +-{ +- state->rf_ramp = cfg; +-} +- +-static void dib0090_set_rframp_pwm(struct dib0090_state *state, const u16 * cfg) +-{ +- state->rf_ramp = cfg; +- +- dib0090_write_reg(state, 0x2a, 0xffff); +- +- dprintk("total RF gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x2a)); +- +- dib0090_write_regs(state, 0x2c, cfg + 3, 6); +- dib0090_write_regs(state, 0x3e, cfg + 9, 2); +-} +- +-static void dib0090_set_bbramp(struct dib0090_state *state, const u16 * cfg) +-{ +- state->bb_ramp = cfg; +- dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ +-} +- +-static void dib0090_set_bbramp_pwm(struct dib0090_state *state, const u16 * cfg) +-{ +- state->bb_ramp = cfg; +- +- dib0090_set_boost(state, cfg[0] > 500); /* we want the boost if the gain is higher that 50dB */ +- +- dib0090_write_reg(state, 0x33, 0xffff); +- dprintk("total BB gain: %ddB, step: %d", (u32) cfg[0], dib0090_read_reg(state, 0x33)); +- dib0090_write_regs(state, 0x35, cfg + 3, 4); +-} +- +-void dib0090_pwm_gain_reset(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- /* reset the AGC */ +- +- if (state->config->use_pwm_agc) { +-#ifdef CONFIG_BAND_SBAND +- if (state->current_band == BAND_SBAND) { +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_sband); +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_boost); +- } else +-#endif +-#ifdef CONFIG_BAND_CBAND +- if (state->current_band == BAND_CBAND) { +- if (state->identity.in_soc) { +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); +- if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_8090); +- else if (state->identity.version == SOC_7090_P1G_11R1 +- || state->identity.version == SOC_7090_P1G_21R1) { +- if (state->config->is_dib7090e) { +- if (state->rf_ramp == NULL) +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090e_sensitivity); +- else +- dib0090_set_rframp_pwm(state, state->rf_ramp); +- } else +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband_7090); +- } +- } else { +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_cband); +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); +- } +- } else +-#endif +-#ifdef CONFIG_BAND_VHF +- if (state->current_band == BAND_VHF) { +- if (state->identity.in_soc) { +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); +- } else { +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_vhf); +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); +- } +- } else +-#endif +- { +- if (state->identity.in_soc) { +- if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_8090); +- else if (state->identity.version == SOC_7090_P1G_11R1 || state->identity.version == SOC_7090_P1G_21R1) +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf_7090); +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal_socs); +- } else { +- dib0090_set_rframp_pwm(state, rf_ramp_pwm_uhf); +- dib0090_set_bbramp_pwm(state, bb_ramp_pwm_normal); +- } +- } +- +- if (state->rf_ramp[0] != 0) +- dib0090_write_reg(state, 0x32, (3 << 11)); +- else +- dib0090_write_reg(state, 0x32, (0 << 11)); +- +- dib0090_write_reg(state, 0x04, 0x03); +- dib0090_write_reg(state, 0x39, (1 << 10)); +- } +-} +- +-EXPORT_SYMBOL(dib0090_pwm_gain_reset); +- +-void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- if (DC_servo_cutoff < 4) +- dib0090_write_reg(state, 0x04, DC_servo_cutoff); +-} +-EXPORT_SYMBOL(dib0090_set_dc_servo); +- +-static u32 dib0090_get_slow_adc_val(struct dib0090_state *state) +-{ +- u16 adc_val = dib0090_read_reg(state, 0x1d); +- if (state->identity.in_soc) +- adc_val >>= 2; +- return adc_val; +-} +- +-int dib0090_gain_control(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- enum frontend_tune_state *tune_state = &state->tune_state; +- int ret = 10; +- +- u16 wbd_val = 0; +- u8 apply_gain_immediatly = 1; +- s16 wbd_error = 0, adc_error = 0; +- +- if (*tune_state == CT_AGC_START) { +- state->agc_freeze = 0; +- dib0090_write_reg(state, 0x04, 0x0); +- +-#ifdef CONFIG_BAND_SBAND +- if (state->current_band == BAND_SBAND) { +- dib0090_set_rframp(state, rf_ramp_sband); +- dib0090_set_bbramp(state, bb_ramp_boost); +- } else +-#endif +-#ifdef CONFIG_BAND_VHF +- if (state->current_band == BAND_VHF && !state->identity.p1g) { +- dib0090_set_rframp(state, rf_ramp_vhf); +- dib0090_set_bbramp(state, bb_ramp_boost); +- } else +-#endif +-#ifdef CONFIG_BAND_CBAND +- if (state->current_band == BAND_CBAND && !state->identity.p1g) { +- dib0090_set_rframp(state, rf_ramp_cband); +- dib0090_set_bbramp(state, bb_ramp_boost); +- } else +-#endif +- if ((state->current_band == BAND_CBAND || state->current_band == BAND_VHF) && state->identity.p1g) { +- dib0090_set_rframp(state, rf_ramp_cband_broadmatching); +- dib0090_set_bbramp(state, bb_ramp_boost); +- } else { +- dib0090_set_rframp(state, rf_ramp_uhf); +- dib0090_set_bbramp(state, bb_ramp_boost); +- } +- +- dib0090_write_reg(state, 0x32, 0); +- dib0090_write_reg(state, 0x39, 0); +- +- dib0090_wbd_target(state, state->current_rf); +- +- state->rf_gain_limit = state->rf_ramp[0] << WBD_ALPHA; +- state->current_gain = ((state->rf_ramp[0] + state->bb_ramp[0]) / 2) << GAIN_ALPHA; +- +- *tune_state = CT_AGC_STEP_0; +- } else if (!state->agc_freeze) { +- s16 wbd = 0, i, cnt; +- +- int adc; +- wbd_val = dib0090_get_slow_adc_val(state); +- +- if (*tune_state == CT_AGC_STEP_0) +- cnt = 5; +- else +- cnt = 1; +- +- for (i = 0; i < cnt; i++) { +- wbd_val = dib0090_get_slow_adc_val(state); +- wbd += dib0090_wbd_to_db(state, wbd_val); +- } +- wbd /= cnt; +- wbd_error = state->wbd_target - wbd; +- +- if (*tune_state == CT_AGC_STEP_0) { +- if (wbd_error < 0 && state->rf_gain_limit > 0 && !state->identity.p1g) { +-#ifdef CONFIG_BAND_CBAND +- /* in case of CBAND tune reduce first the lt_gain2 before adjusting the RF gain */ +- u8 ltg2 = (state->rf_lt_def >> 10) & 0x7; +- if (state->current_band == BAND_CBAND && ltg2) { +- ltg2 >>= 1; +- state->rf_lt_def &= ltg2 << 10; /* reduce in 3 steps from 7 to 0 */ +- } +-#endif +- } else { +- state->agc_step = 0; +- *tune_state = CT_AGC_STEP_1; +- } +- } else { +- /* calc the adc power */ +- adc = state->config->get_adc_power(fe); +- adc = (adc * ((s32) 355774) + (((s32) 1) << 20)) >> 21; /* included in [0:-700] */ +- +- adc_error = (s16) (((s32) ADC_TARGET) - adc); +-#ifdef CONFIG_STANDARD_DAB +- if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) +- adc_error -= 10; +-#endif +-#ifdef CONFIG_STANDARD_DVBT +- if (state->fe->dtv_property_cache.delivery_system == STANDARD_DVBT && +- (state->fe->dtv_property_cache.modulation == QAM_64 || state->fe->dtv_property_cache.modulation == QAM_16)) +- adc_error += 60; +-#endif +-#ifdef CONFIG_SYS_ISDBT +- if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT) && (((state->fe->dtv_property_cache.layer[0].segment_count > +- 0) +- && +- ((state->fe->dtv_property_cache.layer[0].modulation == +- QAM_64) +- || (state->fe->dtv_property_cache. +- layer[0].modulation == QAM_16))) +- || +- ((state->fe->dtv_property_cache.layer[1].segment_count > +- 0) +- && +- ((state->fe->dtv_property_cache.layer[1].modulation == +- QAM_64) +- || (state->fe->dtv_property_cache. +- layer[1].modulation == QAM_16))) +- || +- ((state->fe->dtv_property_cache.layer[2].segment_count > +- 0) +- && +- ((state->fe->dtv_property_cache.layer[2].modulation == +- QAM_64) +- || (state->fe->dtv_property_cache. +- layer[2].modulation == QAM_16))) +- ) +- ) +- adc_error += 60; +-#endif +- +- if (*tune_state == CT_AGC_STEP_1) { /* quickly go to the correct range of the ADC power */ +- if (ABS(adc_error) < 50 || state->agc_step++ > 5) { +- +-#ifdef CONFIG_STANDARD_DAB +- if (state->fe->dtv_property_cache.delivery_system == STANDARD_DAB) { +- dib0090_write_reg(state, 0x02, (1 << 15) | (15 << 11) | (31 << 6) | (63)); /* cap value = 63 : narrow BB filter : Fc = 1.8MHz */ +- dib0090_write_reg(state, 0x04, 0x0); +- } else +-#endif +- { +- dib0090_write_reg(state, 0x02, (1 << 15) | (3 << 11) | (6 << 6) | (32)); +- dib0090_write_reg(state, 0x04, 0x01); /*0 = 1KHz ; 1 = 150Hz ; 2 = 50Hz ; 3 = 50KHz ; 4 = servo fast */ +- } +- +- *tune_state = CT_AGC_STOP; +- } +- } else { +- /* everything higher than or equal to CT_AGC_STOP means tracking */ +- ret = 100; /* 10ms interval */ +- apply_gain_immediatly = 0; +- } +- } +-#ifdef DEBUG_AGC +- dprintk +- ("tune state %d, ADC = %3ddB (ADC err %3d) WBD %3ddB (WBD err %3d, WBD val SADC: %4d), RFGainLimit (TOP): %3d, signal: %3ddBm", +- (u32) *tune_state, (u32) adc, (u32) adc_error, (u32) wbd, (u32) wbd_error, (u32) wbd_val, +- (u32) state->rf_gain_limit >> WBD_ALPHA, (s32) 200 + adc - (state->current_gain >> GAIN_ALPHA)); +-#endif +- } +- +- /* apply gain */ +- if (!state->agc_freeze) +- dib0090_gain_apply(state, adc_error, wbd_error, apply_gain_immediatly); +- return ret; +-} +- +-EXPORT_SYMBOL(dib0090_gain_control); +- +-void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- if (rf) +- *rf = state->gain[0]; +- if (bb) +- *bb = state->gain[1]; +- if (rf_gain_limit) +- *rf_gain_limit = state->rf_gain_limit; +- if (rflt) +- *rflt = (state->rf_lt_def >> 10) & 0x7; +-} +- +-EXPORT_SYMBOL(dib0090_get_current_gain); +- +-u16 dib0090_get_wbd_target(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- u32 f_MHz = state->fe->dtv_property_cache.frequency / 1000000; +- s32 current_temp = state->temperature; +- s32 wbd_thot, wbd_tcold; +- const struct dib0090_wbd_slope *wbd = state->current_wbd_table; +- +- while (f_MHz > wbd->max_freq) +- wbd++; +- +- dprintk("using wbd-table-entry with max freq %d", wbd->max_freq); +- +- if (current_temp < 0) +- current_temp = 0; +- if (current_temp > 128) +- current_temp = 128; +- +- state->wbdmux &= ~(7 << 13); +- if (wbd->wbd_gain != 0) +- state->wbdmux |= (wbd->wbd_gain << 13); +- else +- state->wbdmux |= (4 << 13); +- +- dib0090_write_reg(state, 0x10, state->wbdmux); +- +- wbd_thot = wbd->offset_hot - (((u32) wbd->slope_hot * f_MHz) >> 6); +- wbd_tcold = wbd->offset_cold - (((u32) wbd->slope_cold * f_MHz) >> 6); +- +- wbd_tcold += ((wbd_thot - wbd_tcold) * current_temp) >> 7; +- +- state->wbd_target = dib0090_wbd_to_db(state, state->wbd_offset + wbd_tcold); +- dprintk("wbd-target: %d dB", (u32) state->wbd_target); +- dprintk("wbd offset applied is %d", wbd_tcold); +- +- return state->wbd_offset + wbd_tcold; +-} +-EXPORT_SYMBOL(dib0090_get_wbd_target); +- +-u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- return state->wbd_offset; +-} +-EXPORT_SYMBOL(dib0090_get_wbd_offset); +- +-int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xfff8) +- | ((sw3 & 1) << 2) | ((sw2 & 1) << 1) | (sw1 & 1)); +- +- return 0; +-} +-EXPORT_SYMBOL(dib0090_set_switch); +- +-int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x7fff) +- | ((onoff & 1) << 15)); +- return 0; +-} +-EXPORT_SYMBOL(dib0090_set_vga); +- +-int dib0090_update_rframp_7090(struct dvb_frontend *fe, u8 cfg_sensitivity) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- if ((!state->identity.p1g) || (!state->identity.in_soc) +- || ((state->identity.version != SOC_7090_P1G_21R1) +- && (state->identity.version != SOC_7090_P1G_11R1))) { +- dprintk("%s() function can only be used for dib7090P", __func__); +- return -ENODEV; +- } +- +- if (cfg_sensitivity) +- state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_sensitivity; +- else +- state->rf_ramp = (const u16 *)&rf_ramp_pwm_cband_7090e_aci; +- dib0090_pwm_gain_reset(fe); +- +- return 0; +-} +-EXPORT_SYMBOL(dib0090_update_rframp_7090); +- +-static const u16 dib0090_defaults[] = { +- +- 25, 0x01, +- 0x0000, +- 0x99a0, +- 0x6008, +- 0x0000, +- 0x8bcb, +- 0x0000, +- 0x0405, +- 0x0000, +- 0x0000, +- 0x0000, +- 0xb802, +- 0x0300, +- 0x2d12, +- 0xbac0, +- 0x7c00, +- 0xdbb9, +- 0x0954, +- 0x0743, +- 0x8000, +- 0x0001, +- 0x0040, +- 0x0100, +- 0x0000, +- 0xe910, +- 0x149e, +- +- 1, 0x1c, +- 0xff2d, +- +- 1, 0x39, +- 0x0000, +- +- 2, 0x1e, +- 0x07FF, +- 0x0007, +- +- 1, 0x24, +- EN_UHF | EN_CRYSTAL, +- +- 2, 0x3c, +- 0x3ff, +- 0x111, +- 0 +-}; +- +-static const u16 dib0090_p1g_additionnal_defaults[] = { +- 1, 0x05, +- 0xabcd, +- +- 1, 0x11, +- 0x00b4, +- +- 1, 0x1c, +- 0xfffd, +- +- 1, 0x40, +- 0x108, +- 0 +-}; +- +-static void dib0090_set_default_config(struct dib0090_state *state, const u16 * n) +-{ +- u16 l, r; +- +- l = pgm_read_word(n++); +- while (l) { +- r = pgm_read_word(n++); +- do { +- dib0090_write_reg(state, r, pgm_read_word(n++)); +- r++; +- } while (--l); +- l = pgm_read_word(n++); +- } +-} +- +-#define CAP_VALUE_MIN (u8) 9 +-#define CAP_VALUE_MAX (u8) 40 +-#define HR_MIN (u8) 25 +-#define HR_MAX (u8) 40 +-#define POLY_MIN (u8) 0 +-#define POLY_MAX (u8) 8 +- +-static void dib0090_set_EFUSE(struct dib0090_state *state) +-{ +- u8 c, h, n; +- u16 e2, e4; +- u16 cal; +- +- e2 = dib0090_read_reg(state, 0x26); +- e4 = dib0090_read_reg(state, 0x28); +- +- if ((state->identity.version == P1D_E_F) || +- (state->identity.version == P1G) || (e2 == 0xffff)) { +- +- dib0090_write_reg(state, 0x22, 0x10); +- cal = (dib0090_read_reg(state, 0x22) >> 6) & 0x3ff; +- +- if ((cal < 670) || (cal == 1023)) +- cal = 850; +- n = 165 - ((cal * 10)>>6) ; +- e2 = e4 = (3<<12) | (34<<6) | (n); +- } +- +- if (e2 != e4) +- e2 &= e4; /* Remove the redundancy */ +- +- if (e2 != 0xffff) { +- c = e2 & 0x3f; +- n = (e2 >> 12) & 0xf; +- h = (e2 >> 6) & 0x3f; +- +- if ((c >= CAP_VALUE_MAX) || (c <= CAP_VALUE_MIN)) +- c = 32; +- if ((h >= HR_MAX) || (h <= HR_MIN)) +- h = 34; +- if ((n >= POLY_MAX) || (n <= POLY_MIN)) +- n = 3; +- +- dib0090_write_reg(state, 0x13, (h << 10)) ; +- e2 = (n<<11) | ((h>>2)<<6) | (c); +- dib0090_write_reg(state, 0x2, e2) ; /* Load the BB_2 */ +- } +-} +- +-static int dib0090_reset(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- dib0090_reset_digital(fe, state->config); +- if (dib0090_identify(fe) < 0) +- return -EIO; +- +-#ifdef CONFIG_TUNER_DIB0090_P1B_SUPPORT +- if (!(state->identity.version & 0x1)) /* it is P1B - reset is already done */ +- return 0; +-#endif +- +- if (!state->identity.in_soc) { +- if ((dib0090_read_reg(state, 0x1a) >> 5) & 0x2) +- dib0090_write_reg(state, 0x1b, (EN_IQADC | EN_BB | EN_BIAS | EN_DIGCLK | EN_PLL | EN_CRYSTAL)); +- else +- dib0090_write_reg(state, 0x1b, (EN_DIGCLK | EN_PLL | EN_CRYSTAL)); +- } +- +- dib0090_set_default_config(state, dib0090_defaults); +- +- if (state->identity.in_soc) +- dib0090_write_reg(state, 0x18, 0x2910); /* charge pump current = 0 */ +- +- if (state->identity.p1g) +- dib0090_set_default_config(state, dib0090_p1g_additionnal_defaults); +- +- /* Update the efuse : Only available for KROSUS > P1C and SOC as well*/ +- if (((state->identity.version & 0x1f) >= P1D_E_F) || (state->identity.in_soc)) +- dib0090_set_EFUSE(state); +- +- /* Congigure in function of the crystal */ +- if (state->config->force_crystal_mode != 0) +- dib0090_write_reg(state, 0x14, +- state->config->force_crystal_mode & 3); +- else if (state->config->io.clock_khz >= 24000) +- dib0090_write_reg(state, 0x14, 1); +- else +- dib0090_write_reg(state, 0x14, 2); +- dprintk("Pll lock : %d", (dib0090_read_reg(state, 0x1a) >> 11) & 0x1); +- +- state->calibrate = DC_CAL | WBD_CAL | TEMP_CAL; /* enable iq-offset-calibration and wbd-calibration when tuning next time */ +- +- return 0; +-} +- +-#define steps(u) (((u) > 15) ? ((u)-16) : (u)) +-#define INTERN_WAIT 10 +-static int dib0090_get_offset(struct dib0090_state *state, enum frontend_tune_state *tune_state) +-{ +- int ret = INTERN_WAIT * 10; +- +- switch (*tune_state) { +- case CT_TUNER_STEP_2: +- /* Turns to positive */ +- dib0090_write_reg(state, 0x1f, 0x7); +- *tune_state = CT_TUNER_STEP_3; +- break; +- +- case CT_TUNER_STEP_3: +- state->adc_diff = dib0090_read_reg(state, 0x1d); +- +- /* Turns to negative */ +- dib0090_write_reg(state, 0x1f, 0x4); +- *tune_state = CT_TUNER_STEP_4; +- break; +- +- case CT_TUNER_STEP_4: +- state->adc_diff -= dib0090_read_reg(state, 0x1d); +- *tune_state = CT_TUNER_STEP_5; +- ret = 0; +- break; +- +- default: +- break; +- } +- +- return ret; +-} +- +-struct dc_calibration { +- u8 addr; +- u8 offset; +- u8 pga:1; +- u16 bb1; +- u8 i:1; +-}; +- +-static const struct dc_calibration dc_table[] = { +- /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ +- {0x06, 5, 1, (1 << 13) | (0 << 8) | (26 << 3), 1}, +- {0x07, 11, 1, (1 << 13) | (0 << 8) | (26 << 3), 0}, +- /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ +- {0x06, 0, 0, (1 << 13) | (29 << 8) | (26 << 3), 1}, +- {0x06, 10, 0, (1 << 13) | (29 << 8) | (26 << 3), 0}, +- {0}, +-}; +- +-static const struct dc_calibration dc_p1g_table[] = { +- /* Step1 BB gain1= 26 with boost 1, gain 2 = 0 */ +- /* addr ; trim reg offset ; pga ; CTRL_BB1 value ; i or q */ +- {0x06, 5, 1, (1 << 13) | (0 << 8) | (15 << 3), 1}, +- {0x07, 11, 1, (1 << 13) | (0 << 8) | (15 << 3), 0}, +- /* Step 2 BB gain 1 = 26 with boost = 1 & gain 2 = 29 */ +- {0x06, 0, 0, (1 << 13) | (29 << 8) | (15 << 3), 1}, +- {0x06, 10, 0, (1 << 13) | (29 << 8) | (15 << 3), 0}, +- {0}, +-}; +- +-static void dib0090_set_trim(struct dib0090_state *state) +-{ +- u16 *val; +- +- if (state->dc->addr == 0x07) +- val = &state->bb7; +- else +- val = &state->bb6; +- +- *val &= ~(0x1f << state->dc->offset); +- *val |= state->step << state->dc->offset; +- +- dib0090_write_reg(state, state->dc->addr, *val); +-} +- +-static int dib0090_dc_offset_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) +-{ +- int ret = 0; +- u16 reg; +- +- switch (*tune_state) { +- case CT_TUNER_START: +- dprintk("Start DC offset calibration"); +- +- /* force vcm2 = 0.8V */ +- state->bb6 = 0; +- state->bb7 = 0x040d; +- +- /* the LNA AND LO are off */ +- reg = dib0090_read_reg(state, 0x24) & 0x0ffb; /* shutdown lna and lo */ +- dib0090_write_reg(state, 0x24, reg); +- +- state->wbdmux = dib0090_read_reg(state, 0x10); +- dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x7 << 3) | 0x3); +- dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); +- +- state->dc = dc_table; +- +- if (state->identity.p1g) +- state->dc = dc_p1g_table; +- *tune_state = CT_TUNER_STEP_0; +- +- /* fall through */ +- +- case CT_TUNER_STEP_0: +- dprintk("Sart/continue DC calibration for %s path", (state->dc->i == 1) ? "I" : "Q"); +- dib0090_write_reg(state, 0x01, state->dc->bb1); +- dib0090_write_reg(state, 0x07, state->bb7 | (state->dc->i << 7)); +- +- state->step = 0; +- state->min_adc_diff = 1023; +- *tune_state = CT_TUNER_STEP_1; +- ret = 50; +- break; +- +- case CT_TUNER_STEP_1: +- dib0090_set_trim(state); +- *tune_state = CT_TUNER_STEP_2; +- break; +- +- case CT_TUNER_STEP_2: +- case CT_TUNER_STEP_3: +- case CT_TUNER_STEP_4: +- ret = dib0090_get_offset(state, tune_state); +- break; +- +- case CT_TUNER_STEP_5: /* found an offset */ +- dprintk("adc_diff = %d, current step= %d", (u32) state->adc_diff, state->step); +- if (state->step == 0 && state->adc_diff < 0) { +- state->min_adc_diff = -1023; +- dprintk("Change of sign of the minimum adc diff"); +- } +- +- dprintk("adc_diff = %d, min_adc_diff = %d current_step = %d", state->adc_diff, state->min_adc_diff, state->step); +- +- /* first turn for this frequency */ +- if (state->step == 0) { +- if (state->dc->pga && state->adc_diff < 0) +- state->step = 0x10; +- if (state->dc->pga == 0 && state->adc_diff > 0) +- state->step = 0x10; +- } +- +- /* Look for a change of Sign in the Adc_diff.min_adc_diff is used to STORE the setp N-1 */ +- if ((state->adc_diff & 0x8000) == (state->min_adc_diff & 0x8000) && steps(state->step) < 15) { +- /* stop search when the delta the sign is changing and Steps =15 and Step=0 is force for continuance */ +- state->step++; +- state->min_adc_diff = state->adc_diff; +- *tune_state = CT_TUNER_STEP_1; +- } else { +- /* the minimum was what we have seen in the step before */ +- if (ABS(state->adc_diff) > ABS(state->min_adc_diff)) { +- dprintk("Since adc_diff N = %d > adc_diff step N-1 = %d, Come back one step", state->adc_diff, state->min_adc_diff); +- state->step--; +- } +- +- dib0090_set_trim(state); +- dprintk("BB Offset Cal, BBreg=%hd,Offset=%hd,Value Set=%hd", state->dc->addr, state->adc_diff, state->step); +- +- state->dc++; +- if (state->dc->addr == 0) /* done */ +- *tune_state = CT_TUNER_STEP_6; +- else +- *tune_state = CT_TUNER_STEP_0; +- +- } +- break; +- +- case CT_TUNER_STEP_6: +- dib0090_write_reg(state, 0x07, state->bb7 & ~0x0008); +- dib0090_write_reg(state, 0x1f, 0x7); +- *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ +- state->calibrate &= ~DC_CAL; +- default: +- break; +- } +- return ret; +-} +- +-static int dib0090_wbd_calibration(struct dib0090_state *state, enum frontend_tune_state *tune_state) +-{ +- u8 wbd_gain; +- const struct dib0090_wbd_slope *wbd = state->current_wbd_table; +- +- switch (*tune_state) { +- case CT_TUNER_START: +- while (state->current_rf / 1000 > wbd->max_freq) +- wbd++; +- if (wbd->wbd_gain != 0) +- wbd_gain = wbd->wbd_gain; +- else { +- wbd_gain = 4; +-#if defined(CONFIG_BAND_LBAND) || defined(CONFIG_BAND_SBAND) +- if ((state->current_band == BAND_LBAND) || (state->current_band == BAND_SBAND)) +- wbd_gain = 2; +-#endif +- } +- +- if (wbd_gain == state->wbd_calibration_gain) { /* the WBD calibration has already been done */ +- *tune_state = CT_TUNER_START; +- state->calibrate &= ~WBD_CAL; +- return 0; +- } +- +- dib0090_write_reg(state, 0x10, 0x1b81 | (1 << 10) | (wbd_gain << 13) | (1 << 3)); +- +- dib0090_write_reg(state, 0x24, ((EN_UHF & 0x0fff) | (1 << 1))); +- *tune_state = CT_TUNER_STEP_0; +- state->wbd_calibration_gain = wbd_gain; +- return 90; /* wait for the WBDMUX to switch and for the ADC to sample */ +- +- case CT_TUNER_STEP_0: +- state->wbd_offset = dib0090_get_slow_adc_val(state); +- dprintk("WBD calibration offset = %d", state->wbd_offset); +- *tune_state = CT_TUNER_START; /* reset done -> real tuning can now begin */ +- state->calibrate &= ~WBD_CAL; +- break; +- +- default: +- break; +- } +- return 0; +-} +- +-static void dib0090_set_bandwidth(struct dib0090_state *state) +-{ +- u16 tmp; +- +- if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 5000) +- tmp = (3 << 14); +- else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 6000) +- tmp = (2 << 14); +- else if (state->fe->dtv_property_cache.bandwidth_hz / 1000 <= 7000) +- tmp = (1 << 14); +- else +- tmp = (0 << 14); +- +- state->bb_1_def &= 0x3fff; +- state->bb_1_def |= tmp; +- +- dib0090_write_reg(state, 0x01, state->bb_1_def); /* be sure that we have the right bb-filter */ +- +- dib0090_write_reg(state, 0x03, 0x6008); /* = 0x6008 : vcm3_trim = 1 ; filter2_gm1_trim = 8 ; filter2_cutoff_freq = 0 */ +- dib0090_write_reg(state, 0x04, 0x1); /* 0 = 1KHz ; 1 = 50Hz ; 2 = 150Hz ; 3 = 50KHz ; 4 = servo fast */ +- if (state->identity.in_soc) { +- dib0090_write_reg(state, 0x05, 0x9bcf); /* attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 1 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 15 */ +- } else { +- dib0090_write_reg(state, 0x02, (5 << 11) | (8 << 6) | (22 & 0x3f)); /* 22 = cap_value */ +- dib0090_write_reg(state, 0x05, 0xabcd); /* = 0xabcd : attenuator_ibias_tri = 2 ; input_stage_ibias_tr = 2 ; nc = 11 ; ext_gm_trim = 1 ; obuf_ibias_trim = 4 ; filter13_gm2_ibias_t = 13 */ +- } +-} +- +-static const struct dib0090_pll dib0090_pll_table[] = { +-#ifdef CONFIG_BAND_CBAND +- {56000, 0, 9, 48, 6}, +- {70000, 1, 9, 48, 6}, +- {87000, 0, 8, 32, 4}, +- {105000, 1, 8, 32, 4}, +- {115000, 0, 7, 24, 6}, +- {140000, 1, 7, 24, 6}, +- {170000, 0, 6, 16, 4}, +-#endif +-#ifdef CONFIG_BAND_VHF +- {200000, 1, 6, 16, 4}, +- {230000, 0, 5, 12, 6}, +- {280000, 1, 5, 12, 6}, +- {340000, 0, 4, 8, 4}, +- {380000, 1, 4, 8, 4}, +- {450000, 0, 3, 6, 6}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {580000, 1, 3, 6, 6}, +- {700000, 0, 2, 4, 4}, +- {860000, 1, 2, 4, 4}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1800000, 1, 0, 2, 4}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2900000, 0, 14, 1, 4}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_tuning_table_fm_vhf_on_cband[] = { +- +-#ifdef CONFIG_BAND_CBAND +- {184000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +- {227000, 4, 3, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +- {380000, 4, 7, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, +- {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_tuning_table[] = { +- +-#ifdef CONFIG_BAND_CBAND +- {170000, 4, 1, 15, 0x280, 0x2912, 0xb94e, EN_CAB}, +-#endif +-#ifdef CONFIG_BAND_VHF +- {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +- {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +- {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, +- {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_p1g_tuning_table[] = { +-#ifdef CONFIG_BAND_CBAND +- {170000, 4, 1, 0x820f, 0x300, 0x2d22, 0x82cb, EN_CAB}, +-#endif +-#ifdef CONFIG_BAND_VHF +- {184000, 1, 1, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +- {227000, 1, 3, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +- {380000, 1, 7, 15, 0x300, 0x4d12, 0xb94e, EN_VHF}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {510000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {540000, 2, 1, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {600000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {630000, 2, 4, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {680000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {720000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, +- {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +-#endif +-}; +- +-static const struct dib0090_pll dib0090_p1g_pll_table[] = { +-#ifdef CONFIG_BAND_CBAND +- {57000, 0, 11, 48, 6}, +- {70000, 1, 11, 48, 6}, +- {86000, 0, 10, 32, 4}, +- {105000, 1, 10, 32, 4}, +- {115000, 0, 9, 24, 6}, +- {140000, 1, 9, 24, 6}, +- {170000, 0, 8, 16, 4}, +-#endif +-#ifdef CONFIG_BAND_VHF +- {200000, 1, 8, 16, 4}, +- {230000, 0, 7, 12, 6}, +- {280000, 1, 7, 12, 6}, +- {340000, 0, 6, 8, 4}, +- {380000, 1, 6, 8, 4}, +- {455000, 0, 5, 6, 6}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {580000, 1, 5, 6, 6}, +- {680000, 0, 4, 4, 4}, +- {860000, 1, 4, 4, 4}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1800000, 1, 2, 2, 4}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2900000, 0, 1, 1, 6}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_p1g_tuning_table_fm_vhf_on_cband[] = { +-#ifdef CONFIG_BAND_CBAND +- {184000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, +- {227000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, +- {380000, 4, 3, 0x4187, 0x2c0, 0x2d22, 0x81cb, EN_CAB}, +-#endif +-#ifdef CONFIG_BAND_UHF +- {520000, 2, 0, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {550000, 2, 2, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {650000, 2, 3, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {750000, 2, 5, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {850000, 2, 6, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +- {900000, 2, 7, 15, 0x300, 0x1d12, 0xb9ce, EN_UHF}, +-#endif +-#ifdef CONFIG_BAND_LBAND +- {1500000, 4, 0, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1600000, 4, 1, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +- {1800000, 4, 3, 20, 0x300, 0x1912, 0x82c9, EN_LBD}, +-#endif +-#ifdef CONFIG_BAND_SBAND +- {2300000, 1, 4, 20, 0x300, 0x2d2A, 0x82c7, EN_SBD}, +- {2900000, 1, 7, 20, 0x280, 0x2deb, 0x8347, EN_SBD}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_tuning_table_cband_7090[] = { +-#ifdef CONFIG_BAND_CBAND +- {300000, 4, 3, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +- {380000, 4, 10, 0x018F, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +- {570000, 4, 10, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +- {858000, 4, 5, 0x8190, 0x2c0, 0x2d22, 0xb9ce, EN_CAB}, +-#endif +-}; +- +-static const struct dib0090_tuning dib0090_tuning_table_cband_7090e_sensitivity[] = { +-#ifdef CONFIG_BAND_CBAND +- { 300000, 0 , 3, 0x8105, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, +- { 380000, 0 , 10, 0x810F, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, +- { 600000, 0 , 10, 0x815E, 0x280, 0x2d12, 0xb84e, EN_CAB }, +- { 660000, 0 , 5, 0x85E3, 0x280, 0x2d12, 0xb84e, EN_CAB }, +- { 720000, 0 , 5, 0x852E, 0x280, 0x2d12, 0xb84e, EN_CAB }, +- { 860000, 0 , 4, 0x85E5, 0x280, 0x2d12, 0xb84e, EN_CAB }, +-#endif +-}; +- +-int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, +- u8 cfg_sensitivity) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- const struct dib0090_tuning *tune = +- dib0090_tuning_table_cband_7090e_sensitivity; +- const struct dib0090_tuning dib0090_tuning_table_cband_7090e_aci[] = { +- { 300000, 0 , 3, 0x8165, 0x2c0, 0x2d12, 0xb84e, EN_CAB }, +- { 650000, 0 , 4, 0x815B, 0x280, 0x2d12, 0xb84e, EN_CAB }, +- { 860000, 0 , 5, 0x84EF, 0x280, 0x2d12, 0xb84e, EN_CAB }, +- }; +- +- if ((!state->identity.p1g) || (!state->identity.in_soc) +- || ((state->identity.version != SOC_7090_P1G_21R1) +- && (state->identity.version != SOC_7090_P1G_11R1))) { +- dprintk("%s() function can only be used for dib7090", __func__); +- return -ENODEV; +- } +- +- if (cfg_sensitivity) +- tune = dib0090_tuning_table_cband_7090e_sensitivity; +- else +- tune = dib0090_tuning_table_cband_7090e_aci; +- +- while (state->rf_request > tune->max_freq) +- tune++; +- +- dib0090_write_reg(state, 0x09, (dib0090_read_reg(state, 0x09) & 0x8000) +- | (tune->lna_bias & 0x7fff)); +- dib0090_write_reg(state, 0x0b, (dib0090_read_reg(state, 0x0b) & 0xf83f) +- | ((tune->lna_tune << 6) & 0x07c0)); +- return 0; +-} +-EXPORT_SYMBOL(dib0090_update_tuning_table_7090); +- +-static int dib0090_captrim_search(struct dib0090_state *state, enum frontend_tune_state *tune_state) +-{ +- int ret = 0; +- u16 lo4 = 0xe900; +- +- s16 adc_target; +- u16 adc; +- s8 step_sign; +- u8 force_soft_search = 0; +- +- if (state->identity.version == SOC_8090_P1G_11R1 || state->identity.version == SOC_8090_P1G_21R1) +- force_soft_search = 1; +- +- if (*tune_state == CT_TUNER_START) { +- dprintk("Start Captrim search : %s", (force_soft_search == 1) ? "FORCE SOFT SEARCH" : "AUTO"); +- dib0090_write_reg(state, 0x10, 0x2B1); +- dib0090_write_reg(state, 0x1e, 0x0032); +- +- if (!state->tuner_is_tuned) { +- /* prepare a complete captrim */ +- if (!state->identity.p1g || force_soft_search) +- state->step = state->captrim = state->fcaptrim = 64; +- +- state->current_rf = state->rf_request; +- } else { /* we are already tuned to this frequency - the configuration is correct */ +- if (!state->identity.p1g || force_soft_search) { +- /* do a minimal captrim even if the frequency has not changed */ +- state->step = 4; +- state->captrim = state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7f; +- } +- } +- state->adc_diff = 3000; +- *tune_state = CT_TUNER_STEP_0; +- +- } else if (*tune_state == CT_TUNER_STEP_0) { +- if (state->identity.p1g && !force_soft_search) { +- u8 ratio = 31; +- +- dib0090_write_reg(state, 0x40, (3 << 7) | (ratio << 2) | (1 << 1) | 1); +- dib0090_read_reg(state, 0x40); +- ret = 50; +- } else { +- state->step /= 2; +- dib0090_write_reg(state, 0x18, lo4 | state->captrim); +- +- if (state->identity.in_soc) +- ret = 25; +- } +- *tune_state = CT_TUNER_STEP_1; +- +- } else if (*tune_state == CT_TUNER_STEP_1) { +- if (state->identity.p1g && !force_soft_search) { +- dib0090_write_reg(state, 0x40, 0x18c | (0 << 1) | 0); +- dib0090_read_reg(state, 0x40); +- +- state->fcaptrim = dib0090_read_reg(state, 0x18) & 0x7F; +- dprintk("***Final Captrim= 0x%x", state->fcaptrim); +- *tune_state = CT_TUNER_STEP_3; +- +- } else { +- /* MERGE for all krosus before P1G */ +- adc = dib0090_get_slow_adc_val(state); +- dprintk("CAPTRIM=%d; ADC = %d (ADC) & %dmV", (u32) state->captrim, (u32) adc, (u32) (adc) * (u32) 1800 / (u32) 1024); +- +- if (state->rest == 0 || state->identity.in_soc) { /* Just for 8090P SOCS where auto captrim HW bug : TO CHECK IN ACI for SOCS !!! if 400 for 8090p SOC => tune issue !!! */ +- adc_target = 200; +- } else +- adc_target = 400; +- +- if (adc >= adc_target) { +- adc -= adc_target; +- step_sign = -1; +- } else { +- adc = adc_target - adc; +- step_sign = 1; +- } +- +- if (adc < state->adc_diff) { +- dprintk("CAPTRIM=%d is closer to target (%d/%d)", (u32) state->captrim, (u32) adc, (u32) state->adc_diff); +- state->adc_diff = adc; +- state->fcaptrim = state->captrim; +- } +- +- state->captrim += step_sign * state->step; +- if (state->step >= 1) +- *tune_state = CT_TUNER_STEP_0; +- else +- *tune_state = CT_TUNER_STEP_2; +- +- ret = 25; +- } +- } else if (*tune_state == CT_TUNER_STEP_2) { /* this step is only used by krosus < P1G */ +- /*write the final cptrim config */ +- dib0090_write_reg(state, 0x18, lo4 | state->fcaptrim); +- +- *tune_state = CT_TUNER_STEP_3; +- +- } else if (*tune_state == CT_TUNER_STEP_3) { +- state->calibrate &= ~CAPTRIM_CAL; +- *tune_state = CT_TUNER_STEP_0; +- } +- +- return ret; +-} +- +-static int dib0090_get_temperature(struct dib0090_state *state, enum frontend_tune_state *tune_state) +-{ +- int ret = 15; +- s16 val; +- +- switch (*tune_state) { +- case CT_TUNER_START: +- state->wbdmux = dib0090_read_reg(state, 0x10); +- dib0090_write_reg(state, 0x10, (state->wbdmux & ~(0xff << 3)) | (0x8 << 3)); +- +- state->bias = dib0090_read_reg(state, 0x13); +- dib0090_write_reg(state, 0x13, state->bias | (0x3 << 8)); +- +- *tune_state = CT_TUNER_STEP_0; +- /* wait for the WBDMUX to switch and for the ADC to sample */ +- break; +- +- case CT_TUNER_STEP_0: +- state->adc_diff = dib0090_get_slow_adc_val(state); +- dib0090_write_reg(state, 0x13, (state->bias & ~(0x3 << 8)) | (0x2 << 8)); +- *tune_state = CT_TUNER_STEP_1; +- break; +- +- case CT_TUNER_STEP_1: +- val = dib0090_get_slow_adc_val(state); +- state->temperature = ((s16) ((val - state->adc_diff) * 180) >> 8) + 55; +- +- dprintk("temperature: %d C", state->temperature - 30); +- +- *tune_state = CT_TUNER_STEP_2; +- break; +- +- case CT_TUNER_STEP_2: +- dib0090_write_reg(state, 0x13, state->bias); +- dib0090_write_reg(state, 0x10, state->wbdmux); /* write back original WBDMUX */ +- +- *tune_state = CT_TUNER_START; +- state->calibrate &= ~TEMP_CAL; +- if (state->config->analog_output == 0) +- dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); +- +- break; +- +- default: +- ret = 0; +- break; +- } +- return ret; +-} +- +-#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ +-static int dib0090_tune(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- const struct dib0090_tuning *tune = state->current_tune_table_index; +- const struct dib0090_pll *pll = state->current_pll_table_index; +- enum frontend_tune_state *tune_state = &state->tune_state; +- +- u16 lo5, lo6, Den, tmp; +- u32 FBDiv, Rest, FREF, VCOF_kHz = 0; +- int ret = 10; /* 1ms is the default delay most of the time */ +- u8 c, i; +- +- /************************* VCO ***************************/ +- /* Default values for FG */ +- /* from these are needed : */ +- /* Cp,HFdiv,VCOband,SD,Num,Den,FB and REFDiv */ +- +- /* in any case we first need to do a calibration if needed */ +- if (*tune_state == CT_TUNER_START) { +- /* deactivate DataTX before some calibrations */ +- if (state->calibrate & (DC_CAL | TEMP_CAL | WBD_CAL)) +- dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) & ~(1 << 14)); +- else +- /* Activate DataTX in case a calibration has been done before */ +- if (state->config->analog_output == 0) +- dib0090_write_reg(state, 0x23, dib0090_read_reg(state, 0x23) | (1 << 14)); +- } +- +- if (state->calibrate & DC_CAL) +- return dib0090_dc_offset_calibration(state, tune_state); +- else if (state->calibrate & WBD_CAL) { +- if (state->current_rf == 0) +- state->current_rf = state->fe->dtv_property_cache.frequency / 1000; +- return dib0090_wbd_calibration(state, tune_state); +- } else if (state->calibrate & TEMP_CAL) +- return dib0090_get_temperature(state, tune_state); +- else if (state->calibrate & CAPTRIM_CAL) +- return dib0090_captrim_search(state, tune_state); +- +- if (*tune_state == CT_TUNER_START) { +- /* if soc and AGC pwm control, disengage mux to be able to R/W access to 0x01 register to set the right filter (cutoff_freq_select) during the tune sequence, otherwise, SOC SERPAR error when accessing to 0x01 */ +- if (state->config->use_pwm_agc && state->identity.in_soc) { +- tmp = dib0090_read_reg(state, 0x39); +- if ((tmp >> 10) & 0x1) +- dib0090_write_reg(state, 0x39, tmp & ~(1 << 10)); +- } +- +- state->current_band = (u8) BAND_OF_FREQUENCY(state->fe->dtv_property_cache.frequency / 1000); +- state->rf_request = +- state->fe->dtv_property_cache.frequency / 1000 + (state->current_band == +- BAND_UHF ? state->config->freq_offset_khz_uhf : state->config-> +- freq_offset_khz_vhf); +- +- /* in ISDB-T 1seg we shift tuning frequency */ +- if ((state->fe->dtv_property_cache.delivery_system == SYS_ISDBT && state->fe->dtv_property_cache.isdbt_sb_mode == 1 +- && state->fe->dtv_property_cache.isdbt_partial_reception == 0)) { +- const struct dib0090_low_if_offset_table *LUT_offset = state->config->low_if; +- u8 found_offset = 0; +- u32 margin_khz = 100; +- +- if (LUT_offset != NULL) { +- while (LUT_offset->RF_freq != 0xffff) { +- if (((state->rf_request > (LUT_offset->RF_freq - margin_khz)) +- && (state->rf_request < (LUT_offset->RF_freq + margin_khz))) +- && LUT_offset->std == state->fe->dtv_property_cache.delivery_system) { +- state->rf_request += LUT_offset->offset_khz; +- found_offset = 1; +- break; +- } +- LUT_offset++; +- } +- } +- +- if (found_offset == 0) +- state->rf_request += 400; +- } +- if (state->current_rf != state->rf_request || (state->current_standard != state->fe->dtv_property_cache.delivery_system)) { +- state->tuner_is_tuned = 0; +- state->current_rf = 0; +- state->current_standard = 0; +- +- tune = dib0090_tuning_table; +- if (state->identity.p1g) +- tune = dib0090_p1g_tuning_table; +- +- tmp = (state->identity.version >> 5) & 0x7; +- +- if (state->identity.in_soc) { +- if (state->config->force_cband_input) { /* Use the CBAND input for all band */ +- if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF +- || state->current_band & BAND_UHF) { +- state->current_band = BAND_CBAND; +- if (state->config->is_dib7090e) +- tune = dib0090_tuning_table_cband_7090e_sensitivity; +- else +- tune = dib0090_tuning_table_cband_7090; +- } +- } else { /* Use the CBAND input for all band under UHF */ +- if (state->current_band & BAND_CBAND || state->current_band & BAND_FM || state->current_band & BAND_VHF) { +- state->current_band = BAND_CBAND; +- if (state->config->is_dib7090e) +- tune = dib0090_tuning_table_cband_7090e_sensitivity; +- else +- tune = dib0090_tuning_table_cband_7090; +- } +- } +- } else +- if (tmp == 0x4 || tmp == 0x7) { +- /* CBAND tuner version for VHF */ +- if (state->current_band == BAND_FM || state->current_band == BAND_CBAND || state->current_band == BAND_VHF) { +- state->current_band = BAND_CBAND; /* Force CBAND */ +- +- tune = dib0090_tuning_table_fm_vhf_on_cband; +- if (state->identity.p1g) +- tune = dib0090_p1g_tuning_table_fm_vhf_on_cband; +- } +- } +- +- pll = dib0090_pll_table; +- if (state->identity.p1g) +- pll = dib0090_p1g_pll_table; +- +- /* Look for the interval */ +- while (state->rf_request > tune->max_freq) +- tune++; +- while (state->rf_request > pll->max_freq) +- pll++; +- +- state->current_tune_table_index = tune; +- state->current_pll_table_index = pll; +- +- dib0090_write_reg(state, 0x0b, 0xb800 | (tune->switch_trim)); +- +- VCOF_kHz = (pll->hfdiv * state->rf_request) * 2; +- +- FREF = state->config->io.clock_khz; +- if (state->config->fref_clock_ratio != 0) +- FREF /= state->config->fref_clock_ratio; +- +- FBDiv = (VCOF_kHz / pll->topresc / FREF); +- Rest = (VCOF_kHz / pll->topresc) - FBDiv * FREF; +- +- if (Rest < LPF) +- Rest = 0; +- else if (Rest < 2 * LPF) +- Rest = 2 * LPF; +- else if (Rest > (FREF - LPF)) { +- Rest = 0; +- FBDiv += 1; +- } else if (Rest > (FREF - 2 * LPF)) +- Rest = FREF - 2 * LPF; +- Rest = (Rest * 6528) / (FREF / 10); +- state->rest = Rest; +- +- /* external loop filter, otherwise: +- * lo5 = (0 << 15) | (0 << 12) | (0 << 11) | (3 << 9) | (4 << 6) | (3 << 4) | 4; +- * lo6 = 0x0e34 */ +- +- if (Rest == 0) { +- if (pll->vco_band) +- lo5 = 0x049f; +- else +- lo5 = 0x041f; +- } else { +- if (pll->vco_band) +- lo5 = 0x049e; +- else if (state->config->analog_output) +- lo5 = 0x041d; +- else +- lo5 = 0x041c; +- } +- +- if (state->identity.p1g) { /* Bias is done automatically in P1G */ +- if (state->identity.in_soc) { +- if (state->identity.version == SOC_8090_P1G_11R1) +- lo5 = 0x46f; +- else +- lo5 = 0x42f; +- } else +- lo5 = 0x42c; +- } +- +- lo5 |= (pll->hfdiv_code << 11) | (pll->vco_band << 7); /* bit 15 is the split to the slave, we do not do it here */ +- +- if (!state->config->io.pll_int_loop_filt) { +- if (state->identity.in_soc) +- lo6 = 0xff98; +- else if (state->identity.p1g || (Rest == 0)) +- lo6 = 0xfff8; +- else +- lo6 = 0xff28; +- } else +- lo6 = (state->config->io.pll_int_loop_filt << 3); +- +- Den = 1; +- +- if (Rest > 0) { +- if (state->config->analog_output) +- lo6 |= (1 << 2) | 2; +- else { +- if (state->identity.in_soc) +- lo6 |= (1 << 2) | 2; +- else +- lo6 |= (1 << 2) | 2; +- } +- Den = 255; +- } +- dib0090_write_reg(state, 0x15, (u16) FBDiv); +- if (state->config->fref_clock_ratio != 0) +- dib0090_write_reg(state, 0x16, (Den << 8) | state->config->fref_clock_ratio); +- else +- dib0090_write_reg(state, 0x16, (Den << 8) | 1); +- dib0090_write_reg(state, 0x17, (u16) Rest); +- dib0090_write_reg(state, 0x19, lo5); +- dib0090_write_reg(state, 0x1c, lo6); +- +- lo6 = tune->tuner_enable; +- if (state->config->analog_output) +- lo6 = (lo6 & 0xff9f) | 0x2; +- +- dib0090_write_reg(state, 0x24, lo6 | EN_LO | state->config->use_pwm_agc * EN_CRYSTAL); +- +- } +- +- state->current_rf = state->rf_request; +- state->current_standard = state->fe->dtv_property_cache.delivery_system; +- +- ret = 20; +- state->calibrate = CAPTRIM_CAL; /* captrim serach now */ +- } +- +- else if (*tune_state == CT_TUNER_STEP_0) { /* Warning : because of captrim cal, if you change this step, change it also in _cal.c file because it is the step following captrim cal state machine */ +- const struct dib0090_wbd_slope *wbd = state->current_wbd_table; +- +- while (state->current_rf / 1000 > wbd->max_freq) +- wbd++; +- +- dib0090_write_reg(state, 0x1e, 0x07ff); +- dprintk("Final Captrim: %d", (u32) state->fcaptrim); +- dprintk("HFDIV code: %d", (u32) pll->hfdiv_code); +- dprintk("VCO = %d", (u32) pll->vco_band); +- dprintk("VCOF in kHz: %d ((%d*%d) << 1))", (u32) ((pll->hfdiv * state->rf_request) * 2), (u32) pll->hfdiv, (u32) state->rf_request); +- dprintk("REFDIV: %d, FREF: %d", (u32) 1, (u32) state->config->io.clock_khz); +- dprintk("FBDIV: %d, Rest: %d", (u32) dib0090_read_reg(state, 0x15), (u32) dib0090_read_reg(state, 0x17)); +- dprintk("Num: %d, Den: %d, SD: %d", (u32) dib0090_read_reg(state, 0x17), (u32) (dib0090_read_reg(state, 0x16) >> 8), +- (u32) dib0090_read_reg(state, 0x1c) & 0x3); +- +-#define WBD 0x781 /* 1 1 1 1 0000 0 0 1 */ +- c = 4; +- i = 3; +- +- if (wbd->wbd_gain != 0) +- c = wbd->wbd_gain; +- +- state->wbdmux = (c << 13) | (i << 11) | (WBD | (state->config->use_pwm_agc << 1)); +- dib0090_write_reg(state, 0x10, state->wbdmux); +- +- if ((tune->tuner_enable == EN_CAB) && state->identity.p1g) { +- dprintk("P1G : The cable band is selected and lna_tune = %d", tune->lna_tune); +- dib0090_write_reg(state, 0x09, tune->lna_bias); +- dib0090_write_reg(state, 0x0b, 0xb800 | (tune->lna_tune << 6) | (tune->switch_trim)); +- } else +- dib0090_write_reg(state, 0x09, (tune->lna_tune << 5) | tune->lna_bias); +- +- dib0090_write_reg(state, 0x0c, tune->v2i); +- dib0090_write_reg(state, 0x0d, tune->mix); +- dib0090_write_reg(state, 0x0e, tune->load); +- *tune_state = CT_TUNER_STEP_1; +- +- } else if (*tune_state == CT_TUNER_STEP_1) { +- /* initialize the lt gain register */ +- state->rf_lt_def = 0x7c00; +- +- dib0090_set_bandwidth(state); +- state->tuner_is_tuned = 1; +- +- state->calibrate |= WBD_CAL; +- state->calibrate |= TEMP_CAL; +- *tune_state = CT_TUNER_STOP; +- } else +- ret = FE_CALLBACK_TIME_NEVER; +- return ret; +-} +- +-static int dib0090_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- return state->tune_state; +-} +- +-EXPORT_SYMBOL(dib0090_get_tune_state); +- +-int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- state->tune_state = tune_state; +- return 0; +-} +- +-EXPORT_SYMBOL(dib0090_set_tune_state); +- +-static int dib0090_get_frequency(struct dvb_frontend *fe, u32 * frequency) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- +- *frequency = 1000 * state->current_rf; +- return 0; +-} +- +-static int dib0090_set_params(struct dvb_frontend *fe) +-{ +- struct dib0090_state *state = fe->tuner_priv; +- u32 ret; +- +- state->tune_state = CT_TUNER_START; +- +- do { +- ret = dib0090_tune(fe); +- if (ret != FE_CALLBACK_TIME_NEVER) +- msleep(ret / 10); +- else +- break; +- } while (state->tune_state != CT_TUNER_STOP); +- +- return 0; +-} +- +-static const struct dvb_tuner_ops dib0090_ops = { +- .info = { +- .name = "DiBcom DiB0090", +- .frequency_min = 45000000, +- .frequency_max = 860000000, +- .frequency_step = 1000, +- }, +- .release = dib0090_release, +- +- .init = dib0090_wakeup, +- .sleep = dib0090_sleep, +- .set_params = dib0090_set_params, +- .get_frequency = dib0090_get_frequency, +-}; +- +-static const struct dvb_tuner_ops dib0090_fw_ops = { +- .info = { +- .name = "DiBcom DiB0090", +- .frequency_min = 45000000, +- .frequency_max = 860000000, +- .frequency_step = 1000, +- }, +- .release = dib0090_release, +- +- .init = NULL, +- .sleep = NULL, +- .set_params = NULL, +- .get_frequency = NULL, +-}; +- +-static const struct dib0090_wbd_slope dib0090_wbd_table_default[] = { +- {470, 0, 250, 0, 100, 4}, +- {860, 51, 866, 21, 375, 4}, +- {1700, 0, 800, 0, 850, 4}, +- {2900, 0, 250, 0, 100, 6}, +- {0xFFFF, 0, 0, 0, 0, 0}, +-}; +- +-struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +-{ +- struct dib0090_state *st = kzalloc(sizeof(struct dib0090_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- +- st->config = config; +- st->i2c = i2c; +- st->fe = fe; +- mutex_init(&st->i2c_buffer_lock); +- fe->tuner_priv = st; +- +- if (config->wbd == NULL) +- st->current_wbd_table = dib0090_wbd_table_default; +- else +- st->current_wbd_table = config->wbd; +- +- if (dib0090_reset(fe) != 0) +- goto free_mem; +- +- printk(KERN_INFO "DiB0090: successfully identified\n"); +- memcpy(&fe->ops.tuner_ops, &dib0090_ops, sizeof(struct dvb_tuner_ops)); +- +- return fe; +- free_mem: +- kfree(st); +- fe->tuner_priv = NULL; +- return NULL; +-} +- +-EXPORT_SYMBOL(dib0090_register); +- +-struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +-{ +- struct dib0090_fw_state *st = kzalloc(sizeof(struct dib0090_fw_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- +- st->config = config; +- st->i2c = i2c; +- st->fe = fe; +- mutex_init(&st->i2c_buffer_lock); +- fe->tuner_priv = st; +- +- if (dib0090_fw_reset_digital(fe, st->config) != 0) +- goto free_mem; +- +- dprintk("DiB0090 FW: successfully identified"); +- memcpy(&fe->ops.tuner_ops, &dib0090_fw_ops, sizeof(struct dvb_tuner_ops)); +- +- return fe; +-free_mem: +- kfree(st); +- fe->tuner_priv = NULL; +- return NULL; +-} +-EXPORT_SYMBOL(dib0090_fw_register); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_AUTHOR("Olivier Grenie "); +-MODULE_DESCRIPTION("Driver for the DiBcom 0090 base-band RF Tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib0090.h b/drivers/media/dvb/frontends/dib0090.h +deleted file mode 100644 +index 781dc49..0000000 +--- a/drivers/media/dvb/frontends/dib0090.h ++++ /dev/null +@@ -1,187 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB0090 base-band RF Tuner. +- * +- * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#ifndef DIB0090_H +-#define DIB0090_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-#define DEFAULT_DIB0090_I2C_ADDRESS 0x60 +- +-struct dib0090_io_config { +- u32 clock_khz; +- +- u8 pll_bypass:1; +- u8 pll_range:1; +- u8 pll_prediv:6; +- u8 pll_loopdiv:6; +- +- u8 adc_clock_ratio; /* valid is 8, 7 ,6 */ +- u16 pll_int_loop_filt; +-}; +- +-struct dib0090_wbd_slope { +- u16 max_freq; /* for every frequency less than or equal to that field: this information is correct */ +- u16 slope_cold; +- u16 offset_cold; +- u16 slope_hot; +- u16 offset_hot; +- u8 wbd_gain; +-}; +- +-struct dib0090_low_if_offset_table { +- int std; +- u32 RF_freq; +- s32 offset_khz; +-}; +- +-struct dib0090_config { +- struct dib0090_io_config io; +- int (*reset) (struct dvb_frontend *, int); +- int (*sleep) (struct dvb_frontend *, int); +- +- /* offset in kHz */ +- int freq_offset_khz_uhf; +- int freq_offset_khz_vhf; +- +- int (*get_adc_power) (struct dvb_frontend *); +- +- u8 clkouttobamse:1; /* activate or deactivate clock output */ +- u8 analog_output; +- +- u8 i2c_address; +- /* add drives and other things if necessary */ +- u16 wbd_vhf_offset; +- u16 wbd_cband_offset; +- u8 use_pwm_agc; +- u8 clkoutdrive; +- +- u8 ls_cfg_pad_drv; +- u8 data_tx_drv; +- +- u8 in_soc; +- const struct dib0090_low_if_offset_table *low_if; +- u8 fref_clock_ratio; +- u16 force_cband_input; +- struct dib0090_wbd_slope *wbd; +- u8 is_dib7090e; +- u8 force_crystal_mode; +-}; +- +-#if defined(CONFIG_DVB_TUNER_DIB0090) || (defined(CONFIG_DVB_TUNER_DIB0090_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); +-extern struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config); +-extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast); +-extern void dib0090_pwm_gain_reset(struct dvb_frontend *fe); +-extern u16 dib0090_get_wbd_target(struct dvb_frontend *tuner); +-extern u16 dib0090_get_wbd_offset(struct dvb_frontend *fe); +-extern int dib0090_gain_control(struct dvb_frontend *fe); +-extern enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe); +-extern int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); +-extern void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt); +-extern void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff); +-extern int dib0090_set_switch(struct dvb_frontend *fe, u8 sw1, u8 sw2, u8 sw3); +-extern int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff); +-extern int dib0090_update_rframp_7090(struct dvb_frontend *fe, +- u8 cfg_sensitivity); +-extern int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, +- u8 cfg_sensitivity); +-#else +-static inline struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct dib0090_config *config) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +- +-static inline void dib0090_pwm_gain_reset(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +- +-static inline u16 dib0090_get_wbd_target(struct dvb_frontend *tuner) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +- +-static inline u16 dib0090_get_wbd_offset(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +- +-static inline int dib0090_gain_control(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline enum frontend_tune_state dib0090_get_tune_state(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return CT_DONE; +-} +- +-static inline int dib0090_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline void dib0090_get_current_gain(struct dvb_frontend *fe, u16 * rf, u16 * bb, u16 * rf_gain_limit, u16 * rflt) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +- +-static inline void dib0090_set_dc_servo(struct dvb_frontend *fe, u8 DC_servo_cutoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +- +-static inline int dib0090_set_switch(struct dvb_frontend *fe, +- u8 sw1, u8 sw2, u8 sw3) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib0090_set_vga(struct dvb_frontend *fe, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib0090_update_rframp_7090(struct dvb_frontend *fe, +- u8 cfg_sensitivity) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib0090_update_tuning_table_7090(struct dvb_frontend *fe, +- u8 cfg_sensitivity) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h +deleted file mode 100644 +index 404f63a..0000000 +--- a/drivers/media/dvb/frontends/dib3000.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- * public header file of the frontend drivers for mobile DVB-T demodulators +- * DiBcom 3000M-B and DiBcom 3000P/M-C (http://www.dibcom.fr/) +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * based on GPL code from DibCom, which has +- * +- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- * +- * Acknowledgements +- * +- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver +- * sources, on which this driver (and the dvb-dibusb) are based. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * +- */ +- +-#ifndef DIB3000_H +-#define DIB3000_H +- +-#include +- +-struct dib3000_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-struct dib_fe_xfer_ops +-{ +- /* pid and transfer handling is done in the demodulator */ +- int (*pid_parse)(struct dvb_frontend *fe, int onoff); +- int (*fifo_ctrl)(struct dvb_frontend *fe, int onoff); +- int (*pid_ctrl)(struct dvb_frontend *fe, int index, int pid, int onoff); +- int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl); +-}; +- +-#if defined(CONFIG_DVB_DIB3000MB) || (defined(CONFIG_DVB_DIB3000MB_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, +- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops); +-#else +-static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, +- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_DIB3000MB +- +-#endif // DIB3000_H +diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c +deleted file mode 100644 +index af91e0c..0000000 +--- a/drivers/media/dvb/frontends/dib3000mb.c ++++ /dev/null +@@ -1,829 +0,0 @@ +-/* +- * Frontend driver for mobile DVB-T demodulator DiBcom 3000M-B +- * DiBcom (http://www.dibcom.fr/) +- * +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * based on GPL code from DibCom, which has +- * +- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- * +- * Acknowledgements +- * +- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver +- * sources, on which this driver (and the dvb-dibusb) are based. +- * +- * see Documentation/dvb/README.dvb-usb for more information +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "dib3000.h" +-#include "dib3000mb_priv.h" +- +-/* Version information */ +-#define DRIVER_VERSION "0.1" +-#define DRIVER_DESC "DiBcom 3000M-B DVB-T demodulator" +-#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able))."); +- +-#define deb_info(args...) dprintk(0x01,args) +-#define deb_i2c(args...) dprintk(0x02,args) +-#define deb_srch(args...) dprintk(0x04,args) +-#define deb_info(args...) dprintk(0x01,args) +-#define deb_xfer(args...) dprintk(0x02,args) +-#define deb_setf(args...) dprintk(0x04,args) +-#define deb_getf(args...) dprintk(0x08,args) +- +-static int dib3000_read_reg(struct dib3000_state *state, u16 reg) +-{ +- u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff }; +- u8 rb[2]; +- struct i2c_msg msg[] = { +- { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 }, +- { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 }, +- }; +- +- if (i2c_transfer(state->i2c, msg, 2) != 2) +- deb_i2c("i2c read error\n"); +- +- deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg, +- (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]); +- +- return (rb[0] << 8) | rb[1]; +-} +- +-static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val) +-{ +- u8 b[] = { +- (reg >> 8) & 0xff, reg & 0xff, +- (val >> 8) & 0xff, val & 0xff, +- }; +- struct i2c_msg msg[] = { +- { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 } +- }; +- deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val); +- +- return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0; +-} +- +-static int dib3000_search_status(u16 irq,u16 lock) +-{ +- if (irq & 0x02) { +- if (lock & 0x01) { +- deb_srch("auto search succeeded\n"); +- return 1; // auto search succeeded +- } else { +- deb_srch("auto search not successful\n"); +- return 0; // auto search failed +- } +- } else if (irq & 0x01) { +- deb_srch("auto search failed\n"); +- return 0; // auto search failed +- } +- return -1; // try again +-} +- +-/* for auto search */ +-static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */ +- { /* fft */ +- { /* gua */ +- { 0, 1 }, /* 0 0 { 0,1 } */ +- { 3, 9 }, /* 0 1 { 0,1 } */ +- }, +- { +- { 2, 5 }, /* 1 0 { 0,1 } */ +- { 6, 11 }, /* 1 1 { 0,1 } */ +- } +- }; +- +-static int dib3000mb_get_frontend(struct dvb_frontend* fe); +- +-static int dib3000mb_set_frontend(struct dvb_frontend *fe, int tuner) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- fe_code_rate_t fe_cr = FEC_NONE; +- int search_state, seq; +- +- if (tuner && fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- +- deb_setf("bandwidth: "); +- switch (c->bandwidth_hz) { +- case 8000000: +- deb_setf("8 MHz\n"); +- wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); +- wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); +- break; +- case 7000000: +- deb_setf("7 MHz\n"); +- wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[1]); +- wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_7mhz); +- break; +- case 6000000: +- deb_setf("6 MHz\n"); +- wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[0]); +- wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_6mhz); +- break; +- case 0: +- return -EOPNOTSUPP; +- default: +- err("unknown bandwidth value."); +- return -EINVAL; +- } +- } +- wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); +- +- deb_setf("transmission mode: "); +- switch (c->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- deb_setf("2k\n"); +- wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_2K); +- break; +- case TRANSMISSION_MODE_8K: +- deb_setf("8k\n"); +- wr(DIB3000MB_REG_FFT, DIB3000_TRANSMISSION_MODE_8K); +- break; +- case TRANSMISSION_MODE_AUTO: +- deb_setf("auto\n"); +- break; +- default: +- return -EINVAL; +- } +- +- deb_setf("guard: "); +- switch (c->guard_interval) { +- case GUARD_INTERVAL_1_32: +- deb_setf("1_32\n"); +- wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_32); +- break; +- case GUARD_INTERVAL_1_16: +- deb_setf("1_16\n"); +- wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_16); +- break; +- case GUARD_INTERVAL_1_8: +- deb_setf("1_8\n"); +- wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_8); +- break; +- case GUARD_INTERVAL_1_4: +- deb_setf("1_4\n"); +- wr(DIB3000MB_REG_GUARD_TIME, DIB3000_GUARD_TIME_1_4); +- break; +- case GUARD_INTERVAL_AUTO: +- deb_setf("auto\n"); +- break; +- default: +- return -EINVAL; +- } +- +- deb_setf("inversion: "); +- switch (c->inversion) { +- case INVERSION_OFF: +- deb_setf("off\n"); +- wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_OFF); +- break; +- case INVERSION_AUTO: +- deb_setf("auto "); +- break; +- case INVERSION_ON: +- deb_setf("on\n"); +- wr(DIB3000MB_REG_DDS_INV, DIB3000_DDS_INVERSION_ON); +- break; +- default: +- return -EINVAL; +- } +- +- deb_setf("modulation: "); +- switch (c->modulation) { +- case QPSK: +- deb_setf("qpsk\n"); +- wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_QPSK); +- break; +- case QAM_16: +- deb_setf("qam16\n"); +- wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_16QAM); +- break; +- case QAM_64: +- deb_setf("qam64\n"); +- wr(DIB3000MB_REG_QAM, DIB3000_CONSTELLATION_64QAM); +- break; +- case QAM_AUTO: +- break; +- default: +- return -EINVAL; +- } +- deb_setf("hierarchy: "); +- switch (c->hierarchy) { +- case HIERARCHY_NONE: +- deb_setf("none "); +- /* fall through */ +- case HIERARCHY_1: +- deb_setf("alpha=1\n"); +- wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_1); +- break; +- case HIERARCHY_2: +- deb_setf("alpha=2\n"); +- wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_2); +- break; +- case HIERARCHY_4: +- deb_setf("alpha=4\n"); +- wr(DIB3000MB_REG_VIT_ALPHA, DIB3000_ALPHA_4); +- break; +- case HIERARCHY_AUTO: +- deb_setf("alpha=auto\n"); +- break; +- default: +- return -EINVAL; +- } +- +- deb_setf("hierarchy: "); +- if (c->hierarchy == HIERARCHY_NONE) { +- deb_setf("none\n"); +- wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_OFF); +- wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_HP); +- fe_cr = c->code_rate_HP; +- } else if (c->hierarchy != HIERARCHY_AUTO) { +- deb_setf("on\n"); +- wr(DIB3000MB_REG_VIT_HRCH, DIB3000_HRCH_ON); +- wr(DIB3000MB_REG_VIT_HP, DIB3000_SELECT_LP); +- fe_cr = c->code_rate_LP; +- } +- deb_setf("fec: "); +- switch (fe_cr) { +- case FEC_1_2: +- deb_setf("1_2\n"); +- wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_1_2); +- break; +- case FEC_2_3: +- deb_setf("2_3\n"); +- wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_2_3); +- break; +- case FEC_3_4: +- deb_setf("3_4\n"); +- wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_3_4); +- break; +- case FEC_5_6: +- deb_setf("5_6\n"); +- wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_5_6); +- break; +- case FEC_7_8: +- deb_setf("7_8\n"); +- wr(DIB3000MB_REG_VIT_CODE_RATE, DIB3000_FEC_7_8); +- break; +- case FEC_NONE: +- deb_setf("none "); +- break; +- case FEC_AUTO: +- deb_setf("auto\n"); +- break; +- default: +- return -EINVAL; +- } +- +- seq = dib3000_seq +- [c->transmission_mode == TRANSMISSION_MODE_AUTO] +- [c->guard_interval == GUARD_INTERVAL_AUTO] +- [c->inversion == INVERSION_AUTO]; +- +- deb_setf("seq? %d\n", seq); +- +- wr(DIB3000MB_REG_SEQ, seq); +- +- wr(DIB3000MB_REG_ISI, seq ? DIB3000MB_ISI_INHIBIT : DIB3000MB_ISI_ACTIVATE); +- +- if (c->transmission_mode == TRANSMISSION_MODE_2K) { +- if (c->guard_interval == GUARD_INTERVAL_1_8) { +- wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_2K_1_8); +- } else { +- wr(DIB3000MB_REG_SYNC_IMPROVEMENT, DIB3000MB_SYNC_IMPROVE_DEFAULT); +- } +- +- wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_2K); +- } else { +- wr(DIB3000MB_REG_UNK_121, DIB3000MB_UNK_121_DEFAULT); +- } +- +- wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_OFF); +- wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); +- wr(DIB3000MB_REG_MOBILE_MODE, DIB3000MB_MOBILE_MODE_OFF); +- +- wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_high); +- +- wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_ACTIVATE); +- +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC + DIB3000MB_RESTART_CTRL); +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); +- +- /* wait for AGC lock */ +- msleep(70); +- +- wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); +- +- /* something has to be auto searched */ +- if (c->modulation == QAM_AUTO || +- c->hierarchy == HIERARCHY_AUTO || +- fe_cr == FEC_AUTO || +- c->inversion == INVERSION_AUTO) { +- int as_count=0; +- +- deb_setf("autosearch enabled.\n"); +- +- wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); +- +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AUTO_SEARCH); +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); +- +- while ((search_state = +- dib3000_search_status( +- rd(DIB3000MB_REG_AS_IRQ_PENDING), +- rd(DIB3000MB_REG_LOCK2_VALUE))) < 0 && as_count++ < 100) +- msleep(1); +- +- deb_setf("search_state after autosearch %d after %d checks\n",search_state,as_count); +- +- if (search_state == 1) { +- if (dib3000mb_get_frontend(fe) == 0) { +- deb_setf("reading tuning data from frontend succeeded.\n"); +- return dib3000mb_set_frontend(fe, 0); +- } +- } +- +- } else { +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_CTRL); +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_OFF); +- } +- +- return 0; +-} +- +-static int dib3000mb_fe_init(struct dvb_frontend* fe, int mobile_mode) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- +- deb_info("dib3000mb is getting up.\n"); +- wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_UP); +- +- wr(DIB3000MB_REG_RESTART, DIB3000MB_RESTART_AGC); +- +- wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE); +- wr(DIB3000MB_REG_RESET_DEVICE, DIB3000MB_RESET_DEVICE_RST); +- +- wr(DIB3000MB_REG_CLOCK, DIB3000MB_CLOCK_DEFAULT); +- +- wr(DIB3000MB_REG_ELECT_OUT_MODE, DIB3000MB_ELECT_OUT_MODE_ON); +- +- wr(DIB3000MB_REG_DDS_FREQ_MSB, DIB3000MB_DDS_FREQ_MSB); +- wr(DIB3000MB_REG_DDS_FREQ_LSB, DIB3000MB_DDS_FREQ_LSB); +- +- wr_foreach(dib3000mb_reg_timing_freq, dib3000mb_timing_freq[2]); +- +- wr_foreach(dib3000mb_reg_impulse_noise, +- dib3000mb_impulse_noise_values[DIB3000MB_IMPNOISE_OFF]); +- +- wr_foreach(dib3000mb_reg_agc_gain, dib3000mb_default_agc_gain); +- +- wr(DIB3000MB_REG_PHASE_NOISE, DIB3000MB_PHASE_NOISE_DEFAULT); +- +- wr_foreach(dib3000mb_reg_phase_noise, dib3000mb_default_noise_phase); +- +- wr_foreach(dib3000mb_reg_lock_duration, dib3000mb_default_lock_duration); +- +- wr_foreach(dib3000mb_reg_agc_bandwidth, dib3000mb_agc_bandwidth_low); +- +- wr(DIB3000MB_REG_LOCK0_MASK, DIB3000MB_LOCK0_DEFAULT); +- wr(DIB3000MB_REG_LOCK1_MASK, DIB3000MB_LOCK1_SEARCH_4); +- wr(DIB3000MB_REG_LOCK2_MASK, DIB3000MB_LOCK2_DEFAULT); +- wr(DIB3000MB_REG_SEQ, dib3000_seq[1][1][1]); +- +- wr_foreach(dib3000mb_reg_bandwidth, dib3000mb_bandwidth_8mhz); +- +- wr(DIB3000MB_REG_UNK_68, DIB3000MB_UNK_68); +- wr(DIB3000MB_REG_UNK_69, DIB3000MB_UNK_69); +- wr(DIB3000MB_REG_UNK_71, DIB3000MB_UNK_71); +- wr(DIB3000MB_REG_UNK_77, DIB3000MB_UNK_77); +- wr(DIB3000MB_REG_UNK_78, DIB3000MB_UNK_78); +- wr(DIB3000MB_REG_ISI, DIB3000MB_ISI_INHIBIT); +- wr(DIB3000MB_REG_UNK_92, DIB3000MB_UNK_92); +- wr(DIB3000MB_REG_UNK_96, DIB3000MB_UNK_96); +- wr(DIB3000MB_REG_UNK_97, DIB3000MB_UNK_97); +- wr(DIB3000MB_REG_UNK_106, DIB3000MB_UNK_106); +- wr(DIB3000MB_REG_UNK_107, DIB3000MB_UNK_107); +- wr(DIB3000MB_REG_UNK_108, DIB3000MB_UNK_108); +- wr(DIB3000MB_REG_UNK_122, DIB3000MB_UNK_122); +- wr(DIB3000MB_REG_MOBILE_MODE_QAM, DIB3000MB_MOBILE_MODE_QAM_OFF); +- wr(DIB3000MB_REG_BERLEN, DIB3000MB_BERLEN_DEFAULT); +- +- wr_foreach(dib3000mb_reg_filter_coeffs, dib3000mb_filter_coeffs); +- +- wr(DIB3000MB_REG_MOBILE_ALGO, DIB3000MB_MOBILE_ALGO_ON); +- wr(DIB3000MB_REG_MULTI_DEMOD_MSB, DIB3000MB_MULTI_DEMOD_MSB); +- wr(DIB3000MB_REG_MULTI_DEMOD_LSB, DIB3000MB_MULTI_DEMOD_LSB); +- +- wr(DIB3000MB_REG_OUTPUT_MODE, DIB3000MB_OUTPUT_MODE_SLAVE); +- +- wr(DIB3000MB_REG_FIFO_142, DIB3000MB_FIFO_142); +- wr(DIB3000MB_REG_MPEG2_OUT_MODE, DIB3000MB_MPEG2_OUT_MODE_188); +- wr(DIB3000MB_REG_PID_PARSE, DIB3000MB_PID_PARSE_ACTIVATE); +- wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); +- wr(DIB3000MB_REG_FIFO_146, DIB3000MB_FIFO_146); +- wr(DIB3000MB_REG_FIFO_147, DIB3000MB_FIFO_147); +- +- wr(DIB3000MB_REG_DATA_IN_DIVERSITY, DIB3000MB_DATA_DIVERSITY_IN_OFF); +- +- return 0; +-} +- +-static int dib3000mb_get_frontend(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dib3000_state* state = fe->demodulator_priv; +- fe_code_rate_t *cr; +- u16 tps_val; +- int inv_test1,inv_test2; +- u32 dds_val, threshold = 0x800000; +- +- if (!rd(DIB3000MB_REG_TPS_LOCK)) +- return 0; +- +- dds_val = ((rd(DIB3000MB_REG_DDS_VALUE_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_VALUE_LSB); +- deb_getf("DDS_VAL: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_VALUE_MSB), rd(DIB3000MB_REG_DDS_VALUE_LSB)); +- if (dds_val < threshold) +- inv_test1 = 0; +- else if (dds_val == threshold) +- inv_test1 = 1; +- else +- inv_test1 = 2; +- +- dds_val = ((rd(DIB3000MB_REG_DDS_FREQ_MSB) & 0xff) << 16) + rd(DIB3000MB_REG_DDS_FREQ_LSB); +- deb_getf("DDS_FREQ: %x %x %x",dds_val, rd(DIB3000MB_REG_DDS_FREQ_MSB), rd(DIB3000MB_REG_DDS_FREQ_LSB)); +- if (dds_val < threshold) +- inv_test2 = 0; +- else if (dds_val == threshold) +- inv_test2 = 1; +- else +- inv_test2 = 2; +- +- c->inversion = +- ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) || +- ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ? +- INVERSION_ON : INVERSION_OFF; +- +- deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, c->inversion); +- +- switch ((tps_val = rd(DIB3000MB_REG_TPS_QAM))) { +- case DIB3000_CONSTELLATION_QPSK: +- deb_getf("QPSK "); +- c->modulation = QPSK; +- break; +- case DIB3000_CONSTELLATION_16QAM: +- deb_getf("QAM16 "); +- c->modulation = QAM_16; +- break; +- case DIB3000_CONSTELLATION_64QAM: +- deb_getf("QAM64 "); +- c->modulation = QAM_64; +- break; +- default: +- err("Unexpected constellation returned by TPS (%d)", tps_val); +- break; +- } +- deb_getf("TPS: %d\n", tps_val); +- +- if (rd(DIB3000MB_REG_TPS_HRCH)) { +- deb_getf("HRCH ON\n"); +- cr = &c->code_rate_LP; +- c->code_rate_HP = FEC_NONE; +- switch ((tps_val = rd(DIB3000MB_REG_TPS_VIT_ALPHA))) { +- case DIB3000_ALPHA_0: +- deb_getf("HIERARCHY_NONE "); +- c->hierarchy = HIERARCHY_NONE; +- break; +- case DIB3000_ALPHA_1: +- deb_getf("HIERARCHY_1 "); +- c->hierarchy = HIERARCHY_1; +- break; +- case DIB3000_ALPHA_2: +- deb_getf("HIERARCHY_2 "); +- c->hierarchy = HIERARCHY_2; +- break; +- case DIB3000_ALPHA_4: +- deb_getf("HIERARCHY_4 "); +- c->hierarchy = HIERARCHY_4; +- break; +- default: +- err("Unexpected ALPHA value returned by TPS (%d)", tps_val); +- break; +- } +- deb_getf("TPS: %d\n", tps_val); +- +- tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_LP); +- } else { +- deb_getf("HRCH OFF\n"); +- cr = &c->code_rate_HP; +- c->code_rate_LP = FEC_NONE; +- c->hierarchy = HIERARCHY_NONE; +- +- tps_val = rd(DIB3000MB_REG_TPS_CODE_RATE_HP); +- } +- +- switch (tps_val) { +- case DIB3000_FEC_1_2: +- deb_getf("FEC_1_2 "); +- *cr = FEC_1_2; +- break; +- case DIB3000_FEC_2_3: +- deb_getf("FEC_2_3 "); +- *cr = FEC_2_3; +- break; +- case DIB3000_FEC_3_4: +- deb_getf("FEC_3_4 "); +- *cr = FEC_3_4; +- break; +- case DIB3000_FEC_5_6: +- deb_getf("FEC_5_6 "); +- *cr = FEC_4_5; +- break; +- case DIB3000_FEC_7_8: +- deb_getf("FEC_7_8 "); +- *cr = FEC_7_8; +- break; +- default: +- err("Unexpected FEC returned by TPS (%d)", tps_val); +- break; +- } +- deb_getf("TPS: %d\n",tps_val); +- +- switch ((tps_val = rd(DIB3000MB_REG_TPS_GUARD_TIME))) { +- case DIB3000_GUARD_TIME_1_32: +- deb_getf("GUARD_INTERVAL_1_32 "); +- c->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case DIB3000_GUARD_TIME_1_16: +- deb_getf("GUARD_INTERVAL_1_16 "); +- c->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case DIB3000_GUARD_TIME_1_8: +- deb_getf("GUARD_INTERVAL_1_8 "); +- c->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case DIB3000_GUARD_TIME_1_4: +- deb_getf("GUARD_INTERVAL_1_4 "); +- c->guard_interval = GUARD_INTERVAL_1_4; +- break; +- default: +- err("Unexpected Guard Time returned by TPS (%d)", tps_val); +- break; +- } +- deb_getf("TPS: %d\n", tps_val); +- +- switch ((tps_val = rd(DIB3000MB_REG_TPS_FFT))) { +- case DIB3000_TRANSMISSION_MODE_2K: +- deb_getf("TRANSMISSION_MODE_2K "); +- c->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case DIB3000_TRANSMISSION_MODE_8K: +- deb_getf("TRANSMISSION_MODE_8K "); +- c->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- default: +- err("unexpected transmission mode return by TPS (%d)", tps_val); +- break; +- } +- deb_getf("TPS: %d\n", tps_val); +- +- return 0; +-} +- +-static int dib3000mb_read_status(struct dvb_frontend* fe, fe_status_t *stat) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- +- *stat = 0; +- +- if (rd(DIB3000MB_REG_AGC_LOCK)) +- *stat |= FE_HAS_SIGNAL; +- if (rd(DIB3000MB_REG_CARRIER_LOCK)) +- *stat |= FE_HAS_CARRIER; +- if (rd(DIB3000MB_REG_VIT_LCK)) +- *stat |= FE_HAS_VITERBI; +- if (rd(DIB3000MB_REG_TS_SYNC_LOCK)) +- *stat |= (FE_HAS_SYNC | FE_HAS_LOCK); +- +- deb_getf("actual status is %2x\n",*stat); +- +- deb_getf("autoval: tps: %d, qam: %d, hrch: %d, alpha: %d, hp: %d, lp: %d, guard: %d, fft: %d cell: %d\n", +- rd(DIB3000MB_REG_TPS_LOCK), +- rd(DIB3000MB_REG_TPS_QAM), +- rd(DIB3000MB_REG_TPS_HRCH), +- rd(DIB3000MB_REG_TPS_VIT_ALPHA), +- rd(DIB3000MB_REG_TPS_CODE_RATE_HP), +- rd(DIB3000MB_REG_TPS_CODE_RATE_LP), +- rd(DIB3000MB_REG_TPS_GUARD_TIME), +- rd(DIB3000MB_REG_TPS_FFT), +- rd(DIB3000MB_REG_TPS_CELL_ID)); +- +- //*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- return 0; +-} +- +-static int dib3000mb_read_ber(struct dvb_frontend* fe, u32 *ber) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- +- *ber = ((rd(DIB3000MB_REG_BER_MSB) << 16) | rd(DIB3000MB_REG_BER_LSB)); +- return 0; +-} +- +-/* see dib3000-watch dvb-apps for exact calcuations of signal_strength and snr */ +-static int dib3000mb_read_signal_strength(struct dvb_frontend* fe, u16 *strength) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- +- *strength = rd(DIB3000MB_REG_SIGNAL_POWER) * 0xffff / 0x170; +- return 0; +-} +- +-static int dib3000mb_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- short sigpow = rd(DIB3000MB_REG_SIGNAL_POWER); +- int icipow = ((rd(DIB3000MB_REG_NOISE_POWER_MSB) & 0xff) << 16) | +- rd(DIB3000MB_REG_NOISE_POWER_LSB); +- *snr = (sigpow << 8) / ((icipow > 0) ? icipow : 1); +- return 0; +-} +- +-static int dib3000mb_read_unc_blocks(struct dvb_frontend* fe, u32 *unc) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- +- *unc = rd(DIB3000MB_REG_PACKET_ERROR_RATE); +- return 0; +-} +- +-static int dib3000mb_sleep(struct dvb_frontend* fe) +-{ +- struct dib3000_state* state = fe->demodulator_priv; +- deb_info("dib3000mb is going to bed.\n"); +- wr(DIB3000MB_REG_POWER_CONTROL, DIB3000MB_POWER_DOWN); +- return 0; +-} +- +-static int dib3000mb_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 800; +- return 0; +-} +- +-static int dib3000mb_fe_init_nonmobile(struct dvb_frontend* fe) +-{ +- return dib3000mb_fe_init(fe, 0); +-} +- +-static int dib3000mb_set_frontend_and_tuner(struct dvb_frontend *fe) +-{ +- return dib3000mb_set_frontend(fe, 1); +-} +- +-static void dib3000mb_release(struct dvb_frontend* fe) +-{ +- struct dib3000_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-/* pid filter and transfer stuff */ +-static int dib3000mb_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff) +-{ +- struct dib3000_state *state = fe->demodulator_priv; +- pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0); +- wr(index+DIB3000MB_REG_FIRST_PID,pid); +- return 0; +-} +- +-static int dib3000mb_fifo_control(struct dvb_frontend *fe, int onoff) +-{ +- struct dib3000_state *state = fe->demodulator_priv; +- +- deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling"); +- if (onoff) { +- wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_ACTIVATE); +- } else { +- wr(DIB3000MB_REG_FIFO, DIB3000MB_FIFO_INHIBIT); +- } +- return 0; +-} +- +-static int dib3000mb_pid_parse(struct dvb_frontend *fe, int onoff) +-{ +- struct dib3000_state *state = fe->demodulator_priv; +- deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling"); +- wr(DIB3000MB_REG_PID_PARSE,onoff); +- return 0; +-} +- +-static int dib3000mb_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr) +-{ +- struct dib3000_state *state = fe->demodulator_priv; +- if (onoff) { +- wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr)); +- } else { +- wr(DIB3000MB_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr)); +- } +- return 0; +-} +- +-static struct dvb_frontend_ops dib3000mb_ops; +- +-struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config, +- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops) +-{ +- struct dib3000_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- memcpy(&state->config,config,sizeof(struct dib3000_config)); +- +- /* check for the correct demod */ +- if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM) +- goto error; +- +- if (rd(DIB3000_REG_DEVICE_ID) != DIB3000MB_DEVICE_ID) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &dib3000mb_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- /* set the xfer operations */ +- xfer_ops->pid_parse = dib3000mb_pid_parse; +- xfer_ops->fifo_ctrl = dib3000mb_fifo_control; +- xfer_ops->pid_ctrl = dib3000mb_pid_control; +- xfer_ops->tuner_pass_ctrl = dib3000mb_tuner_pass_ctrl; +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops dib3000mb_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DiBcom 3000M-B DVB-T", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_RECOVER | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib3000mb_release, +- +- .init = dib3000mb_fe_init_nonmobile, +- .sleep = dib3000mb_sleep, +- +- .set_frontend = dib3000mb_set_frontend_and_tuner, +- .get_frontend = dib3000mb_get_frontend, +- .get_tune_settings = dib3000mb_fe_get_tune_settings, +- +- .read_status = dib3000mb_read_status, +- .read_ber = dib3000mb_read_ber, +- .read_signal_strength = dib3000mb_read_signal_strength, +- .read_snr = dib3000mb_read_snr, +- .read_ucblocks = dib3000mb_read_unc_blocks, +-}; +- +-MODULE_AUTHOR(DRIVER_AUTHOR); +-MODULE_DESCRIPTION(DRIVER_DESC); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(dib3000mb_attach); +diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h +deleted file mode 100644 +index 9dc235a..0000000 +--- a/drivers/media/dvb/frontends/dib3000mb_priv.h ++++ /dev/null +@@ -1,556 +0,0 @@ +-/* +- * dib3000mb_priv.h +- * +- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.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, version 2. +- * +- * for more information see dib3000mb.c . +- */ +- +-#ifndef __DIB3000MB_PRIV_H_INCLUDED__ +-#define __DIB3000MB_PRIV_H_INCLUDED__ +- +-/* info and err, taken from usb.h, if there is anything available like by default. */ +-#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg) +-#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg) +-#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg) +- +-/* handy shortcuts */ +-#define rd(reg) dib3000_read_reg(state,reg) +- +-#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \ +- { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; } +- +-#define wr_foreach(a,v) { int i; \ +- if (sizeof(a) != sizeof(v)) \ +- err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\ +- for (i=0; i < sizeof(a)/sizeof(u16); i++) \ +- wr(a[i],v[i]); \ +- } +- +-#define set_or(reg,val) wr(reg,rd(reg) | val) +- +-#define set_and(reg,val) wr(reg,rd(reg) & val) +- +-/* debug */ +- +-#define dprintk(level,args...) \ +- do { if ((debug & level)) { printk(args); } } while (0) +- +-/* mask for enabling a specific pid for the pid_filter */ +-#define DIB3000_ACTIVATE_PID_FILTERING (0x2000) +- +-/* common values for tuning */ +-#define DIB3000_ALPHA_0 ( 0) +-#define DIB3000_ALPHA_1 ( 1) +-#define DIB3000_ALPHA_2 ( 2) +-#define DIB3000_ALPHA_4 ( 4) +- +-#define DIB3000_CONSTELLATION_QPSK ( 0) +-#define DIB3000_CONSTELLATION_16QAM ( 1) +-#define DIB3000_CONSTELLATION_64QAM ( 2) +- +-#define DIB3000_GUARD_TIME_1_32 ( 0) +-#define DIB3000_GUARD_TIME_1_16 ( 1) +-#define DIB3000_GUARD_TIME_1_8 ( 2) +-#define DIB3000_GUARD_TIME_1_4 ( 3) +- +-#define DIB3000_TRANSMISSION_MODE_2K ( 0) +-#define DIB3000_TRANSMISSION_MODE_8K ( 1) +- +-#define DIB3000_SELECT_LP ( 0) +-#define DIB3000_SELECT_HP ( 1) +- +-#define DIB3000_FEC_1_2 ( 1) +-#define DIB3000_FEC_2_3 ( 2) +-#define DIB3000_FEC_3_4 ( 3) +-#define DIB3000_FEC_5_6 ( 5) +-#define DIB3000_FEC_7_8 ( 7) +- +-#define DIB3000_HRCH_OFF ( 0) +-#define DIB3000_HRCH_ON ( 1) +- +-#define DIB3000_DDS_INVERSION_OFF ( 0) +-#define DIB3000_DDS_INVERSION_ON ( 1) +- +-#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8)) +-#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7))) +- +-#define DIB3000_REG_MANUFACTOR_ID ( 1025) +-#define DIB3000_I2C_ID_DIBCOM (0x01b3) +- +-#define DIB3000_REG_DEVICE_ID ( 1026) +-#define DIB3000MB_DEVICE_ID (0x3000) +-#define DIB3000MC_DEVICE_ID (0x3001) +-#define DIB3000P_DEVICE_ID (0x3002) +- +-/* frontend state */ +-struct dib3000_state { +- struct i2c_adapter* i2c; +- +-/* configuration settings */ +- struct dib3000_config config; +- +- struct dvb_frontend frontend; +- int timing_offset; +- int timing_offset_comp_done; +- +- u32 last_tuned_bw; +- u32 last_tuned_freq; +-}; +- +-/* register addresses and some of their default values */ +- +-/* restart subsystems */ +-#define DIB3000MB_REG_RESTART ( 0) +- +-#define DIB3000MB_RESTART_OFF ( 0) +-#define DIB3000MB_RESTART_AUTO_SEARCH (1 << 1) +-#define DIB3000MB_RESTART_CTRL (1 << 2) +-#define DIB3000MB_RESTART_AGC (1 << 3) +- +-/* FFT size */ +-#define DIB3000MB_REG_FFT ( 1) +- +-/* Guard time */ +-#define DIB3000MB_REG_GUARD_TIME ( 2) +- +-/* QAM */ +-#define DIB3000MB_REG_QAM ( 3) +- +-/* Alpha coefficient high priority Viterbi algorithm */ +-#define DIB3000MB_REG_VIT_ALPHA ( 4) +- +-/* spectrum inversion */ +-#define DIB3000MB_REG_DDS_INV ( 5) +- +-/* DDS frequency value (IF position) ad ? values don't match reg_3000mb.txt */ +-#define DIB3000MB_REG_DDS_FREQ_MSB ( 6) +-#define DIB3000MB_REG_DDS_FREQ_LSB ( 7) +-#define DIB3000MB_DDS_FREQ_MSB ( 178) +-#define DIB3000MB_DDS_FREQ_LSB ( 8990) +- +-/* timing frequency (carrier spacing) */ +-static u16 dib3000mb_reg_timing_freq[] = { 8,9 }; +-static u16 dib3000mb_timing_freq[][2] = { +- { 126 , 48873 }, /* 6 MHz */ +- { 147 , 57019 }, /* 7 MHz */ +- { 168 , 65164 }, /* 8 MHz */ +-}; +- +-/* impulse noise parameter */ +-/* 36 ??? */ +- +-static u16 dib3000mb_reg_impulse_noise[] = { 10,11,12,15,36 }; +- +-enum dib3000mb_impulse_noise_type { +- DIB3000MB_IMPNOISE_OFF, +- DIB3000MB_IMPNOISE_MOBILE, +- DIB3000MB_IMPNOISE_FIXED, +- DIB3000MB_IMPNOISE_DEFAULT +-}; +- +-static u16 dib3000mb_impulse_noise_values[][5] = { +- { 0x0000, 0x0004, 0x0014, 0x01ff, 0x0399 }, /* off */ +- { 0x0001, 0x0004, 0x0014, 0x01ff, 0x037b }, /* mobile */ +- { 0x0001, 0x0004, 0x0020, 0x01bd, 0x0399 }, /* fixed */ +- { 0x0000, 0x0002, 0x000a, 0x01ff, 0x0399 }, /* default */ +-}; +- +-/* +- * Dual Automatic-Gain-Control +- * - gains RF in tuner (AGC1) +- * - gains IF after filtering (AGC2) +- */ +- +-/* also from 16 to 18 */ +-static u16 dib3000mb_reg_agc_gain[] = { +- 19,20,21,22,23,24,25,26,27,28,29,30,31,32 +-}; +- +-static u16 dib3000mb_default_agc_gain[] = +- { 0x0001, 52429, 623, 128, 166, 195, 61, /* RF ??? */ +- 0x0001, 53766, 38011, 0, 90, 33, 23 }; /* IF ??? */ +- +-/* phase noise */ +-/* 36 is set when setting the impulse noise */ +-static u16 dib3000mb_reg_phase_noise[] = { 33,34,35,37,38 }; +- +-static u16 dib3000mb_default_noise_phase[] = { 2, 544, 0, 5, 4 }; +- +-/* lock duration */ +-static u16 dib3000mb_reg_lock_duration[] = { 39,40 }; +-static u16 dib3000mb_default_lock_duration[] = { 135, 135 }; +- +-/* AGC loop bandwidth */ +-static u16 dib3000mb_reg_agc_bandwidth[] = { 43,44,45,46,47,48,49,50 }; +- +-static u16 dib3000mb_agc_bandwidth_low[] = +- { 2088, 10, 2088, 10, 3448, 5, 3448, 5 }; +-static u16 dib3000mb_agc_bandwidth_high[] = +- { 2349, 5, 2349, 5, 2586, 2, 2586, 2 }; +- +-/* +- * lock0 definition (coff_lock) +- */ +-#define DIB3000MB_REG_LOCK0_MASK ( 51) +-#define DIB3000MB_LOCK0_DEFAULT ( 4) +- +-/* +- * lock1 definition (cpil_lock) +- * for auto search +- * which values hide behind the lock masks +- */ +-#define DIB3000MB_REG_LOCK1_MASK ( 52) +-#define DIB3000MB_LOCK1_SEARCH_4 (0x0004) +-#define DIB3000MB_LOCK1_SEARCH_2048 (0x0800) +-#define DIB3000MB_LOCK1_DEFAULT (0x0001) +- +-/* +- * lock2 definition (fec_lock) */ +-#define DIB3000MB_REG_LOCK2_MASK ( 53) +-#define DIB3000MB_LOCK2_DEFAULT (0x0080) +- +-/* +- * SEQ ? what was that again ... :) +- * changes when, inversion, guard time and fft is +- * either automatically detected or not +- */ +-#define DIB3000MB_REG_SEQ ( 54) +- +-/* bandwidth */ +-static u16 dib3000mb_reg_bandwidth[] = { 55,56,57,58,59,60,61,62,63,64,65,66,67 }; +-static u16 dib3000mb_bandwidth_6mhz[] = +- { 0, 33, 53312, 112, 46635, 563, 36565, 0, 1000, 0, 1010, 1, 45264 }; +- +-static u16 dib3000mb_bandwidth_7mhz[] = +- { 0, 28, 64421, 96, 39973, 483, 3255, 0, 1000, 0, 1010, 1, 45264 }; +- +-static u16 dib3000mb_bandwidth_8mhz[] = +- { 0, 25, 23600, 84, 34976, 422, 43808, 0, 1000, 0, 1010, 1, 45264 }; +- +-#define DIB3000MB_REG_UNK_68 ( 68) +-#define DIB3000MB_UNK_68 ( 0) +- +-#define DIB3000MB_REG_UNK_69 ( 69) +-#define DIB3000MB_UNK_69 ( 0) +- +-#define DIB3000MB_REG_UNK_71 ( 71) +-#define DIB3000MB_UNK_71 ( 0) +- +-#define DIB3000MB_REG_UNK_77 ( 77) +-#define DIB3000MB_UNK_77 ( 6) +- +-#define DIB3000MB_REG_UNK_78 ( 78) +-#define DIB3000MB_UNK_78 (0x0080) +- +-/* isi */ +-#define DIB3000MB_REG_ISI ( 79) +-#define DIB3000MB_ISI_ACTIVATE ( 0) +-#define DIB3000MB_ISI_INHIBIT ( 1) +- +-/* sync impovement */ +-#define DIB3000MB_REG_SYNC_IMPROVEMENT ( 84) +-#define DIB3000MB_SYNC_IMPROVE_2K_1_8 ( 3) +-#define DIB3000MB_SYNC_IMPROVE_DEFAULT ( 0) +- +-/* phase noise compensation inhibition */ +-#define DIB3000MB_REG_PHASE_NOISE ( 87) +-#define DIB3000MB_PHASE_NOISE_DEFAULT ( 0) +- +-#define DIB3000MB_REG_UNK_92 ( 92) +-#define DIB3000MB_UNK_92 (0x0080) +- +-#define DIB3000MB_REG_UNK_96 ( 96) +-#define DIB3000MB_UNK_96 (0x0010) +- +-#define DIB3000MB_REG_UNK_97 ( 97) +-#define DIB3000MB_UNK_97 (0x0009) +- +-/* mobile mode ??? */ +-#define DIB3000MB_REG_MOBILE_MODE ( 101) +-#define DIB3000MB_MOBILE_MODE_ON ( 1) +-#define DIB3000MB_MOBILE_MODE_OFF ( 0) +- +-#define DIB3000MB_REG_UNK_106 ( 106) +-#define DIB3000MB_UNK_106 (0x0080) +- +-#define DIB3000MB_REG_UNK_107 ( 107) +-#define DIB3000MB_UNK_107 (0x0080) +- +-#define DIB3000MB_REG_UNK_108 ( 108) +-#define DIB3000MB_UNK_108 (0x0080) +- +-/* fft */ +-#define DIB3000MB_REG_UNK_121 ( 121) +-#define DIB3000MB_UNK_121_2K ( 7) +-#define DIB3000MB_UNK_121_DEFAULT ( 5) +- +-#define DIB3000MB_REG_UNK_122 ( 122) +-#define DIB3000MB_UNK_122 ( 2867) +- +-/* QAM for mobile mode */ +-#define DIB3000MB_REG_MOBILE_MODE_QAM ( 126) +-#define DIB3000MB_MOBILE_MODE_QAM_64 ( 3) +-#define DIB3000MB_MOBILE_MODE_QAM_QPSK_16 ( 1) +-#define DIB3000MB_MOBILE_MODE_QAM_OFF ( 0) +- +-/* +- * data diversity when having more than one chip on-board +- * see also DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY +- */ +-#define DIB3000MB_REG_DATA_IN_DIVERSITY ( 127) +-#define DIB3000MB_DATA_DIVERSITY_IN_OFF ( 0) +-#define DIB3000MB_DATA_DIVERSITY_IN_ON ( 2) +- +-/* vit hrch */ +-#define DIB3000MB_REG_VIT_HRCH ( 128) +- +-/* vit code rate */ +-#define DIB3000MB_REG_VIT_CODE_RATE ( 129) +- +-/* vit select hp */ +-#define DIB3000MB_REG_VIT_HP ( 130) +- +-/* time frame for Bit-Error-Rate calculation */ +-#define DIB3000MB_REG_BERLEN ( 135) +-#define DIB3000MB_BERLEN_LONG ( 0) +-#define DIB3000MB_BERLEN_DEFAULT ( 1) +-#define DIB3000MB_BERLEN_MEDIUM ( 2) +-#define DIB3000MB_BERLEN_SHORT ( 3) +- +-/* 142 - 152 FIFO parameters +- * which is what ? +- */ +- +-#define DIB3000MB_REG_FIFO_142 ( 142) +-#define DIB3000MB_FIFO_142 ( 0) +- +-/* MPEG2 TS output mode */ +-#define DIB3000MB_REG_MPEG2_OUT_MODE ( 143) +-#define DIB3000MB_MPEG2_OUT_MODE_204 ( 0) +-#define DIB3000MB_MPEG2_OUT_MODE_188 ( 1) +- +-#define DIB3000MB_REG_PID_PARSE ( 144) +-#define DIB3000MB_PID_PARSE_INHIBIT ( 0) +-#define DIB3000MB_PID_PARSE_ACTIVATE ( 1) +- +-#define DIB3000MB_REG_FIFO ( 145) +-#define DIB3000MB_FIFO_INHIBIT ( 1) +-#define DIB3000MB_FIFO_ACTIVATE ( 0) +- +-#define DIB3000MB_REG_FIFO_146 ( 146) +-#define DIB3000MB_FIFO_146 ( 3) +- +-#define DIB3000MB_REG_FIFO_147 ( 147) +-#define DIB3000MB_FIFO_147 (0x0100) +- +-/* +- * pidfilter +- * it is not a hardware pidfilter but a filter which drops all pids +- * except the ones set. Necessary because of the limited USB1.1 bandwidth. +- * regs 153-168 +- */ +- +-#define DIB3000MB_REG_FIRST_PID ( 153) +-#define DIB3000MB_NUM_PIDS ( 16) +- +-/* +- * output mode +- * USB devices have to use 'slave'-mode +- * see also DIB3000MB_REG_ELECT_OUT_MODE +- */ +-#define DIB3000MB_REG_OUTPUT_MODE ( 169) +-#define DIB3000MB_OUTPUT_MODE_GATED_CLK ( 0) +-#define DIB3000MB_OUTPUT_MODE_CONT_CLK ( 1) +-#define DIB3000MB_OUTPUT_MODE_SERIAL ( 2) +-#define DIB3000MB_OUTPUT_MODE_DATA_DIVERSITY ( 5) +-#define DIB3000MB_OUTPUT_MODE_SLAVE ( 6) +- +-/* irq event mask */ +-#define DIB3000MB_REG_IRQ_EVENT_MASK ( 170) +-#define DIB3000MB_IRQ_EVENT_MASK ( 0) +- +-/* filter coefficients */ +-static u16 dib3000mb_reg_filter_coeffs[] = { +- 171, 172, 173, 174, 175, 176, 177, 178, +- 179, 180, 181, 182, 183, 184, 185, 186, +- 188, 189, 190, 191, 192, 194 +-}; +- +-static u16 dib3000mb_filter_coeffs[] = { +- 226, 160, 29, +- 979, 998, 19, +- 22, 1019, 1006, +- 1022, 12, 6, +- 1017, 1017, 3, +- 6, 1019, +- 1021, 2, 3, +- 1, 0, +-}; +- +-/* +- * mobile algorithm (when you are moving with your device) +- * but not faster than 90 km/h +- */ +-#define DIB3000MB_REG_MOBILE_ALGO ( 195) +-#define DIB3000MB_MOBILE_ALGO_ON ( 0) +-#define DIB3000MB_MOBILE_ALGO_OFF ( 1) +- +-/* multiple demodulators algorithm */ +-#define DIB3000MB_REG_MULTI_DEMOD_MSB ( 206) +-#define DIB3000MB_REG_MULTI_DEMOD_LSB ( 207) +- +-/* terminator, no more demods */ +-#define DIB3000MB_MULTI_DEMOD_MSB ( 32767) +-#define DIB3000MB_MULTI_DEMOD_LSB ( 4095) +- +-/* bring the device into a known */ +-#define DIB3000MB_REG_RESET_DEVICE ( 1024) +-#define DIB3000MB_RESET_DEVICE (0x812c) +-#define DIB3000MB_RESET_DEVICE_RST ( 0) +- +-/* hardware clock configuration */ +-#define DIB3000MB_REG_CLOCK ( 1027) +-#define DIB3000MB_CLOCK_DEFAULT (0x9000) +-#define DIB3000MB_CLOCK_DIVERSITY (0x92b0) +- +-/* power down config */ +-#define DIB3000MB_REG_POWER_CONTROL ( 1028) +-#define DIB3000MB_POWER_DOWN ( 1) +-#define DIB3000MB_POWER_UP ( 0) +- +-/* electrical output mode */ +-#define DIB3000MB_REG_ELECT_OUT_MODE ( 1029) +-#define DIB3000MB_ELECT_OUT_MODE_OFF ( 0) +-#define DIB3000MB_ELECT_OUT_MODE_ON ( 1) +- +-/* set the tuner i2c address */ +-#define DIB3000MB_REG_TUNER ( 1089) +- +-/* monitoring registers (read only) */ +- +-/* agc loop locked (size: 1) */ +-#define DIB3000MB_REG_AGC_LOCK ( 324) +- +-/* agc power (size: 16) */ +-#define DIB3000MB_REG_AGC_POWER ( 325) +- +-/* agc1 value (16) */ +-#define DIB3000MB_REG_AGC1_VALUE ( 326) +- +-/* agc2 value (16) */ +-#define DIB3000MB_REG_AGC2_VALUE ( 327) +- +-/* total RF power (16), can be used for signal strength */ +-#define DIB3000MB_REG_RF_POWER ( 328) +- +-/* dds_frequency with offset (24) */ +-#define DIB3000MB_REG_DDS_VALUE_MSB ( 339) +-#define DIB3000MB_REG_DDS_VALUE_LSB ( 340) +- +-/* timing offset signed (24) */ +-#define DIB3000MB_REG_TIMING_OFFSET_MSB ( 341) +-#define DIB3000MB_REG_TIMING_OFFSET_LSB ( 342) +- +-/* fft start position (13) */ +-#define DIB3000MB_REG_FFT_WINDOW_POS ( 353) +- +-/* carriers locked (1) */ +-#define DIB3000MB_REG_CARRIER_LOCK ( 355) +- +-/* noise power (24) */ +-#define DIB3000MB_REG_NOISE_POWER_MSB ( 372) +-#define DIB3000MB_REG_NOISE_POWER_LSB ( 373) +- +-#define DIB3000MB_REG_MOBILE_NOISE_MSB ( 374) +-#define DIB3000MB_REG_MOBILE_NOISE_LSB ( 375) +- +-/* +- * signal power (16), this and the above can be +- * used to calculate the signal/noise - ratio +- */ +-#define DIB3000MB_REG_SIGNAL_POWER ( 380) +- +-/* mer (24) */ +-#define DIB3000MB_REG_MER_MSB ( 381) +-#define DIB3000MB_REG_MER_LSB ( 382) +- +-/* +- * Transmission Parameter Signalling (TPS) +- * the following registers can be used to get TPS-information. +- * The values are according to the DVB-T standard. +- */ +- +-/* TPS locked (1) */ +-#define DIB3000MB_REG_TPS_LOCK ( 394) +- +-/* QAM from TPS (2) (values according to DIB3000MB_REG_QAM) */ +-#define DIB3000MB_REG_TPS_QAM ( 398) +- +-/* hierarchy from TPS (1) */ +-#define DIB3000MB_REG_TPS_HRCH ( 399) +- +-/* alpha from TPS (3) (values according to DIB3000MB_REG_VIT_ALPHA) */ +-#define DIB3000MB_REG_TPS_VIT_ALPHA ( 400) +- +-/* code rate high priority from TPS (3) (values according to DIB3000MB_FEC_*) */ +-#define DIB3000MB_REG_TPS_CODE_RATE_HP ( 401) +- +-/* code rate low priority from TPS (3) if DIB3000MB_REG_TPS_VIT_ALPHA */ +-#define DIB3000MB_REG_TPS_CODE_RATE_LP ( 402) +- +-/* guard time from TPS (2) (values according to DIB3000MB_REG_GUARD_TIME */ +-#define DIB3000MB_REG_TPS_GUARD_TIME ( 403) +- +-/* fft size from TPS (2) (values according to DIB3000MB_REG_FFT) */ +-#define DIB3000MB_REG_TPS_FFT ( 404) +- +-/* cell id from TPS (16) */ +-#define DIB3000MB_REG_TPS_CELL_ID ( 406) +- +-/* TPS (68) */ +-#define DIB3000MB_REG_TPS_1 ( 408) +-#define DIB3000MB_REG_TPS_2 ( 409) +-#define DIB3000MB_REG_TPS_3 ( 410) +-#define DIB3000MB_REG_TPS_4 ( 411) +-#define DIB3000MB_REG_TPS_5 ( 412) +- +-/* bit error rate (before RS correction) (21) */ +-#define DIB3000MB_REG_BER_MSB ( 414) +-#define DIB3000MB_REG_BER_LSB ( 415) +- +-/* packet error rate (uncorrected TS packets) (16) */ +-#define DIB3000MB_REG_PACKET_ERROR_RATE ( 417) +- +-/* uncorrected packet count (16) */ +-#define DIB3000MB_REG_UNC ( 420) +- +-/* viterbi locked (1) */ +-#define DIB3000MB_REG_VIT_LCK ( 421) +- +-/* viterbi inidcator (16) */ +-#define DIB3000MB_REG_VIT_INDICATOR ( 422) +- +-/* transport stream sync lock (1) */ +-#define DIB3000MB_REG_TS_SYNC_LOCK ( 423) +- +-/* transport stream RS lock (1) */ +-#define DIB3000MB_REG_TS_RS_LOCK ( 424) +- +-/* lock mask 0 value (1) */ +-#define DIB3000MB_REG_LOCK0_VALUE ( 425) +- +-/* lock mask 1 value (1) */ +-#define DIB3000MB_REG_LOCK1_VALUE ( 426) +- +-/* lock mask 2 value (1) */ +-#define DIB3000MB_REG_LOCK2_VALUE ( 427) +- +-/* interrupt pending for auto search */ +-#define DIB3000MB_REG_AS_IRQ_PENDING ( 434) +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c +deleted file mode 100644 +index ffad181..0000000 +--- a/drivers/media/dvb/frontends/dib3000mc.c ++++ /dev/null +@@ -1,940 +0,0 @@ +-/* +- * Driver for DiBcom DiB3000MC/P-demodulator. +- * +- * Copyright (C) 2004-7 DiBcom (http://www.dibcom.fr/) +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) +- * +- * This code is partially based on the previous dib3000mc.c . +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +- +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "dib3000mc.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-static int buggy_sfn_workaround; +-module_param(buggy_sfn_workaround, int, 0644); +-MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); printk("\n"); } } while (0) +- +-struct dib3000mc_state { +- struct dvb_frontend demod; +- struct dib3000mc_config *cfg; +- +- u8 i2c_addr; +- struct i2c_adapter *i2c_adap; +- +- struct dibx000_i2c_master i2c_master; +- +- u32 timf; +- +- u32 current_bandwidth; +- +- u16 dev_id; +- +- u8 sfn_workaround_active :1; +-}; +- +-static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg) +-{ +- u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; +- u8 rb[2]; +- struct i2c_msg msg[2] = { +- { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, +- { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, +- }; +- +- if (i2c_transfer(state->i2c_adap, msg, 2) != 2) +- dprintk("i2c read error on %d\n",reg); +- +- return (rb[0] << 8) | rb[1]; +-} +- +-static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val) +-{ +- u8 b[4] = { +- (reg >> 8) & 0xff, reg & 0xff, +- (val >> 8) & 0xff, val & 0xff, +- }; +- struct i2c_msg msg = { +- .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 +- }; +- return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +-} +- +-static int dib3000mc_identify(struct dib3000mc_state *state) +-{ +- u16 value; +- if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) { +- dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value); +- return -EREMOTEIO; +- } +- +- value = dib3000mc_read_word(state, 1026); +- if (value != 0x3001 && value != 0x3002) { +- dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value); +- return -EREMOTEIO; +- } +- state->dev_id = value; +- +- dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id); +- +- return 0; +-} +- +-static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u32 bw, u8 update_offset) +-{ +- u32 timf; +- +- if (state->timf == 0) { +- timf = 1384402; // default value for 8MHz +- if (update_offset) +- msleep(200); // first time we do an update +- } else +- timf = state->timf; +- +- timf *= (bw / 1000); +- +- if (update_offset) { +- s16 tim_offs = dib3000mc_read_word(state, 416); +- +- if (tim_offs & 0x2000) +- tim_offs -= 0x4000; +- +- if (nfft == TRANSMISSION_MODE_2K) +- tim_offs *= 4; +- +- timf += tim_offs; +- state->timf = timf / (bw / 1000); +- } +- +- dprintk("timf: %d\n", timf); +- +- dib3000mc_write_word(state, 23, (u16) (timf >> 16)); +- dib3000mc_write_word(state, 24, (u16) (timf ) & 0xffff); +- +- return 0; +-} +- +-static int dib3000mc_setup_pwm_state(struct dib3000mc_state *state) +-{ +- u16 reg_51, reg_52 = state->cfg->agc->setup & 0xfefb; +- if (state->cfg->pwm3_inversion) { +- reg_51 = (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0); +- reg_52 |= (1 << 2); +- } else { +- reg_51 = (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0); +- reg_52 |= (1 << 8); +- } +- dib3000mc_write_word(state, 51, reg_51); +- dib3000mc_write_word(state, 52, reg_52); +- +- if (state->cfg->use_pwm3) +- dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0)); +- else +- dib3000mc_write_word(state, 245, 0); +- +- dib3000mc_write_word(state, 1040, 0x3); +- return 0; +-} +- +-static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode) +-{ +- int ret = 0; +- u16 fifo_threshold = 1792; +- u16 outreg = 0; +- u16 outmode = 0; +- u16 elecout = 1; +- u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */ +- +- dprintk("-I- Setting output mode for demod %p to %d\n", +- &state->demod, mode); +- +- switch (mode) { +- case OUTMODE_HIGH_Z: // disable +- elecout = 0; +- break; +- case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock +- outmode = 0; +- break; +- case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock +- outmode = 1; +- break; +- case OUTMODE_MPEG2_SERIAL: // STBs with serial input +- outmode = 2; +- break; +- case OUTMODE_MPEG2_FIFO: // e.g. USB feeding +- elecout = 3; +- /*ADDR @ 206 : +- P_smo_error_discard [1;6:6] = 0 +- P_smo_rs_discard [1;5:5] = 0 +- P_smo_pid_parse [1;4:4] = 0 +- P_smo_fifo_flush [1;3:3] = 0 +- P_smo_mode [2;2:1] = 11 +- P_smo_ovf_prot [1;0:0] = 0 +- */ +- smo_reg |= 3 << 1; +- fifo_threshold = 512; +- outmode = 5; +- break; +- case OUTMODE_DIVERSITY: +- outmode = 4; +- elecout = 1; +- break; +- default: +- dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); +- outmode = 0; +- break; +- } +- +- if ((state->cfg->output_mpeg2_in_188_bytes)) +- smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1 +- +- outreg = dib3000mc_read_word(state, 244) & 0x07FF; +- outreg |= (outmode << 11); +- ret |= dib3000mc_write_word(state, 244, outreg); +- ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/ +- ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */ +- ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */ +- return ret; +-} +- +-static int dib3000mc_set_bandwidth(struct dib3000mc_state *state, u32 bw) +-{ +- u16 bw_cfg[6] = { 0 }; +- u16 imp_bw_cfg[3] = { 0 }; +- u16 reg; +- +-/* settings here are for 27.7MHz */ +- switch (bw) { +- case 8000: +- bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20; +- imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7; +- break; +- +- case 7000: +- bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7; +- imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0; +- break; +- +- case 6000: +- bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5; +- imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089; +- break; +- +- case 5000: +- bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500; +- imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072; +- break; +- +- default: return -EINVAL; +- } +- +- for (reg = 6; reg < 12; reg++) +- dib3000mc_write_word(state, reg, bw_cfg[reg - 6]); +- dib3000mc_write_word(state, 12, 0x0000); +- dib3000mc_write_word(state, 13, 0x03e8); +- dib3000mc_write_word(state, 14, 0x0000); +- dib3000mc_write_word(state, 15, 0x03f2); +- dib3000mc_write_word(state, 16, 0x0001); +- dib3000mc_write_word(state, 17, 0xb0d0); +- // P_sec_len +- dib3000mc_write_word(state, 18, 0x0393); +- dib3000mc_write_word(state, 19, 0x8700); +- +- for (reg = 55; reg < 58; reg++) +- dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]); +- +- // Timing configuration +- dib3000mc_set_timing(state, TRANSMISSION_MODE_2K, bw, 0); +- +- return 0; +-} +- +-static u16 impulse_noise_val[29] = +- +-{ +- 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3, +- 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2, +- 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd +-}; +- +-static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft) +-{ +- u16 i; +- for (i = 58; i < 87; i++) +- dib3000mc_write_word(state, i, impulse_noise_val[i-58]); +- +- if (nfft == TRANSMISSION_MODE_8K) { +- dib3000mc_write_word(state, 58, 0x3b); +- dib3000mc_write_word(state, 84, 0x00); +- dib3000mc_write_word(state, 85, 0x8200); +- } +- +- dib3000mc_write_word(state, 34, 0x1294); +- dib3000mc_write_word(state, 35, 0x1ff8); +- if (mode == 1) +- dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10)); +-} +- +-static int dib3000mc_init(struct dvb_frontend *demod) +-{ +- struct dib3000mc_state *state = demod->demodulator_priv; +- struct dibx000_agc_config *agc = state->cfg->agc; +- +- // Restart Configuration +- dib3000mc_write_word(state, 1027, 0x8000); +- dib3000mc_write_word(state, 1027, 0x0000); +- +- // power up the demod + mobility configuration +- dib3000mc_write_word(state, 140, 0x0000); +- dib3000mc_write_word(state, 1031, 0); +- +- if (state->cfg->mobile_mode) { +- dib3000mc_write_word(state, 139, 0x0000); +- dib3000mc_write_word(state, 141, 0x0000); +- dib3000mc_write_word(state, 175, 0x0002); +- dib3000mc_write_word(state, 1032, 0x0000); +- } else { +- dib3000mc_write_word(state, 139, 0x0001); +- dib3000mc_write_word(state, 141, 0x0000); +- dib3000mc_write_word(state, 175, 0x0000); +- dib3000mc_write_word(state, 1032, 0x012C); +- } +- dib3000mc_write_word(state, 1033, 0x0000); +- +- // P_clk_cfg +- dib3000mc_write_word(state, 1037, 0x3130); +- +- // other configurations +- +- // P_ctrl_sfreq +- dib3000mc_write_word(state, 33, (5 << 0)); +- dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0)); +- +- // Phase noise control +- // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange +- dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0)); +- +- if (state->cfg->phase_noise_mode == 0) +- dib3000mc_write_word(state, 111, 0x00); +- else +- dib3000mc_write_word(state, 111, 0x02); +- +- // P_agc_global +- dib3000mc_write_word(state, 50, 0x8000); +- +- // agc setup misc +- dib3000mc_setup_pwm_state(state); +- +- // P_agc_counter_lock +- dib3000mc_write_word(state, 53, 0x87); +- // P_agc_counter_unlock +- dib3000mc_write_word(state, 54, 0x87); +- +- /* agc */ +- dib3000mc_write_word(state, 36, state->cfg->max_time); +- dib3000mc_write_word(state, 37, (state->cfg->agc_command1 << 13) | (state->cfg->agc_command2 << 12) | (0x1d << 0)); +- dib3000mc_write_word(state, 38, state->cfg->pwm3_value); +- dib3000mc_write_word(state, 39, state->cfg->ln_adc_level); +- +- // set_agc_loop_Bw +- dib3000mc_write_word(state, 40, 0x0179); +- dib3000mc_write_word(state, 41, 0x03f0); +- +- dib3000mc_write_word(state, 42, agc->agc1_max); +- dib3000mc_write_word(state, 43, agc->agc1_min); +- dib3000mc_write_word(state, 44, agc->agc2_max); +- dib3000mc_write_word(state, 45, agc->agc2_min); +- dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2); +- dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2); +- dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2); +- dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2); +- +-// Begin: TimeOut registers +- // P_pha3_thres +- dib3000mc_write_word(state, 110, 3277); +- // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80 +- dib3000mc_write_word(state, 26, 0x6680); +- // lock_mask0 +- dib3000mc_write_word(state, 1, 4); +- // lock_mask1 +- dib3000mc_write_word(state, 2, 4); +- // lock_mask2 +- dib3000mc_write_word(state, 3, 0x1000); +- // P_search_maxtrial=1 +- dib3000mc_write_word(state, 5, 1); +- +- dib3000mc_set_bandwidth(state, 8000); +- +- // div_lock_mask +- dib3000mc_write_word(state, 4, 0x814); +- +- dib3000mc_write_word(state, 21, (1 << 9) | 0x164); +- dib3000mc_write_word(state, 22, 0x463d); +- +- // Spurious rm cfg +- // P_cspu_regul, P_cspu_win_cut +- dib3000mc_write_word(state, 120, 0x200f); +- // P_adp_selec_monit +- dib3000mc_write_word(state, 134, 0); +- +- // Fec cfg +- dib3000mc_write_word(state, 195, 0x10); +- +- // diversity register: P_dvsy_sync_wait.. +- dib3000mc_write_word(state, 180, 0x2FF0); +- +- // Impulse noise configuration +- dib3000mc_set_impulse_noise(state, 0, TRANSMISSION_MODE_8K); +- +- // output mode set-up +- dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); +- +- /* close the i2c-gate */ +- dib3000mc_write_word(state, 769, (1 << 7) ); +- +- return 0; +-} +- +-static int dib3000mc_sleep(struct dvb_frontend *demod) +-{ +- struct dib3000mc_state *state = demod->demodulator_priv; +- +- dib3000mc_write_word(state, 1031, 0xFFFF); +- dib3000mc_write_word(state, 1032, 0xFFFF); +- dib3000mc_write_word(state, 1033, 0xFFF0); +- +- return 0; +-} +- +-static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam) +-{ +- u16 cfg[4] = { 0 },reg; +- switch (qam) { +- case QPSK: +- cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0; +- break; +- case QAM_16: +- cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0; +- break; +- case QAM_64: +- cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8; +- break; +- } +- for (reg = 129; reg < 133; reg++) +- dib3000mc_write_word(state, reg, cfg[reg - 129]); +-} +- +-static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, +- struct dtv_frontend_properties *ch, u16 seq) +-{ +- u16 value; +- u32 bw = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); +- +- dib3000mc_set_bandwidth(state, bw); +- dib3000mc_set_timing(state, ch->transmission_mode, bw, 0); +- +-// if (boost) +-// dib3000mc_write_word(state, 100, (11 << 6) + 6); +-// else +- dib3000mc_write_word(state, 100, (16 << 6) + 9); +- +- dib3000mc_write_word(state, 1027, 0x0800); +- dib3000mc_write_word(state, 1027, 0x0000); +- +- //Default cfg isi offset adp +- dib3000mc_write_word(state, 26, 0x6680); +- dib3000mc_write_word(state, 29, 0x1273); +- dib3000mc_write_word(state, 33, 5); +- dib3000mc_set_adp_cfg(state, QAM_16); +- dib3000mc_write_word(state, 133, 15564); +- +- dib3000mc_write_word(state, 12 , 0x0); +- dib3000mc_write_word(state, 13 , 0x3e8); +- dib3000mc_write_word(state, 14 , 0x0); +- dib3000mc_write_word(state, 15 , 0x3f2); +- +- dib3000mc_write_word(state, 93,0); +- dib3000mc_write_word(state, 94,0); +- dib3000mc_write_word(state, 95,0); +- dib3000mc_write_word(state, 96,0); +- dib3000mc_write_word(state, 97,0); +- dib3000mc_write_word(state, 98,0); +- +- dib3000mc_set_impulse_noise(state, 0, ch->transmission_mode); +- +- value = 0; +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: value |= (0 << 7); break; +- default: +- case TRANSMISSION_MODE_8K: value |= (1 << 7); break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_32: value |= (0 << 5); break; +- case GUARD_INTERVAL_1_16: value |= (1 << 5); break; +- case GUARD_INTERVAL_1_4: value |= (3 << 5); break; +- default: +- case GUARD_INTERVAL_1_8: value |= (2 << 5); break; +- } +- switch (ch->modulation) { +- case QPSK: value |= (0 << 3); break; +- case QAM_16: value |= (1 << 3); break; +- default: +- case QAM_64: value |= (2 << 3); break; +- } +- switch (HIERARCHY_1) { +- case HIERARCHY_2: value |= 2; break; +- case HIERARCHY_4: value |= 4; break; +- default: +- case HIERARCHY_1: value |= 1; break; +- } +- dib3000mc_write_word(state, 0, value); +- dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4)); +- +- value = 0; +- if (ch->hierarchy == 1) +- value |= (1 << 4); +- if (1 == 1) +- value |= 1; +- switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { +- case FEC_2_3: value |= (2 << 1); break; +- case FEC_3_4: value |= (3 << 1); break; +- case FEC_5_6: value |= (5 << 1); break; +- case FEC_7_8: value |= (7 << 1); break; +- default: +- case FEC_1_2: value |= (1 << 1); break; +- } +- dib3000mc_write_word(state, 181, value); +- +- // diversity synchro delay add 50% SFN margin +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_8K: value = 256; break; +- case TRANSMISSION_MODE_2K: +- default: value = 64; break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_16: value *= 2; break; +- case GUARD_INTERVAL_1_8: value *= 4; break; +- case GUARD_INTERVAL_1_4: value *= 8; break; +- default: +- case GUARD_INTERVAL_1_32: value *= 1; break; +- } +- value <<= 4; +- value |= dib3000mc_read_word(state, 180) & 0x000f; +- dib3000mc_write_word(state, 180, value); +- +- // restart demod +- value = dib3000mc_read_word(state, 0); +- dib3000mc_write_word(state, 0, value | (1 << 9)); +- dib3000mc_write_word(state, 0, value); +- +- msleep(30); +- +- dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, ch->transmission_mode); +-} +- +-static int dib3000mc_autosearch_start(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *chan = &demod->dtv_property_cache; +- struct dib3000mc_state *state = demod->demodulator_priv; +- u16 reg; +-// u32 val; +- struct dtv_frontend_properties schan; +- +- schan = *chan; +- +- /* TODO what is that ? */ +- +- /* a channel for autosearch */ +- schan.transmission_mode = TRANSMISSION_MODE_8K; +- schan.guard_interval = GUARD_INTERVAL_1_32; +- schan.modulation = QAM_64; +- schan.code_rate_HP = FEC_2_3; +- schan.code_rate_LP = FEC_2_3; +- schan.hierarchy = 0; +- +- dib3000mc_set_channel_cfg(state, &schan, 11); +- +- reg = dib3000mc_read_word(state, 0); +- dib3000mc_write_word(state, 0, reg | (1 << 8)); +- dib3000mc_read_word(state, 511); +- dib3000mc_write_word(state, 0, reg); +- +- return 0; +-} +- +-static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod) +-{ +- struct dib3000mc_state *state = demod->demodulator_priv; +- u16 irq_pending = dib3000mc_read_word(state, 511); +- +- if (irq_pending & 0x1) // failed +- return 1; +- +- if (irq_pending & 0x2) // succeeded +- return 2; +- +- return 0; // still pending +-} +- +-static int dib3000mc_tune(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib3000mc_state *state = demod->demodulator_priv; +- +- // ** configure demod ** +- dib3000mc_set_channel_cfg(state, ch, 0); +- +- // activates isi +- if (state->sfn_workaround_active) { +- dprintk("SFN workaround is active\n"); +- dib3000mc_write_word(state, 29, 0x1273); +- dib3000mc_write_word(state, 108, 0x4000); // P_pha3_force_pha_shift +- } else { +- dib3000mc_write_word(state, 29, 0x1073); +- dib3000mc_write_word(state, 108, 0x0000); // P_pha3_force_pha_shift +- } +- +- dib3000mc_set_adp_cfg(state, (u8)ch->modulation); +- if (ch->transmission_mode == TRANSMISSION_MODE_8K) { +- dib3000mc_write_word(state, 26, 38528); +- dib3000mc_write_word(state, 33, 8); +- } else { +- dib3000mc_write_word(state, 26, 30336); +- dib3000mc_write_word(state, 33, 6); +- } +- +- if (dib3000mc_read_word(state, 509) & 0x80) +- dib3000mc_set_timing(state, ch->transmission_mode, +- BANDWIDTH_TO_KHZ(ch->bandwidth_hz), 1); +- +- return 0; +-} +- +-struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating) +-{ +- struct dib3000mc_state *st = demod->demodulator_priv; +- return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating); +-} +- +-EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master); +- +-static int dib3000mc_get_frontend(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib3000mc_state *state = fe->demodulator_priv; +- u16 tps = dib3000mc_read_word(state,458); +- +- fep->inversion = INVERSION_AUTO; +- +- fep->bandwidth_hz = state->current_bandwidth; +- +- switch ((tps >> 8) & 0x1) { +- case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; +- case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; +- } +- +- switch (tps & 0x3) { +- case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; +- case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; +- case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; +- case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; +- } +- +- switch ((tps >> 13) & 0x3) { +- case 0: fep->modulation = QPSK; break; +- case 1: fep->modulation = QAM_16; break; +- case 2: +- default: fep->modulation = QAM_64; break; +- } +- +- /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ +- /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */ +- +- fep->hierarchy = HIERARCHY_NONE; +- switch ((tps >> 5) & 0x7) { +- case 1: fep->code_rate_HP = FEC_1_2; break; +- case 2: fep->code_rate_HP = FEC_2_3; break; +- case 3: fep->code_rate_HP = FEC_3_4; break; +- case 5: fep->code_rate_HP = FEC_5_6; break; +- case 7: +- default: fep->code_rate_HP = FEC_7_8; break; +- +- } +- +- switch ((tps >> 2) & 0x7) { +- case 1: fep->code_rate_LP = FEC_1_2; break; +- case 2: fep->code_rate_LP = FEC_2_3; break; +- case 3: fep->code_rate_LP = FEC_3_4; break; +- case 5: fep->code_rate_LP = FEC_5_6; break; +- case 7: +- default: fep->code_rate_LP = FEC_7_8; break; +- } +- +- return 0; +-} +- +-static int dib3000mc_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib3000mc_state *state = fe->demodulator_priv; +- int ret; +- +- dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z); +- +- state->current_bandwidth = fep->bandwidth_hz; +- dib3000mc_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); +- +- /* maybe the parameter has been changed */ +- state->sfn_workaround_active = buggy_sfn_workaround; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- msleep(100); +- } +- +- if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || +- fep->guard_interval == GUARD_INTERVAL_AUTO || +- fep->modulation == QAM_AUTO || +- fep->code_rate_HP == FEC_AUTO) { +- int i = 1000, found; +- +- dib3000mc_autosearch_start(fe); +- do { +- msleep(1); +- found = dib3000mc_autosearch_is_irq(fe); +- } while (found == 0 && i--); +- +- dprintk("autosearch returns: %d\n",found); +- if (found == 0 || found == 1) +- return 0; // no channel found +- +- dib3000mc_get_frontend(fe); +- } +- +- ret = dib3000mc_tune(fe); +- +- /* make this a config parameter */ +- dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO); +- return ret; +-} +- +-static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- u16 lock = dib3000mc_read_word(state, 509); +- +- *stat = 0; +- +- if (lock & 0x8000) +- *stat |= FE_HAS_SIGNAL; +- if (lock & 0x3000) +- *stat |= FE_HAS_CARRIER; +- if (lock & 0x0100) +- *stat |= FE_HAS_VITERBI; +- if (lock & 0x0010) +- *stat |= FE_HAS_SYNC; +- if (lock & 0x0008) +- *stat |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501); +- return 0; +-} +- +-static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- *unc = dib3000mc_read_word(state, 508); +- return 0; +-} +- +-static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- u16 val = dib3000mc_read_word(state, 392); +- *strength = 65535 - val; +- return 0; +-} +- +-static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- *snr = 0x0000; +- return 0; +-} +- +-static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void dib3000mc_release(struct dvb_frontend *fe) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- dibx000_exit_i2c_master(&state->i2c_master); +- kfree(state); +-} +- +-int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0); +- return 0; +-} +-EXPORT_SYMBOL(dib3000mc_pid_control); +- +-int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4); +- tmp |= (onoff << 4); +- return dib3000mc_write_word(state, 206, tmp); +-} +-EXPORT_SYMBOL(dib3000mc_pid_parse); +- +-void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg) +-{ +- struct dib3000mc_state *state = fe->demodulator_priv; +- state->cfg = cfg; +-} +-EXPORT_SYMBOL(dib3000mc_set_config); +- +-int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]) +-{ +- struct dib3000mc_state *dmcst; +- int k; +- u8 new_addr; +- +- static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26}; +- +- dmcst = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); +- if (dmcst == NULL) +- return -ENOMEM; +- +- dmcst->i2c_adap = i2c; +- +- for (k = no_of_demods-1; k >= 0; k--) { +- dmcst->cfg = &cfg[k]; +- +- /* designated i2c address */ +- new_addr = DIB3000MC_I2C_ADDRESS[k]; +- dmcst->i2c_addr = new_addr; +- if (dib3000mc_identify(dmcst) != 0) { +- dmcst->i2c_addr = default_addr; +- if (dib3000mc_identify(dmcst) != 0) { +- dprintk("-E- DiB3000P/MC #%d: not identified\n", k); +- kfree(dmcst); +- return -ENODEV; +- } +- } +- +- dib3000mc_set_output_mode(dmcst, OUTMODE_MPEG2_PAR_CONT_CLK); +- +- // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0) +- dib3000mc_write_word(dmcst, 1024, (new_addr << 3) | 0x1); +- dmcst->i2c_addr = new_addr; +- } +- +- for (k = 0; k < no_of_demods; k++) { +- dmcst->cfg = &cfg[k]; +- dmcst->i2c_addr = DIB3000MC_I2C_ADDRESS[k]; +- +- dib3000mc_write_word(dmcst, 1024, dmcst->i2c_addr << 3); +- +- /* turn off data output */ +- dib3000mc_set_output_mode(dmcst, OUTMODE_HIGH_Z); +- } +- +- kfree(dmcst); +- return 0; +-} +-EXPORT_SYMBOL(dib3000mc_i2c_enumeration); +- +-static struct dvb_frontend_ops dib3000mc_ops; +- +-struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg) +-{ +- struct dvb_frontend *demod; +- struct dib3000mc_state *st; +- st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- +- st->cfg = cfg; +- st->i2c_adap = i2c_adap; +- st->i2c_addr = i2c_addr; +- +- demod = &st->demod; +- demod->demodulator_priv = st; +- memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops)); +- +- if (dib3000mc_identify(st) != 0) +- goto error; +- +- dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr); +- +- dib3000mc_write_word(st, 1037, 0x3130); +- +- return demod; +- +-error: +- kfree(st); +- return NULL; +-} +-EXPORT_SYMBOL(dib3000mc_attach); +- +-static struct dvb_frontend_ops dib3000mc_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DiBcom 3000MC/P", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_RECOVER | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib3000mc_release, +- +- .init = dib3000mc_init, +- .sleep = dib3000mc_sleep, +- +- .set_frontend = dib3000mc_set_frontend, +- .get_tune_settings = dib3000mc_fe_get_tune_settings, +- .get_frontend = dib3000mc_get_frontend, +- +- .read_status = dib3000mc_read_status, +- .read_ber = dib3000mc_read_ber, +- .read_signal_strength = dib3000mc_read_signal_strength, +- .read_snr = dib3000mc_read_snr, +- .read_ucblocks = dib3000mc_read_unc_blocks, +-}; +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb/frontends/dib3000mc.h +deleted file mode 100644 +index d75ffad..0000000 +--- a/drivers/media/dvb/frontends/dib3000mc.h ++++ /dev/null +@@ -1,85 +0,0 @@ +-/* +- * Driver for DiBcom DiB3000MC/P-demodulator. +- * +- * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/) +- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de) +- * +- * This code is partially based on the previous dib3000mc.c . +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#ifndef DIB3000MC_H +-#define DIB3000MC_H +- +-#include "dibx000_common.h" +- +-struct dib3000mc_config { +- struct dibx000_agc_config *agc; +- +- u8 phase_noise_mode; +- u8 impulse_noise_mode; +- +- u8 pwm3_inversion; +- u8 use_pwm3; +- u16 pwm3_value; +- +- u16 max_time; +- u16 ln_adc_level; +- +- u8 agc_command1 :1; +- u8 agc_command2 :1; +- +- u8 mobile_mode; +- +- u8 output_mpeg2_in_188_bytes; +-}; +- +-#define DEFAULT_DIB3000MC_I2C_ADDRESS 16 +-#define DEFAULT_DIB3000P_I2C_ADDRESS 24 +- +-#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && \ +- defined(MODULE)) +-extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- struct dib3000mc_config *cfg); +-extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, +- int no_of_demods, u8 default_addr, +- struct dib3000mc_config cfg[]); +-extern +-struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, +- int gating); +-#else +-static inline +-struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, +- struct dib3000mc_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline +-int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, +- int no_of_demods, u8 default_addr, +- struct dib3000mc_config cfg[]) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline +-struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, +- int gating) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_DIB3000MC +- +-extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff); +-extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff); +- +-extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *); +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c +deleted file mode 100644 +index 148bf79..0000000 +--- a/drivers/media/dvb/frontends/dib7000m.c ++++ /dev/null +@@ -1,1473 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB7000M and +- * first generation DiB7000P-demodulator-family. +- * +- * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "dib7000m.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M: "); printk(args); printk("\n"); } } while (0) +- +-struct dib7000m_state { +- struct dvb_frontend demod; +- struct dib7000m_config cfg; +- +- u8 i2c_addr; +- struct i2c_adapter *i2c_adap; +- +- struct dibx000_i2c_master i2c_master; +- +-/* offset is 1 in case of the 7000MC */ +- u8 reg_offs; +- +- u16 wbd_ref; +- +- u8 current_band; +- u32 current_bandwidth; +- struct dibx000_agc_config *current_agc; +- u32 timf; +- u32 timf_default; +- u32 internal_clk; +- +- u8 div_force_off : 1; +- u8 div_state : 1; +- u16 div_sync_wait; +- +- u16 revision; +- +- u8 agc_state; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[4]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +-}; +- +-enum dib7000m_power_mode { +- DIB7000M_POWER_ALL = 0, +- +- DIB7000M_POWER_NO, +- DIB7000M_POWER_INTERF_ANALOG_AGC, +- DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, +- DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD, +- DIB7000M_POWER_INTERFACE_ONLY, +-}; +- +-static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = (reg >> 8) | 0x80; +- state->i2c_write_buffer[1] = reg & 0xff; +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 2; +- state->msg[1].addr = state->i2c_addr >> 1; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = state->i2c_read_buffer; +- state->msg[1].len = 2; +- +- if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) +- dprintk("i2c read error on %d",reg); +- +- ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +- mutex_unlock(&state->i2c_buffer_lock); +- +- return ret; +-} +- +-static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- state->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- state->i2c_write_buffer[1] = reg & 0xff; +- state->i2c_write_buffer[2] = (val >> 8) & 0xff; +- state->i2c_write_buffer[3] = val & 0xff; +- +- memset(&state->msg[0], 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 4; +- +- ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? +- -EREMOTEIO : 0); +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +-static void dib7000m_write_tab(struct dib7000m_state *state, u16 *buf) +-{ +- u16 l = 0, r, *n; +- n = buf; +- l = *n++; +- while (l) { +- r = *n++; +- +- if (state->reg_offs && (r >= 112 && r <= 331)) // compensate for 7000MC +- r++; +- +- do { +- dib7000m_write_word(state, r, *n++); +- r++; +- } while (--l); +- l = *n++; +- } +-} +- +-static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode) +-{ +- int ret = 0; +- u16 outreg, fifo_threshold, smo_mode, +- sram = 0x0005; /* by default SRAM output is disabled */ +- +- outreg = 0; +- fifo_threshold = 1792; +- smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1); +- +- dprintk( "setting output mode for demod %p to %d", &state->demod, mode); +- +- switch (mode) { +- case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock +- outreg = (1 << 10); /* 0x0400 */ +- break; +- case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock +- outreg = (1 << 10) | (1 << 6); /* 0x0440 */ +- break; +- case OUTMODE_MPEG2_SERIAL: // STBs with serial input +- outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ +- break; +- case OUTMODE_DIVERSITY: +- if (state->cfg.hostbus_diversity) +- outreg = (1 << 10) | (4 << 6); /* 0x0500 */ +- else +- sram |= 0x0c00; +- break; +- case OUTMODE_MPEG2_FIFO: // e.g. USB feeding +- smo_mode |= (3 << 1); +- fifo_threshold = 512; +- outreg = (1 << 10) | (5 << 6); +- break; +- case OUTMODE_HIGH_Z: // disable +- outreg = 0; +- break; +- default: +- dprintk( "Unhandled output_mode passed to be set for demod %p",&state->demod); +- break; +- } +- +- if (state->cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5) ; +- +- ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode); +- ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */ +- ret |= dib7000m_write_word(state, 1795, outreg); +- ret |= dib7000m_write_word(state, 1805, sram); +- +- if (state->revision == 0x4003) { +- u16 clk_cfg1 = dib7000m_read_word(state, 909) & 0xfffd; +- if (mode == OUTMODE_DIVERSITY) +- clk_cfg1 |= (1 << 1); // P_O_CLK_en +- dib7000m_write_word(state, 909, clk_cfg1); +- } +- return ret; +-} +- +-static void dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode) +-{ +- /* by default everything is going to be powered off */ +- u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff; +- u8 offset = 0; +- +- /* now, depending on the requested mode, we power on */ +- switch (mode) { +- /* power up everything in the demod */ +- case DIB7000M_POWER_ALL: +- reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000; +- break; +- +- /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ +- case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ +- reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); +- break; +- +- case DIB7000M_POWER_INTERF_ANALOG_AGC: +- reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); +- reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); +- reg_906 &= ~((1 << 0)); +- break; +- +- case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: +- reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000; +- break; +- +- case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD: +- reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000; +- break; +- case DIB7000M_POWER_NO: +- break; +- } +- +- /* always power down unused parts */ +- if (!state->cfg.mobile_mode) +- reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); +- +- /* P_sdio_select_clk = 0 on MC and after*/ +- if (state->revision != 0x4000) +- reg_906 <<= 1; +- +- if (state->revision == 0x4003) +- offset = 1; +- +- dib7000m_write_word(state, 903 + offset, reg_903); +- dib7000m_write_word(state, 904 + offset, reg_904); +- dib7000m_write_word(state, 905 + offset, reg_905); +- dib7000m_write_word(state, 906 + offset, reg_906); +-} +- +-static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no) +-{ +- int ret = 0; +- u16 reg_913 = dib7000m_read_word(state, 913), +- reg_914 = dib7000m_read_word(state, 914); +- +- switch (no) { +- case DIBX000_SLOW_ADC_ON: +- reg_914 |= (1 << 1) | (1 << 0); +- ret |= dib7000m_write_word(state, 914, reg_914); +- reg_914 &= ~(1 << 1); +- break; +- +- case DIBX000_SLOW_ADC_OFF: +- reg_914 |= (1 << 1) | (1 << 0); +- break; +- +- case DIBX000_ADC_ON: +- if (state->revision == 0x4000) { // workaround for PA/MA +- // power-up ADC +- dib7000m_write_word(state, 913, 0); +- dib7000m_write_word(state, 914, reg_914 & 0x3); +- // power-down bandgag +- dib7000m_write_word(state, 913, (1 << 15)); +- dib7000m_write_word(state, 914, reg_914 & 0x3); +- } +- +- reg_913 &= 0x0fff; +- reg_914 &= 0x0003; +- break; +- +- case DIBX000_ADC_OFF: // leave the VBG voltage on +- reg_913 |= (1 << 14) | (1 << 13) | (1 << 12); +- reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); +- break; +- +- case DIBX000_VBG_ENABLE: +- reg_913 &= ~(1 << 15); +- break; +- +- case DIBX000_VBG_DISABLE: +- reg_913 |= (1 << 15); +- break; +- +- default: +- break; +- } +- +-// dprintk( "913: %x, 914: %x", reg_913, reg_914); +- ret |= dib7000m_write_word(state, 913, reg_913); +- ret |= dib7000m_write_word(state, 914, reg_914); +- +- return ret; +-} +- +-static int dib7000m_set_bandwidth(struct dib7000m_state *state, u32 bw) +-{ +- u32 timf; +- +- if (!bw) +- bw = 8000; +- +- // store the current bandwidth for later use +- state->current_bandwidth = bw; +- +- if (state->timf == 0) { +- dprintk( "using default timf"); +- timf = state->timf_default; +- } else { +- dprintk( "using updated timf"); +- timf = state->timf; +- } +- +- timf = timf * (bw / 50) / 160; +- +- dib7000m_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); +- dib7000m_write_word(state, 24, (u16) ((timf ) & 0xffff)); +- +- return 0; +-} +- +-static int dib7000m_set_diversity_in(struct dvb_frontend *demod, int onoff) +-{ +- struct dib7000m_state *state = demod->demodulator_priv; +- +- if (state->div_force_off) { +- dprintk( "diversity combination deactivated - forced by COFDM parameters"); +- onoff = 0; +- } +- state->div_state = (u8)onoff; +- +- if (onoff) { +- dib7000m_write_word(state, 263 + state->reg_offs, 6); +- dib7000m_write_word(state, 264 + state->reg_offs, 6); +- dib7000m_write_word(state, 266 + state->reg_offs, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); +- } else { +- dib7000m_write_word(state, 263 + state->reg_offs, 1); +- dib7000m_write_word(state, 264 + state->reg_offs, 0); +- dib7000m_write_word(state, 266 + state->reg_offs, 0); +- } +- +- return 0; +-} +- +-static int dib7000m_sad_calib(struct dib7000m_state *state) +-{ +- +-/* internal */ +-// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth +- dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); +- dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 +- +- /* do the calibration */ +- dib7000m_write_word(state, 929, (1 << 0)); +- dib7000m_write_word(state, 929, (0 << 0)); +- +- msleep(1); +- +- return 0; +-} +- +-static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw) +-{ +- dib7000m_write_word(state, 18, (u16) (((bw->internal*1000) >> 16) & 0xffff)); +- dib7000m_write_word(state, 19, (u16) ( (bw->internal*1000) & 0xffff)); +- dib7000m_write_word(state, 21, (u16) ( (bw->ifreq >> 16) & 0xffff)); +- dib7000m_write_word(state, 22, (u16) ( bw->ifreq & 0xffff)); +- +- dib7000m_write_word(state, 928, bw->sad_cfg); +-} +- +-static void dib7000m_reset_pll(struct dib7000m_state *state) +-{ +- const struct dibx000_bandwidth_config *bw = state->cfg.bw; +- u16 reg_907,reg_910; +- +- /* default */ +- reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) | +- (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | +- (bw->enable_refdiv << 1) | (0 << 0); +- reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset; +- +- // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value) +- // this is only working only for 30 MHz crystals +- if (!state->cfg.quartz_direct) { +- reg_910 |= (1 << 5); // forcing the predivider to 1 +- +- // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2) +- if(state->cfg.input_clk_is_div_2) +- reg_907 |= (16 << 9); +- else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary +- reg_907 |= (8 << 9); +- } else { +- reg_907 |= (bw->pll_ratio & 0x3f) << 9; +- reg_910 |= (bw->pll_prediv << 5); +- } +- +- dib7000m_write_word(state, 910, reg_910); // pll cfg +- dib7000m_write_word(state, 907, reg_907); // clk cfg0 +- dib7000m_write_word(state, 908, 0x0006); // clk_cfg1 +- +- dib7000m_reset_pll_common(state, bw); +-} +- +-static void dib7000mc_reset_pll(struct dib7000m_state *state) +-{ +- const struct dibx000_bandwidth_config *bw = state->cfg.bw; +- u16 clk_cfg1; +- +- // clk_cfg0 +- dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0)); +- +- // clk_cfg1 +- //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) | +- clk_cfg1 = (0 << 14) | (3 << 12) |(0 << 11) | +- (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) | +- (1 << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0); +- dib7000m_write_word(state, 908, clk_cfg1); +- clk_cfg1 = (clk_cfg1 & 0xfff7) | (bw->pll_bypass << 3); +- dib7000m_write_word(state, 908, clk_cfg1); +- +- // smpl_cfg +- dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7)); +- +- dib7000m_reset_pll_common(state, bw); +-} +- +-static int dib7000m_reset_gpio(struct dib7000m_state *st) +-{ +- /* reset the GPIOs */ +- dib7000m_write_word(st, 773, st->cfg.gpio_dir); +- dib7000m_write_word(st, 774, st->cfg.gpio_val); +- +- /* TODO 782 is P_gpio_od */ +- +- dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos); +- +- dib7000m_write_word(st, 780, st->cfg.pwm_freq_div); +- return 0; +-} +- +-static u16 dib7000m_defaults_common[] = +- +-{ +- // auto search configuration +- 3, 2, +- 0x0004, +- 0x1000, +- 0x0814, +- +- 12, 6, +- 0x001b, +- 0x7740, +- 0x005b, +- 0x8d80, +- 0x01c9, +- 0xc380, +- 0x0000, +- 0x0080, +- 0x0000, +- 0x0090, +- 0x0001, +- 0xd4c0, +- +- 1, 26, +- 0x6680, // P_corm_thres Lock algorithms configuration +- +- 1, 170, +- 0x0410, // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on +- +- 8, 173, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- +- 1, 182, +- 8192, // P_fft_nb_to_cut +- +- 2, 195, +- 0x0ccd, // P_pha3_thres +- 0, // P_cti_use_cpe, P_cti_use_prog +- +- 1, 205, +- 0x200f, // P_cspu_regul, P_cspu_win_cut +- +- 5, 214, +- 0x023d, // P_adp_regul_cnt +- 0x00a4, // P_adp_noise_cnt +- 0x00a4, // P_adp_regul_ext +- 0x7ff0, // P_adp_noise_ext +- 0x3ccc, // P_adp_fil +- +- 1, 226, +- 0, // P_2d_byp_ti_num +- +- 1, 255, +- 0x800, // P_equal_thres_wgn +- +- 1, 263, +- 0x0001, +- +- 1, 281, +- 0x0010, // P_fec_* +- +- 1, 294, +- 0x0062, // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard +- +- 0 +-}; +- +-static u16 dib7000m_defaults[] = +- +-{ +- /* set ADC level to -16 */ +- 11, 76, +- (1 << 13) - 825 - 117, +- (1 << 13) - 837 - 117, +- (1 << 13) - 811 - 117, +- (1 << 13) - 766 - 117, +- (1 << 13) - 737 - 117, +- (1 << 13) - 693 - 117, +- (1 << 13) - 648 - 117, +- (1 << 13) - 619 - 117, +- (1 << 13) - 575 - 117, +- (1 << 13) - 531 - 117, +- (1 << 13) - 501 - 117, +- +- // Tuner IO bank: max drive (14mA) +- 1, 912, +- 0x2c8a, +- +- 1, 1817, +- 1, +- +- 0, +-}; +- +-static int dib7000m_demod_reset(struct dib7000m_state *state) +-{ +- dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); +- +- /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ +- dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE); +- +- /* restart all parts */ +- dib7000m_write_word(state, 898, 0xffff); +- dib7000m_write_word(state, 899, 0xffff); +- dib7000m_write_word(state, 900, 0xff0f); +- dib7000m_write_word(state, 901, 0xfffc); +- +- dib7000m_write_word(state, 898, 0); +- dib7000m_write_word(state, 899, 0); +- dib7000m_write_word(state, 900, 0); +- dib7000m_write_word(state, 901, 0); +- +- if (state->revision == 0x4000) +- dib7000m_reset_pll(state); +- else +- dib7000mc_reset_pll(state); +- +- if (dib7000m_reset_gpio(state) != 0) +- dprintk( "GPIO reset was not successful."); +- +- if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0) +- dprintk( "OUTPUT_MODE could not be reset."); +- +- /* unforce divstr regardless whether i2c enumeration was done or not */ +- dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) ); +- +- dib7000m_set_bandwidth(state, 8000); +- +- dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON); +- dib7000m_sad_calib(state); +- dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF); +- +- if (state->cfg.dvbt_mode) +- dib7000m_write_word(state, 1796, 0x0); // select DVB-T output +- +- if (state->cfg.mobile_mode) +- dib7000m_write_word(state, 261 + state->reg_offs, 2); +- else +- dib7000m_write_word(state, 224 + state->reg_offs, 1); +- +- // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... +- if(state->cfg.tuner_is_baseband) +- dib7000m_write_word(state, 36, 0x0755); +- else +- dib7000m_write_word(state, 36, 0x1f55); +- +- // P_divclksel=3 P_divbitsel=1 +- if (state->revision == 0x4000) +- dib7000m_write_word(state, 909, (3 << 10) | (1 << 6)); +- else +- dib7000m_write_word(state, 909, (3 << 4) | 1); +- +- dib7000m_write_tab(state, dib7000m_defaults_common); +- dib7000m_write_tab(state, dib7000m_defaults); +- +- dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY); +- +- state->internal_clk = state->cfg.bw->internal; +- +- return 0; +-} +- +-static void dib7000m_restart_agc(struct dib7000m_state *state) +-{ +- // P_restart_iqc & P_restart_agc +- dib7000m_write_word(state, 898, 0x0c00); +- dib7000m_write_word(state, 898, 0x0000); +-} +- +-static int dib7000m_agc_soft_split(struct dib7000m_state *state) +-{ +- u16 agc,split_offset; +- +- if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) +- return 0; +- +- // n_agc_global +- agc = dib7000m_read_word(state, 390); +- +- if (agc > state->current_agc->split.min_thres) +- split_offset = state->current_agc->split.min; +- else if (agc < state->current_agc->split.max_thres) +- split_offset = state->current_agc->split.max; +- else +- split_offset = state->current_agc->split.max * +- (agc - state->current_agc->split.min_thres) / +- (state->current_agc->split.max_thres - state->current_agc->split.min_thres); +- +- dprintk( "AGC split_offset: %d",split_offset); +- +- // P_agc_force_split and P_agc_split_offset +- return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset); +-} +- +-static int dib7000m_update_lna(struct dib7000m_state *state) +-{ +- u16 dyn_gain; +- +- if (state->cfg.update_lna) { +- // read dyn_gain here (because it is demod-dependent and not fe) +- dyn_gain = dib7000m_read_word(state, 390); +- +- if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed +- dib7000m_restart_agc(state); +- return 1; +- } +- } +- return 0; +-} +- +-static int dib7000m_set_agc_config(struct dib7000m_state *state, u8 band) +-{ +- struct dibx000_agc_config *agc = NULL; +- int i; +- if (state->current_band == band && state->current_agc != NULL) +- return 0; +- state->current_band = band; +- +- for (i = 0; i < state->cfg.agc_config_count; i++) +- if (state->cfg.agc[i].band_caps & band) { +- agc = &state->cfg.agc[i]; +- break; +- } +- +- if (agc == NULL) { +- dprintk( "no valid AGC configuration found for band 0x%02x",band); +- return -EINVAL; +- } +- +- state->current_agc = agc; +- +- /* AGC */ +- dib7000m_write_word(state, 72 , agc->setup); +- dib7000m_write_word(state, 73 , agc->inv_gain); +- dib7000m_write_word(state, 74 , agc->time_stabiliz); +- dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock); +- +- // Demod AGC loop configuration +- dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp); +- dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp); +- +- dprintk( "WBD: ref: %d, sel: %d, active: %d, alpha: %d", +- state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); +- +- /* AGC continued */ +- if (state->wbd_ref != 0) +- dib7000m_write_word(state, 102, state->wbd_ref); +- else // use default +- dib7000m_write_word(state, 102, agc->wbd_ref); +- +- dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); +- dib7000m_write_word(state, 104, agc->agc1_max); +- dib7000m_write_word(state, 105, agc->agc1_min); +- dib7000m_write_word(state, 106, agc->agc2_max); +- dib7000m_write_word(state, 107, agc->agc2_min); +- dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); +- dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2); +- dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2); +- dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2); +- +- if (state->revision > 0x4000) { // settings for the MC +- dib7000m_write_word(state, 71, agc->agc1_pt3); +-// dprintk( "929: %x %d %d", +-// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel); +- dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); +- } else { +- // wrong default values +- u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 }; +- for (i = 0; i < 9; i++) +- dib7000m_write_word(state, 88 + i, b[i]); +- } +- return 0; +-} +- +-static void dib7000m_update_timf(struct dib7000m_state *state) +-{ +- u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437); +- state->timf = timf * 160 / (state->current_bandwidth / 50); +- dib7000m_write_word(state, 23, (u16) (timf >> 16)); +- dib7000m_write_word(state, 24, (u16) (timf & 0xffff)); +- dprintk( "updated timf_frequency: %d (default: %d)",state->timf, state->timf_default); +-} +- +-static int dib7000m_agc_startup(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000m_state *state = demod->demodulator_priv; +- u16 cfg_72 = dib7000m_read_word(state, 72); +- int ret = -1; +- u8 *agc_state = &state->agc_state; +- u8 agc_split; +- +- switch (state->agc_state) { +- case 0: +- // set power-up level: interf+analog+AGC +- dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC); +- dib7000m_set_adc_state(state, DIBX000_ADC_ON); +- +- if (dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency/1000)) != 0) +- return -1; +- +- ret = 7; /* ADC power up */ +- (*agc_state)++; +- break; +- +- case 1: +- /* AGC initialization */ +- if (state->cfg.agc_control) +- state->cfg.agc_control(&state->demod, 1); +- +- dib7000m_write_word(state, 75, 32768); +- if (!state->current_agc->perform_agc_softsplit) { +- /* we are using the wbd - so slow AGC startup */ +- dib7000m_write_word(state, 103, 1 << 8); /* force 0 split on WBD and restart AGC */ +- (*agc_state)++; +- ret = 5; +- } else { +- /* default AGC startup */ +- (*agc_state) = 4; +- /* wait AGC rough lock time */ +- ret = 7; +- } +- +- dib7000m_restart_agc(state); +- break; +- +- case 2: /* fast split search path after 5sec */ +- dib7000m_write_word(state, 72, cfg_72 | (1 << 4)); /* freeze AGC loop */ +- dib7000m_write_word(state, 103, 2 << 9); /* fast split search 0.25kHz */ +- (*agc_state)++; +- ret = 14; +- break; +- +- case 3: /* split search ended */ +- agc_split = (u8)dib7000m_read_word(state, 392); /* store the split value for the next time */ +- dib7000m_write_word(state, 75, dib7000m_read_word(state, 390)); /* set AGC gain start value */ +- +- dib7000m_write_word(state, 72, cfg_72 & ~(1 << 4)); /* std AGC loop */ +- dib7000m_write_word(state, 103, (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ +- +- dib7000m_restart_agc(state); +- +- dprintk( "SPLIT %p: %hd", demod, agc_split); +- +- (*agc_state)++; +- ret = 5; +- break; +- +- case 4: /* LNA startup */ +- /* wait AGC accurate lock time */ +- ret = 7; +- +- if (dib7000m_update_lna(state)) +- // wait only AGC rough lock time +- ret = 5; +- else +- (*agc_state)++; +- break; +- +- case 5: +- dib7000m_agc_soft_split(state); +- +- if (state->cfg.agc_control) +- state->cfg.agc_control(&state->demod, 0); +- +- (*agc_state)++; +- break; +- +- default: +- break; +- } +- return ret; +-} +- +-static void dib7000m_set_channel(struct dib7000m_state *state, struct dtv_frontend_properties *ch, +- u8 seq) +-{ +- u16 value, est[4]; +- +- dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); +- +- /* nfft, guard, qam, alpha */ +- value = 0; +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: value |= (0 << 7); break; +- case TRANSMISSION_MODE_4K: value |= (2 << 7); break; +- default: +- case TRANSMISSION_MODE_8K: value |= (1 << 7); break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_32: value |= (0 << 5); break; +- case GUARD_INTERVAL_1_16: value |= (1 << 5); break; +- case GUARD_INTERVAL_1_4: value |= (3 << 5); break; +- default: +- case GUARD_INTERVAL_1_8: value |= (2 << 5); break; +- } +- switch (ch->modulation) { +- case QPSK: value |= (0 << 3); break; +- case QAM_16: value |= (1 << 3); break; +- default: +- case QAM_64: value |= (2 << 3); break; +- } +- switch (HIERARCHY_1) { +- case HIERARCHY_2: value |= 2; break; +- case HIERARCHY_4: value |= 4; break; +- default: +- case HIERARCHY_1: value |= 1; break; +- } +- dib7000m_write_word(state, 0, value); +- dib7000m_write_word(state, 5, (seq << 4)); +- +- /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ +- value = 0; +- if (1 != 0) +- value |= (1 << 6); +- if (ch->hierarchy == 1) +- value |= (1 << 4); +- if (1 == 1) +- value |= 1; +- switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { +- case FEC_2_3: value |= (2 << 1); break; +- case FEC_3_4: value |= (3 << 1); break; +- case FEC_5_6: value |= (5 << 1); break; +- case FEC_7_8: value |= (7 << 1); break; +- default: +- case FEC_1_2: value |= (1 << 1); break; +- } +- dib7000m_write_word(state, 267 + state->reg_offs, value); +- +- /* offset loop parameters */ +- +- /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */ +- dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80); +- +- /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ +- dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3)); +- +- /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */ +- dib7000m_write_word(state, 32, (0 << 4) | 0x3); +- +- /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */ +- dib7000m_write_word(state, 33, (0 << 4) | 0x5); +- +- /* P_dvsy_sync_wait */ +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_8K: value = 256; break; +- case TRANSMISSION_MODE_4K: value = 128; break; +- case TRANSMISSION_MODE_2K: +- default: value = 64; break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_16: value *= 2; break; +- case GUARD_INTERVAL_1_8: value *= 4; break; +- case GUARD_INTERVAL_1_4: value *= 8; break; +- default: +- case GUARD_INTERVAL_1_32: value *= 1; break; +- } +- state->div_sync_wait = (value * 3) / 2 + 32; // add 50% SFN margin + compensate for one DVSY-fifo TODO +- +- /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */ +- /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ +- if (1 == 1 || state->revision > 0x4000) +- state->div_force_off = 0; +- else +- state->div_force_off = 1; +- dib7000m_set_diversity_in(&state->demod, state->div_state); +- +- /* channel estimation fine configuration */ +- switch (ch->modulation) { +- case QAM_64: +- est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ +- est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ +- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ +- break; +- case QAM_16: +- est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ +- est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ +- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ +- break; +- default: +- est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ +- est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ +- est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ +- est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ +- break; +- } +- for (value = 0; value < 4; value++) +- dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]); +- +- // set power-up level: autosearch +- dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD); +-} +- +-static int dib7000m_autosearch_start(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000m_state *state = demod->demodulator_priv; +- struct dtv_frontend_properties schan; +- int ret = 0; +- u32 value, factor; +- +- schan = *ch; +- +- schan.modulation = QAM_64; +- schan.guard_interval = GUARD_INTERVAL_1_32; +- schan.transmission_mode = TRANSMISSION_MODE_8K; +- schan.code_rate_HP = FEC_2_3; +- schan.code_rate_LP = FEC_3_4; +- schan.hierarchy = 0; +- +- dib7000m_set_channel(state, &schan, 7); +- +- factor = BANDWIDTH_TO_KHZ(schan.bandwidth_hz); +- if (factor >= 5000) +- factor = 1; +- else +- factor = 6; +- +- // always use the setting for 8MHz here lock_time for 7,6 MHz are longer +- value = 30 * state->internal_clk * factor; +- ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time +- ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time +- value = 100 * state->internal_clk * factor; +- ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time +- ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time +- value = 500 * state->internal_clk * factor; +- ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time +- ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time +- +- // start search +- value = dib7000m_read_word(state, 0); +- ret |= dib7000m_write_word(state, 0, (u16) (value | (1 << 9))); +- +- /* clear n_irq_pending */ +- if (state->revision == 0x4000) +- dib7000m_write_word(state, 1793, 0); +- else +- dib7000m_read_word(state, 537); +- +- ret |= dib7000m_write_word(state, 0, (u16) value); +- +- return ret; +-} +- +-static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg) +-{ +- u16 irq_pending = dib7000m_read_word(state, reg); +- +- if (irq_pending & 0x1) { // failed +- dprintk( "autosearch failed"); +- return 1; +- } +- +- if (irq_pending & 0x2) { // succeeded +- dprintk( "autosearch succeeded"); +- return 2; +- } +- return 0; // still pending +-} +- +-static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod) +-{ +- struct dib7000m_state *state = demod->demodulator_priv; +- if (state->revision == 0x4000) +- return dib7000m_autosearch_irq(state, 1793); +- else +- return dib7000m_autosearch_irq(state, 537); +-} +- +-static int dib7000m_tune(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000m_state *state = demod->demodulator_priv; +- int ret = 0; +- u16 value; +- +- // we are already tuned - just resuming from suspend +- if (ch != NULL) +- dib7000m_set_channel(state, ch, 0); +- else +- return -EINVAL; +- +- // restart demod +- ret |= dib7000m_write_word(state, 898, 0x4000); +- ret |= dib7000m_write_word(state, 898, 0x0000); +- msleep(45); +- +- dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD); +- /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ +- ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); +- +- // never achieved a lock before - wait for timfreq to update +- if (state->timf == 0) +- msleep(200); +- +- //dump_reg(state); +- /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ +- value = (6 << 8) | 0x80; +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: value |= (7 << 12); break; +- case TRANSMISSION_MODE_4K: value |= (8 << 12); break; +- default: +- case TRANSMISSION_MODE_8K: value |= (9 << 12); break; +- } +- ret |= dib7000m_write_word(state, 26, value); +- +- /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ +- value = (0 << 4); +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: value |= 0x6; break; +- case TRANSMISSION_MODE_4K: value |= 0x7; break; +- default: +- case TRANSMISSION_MODE_8K: value |= 0x8; break; +- } +- ret |= dib7000m_write_word(state, 32, value); +- +- /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ +- value = (0 << 4); +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: value |= 0x6; break; +- case TRANSMISSION_MODE_4K: value |= 0x7; break; +- default: +- case TRANSMISSION_MODE_8K: value |= 0x8; break; +- } +- ret |= dib7000m_write_word(state, 33, value); +- +- // we achieved a lock - it's time to update the timf freq +- if ((dib7000m_read_word(state, 535) >> 6) & 0x1) +- dib7000m_update_timf(state); +- +- dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); +- return ret; +-} +- +-static int dib7000m_wakeup(struct dvb_frontend *demod) +-{ +- struct dib7000m_state *state = demod->demodulator_priv; +- +- dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); +- +- if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) +- dprintk( "could not start Slow ADC"); +- +- return 0; +-} +- +-static int dib7000m_sleep(struct dvb_frontend *demod) +-{ +- struct dib7000m_state *st = demod->demodulator_priv; +- dib7000m_set_output_mode(st, OUTMODE_HIGH_Z); +- dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY); +- return dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | +- dib7000m_set_adc_state(st, DIBX000_ADC_OFF); +-} +- +-static int dib7000m_identify(struct dib7000m_state *state) +-{ +- u16 value; +- +- if ((value = dib7000m_read_word(state, 896)) != 0x01b3) { +- dprintk( "wrong Vendor ID (0x%x)",value); +- return -EREMOTEIO; +- } +- +- state->revision = dib7000m_read_word(state, 897); +- if (state->revision != 0x4000 && +- state->revision != 0x4001 && +- state->revision != 0x4002 && +- state->revision != 0x4003) { +- dprintk( "wrong Device ID (0x%x)",value); +- return -EREMOTEIO; +- } +- +- /* protect this driver to be used with 7000PC */ +- if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) { +- dprintk( "this driver does not work with DiB7000PC"); +- return -EREMOTEIO; +- } +- +- switch (state->revision) { +- case 0x4000: dprintk( "found DiB7000MA/PA/MB/PB"); break; +- case 0x4001: state->reg_offs = 1; dprintk( "found DiB7000HC"); break; +- case 0x4002: state->reg_offs = 1; dprintk( "found DiB7000MC"); break; +- case 0x4003: state->reg_offs = 1; dprintk( "found DiB9000"); break; +- } +- +- return 0; +-} +- +- +-static int dib7000m_get_frontend(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib7000m_state *state = fe->demodulator_priv; +- u16 tps = dib7000m_read_word(state,480); +- +- fep->inversion = INVERSION_AUTO; +- +- fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); +- +- switch ((tps >> 8) & 0x3) { +- case 0: fep->transmission_mode = TRANSMISSION_MODE_2K; break; +- case 1: fep->transmission_mode = TRANSMISSION_MODE_8K; break; +- /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ +- } +- +- switch (tps & 0x3) { +- case 0: fep->guard_interval = GUARD_INTERVAL_1_32; break; +- case 1: fep->guard_interval = GUARD_INTERVAL_1_16; break; +- case 2: fep->guard_interval = GUARD_INTERVAL_1_8; break; +- case 3: fep->guard_interval = GUARD_INTERVAL_1_4; break; +- } +- +- switch ((tps >> 14) & 0x3) { +- case 0: fep->modulation = QPSK; break; +- case 1: fep->modulation = QAM_16; break; +- case 2: +- default: fep->modulation = QAM_64; break; +- } +- +- /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ +- /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ +- +- fep->hierarchy = HIERARCHY_NONE; +- switch ((tps >> 5) & 0x7) { +- case 1: fep->code_rate_HP = FEC_1_2; break; +- case 2: fep->code_rate_HP = FEC_2_3; break; +- case 3: fep->code_rate_HP = FEC_3_4; break; +- case 5: fep->code_rate_HP = FEC_5_6; break; +- case 7: +- default: fep->code_rate_HP = FEC_7_8; break; +- +- } +- +- switch ((tps >> 2) & 0x7) { +- case 1: fep->code_rate_LP = FEC_1_2; break; +- case 2: fep->code_rate_LP = FEC_2_3; break; +- case 3: fep->code_rate_LP = FEC_3_4; break; +- case 5: fep->code_rate_LP = FEC_5_6; break; +- case 7: +- default: fep->code_rate_LP = FEC_7_8; break; +- } +- +- /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ +- +- return 0; +-} +- +-static int dib7000m_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib7000m_state *state = fe->demodulator_priv; +- int time, ret; +- +- dib7000m_set_output_mode(state, OUTMODE_HIGH_Z); +- +- dib7000m_set_bandwidth(state, BANDWIDTH_TO_KHZ(fep->bandwidth_hz)); +- +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- /* start up the AGC */ +- state->agc_state = 0; +- do { +- time = dib7000m_agc_startup(fe); +- if (time != -1) +- msleep(time); +- } while (time != -1); +- +- if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || +- fep->guard_interval == GUARD_INTERVAL_AUTO || +- fep->modulation == QAM_AUTO || +- fep->code_rate_HP == FEC_AUTO) { +- int i = 800, found; +- +- dib7000m_autosearch_start(fe); +- do { +- msleep(1); +- found = dib7000m_autosearch_is_irq(fe); +- } while (found == 0 && i--); +- +- dprintk("autosearch returns: %d",found); +- if (found == 0 || found == 1) +- return 0; // no channel found +- +- dib7000m_get_frontend(fe); +- } +- +- ret = dib7000m_tune(fe); +- +- /* make this a config parameter */ +- dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO); +- return ret; +-} +- +-static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- u16 lock = dib7000m_read_word(state, 535); +- +- *stat = 0; +- +- if (lock & 0x8000) +- *stat |= FE_HAS_SIGNAL; +- if (lock & 0x3000) +- *stat |= FE_HAS_CARRIER; +- if (lock & 0x0100) +- *stat |= FE_HAS_VITERBI; +- if (lock & 0x0010) +- *stat |= FE_HAS_SYNC; +- if (lock & 0x0008) +- *stat |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); +- return 0; +-} +- +-static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- *unc = dib7000m_read_word(state, 534); +- return 0; +-} +- +-static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- u16 val = dib7000m_read_word(state, 390); +- *strength = 65535 - val; +- return 0; +-} +- +-static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) +-{ +- *snr = 0x0000; +- return 0; +-} +- +-static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void dib7000m_release(struct dvb_frontend *demod) +-{ +- struct dib7000m_state *st = demod->demodulator_priv; +- dibx000_exit_i2c_master(&st->i2c_master); +- kfree(st); +-} +- +-struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +-{ +- struct dib7000m_state *st = demod->demodulator_priv; +- return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +-} +-EXPORT_SYMBOL(dib7000m_get_i2c_master); +- +-int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- u16 val = dib7000m_read_word(state, 294 + state->reg_offs) & 0xffef; +- val |= (onoff & 0x1) << 4; +- dprintk("PID filter enabled %d", onoff); +- return dib7000m_write_word(state, 294 + state->reg_offs, val); +-} +-EXPORT_SYMBOL(dib7000m_pid_filter_ctrl); +- +-int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- struct dib7000m_state *state = fe->demodulator_priv; +- dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); +- return dib7000m_write_word(state, 300 + state->reg_offs + id, +- onoff ? (1 << 13) | pid : 0); +-} +-EXPORT_SYMBOL(dib7000m_pid_filter); +- +-#if 0 +-/* used with some prototype boards */ +-int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, +- u8 default_addr, struct dib7000m_config cfg[]) +-{ +- struct dib7000m_state st = { .i2c_adap = i2c }; +- int k = 0; +- u8 new_addr = 0; +- +- for (k = no_of_demods-1; k >= 0; k--) { +- st.cfg = cfg[k]; +- +- /* designated i2c address */ +- new_addr = (0x40 + k) << 1; +- st.i2c_addr = new_addr; +- if (dib7000m_identify(&st) != 0) { +- st.i2c_addr = default_addr; +- if (dib7000m_identify(&st) != 0) { +- dprintk("DiB7000M #%d: not identified", k); +- return -EIO; +- } +- } +- +- /* start diversity to pull_down div_str - just for i2c-enumeration */ +- dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY); +- +- dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output +- +- /* set new i2c address and force divstart */ +- dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2); +- +- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); +- } +- +- for (k = 0; k < no_of_demods; k++) { +- st.cfg = cfg[k]; +- st.i2c_addr = (0x40 + k) << 1; +- +- // unforce divstr +- dib7000m_write_word(&st,1794, st.i2c_addr << 2); +- +- /* deactivate div - it was just for i2c-enumeration */ +- dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z); +- } +- +- return 0; +-} +-EXPORT_SYMBOL(dib7000m_i2c_enumeration); +-#endif +- +-static struct dvb_frontend_ops dib7000m_ops; +-struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) +-{ +- struct dvb_frontend *demod; +- struct dib7000m_state *st; +- st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- +- memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config)); +- st->i2c_adap = i2c_adap; +- st->i2c_addr = i2c_addr; +- +- demod = &st->demod; +- demod->demodulator_priv = st; +- memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); +- mutex_init(&st->i2c_buffer_lock); +- +- st->timf_default = cfg->bw->timf; +- +- if (dib7000m_identify(st) != 0) +- goto error; +- +- if (st->revision == 0x4000) +- dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr); +- else +- dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr); +- +- dib7000m_demod_reset(st); +- +- return demod; +- +-error: +- kfree(st); +- return NULL; +-} +-EXPORT_SYMBOL(dib7000m_attach); +- +-static struct dvb_frontend_ops dib7000m_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DiBcom 7000MA/MB/PA/PB/MC", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_RECOVER | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib7000m_release, +- +- .init = dib7000m_wakeup, +- .sleep = dib7000m_sleep, +- +- .set_frontend = dib7000m_set_frontend, +- .get_tune_settings = dib7000m_fe_get_tune_settings, +- .get_frontend = dib7000m_get_frontend, +- +- .read_status = dib7000m_read_status, +- .read_ber = dib7000m_read_ber, +- .read_signal_strength = dib7000m_read_signal_strength, +- .read_snr = dib7000m_read_snr, +- .read_ucblocks = dib7000m_read_unc_blocks, +-}; +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h +deleted file mode 100644 +index 81fcf22..0000000 +--- a/drivers/media/dvb/frontends/dib7000m.h ++++ /dev/null +@@ -1,90 +0,0 @@ +-#ifndef DIB7000M_H +-#define DIB7000M_H +- +-#include "dibx000_common.h" +- +-struct dib7000m_config { +- u8 dvbt_mode; +- u8 output_mpeg2_in_188_bytes; +- u8 hostbus_diversity; +- u8 tuner_is_baseband; +- u8 mobile_mode; +- int (*update_lna) (struct dvb_frontend *, u16 agc_global); +- +- u8 agc_config_count; +- struct dibx000_agc_config *agc; +- +- struct dibx000_bandwidth_config *bw; +- +-#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff +- u16 gpio_dir; +-#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000 +- u16 gpio_val; +-#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +-#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +-#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +-#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf) +-#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff +- u16 gpio_pwm_pos; +- +- u16 pwm_freq_div; +- +- u8 quartz_direct; +- +- u8 input_clk_is_div_2; +- +- int (*agc_control) (struct dvb_frontend *, u8 before); +-}; +- +-#define DEFAULT_DIB7000M_I2C_ADDRESS 18 +- +-#if defined(CONFIG_DVB_DIB7000M) || (defined(CONFIG_DVB_DIB7000M_MODULE) && \ +- defined(MODULE)) +-extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, +- u8 i2c_addr, +- struct dib7000m_config *cfg); +-extern struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *, +- enum dibx000_i2c_interface, +- int); +-extern int dib7000m_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +-extern int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +-#else +-static inline +-struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap, +- u8 i2c_addr, struct dib7000m_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline +-struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *demod, +- enum dibx000_i2c_interface intf, +- int gating) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline int dib7000m_pid_filter(struct dvb_frontend *fe, u8 id, +- u16 pid, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000m_pid_filter_ctrl(struct dvb_frontend *fe, +- uint8_t onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-#endif +- +-/* TODO +-extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +-extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod); +-extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +-extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod); +-*/ +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c +deleted file mode 100644 +index 5ceadc2..0000000 +--- a/drivers/media/dvb/frontends/dib7000p.c ++++ /dev/null +@@ -1,2462 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC). +- * +- * Copyright (C) 2005-7 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#include +-#include +-#include +-#include +- +-#include "dvb_math.h" +-#include "dvb_frontend.h" +- +-#include "dib7000p.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-static int buggy_sfn_workaround; +-module_param(buggy_sfn_workaround, int, 0644); +-MODULE_PARM_DESC(buggy_sfn_workaround, "Enable work-around for buggy SFNs (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P: "); printk(args); printk("\n"); } } while (0) +- +-struct i2c_device { +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +-}; +- +-struct dib7000p_state { +- struct dvb_frontend demod; +- struct dib7000p_config cfg; +- +- u8 i2c_addr; +- struct i2c_adapter *i2c_adap; +- +- struct dibx000_i2c_master i2c_master; +- +- u16 wbd_ref; +- +- u8 current_band; +- u32 current_bandwidth; +- struct dibx000_agc_config *current_agc; +- u32 timf; +- +- u8 div_force_off:1; +- u8 div_state:1; +- u16 div_sync_wait; +- +- u8 agc_state; +- +- u16 gpio_dir; +- u16 gpio_val; +- +- u8 sfn_workaround_active:1; +- +-#define SOC7090 0x7090 +- u16 version; +- +- u16 tuner_enable; +- struct i2c_adapter dib7090_tuner_adap; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[4]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +- +- u8 input_mode_mpeg; +-}; +- +-enum dib7000p_power_mode { +- DIB7000P_POWER_ALL = 0, +- DIB7000P_POWER_ANALOG_ADC, +- DIB7000P_POWER_INTERFACE_ONLY, +-}; +- +-/* dib7090 specific fonctions */ +-static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode); +-static int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff); +-static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode); +-static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode); +- +-static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = reg >> 8; +- state->i2c_write_buffer[1] = reg & 0xff; +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 2; +- state->msg[1].addr = state->i2c_addr >> 1; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = state->i2c_read_buffer; +- state->msg[1].len = 2; +- +- if (i2c_transfer(state->i2c_adap, state->msg, 2) != 2) +- dprintk("i2c read error on %d", reg); +- +- ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- state->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- state->i2c_write_buffer[1] = reg & 0xff; +- state->i2c_write_buffer[2] = (val >> 8) & 0xff; +- state->i2c_write_buffer[3] = val & 0xff; +- +- memset(&state->msg[0], 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 4; +- +- ret = (i2c_transfer(state->i2c_adap, state->msg, 1) != 1 ? +- -EREMOTEIO : 0); +- mutex_unlock(&state->i2c_buffer_lock); +- return ret; +-} +- +-static void dib7000p_write_tab(struct dib7000p_state *state, u16 * buf) +-{ +- u16 l = 0, r, *n; +- n = buf; +- l = *n++; +- while (l) { +- r = *n++; +- +- do { +- dib7000p_write_word(state, r, *n++); +- r++; +- } while (--l); +- l = *n++; +- } +-} +- +-static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) +-{ +- int ret = 0; +- u16 outreg, fifo_threshold, smo_mode; +- +- outreg = 0; +- fifo_threshold = 1792; +- smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); +- +- dprintk("setting output mode for demod %p to %d", &state->demod, mode); +- +- switch (mode) { +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- outreg = (1 << 10); /* 0x0400 */ +- break; +- case OUTMODE_MPEG2_PAR_CONT_CLK: +- outreg = (1 << 10) | (1 << 6); /* 0x0440 */ +- break; +- case OUTMODE_MPEG2_SERIAL: +- outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ +- break; +- case OUTMODE_DIVERSITY: +- if (state->cfg.hostbus_diversity) +- outreg = (1 << 10) | (4 << 6); /* 0x0500 */ +- else +- outreg = (1 << 11); +- break; +- case OUTMODE_MPEG2_FIFO: +- smo_mode |= (3 << 1); +- fifo_threshold = 512; +- outreg = (1 << 10) | (5 << 6); +- break; +- case OUTMODE_ANALOG_ADC: +- outreg = (1 << 10) | (3 << 6); +- break; +- case OUTMODE_HIGH_Z: +- outreg = 0; +- break; +- default: +- dprintk("Unhandled output_mode passed to be set for demod %p", &state->demod); +- break; +- } +- +- if (state->cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5); +- +- ret |= dib7000p_write_word(state, 235, smo_mode); +- ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ +- if (state->version != SOC7090) +- ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ +- +- return ret; +-} +- +-static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- +- if (state->div_force_off) { +- dprintk("diversity combination deactivated - forced by COFDM parameters"); +- onoff = 0; +- dib7000p_write_word(state, 207, 0); +- } else +- dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); +- +- state->div_state = (u8) onoff; +- +- if (onoff) { +- dib7000p_write_word(state, 204, 6); +- dib7000p_write_word(state, 205, 16); +- /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ +- } else { +- dib7000p_write_word(state, 204, 1); +- dib7000p_write_word(state, 205, 0); +- } +- +- return 0; +-} +- +-static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) +-{ +- /* by default everything is powered off */ +- u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); +- +- /* now, depending on the requested mode, we power on */ +- switch (mode) { +- /* power up everything in the demod */ +- case DIB7000P_POWER_ALL: +- reg_774 = 0x0000; +- reg_775 = 0x0000; +- reg_776 = 0x0; +- reg_899 = 0x0; +- if (state->version == SOC7090) +- reg_1280 &= 0x001f; +- else +- reg_1280 &= 0x01ff; +- break; +- +- case DIB7000P_POWER_ANALOG_ADC: +- /* dem, cfg, iqc, sad, agc */ +- reg_774 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10) | (1 << 9)); +- /* nud */ +- reg_776 &= ~((1 << 0)); +- /* Dout */ +- if (state->version != SOC7090) +- reg_1280 &= ~((1 << 11)); +- reg_1280 &= ~(1 << 6); +- /* fall through wanted to enable the interfaces */ +- +- /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ +- case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ +- if (state->version == SOC7090) +- reg_1280 &= ~((1 << 7) | (1 << 5)); +- else +- reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); +- break; +- +-/* TODO following stuff is just converted from the dib7000-driver - check when is used what */ +- } +- +- dib7000p_write_word(state, 774, reg_774); +- dib7000p_write_word(state, 775, reg_775); +- dib7000p_write_word(state, 776, reg_776); +- dib7000p_write_word(state, 1280, reg_1280); +- if (state->version != SOC7090) +- dib7000p_write_word(state, 899, reg_899); +- +- return 0; +-} +- +-static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) +-{ +- u16 reg_908 = 0, reg_909 = 0; +- u16 reg; +- +- if (state->version != SOC7090) { +- reg_908 = dib7000p_read_word(state, 908); +- reg_909 = dib7000p_read_word(state, 909); +- } +- +- switch (no) { +- case DIBX000_SLOW_ADC_ON: +- if (state->version == SOC7090) { +- reg = dib7000p_read_word(state, 1925); +- +- dib7000p_write_word(state, 1925, reg | (1 << 4) | (1 << 2)); /* en_slowAdc = 1 & reset_sladc = 1 */ +- +- reg = dib7000p_read_word(state, 1925); /* read acces to make it works... strange ... */ +- msleep(200); +- dib7000p_write_word(state, 1925, reg & ~(1 << 4)); /* en_slowAdc = 1 & reset_sladc = 0 */ +- +- reg = dib7000p_read_word(state, 72) & ~((0x3 << 14) | (0x3 << 12)); +- dib7000p_write_word(state, 72, reg | (1 << 14) | (3 << 12) | 524); /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; (Vin2 = Vcm) */ +- } else { +- reg_909 |= (1 << 1) | (1 << 0); +- dib7000p_write_word(state, 909, reg_909); +- reg_909 &= ~(1 << 1); +- } +- break; +- +- case DIBX000_SLOW_ADC_OFF: +- if (state->version == SOC7090) { +- reg = dib7000p_read_word(state, 1925); +- dib7000p_write_word(state, 1925, (reg & ~(1 << 2)) | (1 << 4)); /* reset_sladc = 1 en_slowAdc = 0 */ +- } else +- reg_909 |= (1 << 1) | (1 << 0); +- break; +- +- case DIBX000_ADC_ON: +- reg_908 &= 0x0fff; +- reg_909 &= 0x0003; +- break; +- +- case DIBX000_ADC_OFF: +- reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); +- reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); +- break; +- +- case DIBX000_VBG_ENABLE: +- reg_908 &= ~(1 << 15); +- break; +- +- case DIBX000_VBG_DISABLE: +- reg_908 |= (1 << 15); +- break; +- +- default: +- break; +- } +- +-// dprintk( "908: %x, 909: %x\n", reg_908, reg_909); +- +- reg_909 |= (state->cfg.disable_sample_and_hold & 1) << 4; +- reg_908 |= (state->cfg.enable_current_mirror & 1) << 7; +- +- if (state->version != SOC7090) { +- dib7000p_write_word(state, 908, reg_908); +- dib7000p_write_word(state, 909, reg_909); +- } +-} +- +-static int dib7000p_set_bandwidth(struct dib7000p_state *state, u32 bw) +-{ +- u32 timf; +- +- // store the current bandwidth for later use +- state->current_bandwidth = bw; +- +- if (state->timf == 0) { +- dprintk("using default timf"); +- timf = state->cfg.bw->timf; +- } else { +- dprintk("using updated timf"); +- timf = state->timf; +- } +- +- timf = timf * (bw / 50) / 160; +- +- dib7000p_write_word(state, 23, (u16) ((timf >> 16) & 0xffff)); +- dib7000p_write_word(state, 24, (u16) ((timf) & 0xffff)); +- +- return 0; +-} +- +-static int dib7000p_sad_calib(struct dib7000p_state *state) +-{ +-/* internal */ +- dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); +- +- if (state->version == SOC7090) +- dib7000p_write_word(state, 74, 2048); +- else +- dib7000p_write_word(state, 74, 776); +- +- /* do the calibration */ +- dib7000p_write_word(state, 73, (1 << 0)); +- dib7000p_write_word(state, 73, (0 << 0)); +- +- msleep(1); +- +- return 0; +-} +- +-int dib7000p_set_wbd_ref(struct dvb_frontend *demod, u16 value) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- if (value > 4095) +- value = 4095; +- state->wbd_ref = value; +- return dib7000p_write_word(state, 105, (dib7000p_read_word(state, 105) & 0xf000) | value); +-} +-EXPORT_SYMBOL(dib7000p_set_wbd_ref); +- +-int dib7000p_get_agc_values(struct dvb_frontend *fe, +- u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- +- if (agc_global != NULL) +- *agc_global = dib7000p_read_word(state, 394); +- if (agc1 != NULL) +- *agc1 = dib7000p_read_word(state, 392); +- if (agc2 != NULL) +- *agc2 = dib7000p_read_word(state, 393); +- if (wbd != NULL) +- *wbd = dib7000p_read_word(state, 397); +- +- return 0; +-} +-EXPORT_SYMBOL(dib7000p_get_agc_values); +- +-static void dib7000p_reset_pll(struct dib7000p_state *state) +-{ +- struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; +- u16 clk_cfg0; +- +- if (state->version == SOC7090) { +- dib7000p_write_word(state, 1856, (!bw->pll_reset << 13) | (bw->pll_range << 12) | (bw->pll_ratio << 6) | (bw->pll_prediv)); +- +- while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) +- ; +- +- dib7000p_write_word(state, 1857, dib7000p_read_word(state, 1857) | (!bw->pll_bypass << 15)); +- } else { +- /* force PLL bypass */ +- clk_cfg0 = (1 << 15) | ((bw->pll_ratio & 0x3f) << 9) | +- (bw->modulo << 7) | (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0); +- +- dib7000p_write_word(state, 900, clk_cfg0); +- +- /* P_pll_cfg */ +- dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); +- clk_cfg0 = (bw->pll_bypass << 15) | (clk_cfg0 & 0x7fff); +- dib7000p_write_word(state, 900, clk_cfg0); +- } +- +- dib7000p_write_word(state, 18, (u16) (((bw->internal * 1000) >> 16) & 0xffff)); +- dib7000p_write_word(state, 19, (u16) ((bw->internal * 1000) & 0xffff)); +- dib7000p_write_word(state, 21, (u16) ((bw->ifreq >> 16) & 0xffff)); +- dib7000p_write_word(state, 22, (u16) ((bw->ifreq) & 0xffff)); +- +- dib7000p_write_word(state, 72, bw->sad_cfg); +-} +- +-static u32 dib7000p_get_internal_freq(struct dib7000p_state *state) +-{ +- u32 internal = (u32) dib7000p_read_word(state, 18) << 16; +- internal |= (u32) dib7000p_read_word(state, 19); +- internal /= 1000; +- +- return internal; +-} +- +-int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 reg_1857, reg_1856 = dib7000p_read_word(state, 1856); +- u8 loopdiv, prediv; +- u32 internal, xtal; +- +- /* get back old values */ +- prediv = reg_1856 & 0x3f; +- loopdiv = (reg_1856 >> 6) & 0x3f; +- +- if ((bw != NULL) && (bw->pll_prediv != prediv || bw->pll_ratio != loopdiv)) { +- dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, bw->pll_prediv, loopdiv, bw->pll_ratio); +- reg_1856 &= 0xf000; +- reg_1857 = dib7000p_read_word(state, 1857); +- dib7000p_write_word(state, 1857, reg_1857 & ~(1 << 15)); +- +- dib7000p_write_word(state, 1856, reg_1856 | ((bw->pll_ratio & 0x3f) << 6) | (bw->pll_prediv & 0x3f)); +- +- /* write new system clk into P_sec_len */ +- internal = dib7000p_get_internal_freq(state); +- xtal = (internal / loopdiv) * prediv; +- internal = 1000 * (xtal / bw->pll_prediv) * bw->pll_ratio; /* new internal */ +- dib7000p_write_word(state, 18, (u16) ((internal >> 16) & 0xffff)); +- dib7000p_write_word(state, 19, (u16) (internal & 0xffff)); +- +- dib7000p_write_word(state, 1857, reg_1857 | (1 << 15)); +- +- while (((dib7000p_read_word(state, 1856) >> 15) & 0x1) != 1) +- dprintk("Waiting for PLL to lock"); +- +- return 0; +- } +- return -EIO; +-} +-EXPORT_SYMBOL(dib7000p_update_pll); +- +-static int dib7000p_reset_gpio(struct dib7000p_state *st) +-{ +- /* reset the GPIOs */ +- dprintk("gpio dir: %x: val: %x, pwm_pos: %x", st->gpio_dir, st->gpio_val, st->cfg.gpio_pwm_pos); +- +- dib7000p_write_word(st, 1029, st->gpio_dir); +- dib7000p_write_word(st, 1030, st->gpio_val); +- +- /* TODO 1031 is P_gpio_od */ +- +- dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos); +- +- dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div); +- return 0; +-} +- +-static int dib7000p_cfg_gpio(struct dib7000p_state *st, u8 num, u8 dir, u8 val) +-{ +- st->gpio_dir = dib7000p_read_word(st, 1029); +- st->gpio_dir &= ~(1 << num); /* reset the direction bit */ +- st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ +- dib7000p_write_word(st, 1029, st->gpio_dir); +- +- st->gpio_val = dib7000p_read_word(st, 1030); +- st->gpio_val &= ~(1 << num); /* reset the direction bit */ +- st->gpio_val |= (val & 0x01) << num; /* set the new value */ +- dib7000p_write_word(st, 1030, st->gpio_val); +- +- return 0; +-} +- +-int dib7000p_set_gpio(struct dvb_frontend *demod, u8 num, u8 dir, u8 val) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- return dib7000p_cfg_gpio(state, num, dir, val); +-} +-EXPORT_SYMBOL(dib7000p_set_gpio); +- +-static u16 dib7000p_defaults[] = { +- // auto search configuration +- 3, 2, +- 0x0004, +- (1<<3)|(1<<11)|(1<<12)|(1<<13), +- 0x0814, /* Equal Lock */ +- +- 12, 6, +- 0x001b, +- 0x7740, +- 0x005b, +- 0x8d80, +- 0x01c9, +- 0xc380, +- 0x0000, +- 0x0080, +- 0x0000, +- 0x0090, +- 0x0001, +- 0xd4c0, +- +- 1, 26, +- 0x6680, +- +- /* set ADC level to -16 */ +- 11, 79, +- (1 << 13) - 825 - 117, +- (1 << 13) - 837 - 117, +- (1 << 13) - 811 - 117, +- (1 << 13) - 766 - 117, +- (1 << 13) - 737 - 117, +- (1 << 13) - 693 - 117, +- (1 << 13) - 648 - 117, +- (1 << 13) - 619 - 117, +- (1 << 13) - 575 - 117, +- (1 << 13) - 531 - 117, +- (1 << 13) - 501 - 117, +- +- 1, 142, +- 0x0410, +- +- /* disable power smoothing */ +- 8, 145, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- 0, +- +- 1, 154, +- 1 << 13, +- +- 1, 168, +- 0x0ccd, +- +- 1, 183, +- 0x200f, +- +- 1, 212, +- 0x169, +- +- 5, 187, +- 0x023d, +- 0x00a4, +- 0x00a4, +- 0x7ff0, +- 0x3ccc, +- +- 1, 198, +- 0x800, +- +- 1, 222, +- 0x0010, +- +- 1, 235, +- 0x0062, +- +- 0, +-}; +- +-static int dib7000p_demod_reset(struct dib7000p_state *state) +-{ +- dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); +- +- if (state->version == SOC7090) +- dibx000_reset_i2c_master(&state->i2c_master); +- +- dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); +- +- /* restart all parts */ +- dib7000p_write_word(state, 770, 0xffff); +- dib7000p_write_word(state, 771, 0xffff); +- dib7000p_write_word(state, 772, 0x001f); +- dib7000p_write_word(state, 1280, 0x001f - ((1 << 4) | (1 << 3))); +- +- dib7000p_write_word(state, 770, 0); +- dib7000p_write_word(state, 771, 0); +- dib7000p_write_word(state, 772, 0); +- dib7000p_write_word(state, 1280, 0); +- +- if (state->version != SOC7090) { +- dib7000p_write_word(state, 898, 0x0003); +- dib7000p_write_word(state, 898, 0); +- } +- +- /* default */ +- dib7000p_reset_pll(state); +- +- if (dib7000p_reset_gpio(state) != 0) +- dprintk("GPIO reset was not successful."); +- +- if (state->version == SOC7090) { +- dib7000p_write_word(state, 899, 0); +- +- /* impulse noise */ +- dib7000p_write_word(state, 42, (1<<5) | 3); /* P_iqc_thsat_ipc = 1 ; P_iqc_win2 = 3 */ +- dib7000p_write_word(state, 43, 0x2d4); /*-300 fag P_iqc_dect_min = -280 */ +- dib7000p_write_word(state, 44, 300); /* 300 fag P_iqc_dect_min = +280 */ +- dib7000p_write_word(state, 273, (0<<6) | 30); +- } +- if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) +- dprintk("OUTPUT_MODE could not be reset."); +- +- dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); +- dib7000p_sad_calib(state); +- dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_OFF); +- +- /* unforce divstr regardless whether i2c enumeration was done or not */ +- dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1)); +- +- dib7000p_set_bandwidth(state, 8000); +- +- if (state->version == SOC7090) { +- dib7000p_write_word(state, 36, 0x0755);/* P_iqc_impnc_on =1 & P_iqc_corr_inh = 1 for impulsive noise */ +- } else { +- if (state->cfg.tuner_is_baseband) +- dib7000p_write_word(state, 36, 0x0755); +- else +- dib7000p_write_word(state, 36, 0x1f55); +- } +- +- dib7000p_write_tab(state, dib7000p_defaults); +- if (state->version != SOC7090) { +- dib7000p_write_word(state, 901, 0x0006); +- dib7000p_write_word(state, 902, (3 << 10) | (1 << 6)); +- dib7000p_write_word(state, 905, 0x2c8e); +- } +- +- dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +- +- return 0; +-} +- +-static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) +-{ +- u16 tmp = 0; +- tmp = dib7000p_read_word(state, 903); +- dib7000p_write_word(state, 903, (tmp | 0x1)); +- tmp = dib7000p_read_word(state, 900); +- dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); +-} +- +-static void dib7000p_restart_agc(struct dib7000p_state *state) +-{ +- // P_restart_iqc & P_restart_agc +- dib7000p_write_word(state, 770, (1 << 11) | (1 << 9)); +- dib7000p_write_word(state, 770, 0x0000); +-} +- +-static int dib7000p_update_lna(struct dib7000p_state *state) +-{ +- u16 dyn_gain; +- +- if (state->cfg.update_lna) { +- dyn_gain = dib7000p_read_word(state, 394); +- if (state->cfg.update_lna(&state->demod, dyn_gain)) { +- dib7000p_restart_agc(state); +- return 1; +- } +- } +- +- return 0; +-} +- +-static int dib7000p_set_agc_config(struct dib7000p_state *state, u8 band) +-{ +- struct dibx000_agc_config *agc = NULL; +- int i; +- if (state->current_band == band && state->current_agc != NULL) +- return 0; +- state->current_band = band; +- +- for (i = 0; i < state->cfg.agc_config_count; i++) +- if (state->cfg.agc[i].band_caps & band) { +- agc = &state->cfg.agc[i]; +- break; +- } +- +- if (agc == NULL) { +- dprintk("no valid AGC configuration found for band 0x%02x", band); +- return -EINVAL; +- } +- +- state->current_agc = agc; +- +- /* AGC */ +- dib7000p_write_word(state, 75, agc->setup); +- dib7000p_write_word(state, 76, agc->inv_gain); +- dib7000p_write_word(state, 77, agc->time_stabiliz); +- dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); +- +- // Demod AGC loop configuration +- dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); +- dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); +- +- /* AGC continued */ +- dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", +- state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); +- +- if (state->wbd_ref != 0) +- dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref); +- else +- dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref); +- +- dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); +- +- dib7000p_write_word(state, 107, agc->agc1_max); +- dib7000p_write_word(state, 108, agc->agc1_min); +- dib7000p_write_word(state, 109, agc->agc2_max); +- dib7000p_write_word(state, 110, agc->agc2_min); +- dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2); +- dib7000p_write_word(state, 112, agc->agc1_pt3); +- dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); +- dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); +- dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); +- return 0; +-} +- +-static void dib7000p_set_dds(struct dib7000p_state *state, s32 offset_khz) +-{ +- u32 internal = dib7000p_get_internal_freq(state); +- s32 unit_khz_dds_val = 67108864 / (internal); /* 2**26 / Fsampling is the unit 1KHz offset */ +- u32 abs_offset_khz = ABS(offset_khz); +- u32 dds = state->cfg.bw->ifreq & 0x1ffffff; +- u8 invert = !!(state->cfg.bw->ifreq & (1 << 25)); +- +- dprintk("setting a frequency offset of %dkHz internal freq = %d invert = %d", offset_khz, internal, invert); +- +- if (offset_khz < 0) +- unit_khz_dds_val *= -1; +- +- /* IF tuner */ +- if (invert) +- dds -= (abs_offset_khz * unit_khz_dds_val); /* /100 because of /100 on the unit_khz_dds_val line calc for better accuracy */ +- else +- dds += (abs_offset_khz * unit_khz_dds_val); +- +- if (abs_offset_khz <= (internal / 2)) { /* Max dds offset is the half of the demod freq */ +- dib7000p_write_word(state, 21, (u16) (((dds >> 16) & 0x1ff) | (0 << 10) | (invert << 9))); +- dib7000p_write_word(state, 22, (u16) (dds & 0xffff)); +- } +-} +- +-static int dib7000p_agc_startup(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000p_state *state = demod->demodulator_priv; +- int ret = -1; +- u8 *agc_state = &state->agc_state; +- u8 agc_split; +- u16 reg; +- u32 upd_demod_gain_period = 0x1000; +- +- switch (state->agc_state) { +- case 0: +- dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); +- if (state->version == SOC7090) { +- reg = dib7000p_read_word(state, 0x79b) & 0xff00; +- dib7000p_write_word(state, 0x79a, upd_demod_gain_period & 0xFFFF); /* lsb */ +- dib7000p_write_word(state, 0x79b, reg | (1 << 14) | ((upd_demod_gain_period >> 16) & 0xFF)); +- +- /* enable adc i & q */ +- reg = dib7000p_read_word(state, 0x780); +- dib7000p_write_word(state, 0x780, (reg | (0x3)) & (~(1 << 7))); +- } else { +- dib7000p_set_adc_state(state, DIBX000_ADC_ON); +- dib7000p_pll_clk_cfg(state); +- } +- +- if (dib7000p_set_agc_config(state, BAND_OF_FREQUENCY(ch->frequency / 1000)) != 0) +- return -1; +- +- dib7000p_set_dds(state, 0); +- ret = 7; +- (*agc_state)++; +- break; +- +- case 1: +- if (state->cfg.agc_control) +- state->cfg.agc_control(&state->demod, 1); +- +- dib7000p_write_word(state, 78, 32768); +- if (!state->current_agc->perform_agc_softsplit) { +- /* we are using the wbd - so slow AGC startup */ +- /* force 0 split on WBD and restart AGC */ +- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | (1 << 8)); +- (*agc_state)++; +- ret = 5; +- } else { +- /* default AGC startup */ +- (*agc_state) = 4; +- /* wait AGC rough lock time */ +- ret = 7; +- } +- +- dib7000p_restart_agc(state); +- break; +- +- case 2: /* fast split search path after 5sec */ +- dib7000p_write_word(state, 75, state->current_agc->setup | (1 << 4)); /* freeze AGC loop */ +- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (2 << 9) | (0 << 8)); /* fast split search 0.25kHz */ +- (*agc_state)++; +- ret = 14; +- break; +- +- case 3: /* split search ended */ +- agc_split = (u8) dib7000p_read_word(state, 396); /* store the split value for the next time */ +- dib7000p_write_word(state, 78, dib7000p_read_word(state, 394)); /* set AGC gain start value */ +- +- dib7000p_write_word(state, 75, state->current_agc->setup); /* std AGC loop */ +- dib7000p_write_word(state, 106, (state->current_agc->wbd_sel << 13) | (state->current_agc->wbd_alpha << 9) | agc_split); /* standard split search */ +- +- dib7000p_restart_agc(state); +- +- dprintk("SPLIT %p: %hd", demod, agc_split); +- +- (*agc_state)++; +- ret = 5; +- break; +- +- case 4: /* LNA startup */ +- ret = 7; +- +- if (dib7000p_update_lna(state)) +- ret = 5; +- else +- (*agc_state)++; +- break; +- +- case 5: +- if (state->cfg.agc_control) +- state->cfg.agc_control(&state->demod, 0); +- (*agc_state)++; +- break; +- default: +- break; +- } +- return ret; +-} +- +-static void dib7000p_update_timf(struct dib7000p_state *state) +-{ +- u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428); +- state->timf = timf * 160 / (state->current_bandwidth / 50); +- dib7000p_write_word(state, 23, (u16) (timf >> 16)); +- dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); +- dprintk("updated timf_frequency: %d (default: %d)", state->timf, state->cfg.bw->timf); +- +-} +- +-u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- switch (op) { +- case DEMOD_TIMF_SET: +- state->timf = timf; +- break; +- case DEMOD_TIMF_UPDATE: +- dib7000p_update_timf(state); +- break; +- case DEMOD_TIMF_GET: +- break; +- } +- dib7000p_set_bandwidth(state, state->current_bandwidth); +- return state->timf; +-} +-EXPORT_SYMBOL(dib7000p_ctrl_timf); +- +-static void dib7000p_set_channel(struct dib7000p_state *state, +- struct dtv_frontend_properties *ch, u8 seq) +-{ +- u16 value, est[4]; +- +- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); +- +- /* nfft, guard, qam, alpha */ +- value = 0; +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- value |= (0 << 7); +- break; +- case TRANSMISSION_MODE_4K: +- value |= (2 << 7); +- break; +- default: +- case TRANSMISSION_MODE_8K: +- value |= (1 << 7); +- break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_32: +- value |= (0 << 5); +- break; +- case GUARD_INTERVAL_1_16: +- value |= (1 << 5); +- break; +- case GUARD_INTERVAL_1_4: +- value |= (3 << 5); +- break; +- default: +- case GUARD_INTERVAL_1_8: +- value |= (2 << 5); +- break; +- } +- switch (ch->modulation) { +- case QPSK: +- value |= (0 << 3); +- break; +- case QAM_16: +- value |= (1 << 3); +- break; +- default: +- case QAM_64: +- value |= (2 << 3); +- break; +- } +- switch (HIERARCHY_1) { +- case HIERARCHY_2: +- value |= 2; +- break; +- case HIERARCHY_4: +- value |= 4; +- break; +- default: +- case HIERARCHY_1: +- value |= 1; +- break; +- } +- dib7000p_write_word(state, 0, value); +- dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ +- +- /* P_dintl_native, P_dintlv_inv, P_hrch, P_code_rate, P_select_hp */ +- value = 0; +- if (1 != 0) +- value |= (1 << 6); +- if (ch->hierarchy == 1) +- value |= (1 << 4); +- if (1 == 1) +- value |= 1; +- switch ((ch->hierarchy == 0 || 1 == 1) ? ch->code_rate_HP : ch->code_rate_LP) { +- case FEC_2_3: +- value |= (2 << 1); +- break; +- case FEC_3_4: +- value |= (3 << 1); +- break; +- case FEC_5_6: +- value |= (5 << 1); +- break; +- case FEC_7_8: +- value |= (7 << 1); +- break; +- default: +- case FEC_1_2: +- value |= (1 << 1); +- break; +- } +- dib7000p_write_word(state, 208, value); +- +- /* offset loop parameters */ +- dib7000p_write_word(state, 26, 0x6680); +- dib7000p_write_word(state, 32, 0x0003); +- dib7000p_write_word(state, 29, 0x1273); +- dib7000p_write_word(state, 33, 0x0005); +- +- /* P_dvsy_sync_wait */ +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_8K: +- value = 256; +- break; +- case TRANSMISSION_MODE_4K: +- value = 128; +- break; +- case TRANSMISSION_MODE_2K: +- default: +- value = 64; +- break; +- } +- switch (ch->guard_interval) { +- case GUARD_INTERVAL_1_16: +- value *= 2; +- break; +- case GUARD_INTERVAL_1_8: +- value *= 4; +- break; +- case GUARD_INTERVAL_1_4: +- value *= 8; +- break; +- default: +- case GUARD_INTERVAL_1_32: +- value *= 1; +- break; +- } +- if (state->cfg.diversity_delay == 0) +- state->div_sync_wait = (value * 3) / 2 + 48; +- else +- state->div_sync_wait = (value * 3) / 2 + state->cfg.diversity_delay; +- +- /* deactive the possibility of diversity reception if extended interleaver */ +- state->div_force_off = !1 && ch->transmission_mode != TRANSMISSION_MODE_8K; +- dib7000p_set_diversity_in(&state->demod, state->div_state); +- +- /* channel estimation fine configuration */ +- switch (ch->modulation) { +- case QAM_64: +- est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ +- est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ +- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ +- break; +- case QAM_16: +- est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ +- est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ +- est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ +- break; +- default: +- est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ +- est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ +- est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ +- est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ +- break; +- } +- for (value = 0; value < 4; value++) +- dib7000p_write_word(state, 187 + value, est[value]); +-} +- +-static int dib7000p_autosearch_start(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000p_state *state = demod->demodulator_priv; +- struct dtv_frontend_properties schan; +- u32 value, factor; +- u32 internal = dib7000p_get_internal_freq(state); +- +- schan = *ch; +- schan.modulation = QAM_64; +- schan.guard_interval = GUARD_INTERVAL_1_32; +- schan.transmission_mode = TRANSMISSION_MODE_8K; +- schan.code_rate_HP = FEC_2_3; +- schan.code_rate_LP = FEC_3_4; +- schan.hierarchy = 0; +- +- dib7000p_set_channel(state, &schan, 7); +- +- factor = BANDWIDTH_TO_KHZ(ch->bandwidth_hz); +- if (factor >= 5000) { +- if (state->version == SOC7090) +- factor = 2; +- else +- factor = 1; +- } else +- factor = 6; +- +- value = 30 * internal * factor; +- dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); +- dib7000p_write_word(state, 7, (u16) (value & 0xffff)); +- value = 100 * internal * factor; +- dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); +- dib7000p_write_word(state, 9, (u16) (value & 0xffff)); +- value = 500 * internal * factor; +- dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); +- dib7000p_write_word(state, 11, (u16) (value & 0xffff)); +- +- value = dib7000p_read_word(state, 0); +- dib7000p_write_word(state, 0, (u16) ((1 << 9) | value)); +- dib7000p_read_word(state, 1284); +- dib7000p_write_word(state, 0, (u16) value); +- +- return 0; +-} +- +-static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- u16 irq_pending = dib7000p_read_word(state, 1284); +- +- if (irq_pending & 0x1) +- return 1; +- +- if (irq_pending & 0x2) +- return 2; +- +- return 0; +-} +- +-static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32 bw) +-{ +- static s16 notch[] = { 16143, 14402, 12238, 9713, 6902, 3888, 759, -2392 }; +- static u8 sine[] = { 0, 2, 3, 5, 6, 8, 9, 11, 13, 14, 16, 17, 19, 20, 22, +- 24, 25, 27, 28, 30, 31, 33, 34, 36, 38, 39, 41, 42, 44, 45, 47, 48, 50, 51, +- 53, 55, 56, 58, 59, 61, 62, 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 80, +- 82, 83, 85, 86, 88, 89, 91, 92, 94, 95, 97, 98, 99, 101, 102, 104, 105, +- 107, 108, 109, 111, 112, 114, 115, 117, 118, 119, 121, 122, 123, 125, 126, +- 128, 129, 130, 132, 133, 134, 136, 137, 138, 140, 141, 142, 144, 145, 146, +- 147, 149, 150, 151, 152, 154, 155, 156, 157, 159, 160, 161, 162, 164, 165, +- 166, 167, 168, 170, 171, 172, 173, 174, 175, 177, 178, 179, 180, 181, 182, +- 183, 184, 185, 186, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, +- 199, 200, 201, 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 212, +- 213, 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, +- 225, 226, 227, 227, 228, 229, 229, 230, 231, 231, 232, 233, 233, 234, 235, +- 235, 236, 237, 237, 238, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, +- 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, +- 250, 250, 250, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, +- 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +- 255, 255, 255, 255, 255, 255 +- }; +- +- u32 xtal = state->cfg.bw->xtal_hz / 1000; +- int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz; +- int k; +- int coef_re[8], coef_im[8]; +- int bw_khz = bw; +- u32 pha; +- +- dprintk("relative position of the Spur: %dk (RF: %dk, XTAL: %dk)", f_rel, rf_khz, xtal); +- +- if (f_rel < -bw_khz / 2 || f_rel > bw_khz / 2) +- return; +- +- bw_khz /= 100; +- +- dib7000p_write_word(state, 142, 0x0610); +- +- for (k = 0; k < 8; k++) { +- pha = ((f_rel * (k + 1) * 112 * 80 / bw_khz) / 1000) & 0x3ff; +- +- if (pha == 0) { +- coef_re[k] = 256; +- coef_im[k] = 0; +- } else if (pha < 256) { +- coef_re[k] = sine[256 - (pha & 0xff)]; +- coef_im[k] = sine[pha & 0xff]; +- } else if (pha == 256) { +- coef_re[k] = 0; +- coef_im[k] = 256; +- } else if (pha < 512) { +- coef_re[k] = -sine[pha & 0xff]; +- coef_im[k] = sine[256 - (pha & 0xff)]; +- } else if (pha == 512) { +- coef_re[k] = -256; +- coef_im[k] = 0; +- } else if (pha < 768) { +- coef_re[k] = -sine[256 - (pha & 0xff)]; +- coef_im[k] = -sine[pha & 0xff]; +- } else if (pha == 768) { +- coef_re[k] = 0; +- coef_im[k] = -256; +- } else { +- coef_re[k] = sine[pha & 0xff]; +- coef_im[k] = -sine[256 - (pha & 0xff)]; +- } +- +- coef_re[k] *= notch[k]; +- coef_re[k] += (1 << 14); +- if (coef_re[k] >= (1 << 24)) +- coef_re[k] = (1 << 24) - 1; +- coef_re[k] /= (1 << 15); +- +- coef_im[k] *= notch[k]; +- coef_im[k] += (1 << 14); +- if (coef_im[k] >= (1 << 24)) +- coef_im[k] = (1 << 24) - 1; +- coef_im[k] /= (1 << 15); +- +- dprintk("PALF COEF: %d re: %d im: %d", k, coef_re[k], coef_im[k]); +- +- dib7000p_write_word(state, 143, (0 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); +- dib7000p_write_word(state, 144, coef_im[k] & 0x3ff); +- dib7000p_write_word(state, 143, (1 << 14) | (k << 10) | (coef_re[k] & 0x3ff)); +- } +- dib7000p_write_word(state, 143, 0); +-} +- +-static int dib7000p_tune(struct dvb_frontend *demod) +-{ +- struct dtv_frontend_properties *ch = &demod->dtv_property_cache; +- struct dib7000p_state *state = demod->demodulator_priv; +- u16 tmp = 0; +- +- if (ch != NULL) +- dib7000p_set_channel(state, ch, 0); +- else +- return -EINVAL; +- +- // restart demod +- dib7000p_write_word(state, 770, 0x4000); +- dib7000p_write_word(state, 770, 0x0000); +- msleep(45); +- +- /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ +- tmp = (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3); +- if (state->sfn_workaround_active) { +- dprintk("SFN workaround is active"); +- tmp |= (1 << 9); +- dib7000p_write_word(state, 166, 0x4000); +- } else { +- dib7000p_write_word(state, 166, 0x0000); +- } +- dib7000p_write_word(state, 29, tmp); +- +- // never achieved a lock with that bandwidth so far - wait for osc-freq to update +- if (state->timf == 0) +- msleep(200); +- +- /* offset loop parameters */ +- +- /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ +- tmp = (6 << 8) | 0x80; +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- tmp |= (2 << 12); +- break; +- case TRANSMISSION_MODE_4K: +- tmp |= (3 << 12); +- break; +- default: +- case TRANSMISSION_MODE_8K: +- tmp |= (4 << 12); +- break; +- } +- dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ +- +- /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ +- tmp = (0 << 4); +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- tmp |= 0x6; +- break; +- case TRANSMISSION_MODE_4K: +- tmp |= 0x7; +- break; +- default: +- case TRANSMISSION_MODE_8K: +- tmp |= 0x8; +- break; +- } +- dib7000p_write_word(state, 32, tmp); +- +- /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ +- tmp = (0 << 4); +- switch (ch->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- tmp |= 0x6; +- break; +- case TRANSMISSION_MODE_4K: +- tmp |= 0x7; +- break; +- default: +- case TRANSMISSION_MODE_8K: +- tmp |= 0x8; +- break; +- } +- dib7000p_write_word(state, 33, tmp); +- +- tmp = dib7000p_read_word(state, 509); +- if (!((tmp >> 6) & 0x1)) { +- /* restart the fec */ +- tmp = dib7000p_read_word(state, 771); +- dib7000p_write_word(state, 771, tmp | (1 << 1)); +- dib7000p_write_word(state, 771, tmp); +- msleep(40); +- tmp = dib7000p_read_word(state, 509); +- } +- // we achieved a lock - it's time to update the osc freq +- if ((tmp >> 6) & 0x1) { +- dib7000p_update_timf(state); +- /* P_timf_alpha += 2 */ +- tmp = dib7000p_read_word(state, 26); +- dib7000p_write_word(state, 26, (tmp & ~(0xf << 12)) | ((((tmp >> 12) & 0xf) + 5) << 12)); +- } +- +- if (state->cfg.spur_protect) +- dib7000p_spur_protect(state, ch->frequency / 1000, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); +- +- dib7000p_set_bandwidth(state, BANDWIDTH_TO_KHZ(ch->bandwidth_hz)); +- return 0; +-} +- +-static int dib7000p_wakeup(struct dvb_frontend *demod) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); +- dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); +- if (state->version == SOC7090) +- dib7000p_sad_calib(state); +- return 0; +-} +- +-static int dib7000p_sleep(struct dvb_frontend *demod) +-{ +- struct dib7000p_state *state = demod->demodulator_priv; +- if (state->version == SOC7090) +- return dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +- return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +-} +- +-static int dib7000p_identify(struct dib7000p_state *st) +-{ +- u16 value; +- dprintk("checking demod on I2C address: %d (%x)", st->i2c_addr, st->i2c_addr); +- +- if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { +- dprintk("wrong Vendor ID (read=0x%x)", value); +- return -EREMOTEIO; +- } +- +- if ((value = dib7000p_read_word(st, 769)) != 0x4000) { +- dprintk("wrong Device ID (%x)", value); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int dib7000p_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 tps = dib7000p_read_word(state, 463); +- +- fep->inversion = INVERSION_AUTO; +- +- fep->bandwidth_hz = BANDWIDTH_TO_HZ(state->current_bandwidth); +- +- switch ((tps >> 8) & 0x3) { +- case 0: +- fep->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- fep->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- /* case 2: fep->transmission_mode = TRANSMISSION_MODE_4K; break; */ +- } +- +- switch (tps & 0x3) { +- case 0: +- fep->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- fep->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- fep->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- fep->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- switch ((tps >> 14) & 0x3) { +- case 0: +- fep->modulation = QPSK; +- break; +- case 1: +- fep->modulation = QAM_16; +- break; +- case 2: +- default: +- fep->modulation = QAM_64; +- break; +- } +- +- /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ +- /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ +- +- fep->hierarchy = HIERARCHY_NONE; +- switch ((tps >> 5) & 0x7) { +- case 1: +- fep->code_rate_HP = FEC_1_2; +- break; +- case 2: +- fep->code_rate_HP = FEC_2_3; +- break; +- case 3: +- fep->code_rate_HP = FEC_3_4; +- break; +- case 5: +- fep->code_rate_HP = FEC_5_6; +- break; +- case 7: +- default: +- fep->code_rate_HP = FEC_7_8; +- break; +- +- } +- +- switch ((tps >> 2) & 0x7) { +- case 1: +- fep->code_rate_LP = FEC_1_2; +- break; +- case 2: +- fep->code_rate_LP = FEC_2_3; +- break; +- case 3: +- fep->code_rate_LP = FEC_3_4; +- break; +- case 5: +- fep->code_rate_LP = FEC_5_6; +- break; +- case 7: +- default: +- fep->code_rate_LP = FEC_7_8; +- break; +- } +- +- /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ +- +- return 0; +-} +- +-static int dib7000p_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct dib7000p_state *state = fe->demodulator_priv; +- int time, ret; +- +- if (state->version == SOC7090) +- dib7090_set_diversity_in(fe, 0); +- else +- dib7000p_set_output_mode(state, OUTMODE_HIGH_Z); +- +- /* maybe the parameter has been changed */ +- state->sfn_workaround_active = buggy_sfn_workaround; +- +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- /* start up the AGC */ +- state->agc_state = 0; +- do { +- time = dib7000p_agc_startup(fe); +- if (time != -1) +- msleep(time); +- } while (time != -1); +- +- if (fep->transmission_mode == TRANSMISSION_MODE_AUTO || +- fep->guard_interval == GUARD_INTERVAL_AUTO || fep->modulation == QAM_AUTO || fep->code_rate_HP == FEC_AUTO) { +- int i = 800, found; +- +- dib7000p_autosearch_start(fe); +- do { +- msleep(1); +- found = dib7000p_autosearch_is_irq(fe); +- } while (found == 0 && i--); +- +- dprintk("autosearch returns: %d", found); +- if (found == 0 || found == 1) +- return 0; +- +- dib7000p_get_frontend(fe); +- } +- +- ret = dib7000p_tune(fe); +- +- /* make this a config parameter */ +- if (state->version == SOC7090) { +- dib7090_set_output_mode(fe, state->cfg.output_mode); +- if (state->cfg.enMpegOutput == 0) { +- dib7090_setDibTxMux(state, MPEG_ON_DIBTX); +- dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- } +- } else +- dib7000p_set_output_mode(state, state->cfg.output_mode); +- +- return ret; +-} +- +-static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t * stat) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 lock = dib7000p_read_word(state, 509); +- +- *stat = 0; +- +- if (lock & 0x8000) +- *stat |= FE_HAS_SIGNAL; +- if (lock & 0x3000) +- *stat |= FE_HAS_CARRIER; +- if (lock & 0x0100) +- *stat |= FE_HAS_VITERBI; +- if (lock & 0x0010) +- *stat |= FE_HAS_SYNC; +- if ((lock & 0x0038) == 0x38) +- *stat |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int dib7000p_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); +- return 0; +-} +- +-static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- *unc = dib7000p_read_word(state, 506); +- return 0; +-} +- +-static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 val = dib7000p_read_word(state, 394); +- *strength = 65535 - val; +- return 0; +-} +- +-static int dib7000p_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 val; +- s32 signal_mant, signal_exp, noise_mant, noise_exp; +- u32 result = 0; +- +- val = dib7000p_read_word(state, 479); +- noise_mant = (val >> 4) & 0xff; +- noise_exp = ((val & 0xf) << 2); +- val = dib7000p_read_word(state, 480); +- noise_exp += ((val >> 14) & 0x3); +- if ((noise_exp & 0x20) != 0) +- noise_exp -= 0x40; +- +- signal_mant = (val >> 6) & 0xFF; +- signal_exp = (val & 0x3F); +- if ((signal_exp & 0x20) != 0) +- signal_exp -= 0x40; +- +- if (signal_mant != 0) +- result = intlog10(2) * 10 * signal_exp + 10 * intlog10(signal_mant); +- else +- result = intlog10(2) * 10 * signal_exp - 100; +- +- if (noise_mant != 0) +- result -= intlog10(2) * 10 * noise_exp + 10 * intlog10(noise_mant); +- else +- result -= intlog10(2) * 10 * noise_exp - 100; +- +- *snr = result / ((1 << 24) / 10); +- return 0; +-} +- +-static int dib7000p_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void dib7000p_release(struct dvb_frontend *demod) +-{ +- struct dib7000p_state *st = demod->demodulator_priv; +- dibx000_exit_i2c_master(&st->i2c_master); +- i2c_del_adapter(&st->dib7090_tuner_adap); +- kfree(st); +-} +- +-int dib7000pc_detection(struct i2c_adapter *i2c_adap) +-{ +- u8 *tx, *rx; +- struct i2c_msg msg[2] = { +- {.addr = 18 >> 1, .flags = 0, .len = 2}, +- {.addr = 18 >> 1, .flags = I2C_M_RD, .len = 2}, +- }; +- int ret = 0; +- +- tx = kzalloc(2*sizeof(u8), GFP_KERNEL); +- if (!tx) +- return -ENOMEM; +- rx = kzalloc(2*sizeof(u8), GFP_KERNEL); +- if (!rx) { +- ret = -ENOMEM; +- goto rx_memory_error; +- } +- +- msg[0].buf = tx; +- msg[1].buf = rx; +- +- tx[0] = 0x03; +- tx[1] = 0x00; +- +- if (i2c_transfer(i2c_adap, msg, 2) == 2) +- if (rx[0] == 0x01 && rx[1] == 0xb3) { +- dprintk("-D- DiB7000PC detected"); +- return 1; +- } +- +- msg[0].addr = msg[1].addr = 0x40; +- +- if (i2c_transfer(i2c_adap, msg, 2) == 2) +- if (rx[0] == 0x01 && rx[1] == 0xb3) { +- dprintk("-D- DiB7000PC detected"); +- return 1; +- } +- +- dprintk("-D- DiB7000PC not detected"); +- +- kfree(rx); +-rx_memory_error: +- kfree(tx); +- return ret; +-} +-EXPORT_SYMBOL(dib7000pc_detection); +- +-struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +-{ +- struct dib7000p_state *st = demod->demodulator_priv; +- return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +-} +-EXPORT_SYMBOL(dib7000p_get_i2c_master); +- +-int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 val = dib7000p_read_word(state, 235) & 0xffef; +- val |= (onoff & 0x1) << 4; +- dprintk("PID filter enabled %d", onoff); +- return dib7000p_write_word(state, 235, val); +-} +-EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); +- +-int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); +- return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); +-} +-EXPORT_SYMBOL(dib7000p_pid_filter); +- +-int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +-{ +- struct dib7000p_state *dpst; +- int k = 0; +- u8 new_addr = 0; +- +- dpst = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); +- if (!dpst) +- return -ENOMEM; +- +- dpst->i2c_adap = i2c; +- mutex_init(&dpst->i2c_buffer_lock); +- +- for (k = no_of_demods - 1; k >= 0; k--) { +- dpst->cfg = cfg[k]; +- +- /* designated i2c address */ +- if (cfg[k].default_i2c_addr != 0) +- new_addr = cfg[k].default_i2c_addr + (k << 1); +- else +- new_addr = (0x40 + k) << 1; +- dpst->i2c_addr = new_addr; +- dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ +- if (dib7000p_identify(dpst) != 0) { +- dpst->i2c_addr = default_addr; +- dib7000p_write_word(dpst, 1287, 0x0003); /* sram lead in, rdy */ +- if (dib7000p_identify(dpst) != 0) { +- dprintk("DiB7000P #%d: not identified\n", k); +- kfree(dpst); +- return -EIO; +- } +- } +- +- /* start diversity to pull_down div_str - just for i2c-enumeration */ +- dib7000p_set_output_mode(dpst, OUTMODE_DIVERSITY); +- +- /* set new i2c address and force divstart */ +- dib7000p_write_word(dpst, 1285, (new_addr << 2) | 0x2); +- +- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); +- } +- +- for (k = 0; k < no_of_demods; k++) { +- dpst->cfg = cfg[k]; +- if (cfg[k].default_i2c_addr != 0) +- dpst->i2c_addr = (cfg[k].default_i2c_addr + k) << 1; +- else +- dpst->i2c_addr = (0x40 + k) << 1; +- +- // unforce divstr +- dib7000p_write_word(dpst, 1285, dpst->i2c_addr << 2); +- +- /* deactivate div - it was just for i2c-enumeration */ +- dib7000p_set_output_mode(dpst, OUTMODE_HIGH_Z); +- } +- +- kfree(dpst); +- return 0; +-} +-EXPORT_SYMBOL(dib7000p_i2c_enumeration); +- +-static const s32 lut_1000ln_mant[] = { +- 6908, 6956, 7003, 7047, 7090, 7131, 7170, 7208, 7244, 7279, 7313, 7346, 7377, 7408, 7438, 7467, 7495, 7523, 7549, 7575, 7600 +-}; +- +-static s32 dib7000p_get_adc_power(struct dvb_frontend *fe) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u32 tmp_val = 0, exp = 0, mant = 0; +- s32 pow_i; +- u16 buf[2]; +- u8 ix = 0; +- +- buf[0] = dib7000p_read_word(state, 0x184); +- buf[1] = dib7000p_read_word(state, 0x185); +- pow_i = (buf[0] << 16) | buf[1]; +- dprintk("raw pow_i = %d", pow_i); +- +- tmp_val = pow_i; +- while (tmp_val >>= 1) +- exp++; +- +- mant = (pow_i * 1000 / (1 << exp)); +- dprintk(" mant = %d exp = %d", mant / 1000, exp); +- +- ix = (u8) ((mant - 1000) / 100); /* index of the LUT */ +- dprintk(" ix = %d", ix); +- +- pow_i = (lut_1000ln_mant[ix] + 693 * (exp - 20) - 6908); +- pow_i = (pow_i << 8) / 1000; +- dprintk(" pow_i = %d", pow_i); +- +- return pow_i; +-} +- +-static int map_addr_to_serpar_number(struct i2c_msg *msg) +-{ +- if ((msg->buf[0] <= 15)) +- msg->buf[0] -= 1; +- else if (msg->buf[0] == 17) +- msg->buf[0] = 15; +- else if (msg->buf[0] == 16) +- msg->buf[0] = 17; +- else if (msg->buf[0] == 19) +- msg->buf[0] = 16; +- else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) +- msg->buf[0] -= 3; +- else if (msg->buf[0] == 28) +- msg->buf[0] = 23; +- else +- return -EINVAL; +- return 0; +-} +- +-static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); +- u8 n_overflow = 1; +- u16 i = 1000; +- u16 serpar_num = msg[0].buf[0]; +- +- while (n_overflow == 1 && i) { +- n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; +- i--; +- if (i == 0) +- dprintk("Tuner ITF: write busy (overflow)"); +- } +- dib7000p_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); +- dib7000p_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); +- +- return num; +-} +- +-static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); +- u8 n_overflow = 1, n_empty = 1; +- u16 i = 1000; +- u16 serpar_num = msg[0].buf[0]; +- u16 read_word; +- +- while (n_overflow == 1 && i) { +- n_overflow = (dib7000p_read_word(state, 1984) >> 1) & 0x1; +- i--; +- if (i == 0) +- dprintk("TunerITF: read busy (overflow)"); +- } +- dib7000p_write_word(state, 1985, (0 << 6) | (serpar_num & 0x3f)); +- +- i = 1000; +- while (n_empty == 1 && i) { +- n_empty = dib7000p_read_word(state, 1984) & 0x1; +- i--; +- if (i == 0) +- dprintk("TunerITF: read busy (empty)"); +- } +- read_word = dib7000p_read_word(state, 1987); +- msg[1].buf[0] = (read_word >> 8) & 0xff; +- msg[1].buf[1] = (read_word) & 0xff; +- +- return num; +-} +- +-static int w7090p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- if (map_addr_to_serpar_number(&msg[0]) == 0) { /* else = Tuner regs to ignore : DIG_CFG, CTRL_RF_LT, PLL_CFG, PWM1_REG, ADCCLK, DIG_CFG_3; SLEEP_EN... */ +- if (num == 1) { /* write */ +- return w7090p_tuner_write_serpar(i2c_adap, msg, 1); +- } else { /* read */ +- return w7090p_tuner_read_serpar(i2c_adap, msg, 2); +- } +- } +- return num; +-} +- +-static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num, u16 apb_address) +-{ +- struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); +- u16 word; +- +- if (num == 1) { /* write */ +- dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); +- } else { +- word = dib7000p_read_word(state, apb_address); +- msg[1].buf[0] = (word >> 8) & 0xff; +- msg[1].buf[1] = (word) & 0xff; +- } +- +- return num; +-} +- +-static int dib7090_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dib7000p_state *state = i2c_get_adapdata(i2c_adap); +- +- u16 apb_address = 0, word; +- int i = 0; +- switch (msg[0].buf[0]) { +- case 0x12: +- apb_address = 1920; +- break; +- case 0x14: +- apb_address = 1921; +- break; +- case 0x24: +- apb_address = 1922; +- break; +- case 0x1a: +- apb_address = 1923; +- break; +- case 0x22: +- apb_address = 1924; +- break; +- case 0x33: +- apb_address = 1926; +- break; +- case 0x34: +- apb_address = 1927; +- break; +- case 0x35: +- apb_address = 1928; +- break; +- case 0x36: +- apb_address = 1929; +- break; +- case 0x37: +- apb_address = 1930; +- break; +- case 0x38: +- apb_address = 1931; +- break; +- case 0x39: +- apb_address = 1932; +- break; +- case 0x2a: +- apb_address = 1935; +- break; +- case 0x2b: +- apb_address = 1936; +- break; +- case 0x2c: +- apb_address = 1937; +- break; +- case 0x2d: +- apb_address = 1938; +- break; +- case 0x2e: +- apb_address = 1939; +- break; +- case 0x2f: +- apb_address = 1940; +- break; +- case 0x30: +- apb_address = 1941; +- break; +- case 0x31: +- apb_address = 1942; +- break; +- case 0x32: +- apb_address = 1943; +- break; +- case 0x3e: +- apb_address = 1944; +- break; +- case 0x3f: +- apb_address = 1945; +- break; +- case 0x40: +- apb_address = 1948; +- break; +- case 0x25: +- apb_address = 914; +- break; +- case 0x26: +- apb_address = 915; +- break; +- case 0x27: +- apb_address = 917; +- break; +- case 0x28: +- apb_address = 916; +- break; +- case 0x1d: +- i = ((dib7000p_read_word(state, 72) >> 12) & 0x3); +- word = dib7000p_read_word(state, 384 + i); +- msg[1].buf[0] = (word >> 8) & 0xff; +- msg[1].buf[1] = (word) & 0xff; +- return num; +- case 0x1f: +- if (num == 1) { /* write */ +- word = (u16) ((msg[0].buf[1] << 8) | msg[0].buf[2]); +- word &= 0x3; +- word = (dib7000p_read_word(state, 72) & ~(3 << 12)) | (word << 12); +- dib7000p_write_word(state, 72, word); /* Set the proper input */ +- return num; +- } +- } +- +- if (apb_address != 0) /* R/W acces via APB */ +- return dib7090p_rw_on_apb(i2c_adap, msg, num, apb_address); +- else /* R/W access via SERPAR */ +- return w7090p_tuner_rw_serpar(i2c_adap, msg, num); +- +- return 0; +-} +- +-static u32 dib7000p_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dib7090_tuner_xfer_algo = { +- .master_xfer = dib7090_tuner_xfer, +- .functionality = dib7000p_i2c_func, +-}; +- +-struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +-{ +- struct dib7000p_state *st = fe->demodulator_priv; +- return &st->dib7090_tuner_adap; +-} +-EXPORT_SYMBOL(dib7090_get_i2c_tuner); +- +-static int dib7090_host_bus_drive(struct dib7000p_state *state, u8 drive) +-{ +- u16 reg; +- +- /* drive host bus 2, 3, 4 */ +- reg = dib7000p_read_word(state, 1798) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive << 12) | (drive << 6) | drive; +- dib7000p_write_word(state, 1798, reg); +- +- /* drive host bus 5,6 */ +- reg = dib7000p_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); +- reg |= (drive << 8) | (drive << 2); +- dib7000p_write_word(state, 1799, reg); +- +- /* drive host bus 7, 8, 9 */ +- reg = dib7000p_read_word(state, 1800) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive << 12) | (drive << 6) | drive; +- dib7000p_write_word(state, 1800, reg); +- +- /* drive host bus 10, 11 */ +- reg = dib7000p_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); +- reg |= (drive << 8) | (drive << 2); +- dib7000p_write_word(state, 1801, reg); +- +- /* drive host bus 12, 13, 14 */ +- reg = dib7000p_read_word(state, 1802) & ~((0x7) | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive << 12) | (drive << 6) | drive; +- dib7000p_write_word(state, 1802, reg); +- +- return 0; +-} +- +-static u32 dib7090_calcSyncFreq(u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 syncSize) +-{ +- u32 quantif = 3; +- u32 nom = (insertExtSynchro * P_Kin + syncSize); +- u32 denom = P_Kout; +- u32 syncFreq = ((nom << quantif) / denom); +- +- if ((syncFreq & ((1 << quantif) - 1)) != 0) +- syncFreq = (syncFreq >> quantif) + 1; +- else +- syncFreq = (syncFreq >> quantif); +- +- if (syncFreq != 0) +- syncFreq = syncFreq - 1; +- +- return syncFreq; +-} +- +-static int dib7090_cfg_DibTx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, u32 syncWord, u32 syncSize) +-{ +- dprintk("Configure DibStream Tx"); +- +- dib7000p_write_word(state, 1615, 1); +- dib7000p_write_word(state, 1603, P_Kin); +- dib7000p_write_word(state, 1605, P_Kout); +- dib7000p_write_word(state, 1606, insertExtSynchro); +- dib7000p_write_word(state, 1608, synchroMode); +- dib7000p_write_word(state, 1609, (syncWord >> 16) & 0xffff); +- dib7000p_write_word(state, 1610, syncWord & 0xffff); +- dib7000p_write_word(state, 1612, syncSize); +- dib7000p_write_word(state, 1615, 0); +- +- return 0; +-} +- +-static int dib7090_cfg_DibRx(struct dib7000p_state *state, u32 P_Kin, u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, u32 syncWord, u32 syncSize, +- u32 dataOutRate) +-{ +- u32 syncFreq; +- +- dprintk("Configure DibStream Rx"); +- if ((P_Kin != 0) && (P_Kout != 0)) { +- syncFreq = dib7090_calcSyncFreq(P_Kin, P_Kout, insertExtSynchro, syncSize); +- dib7000p_write_word(state, 1542, syncFreq); +- } +- dib7000p_write_word(state, 1554, 1); +- dib7000p_write_word(state, 1536, P_Kin); +- dib7000p_write_word(state, 1537, P_Kout); +- dib7000p_write_word(state, 1539, synchroMode); +- dib7000p_write_word(state, 1540, (syncWord >> 16) & 0xffff); +- dib7000p_write_word(state, 1541, syncWord & 0xffff); +- dib7000p_write_word(state, 1543, syncSize); +- dib7000p_write_word(state, 1544, dataOutRate); +- dib7000p_write_word(state, 1554, 0); +- +- return 0; +-} +- +-static void dib7090_enMpegMux(struct dib7000p_state *state, int onoff) +-{ +- u16 reg_1287 = dib7000p_read_word(state, 1287); +- +- switch (onoff) { +- case 1: +- reg_1287 &= ~(1<<7); +- break; +- case 0: +- reg_1287 |= (1<<7); +- break; +- } +- +- dib7000p_write_word(state, 1287, reg_1287); +-} +- +-static void dib7090_configMpegMux(struct dib7000p_state *state, +- u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) +-{ +- dprintk("Enable Mpeg mux"); +- +- dib7090_enMpegMux(state, 0); +- +- /* If the input mode is MPEG do not divide the serial clock */ +- if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) +- enSerialClkDiv2 = 0; +- +- dib7000p_write_word(state, 1287, ((pulseWidth & 0x1f) << 2) +- | ((enSerialMode & 0x1) << 1) +- | (enSerialClkDiv2 & 0x1)); +- +- dib7090_enMpegMux(state, 1); +-} +- +-static void dib7090_setDibTxMux(struct dib7000p_state *state, int mode) +-{ +- u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 7); +- +- switch (mode) { +- case MPEG_ON_DIBTX: +- dprintk("SET MPEG ON DIBSTREAM TX"); +- dib7090_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); +- reg_1288 |= (1<<9); +- break; +- case DIV_ON_DIBTX: +- dprintk("SET DIV_OUT ON DIBSTREAM TX"); +- dib7090_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); +- reg_1288 |= (1<<8); +- break; +- case ADC_ON_DIBTX: +- dprintk("SET ADC_OUT ON DIBSTREAM TX"); +- dib7090_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); +- reg_1288 |= (1<<7); +- break; +- default: +- break; +- } +- dib7000p_write_word(state, 1288, reg_1288); +-} +- +-static void dib7090_setHostBusMux(struct dib7000p_state *state, int mode) +-{ +- u16 reg_1288 = dib7000p_read_word(state, 1288) & ~(0x7 << 4); +- +- switch (mode) { +- case DEMOUT_ON_HOSTBUS: +- dprintk("SET DEM OUT OLD INTERF ON HOST BUS"); +- dib7090_enMpegMux(state, 0); +- reg_1288 |= (1<<6); +- break; +- case DIBTX_ON_HOSTBUS: +- dprintk("SET DIBSTREAM TX ON HOST BUS"); +- dib7090_enMpegMux(state, 0); +- reg_1288 |= (1<<5); +- break; +- case MPEG_ON_HOSTBUS: +- dprintk("SET MPEG MUX ON HOST BUS"); +- reg_1288 |= (1<<4); +- break; +- default: +- break; +- } +- dib7000p_write_word(state, 1288, reg_1288); +-} +- +-int dib7090_set_diversity_in(struct dvb_frontend *fe, int onoff) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 reg_1287; +- +- switch (onoff) { +- case 0: /* only use the internal way - not the diversity input */ +- dprintk("%s mode OFF : by default Enable Mpeg INPUT", __func__); +- dib7090_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); +- +- /* Do not divide the serial clock of MPEG MUX */ +- /* in SERIAL MODE in case input mode MPEG is used */ +- reg_1287 = dib7000p_read_word(state, 1287); +- /* enSerialClkDiv2 == 1 ? */ +- if ((reg_1287 & 0x1) == 1) { +- /* force enSerialClkDiv2 = 0 */ +- reg_1287 &= ~0x1; +- dib7000p_write_word(state, 1287, reg_1287); +- } +- state->input_mode_mpeg = 1; +- break; +- case 1: /* both ways */ +- case 2: /* only the diversity input */ +- dprintk("%s ON : Enable diversity INPUT", __func__); +- dib7090_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); +- state->input_mode_mpeg = 0; +- break; +- } +- +- dib7000p_set_diversity_in(&state->demod, onoff); +- return 0; +-} +- +-static int dib7090_set_output_mode(struct dvb_frontend *fe, int mode) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- +- u16 outreg, smo_mode, fifo_threshold; +- u8 prefer_mpeg_mux_use = 1; +- int ret = 0; +- +- dib7090_host_bus_drive(state, 1); +- +- fifo_threshold = 1792; +- smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); +- outreg = dib7000p_read_word(state, 1286) & ~((1 << 10) | (0x7 << 6) | (1 << 1)); +- +- switch (mode) { +- case OUTMODE_HIGH_Z: +- outreg = 0; +- break; +- +- case OUTMODE_MPEG2_SERIAL: +- if (prefer_mpeg_mux_use) { +- dprintk("setting output mode TS_SERIAL using Mpeg Mux"); +- dib7090_configMpegMux(state, 3, 1, 1); +- dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); +- } else {/* Use Smooth block */ +- dprintk("setting output mode TS_SERIAL using Smooth bloc"); +- dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (2<<6) | (0 << 1); +- } +- break; +- +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- if (prefer_mpeg_mux_use) { +- dprintk("setting output mode TS_PARALLEL_GATED using Mpeg Mux"); +- dib7090_configMpegMux(state, 2, 0, 0); +- dib7090_setHostBusMux(state, MPEG_ON_HOSTBUS); +- } else { /* Use Smooth block */ +- dprintk("setting output mode TS_PARALLEL_GATED using Smooth block"); +- dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (0<<6); +- } +- break; +- +- case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ +- dprintk("setting output mode TS_PARALLEL_CONT using Smooth block"); +- dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (1<<6); +- break; +- +- case OUTMODE_MPEG2_FIFO: /* Using Smooth block because not supported by new Mpeg Mux bloc */ +- dprintk("setting output mode TS_FIFO using Smooth block"); +- dib7090_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (5<<6); +- smo_mode |= (3 << 1); +- fifo_threshold = 512; +- break; +- +- case OUTMODE_DIVERSITY: +- dprintk("setting output mode MODE_DIVERSITY"); +- dib7090_setDibTxMux(state, DIV_ON_DIBTX); +- dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- break; +- +- case OUTMODE_ANALOG_ADC: +- dprintk("setting output mode MODE_ANALOG_ADC"); +- dib7090_setDibTxMux(state, ADC_ON_DIBTX); +- dib7090_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- break; +- } +- if (mode != OUTMODE_HIGH_Z) +- outreg |= (1 << 10); +- +- if (state->cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5); +- +- ret |= dib7000p_write_word(state, 235, smo_mode); +- ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ +- ret |= dib7000p_write_word(state, 1286, outreg); +- +- return ret; +-} +- +-int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 en_cur_state; +- +- dprintk("sleep dib7090: %d", onoff); +- +- en_cur_state = dib7000p_read_word(state, 1922); +- +- if (en_cur_state > 0xff) +- state->tuner_enable = en_cur_state; +- +- if (onoff) +- en_cur_state &= 0x00ff; +- else { +- if (state->tuner_enable != 0) +- en_cur_state = state->tuner_enable; +- } +- +- dib7000p_write_word(state, 1922, en_cur_state); +- +- return 0; +-} +-EXPORT_SYMBOL(dib7090_tuner_sleep); +- +-int dib7090_get_adc_power(struct dvb_frontend *fe) +-{ +- return dib7000p_get_adc_power(fe); +-} +-EXPORT_SYMBOL(dib7090_get_adc_power); +- +-int dib7090_slave_reset(struct dvb_frontend *fe) +-{ +- struct dib7000p_state *state = fe->demodulator_priv; +- u16 reg; +- +- reg = dib7000p_read_word(state, 1794); +- dib7000p_write_word(state, 1794, reg | (4 << 12)); +- +- dib7000p_write_word(state, 1032, 0xffff); +- return 0; +-} +-EXPORT_SYMBOL(dib7090_slave_reset); +- +-static struct dvb_frontend_ops dib7000p_ops; +-struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +-{ +- struct dvb_frontend *demod; +- struct dib7000p_state *st; +- st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- +- memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config)); +- st->i2c_adap = i2c_adap; +- st->i2c_addr = i2c_addr; +- st->gpio_val = cfg->gpio_val; +- st->gpio_dir = cfg->gpio_dir; +- +- /* Ensure the output mode remains at the previous default if it's +- * not specifically set by the caller. +- */ +- if ((st->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) +- st->cfg.output_mode = OUTMODE_MPEG2_FIFO; +- +- demod = &st->demod; +- demod->demodulator_priv = st; +- memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); +- mutex_init(&st->i2c_buffer_lock); +- +- dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ +- +- if (dib7000p_identify(st) != 0) +- goto error; +- +- st->version = dib7000p_read_word(st, 897); +- +- /* FIXME: make sure the dev.parent field is initialized, or else +- request_firmware() will hit an OOPS (this should be moved somewhere +- more common) */ +- st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; +- +- /* FIXME: make sure the dev.parent field is initialized, or else +- request_firmware() will hit an OOPS (this should be moved somewhere +- more common) */ +- st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; +- +- dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); +- +- /* init 7090 tuner adapter */ +- strncpy(st->dib7090_tuner_adap.name, "DiB7090 tuner interface", sizeof(st->dib7090_tuner_adap.name)); +- st->dib7090_tuner_adap.algo = &dib7090_tuner_xfer_algo; +- st->dib7090_tuner_adap.algo_data = NULL; +- st->dib7090_tuner_adap.dev.parent = st->i2c_adap->dev.parent; +- i2c_set_adapdata(&st->dib7090_tuner_adap, st); +- i2c_add_adapter(&st->dib7090_tuner_adap); +- +- dib7000p_demod_reset(st); +- +- if (st->version == SOC7090) { +- dib7090_set_output_mode(demod, st->cfg.output_mode); +- dib7090_set_diversity_in(demod, 0); +- } +- +- return demod; +- +-error: +- kfree(st); +- return NULL; +-} +-EXPORT_SYMBOL(dib7000p_attach); +- +-static struct dvb_frontend_ops dib7000p_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DiBcom 7000PC", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib7000p_release, +- +- .init = dib7000p_wakeup, +- .sleep = dib7000p_sleep, +- +- .set_frontend = dib7000p_set_frontend, +- .get_tune_settings = dib7000p_fe_get_tune_settings, +- .get_frontend = dib7000p_get_frontend, +- +- .read_status = dib7000p_read_status, +- .read_ber = dib7000p_read_ber, +- .read_signal_strength = dib7000p_read_signal_strength, +- .read_snr = dib7000p_read_snr, +- .read_ucblocks = dib7000p_read_unc_blocks, +-}; +- +-MODULE_AUTHOR("Olivier Grenie "); +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h +deleted file mode 100644 +index b61b03a..0000000 +--- a/drivers/media/dvb/frontends/dib7000p.h ++++ /dev/null +@@ -1,158 +0,0 @@ +-#ifndef DIB7000P_H +-#define DIB7000P_H +- +-#include "dibx000_common.h" +- +-struct dib7000p_config { +- u8 output_mpeg2_in_188_bytes; +- u8 hostbus_diversity; +- u8 tuner_is_baseband; +- int (*update_lna) (struct dvb_frontend *, u16 agc_global); +- +- u8 agc_config_count; +- struct dibx000_agc_config *agc; +- struct dibx000_bandwidth_config *bw; +- +-#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff +- u16 gpio_dir; +-#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000 +- u16 gpio_val; +-#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +-#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +-#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +-#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf) +-#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff +- u16 gpio_pwm_pos; +- +- u16 pwm_freq_div; +- +- u8 quartz_direct; +- +- u8 spur_protect; +- +- int (*agc_control) (struct dvb_frontend *, u8 before); +- +- u8 output_mode; +- u8 disable_sample_and_hold:1; +- +- u8 enable_current_mirror:1; +- u16 diversity_delay; +- +- u8 default_i2c_addr; +- u8 enMpegOutput:1; +-}; +- +-#define DEFAULT_DIB7000P_I2C_ADDRESS 18 +- +-#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \ +- defined(MODULE)) +-extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +-extern struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); +-extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]); +-extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); +-extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); +-extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); +-extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +-extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +-extern int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw); +-extern u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf); +-extern int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff); +-extern int dib7090_get_adc_power(struct dvb_frontend *fe); +-extern struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe); +-extern int dib7090_slave_reset(struct dvb_frontend *fe); +-extern int dib7000p_get_agc_values(struct dvb_frontend *fe, +- u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd); +-#else +-static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_update_pll(struct dvb_frontend *fe, struct dibx000_bandwidth_config *bw) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline u32 dib7000p_ctrl_timf(struct dvb_frontend *fe, u8 op, u32 timf) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +- +-static inline int dib7090_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7090_get_adc_power(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline struct i2c_adapter *dib7090_get_i2c_tuner(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib7090_slave_reset(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib7000p_get_agc_values(struct dvb_frontend *fe, +- u16 *agc_global, u16 *agc1, u16 *agc2, u16 *wbd) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c +deleted file mode 100644 +index 9ca34f4..0000000 +--- a/drivers/media/dvb/frontends/dib8000.c ++++ /dev/null +@@ -1,3558 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB8000 chip (ISDB-T). +- * +- * Copyright (C) 2009 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#include +-#include +-#include +-#include +- +-#include "dvb_math.h" +- +-#include "dvb_frontend.h" +- +-#include "dib8000.h" +- +-#define LAYER_ALL -1 +-#define LAYER_A 1 +-#define LAYER_B 2 +-#define LAYER_C 3 +- +-#define FE_CALLBACK_TIME_NEVER 0xffffffff +-#define MAX_NUMBER_OF_FRONTENDS 6 +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB8000: "); printk(args); printk("\n"); } } while (0) +- +-#define FE_STATUS_TUNE_FAILED 0 +- +-struct i2c_device { +- struct i2c_adapter *adap; +- u8 addr; +- u8 *i2c_write_buffer; +- u8 *i2c_read_buffer; +- struct mutex *i2c_buffer_lock; +-}; +- +-struct dib8000_state { +- struct dib8000_config cfg; +- +- struct i2c_device i2c; +- +- struct dibx000_i2c_master i2c_master; +- +- u16 wbd_ref; +- +- u8 current_band; +- u32 current_bandwidth; +- struct dibx000_agc_config *current_agc; +- u32 timf; +- u32 timf_default; +- +- u8 div_force_off:1; +- u8 div_state:1; +- u16 div_sync_wait; +- +- u8 agc_state; +- u8 differential_constellation; +- u8 diversity_onoff; +- +- s16 ber_monitored_layer; +- u16 gpio_dir; +- u16 gpio_val; +- +- u16 revision; +- u8 isdbt_cfg_loaded; +- enum frontend_tune_state tune_state; +- u32 status; +- +- struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[4]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +- u8 input_mode_mpeg; +- +- u16 tuner_enable; +- struct i2c_adapter dib8096p_tuner_adap; +-}; +- +-enum dib8000_power_mode { +- DIB8000_POWER_ALL = 0, +- DIB8000_POWER_INTERFACE_ONLY, +-}; +- +-static u16 dib8000_i2c_read16(struct i2c_device *i2c, u16 reg) +-{ +- u16 ret; +- struct i2c_msg msg[2] = { +- {.addr = i2c->addr >> 1, .flags = 0, .len = 2}, +- {.addr = i2c->addr >> 1, .flags = I2C_M_RD, .len = 2}, +- }; +- +- if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- msg[0].buf = i2c->i2c_write_buffer; +- msg[0].buf[0] = reg >> 8; +- msg[0].buf[1] = reg & 0xff; +- msg[1].buf = i2c->i2c_read_buffer; +- +- if (i2c_transfer(i2c->adap, msg, 2) != 2) +- dprintk("i2c read error on %d", reg); +- +- ret = (msg[1].buf[0] << 8) | msg[1].buf[1]; +- mutex_unlock(i2c->i2c_buffer_lock); +- return ret; +-} +- +-static u16 dib8000_read_word(struct dib8000_state *state, u16 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- state->i2c_write_buffer[0] = reg >> 8; +- state->i2c_write_buffer[1] = reg & 0xff; +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c.addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 2; +- state->msg[1].addr = state->i2c.addr >> 1; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = state->i2c_read_buffer; +- state->msg[1].len = 2; +- +- if (i2c_transfer(state->i2c.adap, state->msg, 2) != 2) +- dprintk("i2c read error on %d", reg); +- +- ret = (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +- mutex_unlock(&state->i2c_buffer_lock); +- +- return ret; +-} +- +-static u32 dib8000_read32(struct dib8000_state *state, u16 reg) +-{ +- u16 rw[2]; +- +- rw[0] = dib8000_read_word(state, reg + 0); +- rw[1] = dib8000_read_word(state, reg + 1); +- +- return ((rw[0] << 16) | (rw[1])); +-} +- +-static int dib8000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +-{ +- struct i2c_msg msg = {.addr = i2c->addr >> 1, .flags = 0, .len = 4}; +- int ret = 0; +- +- if (mutex_lock_interruptible(i2c->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- msg.buf = i2c->i2c_write_buffer; +- msg.buf[0] = (reg >> 8) & 0xff; +- msg.buf[1] = reg & 0xff; +- msg.buf[2] = (val >> 8) & 0xff; +- msg.buf[3] = val & 0xff; +- +- ret = i2c_transfer(i2c->adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +- mutex_unlock(i2c->i2c_buffer_lock); +- +- return ret; +-} +- +-static int dib8000_write_word(struct dib8000_state *state, u16 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&state->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- state->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- state->i2c_write_buffer[1] = reg & 0xff; +- state->i2c_write_buffer[2] = (val >> 8) & 0xff; +- state->i2c_write_buffer[3] = val & 0xff; +- +- memset(&state->msg[0], 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c.addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 4; +- +- ret = (i2c_transfer(state->i2c.adap, state->msg, 1) != 1 ? +- -EREMOTEIO : 0); +- mutex_unlock(&state->i2c_buffer_lock); +- +- return ret; +-} +- +-static const s16 coeff_2k_sb_1seg_dqpsk[8] = { +- (769 << 5) | 0x0a, (745 << 5) | 0x03, (595 << 5) | 0x0d, (769 << 5) | 0x0a, (920 << 5) | 0x09, (784 << 5) | 0x02, (519 << 5) | 0x0c, +- (920 << 5) | 0x09 +-}; +- +-static const s16 coeff_2k_sb_1seg[8] = { +- (692 << 5) | 0x0b, (683 << 5) | 0x01, (519 << 5) | 0x09, (692 << 5) | 0x0b, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f, 0 | 0x1f +-}; +- +-static const s16 coeff_2k_sb_3seg_0dqpsk_1dqpsk[8] = { +- (832 << 5) | 0x10, (912 << 5) | 0x05, (900 << 5) | 0x12, (832 << 5) | 0x10, (-931 << 5) | 0x0f, (912 << 5) | 0x04, (807 << 5) | 0x11, +- (-931 << 5) | 0x0f +-}; +- +-static const s16 coeff_2k_sb_3seg_0dqpsk[8] = { +- (622 << 5) | 0x0c, (941 << 5) | 0x04, (796 << 5) | 0x10, (622 << 5) | 0x0c, (982 << 5) | 0x0c, (519 << 5) | 0x02, (572 << 5) | 0x0e, +- (982 << 5) | 0x0c +-}; +- +-static const s16 coeff_2k_sb_3seg_1dqpsk[8] = { +- (699 << 5) | 0x14, (607 << 5) | 0x04, (944 << 5) | 0x13, (699 << 5) | 0x14, (-720 << 5) | 0x0d, (640 << 5) | 0x03, (866 << 5) | 0x12, +- (-720 << 5) | 0x0d +-}; +- +-static const s16 coeff_2k_sb_3seg[8] = { +- (664 << 5) | 0x0c, (925 << 5) | 0x03, (937 << 5) | 0x10, (664 << 5) | 0x0c, (-610 << 5) | 0x0a, (697 << 5) | 0x01, (836 << 5) | 0x0e, +- (-610 << 5) | 0x0a +-}; +- +-static const s16 coeff_4k_sb_1seg_dqpsk[8] = { +- (-955 << 5) | 0x0e, (687 << 5) | 0x04, (818 << 5) | 0x10, (-955 << 5) | 0x0e, (-922 << 5) | 0x0d, (750 << 5) | 0x03, (665 << 5) | 0x0f, +- (-922 << 5) | 0x0d +-}; +- +-static const s16 coeff_4k_sb_1seg[8] = { +- (638 << 5) | 0x0d, (683 << 5) | 0x02, (638 << 5) | 0x0d, (638 << 5) | 0x0d, (-655 << 5) | 0x0a, (517 << 5) | 0x00, (698 << 5) | 0x0d, +- (-655 << 5) | 0x0a +-}; +- +-static const s16 coeff_4k_sb_3seg_0dqpsk_1dqpsk[8] = { +- (-707 << 5) | 0x14, (910 << 5) | 0x06, (889 << 5) | 0x16, (-707 << 5) | 0x14, (-958 << 5) | 0x13, (993 << 5) | 0x05, (523 << 5) | 0x14, +- (-958 << 5) | 0x13 +-}; +- +-static const s16 coeff_4k_sb_3seg_0dqpsk[8] = { +- (-723 << 5) | 0x13, (910 << 5) | 0x05, (777 << 5) | 0x14, (-723 << 5) | 0x13, (-568 << 5) | 0x0f, (547 << 5) | 0x03, (696 << 5) | 0x12, +- (-568 << 5) | 0x0f +-}; +- +-static const s16 coeff_4k_sb_3seg_1dqpsk[8] = { +- (-940 << 5) | 0x15, (607 << 5) | 0x05, (915 << 5) | 0x16, (-940 << 5) | 0x15, (-848 << 5) | 0x13, (683 << 5) | 0x04, (543 << 5) | 0x14, +- (-848 << 5) | 0x13 +-}; +- +-static const s16 coeff_4k_sb_3seg[8] = { +- (612 << 5) | 0x12, (910 << 5) | 0x04, (864 << 5) | 0x14, (612 << 5) | 0x12, (-869 << 5) | 0x13, (683 << 5) | 0x02, (869 << 5) | 0x12, +- (-869 << 5) | 0x13 +-}; +- +-static const s16 coeff_8k_sb_1seg_dqpsk[8] = { +- (-835 << 5) | 0x12, (684 << 5) | 0x05, (735 << 5) | 0x14, (-835 << 5) | 0x12, (-598 << 5) | 0x10, (781 << 5) | 0x04, (739 << 5) | 0x13, +- (-598 << 5) | 0x10 +-}; +- +-static const s16 coeff_8k_sb_1seg[8] = { +- (673 << 5) | 0x0f, (683 << 5) | 0x03, (808 << 5) | 0x12, (673 << 5) | 0x0f, (585 << 5) | 0x0f, (512 << 5) | 0x01, (780 << 5) | 0x0f, +- (585 << 5) | 0x0f +-}; +- +-static const s16 coeff_8k_sb_3seg_0dqpsk_1dqpsk[8] = { +- (863 << 5) | 0x17, (930 << 5) | 0x07, (878 << 5) | 0x19, (863 << 5) | 0x17, (0 << 5) | 0x14, (521 << 5) | 0x05, (980 << 5) | 0x18, +- (0 << 5) | 0x14 +-}; +- +-static const s16 coeff_8k_sb_3seg_0dqpsk[8] = { +- (-924 << 5) | 0x17, (910 << 5) | 0x06, (774 << 5) | 0x17, (-924 << 5) | 0x17, (-877 << 5) | 0x15, (565 << 5) | 0x04, (553 << 5) | 0x15, +- (-877 << 5) | 0x15 +-}; +- +-static const s16 coeff_8k_sb_3seg_1dqpsk[8] = { +- (-921 << 5) | 0x19, (607 << 5) | 0x06, (881 << 5) | 0x19, (-921 << 5) | 0x19, (-921 << 5) | 0x14, (713 << 5) | 0x05, (1018 << 5) | 0x18, +- (-921 << 5) | 0x14 +-}; +- +-static const s16 coeff_8k_sb_3seg[8] = { +- (514 << 5) | 0x14, (910 << 5) | 0x05, (861 << 5) | 0x17, (514 << 5) | 0x14, (690 << 5) | 0x14, (683 << 5) | 0x03, (662 << 5) | 0x15, +- (690 << 5) | 0x14 +-}; +- +-static const s16 ana_fe_coeff_3seg[24] = { +- 81, 80, 78, 74, 68, 61, 54, 45, 37, 28, 19, 11, 4, 1022, 1017, 1013, 1010, 1008, 1008, 1008, 1008, 1010, 1014, 1017 +-}; +- +-static const s16 ana_fe_coeff_1seg[24] = { +- 249, 226, 164, 82, 5, 981, 970, 988, 1018, 20, 31, 26, 8, 1012, 1000, 1018, 1012, 8, 15, 14, 9, 3, 1017, 1003 +-}; +- +-static const s16 ana_fe_coeff_13seg[24] = { +- 396, 305, 105, -51, -77, -12, 41, 31, -11, -30, -11, 14, 15, -2, -13, -7, 5, 8, 1, -6, -7, -3, 0, 1 +-}; +- +-static u16 fft_to_mode(struct dib8000_state *state) +-{ +- u16 mode; +- switch (state->fe[0]->dtv_property_cache.transmission_mode) { +- case TRANSMISSION_MODE_2K: +- mode = 1; +- break; +- case TRANSMISSION_MODE_4K: +- mode = 2; +- break; +- default: +- case TRANSMISSION_MODE_AUTO: +- case TRANSMISSION_MODE_8K: +- mode = 3; +- break; +- } +- return mode; +-} +- +-static void dib8000_set_acquisition_mode(struct dib8000_state *state) +-{ +- u16 nud = dib8000_read_word(state, 298); +- nud |= (1 << 3) | (1 << 0); +- dprintk("acquisition mode activated"); +- dib8000_write_word(state, 298, nud); +-} +-static int dib8000_set_output_mode(struct dvb_frontend *fe, int mode) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- u16 outreg, fifo_threshold, smo_mode, sram = 0x0205; /* by default SDRAM deintlv is enabled */ +- +- outreg = 0; +- fifo_threshold = 1792; +- smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); +- +- dprintk("-I- Setting output mode for demod %p to %d", +- &state->fe[0], mode); +- +- switch (mode) { +- case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock +- outreg = (1 << 10); /* 0x0400 */ +- break; +- case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock +- outreg = (1 << 10) | (1 << 6); /* 0x0440 */ +- break; +- case OUTMODE_MPEG2_SERIAL: // STBs with serial input +- outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ +- break; +- case OUTMODE_DIVERSITY: +- if (state->cfg.hostbus_diversity) { +- outreg = (1 << 10) | (4 << 6); /* 0x0500 */ +- sram &= 0xfdff; +- } else +- sram |= 0x0c00; +- break; +- case OUTMODE_MPEG2_FIFO: // e.g. USB feeding +- smo_mode |= (3 << 1); +- fifo_threshold = 512; +- outreg = (1 << 10) | (5 << 6); +- break; +- case OUTMODE_HIGH_Z: // disable +- outreg = 0; +- break; +- +- case OUTMODE_ANALOG_ADC: +- outreg = (1 << 10) | (3 << 6); +- dib8000_set_acquisition_mode(state); +- break; +- +- default: +- dprintk("Unhandled output_mode passed to be set for demod %p", +- &state->fe[0]); +- return -EINVAL; +- } +- +- if (state->cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5); +- +- dib8000_write_word(state, 299, smo_mode); +- dib8000_write_word(state, 300, fifo_threshold); /* synchronous fread */ +- dib8000_write_word(state, 1286, outreg); +- dib8000_write_word(state, 1291, sram); +- +- return 0; +-} +- +-static int dib8000_set_diversity_in(struct dvb_frontend *fe, int onoff) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 sync_wait = dib8000_read_word(state, 273) & 0xfff0; +- +- if (!state->differential_constellation) { +- dib8000_write_word(state, 272, 1 << 9); //dvsy_off_lmod4 = 1 +- dib8000_write_word(state, 273, sync_wait | (1 << 2) | 2); // sync_enable = 1; comb_mode = 2 +- } else { +- dib8000_write_word(state, 272, 0); //dvsy_off_lmod4 = 0 +- dib8000_write_word(state, 273, sync_wait); // sync_enable = 0; comb_mode = 0 +- } +- state->diversity_onoff = onoff; +- +- switch (onoff) { +- case 0: /* only use the internal way - not the diversity input */ +- dib8000_write_word(state, 270, 1); +- dib8000_write_word(state, 271, 0); +- break; +- case 1: /* both ways */ +- dib8000_write_word(state, 270, 6); +- dib8000_write_word(state, 271, 6); +- break; +- case 2: /* only the diversity input */ +- dib8000_write_word(state, 270, 0); +- dib8000_write_word(state, 271, 1); +- break; +- } +- return 0; +-} +- +-static void dib8000_set_power_mode(struct dib8000_state *state, enum dib8000_power_mode mode) +-{ +- /* by default everything is going to be powered off */ +- u16 reg_774 = 0x3fff, reg_775 = 0xffff, reg_776 = 0xffff, +- reg_900 = (dib8000_read_word(state, 900) & 0xfffc) | 0x3, +- reg_1280; +- +- if (state->revision != 0x8090) +- reg_1280 = (dib8000_read_word(state, 1280) & 0x00ff) | 0xff00; +- else +- reg_1280 = (dib8000_read_word(state, 1280) & 0x707f) | 0x8f80; +- +- /* now, depending on the requested mode, we power on */ +- switch (mode) { +- /* power up everything in the demod */ +- case DIB8000_POWER_ALL: +- reg_774 = 0x0000; +- reg_775 = 0x0000; +- reg_776 = 0x0000; +- reg_900 &= 0xfffc; +- if (state->revision != 0x8090) +- reg_1280 &= 0x00ff; +- else +- reg_1280 &= 0x707f; +- break; +- case DIB8000_POWER_INTERFACE_ONLY: +- if (state->revision != 0x8090) +- reg_1280 &= 0x00ff; +- else +- reg_1280 &= 0xfa7b; +- break; +- } +- +- dprintk("powermode : 774 : %x ; 775 : %x; 776 : %x ; 900 : %x; 1280 : %x", reg_774, reg_775, reg_776, reg_900, reg_1280); +- dib8000_write_word(state, 774, reg_774); +- dib8000_write_word(state, 775, reg_775); +- dib8000_write_word(state, 776, reg_776); +- dib8000_write_word(state, 900, reg_900); +- dib8000_write_word(state, 1280, reg_1280); +-} +- +-static int dib8000_init_sdram(struct dib8000_state *state) +-{ +- u16 reg = 0; +- dprintk("Init sdram"); +- +- reg = dib8000_read_word(state, 274)&0xfff0; +- /* P_dintlv_delay_ram = 7 because of MobileSdram */ +- dib8000_write_word(state, 274, reg | 0x7); +- +- dib8000_write_word(state, 1803, (7<<2)); +- +- reg = dib8000_read_word(state, 1280); +- /* force restart P_restart_sdram */ +- dib8000_write_word(state, 1280, reg | (1<<2)); +- +- /* release restart P_restart_sdram */ +- dib8000_write_word(state, 1280, reg); +- +- return 0; +-} +- +-static int dib8000_set_adc_state(struct dib8000_state *state, enum dibx000_adc_states no) +-{ +- int ret = 0; +- u16 reg, reg_907 = dib8000_read_word(state, 907); +- u16 reg_908 = dib8000_read_word(state, 908); +- +- switch (no) { +- case DIBX000_SLOW_ADC_ON: +- if (state->revision != 0x8090) { +- reg_908 |= (1 << 1) | (1 << 0); +- ret |= dib8000_write_word(state, 908, reg_908); +- reg_908 &= ~(1 << 1); +- } else { +- reg = dib8000_read_word(state, 1925); +- /* en_slowAdc = 1 & reset_sladc = 1 */ +- dib8000_write_word(state, 1925, reg | +- (1<<4) | (1<<2)); +- +- /* read acces to make it works... strange ... */ +- reg = dib8000_read_word(state, 1925); +- msleep(20); +- /* en_slowAdc = 1 & reset_sladc = 0 */ +- dib8000_write_word(state, 1925, reg & ~(1<<4)); +- +- reg = dib8000_read_word(state, 921) & ~((0x3 << 14) +- | (0x3 << 12)); +- /* ref = Vin1 => Vbg ; sel = Vin0 or Vin3 ; +- (Vin2 = Vcm) */ +- dib8000_write_word(state, 921, reg | (1 << 14) +- | (3 << 12)); +- } +- break; +- +- case DIBX000_SLOW_ADC_OFF: +- if (state->revision == 0x8090) { +- reg = dib8000_read_word(state, 1925); +- /* reset_sladc = 1 en_slowAdc = 0 */ +- dib8000_write_word(state, 1925, +- (reg & ~(1<<2)) | (1<<4)); +- } +- reg_908 |= (1 << 1) | (1 << 0); +- break; +- +- case DIBX000_ADC_ON: +- reg_907 &= 0x0fff; +- reg_908 &= 0x0003; +- break; +- +- case DIBX000_ADC_OFF: // leave the VBG voltage on +- reg_907 |= (1 << 14) | (1 << 13) | (1 << 12); +- reg_908 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); +- break; +- +- case DIBX000_VBG_ENABLE: +- reg_907 &= ~(1 << 15); +- break; +- +- case DIBX000_VBG_DISABLE: +- reg_907 |= (1 << 15); +- break; +- +- default: +- break; +- } +- +- ret |= dib8000_write_word(state, 907, reg_907); +- ret |= dib8000_write_word(state, 908, reg_908); +- +- return ret; +-} +- +-static int dib8000_set_bandwidth(struct dvb_frontend *fe, u32 bw) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u32 timf; +- +- if (bw == 0) +- bw = 6000; +- +- if (state->timf == 0) { +- dprintk("using default timf"); +- timf = state->timf_default; +- } else { +- dprintk("using updated timf"); +- timf = state->timf; +- } +- +- dib8000_write_word(state, 29, (u16) ((timf >> 16) & 0xffff)); +- dib8000_write_word(state, 30, (u16) ((timf) & 0xffff)); +- +- return 0; +-} +- +-static int dib8000_sad_calib(struct dib8000_state *state) +-{ +- if (state->revision == 0x8090) { +- dprintk("%s: the sad calibration is not needed for the dib8096P", +- __func__); +- return 0; +- } +- /* internal */ +- dib8000_write_word(state, 923, (0 << 1) | (0 << 0)); +- dib8000_write_word(state, 924, 776); // 0.625*3.3 / 4096 +- +- /* do the calibration */ +- dib8000_write_word(state, 923, (1 << 0)); +- dib8000_write_word(state, 923, (0 << 0)); +- +- msleep(1); +- return 0; +-} +- +-int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- if (value > 4095) +- value = 4095; +- state->wbd_ref = value; +- return dib8000_write_word(state, 106, value); +-} +- +-EXPORT_SYMBOL(dib8000_set_wbd_ref); +-static void dib8000_reset_pll_common(struct dib8000_state *state, const struct dibx000_bandwidth_config *bw) +-{ +- dprintk("ifreq: %d %x, inversion: %d", bw->ifreq, bw->ifreq, bw->ifreq >> 25); +- if (state->revision != 0x8090) { +- dib8000_write_word(state, 23, +- (u16) (((bw->internal * 1000) >> 16) & 0xffff)); +- dib8000_write_word(state, 24, +- (u16) ((bw->internal * 1000) & 0xffff)); +- } else { +- dib8000_write_word(state, 23, (u16) (((bw->internal / 2 * 1000) >> 16) & 0xffff)); +- dib8000_write_word(state, 24, +- (u16) ((bw->internal / 2 * 1000) & 0xffff)); +- } +- dib8000_write_word(state, 27, (u16) ((bw->ifreq >> 16) & 0x01ff)); +- dib8000_write_word(state, 28, (u16) (bw->ifreq & 0xffff)); +- dib8000_write_word(state, 26, (u16) ((bw->ifreq >> 25) & 0x0003)); +- +- if (state->revision != 0x8090) +- dib8000_write_word(state, 922, bw->sad_cfg); +-} +- +-static void dib8000_reset_pll(struct dib8000_state *state) +-{ +- const struct dibx000_bandwidth_config *pll = state->cfg.pll; +- u16 clk_cfg1, reg; +- +- if (state->revision != 0x8090) { +- dib8000_write_word(state, 901, +- (pll->pll_prediv << 8) | (pll->pll_ratio << 0)); +- +- clk_cfg1 = (1 << 10) | (0 << 9) | (pll->IO_CLK_en_core << 8) | +- (pll->bypclk_div << 5) | (pll->enable_refdiv << 4) | +- (1 << 3) | (pll->pll_range << 1) | +- (pll->pll_reset << 0); +- +- dib8000_write_word(state, 902, clk_cfg1); +- clk_cfg1 = (clk_cfg1 & 0xfff7) | (pll->pll_bypass << 3); +- dib8000_write_word(state, 902, clk_cfg1); +- +- dprintk("clk_cfg1: 0x%04x", clk_cfg1); +- +- /* smpl_cfg: P_refclksel=2, P_ensmplsel=1 nodivsmpl=1 */ +- if (state->cfg.pll->ADClkSrc == 0) +- dib8000_write_word(state, 904, +- (0 << 15) | (0 << 12) | (0 << 10) | +- (pll->modulo << 8) | +- (pll->ADClkSrc << 7) | (0 << 1)); +- else if (state->cfg.refclksel != 0) +- dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | +- ((state->cfg.refclksel & 0x3) << 10) | +- (pll->modulo << 8) | +- (pll->ADClkSrc << 7) | (0 << 1)); +- else +- dib8000_write_word(state, 904, (0 << 15) | (1 << 12) | +- (3 << 10) | (pll->modulo << 8) | +- (pll->ADClkSrc << 7) | (0 << 1)); +- } else { +- dib8000_write_word(state, 1856, (!pll->pll_reset<<13) | +- (pll->pll_range<<12) | (pll->pll_ratio<<6) | +- (pll->pll_prediv)); +- +- reg = dib8000_read_word(state, 1857); +- dib8000_write_word(state, 1857, reg|(!pll->pll_bypass<<15)); +- +- reg = dib8000_read_word(state, 1858); /* Force clk out pll /2 */ +- dib8000_write_word(state, 1858, reg | 1); +- +- dib8000_write_word(state, 904, (pll->modulo << 8)); +- } +- +- dib8000_reset_pll_common(state, pll); +-} +- +-int dib8000_update_pll(struct dvb_frontend *fe, +- struct dibx000_bandwidth_config *pll) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 reg_1857, reg_1856 = dib8000_read_word(state, 1856); +- u8 loopdiv, prediv; +- u32 internal, xtal; +- +- /* get back old values */ +- prediv = reg_1856 & 0x3f; +- loopdiv = (reg_1856 >> 6) & 0x3f; +- +- if ((pll != NULL) && (pll->pll_prediv != prediv || +- pll->pll_ratio != loopdiv)) { +- dprintk("Updating pll (prediv: old = %d new = %d ; loopdiv : old = %d new = %d)", prediv, pll->pll_prediv, loopdiv, pll->pll_ratio); +- reg_1856 &= 0xf000; +- reg_1857 = dib8000_read_word(state, 1857); +- /* disable PLL */ +- dib8000_write_word(state, 1857, reg_1857 & ~(1 << 15)); +- +- dib8000_write_word(state, 1856, reg_1856 | +- ((pll->pll_ratio & 0x3f) << 6) | +- (pll->pll_prediv & 0x3f)); +- +- /* write new system clk into P_sec_len */ +- internal = dib8000_read32(state, 23) / 1000; +- dprintk("Old Internal = %d", internal); +- xtal = 2 * (internal / loopdiv) * prediv; +- internal = 1000 * (xtal/pll->pll_prediv) * pll->pll_ratio; +- dprintk("Xtal = %d , New Fmem = %d New Fdemod = %d, New Fsampling = %d", xtal, internal/1000, internal/2000, internal/8000); +- dprintk("New Internal = %d", internal); +- +- dib8000_write_word(state, 23, +- (u16) (((internal / 2) >> 16) & 0xffff)); +- dib8000_write_word(state, 24, (u16) ((internal / 2) & 0xffff)); +- /* enable PLL */ +- dib8000_write_word(state, 1857, reg_1857 | (1 << 15)); +- +- while (((dib8000_read_word(state, 1856)>>15)&0x1) != 1) +- dprintk("Waiting for PLL to lock"); +- +- /* verify */ +- reg_1856 = dib8000_read_word(state, 1856); +- dprintk("PLL Updated with prediv = %d and loopdiv = %d", +- reg_1856&0x3f, (reg_1856>>6)&0x3f); +- +- return 0; +- } +- return -EINVAL; +-} +-EXPORT_SYMBOL(dib8000_update_pll); +- +- +-static int dib8000_reset_gpio(struct dib8000_state *st) +-{ +- /* reset the GPIOs */ +- dib8000_write_word(st, 1029, st->cfg.gpio_dir); +- dib8000_write_word(st, 1030, st->cfg.gpio_val); +- +- /* TODO 782 is P_gpio_od */ +- +- dib8000_write_word(st, 1032, st->cfg.gpio_pwm_pos); +- +- dib8000_write_word(st, 1037, st->cfg.pwm_freq_div); +- return 0; +-} +- +-static int dib8000_cfg_gpio(struct dib8000_state *st, u8 num, u8 dir, u8 val) +-{ +- st->cfg.gpio_dir = dib8000_read_word(st, 1029); +- st->cfg.gpio_dir &= ~(1 << num); /* reset the direction bit */ +- st->cfg.gpio_dir |= (dir & 0x1) << num; /* set the new direction */ +- dib8000_write_word(st, 1029, st->cfg.gpio_dir); +- +- st->cfg.gpio_val = dib8000_read_word(st, 1030); +- st->cfg.gpio_val &= ~(1 << num); /* reset the direction bit */ +- st->cfg.gpio_val |= (val & 0x01) << num; /* set the new value */ +- dib8000_write_word(st, 1030, st->cfg.gpio_val); +- +- dprintk("gpio dir: %x: gpio val: %x", st->cfg.gpio_dir, st->cfg.gpio_val); +- +- return 0; +-} +- +-int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- return dib8000_cfg_gpio(state, num, dir, val); +-} +- +-EXPORT_SYMBOL(dib8000_set_gpio); +-static const u16 dib8000_defaults[] = { +- /* auto search configuration - lock0 by default waiting +- * for cpil_lock; lock1 cpil_lock; lock2 tmcc_sync_lock */ +- 3, 7, +- 0x0004, +- 0x0400, +- 0x0814, +- +- 12, 11, +- 0x001b, +- 0x7740, +- 0x005b, +- 0x8d80, +- 0x01c9, +- 0xc380, +- 0x0000, +- 0x0080, +- 0x0000, +- 0x0090, +- 0x0001, +- 0xd4c0, +- +- /*1, 32, +- 0x6680 // P_corm_thres Lock algorithms configuration */ +- +- 11, 80, /* set ADC level to -16 */ +- (1 << 13) - 825 - 117, +- (1 << 13) - 837 - 117, +- (1 << 13) - 811 - 117, +- (1 << 13) - 766 - 117, +- (1 << 13) - 737 - 117, +- (1 << 13) - 693 - 117, +- (1 << 13) - 648 - 117, +- (1 << 13) - 619 - 117, +- (1 << 13) - 575 - 117, +- (1 << 13) - 531 - 117, +- (1 << 13) - 501 - 117, +- +- 4, 108, +- 0, +- 0, +- 0, +- 0, +- +- 1, 175, +- 0x0410, +- 1, 179, +- 8192, // P_fft_nb_to_cut +- +- 6, 181, +- 0x2800, // P_coff_corthres_ ( 2k 4k 8k ) 0x2800 +- 0x2800, +- 0x2800, +- 0x2800, // P_coff_cpilthres_ ( 2k 4k 8k ) 0x2800 +- 0x2800, +- 0x2800, +- +- 2, 193, +- 0x0666, // P_pha3_thres +- 0x0000, // P_cti_use_cpe, P_cti_use_prog +- +- 2, 205, +- 0x200f, // P_cspu_regul, P_cspu_win_cut +- 0x000f, // P_des_shift_work +- +- 5, 215, +- 0x023d, // P_adp_regul_cnt +- 0x00a4, // P_adp_noise_cnt +- 0x00a4, // P_adp_regul_ext +- 0x7ff0, // P_adp_noise_ext +- 0x3ccc, // P_adp_fil +- +- 1, 230, +- 0x0000, // P_2d_byp_ti_num +- +- 1, 263, +- 0x800, //P_equal_thres_wgn +- +- 1, 268, +- (2 << 9) | 39, // P_equal_ctrl_synchro, P_equal_speedmode +- +- 1, 270, +- 0x0001, // P_div_lock0_wait +- 1, 285, +- 0x0020, //p_fec_ +- 1, 299, +- 0x0062, /* P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard */ +- +- 1, 338, +- (1 << 12) | // P_ctrl_corm_thres4pre_freq_inh=1 +- (1 << 10) | +- (0 << 9) | /* P_ctrl_pre_freq_inh=0 */ +- (3 << 5) | /* P_ctrl_pre_freq_step=3 */ +- (1 << 0), /* P_pre_freq_win_len=1 */ +- +- 0, +-}; +- +-static u16 dib8000_identify(struct i2c_device *client) +-{ +- u16 value; +- +- //because of glitches sometimes +- value = dib8000_i2c_read16(client, 896); +- +- if ((value = dib8000_i2c_read16(client, 896)) != 0x01b3) { +- dprintk("wrong Vendor ID (read=0x%x)", value); +- return 0; +- } +- +- value = dib8000_i2c_read16(client, 897); +- if (value != 0x8000 && value != 0x8001 && +- value != 0x8002 && value != 0x8090) { +- dprintk("wrong Device ID (%x)", value); +- return 0; +- } +- +- switch (value) { +- case 0x8000: +- dprintk("found DiB8000A"); +- break; +- case 0x8001: +- dprintk("found DiB8000B"); +- break; +- case 0x8002: +- dprintk("found DiB8000C"); +- break; +- case 0x8090: +- dprintk("found DiB8096P"); +- break; +- } +- return value; +-} +- +-static int dib8000_reset(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- if ((state->revision = dib8000_identify(&state->i2c)) == 0) +- return -EINVAL; +- +- /* sram lead in, rdy */ +- if (state->revision != 0x8090) +- dib8000_write_word(state, 1287, 0x0003); +- +- if (state->revision == 0x8000) +- dprintk("error : dib8000 MA not supported"); +- +- dibx000_reset_i2c_master(&state->i2c_master); +- +- dib8000_set_power_mode(state, DIB8000_POWER_ALL); +- +- /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ +- dib8000_set_adc_state(state, DIBX000_VBG_ENABLE); +- +- /* restart all parts */ +- dib8000_write_word(state, 770, 0xffff); +- dib8000_write_word(state, 771, 0xffff); +- dib8000_write_word(state, 772, 0xfffc); +- if (state->revision == 0x8090) +- dib8000_write_word(state, 1280, 0x0045); +- else +- dib8000_write_word(state, 1280, 0x004d); +- dib8000_write_word(state, 1281, 0x000c); +- +- dib8000_write_word(state, 770, 0x0000); +- dib8000_write_word(state, 771, 0x0000); +- dib8000_write_word(state, 772, 0x0000); +- dib8000_write_word(state, 898, 0x0004); // sad +- dib8000_write_word(state, 1280, 0x0000); +- dib8000_write_word(state, 1281, 0x0000); +- +- /* drives */ +- if (state->revision != 0x8090) { +- if (state->cfg.drives) +- dib8000_write_word(state, 906, state->cfg.drives); +- else { +- dprintk("using standard PAD-drive-settings, please adjust settings in config-struct to be optimal."); +- /* min drive SDRAM - not optimal - adjust */ +- dib8000_write_word(state, 906, 0x2d98); +- } +- } +- +- dib8000_reset_pll(state); +- if (state->revision != 0x8090) +- dib8000_write_word(state, 898, 0x0004); +- +- if (dib8000_reset_gpio(state) != 0) +- dprintk("GPIO reset was not successful."); +- +- if ((state->revision != 0x8090) && +- (dib8000_set_output_mode(fe, OUTMODE_HIGH_Z) != 0)) +- dprintk("OUTPUT_MODE could not be resetted."); +- +- state->current_agc = NULL; +- +- // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... +- /* P_iqc_ca2 = 0; P_iqc_impnc_on = 0; P_iqc_mode = 0; */ +- if (state->cfg.pll->ifreq == 0) +- dib8000_write_word(state, 40, 0x0755); /* P_iqc_corr_inh = 0 enable IQcorr block */ +- else +- dib8000_write_word(state, 40, 0x1f55); /* P_iqc_corr_inh = 1 disable IQcorr block */ +- +- { +- u16 l = 0, r; +- const u16 *n; +- n = dib8000_defaults; +- l = *n++; +- while (l) { +- r = *n++; +- do { +- dib8000_write_word(state, r, *n++); +- r++; +- } while (--l); +- l = *n++; +- } +- } +- if (state->revision != 0x8090) +- dib8000_write_word(state, 903, (0 << 4) | 2); +- state->isdbt_cfg_loaded = 0; +- +- //div_cfg override for special configs +- if (state->cfg.div_cfg != 0) +- dib8000_write_word(state, 903, state->cfg.div_cfg); +- +- /* unforce divstr regardless whether i2c enumeration was done or not */ +- dib8000_write_word(state, 1285, dib8000_read_word(state, 1285) & ~(1 << 1)); +- +- dib8000_set_bandwidth(fe, 6000); +- +- dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON); +- if (state->revision != 0x8090) { +- dib8000_sad_calib(state); +- dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF); +- } +- +- dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); +- +- return 0; +-} +- +-static void dib8000_restart_agc(struct dib8000_state *state) +-{ +- // P_restart_iqc & P_restart_agc +- dib8000_write_word(state, 770, 0x0a00); +- dib8000_write_word(state, 770, 0x0000); +-} +- +-static int dib8000_update_lna(struct dib8000_state *state) +-{ +- u16 dyn_gain; +- +- if (state->cfg.update_lna) { +- // read dyn_gain here (because it is demod-dependent and not tuner) +- dyn_gain = dib8000_read_word(state, 390); +- +- if (state->cfg.update_lna(state->fe[0], dyn_gain)) { +- dib8000_restart_agc(state); +- return 1; +- } +- } +- return 0; +-} +- +-static int dib8000_set_agc_config(struct dib8000_state *state, u8 band) +-{ +- struct dibx000_agc_config *agc = NULL; +- int i; +- u16 reg; +- +- if (state->current_band == band && state->current_agc != NULL) +- return 0; +- state->current_band = band; +- +- for (i = 0; i < state->cfg.agc_config_count; i++) +- if (state->cfg.agc[i].band_caps & band) { +- agc = &state->cfg.agc[i]; +- break; +- } +- +- if (agc == NULL) { +- dprintk("no valid AGC configuration found for band 0x%02x", band); +- return -EINVAL; +- } +- +- state->current_agc = agc; +- +- /* AGC */ +- dib8000_write_word(state, 76, agc->setup); +- dib8000_write_word(state, 77, agc->inv_gain); +- dib8000_write_word(state, 78, agc->time_stabiliz); +- dib8000_write_word(state, 101, (agc->alpha_level << 12) | agc->thlock); +- +- // Demod AGC loop configuration +- dib8000_write_word(state, 102, (agc->alpha_mant << 5) | agc->alpha_exp); +- dib8000_write_word(state, 103, (agc->beta_mant << 6) | agc->beta_exp); +- +- dprintk("WBD: ref: %d, sel: %d, active: %d, alpha: %d", +- state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); +- +- /* AGC continued */ +- if (state->wbd_ref != 0) +- dib8000_write_word(state, 106, state->wbd_ref); +- else // use default +- dib8000_write_word(state, 106, agc->wbd_ref); +- +- if (state->revision == 0x8090) { +- reg = dib8000_read_word(state, 922) & (0x3 << 2); +- dib8000_write_word(state, 922, reg | (agc->wbd_sel << 2)); +- } +- +- dib8000_write_word(state, 107, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8)); +- dib8000_write_word(state, 108, agc->agc1_max); +- dib8000_write_word(state, 109, agc->agc1_min); +- dib8000_write_word(state, 110, agc->agc2_max); +- dib8000_write_word(state, 111, agc->agc2_min); +- dib8000_write_word(state, 112, (agc->agc1_pt1 << 8) | agc->agc1_pt2); +- dib8000_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); +- dib8000_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); +- dib8000_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); +- +- dib8000_write_word(state, 75, agc->agc1_pt3); +- if (state->revision != 0x8090) +- dib8000_write_word(state, 923, +- (dib8000_read_word(state, 923) & 0xffe3) | +- (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); +- +- return 0; +-} +- +-void dib8000_pwm_agc_reset(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- dib8000_set_adc_state(state, DIBX000_ADC_ON); +- dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))); +-} +-EXPORT_SYMBOL(dib8000_pwm_agc_reset); +- +-static int dib8000_agc_soft_split(struct dib8000_state *state) +-{ +- u16 agc, split_offset; +- +- if (!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) +- return FE_CALLBACK_TIME_NEVER; +- +- // n_agc_global +- agc = dib8000_read_word(state, 390); +- +- if (agc > state->current_agc->split.min_thres) +- split_offset = state->current_agc->split.min; +- else if (agc < state->current_agc->split.max_thres) +- split_offset = state->current_agc->split.max; +- else +- split_offset = state->current_agc->split.max * +- (agc - state->current_agc->split.min_thres) / +- (state->current_agc->split.max_thres - state->current_agc->split.min_thres); +- +- dprintk("AGC split_offset: %d", split_offset); +- +- // P_agc_force_split and P_agc_split_offset +- dib8000_write_word(state, 107, (dib8000_read_word(state, 107) & 0xff00) | split_offset); +- return 5000; +-} +- +-static int dib8000_agc_startup(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- enum frontend_tune_state *tune_state = &state->tune_state; +- int ret = 0; +- u16 reg, upd_demod_gain_period = 0x8000; +- +- switch (*tune_state) { +- case CT_AGC_START: +- // set power-up level: interf+analog+AGC +- +- if (state->revision != 0x8090) +- dib8000_set_adc_state(state, DIBX000_ADC_ON); +- else { +- dib8000_set_power_mode(state, DIB8000_POWER_ALL); +- +- reg = dib8000_read_word(state, 1947)&0xff00; +- dib8000_write_word(state, 1946, +- upd_demod_gain_period & 0xFFFF); +- /* bit 14 = enDemodGain */ +- dib8000_write_word(state, 1947, reg | (1<<14) | +- ((upd_demod_gain_period >> 16) & 0xFF)); +- +- /* enable adc i & q */ +- reg = dib8000_read_word(state, 1920); +- dib8000_write_word(state, 1920, (reg | 0x3) & +- (~(1 << 7))); +- } +- +- if (dib8000_set_agc_config(state, (unsigned char)(BAND_OF_FREQUENCY(fe->dtv_property_cache.frequency / 1000))) != 0) { +- *tune_state = CT_AGC_STOP; +- state->status = FE_STATUS_TUNE_FAILED; +- break; +- } +- +- ret = 70; +- *tune_state = CT_AGC_STEP_0; +- break; +- +- case CT_AGC_STEP_0: +- //AGC initialization +- if (state->cfg.agc_control) +- state->cfg.agc_control(fe, 1); +- +- dib8000_restart_agc(state); +- +- // wait AGC rough lock time +- ret = 50; +- *tune_state = CT_AGC_STEP_1; +- break; +- +- case CT_AGC_STEP_1: +- // wait AGC accurate lock time +- ret = 70; +- +- if (dib8000_update_lna(state)) +- // wait only AGC rough lock time +- ret = 50; +- else +- *tune_state = CT_AGC_STEP_2; +- break; +- +- case CT_AGC_STEP_2: +- dib8000_agc_soft_split(state); +- +- if (state->cfg.agc_control) +- state->cfg.agc_control(fe, 0); +- +- *tune_state = CT_AGC_STOP; +- break; +- default: +- ret = dib8000_agc_soft_split(state); +- break; +- } +- return ret; +- +-} +- +-static void dib8096p_host_bus_drive(struct dib8000_state *state, u8 drive) +-{ +- u16 reg; +- +- drive &= 0x7; +- +- /* drive host bus 2, 3, 4 */ +- reg = dib8000_read_word(state, 1798) & +- ~(0x7 | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive<<12) | (drive<<6) | drive; +- dib8000_write_word(state, 1798, reg); +- +- /* drive host bus 5,6 */ +- reg = dib8000_read_word(state, 1799) & ~((0x7 << 2) | (0x7 << 8)); +- reg |= (drive<<8) | (drive<<2); +- dib8000_write_word(state, 1799, reg); +- +- /* drive host bus 7, 8, 9 */ +- reg = dib8000_read_word(state, 1800) & +- ~(0x7 | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive<<12) | (drive<<6) | drive; +- dib8000_write_word(state, 1800, reg); +- +- /* drive host bus 10, 11 */ +- reg = dib8000_read_word(state, 1801) & ~((0x7 << 2) | (0x7 << 8)); +- reg |= (drive<<8) | (drive<<2); +- dib8000_write_word(state, 1801, reg); +- +- /* drive host bus 12, 13, 14 */ +- reg = dib8000_read_word(state, 1802) & +- ~(0x7 | (0x7 << 6) | (0x7 << 12)); +- reg |= (drive<<12) | (drive<<6) | drive; +- dib8000_write_word(state, 1802, reg); +-} +- +-static u32 dib8096p_calcSyncFreq(u32 P_Kin, u32 P_Kout, +- u32 insertExtSynchro, u32 syncSize) +-{ +- u32 quantif = 3; +- u32 nom = (insertExtSynchro * P_Kin+syncSize); +- u32 denom = P_Kout; +- u32 syncFreq = ((nom << quantif) / denom); +- +- if ((syncFreq & ((1 << quantif) - 1)) != 0) +- syncFreq = (syncFreq >> quantif) + 1; +- else +- syncFreq = (syncFreq >> quantif); +- +- if (syncFreq != 0) +- syncFreq = syncFreq - 1; +- +- return syncFreq; +-} +- +-static void dib8096p_cfg_DibTx(struct dib8000_state *state, u32 P_Kin, +- u32 P_Kout, u32 insertExtSynchro, u32 synchroMode, +- u32 syncWord, u32 syncSize) +-{ +- dprintk("Configure DibStream Tx"); +- +- dib8000_write_word(state, 1615, 1); +- dib8000_write_word(state, 1603, P_Kin); +- dib8000_write_word(state, 1605, P_Kout); +- dib8000_write_word(state, 1606, insertExtSynchro); +- dib8000_write_word(state, 1608, synchroMode); +- dib8000_write_word(state, 1609, (syncWord >> 16) & 0xffff); +- dib8000_write_word(state, 1610, syncWord & 0xffff); +- dib8000_write_word(state, 1612, syncSize); +- dib8000_write_word(state, 1615, 0); +-} +- +-static void dib8096p_cfg_DibRx(struct dib8000_state *state, u32 P_Kin, +- u32 P_Kout, u32 synchroMode, u32 insertExtSynchro, +- u32 syncWord, u32 syncSize, u32 dataOutRate) +-{ +- u32 syncFreq; +- +- dprintk("Configure DibStream Rx synchroMode = %d", synchroMode); +- +- if ((P_Kin != 0) && (P_Kout != 0)) { +- syncFreq = dib8096p_calcSyncFreq(P_Kin, P_Kout, +- insertExtSynchro, syncSize); +- dib8000_write_word(state, 1542, syncFreq); +- } +- +- dib8000_write_word(state, 1554, 1); +- dib8000_write_word(state, 1536, P_Kin); +- dib8000_write_word(state, 1537, P_Kout); +- dib8000_write_word(state, 1539, synchroMode); +- dib8000_write_word(state, 1540, (syncWord >> 16) & 0xffff); +- dib8000_write_word(state, 1541, syncWord & 0xffff); +- dib8000_write_word(state, 1543, syncSize); +- dib8000_write_word(state, 1544, dataOutRate); +- dib8000_write_word(state, 1554, 0); +-} +- +-static void dib8096p_enMpegMux(struct dib8000_state *state, int onoff) +-{ +- u16 reg_1287; +- +- reg_1287 = dib8000_read_word(state, 1287); +- +- switch (onoff) { +- case 1: +- reg_1287 &= ~(1 << 8); +- break; +- case 0: +- reg_1287 |= (1 << 8); +- break; +- } +- +- dib8000_write_word(state, 1287, reg_1287); +-} +- +-static void dib8096p_configMpegMux(struct dib8000_state *state, +- u16 pulseWidth, u16 enSerialMode, u16 enSerialClkDiv2) +-{ +- u16 reg_1287; +- +- dprintk("Enable Mpeg mux"); +- +- dib8096p_enMpegMux(state, 0); +- +- /* If the input mode is MPEG do not divide the serial clock */ +- if ((enSerialMode == 1) && (state->input_mode_mpeg == 1)) +- enSerialClkDiv2 = 0; +- +- reg_1287 = ((pulseWidth & 0x1f) << 3) | +- ((enSerialMode & 0x1) << 2) | (enSerialClkDiv2 & 0x1); +- dib8000_write_word(state, 1287, reg_1287); +- +- dib8096p_enMpegMux(state, 1); +-} +- +-static void dib8096p_setDibTxMux(struct dib8000_state *state, int mode) +-{ +- u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 7); +- +- switch (mode) { +- case MPEG_ON_DIBTX: +- dprintk("SET MPEG ON DIBSTREAM TX"); +- dib8096p_cfg_DibTx(state, 8, 5, 0, 0, 0, 0); +- reg_1288 |= (1 << 9); break; +- case DIV_ON_DIBTX: +- dprintk("SET DIV_OUT ON DIBSTREAM TX"); +- dib8096p_cfg_DibTx(state, 5, 5, 0, 0, 0, 0); +- reg_1288 |= (1 << 8); break; +- case ADC_ON_DIBTX: +- dprintk("SET ADC_OUT ON DIBSTREAM TX"); +- dib8096p_cfg_DibTx(state, 20, 5, 10, 0, 0, 0); +- reg_1288 |= (1 << 7); break; +- default: +- break; +- } +- dib8000_write_word(state, 1288, reg_1288); +-} +- +-static void dib8096p_setHostBusMux(struct dib8000_state *state, int mode) +-{ +- u16 reg_1288 = dib8000_read_word(state, 1288) & ~(0x7 << 4); +- +- switch (mode) { +- case DEMOUT_ON_HOSTBUS: +- dprintk("SET DEM OUT OLD INTERF ON HOST BUS"); +- dib8096p_enMpegMux(state, 0); +- reg_1288 |= (1 << 6); +- break; +- case DIBTX_ON_HOSTBUS: +- dprintk("SET DIBSTREAM TX ON HOST BUS"); +- dib8096p_enMpegMux(state, 0); +- reg_1288 |= (1 << 5); +- break; +- case MPEG_ON_HOSTBUS: +- dprintk("SET MPEG MUX ON HOST BUS"); +- reg_1288 |= (1 << 4); +- break; +- default: +- break; +- } +- dib8000_write_word(state, 1288, reg_1288); +-} +- +-static int dib8096p_set_diversity_in(struct dvb_frontend *fe, int onoff) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 reg_1287; +- +- switch (onoff) { +- case 0: /* only use the internal way - not the diversity input */ +- dprintk("%s mode OFF : by default Enable Mpeg INPUT", +- __func__); +- /* outputRate = 8 */ +- dib8096p_cfg_DibRx(state, 8, 5, 0, 0, 0, 8, 0); +- +- /* Do not divide the serial clock of MPEG MUX in +- SERIAL MODE in case input mode MPEG is used */ +- reg_1287 = dib8000_read_word(state, 1287); +- /* enSerialClkDiv2 == 1 ? */ +- if ((reg_1287 & 0x1) == 1) { +- /* force enSerialClkDiv2 = 0 */ +- reg_1287 &= ~0x1; +- dib8000_write_word(state, 1287, reg_1287); +- } +- state->input_mode_mpeg = 1; +- break; +- case 1: /* both ways */ +- case 2: /* only the diversity input */ +- dprintk("%s ON : Enable diversity INPUT", __func__); +- dib8096p_cfg_DibRx(state, 5, 5, 0, 0, 0, 0, 0); +- state->input_mode_mpeg = 0; +- break; +- } +- +- dib8000_set_diversity_in(state->fe[0], onoff); +- return 0; +-} +- +-static int dib8096p_set_output_mode(struct dvb_frontend *fe, int mode) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 outreg, smo_mode, fifo_threshold; +- u8 prefer_mpeg_mux_use = 1; +- int ret = 0; +- +- dib8096p_host_bus_drive(state, 1); +- +- fifo_threshold = 1792; +- smo_mode = (dib8000_read_word(state, 299) & 0x0050) | (1 << 1); +- outreg = dib8000_read_word(state, 1286) & +- ~((1 << 10) | (0x7 << 6) | (1 << 1)); +- +- switch (mode) { +- case OUTMODE_HIGH_Z: +- outreg = 0; +- break; +- +- case OUTMODE_MPEG2_SERIAL: +- if (prefer_mpeg_mux_use) { +- dprintk("dib8096P setting output mode TS_SERIAL using Mpeg Mux"); +- dib8096p_configMpegMux(state, 3, 1, 1); +- dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); +- } else {/* Use Smooth block */ +- dprintk("dib8096P setting output mode TS_SERIAL using Smooth bloc"); +- dib8096p_setHostBusMux(state, +- DEMOUT_ON_HOSTBUS); +- outreg |= (2 << 6) | (0 << 1); +- } +- break; +- +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- if (prefer_mpeg_mux_use) { +- dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Mpeg Mux"); +- dib8096p_configMpegMux(state, 2, 0, 0); +- dib8096p_setHostBusMux(state, MPEG_ON_HOSTBUS); +- } else { /* Use Smooth block */ +- dprintk("dib8096P setting output mode TS_PARALLEL_GATED using Smooth block"); +- dib8096p_setHostBusMux(state, +- DEMOUT_ON_HOSTBUS); +- outreg |= (0 << 6); +- } +- break; +- +- case OUTMODE_MPEG2_PAR_CONT_CLK: /* Using Smooth block only */ +- dprintk("dib8096P setting output mode TS_PARALLEL_CONT using Smooth block"); +- dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (1 << 6); +- break; +- +- case OUTMODE_MPEG2_FIFO: +- /* Using Smooth block because not supported +- by new Mpeg Mux bloc */ +- dprintk("dib8096P setting output mode TS_FIFO using Smooth block"); +- dib8096p_setHostBusMux(state, DEMOUT_ON_HOSTBUS); +- outreg |= (5 << 6); +- smo_mode |= (3 << 1); +- fifo_threshold = 512; +- break; +- +- case OUTMODE_DIVERSITY: +- dprintk("dib8096P setting output mode MODE_DIVERSITY"); +- dib8096p_setDibTxMux(state, DIV_ON_DIBTX); +- dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- break; +- +- case OUTMODE_ANALOG_ADC: +- dprintk("dib8096P setting output mode MODE_ANALOG_ADC"); +- dib8096p_setDibTxMux(state, ADC_ON_DIBTX); +- dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- break; +- } +- +- if (mode != OUTMODE_HIGH_Z) +- outreg |= (1<<10); +- +- dprintk("output_mpeg2_in_188_bytes = %d", +- state->cfg.output_mpeg2_in_188_bytes); +- if (state->cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5); +- +- ret |= dib8000_write_word(state, 299, smo_mode); +- /* synchronous fread */ +- ret |= dib8000_write_word(state, 299 + 1, fifo_threshold); +- ret |= dib8000_write_word(state, 1286, outreg); +- +- return ret; +-} +- +-static int map_addr_to_serpar_number(struct i2c_msg *msg) +-{ +- if (msg->buf[0] <= 15) +- msg->buf[0] -= 1; +- else if (msg->buf[0] == 17) +- msg->buf[0] = 15; +- else if (msg->buf[0] == 16) +- msg->buf[0] = 17; +- else if (msg->buf[0] == 19) +- msg->buf[0] = 16; +- else if (msg->buf[0] >= 21 && msg->buf[0] <= 25) +- msg->buf[0] -= 3; +- else if (msg->buf[0] == 28) +- msg->buf[0] = 23; +- else if (msg->buf[0] == 99) +- msg->buf[0] = 99; +- else +- return -EINVAL; +- return 0; +-} +- +-static int dib8096p_tuner_write_serpar(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct dib8000_state *state = i2c_get_adapdata(i2c_adap); +- u8 n_overflow = 1; +- u16 i = 1000; +- u16 serpar_num = msg[0].buf[0]; +- +- while (n_overflow == 1 && i) { +- n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; +- i--; +- if (i == 0) +- dprintk("Tuner ITF: write busy (overflow)"); +- } +- dib8000_write_word(state, 1985, (1 << 6) | (serpar_num & 0x3f)); +- dib8000_write_word(state, 1986, (msg[0].buf[1] << 8) | msg[0].buf[2]); +- +- return num; +-} +- +-static int dib8096p_tuner_read_serpar(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct dib8000_state *state = i2c_get_adapdata(i2c_adap); +- u8 n_overflow = 1, n_empty = 1; +- u16 i = 1000; +- u16 serpar_num = msg[0].buf[0]; +- u16 read_word; +- +- while (n_overflow == 1 && i) { +- n_overflow = (dib8000_read_word(state, 1984) >> 1) & 0x1; +- i--; +- if (i == 0) +- dprintk("TunerITF: read busy (overflow)"); +- } +- dib8000_write_word(state, 1985, (0<<6) | (serpar_num&0x3f)); +- +- i = 1000; +- while (n_empty == 1 && i) { +- n_empty = dib8000_read_word(state, 1984)&0x1; +- i--; +- if (i == 0) +- dprintk("TunerITF: read busy (empty)"); +- } +- +- read_word = dib8000_read_word(state, 1987); +- msg[1].buf[0] = (read_word >> 8) & 0xff; +- msg[1].buf[1] = (read_word) & 0xff; +- +- return num; +-} +- +-static int dib8096p_tuner_rw_serpar(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- if (map_addr_to_serpar_number(&msg[0]) == 0) { +- if (num == 1) /* write */ +- return dib8096p_tuner_write_serpar(i2c_adap, msg, 1); +- else /* read */ +- return dib8096p_tuner_read_serpar(i2c_adap, msg, 2); +- } +- return num; +-} +- +-static int dib8096p_rw_on_apb(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num, u16 apb_address) +-{ +- struct dib8000_state *state = i2c_get_adapdata(i2c_adap); +- u16 word; +- +- if (num == 1) { /* write */ +- dib8000_write_word(state, apb_address, +- ((msg[0].buf[1] << 8) | (msg[0].buf[2]))); +- } else { +- word = dib8000_read_word(state, apb_address); +- msg[1].buf[0] = (word >> 8) & 0xff; +- msg[1].buf[1] = (word) & 0xff; +- } +- return num; +-} +- +-static int dib8096p_tuner_xfer(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct dib8000_state *state = i2c_get_adapdata(i2c_adap); +- u16 apb_address = 0, word; +- int i = 0; +- +- switch (msg[0].buf[0]) { +- case 0x12: +- apb_address = 1920; +- break; +- case 0x14: +- apb_address = 1921; +- break; +- case 0x24: +- apb_address = 1922; +- break; +- case 0x1a: +- apb_address = 1923; +- break; +- case 0x22: +- apb_address = 1924; +- break; +- case 0x33: +- apb_address = 1926; +- break; +- case 0x34: +- apb_address = 1927; +- break; +- case 0x35: +- apb_address = 1928; +- break; +- case 0x36: +- apb_address = 1929; +- break; +- case 0x37: +- apb_address = 1930; +- break; +- case 0x38: +- apb_address = 1931; +- break; +- case 0x39: +- apb_address = 1932; +- break; +- case 0x2a: +- apb_address = 1935; +- break; +- case 0x2b: +- apb_address = 1936; +- break; +- case 0x2c: +- apb_address = 1937; +- break; +- case 0x2d: +- apb_address = 1938; +- break; +- case 0x2e: +- apb_address = 1939; +- break; +- case 0x2f: +- apb_address = 1940; +- break; +- case 0x30: +- apb_address = 1941; +- break; +- case 0x31: +- apb_address = 1942; +- break; +- case 0x32: +- apb_address = 1943; +- break; +- case 0x3e: +- apb_address = 1944; +- break; +- case 0x3f: +- apb_address = 1945; +- break; +- case 0x40: +- apb_address = 1948; +- break; +- case 0x25: +- apb_address = 936; +- break; +- case 0x26: +- apb_address = 937; +- break; +- case 0x27: +- apb_address = 938; +- break; +- case 0x28: +- apb_address = 939; +- break; +- case 0x1d: +- /* get sad sel request */ +- i = ((dib8000_read_word(state, 921) >> 12)&0x3); +- word = dib8000_read_word(state, 924+i); +- msg[1].buf[0] = (word >> 8) & 0xff; +- msg[1].buf[1] = (word) & 0xff; +- return num; +- case 0x1f: +- if (num == 1) { /* write */ +- word = (u16) ((msg[0].buf[1] << 8) | +- msg[0].buf[2]); +- /* in the VGAMODE Sel are located on bit 0/1 */ +- word &= 0x3; +- word = (dib8000_read_word(state, 921) & +- ~(3<<12)) | (word<<12); +- /* Set the proper input */ +- dib8000_write_word(state, 921, word); +- return num; +- } +- } +- +- if (apb_address != 0) /* R/W acces via APB */ +- return dib8096p_rw_on_apb(i2c_adap, msg, num, apb_address); +- else /* R/W access via SERPAR */ +- return dib8096p_tuner_rw_serpar(i2c_adap, msg, num); +- +- return 0; +-} +- +-static u32 dib8096p_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dib8096p_tuner_xfer_algo = { +- .master_xfer = dib8096p_tuner_xfer, +- .functionality = dib8096p_i2c_func, +-}; +- +-struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) +-{ +- struct dib8000_state *st = fe->demodulator_priv; +- return &st->dib8096p_tuner_adap; +-} +-EXPORT_SYMBOL(dib8096p_get_i2c_tuner); +- +-int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 en_cur_state; +- +- dprintk("sleep dib8096p: %d", onoff); +- +- en_cur_state = dib8000_read_word(state, 1922); +- +- /* LNAs and MIX are ON and therefore it is a valid configuration */ +- if (en_cur_state > 0xff) +- state->tuner_enable = en_cur_state ; +- +- if (onoff) +- en_cur_state &= 0x00ff; +- else { +- if (state->tuner_enable != 0) +- en_cur_state = state->tuner_enable; +- } +- +- dib8000_write_word(state, 1922, en_cur_state); +- +- return 0; +-} +-EXPORT_SYMBOL(dib8096p_tuner_sleep); +- +-static const s32 lut_1000ln_mant[] = +-{ +- 908, 7003, 7090, 7170, 7244, 7313, 7377, 7438, 7495, 7549, 7600 +-}; +- +-s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u32 ix = 0, tmp_val = 0, exp = 0, mant = 0; +- s32 val; +- +- val = dib8000_read32(state, 384); +- if (mode) { +- tmp_val = val; +- while (tmp_val >>= 1) +- exp++; +- mant = (val * 1000 / (1<demodulator_priv; +- int val = 0; +- +- switch (IQ) { +- case 1: +- val = dib8000_read_word(state, 403); +- break; +- case 0: +- val = dib8000_read_word(state, 404); +- break; +- } +- if (val & 0x200) +- val -= 1024; +- +- return val; +-} +-EXPORT_SYMBOL(dib8090p_get_dc_power); +- +-static void dib8000_update_timf(struct dib8000_state *state) +-{ +- u32 timf = state->timf = dib8000_read32(state, 435); +- +- dib8000_write_word(state, 29, (u16) (timf >> 16)); +- dib8000_write_word(state, 30, (u16) (timf & 0xffff)); +- dprintk("Updated timing frequency: %d (default: %d)", state->timf, state->timf_default); +-} +- +-u32 dib8000_ctrl_timf(struct dvb_frontend *fe, uint8_t op, uint32_t timf) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- switch (op) { +- case DEMOD_TIMF_SET: +- state->timf = timf; +- break; +- case DEMOD_TIMF_UPDATE: +- dib8000_update_timf(state); +- break; +- case DEMOD_TIMF_GET: +- break; +- } +- dib8000_set_bandwidth(state->fe[0], 6000); +- +- return state->timf; +-} +-EXPORT_SYMBOL(dib8000_ctrl_timf); +- +-static const u16 adc_target_16dB[11] = { +- (1 << 13) - 825 - 117, +- (1 << 13) - 837 - 117, +- (1 << 13) - 811 - 117, +- (1 << 13) - 766 - 117, +- (1 << 13) - 737 - 117, +- (1 << 13) - 693 - 117, +- (1 << 13) - 648 - 117, +- (1 << 13) - 619 - 117, +- (1 << 13) - 575 - 117, +- (1 << 13) - 531 - 117, +- (1 << 13) - 501 - 117 +-}; +-static const u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; +- +-static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosearching) +-{ +- u16 mode, max_constellation, seg_diff_mask = 0, nbseg_diff = 0; +- u8 guard, crate, constellation, timeI; +- u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled +- const s16 *ncoeff = NULL, *ana_fe; +- u16 tmcc_pow = 0; +- u16 coff_pow = 0x2800; +- u16 init_prbs = 0xfff; +- u16 ana_gain = 0; +- +- if (state->revision == 0x8090) +- dib8000_init_sdram(state); +- +- if (state->ber_monitored_layer != LAYER_ALL) +- dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & 0x60) | state->ber_monitored_layer); +- else +- dib8000_write_word(state, 285, dib8000_read_word(state, 285) & 0x60); +- +- i = dib8000_read_word(state, 26) & 1; // P_dds_invspec +- dib8000_write_word(state, 26, state->fe[0]->dtv_property_cache.inversion^i); +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { +- //compute new dds_freq for the seg and adjust prbs +- int seg_offset = +- state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx - +- (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) - +- (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2); +- int clk = state->cfg.pll->internal; +- u32 segtodds = ((u32) (430 << 23) / clk) << 3; // segtodds = SegBW / Fclk * pow(2,26) +- int dds_offset = seg_offset * segtodds; +- int new_dds, sub_channel; +- if ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) +- dds_offset -= (int)(segtodds / 2); +- +- if (state->cfg.pll->ifreq == 0) { +- if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) { +- dib8000_write_word(state, 26, dib8000_read_word(state, 26) | 1); +- new_dds = dds_offset; +- } else +- new_dds = dds_offset; +- +- // We shift tuning frequency if the wanted segment is : +- // - the segment of center frequency with an odd total number of segments +- // - the segment to the left of center frequency with an even total number of segments +- // - the segment to the right of center frequency with an even total number of segments +- if ((state->fe[0]->dtv_property_cache.delivery_system == SYS_ISDBT) +- && (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) +- && (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) +- && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == +- ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) +- || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) +- && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2))) +- || (((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) == 0) +- && (state->fe[0]->dtv_property_cache.isdbt_sb_segment_idx == +- ((state->fe[0]->dtv_property_cache.isdbt_sb_segment_count / 2) + 1))) +- )) { +- new_dds -= ((u32) (850 << 22) / clk) << 4; // new_dds = 850 (freq shift in KHz) / Fclk * pow(2,26) +- } +- } else { +- if ((state->fe[0]->dtv_property_cache.inversion ^ i) == 0) +- new_dds = state->cfg.pll->ifreq - dds_offset; +- else +- new_dds = state->cfg.pll->ifreq + dds_offset; +- } +- dib8000_write_word(state, 27, (u16) ((new_dds >> 16) & 0x01ff)); +- dib8000_write_word(state, 28, (u16) (new_dds & 0xffff)); +- if (state->fe[0]->dtv_property_cache.isdbt_sb_segment_count % 2) +- sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset) + 1) % 41) / 3; +- else +- sub_channel = ((state->fe[0]->dtv_property_cache.isdbt_sb_subchannel + (3 * seg_offset)) % 41) / 3; +- sub_channel -= 6; +- +- if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K +- || state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_4K) { +- dib8000_write_word(state, 219, dib8000_read_word(state, 219) | 0x1); //adp_pass =1 +- dib8000_write_word(state, 190, dib8000_read_word(state, 190) | (0x1 << 14)); //pha3_force_pha_shift = 1 +- } else { +- dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); //adp_pass =0 +- dib8000_write_word(state, 190, dib8000_read_word(state, 190) & 0xbfff); //pha3_force_pha_shift = 0 +- } +- +- switch (state->fe[0]->dtv_property_cache.transmission_mode) { +- case TRANSMISSION_MODE_2K: +- switch (sub_channel) { +- case -6: +- init_prbs = 0x0; +- break; // 41, 0, 1 +- case -5: +- init_prbs = 0x423; +- break; // 02~04 +- case -4: +- init_prbs = 0x9; +- break; // 05~07 +- case -3: +- init_prbs = 0x5C7; +- break; // 08~10 +- case -2: +- init_prbs = 0x7A6; +- break; // 11~13 +- case -1: +- init_prbs = 0x3D8; +- break; // 14~16 +- case 0: +- init_prbs = 0x527; +- break; // 17~19 +- case 1: +- init_prbs = 0x7FF; +- break; // 20~22 +- case 2: +- init_prbs = 0x79B; +- break; // 23~25 +- case 3: +- init_prbs = 0x3D6; +- break; // 26~28 +- case 4: +- init_prbs = 0x3A2; +- break; // 29~31 +- case 5: +- init_prbs = 0x53B; +- break; // 32~34 +- case 6: +- init_prbs = 0x2F4; +- break; // 35~37 +- default: +- case 7: +- init_prbs = 0x213; +- break; // 38~40 +- } +- break; +- +- case TRANSMISSION_MODE_4K: +- switch (sub_channel) { +- case -6: +- init_prbs = 0x0; +- break; // 41, 0, 1 +- case -5: +- init_prbs = 0x208; +- break; // 02~04 +- case -4: +- init_prbs = 0xC3; +- break; // 05~07 +- case -3: +- init_prbs = 0x7B9; +- break; // 08~10 +- case -2: +- init_prbs = 0x423; +- break; // 11~13 +- case -1: +- init_prbs = 0x5C7; +- break; // 14~16 +- case 0: +- init_prbs = 0x3D8; +- break; // 17~19 +- case 1: +- init_prbs = 0x7FF; +- break; // 20~22 +- case 2: +- init_prbs = 0x3D6; +- break; // 23~25 +- case 3: +- init_prbs = 0x53B; +- break; // 26~28 +- case 4: +- init_prbs = 0x213; +- break; // 29~31 +- case 5: +- init_prbs = 0x29; +- break; // 32~34 +- case 6: +- init_prbs = 0xD0; +- break; // 35~37 +- default: +- case 7: +- init_prbs = 0x48E; +- break; // 38~40 +- } +- break; +- +- default: +- case TRANSMISSION_MODE_8K: +- switch (sub_channel) { +- case -6: +- init_prbs = 0x0; +- break; // 41, 0, 1 +- case -5: +- init_prbs = 0x740; +- break; // 02~04 +- case -4: +- init_prbs = 0x069; +- break; // 05~07 +- case -3: +- init_prbs = 0x7DD; +- break; // 08~10 +- case -2: +- init_prbs = 0x208; +- break; // 11~13 +- case -1: +- init_prbs = 0x7B9; +- break; // 14~16 +- case 0: +- init_prbs = 0x5C7; +- break; // 17~19 +- case 1: +- init_prbs = 0x7FF; +- break; // 20~22 +- case 2: +- init_prbs = 0x53B; +- break; // 23~25 +- case 3: +- init_prbs = 0x29; +- break; // 26~28 +- case 4: +- init_prbs = 0x48E; +- break; // 29~31 +- case 5: +- init_prbs = 0x4C4; +- break; // 32~34 +- case 6: +- init_prbs = 0x367; +- break; // 33~37 +- default: +- case 7: +- init_prbs = 0x684; +- break; // 38~40 +- } +- break; +- } +- } else { +- dib8000_write_word(state, 27, (u16) ((state->cfg.pll->ifreq >> 16) & 0x01ff)); +- dib8000_write_word(state, 28, (u16) (state->cfg.pll->ifreq & 0xffff)); +- dib8000_write_word(state, 26, (u16) ((state->cfg.pll->ifreq >> 25) & 0x0003)); +- } +- /*P_mode == ?? */ +- dib8000_write_word(state, 10, (seq << 4)); +- // dib8000_write_word(state, 287, (dib8000_read_word(state, 287) & 0xe000) | 0x1000); +- +- switch (state->fe[0]->dtv_property_cache.guard_interval) { +- case GUARD_INTERVAL_1_32: +- guard = 0; +- break; +- case GUARD_INTERVAL_1_16: +- guard = 1; +- break; +- case GUARD_INTERVAL_1_8: +- guard = 2; +- break; +- case GUARD_INTERVAL_1_4: +- default: +- guard = 3; +- break; +- } +- +- dib8000_write_word(state, 1, (init_prbs << 2) | (guard & 0x3)); // ADDR 1 +- +- max_constellation = DQPSK; +- for (i = 0; i < 3; i++) { +- switch (state->fe[0]->dtv_property_cache.layer[i].modulation) { +- case DQPSK: +- constellation = 0; +- break; +- case QPSK: +- constellation = 1; +- break; +- case QAM_16: +- constellation = 2; +- break; +- case QAM_64: +- default: +- constellation = 3; +- break; +- } +- +- switch (state->fe[0]->dtv_property_cache.layer[i].fec) { +- case FEC_1_2: +- crate = 1; +- break; +- case FEC_2_3: +- crate = 2; +- break; +- case FEC_3_4: +- crate = 3; +- break; +- case FEC_5_6: +- crate = 5; +- break; +- case FEC_7_8: +- default: +- crate = 7; +- break; +- } +- +- if ((state->fe[0]->dtv_property_cache.layer[i].interleaving > 0) && +- ((state->fe[0]->dtv_property_cache.layer[i].interleaving <= 3) || +- (state->fe[0]->dtv_property_cache.layer[i].interleaving == 4 && state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1)) +- ) +- timeI = state->fe[0]->dtv_property_cache.layer[i].interleaving; +- else +- timeI = 0; +- dib8000_write_word(state, 2 + i, (constellation << 10) | ((state->fe[0]->dtv_property_cache.layer[i].segment_count & 0xf) << 6) | +- (crate << 3) | timeI); +- if (state->fe[0]->dtv_property_cache.layer[i].segment_count > 0) { +- switch (max_constellation) { +- case DQPSK: +- case QPSK: +- if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_16 || +- state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) +- max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; +- break; +- case QAM_16: +- if (state->fe[0]->dtv_property_cache.layer[i].modulation == QAM_64) +- max_constellation = state->fe[0]->dtv_property_cache.layer[i].modulation; +- break; +- } +- } +- } +- +- mode = fft_to_mode(state); +- +- //dib8000_write_word(state, 5, 13); /*p_last_seg = 13*/ +- +- dib8000_write_word(state, 274, (dib8000_read_word(state, 274) & 0xffcf) | +- ((state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 5) | ((state->fe[0]->dtv_property_cache. +- isdbt_sb_mode & 1) << 4)); +- +- dprintk("mode = %d ; guard = %d", mode, state->fe[0]->dtv_property_cache.guard_interval); +- +- /* signal optimization parameter */ +- +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception) { +- seg_diff_mask = (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) << permu_seg[0]; +- for (i = 1; i < 3; i++) +- nbseg_diff += +- (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; +- for (i = 0; i < nbseg_diff; i++) +- seg_diff_mask |= 1 << permu_seg[i + 1]; +- } else { +- for (i = 0; i < 3; i++) +- nbseg_diff += +- (state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * state->fe[0]->dtv_property_cache.layer[i].segment_count; +- for (i = 0; i < nbseg_diff; i++) +- seg_diff_mask |= 1 << permu_seg[i]; +- } +- dprintk("nbseg_diff = %X (%d)", seg_diff_mask, seg_diff_mask); +- +- state->differential_constellation = (seg_diff_mask != 0); +- if (state->revision != 0x8090) +- dib8000_set_diversity_in(state->fe[0], state->diversity_onoff); +- else +- dib8096p_set_diversity_in(state->fe[0], state->diversity_onoff); +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) +- seg_mask13 = 0x00E0; +- else // 1-segment +- seg_mask13 = 0x0040; +- } else +- seg_mask13 = 0x1fff; +- +- // WRITE: Mode & Diff mask +- dib8000_write_word(state, 0, (mode << 13) | seg_diff_mask); +- +- if ((seg_diff_mask) || (state->fe[0]->dtv_property_cache.isdbt_sb_mode)) +- dib8000_write_word(state, 268, (dib8000_read_word(state, 268) & 0xF9FF) | 0x0200); +- else +- dib8000_write_word(state, 268, (2 << 9) | 39); //init value +- +- // ---- SMALL ---- +- // P_small_seg_diff +- dib8000_write_word(state, 352, seg_diff_mask); // ADDR 352 +- +- dib8000_write_word(state, 353, seg_mask13); // ADDR 353 +- +-/* // P_small_narrow_band=0, P_small_last_seg=13, P_small_offset_num_car=5 */ +- +- // ---- SMALL ---- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- switch (state->fe[0]->dtv_property_cache.transmission_mode) { +- case TRANSMISSION_MODE_2K: +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) +- ncoeff = coeff_2k_sb_1seg_dqpsk; +- else // QPSK or QAM +- ncoeff = coeff_2k_sb_1seg; +- } else { // 3-segments +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) +- ncoeff = coeff_2k_sb_3seg_0dqpsk_1dqpsk; +- else // QPSK or QAM on external segments +- ncoeff = coeff_2k_sb_3seg_0dqpsk; +- } else { // QPSK or QAM on central segment +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) +- ncoeff = coeff_2k_sb_3seg_1dqpsk; +- else // QPSK or QAM on external segments +- ncoeff = coeff_2k_sb_3seg; +- } +- } +- break; +- +- case TRANSMISSION_MODE_4K: +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) +- ncoeff = coeff_4k_sb_1seg_dqpsk; +- else // QPSK or QAM +- ncoeff = coeff_4k_sb_1seg; +- } else { // 3-segments +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { +- ncoeff = coeff_4k_sb_3seg_0dqpsk_1dqpsk; +- } else { // QPSK or QAM on external segments +- ncoeff = coeff_4k_sb_3seg_0dqpsk; +- } +- } else { // QPSK or QAM on central segment +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { +- ncoeff = coeff_4k_sb_3seg_1dqpsk; +- } else // QPSK or QAM on external segments +- ncoeff = coeff_4k_sb_3seg; +- } +- } +- break; +- +- case TRANSMISSION_MODE_AUTO: +- case TRANSMISSION_MODE_8K: +- default: +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) +- ncoeff = coeff_8k_sb_1seg_dqpsk; +- else // QPSK or QAM +- ncoeff = coeff_8k_sb_1seg; +- } else { // 3-segments +- if (state->fe[0]->dtv_property_cache.layer[0].modulation == DQPSK) { +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { +- ncoeff = coeff_8k_sb_3seg_0dqpsk_1dqpsk; +- } else { // QPSK or QAM on external segments +- ncoeff = coeff_8k_sb_3seg_0dqpsk; +- } +- } else { // QPSK or QAM on central segment +- if (state->fe[0]->dtv_property_cache.layer[1].modulation == DQPSK) { +- ncoeff = coeff_8k_sb_3seg_1dqpsk; +- } else // QPSK or QAM on external segments +- ncoeff = coeff_8k_sb_3seg; +- } +- } +- break; +- } +- for (i = 0; i < 8; i++) +- dib8000_write_word(state, 343 + i, ncoeff[i]); +- } +- +- // P_small_coef_ext_enable=ISDB-Tsb, P_small_narrow_band=ISDB-Tsb, P_small_last_seg=13, P_small_offset_num_car=5 +- dib8000_write_word(state, 351, +- (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 9) | (state->fe[0]->dtv_property_cache.isdbt_sb_mode << 8) | (13 << 4) | 5); +- +- // ---- COFF ---- +- // Carloff, the most robust +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- +- // P_coff_cpil_alpha=4, P_coff_inh=0, P_coff_cpil_winlen=64 +- // P_coff_narrow_band=1, P_coff_square_val=1, P_coff_one_seg=~partial_rcpt, P_coff_use_tmcc=1, P_coff_use_ac=1 +- dib8000_write_word(state, 187, +- (4 << 12) | (0 << 11) | (63 << 5) | (0x3 << 3) | ((~state->fe[0]->dtv_property_cache.isdbt_partial_reception & 1) << 2) +- | 0x3); +- +-/* // P_small_coef_ext_enable = 1 */ +-/* dib8000_write_word(state, 351, dib8000_read_word(state, 351) | 0x200); */ +- +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +- +- // P_coff_winlen=63, P_coff_thres_lock=15, P_coff_one_seg_width= (P_mode == 3) , P_coff_one_seg_sym= (P_mode-1) +- if (mode == 3) +- dib8000_write_word(state, 180, 0x1fcf | ((mode - 1) << 14)); +- else +- dib8000_write_word(state, 180, 0x0fcf | ((mode - 1) << 14)); +- // P_ctrl_corm_thres4pre_freq_inh=1,P_ctrl_pre_freq_mode_sat=1, +- // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 5, P_pre_freq_win_len=4 +- dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (5 << 5) | 4); +- // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 +- dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); +- // P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 +- dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); +- +- // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k +- dib8000_write_word(state, 181, 300); +- dib8000_write_word(state, 182, 150); +- dib8000_write_word(state, 183, 80); +- dib8000_write_word(state, 184, 300); +- dib8000_write_word(state, 185, 150); +- dib8000_write_word(state, 186, 80); +- } else { // Sound Broadcasting mode 3 seg +- // P_coff_one_seg_sym= 1, P_coff_one_seg_width= 1, P_coff_winlen=63, P_coff_thres_lock=15 +- /* if (mode == 3) */ +- /* dib8000_write_word(state, 180, 0x2fca | ((0) << 14)); */ +- /* else */ +- /* dib8000_write_word(state, 180, 0x2fca | ((1) << 14)); */ +- dib8000_write_word(state, 180, 0x1fcf | (1 << 14)); +- +- // P_ctrl_corm_thres4pre_freq_inh = 1, P_ctrl_pre_freq_mode_sat=1, +- // P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 4, P_pre_freq_win_len=4 +- dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (4 << 5) | 4); +- // P_ctrl_pre_freq_win_len=16, P_ctrl_pre_freq_thres_lockin=8 +- dib8000_write_word(state, 340, (16 << 6) | (8 << 0)); +- //P_ctrl_pre_freq_thres_lockout=6, P_small_use_tmcc/ac/cp=1 +- dib8000_write_word(state, 341, (6 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); +- +- // P_coff_corthres_8k, 4k, 2k and P_coff_cpilthres_8k, 4k, 2k +- dib8000_write_word(state, 181, 350); +- dib8000_write_word(state, 182, 300); +- dib8000_write_word(state, 183, 250); +- dib8000_write_word(state, 184, 350); +- dib8000_write_word(state, 185, 300); +- dib8000_write_word(state, 186, 250); +- } +- +- } else if (state->isdbt_cfg_loaded == 0) { // if not Sound Broadcasting mode : put default values for 13 segments +- dib8000_write_word(state, 180, (16 << 6) | 9); +- dib8000_write_word(state, 187, (4 << 12) | (8 << 5) | 0x2); +- coff_pow = 0x2800; +- for (i = 0; i < 6; i++) +- dib8000_write_word(state, 181 + i, coff_pow); +- +- // P_ctrl_corm_thres4pre_freq_inh=1, P_ctrl_pre_freq_mode_sat=1, +- // P_ctrl_pre_freq_mode_sat=1, P_ctrl_pre_freq_inh=0, P_ctrl_pre_freq_step = 3, P_pre_freq_win_len=1 +- dib8000_write_word(state, 338, (1 << 12) | (1 << 10) | (0 << 9) | (3 << 5) | 1); +- +- // P_ctrl_pre_freq_win_len=8, P_ctrl_pre_freq_thres_lockin=6 +- dib8000_write_word(state, 340, (8 << 6) | (6 << 0)); +- // P_ctrl_pre_freq_thres_lockout=4, P_small_use_tmcc/ac/cp=1 +- dib8000_write_word(state, 341, (4 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); +- } +- // ---- FFT ---- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 && state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) +- dib8000_write_word(state, 178, 64); // P_fft_powrange=64 +- else +- dib8000_write_word(state, 178, 32); // P_fft_powrange=32 +- +- /* make the cpil_coff_lock more robust but slower p_coff_winlen +- * 6bits; p_coff_thres_lock 6bits (for coff lock if needed) +- */ +- /* if ( ( nbseg_diff>0)&&(nbseg_diff<13)) +- dib8000_write_word(state, 187, (dib8000_read_word(state, 187) & 0xfffb) | (1 << 3)); */ +- +- dib8000_write_word(state, 189, ~seg_mask13 | seg_diff_mask); /* P_lmod4_seg_inh */ +- dib8000_write_word(state, 192, ~seg_mask13 | seg_diff_mask); /* P_pha3_seg_inh */ +- dib8000_write_word(state, 225, ~seg_mask13 | seg_diff_mask); /* P_tac_seg_inh */ +- if ((!state->fe[0]->dtv_property_cache.isdbt_sb_mode) && (state->cfg.pll->ifreq == 0)) +- dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask | 0x40); /* P_equal_noise_seg_inh */ +- else +- dib8000_write_word(state, 266, ~seg_mask13 | seg_diff_mask); /* P_equal_noise_seg_inh */ +- dib8000_write_word(state, 287, ~seg_mask13 | 0x1000); /* P_tmcc_seg_inh */ +- //dib8000_write_word(state, 288, ~seg_mask13 | seg_diff_mask); /* P_tmcc_seg_eq_inh */ +- if (!autosearching) +- dib8000_write_word(state, 288, (~seg_mask13 | seg_diff_mask) & 0x1fff); /* P_tmcc_seg_eq_inh */ +- else +- dib8000_write_word(state, 288, 0x1fff); //disable equalisation of the tmcc when autosearch to be able to find the DQPSK channels. +- dprintk("287 = %X (%d)", ~seg_mask13 | 0x1000, ~seg_mask13 | 0x1000); +- +- dib8000_write_word(state, 211, seg_mask13 & (~seg_diff_mask)); /* P_des_seg_enabled */ +- +- /* offset loop parameters */ +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) +- /* P_timf_alpha = (11-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ +- dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x40); +- +- else // Sound Broadcasting mode 3 seg +- /* P_timf_alpha = (10-P_mode), P_corm_alpha=6, P_corm_thres=0x80 */ +- dib8000_write_word(state, 32, ((10 - mode) << 12) | (6 << 8) | 0x60); +- } else +- // TODO in 13 seg, timf_alpha can always be the same or not ? +- /* P_timf_alpha = (9-P_mode, P_corm_alpha=6, P_corm_thres=0x80 */ +- dib8000_write_word(state, 32, ((9 - mode) << 12) | (6 << 8) | 0x80); +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) +- /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (11-P_mode) */ +- dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (10 - mode)); +- +- else // Sound Broadcasting mode 3 seg +- /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = (10-P_mode) */ +- dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (9 - mode)); +- } else +- /* P_ctrl_pha_off_max=3 P_ctrl_sfreq_inh =0 P_ctrl_sfreq_step = 9 */ +- dib8000_write_word(state, 37, (3 << 5) | (0 << 4) | (8 - mode)); +- +- /* P_dvsy_sync_wait - reuse mode */ +- switch (state->fe[0]->dtv_property_cache.transmission_mode) { +- case TRANSMISSION_MODE_8K: +- mode = 256; +- break; +- case TRANSMISSION_MODE_4K: +- mode = 128; +- break; +- default: +- case TRANSMISSION_MODE_2K: +- mode = 64; +- break; +- } +- if (state->cfg.diversity_delay == 0) +- mode = (mode * (1 << (guard)) * 3) / 2 + 48; // add 50% SFN margin + compensate for one DVSY-fifo +- else +- mode = (mode * (1 << (guard)) * 3) / 2 + state->cfg.diversity_delay; // add 50% SFN margin + compensate for DVSY-fifo +- mode <<= 4; +- dib8000_write_word(state, 273, (dib8000_read_word(state, 273) & 0x000f) | mode); +- +- /* channel estimation fine configuration */ +- switch (max_constellation) { +- case QAM_64: +- ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB +- coeff[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ +- coeff[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ +- coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- coeff[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ +- //if (!state->cfg.hostbus_diversity) //if diversity, we should prehaps use the configuration of the max_constallation -1 +- break; +- case QAM_16: +- ana_gain = 0x7; // -1 : avoid def_est saturation when ADC target is -16dB +- coeff[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ +- coeff[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ +- coeff[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ +- coeff[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ +- //if (!((state->cfg.hostbus_diversity) && (max_constellation == QAM_16))) +- break; +- default: +- ana_gain = 0; // 0 : goes along with ADC target at -22dB to keep good mobile performance and lock at sensitivity level +- coeff[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ +- coeff[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ +- coeff[2] = 0x0333; /* P_adp_regul_ext 0.1 */ +- coeff[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ +- break; +- } +- for (mode = 0; mode < 4; mode++) +- dib8000_write_word(state, 215 + mode, coeff[mode]); +- +- // update ana_gain depending on max constellation +- dib8000_write_word(state, 116, ana_gain); +- // update ADC target depending on ana_gain +- if (ana_gain) { // set -16dB ADC target for ana_gain=-1 +- for (i = 0; i < 10; i++) +- dib8000_write_word(state, 80 + i, adc_target_16dB[i]); +- } else { // set -22dB ADC target for ana_gain=0 +- for (i = 0; i < 10; i++) +- dib8000_write_word(state, 80 + i, adc_target_16dB[i] - 355); +- } +- +- // ---- ANA_FE ---- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 1) +- ana_fe = ana_fe_coeff_3seg; +- else // 1-segment +- ana_fe = ana_fe_coeff_1seg; +- } else +- ana_fe = ana_fe_coeff_13seg; +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1 || state->isdbt_cfg_loaded == 0) +- for (mode = 0; mode < 24; mode++) +- dib8000_write_word(state, 117 + mode, ana_fe[mode]); +- +- // ---- CHAN_BLK ---- +- for (i = 0; i < 13; i++) { +- if ((((~seg_diff_mask) >> i) & 1) == 1) { +- P_cfr_left_edge += (1 << i) * ((i == 0) || ((((seg_mask13 & (~seg_diff_mask)) >> (i - 1)) & 1) == 0)); +- P_cfr_right_edge += (1 << i) * ((i == 12) || ((((seg_mask13 & (~seg_diff_mask)) >> (i + 1)) & 1) == 0)); +- } +- } +- dib8000_write_word(state, 222, P_cfr_left_edge); // P_cfr_left_edge +- dib8000_write_word(state, 223, P_cfr_right_edge); // P_cfr_right_edge +- // "P_cspu_left_edge" not used => do not care +- // "P_cspu_right_edge" not used => do not care +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- dib8000_write_word(state, 228, 1); // P_2d_mode_byp=1 +- dib8000_write_word(state, 205, dib8000_read_word(state, 205) & 0xfff0); // P_cspu_win_cut = 0 +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0 +- && state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_2K) { +- //dib8000_write_word(state, 219, dib8000_read_word(state, 219) & 0xfffe); // P_adp_pass = 0 +- dib8000_write_word(state, 265, 15); // P_equal_noise_sel = 15 +- } +- } else if (state->isdbt_cfg_loaded == 0) { +- dib8000_write_word(state, 228, 0); // default value +- dib8000_write_word(state, 265, 31); // default value +- dib8000_write_word(state, 205, 0x200f); // init value +- } +- // ---- TMCC ---- +- for (i = 0; i < 3; i++) +- tmcc_pow += +- (((state->fe[0]->dtv_property_cache.layer[i].modulation == DQPSK) * 4 + 1) * state->fe[0]->dtv_property_cache.layer[i].segment_count); +- // Quantif of "P_tmcc_dec_thres_?k" is (0, 5+mode, 9); +- // Threshold is set at 1/4 of max power. +- tmcc_pow *= (1 << (9 - 2)); +- +- dib8000_write_word(state, 290, tmcc_pow); // P_tmcc_dec_thres_2k +- dib8000_write_word(state, 291, tmcc_pow); // P_tmcc_dec_thres_4k +- dib8000_write_word(state, 292, tmcc_pow); // P_tmcc_dec_thres_8k +- //dib8000_write_word(state, 287, (1 << 13) | 0x1000 ); +- // ---- PHA3 ---- +- +- if (state->isdbt_cfg_loaded == 0) +- dib8000_write_word(state, 250, 3285); /*p_2d_hspeed_thr0 */ +- +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) +- state->isdbt_cfg_loaded = 0; +- else +- state->isdbt_cfg_loaded = 1; +- +-} +- +-static int dib8000_autosearch_start(struct dvb_frontend *fe) +-{ +- u8 factor; +- u32 value; +- struct dib8000_state *state = fe->demodulator_priv; +- +- int slist = 0; +- +- state->fe[0]->dtv_property_cache.inversion = 0; +- if (!state->fe[0]->dtv_property_cache.isdbt_sb_mode) +- state->fe[0]->dtv_property_cache.layer[0].segment_count = 13; +- state->fe[0]->dtv_property_cache.layer[0].modulation = QAM_64; +- state->fe[0]->dtv_property_cache.layer[0].fec = FEC_2_3; +- state->fe[0]->dtv_property_cache.layer[0].interleaving = 0; +- +- //choose the right list, in sb, always do everything +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode) { +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; +- slist = 7; +- dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); +- } else { +- if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) { +- if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { +- slist = 7; +- dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 to have autosearch start ok with mode2 +- } else +- slist = 3; +- } else { +- if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) { +- slist = 2; +- dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 +- } else +- slist = 0; +- } +- +- if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; +- if (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; +- +- dprintk("using list for autosearch : %d", slist); +- dib8000_set_channel(state, (unsigned char)slist, 1); +- //dib8000_write_word(state, 0, (dib8000_read_word(state, 0) & 0x9fff) | (1 << 13)); // P_mode = 1 +- +- factor = 1; +- +- //set lock_mask values +- dib8000_write_word(state, 6, 0x4); +- dib8000_write_word(state, 7, 0x8); +- dib8000_write_word(state, 8, 0x1000); +- +- //set lock_mask wait time values +- value = 50 * state->cfg.pll->internal * factor; +- dib8000_write_word(state, 11, (u16) ((value >> 16) & 0xffff)); // lock0 wait time +- dib8000_write_word(state, 12, (u16) (value & 0xffff)); // lock0 wait time +- value = 100 * state->cfg.pll->internal * factor; +- dib8000_write_word(state, 13, (u16) ((value >> 16) & 0xffff)); // lock1 wait time +- dib8000_write_word(state, 14, (u16) (value & 0xffff)); // lock1 wait time +- value = 1000 * state->cfg.pll->internal * factor; +- dib8000_write_word(state, 15, (u16) ((value >> 16) & 0xffff)); // lock2 wait time +- dib8000_write_word(state, 16, (u16) (value & 0xffff)); // lock2 wait time +- +- value = dib8000_read_word(state, 0); +- dib8000_write_word(state, 0, (u16) ((1 << 15) | value)); +- dib8000_read_word(state, 1284); // reset the INT. n_irq_pending +- dib8000_write_word(state, 0, (u16) value); +- +- } +- +- return 0; +-} +- +-static int dib8000_autosearch_irq(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 irq_pending = dib8000_read_word(state, 1284); +- +- if (irq_pending & 0x1) { // failed +- dprintk("dib8000_autosearch_irq failed"); +- return 1; +- } +- +- if (irq_pending & 0x2) { // succeeded +- dprintk("dib8000_autosearch_irq succeeded"); +- return 2; +- } +- +- return 0; // still pending +-} +- +-static int dib8000_tune(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- int ret = 0; +- u16 lock, value, mode = fft_to_mode(state); +- +- // we are already tuned - just resuming from suspend +- if (state == NULL) +- return -EINVAL; +- +- dib8000_set_bandwidth(fe, state->fe[0]->dtv_property_cache.bandwidth_hz / 1000); +- dib8000_set_channel(state, 0, 0); +- +- // restart demod +- ret |= dib8000_write_word(state, 770, 0x4000); +- ret |= dib8000_write_word(state, 770, 0x0000); +- msleep(45); +- +- /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3 */ +- /* ret |= dib8000_write_word(state, 29, (0 << 9) | (4 << 5) | (0 << 4) | (3 << 0) ); workaround inh_isi stays at 1 */ +- +- // never achieved a lock before - wait for timfreq to update +- if (state->timf == 0) { +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) +- msleep(300); +- else // Sound Broadcasting mode 3 seg +- msleep(500); +- } else // 13 seg +- msleep(200); +- } +- if (state->fe[0]->dtv_property_cache.isdbt_sb_mode == 1) { +- if (state->fe[0]->dtv_property_cache.isdbt_partial_reception == 0) { +- +- /* P_timf_alpha = (13-P_mode) , P_corm_alpha=6, P_corm_thres=0x40 alpha to check on board */ +- dib8000_write_word(state, 32, ((13 - mode) << 12) | (6 << 8) | 0x40); +- //dib8000_write_word(state, 32, (8 << 12) | (6 << 8) | 0x80); +- +- /* P_ctrl_sfreq_step= (12-P_mode) P_ctrl_sfreq_inh =0 P_ctrl_pha_off_max */ +- ret |= dib8000_write_word(state, 37, (12 - mode) | ((5 + mode) << 5)); +- +- } else { // Sound Broadcasting mode 3 seg +- +- /* P_timf_alpha = (12-P_mode) , P_corm_alpha=6, P_corm_thres=0x60 alpha to check on board */ +- dib8000_write_word(state, 32, ((12 - mode) << 12) | (6 << 8) | 0x60); +- +- ret |= dib8000_write_word(state, 37, (11 - mode) | ((5 + mode) << 5)); +- } +- +- } else { // 13 seg +- /* P_timf_alpha = 8 , P_corm_alpha=6, P_corm_thres=0x80 alpha to check on board */ +- dib8000_write_word(state, 32, ((11 - mode) << 12) | (6 << 8) | 0x80); +- +- ret |= dib8000_write_word(state, 37, (10 - mode) | ((5 + mode) << 5)); +- +- } +- +- // we achieved a coff_cpil_lock - it's time to update the timf +- if (state->revision != 0x8090) +- lock = dib8000_read_word(state, 568); +- else +- lock = dib8000_read_word(state, 570); +- if ((lock >> 11) & 0x1) +- dib8000_update_timf(state); +- +- //now that tune is finished, lock0 should lock on fec_mpeg to output this lock on MP_LOCK. It's changed in autosearch start +- dib8000_write_word(state, 6, 0x200); +- +- if (state->revision == 0x8002) { +- value = dib8000_read_word(state, 903); +- dib8000_write_word(state, 903, value & ~(1 << 3)); +- msleep(1); +- dib8000_write_word(state, 903, value | (1 << 3)); +- } +- +- return ret; +-} +- +-static int dib8000_wakeup(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- int ret; +- +- dib8000_set_power_mode(state, DIB8000_POWER_ALL); +- dib8000_set_adc_state(state, DIBX000_ADC_ON); +- if (dib8000_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) +- dprintk("could not start Slow ADC"); +- +- if (state->revision != 0x8090) +- dib8000_sad_calib(state); +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- ret = state->fe[index_frontend]->ops.init(state->fe[index_frontend]); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int dib8000_sleep(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- int ret; +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); +- if (ret < 0) +- return ret; +- } +- +- if (state->revision != 0x8090) +- dib8000_set_output_mode(fe, OUTMODE_HIGH_Z); +- dib8000_set_power_mode(state, DIB8000_POWER_INTERFACE_ONLY); +- return dib8000_set_adc_state(state, DIBX000_SLOW_ADC_OFF) | dib8000_set_adc_state(state, DIBX000_ADC_OFF); +-} +- +-enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- return state->tune_state; +-} +-EXPORT_SYMBOL(dib8000_get_tune_state); +- +-int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- state->tune_state = tune_state; +- return 0; +-} +-EXPORT_SYMBOL(dib8000_set_tune_state); +- +-static int dib8000_get_frontend(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 i, val = 0; +- fe_status_t stat; +- u8 index_frontend, sub_index_frontend; +- +- fe->dtv_property_cache.bandwidth_hz = 6000000; +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); +- if (stat&FE_HAS_SYNC) { +- dprintk("TMCC lock on the slave%i", index_frontend); +- /* synchronize the cache with the other frontends */ +- state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]); +- for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); sub_index_frontend++) { +- if (sub_index_frontend != index_frontend) { +- state->fe[sub_index_frontend]->dtv_property_cache.isdbt_sb_mode = state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode; +- state->fe[sub_index_frontend]->dtv_property_cache.inversion = state->fe[index_frontend]->dtv_property_cache.inversion; +- state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = state->fe[index_frontend]->dtv_property_cache.transmission_mode; +- state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = state->fe[index_frontend]->dtv_property_cache.guard_interval; +- state->fe[sub_index_frontend]->dtv_property_cache.isdbt_partial_reception = state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception; +- for (i = 0; i < 3; i++) { +- state->fe[sub_index_frontend]->dtv_property_cache.layer[i].segment_count = state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count; +- state->fe[sub_index_frontend]->dtv_property_cache.layer[i].interleaving = state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving; +- state->fe[sub_index_frontend]->dtv_property_cache.layer[i].fec = state->fe[index_frontend]->dtv_property_cache.layer[i].fec; +- state->fe[sub_index_frontend]->dtv_property_cache.layer[i].modulation = state->fe[index_frontend]->dtv_property_cache.layer[i].modulation; +- } +- } +- } +- return 0; +- } +- } +- +- fe->dtv_property_cache.isdbt_sb_mode = dib8000_read_word(state, 508) & 0x1; +- +- if (state->revision == 0x8090) +- val = dib8000_read_word(state, 572); +- else +- val = dib8000_read_word(state, 570); +- fe->dtv_property_cache.inversion = (val & 0x40) >> 6; +- switch ((val & 0x30) >> 4) { +- case 1: +- fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 3: +- default: +- fe->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; +- break; +- } +- +- switch (val & 0x3) { +- case 0: +- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; +- dprintk("dib8000_get_frontend GI = 1/32 "); +- break; +- case 1: +- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; +- dprintk("dib8000_get_frontend GI = 1/16 "); +- break; +- case 2: +- dprintk("dib8000_get_frontend GI = 1/8 "); +- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- dprintk("dib8000_get_frontend GI = 1/4 "); +- fe->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- val = dib8000_read_word(state, 505); +- fe->dtv_property_cache.isdbt_partial_reception = val & 1; +- dprintk("dib8000_get_frontend : partial_reception = %d ", fe->dtv_property_cache.isdbt_partial_reception); +- +- for (i = 0; i < 3; i++) { +- val = dib8000_read_word(state, 493 + i); +- fe->dtv_property_cache.layer[i].segment_count = val & 0x0F; +- dprintk("dib8000_get_frontend : Layer %d segments = %d ", i, fe->dtv_property_cache.layer[i].segment_count); +- +- val = dib8000_read_word(state, 499 + i); +- fe->dtv_property_cache.layer[i].interleaving = val & 0x3; +- dprintk("dib8000_get_frontend : Layer %d time_intlv = %d ", i, fe->dtv_property_cache.layer[i].interleaving); +- +- val = dib8000_read_word(state, 481 + i); +- switch (val & 0x7) { +- case 1: +- fe->dtv_property_cache.layer[i].fec = FEC_1_2; +- dprintk("dib8000_get_frontend : Layer %d Code Rate = 1/2 ", i); +- break; +- case 2: +- fe->dtv_property_cache.layer[i].fec = FEC_2_3; +- dprintk("dib8000_get_frontend : Layer %d Code Rate = 2/3 ", i); +- break; +- case 3: +- fe->dtv_property_cache.layer[i].fec = FEC_3_4; +- dprintk("dib8000_get_frontend : Layer %d Code Rate = 3/4 ", i); +- break; +- case 5: +- fe->dtv_property_cache.layer[i].fec = FEC_5_6; +- dprintk("dib8000_get_frontend : Layer %d Code Rate = 5/6 ", i); +- break; +- default: +- fe->dtv_property_cache.layer[i].fec = FEC_7_8; +- dprintk("dib8000_get_frontend : Layer %d Code Rate = 7/8 ", i); +- break; +- } +- +- val = dib8000_read_word(state, 487 + i); +- switch (val & 0x3) { +- case 0: +- dprintk("dib8000_get_frontend : Layer %d DQPSK ", i); +- fe->dtv_property_cache.layer[i].modulation = DQPSK; +- break; +- case 1: +- fe->dtv_property_cache.layer[i].modulation = QPSK; +- dprintk("dib8000_get_frontend : Layer %d QPSK ", i); +- break; +- case 2: +- fe->dtv_property_cache.layer[i].modulation = QAM_16; +- dprintk("dib8000_get_frontend : Layer %d QAM16 ", i); +- break; +- case 3: +- default: +- dprintk("dib8000_get_frontend : Layer %d QAM64 ", i); +- fe->dtv_property_cache.layer[i].modulation = QAM_64; +- break; +- } +- } +- +- /* synchronize the cache with the other frontends */ +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->dtv_property_cache.isdbt_sb_mode = fe->dtv_property_cache.isdbt_sb_mode; +- state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; +- state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; +- state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; +- state->fe[index_frontend]->dtv_property_cache.isdbt_partial_reception = fe->dtv_property_cache.isdbt_partial_reception; +- for (i = 0; i < 3; i++) { +- state->fe[index_frontend]->dtv_property_cache.layer[i].segment_count = fe->dtv_property_cache.layer[i].segment_count; +- state->fe[index_frontend]->dtv_property_cache.layer[i].interleaving = fe->dtv_property_cache.layer[i].interleaving; +- state->fe[index_frontend]->dtv_property_cache.layer[i].fec = fe->dtv_property_cache.layer[i].fec; +- state->fe[index_frontend]->dtv_property_cache.layer[i].modulation = fe->dtv_property_cache.layer[i].modulation; +- } +- } +- return 0; +-} +- +-static int dib8000_set_frontend(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 nbr_pending, exit_condition, index_frontend; +- s8 index_frontend_success = -1; +- int time, ret; +- int time_slave = FE_CALLBACK_TIME_NEVER; +- +- if (state->fe[0]->dtv_property_cache.frequency == 0) { +- dprintk("dib8000: must at least specify frequency "); +- return 0; +- } +- +- if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { +- dprintk("dib8000: no bandwidth specified, set to default "); +- state->fe[0]->dtv_property_cache.bandwidth_hz = 6000000; +- } +- +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- /* synchronization of the cache */ +- state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_ISDBT; +- memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); +- +- if (state->revision != 0x8090) +- dib8000_set_output_mode(state->fe[index_frontend], +- OUTMODE_HIGH_Z); +- else +- dib8096p_set_output_mode(state->fe[index_frontend], +- OUTMODE_HIGH_Z); +- if (state->fe[index_frontend]->ops.tuner_ops.set_params) +- state->fe[index_frontend]->ops.tuner_ops.set_params(state->fe[index_frontend]); +- +- dib8000_set_tune_state(state->fe[index_frontend], CT_AGC_START); +- } +- +- /* start up the AGC */ +- do { +- time = dib8000_agc_startup(state->fe[0]); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- time_slave = dib8000_agc_startup(state->fe[index_frontend]); +- if (time == FE_CALLBACK_TIME_NEVER) +- time = time_slave; +- else if ((time_slave != FE_CALLBACK_TIME_NEVER) && (time_slave > time)) +- time = time_slave; +- } +- if (time != FE_CALLBACK_TIME_NEVER) +- msleep(time / 10); +- else +- break; +- exit_condition = 1; +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- if (dib8000_get_tune_state(state->fe[index_frontend]) != CT_AGC_STOP) { +- exit_condition = 0; +- break; +- } +- } +- } while (exit_condition == 0); +- +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- dib8000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); +- +- if ((state->fe[0]->dtv_property_cache.delivery_system != SYS_ISDBT) || +- (state->fe[0]->dtv_property_cache.inversion == INVERSION_AUTO) || +- (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO) || +- (state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO) || +- (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) != 0) && +- (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0xff) && +- (state->fe[0]->dtv_property_cache.layer[0].segment_count != 0) && +- ((state->fe[0]->dtv_property_cache.layer[0].modulation == QAM_AUTO) || +- (state->fe[0]->dtv_property_cache.layer[0].fec == FEC_AUTO))) || +- (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 1)) != 0) && +- (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0xff) && +- (state->fe[0]->dtv_property_cache.layer[1].segment_count != 0) && +- ((state->fe[0]->dtv_property_cache.layer[1].modulation == QAM_AUTO) || +- (state->fe[0]->dtv_property_cache.layer[1].fec == FEC_AUTO))) || +- (((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 2)) != 0) && +- (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0xff) && +- (state->fe[0]->dtv_property_cache.layer[2].segment_count != 0) && +- ((state->fe[0]->dtv_property_cache.layer[2].modulation == QAM_AUTO) || +- (state->fe[0]->dtv_property_cache.layer[2].fec == FEC_AUTO))) || +- (((state->fe[0]->dtv_property_cache.layer[0].segment_count == 0) || +- ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (1 << 0)) == 0)) && +- ((state->fe[0]->dtv_property_cache.layer[1].segment_count == 0) || +- ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (2 << 0)) == 0)) && +- ((state->fe[0]->dtv_property_cache.layer[2].segment_count == 0) || ((state->fe[0]->dtv_property_cache.isdbt_layer_enabled & (3 << 0)) == 0)))) { +- int i = 100; +- u8 found = 0; +- u8 tune_failed = 0; +- +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- dib8000_set_bandwidth(state->fe[index_frontend], fe->dtv_property_cache.bandwidth_hz / 1000); +- dib8000_autosearch_start(state->fe[index_frontend]); +- } +- +- do { +- msleep(20); +- nbr_pending = 0; +- exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- if (((tune_failed >> index_frontend) & 0x1) == 0) { +- found = dib8000_autosearch_irq(state->fe[index_frontend]); +- switch (found) { +- case 0: /* tune pending */ +- nbr_pending++; +- break; +- case 2: +- dprintk("autosearch succeed on the frontend%i", index_frontend); +- exit_condition = 2; +- index_frontend_success = index_frontend; +- break; +- default: +- dprintk("unhandled autosearch result"); +- case 1: +- tune_failed |= (1 << index_frontend); +- dprintk("autosearch failed for the frontend%i", index_frontend); +- break; +- } +- } +- } +- +- /* if all tune are done and no success, exit: tune failed */ +- if ((nbr_pending == 0) && (exit_condition == 0)) +- exit_condition = 1; +- } while ((exit_condition == 0) && i--); +- +- if (exit_condition == 1) { /* tune failed */ +- dprintk("tune failed"); +- return 0; +- } +- +- dprintk("tune success on frontend%i", index_frontend_success); +- +- dib8000_get_frontend(fe); +- } +- +- for (index_frontend = 0, ret = 0; (ret >= 0) && (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- ret = dib8000_tune(state->fe[index_frontend]); +- +- /* set output mode and diversity input */ +- if (state->revision != 0x8090) { +- dib8000_set_output_mode(state->fe[0], state->cfg.output_mode); +- for (index_frontend = 1; +- (index_frontend < MAX_NUMBER_OF_FRONTENDS) && +- (state->fe[index_frontend] != NULL); +- index_frontend++) { +- dib8000_set_output_mode(state->fe[index_frontend], +- OUTMODE_DIVERSITY); +- dib8000_set_diversity_in(state->fe[index_frontend-1], 1); +- } +- +- /* turn off the diversity of the last chip */ +- dib8000_set_diversity_in(state->fe[index_frontend-1], 0); +- } else { +- dib8096p_set_output_mode(state->fe[0], state->cfg.output_mode); +- if (state->cfg.enMpegOutput == 0) { +- dib8096p_setDibTxMux(state, MPEG_ON_DIBTX); +- dib8096p_setHostBusMux(state, DIBTX_ON_HOSTBUS); +- } +- for (index_frontend = 1; +- (index_frontend < MAX_NUMBER_OF_FRONTENDS) && +- (state->fe[index_frontend] != NULL); +- index_frontend++) { +- dib8096p_set_output_mode(state->fe[index_frontend], +- OUTMODE_DIVERSITY); +- dib8096p_set_diversity_in(state->fe[index_frontend-1], 1); +- } +- +- /* turn off the diversity of the last chip */ +- dib8096p_set_diversity_in(state->fe[index_frontend-1], 0); +- } +- +- return ret; +-} +- +-static u16 dib8000_read_lock(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- if (state->revision == 0x8090) +- return dib8000_read_word(state, 570); +- return dib8000_read_word(state, 568); +-} +- +-static int dib8000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u16 lock_slave = 0, lock; +- u8 index_frontend; +- +- if (state->revision == 0x8090) +- lock = dib8000_read_word(state, 570); +- else +- lock = dib8000_read_word(state, 568); +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- lock_slave |= dib8000_read_lock(state->fe[index_frontend]); +- +- *stat = 0; +- +- if (((lock >> 13) & 1) || ((lock_slave >> 13) & 1)) +- *stat |= FE_HAS_SIGNAL; +- +- if (((lock >> 8) & 1) || ((lock_slave >> 8) & 1)) /* Equal */ +- *stat |= FE_HAS_CARRIER; +- +- if ((((lock >> 1) & 0xf) == 0xf) || (((lock_slave >> 1) & 0xf) == 0xf)) /* TMCC_SYNC */ +- *stat |= FE_HAS_SYNC; +- +- if ((((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) && ((lock >> 5) & 7)) /* FEC MPEG */ +- *stat |= FE_HAS_LOCK; +- +- if (((lock >> 12) & 1) || ((lock_slave >> 12) & 1)) { +- lock = dib8000_read_word(state, 554); /* Viterbi Layer A */ +- if (lock & 0x01) +- *stat |= FE_HAS_VITERBI; +- +- lock = dib8000_read_word(state, 555); /* Viterbi Layer B */ +- if (lock & 0x01) +- *stat |= FE_HAS_VITERBI; +- +- lock = dib8000_read_word(state, 556); /* Viterbi Layer C */ +- if (lock & 0x01) +- *stat |= FE_HAS_VITERBI; +- } +- +- return 0; +-} +- +-static int dib8000_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- /* 13 segments */ +- if (state->revision == 0x8090) +- *ber = (dib8000_read_word(state, 562) << 16) | +- dib8000_read_word(state, 563); +- else +- *ber = (dib8000_read_word(state, 560) << 16) | +- dib8000_read_word(state, 561); +- return 0; +-} +- +-static int dib8000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- /* packet error on 13 seg */ +- if (state->revision == 0x8090) +- *unc = dib8000_read_word(state, 567); +- else +- *unc = dib8000_read_word(state, 565); +- return 0; +-} +- +-static int dib8000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- u16 val; +- +- *strength = 0; +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); +- if (val > 65535 - *strength) +- *strength = 65535; +- else +- *strength += val; +- } +- +- val = 65535 - dib8000_read_word(state, 390); +- if (val > 65535 - *strength) +- *strength = 65535; +- else +- *strength += val; +- return 0; +-} +- +-static u32 dib8000_get_snr(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u32 n, s, exp; +- u16 val; +- +- if (state->revision != 0x8090) +- val = dib8000_read_word(state, 542); +- else +- val = dib8000_read_word(state, 544); +- n = (val >> 6) & 0xff; +- exp = (val & 0x3f); +- if ((exp & 0x20) != 0) +- exp -= 0x40; +- n <<= exp+16; +- +- if (state->revision != 0x8090) +- val = dib8000_read_word(state, 543); +- else +- val = dib8000_read_word(state, 545); +- s = (val >> 6) & 0xff; +- exp = (val & 0x3f); +- if ((exp & 0x20) != 0) +- exp -= 0x40; +- s <<= exp+16; +- +- if (n > 0) { +- u32 t = (s/n) << 16; +- return t + ((s << 16) - n*t) / n; +- } +- return 0xffffffff; +-} +- +-static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- u32 snr_master; +- +- snr_master = dib8000_get_snr(fe); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- snr_master += dib8000_get_snr(state->fe[index_frontend]); +- +- if ((snr_master >> 16) != 0) { +- snr_master = 10*intlog10(snr_master>>16); +- *snr = snr_master / ((1 << 24) / 10); +- } +- else +- *snr = 0; +- +- return 0; +-} +- +-int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend = 1; +- +- while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) +- index_frontend++; +- if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { +- dprintk("set slave fe %p to index %i", fe_slave, index_frontend); +- state->fe[index_frontend] = fe_slave; +- return 0; +- } +- +- dprintk("too many slave frontend"); +- return -ENOMEM; +-} +-EXPORT_SYMBOL(dib8000_set_slave_frontend); +- +-int dib8000_remove_slave_frontend(struct dvb_frontend *fe) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- u8 index_frontend = 1; +- +- while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) +- index_frontend++; +- if (index_frontend != 1) { +- dprintk("remove slave fe %p (index %i)", state->fe[index_frontend-1], index_frontend-1); +- state->fe[index_frontend] = NULL; +- return 0; +- } +- +- dprintk("no frontend to be removed"); +- return -ENODEV; +-} +-EXPORT_SYMBOL(dib8000_remove_slave_frontend); +- +-struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +-{ +- struct dib8000_state *state = fe->demodulator_priv; +- +- if (slave_index >= MAX_NUMBER_OF_FRONTENDS) +- return NULL; +- return state->fe[slave_index]; +-} +-EXPORT_SYMBOL(dib8000_get_slave_frontend); +- +- +-int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, +- u8 default_addr, u8 first_addr, u8 is_dib8096p) +-{ +- int k = 0, ret = 0; +- u8 new_addr = 0; +- struct i2c_device client = {.adap = host }; +- +- client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); +- if (!client.i2c_write_buffer) { +- dprintk("%s: not enough memory", __func__); +- return -ENOMEM; +- } +- client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); +- if (!client.i2c_read_buffer) { +- dprintk("%s: not enough memory", __func__); +- ret = -ENOMEM; +- goto error_memory_read; +- } +- client.i2c_buffer_lock = kzalloc(sizeof(struct mutex), GFP_KERNEL); +- if (!client.i2c_buffer_lock) { +- dprintk("%s: not enough memory", __func__); +- ret = -ENOMEM; +- goto error_memory_lock; +- } +- mutex_init(client.i2c_buffer_lock); +- +- for (k = no_of_demods - 1; k >= 0; k--) { +- /* designated i2c address */ +- new_addr = first_addr + (k << 1); +- +- client.addr = new_addr; +- if (!is_dib8096p) +- dib8000_i2c_write16(&client, 1287, 0x0003); /* sram lead in, rdy */ +- if (dib8000_identify(&client) == 0) { +- /* sram lead in, rdy */ +- if (!is_dib8096p) +- dib8000_i2c_write16(&client, 1287, 0x0003); +- client.addr = default_addr; +- if (dib8000_identify(&client) == 0) { +- dprintk("#%d: not identified", k); +- ret = -EINVAL; +- goto error; +- } +- } +- +- /* start diversity to pull_down div_str - just for i2c-enumeration */ +- dib8000_i2c_write16(&client, 1286, (1 << 10) | (4 << 6)); +- +- /* set new i2c address and force divstart */ +- dib8000_i2c_write16(&client, 1285, (new_addr << 2) | 0x2); +- client.addr = new_addr; +- dib8000_identify(&client); +- +- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); +- } +- +- for (k = 0; k < no_of_demods; k++) { +- new_addr = first_addr | (k << 1); +- client.addr = new_addr; +- +- // unforce divstr +- dib8000_i2c_write16(&client, 1285, new_addr << 2); +- +- /* deactivate div - it was just for i2c-enumeration */ +- dib8000_i2c_write16(&client, 1286, 0); +- } +- +-error: +- kfree(client.i2c_buffer_lock); +-error_memory_lock: +- kfree(client.i2c_read_buffer); +-error_memory_read: +- kfree(client.i2c_write_buffer); +- +- return ret; +-} +- +-EXPORT_SYMBOL(dib8000_i2c_enumeration); +-static int dib8000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- tune->step_size = 0; +- tune->max_drift = 0; +- return 0; +-} +- +-static void dib8000_release(struct dvb_frontend *fe) +-{ +- struct dib8000_state *st = fe->demodulator_priv; +- u8 index_frontend; +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) +- dvb_frontend_detach(st->fe[index_frontend]); +- +- dibx000_exit_i2c_master(&st->i2c_master); +- i2c_del_adapter(&st->dib8096p_tuner_adap); +- kfree(st->fe[0]); +- kfree(st); +-} +- +-struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +-{ +- struct dib8000_state *st = fe->demodulator_priv; +- return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +-} +- +-EXPORT_SYMBOL(dib8000_get_i2c_master); +- +-int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- struct dib8000_state *st = fe->demodulator_priv; +- u16 val = dib8000_read_word(st, 299) & 0xffef; +- val |= (onoff & 0x1) << 4; +- +- dprintk("pid filter enabled %d", onoff); +- return dib8000_write_word(st, 299, val); +-} +-EXPORT_SYMBOL(dib8000_pid_filter_ctrl); +- +-int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- struct dib8000_state *st = fe->demodulator_priv; +- dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); +- return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); +-} +-EXPORT_SYMBOL(dib8000_pid_filter); +- +-static const struct dvb_frontend_ops dib8000_ops = { +- .delsys = { SYS_ISDBT }, +- .info = { +- .name = "DiBcom 8000 ISDB-T", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib8000_release, +- +- .init = dib8000_wakeup, +- .sleep = dib8000_sleep, +- +- .set_frontend = dib8000_set_frontend, +- .get_tune_settings = dib8000_fe_get_tune_settings, +- .get_frontend = dib8000_get_frontend, +- +- .read_status = dib8000_read_status, +- .read_ber = dib8000_read_ber, +- .read_signal_strength = dib8000_read_signal_strength, +- .read_snr = dib8000_read_snr, +- .read_ucblocks = dib8000_read_unc_blocks, +-}; +- +-struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +-{ +- struct dvb_frontend *fe; +- struct dib8000_state *state; +- +- dprintk("dib8000_attach"); +- +- state = kzalloc(sizeof(struct dib8000_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); +- if (fe == NULL) +- goto error; +- +- memcpy(&state->cfg, cfg, sizeof(struct dib8000_config)); +- state->i2c.adap = i2c_adap; +- state->i2c.addr = i2c_addr; +- state->i2c.i2c_write_buffer = state->i2c_write_buffer; +- state->i2c.i2c_read_buffer = state->i2c_read_buffer; +- mutex_init(&state->i2c_buffer_lock); +- state->i2c.i2c_buffer_lock = &state->i2c_buffer_lock; +- state->gpio_val = cfg->gpio_val; +- state->gpio_dir = cfg->gpio_dir; +- +- /* Ensure the output mode remains at the previous default if it's +- * not specifically set by the caller. +- */ +- if ((state->cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (state->cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) +- state->cfg.output_mode = OUTMODE_MPEG2_FIFO; +- +- state->fe[0] = fe; +- fe->demodulator_priv = state; +- memcpy(&state->fe[0]->ops, &dib8000_ops, sizeof(struct dvb_frontend_ops)); +- +- state->timf_default = cfg->pll->timf; +- +- if (dib8000_identify(&state->i2c) == 0) +- goto error; +- +- dibx000_init_i2c_master(&state->i2c_master, DIB8000, state->i2c.adap, state->i2c.addr); +- +- /* init 8096p tuner adapter */ +- strncpy(state->dib8096p_tuner_adap.name, "DiB8096P tuner interface", +- sizeof(state->dib8096p_tuner_adap.name)); +- state->dib8096p_tuner_adap.algo = &dib8096p_tuner_xfer_algo; +- state->dib8096p_tuner_adap.algo_data = NULL; +- state->dib8096p_tuner_adap.dev.parent = state->i2c.adap->dev.parent; +- i2c_set_adapdata(&state->dib8096p_tuner_adap, state); +- i2c_add_adapter(&state->dib8096p_tuner_adap); +- +- dib8000_reset(fe); +- +- dib8000_write_word(state, 285, (dib8000_read_word(state, 285) & ~0x60) | (3 << 5)); /* ber_rs_len = 3 */ +- +- return fe; +- +- error: +- kfree(state); +- return NULL; +-} +- +-EXPORT_SYMBOL(dib8000_attach); +- +-MODULE_AUTHOR("Olivier Grenie "); +-MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h +deleted file mode 100644 +index 39591bb..0000000 +--- a/drivers/media/dvb/frontends/dib8000.h ++++ /dev/null +@@ -1,174 +0,0 @@ +-#ifndef DIB8000_H +-#define DIB8000_H +- +-#include "dibx000_common.h" +- +-struct dib8000_config { +- u8 output_mpeg2_in_188_bytes; +- u8 hostbus_diversity; +- u8 tuner_is_baseband; +- int (*update_lna) (struct dvb_frontend *, u16 agc_global); +- +- u8 agc_config_count; +- struct dibx000_agc_config *agc; +- struct dibx000_bandwidth_config *pll; +- +-#define DIB8000_GPIO_DEFAULT_DIRECTIONS 0xffff +- u16 gpio_dir; +-#define DIB8000_GPIO_DEFAULT_VALUES 0x0000 +- u16 gpio_val; +-#define DIB8000_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +-#define DIB8000_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +-#define DIB8000_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +-#define DIB8000_GPIO_PWM_POS3(v) (v & 0xf) +-#define DIB8000_GPIO_DEFAULT_PWM_POS 0xffff +- u16 gpio_pwm_pos; +- u16 pwm_freq_div; +- +- void (*agc_control) (struct dvb_frontend *, u8 before); +- +- u16 drives; +- u16 diversity_delay; +- u8 div_cfg; +- u8 output_mode; +- u8 refclksel; +- u8 enMpegOutput:1; +-}; +- +-#define DEFAULT_DIB8000_I2C_ADDRESS 18 +- +-#if defined(CONFIG_DVB_DIB8000) || (defined(CONFIG_DVB_DIB8000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg); +-extern struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); +- +-extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, +- u8 default_addr, u8 first_addr, u8 is_dib8096p); +- +-extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); +-extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); +-extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff); +-extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +-extern int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state); +-extern enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe); +-extern void dib8000_pwm_agc_reset(struct dvb_frontend *fe); +-extern s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode); +-extern struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe); +-extern int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff); +-extern int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ); +-extern u32 dib8000_ctrl_timf(struct dvb_frontend *fe, +- uint8_t op, uint32_t timf); +-extern int dib8000_update_pll(struct dvb_frontend *fe, +- struct dibx000_bandwidth_config *pll); +-extern int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); +-extern int dib8000_remove_slave_frontend(struct dvb_frontend *fe); +-extern struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +-#else +-static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface i, int x) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib8000_i2c_enumeration(struct i2c_adapter *host, +- int no_of_demods, u8 default_addr, u8 first_addr, +- u8 is_dib8096p) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib8000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-static inline int dib8000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-static inline enum frontend_tune_state dib8000_get_tune_state(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return CT_SHUTDOWN; +-} +-static inline void dib8000_pwm_agc_reset(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +-} +-static inline struct i2c_adapter *dib8096p_get_i2c_tuner(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline int dib8096p_tuner_sleep(struct dvb_frontend *fe, int onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +-static inline s32 dib8000_get_adc_power(struct dvb_frontend *fe, u8 mode) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +-static inline int dib8090p_get_dc_power(struct dvb_frontend *fe, u8 IQ) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +-static inline u32 dib8000_ctrl_timf(struct dvb_frontend *fe, +- uint8_t op, uint32_t timf) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return 0; +-} +-static inline int dib8000_update_pll(struct dvb_frontend *fe, +- struct dibx000_bandwidth_config *pll) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-static inline int dib8000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-int dib8000_remove_slave_frontend(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline struct dvb_frontend *dib8000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c +deleted file mode 100644 +index 863ef3c..0000000 +--- a/drivers/media/dvb/frontends/dib9000.c ++++ /dev/null +@@ -1,2532 +0,0 @@ +-/* +- * Linux-DVB Driver for DiBcom's DiB9000 and demodulator-family. +- * +- * Copyright (C) 2005-10 DiBcom (http://www.dibcom.fr/) +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation, version 2. +- */ +-#include +-#include +-#include +- +-#include "dvb_math.h" +-#include "dvb_frontend.h" +- +-#include "dib9000.h" +-#include "dibx000_common.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB9000: "); printk(args); printk("\n"); } } while (0) +-#define MAX_NUMBER_OF_FRONTENDS 6 +- +-struct i2c_device { +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +- u8 *i2c_read_buffer; +- u8 *i2c_write_buffer; +-}; +- +-/* lock */ +-#define DIB_LOCK struct mutex +-#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0) +-#define DibReleaseLock(lock) mutex_unlock(lock) +-#define DibInitLock(lock) mutex_init(lock) +-#define DibFreeLock(lock) +- +-struct dib9000_pid_ctrl { +-#define DIB9000_PID_FILTER_CTRL 0 +-#define DIB9000_PID_FILTER 1 +- u8 cmd; +- u8 id; +- u16 pid; +- u8 onoff; +-}; +- +-struct dib9000_state { +- struct i2c_device i2c; +- +- struct dibx000_i2c_master i2c_master; +- struct i2c_adapter tuner_adap; +- struct i2c_adapter component_bus; +- +- u16 revision; +- u8 reg_offs; +- +- enum frontend_tune_state tune_state; +- u32 status; +- struct dvb_frontend_parametersContext channel_status; +- +- u8 fe_id; +- +-#define DIB9000_GPIO_DEFAULT_DIRECTIONS 0xffff +- u16 gpio_dir; +-#define DIB9000_GPIO_DEFAULT_VALUES 0x0000 +- u16 gpio_val; +-#define DIB9000_GPIO_DEFAULT_PWM_POS 0xffff +- u16 gpio_pwm_pos; +- +- union { /* common for all chips */ +- struct { +- u8 mobile_mode:1; +- } host; +- +- struct { +- struct dib9000_fe_memory_map { +- u16 addr; +- u16 size; +- } fe_mm[18]; +- u8 memcmd; +- +- DIB_LOCK mbx_if_lock; /* to protect read/write operations */ +- DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */ +- +- DIB_LOCK mem_lock; /* to protect the memory accesses */ +- DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */ +- +-#define MBX_MAX_WORDS (256 - 200 - 2) +-#define DIB9000_MSG_CACHE_SIZE 2 +- u16 message_cache[DIB9000_MSG_CACHE_SIZE][MBX_MAX_WORDS]; +- u8 fw_is_running; +- } risc; +- } platform; +- +- union { /* common for all platforms */ +- struct { +- struct dib9000_config cfg; +- } d9; +- } chip; +- +- struct dvb_frontend *fe[MAX_NUMBER_OF_FRONTENDS]; +- u16 component_bus_speed; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[2]; +- u8 i2c_write_buffer[255]; +- u8 i2c_read_buffer[255]; +- DIB_LOCK demod_lock; +- u8 get_frontend_internal; +- struct dib9000_pid_ctrl pid_ctrl[10]; +- s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ +-}; +- +-static const u32 fe_info[44] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0 +-}; +- +-enum dib9000_power_mode { +- DIB9000_POWER_ALL = 0, +- +- DIB9000_POWER_NO, +- DIB9000_POWER_INTERF_ANALOG_AGC, +- DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, +- DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD, +- DIB9000_POWER_INTERFACE_ONLY, +-}; +- +-enum dib9000_out_messages { +- OUT_MSG_HBM_ACK, +- OUT_MSG_HOST_BUF_FAIL, +- OUT_MSG_REQ_VERSION, +- OUT_MSG_BRIDGE_I2C_W, +- OUT_MSG_BRIDGE_I2C_R, +- OUT_MSG_BRIDGE_APB_W, +- OUT_MSG_BRIDGE_APB_R, +- OUT_MSG_SCAN_CHANNEL, +- OUT_MSG_MONIT_DEMOD, +- OUT_MSG_CONF_GPIO, +- OUT_MSG_DEBUG_HELP, +- OUT_MSG_SUBBAND_SEL, +- OUT_MSG_ENABLE_TIME_SLICE, +- OUT_MSG_FE_FW_DL, +- OUT_MSG_FE_CHANNEL_SEARCH, +- OUT_MSG_FE_CHANNEL_TUNE, +- OUT_MSG_FE_SLEEP, +- OUT_MSG_FE_SYNC, +- OUT_MSG_CTL_MONIT, +- +- OUT_MSG_CONF_SVC, +- OUT_MSG_SET_HBM, +- OUT_MSG_INIT_DEMOD, +- OUT_MSG_ENABLE_DIVERSITY, +- OUT_MSG_SET_OUTPUT_MODE, +- OUT_MSG_SET_PRIORITARY_CHANNEL, +- OUT_MSG_ACK_FRG, +- OUT_MSG_INIT_PMU, +-}; +- +-enum dib9000_in_messages { +- IN_MSG_DATA, +- IN_MSG_FRAME_INFO, +- IN_MSG_CTL_MONIT, +- IN_MSG_ACK_FREE_ITEM, +- IN_MSG_DEBUG_BUF, +- IN_MSG_MPE_MONITOR, +- IN_MSG_RAWTS_MONITOR, +- IN_MSG_END_BRIDGE_I2C_RW, +- IN_MSG_END_BRIDGE_APB_RW, +- IN_MSG_VERSION, +- IN_MSG_END_OF_SCAN, +- IN_MSG_MONIT_DEMOD, +- IN_MSG_ERROR, +- IN_MSG_FE_FW_DL_DONE, +- IN_MSG_EVENT, +- IN_MSG_ACK_CHANGE_SVC, +- IN_MSG_HBM_PROF, +-}; +- +-/* memory_access requests */ +-#define FE_MM_W_CHANNEL 0 +-#define FE_MM_W_FE_INFO 1 +-#define FE_MM_RW_SYNC 2 +- +-#define FE_SYNC_CHANNEL 1 +-#define FE_SYNC_W_GENERIC_MONIT 2 +-#define FE_SYNC_COMPONENT_ACCESS 3 +- +-#define FE_MM_R_CHANNEL_SEARCH_STATE 3 +-#define FE_MM_R_CHANNEL_UNION_CONTEXT 4 +-#define FE_MM_R_FE_INFO 5 +-#define FE_MM_R_FE_MONITOR 6 +- +-#define FE_MM_W_CHANNEL_HEAD 7 +-#define FE_MM_W_CHANNEL_UNION 8 +-#define FE_MM_W_CHANNEL_CONTEXT 9 +-#define FE_MM_R_CHANNEL_UNION 10 +-#define FE_MM_R_CHANNEL_CONTEXT 11 +-#define FE_MM_R_CHANNEL_TUNE_STATE 12 +- +-#define FE_MM_R_GENERIC_MONITORING_SIZE 13 +-#define FE_MM_W_GENERIC_MONITORING 14 +-#define FE_MM_R_GENERIC_MONITORING 15 +- +-#define FE_MM_W_COMPONENT_ACCESS 16 +-#define FE_MM_RW_COMPONENT_ACCESS_BUFFER 17 +-static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len); +-static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len); +- +-static u16 to_fw_output_mode(u16 mode) +-{ +- switch (mode) { +- case OUTMODE_HIGH_Z: +- return 0; +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- return 4; +- case OUTMODE_MPEG2_PAR_CONT_CLK: +- return 8; +- case OUTMODE_MPEG2_SERIAL: +- return 16; +- case OUTMODE_DIVERSITY: +- return 128; +- case OUTMODE_MPEG2_FIFO: +- return 2; +- case OUTMODE_ANALOG_ADC: +- return 1; +- default: +- return 0; +- } +-} +- +-static u16 dib9000_read16_attr(struct dib9000_state *state, u16 reg, u8 * b, u32 len, u16 attribute) +-{ +- u32 chunk_size = 126; +- u32 l; +- int ret; +- +- if (state->platform.risc.fw_is_running && (reg < 1024)) +- return dib9000_risc_apb_access_read(state, reg, attribute, NULL, 0, b, len); +- +- memset(state->msg, 0, 2 * sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c.i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = 2; +- state->msg[1].addr = state->i2c.i2c_addr >> 1; +- state->msg[1].flags = I2C_M_RD; +- state->msg[1].buf = b; +- state->msg[1].len = len; +- +- state->i2c_write_buffer[0] = reg >> 8; +- state->i2c_write_buffer[1] = reg & 0xff; +- +- if (attribute & DATA_BUS_ACCESS_MODE_8BIT) +- state->i2c_write_buffer[0] |= (1 << 5); +- if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +- state->i2c_write_buffer[0] |= (1 << 4); +- +- do { +- l = len < chunk_size ? len : chunk_size; +- state->msg[1].len = l; +- state->msg[1].buf = b; +- ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 2) != 2 ? -EREMOTEIO : 0; +- if (ret != 0) { +- dprintk("i2c read error on %d", reg); +- return -EREMOTEIO; +- } +- +- b += l; +- len -= l; +- +- if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) +- reg += l / 2; +- } while ((ret == 0) && len); +- +- return 0; +-} +- +-static u16 dib9000_i2c_read16(struct i2c_device *i2c, u16 reg) +-{ +- struct i2c_msg msg[2] = { +- {.addr = i2c->i2c_addr >> 1, .flags = 0, +- .buf = i2c->i2c_write_buffer, .len = 2}, +- {.addr = i2c->i2c_addr >> 1, .flags = I2C_M_RD, +- .buf = i2c->i2c_read_buffer, .len = 2}, +- }; +- +- i2c->i2c_write_buffer[0] = reg >> 8; +- i2c->i2c_write_buffer[1] = reg & 0xff; +- +- if (i2c_transfer(i2c->i2c_adap, msg, 2) != 2) { +- dprintk("read register %x error", reg); +- return 0; +- } +- +- return (i2c->i2c_read_buffer[0] << 8) | i2c->i2c_read_buffer[1]; +-} +- +-static inline u16 dib9000_read_word(struct dib9000_state *state, u16 reg) +-{ +- if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, 0) != 0) +- return 0; +- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +-} +- +-static inline u16 dib9000_read_word_attr(struct dib9000_state *state, u16 reg, u16 attribute) +-{ +- if (dib9000_read16_attr(state, reg, state->i2c_read_buffer, 2, +- attribute) != 0) +- return 0; +- return (state->i2c_read_buffer[0] << 8) | state->i2c_read_buffer[1]; +-} +- +-#define dib9000_read16_noinc_attr(state, reg, b, len, attribute) dib9000_read16_attr(state, reg, b, len, (attribute) | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +- +-static u16 dib9000_write16_attr(struct dib9000_state *state, u16 reg, const u8 * buf, u32 len, u16 attribute) +-{ +- u32 chunk_size = 126; +- u32 l; +- int ret; +- +- if (state->platform.risc.fw_is_running && (reg < 1024)) { +- if (dib9000_risc_apb_access_write +- (state, reg, DATA_BUS_ACCESS_MODE_16BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | attribute, buf, len) != 0) +- return -EINVAL; +- return 0; +- } +- +- memset(&state->msg[0], 0, sizeof(struct i2c_msg)); +- state->msg[0].addr = state->i2c.i2c_addr >> 1; +- state->msg[0].flags = 0; +- state->msg[0].buf = state->i2c_write_buffer; +- state->msg[0].len = len + 2; +- +- state->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- state->i2c_write_buffer[1] = (reg) & 0xff; +- +- if (attribute & DATA_BUS_ACCESS_MODE_8BIT) +- state->i2c_write_buffer[0] |= (1 << 5); +- if (attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +- state->i2c_write_buffer[0] |= (1 << 4); +- +- do { +- l = len < chunk_size ? len : chunk_size; +- state->msg[0].len = l + 2; +- memcpy(&state->i2c_write_buffer[2], buf, l); +- +- ret = i2c_transfer(state->i2c.i2c_adap, state->msg, 1) != 1 ? -EREMOTEIO : 0; +- +- buf += l; +- len -= l; +- +- if (!(attribute & DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT)) +- reg += l / 2; +- } while ((ret == 0) && len); +- +- return ret; +-} +- +-static int dib9000_i2c_write16(struct i2c_device *i2c, u16 reg, u16 val) +-{ +- struct i2c_msg msg = { +- .addr = i2c->i2c_addr >> 1, .flags = 0, +- .buf = i2c->i2c_write_buffer, .len = 4 +- }; +- +- i2c->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- i2c->i2c_write_buffer[1] = reg & 0xff; +- i2c->i2c_write_buffer[2] = (val >> 8) & 0xff; +- i2c->i2c_write_buffer[3] = val & 0xff; +- +- return i2c_transfer(i2c->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +-} +- +-static inline int dib9000_write_word(struct dib9000_state *state, u16 reg, u16 val) +-{ +- u8 b[2] = { val >> 8, val & 0xff }; +- return dib9000_write16_attr(state, reg, b, 2, 0); +-} +- +-static inline int dib9000_write_word_attr(struct dib9000_state *state, u16 reg, u16 val, u16 attribute) +-{ +- u8 b[2] = { val >> 8, val & 0xff }; +- return dib9000_write16_attr(state, reg, b, 2, attribute); +-} +- +-#define dib9000_write(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, 0) +-#define dib9000_write16_noinc(state, reg, buf, len) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +-#define dib9000_write16_noinc_attr(state, reg, buf, len, attribute) dib9000_write16_attr(state, reg, buf, len, DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT | (attribute)) +- +-#define dib9000_mbx_send(state, id, data, len) dib9000_mbx_send_attr(state, id, data, len, 0) +-#define dib9000_mbx_get_message(state, id, msg, len) dib9000_mbx_get_message_attr(state, id, msg, len, 0) +- +-#define MAC_IRQ (1 << 1) +-#define IRQ_POL_MSK (1 << 4) +- +-#define dib9000_risc_mem_read_chunks(state, b, len) dib9000_read16_attr(state, 1063, b, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +-#define dib9000_risc_mem_write_chunks(state, buf, len) dib9000_write16_attr(state, 1063, buf, len, DATA_BUS_ACCESS_MODE_8BIT | DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT) +- +-static void dib9000_risc_mem_setup_cmd(struct dib9000_state *state, u32 addr, u32 len, u8 reading) +-{ +- u8 b[14] = { 0 }; +- +-/* dprintk("%d memcmd: %d %d %d\n", state->fe_id, addr, addr+len, len); */ +-/* b[0] = 0 << 7; */ +- b[1] = 1; +- +-/* b[2] = 0; */ +-/* b[3] = 0; */ +- b[4] = (u8) (addr >> 8); +- b[5] = (u8) (addr & 0xff); +- +-/* b[10] = 0; */ +-/* b[11] = 0; */ +- b[12] = (u8) (addr >> 8); +- b[13] = (u8) (addr & 0xff); +- +- addr += len; +-/* b[6] = 0; */ +-/* b[7] = 0; */ +- b[8] = (u8) (addr >> 8); +- b[9] = (u8) (addr & 0xff); +- +- dib9000_write(state, 1056, b, 14); +- if (reading) +- dib9000_write_word(state, 1056, (1 << 15) | 1); +- state->platform.risc.memcmd = -1; /* if it was called directly reset it - to force a future setup-call to set it */ +-} +- +-static void dib9000_risc_mem_setup(struct dib9000_state *state, u8 cmd) +-{ +- struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd & 0x7f]; +- /* decide whether we need to "refresh" the memory controller */ +- if (state->platform.risc.memcmd == cmd && /* same command */ +- !(cmd & 0x80 && m->size < 67)) /* and we do not want to read something with less than 67 bytes looping - working around a bug in the memory controller */ +- return; +- dib9000_risc_mem_setup_cmd(state, m->addr, m->size, cmd & 0x80); +- state->platform.risc.memcmd = cmd; +-} +- +-static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u16 len) +-{ +- if (!state->platform.risc.fw_is_running) +- return -EIO; +- +- DibAcquireLock(&state->platform.risc.mem_lock); +- dib9000_risc_mem_setup(state, cmd | 0x80); +- dib9000_risc_mem_read_chunks(state, b, len); +- DibReleaseLock(&state->platform.risc.mem_lock); +- return 0; +-} +- +-static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 * b) +-{ +- struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[cmd]; +- if (!state->platform.risc.fw_is_running) +- return -EIO; +- +- DibAcquireLock(&state->platform.risc.mem_lock); +- dib9000_risc_mem_setup(state, cmd); +- dib9000_risc_mem_write_chunks(state, b, m->size); +- DibReleaseLock(&state->platform.risc.mem_lock); +- return 0; +-} +- +-static int dib9000_firmware_download(struct dib9000_state *state, u8 risc_id, u16 key, const u8 * code, u32 len) +-{ +- u16 offs; +- +- if (risc_id == 1) +- offs = 16; +- else +- offs = 0; +- +- /* config crtl reg */ +- dib9000_write_word(state, 1024 + offs, 0x000f); +- dib9000_write_word(state, 1025 + offs, 0); +- dib9000_write_word(state, 1031 + offs, key); +- +- dprintk("going to download %dB of microcode", len); +- if (dib9000_write16_noinc(state, 1026 + offs, (u8 *) code, (u16) len) != 0) { +- dprintk("error while downloading microcode for RISC %c", 'A' + risc_id); +- return -EIO; +- } +- +- dprintk("Microcode for RISC %c loaded", 'A' + risc_id); +- +- return 0; +-} +- +-static int dib9000_mbx_host_init(struct dib9000_state *state, u8 risc_id) +-{ +- u16 mbox_offs; +- u16 reset_reg; +- u16 tries = 1000; +- +- if (risc_id == 1) +- mbox_offs = 16; +- else +- mbox_offs = 0; +- +- /* Reset mailbox */ +- dib9000_write_word(state, 1027 + mbox_offs, 0x8000); +- +- /* Read reset status */ +- do { +- reset_reg = dib9000_read_word(state, 1027 + mbox_offs); +- msleep(100); +- } while ((reset_reg & 0x8000) && --tries); +- +- if (reset_reg & 0x8000) { +- dprintk("MBX: init ERROR, no response from RISC %c", 'A' + risc_id); +- return -EIO; +- } +- dprintk("MBX: initialized"); +- return 0; +-} +- +-#define MAX_MAILBOX_TRY 100 +-static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, u8 len, u16 attr) +-{ +- u8 *d, b[2]; +- u16 tmp; +- u16 size; +- u32 i; +- int ret = 0; +- +- if (!state->platform.risc.fw_is_running) +- return -EINVAL; +- +- DibAcquireLock(&state->platform.risc.mbx_if_lock); +- tmp = MAX_MAILBOX_TRY; +- do { +- size = dib9000_read_word_attr(state, 1043, attr) & 0xff; +- if ((size + len + 1) > MBX_MAX_WORDS && --tmp) { +- dprintk("MBX: RISC mbx full, retrying"); +- msleep(100); +- } else +- break; +- } while (1); +- +- /*dprintk( "MBX: size: %d", size); */ +- +- if (tmp == 0) { +- ret = -EINVAL; +- goto out; +- } +-#ifdef DUMP_MSG +- dprintk("--> %02x %d ", id, len + 1); +- for (i = 0; i < len; i++) +- dprintk("%04x ", data[i]); +- dprintk("\n"); +-#endif +- +- /* byte-order conversion - works on big (where it is not necessary) or little endian */ +- d = (u8 *) data; +- for (i = 0; i < len; i++) { +- tmp = data[i]; +- *d++ = tmp >> 8; +- *d++ = tmp & 0xff; +- } +- +- /* write msg */ +- b[0] = id; +- b[1] = len + 1; +- if (dib9000_write16_noinc_attr(state, 1045, b, 2, attr) != 0 || dib9000_write16_noinc_attr(state, 1045, (u8 *) data, len * 2, attr) != 0) { +- ret = -EIO; +- goto out; +- } +- +- /* update register nb_mes_in_RX */ +- ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); +- +-out: +- DibReleaseLock(&state->platform.risc.mbx_if_lock); +- +- return ret; +-} +- +-static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, u16 attr) +-{ +-#ifdef DUMP_MSG +- u16 *d = data; +-#endif +- +- u16 tmp, i; +- u8 size; +- u8 mc_base; +- +- if (!state->platform.risc.fw_is_running) +- return 0; +- +- DibAcquireLock(&state->platform.risc.mbx_if_lock); +- if (risc_id == 1) +- mc_base = 16; +- else +- mc_base = 0; +- +- /* Length and type in the first word */ +- *data = dib9000_read_word_attr(state, 1029 + mc_base, attr); +- +- size = *data & 0xff; +- if (size <= MBX_MAX_WORDS) { +- data++; +- size--; /* Initial word already read */ +- +- dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, size * 2, attr); +- +- /* to word conversion */ +- for (i = 0; i < size; i++) { +- tmp = *data; +- *data = (tmp >> 8) | (tmp << 8); +- data++; +- } +- +-#ifdef DUMP_MSG +- dprintk("<-- "); +- for (i = 0; i < size + 1; i++) +- dprintk("%04x ", d[i]); +- dprintk("\n"); +-#endif +- } else { +- dprintk("MBX: message is too big for message cache (%d), flushing message", size); +- size--; /* Initial word already read */ +- while (size--) +- dib9000_read16_noinc_attr(state, 1029 + mc_base, (u8 *) data, 2, attr); +- } +- /* Update register nb_mes_in_TX */ +- dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); +- +- DibReleaseLock(&state->platform.risc.mbx_if_lock); +- +- return size + 1; +-} +- +-static int dib9000_risc_debug_buf(struct dib9000_state *state, u16 * data, u8 size) +-{ +- u32 ts = data[1] << 16 | data[0]; +- char *b = (char *)&data[2]; +- +- b[2 * (size - 2) - 1] = '\0'; /* Bullet proof the buffer */ +- if (*b == '~') { +- b++; +- dprintk(b); +- } else +- dprintk("RISC%d: %d.%04d %s", state->fe_id, ts / 10000, ts % 10000, *b ? b : ""); +- return 1; +-} +- +-static int dib9000_mbx_fetch_to_cache(struct dib9000_state *state, u16 attr) +-{ +- int i; +- u8 size; +- u16 *block; +- /* find a free slot */ +- for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { +- block = state->platform.risc.message_cache[i]; +- if (*block == 0) { +- size = dib9000_mbx_read(state, block, 1, attr); +- +-/* dprintk( "MBX: fetched %04x message to cache", *block); */ +- +- switch (*block >> 8) { +- case IN_MSG_DEBUG_BUF: +- dib9000_risc_debug_buf(state, block + 1, size); /* debug-messages are going to be printed right away */ +- *block = 0; /* free the block */ +- break; +-#if 0 +- case IN_MSG_DATA: /* FE-TRACE */ +- dib9000_risc_data_process(state, block + 1, size); +- *block = 0; +- break; +-#endif +- default: +- break; +- } +- +- return 1; +- } +- } +- dprintk("MBX: no free cache-slot found for new message..."); +- return -1; +-} +- +-static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) +-{ +- if (risc_id == 0) +- return (u8) (dib9000_read_word_attr(state, 1028, attr) >> 10) & 0x1f; /* 5 bit field */ +- else +- return (u8) (dib9000_read_word_attr(state, 1044, attr) >> 8) & 0x7f; /* 7 bit field */ +-} +- +-static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) +-{ +- int ret = 0; +- u16 tmp; +- +- if (!state->platform.risc.fw_is_running) +- return -1; +- +- DibAcquireLock(&state->platform.risc.mbx_lock); +- +- if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ +- ret = dib9000_mbx_fetch_to_cache(state, attr); +- +- tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ +-/* if (tmp) */ +-/* dprintk( "cleared IRQ: %x", tmp); */ +- DibReleaseLock(&state->platform.risc.mbx_lock); +- +- return ret; +-} +- +-static int dib9000_mbx_get_message_attr(struct dib9000_state *state, u16 id, u16 * msg, u8 * size, u16 attr) +-{ +- u8 i; +- u16 *block; +- u16 timeout = 30; +- +- *msg = 0; +- do { +- /* dib9000_mbx_get_from_cache(); */ +- for (i = 0; i < DIB9000_MSG_CACHE_SIZE; i++) { +- block = state->platform.risc.message_cache[i]; +- if ((*block >> 8) == id) { +- *size = (*block & 0xff) - 1; +- memcpy(msg, block + 1, (*size) * 2); +- *block = 0; /* free the block */ +- i = 0; /* signal that we found a message */ +- break; +- } +- } +- +- if (i == 0) +- break; +- +- if (dib9000_mbx_process(state, attr) == -1) /* try to fetch one message - if any */ +- return -1; +- +- } while (--timeout); +- +- if (timeout == 0) { +- dprintk("waiting for message %d timed out", id); +- return -1; +- } +- +- return i == 0; +-} +- +-static int dib9000_risc_check_version(struct dib9000_state *state) +-{ +- u8 r[4]; +- u8 size; +- u16 fw_version = 0; +- +- if (dib9000_mbx_send(state, OUT_MSG_REQ_VERSION, &fw_version, 1) != 0) +- return -EIO; +- +- if (dib9000_mbx_get_message(state, IN_MSG_VERSION, (u16 *) r, &size) < 0) +- return -EIO; +- +- fw_version = (r[0] << 8) | r[1]; +- dprintk("RISC: ver: %d.%02d (IC: %d)", fw_version >> 10, fw_version & 0x3ff, (r[2] << 8) | r[3]); +- +- if ((fw_version >> 10) != 7) +- return -EINVAL; +- +- switch (fw_version & 0x3ff) { +- case 11: +- case 12: +- case 14: +- case 15: +- case 16: +- case 17: +- break; +- default: +- dprintk("RISC: invalid firmware version"); +- return -EINVAL; +- } +- +- dprintk("RISC: valid firmware version"); +- return 0; +-} +- +-static int dib9000_fw_boot(struct dib9000_state *state, const u8 * codeA, u32 lenA, const u8 * codeB, u32 lenB) +-{ +- /* Reconfig pool mac ram */ +- dib9000_write_word(state, 1225, 0x02); /* A: 8k C, 4 k D - B: 32k C 6 k D - IRAM 96k */ +- dib9000_write_word(state, 1226, 0x05); +- +- /* Toggles IP crypto to Host APB interface. */ +- dib9000_write_word(state, 1542, 1); +- +- /* Set jump and no jump in the dma box */ +- dib9000_write_word(state, 1074, 0); +- dib9000_write_word(state, 1075, 0); +- +- /* Set MAC as APB Master. */ +- dib9000_write_word(state, 1237, 0); +- +- /* Reset the RISCs */ +- if (codeA != NULL) +- dib9000_write_word(state, 1024, 2); +- else +- dib9000_write_word(state, 1024, 15); +- if (codeB != NULL) +- dib9000_write_word(state, 1040, 2); +- +- if (codeA != NULL) +- dib9000_firmware_download(state, 0, 0x1234, codeA, lenA); +- if (codeB != NULL) +- dib9000_firmware_download(state, 1, 0x1234, codeB, lenB); +- +- /* Run the RISCs */ +- if (codeA != NULL) +- dib9000_write_word(state, 1024, 0); +- if (codeB != NULL) +- dib9000_write_word(state, 1040, 0); +- +- if (codeA != NULL) +- if (dib9000_mbx_host_init(state, 0) != 0) +- return -EIO; +- if (codeB != NULL) +- if (dib9000_mbx_host_init(state, 1) != 0) +- return -EIO; +- +- msleep(100); +- state->platform.risc.fw_is_running = 1; +- +- if (dib9000_risc_check_version(state) != 0) +- return -EINVAL; +- +- state->platform.risc.memcmd = 0xff; +- return 0; +-} +- +-static u16 dib9000_identify(struct i2c_device *client) +-{ +- u16 value; +- +- value = dib9000_i2c_read16(client, 896); +- if (value != 0x01b3) { +- dprintk("wrong Vendor ID (0x%x)", value); +- return 0; +- } +- +- value = dib9000_i2c_read16(client, 897); +- if (value != 0x4000 && value != 0x4001 && value != 0x4002 && value != 0x4003 && value != 0x4004 && value != 0x4005) { +- dprintk("wrong Device ID (0x%x)", value); +- return 0; +- } +- +- /* protect this driver to be used with 7000PC */ +- if (value == 0x4000 && dib9000_i2c_read16(client, 769) == 0x4000) { +- dprintk("this driver does not work with DiB7000PC"); +- return 0; +- } +- +- switch (value) { +- case 0x4000: +- dprintk("found DiB7000MA/PA/MB/PB"); +- break; +- case 0x4001: +- dprintk("found DiB7000HC"); +- break; +- case 0x4002: +- dprintk("found DiB7000MC"); +- break; +- case 0x4003: +- dprintk("found DiB9000A"); +- break; +- case 0x4004: +- dprintk("found DiB9000H"); +- break; +- case 0x4005: +- dprintk("found DiB9000M"); +- break; +- } +- +- return value; +-} +- +-static void dib9000_set_power_mode(struct dib9000_state *state, enum dib9000_power_mode mode) +-{ +- /* by default everything is going to be powered off */ +- u16 reg_903 = 0x3fff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906; +- u8 offset; +- +- if (state->revision == 0x4003 || state->revision == 0x4004 || state->revision == 0x4005) +- offset = 1; +- else +- offset = 0; +- +- reg_906 = dib9000_read_word(state, 906 + offset) | 0x3; /* keep settings for RISC */ +- +- /* now, depending on the requested mode, we power on */ +- switch (mode) { +- /* power up everything in the demod */ +- case DIB9000_POWER_ALL: +- reg_903 = 0x0000; +- reg_904 = 0x0000; +- reg_905 = 0x0000; +- reg_906 = 0x0000; +- break; +- +- /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ +- case DIB9000_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ +- reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); +- break; +- +- case DIB9000_POWER_INTERF_ANALOG_AGC: +- reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); +- reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); +- reg_906 &= ~((1 << 0)); +- break; +- +- case DIB9000_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: +- reg_903 = 0x0000; +- reg_904 = 0x801f; +- reg_905 = 0x0000; +- reg_906 &= ~((1 << 0)); +- break; +- +- case DIB9000_POWER_COR4_CRY_ESRAM_MOUT_NUD: +- reg_903 = 0x0000; +- reg_904 = 0x8000; +- reg_905 = 0x010b; +- reg_906 &= ~((1 << 0)); +- break; +- default: +- case DIB9000_POWER_NO: +- break; +- } +- +- /* always power down unused parts */ +- if (!state->platform.host.mobile_mode) +- reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); +- +- /* P_sdio_select_clk = 0 on MC and after */ +- if (state->revision != 0x4000) +- reg_906 <<= 1; +- +- dib9000_write_word(state, 903 + offset, reg_903); +- dib9000_write_word(state, 904 + offset, reg_904); +- dib9000_write_word(state, 905 + offset, reg_905); +- dib9000_write_word(state, 906 + offset, reg_906); +-} +- +-static int dib9000_fw_reset(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- +- dib9000_write_word(state, 1817, 0x0003); +- +- dib9000_write_word(state, 1227, 1); +- dib9000_write_word(state, 1227, 0); +- +- switch ((state->revision = dib9000_identify(&state->i2c))) { +- case 0x4003: +- case 0x4004: +- case 0x4005: +- state->reg_offs = 1; +- break; +- default: +- return -EINVAL; +- } +- +- /* reset the i2c-master to use the host interface */ +- dibx000_reset_i2c_master(&state->i2c_master); +- +- dib9000_set_power_mode(state, DIB9000_POWER_ALL); +- +- /* unforce divstr regardless whether i2c enumeration was done or not */ +- dib9000_write_word(state, 1794, dib9000_read_word(state, 1794) & ~(1 << 1)); +- dib9000_write_word(state, 1796, 0); +- dib9000_write_word(state, 1805, 0x805); +- +- /* restart all parts */ +- dib9000_write_word(state, 898, 0xffff); +- dib9000_write_word(state, 899, 0xffff); +- dib9000_write_word(state, 900, 0x0001); +- dib9000_write_word(state, 901, 0xff19); +- dib9000_write_word(state, 902, 0x003c); +- +- dib9000_write_word(state, 898, 0); +- dib9000_write_word(state, 899, 0); +- dib9000_write_word(state, 900, 0); +- dib9000_write_word(state, 901, 0); +- dib9000_write_word(state, 902, 0); +- +- dib9000_write_word(state, 911, state->chip.d9.cfg.if_drives); +- +- dib9000_set_power_mode(state, DIB9000_POWER_INTERFACE_ONLY); +- +- return 0; +-} +- +-static int dib9000_risc_apb_access_read(struct dib9000_state *state, u32 address, u16 attribute, const u8 * tx, u32 txlen, u8 * b, u32 len) +-{ +- u16 mb[10]; +- u8 i, s; +- +- if (address >= 1024 || !state->platform.risc.fw_is_running) +- return -EINVAL; +- +- /* dprintk( "APB access thru rd fw %d %x", address, attribute); */ +- +- mb[0] = (u16) address; +- mb[1] = len / 2; +- dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_R, mb, 2, attribute); +- switch (dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute)) { +- case 1: +- s--; +- for (i = 0; i < s; i++) { +- b[i * 2] = (mb[i + 1] >> 8) & 0xff; +- b[i * 2 + 1] = (mb[i + 1]) & 0xff; +- } +- return 0; +- default: +- return -EIO; +- } +- return -EIO; +-} +- +-static int dib9000_risc_apb_access_write(struct dib9000_state *state, u32 address, u16 attribute, const u8 * b, u32 len) +-{ +- u16 mb[10]; +- u8 s, i; +- +- if (address >= 1024 || !state->platform.risc.fw_is_running) +- return -EINVAL; +- +- /* dprintk( "APB access thru wr fw %d %x", address, attribute); */ +- +- mb[0] = (unsigned short)address; +- for (i = 0; i < len && i < 20; i += 2) +- mb[1 + (i / 2)] = (b[i] << 8 | b[i + 1]); +- +- dib9000_mbx_send_attr(state, OUT_MSG_BRIDGE_APB_W, mb, 1 + len / 2, attribute); +- return dib9000_mbx_get_message_attr(state, IN_MSG_END_BRIDGE_APB_RW, mb, &s, attribute) == 1 ? 0 : -EINVAL; +-} +- +-static int dib9000_fw_memmbx_sync(struct dib9000_state *state, u8 i) +-{ +- u8 index_loop = 10; +- +- if (!state->platform.risc.fw_is_running) +- return 0; +- dib9000_risc_mem_write(state, FE_MM_RW_SYNC, &i); +- do { +- dib9000_risc_mem_read(state, FE_MM_RW_SYNC, state->i2c_read_buffer, 1); +- } while (state->i2c_read_buffer[0] && index_loop--); +- +- if (index_loop > 0) +- return 0; +- return -EIO; +-} +- +-static int dib9000_fw_init(struct dib9000_state *state) +-{ +- struct dibGPIOFunction *f; +- u16 b[40] = { 0 }; +- u8 i; +- u8 size; +- +- if (dib9000_fw_boot(state, NULL, 0, state->chip.d9.cfg.microcode_B_fe_buffer, state->chip.d9.cfg.microcode_B_fe_size) != 0) +- return -EIO; +- +- /* initialize the firmware */ +- for (i = 0; i < ARRAY_SIZE(state->chip.d9.cfg.gpio_function); i++) { +- f = &state->chip.d9.cfg.gpio_function[i]; +- if (f->mask) { +- switch (f->function) { +- case BOARD_GPIO_FUNCTION_COMPONENT_ON: +- b[0] = (u16) f->mask; +- b[1] = (u16) f->direction; +- b[2] = (u16) f->value; +- break; +- case BOARD_GPIO_FUNCTION_COMPONENT_OFF: +- b[3] = (u16) f->mask; +- b[4] = (u16) f->direction; +- b[5] = (u16) f->value; +- break; +- } +- } +- } +- if (dib9000_mbx_send(state, OUT_MSG_CONF_GPIO, b, 15) != 0) +- return -EIO; +- +- /* subband */ +- b[0] = state->chip.d9.cfg.subband.size; /* type == 0 -> GPIO - PWM not yet supported */ +- for (i = 0; i < state->chip.d9.cfg.subband.size; i++) { +- b[1 + i * 4] = state->chip.d9.cfg.subband.subband[i].f_mhz; +- b[2 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.mask; +- b[3 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.direction; +- b[4 + i * 4] = (u16) state->chip.d9.cfg.subband.subband[i].gpio.value; +- } +- b[1 + i * 4] = 0; /* fe_id */ +- if (dib9000_mbx_send(state, OUT_MSG_SUBBAND_SEL, b, 2 + 4 * i) != 0) +- return -EIO; +- +- /* 0 - id, 1 - no_of_frontends */ +- b[0] = (0 << 8) | 1; +- /* 0 = i2c-address demod, 0 = tuner */ +- b[1] = (0 << 8) | (0); +- b[2] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000) >> 16) & 0xffff); +- b[3] = (u16) (((state->chip.d9.cfg.xtal_clock_khz * 1000)) & 0xffff); +- b[4] = (u16) ((state->chip.d9.cfg.vcxo_timer >> 16) & 0xffff); +- b[5] = (u16) ((state->chip.d9.cfg.vcxo_timer) & 0xffff); +- b[6] = (u16) ((state->chip.d9.cfg.timing_frequency >> 16) & 0xffff); +- b[7] = (u16) ((state->chip.d9.cfg.timing_frequency) & 0xffff); +- b[29] = state->chip.d9.cfg.if_drives; +- if (dib9000_mbx_send(state, OUT_MSG_INIT_DEMOD, b, ARRAY_SIZE(b)) != 0) +- return -EIO; +- +- if (dib9000_mbx_send(state, OUT_MSG_FE_FW_DL, NULL, 0) != 0) +- return -EIO; +- +- if (dib9000_mbx_get_message(state, IN_MSG_FE_FW_DL_DONE, b, &size) < 0) +- return -EIO; +- +- if (size > ARRAY_SIZE(b)) { +- dprintk("error : firmware returned %dbytes needed but the used buffer has only %dbytes\n Firmware init ABORTED", size, +- (int)ARRAY_SIZE(b)); +- return -EINVAL; +- } +- +- for (i = 0; i < size; i += 2) { +- state->platform.risc.fe_mm[i / 2].addr = b[i + 0]; +- state->platform.risc.fe_mm[i / 2].size = b[i + 1]; +- } +- +- return 0; +-} +- +-static void dib9000_fw_set_channel_head(struct dib9000_state *state) +-{ +- u8 b[9]; +- u32 freq = state->fe[0]->dtv_property_cache.frequency / 1000; +- if (state->fe_id % 2) +- freq += 101; +- +- b[0] = (u8) ((freq >> 0) & 0xff); +- b[1] = (u8) ((freq >> 8) & 0xff); +- b[2] = (u8) ((freq >> 16) & 0xff); +- b[3] = (u8) ((freq >> 24) & 0xff); +- b[4] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 0) & 0xff); +- b[5] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 8) & 0xff); +- b[6] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 16) & 0xff); +- b[7] = (u8) ((state->fe[0]->dtv_property_cache.bandwidth_hz / 1000 >> 24) & 0xff); +- b[8] = 0x80; /* do not wait for CELL ID when doing autosearch */ +- if (state->fe[0]->dtv_property_cache.delivery_system == SYS_DVBT) +- b[8] |= 1; +- dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_HEAD, b); +-} +- +-static int dib9000_fw_get_channel(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- struct dibDVBTChannel { +- s8 spectrum_inversion; +- +- s8 nfft; +- s8 guard; +- s8 constellation; +- +- s8 hrch; +- s8 alpha; +- s8 code_rate_hp; +- s8 code_rate_lp; +- s8 select_hp; +- +- s8 intlv_native; +- }; +- struct dibDVBTChannel *ch; +- int ret = 0; +- +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { +- ret = -EIO; +- goto error; +- } +- +- dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_UNION, +- state->i2c_read_buffer, sizeof(struct dibDVBTChannel)); +- ch = (struct dibDVBTChannel *)state->i2c_read_buffer; +- +- +- switch (ch->spectrum_inversion & 0x7) { +- case 1: +- state->fe[0]->dtv_property_cache.inversion = INVERSION_ON; +- break; +- case 0: +- state->fe[0]->dtv_property_cache.inversion = INVERSION_OFF; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.inversion = INVERSION_AUTO; +- break; +- } +- switch (ch->nfft) { +- case 0: +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 2: +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_4K; +- break; +- case 1: +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_8K; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.transmission_mode = TRANSMISSION_MODE_AUTO; +- break; +- } +- switch (ch->guard) { +- case 0: +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_1_4; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.guard_interval = GUARD_INTERVAL_AUTO; +- break; +- } +- switch (ch->constellation) { +- case 2: +- state->fe[0]->dtv_property_cache.modulation = QAM_64; +- break; +- case 1: +- state->fe[0]->dtv_property_cache.modulation = QAM_16; +- break; +- case 0: +- state->fe[0]->dtv_property_cache.modulation = QPSK; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.modulation = QAM_AUTO; +- break; +- } +- switch (ch->hrch) { +- case 0: +- state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_1; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.hierarchy = HIERARCHY_AUTO; +- break; +- } +- switch (ch->code_rate_hp) { +- case 1: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_1_2; +- break; +- case 2: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_2_3; +- break; +- case 3: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_3_4; +- break; +- case 5: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_5_6; +- break; +- case 7: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_7_8; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.code_rate_HP = FEC_AUTO; +- break; +- } +- switch (ch->code_rate_lp) { +- case 1: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_1_2; +- break; +- case 2: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_2_3; +- break; +- case 3: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_3_4; +- break; +- case 5: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_5_6; +- break; +- case 7: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_7_8; +- break; +- default: +- case -1: +- state->fe[0]->dtv_property_cache.code_rate_LP = FEC_AUTO; +- break; +- } +- +-error: +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- return ret; +-} +- +-static int dib9000_fw_set_channel_union(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- struct dibDVBTChannel { +- s8 spectrum_inversion; +- +- s8 nfft; +- s8 guard; +- s8 constellation; +- +- s8 hrch; +- s8 alpha; +- s8 code_rate_hp; +- s8 code_rate_lp; +- s8 select_hp; +- +- s8 intlv_native; +- }; +- struct dibDVBTChannel ch; +- +- switch (state->fe[0]->dtv_property_cache.inversion) { +- case INVERSION_ON: +- ch.spectrum_inversion = 1; +- break; +- case INVERSION_OFF: +- ch.spectrum_inversion = 0; +- break; +- default: +- case INVERSION_AUTO: +- ch.spectrum_inversion = -1; +- break; +- } +- switch (state->fe[0]->dtv_property_cache.transmission_mode) { +- case TRANSMISSION_MODE_2K: +- ch.nfft = 0; +- break; +- case TRANSMISSION_MODE_4K: +- ch.nfft = 2; +- break; +- case TRANSMISSION_MODE_8K: +- ch.nfft = 1; +- break; +- default: +- case TRANSMISSION_MODE_AUTO: +- ch.nfft = 1; +- break; +- } +- switch (state->fe[0]->dtv_property_cache.guard_interval) { +- case GUARD_INTERVAL_1_32: +- ch.guard = 0; +- break; +- case GUARD_INTERVAL_1_16: +- ch.guard = 1; +- break; +- case GUARD_INTERVAL_1_8: +- ch.guard = 2; +- break; +- case GUARD_INTERVAL_1_4: +- ch.guard = 3; +- break; +- default: +- case GUARD_INTERVAL_AUTO: +- ch.guard = -1; +- break; +- } +- switch (state->fe[0]->dtv_property_cache.modulation) { +- case QAM_64: +- ch.constellation = 2; +- break; +- case QAM_16: +- ch.constellation = 1; +- break; +- case QPSK: +- ch.constellation = 0; +- break; +- default: +- case QAM_AUTO: +- ch.constellation = -1; +- break; +- } +- switch (state->fe[0]->dtv_property_cache.hierarchy) { +- case HIERARCHY_NONE: +- ch.hrch = 0; +- break; +- case HIERARCHY_1: +- case HIERARCHY_2: +- case HIERARCHY_4: +- ch.hrch = 1; +- break; +- default: +- case HIERARCHY_AUTO: +- ch.hrch = -1; +- break; +- } +- ch.alpha = 1; +- switch (state->fe[0]->dtv_property_cache.code_rate_HP) { +- case FEC_1_2: +- ch.code_rate_hp = 1; +- break; +- case FEC_2_3: +- ch.code_rate_hp = 2; +- break; +- case FEC_3_4: +- ch.code_rate_hp = 3; +- break; +- case FEC_5_6: +- ch.code_rate_hp = 5; +- break; +- case FEC_7_8: +- ch.code_rate_hp = 7; +- break; +- default: +- case FEC_AUTO: +- ch.code_rate_hp = -1; +- break; +- } +- switch (state->fe[0]->dtv_property_cache.code_rate_LP) { +- case FEC_1_2: +- ch.code_rate_lp = 1; +- break; +- case FEC_2_3: +- ch.code_rate_lp = 2; +- break; +- case FEC_3_4: +- ch.code_rate_lp = 3; +- break; +- case FEC_5_6: +- ch.code_rate_lp = 5; +- break; +- case FEC_7_8: +- ch.code_rate_lp = 7; +- break; +- default: +- case FEC_AUTO: +- ch.code_rate_lp = -1; +- break; +- } +- ch.select_hp = 1; +- ch.intlv_native = 1; +- +- dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_UNION, (u8 *) &ch); +- +- return 0; +-} +- +-static int dib9000_fw_tune(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- int ret = 10, search = state->channel_status.status == CHANNEL_STATUS_PARAMETERS_UNKNOWN; +- s8 i; +- +- switch (state->tune_state) { +- case CT_DEMOD_START: +- dib9000_fw_set_channel_head(state); +- +- /* write the channel context - a channel is initialized to 0, so it is OK */ +- dib9000_risc_mem_write(state, FE_MM_W_CHANNEL_CONTEXT, (u8 *) fe_info); +- dib9000_risc_mem_write(state, FE_MM_W_FE_INFO, (u8 *) fe_info); +- +- if (search) +- dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_SEARCH, NULL, 0); +- else { +- dib9000_fw_set_channel_union(fe); +- dib9000_mbx_send(state, OUT_MSG_FE_CHANNEL_TUNE, NULL, 0); +- } +- state->tune_state = CT_DEMOD_STEP_1; +- break; +- case CT_DEMOD_STEP_1: +- if (search) +- dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_SEARCH_STATE, state->i2c_read_buffer, 1); +- else +- dib9000_risc_mem_read(state, FE_MM_R_CHANNEL_TUNE_STATE, state->i2c_read_buffer, 1); +- i = (s8)state->i2c_read_buffer[0]; +- switch (i) { /* something happened */ +- case 0: +- break; +- case -2: /* tps locks are "slower" than MPEG locks -> even in autosearch data is OK here */ +- if (search) +- state->status = FE_STATUS_DEMOD_SUCCESS; +- else { +- state->tune_state = CT_DEMOD_STOP; +- state->status = FE_STATUS_LOCKED; +- } +- break; +- default: +- state->status = FE_STATUS_TUNE_FAILED; +- state->tune_state = CT_DEMOD_STOP; +- break; +- } +- break; +- default: +- ret = FE_CALLBACK_TIME_NEVER; +- break; +- } +- +- return ret; +-} +- +-static int dib9000_fw_set_diversity_in(struct dvb_frontend *fe, int onoff) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 mode = (u16) onoff; +- return dib9000_mbx_send(state, OUT_MSG_ENABLE_DIVERSITY, &mode, 1); +-} +- +-static int dib9000_fw_set_output_mode(struct dvb_frontend *fe, int mode) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 outreg, smo_mode; +- +- dprintk("setting output mode for demod %p to %d", fe, mode); +- +- switch (mode) { +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- outreg = (1 << 10); /* 0x0400 */ +- break; +- case OUTMODE_MPEG2_PAR_CONT_CLK: +- outreg = (1 << 10) | (1 << 6); /* 0x0440 */ +- break; +- case OUTMODE_MPEG2_SERIAL: +- outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ +- break; +- case OUTMODE_DIVERSITY: +- outreg = (1 << 10) | (4 << 6); /* 0x0500 */ +- break; +- case OUTMODE_MPEG2_FIFO: +- outreg = (1 << 10) | (5 << 6); +- break; +- case OUTMODE_HIGH_Z: +- outreg = 0; +- break; +- default: +- dprintk("Unhandled output_mode passed to be set for demod %p", &state->fe[0]); +- return -EINVAL; +- } +- +- dib9000_write_word(state, 1795, outreg); +- +- switch (mode) { +- case OUTMODE_MPEG2_PAR_GATED_CLK: +- case OUTMODE_MPEG2_PAR_CONT_CLK: +- case OUTMODE_MPEG2_SERIAL: +- case OUTMODE_MPEG2_FIFO: +- smo_mode = (dib9000_read_word(state, 295) & 0x0010) | (1 << 1); +- if (state->chip.d9.cfg.output_mpeg2_in_188_bytes) +- smo_mode |= (1 << 5); +- dib9000_write_word(state, 295, smo_mode); +- break; +- } +- +- outreg = to_fw_output_mode(mode); +- return dib9000_mbx_send(state, OUT_MSG_SET_OUTPUT_MODE, &outreg, 1); +-} +- +-static int dib9000_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dib9000_state *state = i2c_get_adapdata(i2c_adap); +- u16 i, len, t, index_msg; +- +- for (index_msg = 0; index_msg < num; index_msg++) { +- if (msg[index_msg].flags & I2C_M_RD) { /* read */ +- len = msg[index_msg].len; +- if (len > 16) +- len = 16; +- +- if (dib9000_read_word(state, 790) != 0) +- dprintk("TunerITF: read busy"); +- +- dib9000_write_word(state, 784, (u16) (msg[index_msg].addr)); +- dib9000_write_word(state, 787, (len / 2) - 1); +- dib9000_write_word(state, 786, 1); /* start read */ +- +- i = 1000; +- while (dib9000_read_word(state, 790) != (len / 2) && i) +- i--; +- +- if (i == 0) +- dprintk("TunerITF: read failed"); +- +- for (i = 0; i < len; i += 2) { +- t = dib9000_read_word(state, 785); +- msg[index_msg].buf[i] = (t >> 8) & 0xff; +- msg[index_msg].buf[i + 1] = (t) & 0xff; +- } +- if (dib9000_read_word(state, 790) != 0) +- dprintk("TunerITF: read more data than expected"); +- } else { +- i = 1000; +- while (dib9000_read_word(state, 789) && i) +- i--; +- if (i == 0) +- dprintk("TunerITF: write busy"); +- +- len = msg[index_msg].len; +- if (len > 16) +- len = 16; +- +- for (i = 0; i < len; i += 2) +- dib9000_write_word(state, 785, (msg[index_msg].buf[i] << 8) | msg[index_msg].buf[i + 1]); +- dib9000_write_word(state, 784, (u16) msg[index_msg].addr); +- dib9000_write_word(state, 787, (len / 2) - 1); +- dib9000_write_word(state, 786, 0); /* start write */ +- +- i = 1000; +- while (dib9000_read_word(state, 791) > 0 && i) +- i--; +- if (i == 0) +- dprintk("TunerITF: write failed"); +- } +- } +- return num; +-} +- +-int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- +- state->component_bus_speed = speed; +- return 0; +-} +-EXPORT_SYMBOL(dib9000_fw_set_component_bus_speed); +- +-static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dib9000_state *state = i2c_get_adapdata(i2c_adap); +- u8 type = 0; /* I2C */ +- u8 port = DIBX000_I2C_INTERFACE_GPIO_3_4; +- u16 scl = state->component_bus_speed; /* SCL frequency */ +- struct dib9000_fe_memory_map *m = &state->platform.risc.fe_mm[FE_MM_RW_COMPONENT_ACCESS_BUFFER]; +- u8 p[13] = { 0 }; +- +- p[0] = type; +- p[1] = port; +- p[2] = msg[0].addr << 1; +- +- p[3] = (u8) scl & 0xff; /* scl */ +- p[4] = (u8) (scl >> 8); +- +- p[7] = 0; +- p[8] = 0; +- +- p[9] = (u8) (msg[0].len); +- p[10] = (u8) (msg[0].len >> 8); +- if ((num > 1) && (msg[1].flags & I2C_M_RD)) { +- p[11] = (u8) (msg[1].len); +- p[12] = (u8) (msg[1].len >> 8); +- } else { +- p[11] = 0; +- p[12] = 0; +- } +- +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- +- dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p); +- +- { /* write-part */ +- dib9000_risc_mem_setup_cmd(state, m->addr, msg[0].len, 0); +- dib9000_risc_mem_write_chunks(state, msg[0].buf, msg[0].len); +- } +- +- /* do the transaction */ +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- return 0; +- } +- +- /* read back any possible result */ +- if ((num > 1) && (msg[1].flags & I2C_M_RD)) +- dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); +- +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- +- return num; +-} +- +-static u32 dib9000_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static struct i2c_algorithm dib9000_tuner_algo = { +- .master_xfer = dib9000_tuner_xfer, +- .functionality = dib9000_i2c_func, +-}; +- +-static struct i2c_algorithm dib9000_component_bus_algo = { +- .master_xfer = dib9000_fw_component_bus_xfer, +- .functionality = dib9000_i2c_func, +-}; +- +-struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +-{ +- struct dib9000_state *st = fe->demodulator_priv; +- return &st->tuner_adap; +-} +-EXPORT_SYMBOL(dib9000_get_tuner_interface); +- +-struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +-{ +- struct dib9000_state *st = fe->demodulator_priv; +- return &st->component_bus; +-} +-EXPORT_SYMBOL(dib9000_get_component_bus_interface); +- +-struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +-{ +- struct dib9000_state *st = fe->demodulator_priv; +- return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +-} +-EXPORT_SYMBOL(dib9000_get_i2c_master); +- +-int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +-{ +- struct dib9000_state *st = fe->demodulator_priv; +- +- st->i2c.i2c_adap = i2c; +- return 0; +-} +-EXPORT_SYMBOL(dib9000_set_i2c_adapter); +- +-static int dib9000_cfg_gpio(struct dib9000_state *st, u8 num, u8 dir, u8 val) +-{ +- st->gpio_dir = dib9000_read_word(st, 773); +- st->gpio_dir &= ~(1 << num); /* reset the direction bit */ +- st->gpio_dir |= (dir & 0x1) << num; /* set the new direction */ +- dib9000_write_word(st, 773, st->gpio_dir); +- +- st->gpio_val = dib9000_read_word(st, 774); +- st->gpio_val &= ~(1 << num); /* reset the direction bit */ +- st->gpio_val |= (val & 0x01) << num; /* set the new value */ +- dib9000_write_word(st, 774, st->gpio_val); +- +- dprintk("gpio dir: %04x: gpio val: %04x", st->gpio_dir, st->gpio_val); +- +- return 0; +-} +- +-int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- return dib9000_cfg_gpio(state, num, dir, val); +-} +-EXPORT_SYMBOL(dib9000_set_gpio); +- +-int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 val; +- int ret; +- +- if ((state->pid_ctrl_index != -2) && (state->pid_ctrl_index < 9)) { +- /* postpone the pid filtering cmd */ +- dprintk("pid filter cmd postpone"); +- state->pid_ctrl_index++; +- state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER_CTRL; +- state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; +- return 0; +- } +- +- DibAcquireLock(&state->demod_lock); +- +- val = dib9000_read_word(state, 294 + 1) & 0xffef; +- val |= (onoff & 0x1) << 4; +- +- dprintk("PID filter enabled %d", onoff); +- ret = dib9000_write_word(state, 294 + 1, val); +- DibReleaseLock(&state->demod_lock); +- return ret; +- +-} +-EXPORT_SYMBOL(dib9000_fw_pid_filter_ctrl); +- +-int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- int ret; +- +- if (state->pid_ctrl_index != -2) { +- /* postpone the pid filtering cmd */ +- dprintk("pid filter postpone"); +- if (state->pid_ctrl_index < 9) { +- state->pid_ctrl_index++; +- state->pid_ctrl[state->pid_ctrl_index].cmd = DIB9000_PID_FILTER; +- state->pid_ctrl[state->pid_ctrl_index].id = id; +- state->pid_ctrl[state->pid_ctrl_index].pid = pid; +- state->pid_ctrl[state->pid_ctrl_index].onoff = onoff; +- } else +- dprintk("can not add any more pid ctrl cmd"); +- return 0; +- } +- +- DibAcquireLock(&state->demod_lock); +- dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); +- ret = dib9000_write_word(state, 300 + 1 + id, +- onoff ? (1 << 13) | pid : 0); +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +-EXPORT_SYMBOL(dib9000_fw_pid_filter); +- +-int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- return dib9000_fw_init(state); +-} +-EXPORT_SYMBOL(dib9000_firmware_post_pll_init); +- +-static void dib9000_release(struct dvb_frontend *demod) +-{ +- struct dib9000_state *st = demod->demodulator_priv; +- u8 index_frontend; +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) +- dvb_frontend_detach(st->fe[index_frontend]); +- +- DibFreeLock(&state->platform.risc.mbx_if_lock); +- DibFreeLock(&state->platform.risc.mbx_lock); +- DibFreeLock(&state->platform.risc.mem_lock); +- DibFreeLock(&state->platform.risc.mem_mbx_lock); +- DibFreeLock(&state->demod_lock); +- dibx000_exit_i2c_master(&st->i2c_master); +- +- i2c_del_adapter(&st->tuner_adap); +- i2c_del_adapter(&st->component_bus); +- kfree(st->fe[0]); +- kfree(st); +-} +- +-static int dib9000_wakeup(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int dib9000_sleep(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- int ret = 0; +- +- DibAcquireLock(&state->demod_lock); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]); +- if (ret < 0) +- goto error; +- } +- ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); +- +-error: +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +- +-static int dib9000_fe_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static int dib9000_get_frontend(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend, sub_index_frontend; +- fe_status_t stat; +- int ret = 0; +- +- if (state->get_frontend_internal == 0) +- DibAcquireLock(&state->demod_lock); +- +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat); +- if (stat & FE_HAS_SYNC) { +- dprintk("TPS lock on the slave%i", index_frontend); +- +- /* synchronize the cache with the other frontends */ +- state->fe[index_frontend]->ops.get_frontend(state->fe[index_frontend]); +- for (sub_index_frontend = 0; (sub_index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[sub_index_frontend] != NULL); +- sub_index_frontend++) { +- if (sub_index_frontend != index_frontend) { +- state->fe[sub_index_frontend]->dtv_property_cache.modulation = +- state->fe[index_frontend]->dtv_property_cache.modulation; +- state->fe[sub_index_frontend]->dtv_property_cache.inversion = +- state->fe[index_frontend]->dtv_property_cache.inversion; +- state->fe[sub_index_frontend]->dtv_property_cache.transmission_mode = +- state->fe[index_frontend]->dtv_property_cache.transmission_mode; +- state->fe[sub_index_frontend]->dtv_property_cache.guard_interval = +- state->fe[index_frontend]->dtv_property_cache.guard_interval; +- state->fe[sub_index_frontend]->dtv_property_cache.hierarchy = +- state->fe[index_frontend]->dtv_property_cache.hierarchy; +- state->fe[sub_index_frontend]->dtv_property_cache.code_rate_HP = +- state->fe[index_frontend]->dtv_property_cache.code_rate_HP; +- state->fe[sub_index_frontend]->dtv_property_cache.code_rate_LP = +- state->fe[index_frontend]->dtv_property_cache.code_rate_LP; +- state->fe[sub_index_frontend]->dtv_property_cache.rolloff = +- state->fe[index_frontend]->dtv_property_cache.rolloff; +- } +- } +- ret = 0; +- goto return_value; +- } +- } +- +- /* get the channel from master chip */ +- ret = dib9000_fw_get_channel(fe); +- if (ret != 0) +- goto return_value; +- +- /* synchronize the cache with the other frontends */ +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->dtv_property_cache.inversion = fe->dtv_property_cache.inversion; +- state->fe[index_frontend]->dtv_property_cache.transmission_mode = fe->dtv_property_cache.transmission_mode; +- state->fe[index_frontend]->dtv_property_cache.guard_interval = fe->dtv_property_cache.guard_interval; +- state->fe[index_frontend]->dtv_property_cache.modulation = fe->dtv_property_cache.modulation; +- state->fe[index_frontend]->dtv_property_cache.hierarchy = fe->dtv_property_cache.hierarchy; +- state->fe[index_frontend]->dtv_property_cache.code_rate_HP = fe->dtv_property_cache.code_rate_HP; +- state->fe[index_frontend]->dtv_property_cache.code_rate_LP = fe->dtv_property_cache.code_rate_LP; +- state->fe[index_frontend]->dtv_property_cache.rolloff = fe->dtv_property_cache.rolloff; +- } +- ret = 0; +- +-return_value: +- if (state->get_frontend_internal == 0) +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +- +-static int dib9000_set_tune_state(struct dvb_frontend *fe, enum frontend_tune_state tune_state) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- state->tune_state = tune_state; +- if (tune_state == CT_DEMOD_START) +- state->status = FE_STATUS_TUNE_PENDING; +- +- return 0; +-} +- +-static u32 dib9000_get_status(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- return state->status; +-} +- +-static int dib9000_set_channel_status(struct dvb_frontend *fe, struct dvb_frontend_parametersContext *channel_status) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- +- memcpy(&state->channel_status, channel_status, sizeof(struct dvb_frontend_parametersContext)); +- return 0; +-} +- +-static int dib9000_set_frontend(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- int sleep_time, sleep_time_slave; +- u32 frontend_status; +- u8 nbr_pending, exit_condition, index_frontend, index_frontend_success; +- struct dvb_frontend_parametersContext channel_status; +- +- /* check that the correct parameters are set */ +- if (state->fe[0]->dtv_property_cache.frequency == 0) { +- dprintk("dib9000: must specify frequency "); +- return 0; +- } +- +- if (state->fe[0]->dtv_property_cache.bandwidth_hz == 0) { +- dprintk("dib9000: must specify bandwidth "); +- return 0; +- } +- +- state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ +- DibAcquireLock(&state->demod_lock); +- +- fe->dtv_property_cache.delivery_system = SYS_DVBT; +- +- /* set the master status */ +- if (state->fe[0]->dtv_property_cache.transmission_mode == TRANSMISSION_MODE_AUTO || +- state->fe[0]->dtv_property_cache.guard_interval == GUARD_INTERVAL_AUTO || +- state->fe[0]->dtv_property_cache.modulation == QAM_AUTO || +- state->fe[0]->dtv_property_cache.code_rate_HP == FEC_AUTO) { +- /* no channel specified, autosearch the channel */ +- state->channel_status.status = CHANNEL_STATUS_PARAMETERS_UNKNOWN; +- } else +- state->channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; +- +- /* set mode and status for the different frontends */ +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- dib9000_fw_set_diversity_in(state->fe[index_frontend], 1); +- +- /* synchronization of the cache */ +- memcpy(&state->fe[index_frontend]->dtv_property_cache, &fe->dtv_property_cache, sizeof(struct dtv_frontend_properties)); +- +- state->fe[index_frontend]->dtv_property_cache.delivery_system = SYS_DVBT; +- dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_HIGH_Z); +- +- dib9000_set_channel_status(state->fe[index_frontend], &state->channel_status); +- dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); +- } +- +- /* actual tune */ +- exit_condition = 0; /* 0: tune pending; 1: tune failed; 2:tune success */ +- index_frontend_success = 0; +- do { +- sleep_time = dib9000_fw_tune(state->fe[0]); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); +- if (sleep_time == FE_CALLBACK_TIME_NEVER) +- sleep_time = sleep_time_slave; +- else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) +- sleep_time = sleep_time_slave; +- } +- if (sleep_time != FE_CALLBACK_TIME_NEVER) +- msleep(sleep_time / 10); +- else +- break; +- +- nbr_pending = 0; +- exit_condition = 0; +- index_frontend_success = 0; +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- frontend_status = -dib9000_get_status(state->fe[index_frontend]); +- if (frontend_status > -FE_STATUS_TUNE_PENDING) { +- exit_condition = 2; /* tune success */ +- index_frontend_success = index_frontend; +- break; +- } +- if (frontend_status == -FE_STATUS_TUNE_PENDING) +- nbr_pending++; /* some frontends are still tuning */ +- } +- if ((exit_condition != 2) && (nbr_pending == 0)) +- exit_condition = 1; /* if all tune are done and no success, exit: tune failed */ +- +- } while (exit_condition == 0); +- +- /* check the tune result */ +- if (exit_condition == 1) { /* tune failed */ +- dprintk("tune failed"); +- DibReleaseLock(&state->demod_lock); +- /* tune failed; put all the pid filtering cmd to junk */ +- state->pid_ctrl_index = -1; +- return 0; +- } +- +- dprintk("tune success on frontend%i", index_frontend_success); +- +- /* synchronize all the channel cache */ +- state->get_frontend_internal = 1; +- dib9000_get_frontend(state->fe[0]); +- state->get_frontend_internal = 0; +- +- /* retune the other frontends with the found channel */ +- channel_status.status = CHANNEL_STATUS_PARAMETERS_SET; +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- /* only retune the frontends which was not tuned success */ +- if (index_frontend != index_frontend_success) { +- dib9000_set_channel_status(state->fe[index_frontend], &channel_status); +- dib9000_set_tune_state(state->fe[index_frontend], CT_DEMOD_START); +- } +- } +- do { +- sleep_time = FE_CALLBACK_TIME_NEVER; +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- if (index_frontend != index_frontend_success) { +- sleep_time_slave = dib9000_fw_tune(state->fe[index_frontend]); +- if (sleep_time == FE_CALLBACK_TIME_NEVER) +- sleep_time = sleep_time_slave; +- else if ((sleep_time_slave != FE_CALLBACK_TIME_NEVER) && (sleep_time_slave > sleep_time)) +- sleep_time = sleep_time_slave; +- } +- } +- if (sleep_time != FE_CALLBACK_TIME_NEVER) +- msleep(sleep_time / 10); +- else +- break; +- +- nbr_pending = 0; +- for (index_frontend = 0; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- if (index_frontend != index_frontend_success) { +- frontend_status = -dib9000_get_status(state->fe[index_frontend]); +- if ((index_frontend != index_frontend_success) && (frontend_status == -FE_STATUS_TUNE_PENDING)) +- nbr_pending++; /* some frontends are still tuning */ +- } +- } +- } while (nbr_pending != 0); +- +- /* set the output mode */ +- dib9000_fw_set_output_mode(state->fe[0], state->chip.d9.cfg.output_mode); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- dib9000_fw_set_output_mode(state->fe[index_frontend], OUTMODE_DIVERSITY); +- +- /* turn off the diversity for the last frontend */ +- dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); +- +- DibReleaseLock(&state->demod_lock); +- if (state->pid_ctrl_index >= 0) { +- u8 index_pid_filter_cmd; +- u8 pid_ctrl_index = state->pid_ctrl_index; +- +- state->pid_ctrl_index = -2; +- for (index_pid_filter_cmd = 0; +- index_pid_filter_cmd <= pid_ctrl_index; +- index_pid_filter_cmd++) { +- if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER_CTRL) +- dib9000_fw_pid_filter_ctrl(state->fe[0], +- state->pid_ctrl[index_pid_filter_cmd].onoff); +- else if (state->pid_ctrl[index_pid_filter_cmd].cmd == DIB9000_PID_FILTER) +- dib9000_fw_pid_filter(state->fe[0], +- state->pid_ctrl[index_pid_filter_cmd].id, +- state->pid_ctrl[index_pid_filter_cmd].pid, +- state->pid_ctrl[index_pid_filter_cmd].onoff); +- } +- } +- /* do not postpone any more the pid filtering */ +- state->pid_ctrl_index = -2; +- +- return 0; +-} +- +-static u16 dib9000_read_lock(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- +- return dib9000_read_word(state, 535); +-} +- +-static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- u16 lock = 0, lock_slave = 0; +- +- DibAcquireLock(&state->demod_lock); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- lock_slave |= dib9000_read_lock(state->fe[index_frontend]); +- +- lock = dib9000_read_word(state, 535); +- +- *stat = 0; +- +- if ((lock & 0x8000) || (lock_slave & 0x8000)) +- *stat |= FE_HAS_SIGNAL; +- if ((lock & 0x3000) || (lock_slave & 0x3000)) +- *stat |= FE_HAS_CARRIER; +- if ((lock & 0x0100) || (lock_slave & 0x0100)) +- *stat |= FE_HAS_VITERBI; +- if (((lock & 0x0038) == 0x38) || ((lock_slave & 0x0038) == 0x38)) +- *stat |= FE_HAS_SYNC; +- if ((lock & 0x0008) || (lock_slave & 0x0008)) +- *stat |= FE_HAS_LOCK; +- +- DibReleaseLock(&state->demod_lock); +- +- return 0; +-} +- +-static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 *c; +- int ret = 0; +- +- DibAcquireLock(&state->demod_lock); +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- ret = -EIO; +- goto error; +- } +- dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, +- state->i2c_read_buffer, 16 * 2); +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- +- c = (u16 *)state->i2c_read_buffer; +- +- *ber = c[10] << 16 | c[11]; +- +-error: +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +- +-static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- u16 *c = (u16 *)state->i2c_read_buffer; +- u16 val; +- int ret = 0; +- +- DibAcquireLock(&state->demod_lock); +- *strength = 0; +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) { +- state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val); +- if (val > 65535 - *strength) +- *strength = 65535; +- else +- *strength += val; +- } +- +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { +- ret = -EIO; +- goto error; +- } +- dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- +- val = 65535 - c[4]; +- if (val > 65535 - *strength) +- *strength = 65535; +- else +- *strength += val; +- +-error: +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +- +-static u32 dib9000_get_snr(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 *c = (u16 *)state->i2c_read_buffer; +- u32 n, s, exp; +- u16 val; +- +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) +- return -EIO; +- dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- +- val = c[7]; +- n = (val >> 4) & 0xff; +- exp = ((val & 0xf) << 2); +- val = c[8]; +- exp += ((val >> 14) & 0x3); +- if ((exp & 0x20) != 0) +- exp -= 0x40; +- n <<= exp + 16; +- +- s = (val >> 6) & 0xFF; +- exp = (val & 0x3F); +- if ((exp & 0x20) != 0) +- exp -= 0x40; +- s <<= exp + 16; +- +- if (n > 0) { +- u32 t = (s / n) << 16; +- return t + ((s << 16) - n * t) / n; +- } +- return 0xffffffff; +-} +- +-static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend; +- u32 snr_master; +- +- DibAcquireLock(&state->demod_lock); +- snr_master = dib9000_get_snr(fe); +- for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) +- snr_master += dib9000_get_snr(state->fe[index_frontend]); +- +- if ((snr_master >> 16) != 0) { +- snr_master = 10 * intlog10(snr_master >> 16); +- *snr = snr_master / ((1 << 24) / 10); +- } else +- *snr = 0; +- +- DibReleaseLock(&state->demod_lock); +- +- return 0; +-} +- +-static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u16 *c = (u16 *)state->i2c_read_buffer; +- int ret = 0; +- +- DibAcquireLock(&state->demod_lock); +- DibAcquireLock(&state->platform.risc.mem_mbx_lock); +- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { +- ret = -EIO; +- goto error; +- } +- dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); +- DibReleaseLock(&state->platform.risc.mem_mbx_lock); +- +- *unc = c[12]; +- +-error: +- DibReleaseLock(&state->demod_lock); +- return ret; +-} +- +-int dib9000_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, u8 first_addr) +-{ +- int k = 0, ret = 0; +- u8 new_addr = 0; +- struct i2c_device client = {.i2c_adap = i2c }; +- +- client.i2c_write_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); +- if (!client.i2c_write_buffer) { +- dprintk("%s: not enough memory", __func__); +- return -ENOMEM; +- } +- client.i2c_read_buffer = kzalloc(4 * sizeof(u8), GFP_KERNEL); +- if (!client.i2c_read_buffer) { +- dprintk("%s: not enough memory", __func__); +- ret = -ENOMEM; +- goto error_memory; +- } +- +- client.i2c_addr = default_addr + 16; +- dib9000_i2c_write16(&client, 1796, 0x0); +- +- for (k = no_of_demods - 1; k >= 0; k--) { +- /* designated i2c address */ +- new_addr = first_addr + (k << 1); +- client.i2c_addr = default_addr; +- +- dib9000_i2c_write16(&client, 1817, 3); +- dib9000_i2c_write16(&client, 1796, 0); +- dib9000_i2c_write16(&client, 1227, 1); +- dib9000_i2c_write16(&client, 1227, 0); +- +- client.i2c_addr = new_addr; +- dib9000_i2c_write16(&client, 1817, 3); +- dib9000_i2c_write16(&client, 1796, 0); +- dib9000_i2c_write16(&client, 1227, 1); +- dib9000_i2c_write16(&client, 1227, 0); +- +- if (dib9000_identify(&client) == 0) { +- client.i2c_addr = default_addr; +- if (dib9000_identify(&client) == 0) { +- dprintk("DiB9000 #%d: not identified", k); +- ret = -EIO; +- goto error; +- } +- } +- +- dib9000_i2c_write16(&client, 1795, (1 << 10) | (4 << 6)); +- dib9000_i2c_write16(&client, 1794, (new_addr << 2) | 2); +- +- dprintk("IC %d initialized (to i2c_address 0x%x)", k, new_addr); +- } +- +- for (k = 0; k < no_of_demods; k++) { +- new_addr = first_addr | (k << 1); +- client.i2c_addr = new_addr; +- +- dib9000_i2c_write16(&client, 1794, (new_addr << 2)); +- dib9000_i2c_write16(&client, 1795, 0); +- } +- +-error: +- kfree(client.i2c_read_buffer); +-error_memory: +- kfree(client.i2c_write_buffer); +- +- return ret; +-} +-EXPORT_SYMBOL(dib9000_i2c_enumeration); +- +-int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend = 1; +- +- while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) +- index_frontend++; +- if (index_frontend < MAX_NUMBER_OF_FRONTENDS) { +- dprintk("set slave fe %p to index %i", fe_slave, index_frontend); +- state->fe[index_frontend] = fe_slave; +- return 0; +- } +- +- dprintk("too many slave frontend"); +- return -ENOMEM; +-} +-EXPORT_SYMBOL(dib9000_set_slave_frontend); +- +-int dib9000_remove_slave_frontend(struct dvb_frontend *fe) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- u8 index_frontend = 1; +- +- while ((index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL)) +- index_frontend++; +- if (index_frontend != 1) { +- dprintk("remove slave fe %p (index %i)", state->fe[index_frontend - 1], index_frontend - 1); +- state->fe[index_frontend] = NULL; +- return 0; +- } +- +- dprintk("no frontend to be removed"); +- return -ENODEV; +-} +-EXPORT_SYMBOL(dib9000_remove_slave_frontend); +- +-struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +-{ +- struct dib9000_state *state = fe->demodulator_priv; +- +- if (slave_index >= MAX_NUMBER_OF_FRONTENDS) +- return NULL; +- return state->fe[slave_index]; +-} +-EXPORT_SYMBOL(dib9000_get_slave_frontend); +- +-static struct dvb_frontend_ops dib9000_ops; +-struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg) +-{ +- struct dvb_frontend *fe; +- struct dib9000_state *st; +- st = kzalloc(sizeof(struct dib9000_state), GFP_KERNEL); +- if (st == NULL) +- return NULL; +- fe = kzalloc(sizeof(struct dvb_frontend), GFP_KERNEL); +- if (fe == NULL) { +- kfree(st); +- return NULL; +- } +- +- memcpy(&st->chip.d9.cfg, cfg, sizeof(struct dib9000_config)); +- st->i2c.i2c_adap = i2c_adap; +- st->i2c.i2c_addr = i2c_addr; +- st->i2c.i2c_write_buffer = st->i2c_write_buffer; +- st->i2c.i2c_read_buffer = st->i2c_read_buffer; +- +- st->gpio_dir = DIB9000_GPIO_DEFAULT_DIRECTIONS; +- st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; +- st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; +- +- DibInitLock(&st->platform.risc.mbx_if_lock); +- DibInitLock(&st->platform.risc.mbx_lock); +- DibInitLock(&st->platform.risc.mem_lock); +- DibInitLock(&st->platform.risc.mem_mbx_lock); +- DibInitLock(&st->demod_lock); +- st->get_frontend_internal = 0; +- +- st->pid_ctrl_index = -2; +- +- st->fe[0] = fe; +- fe->demodulator_priv = st; +- memcpy(&st->fe[0]->ops, &dib9000_ops, sizeof(struct dvb_frontend_ops)); +- +- /* Ensure the output mode remains at the previous default if it's +- * not specifically set by the caller. +- */ +- if ((st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_SERIAL) && (st->chip.d9.cfg.output_mode != OUTMODE_MPEG2_PAR_GATED_CLK)) +- st->chip.d9.cfg.output_mode = OUTMODE_MPEG2_FIFO; +- +- if (dib9000_identify(&st->i2c) == 0) +- goto error; +- +- dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c.i2c_adap, st->i2c.i2c_addr); +- +- st->tuner_adap.dev.parent = i2c_adap->dev.parent; +- strncpy(st->tuner_adap.name, "DIB9000_FW TUNER ACCESS", sizeof(st->tuner_adap.name)); +- st->tuner_adap.algo = &dib9000_tuner_algo; +- st->tuner_adap.algo_data = NULL; +- i2c_set_adapdata(&st->tuner_adap, st); +- if (i2c_add_adapter(&st->tuner_adap) < 0) +- goto error; +- +- st->component_bus.dev.parent = i2c_adap->dev.parent; +- strncpy(st->component_bus.name, "DIB9000_FW COMPONENT BUS ACCESS", sizeof(st->component_bus.name)); +- st->component_bus.algo = &dib9000_component_bus_algo; +- st->component_bus.algo_data = NULL; +- st->component_bus_speed = 340; +- i2c_set_adapdata(&st->component_bus, st); +- if (i2c_add_adapter(&st->component_bus) < 0) +- goto component_bus_add_error; +- +- dib9000_fw_reset(fe); +- +- return fe; +- +-component_bus_add_error: +- i2c_del_adapter(&st->tuner_adap); +-error: +- kfree(st); +- return NULL; +-} +-EXPORT_SYMBOL(dib9000_attach); +- +-static struct dvb_frontend_ops dib9000_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "DiBcom 9000", +- .frequency_min = 44250000, +- .frequency_max = 867250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER | FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dib9000_release, +- +- .init = dib9000_wakeup, +- .sleep = dib9000_sleep, +- +- .set_frontend = dib9000_set_frontend, +- .get_tune_settings = dib9000_fe_get_tune_settings, +- .get_frontend = dib9000_get_frontend, +- +- .read_status = dib9000_read_status, +- .read_ber = dib9000_read_ber, +- .read_signal_strength = dib9000_read_signal_strength, +- .read_snr = dib9000_read_snr, +- .read_ucblocks = dib9000_read_unc_blocks, +-}; +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_AUTHOR("Olivier Grenie "); +-MODULE_DESCRIPTION("Driver for the DiBcom 9000 COFDM demodulator"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dib9000.h b/drivers/media/dvb/frontends/dib9000.h +deleted file mode 100644 +index b5781a4..0000000 +--- a/drivers/media/dvb/frontends/dib9000.h ++++ /dev/null +@@ -1,131 +0,0 @@ +-#ifndef DIB9000_H +-#define DIB9000_H +- +-#include "dibx000_common.h" +- +-struct dib9000_config { +- u8 dvbt_mode; +- u8 output_mpeg2_in_188_bytes; +- u8 hostbus_diversity; +- struct dibx000_bandwidth_config *bw; +- +- u16 if_drives; +- +- u32 timing_frequency; +- u32 xtal_clock_khz; +- u32 vcxo_timer; +- u32 demod_clock_khz; +- +- const u8 *microcode_B_fe_buffer; +- u32 microcode_B_fe_size; +- +- struct dibGPIOFunction gpio_function[2]; +- struct dibSubbandSelection subband; +- +- u8 output_mode; +-}; +- +-#define DEFAULT_DIB9000_I2C_ADDRESS 18 +- +-#if defined(CONFIG_DVB_DIB9000) || (defined(CONFIG_DVB_DIB9000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, const struct dib9000_config *cfg); +-extern int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr); +-extern struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe); +-extern struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating); +-extern int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val); +-extern int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); +-extern int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff); +-extern int dib9000_firmware_post_pll_init(struct dvb_frontend *fe); +-extern int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave); +-extern int dib9000_remove_slave_frontend(struct dvb_frontend *fe); +-extern struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index); +-extern struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe); +-extern int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c); +-extern int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed); +-#else +-static inline struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib9000_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct i2c_adapter *dib9000_get_i2c_master(struct dvb_frontend *fe, enum dibx000_i2c_interface intf, int gating) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib9000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u8 default_addr, u8 first_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline struct i2c_adapter *dib9000_get_tuner_interface(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib9000_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib9000_firmware_post_pll_init(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib9000_set_slave_frontend(struct dvb_frontend *fe, struct dvb_frontend *fe_slave) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-int dib9000_remove_slave_frontend(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline struct dvb_frontend *dib9000_get_slave_frontend(struct dvb_frontend *fe, int slave_index) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct i2c_adapter *dib9000_get_component_bus_interface(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int dib9000_set_i2c_adapter(struct dvb_frontend *fe, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +- +-static inline int dib9000_fw_set_component_bus_speed(struct dvb_frontend *fe, u16 speed) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c +deleted file mode 100644 +index 43be723..0000000 +--- a/drivers/media/dvb/frontends/dibx000_common.c ++++ /dev/null +@@ -1,515 +0,0 @@ +-#include +-#include +-#include +- +-#include "dibx000_common.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); +- +-#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); printk("\n"); } } while (0) +- +-static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- mst->i2c_write_buffer[0] = (reg >> 8) & 0xff; +- mst->i2c_write_buffer[1] = reg & 0xff; +- mst->i2c_write_buffer[2] = (val >> 8) & 0xff; +- mst->i2c_write_buffer[3] = val & 0xff; +- +- memset(mst->msg, 0, sizeof(struct i2c_msg)); +- mst->msg[0].addr = mst->i2c_addr; +- mst->msg[0].flags = 0; +- mst->msg[0].buf = mst->i2c_write_buffer; +- mst->msg[0].len = 4; +- +- ret = i2c_transfer(mst->i2c_adap, mst->msg, 1) != 1 ? -EREMOTEIO : 0; +- mutex_unlock(&mst->i2c_buffer_lock); +- +- return ret; +-} +- +-static u16 dibx000_read_word(struct dibx000_i2c_master *mst, u16 reg) +-{ +- u16 ret; +- +- if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return 0; +- } +- +- mst->i2c_write_buffer[0] = reg >> 8; +- mst->i2c_write_buffer[1] = reg & 0xff; +- +- memset(mst->msg, 0, 2 * sizeof(struct i2c_msg)); +- mst->msg[0].addr = mst->i2c_addr; +- mst->msg[0].flags = 0; +- mst->msg[0].buf = mst->i2c_write_buffer; +- mst->msg[0].len = 2; +- mst->msg[1].addr = mst->i2c_addr; +- mst->msg[1].flags = I2C_M_RD; +- mst->msg[1].buf = mst->i2c_read_buffer; +- mst->msg[1].len = 2; +- +- if (i2c_transfer(mst->i2c_adap, mst->msg, 2) != 2) +- dprintk("i2c read error on %d", reg); +- +- ret = (mst->i2c_read_buffer[0] << 8) | mst->i2c_read_buffer[1]; +- mutex_unlock(&mst->i2c_buffer_lock); +- +- return ret; +-} +- +-static int dibx000_is_i2c_done(struct dibx000_i2c_master *mst) +-{ +- int i = 100; +- u16 status; +- +- while (((status = dibx000_read_word(mst, mst->base_reg + 2)) & 0x0100) == 0 && --i > 0) +- ; +- +- /* i2c timed out */ +- if (i == 0) +- return -EREMOTEIO; +- +- /* no acknowledge */ +- if ((status & 0x0080) == 0) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int dibx000_master_i2c_write(struct dibx000_i2c_master *mst, struct i2c_msg *msg, u8 stop) +-{ +- u16 data; +- u16 da; +- u16 i; +- u16 txlen = msg->len, len; +- const u8 *b = msg->buf; +- +- while (txlen) { +- dibx000_read_word(mst, mst->base_reg + 2); +- +- len = txlen > 8 ? 8 : txlen; +- for (i = 0; i < len; i += 2) { +- data = *b++ << 8; +- if (i+1 < len) +- data |= *b++; +- dibx000_write_word(mst, mst->base_reg, data); +- } +- da = (((u8) (msg->addr)) << 9) | +- (1 << 8) | +- (1 << 7) | +- (0 << 6) | +- (0 << 5) | +- ((len & 0x7) << 2) | +- (0 << 1) | +- (0 << 0); +- +- if (txlen == msg->len) +- da |= 1 << 5; /* start */ +- +- if (txlen-len == 0 && stop) +- da |= 1 << 6; /* stop */ +- +- dibx000_write_word(mst, mst->base_reg+1, da); +- +- if (dibx000_is_i2c_done(mst) != 0) +- return -EREMOTEIO; +- txlen -= len; +- } +- +- return 0; +-} +- +-static int dibx000_master_i2c_read(struct dibx000_i2c_master *mst, struct i2c_msg *msg) +-{ +- u16 da; +- u8 *b = msg->buf; +- u16 rxlen = msg->len, len; +- +- while (rxlen) { +- len = rxlen > 8 ? 8 : rxlen; +- da = (((u8) (msg->addr)) << 9) | +- (1 << 8) | +- (1 << 7) | +- (0 << 6) | +- (0 << 5) | +- ((len & 0x7) << 2) | +- (1 << 1) | +- (0 << 0); +- +- if (rxlen == msg->len) +- da |= 1 << 5; /* start */ +- +- if (rxlen-len == 0) +- da |= 1 << 6; /* stop */ +- dibx000_write_word(mst, mst->base_reg+1, da); +- +- if (dibx000_is_i2c_done(mst) != 0) +- return -EREMOTEIO; +- +- rxlen -= len; +- +- while (len) { +- da = dibx000_read_word(mst, mst->base_reg); +- *b++ = (da >> 8) & 0xff; +- len--; +- if (len >= 1) { +- *b++ = da & 0xff; +- len--; +- } +- } +- } +- +- return 0; +-} +- +-int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed) +-{ +- struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); +- +- if (mst->device_rev < DIB7000MC && speed < 235) +- speed = 235; +- return dibx000_write_word(mst, mst->base_reg + 3, (u16)(60000 / speed)); +- +-} +-EXPORT_SYMBOL(dibx000_i2c_set_speed); +- +-static u32 dibx000_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, +- enum dibx000_i2c_interface intf) +-{ +- if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) { +- dprintk("selecting interface: %d", intf); +- mst->selected_interface = intf; +- return dibx000_write_word(mst, mst->base_reg + 4, intf); +- } +- return 0; +-} +- +-static int dibx000_i2c_master_xfer_gpio12(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); +- int msg_index; +- int ret = 0; +- +- dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_1_2); +- for (msg_index = 0; msg_index < num; msg_index++) { +- if (msg[msg_index].flags & I2C_M_RD) { +- ret = dibx000_master_i2c_read(mst, &msg[msg_index]); +- if (ret != 0) +- return 0; +- } else { +- ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); +- if (ret != 0) +- return 0; +- } +- } +- +- return num; +-} +- +-static int dibx000_i2c_master_xfer_gpio34(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); +- int msg_index; +- int ret = 0; +- +- dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_3_4); +- for (msg_index = 0; msg_index < num; msg_index++) { +- if (msg[msg_index].flags & I2C_M_RD) { +- ret = dibx000_master_i2c_read(mst, &msg[msg_index]); +- if (ret != 0) +- return 0; +- } else { +- ret = dibx000_master_i2c_write(mst, &msg[msg_index], 1); +- if (ret != 0) +- return 0; +- } +- } +- +- return num; +-} +- +-static struct i2c_algorithm dibx000_i2c_master_gpio12_xfer_algo = { +- .master_xfer = dibx000_i2c_master_xfer_gpio12, +- .functionality = dibx000_i2c_func, +-}; +- +-static struct i2c_algorithm dibx000_i2c_master_gpio34_xfer_algo = { +- .master_xfer = dibx000_i2c_master_xfer_gpio34, +- .functionality = dibx000_i2c_func, +-}; +- +-static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], +- u8 addr, int onoff) +-{ +- u16 val; +- +- +- if (onoff) +- val = addr << 8; // bit 7 = use master or not, if 0, the gate is open +- else +- val = 1 << 7; +- +- if (mst->device_rev > DIB7000) +- val <<= 1; +- +- tx[0] = (((mst->base_reg + 1) >> 8) & 0xff); +- tx[1] = ((mst->base_reg + 1) & 0xff); +- tx[2] = val >> 8; +- tx[3] = val & 0xff; +- +- return 0; +-} +- +-static int dibx000_i2c_gated_gpio67_xfer(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); +- int ret; +- +- if (num > 32) { +- dprintk("%s: too much I2C message to be transmitted (%i).\ +- Maximum is 32", __func__, num); +- return -ENOMEM; +- } +- +- dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_GPIO_6_7); +- +- if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- +- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); +- +- /* open the gate */ +- dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); +- mst->msg[0].addr = mst->i2c_addr; +- mst->msg[0].buf = &mst->i2c_write_buffer[0]; +- mst->msg[0].len = 4; +- +- memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); +- +- /* close the gate */ +- dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); +- mst->msg[num + 1].addr = mst->i2c_addr; +- mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; +- mst->msg[num + 1].len = 4; +- +- ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? +- num : -EIO); +- +- mutex_unlock(&mst->i2c_buffer_lock); +- return ret; +-} +- +-static struct i2c_algorithm dibx000_i2c_gated_gpio67_algo = { +- .master_xfer = dibx000_i2c_gated_gpio67_xfer, +- .functionality = dibx000_i2c_func, +-}; +- +-static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, +- struct i2c_msg msg[], int num) +-{ +- struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap); +- int ret; +- +- if (num > 32) { +- dprintk("%s: too much I2C message to be transmitted (%i).\ +- Maximum is 32", __func__, num); +- return -ENOMEM; +- } +- +- dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); +- +- if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- memset(mst->msg, 0, sizeof(struct i2c_msg) * (2 + num)); +- +- /* open the gate */ +- dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[0], msg[0].addr, 1); +- mst->msg[0].addr = mst->i2c_addr; +- mst->msg[0].buf = &mst->i2c_write_buffer[0]; +- mst->msg[0].len = 4; +- +- memcpy(&mst->msg[1], msg, sizeof(struct i2c_msg) * num); +- +- /* close the gate */ +- dibx000_i2c_gate_ctrl(mst, &mst->i2c_write_buffer[4], 0, 0); +- mst->msg[num + 1].addr = mst->i2c_addr; +- mst->msg[num + 1].buf = &mst->i2c_write_buffer[4]; +- mst->msg[num + 1].len = 4; +- +- ret = (i2c_transfer(mst->i2c_adap, mst->msg, 2 + num) == 2 + num ? +- num : -EIO); +- mutex_unlock(&mst->i2c_buffer_lock); +- return ret; +-} +- +-static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = { +- .master_xfer = dibx000_i2c_gated_tuner_xfer, +- .functionality = dibx000_i2c_func, +-}; +- +-struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, +- enum dibx000_i2c_interface intf, +- int gating) +-{ +- struct i2c_adapter *i2c = NULL; +- +- switch (intf) { +- case DIBX000_I2C_INTERFACE_TUNER: +- if (gating) +- i2c = &mst->gated_tuner_i2c_adap; +- break; +- case DIBX000_I2C_INTERFACE_GPIO_1_2: +- if (!gating) +- i2c = &mst->master_i2c_adap_gpio12; +- break; +- case DIBX000_I2C_INTERFACE_GPIO_3_4: +- if (!gating) +- i2c = &mst->master_i2c_adap_gpio34; +- break; +- case DIBX000_I2C_INTERFACE_GPIO_6_7: +- if (gating) +- i2c = &mst->master_i2c_adap_gpio67; +- break; +- default: +- printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n"); +- break; +- } +- +- return i2c; +-} +- +-EXPORT_SYMBOL(dibx000_get_i2c_adapter); +- +-void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst) +-{ +- /* initialize the i2c-master by closing the gate */ +- u8 tx[4]; +- struct i2c_msg m = {.addr = mst->i2c_addr,.buf = tx,.len = 4 }; +- +- dibx000_i2c_gate_ctrl(mst, tx, 0, 0); +- i2c_transfer(mst->i2c_adap, &m, 1); +- mst->selected_interface = 0xff; // the first time force a select of the I2C +- dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); +-} +- +-EXPORT_SYMBOL(dibx000_reset_i2c_master); +- +-static int i2c_adapter_init(struct i2c_adapter *i2c_adap, +- struct i2c_algorithm *algo, const char *name, +- struct dibx000_i2c_master *mst) +-{ +- strncpy(i2c_adap->name, name, sizeof(i2c_adap->name)); +- i2c_adap->algo = algo; +- i2c_adap->algo_data = NULL; +- i2c_set_adapdata(i2c_adap, mst); +- if (i2c_add_adapter(i2c_adap) < 0) +- return -ENODEV; +- return 0; +-} +- +-int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, +- struct i2c_adapter *i2c_adap, u8 i2c_addr) +-{ +- int ret; +- +- mutex_init(&mst->i2c_buffer_lock); +- if (mutex_lock_interruptible(&mst->i2c_buffer_lock) < 0) { +- dprintk("could not acquire lock"); +- return -EINVAL; +- } +- memset(mst->msg, 0, sizeof(struct i2c_msg)); +- mst->msg[0].addr = i2c_addr >> 1; +- mst->msg[0].flags = 0; +- mst->msg[0].buf = mst->i2c_write_buffer; +- mst->msg[0].len = 4; +- +- mst->device_rev = device_rev; +- mst->i2c_adap = i2c_adap; +- mst->i2c_addr = i2c_addr >> 1; +- +- if (device_rev == DIB7000P || device_rev == DIB8000) +- mst->base_reg = 1024; +- else +- mst->base_reg = 768; +- +- mst->gated_tuner_i2c_adap.dev.parent = mst->i2c_adap->dev.parent; +- if (i2c_adapter_init +- (&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, +- "DiBX000 tuner I2C bus", mst) != 0) +- printk(KERN_ERR +- "DiBX000: could not initialize the tuner i2c_adapter\n"); +- +- mst->master_i2c_adap_gpio12.dev.parent = mst->i2c_adap->dev.parent; +- if (i2c_adapter_init +- (&mst->master_i2c_adap_gpio12, &dibx000_i2c_master_gpio12_xfer_algo, +- "DiBX000 master GPIO12 I2C bus", mst) != 0) +- printk(KERN_ERR +- "DiBX000: could not initialize the master i2c_adapter\n"); +- +- mst->master_i2c_adap_gpio34.dev.parent = mst->i2c_adap->dev.parent; +- if (i2c_adapter_init +- (&mst->master_i2c_adap_gpio34, &dibx000_i2c_master_gpio34_xfer_algo, +- "DiBX000 master GPIO34 I2C bus", mst) != 0) +- printk(KERN_ERR +- "DiBX000: could not initialize the master i2c_adapter\n"); +- +- mst->master_i2c_adap_gpio67.dev.parent = mst->i2c_adap->dev.parent; +- if (i2c_adapter_init +- (&mst->master_i2c_adap_gpio67, &dibx000_i2c_gated_gpio67_algo, +- "DiBX000 master GPIO67 I2C bus", mst) != 0) +- printk(KERN_ERR +- "DiBX000: could not initialize the master i2c_adapter\n"); +- +- /* initialize the i2c-master by closing the gate */ +- dibx000_i2c_gate_ctrl(mst, mst->i2c_write_buffer, 0, 0); +- +- ret = (i2c_transfer(i2c_adap, mst->msg, 1) == 1); +- mutex_unlock(&mst->i2c_buffer_lock); +- +- return ret; +-} +- +-EXPORT_SYMBOL(dibx000_init_i2c_master); +- +-void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) +-{ +- i2c_del_adapter(&mst->gated_tuner_i2c_adap); +- i2c_del_adapter(&mst->master_i2c_adap_gpio12); +- i2c_del_adapter(&mst->master_i2c_adap_gpio34); +- i2c_del_adapter(&mst->master_i2c_adap_gpio67); +-} +-EXPORT_SYMBOL(dibx000_exit_i2c_master); +- +- +-u32 systime(void) +-{ +- struct timespec t; +- +- t = current_kernel_time(); +- return (t.tv_sec * 10000) + (t.tv_nsec / 100000); +-} +-EXPORT_SYMBOL(systime); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Common function the DiBcom demodulator family"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h +deleted file mode 100644 +index 5f48488..0000000 +--- a/drivers/media/dvb/frontends/dibx000_common.h ++++ /dev/null +@@ -1,280 +0,0 @@ +-#ifndef DIBX000_COMMON_H +-#define DIBX000_COMMON_H +- +-enum dibx000_i2c_interface { +- DIBX000_I2C_INTERFACE_TUNER = 0, +- DIBX000_I2C_INTERFACE_GPIO_1_2 = 1, +- DIBX000_I2C_INTERFACE_GPIO_3_4 = 2, +- DIBX000_I2C_INTERFACE_GPIO_6_7 = 3 +-}; +- +-struct dibx000_i2c_master { +-#define DIB3000MC 1 +-#define DIB7000 2 +-#define DIB7000P 11 +-#define DIB7000MC 12 +-#define DIB8000 13 +- u16 device_rev; +- +- enum dibx000_i2c_interface selected_interface; +- +-/* struct i2c_adapter tuner_i2c_adap; */ +- struct i2c_adapter gated_tuner_i2c_adap; +- struct i2c_adapter master_i2c_adap_gpio12; +- struct i2c_adapter master_i2c_adap_gpio34; +- struct i2c_adapter master_i2c_adap_gpio67; +- +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +- +- u16 base_reg; +- +- /* for the I2C transfer */ +- struct i2c_msg msg[34]; +- u8 i2c_write_buffer[8]; +- u8 i2c_read_buffer[2]; +- struct mutex i2c_buffer_lock; +-}; +- +-extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, +- u16 device_rev, struct i2c_adapter *i2c_adap, +- u8 i2c_addr); +-extern struct i2c_adapter *dibx000_get_i2c_adapter(struct dibx000_i2c_master +- *mst, +- enum dibx000_i2c_interface +- intf, int gating); +-extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); +-extern void dibx000_reset_i2c_master(struct dibx000_i2c_master *mst); +-extern int dibx000_i2c_set_speed(struct i2c_adapter *i2c_adap, u16 speed); +- +-extern u32 systime(void); +- +-#define BAND_LBAND 0x01 +-#define BAND_UHF 0x02 +-#define BAND_VHF 0x04 +-#define BAND_SBAND 0x08 +-#define BAND_FM 0x10 +-#define BAND_CBAND 0x20 +- +-#define BAND_OF_FREQUENCY(freq_kHz) ((freq_kHz) <= 170000 ? BAND_CBAND : \ +- (freq_kHz) <= 115000 ? BAND_FM : \ +- (freq_kHz) <= 250000 ? BAND_VHF : \ +- (freq_kHz) <= 863000 ? BAND_UHF : \ +- (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) +- +-struct dibx000_agc_config { +- /* defines the capabilities of this AGC-setting - using the BAND_-defines */ +- u8 band_caps; +- +- u16 setup; +- +- u16 inv_gain; +- u16 time_stabiliz; +- +- u8 alpha_level; +- u16 thlock; +- +- u8 wbd_inv; +- u16 wbd_ref; +- u8 wbd_sel; +- u8 wbd_alpha; +- +- u16 agc1_max; +- u16 agc1_min; +- u16 agc2_max; +- u16 agc2_min; +- +- u8 agc1_pt1; +- u8 agc1_pt2; +- u8 agc1_pt3; +- +- u8 agc1_slope1; +- u8 agc1_slope2; +- +- u8 agc2_pt1; +- u8 agc2_pt2; +- +- u8 agc2_slope1; +- u8 agc2_slope2; +- +- u8 alpha_mant; +- u8 alpha_exp; +- +- u8 beta_mant; +- u8 beta_exp; +- +- u8 perform_agc_softsplit; +- +- struct { +- u16 min; +- u16 max; +- u16 min_thres; +- u16 max_thres; +- } split; +-}; +- +-struct dibx000_bandwidth_config { +- u32 internal; +- u32 sampling; +- +- u8 pll_prediv; +- u8 pll_ratio; +- u8 pll_range; +- u8 pll_reset; +- u8 pll_bypass; +- +- u8 enable_refdiv; +- u8 bypclk_div; +- u8 IO_CLK_en_core; +- u8 ADClkSrc; +- u8 modulo; +- +- u16 sad_cfg; +- +- u32 ifreq; +- u32 timf; +- +- u32 xtal_hz; +-}; +- +-enum dibx000_adc_states { +- DIBX000_SLOW_ADC_ON = 0, +- DIBX000_SLOW_ADC_OFF, +- DIBX000_ADC_ON, +- DIBX000_ADC_OFF, +- DIBX000_VBG_ENABLE, +- DIBX000_VBG_DISABLE, +-}; +- +-#define BANDWIDTH_TO_KHZ(v) ((v) / 1000) +-#define BANDWIDTH_TO_HZ(v) ((v) * 1000) +- +-/* Chip output mode. */ +-#define OUTMODE_HIGH_Z 0 +-#define OUTMODE_MPEG2_PAR_GATED_CLK 1 +-#define OUTMODE_MPEG2_PAR_CONT_CLK 2 +-#define OUTMODE_MPEG2_SERIAL 7 +-#define OUTMODE_DIVERSITY 4 +-#define OUTMODE_MPEG2_FIFO 5 +-#define OUTMODE_ANALOG_ADC 6 +- +-#define INPUT_MODE_OFF 0x11 +-#define INPUT_MODE_DIVERSITY 0x12 +-#define INPUT_MODE_MPEG 0x13 +- +-enum frontend_tune_state { +- CT_TUNER_START = 10, +- CT_TUNER_STEP_0, +- CT_TUNER_STEP_1, +- CT_TUNER_STEP_2, +- CT_TUNER_STEP_3, +- CT_TUNER_STEP_4, +- CT_TUNER_STEP_5, +- CT_TUNER_STEP_6, +- CT_TUNER_STEP_7, +- CT_TUNER_STOP, +- +- CT_AGC_START = 20, +- CT_AGC_STEP_0, +- CT_AGC_STEP_1, +- CT_AGC_STEP_2, +- CT_AGC_STEP_3, +- CT_AGC_STEP_4, +- CT_AGC_STOP, +- +- CT_DEMOD_START = 30, +- CT_DEMOD_STEP_1, +- CT_DEMOD_STEP_2, +- CT_DEMOD_STEP_3, +- CT_DEMOD_STEP_4, +- CT_DEMOD_STEP_5, +- CT_DEMOD_STEP_6, +- CT_DEMOD_STEP_7, +- CT_DEMOD_STEP_8, +- CT_DEMOD_STEP_9, +- CT_DEMOD_STEP_10, +- CT_DEMOD_SEARCH_NEXT = 41, +- CT_DEMOD_STEP_LOCKED, +- CT_DEMOD_STOP, +- +- CT_DONE = 100, +- CT_SHUTDOWN, +- +-}; +- +-struct dvb_frontend_parametersContext { +-#define CHANNEL_STATUS_PARAMETERS_UNKNOWN 0x01 +-#define CHANNEL_STATUS_PARAMETERS_SET 0x02 +- u8 status; +- u32 tune_time_estimation[2]; +- s32 tps_available; +- u16 tps[9]; +-}; +- +-#define FE_STATUS_TUNE_FAILED 0 +-#define FE_STATUS_TUNE_TIMED_OUT -1 +-#define FE_STATUS_TUNE_TIME_TOO_SHORT -2 +-#define FE_STATUS_TUNE_PENDING -3 +-#define FE_STATUS_STD_SUCCESS -4 +-#define FE_STATUS_FFT_SUCCESS -5 +-#define FE_STATUS_DEMOD_SUCCESS -6 +-#define FE_STATUS_LOCKED -7 +-#define FE_STATUS_DATA_LOCKED -8 +- +-#define FE_CALLBACK_TIME_NEVER 0xffffffff +- +-#define ABS(x) ((x < 0) ? (-x) : (x)) +- +-#define DATA_BUS_ACCESS_MODE_8BIT 0x01 +-#define DATA_BUS_ACCESS_MODE_16BIT 0x02 +-#define DATA_BUS_ACCESS_MODE_NO_ADDRESS_INCREMENT 0x10 +- +-struct dibGPIOFunction { +-#define BOARD_GPIO_COMPONENT_BUS_ADAPTER 1 +-#define BOARD_GPIO_COMPONENT_DEMOD 2 +- u8 component; +- +-#define BOARD_GPIO_FUNCTION_BOARD_ON 1 +-#define BOARD_GPIO_FUNCTION_BOARD_OFF 2 +-#define BOARD_GPIO_FUNCTION_COMPONENT_ON 3 +-#define BOARD_GPIO_FUNCTION_COMPONENT_OFF 4 +-#define BOARD_GPIO_FUNCTION_SUBBAND_PWM 5 +-#define BOARD_GPIO_FUNCTION_SUBBAND_GPIO 6 +- u8 function; +- +-/* mask, direction and value are used specify which GPIO to change GPIO0 +- * is LSB and possible GPIO31 is MSB. The same bit-position as in the +- * mask is used for the direction and the value. Direction == 1 is OUT, +- * 0 == IN. For direction "OUT" value is either 1 or 0, for direction IN +- * value has no meaning. +- * +- * In case of BOARD_GPIO_FUNCTION_PWM mask is giving the GPIO to be +- * used to do the PWM. Direction gives the PWModulator to be used. +- * Value gives the PWM value in device-dependent scale. +- */ +- u32 mask; +- u32 direction; +- u32 value; +-}; +- +-#define MAX_NB_SUBBANDS 8 +-struct dibSubbandSelection { +- u8 size; /* Actual number of subbands. */ +- struct { +- u16 f_mhz; +- struct dibGPIOFunction gpio; +- } subband[MAX_NB_SUBBANDS]; +-}; +- +-#define DEMOD_TIMF_SET 0x00 +-#define DEMOD_TIMF_GET 0x01 +-#define DEMOD_TIMF_UPDATE 0x02 +- +-#define MPEG_ON_DIBTX 1 +-#define DIV_ON_DIBTX 2 +-#define ADC_ON_DIBTX 3 +-#define DEMOUT_ON_HOSTBUS 4 +-#define DIBTX_ON_HOSTBUS 5 +-#define MPEG_ON_HOSTBUS 6 +- +-#endif +diff --git a/drivers/media/dvb/frontends/drxd.h b/drivers/media/dvb/frontends/drxd.h +deleted file mode 100644 +index 3439873..0000000 +--- a/drivers/media/dvb/frontends/drxd.h ++++ /dev/null +@@ -1,59 +0,0 @@ +-/* +- * drxd.h: DRXD DVB-T demodulator driver +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#ifndef _DRXD_H_ +-#define _DRXD_H_ +- +-#include +-#include +- +-struct drxd_config { +- u8 index; +- +- u8 pll_address; +- u8 pll_type; +-#define DRXD_PLL_NONE 0 +-#define DRXD_PLL_DTT7520X 1 +-#define DRXD_PLL_MT3X0823 2 +- +- u32 clock; +- u8 insert_rs_byte; +- +- u8 demod_address; +- u8 demoda_address; +- u8 demod_revision; +- +- /* If the tuner is not behind an i2c gate, be sure to flip this bit +- or else the i2c bus could get wedged */ +- u8 disable_i2c_gate_ctrl; +- +- u32 IF; +- s16(*osc_deviation) (void *priv, s16 dev, int flag); +-}; +- +-extern +-struct dvb_frontend *drxd_attach(const struct drxd_config *config, +- void *priv, struct i2c_adapter *i2c, +- struct device *dev); +-extern int drxd_config_i2c(struct dvb_frontend *, int); +-#endif +diff --git a/drivers/media/dvb/frontends/drxd_firm.c b/drivers/media/dvb/frontends/drxd_firm.c +deleted file mode 100644 +index 5418b0b..0000000 +--- a/drivers/media/dvb/frontends/drxd_firm.c ++++ /dev/null +@@ -1,929 +0,0 @@ +-/* +- * drxd_firm.c : DRXD firmware tables +- * +- * Copyright (C) 2006-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-/* TODO: generate this file with a script from a settings file */ +- +-/* Contains A2 firmware version: 1.4.2 +- * Contains B1 firmware version: 3.3.33 +- * Contains settings from driver 1.4.23 +-*/ +- +-#include "drxd_firm.h" +- +-#define ADDRESS(x) ((x) & 0xFF), (((x)>>8) & 0xFF), (((x)>>16) & 0xFF), (((x)>>24) & 0xFF) +-#define LENGTH(x) ((x) & 0xFF), (((x)>>8) & 0xFF) +- +-/* Is written via block write, must be little endian */ +-#define DATA16(x) ((x) & 0xFF), (((x)>>8) & 0xFF) +- +-#define WRBLOCK(a, l) ADDRESS(a), LENGTH(l) +-#define WR16(a, d) ADDRESS(a), LENGTH(1), DATA16(d) +- +-#define END_OF_TABLE 0xFF, 0xFF, 0xFF, 0xFF +- +-/* HI firmware patches */ +- +-#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A +-#define HI_TR_FUNC_SIZE 9 /* size of this function in instruction words */ +- +-u8 DRXD_InitAtomicRead[] = { +- WRBLOCK(HI_TR_FUNC_ADDR, HI_TR_FUNC_SIZE), +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x60, 0x04, /* r0rami.dt -> ring.xba; */ +- 0x61, 0x04, /* r0rami.dt -> ring.xad; */ +- 0xE3, 0x07, /* HI_RA_RAM_USR_BEGIN -> ring.iad; */ +- 0x40, 0x00, /* (long immediate) */ +- 0x64, 0x04, /* r0rami.dt -> ring.len; */ +- 0x65, 0x04, /* r0rami.dt -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x38, 0x00, /* 0 -> jumps.ad; */ +- END_OF_TABLE +-}; +- +-/* Pins D0 and D1 of the parallel MPEG output can be used +- to set the I2C address of a device. */ +- +-#define HI_RST_FUNC_ADDR (HI_IF_RAM_USR_BEGIN__A + HI_TR_FUNC_SIZE) +-#define HI_RST_FUNC_SIZE 54 /* size of this function in instruction words */ +- +-/* D0 Version */ +-u8 DRXD_HiI2cPatch_1[] = { +- WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), +- 0xC8, 0x07, 0x01, 0x00, /* MASK -> reg0.dt; */ +- 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ +- 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ +- 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ +- 0x23, 0x00, /* &data -> ring.iad; */ +- 0x24, 0x00, /* 0 -> ring.len; */ +- 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x42, 0x00, /* &data+1 -> w0ram.ad; */ +- 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ +- 0x63, 0x00, /* &data+1 -> ring.iad; */ +- 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ +- 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ +- 0x23, 0x00, /* &data -> ring.iad; */ +- 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x42, 0x00, /* &data+1 -> w0ram.ad; */ +- 0x0F, 0x04, /* r0ram.dt -> and.op; */ +- 0x1C, 0x06, /* reg0.dt -> and.tr; */ +- 0xCF, 0x04, /* and.rs -> add.op; */ +- 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ +- 0xD0, 0x04, /* add.rs -> add.tr; */ +- 0xC8, 0x04, /* add.rs -> reg0.dt; */ +- 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ +- 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ +- 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ +- 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ +- 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ +- 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ +- 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ +- 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ +- +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- +- /* Force quick and dirty reset */ +- WR16(B_HI_CT_REG_COMM_STATE__A, 0), +- END_OF_TABLE +-}; +- +-/* D0,D1 Version */ +-u8 DRXD_HiI2cPatch_3[] = { +- WRBLOCK(HI_RST_FUNC_ADDR, HI_RST_FUNC_SIZE), +- 0xC8, 0x07, 0x03, 0x00, /* MASK -> reg0.dt; */ +- 0xE0, 0x07, 0x15, 0x02, /* (EC__BLK << 6) + EC_OC_REG__BNK -> ring.xba; */ +- 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ +- 0xA2, 0x00, /* M_BNK_ID_DAT -> ring.iba; */ +- 0x23, 0x00, /* &data -> ring.iad; */ +- 0x24, 0x00, /* 0 -> ring.len; */ +- 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x42, 0x00, /* &data+1 -> w0ram.ad; */ +- 0xC0, 0x07, 0xFF, 0x0F, /* -1 -> w0ram.dt; */ +- 0x63, 0x00, /* &data+1 -> ring.iad; */ +- 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0xE1, 0x07, 0x38, 0x00, /* EC_OC_REG_OCR_MPG_USR_DAT__A -> ring.xad; */ +- 0xA5, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_READ -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0xE1, 0x07, 0x12, 0x00, /* EC_OC_REG_OC_MPG_SIO__A -> ring.xad; */ +- 0x23, 0x00, /* &data -> ring.iad; */ +- 0x65, 0x02, /* M_RC_CTR_SWAP | M_RC_CTR_WRITE -> ring.ctl; */ +- 0x26, 0x00, /* 0 -> ring.rdy; */ +- 0x42, 0x00, /* &data+1 -> w0ram.ad; */ +- 0x0F, 0x04, /* r0ram.dt -> and.op; */ +- 0x1C, 0x06, /* reg0.dt -> and.tr; */ +- 0xCF, 0x04, /* and.rs -> add.op; */ +- 0xD0, 0x07, 0x70, 0x00, /* DEF_DEV_ID -> add.tr; */ +- 0xD0, 0x04, /* add.rs -> add.tr; */ +- 0xC8, 0x04, /* add.rs -> reg0.dt; */ +- 0x60, 0x00, /* reg0.dt -> w0ram.dt; */ +- 0xC2, 0x07, 0x10, 0x00, /* SLV0_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ +- 0xC2, 0x07, 0x20, 0x00, /* SLV1_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x06, /* reg0.dt -> w0rami.dt; */ +- 0xC2, 0x07, 0x30, 0x00, /* CMD_BASE -> w0rami.ad; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x01, 0x00, /* 0 -> w0rami.dt; */ +- 0x68, 0x00, /* M_IC_SEL_PT1 -> i2c.sel; */ +- 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ +- 0x28, 0x00, /* M_IC_SEL_PT0 -> i2c.sel; */ +- 0x29, 0x00, /* M_IC_CMD_RESET -> i2c.cmd; */ +- 0xF8, 0x07, 0x2F, 0x00, /* 0x2F -> jumps.ad; */ +- +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 0) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 1) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 2) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- WR16((B_HI_IF_RAM_TRP_BPT0__AX + ((2 * 3) + 1)), +- (u16) (HI_RST_FUNC_ADDR & 0x3FF)), +- +- /* Force quick and dirty reset */ +- WR16(B_HI_CT_REG_COMM_STATE__A, 0), +- END_OF_TABLE +-}; +- +-u8 DRXD_ResetCEFR[] = { +- WRBLOCK(CE_REG_FR_TREAL00__A, 57), +- 0x52, 0x00, /* CE_REG_FR_TREAL00__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG00__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL01__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG01__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL02__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG02__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL03__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG03__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL04__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG04__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL05__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG05__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL06__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG06__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL07__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG07__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL08__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG08__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL09__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG09__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL10__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG10__A */ +- 0x52, 0x00, /* CE_REG_FR_TREAL11__A */ +- 0x00, 0x00, /* CE_REG_FR_TIMAG11__A */ +- +- 0x52, 0x00, /* CE_REG_FR_MID_TAP__A */ +- +- 0x0B, 0x00, /* CE_REG_FR_SQS_G00__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G01__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G02__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G03__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G04__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G05__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G06__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G07__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G08__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G09__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G10__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G11__A */ +- 0x0B, 0x00, /* CE_REG_FR_SQS_G12__A */ +- +- 0xFF, 0x01, /* CE_REG_FR_RIO_G00__A */ +- 0x90, 0x01, /* CE_REG_FR_RIO_G01__A */ +- 0x0B, 0x01, /* CE_REG_FR_RIO_G02__A */ +- 0xC8, 0x00, /* CE_REG_FR_RIO_G03__A */ +- 0xA0, 0x00, /* CE_REG_FR_RIO_G04__A */ +- 0x85, 0x00, /* CE_REG_FR_RIO_G05__A */ +- 0x72, 0x00, /* CE_REG_FR_RIO_G06__A */ +- 0x64, 0x00, /* CE_REG_FR_RIO_G07__A */ +- 0x59, 0x00, /* CE_REG_FR_RIO_G08__A */ +- 0x50, 0x00, /* CE_REG_FR_RIO_G09__A */ +- 0x49, 0x00, /* CE_REG_FR_RIO_G10__A */ +- +- 0x10, 0x00, /* CE_REG_FR_MODE__A */ +- 0x78, 0x00, /* CE_REG_FR_SQS_TRH__A */ +- 0x00, 0x00, /* CE_REG_FR_RIO_GAIN__A */ +- 0x00, 0x02, /* CE_REG_FR_BYPASS__A */ +- 0x0D, 0x00, /* CE_REG_FR_PM_SET__A */ +- 0x07, 0x00, /* CE_REG_FR_ERR_SH__A */ +- 0x04, 0x00, /* CE_REG_FR_MAN_SH__A */ +- 0x06, 0x00, /* CE_REG_FR_TAP_SH__A */ +- +- END_OF_TABLE +-}; +- +-u8 DRXD_InitFEA2_1[] = { +- WRBLOCK(FE_AD_REG_PD__A, 3), +- 0x00, 0x00, /* FE_AD_REG_PD__A */ +- 0x01, 0x00, /* FE_AD_REG_INVEXT__A */ +- 0x00, 0x00, /* FE_AD_REG_CLKNEG__A */ +- +- WRBLOCK(FE_AG_REG_DCE_AUR_CNT__A, 2), +- 0x10, 0x00, /* FE_AG_REG_DCE_AUR_CNT__A */ +- 0x10, 0x00, /* FE_AG_REG_DCE_RUR_CNT__A */ +- +- WRBLOCK(FE_AG_REG_ACE_AUR_CNT__A, 2), +- 0x0E, 0x00, /* FE_AG_REG_ACE_AUR_CNT__A */ +- 0x00, 0x00, /* FE_AG_REG_ACE_RUR_CNT__A */ +- +- WRBLOCK(FE_AG_REG_EGC_FLA_RGN__A, 5), +- 0x04, 0x00, /* FE_AG_REG_EGC_FLA_RGN__A */ +- 0x1F, 0x00, /* FE_AG_REG_EGC_SLO_RGN__A */ +- 0x00, 0x00, /* FE_AG_REG_EGC_JMP_PSN__A */ +- 0x00, 0x00, /* FE_AG_REG_EGC_FLA_INC__A */ +- 0x00, 0x00, /* FE_AG_REG_EGC_FLA_DEC__A */ +- +- WRBLOCK(FE_AG_REG_GC1_AGC_MAX__A, 2), +- 0xFF, 0x01, /* FE_AG_REG_GC1_AGC_MAX__A */ +- 0x00, 0xFE, /* FE_AG_REG_GC1_AGC_MIN__A */ +- +- WRBLOCK(FE_AG_REG_IND_WIN__A, 29), +- 0x00, 0x00, /* FE_AG_REG_IND_WIN__A */ +- 0x05, 0x00, /* FE_AG_REG_IND_THD_LOL__A */ +- 0x0F, 0x00, /* FE_AG_REG_IND_THD_HIL__A */ +- 0x00, 0x00, /* FE_AG_REG_IND_DEL__A don't care */ +- 0x1E, 0x00, /* FE_AG_REG_IND_PD1_WRI__A */ +- 0x0C, 0x00, /* FE_AG_REG_PDA_AUR_CNT__A */ +- 0x00, 0x00, /* FE_AG_REG_PDA_RUR_CNT__A */ +- 0x00, 0x00, /* FE_AG_REG_PDA_AVE_DAT__A don't care */ +- 0x00, 0x00, /* FE_AG_REG_PDC_RUR_CNT__A */ +- 0x01, 0x00, /* FE_AG_REG_PDC_SET_LVL__A */ +- 0x02, 0x00, /* FE_AG_REG_PDC_FLA_RGN__A */ +- 0x00, 0x00, /* FE_AG_REG_PDC_JMP_PSN__A don't care */ +- 0xFF, 0xFF, /* FE_AG_REG_PDC_FLA_STP__A */ +- 0xFF, 0xFF, /* FE_AG_REG_PDC_SLO_STP__A */ +- 0x00, 0x1F, /* FE_AG_REG_PDC_PD2_WRI__A don't care */ +- 0x00, 0x00, /* FE_AG_REG_PDC_MAP_DAT__A don't care */ +- 0x02, 0x00, /* FE_AG_REG_PDC_MAX__A */ +- 0x0C, 0x00, /* FE_AG_REG_TGA_AUR_CNT__A */ +- 0x00, 0x00, /* FE_AG_REG_TGA_RUR_CNT__A */ +- 0x00, 0x00, /* FE_AG_REG_TGA_AVE_DAT__A don't care */ +- 0x00, 0x00, /* FE_AG_REG_TGC_RUR_CNT__A */ +- 0x22, 0x00, /* FE_AG_REG_TGC_SET_LVL__A */ +- 0x15, 0x00, /* FE_AG_REG_TGC_FLA_RGN__A */ +- 0x00, 0x00, /* FE_AG_REG_TGC_JMP_PSN__A don't care */ +- 0x01, 0x00, /* FE_AG_REG_TGC_FLA_STP__A */ +- 0x0A, 0x00, /* FE_AG_REG_TGC_SLO_STP__A */ +- 0x00, 0x00, /* FE_AG_REG_TGC_MAP_DAT__A don't care */ +- 0x10, 0x00, /* FE_AG_REG_FGA_AUR_CNT__A */ +- 0x10, 0x00, /* FE_AG_REG_FGA_RUR_CNT__A */ +- +- WRBLOCK(FE_AG_REG_BGC_FGC_WRI__A, 2), +- 0x00, 0x00, /* FE_AG_REG_BGC_FGC_WRI__A */ +- 0x00, 0x00, /* FE_AG_REG_BGC_CGC_WRI__A */ +- +- WRBLOCK(FE_FD_REG_SCL__A, 3), +- 0x05, 0x00, /* FE_FD_REG_SCL__A */ +- 0x03, 0x00, /* FE_FD_REG_MAX_LEV__A */ +- 0x05, 0x00, /* FE_FD_REG_NR__A */ +- +- WRBLOCK(FE_CF_REG_SCL__A, 5), +- 0x16, 0x00, /* FE_CF_REG_SCL__A */ +- 0x04, 0x00, /* FE_CF_REG_MAX_LEV__A */ +- 0x06, 0x00, /* FE_CF_REG_NR__A */ +- 0x00, 0x00, /* FE_CF_REG_IMP_VAL__A */ +- 0x01, 0x00, /* FE_CF_REG_MEAS_VAL__A */ +- +- WRBLOCK(FE_CU_REG_FRM_CNT_RST__A, 2), +- 0x00, 0x08, /* FE_CU_REG_FRM_CNT_RST__A */ +- 0x00, 0x00, /* FE_CU_REG_FRM_CNT_STR__A */ +- +- END_OF_TABLE +-}; +- +- /* with PGA */ +-/* WR16COND( DRXD_WITH_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0004), */ +- /* without PGA */ +-/* WR16COND( DRXD_WITHOUT_PGA, FE_AG_REG_AG_PGA_MODE__A , 0x0001), */ +-/* WR16(FE_AG_REG_AG_AGC_SIO__A, (extAttr -> FeAgRegAgAgcSio), 0x0000 );*/ +-/* WR16(FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ +- +-u8 DRXD_InitFEA2_2[] = { +- WR16(FE_AG_REG_CDR_RUR_CNT__A, 0x0010), +- WR16(FE_AG_REG_FGM_WRI__A, 48), +- /* Activate measurement, activate scale */ +- WR16(FE_FD_REG_MEAS_VAL__A, 0x0001), +- +- WR16(FE_CU_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_CF_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_IF_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_FD_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_FS_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_AD_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_AG_REG_COMM_EXEC__A, 0x0001), +- WR16(FE_AG_REG_AG_MODE_LOP__A, 0x895E), +- +- END_OF_TABLE +-}; +- +-u8 DRXD_InitFEB1_1[] = { +- WR16(B_FE_AD_REG_PD__A, 0x0000), +- WR16(B_FE_AD_REG_CLKNEG__A, 0x0000), +- WR16(B_FE_AG_REG_BGC_FGC_WRI__A, 0x0000), +- WR16(B_FE_AG_REG_BGC_CGC_WRI__A, 0x0000), +- WR16(B_FE_AG_REG_AG_MODE_LOP__A, 0x000a), +- WR16(B_FE_AG_REG_IND_PD1_WRI__A, 35), +- WR16(B_FE_AG_REG_IND_WIN__A, 0), +- WR16(B_FE_AG_REG_IND_THD_LOL__A, 8), +- WR16(B_FE_AG_REG_IND_THD_HIL__A, 8), +- WR16(B_FE_CF_REG_IMP_VAL__A, 1), +- WR16(B_FE_AG_REG_EGC_FLA_RGN__A, 7), +- END_OF_TABLE +-}; +- +- /* with PGA */ +-/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , 0x0000, 0x0000); */ +- /* without PGA */ +-/* WR16(B_FE_AG_REG_AG_PGA_MODE__A , +- B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000);*/ +- /* WR16(B_FE_AG_REG_AG_AGC_SIO__A,(extAttr -> FeAgRegAgAgcSio), 0x0000 );*//*added HS 23-05-2005 */ +-/* WR16(B_FE_AG_REG_AG_PWD__A ,(extAttr -> FeAgRegAgPwd), 0x0000 );*/ +- +-u8 DRXD_InitFEB1_2[] = { +- WR16(B_FE_COMM_EXEC__A, 0x0001), +- +- /* RF-AGC setup */ +- WR16(B_FE_AG_REG_PDA_AUR_CNT__A, 0x0C), +- WR16(B_FE_AG_REG_PDC_SET_LVL__A, 0x01), +- WR16(B_FE_AG_REG_PDC_FLA_RGN__A, 0x02), +- WR16(B_FE_AG_REG_PDC_FLA_STP__A, 0xFFFF), +- WR16(B_FE_AG_REG_PDC_SLO_STP__A, 0xFFFF), +- WR16(B_FE_AG_REG_PDC_MAX__A, 0x02), +- WR16(B_FE_AG_REG_TGA_AUR_CNT__A, 0x0C), +- WR16(B_FE_AG_REG_TGC_SET_LVL__A, 0x22), +- WR16(B_FE_AG_REG_TGC_FLA_RGN__A, 0x15), +- WR16(B_FE_AG_REG_TGC_FLA_STP__A, 0x01), +- WR16(B_FE_AG_REG_TGC_SLO_STP__A, 0x0A), +- +- WR16(B_FE_CU_REG_DIV_NFC_CLP__A, 0), +- WR16(B_FE_CU_REG_CTR_NFC_OCR__A, 25000), +- WR16(B_FE_CU_REG_CTR_NFC_ICR__A, 1), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitCPA2[] = { +- WRBLOCK(CP_REG_BR_SPL_OFFSET__A, 2), +- 0x07, 0x00, /* CP_REG_BR_SPL_OFFSET__A */ +- 0x0A, 0x00, /* CP_REG_BR_STR_DEL__A */ +- +- WRBLOCK(CP_REG_RT_ANG_INC0__A, 4), +- 0x00, 0x00, /* CP_REG_RT_ANG_INC0__A */ +- 0x00, 0x00, /* CP_REG_RT_ANG_INC1__A */ +- 0x03, 0x00, /* CP_REG_RT_DETECT_ENA__A */ +- 0x03, 0x00, /* CP_REG_RT_DETECT_TRH__A */ +- +- WRBLOCK(CP_REG_AC_NEXP_OFFS__A, 5), +- 0x32, 0x00, /* CP_REG_AC_NEXP_OFFS__A */ +- 0x62, 0x00, /* CP_REG_AC_AVER_POW__A */ +- 0x82, 0x00, /* CP_REG_AC_MAX_POW__A */ +- 0x26, 0x00, /* CP_REG_AC_WEIGHT_MAN__A */ +- 0x0F, 0x00, /* CP_REG_AC_WEIGHT_EXP__A */ +- +- WRBLOCK(CP_REG_AC_AMP_MODE__A, 2), +- 0x02, 0x00, /* CP_REG_AC_AMP_MODE__A */ +- 0x01, 0x00, /* CP_REG_AC_AMP_FIX__A */ +- +- WR16(CP_REG_INTERVAL__A, 0x0005), +- WR16(CP_REG_RT_EXP_MARG__A, 0x0004), +- WR16(CP_REG_AC_ANG_MODE__A, 0x0003), +- +- WR16(CP_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitCPB1[] = { +- WR16(B_CP_REG_BR_SPL_OFFSET__A, 0x0008), +- WR16(B_CP_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitCEA2[] = { +- WRBLOCK(CE_REG_AVG_POW__A, 4), +- 0x62, 0x00, /* CE_REG_AVG_POW__A */ +- 0x78, 0x00, /* CE_REG_MAX_POW__A */ +- 0x62, 0x00, /* CE_REG_ATT__A */ +- 0x17, 0x00, /* CE_REG_NRED__A */ +- +- WRBLOCK(CE_REG_NE_ERR_SELECT__A, 2), +- 0x07, 0x00, /* CE_REG_NE_ERR_SELECT__A */ +- 0xEB, 0xFF, /* CE_REG_NE_TD_CAL__A */ +- +- WRBLOCK(CE_REG_NE_MIXAVG__A, 2), +- 0x06, 0x00, /* CE_REG_NE_MIXAVG__A */ +- 0x00, 0x00, /* CE_REG_NE_NUPD_OFS__A */ +- +- WRBLOCK(CE_REG_PE_NEXP_OFFS__A, 2), +- 0x00, 0x00, /* CE_REG_PE_NEXP_OFFS__A */ +- 0x00, 0x00, /* CE_REG_PE_TIMESHIFT__A */ +- +- WRBLOCK(CE_REG_TP_A0_TAP_NEW__A, 3), +- 0x00, 0x01, /* CE_REG_TP_A0_TAP_NEW__A */ +- 0x01, 0x00, /* CE_REG_TP_A0_TAP_NEW_VALID__A */ +- 0x0E, 0x00, /* CE_REG_TP_A0_MU_LMS_STEP__A */ +- +- WRBLOCK(CE_REG_TP_A1_TAP_NEW__A, 3), +- 0x00, 0x00, /* CE_REG_TP_A1_TAP_NEW__A */ +- 0x01, 0x00, /* CE_REG_TP_A1_TAP_NEW_VALID__A */ +- 0x0A, 0x00, /* CE_REG_TP_A1_MU_LMS_STEP__A */ +- +- WRBLOCK(CE_REG_FI_SHT_INCR__A, 2), +- 0x12, 0x00, /* CE_REG_FI_SHT_INCR__A */ +- 0x0C, 0x00, /* CE_REG_FI_EXP_NORM__A */ +- +- WRBLOCK(CE_REG_IR_INPUTSEL__A, 3), +- 0x00, 0x00, /* CE_REG_IR_INPUTSEL__A */ +- 0x00, 0x00, /* CE_REG_IR_STARTPOS__A */ +- 0xFF, 0x00, /* CE_REG_IR_NEXP_THRES__A */ +- +- WR16(CE_REG_TI_NEXP_OFFS__A, 0x0000), +- +- END_OF_TABLE +-}; +- +-u8 DRXD_InitCEB1[] = { +- WR16(B_CE_REG_TI_PHN_ENABLE__A, 0x0001), +- WR16(B_CE_REG_FR_PM_SET__A, 0x000D), +- +- END_OF_TABLE +-}; +- +-u8 DRXD_InitEQA2[] = { +- WRBLOCK(EQ_REG_OT_QNT_THRES0__A, 4), +- 0x1E, 0x00, /* EQ_REG_OT_QNT_THRES0__A */ +- 0x1F, 0x00, /* EQ_REG_OT_QNT_THRES1__A */ +- 0x06, 0x00, /* EQ_REG_OT_CSI_STEP__A */ +- 0x02, 0x00, /* EQ_REG_OT_CSI_OFFSET__A */ +- +- WR16(EQ_REG_TD_REQ_SMB_CNT__A, 0x0200), +- WR16(EQ_REG_IS_CLIP_EXP__A, 0x001F), +- WR16(EQ_REG_SN_OFFSET__A, (u16) (-7)), +- WR16(EQ_REG_RC_SEL_CAR__A, 0x0002), +- WR16(EQ_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitEQB1[] = { +- WR16(B_EQ_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_ResetECRAM[] = { +- /* Reset packet sync bytes in EC_VD ram */ +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), +- +- /* Reset packet sync bytes in EC_RS ram */ +- WR16(EC_RS_EC_RAM__A, 0x0000), +- WR16(EC_RS_EC_RAM__A + 204, 0x0000), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitECA2[] = { +- WRBLOCK(EC_SB_REG_CSI_HI__A, 6), +- 0x1F, 0x00, /* EC_SB_REG_CSI_HI__A */ +- 0x1E, 0x00, /* EC_SB_REG_CSI_LO__A */ +- 0x01, 0x00, /* EC_SB_REG_SMB_TGL__A */ +- 0x7F, 0x00, /* EC_SB_REG_SNR_HI__A */ +- 0x7F, 0x00, /* EC_SB_REG_SNR_MID__A */ +- 0x7F, 0x00, /* EC_SB_REG_SNR_LO__A */ +- +- WRBLOCK(EC_RS_REG_REQ_PCK_CNT__A, 2), +- 0x00, 0x10, /* EC_RS_REG_REQ_PCK_CNT__A */ +- DATA16(EC_RS_REG_VAL_PCK), /* EC_RS_REG_VAL__A */ +- +- WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), +- 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ +- 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ +- 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ +- 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ +- 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ +- +- WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), +- 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ +- 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ +- +- WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), +- 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ +- 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ +- 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ +- 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ +- 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ +- 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ +- 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ +- +- WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), +- 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ +- 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ +- +- WR16(EC_SB_REG_CSI_OFS__A, 0x0001), +- WR16(EC_VD_REG_FORCE__A, 0x0002), +- WR16(EC_VD_REG_REQ_SMB_CNT__A, 0x0001), +- WR16(EC_VD_REG_RLK_ENA__A, 0x0001), +- WR16(EC_OD_REG_SYNC__A, 0x0664), +- WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), +- WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), +- /* Output zero on monitorbus pads, power saving */ +- WR16(EC_OC_REG_OCR_MON_UOS__A, +- (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | +- EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | +- EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), +- WR16(EC_OC_REG_OCR_MON_WRI__A, +- EC_OC_REG_OCR_MON_WRI_INIT), +- +-/* CHK_ERROR(ResetECRAM(demod)); */ +- /* Reset packet sync bytes in EC_VD ram */ +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), +- +- /* Reset packet sync bytes in EC_RS ram */ +- WR16(EC_RS_EC_RAM__A, 0x0000), +- WR16(EC_RS_EC_RAM__A + 204, 0x0000), +- +- WR16(EC_SB_REG_COMM_EXEC__A, 0x0001), +- WR16(EC_VD_REG_COMM_EXEC__A, 0x0001), +- WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), +- WR16(EC_RS_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitECB1[] = { +- WR16(B_EC_SB_REG_CSI_OFS0__A, 0x0001), +- WR16(B_EC_SB_REG_CSI_OFS1__A, 0x0001), +- WR16(B_EC_SB_REG_CSI_OFS2__A, 0x0001), +- WR16(B_EC_SB_REG_CSI_LO__A, 0x000c), +- WR16(B_EC_SB_REG_CSI_HI__A, 0x0018), +- WR16(B_EC_SB_REG_SNR_HI__A, 0x007f), +- WR16(B_EC_SB_REG_SNR_MID__A, 0x007f), +- WR16(B_EC_SB_REG_SNR_LO__A, 0x007f), +- +- WR16(B_EC_OC_REG_DTO_CLKMODE__A, 0x0002), +- WR16(B_EC_OC_REG_DTO_PER__A, 0x0006), +- WR16(B_EC_OC_REG_DTO_BUR__A, 0x0001), +- WR16(B_EC_OC_REG_RCR_CLKMODE__A, 0x0000), +- WR16(B_EC_OC_REG_RCN_GAI_LVL__A, 0x000D), +- WR16(B_EC_OC_REG_OC_MPG_SIO__A, 0x0000), +- +- /* Needed because shadow registers do not have correct default value */ +- WR16(B_EC_OC_REG_RCN_CST_LOP__A, 0x1000), +- WR16(B_EC_OC_REG_RCN_CST_HIP__A, 0x0000), +- WR16(B_EC_OC_REG_RCN_CRA_LOP__A, 0x0000), +- WR16(B_EC_OC_REG_RCN_CRA_HIP__A, 0x00C0), +- WR16(B_EC_OC_REG_RCN_CLP_LOP__A, 0x0000), +- WR16(B_EC_OC_REG_RCN_CLP_HIP__A, 0x00C0), +- WR16(B_EC_OC_REG_DTO_INC_LOP__A, 0x0000), +- WR16(B_EC_OC_REG_DTO_INC_HIP__A, 0x00C0), +- +- WR16(B_EC_OD_REG_SYNC__A, 0x0664), +- WR16(B_EC_RS_REG_REQ_PCK_CNT__A, 0x1000), +- +-/* CHK_ERROR(ResetECRAM(demod)); */ +- /* Reset packet sync bytes in EC_VD ram */ +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), +- +- /* Reset packet sync bytes in EC_RS ram */ +- WR16(EC_RS_EC_RAM__A, 0x0000), +- WR16(EC_RS_EC_RAM__A + 204, 0x0000), +- +- WR16(B_EC_SB_REG_COMM_EXEC__A, 0x0001), +- WR16(B_EC_VD_REG_COMM_EXEC__A, 0x0001), +- WR16(B_EC_OD_REG_COMM_EXEC__A, 0x0001), +- WR16(B_EC_RS_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_ResetECA2[] = { +- +- WR16(EC_OC_REG_COMM_EXEC__A, 0x0000), +- WR16(EC_OD_REG_COMM_EXEC__A, 0x0000), +- +- WRBLOCK(EC_OC_REG_TMD_TOP_MODE__A, 5), +- 0x03, 0x00, /* EC_OC_REG_TMD_TOP_MODE__A */ +- 0xF4, 0x01, /* EC_OC_REG_TMD_TOP_CNT__A */ +- 0xC0, 0x03, /* EC_OC_REG_TMD_HIL_MAR__A */ +- 0x40, 0x00, /* EC_OC_REG_TMD_LOL_MAR__A */ +- 0x03, 0x00, /* EC_OC_REG_TMD_CUR_CNT__A */ +- +- WRBLOCK(EC_OC_REG_AVR_ASH_CNT__A, 2), +- 0x06, 0x00, /* EC_OC_REG_AVR_ASH_CNT__A */ +- 0x02, 0x00, /* EC_OC_REG_AVR_BSH_CNT__A */ +- +- WRBLOCK(EC_OC_REG_RCN_MODE__A, 7), +- 0x07, 0x00, /* EC_OC_REG_RCN_MODE__A */ +- 0x00, 0x00, /* EC_OC_REG_RCN_CRA_LOP__A */ +- 0xc0, 0x00, /* EC_OC_REG_RCN_CRA_HIP__A */ +- 0x00, 0x10, /* EC_OC_REG_RCN_CST_LOP__A */ +- 0x00, 0x00, /* EC_OC_REG_RCN_CST_HIP__A */ +- 0xFF, 0x01, /* EC_OC_REG_RCN_SET_LVL__A */ +- 0x0D, 0x00, /* EC_OC_REG_RCN_GAI_LVL__A */ +- +- WRBLOCK(EC_OC_REG_RCN_CLP_LOP__A, 2), +- 0x00, 0x00, /* EC_OC_REG_RCN_CLP_LOP__A */ +- 0xC0, 0x00, /* EC_OC_REG_RCN_CLP_HIP__A */ +- +- WR16(EC_OD_REG_SYNC__A, 0x0664), +- WR16(EC_OC_REG_OC_MON_SIO__A, 0x0000), +- WR16(EC_OC_REG_SNC_ISC_LVL__A, 0x0D0C), +- /* Output zero on monitorbus pads, power saving */ +- WR16(EC_OC_REG_OCR_MON_UOS__A, +- (EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE | +- EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE | +- EC_OC_REG_OCR_MON_UOS_VAL_ENABLE | +- EC_OC_REG_OCR_MON_UOS_CLK_ENABLE)), +- WR16(EC_OC_REG_OCR_MON_WRI__A, +- EC_OC_REG_OCR_MON_WRI_INIT), +- +-/* CHK_ERROR(ResetECRAM(demod)); */ +- /* Reset packet sync bytes in EC_VD ram */ +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (0 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (1 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (2 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (3 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (4 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (5 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (6 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (7 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (8 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (9 * 17), 0x0000), +- WR16(EC_OD_DEINT_RAM__A + 0x3b7 + (10 * 17), 0x0000), +- +- /* Reset packet sync bytes in EC_RS ram */ +- WR16(EC_RS_EC_RAM__A, 0x0000), +- WR16(EC_RS_EC_RAM__A + 204, 0x0000), +- +- WR16(EC_OD_REG_COMM_EXEC__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_InitSC[] = { +- WR16(SC_COMM_EXEC__A, 0), +- WR16(SC_COMM_STATE__A, 0), +- +-#ifdef COMPILE_FOR_QT +- WR16(SC_RA_RAM_BE_OPT_DELAY__A, 0x100), +-#endif +- +- /* SC is not started, this is done in SetChannels() */ +- END_OF_TABLE +-}; +- +-/* Diversity settings */ +- +-u8 DRXD_InitDiversityFront[] = { +- /* Start demod ********* RF in , diversity out **************************** */ +- WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | +- B_SC_RA_RAM_CONFIG_FREQSCAN__M), +- +- WR16(B_SC_RA_RAM_LC_ABS_2K__A, 0x7), +- WR16(B_SC_RA_RAM_LC_ABS_8K__A, 0x7), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), +- WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), +- WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), +- WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), +- +- WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), +- WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), +- WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), +- WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), +- +- WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), +- WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), +- WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), +- WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), +- WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), +- +- WR16(B_CC_REG_DIVERSITY__A, 0x0001), +- WR16(B_EC_OC_REG_OC_MODE_HIP__A, 0x0010), +- WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | +- B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), +- +- /* 0x2a ), *//* CE to PASS mux */ +- +- END_OF_TABLE +-}; +- +-u8 DRXD_InitDiversityEnd[] = { +- /* End demod *********** combining RF in and diversity in, MPEG TS out **** */ +- /* disable near/far; switch on timing slave mode */ +- WR16(B_SC_RA_RAM_CONFIG__A, B_SC_RA_RAM_CONFIG_FR_ENABLE__M | +- B_SC_RA_RAM_CONFIG_FREQSCAN__M | +- B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M | +- B_SC_RA_RAM_CONFIG_SLAVE__M | +- B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M +-/* MV from CtrlDiversity */ +- ), +-#ifdef DRXDDIV_SRMM_SLAVING +- WR16(SC_RA_RAM_LC_ABS_2K__A, 0x3c7), +- WR16(SC_RA_RAM_LC_ABS_8K__A, 0x3c7), +-#else +- WR16(SC_RA_RAM_LC_ABS_2K__A, 0x7), +- WR16(SC_RA_RAM_LC_ABS_8K__A, 0x7), +-#endif +- +- WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, IRLEN_COARSE_8K), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, 1 << (11 - IRLEN_COARSE_8K)), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, 1 << (17 - IRLEN_COARSE_8K)), +- WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, IRLEN_FINE_8K), +- WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, 1 << (11 - IRLEN_FINE_8K)), +- WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, 1 << (17 - IRLEN_FINE_8K)), +- +- WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, IRLEN_COARSE_2K), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, 1 << (11 - IRLEN_COARSE_2K)), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, 1 << (17 - IRLEN_COARSE_2K)), +- WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, IRLEN_FINE_2K), +- WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, 1 << (11 - IRLEN_FINE_2K)), +- WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, 1 << (17 - IRLEN_FINE_2K)), +- +- WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, 7), +- WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, 4), +- WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, 7), +- WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, 4), +- WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, 500), +- +- WR16(B_CC_REG_DIVERSITY__A, 0x0001), +- END_OF_TABLE +-}; +- +-u8 DRXD_DisableDiversity[] = { +- WR16(B_SC_RA_RAM_LC_ABS_2K__A, B_SC_RA_RAM_LC_ABS_2K__PRE), +- WR16(B_SC_RA_RAM_LC_ABS_8K__A, B_SC_RA_RAM_LC_ABS_8K__PRE), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A, +- B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A, +- B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE), +- WR16(B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A, +- B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_8K_LENGTH__A, +- B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_8K_FREQINC__A, +- B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_8K_KAISINC__A, +- B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE), +- +- WR16(B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A, +- B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A, +- B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE), +- WR16(B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A, +- B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_2K_LENGTH__A, +- B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_2K_FREQINC__A, +- B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE), +- WR16(B_SC_RA_RAM_IR_FINE_2K_KAISINC__A, +- B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE), +- +- WR16(B_LC_RA_RAM_FILTER_CRMM_A__A, B_LC_RA_RAM_FILTER_CRMM_A__PRE), +- WR16(B_LC_RA_RAM_FILTER_CRMM_B__A, B_LC_RA_RAM_FILTER_CRMM_B__PRE), +- WR16(B_LC_RA_RAM_FILTER_SRMM_A__A, B_LC_RA_RAM_FILTER_SRMM_A__PRE), +- WR16(B_LC_RA_RAM_FILTER_SRMM_B__A, B_LC_RA_RAM_FILTER_SRMM_B__PRE), +- WR16(B_LC_RA_RAM_FILTER_SYM_SET__A, B_LC_RA_RAM_FILTER_SYM_SET__PRE), +- +- WR16(B_CC_REG_DIVERSITY__A, 0x0000), +- WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_INIT), /* combining disabled */ +- +- END_OF_TABLE +-}; +- +-u8 DRXD_StartDiversityFront[] = { +- /* Start demod, RF in and diversity out, no combining */ +- WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), +- WR16(B_FE_AD_REG_FDB_IN__A, 0x0), +- WR16(B_FE_AD_REG_INVEXT__A, 0x0), +- WR16(B_EQ_REG_COMM_MB__A, 0x12), /* EQ to MB out */ +- WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_PASS_B_CE | /* CE to PASS mux */ +- B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE | B_EQ_REG_RC_SEL_CAR_MEAS_B_CE), +- +- WR16(SC_RA_RAM_ECHO_SHIFT_LIM__A, 2), +- +- END_OF_TABLE +-}; +- +-u8 DRXD_StartDiversityEnd[] = { +- /* End demod, combining RF in and diversity in, MPEG TS out */ +- WR16(B_FE_CF_REG_IMP_VAL__A, 0x0), /* disable impulse noise cruncher */ +- WR16(B_FE_AD_REG_INVEXT__A, 0x0), /* clock inversion (for sohard board) */ +- WR16(B_CP_REG_BR_STR_DEL__A, 10), /* apperently no mb delay matching is best */ +- +- WR16(B_EQ_REG_RC_SEL_CAR__A, B_EQ_REG_RC_SEL_CAR_DIV_ON | /* org = 0x81 combining enabled */ +- B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | +- B_EQ_REG_RC_SEL_CAR_PASS_A_CC | B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC), +- +- END_OF_TABLE +-}; +- +-u8 DRXD_DiversityDelay8MHZ[] = { +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1150 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1100 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 1000 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 800 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5420 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5200 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4800 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 4000 - 50), +- END_OF_TABLE +-}; +- +-u8 DRXD_DiversityDelay6MHZ[] = /* also used ok for 7 MHz */ +-{ +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A, 1100 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A, 1000 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A, 900 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A, 600 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A, 5300 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A, 5000 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A, 4500 - 50), +- WR16(B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A, 3500 - 50), +- END_OF_TABLE +-}; +diff --git a/drivers/media/dvb/frontends/drxd_firm.h b/drivers/media/dvb/frontends/drxd_firm.h +deleted file mode 100644 +index 41597e8..0000000 +--- a/drivers/media/dvb/frontends/drxd_firm.h ++++ /dev/null +@@ -1,115 +0,0 @@ +-/* +- * drxd_firm.h +- * +- * Copyright (C) 2006-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#ifndef _DRXD_FIRM_H_ +-#define _DRXD_FIRM_H_ +- +-#include +-#include "drxd_map_firm.h" +- +-#define VERSION_MAJOR 1 +-#define VERSION_MINOR 4 +-#define VERSION_PATCH 23 +- +-#define HI_TR_FUNC_ADDR HI_IF_RAM_USR_BEGIN__A +- +-#define DRXD_MAX_RETRIES (1000) +-#define HI_I2C_DELAY 84 +-#define HI_I2C_BRIDGE_DELAY 750 +- +-#define EQ_TD_TPS_PWR_UNKNOWN 0x00C0 /* Unknown configurations */ +-#define EQ_TD_TPS_PWR_QPSK 0x016a +-#define EQ_TD_TPS_PWR_QAM16_ALPHAN 0x0195 +-#define EQ_TD_TPS_PWR_QAM16_ALPHA1 0x0195 +-#define EQ_TD_TPS_PWR_QAM16_ALPHA2 0x011E +-#define EQ_TD_TPS_PWR_QAM16_ALPHA4 0x01CE +-#define EQ_TD_TPS_PWR_QAM64_ALPHAN 0x019F +-#define EQ_TD_TPS_PWR_QAM64_ALPHA1 0x019F +-#define EQ_TD_TPS_PWR_QAM64_ALPHA2 0x00F8 +-#define EQ_TD_TPS_PWR_QAM64_ALPHA4 0x014D +- +-#define DRXD_DEF_AG_PWD_CONSUMER 0x000E +-#define DRXD_DEF_AG_PWD_PRO 0x0000 +-#define DRXD_DEF_AG_AGC_SIO 0x0000 +- +-#define DRXD_FE_CTRL_MAX 1023 +- +-#define DRXD_OSCDEV_DO_SCAN (16) +- +-#define DRXD_OSCDEV_DONT_SCAN (0) +- +-#define DRXD_OSCDEV_STEP (275) +- +-#define DRXD_SCAN_TIMEOUT (650) +- +-#define DRXD_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) +-#define DRXD_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) +-#define DRXD_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) +- +-#define IRLEN_COARSE_8K (10) +-#define IRLEN_FINE_8K (10) +-#define IRLEN_COARSE_2K (7) +-#define IRLEN_FINE_2K (9) +-#define DIFF_INVALID (511) +-#define DIFF_TARGET (4) +-#define DIFF_MARGIN (1) +- +-extern u8 DRXD_InitAtomicRead[]; +-extern u8 DRXD_HiI2cPatch_1[]; +-extern u8 DRXD_HiI2cPatch_3[]; +- +-extern u8 DRXD_InitSC[]; +- +-extern u8 DRXD_ResetCEFR[]; +-extern u8 DRXD_InitFEA2_1[]; +-extern u8 DRXD_InitFEA2_2[]; +-extern u8 DRXD_InitCPA2[]; +-extern u8 DRXD_InitCEA2[]; +-extern u8 DRXD_InitEQA2[]; +-extern u8 DRXD_InitECA2[]; +-extern u8 DRXD_ResetECA2[]; +-extern u8 DRXD_ResetECRAM[]; +- +-extern u8 DRXD_A2_microcode[]; +-extern u32 DRXD_A2_microcode_length; +- +-extern u8 DRXD_InitFEB1_1[]; +-extern u8 DRXD_InitFEB1_2[]; +-extern u8 DRXD_InitCPB1[]; +-extern u8 DRXD_InitCEB1[]; +-extern u8 DRXD_InitEQB1[]; +-extern u8 DRXD_InitECB1[]; +- +-extern u8 DRXD_InitDiversityFront[]; +-extern u8 DRXD_InitDiversityEnd[]; +-extern u8 DRXD_DisableDiversity[]; +-extern u8 DRXD_StartDiversityFront[]; +-extern u8 DRXD_StartDiversityEnd[]; +- +-extern u8 DRXD_DiversityDelay8MHZ[]; +-extern u8 DRXD_DiversityDelay6MHZ[]; +- +-extern u8 DRXD_B1_microcode[]; +-extern u32 DRXD_B1_microcode_length; +- +-#endif +diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c +deleted file mode 100644 +index 7bf39cd..0000000 +--- a/drivers/media/dvb/frontends/drxd_hard.c ++++ /dev/null +@@ -1,2992 +0,0 @@ +-/* +- * drxd_hard.c: DVB-T Demodulator Micronas DRX3975D-A2,DRX397xD-B1 +- * +- * Copyright (C) 2003-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "drxd.h" +-#include "drxd_firm.h" +- +-#define DRX_FW_FILENAME_A2 "drxd-a2-1.1.fw" +-#define DRX_FW_FILENAME_B1 "drxd-b1-1.1.fw" +- +-#define CHUNK_SIZE 48 +- +-#define DRX_I2C_RMW 0x10 +-#define DRX_I2C_BROADCAST 0x20 +-#define DRX_I2C_CLEARCRC 0x80 +-#define DRX_I2C_SINGLE_MASTER 0xC0 +-#define DRX_I2C_MODEFLAGS 0xC0 +-#define DRX_I2C_FLAGS 0xF0 +- +-#ifndef SIZEOF_ARRAY +-#define SIZEOF_ARRAY(array) (sizeof((array))/sizeof((array)[0])) +-#endif +- +-#define DEFAULT_LOCK_TIMEOUT 1100 +- +-#define DRX_CHANNEL_AUTO 0 +-#define DRX_CHANNEL_HIGH 1 +-#define DRX_CHANNEL_LOW 2 +- +-#define DRX_LOCK_MPEG 1 +-#define DRX_LOCK_FEC 2 +-#define DRX_LOCK_DEMOD 4 +- +-/****************************************************************************/ +- +-enum CSCDState { +- CSCD_INIT = 0, +- CSCD_SET, +- CSCD_SAVED +-}; +- +-enum CDrxdState { +- DRXD_UNINITIALIZED = 0, +- DRXD_STOPPED, +- DRXD_STARTED +-}; +- +-enum AGC_CTRL_MODE { +- AGC_CTRL_AUTO = 0, +- AGC_CTRL_USER, +- AGC_CTRL_OFF +-}; +- +-enum OperationMode { +- OM_Default, +- OM_DVBT_Diversity_Front, +- OM_DVBT_Diversity_End +-}; +- +-struct SCfgAgc { +- enum AGC_CTRL_MODE ctrlMode; +- u16 outputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ +- u16 settleLevel; /* range [0, ... , 1023], 1/n of fullscale range */ +- u16 minOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ +- u16 maxOutputLevel; /* range [0, ... , 1023], 1/n of fullscale range */ +- u16 speed; /* range [0, ... , 1023], 1/n of fullscale range */ +- +- u16 R1; +- u16 R2; +- u16 R3; +-}; +- +-struct SNoiseCal { +- int cpOpt; +- u16 cpNexpOfs; +- u16 tdCal2k; +- u16 tdCal8k; +-}; +- +-enum app_env { +- APPENV_STATIC = 0, +- APPENV_PORTABLE = 1, +- APPENV_MOBILE = 2 +-}; +- +-enum EIFFilter { +- IFFILTER_SAW = 0, +- IFFILTER_DISCRETE = 1 +-}; +- +-struct drxd_state { +- struct dvb_frontend frontend; +- struct dvb_frontend_ops ops; +- struct dtv_frontend_properties props; +- +- const struct firmware *fw; +- struct device *dev; +- +- struct i2c_adapter *i2c; +- void *priv; +- struct drxd_config config; +- +- int i2c_access; +- int init_done; +- struct mutex mutex; +- +- u8 chip_adr; +- u16 hi_cfg_timing_div; +- u16 hi_cfg_bridge_delay; +- u16 hi_cfg_wakeup_key; +- u16 hi_cfg_ctrl; +- +- u16 intermediate_freq; +- u16 osc_clock_freq; +- +- enum CSCDState cscd_state; +- enum CDrxdState drxd_state; +- +- u16 sys_clock_freq; +- s16 osc_clock_deviation; +- u16 expected_sys_clock_freq; +- +- u16 insert_rs_byte; +- u16 enable_parallel; +- +- int operation_mode; +- +- struct SCfgAgc if_agc_cfg; +- struct SCfgAgc rf_agc_cfg; +- +- struct SNoiseCal noise_cal; +- +- u32 fe_fs_add_incr; +- u32 org_fe_fs_add_incr; +- u16 current_fe_if_incr; +- +- u16 m_FeAgRegAgPwd; +- u16 m_FeAgRegAgAgcSio; +- +- u16 m_EcOcRegOcModeLop; +- u16 m_EcOcRegSncSncLvl; +- u8 *m_InitAtomicRead; +- u8 *m_HiI2cPatch; +- +- u8 *m_ResetCEFR; +- u8 *m_InitFE_1; +- u8 *m_InitFE_2; +- u8 *m_InitCP; +- u8 *m_InitCE; +- u8 *m_InitEQ; +- u8 *m_InitSC; +- u8 *m_InitEC; +- u8 *m_ResetECRAM; +- u8 *m_InitDiversityFront; +- u8 *m_InitDiversityEnd; +- u8 *m_DisableDiversity; +- u8 *m_StartDiversityFront; +- u8 *m_StartDiversityEnd; +- +- u8 *m_DiversityDelay8MHZ; +- u8 *m_DiversityDelay6MHZ; +- +- u8 *microcode; +- u32 microcode_length; +- +- int type_A; +- int PGA; +- int diversity; +- int tuner_mirrors; +- +- enum app_env app_env_default; +- enum app_env app_env_diversity; +- +-}; +- +-/****************************************************************************/ +-/* I2C **********************************************************************/ +-/****************************************************************************/ +- +-static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 * data, int len) +-{ +- struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len }; +- +- if (i2c_transfer(adap, &msg, 1) != 1) +- return -1; +- return 0; +-} +- +-static int i2c_read(struct i2c_adapter *adap, +- u8 adr, u8 *msg, int len, u8 *answ, int alen) +-{ +- struct i2c_msg msgs[2] = { +- { +- .addr = adr, .flags = 0, +- .buf = msg, .len = len +- }, { +- .addr = adr, .flags = I2C_M_RD, +- .buf = answ, .len = alen +- } +- }; +- if (i2c_transfer(adap, msgs, 2) != 2) +- return -1; +- return 0; +-} +- +-static inline u32 MulDiv32(u32 a, u32 b, u32 c) +-{ +- u64 tmp64; +- +- tmp64 = (u64)a * (u64)b; +- do_div(tmp64, c); +- +- return (u32) tmp64; +-} +- +-static int Read16(struct drxd_state *state, u32 reg, u16 *data, u8 flags) +-{ +- u8 adr = state->config.demod_address; +- u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, +- flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff +- }; +- u8 mm2[2]; +- if (i2c_read(state->i2c, adr, mm1, 4, mm2, 2) < 0) +- return -1; +- if (data) +- *data = mm2[0] | (mm2[1] << 8); +- return mm2[0] | (mm2[1] << 8); +-} +- +-static int Read32(struct drxd_state *state, u32 reg, u32 *data, u8 flags) +-{ +- u8 adr = state->config.demod_address; +- u8 mm1[4] = { reg & 0xff, (reg >> 16) & 0xff, +- flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff +- }; +- u8 mm2[4]; +- +- if (i2c_read(state->i2c, adr, mm1, 4, mm2, 4) < 0) +- return -1; +- if (data) +- *data = +- mm2[0] | (mm2[1] << 8) | (mm2[2] << 16) | (mm2[3] << 24); +- return 0; +-} +- +-static int Write16(struct drxd_state *state, u32 reg, u16 data, u8 flags) +-{ +- u8 adr = state->config.demod_address; +- u8 mm[6] = { reg & 0xff, (reg >> 16) & 0xff, +- flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, +- data & 0xff, (data >> 8) & 0xff +- }; +- +- if (i2c_write(state->i2c, adr, mm, 6) < 0) +- return -1; +- return 0; +-} +- +-static int Write32(struct drxd_state *state, u32 reg, u32 data, u8 flags) +-{ +- u8 adr = state->config.demod_address; +- u8 mm[8] = { reg & 0xff, (reg >> 16) & 0xff, +- flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff, +- data & 0xff, (data >> 8) & 0xff, +- (data >> 16) & 0xff, (data >> 24) & 0xff +- }; +- +- if (i2c_write(state->i2c, adr, mm, 8) < 0) +- return -1; +- return 0; +-} +- +-static int write_chunk(struct drxd_state *state, +- u32 reg, u8 *data, u32 len, u8 flags) +-{ +- u8 adr = state->config.demod_address; +- u8 mm[CHUNK_SIZE + 4] = { reg & 0xff, (reg >> 16) & 0xff, +- flags | ((reg >> 24) & 0xff), (reg >> 8) & 0xff +- }; +- int i; +- +- for (i = 0; i < len; i++) +- mm[4 + i] = data[i]; +- if (i2c_write(state->i2c, adr, mm, 4 + len) < 0) { +- printk(KERN_ERR "error in write_chunk\n"); +- return -1; +- } +- return 0; +-} +- +-static int WriteBlock(struct drxd_state *state, +- u32 Address, u16 BlockSize, u8 *pBlock, u8 Flags) +-{ +- while (BlockSize > 0) { +- u16 Chunk = BlockSize > CHUNK_SIZE ? CHUNK_SIZE : BlockSize; +- +- if (write_chunk(state, Address, pBlock, Chunk, Flags) < 0) +- return -1; +- pBlock += Chunk; +- Address += (Chunk >> 1); +- BlockSize -= Chunk; +- } +- return 0; +-} +- +-static int WriteTable(struct drxd_state *state, u8 * pTable) +-{ +- int status = 0; +- +- if (pTable == NULL) +- return 0; +- +- while (!status) { +- u16 Length; +- u32 Address = pTable[0] | (pTable[1] << 8) | +- (pTable[2] << 16) | (pTable[3] << 24); +- +- if (Address == 0xFFFFFFFF) +- break; +- pTable += sizeof(u32); +- +- Length = pTable[0] | (pTable[1] << 8); +- pTable += sizeof(u16); +- if (!Length) +- break; +- status = WriteBlock(state, Address, Length * 2, pTable, 0); +- pTable += (Length * 2); +- } +- return status; +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static int ResetCEFR(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_ResetCEFR); +-} +- +-static int InitCP(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_InitCP); +-} +- +-static int InitCE(struct drxd_state *state) +-{ +- int status; +- enum app_env AppEnv = state->app_env_default; +- +- do { +- status = WriteTable(state, state->m_InitCE); +- if (status < 0) +- break; +- +- if (state->operation_mode == OM_DVBT_Diversity_Front || +- state->operation_mode == OM_DVBT_Diversity_End) { +- AppEnv = state->app_env_diversity; +- } +- if (AppEnv == APPENV_STATIC) { +- status = Write16(state, CE_REG_TAPSET__A, 0x0000, 0); +- if (status < 0) +- break; +- } else if (AppEnv == APPENV_PORTABLE) { +- status = Write16(state, CE_REG_TAPSET__A, 0x0001, 0); +- if (status < 0) +- break; +- } else if (AppEnv == APPENV_MOBILE && state->type_A) { +- status = Write16(state, CE_REG_TAPSET__A, 0x0002, 0); +- if (status < 0) +- break; +- } else if (AppEnv == APPENV_MOBILE && !state->type_A) { +- status = Write16(state, CE_REG_TAPSET__A, 0x0006, 0); +- if (status < 0) +- break; +- } +- +- /* start ce */ +- status = Write16(state, B_CE_REG_COMM_EXEC__A, 0x0001, 0); +- if (status < 0) +- break; +- } while (0); +- return status; +-} +- +-static int StopOC(struct drxd_state *state) +-{ +- int status = 0; +- u16 ocSyncLvl = 0; +- u16 ocModeLop = state->m_EcOcRegOcModeLop; +- u16 dtoIncLop = 0; +- u16 dtoIncHip = 0; +- +- do { +- /* Store output configuration */ +- status = Read16(state, EC_OC_REG_SNC_ISC_LVL__A, &ocSyncLvl, 0); +- if (status < 0) +- break; +- /* CHK_ERROR(Read16(EC_OC_REG_OC_MODE_LOP__A, &ocModeLop)); */ +- state->m_EcOcRegSncSncLvl = ocSyncLvl; +- /* m_EcOcRegOcModeLop = ocModeLop; */ +- +- /* Flush FIFO (byte-boundary) at fixed rate */ +- status = Read16(state, EC_OC_REG_RCN_MAP_LOP__A, &dtoIncLop, 0); +- if (status < 0) +- break; +- status = Read16(state, EC_OC_REG_RCN_MAP_HIP__A, &dtoIncHip, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_DTO_INC_LOP__A, dtoIncLop, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_DTO_INC_HIP__A, dtoIncHip, 0); +- if (status < 0) +- break; +- ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M); +- ocModeLop |= EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC; +- status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); +- if (status < 0) +- break; +- +- msleep(1); +- /* Output pins to '0' */ +- status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS__M, 0); +- if (status < 0) +- break; +- +- /* Force the OC out of sync */ +- ocSyncLvl &= ~(EC_OC_REG_SNC_ISC_LVL_OSC__M); +- status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, ocSyncLvl, 0); +- if (status < 0) +- break; +- ocModeLop &= ~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M); +- ocModeLop |= EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE; +- ocModeLop |= 0x2; /* Magically-out-of-sync */ +- status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, ocModeLop, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_COMM_INT_STA__A, 0x0, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); +- if (status < 0) +- break; +- } while (0); +- +- return status; +-} +- +-static int StartOC(struct drxd_state *state) +-{ +- int status = 0; +- +- do { +- /* Stop OC */ +- status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_HOLD, 0); +- if (status < 0) +- break; +- +- /* Restore output configuration */ +- status = Write16(state, EC_OC_REG_SNC_ISC_LVL__A, state->m_EcOcRegSncSncLvl, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, state->m_EcOcRegOcModeLop, 0); +- if (status < 0) +- break; +- +- /* Output pins active again */ +- status = Write16(state, EC_OC_REG_OCR_MPG_UOS__A, EC_OC_REG_OCR_MPG_UOS_INIT, 0); +- if (status < 0) +- break; +- +- /* Start OC */ +- status = Write16(state, EC_OC_REG_COMM_EXEC__A, EC_OC_REG_COMM_EXEC_CTL_ACTIVE, 0); +- if (status < 0) +- break; +- } while (0); +- return status; +-} +- +-static int InitEQ(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_InitEQ); +-} +- +-static int InitEC(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_InitEC); +-} +- +-static int InitSC(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_InitSC); +-} +- +-static int InitAtomicRead(struct drxd_state *state) +-{ +- return WriteTable(state, state->m_InitAtomicRead); +-} +- +-static int CorrectSysClockDeviation(struct drxd_state *state); +- +-static int DRX_GetLockStatus(struct drxd_state *state, u32 * pLockStatus) +-{ +- u16 ScRaRamLock = 0; +- const u16 mpeg_lock_mask = (SC_RA_RAM_LOCK_MPEG__M | +- SC_RA_RAM_LOCK_FEC__M | +- SC_RA_RAM_LOCK_DEMOD__M); +- const u16 fec_lock_mask = (SC_RA_RAM_LOCK_FEC__M | +- SC_RA_RAM_LOCK_DEMOD__M); +- const u16 demod_lock_mask = SC_RA_RAM_LOCK_DEMOD__M; +- +- int status; +- +- *pLockStatus = 0; +- +- status = Read16(state, SC_RA_RAM_LOCK__A, &ScRaRamLock, 0x0000); +- if (status < 0) { +- printk(KERN_ERR "Can't read SC_RA_RAM_LOCK__A status = %08x\n", status); +- return status; +- } +- +- if (state->drxd_state != DRXD_STARTED) +- return 0; +- +- if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask) { +- *pLockStatus |= DRX_LOCK_MPEG; +- CorrectSysClockDeviation(state); +- } +- +- if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask) +- *pLockStatus |= DRX_LOCK_FEC; +- +- if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask) +- *pLockStatus |= DRX_LOCK_DEMOD; +- return 0; +-} +- +-/****************************************************************************/ +- +-static int SetCfgIfAgc(struct drxd_state *state, struct SCfgAgc *cfg) +-{ +- int status; +- +- if (cfg->outputLevel > DRXD_FE_CTRL_MAX) +- return -1; +- +- if (cfg->ctrlMode == AGC_CTRL_USER) { +- do { +- u16 FeAgRegPm1AgcWri; +- u16 FeAgRegAgModeLop; +- +- status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); +- if (status < 0) +- break; +- FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); +- FeAgRegAgModeLop |= FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC; +- status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); +- if (status < 0) +- break; +- +- FeAgRegPm1AgcWri = (u16) (cfg->outputLevel & +- FE_AG_REG_PM1_AGC_WRI__M); +- status = Write16(state, FE_AG_REG_PM1_AGC_WRI__A, FeAgRegPm1AgcWri, 0); +- if (status < 0) +- break; +- } while (0); +- } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { +- if (((cfg->maxOutputLevel) < (cfg->minOutputLevel)) || +- ((cfg->maxOutputLevel) > DRXD_FE_CTRL_MAX) || +- ((cfg->speed) > DRXD_FE_CTRL_MAX) || +- ((cfg->settleLevel) > DRXD_FE_CTRL_MAX) +- ) +- return -1; +- do { +- u16 FeAgRegAgModeLop; +- u16 FeAgRegEgcSetLvl; +- u16 slope, offset; +- +- /* == Mode == */ +- +- status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &FeAgRegAgModeLop, 0); +- if (status < 0) +- break; +- FeAgRegAgModeLop &= (~FE_AG_REG_AG_MODE_LOP_MODE_4__M); +- FeAgRegAgModeLop |= +- FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC; +- status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, FeAgRegAgModeLop, 0); +- if (status < 0) +- break; +- +- /* == Settle level == */ +- +- FeAgRegEgcSetLvl = (u16) ((cfg->settleLevel >> 1) & +- FE_AG_REG_EGC_SET_LVL__M); +- status = Write16(state, FE_AG_REG_EGC_SET_LVL__A, FeAgRegEgcSetLvl, 0); +- if (status < 0) +- break; +- +- /* == Min/Max == */ +- +- slope = (u16) ((cfg->maxOutputLevel - +- cfg->minOutputLevel) / 2); +- offset = (u16) ((cfg->maxOutputLevel + +- cfg->minOutputLevel) / 2 - 511); +- +- status = Write16(state, FE_AG_REG_GC1_AGC_RIC__A, slope, 0); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_GC1_AGC_OFF__A, offset, 0); +- if (status < 0) +- break; +- +- /* == Speed == */ +- { +- const u16 maxRur = 8; +- const u16 slowIncrDecLUT[] = { 3, 4, 4, 5, 6 }; +- const u16 fastIncrDecLUT[] = { 14, 15, 15, 16, +- 17, 18, 18, 19, +- 20, 21, 22, 23, +- 24, 26, 27, 28, +- 29, 31 +- }; +- +- u16 fineSteps = (DRXD_FE_CTRL_MAX + 1) / +- (maxRur + 1); +- u16 fineSpeed = (u16) (cfg->speed - +- ((cfg->speed / +- fineSteps) * +- fineSteps)); +- u16 invRurCount = (u16) (cfg->speed / +- fineSteps); +- u16 rurCount; +- if (invRurCount > maxRur) { +- rurCount = 0; +- fineSpeed += fineSteps; +- } else { +- rurCount = maxRur - invRurCount; +- } +- +- /* +- fastInc = default * +- (2^(fineSpeed/fineSteps)) +- => range[default...2*default> +- slowInc = default * +- (2^(fineSpeed/fineSteps)) +- */ +- { +- u16 fastIncrDec = +- fastIncrDecLUT[fineSpeed / +- ((fineSteps / +- (14 + 1)) + 1)]; +- u16 slowIncrDec = +- slowIncrDecLUT[fineSpeed / +- (fineSteps / +- (3 + 1))]; +- +- status = Write16(state, FE_AG_REG_EGC_RUR_CNT__A, rurCount, 0); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_EGC_FAS_INC__A, fastIncrDec, 0); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_EGC_FAS_DEC__A, fastIncrDec, 0); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_EGC_SLO_INC__A, slowIncrDec, 0); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_EGC_SLO_DEC__A, slowIncrDec, 0); +- if (status < 0) +- break; +- } +- } +- } while (0); +- +- } else { +- /* No OFF mode for IF control */ +- return -1; +- } +- return status; +-} +- +-static int SetCfgRfAgc(struct drxd_state *state, struct SCfgAgc *cfg) +-{ +- int status = 0; +- +- if (cfg->outputLevel > DRXD_FE_CTRL_MAX) +- return -1; +- +- if (cfg->ctrlMode == AGC_CTRL_USER) { +- do { +- u16 AgModeLop = 0; +- u16 level = (cfg->outputLevel); +- +- if (level == DRXD_FE_CTRL_MAX) +- level++; +- +- status = Write16(state, FE_AG_REG_PM2_AGC_WRI__A, level, 0x0000); +- if (status < 0) +- break; +- +- /*==== Mode ====*/ +- +- /* Powerdown PD2, WRI source */ +- state->m_FeAgRegAgPwd &= ~(FE_AG_REG_AG_PWD_PWD_PD2__M); +- state->m_FeAgRegAgPwd |= +- FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; +- status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); +- if (status < 0) +- break; +- +- status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); +- if (status < 0) +- break; +- AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | +- FE_AG_REG_AG_MODE_LOP_MODE_E__M)); +- AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | +- FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); +- status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); +- if (status < 0) +- break; +- +- /* enable AGC2 pin */ +- { +- u16 FeAgRegAgAgcSio = 0; +- status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- FeAgRegAgAgcSio &= +- ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); +- FeAgRegAgAgcSio |= +- FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; +- status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- } +- +- } while (0); +- } else if (cfg->ctrlMode == AGC_CTRL_AUTO) { +- u16 AgModeLop = 0; +- +- do { +- u16 level; +- /* Automatic control */ +- /* Powerup PD2, AGC2 as output, TGC source */ +- (state->m_FeAgRegAgPwd) &= +- ~(FE_AG_REG_AG_PWD_PWD_PD2__M); +- (state->m_FeAgRegAgPwd) |= +- FE_AG_REG_AG_PWD_PWD_PD2_DISABLE; +- status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); +- if (status < 0) +- break; +- +- status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); +- if (status < 0) +- break; +- AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | +- FE_AG_REG_AG_MODE_LOP_MODE_E__M)); +- AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | +- FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC); +- status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); +- if (status < 0) +- break; +- /* Settle level */ +- level = (((cfg->settleLevel) >> 4) & +- FE_AG_REG_TGC_SET_LVL__M); +- status = Write16(state, FE_AG_REG_TGC_SET_LVL__A, level, 0x0000); +- if (status < 0) +- break; +- +- /* Min/max: don't care */ +- +- /* Speed: TODO */ +- +- /* enable AGC2 pin */ +- { +- u16 FeAgRegAgAgcSio = 0; +- status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- FeAgRegAgAgcSio &= +- ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); +- FeAgRegAgAgcSio |= +- FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT; +- status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- } +- +- } while (0); +- } else { +- u16 AgModeLop = 0; +- +- do { +- /* No RF AGC control */ +- /* Powerdown PD2, AGC2 as output, WRI source */ +- (state->m_FeAgRegAgPwd) &= +- ~(FE_AG_REG_AG_PWD_PWD_PD2__M); +- (state->m_FeAgRegAgPwd) |= +- FE_AG_REG_AG_PWD_PWD_PD2_ENABLE; +- status = Write16(state, FE_AG_REG_AG_PWD__A, (state->m_FeAgRegAgPwd), 0x0000); +- if (status < 0) +- break; +- +- status = Read16(state, FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); +- if (status < 0) +- break; +- AgModeLop &= (~(FE_AG_REG_AG_MODE_LOP_MODE_5__M | +- FE_AG_REG_AG_MODE_LOP_MODE_E__M)); +- AgModeLop |= (FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC | +- FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC); +- status = Write16(state, FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); +- if (status < 0) +- break; +- +- /* set FeAgRegAgAgcSio AGC2 (RF) as input */ +- { +- u16 FeAgRegAgAgcSio = 0; +- status = Read16(state, FE_AG_REG_AG_AGC_SIO__A, &FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- FeAgRegAgAgcSio &= +- ~(FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M); +- FeAgRegAgAgcSio |= +- FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT; +- status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- } +- } while (0); +- } +- return status; +-} +- +-static int ReadIFAgc(struct drxd_state *state, u32 * pValue) +-{ +- int status = 0; +- +- *pValue = 0; +- if (state->if_agc_cfg.ctrlMode != AGC_CTRL_OFF) { +- u16 Value; +- status = Read16(state, FE_AG_REG_GC1_AGC_DAT__A, &Value, 0); +- Value &= FE_AG_REG_GC1_AGC_DAT__M; +- if (status >= 0) { +- /* 3.3V +- | +- R1 +- | +- Vin - R3 - * -- Vout +- | +- R2 +- | +- GND +- */ +- u32 R1 = state->if_agc_cfg.R1; +- u32 R2 = state->if_agc_cfg.R2; +- u32 R3 = state->if_agc_cfg.R3; +- +- u32 Vmax, Rpar, Vmin, Vout; +- +- if (R2 == 0 && (R1 == 0 || R3 == 0)) +- return 0; +- +- Vmax = (3300 * R2) / (R1 + R2); +- Rpar = (R2 * R3) / (R3 + R2); +- Vmin = (3300 * Rpar) / (R1 + Rpar); +- Vout = Vmin + ((Vmax - Vmin) * Value) / 1024; +- +- *pValue = Vout; +- } +- } +- return status; +-} +- +-static int load_firmware(struct drxd_state *state, const char *fw_name) +-{ +- const struct firmware *fw; +- +- if (request_firmware(&fw, fw_name, state->dev) < 0) { +- printk(KERN_ERR "drxd: firmware load failure [%s]\n", fw_name); +- return -EIO; +- } +- +- state->microcode = kmemdup(fw->data, fw->size, GFP_KERNEL); +- if (state->microcode == NULL) { +- release_firmware(fw); +- printk(KERN_ERR "drxd: firmware load failure: no memory\n"); +- return -ENOMEM; +- } +- +- state->microcode_length = fw->size; +- release_firmware(fw); +- return 0; +-} +- +-static int DownloadMicrocode(struct drxd_state *state, +- const u8 *pMCImage, u32 Length) +-{ +- u8 *pSrc; +- u32 Address; +- u16 nBlocks; +- u16 BlockSize; +- u32 offset = 0; +- int i, status = 0; +- +- pSrc = (u8 *) pMCImage; +- /* We're not using Flags */ +- /* Flags = (pSrc[0] << 8) | pSrc[1]; */ +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- nBlocks = (pSrc[0] << 8) | pSrc[1]; +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- for (i = 0; i < nBlocks; i++) { +- Address = (pSrc[0] << 24) | (pSrc[1] << 16) | +- (pSrc[2] << 8) | pSrc[3]; +- pSrc += sizeof(u32); +- offset += sizeof(u32); +- +- BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16); +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- /* We're not using Flags */ +- /* u16 Flags = (pSrc[0] << 8) | pSrc[1]; */ +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- /* We're not using BlockCRC */ +- /* u16 BlockCRC = (pSrc[0] << 8) | pSrc[1]; */ +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- status = WriteBlock(state, Address, BlockSize, +- pSrc, DRX_I2C_CLEARCRC); +- if (status < 0) +- break; +- pSrc += BlockSize; +- offset += BlockSize; +- } +- +- return status; +-} +- +-static int HI_Command(struct drxd_state *state, u16 cmd, u16 * pResult) +-{ +- u32 nrRetries = 0; +- u16 waitCmd; +- int status; +- +- status = Write16(state, HI_RA_RAM_SRV_CMD__A, cmd, 0); +- if (status < 0) +- return status; +- +- do { +- nrRetries += 1; +- if (nrRetries > DRXD_MAX_RETRIES) { +- status = -1; +- break; +- }; +- status = Read16(state, HI_RA_RAM_SRV_CMD__A, &waitCmd, 0); +- } while (waitCmd != 0); +- +- if (status >= 0) +- status = Read16(state, HI_RA_RAM_SRV_RES__A, pResult, 0); +- return status; +-} +- +-static int HI_CfgCommand(struct drxd_state *state) +-{ +- int status = 0; +- +- mutex_lock(&state->mutex); +- Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); +- Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, state->hi_cfg_timing_div, 0); +- Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, state->hi_cfg_bridge_delay, 0); +- Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, state->hi_cfg_wakeup_key, 0); +- Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, state->hi_cfg_ctrl, 0); +- +- Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, HI_RA_RAM_SRV_RST_KEY_ACT, 0); +- +- if ((state->hi_cfg_ctrl & HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) == +- HI_RA_RAM_SRV_CFG_ACT_PWD_EXE) +- status = Write16(state, HI_RA_RAM_SRV_CMD__A, +- HI_RA_RAM_SRV_CMD_CONFIG, 0); +- else +- status = HI_Command(state, HI_RA_RAM_SRV_CMD_CONFIG, 0); +- mutex_unlock(&state->mutex); +- return status; +-} +- +-static int InitHI(struct drxd_state *state) +-{ +- state->hi_cfg_wakeup_key = (state->chip_adr); +- /* port/bridge/power down ctrl */ +- state->hi_cfg_ctrl = HI_RA_RAM_SRV_CFG_ACT_SLV0_ON; +- return HI_CfgCommand(state); +-} +- +-static int HI_ResetCommand(struct drxd_state *state) +-{ +- int status; +- +- mutex_lock(&state->mutex); +- status = Write16(state, HI_RA_RAM_SRV_RST_KEY__A, +- HI_RA_RAM_SRV_RST_KEY_ACT, 0); +- if (status == 0) +- status = HI_Command(state, HI_RA_RAM_SRV_CMD_RESET, 0); +- mutex_unlock(&state->mutex); +- msleep(1); +- return status; +-} +- +-static int DRX_ConfigureI2CBridge(struct drxd_state *state, int bEnableBridge) +-{ +- state->hi_cfg_ctrl &= (~HI_RA_RAM_SRV_CFG_ACT_BRD__M); +- if (bEnableBridge) +- state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_ON; +- else +- state->hi_cfg_ctrl |= HI_RA_RAM_SRV_CFG_ACT_BRD_OFF; +- +- return HI_CfgCommand(state); +-} +- +-#define HI_TR_WRITE 0x9 +-#define HI_TR_READ 0xA +-#define HI_TR_READ_WRITE 0xB +-#define HI_TR_BROADCAST 0x4 +- +-#if 0 +-static int AtomicReadBlock(struct drxd_state *state, +- u32 Addr, u16 DataSize, u8 *pData, u8 Flags) +-{ +- int status; +- int i = 0; +- +- /* Parameter check */ +- if ((!pData) || ((DataSize & 1) != 0)) +- return -1; +- +- mutex_lock(&state->mutex); +- +- do { +- /* Instruct HI to read n bytes */ +- /* TODO use proper names forthese egisters */ +- status = Write16(state, HI_RA_RAM_SRV_CFG_KEY__A, (HI_TR_FUNC_ADDR & 0xFFFF), 0); +- if (status < 0) +- break; +- status = Write16(state, HI_RA_RAM_SRV_CFG_DIV__A, (u16) (Addr >> 16), 0); +- if (status < 0) +- break; +- status = Write16(state, HI_RA_RAM_SRV_CFG_BDL__A, (u16) (Addr & 0xFFFF), 0); +- if (status < 0) +- break; +- status = Write16(state, HI_RA_RAM_SRV_CFG_WUP__A, (u16) ((DataSize / 2) - 1), 0); +- if (status < 0) +- break; +- status = Write16(state, HI_RA_RAM_SRV_CFG_ACT__A, HI_TR_READ, 0); +- if (status < 0) +- break; +- +- status = HI_Command(state, HI_RA_RAM_SRV_CMD_EXECUTE, 0); +- if (status < 0) +- break; +- +- } while (0); +- +- if (status >= 0) { +- for (i = 0; i < (DataSize / 2); i += 1) { +- u16 word; +- +- status = Read16(state, (HI_RA_RAM_USR_BEGIN__A + i), +- &word, 0); +- if (status < 0) +- break; +- pData[2 * i] = (u8) (word & 0xFF); +- pData[(2 * i) + 1] = (u8) (word >> 8); +- } +- } +- mutex_unlock(&state->mutex); +- return status; +-} +- +-static int AtomicReadReg32(struct drxd_state *state, +- u32 Addr, u32 *pData, u8 Flags) +-{ +- u8 buf[sizeof(u32)]; +- int status; +- +- if (!pData) +- return -1; +- status = AtomicReadBlock(state, Addr, sizeof(u32), buf, Flags); +- *pData = (((u32) buf[0]) << 0) + +- (((u32) buf[1]) << 8) + +- (((u32) buf[2]) << 16) + (((u32) buf[3]) << 24); +- return status; +-} +-#endif +- +-static int StopAllProcessors(struct drxd_state *state) +-{ +- return Write16(state, HI_COMM_EXEC__A, +- SC_COMM_EXEC_CTL_STOP, DRX_I2C_BROADCAST); +-} +- +-static int EnableAndResetMB(struct drxd_state *state) +-{ +- if (state->type_A) { +- /* disable? monitor bus observe @ EC_OC */ +- Write16(state, EC_OC_REG_OC_MON_SIO__A, 0x0000, 0x0000); +- } +- +- /* do inverse broadcast, followed by explicit write to HI */ +- Write16(state, HI_COMM_MB__A, 0x0000, DRX_I2C_BROADCAST); +- Write16(state, HI_COMM_MB__A, 0x0000, 0x0000); +- return 0; +-} +- +-static int InitCC(struct drxd_state *state) +-{ +- if (state->osc_clock_freq == 0 || +- state->osc_clock_freq > 20000 || +- (state->osc_clock_freq % 4000) != 0) { +- printk(KERN_ERR "invalid osc frequency %d\n", state->osc_clock_freq); +- return -1; +- } +- +- Write16(state, CC_REG_OSC_MODE__A, CC_REG_OSC_MODE_M20, 0); +- Write16(state, CC_REG_PLL_MODE__A, CC_REG_PLL_MODE_BYPASS_PLL | +- CC_REG_PLL_MODE_PUMP_CUR_12, 0); +- Write16(state, CC_REG_REF_DIVIDE__A, state->osc_clock_freq / 4000, 0); +- Write16(state, CC_REG_PWD_MODE__A, CC_REG_PWD_MODE_DOWN_PLL, 0); +- Write16(state, CC_REG_UPDATE__A, CC_REG_UPDATE_KEY, 0); +- +- return 0; +-} +- +-static int ResetECOD(struct drxd_state *state) +-{ +- int status = 0; +- +- if (state->type_A) +- status = Write16(state, EC_OD_REG_SYNC__A, 0x0664, 0); +- else +- status = Write16(state, B_EC_OD_REG_SYNC__A, 0x0664, 0); +- +- if (!(status < 0)) +- status = WriteTable(state, state->m_ResetECRAM); +- if (!(status < 0)) +- status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0001, 0); +- return status; +-} +- +-/* Configure PGA switch */ +- +-static int SetCfgPga(struct drxd_state *state, int pgaSwitch) +-{ +- int status; +- u16 AgModeLop = 0; +- u16 AgModeHip = 0; +- do { +- if (pgaSwitch) { +- /* PGA on */ +- /* fine gain */ +- status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); +- if (status < 0) +- break; +- AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); +- AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC; +- status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); +- if (status < 0) +- break; +- +- /* coarse gain */ +- status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); +- if (status < 0) +- break; +- AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); +- AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC; +- status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); +- if (status < 0) +- break; +- +- /* enable fine and coarse gain, enable AAF, +- no ext resistor */ +- status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN, 0x0000); +- if (status < 0) +- break; +- } else { +- /* PGA off, bypass */ +- +- /* fine gain */ +- status = Read16(state, B_FE_AG_REG_AG_MODE_LOP__A, &AgModeLop, 0x0000); +- if (status < 0) +- break; +- AgModeLop &= (~(B_FE_AG_REG_AG_MODE_LOP_MODE_C__M)); +- AgModeLop |= B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC; +- status = Write16(state, B_FE_AG_REG_AG_MODE_LOP__A, AgModeLop, 0x0000); +- if (status < 0) +- break; +- +- /* coarse gain */ +- status = Read16(state, B_FE_AG_REG_AG_MODE_HIP__A, &AgModeHip, 0x0000); +- if (status < 0) +- break; +- AgModeHip &= (~(B_FE_AG_REG_AG_MODE_HIP_MODE_J__M)); +- AgModeHip |= B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC; +- status = Write16(state, B_FE_AG_REG_AG_MODE_HIP__A, AgModeHip, 0x0000); +- if (status < 0) +- break; +- +- /* disable fine and coarse gain, enable AAF, +- no ext resistor */ +- status = Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, 0x0000); +- if (status < 0) +- break; +- } +- } while (0); +- return status; +-} +- +-static int InitFE(struct drxd_state *state) +-{ +- int status; +- +- do { +- status = WriteTable(state, state->m_InitFE_1); +- if (status < 0) +- break; +- +- if (state->type_A) { +- status = Write16(state, FE_AG_REG_AG_PGA_MODE__A, +- FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, +- 0); +- } else { +- if (state->PGA) +- status = SetCfgPga(state, 0); +- else +- status = +- Write16(state, B_FE_AG_REG_AG_PGA_MODE__A, +- B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN, +- 0); +- } +- +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_AG_AGC_SIO__A, state->m_FeAgRegAgAgcSio, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, FE_AG_REG_AG_PWD__A, state->m_FeAgRegAgPwd, 0x0000); +- if (status < 0) +- break; +- +- status = WriteTable(state, state->m_InitFE_2); +- if (status < 0) +- break; +- +- } while (0); +- +- return status; +-} +- +-static int InitFT(struct drxd_state *state) +-{ +- /* +- norm OFFSET, MB says =2 voor 8K en =3 voor 2K waarschijnlijk +- SC stuff +- */ +- return Write16(state, FT_REG_COMM_EXEC__A, 0x0001, 0x0000); +-} +- +-static int SC_WaitForReady(struct drxd_state *state) +-{ +- u16 curCmd; +- int i; +- +- for (i = 0; i < DRXD_MAX_RETRIES; i += 1) { +- int status = Read16(state, SC_RA_RAM_CMD__A, &curCmd, 0); +- if (status == 0 || curCmd == 0) +- return status; +- } +- return -1; +-} +- +-static int SC_SendCommand(struct drxd_state *state, u16 cmd) +-{ +- int status = 0; +- u16 errCode; +- +- Write16(state, SC_RA_RAM_CMD__A, cmd, 0); +- SC_WaitForReady(state); +- +- Read16(state, SC_RA_RAM_CMD_ADDR__A, &errCode, 0); +- +- if (errCode == 0xFFFF) { +- printk(KERN_ERR "Command Error\n"); +- status = -1; +- } +- +- return status; +-} +- +-static int SC_ProcStartCommand(struct drxd_state *state, +- u16 subCmd, u16 param0, u16 param1) +-{ +- int status = 0; +- u16 scExec; +- +- mutex_lock(&state->mutex); +- do { +- Read16(state, SC_COMM_EXEC__A, &scExec, 0); +- if (scExec != 1) { +- status = -1; +- break; +- } +- SC_WaitForReady(state); +- Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); +- Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); +- Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); +- +- SC_SendCommand(state, SC_RA_RAM_CMD_PROC_START); +- } while (0); +- mutex_unlock(&state->mutex); +- return status; +-} +- +-static int SC_SetPrefParamCommand(struct drxd_state *state, +- u16 subCmd, u16 param0, u16 param1) +-{ +- int status; +- +- mutex_lock(&state->mutex); +- do { +- status = SC_WaitForReady(state); +- if (status < 0) +- break; +- status = Write16(state, SC_RA_RAM_CMD_ADDR__A, subCmd, 0); +- if (status < 0) +- break; +- status = Write16(state, SC_RA_RAM_PARAM1__A, param1, 0); +- if (status < 0) +- break; +- status = Write16(state, SC_RA_RAM_PARAM0__A, param0, 0); +- if (status < 0) +- break; +- +- status = SC_SendCommand(state, SC_RA_RAM_CMD_SET_PREF_PARAM); +- if (status < 0) +- break; +- } while (0); +- mutex_unlock(&state->mutex); +- return status; +-} +- +-#if 0 +-static int SC_GetOpParamCommand(struct drxd_state *state, u16 * result) +-{ +- int status = 0; +- +- mutex_lock(&state->mutex); +- do { +- status = SC_WaitForReady(state); +- if (status < 0) +- break; +- status = SC_SendCommand(state, SC_RA_RAM_CMD_GET_OP_PARAM); +- if (status < 0) +- break; +- status = Read16(state, SC_RA_RAM_PARAM0__A, result, 0); +- if (status < 0) +- break; +- } while (0); +- mutex_unlock(&state->mutex); +- return status; +-} +-#endif +- +-static int ConfigureMPEGOutput(struct drxd_state *state, int bEnableOutput) +-{ +- int status; +- +- do { +- u16 EcOcRegIprInvMpg = 0; +- u16 EcOcRegOcModeLop = 0; +- u16 EcOcRegOcModeHip = 0; +- u16 EcOcRegOcMpgSio = 0; +- +- /*CHK_ERROR(Read16(state, EC_OC_REG_OC_MODE_LOP__A, &EcOcRegOcModeLop, 0)); */ +- +- if (state->operation_mode == OM_DVBT_Diversity_Front) { +- if (bEnableOutput) { +- EcOcRegOcModeHip |= +- B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR; +- } else +- EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; +- EcOcRegOcModeLop |= +- EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; +- } else { +- EcOcRegOcModeLop = state->m_EcOcRegOcModeLop; +- +- if (bEnableOutput) +- EcOcRegOcMpgSio &= (~(EC_OC_REG_OC_MPG_SIO__M)); +- else +- EcOcRegOcMpgSio |= EC_OC_REG_OC_MPG_SIO__M; +- +- /* Don't Insert RS Byte */ +- if (state->insert_rs_byte) { +- EcOcRegOcModeLop &= +- (~(EC_OC_REG_OC_MODE_LOP_PAR_ENA__M)); +- EcOcRegOcModeHip &= +- (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); +- EcOcRegOcModeHip |= +- EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE; +- } else { +- EcOcRegOcModeLop |= +- EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE; +- EcOcRegOcModeHip &= +- (~EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M); +- EcOcRegOcModeHip |= +- EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE; +- } +- +- /* Mode = Parallel */ +- if (state->enable_parallel) +- EcOcRegOcModeLop &= +- (~(EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M)); +- else +- EcOcRegOcModeLop |= +- EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL; +- } +- /* Invert Data */ +- /* EcOcRegIprInvMpg |= 0x00FF; */ +- EcOcRegIprInvMpg &= (~(0x00FF)); +- +- /* Invert Error ( we don't use the pin ) */ +- /* EcOcRegIprInvMpg |= 0x0100; */ +- EcOcRegIprInvMpg &= (~(0x0100)); +- +- /* Invert Start ( we don't use the pin ) */ +- /* EcOcRegIprInvMpg |= 0x0200; */ +- EcOcRegIprInvMpg &= (~(0x0200)); +- +- /* Invert Valid ( we don't use the pin ) */ +- /* EcOcRegIprInvMpg |= 0x0400; */ +- EcOcRegIprInvMpg &= (~(0x0400)); +- +- /* Invert Clock */ +- /* EcOcRegIprInvMpg |= 0x0800; */ +- EcOcRegIprInvMpg &= (~(0x0800)); +- +- /* EcOcRegOcModeLop =0x05; */ +- status = Write16(state, EC_OC_REG_IPR_INV_MPG__A, EcOcRegIprInvMpg, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_OC_MODE_LOP__A, EcOcRegOcModeLop, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_OC_MODE_HIP__A, EcOcRegOcModeHip, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_OC_REG_OC_MPG_SIO__A, EcOcRegOcMpgSio, 0); +- if (status < 0) +- break; +- } while (0); +- return status; +-} +- +-static int SetDeviceTypeId(struct drxd_state *state) +-{ +- int status = 0; +- u16 deviceId = 0; +- +- do { +- status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); +- if (status < 0) +- break; +- /* TODO: why twice? */ +- status = Read16(state, CC_REG_JTAGID_L__A, &deviceId, 0); +- if (status < 0) +- break; +- printk(KERN_INFO "drxd: deviceId = %04x\n", deviceId); +- +- state->type_A = 0; +- state->PGA = 0; +- state->diversity = 0; +- if (deviceId == 0) { /* on A2 only 3975 available */ +- state->type_A = 1; +- printk(KERN_INFO "DRX3975D-A2\n"); +- } else { +- deviceId >>= 12; +- printk(KERN_INFO "DRX397%dD-B1\n", deviceId); +- switch (deviceId) { +- case 4: +- state->diversity = 1; +- case 3: +- case 7: +- state->PGA = 1; +- break; +- case 6: +- state->diversity = 1; +- case 5: +- case 8: +- break; +- default: +- status = -1; +- break; +- } +- } +- } while (0); +- +- if (status < 0) +- return status; +- +- /* Init Table selection */ +- state->m_InitAtomicRead = DRXD_InitAtomicRead; +- state->m_InitSC = DRXD_InitSC; +- state->m_ResetECRAM = DRXD_ResetECRAM; +- if (state->type_A) { +- state->m_ResetCEFR = DRXD_ResetCEFR; +- state->m_InitFE_1 = DRXD_InitFEA2_1; +- state->m_InitFE_2 = DRXD_InitFEA2_2; +- state->m_InitCP = DRXD_InitCPA2; +- state->m_InitCE = DRXD_InitCEA2; +- state->m_InitEQ = DRXD_InitEQA2; +- state->m_InitEC = DRXD_InitECA2; +- if (load_firmware(state, DRX_FW_FILENAME_A2)) +- return -EIO; +- } else { +- state->m_ResetCEFR = NULL; +- state->m_InitFE_1 = DRXD_InitFEB1_1; +- state->m_InitFE_2 = DRXD_InitFEB1_2; +- state->m_InitCP = DRXD_InitCPB1; +- state->m_InitCE = DRXD_InitCEB1; +- state->m_InitEQ = DRXD_InitEQB1; +- state->m_InitEC = DRXD_InitECB1; +- if (load_firmware(state, DRX_FW_FILENAME_B1)) +- return -EIO; +- } +- if (state->diversity) { +- state->m_InitDiversityFront = DRXD_InitDiversityFront; +- state->m_InitDiversityEnd = DRXD_InitDiversityEnd; +- state->m_DisableDiversity = DRXD_DisableDiversity; +- state->m_StartDiversityFront = DRXD_StartDiversityFront; +- state->m_StartDiversityEnd = DRXD_StartDiversityEnd; +- state->m_DiversityDelay8MHZ = DRXD_DiversityDelay8MHZ; +- state->m_DiversityDelay6MHZ = DRXD_DiversityDelay6MHZ; +- } else { +- state->m_InitDiversityFront = NULL; +- state->m_InitDiversityEnd = NULL; +- state->m_DisableDiversity = NULL; +- state->m_StartDiversityFront = NULL; +- state->m_StartDiversityEnd = NULL; +- state->m_DiversityDelay8MHZ = NULL; +- state->m_DiversityDelay6MHZ = NULL; +- } +- +- return status; +-} +- +-static int CorrectSysClockDeviation(struct drxd_state *state) +-{ +- int status; +- s32 incr = 0; +- s32 nomincr = 0; +- u32 bandwidth = 0; +- u32 sysClockInHz = 0; +- u32 sysClockFreq = 0; /* in kHz */ +- s16 oscClockDeviation; +- s16 Diff; +- +- do { +- /* Retrieve bandwidth and incr, sanity check */ +- +- /* These accesses should be AtomicReadReg32, but that +- causes trouble (at least for diversity */ +- status = Read32(state, LC_RA_RAM_IFINCR_NOM_L__A, ((u32 *) &nomincr), 0); +- if (status < 0) +- break; +- status = Read32(state, FE_IF_REG_INCR0__A, (u32 *) &incr, 0); +- if (status < 0) +- break; +- +- if (state->type_A) { +- if ((nomincr - incr < -500) || (nomincr - incr > 500)) +- break; +- } else { +- if ((nomincr - incr < -2000) || (nomincr - incr > 2000)) +- break; +- } +- +- switch (state->props.bandwidth_hz) { +- case 8000000: +- bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; +- break; +- case 7000000: +- bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; +- break; +- case 6000000: +- bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; +- break; +- default: +- return -1; +- break; +- } +- +- /* Compute new sysclock value +- sysClockFreq = (((incr + 2^23)*bandwidth)/2^21)/1000 */ +- incr += (1 << 23); +- sysClockInHz = MulDiv32(incr, bandwidth, 1 << 21); +- sysClockFreq = (u32) (sysClockInHz / 1000); +- /* rounding */ +- if ((sysClockInHz % 1000) > 500) +- sysClockFreq++; +- +- /* Compute clock deviation in ppm */ +- oscClockDeviation = (u16) ((((s32) (sysClockFreq) - +- (s32) +- (state->expected_sys_clock_freq)) * +- 1000000L) / +- (s32) +- (state->expected_sys_clock_freq)); +- +- Diff = oscClockDeviation - state->osc_clock_deviation; +- /*printk(KERN_INFO "sysclockdiff=%d\n", Diff); */ +- if (Diff >= -200 && Diff <= 200) { +- state->sys_clock_freq = (u16) sysClockFreq; +- if (oscClockDeviation != state->osc_clock_deviation) { +- if (state->config.osc_deviation) { +- state->config.osc_deviation(state->priv, +- oscClockDeviation, +- 1); +- state->osc_clock_deviation = +- oscClockDeviation; +- } +- } +- /* switch OFF SRMM scan in SC */ +- status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DONT_SCAN, 0); +- if (status < 0) +- break; +- /* overrule FE_IF internal value for +- proper re-locking */ +- status = Write16(state, SC_RA_RAM_IF_SAVE__AX, state->current_fe_if_incr, 0); +- if (status < 0) +- break; +- state->cscd_state = CSCD_SAVED; +- } +- } while (0); +- +- return status; +-} +- +-static int DRX_Stop(struct drxd_state *state) +-{ +- int status; +- +- if (state->drxd_state != DRXD_STARTED) +- return 0; +- +- do { +- if (state->cscd_state != CSCD_SAVED) { +- u32 lock; +- status = DRX_GetLockStatus(state, &lock); +- if (status < 0) +- break; +- } +- +- status = StopOC(state); +- if (status < 0) +- break; +- +- state->drxd_state = DRXD_STOPPED; +- +- status = ConfigureMPEGOutput(state, 0); +- if (status < 0) +- break; +- +- if (state->type_A) { +- /* Stop relevant processors off the device */ +- status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- +- status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- } else { +- /* Stop all processors except HI & CC & FE */ +- status = Write16(state, B_SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, B_LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, B_FT_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, B_CP_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, B_CE_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, B_EQ_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, EC_OD_REG_COMM_EXEC__A, 0x0000, 0); +- if (status < 0) +- break; +- } +- +- } while (0); +- return status; +-} +- +-int SetOperationMode(struct drxd_state *state, int oMode) +-{ +- int status; +- +- do { +- if (state->drxd_state != DRXD_STOPPED) { +- status = -1; +- break; +- } +- +- if (oMode == state->operation_mode) { +- status = 0; +- break; +- } +- +- if (oMode != OM_Default && !state->diversity) { +- status = -1; +- break; +- } +- +- switch (oMode) { +- case OM_DVBT_Diversity_Front: +- status = WriteTable(state, state->m_InitDiversityFront); +- break; +- case OM_DVBT_Diversity_End: +- status = WriteTable(state, state->m_InitDiversityEnd); +- break; +- case OM_Default: +- /* We need to check how to +- get DRXD out of diversity */ +- default: +- status = WriteTable(state, state->m_DisableDiversity); +- break; +- } +- } while (0); +- +- if (!status) +- state->operation_mode = oMode; +- return status; +-} +- +-static int StartDiversity(struct drxd_state *state) +-{ +- int status = 0; +- u16 rcControl; +- +- do { +- if (state->operation_mode == OM_DVBT_Diversity_Front) { +- status = WriteTable(state, state->m_StartDiversityFront); +- if (status < 0) +- break; +- } else if (state->operation_mode == OM_DVBT_Diversity_End) { +- status = WriteTable(state, state->m_StartDiversityEnd); +- if (status < 0) +- break; +- if (state->props.bandwidth_hz == 8000000) { +- status = WriteTable(state, state->m_DiversityDelay8MHZ); +- if (status < 0) +- break; +- } else { +- status = WriteTable(state, state->m_DiversityDelay6MHZ); +- if (status < 0) +- break; +- } +- +- status = Read16(state, B_EQ_REG_RC_SEL_CAR__A, &rcControl, 0); +- if (status < 0) +- break; +- rcControl &= ~(B_EQ_REG_RC_SEL_CAR_FFTMODE__M); +- rcControl |= B_EQ_REG_RC_SEL_CAR_DIV_ON | +- /* combining enabled */ +- B_EQ_REG_RC_SEL_CAR_MEAS_A_CC | +- B_EQ_REG_RC_SEL_CAR_PASS_A_CC | +- B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC; +- status = Write16(state, B_EQ_REG_RC_SEL_CAR__A, rcControl, 0); +- if (status < 0) +- break; +- } +- } while (0); +- return status; +-} +- +-static int SetFrequencyShift(struct drxd_state *state, +- u32 offsetFreq, int channelMirrored) +-{ +- int negativeShift = (state->tuner_mirrors == channelMirrored); +- +- /* Handle all mirroring +- * +- * Note: ADC mirroring (aliasing) is implictly handled by limiting +- * feFsRegAddInc to 28 bits below +- * (if the result before masking is more than 28 bits, this means +- * that the ADC is mirroring. +- * The masking is in fact the aliasing of the ADC) +- * +- */ +- +- /* Compute register value, unsigned computation */ +- state->fe_fs_add_incr = MulDiv32(state->intermediate_freq + +- offsetFreq, +- 1 << 28, state->sys_clock_freq); +- /* Remove integer part */ +- state->fe_fs_add_incr &= 0x0FFFFFFFL; +- if (negativeShift) +- state->fe_fs_add_incr = ((1 << 28) - state->fe_fs_add_incr); +- +- /* Save the frequency shift without tunerOffset compensation +- for CtrlGetChannel. */ +- state->org_fe_fs_add_incr = MulDiv32(state->intermediate_freq, +- 1 << 28, state->sys_clock_freq); +- /* Remove integer part */ +- state->org_fe_fs_add_incr &= 0x0FFFFFFFL; +- if (negativeShift) +- state->org_fe_fs_add_incr = ((1L << 28) - +- state->org_fe_fs_add_incr); +- +- return Write32(state, FE_FS_REG_ADD_INC_LOP__A, +- state->fe_fs_add_incr, 0); +-} +- +-static int SetCfgNoiseCalibration(struct drxd_state *state, +- struct SNoiseCal *noiseCal) +-{ +- u16 beOptEna; +- int status = 0; +- +- do { +- status = Read16(state, SC_RA_RAM_BE_OPT_ENA__A, &beOptEna, 0); +- if (status < 0) +- break; +- if (noiseCal->cpOpt) { +- beOptEna |= (1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); +- } else { +- beOptEna &= ~(1 << SC_RA_RAM_BE_OPT_ENA_CP_OPT); +- status = Write16(state, CP_REG_AC_NEXP_OFFS__A, noiseCal->cpNexpOfs, 0); +- if (status < 0) +- break; +- } +- status = Write16(state, SC_RA_RAM_BE_OPT_ENA__A, beOptEna, 0); +- if (status < 0) +- break; +- +- if (!state->type_A) { +- status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_2K__A, noiseCal->tdCal2k, 0); +- if (status < 0) +- break; +- status = Write16(state, B_SC_RA_RAM_CO_TD_CAL_8K__A, noiseCal->tdCal8k, 0); +- if (status < 0) +- break; +- } +- } while (0); +- +- return status; +-} +- +-static int DRX_Start(struct drxd_state *state, s32 off) +-{ +- struct dtv_frontend_properties *p = &state->props; +- int status; +- +- u16 transmissionParams = 0; +- u16 operationMode = 0; +- u16 qpskTdTpsPwr = 0; +- u16 qam16TdTpsPwr = 0; +- u16 qam64TdTpsPwr = 0; +- u32 feIfIncr = 0; +- u32 bandwidth = 0; +- int mirrorFreqSpect; +- +- u16 qpskSnCeGain = 0; +- u16 qam16SnCeGain = 0; +- u16 qam64SnCeGain = 0; +- u16 qpskIsGainMan = 0; +- u16 qam16IsGainMan = 0; +- u16 qam64IsGainMan = 0; +- u16 qpskIsGainExp = 0; +- u16 qam16IsGainExp = 0; +- u16 qam64IsGainExp = 0; +- u16 bandwidthParam = 0; +- +- if (off < 0) +- off = (off - 500) / 1000; +- else +- off = (off + 500) / 1000; +- +- do { +- if (state->drxd_state != DRXD_STOPPED) +- return -1; +- status = ResetECOD(state); +- if (status < 0) +- break; +- if (state->type_A) { +- status = InitSC(state); +- if (status < 0) +- break; +- } else { +- status = InitFT(state); +- if (status < 0) +- break; +- status = InitCP(state); +- if (status < 0) +- break; +- status = InitCE(state); +- if (status < 0) +- break; +- status = InitEQ(state); +- if (status < 0) +- break; +- status = InitSC(state); +- if (status < 0) +- break; +- } +- +- /* Restore current IF & RF AGC settings */ +- +- status = SetCfgIfAgc(state, &state->if_agc_cfg); +- if (status < 0) +- break; +- status = SetCfgRfAgc(state, &state->rf_agc_cfg); +- if (status < 0) +- break; +- +- mirrorFreqSpect = (state->props.inversion == INVERSION_ON); +- +- switch (p->transmission_mode) { +- default: /* Not set, detect it automatically */ +- operationMode |= SC_RA_RAM_OP_AUTO_MODE__M; +- /* fall through , try first guess DRX_FFTMODE_8K */ +- case TRANSMISSION_MODE_8K: +- transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_8K; +- if (state->type_A) { +- status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_8K, 0x0000); +- if (status < 0) +- break; +- qpskSnCeGain = 99; +- qam16SnCeGain = 83; +- qam64SnCeGain = 67; +- } +- break; +- case TRANSMISSION_MODE_2K: +- transmissionParams |= SC_RA_RAM_OP_PARAM_MODE_2K; +- if (state->type_A) { +- status = Write16(state, EC_SB_REG_TR_MODE__A, EC_SB_REG_TR_MODE_2K, 0x0000); +- if (status < 0) +- break; +- qpskSnCeGain = 97; +- qam16SnCeGain = 71; +- qam64SnCeGain = 65; +- } +- break; +- } +- +- switch (p->guard_interval) { +- case GUARD_INTERVAL_1_4: +- transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; +- break; +- case GUARD_INTERVAL_1_8: +- transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_8; +- break; +- case GUARD_INTERVAL_1_16: +- transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_16; +- break; +- case GUARD_INTERVAL_1_32: +- transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_32; +- break; +- default: /* Not set, detect it automatically */ +- operationMode |= SC_RA_RAM_OP_AUTO_GUARD__M; +- /* try first guess 1/4 */ +- transmissionParams |= SC_RA_RAM_OP_PARAM_GUARD_4; +- break; +- } +- +- switch (p->hierarchy) { +- case HIERARCHY_1: +- transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A1; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0001, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_ALPHA__A, 0x0001, 0x0000); +- if (status < 0) +- break; +- +- qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; +- qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA1; +- qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA1; +- +- qpskIsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; +- qam16IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; +- qam64IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; +- +- qpskIsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; +- qam16IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; +- qam64IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; +- } +- break; +- +- case HIERARCHY_2: +- transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A2; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0002, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_ALPHA__A, 0x0002, 0x0000); +- if (status < 0) +- break; +- +- qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; +- qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA2; +- qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA2; +- +- qpskIsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; +- qam16IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE; +- qam64IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE; +- +- qpskIsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; +- qam16IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE; +- qam64IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE; +- } +- break; +- case HIERARCHY_4: +- transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_A4; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0003, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_ALPHA__A, 0x0003, 0x0000); +- if (status < 0) +- break; +- +- qpskTdTpsPwr = EQ_TD_TPS_PWR_UNKNOWN; +- qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHA4; +- qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHA4; +- +- qpskIsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE; +- qam16IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE; +- qam64IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE; +- +- qpskIsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE; +- qam16IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE; +- qam64IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE; +- } +- break; +- case HIERARCHY_AUTO: +- default: +- /* Not set, detect it automatically, start with none */ +- operationMode |= SC_RA_RAM_OP_AUTO_HIER__M; +- transmissionParams |= SC_RA_RAM_OP_PARAM_HIER_NO; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_ALPHA__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_ALPHA__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- +- qpskTdTpsPwr = EQ_TD_TPS_PWR_QPSK; +- qam16TdTpsPwr = EQ_TD_TPS_PWR_QAM16_ALPHAN; +- qam64TdTpsPwr = EQ_TD_TPS_PWR_QAM64_ALPHAN; +- +- qpskIsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE; +- qam16IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE; +- qam64IsGainMan = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE; +- +- qpskIsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE; +- qam16IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE; +- qam64IsGainExp = +- SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE; +- } +- break; +- } +- status = status; +- if (status < 0) +- break; +- +- switch (p->modulation) { +- default: +- operationMode |= SC_RA_RAM_OP_AUTO_CONST__M; +- /* fall through , try first guess +- DRX_CONSTELLATION_QAM64 */ +- case QAM_64: +- transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM64; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_CONST__A, 0x0002, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_64QAM, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0020, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0008, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0002, 0x0000); +- if (status < 0) +- break; +- +- status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam64TdTpsPwr, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_SN_CEGAIN__A, qam64SnCeGain, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam64IsGainMan, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam64IsGainExp, 0x0000); +- if (status < 0) +- break; +- } +- break; +- case QPSK: +- transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QPSK; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_CONST__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_QPSK, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- +- status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qpskTdTpsPwr, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_SN_CEGAIN__A, qpskSnCeGain, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qpskIsGainMan, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qpskIsGainExp, 0x0000); +- if (status < 0) +- break; +- } +- break; +- +- case QAM_16: +- transmissionParams |= SC_RA_RAM_OP_PARAM_CONST_QAM16; +- if (state->type_A) { +- status = Write16(state, EQ_REG_OT_CONST__A, 0x0001, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_CONST__A, EC_SB_REG_CONST_16QAM, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_MSB__A, 0x0010, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_BIT2__A, 0x0004, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EC_SB_REG_SCALE_LSB__A, 0x0000, 0x0000); +- if (status < 0) +- break; +- +- status = Write16(state, EQ_REG_TD_TPS_PWR_OFS__A, qam16TdTpsPwr, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_SN_CEGAIN__A, qam16SnCeGain, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_MAN__A, qam16IsGainMan, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, EQ_REG_IS_GAIN_EXP__A, qam16IsGainExp, 0x0000); +- if (status < 0) +- break; +- } +- break; +- +- } +- status = status; +- if (status < 0) +- break; +- +- switch (DRX_CHANNEL_HIGH) { +- default: +- case DRX_CHANNEL_AUTO: +- case DRX_CHANNEL_LOW: +- transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_LO; +- status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_LO, 0x0000); +- if (status < 0) +- break; +- break; +- case DRX_CHANNEL_HIGH: +- transmissionParams |= SC_RA_RAM_OP_PARAM_PRIO_HI; +- status = Write16(state, EC_SB_REG_PRIOR__A, EC_SB_REG_PRIOR_HI, 0x0000); +- if (status < 0) +- break; +- break; +- +- } +- +- switch (p->code_rate_HP) { +- case FEC_1_2: +- transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_1_2; +- if (state->type_A) { +- status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C1_2, 0x0000); +- if (status < 0) +- break; +- } +- break; +- default: +- operationMode |= SC_RA_RAM_OP_AUTO_RATE__M; +- case FEC_2_3: +- transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_2_3; +- if (state->type_A) { +- status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C2_3, 0x0000); +- if (status < 0) +- break; +- } +- break; +- case FEC_3_4: +- transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_3_4; +- if (state->type_A) { +- status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C3_4, 0x0000); +- if (status < 0) +- break; +- } +- break; +- case FEC_5_6: +- transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_5_6; +- if (state->type_A) { +- status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C5_6, 0x0000); +- if (status < 0) +- break; +- } +- break; +- case FEC_7_8: +- transmissionParams |= SC_RA_RAM_OP_PARAM_RATE_7_8; +- if (state->type_A) { +- status = Write16(state, EC_VD_REG_SET_CODERATE__A, EC_VD_REG_SET_CODERATE_C7_8, 0x0000); +- if (status < 0) +- break; +- } +- break; +- } +- status = status; +- if (status < 0) +- break; +- +- /* First determine real bandwidth (Hz) */ +- /* Also set delay for impulse noise cruncher (only A2) */ +- /* Also set parameters for EC_OC fix, note +- EC_OC_REG_TMD_HIL_MAR is changed +- by SC for fix for some 8K,1/8 guard but is restored by +- InitEC and ResetEC +- functions */ +- switch (p->bandwidth_hz) { +- case 0: +- p->bandwidth_hz = 8000000; +- /* fall through */ +- case 8000000: +- /* (64/7)*(8/8)*1000000 */ +- bandwidth = DRXD_BANDWIDTH_8MHZ_IN_HZ; +- +- bandwidthParam = 0; +- status = Write16(state, +- FE_AG_REG_IND_DEL__A, 50, 0x0000); +- break; +- case 7000000: +- /* (64/7)*(7/8)*1000000 */ +- bandwidth = DRXD_BANDWIDTH_7MHZ_IN_HZ; +- bandwidthParam = 0x4807; /*binary:0100 1000 0000 0111 */ +- status = Write16(state, +- FE_AG_REG_IND_DEL__A, 59, 0x0000); +- break; +- case 6000000: +- /* (64/7)*(6/8)*1000000 */ +- bandwidth = DRXD_BANDWIDTH_6MHZ_IN_HZ; +- bandwidthParam = 0x0F07; /*binary: 0000 1111 0000 0111 */ +- status = Write16(state, +- FE_AG_REG_IND_DEL__A, 71, 0x0000); +- break; +- default: +- status = -EINVAL; +- } +- if (status < 0) +- break; +- +- status = Write16(state, SC_RA_RAM_BAND__A, bandwidthParam, 0x0000); +- if (status < 0) +- break; +- +- { +- u16 sc_config; +- status = Read16(state, SC_RA_RAM_CONFIG__A, &sc_config, 0); +- if (status < 0) +- break; +- +- /* enable SLAVE mode in 2k 1/32 to +- prevent timing change glitches */ +- if ((p->transmission_mode == TRANSMISSION_MODE_2K) && +- (p->guard_interval == GUARD_INTERVAL_1_32)) { +- /* enable slave */ +- sc_config |= SC_RA_RAM_CONFIG_SLAVE__M; +- } else { +- /* disable slave */ +- sc_config &= ~SC_RA_RAM_CONFIG_SLAVE__M; +- } +- status = Write16(state, SC_RA_RAM_CONFIG__A, sc_config, 0); +- if (status < 0) +- break; +- } +- +- status = SetCfgNoiseCalibration(state, &state->noise_cal); +- if (status < 0) +- break; +- +- if (state->cscd_state == CSCD_INIT) { +- /* switch on SRMM scan in SC */ +- status = Write16(state, SC_RA_RAM_SAMPLE_RATE_COUNT__A, DRXD_OSCDEV_DO_SCAN, 0x0000); +- if (status < 0) +- break; +-/* CHK_ERROR(Write16(SC_RA_RAM_SAMPLE_RATE_STEP__A, DRXD_OSCDEV_STEP, 0x0000));*/ +- state->cscd_state = CSCD_SET; +- } +- +- /* Now compute FE_IF_REG_INCR */ +- /*((( SysFreq/BandWidth)/2)/2) -1) * 2^23) => +- ((SysFreq / BandWidth) * (2^21) ) - (2^23) */ +- feIfIncr = MulDiv32(state->sys_clock_freq * 1000, +- (1ULL << 21), bandwidth) - (1 << 23); +- status = Write16(state, FE_IF_REG_INCR0__A, (u16) (feIfIncr & FE_IF_REG_INCR0__M), 0x0000); +- if (status < 0) +- break; +- status = Write16(state, FE_IF_REG_INCR1__A, (u16) ((feIfIncr >> FE_IF_REG_INCR0__W) & FE_IF_REG_INCR1__M), 0x0000); +- if (status < 0) +- break; +- /* Bandwidth setting done */ +- +- /* Mirror & frequency offset */ +- SetFrequencyShift(state, off, mirrorFreqSpect); +- +- /* Start SC, write channel settings to SC */ +- +- /* Enable SC after setting all other parameters */ +- status = Write16(state, SC_COMM_STATE__A, 0, 0x0000); +- if (status < 0) +- break; +- status = Write16(state, SC_COMM_EXEC__A, 1, 0x0000); +- if (status < 0) +- break; +- +- /* Write SC parameter registers, operation mode */ +-#if 1 +- operationMode = (SC_RA_RAM_OP_AUTO_MODE__M | +- SC_RA_RAM_OP_AUTO_GUARD__M | +- SC_RA_RAM_OP_AUTO_CONST__M | +- SC_RA_RAM_OP_AUTO_HIER__M | +- SC_RA_RAM_OP_AUTO_RATE__M); +-#endif +- status = SC_SetPrefParamCommand(state, 0x0000, transmissionParams, operationMode); +- if (status < 0) +- break; +- +- /* Start correct processes to get in lock */ +- status = SC_ProcStartCommand(state, SC_RA_RAM_PROC_LOCKTRACK, SC_RA_RAM_SW_EVENT_RUN_NMASK__M, SC_RA_RAM_LOCKTRACK_MIN); +- if (status < 0) +- break; +- +- status = StartOC(state); +- if (status < 0) +- break; +- +- if (state->operation_mode != OM_Default) { +- status = StartDiversity(state); +- if (status < 0) +- break; +- } +- +- state->drxd_state = DRXD_STARTED; +- } while (0); +- +- return status; +-} +- +-static int CDRXD(struct drxd_state *state, u32 IntermediateFrequency) +-{ +- u32 ulRfAgcOutputLevel = 0xffffffff; +- u32 ulRfAgcSettleLevel = 528; /* Optimum value for MT2060 */ +- u32 ulRfAgcMinLevel = 0; /* Currently unused */ +- u32 ulRfAgcMaxLevel = DRXD_FE_CTRL_MAX; /* Currently unused */ +- u32 ulRfAgcSpeed = 0; /* Currently unused */ +- u32 ulRfAgcMode = 0; /*2; Off */ +- u32 ulRfAgcR1 = 820; +- u32 ulRfAgcR2 = 2200; +- u32 ulRfAgcR3 = 150; +- u32 ulIfAgcMode = 0; /* Auto */ +- u32 ulIfAgcOutputLevel = 0xffffffff; +- u32 ulIfAgcSettleLevel = 0xffffffff; +- u32 ulIfAgcMinLevel = 0xffffffff; +- u32 ulIfAgcMaxLevel = 0xffffffff; +- u32 ulIfAgcSpeed = 0xffffffff; +- u32 ulIfAgcR1 = 820; +- u32 ulIfAgcR2 = 2200; +- u32 ulIfAgcR3 = 150; +- u32 ulClock = state->config.clock; +- u32 ulSerialMode = 0; +- u32 ulEcOcRegOcModeLop = 4; /* Dynamic DTO source */ +- u32 ulHiI2cDelay = HI_I2C_DELAY; +- u32 ulHiI2cBridgeDelay = HI_I2C_BRIDGE_DELAY; +- u32 ulHiI2cPatch = 0; +- u32 ulEnvironment = APPENV_PORTABLE; +- u32 ulEnvironmentDiversity = APPENV_MOBILE; +- u32 ulIFFilter = IFFILTER_SAW; +- +- state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; +- state->if_agc_cfg.outputLevel = 0; +- state->if_agc_cfg.settleLevel = 140; +- state->if_agc_cfg.minOutputLevel = 0; +- state->if_agc_cfg.maxOutputLevel = 1023; +- state->if_agc_cfg.speed = 904; +- +- if (ulIfAgcMode == 1 && ulIfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { +- state->if_agc_cfg.ctrlMode = AGC_CTRL_USER; +- state->if_agc_cfg.outputLevel = (u16) (ulIfAgcOutputLevel); +- } +- +- if (ulIfAgcMode == 0 && +- ulIfAgcSettleLevel <= DRXD_FE_CTRL_MAX && +- ulIfAgcMinLevel <= DRXD_FE_CTRL_MAX && +- ulIfAgcMaxLevel <= DRXD_FE_CTRL_MAX && +- ulIfAgcSpeed <= DRXD_FE_CTRL_MAX) { +- state->if_agc_cfg.ctrlMode = AGC_CTRL_AUTO; +- state->if_agc_cfg.settleLevel = (u16) (ulIfAgcSettleLevel); +- state->if_agc_cfg.minOutputLevel = (u16) (ulIfAgcMinLevel); +- state->if_agc_cfg.maxOutputLevel = (u16) (ulIfAgcMaxLevel); +- state->if_agc_cfg.speed = (u16) (ulIfAgcSpeed); +- } +- +- state->if_agc_cfg.R1 = (u16) (ulIfAgcR1); +- state->if_agc_cfg.R2 = (u16) (ulIfAgcR2); +- state->if_agc_cfg.R3 = (u16) (ulIfAgcR3); +- +- state->rf_agc_cfg.R1 = (u16) (ulRfAgcR1); +- state->rf_agc_cfg.R2 = (u16) (ulRfAgcR2); +- state->rf_agc_cfg.R3 = (u16) (ulRfAgcR3); +- +- state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; +- /* rest of the RFAgcCfg structure currently unused */ +- if (ulRfAgcMode == 1 && ulRfAgcOutputLevel <= DRXD_FE_CTRL_MAX) { +- state->rf_agc_cfg.ctrlMode = AGC_CTRL_USER; +- state->rf_agc_cfg.outputLevel = (u16) (ulRfAgcOutputLevel); +- } +- +- if (ulRfAgcMode == 0 && +- ulRfAgcSettleLevel <= DRXD_FE_CTRL_MAX && +- ulRfAgcMinLevel <= DRXD_FE_CTRL_MAX && +- ulRfAgcMaxLevel <= DRXD_FE_CTRL_MAX && +- ulRfAgcSpeed <= DRXD_FE_CTRL_MAX) { +- state->rf_agc_cfg.ctrlMode = AGC_CTRL_AUTO; +- state->rf_agc_cfg.settleLevel = (u16) (ulRfAgcSettleLevel); +- state->rf_agc_cfg.minOutputLevel = (u16) (ulRfAgcMinLevel); +- state->rf_agc_cfg.maxOutputLevel = (u16) (ulRfAgcMaxLevel); +- state->rf_agc_cfg.speed = (u16) (ulRfAgcSpeed); +- } +- +- if (ulRfAgcMode == 2) +- state->rf_agc_cfg.ctrlMode = AGC_CTRL_OFF; +- +- if (ulEnvironment <= 2) +- state->app_env_default = (enum app_env) +- (ulEnvironment); +- if (ulEnvironmentDiversity <= 2) +- state->app_env_diversity = (enum app_env) +- (ulEnvironmentDiversity); +- +- if (ulIFFilter == IFFILTER_DISCRETE) { +- /* discrete filter */ +- state->noise_cal.cpOpt = 0; +- state->noise_cal.cpNexpOfs = 40; +- state->noise_cal.tdCal2k = -40; +- state->noise_cal.tdCal8k = -24; +- } else { +- /* SAW filter */ +- state->noise_cal.cpOpt = 1; +- state->noise_cal.cpNexpOfs = 0; +- state->noise_cal.tdCal2k = -21; +- state->noise_cal.tdCal8k = -24; +- } +- state->m_EcOcRegOcModeLop = (u16) (ulEcOcRegOcModeLop); +- +- state->chip_adr = (state->config.demod_address << 1) | 1; +- switch (ulHiI2cPatch) { +- case 1: +- state->m_HiI2cPatch = DRXD_HiI2cPatch_1; +- break; +- case 3: +- state->m_HiI2cPatch = DRXD_HiI2cPatch_3; +- break; +- default: +- state->m_HiI2cPatch = NULL; +- } +- +- /* modify tuner and clock attributes */ +- state->intermediate_freq = (u16) (IntermediateFrequency / 1000); +- /* expected system clock frequency in kHz */ +- state->expected_sys_clock_freq = 48000; +- /* real system clock frequency in kHz */ +- state->sys_clock_freq = 48000; +- state->osc_clock_freq = (u16) ulClock; +- state->osc_clock_deviation = 0; +- state->cscd_state = CSCD_INIT; +- state->drxd_state = DRXD_UNINITIALIZED; +- +- state->PGA = 0; +- state->type_A = 0; +- state->tuner_mirrors = 0; +- +- /* modify MPEG output attributes */ +- state->insert_rs_byte = state->config.insert_rs_byte; +- state->enable_parallel = (ulSerialMode != 1); +- +- /* Timing div, 250ns/Psys */ +- /* Timing div, = ( delay (nano seconds) * sysclk (kHz) )/ 1000 */ +- +- state->hi_cfg_timing_div = (u16) ((state->sys_clock_freq / 1000) * +- ulHiI2cDelay) / 1000; +- /* Bridge delay, uses oscilator clock */ +- /* Delay = ( delay (nano seconds) * oscclk (kHz) )/ 1000 */ +- state->hi_cfg_bridge_delay = (u16) ((state->osc_clock_freq / 1000) * +- ulHiI2cBridgeDelay) / 1000; +- +- state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; +- /* state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; */ +- state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; +- return 0; +-} +- +-int DRXD_init(struct drxd_state *state, const u8 * fw, u32 fw_size) +-{ +- int status = 0; +- u32 driverVersion; +- +- if (state->init_done) +- return 0; +- +- CDRXD(state, state->config.IF ? state->config.IF : 36000000); +- +- do { +- state->operation_mode = OM_Default; +- +- status = SetDeviceTypeId(state); +- if (status < 0) +- break; +- +- /* Apply I2c address patch to B1 */ +- if (!state->type_A && state->m_HiI2cPatch != NULL) +- status = WriteTable(state, state->m_HiI2cPatch); +- if (status < 0) +- break; +- +- if (state->type_A) { +- /* HI firmware patch for UIO readout, +- avoid clearing of result register */ +- status = Write16(state, 0x43012D, 0x047f, 0); +- if (status < 0) +- break; +- } +- +- status = HI_ResetCommand(state); +- if (status < 0) +- break; +- +- status = StopAllProcessors(state); +- if (status < 0) +- break; +- status = InitCC(state); +- if (status < 0) +- break; +- +- state->osc_clock_deviation = 0; +- +- if (state->config.osc_deviation) +- state->osc_clock_deviation = +- state->config.osc_deviation(state->priv, 0, 0); +- { +- /* Handle clock deviation */ +- s32 devB; +- s32 devA = (s32) (state->osc_clock_deviation) * +- (s32) (state->expected_sys_clock_freq); +- /* deviation in kHz */ +- s32 deviation = (devA / (1000000L)); +- /* rounding, signed */ +- if (devA > 0) +- devB = (2); +- else +- devB = (-2); +- if ((devB * (devA % 1000000L) > 1000000L)) { +- /* add +1 or -1 */ +- deviation += (devB / 2); +- } +- +- state->sys_clock_freq = +- (u16) ((state->expected_sys_clock_freq) + +- deviation); +- } +- status = InitHI(state); +- if (status < 0) +- break; +- status = InitAtomicRead(state); +- if (status < 0) +- break; +- +- status = EnableAndResetMB(state); +- if (status < 0) +- break; +- if (state->type_A) +- status = ResetCEFR(state); +- if (status < 0) +- break; +- +- if (fw) { +- status = DownloadMicrocode(state, fw, fw_size); +- if (status < 0) +- break; +- } else { +- status = DownloadMicrocode(state, state->microcode, state->microcode_length); +- if (status < 0) +- break; +- } +- +- if (state->PGA) { +- state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_PRO; +- SetCfgPga(state, 0); /* PGA = 0 dB */ +- } else { +- state->m_FeAgRegAgPwd = DRXD_DEF_AG_PWD_CONSUMER; +- } +- +- state->m_FeAgRegAgAgcSio = DRXD_DEF_AG_AGC_SIO; +- +- status = InitFE(state); +- if (status < 0) +- break; +- status = InitFT(state); +- if (status < 0) +- break; +- status = InitCP(state); +- if (status < 0) +- break; +- status = InitCE(state); +- if (status < 0) +- break; +- status = InitEQ(state); +- if (status < 0) +- break; +- status = InitEC(state); +- if (status < 0) +- break; +- status = InitSC(state); +- if (status < 0) +- break; +- +- status = SetCfgIfAgc(state, &state->if_agc_cfg); +- if (status < 0) +- break; +- status = SetCfgRfAgc(state, &state->rf_agc_cfg); +- if (status < 0) +- break; +- +- state->cscd_state = CSCD_INIT; +- status = Write16(state, SC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- status = Write16(state, LC_COMM_EXEC__A, SC_COMM_EXEC_CTL_STOP, 0); +- if (status < 0) +- break; +- +- driverVersion = (((VERSION_MAJOR / 10) << 4) + +- (VERSION_MAJOR % 10)) << 24; +- driverVersion += (((VERSION_MINOR / 10) << 4) + +- (VERSION_MINOR % 10)) << 16; +- driverVersion += ((VERSION_PATCH / 1000) << 12) + +- ((VERSION_PATCH / 100) << 8) + +- ((VERSION_PATCH / 10) << 4) + (VERSION_PATCH % 10); +- +- status = Write32(state, SC_RA_RAM_DRIVER_VERSION__AX, driverVersion, 0); +- if (status < 0) +- break; +- +- status = StopOC(state); +- if (status < 0) +- break; +- +- state->drxd_state = DRXD_STOPPED; +- state->init_done = 1; +- status = 0; +- } while (0); +- return status; +-} +- +-int DRXD_status(struct drxd_state *state, u32 * pLockStatus) +-{ +- DRX_GetLockStatus(state, pLockStatus); +- +- /*if (*pLockStatus&DRX_LOCK_MPEG) */ +- if (*pLockStatus & DRX_LOCK_FEC) { +- ConfigureMPEGOutput(state, 1); +- /* Get status again, in case we have MPEG lock now */ +- /*DRX_GetLockStatus(state, pLockStatus); */ +- } +- +- return 0; +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static int drxd_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- u32 value; +- int res; +- +- res = ReadIFAgc(state, &value); +- if (res < 0) +- *strength = 0; +- else +- *strength = 0xffff - (value << 4); +- return 0; +-} +- +-static int drxd_read_status(struct dvb_frontend *fe, fe_status_t * status) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- u32 lock; +- +- DRXD_status(state, &lock); +- *status = 0; +- /* No MPEG lock in V255 firmware, bug ? */ +-#if 1 +- if (lock & DRX_LOCK_MPEG) +- *status |= FE_HAS_LOCK; +-#else +- if (lock & DRX_LOCK_FEC) +- *status |= FE_HAS_LOCK; +-#endif +- if (lock & DRX_LOCK_FEC) +- *status |= FE_HAS_VITERBI | FE_HAS_SYNC; +- if (lock & DRX_LOCK_DEMOD) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- +- return 0; +-} +- +-static int drxd_init(struct dvb_frontend *fe) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- int err = 0; +- +-/* if (request_firmware(&state->fw, "drxd.fw", state->dev)<0) */ +- return DRXD_init(state, 0, 0); +- +- err = DRXD_init(state, state->fw->data, state->fw->size); +- release_firmware(state->fw); +- return err; +-} +- +-int drxd_config_i2c(struct dvb_frontend *fe, int onoff) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- +- if (state->config.disable_i2c_gate_ctrl == 1) +- return 0; +- +- return DRX_ConfigureI2CBridge(state, onoff); +-} +-EXPORT_SYMBOL(drxd_config_i2c); +- +-static int drxd_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *sets) +-{ +- sets->min_delay_ms = 10000; +- sets->max_drift = 0; +- sets->step_size = 0; +- return 0; +-} +- +-static int drxd_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- *ber = 0; +- return 0; +-} +- +-static int drxd_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- *snr = 0; +- return 0; +-} +- +-static int drxd_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +-{ +- *ucblocks = 0; +- return 0; +-} +- +-static int drxd_sleep(struct dvb_frontend *fe) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- +- ConfigureMPEGOutput(state, 0); +- return 0; +-} +- +-static int drxd_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- return drxd_config_i2c(fe, enable); +-} +- +-static int drxd_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct drxd_state *state = fe->demodulator_priv; +- s32 off = 0; +- +- state->props = *p; +- DRX_Stop(state); +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- msleep(200); +- +- return DRX_Start(state, off); +-} +- +-static void drxd_release(struct dvb_frontend *fe) +-{ +- struct drxd_state *state = fe->demodulator_priv; +- +- kfree(state); +-} +- +-static struct dvb_frontend_ops drxd_ops = { +- .delsys = { SYS_DVBT}, +- .info = { +- .name = "Micronas DRXD DVB-T", +- .frequency_min = 47125000, +- .frequency_max = 855250000, +- .frequency_stepsize = 166667, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | FE_CAN_MUTE_TS}, +- +- .release = drxd_release, +- .init = drxd_init, +- .sleep = drxd_sleep, +- .i2c_gate_ctrl = drxd_i2c_gate_ctrl, +- +- .set_frontend = drxd_set_frontend, +- .get_tune_settings = drxd_get_tune_settings, +- +- .read_status = drxd_read_status, +- .read_ber = drxd_read_ber, +- .read_signal_strength = drxd_read_signal_strength, +- .read_snr = drxd_read_snr, +- .read_ucblocks = drxd_read_ucblocks, +-}; +- +-struct dvb_frontend *drxd_attach(const struct drxd_config *config, +- void *priv, struct i2c_adapter *i2c, +- struct device *dev) +-{ +- struct drxd_state *state = NULL; +- +- state = kmalloc(sizeof(struct drxd_state), GFP_KERNEL); +- if (!state) +- return NULL; +- memset(state, 0, sizeof(*state)); +- +- memcpy(&state->ops, &drxd_ops, sizeof(struct dvb_frontend_ops)); +- state->dev = dev; +- state->config = *config; +- state->i2c = i2c; +- state->priv = priv; +- +- mutex_init(&state->mutex); +- +- if (Read16(state, 0, 0, 0) < 0) +- goto error; +- +- memcpy(&state->frontend.ops, &drxd_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- ConfigureMPEGOutput(state, 0); +- return &state->frontend; +- +-error: +- printk(KERN_ERR "drxd: not found\n"); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(drxd_attach); +- +-MODULE_DESCRIPTION("DRXD driver"); +-MODULE_AUTHOR("Micronas"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/drxd_map_firm.h b/drivers/media/dvb/frontends/drxd_map_firm.h +deleted file mode 100644 +index 6bc553a..0000000 +--- a/drivers/media/dvb/frontends/drxd_map_firm.h ++++ /dev/null +@@ -1,1013 +0,0 @@ +-/* +- * drx3973d_map_firm.h +- * +- * Copyright (C) 2006-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#ifndef __DRX3973D_MAP__H__ +-#define __DRX3973D_MAP__H__ +- +-/* +- * Note: originally, this file contained 12000+ lines of data +- * Probably a few lines for every firwmare assembler instruction. However, +- * only a few defines were actually used. So, removed all uneeded lines. +- * If ever needed, the other lines can be easily obtained via git history. +- */ +- +-#define HI_COMM_EXEC__A 0x400000 +-#define HI_COMM_MB__A 0x400002 +-#define HI_CT_REG_COMM_STATE__A 0x410001 +-#define HI_RA_RAM_SRV_RES__A 0x420031 +-#define HI_RA_RAM_SRV_CMD__A 0x420032 +-#define HI_RA_RAM_SRV_CMD_RESET 0x2 +-#define HI_RA_RAM_SRV_CMD_CONFIG 0x3 +-#define HI_RA_RAM_SRV_CMD_EXECUTE 0x6 +-#define HI_RA_RAM_SRV_RST_KEY__A 0x420033 +-#define HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 +-#define HI_RA_RAM_SRV_CFG_KEY__A 0x420033 +-#define HI_RA_RAM_SRV_CFG_DIV__A 0x420034 +-#define HI_RA_RAM_SRV_CFG_BDL__A 0x420035 +-#define HI_RA_RAM_SRV_CFG_WUP__A 0x420036 +-#define HI_RA_RAM_SRV_CFG_ACT__A 0x420037 +-#define HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 +-#define HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 +-#define HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 +-#define HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 +-#define HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 +-#define HI_RA_RAM_USR_BEGIN__A 0x420040 +-#define HI_IF_RAM_TRP_BPT0__AX 0x430000 +-#define HI_IF_RAM_USR_BEGIN__A 0x430200 +-#define SC_COMM_EXEC__A 0x800000 +-#define SC_COMM_EXEC_CTL_STOP 0x0 +-#define SC_COMM_STATE__A 0x800001 +-#define SC_RA_RAM_PARAM0__A 0x820040 +-#define SC_RA_RAM_PARAM1__A 0x820041 +-#define SC_RA_RAM_CMD_ADDR__A 0x820042 +-#define SC_RA_RAM_CMD__A 0x820043 +-#define SC_RA_RAM_CMD_PROC_START 0x1 +-#define SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +-#define SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +-#define SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +-#define SC_RA_RAM_LOCKTRACK_MIN 0x1 +-#define SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +-#define SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +-#define SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +-#define SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +-#define SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +-#define SC_RA_RAM_OP_PARAM_GUARD_4 0xC +-#define SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +-#define SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +-#define SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +-#define SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +-#define SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +-#define SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +-#define SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +-#define SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +-#define SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +-#define SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +-#define SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +-#define SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +-#define SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +-#define SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +-#define SC_RA_RAM_OP_AUTO_MODE__M 0x1 +-#define SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +-#define SC_RA_RAM_OP_AUTO_CONST__M 0x4 +-#define SC_RA_RAM_OP_AUTO_HIER__M 0x8 +-#define SC_RA_RAM_OP_AUTO_RATE__M 0x10 +-#define SC_RA_RAM_LOCK__A 0x82004B +-#define SC_RA_RAM_LOCK_DEMOD__M 0x1 +-#define SC_RA_RAM_LOCK_FEC__M 0x2 +-#define SC_RA_RAM_LOCK_MPEG__M 0x4 +-#define SC_RA_RAM_BE_OPT_ENA__A 0x82004C +-#define SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 +-#define SC_RA_RAM_BE_OPT_DELAY__A 0x82004D +-#define SC_RA_RAM_CONFIG__A 0x820050 +-#define SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 +-#define SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 +-#define SC_RA_RAM_CONFIG_SLAVE__M 0x20 +-#define SC_RA_RAM_IF_SAVE__AX 0x82008E +-#define SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 +-#define SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 +-#define SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 +-#define SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 +-#define SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 +-#define SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 +-#define SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 +-#define SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 +-#define SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 +-#define SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 +-#define SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 +-#define SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 +-#define SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 +-#define SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 +-#define SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 +-#define SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 +-#define SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 +-#define SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 +-#define SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA +-#define SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB +-#define SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB +-#define SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 +-#define SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC +-#define SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 +-#define SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD +-#define SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 +-#define SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 +-#define SC_RA_RAM_BAND__A 0x8200EC +-#define SC_RA_RAM_LC_ABS_2K__A 0x8200F4 +-#define SC_RA_RAM_LC_ABS_2K__PRE 0x1F +-#define SC_RA_RAM_LC_ABS_8K__A 0x8200F5 +-#define SC_RA_RAM_LC_ABS_8K__PRE 0x1F +-#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x1D6 +-#define SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 +-#define SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1BB +-#define SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x5 +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x1EF +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x15E +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x5 +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x11A +-#define SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x6 +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x1FB +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x12F +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x5 +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x197 +-#define SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x5 +-#define SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE +-#define SC_RA_RAM_PROC_LOCKTRACK 0x0 +-#define FE_COMM_EXEC__A 0xC00000 +-#define FE_AD_REG_COMM_EXEC__A 0xC10000 +-#define FE_AD_REG_FDB_IN__A 0xC10012 +-#define FE_AD_REG_PD__A 0xC10013 +-#define FE_AD_REG_INVEXT__A 0xC10014 +-#define FE_AD_REG_CLKNEG__A 0xC10015 +-#define FE_AG_REG_COMM_EXEC__A 0xC20000 +-#define FE_AG_REG_AG_MODE_LOP__A 0xC20010 +-#define FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 +-#define FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 +-#define FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 +-#define FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 +-#define FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 +-#define FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 +-#define FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 +-#define FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 +-#define FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 +-#define FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 +-#define FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 +-#define FE_AG_REG_AG_MODE_HIP__A 0xC20011 +-#define FE_AG_REG_AG_PGA_MODE__A 0xC20012 +-#define FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 +-#define FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 +-#define FE_AG_REG_AG_AGC_SIO__A 0xC20013 +-#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 +-#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 +-#define FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 +-#define FE_AG_REG_AG_PWD__A 0xC20015 +-#define FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 +-#define FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 +-#define FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 +-#define FE_AG_REG_DCE_AUR_CNT__A 0xC20016 +-#define FE_AG_REG_DCE_RUR_CNT__A 0xC20017 +-#define FE_AG_REG_ACE_AUR_CNT__A 0xC2001A +-#define FE_AG_REG_ACE_RUR_CNT__A 0xC2001B +-#define FE_AG_REG_CDR_RUR_CNT__A 0xC20020 +-#define FE_AG_REG_EGC_RUR_CNT__A 0xC20024 +-#define FE_AG_REG_EGC_SET_LVL__A 0xC20025 +-#define FE_AG_REG_EGC_SET_LVL__M 0x1FF +-#define FE_AG_REG_EGC_FLA_RGN__A 0xC20026 +-#define FE_AG_REG_EGC_SLO_RGN__A 0xC20027 +-#define FE_AG_REG_EGC_JMP_PSN__A 0xC20028 +-#define FE_AG_REG_EGC_FLA_INC__A 0xC20029 +-#define FE_AG_REG_EGC_FLA_DEC__A 0xC2002A +-#define FE_AG_REG_EGC_SLO_INC__A 0xC2002B +-#define FE_AG_REG_EGC_SLO_DEC__A 0xC2002C +-#define FE_AG_REG_EGC_FAS_INC__A 0xC2002D +-#define FE_AG_REG_EGC_FAS_DEC__A 0xC2002E +-#define FE_AG_REG_PM1_AGC_WRI__A 0xC20030 +-#define FE_AG_REG_PM1_AGC_WRI__M 0x7FF +-#define FE_AG_REG_GC1_AGC_RIC__A 0xC20031 +-#define FE_AG_REG_GC1_AGC_OFF__A 0xC20032 +-#define FE_AG_REG_GC1_AGC_MAX__A 0xC20033 +-#define FE_AG_REG_GC1_AGC_MIN__A 0xC20034 +-#define FE_AG_REG_GC1_AGC_DAT__A 0xC20035 +-#define FE_AG_REG_GC1_AGC_DAT__M 0x3FF +-#define FE_AG_REG_PM2_AGC_WRI__A 0xC20036 +-#define FE_AG_REG_IND_WIN__A 0xC2003C +-#define FE_AG_REG_IND_THD_LOL__A 0xC2003D +-#define FE_AG_REG_IND_THD_HIL__A 0xC2003E +-#define FE_AG_REG_IND_DEL__A 0xC2003F +-#define FE_AG_REG_IND_PD1_WRI__A 0xC20040 +-#define FE_AG_REG_PDA_AUR_CNT__A 0xC20041 +-#define FE_AG_REG_PDA_RUR_CNT__A 0xC20042 +-#define FE_AG_REG_PDA_AVE_DAT__A 0xC20043 +-#define FE_AG_REG_PDC_RUR_CNT__A 0xC20044 +-#define FE_AG_REG_PDC_SET_LVL__A 0xC20045 +-#define FE_AG_REG_PDC_FLA_RGN__A 0xC20046 +-#define FE_AG_REG_PDC_JMP_PSN__A 0xC20047 +-#define FE_AG_REG_PDC_FLA_STP__A 0xC20048 +-#define FE_AG_REG_PDC_SLO_STP__A 0xC20049 +-#define FE_AG_REG_PDC_PD2_WRI__A 0xC2004A +-#define FE_AG_REG_PDC_MAP_DAT__A 0xC2004B +-#define FE_AG_REG_PDC_MAX__A 0xC2004C +-#define FE_AG_REG_TGA_AUR_CNT__A 0xC2004D +-#define FE_AG_REG_TGA_RUR_CNT__A 0xC2004E +-#define FE_AG_REG_TGA_AVE_DAT__A 0xC2004F +-#define FE_AG_REG_TGC_RUR_CNT__A 0xC20050 +-#define FE_AG_REG_TGC_SET_LVL__A 0xC20051 +-#define FE_AG_REG_TGC_SET_LVL__M 0x3F +-#define FE_AG_REG_TGC_FLA_RGN__A 0xC20052 +-#define FE_AG_REG_TGC_JMP_PSN__A 0xC20053 +-#define FE_AG_REG_TGC_FLA_STP__A 0xC20054 +-#define FE_AG_REG_TGC_SLO_STP__A 0xC20055 +-#define FE_AG_REG_TGC_MAP_DAT__A 0xC20056 +-#define FE_AG_REG_FGA_AUR_CNT__A 0xC20057 +-#define FE_AG_REG_FGA_RUR_CNT__A 0xC20058 +-#define FE_AG_REG_FGM_WRI__A 0xC20061 +-#define FE_AG_REG_BGC_FGC_WRI__A 0xC20068 +-#define FE_AG_REG_BGC_CGC_WRI__A 0xC20069 +-#define FE_FS_REG_COMM_EXEC__A 0xC30000 +-#define FE_FS_REG_ADD_INC_LOP__A 0xC30010 +-#define FE_FD_REG_COMM_EXEC__A 0xC40000 +-#define FE_FD_REG_SCL__A 0xC40010 +-#define FE_FD_REG_MAX_LEV__A 0xC40011 +-#define FE_FD_REG_NR__A 0xC40012 +-#define FE_FD_REG_MEAS_VAL__A 0xC40014 +-#define FE_IF_REG_COMM_EXEC__A 0xC50000 +-#define FE_IF_REG_INCR0__A 0xC50010 +-#define FE_IF_REG_INCR0__W 16 +-#define FE_IF_REG_INCR0__M 0xFFFF +-#define FE_IF_REG_INCR1__A 0xC50011 +-#define FE_IF_REG_INCR1__M 0xFF +-#define FE_CF_REG_COMM_EXEC__A 0xC60000 +-#define FE_CF_REG_SCL__A 0xC60010 +-#define FE_CF_REG_MAX_LEV__A 0xC60011 +-#define FE_CF_REG_NR__A 0xC60012 +-#define FE_CF_REG_IMP_VAL__A 0xC60013 +-#define FE_CF_REG_MEAS_VAL__A 0xC60014 +-#define FE_CU_REG_COMM_EXEC__A 0xC70000 +-#define FE_CU_REG_FRM_CNT_RST__A 0xC70011 +-#define FE_CU_REG_FRM_CNT_STR__A 0xC70012 +-#define FT_COMM_EXEC__A 0x1000000 +-#define FT_REG_COMM_EXEC__A 0x1010000 +-#define CP_COMM_EXEC__A 0x1400000 +-#define CP_REG_COMM_EXEC__A 0x1410000 +-#define CP_REG_INTERVAL__A 0x1410011 +-#define CP_REG_BR_SPL_OFFSET__A 0x1410023 +-#define CP_REG_BR_STR_DEL__A 0x1410024 +-#define CP_REG_RT_ANG_INC0__A 0x1410030 +-#define CP_REG_RT_ANG_INC1__A 0x1410031 +-#define CP_REG_RT_DETECT_ENA__A 0x1410032 +-#define CP_REG_RT_DETECT_TRH__A 0x1410033 +-#define CP_REG_RT_EXP_MARG__A 0x141003E +-#define CP_REG_AC_NEXP_OFFS__A 0x1410040 +-#define CP_REG_AC_AVER_POW__A 0x1410041 +-#define CP_REG_AC_MAX_POW__A 0x1410042 +-#define CP_REG_AC_WEIGHT_MAN__A 0x1410043 +-#define CP_REG_AC_WEIGHT_EXP__A 0x1410044 +-#define CP_REG_AC_AMP_MODE__A 0x1410047 +-#define CP_REG_AC_AMP_FIX__A 0x1410048 +-#define CP_REG_AC_ANG_MODE__A 0x141004A +-#define CE_COMM_EXEC__A 0x1800000 +-#define CE_REG_COMM_EXEC__A 0x1810000 +-#define CE_REG_TAPSET__A 0x1810011 +-#define CE_REG_AVG_POW__A 0x1810012 +-#define CE_REG_MAX_POW__A 0x1810013 +-#define CE_REG_ATT__A 0x1810014 +-#define CE_REG_NRED__A 0x1810015 +-#define CE_REG_NE_ERR_SELECT__A 0x1810043 +-#define CE_REG_NE_TD_CAL__A 0x1810044 +-#define CE_REG_NE_MIXAVG__A 0x1810046 +-#define CE_REG_NE_NUPD_OFS__A 0x1810047 +-#define CE_REG_PE_NEXP_OFFS__A 0x1810050 +-#define CE_REG_PE_TIMESHIFT__A 0x1810051 +-#define CE_REG_TP_A0_TAP_NEW__A 0x1810064 +-#define CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 +-#define CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 +-#define CE_REG_TP_A1_TAP_NEW__A 0x1810068 +-#define CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 +-#define CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A +-#define CE_REG_TI_NEXP_OFFS__A 0x1810070 +-#define CE_REG_FI_SHT_INCR__A 0x1810090 +-#define CE_REG_FI_EXP_NORM__A 0x1810091 +-#define CE_REG_IR_INPUTSEL__A 0x18100A0 +-#define CE_REG_IR_STARTPOS__A 0x18100A1 +-#define CE_REG_IR_NEXP_THRES__A 0x18100A2 +-#define CE_REG_FR_TREAL00__A 0x1820010 +-#define CE_REG_FR_TIMAG00__A 0x1820011 +-#define CE_REG_FR_TREAL01__A 0x1820012 +-#define CE_REG_FR_TIMAG01__A 0x1820013 +-#define CE_REG_FR_TREAL02__A 0x1820014 +-#define CE_REG_FR_TIMAG02__A 0x1820015 +-#define CE_REG_FR_TREAL03__A 0x1820016 +-#define CE_REG_FR_TIMAG03__A 0x1820017 +-#define CE_REG_FR_TREAL04__A 0x1820018 +-#define CE_REG_FR_TIMAG04__A 0x1820019 +-#define CE_REG_FR_TREAL05__A 0x182001A +-#define CE_REG_FR_TIMAG05__A 0x182001B +-#define CE_REG_FR_TREAL06__A 0x182001C +-#define CE_REG_FR_TIMAG06__A 0x182001D +-#define CE_REG_FR_TREAL07__A 0x182001E +-#define CE_REG_FR_TIMAG07__A 0x182001F +-#define CE_REG_FR_TREAL08__A 0x1820020 +-#define CE_REG_FR_TIMAG08__A 0x1820021 +-#define CE_REG_FR_TREAL09__A 0x1820022 +-#define CE_REG_FR_TIMAG09__A 0x1820023 +-#define CE_REG_FR_TREAL10__A 0x1820024 +-#define CE_REG_FR_TIMAG10__A 0x1820025 +-#define CE_REG_FR_TREAL11__A 0x1820026 +-#define CE_REG_FR_TIMAG11__A 0x1820027 +-#define CE_REG_FR_MID_TAP__A 0x1820028 +-#define CE_REG_FR_SQS_G00__A 0x1820029 +-#define CE_REG_FR_SQS_G01__A 0x182002A +-#define CE_REG_FR_SQS_G02__A 0x182002B +-#define CE_REG_FR_SQS_G03__A 0x182002C +-#define CE_REG_FR_SQS_G04__A 0x182002D +-#define CE_REG_FR_SQS_G05__A 0x182002E +-#define CE_REG_FR_SQS_G06__A 0x182002F +-#define CE_REG_FR_SQS_G07__A 0x1820030 +-#define CE_REG_FR_SQS_G08__A 0x1820031 +-#define CE_REG_FR_SQS_G09__A 0x1820032 +-#define CE_REG_FR_SQS_G10__A 0x1820033 +-#define CE_REG_FR_SQS_G11__A 0x1820034 +-#define CE_REG_FR_SQS_G12__A 0x1820035 +-#define CE_REG_FR_RIO_G00__A 0x1820036 +-#define CE_REG_FR_RIO_G01__A 0x1820037 +-#define CE_REG_FR_RIO_G02__A 0x1820038 +-#define CE_REG_FR_RIO_G03__A 0x1820039 +-#define CE_REG_FR_RIO_G04__A 0x182003A +-#define CE_REG_FR_RIO_G05__A 0x182003B +-#define CE_REG_FR_RIO_G06__A 0x182003C +-#define CE_REG_FR_RIO_G07__A 0x182003D +-#define CE_REG_FR_RIO_G08__A 0x182003E +-#define CE_REG_FR_RIO_G09__A 0x182003F +-#define CE_REG_FR_RIO_G10__A 0x1820040 +-#define CE_REG_FR_MODE__A 0x1820041 +-#define CE_REG_FR_SQS_TRH__A 0x1820042 +-#define CE_REG_FR_RIO_GAIN__A 0x1820043 +-#define CE_REG_FR_BYPASS__A 0x1820044 +-#define CE_REG_FR_PM_SET__A 0x1820045 +-#define CE_REG_FR_ERR_SH__A 0x1820046 +-#define CE_REG_FR_MAN_SH__A 0x1820047 +-#define CE_REG_FR_TAP_SH__A 0x1820048 +-#define EQ_COMM_EXEC__A 0x1C00000 +-#define EQ_REG_COMM_EXEC__A 0x1C10000 +-#define EQ_REG_COMM_MB__A 0x1C10002 +-#define EQ_REG_IS_GAIN_MAN__A 0x1C10015 +-#define EQ_REG_IS_GAIN_EXP__A 0x1C10016 +-#define EQ_REG_IS_CLIP_EXP__A 0x1C10017 +-#define EQ_REG_SN_CEGAIN__A 0x1C1002A +-#define EQ_REG_SN_OFFSET__A 0x1C1002B +-#define EQ_REG_RC_SEL_CAR__A 0x1C10032 +-#define EQ_REG_RC_SEL_CAR_INIT 0x0 +-#define EQ_REG_RC_SEL_CAR_DIV_ON 0x1 +-#define EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 +-#define EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 +-#define EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 +-#define EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 +-#define EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 +-#define EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 +-#define EQ_REG_OT_CONST__A 0x1C10046 +-#define EQ_REG_OT_ALPHA__A 0x1C10047 +-#define EQ_REG_OT_QNT_THRES0__A 0x1C10048 +-#define EQ_REG_OT_QNT_THRES1__A 0x1C10049 +-#define EQ_REG_OT_CSI_STEP__A 0x1C1004A +-#define EQ_REG_OT_CSI_OFFSET__A 0x1C1004B +-#define EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 +-#define EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 +-#define EC_SB_REG_COMM_EXEC__A 0x2010000 +-#define EC_SB_REG_TR_MODE__A 0x2010010 +-#define EC_SB_REG_TR_MODE_8K 0x0 +-#define EC_SB_REG_TR_MODE_2K 0x1 +-#define EC_SB_REG_CONST__A 0x2010011 +-#define EC_SB_REG_CONST_QPSK 0x0 +-#define EC_SB_REG_CONST_16QAM 0x1 +-#define EC_SB_REG_CONST_64QAM 0x2 +-#define EC_SB_REG_ALPHA__A 0x2010012 +-#define EC_SB_REG_PRIOR__A 0x2010013 +-#define EC_SB_REG_PRIOR_HI 0x0 +-#define EC_SB_REG_PRIOR_LO 0x1 +-#define EC_SB_REG_CSI_HI__A 0x2010014 +-#define EC_SB_REG_CSI_LO__A 0x2010015 +-#define EC_SB_REG_SMB_TGL__A 0x2010016 +-#define EC_SB_REG_SNR_HI__A 0x2010017 +-#define EC_SB_REG_SNR_MID__A 0x2010018 +-#define EC_SB_REG_SNR_LO__A 0x2010019 +-#define EC_SB_REG_SCALE_MSB__A 0x201001A +-#define EC_SB_REG_SCALE_BIT2__A 0x201001B +-#define EC_SB_REG_SCALE_LSB__A 0x201001C +-#define EC_SB_REG_CSI_OFS__A 0x201001D +-#define EC_VD_REG_COMM_EXEC__A 0x2090000 +-#define EC_VD_REG_FORCE__A 0x2090010 +-#define EC_VD_REG_SET_CODERATE__A 0x2090011 +-#define EC_VD_REG_SET_CODERATE_C1_2 0x0 +-#define EC_VD_REG_SET_CODERATE_C2_3 0x1 +-#define EC_VD_REG_SET_CODERATE_C3_4 0x2 +-#define EC_VD_REG_SET_CODERATE_C5_6 0x3 +-#define EC_VD_REG_SET_CODERATE_C7_8 0x4 +-#define EC_VD_REG_REQ_SMB_CNT__A 0x2090012 +-#define EC_VD_REG_RLK_ENA__A 0x2090014 +-#define EC_OD_REG_COMM_EXEC__A 0x2110000 +-#define EC_OD_REG_SYNC__A 0x2110010 +-#define EC_OD_DEINT_RAM__A 0x2120000 +-#define EC_RS_REG_COMM_EXEC__A 0x2130000 +-#define EC_RS_REG_REQ_PCK_CNT__A 0x2130010 +-#define EC_RS_REG_VAL__A 0x2130011 +-#define EC_RS_REG_VAL_PCK 0x1 +-#define EC_RS_EC_RAM__A 0x2140000 +-#define EC_OC_REG_COMM_EXEC__A 0x2150000 +-#define EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 +-#define EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 +-#define EC_OC_REG_COMM_INT_STA__A 0x2150007 +-#define EC_OC_REG_OC_MODE_LOP__A 0x2150010 +-#define EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 +-#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 +-#define EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 +-#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 +-#define EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 +-#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 +-#define EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 +-#define EC_OC_REG_OC_MODE_HIP__A 0x2150011 +-#define EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 +-#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 +-#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 +-#define EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 +-#define EC_OC_REG_OC_MPG_SIO__A 0x2150012 +-#define EC_OC_REG_OC_MPG_SIO__M 0xFFF +-#define EC_OC_REG_OC_MON_SIO__A 0x2150013 +-#define EC_OC_REG_DTO_INC_LOP__A 0x2150014 +-#define EC_OC_REG_DTO_INC_HIP__A 0x2150015 +-#define EC_OC_REG_SNC_ISC_LVL__A 0x2150016 +-#define EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 +-#define EC_OC_REG_TMD_TOP_MODE__A 0x215001D +-#define EC_OC_REG_TMD_TOP_CNT__A 0x215001E +-#define EC_OC_REG_TMD_HIL_MAR__A 0x215001F +-#define EC_OC_REG_TMD_LOL_MAR__A 0x2150020 +-#define EC_OC_REG_TMD_CUR_CNT__A 0x2150021 +-#define EC_OC_REG_AVR_ASH_CNT__A 0x2150023 +-#define EC_OC_REG_AVR_BSH_CNT__A 0x2150024 +-#define EC_OC_REG_RCN_MODE__A 0x2150027 +-#define EC_OC_REG_RCN_CRA_LOP__A 0x2150028 +-#define EC_OC_REG_RCN_CRA_HIP__A 0x2150029 +-#define EC_OC_REG_RCN_CST_LOP__A 0x215002A +-#define EC_OC_REG_RCN_CST_HIP__A 0x215002B +-#define EC_OC_REG_RCN_SET_LVL__A 0x215002C +-#define EC_OC_REG_RCN_GAI_LVL__A 0x215002D +-#define EC_OC_REG_RCN_CLP_LOP__A 0x2150032 +-#define EC_OC_REG_RCN_CLP_HIP__A 0x2150033 +-#define EC_OC_REG_RCN_MAP_LOP__A 0x2150034 +-#define EC_OC_REG_RCN_MAP_HIP__A 0x2150035 +-#define EC_OC_REG_OCR_MPG_UOS__A 0x2150036 +-#define EC_OC_REG_OCR_MPG_UOS__M 0xFFF +-#define EC_OC_REG_OCR_MPG_UOS_INIT 0x0 +-#define EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 +-#define EC_OC_REG_OCR_MON_UOS__A 0x2150039 +-#define EC_OC_REG_OCR_MON_UOS_DAT_0_ENABLE 0x1 +-#define EC_OC_REG_OCR_MON_UOS_DAT_1_ENABLE 0x2 +-#define EC_OC_REG_OCR_MON_UOS_DAT_2_ENABLE 0x4 +-#define EC_OC_REG_OCR_MON_UOS_DAT_3_ENABLE 0x8 +-#define EC_OC_REG_OCR_MON_UOS_DAT_4_ENABLE 0x10 +-#define EC_OC_REG_OCR_MON_UOS_DAT_5_ENABLE 0x20 +-#define EC_OC_REG_OCR_MON_UOS_DAT_6_ENABLE 0x40 +-#define EC_OC_REG_OCR_MON_UOS_DAT_7_ENABLE 0x80 +-#define EC_OC_REG_OCR_MON_UOS_DAT_8_ENABLE 0x100 +-#define EC_OC_REG_OCR_MON_UOS_DAT_9_ENABLE 0x200 +-#define EC_OC_REG_OCR_MON_UOS_VAL_ENABLE 0x400 +-#define EC_OC_REG_OCR_MON_UOS_CLK_ENABLE 0x800 +-#define EC_OC_REG_OCR_MON_WRI__A 0x215003A +-#define EC_OC_REG_OCR_MON_WRI_INIT 0x0 +-#define EC_OC_REG_IPR_INV_MPG__A 0x2150045 +-#define CC_REG_OSC_MODE__A 0x2410010 +-#define CC_REG_OSC_MODE_M20 0x1 +-#define CC_REG_PLL_MODE__A 0x2410011 +-#define CC_REG_PLL_MODE_BYPASS_PLL 0x1 +-#define CC_REG_PLL_MODE_PUMP_CUR_12 0x14 +-#define CC_REG_REF_DIVIDE__A 0x2410012 +-#define CC_REG_PWD_MODE__A 0x2410015 +-#define CC_REG_PWD_MODE_DOWN_PLL 0x2 +-#define CC_REG_UPDATE__A 0x2410017 +-#define CC_REG_UPDATE_KEY 0x3973 +-#define CC_REG_JTAGID_L__A 0x2410019 +-#define LC_COMM_EXEC__A 0x2800000 +-#define LC_RA_RAM_IFINCR_NOM_L__A 0x282000C +-#define LC_RA_RAM_FILTER_SYM_SET__A 0x282001A +-#define LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 +-#define LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 +-#define LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 +-#define LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 +-#define LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 +-#define LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 +-#define LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 +-#define LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 +-#define LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 +-#define B_HI_COMM_EXEC__A 0x400000 +-#define B_HI_COMM_MB__A 0x400002 +-#define B_HI_CT_REG_COMM_STATE__A 0x410001 +-#define B_HI_RA_RAM_SRV_RES__A 0x420031 +-#define B_HI_RA_RAM_SRV_CMD__A 0x420032 +-#define B_HI_RA_RAM_SRV_CMD_RESET 0x2 +-#define B_HI_RA_RAM_SRV_CMD_CONFIG 0x3 +-#define B_HI_RA_RAM_SRV_CMD_EXECUTE 0x6 +-#define B_HI_RA_RAM_SRV_RST_KEY__A 0x420033 +-#define B_HI_RA_RAM_SRV_RST_KEY_ACT 0x3973 +-#define B_HI_RA_RAM_SRV_CFG_KEY__A 0x420033 +-#define B_HI_RA_RAM_SRV_CFG_DIV__A 0x420034 +-#define B_HI_RA_RAM_SRV_CFG_BDL__A 0x420035 +-#define B_HI_RA_RAM_SRV_CFG_WUP__A 0x420036 +-#define B_HI_RA_RAM_SRV_CFG_ACT__A 0x420037 +-#define B_HI_RA_RAM_SRV_CFG_ACT_SLV0_ON 0x1 +-#define B_HI_RA_RAM_SRV_CFG_ACT_BRD__M 0x4 +-#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_OFF 0x0 +-#define B_HI_RA_RAM_SRV_CFG_ACT_BRD_ON 0x4 +-#define B_HI_RA_RAM_SRV_CFG_ACT_PWD_EXE 0x8 +-#define B_HI_RA_RAM_USR_BEGIN__A 0x420040 +-#define B_HI_IF_RAM_TRP_BPT0__AX 0x430000 +-#define B_HI_IF_RAM_USR_BEGIN__A 0x430200 +-#define B_SC_COMM_EXEC__A 0x800000 +-#define B_SC_COMM_EXEC_CTL_STOP 0x0 +-#define B_SC_COMM_STATE__A 0x800001 +-#define B_SC_RA_RAM_PARAM0__A 0x820040 +-#define B_SC_RA_RAM_PARAM1__A 0x820041 +-#define B_SC_RA_RAM_CMD_ADDR__A 0x820042 +-#define B_SC_RA_RAM_CMD__A 0x820043 +-#define B_SC_RA_RAM_CMD_PROC_START 0x1 +-#define B_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +-#define B_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +-#define B_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +-#define B_SC_RA_RAM_LOCKTRACK_MIN 0x1 +-#define B_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +-#define B_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +-#define B_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +-#define B_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +-#define B_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +-#define B_SC_RA_RAM_OP_PARAM_GUARD_4 0xC +-#define B_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +-#define B_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +-#define B_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +-#define B_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +-#define B_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +-#define B_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +-#define B_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +-#define B_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +-#define B_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +-#define B_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +-#define B_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +-#define B_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +-#define B_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +-#define B_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +-#define B_SC_RA_RAM_OP_AUTO_MODE__M 0x1 +-#define B_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +-#define B_SC_RA_RAM_OP_AUTO_CONST__M 0x4 +-#define B_SC_RA_RAM_OP_AUTO_HIER__M 0x8 +-#define B_SC_RA_RAM_OP_AUTO_RATE__M 0x10 +-#define B_SC_RA_RAM_LOCK__A 0x82004B +-#define B_SC_RA_RAM_LOCK_DEMOD__M 0x1 +-#define B_SC_RA_RAM_LOCK_FEC__M 0x2 +-#define B_SC_RA_RAM_LOCK_MPEG__M 0x4 +-#define B_SC_RA_RAM_BE_OPT_ENA__A 0x82004C +-#define B_SC_RA_RAM_BE_OPT_ENA_CP_OPT 0x1 +-#define B_SC_RA_RAM_BE_OPT_DELAY__A 0x82004D +-#define B_SC_RA_RAM_CONFIG__A 0x820050 +-#define B_SC_RA_RAM_CONFIG_FR_ENABLE__M 0x4 +-#define B_SC_RA_RAM_CONFIG_FREQSCAN__M 0x10 +-#define B_SC_RA_RAM_CONFIG_SLAVE__M 0x20 +-#define B_SC_RA_RAM_CONFIG_DIV_BLANK_ENABLE__M 0x200 +-#define B_SC_RA_RAM_CONFIG_DIV_ECHO_ENABLE__M 0x400 +-#define B_SC_RA_RAM_CO_TD_CAL_2K__A 0x82005D +-#define B_SC_RA_RAM_CO_TD_CAL_8K__A 0x82005E +-#define B_SC_RA_RAM_IF_SAVE__AX 0x82008E +-#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_32__A 0x820098 +-#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_16__A 0x820099 +-#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_8__A 0x82009A +-#define B_SC_RA_RAM_DIVERSITY_DELAY_2K_4__A 0x82009B +-#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_32__A 0x82009C +-#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_16__A 0x82009D +-#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_8__A 0x82009E +-#define B_SC_RA_RAM_DIVERSITY_DELAY_8K_4__A 0x82009F +-#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__A 0x8200D1 +-#define B_SC_RA_RAM_IR_COARSE_2K_LENGTH__PRE 0x9 +-#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__A 0x8200D2 +-#define B_SC_RA_RAM_IR_COARSE_2K_FREQINC__PRE 0x4 +-#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__A 0x8200D3 +-#define B_SC_RA_RAM_IR_COARSE_2K_KAISINC__PRE 0x100 +-#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__A 0x8200D4 +-#define B_SC_RA_RAM_IR_COARSE_8K_LENGTH__PRE 0x8 +-#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__A 0x8200D5 +-#define B_SC_RA_RAM_IR_COARSE_8K_FREQINC__PRE 0x8 +-#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__A 0x8200D6 +-#define B_SC_RA_RAM_IR_COARSE_8K_KAISINC__PRE 0x200 +-#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__A 0x8200D7 +-#define B_SC_RA_RAM_IR_FINE_2K_LENGTH__PRE 0x9 +-#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__A 0x8200D8 +-#define B_SC_RA_RAM_IR_FINE_2K_FREQINC__PRE 0x4 +-#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__A 0x8200D9 +-#define B_SC_RA_RAM_IR_FINE_2K_KAISINC__PRE 0x100 +-#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__A 0x8200DA +-#define B_SC_RA_RAM_IR_FINE_8K_LENGTH__PRE 0xB +-#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__A 0x8200DB +-#define B_SC_RA_RAM_IR_FINE_8K_FREQINC__PRE 0x1 +-#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__A 0x8200DC +-#define B_SC_RA_RAM_IR_FINE_8K_KAISINC__PRE 0x40 +-#define B_SC_RA_RAM_ECHO_SHIFT_LIM__A 0x8200DD +-#define B_SC_RA_RAM_SAMPLE_RATE_COUNT__A 0x8200E8 +-#define B_SC_RA_RAM_SAMPLE_RATE_STEP__A 0x8200E9 +-#define B_SC_RA_RAM_BAND__A 0x8200EC +-#define B_SC_RA_RAM_LC_ABS_2K__A 0x8200F4 +-#define B_SC_RA_RAM_LC_ABS_2K__PRE 0x1F +-#define B_SC_RA_RAM_LC_ABS_8K__A 0x8200F5 +-#define B_SC_RA_RAM_LC_ABS_8K__PRE 0x1F +-#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_MAN__PRE 0x100 +-#define B_SC_RA_RAM_EQ_IS_GAIN_UNKNOWN_EXP__PRE 0x4 +-#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_MAN__PRE 0x1E2 +-#define B_SC_RA_RAM_EQ_IS_GAIN_QPSK_EXP__PRE 0x4 +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_MAN__PRE 0x10D +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_EXP__PRE 0x5 +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_MAN__PRE 0x17D +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A2_EXP__PRE 0x4 +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_MAN__PRE 0x133 +-#define B_SC_RA_RAM_EQ_IS_GAIN_16QAM_A4_EXP__PRE 0x5 +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_MAN__PRE 0x114 +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_EXP__PRE 0x5 +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_MAN__PRE 0x14A +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A2_EXP__PRE 0x4 +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_MAN__PRE 0x1BB +-#define B_SC_RA_RAM_EQ_IS_GAIN_64QAM_A4_EXP__PRE 0x4 +-#define B_SC_RA_RAM_DRIVER_VERSION__AX 0x8201FE +-#define B_SC_RA_RAM_PROC_LOCKTRACK 0x0 +-#define B_FE_COMM_EXEC__A 0xC00000 +-#define B_FE_AD_REG_COMM_EXEC__A 0xC10000 +-#define B_FE_AD_REG_FDB_IN__A 0xC10012 +-#define B_FE_AD_REG_PD__A 0xC10013 +-#define B_FE_AD_REG_INVEXT__A 0xC10014 +-#define B_FE_AD_REG_CLKNEG__A 0xC10015 +-#define B_FE_AG_REG_COMM_EXEC__A 0xC20000 +-#define B_FE_AG_REG_AG_MODE_LOP__A 0xC20010 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_4__M 0x10 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_STATIC 0x0 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_4_DYNAMIC 0x10 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_5__M 0x20 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_5_STATIC 0x0 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_C__M 0x1000 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_STATIC 0x0 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_C_DYNAMIC 0x1000 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_E__M 0x4000 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_STATIC 0x0 +-#define B_FE_AG_REG_AG_MODE_LOP_MODE_E_DYNAMIC 0x4000 +-#define B_FE_AG_REG_AG_MODE_HIP__A 0xC20011 +-#define B_FE_AG_REG_AG_MODE_HIP_MODE_J__M 0x8 +-#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_STATIC 0x0 +-#define B_FE_AG_REG_AG_MODE_HIP_MODE_J_DYNAMIC 0x8 +-#define B_FE_AG_REG_AG_PGA_MODE__A 0xC20012 +-#define B_FE_AG_REG_AG_PGA_MODE_PFY_PCY_AFY_REN 0x0 +-#define B_FE_AG_REG_AG_PGA_MODE_PFN_PCN_AFY_REN 0x1 +-#define B_FE_AG_REG_AG_AGC_SIO__A 0xC20013 +-#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2__M 0x2 +-#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_OUTPUT 0x0 +-#define B_FE_AG_REG_AG_AGC_SIO_AGC_SIO_2_INPUT 0x2 +-#define B_FE_AG_REG_AG_PWD__A 0xC20015 +-#define B_FE_AG_REG_AG_PWD_PWD_PD2__M 0x2 +-#define B_FE_AG_REG_AG_PWD_PWD_PD2_DISABLE 0x0 +-#define B_FE_AG_REG_AG_PWD_PWD_PD2_ENABLE 0x2 +-#define B_FE_AG_REG_DCE_AUR_CNT__A 0xC20016 +-#define B_FE_AG_REG_DCE_RUR_CNT__A 0xC20017 +-#define B_FE_AG_REG_ACE_AUR_CNT__A 0xC2001A +-#define B_FE_AG_REG_ACE_RUR_CNT__A 0xC2001B +-#define B_FE_AG_REG_CDR_RUR_CNT__A 0xC20020 +-#define B_FE_AG_REG_EGC_RUR_CNT__A 0xC20024 +-#define B_FE_AG_REG_EGC_SET_LVL__A 0xC20025 +-#define B_FE_AG_REG_EGC_SET_LVL__M 0x1FF +-#define B_FE_AG_REG_EGC_FLA_RGN__A 0xC20026 +-#define B_FE_AG_REG_EGC_SLO_RGN__A 0xC20027 +-#define B_FE_AG_REG_EGC_JMP_PSN__A 0xC20028 +-#define B_FE_AG_REG_EGC_FLA_INC__A 0xC20029 +-#define B_FE_AG_REG_EGC_FLA_DEC__A 0xC2002A +-#define B_FE_AG_REG_EGC_SLO_INC__A 0xC2002B +-#define B_FE_AG_REG_EGC_SLO_DEC__A 0xC2002C +-#define B_FE_AG_REG_EGC_FAS_INC__A 0xC2002D +-#define B_FE_AG_REG_EGC_FAS_DEC__A 0xC2002E +-#define B_FE_AG_REG_PM1_AGC_WRI__A 0xC20030 +-#define B_FE_AG_REG_PM1_AGC_WRI__M 0x7FF +-#define B_FE_AG_REG_GC1_AGC_RIC__A 0xC20031 +-#define B_FE_AG_REG_GC1_AGC_OFF__A 0xC20032 +-#define B_FE_AG_REG_GC1_AGC_MAX__A 0xC20033 +-#define B_FE_AG_REG_GC1_AGC_MIN__A 0xC20034 +-#define B_FE_AG_REG_GC1_AGC_DAT__A 0xC20035 +-#define B_FE_AG_REG_GC1_AGC_DAT__M 0x3FF +-#define B_FE_AG_REG_PM2_AGC_WRI__A 0xC20036 +-#define B_FE_AG_REG_IND_WIN__A 0xC2003C +-#define B_FE_AG_REG_IND_THD_LOL__A 0xC2003D +-#define B_FE_AG_REG_IND_THD_HIL__A 0xC2003E +-#define B_FE_AG_REG_IND_DEL__A 0xC2003F +-#define B_FE_AG_REG_IND_PD1_WRI__A 0xC20040 +-#define B_FE_AG_REG_PDA_AUR_CNT__A 0xC20041 +-#define B_FE_AG_REG_PDA_RUR_CNT__A 0xC20042 +-#define B_FE_AG_REG_PDA_AVE_DAT__A 0xC20043 +-#define B_FE_AG_REG_PDC_RUR_CNT__A 0xC20044 +-#define B_FE_AG_REG_PDC_SET_LVL__A 0xC20045 +-#define B_FE_AG_REG_PDC_FLA_RGN__A 0xC20046 +-#define B_FE_AG_REG_PDC_JMP_PSN__A 0xC20047 +-#define B_FE_AG_REG_PDC_FLA_STP__A 0xC20048 +-#define B_FE_AG_REG_PDC_SLO_STP__A 0xC20049 +-#define B_FE_AG_REG_PDC_PD2_WRI__A 0xC2004A +-#define B_FE_AG_REG_PDC_MAP_DAT__A 0xC2004B +-#define B_FE_AG_REG_PDC_MAX__A 0xC2004C +-#define B_FE_AG_REG_TGA_AUR_CNT__A 0xC2004D +-#define B_FE_AG_REG_TGA_RUR_CNT__A 0xC2004E +-#define B_FE_AG_REG_TGA_AVE_DAT__A 0xC2004F +-#define B_FE_AG_REG_TGC_RUR_CNT__A 0xC20050 +-#define B_FE_AG_REG_TGC_SET_LVL__A 0xC20051 +-#define B_FE_AG_REG_TGC_SET_LVL__M 0x3F +-#define B_FE_AG_REG_TGC_FLA_RGN__A 0xC20052 +-#define B_FE_AG_REG_TGC_JMP_PSN__A 0xC20053 +-#define B_FE_AG_REG_TGC_FLA_STP__A 0xC20054 +-#define B_FE_AG_REG_TGC_SLO_STP__A 0xC20055 +-#define B_FE_AG_REG_TGC_MAP_DAT__A 0xC20056 +-#define B_FE_AG_REG_FGM_WRI__A 0xC20061 +-#define B_FE_AG_REG_BGC_FGC_WRI__A 0xC20068 +-#define B_FE_AG_REG_BGC_CGC_WRI__A 0xC20069 +-#define B_FE_FS_REG_COMM_EXEC__A 0xC30000 +-#define B_FE_FS_REG_ADD_INC_LOP__A 0xC30010 +-#define B_FE_FD_REG_COMM_EXEC__A 0xC40000 +-#define B_FE_FD_REG_SCL__A 0xC40010 +-#define B_FE_FD_REG_MAX_LEV__A 0xC40011 +-#define B_FE_FD_REG_NR__A 0xC40012 +-#define B_FE_FD_REG_MEAS_VAL__A 0xC40014 +-#define B_FE_IF_REG_COMM_EXEC__A 0xC50000 +-#define B_FE_IF_REG_INCR0__A 0xC50010 +-#define B_FE_IF_REG_INCR0__W 16 +-#define B_FE_IF_REG_INCR0__M 0xFFFF +-#define B_FE_IF_REG_INCR1__A 0xC50011 +-#define B_FE_IF_REG_INCR1__M 0xFF +-#define B_FE_CF_REG_COMM_EXEC__A 0xC60000 +-#define B_FE_CF_REG_SCL__A 0xC60010 +-#define B_FE_CF_REG_MAX_LEV__A 0xC60011 +-#define B_FE_CF_REG_NR__A 0xC60012 +-#define B_FE_CF_REG_IMP_VAL__A 0xC60013 +-#define B_FE_CF_REG_MEAS_VAL__A 0xC60014 +-#define B_FE_CU_REG_COMM_EXEC__A 0xC70000 +-#define B_FE_CU_REG_FRM_CNT_RST__A 0xC70011 +-#define B_FE_CU_REG_FRM_CNT_STR__A 0xC70012 +-#define B_FE_CU_REG_CTR_NFC_ICR__A 0xC70020 +-#define B_FE_CU_REG_CTR_NFC_OCR__A 0xC70021 +-#define B_FE_CU_REG_DIV_NFC_CLP__A 0xC70027 +-#define B_FT_COMM_EXEC__A 0x1000000 +-#define B_FT_REG_COMM_EXEC__A 0x1010000 +-#define B_CP_COMM_EXEC__A 0x1400000 +-#define B_CP_REG_COMM_EXEC__A 0x1410000 +-#define B_CP_REG_INTERVAL__A 0x1410011 +-#define B_CP_REG_BR_SPL_OFFSET__A 0x1410023 +-#define B_CP_REG_BR_STR_DEL__A 0x1410024 +-#define B_CP_REG_RT_ANG_INC0__A 0x1410030 +-#define B_CP_REG_RT_ANG_INC1__A 0x1410031 +-#define B_CP_REG_RT_DETECT_TRH__A 0x1410033 +-#define B_CP_REG_AC_NEXP_OFFS__A 0x1410040 +-#define B_CP_REG_AC_AVER_POW__A 0x1410041 +-#define B_CP_REG_AC_MAX_POW__A 0x1410042 +-#define B_CP_REG_AC_WEIGHT_MAN__A 0x1410043 +-#define B_CP_REG_AC_WEIGHT_EXP__A 0x1410044 +-#define B_CP_REG_AC_AMP_MODE__A 0x1410047 +-#define B_CP_REG_AC_AMP_FIX__A 0x1410048 +-#define B_CP_REG_AC_ANG_MODE__A 0x141004A +-#define B_CE_COMM_EXEC__A 0x1800000 +-#define B_CE_REG_COMM_EXEC__A 0x1810000 +-#define B_CE_REG_TAPSET__A 0x1810011 +-#define B_CE_REG_AVG_POW__A 0x1810012 +-#define B_CE_REG_MAX_POW__A 0x1810013 +-#define B_CE_REG_ATT__A 0x1810014 +-#define B_CE_REG_NRED__A 0x1810015 +-#define B_CE_REG_NE_ERR_SELECT__A 0x1810043 +-#define B_CE_REG_NE_TD_CAL__A 0x1810044 +-#define B_CE_REG_NE_MIXAVG__A 0x1810046 +-#define B_CE_REG_NE_NUPD_OFS__A 0x1810047 +-#define B_CE_REG_PE_NEXP_OFFS__A 0x1810050 +-#define B_CE_REG_PE_TIMESHIFT__A 0x1810051 +-#define B_CE_REG_TP_A0_TAP_NEW__A 0x1810064 +-#define B_CE_REG_TP_A0_TAP_NEW_VALID__A 0x1810065 +-#define B_CE_REG_TP_A0_MU_LMS_STEP__A 0x1810066 +-#define B_CE_REG_TP_A1_TAP_NEW__A 0x1810068 +-#define B_CE_REG_TP_A1_TAP_NEW_VALID__A 0x1810069 +-#define B_CE_REG_TP_A1_MU_LMS_STEP__A 0x181006A +-#define B_CE_REG_TI_PHN_ENABLE__A 0x1810073 +-#define B_CE_REG_FI_SHT_INCR__A 0x1810090 +-#define B_CE_REG_FI_EXP_NORM__A 0x1810091 +-#define B_CE_REG_IR_INPUTSEL__A 0x18100A0 +-#define B_CE_REG_IR_STARTPOS__A 0x18100A1 +-#define B_CE_REG_IR_NEXP_THRES__A 0x18100A2 +-#define B_CE_REG_FR_TREAL00__A 0x1820010 +-#define B_CE_REG_FR_TIMAG00__A 0x1820011 +-#define B_CE_REG_FR_TREAL01__A 0x1820012 +-#define B_CE_REG_FR_TIMAG01__A 0x1820013 +-#define B_CE_REG_FR_TREAL02__A 0x1820014 +-#define B_CE_REG_FR_TIMAG02__A 0x1820015 +-#define B_CE_REG_FR_TREAL03__A 0x1820016 +-#define B_CE_REG_FR_TIMAG03__A 0x1820017 +-#define B_CE_REG_FR_TREAL04__A 0x1820018 +-#define B_CE_REG_FR_TIMAG04__A 0x1820019 +-#define B_CE_REG_FR_TREAL05__A 0x182001A +-#define B_CE_REG_FR_TIMAG05__A 0x182001B +-#define B_CE_REG_FR_TREAL06__A 0x182001C +-#define B_CE_REG_FR_TIMAG06__A 0x182001D +-#define B_CE_REG_FR_TREAL07__A 0x182001E +-#define B_CE_REG_FR_TIMAG07__A 0x182001F +-#define B_CE_REG_FR_TREAL08__A 0x1820020 +-#define B_CE_REG_FR_TIMAG08__A 0x1820021 +-#define B_CE_REG_FR_TREAL09__A 0x1820022 +-#define B_CE_REG_FR_TIMAG09__A 0x1820023 +-#define B_CE_REG_FR_TREAL10__A 0x1820024 +-#define B_CE_REG_FR_TIMAG10__A 0x1820025 +-#define B_CE_REG_FR_TREAL11__A 0x1820026 +-#define B_CE_REG_FR_TIMAG11__A 0x1820027 +-#define B_CE_REG_FR_MID_TAP__A 0x1820028 +-#define B_CE_REG_FR_SQS_G00__A 0x1820029 +-#define B_CE_REG_FR_SQS_G01__A 0x182002A +-#define B_CE_REG_FR_SQS_G02__A 0x182002B +-#define B_CE_REG_FR_SQS_G03__A 0x182002C +-#define B_CE_REG_FR_SQS_G04__A 0x182002D +-#define B_CE_REG_FR_SQS_G05__A 0x182002E +-#define B_CE_REG_FR_SQS_G06__A 0x182002F +-#define B_CE_REG_FR_SQS_G07__A 0x1820030 +-#define B_CE_REG_FR_SQS_G08__A 0x1820031 +-#define B_CE_REG_FR_SQS_G09__A 0x1820032 +-#define B_CE_REG_FR_SQS_G10__A 0x1820033 +-#define B_CE_REG_FR_SQS_G11__A 0x1820034 +-#define B_CE_REG_FR_SQS_G12__A 0x1820035 +-#define B_CE_REG_FR_RIO_G00__A 0x1820036 +-#define B_CE_REG_FR_RIO_G01__A 0x1820037 +-#define B_CE_REG_FR_RIO_G02__A 0x1820038 +-#define B_CE_REG_FR_RIO_G03__A 0x1820039 +-#define B_CE_REG_FR_RIO_G04__A 0x182003A +-#define B_CE_REG_FR_RIO_G05__A 0x182003B +-#define B_CE_REG_FR_RIO_G06__A 0x182003C +-#define B_CE_REG_FR_RIO_G07__A 0x182003D +-#define B_CE_REG_FR_RIO_G08__A 0x182003E +-#define B_CE_REG_FR_RIO_G09__A 0x182003F +-#define B_CE_REG_FR_RIO_G10__A 0x1820040 +-#define B_CE_REG_FR_MODE__A 0x1820041 +-#define B_CE_REG_FR_SQS_TRH__A 0x1820042 +-#define B_CE_REG_FR_RIO_GAIN__A 0x1820043 +-#define B_CE_REG_FR_BYPASS__A 0x1820044 +-#define B_CE_REG_FR_PM_SET__A 0x1820045 +-#define B_CE_REG_FR_ERR_SH__A 0x1820046 +-#define B_CE_REG_FR_MAN_SH__A 0x1820047 +-#define B_CE_REG_FR_TAP_SH__A 0x1820048 +-#define B_EQ_COMM_EXEC__A 0x1C00000 +-#define B_EQ_REG_COMM_EXEC__A 0x1C10000 +-#define B_EQ_REG_COMM_MB__A 0x1C10002 +-#define B_EQ_REG_IS_GAIN_MAN__A 0x1C10015 +-#define B_EQ_REG_IS_GAIN_EXP__A 0x1C10016 +-#define B_EQ_REG_IS_CLIP_EXP__A 0x1C10017 +-#define B_EQ_REG_SN_CEGAIN__A 0x1C1002A +-#define B_EQ_REG_SN_OFFSET__A 0x1C1002B +-#define B_EQ_REG_RC_SEL_CAR__A 0x1C10032 +-#define B_EQ_REG_RC_SEL_CAR_INIT 0x2 +-#define B_EQ_REG_RC_SEL_CAR_DIV_ON 0x1 +-#define B_EQ_REG_RC_SEL_CAR_PASS_A_CC 0x0 +-#define B_EQ_REG_RC_SEL_CAR_PASS_B_CE 0x2 +-#define B_EQ_REG_RC_SEL_CAR_LOCAL_A_CC 0x0 +-#define B_EQ_REG_RC_SEL_CAR_LOCAL_B_CE 0x8 +-#define B_EQ_REG_RC_SEL_CAR_MEAS_A_CC 0x0 +-#define B_EQ_REG_RC_SEL_CAR_MEAS_B_CE 0x20 +-#define B_EQ_REG_RC_SEL_CAR_FFTMODE__M 0x80 +-#define B_EQ_REG_OT_CONST__A 0x1C10046 +-#define B_EQ_REG_OT_ALPHA__A 0x1C10047 +-#define B_EQ_REG_OT_QNT_THRES0__A 0x1C10048 +-#define B_EQ_REG_OT_QNT_THRES1__A 0x1C10049 +-#define B_EQ_REG_OT_CSI_STEP__A 0x1C1004A +-#define B_EQ_REG_OT_CSI_OFFSET__A 0x1C1004B +-#define B_EQ_REG_TD_REQ_SMB_CNT__A 0x1C10061 +-#define B_EQ_REG_TD_TPS_PWR_OFS__A 0x1C10062 +-#define B_EC_SB_REG_COMM_EXEC__A 0x2010000 +-#define B_EC_SB_REG_TR_MODE__A 0x2010010 +-#define B_EC_SB_REG_TR_MODE_8K 0x0 +-#define B_EC_SB_REG_TR_MODE_2K 0x1 +-#define B_EC_SB_REG_CONST__A 0x2010011 +-#define B_EC_SB_REG_CONST_QPSK 0x0 +-#define B_EC_SB_REG_CONST_16QAM 0x1 +-#define B_EC_SB_REG_CONST_64QAM 0x2 +-#define B_EC_SB_REG_ALPHA__A 0x2010012 +-#define B_EC_SB_REG_PRIOR__A 0x2010013 +-#define B_EC_SB_REG_PRIOR_HI 0x0 +-#define B_EC_SB_REG_PRIOR_LO 0x1 +-#define B_EC_SB_REG_CSI_HI__A 0x2010014 +-#define B_EC_SB_REG_CSI_LO__A 0x2010015 +-#define B_EC_SB_REG_SMB_TGL__A 0x2010016 +-#define B_EC_SB_REG_SNR_HI__A 0x2010017 +-#define B_EC_SB_REG_SNR_MID__A 0x2010018 +-#define B_EC_SB_REG_SNR_LO__A 0x2010019 +-#define B_EC_SB_REG_SCALE_MSB__A 0x201001A +-#define B_EC_SB_REG_SCALE_BIT2__A 0x201001B +-#define B_EC_SB_REG_SCALE_LSB__A 0x201001C +-#define B_EC_SB_REG_CSI_OFS0__A 0x201001D +-#define B_EC_SB_REG_CSI_OFS1__A 0x201001E +-#define B_EC_SB_REG_CSI_OFS2__A 0x201001F +-#define B_EC_VD_REG_COMM_EXEC__A 0x2090000 +-#define B_EC_VD_REG_FORCE__A 0x2090010 +-#define B_EC_VD_REG_SET_CODERATE__A 0x2090011 +-#define B_EC_VD_REG_SET_CODERATE_C1_2 0x0 +-#define B_EC_VD_REG_SET_CODERATE_C2_3 0x1 +-#define B_EC_VD_REG_SET_CODERATE_C3_4 0x2 +-#define B_EC_VD_REG_SET_CODERATE_C5_6 0x3 +-#define B_EC_VD_REG_SET_CODERATE_C7_8 0x4 +-#define B_EC_VD_REG_REQ_SMB_CNT__A 0x2090012 +-#define B_EC_VD_REG_RLK_ENA__A 0x2090014 +-#define B_EC_OD_REG_COMM_EXEC__A 0x2110000 +-#define B_EC_OD_REG_SYNC__A 0x2110664 +-#define B_EC_OD_DEINT_RAM__A 0x2120000 +-#define B_EC_RS_REG_COMM_EXEC__A 0x2130000 +-#define B_EC_RS_REG_REQ_PCK_CNT__A 0x2130010 +-#define B_EC_RS_REG_VAL__A 0x2130011 +-#define B_EC_RS_REG_VAL_PCK 0x1 +-#define B_EC_RS_EC_RAM__A 0x2140000 +-#define B_EC_OC_REG_COMM_EXEC__A 0x2150000 +-#define B_EC_OC_REG_COMM_EXEC_CTL_ACTIVE 0x1 +-#define B_EC_OC_REG_COMM_EXEC_CTL_HOLD 0x2 +-#define B_EC_OC_REG_COMM_INT_STA__A 0x2150007 +-#define B_EC_OC_REG_OC_MODE_LOP__A 0x2150010 +-#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA__M 0x1 +-#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_ENABLE 0x0 +-#define B_EC_OC_REG_OC_MODE_LOP_PAR_ENA_DISABLE 0x1 +-#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC__M 0x4 +-#define B_EC_OC_REG_OC_MODE_LOP_DTO_CTR_SRC_STATIC 0x0 +-#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE__M 0x80 +-#define B_EC_OC_REG_OC_MODE_LOP_MPG_TRM_MDE_SERIAL 0x80 +-#define B_EC_OC_REG_OC_MODE_HIP__A 0x2150011 +-#define B_EC_OC_REG_OC_MODE_HIP_MPG_BUS_SRC_MONITOR 0x10 +-#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL__M 0x200 +-#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_DISABLE 0x0 +-#define B_EC_OC_REG_OC_MODE_HIP_MPG_PAR_VAL_ENABLE 0x200 +-#define B_EC_OC_REG_OC_MPG_SIO__A 0x2150012 +-#define B_EC_OC_REG_OC_MPG_SIO__M 0xFFF +-#define B_EC_OC_REG_DTO_INC_LOP__A 0x2150014 +-#define B_EC_OC_REG_DTO_INC_HIP__A 0x2150015 +-#define B_EC_OC_REG_SNC_ISC_LVL__A 0x2150016 +-#define B_EC_OC_REG_SNC_ISC_LVL_OSC__M 0xF0 +-#define B_EC_OC_REG_TMD_TOP_MODE__A 0x215001D +-#define B_EC_OC_REG_TMD_TOP_CNT__A 0x215001E +-#define B_EC_OC_REG_TMD_HIL_MAR__A 0x215001F +-#define B_EC_OC_REG_TMD_LOL_MAR__A 0x2150020 +-#define B_EC_OC_REG_TMD_CUR_CNT__A 0x2150021 +-#define B_EC_OC_REG_AVR_ASH_CNT__A 0x2150023 +-#define B_EC_OC_REG_AVR_BSH_CNT__A 0x2150024 +-#define B_EC_OC_REG_RCN_MODE__A 0x2150027 +-#define B_EC_OC_REG_RCN_CRA_LOP__A 0x2150028 +-#define B_EC_OC_REG_RCN_CRA_HIP__A 0x2150029 +-#define B_EC_OC_REG_RCN_CST_LOP__A 0x215002A +-#define B_EC_OC_REG_RCN_CST_HIP__A 0x215002B +-#define B_EC_OC_REG_RCN_SET_LVL__A 0x215002C +-#define B_EC_OC_REG_RCN_GAI_LVL__A 0x215002D +-#define B_EC_OC_REG_RCN_CLP_LOP__A 0x2150032 +-#define B_EC_OC_REG_RCN_CLP_HIP__A 0x2150033 +-#define B_EC_OC_REG_RCN_MAP_LOP__A 0x2150034 +-#define B_EC_OC_REG_RCN_MAP_HIP__A 0x2150035 +-#define B_EC_OC_REG_OCR_MPG_UOS__A 0x2150036 +-#define B_EC_OC_REG_OCR_MPG_UOS__M 0xFFF +-#define B_EC_OC_REG_OCR_MPG_UOS_INIT 0x0 +-#define B_EC_OC_REG_OCR_MPG_USR_DAT__A 0x2150038 +-#define B_EC_OC_REG_IPR_INV_MPG__A 0x2150045 +-#define B_EC_OC_REG_DTO_CLKMODE__A 0x2150047 +-#define B_EC_OC_REG_DTO_PER__A 0x2150048 +-#define B_EC_OC_REG_DTO_BUR__A 0x2150049 +-#define B_EC_OC_REG_RCR_CLKMODE__A 0x215004A +-#define B_CC_REG_OSC_MODE__A 0x2410010 +-#define B_CC_REG_OSC_MODE_M20 0x1 +-#define B_CC_REG_PLL_MODE__A 0x2410011 +-#define B_CC_REG_PLL_MODE_BYPASS_PLL 0x1 +-#define B_CC_REG_PLL_MODE_PUMP_CUR_12 0x14 +-#define B_CC_REG_REF_DIVIDE__A 0x2410012 +-#define B_CC_REG_PWD_MODE__A 0x2410015 +-#define B_CC_REG_PWD_MODE_DOWN_PLL 0x2 +-#define B_CC_REG_UPDATE__A 0x2410017 +-#define B_CC_REG_UPDATE_KEY 0x3973 +-#define B_CC_REG_JTAGID_L__A 0x2410019 +-#define B_CC_REG_DIVERSITY__A 0x241001B +-#define B_LC_COMM_EXEC__A 0x2800000 +-#define B_LC_RA_RAM_IFINCR_NOM_L__A 0x282000C +-#define B_LC_RA_RAM_FILTER_SYM_SET__A 0x282001A +-#define B_LC_RA_RAM_FILTER_SYM_SET__PRE 0x3E8 +-#define B_LC_RA_RAM_FILTER_CRMM_A__A 0x2820060 +-#define B_LC_RA_RAM_FILTER_CRMM_A__PRE 0x4 +-#define B_LC_RA_RAM_FILTER_CRMM_B__A 0x2820061 +-#define B_LC_RA_RAM_FILTER_CRMM_B__PRE 0x1 +-#define B_LC_RA_RAM_FILTER_SRMM_A__A 0x2820068 +-#define B_LC_RA_RAM_FILTER_SRMM_A__PRE 0x4 +-#define B_LC_RA_RAM_FILTER_SRMM_B__A 0x2820069 +-#define B_LC_RA_RAM_FILTER_SRMM_B__PRE 0x1 +- +-#endif +diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h +deleted file mode 100644 +index 0209818..0000000 +--- a/drivers/media/dvb/frontends/drxk.h ++++ /dev/null +@@ -1,50 +0,0 @@ +-#ifndef _DRXK_H_ +-#define _DRXK_H_ +- +-#include +-#include +- +-/** +- * struct drxk_config - Configure the initial parameters for DRX-K +- * +- * adr: I2C Address of the DRX-K +- * parallel_ts: true means that the device uses parallel TS, +- * Serial otherwise. +- * single_master: Device is on the single master mode +- * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner +- * antenna_gpio: GPIO bit used to control the antenna +- * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1 +- * means that 1=DVBC, 0 = DVBT. Zero means the opposite. +- * microcode_name: Name of the firmware file with the microcode +- * +- * On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is +- * UIO-3. +- */ +-struct drxk_config { +- u8 adr; +- bool single_master; +- bool no_i2c_bridge; +- bool parallel_ts; +- +- bool antenna_dvbt; +- u16 antenna_gpio; +- +- int chunk_size; +- +- const char *microcode_name; +-}; +- +-#if defined(CONFIG_DVB_DRXK) || (defined(CONFIG_DVB_DRXK_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *drxk_attach(const struct drxk_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *drxk_attach(const struct drxk_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c +deleted file mode 100644 +index 3cce362..0000000 +--- a/drivers/media/dvb/frontends/drxk_hard.c ++++ /dev/null +@@ -1,6446 +0,0 @@ +-/* +- * drxk_hard: DRX-K DVB-C/T demodulator driver +- * +- * Copyright (C) 2010-2011 Digital Devices GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "drxk.h" +-#include "drxk_hard.h" +- +-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode); +-static int PowerDownQAM(struct drxk_state *state); +-static int SetDVBTStandard(struct drxk_state *state, +- enum OperationMode oMode); +-static int SetQAMStandard(struct drxk_state *state, +- enum OperationMode oMode); +-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, +- s32 tunerFreqOffset); +-static int SetDVBTStandard(struct drxk_state *state, +- enum OperationMode oMode); +-static int DVBTStart(struct drxk_state *state); +-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz, +- s32 tunerFreqOffset); +-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus); +-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus); +-static int SwitchAntennaToQAM(struct drxk_state *state); +-static int SwitchAntennaToDVBT(struct drxk_state *state); +- +-static bool IsDVBT(struct drxk_state *state) +-{ +- return state->m_OperationMode == OM_DVBT; +-} +- +-static bool IsQAM(struct drxk_state *state) +-{ +- return state->m_OperationMode == OM_QAM_ITU_A || +- state->m_OperationMode == OM_QAM_ITU_B || +- state->m_OperationMode == OM_QAM_ITU_C; +-} +- +-bool IsA1WithPatchCode(struct drxk_state *state) +-{ +- return state->m_DRXK_A1_PATCH_CODE; +-} +- +-bool IsA1WithRomCode(struct drxk_state *state) +-{ +- return state->m_DRXK_A1_ROM_CODE; +-} +- +-#define NOA1ROM 0 +- +-#define DRXDAP_FASI_SHORT_FORMAT(addr) (((addr) & 0xFC30FF80) == 0) +-#define DRXDAP_FASI_LONG_FORMAT(addr) (((addr) & 0xFC30FF80) != 0) +- +-#define DEFAULT_MER_83 165 +-#define DEFAULT_MER_93 250 +- +-#ifndef DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH +-#define DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH (0x02) +-#endif +- +-#ifndef DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH +-#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03) +-#endif +- +-#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH +-#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06) +-#endif +- +-#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700 +-#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500 +- +-#ifndef DRXK_KI_RAGC_ATV +-#define DRXK_KI_RAGC_ATV 4 +-#endif +-#ifndef DRXK_KI_IAGC_ATV +-#define DRXK_KI_IAGC_ATV 6 +-#endif +-#ifndef DRXK_KI_DAGC_ATV +-#define DRXK_KI_DAGC_ATV 7 +-#endif +- +-#ifndef DRXK_KI_RAGC_QAM +-#define DRXK_KI_RAGC_QAM 3 +-#endif +-#ifndef DRXK_KI_IAGC_QAM +-#define DRXK_KI_IAGC_QAM 4 +-#endif +-#ifndef DRXK_KI_DAGC_QAM +-#define DRXK_KI_DAGC_QAM 7 +-#endif +-#ifndef DRXK_KI_RAGC_DVBT +-#define DRXK_KI_RAGC_DVBT (IsA1WithPatchCode(state) ? 3 : 2) +-#endif +-#ifndef DRXK_KI_IAGC_DVBT +-#define DRXK_KI_IAGC_DVBT (IsA1WithPatchCode(state) ? 4 : 2) +-#endif +-#ifndef DRXK_KI_DAGC_DVBT +-#define DRXK_KI_DAGC_DVBT (IsA1WithPatchCode(state) ? 10 : 7) +-#endif +- +-#ifndef DRXK_AGC_DAC_OFFSET +-#define DRXK_AGC_DAC_OFFSET (0x800) +-#endif +- +-#ifndef DRXK_BANDWIDTH_8MHZ_IN_HZ +-#define DRXK_BANDWIDTH_8MHZ_IN_HZ (0x8B8249L) +-#endif +- +-#ifndef DRXK_BANDWIDTH_7MHZ_IN_HZ +-#define DRXK_BANDWIDTH_7MHZ_IN_HZ (0x7A1200L) +-#endif +- +-#ifndef DRXK_BANDWIDTH_6MHZ_IN_HZ +-#define DRXK_BANDWIDTH_6MHZ_IN_HZ (0x68A1B6L) +-#endif +- +-#ifndef DRXK_QAM_SYMBOLRATE_MAX +-#define DRXK_QAM_SYMBOLRATE_MAX (7233000) +-#endif +- +-#define DRXK_BL_ROM_OFFSET_TAPS_DVBT 56 +-#define DRXK_BL_ROM_OFFSET_TAPS_ITU_A 64 +-#define DRXK_BL_ROM_OFFSET_TAPS_ITU_C 0x5FE0 +-#define DRXK_BL_ROM_OFFSET_TAPS_BG 24 +-#define DRXK_BL_ROM_OFFSET_TAPS_DKILLP 32 +-#define DRXK_BL_ROM_OFFSET_TAPS_NTSC 40 +-#define DRXK_BL_ROM_OFFSET_TAPS_FM 48 +-#define DRXK_BL_ROM_OFFSET_UCODE 0 +- +-#define DRXK_BLC_TIMEOUT 100 +- +-#define DRXK_BLCC_NR_ELEMENTS_TAPS 2 +-#define DRXK_BLCC_NR_ELEMENTS_UCODE 6 +- +-#define DRXK_BLDC_NR_ELEMENTS_TAPS 28 +- +-#ifndef DRXK_OFDM_NE_NOTCH_WIDTH +-#define DRXK_OFDM_NE_NOTCH_WIDTH (4) +-#endif +- +-#define DRXK_QAM_SL_SIG_POWER_QAM16 (40960) +-#define DRXK_QAM_SL_SIG_POWER_QAM32 (20480) +-#define DRXK_QAM_SL_SIG_POWER_QAM64 (43008) +-#define DRXK_QAM_SL_SIG_POWER_QAM128 (20992) +-#define DRXK_QAM_SL_SIG_POWER_QAM256 (43520) +- +-static unsigned int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable debug messages"); +- +-#define dprintk(level, fmt, arg...) do { \ +-if (debug >= level) \ +- printk(KERN_DEBUG "drxk: %s" fmt, __func__, ## arg); \ +-} while (0) +- +- +-static inline u32 MulDiv32(u32 a, u32 b, u32 c) +-{ +- u64 tmp64; +- +- tmp64 = (u64) a * (u64) b; +- do_div(tmp64, c); +- +- return (u32) tmp64; +-} +- +-inline u32 Frac28a(u32 a, u32 c) +-{ +- int i = 0; +- u32 Q1 = 0; +- u32 R0 = 0; +- +- R0 = (a % c) << 4; /* 32-28 == 4 shifts possible at max */ +- Q1 = a / c; /* integer part, only the 4 least significant bits +- will be visible in the result */ +- +- /* division using radix 16, 7 nibbles in the result */ +- for (i = 0; i < 7; i++) { +- Q1 = (Q1 << 4) | (R0 / c); +- R0 = (R0 % c) << 4; +- } +- /* rounding */ +- if ((R0 >> 3) >= c) +- Q1++; +- +- return Q1; +-} +- +-static u32 Log10Times100(u32 x) +-{ +- static const u8 scale = 15; +- static const u8 indexWidth = 5; +- u8 i = 0; +- u32 y = 0; +- u32 d = 0; +- u32 k = 0; +- u32 r = 0; +- /* +- log2lut[n] = (1< 0; k--) { +- if (x & (((u32) 1) << scale)) +- break; +- x <<= 1; +- } +- } else { +- for (k = scale; k < 31; k++) { +- if ((x & (((u32) (-1)) << (scale + 1))) == 0) +- break; +- x >>= 1; +- } +- } +- /* +- Now x has binary point between bit[scale] and bit[scale-1] +- and 1.0 <= x < 2.0 */ +- +- /* correction for divison: log(x) = log(x/y)+log(y) */ +- y = k * ((((u32) 1) << scale) * 200); +- +- /* remove integer part */ +- x &= ((((u32) 1) << scale) - 1); +- /* get index */ +- i = (u8) (x >> (scale - indexWidth)); +- /* compute delta (x - a) */ +- d = x & ((((u32) 1) << (scale - indexWidth)) - 1); +- /* compute log, multiplication (d* (..)) must be within range ! */ +- y += log2lut[i] + +- ((d * (log2lut[i + 1] - log2lut[i])) >> (scale - indexWidth)); +- /* Conver to log10() */ +- y /= 108853; /* (log2(10) << scale) */ +- r = (y >> 1); +- /* rounding */ +- if (y & ((u32) 1)) +- r++; +- return r; +-} +- +-/****************************************************************************/ +-/* I2C **********************************************************************/ +-/****************************************************************************/ +- +-static int i2c_read1(struct i2c_adapter *adapter, u8 adr, u8 *val) +-{ +- struct i2c_msg msgs[1] = { {.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1} +- }; +- +- return i2c_transfer(adapter, msgs, 1); +-} +- +-static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +-{ +- int status; +- struct i2c_msg msg = { +- .addr = adr, .flags = 0, .buf = data, .len = len }; +- +- dprintk(3, ":"); +- if (debug > 2) { +- int i; +- for (i = 0; i < len; i++) +- printk(KERN_CONT " %02x", data[i]); +- printk(KERN_CONT "\n"); +- } +- status = i2c_transfer(adap, &msg, 1); +- if (status >= 0 && status != 1) +- status = -EIO; +- +- if (status < 0) +- printk(KERN_ERR "drxk: i2c write error at addr 0x%02x\n", adr); +- +- return status; +-} +- +-static int i2c_read(struct i2c_adapter *adap, +- u8 adr, u8 *msg, int len, u8 *answ, int alen) +-{ +- int status; +- struct i2c_msg msgs[2] = { +- {.addr = adr, .flags = 0, +- .buf = msg, .len = len}, +- {.addr = adr, .flags = I2C_M_RD, +- .buf = answ, .len = alen} +- }; +- +- status = i2c_transfer(adap, msgs, 2); +- if (status != 2) { +- if (debug > 2) +- printk(KERN_CONT ": ERROR!\n"); +- if (status >= 0) +- status = -EIO; +- +- printk(KERN_ERR "drxk: i2c read error at addr 0x%02x\n", adr); +- return status; +- } +- if (debug > 2) { +- int i; +- dprintk(2, ": read from"); +- for (i = 0; i < len; i++) +- printk(KERN_CONT " %02x", msg[i]); +- printk(KERN_CONT ", value = "); +- for (i = 0; i < alen; i++) +- printk(KERN_CONT " %02x", answ[i]); +- printk(KERN_CONT "\n"); +- } +- return 0; +-} +- +-static int read16_flags(struct drxk_state *state, u32 reg, u16 *data, u8 flags) +-{ +- int status; +- u8 adr = state->demod_address, mm1[4], mm2[2], len; +- +- if (state->single_master) +- flags |= 0xC0; +- +- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { +- mm1[0] = (((reg << 1) & 0xFF) | 0x01); +- mm1[1] = ((reg >> 16) & 0xFF); +- mm1[2] = ((reg >> 24) & 0xFF) | flags; +- mm1[3] = ((reg >> 7) & 0xFF); +- len = 4; +- } else { +- mm1[0] = ((reg << 1) & 0xFF); +- mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); +- len = 2; +- } +- dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); +- status = i2c_read(state->i2c, adr, mm1, len, mm2, 2); +- if (status < 0) +- return status; +- if (data) +- *data = mm2[0] | (mm2[1] << 8); +- +- return 0; +-} +- +-static int read16(struct drxk_state *state, u32 reg, u16 *data) +-{ +- return read16_flags(state, reg, data, 0); +-} +- +-static int read32_flags(struct drxk_state *state, u32 reg, u32 *data, u8 flags) +-{ +- int status; +- u8 adr = state->demod_address, mm1[4], mm2[4], len; +- +- if (state->single_master) +- flags |= 0xC0; +- +- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { +- mm1[0] = (((reg << 1) & 0xFF) | 0x01); +- mm1[1] = ((reg >> 16) & 0xFF); +- mm1[2] = ((reg >> 24) & 0xFF) | flags; +- mm1[3] = ((reg >> 7) & 0xFF); +- len = 4; +- } else { +- mm1[0] = ((reg << 1) & 0xFF); +- mm1[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); +- len = 2; +- } +- dprintk(2, "(0x%08x, 0x%02x)\n", reg, flags); +- status = i2c_read(state->i2c, adr, mm1, len, mm2, 4); +- if (status < 0) +- return status; +- if (data) +- *data = mm2[0] | (mm2[1] << 8) | +- (mm2[2] << 16) | (mm2[3] << 24); +- +- return 0; +-} +- +-static int read32(struct drxk_state *state, u32 reg, u32 *data) +-{ +- return read32_flags(state, reg, data, 0); +-} +- +-static int write16_flags(struct drxk_state *state, u32 reg, u16 data, u8 flags) +-{ +- u8 adr = state->demod_address, mm[6], len; +- +- if (state->single_master) +- flags |= 0xC0; +- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { +- mm[0] = (((reg << 1) & 0xFF) | 0x01); +- mm[1] = ((reg >> 16) & 0xFF); +- mm[2] = ((reg >> 24) & 0xFF) | flags; +- mm[3] = ((reg >> 7) & 0xFF); +- len = 4; +- } else { +- mm[0] = ((reg << 1) & 0xFF); +- mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); +- len = 2; +- } +- mm[len] = data & 0xff; +- mm[len + 1] = (data >> 8) & 0xff; +- +- dprintk(2, "(0x%08x, 0x%04x, 0x%02x)\n", reg, data, flags); +- return i2c_write(state->i2c, adr, mm, len + 2); +-} +- +-static int write16(struct drxk_state *state, u32 reg, u16 data) +-{ +- return write16_flags(state, reg, data, 0); +-} +- +-static int write32_flags(struct drxk_state *state, u32 reg, u32 data, u8 flags) +-{ +- u8 adr = state->demod_address, mm[8], len; +- +- if (state->single_master) +- flags |= 0xC0; +- if (DRXDAP_FASI_LONG_FORMAT(reg) || (flags != 0)) { +- mm[0] = (((reg << 1) & 0xFF) | 0x01); +- mm[1] = ((reg >> 16) & 0xFF); +- mm[2] = ((reg >> 24) & 0xFF) | flags; +- mm[3] = ((reg >> 7) & 0xFF); +- len = 4; +- } else { +- mm[0] = ((reg << 1) & 0xFF); +- mm[1] = (((reg >> 16) & 0x0F) | ((reg >> 18) & 0xF0)); +- len = 2; +- } +- mm[len] = data & 0xff; +- mm[len + 1] = (data >> 8) & 0xff; +- mm[len + 2] = (data >> 16) & 0xff; +- mm[len + 3] = (data >> 24) & 0xff; +- dprintk(2, "(0x%08x, 0x%08x, 0x%02x)\n", reg, data, flags); +- +- return i2c_write(state->i2c, adr, mm, len + 4); +-} +- +-static int write32(struct drxk_state *state, u32 reg, u32 data) +-{ +- return write32_flags(state, reg, data, 0); +-} +- +-static int write_block(struct drxk_state *state, u32 Address, +- const int BlockSize, const u8 pBlock[]) +-{ +- int status = 0, BlkSize = BlockSize; +- u8 Flags = 0; +- +- if (state->single_master) +- Flags |= 0xC0; +- +- while (BlkSize > 0) { +- int Chunk = BlkSize > state->m_ChunkSize ? +- state->m_ChunkSize : BlkSize; +- u8 *AdrBuf = &state->Chunk[0]; +- u32 AdrLength = 0; +- +- if (DRXDAP_FASI_LONG_FORMAT(Address) || (Flags != 0)) { +- AdrBuf[0] = (((Address << 1) & 0xFF) | 0x01); +- AdrBuf[1] = ((Address >> 16) & 0xFF); +- AdrBuf[2] = ((Address >> 24) & 0xFF); +- AdrBuf[3] = ((Address >> 7) & 0xFF); +- AdrBuf[2] |= Flags; +- AdrLength = 4; +- if (Chunk == state->m_ChunkSize) +- Chunk -= 2; +- } else { +- AdrBuf[0] = ((Address << 1) & 0xFF); +- AdrBuf[1] = (((Address >> 16) & 0x0F) | +- ((Address >> 18) & 0xF0)); +- AdrLength = 2; +- } +- memcpy(&state->Chunk[AdrLength], pBlock, Chunk); +- dprintk(2, "(0x%08x, 0x%02x)\n", Address, Flags); +- if (debug > 1) { +- int i; +- if (pBlock) +- for (i = 0; i < Chunk; i++) +- printk(KERN_CONT " %02x", pBlock[i]); +- printk(KERN_CONT "\n"); +- } +- status = i2c_write(state->i2c, state->demod_address, +- &state->Chunk[0], Chunk + AdrLength); +- if (status < 0) { +- printk(KERN_ERR "drxk: %s: i2c write error at addr 0x%02x\n", +- __func__, Address); +- break; +- } +- pBlock += Chunk; +- Address += (Chunk >> 1); +- BlkSize -= Chunk; +- } +- return status; +-} +- +-#ifndef DRXK_MAX_RETRIES_POWERUP +-#define DRXK_MAX_RETRIES_POWERUP 20 +-#endif +- +-int PowerUpDevice(struct drxk_state *state) +-{ +- int status; +- u8 data = 0; +- u16 retryCount = 0; +- +- dprintk(1, "\n"); +- +- status = i2c_read1(state->i2c, state->demod_address, &data); +- if (status < 0) { +- do { +- data = 0; +- status = i2c_write(state->i2c, state->demod_address, +- &data, 1); +- msleep(10); +- retryCount++; +- if (status < 0) +- continue; +- status = i2c_read1(state->i2c, state->demod_address, +- &data); +- } while (status < 0 && +- (retryCount < DRXK_MAX_RETRIES_POWERUP)); +- if (status < 0 && retryCount >= DRXK_MAX_RETRIES_POWERUP) +- goto error; +- } +- +- /* Make sure all clk domains are active */ +- status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_NONE); +- if (status < 0) +- goto error; +- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); +- if (status < 0) +- goto error; +- /* Enable pll lock tests */ +- status = write16(state, SIO_CC_PLL_LOCK__A, 1); +- if (status < 0) +- goto error; +- +- state->m_currentPowerMode = DRX_POWER_UP; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +- +-static int init_state(struct drxk_state *state) +-{ +- /* +- * FIXME: most (all?) of the values bellow should be moved into +- * struct drxk_config, as they are probably board-specific +- */ +- u32 ulVSBIfAgcMode = DRXK_AGC_CTRL_AUTO; +- u32 ulVSBIfAgcOutputLevel = 0; +- u32 ulVSBIfAgcMinLevel = 0; +- u32 ulVSBIfAgcMaxLevel = 0x7FFF; +- u32 ulVSBIfAgcSpeed = 3; +- +- u32 ulVSBRfAgcMode = DRXK_AGC_CTRL_AUTO; +- u32 ulVSBRfAgcOutputLevel = 0; +- u32 ulVSBRfAgcMinLevel = 0; +- u32 ulVSBRfAgcMaxLevel = 0x7FFF; +- u32 ulVSBRfAgcSpeed = 3; +- u32 ulVSBRfAgcTop = 9500; +- u32 ulVSBRfAgcCutOffCurrent = 4000; +- +- u32 ulATVIfAgcMode = DRXK_AGC_CTRL_AUTO; +- u32 ulATVIfAgcOutputLevel = 0; +- u32 ulATVIfAgcMinLevel = 0; +- u32 ulATVIfAgcMaxLevel = 0; +- u32 ulATVIfAgcSpeed = 3; +- +- u32 ulATVRfAgcMode = DRXK_AGC_CTRL_OFF; +- u32 ulATVRfAgcOutputLevel = 0; +- u32 ulATVRfAgcMinLevel = 0; +- u32 ulATVRfAgcMaxLevel = 0; +- u32 ulATVRfAgcTop = 9500; +- u32 ulATVRfAgcCutOffCurrent = 4000; +- u32 ulATVRfAgcSpeed = 3; +- +- u32 ulQual83 = DEFAULT_MER_83; +- u32 ulQual93 = DEFAULT_MER_93; +- +- u32 ulDVBTStaticTSClock = 1; +- u32 ulDVBCStaticTSClock = 1; +- +- u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; +- u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; +- +- /* io_pad_cfg register (8 bit reg.) MSB bit is 1 (default value) */ +- /* io_pad_cfg_mode output mode is drive always */ +- /* io_pad_cfg_drive is set to power 2 (23 mA) */ +- u32 ulGPIOCfg = 0x0113; +- u32 ulInvertTSClock = 0; +- u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH; +- u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH; +- u32 ulDVBTBitrate = 50000000; +- u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8; +- +- u32 ulInsertRSByte = 0; +- +- u32 ulRfMirror = 1; +- u32 ulPowerDown = 0; +- +- dprintk(1, "\n"); +- +- state->m_hasLNA = false; +- state->m_hasDVBT = false; +- state->m_hasDVBC = false; +- state->m_hasATV = false; +- state->m_hasOOB = false; +- state->m_hasAudio = false; +- +- if (!state->m_ChunkSize) +- state->m_ChunkSize = 124; +- +- state->m_oscClockFreq = 0; +- state->m_smartAntInverted = false; +- state->m_bPDownOpenBridge = false; +- +- /* real system clock frequency in kHz */ +- state->m_sysClockFreq = 151875; +- /* Timing div, 250ns/Psys */ +- /* Timing div, = (delay (nano seconds) * sysclk (kHz))/ 1000 */ +- state->m_HICfgTimingDiv = ((state->m_sysClockFreq / 1000) * +- HI_I2C_DELAY) / 1000; +- /* Clipping */ +- if (state->m_HICfgTimingDiv > SIO_HI_RA_RAM_PAR_2_CFG_DIV__M) +- state->m_HICfgTimingDiv = SIO_HI_RA_RAM_PAR_2_CFG_DIV__M; +- state->m_HICfgWakeUpKey = (state->demod_address << 1); +- /* port/bridge/power down ctrl */ +- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; +- +- state->m_bPowerDown = (ulPowerDown != 0); +- +- state->m_DRXK_A1_PATCH_CODE = false; +- state->m_DRXK_A1_ROM_CODE = false; +- state->m_DRXK_A2_ROM_CODE = false; +- state->m_DRXK_A3_ROM_CODE = false; +- state->m_DRXK_A2_PATCH_CODE = false; +- state->m_DRXK_A3_PATCH_CODE = false; +- +- /* Init AGC and PGA parameters */ +- /* VSB IF */ +- state->m_vsbIfAgcCfg.ctrlMode = (ulVSBIfAgcMode); +- state->m_vsbIfAgcCfg.outputLevel = (ulVSBIfAgcOutputLevel); +- state->m_vsbIfAgcCfg.minOutputLevel = (ulVSBIfAgcMinLevel); +- state->m_vsbIfAgcCfg.maxOutputLevel = (ulVSBIfAgcMaxLevel); +- state->m_vsbIfAgcCfg.speed = (ulVSBIfAgcSpeed); +- state->m_vsbPgaCfg = 140; +- +- /* VSB RF */ +- state->m_vsbRfAgcCfg.ctrlMode = (ulVSBRfAgcMode); +- state->m_vsbRfAgcCfg.outputLevel = (ulVSBRfAgcOutputLevel); +- state->m_vsbRfAgcCfg.minOutputLevel = (ulVSBRfAgcMinLevel); +- state->m_vsbRfAgcCfg.maxOutputLevel = (ulVSBRfAgcMaxLevel); +- state->m_vsbRfAgcCfg.speed = (ulVSBRfAgcSpeed); +- state->m_vsbRfAgcCfg.top = (ulVSBRfAgcTop); +- state->m_vsbRfAgcCfg.cutOffCurrent = (ulVSBRfAgcCutOffCurrent); +- state->m_vsbPreSawCfg.reference = 0x07; +- state->m_vsbPreSawCfg.usePreSaw = true; +- +- state->m_Quality83percent = DEFAULT_MER_83; +- state->m_Quality93percent = DEFAULT_MER_93; +- if (ulQual93 <= 500 && ulQual83 < ulQual93) { +- state->m_Quality83percent = ulQual83; +- state->m_Quality93percent = ulQual93; +- } +- +- /* ATV IF */ +- state->m_atvIfAgcCfg.ctrlMode = (ulATVIfAgcMode); +- state->m_atvIfAgcCfg.outputLevel = (ulATVIfAgcOutputLevel); +- state->m_atvIfAgcCfg.minOutputLevel = (ulATVIfAgcMinLevel); +- state->m_atvIfAgcCfg.maxOutputLevel = (ulATVIfAgcMaxLevel); +- state->m_atvIfAgcCfg.speed = (ulATVIfAgcSpeed); +- +- /* ATV RF */ +- state->m_atvRfAgcCfg.ctrlMode = (ulATVRfAgcMode); +- state->m_atvRfAgcCfg.outputLevel = (ulATVRfAgcOutputLevel); +- state->m_atvRfAgcCfg.minOutputLevel = (ulATVRfAgcMinLevel); +- state->m_atvRfAgcCfg.maxOutputLevel = (ulATVRfAgcMaxLevel); +- state->m_atvRfAgcCfg.speed = (ulATVRfAgcSpeed); +- state->m_atvRfAgcCfg.top = (ulATVRfAgcTop); +- state->m_atvRfAgcCfg.cutOffCurrent = (ulATVRfAgcCutOffCurrent); +- state->m_atvPreSawCfg.reference = 0x04; +- state->m_atvPreSawCfg.usePreSaw = true; +- +- +- /* DVBT RF */ +- state->m_dvbtRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF; +- state->m_dvbtRfAgcCfg.outputLevel = 0; +- state->m_dvbtRfAgcCfg.minOutputLevel = 0; +- state->m_dvbtRfAgcCfg.maxOutputLevel = 0xFFFF; +- state->m_dvbtRfAgcCfg.top = 0x2100; +- state->m_dvbtRfAgcCfg.cutOffCurrent = 4000; +- state->m_dvbtRfAgcCfg.speed = 1; +- +- +- /* DVBT IF */ +- state->m_dvbtIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO; +- state->m_dvbtIfAgcCfg.outputLevel = 0; +- state->m_dvbtIfAgcCfg.minOutputLevel = 0; +- state->m_dvbtIfAgcCfg.maxOutputLevel = 9000; +- state->m_dvbtIfAgcCfg.top = 13424; +- state->m_dvbtIfAgcCfg.cutOffCurrent = 0; +- state->m_dvbtIfAgcCfg.speed = 3; +- state->m_dvbtIfAgcCfg.FastClipCtrlDelay = 30; +- state->m_dvbtIfAgcCfg.IngainTgtMax = 30000; +- /* state->m_dvbtPgaCfg = 140; */ +- +- state->m_dvbtPreSawCfg.reference = 4; +- state->m_dvbtPreSawCfg.usePreSaw = false; +- +- /* QAM RF */ +- state->m_qamRfAgcCfg.ctrlMode = DRXK_AGC_CTRL_OFF; +- state->m_qamRfAgcCfg.outputLevel = 0; +- state->m_qamRfAgcCfg.minOutputLevel = 6023; +- state->m_qamRfAgcCfg.maxOutputLevel = 27000; +- state->m_qamRfAgcCfg.top = 0x2380; +- state->m_qamRfAgcCfg.cutOffCurrent = 4000; +- state->m_qamRfAgcCfg.speed = 3; +- +- /* QAM IF */ +- state->m_qamIfAgcCfg.ctrlMode = DRXK_AGC_CTRL_AUTO; +- state->m_qamIfAgcCfg.outputLevel = 0; +- state->m_qamIfAgcCfg.minOutputLevel = 0; +- state->m_qamIfAgcCfg.maxOutputLevel = 9000; +- state->m_qamIfAgcCfg.top = 0x0511; +- state->m_qamIfAgcCfg.cutOffCurrent = 0; +- state->m_qamIfAgcCfg.speed = 3; +- state->m_qamIfAgcCfg.IngainTgtMax = 5119; +- state->m_qamIfAgcCfg.FastClipCtrlDelay = 50; +- +- state->m_qamPgaCfg = 140; +- state->m_qamPreSawCfg.reference = 4; +- state->m_qamPreSawCfg.usePreSaw = false; +- +- state->m_OperationMode = OM_NONE; +- state->m_DrxkState = DRXK_UNINITIALIZED; +- +- /* MPEG output configuration */ +- state->m_enableMPEGOutput = true; /* If TRUE; enable MPEG ouput */ +- state->m_insertRSByte = false; /* If TRUE; insert RS byte */ +- state->m_invertDATA = false; /* If TRUE; invert DATA signals */ +- state->m_invertERR = false; /* If TRUE; invert ERR signal */ +- state->m_invertSTR = false; /* If TRUE; invert STR signals */ +- state->m_invertVAL = false; /* If TRUE; invert VAL signals */ +- state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */ +- state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0); +- state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0); +- /* If TRUE; static MPEG clockrate will be used; +- otherwise clockrate will adapt to the bitrate of the TS */ +- +- state->m_DVBTBitrate = ulDVBTBitrate; +- state->m_DVBCBitrate = ulDVBCBitrate; +- +- state->m_TSDataStrength = (ulTSDataStrength & 0x07); +- state->m_TSClockkStrength = (ulTSClockkStrength & 0x07); +- +- /* Maximum bitrate in b/s in case static clockrate is selected */ +- state->m_mpegTsStaticBitrate = 19392658; +- state->m_disableTEIhandling = false; +- +- if (ulInsertRSByte) +- state->m_insertRSByte = true; +- +- state->m_MpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT; +- if (ulMpegLockTimeOut < 10000) +- state->m_MpegLockTimeOut = ulMpegLockTimeOut; +- state->m_DemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT; +- if (ulDemodLockTimeOut < 10000) +- state->m_DemodLockTimeOut = ulDemodLockTimeOut; +- +- /* QAM defaults */ +- state->m_Constellation = DRX_CONSTELLATION_AUTO; +- state->m_qamInterleaveMode = DRXK_QAM_I12_J17; +- state->m_fecRsPlen = 204 * 8; /* fecRsPlen annex A */ +- state->m_fecRsPrescale = 1; +- +- state->m_sqiSpeed = DRXK_DVBT_SQI_SPEED_MEDIUM; +- state->m_agcFastClipCtrlDelay = 0; +- +- state->m_GPIOCfg = (ulGPIOCfg); +- +- state->m_bPowerDown = false; +- state->m_currentPowerMode = DRX_POWER_DOWN; +- +- state->m_rfmirror = (ulRfMirror == 0); +- state->m_IfAgcPol = false; +- return 0; +-} +- +-static int DRXX_Open(struct drxk_state *state) +-{ +- int status = 0; +- u32 jtag = 0; +- u16 bid = 0; +- u16 key = 0; +- +- dprintk(1, "\n"); +- /* stop lock indicator process */ +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- /* Check device id */ +- status = read16(state, SIO_TOP_COMM_KEY__A, &key); +- if (status < 0) +- goto error; +- status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); +- if (status < 0) +- goto error; +- status = read32(state, SIO_TOP_JTAGID_LO__A, &jtag); +- if (status < 0) +- goto error; +- status = read16(state, SIO_PDR_UIO_IN_HI__A, &bid); +- if (status < 0) +- goto error; +- status = write16(state, SIO_TOP_COMM_KEY__A, key); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int GetDeviceCapabilities(struct drxk_state *state) +-{ +- u16 sioPdrOhwCfg = 0; +- u32 sioTopJtagidLo = 0; +- int status; +- const char *spin = ""; +- +- dprintk(1, "\n"); +- +- /* driver 0.9.0 */ +- /* stop lock indicator process */ +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA); +- if (status < 0) +- goto error; +- status = read16(state, SIO_PDR_OHW_CFG__A, &sioPdrOhwCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); +- if (status < 0) +- goto error; +- +- switch ((sioPdrOhwCfg & SIO_PDR_OHW_CFG_FREF_SEL__M)) { +- case 0: +- /* ignore (bypass ?) */ +- break; +- case 1: +- /* 27 MHz */ +- state->m_oscClockFreq = 27000; +- break; +- case 2: +- /* 20.25 MHz */ +- state->m_oscClockFreq = 20250; +- break; +- case 3: +- /* 4 MHz */ +- state->m_oscClockFreq = 20250; +- break; +- default: +- printk(KERN_ERR "drxk: Clock Frequency is unkonwn\n"); +- return -EINVAL; +- } +- /* +- Determine device capabilities +- Based on pinning v14 +- */ +- status = read32(state, SIO_TOP_JTAGID_LO__A, &sioTopJtagidLo); +- if (status < 0) +- goto error; +- +-printk(KERN_ERR "drxk: status = 0x%08x\n", sioTopJtagidLo); +- +- /* driver 0.9.0 */ +- switch ((sioTopJtagidLo >> 29) & 0xF) { +- case 0: +- state->m_deviceSpin = DRXK_SPIN_A1; +- spin = "A1"; +- break; +- case 2: +- state->m_deviceSpin = DRXK_SPIN_A2; +- spin = "A2"; +- break; +- case 3: +- state->m_deviceSpin = DRXK_SPIN_A3; +- spin = "A3"; +- break; +- default: +- state->m_deviceSpin = DRXK_SPIN_UNKNOWN; +- status = -EINVAL; +- printk(KERN_ERR "drxk: Spin %d unknown\n", +- (sioTopJtagidLo >> 29) & 0xF); +- goto error2; +- } +- switch ((sioTopJtagidLo >> 12) & 0xFF) { +- case 0x13: +- /* typeId = DRX3913K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = false; +- state->m_hasAudio = false; +- state->m_hasDVBT = true; +- state->m_hasDVBC = true; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = false; +- state->m_hasGPIO1 = false; +- state->m_hasIRQN = false; +- break; +- case 0x15: +- /* typeId = DRX3915K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = false; +- state->m_hasDVBT = true; +- state->m_hasDVBC = false; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x16: +- /* typeId = DRX3916K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = false; +- state->m_hasDVBT = true; +- state->m_hasDVBC = false; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x18: +- /* typeId = DRX3918K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = true; +- state->m_hasDVBT = true; +- state->m_hasDVBC = false; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x21: +- /* typeId = DRX3921K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = true; +- state->m_hasDVBT = true; +- state->m_hasDVBC = true; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x23: +- /* typeId = DRX3923K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = true; +- state->m_hasDVBT = true; +- state->m_hasDVBC = true; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x25: +- /* typeId = DRX3925K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = true; +- state->m_hasDVBT = true; +- state->m_hasDVBC = true; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- case 0x26: +- /* typeId = DRX3926K_TYPE_ID */ +- state->m_hasLNA = false; +- state->m_hasOOB = false; +- state->m_hasATV = true; +- state->m_hasAudio = false; +- state->m_hasDVBT = true; +- state->m_hasDVBC = true; +- state->m_hasSAWSW = true; +- state->m_hasGPIO2 = true; +- state->m_hasGPIO1 = true; +- state->m_hasIRQN = false; +- break; +- default: +- printk(KERN_ERR "drxk: DeviceID 0x%02x not supported\n", +- ((sioTopJtagidLo >> 12) & 0xFF)); +- status = -EINVAL; +- goto error2; +- } +- +- printk(KERN_INFO +- "drxk: detected a drx-39%02xk, spin %s, xtal %d.%03d MHz\n", +- ((sioTopJtagidLo >> 12) & 0xFF), spin, +- state->m_oscClockFreq / 1000, +- state->m_oscClockFreq % 1000); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +-error2: +- return status; +-} +- +-static int HI_Command(struct drxk_state *state, u16 cmd, u16 *pResult) +-{ +- int status; +- bool powerdown_cmd; +- +- dprintk(1, "\n"); +- +- /* Write command */ +- status = write16(state, SIO_HI_RA_RAM_CMD__A, cmd); +- if (status < 0) +- goto error; +- if (cmd == SIO_HI_RA_RAM_CMD_RESET) +- msleep(1); +- +- powerdown_cmd = +- (bool) ((cmd == SIO_HI_RA_RAM_CMD_CONFIG) && +- ((state->m_HICfgCtrl) & +- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M) == +- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ); +- if (powerdown_cmd == false) { +- /* Wait until command rdy */ +- u32 retryCount = 0; +- u16 waitCmd; +- +- do { +- msleep(1); +- retryCount += 1; +- status = read16(state, SIO_HI_RA_RAM_CMD__A, +- &waitCmd); +- } while ((status < 0) && (retryCount < DRXK_MAX_RETRIES) +- && (waitCmd != 0)); +- if (status < 0) +- goto error; +- status = read16(state, SIO_HI_RA_RAM_RES__A, pResult); +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int HI_CfgCommand(struct drxk_state *state) +-{ +- int status; +- +- dprintk(1, "\n"); +- +- mutex_lock(&state->mutex); +- +- status = write16(state, SIO_HI_RA_RAM_PAR_6__A, state->m_HICfgTimeout); +- if (status < 0) +- goto error; +- status = write16(state, SIO_HI_RA_RAM_PAR_5__A, state->m_HICfgCtrl); +- if (status < 0) +- goto error; +- status = write16(state, SIO_HI_RA_RAM_PAR_4__A, state->m_HICfgWakeUpKey); +- if (status < 0) +- goto error; +- status = write16(state, SIO_HI_RA_RAM_PAR_3__A, state->m_HICfgBridgeDelay); +- if (status < 0) +- goto error; +- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, state->m_HICfgTimingDiv); +- if (status < 0) +- goto error; +- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); +- if (status < 0) +- goto error; +- status = HI_Command(state, SIO_HI_RA_RAM_CMD_CONFIG, 0); +- if (status < 0) +- goto error; +- +- state->m_HICfgCtrl &= ~SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; +-error: +- mutex_unlock(&state->mutex); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int InitHI(struct drxk_state *state) +-{ +- dprintk(1, "\n"); +- +- state->m_HICfgWakeUpKey = (state->demod_address << 1); +- state->m_HICfgTimeout = 0x96FF; +- /* port/bridge/power down ctrl */ +- state->m_HICfgCtrl = SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE; +- +- return HI_CfgCommand(state); +-} +- +-static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable) +-{ +- int status = -1; +- u16 sioPdrMclkCfg = 0; +- u16 sioPdrMdxCfg = 0; +- +- dprintk(1, ": mpeg %s, %s mode\n", +- mpegEnable ? "enable" : "disable", +- state->m_enableParallel ? "parallel" : "serial"); +- +- /* stop lock indicator process */ +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- +- /* MPEG TS pad configuration */ +- status = write16(state, SIO_TOP_COMM_KEY__A, 0xFABA); +- if (status < 0) +- goto error; +- +- if (mpegEnable == false) { +- /* Set MPEG TS pads to inputmode */ +- status = write16(state, SIO_PDR_MSTRT_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MCLK_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD0_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- } else { +- /* Enable MPEG output */ +- sioPdrMdxCfg = +- ((state->m_TSDataStrength << +- SIO_PDR_MD0_CFG_DRIVE__B) | 0x0003); +- sioPdrMclkCfg = ((state->m_TSClockkStrength << +- SIO_PDR_MCLK_CFG_DRIVE__B) | +- 0x0003); +- +- status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */ +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */ +- if (status < 0) +- goto error; +- if (state->m_enableParallel == true) { +- /* paralel -> enable MD1 to MD7 */ +- status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD2_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD3_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD4_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD5_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD6_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD7_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- } else { +- sioPdrMdxCfg = ((state->m_TSDataStrength << +- SIO_PDR_MD0_CFG_DRIVE__B) +- | 0x0003); +- /* serial -> disable MD1 to MD7 */ +- status = write16(state, SIO_PDR_MD1_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD2_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD3_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD4_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD5_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD6_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD7_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- } +- status = write16(state, SIO_PDR_MCLK_CFG__A, sioPdrMclkCfg); +- if (status < 0) +- goto error; +- status = write16(state, SIO_PDR_MD0_CFG__A, sioPdrMdxCfg); +- if (status < 0) +- goto error; +- } +- /* Enable MB output over MPEG pads and ctl input */ +- status = write16(state, SIO_PDR_MON_CFG__A, 0x0000); +- if (status < 0) +- goto error; +- /* Write nomagic word to enable pdr reg write */ +- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int MPEGTSDisable(struct drxk_state *state) +-{ +- dprintk(1, "\n"); +- +- return MPEGTSConfigurePins(state, false); +-} +- +-static int BLChainCmd(struct drxk_state *state, +- u16 romOffset, u16 nrOfElements, u32 timeOut) +-{ +- u16 blStatus = 0; +- int status; +- unsigned long end; +- +- dprintk(1, "\n"); +- mutex_lock(&state->mutex); +- status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_CHAIN); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_CHAIN_ADDR__A, romOffset); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_CHAIN_LEN__A, nrOfElements); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); +- if (status < 0) +- goto error; +- +- end = jiffies + msecs_to_jiffies(timeOut); +- do { +- msleep(1); +- status = read16(state, SIO_BL_STATUS__A, &blStatus); +- if (status < 0) +- goto error; +- } while ((blStatus == 0x1) && +- ((time_is_after_jiffies(end)))); +- +- if (blStatus == 0x1) { +- printk(KERN_ERR "drxk: SIO not ready\n"); +- status = -EINVAL; +- goto error2; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +-error2: +- mutex_unlock(&state->mutex); +- return status; +-} +- +- +-static int DownloadMicrocode(struct drxk_state *state, +- const u8 pMCImage[], u32 Length) +-{ +- const u8 *pSrc = pMCImage; +- u16 Flags; +- u16 Drain; +- u32 Address; +- u16 nBlocks; +- u16 BlockSize; +- u16 BlockCRC; +- u32 offset = 0; +- u32 i; +- int status = 0; +- +- dprintk(1, "\n"); +- +- /* down the drain (we don care about MAGIC_WORD) */ +- Drain = (pSrc[0] << 8) | pSrc[1]; +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- nBlocks = (pSrc[0] << 8) | pSrc[1]; +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- for (i = 0; i < nBlocks; i += 1) { +- Address = (pSrc[0] << 24) | (pSrc[1] << 16) | +- (pSrc[2] << 8) | pSrc[3]; +- pSrc += sizeof(u32); +- offset += sizeof(u32); +- +- BlockSize = ((pSrc[0] << 8) | pSrc[1]) * sizeof(u16); +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- Flags = (pSrc[0] << 8) | pSrc[1]; +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- BlockCRC = (pSrc[0] << 8) | pSrc[1]; +- pSrc += sizeof(u16); +- offset += sizeof(u16); +- +- if (offset + BlockSize > Length) { +- printk(KERN_ERR "drxk: Firmware is corrupted.\n"); +- return -EINVAL; +- } +- +- status = write_block(state, Address, BlockSize, pSrc); +- if (status < 0) { +- printk(KERN_ERR "drxk: Error %d while loading firmware\n", status); +- break; +- } +- pSrc += BlockSize; +- offset += BlockSize; +- } +- return status; +-} +- +-static int DVBTEnableOFDMTokenRing(struct drxk_state *state, bool enable) +-{ +- int status; +- u16 data = 0; +- u16 desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_ON; +- u16 desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED; +- unsigned long end; +- +- dprintk(1, "\n"); +- +- if (enable == false) { +- desiredCtrl = SIO_OFDM_SH_OFDM_RING_ENABLE_OFF; +- desiredStatus = SIO_OFDM_SH_OFDM_RING_STATUS_DOWN; +- } +- +- status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); +- if (status >= 0 && data == desiredStatus) { +- /* tokenring already has correct status */ +- return status; +- } +- /* Disable/enable dvbt tokenring bridge */ +- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, desiredCtrl); +- +- end = jiffies + msecs_to_jiffies(DRXK_OFDM_TR_SHUTDOWN_TIMEOUT); +- do { +- status = read16(state, SIO_OFDM_SH_OFDM_RING_STATUS__A, &data); +- if ((status >= 0 && data == desiredStatus) || time_is_after_jiffies(end)) +- break; +- msleep(1); +- } while (1); +- if (data != desiredStatus) { +- printk(KERN_ERR "drxk: SIO not ready\n"); +- return -EINVAL; +- } +- return status; +-} +- +-static int MPEGTSStop(struct drxk_state *state) +-{ +- int status = 0; +- u16 fecOcSncMode = 0; +- u16 fecOcIprMode = 0; +- +- dprintk(1, "\n"); +- +- /* Gracefull shutdown (byte boundaries) */ +- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode); +- if (status < 0) +- goto error; +- fecOcSncMode |= FEC_OC_SNC_MODE_SHUTDOWN__M; +- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode); +- if (status < 0) +- goto error; +- +- /* Suppress MCLK during absence of data */ +- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcIprMode); +- if (status < 0) +- goto error; +- fecOcIprMode |= FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M; +- status = write16(state, FEC_OC_IPR_MODE__A, fecOcIprMode); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int scu_command(struct drxk_state *state, +- u16 cmd, u8 parameterLen, +- u16 *parameter, u8 resultLen, u16 *result) +-{ +-#if (SCU_RAM_PARAM_0__A - SCU_RAM_PARAM_15__A) != 15 +-#error DRXK register mapping no longer compatible with this routine! +-#endif +- u16 curCmd = 0; +- int status = -EINVAL; +- unsigned long end; +- u8 buffer[34]; +- int cnt = 0, ii; +- const char *p; +- char errname[30]; +- +- dprintk(1, "\n"); +- +- if ((cmd == 0) || ((parameterLen > 0) && (parameter == NULL)) || +- ((resultLen > 0) && (result == NULL))) { +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +- } +- +- mutex_lock(&state->mutex); +- +- /* assume that the command register is ready +- since it is checked afterwards */ +- for (ii = parameterLen - 1; ii >= 0; ii -= 1) { +- buffer[cnt++] = (parameter[ii] & 0xFF); +- buffer[cnt++] = ((parameter[ii] >> 8) & 0xFF); +- } +- buffer[cnt++] = (cmd & 0xFF); +- buffer[cnt++] = ((cmd >> 8) & 0xFF); +- +- write_block(state, SCU_RAM_PARAM_0__A - +- (parameterLen - 1), cnt, buffer); +- /* Wait until SCU has processed command */ +- end = jiffies + msecs_to_jiffies(DRXK_MAX_WAITTIME); +- do { +- msleep(1); +- status = read16(state, SCU_RAM_COMMAND__A, &curCmd); +- if (status < 0) +- goto error; +- } while (!(curCmd == DRX_SCU_READY) && (time_is_after_jiffies(end))); +- if (curCmd != DRX_SCU_READY) { +- printk(KERN_ERR "drxk: SCU not ready\n"); +- status = -EIO; +- goto error2; +- } +- /* read results */ +- if ((resultLen > 0) && (result != NULL)) { +- s16 err; +- int ii; +- +- for (ii = resultLen - 1; ii >= 0; ii -= 1) { +- status = read16(state, SCU_RAM_PARAM_0__A - ii, &result[ii]); +- if (status < 0) +- goto error; +- } +- +- /* Check if an error was reported by SCU */ +- err = (s16)result[0]; +- if (err >= 0) +- goto error; +- +- /* check for the known error codes */ +- switch (err) { +- case SCU_RESULT_UNKCMD: +- p = "SCU_RESULT_UNKCMD"; +- break; +- case SCU_RESULT_UNKSTD: +- p = "SCU_RESULT_UNKSTD"; +- break; +- case SCU_RESULT_SIZE: +- p = "SCU_RESULT_SIZE"; +- break; +- case SCU_RESULT_INVPAR: +- p = "SCU_RESULT_INVPAR"; +- break; +- default: /* Other negative values are errors */ +- sprintf(errname, "ERROR: %d\n", err); +- p = errname; +- } +- printk(KERN_ERR "drxk: %s while sending cmd 0x%04x with params:", p, cmd); +- print_hex_dump_bytes("drxk: ", DUMP_PREFIX_NONE, buffer, cnt); +- status = -EINVAL; +- goto error2; +- } +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +-error2: +- mutex_unlock(&state->mutex); +- return status; +-} +- +-static int SetIqmAf(struct drxk_state *state, bool active) +-{ +- u16 data = 0; +- int status; +- +- dprintk(1, "\n"); +- +- /* Configure IQM */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- +- if (!active) { +- data |= (IQM_AF_STDBY_STDBY_ADC_STANDBY +- | IQM_AF_STDBY_STDBY_AMP_STANDBY +- | IQM_AF_STDBY_STDBY_PD_STANDBY +- | IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY +- | IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY); +- } else { +- data &= ((~IQM_AF_STDBY_STDBY_ADC_STANDBY) +- & (~IQM_AF_STDBY_STDBY_AMP_STANDBY) +- & (~IQM_AF_STDBY_STDBY_PD_STANDBY) +- & (~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY) +- & (~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY) +- ); +- } +- status = write16(state, IQM_AF_STDBY__A, data); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int CtrlPowerMode(struct drxk_state *state, enum DRXPowerMode *mode) +-{ +- int status = 0; +- u16 sioCcPwdMode = 0; +- +- dprintk(1, "\n"); +- +- /* Check arguments */ +- if (mode == NULL) +- return -EINVAL; +- +- switch (*mode) { +- case DRX_POWER_UP: +- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_NONE; +- break; +- case DRXK_POWER_DOWN_OFDM: +- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OFDM; +- break; +- case DRXK_POWER_DOWN_CORE: +- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_CLOCK; +- break; +- case DRXK_POWER_DOWN_PLL: +- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_PLL; +- break; +- case DRX_POWER_DOWN: +- sioCcPwdMode = SIO_CC_PWD_MODE_LEVEL_OSC; +- break; +- default: +- /* Unknow sleep mode */ +- return -EINVAL; +- } +- +- /* If already in requested power mode, do nothing */ +- if (state->m_currentPowerMode == *mode) +- return 0; +- +- /* For next steps make sure to start from DRX_POWER_UP mode */ +- if (state->m_currentPowerMode != DRX_POWER_UP) { +- status = PowerUpDevice(state); +- if (status < 0) +- goto error; +- status = DVBTEnableOFDMTokenRing(state, true); +- if (status < 0) +- goto error; +- } +- +- if (*mode == DRX_POWER_UP) { +- /* Restore analog & pin configuartion */ +- } else { +- /* Power down to requested mode */ +- /* Backup some register settings */ +- /* Set pins with possible pull-ups connected +- to them in input mode */ +- /* Analog power down */ +- /* ADC power down */ +- /* Power down device */ +- /* stop all comm_exec */ +- /* Stop and power down previous standard */ +- switch (state->m_OperationMode) { +- case OM_DVBT: +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = PowerDownDVBT(state, false); +- if (status < 0) +- goto error; +- break; +- case OM_QAM_ITU_A: +- case OM_QAM_ITU_C: +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = PowerDownQAM(state); +- if (status < 0) +- goto error; +- break; +- default: +- break; +- } +- status = DVBTEnableOFDMTokenRing(state, false); +- if (status < 0) +- goto error; +- status = write16(state, SIO_CC_PWD_MODE__A, sioCcPwdMode); +- if (status < 0) +- goto error; +- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); +- if (status < 0) +- goto error; +- +- if (*mode != DRXK_POWER_DOWN_OFDM) { +- state->m_HICfgCtrl |= +- SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; +- status = HI_CfgCommand(state); +- if (status < 0) +- goto error; +- } +- } +- state->m_currentPowerMode = *mode; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int PowerDownDVBT(struct drxk_state *state, bool setPowerMode) +-{ +- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; +- u16 cmdResult = 0; +- u16 data = 0; +- int status; +- +- dprintk(1, "\n"); +- +- status = read16(state, SCU_COMM_EXEC__A, &data); +- if (status < 0) +- goto error; +- if (data == SCU_COMM_EXEC_ACTIVE) { +- /* Send OFDM stop command */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- /* Send OFDM reset command */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- } +- +- /* Reset datapath for OFDM, processors first */ +- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); +- if (status < 0) +- goto error; +- +- /* powerdown AFE */ +- status = SetIqmAf(state, false); +- if (status < 0) +- goto error; +- +- /* powerdown to OFDM mode */ +- if (setPowerMode) { +- status = CtrlPowerMode(state, &powerMode); +- if (status < 0) +- goto error; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SetOperationMode(struct drxk_state *state, +- enum OperationMode oMode) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- /* +- Stop and power down previous standard +- TODO investigate total power down instead of partial +- power down depending on "previous" standard. +- */ +- +- /* disable HW lock indicator */ +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- +- /* Device is already at the required mode */ +- if (state->m_OperationMode == oMode) +- return 0; +- +- switch (state->m_OperationMode) { +- /* OM_NONE was added for start up */ +- case OM_NONE: +- break; +- case OM_DVBT: +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = PowerDownDVBT(state, true); +- if (status < 0) +- goto error; +- state->m_OperationMode = OM_NONE; +- break; +- case OM_QAM_ITU_A: /* fallthrough */ +- case OM_QAM_ITU_C: +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = PowerDownQAM(state); +- if (status < 0) +- goto error; +- state->m_OperationMode = OM_NONE; +- break; +- case OM_QAM_ITU_B: +- default: +- status = -EINVAL; +- goto error; +- } +- +- /* +- Power up new standard +- */ +- switch (oMode) { +- case OM_DVBT: +- dprintk(1, ": DVB-T\n"); +- state->m_OperationMode = oMode; +- status = SetDVBTStandard(state, oMode); +- if (status < 0) +- goto error; +- break; +- case OM_QAM_ITU_A: /* fallthrough */ +- case OM_QAM_ITU_C: +- dprintk(1, ": DVB-C Annex %c\n", +- (state->m_OperationMode == OM_QAM_ITU_A) ? 'A' : 'C'); +- state->m_OperationMode = oMode; +- status = SetQAMStandard(state, oMode); +- if (status < 0) +- goto error; +- break; +- case OM_QAM_ITU_B: +- default: +- status = -EINVAL; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int Start(struct drxk_state *state, s32 offsetFreq, +- s32 IntermediateFrequency) +-{ +- int status = -EINVAL; +- +- u16 IFreqkHz; +- s32 OffsetkHz = offsetFreq / 1000; +- +- dprintk(1, "\n"); +- if (state->m_DrxkState != DRXK_STOPPED && +- state->m_DrxkState != DRXK_DTV_STARTED) +- goto error; +- +- state->m_bMirrorFreqSpect = (state->props.inversion == INVERSION_ON); +- +- if (IntermediateFrequency < 0) { +- state->m_bMirrorFreqSpect = !state->m_bMirrorFreqSpect; +- IntermediateFrequency = -IntermediateFrequency; +- } +- +- switch (state->m_OperationMode) { +- case OM_QAM_ITU_A: +- case OM_QAM_ITU_C: +- IFreqkHz = (IntermediateFrequency / 1000); +- status = SetQAM(state, IFreqkHz, OffsetkHz); +- if (status < 0) +- goto error; +- state->m_DrxkState = DRXK_DTV_STARTED; +- break; +- case OM_DVBT: +- IFreqkHz = (IntermediateFrequency / 1000); +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = SetDVBT(state, IFreqkHz, OffsetkHz); +- if (status < 0) +- goto error; +- status = DVBTStart(state); +- if (status < 0) +- goto error; +- state->m_DrxkState = DRXK_DTV_STARTED; +- break; +- default: +- break; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int ShutDown(struct drxk_state *state) +-{ +- dprintk(1, "\n"); +- +- MPEGTSStop(state); +- return 0; +-} +- +-static int GetLockStatus(struct drxk_state *state, u32 *pLockStatus, +- u32 Time) +-{ +- int status = -EINVAL; +- +- dprintk(1, "\n"); +- +- if (pLockStatus == NULL) +- goto error; +- +- *pLockStatus = NOT_LOCKED; +- +- /* define the SCU command code */ +- switch (state->m_OperationMode) { +- case OM_QAM_ITU_A: +- case OM_QAM_ITU_B: +- case OM_QAM_ITU_C: +- status = GetQAMLockStatus(state, pLockStatus); +- break; +- case OM_DVBT: +- status = GetDVBTLockStatus(state, pLockStatus); +- break; +- default: +- break; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int MPEGTSStart(struct drxk_state *state) +-{ +- int status; +- +- u16 fecOcSncMode = 0; +- +- /* Allow OC to sync again */ +- status = read16(state, FEC_OC_SNC_MODE__A, &fecOcSncMode); +- if (status < 0) +- goto error; +- fecOcSncMode &= ~FEC_OC_SNC_MODE_SHUTDOWN__M; +- status = write16(state, FEC_OC_SNC_MODE__A, fecOcSncMode); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_SNC_UNLOCK__A, 1); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int MPEGTSDtoInit(struct drxk_state *state) +-{ +- int status; +- +- dprintk(1, "\n"); +- +- /* Rate integration settings */ +- status = write16(state, FEC_OC_RCN_CTL_STEP_LO__A, 0x0000); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_RCN_CTL_STEP_HI__A, 0x000C); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_RCN_GAIN__A, 0x000A); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_AVR_PARM_A__A, 0x0008); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_AVR_PARM_B__A, 0x0006); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_TMD_HI_MARGIN__A, 0x0680); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_TMD_LO_MARGIN__A, 0x0080); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_TMD_COUNT__A, 0x03F4); +- if (status < 0) +- goto error; +- +- /* Additional configuration */ +- status = write16(state, FEC_OC_OCR_INVERT__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_SNC_LWM__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_SNC_HWM__A, 12); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int MPEGTSDtoSetup(struct drxk_state *state, +- enum OperationMode oMode) +-{ +- int status; +- +- u16 fecOcRegMode = 0; /* FEC_OC_MODE register value */ +- u16 fecOcRegIprMode = 0; /* FEC_OC_IPR_MODE register value */ +- u16 fecOcDtoMode = 0; /* FEC_OC_IPR_INVERT register value */ +- u16 fecOcFctMode = 0; /* FEC_OC_IPR_INVERT register value */ +- u16 fecOcDtoPeriod = 2; /* FEC_OC_IPR_INVERT register value */ +- u16 fecOcDtoBurstLen = 188; /* FEC_OC_IPR_INVERT register value */ +- u32 fecOcRcnCtlRate = 0; /* FEC_OC_IPR_INVERT register value */ +- u16 fecOcTmdMode = 0; +- u16 fecOcTmdIntUpdRate = 0; +- u32 maxBitRate = 0; +- bool staticCLK = false; +- +- dprintk(1, "\n"); +- +- /* Check insertion of the Reed-Solomon parity bytes */ +- status = read16(state, FEC_OC_MODE__A, &fecOcRegMode); +- if (status < 0) +- goto error; +- status = read16(state, FEC_OC_IPR_MODE__A, &fecOcRegIprMode); +- if (status < 0) +- goto error; +- fecOcRegMode &= (~FEC_OC_MODE_PARITY__M); +- fecOcRegIprMode &= (~FEC_OC_IPR_MODE_MVAL_DIS_PAR__M); +- if (state->m_insertRSByte == true) { +- /* enable parity symbol forward */ +- fecOcRegMode |= FEC_OC_MODE_PARITY__M; +- /* MVAL disable during parity bytes */ +- fecOcRegIprMode |= FEC_OC_IPR_MODE_MVAL_DIS_PAR__M; +- /* TS burst length to 204 */ +- fecOcDtoBurstLen = 204; +- } +- +- /* Check serial or parrallel output */ +- fecOcRegIprMode &= (~(FEC_OC_IPR_MODE_SERIAL__M)); +- if (state->m_enableParallel == false) { +- /* MPEG data output is serial -> set ipr_mode[0] */ +- fecOcRegIprMode |= FEC_OC_IPR_MODE_SERIAL__M; +- } +- +- switch (oMode) { +- case OM_DVBT: +- maxBitRate = state->m_DVBTBitrate; +- fecOcTmdMode = 3; +- fecOcRcnCtlRate = 0xC00000; +- staticCLK = state->m_DVBTStaticCLK; +- break; +- case OM_QAM_ITU_A: /* fallthrough */ +- case OM_QAM_ITU_C: +- fecOcTmdMode = 0x0004; +- fecOcRcnCtlRate = 0xD2B4EE; /* good for >63 Mb/s */ +- maxBitRate = state->m_DVBCBitrate; +- staticCLK = state->m_DVBCStaticCLK; +- break; +- default: +- status = -EINVAL; +- } /* switch (standard) */ +- if (status < 0) +- goto error; +- +- /* Configure DTO's */ +- if (staticCLK) { +- u32 bitRate = 0; +- +- /* Rational DTO for MCLK source (static MCLK rate), +- Dynamic DTO for optimal grouping +- (avoid intra-packet gaps), +- DTO offset enable to sync TS burst with MSTRT */ +- fecOcDtoMode = (FEC_OC_DTO_MODE_DYNAMIC__M | +- FEC_OC_DTO_MODE_OFFSET_ENABLE__M); +- fecOcFctMode = (FEC_OC_FCT_MODE_RAT_ENA__M | +- FEC_OC_FCT_MODE_VIRT_ENA__M); +- +- /* Check user defined bitrate */ +- bitRate = maxBitRate; +- if (bitRate > 75900000UL) { /* max is 75.9 Mb/s */ +- bitRate = 75900000UL; +- } +- /* Rational DTO period: +- dto_period = (Fsys / bitrate) - 2 +- +- Result should be floored, +- to make sure >= requested bitrate +- */ +- fecOcDtoPeriod = (u16) (((state->m_sysClockFreq) +- * 1000) / bitRate); +- if (fecOcDtoPeriod <= 2) +- fecOcDtoPeriod = 0; +- else +- fecOcDtoPeriod -= 2; +- fecOcTmdIntUpdRate = 8; +- } else { +- /* (commonAttr->staticCLK == false) => dynamic mode */ +- fecOcDtoMode = FEC_OC_DTO_MODE_DYNAMIC__M; +- fecOcFctMode = FEC_OC_FCT_MODE__PRE; +- fecOcTmdIntUpdRate = 5; +- } +- +- /* Write appropriate registers with requested configuration */ +- status = write16(state, FEC_OC_DTO_BURST_LEN__A, fecOcDtoBurstLen); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_DTO_PERIOD__A, fecOcDtoPeriod); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_DTO_MODE__A, fecOcDtoMode); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_FCT_MODE__A, fecOcFctMode); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_MODE__A, fecOcRegMode); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_IPR_MODE__A, fecOcRegIprMode); +- if (status < 0) +- goto error; +- +- /* Rate integration settings */ +- status = write32(state, FEC_OC_RCN_CTL_RATE_LO__A, fecOcRcnCtlRate); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_TMD_INT_UPD_RATE__A, fecOcTmdIntUpdRate); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_TMD_MODE__A, fecOcTmdMode); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int MPEGTSConfigurePolarity(struct drxk_state *state) +-{ +- u16 fecOcRegIprInvert = 0; +- +- /* Data mask for the output data byte */ +- u16 InvertDataMask = +- FEC_OC_IPR_INVERT_MD7__M | FEC_OC_IPR_INVERT_MD6__M | +- FEC_OC_IPR_INVERT_MD5__M | FEC_OC_IPR_INVERT_MD4__M | +- FEC_OC_IPR_INVERT_MD3__M | FEC_OC_IPR_INVERT_MD2__M | +- FEC_OC_IPR_INVERT_MD1__M | FEC_OC_IPR_INVERT_MD0__M; +- +- dprintk(1, "\n"); +- +- /* Control selective inversion of output bits */ +- fecOcRegIprInvert &= (~(InvertDataMask)); +- if (state->m_invertDATA == true) +- fecOcRegIprInvert |= InvertDataMask; +- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MERR__M)); +- if (state->m_invertERR == true) +- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MERR__M; +- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MSTRT__M)); +- if (state->m_invertSTR == true) +- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MSTRT__M; +- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MVAL__M)); +- if (state->m_invertVAL == true) +- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MVAL__M; +- fecOcRegIprInvert &= (~(FEC_OC_IPR_INVERT_MCLK__M)); +- if (state->m_invertCLK == true) +- fecOcRegIprInvert |= FEC_OC_IPR_INVERT_MCLK__M; +- +- return write16(state, FEC_OC_IPR_INVERT__A, fecOcRegIprInvert); +-} +- +-#define SCU_RAM_AGC_KI_INV_RF_POL__M 0x4000 +- +-static int SetAgcRf(struct drxk_state *state, +- struct SCfgAgc *pAgcCfg, bool isDTV) +-{ +- int status = -EINVAL; +- u16 data = 0; +- struct SCfgAgc *pIfAgcSettings; +- +- dprintk(1, "\n"); +- +- if (pAgcCfg == NULL) +- goto error; +- +- switch (pAgcCfg->ctrlMode) { +- case DRXK_AGC_CTRL_AUTO: +- /* Enable RF AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- +- /* Enable SCU RF AGC loop */ +- data &= ~SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; +- +- /* Polarity */ +- if (state->m_RfAgcPol) +- data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; +- else +- data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- +- /* Set speed (using complementary reduction value) */ +- status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); +- if (status < 0) +- goto error; +- +- data &= ~SCU_RAM_AGC_KI_RED_RAGC_RED__M; +- data |= (~(pAgcCfg->speed << +- SCU_RAM_AGC_KI_RED_RAGC_RED__B) +- & SCU_RAM_AGC_KI_RED_RAGC_RED__M); +- +- status = write16(state, SCU_RAM_AGC_KI_RED__A, data); +- if (status < 0) +- goto error; +- +- if (IsDVBT(state)) +- pIfAgcSettings = &state->m_dvbtIfAgcCfg; +- else if (IsQAM(state)) +- pIfAgcSettings = &state->m_qamIfAgcCfg; +- else +- pIfAgcSettings = &state->m_atvIfAgcCfg; +- if (pIfAgcSettings == NULL) { +- status = -EINVAL; +- goto error; +- } +- +- /* Set TOP, only if IF-AGC is in AUTO mode */ +- if (pIfAgcSettings->ctrlMode == DRXK_AGC_CTRL_AUTO) +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->top); +- if (status < 0) +- goto error; +- +- /* Cut-Off current */ +- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, pAgcCfg->cutOffCurrent); +- if (status < 0) +- goto error; +- +- /* Max. output level */ +- status = write16(state, SCU_RAM_AGC_RF_MAX__A, pAgcCfg->maxOutputLevel); +- if (status < 0) +- goto error; +- +- break; +- +- case DRXK_AGC_CTRL_USER: +- /* Enable RF AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data &= ~IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- +- /* Disable SCU RF AGC loop */ +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; +- if (state->m_RfAgcPol) +- data |= SCU_RAM_AGC_CONFIG_INV_RF_POL__M; +- else +- data &= ~SCU_RAM_AGC_CONFIG_INV_RF_POL__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- +- /* SCU c.o.c. to 0, enabling full control range */ +- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI_CO__A, 0); +- if (status < 0) +- goto error; +- +- /* Write value to output pin */ +- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, pAgcCfg->outputLevel); +- if (status < 0) +- goto error; +- break; +- +- case DRXK_AGC_CTRL_OFF: +- /* Disable RF AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data |= IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- +- /* Disable SCU RF AGC loop */ +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- data |= SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- break; +- +- default: +- status = -EINVAL; +- +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-#define SCU_RAM_AGC_KI_INV_IF_POL__M 0x2000 +- +-static int SetAgcIf(struct drxk_state *state, +- struct SCfgAgc *pAgcCfg, bool isDTV) +-{ +- u16 data = 0; +- int status = 0; +- struct SCfgAgc *pRfAgcSettings; +- +- dprintk(1, "\n"); +- +- switch (pAgcCfg->ctrlMode) { +- case DRXK_AGC_CTRL_AUTO: +- +- /* Enable IF AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- +- /* Enable SCU IF AGC loop */ +- data &= ~SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; +- +- /* Polarity */ +- if (state->m_IfAgcPol) +- data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; +- else +- data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- +- /* Set speed (using complementary reduction value) */ +- status = read16(state, SCU_RAM_AGC_KI_RED__A, &data); +- if (status < 0) +- goto error; +- data &= ~SCU_RAM_AGC_KI_RED_IAGC_RED__M; +- data |= (~(pAgcCfg->speed << +- SCU_RAM_AGC_KI_RED_IAGC_RED__B) +- & SCU_RAM_AGC_KI_RED_IAGC_RED__M); +- +- status = write16(state, SCU_RAM_AGC_KI_RED__A, data); +- if (status < 0) +- goto error; +- +- if (IsQAM(state)) +- pRfAgcSettings = &state->m_qamRfAgcCfg; +- else +- pRfAgcSettings = &state->m_atvRfAgcCfg; +- if (pRfAgcSettings == NULL) +- return -1; +- /* Restore TOP */ +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pRfAgcSettings->top); +- if (status < 0) +- goto error; +- break; +- +- case DRXK_AGC_CTRL_USER: +- +- /* Enable IF AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data &= ~IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- +- /* Disable SCU IF AGC loop */ +- data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; +- +- /* Polarity */ +- if (state->m_IfAgcPol) +- data |= SCU_RAM_AGC_CONFIG_INV_IF_POL__M; +- else +- data &= ~SCU_RAM_AGC_CONFIG_INV_IF_POL__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- +- /* Write value to output pin */ +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, pAgcCfg->outputLevel); +- if (status < 0) +- goto error; +- break; +- +- case DRXK_AGC_CTRL_OFF: +- +- /* Disable If AGC DAC */ +- status = read16(state, IQM_AF_STDBY__A, &data); +- if (status < 0) +- goto error; +- data |= IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY; +- status = write16(state, IQM_AF_STDBY__A, data); +- if (status < 0) +- goto error; +- +- /* Disable SCU IF AGC loop */ +- status = read16(state, SCU_RAM_AGC_CONFIG__A, &data); +- if (status < 0) +- goto error; +- data |= SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M; +- status = write16(state, SCU_RAM_AGC_CONFIG__A, data); +- if (status < 0) +- goto error; +- break; +- } /* switch (agcSettingsIf->ctrlMode) */ +- +- /* always set the top to support +- configurations without if-loop */ +- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, pAgcCfg->top); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int ReadIFAgc(struct drxk_state *state, u32 *pValue) +-{ +- u16 agcDacLvl; +- int status; +- u16 Level = 0; +- +- dprintk(1, "\n"); +- +- status = read16(state, IQM_AF_AGC_IF__A, &agcDacLvl); +- if (status < 0) { +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +- } +- +- *pValue = 0; +- +- if (agcDacLvl > DRXK_AGC_DAC_OFFSET) +- Level = agcDacLvl - DRXK_AGC_DAC_OFFSET; +- if (Level < 14000) +- *pValue = (14000 - Level) / 4; +- else +- *pValue = 0; +- +- return status; +-} +- +-static int GetQAMSignalToNoise(struct drxk_state *state, +- s32 *pSignalToNoise) +-{ +- int status = 0; +- u16 qamSlErrPower = 0; /* accum. error between +- raw and sliced symbols */ +- u32 qamSlSigPower = 0; /* used for MER, depends of +- QAM modulation */ +- u32 qamSlMer = 0; /* QAM MER */ +- +- dprintk(1, "\n"); +- +- /* MER calculation */ +- +- /* get the register value needed for MER */ +- status = read16(state, QAM_SL_ERR_POWER__A, &qamSlErrPower); +- if (status < 0) { +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return -EINVAL; +- } +- +- switch (state->props.modulation) { +- case QAM_16: +- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM16 << 2; +- break; +- case QAM_32: +- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM32 << 2; +- break; +- case QAM_64: +- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM64 << 2; +- break; +- case QAM_128: +- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM128 << 2; +- break; +- default: +- case QAM_256: +- qamSlSigPower = DRXK_QAM_SL_SIG_POWER_QAM256 << 2; +- break; +- } +- +- if (qamSlErrPower > 0) { +- qamSlMer = Log10Times100(qamSlSigPower) - +- Log10Times100((u32) qamSlErrPower); +- } +- *pSignalToNoise = qamSlMer; +- +- return status; +-} +- +-static int GetDVBTSignalToNoise(struct drxk_state *state, +- s32 *pSignalToNoise) +-{ +- int status; +- u16 regData = 0; +- u32 EqRegTdSqrErrI = 0; +- u32 EqRegTdSqrErrQ = 0; +- u16 EqRegTdSqrErrExp = 0; +- u16 EqRegTdTpsPwrOfs = 0; +- u16 EqRegTdReqSmbCnt = 0; +- u32 tpsCnt = 0; +- u32 SqrErrIQ = 0; +- u32 a = 0; +- u32 b = 0; +- u32 c = 0; +- u32 iMER = 0; +- u16 transmissionParams = 0; +- +- dprintk(1, "\n"); +- +- status = read16(state, OFDM_EQ_TOP_TD_TPS_PWR_OFS__A, &EqRegTdTpsPwrOfs); +- if (status < 0) +- goto error; +- status = read16(state, OFDM_EQ_TOP_TD_REQ_SMB_CNT__A, &EqRegTdReqSmbCnt); +- if (status < 0) +- goto error; +- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_EXP__A, &EqRegTdSqrErrExp); +- if (status < 0) +- goto error; +- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_I__A, ®Data); +- if (status < 0) +- goto error; +- /* Extend SQR_ERR_I operational range */ +- EqRegTdSqrErrI = (u32) regData; +- if ((EqRegTdSqrErrExp > 11) && +- (EqRegTdSqrErrI < 0x00000FFFUL)) { +- EqRegTdSqrErrI += 0x00010000UL; +- } +- status = read16(state, OFDM_EQ_TOP_TD_SQR_ERR_Q__A, ®Data); +- if (status < 0) +- goto error; +- /* Extend SQR_ERR_Q operational range */ +- EqRegTdSqrErrQ = (u32) regData; +- if ((EqRegTdSqrErrExp > 11) && +- (EqRegTdSqrErrQ < 0x00000FFFUL)) +- EqRegTdSqrErrQ += 0x00010000UL; +- +- status = read16(state, OFDM_SC_RA_RAM_OP_PARAM__A, &transmissionParams); +- if (status < 0) +- goto error; +- +- /* Check input data for MER */ +- +- /* MER calculation (in 0.1 dB) without math.h */ +- if ((EqRegTdTpsPwrOfs == 0) || (EqRegTdReqSmbCnt == 0)) +- iMER = 0; +- else if ((EqRegTdSqrErrI + EqRegTdSqrErrQ) == 0) { +- /* No error at all, this must be the HW reset value +- * Apparently no first measurement yet +- * Set MER to 0.0 */ +- iMER = 0; +- } else { +- SqrErrIQ = (EqRegTdSqrErrI + EqRegTdSqrErrQ) << +- EqRegTdSqrErrExp; +- if ((transmissionParams & +- OFDM_SC_RA_RAM_OP_PARAM_MODE__M) +- == OFDM_SC_RA_RAM_OP_PARAM_MODE_2K) +- tpsCnt = 17; +- else +- tpsCnt = 68; +- +- /* IMER = 100 * log10 (x) +- where x = (EqRegTdTpsPwrOfs^2 * +- EqRegTdReqSmbCnt * tpsCnt)/SqrErrIQ +- +- => IMER = a + b -c +- where a = 100 * log10 (EqRegTdTpsPwrOfs^2) +- b = 100 * log10 (EqRegTdReqSmbCnt * tpsCnt) +- c = 100 * log10 (SqrErrIQ) +- */ +- +- /* log(x) x = 9bits * 9bits->18 bits */ +- a = Log10Times100(EqRegTdTpsPwrOfs * +- EqRegTdTpsPwrOfs); +- /* log(x) x = 16bits * 7bits->23 bits */ +- b = Log10Times100(EqRegTdReqSmbCnt * tpsCnt); +- /* log(x) x = (16bits + 16bits) << 15 ->32 bits */ +- c = Log10Times100(SqrErrIQ); +- +- iMER = a + b; +- /* No negative MER, clip to zero */ +- if (iMER > c) +- iMER -= c; +- else +- iMER = 0; +- } +- *pSignalToNoise = iMER; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int GetSignalToNoise(struct drxk_state *state, s32 *pSignalToNoise) +-{ +- dprintk(1, "\n"); +- +- *pSignalToNoise = 0; +- switch (state->m_OperationMode) { +- case OM_DVBT: +- return GetDVBTSignalToNoise(state, pSignalToNoise); +- case OM_QAM_ITU_A: +- case OM_QAM_ITU_C: +- return GetQAMSignalToNoise(state, pSignalToNoise); +- default: +- break; +- } +- return 0; +-} +- +-#if 0 +-static int GetDVBTQuality(struct drxk_state *state, s32 *pQuality) +-{ +- /* SNR Values for quasi errorfree reception rom Nordig 2.2 */ +- int status = 0; +- +- dprintk(1, "\n"); +- +- static s32 QE_SN[] = { +- 51, /* QPSK 1/2 */ +- 69, /* QPSK 2/3 */ +- 79, /* QPSK 3/4 */ +- 89, /* QPSK 5/6 */ +- 97, /* QPSK 7/8 */ +- 108, /* 16-QAM 1/2 */ +- 131, /* 16-QAM 2/3 */ +- 146, /* 16-QAM 3/4 */ +- 156, /* 16-QAM 5/6 */ +- 160, /* 16-QAM 7/8 */ +- 165, /* 64-QAM 1/2 */ +- 187, /* 64-QAM 2/3 */ +- 202, /* 64-QAM 3/4 */ +- 216, /* 64-QAM 5/6 */ +- 225, /* 64-QAM 7/8 */ +- }; +- +- *pQuality = 0; +- +- do { +- s32 SignalToNoise = 0; +- u16 Constellation = 0; +- u16 CodeRate = 0; +- u32 SignalToNoiseRel; +- u32 BERQuality; +- +- status = GetDVBTSignalToNoise(state, &SignalToNoise); +- if (status < 0) +- break; +- status = read16(state, OFDM_EQ_TOP_TD_TPS_CONST__A, &Constellation); +- if (status < 0) +- break; +- Constellation &= OFDM_EQ_TOP_TD_TPS_CONST__M; +- +- status = read16(state, OFDM_EQ_TOP_TD_TPS_CODE_HP__A, &CodeRate); +- if (status < 0) +- break; +- CodeRate &= OFDM_EQ_TOP_TD_TPS_CODE_HP__M; +- +- if (Constellation > OFDM_EQ_TOP_TD_TPS_CONST_64QAM || +- CodeRate > OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8) +- break; +- SignalToNoiseRel = SignalToNoise - +- QE_SN[Constellation * 5 + CodeRate]; +- BERQuality = 100; +- +- if (SignalToNoiseRel < -70) +- *pQuality = 0; +- else if (SignalToNoiseRel < 30) +- *pQuality = ((SignalToNoiseRel + 70) * +- BERQuality) / 100; +- else +- *pQuality = BERQuality; +- } while (0); +- return 0; +-}; +- +-static int GetDVBCQuality(struct drxk_state *state, s32 *pQuality) +-{ +- int status = 0; +- *pQuality = 0; +- +- dprintk(1, "\n"); +- +- do { +- u32 SignalToNoise = 0; +- u32 BERQuality = 100; +- u32 SignalToNoiseRel = 0; +- +- status = GetQAMSignalToNoise(state, &SignalToNoise); +- if (status < 0) +- break; +- +- switch (state->props.modulation) { +- case QAM_16: +- SignalToNoiseRel = SignalToNoise - 200; +- break; +- case QAM_32: +- SignalToNoiseRel = SignalToNoise - 230; +- break; /* Not in NorDig */ +- case QAM_64: +- SignalToNoiseRel = SignalToNoise - 260; +- break; +- case QAM_128: +- SignalToNoiseRel = SignalToNoise - 290; +- break; +- default: +- case QAM_256: +- SignalToNoiseRel = SignalToNoise - 320; +- break; +- } +- +- if (SignalToNoiseRel < -70) +- *pQuality = 0; +- else if (SignalToNoiseRel < 30) +- *pQuality = ((SignalToNoiseRel + 70) * +- BERQuality) / 100; +- else +- *pQuality = BERQuality; +- } while (0); +- +- return status; +-} +- +-static int GetQuality(struct drxk_state *state, s32 *pQuality) +-{ +- dprintk(1, "\n"); +- +- switch (state->m_OperationMode) { +- case OM_DVBT: +- return GetDVBTQuality(state, pQuality); +- case OM_QAM_ITU_A: +- return GetDVBCQuality(state, pQuality); +- default: +- break; +- } +- +- return 0; +-} +-#endif +- +-/* Free data ram in SIO HI */ +-#define SIO_HI_RA_RAM_USR_BEGIN__A 0x420040 +-#define SIO_HI_RA_RAM_USR_END__A 0x420060 +- +-#define DRXK_HI_ATOMIC_BUF_START (SIO_HI_RA_RAM_USR_BEGIN__A) +-#define DRXK_HI_ATOMIC_BUF_END (SIO_HI_RA_RAM_USR_BEGIN__A + 7) +-#define DRXK_HI_ATOMIC_READ SIO_HI_RA_RAM_PAR_3_ACP_RW_READ +-#define DRXK_HI_ATOMIC_WRITE SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE +- +-#define DRXDAP_FASI_ADDR2BLOCK(addr) (((addr) >> 22) & 0x3F) +-#define DRXDAP_FASI_ADDR2BANK(addr) (((addr) >> 16) & 0x3F) +-#define DRXDAP_FASI_ADDR2OFFSET(addr) ((addr) & 0x7FFF) +- +-static int ConfigureI2CBridge(struct drxk_state *state, bool bEnableBridge) +-{ +- int status = -EINVAL; +- +- dprintk(1, "\n"); +- +- if (state->m_DrxkState == DRXK_UNINITIALIZED) +- goto error; +- if (state->m_DrxkState == DRXK_POWERED_DOWN) +- goto error; +- +- if (state->no_i2c_bridge) +- return 0; +- +- status = write16(state, SIO_HI_RA_RAM_PAR_1__A, SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY); +- if (status < 0) +- goto error; +- if (bEnableBridge) { +- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED); +- if (status < 0) +- goto error; +- } else { +- status = write16(state, SIO_HI_RA_RAM_PAR_2__A, SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN); +- if (status < 0) +- goto error; +- } +- +- status = HI_Command(state, SIO_HI_RA_RAM_CMD_BRDCTRL, 0); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SetPreSaw(struct drxk_state *state, +- struct SCfgPreSaw *pPreSawCfg) +-{ +- int status = -EINVAL; +- +- dprintk(1, "\n"); +- +- if ((pPreSawCfg == NULL) +- || (pPreSawCfg->reference > IQM_AF_PDREF__M)) +- goto error; +- +- status = write16(state, IQM_AF_PDREF__A, pPreSawCfg->reference); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int BLDirectCmd(struct drxk_state *state, u32 targetAddr, +- u16 romOffset, u16 nrOfElements, u32 timeOut) +-{ +- u16 blStatus = 0; +- u16 offset = (u16) ((targetAddr >> 0) & 0x00FFFF); +- u16 blockbank = (u16) ((targetAddr >> 16) & 0x000FFF); +- int status; +- unsigned long end; +- +- dprintk(1, "\n"); +- +- mutex_lock(&state->mutex); +- status = write16(state, SIO_BL_MODE__A, SIO_BL_MODE_DIRECT); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_TGT_HDR__A, blockbank); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_TGT_ADDR__A, offset); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_SRC_ADDR__A, romOffset); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_SRC_LEN__A, nrOfElements); +- if (status < 0) +- goto error; +- status = write16(state, SIO_BL_ENABLE__A, SIO_BL_ENABLE_ON); +- if (status < 0) +- goto error; +- +- end = jiffies + msecs_to_jiffies(timeOut); +- do { +- status = read16(state, SIO_BL_STATUS__A, &blStatus); +- if (status < 0) +- goto error; +- } while ((blStatus == 0x1) && time_is_after_jiffies(end)); +- if (blStatus == 0x1) { +- printk(KERN_ERR "drxk: SIO not ready\n"); +- status = -EINVAL; +- goto error2; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +-error2: +- mutex_unlock(&state->mutex); +- return status; +- +-} +- +-static int ADCSyncMeasurement(struct drxk_state *state, u16 *count) +-{ +- u16 data = 0; +- int status; +- +- dprintk(1, "\n"); +- +- /* Start measurement */ +- status = write16(state, IQM_AF_COMM_EXEC__A, IQM_AF_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_START_LOCK__A, 1); +- if (status < 0) +- goto error; +- +- *count = 0; +- status = read16(state, IQM_AF_PHASE0__A, &data); +- if (status < 0) +- goto error; +- if (data == 127) +- *count = *count + 1; +- status = read16(state, IQM_AF_PHASE1__A, &data); +- if (status < 0) +- goto error; +- if (data == 127) +- *count = *count + 1; +- status = read16(state, IQM_AF_PHASE2__A, &data); +- if (status < 0) +- goto error; +- if (data == 127) +- *count = *count + 1; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int ADCSynchronization(struct drxk_state *state) +-{ +- u16 count = 0; +- int status; +- +- dprintk(1, "\n"); +- +- status = ADCSyncMeasurement(state, &count); +- if (status < 0) +- goto error; +- +- if (count == 1) { +- /* Try sampling on a diffrent edge */ +- u16 clkNeg = 0; +- +- status = read16(state, IQM_AF_CLKNEG__A, &clkNeg); +- if (status < 0) +- goto error; +- if ((clkNeg | IQM_AF_CLKNEG_CLKNEGDATA__M) == +- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS) { +- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); +- clkNeg |= +- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG; +- } else { +- clkNeg &= (~(IQM_AF_CLKNEG_CLKNEGDATA__M)); +- clkNeg |= +- IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS; +- } +- status = write16(state, IQM_AF_CLKNEG__A, clkNeg); +- if (status < 0) +- goto error; +- status = ADCSyncMeasurement(state, &count); +- if (status < 0) +- goto error; +- } +- +- if (count < 2) +- status = -EINVAL; +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SetFrequencyShifter(struct drxk_state *state, +- u16 intermediateFreqkHz, +- s32 tunerFreqOffset, bool isDTV) +-{ +- bool selectPosImage = false; +- u32 rfFreqResidual = tunerFreqOffset; +- u32 fmFrequencyShift = 0; +- bool tunerMirror = !state->m_bMirrorFreqSpect; +- u32 adcFreq; +- bool adcFlip; +- int status; +- u32 ifFreqActual; +- u32 samplingFrequency = (u32) (state->m_sysClockFreq / 3); +- u32 frequencyShift; +- bool imageToSelect; +- +- dprintk(1, "\n"); +- +- /* +- Program frequency shifter +- No need to account for mirroring on RF +- */ +- if (isDTV) { +- if ((state->m_OperationMode == OM_QAM_ITU_A) || +- (state->m_OperationMode == OM_QAM_ITU_C) || +- (state->m_OperationMode == OM_DVBT)) +- selectPosImage = true; +- else +- selectPosImage = false; +- } +- if (tunerMirror) +- /* tuner doesn't mirror */ +- ifFreqActual = intermediateFreqkHz + +- rfFreqResidual + fmFrequencyShift; +- else +- /* tuner mirrors */ +- ifFreqActual = intermediateFreqkHz - +- rfFreqResidual - fmFrequencyShift; +- if (ifFreqActual > samplingFrequency / 2) { +- /* adc mirrors */ +- adcFreq = samplingFrequency - ifFreqActual; +- adcFlip = true; +- } else { +- /* adc doesn't mirror */ +- adcFreq = ifFreqActual; +- adcFlip = false; +- } +- +- frequencyShift = adcFreq; +- imageToSelect = state->m_rfmirror ^ tunerMirror ^ +- adcFlip ^ selectPosImage; +- state->m_IqmFsRateOfs = +- Frac28a((frequencyShift), samplingFrequency); +- +- if (imageToSelect) +- state->m_IqmFsRateOfs = ~state->m_IqmFsRateOfs + 1; +- +- /* Program frequency shifter with tuner offset compensation */ +- /* frequencyShift += tunerFreqOffset; TODO */ +- status = write32(state, IQM_FS_RATE_OFS_LO__A, +- state->m_IqmFsRateOfs); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int InitAGC(struct drxk_state *state, bool isDTV) +-{ +- u16 ingainTgt = 0; +- u16 ingainTgtMin = 0; +- u16 ingainTgtMax = 0; +- u16 clpCyclen = 0; +- u16 clpSumMin = 0; +- u16 clpDirTo = 0; +- u16 snsSumMin = 0; +- u16 snsSumMax = 0; +- u16 clpSumMax = 0; +- u16 snsDirTo = 0; +- u16 kiInnergainMin = 0; +- u16 ifIaccuHiTgt = 0; +- u16 ifIaccuHiTgtMin = 0; +- u16 ifIaccuHiTgtMax = 0; +- u16 data = 0; +- u16 fastClpCtrlDelay = 0; +- u16 clpCtrlMode = 0; +- int status = 0; +- +- dprintk(1, "\n"); +- +- /* Common settings */ +- snsSumMax = 1023; +- ifIaccuHiTgtMin = 2047; +- clpCyclen = 500; +- clpSumMax = 1023; +- +- /* AGCInit() not available for DVBT; init done in microcode */ +- if (!IsQAM(state)) { +- printk(KERN_ERR "drxk: %s: mode %d is not DVB-C\n", __func__, state->m_OperationMode); +- return -EINVAL; +- } +- +- /* FIXME: Analog TV AGC require different settings */ +- +- /* Standard specific settings */ +- clpSumMin = 8; +- clpDirTo = (u16) -9; +- clpCtrlMode = 0; +- snsSumMin = 8; +- snsDirTo = (u16) -9; +- kiInnergainMin = (u16) -1030; +- ifIaccuHiTgtMax = 0x2380; +- ifIaccuHiTgt = 0x2380; +- ingainTgtMin = 0x0511; +- ingainTgt = 0x0511; +- ingainTgtMax = 5119; +- fastClpCtrlDelay = state->m_qamIfAgcCfg.FastClipCtrlDelay; +- +- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, fastClpCtrlDelay); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_AGC_CLP_CTRL_MODE__A, clpCtrlMode); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_INGAIN_TGT__A, ingainTgt); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MIN__A, ingainTgtMin); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, ingainTgtMax); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A, ifIaccuHiTgtMin); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A, ifIaccuHiTgtMax); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_IF_IACCU_LO__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_RF_IACCU_HI__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_RF_IACCU_LO__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_SUM_MAX__A, clpSumMax); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_SUM_MAX__A, snsSumMax); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_AGC_KI_INNERGAIN_MIN__A, kiInnergainMin); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_IF_IACCU_HI_TGT__A, ifIaccuHiTgt); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_CYCLEN__A, clpCyclen); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MAX__A, 1023); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_RF_SNS_DEV_MIN__A, (u16) -1023); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A, 50); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_AGC_KI_MAXMINGAIN_TH__A, 20); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_SUM_MIN__A, clpSumMin); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_SUM_MIN__A, snsSumMin); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_DIR_TO__A, clpDirTo); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_DIR_TO__A, snsDirTo); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_KI_MINGAIN__A, 0x7fff); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_KI_MAXGAIN__A, 0x0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_KI_MIN__A, 0x0117); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_KI_MAX__A, 0x0657); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_SUM__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_CYCCNT__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_DIR_WD__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_CLP_DIR_STP__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_SUM__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_CYCCNT__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_DIR_WD__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_DIR_STP__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_SNS_CYCLEN__A, 500); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_KI_CYCLEN__A, 500); +- if (status < 0) +- goto error; +- +- /* Initialize inner-loop KI gain factors */ +- status = read16(state, SCU_RAM_AGC_KI__A, &data); +- if (status < 0) +- goto error; +- +- data = 0x0657; +- data &= ~SCU_RAM_AGC_KI_RF__M; +- data |= (DRXK_KI_RAGC_QAM << SCU_RAM_AGC_KI_RF__B); +- data &= ~SCU_RAM_AGC_KI_IF__M; +- data |= (DRXK_KI_IAGC_QAM << SCU_RAM_AGC_KI_IF__B); +- +- status = write16(state, SCU_RAM_AGC_KI__A, data); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int DVBTQAMGetAccPktErr(struct drxk_state *state, u16 *packetErr) +-{ +- int status; +- +- dprintk(1, "\n"); +- if (packetErr == NULL) +- status = write16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, 0); +- else +- status = read16(state, SCU_RAM_FEC_ACCUM_PKT_FAILURES__A, packetErr); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int DVBTScCommand(struct drxk_state *state, +- u16 cmd, u16 subcmd, +- u16 param0, u16 param1, u16 param2, +- u16 param3, u16 param4) +-{ +- u16 curCmd = 0; +- u16 errCode = 0; +- u16 retryCnt = 0; +- u16 scExec = 0; +- int status; +- +- dprintk(1, "\n"); +- status = read16(state, OFDM_SC_COMM_EXEC__A, &scExec); +- if (scExec != 1) { +- /* SC is not running */ +- status = -EINVAL; +- } +- if (status < 0) +- goto error; +- +- /* Wait until sc is ready to receive command */ +- retryCnt = 0; +- do { +- msleep(1); +- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd); +- retryCnt++; +- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES)); +- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0)) +- goto error; +- +- /* Write sub-command */ +- switch (cmd) { +- /* All commands using sub-cmd */ +- case OFDM_SC_RA_RAM_CMD_PROC_START: +- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: +- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: +- status = write16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, subcmd); +- if (status < 0) +- goto error; +- break; +- default: +- /* Do nothing */ +- break; +- } +- +- /* Write needed parameters and the command */ +- switch (cmd) { +- /* All commands using 5 parameters */ +- /* All commands using 4 parameters */ +- /* All commands using 3 parameters */ +- /* All commands using 2 parameters */ +- case OFDM_SC_RA_RAM_CMD_PROC_START: +- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: +- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: +- status = write16(state, OFDM_SC_RA_RAM_PARAM1__A, param1); +- /* All commands using 1 parameters */ +- case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: +- case OFDM_SC_RA_RAM_CMD_USER_IO: +- status = write16(state, OFDM_SC_RA_RAM_PARAM0__A, param0); +- /* All commands using 0 parameters */ +- case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: +- case OFDM_SC_RA_RAM_CMD_NULL: +- /* Write command */ +- status = write16(state, OFDM_SC_RA_RAM_CMD__A, cmd); +- break; +- default: +- /* Unknown command */ +- status = -EINVAL; +- } +- if (status < 0) +- goto error; +- +- /* Wait until sc is ready processing command */ +- retryCnt = 0; +- do { +- msleep(1); +- status = read16(state, OFDM_SC_RA_RAM_CMD__A, &curCmd); +- retryCnt++; +- } while ((curCmd != 0) && (retryCnt < DRXK_MAX_RETRIES)); +- if (retryCnt >= DRXK_MAX_RETRIES && (status < 0)) +- goto error; +- +- /* Check for illegal cmd */ +- status = read16(state, OFDM_SC_RA_RAM_CMD_ADDR__A, &errCode); +- if (errCode == 0xFFFF) { +- /* illegal command */ +- status = -EINVAL; +- } +- if (status < 0) +- goto error; +- +- /* Retreive results parameters from SC */ +- switch (cmd) { +- /* All commands yielding 5 results */ +- /* All commands yielding 4 results */ +- /* All commands yielding 3 results */ +- /* All commands yielding 2 results */ +- /* All commands yielding 1 result */ +- case OFDM_SC_RA_RAM_CMD_USER_IO: +- case OFDM_SC_RA_RAM_CMD_GET_OP_PARAM: +- status = read16(state, OFDM_SC_RA_RAM_PARAM0__A, &(param0)); +- /* All commands yielding 0 results */ +- case OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING: +- case OFDM_SC_RA_RAM_CMD_SET_TIMER: +- case OFDM_SC_RA_RAM_CMD_PROC_START: +- case OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM: +- case OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM: +- case OFDM_SC_RA_RAM_CMD_NULL: +- break; +- default: +- /* Unknown command */ +- status = -EINVAL; +- break; +- } /* switch (cmd->cmd) */ +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int PowerUpDVBT(struct drxk_state *state) +-{ +- enum DRXPowerMode powerMode = DRX_POWER_UP; +- int status; +- +- dprintk(1, "\n"); +- status = CtrlPowerMode(state, &powerMode); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int DVBTCtrlSetIncEnable(struct drxk_state *state, bool *enabled) +-{ +- int status; +- +- dprintk(1, "\n"); +- if (*enabled == true) +- status = write16(state, IQM_CF_BYPASSDET__A, 0); +- else +- status = write16(state, IQM_CF_BYPASSDET__A, 1); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-#define DEFAULT_FR_THRES_8K 4000 +-static int DVBTCtrlSetFrEnable(struct drxk_state *state, bool *enabled) +-{ +- +- int status; +- +- dprintk(1, "\n"); +- if (*enabled == true) { +- /* write mask to 1 */ +- status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, +- DEFAULT_FR_THRES_8K); +- } else { +- /* write mask to 0 */ +- status = write16(state, OFDM_SC_RA_RAM_FR_THRES_8K__A, 0); +- } +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int DVBTCtrlSetEchoThreshold(struct drxk_state *state, +- struct DRXKCfgDvbtEchoThres_t *echoThres) +-{ +- u16 data = 0; +- int status; +- +- dprintk(1, "\n"); +- status = read16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, &data); +- if (status < 0) +- goto error; +- +- switch (echoThres->fftMode) { +- case DRX_FFTMODE_2K: +- data &= ~OFDM_SC_RA_RAM_ECHO_THRES_2K__M; +- data |= ((echoThres->threshold << +- OFDM_SC_RA_RAM_ECHO_THRES_2K__B) +- & (OFDM_SC_RA_RAM_ECHO_THRES_2K__M)); +- break; +- case DRX_FFTMODE_8K: +- data &= ~OFDM_SC_RA_RAM_ECHO_THRES_8K__M; +- data |= ((echoThres->threshold << +- OFDM_SC_RA_RAM_ECHO_THRES_8K__B) +- & (OFDM_SC_RA_RAM_ECHO_THRES_8K__M)); +- break; +- default: +- return -EINVAL; +- } +- +- status = write16(state, OFDM_SC_RA_RAM_ECHO_THRES__A, data); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int DVBTCtrlSetSqiSpeed(struct drxk_state *state, +- enum DRXKCfgDvbtSqiSpeed *speed) +-{ +- int status = -EINVAL; +- +- dprintk(1, "\n"); +- +- switch (*speed) { +- case DRXK_DVBT_SQI_SPEED_FAST: +- case DRXK_DVBT_SQI_SPEED_MEDIUM: +- case DRXK_DVBT_SQI_SPEED_SLOW: +- break; +- default: +- goto error; +- } +- status = write16(state, SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A, +- (u16) *speed); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief Activate DVBT specific presets +-* \param demod instance of demodulator. +-* \return DRXStatus_t. +-* +-* Called in DVBTSetStandard +-* +-*/ +-static int DVBTActivatePresets(struct drxk_state *state) +-{ +- int status; +- bool setincenable = false; +- bool setfrenable = true; +- +- struct DRXKCfgDvbtEchoThres_t echoThres2k = { 0, DRX_FFTMODE_2K }; +- struct DRXKCfgDvbtEchoThres_t echoThres8k = { 0, DRX_FFTMODE_8K }; +- +- dprintk(1, "\n"); +- status = DVBTCtrlSetIncEnable(state, &setincenable); +- if (status < 0) +- goto error; +- status = DVBTCtrlSetFrEnable(state, &setfrenable); +- if (status < 0) +- goto error; +- status = DVBTCtrlSetEchoThreshold(state, &echoThres2k); +- if (status < 0) +- goto error; +- status = DVBTCtrlSetEchoThreshold(state, &echoThres8k); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_AGC_INGAIN_TGT_MAX__A, state->m_dvbtIfAgcCfg.IngainTgtMax); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief Initialize channelswitch-independent settings for DVBT. +-* \param demod instance of demodulator. +-* \return DRXStatus_t. +-* +-* For ROM code channel filter taps are loaded from the bootloader. For microcode +-* the DVB-T taps from the drxk_filters.h are used. +-*/ +-static int SetDVBTStandard(struct drxk_state *state, +- enum OperationMode oMode) +-{ +- u16 cmdResult = 0; +- u16 data = 0; +- int status; +- +- dprintk(1, "\n"); +- +- PowerUpDVBT(state); +- /* added antenna switch */ +- SwitchAntennaToDVBT(state); +- /* send OFDM reset command */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- /* send OFDM setenv command */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- /* reset datapath for OFDM, processors first */ +- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); +- if (status < 0) +- goto error; +- +- /* IQM setup */ +- /* synchronize on ofdstate->m_festart */ +- status = write16(state, IQM_AF_UPD_SEL__A, 1); +- if (status < 0) +- goto error; +- /* window size for clipping ADC detection */ +- status = write16(state, IQM_AF_CLP_LEN__A, 0); +- if (status < 0) +- goto error; +- /* window size for for sense pre-SAW detection */ +- status = write16(state, IQM_AF_SNS_LEN__A, 0); +- if (status < 0) +- goto error; +- /* sense threshold for sense pre-SAW detection */ +- status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); +- if (status < 0) +- goto error; +- status = SetIqmAf(state, true); +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_AF_AGC_RF__A, 0); +- if (status < 0) +- goto error; +- +- /* Impulse noise cruncher setup */ +- status = write16(state, IQM_AF_INC_LCT__A, 0); /* crunch in IQM_CF */ +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_DET_LCT__A, 0); /* detect in IQM_CF */ +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_WND_LEN__A, 3); /* peak detector window length */ +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_RC_STRETCH__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_OUT_ENA__A, 0x4); /* enable output 2 */ +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_DS_ENA__A, 0x4); /* decimate output 2 */ +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_SCALE__A, 1600); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_SCALE_SH__A, 0); +- if (status < 0) +- goto error; +- +- /* virtual clipping threshold for clipping ADC detection */ +- status = write16(state, IQM_AF_CLP_TH__A, 448); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_DATATH__A, 495); /* crunching threshold */ +- if (status < 0) +- goto error; +- +- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_DVBT, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_CF_PKDTH__A, 2); /* peak detector threshold */ +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_POW_MEAS_LEN__A, 2); +- if (status < 0) +- goto error; +- /* enable power measurement interrupt */ +- status = write16(state, IQM_CF_COMM_INT_MSK__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); +- if (status < 0) +- goto error; +- +- /* IQM will not be reset from here, sync ADC and update/init AGC */ +- status = ADCSynchronization(state); +- if (status < 0) +- goto error; +- status = SetPreSaw(state, &state->m_dvbtPreSawCfg); +- if (status < 0) +- goto error; +- +- /* Halt SCU to enable safe non-atomic accesses */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); +- if (status < 0) +- goto error; +- +- status = SetAgcRf(state, &state->m_dvbtRfAgcCfg, true); +- if (status < 0) +- goto error; +- status = SetAgcIf(state, &state->m_dvbtIfAgcCfg, true); +- if (status < 0) +- goto error; +- +- /* Set Noise Estimation notch width and enable DC fix */ +- status = read16(state, OFDM_SC_RA_RAM_CONFIG__A, &data); +- if (status < 0) +- goto error; +- data |= OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M; +- status = write16(state, OFDM_SC_RA_RAM_CONFIG__A, data); +- if (status < 0) +- goto error; +- +- /* Activate SCU to enable SCU commands */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- +- if (!state->m_DRXK_A3_ROM_CODE) { +- /* AGCInit() is not done for DVBT, so set agcFastClipCtrlDelay */ +- status = write16(state, SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A, state->m_dvbtIfAgcCfg.FastClipCtrlDelay); +- if (status < 0) +- goto error; +- } +- +- /* OFDM_SC setup */ +-#ifdef COMPILE_FOR_NONRT +- status = write16(state, OFDM_SC_RA_RAM_BE_OPT_DELAY__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A, 2); +- if (status < 0) +- goto error; +-#endif +- +- /* FEC setup */ +- status = write16(state, FEC_DI_INPUT_CTL__A, 1); /* OFDM input */ +- if (status < 0) +- goto error; +- +- +-#ifdef COMPILE_FOR_NONRT +- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x400); +- if (status < 0) +- goto error; +-#else +- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, 0x1000); +- if (status < 0) +- goto error; +-#endif +- status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, 0x0001); +- if (status < 0) +- goto error; +- +- /* Setup MPEG bus */ +- status = MPEGTSDtoSetup(state, OM_DVBT); +- if (status < 0) +- goto error; +- /* Set DVBT Presets */ +- status = DVBTActivatePresets(state); +- if (status < 0) +- goto error; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +-/** +-* \brief Start dvbt demodulating for channel. +-* \param demod instance of demodulator. +-* \return DRXStatus_t. +-*/ +-static int DVBTStart(struct drxk_state *state) +-{ +- u16 param1; +- int status; +- /* DRXKOfdmScCmd_t scCmd; */ +- +- dprintk(1, "\n"); +- /* Start correct processes to get in lock */ +- /* DRXK: OFDM_SC_RA_RAM_PROC_LOCKTRACK is no longer in mapfile! */ +- param1 = OFDM_SC_RA_RAM_LOCKTRACK_MIN; +- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_PROC_START, 0, OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M, param1, 0, 0, 0); +- if (status < 0) +- goto error; +- /* Start FEC OC */ +- status = MPEGTSStart(state); +- if (status < 0) +- goto error; +- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +- +-/*============================================================================*/ +- +-/** +-* \brief Set up dvbt demodulator for channel. +-* \param demod instance of demodulator. +-* \return DRXStatus_t. +-* // original DVBTSetChannel() +-*/ +-static int SetDVBT(struct drxk_state *state, u16 IntermediateFreqkHz, +- s32 tunerFreqOffset) +-{ +- u16 cmdResult = 0; +- u16 transmissionParams = 0; +- u16 operationMode = 0; +- u32 iqmRcRateOfs = 0; +- u32 bandwidth = 0; +- u16 param1; +- int status; +- +- dprintk(1, "IF =%d, TFO = %d\n", IntermediateFreqkHz, tunerFreqOffset); +- +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- /* Halt SCU to enable safe non-atomic accesses */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); +- if (status < 0) +- goto error; +- +- /* Stop processors */ +- status = write16(state, OFDM_SC_COMM_EXEC__A, OFDM_SC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_LC_COMM_EXEC__A, OFDM_LC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- +- /* Mandatory fix, always stop CP, required to set spl offset back to +- hardware default (is set to 0 by ucode during pilot detection */ +- status = write16(state, OFDM_CP_COMM_EXEC__A, OFDM_CP_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- +- /*== Write channel settings to device =====================================*/ +- +- /* mode */ +- switch (state->props.transmission_mode) { +- case TRANSMISSION_MODE_AUTO: +- default: +- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_MODE__M; +- /* fall through , try first guess DRX_FFTMODE_8K */ +- case TRANSMISSION_MODE_8K: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_8K; +- break; +- case TRANSMISSION_MODE_2K: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_MODE_2K; +- break; +- } +- +- /* guard */ +- switch (state->props.guard_interval) { +- default: +- case GUARD_INTERVAL_AUTO: +- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_GUARD__M; +- /* fall through , try first guess DRX_GUARD_1DIV4 */ +- case GUARD_INTERVAL_1_4: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_4; +- break; +- case GUARD_INTERVAL_1_32: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_32; +- break; +- case GUARD_INTERVAL_1_16: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_16; +- break; +- case GUARD_INTERVAL_1_8: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_GUARD_8; +- break; +- } +- +- /* hierarchy */ +- switch (state->props.hierarchy) { +- case HIERARCHY_AUTO: +- case HIERARCHY_NONE: +- default: +- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_HIER__M; +- /* fall through , try first guess SC_RA_RAM_OP_PARAM_HIER_NO */ +- /* transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_NO; */ +- /* break; */ +- case HIERARCHY_1: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A1; +- break; +- case HIERARCHY_2: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A2; +- break; +- case HIERARCHY_4: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_HIER_A4; +- break; +- } +- +- +- /* modulation */ +- switch (state->props.modulation) { +- case QAM_AUTO: +- default: +- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_CONST__M; +- /* fall through , try first guess DRX_CONSTELLATION_QAM64 */ +- case QAM_64: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64; +- break; +- case QPSK: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK; +- break; +- case QAM_16: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16; +- break; +- } +-#if 0 +- /* No hierachical channels support in BDA */ +- /* Priority (only for hierarchical channels) */ +- switch (channel->priority) { +- case DRX_PRIORITY_LOW: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO; +- WR16(devAddr, OFDM_EC_SB_PRIOR__A, +- OFDM_EC_SB_PRIOR_LO); +- break; +- case DRX_PRIORITY_HIGH: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; +- WR16(devAddr, OFDM_EC_SB_PRIOR__A, +- OFDM_EC_SB_PRIOR_HI)); +- break; +- case DRX_PRIORITY_UNKNOWN: /* fall through */ +- default: +- status = -EINVAL; +- goto error; +- } +-#else +- /* Set Priorty high */ +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI; +- status = write16(state, OFDM_EC_SB_PRIOR__A, OFDM_EC_SB_PRIOR_HI); +- if (status < 0) +- goto error; +-#endif +- +- /* coderate */ +- switch (state->props.code_rate_HP) { +- case FEC_AUTO: +- default: +- operationMode |= OFDM_SC_RA_RAM_OP_AUTO_RATE__M; +- /* fall through , try first guess DRX_CODERATE_2DIV3 */ +- case FEC_2_3: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3; +- break; +- case FEC_1_2: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2; +- break; +- case FEC_3_4: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4; +- break; +- case FEC_5_6: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6; +- break; +- case FEC_7_8: +- transmissionParams |= OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8; +- break; +- } +- +- /* SAW filter selection: normaly not necesarry, but if wanted +- the application can select a SAW filter via the driver by using UIOs */ +- /* First determine real bandwidth (Hz) */ +- /* Also set delay for impulse noise cruncher */ +- /* Also set parameters for EC_OC fix, note EC_OC_REG_TMD_HIL_MAR is changed +- by SC for fix for some 8K,1/8 guard but is restored by InitEC and ResetEC +- functions */ +- switch (state->props.bandwidth_hz) { +- case 0: +- state->props.bandwidth_hz = 8000000; +- /* fall though */ +- case 8000000: +- bandwidth = DRXK_BANDWIDTH_8MHZ_IN_HZ; +- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3052); +- if (status < 0) +- goto error; +- /* cochannel protection for PAL 8 MHz */ +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 7); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 7); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 7); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); +- if (status < 0) +- goto error; +- break; +- case 7000000: +- bandwidth = DRXK_BANDWIDTH_7MHZ_IN_HZ; +- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 3491); +- if (status < 0) +- goto error; +- /* cochannel protection for PAL 7 MHz */ +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); +- if (status < 0) +- goto error; +- break; +- case 6000000: +- bandwidth = DRXK_BANDWIDTH_6MHZ_IN_HZ; +- status = write16(state, OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A, 4073); +- if (status < 0) +- goto error; +- /* cochannel protection for NTSC 6 MHz */ +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A, 19); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A, 19); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A, 14); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A, 1); +- if (status < 0) +- goto error; +- break; +- default: +- status = -EINVAL; +- goto error; +- } +- +- if (iqmRcRateOfs == 0) { +- /* Now compute IQM_RC_RATE_OFS +- (((SysFreq/BandWidth)/2)/2) -1) * 2^23) +- => +- ((SysFreq / BandWidth) * (2^21)) - (2^23) +- */ +- /* (SysFreq / BandWidth) * (2^28) */ +- /* assert (MAX(sysClk)/MIN(bandwidth) < 16) +- => assert(MAX(sysClk) < 16*MIN(bandwidth)) +- => assert(109714272 > 48000000) = true so Frac 28 can be used */ +- iqmRcRateOfs = Frac28a((u32) +- ((state->m_sysClockFreq * +- 1000) / 3), bandwidth); +- /* (SysFreq / BandWidth) * (2^21), rounding before truncating */ +- if ((iqmRcRateOfs & 0x7fL) >= 0x40) +- iqmRcRateOfs += 0x80L; +- iqmRcRateOfs = iqmRcRateOfs >> 7; +- /* ((SysFreq / BandWidth) * (2^21)) - (2^23) */ +- iqmRcRateOfs = iqmRcRateOfs - (1 << 23); +- } +- +- iqmRcRateOfs &= +- ((((u32) IQM_RC_RATE_OFS_HI__M) << +- IQM_RC_RATE_OFS_LO__W) | IQM_RC_RATE_OFS_LO__M); +- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRateOfs); +- if (status < 0) +- goto error; +- +- /* Bandwidth setting done */ +- +-#if 0 +- status = DVBTSetFrequencyShift(demod, channel, tunerOffset); +- if (status < 0) +- goto error; +-#endif +- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true); +- if (status < 0) +- goto error; +- +- /*== Start SC, write channel settings to SC ===============================*/ +- +- /* Activate SCU to enable SCU commands */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- +- /* Enable SC after setting all other parameters */ +- status = write16(state, OFDM_SC_COMM_STATE__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, OFDM_SC_COMM_EXEC__A, 1); +- if (status < 0) +- goto error; +- +- +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_OFDM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- /* Write SC parameter registers, set all AUTO flags in operation mode */ +- param1 = (OFDM_SC_RA_RAM_OP_AUTO_MODE__M | +- OFDM_SC_RA_RAM_OP_AUTO_GUARD__M | +- OFDM_SC_RA_RAM_OP_AUTO_CONST__M | +- OFDM_SC_RA_RAM_OP_AUTO_HIER__M | +- OFDM_SC_RA_RAM_OP_AUTO_RATE__M); +- status = DVBTScCommand(state, OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM, +- 0, transmissionParams, param1, 0, 0, 0); +- if (status < 0) +- goto error; +- +- if (!state->m_DRXK_A3_ROM_CODE) +- status = DVBTCtrlSetSqiSpeed(state, &state->m_sqiSpeed); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +- +-/*============================================================================*/ +- +-/** +-* \brief Retreive lock status . +-* \param demod Pointer to demodulator instance. +-* \param lockStat Pointer to lock status structure. +-* \return DRXStatus_t. +-* +-*/ +-static int GetDVBTLockStatus(struct drxk_state *state, u32 *pLockStatus) +-{ +- int status; +- const u16 mpeg_lock_mask = (OFDM_SC_RA_RAM_LOCK_MPEG__M | +- OFDM_SC_RA_RAM_LOCK_FEC__M); +- const u16 fec_lock_mask = (OFDM_SC_RA_RAM_LOCK_FEC__M); +- const u16 demod_lock_mask = OFDM_SC_RA_RAM_LOCK_DEMOD__M; +- +- u16 ScRaRamLock = 0; +- u16 ScCommExec = 0; +- +- dprintk(1, "\n"); +- +- *pLockStatus = NOT_LOCKED; +- /* driver 0.9.0 */ +- /* Check if SC is running */ +- status = read16(state, OFDM_SC_COMM_EXEC__A, &ScCommExec); +- if (status < 0) +- goto end; +- if (ScCommExec == OFDM_SC_COMM_EXEC_STOP) +- goto end; +- +- status = read16(state, OFDM_SC_RA_RAM_LOCK__A, &ScRaRamLock); +- if (status < 0) +- goto end; +- +- if ((ScRaRamLock & mpeg_lock_mask) == mpeg_lock_mask) +- *pLockStatus = MPEG_LOCK; +- else if ((ScRaRamLock & fec_lock_mask) == fec_lock_mask) +- *pLockStatus = FEC_LOCK; +- else if ((ScRaRamLock & demod_lock_mask) == demod_lock_mask) +- *pLockStatus = DEMOD_LOCK; +- else if (ScRaRamLock & OFDM_SC_RA_RAM_LOCK_NODVBT__M) +- *pLockStatus = NEVER_LOCK; +-end: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int PowerUpQAM(struct drxk_state *state) +-{ +- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; +- int status; +- +- dprintk(1, "\n"); +- status = CtrlPowerMode(state, &powerMode); +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +- +-/** Power Down QAM */ +-static int PowerDownQAM(struct drxk_state *state) +-{ +- u16 data = 0; +- u16 cmdResult; +- int status = 0; +- +- dprintk(1, "\n"); +- status = read16(state, SCU_COMM_EXEC__A, &data); +- if (status < 0) +- goto error; +- if (data == SCU_COMM_EXEC_ACTIVE) { +- /* +- STOP demodulator +- QAM and HW blocks +- */ +- /* stop all comstate->m_exec */ +- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_STOP, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- } +- /* powerdown AFE */ +- status = SetIqmAf(state, false); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief Setup of the QAM Measurement intervals for signal quality +-* \param demod instance of demod. +-* \param modulation current modulation. +-* \return DRXStatus_t. +-* +-* NOTE: +-* Take into account that for certain settings the errorcounters can overflow. +-* The implementation does not check this. +-* +-*/ +-static int SetQAMMeasurement(struct drxk_state *state, +- enum EDrxkConstellation modulation, +- u32 symbolRate) +-{ +- u32 fecBitsDesired = 0; /* BER accounting period */ +- u32 fecRsPeriodTotal = 0; /* Total period */ +- u16 fecRsPrescale = 0; /* ReedSolomon Measurement Prescale */ +- u16 fecRsPeriod = 0; /* Value for corresponding I2C register */ +- int status = 0; +- +- dprintk(1, "\n"); +- +- fecRsPrescale = 1; +- /* fecBitsDesired = symbolRate [kHz] * +- FrameLenght [ms] * +- (modulation + 1) * +- SyncLoss (== 1) * +- ViterbiLoss (==1) +- */ +- switch (modulation) { +- case DRX_CONSTELLATION_QAM16: +- fecBitsDesired = 4 * symbolRate; +- break; +- case DRX_CONSTELLATION_QAM32: +- fecBitsDesired = 5 * symbolRate; +- break; +- case DRX_CONSTELLATION_QAM64: +- fecBitsDesired = 6 * symbolRate; +- break; +- case DRX_CONSTELLATION_QAM128: +- fecBitsDesired = 7 * symbolRate; +- break; +- case DRX_CONSTELLATION_QAM256: +- fecBitsDesired = 8 * symbolRate; +- break; +- default: +- status = -EINVAL; +- } +- if (status < 0) +- goto error; +- +- fecBitsDesired /= 1000; /* symbolRate [Hz] -> symbolRate [kHz] */ +- fecBitsDesired *= 500; /* meas. period [ms] */ +- +- /* Annex A/C: bits/RsPeriod = 204 * 8 = 1632 */ +- /* fecRsPeriodTotal = fecBitsDesired / 1632 */ +- fecRsPeriodTotal = (fecBitsDesired / 1632UL) + 1; /* roughly ceil */ +- +- /* fecRsPeriodTotal = fecRsPrescale * fecRsPeriod */ +- fecRsPrescale = 1 + (u16) (fecRsPeriodTotal >> 16); +- if (fecRsPrescale == 0) { +- /* Divide by zero (though impossible) */ +- status = -EINVAL; +- if (status < 0) +- goto error; +- } +- fecRsPeriod = +- ((u16) fecRsPeriodTotal + +- (fecRsPrescale >> 1)) / fecRsPrescale; +- +- /* write corresponding registers */ +- status = write16(state, FEC_RS_MEASUREMENT_PERIOD__A, fecRsPeriod); +- if (status < 0) +- goto error; +- status = write16(state, FEC_RS_MEASUREMENT_PRESCALE__A, fecRsPrescale); +- if (status < 0) +- goto error; +- status = write16(state, FEC_OC_SNC_FAIL_PERIOD__A, fecRsPeriod); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SetQAM16(struct drxk_state *state) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- /* QAM Equalizer Setup */ +- /* Equalizer */ +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13517); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 13517); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 13517); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13517); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13517); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 13517); +- if (status < 0) +- goto error; +- /* Decision Feedback Equalizer */ +- status = write16(state, QAM_DQ_QUAL_FUN0__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN1__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN2__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN3__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN4__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_SY_SYNC_HWM__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_AWM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_LWM__A, 3); +- if (status < 0) +- goto error; +- +- /* QAM Slicer Settings */ +- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM16); +- if (status < 0) +- goto error; +- +- /* QAM Loop Controller Coeficients */ +- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 32); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); +- if (status < 0) +- goto error; +- +- +- /* QAM State Machine (FSM) Thresholds */ +- +- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 140); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 95); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 120); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 230); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 105); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 24); +- if (status < 0) +- goto error; +- +- +- /* QAM FSM Tracking Parameters */ +- +- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 220); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 25); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 6); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -65); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -127); +- if (status < 0) +- goto error; +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief QAM32 specific setup +-* \param demod instance of demod. +-* \return DRXStatus_t. +-*/ +-static int SetQAM32(struct drxk_state *state) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- +- /* QAM Equalizer Setup */ +- /* Equalizer */ +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6707); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6707); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6707); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6707); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6707); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 6707); +- if (status < 0) +- goto error; +- +- /* Decision Feedback Equalizer */ +- status = write16(state, QAM_DQ_QUAL_FUN0__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN1__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN2__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN3__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_SY_SYNC_HWM__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_AWM__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_LWM__A, 3); +- if (status < 0) +- goto error; +- +- /* QAM Slicer Settings */ +- +- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM32); +- if (status < 0) +- goto error; +- +- +- /* QAM Loop Controller Coeficients */ +- +- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 20); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 20); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); +- if (status < 0) +- goto error; +- +- +- /* QAM State Machine (FSM) Thresholds */ +- +- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 90); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 170); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 10); +- if (status < 0) +- goto error; +- +- +- /* QAM FSM Tracking Parameters */ +- +- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 140); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) -8); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) -16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -26); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -56); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -86); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief QAM64 specific setup +-* \param demod instance of demod. +-* \return DRXStatus_t. +-*/ +-static int SetQAM64(struct drxk_state *state) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- /* QAM Equalizer Setup */ +- /* Equalizer */ +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 13336); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12618); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 11988); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 13809); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13809); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15609); +- if (status < 0) +- goto error; +- +- /* Decision Feedback Equalizer */ +- status = write16(state, QAM_DQ_QUAL_FUN0__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN1__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN2__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN3__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN4__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_SY_SYNC_HWM__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_AWM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_LWM__A, 3); +- if (status < 0) +- goto error; +- +- /* QAM Slicer Settings */ +- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM64); +- if (status < 0) +- goto error; +- +- +- /* QAM Loop Controller Coeficients */ +- +- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 30); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 100); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 30); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); +- if (status < 0) +- goto error; +- +- +- /* QAM State Machine (FSM) Thresholds */ +- +- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 100); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 110); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 200); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 95); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 15); +- if (status < 0) +- goto error; +- +- +- /* QAM FSM Tracking Parameters */ +- +- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 141); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 7); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -45); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -80); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief QAM128 specific setup +-* \param demod: instance of demod. +-* \return DRXStatus_t. +-*/ +-static int SetQAM128(struct drxk_state *state) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- /* QAM Equalizer Setup */ +- /* Equalizer */ +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 6564); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 6598); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 6394); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 6409); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 6656); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 7238); +- if (status < 0) +- goto error; +- +- /* Decision Feedback Equalizer */ +- status = write16(state, QAM_DQ_QUAL_FUN0__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN1__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN2__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN3__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN4__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_SY_SYNC_HWM__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_AWM__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_LWM__A, 3); +- if (status < 0) +- goto error; +- +- +- /* QAM Slicer Settings */ +- +- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM128); +- if (status < 0) +- goto error; +- +- +- /* QAM Loop Controller Coeficients */ +- +- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 120); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 60); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 64); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 0); +- if (status < 0) +- goto error; +- +- +- /* QAM State Machine (FSM) Thresholds */ +- +- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 140); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 100); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 5); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); +- if (status < 0) +- goto error; +- +- /* QAM FSM Tracking Parameters */ +- +- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 65); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 3); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) -1); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) -12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -23); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief QAM256 specific setup +-* \param demod: instance of demod. +-* \return DRXStatus_t. +-*/ +-static int SetQAM256(struct drxk_state *state) +-{ +- int status = 0; +- +- dprintk(1, "\n"); +- /* QAM Equalizer Setup */ +- /* Equalizer */ +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD0__A, 11502); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD1__A, 12084); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD2__A, 12543); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD3__A, 12931); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD4__A, 13629); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_EQ_CMA_RAD5__A, 15385); +- if (status < 0) +- goto error; +- +- /* Decision Feedback Equalizer */ +- status = write16(state, QAM_DQ_QUAL_FUN0__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN1__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN2__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN3__A, 8); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN4__A, 6); +- if (status < 0) +- goto error; +- status = write16(state, QAM_DQ_QUAL_FUN5__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_SY_SYNC_HWM__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_AWM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_SYNC_LWM__A, 3); +- if (status < 0) +- goto error; +- +- /* QAM Slicer Settings */ +- +- status = write16(state, SCU_RAM_QAM_SL_SIG_POWER__A, DRXK_QAM_SL_SIG_POWER_QAM256); +- if (status < 0) +- goto error; +- +- +- /* QAM Loop Controller Coeficients */ +- +- status = write16(state, SCU_RAM_QAM_LC_CA_FINE__A, 15); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CA_COARSE__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_MEDIUM__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EP_COARSE__A, 24); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_FINE__A, 12); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_MEDIUM__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_EI_COARSE__A, 16); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_LC_CP_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_MEDIUM__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CP_COARSE__A, 250); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_MEDIUM__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CI_COARSE__A, 125); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_FINE__A, 16); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_MEDIUM__A, 25); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF_COARSE__A, 48); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_FINE__A, 5); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_MEDIUM__A, 10); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_LC_CF1_COARSE__A, 10); +- if (status < 0) +- goto error; +- +- +- /* QAM State Machine (FSM) Thresholds */ +- +- status = write16(state, SCU_RAM_QAM_FSM_RTH__A, 50); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FTH__A, 60); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_CTH__A, 80); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_PTH__A, 100); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_QTH__A, 150); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_MTH__A, 110); +- if (status < 0) +- goto error; +- +- status = write16(state, SCU_RAM_QAM_FSM_RATE_LIM__A, 40); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_COUNT_LIM__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_FREQ_LIM__A, 12); +- if (status < 0) +- goto error; +- +- +- /* QAM FSM Tracking Parameters */ +- +- status = write16(state, SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A, (u16) 8); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A, (u16) 74); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A, (u16) 18); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A, (u16) 13); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A, (u16) 7); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A, (u16) 0); +- if (status < 0) +- goto error; +- status = write16(state, SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A, (u16) -8); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +- +-/*============================================================================*/ +-/** +-* \brief Reset QAM block. +-* \param demod: instance of demod. +-* \param channel: pointer to channel data. +-* \return DRXStatus_t. +-*/ +-static int QAMResetQAM(struct drxk_state *state) +-{ +- int status; +- u16 cmdResult; +- +- dprintk(1, "\n"); +- /* Stop QAM comstate->m_exec */ +- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_RESET, 0, NULL, 1, &cmdResult); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief Set QAM symbolrate. +-* \param demod: instance of demod. +-* \param channel: pointer to channel data. +-* \return DRXStatus_t. +-*/ +-static int QAMSetSymbolrate(struct drxk_state *state) +-{ +- u32 adcFrequency = 0; +- u32 symbFreq = 0; +- u32 iqmRcRate = 0; +- u16 ratesel = 0; +- u32 lcSymbRate = 0; +- int status; +- +- dprintk(1, "\n"); +- /* Select & calculate correct IQM rate */ +- adcFrequency = (state->m_sysClockFreq * 1000) / 3; +- ratesel = 0; +- /* printk(KERN_DEBUG "drxk: SR %d\n", state->props.symbol_rate); */ +- if (state->props.symbol_rate <= 1188750) +- ratesel = 3; +- else if (state->props.symbol_rate <= 2377500) +- ratesel = 2; +- else if (state->props.symbol_rate <= 4755000) +- ratesel = 1; +- status = write16(state, IQM_FD_RATESEL__A, ratesel); +- if (status < 0) +- goto error; +- +- /* +- IqmRcRate = ((Fadc / (symbolrate * (4<props.symbol_rate * (1 << ratesel); +- if (symbFreq == 0) { +- /* Divide by zero */ +- status = -EINVAL; +- goto error; +- } +- iqmRcRate = (adcFrequency / symbFreq) * (1 << 21) + +- (Frac28a((adcFrequency % symbFreq), symbFreq) >> 7) - +- (1 << 23); +- status = write32(state, IQM_RC_RATE_OFS_LO__A, iqmRcRate); +- if (status < 0) +- goto error; +- state->m_iqmRcRate = iqmRcRate; +- /* +- LcSymbFreq = round (.125 * symbolrate / adcFreq * (1<<15)) +- */ +- symbFreq = state->props.symbol_rate; +- if (adcFrequency == 0) { +- /* Divide by zero */ +- status = -EINVAL; +- goto error; +- } +- lcSymbRate = (symbFreq / adcFrequency) * (1 << 12) + +- (Frac28a((symbFreq % adcFrequency), adcFrequency) >> +- 16); +- if (lcSymbRate > 511) +- lcSymbRate = 511; +- status = write16(state, QAM_LC_SYMBOL_FREQ__A, (u16) lcSymbRate); +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-/*============================================================================*/ +- +-/** +-* \brief Get QAM lock status. +-* \param demod: instance of demod. +-* \param channel: pointer to channel data. +-* \return DRXStatus_t. +-*/ +- +-static int GetQAMLockStatus(struct drxk_state *state, u32 *pLockStatus) +-{ +- int status; +- u16 Result[2] = { 0, 0 }; +- +- dprintk(1, "\n"); +- *pLockStatus = NOT_LOCKED; +- status = scu_command(state, +- SCU_RAM_COMMAND_STANDARD_QAM | +- SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK, 0, NULL, 2, +- Result); +- if (status < 0) +- printk(KERN_ERR "drxk: %s status = %08x\n", __func__, status); +- +- if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED) { +- /* 0x0000 NOT LOCKED */ +- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_LOCKED) { +- /* 0x4000 DEMOD LOCKED */ +- *pLockStatus = DEMOD_LOCK; +- } else if (Result[1] < SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK) { +- /* 0x8000 DEMOD + FEC LOCKED (system lock) */ +- *pLockStatus = MPEG_LOCK; +- } else { +- /* 0xC000 NEVER LOCKED */ +- /* (system will never be able to lock to the signal) */ +- /* TODO: check this, intermediate & standard specific lock states are not +- taken into account here */ +- *pLockStatus = NEVER_LOCK; +- } +- return status; +-} +- +-#define QAM_MIRROR__M 0x03 +-#define QAM_MIRROR_NORMAL 0x00 +-#define QAM_MIRRORED 0x01 +-#define QAM_MIRROR_AUTO_ON 0x02 +-#define QAM_LOCKRANGE__M 0x10 +-#define QAM_LOCKRANGE_NORMAL 0x10 +- +-static int SetQAM(struct drxk_state *state, u16 IntermediateFreqkHz, +- s32 tunerFreqOffset) +-{ +- int status; +- u16 setParamParameters[4] = { 0, 0, 0, 0 }; +- u16 cmdResult; +- +- dprintk(1, "\n"); +- /* +- * STEP 1: reset demodulator +- * resets FEC DI and FEC RS +- * resets QAM block +- * resets SCU variables +- */ +- status = write16(state, FEC_DI_COMM_EXEC__A, FEC_DI_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, FEC_RS_COMM_EXEC__A, FEC_RS_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = QAMResetQAM(state); +- if (status < 0) +- goto error; +- +- /* +- * STEP 2: configure demodulator +- * -set params; resets IQM,QAM,FEC HW; initializes some +- * SCU variables +- */ +- status = QAMSetSymbolrate(state); +- if (status < 0) +- goto error; +- +- /* Set params */ +- switch (state->props.modulation) { +- case QAM_256: +- state->m_Constellation = DRX_CONSTELLATION_QAM256; +- break; +- case QAM_AUTO: +- case QAM_64: +- state->m_Constellation = DRX_CONSTELLATION_QAM64; +- break; +- case QAM_16: +- state->m_Constellation = DRX_CONSTELLATION_QAM16; +- break; +- case QAM_32: +- state->m_Constellation = DRX_CONSTELLATION_QAM32; +- break; +- case QAM_128: +- state->m_Constellation = DRX_CONSTELLATION_QAM128; +- break; +- default: +- status = -EINVAL; +- break; +- } +- if (status < 0) +- goto error; +- setParamParameters[0] = state->m_Constellation; /* modulation */ +- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ +- if (state->m_OperationMode == OM_QAM_ITU_C) +- setParamParameters[2] = QAM_TOP_ANNEX_C; +- else +- setParamParameters[2] = QAM_TOP_ANNEX_A; +- setParamParameters[3] |= (QAM_MIRROR_AUTO_ON); +- /* Env parameters */ +- /* check for LOCKRANGE Extented */ +- /* setParamParameters[3] |= QAM_LOCKRANGE_NORMAL; */ +- +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 4, setParamParameters, 1, &cmdResult); +- if (status < 0) { +- /* Fall-back to the simpler call */ +- if (state->m_OperationMode == OM_QAM_ITU_C) +- setParamParameters[0] = QAM_TOP_ANNEX_C; +- else +- setParamParameters[0] = QAM_TOP_ANNEX_A; +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV, 1, setParamParameters, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- setParamParameters[0] = state->m_Constellation; /* modulation */ +- setParamParameters[1] = DRXK_QAM_I12_J17; /* interleave mode */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM, 2, setParamParameters, 1, &cmdResult); +- } +- if (status < 0) +- goto error; +- +- /* +- * STEP 3: enable the system in a mode where the ADC provides valid +- * signal setup modulation independent registers +- */ +-#if 0 +- status = SetFrequency(channel, tunerFreqOffset)); +- if (status < 0) +- goto error; +-#endif +- status = SetFrequencyShifter(state, IntermediateFreqkHz, tunerFreqOffset, true); +- if (status < 0) +- goto error; +- +- /* Setup BER measurement */ +- status = SetQAMMeasurement(state, state->m_Constellation, state->props.symbol_rate); +- if (status < 0) +- goto error; +- +- /* Reset default values */ +- status = write16(state, IQM_CF_SCALE_SH__A, IQM_CF_SCALE_SH__PRE); +- if (status < 0) +- goto error; +- status = write16(state, QAM_SY_TIMEOUT__A, QAM_SY_TIMEOUT__PRE); +- if (status < 0) +- goto error; +- +- /* Reset default LC values */ +- status = write16(state, QAM_LC_RATE_LIMIT__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_LPF_FACTORP__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_LPF_FACTORI__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_MODE__A, 7); +- if (status < 0) +- goto error; +- +- status = write16(state, QAM_LC_QUAL_TAB0__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB1__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB2__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB3__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB4__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB5__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB6__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB8__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB9__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB10__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB12__A, 2); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB15__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB16__A, 3); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB20__A, 4); +- if (status < 0) +- goto error; +- status = write16(state, QAM_LC_QUAL_TAB25__A, 4); +- if (status < 0) +- goto error; +- +- /* Mirroring, QAM-block starting point not inverted */ +- status = write16(state, QAM_SY_SP_INV__A, QAM_SY_SP_INV_SPECTRUM_INV_DIS); +- if (status < 0) +- goto error; +- +- /* Halt SCU to enable safe non-atomic accesses */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); +- if (status < 0) +- goto error; +- +- /* STEP 4: modulation specific setup */ +- switch (state->props.modulation) { +- case QAM_16: +- status = SetQAM16(state); +- break; +- case QAM_32: +- status = SetQAM32(state); +- break; +- case QAM_AUTO: +- case QAM_64: +- status = SetQAM64(state); +- break; +- case QAM_128: +- status = SetQAM128(state); +- break; +- case QAM_256: +- status = SetQAM256(state); +- break; +- default: +- status = -EINVAL; +- break; +- } +- if (status < 0) +- goto error; +- +- /* Activate SCU to enable SCU commands */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- +- /* Re-configure MPEG output, requires knowledge of channel bitrate */ +- /* extAttr->currentChannel.modulation = channel->modulation; */ +- /* extAttr->currentChannel.symbolrate = channel->symbolrate; */ +- status = MPEGTSDtoSetup(state, state->m_OperationMode); +- if (status < 0) +- goto error; +- +- /* Start processes */ +- status = MPEGTSStart(state); +- if (status < 0) +- goto error; +- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- status = write16(state, QAM_COMM_EXEC__A, QAM_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_ACTIVE); +- if (status < 0) +- goto error; +- +- /* STEP 5: start QAM demodulator (starts FEC, QAM and IQM HW) */ +- status = scu_command(state, SCU_RAM_COMMAND_STANDARD_QAM | SCU_RAM_COMMAND_CMD_DEMOD_START, 0, NULL, 1, &cmdResult); +- if (status < 0) +- goto error; +- +- /* update global DRXK data container */ +-/*? extAttr->qamInterleaveMode = DRXK_QAM_I12_J17; */ +- +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SetQAMStandard(struct drxk_state *state, +- enum OperationMode oMode) +-{ +- int status; +-#ifdef DRXK_QAM_TAPS +-#define DRXK_QAMA_TAPS_SELECT +-#include "drxk_filters.h" +-#undef DRXK_QAMA_TAPS_SELECT +-#endif +- +- dprintk(1, "\n"); +- +- /* added antenna switch */ +- SwitchAntennaToQAM(state); +- +- /* Ensure correct power-up mode */ +- status = PowerUpQAM(state); +- if (status < 0) +- goto error; +- /* Reset QAM block */ +- status = QAMResetQAM(state); +- if (status < 0) +- goto error; +- +- /* Setup IQM */ +- +- status = write16(state, IQM_COMM_EXEC__A, IQM_COMM_EXEC_B_STOP); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_AMUX__A, IQM_AF_AMUX_SIGNAL2ADC); +- if (status < 0) +- goto error; +- +- /* Upload IQM Channel Filter settings by +- boot loader from ROM table */ +- switch (oMode) { +- case OM_QAM_ITU_A: +- status = BLChainCmd(state, DRXK_BL_ROM_OFFSET_TAPS_ITU_A, DRXK_BLCC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); +- break; +- case OM_QAM_ITU_C: +- status = BLDirectCmd(state, IQM_CF_TAP_RE0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); +- if (status < 0) +- goto error; +- status = BLDirectCmd(state, IQM_CF_TAP_IM0__A, DRXK_BL_ROM_OFFSET_TAPS_ITU_C, DRXK_BLDC_NR_ELEMENTS_TAPS, DRXK_BLC_TIMEOUT); +- break; +- default: +- status = -EINVAL; +- } +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_CF_OUT_ENA__A, (1 << IQM_CF_OUT_ENA_QAM__B)); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_SYMMETRIC__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_MIDTAP__A, ((1 << IQM_CF_MIDTAP_RE__B) | (1 << IQM_CF_MIDTAP_IM__B))); +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_RC_STRETCH__A, 21); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_CLP_LEN__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_CLP_TH__A, 448); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_SNS_LEN__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_POW_MEAS_LEN__A, 0); +- if (status < 0) +- goto error; +- +- status = write16(state, IQM_FS_ADJ_SEL__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_RC_ADJ_SEL__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_ADJ_SEL__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_UPD_SEL__A, 0); +- if (status < 0) +- goto error; +- +- /* IQM Impulse Noise Processing Unit */ +- status = write16(state, IQM_CF_CLP_VAL__A, 500); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_DATATH__A, 1000); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_BYPASSDET__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_DET_LCT__A, 0); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_WND_LEN__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_CF_PKDTH__A, 1); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_INC_BYPASS__A, 1); +- if (status < 0) +- goto error; +- +- /* turn on IQMAF. Must be done before setAgc**() */ +- status = SetIqmAf(state, true); +- if (status < 0) +- goto error; +- status = write16(state, IQM_AF_START_LOCK__A, 0x01); +- if (status < 0) +- goto error; +- +- /* IQM will not be reset from here, sync ADC and update/init AGC */ +- status = ADCSynchronization(state); +- if (status < 0) +- goto error; +- +- /* Set the FSM step period */ +- status = write16(state, SCU_RAM_QAM_FSM_STEP_PERIOD__A, 2000); +- if (status < 0) +- goto error; +- +- /* Halt SCU to enable safe non-atomic accesses */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_HOLD); +- if (status < 0) +- goto error; +- +- /* No more resets of the IQM, current standard correctly set => +- now AGCs can be configured. */ +- +- status = InitAGC(state, true); +- if (status < 0) +- goto error; +- status = SetPreSaw(state, &(state->m_qamPreSawCfg)); +- if (status < 0) +- goto error; +- +- /* Configure AGC's */ +- status = SetAgcRf(state, &(state->m_qamRfAgcCfg), true); +- if (status < 0) +- goto error; +- status = SetAgcIf(state, &(state->m_qamIfAgcCfg), true); +- if (status < 0) +- goto error; +- +- /* Activate SCU to enable SCU commands */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int WriteGPIO(struct drxk_state *state) +-{ +- int status; +- u16 value = 0; +- +- dprintk(1, "\n"); +- /* stop lock indicator process */ +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- +- /* Write magic word to enable pdr reg write */ +- status = write16(state, SIO_TOP_COMM_KEY__A, SIO_TOP_COMM_KEY_KEY); +- if (status < 0) +- goto error; +- +- if (state->m_hasSAWSW) { +- if (state->UIO_mask & 0x0001) { /* UIO-1 */ +- /* write to io pad configuration register - output mode */ +- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); +- if (status < 0) +- goto error; +- +- /* use corresponding bit in io data output registar */ +- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); +- if (status < 0) +- goto error; +- if ((state->m_GPIO & 0x0001) == 0) +- value &= 0x7FFF; /* write zero to 15th bit - 1st UIO */ +- else +- value |= 0x8000; /* write one to 15th bit - 1st UIO */ +- /* write back to io data output register */ +- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); +- if (status < 0) +- goto error; +- } +- if (state->UIO_mask & 0x0002) { /* UIO-2 */ +- /* write to io pad configuration register - output mode */ +- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); +- if (status < 0) +- goto error; +- +- /* use corresponding bit in io data output registar */ +- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); +- if (status < 0) +- goto error; +- if ((state->m_GPIO & 0x0002) == 0) +- value &= 0xBFFF; /* write zero to 14th bit - 2st UIO */ +- else +- value |= 0x4000; /* write one to 14th bit - 2st UIO */ +- /* write back to io data output register */ +- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); +- if (status < 0) +- goto error; +- } +- if (state->UIO_mask & 0x0004) { /* UIO-3 */ +- /* write to io pad configuration register - output mode */ +- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); +- if (status < 0) +- goto error; +- +- /* use corresponding bit in io data output registar */ +- status = read16(state, SIO_PDR_UIO_OUT_LO__A, &value); +- if (status < 0) +- goto error; +- if ((state->m_GPIO & 0x0004) == 0) +- value &= 0xFFFB; /* write zero to 2nd bit - 3rd UIO */ +- else +- value |= 0x0004; /* write one to 2nd bit - 3rd UIO */ +- /* write back to io data output register */ +- status = write16(state, SIO_PDR_UIO_OUT_LO__A, value); +- if (status < 0) +- goto error; +- } +- } +- /* Write magic word to disable pdr reg write */ +- status = write16(state, SIO_TOP_COMM_KEY__A, 0x0000); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SwitchAntennaToQAM(struct drxk_state *state) +-{ +- int status = 0; +- bool gpio_state; +- +- dprintk(1, "\n"); +- +- if (!state->antenna_gpio) +- return 0; +- +- gpio_state = state->m_GPIO & state->antenna_gpio; +- +- if (state->antenna_dvbt ^ gpio_state) { +- /* Antenna is on DVB-T mode. Switch */ +- if (state->antenna_dvbt) +- state->m_GPIO &= ~state->antenna_gpio; +- else +- state->m_GPIO |= state->antenna_gpio; +- status = WriteGPIO(state); +- } +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +-static int SwitchAntennaToDVBT(struct drxk_state *state) +-{ +- int status = 0; +- bool gpio_state; +- +- dprintk(1, "\n"); +- +- if (!state->antenna_gpio) +- return 0; +- +- gpio_state = state->m_GPIO & state->antenna_gpio; +- +- if (!(state->antenna_dvbt ^ gpio_state)) { +- /* Antenna is on DVB-C mode. Switch */ +- if (state->antenna_dvbt) +- state->m_GPIO |= state->antenna_gpio; +- else +- state->m_GPIO &= ~state->antenna_gpio; +- status = WriteGPIO(state); +- } +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- return status; +-} +- +- +-static int PowerDownDevice(struct drxk_state *state) +-{ +- /* Power down to requested mode */ +- /* Backup some register settings */ +- /* Set pins with possible pull-ups connected to them in input mode */ +- /* Analog power down */ +- /* ADC power down */ +- /* Power down device */ +- int status; +- +- dprintk(1, "\n"); +- if (state->m_bPDownOpenBridge) { +- /* Open I2C bridge before power down of DRXK */ +- status = ConfigureI2CBridge(state, true); +- if (status < 0) +- goto error; +- } +- /* driver 0.9.0 */ +- status = DVBTEnableOFDMTokenRing(state, false); +- if (status < 0) +- goto error; +- +- status = write16(state, SIO_CC_PWD_MODE__A, SIO_CC_PWD_MODE_LEVEL_CLOCK); +- if (status < 0) +- goto error; +- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); +- if (status < 0) +- goto error; +- state->m_HICfgCtrl |= SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ; +- status = HI_CfgCommand(state); +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static int load_microcode(struct drxk_state *state, const char *mc_name) +-{ +- const struct firmware *fw = NULL; +- int err = 0; +- +- dprintk(1, "\n"); +- +- err = request_firmware(&fw, mc_name, state->i2c->dev.parent); +- if (err < 0) { +- printk(KERN_ERR +- "drxk: Could not load firmware file %s.\n", mc_name); +- printk(KERN_INFO +- "drxk: Copy %s to your hotplug directory!\n", mc_name); +- return err; +- } +- err = DownloadMicrocode(state, fw->data, fw->size); +- release_firmware(fw); +- return err; +-} +- +-static int init_drxk(struct drxk_state *state) +-{ +- int status = 0; +- enum DRXPowerMode powerMode = DRXK_POWER_DOWN_OFDM; +- u16 driverVersion; +- +- dprintk(1, "\n"); +- if ((state->m_DrxkState == DRXK_UNINITIALIZED)) { +- status = PowerUpDevice(state); +- if (status < 0) +- goto error; +- status = DRXX_Open(state); +- if (status < 0) +- goto error; +- /* Soft reset of OFDM-, sys- and osc-clockdomain */ +- status = write16(state, SIO_CC_SOFT_RST__A, SIO_CC_SOFT_RST_OFDM__M | SIO_CC_SOFT_RST_SYS__M | SIO_CC_SOFT_RST_OSC__M); +- if (status < 0) +- goto error; +- status = write16(state, SIO_CC_UPDATE__A, SIO_CC_UPDATE_KEY); +- if (status < 0) +- goto error; +- /* TODO is this needed, if yes how much delay in worst case scenario */ +- msleep(1); +- state->m_DRXK_A3_PATCH_CODE = true; +- status = GetDeviceCapabilities(state); +- if (status < 0) +- goto error; +- +- /* Bridge delay, uses oscilator clock */ +- /* Delay = (delay (nano seconds) * oscclk (kHz))/ 1000 */ +- /* SDA brdige delay */ +- state->m_HICfgBridgeDelay = +- (u16) ((state->m_oscClockFreq / 1000) * +- HI_I2C_BRIDGE_DELAY) / 1000; +- /* Clipping */ +- if (state->m_HICfgBridgeDelay > +- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M) { +- state->m_HICfgBridgeDelay = +- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M; +- } +- /* SCL bridge delay, same as SDA for now */ +- state->m_HICfgBridgeDelay += +- state->m_HICfgBridgeDelay << +- SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B; +- +- status = InitHI(state); +- if (status < 0) +- goto error; +- /* disable various processes */ +-#if NOA1ROM +- if (!(state->m_DRXK_A1_ROM_CODE) +- && !(state->m_DRXK_A2_ROM_CODE)) +-#endif +- { +- status = write16(state, SCU_RAM_GPIO__A, SCU_RAM_GPIO_HW_LOCK_IND_DISABLE); +- if (status < 0) +- goto error; +- } +- +- /* disable MPEG port */ +- status = MPEGTSDisable(state); +- if (status < 0) +- goto error; +- +- /* Stop AUD and SCU */ +- status = write16(state, AUD_COMM_EXEC__A, AUD_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- +- /* enable token-ring bus through OFDM block for possible ucode upload */ +- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_ON); +- if (status < 0) +- goto error; +- +- /* include boot loader section */ +- status = write16(state, SIO_BL_COMM_EXEC__A, SIO_BL_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- status = BLChainCmd(state, 0, 6, 100); +- if (status < 0) +- goto error; +- +- if (!state->microcode_name) +- load_microcode(state, "drxk_a3.mc"); +- else +- load_microcode(state, state->microcode_name); +- +- /* disable token-ring bus through OFDM block for possible ucode upload */ +- status = write16(state, SIO_OFDM_SH_OFDM_RING_ENABLE__A, SIO_OFDM_SH_OFDM_RING_ENABLE_OFF); +- if (status < 0) +- goto error; +- +- /* Run SCU for a little while to initialize microcode version numbers */ +- status = write16(state, SCU_COMM_EXEC__A, SCU_COMM_EXEC_ACTIVE); +- if (status < 0) +- goto error; +- status = DRXX_Open(state); +- if (status < 0) +- goto error; +- /* added for test */ +- msleep(30); +- +- powerMode = DRXK_POWER_DOWN_OFDM; +- status = CtrlPowerMode(state, &powerMode); +- if (status < 0) +- goto error; +- +- /* Stamp driver version number in SCU data RAM in BCD code +- Done to enable field application engineers to retreive drxdriver version +- via I2C from SCU RAM. +- Not using SCU command interface for SCU register access since no +- microcode may be present. +- */ +- driverVersion = +- (((DRXK_VERSION_MAJOR / 100) % 10) << 12) + +- (((DRXK_VERSION_MAJOR / 10) % 10) << 8) + +- ((DRXK_VERSION_MAJOR % 10) << 4) + +- (DRXK_VERSION_MINOR % 10); +- status = write16(state, SCU_RAM_DRIVER_VER_HI__A, driverVersion); +- if (status < 0) +- goto error; +- driverVersion = +- (((DRXK_VERSION_PATCH / 1000) % 10) << 12) + +- (((DRXK_VERSION_PATCH / 100) % 10) << 8) + +- (((DRXK_VERSION_PATCH / 10) % 10) << 4) + +- (DRXK_VERSION_PATCH % 10); +- status = write16(state, SCU_RAM_DRIVER_VER_LO__A, driverVersion); +- if (status < 0) +- goto error; +- +- printk(KERN_INFO "DRXK driver version %d.%d.%d\n", +- DRXK_VERSION_MAJOR, DRXK_VERSION_MINOR, +- DRXK_VERSION_PATCH); +- +- /* Dirty fix of default values for ROM/PATCH microcode +- Dirty because this fix makes it impossible to setup suitable values +- before calling DRX_Open. This solution requires changes to RF AGC speed +- to be done via the CTRL function after calling DRX_Open */ +- +- /* m_dvbtRfAgcCfg.speed = 3; */ +- +- /* Reset driver debug flags to 0 */ +- status = write16(state, SCU_RAM_DRIVER_DEBUG__A, 0); +- if (status < 0) +- goto error; +- /* driver 0.9.0 */ +- /* Setup FEC OC: +- NOTE: No more full FEC resets allowed afterwards!! */ +- status = write16(state, FEC_COMM_EXEC__A, FEC_COMM_EXEC_STOP); +- if (status < 0) +- goto error; +- /* MPEGTS functions are still the same */ +- status = MPEGTSDtoInit(state); +- if (status < 0) +- goto error; +- status = MPEGTSStop(state); +- if (status < 0) +- goto error; +- status = MPEGTSConfigurePolarity(state); +- if (status < 0) +- goto error; +- status = MPEGTSConfigurePins(state, state->m_enableMPEGOutput); +- if (status < 0) +- goto error; +- /* added: configure GPIO */ +- status = WriteGPIO(state); +- if (status < 0) +- goto error; +- +- state->m_DrxkState = DRXK_STOPPED; +- +- if (state->m_bPowerDown) { +- status = PowerDownDevice(state); +- if (status < 0) +- goto error; +- state->m_DrxkState = DRXK_POWERED_DOWN; +- } else +- state->m_DrxkState = DRXK_STOPPED; +- } +-error: +- if (status < 0) +- printk(KERN_ERR "drxk: Error %d on %s\n", status, __func__); +- +- return status; +-} +- +-static void drxk_release(struct dvb_frontend *fe) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- +- dprintk(1, "\n"); +- kfree(state); +-} +- +-static int drxk_sleep(struct dvb_frontend *fe) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- +- dprintk(1, "\n"); +- ShutDown(state); +- return 0; +-} +- +-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- +- dprintk(1, "%s\n", enable ? "enable" : "disable"); +- return ConfigureI2CBridge(state, enable ? true : false); +-} +- +-static int drxk_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- u32 delsys = p->delivery_system, old_delsys; +- struct drxk_state *state = fe->demodulator_priv; +- u32 IF; +- +- dprintk(1, "\n"); +- +- if (!fe->ops.tuner_ops.get_if_frequency) { +- printk(KERN_ERR +- "drxk: Error: get_if_frequency() not defined at tuner. Can't work without it!\n"); +- return -EINVAL; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- old_delsys = state->props.delivery_system; +- state->props = *p; +- +- if (old_delsys != delsys) { +- ShutDown(state); +- switch (delsys) { +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- if (!state->m_hasDVBC) +- return -EINVAL; +- state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ? true : false; +- if (state->m_itut_annex_c) +- SetOperationMode(state, OM_QAM_ITU_C); +- else +- SetOperationMode(state, OM_QAM_ITU_A); +- break; +- case SYS_DVBT: +- if (!state->m_hasDVBT) +- return -EINVAL; +- SetOperationMode(state, OM_DVBT); +- break; +- default: +- return -EINVAL; +- } +- } +- +- fe->ops.tuner_ops.get_if_frequency(fe, &IF); +- Start(state, 0, IF); +- +- /* printk(KERN_DEBUG "drxk: %s IF=%d done\n", __func__, IF); */ +- +- return 0; +-} +- +-static int drxk_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- u32 stat; +- +- dprintk(1, "\n"); +- *status = 0; +- GetLockStatus(state, &stat, 0); +- if (stat == MPEG_LOCK) +- *status |= 0x1f; +- if (stat == FEC_LOCK) +- *status |= 0x0f; +- if (stat == DEMOD_LOCK) +- *status |= 0x07; +- return 0; +-} +- +-static int drxk_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- dprintk(1, "\n"); +- +- *ber = 0; +- return 0; +-} +- +-static int drxk_read_signal_strength(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- u32 val = 0; +- +- dprintk(1, "\n"); +- ReadIFAgc(state, &val); +- *strength = val & 0xffff; +- return 0; +-} +- +-static int drxk_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- s32 snr2; +- +- dprintk(1, "\n"); +- GetSignalToNoise(state, &snr2); +- *snr = snr2 & 0xffff; +- return 0; +-} +- +-static int drxk_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct drxk_state *state = fe->demodulator_priv; +- u16 err; +- +- dprintk(1, "\n"); +- DVBTQAMGetAccPktErr(state, &err); +- *ucblocks = (u32) err; +- return 0; +-} +- +-static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings +- *sets) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- dprintk(1, "\n"); +- switch (p->delivery_system) { +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- sets->min_delay_ms = 3000; +- sets->max_drift = 0; +- sets->step_size = 0; +- return 0; +- default: +- /* +- * For DVB-T, let it use the default DVB core way, that is: +- * fepriv->step_size = fe->ops.info.frequency_stepsize * 2 +- */ +- return -EINVAL; +- } +-} +- +-static struct dvb_frontend_ops drxk_ops = { +- /* .delsys will be filled dynamically */ +- .info = { +- .name = "DRXK", +- .frequency_min = 47000000, +- .frequency_max = 865000000, +- /* For DVB-C */ +- .symbol_rate_min = 870000, +- .symbol_rate_max = 11700000, +- /* For DVB-T */ +- .frequency_stepsize = 166667, +- +- .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_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_MUTE_TS | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | +- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO +- }, +- +- .release = drxk_release, +- .sleep = drxk_sleep, +- .i2c_gate_ctrl = drxk_gate_ctrl, +- +- .set_frontend = drxk_set_parameters, +- .get_tune_settings = drxk_get_tune_settings, +- +- .read_status = drxk_read_status, +- .read_ber = drxk_read_ber, +- .read_signal_strength = drxk_read_signal_strength, +- .read_snr = drxk_read_snr, +- .read_ucblocks = drxk_read_ucblocks, +-}; +- +-struct dvb_frontend *drxk_attach(const struct drxk_config *config, +- struct i2c_adapter *i2c) +-{ +- int n; +- +- struct drxk_state *state = NULL; +- u8 adr = config->adr; +- +- dprintk(1, "\n"); +- state = kzalloc(sizeof(struct drxk_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- state->i2c = i2c; +- state->demod_address = adr; +- state->single_master = config->single_master; +- state->microcode_name = config->microcode_name; +- state->no_i2c_bridge = config->no_i2c_bridge; +- state->antenna_gpio = config->antenna_gpio; +- state->antenna_dvbt = config->antenna_dvbt; +- state->m_ChunkSize = config->chunk_size; +- +- if (config->parallel_ts) +- state->m_enableParallel = true; +- else +- state->m_enableParallel = false; +- +- /* NOTE: as more UIO bits will be used, add them to the mask */ +- state->UIO_mask = config->antenna_gpio; +- +- /* Default gpio to DVB-C */ +- if (!state->antenna_dvbt && state->antenna_gpio) +- state->m_GPIO |= state->antenna_gpio; +- else +- state->m_GPIO &= ~state->antenna_gpio; +- +- mutex_init(&state->mutex); +- +- memcpy(&state->frontend.ops, &drxk_ops, sizeof(drxk_ops)); +- state->frontend.demodulator_priv = state; +- +- init_state(state); +- if (init_drxk(state) < 0) +- goto error; +- +- /* Initialize the supported delivery systems */ +- n = 0; +- if (state->m_hasDVBC) { +- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_A; +- state->frontend.ops.delsys[n++] = SYS_DVBC_ANNEX_C; +- strlcat(state->frontend.ops.info.name, " DVB-C", +- sizeof(state->frontend.ops.info.name)); +- } +- if (state->m_hasDVBT) { +- state->frontend.ops.delsys[n++] = SYS_DVBT; +- strlcat(state->frontend.ops.info.name, " DVB-T", +- sizeof(state->frontend.ops.info.name)); +- } +- +- printk(KERN_INFO "drxk: frontend initialized.\n"); +- return &state->frontend; +- +-error: +- printk(KERN_ERR "drxk: not found\n"); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(drxk_attach); +- +-MODULE_DESCRIPTION("DRX-K driver"); +-MODULE_AUTHOR("Ralph Metzler"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h +deleted file mode 100644 +index 3a58b73..0000000 +--- a/drivers/media/dvb/frontends/drxk_hard.h ++++ /dev/null +@@ -1,348 +0,0 @@ +-#include "drxk_map.h" +- +-#define DRXK_VERSION_MAJOR 0 +-#define DRXK_VERSION_MINOR 9 +-#define DRXK_VERSION_PATCH 4300 +- +-#define HI_I2C_DELAY 42 +-#define HI_I2C_BRIDGE_DELAY 350 +-#define DRXK_MAX_RETRIES 100 +- +-#define DRIVER_4400 1 +- +-#define DRXX_JTAGID 0x039210D9 +-#define DRXX_J_JTAGID 0x239310D9 +-#define DRXX_K_JTAGID 0x039210D9 +- +-#define DRX_UNKNOWN 254 +-#define DRX_AUTO 255 +- +-#define DRX_SCU_READY 0 +-#define DRXK_MAX_WAITTIME (200) +-#define SCU_RESULT_OK 0 +-#define SCU_RESULT_SIZE -4 +-#define SCU_RESULT_INVPAR -3 +-#define SCU_RESULT_UNKSTD -2 +-#define SCU_RESULT_UNKCMD -1 +- +-#ifndef DRXK_OFDM_TR_SHUTDOWN_TIMEOUT +-#define DRXK_OFDM_TR_SHUTDOWN_TIMEOUT (200) +-#endif +- +-#define DRXK_8VSB_MPEG_BIT_RATE 19392658UL /*bps*/ +-#define DRXK_DVBT_MPEG_BIT_RATE 32000000UL /*bps*/ +-#define DRXK_QAM16_MPEG_BIT_RATE 27000000UL /*bps*/ +-#define DRXK_QAM32_MPEG_BIT_RATE 33000000UL /*bps*/ +-#define DRXK_QAM64_MPEG_BIT_RATE 40000000UL /*bps*/ +-#define DRXK_QAM128_MPEG_BIT_RATE 46000000UL /*bps*/ +-#define DRXK_QAM256_MPEG_BIT_RATE 52000000UL /*bps*/ +-#define DRXK_MAX_MPEG_BIT_RATE 52000000UL /*bps*/ +- +-#define IQM_CF_OUT_ENA_OFDM__M 0x4 +-#define IQM_FS_ADJ_SEL_B_QAM 0x1 +-#define IQM_FS_ADJ_SEL_B_OFF 0x0 +-#define IQM_FS_ADJ_SEL_B_VSB 0x2 +-#define IQM_RC_ADJ_SEL_B_OFF 0x0 +-#define IQM_RC_ADJ_SEL_B_QAM 0x1 +-#define IQM_RC_ADJ_SEL_B_VSB 0x2 +- +-enum OperationMode { +- OM_NONE, +- OM_QAM_ITU_A, +- OM_QAM_ITU_B, +- OM_QAM_ITU_C, +- OM_DVBT +-}; +- +-enum DRXPowerMode { +- DRX_POWER_UP = 0, +- DRX_POWER_MODE_1, +- DRX_POWER_MODE_2, +- DRX_POWER_MODE_3, +- DRX_POWER_MODE_4, +- DRX_POWER_MODE_5, +- DRX_POWER_MODE_6, +- DRX_POWER_MODE_7, +- DRX_POWER_MODE_8, +- +- DRX_POWER_MODE_9, +- DRX_POWER_MODE_10, +- DRX_POWER_MODE_11, +- DRX_POWER_MODE_12, +- DRX_POWER_MODE_13, +- DRX_POWER_MODE_14, +- DRX_POWER_MODE_15, +- DRX_POWER_MODE_16, +- DRX_POWER_DOWN = 255 +-}; +- +- +-/** /brief Intermediate power mode for DRXK, power down OFDM clock domain */ +-#ifndef DRXK_POWER_DOWN_OFDM +-#define DRXK_POWER_DOWN_OFDM DRX_POWER_MODE_1 +-#endif +- +-/** /brief Intermediate power mode for DRXK, power down core (sysclk) */ +-#ifndef DRXK_POWER_DOWN_CORE +-#define DRXK_POWER_DOWN_CORE DRX_POWER_MODE_9 +-#endif +- +-/** /brief Intermediate power mode for DRXK, power down pll (only osc runs) */ +-#ifndef DRXK_POWER_DOWN_PLL +-#define DRXK_POWER_DOWN_PLL DRX_POWER_MODE_10 +-#endif +- +- +-enum AGC_CTRL_MODE { DRXK_AGC_CTRL_AUTO = 0, DRXK_AGC_CTRL_USER, DRXK_AGC_CTRL_OFF }; +-enum EDrxkState { DRXK_UNINITIALIZED = 0, DRXK_STOPPED, DRXK_DTV_STARTED, DRXK_ATV_STARTED, DRXK_POWERED_DOWN }; +-enum EDrxkCoefArrayIndex { +- DRXK_COEF_IDX_MN = 0, +- DRXK_COEF_IDX_FM , +- DRXK_COEF_IDX_L , +- DRXK_COEF_IDX_LP , +- DRXK_COEF_IDX_BG , +- DRXK_COEF_IDX_DK , +- DRXK_COEF_IDX_I , +- DRXK_COEF_IDX_MAX +-}; +-enum EDrxkSifAttenuation { +- DRXK_SIF_ATTENUATION_0DB, +- DRXK_SIF_ATTENUATION_3DB, +- DRXK_SIF_ATTENUATION_6DB, +- DRXK_SIF_ATTENUATION_9DB +-}; +-enum EDrxkConstellation { +- DRX_CONSTELLATION_BPSK = 0, +- DRX_CONSTELLATION_QPSK, +- DRX_CONSTELLATION_PSK8, +- DRX_CONSTELLATION_QAM16, +- DRX_CONSTELLATION_QAM32, +- DRX_CONSTELLATION_QAM64, +- DRX_CONSTELLATION_QAM128, +- DRX_CONSTELLATION_QAM256, +- DRX_CONSTELLATION_QAM512, +- DRX_CONSTELLATION_QAM1024, +- DRX_CONSTELLATION_UNKNOWN = DRX_UNKNOWN, +- DRX_CONSTELLATION_AUTO = DRX_AUTO +-}; +-enum EDrxkInterleaveMode { +- DRXK_QAM_I12_J17 = 16, +- DRXK_QAM_I_UNKNOWN = DRX_UNKNOWN +-}; +-enum { +- DRXK_SPIN_A1 = 0, +- DRXK_SPIN_A2, +- DRXK_SPIN_A3, +- DRXK_SPIN_UNKNOWN +-}; +- +-enum DRXKCfgDvbtSqiSpeed { +- DRXK_DVBT_SQI_SPEED_FAST = 0, +- DRXK_DVBT_SQI_SPEED_MEDIUM, +- DRXK_DVBT_SQI_SPEED_SLOW, +- DRXK_DVBT_SQI_SPEED_UNKNOWN = DRX_UNKNOWN +-} ; +- +-enum DRXFftmode_t { +- DRX_FFTMODE_2K = 0, +- DRX_FFTMODE_4K, +- DRX_FFTMODE_8K, +- DRX_FFTMODE_UNKNOWN = DRX_UNKNOWN, +- DRX_FFTMODE_AUTO = DRX_AUTO +-}; +- +-enum DRXMPEGStrWidth_t { +- DRX_MPEG_STR_WIDTH_1, +- DRX_MPEG_STR_WIDTH_8 +-}; +- +-enum DRXQamLockRange_t { +- DRX_QAM_LOCKRANGE_NORMAL, +- DRX_QAM_LOCKRANGE_EXTENDED +-}; +- +-struct DRXKCfgDvbtEchoThres_t { +- u16 threshold; +- enum DRXFftmode_t fftMode; +-} ; +- +-struct SCfgAgc { +- enum AGC_CTRL_MODE ctrlMode; /* off, user, auto */ +- u16 outputLevel; /* range dependent on AGC */ +- u16 minOutputLevel; /* range dependent on AGC */ +- u16 maxOutputLevel; /* range dependent on AGC */ +- u16 speed; /* range dependent on AGC */ +- u16 top; /* rf-agc take over point */ +- u16 cutOffCurrent; /* rf-agc is accelerated if output current +- is below cut-off current */ +- u16 IngainTgtMax; +- u16 FastClipCtrlDelay; +-}; +- +-struct SCfgPreSaw { +- u16 reference; /* pre SAW reference value, range 0 .. 31 */ +- bool usePreSaw; /* TRUE algorithms must use pre SAW sense */ +-}; +- +-struct DRXKOfdmScCmd_t { +- u16 cmd; /**< Command number */ +- u16 subcmd; /**< Sub-command parameter*/ +- u16 param0; /**< General purpous param */ +- u16 param1; /**< General purpous param */ +- u16 param2; /**< General purpous param */ +- u16 param3; /**< General purpous param */ +- u16 param4; /**< General purpous param */ +-}; +- +-struct drxk_state { +- struct dvb_frontend frontend; +- struct dtv_frontend_properties props; +- struct device *dev; +- +- struct i2c_adapter *i2c; +- u8 demod_address; +- void *priv; +- +- struct mutex mutex; +- +- u32 m_Instance; /**< Channel 1,2,3 or 4 */ +- +- int m_ChunkSize; +- u8 Chunk[256]; +- +- bool m_hasLNA; +- bool m_hasDVBT; +- bool m_hasDVBC; +- bool m_hasAudio; +- bool m_hasATV; +- bool m_hasOOB; +- bool m_hasSAWSW; /**< TRUE if mat_tx is available */ +- bool m_hasGPIO1; /**< TRUE if mat_rx is available */ +- bool m_hasGPIO2; /**< TRUE if GPIO is available */ +- bool m_hasIRQN; /**< TRUE if IRQN is available */ +- u16 m_oscClockFreq; +- u16 m_HICfgTimingDiv; +- u16 m_HICfgBridgeDelay; +- u16 m_HICfgWakeUpKey; +- u16 m_HICfgTimeout; +- u16 m_HICfgCtrl; +- s32 m_sysClockFreq; /**< system clock frequency in kHz */ +- +- enum EDrxkState m_DrxkState; /**< State of Drxk (init,stopped,started) */ +- enum OperationMode m_OperationMode; /**< digital standards */ +- struct SCfgAgc m_vsbRfAgcCfg; /**< settings for VSB RF-AGC */ +- struct SCfgAgc m_vsbIfAgcCfg; /**< settings for VSB IF-AGC */ +- u16 m_vsbPgaCfg; /**< settings for VSB PGA */ +- struct SCfgPreSaw m_vsbPreSawCfg; /**< settings for pre SAW sense */ +- s32 m_Quality83percent; /**< MER level (*0.1 dB) for 83% quality indication */ +- s32 m_Quality93percent; /**< MER level (*0.1 dB) for 93% quality indication */ +- bool m_smartAntInverted; +- bool m_bDebugEnableBridge; +- bool m_bPDownOpenBridge; /**< only open DRXK bridge before power-down once it has been accessed */ +- bool m_bPowerDown; /**< Power down when not used */ +- +- u32 m_IqmFsRateOfs; /**< frequency shift as written to DRXK register (28bit fixpoint) */ +- +- bool m_enableMPEGOutput; /**< If TRUE, enable MPEG output */ +- bool m_insertRSByte; /**< If TRUE, insert RS byte */ +- bool m_enableParallel; /**< If TRUE, parallel out otherwise serial */ +- bool m_invertDATA; /**< If TRUE, invert DATA signals */ +- bool m_invertERR; /**< If TRUE, invert ERR signal */ +- bool m_invertSTR; /**< If TRUE, invert STR signals */ +- bool m_invertVAL; /**< If TRUE, invert VAL signals */ +- bool m_invertCLK; /**< If TRUE, invert CLK signals */ +- bool m_DVBCStaticCLK; +- bool m_DVBTStaticCLK; /**< If TRUE, static MPEG clockrate will +- be used, otherwise clockrate will +- adapt to the bitrate of the TS */ +- u32 m_DVBTBitrate; +- u32 m_DVBCBitrate; +- +- u8 m_TSDataStrength; +- u8 m_TSClockkStrength; +- +- bool m_itut_annex_c; /* If true, uses ITU-T DVB-C Annex C, instead of Annex A */ +- +- enum DRXMPEGStrWidth_t m_widthSTR; /**< MPEG start width */ +- u32 m_mpegTsStaticBitrate; /**< Maximum bitrate in b/s in case +- static clockrate is selected */ +- +- /* LARGE_INTEGER m_StartTime; */ /**< Contains the time of the last demod start */ +- s32 m_MpegLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */ +- s32 m_DemodLockTimeOut; /**< WaitForLockStatus Timeout (counts from start time) */ +- +- bool m_disableTEIhandling; +- +- bool m_RfAgcPol; +- bool m_IfAgcPol; +- +- struct SCfgAgc m_atvRfAgcCfg; /**< settings for ATV RF-AGC */ +- struct SCfgAgc m_atvIfAgcCfg; /**< settings for ATV IF-AGC */ +- struct SCfgPreSaw m_atvPreSawCfg; /**< settings for ATV pre SAW sense */ +- bool m_phaseCorrectionBypass; +- s16 m_atvTopVidPeak; +- u16 m_atvTopNoiseTh; +- enum EDrxkSifAttenuation m_sifAttenuation; +- bool m_enableCVBSOutput; +- bool m_enableSIFOutput; +- bool m_bMirrorFreqSpect; +- enum EDrxkConstellation m_Constellation; /**< Constellation type of the channel */ +- u32 m_CurrSymbolRate; /**< Current QAM symbol rate */ +- struct SCfgAgc m_qamRfAgcCfg; /**< settings for QAM RF-AGC */ +- struct SCfgAgc m_qamIfAgcCfg; /**< settings for QAM IF-AGC */ +- u16 m_qamPgaCfg; /**< settings for QAM PGA */ +- struct SCfgPreSaw m_qamPreSawCfg; /**< settings for QAM pre SAW sense */ +- enum EDrxkInterleaveMode m_qamInterleaveMode; /**< QAM Interleave mode */ +- u16 m_fecRsPlen; +- u16 m_fecRsPrescale; +- +- enum DRXKCfgDvbtSqiSpeed m_sqiSpeed; +- +- u16 m_GPIO; +- u16 m_GPIOCfg; +- +- struct SCfgAgc m_dvbtRfAgcCfg; /**< settings for QAM RF-AGC */ +- struct SCfgAgc m_dvbtIfAgcCfg; /**< settings for QAM IF-AGC */ +- struct SCfgPreSaw m_dvbtPreSawCfg; /**< settings for QAM pre SAW sense */ +- +- u16 m_agcFastClipCtrlDelay; +- bool m_adcCompPassed; +- u16 m_adcCompCoef[64]; +- u16 m_adcState; +- +- u8 *m_microcode; +- int m_microcode_length; +- bool m_DRXK_A1_PATCH_CODE; +- bool m_DRXK_A1_ROM_CODE; +- bool m_DRXK_A2_ROM_CODE; +- bool m_DRXK_A3_ROM_CODE; +- bool m_DRXK_A2_PATCH_CODE; +- bool m_DRXK_A3_PATCH_CODE; +- +- bool m_rfmirror; +- u8 m_deviceSpin; +- u32 m_iqmRcRate; +- +- enum DRXPowerMode m_currentPowerMode; +- +- /* +- * Configurable parameters at the driver. They stores the values found +- * at struct drxk_config. +- */ +- +- u16 UIO_mask; /* Bits used by UIO */ +- +- bool single_master; +- bool no_i2c_bridge; +- bool antenna_dvbt; +- u16 antenna_gpio; +- +- const char *microcode_name; +-}; +- +-#define NEVER_LOCK 0 +-#define NOT_LOCKED 1 +-#define DEMOD_LOCK 2 +-#define FEC_LOCK 3 +-#define MPEG_LOCK 4 +- +diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h +deleted file mode 100644 +index 9b11a83..0000000 +--- a/drivers/media/dvb/frontends/drxk_map.h ++++ /dev/null +@@ -1,449 +0,0 @@ +-#define AUD_COMM_EXEC__A 0x1000000 +-#define AUD_COMM_EXEC_STOP 0x0 +-#define FEC_COMM_EXEC__A 0x1C00000 +-#define FEC_COMM_EXEC_STOP 0x0 +-#define FEC_COMM_EXEC_ACTIVE 0x1 +-#define FEC_DI_COMM_EXEC__A 0x1C20000 +-#define FEC_DI_COMM_EXEC_STOP 0x0 +-#define FEC_DI_INPUT_CTL__A 0x1C20016 +-#define FEC_RS_COMM_EXEC__A 0x1C30000 +-#define FEC_RS_COMM_EXEC_STOP 0x0 +-#define FEC_RS_MEASUREMENT_PERIOD__A 0x1C30012 +-#define FEC_RS_MEASUREMENT_PRESCALE__A 0x1C30013 +-#define FEC_OC_MODE__A 0x1C40011 +-#define FEC_OC_MODE_PARITY__M 0x1 +-#define FEC_OC_DTO_MODE__A 0x1C40014 +-#define FEC_OC_DTO_MODE_DYNAMIC__M 0x1 +-#define FEC_OC_DTO_MODE_OFFSET_ENABLE__M 0x4 +-#define FEC_OC_DTO_PERIOD__A 0x1C40015 +-#define FEC_OC_DTO_BURST_LEN__A 0x1C40018 +-#define FEC_OC_FCT_MODE__A 0x1C4001A +-#define FEC_OC_FCT_MODE__PRE 0x0 +-#define FEC_OC_FCT_MODE_RAT_ENA__M 0x1 +-#define FEC_OC_FCT_MODE_VIRT_ENA__M 0x2 +-#define FEC_OC_TMD_MODE__A 0x1C4001E +-#define FEC_OC_TMD_COUNT__A 0x1C4001F +-#define FEC_OC_TMD_HI_MARGIN__A 0x1C40020 +-#define FEC_OC_TMD_LO_MARGIN__A 0x1C40021 +-#define FEC_OC_TMD_INT_UPD_RATE__A 0x1C40023 +-#define FEC_OC_AVR_PARM_A__A 0x1C40026 +-#define FEC_OC_AVR_PARM_B__A 0x1C40027 +-#define FEC_OC_RCN_GAIN__A 0x1C4002E +-#define FEC_OC_RCN_CTL_RATE_LO__A 0x1C40030 +-#define FEC_OC_RCN_CTL_STEP_LO__A 0x1C40032 +-#define FEC_OC_RCN_CTL_STEP_HI__A 0x1C40033 +-#define FEC_OC_SNC_MODE__A 0x1C40040 +-#define FEC_OC_SNC_MODE_SHUTDOWN__M 0x10 +-#define FEC_OC_SNC_LWM__A 0x1C40041 +-#define FEC_OC_SNC_HWM__A 0x1C40042 +-#define FEC_OC_SNC_UNLOCK__A 0x1C40043 +-#define FEC_OC_SNC_FAIL_PERIOD__A 0x1C40046 +-#define FEC_OC_IPR_MODE__A 0x1C40048 +-#define FEC_OC_IPR_MODE_SERIAL__M 0x1 +-#define FEC_OC_IPR_MODE_MCLK_DIS_DAT_ABS__M 0x4 +-#define FEC_OC_IPR_MODE_MVAL_DIS_PAR__M 0x10 +-#define FEC_OC_IPR_INVERT__A 0x1C40049 +-#define FEC_OC_IPR_INVERT_MD0__M 0x1 +-#define FEC_OC_IPR_INVERT_MD1__M 0x2 +-#define FEC_OC_IPR_INVERT_MD2__M 0x4 +-#define FEC_OC_IPR_INVERT_MD3__M 0x8 +-#define FEC_OC_IPR_INVERT_MD4__M 0x10 +-#define FEC_OC_IPR_INVERT_MD5__M 0x20 +-#define FEC_OC_IPR_INVERT_MD6__M 0x40 +-#define FEC_OC_IPR_INVERT_MD7__M 0x80 +-#define FEC_OC_IPR_INVERT_MERR__M 0x100 +-#define FEC_OC_IPR_INVERT_MSTRT__M 0x200 +-#define FEC_OC_IPR_INVERT_MVAL__M 0x400 +-#define FEC_OC_IPR_INVERT_MCLK__M 0x800 +-#define FEC_OC_OCR_INVERT__A 0x1C40052 +-#define IQM_COMM_EXEC__A 0x1800000 +-#define IQM_COMM_EXEC_B_STOP 0x0 +-#define IQM_COMM_EXEC_B_ACTIVE 0x1 +-#define IQM_FS_RATE_OFS_LO__A 0x1820010 +-#define IQM_FS_ADJ_SEL__A 0x1820014 +-#define IQM_FS_ADJ_SEL_B_OFF 0x0 +-#define IQM_FS_ADJ_SEL_B_QAM 0x1 +-#define IQM_FS_ADJ_SEL_B_VSB 0x2 +-#define IQM_FD_RATESEL__A 0x1830010 +-#define IQM_RC_RATE_OFS_LO__A 0x1840010 +-#define IQM_RC_RATE_OFS_LO__W 16 +-#define IQM_RC_RATE_OFS_LO__M 0xFFFF +-#define IQM_RC_RATE_OFS_HI__M 0xFF +-#define IQM_RC_ADJ_SEL__A 0x1840014 +-#define IQM_RC_ADJ_SEL_B_OFF 0x0 +-#define IQM_RC_ADJ_SEL_B_QAM 0x1 +-#define IQM_RC_ADJ_SEL_B_VSB 0x2 +-#define IQM_RC_STRETCH__A 0x1840016 +-#define IQM_CF_COMM_INT_MSK__A 0x1860006 +-#define IQM_CF_SYMMETRIC__A 0x1860010 +-#define IQM_CF_MIDTAP__A 0x1860011 +-#define IQM_CF_MIDTAP_RE__B 0 +-#define IQM_CF_MIDTAP_IM__B 1 +-#define IQM_CF_OUT_ENA__A 0x1860012 +-#define IQM_CF_OUT_ENA_QAM__B 1 +-#define IQM_CF_OUT_ENA_OFDM__M 0x4 +-#define IQM_CF_ADJ_SEL__A 0x1860013 +-#define IQM_CF_SCALE__A 0x1860014 +-#define IQM_CF_SCALE_SH__A 0x1860015 +-#define IQM_CF_SCALE_SH__PRE 0x0 +-#define IQM_CF_POW_MEAS_LEN__A 0x1860017 +-#define IQM_CF_DS_ENA__A 0x1860019 +-#define IQM_CF_TAP_RE0__A 0x1860020 +-#define IQM_CF_TAP_IM0__A 0x1860040 +-#define IQM_CF_CLP_VAL__A 0x1860060 +-#define IQM_CF_DATATH__A 0x1860061 +-#define IQM_CF_PKDTH__A 0x1860062 +-#define IQM_CF_WND_LEN__A 0x1860063 +-#define IQM_CF_DET_LCT__A 0x1860064 +-#define IQM_CF_BYPASSDET__A 0x1860067 +-#define IQM_AF_COMM_EXEC__A 0x1870000 +-#define IQM_AF_COMM_EXEC_ACTIVE 0x1 +-#define IQM_AF_CLKNEG__A 0x1870012 +-#define IQM_AF_CLKNEG_CLKNEGDATA__M 0x2 +-#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_POS 0x0 +-#define IQM_AF_CLKNEG_CLKNEGDATA_CLK_ADC_DATA_NEG 0x2 +-#define IQM_AF_START_LOCK__A 0x187001B +-#define IQM_AF_PHASE0__A 0x187001C +-#define IQM_AF_PHASE1__A 0x187001D +-#define IQM_AF_PHASE2__A 0x187001E +-#define IQM_AF_CLP_LEN__A 0x1870023 +-#define IQM_AF_CLP_TH__A 0x1870024 +-#define IQM_AF_SNS_LEN__A 0x1870026 +-#define IQM_AF_AGC_IF__A 0x1870028 +-#define IQM_AF_AGC_RF__A 0x1870029 +-#define IQM_AF_PDREF__A 0x187002B +-#define IQM_AF_PDREF__M 0x1F +-#define IQM_AF_STDBY__A 0x187002C +-#define IQM_AF_STDBY_STDBY_ADC_STANDBY 0x2 +-#define IQM_AF_STDBY_STDBY_AMP_STANDBY 0x4 +-#define IQM_AF_STDBY_STDBY_PD_STANDBY 0x8 +-#define IQM_AF_STDBY_STDBY_TAGC_IF_STANDBY 0x10 +-#define IQM_AF_STDBY_STDBY_TAGC_RF_STANDBY 0x20 +-#define IQM_AF_AMUX__A 0x187002D +-#define IQM_AF_AMUX_SIGNAL2ADC 0x1 +-#define IQM_AF_UPD_SEL__A 0x187002F +-#define IQM_AF_INC_LCT__A 0x1870034 +-#define IQM_AF_INC_BYPASS__A 0x1870036 +-#define OFDM_CP_COMM_EXEC__A 0x2800000 +-#define OFDM_CP_COMM_EXEC_STOP 0x0 +-#define OFDM_EC_SB_PRIOR__A 0x3410013 +-#define OFDM_EC_SB_PRIOR_HI 0x0 +-#define OFDM_EC_SB_PRIOR_LO 0x1 +-#define OFDM_EQ_TOP_TD_TPS_CONST__A 0x3010054 +-#define OFDM_EQ_TOP_TD_TPS_CONST__M 0x3 +-#define OFDM_EQ_TOP_TD_TPS_CONST_64QAM 0x2 +-#define OFDM_EQ_TOP_TD_TPS_CODE_HP__A 0x3010056 +-#define OFDM_EQ_TOP_TD_TPS_CODE_HP__M 0x7 +-#define OFDM_EQ_TOP_TD_TPS_CODE_LP_7_8 0x4 +-#define OFDM_EQ_TOP_TD_SQR_ERR_I__A 0x301005E +-#define OFDM_EQ_TOP_TD_SQR_ERR_Q__A 0x301005F +-#define OFDM_EQ_TOP_TD_SQR_ERR_EXP__A 0x3010060 +-#define OFDM_EQ_TOP_TD_REQ_SMB_CNT__A 0x3010061 +-#define OFDM_EQ_TOP_TD_TPS_PWR_OFS__A 0x3010062 +-#define OFDM_LC_COMM_EXEC__A 0x3800000 +-#define OFDM_LC_COMM_EXEC_STOP 0x0 +-#define OFDM_SC_COMM_EXEC__A 0x3C00000 +-#define OFDM_SC_COMM_EXEC_STOP 0x0 +-#define OFDM_SC_COMM_STATE__A 0x3C00001 +-#define OFDM_SC_RA_RAM_PARAM0__A 0x3C20040 +-#define OFDM_SC_RA_RAM_PARAM1__A 0x3C20041 +-#define OFDM_SC_RA_RAM_CMD_ADDR__A 0x3C20042 +-#define OFDM_SC_RA_RAM_CMD__A 0x3C20043 +-#define OFDM_SC_RA_RAM_CMD_NULL 0x0 +-#define OFDM_SC_RA_RAM_CMD_PROC_START 0x1 +-#define OFDM_SC_RA_RAM_CMD_SET_PREF_PARAM 0x3 +-#define OFDM_SC_RA_RAM_CMD_PROGRAM_PARAM 0x4 +-#define OFDM_SC_RA_RAM_CMD_GET_OP_PARAM 0x5 +-#define OFDM_SC_RA_RAM_CMD_USER_IO 0x6 +-#define OFDM_SC_RA_RAM_CMD_SET_TIMER 0x7 +-#define OFDM_SC_RA_RAM_CMD_SET_ECHO_TIMING 0x8 +-#define OFDM_SC_RA_RAM_SW_EVENT_RUN_NMASK__M 0x1 +-#define OFDM_SC_RA_RAM_LOCKTRACK_MIN 0x1 +-#define OFDM_SC_RA_RAM_OP_PARAM__A 0x3C20048 +-#define OFDM_SC_RA_RAM_OP_PARAM_MODE__M 0x3 +-#define OFDM_SC_RA_RAM_OP_PARAM_MODE_2K 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_MODE_8K 0x1 +-#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_32 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_16 0x4 +-#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_8 0x8 +-#define OFDM_SC_RA_RAM_OP_PARAM_GUARD_4 0xC +-#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QPSK 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM16 0x10 +-#define OFDM_SC_RA_RAM_OP_PARAM_CONST_QAM64 0x20 +-#define OFDM_SC_RA_RAM_OP_PARAM_HIER_NO 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A1 0x40 +-#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A2 0x80 +-#define OFDM_SC_RA_RAM_OP_PARAM_HIER_A4 0xC0 +-#define OFDM_SC_RA_RAM_OP_PARAM_RATE_1_2 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_RATE_2_3 0x200 +-#define OFDM_SC_RA_RAM_OP_PARAM_RATE_3_4 0x400 +-#define OFDM_SC_RA_RAM_OP_PARAM_RATE_5_6 0x600 +-#define OFDM_SC_RA_RAM_OP_PARAM_RATE_7_8 0x800 +-#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_HI 0x0 +-#define OFDM_SC_RA_RAM_OP_PARAM_PRIO_LO 0x1000 +-#define OFDM_SC_RA_RAM_OP_AUTO_MODE__M 0x1 +-#define OFDM_SC_RA_RAM_OP_AUTO_GUARD__M 0x2 +-#define OFDM_SC_RA_RAM_OP_AUTO_CONST__M 0x4 +-#define OFDM_SC_RA_RAM_OP_AUTO_HIER__M 0x8 +-#define OFDM_SC_RA_RAM_OP_AUTO_RATE__M 0x10 +-#define OFDM_SC_RA_RAM_LOCK__A 0x3C2004B +-#define OFDM_SC_RA_RAM_LOCK_DEMOD__M 0x1 +-#define OFDM_SC_RA_RAM_LOCK_FEC__M 0x2 +-#define OFDM_SC_RA_RAM_LOCK_MPEG__M 0x4 +-#define OFDM_SC_RA_RAM_LOCK_NODVBT__M 0x8 +-#define OFDM_SC_RA_RAM_BE_OPT_DELAY__A 0x3C2004D +-#define OFDM_SC_RA_RAM_BE_OPT_INIT_DELAY__A 0x3C2004E +-#define OFDM_SC_RA_RAM_ECHO_THRES__A 0x3C2004F +-#define OFDM_SC_RA_RAM_ECHO_THRES_8K__B 0 +-#define OFDM_SC_RA_RAM_ECHO_THRES_8K__M 0xFF +-#define OFDM_SC_RA_RAM_ECHO_THRES_2K__B 8 +-#define OFDM_SC_RA_RAM_ECHO_THRES_2K__M 0xFF00 +-#define OFDM_SC_RA_RAM_CONFIG__A 0x3C20050 +-#define OFDM_SC_RA_RAM_CONFIG_NE_FIX_ENABLE__M 0x800 +-#define OFDM_SC_RA_RAM_FR_THRES_8K__A 0x3C2007D +-#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_LEFT__A 0x3C200E0 +-#define OFDM_SC_RA_RAM_NI_INIT_2K_PER_RIGHT__A 0x3C200E1 +-#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_LEFT__A 0x3C200E3 +-#define OFDM_SC_RA_RAM_NI_INIT_8K_PER_RIGHT__A 0x3C200E4 +-#define OFDM_SC_RA_RAM_SRMM_FIX_FACT_8K__A 0x3C200F8 +-#define QAM_COMM_EXEC__A 0x1400000 +-#define QAM_COMM_EXEC_STOP 0x0 +-#define QAM_COMM_EXEC_ACTIVE 0x1 +-#define QAM_TOP_ANNEX_A 0x0 +-#define QAM_TOP_ANNEX_C 0x2 +-#define QAM_SL_ERR_POWER__A 0x1430017 +-#define QAM_DQ_QUAL_FUN0__A 0x1440018 +-#define QAM_DQ_QUAL_FUN1__A 0x1440019 +-#define QAM_DQ_QUAL_FUN2__A 0x144001A +-#define QAM_DQ_QUAL_FUN3__A 0x144001B +-#define QAM_DQ_QUAL_FUN4__A 0x144001C +-#define QAM_DQ_QUAL_FUN5__A 0x144001D +-#define QAM_LC_MODE__A 0x1450010 +-#define QAM_LC_QUAL_TAB0__A 0x1450018 +-#define QAM_LC_QUAL_TAB1__A 0x1450019 +-#define QAM_LC_QUAL_TAB2__A 0x145001A +-#define QAM_LC_QUAL_TAB3__A 0x145001B +-#define QAM_LC_QUAL_TAB4__A 0x145001C +-#define QAM_LC_QUAL_TAB5__A 0x145001D +-#define QAM_LC_QUAL_TAB6__A 0x145001E +-#define QAM_LC_QUAL_TAB8__A 0x145001F +-#define QAM_LC_QUAL_TAB9__A 0x1450020 +-#define QAM_LC_QUAL_TAB10__A 0x1450021 +-#define QAM_LC_QUAL_TAB12__A 0x1450022 +-#define QAM_LC_QUAL_TAB15__A 0x1450023 +-#define QAM_LC_QUAL_TAB16__A 0x1450024 +-#define QAM_LC_QUAL_TAB20__A 0x1450025 +-#define QAM_LC_QUAL_TAB25__A 0x1450026 +-#define QAM_LC_LPF_FACTORP__A 0x1450028 +-#define QAM_LC_LPF_FACTORI__A 0x1450029 +-#define QAM_LC_RATE_LIMIT__A 0x145002A +-#define QAM_LC_SYMBOL_FREQ__A 0x145002B +-#define QAM_SY_TIMEOUT__A 0x1470011 +-#define QAM_SY_TIMEOUT__PRE 0x3A98 +-#define QAM_SY_SYNC_LWM__A 0x1470012 +-#define QAM_SY_SYNC_AWM__A 0x1470013 +-#define QAM_SY_SYNC_HWM__A 0x1470014 +-#define QAM_SY_SP_INV__A 0x1470017 +-#define QAM_SY_SP_INV_SPECTRUM_INV_DIS 0x0 +-#define SCU_COMM_EXEC__A 0x800000 +-#define SCU_COMM_EXEC_STOP 0x0 +-#define SCU_COMM_EXEC_ACTIVE 0x1 +-#define SCU_COMM_EXEC_HOLD 0x2 +-#define SCU_RAM_DRIVER_DEBUG__A 0x831EBF +-#define SCU_RAM_QAM_FSM_STEP_PERIOD__A 0x831EC4 +-#define SCU_RAM_GPIO__A 0x831EC7 +-#define SCU_RAM_GPIO_HW_LOCK_IND_DISABLE 0x0 +-#define SCU_RAM_AGC_CLP_CTRL_MODE__A 0x831EC8 +-#define SCU_RAM_FEC_ACCUM_PKT_FAILURES__A 0x831ECB +-#define SCU_RAM_FEC_PRE_RS_BER_FILTER_SH__A 0x831F05 +-#define SCU_RAM_AGC_FAST_SNS_CTRL_DELAY__A 0x831F15 +-#define SCU_RAM_AGC_KI_CYCLEN__A 0x831F17 +-#define SCU_RAM_AGC_SNS_CYCLEN__A 0x831F18 +-#define SCU_RAM_AGC_RF_SNS_DEV_MAX__A 0x831F19 +-#define SCU_RAM_AGC_RF_SNS_DEV_MIN__A 0x831F1A +-#define SCU_RAM_AGC_RF_MAX__A 0x831F1B +-#define SCU_RAM_AGC_CONFIG__A 0x831F24 +-#define SCU_RAM_AGC_CONFIG_DISABLE_RF_AGC__M 0x1 +-#define SCU_RAM_AGC_CONFIG_DISABLE_IF_AGC__M 0x2 +-#define SCU_RAM_AGC_CONFIG_INV_IF_POL__M 0x100 +-#define SCU_RAM_AGC_CONFIG_INV_RF_POL__M 0x200 +-#define SCU_RAM_AGC_KI__A 0x831F25 +-#define SCU_RAM_AGC_KI_RF__B 4 +-#define SCU_RAM_AGC_KI_RF__M 0xF0 +-#define SCU_RAM_AGC_KI_IF__B 8 +-#define SCU_RAM_AGC_KI_IF__M 0xF00 +-#define SCU_RAM_AGC_KI_RED__A 0x831F26 +-#define SCU_RAM_AGC_KI_RED_RAGC_RED__B 2 +-#define SCU_RAM_AGC_KI_RED_RAGC_RED__M 0xC +-#define SCU_RAM_AGC_KI_RED_IAGC_RED__B 4 +-#define SCU_RAM_AGC_KI_RED_IAGC_RED__M 0x30 +-#define SCU_RAM_AGC_KI_INNERGAIN_MIN__A 0x831F27 +-#define SCU_RAM_AGC_KI_MINGAIN__A 0x831F28 +-#define SCU_RAM_AGC_KI_MAXGAIN__A 0x831F29 +-#define SCU_RAM_AGC_KI_MAXMINGAIN_TH__A 0x831F2A +-#define SCU_RAM_AGC_KI_MIN__A 0x831F2B +-#define SCU_RAM_AGC_KI_MAX__A 0x831F2C +-#define SCU_RAM_AGC_CLP_SUM__A 0x831F2D +-#define SCU_RAM_AGC_CLP_SUM_MIN__A 0x831F2E +-#define SCU_RAM_AGC_CLP_SUM_MAX__A 0x831F2F +-#define SCU_RAM_AGC_CLP_CYCLEN__A 0x831F30 +-#define SCU_RAM_AGC_CLP_CYCCNT__A 0x831F31 +-#define SCU_RAM_AGC_CLP_DIR_TO__A 0x831F32 +-#define SCU_RAM_AGC_CLP_DIR_WD__A 0x831F33 +-#define SCU_RAM_AGC_CLP_DIR_STP__A 0x831F34 +-#define SCU_RAM_AGC_SNS_SUM__A 0x831F35 +-#define SCU_RAM_AGC_SNS_SUM_MIN__A 0x831F36 +-#define SCU_RAM_AGC_SNS_SUM_MAX__A 0x831F37 +-#define SCU_RAM_AGC_SNS_CYCCNT__A 0x831F38 +-#define SCU_RAM_AGC_SNS_DIR_TO__A 0x831F39 +-#define SCU_RAM_AGC_SNS_DIR_WD__A 0x831F3A +-#define SCU_RAM_AGC_SNS_DIR_STP__A 0x831F3B +-#define SCU_RAM_AGC_INGAIN_TGT__A 0x831F3D +-#define SCU_RAM_AGC_INGAIN_TGT_MIN__A 0x831F3E +-#define SCU_RAM_AGC_INGAIN_TGT_MAX__A 0x831F3F +-#define SCU_RAM_AGC_IF_IACCU_HI__A 0x831F40 +-#define SCU_RAM_AGC_IF_IACCU_LO__A 0x831F41 +-#define SCU_RAM_AGC_IF_IACCU_HI_TGT__A 0x831F42 +-#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MIN__A 0x831F43 +-#define SCU_RAM_AGC_IF_IACCU_HI_TGT_MAX__A 0x831F44 +-#define SCU_RAM_AGC_RF_IACCU_HI__A 0x831F45 +-#define SCU_RAM_AGC_RF_IACCU_LO__A 0x831F46 +-#define SCU_RAM_AGC_RF_IACCU_HI_CO__A 0x831F47 +-#define SCU_RAM_QAM_FSM_MEDIAN_AV_MULT__A 0x831F84 +-#define SCU_RAM_QAM_FSM_RADIUS_AV_LIMIT__A 0x831F85 +-#define SCU_RAM_QAM_FSM_LCAVG_OFFSET1__A 0x831F86 +-#define SCU_RAM_QAM_FSM_LCAVG_OFFSET2__A 0x831F87 +-#define SCU_RAM_QAM_FSM_LCAVG_OFFSET3__A 0x831F88 +-#define SCU_RAM_QAM_FSM_LCAVG_OFFSET4__A 0x831F89 +-#define SCU_RAM_QAM_FSM_LCAVG_OFFSET5__A 0x831F8A +-#define SCU_RAM_QAM_FSM_RTH__A 0x831F8E +-#define SCU_RAM_QAM_FSM_FTH__A 0x831F8F +-#define SCU_RAM_QAM_FSM_PTH__A 0x831F90 +-#define SCU_RAM_QAM_FSM_MTH__A 0x831F91 +-#define SCU_RAM_QAM_FSM_CTH__A 0x831F92 +-#define SCU_RAM_QAM_FSM_QTH__A 0x831F93 +-#define SCU_RAM_QAM_FSM_RATE_LIM__A 0x831F94 +-#define SCU_RAM_QAM_FSM_FREQ_LIM__A 0x831F95 +-#define SCU_RAM_QAM_FSM_COUNT_LIM__A 0x831F96 +-#define SCU_RAM_QAM_LC_CA_COARSE__A 0x831F97 +-#define SCU_RAM_QAM_LC_CA_FINE__A 0x831F99 +-#define SCU_RAM_QAM_LC_CP_COARSE__A 0x831F9A +-#define SCU_RAM_QAM_LC_CP_MEDIUM__A 0x831F9B +-#define SCU_RAM_QAM_LC_CP_FINE__A 0x831F9C +-#define SCU_RAM_QAM_LC_CI_COARSE__A 0x831F9D +-#define SCU_RAM_QAM_LC_CI_MEDIUM__A 0x831F9E +-#define SCU_RAM_QAM_LC_CI_FINE__A 0x831F9F +-#define SCU_RAM_QAM_LC_EP_COARSE__A 0x831FA0 +-#define SCU_RAM_QAM_LC_EP_MEDIUM__A 0x831FA1 +-#define SCU_RAM_QAM_LC_EP_FINE__A 0x831FA2 +-#define SCU_RAM_QAM_LC_EI_COARSE__A 0x831FA3 +-#define SCU_RAM_QAM_LC_EI_MEDIUM__A 0x831FA4 +-#define SCU_RAM_QAM_LC_EI_FINE__A 0x831FA5 +-#define SCU_RAM_QAM_LC_CF_COARSE__A 0x831FA6 +-#define SCU_RAM_QAM_LC_CF_MEDIUM__A 0x831FA7 +-#define SCU_RAM_QAM_LC_CF_FINE__A 0x831FA8 +-#define SCU_RAM_QAM_LC_CF1_COARSE__A 0x831FA9 +-#define SCU_RAM_QAM_LC_CF1_MEDIUM__A 0x831FAA +-#define SCU_RAM_QAM_LC_CF1_FINE__A 0x831FAB +-#define SCU_RAM_QAM_SL_SIG_POWER__A 0x831FAC +-#define SCU_RAM_QAM_EQ_CMA_RAD0__A 0x831FAD +-#define SCU_RAM_QAM_EQ_CMA_RAD1__A 0x831FAE +-#define SCU_RAM_QAM_EQ_CMA_RAD2__A 0x831FAF +-#define SCU_RAM_QAM_EQ_CMA_RAD3__A 0x831FB0 +-#define SCU_RAM_QAM_EQ_CMA_RAD4__A 0x831FB1 +-#define SCU_RAM_QAM_EQ_CMA_RAD5__A 0x831FB2 +-#define SCU_RAM_QAM_LOCKED_LOCKED_DEMOD_LOCKED 0x4000 +-#define SCU_RAM_QAM_LOCKED_LOCKED_LOCKED 0x8000 +-#define SCU_RAM_QAM_LOCKED_LOCKED_NEVER_LOCK 0xC000 +-#define SCU_RAM_AGC_FAST_CLP_CTRL_DELAY__A 0x831FEA +-#define SCU_RAM_DRIVER_VER_HI__A 0x831FEB +-#define SCU_RAM_DRIVER_VER_LO__A 0x831FEC +-#define SCU_RAM_PARAM_15__A 0x831FED +-#define SCU_RAM_PARAM_0__A 0x831FFC +-#define SCU_RAM_COMMAND__A 0x831FFD +-#define SCU_RAM_COMMAND_CMD_DEMOD_RESET 0x1 +-#define SCU_RAM_COMMAND_CMD_DEMOD_SET_ENV 0x2 +-#define SCU_RAM_COMMAND_CMD_DEMOD_SET_PARAM 0x3 +-#define SCU_RAM_COMMAND_CMD_DEMOD_START 0x4 +-#define SCU_RAM_COMMAND_CMD_DEMOD_GET_LOCK 0x5 +-#define SCU_RAM_COMMAND_CMD_DEMOD_STOP 0x9 +-#define SCU_RAM_COMMAND_STANDARD_QAM 0x200 +-#define SCU_RAM_COMMAND_STANDARD_OFDM 0x400 +-#define SIO_TOP_COMM_KEY__A 0x41000F +-#define SIO_TOP_COMM_KEY_KEY 0xFABA +-#define SIO_TOP_JTAGID_LO__A 0x410012 +-#define SIO_HI_RA_RAM_RES__A 0x420031 +-#define SIO_HI_RA_RAM_CMD__A 0x420032 +-#define SIO_HI_RA_RAM_CMD_RESET 0x2 +-#define SIO_HI_RA_RAM_CMD_CONFIG 0x3 +-#define SIO_HI_RA_RAM_CMD_BRDCTRL 0x7 +-#define SIO_HI_RA_RAM_PAR_1__A 0x420033 +-#define SIO_HI_RA_RAM_PAR_1_PAR1_SEC_KEY 0x3945 +-#define SIO_HI_RA_RAM_PAR_2__A 0x420034 +-#define SIO_HI_RA_RAM_PAR_2_CFG_DIV__M 0x7F +-#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_OPEN 0x0 +-#define SIO_HI_RA_RAM_PAR_2_BRD_CFG_CLOSED 0x4 +-#define SIO_HI_RA_RAM_PAR_3__A 0x420035 +-#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SDA__M 0x7F +-#define SIO_HI_RA_RAM_PAR_3_CFG_DBL_SCL__B 7 +-#define SIO_HI_RA_RAM_PAR_3_ACP_RW_READ 0x0 +-#define SIO_HI_RA_RAM_PAR_3_ACP_RW_WRITE 0x8 +-#define SIO_HI_RA_RAM_PAR_4__A 0x420036 +-#define SIO_HI_RA_RAM_PAR_5__A 0x420037 +-#define SIO_HI_RA_RAM_PAR_5_CFG_SLV0_SLAVE 0x1 +-#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP__M 0x8 +-#define SIO_HI_RA_RAM_PAR_5_CFG_SLEEP_ZZZ 0x8 +-#define SIO_HI_RA_RAM_PAR_6__A 0x420038 +-#define SIO_CC_PLL_LOCK__A 0x450012 +-#define SIO_CC_PWD_MODE__A 0x450015 +-#define SIO_CC_PWD_MODE_LEVEL_NONE 0x0 +-#define SIO_CC_PWD_MODE_LEVEL_OFDM 0x1 +-#define SIO_CC_PWD_MODE_LEVEL_CLOCK 0x2 +-#define SIO_CC_PWD_MODE_LEVEL_PLL 0x3 +-#define SIO_CC_PWD_MODE_LEVEL_OSC 0x4 +-#define SIO_CC_SOFT_RST__A 0x450016 +-#define SIO_CC_SOFT_RST_OFDM__M 0x1 +-#define SIO_CC_SOFT_RST_SYS__M 0x2 +-#define SIO_CC_SOFT_RST_OSC__M 0x4 +-#define SIO_CC_UPDATE__A 0x450017 +-#define SIO_CC_UPDATE_KEY 0xFABA +-#define SIO_OFDM_SH_OFDM_RING_ENABLE__A 0x470010 +-#define SIO_OFDM_SH_OFDM_RING_ENABLE_OFF 0x0 +-#define SIO_OFDM_SH_OFDM_RING_ENABLE_ON 0x1 +-#define SIO_OFDM_SH_OFDM_RING_STATUS__A 0x470012 +-#define SIO_OFDM_SH_OFDM_RING_STATUS_DOWN 0x0 +-#define SIO_OFDM_SH_OFDM_RING_STATUS_ENABLED 0x1 +-#define SIO_BL_COMM_EXEC__A 0x480000 +-#define SIO_BL_COMM_EXEC_ACTIVE 0x1 +-#define SIO_BL_STATUS__A 0x480010 +-#define SIO_BL_MODE__A 0x480011 +-#define SIO_BL_MODE_DIRECT 0x0 +-#define SIO_BL_MODE_CHAIN 0x1 +-#define SIO_BL_ENABLE__A 0x480012 +-#define SIO_BL_ENABLE_ON 0x1 +-#define SIO_BL_TGT_HDR__A 0x480014 +-#define SIO_BL_TGT_ADDR__A 0x480015 +-#define SIO_BL_SRC_ADDR__A 0x480016 +-#define SIO_BL_SRC_LEN__A 0x480017 +-#define SIO_BL_CHAIN_ADDR__A 0x480018 +-#define SIO_BL_CHAIN_LEN__A 0x480019 +-#define SIO_PDR_MON_CFG__A 0x7F0010 +-#define SIO_PDR_UIO_IN_HI__A 0x7F0015 +-#define SIO_PDR_UIO_OUT_LO__A 0x7F0016 +-#define SIO_PDR_OHW_CFG__A 0x7F001F +-#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 +-#define SIO_PDR_MSTRT_CFG__A 0x7F0025 +-#define SIO_PDR_MERR_CFG__A 0x7F0026 +-#define SIO_PDR_MCLK_CFG__A 0x7F0028 +-#define SIO_PDR_MCLK_CFG_DRIVE__B 3 +-#define SIO_PDR_MVAL_CFG__A 0x7F0029 +-#define SIO_PDR_MD0_CFG__A 0x7F002A +-#define SIO_PDR_MD0_CFG_DRIVE__B 3 +-#define SIO_PDR_MD1_CFG__A 0x7F002B +-#define SIO_PDR_MD2_CFG__A 0x7F002C +-#define SIO_PDR_MD3_CFG__A 0x7F002D +-#define SIO_PDR_MD4_CFG__A 0x7F002F +-#define SIO_PDR_MD5_CFG__A 0x7F0030 +-#define SIO_PDR_MD6_CFG__A 0x7F0031 +-#define SIO_PDR_MD7_CFG__A 0x7F0032 +-#define SIO_PDR_SMA_TX_CFG__A 0x7F0038 +diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c +deleted file mode 100644 +index af65d01..0000000 +--- a/drivers/media/dvb/frontends/ds3000.c ++++ /dev/null +@@ -1,1309 +0,0 @@ +-/* +- Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver +- Copyright (C) 2009 Konstantin Dimitrov +- +- Copyright (C) 2009 TurboSight.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 +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "ds3000.h" +- +-static int debug; +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(args); \ +- } while (0) +- +-/* as of March 2009 current DS3000 firmware version is 1.78 */ +-/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */ +-#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" +- +-#define DS3000_SAMPLE_RATE 96000 /* in kHz */ +-#define DS3000_XTAL_FREQ 27000 /* in kHz */ +- +-/* Register values to initialise the demod in DVB-S mode */ +-static u8 ds3000_dvbs_init_tab[] = { +- 0x23, 0x05, +- 0x08, 0x03, +- 0x0c, 0x00, +- 0x21, 0x54, +- 0x25, 0x82, +- 0x27, 0x31, +- 0x30, 0x08, +- 0x31, 0x40, +- 0x32, 0x32, +- 0x33, 0x35, +- 0x35, 0xff, +- 0x3a, 0x00, +- 0x37, 0x10, +- 0x38, 0x10, +- 0x39, 0x02, +- 0x42, 0x60, +- 0x4a, 0x40, +- 0x4b, 0x04, +- 0x4d, 0x91, +- 0x5d, 0xc8, +- 0x50, 0x77, +- 0x51, 0x77, +- 0x52, 0x36, +- 0x53, 0x36, +- 0x56, 0x01, +- 0x63, 0x43, +- 0x64, 0x30, +- 0x65, 0x40, +- 0x68, 0x26, +- 0x69, 0x4c, +- 0x70, 0x20, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x40, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x60, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x80, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0xa0, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x1f, +- 0x76, 0x00, +- 0x77, 0xd1, +- 0x78, 0x0c, +- 0x79, 0x80, +- 0x7f, 0x04, +- 0x7c, 0x00, +- 0x80, 0x86, +- 0x81, 0xa6, +- 0x85, 0x04, +- 0xcd, 0xf4, +- 0x90, 0x33, +- 0xa0, 0x44, +- 0xc0, 0x18, +- 0xc3, 0x10, +- 0xc4, 0x08, +- 0xc5, 0x80, +- 0xc6, 0x80, +- 0xc7, 0x0a, +- 0xc8, 0x1a, +- 0xc9, 0x80, +- 0xfe, 0x92, +- 0xe0, 0xf8, +- 0xe6, 0x8b, +- 0xd0, 0x40, +- 0xf8, 0x20, +- 0xfa, 0x0f, +- 0xfd, 0x20, +- 0xad, 0x20, +- 0xae, 0x07, +- 0xb8, 0x00, +-}; +- +-/* Register values to initialise the demod in DVB-S2 mode */ +-static u8 ds3000_dvbs2_init_tab[] = { +- 0x23, 0x0f, +- 0x08, 0x07, +- 0x0c, 0x00, +- 0x21, 0x54, +- 0x25, 0x82, +- 0x27, 0x31, +- 0x30, 0x08, +- 0x31, 0x32, +- 0x32, 0x32, +- 0x33, 0x35, +- 0x35, 0xff, +- 0x3a, 0x00, +- 0x37, 0x10, +- 0x38, 0x10, +- 0x39, 0x02, +- 0x42, 0x60, +- 0x4a, 0x80, +- 0x4b, 0x04, +- 0x4d, 0x81, +- 0x5d, 0x88, +- 0x50, 0x36, +- 0x51, 0x36, +- 0x52, 0x36, +- 0x53, 0x36, +- 0x63, 0x60, +- 0x64, 0x10, +- 0x65, 0x10, +- 0x68, 0x04, +- 0x69, 0x29, +- 0x70, 0x20, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x40, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x60, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x80, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0xa0, +- 0x71, 0x70, +- 0x72, 0x04, +- 0x73, 0x00, +- 0x70, 0x1f, +- 0xa0, 0x44, +- 0xc0, 0x08, +- 0xc1, 0x10, +- 0xc2, 0x08, +- 0xc3, 0x10, +- 0xc4, 0x08, +- 0xc5, 0xf0, +- 0xc6, 0xf0, +- 0xc7, 0x0a, +- 0xc8, 0x1a, +- 0xc9, 0x80, +- 0xca, 0x23, +- 0xcb, 0x24, +- 0xce, 0x74, +- 0x90, 0x03, +- 0x76, 0x80, +- 0x77, 0x42, +- 0x78, 0x0a, +- 0x79, 0x80, +- 0xad, 0x40, +- 0xae, 0x07, +- 0x7f, 0xd4, +- 0x7c, 0x00, +- 0x80, 0xa8, +- 0x81, 0xda, +- 0x7c, 0x01, +- 0x80, 0xda, +- 0x81, 0xec, +- 0x7c, 0x02, +- 0x80, 0xca, +- 0x81, 0xeb, +- 0x7c, 0x03, +- 0x80, 0xba, +- 0x81, 0xdb, +- 0x85, 0x08, +- 0x86, 0x00, +- 0x87, 0x02, +- 0x89, 0x80, +- 0x8b, 0x44, +- 0x8c, 0xaa, +- 0x8a, 0x10, +- 0xba, 0x00, +- 0xf5, 0x04, +- 0xfe, 0x44, +- 0xd2, 0x32, +- 0xb8, 0x00, +-}; +- +-struct ds3000_state { +- struct i2c_adapter *i2c; +- const struct ds3000_config *config; +- struct dvb_frontend frontend; +- u8 skip_fw_load; +- /* previous uncorrected block counter for DVB-S2 */ +- u16 prevUCBS2; +-}; +- +-static int ds3000_writereg(struct ds3000_state *state, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = 0, .buf = buf, .len = 2 }; +- int err; +- +- dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); +- +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," +- " value == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = 0x60, +- .flags = 0, .buf = buf, .len = 2 }; +- int err; +- +- dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); +- +- ds3000_writereg(state, 0x03, 0x11); +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk("%s: writereg error(err == %i, reg == 0x%02x," +- " value == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-/* I2C write for 8k firmware load */ +-static int ds3000_writeFW(struct ds3000_state *state, int reg, +- const u8 *data, u16 len) +-{ +- int i, ret = -EREMOTEIO; +- struct i2c_msg msg; +- u8 *buf; +- +- buf = kmalloc(33, GFP_KERNEL); +- if (buf == NULL) { +- printk(KERN_ERR "Unable to kmalloc\n"); +- ret = -ENOMEM; +- goto error; +- } +- +- *(buf) = reg; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.buf = buf; +- msg.len = 33; +- +- for (i = 0; i < len; i += 32) { +- memcpy(buf + 1, data + i, 32); +- +- dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) { +- printk(KERN_ERR "%s: write error(err == %i, " +- "reg == 0x%02x\n", __func__, ret, reg); +- ret = -EREMOTEIO; +- } +- } +- +-error: +- kfree(buf); +- +- return ret; +-} +- +-static int ds3000_readreg(struct ds3000_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); +- return ret; +- } +- +- dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); +- +- return b1[0]; +-} +- +-static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = 0x60, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = 0x60, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- +- ds3000_writereg(state, 0x03, 0x12); +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); +- return ret; +- } +- +- dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); +- +- return b1[0]; +-} +- +-static int ds3000_load_firmware(struct dvb_frontend *fe, +- const struct firmware *fw); +- +-static int ds3000_firmware_ondemand(struct dvb_frontend *fe) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- const struct firmware *fw; +- int ret = 0; +- +- dprintk("%s()\n", __func__); +- +- if (ds3000_readreg(state, 0xb2) <= 0) +- return ret; +- +- if (state->skip_fw_load) +- return 0; +- /* Load firmware */ +- /* request the firmware, this will block until someone uploads it */ +- printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, +- DS3000_DEFAULT_FIRMWARE); +- ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, +- state->i2c->dev.parent); +- printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); +- if (ret) { +- printk(KERN_ERR "%s: No firmware uploaded (timeout or file not " +- "found?)\n", __func__); +- return ret; +- } +- +- /* Make sure we don't recurse back through here during loading */ +- state->skip_fw_load = 1; +- +- ret = ds3000_load_firmware(fe, fw); +- if (ret) +- printk("%s: Writing firmware to device failed\n", __func__); +- +- release_firmware(fw); +- +- dprintk("%s: Firmware upload %s\n", __func__, +- ret == 0 ? "complete" : "failed"); +- +- /* Ensure firmware is always loaded if required */ +- state->skip_fw_load = 0; +- +- return ret; +-} +- +-static int ds3000_load_firmware(struct dvb_frontend *fe, +- const struct firmware *fw) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", +- fw->size, +- fw->data[0], +- fw->data[1], +- fw->data[fw->size - 2], +- fw->data[fw->size - 1]); +- +- /* Begin the firmware load process */ +- ds3000_writereg(state, 0xb2, 0x01); +- /* write the entire firmware */ +- ds3000_writeFW(state, 0xb0, fw->data, fw->size); +- ds3000_writereg(state, 0xb2, 0x00); +- +- return 0; +-} +- +-static int ds3000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- u8 data; +- +- dprintk("%s(%d)\n", __func__, voltage); +- +- data = ds3000_readreg(state, 0xa2); +- data |= 0x03; /* bit0 V/H, bit1 off/on */ +- +- switch (voltage) { +- case SEC_VOLTAGE_18: +- data &= ~0x03; +- break; +- case SEC_VOLTAGE_13: +- data &= ~0x03; +- data |= 0x01; +- break; +- case SEC_VOLTAGE_OFF: +- break; +- } +- +- ds3000_writereg(state, 0xa2, data); +- +- return 0; +-} +- +-static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int lock; +- +- *status = 0; +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- lock = ds3000_readreg(state, 0xd1); +- if ((lock & 0x07) == 0x07) +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | +- FE_HAS_LOCK; +- +- break; +- case SYS_DVBS2: +- lock = ds3000_readreg(state, 0x0d); +- if ((lock & 0x8f) == 0x8f) +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | +- FE_HAS_LOCK; +- +- break; +- default: +- return 1; +- } +- +- dprintk("%s: status = 0x%02x\n", __func__, lock); +- +- return 0; +-} +- +-/* read DS3000 BER value */ +-static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 data; +- u32 ber_reading, lpdc_frames; +- +- dprintk("%s()\n", __func__); +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- /* set the number of bytes checked during +- BER estimation */ +- ds3000_writereg(state, 0xf9, 0x04); +- /* read BER estimation status */ +- data = ds3000_readreg(state, 0xf8); +- /* check if BER estimation is ready */ +- if ((data & 0x10) == 0) { +- /* this is the number of error bits, +- to calculate the bit error rate +- divide to 8388608 */ +- *ber = (ds3000_readreg(state, 0xf7) << 8) | +- ds3000_readreg(state, 0xf6); +- /* start counting error bits */ +- /* need to be set twice +- otherwise it fails sometimes */ +- data |= 0x10; +- ds3000_writereg(state, 0xf8, data); +- ds3000_writereg(state, 0xf8, data); +- } else +- /* used to indicate that BER estimation +- is not ready, i.e. BER is unknown */ +- *ber = 0xffffffff; +- break; +- case SYS_DVBS2: +- /* read the number of LPDC decoded frames */ +- lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) | +- (ds3000_readreg(state, 0xd6) << 8) | +- ds3000_readreg(state, 0xd5); +- /* read the number of packets with bad CRC */ +- ber_reading = (ds3000_readreg(state, 0xf8) << 8) | +- ds3000_readreg(state, 0xf7); +- if (lpdc_frames > 750) { +- /* clear LPDC frame counters */ +- ds3000_writereg(state, 0xd1, 0x01); +- /* clear bad packets counter */ +- ds3000_writereg(state, 0xf9, 0x01); +- /* enable bad packets counter */ +- ds3000_writereg(state, 0xf9, 0x00); +- /* enable LPDC frame counters */ +- ds3000_writereg(state, 0xd1, 0x00); +- *ber = ber_reading; +- } else +- /* used to indicate that BER estimation is not ready, +- i.e. BER is unknown */ +- *ber = 0xffffffff; +- break; +- default: +- return 1; +- } +- +- return 0; +-} +- +-/* read TS2020 signal strength */ +-static int ds3000_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- u16 sig_reading, sig_strength; +- u8 rfgain, bbgain; +- +- dprintk("%s()\n", __func__); +- +- rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; +- bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; +- +- if (rfgain > 15) +- rfgain = 15; +- if (bbgain > 13) +- bbgain = 13; +- +- sig_reading = rfgain * 2 + bbgain * 3; +- +- sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; +- +- /* cook the value to be suitable for szap-s2 human readable output */ +- *signal_strength = sig_strength * 1000; +- +- dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, +- sig_reading, *signal_strength); +- +- return 0; +-} +- +-/* calculate DS3000 snr value in dB */ +-static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 snr_reading, snr_value; +- u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp; +- static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */ +- 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03, +- 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717, +- 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505 +- }; +- static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */ +- 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103, +- 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5, +- 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6, +- 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888, +- 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51, +- 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68, +- 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206, +- 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a, +- 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649, +- 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813, +- 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1, +- 0x49e9, 0x4a20, 0x4a57 +- }; +- +- dprintk("%s()\n", __func__); +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- snr_reading = ds3000_readreg(state, 0xff); +- snr_reading /= 8; +- if (snr_reading == 0) +- *snr = 0x0000; +- else { +- if (snr_reading > 20) +- snr_reading = 20; +- snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026; +- /* cook the value to be suitable for szap-s2 +- human readable output */ +- *snr = snr_value * 8 * 655; +- } +- dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, +- snr_reading, *snr); +- break; +- case SYS_DVBS2: +- dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) + +- (ds3000_readreg(state, 0x8d) << 4); +- dvbs2_signal_reading = ds3000_readreg(state, 0x8e); +- tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; +- if (tmp == 0) { +- *snr = 0x0000; +- return 0; +- } +- if (dvbs2_noise_reading == 0) { +- snr_value = 0x0013; +- /* cook the value to be suitable for szap-s2 +- human readable output */ +- *snr = 0xffff; +- return 0; +- } +- if (tmp > dvbs2_noise_reading) { +- snr_reading = tmp / dvbs2_noise_reading; +- if (snr_reading > 80) +- snr_reading = 80; +- snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000; +- /* cook the value to be suitable for szap-s2 +- human readable output */ +- *snr = snr_value * 5 * 655; +- } else { +- snr_reading = dvbs2_noise_reading / tmp; +- if (snr_reading > 80) +- snr_reading = 80; +- *snr = -(dvbs2_snr_tab[snr_reading] / 1000); +- } +- dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, +- snr_reading, *snr); +- break; +- default: +- return 1; +- } +- +- return 0; +-} +- +-/* read DS3000 uncorrected blocks */ +-static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 data; +- u16 _ucblocks; +- +- dprintk("%s()\n", __func__); +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- *ucblocks = (ds3000_readreg(state, 0xf5) << 8) | +- ds3000_readreg(state, 0xf4); +- data = ds3000_readreg(state, 0xf8); +- /* clear packet counters */ +- data &= ~0x20; +- ds3000_writereg(state, 0xf8, data); +- /* enable packet counters */ +- data |= 0x20; +- ds3000_writereg(state, 0xf8, data); +- break; +- case SYS_DVBS2: +- _ucblocks = (ds3000_readreg(state, 0xe2) << 8) | +- ds3000_readreg(state, 0xe1); +- if (_ucblocks > state->prevUCBS2) +- *ucblocks = _ucblocks - state->prevUCBS2; +- else +- *ucblocks = state->prevUCBS2 - _ucblocks; +- state->prevUCBS2 = _ucblocks; +- break; +- default: +- return 1; +- } +- +- return 0; +-} +- +-static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- u8 data; +- +- dprintk("%s(%d)\n", __func__, tone); +- if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { +- printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); +- return -EINVAL; +- } +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- ds3000_writereg(state, 0xa2, data); +- +- switch (tone) { +- case SEC_TONE_ON: +- dprintk("%s: setting tone on\n", __func__); +- data = ds3000_readreg(state, 0xa1); +- data &= ~0x43; +- data |= 0x04; +- ds3000_writereg(state, 0xa1, data); +- break; +- case SEC_TONE_OFF: +- dprintk("%s: setting tone off\n", __func__); +- data = ds3000_readreg(state, 0xa2); +- data |= 0x80; +- ds3000_writereg(state, 0xa2, data); +- break; +- } +- +- return 0; +-} +- +-static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *d) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- int i; +- u8 data; +- +- /* Dump DiSEqC message */ +- dprintk("%s(", __func__); +- for (i = 0 ; i < d->msg_len;) { +- dprintk("0x%02x", d->msg[i]); +- if (++i < d->msg_len) +- dprintk(", "); +- } +- +- /* enable DiSEqC message send pin */ +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- ds3000_writereg(state, 0xa2, data); +- +- /* DiSEqC message */ +- for (i = 0; i < d->msg_len; i++) +- ds3000_writereg(state, 0xa3 + i, d->msg[i]); +- +- data = ds3000_readreg(state, 0xa1); +- /* clear DiSEqC message length and status, +- enable DiSEqC message send */ +- data &= ~0xf8; +- /* set DiSEqC mode, modulation active during 33 pulses, +- set DiSEqC message length */ +- data |= ((d->msg_len - 1) << 3) | 0x07; +- ds3000_writereg(state, 0xa1, data); +- +- /* wait up to 150ms for DiSEqC transmission to complete */ +- for (i = 0; i < 15; i++) { +- data = ds3000_readreg(state, 0xa1); +- if ((data & 0x40) == 0) +- break; +- msleep(10); +- } +- +- /* DiSEqC timeout after 150ms */ +- if (i == 15) { +- data = ds3000_readreg(state, 0xa1); +- data &= ~0x80; +- data |= 0x40; +- ds3000_writereg(state, 0xa1, data); +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- data |= 0x80; +- ds3000_writereg(state, 0xa2, data); +- +- return 1; +- } +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- data |= 0x80; +- ds3000_writereg(state, 0xa2, data); +- +- return 0; +-} +- +-/* Send DiSEqC burst */ +-static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- int i; +- u8 data; +- +- dprintk("%s()\n", __func__); +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- ds3000_writereg(state, 0xa2, data); +- +- /* DiSEqC burst */ +- if (burst == SEC_MINI_A) +- /* Unmodulated tone burst */ +- ds3000_writereg(state, 0xa1, 0x02); +- else if (burst == SEC_MINI_B) +- /* Modulated tone burst */ +- ds3000_writereg(state, 0xa1, 0x01); +- else +- return -EINVAL; +- +- msleep(13); +- for (i = 0; i < 5; i++) { +- data = ds3000_readreg(state, 0xa1); +- if ((data & 0x40) == 0) +- break; +- msleep(1); +- } +- +- if (i == 5) { +- data = ds3000_readreg(state, 0xa1); +- data &= ~0x80; +- data |= 0x40; +- ds3000_writereg(state, 0xa1, data); +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- data |= 0x80; +- ds3000_writereg(state, 0xa2, data); +- +- return 1; +- } +- +- data = ds3000_readreg(state, 0xa2); +- data &= ~0xc0; +- data |= 0x80; +- ds3000_writereg(state, 0xa2, data); +- +- return 0; +-} +- +-static void ds3000_release(struct dvb_frontend *fe) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- dprintk("%s\n", __func__); +- kfree(state); +-} +- +-static struct dvb_frontend_ops ds3000_ops; +- +-struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, +- struct i2c_adapter *i2c) +-{ +- struct ds3000_state *state = NULL; +- int ret; +- +- dprintk("%s\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct ds3000_state), GFP_KERNEL); +- if (state == NULL) { +- printk(KERN_ERR "Unable to kmalloc\n"); +- goto error2; +- } +- +- state->config = config; +- state->i2c = i2c; +- state->prevUCBS2 = 0; +- +- /* check if the demod is present */ +- ret = ds3000_readreg(state, 0x00) & 0xfe; +- if (ret != 0xe0) { +- printk(KERN_ERR "Invalid probe, probably not a DS3000\n"); +- goto error3; +- } +- +- printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n", +- ds3000_readreg(state, 0x02), +- ds3000_readreg(state, 0x01)); +- +- memcpy(&state->frontend.ops, &ds3000_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error3: +- kfree(state); +-error2: +- return NULL; +-} +-EXPORT_SYMBOL(ds3000_attach); +- +-static int ds3000_set_carrier_offset(struct dvb_frontend *fe, +- s32 carrier_offset_khz) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- s32 tmp; +- +- tmp = carrier_offset_khz; +- tmp *= 65536; +- tmp = (2 * tmp + DS3000_SAMPLE_RATE) / (2 * DS3000_SAMPLE_RATE); +- +- if (tmp < 0) +- tmp += 65536; +- +- ds3000_writereg(state, 0x5f, tmp >> 8); +- ds3000_writereg(state, 0x5e, tmp & 0xff); +- +- return 0; +-} +- +-static int ds3000_set_frontend(struct dvb_frontend *fe) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- int i; +- fe_status_t status; +- u8 mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf, div4; +- s32 offset_khz; +- u16 value, ndiv; +- u32 f3db; +- +- dprintk("%s() ", __func__); +- +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- /* Tune */ +- /* unknown */ +- ds3000_tuner_writereg(state, 0x07, 0x02); +- ds3000_tuner_writereg(state, 0x10, 0x00); +- ds3000_tuner_writereg(state, 0x60, 0x79); +- ds3000_tuner_writereg(state, 0x08, 0x01); +- ds3000_tuner_writereg(state, 0x00, 0x01); +- div4 = 0; +- +- /* calculate and set freq divider */ +- if (c->frequency < 1146000) { +- ds3000_tuner_writereg(state, 0x10, 0x11); +- div4 = 1; +- ndiv = ((c->frequency * (6 + 8) * 4) + +- (DS3000_XTAL_FREQ / 2)) / +- DS3000_XTAL_FREQ - 1024; +- } else { +- ds3000_tuner_writereg(state, 0x10, 0x01); +- ndiv = ((c->frequency * (6 + 8) * 2) + +- (DS3000_XTAL_FREQ / 2)) / +- DS3000_XTAL_FREQ - 1024; +- } +- +- ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); +- ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); +- +- /* set pll */ +- ds3000_tuner_writereg(state, 0x03, 0x06); +- ds3000_tuner_writereg(state, 0x51, 0x0f); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x10); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- msleep(5); +- +- /* unknown */ +- ds3000_tuner_writereg(state, 0x51, 0x17); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x08); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- msleep(5); +- +- value = ds3000_tuner_readreg(state, 0x3d); +- value &= 0x0f; +- if ((value > 4) && (value < 15)) { +- value -= 3; +- if (value < 4) +- value = 4; +- value = ((value << 3) | 0x01) & 0x79; +- } +- +- ds3000_tuner_writereg(state, 0x60, value); +- ds3000_tuner_writereg(state, 0x51, 0x17); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x08); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- +- /* set low-pass filter period */ +- ds3000_tuner_writereg(state, 0x04, 0x2e); +- ds3000_tuner_writereg(state, 0x51, 0x1b); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x04); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- msleep(5); +- +- f3db = ((c->symbol_rate / 1000) << 2) / 5 + 2000; +- if ((c->symbol_rate / 1000) < 5000) +- f3db += 3000; +- if (f3db < 7000) +- f3db = 7000; +- if (f3db > 40000) +- f3db = 40000; +- +- /* set low-pass filter baseband */ +- value = ds3000_tuner_readreg(state, 0x26); +- mlpf = 0x2e * 207 / ((value << 1) + 151); +- mlpf_max = mlpf * 135 / 100; +- mlpf_min = mlpf * 78 / 100; +- if (mlpf_max > 63) +- mlpf_max = 63; +- +- /* rounded to the closest integer */ +- nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) +- / (2766 * DS3000_XTAL_FREQ); +- if (nlpf > 23) +- nlpf = 23; +- if (nlpf < 1) +- nlpf = 1; +- +- /* rounded to the closest integer */ +- mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + +- (1000 * f3db / 2)) / (1000 * f3db); +- +- if (mlpf_new < mlpf_min) { +- nlpf++; +- mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + +- (1000 * f3db / 2)) / (1000 * f3db); +- } +- +- if (mlpf_new > mlpf_max) +- mlpf_new = mlpf_max; +- +- ds3000_tuner_writereg(state, 0x04, mlpf_new); +- ds3000_tuner_writereg(state, 0x06, nlpf); +- ds3000_tuner_writereg(state, 0x51, 0x1b); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x04); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- msleep(5); +- +- /* unknown */ +- ds3000_tuner_writereg(state, 0x51, 0x1e); +- ds3000_tuner_writereg(state, 0x51, 0x1f); +- ds3000_tuner_writereg(state, 0x50, 0x01); +- ds3000_tuner_writereg(state, 0x50, 0x00); +- msleep(60); +- +- offset_khz = (ndiv - ndiv % 2 + 1024) * DS3000_XTAL_FREQ +- / (6 + 8) / (div4 + 1) / 2 - c->frequency; +- +- /* ds3000 global reset */ +- ds3000_writereg(state, 0x07, 0x80); +- ds3000_writereg(state, 0x07, 0x00); +- /* ds3000 build-in uC reset */ +- ds3000_writereg(state, 0xb2, 0x01); +- /* ds3000 software reset */ +- ds3000_writereg(state, 0x00, 0x01); +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- /* initialise the demod in DVB-S mode */ +- for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) +- ds3000_writereg(state, +- ds3000_dvbs_init_tab[i], +- ds3000_dvbs_init_tab[i + 1]); +- value = ds3000_readreg(state, 0xfe); +- value &= 0xc0; +- value |= 0x1b; +- ds3000_writereg(state, 0xfe, value); +- break; +- case SYS_DVBS2: +- /* initialise the demod in DVB-S2 mode */ +- for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) +- ds3000_writereg(state, +- ds3000_dvbs2_init_tab[i], +- ds3000_dvbs2_init_tab[i + 1]); +- ds3000_writereg(state, 0xfe, 0x98); +- break; +- default: +- return 1; +- } +- +- /* enable 27MHz clock output */ +- ds3000_writereg(state, 0x29, 0x80); +- /* enable ac coupling */ +- ds3000_writereg(state, 0x25, 0x8a); +- +- /* enhance symbol rate performance */ +- if ((c->symbol_rate / 1000) <= 5000) { +- value = 29777 / (c->symbol_rate / 1000) + 1; +- if (value % 2 != 0) +- value++; +- ds3000_writereg(state, 0xc3, 0x0d); +- ds3000_writereg(state, 0xc8, value); +- ds3000_writereg(state, 0xc4, 0x10); +- ds3000_writereg(state, 0xc7, 0x0e); +- } else if ((c->symbol_rate / 1000) <= 10000) { +- value = 92166 / (c->symbol_rate / 1000) + 1; +- if (value % 2 != 0) +- value++; +- ds3000_writereg(state, 0xc3, 0x07); +- ds3000_writereg(state, 0xc8, value); +- ds3000_writereg(state, 0xc4, 0x09); +- ds3000_writereg(state, 0xc7, 0x12); +- } else if ((c->symbol_rate / 1000) <= 20000) { +- value = 64516 / (c->symbol_rate / 1000) + 1; +- ds3000_writereg(state, 0xc3, value); +- ds3000_writereg(state, 0xc8, 0x0e); +- ds3000_writereg(state, 0xc4, 0x07); +- ds3000_writereg(state, 0xc7, 0x18); +- } else { +- value = 129032 / (c->symbol_rate / 1000) + 1; +- ds3000_writereg(state, 0xc3, value); +- ds3000_writereg(state, 0xc8, 0x0a); +- ds3000_writereg(state, 0xc4, 0x05); +- ds3000_writereg(state, 0xc7, 0x24); +- } +- +- /* normalized symbol rate rounded to the closest integer */ +- value = (((c->symbol_rate / 1000) << 16) + +- (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; +- ds3000_writereg(state, 0x61, value & 0x00ff); +- ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); +- +- /* co-channel interference cancellation disabled */ +- ds3000_writereg(state, 0x56, 0x00); +- +- /* equalizer disabled */ +- ds3000_writereg(state, 0x76, 0x00); +- +- /*ds3000_writereg(state, 0x08, 0x03); +- ds3000_writereg(state, 0xfd, 0x22); +- ds3000_writereg(state, 0x08, 0x07); +- ds3000_writereg(state, 0xfd, 0x42); +- ds3000_writereg(state, 0x08, 0x07);*/ +- +- if (state->config->ci_mode) { +- switch (c->delivery_system) { +- case SYS_DVBS: +- default: +- ds3000_writereg(state, 0xfd, 0x80); +- break; +- case SYS_DVBS2: +- ds3000_writereg(state, 0xfd, 0x01); +- break; +- } +- } +- +- /* ds3000 out of software reset */ +- ds3000_writereg(state, 0x00, 0x00); +- /* start ds3000 build-in uC */ +- ds3000_writereg(state, 0xb2, 0x00); +- +- ds3000_set_carrier_offset(fe, offset_khz); +- +- for (i = 0; i < 30 ; i++) { +- ds3000_read_status(fe, &status); +- if (status & FE_HAS_LOCK) +- break; +- +- msleep(10); +- } +- +- return 0; +-} +- +-static int ds3000_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status) +-{ +- if (re_tune) { +- int ret = ds3000_set_frontend(fe); +- if (ret) +- return ret; +- } +- +- *delay = HZ / 5; +- +- return ds3000_read_status(fe, status); +-} +- +-static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) +-{ +- dprintk("%s()\n", __func__); +- return DVBFE_ALGO_HW; +-} +- +-/* +- * Initialise or wake up device +- * +- * Power config will reset and load initial firmware if required +- */ +-static int ds3000_initfe(struct dvb_frontend *fe) +-{ +- struct ds3000_state *state = fe->demodulator_priv; +- int ret; +- +- dprintk("%s()\n", __func__); +- /* hard reset */ +- ds3000_writereg(state, 0x08, 0x01 | ds3000_readreg(state, 0x08)); +- msleep(1); +- +- /* TS2020 init */ +- ds3000_tuner_writereg(state, 0x42, 0x73); +- ds3000_tuner_writereg(state, 0x05, 0x01); +- ds3000_tuner_writereg(state, 0x62, 0xf5); +- /* Load the firmware if required */ +- ret = ds3000_firmware_ondemand(fe); +- if (ret != 0) { +- printk(KERN_ERR "%s: Unable initialize firmware\n", __func__); +- return ret; +- } +- +- return 0; +-} +- +-/* Put device to sleep */ +-static int ds3000_sleep(struct dvb_frontend *fe) +-{ +- dprintk("%s()\n", __func__); +- return 0; +-} +- +-static struct dvb_frontend_ops ds3000_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2}, +- .info = { +- .name = "Montage Technology DS3000/TS2020", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1011, /* kHz for QPSK frontends */ +- .frequency_tolerance = 5000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_2G_MODULATION | +- FE_CAN_QPSK | FE_CAN_RECOVER +- }, +- +- .release = ds3000_release, +- +- .init = ds3000_initfe, +- .sleep = ds3000_sleep, +- .read_status = ds3000_read_status, +- .read_ber = ds3000_read_ber, +- .read_signal_strength = ds3000_read_signal_strength, +- .read_snr = ds3000_read_snr, +- .read_ucblocks = ds3000_read_ucblocks, +- .set_voltage = ds3000_set_voltage, +- .set_tone = ds3000_set_tone, +- .diseqc_send_master_cmd = ds3000_send_diseqc_msg, +- .diseqc_send_burst = ds3000_diseqc_send_burst, +- .get_frontend_algo = ds3000_get_algo, +- +- .set_frontend = ds3000_set_frontend, +- .tune = ds3000_tune, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " +- "DS3000/TS2020 hardware"); +-MODULE_AUTHOR("Konstantin Dimitrov"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h +deleted file mode 100644 +index 1b73688..0000000 +--- a/drivers/media/dvb/frontends/ds3000.h ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver +- Copyright (C) 2009 Konstantin Dimitrov +- +- Copyright (C) 2009 TurboSight.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. +-*/ +- +-#ifndef DS3000_H +-#define DS3000_H +- +-#include +- +-struct ds3000_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- u8 ci_mode; +- /* Set device param to start dma */ +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +-}; +- +-#if defined(CONFIG_DVB_DS3000) || \ +- (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline +-struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_DS3000 */ +-#endif /* DS3000_H */ +diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c +deleted file mode 100644 +index 1ab3483..0000000 +--- a/drivers/media/dvb/frontends/dvb-pll.c ++++ /dev/null +@@ -1,794 +0,0 @@ +-/* +- * descriptions + helper functions for simple dvb plls. +- * +- * (c) 2004 Gerd Knorr [SuSE Labs] +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb-pll.h" +- +-struct dvb_pll_priv { +- /* pll number */ +- int nr; +- +- /* i2c details */ +- int pll_i2c_address; +- struct i2c_adapter *i2c; +- +- /* the PLL descriptor */ +- struct dvb_pll_desc *pll_desc; +- +- /* cached frequency/bandwidth */ +- u32 frequency; +- u32 bandwidth; +-}; +- +-#define DVB_PLL_MAX 64 +- +-static unsigned int dvb_pll_devcount; +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable verbose debug messages"); +- +-static unsigned int id[DVB_PLL_MAX] = +- { [ 0 ... (DVB_PLL_MAX-1) ] = DVB_PLL_UNDEFINED }; +-module_param_array(id, int, NULL, 0644); +-MODULE_PARM_DESC(id, "force pll id to use (DEBUG ONLY)"); +- +-/* ----------------------------------------------------------- */ +- +-struct dvb_pll_desc { +- char *name; +- u32 min; +- u32 max; +- u32 iffreq; +- void (*set)(struct dvb_frontend *fe, u8 *buf); +- u8 *initdata; +- u8 *initdata2; +- u8 *sleepdata; +- int count; +- struct { +- u32 limit; +- u32 stepsize; +- u8 config; +- u8 cb; +- } entries[12]; +-}; +- +-/* ----------------------------------------------------------- */ +-/* descriptions */ +- +-static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { +- .name = "Thomson dtt7579", +- .min = 177000000, +- .max = 858000000, +- .iffreq= 36166667, +- .sleepdata = (u8[]){ 2, 0xb4, 0x03 }, +- .count = 4, +- .entries = { +- { 443250000, 166667, 0xb4, 0x02 }, +- { 542000000, 166667, 0xb4, 0x08 }, +- { 771000000, 166667, 0xbc, 0x08 }, +- { 999999999, 166667, 0xf4, 0x08 }, +- }, +-}; +- +-static void thomson_dtt759x_bw(struct dvb_frontend *fe, u8 *buf) +-{ +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- if (bw == 7000000) +- buf[3] |= 0x10; +-} +- +-static struct dvb_pll_desc dvb_pll_thomson_dtt759x = { +- .name = "Thomson dtt759x", +- .min = 177000000, +- .max = 896000000, +- .set = thomson_dtt759x_bw, +- .iffreq= 36166667, +- .sleepdata = (u8[]){ 2, 0x84, 0x03 }, +- .count = 5, +- .entries = { +- { 264000000, 166667, 0xb4, 0x02 }, +- { 470000000, 166667, 0xbc, 0x02 }, +- { 735000000, 166667, 0xbc, 0x08 }, +- { 835000000, 166667, 0xf4, 0x08 }, +- { 999999999, 166667, 0xfc, 0x08 }, +- }, +-}; +- +-static struct dvb_pll_desc dvb_pll_lg_z201 = { +- .name = "LG z201", +- .min = 174000000, +- .max = 862000000, +- .iffreq= 36166667, +- .sleepdata = (u8[]){ 2, 0xbc, 0x03 }, +- .count = 5, +- .entries = { +- { 157500000, 166667, 0xbc, 0x01 }, +- { 443250000, 166667, 0xbc, 0x02 }, +- { 542000000, 166667, 0xbc, 0x04 }, +- { 830000000, 166667, 0xf4, 0x04 }, +- { 999999999, 166667, 0xfc, 0x04 }, +- }, +-}; +- +-static struct dvb_pll_desc dvb_pll_unknown_1 = { +- .name = "unknown 1", /* used by dntv live dvb-t */ +- .min = 174000000, +- .max = 862000000, +- .iffreq= 36166667, +- .count = 9, +- .entries = { +- { 150000000, 166667, 0xb4, 0x01 }, +- { 173000000, 166667, 0xbc, 0x01 }, +- { 250000000, 166667, 0xb4, 0x02 }, +- { 400000000, 166667, 0xbc, 0x02 }, +- { 420000000, 166667, 0xf4, 0x02 }, +- { 470000000, 166667, 0xfc, 0x02 }, +- { 600000000, 166667, 0xbc, 0x08 }, +- { 730000000, 166667, 0xf4, 0x08 }, +- { 999999999, 166667, 0xfc, 0x08 }, +- }, +-}; +- +-/* Infineon TUA6010XS +- * used in Thomson Cable Tuner +- */ +-static struct dvb_pll_desc dvb_pll_tua6010xs = { +- .name = "Infineon TUA6010XS", +- .min = 44250000, +- .max = 858000000, +- .iffreq= 36125000, +- .count = 3, +- .entries = { +- { 115750000, 62500, 0x8e, 0x03 }, +- { 403250000, 62500, 0x8e, 0x06 }, +- { 999999999, 62500, 0x8e, 0x85 }, +- }, +-}; +- +-/* Panasonic env57h1xd5 (some Philips PLL ?) */ +-static struct dvb_pll_desc dvb_pll_env57h1xd5 = { +- .name = "Panasonic ENV57H1XD5", +- .min = 44250000, +- .max = 858000000, +- .iffreq= 36125000, +- .count = 4, +- .entries = { +- { 153000000, 166667, 0xc2, 0x41 }, +- { 470000000, 166667, 0xc2, 0x42 }, +- { 526000000, 166667, 0xc2, 0x84 }, +- { 999999999, 166667, 0xc2, 0xa4 }, +- }, +-}; +- +-/* Philips TDA6650/TDA6651 +- * used in Panasonic ENV77H11D5 +- */ +-static void tda665x_bw(struct dvb_frontend *fe, u8 *buf) +-{ +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- if (bw == 8000000) +- buf[3] |= 0x08; +-} +- +-static struct dvb_pll_desc dvb_pll_tda665x = { +- .name = "Philips TDA6650/TDA6651", +- .min = 44250000, +- .max = 858000000, +- .set = tda665x_bw, +- .iffreq= 36166667, +- .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab }, +- .count = 12, +- .entries = { +- { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, +- { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, +- { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, +- { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, +- { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, +- { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, +- { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, +- { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, +- { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, +- { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, +- { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, +- { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, +- } +-}; +- +-/* Infineon TUA6034 +- * used in LG TDTP E102P +- */ +-static void tua6034_bw(struct dvb_frontend *fe, u8 *buf) +-{ +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- if (bw == 7000000) +- buf[3] |= 0x08; +-} +- +-static struct dvb_pll_desc dvb_pll_tua6034 = { +- .name = "Infineon TUA6034", +- .min = 44250000, +- .max = 858000000, +- .iffreq= 36166667, +- .count = 3, +- .set = tua6034_bw, +- .entries = { +- { 174500000, 62500, 0xce, 0x01 }, +- { 230000000, 62500, 0xce, 0x02 }, +- { 999999999, 62500, 0xce, 0x04 }, +- }, +-}; +- +-/* ALPS TDED4 +- * used in Nebula-Cards and USB boxes +- */ +-static void tded4_bw(struct dvb_frontend *fe, u8 *buf) +-{ +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- if (bw == 8000000) +- buf[3] |= 0x04; +-} +- +-static struct dvb_pll_desc dvb_pll_tded4 = { +- .name = "ALPS TDED4", +- .min = 47000000, +- .max = 863000000, +- .iffreq= 36166667, +- .set = tded4_bw, +- .count = 4, +- .entries = { +- { 153000000, 166667, 0x85, 0x01 }, +- { 470000000, 166667, 0x85, 0x02 }, +- { 823000000, 166667, 0x85, 0x08 }, +- { 999999999, 166667, 0x85, 0x88 }, +- } +-}; +- +-/* ALPS TDHU2 +- * used in AverTVHD MCE A180 +- */ +-static struct dvb_pll_desc dvb_pll_tdhu2 = { +- .name = "ALPS TDHU2", +- .min = 54000000, +- .max = 864000000, +- .iffreq= 44000000, +- .count = 4, +- .entries = { +- { 162000000, 62500, 0x85, 0x01 }, +- { 426000000, 62500, 0x85, 0x02 }, +- { 782000000, 62500, 0x85, 0x08 }, +- { 999999999, 62500, 0x85, 0x88 }, +- } +-}; +- +-/* Samsung TBMV30111IN / TBMV30712IN1 +- * used in Air2PC ATSC - 2nd generation (nxt2002) +- */ +-static struct dvb_pll_desc dvb_pll_samsung_tbmv = { +- .name = "Samsung TBMV30111IN / TBMV30712IN1", +- .min = 54000000, +- .max = 860000000, +- .iffreq= 44000000, +- .count = 6, +- .entries = { +- { 172000000, 166667, 0xb4, 0x01 }, +- { 214000000, 166667, 0xb4, 0x02 }, +- { 467000000, 166667, 0xbc, 0x02 }, +- { 721000000, 166667, 0xbc, 0x08 }, +- { 841000000, 166667, 0xf4, 0x08 }, +- { 999999999, 166667, 0xfc, 0x02 }, +- } +-}; +- +-/* +- * Philips SD1878 Tuner. +- */ +-static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { +- .name = "Philips SD1878", +- .min = 950000, +- .max = 2150000, +- .iffreq= 249, /* zero-IF, offset 249 is to round up */ +- .count = 4, +- .entries = { +- { 1250000, 500, 0xc4, 0x00}, +- { 1450000, 500, 0xc4, 0x40}, +- { 2050000, 500, 0xc4, 0x80}, +- { 2150000, 500, 0xc4, 0xc0}, +- }, +-}; +- +-static void opera1_bw(struct dvb_frontend *fe, u8 *buf) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_pll_priv *priv = fe->tuner_priv; +- u32 b_w = (c->symbol_rate * 27) / 32000; +- struct i2c_msg msg = { +- .addr = priv->pll_i2c_address, +- .flags = 0, +- .buf = buf, +- .len = 4 +- }; +- int result; +- u8 lpf; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- result = i2c_transfer(priv->i2c, &msg, 1); +- if (result != 1) +- printk(KERN_ERR "%s: i2c_transfer failed:%d", +- __func__, result); +- +- if (b_w <= 10000) +- lpf = 0xc; +- else if (b_w <= 12000) +- lpf = 0x2; +- else if (b_w <= 14000) +- lpf = 0xa; +- else if (b_w <= 16000) +- lpf = 0x6; +- else if (b_w <= 18000) +- lpf = 0xe; +- else if (b_w <= 20000) +- lpf = 0x1; +- else if (b_w <= 22000) +- lpf = 0x9; +- else if (b_w <= 24000) +- lpf = 0x5; +- else if (b_w <= 26000) +- lpf = 0xd; +- else if (b_w <= 28000) +- lpf = 0x3; +- else +- lpf = 0xb; +- buf[2] ^= 0x1c; /* Flip bits 3-5 */ +- /* Set lpf */ +- buf[2] |= ((lpf >> 2) & 0x3) << 3; +- buf[3] |= (lpf & 0x3) << 2; +- +- return; +-} +- +-static struct dvb_pll_desc dvb_pll_opera1 = { +- .name = "Opera Tuner", +- .min = 900000, +- .max = 2250000, +- .initdata = (u8[]){ 4, 0x08, 0xe5, 0xe1, 0x00 }, +- .initdata2 = (u8[]){ 4, 0x08, 0xe5, 0xe5, 0x00 }, +- .iffreq= 0, +- .set = opera1_bw, +- .count = 8, +- .entries = { +- { 1064000, 500, 0xf9, 0xc2 }, +- { 1169000, 500, 0xf9, 0xe2 }, +- { 1299000, 500, 0xf9, 0x20 }, +- { 1444000, 500, 0xf9, 0x40 }, +- { 1606000, 500, 0xf9, 0x60 }, +- { 1777000, 500, 0xf9, 0x80 }, +- { 1941000, 500, 0xf9, 0xa0 }, +- { 2250000, 500, 0xf9, 0xc0 }, +- } +-}; +- +-static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- struct i2c_msg msg = { +- .addr = priv->pll_i2c_address, +- .flags = 0, +- .buf = buf, +- .len = 4 +- }; +- int result; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- result = i2c_transfer(priv->i2c, &msg, 1); +- if (result != 1) +- printk(KERN_ERR "%s: i2c_transfer failed:%d", +- __func__, result); +- +- buf[2] = 0x9e; +- buf[3] = 0x90; +- +- return; +-} +- +-/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */ +-static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = { +- .name = "Samsung DTOS403IH102A", +- .min = 44250000, +- .max = 858000000, +- .iffreq = 36125000, +- .count = 8, +- .set = samsung_dtos403ih102a_set, +- .entries = { +- { 135000000, 62500, 0xbe, 0x01 }, +- { 177000000, 62500, 0xf6, 0x01 }, +- { 370000000, 62500, 0xbe, 0x02 }, +- { 450000000, 62500, 0xf6, 0x02 }, +- { 466000000, 62500, 0xfe, 0x02 }, +- { 538000000, 62500, 0xbe, 0x08 }, +- { 826000000, 62500, 0xf6, 0x08 }, +- { 999999999, 62500, 0xfe, 0x08 }, +- } +-}; +- +-/* Samsung TDTC9251DH0 DVB-T NIM, as used on AirStar 2 */ +-static struct dvb_pll_desc dvb_pll_samsung_tdtc9251dh0 = { +- .name = "Samsung TDTC9251DH0", +- .min = 48000000, +- .max = 863000000, +- .iffreq = 36166667, +- .count = 3, +- .entries = { +- { 157500000, 166667, 0xcc, 0x09 }, +- { 443000000, 166667, 0xcc, 0x0a }, +- { 863000000, 166667, 0xcc, 0x08 }, +- } +-}; +- +-/* Samsung TBDU18132 DVB-S NIM with TSA5059 PLL, used in SkyStar2 DVB-S 2.3 */ +-static struct dvb_pll_desc dvb_pll_samsung_tbdu18132 = { +- .name = "Samsung TBDU18132", +- .min = 950000, +- .max = 2150000, /* guesses */ +- .iffreq = 0, +- .count = 2, +- .entries = { +- { 1550000, 125, 0x84, 0x82 }, +- { 4095937, 125, 0x84, 0x80 }, +- } +- /* TSA5059 PLL has a 17 bit divisor rather than the 15 bits supported +- * by this driver. The two extra bits are 0x60 in the third byte. 15 +- * bits is enough for over 4 GHz, which is enough to cover the range +- * of this tuner. We could use the additional divisor bits by adding +- * more entries, e.g. +- { 0x0ffff * 125 + 125/2, 125, 0x84 | 0x20, }, +- { 0x17fff * 125 + 125/2, 125, 0x84 | 0x40, }, +- { 0x1ffff * 125 + 125/2, 125, 0x84 | 0x60, }, */ +-}; +- +-/* Samsung TBMU24112 DVB-S NIM with SL1935 zero-IF tuner */ +-static struct dvb_pll_desc dvb_pll_samsung_tbmu24112 = { +- .name = "Samsung TBMU24112", +- .min = 950000, +- .max = 2150000, /* guesses */ +- .iffreq = 0, +- .count = 2, +- .entries = { +- { 1500000, 125, 0x84, 0x18 }, +- { 9999999, 125, 0x84, 0x08 }, +- } +-}; +- +-/* Alps TDEE4 DVB-C NIM, used on Cablestar 2 */ +-/* byte 4 : 1 * * AGD R3 R2 R1 R0 +- * byte 5 : C1 * RE RTS BS4 BS3 BS2 BS1 +- * AGD = 1, R3 R2 R1 R0 = 0 1 0 1 => byte 4 = 1**10101 = 0x95 +- * Range(MHz) C1 * RE RTS BS4 BS3 BS2 BS1 Byte 5 +- * 47 - 153 0 * 0 0 0 0 0 1 0x01 +- * 153 - 430 0 * 0 0 0 0 1 0 0x02 +- * 430 - 822 0 * 0 0 1 0 0 0 0x08 +- * 822 - 862 1 * 0 0 1 0 0 0 0x88 */ +-static struct dvb_pll_desc dvb_pll_alps_tdee4 = { +- .name = "ALPS TDEE4", +- .min = 47000000, +- .max = 862000000, +- .iffreq = 36125000, +- .count = 4, +- .entries = { +- { 153000000, 62500, 0x95, 0x01 }, +- { 430000000, 62500, 0x95, 0x02 }, +- { 822000000, 62500, 0x95, 0x08 }, +- { 999999999, 62500, 0x95, 0x88 }, +- } +-}; +- +-/* ----------------------------------------------------------- */ +- +-static struct dvb_pll_desc *pll_list[] = { +- [DVB_PLL_UNDEFINED] = NULL, +- [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, +- [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, +- [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201, +- [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1, +- [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs, +- [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5, +- [DVB_PLL_TUA6034] = &dvb_pll_tua6034, +- [DVB_PLL_TDA665X] = &dvb_pll_tda665x, +- [DVB_PLL_TDED4] = &dvb_pll_tded4, +- [DVB_PLL_TDEE4] = &dvb_pll_alps_tdee4, +- [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, +- [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, +- [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, +- [DVB_PLL_OPERA1] = &dvb_pll_opera1, +- [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a, +- [DVB_PLL_SAMSUNG_TDTC9251DH0] = &dvb_pll_samsung_tdtc9251dh0, +- [DVB_PLL_SAMSUNG_TBDU18132] = &dvb_pll_samsung_tbdu18132, +- [DVB_PLL_SAMSUNG_TBMU24112] = &dvb_pll_samsung_tbmu24112, +-}; +- +-/* ----------------------------------------------------------- */ +-/* code */ +- +-static int dvb_pll_configure(struct dvb_frontend *fe, u8 *buf, +- const u32 frequency) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- struct dvb_pll_desc *desc = priv->pll_desc; +- u32 div; +- int i; +- +- if (frequency && (frequency < desc->min || frequency > desc->max)) +- return -EINVAL; +- +- for (i = 0; i < desc->count; i++) { +- if (frequency > desc->entries[i].limit) +- continue; +- break; +- } +- +- if (debug) +- printk("pll: %s: freq=%d | i=%d/%d\n", desc->name, +- frequency, i, desc->count); +- if (i == desc->count) +- return -EINVAL; +- +- div = (frequency + desc->iffreq + +- desc->entries[i].stepsize/2) / desc->entries[i].stepsize; +- buf[0] = div >> 8; +- buf[1] = div & 0xff; +- buf[2] = desc->entries[i].config; +- buf[3] = desc->entries[i].cb; +- +- if (desc->set) +- desc->set(fe, buf); +- +- if (debug) +- printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", +- desc->name, div, buf[0], buf[1], buf[2], buf[3]); +- +- // calculate the frequency we set it to +- return (div * desc->entries[i].stepsize) - desc->iffreq; +-} +- +-static int dvb_pll_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int dvb_pll_sleep(struct dvb_frontend *fe) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- +- if (priv->i2c == NULL) +- return -EINVAL; +- +- if (priv->pll_desc->sleepdata) { +- struct i2c_msg msg = { .flags = 0, +- .addr = priv->pll_i2c_address, +- .buf = priv->pll_desc->sleepdata + 1, +- .len = priv->pll_desc->sleepdata[0] }; +- +- int result; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { +- return result; +- } +- return 0; +- } +- /* Shouldn't be called when initdata is NULL, maybe BUG()? */ +- return -EINVAL; +-} +- +-static int dvb_pll_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_pll_priv *priv = fe->tuner_priv; +- u8 buf[4]; +- struct i2c_msg msg = +- { .addr = priv->pll_i2c_address, .flags = 0, +- .buf = buf, .len = sizeof(buf) }; +- int result; +- u32 frequency = 0; +- +- if (priv->i2c == NULL) +- return -EINVAL; +- +- result = dvb_pll_configure(fe, buf, c->frequency); +- if (result < 0) +- return result; +- else +- frequency = result; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { +- return result; +- } +- +- priv->frequency = frequency; +- priv->bandwidth = c->bandwidth_hz; +- +- return 0; +-} +- +-static int dvb_pll_calc_regs(struct dvb_frontend *fe, +- u8 *buf, int buf_len) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct dvb_pll_priv *priv = fe->tuner_priv; +- int result; +- u32 frequency = 0; +- +- if (buf_len < 5) +- return -EINVAL; +- +- result = dvb_pll_configure(fe, buf + 1, c->frequency); +- if (result < 0) +- return result; +- else +- frequency = result; +- +- buf[0] = priv->pll_i2c_address; +- +- priv->frequency = frequency; +- priv->bandwidth = c->bandwidth_hz; +- +- return 5; +-} +- +-static int dvb_pll_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- *bandwidth = priv->bandwidth; +- return 0; +-} +- +-static int dvb_pll_init(struct dvb_frontend *fe) +-{ +- struct dvb_pll_priv *priv = fe->tuner_priv; +- +- if (priv->i2c == NULL) +- return -EINVAL; +- +- if (priv->pll_desc->initdata) { +- struct i2c_msg msg = { .flags = 0, +- .addr = priv->pll_i2c_address, +- .buf = priv->pll_desc->initdata + 1, +- .len = priv->pll_desc->initdata[0] }; +- +- int result; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- result = i2c_transfer(priv->i2c, &msg, 1); +- if (result != 1) +- return result; +- if (priv->pll_desc->initdata2) { +- msg.buf = priv->pll_desc->initdata2 + 1; +- msg.len = priv->pll_desc->initdata2[0]; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- result = i2c_transfer(priv->i2c, &msg, 1); +- if (result != 1) +- return result; +- } +- return 0; +- } +- /* Shouldn't be called when initdata is NULL, maybe BUG()? */ +- return -EINVAL; +-} +- +-static struct dvb_tuner_ops dvb_pll_tuner_ops = { +- .release = dvb_pll_release, +- .sleep = dvb_pll_sleep, +- .init = dvb_pll_init, +- .set_params = dvb_pll_set_params, +- .calc_regs = dvb_pll_calc_regs, +- .get_frequency = dvb_pll_get_frequency, +- .get_bandwidth = dvb_pll_get_bandwidth, +-}; +- +-struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, +- struct i2c_adapter *i2c, +- unsigned int pll_desc_id) +-{ +- u8 b1 [] = { 0 }; +- struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, +- .buf = b1, .len = 1 }; +- struct dvb_pll_priv *priv = NULL; +- int ret; +- struct dvb_pll_desc *desc; +- +- if ((id[dvb_pll_devcount] > DVB_PLL_UNDEFINED) && +- (id[dvb_pll_devcount] < ARRAY_SIZE(pll_list))) +- pll_desc_id = id[dvb_pll_devcount]; +- +- BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list)); +- +- desc = pll_list[pll_desc_id]; +- +- if (i2c != NULL) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = i2c_transfer (i2c, &msg, 1); +- if (ret != 1) +- return NULL; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->pll_i2c_address = pll_addr; +- priv->i2c = i2c; +- priv->pll_desc = desc; +- priv->nr = dvb_pll_devcount++; +- +- memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- strncpy(fe->ops.tuner_ops.info.name, desc->name, +- sizeof(fe->ops.tuner_ops.info.name)); +- fe->ops.tuner_ops.info.frequency_min = desc->min; +- fe->ops.tuner_ops.info.frequency_max = desc->max; +- if (!desc->initdata) +- fe->ops.tuner_ops.init = NULL; +- if (!desc->sleepdata) +- fe->ops.tuner_ops.sleep = NULL; +- +- fe->tuner_priv = priv; +- +- if ((debug) || (id[priv->nr] == pll_desc_id)) { +- printk("dvb-pll[%d]", priv->nr); +- if (i2c != NULL) +- printk(" %d-%04x", i2c_adapter_id(i2c), pll_addr); +- printk(": id# %d (%s) attached, %s\n", pll_desc_id, desc->name, +- id[priv->nr] == pll_desc_id ? +- "insmod option" : "autodetected"); +- } +- +- return fe; +-} +-EXPORT_SYMBOL(dvb_pll_attach); +- +-MODULE_DESCRIPTION("dvb pll library"); +-MODULE_AUTHOR("Gerd Knorr"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h +deleted file mode 100644 +index 0869643..0000000 +--- a/drivers/media/dvb/frontends/dvb-pll.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- * descriptions + helper functions for simple dvb plls. +- */ +- +-#ifndef __DVB_PLL_H__ +-#define __DVB_PLL_H__ +- +-#include +-#include "dvb_frontend.h" +- +-#define DVB_PLL_UNDEFINED 0 +-#define DVB_PLL_THOMSON_DTT7579 1 +-#define DVB_PLL_THOMSON_DTT759X 2 +-#define DVB_PLL_LG_Z201 3 +-#define DVB_PLL_UNKNOWN_1 4 +-#define DVB_PLL_TUA6010XS 5 +-#define DVB_PLL_ENV57H1XD5 6 +-#define DVB_PLL_TUA6034 7 +-#define DVB_PLL_TDA665X 8 +-#define DVB_PLL_TDED4 9 +-#define DVB_PLL_TDHU2 10 +-#define DVB_PLL_SAMSUNG_TBMV 11 +-#define DVB_PLL_PHILIPS_SD1878_TDA8261 12 +-#define DVB_PLL_OPERA1 13 +-#define DVB_PLL_SAMSUNG_DTOS403IH102A 14 +-#define DVB_PLL_SAMSUNG_TDTC9251DH0 15 +-#define DVB_PLL_SAMSUNG_TBDU18132 16 +-#define DVB_PLL_SAMSUNG_TBMU24112 17 +-#define DVB_PLL_TDEE4 18 +- +-/** +- * Attach a dvb-pll to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param pll_addr i2c address of the PLL (if used). +- * @param i2c i2c adapter to use (set to NULL if not used). +- * @param pll_desc_id dvb_pll_desc to use. +- * @return Frontend pointer on success, NULL on failure +- */ +-#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, +- int pll_addr, +- struct i2c_adapter *i2c, +- unsigned int pll_desc_id); +-#else +-static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, +- int pll_addr, +- struct i2c_adapter *i2c, +- unsigned int pll_desc_id) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c +deleted file mode 100644 +index dcfc902..0000000 +--- a/drivers/media/dvb/frontends/dvb_dummy_fe.c ++++ /dev/null +@@ -1,276 +0,0 @@ +-/* +- * Driver for Dummy Frontend +- * +- * Written by Emard +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "dvb_dummy_fe.h" +- +- +-struct dvb_dummy_fe_state { +- struct dvb_frontend frontend; +-}; +- +- +-static int dvb_dummy_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- *status = FE_HAS_SIGNAL +- | FE_HAS_CARRIER +- | FE_HAS_VITERBI +- | FE_HAS_SYNC +- | FE_HAS_LOCK; +- +- return 0; +-} +- +-static int dvb_dummy_fe_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- *ber = 0; +- return 0; +-} +- +-static int dvb_dummy_fe_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- *strength = 0; +- return 0; +-} +- +-static int dvb_dummy_fe_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- *snr = 0; +- return 0; +-} +- +-static int dvb_dummy_fe_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- *ucblocks = 0; +- return 0; +-} +- +-/* +- * Only needed if it actually reads something from the hardware +- */ +-static int dvb_dummy_fe_get_frontend(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int dvb_dummy_fe_set_frontend(struct dvb_frontend *fe) +-{ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- return 0; +-} +- +-static int dvb_dummy_fe_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int dvb_dummy_fe_init(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int dvb_dummy_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- return 0; +-} +- +-static int dvb_dummy_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- return 0; +-} +- +-static void dvb_dummy_fe_release(struct dvb_frontend* fe) +-{ +- struct dvb_dummy_fe_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops; +- +-struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) +-{ +- struct dvb_dummy_fe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &dvb_dummy_fe_ofdm_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops; +- +-struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) +-{ +- struct dvb_dummy_fe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &dvb_dummy_fe_qpsk_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops dvb_dummy_fe_qam_ops; +- +-struct dvb_frontend *dvb_dummy_fe_qam_attach(void) +-{ +- struct dvb_dummy_fe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &dvb_dummy_fe_qam_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops dvb_dummy_fe_ofdm_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Dummy DVB-T", +- .frequency_min = 0, +- .frequency_max = 863250000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = dvb_dummy_fe_release, +- +- .init = dvb_dummy_fe_init, +- .sleep = dvb_dummy_fe_sleep, +- +- .set_frontend = dvb_dummy_fe_set_frontend, +- .get_frontend = dvb_dummy_fe_get_frontend, +- +- .read_status = dvb_dummy_fe_read_status, +- .read_ber = dvb_dummy_fe_read_ber, +- .read_signal_strength = dvb_dummy_fe_read_signal_strength, +- .read_snr = dvb_dummy_fe_read_snr, +- .read_ucblocks = dvb_dummy_fe_read_ucblocks, +-}; +- +-static struct dvb_frontend_ops dvb_dummy_fe_qam_ops = { +- .delsys = { SYS_DVBC_ANNEX_A }, +- .info = { +- .name = "Dummy DVB-C", +- .frequency_stepsize = 62500, +- .frequency_min = 51000000, +- .frequency_max = 858000000, +- .symbol_rate_min = (57840000/2)/64, /* SACLK/64 == (XIN/2)/64 */ +- .symbol_rate_max = (57840000/2)/4, /* SACLK/4 */ +- .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | +- FE_CAN_FEC_AUTO | FE_CAN_INVERSION_AUTO +- }, +- +- .release = dvb_dummy_fe_release, +- +- .init = dvb_dummy_fe_init, +- .sleep = dvb_dummy_fe_sleep, +- +- .set_frontend = dvb_dummy_fe_set_frontend, +- .get_frontend = dvb_dummy_fe_get_frontend, +- +- .read_status = dvb_dummy_fe_read_status, +- .read_ber = dvb_dummy_fe_read_ber, +- .read_signal_strength = dvb_dummy_fe_read_signal_strength, +- .read_snr = dvb_dummy_fe_read_snr, +- .read_ucblocks = dvb_dummy_fe_read_ucblocks, +-}; +- +-static struct dvb_frontend_ops dvb_dummy_fe_qpsk_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Dummy DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 250, /* kHz for QPSK frontends */ +- .frequency_tolerance = 29500, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .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 +- }, +- +- .release = dvb_dummy_fe_release, +- +- .init = dvb_dummy_fe_init, +- .sleep = dvb_dummy_fe_sleep, +- +- .set_frontend = dvb_dummy_fe_set_frontend, +- .get_frontend = dvb_dummy_fe_get_frontend, +- +- .read_status = dvb_dummy_fe_read_status, +- .read_ber = dvb_dummy_fe_read_ber, +- .read_signal_strength = dvb_dummy_fe_read_signal_strength, +- .read_snr = dvb_dummy_fe_read_snr, +- .read_ucblocks = dvb_dummy_fe_read_ucblocks, +- +- .set_voltage = dvb_dummy_fe_set_voltage, +- .set_tone = dvb_dummy_fe_set_tone, +-}; +- +-MODULE_DESCRIPTION("DVB DUMMY Frontend"); +-MODULE_AUTHOR("Emard"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(dvb_dummy_fe_ofdm_attach); +-EXPORT_SYMBOL(dvb_dummy_fe_qam_attach); +-EXPORT_SYMBOL(dvb_dummy_fe_qpsk_attach); +diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h +deleted file mode 100644 +index 1fcb987..0000000 +--- a/drivers/media/dvb/frontends/dvb_dummy_fe.h ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- * Driver for Dummy Frontend +- * +- * Written by Emard +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef DVB_DUMMY_FE_H +-#define DVB_DUMMY_FE_H +- +-#include +-#include "dvb_frontend.h" +- +-#if defined(CONFIG_DVB_DUMMY_FE) || (defined(CONFIG_DVB_DUMMY_FE_MODULE) && \ +-defined(MODULE)) +-extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void); +-extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void); +-extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void); +-#else +-static inline struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline struct dvb_frontend *dvb_dummy_fe_qam_attach(void) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_DUMMY_FE */ +- +-#endif // DVB_DUMMY_FE_H +diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c +deleted file mode 100644 +index c56fddb..0000000 +--- a/drivers/media/dvb/frontends/ec100.c ++++ /dev/null +@@ -1,335 +0,0 @@ +-/* +- * E3C EC100 demodulator driver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include "dvb_frontend.h" +-#include "ec100_priv.h" +-#include "ec100.h" +- +-int ec100_debug; +-module_param_named(debug, ec100_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-struct ec100_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend frontend; +- struct ec100_config config; +- +- u16 ber; +-}; +- +-/* write single register */ +-static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val) +-{ +- u8 buf[2] = {reg, val}; +- struct i2c_msg msg = { +- .addr = state->config.demod_address, +- .flags = 0, +- .len = 2, +- .buf = buf}; +- +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- warn("I2C write failed reg:%02x", reg); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* read single register */ +-static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val) +-{ +- struct i2c_msg msg[2] = { +- { +- .addr = state->config.demod_address, +- .flags = 0, +- .len = 1, +- .buf = ® +- }, { +- .addr = state->config.demod_address, +- .flags = I2C_M_RD, +- .len = 1, +- .buf = val +- } +- }; +- +- if (i2c_transfer(state->i2c, msg, 2) != 2) { +- warn("I2C read failed reg:%02x", reg); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int ec100_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct ec100_state *state = fe->demodulator_priv; +- int ret; +- u8 tmp, tmp2; +- +- deb_info("%s: freq:%d bw:%d\n", __func__, c->frequency, +- c->bandwidth_hz); +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- ret = ec100_write_reg(state, 0x04, 0x06); +- if (ret) +- goto error; +- ret = ec100_write_reg(state, 0x67, 0x58); +- if (ret) +- goto error; +- ret = ec100_write_reg(state, 0x05, 0x18); +- if (ret) +- goto error; +- +- /* reg/bw | 6 | 7 | 8 +- -------+------+------+------ +- A 0x1b | 0xa1 | 0xe7 | 0x2c +- A 0x1c | 0x55 | 0x63 | 0x72 +- -------+------+------+------ +- B 0x1b | 0xb7 | 0x00 | 0x49 +- B 0x1c | 0x55 | 0x64 | 0x72 */ +- +- switch (c->bandwidth_hz) { +- case 6000000: +- tmp = 0xb7; +- tmp2 = 0x55; +- break; +- case 7000000: +- tmp = 0x00; +- tmp2 = 0x64; +- break; +- case 8000000: +- default: +- tmp = 0x49; +- tmp2 = 0x72; +- } +- +- ret = ec100_write_reg(state, 0x1b, tmp); +- if (ret) +- goto error; +- ret = ec100_write_reg(state, 0x1c, tmp2); +- if (ret) +- goto error; +- +- ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */ +- if (ret) +- goto error; +- ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */ +- if (ret) +- goto error; +- +- ret = ec100_write_reg(state, 0x08, 0x24); +- if (ret) +- goto error; +- +- ret = ec100_write_reg(state, 0x00, 0x00); /* go */ +- if (ret) +- goto error; +- ret = ec100_write_reg(state, 0x00, 0x20); /* go */ +- if (ret) +- goto error; +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec100_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- fesettings->min_delay_ms = 300; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- +- return 0; +-} +- +-static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct ec100_state *state = fe->demodulator_priv; +- int ret; +- u8 tmp; +- *status = 0; +- +- ret = ec100_read_reg(state, 0x42, &tmp); +- if (ret) +- goto error; +- +- if (tmp & 0x80) { +- /* bit7 set - have lock */ +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | +- FE_HAS_SYNC | FE_HAS_LOCK; +- } else { +- ret = ec100_read_reg(state, 0x01, &tmp); +- if (ret) +- goto error; +- +- if (tmp & 0x10) { +- /* bit4 set - have signal */ +- *status |= FE_HAS_SIGNAL; +- if (!(tmp & 0x01)) { +- /* bit0 clear - have ~valid signal */ +- *status |= FE_HAS_CARRIER | FE_HAS_VITERBI; +- } +- } +- } +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct ec100_state *state = fe->demodulator_priv; +- int ret; +- u8 tmp, tmp2; +- u16 ber2; +- +- *ber = 0; +- +- ret = ec100_read_reg(state, 0x65, &tmp); +- if (ret) +- goto error; +- ret = ec100_read_reg(state, 0x66, &tmp2); +- if (ret) +- goto error; +- +- ber2 = (tmp2 << 8) | tmp; +- +- /* if counter overflow or clear */ +- if (ber2 < state->ber) +- *ber = ber2; +- else +- *ber = ber2 - state->ber; +- +- state->ber = ber2; +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct ec100_state *state = fe->demodulator_priv; +- int ret; +- u8 tmp; +- +- ret = ec100_read_reg(state, 0x24, &tmp); +- if (ret) { +- *strength = 0; +- goto error; +- } +- +- *strength = ((tmp << 8) | tmp); +- +- return ret; +-error: +- deb_info("%s: failed:%d\n", __func__, ret); +- return ret; +-} +- +-static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- *snr = 0; +- return 0; +-} +- +-static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- return 0; +-} +- +-static void ec100_release(struct dvb_frontend *fe) +-{ +- struct ec100_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops ec100_ops; +- +-struct dvb_frontend *ec100_attach(const struct ec100_config *config, +- struct i2c_adapter *i2c) +-{ +- int ret; +- struct ec100_state *state = NULL; +- u8 tmp; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- memcpy(&state->config, config, sizeof(struct ec100_config)); +- +- /* check if the demod is there */ +- ret = ec100_read_reg(state, 0x33, &tmp); +- if (ret || tmp != 0x0b) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &ec100_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(ec100_attach); +- +-static struct dvb_frontend_ops ec100_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "E3C EC100 DVB-T", +- .caps = +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | +- FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | +- FE_CAN_MUTE_TS +- }, +- +- .release = ec100_release, +- .set_frontend = ec100_set_frontend, +- .get_tune_settings = ec100_get_tune_settings, +- .read_status = ec100_read_status, +- .read_ber = ec100_read_ber, +- .read_signal_strength = ec100_read_signal_strength, +- .read_snr = ec100_read_snr, +- .read_ucblocks = ec100_read_ucblocks, +-}; +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h +deleted file mode 100644 +index ee8e524..0000000 +--- a/drivers/media/dvb/frontends/ec100.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- * E3C EC100 demodulator driver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef EC100_H +-#define EC100_H +- +-#include +- +-struct ec100_config { +- /* demodulator's I2C address */ +- u8 demod_address; +-}; +- +- +-#if defined(CONFIG_DVB_EC100) || \ +- (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *ec100_attach( +- const struct ec100_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* EC100_H */ +diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h +deleted file mode 100644 +index 5c99014..0000000 +--- a/drivers/media/dvb/frontends/ec100_priv.h ++++ /dev/null +@@ -1,39 +0,0 @@ +-/* +- * E3C EC100 demodulator driver +- * +- * Copyright (C) 2009 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef EC100_PRIV +-#define EC100_PRIV +- +-#define LOG_PREFIX "ec100" +- +-#define dprintk(var, level, args...) \ +- do { if ((var & level)) printk(args); } while (0) +- +-#define deb_info(args...) dprintk(ec100_debug, 0x01, args) +- +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-#endif /* EC100_PRIV */ +diff --git a/drivers/media/dvb/frontends/eds1547.h b/drivers/media/dvb/frontends/eds1547.h +deleted file mode 100644 +index c983f2f..0000000 +--- a/drivers/media/dvb/frontends/eds1547.h ++++ /dev/null +@@ -1,133 +0,0 @@ +-/* eds1547.h Earda EDS-1547 tuner support +-* +-* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +-* +-* This program is free software; you can redistribute it and/or modify it +-* under the terms of the GNU General Public License as published by the +-* Free Software Foundation, version 2. +-* +-* see Documentation/dvb/README.dvb-usb for more information +-*/ +- +-#ifndef EDS1547 +-#define EDS1547 +- +-static u8 stv0288_earda_inittab[] = { +- 0x01, 0x57, +- 0x02, 0x20, +- 0x03, 0x8e, +- 0x04, 0x8e, +- 0x05, 0x12, +- 0x06, 0x00, +- 0x07, 0x00, +- 0x09, 0x00, +- 0x0a, 0x04, +- 0x0b, 0x00, +- 0x0c, 0x00, +- 0x0d, 0x00, +- 0x0e, 0xd4, +- 0x0f, 0x30, +- 0x11, 0x44, +- 0x12, 0x03, +- 0x13, 0x48, +- 0x14, 0x84, +- 0x15, 0x45, +- 0x16, 0xb7, +- 0x17, 0x9c, +- 0x18, 0x00, +- 0x19, 0xa6, +- 0x1a, 0x88, +- 0x1b, 0x8f, +- 0x1c, 0xf0, +- 0x20, 0x0b, +- 0x21, 0x54, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x2b, 0xff, +- 0x2c, 0xf7, +- 0x30, 0x00, +- 0x31, 0x1e, +- 0x32, 0x14, +- 0x33, 0x0f, +- 0x34, 0x09, +- 0x35, 0x0c, +- 0x36, 0x05, +- 0x37, 0x2f, +- 0x38, 0x16, +- 0x39, 0xbd, +- 0x3a, 0x00, +- 0x3b, 0x13, +- 0x3c, 0x11, +- 0x3d, 0x30, +- 0x40, 0x63, +- 0x41, 0x04, +- 0x42, 0x20, +- 0x43, 0x00, +- 0x44, 0x00, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x47, 0x00, +- 0x4a, 0x00, +- 0x50, 0x10, +- 0x51, 0x36, +- 0x52, 0x09, +- 0x53, 0x94, +- 0x54, 0x62, +- 0x55, 0x29, +- 0x56, 0x64, +- 0x57, 0x2b, +- 0x58, 0x54, +- 0x59, 0x86, +- 0x5a, 0x00, +- 0x5b, 0x9b, +- 0x5c, 0x08, +- 0x5d, 0x7f, +- 0x5e, 0x00, +- 0x5f, 0xff, +- 0x70, 0x00, +- 0x71, 0x00, +- 0x72, 0x00, +- 0x74, 0x00, +- 0x75, 0x00, +- 0x76, 0x00, +- 0x81, 0x00, +- 0x82, 0x3f, +- 0x83, 0x3f, +- 0x84, 0x00, +- 0x85, 0x00, +- 0x88, 0x00, +- 0x89, 0x00, +- 0x8a, 0x00, +- 0x8b, 0x00, +- 0x8c, 0x00, +- 0x90, 0x00, +- 0x91, 0x00, +- 0x92, 0x00, +- 0x93, 0x00, +- 0x94, 0x1c, +- 0x97, 0x00, +- 0xa0, 0x48, +- 0xa1, 0x00, +- 0xb0, 0xb8, +- 0xb1, 0x3a, +- 0xb2, 0x10, +- 0xb3, 0x82, +- 0xb4, 0x80, +- 0xb5, 0x82, +- 0xb6, 0x82, +- 0xb7, 0x82, +- 0xb8, 0x20, +- 0xb9, 0x00, +- 0xf0, 0x00, +- 0xf1, 0x00, +- 0xf2, 0xc0, +- 0xff,0xff, +-}; +- +-static struct stv0288_config earda_config = { +- .demod_address = 0x68, +- .min_delay_ms = 100, +- .inittab = stv0288_earda_inittab, +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/hd29l2.c b/drivers/media/dvb/frontends/hd29l2.c +deleted file mode 100644 +index a003181..0000000 +--- a/drivers/media/dvb/frontends/hd29l2.c ++++ /dev/null +@@ -1,861 +0,0 @@ +-/* +- * HDIC HD29L2 DMB-TH demodulator driver +- * +- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D +- * +- * Author: Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "hd29l2_priv.h" +- +-int hd29l2_debug; +-module_param_named(debug, hd29l2_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-/* write multiple registers */ +-static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) +-{ +- int ret; +- u8 buf[2 + len]; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->cfg.i2c_addr, +- .flags = 0, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- buf[0] = 0x00; +- buf[1] = reg; +- memcpy(&buf[2], val, len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* read multiple registers */ +-static int hd29l2_rd_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) +-{ +- int ret; +- u8 buf[2] = { 0x00, reg }; +- struct i2c_msg msg[2] = { +- { +- .addr = priv->cfg.i2c_addr, +- .flags = 0, +- .len = 2, +- .buf = buf, +- }, { +- .addr = priv->cfg.i2c_addr, +- .flags = I2C_M_RD, +- .len = len, +- .buf = val, +- } +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- ret = 0; +- } else { +- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- +- return ret; +-} +- +-/* write single register */ +-static int hd29l2_wr_reg(struct hd29l2_priv *priv, u8 reg, u8 val) +-{ +- return hd29l2_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +-static int hd29l2_rd_reg(struct hd29l2_priv *priv, u8 reg, u8 *val) +-{ +- return hd29l2_rd_regs(priv, reg, val, 1); +-} +- +-/* write single register with mask */ +-static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask) +-{ +- int ret; +- u8 tmp; +- +- /* no need for read if whole reg is written */ +- if (mask != 0xff) { +- ret = hd29l2_rd_regs(priv, reg, &tmp, 1); +- if (ret) +- return ret; +- +- val &= mask; +- tmp &= ~mask; +- val |= tmp; +- } +- +- return hd29l2_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register with mask */ +-int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) +-{ +- int ret, i; +- u8 tmp; +- +- ret = hd29l2_rd_regs(priv, reg, &tmp, 1); +- if (ret) +- return ret; +- +- tmp &= mask; +- +- /* find position of the first bit */ +- for (i = 0; i < 8; i++) { +- if ((mask >> i) & 0x01) +- break; +- } +- *val = tmp >> i; +- +- return 0; +-} +- +-static int hd29l2_soft_reset(struct hd29l2_priv *priv) +-{ +- int ret; +- u8 tmp; +- +- ret = hd29l2_rd_reg(priv, 0x26, &tmp); +- if (ret) +- goto err; +- +- ret = hd29l2_wr_reg(priv, 0x26, 0x0d); +- if (ret) +- goto err; +- +- usleep_range(10000, 20000); +- +- ret = hd29l2_wr_reg(priv, 0x26, tmp); +- if (ret) +- goto err; +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- int ret, i; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 tmp; +- +- dbg("%s: enable=%d", __func__, enable); +- +- /* set tuner address for demod */ +- if (!priv->tuner_i2c_addr_programmed && enable) { +- /* no need to set tuner address every time, once is enough */ +- ret = hd29l2_wr_reg(priv, 0x9d, priv->cfg.tuner_i2c_addr << 1); +- if (ret) +- goto err; +- +- priv->tuner_i2c_addr_programmed = true; +- } +- +- /* open / close gate */ +- ret = hd29l2_wr_reg(priv, 0x9f, enable); +- if (ret) +- goto err; +- +- /* wait demod ready */ +- for (i = 10; i; i--) { +- ret = hd29l2_rd_reg(priv, 0x9e, &tmp); +- if (ret) +- goto err; +- +- if (tmp == enable) +- break; +- +- usleep_range(5000, 10000); +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- int ret; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 buf[2]; +- +- *status = 0; +- +- ret = hd29l2_rd_reg(priv, 0x05, &buf[0]); +- if (ret) +- goto err; +- +- if (buf[0] & 0x01) { +- /* full lock */ +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | +- FE_HAS_SYNC | FE_HAS_LOCK; +- } else { +- ret = hd29l2_rd_reg(priv, 0x0d, &buf[1]); +- if (ret) +- goto err; +- +- if ((buf[1] & 0xfe) == 0x78) +- /* partial lock */ +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC; +- } +- +- priv->fe_status = *status; +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- int ret; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 buf[2]; +- u16 tmp; +- +- if (!(priv->fe_status & FE_HAS_LOCK)) { +- *snr = 0; +- ret = 0; +- goto err; +- } +- +- ret = hd29l2_rd_regs(priv, 0x0b, buf, 2); +- if (ret) +- goto err; +- +- tmp = (buf[0] << 8) | buf[1]; +- +- /* report SNR in dB * 10 */ +- #define LOG10_20736_24 72422627 /* log10(20736) << 24 */ +- if (tmp) +- *snr = (LOG10_20736_24 - intlog10(tmp)) / ((1 << 24) / 100); +- else +- *snr = 0; +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- int ret; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 buf[2]; +- u16 tmp; +- +- *strength = 0; +- +- ret = hd29l2_rd_regs(priv, 0xd5, buf, 2); +- if (ret) +- goto err; +- +- tmp = buf[0] << 8 | buf[1]; +- tmp = ~tmp & 0x0fff; +- +- /* scale value to 0x0000-0xffff from 0x0000-0x0fff */ +- *strength = tmp * 0xffff / 0x0fff; +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- int ret; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 buf[2]; +- +- if (!(priv->fe_status & FE_HAS_SYNC)) { +- *ber = 0; +- ret = 0; +- goto err; +- } +- +- ret = hd29l2_rd_regs(priv, 0xd9, buf, 2); +- if (ret) { +- *ber = 0; +- goto err; +- } +- +- /* LDPC BER */ +- *ber = ((buf[0] & 0x0f) << 8) | buf[1]; +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- /* no way to read? */ +- *ucblocks = 0; +- return 0; +-} +- +-static enum dvbfe_search hd29l2_search(struct dvb_frontend *fe) +-{ +- int ret, i; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 tmp, buf[3]; +- u8 modulation, carrier, guard_interval, interleave, code_rate; +- u64 num64; +- u32 if_freq, if_ctl; +- bool auto_mode; +- +- dbg("%s: delivery_system=%d frequency=%d bandwidth_hz=%d " \ +- "modulation=%d inversion=%d fec_inner=%d guard_interval=%d", +- __func__, +- c->delivery_system, c->frequency, c->bandwidth_hz, +- c->modulation, c->inversion, c->fec_inner, c->guard_interval); +- +- /* as for now we detect always params automatically */ +- auto_mode = true; +- +- /* program tuner */ +- if (fe->ops.tuner_ops.set_params) +- fe->ops.tuner_ops.set_params(fe); +- +- /* get and program IF */ +- if (fe->ops.tuner_ops.get_if_frequency) +- fe->ops.tuner_ops.get_if_frequency(fe, &if_freq); +- else +- if_freq = 0; +- +- if (if_freq) { +- /* normal IF */ +- +- /* calc IF control value */ +- num64 = if_freq; +- num64 *= 0x800000; +- num64 = div_u64(num64, HD29L2_XTAL); +- num64 -= 0x800000; +- if_ctl = num64; +- +- tmp = 0xfc; /* tuner type normal */ +- } else { +- /* zero IF */ +- if_ctl = 0; +- tmp = 0xfe; /* tuner type Zero-IF */ +- } +- +- buf[0] = ((if_ctl >> 0) & 0xff); +- buf[1] = ((if_ctl >> 8) & 0xff); +- buf[2] = ((if_ctl >> 16) & 0xff); +- +- /* program IF control */ +- ret = hd29l2_wr_regs(priv, 0x14, buf, 3); +- if (ret) +- goto err; +- +- /* program tuner type */ +- ret = hd29l2_wr_reg(priv, 0xab, tmp); +- if (ret) +- goto err; +- +- dbg("%s: if_freq=%d if_ctl=%x", __func__, if_freq, if_ctl); +- +- if (auto_mode) { +- /* +- * use auto mode +- */ +- +- /* disable quick mode */ +- ret = hd29l2_wr_reg_mask(priv, 0xac, 0 << 7, 0x80); +- if (ret) +- goto err; +- +- ret = hd29l2_wr_reg_mask(priv, 0x82, 1 << 1, 0x02); +- if (ret) +- goto err; +- +- /* enable auto mode */ +- ret = hd29l2_wr_reg_mask(priv, 0x7d, 1 << 6, 0x40); +- if (ret) +- goto err; +- +- ret = hd29l2_wr_reg_mask(priv, 0x81, 1 << 3, 0x08); +- if (ret) +- goto err; +- +- /* soft reset */ +- ret = hd29l2_soft_reset(priv); +- if (ret) +- goto err; +- +- /* detect modulation */ +- for (i = 30; i; i--) { +- msleep(100); +- +- ret = hd29l2_rd_reg(priv, 0x0d, &tmp); +- if (ret) +- goto err; +- +- if ((((tmp & 0xf0) >= 0x10) && +- ((tmp & 0x0f) == 0x08)) || (tmp >= 0x2c)) +- break; +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) +- /* detection failed */ +- return DVBFE_ALGO_SEARCH_FAILED; +- +- /* read modulation */ +- ret = hd29l2_rd_reg_mask(priv, 0x7d, &modulation, 0x07); +- if (ret) +- goto err; +- } else { +- /* +- * use manual mode +- */ +- +- modulation = HD29L2_QAM64; +- carrier = HD29L2_CARRIER_MULTI; +- guard_interval = HD29L2_PN945; +- interleave = HD29L2_INTERLEAVER_420; +- code_rate = HD29L2_CODE_RATE_08; +- +- tmp = (code_rate << 3) | modulation; +- ret = hd29l2_wr_reg_mask(priv, 0x7d, tmp, 0x5f); +- if (ret) +- goto err; +- +- tmp = (carrier << 2) | guard_interval; +- ret = hd29l2_wr_reg_mask(priv, 0x81, tmp, 0x0f); +- if (ret) +- goto err; +- +- tmp = interleave; +- ret = hd29l2_wr_reg_mask(priv, 0x82, tmp, 0x03); +- if (ret) +- goto err; +- } +- +- /* ensure modulation validy */ +- /* 0=QAM4_NR, 1=QAM4, 2=QAM16, 3=QAM32, 4=QAM64 */ +- if (modulation > (ARRAY_SIZE(reg_mod_vals_tab[0].val) - 1)) { +- dbg("%s: modulation=%d not valid", __func__, modulation); +- goto err; +- } +- +- /* program registers according to modulation */ +- for (i = 0; i < ARRAY_SIZE(reg_mod_vals_tab); i++) { +- ret = hd29l2_wr_reg(priv, reg_mod_vals_tab[i].reg, +- reg_mod_vals_tab[i].val[modulation]); +- if (ret) +- goto err; +- } +- +- /* read guard interval */ +- ret = hd29l2_rd_reg_mask(priv, 0x81, &guard_interval, 0x03); +- if (ret) +- goto err; +- +- /* read carrier mode */ +- ret = hd29l2_rd_reg_mask(priv, 0x81, &carrier, 0x04); +- if (ret) +- goto err; +- +- dbg("%s: modulation=%d guard_interval=%d carrier=%d", +- __func__, modulation, guard_interval, carrier); +- +- if ((carrier == HD29L2_CARRIER_MULTI) && (modulation == HD29L2_QAM64) && +- (guard_interval == HD29L2_PN945)) { +- dbg("%s: C=3780 && QAM64 && PN945", __func__); +- +- ret = hd29l2_wr_reg(priv, 0x42, 0x33); +- if (ret) +- goto err; +- +- ret = hd29l2_wr_reg(priv, 0xdd, 0x01); +- if (ret) +- goto err; +- } +- +- usleep_range(10000, 20000); +- +- /* soft reset */ +- ret = hd29l2_soft_reset(priv); +- if (ret) +- goto err; +- +- /* wait demod lock */ +- for (i = 30; i; i--) { +- msleep(100); +- +- /* read lock bit */ +- ret = hd29l2_rd_reg_mask(priv, 0x05, &tmp, 0x01); +- if (ret) +- goto err; +- +- if (tmp) +- break; +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) +- return DVBFE_ALGO_SEARCH_AGAIN; +- +- return DVBFE_ALGO_SEARCH_SUCCESS; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return DVBFE_ALGO_SEARCH_ERROR; +-} +- +-static int hd29l2_get_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-static int hd29l2_get_frontend(struct dvb_frontend *fe) +-{ +- int ret; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 buf[3]; +- u32 if_ctl; +- char *str_constellation, *str_code_rate, *str_constellation_code_rate, +- *str_guard_interval, *str_carrier, *str_guard_interval_carrier, +- *str_interleave, *str_interleave_; +- +- ret = hd29l2_rd_reg(priv, 0x7d, &buf[0]); +- if (ret) +- goto err; +- +- ret = hd29l2_rd_regs(priv, 0x81, &buf[1], 2); +- if (ret) +- goto err; +- +- /* constellation, 0x7d[2:0] */ +- switch ((buf[0] >> 0) & 0x07) { +- case 0: /* QAM4NR */ +- str_constellation = "QAM4NR"; +- c->modulation = QAM_AUTO; /* FIXME */ +- break; +- case 1: /* QAM4 */ +- str_constellation = "QAM4"; +- c->modulation = QPSK; /* FIXME */ +- break; +- case 2: +- str_constellation = "QAM16"; +- c->modulation = QAM_16; +- break; +- case 3: +- str_constellation = "QAM32"; +- c->modulation = QAM_32; +- break; +- case 4: +- str_constellation = "QAM64"; +- c->modulation = QAM_64; +- break; +- default: +- str_constellation = "?"; +- } +- +- /* LDPC code rate, 0x7d[4:3] */ +- switch ((buf[0] >> 3) & 0x03) { +- case 0: /* 0.4 */ +- str_code_rate = "0.4"; +- c->fec_inner = FEC_AUTO; /* FIXME */ +- break; +- case 1: /* 0.6 */ +- str_code_rate = "0.6"; +- c->fec_inner = FEC_3_5; +- break; +- case 2: /* 0.8 */ +- str_code_rate = "0.8"; +- c->fec_inner = FEC_4_5; +- break; +- default: +- str_code_rate = "?"; +- } +- +- /* constellation & code rate set, 0x7d[6] */ +- switch ((buf[0] >> 6) & 0x01) { +- case 0: +- str_constellation_code_rate = "manual"; +- break; +- case 1: +- str_constellation_code_rate = "auto"; +- break; +- default: +- str_constellation_code_rate = "?"; +- } +- +- /* frame header, 0x81[1:0] */ +- switch ((buf[1] >> 0) & 0x03) { +- case 0: /* PN945 */ +- str_guard_interval = "PN945"; +- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ +- break; +- case 1: /* PN595 */ +- str_guard_interval = "PN595"; +- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ +- break; +- case 2: /* PN420 */ +- str_guard_interval = "PN420"; +- c->guard_interval = GUARD_INTERVAL_AUTO; /* FIXME */ +- break; +- default: +- str_guard_interval = "?"; +- } +- +- /* carrier, 0x81[2] */ +- switch ((buf[1] >> 2) & 0x01) { +- case 0: +- str_carrier = "C=1"; +- break; +- case 1: +- str_carrier = "C=3780"; +- break; +- default: +- str_carrier = "?"; +- } +- +- /* frame header & carrier set, 0x81[3] */ +- switch ((buf[1] >> 3) & 0x01) { +- case 0: +- str_guard_interval_carrier = "manual"; +- break; +- case 1: +- str_guard_interval_carrier = "auto"; +- break; +- default: +- str_guard_interval_carrier = "?"; +- } +- +- /* interleave, 0x82[0] */ +- switch ((buf[2] >> 0) & 0x01) { +- case 0: +- str_interleave = "M=720"; +- break; +- case 1: +- str_interleave = "M=240"; +- break; +- default: +- str_interleave = "?"; +- } +- +- /* interleave set, 0x82[1] */ +- switch ((buf[2] >> 1) & 0x01) { +- case 0: +- str_interleave_ = "manual"; +- break; +- case 1: +- str_interleave_ = "auto"; +- break; +- default: +- str_interleave_ = "?"; +- } +- +- /* +- * We can read out current detected NCO and use that value next +- * time instead of calculating new value from targed IF. +- * I think it will not effect receiver sensitivity but gaining lock +- * after tune could be easier... +- */ +- ret = hd29l2_rd_regs(priv, 0xb1, &buf[0], 3); +- if (ret) +- goto err; +- +- if_ctl = (buf[0] << 16) | ((buf[1] - 7) << 8) | buf[2]; +- +- dbg("%s: %s %s %s | %s %s %s | %s %s | NCO=%06x", __func__, +- str_constellation, str_code_rate, str_constellation_code_rate, +- str_guard_interval, str_carrier, str_guard_interval_carrier, +- str_interleave, str_interleave_, if_ctl); +- +- return 0; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int hd29l2_init(struct dvb_frontend *fe) +-{ +- int ret, i; +- struct hd29l2_priv *priv = fe->demodulator_priv; +- u8 tmp; +- static const struct reg_val tab[] = { +- { 0x3a, 0x06 }, +- { 0x3b, 0x03 }, +- { 0x3c, 0x04 }, +- { 0xaf, 0x06 }, +- { 0xb0, 0x1b }, +- { 0x80, 0x64 }, +- { 0x10, 0x38 }, +- }; +- +- dbg("%s:", __func__); +- +- /* reset demod */ +- /* it is recommended to HW reset chip using RST_N pin */ +- if (fe->callback) { +- ret = fe->callback(fe, DVB_FRONTEND_COMPONENT_DEMOD, 0, 0); +- if (ret) +- goto err; +- +- /* reprogramming needed because HW reset clears registers */ +- priv->tuner_i2c_addr_programmed = false; +- } +- +- /* init */ +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = hd29l2_wr_reg(priv, tab[i].reg, tab[i].val); +- if (ret) +- goto err; +- } +- +- /* TS params */ +- ret = hd29l2_rd_reg(priv, 0x36, &tmp); +- if (ret) +- goto err; +- +- tmp &= 0x1b; +- tmp |= priv->cfg.ts_mode; +- ret = hd29l2_wr_reg(priv, 0x36, tmp); +- if (ret) +- goto err; +- +- ret = hd29l2_rd_reg(priv, 0x31, &tmp); +- tmp &= 0xef; +- +- if (!(priv->cfg.ts_mode >> 7)) +- /* set b4 for serial TS */ +- tmp |= 0x10; +- +- ret = hd29l2_wr_reg(priv, 0x31, tmp); +- if (ret) +- goto err; +- +- return ret; +-err: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static void hd29l2_release(struct dvb_frontend *fe) +-{ +- struct hd29l2_priv *priv = fe->demodulator_priv; +- kfree(priv); +-} +- +-static struct dvb_frontend_ops hd29l2_ops; +- +-struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config, +- struct i2c_adapter *i2c) +-{ +- int ret; +- struct hd29l2_priv *priv = NULL; +- u8 tmp; +- +- /* allocate memory for the internal state */ +- priv = kzalloc(sizeof(struct hd29l2_priv), GFP_KERNEL); +- if (priv == NULL) +- goto err; +- +- /* setup the state */ +- priv->i2c = i2c; +- memcpy(&priv->cfg, config, sizeof(struct hd29l2_config)); +- +- +- /* check if the demod is there */ +- ret = hd29l2_rd_reg(priv, 0x00, &tmp); +- if (ret) +- goto err; +- +- /* create dvb_frontend */ +- memcpy(&priv->fe.ops, &hd29l2_ops, sizeof(struct dvb_frontend_ops)); +- priv->fe.demodulator_priv = priv; +- +- return &priv->fe; +-err: +- kfree(priv); +- return NULL; +-} +-EXPORT_SYMBOL(hd29l2_attach); +- +-static struct dvb_frontend_ops hd29l2_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "HDIC HD29L2 DMB-TH", +- .frequency_min = 474000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 10000, +- .caps = FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | +- FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_BANDWIDTH_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | +- FE_CAN_RECOVER +- }, +- +- .release = hd29l2_release, +- +- .init = hd29l2_init, +- +- .get_frontend_algo = hd29l2_get_frontend_algo, +- .search = hd29l2_search, +- .get_frontend = hd29l2_get_frontend, +- +- .read_status = hd29l2_read_status, +- .read_snr = hd29l2_read_snr, +- .read_signal_strength = hd29l2_read_signal_strength, +- .read_ber = hd29l2_read_ber, +- .read_ucblocks = hd29l2_read_ucblocks, +- +- .i2c_gate_ctrl = hd29l2_i2c_gate_ctrl, +-}; +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("HDIC HD29L2 DMB-TH demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/hd29l2.h b/drivers/media/dvb/frontends/hd29l2.h +deleted file mode 100644 +index a7a6443..0000000 +--- a/drivers/media/dvb/frontends/hd29l2.h ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* +- * HDIC HD29L2 DMB-TH demodulator driver +- * +- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D +- * +- * Author: Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef HD29L2_H +-#define HD29L2_H +- +-#include +- +-struct hd29l2_config { +- /* +- * demodulator I2C address +- */ +- u8 i2c_addr; +- +- /* +- * tuner I2C address +- * only needed when tuner is behind demod I2C-gate +- */ +- u8 tuner_i2c_addr; +- +- /* +- * TS settings +- */ +-#define HD29L2_TS_SERIAL 0x00 +-#define HD29L2_TS_PARALLEL 0x80 +-#define HD29L2_TS_CLK_NORMAL 0x40 +-#define HD29L2_TS_CLK_INVERTED 0x00 +-#define HD29L2_TS_CLK_GATED 0x20 +-#define HD29L2_TS_CLK_FREE 0x00 +- u8 ts_mode; +-}; +- +- +-#if defined(CONFIG_DVB_HD29L2) || \ +- (defined(CONFIG_DVB_HD29L2_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *hd29l2_attach(const struct hd29l2_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *hd29l2_attach( +-const struct hd29l2_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* HD29L2_H */ +diff --git a/drivers/media/dvb/frontends/hd29l2_priv.h b/drivers/media/dvb/frontends/hd29l2_priv.h +deleted file mode 100644 +index ba16dc3..0000000 +--- a/drivers/media/dvb/frontends/hd29l2_priv.h ++++ /dev/null +@@ -1,314 +0,0 @@ +-/* +- * HDIC HD29L2 DMB-TH demodulator driver +- * +- * Copyright (C) 2011 Metropolia University of Applied Sciences, Electria R&D +- * +- * Author: Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef HD29L2_PRIV +-#define HD29L2_PRIV +- +-#include +-#include "dvb_frontend.h" +-#include "dvb_math.h" +-#include "hd29l2.h" +- +-#define LOG_PREFIX "hd29l2" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (hd29l2_debug) \ +- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-#define HD29L2_XTAL 30400000 /* Hz */ +- +- +-#define HD29L2_QAM4NR 0x00 +-#define HD29L2_QAM4 0x01 +-#define HD29L2_QAM16 0x02 +-#define HD29L2_QAM32 0x03 +-#define HD29L2_QAM64 0x04 +- +-#define HD29L2_CODE_RATE_04 0x00 +-#define HD29L2_CODE_RATE_06 0x08 +-#define HD29L2_CODE_RATE_08 0x10 +- +-#define HD29L2_PN945 0x00 +-#define HD29L2_PN595 0x01 +-#define HD29L2_PN420 0x02 +- +-#define HD29L2_CARRIER_SINGLE 0x00 +-#define HD29L2_CARRIER_MULTI 0x01 +- +-#define HD29L2_INTERLEAVER_720 0x00 +-#define HD29L2_INTERLEAVER_420 0x01 +- +-struct reg_val { +- u8 reg; +- u8 val; +-}; +- +-struct reg_mod_vals { +- u8 reg; +- u8 val[5]; +-}; +- +-struct hd29l2_priv { +- struct i2c_adapter *i2c; +- struct dvb_frontend fe; +- struct hd29l2_config cfg; +- u8 tuner_i2c_addr_programmed:1; +- +- fe_status_t fe_status; +-}; +- +-static const struct reg_mod_vals reg_mod_vals_tab[] = { +- /* REG, QAM4NR, QAM4,QAM16,QAM32,QAM64 */ +- { 0x01, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, +- { 0x02, { 0x07, 0x07, 0x07, 0x07, 0x07 } }, +- { 0x03, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, +- { 0x04, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x05, { 0x61, 0x60, 0x60, 0x61, 0x60 } }, +- { 0x06, { 0xff, 0xff, 0xff, 0xff, 0xff } }, +- { 0x07, { 0xff, 0xff, 0xff, 0xff, 0xff } }, +- { 0x08, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x09, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x0a, { 0x15, 0x15, 0x03, 0x03, 0x03 } }, +- { 0x0d, { 0x78, 0x78, 0x88, 0x78, 0x78 } }, +- { 0x0e, { 0xa0, 0x90, 0xa0, 0xa0, 0xa0 } }, +- { 0x0f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x10, { 0xa0, 0xa0, 0x58, 0x38, 0x38 } }, +- { 0x11, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x12, { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a } }, +- { 0x13, { 0xa2, 0xa2, 0xa2, 0xa2, 0xa2 } }, +- { 0x17, { 0x40, 0x40, 0x40, 0x40, 0x40 } }, +- { 0x18, { 0x21, 0x21, 0x42, 0x52, 0x42 } }, +- { 0x19, { 0x21, 0x21, 0x62, 0x72, 0x62 } }, +- { 0x1a, { 0x32, 0x43, 0xa9, 0xb9, 0xa9 } }, +- { 0x1b, { 0x32, 0x43, 0xb9, 0xd8, 0xb9 } }, +- { 0x1c, { 0x02, 0x02, 0x03, 0x02, 0x03 } }, +- { 0x1d, { 0x0c, 0x0c, 0x01, 0x02, 0x02 } }, +- { 0x1e, { 0x02, 0x02, 0x02, 0x01, 0x02 } }, +- { 0x1f, { 0x02, 0x02, 0x01, 0x02, 0x04 } }, +- { 0x20, { 0x01, 0x02, 0x01, 0x01, 0x01 } }, +- { 0x21, { 0x08, 0x08, 0x0a, 0x0a, 0x0a } }, +- { 0x22, { 0x06, 0x06, 0x04, 0x05, 0x05 } }, +- { 0x23, { 0x06, 0x06, 0x05, 0x03, 0x05 } }, +- { 0x24, { 0x08, 0x08, 0x05, 0x07, 0x07 } }, +- { 0x25, { 0x16, 0x10, 0x10, 0x0a, 0x10 } }, +- { 0x26, { 0x14, 0x14, 0x04, 0x04, 0x04 } }, +- { 0x27, { 0x58, 0x58, 0x58, 0x5c, 0x58 } }, +- { 0x28, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0x29, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0x2a, { 0x08, 0x0a, 0x08, 0x08, 0x08 } }, +- { 0x2b, { 0x08, 0x08, 0x08, 0x08, 0x08 } }, +- { 0x2c, { 0x06, 0x06, 0x06, 0x06, 0x06 } }, +- { 0x2d, { 0x05, 0x06, 0x06, 0x06, 0x06 } }, +- { 0x2e, { 0x21, 0x21, 0x21, 0x21, 0x21 } }, +- { 0x2f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x30, { 0x14, 0x14, 0x14, 0x14, 0x14 } }, +- { 0x33, { 0xb7, 0xb7, 0xb7, 0xb7, 0xb7 } }, +- { 0x34, { 0x81, 0x81, 0x81, 0x81, 0x81 } }, +- { 0x35, { 0x80, 0x80, 0x80, 0x80, 0x80 } }, +- { 0x37, { 0x70, 0x70, 0x70, 0x70, 0x70 } }, +- { 0x38, { 0x04, 0x04, 0x02, 0x02, 0x02 } }, +- { 0x39, { 0x07, 0x07, 0x05, 0x05, 0x05 } }, +- { 0x3a, { 0x06, 0x06, 0x06, 0x06, 0x06 } }, +- { 0x3b, { 0x03, 0x03, 0x03, 0x03, 0x03 } }, +- { 0x3c, { 0x07, 0x06, 0x04, 0x04, 0x04 } }, +- { 0x3d, { 0xf0, 0xf0, 0xf0, 0xf0, 0x80 } }, +- { 0x3e, { 0x60, 0x60, 0x60, 0x60, 0xff } }, +- { 0x3f, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x40, { 0x5b, 0x5b, 0x5b, 0x57, 0x50 } }, +- { 0x41, { 0x30, 0x30, 0x30, 0x30, 0x18 } }, +- { 0x42, { 0x20, 0x20, 0x20, 0x00, 0x30 } }, +- { 0x43, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x44, { 0x3f, 0x3f, 0x3f, 0x3f, 0x3f } }, +- { 0x45, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x46, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0x47, { 0x00, 0x00, 0x95, 0x00, 0x95 } }, +- { 0x48, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } }, +- { 0x49, { 0xc0, 0xc0, 0xc0, 0xc0, 0xc0 } }, +- { 0x4a, { 0x40, 0x40, 0x33, 0x11, 0x11 } }, +- { 0x4b, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, +- { 0x4c, { 0x40, 0x40, 0x99, 0x11, 0x11 } }, +- { 0x4d, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, +- { 0x4e, { 0x40, 0x40, 0x66, 0x77, 0x77 } }, +- { 0x4f, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, +- { 0x50, { 0x40, 0x40, 0x88, 0x33, 0x11 } }, +- { 0x51, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, +- { 0x52, { 0x40, 0x40, 0x88, 0x02, 0x02 } }, +- { 0x53, { 0x40, 0x40, 0x00, 0x02, 0x02 } }, +- { 0x54, { 0x00, 0x00, 0x88, 0x33, 0x33 } }, +- { 0x55, { 0x40, 0x40, 0x00, 0x00, 0x00 } }, +- { 0x56, { 0x00, 0x00, 0x00, 0x0b, 0x00 } }, +- { 0x57, { 0x40, 0x40, 0x0a, 0x0b, 0x0a } }, +- { 0x58, { 0xaa, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x59, { 0x7a, 0x40, 0x02, 0x02, 0x02 } }, +- { 0x5a, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, +- { 0x5b, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, +- { 0x5c, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, +- { 0x5d, { 0x18, 0x18, 0x01, 0x01, 0x01 } }, +- { 0x5e, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } }, +- { 0x5f, { 0xc0, 0xc0, 0xc0, 0xff, 0xc0 } }, +- { 0x60, { 0x40, 0x40, 0x00, 0x30, 0x30 } }, +- { 0x61, { 0x40, 0x40, 0x10, 0x30, 0x30 } }, +- { 0x62, { 0x40, 0x40, 0x00, 0x30, 0x30 } }, +- { 0x63, { 0x40, 0x40, 0x05, 0x30, 0x30 } }, +- { 0x64, { 0x40, 0x40, 0x06, 0x00, 0x30 } }, +- { 0x65, { 0x40, 0x40, 0x06, 0x08, 0x30 } }, +- { 0x66, { 0x40, 0x40, 0x00, 0x00, 0x20 } }, +- { 0x67, { 0x40, 0x40, 0x01, 0x04, 0x20 } }, +- { 0x68, { 0x00, 0x00, 0x30, 0x00, 0x20 } }, +- { 0x69, { 0xa0, 0xa0, 0x00, 0x08, 0x20 } }, +- { 0x6a, { 0x00, 0x00, 0x30, 0x00, 0x25 } }, +- { 0x6b, { 0xa0, 0xa0, 0x00, 0x06, 0x25 } }, +- { 0x6c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x6d, { 0xa0, 0x60, 0x0c, 0x03, 0x0c } }, +- { 0x6e, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x6f, { 0xa0, 0x60, 0x04, 0x01, 0x04 } }, +- { 0x70, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } }, +- { 0x71, { 0x58, 0x58, 0xaa, 0xaa, 0xaa } }, +- { 0x72, { 0x58, 0x58, 0xff, 0xff, 0xff } }, +- { 0x73, { 0x58, 0x58, 0xff, 0xff, 0xff } }, +- { 0x74, { 0x06, 0x06, 0x09, 0x05, 0x05 } }, +- { 0x75, { 0x06, 0x06, 0x0a, 0x10, 0x10 } }, +- { 0x76, { 0x10, 0x10, 0x06, 0x0a, 0x0a } }, +- { 0x77, { 0x12, 0x18, 0x28, 0x10, 0x28 } }, +- { 0x78, { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8 } }, +- { 0x79, { 0x15, 0x15, 0x03, 0x03, 0x03 } }, +- { 0x7a, { 0x02, 0x02, 0x01, 0x04, 0x03 } }, +- { 0x7b, { 0x01, 0x02, 0x03, 0x03, 0x03 } }, +- { 0x7c, { 0x28, 0x28, 0x28, 0x28, 0x28 } }, +- { 0x7f, { 0x25, 0x92, 0x5f, 0x17, 0x2d } }, +- { 0x80, { 0x64, 0x64, 0x64, 0x74, 0x64 } }, +- { 0x83, { 0x06, 0x03, 0x04, 0x04, 0x04 } }, +- { 0x84, { 0xff, 0xff, 0xff, 0xff, 0xff } }, +- { 0x85, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, +- { 0x86, { 0x00, 0x00, 0x11, 0x11, 0x11 } }, +- { 0x87, { 0x03, 0x03, 0x03, 0x03, 0x03 } }, +- { 0x88, { 0x09, 0x09, 0x09, 0x09, 0x09 } }, +- { 0x89, { 0x20, 0x20, 0x30, 0x20, 0x20 } }, +- { 0x8a, { 0x03, 0x03, 0x02, 0x03, 0x02 } }, +- { 0x8b, { 0x00, 0x07, 0x09, 0x00, 0x09 } }, +- { 0x8c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x8d, { 0x4f, 0x4f, 0x4f, 0x3f, 0x4f } }, +- { 0x8e, { 0xf0, 0xf0, 0x60, 0xf0, 0xa0 } }, +- { 0x8f, { 0xe8, 0xe8, 0xe8, 0xe8, 0xe8 } }, +- { 0x90, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, +- { 0x91, { 0x40, 0x40, 0x70, 0x70, 0x10 } }, +- { 0x92, { 0x00, 0x00, 0x00, 0x00, 0x04 } }, +- { 0x93, { 0x60, 0x60, 0x60, 0x60, 0x60 } }, +- { 0x94, { 0x00, 0x00, 0x00, 0x00, 0x03 } }, +- { 0x95, { 0x09, 0x09, 0x47, 0x47, 0x47 } }, +- { 0x96, { 0x80, 0xa0, 0xa0, 0x40, 0xa0 } }, +- { 0x97, { 0x60, 0x60, 0x60, 0x60, 0x60 } }, +- { 0x98, { 0x50, 0x50, 0x50, 0x30, 0x50 } }, +- { 0x99, { 0x10, 0x10, 0x10, 0x10, 0x10 } }, +- { 0x9a, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0x9b, { 0x40, 0x40, 0x40, 0x30, 0x40 } }, +- { 0x9c, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa0, { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 } }, +- { 0xa1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa2, { 0x30, 0x30, 0x00, 0x30, 0x00 } }, +- { 0xa3, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa4, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa5, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xa8, { 0x77, 0x77, 0x77, 0x77, 0x77 } }, +- { 0xa9, { 0x02, 0x02, 0x02, 0x02, 0x02 } }, +- { 0xaa, { 0x40, 0x40, 0x40, 0x40, 0x40 } }, +- { 0xac, { 0x1f, 0x1f, 0x1f, 0x1f, 0x1f } }, +- { 0xad, { 0x14, 0x14, 0x14, 0x14, 0x14 } }, +- { 0xae, { 0x78, 0x78, 0x78, 0x78, 0x78 } }, +- { 0xaf, { 0x06, 0x06, 0x06, 0x06, 0x07 } }, +- { 0xb0, { 0x1b, 0x1b, 0x1b, 0x19, 0x1b } }, +- { 0xb1, { 0x18, 0x17, 0x17, 0x18, 0x17 } }, +- { 0xb2, { 0x35, 0x82, 0x82, 0x38, 0x82 } }, +- { 0xb3, { 0xb6, 0xce, 0xc7, 0x5c, 0xb0 } }, +- { 0xb4, { 0x3f, 0x3e, 0x3e, 0x3f, 0x3e } }, +- { 0xb5, { 0x70, 0x58, 0x50, 0x68, 0x50 } }, +- { 0xb6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xb7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xb8, { 0x03, 0x03, 0x01, 0x01, 0x01 } }, +- { 0xb9, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xba, { 0x06, 0x06, 0x0a, 0x05, 0x0a } }, +- { 0xbb, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xbc, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xbd, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xbe, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xbf, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc3, { 0x00, 0x00, 0x88, 0x66, 0x88 } }, +- { 0xc4, { 0x10, 0x10, 0x00, 0x00, 0x00 } }, +- { 0xc5, { 0x00, 0x00, 0x44, 0x60, 0x44 } }, +- { 0xc6, { 0x10, 0x0a, 0x00, 0x00, 0x00 } }, +- { 0xc7, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc8, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xc9, { 0x90, 0x04, 0x00, 0x00, 0x00 } }, +- { 0xca, { 0x90, 0x08, 0x01, 0x01, 0x01 } }, +- { 0xcb, { 0xa0, 0x04, 0x00, 0x44, 0x00 } }, +- { 0xcc, { 0xa0, 0x10, 0x03, 0x00, 0x03 } }, +- { 0xcd, { 0x06, 0x06, 0x06, 0x05, 0x06 } }, +- { 0xce, { 0x05, 0x05, 0x01, 0x01, 0x01 } }, +- { 0xcf, { 0x40, 0x20, 0x18, 0x18, 0x18 } }, +- { 0xd0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xd1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xd2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xd3, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xd4, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, +- { 0xd5, { 0x05, 0x05, 0x05, 0x03, 0x05 } }, +- { 0xd6, { 0xac, 0x22, 0xca, 0x8f, 0xca } }, +- { 0xd7, { 0x20, 0x20, 0x20, 0x20, 0x20 } }, +- { 0xd8, { 0x01, 0x01, 0x01, 0x01, 0x01 } }, +- { 0xd9, { 0x00, 0x00, 0x0f, 0x00, 0x0f } }, +- { 0xda, { 0x00, 0xff, 0xff, 0x0e, 0xff } }, +- { 0xdb, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0xdc, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0xdd, { 0x05, 0x05, 0x05, 0x05, 0x05 } }, +- { 0xde, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0xdf, { 0x42, 0x42, 0x44, 0x44, 0x04 } }, +- { 0xe0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xe1, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xe2, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xe3, { 0x00, 0x00, 0x26, 0x06, 0x26 } }, +- { 0xe4, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xe5, { 0x01, 0x0a, 0x01, 0x01, 0x01 } }, +- { 0xe6, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xe7, { 0x08, 0x08, 0x08, 0x08, 0x08 } }, +- { 0xe8, { 0x63, 0x63, 0x63, 0x63, 0x63 } }, +- { 0xe9, { 0x59, 0x59, 0x59, 0x59, 0x59 } }, +- { 0xea, { 0x80, 0x80, 0x20, 0x80, 0x80 } }, +- { 0xeb, { 0x37, 0x37, 0x78, 0x37, 0x77 } }, +- { 0xec, { 0x1f, 0x1f, 0x25, 0x25, 0x25 } }, +- { 0xed, { 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } }, +- { 0xee, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +- { 0xef, { 0x70, 0x70, 0x58, 0x38, 0x58 } }, +- { 0xf0, { 0x00, 0x00, 0x00, 0x00, 0x00 } }, +-}; +- +-#endif /* HD29L2_PRIV */ +diff --git a/drivers/media/dvb/frontends/isl6405.c b/drivers/media/dvb/frontends/isl6405.c +deleted file mode 100644 +index 33d33f4..0000000 +--- a/drivers/media/dvb/frontends/isl6405.c ++++ /dev/null +@@ -1,164 +0,0 @@ +-/* +- * isl6405.c - driver for dual lnb supply and control ic ISL6405 +- * +- * Copyright (C) 2008 Hartmut Hackmann +- * Copyright (C) 2006 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "isl6405.h" +- +-struct isl6405 { +- u8 config; +- u8 override_or; +- u8 override_and; +- struct i2c_adapter *i2c; +- u8 i2c_addr; +-}; +- +-static int isl6405_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, +- .buf = &isl6405->config, +- .len = sizeof(isl6405->config) }; +- +- if (isl6405->override_or & 0x80) { +- isl6405->config &= ~(ISL6405_VSEL2 | ISL6405_EN2); +- switch (voltage) { +- case SEC_VOLTAGE_OFF: +- break; +- case SEC_VOLTAGE_13: +- isl6405->config |= ISL6405_EN2; +- break; +- case SEC_VOLTAGE_18: +- isl6405->config |= (ISL6405_EN2 | ISL6405_VSEL2); +- break; +- default: +- return -EINVAL; +- } +- } else { +- isl6405->config &= ~(ISL6405_VSEL1 | ISL6405_EN1); +- switch (voltage) { +- case SEC_VOLTAGE_OFF: +- break; +- case SEC_VOLTAGE_13: +- isl6405->config |= ISL6405_EN1; +- break; +- case SEC_VOLTAGE_18: +- isl6405->config |= (ISL6405_EN1 | ISL6405_VSEL1); +- break; +- default: +- return -EINVAL; +- }; +- } +- isl6405->config |= isl6405->override_or; +- isl6405->config &= isl6405->override_and; +- +- return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static int isl6405_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +-{ +- struct isl6405 *isl6405 = (struct isl6405 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = isl6405->i2c_addr, .flags = 0, +- .buf = &isl6405->config, +- .len = sizeof(isl6405->config) }; +- +- if (isl6405->override_or & 0x80) { +- if (arg) +- isl6405->config |= ISL6405_LLC2; +- else +- isl6405->config &= ~ISL6405_LLC2; +- } else { +- if (arg) +- isl6405->config |= ISL6405_LLC1; +- else +- isl6405->config &= ~ISL6405_LLC1; +- } +- isl6405->config |= isl6405->override_or; +- isl6405->config &= isl6405->override_and; +- +- return (i2c_transfer(isl6405->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static void isl6405_release(struct dvb_frontend *fe) +-{ +- /* power off */ +- isl6405_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- /* free */ +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, +- u8 i2c_addr, u8 override_set, u8 override_clear) +-{ +- struct isl6405 *isl6405 = kmalloc(sizeof(struct isl6405), GFP_KERNEL); +- if (!isl6405) +- return NULL; +- +- /* default configuration */ +- if (override_set & 0x80) +- isl6405->config = ISL6405_ISEL2; +- else +- isl6405->config = ISL6405_ISEL1; +- isl6405->i2c = i2c; +- isl6405->i2c_addr = i2c_addr; +- fe->sec_priv = isl6405; +- +- /* bits which should be forced to '1' */ +- isl6405->override_or = override_set; +- +- /* bits which should be forced to '0' */ +- isl6405->override_and = ~override_clear; +- +- /* detect if it is present or not */ +- if (isl6405_set_voltage(fe, SEC_VOLTAGE_OFF)) { +- kfree(isl6405); +- fe->sec_priv = NULL; +- return NULL; +- } +- +- /* install release callback */ +- fe->ops.release_sec = isl6405_release; +- +- /* override frontend ops */ +- fe->ops.set_voltage = isl6405_set_voltage; +- fe->ops.enable_high_lnb_voltage = isl6405_enable_high_lnb_voltage; +- +- return fe; +-} +-EXPORT_SYMBOL(isl6405_attach); +- +-MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405"); +-MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/isl6405.h b/drivers/media/dvb/frontends/isl6405.h +deleted file mode 100644 +index 1c793d3..0000000 +--- a/drivers/media/dvb/frontends/isl6405.h ++++ /dev/null +@@ -1,74 +0,0 @@ +-/* +- * isl6405.h - driver for dual lnb supply and control ic ISL6405 +- * +- * Copyright (C) 2008 Hartmut Hackmann +- * Copyright (C) 2006 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef _ISL6405_H +-#define _ISL6405_H +- +-#include +- +-/* system register bits */ +- +-/* this bit selects register (control) 1 or 2 +- note that the bit maps are different */ +- +-#define ISL6405_SR 0x80 +- +-/* SR = 0 */ +-#define ISL6405_OLF1 0x01 +-#define ISL6405_EN1 0x02 +-#define ISL6405_VSEL1 0x04 +-#define ISL6405_LLC1 0x08 +-#define ISL6405_ENT1 0x10 +-#define ISL6405_ISEL1 0x20 +-#define ISL6405_DCL 0x40 +- +-/* SR = 1 */ +-#define ISL6405_OLF2 0x01 +-#define ISL6405_OTF 0x02 +-#define ISL6405_EN2 0x04 +-#define ISL6405_VSEL2 0x08 +-#define ISL6405_LLC2 0x10 +-#define ISL6405_ENT2 0x20 +-#define ISL6405_ISEL2 0x40 +- +-#if defined(CONFIG_DVB_ISL6405) || (defined(CONFIG_DVB_ISL6405_MODULE) && defined(MODULE)) +-/* override_set and override_clear control which system register bits (above) +- * to always set & clear +- */ +-extern struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, +- u8 i2c_addr, u8 override_set, u8 override_clear); +-#else +-static inline struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 i2c_addr, +- u8 override_set, u8 override_clear) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_ISL6405 */ +- +-#endif +diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb/frontends/isl6421.c +deleted file mode 100644 +index 684c8ec..0000000 +--- a/drivers/media/dvb/frontends/isl6421.c ++++ /dev/null +@@ -1,141 +0,0 @@ +-/* +- * isl6421.h - driver for lnb supply and control ic ISL6421 +- * +- * Copyright (C) 2006 Andrew de Quincey +- * Copyright (C) 2006 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "isl6421.h" +- +-struct isl6421 { +- u8 config; +- u8 override_or; +- u8 override_and; +- struct i2c_adapter *i2c; +- u8 i2c_addr; +-}; +- +-static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, +- .buf = &isl6421->config, +- .len = sizeof(isl6421->config) }; +- +- isl6421->config &= ~(ISL6421_VSEL1 | ISL6421_EN1); +- +- switch(voltage) { +- case SEC_VOLTAGE_OFF: +- break; +- case SEC_VOLTAGE_13: +- isl6421->config |= ISL6421_EN1; +- break; +- case SEC_VOLTAGE_18: +- isl6421->config |= (ISL6421_EN1 | ISL6421_VSEL1); +- break; +- default: +- return -EINVAL; +- }; +- +- isl6421->config |= isl6421->override_or; +- isl6421->config &= isl6421->override_and; +- +- return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +-{ +- struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0, +- .buf = &isl6421->config, +- .len = sizeof(isl6421->config) }; +- +- if (arg) +- isl6421->config |= ISL6421_LLC1; +- else +- isl6421->config &= ~ISL6421_LLC1; +- +- isl6421->config |= isl6421->override_or; +- isl6421->config &= isl6421->override_and; +- +- return (i2c_transfer(isl6421->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static void isl6421_release(struct dvb_frontend *fe) +-{ +- /* power off */ +- isl6421_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- /* free */ +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, +- u8 override_set, u8 override_clear) +-{ +- struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL); +- if (!isl6421) +- return NULL; +- +- /* default configuration */ +- isl6421->config = ISL6421_ISEL1; +- isl6421->i2c = i2c; +- isl6421->i2c_addr = i2c_addr; +- fe->sec_priv = isl6421; +- +- /* bits which should be forced to '1' */ +- isl6421->override_or = override_set; +- +- /* bits which should be forced to '0' */ +- isl6421->override_and = ~override_clear; +- +- /* detect if it is present or not */ +- if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { +- kfree(isl6421); +- fe->sec_priv = NULL; +- return NULL; +- } +- +- /* install release callback */ +- fe->ops.release_sec = isl6421_release; +- +- /* override frontend ops */ +- fe->ops.set_voltage = isl6421_set_voltage; +- fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage; +- +- return fe; +-} +-EXPORT_SYMBOL(isl6421_attach); +- +-MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); +-MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/isl6421.h b/drivers/media/dvb/frontends/isl6421.h +deleted file mode 100644 +index 47e4518..0000000 +--- a/drivers/media/dvb/frontends/isl6421.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- * isl6421.h - driver for lnb supply and control ic ISL6421 +- * +- * Copyright (C) 2006 Andrew de Quincey +- * Copyright (C) 2006 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef _ISL6421_H +-#define _ISL6421_H +- +-#include +- +-/* system register bits */ +-#define ISL6421_OLF1 0x01 +-#define ISL6421_EN1 0x02 +-#define ISL6421_VSEL1 0x04 +-#define ISL6421_LLC1 0x08 +-#define ISL6421_ENT1 0x10 +-#define ISL6421_ISEL1 0x20 +-#define ISL6421_DCL 0x40 +- +-#if defined(CONFIG_DVB_ISL6421) || (defined(CONFIG_DVB_ISL6421_MODULE) && defined(MODULE)) +-/* override_set and override_clear control which system register bits (above) to always set & clear */ +-extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, +- u8 override_set, u8 override_clear); +-#else +-static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr, +- u8 override_set, u8 override_clear) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_ISL6421 +- +-#endif +diff --git a/drivers/media/dvb/frontends/isl6423.c b/drivers/media/dvb/frontends/isl6423.c +deleted file mode 100644 +index dca5beb..0000000 +--- a/drivers/media/dvb/frontends/isl6423.c ++++ /dev/null +@@ -1,308 +0,0 @@ +-/* +- Intersil ISL6423 SEC and LNB Power supply controller +- +- Copyright (C) Manu Abraham +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "isl6423.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "Set Verbosity level"); +- +-#define FE_ERROR 0 +-#define FE_NOTICE 1 +-#define FE_INFO 2 +-#define FE_DEBUG 3 +-#define FE_DEBUGREG 4 +- +-#define dprintk(__y, __z, format, arg...) do { \ +- if (__z) { \ +- if ((verbose > FE_ERROR) && (verbose > __y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_NOTICE) && (verbose > __y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_INFO) && (verbose > __y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_DEBUG) && (verbose > __y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (verbose > __y) \ +- printk(format, ##arg); \ +- } \ +-} while (0) +- +-struct isl6423_dev { +- const struct isl6423_config *config; +- struct i2c_adapter *i2c; +- +- u8 reg_3; +- u8 reg_4; +- +- unsigned int verbose; +-}; +- +-static int isl6423_write(struct isl6423_dev *isl6423, u8 reg) +-{ +- struct i2c_adapter *i2c = isl6423->i2c; +- u8 addr = isl6423->config->addr; +- int err = 0; +- +- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = ®, .len = 1 }; +- +- dprintk(FE_DEBUG, 1, "write reg %02X", reg); +- err = i2c_transfer(i2c, &msg, 1); +- if (err < 0) +- goto exit; +- return 0; +- +-exit: +- dprintk(FE_ERROR, 1, "I/O error <%d>", err); +- return err; +-} +- +-static int isl6423_set_modulation(struct dvb_frontend *fe) +-{ +- struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; +- const struct isl6423_config *config = isl6423->config; +- int err = 0; +- u8 reg_2 = 0; +- +- reg_2 = 0x01 << 5; +- +- if (config->mod_extern) +- reg_2 |= (1 << 3); +- else +- reg_2 |= (1 << 4); +- +- err = isl6423_write(isl6423, reg_2); +- if (err < 0) +- goto exit; +- return 0; +- +-exit: +- dprintk(FE_ERROR, 1, "I/O error <%d>", err); +- return err; +-} +- +-static int isl6423_voltage_boost(struct dvb_frontend *fe, long arg) +-{ +- struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; +- u8 reg_3 = isl6423->reg_3; +- u8 reg_4 = isl6423->reg_4; +- int err = 0; +- +- if (arg) { +- /* EN = 1, VSPEN = 1, VBOT = 1 */ +- reg_4 |= (1 << 4); +- reg_4 |= 0x1; +- reg_3 |= (1 << 3); +- } else { +- /* EN = 1, VSPEN = 1, VBOT = 0 */ +- reg_4 |= (1 << 4); +- reg_4 &= ~0x1; +- reg_3 |= (1 << 3); +- } +- err = isl6423_write(isl6423, reg_3); +- if (err < 0) +- goto exit; +- +- err = isl6423_write(isl6423, reg_4); +- if (err < 0) +- goto exit; +- +- isl6423->reg_3 = reg_3; +- isl6423->reg_4 = reg_4; +- +- return 0; +-exit: +- dprintk(FE_ERROR, 1, "I/O error <%d>", err); +- return err; +-} +- +- +-static int isl6423_set_voltage(struct dvb_frontend *fe, +- enum fe_sec_voltage voltage) +-{ +- struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; +- u8 reg_3 = isl6423->reg_3; +- u8 reg_4 = isl6423->reg_4; +- int err = 0; +- +- switch (voltage) { +- case SEC_VOLTAGE_OFF: +- /* EN = 0 */ +- reg_4 &= ~(1 << 4); +- break; +- +- case SEC_VOLTAGE_13: +- /* EN = 1, VSPEN = 1, VTOP = 0, VBOT = 0 */ +- reg_4 |= (1 << 4); +- reg_4 &= ~0x3; +- reg_3 |= (1 << 3); +- break; +- +- case SEC_VOLTAGE_18: +- /* EN = 1, VSPEN = 1, VTOP = 1, VBOT = 0 */ +- reg_4 |= (1 << 4); +- reg_4 |= 0x2; +- reg_4 &= ~0x1; +- reg_3 |= (1 << 3); +- break; +- +- default: +- break; +- } +- err = isl6423_write(isl6423, reg_3); +- if (err < 0) +- goto exit; +- +- err = isl6423_write(isl6423, reg_4); +- if (err < 0) +- goto exit; +- +- isl6423->reg_3 = reg_3; +- isl6423->reg_4 = reg_4; +- +- return 0; +-exit: +- dprintk(FE_ERROR, 1, "I/O error <%d>", err); +- return err; +-} +- +-static int isl6423_set_current(struct dvb_frontend *fe) +-{ +- struct isl6423_dev *isl6423 = (struct isl6423_dev *) fe->sec_priv; +- u8 reg_3 = isl6423->reg_3; +- const struct isl6423_config *config = isl6423->config; +- int err = 0; +- +- switch (config->current_max) { +- case SEC_CURRENT_275m: +- /* 275mA */ +- /* ISELH = 0, ISELL = 0 */ +- reg_3 &= ~0x3; +- break; +- +- case SEC_CURRENT_515m: +- /* 515mA */ +- /* ISELH = 0, ISELL = 1 */ +- reg_3 &= ~0x2; +- reg_3 |= 0x1; +- break; +- +- case SEC_CURRENT_635m: +- /* 635mA */ +- /* ISELH = 1, ISELL = 0 */ +- reg_3 &= ~0x1; +- reg_3 |= 0x2; +- break; +- +- case SEC_CURRENT_800m: +- /* 800mA */ +- /* ISELH = 1, ISELL = 1 */ +- reg_3 |= 0x3; +- break; +- } +- +- err = isl6423_write(isl6423, reg_3); +- if (err < 0) +- goto exit; +- +- switch (config->curlim) { +- case SEC_CURRENT_LIM_ON: +- /* DCL = 0 */ +- reg_3 &= ~0x10; +- break; +- +- case SEC_CURRENT_LIM_OFF: +- /* DCL = 1 */ +- reg_3 |= 0x10; +- break; +- } +- +- err = isl6423_write(isl6423, reg_3); +- if (err < 0) +- goto exit; +- +- isl6423->reg_3 = reg_3; +- +- return 0; +-exit: +- dprintk(FE_ERROR, 1, "I/O error <%d>", err); +- return err; +-} +- +-static void isl6423_release(struct dvb_frontend *fe) +-{ +- isl6423_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct isl6423_config *config) +-{ +- struct isl6423_dev *isl6423; +- +- isl6423 = kzalloc(sizeof(struct isl6423_dev), GFP_KERNEL); +- if (!isl6423) +- return NULL; +- +- isl6423->config = config; +- isl6423->i2c = i2c; +- fe->sec_priv = isl6423; +- +- /* SR3H = 0, SR3M = 1, SR3L = 0 */ +- isl6423->reg_3 = 0x02 << 5; +- /* SR4H = 0, SR4M = 1, SR4L = 1 */ +- isl6423->reg_4 = 0x03 << 5; +- +- if (isl6423_set_current(fe)) +- goto exit; +- +- if (isl6423_set_modulation(fe)) +- goto exit; +- +- fe->ops.release_sec = isl6423_release; +- fe->ops.set_voltage = isl6423_set_voltage; +- fe->ops.enable_high_lnb_voltage = isl6423_voltage_boost; +- isl6423->verbose = verbose; +- +- return fe; +- +-exit: +- kfree(isl6423); +- fe->sec_priv = NULL; +- return NULL; +-} +-EXPORT_SYMBOL(isl6423_attach); +- +-MODULE_DESCRIPTION("ISL6423 SEC"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/isl6423.h b/drivers/media/dvb/frontends/isl6423.h +deleted file mode 100644 +index e1a37fb..0000000 +--- a/drivers/media/dvb/frontends/isl6423.h ++++ /dev/null +@@ -1,63 +0,0 @@ +-/* +- Intersil ISL6423 SEC and LNB Power supply controller +- +- Copyright (C) Manu Abraham +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __ISL_6423_H +-#define __ISL_6423_H +- +-#include +- +-enum isl6423_current { +- SEC_CURRENT_275m = 0, +- SEC_CURRENT_515m, +- SEC_CURRENT_635m, +- SEC_CURRENT_800m, +-}; +- +-enum isl6423_curlim { +- SEC_CURRENT_LIM_ON = 1, +- SEC_CURRENT_LIM_OFF +-}; +- +-struct isl6423_config { +- enum isl6423_current current_max; +- enum isl6423_curlim curlim; +- u8 addr; +- u8 mod_extern; +-}; +- +-#if defined(CONFIG_DVB_ISL6423) || (defined(CONFIG_DVB_ISL6423_MODULE) && defined(MODULE)) +- +- +-extern struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct isl6423_config *config); +- +-#else +-static inline struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, +- const struct isl6423_config *config) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif /* CONFIG_DVB_ISL6423 */ +- +-#endif /* __ISL_6423_H */ +diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h +deleted file mode 100644 +index 93b086e..0000000 +--- a/drivers/media/dvb/frontends/it913x-fe-priv.h ++++ /dev/null +@@ -1,1046 +0,0 @@ +- +-struct it913xset { u32 pro; +- u32 address; +- u8 reg[15]; +- u8 count; +-}; +- +-struct adctable { u32 adcFrequency; +- u32 bandwidth; +- u32 coeff_1_2048; +- u32 coeff_1_4096; +- u32 coeff_1_8191; +- u32 coeff_1_8192; +- u32 coeff_1_8193; +- u32 coeff_2_2k; +- u32 coeff_2_4k; +- u32 coeff_2_8k; +- u16 bfsfcw_fftinx_ratio; +- u16 fftinx_bfsfcw_ratio; +-}; +- +-/* clock and coeff tables only table 3 is used with IT9137*/ +-/* TODO other tables relate AF9035 may be removed */ +-static struct adctable tab1[] = { +- { 20156250, 6000000, +- 0x02b8ba6e, 0x015c5d37, 0x00ae340d, 0x00ae2e9b, 0x00ae292a, +- 0x015c5d37, 0x00ae2e9b, 0x0057174e, 0x02f1, 0x015c }, +- { 20156250, 7000000, +- 0x032cd980, 0x01966cc0, 0x00cb3cba, 0x00cb3660, 0x00cb3007, +- 0x01966cc0, 0x00cb3660, 0x00659b30, 0x0285, 0x0196 }, +- { 20156250, 8000000, +- 0x03a0f893, 0x01d07c49, 0x00e84567, 0x00e83e25, 0x00e836e3, +- 0x01d07c49, 0x00e83e25, 0x00741f12, 0x0234, 0x01d0 }, +- { 20156250, 5000000, +- 0x02449b5c, 0x01224dae, 0x00912b60, 0x009126d7, 0x0091224e, +- 0x01224dae, 0x009126d7, 0x0048936b, 0x0387, 0x0122 } +-}; +- +-static struct adctable tab2[] = { +- { 20187500, 6000000, +- 0x02b7a654, 0x015bd32a, 0x00adef04, 0x00ade995, 0x00ade426, +- 0x015bd32a, 0x00ade995, 0x0056f4ca, 0x02f2, 0x015c }, +- { 20187500, 7000000, +- 0x032b9761, 0x0195cbb1, 0x00caec30, 0x00cae5d8, 0x00cadf81, +- 0x0195cbb1, 0x00cae5d8, 0x006572ec, 0x0286, 0x0196 }, +- { 20187500, 8000000, +- 0x039f886f, 0x01cfc438, 0x00e7e95b, 0x00e7e21c, 0x00e7dadd, +- 0x01cfc438, 0x00e7e21c, 0x0073f10e, 0x0235, 0x01d0 }, +- { 20187500, 5000000, +- 0x0243b546, 0x0121daa3, 0x0090f1d9, 0x0090ed51, 0x0090e8ca, +- 0x0121daa3, 0x0090ed51, 0x004876a9, 0x0388, 0x0122 } +- +-}; +- +-static struct adctable tab3[] = { +- { 20250000, 6000000, +- 0x02b580ad, 0x015ac057, 0x00ad6597, 0x00ad602b, 0x00ad5ac1, +- 0x015ac057, 0x00ad602b, 0x0056b016, 0x02f4, 0x015b }, +- { 20250000, 7000000, +- 0x03291620, 0x01948b10, 0x00ca4bda, 0x00ca4588, 0x00ca3f36, +- 0x01948b10, 0x00ca4588, 0x006522c4, 0x0288, 0x0195 }, +- { 20250000, 8000000, +- 0x039cab92, 0x01ce55c9, 0x00e7321e, 0x00e72ae4, 0x00e723ab, +- 0x01ce55c9, 0x00e72ae4, 0x00739572, 0x0237, 0x01ce }, +- { 20250000, 5000000, +- 0x0241eb3b, 0x0120f59e, 0x00907f53, 0x00907acf, 0x0090764b, +- 0x0120f59e, 0x00907acf, 0x00483d67, 0x038b, 0x0121 } +- +-}; +- +-static struct adctable tab4[] = { +- { 20583333, 6000000, +- 0x02aa4598, 0x015522cc, 0x00aa96bb, 0x00aa9166, 0x00aa8c12, +- 0x015522cc, 0x00aa9166, 0x005548b3, 0x0300, 0x0155 }, +- { 20583333, 7000000, +- 0x031bfbdc, 0x018dfdee, 0x00c7052f, 0x00c6fef7, 0x00c6f8bf, +- 0x018dfdee, 0x00c6fef7, 0x00637f7b, 0x0293, 0x018e }, +- { 20583333, 8000000, +- 0x038db21f, 0x01c6d910, 0x00e373a3, 0x00e36c88, 0x00e3656d, +- 0x01c6d910, 0x00e36c88, 0x0071b644, 0x0240, 0x01c7 }, +- { 20583333, 5000000, +- 0x02388f54, 0x011c47aa, 0x008e2846, 0x008e23d5, 0x008e1f64, +- 0x011c47aa, 0x008e23d5, 0x004711ea, 0x039a, 0x011c } +- +-}; +- +-static struct adctable tab5[] = { +- { 20416667, 6000000, +- 0x02afd765, 0x0157ebb3, 0x00abfb39, 0x00abf5d9, 0x00abf07a, +- 0x0157ebb3, 0x00abf5d9, 0x0055faed, 0x02fa, 0x0158 }, +- { 20416667, 7000000, +- 0x03227b4b, 0x01913da6, 0x00c8a518, 0x00c89ed3, 0x00c8988e, +- 0x01913da6, 0x00c89ed3, 0x00644f69, 0x028d, 0x0191 }, +- { 20416667, 8000000, +- 0x03951f32, 0x01ca8f99, 0x00e54ef7, 0x00e547cc, 0x00e540a2, +- 0x01ca8f99, 0x00e547cc, 0x0072a3e6, 0x023c, 0x01cb }, +- { 20416667, 5000000, +- 0x023d337f, 0x011e99c0, 0x008f515a, 0x008f4ce0, 0x008f4865, +- 0x011e99c0, 0x008f4ce0, 0x0047a670, 0x0393, 0x011f } +- +-}; +- +-static struct adctable tab6[] = { +- { 20480000, 6000000, +- 0x02adb6db, 0x0156db6e, 0x00ab7312, 0x00ab6db7, 0x00ab685c, +- 0x0156db6e, 0x00ab6db7, 0x0055b6db, 0x02fd, 0x0157 }, +- { 20480000, 7000000, +- 0x03200000, 0x01900000, 0x00c80640, 0x00c80000, 0x00c7f9c0, +- 0x01900000, 0x00c80000, 0x00640000, 0x028f, 0x0190 }, +- { 20480000, 8000000, +- 0x03924925, 0x01c92492, 0x00e4996e, 0x00e49249, 0x00e48b25, +- 0x01c92492, 0x00e49249, 0x00724925, 0x023d, 0x01c9 }, +- { 20480000, 5000000, +- 0x023b6db7, 0x011db6db, 0x008edfe5, 0x008edb6e, 0x008ed6f7, +- 0x011db6db, 0x008edb6e, 0x00476db7, 0x0396, 0x011e } +-}; +- +-static struct adctable tab7[] = { +- { 20500000, 6000000, +- 0x02ad0b99, 0x015685cc, 0x00ab4840, 0x00ab42e6, 0x00ab3d8c, +- 0x015685cc, 0x00ab42e6, 0x0055a173, 0x02fd, 0x0157 }, +- { 20500000, 7000000, +- 0x031f3832, 0x018f9c19, 0x00c7d44b, 0x00c7ce0c, 0x00c7c7ce, +- 0x018f9c19, 0x00c7ce0c, 0x0063e706, 0x0290, 0x0190 }, +- { 20500000, 8000000, +- 0x039164cb, 0x01c8b266, 0x00e46056, 0x00e45933, 0x00e45210, +- 0x01c8b266, 0x00e45933, 0x00722c99, 0x023e, 0x01c9 }, +- { 20500000, 5000000, +- 0x023adeff, 0x011d6f80, 0x008ebc36, 0x008eb7c0, 0x008eb34a, +- 0x011d6f80, 0x008eb7c0, 0x00475be0, 0x0396, 0x011d } +- +-}; +- +-static struct adctable tab8[] = { +- { 20625000, 6000000, +- 0x02a8e4bd, 0x0154725e, 0x00aa3e81, 0x00aa392f, 0x00aa33de, +- 0x0154725e, 0x00aa392f, 0x00551c98, 0x0302, 0x0154 }, +- { 20625000, 7000000, +- 0x031a6032, 0x018d3019, 0x00c69e41, 0x00c6980c, 0x00c691d8, +- 0x018d3019, 0x00c6980c, 0x00634c06, 0x0294, 0x018d }, +- { 20625000, 8000000, +- 0x038bdba6, 0x01c5edd3, 0x00e2fe02, 0x00e2f6ea, 0x00e2efd2, +- 0x01c5edd3, 0x00e2f6ea, 0x00717b75, 0x0242, 0x01c6 }, +- { 20625000, 5000000, +- 0x02376948, 0x011bb4a4, 0x008ddec1, 0x008dda52, 0x008dd5e3, +- 0x011bb4a4, 0x008dda52, 0x0046ed29, 0x039c, 0x011c } +- +-}; +- +-struct table { +- u32 xtal; +- struct adctable *table; +-}; +- +-static struct table fe_clockTable[] = { +- {12000000, tab3}, /* 12.00MHz */ +- {20480000, tab6}, /* 20.48MHz */ +- {36000000, tab3}, /* 36.00MHz */ +- {30000000, tab1}, /* 30.00MHz */ +- {26000000, tab4}, /* 26.00MHz */ +- {28000000, tab5}, /* 28.00MHz */ +- {32000000, tab7}, /* 32.00MHz */ +- {34000000, tab2}, /* 34.00MHz */ +- {24000000, tab1}, /* 24.00MHz */ +- {22000000, tab8}, /* 22.00MHz */ +-}; +- +-/* fe get */ +-fe_code_rate_t fe_code[] = { +- FEC_1_2, +- FEC_2_3, +- FEC_3_4, +- FEC_5_6, +- FEC_7_8, +- FEC_NONE, +-}; +- +-fe_guard_interval_t fe_gi[] = { +- GUARD_INTERVAL_1_32, +- GUARD_INTERVAL_1_16, +- GUARD_INTERVAL_1_8, +- GUARD_INTERVAL_1_4, +-}; +- +-fe_hierarchy_t fe_hi[] = { +- HIERARCHY_NONE, +- HIERARCHY_1, +- HIERARCHY_2, +- HIERARCHY_4, +-}; +- +-fe_transmit_mode_t fe_mode[] = { +- TRANSMISSION_MODE_2K, +- TRANSMISSION_MODE_8K, +- TRANSMISSION_MODE_4K, +-}; +- +-fe_modulation_t fe_con[] = { +- QPSK, +- QAM_16, +- QAM_64, +-}; +- +-/* Standard demodulator functions */ +-static struct it913xset set_solo_fe[] = { +- {PRO_LINK, GPIOH5_EN, {0x01}, 0x01}, +- {PRO_LINK, GPIOH5_ON, {0x01}, 0x01}, +- {PRO_LINK, GPIOH5_O, {0x00}, 0x01}, +- {PRO_LINK, GPIOH5_O, {0x01}, 0x01}, +- {PRO_LINK, DVBT_INTEN, {0x04}, 0x01}, +- {PRO_LINK, DVBT_ENABLE, {0x05}, 0x01}, +- {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, +- {PRO_LINK, HOSTB_MPEG_SER_MODE, {0x00}, 0x01}, +- {PRO_LINK, HOSTB_MPEG_PAR_MODE, {0x00}, 0x01}, +- {PRO_DMOD, DCA_UPPER_CHIP, {0x00}, 0x01}, +- {PRO_LINK, HOSTB_DCA_UPPER, {0x00}, 0x01}, +- {PRO_DMOD, DCA_LOWER_CHIP, {0x00}, 0x01}, +- {PRO_LINK, HOSTB_DCA_LOWER, {0x00}, 0x01}, +- {PRO_DMOD, DCA_PLATCH, {0x00}, 0x01}, +- {PRO_DMOD, DCA_FPGA_LATCH, {0x00}, 0x01}, +- {PRO_DMOD, DCA_STAND_ALONE, {0x01}, 0x01}, +- {PRO_DMOD, DCA_ENABLE, {0x00}, 0x01}, +- {PRO_DMOD, MP2IF_MPEG_PAR_MODE, {0x00}, 0x01}, +- {PRO_DMOD, BFS_FCW, {0x00, 0x00, 0x00}, 0x03}, +- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +-}; +- +- +-static struct it913xset init_1[] = { +- {PRO_LINK, LOCK3_OUT, {0x01}, 0x01}, +- {PRO_LINK, PADMISCDRSR, {0x01}, 0x01}, +- {PRO_LINK, PADMISCDR2, {0x00}, 0x01}, +- {PRO_DMOD, 0xec57, {0x00, 0x00}, 0x02}, +- {PRO_LINK, PADMISCDR4, {0x00}, 0x01}, /* Power up */ +- {PRO_LINK, PADMISCDR8, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +- +-/* Version 1 types */ +-static struct it913xset it9135_v1[] = { +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a}, 0x01}, +- {PRO_DMOD, 0x007e, {0x04}, 0x01}, +- {PRO_DMOD, 0x0081, {0x0a}, 0x01}, +- {PRO_DMOD, 0x008a, {0x01}, 0x01}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06}, 0x01}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009f, {0xe1}, 0x01}, +- {PRO_DMOD, 0x00a0, {0xcf}, 0x01}, +- {PRO_DMOD, 0x00a3, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a5, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a6, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a9, {0x00}, 0x01}, +- {PRO_DMOD, 0x00aa, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00c2, {0x05}, 0x01}, +- {PRO_DMOD, 0x00c6, {0x19}, 0x01}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf016, {0x10}, 0x01}, +- {PRO_DMOD, 0xf017, {0x04}, 0x01}, +- {PRO_DMOD, 0xf018, {0x05}, 0x01}, +- {PRO_DMOD, 0xf019, {0x04}, 0x01}, +- {PRO_DMOD, 0xf01a, {0x05}, 0x01}, +- {PRO_DMOD, 0xf021, {0x03}, 0x01}, +- {PRO_DMOD, 0xf022, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf023, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf02b, {0x00}, 0x01}, +- {PRO_DMOD, 0xf02c, {0x01}, 0x01}, +- {PRO_DMOD, 0xf064, {0x03}, 0x01}, +- {PRO_DMOD, 0xf065, {0xf9}, 0x01}, +- {PRO_DMOD, 0xf066, {0x03}, 0x01}, +- {PRO_DMOD, 0xf067, {0x01}, 0x01}, +- {PRO_DMOD, 0xf06f, {0xe0}, 0x01}, +- {PRO_DMOD, 0xf070, {0x03}, 0x01}, +- {PRO_DMOD, 0xf072, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf073, {0x03}, 0x01}, +- {PRO_DMOD, 0xf078, {0x00}, 0x01}, +- {PRO_DMOD, 0xf087, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09b, {0x3f}, 0x01}, +- {PRO_DMOD, 0xf09c, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09d, {0x20}, 0x01}, +- {PRO_DMOD, 0xf09e, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09f, {0x0c}, 0x01}, +- {PRO_DMOD, 0xf0a0, {0x00}, 0x01}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14d, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf15b, {0x08}, 0x01}, +- {PRO_DMOD, 0xf15d, {0x03}, 0x01}, +- {PRO_DMOD, 0xf15e, {0x05}, 0x01}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01}, 0x01}, +- {PRO_DMOD, 0xf167, {0x40}, 0x01}, +- {PRO_DMOD, 0xf168, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf17a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf17b, {0x00}, 0x01}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36}, 0x01}, +- {PRO_DMOD, 0xf1bd, {0x00}, 0x01}, +- {PRO_DMOD, 0xf1cb, {0xa0}, 0x01}, +- {PRO_DMOD, 0xf1cc, {0x01}, 0x01}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf40e, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf40f, {0x40}, 0x01}, +- {PRO_DMOD, 0xf410, {0x08}, 0x01}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15}, 0x01}, +- {PRO_DMOD, 0xf562, {0x20}, 0x01}, +- {PRO_DMOD, 0xf5df, {0xfb}, 0x01}, +- {PRO_DMOD, 0xf5e0, {0x00}, 0x01}, +- {PRO_DMOD, 0xf5e3, {0x09}, 0x01}, +- {PRO_DMOD, 0xf5e4, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5e5, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, +- {PRO_DMOD, 0xf600, {0x05}, 0x01}, +- {PRO_DMOD, 0xf601, {0x08}, 0x01}, +- {PRO_DMOD, 0xf602, {0x0b}, 0x01}, +- {PRO_DMOD, 0xf603, {0x0e}, 0x01}, +- {PRO_DMOD, 0xf604, {0x11}, 0x01}, +- {PRO_DMOD, 0xf605, {0x14}, 0x01}, +- {PRO_DMOD, 0xf606, {0x17}, 0x01}, +- {PRO_DMOD, 0xf607, {0x1f}, 0x01}, +- {PRO_DMOD, 0xf60e, {0x00}, 0x01}, +- {PRO_DMOD, 0xf60f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf610, {0x32}, 0x01}, +- {PRO_DMOD, 0xf611, {0x10}, 0x01}, +- {PRO_DMOD, 0xf707, {0xfc}, 0x01}, +- {PRO_DMOD, 0xf708, {0x00}, 0x01}, +- {PRO_DMOD, 0xf709, {0x37}, 0x01}, +- {PRO_DMOD, 0xf70a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40}, 0x01}, +- {PRO_DMOD, 0xf810, {0x54}, 0x01}, +- {PRO_DMOD, 0xf811, {0x5a}, 0x01}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_38[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x38}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x0a}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, +- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc8, 0xb8, +- 0xd0, 0xc3, 0x01}, 0x0a}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x32}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, +- {PRO_DMOD, 0x00c4, {0x00}, 0x01}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, +- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x02, 0x02, 0x02, 0x09, 0x50, 0x7b, 0x77, +- 0x00, 0x02, 0xc8, 0x05, 0x7b}, 0x0c}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, +- {PRO_DMOD, 0x011a, {0xc8, 0x7b, 0x8a, 0xa0}, 0x04}, +- {PRO_DMOD, 0x0122, {0x02, 0x18, 0xc3}, 0x03}, +- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, +- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, +- {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05}, +- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc8, 0x59}, 0x05}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf085, {0x00, 0x02, 0x00}, 0x03}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, +- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, +- 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_51[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x51}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x0a}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x06, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xc8, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, +- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x02, 0x0a, 0x03, 0xc0, 0x96, +- 0xcf, 0xc3, 0x01}, 0x0a}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, +- {PRO_DMOD, 0x00c4, {0x00}, 0x01}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, +- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x7a, 0x77, +- 0x01, 0x02, 0xb0, 0x02, 0x7a}, 0x0c}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, +- {PRO_DMOD, 0x011a, {0xc0, 0x7a, 0xac, 0x8c}, 0x04}, +- {PRO_DMOD, 0x0122, {0x02, 0x70, 0xa4}, 0x03}, +- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, +- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, +- {PRO_DMOD, 0x0137, {0x01, 0x00, 0x07, 0x00, 0x06}, 0x05}, +- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xc0, 0x59}, 0x05}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, +- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, +- 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_52[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x52}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x10}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0xa0, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04, 0x00}, 0x02}, +- {PRO_DMOD, 0x0081, { 0x0a, 0x12, 0x03, 0x0a, 0x03, 0xb3, 0x97, +- 0xc0, 0x9e, 0x01}, 0x0a}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x3c}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05}, 0x03}, +- {PRO_DMOD, 0x00c4, {0x00}, 0x01}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cc, {0x2e, 0x51, 0x33}, 0x03}, +- {PRO_DMOD, 0x00f3, {0x05, 0x91, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x03, 0x02, 0x02, 0x09, 0x50, 0x74, 0x77, +- 0x02, 0x02, 0xae, 0x02, 0x6e}, 0x0c}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03, 0x02, 0x80}, 0x04}, +- {PRO_DMOD, 0x011a, {0xcd, 0x62, 0xa4, 0x8c}, 0x04}, +- {PRO_DMOD, 0x0122, {0x03, 0x18, 0x9e}, 0x03}, +- {PRO_DMOD, 0x0127, {0x00, 0x07}, 0x02}, +- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, +- {PRO_DMOD, 0x0137, {0x00, 0x00, 0x07, 0x00, 0x06}, 0x05}, +- {PRO_DMOD, 0x013d, {0x00, 0x01, 0x5b, 0xb6, 0x59}, 0x05}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf016, {0x10, 0x04, 0x05, 0x04, 0x05}, 0x05}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00, 0x03, 0x0a, 0x0a}, 0x05}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00, 0x01}, 0x04}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf085, {0xc0, 0x01, 0x00}, 0x03}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5df, {0xfb, 0x00}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf5f8, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5fd, {0x01}, 0x01}, +- {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, +- 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-/* Version 2 types */ +-static struct it913xset it9135_v2[] = { +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a}, 0x01}, +- {PRO_DMOD, 0x007e, {0x04}, 0x01}, +- {PRO_DMOD, 0x0081, {0x0a}, 0x01}, +- {PRO_DMOD, 0x008a, {0x01}, 0x01}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06}, 0x01}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009f, {0xe1}, 0x01}, +- {PRO_DMOD, 0x00a0, {0xcf}, 0x01}, +- {PRO_DMOD, 0x00a3, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a5, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a6, {0x01}, 0x01}, +- {PRO_DMOD, 0x00a9, {0x00}, 0x01}, +- {PRO_DMOD, 0x00aa, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00c2, {0x05}, 0x01}, +- {PRO_DMOD, 0x00c6, {0x19}, 0x01}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf02b, {0x00}, 0x01}, +- {PRO_DMOD, 0xf064, {0x03}, 0x01}, +- {PRO_DMOD, 0xf065, {0xf9}, 0x01}, +- {PRO_DMOD, 0xf066, {0x03}, 0x01}, +- {PRO_DMOD, 0xf067, {0x01}, 0x01}, +- {PRO_DMOD, 0xf06f, {0xe0}, 0x01}, +- {PRO_DMOD, 0xf070, {0x03}, 0x01}, +- {PRO_DMOD, 0xf072, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf073, {0x03}, 0x01}, +- {PRO_DMOD, 0xf078, {0x00}, 0x01}, +- {PRO_DMOD, 0xf087, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09b, {0x3f}, 0x01}, +- {PRO_DMOD, 0xf09c, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09d, {0x20}, 0x01}, +- {PRO_DMOD, 0xf09e, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09f, {0x0c}, 0x01}, +- {PRO_DMOD, 0xf0a0, {0x00}, 0x01}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14d, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf15b, {0x08}, 0x01}, +- {PRO_DMOD, 0xf15d, {0x03}, 0x01}, +- {PRO_DMOD, 0xf15e, {0x05}, 0x01}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01}, 0x01}, +- {PRO_DMOD, 0xf167, {0x40}, 0x01}, +- {PRO_DMOD, 0xf168, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf17a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf17b, {0x00}, 0x01}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36}, 0x01}, +- {PRO_DMOD, 0xf1bd, {0x00}, 0x01}, +- {PRO_DMOD, 0xf1cb, {0xa0}, 0x01}, +- {PRO_DMOD, 0xf1cc, {0x01}, 0x01}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf40e, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf40f, {0x40}, 0x01}, +- {PRO_DMOD, 0xf410, {0x08}, 0x01}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15}, 0x01}, +- {PRO_DMOD, 0xf562, {0x20}, 0x01}, +- {PRO_DMOD, 0xf5e3, {0x09}, 0x01}, +- {PRO_DMOD, 0xf5e4, {0x01}, 0x01}, +- {PRO_DMOD, 0xf5e5, {0x01}, 0x01}, +- {PRO_DMOD, 0xf600, {0x05}, 0x01}, +- {PRO_DMOD, 0xf601, {0x08}, 0x01}, +- {PRO_DMOD, 0xf602, {0x0b}, 0x01}, +- {PRO_DMOD, 0xf603, {0x0e}, 0x01}, +- {PRO_DMOD, 0xf604, {0x11}, 0x01}, +- {PRO_DMOD, 0xf605, {0x14}, 0x01}, +- {PRO_DMOD, 0xf606, {0x17}, 0x01}, +- {PRO_DMOD, 0xf607, {0x1f}, 0x01}, +- {PRO_DMOD, 0xf60e, {0x00}, 0x01}, +- {PRO_DMOD, 0xf60f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf610, {0x32}, 0x01}, +- {PRO_DMOD, 0xf611, {0x10}, 0x01}, +- {PRO_DMOD, 0xf707, {0xfc}, 0x01}, +- {PRO_DMOD, 0xf708, {0x00}, 0x01}, +- {PRO_DMOD, 0xf709, {0x37}, 0x01}, +- {PRO_DMOD, 0xf70a, {0x00}, 0x01}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40}, 0x01}, +- {PRO_DMOD, 0xf810, {0x54}, 0x01}, +- {PRO_DMOD, 0xf811, {0x5a}, 0x01}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_60[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x60}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x0a}, 0x01}, +- {PRO_DMOD, 0x006a, {0x03}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04}, 0x01}, +- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, +- {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbe, 0xa0, 0xc6, 0xb6, 0x01}, 0x07}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, +- {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x0a, 0x50, 0x7b, 0x8c, +- 0x00, 0x02, 0xbe, 0x00}, 0x0b}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, +- {PRO_DMOD, 0x011a, {0xbe}, 0x01}, +- {PRO_DMOD, 0x0124, {0xae}, 0x01}, +- {PRO_DMOD, 0x0127, {0x00}, 0x01}, +- {PRO_DMOD, 0x012a, {0x56, 0x50, 0x47, 0x42}, 0x04}, +- {PRO_DMOD, 0x0137, {0x00}, 0x01}, +- {PRO_DMOD, 0x013b, {0x08}, 0x01}, +- {PRO_DMOD, 0x013f, {0x5b}, 0x01}, +- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x19, 0x19, 0x8c, 0x8c, 0x8c, +- 0x6e, 0x8c, 0x50, 0x8c, 0x8c, 0xac, 0xc6, +- 0x33}, 0x0f}, +- {PRO_DMOD, 0x0151, {0x28}, 0x01}, +- {PRO_DMOD, 0x0153, {0xbc}, 0x01}, +- {PRO_DMOD, 0x0178, {0x09}, 0x01}, +- {PRO_DMOD, 0x0181, {0x94, 0x6e}, 0x02}, +- {PRO_DMOD, 0x0185, {0x24}, 0x01}, +- {PRO_DMOD, 0x0187, {0x00, 0x00, 0xbe, 0x02, 0x80}, 0x05}, +- {PRO_DMOD, 0xed02, {0xff}, 0x01}, +- {PRO_DMOD, 0xee42, {0xff}, 0x01}, +- {PRO_DMOD, 0xee82, {0xff}, 0x01}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf087, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf600, {0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17 +- , 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_61[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x61}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x06}, 0x01}, +- {PRO_DMOD, 0x006a, {0x03}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x90, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04}, 0x01}, +- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, +- {PRO_DMOD, 0x0084, {0x0a, 0x33, 0xbc, 0x9c, 0xcc, 0xa8, 0x01}, 0x07}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5c, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, +- {PRO_DMOD, 0x00f3, {0x05, 0xa0, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x03, 0x03, 0x02, 0x08, 0x50, 0x7b, 0x8c, +- 0x01, 0x02, 0xc8, 0x00}, 0x0b}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, +- {PRO_DMOD, 0x011a, {0xc6}, 0x01}, +- {PRO_DMOD, 0x0124, {0xa8}, 0x01}, +- {PRO_DMOD, 0x0127, {0x00}, 0x01}, +- {PRO_DMOD, 0x012a, {0x59, 0x50, 0x47, 0x42}, 0x04}, +- {PRO_DMOD, 0x0137, {0x00}, 0x01}, +- {PRO_DMOD, 0x013b, {0x05}, 0x01}, +- {PRO_DMOD, 0x013f, {0x5b}, 0x01}, +- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x59, 0x8c, 0x8c, 0x8c, +- 0x7b, 0x8c, 0x50, 0x8c, 0x8c, 0xa8, 0xc6, +- 0x33}, 0x0f}, +- {PRO_DMOD, 0x0151, {0x28}, 0x01}, +- {PRO_DMOD, 0x0153, {0xcc}, 0x01}, +- {PRO_DMOD, 0x0178, {0x09}, 0x01}, +- {PRO_DMOD, 0x0181, {0x9c, 0x76}, 0x02}, +- {PRO_DMOD, 0x0185, {0x28}, 0x01}, +- {PRO_DMOD, 0x0187, {0x01, 0x00, 0xaa, 0x02, 0x80}, 0x05}, +- {PRO_DMOD, 0xed02, {0xff}, 0x01}, +- {PRO_DMOD, 0xee42, {0xff}, 0x01}, +- {PRO_DMOD, 0xee82, {0xff}, 0x01}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf087, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, +- 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-static struct it913xset it9135_62[] = { +- {PRO_DMOD, 0x0043, {0x00}, 0x01}, +- {PRO_DMOD, 0x0046, {0x62}, 0x01}, +- {PRO_DMOD, 0x0051, {0x01}, 0x01}, +- {PRO_DMOD, 0x005f, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0x0068, {0x0a}, 0x01}, +- {PRO_DMOD, 0x006a, {0x03}, 0x01}, +- {PRO_DMOD, 0x0070, {0x0a, 0x05, 0x02}, 0x03}, +- {PRO_DMOD, 0x0075, {0x8c, 0x8c, 0x8c, 0x8c, 0x01}, 0x05}, +- {PRO_DMOD, 0x007e, {0x04}, 0x01}, +- {PRO_DMOD, 0x0081, {0x0a, 0x12}, 0x02}, +- {PRO_DMOD, 0x0084, { 0x0a, 0x33, 0xb8, 0x9c, 0xb2, 0xa6, 0x01}, +- 0x07}, +- {PRO_DMOD, 0x008e, {0x01}, 0x01}, +- {PRO_DMOD, 0x0092, {0x06, 0x00, 0x00, 0x00, 0x00}, 0x05}, +- {PRO_DMOD, 0x0099, {0x01}, 0x01}, +- {PRO_DMOD, 0x009b, {0x3c, 0x28}, 0x02}, +- {PRO_DMOD, 0x009f, {0xe1, 0xcf}, 0x02}, +- {PRO_DMOD, 0x00a3, {0x01, 0x5a, 0x01, 0x01}, 0x04}, +- {PRO_DMOD, 0x00a9, {0x00, 0x01}, 0x02}, +- {PRO_DMOD, 0x00b0, {0x01}, 0x01}, +- {PRO_DMOD, 0x00b3, {0x02, 0x3a}, 0x02}, +- {PRO_DMOD, 0x00b6, {0x14}, 0x01}, +- {PRO_DMOD, 0x00c0, {0x11, 0x00, 0x05, 0x01, 0x00}, 0x05}, +- {PRO_DMOD, 0x00c6, {0x19, 0x00}, 0x02}, +- {PRO_DMOD, 0x00cb, {0x32, 0x2c, 0x4f, 0x30}, 0x04}, +- {PRO_DMOD, 0x00f3, {0x05, 0x8c, 0x8c}, 0x03}, +- {PRO_DMOD, 0x00f8, {0x03, 0x06, 0x06}, 0x03}, +- {PRO_DMOD, 0x00fc, { 0x02, 0x03, 0x02, 0x09, 0x50, 0x6e, 0x8c, +- 0x02, 0x02, 0xc2, 0x00}, 0x0b}, +- {PRO_DMOD, 0x0109, {0x02}, 0x01}, +- {PRO_DMOD, 0x0115, {0x0a, 0x03}, 0x02}, +- {PRO_DMOD, 0x011a, {0xb8}, 0x01}, +- {PRO_DMOD, 0x0124, {0xa8}, 0x01}, +- {PRO_DMOD, 0x0127, {0x00}, 0x01}, +- {PRO_DMOD, 0x012a, {0x53, 0x51, 0x4e, 0x43}, 0x04}, +- {PRO_DMOD, 0x0137, {0x00}, 0x01}, +- {PRO_DMOD, 0x013b, {0x05}, 0x01}, +- {PRO_DMOD, 0x013f, {0x5b}, 0x01}, +- {PRO_DMOD, 0x0141, { 0x59, 0xf9, 0x59, 0x19, 0x8c, 0x8c, 0x8c, +- 0x7b, 0x8c, 0x50, 0x70, 0x8c, 0x96, 0xd0, +- 0x33}, 0x0f}, +- {PRO_DMOD, 0x0151, {0x28}, 0x01}, +- {PRO_DMOD, 0x0153, {0xb2}, 0x01}, +- {PRO_DMOD, 0x0178, {0x09}, 0x01}, +- {PRO_DMOD, 0x0181, {0x9c, 0x6e}, 0x02}, +- {PRO_DMOD, 0x0185, {0x24}, 0x01}, +- {PRO_DMOD, 0x0187, {0x00, 0x00, 0xb8, 0x02, 0x80}, 0x05}, +- {PRO_DMOD, 0xed02, {0xff}, 0x01}, +- {PRO_DMOD, 0xee42, {0xff}, 0x01}, +- {PRO_DMOD, 0xee82, {0xff}, 0x01}, +- {PRO_DMOD, 0xf000, {0x0f}, 0x01}, +- {PRO_DMOD, 0xf01f, {0x8c, 0x00}, 0x02}, +- {PRO_DMOD, 0xf029, {0x8c, 0x00, 0x00}, 0x03}, +- {PRO_DMOD, 0xf064, {0x03, 0xf9, 0x03, 0x01}, 0x04}, +- {PRO_DMOD, 0xf06f, {0xe0, 0x03}, 0x02}, +- {PRO_DMOD, 0xf072, {0x0f, 0x03}, 0x02}, +- {PRO_DMOD, 0xf077, {0x01, 0x00}, 0x02}, +- {PRO_DMOD, 0xf087, {0x00}, 0x01}, +- {PRO_DMOD, 0xf09b, {0x3f, 0x00, 0x20, 0x00, 0x0c, 0x00}, 0x06}, +- {PRO_DMOD, 0xf130, {0x04}, 0x01}, +- {PRO_DMOD, 0xf132, {0x04}, 0x01}, +- {PRO_DMOD, 0xf144, {0x1a}, 0x01}, +- {PRO_DMOD, 0xf146, {0x00}, 0x01}, +- {PRO_DMOD, 0xf14a, {0x01}, 0x01}, +- {PRO_DMOD, 0xf14c, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf14f, {0x04}, 0x01}, +- {PRO_DMOD, 0xf158, {0x7f}, 0x01}, +- {PRO_DMOD, 0xf15a, {0x00, 0x08}, 0x02}, +- {PRO_DMOD, 0xf15d, {0x03, 0x05}, 0x02}, +- {PRO_DMOD, 0xf163, {0x05}, 0x01}, +- {PRO_DMOD, 0xf166, {0x01, 0x40, 0x0f}, 0x03}, +- {PRO_DMOD, 0xf17a, {0x00, 0x00}, 0x02}, +- {PRO_DMOD, 0xf183, {0x01}, 0x01}, +- {PRO_DMOD, 0xf19d, {0x40}, 0x01}, +- {PRO_DMOD, 0xf1bc, {0x36, 0x00}, 0x02}, +- {PRO_DMOD, 0xf1cb, {0xa0, 0x01}, 0x02}, +- {PRO_DMOD, 0xf204, {0x10}, 0x01}, +- {PRO_DMOD, 0xf214, {0x00}, 0x01}, +- {PRO_DMOD, 0xf24c, {0x88, 0x95, 0x9a, 0x90}, 0x04}, +- {PRO_DMOD, 0xf25a, {0x07, 0xe8, 0x03, 0xb0, 0x04}, 0x05}, +- {PRO_DMOD, 0xf270, {0x01, 0x02, 0x01, 0x02}, 0x04}, +- {PRO_DMOD, 0xf40e, {0x0a, 0x40, 0x08}, 0x03}, +- {PRO_DMOD, 0xf55f, {0x0a}, 0x01}, +- {PRO_DMOD, 0xf561, {0x15, 0x20}, 0x02}, +- {PRO_DMOD, 0xf5e3, {0x09, 0x01, 0x01}, 0x03}, +- {PRO_DMOD, 0xf600, { 0x05, 0x08, 0x0b, 0x0e, 0x11, 0x14, 0x17, +- 0x1f}, 0x08}, +- {PRO_DMOD, 0xf60e, {0x00, 0x04, 0x32, 0x10}, 0x04}, +- {PRO_DMOD, 0xf707, {0xfc, 0x00, 0x37, 0x00}, 0x04}, +- {PRO_DMOD, 0xf78b, {0x01}, 0x01}, +- {PRO_DMOD, 0xf80f, {0x40, 0x54, 0x5a}, 0x03}, +- {PRO_DMOD, 0xf905, {0x01}, 0x01}, +- {PRO_DMOD, 0xfb06, {0x03}, 0x01}, +- {PRO_DMOD, 0xfd8b, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00} /* Terminating Entry */ +-}; +- +-/* Tuner setting scripts (still keeping it9137) */ +-static struct it913xset it9137_tuner_off[] = { +- {PRO_DMOD, 0xfba8, {0x01}, 0x01}, /* Tuner Clock Off */ +- {PRO_DMOD, 0xec40, {0x00}, 0x01}, /* Power Down Tuner */ +- {PRO_DMOD, 0xec02, {0x3f, 0x1f, 0x3f, 0x3f}, 0x04}, +- {PRO_DMOD, 0xec06, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00}, 0x0c}, +- {PRO_DMOD, 0xec12, {0x00, 0x00, 0x00, 0x00}, 0x04}, +- {PRO_DMOD, 0xec17, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00}, 0x09}, +- {PRO_DMOD, 0xec22, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00}, 0x0a}, +- {PRO_DMOD, 0xec20, {0x00}, 0x01}, +- {PRO_DMOD, 0xec3f, {0x01}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +-}; +- +-static struct it913xset set_it9135_template[] = { +- {PRO_DMOD, 0xee06, {0x00}, 0x01}, +- {PRO_DMOD, 0xec56, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4c, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4d, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4e, {0x00}, 0x01}, +- {PRO_DMOD, 0x011e, {0x00}, 0x01}, /* Older Devices */ +- {PRO_DMOD, 0x011f, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +-}; +- +-static struct it913xset set_it9137_template[] = { +- {PRO_DMOD, 0xee06, {0x00}, 0x01}, +- {PRO_DMOD, 0xec56, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4c, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4d, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4e, {0x00}, 0x01}, +- {PRO_DMOD, 0xec4f, {0x00}, 0x01}, +- {PRO_DMOD, 0xec50, {0x00}, 0x01}, +- {0xff, 0x0000, {0x00}, 0x00}, /* Terminating Entry */ +-}; +diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c +deleted file mode 100644 +index ccc36bf..0000000 +--- a/drivers/media/dvb/frontends/it913x-fe.c ++++ /dev/null +@@ -1,976 +0,0 @@ +-/* +- * Driver for it913x-fe Frontend +- * +- * with support for on chip it9137 integral tuner +- * +- * Copyright (C) 2011 Malcolm Priestley (tvboxspy@gmail.com) +- * IT9137 Copyright (C) ITE Tech Inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "it913x-fe.h" +-#include "it913x-fe-priv.h" +- +-static int it913x_debug; +- +-module_param_named(debug, it913x_debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); +- +-#define dprintk(level, args...) do { \ +- if (level & it913x_debug) \ +- printk(KERN_DEBUG "it913x-fe: " args); \ +-} while (0) +- +-#define deb_info(args...) dprintk(0x01, args) +-#define debug_data_snipet(level, name, p) \ +- dprintk(level, name" (%02x%02x%02x%02x%02x%02x%02x%02x)", \ +- *p, *(p+1), *(p+2), *(p+3), *(p+4), \ +- *(p+5), *(p+6), *(p+7)); +-#define info(format, arg...) \ +- printk(KERN_INFO "it913x-fe: " format "\n" , ## arg) +- +-struct it913x_fe_state { +- struct dvb_frontend frontend; +- struct i2c_adapter *i2c_adap; +- struct ite_config *config; +- u8 i2c_addr; +- u32 frequency; +- fe_modulation_t constellation; +- fe_transmit_mode_t transmission_mode; +- u32 crystalFrequency; +- u32 adcFrequency; +- u8 tuner_type; +- struct adctable *table; +- fe_status_t it913x_status; +- u16 tun_xtal; +- u8 tun_fdiv; +- u8 tun_clk_mode; +- u32 tun_fn_min; +- u32 ucblocks; +-}; +- +-static int it913x_read_reg(struct it913x_fe_state *state, +- u32 reg, u8 *data, u8 count) +-{ +- int ret; +- u8 pro = PRO_DMOD; /* All reads from demodulator */ +- u8 b[4]; +- struct i2c_msg msg[2] = { +- { .addr = state->i2c_addr + (pro << 1), .flags = 0, +- .buf = b, .len = sizeof(b) }, +- { .addr = state->i2c_addr + (pro << 1), .flags = I2C_M_RD, +- .buf = data, .len = count } +- }; +- b[0] = (u8) reg >> 24; +- b[1] = (u8)(reg >> 16) & 0xff; +- b[2] = (u8)(reg >> 8) & 0xff; +- b[3] = (u8) reg & 0xff; +- +- ret = i2c_transfer(state->i2c_adap, msg, 2); +- +- return ret; +-} +- +-static int it913x_read_reg_u8(struct it913x_fe_state *state, u32 reg) +-{ +- int ret; +- u8 b[1]; +- ret = it913x_read_reg(state, reg, &b[0], sizeof(b)); +- return (ret < 0) ? -ENODEV : b[0]; +-} +- +-static int it913x_write(struct it913x_fe_state *state, +- u8 pro, u32 reg, u8 buf[], u8 count) +-{ +- u8 b[256]; +- struct i2c_msg msg[1] = { +- { .addr = state->i2c_addr + (pro << 1), .flags = 0, +- .buf = b, .len = count + 4 } +- }; +- int ret; +- +- b[0] = (u8) reg >> 24; +- b[1] = (u8)(reg >> 16) & 0xff; +- b[2] = (u8)(reg >> 8) & 0xff; +- b[3] = (u8) reg & 0xff; +- memcpy(&b[4], buf, count); +- +- ret = i2c_transfer(state->i2c_adap, msg, 1); +- +- if (ret < 0) +- return -EIO; +- +- return 0; +-} +- +-static int it913x_write_reg(struct it913x_fe_state *state, +- u8 pro, u32 reg, u32 data) +-{ +- int ret; +- u8 b[4]; +- u8 s; +- +- b[0] = data >> 24; +- b[1] = (data >> 16) & 0xff; +- b[2] = (data >> 8) & 0xff; +- b[3] = data & 0xff; +- /* expand write as needed */ +- if (data < 0x100) +- s = 3; +- else if (data < 0x1000) +- s = 2; +- else if (data < 0x100000) +- s = 1; +- else +- s = 0; +- +- ret = it913x_write(state, pro, reg, &b[s], sizeof(b) - s); +- +- return ret; +-} +- +-static int it913x_fe_script_loader(struct it913x_fe_state *state, +- struct it913xset *loadscript) +-{ +- int ret, i; +- if (loadscript == NULL) +- return -EINVAL; +- +- for (i = 0; i < 1000; ++i) { +- if (loadscript[i].pro == 0xff) +- break; +- ret = it913x_write(state, loadscript[i].pro, +- loadscript[i].address, +- loadscript[i].reg, loadscript[i].count); +- if (ret < 0) +- return -ENODEV; +- } +- return 0; +-} +- +-static int it913x_init_tuner(struct it913x_fe_state *state) +-{ +- int ret, i, reg; +- u8 val, nv_val; +- u8 nv[] = {48, 32, 24, 16, 12, 8, 6, 4, 2}; +- u8 b[2]; +- +- reg = it913x_read_reg_u8(state, 0xec86); +- switch (reg) { +- case 0: +- state->tun_clk_mode = reg; +- state->tun_xtal = 2000; +- state->tun_fdiv = 3; +- val = 16; +- break; +- case -ENODEV: +- return -ENODEV; +- case 1: +- default: +- state->tun_clk_mode = reg; +- state->tun_xtal = 640; +- state->tun_fdiv = 1; +- val = 6; +- break; +- } +- +- reg = it913x_read_reg_u8(state, 0xed03); +- +- if (reg < 0) +- return -ENODEV; +- else if (reg < sizeof(nv)) +- nv_val = nv[reg]; +- else +- nv_val = 2; +- +- for (i = 0; i < 50; i++) { +- ret = it913x_read_reg(state, 0xed23, &b[0], sizeof(b)); +- reg = (b[1] << 8) + b[0]; +- if (reg > 0) +- break; +- if (ret < 0) +- return -ENODEV; +- udelay(2000); +- } +- state->tun_fn_min = state->tun_xtal * reg; +- state->tun_fn_min /= (state->tun_fdiv * nv_val); +- deb_info("Tuner fn_min %d", state->tun_fn_min); +- +- if (state->config->chip_ver > 1) +- msleep(50); +- else { +- for (i = 0; i < 50; i++) { +- reg = it913x_read_reg_u8(state, 0xec82); +- if (reg > 0) +- break; +- if (reg < 0) +- return -ENODEV; +- udelay(2000); +- } +- } +- +- return it913x_write_reg(state, PRO_DMOD, 0xed81, val); +-} +- +-static int it9137_set_tuner(struct it913x_fe_state *state, +- u32 bandwidth, u32 frequency_m) +-{ +- struct it913xset *set_tuner = set_it9137_template; +- int ret, reg; +- u32 frequency = frequency_m / 1000; +- u32 freq, temp_f, tmp; +- u16 iqik_m_cal; +- u16 n_div; +- u8 n; +- u8 l_band; +- u8 lna_band; +- u8 bw; +- +- if (state->config->firmware_ver == 1) +- set_tuner = set_it9135_template; +- else +- set_tuner = set_it9137_template; +- +- deb_info("Tuner Frequency %d Bandwidth %d", frequency, bandwidth); +- +- if (frequency >= 51000 && frequency <= 440000) { +- l_band = 0; +- lna_band = 0; +- } else if (frequency > 440000 && frequency <= 484000) { +- l_band = 1; +- lna_band = 1; +- } else if (frequency > 484000 && frequency <= 533000) { +- l_band = 1; +- lna_band = 2; +- } else if (frequency > 533000 && frequency <= 587000) { +- l_band = 1; +- lna_band = 3; +- } else if (frequency > 587000 && frequency <= 645000) { +- l_band = 1; +- lna_band = 4; +- } else if (frequency > 645000 && frequency <= 710000) { +- l_band = 1; +- lna_band = 5; +- } else if (frequency > 710000 && frequency <= 782000) { +- l_band = 1; +- lna_band = 6; +- } else if (frequency > 782000 && frequency <= 860000) { +- l_band = 1; +- lna_band = 7; +- } else if (frequency > 1450000 && frequency <= 1492000) { +- l_band = 1; +- lna_band = 0; +- } else if (frequency > 1660000 && frequency <= 1685000) { +- l_band = 1; +- lna_band = 1; +- } else +- return -EINVAL; +- set_tuner[0].reg[0] = lna_band; +- +- switch (bandwidth) { +- case 5000000: +- bw = 0; +- break; +- case 6000000: +- bw = 2; +- break; +- case 7000000: +- bw = 4; +- break; +- default: +- case 8000000: +- bw = 6; +- break; +- } +- +- set_tuner[1].reg[0] = bw; +- set_tuner[2].reg[0] = 0xa0 | (l_band << 3); +- +- if (frequency > 53000 && frequency <= 74000) { +- n_div = 48; +- n = 0; +- } else if (frequency > 74000 && frequency <= 111000) { +- n_div = 32; +- n = 1; +- } else if (frequency > 111000 && frequency <= 148000) { +- n_div = 24; +- n = 2; +- } else if (frequency > 148000 && frequency <= 222000) { +- n_div = 16; +- n = 3; +- } else if (frequency > 222000 && frequency <= 296000) { +- n_div = 12; +- n = 4; +- } else if (frequency > 296000 && frequency <= 445000) { +- n_div = 8; +- n = 5; +- } else if (frequency > 445000 && frequency <= state->tun_fn_min) { +- n_div = 6; +- n = 6; +- } else if (frequency > state->tun_fn_min && frequency <= 950000) { +- n_div = 4; +- n = 7; +- } else if (frequency > 1450000 && frequency <= 1680000) { +- n_div = 2; +- n = 0; +- } else +- return -EINVAL; +- +- reg = it913x_read_reg_u8(state, 0xed81); +- iqik_m_cal = (u16)reg * n_div; +- +- if (reg < 0x20) { +- if (state->tun_clk_mode == 0) +- iqik_m_cal = (iqik_m_cal * 9) >> 5; +- else +- iqik_m_cal >>= 1; +- } else { +- iqik_m_cal = 0x40 - iqik_m_cal; +- if (state->tun_clk_mode == 0) +- iqik_m_cal = ~((iqik_m_cal * 9) >> 5); +- else +- iqik_m_cal = ~(iqik_m_cal >> 1); +- } +- +- temp_f = frequency * (u32)n_div * (u32)state->tun_fdiv; +- freq = temp_f / state->tun_xtal; +- tmp = freq * state->tun_xtal; +- +- if ((temp_f - tmp) >= (state->tun_xtal >> 1)) +- freq++; +- +- freq += (u32) n << 13; +- /* Frequency OMEGA_IQIK_M_CAL_MID*/ +- temp_f = freq + (u32)iqik_m_cal; +- +- set_tuner[3].reg[0] = temp_f & 0xff; +- set_tuner[4].reg[0] = (temp_f >> 8) & 0xff; +- +- deb_info("High Frequency = %04x", temp_f); +- +- /* Lower frequency */ +- set_tuner[5].reg[0] = freq & 0xff; +- set_tuner[6].reg[0] = (freq >> 8) & 0xff; +- +- deb_info("low Frequency = %04x", freq); +- +- ret = it913x_fe_script_loader(state, set_tuner); +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int it913x_fe_select_bw(struct it913x_fe_state *state, +- u32 bandwidth, u32 adcFrequency) +-{ +- int ret, i; +- u8 buffer[256]; +- u32 coeff[8]; +- u16 bfsfcw_fftinx_ratio; +- u16 fftinx_bfsfcw_ratio; +- u8 count; +- u8 bw; +- u8 adcmultiplier; +- +- deb_info("Bandwidth %d Adc %d", bandwidth, adcFrequency); +- +- switch (bandwidth) { +- case 5000000: +- bw = 3; +- break; +- case 6000000: +- bw = 0; +- break; +- case 7000000: +- bw = 1; +- break; +- default: +- case 8000000: +- bw = 2; +- break; +- } +- ret = it913x_write_reg(state, PRO_DMOD, REG_BW, bw); +- +- if (state->table == NULL) +- return -EINVAL; +- +- /* In write order */ +- coeff[0] = state->table[bw].coeff_1_2048; +- coeff[1] = state->table[bw].coeff_2_2k; +- coeff[2] = state->table[bw].coeff_1_8191; +- coeff[3] = state->table[bw].coeff_1_8192; +- coeff[4] = state->table[bw].coeff_1_8193; +- coeff[5] = state->table[bw].coeff_2_8k; +- coeff[6] = state->table[bw].coeff_1_4096; +- coeff[7] = state->table[bw].coeff_2_4k; +- bfsfcw_fftinx_ratio = state->table[bw].bfsfcw_fftinx_ratio; +- fftinx_bfsfcw_ratio = state->table[bw].fftinx_bfsfcw_ratio; +- +- /* ADC multiplier */ +- ret = it913x_read_reg_u8(state, ADC_X_2); +- if (ret < 0) +- return -EINVAL; +- +- adcmultiplier = ret; +- +- count = 0; +- +- /* Build Buffer for COEFF Registers */ +- for (i = 0; i < 8; i++) { +- if (adcmultiplier == 1) +- coeff[i] /= 2; +- buffer[count++] = (coeff[i] >> 24) & 0x3; +- buffer[count++] = (coeff[i] >> 16) & 0xff; +- buffer[count++] = (coeff[i] >> 8) & 0xff; +- buffer[count++] = coeff[i] & 0xff; +- } +- +- /* bfsfcw_fftinx_ratio register 0x21-0x22 */ +- buffer[count++] = bfsfcw_fftinx_ratio & 0xff; +- buffer[count++] = (bfsfcw_fftinx_ratio >> 8) & 0xff; +- /* fftinx_bfsfcw_ratio register 0x23-0x24 */ +- buffer[count++] = fftinx_bfsfcw_ratio & 0xff; +- buffer[count++] = (fftinx_bfsfcw_ratio >> 8) & 0xff; +- /* start at COEFF_1_2048 and write through to fftinx_bfsfcw_ratio*/ +- ret = it913x_write(state, PRO_DMOD, COEFF_1_2048, buffer, count); +- +- for (i = 0; i < 42; i += 8) +- debug_data_snipet(0x1, "Buffer", &buffer[i]); +- +- return ret; +-} +- +- +- +-static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret, i; +- fe_status_t old_status = state->it913x_status; +- *status = 0; +- +- if (state->it913x_status == 0) { +- ret = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); +- if (ret == 0x1) { +- *status |= FE_HAS_SIGNAL; +- for (i = 0; i < 40; i++) { +- ret = it913x_read_reg_u8(state, MP2IF_SYNC_LK); +- if (ret == 0x1) +- break; +- msleep(25); +- } +- if (ret == 0x1) +- *status |= FE_HAS_CARRIER +- | FE_HAS_VITERBI +- | FE_HAS_SYNC; +- state->it913x_status = *status; +- } +- } +- +- if (state->it913x_status & FE_HAS_SYNC) { +- ret = it913x_read_reg_u8(state, TPSD_LOCK); +- if (ret == 0x1) +- *status |= FE_HAS_LOCK +- | state->it913x_status; +- else +- state->it913x_status = 0; +- if (old_status != state->it913x_status) +- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, ret); +- } +- +- return 0; +-} +- +-static int it913x_fe_read_signal_strength(struct dvb_frontend *fe, +- u16 *strength) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL); +- /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/ +- if (state->it913x_status & FE_HAS_SIGNAL) +- ret = (ret * 0xff) / 0x64; +- else +- ret = 0x0; +- ret |= ret << 0x8; +- *strength = ret; +- return 0; +-} +- +-static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 reg[3]; +- u32 snr_val, snr_min, snr_max; +- u32 temp; +- +- ret = it913x_read_reg(state, 0x2c, reg, sizeof(reg)); +- +- snr_val = (u32)(reg[2] << 16) | (reg[1] << 8) | reg[0]; +- +- ret |= it913x_read_reg(state, 0xf78b, reg, 1); +- if (reg[0]) +- snr_val /= reg[0]; +- +- if (state->transmission_mode == TRANSMISSION_MODE_2K) +- snr_val *= 4; +- else if (state->transmission_mode == TRANSMISSION_MODE_4K) +- snr_val *= 2; +- +- if (state->constellation == QPSK) { +- snr_min = 0xb4711; +- snr_max = 0x191451; +- } else if (state->constellation == QAM_16) { +- snr_min = 0x4f0d5; +- snr_max = 0xc7925; +- } else if (state->constellation == QAM_64) { +- snr_min = 0x256d0; +- snr_max = 0x626be; +- } else +- return -EINVAL; +- +- if (snr_val < snr_min) +- *snr = 0; +- else if (snr_val < snr_max) { +- temp = (snr_val - snr_min) >> 5; +- temp *= 0xffff; +- temp /= (snr_max - snr_min) >> 5; +- *snr = (u16)temp; +- } else +- *snr = 0xffff; +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 reg[5]; +- /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */ +- ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); +- state->ucblocks += (u32)(reg[1] << 8) | reg[0]; +- *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2]; +- return 0; +-} +- +-static int it913x_fe_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 reg[2]; +- /* Aborted Packets */ +- ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); +- state->ucblocks += (u32)(reg[1] << 8) | reg[0]; +- *ucblocks = state->ucblocks; +- return ret; +-} +- +-static int it913x_fe_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret; +- u8 reg[8]; +- +- ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); +- +- if (reg[3] < 3) +- p->modulation = fe_con[reg[3]]; +- +- if (reg[0] < 3) +- p->transmission_mode = fe_mode[reg[0]]; +- +- if (reg[1] < 4) +- p->guard_interval = fe_gi[reg[1]]; +- +- if (reg[2] < 4) +- p->hierarchy = fe_hi[reg[2]]; +- +- p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE; +- p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE; +- +- /* Update internal state to reflect the autodetected props */ +- state->constellation = p->modulation; +- state->transmission_mode = p->transmission_mode; +- +- return 0; +-} +- +-static int it913x_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret, i; +- u8 empty_ch, last_ch; +- +- state->it913x_status = 0; +- +- /* Set bw*/ +- ret = it913x_fe_select_bw(state, p->bandwidth_hz, +- state->adcFrequency); +- +- /* Training Mode Off */ +- ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); +- +- /* Clear Empty Channel */ +- ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); +- +- /* Clear bits */ +- ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); +- /* LED on */ +- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); +- /* Select Band*/ +- if ((p->frequency >= 51000000) && (p->frequency <= 230000000)) +- i = 0; +- else if ((p->frequency >= 350000000) && (p->frequency <= 900000000)) +- i = 1; +- else if ((p->frequency >= 1450000000) && (p->frequency <= 1680000000)) +- i = 2; +- else +- return -EOPNOTSUPP; +- +- ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); +- +- deb_info("Frontend Set Tuner Type %02x", state->tuner_type); +- switch (state->tuner_type) { +- case IT9135_38: +- case IT9135_51: +- case IT9135_52: +- case IT9135_60: +- case IT9135_61: +- case IT9135_62: +- ret = it9137_set_tuner(state, +- p->bandwidth_hz, p->frequency); +- break; +- default: +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- break; +- } +- /* LED off */ +- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); +- /* Trigger ofsm */ +- ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); +- last_ch = 2; +- for (i = 0; i < 40; ++i) { +- empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); +- if (last_ch == 1 && empty_ch == 1) +- break; +- if (last_ch == 2 && empty_ch == 2) +- return 0; +- last_ch = empty_ch; +- msleep(25); +- } +- for (i = 0; i < 40; ++i) { +- if (it913x_read_reg_u8(state, D_TPSD_LOCK) == 1) +- break; +- msleep(25); +- } +- +- state->frequency = p->frequency; +- return 0; +-} +- +-static int it913x_fe_suspend(struct it913x_fe_state *state) +-{ +- int ret, i; +- u8 b; +- +- ret = it913x_write_reg(state, PRO_DMOD, SUSPEND_FLAG, 0x1); +- +- ret |= it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); +- +- for (i = 0; i < 128; i++) { +- ret = it913x_read_reg(state, SUSPEND_FLAG, &b, 1); +- if (ret < 0) +- return -ENODEV; +- if (b == 0) +- break; +- +- } +- +- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x8); +- /* Turn LED off */ +- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); +- +- ret |= it913x_fe_script_loader(state, it9137_tuner_off); +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-/* Power sequence */ +-/* Power Up Tuner on -> Frontend suspend off -> Tuner clk on */ +-/* Power Down Frontend suspend on -> Tuner clk off -> Tuner off */ +- +-static int it913x_fe_sleep(struct dvb_frontend *fe) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- return it913x_fe_suspend(state); +-} +- +-static u32 compute_div(u32 a, u32 b, u32 x) +-{ +- u32 res = 0; +- u32 c = 0; +- u32 i = 0; +- +- if (a > b) { +- c = a / b; +- a = a - c * b; +- } +- +- for (i = 0; i < x; i++) { +- if (a >= b) { +- res += 1; +- a -= b; +- } +- a <<= 1; +- res <<= 1; +- } +- +- res = (c << x) + res; +- +- return res; +-} +- +-static int it913x_fe_start(struct it913x_fe_state *state) +-{ +- struct it913xset *set_lna; +- struct it913xset *set_mode; +- int ret; +- u8 adf = (state->config->adf & 0xf); +- u32 adc, xtal; +- u8 b[4]; +- +- if (state->config->chip_ver == 1) +- ret = it913x_init_tuner(state); +- +- info("ADF table value :%02x", adf); +- +- if (adf < 10) { +- state->crystalFrequency = fe_clockTable[adf].xtal ; +- state->table = fe_clockTable[adf].table; +- state->adcFrequency = state->table->adcFrequency; +- +- adc = compute_div(state->adcFrequency, 1000000ul, 19ul); +- xtal = compute_div(state->crystalFrequency, 1000000ul, 19ul); +- +- } else +- return -EINVAL; +- +- /* Set LED indicator on GPIOH3 */ +- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_EN, 0x1); +- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_ON, 0x1); +- ret |= it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); +- +- ret |= it913x_write_reg(state, PRO_LINK, 0xf641, state->tuner_type); +- ret |= it913x_write_reg(state, PRO_DMOD, 0xf5ca, 0x01); +- ret |= it913x_write_reg(state, PRO_DMOD, 0xf715, 0x01); +- +- b[0] = xtal & 0xff; +- b[1] = (xtal >> 8) & 0xff; +- b[2] = (xtal >> 16) & 0xff; +- b[3] = (xtal >> 24); +- ret |= it913x_write(state, PRO_DMOD, XTAL_CLK, b , 4); +- +- b[0] = adc & 0xff; +- b[1] = (adc >> 8) & 0xff; +- b[2] = (adc >> 16) & 0xff; +- ret |= it913x_write(state, PRO_DMOD, ADC_FREQ, b, 3); +- +- if (state->config->adc_x2) +- ret |= it913x_write_reg(state, PRO_DMOD, ADC_X_2, 0x01); +- b[0] = 0; +- b[1] = 0; +- b[2] = 0; +- ret |= it913x_write(state, PRO_DMOD, 0x0029, b, 3); +- +- info("Crystal Frequency :%d Adc Frequency :%d ADC X2: %02x", +- state->crystalFrequency, state->adcFrequency, +- state->config->adc_x2); +- deb_info("Xtal value :%04x Adc value :%04x", xtal, adc); +- +- if (ret < 0) +- return -ENODEV; +- +- /* v1 or v2 tuner script */ +- if (state->config->chip_ver > 1) +- ret = it913x_fe_script_loader(state, it9135_v2); +- else +- ret = it913x_fe_script_loader(state, it9135_v1); +- if (ret < 0) +- return ret; +- +- /* LNA Scripts */ +- switch (state->tuner_type) { +- case IT9135_51: +- set_lna = it9135_51; +- break; +- case IT9135_52: +- set_lna = it9135_52; +- break; +- case IT9135_60: +- set_lna = it9135_60; +- break; +- case IT9135_61: +- set_lna = it9135_61; +- break; +- case IT9135_62: +- set_lna = it9135_62; +- break; +- case IT9135_38: +- default: +- set_lna = it9135_38; +- } +- info("Tuner LNA type :%02x", state->tuner_type); +- +- ret = it913x_fe_script_loader(state, set_lna); +- if (ret < 0) +- return ret; +- +- if (state->config->chip_ver == 2) { +- ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x1); +- ret |= it913x_write_reg(state, PRO_LINK, PADODPU, 0x0); +- ret |= it913x_write_reg(state, PRO_LINK, AGC_O_D, 0x0); +- ret |= it913x_init_tuner(state); +- } +- if (ret < 0) +- return -ENODEV; +- +- /* Always solo frontend */ +- set_mode = set_solo_fe; +- ret |= it913x_fe_script_loader(state, set_mode); +- +- ret |= it913x_fe_suspend(state); +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static int it913x_fe_init(struct dvb_frontend *fe) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- int ret = 0; +- /* Power Up Tuner - common all versions */ +- ret = it913x_write_reg(state, PRO_DMOD, 0xec40, 0x1); +- +- ret |= it913x_fe_script_loader(state, init_1); +- +- ret |= it913x_write_reg(state, PRO_DMOD, AFE_MEM0, 0x0); +- +- ret |= it913x_write_reg(state, PRO_DMOD, 0xfba8, 0x0); +- +- return (ret < 0) ? -ENODEV : 0; +-} +- +-static void it913x_fe_release(struct dvb_frontend *fe) +-{ +- struct it913x_fe_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops it913x_fe_ofdm_ops; +- +-struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, +- u8 i2c_addr, struct ite_config *config) +-{ +- struct it913x_fe_state *state = NULL; +- int ret; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct it913x_fe_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- if (config == NULL) +- goto error; +- +- state->i2c_adap = i2c_adap; +- state->i2c_addr = i2c_addr; +- state->config = config; +- +- switch (state->config->tuner_id_0) { +- case IT9135_51: +- case IT9135_52: +- case IT9135_60: +- case IT9135_61: +- case IT9135_62: +- state->tuner_type = state->config->tuner_id_0; +- break; +- default: +- case IT9135_38: +- state->tuner_type = IT9135_38; +- } +- +- ret = it913x_fe_start(state); +- if (ret < 0) +- goto error; +- +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &it913x_fe_ofdm_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(it913x_fe_attach); +- +-static struct dvb_frontend_ops it913x_fe_ofdm_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "it913x-fe DVB-T", +- .frequency_min = 51000000, +- .frequency_max = 1680000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = it913x_fe_release, +- +- .init = it913x_fe_init, +- .sleep = it913x_fe_sleep, +- +- .set_frontend = it913x_fe_set_frontend, +- .get_frontend = it913x_fe_get_frontend, +- +- .read_status = it913x_fe_read_status, +- .read_signal_strength = it913x_fe_read_signal_strength, +- .read_snr = it913x_fe_read_snr, +- .read_ber = it913x_fe_read_ber, +- .read_ucblocks = it913x_fe_read_ucblocks, +-}; +- +-MODULE_DESCRIPTION("it913x Frontend and it9137 tuner"); +-MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); +-MODULE_VERSION("1.13"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h +deleted file mode 100644 +index c4a908e..0000000 +--- a/drivers/media/dvb/frontends/it913x-fe.h ++++ /dev/null +@@ -1,233 +0,0 @@ +-/* +- * Driver for it913x Frontend +- * +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef IT913X_FE_H +-#define IT913X_FE_H +- +-#include +-#include "dvb_frontend.h" +- +-struct ite_config { +- u8 chip_ver; +- u16 chip_type; +- u32 firmware; +- u8 firmware_ver; +- u8 adc_x2; +- u8 tuner_id_0; +- u8 tuner_id_1; +- u8 dual_mode; +- u8 adf; +-}; +- +-#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \ +-defined(MODULE)) +-extern struct dvb_frontend *it913x_fe_attach(struct i2c_adapter *i2c_adap, +- u8 i2c_addr, struct ite_config *config); +-#else +-static inline struct dvb_frontend *it913x_fe_attach( +- struct i2c_adapter *i2c_adap, +- u8 i2c_addr, struct ite_config *config) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_IT913X_FE */ +-#define I2C_BASE_ADDR 0x10 +-#define DEV_0 0x0 +-#define DEV_1 0x10 +-#define PRO_LINK 0x0 +-#define PRO_DMOD 0x1 +-#define DEV_0_DMOD (PRO_DMOD << 0x7) +-#define DEV_1_DMOD (DEV_0_DMOD | DEV_1) +-#define CHIP2_I2C_ADDR 0x3a +- +-#define AFE_MEM0 0xfb24 +- +-#define MP2_SW_RST 0xf99d +-#define MP2IF2_SW_RST 0xf9a4 +- +-#define PADODPU 0xd827 +-#define THIRDODPU 0xd828 +-#define AGC_O_D 0xd829 +- +-#define EP0_TX_EN 0xdd11 +-#define EP0_TX_NAK 0xdd13 +-#define EP4_TX_LEN_LSB 0xdd88 +-#define EP4_TX_LEN_MSB 0xdd89 +-#define EP4_MAX_PKT 0xdd0c +-#define EP5_TX_LEN_LSB 0xdd8a +-#define EP5_TX_LEN_MSB 0xdd8b +-#define EP5_MAX_PKT 0xdd0d +- +-#define IO_MUX_POWER_CLK 0xd800 +-#define CLK_O_EN 0xd81a +-#define I2C_CLK 0xf103 +-#define I2C_CLK_100 0x7 +-#define I2C_CLK_400 0x1a +- +-#define D_TPSD_LOCK 0xf5a9 +-#define MP2IF2_EN 0xf9a3 +-#define MP2IF_SERIAL 0xf985 +-#define TSIS_ENABLE 0xf9cd +-#define MP2IF2_HALF_PSB 0xf9a5 +-#define MP2IF_STOP_EN 0xf9b5 +-#define MPEG_FULL_SPEED 0xf990 +-#define TOP_HOSTB_SER_MODE 0xd91c +- +-#define PID_RST 0xf992 +-#define PID_EN 0xf993 +-#define PID_INX_EN 0xf994 +-#define PID_INX 0xf995 +-#define PID_LSB 0xf996 +-#define PID_MSB 0xf997 +- +-#define MP2IF_MPEG_PAR_MODE 0xf986 +-#define DCA_UPPER_CHIP 0xf731 +-#define DCA_LOWER_CHIP 0xf732 +-#define DCA_PLATCH 0xf730 +-#define DCA_FPGA_LATCH 0xf778 +-#define DCA_STAND_ALONE 0xf73c +-#define DCA_ENABLE 0xf776 +- +-#define DVBT_INTEN 0xf41f +-#define DVBT_ENABLE 0xf41a +-#define HOSTB_DCA_LOWER 0xd91f +-#define HOSTB_MPEG_PAR_MODE 0xd91b +-#define HOSTB_MPEG_SER_MODE 0xd91c +-#define HOSTB_MPEG_SER_DO7 0xd91d +-#define HOSTB_DCA_UPPER 0xd91e +-#define PADMISCDR2 0xd830 +-#define PADMISCDR4 0xd831 +-#define PADMISCDR8 0xd832 +-#define PADMISCDRSR 0xd833 +-#define LOCK3_OUT 0xd8fd +- +-#define GPIOH1_O 0xd8af +-#define GPIOH1_EN 0xd8b0 +-#define GPIOH1_ON 0xd8b1 +-#define GPIOH3_O 0xd8b3 +-#define GPIOH3_EN 0xd8b4 +-#define GPIOH3_ON 0xd8b5 +-#define GPIOH5_O 0xd8bb +-#define GPIOH5_EN 0xd8bc +-#define GPIOH5_ON 0xd8bd +- +-#define AFE_MEM0 0xfb24 +- +-#define REG_TPSD_TX_MODE 0xf900 +-#define REG_TPSD_GI 0xf901 +-#define REG_TPSD_HIER 0xf902 +-#define REG_TPSD_CONST 0xf903 +-#define REG_BW 0xf904 +-#define REG_PRIV 0xf905 +-#define REG_TPSD_HP_CODE 0xf906 +-#define REG_TPSD_LP_CODE 0xf907 +- +-#define MP2IF_SYNC_LK 0xf999 +-#define ADC_FREQ 0xf1cd +- +-#define TRIGGER_OFSM 0x0000 +-/* COEFF Registers start at 0x0001 to 0x0020 */ +-#define COEFF_1_2048 0x0001 +-#define XTAL_CLK 0x0025 +-#define BFS_FCW 0x0029 +- +-/* Error Regs */ +-#define RSD_ABORT_PKT_LSB 0x0032 +-#define RSD_ABORT_PKT_MSB 0x0033 +-#define RSD_BIT_ERR_0_7 0x0034 +-#define RSD_BIT_ERR_8_15 0x0035 +-#define RSD_BIT_ERR_23_16 0x0036 +-#define RSD_BIT_COUNT_LSB 0x0037 +-#define RSD_BIT_COUNT_MSB 0x0038 +- +-#define TPSD_LOCK 0x003c +-#define TRAINING_MODE 0x0040 +-#define ADC_X_2 0x0045 +-#define TUNER_ID 0x0046 +-#define EMPTY_CHANNEL_STATUS 0x0047 +-#define SIGNAL_LEVEL 0x0048 +-#define SIGNAL_QUALITY 0x0049 +-#define EST_SIGNAL_LEVEL 0x004a +-#define FREE_BAND 0x004b +-#define SUSPEND_FLAG 0x004c +-/* Build in tuner types */ +-#define IT9137 0x38 +-#define IT9135_38 0x38 +-#define IT9135_51 0x51 +-#define IT9135_52 0x52 +-#define IT9135_60 0x60 +-#define IT9135_61 0x61 +-#define IT9135_62 0x62 +- +-enum { +- CMD_DEMOD_READ = 0, +- CMD_DEMOD_WRITE, +- CMD_TUNER_READ, +- CMD_TUNER_WRITE, +- CMD_REG_EEPROM_READ, +- CMD_REG_EEPROM_WRITE, +- CMD_DATA_READ, +- CMD_VAR_READ = 8, +- CMD_VAR_WRITE, +- CMD_PLATFORM_GET, +- CMD_PLATFORM_SET, +- CMD_IP_CACHE, +- CMD_IP_ADD, +- CMD_IP_REMOVE, +- CMD_PID_ADD, +- CMD_PID_REMOVE, +- CMD_SIPSI_GET, +- CMD_SIPSI_MPE_RESET, +- CMD_H_PID_ADD = 0x15, +- CMD_H_PID_REMOVE, +- CMD_ABORT, +- CMD_IR_GET, +- CMD_IR_SET, +- CMD_FW_DOWNLOAD = 0x21, +- CMD_QUERYINFO, +- CMD_BOOT, +- CMD_FW_DOWNLOAD_BEGIN, +- CMD_FW_DOWNLOAD_END, +- CMD_RUN_CODE, +- CMD_SCATTER_READ = 0x28, +- CMD_SCATTER_WRITE, +- CMD_GENERIC_READ, +- CMD_GENERIC_WRITE +-}; +- +-enum { +- READ_LONG, +- WRITE_LONG, +- READ_SHORT, +- WRITE_SHORT, +- READ_DATA, +- WRITE_DATA, +- WRITE_CMD, +-}; +- +-enum { +- IT9135_AUTO = 0, +- IT9137_FW, +- IT9135_V1_FW, +- IT9135_V2_FW, +-}; +- +-#endif /* IT913X_FE_H */ +diff --git a/drivers/media/dvb/frontends/itd1000.c b/drivers/media/dvb/frontends/itd1000.c +deleted file mode 100644 +index 3164575..0000000 +--- a/drivers/media/dvb/frontends/itd1000.c ++++ /dev/null +@@ -1,399 +0,0 @@ +-/* +- * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" +- * +- * Copyright (c) 2007-8 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "itd1000.h" +-#include "itd1000_priv.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-#define itd_dbg(args...) do { \ +- if (debug) { \ +- printk(KERN_DEBUG "ITD1000: " args);\ +- } \ +-} while (0) +- +-#define itd_warn(args...) do { \ +- printk(KERN_WARNING "ITD1000: " args); \ +-} while (0) +- +-#define itd_info(args...) do { \ +- printk(KERN_INFO "ITD1000: " args); \ +-} while (0) +- +-/* don't write more than one byte with flexcop behind */ +-static int itd1000_write_regs(struct itd1000_state *state, u8 reg, u8 v[], u8 len) +-{ +- u8 buf[1+len]; +- struct i2c_msg msg = { +- .addr = state->cfg->i2c_address, .flags = 0, .buf = buf, .len = len+1 +- }; +- buf[0] = reg; +- memcpy(&buf[1], v, len); +- +- /* itd_dbg("wr %02x: %02x\n", reg, v[0]); */ +- +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- printk(KERN_WARNING "itd1000 I2C write failed\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int itd1000_read_reg(struct itd1000_state *state, u8 reg) +-{ +- u8 val; +- struct i2c_msg msg[2] = { +- { .addr = state->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = state->cfg->i2c_address, .flags = I2C_M_RD, .buf = &val, .len = 1 }, +- }; +- +- /* ugly flexcop workaround */ +- itd1000_write_regs(state, (reg - 1) & 0xff, &state->shadow[(reg - 1) & 0xff], 1); +- +- if (i2c_transfer(state->i2c, msg, 2) != 2) { +- itd_warn("itd1000 I2C read failed\n"); +- return -EREMOTEIO; +- } +- return val; +-} +- +-static inline int itd1000_write_reg(struct itd1000_state *state, u8 r, u8 v) +-{ +- int ret = itd1000_write_regs(state, r, &v, 1); +- state->shadow[r] = v; +- return ret; +-} +- +- +-static struct { +- u32 symbol_rate; +- u8 pgaext : 4; /* PLLFH */ +- u8 bbgvmin : 4; /* BBGVMIN */ +-} itd1000_lpf_pga[] = { +- { 0, 0x8, 0x3 }, +- { 5200000, 0x8, 0x3 }, +- { 12200000, 0x4, 0x3 }, +- { 15400000, 0x2, 0x3 }, +- { 19800000, 0x2, 0x3 }, +- { 21500000, 0x2, 0x3 }, +- { 24500000, 0x2, 0x3 }, +- { 28400000, 0x2, 0x3 }, +- { 33400000, 0x2, 0x3 }, +- { 34400000, 0x1, 0x4 }, +- { 34400000, 0x1, 0x4 }, +- { 38400000, 0x1, 0x4 }, +- { 38400000, 0x1, 0x4 }, +- { 40400000, 0x1, 0x4 }, +- { 45400000, 0x1, 0x4 }, +-}; +- +-static void itd1000_set_lpf_bw(struct itd1000_state *state, u32 symbol_rate) +-{ +- u8 i; +- u8 con1 = itd1000_read_reg(state, CON1) & 0xfd; +- u8 pllfh = itd1000_read_reg(state, PLLFH) & 0x0f; +- u8 bbgvmin = itd1000_read_reg(state, BBGVMIN) & 0xf0; +- u8 bw = itd1000_read_reg(state, BW) & 0xf0; +- +- itd_dbg("symbol_rate = %d\n", symbol_rate); +- +- /* not sure what is that ? - starting to download the table */ +- itd1000_write_reg(state, CON1, con1 | (1 << 1)); +- +- for (i = 0; i < ARRAY_SIZE(itd1000_lpf_pga); i++) +- if (symbol_rate < itd1000_lpf_pga[i].symbol_rate) { +- itd_dbg("symrate: index: %d pgaext: %x, bbgvmin: %x\n", i, itd1000_lpf_pga[i].pgaext, itd1000_lpf_pga[i].bbgvmin); +- itd1000_write_reg(state, PLLFH, pllfh | (itd1000_lpf_pga[i].pgaext << 4)); +- itd1000_write_reg(state, BBGVMIN, bbgvmin | (itd1000_lpf_pga[i].bbgvmin)); +- itd1000_write_reg(state, BW, bw | (i & 0x0f)); +- break; +- } +- +- itd1000_write_reg(state, CON1, con1 | (0 << 1)); +-} +- +-static struct { +- u8 vcorg; +- u32 fmax_rg; +-} itd1000_vcorg[] = { +- { 1, 920000 }, +- { 2, 971000 }, +- { 3, 1031000 }, +- { 4, 1091000 }, +- { 5, 1171000 }, +- { 6, 1281000 }, +- { 7, 1381000 }, +- { 8, 500000 }, /* this is intentional. */ +- { 9, 1451000 }, +- { 10, 1531000 }, +- { 11, 1631000 }, +- { 12, 1741000 }, +- { 13, 1891000 }, +- { 14, 2071000 }, +- { 15, 2250000 }, +-}; +- +-static void itd1000_set_vco(struct itd1000_state *state, u32 freq_khz) +-{ +- u8 i; +- u8 gvbb_i2c = itd1000_read_reg(state, GVBB_I2C) & 0xbf; +- u8 vco_chp1_i2c = itd1000_read_reg(state, VCO_CHP1_I2C) & 0x0f; +- u8 adcout; +- +- /* reserved bit again (reset ?) */ +- itd1000_write_reg(state, GVBB_I2C, gvbb_i2c | (1 << 6)); +- +- for (i = 0; i < ARRAY_SIZE(itd1000_vcorg); i++) { +- if (freq_khz < itd1000_vcorg[i].fmax_rg) { +- itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | (itd1000_vcorg[i].vcorg << 4)); +- msleep(1); +- +- adcout = itd1000_read_reg(state, PLLLOCK) & 0x0f; +- +- itd_dbg("VCO: %dkHz: %d -> ADCOUT: %d %02x\n", freq_khz, itd1000_vcorg[i].vcorg, adcout, vco_chp1_i2c); +- +- if (adcout > 13) { +- if (!(itd1000_vcorg[i].vcorg == 7 || itd1000_vcorg[i].vcorg == 15)) +- itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg + 1) << 4)); +- } else if (adcout < 2) { +- if (!(itd1000_vcorg[i].vcorg == 1 || itd1000_vcorg[i].vcorg == 9)) +- itd1000_write_reg(state, VCO_CHP1_I2C, vco_chp1_i2c | ((itd1000_vcorg[i].vcorg - 1) << 4)); +- } +- break; +- } +- } +-} +- +-static const struct { +- u32 freq; +- u8 values[10]; /* RFTR, RFST1 - RFST9 */ +-} itd1000_fre_values[] = { +- { 1075000, { 0x59, 0x1d, 0x1c, 0x17, 0x16, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, +- { 1250000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, +- { 1450000, { 0x89, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, +- { 1650000, { 0x69, 0x1e, 0x1d, 0x17, 0x15, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, +- { 1750000, { 0x69, 0x1e, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a } }, +- { 1850000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, +- { 1900000, { 0x69, 0x1d, 0x17, 0x15, 0x14, 0x0f, 0x0e, 0x0d, 0x0b, 0x0a } }, +- { 1950000, { 0x69, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0d, 0x0b, 0x0a } }, +- { 2050000, { 0x69, 0x1e, 0x1d, 0x17, 0x16, 0x14, 0x13, 0x0e, 0x0b, 0x0a } }, +- { 2150000, { 0x69, 0x1d, 0x1c, 0x17, 0x15, 0x14, 0x13, 0x0f, 0x0e, 0x0b } } +-}; +- +- +-#define FREF 16 +- +-static void itd1000_set_lo(struct itd1000_state *state, u32 freq_khz) +-{ +- int i, j; +- u32 plln, pllf; +- u64 tmp; +- +- plln = (freq_khz * 1000) / 2 / FREF; +- +- /* Compute the factional part times 1000 */ +- tmp = plln % 1000000; +- plln /= 1000000; +- +- tmp *= 1048576; +- do_div(tmp, 1000000); +- pllf = (u32) tmp; +- +- state->frequency = ((plln * 1000) + (pllf * 1000)/1048576) * 2*FREF; +- itd_dbg("frequency: %dkHz (wanted) %dkHz (set), PLLF = %d, PLLN = %d\n", freq_khz, state->frequency, pllf, plln); +- +- itd1000_write_reg(state, PLLNH, 0x80); /* PLLNH */; +- itd1000_write_reg(state, PLLNL, plln & 0xff); +- itd1000_write_reg(state, PLLFH, (itd1000_read_reg(state, PLLFH) & 0xf0) | ((pllf >> 16) & 0x0f)); +- itd1000_write_reg(state, PLLFM, (pllf >> 8) & 0xff); +- itd1000_write_reg(state, PLLFL, (pllf >> 0) & 0xff); +- +- for (i = 0; i < ARRAY_SIZE(itd1000_fre_values); i++) { +- if (freq_khz <= itd1000_fre_values[i].freq) { +- itd_dbg("fre_values: %d\n", i); +- itd1000_write_reg(state, RFTR, itd1000_fre_values[i].values[0]); +- for (j = 0; j < 9; j++) +- itd1000_write_reg(state, RFST1+j, itd1000_fre_values[i].values[j+1]); +- break; +- } +- } +- +- itd1000_set_vco(state, freq_khz); +-} +- +-static int itd1000_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct itd1000_state *state = fe->tuner_priv; +- u8 pllcon1; +- +- itd1000_set_lo(state, c->frequency); +- itd1000_set_lpf_bw(state, c->symbol_rate); +- +- pllcon1 = itd1000_read_reg(state, PLLCON1) & 0x7f; +- itd1000_write_reg(state, PLLCON1, pllcon1 | (1 << 7)); +- itd1000_write_reg(state, PLLCON1, pllcon1); +- +- return 0; +-} +- +-static int itd1000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct itd1000_state *state = fe->tuner_priv; +- *frequency = state->frequency; +- return 0; +-} +- +-static int itd1000_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- return 0; +-} +- +-static u8 itd1000_init_tab[][2] = { +- { PLLCON1, 0x65 }, /* Register does not change */ +- { PLLNH, 0x80 }, /* Bits [7:6] do not change */ +- { RESERVED_0X6D, 0x3b }, +- { VCO_CHP2_I2C, 0x12 }, +- { 0x72, 0xf9 }, /* No such regsister defined */ +- { RESERVED_0X73, 0xff }, +- { RESERVED_0X74, 0xb2 }, +- { RESERVED_0X75, 0xc7 }, +- { EXTGVBBRF, 0xf0 }, +- { DIVAGCCK, 0x80 }, +- { BBTR, 0xa0 }, +- { RESERVED_0X7E, 0x4f }, +- { 0x82, 0x88 }, /* No such regsister defined */ +- { 0x83, 0x80 }, /* No such regsister defined */ +- { 0x84, 0x80 }, /* No such regsister defined */ +- { RESERVED_0X85, 0x74 }, +- { RESERVED_0X86, 0xff }, +- { RESERVED_0X88, 0x02 }, +- { RESERVED_0X89, 0x16 }, +- { RFST0, 0x1f }, +- { RESERVED_0X94, 0x66 }, +- { RESERVED_0X95, 0x66 }, +- { RESERVED_0X96, 0x77 }, +- { RESERVED_0X97, 0x99 }, +- { RESERVED_0X98, 0xff }, +- { RESERVED_0X99, 0xfc }, +- { RESERVED_0X9A, 0xba }, +- { RESERVED_0X9B, 0xaa }, +-}; +- +-static u8 itd1000_reinit_tab[][2] = { +- { VCO_CHP1_I2C, 0x8a }, +- { BW, 0x87 }, +- { GVBB_I2C, 0x03 }, +- { BBGVMIN, 0x03 }, +- { CON1, 0x2e }, +-}; +- +- +-static int itd1000_init(struct dvb_frontend *fe) +-{ +- struct itd1000_state *state = fe->tuner_priv; +- int i; +- +- for (i = 0; i < ARRAY_SIZE(itd1000_init_tab); i++) +- itd1000_write_reg(state, itd1000_init_tab[i][0], itd1000_init_tab[i][1]); +- +- for (i = 0; i < ARRAY_SIZE(itd1000_reinit_tab); i++) +- itd1000_write_reg(state, itd1000_reinit_tab[i][0], itd1000_reinit_tab[i][1]); +- +- return 0; +-} +- +-static int itd1000_sleep(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int itd1000_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static const struct dvb_tuner_ops itd1000_tuner_ops = { +- .info = { +- .name = "Integrant ITD1000", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 125, /* kHz for QPSK frontends */ +- }, +- +- .release = itd1000_release, +- +- .init = itd1000_init, +- .sleep = itd1000_sleep, +- +- .set_params = itd1000_set_parameters, +- .get_frequency = itd1000_get_frequency, +- .get_bandwidth = itd1000_get_bandwidth +-}; +- +- +-struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +-{ +- struct itd1000_state *state = NULL; +- u8 i = 0; +- +- state = kzalloc(sizeof(struct itd1000_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- state->cfg = cfg; +- state->i2c = i2c; +- +- i = itd1000_read_reg(state, 0); +- if (i != 0) { +- kfree(state); +- return NULL; +- } +- itd_info("successfully identified (ID: %d)\n", i); +- +- memset(state->shadow, 0xff, sizeof(state->shadow)); +- for (i = 0x65; i < 0x9c; i++) +- state->shadow[i] = itd1000_read_reg(state, i); +- +- memcpy(&fe->ops.tuner_ops, &itd1000_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = state; +- +- return fe; +-} +-EXPORT_SYMBOL(itd1000_attach); +- +-MODULE_AUTHOR("Patrick Boettcher "); +-MODULE_DESCRIPTION("Integrant ITD1000 driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/itd1000.h b/drivers/media/dvb/frontends/itd1000.h +deleted file mode 100644 +index 5e18df0..0000000 +--- a/drivers/media/dvb/frontends/itd1000.h ++++ /dev/null +@@ -1,42 +0,0 @@ +-/* +- * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" +- * +- * Copyright (c) 2007 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef ITD1000_H +-#define ITD1000_H +- +-struct dvb_frontend; +-struct i2c_adapter; +- +-struct itd1000_config { +- u8 i2c_address; +-}; +- +-#if defined(CONFIG_DVB_TUNER_ITD1000) || (defined(CONFIG_DVB_TUNER_ITD1000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg); +-#else +-static inline struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct itd1000_config *cfg) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/itd1000_priv.h b/drivers/media/dvb/frontends/itd1000_priv.h +deleted file mode 100644 +index 08ca851..0000000 +--- a/drivers/media/dvb/frontends/itd1000_priv.h ++++ /dev/null +@@ -1,88 +0,0 @@ +-/* +- * Driver for the Integrant ITD1000 "Zero-IF Tuner IC for Direct Broadcast Satellite" +- * +- * Copyright (c) 2007 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef ITD1000_PRIV_H +-#define ITD1000_PRIV_H +- +-struct itd1000_state { +- struct itd1000_config *cfg; +- struct i2c_adapter *i2c; +- +- u32 frequency; /* contains the value resulting from the LO-setting */ +- +- /* ugly workaround for flexcop's incapable i2c-controller +- * FIXME, if possible +- */ +- u8 shadow[256]; +-}; +- +-enum itd1000_register { +- VCO_CHP1 = 0x65, +- VCO_CHP2, +- PLLCON1, +- PLLNH, +- PLLNL, +- PLLFH, +- PLLFM, +- PLLFL, +- RESERVED_0X6D, +- PLLLOCK, +- VCO_CHP2_I2C, +- VCO_CHP1_I2C, +- BW, +- RESERVED_0X73 = 0x73, +- RESERVED_0X74, +- RESERVED_0X75, +- GVBB, +- GVRF, +- GVBB_I2C, +- EXTGVBBRF, +- DIVAGCCK, +- BBTR, +- RFTR, +- BBGVMIN, +- RESERVED_0X7E, +- RESERVED_0X85 = 0x85, +- RESERVED_0X86, +- CON1, +- RESERVED_0X88, +- RESERVED_0X89, +- RFST0, +- RFST1, +- RFST2, +- RFST3, +- RFST4, +- RFST5, +- RFST6, +- RFST7, +- RFST8, +- RFST9, +- RESERVED_0X94, +- RESERVED_0X95, +- RESERVED_0X96, +- RESERVED_0X97, +- RESERVED_0X98, +- RESERVED_0X99, +- RESERVED_0X9A, +- RESERVED_0X9B, +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/ix2505v.c b/drivers/media/dvb/frontends/ix2505v.c +deleted file mode 100644 +index bc5a820..0000000 +--- a/drivers/media/dvb/frontends/ix2505v.c ++++ /dev/null +@@ -1,325 +0,0 @@ +-/** +- * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner +- * +- * Copyright (C) 2010 Malcolm Priestley +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#include "ix2505v.h" +- +-static int ix2505v_debug; +-#define dprintk(level, args...) do { \ +- if (ix2505v_debug & level) \ +- printk(KERN_DEBUG "ix2505v: " args); \ +-} while (0) +- +-#define deb_info(args...) dprintk(0x01, args) +-#define deb_i2c(args...) dprintk(0x02, args) +- +-struct ix2505v_state { +- struct i2c_adapter *i2c; +- const struct ix2505v_config *config; +- u32 frequency; +-}; +- +-/** +- * Data read format of the Sharp IX2505V B0017 +- * +- * byte1: 1 | 1 | 0 | 0 | 0 | MA1 | MA0 | 1 +- * byte2: POR | FL | RD2 | RD1 | RD0 | X | X | X +- * +- * byte1 = address +- * byte2; +- * POR = Power on Reset (VCC H=<2.2v L=>2.2v) +- * FL = Phase Lock (H=lock L=unlock) +- * RD0-2 = Reserved internal operations +- * +- * Only POR can be used to check the tuner is present +- * +- * Caution: after byte2 the I2C reverts to write mode continuing to read +- * may corrupt tuning data. +- * +- */ +- +-static int ix2505v_read_status_reg(struct ix2505v_state *state) +-{ +- u8 addr = state->config->tuner_address; +- u8 b2[] = {0}; +- int ret; +- +- struct i2c_msg msg[1] = { +- { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 1); +- deb_i2c("Read %s ", __func__); +- +- return (ret == 1) ? (int) b2[0] : -1; +-} +- +-static int ix2505v_write(struct ix2505v_state *state, u8 buf[], u8 count) +-{ +- struct i2c_msg msg[1] = { +- { .addr = state->config->tuner_address, .flags = 0, +- .buf = buf, .len = count }, +- }; +- +- int ret; +- +- ret = i2c_transfer(state->i2c, msg, 1); +- +- if (ret != 1) { +- deb_i2c("%s: i2c error, ret=%d\n", __func__, ret); +- return -EIO; +- } +- +- return 0; +-} +- +-static int ix2505v_release(struct dvb_frontend *fe) +-{ +- struct ix2505v_state *state = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(state); +- +- return 0; +-} +- +-/** +- * Data write format of the Sharp IX2505V B0017 +- * +- * byte1: 1 | 1 | 0 | 0 | 0 | 0(MA1)| 0(MA0)| 0 +- * byte2: 0 | BG1 | BG2 | N8 | N7 | N6 | N5 | N4 +- * byte3: N3 | N2 | N1 | A5 | A4 | A3 | A2 | A1 +- * byte4: 1 | 1(C1) | 1(C0) | PD5 | PD4 | TM | 0(RTS)| 1(REF) +- * byte5: BA2 | BA1 | BA0 | PSC | PD3 |PD2/TS2|DIV/TS1|PD0/TS0 +- * +- * byte1 = address +- * +- * Write order +- * 1) byte1 -> byte2 -> byte3 -> byte4 -> byte5 +- * 2) byte1 -> byte4 -> byte5 -> byte2 -> byte3 +- * 3) byte1 -> byte2 -> byte3 -> byte4 +- * 4) byte1 -> byte4 -> byte5 -> byte2 +- * 5) byte1 -> byte2 -> byte3 +- * 6) byte1 -> byte4 -> byte5 +- * 7) byte1 -> byte2 +- * 8) byte1 -> byte4 +- * +- * Recommended Setup +- * 1 -> 8 -> 6 +- */ +- +-static int ix2505v_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct ix2505v_state *state = fe->tuner_priv; +- u32 frequency = c->frequency; +- u32 b_w = (c->symbol_rate * 27) / 32000; +- u32 div_factor, N , A, x; +- int ret = 0, len; +- u8 gain, cc, ref, psc, local_osc, lpf; +- u8 data[4] = {0}; +- +- if ((frequency < fe->ops.info.frequency_min) +- || (frequency > fe->ops.info.frequency_max)) +- return -EINVAL; +- +- if (state->config->tuner_gain) +- gain = (state->config->tuner_gain < 4) +- ? state->config->tuner_gain : 0; +- else +- gain = 0x0; +- +- if (state->config->tuner_chargepump) +- cc = state->config->tuner_chargepump; +- else +- cc = 0x3; +- +- ref = 8; /* REF =1 */ +- psc = 32; /* PSC = 0 */ +- +- div_factor = (frequency * ref) / 40; /* local osc = 4Mhz */ +- x = div_factor / psc; +- N = x/100; +- A = ((x - (N * 100)) * psc) / 100; +- +- data[0] = ((gain & 0x3) << 5) | (N >> 3); +- data[1] = (N << 5) | (A & 0x1f); +- data[2] = 0x81 | ((cc & 0x3) << 5) ; /*PD5,PD4 & TM = 0|C1,C0|REF=1*/ +- +- deb_info("Frq=%d x=%d N=%d A=%d\n", frequency, x, N, A); +- +- if (frequency <= 1065000) +- local_osc = (6 << 5) | 2; +- else if (frequency <= 1170000) +- local_osc = (7 << 5) | 2; +- else if (frequency <= 1300000) +- local_osc = (1 << 5); +- else if (frequency <= 1445000) +- local_osc = (2 << 5); +- else if (frequency <= 1607000) +- local_osc = (3 << 5); +- else if (frequency <= 1778000) +- local_osc = (4 << 5); +- else if (frequency <= 1942000) +- local_osc = (5 << 5); +- else /*frequency up to 2150000*/ +- local_osc = (6 << 5); +- +- data[3] = local_osc; /* all other bits set 0 */ +- +- if (b_w <= 10000) +- lpf = 0xc; +- else if (b_w <= 12000) +- lpf = 0x2; +- else if (b_w <= 14000) +- lpf = 0xa; +- else if (b_w <= 16000) +- lpf = 0x6; +- else if (b_w <= 18000) +- lpf = 0xe; +- else if (b_w <= 20000) +- lpf = 0x1; +- else if (b_w <= 22000) +- lpf = 0x9; +- else if (b_w <= 24000) +- lpf = 0x5; +- else if (b_w <= 26000) +- lpf = 0xd; +- else if (b_w <= 28000) +- lpf = 0x3; +- else +- lpf = 0xb; +- +- deb_info("Osc=%x b_w=%x lpf=%x\n", local_osc, b_w, lpf); +- deb_info("Data 0=[%x%x%x%x]\n", data[0], data[1], data[2], data[3]); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- len = sizeof(data); +- ret |= ix2505v_write(state, data, len); +- +- data[2] |= 0x4; /* set TM = 1 other bits same */ +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- len = 1; +- ret |= ix2505v_write(state, &data[2], len); /* write byte 4 only */ +- +- msleep(10); +- +- data[2] |= ((lpf >> 2) & 0x3) << 3; /* lpf */ +- data[3] |= (lpf & 0x3) << 2; +- +- deb_info("Data 2=[%x%x]\n", data[2], data[3]); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- len = 2; +- ret |= ix2505v_write(state, &data[2], len); /* write byte 4 & 5 */ +- +- if (state->config->min_delay_ms) +- msleep(state->config->min_delay_ms); +- +- state->frequency = frequency; +- +- return ret; +-} +- +-static int ix2505v_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct ix2505v_state *state = fe->tuner_priv; +- +- *frequency = state->frequency; +- +- return 0; +-} +- +-static struct dvb_tuner_ops ix2505v_tuner_ops = { +- .info = { +- .name = "Sharp IX2505V (B0017)", +- .frequency_min = 950000, +- .frequency_max = 2175000 +- }, +- .release = ix2505v_release, +- .set_params = ix2505v_set_params, +- .get_frequency = ix2505v_get_frequency, +-}; +- +-struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, +- const struct ix2505v_config *config, +- struct i2c_adapter *i2c) +-{ +- struct ix2505v_state *state = NULL; +- int ret; +- +- if (NULL == config) { +- deb_i2c("%s: no config ", __func__); +- goto error; +- } +- +- state = kzalloc(sizeof(struct ix2505v_state), GFP_KERNEL); +- if (NULL == state) +- return NULL; +- +- state->config = config; +- state->i2c = i2c; +- +- if (state->config->tuner_write_only) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = ix2505v_read_status_reg(state); +- +- if (ret & 0x80) { +- deb_i2c("%s: No IX2505V found\n", __func__); +- goto error; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- fe->tuner_priv = state; +- +- memcpy(&fe->ops.tuner_ops, &ix2505v_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- deb_i2c("%s: initialization (%s addr=0x%02x) ok\n", +- __func__, fe->ops.tuner_ops.info.name, config->tuner_address); +- +- return fe; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(ix2505v_attach); +- +-module_param_named(debug, ix2505v_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +-MODULE_DESCRIPTION("DVB IX2505V tuner driver"); +-MODULE_AUTHOR("Malcolm Priestley"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/ix2505v.h b/drivers/media/dvb/frontends/ix2505v.h +deleted file mode 100644 +index 67e89d6..0000000 +--- a/drivers/media/dvb/frontends/ix2505v.h ++++ /dev/null +@@ -1,64 +0,0 @@ +-/** +- * Driver for Sharp IX2505V (marked B0017) DVB-S silicon tuner +- * +- * Copyright (C) 2010 Malcolm Priestley +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef DVB_IX2505V_H +-#define DVB_IX2505V_H +- +-#include +-#include "dvb_frontend.h" +- +-/** +- * Attach a ix2505v tuner to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param config ix2505v_config structure +- * @return FE pointer on success, NULL on failure. +- */ +- +-struct ix2505v_config { +- u8 tuner_address; +- +- /*Baseband AMP gain control 0/1=0dB(default) 2=-2bB 3=-4dB */ +- u8 tuner_gain; +- +- /*Charge pump output +/- 0=120 1=260 2=555 3=1200(default) */ +- u8 tuner_chargepump; +- +- /* delay after tune */ +- int min_delay_ms; +- +- /* disables reads*/ +- u8 tuner_write_only; +- +-}; +- +-#if defined(CONFIG_DVB_IX2505V) || \ +- (defined(CONFIG_DVB_IX2505V_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, +- const struct ix2505v_config *config, struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, +- const struct ix2505v_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* DVB_IX2505V_H */ +diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c +deleted file mode 100644 +index 36fcf55..0000000 +--- a/drivers/media/dvb/frontends/l64781.c ++++ /dev/null +@@ -1,609 +0,0 @@ +-/* +- driver for LSI L64781 COFDM demodulator +- +- Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH +- Marko Kohtala +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "l64781.h" +- +- +-struct l64781_state { +- struct i2c_adapter* i2c; +- const struct l64781_config* config; +- struct dvb_frontend frontend; +- +- /* private demodulator data */ +- unsigned int first:1; +-}; +- +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "l64781: " args); \ +- } while (0) +- +-static int debug; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +- +-static int l64781_writereg (struct l64781_state* state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf [] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- +- if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) +- dprintk ("%s: write_reg error (reg == %02x) = %02x!\n", +- __func__, reg, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static int l64781_readreg (struct l64781_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) return ret; +- +- return b1[0]; +-} +- +-static void apply_tps (struct l64781_state* state) +-{ +- l64781_writereg (state, 0x2a, 0x00); +- l64781_writereg (state, 0x2a, 0x01); +- +- /* This here is a little bit questionable because it enables +- the automatic update of TPS registers. I think we'd need to +- handle the IRQ from FE to update some other registers as +- well, or at least implement some magic to tuning to correct +- to the TPS received from transmission. */ +- l64781_writereg (state, 0x2a, 0x02); +-} +- +- +-static void reset_afc (struct l64781_state* state) +-{ +- /* Set AFC stall for the AFC_INIT_FRQ setting, TIM_STALL for +- timing offset */ +- l64781_writereg (state, 0x07, 0x9e); /* stall AFC */ +- l64781_writereg (state, 0x08, 0); /* AFC INIT FREQ */ +- l64781_writereg (state, 0x09, 0); +- l64781_writereg (state, 0x0a, 0); +- l64781_writereg (state, 0x07, 0x8e); +- l64781_writereg (state, 0x0e, 0); /* AGC gain to zero in beginning */ +- l64781_writereg (state, 0x11, 0x80); /* stall TIM */ +- l64781_writereg (state, 0x10, 0); /* TIM_OFFSET_LSB */ +- l64781_writereg (state, 0x12, 0); +- l64781_writereg (state, 0x13, 0); +- l64781_writereg (state, 0x11, 0x00); +-} +- +-static int reset_and_configure (struct l64781_state* state) +-{ +- u8 buf [] = { 0x06 }; +- struct i2c_msg msg = { .addr = 0x00, .flags = 0, .buf = buf, .len = 1 }; +- // NOTE: this is correct in writing to address 0x00 +- +- return (i2c_transfer(state->i2c, &msg, 1) == 1) ? 0 : -ENODEV; +-} +- +-static int apply_frontend_param(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct l64781_state* state = fe->demodulator_priv; +- /* The coderates for FEC_NONE, FEC_4_5 and FEC_FEC_6_7 are arbitrary */ +- static const u8 fec_tab[] = { 7, 0, 1, 2, 9, 3, 10, 4 }; +- /* QPSK, QAM_16, QAM_64 */ +- static const u8 qam_tab [] = { 2, 4, 0, 6 }; +- static const u8 guard_tab [] = { 1, 2, 4, 8 }; +- /* The Grundig 29504-401.04 Tuner comes with 18.432MHz crystal. */ +- static const u32 ppm = 8000; +- u32 ddfs_offset_fixed; +-/* u32 ddfs_offset_variable = 0x6000-((1000000UL+ppm)/ */ +-/* bw_tab[p->bandWidth]<<10)/15625; */ +- u32 init_freq; +- u32 spi_bias; +- u8 val0x04; +- u8 val0x05; +- u8 val0x06; +- int bw; +- +- switch (p->bandwidth_hz) { +- case 8000000: +- bw = 8; +- break; +- case 7000000: +- bw = 7; +- break; +- case 6000000: +- bw = 6; +- break; +- default: +- return -EINVAL; +- } +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- if (p->inversion != INVERSION_ON && +- p->inversion != INVERSION_OFF) +- return -EINVAL; +- +- if (p->code_rate_HP != FEC_1_2 && p->code_rate_HP != FEC_2_3 && +- p->code_rate_HP != FEC_3_4 && p->code_rate_HP != FEC_5_6 && +- p->code_rate_HP != FEC_7_8) +- return -EINVAL; +- +- if (p->hierarchy != HIERARCHY_NONE && +- (p->code_rate_LP != FEC_1_2 && p->code_rate_LP != FEC_2_3 && +- p->code_rate_LP != FEC_3_4 && p->code_rate_LP != FEC_5_6 && +- p->code_rate_LP != FEC_7_8)) +- return -EINVAL; +- +- if (p->modulation != QPSK && p->modulation != QAM_16 && +- p->modulation != QAM_64) +- return -EINVAL; +- +- if (p->transmission_mode != TRANSMISSION_MODE_2K && +- p->transmission_mode != TRANSMISSION_MODE_8K) +- return -EINVAL; +- +- if (p->guard_interval < GUARD_INTERVAL_1_32 || +- p->guard_interval > GUARD_INTERVAL_1_4) +- return -EINVAL; +- +- if (p->hierarchy < HIERARCHY_NONE || +- p->hierarchy > HIERARCHY_4) +- return -EINVAL; +- +- ddfs_offset_fixed = 0x4000-(ppm<<16)/bw/1000000; +- +- /* This works up to 20000 ppm, it overflows if too large ppm! */ +- init_freq = (((8UL<<25) + (8UL<<19) / 25*ppm / (15625/25)) / +- bw & 0xFFFFFF); +- +- /* SPI bias calculation is slightly modified to fit in 32bit */ +- /* will work for high ppm only... */ +- spi_bias = 378 * (1 << 10); +- spi_bias *= 16; +- spi_bias *= bw; +- spi_bias *= qam_tab[p->modulation]; +- spi_bias /= p->code_rate_HP + 1; +- spi_bias /= (guard_tab[p->guard_interval] + 32); +- spi_bias *= 1000; +- spi_bias /= 1000 + ppm/1000; +- spi_bias *= p->code_rate_HP; +- +- val0x04 = (p->transmission_mode << 2) | p->guard_interval; +- val0x05 = fec_tab[p->code_rate_HP]; +- +- if (p->hierarchy != HIERARCHY_NONE) +- val0x05 |= (p->code_rate_LP - FEC_1_2) << 3; +- +- val0x06 = (p->hierarchy << 2) | p->modulation; +- +- l64781_writereg (state, 0x04, val0x04); +- l64781_writereg (state, 0x05, val0x05); +- l64781_writereg (state, 0x06, val0x06); +- +- reset_afc (state); +- +- /* Technical manual section 2.6.1, TIM_IIR_GAIN optimal values */ +- l64781_writereg (state, 0x15, +- p->transmission_mode == TRANSMISSION_MODE_2K ? 1 : 3); +- l64781_writereg (state, 0x16, init_freq & 0xff); +- l64781_writereg (state, 0x17, (init_freq >> 8) & 0xff); +- l64781_writereg (state, 0x18, (init_freq >> 16) & 0xff); +- +- l64781_writereg (state, 0x1b, spi_bias & 0xff); +- l64781_writereg (state, 0x1c, (spi_bias >> 8) & 0xff); +- l64781_writereg (state, 0x1d, ((spi_bias >> 16) & 0x7f) | +- (p->inversion == INVERSION_ON ? 0x80 : 0x00)); +- +- l64781_writereg (state, 0x22, ddfs_offset_fixed & 0xff); +- l64781_writereg (state, 0x23, (ddfs_offset_fixed >> 8) & 0x3f); +- +- l64781_readreg (state, 0x00); /* clear interrupt registers... */ +- l64781_readreg (state, 0x01); /* dto. */ +- +- apply_tps (state); +- +- return 0; +-} +- +-static int get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct l64781_state* state = fe->demodulator_priv; +- int tmp; +- +- +- tmp = l64781_readreg(state, 0x04); +- switch(tmp & 3) { +- case 0: +- p->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- p->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- p->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- p->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- switch((tmp >> 2) & 3) { +- case 0: +- p->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- p->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- default: +- printk(KERN_WARNING "Unexpected value for transmission_mode\n"); +- } +- +- tmp = l64781_readreg(state, 0x05); +- switch(tmp & 7) { +- case 0: +- p->code_rate_HP = FEC_1_2; +- break; +- case 1: +- p->code_rate_HP = FEC_2_3; +- break; +- case 2: +- p->code_rate_HP = FEC_3_4; +- break; +- case 3: +- p->code_rate_HP = FEC_5_6; +- break; +- case 4: +- p->code_rate_HP = FEC_7_8; +- break; +- default: +- printk("Unexpected value for code_rate_HP\n"); +- } +- switch((tmp >> 3) & 7) { +- case 0: +- p->code_rate_LP = FEC_1_2; +- break; +- case 1: +- p->code_rate_LP = FEC_2_3; +- break; +- case 2: +- p->code_rate_LP = FEC_3_4; +- break; +- case 3: +- p->code_rate_LP = FEC_5_6; +- break; +- case 4: +- p->code_rate_LP = FEC_7_8; +- break; +- default: +- printk("Unexpected value for code_rate_LP\n"); +- } +- +- tmp = l64781_readreg(state, 0x06); +- switch(tmp & 3) { +- case 0: +- p->modulation = QPSK; +- break; +- case 1: +- p->modulation = QAM_16; +- break; +- case 2: +- p->modulation = QAM_64; +- break; +- default: +- printk(KERN_WARNING "Unexpected value for modulation\n"); +- } +- switch((tmp >> 2) & 7) { +- case 0: +- p->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- p->hierarchy = HIERARCHY_1; +- break; +- case 2: +- p->hierarchy = HIERARCHY_2; +- break; +- case 3: +- p->hierarchy = HIERARCHY_4; +- break; +- default: +- printk("Unexpected value for hierarchy\n"); +- } +- +- +- tmp = l64781_readreg (state, 0x1d); +- p->inversion = (tmp & 0x80) ? INVERSION_ON : INVERSION_OFF; +- +- tmp = (int) (l64781_readreg (state, 0x08) | +- (l64781_readreg (state, 0x09) << 8) | +- (l64781_readreg (state, 0x0a) << 16)); +- p->frequency += tmp; +- +- return 0; +-} +- +-static int l64781_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- int sync = l64781_readreg (state, 0x32); +- int gain = l64781_readreg (state, 0x0e); +- +- l64781_readreg (state, 0x00); /* clear interrupt registers... */ +- l64781_readreg (state, 0x01); /* dto. */ +- +- *status = 0; +- +- if (gain > 5) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 0x02) /* VCXO locked, this criteria should be ok */ +- *status |= FE_HAS_CARRIER; +- +- if (sync & 0x20) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 0x40) +- *status |= FE_HAS_SYNC; +- +- if (sync == 0x7f) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int l64781_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- /* XXX FIXME: set up counting period (reg 0x26...0x28) +- */ +- *ber = l64781_readreg (state, 0x39) +- | (l64781_readreg (state, 0x3a) << 8); +- +- return 0; +-} +- +-static int l64781_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- u8 gain = l64781_readreg (state, 0x0e); +- *signal_strength = (gain << 8) | gain; +- +- return 0; +-} +- +-static int l64781_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- u8 avg_quality = 0xff - l64781_readreg (state, 0x33); +- *snr = (avg_quality << 8) | avg_quality; /* not exact, but...*/ +- +- return 0; +-} +- +-static int l64781_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- *ucblocks = l64781_readreg (state, 0x37) +- | (l64781_readreg (state, 0x38) << 8); +- +- return 0; +-} +- +-static int l64781_sleep(struct dvb_frontend* fe) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- /* Power down */ +- return l64781_writereg (state, 0x3e, 0x5a); +-} +- +-static int l64781_init(struct dvb_frontend* fe) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- +- reset_and_configure (state); +- +- /* Power up */ +- l64781_writereg (state, 0x3e, 0xa5); +- +- /* Reset hard */ +- l64781_writereg (state, 0x2a, 0x04); +- l64781_writereg (state, 0x2a, 0x00); +- +- /* Set tuner specific things */ +- /* AFC_POL, set also in reset_afc */ +- l64781_writereg (state, 0x07, 0x8e); +- +- /* Use internal ADC */ +- l64781_writereg (state, 0x0b, 0x81); +- +- /* AGC loop gain, and polarity is positive */ +- l64781_writereg (state, 0x0c, 0x84); +- +- /* Internal ADC outputs two's complement */ +- l64781_writereg (state, 0x0d, 0x8c); +- +- /* With ppm=8000, it seems the DTR_SENSITIVITY will result in +- value of 2 with all possible bandwidths and guard +- intervals, which is the initial value anyway. */ +- /*l64781_writereg (state, 0x19, 0x92);*/ +- +- /* Everything is two's complement, soft bit and CSI_OUT too */ +- l64781_writereg (state, 0x1e, 0x09); +- +- /* delay a bit after first init attempt */ +- if (state->first) { +- state->first = 0; +- msleep(200); +- } +- +- return 0; +-} +- +-static int l64781_get_tune_settings(struct dvb_frontend* fe, +- struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 4000; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void l64781_release(struct dvb_frontend* fe) +-{ +- struct l64781_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops l64781_ops; +- +-struct dvb_frontend* l64781_attach(const struct l64781_config* config, +- struct i2c_adapter* i2c) +-{ +- struct l64781_state* state = NULL; +- int reg0x3e = -1; +- u8 b0 [] = { 0x1a }; +- u8 b1 [] = { 0x00 }; +- struct i2c_msg msg [] = { { .addr = config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->first = 1; +- +- /** +- * the L64781 won't show up before we send the reset_and_configure() +- * broadcast. If nothing responds there is no L64781 on the bus... +- */ +- if (reset_and_configure(state) < 0) { +- dprintk("No response to reset and configure broadcast...\n"); +- goto error; +- } +- +- /* The chip always responds to reads */ +- if (i2c_transfer(state->i2c, msg, 2) != 2) { +- dprintk("No response to read on I2C bus\n"); +- goto error; +- } +- +- /* Save current register contents for bailout */ +- reg0x3e = l64781_readreg(state, 0x3e); +- +- /* Reading the POWER_DOWN register always returns 0 */ +- if (reg0x3e != 0) { +- dprintk("Device doesn't look like L64781\n"); +- goto error; +- } +- +- /* Turn the chip off */ +- l64781_writereg (state, 0x3e, 0x5a); +- +- /* Responds to all reads with 0 */ +- if (l64781_readreg(state, 0x1a) != 0) { +- dprintk("Read 1 returned unexpcted value\n"); +- goto error; +- } +- +- /* Turn the chip on */ +- l64781_writereg (state, 0x3e, 0xa5); +- +- /* Responds with register default value */ +- if (l64781_readreg(state, 0x1a) != 0xa1) { +- dprintk("Read 2 returned unexpcted value\n"); +- goto error; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &l64781_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- if (reg0x3e >= 0) +- l64781_writereg (state, 0x3e, reg0x3e); /* restore reg 0x3e */ +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops l64781_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "LSI L64781 DVB-T", +- /* .frequency_min = ???,*/ +- /* .frequency_max = ???,*/ +- .frequency_stepsize = 166666, +- /* .frequency_tolerance = ???,*/ +- /* .symbol_rate_tolerance = ???,*/ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_MUTE_TS +- }, +- +- .release = l64781_release, +- +- .init = l64781_init, +- .sleep = l64781_sleep, +- +- .set_frontend = apply_frontend_param, +- .get_frontend = get_frontend, +- .get_tune_settings = l64781_get_tune_settings, +- +- .read_status = l64781_read_status, +- .read_ber = l64781_read_ber, +- .read_signal_strength = l64781_read_signal_strength, +- .read_snr = l64781_read_snr, +- .read_ucblocks = l64781_read_ucblocks, +-}; +- +-MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(l64781_attach); +diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h +deleted file mode 100644 +index 1305a9e..0000000 +--- a/drivers/media/dvb/frontends/l64781.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- driver for LSI L64781 COFDM demodulator +- +- Copyright (C) 2001 Holger Waechtler for Convergence Integrated Media GmbH +- Marko Kohtala +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef L64781_H +-#define L64781_H +- +-#include +- +-struct l64781_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-#if defined(CONFIG_DVB_L64781) || (defined(CONFIG_DVB_L64781_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* l64781_attach(const struct l64781_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_L64781 +- +-#endif // L64781_H +diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c +deleted file mode 100644 +index 1d2c473..0000000 +--- a/drivers/media/dvb/frontends/lgdt3305.c ++++ /dev/null +@@ -1,1222 +0,0 @@ +-/* +- * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM +- * +- * Copyright (C) 2008, 2009, 2010 Michael Krufky +- * +- * LGDT3304 support by Jarod Wilson +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +-#include +-#include "dvb_math.h" +-#include "lgdt3305.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); +- +-#define DBG_INFO 1 +-#define DBG_REG 2 +- +-#define lg_printk(kern, fmt, arg...) \ +- printk(kern "%s: " fmt, __func__, ##arg) +- +-#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg) +-#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) +-#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) +-#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ +- lg_printk(KERN_DEBUG, fmt, ##arg) +-#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ +- lg_printk(KERN_DEBUG, fmt, ##arg) +- +-#define lg_fail(ret) \ +-({ \ +- int __ret; \ +- __ret = (ret < 0); \ +- if (__ret) \ +- lg_err("error %d on line %d\n", ret, __LINE__); \ +- __ret; \ +-}) +- +-struct lgdt3305_state { +- struct i2c_adapter *i2c_adap; +- const struct lgdt3305_config *cfg; +- +- struct dvb_frontend frontend; +- +- fe_modulation_t current_modulation; +- u32 current_frequency; +- u32 snr; +-}; +- +-/* ------------------------------------------------------------------------ */ +- +-/* FIXME: verify & document the LGDT3304 registers */ +- +-#define LGDT3305_GEN_CTRL_1 0x0000 +-#define LGDT3305_GEN_CTRL_2 0x0001 +-#define LGDT3305_GEN_CTRL_3 0x0002 +-#define LGDT3305_GEN_STATUS 0x0003 +-#define LGDT3305_GEN_CONTROL 0x0007 +-#define LGDT3305_GEN_CTRL_4 0x000a +-#define LGDT3305_DGTL_AGC_REF_1 0x0012 +-#define LGDT3305_DGTL_AGC_REF_2 0x0013 +-#define LGDT3305_CR_CTR_FREQ_1 0x0106 +-#define LGDT3305_CR_CTR_FREQ_2 0x0107 +-#define LGDT3305_CR_CTR_FREQ_3 0x0108 +-#define LGDT3305_CR_CTR_FREQ_4 0x0109 +-#define LGDT3305_CR_MSE_1 0x011b +-#define LGDT3305_CR_MSE_2 0x011c +-#define LGDT3305_CR_LOCK_STATUS 0x011d +-#define LGDT3305_CR_CTRL_7 0x0126 +-#define LGDT3305_AGC_POWER_REF_1 0x0300 +-#define LGDT3305_AGC_POWER_REF_2 0x0301 +-#define LGDT3305_AGC_DELAY_PT_1 0x0302 +-#define LGDT3305_AGC_DELAY_PT_2 0x0303 +-#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306 +-#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307 +-#define LGDT3305_IFBW_1 0x0308 +-#define LGDT3305_IFBW_2 0x0309 +-#define LGDT3305_AGC_CTRL_1 0x030c +-#define LGDT3305_AGC_CTRL_4 0x0314 +-#define LGDT3305_EQ_MSE_1 0x0413 +-#define LGDT3305_EQ_MSE_2 0x0414 +-#define LGDT3305_EQ_MSE_3 0x0415 +-#define LGDT3305_PT_MSE_1 0x0417 +-#define LGDT3305_PT_MSE_2 0x0418 +-#define LGDT3305_PT_MSE_3 0x0419 +-#define LGDT3305_FEC_BLOCK_CTRL 0x0504 +-#define LGDT3305_FEC_LOCK_STATUS 0x050a +-#define LGDT3305_FEC_PKT_ERR_1 0x050c +-#define LGDT3305_FEC_PKT_ERR_2 0x050d +-#define LGDT3305_TP_CTRL_1 0x050e +-#define LGDT3305_BERT_PERIOD 0x0801 +-#define LGDT3305_BERT_ERROR_COUNT_1 0x080a +-#define LGDT3305_BERT_ERROR_COUNT_2 0x080b +-#define LGDT3305_BERT_ERROR_COUNT_3 0x080c +-#define LGDT3305_BERT_ERROR_COUNT_4 0x080d +- +-static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val) +-{ +- int ret; +- u8 buf[] = { reg >> 8, reg & 0xff, val }; +- struct i2c_msg msg = { +- .addr = state->cfg->i2c_addr, .flags = 0, +- .buf = buf, .len = 3, +- }; +- +- lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); +- +- ret = i2c_transfer(state->i2c_adap, &msg, 1); +- +- if (ret != 1) { +- lg_err("error (addr %02x %02x <- %02x, err = %i)\n", +- msg.buf[0], msg.buf[1], msg.buf[2], ret); +- if (ret < 0) +- return ret; +- else +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val) +-{ +- int ret; +- u8 reg_buf[] = { reg >> 8, reg & 0xff }; +- struct i2c_msg msg[] = { +- { .addr = state->cfg->i2c_addr, +- .flags = 0, .buf = reg_buf, .len = 2 }, +- { .addr = state->cfg->i2c_addr, +- .flags = I2C_M_RD, .buf = val, .len = 1 }, +- }; +- +- lg_reg("reg: 0x%04x\n", reg); +- +- ret = i2c_transfer(state->i2c_adap, msg, 2); +- +- if (ret != 2) { +- lg_err("error (addr %02x reg %04x error (ret == %i)\n", +- state->cfg->i2c_addr, reg, ret); +- if (ret < 0) +- return ret; +- else +- return -EREMOTEIO; +- } +- return 0; +-} +- +-#define read_reg(state, reg) \ +-({ \ +- u8 __val; \ +- int ret = lgdt3305_read_reg(state, reg, &__val); \ +- if (lg_fail(ret)) \ +- __val = 0; \ +- __val; \ +-}) +- +-static int lgdt3305_set_reg_bit(struct lgdt3305_state *state, +- u16 reg, int bit, int onoff) +-{ +- u8 val; +- int ret; +- +- lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); +- +- ret = lgdt3305_read_reg(state, reg, &val); +- if (lg_fail(ret)) +- goto fail; +- +- val &= ~(1 << bit); +- val |= (onoff & 1) << bit; +- +- ret = lgdt3305_write_reg(state, reg, val); +-fail: +- return ret; +-} +- +-struct lgdt3305_reg { +- u16 reg; +- u8 val; +-}; +- +-static int lgdt3305_write_regs(struct lgdt3305_state *state, +- struct lgdt3305_reg *regs, int len) +-{ +- int i, ret; +- +- lg_reg("writing %d registers...\n", len); +- +- for (i = 0; i < len - 1; i++) { +- ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val); +- if (lg_fail(ret)) +- return ret; +- } +- return 0; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_soft_reset(struct lgdt3305_state *state) +-{ +- int ret; +- +- lg_dbg("\n"); +- +- ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0); +- if (lg_fail(ret)) +- goto fail; +- +- msleep(20); +- ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1); +-fail: +- return ret; +-} +- +-static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, +- enum lgdt3305_mpeg_mode mode) +-{ +- lg_dbg("(%d)\n", mode); +- return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); +-} +- +-static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state, +- enum lgdt3305_tp_clock_edge edge, +- enum lgdt3305_tp_valid_polarity valid) +-{ +- u8 val; +- int ret; +- +- lg_dbg("edge = %d, valid = %d\n", edge, valid); +- +- ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val); +- if (lg_fail(ret)) +- goto fail; +- +- val &= ~0x09; +- +- if (edge) +- val |= 0x08; +- if (valid) +- val |= 0x01; +- +- ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_soft_reset(state); +-fail: +- return ret; +-} +- +-static int lgdt3305_set_modulation(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- u8 opermode; +- int ret; +- +- lg_dbg("\n"); +- +- ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode); +- if (lg_fail(ret)) +- goto fail; +- +- opermode &= ~0x03; +- +- switch (p->modulation) { +- case VSB_8: +- opermode |= 0x03; +- break; +- case QAM_64: +- opermode |= 0x00; +- break; +- case QAM_256: +- opermode |= 0x01; +- break; +- default: +- return -EINVAL; +- } +- ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode); +-fail: +- return ret; +-} +- +-static int lgdt3305_set_filter_extension(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- int val; +- +- switch (p->modulation) { +- case VSB_8: +- val = 0; +- break; +- case QAM_64: +- case QAM_256: +- val = 1; +- break; +- default: +- return -EINVAL; +- } +- lg_dbg("val = %d\n", val); +- +- return lgdt3305_set_reg_bit(state, 0x043f, 2, val); +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- u16 agc_ref; +- +- switch (p->modulation) { +- case VSB_8: +- agc_ref = 0x32c4; +- break; +- case QAM_64: +- agc_ref = 0x2a00; +- break; +- case QAM_256: +- agc_ref = 0x2a80; +- break; +- default: +- return -EINVAL; +- } +- +- lg_dbg("agc ref: 0x%04x\n", agc_ref); +- +- lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8); +- lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff); +- +- return 0; +-} +- +-static int lgdt3305_rfagc_loop(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- u16 ifbw, rfbw, agcdelay; +- +- switch (p->modulation) { +- case VSB_8: +- agcdelay = 0x04c0; +- rfbw = 0x8000; +- ifbw = 0x8000; +- break; +- case QAM_64: +- case QAM_256: +- agcdelay = 0x046b; +- rfbw = 0x8889; +- /* FIXME: investigate optimal ifbw & rfbw values for the +- * DT3304 and re-write this switch..case block */ +- if (state->cfg->demod_chip == LGDT3304) +- ifbw = 0x6666; +- else /* (state->cfg->demod_chip == LGDT3305) */ +- ifbw = 0x8888; +- break; +- default: +- return -EINVAL; +- } +- +- if (state->cfg->rf_agc_loop) { +- lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw); +- +- /* rf agc loop filter bandwidth */ +- lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1, +- agcdelay >> 8); +- lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2, +- agcdelay & 0xff); +- +- lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1, +- rfbw >> 8); +- lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2, +- rfbw & 0xff); +- } else { +- lg_dbg("ifbw: 0x%04x\n", ifbw); +- +- /* if agc loop filter bandwidth */ +- lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8); +- lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff); +- } +- +- return 0; +-} +- +-static int lgdt3305_agc_setup(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- int lockdten, acqen; +- +- switch (p->modulation) { +- case VSB_8: +- lockdten = 0; +- acqen = 0; +- break; +- case QAM_64: +- case QAM_256: +- lockdten = 1; +- acqen = 1; +- break; +- default: +- return -EINVAL; +- } +- +- lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen); +- +- /* control agc function */ +- switch (state->cfg->demod_chip) { +- case LGDT3304: +- lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1); +- lgdt3305_set_reg_bit(state, 0x030e, 2, acqen); +- break; +- case LGDT3305: +- lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1); +- lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen); +- break; +- default: +- return -EINVAL; +- } +- +- return lgdt3305_rfagc_loop(state, p); +-} +- +-static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- u16 usref = 0; +- +- switch (p->modulation) { +- case VSB_8: +- if (state->cfg->usref_8vsb) +- usref = state->cfg->usref_8vsb; +- break; +- case QAM_64: +- if (state->cfg->usref_qam64) +- usref = state->cfg->usref_qam64; +- break; +- case QAM_256: +- if (state->cfg->usref_qam256) +- usref = state->cfg->usref_qam256; +- break; +- default: +- return -EINVAL; +- } +- +- if (usref) { +- lg_dbg("set manual mode: 0x%04x\n", usref); +- +- lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1); +- +- lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1, +- 0xff & (usref >> 8)); +- lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2, +- 0xff & (usref >> 0)); +- } +- return 0; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_spectral_inversion(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p, +- int inversion) +-{ +- int ret; +- +- lg_dbg("(%d)\n", inversion); +- +- switch (p->modulation) { +- case VSB_8: +- ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7, +- inversion ? 0xf9 : 0x79); +- break; +- case QAM_64: +- case QAM_256: +- ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL, +- inversion ? 0xfd : 0xff); +- break; +- default: +- ret = -EINVAL; +- } +- return ret; +-} +- +-static int lgdt3305_set_if(struct lgdt3305_state *state, +- struct dtv_frontend_properties *p) +-{ +- u16 if_freq_khz; +- u8 nco1, nco2, nco3, nco4; +- u64 nco; +- +- switch (p->modulation) { +- case VSB_8: +- if_freq_khz = state->cfg->vsb_if_khz; +- break; +- case QAM_64: +- case QAM_256: +- if_freq_khz = state->cfg->qam_if_khz; +- break; +- default: +- return -EINVAL; +- } +- +- nco = if_freq_khz / 10; +- +- switch (p->modulation) { +- case VSB_8: +- nco <<= 24; +- do_div(nco, 625); +- break; +- case QAM_64: +- case QAM_256: +- nco <<= 28; +- do_div(nco, 625); +- break; +- default: +- return -EINVAL; +- } +- +- nco1 = (nco >> 24) & 0x3f; +- nco1 |= 0x40; +- nco2 = (nco >> 16) & 0xff; +- nco3 = (nco >> 8) & 0xff; +- nco4 = nco & 0xff; +- +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4); +- +- lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n", +- if_freq_khz, nco1, nco2, nco3, nco4); +- +- return 0; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- +- if (state->cfg->deny_i2c_rptr) +- return 0; +- +- lg_dbg("(%d)\n", enable); +- +- return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5, +- enable ? 0 : 1); +-} +- +-static int lgdt3305_sleep(struct dvb_frontend *fe) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- u8 gen_ctrl_3, gen_ctrl_4; +- +- lg_dbg("\n"); +- +- gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3); +- gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4); +- +- /* hold in software reset while sleeping */ +- gen_ctrl_3 &= ~0x01; +- /* tristate the IF-AGC pin */ +- gen_ctrl_3 |= 0x02; +- /* tristate the RF-AGC pin */ +- gen_ctrl_3 |= 0x04; +- +- /* disable vsb/qam module */ +- gen_ctrl_4 &= ~0x01; +- /* disable adc module */ +- gen_ctrl_4 &= ~0x02; +- +- lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3); +- lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4); +- +- return 0; +-} +- +-static int lgdt3305_init(struct dvb_frontend *fe) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- int ret; +- +- static struct lgdt3305_reg lgdt3304_init_data[] = { +- { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, +- { .reg = 0x000d, .val = 0x02, }, +- { .reg = 0x000e, .val = 0x02, }, +- { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, +- { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, +- { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, }, +- { .reg = 0x0112, .val = 0x17, }, +- { .reg = 0x0113, .val = 0x15, }, +- { .reg = 0x0114, .val = 0x18, }, +- { .reg = 0x0115, .val = 0xff, }, +- { .reg = 0x0116, .val = 0x3c, }, +- { .reg = 0x0214, .val = 0x67, }, +- { .reg = 0x0424, .val = 0x8d, }, +- { .reg = 0x0427, .val = 0x12, }, +- { .reg = 0x0428, .val = 0x4f, }, +- { .reg = LGDT3305_IFBW_1, .val = 0x80, }, +- { .reg = LGDT3305_IFBW_2, .val = 0x00, }, +- { .reg = 0x030a, .val = 0x08, }, +- { .reg = 0x030b, .val = 0x9b, }, +- { .reg = 0x030d, .val = 0x00, }, +- { .reg = 0x030e, .val = 0x1c, }, +- { .reg = 0x0314, .val = 0xe1, }, +- { .reg = 0x000d, .val = 0x82, }, +- { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, +- { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, }, +- }; +- +- static struct lgdt3305_reg lgdt3305_init_data[] = { +- { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, }, +- { .reg = LGDT3305_GEN_CTRL_2, .val = 0xb0, }, +- { .reg = LGDT3305_GEN_CTRL_3, .val = 0x01, }, +- { .reg = LGDT3305_GEN_CONTROL, .val = 0x6f, }, +- { .reg = LGDT3305_GEN_CTRL_4, .val = 0x03, }, +- { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, }, +- { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, }, +- { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, }, +- { .reg = LGDT3305_CR_CTRL_7, .val = 0x79, }, +- { .reg = LGDT3305_AGC_POWER_REF_1, .val = 0x32, }, +- { .reg = LGDT3305_AGC_POWER_REF_2, .val = 0xc4, }, +- { .reg = LGDT3305_AGC_DELAY_PT_1, .val = 0x0d, }, +- { .reg = LGDT3305_AGC_DELAY_PT_2, .val = 0x30, }, +- { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1, .val = 0x80, }, +- { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2, .val = 0x00, }, +- { .reg = LGDT3305_IFBW_1, .val = 0x80, }, +- { .reg = LGDT3305_IFBW_2, .val = 0x00, }, +- { .reg = LGDT3305_AGC_CTRL_1, .val = 0x30, }, +- { .reg = LGDT3305_AGC_CTRL_4, .val = 0x61, }, +- { .reg = LGDT3305_FEC_BLOCK_CTRL, .val = 0xff, }, +- { .reg = LGDT3305_TP_CTRL_1, .val = 0x1b, }, +- }; +- +- lg_dbg("\n"); +- +- switch (state->cfg->demod_chip) { +- case LGDT3304: +- ret = lgdt3305_write_regs(state, lgdt3304_init_data, +- ARRAY_SIZE(lgdt3304_init_data)); +- break; +- case LGDT3305: +- ret = lgdt3305_write_regs(state, lgdt3305_init_data, +- ARRAY_SIZE(lgdt3305_init_data)); +- break; +- default: +- ret = -EINVAL; +- } +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_soft_reset(state); +-fail: +- return ret; +-} +- +-static int lgdt3304_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgdt3305_state *state = fe->demodulator_priv; +- int ret; +- +- lg_dbg("(%d, %d)\n", p->frequency, p->modulation); +- +- if (fe->ops.tuner_ops.set_params) { +- ret = fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- if (lg_fail(ret)) +- goto fail; +- state->current_frequency = p->frequency; +- } +- +- ret = lgdt3305_set_modulation(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_passband_digital_agc(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_agc_setup(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */ +- switch (p->modulation) { +- case VSB_8: +- lgdt3305_write_reg(state, 0x030d, 0x00); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac); +- lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba); +- break; +- case QAM_64: +- case QAM_256: +- lgdt3305_write_reg(state, 0x030d, 0x14); +- ret = lgdt3305_set_if(state, p); +- if (lg_fail(ret)) +- goto fail; +- break; +- default: +- return -EINVAL; +- } +- +- +- ret = lgdt3305_spectral_inversion(state, p, +- state->cfg->spectral_inversion +- ? 1 : 0); +- if (lg_fail(ret)) +- goto fail; +- +- state->current_modulation = p->modulation; +- +- ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); +- if (lg_fail(ret)) +- goto fail; +- +- /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ +- ret = lgdt3305_mpeg_mode_polarity(state, +- state->cfg->tpclk_edge, +- state->cfg->tpvalid_polarity); +-fail: +- return ret; +-} +- +-static int lgdt3305_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgdt3305_state *state = fe->demodulator_priv; +- int ret; +- +- lg_dbg("(%d, %d)\n", p->frequency, p->modulation); +- +- if (fe->ops.tuner_ops.set_params) { +- ret = fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- if (lg_fail(ret)) +- goto fail; +- state->current_frequency = p->frequency; +- } +- +- ret = lgdt3305_set_modulation(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_passband_digital_agc(state, p); +- if (lg_fail(ret)) +- goto fail; +- ret = lgdt3305_set_agc_power_ref(state, p); +- if (lg_fail(ret)) +- goto fail; +- ret = lgdt3305_agc_setup(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- /* low if */ +- ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f); +- if (lg_fail(ret)) +- goto fail; +- ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_set_if(state, p); +- if (lg_fail(ret)) +- goto fail; +- ret = lgdt3305_spectral_inversion(state, p, +- state->cfg->spectral_inversion +- ? 1 : 0); +- if (lg_fail(ret)) +- goto fail; +- +- ret = lgdt3305_set_filter_extension(state, p); +- if (lg_fail(ret)) +- goto fail; +- +- state->current_modulation = p->modulation; +- +- ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode); +- if (lg_fail(ret)) +- goto fail; +- +- /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ +- ret = lgdt3305_mpeg_mode_polarity(state, +- state->cfg->tpclk_edge, +- state->cfg->tpvalid_polarity); +-fail: +- return ret; +-} +- +-static int lgdt3305_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgdt3305_state *state = fe->demodulator_priv; +- +- lg_dbg("\n"); +- +- p->modulation = state->current_modulation; +- p->frequency = state->current_frequency; +- return 0; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state, +- int *locked) +-{ +- u8 val; +- int ret; +- char *cr_lock_state = ""; +- +- *locked = 0; +- +- ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val); +- if (lg_fail(ret)) +- goto fail; +- +- switch (state->current_modulation) { +- case QAM_256: +- case QAM_64: +- if (val & (1 << 1)) +- *locked = 1; +- +- switch (val & 0x07) { +- case 0: +- cr_lock_state = "QAM UNLOCK"; +- break; +- case 4: +- cr_lock_state = "QAM 1stLock"; +- break; +- case 6: +- cr_lock_state = "QAM 2ndLock"; +- break; +- case 7: +- cr_lock_state = "QAM FinalLock"; +- break; +- default: +- cr_lock_state = "CLOCKQAM-INVALID!"; +- break; +- } +- break; +- case VSB_8: +- if (val & (1 << 7)) { +- *locked = 1; +- cr_lock_state = "CLOCKVSB"; +- } +- break; +- default: +- ret = -EINVAL; +- } +- lg_dbg("(%d) %s\n", *locked, cr_lock_state); +-fail: +- return ret; +-} +- +-static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state, +- int *locked) +-{ +- u8 val; +- int ret, mpeg_lock, fec_lock, viterbi_lock; +- +- *locked = 0; +- +- switch (state->current_modulation) { +- case QAM_256: +- case QAM_64: +- ret = lgdt3305_read_reg(state, +- LGDT3305_FEC_LOCK_STATUS, &val); +- if (lg_fail(ret)) +- goto fail; +- +- mpeg_lock = (val & (1 << 0)) ? 1 : 0; +- fec_lock = (val & (1 << 2)) ? 1 : 0; +- viterbi_lock = (val & (1 << 3)) ? 1 : 0; +- +- *locked = mpeg_lock && fec_lock && viterbi_lock; +- +- lg_dbg("(%d) %s%s%s\n", *locked, +- mpeg_lock ? "mpeg lock " : "", +- fec_lock ? "fec lock " : "", +- viterbi_lock ? "viterbi lock" : ""); +- break; +- case VSB_8: +- default: +- ret = -EINVAL; +- } +-fail: +- return ret; +-} +- +-static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- u8 val; +- int ret, signal, inlock, nofecerr, snrgood, +- cr_lock, fec_lock, sync_lock; +- +- *status = 0; +- +- ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val); +- if (lg_fail(ret)) +- goto fail; +- +- signal = (val & (1 << 4)) ? 1 : 0; +- inlock = (val & (1 << 3)) ? 0 : 1; +- sync_lock = (val & (1 << 2)) ? 1 : 0; +- nofecerr = (val & (1 << 1)) ? 1 : 0; +- snrgood = (val & (1 << 0)) ? 1 : 0; +- +- lg_dbg("%s%s%s%s%s\n", +- signal ? "SIGNALEXIST " : "", +- inlock ? "INLOCK " : "", +- sync_lock ? "SYNCLOCK " : "", +- nofecerr ? "NOFECERR " : "", +- snrgood ? "SNRGOOD " : ""); +- +- ret = lgdt3305_read_cr_lock_status(state, &cr_lock); +- if (lg_fail(ret)) +- goto fail; +- +- if (signal) +- *status |= FE_HAS_SIGNAL; +- if (cr_lock) +- *status |= FE_HAS_CARRIER; +- if (nofecerr) +- *status |= FE_HAS_VITERBI; +- if (sync_lock) +- *status |= FE_HAS_SYNC; +- +- switch (state->current_modulation) { +- case QAM_256: +- case QAM_64: +- /* signal bit is unreliable on the DT3304 in QAM mode */ +- if (((LGDT3304 == state->cfg->demod_chip)) && (cr_lock)) +- *status |= FE_HAS_SIGNAL; +- +- ret = lgdt3305_read_fec_lock_status(state, &fec_lock); +- if (lg_fail(ret)) +- goto fail; +- +- if (fec_lock) +- *status |= FE_HAS_LOCK; +- break; +- case VSB_8: +- if (inlock) +- *status |= FE_HAS_LOCK; +- break; +- default: +- ret = -EINVAL; +- } +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-/* borrowed from lgdt330x.c */ +-static u32 calculate_snr(u32 mse, u32 c) +-{ +- if (mse == 0) /* no signal */ +- return 0; +- +- mse = intlog10(mse); +- if (mse > c) { +- /* Negative SNR, which is possible, but realisticly the +- demod will lose lock before the signal gets this bad. The +- API only allows for unsigned values, so just return 0 */ +- return 0; +- } +- return 10*(c - mse); +-} +- +-static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- u32 noise; /* noise value */ +- u32 c; /* per-modulation SNR calculation constant */ +- +- switch (state->current_modulation) { +- case VSB_8: +-#ifdef USE_PTMSE +- /* Use Phase Tracker Mean-Square Error Register */ +- /* SNR for ranges from -13.11 to +44.08 */ +- noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) | +- (read_reg(state, LGDT3305_PT_MSE_2) << 8) | +- (read_reg(state, LGDT3305_PT_MSE_3) & 0xff); +- c = 73957994; /* log10(25*32^2)*2^24 */ +-#else +- /* Use Equalizer Mean-Square Error Register */ +- /* SNR for ranges from -16.12 to +44.08 */ +- noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) | +- (read_reg(state, LGDT3305_EQ_MSE_2) << 8) | +- (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff); +- c = 73957994; /* log10(25*32^2)*2^24 */ +-#endif +- break; +- case QAM_64: +- case QAM_256: +- noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) | +- (read_reg(state, LGDT3305_CR_MSE_2) & 0xff); +- +- c = (state->current_modulation == QAM_64) ? +- 97939837 : 98026066; +- /* log10(688128)*2^24 and log10(696320)*2^24 */ +- break; +- default: +- return -EINVAL; +- } +- state->snr = calculate_snr(noise, c); +- /* report SNR in dB * 10 */ +- *snr = (state->snr / ((1 << 24) / 10)); +- lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise, +- state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); +- +- return 0; +-} +- +-static int lgdt3305_read_signal_strength(struct dvb_frontend *fe, +- u16 *strength) +-{ +- /* borrowed from lgdt330x.c +- * +- * Calculate strength from SNR up to 35dB +- * Even though the SNR can go higher than 35dB, +- * there is some comfort factor in having a range of +- * strong signals that can show at 100% +- */ +- struct lgdt3305_state *state = fe->demodulator_priv; +- u16 snr; +- int ret; +- +- *strength = 0; +- +- ret = fe->ops.read_snr(fe, &snr); +- if (lg_fail(ret)) +- goto fail; +- /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ +- /* scale the range 0 - 35*2^24 into 0 - 65535 */ +- if (state->snr >= 8960 * 0x10000) +- *strength = 0xffff; +- else +- *strength = state->snr / 8960; +-fail: +- return ret; +-} +- +-/* ------------------------------------------------------------------------ */ +- +-static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- *ber = 0; +- return 0; +-} +- +-static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- +- *ucblocks = +- (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) | +- (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff); +- +- return 0; +-} +- +-static int lgdt3305_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings +- *fe_tune_settings) +-{ +- fe_tune_settings->min_delay_ms = 500; +- lg_dbg("\n"); +- return 0; +-} +- +-static void lgdt3305_release(struct dvb_frontend *fe) +-{ +- struct lgdt3305_state *state = fe->demodulator_priv; +- lg_dbg("\n"); +- kfree(state); +-} +- +-static struct dvb_frontend_ops lgdt3304_ops; +-static struct dvb_frontend_ops lgdt3305_ops; +- +-struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, +- struct i2c_adapter *i2c_adap) +-{ +- struct lgdt3305_state *state = NULL; +- int ret; +- u8 val; +- +- lg_dbg("(%d-%04x)\n", +- i2c_adap ? i2c_adapter_id(i2c_adap) : 0, +- config ? config->i2c_addr : 0); +- +- state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL); +- if (state == NULL) +- goto fail; +- +- state->cfg = config; +- state->i2c_adap = i2c_adap; +- +- switch (config->demod_chip) { +- case LGDT3304: +- memcpy(&state->frontend.ops, &lgdt3304_ops, +- sizeof(struct dvb_frontend_ops)); +- break; +- case LGDT3305: +- memcpy(&state->frontend.ops, &lgdt3305_ops, +- sizeof(struct dvb_frontend_ops)); +- break; +- default: +- goto fail; +- } +- state->frontend.demodulator_priv = state; +- +- /* verify that we're talking to a lg dt3304/5 */ +- ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val); +- if ((lg_fail(ret)) | (val == 0)) +- goto fail; +- ret = lgdt3305_write_reg(state, 0x0808, 0x80); +- if (lg_fail(ret)) +- goto fail; +- ret = lgdt3305_read_reg(state, 0x0808, &val); +- if ((lg_fail(ret)) | (val != 0x80)) +- goto fail; +- ret = lgdt3305_write_reg(state, 0x0808, 0x00); +- if (lg_fail(ret)) +- goto fail; +- +- state->current_frequency = -1; +- state->current_modulation = -1; +- +- return &state->frontend; +-fail: +- lg_warn("unable to detect %s hardware\n", +- config->demod_chip ? "LGDT3304" : "LGDT3305"); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(lgdt3305_attach); +- +-static struct dvb_frontend_ops lgdt3304_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "LG Electronics LGDT3304 VSB/QAM Frontend", +- .frequency_min = 54000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, +- .init = lgdt3305_init, +- .set_frontend = lgdt3304_set_parameters, +- .get_frontend = lgdt3305_get_frontend, +- .get_tune_settings = lgdt3305_get_tune_settings, +- .read_status = lgdt3305_read_status, +- .read_ber = lgdt3305_read_ber, +- .read_signal_strength = lgdt3305_read_signal_strength, +- .read_snr = lgdt3305_read_snr, +- .read_ucblocks = lgdt3305_read_ucblocks, +- .release = lgdt3305_release, +-}; +- +-static struct dvb_frontend_ops lgdt3305_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "LG Electronics LGDT3305 VSB/QAM Frontend", +- .frequency_min = 54000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, +- .init = lgdt3305_init, +- .sleep = lgdt3305_sleep, +- .set_frontend = lgdt3305_set_parameters, +- .get_frontend = lgdt3305_get_frontend, +- .get_tune_settings = lgdt3305_get_tune_settings, +- .read_status = lgdt3305_read_status, +- .read_ber = lgdt3305_read_ber, +- .read_signal_strength = lgdt3305_read_signal_strength, +- .read_snr = lgdt3305_read_snr, +- .read_ucblocks = lgdt3305_read_ucblocks, +- .release = lgdt3305_release, +-}; +- +-MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); +-MODULE_AUTHOR("Michael Krufky "); +-MODULE_LICENSE("GPL"); +-MODULE_VERSION("0.2"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h +deleted file mode 100644 +index 02172ec..0000000 +--- a/drivers/media/dvb/frontends/lgdt3305.h ++++ /dev/null +@@ -1,91 +0,0 @@ +-/* +- * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM +- * +- * Copyright (C) 2008, 2009, 2010 Michael Krufky +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef _LGDT3305_H_ +-#define _LGDT3305_H_ +- +-#include +-#include "dvb_frontend.h" +- +- +-enum lgdt3305_mpeg_mode { +- LGDT3305_MPEG_PARALLEL = 0, +- LGDT3305_MPEG_SERIAL = 1, +-}; +- +-enum lgdt3305_tp_clock_edge { +- LGDT3305_TPCLK_RISING_EDGE = 0, +- LGDT3305_TPCLK_FALLING_EDGE = 1, +-}; +- +-enum lgdt3305_tp_valid_polarity { +- LGDT3305_TP_VALID_LOW = 0, +- LGDT3305_TP_VALID_HIGH = 1, +-}; +- +-enum lgdt_demod_chip_type { +- LGDT3305 = 0, +- LGDT3304 = 1, +-}; +- +-struct lgdt3305_config { +- u8 i2c_addr; +- +- /* user defined IF frequency in KHz */ +- u16 qam_if_khz; +- u16 vsb_if_khz; +- +- /* AGC Power reference - defaults are used if left unset */ +- u16 usref_8vsb; /* default: 0x32c4 */ +- u16 usref_qam64; /* default: 0x5400 */ +- u16 usref_qam256; /* default: 0x2a80 */ +- +- /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ +- unsigned int deny_i2c_rptr:1; +- +- /* spectral inversion - 0:disabled 1:enabled */ +- unsigned int spectral_inversion:1; +- +- /* use RF AGC loop - 0:disabled 1:enabled */ +- unsigned int rf_agc_loop:1; +- +- enum lgdt3305_mpeg_mode mpeg_mode; +- enum lgdt3305_tp_clock_edge tpclk_edge; +- enum lgdt3305_tp_valid_polarity tpvalid_polarity; +- enum lgdt_demod_chip_type demod_chip; +-}; +- +-#if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \ +- defined(MODULE)) +-extern +-struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, +- struct i2c_adapter *i2c_adap); +-#else +-static inline +-struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, +- struct i2c_adapter *i2c_adap) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_LGDT3305 */ +- +-#endif /* _LGDT3305_H_ */ +diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c +deleted file mode 100644 +index e046622..0000000 +--- a/drivers/media/dvb/frontends/lgdt330x.c ++++ /dev/null +@@ -1,831 +0,0 @@ +-/* +- * Support for LGDT3302 and LGDT3303 - VSB/QAM +- * +- * Copyright (C) 2005 Wilson Michaels +- * +- * 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. +- * +- */ +- +-/* +- * NOTES ABOUT THIS DRIVER +- * +- * This Linux driver supports: +- * DViCO FusionHDTV 3 Gold-Q +- * DViCO FusionHDTV 3 Gold-T +- * DViCO FusionHDTV 5 Gold +- * DViCO FusionHDTV 5 Lite +- * DViCO FusionHDTV 5 USB Gold +- * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) +- * pcHDTV HD5500 +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "dvb_math.h" +-#include "lgdt330x_priv.h" +-#include "lgdt330x.h" +- +-/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ +-/* #define USE_EQMSE */ +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off)."); +-#define dprintk(args...) \ +-do { \ +-if (debug) printk(KERN_DEBUG "lgdt330x: " args); \ +-} while (0) +- +-struct lgdt330x_state +-{ +- struct i2c_adapter* i2c; +- +- /* Configuration settings */ +- const struct lgdt330x_config* config; +- +- struct dvb_frontend frontend; +- +- /* Demodulator private data */ +- fe_modulation_t current_modulation; +- u32 snr; /* Result of last SNR calculation */ +- +- /* Tuner private data */ +- u32 current_frequency; +-}; +- +-static int i2c_write_demod_bytes (struct lgdt330x_state* state, +- u8 *buf, /* data bytes to send */ +- int len /* number of bytes to send */ ) +-{ +- struct i2c_msg msg = +- { .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 }; +- int i; +- int err; +- +- for (i=0; ii2c, &msg, 1)) != 1) { +- printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __func__, msg.buf[0], msg.buf[1], err); +- if (err < 0) +- return err; +- else +- return -EREMOTEIO; +- } +- msg.buf += 2; +- } +- return 0; +-} +- +-/* +- * This routine writes the register (reg) to the demod bus +- * then reads the data returned for (len) bytes. +- */ +- +-static int i2c_read_demod_bytes(struct lgdt330x_state *state, +- enum I2C_REG reg, u8 *buf, int len) +-{ +- u8 wr [] = { reg }; +- struct i2c_msg msg [] = { +- { .addr = state->config->demod_address, +- .flags = 0, .buf = wr, .len = 1 }, +- { .addr = state->config->demod_address, +- .flags = I2C_M_RD, .buf = buf, .len = len }, +- }; +- int ret; +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) { +- printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret); +- if (ret >= 0) +- ret = -EIO; +- } else { +- ret = 0; +- } +- return ret; +-} +- +-/* Software reset */ +-static int lgdt3302_SwReset(struct lgdt330x_state* state) +-{ +- u8 ret; +- u8 reset[] = { +- IRQ_MASK, +- 0x00 /* bit 6 is active low software reset +- * bits 5-0 are 1 to mask interrupts */ +- }; +- +- ret = i2c_write_demod_bytes(state, +- reset, sizeof(reset)); +- if (ret == 0) { +- +- /* force reset high (inactive) and unmask interrupts */ +- reset[1] = 0x7f; +- ret = i2c_write_demod_bytes(state, +- reset, sizeof(reset)); +- } +- return ret; +-} +- +-static int lgdt3303_SwReset(struct lgdt330x_state* state) +-{ +- u8 ret; +- u8 reset[] = { +- 0x02, +- 0x00 /* bit 0 is active low software reset */ +- }; +- +- ret = i2c_write_demod_bytes(state, +- reset, sizeof(reset)); +- if (ret == 0) { +- +- /* force reset high (inactive) */ +- reset[1] = 0x01; +- ret = i2c_write_demod_bytes(state, +- reset, sizeof(reset)); +- } +- return ret; +-} +- +-static int lgdt330x_SwReset(struct lgdt330x_state* state) +-{ +- switch (state->config->demod_chip) { +- case LGDT3302: +- return lgdt3302_SwReset(state); +- case LGDT3303: +- return lgdt3303_SwReset(state); +- default: +- return -ENODEV; +- } +-} +- +-static int lgdt330x_init(struct dvb_frontend* fe) +-{ +- /* Hardware reset is done using gpio[0] of cx23880x chip. +- * I'd like to do it here, but don't know how to find chip address. +- * cx88-cards.c arranges for the reset bit to be inactive (high). +- * Maybe there needs to be a callable function in cx88-core or +- * the caller of this function needs to do it. */ +- +- /* +- * Array of byte pairs +- * to initialize each different chip +- */ +- static u8 lgdt3302_init_data[] = { +- /* Use 50MHz parameter values from spec sheet since xtal is 50 */ +- /* Change the value of NCOCTFV[25:0] of carrier +- recovery center frequency register */ +- VSB_CARRIER_FREQ0, 0x00, +- VSB_CARRIER_FREQ1, 0x87, +- VSB_CARRIER_FREQ2, 0x8e, +- VSB_CARRIER_FREQ3, 0x01, +- /* Change the TPCLK pin polarity +- data is valid on falling clock */ +- DEMUX_CONTROL, 0xfb, +- /* Change the value of IFBW[11:0] of +- AGC IF/RF loop filter bandwidth register */ +- AGC_RF_BANDWIDTH0, 0x40, +- AGC_RF_BANDWIDTH1, 0x93, +- AGC_RF_BANDWIDTH2, 0x00, +- /* Change the value of bit 6, 'nINAGCBY' and +- 'NSSEL[1:0] of ACG function control register 2 */ +- AGC_FUNC_CTRL2, 0xc6, +- /* Change the value of bit 6 'RFFIX' +- of AGC function control register 3 */ +- AGC_FUNC_CTRL3, 0x40, +- /* Set the value of 'INLVTHD' register 0x2a/0x2c +- to 0x7fe */ +- AGC_DELAY0, 0x07, +- AGC_DELAY2, 0xfe, +- /* Change the value of IAGCBW[15:8] +- of inner AGC loop filter bandwidth */ +- AGC_LOOP_BANDWIDTH0, 0x08, +- AGC_LOOP_BANDWIDTH1, 0x9a +- }; +- +- static u8 lgdt3303_init_data[] = { +- 0x4c, 0x14 +- }; +- +- static u8 flip_1_lgdt3303_init_data[] = { +- 0x4c, 0x14, +- 0x87, 0xf3 +- }; +- +- static u8 flip_2_lgdt3303_init_data[] = { +- 0x4c, 0x14, +- 0x87, 0xda +- }; +- +- struct lgdt330x_state* state = fe->demodulator_priv; +- char *chip_name; +- int err; +- +- switch (state->config->demod_chip) { +- case LGDT3302: +- chip_name = "LGDT3302"; +- err = i2c_write_demod_bytes(state, lgdt3302_init_data, +- sizeof(lgdt3302_init_data)); +- break; +- case LGDT3303: +- chip_name = "LGDT3303"; +- switch (state->config->clock_polarity_flip) { +- case 2: +- err = i2c_write_demod_bytes(state, +- flip_2_lgdt3303_init_data, +- sizeof(flip_2_lgdt3303_init_data)); +- break; +- case 1: +- err = i2c_write_demod_bytes(state, +- flip_1_lgdt3303_init_data, +- sizeof(flip_1_lgdt3303_init_data)); +- break; +- case 0: +- default: +- err = i2c_write_demod_bytes(state, lgdt3303_init_data, +- sizeof(lgdt3303_init_data)); +- } +- break; +- default: +- chip_name = "undefined"; +- printk (KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n"); +- err = -ENODEV; +- } +- dprintk("%s entered as %s\n", __func__, chip_name); +- if (err < 0) +- return err; +- return lgdt330x_SwReset(state); +-} +- +-static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- *ber = 0; /* Not supplied by the demod chips */ +- return 0; +-} +- +-static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct lgdt330x_state* state = fe->demodulator_priv; +- int err; +- u8 buf[2]; +- +- *ucblocks = 0; +- +- switch (state->config->demod_chip) { +- case LGDT3302: +- err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, +- buf, sizeof(buf)); +- break; +- case LGDT3303: +- err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, +- buf, sizeof(buf)); +- break; +- default: +- printk(KERN_WARNING +- "Only LGDT3302 and LGDT3303 are supported chips.\n"); +- err = -ENODEV; +- } +- if (err < 0) +- return err; +- +- *ucblocks = (buf[0] << 8) | buf[1]; +- return 0; +-} +- +-static int lgdt330x_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- /* +- * Array of byte pairs +- * to initialize 8VSB for lgdt3303 chip 50 MHz IF +- */ +- static u8 lgdt3303_8vsb_44_data[] = { +- 0x04, 0x00, +- 0x0d, 0x40, +- 0x0e, 0x87, +- 0x0f, 0x8e, +- 0x10, 0x01, +- 0x47, 0x8b }; +- +- /* +- * Array of byte pairs +- * to initialize QAM for lgdt3303 chip +- */ +- static u8 lgdt3303_qam_data[] = { +- 0x04, 0x00, +- 0x0d, 0x00, +- 0x0e, 0x00, +- 0x0f, 0x00, +- 0x10, 0x00, +- 0x51, 0x63, +- 0x47, 0x66, +- 0x48, 0x66, +- 0x4d, 0x1a, +- 0x49, 0x08, +- 0x4a, 0x9b }; +- +- struct lgdt330x_state* state = fe->demodulator_priv; +- +- static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; +- +- int err = 0; +- /* Change only if we are actually changing the modulation */ +- if (state->current_modulation != p->modulation) { +- switch (p->modulation) { +- case VSB_8: +- dprintk("%s: VSB_8 MODE\n", __func__); +- +- /* Select VSB mode */ +- top_ctrl_cfg[1] = 0x03; +- +- /* Select ANT connector if supported by card */ +- if (state->config->pll_rf_set) +- state->config->pll_rf_set(fe, 1); +- +- if (state->config->demod_chip == LGDT3303) { +- err = i2c_write_demod_bytes(state, lgdt3303_8vsb_44_data, +- sizeof(lgdt3303_8vsb_44_data)); +- } +- break; +- +- case QAM_64: +- dprintk("%s: QAM_64 MODE\n", __func__); +- +- /* Select QAM_64 mode */ +- top_ctrl_cfg[1] = 0x00; +- +- /* Select CABLE connector if supported by card */ +- if (state->config->pll_rf_set) +- state->config->pll_rf_set(fe, 0); +- +- if (state->config->demod_chip == LGDT3303) { +- err = i2c_write_demod_bytes(state, lgdt3303_qam_data, +- sizeof(lgdt3303_qam_data)); +- } +- break; +- +- case QAM_256: +- dprintk("%s: QAM_256 MODE\n", __func__); +- +- /* Select QAM_256 mode */ +- top_ctrl_cfg[1] = 0x01; +- +- /* Select CABLE connector if supported by card */ +- if (state->config->pll_rf_set) +- state->config->pll_rf_set(fe, 0); +- +- if (state->config->demod_chip == LGDT3303) { +- err = i2c_write_demod_bytes(state, lgdt3303_qam_data, +- sizeof(lgdt3303_qam_data)); +- } +- break; +- default: +- printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __func__, p->modulation); +- return -1; +- } +- if (err < 0) +- printk(KERN_WARNING "lgdt330x: %s: error blasting " +- "bytes to lgdt3303 for modulation type(%d)\n", +- __func__, p->modulation); +- +- /* +- * select serial or parallel MPEG harware interface +- * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303 +- * Parallel: 0x00 +- */ +- top_ctrl_cfg[1] |= state->config->serial_mpeg; +- +- /* Select the requested mode */ +- i2c_write_demod_bytes(state, top_ctrl_cfg, +- sizeof(top_ctrl_cfg)); +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- state->current_modulation = p->modulation; +- } +- +- /* Tune to the specified frequency */ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Keep track of the new frequency */ +- /* FIXME this is the wrong way to do this... */ +- /* The tuner is shared with the video4linux analog API */ +- state->current_frequency = p->frequency; +- +- lgdt330x_SwReset(state); +- return 0; +-} +- +-static int lgdt330x_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgdt330x_state *state = fe->demodulator_priv; +- p->frequency = state->current_frequency; +- return 0; +-} +- +-static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct lgdt330x_state* state = fe->demodulator_priv; +- u8 buf[3]; +- +- *status = 0; /* Reset status result */ +- +- /* AGC status register */ +- i2c_read_demod_bytes(state, AGC_STATUS, buf, 1); +- dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); +- if ((buf[0] & 0x0c) == 0x8){ +- /* Test signal does not exist flag */ +- /* as well as the AGC lock flag. */ +- *status |= FE_HAS_SIGNAL; +- } +- +- /* +- * You must set the Mask bits to 1 in the IRQ_MASK in order +- * to see that status bit in the IRQ_STATUS register. +- * This is done in SwReset(); +- */ +- /* signal status */ +- i2c_read_demod_bytes(state, TOP_CONTROL, buf, sizeof(buf)); +- dprintk("%s: TOP_CONTROL = 0x%02x, IRO_MASK = 0x%02x, IRQ_STATUS = 0x%02x\n", __func__, buf[0], buf[1], buf[2]); +- +- +- /* sync status */ +- if ((buf[2] & 0x03) == 0x01) { +- *status |= FE_HAS_SYNC; +- } +- +- /* FEC error status */ +- if ((buf[2] & 0x0c) == 0x08) { +- *status |= FE_HAS_LOCK; +- *status |= FE_HAS_VITERBI; +- } +- +- /* Carrier Recovery Lock Status Register */ +- i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); +- dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); +- switch (state->current_modulation) { +- case QAM_256: +- case QAM_64: +- /* Need to understand why there are 3 lock levels here */ +- if ((buf[0] & 0x07) == 0x07) +- *status |= FE_HAS_CARRIER; +- break; +- case VSB_8: +- if ((buf[0] & 0x80) == 0x80) +- *status |= FE_HAS_CARRIER; +- break; +- default: +- printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); +- } +- +- return 0; +-} +- +-static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct lgdt330x_state* state = fe->demodulator_priv; +- int err; +- u8 buf[3]; +- +- *status = 0; /* Reset status result */ +- +- /* lgdt3303 AGC status register */ +- err = i2c_read_demod_bytes(state, 0x58, buf, 1); +- if (err < 0) +- return err; +- +- dprintk("%s: AGC_STATUS = 0x%02x\n", __func__, buf[0]); +- if ((buf[0] & 0x21) == 0x01){ +- /* Test input signal does not exist flag */ +- /* as well as the AGC lock flag. */ +- *status |= FE_HAS_SIGNAL; +- } +- +- /* Carrier Recovery Lock Status Register */ +- i2c_read_demod_bytes(state, CARRIER_LOCK, buf, 1); +- dprintk("%s: CARRIER_LOCK = 0x%02x\n", __func__, buf[0]); +- switch (state->current_modulation) { +- case QAM_256: +- case QAM_64: +- /* Need to understand why there are 3 lock levels here */ +- if ((buf[0] & 0x07) == 0x07) +- *status |= FE_HAS_CARRIER; +- else +- break; +- i2c_read_demod_bytes(state, 0x8a, buf, 1); +- if ((buf[0] & 0x04) == 0x04) +- *status |= FE_HAS_SYNC; +- if ((buf[0] & 0x01) == 0x01) +- *status |= FE_HAS_LOCK; +- if ((buf[0] & 0x08) == 0x08) +- *status |= FE_HAS_VITERBI; +- break; +- case VSB_8: +- if ((buf[0] & 0x80) == 0x80) +- *status |= FE_HAS_CARRIER; +- else +- break; +- i2c_read_demod_bytes(state, 0x38, buf, 1); +- if ((buf[0] & 0x02) == 0x00) +- *status |= FE_HAS_SYNC; +- if ((buf[0] & 0x01) == 0x01) { +- *status |= FE_HAS_LOCK; +- *status |= FE_HAS_VITERBI; +- } +- break; +- default: +- printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __func__); +- } +- return 0; +-} +- +-/* Calculate SNR estimation (scaled by 2^24) +- +- 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM +- equations from LGDT3303 datasheet. VSB is the same between the '02 +- and '03, so maybe QAM is too? Perhaps someone with a newer datasheet +- that has QAM information could verify? +- +- For 8-VSB: (two ways, take your pick) +- LGDT3302: +- SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) +- LGDT3303: +- SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) +- LGDT3302 & LGDT3303: +- SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) +- For 64-QAM: +- SNR = 10 * log10( 688128 / MSEQAM) +- For 256-QAM: +- SNR = 10 * log10( 696320 / MSEQAM) +- +- We re-write the snr equation as: +- SNR * 2^24 = 10*(c - intlog10(MSE)) +- Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ +- +-static u32 calculate_snr(u32 mse, u32 c) +-{ +- if (mse == 0) /* No signal */ +- return 0; +- +- mse = intlog10(mse); +- if (mse > c) { +- /* Negative SNR, which is possible, but realisticly the +- demod will lose lock before the signal gets this bad. The +- API only allows for unsigned values, so just return 0 */ +- return 0; +- } +- return 10*(c - mse); +-} +- +-static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; +- u8 buf[5]; /* read data buffer */ +- u32 noise; /* noise value */ +- u32 c; /* per-modulation SNR calculation constant */ +- +- switch(state->current_modulation) { +- case VSB_8: +- i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); +-#ifdef USE_EQMSE +- /* Use Equalizer Mean-Square Error Register */ +- /* SNR for ranges from -15.61 to +41.58 */ +- noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; +- c = 69765745; /* log10(25*24^2)*2^24 */ +-#else +- /* Use Phase Tracker Mean-Square Error Register */ +- /* SNR for ranges from -13.11 to +44.08 */ +- noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; +- c = 73957994; /* log10(25*32^2)*2^24 */ +-#endif +- break; +- case QAM_64: +- case QAM_256: +- i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); +- noise = ((buf[0] & 3) << 8) | buf[1]; +- c = state->current_modulation == QAM_64 ? 97939837 : 98026066; +- /* log10(688128)*2^24 and log10(696320)*2^24 */ +- break; +- default: +- printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", +- __func__); +- return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ +- } +- +- state->snr = calculate_snr(noise, c); +- *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ +- +- dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, +- state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); +- +- return 0; +-} +- +-static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; +- u8 buf[5]; /* read data buffer */ +- u32 noise; /* noise value */ +- u32 c; /* per-modulation SNR calculation constant */ +- +- switch(state->current_modulation) { +- case VSB_8: +- i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); +-#ifdef USE_EQMSE +- /* Use Equalizer Mean-Square Error Register */ +- /* SNR for ranges from -16.12 to +44.08 */ +- noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; +- c = 73957994; /* log10(25*32^2)*2^24 */ +-#else +- /* Use Phase Tracker Mean-Square Error Register */ +- /* SNR for ranges from -13.11 to +44.08 */ +- noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; +- c = 73957994; /* log10(25*32^2)*2^24 */ +-#endif +- break; +- case QAM_64: +- case QAM_256: +- i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); +- noise = (buf[0] << 8) | buf[1]; +- c = state->current_modulation == QAM_64 ? 97939837 : 98026066; +- /* log10(688128)*2^24 and log10(696320)*2^24 */ +- break; +- default: +- printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", +- __func__); +- return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ +- } +- +- state->snr = calculate_snr(noise, c); +- *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ +- +- dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, +- state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); +- +- return 0; +-} +- +-static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- /* Calculate Strength from SNR up to 35dB */ +- /* Even though the SNR can go higher than 35dB, there is some comfort */ +- /* factor in having a range of strong signals that can show at 100% */ +- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; +- u16 snr; +- int ret; +- +- ret = fe->ops.read_snr(fe, &snr); +- if (ret != 0) +- return ret; +- /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ +- /* scale the range 0 - 35*2^24 into 0 - 65535 */ +- if (state->snr >= 8960 * 0x10000) +- *strength = 0xffff; +- else +- *strength = state->snr / 8960; +- +- return 0; +-} +- +-static int lgdt330x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +-{ +- /* I have no idea about this - it may not be needed */ +- fe_tune_settings->min_delay_ms = 500; +- fe_tune_settings->step_size = 0; +- fe_tune_settings->max_drift = 0; +- return 0; +-} +- +-static void lgdt330x_release(struct dvb_frontend* fe) +-{ +- struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops lgdt3302_ops; +-static struct dvb_frontend_ops lgdt3303_ops; +- +-struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, +- struct i2c_adapter* i2c) +-{ +- struct lgdt330x_state* state = NULL; +- u8 buf[1]; +- +- /* Allocate memory for the internal state */ +- state = kzalloc(sizeof(struct lgdt330x_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* Setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* Create dvb_frontend */ +- switch (config->demod_chip) { +- case LGDT3302: +- memcpy(&state->frontend.ops, &lgdt3302_ops, sizeof(struct dvb_frontend_ops)); +- break; +- case LGDT3303: +- memcpy(&state->frontend.ops, &lgdt3303_ops, sizeof(struct dvb_frontend_ops)); +- break; +- default: +- goto error; +- } +- state->frontend.demodulator_priv = state; +- +- /* Verify communication with demod chip */ +- if (i2c_read_demod_bytes(state, 2, buf, 1)) +- goto error; +- +- state->current_frequency = -1; +- state->current_modulation = -1; +- +- return &state->frontend; +- +-error: +- kfree(state); +- dprintk("%s: ERROR\n",__func__); +- return NULL; +-} +- +-static struct dvb_frontend_ops lgdt3302_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name= "LG Electronics LGDT3302 VSB/QAM Frontend", +- .frequency_min= 54000000, +- .frequency_max= 858000000, +- .frequency_stepsize= 62500, +- .symbol_rate_min = 5056941, /* QAM 64 */ +- .symbol_rate_max = 10762000, /* VSB 8 */ +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- .init = lgdt330x_init, +- .set_frontend = lgdt330x_set_parameters, +- .get_frontend = lgdt330x_get_frontend, +- .get_tune_settings = lgdt330x_get_tune_settings, +- .read_status = lgdt3302_read_status, +- .read_ber = lgdt330x_read_ber, +- .read_signal_strength = lgdt330x_read_signal_strength, +- .read_snr = lgdt3302_read_snr, +- .read_ucblocks = lgdt330x_read_ucblocks, +- .release = lgdt330x_release, +-}; +- +-static struct dvb_frontend_ops lgdt3303_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name= "LG Electronics LGDT3303 VSB/QAM Frontend", +- .frequency_min= 54000000, +- .frequency_max= 858000000, +- .frequency_stepsize= 62500, +- .symbol_rate_min = 5056941, /* QAM 64 */ +- .symbol_rate_max = 10762000, /* VSB 8 */ +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- .init = lgdt330x_init, +- .set_frontend = lgdt330x_set_parameters, +- .get_frontend = lgdt330x_get_frontend, +- .get_tune_settings = lgdt330x_get_tune_settings, +- .read_status = lgdt3303_read_status, +- .read_ber = lgdt330x_read_ber, +- .read_signal_strength = lgdt330x_read_signal_strength, +- .read_snr = lgdt3303_read_snr, +- .read_ucblocks = lgdt330x_read_ucblocks, +- .release = lgdt330x_release, +-}; +- +-MODULE_DESCRIPTION("LGDT330X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); +-MODULE_AUTHOR("Wilson Michaels"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(lgdt330x_attach); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/lgdt330x.h b/drivers/media/dvb/frontends/lgdt330x.h +deleted file mode 100644 +index 9012504..0000000 +--- a/drivers/media/dvb/frontends/lgdt330x.h ++++ /dev/null +@@ -1,73 +0,0 @@ +-/* +- * Support for LGDT3302 and LGDT3303 - VSB/QAM +- * +- * Copyright (C) 2005 Wilson Michaels +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef LGDT330X_H +-#define LGDT330X_H +- +-#include +- +-typedef enum lg_chip_t { +- UNDEFINED, +- LGDT3302, +- LGDT3303 +-}lg_chip_type; +- +-struct lgdt330x_config +-{ +- /* The demodulator's i2c address */ +- u8 demod_address; +- +- /* LG demodulator chip LGDT3302 or LGDT3303 */ +- lg_chip_type demod_chip; +- +- /* MPEG hardware interface - 0:parallel 1:serial */ +- int serial_mpeg; +- +- /* PLL interface */ +- int (*pll_rf_set) (struct dvb_frontend* fe, int index); +- +- /* Need to set device param for start_dma */ +- int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); +- +- /* Flip the polarity of the mpeg data transfer clock using alternate init data +- * This option applies ONLY to LGDT3303 - 0:disabled (default) 1:enabled */ +- int clock_polarity_flip; +-}; +- +-#if defined(CONFIG_DVB_LGDT330X) || (defined(CONFIG_DVB_LGDT330X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_LGDT330X +- +-#endif /* LGDT330X_H */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h +deleted file mode 100644 +index 38c7669..0000000 +--- a/drivers/media/dvb/frontends/lgdt330x_priv.h ++++ /dev/null +@@ -1,77 +0,0 @@ +-/* +- * Support for LGDT3302 and LGDT3303 - VSB/QAM +- * +- * Copyright (C) 2005 Wilson Michaels +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef _LGDT330X_PRIV_ +-#define _LGDT330X_PRIV_ +- +-/* i2c control register addresses */ +-enum I2C_REG { +- TOP_CONTROL= 0x00, +- IRQ_MASK= 0x01, +- IRQ_STATUS= 0x02, +- VSB_CARRIER_FREQ0= 0x16, +- VSB_CARRIER_FREQ1= 0x17, +- VSB_CARRIER_FREQ2= 0x18, +- VSB_CARRIER_FREQ3= 0x19, +- CARRIER_MSEQAM1= 0x1a, +- CARRIER_MSEQAM2= 0x1b, +- CARRIER_LOCK= 0x1c, +- TIMING_RECOVERY= 0x1d, +- AGC_DELAY0= 0x2a, +- AGC_DELAY1= 0x2b, +- AGC_DELAY2= 0x2c, +- AGC_RF_BANDWIDTH0= 0x2d, +- AGC_RF_BANDWIDTH1= 0x2e, +- AGC_RF_BANDWIDTH2= 0x2f, +- AGC_LOOP_BANDWIDTH0= 0x30, +- AGC_LOOP_BANDWIDTH1= 0x31, +- AGC_FUNC_CTRL1= 0x32, +- AGC_FUNC_CTRL2= 0x33, +- AGC_FUNC_CTRL3= 0x34, +- AGC_RFIF_ACC0= 0x39, +- AGC_RFIF_ACC1= 0x3a, +- AGC_RFIF_ACC2= 0x3b, +- AGC_STATUS= 0x3f, +- SYNC_STATUS_VSB= 0x43, +- DEMUX_CONTROL= 0x66, +- LGDT3302_EQPH_ERR0= 0x47, +- LGDT3302_EQ_ERR1= 0x48, +- LGDT3302_EQ_ERR2= 0x49, +- LGDT3302_PH_ERR1= 0x4a, +- LGDT3302_PH_ERR2= 0x4b, +- LGDT3302_PACKET_ERR_COUNTER1= 0x6a, +- LGDT3302_PACKET_ERR_COUNTER2= 0x6b, +- LGDT3303_EQPH_ERR0= 0x6e, +- LGDT3303_EQ_ERR1= 0x6f, +- LGDT3303_EQ_ERR2= 0x70, +- LGDT3303_PH_ERR1= 0x71, +- LGDT3303_PH_ERR2= 0x72, +- LGDT3303_PACKET_ERR_COUNTER1= 0x8b, +- LGDT3303_PACKET_ERR_COUNTER2= 0x8c, +-}; +- +-#endif /* _LGDT330X_PRIV_ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c +deleted file mode 100644 +index 2cec804..0000000 +--- a/drivers/media/dvb/frontends/lgs8gl5.c ++++ /dev/null +@@ -1,453 +0,0 @@ +-/* +- Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver +- +- Copyright (C) 2008 Sirius International (Hong Kong) Limited +- Timothy Lee +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "lgs8gl5.h" +- +- +-#define REG_RESET 0x02 +-#define REG_RESET_OFF 0x01 +-#define REG_03 0x03 +-#define REG_04 0x04 +-#define REG_07 0x07 +-#define REG_09 0x09 +-#define REG_0A 0x0a +-#define REG_0B 0x0b +-#define REG_0C 0x0c +-#define REG_37 0x37 +-#define REG_STRENGTH 0x4b +-#define REG_STRENGTH_MASK 0x7f +-#define REG_STRENGTH_CARRIER 0x80 +-#define REG_INVERSION 0x7c +-#define REG_INVERSION_ON 0x80 +-#define REG_7D 0x7d +-#define REG_7E 0x7e +-#define REG_A2 0xa2 +-#define REG_STATUS 0xa4 +-#define REG_STATUS_SYNC 0x04 +-#define REG_STATUS_LOCK 0x01 +- +- +-struct lgs8gl5_state { +- struct i2c_adapter *i2c; +- const struct lgs8gl5_config *config; +- struct dvb_frontend frontend; +-}; +- +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "lgs8gl5: " args); \ +- } while (0) +- +- +-/* Writes into demod's register */ +-static int +-lgs8gl5_write_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = {reg, data}; +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 +- }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) +- dprintk("%s: error (reg=0x%02x, val=0x%02x, ret=%i)\n", +- __func__, reg, data, ret); +- return (ret != 1) ? -1 : 0; +-} +- +- +-/* Reads from demod's register */ +-static int +-lgs8gl5_read_reg(struct lgs8gl5_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = {reg}; +- u8 b1[] = {0}; +- struct i2c_msg msg[2] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, +- { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) +- return -EIO; +- +- return b1[0]; +-} +- +- +-static int +-lgs8gl5_update_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +-{ +- lgs8gl5_read_reg(state, reg); +- lgs8gl5_write_reg(state, reg, data); +- return 0; +-} +- +- +-/* Writes into alternate device's register */ +-/* TODO: Find out what that device is for! */ +-static int +-lgs8gl5_update_alt_reg(struct lgs8gl5_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 b0[] = {reg}; +- u8 b1[] = {0}; +- u8 b2[] = {reg, data}; +- struct i2c_msg msg[3] = { +- { +- .addr = state->config->demod_address + 2, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, +- { +- .addr = state->config->demod_address + 2, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- }, +- { +- .addr = state->config->demod_address + 2, +- .flags = 0, +- .buf = b2, +- .len = 2 +- }, +- }; +- +- ret = i2c_transfer(state->i2c, msg, 3); +- return (ret != 3) ? -1 : 0; +-} +- +- +-static void +-lgs8gl5_soft_reset(struct lgs8gl5_state *state) +-{ +- u8 val; +- +- dprintk("%s\n", __func__); +- +- val = lgs8gl5_read_reg(state, REG_RESET); +- lgs8gl5_write_reg(state, REG_RESET, val & ~REG_RESET_OFF); +- lgs8gl5_write_reg(state, REG_RESET, val | REG_RESET_OFF); +- msleep(5); +-} +- +- +-/* Starts demodulation */ +-static void +-lgs8gl5_start_demod(struct lgs8gl5_state *state) +-{ +- u8 val; +- int n; +- +- dprintk("%s\n", __func__); +- +- lgs8gl5_update_alt_reg(state, 0xc2, 0x28); +- lgs8gl5_soft_reset(state); +- lgs8gl5_update_reg(state, REG_07, 0x10); +- lgs8gl5_update_reg(state, REG_07, 0x10); +- lgs8gl5_write_reg(state, REG_09, 0x0e); +- lgs8gl5_write_reg(state, REG_0A, 0xe5); +- lgs8gl5_write_reg(state, REG_0B, 0x35); +- lgs8gl5_write_reg(state, REG_0C, 0x30); +- +- lgs8gl5_update_reg(state, REG_03, 0x00); +- lgs8gl5_update_reg(state, REG_7E, 0x01); +- lgs8gl5_update_alt_reg(state, 0xc5, 0x00); +- lgs8gl5_update_reg(state, REG_04, 0x02); +- lgs8gl5_update_reg(state, REG_37, 0x01); +- lgs8gl5_soft_reset(state); +- +- /* Wait for carrier */ +- for (n = 0; n < 10; n++) { +- val = lgs8gl5_read_reg(state, REG_STRENGTH); +- dprintk("Wait for carrier[%d] 0x%02X\n", n, val); +- if (val & REG_STRENGTH_CARRIER) +- break; +- msleep(4); +- } +- if (!(val & REG_STRENGTH_CARRIER)) +- return; +- +- /* Wait for lock */ +- for (n = 0; n < 20; n++) { +- val = lgs8gl5_read_reg(state, REG_STATUS); +- dprintk("Wait for lock[%d] 0x%02X\n", n, val); +- if (val & REG_STATUS_LOCK) +- break; +- msleep(12); +- } +- if (!(val & REG_STATUS_LOCK)) +- return; +- +- lgs8gl5_write_reg(state, REG_7D, lgs8gl5_read_reg(state, REG_A2)); +- lgs8gl5_soft_reset(state); +-} +- +- +-static int +-lgs8gl5_init(struct dvb_frontend *fe) +-{ +- struct lgs8gl5_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- lgs8gl5_update_alt_reg(state, 0xc2, 0x28); +- lgs8gl5_soft_reset(state); +- lgs8gl5_update_reg(state, REG_07, 0x10); +- lgs8gl5_update_reg(state, REG_07, 0x10); +- lgs8gl5_write_reg(state, REG_09, 0x0e); +- lgs8gl5_write_reg(state, REG_0A, 0xe5); +- lgs8gl5_write_reg(state, REG_0B, 0x35); +- lgs8gl5_write_reg(state, REG_0C, 0x30); +- +- return 0; +-} +- +- +-static int +-lgs8gl5_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct lgs8gl5_state *state = fe->demodulator_priv; +- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); +- u8 flags = lgs8gl5_read_reg(state, REG_STATUS); +- +- *status = 0; +- +- if ((level & REG_STRENGTH_MASK) > 0) +- *status |= FE_HAS_SIGNAL; +- if (level & REG_STRENGTH_CARRIER) +- *status |= FE_HAS_CARRIER; +- if (flags & REG_STATUS_SYNC) +- *status |= FE_HAS_SYNC; +- if (flags & REG_STATUS_LOCK) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- *ber = 0; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_read_signal_strength(struct dvb_frontend *fe, u16 *signal_strength) +-{ +- struct lgs8gl5_state *state = fe->demodulator_priv; +- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); +- *signal_strength = (level & REG_STRENGTH_MASK) << 8; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct lgs8gl5_state *state = fe->demodulator_priv; +- u8 level = lgs8gl5_read_reg(state, REG_STRENGTH); +- *snr = (level & REG_STRENGTH_MASK) << 8; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgs8gl5_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (p->bandwidth_hz != 8000000) +- return -EINVAL; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* lgs8gl5_set_inversion(state, p->inversion); */ +- +- lgs8gl5_start_demod(state); +- +- return 0; +-} +- +- +-static int +-lgs8gl5_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct lgs8gl5_state *state = fe->demodulator_priv; +- u8 inv = lgs8gl5_read_reg(state, REG_INVERSION); +- +- p->inversion = (inv & REG_INVERSION_ON) ? INVERSION_ON : INVERSION_OFF; +- +- p->code_rate_HP = FEC_1_2; +- p->code_rate_LP = FEC_7_8; +- p->guard_interval = GUARD_INTERVAL_1_32; +- p->transmission_mode = TRANSMISSION_MODE_2K; +- p->modulation = QAM_64; +- p->hierarchy = HIERARCHY_NONE; +- p->bandwidth_hz = 8000000; +- +- return 0; +-} +- +- +-static int +-lgs8gl5_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- fesettings->min_delay_ms = 240; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +- +-static void +-lgs8gl5_release(struct dvb_frontend *fe) +-{ +- struct lgs8gl5_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +- +-static struct dvb_frontend_ops lgs8gl5_ops; +- +- +-struct dvb_frontend* +-lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) +-{ +- struct lgs8gl5_state *state = NULL; +- +- dprintk("%s\n", __func__); +- +- /* Allocate memory for the internal state */ +- state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* Setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* Check if the demod is there */ +- if (lgs8gl5_read_reg(state, REG_RESET) < 0) +- goto error; +- +- /* Create dvb_frontend */ +- memcpy(&state->frontend.ops, &lgs8gl5_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(lgs8gl5_attach); +- +- +-static struct dvb_frontend_ops lgs8gl5_ops = { +- .delsys = { SYS_DMBTH }, +- .info = { +- .name = "Legend Silicon LGS-8GL5 DMB-TH", +- .frequency_min = 474000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 10000, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_BANDWIDTH_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | +- FE_CAN_RECOVER +- }, +- +- .release = lgs8gl5_release, +- +- .init = lgs8gl5_init, +- +- .set_frontend = lgs8gl5_set_frontend, +- .get_frontend = lgs8gl5_get_frontend, +- .get_tune_settings = lgs8gl5_get_tune_settings, +- +- .read_status = lgs8gl5_read_status, +- .read_ber = lgs8gl5_read_ber, +- .read_signal_strength = lgs8gl5_read_signal_strength, +- .read_snr = lgs8gl5_read_snr, +- .read_ucblocks = lgs8gl5_read_ucblocks, +-}; +- +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Legend Silicon LGS-8GL5 DMB-TH Demodulator driver"); +-MODULE_AUTHOR("Timothy Lee"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/lgs8gl5.h b/drivers/media/dvb/frontends/lgs8gl5.h +deleted file mode 100644 +index d141767..0000000 +--- a/drivers/media/dvb/frontends/lgs8gl5.h ++++ /dev/null +@@ -1,45 +0,0 @@ +-/* +- Legend Silicon LGS-8GL5 DMB-TH OFDM demodulator driver +- +- Copyright (C) 2008 Sirius International (Hong Kong) Limited +- Timothy Lee +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef LGS8GL5_H +-#define LGS8GL5_H +- +-#include +- +-struct lgs8gl5_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-#if defined(CONFIG_DVB_LGS8GL5) || \ +- (defined(CONFIG_DVB_LGS8GL5_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *lgs8gl5_attach( +- const struct lgs8gl5_config *config, struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *lgs8gl5_attach( +- const struct lgs8gl5_config *config, struct i2c_adapter *i2c) { +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_LGS8GL5 */ +- +-#endif /* LGS8GL5_H */ +diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c +deleted file mode 100644 +index 4de1d35..0000000 +--- a/drivers/media/dvb/frontends/lgs8gxx.c ++++ /dev/null +@@ -1,1073 +0,0 @@ +-/* +- * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator +- * LGS8913, LGS8GL5, LGS8G75 +- * experimental support LGS8G42, LGS8G52 +- * +- * Copyright (C) 2007-2009 David T.L. Wong +- * Copyright (C) 2008 Sirius International (Hong Kong) Limited +- * Timothy Lee (for initial work on LGS8GL5) +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "lgs8gxx.h" +-#include "lgs8gxx_priv.h" +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "lgs8gxx: " args); \ +- } while (0) +- +-static int debug; +-static int fake_signal_str = 1; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-module_param(fake_signal_str, int, 0644); +-MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." +-"Signal strength calculation is slow.(default:on)."); +- +-/* LGS8GXX internal helper functions */ +- +-static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; +- +- msg.addr = priv->config->demod_address; +- if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) +- msg.addr += 0x02; +- +- if (debug >= 2) +- dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); +- +- ret = i2c_transfer(priv->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", +- __func__, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) +-{ +- int ret; +- u8 dev_addr; +- +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { .flags = 0, .buf = b0, .len = 1 }, +- { .flags = I2C_M_RD, .buf = b1, .len = 1 }, +- }; +- +- dev_addr = priv->config->demod_address; +- if (priv->config->prod != LGS8GXX_PROD_LGS8G75 && reg >= 0xC0) +- dev_addr += 0x02; +- msg[1].addr = msg[0].addr = dev_addr; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret != 2) { +- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); +- return -1; +- } +- +- *p_data = b1[0]; +- if (debug >= 2) +- dprintk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); +- return 0; +-} +- +-static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) +-{ +- lgs8gxx_write_reg(priv, 0x02, 0x00); +- msleep(1); +- lgs8gxx_write_reg(priv, 0x02, 0x01); +- msleep(100); +- +- return 0; +-} +- +-static int wait_reg_mask(struct lgs8gxx_state *priv, u8 reg, u8 mask, +- u8 val, u8 delay, u8 tries) +-{ +- u8 t; +- int i; +- +- for (i = 0; i < tries; i++) { +- lgs8gxx_read_reg(priv, reg, &t); +- +- if ((t & mask) == val) +- return 0; +- msleep(delay); +- } +- +- return 1; +-} +- +-static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) +-{ +- const struct lgs8gxx_config *config = priv->config; +- u8 if_conf; +- +- if_conf = 0x10; /* AGC output on, RF_AGC output off; */ +- +- if_conf |= +- ((config->ext_adc) ? 0x80 : 0x00) | +- ((config->if_neg_center) ? 0x04 : 0x00) | +- ((config->if_freq == 0) ? 0x08 : 0x00) | /* Baseband */ +- ((config->adc_signed) ? 0x02 : 0x00) | +- ((config->if_neg_edge) ? 0x01 : 0x00); +- +- if (config->ext_adc && +- (config->prod == LGS8GXX_PROD_LGS8G52)) { +- lgs8gxx_write_reg(priv, 0xBA, 0x40); +- } +- +- lgs8gxx_write_reg(priv, 0x07, if_conf); +- +- return 0; +-} +- +-static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) +-{ +- u64 val; +- u32 v32; +- u32 if_clk; +- +- if_clk = priv->config->if_clk_freq; +- +- val = freq; +- if (freq != 0) { +- val <<= 32; +- if (if_clk != 0) +- do_div(val, if_clk); +- v32 = val & 0xFFFFFFFF; +- dprintk("Set IF Freq to %dkHz\n", freq); +- } else { +- v32 = 0; +- dprintk("Set IF Freq to baseband\n"); +- } +- dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_write_reg(priv, 0x08, 0xFF & (v32)); +- lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32 >> 8)); +- lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 16)); +- lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 24)); +- } else { +- lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); +- lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); +- lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); +- lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); +- } +- +- return 0; +-} +- +-static int lgs8gxx_get_afc_phase(struct lgs8gxx_state *priv) +-{ +- u64 val; +- u32 v32 = 0; +- u8 reg_addr, t; +- int i; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) +- reg_addr = 0x23; +- else +- reg_addr = 0x48; +- +- for (i = 0; i < 4; i++) { +- lgs8gxx_read_reg(priv, reg_addr, &t); +- v32 <<= 8; +- v32 |= t; +- reg_addr--; +- } +- +- val = v32; +- val *= priv->config->if_clk_freq; +- val >>= 32; +- dprintk("AFC = %u kHz\n", (u32)val); +- return 0; +-} +- +-static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) +-{ +- u8 t; +- u8 prod = priv->config->prod; +- +- if (prod == LGS8GXX_PROD_LGS8913) +- lgs8gxx_write_reg(priv, 0xC6, 0x01); +- +- if (prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_read_reg(priv, 0x0C, &t); +- t &= (~0x04); +- lgs8gxx_write_reg(priv, 0x0C, t | 0x80); +- lgs8gxx_write_reg(priv, 0x39, 0x00); +- lgs8gxx_write_reg(priv, 0x3D, 0x04); +- } else if (prod == LGS8GXX_PROD_LGS8913 || +- prod == LGS8GXX_PROD_LGS8GL5 || +- prod == LGS8GXX_PROD_LGS8G42 || +- prod == LGS8GXX_PROD_LGS8G52 || +- prod == LGS8GXX_PROD_LGS8G54) { +- lgs8gxx_read_reg(priv, 0x7E, &t); +- lgs8gxx_write_reg(priv, 0x7E, t | 0x01); +- +- /* clear FEC self reset */ +- lgs8gxx_read_reg(priv, 0xC5, &t); +- lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); +- } +- +- if (prod == LGS8GXX_PROD_LGS8913) { +- /* FEC auto detect */ +- lgs8gxx_write_reg(priv, 0xC1, 0x03); +- +- lgs8gxx_read_reg(priv, 0x7C, &t); +- t = (t & 0x8C) | 0x03; +- lgs8gxx_write_reg(priv, 0x7C, t); +- +- /* BER test mode */ +- lgs8gxx_read_reg(priv, 0xC3, &t); +- t = (t & 0xEF) | 0x10; +- lgs8gxx_write_reg(priv, 0xC3, t); +- } +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G52) +- lgs8gxx_write_reg(priv, 0xD9, 0x40); +- +- return 0; +-} +- +-static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) +-{ +- int ret = 0; +- u8 t; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- u8 t2; +- lgs8gxx_read_reg(priv, 0x0C, &t); +- t &= (~0x80); +- lgs8gxx_write_reg(priv, 0x0C, t); +- +- lgs8gxx_read_reg(priv, 0x0C, &t); +- lgs8gxx_read_reg(priv, 0x19, &t2); +- +- if (((t&0x03) == 0x01) && (t2&0x01)) { +- lgs8gxx_write_reg(priv, 0x6E, 0x05); +- lgs8gxx_write_reg(priv, 0x39, 0x02); +- lgs8gxx_write_reg(priv, 0x39, 0x03); +- lgs8gxx_write_reg(priv, 0x3D, 0x05); +- lgs8gxx_write_reg(priv, 0x3E, 0x28); +- lgs8gxx_write_reg(priv, 0x53, 0x80); +- } else { +- lgs8gxx_write_reg(priv, 0x6E, 0x3F); +- lgs8gxx_write_reg(priv, 0x39, 0x00); +- lgs8gxx_write_reg(priv, 0x3D, 0x04); +- } +- +- lgs8gxx_soft_reset(priv); +- return 0; +- } +- +- /* turn off auto-detect; manual settings */ +- lgs8gxx_write_reg(priv, 0x7E, 0); +- if (priv->config->prod == LGS8GXX_PROD_LGS8913) +- lgs8gxx_write_reg(priv, 0xC1, 0); +- +- ret = lgs8gxx_read_reg(priv, 0xC5, &t); +- t = (t & 0xE0) | 0x06; +- lgs8gxx_write_reg(priv, 0xC5, t); +- +- lgs8gxx_soft_reset(priv); +- +- return 0; +-} +- +-static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) +-{ +- int ret = 0; +- u8 t; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) +- ret = lgs8gxx_read_reg(priv, 0x13, &t); +- else +- ret = lgs8gxx_read_reg(priv, 0x4B, &t); +- if (ret != 0) +- return ret; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) +- *locked = ((t & 0x80) == 0x80) ? 1 : 0; +- else +- *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; +- return 0; +-} +- +-/* Wait for Code Acquisition Lock */ +-static int lgs8gxx_wait_ca_lock(struct lgs8gxx_state *priv, u8 *locked) +-{ +- int ret = 0; +- u8 reg, mask, val; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- reg = 0x13; +- mask = 0x80; +- val = 0x80; +- } else { +- reg = 0x4B; +- mask = 0xC0; +- val = 0xC0; +- } +- +- ret = wait_reg_mask(priv, reg, mask, val, 50, 40); +- *locked = (ret == 0) ? 1 : 0; +- +- return 0; +-} +- +-static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, +- u8 *finished) +-{ +- int ret = 0; +- u8 reg, mask, val; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- reg = 0x1f; +- mask = 0xC0; +- val = 0x80; +- } else { +- reg = 0xA4; +- mask = 0x03; +- val = 0x01; +- } +- +- ret = wait_reg_mask(priv, reg, mask, val, 10, 20); +- *finished = (ret == 0) ? 1 : 0; +- +- return 0; +-} +- +-static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 cpn, +- u8 *locked) +-{ +- int err = 0; +- u8 ad_fini = 0; +- u8 t1, t2; +- +- if (gi == GI_945) +- dprintk("try GI 945\n"); +- else if (gi == GI_595) +- dprintk("try GI 595\n"); +- else if (gi == GI_420) +- dprintk("try GI 420\n"); +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_read_reg(priv, 0x0C, &t1); +- lgs8gxx_read_reg(priv, 0x18, &t2); +- t1 &= ~(GI_MASK); +- t1 |= gi; +- t2 &= 0xFE; +- t2 |= cpn ? 0x01 : 0x00; +- lgs8gxx_write_reg(priv, 0x0C, t1); +- lgs8gxx_write_reg(priv, 0x18, t2); +- } else { +- lgs8gxx_write_reg(priv, 0x04, gi); +- } +- lgs8gxx_soft_reset(priv); +- err = lgs8gxx_wait_ca_lock(priv, locked); +- if (err || !(*locked)) +- return err; +- err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); +- if (err != 0) +- return err; +- if (ad_fini) { +- dprintk("auto detect finished\n"); +- } else +- *locked = 0; +- +- return 0; +-} +- +-static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, +- u8 *detected_param, u8 *gi) +-{ +- int i, j; +- int err = 0; +- u8 locked = 0, tmp_gi; +- +- dprintk("%s\n", __func__); +- +- lgs8gxx_set_mode_auto(priv); +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_write_reg(priv, 0x67, 0xAA); +- lgs8gxx_write_reg(priv, 0x6E, 0x3F); +- } else { +- /* Guard Interval */ +- lgs8gxx_write_reg(priv, 0x03, 00); +- } +- +- for (i = 0; i < 2; i++) { +- for (j = 0; j < 2; j++) { +- tmp_gi = GI_945; +- err = lgs8gxx_autolock_gi(priv, GI_945, j, &locked); +- if (err) +- goto out; +- if (locked) +- goto locked; +- } +- for (j = 0; j < 2; j++) { +- tmp_gi = GI_420; +- err = lgs8gxx_autolock_gi(priv, GI_420, j, &locked); +- if (err) +- goto out; +- if (locked) +- goto locked; +- } +- tmp_gi = GI_595; +- err = lgs8gxx_autolock_gi(priv, GI_595, 1, &locked); +- if (err) +- goto out; +- if (locked) +- goto locked; +- } +- +-locked: +- if ((err == 0) && (locked == 1)) { +- u8 t; +- +- if (priv->config->prod != LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_read_reg(priv, 0xA2, &t); +- *detected_param = t; +- } else { +- lgs8gxx_read_reg(priv, 0x1F, &t); +- *detected_param = t & 0x3F; +- } +- +- if (tmp_gi == GI_945) +- dprintk("GI 945 locked\n"); +- else if (tmp_gi == GI_595) +- dprintk("GI 595 locked\n"); +- else if (tmp_gi == GI_420) +- dprintk("GI 420 locked\n"); +- *gi = tmp_gi; +- } +- if (!locked) +- err = -1; +- +-out: +- return err; +-} +- +-static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) +-{ +- s8 err; +- u8 gi = 0x2; +- u8 detected_param = 0; +- +- err = lgs8gxx_auto_detect(priv, &detected_param, &gi); +- +- if (err != 0) { +- dprintk("lgs8gxx_auto_detect failed\n"); +- } else +- dprintk("detected param = 0x%02X\n", detected_param); +- +- /* Apply detected parameters */ +- if (priv->config->prod == LGS8GXX_PROD_LGS8913) { +- u8 inter_leave_len = detected_param & TIM_MASK ; +- /* Fix 8913 time interleaver detection bug */ +- inter_leave_len = (inter_leave_len == TIM_MIDDLE) ? 0x60 : 0x40; +- detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; +- detected_param |= inter_leave_len; +- } +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- u8 t; +- lgs8gxx_read_reg(priv, 0x19, &t); +- t &= 0x81; +- t |= detected_param << 1; +- lgs8gxx_write_reg(priv, 0x19, t); +- } else { +- lgs8gxx_write_reg(priv, 0x7D, detected_param); +- if (priv->config->prod == LGS8GXX_PROD_LGS8913) +- lgs8gxx_write_reg(priv, 0xC0, detected_param); +- } +- /* lgs8gxx_soft_reset(priv); */ +- +- /* Enter manual mode */ +- lgs8gxx_set_mode_manual(priv); +- +- switch (gi) { +- case GI_945: +- priv->curr_gi = 945; break; +- case GI_595: +- priv->curr_gi = 595; break; +- case GI_420: +- priv->curr_gi = 420; break; +- default: +- priv->curr_gi = 945; break; +- } +-} +- +-static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, +- u8 serial, u8 clk_pol, u8 clk_gated) +-{ +- int ret = 0; +- u8 t, reg_addr; +- +- reg_addr = (priv->config->prod == LGS8GXX_PROD_LGS8G75) ? 0x30 : 0xC2; +- ret = lgs8gxx_read_reg(priv, reg_addr, &t); +- if (ret != 0) +- return ret; +- +- t &= 0xF8; +- t |= serial ? TS_SERIAL : TS_PARALLEL; +- t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; +- t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; +- +- ret = lgs8gxx_write_reg(priv, reg_addr, t); +- if (ret != 0) +- return ret; +- +- return 0; +-} +- +-/* A/D input peak-to-peak voltage range */ +-static int lgs8g75_set_adc_vpp(struct lgs8gxx_state *priv, +- u8 sel) +-{ +- u8 r26 = 0x73, r27 = 0x90; +- +- if (priv->config->prod != LGS8GXX_PROD_LGS8G75) +- return 0; +- +- r26 |= (sel & 0x01) << 7; +- r27 |= (sel & 0x02) >> 1; +- lgs8gxx_write_reg(priv, 0x26, r26); +- lgs8gxx_write_reg(priv, 0x27, r27); +- +- return 0; +-} +- +-/* LGS8913 demod frontend functions */ +- +-static int lgs8913_init(struct lgs8gxx_state *priv) +-{ +- u8 t; +- +- /* LGS8913 specific */ +- lgs8gxx_write_reg(priv, 0xc1, 0x3); +- +- lgs8gxx_read_reg(priv, 0x7c, &t); +- lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); +- +- /* LGS8913 specific */ +- lgs8gxx_read_reg(priv, 0xc3, &t); +- lgs8gxx_write_reg(priv, 0xc3, t&0x10); +- +- +- return 0; +-} +- +-static int lgs8g75_init_data(struct lgs8gxx_state *priv) +-{ +- const struct firmware *fw; +- int rc; +- int i; +- +- rc = request_firmware(&fw, "lgs8g75.fw", &priv->i2c->dev); +- if (rc) +- return rc; +- +- lgs8gxx_write_reg(priv, 0xC6, 0x40); +- +- lgs8gxx_write_reg(priv, 0x3D, 0x04); +- lgs8gxx_write_reg(priv, 0x39, 0x00); +- +- lgs8gxx_write_reg(priv, 0x3A, 0x00); +- lgs8gxx_write_reg(priv, 0x38, 0x00); +- lgs8gxx_write_reg(priv, 0x3B, 0x00); +- lgs8gxx_write_reg(priv, 0x38, 0x00); +- +- for (i = 0; i < fw->size; i++) { +- lgs8gxx_write_reg(priv, 0x38, 0x00); +- lgs8gxx_write_reg(priv, 0x3A, (u8)(i&0xff)); +- lgs8gxx_write_reg(priv, 0x3B, (u8)(i>>8)); +- lgs8gxx_write_reg(priv, 0x3C, fw->data[i]); +- } +- +- lgs8gxx_write_reg(priv, 0x38, 0x00); +- +- release_firmware(fw); +- return 0; +-} +- +-static int lgs8gxx_init(struct dvb_frontend *fe) +-{ +- struct lgs8gxx_state *priv = +- (struct lgs8gxx_state *)fe->demodulator_priv; +- const struct lgs8gxx_config *config = priv->config; +- u8 data = 0; +- s8 err; +- dprintk("%s\n", __func__); +- +- lgs8gxx_read_reg(priv, 0, &data); +- dprintk("reg 0 = 0x%02X\n", data); +- +- if (config->prod == LGS8GXX_PROD_LGS8G75) +- lgs8g75_set_adc_vpp(priv, config->adc_vpp); +- +- /* Setup MPEG output format */ +- err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, +- config->ts_clk_pol, +- config->ts_clk_gated); +- if (err != 0) +- return -EIO; +- +- if (config->prod == LGS8GXX_PROD_LGS8913) +- lgs8913_init(priv); +- lgs8gxx_set_if_freq(priv, priv->config->if_freq); +- lgs8gxx_set_ad_mode(priv); +- +- return 0; +-} +- +-static void lgs8gxx_release(struct dvb_frontend *fe) +-{ +- struct lgs8gxx_state *state = fe->demodulator_priv; +- dprintk("%s\n", __func__); +- +- kfree(state); +-} +- +- +-static int lgs8gxx_write(struct dvb_frontend *fe, const u8 buf[], int len) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- +- if (len != 2) +- return -EINVAL; +- +- return lgs8gxx_write_reg(priv, buf[0], buf[1]); +-} +- +-static int lgs8gxx_set_fe(struct dvb_frontend *fe) +-{ +- +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- /* set frequency */ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* start auto lock */ +- lgs8gxx_auto_lock(priv); +- +- msleep(10); +- +- return 0; +-} +- +-static int lgs8gxx_get_fe(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; +- dprintk("%s\n", __func__); +- +- /* TODO: get real readings from device */ +- /* inversion status */ +- fe_params->inversion = INVERSION_OFF; +- +- /* bandwidth */ +- fe_params->bandwidth_hz = 8000000; +- +- fe_params->code_rate_HP = FEC_AUTO; +- fe_params->code_rate_LP = FEC_AUTO; +- +- fe_params->modulation = QAM_AUTO; +- +- /* transmission mode */ +- fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; +- +- /* guard interval */ +- fe_params->guard_interval = GUARD_INTERVAL_AUTO; +- +- /* hierarchy */ +- fe_params->hierarchy = HIERARCHY_NONE; +- +- return 0; +-} +- +-static +-int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- /* FIXME: copy from tda1004x.c */ +- fesettings->min_delay_ms = 800; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- s8 ret; +- u8 t, locked = 0; +- +- dprintk("%s\n", __func__); +- *fe_status = 0; +- +- lgs8gxx_get_afc_phase(priv); +- lgs8gxx_is_locked(priv, &locked); +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- if (locked) +- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- return 0; +- } +- +- ret = lgs8gxx_read_reg(priv, 0x4B, &t); +- if (ret != 0) +- return -EIO; +- +- dprintk("Reg 0x4B: 0x%02X\n", t); +- +- *fe_status = 0; +- if (priv->config->prod == LGS8GXX_PROD_LGS8913) { +- if ((t & 0x40) == 0x40) +- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; +- if ((t & 0x80) == 0x80) +- *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | +- FE_HAS_LOCK; +- } else { +- if ((t & 0x80) == 0x80) +- *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | +- FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- } +- +- /* success */ +- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); +- return 0; +-} +- +-static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) +-{ +- u16 v; +- u8 agc_lvl[2], cat; +- +- dprintk("%s()\n", __func__); +- lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); +- lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); +- +- v = agc_lvl[0]; +- v <<= 8; +- v |= agc_lvl[1]; +- +- dprintk("agc_lvl: 0x%04X\n", v); +- +- if (v < 0x100) +- cat = 0; +- else if (v < 0x190) +- cat = 5; +- else if (v < 0x2A8) +- cat = 4; +- else if (v < 0x381) +- cat = 3; +- else if (v < 0x400) +- cat = 2; +- else if (v == 0x400) +- cat = 1; +- else +- cat = 0; +- +- *signal = cat * 65535 / 5; +- +- return 0; +-} +- +-static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +-{ +- u8 t; s8 ret; +- s16 max_strength = 0; +- u8 str; +- u16 i, gi = priv->curr_gi; +- +- dprintk("%s\n", __func__); +- +- ret = lgs8gxx_read_reg(priv, 0x4B, &t); +- if (ret != 0) +- return -EIO; +- +- if (fake_signal_str) { +- if ((t & 0xC0) == 0xC0) { +- dprintk("Fake signal strength\n"); +- *signal = 0x7FFF; +- } else +- *signal = 0; +- return 0; +- } +- +- dprintk("gi = %d\n", gi); +- for (i = 0; i < gi; i++) { +- +- if ((i & 0xFF) == 0) +- lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); +- lgs8gxx_write_reg(priv, 0x83, i & 0xFF); +- +- lgs8gxx_read_reg(priv, 0x94, &str); +- if (max_strength < str) +- max_strength = str; +- } +- +- *signal = max_strength; +- dprintk("%s: signal=0x%02X\n", __func__, *signal); +- +- lgs8gxx_read_reg(priv, 0x95, &t); +- dprintk("%s: AVG Noise=0x%02X\n", __func__, t); +- +- return 0; +-} +- +-static int lgs8g75_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +-{ +- u8 t; +- s16 v = 0; +- +- dprintk("%s\n", __func__); +- +- lgs8gxx_read_reg(priv, 0xB1, &t); +- v |= t; +- v <<= 8; +- lgs8gxx_read_reg(priv, 0xB0, &t); +- v |= t; +- +- *signal = v; +- dprintk("%s: signal=0x%02X\n", __func__, *signal); +- +- return 0; +-} +- +-static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8913) +- return lgs8913_read_signal_strength(priv, signal); +- else if (priv->config->prod == LGS8GXX_PROD_LGS8G75) +- return lgs8g75_read_signal_strength(priv, signal); +- else +- return lgs8gxx_read_signal_agc(priv, signal); +-} +- +-static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- u8 t; +- *snr = 0; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) +- lgs8gxx_read_reg(priv, 0x34, &t); +- else +- lgs8gxx_read_reg(priv, 0x95, &t); +- dprintk("AVG Noise=0x%02X\n", t); +- *snr = 256 - t; +- *snr <<= 8; +- dprintk("snr=0x%x\n", *snr); +- +- return 0; +-} +- +-static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- *ucblocks = 0; +- dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); +- return 0; +-} +- +-static void packet_counter_start(struct lgs8gxx_state *priv) +-{ +- u8 orig, t; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_read_reg(priv, 0x30, &orig); +- orig &= 0xE7; +- t = orig | 0x10; +- lgs8gxx_write_reg(priv, 0x30, t); +- t = orig | 0x18; +- lgs8gxx_write_reg(priv, 0x30, t); +- t = orig | 0x10; +- lgs8gxx_write_reg(priv, 0x30, t); +- } else { +- lgs8gxx_write_reg(priv, 0xC6, 0x01); +- lgs8gxx_write_reg(priv, 0xC6, 0x41); +- lgs8gxx_write_reg(priv, 0xC6, 0x01); +- } +-} +- +-static void packet_counter_stop(struct lgs8gxx_state *priv) +-{ +- u8 t; +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- lgs8gxx_read_reg(priv, 0x30, &t); +- t &= 0xE7; +- lgs8gxx_write_reg(priv, 0x30, t); +- } else { +- lgs8gxx_write_reg(priv, 0xC6, 0x81); +- } +-} +- +-static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- u8 reg_err, reg_total, t; +- u32 total_cnt = 0, err_cnt = 0; +- int i; +- +- dprintk("%s\n", __func__); +- +- packet_counter_start(priv); +- msleep(200); +- packet_counter_stop(priv); +- +- if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { +- reg_total = 0x28; reg_err = 0x2C; +- } else { +- reg_total = 0xD0; reg_err = 0xD4; +- } +- +- for (i = 0; i < 4; i++) { +- total_cnt <<= 8; +- lgs8gxx_read_reg(priv, reg_total+3-i, &t); +- total_cnt |= t; +- } +- for (i = 0; i < 4; i++) { +- err_cnt <<= 8; +- lgs8gxx_read_reg(priv, reg_err+3-i, &t); +- err_cnt |= t; +- } +- dprintk("error=%d total=%d\n", err_cnt, total_cnt); +- +- if (total_cnt == 0) +- *ber = 0; +- else +- *ber = err_cnt * 100 / total_cnt; +- +- dprintk("%s: ber=0x%x\n", __func__, *ber); +- return 0; +-} +- +-static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct lgs8gxx_state *priv = fe->demodulator_priv; +- +- if (priv->config->tuner_address == 0) +- return 0; +- if (enable) { +- u8 v = 0x80 | priv->config->tuner_address; +- return lgs8gxx_write_reg(priv, 0x01, v); +- } +- return lgs8gxx_write_reg(priv, 0x01, 0); +-} +- +-static struct dvb_frontend_ops lgs8gxx_ops = { +- .delsys = { SYS_DMBTH }, +- .info = { +- .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", +- .frequency_min = 474000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 10000, +- .caps = +- FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO +- }, +- +- .release = lgs8gxx_release, +- +- .init = lgs8gxx_init, +- .write = lgs8gxx_write, +- .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, +- +- .set_frontend = lgs8gxx_set_fe, +- .get_frontend = lgs8gxx_get_fe, +- .get_tune_settings = lgs8gxx_get_tune_settings, +- +- .read_status = lgs8gxx_read_status, +- .read_ber = lgs8gxx_read_ber, +- .read_signal_strength = lgs8gxx_read_signal_strength, +- .read_snr = lgs8gxx_read_snr, +- .read_ucblocks = lgs8gxx_read_ucblocks, +-}; +- +-struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, +- struct i2c_adapter *i2c) +-{ +- struct lgs8gxx_state *priv = NULL; +- u8 data = 0; +- +- dprintk("%s()\n", __func__); +- +- if (config == NULL || i2c == NULL) +- return NULL; +- +- priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); +- if (priv == NULL) +- goto error_out; +- +- priv->config = config; +- priv->i2c = i2c; +- +- /* check if the demod is there */ +- if (lgs8gxx_read_reg(priv, 0, &data) != 0) { +- dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", +- __func__, priv->config->demod_address); +- goto error_out; +- } +- +- lgs8gxx_read_reg(priv, 1, &data); +- +- memcpy(&priv->frontend.ops, &lgs8gxx_ops, +- sizeof(struct dvb_frontend_ops)); +- priv->frontend.demodulator_priv = priv; +- +- if (config->prod == LGS8GXX_PROD_LGS8G75) +- lgs8g75_init_data(priv); +- +- return &priv->frontend; +- +-error_out: +- dprintk("%s() error_out\n", __func__); +- kfree(priv); +- return NULL; +- +-} +-EXPORT_SYMBOL(lgs8gxx_attach); +- +-MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); +-MODULE_AUTHOR("David T. L. Wong "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/lgs8gxx.h b/drivers/media/dvb/frontends/lgs8gxx.h +deleted file mode 100644 +index 33c3c5e..0000000 +--- a/drivers/media/dvb/frontends/lgs8gxx.h ++++ /dev/null +@@ -1,95 +0,0 @@ +-/* +- * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator +- * LGS8913, LGS8GL5, LGS8G75 +- * experimental support LGS8G42, LGS8G52 +- * +- * Copyright (C) 2007-2009 David T.L. Wong +- * Copyright (C) 2008 Sirius International (Hong Kong) Limited +- * Timothy Lee (for initial work on LGS8GL5) +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef __LGS8GXX_H__ +-#define __LGS8GXX_H__ +- +-#include +-#include +- +-#define LGS8GXX_PROD_LGS8913 0 +-#define LGS8GXX_PROD_LGS8GL5 1 +-#define LGS8GXX_PROD_LGS8G42 3 +-#define LGS8GXX_PROD_LGS8G52 4 +-#define LGS8GXX_PROD_LGS8G54 5 +-#define LGS8GXX_PROD_LGS8G75 6 +- +-struct lgs8gxx_config { +- +- /* product type */ +- u8 prod; +- +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* parallel or serial transport stream */ +- u8 serial_ts; +- +- /* transport stream polarity*/ +- u8 ts_clk_pol; +- +- /* transport stream clock gated by ts_valid */ +- u8 ts_clk_gated; +- +- /* A/D Clock frequency */ +- u32 if_clk_freq; /* in kHz */ +- +- /* IF frequency */ +- u32 if_freq; /* in kHz */ +- +- /*Use External ADC*/ +- u8 ext_adc; +- +- /*External ADC output two's complement*/ +- u8 adc_signed; +- +- /*Sample IF data at falling edge of IF_CLK*/ +- u8 if_neg_edge; +- +- /*IF use Negative center frequency*/ +- u8 if_neg_center; +- +- /*8G75 internal ADC input range selection*/ +- /*0: 0.8Vpp, 1: 1.0Vpp, 2: 1.6Vpp, 3: 2.0Vpp*/ +- u8 adc_vpp; +- +- /* slave address and configuration of the tuner */ +- u8 tuner_address; +-}; +- +-#if defined(CONFIG_DVB_LGS8GXX) || \ +- (defined(CONFIG_DVB_LGS8GXX_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline +-struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, +- struct i2c_adapter *i2c) { +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_LGS8GXX */ +- +-#endif /* __LGS8GXX_H__ */ +diff --git a/drivers/media/dvb/frontends/lgs8gxx_priv.h b/drivers/media/dvb/frontends/lgs8gxx_priv.h +deleted file mode 100644 +index 8ef376f..0000000 +--- a/drivers/media/dvb/frontends/lgs8gxx_priv.h ++++ /dev/null +@@ -1,70 +0,0 @@ +-/* +- * Support for Legend Silicon GB20600 (a.k.a DMB-TH) demodulator +- * LGS8913, LGS8GL5, LGS8G75 +- * experimental support LGS8G42, LGS8G52 +- * +- * Copyright (C) 2007-2009 David T.L. Wong +- * Copyright (C) 2008 Sirius International (Hong Kong) Limited +- * Timothy Lee (for initial work on LGS8GL5) +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef LGS8913_PRIV_H +-#define LGS8913_PRIV_H +- +-struct lgs8gxx_state { +- struct i2c_adapter *i2c; +- /* configuration settings */ +- const struct lgs8gxx_config *config; +- struct dvb_frontend frontend; +- u16 curr_gi; /* current guard interval */ +-}; +- +-#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */ +-#define SC_QAM64 0x10 /* 64QAM modulation */ +-#define SC_QAM32 0x0C /* 32QAM modulation */ +-#define SC_QAM16 0x08 /* 16QAM modulation */ +-#define SC_QAM4NR 0x04 /* 4QAM-NR modulation */ +-#define SC_QAM4 0x00 /* 4QAM modulation */ +- +-#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ +-#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */ +-#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */ +-#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ +- +-#define TIM_MASK 0x20 /* Time Interleave Length Mask */ +-#define TIM_LONG 0x20 /* Time Interleave Length = 720 */ +-#define TIM_MIDDLE 0x00 /* Time Interleave Length = 240 */ +- +-#define CF_MASK 0x80 /* Control Frame Mask */ +-#define CF_EN 0x80 /* Control Frame On */ +- +-#define GI_MASK 0x03 /* Guard Interval Mask */ +-#define GI_420 0x00 /* 1/9 Guard Interval */ +-#define GI_595 0x01 /* */ +-#define GI_945 0x02 /* 1/4 Guard Interval */ +- +- +-#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */ +-#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */ +-#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */ +-#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */ +-#define TS_CLK_GATED 0x00 /* MPEG clock gated */ +-#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/ +- +- +-#endif +diff --git a/drivers/media/dvb/frontends/lnbh24.h b/drivers/media/dvb/frontends/lnbh24.h +deleted file mode 100644 +index c059b16..0000000 +--- a/drivers/media/dvb/frontends/lnbh24.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- * lnbh24.h - driver for lnb supply and control ic lnbh24 +- * +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _LNBH24_H +-#define _LNBH24_H +- +-/* system register bits */ +-#define LNBH24_OLF 0x01 +-#define LNBH24_OTF 0x02 +-#define LNBH24_EN 0x04 +-#define LNBH24_VSEL 0x08 +-#define LNBH24_LLC 0x10 +-#define LNBH24_TEN 0x20 +-#define LNBH24_TTX 0x40 +-#define LNBH24_PCL 0x80 +- +-#include +- +-#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ +- && defined(MODULE)) +-/* override_set and override_clear control which +- system register bits (above) to always set & clear */ +-extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear, u8 i2c_addr); +-#else +-static inline struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear, u8 i2c_addr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c +deleted file mode 100644 +index 1343725..0000000 +--- a/drivers/media/dvb/frontends/lnbp21.c ++++ /dev/null +@@ -1,188 +0,0 @@ +-/* +- * lnbp21.c - driver for lnb supply and control ic lnbp21 +- * +- * Copyright (C) 2006, 2009 Oliver Endriss +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "lnbp21.h" +-#include "lnbh24.h" +- +-struct lnbp21 { +- u8 config; +- u8 override_or; +- u8 override_and; +- struct i2c_adapter *i2c; +- u8 i2c_addr; +-}; +- +-static int lnbp21_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage) +-{ +- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, +- .buf = &lnbp21->config, +- .len = sizeof(lnbp21->config) }; +- +- lnbp21->config &= ~(LNBP21_VSEL | LNBP21_EN); +- +- switch(voltage) { +- case SEC_VOLTAGE_OFF: +- break; +- case SEC_VOLTAGE_13: +- lnbp21->config |= LNBP21_EN; +- break; +- case SEC_VOLTAGE_18: +- lnbp21->config |= (LNBP21_EN | LNBP21_VSEL); +- break; +- default: +- return -EINVAL; +- }; +- +- lnbp21->config |= lnbp21->override_or; +- lnbp21->config &= lnbp21->override_and; +- +- return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +-{ +- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, +- .buf = &lnbp21->config, +- .len = sizeof(lnbp21->config) }; +- +- if (arg) +- lnbp21->config |= LNBP21_LLC; +- else +- lnbp21->config &= ~LNBP21_LLC; +- +- lnbp21->config |= lnbp21->override_or; +- lnbp21->config &= lnbp21->override_and; +- +- return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static int lnbp21_set_tone(struct dvb_frontend *fe, +- fe_sec_tone_mode_t tone) +-{ +- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv; +- struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0, +- .buf = &lnbp21->config, +- .len = sizeof(lnbp21->config) }; +- +- switch (tone) { +- case SEC_TONE_OFF: +- lnbp21->config &= ~LNBP21_TEN; +- break; +- case SEC_TONE_ON: +- lnbp21->config |= LNBP21_TEN; +- break; +- default: +- return -EINVAL; +- }; +- +- lnbp21->config |= lnbp21->override_or; +- lnbp21->config &= lnbp21->override_and; +- +- return (i2c_transfer(lnbp21->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static void lnbp21_release(struct dvb_frontend *fe) +-{ +- /* LNBP power off */ +- lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- /* free data */ +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear, u8 i2c_addr, u8 config) +-{ +- struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL); +- if (!lnbp21) +- return NULL; +- +- /* default configuration */ +- lnbp21->config = config; +- lnbp21->i2c = i2c; +- lnbp21->i2c_addr = i2c_addr; +- fe->sec_priv = lnbp21; +- +- /* bits which should be forced to '1' */ +- lnbp21->override_or = override_set; +- +- /* bits which should be forced to '0' */ +- lnbp21->override_and = ~override_clear; +- +- /* detect if it is present or not */ +- if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) { +- kfree(lnbp21); +- return NULL; +- } +- +- /* install release callback */ +- fe->ops.release_sec = lnbp21_release; +- +- /* override frontend ops */ +- fe->ops.set_voltage = lnbp21_set_voltage; +- fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; +- if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/ +- fe->ops.set_tone = lnbp21_set_tone; +- printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); +- +- return fe; +-} +- +-struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear, u8 i2c_addr) +-{ +- return lnbx2x_attach(fe, i2c, override_set, override_clear, +- i2c_addr, LNBH24_TTX); +-} +-EXPORT_SYMBOL(lnbh24_attach); +- +-struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear) +-{ +- return lnbx2x_attach(fe, i2c, override_set, override_clear, +- 0x08, LNBP21_ISEL); +-} +-EXPORT_SYMBOL(lnbp21_attach); +- +-MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24"); +-MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb/frontends/lnbp21.h +deleted file mode 100644 +index fcdf1c6..0000000 +--- a/drivers/media/dvb/frontends/lnbp21.h ++++ /dev/null +@@ -1,75 +0,0 @@ +-/* +- * lnbp21.h - driver for lnb supply and control ic lnbp21 +- * +- * Copyright (C) 2006 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef _LNBP21_H +-#define _LNBP21_H +- +-/* system register bits */ +-/* [RO] 0=OK; 1=over current limit flag */ +-#define LNBP21_OLF 0x01 +-/* [RO] 0=OK; 1=over temperature flag (150 C) */ +-#define LNBP21_OTF 0x02 +-/* [RW] 0=disable LNB power, enable loopthrough +- 1=enable LNB power, disable loopthrough */ +-#define LNBP21_EN 0x04 +-/* [RW] 0=low voltage (13/14V, vert pol) +- 1=high voltage (18/19V,horiz pol) */ +-#define LNBP21_VSEL 0x08 +-/* [RW] increase LNB voltage by 1V: +- 0=13/18V; 1=14/19V */ +-#define LNBP21_LLC 0x10 +-/* [RW] 0=tone controlled by DSQIN pin +- 1=tone enable, disable DSQIN */ +-#define LNBP21_TEN 0x20 +-/* [RW] current limit select: +- 0:Iout=500-650mA Isc=300mA +- 1:Iout=400-550mA Isc=200mA */ +-#define LNBP21_ISEL 0x40 +-/* [RW] short-circuit protect: +- 0=pulsed (dynamic) curr limiting +- 1=static curr limiting */ +-#define LNBP21_PCL 0x80 +- +-#include +- +-#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \ +- && defined(MODULE)) +-/* override_set and override_clear control which +- system register bits (above) to always set & clear */ +-extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear); +-#else +-static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 override_set, +- u8 override_clear) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/lnbp22.c b/drivers/media/dvb/frontends/lnbp22.c +deleted file mode 100644 +index 84ad039..0000000 +--- a/drivers/media/dvb/frontends/lnbp22.c ++++ /dev/null +@@ -1,148 +0,0 @@ +-/* +- * lnbp22.h - driver for lnb supply and control ic lnbp22 +- * +- * Copyright (C) 2006 Dominik Kuhlen +- * Based on lnbp21 driver +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "lnbp22.h" +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +- +-#define dprintk(lvl, arg...) if (debug >= (lvl)) printk(arg) +- +-struct lnbp22 { +- u8 config[4]; +- struct i2c_adapter *i2c; +-}; +- +-static int lnbp22_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct lnbp22 *lnbp22 = (struct lnbp22 *)fe->sec_priv; +- struct i2c_msg msg = { +- .addr = 0x08, +- .flags = 0, +- .buf = (char *)&lnbp22->config, +- .len = sizeof(lnbp22->config), +- }; +- +- dprintk(1, "%s: %d (18V=%d 13V=%d)\n", __func__, voltage, +- SEC_VOLTAGE_18, SEC_VOLTAGE_13); +- +- lnbp22->config[3] = 0x60; /* Power down */ +- switch (voltage) { +- case SEC_VOLTAGE_OFF: +- break; +- case SEC_VOLTAGE_13: +- lnbp22->config[3] |= LNBP22_EN; +- break; +- case SEC_VOLTAGE_18: +- lnbp22->config[3] |= (LNBP22_EN | LNBP22_VSEL); +- break; +- default: +- return -EINVAL; +- }; +- +- dprintk(1, "%s: 0x%02x)\n", __func__, lnbp22->config[3]); +- return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static int lnbp22_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg) +-{ +- struct lnbp22 *lnbp22 = (struct lnbp22 *) fe->sec_priv; +- struct i2c_msg msg = { +- .addr = 0x08, +- .flags = 0, +- .buf = (char *)&lnbp22->config, +- .len = sizeof(lnbp22->config), +- }; +- +- dprintk(1, "%s: %d\n", __func__, (int)arg); +- if (arg) +- lnbp22->config[3] |= LNBP22_LLC; +- else +- lnbp22->config[3] &= ~LNBP22_LLC; +- +- return (i2c_transfer(lnbp22->i2c, &msg, 1) == 1) ? 0 : -EIO; +-} +- +-static void lnbp22_release(struct dvb_frontend *fe) +-{ +- dprintk(1, "%s\n", __func__); +- /* LNBP power off */ +- lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF); +- +- /* free data */ +- kfree(fe->sec_priv); +- fe->sec_priv = NULL; +-} +- +-struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c) +-{ +- struct lnbp22 *lnbp22 = kmalloc(sizeof(struct lnbp22), GFP_KERNEL); +- if (!lnbp22) +- return NULL; +- +- /* default configuration */ +- lnbp22->config[0] = 0x00; /* ? */ +- lnbp22->config[1] = 0x28; /* ? */ +- lnbp22->config[2] = 0x48; /* ? */ +- lnbp22->config[3] = 0x60; /* Power down */ +- lnbp22->i2c = i2c; +- fe->sec_priv = lnbp22; +- +- /* detect if it is present or not */ +- if (lnbp22_set_voltage(fe, SEC_VOLTAGE_OFF)) { +- dprintk(0, "%s LNBP22 not found\n", __func__); +- kfree(lnbp22); +- fe->sec_priv = NULL; +- return NULL; +- } +- +- /* install release callback */ +- fe->ops.release_sec = lnbp22_release; +- +- /* override frontend ops */ +- fe->ops.set_voltage = lnbp22_set_voltage; +- fe->ops.enable_high_lnb_voltage = lnbp22_enable_high_lnb_voltage; +- +- return fe; +-} +-EXPORT_SYMBOL(lnbp22_attach); +- +-MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); +-MODULE_AUTHOR("Dominik Kuhlen"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/lnbp22.h b/drivers/media/dvb/frontends/lnbp22.h +deleted file mode 100644 +index 63e2dec..0000000 +--- a/drivers/media/dvb/frontends/lnbp22.h ++++ /dev/null +@@ -1,57 +0,0 @@ +-/* +- * lnbp22.h - driver for lnb supply and control ic lnbp22 +- * +- * Copyright (C) 2006 Dominik Kuhlen +- * Based on lnbp21.h +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org +- */ +- +-#ifndef _LNBP22_H +-#define _LNBP22_H +- +-/* Enable */ +-#define LNBP22_EN 0x10 +-/* Voltage selection */ +-#define LNBP22_VSEL 0x02 +-/* Plus 1 Volt Bit */ +-#define LNBP22_LLC 0x01 +- +-#include +- +-#if defined(CONFIG_DVB_LNBP22) || \ +- (defined(CONFIG_DVB_LNBP22_MODULE) && defined(MODULE)) +-/* +- * override_set and override_clear control which system register bits (above) +- * to always set & clear +- */ +-extern struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_LNBP22 */ +- +-#endif /* _LNBP22_H */ +diff --git a/drivers/media/dvb/frontends/mb86a16.c b/drivers/media/dvb/frontends/mb86a16.c +deleted file mode 100644 +index 9ae40ab..0000000 +--- a/drivers/media/dvb/frontends/mb86a16.c ++++ /dev/null +@@ -1,1878 +0,0 @@ +-/* +- Fujitsu MB86A16 DVB-S/DSS DC Receiver driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "mb86a16.h" +-#include "mb86a16_priv.h" +- +-unsigned int verbose = 5; +-module_param(verbose, int, 0644); +- +-#define ABS(x) ((x) < 0 ? (-x) : (x)) +- +-struct mb86a16_state { +- struct i2c_adapter *i2c_adap; +- const struct mb86a16_config *config; +- struct dvb_frontend frontend; +- +- /* tuning parameters */ +- int frequency; +- int srate; +- +- /* Internal stuff */ +- int master_clk; +- int deci; +- int csel; +- int rsel; +-}; +- +-#define MB86A16_ERROR 0 +-#define MB86A16_NOTICE 1 +-#define MB86A16_INFO 2 +-#define MB86A16_DEBUG 3 +- +-#define dprintk(x, y, z, format, arg...) do { \ +- if (z) { \ +- if ((x > MB86A16_ERROR) && (x > y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__, ##arg); \ +- else if ((x > MB86A16_NOTICE) && (x > y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__, ##arg); \ +- else if ((x > MB86A16_INFO) && (x > y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__, ##arg); \ +- else if ((x > MB86A16_DEBUG) && (x > y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__, ##arg); \ +- } else { \ +- if (x > y) \ +- printk(format, ##arg); \ +- } \ +-} while (0) +- +-#define TRACE_IN dprintk(verbose, MB86A16_DEBUG, 1, "-->()") +-#define TRACE_OUT dprintk(verbose, MB86A16_DEBUG, 1, "()-->") +- +-static int mb86a16_write(struct mb86a16_state *state, u8 reg, u8 val) +-{ +- int ret; +- u8 buf[] = { reg, val }; +- +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 +- }; +- +- dprintk(verbose, MB86A16_DEBUG, 1, +- "writing to [0x%02x],Reg[0x%02x],Data[0x%02x]", +- state->config->demod_address, buf[0], buf[1]); +- +- ret = i2c_transfer(state->i2c_adap, &msg, 1); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int mb86a16_read(struct mb86a16_state *state, u8 reg, u8 *val) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- ret = i2c_transfer(state->i2c_adap, msg, 2); +- if (ret != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "read error(reg=0x%02x, ret=0x%i)", +- reg, ret); +- +- return -EREMOTEIO; +- } +- *val = b1[0]; +- +- return ret; +-} +- +-static int CNTM_set(struct mb86a16_state *state, +- unsigned char timint1, +- unsigned char timint2, +- unsigned char cnext) +-{ +- unsigned char val; +- +- val = (timint1 << 4) | (timint2 << 2) | cnext; +- if (mb86a16_write(state, MB86A16_CNTMR, val) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int smrt_set(struct mb86a16_state *state, int rate) +-{ +- int tmp ; +- int m ; +- unsigned char STOFS0, STOFS1; +- +- m = 1 << state->deci; +- tmp = (8192 * state->master_clk - 2 * m * rate * 8192 + state->master_clk / 2) / state->master_clk; +- +- STOFS0 = tmp & 0x0ff; +- STOFS1 = (tmp & 0xf00) >> 8; +- +- if (mb86a16_write(state, MB86A16_SRATE1, (state->deci << 2) | +- (state->csel << 1) | +- state->rsel) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_SRATE2, STOFS0) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_SRATE3, STOFS1) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -1; +-} +- +-static int srst(struct mb86a16_state *state) +-{ +- if (mb86a16_write(state, MB86A16_RESET, 0x04) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- +-} +- +-static int afcex_data_set(struct mb86a16_state *state, +- unsigned char AFCEX_L, +- unsigned char AFCEX_H) +-{ +- if (mb86a16_write(state, MB86A16_AFCEXL, AFCEX_L) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_AFCEXH, AFCEX_H) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- +- return -1; +-} +- +-static int afcofs_data_set(struct mb86a16_state *state, +- unsigned char AFCEX_L, +- unsigned char AFCEX_H) +-{ +- if (mb86a16_write(state, 0x58, AFCEX_L) < 0) +- goto err; +- if (mb86a16_write(state, 0x59, AFCEX_H) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int stlp_set(struct mb86a16_state *state, +- unsigned char STRAS, +- unsigned char STRBS) +-{ +- if (mb86a16_write(state, MB86A16_STRFILTCOEF1, (STRBS << 3) | (STRAS)) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int Vi_set(struct mb86a16_state *state, unsigned char ETH, unsigned char VIA) +-{ +- if (mb86a16_write(state, MB86A16_VISET2, 0x04) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_VISET3, 0xf5) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int initial_set(struct mb86a16_state *state) +-{ +- if (stlp_set(state, 5, 7)) +- goto err; +- +- udelay(100); +- if (afcex_data_set(state, 0, 0)) +- goto err; +- +- udelay(100); +- if (afcofs_data_set(state, 0, 0)) +- goto err; +- +- udelay(100); +- if (mb86a16_write(state, MB86A16_CRLFILTCOEF1, 0x16) < 0) +- goto err; +- if (mb86a16_write(state, 0x2f, 0x21) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_VIMAG, 0x38) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_FAGCS1, 0x00) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_FAGCS2, 0x1c) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_FAGCS3, 0x20) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_FAGCS4, 0x1e) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_FAGCS5, 0x23) < 0) +- goto err; +- if (mb86a16_write(state, 0x54, 0xff) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_TSOUT, 0x00) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int S01T_set(struct mb86a16_state *state, +- unsigned char s1t, +- unsigned s0t) +-{ +- if (mb86a16_write(state, 0x33, (s1t << 3) | s0t) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +- +-static int EN_set(struct mb86a16_state *state, +- int cren, +- int afcen) +-{ +- unsigned char val; +- +- val = 0x7a | (cren << 7) | (afcen << 2); +- if (mb86a16_write(state, 0x49, val) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int AFCEXEN_set(struct mb86a16_state *state, +- int afcexen, +- int smrt) +-{ +- unsigned char AFCA ; +- +- if (smrt > 18875) +- AFCA = 4; +- else if (smrt > 9375) +- AFCA = 3; +- else if (smrt > 2250) +- AFCA = 2; +- else +- AFCA = 1; +- +- if (mb86a16_write(state, 0x2a, 0x02 | (afcexen << 5) | (AFCA << 2)) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int DAGC_data_set(struct mb86a16_state *state, +- unsigned char DAGCA, +- unsigned char DAGCW) +-{ +- if (mb86a16_write(state, 0x2d, (DAGCA << 3) | DAGCW) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static void smrt_info_get(struct mb86a16_state *state, int rate) +-{ +- if (rate >= 37501) { +- state->deci = 0; state->csel = 0; state->rsel = 0; +- } else if (rate >= 30001) { +- state->deci = 0; state->csel = 0; state->rsel = 1; +- } else if (rate >= 26251) { +- state->deci = 0; state->csel = 1; state->rsel = 0; +- } else if (rate >= 22501) { +- state->deci = 0; state->csel = 1; state->rsel = 1; +- } else if (rate >= 18751) { +- state->deci = 1; state->csel = 0; state->rsel = 0; +- } else if (rate >= 15001) { +- state->deci = 1; state->csel = 0; state->rsel = 1; +- } else if (rate >= 13126) { +- state->deci = 1; state->csel = 1; state->rsel = 0; +- } else if (rate >= 11251) { +- state->deci = 1; state->csel = 1; state->rsel = 1; +- } else if (rate >= 9376) { +- state->deci = 2; state->csel = 0; state->rsel = 0; +- } else if (rate >= 7501) { +- state->deci = 2; state->csel = 0; state->rsel = 1; +- } else if (rate >= 6563) { +- state->deci = 2; state->csel = 1; state->rsel = 0; +- } else if (rate >= 5626) { +- state->deci = 2; state->csel = 1; state->rsel = 1; +- } else if (rate >= 4688) { +- state->deci = 3; state->csel = 0; state->rsel = 0; +- } else if (rate >= 3751) { +- state->deci = 3; state->csel = 0; state->rsel = 1; +- } else if (rate >= 3282) { +- state->deci = 3; state->csel = 1; state->rsel = 0; +- } else if (rate >= 2814) { +- state->deci = 3; state->csel = 1; state->rsel = 1; +- } else if (rate >= 2344) { +- state->deci = 4; state->csel = 0; state->rsel = 0; +- } else if (rate >= 1876) { +- state->deci = 4; state->csel = 0; state->rsel = 1; +- } else if (rate >= 1641) { +- state->deci = 4; state->csel = 1; state->rsel = 0; +- } else if (rate >= 1407) { +- state->deci = 4; state->csel = 1; state->rsel = 1; +- } else if (rate >= 1172) { +- state->deci = 5; state->csel = 0; state->rsel = 0; +- } else if (rate >= 939) { +- state->deci = 5; state->csel = 0; state->rsel = 1; +- } else if (rate >= 821) { +- state->deci = 5; state->csel = 1; state->rsel = 0; +- } else { +- state->deci = 5; state->csel = 1; state->rsel = 1; +- } +- +- if (state->csel == 0) +- state->master_clk = 92000; +- else +- state->master_clk = 61333; +- +-} +- +-static int signal_det(struct mb86a16_state *state, +- int smrt, +- unsigned char *SIG) +-{ +- +- int ret ; +- int smrtd ; +- int wait_sym ; +- +- u32 wait_t; +- unsigned char S[3] ; +- int i ; +- +- if (*SIG > 45) { +- if (CNTM_set(state, 2, 1, 2) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); +- return -1; +- } +- wait_sym = 40000; +- } else { +- if (CNTM_set(state, 3, 1, 2) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); +- return -1; +- } +- wait_sym = 80000; +- } +- for (i = 0; i < 3; i++) { +- if (i == 0) +- smrtd = smrt * 98 / 100; +- else if (i == 1) +- smrtd = smrt; +- else +- smrtd = smrt * 102 / 100; +- smrt_info_get(state, smrtd); +- smrt_set(state, smrtd); +- srst(state); +- wait_t = (wait_sym + 99 * smrtd / 100) / smrtd; +- if (wait_t == 0) +- wait_t = 1; +- msleep_interruptible(10); +- if (mb86a16_read(state, 0x37, &(S[i])) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- } +- if ((S[1] > S[0] * 112 / 100) && +- (S[1] > S[2] * 112 / 100)) { +- +- ret = 1; +- } else { +- ret = 0; +- } +- *SIG = S[1]; +- +- if (CNTM_set(state, 0, 1, 2) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "CNTM set Error"); +- return -1; +- } +- +- return ret; +-} +- +-static int rf_val_set(struct mb86a16_state *state, +- int f, +- int smrt, +- unsigned char R) +-{ +- unsigned char C, F, B; +- int M; +- unsigned char rf_val[5]; +- int ack = -1; +- +- if (smrt > 37750) +- C = 1; +- else if (smrt > 18875) +- C = 2; +- else if (smrt > 5500) +- C = 3; +- else +- C = 4; +- +- if (smrt > 30500) +- F = 3; +- else if (smrt > 9375) +- F = 1; +- else if (smrt > 4625) +- F = 0; +- else +- F = 2; +- +- if (f < 1060) +- B = 0; +- else if (f < 1175) +- B = 1; +- else if (f < 1305) +- B = 2; +- else if (f < 1435) +- B = 3; +- else if (f < 1570) +- B = 4; +- else if (f < 1715) +- B = 5; +- else if (f < 1845) +- B = 6; +- else if (f < 1980) +- B = 7; +- else if (f < 2080) +- B = 8; +- else +- B = 9; +- +- M = f * (1 << R) / 2; +- +- rf_val[0] = 0x01 | (C << 3) | (F << 1); +- rf_val[1] = (R << 5) | ((M & 0x1f000) >> 12); +- rf_val[2] = (M & 0x00ff0) >> 4; +- rf_val[3] = ((M & 0x0000f) << 4) | B; +- +- /* Frequency Set */ +- if (mb86a16_write(state, 0x21, rf_val[0]) < 0) +- ack = 0; +- if (mb86a16_write(state, 0x22, rf_val[1]) < 0) +- ack = 0; +- if (mb86a16_write(state, 0x23, rf_val[2]) < 0) +- ack = 0; +- if (mb86a16_write(state, 0x24, rf_val[3]) < 0) +- ack = 0; +- if (mb86a16_write(state, 0x25, 0x01) < 0) +- ack = 0; +- if (ack == 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "RF Setup - I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int afcerr_chk(struct mb86a16_state *state) +-{ +- unsigned char AFCM_L, AFCM_H ; +- int AFCM ; +- int afcm, afcerr ; +- +- if (mb86a16_read(state, 0x0e, &AFCM_L) != 2) +- goto err; +- if (mb86a16_read(state, 0x0f, &AFCM_H) != 2) +- goto err; +- +- AFCM = (AFCM_H << 8) + AFCM_L; +- +- if (AFCM > 2048) +- afcm = AFCM - 4096; +- else +- afcm = AFCM; +- afcerr = afcm * state->master_clk / 8192; +- +- return afcerr; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int dagcm_val_get(struct mb86a16_state *state) +-{ +- int DAGCM; +- unsigned char DAGCM_H, DAGCM_L; +- +- if (mb86a16_read(state, 0x45, &DAGCM_L) != 2) +- goto err; +- if (mb86a16_read(state, 0x46, &DAGCM_H) != 2) +- goto err; +- +- DAGCM = (DAGCM_H << 8) + DAGCM_L; +- +- return DAGCM; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int mb86a16_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- u8 stat, stat2; +- struct mb86a16_state *state = fe->demodulator_priv; +- +- *status = 0; +- +- if (mb86a16_read(state, MB86A16_SIG1, &stat) != 2) +- goto err; +- if (mb86a16_read(state, MB86A16_SIG2, &stat2) != 2) +- goto err; +- if ((stat > 25) && (stat2 > 25)) +- *status |= FE_HAS_SIGNAL; +- if ((stat > 45) && (stat2 > 45)) +- *status |= FE_HAS_CARRIER; +- +- if (mb86a16_read(state, MB86A16_STATUS, &stat) != 2) +- goto err; +- +- if (stat & 0x01) +- *status |= FE_HAS_SYNC; +- if (stat & 0x01) +- *status |= FE_HAS_VITERBI; +- +- if (mb86a16_read(state, MB86A16_FRAMESYNC, &stat) != 2) +- goto err; +- +- if ((stat & 0x0f) && (*status & FE_HAS_VITERBI)) +- *status |= FE_HAS_LOCK; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int sync_chk(struct mb86a16_state *state, +- unsigned char *VIRM) +-{ +- unsigned char val; +- int sync; +- +- if (mb86a16_read(state, 0x0d, &val) != 2) +- goto err; +- +- dprintk(verbose, MB86A16_INFO, 1, "Status = %02x,", val); +- sync = val & 0x01; +- *VIRM = (val & 0x1c) >> 2; +- +- return sync; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- +-} +- +-static int freqerr_chk(struct mb86a16_state *state, +- int fTP, +- int smrt, +- int unit) +-{ +- unsigned char CRM, AFCML, AFCMH; +- unsigned char temp1, temp2, temp3; +- int crm, afcm, AFCM; +- int crrerr, afcerr; /* kHz */ +- int frqerr; /* MHz */ +- int afcen, afcexen = 0; +- int R, M, fOSC, fOSC_OFS; +- +- if (mb86a16_read(state, 0x43, &CRM) != 2) +- goto err; +- +- if (CRM > 127) +- crm = CRM - 256; +- else +- crm = CRM; +- +- crrerr = smrt * crm / 256; +- if (mb86a16_read(state, 0x49, &temp1) != 2) +- goto err; +- +- afcen = (temp1 & 0x04) >> 2; +- if (afcen == 0) { +- if (mb86a16_read(state, 0x2a, &temp1) != 2) +- goto err; +- afcexen = (temp1 & 0x20) >> 5; +- } +- +- if (afcen == 1) { +- if (mb86a16_read(state, 0x0e, &AFCML) != 2) +- goto err; +- if (mb86a16_read(state, 0x0f, &AFCMH) != 2) +- goto err; +- } else if (afcexen == 1) { +- if (mb86a16_read(state, 0x2b, &AFCML) != 2) +- goto err; +- if (mb86a16_read(state, 0x2c, &AFCMH) != 2) +- goto err; +- } +- if ((afcen == 1) || (afcexen == 1)) { +- smrt_info_get(state, smrt); +- AFCM = ((AFCMH & 0x01) << 8) + AFCML; +- if (AFCM > 255) +- afcm = AFCM - 512; +- else +- afcm = AFCM; +- +- afcerr = afcm * state->master_clk / 8192; +- } else +- afcerr = 0; +- +- if (mb86a16_read(state, 0x22, &temp1) != 2) +- goto err; +- if (mb86a16_read(state, 0x23, &temp2) != 2) +- goto err; +- if (mb86a16_read(state, 0x24, &temp3) != 2) +- goto err; +- +- R = (temp1 & 0xe0) >> 5; +- M = ((temp1 & 0x1f) << 12) + (temp2 << 4) + (temp3 >> 4); +- if (R == 0) +- fOSC = 2 * M; +- else +- fOSC = M; +- +- fOSC_OFS = fOSC - fTP; +- +- if (unit == 0) { /* MHz */ +- if (crrerr + afcerr + fOSC_OFS * 1000 >= 0) +- frqerr = (crrerr + afcerr + fOSC_OFS * 1000 + 500) / 1000; +- else +- frqerr = (crrerr + afcerr + fOSC_OFS * 1000 - 500) / 1000; +- } else { /* kHz */ +- frqerr = crrerr + afcerr + fOSC_OFS * 1000; +- } +- +- return frqerr; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static unsigned char vco_dev_get(struct mb86a16_state *state, int smrt) +-{ +- unsigned char R; +- +- if (smrt > 9375) +- R = 0; +- else +- R = 1; +- +- return R; +-} +- +-static void swp_info_get(struct mb86a16_state *state, +- int fOSC_start, +- int smrt, +- int v, int R, +- int swp_ofs, +- int *fOSC, +- int *afcex_freq, +- unsigned char *AFCEX_L, +- unsigned char *AFCEX_H) +-{ +- int AFCEX ; +- int crnt_swp_freq ; +- +- crnt_swp_freq = fOSC_start * 1000 + v * swp_ofs; +- +- if (R == 0) +- *fOSC = (crnt_swp_freq + 1000) / 2000 * 2; +- else +- *fOSC = (crnt_swp_freq + 500) / 1000; +- +- if (*fOSC >= crnt_swp_freq) +- *afcex_freq = *fOSC * 1000 - crnt_swp_freq; +- else +- *afcex_freq = crnt_swp_freq - *fOSC * 1000; +- +- AFCEX = *afcex_freq * 8192 / state->master_clk; +- *AFCEX_L = AFCEX & 0x00ff; +- *AFCEX_H = (AFCEX & 0x0f00) >> 8; +-} +- +- +-static int swp_freq_calcuation(struct mb86a16_state *state, int i, int v, int *V, int vmax, int vmin, +- int SIGMIN, int fOSC, int afcex_freq, int swp_ofs, unsigned char *SIG1) +-{ +- int swp_freq ; +- +- if ((i % 2 == 1) && (v <= vmax)) { +- /* positive v (case 1) */ +- if ((v - 1 == vmin) && +- (*(V + 30 + v) >= 0) && +- (*(V + 30 + v - 1) >= 0) && +- (*(V + 30 + v - 1) > *(V + 30 + v)) && +- (*(V + 30 + v - 1) > SIGMIN)) { +- +- swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; +- *SIG1 = *(V + 30 + v - 1); +- } else if ((v == vmax) && +- (*(V + 30 + v) >= 0) && +- (*(V + 30 + v - 1) >= 0) && +- (*(V + 30 + v) > *(V + 30 + v - 1)) && +- (*(V + 30 + v) > SIGMIN)) { +- /* (case 2) */ +- swp_freq = fOSC * 1000 + afcex_freq; +- *SIG1 = *(V + 30 + v); +- } else if ((*(V + 30 + v) > 0) && +- (*(V + 30 + v - 1) > 0) && +- (*(V + 30 + v - 2) > 0) && +- (*(V + 30 + v - 3) > 0) && +- (*(V + 30 + v - 1) > *(V + 30 + v)) && +- (*(V + 30 + v - 2) > *(V + 30 + v - 3)) && +- ((*(V + 30 + v - 1) > SIGMIN) || +- (*(V + 30 + v - 2) > SIGMIN))) { +- /* (case 3) */ +- if (*(V + 30 + v - 1) >= *(V + 30 + v - 2)) { +- swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; +- *SIG1 = *(V + 30 + v - 1); +- } else { +- swp_freq = fOSC * 1000 + afcex_freq - swp_ofs * 2; +- *SIG1 = *(V + 30 + v - 2); +- } +- } else if ((v == vmax) && +- (*(V + 30 + v) >= 0) && +- (*(V + 30 + v - 1) >= 0) && +- (*(V + 30 + v - 2) >= 0) && +- (*(V + 30 + v) > *(V + 30 + v - 2)) && +- (*(V + 30 + v - 1) > *(V + 30 + v - 2)) && +- ((*(V + 30 + v) > SIGMIN) || +- (*(V + 30 + v - 1) > SIGMIN))) { +- /* (case 4) */ +- if (*(V + 30 + v) >= *(V + 30 + v - 1)) { +- swp_freq = fOSC * 1000 + afcex_freq; +- *SIG1 = *(V + 30 + v); +- } else { +- swp_freq = fOSC * 1000 + afcex_freq - swp_ofs; +- *SIG1 = *(V + 30 + v - 1); +- } +- } else { +- swp_freq = -1 ; +- } +- } else if ((i % 2 == 0) && (v >= vmin)) { +- /* Negative v (case 1) */ +- if ((*(V + 30 + v) > 0) && +- (*(V + 30 + v + 1) > 0) && +- (*(V + 30 + v + 2) > 0) && +- (*(V + 30 + v + 1) > *(V + 30 + v)) && +- (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && +- (*(V + 30 + v + 1) > SIGMIN)) { +- +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; +- *SIG1 = *(V + 30 + v + 1); +- } else if ((v + 1 == vmax) && +- (*(V + 30 + v) >= 0) && +- (*(V + 30 + v + 1) >= 0) && +- (*(V + 30 + v + 1) > *(V + 30 + v)) && +- (*(V + 30 + v + 1) > SIGMIN)) { +- /* (case 2) */ +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; +- *SIG1 = *(V + 30 + v); +- } else if ((v == vmin) && +- (*(V + 30 + v) > 0) && +- (*(V + 30 + v + 1) > 0) && +- (*(V + 30 + v + 2) > 0) && +- (*(V + 30 + v) > *(V + 30 + v + 1)) && +- (*(V + 30 + v) > *(V + 30 + v + 2)) && +- (*(V + 30 + v) > SIGMIN)) { +- /* (case 3) */ +- swp_freq = fOSC * 1000 + afcex_freq; +- *SIG1 = *(V + 30 + v); +- } else if ((*(V + 30 + v) >= 0) && +- (*(V + 30 + v + 1) >= 0) && +- (*(V + 30 + v + 2) >= 0) && +- (*(V + 30 + v + 3) >= 0) && +- (*(V + 30 + v + 1) > *(V + 30 + v)) && +- (*(V + 30 + v + 2) > *(V + 30 + v + 3)) && +- ((*(V + 30 + v + 1) > SIGMIN) || +- (*(V + 30 + v + 2) > SIGMIN))) { +- /* (case 4) */ +- if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; +- *SIG1 = *(V + 30 + v + 1); +- } else { +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; +- *SIG1 = *(V + 30 + v + 2); +- } +- } else if ((*(V + 30 + v) >= 0) && +- (*(V + 30 + v + 1) >= 0) && +- (*(V + 30 + v + 2) >= 0) && +- (*(V + 30 + v + 3) >= 0) && +- (*(V + 30 + v) > *(V + 30 + v + 2)) && +- (*(V + 30 + v + 1) > *(V + 30 + v + 2)) && +- (*(V + 30 + v) > *(V + 30 + v + 3)) && +- (*(V + 30 + v + 1) > *(V + 30 + v + 3)) && +- ((*(V + 30 + v) > SIGMIN) || +- (*(V + 30 + v + 1) > SIGMIN))) { +- /* (case 5) */ +- if (*(V + 30 + v) >= *(V + 30 + v + 1)) { +- swp_freq = fOSC * 1000 + afcex_freq; +- *SIG1 = *(V + 30 + v); +- } else { +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; +- *SIG1 = *(V + 30 + v + 1); +- } +- } else if ((v + 2 == vmin) && +- (*(V + 30 + v) >= 0) && +- (*(V + 30 + v + 1) >= 0) && +- (*(V + 30 + v + 2) >= 0) && +- (*(V + 30 + v + 1) > *(V + 30 + v)) && +- (*(V + 30 + v + 2) > *(V + 30 + v)) && +- ((*(V + 30 + v + 1) > SIGMIN) || +- (*(V + 30 + v + 2) > SIGMIN))) { +- /* (case 6) */ +- if (*(V + 30 + v + 1) >= *(V + 30 + v + 2)) { +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs; +- *SIG1 = *(V + 30 + v + 1); +- } else { +- swp_freq = fOSC * 1000 + afcex_freq + swp_ofs * 2; +- *SIG1 = *(V + 30 + v + 2); +- } +- } else if ((vmax == 0) && (vmin == 0) && (*(V + 30 + v) > SIGMIN)) { +- swp_freq = fOSC * 1000; +- *SIG1 = *(V + 30 + v); +- } else +- swp_freq = -1; +- } else +- swp_freq = -1; +- +- return swp_freq; +-} +- +-static void swp_info_get2(struct mb86a16_state *state, +- int smrt, +- int R, +- int swp_freq, +- int *afcex_freq, +- int *fOSC, +- unsigned char *AFCEX_L, +- unsigned char *AFCEX_H) +-{ +- int AFCEX ; +- +- if (R == 0) +- *fOSC = (swp_freq + 1000) / 2000 * 2; +- else +- *fOSC = (swp_freq + 500) / 1000; +- +- if (*fOSC >= swp_freq) +- *afcex_freq = *fOSC * 1000 - swp_freq; +- else +- *afcex_freq = swp_freq - *fOSC * 1000; +- +- AFCEX = *afcex_freq * 8192 / state->master_clk; +- *AFCEX_L = AFCEX & 0x00ff; +- *AFCEX_H = (AFCEX & 0x0f00) >> 8; +-} +- +-static void afcex_info_get(struct mb86a16_state *state, +- int afcex_freq, +- unsigned char *AFCEX_L, +- unsigned char *AFCEX_H) +-{ +- int AFCEX ; +- +- AFCEX = afcex_freq * 8192 / state->master_clk; +- *AFCEX_L = AFCEX & 0x00ff; +- *AFCEX_H = (AFCEX & 0x0f00) >> 8; +-} +- +-static int SEQ_set(struct mb86a16_state *state, unsigned char loop) +-{ +- /* SLOCK0 = 0 */ +- if (mb86a16_write(state, 0x32, 0x02 | (loop << 2)) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int iq_vt_set(struct mb86a16_state *state, unsigned char IQINV) +-{ +- /* Viterbi Rate, IQ Settings */ +- if (mb86a16_write(state, 0x06, 0xdf | (IQINV << 5)) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int FEC_srst(struct mb86a16_state *state) +-{ +- if (mb86a16_write(state, MB86A16_RESET, 0x02) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int S2T_set(struct mb86a16_state *state, unsigned char S2T) +-{ +- if (mb86a16_write(state, 0x34, 0x70 | S2T) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int S45T_set(struct mb86a16_state *state, unsigned char S4T, unsigned char S5T) +-{ +- if (mb86a16_write(state, 0x35, 0x00 | (S5T << 4) | S4T) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +- +-static int mb86a16_set_fe(struct mb86a16_state *state) +-{ +- u8 agcval, cnmval; +- +- int i, j; +- int fOSC = 0; +- int fOSC_start = 0; +- int wait_t; +- int fcp; +- int swp_ofs; +- int V[60]; +- u8 SIG1MIN; +- +- unsigned char CREN, AFCEN, AFCEXEN; +- unsigned char SIG1; +- unsigned char TIMINT1, TIMINT2, TIMEXT; +- unsigned char S0T, S1T; +- unsigned char S2T; +-/* unsigned char S2T, S3T; */ +- unsigned char S4T, S5T; +- unsigned char AFCEX_L, AFCEX_H; +- unsigned char R; +- unsigned char VIRM; +- unsigned char ETH, VIA; +- unsigned char junk; +- +- int loop; +- int ftemp; +- int v, vmax, vmin; +- int vmax_his, vmin_his; +- int swp_freq, prev_swp_freq[20]; +- int prev_freq_num; +- int signal_dupl; +- int afcex_freq; +- int signal; +- int afcerr; +- int temp_freq, delta_freq; +- int dagcm[4]; +- int smrt_d; +-/* int freq_err; */ +- int n; +- int ret = -1; +- int sync; +- +- dprintk(verbose, MB86A16_INFO, 1, "freq=%d Mhz, symbrt=%d Ksps", state->frequency, state->srate); +- +- fcp = 3000; +- swp_ofs = state->srate / 4; +- +- for (i = 0; i < 60; i++) +- V[i] = -1; +- +- for (i = 0; i < 20; i++) +- prev_swp_freq[i] = 0; +- +- SIG1MIN = 25; +- +- for (n = 0; ((n < 3) && (ret == -1)); n++) { +- SEQ_set(state, 0); +- iq_vt_set(state, 0); +- +- CREN = 0; +- AFCEN = 0; +- AFCEXEN = 1; +- TIMINT1 = 0; +- TIMINT2 = 1; +- TIMEXT = 2; +- S1T = 0; +- S0T = 0; +- +- if (initial_set(state) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "initial set failed"); +- return -1; +- } +- if (DAGC_data_set(state, 3, 2) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); +- return -1; +- } +- if (EN_set(state, CREN, AFCEN) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); +- return -1; /* (0, 0) */ +- } +- if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); +- return -1; /* (1, smrt) = (1, symbolrate) */ +- } +- if (CNTM_set(state, TIMINT1, TIMINT2, TIMEXT) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "CNTM set error"); +- return -1; /* (0, 1, 2) */ +- } +- if (S01T_set(state, S1T, S0T) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); +- return -1; /* (0, 0) */ +- } +- smrt_info_get(state, state->srate); +- if (smrt_set(state, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "smrt info get error"); +- return -1; +- } +- +- R = vco_dev_get(state, state->srate); +- if (R == 1) +- fOSC_start = state->frequency; +- +- else if (R == 0) { +- if (state->frequency % 2 == 0) { +- fOSC_start = state->frequency; +- } else { +- fOSC_start = state->frequency + 1; +- if (fOSC_start > 2150) +- fOSC_start = state->frequency - 1; +- } +- } +- loop = 1; +- ftemp = fOSC_start * 1000; +- vmax = 0 ; +- while (loop == 1) { +- ftemp = ftemp + swp_ofs; +- vmax++; +- +- /* Upper bound */ +- if (ftemp > 2150000) { +- loop = 0; +- vmax--; +- } else { +- if ((ftemp == 2150000) || +- (ftemp - state->frequency * 1000 >= fcp + state->srate / 4)) +- loop = 0; +- } +- } +- +- loop = 1; +- ftemp = fOSC_start * 1000; +- vmin = 0 ; +- while (loop == 1) { +- ftemp = ftemp - swp_ofs; +- vmin--; +- +- /* Lower bound */ +- if (ftemp < 950000) { +- loop = 0; +- vmin++; +- } else { +- if ((ftemp == 950000) || +- (state->frequency * 1000 - ftemp >= fcp + state->srate / 4)) +- loop = 0; +- } +- } +- +- wait_t = (8000 + state->srate / 2) / state->srate; +- if (wait_t == 0) +- wait_t = 1; +- +- i = 0; +- j = 0; +- prev_freq_num = 0; +- loop = 1; +- signal = 0; +- vmax_his = 0; +- vmin_his = 0; +- v = 0; +- +- while (loop == 1) { +- swp_info_get(state, fOSC_start, state->srate, +- v, R, swp_ofs, &fOSC, +- &afcex_freq, &AFCEX_L, &AFCEX_H); +- +- udelay(100); +- if (rf_val_set(state, fOSC, state->srate, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); +- return -1; +- } +- udelay(100); +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); +- return -1; +- } +- if (srst(state) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "srst error"); +- return -1; +- } +- msleep_interruptible(wait_t); +- +- if (mb86a16_read(state, 0x37, &SIG1) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -1; +- } +- V[30 + v] = SIG1 ; +- swp_freq = swp_freq_calcuation(state, i, v, V, vmax, vmin, +- SIG1MIN, fOSC, afcex_freq, +- swp_ofs, &SIG1); /* changed */ +- +- signal_dupl = 0; +- for (j = 0; j < prev_freq_num; j++) { +- if ((ABS(prev_swp_freq[j] - swp_freq)) < (swp_ofs * 3 / 2)) { +- signal_dupl = 1; +- dprintk(verbose, MB86A16_INFO, 1, "Probably Duplicate Signal, j = %d", j); +- } +- } +- if ((signal_dupl == 0) && (swp_freq > 0) && (ABS(swp_freq - state->frequency * 1000) < fcp + state->srate / 6)) { +- dprintk(verbose, MB86A16_DEBUG, 1, "------ Signal detect ------ [swp_freq=[%07d, srate=%05d]]", swp_freq, state->srate); +- prev_swp_freq[prev_freq_num] = swp_freq; +- prev_freq_num++; +- swp_info_get2(state, state->srate, R, swp_freq, +- &afcex_freq, &fOSC, +- &AFCEX_L, &AFCEX_H); +- +- if (rf_val_set(state, fOSC, state->srate, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); +- return -1; +- } +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); +- return -1; +- } +- signal = signal_det(state, state->srate, &SIG1); +- if (signal == 1) { +- dprintk(verbose, MB86A16_ERROR, 1, "***** Signal Found *****"); +- loop = 0; +- } else { +- dprintk(verbose, MB86A16_ERROR, 1, "!!!!! No signal !!!!!, try again..."); +- smrt_info_get(state, state->srate); +- if (smrt_set(state, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); +- return -1; +- } +- } +- } +- if (v > vmax) +- vmax_his = 1 ; +- if (v < vmin) +- vmin_his = 1 ; +- i++; +- +- if ((i % 2 == 1) && (vmax_his == 1)) +- i++; +- if ((i % 2 == 0) && (vmin_his == 1)) +- i++; +- +- if (i % 2 == 1) +- v = (i + 1) / 2; +- else +- v = -i / 2; +- +- if ((vmax_his == 1) && (vmin_his == 1)) +- loop = 0 ; +- } +- +- if (signal == 1) { +- dprintk(verbose, MB86A16_INFO, 1, " Start Freq Error Check"); +- S1T = 7 ; +- S0T = 1 ; +- CREN = 0 ; +- AFCEN = 1 ; +- AFCEXEN = 0 ; +- +- if (S01T_set(state, S1T, S0T) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); +- return -1; +- } +- smrt_info_get(state, state->srate); +- if (smrt_set(state, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); +- return -1; +- } +- if (EN_set(state, CREN, AFCEN) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); +- return -1; +- } +- if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); +- return -1; +- } +- afcex_info_get(state, afcex_freq, &AFCEX_L, &AFCEX_H); +- if (afcofs_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "AFCOFS data set error"); +- return -1; +- } +- if (srst(state) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "srst error"); +- return -1; +- } +- /* delay 4~200 */ +- wait_t = 200000 / state->master_clk + 200000 / state->srate; +- msleep(wait_t); +- afcerr = afcerr_chk(state); +- if (afcerr == -1) +- return -1; +- +- swp_freq = fOSC * 1000 + afcerr ; +- AFCEXEN = 1 ; +- if (state->srate >= 1500) +- smrt_d = state->srate / 3; +- else +- smrt_d = state->srate / 2; +- smrt_info_get(state, smrt_d); +- if (smrt_set(state, smrt_d) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); +- return -1; +- } +- if (AFCEXEN_set(state, AFCEXEN, smrt_d) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); +- return -1; +- } +- R = vco_dev_get(state, smrt_d); +- if (DAGC_data_set(state, 2, 0) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); +- return -1; +- } +- for (i = 0; i < 3; i++) { +- temp_freq = swp_freq + (i - 1) * state->srate / 8; +- swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); +- if (rf_val_set(state, fOSC, smrt_d, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); +- return -1; +- } +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); +- return -1; +- } +- wait_t = 200000 / state->master_clk + 40000 / smrt_d; +- msleep(wait_t); +- dagcm[i] = dagcm_val_get(state); +- } +- if ((dagcm[0] > dagcm[1]) && +- (dagcm[0] > dagcm[2]) && +- (dagcm[0] - dagcm[1] > 2 * (dagcm[2] - dagcm[1]))) { +- +- temp_freq = swp_freq - 2 * state->srate / 8; +- swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); +- if (rf_val_set(state, fOSC, smrt_d, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); +- return -1; +- } +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); +- return -1; +- } +- wait_t = 200000 / state->master_clk + 40000 / smrt_d; +- msleep(wait_t); +- dagcm[3] = dagcm_val_get(state); +- if (dagcm[3] > dagcm[1]) +- delta_freq = (dagcm[2] - dagcm[0] + dagcm[1] - dagcm[3]) * state->srate / 300; +- else +- delta_freq = 0; +- } else if ((dagcm[2] > dagcm[1]) && +- (dagcm[2] > dagcm[0]) && +- (dagcm[2] - dagcm[1] > 2 * (dagcm[0] - dagcm[1]))) { +- +- temp_freq = swp_freq + 2 * state->srate / 8; +- swp_info_get2(state, smrt_d, R, temp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); +- if (rf_val_set(state, fOSC, smrt_d, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set"); +- return -1; +- } +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set"); +- return -1; +- } +- wait_t = 200000 / state->master_clk + 40000 / smrt_d; +- msleep(wait_t); +- dagcm[3] = dagcm_val_get(state); +- if (dagcm[3] > dagcm[1]) +- delta_freq = (dagcm[2] - dagcm[0] + dagcm[3] - dagcm[1]) * state->srate / 300; +- else +- delta_freq = 0 ; +- +- } else { +- delta_freq = 0 ; +- } +- dprintk(verbose, MB86A16_INFO, 1, "SWEEP Frequency = %d", swp_freq); +- swp_freq += delta_freq; +- dprintk(verbose, MB86A16_INFO, 1, "Adjusting .., DELTA Freq = %d, SWEEP Freq=%d", delta_freq, swp_freq); +- if (ABS(state->frequency * 1000 - swp_freq) > 3800) { +- dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL !"); +- } else { +- +- S1T = 0; +- S0T = 3; +- CREN = 1; +- AFCEN = 0; +- AFCEXEN = 1; +- +- if (S01T_set(state, S1T, S0T) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "S01T set error"); +- return -1; +- } +- if (DAGC_data_set(state, 0, 0) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "DAGC data set error"); +- return -1; +- } +- R = vco_dev_get(state, state->srate); +- smrt_info_get(state, state->srate); +- if (smrt_set(state, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "smrt set error"); +- return -1; +- } +- if (EN_set(state, CREN, AFCEN) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "EN set error"); +- return -1; +- } +- if (AFCEXEN_set(state, AFCEXEN, state->srate) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "AFCEXEN set error"); +- return -1; +- } +- swp_info_get2(state, state->srate, R, swp_freq, &afcex_freq, &fOSC, &AFCEX_L, &AFCEX_H); +- if (rf_val_set(state, fOSC, state->srate, R) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "rf val set error"); +- return -1; +- } +- if (afcex_data_set(state, AFCEX_L, AFCEX_H) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "afcex data set error"); +- return -1; +- } +- if (srst(state) < 0) { +- dprintk(verbose, MB86A16_ERROR, 1, "srst error"); +- return -1; +- } +- wait_t = 7 + (10000 + state->srate / 2) / state->srate; +- if (wait_t == 0) +- wait_t = 1; +- msleep_interruptible(wait_t); +- if (mb86a16_read(state, 0x37, &SIG1) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- if (SIG1 > 110) { +- S2T = 4; S4T = 1; S5T = 6; ETH = 4; VIA = 6; +- wait_t = 7 + (917504 + state->srate / 2) / state->srate; +- } else if (SIG1 > 105) { +- S2T = 4; S4T = 2; S5T = 8; ETH = 7; VIA = 2; +- wait_t = 7 + (1048576 + state->srate / 2) / state->srate; +- } else if (SIG1 > 85) { +- S2T = 5; S4T = 2; S5T = 8; ETH = 7; VIA = 2; +- wait_t = 7 + (1310720 + state->srate / 2) / state->srate; +- } else if (SIG1 > 65) { +- S2T = 6; S4T = 2; S5T = 8; ETH = 7; VIA = 2; +- wait_t = 7 + (1572864 + state->srate / 2) / state->srate; +- } else { +- S2T = 7; S4T = 2; S5T = 8; ETH = 7; VIA = 2; +- wait_t = 7 + (2097152 + state->srate / 2) / state->srate; +- } +- wait_t *= 2; /* FOS */ +- S2T_set(state, S2T); +- S45T_set(state, S4T, S5T); +- Vi_set(state, ETH, VIA); +- srst(state); +- msleep_interruptible(wait_t); +- sync = sync_chk(state, &VIRM); +- dprintk(verbose, MB86A16_INFO, 1, "-------- Viterbi=[%d] SYNC=[%d] ---------", VIRM, sync); +- if (VIRM) { +- if (VIRM == 4) { +- /* 5/6 */ +- if (SIG1 > 110) +- wait_t = (786432 + state->srate / 2) / state->srate; +- else +- wait_t = (1572864 + state->srate / 2) / state->srate; +- if (state->srate < 5000) +- /* FIXME ! , should be a long wait ! */ +- msleep_interruptible(wait_t); +- else +- msleep_interruptible(wait_t); +- +- if (sync_chk(state, &junk) == 0) { +- iq_vt_set(state, 1); +- FEC_srst(state); +- } +- } +- /* 1/2, 2/3, 3/4, 7/8 */ +- if (SIG1 > 110) +- wait_t = (786432 + state->srate / 2) / state->srate; +- else +- wait_t = (1572864 + state->srate / 2) / state->srate; +- msleep_interruptible(wait_t); +- SEQ_set(state, 1); +- } else { +- dprintk(verbose, MB86A16_INFO, 1, "NO -- SYNC"); +- SEQ_set(state, 1); +- ret = -1; +- } +- } +- } else { +- dprintk(verbose, MB86A16_INFO, 1, "NO -- SIGNAL"); +- ret = -1; +- } +- +- sync = sync_chk(state, &junk); +- if (sync) { +- dprintk(verbose, MB86A16_INFO, 1, "******* SYNC *******"); +- freqerr_chk(state, state->frequency, state->srate, 1); +- ret = 0; +- break; +- } +- } +- +- mb86a16_read(state, 0x15, &agcval); +- mb86a16_read(state, 0x26, &cnmval); +- dprintk(verbose, MB86A16_INFO, 1, "AGC = %02x CNM = %02x", agcval, cnmval); +- +- return ret; +-} +- +-static int mb86a16_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *cmd) +-{ +- struct mb86a16_state *state = fe->demodulator_priv; +- int i; +- u8 regs; +- +- if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) +- goto err; +- +- regs = 0x18; +- +- if (cmd->msg_len > 5 || cmd->msg_len < 4) +- return -EINVAL; +- +- for (i = 0; i < cmd->msg_len; i++) { +- if (mb86a16_write(state, regs, cmd->msg[i]) < 0) +- goto err; +- +- regs++; +- } +- i += 0x90; +- +- msleep_interruptible(10); +- +- if (mb86a16_write(state, MB86A16_DCC1, i) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int mb86a16_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +-{ +- struct mb86a16_state *state = fe->demodulator_priv; +- +- switch (burst) { +- case SEC_MINI_A: +- if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | +- MB86A16_DCC1_TBEN | +- MB86A16_DCC1_TBO) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) +- goto err; +- break; +- case SEC_MINI_B: +- if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | +- MB86A16_DCC1_TBEN) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) +- goto err; +- break; +- } +- +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int mb86a16_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct mb86a16_state *state = fe->demodulator_priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- if (mb86a16_write(state, MB86A16_TONEOUT2, 0x00) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA | +- MB86A16_DCC1_CTOE) < 0) +- +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, MB86A16_DCCOUT_DISEN) < 0) +- goto err; +- break; +- case SEC_TONE_OFF: +- if (mb86a16_write(state, MB86A16_TONEOUT2, 0x04) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCC1, MB86A16_DCC1_DISTA) < 0) +- goto err; +- if (mb86a16_write(state, MB86A16_DCCOUT, 0x00) < 0) +- goto err; +- break; +- default: +- return -EINVAL; +- } +- return 0; +- +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static enum dvbfe_search mb86a16_search(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mb86a16_state *state = fe->demodulator_priv; +- +- state->frequency = p->frequency / 1000; +- state->srate = p->symbol_rate / 1000; +- +- if (!mb86a16_set_fe(state)) { +- dprintk(verbose, MB86A16_ERROR, 1, "Successfully acquired LOCK"); +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } +- +- dprintk(verbose, MB86A16_ERROR, 1, "Lock acquisition failed!"); +- return DVBFE_ALGO_SEARCH_FAILED; +-} +- +-static void mb86a16_release(struct dvb_frontend *fe) +-{ +- struct mb86a16_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static int mb86a16_init(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int mb86a16_sleep(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int mb86a16_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- u8 ber_mon, ber_tab, ber_lsb, ber_mid, ber_msb, ber_tim, ber_rst; +- u32 timer; +- +- struct mb86a16_state *state = fe->demodulator_priv; +- +- *ber = 0; +- if (mb86a16_read(state, MB86A16_BERMON, &ber_mon) != 2) +- goto err; +- if (mb86a16_read(state, MB86A16_BERTAB, &ber_tab) != 2) +- goto err; +- if (mb86a16_read(state, MB86A16_BERLSB, &ber_lsb) != 2) +- goto err; +- if (mb86a16_read(state, MB86A16_BERMID, &ber_mid) != 2) +- goto err; +- if (mb86a16_read(state, MB86A16_BERMSB, &ber_msb) != 2) +- goto err; +- /* BER monitor invalid when BER_EN = 0 */ +- if (ber_mon & 0x04) { +- /* coarse, fast calculation */ +- *ber = ber_tab & 0x1f; +- dprintk(verbose, MB86A16_DEBUG, 1, "BER coarse=[0x%02x]", *ber); +- if (ber_mon & 0x01) { +- /* +- * BER_SEL = 1, The monitored BER is the estimated +- * value with a Reed-Solomon decoder error amount at +- * the deinterleaver output. +- * monitored BER is expressed as a 20 bit output in total +- */ +- ber_rst = ber_mon >> 3; +- *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; +- if (ber_rst == 0) +- timer = 12500000; +- if (ber_rst == 1) +- timer = 25000000; +- if (ber_rst == 2) +- timer = 50000000; +- if (ber_rst == 3) +- timer = 100000000; +- +- *ber /= timer; +- dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); +- } else { +- /* +- * BER_SEL = 0, The monitored BER is the estimated +- * value with a Viterbi decoder error amount at the +- * QPSK demodulator output. +- * monitored BER is expressed as a 24 bit output in total +- */ +- ber_tim = ber_mon >> 1; +- *ber = (((ber_msb << 8) | ber_mid) << 8) | ber_lsb; +- if (ber_tim == 0) +- timer = 16; +- if (ber_tim == 1) +- timer = 24; +- +- *ber /= 2 ^ timer; +- dprintk(verbose, MB86A16_DEBUG, 1, "BER fine=[0x%02x]", *ber); +- } +- } +- return 0; +-err: +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +-} +- +-static int mb86a16_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- u8 agcm = 0; +- struct mb86a16_state *state = fe->demodulator_priv; +- +- *strength = 0; +- if (mb86a16_read(state, MB86A16_AGCM, &agcm) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- *strength = ((0xff - agcm) * 100) / 256; +- dprintk(verbose, MB86A16_DEBUG, 1, "Signal strength=[%d %%]", (u8) *strength); +- *strength = (0xffff - 0xff) + agcm; +- +- return 0; +-} +- +-struct cnr { +- u8 cn_reg; +- u8 cn_val; +-}; +- +-static const struct cnr cnr_tab[] = { +- { 35, 2 }, +- { 40, 3 }, +- { 50, 4 }, +- { 60, 5 }, +- { 70, 6 }, +- { 80, 7 }, +- { 92, 8 }, +- { 103, 9 }, +- { 115, 10 }, +- { 138, 12 }, +- { 162, 15 }, +- { 180, 18 }, +- { 185, 19 }, +- { 189, 20 }, +- { 195, 22 }, +- { 199, 24 }, +- { 201, 25 }, +- { 202, 26 }, +- { 203, 27 }, +- { 205, 28 }, +- { 208, 30 } +-}; +- +-static int mb86a16_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct mb86a16_state *state = fe->demodulator_priv; +- int i = 0; +- int low_tide = 2, high_tide = 30, q_level; +- u8 cn; +- +- *snr = 0; +- if (mb86a16_read(state, 0x26, &cn) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- +- for (i = 0; i < ARRAY_SIZE(cnr_tab); i++) { +- if (cn < cnr_tab[i].cn_reg) { +- *snr = cnr_tab[i].cn_val; +- break; +- } +- } +- q_level = (*snr * 100) / (high_tide - low_tide); +- dprintk(verbose, MB86A16_ERROR, 1, "SNR (Quality) = [%d dB], Level=%d %%", *snr, q_level); +- *snr = (0xffff - 0xff) + *snr; +- +- return 0; +-} +- +-static int mb86a16_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- u8 dist; +- struct mb86a16_state *state = fe->demodulator_priv; +- +- if (mb86a16_read(state, MB86A16_DISTMON, &dist) != 2) { +- dprintk(verbose, MB86A16_ERROR, 1, "I2C transfer error"); +- return -EREMOTEIO; +- } +- *ucblocks = dist; +- +- return 0; +-} +- +-static enum dvbfe_algo mb86a16_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-static struct dvb_frontend_ops mb86a16_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Fujitsu MB86A16 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 3000, +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | +- FE_CAN_FEC_7_8 | FE_CAN_QPSK | +- FE_CAN_FEC_AUTO +- }, +- .release = mb86a16_release, +- +- .get_frontend_algo = mb86a16_frontend_algo, +- .search = mb86a16_search, +- .init = mb86a16_init, +- .sleep = mb86a16_sleep, +- .read_status = mb86a16_read_status, +- +- .read_ber = mb86a16_read_ber, +- .read_signal_strength = mb86a16_read_signal_strength, +- .read_snr = mb86a16_read_snr, +- .read_ucblocks = mb86a16_read_ucblocks, +- +- .diseqc_send_master_cmd = mb86a16_send_diseqc_msg, +- .diseqc_send_burst = mb86a16_send_diseqc_burst, +- .set_tone = mb86a16_set_tone, +-}; +- +-struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, +- struct i2c_adapter *i2c_adap) +-{ +- u8 dev_id = 0; +- struct mb86a16_state *state = NULL; +- +- state = kmalloc(sizeof(struct mb86a16_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->config = config; +- state->i2c_adap = i2c_adap; +- +- mb86a16_read(state, 0x7f, &dev_id); +- if (dev_id != 0xfe) +- goto error; +- +- memcpy(&state->frontend.ops, &mb86a16_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- state->frontend.ops.set_voltage = state->config->set_voltage; +- +- return &state->frontend; +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(mb86a16_attach); +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Manu Abraham"); +diff --git a/drivers/media/dvb/frontends/mb86a16.h b/drivers/media/dvb/frontends/mb86a16.h +deleted file mode 100644 +index 6ea8c37..0000000 +--- a/drivers/media/dvb/frontends/mb86a16.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* +- Fujitsu MB86A16 DVB-S/DSS DC Receiver driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MB86A16_H +-#define __MB86A16_H +- +-#include +-#include "dvb_frontend.h" +- +- +-struct mb86a16_config { +- u8 demod_address; +- +- int (*set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); +-}; +- +- +- +-#if defined(CONFIG_DVB_MB86A16) || (defined(CONFIG_DVB_MB86A16_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, +- struct i2c_adapter *i2c_adap); +- +-#else +- +-static inline struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, +- struct i2c_adapter *i2c_adap) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif /* CONFIG_DVB_MB86A16 */ +- +-#endif /* __MB86A16_H */ +diff --git a/drivers/media/dvb/frontends/mb86a16_priv.h b/drivers/media/dvb/frontends/mb86a16_priv.h +deleted file mode 100644 +index 360a35a..0000000 +--- a/drivers/media/dvb/frontends/mb86a16_priv.h ++++ /dev/null +@@ -1,151 +0,0 @@ +-/* +- Fujitsu MB86A16 DVB-S/DSS DC Receiver driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MB86A16_PRIV_H +-#define __MB86A16_PRIV_H +- +-#define MB86A16_TSOUT 0x00 +-#define MB86A16_TSOUT_HIZSEL (0x01 << 5) +-#define MB86A16_TSOUT_HIZCNTI (0x01 << 4) +-#define MB86A16_TSOUT_MODE (0x01 << 3) +-#define MB86A16_TSOUT_ORDER (0x01 << 2) +-#define MB86A16_TSOUT_ERROR (0x01 << 1) +-#define Mb86A16_TSOUT_EDGE (0x01 << 0) +- +-#define MB86A16_FEC 0x01 +-#define MB86A16_FEC_FSYNC (0x01 << 5) +-#define MB86A16_FEC_PCKB8 (0x01 << 4) +-#define MB86A16_FEC_DVDS (0x01 << 3) +-#define MB86A16_FEC_EREN (0x01 << 2) +-#define Mb86A16_FEC_RSEN (0x01 << 1) +-#define MB86A16_FEC_DIEN (0x01 << 0) +- +-#define MB86A16_AGC 0x02 +-#define MB86A16_AGC_AGMD (0x01 << 6) +-#define MB86A16_AGC_AGCW (0x0f << 2) +-#define MB86A16_AGC_AGCP (0x01 << 1) +-#define MB86A16_AGC_AGCR (0x01 << 0) +- +-#define MB86A16_SRATE1 0x03 +-#define MB86A16_SRATE1_DECI (0x07 << 2) +-#define MB86A16_SRATE1_CSEL (0x01 << 1) +-#define MB86A16_SRATE1_RSEL (0x01 << 0) +- +-#define MB86A16_SRATE2 0x04 +-#define MB86A16_SRATE2_STOFSL (0xff << 0) +- +-#define MB86A16_SRATE3 0x05 +-#define MB86A16_SRATE2_STOFSH (0xff << 0) +- +-#define MB86A16_VITERBI 0x06 +-#define MB86A16_FRAMESYNC 0x07 +-#define MB86A16_CRLFILTCOEF1 0x08 +-#define MB86A16_CRLFILTCOEF2 0x09 +-#define MB86A16_STRFILTCOEF1 0x0a +-#define MB86A16_STRFILTCOEF2 0x0b +-#define MB86A16_RESET 0x0c +-#define MB86A16_STATUS 0x0d +-#define MB86A16_AFCML 0x0e +-#define MB86A16_AFCMH 0x0f +-#define MB86A16_BERMON 0x10 +-#define MB86A16_BERTAB 0x11 +-#define MB86A16_BERLSB 0x12 +-#define MB86A16_BERMID 0x13 +-#define MB86A16_BERMSB 0x14 +-#define MB86A16_AGCM 0x15 +- +-#define MB86A16_DCC1 0x16 +-#define MB86A16_DCC1_DISTA (0x01 << 7) +-#define MB86A16_DCC1_PRTY (0x01 << 6) +-#define MB86A16_DCC1_CTOE (0x01 << 5) +-#define MB86A16_DCC1_TBEN (0x01 << 4) +-#define MB86A16_DCC1_TBO (0x01 << 3) +-#define MB86A16_DCC1_NUM (0x07 << 0) +- +-#define MB86A16_DCC2 0x17 +-#define MB86A16_DCC2_DCBST (0x01 << 0) +- +-#define MB86A16_DCC3 0x18 +-#define MB86A16_DCC3_CODE0 (0xff << 0) +- +-#define MB86A16_DCC4 0x19 +-#define MB86A16_DCC4_CODE1 (0xff << 0) +- +-#define MB86A16_DCC5 0x1a +-#define MB86A16_DCC5_CODE2 (0xff << 0) +- +-#define MB86A16_DCC6 0x1b +-#define MB86A16_DCC6_CODE3 (0xff << 0) +- +-#define MB86A16_DCC7 0x1c +-#define MB86A16_DCC7_CODE4 (0xff << 0) +- +-#define MB86A16_DCC8 0x1d +-#define MB86A16_DCC8_CODE5 (0xff << 0) +- +-#define MB86A16_DCCOUT 0x1e +-#define MB86A16_DCCOUT_DISEN (0x01 << 0) +- +-#define MB86A16_TONEOUT1 0x1f +-#define MB86A16_TONE_TDIVL (0xff << 0) +- +-#define MB86A16_TONEOUT2 0x20 +-#define MB86A16_TONE_TMD (0x03 << 2) +-#define MB86A16_TONE_TDIVH (0x03 << 0) +- +-#define MB86A16_FREQ1 0x21 +-#define MB86A16_FREQ2 0x22 +-#define MB86A16_FREQ3 0x23 +-#define MB86A16_FREQ4 0x24 +-#define MB86A16_FREQSET 0x25 +-#define MB86A16_CNM 0x26 +-#define MB86A16_PORT0 0x27 +-#define MB86A16_PORT1 0x28 +-#define MB86A16_DRCFILT 0x29 +-#define MB86A16_AFC 0x2a +-#define MB86A16_AFCEXL 0x2b +-#define MB86A16_AFCEXH 0x2c +-#define MB86A16_DAGC 0x2d +-#define MB86A16_SEQMODE 0x32 +-#define MB86A16_S0S1T 0x33 +-#define MB86A16_S2S3T 0x34 +-#define MB86A16_S4S5T 0x35 +-#define MB86A16_CNTMR 0x36 +-#define MB86A16_SIG1 0x37 +-#define MB86A16_SIG2 0x38 +-#define MB86A16_VIMAG 0x39 +-#define MB86A16_VISET1 0x3a +-#define MB86A16_VISET2 0x3b +-#define MB86A16_VISET3 0x3c +-#define MB86A16_FAGCS1 0x3d +-#define MB86A16_FAGCS2 0x3e +-#define MB86A16_FAGCS3 0x3f +-#define MB86A16_FAGCS4 0x40 +-#define MB86A16_FAGCS5 0x41 +-#define MB86A16_FAGCS6 0x42 +-#define MB86A16_CRM 0x43 +-#define MB86A16_STRM 0x44 +-#define MB86A16_DAGCML 0x45 +-#define MB86A16_DAGCMH 0x46 +-#define MB86A16_QPSKTST 0x49 +-#define MB86A16_DISTMON 0x52 +-#define MB86A16_VERSION 0x7f +- +-#endif /* __MB86A16_PRIV_H */ +diff --git a/drivers/media/dvb/frontends/mb86a20s.c b/drivers/media/dvb/frontends/mb86a20s.c +deleted file mode 100644 +index fade566..0000000 +--- a/drivers/media/dvb/frontends/mb86a20s.c ++++ /dev/null +@@ -1,701 +0,0 @@ +-/* +- * Fujitu mb86a20s ISDB-T/ISDB-Tsb Module driver +- * +- * Copyright (C) 2010 Mauro Carvalho Chehab +- * Copyright (C) 2009-2010 Douglas Landgraf +- * +- * FIXME: Need to port to DVB v5.2 API +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- */ +- +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "mb86a20s.h" +- +-static int debug = 1; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-#define rc(args...) do { \ +- printk(KERN_ERR "mb86a20s: " args); \ +-} while (0) +- +-#define dprintk(args...) \ +- do { \ +- if (debug) { \ +- printk(KERN_DEBUG "mb86a20s: %s: ", __func__); \ +- printk(args); \ +- } \ +- } while (0) +- +-struct mb86a20s_state { +- struct i2c_adapter *i2c; +- const struct mb86a20s_config *config; +- +- struct dvb_frontend frontend; +- +- bool need_init; +-}; +- +-struct regdata { +- u8 reg; +- u8 data; +-}; +- +-/* +- * Initialization sequence: Use whatevere default values that PV SBTVD +- * does on its initialisation, obtained via USB snoop +- */ +-static struct regdata mb86a20s_init[] = { +- { 0x70, 0x0f }, +- { 0x70, 0xff }, +- { 0x08, 0x01 }, +- { 0x09, 0x3e }, +- { 0x50, 0xd1 }, { 0x51, 0x22 }, +- { 0x39, 0x01 }, +- { 0x71, 0x00 }, +- { 0x28, 0x2a }, { 0x29, 0x00 }, { 0x2a, 0xff }, { 0x2b, 0x80 }, +- { 0x28, 0x20 }, { 0x29, 0x33 }, { 0x2a, 0xdf }, { 0x2b, 0xa9 }, +- { 0x28, 0x22 }, { 0x29, 0x00 }, { 0x2a, 0x1f }, { 0x2b, 0xf0 }, +- { 0x3b, 0x21 }, +- { 0x3c, 0x3a }, +- { 0x01, 0x0d }, +- { 0x04, 0x08 }, { 0x05, 0x05 }, +- { 0x04, 0x0e }, { 0x05, 0x00 }, +- { 0x04, 0x0f }, { 0x05, 0x14 }, +- { 0x04, 0x0b }, { 0x05, 0x8c }, +- { 0x04, 0x00 }, { 0x05, 0x00 }, +- { 0x04, 0x01 }, { 0x05, 0x07 }, +- { 0x04, 0x02 }, { 0x05, 0x0f }, +- { 0x04, 0x03 }, { 0x05, 0xa0 }, +- { 0x04, 0x09 }, { 0x05, 0x00 }, +- { 0x04, 0x0a }, { 0x05, 0xff }, +- { 0x04, 0x27 }, { 0x05, 0x64 }, +- { 0x04, 0x28 }, { 0x05, 0x00 }, +- { 0x04, 0x1e }, { 0x05, 0xff }, +- { 0x04, 0x29 }, { 0x05, 0x0a }, +- { 0x04, 0x32 }, { 0x05, 0x0a }, +- { 0x04, 0x14 }, { 0x05, 0x02 }, +- { 0x04, 0x04 }, { 0x05, 0x00 }, +- { 0x04, 0x05 }, { 0x05, 0x22 }, +- { 0x04, 0x06 }, { 0x05, 0x0e }, +- { 0x04, 0x07 }, { 0x05, 0xd8 }, +- { 0x04, 0x12 }, { 0x05, 0x00 }, +- { 0x04, 0x13 }, { 0x05, 0xff }, +- { 0x04, 0x15 }, { 0x05, 0x4e }, +- { 0x04, 0x16 }, { 0x05, 0x20 }, +- { 0x52, 0x01 }, +- { 0x50, 0xa7 }, { 0x51, 0xff }, +- { 0x50, 0xa8 }, { 0x51, 0xff }, +- { 0x50, 0xa9 }, { 0x51, 0xff }, +- { 0x50, 0xaa }, { 0x51, 0xff }, +- { 0x50, 0xab }, { 0x51, 0xff }, +- { 0x50, 0xac }, { 0x51, 0xff }, +- { 0x50, 0xad }, { 0x51, 0xff }, +- { 0x50, 0xae }, { 0x51, 0xff }, +- { 0x50, 0xaf }, { 0x51, 0xff }, +- { 0x5e, 0x07 }, +- { 0x50, 0xdc }, { 0x51, 0x01 }, +- { 0x50, 0xdd }, { 0x51, 0xf4 }, +- { 0x50, 0xde }, { 0x51, 0x01 }, +- { 0x50, 0xdf }, { 0x51, 0xf4 }, +- { 0x50, 0xe0 }, { 0x51, 0x01 }, +- { 0x50, 0xe1 }, { 0x51, 0xf4 }, +- { 0x50, 0xb0 }, { 0x51, 0x07 }, +- { 0x50, 0xb2 }, { 0x51, 0xff }, +- { 0x50, 0xb3 }, { 0x51, 0xff }, +- { 0x50, 0xb4 }, { 0x51, 0xff }, +- { 0x50, 0xb5 }, { 0x51, 0xff }, +- { 0x50, 0xb6 }, { 0x51, 0xff }, +- { 0x50, 0xb7 }, { 0x51, 0xff }, +- { 0x50, 0x50 }, { 0x51, 0x02 }, +- { 0x50, 0x51 }, { 0x51, 0x04 }, +- { 0x45, 0x04 }, +- { 0x48, 0x04 }, +- { 0x50, 0xd5 }, { 0x51, 0x01 }, /* Serial */ +- { 0x50, 0xd6 }, { 0x51, 0x1f }, +- { 0x50, 0xd2 }, { 0x51, 0x03 }, +- { 0x50, 0xd7 }, { 0x51, 0x3f }, +- { 0x28, 0x74 }, { 0x29, 0x00 }, { 0x28, 0x74 }, { 0x29, 0x40 }, +- { 0x28, 0x46 }, { 0x29, 0x2c }, { 0x28, 0x46 }, { 0x29, 0x0c }, +- { 0x04, 0x40 }, { 0x05, 0x01 }, +- { 0x28, 0x00 }, { 0x29, 0x10 }, +- { 0x28, 0x05 }, { 0x29, 0x02 }, +- { 0x1c, 0x01 }, +- { 0x28, 0x06 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x03 }, +- { 0x28, 0x07 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0d }, +- { 0x28, 0x08 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x02 }, +- { 0x28, 0x09 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x01 }, +- { 0x28, 0x0a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x21 }, +- { 0x28, 0x0b }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x29 }, +- { 0x28, 0x0c }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x16 }, +- { 0x28, 0x0d }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x31 }, +- { 0x28, 0x0e }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0e }, +- { 0x28, 0x0f }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x4e }, +- { 0x28, 0x10 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x46 }, +- { 0x28, 0x11 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x0f }, +- { 0x28, 0x12 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x56 }, +- { 0x28, 0x13 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x35 }, +- { 0x28, 0x14 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbe }, +- { 0x28, 0x15 }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0x84 }, +- { 0x28, 0x16 }, { 0x29, 0x00 }, { 0x2a, 0x03 }, { 0x2b, 0xee }, +- { 0x28, 0x17 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x98 }, +- { 0x28, 0x18 }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x9f }, +- { 0x28, 0x19 }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0xb2 }, +- { 0x28, 0x1a }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0xc2 }, +- { 0x28, 0x1b }, { 0x29, 0x00 }, { 0x2a, 0x07 }, { 0x2b, 0x4a }, +- { 0x28, 0x1c }, { 0x29, 0x00 }, { 0x2a, 0x01 }, { 0x2b, 0xbc }, +- { 0x28, 0x1d }, { 0x29, 0x00 }, { 0x2a, 0x04 }, { 0x2b, 0xba }, +- { 0x28, 0x1e }, { 0x29, 0x00 }, { 0x2a, 0x06 }, { 0x2b, 0x14 }, +- { 0x50, 0x1e }, { 0x51, 0x5d }, +- { 0x50, 0x22 }, { 0x51, 0x00 }, +- { 0x50, 0x23 }, { 0x51, 0xc8 }, +- { 0x50, 0x24 }, { 0x51, 0x00 }, +- { 0x50, 0x25 }, { 0x51, 0xf0 }, +- { 0x50, 0x26 }, { 0x51, 0x00 }, +- { 0x50, 0x27 }, { 0x51, 0xc3 }, +- { 0x50, 0x39 }, { 0x51, 0x02 }, +- { 0x28, 0x6a }, { 0x29, 0x00 }, { 0x2a, 0x00 }, { 0x2b, 0x00 }, +- { 0xd0, 0x00 }, +-}; +- +-static struct regdata mb86a20s_reset_reception[] = { +- { 0x70, 0xf0 }, +- { 0x70, 0xff }, +- { 0x08, 0x01 }, +- { 0x08, 0x00 }, +-}; +- +-static int mb86a20s_i2c_writereg(struct mb86a20s_state *state, +- u8 i2c_addr, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 +- }; +- int rc; +- +- rc = i2c_transfer(state->i2c, &msg, 1); +- if (rc != 1) { +- printk("%s: writereg error (rc == %i, reg == 0x%02x," +- " data == 0x%02x)\n", __func__, rc, reg, data); +- return rc; +- } +- +- return 0; +-} +- +-static int mb86a20s_i2c_writeregdata(struct mb86a20s_state *state, +- u8 i2c_addr, struct regdata *rd, int size) +-{ +- int i, rc; +- +- for (i = 0; i < size; i++) { +- rc = mb86a20s_i2c_writereg(state, i2c_addr, rd[i].reg, +- rd[i].data); +- if (rc < 0) +- return rc; +- } +- return 0; +-} +- +-static int mb86a20s_i2c_readreg(struct mb86a20s_state *state, +- u8 i2c_addr, u8 reg) +-{ +- u8 val; +- int rc; +- struct i2c_msg msg[] = { +- { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } +- }; +- +- rc = i2c_transfer(state->i2c, msg, 2); +- +- if (rc != 2) { +- rc("%s: reg=0x%x (error=%d)\n", __func__, reg, rc); +- return rc; +- } +- +- return val; +-} +- +-#define mb86a20s_readreg(state, reg) \ +- mb86a20s_i2c_readreg(state, state->config->demod_address, reg) +-#define mb86a20s_writereg(state, reg, val) \ +- mb86a20s_i2c_writereg(state, state->config->demod_address, reg, val) +-#define mb86a20s_writeregdata(state, regdata) \ +- mb86a20s_i2c_writeregdata(state, state->config->demod_address, \ +- regdata, ARRAY_SIZE(regdata)) +- +-static int mb86a20s_initfe(struct dvb_frontend *fe) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- int rc; +- u8 regD5 = 1; +- +- dprintk("\n"); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- /* Initialize the frontend */ +- rc = mb86a20s_writeregdata(state, mb86a20s_init); +- if (rc < 0) +- goto err; +- +- if (!state->config->is_serial) { +- regD5 &= ~1; +- +- rc = mb86a20s_writereg(state, 0x50, 0xd5); +- if (rc < 0) +- goto err; +- rc = mb86a20s_writereg(state, 0x51, regD5); +- if (rc < 0) +- goto err; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +-err: +- if (rc < 0) { +- state->need_init = true; +- printk(KERN_INFO "mb86a20s: Init failed. Will try again later\n"); +- } else { +- state->need_init = false; +- dprintk("Initialization succeeded.\n"); +- } +- return rc; +-} +- +-static int mb86a20s_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- unsigned rf_max, rf_min, rf; +- u8 val; +- +- dprintk("\n"); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- /* Does a binary search to get RF strength */ +- rf_max = 0xfff; +- rf_min = 0; +- do { +- rf = (rf_max + rf_min) / 2; +- mb86a20s_writereg(state, 0x04, 0x1f); +- mb86a20s_writereg(state, 0x05, rf >> 8); +- mb86a20s_writereg(state, 0x04, 0x20); +- mb86a20s_writereg(state, 0x04, rf); +- +- val = mb86a20s_readreg(state, 0x02); +- if (val & 0x08) +- rf_min = (rf_max + rf_min) / 2; +- else +- rf_max = (rf_max + rf_min) / 2; +- if (rf_max - rf_min < 4) { +- *strength = (((rf_max + rf_min) / 2) * 65535) / 4095; +- break; +- } +- } while (1); +- +- dprintk("signal strength = %d\n", *strength); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- return 0; +-} +- +-static int mb86a20s_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- u8 val; +- +- dprintk("\n"); +- *status = 0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- val = mb86a20s_readreg(state, 0x0a) & 0xf; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (val >= 2) +- *status |= FE_HAS_SIGNAL; +- +- if (val >= 4) +- *status |= FE_HAS_CARRIER; +- +- if (val >= 5) +- *status |= FE_HAS_VITERBI; +- +- if (val >= 7) +- *status |= FE_HAS_SYNC; +- +- if (val >= 8) /* Maybe 9? */ +- *status |= FE_HAS_LOCK; +- +- dprintk("val = %d, status = 0x%02x\n", val, *status); +- +- return 0; +-} +- +-static int mb86a20s_set_frontend(struct dvb_frontend *fe) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- int rc; +-#if 0 +- /* +- * FIXME: Properly implement the set frontend properties +- */ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +-#endif +- +- dprintk("\n"); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- dprintk("Calling tuner set parameters\n"); +- fe->ops.tuner_ops.set_params(fe); +- +- /* +- * Make it more reliable: if, for some reason, the initial +- * device initialization doesn't happen, initialize it when +- * a SBTVD parameters are adjusted. +- * +- * Unfortunately, due to a hard to track bug at tda829x/tda18271, +- * the agc callback logic is not called during DVB attach time, +- * causing mb86a20s to not be initialized with Kworld SBTVD. +- * So, this hack is needed, in order to make Kworld SBTVD to work. +- */ +- if (state->need_init) +- mb86a20s_initfe(fe); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- rc = mb86a20s_writeregdata(state, mb86a20s_reset_reception); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- return rc; +-} +- +-static int mb86a20s_get_modulation(struct mb86a20s_state *state, +- unsigned layer) +-{ +- int rc; +- static unsigned char reg[] = { +- [0] = 0x86, /* Layer A */ +- [1] = 0x8a, /* Layer B */ +- [2] = 0x8e, /* Layer C */ +- }; +- +- if (layer >= ARRAY_SIZE(reg)) +- return -EINVAL; +- rc = mb86a20s_writereg(state, 0x6d, reg[layer]); +- if (rc < 0) +- return rc; +- rc = mb86a20s_readreg(state, 0x6e); +- if (rc < 0) +- return rc; +- switch ((rc & 0x70) >> 4) { +- case 0: +- return DQPSK; +- case 1: +- return QPSK; +- case 2: +- return QAM_16; +- case 3: +- return QAM_64; +- default: +- return QAM_AUTO; +- } +-} +- +-static int mb86a20s_get_fec(struct mb86a20s_state *state, +- unsigned layer) +-{ +- int rc; +- +- static unsigned char reg[] = { +- [0] = 0x87, /* Layer A */ +- [1] = 0x8b, /* Layer B */ +- [2] = 0x8f, /* Layer C */ +- }; +- +- if (layer >= ARRAY_SIZE(reg)) +- return -EINVAL; +- rc = mb86a20s_writereg(state, 0x6d, reg[layer]); +- if (rc < 0) +- return rc; +- rc = mb86a20s_readreg(state, 0x6e); +- if (rc < 0) +- return rc; +- switch (rc) { +- case 0: +- return FEC_1_2; +- case 1: +- return FEC_2_3; +- case 2: +- return FEC_3_4; +- case 3: +- return FEC_5_6; +- case 4: +- return FEC_7_8; +- default: +- return FEC_AUTO; +- } +-} +- +-static int mb86a20s_get_interleaving(struct mb86a20s_state *state, +- unsigned layer) +-{ +- int rc; +- +- static unsigned char reg[] = { +- [0] = 0x88, /* Layer A */ +- [1] = 0x8c, /* Layer B */ +- [2] = 0x90, /* Layer C */ +- }; +- +- if (layer >= ARRAY_SIZE(reg)) +- return -EINVAL; +- rc = mb86a20s_writereg(state, 0x6d, reg[layer]); +- if (rc < 0) +- return rc; +- rc = mb86a20s_readreg(state, 0x6e); +- if (rc < 0) +- return rc; +- if (rc > 3) +- return -EINVAL; /* Not used */ +- return rc; +-} +- +-static int mb86a20s_get_segment_count(struct mb86a20s_state *state, +- unsigned layer) +-{ +- int rc, count; +- +- static unsigned char reg[] = { +- [0] = 0x89, /* Layer A */ +- [1] = 0x8d, /* Layer B */ +- [2] = 0x91, /* Layer C */ +- }; +- +- if (layer >= ARRAY_SIZE(reg)) +- return -EINVAL; +- rc = mb86a20s_writereg(state, 0x6d, reg[layer]); +- if (rc < 0) +- return rc; +- rc = mb86a20s_readreg(state, 0x6e); +- if (rc < 0) +- return rc; +- count = (rc >> 4) & 0x0f; +- +- return count; +-} +- +-static int mb86a20s_get_frontend(struct dvb_frontend *fe) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- int i, rc; +- +- /* Fixed parameters */ +- p->delivery_system = SYS_ISDBT; +- p->bandwidth_hz = 6000000; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- /* Check for partial reception */ +- rc = mb86a20s_writereg(state, 0x6d, 0x85); +- if (rc >= 0) +- rc = mb86a20s_readreg(state, 0x6e); +- if (rc >= 0) +- p->isdbt_partial_reception = (rc & 0x10) ? 1 : 0; +- +- /* Get per-layer data */ +- p->isdbt_layer_enabled = 0; +- for (i = 0; i < 3; i++) { +- rc = mb86a20s_get_segment_count(state, i); +- if (rc >= 0 && rc < 14) +- p->layer[i].segment_count = rc; +- if (rc == 0x0f) +- continue; +- p->isdbt_layer_enabled |= 1 << i; +- rc = mb86a20s_get_modulation(state, i); +- if (rc >= 0) +- p->layer[i].modulation = rc; +- rc = mb86a20s_get_fec(state, i); +- if (rc >= 0) +- p->layer[i].fec = rc; +- rc = mb86a20s_get_interleaving(state, i); +- if (rc >= 0) +- p->layer[i].interleaving = rc; +- } +- +- p->isdbt_sb_mode = 0; +- rc = mb86a20s_writereg(state, 0x6d, 0x84); +- if ((rc >= 0) && ((rc & 0x60) == 0x20)) { +- p->isdbt_sb_mode = 1; +- /* At least, one segment should exist */ +- if (!p->isdbt_sb_segment_count) +- p->isdbt_sb_segment_count = 1; +- } else +- p->isdbt_sb_segment_count = 0; +- +- /* Get transmission mode and guard interval */ +- p->transmission_mode = TRANSMISSION_MODE_AUTO; +- p->guard_interval = GUARD_INTERVAL_AUTO; +- rc = mb86a20s_readreg(state, 0x07); +- if (rc >= 0) { +- if ((rc & 0x60) == 0x20) { +- switch (rc & 0x0c >> 2) { +- case 0: +- p->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- p->transmission_mode = TRANSMISSION_MODE_4K; +- break; +- case 2: +- p->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- } +- } +- if (!(rc & 0x10)) { +- switch (rc & 0x3) { +- case 0: +- p->guard_interval = GUARD_INTERVAL_1_4; +- break; +- case 1: +- p->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 2: +- p->guard_interval = GUARD_INTERVAL_1_16; +- break; +- } +- } +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- return 0; +-} +- +-static int mb86a20s_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status) +-{ +- int rc = 0; +- +- dprintk("\n"); +- +- if (re_tune) +- rc = mb86a20s_set_frontend(fe); +- +- if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) +- mb86a20s_read_status(fe, status); +- +- return rc; +-} +- +-static void mb86a20s_release(struct dvb_frontend *fe) +-{ +- struct mb86a20s_state *state = fe->demodulator_priv; +- +- dprintk("\n"); +- +- kfree(state); +-} +- +-static struct dvb_frontend_ops mb86a20s_ops; +- +-struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, +- struct i2c_adapter *i2c) +-{ +- u8 rev; +- +- /* allocate memory for the internal state */ +- struct mb86a20s_state *state = +- kzalloc(sizeof(struct mb86a20s_state), GFP_KERNEL); +- +- dprintk("\n"); +- if (state == NULL) { +- rc("Unable to kzalloc\n"); +- goto error; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &mb86a20s_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- /* Check if it is a mb86a20s frontend */ +- rev = mb86a20s_readreg(state, 0); +- +- if (rev == 0x13) { +- printk(KERN_INFO "Detected a Fujitsu mb86a20s frontend\n"); +- } else { +- printk(KERN_ERR "Frontend revision %d is unknown - aborting.\n", +- rev); +- goto error; +- } +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(mb86a20s_attach); +- +-static struct dvb_frontend_ops mb86a20s_ops = { +- .delsys = { SYS_ISDBT }, +- /* Use dib8000 values per default */ +- .info = { +- .name = "Fujitsu mb86A20s", +- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_RECOVER | +- 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_TRANSMISSION_MODE_AUTO | FE_CAN_QAM_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, +- /* Actually, those values depend on the used tuner */ +- .frequency_min = 45000000, +- .frequency_max = 864000000, +- .frequency_stepsize = 62500, +- }, +- +- .release = mb86a20s_release, +- +- .init = mb86a20s_initfe, +- .set_frontend = mb86a20s_set_frontend, +- .get_frontend = mb86a20s_get_frontend, +- .read_status = mb86a20s_read_status, +- .read_signal_strength = mb86a20s_read_signal_strength, +- .tune = mb86a20s_tune, +-}; +- +-MODULE_DESCRIPTION("DVB Frontend module for Fujitsu mb86A20s hardware"); +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/mb86a20s.h b/drivers/media/dvb/frontends/mb86a20s.h +deleted file mode 100644 +index bf22e77..0000000 +--- a/drivers/media/dvb/frontends/mb86a20s.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* +- * Fujitsu mb86a20s driver +- * +- * Copyright (C) 2010 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 version 2. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- */ +- +-#ifndef MB86A20S_H +-#define MB86A20S_H +- +-#include +- +-/** +- * struct mb86a20s_config - Define the per-device attributes of the frontend +- * +- * @demod_address: the demodulator's i2c address +- */ +- +-struct mb86a20s_config { +- u8 demod_address; +- bool is_serial; +-}; +- +-#if defined(CONFIG_DVB_MB86A20S) || (defined(CONFIG_DVB_MB86A20S_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, +- struct i2c_adapter *i2c); +-extern struct i2c_adapter *mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *); +-#else +-static inline struct dvb_frontend *mb86a20s_attach( +- const struct mb86a20s_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static struct i2c_adapter * +- mb86a20s_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* MB86A20S */ +diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c +deleted file mode 100644 +index e20bf13..0000000 +--- a/drivers/media/dvb/frontends/mt312.c ++++ /dev/null +@@ -1,839 +0,0 @@ +-/* +- Driver for Zarlink VP310/MT312/ZL10313 Satellite Channel Decoder +- +- Copyright (C) 2003 Andreas Oberritter +- Copyright (C) 2008 Matthias Schwarzott +- +- 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. +- +- References: +- http://products.zarlink.com/product_profiles/MT312.htm +- http://products.zarlink.com/product_profiles/SL1935.htm +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "mt312_priv.h" +-#include "mt312.h" +- +- +-struct mt312_state { +- struct i2c_adapter *i2c; +- /* configuration settings */ +- const struct mt312_config *config; +- struct dvb_frontend frontend; +- +- u8 id; +- unsigned long xtal; +- u8 freq_mult; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "mt312: " args); \ +- } while (0) +- +-#define MT312_PLL_CLK 10000000UL /* 10 MHz */ +-#define MT312_PLL_CLK_10_111 10111000UL /* 10.111 MHz */ +- +-static int mt312_read(struct mt312_state *state, const enum mt312_reg_addr reg, +- u8 *buf, const size_t count) +-{ +- int ret; +- struct i2c_msg msg[2]; +- u8 regbuf[1] = { reg }; +- +- msg[0].addr = state->config->demod_address; +- msg[0].flags = 0; +- msg[0].buf = regbuf; +- msg[0].len = 1; +- msg[1].addr = state->config->demod_address; +- msg[1].flags = I2C_M_RD; +- msg[1].buf = buf; +- msg[1].len = count; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk(KERN_DEBUG "%s: ret == %d\n", __func__, ret); +- return -EREMOTEIO; +- } +- +- if (debug) { +- int i; +- dprintk("R(%d):", reg & 0x7f); +- for (i = 0; i < count; i++) +- printk(KERN_CONT " %02x", buf[i]); +- printk("\n"); +- } +- +- return 0; +-} +- +-static int mt312_write(struct mt312_state *state, const enum mt312_reg_addr reg, +- const u8 *src, const size_t count) +-{ +- int ret; +- u8 buf[count + 1]; +- struct i2c_msg msg; +- +- if (debug) { +- int i; +- dprintk("W(%d):", reg & 0x7f); +- for (i = 0; i < count; i++) +- printk(KERN_CONT " %02x", src[i]); +- printk("\n"); +- } +- +- buf[0] = reg; +- memcpy(&buf[1], src, count); +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.buf = buf; +- msg.len = count + 1; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) { +- dprintk("%s: ret == %d\n", __func__, ret); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static inline int mt312_readreg(struct mt312_state *state, +- const enum mt312_reg_addr reg, u8 *val) +-{ +- return mt312_read(state, reg, val, 1); +-} +- +-static inline int mt312_writereg(struct mt312_state *state, +- const enum mt312_reg_addr reg, const u8 val) +-{ +- return mt312_write(state, reg, &val, 1); +-} +- +-static inline u32 mt312_div(u32 a, u32 b) +-{ +- return (a + (b / 2)) / b; +-} +- +-static int mt312_reset(struct mt312_state *state, const u8 full) +-{ +- return mt312_writereg(state, RESET, full ? 0x80 : 0x40); +-} +- +-static int mt312_get_inversion(struct mt312_state *state, +- fe_spectral_inversion_t *i) +-{ +- int ret; +- u8 vit_mode; +- +- ret = mt312_readreg(state, VIT_MODE, &vit_mode); +- if (ret < 0) +- return ret; +- +- if (vit_mode & 0x80) /* auto inversion was used */ +- *i = (vit_mode & 0x40) ? INVERSION_ON : INVERSION_OFF; +- +- return 0; +-} +- +-static int mt312_get_symbol_rate(struct mt312_state *state, u32 *sr) +-{ +- int ret; +- u8 sym_rate_h; +- u8 dec_ratio; +- u16 sym_rat_op; +- u16 monitor; +- u8 buf[2]; +- +- ret = mt312_readreg(state, SYM_RATE_H, &sym_rate_h); +- if (ret < 0) +- return ret; +- +- if (sym_rate_h & 0x80) { +- /* symbol rate search was used */ +- ret = mt312_writereg(state, MON_CTRL, 0x03); +- if (ret < 0) +- return ret; +- +- ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- monitor = (buf[0] << 8) | buf[1]; +- +- dprintk("sr(auto) = %u\n", +- mt312_div(monitor * 15625, 4)); +- } else { +- ret = mt312_writereg(state, MON_CTRL, 0x05); +- if (ret < 0) +- return ret; +- +- ret = mt312_read(state, MONITOR_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- dec_ratio = ((buf[0] >> 5) & 0x07) * 32; +- +- ret = mt312_read(state, SYM_RAT_OP_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- sym_rat_op = (buf[0] << 8) | buf[1]; +- +- dprintk("sym_rat_op=%d dec_ratio=%d\n", +- sym_rat_op, dec_ratio); +- dprintk("*sr(manual) = %lu\n", +- (((state->xtal * 8192) / (sym_rat_op + 8192)) * +- 2) - dec_ratio); +- } +- +- return 0; +-} +- +-static int mt312_get_code_rate(struct mt312_state *state, fe_code_rate_t *cr) +-{ +- const fe_code_rate_t fec_tab[8] = +- { FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_6_7, FEC_7_8, +- FEC_AUTO, FEC_AUTO }; +- +- int ret; +- u8 fec_status; +- +- ret = mt312_readreg(state, FEC_STATUS, &fec_status); +- if (ret < 0) +- return ret; +- +- *cr = fec_tab[(fec_status >> 4) & 0x07]; +- +- return 0; +-} +- +-static int mt312_initfe(struct dvb_frontend *fe) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- +- /* wake up */ +- ret = mt312_writereg(state, CONFIG, +- (state->freq_mult == 6 ? 0x88 : 0x8c)); +- if (ret < 0) +- return ret; +- +- /* wait at least 150 usec */ +- udelay(150); +- +- /* full reset */ +- ret = mt312_reset(state, 1); +- if (ret < 0) +- return ret; +- +-/* Per datasheet, write correct values. 09/28/03 ACCJr. +- * If we don't do this, we won't get FE_HAS_VITERBI in the VP310. */ +- { +- u8 buf_def[8] = { 0x14, 0x12, 0x03, 0x02, +- 0x01, 0x00, 0x00, 0x00 }; +- +- ret = mt312_write(state, VIT_SETUP, buf_def, sizeof(buf_def)); +- if (ret < 0) +- return ret; +- } +- +- switch (state->id) { +- case ID_ZL10313: +- /* enable ADC */ +- ret = mt312_writereg(state, GPP_CTRL, 0x80); +- if (ret < 0) +- return ret; +- +- /* configure ZL10313 for optimal ADC performance */ +- buf[0] = 0x80; +- buf[1] = 0xB0; +- ret = mt312_write(state, HW_CTRL, buf, 2); +- if (ret < 0) +- return ret; +- +- /* enable MPEG output and ADCs */ +- ret = mt312_writereg(state, HW_CTRL, 0x00); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, MPEG_CTRL, 0x00); +- if (ret < 0) +- return ret; +- +- break; +- } +- +- /* SYS_CLK */ +- buf[0] = mt312_div(state->xtal * state->freq_mult * 2, 1000000); +- +- /* DISEQC_RATIO */ +- buf[1] = mt312_div(state->xtal, 22000 * 4); +- +- ret = mt312_write(state, SYS_CLK, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, SNR_THS_HIGH, 0x32); +- if (ret < 0) +- return ret; +- +- /* different MOCLK polarity */ +- switch (state->id) { +- case ID_ZL10313: +- buf[0] = 0x33; +- break; +- default: +- buf[0] = 0x53; +- break; +- } +- +- ret = mt312_writereg(state, OP_CTRL, buf[0]); +- if (ret < 0) +- return ret; +- +- /* TS_SW_LIM */ +- buf[0] = 0x8c; +- buf[1] = 0x98; +- +- ret = mt312_write(state, TS_SW_LIM_L, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, CS_SW_LIM, 0x69); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static int mt312_send_master_cmd(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *c) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 diseqc_mode; +- +- if ((c->msg_len == 0) || (c->msg_len > sizeof(c->msg))) +- return -EINVAL; +- +- ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); +- if (ret < 0) +- return ret; +- +- ret = mt312_write(state, (0x80 | DISEQC_INSTR), c->msg, c->msg_len); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, DISEQC_MODE, +- (diseqc_mode & 0x40) | ((c->msg_len - 1) << 3) +- | 0x04); +- if (ret < 0) +- return ret; +- +- /* is there a better way to wait for message to be transmitted */ +- msleep(100); +- +- /* set DISEQC_MODE[2:0] to zero if a return message is expected */ +- if (c->msg[0] & 0x02) { +- ret = mt312_writereg(state, DISEQC_MODE, (diseqc_mode & 0x40)); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int mt312_send_burst(struct dvb_frontend *fe, const fe_sec_mini_cmd_t c) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- const u8 mini_tab[2] = { 0x02, 0x03 }; +- +- int ret; +- u8 diseqc_mode; +- +- if (c > SEC_MINI_B) +- return -EINVAL; +- +- ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, DISEQC_MODE, +- (diseqc_mode & 0x40) | mini_tab[c]); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static int mt312_set_tone(struct dvb_frontend *fe, const fe_sec_tone_mode_t t) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- const u8 tone_tab[2] = { 0x01, 0x00 }; +- +- int ret; +- u8 diseqc_mode; +- +- if (t > SEC_TONE_OFF) +- return -EINVAL; +- +- ret = mt312_readreg(state, DISEQC_MODE, &diseqc_mode); +- if (ret < 0) +- return ret; +- +- ret = mt312_writereg(state, DISEQC_MODE, +- (diseqc_mode & 0x40) | tone_tab[t]); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static int mt312_set_voltage(struct dvb_frontend *fe, const fe_sec_voltage_t v) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- const u8 volt_tab[3] = { 0x00, 0x40, 0x00 }; +- u8 val; +- +- if (v > SEC_VOLTAGE_OFF) +- return -EINVAL; +- +- val = volt_tab[v]; +- if (state->config->voltage_inverted) +- val ^= 0x40; +- +- return mt312_writereg(state, DISEQC_MODE, val); +-} +- +-static int mt312_read_status(struct dvb_frontend *fe, fe_status_t *s) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 status[3]; +- +- *s = 0; +- +- ret = mt312_read(state, QPSK_STAT_H, status, sizeof(status)); +- if (ret < 0) +- return ret; +- +- dprintk("QPSK_STAT_H: 0x%02x, QPSK_STAT_L: 0x%02x," +- " FEC_STATUS: 0x%02x\n", status[0], status[1], status[2]); +- +- if (status[0] & 0xc0) +- *s |= FE_HAS_SIGNAL; /* signal noise ratio */ +- if (status[0] & 0x04) +- *s |= FE_HAS_CARRIER; /* qpsk carrier lock */ +- if (status[2] & 0x02) +- *s |= FE_HAS_VITERBI; /* viterbi lock */ +- if (status[2] & 0x04) +- *s |= FE_HAS_SYNC; /* byte align lock */ +- if (status[0] & 0x01) +- *s |= FE_HAS_LOCK; /* qpsk lock */ +- +- return 0; +-} +- +-static int mt312_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[3]; +- +- ret = mt312_read(state, RS_BERCNT_H, buf, 3); +- if (ret < 0) +- return ret; +- +- *ber = ((buf[0] << 16) | (buf[1] << 8) | buf[2]) * 64; +- +- return 0; +-} +- +-static int mt312_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[3]; +- u16 agc; +- s16 err_db; +- +- ret = mt312_read(state, AGC_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- agc = (buf[0] << 6) | (buf[1] >> 2); +- err_db = (s16) (((buf[1] & 0x03) << 14) | buf[2] << 6) >> 6; +- +- *signal_strength = agc; +- +- dprintk("agc=%08x err_db=%hd\n", agc, err_db); +- +- return 0; +-} +- +-static int mt312_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- +- ret = mt312_read(state, M_SNR_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- *snr = 0xFFFF - ((((buf[0] & 0x7f) << 8) | buf[1]) << 1); +- +- return 0; +-} +- +-static int mt312_read_ucblocks(struct dvb_frontend *fe, u32 *ubc) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- +- ret = mt312_read(state, RS_UBC_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- *ubc = (buf[0] << 8) | buf[1]; +- +- return 0; +-} +- +-static int mt312_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 buf[5], config_val; +- u16 sr; +- +- const u8 fec_tab[10] = +- { 0x00, 0x01, 0x02, 0x04, 0x3f, 0x08, 0x10, 0x20, 0x3f, 0x3f }; +- const u8 inv_tab[3] = { 0x00, 0x40, 0x80 }; +- +- dprintk("%s: Freq %d\n", __func__, p->frequency); +- +- if ((p->frequency < fe->ops.info.frequency_min) +- || (p->frequency > fe->ops.info.frequency_max)) +- return -EINVAL; +- +- if ((p->inversion < INVERSION_OFF) +- || (p->inversion > INVERSION_ON)) +- return -EINVAL; +- +- if ((p->symbol_rate < fe->ops.info.symbol_rate_min) +- || (p->symbol_rate > fe->ops.info.symbol_rate_max)) +- return -EINVAL; +- +- if ((p->fec_inner < FEC_NONE) +- || (p->fec_inner > FEC_AUTO)) +- return -EINVAL; +- +- if ((p->fec_inner == FEC_4_5) +- || (p->fec_inner == FEC_8_9)) +- return -EINVAL; +- +- switch (state->id) { +- case ID_VP310: +- /* For now we will do this only for the VP310. +- * It should be better for the mt312 as well, +- * but tuning will be slower. ACCJr 09/29/03 +- */ +- ret = mt312_readreg(state, CONFIG, &config_val); +- if (ret < 0) +- return ret; +- if (p->symbol_rate >= 30000000) { +- /* Note that 30MS/s should use 90MHz */ +- if (state->freq_mult == 6) { +- /* We are running 60MHz */ +- state->freq_mult = 9; +- ret = mt312_initfe(fe); +- if (ret < 0) +- return ret; +- } +- } else { +- if (state->freq_mult == 9) { +- /* We are running 90MHz */ +- state->freq_mult = 6; +- ret = mt312_initfe(fe); +- if (ret < 0) +- return ret; +- } +- } +- break; +- +- case ID_MT312: +- case ID_ZL10313: +- break; +- +- default: +- return -EINVAL; +- } +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* sr = (u16)(sr * 256.0 / 1000000.0) */ +- sr = mt312_div(p->symbol_rate * 4, 15625); +- +- /* SYM_RATE */ +- buf[0] = (sr >> 8) & 0x3f; +- buf[1] = (sr >> 0) & 0xff; +- +- /* VIT_MODE */ +- buf[2] = inv_tab[p->inversion] | fec_tab[p->fec_inner]; +- +- /* QPSK_CTRL */ +- buf[3] = 0x40; /* swap I and Q before QPSK demodulation */ +- +- if (p->symbol_rate < 10000000) +- buf[3] |= 0x04; /* use afc mode */ +- +- /* GO */ +- buf[4] = 0x01; +- +- ret = mt312_write(state, SYM_RATE_H, buf, sizeof(buf)); +- if (ret < 0) +- return ret; +- +- mt312_reset(state, 0); +- +- return 0; +-} +- +-static int mt312_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- +- ret = mt312_get_inversion(state, &p->inversion); +- if (ret < 0) +- return ret; +- +- ret = mt312_get_symbol_rate(state, &p->symbol_rate); +- if (ret < 0) +- return ret; +- +- ret = mt312_get_code_rate(state, &p->fec_inner); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static int mt312_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- +- u8 val = 0x00; +- int ret; +- +- switch (state->id) { +- case ID_ZL10313: +- ret = mt312_readreg(state, GPP_CTRL, &val); +- if (ret < 0) +- goto error; +- +- /* preserve this bit to not accidentally shutdown ADC */ +- val &= 0x80; +- break; +- } +- +- if (enable) +- val |= 0x40; +- else +- val &= ~0x40; +- +- ret = mt312_writereg(state, GPP_CTRL, val); +- +-error: +- return ret; +-} +- +-static int mt312_sleep(struct dvb_frontend *fe) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- int ret; +- u8 config; +- +- /* reset all registers to defaults */ +- ret = mt312_reset(state, 1); +- if (ret < 0) +- return ret; +- +- if (state->id == ID_ZL10313) { +- /* reset ADC */ +- ret = mt312_writereg(state, GPP_CTRL, 0x00); +- if (ret < 0) +- return ret; +- +- /* full shutdown of ADCs, mpeg bus tristated */ +- ret = mt312_writereg(state, HW_CTRL, 0x0d); +- if (ret < 0) +- return ret; +- } +- +- ret = mt312_readreg(state, CONFIG, &config); +- if (ret < 0) +- return ret; +- +- /* enter standby */ +- ret = mt312_writereg(state, CONFIG, config & 0x7f); +- if (ret < 0) +- return ret; +- +- return 0; +-} +- +-static int mt312_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *fesettings) +-{ +- fesettings->min_delay_ms = 50; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void mt312_release(struct dvb_frontend *fe) +-{ +- struct mt312_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-#define MT312_SYS_CLK 90000000UL /* 90 MHz */ +-static struct dvb_frontend_ops mt312_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Zarlink ???? DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- /* FIXME: adjust freq to real used xtal */ +- .frequency_stepsize = (MT312_PLL_CLK / 1000) / 128, +- .symbol_rate_min = MT312_SYS_CLK / 128, /* FIXME as above */ +- .symbol_rate_max = MT312_SYS_CLK / 2, +- .caps = +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | FE_CAN_QPSK | FE_CAN_MUTE_TS | +- FE_CAN_RECOVER +- }, +- +- .release = mt312_release, +- +- .init = mt312_initfe, +- .sleep = mt312_sleep, +- .i2c_gate_ctrl = mt312_i2c_gate_ctrl, +- +- .set_frontend = mt312_set_frontend, +- .get_frontend = mt312_get_frontend, +- .get_tune_settings = mt312_get_tune_settings, +- +- .read_status = mt312_read_status, +- .read_ber = mt312_read_ber, +- .read_signal_strength = mt312_read_signal_strength, +- .read_snr = mt312_read_snr, +- .read_ucblocks = mt312_read_ucblocks, +- +- .diseqc_send_master_cmd = mt312_send_master_cmd, +- .diseqc_send_burst = mt312_send_burst, +- .set_tone = mt312_set_tone, +- .set_voltage = mt312_set_voltage, +-}; +- +-struct dvb_frontend *mt312_attach(const struct mt312_config *config, +- struct i2c_adapter *i2c) +-{ +- struct mt312_state *state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if (mt312_readreg(state, ID, &state->id) < 0) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &mt312_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- switch (state->id) { +- case ID_VP310: +- strcpy(state->frontend.ops.info.name, "Zarlink VP310 DVB-S"); +- state->xtal = MT312_PLL_CLK; +- state->freq_mult = 9; +- break; +- case ID_MT312: +- strcpy(state->frontend.ops.info.name, "Zarlink MT312 DVB-S"); +- state->xtal = MT312_PLL_CLK; +- state->freq_mult = 6; +- break; +- case ID_ZL10313: +- strcpy(state->frontend.ops.info.name, "Zarlink ZL10313 DVB-S"); +- state->xtal = MT312_PLL_CLK_10_111; +- state->freq_mult = 9; +- break; +- default: +- printk(KERN_WARNING "Only Zarlink VP310/MT312/ZL10313" +- " are supported chips.\n"); +- goto error; +- } +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(mt312_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Zarlink VP310/MT312/ZL10313 DVB-S Demodulator driver"); +-MODULE_AUTHOR("Andreas Oberritter "); +-MODULE_AUTHOR("Matthias Schwarzott "); +-MODULE_LICENSE("GPL"); +- +diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h +deleted file mode 100644 +index 29e3bb5..0000000 +--- a/drivers/media/dvb/frontends/mt312.h ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- Driver for Zarlink MT312 Satellite Channel Decoder +- +- Copyright (C) 2003 Andreas Oberritter +- +- 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. +- +- References: +- http://products.zarlink.com/product_profiles/MT312.htm +- http://products.zarlink.com/product_profiles/SL1935.htm +-*/ +- +-#ifndef MT312_H +-#define MT312_H +- +-#include +- +-struct mt312_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* inverted voltage setting */ +- unsigned int voltage_inverted:1; +-}; +- +-#if defined(CONFIG_DVB_MT312) || (defined(CONFIG_DVB_MT312_MODULE) && defined(MODULE)) +-struct dvb_frontend *mt312_attach(const struct mt312_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *mt312_attach( +- const struct mt312_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_MT312 */ +- +-#endif /* MT312_H */ +diff --git a/drivers/media/dvb/frontends/mt312_priv.h b/drivers/media/dvb/frontends/mt312_priv.h +deleted file mode 100644 +index a3959f9..0000000 +--- a/drivers/media/dvb/frontends/mt312_priv.h ++++ /dev/null +@@ -1,165 +0,0 @@ +-/* +- Driver for Zarlink MT312 QPSK Frontend +- +- Copyright (C) 2003 Andreas Oberritter +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef _DVB_FRONTENDS_MT312_PRIV +-#define _DVB_FRONTENDS_MT312_PRIV +- +-enum mt312_reg_addr { +- QPSK_INT_H = 0, +- QPSK_INT_M = 1, +- QPSK_INT_L = 2, +- FEC_INT = 3, +- QPSK_STAT_H = 4, +- QPSK_STAT_L = 5, +- FEC_STATUS = 6, +- LNB_FREQ_H = 7, +- LNB_FREQ_L = 8, +- M_SNR_H = 9, +- M_SNR_L = 10, +- VIT_ERRCNT_H = 11, +- VIT_ERRCNT_M = 12, +- VIT_ERRCNT_L = 13, +- RS_BERCNT_H = 14, +- RS_BERCNT_M = 15, +- RS_BERCNT_L = 16, +- RS_UBC_H = 17, +- RS_UBC_L = 18, +- SIG_LEVEL = 19, +- GPP_CTRL = 20, +- RESET = 21, +- DISEQC_MODE = 22, +- SYM_RATE_H = 23, +- SYM_RATE_L = 24, +- VIT_MODE = 25, +- QPSK_CTRL = 26, +- GO = 27, +- IE_QPSK_H = 28, +- IE_QPSK_M = 29, +- IE_QPSK_L = 30, +- IE_FEC = 31, +- QPSK_STAT_EN = 32, +- FEC_STAT_EN = 33, +- SYS_CLK = 34, +- DISEQC_RATIO = 35, +- DISEQC_INSTR = 36, +- FR_LIM = 37, +- FR_OFF = 38, +- AGC_CTRL = 39, +- AGC_INIT = 40, +- AGC_REF = 41, +- AGC_MAX = 42, +- AGC_MIN = 43, +- AGC_LK_TH = 44, +- TS_AGC_LK_TH = 45, +- AGC_PWR_SET = 46, +- QPSK_MISC = 47, +- SNR_THS_LOW = 48, +- SNR_THS_HIGH = 49, +- TS_SW_RATE = 50, +- TS_SW_LIM_L = 51, +- TS_SW_LIM_H = 52, +- CS_SW_RATE_1 = 53, +- CS_SW_RATE_2 = 54, +- CS_SW_RATE_3 = 55, +- CS_SW_RATE_4 = 56, +- CS_SW_LIM = 57, +- TS_LPK = 58, +- TS_LPK_M = 59, +- TS_LPK_L = 60, +- CS_KPROP_H = 61, +- CS_KPROP_L = 62, +- CS_KINT_H = 63, +- CS_KINT_L = 64, +- QPSK_SCALE = 65, +- TLD_OUTCLK_TH = 66, +- TLD_INCLK_TH = 67, +- FLD_TH = 68, +- PLD_OUTLK3 = 69, +- PLD_OUTLK2 = 70, +- PLD_OUTLK1 = 71, +- PLD_OUTLK0 = 72, +- PLD_INLK3 = 73, +- PLD_INLK2 = 74, +- PLD_INLK1 = 75, +- PLD_INLK0 = 76, +- PLD_ACC_TIME = 77, +- SWEEP_PAR = 78, +- STARTUP_TIME = 79, +- LOSSLOCK_TH = 80, +- FEC_LOCK_TM = 81, +- LOSSLOCK_TM = 82, +- VIT_ERRPER_H = 83, +- VIT_ERRPER_M = 84, +- VIT_ERRPER_L = 85, +- HW_CTRL = 84, /* ZL10313 only */ +- MPEG_CTRL = 85, /* ZL10313 only */ +- VIT_SETUP = 86, +- VIT_REF0 = 87, +- VIT_REF1 = 88, +- VIT_REF2 = 89, +- VIT_REF3 = 90, +- VIT_REF4 = 91, +- VIT_REF5 = 92, +- VIT_REF6 = 93, +- VIT_MAXERR = 94, +- BA_SETUPT = 95, +- OP_CTRL = 96, +- FEC_SETUP = 97, +- PROG_SYNC = 98, +- AFC_SEAR_TH = 99, +- CSACC_DIF_TH = 100, +- QPSK_LK_CT = 101, +- QPSK_ST_CT = 102, +- MON_CTRL = 103, +- QPSK_RESET = 104, +- QPSK_TST_CT = 105, +- QPSK_TST_ST = 106, +- TEST_R = 107, +- AGC_H = 108, +- AGC_M = 109, +- AGC_L = 110, +- FREQ_ERR1_H = 111, +- FREQ_ERR1_M = 112, +- FREQ_ERR1_L = 113, +- FREQ_ERR2_H = 114, +- FREQ_ERR2_L = 115, +- SYM_RAT_OP_H = 116, +- SYM_RAT_OP_L = 117, +- DESEQC2_INT = 118, +- DISEQC2_STAT = 119, +- DISEQC2_FIFO = 120, +- DISEQC2_CTRL1 = 121, +- DISEQC2_CTRL2 = 122, +- MONITOR_H = 123, +- MONITOR_L = 124, +- TEST_MODE = 125, +- ID = 126, +- CONFIG = 127 +-}; +- +-enum mt312_model_id { +- ID_VP310 = 1, +- ID_MT312 = 3, +- ID_ZL10313 = 5, +-}; +- +-#endif /* DVB_FRONTENDS_MT312_PRIV */ +diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c +deleted file mode 100644 +index 2c3b50e..0000000 +--- a/drivers/media/dvb/frontends/mt352.c ++++ /dev/null +@@ -1,610 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T MT352 demodulator +- * +- * Written by Holger Waechtler +- * and Daniel Mack +- * +- * AVerMedia AVerTV DVB-T 771 support by +- * Wolfram Joost +- * +- * Support for Samsung TDTC9251DH01C(M) tuner +- * Copyright (C) 2004 Antonio Mancuso +- * Amauri Celani +- * +- * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by +- * Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "mt352_priv.h" +-#include "mt352.h" +- +-struct mt352_state { +- struct i2c_adapter* i2c; +- struct dvb_frontend frontend; +- +- /* configuration settings */ +- struct mt352_config config; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "mt352: " args); \ +- } while (0) +- +-static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, +- .buf = buf, .len = 2 }; +- int err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk("mt352_write() to reg %x failed (err = %d)!\n", reg, err); +- return err; +- } +- return 0; +-} +- +-static int _mt352_write(struct dvb_frontend* fe, const u8 ibuf[], int ilen) +-{ +- int err,i; +- for (i=0; i < ilen-1; i++) +- if ((err = mt352_single_write(fe,ibuf[0]+i,ibuf[i+1]))) +- return err; +- +- return 0; +-} +- +-static int mt352_read_register(struct mt352_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config.demod_address, +- .flags = 0, +- .buf = b0, .len = 1 }, +- { .addr = state->config.demod_address, +- .flags = I2C_M_RD, +- .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk("%s: readreg error (reg=%d, ret==%i)\n", +- __func__, reg, ret); +- return ret; +- } +- +- return b1[0]; +-} +- +-static int mt352_sleep(struct dvb_frontend* fe) +-{ +- static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 }; +- +- _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown)); +- return 0; +-} +- +-static void mt352_calc_nominal_rate(struct mt352_state* state, +- u32 bandwidth, +- unsigned char *buf) +-{ +- u32 adc_clock = 20480; /* 20.340 MHz */ +- u32 bw,value; +- +- switch (bandwidth) { +- case 6000000: +- bw = 6; +- break; +- case 7000000: +- bw = 7; +- break; +- case 8000000: +- default: +- bw = 8; +- break; +- } +- if (state->config.adc_clock) +- adc_clock = state->config.adc_clock; +- +- value = 64 * bw * (1<<16) / (7 * 8); +- value = value * 1000 / adc_clock; +- dprintk("%s: bw %d, adc_clock %d => 0x%x\n", +- __func__, bw, adc_clock, value); +- buf[0] = msb(value); +- buf[1] = lsb(value); +-} +- +-static void mt352_calc_input_freq(struct mt352_state* state, +- unsigned char *buf) +-{ +- int adc_clock = 20480; /* 20.480000 MHz */ +- int if2 = 36167; /* 36.166667 MHz */ +- int ife,value; +- +- if (state->config.adc_clock) +- adc_clock = state->config.adc_clock; +- if (state->config.if2) +- if2 = state->config.if2; +- +- if (adc_clock >= if2 * 2) +- ife = if2; +- else { +- ife = adc_clock - (if2 % adc_clock); +- if (ife > adc_clock / 2) +- ife = adc_clock - ife; +- } +- value = -16374 * ife / adc_clock; +- dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", +- __func__, if2, ife, adc_clock, value, value & 0x3fff); +- buf[0] = msb(value); +- buf[1] = lsb(value); +-} +- +-static int mt352_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *op = &fe->dtv_property_cache; +- struct mt352_state* state = fe->demodulator_priv; +- unsigned char buf[13]; +- static unsigned char tuner_go[] = { 0x5d, 0x01 }; +- static unsigned char fsm_go[] = { 0x5e, 0x01 }; +- unsigned int tps = 0; +- +- switch (op->code_rate_HP) { +- case FEC_2_3: +- tps |= (1 << 7); +- break; +- case FEC_3_4: +- tps |= (2 << 7); +- break; +- case FEC_5_6: +- tps |= (3 << 7); +- break; +- case FEC_7_8: +- tps |= (4 << 7); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- break; +- default: +- return -EINVAL; +- } +- +- switch (op->code_rate_LP) { +- case FEC_2_3: +- tps |= (1 << 4); +- break; +- case FEC_3_4: +- tps |= (2 << 4); +- break; +- case FEC_5_6: +- tps |= (3 << 4); +- break; +- case FEC_7_8: +- tps |= (4 << 4); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- break; +- case FEC_NONE: +- if (op->hierarchy == HIERARCHY_AUTO || +- op->hierarchy == HIERARCHY_NONE) +- break; +- default: +- return -EINVAL; +- } +- +- switch (op->modulation) { +- case QPSK: +- break; +- case QAM_AUTO: +- case QAM_16: +- tps |= (1 << 13); +- break; +- case QAM_64: +- tps |= (2 << 13); +- break; +- default: +- return -EINVAL; +- } +- +- switch (op->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- case TRANSMISSION_MODE_AUTO: +- break; +- case TRANSMISSION_MODE_8K: +- tps |= (1 << 0); +- break; +- default: +- return -EINVAL; +- } +- +- switch (op->guard_interval) { +- case GUARD_INTERVAL_1_32: +- case GUARD_INTERVAL_AUTO: +- break; +- case GUARD_INTERVAL_1_16: +- tps |= (1 << 2); +- break; +- case GUARD_INTERVAL_1_8: +- tps |= (2 << 2); +- break; +- case GUARD_INTERVAL_1_4: +- tps |= (3 << 2); +- break; +- default: +- return -EINVAL; +- } +- +- switch (op->hierarchy) { +- case HIERARCHY_AUTO: +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- tps |= (1 << 10); +- break; +- case HIERARCHY_2: +- tps |= (2 << 10); +- break; +- case HIERARCHY_4: +- tps |= (3 << 10); +- break; +- default: +- return -EINVAL; +- } +- +- +- buf[0] = TPS_GIVEN_1; /* TPS_GIVEN_1 and following registers */ +- +- buf[1] = msb(tps); /* TPS_GIVEN_(1|0) */ +- buf[2] = lsb(tps); +- +- buf[3] = 0x50; // old +-// buf[3] = 0xf4; // pinnacle +- +- mt352_calc_nominal_rate(state, op->bandwidth_hz, buf+4); +- mt352_calc_input_freq(state, buf+6); +- +- if (state->config.no_tuner) { +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- _mt352_write(fe, buf, 8); +- _mt352_write(fe, fsm_go, 2); +- } else { +- if (fe->ops.tuner_ops.calc_regs) { +- fe->ops.tuner_ops.calc_regs(fe, buf+8, 5); +- buf[8] <<= 1; +- _mt352_write(fe, buf, sizeof(buf)); +- _mt352_write(fe, tuner_go, 2); +- } +- } +- +- return 0; +-} +- +-static int mt352_get_parameters(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *op = &fe->dtv_property_cache; +- struct mt352_state* state = fe->demodulator_priv; +- u16 tps; +- u16 div; +- u8 trl; +- static const u8 tps_fec_to_api[8] = +- { +- FEC_1_2, +- FEC_2_3, +- FEC_3_4, +- FEC_5_6, +- FEC_7_8, +- FEC_AUTO, +- FEC_AUTO, +- FEC_AUTO +- }; +- +- if ( (mt352_read_register(state,0x00) & 0xC0) != 0xC0 ) +- return -EINVAL; +- +- /* Use TPS_RECEIVED-registers, not the TPS_CURRENT-registers because +- * the mt352 sometimes works with the wrong parameters +- */ +- tps = (mt352_read_register(state, TPS_RECEIVED_1) << 8) | mt352_read_register(state, TPS_RECEIVED_0); +- div = (mt352_read_register(state, CHAN_START_1) << 8) | mt352_read_register(state, CHAN_START_0); +- trl = mt352_read_register(state, TRL_NOMINAL_RATE_1); +- +- op->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; +- op->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; +- +- switch ( (tps >> 13) & 3) +- { +- case 0: +- op->modulation = QPSK; +- break; +- case 1: +- op->modulation = QAM_16; +- break; +- case 2: +- op->modulation = QAM_64; +- break; +- default: +- op->modulation = QAM_AUTO; +- break; +- } +- +- op->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : TRANSMISSION_MODE_2K; +- +- switch ( (tps >> 2) & 3) +- { +- case 0: +- op->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- op->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- op->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- op->guard_interval = GUARD_INTERVAL_1_4; +- break; +- default: +- op->guard_interval = GUARD_INTERVAL_AUTO; +- break; +- } +- +- switch ( (tps >> 10) & 7) +- { +- case 0: +- op->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- op->hierarchy = HIERARCHY_1; +- break; +- case 2: +- op->hierarchy = HIERARCHY_2; +- break; +- case 3: +- op->hierarchy = HIERARCHY_4; +- break; +- default: +- op->hierarchy = HIERARCHY_AUTO; +- break; +- } +- +- op->frequency = (500 * (div - IF_FREQUENCYx6)) / 3 * 1000; +- +- if (trl == 0x72) +- op->bandwidth_hz = 8000000; +- else if (trl == 0x64) +- op->bandwidth_hz = 7000000; +- else +- op->bandwidth_hz = 6000000; +- +- +- if (mt352_read_register(state, STATUS_2) & 0x02) +- op->inversion = INVERSION_OFF; +- else +- op->inversion = INVERSION_ON; +- +- return 0; +-} +- +-static int mt352_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- int s0, s1, s3; +- +- /* FIXME: +- * +- * The MT352 design manual from Zarlink states (page 46-47): +- * +- * Notes about the TUNER_GO register: +- * +- * If the Read_Tuner_Byte (bit-1) is activated, then the tuner status +- * byte is copied from the tuner to the STATUS_3 register and +- * completion of the read operation is indicated by bit-5 of the +- * INTERRUPT_3 register. +- */ +- +- if ((s0 = mt352_read_register(state, STATUS_0)) < 0) +- return -EREMOTEIO; +- if ((s1 = mt352_read_register(state, STATUS_1)) < 0) +- return -EREMOTEIO; +- if ((s3 = mt352_read_register(state, STATUS_3)) < 0) +- return -EREMOTEIO; +- +- *status = 0; +- if (s0 & (1 << 4)) +- *status |= FE_HAS_CARRIER; +- if (s0 & (1 << 1)) +- *status |= FE_HAS_VITERBI; +- if (s0 & (1 << 5)) +- *status |= FE_HAS_LOCK; +- if (s1 & (1 << 1)) +- *status |= FE_HAS_SYNC; +- if (s3 & (1 << 6)) +- *status |= FE_HAS_SIGNAL; +- +- if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != +- (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) +- *status &= ~FE_HAS_LOCK; +- +- return 0; +-} +- +-static int mt352_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- +- *ber = (mt352_read_register (state, RS_ERR_CNT_2) << 16) | +- (mt352_read_register (state, RS_ERR_CNT_1) << 8) | +- (mt352_read_register (state, RS_ERR_CNT_0)); +- +- return 0; +-} +- +-static int mt352_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- +- /* align the 12 bit AGC gain with the most significant bits */ +- u16 signal = ((mt352_read_register(state, AGC_GAIN_1) & 0x0f) << 12) | +- (mt352_read_register(state, AGC_GAIN_0) << 4); +- +- /* inverse of gain is signal strength */ +- *strength = ~signal; +- return 0; +-} +- +-static int mt352_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- +- u8 _snr = mt352_read_register (state, SNR); +- *snr = (_snr << 8) | _snr; +- +- return 0; +-} +- +-static int mt352_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- +- *ucblocks = (mt352_read_register (state, RS_UBC_1) << 8) | +- (mt352_read_register (state, RS_UBC_0)); +- +- return 0; +-} +- +-static int mt352_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +-{ +- fe_tune_settings->min_delay_ms = 800; +- fe_tune_settings->step_size = 0; +- fe_tune_settings->max_drift = 0; +- +- return 0; +-} +- +-static int mt352_init(struct dvb_frontend* fe) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- +- static u8 mt352_reset_attach [] = { RESET, 0xC0 }; +- +- dprintk("%s: hello\n",__func__); +- +- if ((mt352_read_register(state, CLOCK_CTL) & 0x10) == 0 || +- (mt352_read_register(state, CONFIG) & 0x20) == 0) { +- +- /* Do a "hard" reset */ +- _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach)); +- return state->config.demod_init(fe); +- } +- +- return 0; +-} +- +-static void mt352_release(struct dvb_frontend* fe) +-{ +- struct mt352_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops mt352_ops; +- +-struct dvb_frontend* mt352_attach(const struct mt352_config* config, +- struct i2c_adapter* i2c) +-{ +- struct mt352_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct mt352_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- memcpy(&state->config,config,sizeof(struct mt352_config)); +- +- /* check if the demod is there */ +- if (mt352_read_register(state, CHIP_ID) != ID_MT352) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &mt352_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops mt352_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Zarlink MT352 DVB-T", +- .frequency_min = 174000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 166667, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | +- FE_CAN_MUTE_TS +- }, +- +- .release = mt352_release, +- +- .init = mt352_init, +- .sleep = mt352_sleep, +- .write = _mt352_write, +- +- .set_frontend = mt352_set_parameters, +- .get_frontend = mt352_get_parameters, +- .get_tune_settings = mt352_get_tune_settings, +- +- .read_status = mt352_read_status, +- .read_ber = mt352_read_ber, +- .read_signal_strength = mt352_read_signal_strength, +- .read_snr = mt352_read_snr, +- .read_ucblocks = mt352_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(mt352_attach); +diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h +deleted file mode 100644 +index ca2562d..0000000 +--- a/drivers/media/dvb/frontends/mt352.h ++++ /dev/null +@@ -1,73 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T MT352 demodulator +- * +- * Written by Holger Waechtler +- * and Daniel Mack +- * +- * AVerMedia AVerTV DVB-T 771 support by +- * Wolfram Joost +- * +- * Support for Samsung TDTC9251DH01C(M) tuner +- * Copyright (C) 2004 Antonio Mancuso +- * Amauri Celani +- * +- * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by +- * Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef MT352_H +-#define MT352_H +- +-#include +- +-struct mt352_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* frequencies in kHz */ +- int adc_clock; // default: 20480 +- int if2; // default: 36166 +- +- /* set if no pll is connected to the secondary i2c bus */ +- int no_tuner; +- +- /* Initialise the demodulator and PLL. Cannot be NULL */ +- int (*demod_init)(struct dvb_frontend* fe); +-}; +- +-#if defined(CONFIG_DVB_MT352) || (defined(CONFIG_DVB_MT352_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* mt352_attach(const struct mt352_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_MT352 +- +-static inline int mt352_write(struct dvb_frontend *fe, const u8 buf[], int len) { +- int r = 0; +- if (fe->ops.write) +- r = fe->ops.write(fe, buf, len); +- return r; +-} +- +-#endif // MT352_H +diff --git a/drivers/media/dvb/frontends/mt352_priv.h b/drivers/media/dvb/frontends/mt352_priv.h +deleted file mode 100644 +index 44ad0d4..0000000 +--- a/drivers/media/dvb/frontends/mt352_priv.h ++++ /dev/null +@@ -1,127 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T MT352 demodulator +- * +- * Written by Holger Waechtler +- * and Daniel Mack +- * +- * AVerMedia AVerTV DVB-T 771 support by +- * Wolfram Joost +- * +- * Support for Samsung TDTC9251DH01C(M) tuner +- * Copyright (C) 2004 Antonio Mancuso +- * Amauri Celani +- * +- * DVICO FusionHDTV DVB-T1 and DVICO FusionHDTV DVB-T Lite support by +- * Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef _MT352_PRIV_ +-#define _MT352_PRIV_ +- +-#define ID_MT352 0x13 +- +-#define msb(x) (((x) >> 8) & 0xff) +-#define lsb(x) ((x) & 0xff) +- +-enum mt352_reg_addr { +- STATUS_0 = 0x00, +- STATUS_1 = 0x01, +- STATUS_2 = 0x02, +- STATUS_3 = 0x03, +- STATUS_4 = 0x04, +- INTERRUPT_0 = 0x05, +- INTERRUPT_1 = 0x06, +- INTERRUPT_2 = 0x07, +- INTERRUPT_3 = 0x08, +- SNR = 0x09, +- VIT_ERR_CNT_2 = 0x0A, +- VIT_ERR_CNT_1 = 0x0B, +- VIT_ERR_CNT_0 = 0x0C, +- RS_ERR_CNT_2 = 0x0D, +- RS_ERR_CNT_1 = 0x0E, +- RS_ERR_CNT_0 = 0x0F, +- RS_UBC_1 = 0x10, +- RS_UBC_0 = 0x11, +- AGC_GAIN_3 = 0x12, +- AGC_GAIN_2 = 0x13, +- AGC_GAIN_1 = 0x14, +- AGC_GAIN_0 = 0x15, +- FREQ_OFFSET_2 = 0x17, +- FREQ_OFFSET_1 = 0x18, +- FREQ_OFFSET_0 = 0x19, +- TIMING_OFFSET_1 = 0x1A, +- TIMING_OFFSET_0 = 0x1B, +- CHAN_FREQ_1 = 0x1C, +- CHAN_FREQ_0 = 0x1D, +- TPS_RECEIVED_1 = 0x1E, +- TPS_RECEIVED_0 = 0x1F, +- TPS_CURRENT_1 = 0x20, +- TPS_CURRENT_0 = 0x21, +- TPS_CELL_ID_1 = 0x22, +- TPS_CELL_ID_0 = 0x23, +- TPS_MISC_DATA_2 = 0x24, +- TPS_MISC_DATA_1 = 0x25, +- TPS_MISC_DATA_0 = 0x26, +- RESET = 0x50, +- TPS_GIVEN_1 = 0x51, +- TPS_GIVEN_0 = 0x52, +- ACQ_CTL = 0x53, +- TRL_NOMINAL_RATE_1 = 0x54, +- TRL_NOMINAL_RATE_0 = 0x55, +- INPUT_FREQ_1 = 0x56, +- INPUT_FREQ_0 = 0x57, +- TUNER_ADDR = 0x58, +- CHAN_START_1 = 0x59, +- CHAN_START_0 = 0x5A, +- CONT_1 = 0x5B, +- CONT_0 = 0x5C, +- TUNER_GO = 0x5D, +- STATUS_EN_0 = 0x5F, +- STATUS_EN_1 = 0x60, +- INTERRUPT_EN_0 = 0x61, +- INTERRUPT_EN_1 = 0x62, +- INTERRUPT_EN_2 = 0x63, +- INTERRUPT_EN_3 = 0x64, +- AGC_TARGET = 0x67, +- AGC_CTL = 0x68, +- CAPT_RANGE = 0x75, +- SNR_SELECT_1 = 0x79, +- SNR_SELECT_0 = 0x7A, +- RS_ERR_PER_1 = 0x7C, +- RS_ERR_PER_0 = 0x7D, +- CHIP_ID = 0x7F, +- CHAN_STOP_1 = 0x80, +- CHAN_STOP_0 = 0x81, +- CHAN_STEP_1 = 0x82, +- CHAN_STEP_0 = 0x83, +- FEC_LOCK_TIME = 0x85, +- OFDM_LOCK_TIME = 0x86, +- ACQ_DELAY = 0x87, +- SCAN_CTL = 0x88, +- CLOCK_CTL = 0x89, +- CONFIG = 0x8A, +- MCLK_RATIO = 0x8B, +- GPP_CTL = 0x8C, +- ADC_CTL_1 = 0x8E, +- ADC_CTL_0 = 0x8F +-}; +- +-/* here we assume 1/6MHz == 166.66kHz stepsize */ +-#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ +- +-#endif /* _MT352_PRIV_ */ +diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c +deleted file mode 100644 +index 49ca78d..0000000 +--- a/drivers/media/dvb/frontends/nxt200x.c ++++ /dev/null +@@ -1,1240 +0,0 @@ +-/* +- * Support for NXT2002 and NXT2004 - VSB/QAM +- * +- * Copyright (C) 2005 Kirk Lapray +- * Copyright (C) 2006 Michael Krufky +- * based on nxt2002 by Taylor Jacob +- * and nxt2004 by Jean-Francois Thibert +- * +- * 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. +- * +-*/ +- +-/* +- * NOTES ABOUT THIS DRIVER +- * +- * This Linux driver supports: +- * B2C2/BBTI Technisat Air2PC - ATSC (NXT2002) +- * AverTVHD MCE A180 (NXT2004) +- * ATI HDTV Wonder (NXT2004) +- * +- * This driver needs external firmware. Please use the command +- * "/Documentation/dvb/get_dvb_firmware nxt2002" or +- * "/Documentation/dvb/get_dvb_firmware nxt2004" to +- * download/extract the appropriate firmware, and then copy it to +- * /usr/lib/hotplug/firmware/ or /lib/firmware/ +- * (depending on configuration of firmware hotplug). +- */ +-#define NXT2002_DEFAULT_FIRMWARE "dvb-fe-nxt2002.fw" +-#define NXT2004_DEFAULT_FIRMWARE "dvb-fe-nxt2004.fw" +-#define CRC_CCIT_MASK 0x1021 +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "nxt200x.h" +- +-struct nxt200x_state { +- +- struct i2c_adapter* i2c; +- const struct nxt200x_config* config; +- struct dvb_frontend frontend; +- +- /* demodulator private data */ +- nxt_chip_type demod_chip; +- u8 initialised:1; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "nxt200x: " args); \ +- } while (0) +- +-static int i2c_writebytes (struct nxt200x_state* state, u8 addr, u8 *buf, u8 len) +-{ +- int err; +- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = len }; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n", +- __func__, addr, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int i2c_readbytes(struct nxt200x_state *state, u8 addr, u8 *buf, u8 len) +-{ +- int err; +- struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = buf, .len = len }; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n", +- __func__, addr, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg, +- const u8 *buf, u8 len) +-{ +- u8 buf2 [len+1]; +- int err; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf2, .len = len + 1 }; +- +- buf2[0] = reg; +- memcpy(&buf2[1], buf, len); +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk (KERN_WARNING "nxt200x: %s: i2c write error (addr 0x%02x, err == %i)\n", +- __func__, state->config->demod_address, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int nxt200x_readbytes(struct nxt200x_state *state, u8 reg, u8 *buf, u8 len) +-{ +- u8 reg2 [] = { reg }; +- +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = reg2, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len } }; +- +- int err; +- +- if ((err = i2c_transfer (state->i2c, msg, 2)) != 2) { +- printk (KERN_WARNING "nxt200x: %s: i2c read error (addr 0x%02x, err == %i)\n", +- __func__, state->config->demod_address, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static u16 nxt200x_crc(u16 crc, u8 c) +-{ +- u8 i; +- u16 input = (u16) c & 0xFF; +- +- input<<=8; +- for(i=0; i<8; i++) { +- if((crc^input) & 0x8000) +- crc=(crc<<1)^CRC_CCIT_MASK; +- else +- crc<<=1; +- input<<=1; +- } +- return crc; +-} +- +-static int nxt200x_writereg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) +-{ +- u8 attr, len2, buf; +- dprintk("%s\n", __func__); +- +- /* set mutli register register */ +- nxt200x_writebytes(state, 0x35, ®, 1); +- +- /* send the actual data */ +- nxt200x_writebytes(state, 0x36, data, len); +- +- switch (state->demod_chip) { +- case NXT2002: +- len2 = len; +- buf = 0x02; +- break; +- case NXT2004: +- /* probably not right, but gives correct values */ +- attr = 0x02; +- if (reg & 0x80) { +- attr = attr << 1; +- if (reg & 0x04) +- attr = attr >> 1; +- } +- /* set write bit */ +- len2 = ((attr << 4) | 0x10) | len; +- buf = 0x80; +- break; +- default: +- return -EINVAL; +- break; +- } +- +- /* set multi register length */ +- nxt200x_writebytes(state, 0x34, &len2, 1); +- +- /* toggle the multireg write bit */ +- nxt200x_writebytes(state, 0x21, &buf, 1); +- +- nxt200x_readbytes(state, 0x21, &buf, 1); +- +- switch (state->demod_chip) { +- case NXT2002: +- if ((buf & 0x02) == 0) +- return 0; +- break; +- case NXT2004: +- if (buf == 0) +- return 0; +- break; +- default: +- return -EINVAL; +- break; +- } +- +- printk(KERN_WARNING "nxt200x: Error writing multireg register 0x%02X\n",reg); +- +- return 0; +-} +- +-static int nxt200x_readreg_multibyte (struct nxt200x_state* state, u8 reg, u8* data, u8 len) +-{ +- int i; +- u8 buf, len2, attr; +- dprintk("%s\n", __func__); +- +- /* set mutli register register */ +- nxt200x_writebytes(state, 0x35, ®, 1); +- +- switch (state->demod_chip) { +- case NXT2002: +- /* set multi register length */ +- len2 = len & 0x80; +- nxt200x_writebytes(state, 0x34, &len2, 1); +- +- /* read the actual data */ +- nxt200x_readbytes(state, reg, data, len); +- return 0; +- break; +- case NXT2004: +- /* probably not right, but gives correct values */ +- attr = 0x02; +- if (reg & 0x80) { +- attr = attr << 1; +- if (reg & 0x04) +- attr = attr >> 1; +- } +- +- /* set multi register length */ +- len2 = (attr << 4) | len; +- nxt200x_writebytes(state, 0x34, &len2, 1); +- +- /* toggle the multireg bit*/ +- buf = 0x80; +- nxt200x_writebytes(state, 0x21, &buf, 1); +- +- /* read the actual data */ +- for(i = 0; i < len; i++) { +- nxt200x_readbytes(state, 0x36 + i, &data[i], 1); +- } +- return 0; +- break; +- default: +- return -EINVAL; +- break; +- } +-} +- +-static void nxt200x_microcontroller_stop (struct nxt200x_state* state) +-{ +- u8 buf, stopval, counter = 0; +- dprintk("%s\n", __func__); +- +- /* set correct stop value */ +- switch (state->demod_chip) { +- case NXT2002: +- stopval = 0x40; +- break; +- case NXT2004: +- stopval = 0x10; +- break; +- default: +- stopval = 0; +- break; +- } +- +- buf = 0x80; +- nxt200x_writebytes(state, 0x22, &buf, 1); +- +- while (counter < 20) { +- nxt200x_readbytes(state, 0x31, &buf, 1); +- if (buf & stopval) +- return; +- msleep(10); +- counter++; +- } +- +- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt200x to stop. This is ok after firmware upload.\n"); +- return; +-} +- +-static void nxt200x_microcontroller_start (struct nxt200x_state* state) +-{ +- u8 buf; +- dprintk("%s\n", __func__); +- +- buf = 0x00; +- nxt200x_writebytes(state, 0x22, &buf, 1); +-} +- +-static void nxt2004_microcontroller_init (struct nxt200x_state* state) +-{ +- u8 buf[9]; +- u8 counter = 0; +- dprintk("%s\n", __func__); +- +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x2b, buf, 1); +- buf[0] = 0x70; +- nxt200x_writebytes(state, 0x34, buf, 1); +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x35, buf, 1); +- buf[0] = 0x01; buf[1] = 0x23; buf[2] = 0x45; buf[3] = 0x67; buf[4] = 0x89; +- buf[5] = 0xAB; buf[6] = 0xCD; buf[7] = 0xEF; buf[8] = 0xC0; +- nxt200x_writebytes(state, 0x36, buf, 9); +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x21, buf, 1); +- +- while (counter < 20) { +- nxt200x_readbytes(state, 0x21, buf, 1); +- if (buf[0] == 0) +- return; +- msleep(10); +- counter++; +- } +- +- printk(KERN_WARNING "nxt200x: Timeout waiting for nxt2004 to init.\n"); +- +- return; +-} +- +-static int nxt200x_writetuner (struct nxt200x_state* state, u8* data) +-{ +- u8 buf, count = 0; +- +- dprintk("%s\n", __func__); +- +- dprintk("Tuner Bytes: %02X %02X %02X %02X\n", data[1], data[2], data[3], data[4]); +- +- /* if NXT2004, write directly to tuner. if NXT2002, write through NXT chip. +- * direct write is required for Philips TUV1236D and ALPS TDHU2 */ +- switch (state->demod_chip) { +- case NXT2004: +- if (i2c_writebytes(state, data[0], data+1, 4)) +- printk(KERN_WARNING "nxt200x: error writing to tuner\n"); +- /* wait until we have a lock */ +- while (count < 20) { +- i2c_readbytes(state, data[0], &buf, 1); +- if (buf & 0x40) +- return 0; +- msleep(100); +- count++; +- } +- printk("nxt2004: timeout waiting for tuner lock\n"); +- break; +- case NXT2002: +- /* set the i2c transfer speed to the tuner */ +- buf = 0x03; +- nxt200x_writebytes(state, 0x20, &buf, 1); +- +- /* setup to transfer 4 bytes via i2c */ +- buf = 0x04; +- nxt200x_writebytes(state, 0x34, &buf, 1); +- +- /* write actual tuner bytes */ +- nxt200x_writebytes(state, 0x36, data+1, 4); +- +- /* set tuner i2c address */ +- buf = data[0] << 1; +- nxt200x_writebytes(state, 0x35, &buf, 1); +- +- /* write UC Opmode to begin transfer */ +- buf = 0x80; +- nxt200x_writebytes(state, 0x21, &buf, 1); +- +- while (count < 20) { +- nxt200x_readbytes(state, 0x21, &buf, 1); +- if ((buf & 0x80)== 0x00) +- return 0; +- msleep(100); +- count++; +- } +- printk("nxt2002: timeout error writing tuner\n"); +- break; +- default: +- return -EINVAL; +- break; +- } +- return 0; +-} +- +-static void nxt200x_agc_reset(struct nxt200x_state* state) +-{ +- u8 buf; +- dprintk("%s\n", __func__); +- +- switch (state->demod_chip) { +- case NXT2002: +- buf = 0x08; +- nxt200x_writebytes(state, 0x08, &buf, 1); +- buf = 0x00; +- nxt200x_writebytes(state, 0x08, &buf, 1); +- break; +- case NXT2004: +- nxt200x_readreg_multibyte(state, 0x08, &buf, 1); +- buf = 0x08; +- nxt200x_writereg_multibyte(state, 0x08, &buf, 1); +- buf = 0x00; +- nxt200x_writereg_multibyte(state, 0x08, &buf, 1); +- break; +- default: +- break; +- } +- return; +-} +- +-static int nxt2002_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +-{ +- +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 buf[3], written = 0, chunkpos = 0; +- u16 rambase, position, crc = 0; +- +- dprintk("%s\n", __func__); +- dprintk("Firmware is %zu bytes\n", fw->size); +- +- /* Get the RAM base for this nxt2002 */ +- nxt200x_readbytes(state, 0x10, buf, 1); +- +- if (buf[0] & 0x10) +- rambase = 0x1000; +- else +- rambase = 0x0000; +- +- dprintk("rambase on this nxt2002 is %04X\n", rambase); +- +- /* Hold the micro in reset while loading firmware */ +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x2B, buf, 1); +- +- for (position = 0; position < fw->size; position++) { +- if (written == 0) { +- crc = 0; +- chunkpos = 0x28; +- buf[0] = ((rambase + position) >> 8); +- buf[1] = (rambase + position) & 0xFF; +- buf[2] = 0x81; +- /* write starting address */ +- nxt200x_writebytes(state, 0x29, buf, 3); +- } +- written++; +- chunkpos++; +- +- if ((written % 4) == 0) +- nxt200x_writebytes(state, chunkpos, &fw->data[position-3], 4); +- +- crc = nxt200x_crc(crc, fw->data[position]); +- +- if ((written == 255) || (position+1 == fw->size)) { +- /* write remaining bytes of firmware */ +- nxt200x_writebytes(state, chunkpos+4-(written %4), +- &fw->data[position-(written %4) + 1], +- written %4); +- buf[0] = crc << 8; +- buf[1] = crc & 0xFF; +- +- /* write crc */ +- nxt200x_writebytes(state, 0x2C, buf, 2); +- +- /* do a read to stop things */ +- nxt200x_readbytes(state, 0x2A, buf, 1); +- +- /* set transfer mode to complete */ +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x2B, buf, 1); +- +- written = 0; +- } +- } +- +- return 0; +-}; +- +-static int nxt2004_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +-{ +- +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 buf[3]; +- u16 rambase, position, crc=0; +- +- dprintk("%s\n", __func__); +- dprintk("Firmware is %zu bytes\n", fw->size); +- +- /* set rambase */ +- rambase = 0x1000; +- +- /* hold the micro in reset while loading firmware */ +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x2B, buf,1); +- +- /* calculate firmware CRC */ +- for (position = 0; position < fw->size; position++) { +- crc = nxt200x_crc(crc, fw->data[position]); +- } +- +- buf[0] = rambase >> 8; +- buf[1] = rambase & 0xFF; +- buf[2] = 0x81; +- /* write starting address */ +- nxt200x_writebytes(state,0x29,buf,3); +- +- for (position = 0; position < fw->size;) { +- nxt200x_writebytes(state, 0x2C, &fw->data[position], +- fw->size-position > 255 ? 255 : fw->size-position); +- position += (fw->size-position > 255 ? 255 : fw->size-position); +- } +- buf[0] = crc >> 8; +- buf[1] = crc & 0xFF; +- +- dprintk("firmware crc is 0x%02X 0x%02X\n", buf[0], buf[1]); +- +- /* write crc */ +- nxt200x_writebytes(state, 0x2C, buf,2); +- +- /* do a read to stop things */ +- nxt200x_readbytes(state, 0x2C, buf, 1); +- +- /* set transfer mode to complete */ +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x2B, buf,1); +- +- return 0; +-}; +- +-static int nxt200x_setup_frontend_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 buf[5]; +- +- /* stop the micro first */ +- nxt200x_microcontroller_stop(state); +- +- if (state->demod_chip == NXT2004) { +- /* make sure demod is set to digital */ +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x14, buf, 1); +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x17, buf, 1); +- } +- +- /* set additional params */ +- switch (p->modulation) { +- case QAM_64: +- case QAM_256: +- /* Set punctured clock for QAM */ +- /* This is just a guess since I am unable to test it */ +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 1); +- break; +- case VSB_8: +- /* Set non-punctured clock for VSB */ +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- break; +- default: +- return -EINVAL; +- break; +- } +- +- if (fe->ops.tuner_ops.calc_regs) { +- /* get tuning information */ +- fe->ops.tuner_ops.calc_regs(fe, buf, 5); +- +- /* write frequency information */ +- nxt200x_writetuner(state, buf); +- } +- +- /* reset the agc now that tuning has been completed */ +- nxt200x_agc_reset(state); +- +- /* set target power level */ +- switch (p->modulation) { +- case QAM_64: +- case QAM_256: +- buf[0] = 0x74; +- break; +- case VSB_8: +- buf[0] = 0x70; +- break; +- default: +- return -EINVAL; +- break; +- } +- nxt200x_writebytes(state, 0x42, buf, 1); +- +- /* configure sdm */ +- switch (state->demod_chip) { +- case NXT2002: +- buf[0] = 0x87; +- break; +- case NXT2004: +- buf[0] = 0x07; +- break; +- default: +- return -EINVAL; +- break; +- } +- nxt200x_writebytes(state, 0x57, buf, 1); +- +- /* write sdm1 input */ +- buf[0] = 0x10; +- buf[1] = 0x00; +- switch (state->demod_chip) { +- case NXT2002: +- nxt200x_writereg_multibyte(state, 0x58, buf, 2); +- break; +- case NXT2004: +- nxt200x_writebytes(state, 0x58, buf, 2); +- break; +- default: +- return -EINVAL; +- break; +- } +- +- /* write sdmx input */ +- switch (p->modulation) { +- case QAM_64: +- buf[0] = 0x68; +- break; +- case QAM_256: +- buf[0] = 0x64; +- break; +- case VSB_8: +- buf[0] = 0x60; +- break; +- default: +- return -EINVAL; +- break; +- } +- buf[1] = 0x00; +- switch (state->demod_chip) { +- case NXT2002: +- nxt200x_writereg_multibyte(state, 0x5C, buf, 2); +- break; +- case NXT2004: +- nxt200x_writebytes(state, 0x5C, buf, 2); +- break; +- default: +- return -EINVAL; +- break; +- } +- +- /* write adc power lpf fc */ +- buf[0] = 0x05; +- nxt200x_writebytes(state, 0x43, buf, 1); +- +- if (state->demod_chip == NXT2004) { +- /* write ??? */ +- buf[0] = 0x00; +- buf[1] = 0x00; +- nxt200x_writebytes(state, 0x46, buf, 2); +- } +- +- /* write accumulator2 input */ +- buf[0] = 0x80; +- buf[1] = 0x00; +- switch (state->demod_chip) { +- case NXT2002: +- nxt200x_writereg_multibyte(state, 0x4B, buf, 2); +- break; +- case NXT2004: +- nxt200x_writebytes(state, 0x4B, buf, 2); +- break; +- default: +- return -EINVAL; +- break; +- } +- +- /* write kg1 */ +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x4D, buf, 1); +- +- /* write sdm12 lpf fc */ +- buf[0] = 0x44; +- nxt200x_writebytes(state, 0x55, buf, 1); +- +- /* write agc control reg */ +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x41, buf, 1); +- +- if (state->demod_chip == NXT2004) { +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x24; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- /* soft reset? */ +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x10; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x04; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x81, buf, 1); +- buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; +- nxt200x_writereg_multibyte(state, 0x82, buf, 3); +- nxt200x_readreg_multibyte(state, 0x88, buf, 1); +- buf[0] = 0x11; +- nxt200x_writereg_multibyte(state, 0x88, buf, 1); +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x44; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- } +- +- /* write agc ucgp0 */ +- switch (p->modulation) { +- case QAM_64: +- buf[0] = 0x02; +- break; +- case QAM_256: +- buf[0] = 0x03; +- break; +- case VSB_8: +- buf[0] = 0x00; +- break; +- default: +- return -EINVAL; +- break; +- } +- nxt200x_writebytes(state, 0x30, buf, 1); +- +- /* write agc control reg */ +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x41, buf, 1); +- +- /* write accumulator2 input */ +- buf[0] = 0x80; +- buf[1] = 0x00; +- switch (state->demod_chip) { +- case NXT2002: +- nxt200x_writereg_multibyte(state, 0x49, buf, 2); +- nxt200x_writereg_multibyte(state, 0x4B, buf, 2); +- break; +- case NXT2004: +- nxt200x_writebytes(state, 0x49, buf, 2); +- nxt200x_writebytes(state, 0x4B, buf, 2); +- break; +- default: +- return -EINVAL; +- break; +- } +- +- /* write agc control reg */ +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x41, buf, 1); +- +- nxt200x_microcontroller_start(state); +- +- if (state->demod_chip == NXT2004) { +- nxt2004_microcontroller_init(state); +- +- /* ???? */ +- buf[0] = 0xF0; +- buf[1] = 0x00; +- nxt200x_writebytes(state, 0x5C, buf, 2); +- } +- +- /* adjacent channel detection should be done here, but I don't +- have any stations with this need so I cannot test it */ +- +- return 0; +-} +- +-static int nxt200x_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 lock; +- nxt200x_readbytes(state, 0x31, &lock, 1); +- +- *status = 0; +- if (lock & 0x20) { +- *status |= FE_HAS_SIGNAL; +- *status |= FE_HAS_CARRIER; +- *status |= FE_HAS_VITERBI; +- *status |= FE_HAS_SYNC; +- *status |= FE_HAS_LOCK; +- } +- return 0; +-} +- +-static int nxt200x_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 b[3]; +- +- nxt200x_readreg_multibyte(state, 0xE6, b, 3); +- +- *ber = ((b[0] << 8) + b[1]) * 8; +- +- return 0; +-} +- +-static int nxt200x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 b[2]; +- u16 temp = 0; +- +- /* setup to read cluster variance */ +- b[0] = 0x00; +- nxt200x_writebytes(state, 0xA1, b, 1); +- +- /* get multreg val */ +- nxt200x_readreg_multibyte(state, 0xA6, b, 2); +- +- temp = (b[0] << 8) | b[1]; +- *strength = ((0x7FFF - temp) & 0x0FFF) * 16; +- +- return 0; +-} +- +-static int nxt200x_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 b[2]; +- u16 temp = 0, temp2; +- u32 snrdb = 0; +- +- /* setup to read cluster variance */ +- b[0] = 0x00; +- nxt200x_writebytes(state, 0xA1, b, 1); +- +- /* get multreg val from 0xA6 */ +- nxt200x_readreg_multibyte(state, 0xA6, b, 2); +- +- temp = (b[0] << 8) | b[1]; +- temp2 = 0x7FFF - temp; +- +- /* snr will be in db */ +- if (temp2 > 0x7F00) +- snrdb = 1000*24 + ( 1000*(30-24) * ( temp2 - 0x7F00 ) / ( 0x7FFF - 0x7F00 ) ); +- else if (temp2 > 0x7EC0) +- snrdb = 1000*18 + ( 1000*(24-18) * ( temp2 - 0x7EC0 ) / ( 0x7F00 - 0x7EC0 ) ); +- else if (temp2 > 0x7C00) +- snrdb = 1000*12 + ( 1000*(18-12) * ( temp2 - 0x7C00 ) / ( 0x7EC0 - 0x7C00 ) ); +- else +- snrdb = 1000*0 + ( 1000*(12-0) * ( temp2 - 0 ) / ( 0x7C00 - 0 ) ); +- +- /* the value reported back from the frontend will be FFFF=32db 0000=0db */ +- *snr = snrdb * (0xFFFF/32000); +- +- return 0; +-} +- +-static int nxt200x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- u8 b[3]; +- +- nxt200x_readreg_multibyte(state, 0xE6, b, 3); +- *ucblocks = b[2]; +- +- return 0; +-} +- +-static int nxt200x_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int nxt2002_init(struct dvb_frontend* fe) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- const struct firmware *fw; +- int ret; +- u8 buf[2]; +- +- /* request the firmware, this will block until someone uploads it */ +- printk("nxt2002: Waiting for firmware upload (%s)...\n", NXT2002_DEFAULT_FIRMWARE); +- ret = request_firmware(&fw, NXT2002_DEFAULT_FIRMWARE, +- state->i2c->dev.parent); +- printk("nxt2002: Waiting for firmware upload(2)...\n"); +- if (ret) { +- printk("nxt2002: No firmware uploaded (timeout or file not found?)\n"); +- return ret; +- } +- +- ret = nxt2002_load_firmware(fe, fw); +- release_firmware(fw); +- if (ret) { +- printk("nxt2002: Writing firmware to device failed\n"); +- return ret; +- } +- printk("nxt2002: Firmware upload complete\n"); +- +- /* Put the micro into reset */ +- nxt200x_microcontroller_stop(state); +- +- /* ensure transfer is complete */ +- buf[0]=0x00; +- nxt200x_writebytes(state, 0x2B, buf, 1); +- +- /* Put the micro into reset for real this time */ +- nxt200x_microcontroller_stop(state); +- +- /* soft reset everything (agc,frontend,eq,fec)*/ +- buf[0] = 0x0F; +- nxt200x_writebytes(state, 0x08, buf, 1); +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x08, buf, 1); +- +- /* write agc sdm configure */ +- buf[0] = 0xF1; +- nxt200x_writebytes(state, 0x57, buf, 1); +- +- /* write mod output format */ +- buf[0] = 0x20; +- nxt200x_writebytes(state, 0x09, buf, 1); +- +- /* write fec mpeg mode */ +- buf[0] = 0x7E; +- buf[1] = 0x00; +- nxt200x_writebytes(state, 0xE9, buf, 2); +- +- /* write mux selection */ +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0xCC, buf, 1); +- +- return 0; +-} +- +-static int nxt2004_init(struct dvb_frontend* fe) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- const struct firmware *fw; +- int ret; +- u8 buf[3]; +- +- /* ??? */ +- buf[0]=0x00; +- nxt200x_writebytes(state, 0x1E, buf, 1); +- +- /* request the firmware, this will block until someone uploads it */ +- printk("nxt2004: Waiting for firmware upload (%s)...\n", NXT2004_DEFAULT_FIRMWARE); +- ret = request_firmware(&fw, NXT2004_DEFAULT_FIRMWARE, +- state->i2c->dev.parent); +- printk("nxt2004: Waiting for firmware upload(2)...\n"); +- if (ret) { +- printk("nxt2004: No firmware uploaded (timeout or file not found?)\n"); +- return ret; +- } +- +- ret = nxt2004_load_firmware(fe, fw); +- release_firmware(fw); +- if (ret) { +- printk("nxt2004: Writing firmware to device failed\n"); +- return ret; +- } +- printk("nxt2004: Firmware upload complete\n"); +- +- /* ensure transfer is complete */ +- buf[0] = 0x01; +- nxt200x_writebytes(state, 0x19, buf, 1); +- +- nxt2004_microcontroller_init(state); +- nxt200x_microcontroller_stop(state); +- nxt200x_microcontroller_stop(state); +- nxt2004_microcontroller_init(state); +- nxt200x_microcontroller_stop(state); +- +- /* soft reset everything (agc,frontend,eq,fec)*/ +- buf[0] = 0xFF; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- +- /* write agc sdm configure */ +- buf[0] = 0xD7; +- nxt200x_writebytes(state, 0x57, buf, 1); +- +- /* ???*/ +- buf[0] = 0x07; +- buf[1] = 0xfe; +- nxt200x_writebytes(state, 0x35, buf, 2); +- buf[0] = 0x12; +- nxt200x_writebytes(state, 0x34, buf, 1); +- buf[0] = 0x80; +- nxt200x_writebytes(state, 0x21, buf, 1); +- +- /* ???*/ +- buf[0] = 0x21; +- nxt200x_writebytes(state, 0x0A, buf, 1); +- +- /* ???*/ +- buf[0] = 0x01; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- /* write fec mpeg mode */ +- buf[0] = 0x7E; +- buf[1] = 0x00; +- nxt200x_writebytes(state, 0xE9, buf, 2); +- +- /* write mux selection */ +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0xCC, buf, 1); +- +- /* ???*/ +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- /* soft reset? */ +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x10; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- +- /* ???*/ +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x01; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x70; +- nxt200x_writereg_multibyte(state, 0x81, buf, 1); +- buf[0] = 0x31; buf[1] = 0x5E; buf[2] = 0x66; +- nxt200x_writereg_multibyte(state, 0x82, buf, 3); +- +- nxt200x_readreg_multibyte(state, 0x88, buf, 1); +- buf[0] = 0x11; +- nxt200x_writereg_multibyte(state, 0x88, buf, 1); +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x40; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- nxt200x_readbytes(state, 0x10, buf, 1); +- buf[0] = 0x10; +- nxt200x_writebytes(state, 0x10, buf, 1); +- nxt200x_readbytes(state, 0x0A, buf, 1); +- buf[0] = 0x21; +- nxt200x_writebytes(state, 0x0A, buf, 1); +- +- nxt2004_microcontroller_init(state); +- +- buf[0] = 0x21; +- nxt200x_writebytes(state, 0x0A, buf, 1); +- buf[0] = 0x7E; +- nxt200x_writebytes(state, 0xE9, buf, 1); +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0xEA, buf, 1); +- +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- /* soft reset? */ +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x10; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- nxt200x_readreg_multibyte(state, 0x08, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x08, buf, 1); +- +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x04; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x00; +- nxt200x_writereg_multibyte(state, 0x81, buf, 1); +- buf[0] = 0x80; buf[1] = 0x00; buf[2] = 0x00; +- nxt200x_writereg_multibyte(state, 0x82, buf, 3); +- +- nxt200x_readreg_multibyte(state, 0x88, buf, 1); +- buf[0] = 0x11; +- nxt200x_writereg_multibyte(state, 0x88, buf, 1); +- +- nxt200x_readreg_multibyte(state, 0x80, buf, 1); +- buf[0] = 0x44; +- nxt200x_writereg_multibyte(state, 0x80, buf, 1); +- +- /* initialize tuner */ +- nxt200x_readbytes(state, 0x10, buf, 1); +- buf[0] = 0x12; +- nxt200x_writebytes(state, 0x10, buf, 1); +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x13, buf, 1); +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x16, buf, 1); +- buf[0] = 0x04; +- nxt200x_writebytes(state, 0x14, buf, 1); +- buf[0] = 0x00; +- nxt200x_writebytes(state, 0x14, buf, 1); +- nxt200x_writebytes(state, 0x17, buf, 1); +- nxt200x_writebytes(state, 0x14, buf, 1); +- nxt200x_writebytes(state, 0x17, buf, 1); +- +- return 0; +-} +- +-static int nxt200x_init(struct dvb_frontend* fe) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- int ret = 0; +- +- if (!state->initialised) { +- switch (state->demod_chip) { +- case NXT2002: +- ret = nxt2002_init(fe); +- break; +- case NXT2004: +- ret = nxt2004_init(fe); +- break; +- default: +- return -EINVAL; +- break; +- } +- state->initialised = 1; +- } +- return ret; +-} +- +-static int nxt200x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 500; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void nxt200x_release(struct dvb_frontend* fe) +-{ +- struct nxt200x_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops nxt200x_ops; +- +-struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, +- struct i2c_adapter* i2c) +-{ +- struct nxt200x_state* state = NULL; +- u8 buf [] = {0,0,0,0,0}; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct nxt200x_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- +- /* read card id */ +- nxt200x_readbytes(state, 0x00, buf, 5); +- dprintk("NXT info: %02X %02X %02X %02X %02X\n", +- buf[0], buf[1], buf[2], buf[3], buf[4]); +- +- /* set demod chip */ +- switch (buf[0]) { +- case 0x04: +- state->demod_chip = NXT2002; +- printk("nxt200x: NXT2002 Detected\n"); +- break; +- case 0x05: +- state->demod_chip = NXT2004; +- printk("nxt200x: NXT2004 Detected\n"); +- break; +- default: +- goto error; +- } +- +- /* make sure demod chip is supported */ +- switch (state->demod_chip) { +- case NXT2002: +- if (buf[0] != 0x04) goto error; /* device id */ +- if (buf[1] != 0x02) goto error; /* fab id */ +- if (buf[2] != 0x11) goto error; /* month */ +- if (buf[3] != 0x20) goto error; /* year msb */ +- if (buf[4] != 0x00) goto error; /* year lsb */ +- break; +- case NXT2004: +- if (buf[0] != 0x05) goto error; /* device id */ +- break; +- default: +- goto error; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &nxt200x_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- printk("Unknown/Unsupported NXT chip: %02X %02X %02X %02X %02X\n", +- buf[0], buf[1], buf[2], buf[3], buf[4]); +- return NULL; +-} +- +-static struct dvb_frontend_ops nxt200x_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Nextwave NXT200X VSB/QAM frontend", +- .frequency_min = 54000000, +- .frequency_max = 860000000, +- .frequency_stepsize = 166666, /* stepsize is just a guess */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_8VSB | FE_CAN_QAM_64 | FE_CAN_QAM_256 +- }, +- +- .release = nxt200x_release, +- +- .init = nxt200x_init, +- .sleep = nxt200x_sleep, +- +- .set_frontend = nxt200x_setup_frontend_parameters, +- .get_tune_settings = nxt200x_get_tune_settings, +- +- .read_status = nxt200x_read_status, +- .read_ber = nxt200x_read_ber, +- .read_signal_strength = nxt200x_read_signal_strength, +- .read_snr = nxt200x_read_snr, +- .read_ucblocks = nxt200x_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulator Driver"); +-MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(nxt200x_attach); +- +diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb/frontends/nxt200x.h +deleted file mode 100644 +index f3c8458..0000000 +--- a/drivers/media/dvb/frontends/nxt200x.h ++++ /dev/null +@@ -1,63 +0,0 @@ +-/* +- * Support for NXT2002 and NXT2004 - VSB/QAM +- * +- * Copyright (C) 2005 Kirk Lapray (kirk.lapray@gmail.com) +- * based on nxt2002 by Taylor Jacob +- * and nxt2004 by Jean-Francois Thibert (jeanfrancois@sagetv.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. +- * +-*/ +- +-#ifndef NXT200X_H +-#define NXT200X_H +- +-#include +-#include +- +-typedef enum nxt_chip_t { +- NXTUNDEFINED, +- NXT2002, +- NXT2004 +-}nxt_chip_type; +- +-struct nxt200x_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* need to set device param for start_dma */ +- int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); +-}; +- +-#if defined(CONFIG_DVB_NXT200X) || (defined(CONFIG_DVB_NXT200X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_NXT200X +- +-#endif /* NXT200X_H */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c +deleted file mode 100644 +index 90ae6c7..0000000 +--- a/drivers/media/dvb/frontends/nxt6000.c ++++ /dev/null +@@ -1,616 +0,0 @@ +-/* +- NxtWave Communications - NXT6000 demodulator driver +- +- Copyright (C) 2002-2003 Florian Schirmer +- Copyright (C) 2003 Paul Andreassen +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "nxt6000_priv.h" +-#include "nxt6000.h" +- +- +- +-struct nxt6000_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct nxt6000_config* config; +- struct dvb_frontend frontend; +-}; +- +-static int debug; +-#define dprintk if (debug) printk +- +-static int nxt6000_writereg(struct nxt6000_state* state, u8 reg, u8 data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; +- int ret; +- +- if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) +- dprintk("nxt6000: nxt6000_write error (reg: 0x%02X, data: 0x%02X, ret: %d)\n", reg, data, ret); +- +- return (ret != 1) ? -EIO : 0; +-} +- +-static u8 nxt6000_readreg(struct nxt6000_state* state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msgs[] = { +- {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, +- {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} +- }; +- +- ret = i2c_transfer(state->i2c, msgs, 2); +- +- if (ret != 2) +- dprintk("nxt6000: nxt6000_read error (reg: 0x%02X, ret: %d)\n", reg, ret); +- +- return b1[0]; +-} +- +-static void nxt6000_reset(struct nxt6000_state* state) +-{ +- u8 val; +- +- val = nxt6000_readreg(state, OFDM_COR_CTL); +- +- nxt6000_writereg(state, OFDM_COR_CTL, val & ~COREACT); +- nxt6000_writereg(state, OFDM_COR_CTL, val | COREACT); +-} +- +-static int nxt6000_set_bandwidth(struct nxt6000_state *state, u32 bandwidth) +-{ +- u16 nominal_rate; +- int result; +- +- switch (bandwidth) { +- case 6000000: +- nominal_rate = 0x55B7; +- break; +- +- case 7000000: +- nominal_rate = 0x6400; +- break; +- +- case 8000000: +- nominal_rate = 0x7249; +- break; +- +- default: +- return -EINVAL; +- } +- +- if ((result = nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, nominal_rate & 0xFF)) < 0) +- return result; +- +- return nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, (nominal_rate >> 8) & 0xFF); +-} +- +-static int nxt6000_set_guard_interval(struct nxt6000_state* state, fe_guard_interval_t guard_interval) +-{ +- switch (guard_interval) { +- +- case GUARD_INTERVAL_1_32: +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x00 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); +- +- case GUARD_INTERVAL_1_16: +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x01 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); +- +- case GUARD_INTERVAL_AUTO: +- case GUARD_INTERVAL_1_8: +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x02 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); +- +- case GUARD_INTERVAL_1_4: +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, 0x03 | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x03)); +- +- default: +- return -EINVAL; +- } +-} +- +-static int nxt6000_set_inversion(struct nxt6000_state* state, fe_spectral_inversion_t inversion) +-{ +- switch (inversion) { +- +- case INVERSION_OFF: +- return nxt6000_writereg(state, OFDM_ITB_CTL, 0x00); +- +- case INVERSION_ON: +- return nxt6000_writereg(state, OFDM_ITB_CTL, ITBINV); +- +- default: +- return -EINVAL; +- +- } +-} +- +-static int nxt6000_set_transmission_mode(struct nxt6000_state* state, fe_transmit_mode_t transmission_mode) +-{ +- int result; +- +- switch (transmission_mode) { +- +- case TRANSMISSION_MODE_2K: +- if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x00 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) +- return result; +- +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x00 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); +- +- case TRANSMISSION_MODE_8K: +- case TRANSMISSION_MODE_AUTO: +- if ((result = nxt6000_writereg(state, EN_DMD_RACQ, 0x02 | (nxt6000_readreg(state, EN_DMD_RACQ) & ~0x03))) < 0) +- return result; +- +- return nxt6000_writereg(state, OFDM_COR_MODEGUARD, (0x01 << 2) | (nxt6000_readreg(state, OFDM_COR_MODEGUARD) & ~0x04)); +- +- default: +- return -EINVAL; +- +- } +-} +- +-static void nxt6000_setup(struct dvb_frontend* fe) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- nxt6000_writereg(state, RS_COR_SYNC_PARAM, SYNC_PARAM); +- nxt6000_writereg(state, BER_CTRL, /*(1 << 2) | */ (0x01 << 1) | 0x01); +- nxt6000_writereg(state, VIT_BERTIME_2, 0x00); // BER Timer = 0x000200 * 256 = 131072 bits +- nxt6000_writereg(state, VIT_BERTIME_1, 0x02); // +- nxt6000_writereg(state, VIT_BERTIME_0, 0x00); // +- nxt6000_writereg(state, VIT_COR_INTEN, 0x98); // Enable BER interrupts +- nxt6000_writereg(state, VIT_COR_CTL, 0x82); // Enable BER measurement +- nxt6000_writereg(state, VIT_COR_CTL, VIT_COR_RESYNC | 0x02 ); +- nxt6000_writereg(state, OFDM_COR_CTL, (0x01 << 5) | (nxt6000_readreg(state, OFDM_COR_CTL) & 0x0F)); +- nxt6000_writereg(state, OFDM_COR_MODEGUARD, FORCEMODE8K | 0x02); +- nxt6000_writereg(state, OFDM_AGC_CTL, AGCLAST | INITIAL_AGC_BW); +- nxt6000_writereg(state, OFDM_ITB_FREQ_1, 0x06); +- nxt6000_writereg(state, OFDM_ITB_FREQ_2, 0x31); +- nxt6000_writereg(state, OFDM_CAS_CTL, (0x01 << 7) | (0x02 << 3) | 0x04); +- nxt6000_writereg(state, CAS_FREQ, 0xBB); /* CHECKME */ +- nxt6000_writereg(state, OFDM_SYR_CTL, 1 << 2); +- nxt6000_writereg(state, OFDM_PPM_CTL_1, PPM256); +- nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_1, 0x49); +- nxt6000_writereg(state, OFDM_TRL_NOMINALRATE_2, 0x72); +- nxt6000_writereg(state, ANALOG_CONTROL_0, 1 << 5); +- nxt6000_writereg(state, EN_DMD_RACQ, (1 << 7) | (3 << 4) | 2); +- nxt6000_writereg(state, DIAG_CONFIG, TB_SET); +- +- if (state->config->clock_inversion) +- nxt6000_writereg(state, SUB_DIAG_MODE_SEL, CLKINVERSION); +- else +- nxt6000_writereg(state, SUB_DIAG_MODE_SEL, 0); +- +- nxt6000_writereg(state, TS_FORMAT, 0); +-} +- +-static void nxt6000_dump_status(struct nxt6000_state *state) +-{ +- u8 val; +- +-/* +- printk("RS_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, RS_COR_STAT)); +- printk("VIT_SYNC_STATUS: 0x%02X\n", nxt6000_readreg(fe, VIT_SYNC_STATUS)); +- printk("OFDM_COR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_COR_STAT)); +- printk("OFDM_SYR_STAT: 0x%02X\n", nxt6000_readreg(fe, OFDM_SYR_STAT)); +- printk("OFDM_TPS_RCVD_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_1)); +- printk("OFDM_TPS_RCVD_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_2)); +- printk("OFDM_TPS_RCVD_3: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_3)); +- printk("OFDM_TPS_RCVD_4: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RCVD_4)); +- printk("OFDM_TPS_RESERVED_1: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_1)); +- printk("OFDM_TPS_RESERVED_2: 0x%02X\n", nxt6000_readreg(fe, OFDM_TPS_RESERVED_2)); +-*/ +- printk("NXT6000 status:"); +- +- val = nxt6000_readreg(state, RS_COR_STAT); +- +- printk(" DATA DESCR LOCK: %d,", val & 0x01); +- printk(" DATA SYNC LOCK: %d,", (val >> 1) & 0x01); +- +- val = nxt6000_readreg(state, VIT_SYNC_STATUS); +- +- printk(" VITERBI LOCK: %d,", (val >> 7) & 0x01); +- +- switch ((val >> 4) & 0x07) { +- +- case 0x00: +- printk(" VITERBI CODERATE: 1/2,"); +- break; +- +- case 0x01: +- printk(" VITERBI CODERATE: 2/3,"); +- break; +- +- case 0x02: +- printk(" VITERBI CODERATE: 3/4,"); +- break; +- +- case 0x03: +- printk(" VITERBI CODERATE: 5/6,"); +- break; +- +- case 0x04: +- printk(" VITERBI CODERATE: 7/8,"); +- break; +- +- default: +- printk(" VITERBI CODERATE: Reserved,"); +- +- } +- +- val = nxt6000_readreg(state, OFDM_COR_STAT); +- +- printk(" CHCTrack: %d,", (val >> 7) & 0x01); +- printk(" TPSLock: %d,", (val >> 6) & 0x01); +- printk(" SYRLock: %d,", (val >> 5) & 0x01); +- printk(" AGCLock: %d,", (val >> 4) & 0x01); +- +- switch (val & 0x0F) { +- +- case 0x00: +- printk(" CoreState: IDLE,"); +- break; +- +- case 0x02: +- printk(" CoreState: WAIT_AGC,"); +- break; +- +- case 0x03: +- printk(" CoreState: WAIT_SYR,"); +- break; +- +- case 0x04: +- printk(" CoreState: WAIT_PPM,"); +- break; +- +- case 0x01: +- printk(" CoreState: WAIT_TRL,"); +- break; +- +- case 0x05: +- printk(" CoreState: WAIT_TPS,"); +- break; +- +- case 0x06: +- printk(" CoreState: MONITOR_TPS,"); +- break; +- +- default: +- printk(" CoreState: Reserved,"); +- +- } +- +- val = nxt6000_readreg(state, OFDM_SYR_STAT); +- +- printk(" SYRLock: %d,", (val >> 4) & 0x01); +- printk(" SYRMode: %s,", (val >> 2) & 0x01 ? "8K" : "2K"); +- +- switch ((val >> 4) & 0x03) { +- +- case 0x00: +- printk(" SYRGuard: 1/32,"); +- break; +- +- case 0x01: +- printk(" SYRGuard: 1/16,"); +- break; +- +- case 0x02: +- printk(" SYRGuard: 1/8,"); +- break; +- +- case 0x03: +- printk(" SYRGuard: 1/4,"); +- break; +- } +- +- val = nxt6000_readreg(state, OFDM_TPS_RCVD_3); +- +- switch ((val >> 4) & 0x07) { +- +- case 0x00: +- printk(" TPSLP: 1/2,"); +- break; +- +- case 0x01: +- printk(" TPSLP: 2/3,"); +- break; +- +- case 0x02: +- printk(" TPSLP: 3/4,"); +- break; +- +- case 0x03: +- printk(" TPSLP: 5/6,"); +- break; +- +- case 0x04: +- printk(" TPSLP: 7/8,"); +- break; +- +- default: +- printk(" TPSLP: Reserved,"); +- +- } +- +- switch (val & 0x07) { +- +- case 0x00: +- printk(" TPSHP: 1/2,"); +- break; +- +- case 0x01: +- printk(" TPSHP: 2/3,"); +- break; +- +- case 0x02: +- printk(" TPSHP: 3/4,"); +- break; +- +- case 0x03: +- printk(" TPSHP: 5/6,"); +- break; +- +- case 0x04: +- printk(" TPSHP: 7/8,"); +- break; +- +- default: +- printk(" TPSHP: Reserved,"); +- +- } +- +- val = nxt6000_readreg(state, OFDM_TPS_RCVD_4); +- +- printk(" TPSMode: %s,", val & 0x01 ? "8K" : "2K"); +- +- switch ((val >> 4) & 0x03) { +- +- case 0x00: +- printk(" TPSGuard: 1/32,"); +- break; +- +- case 0x01: +- printk(" TPSGuard: 1/16,"); +- break; +- +- case 0x02: +- printk(" TPSGuard: 1/8,"); +- break; +- +- case 0x03: +- printk(" TPSGuard: 1/4,"); +- break; +- +- } +- +- /* Strange magic required to gain access to RF_AGC_STATUS */ +- nxt6000_readreg(state, RF_AGC_VAL_1); +- val = nxt6000_readreg(state, RF_AGC_STATUS); +- val = nxt6000_readreg(state, RF_AGC_STATUS); +- +- printk(" RF AGC LOCK: %d,", (val >> 4) & 0x01); +- printk("\n"); +-} +- +-static int nxt6000_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- u8 core_status; +- struct nxt6000_state* state = fe->demodulator_priv; +- +- *status = 0; +- +- core_status = nxt6000_readreg(state, OFDM_COR_STAT); +- +- if (core_status & AGCLOCKED) +- *status |= FE_HAS_SIGNAL; +- +- if (nxt6000_readreg(state, OFDM_SYR_STAT) & GI14_SYR_LOCK) +- *status |= FE_HAS_CARRIER; +- +- if (nxt6000_readreg(state, VIT_SYNC_STATUS) & VITINSYNC) +- *status |= FE_HAS_VITERBI; +- +- if (nxt6000_readreg(state, RS_COR_STAT) & RSCORESTATUS) +- *status |= FE_HAS_SYNC; +- +- if ((core_status & TPSLOCKED) && (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC))) +- *status |= FE_HAS_LOCK; +- +- if (debug) +- nxt6000_dump_status(state); +- +- return 0; +-} +- +-static int nxt6000_init(struct dvb_frontend* fe) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- nxt6000_reset(state); +- nxt6000_setup(fe); +- +- return 0; +-} +- +-static int nxt6000_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct nxt6000_state* state = fe->demodulator_priv; +- int result; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- result = nxt6000_set_bandwidth(state, p->bandwidth_hz); +- if (result < 0) +- return result; +- +- result = nxt6000_set_guard_interval(state, p->guard_interval); +- if (result < 0) +- return result; +- +- result = nxt6000_set_transmission_mode(state, p->transmission_mode); +- if (result < 0) +- return result; +- +- result = nxt6000_set_inversion(state, p->inversion); +- if (result < 0) +- return result; +- +- msleep(500); +- return 0; +-} +- +-static void nxt6000_release(struct dvb_frontend* fe) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static int nxt6000_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- *snr = nxt6000_readreg( state, OFDM_CHC_SNR) / 8; +- +- return 0; +-} +- +-static int nxt6000_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18 ); +- +- *ber = (nxt6000_readreg( state, VIT_BER_1 ) << 8 ) | +- nxt6000_readreg( state, VIT_BER_0 ); +- +- nxt6000_writereg( state, VIT_COR_INTSTAT, 0x18); // Clear BER Done interrupts +- +- return 0; +-} +- +-static int nxt6000_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- *signal_strength = (short) (511 - +- (nxt6000_readreg(state, AGC_GAIN_1) + +- ((nxt6000_readreg(state, AGC_GAIN_2) & 0x03) << 8))); +- +- return 0; +-} +- +-static int nxt6000_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 500; +- return 0; +-} +- +-static int nxt6000_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct nxt6000_state* state = fe->demodulator_priv; +- +- if (enable) { +- return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x01); +- } else { +- return nxt6000_writereg(state, ENABLE_TUNER_IIC, 0x00); +- } +-} +- +-static struct dvb_frontend_ops nxt6000_ops; +- +-struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, +- struct i2c_adapter* i2c) +-{ +- struct nxt6000_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if (nxt6000_readreg(state, OFDM_MSC_REV) != NXT6000ASICDEVICE) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &nxt6000_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops nxt6000_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "NxtWave NXT6000 DVB-T", +- .frequency_min = 0, +- .frequency_max = 863250000, +- .frequency_stepsize = 62500, +- /*.frequency_tolerance = *//* FIXME: 12% of SR */ +- .symbol_rate_min = 0, /* FIXME */ +- .symbol_rate_max = 9360000, /* FIXME */ +- .symbol_rate_tolerance = 4000, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = nxt6000_release, +- +- .init = nxt6000_init, +- .i2c_gate_ctrl = nxt6000_i2c_gate_ctrl, +- +- .get_tune_settings = nxt6000_fe_get_tune_settings, +- +- .set_frontend = nxt6000_set_frontend, +- +- .read_status = nxt6000_read_status, +- .read_ber = nxt6000_read_ber, +- .read_signal_strength = nxt6000_read_signal_strength, +- .read_snr = nxt6000_read_snr, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); +-MODULE_AUTHOR("Florian Schirmer"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(nxt6000_attach); +diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h +deleted file mode 100644 +index 878eb38..0000000 +--- a/drivers/media/dvb/frontends/nxt6000.h ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- NxtWave Communications - NXT6000 demodulator driver +- +- Copyright (C) 2002-2003 Florian Schirmer +- Copyright (C) 2003 Paul Andreassen +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef NXT6000_H +-#define NXT6000_H +- +-#include +- +-struct nxt6000_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* should clock inversion be used? */ +- u8 clock_inversion:1; +-}; +- +-#if defined(CONFIG_DVB_NXT6000) || (defined(CONFIG_DVB_NXT6000_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_NXT6000 +- +-#endif // NXT6000_H +diff --git a/drivers/media/dvb/frontends/nxt6000_priv.h b/drivers/media/dvb/frontends/nxt6000_priv.h +deleted file mode 100644 +index 0422e58..0000000 +--- a/drivers/media/dvb/frontends/nxt6000_priv.h ++++ /dev/null +@@ -1,286 +0,0 @@ +-/* +- * Public Include File for DRV6000 users +- * (ie. NxtWave Communications - NXT6000 demodulator driver) +- * +- * Copyright (C) 2001 NxtWave Communications, Inc. +- * +- */ +- +-/* Nxt6000 Register Addresses and Bit Masks */ +- +-/* Maximum Register Number */ +-#define MAXNXT6000REG (0x9A) +- +-/* 0x1B A_VIT_BER_0 aka 0x3A */ +-#define A_VIT_BER_0 (0x1B) +- +-/* 0x1D A_VIT_BER_TIMER_0 aka 0x38 */ +-#define A_VIT_BER_TIMER_0 (0x1D) +- +-/* 0x21 RS_COR_STAT */ +-#define RS_COR_STAT (0x21) +-#define RSCORESTATUS (0x03) +- +-/* 0x22 RS_COR_INTEN */ +-#define RS_COR_INTEN (0x22) +- +-/* 0x23 RS_COR_INSTAT */ +-#define RS_COR_INSTAT (0x23) +-#define INSTAT_ERROR (0x04) +-#define LOCK_LOSS_BITS (0x03) +- +-/* 0x24 RS_COR_SYNC_PARAM */ +-#define RS_COR_SYNC_PARAM (0x24) +-#define SYNC_PARAM (0x03) +- +-/* 0x25 BER_CTRL */ +-#define BER_CTRL (0x25) +-#define BER_ENABLE (0x02) +-#define BER_RESET (0x01) +- +-/* 0x26 BER_PAY */ +-#define BER_PAY (0x26) +- +-/* 0x27 BER_PKT_L */ +-#define BER_PKT_L (0x27) +-#define BER_PKTOVERFLOW (0x80) +- +-/* 0x30 VIT_COR_CTL */ +-#define VIT_COR_CTL (0x30) +-#define BER_CONTROL (0x02) +-#define VIT_COR_MASK (0x82) +-#define VIT_COR_RESYNC (0x80) +- +- +-/* 0x32 VIT_SYNC_STATUS */ +-#define VIT_SYNC_STATUS (0x32) +-#define VITINSYNC (0x80) +- +-/* 0x33 VIT_COR_INTEN */ +-#define VIT_COR_INTEN (0x33) +-#define GLOBAL_ENABLE (0x80) +- +-/* 0x34 VIT_COR_INTSTAT */ +-#define VIT_COR_INTSTAT (0x34) +-#define BER_DONE (0x08) +-#define BER_OVERFLOW (0x10) +- +-/* 0x38 VIT_BERTIME_2 */ +-#define VIT_BERTIME_2 (0x38) +- +-/* 0x39 VIT_BERTIME_1 */ +-#define VIT_BERTIME_1 (0x39) +- +-/* 0x3A VIT_BERTIME_0 */ +-#define VIT_BERTIME_0 (0x3a) +- +- /* 0x38 OFDM_BERTimer *//* Use the alias registers */ +-#define A_VIT_BER_TIMER_0 (0x1D) +- +- /* 0x3A VIT_BER_TIMER_0 *//* Use the alias registers */ +-#define A_VIT_BER_0 (0x1B) +- +-/* 0x3B VIT_BER_1 */ +-#define VIT_BER_1 (0x3b) +- +-/* 0x3C VIT_BER_0 */ +-#define VIT_BER_0 (0x3c) +- +-/* 0x40 OFDM_COR_CTL */ +-#define OFDM_COR_CTL (0x40) +-#define COREACT (0x20) +-#define HOLDSM (0x10) +-#define WAIT_AGC (0x02) +-#define WAIT_SYR (0x03) +- +-/* 0x41 OFDM_COR_STAT */ +-#define OFDM_COR_STAT (0x41) +-#define COR_STATUS (0x0F) +-#define MONITOR_TPS (0x06) +-#define TPSLOCKED (0x40) +-#define AGCLOCKED (0x10) +- +-/* 0x42 OFDM_COR_INTEN */ +-#define OFDM_COR_INTEN (0x42) +-#define TPSRCVBAD (0x04) +-#define TPSRCVCHANGED (0x02) +-#define TPSRCVUPDATE (0x01) +- +-/* 0x43 OFDM_COR_INSTAT */ +-#define OFDM_COR_INSTAT (0x43) +- +-/* 0x44 OFDM_COR_MODEGUARD */ +-#define OFDM_COR_MODEGUARD (0x44) +-#define FORCEMODE (0x08) +-#define FORCEMODE8K (0x04) +- +-/* 0x45 OFDM_AGC_CTL */ +-#define OFDM_AGC_CTL (0x45) +-#define INITIAL_AGC_BW (0x08) +-#define AGCNEG (0x02) +-#define AGCLAST (0x10) +- +-/* 0x48 OFDM_AGC_TARGET */ +-#define OFDM_AGC_TARGET (0x48) +-#define OFDM_AGC_TARGET_DEFAULT (0x28) +-#define OFDM_AGC_TARGET_IMPULSE (0x38) +- +-/* 0x49 OFDM_AGC_GAIN_1 */ +-#define OFDM_AGC_GAIN_1 (0x49) +- +-/* 0x4B OFDM_ITB_CTL */ +-#define OFDM_ITB_CTL (0x4B) +-#define ITBINV (0x01) +- +-/* 0x49 AGC_GAIN_1 */ +-#define AGC_GAIN_1 (0x49) +- +-/* 0x4A AGC_GAIN_2 */ +-#define AGC_GAIN_2 (0x4A) +- +-/* 0x4C OFDM_ITB_FREQ_1 */ +-#define OFDM_ITB_FREQ_1 (0x4C) +- +-/* 0x4D OFDM_ITB_FREQ_2 */ +-#define OFDM_ITB_FREQ_2 (0x4D) +- +-/* 0x4E OFDM_CAS_CTL */ +-#define OFDM_CAS_CTL (0x4E) +-#define ACSDIS (0x40) +-#define CCSEN (0x80) +- +-/* 0x4F CAS_FREQ */ +-#define CAS_FREQ (0x4F) +- +-/* 0x51 OFDM_SYR_CTL */ +-#define OFDM_SYR_CTL (0x51) +-#define SIXTH_ENABLE (0x80) +-#define SYR_TRACKING_DISABLE (0x01) +- +-/* 0x52 OFDM_SYR_STAT */ +-#define OFDM_SYR_STAT (0x52) +-#define GI14_2K_SYR_LOCK (0x13) +-#define GI14_8K_SYR_LOCK (0x17) +-#define GI14_SYR_LOCK (0x10) +- +-/* 0x55 OFDM_SYR_OFFSET_1 */ +-#define OFDM_SYR_OFFSET_1 (0x55) +- +-/* 0x56 OFDM_SYR_OFFSET_2 */ +-#define OFDM_SYR_OFFSET_2 (0x56) +- +-/* 0x58 OFDM_SCR_CTL */ +-#define OFDM_SCR_CTL (0x58) +-#define SYR_ADJ_DECAY_MASK (0x70) +-#define SYR_ADJ_DECAY (0x30) +- +-/* 0x59 OFDM_PPM_CTL_1 */ +-#define OFDM_PPM_CTL_1 (0x59) +-#define PPMMAX_MASK (0x30) +-#define PPM256 (0x30) +- +-/* 0x5B OFDM_TRL_NOMINALRATE_1 */ +-#define OFDM_TRL_NOMINALRATE_1 (0x5B) +- +-/* 0x5C OFDM_TRL_NOMINALRATE_2 */ +-#define OFDM_TRL_NOMINALRATE_2 (0x5C) +- +-/* 0x5D OFDM_TRL_TIME_1 */ +-#define OFDM_TRL_TIME_1 (0x5D) +- +-/* 0x60 OFDM_CRL_FREQ_1 */ +-#define OFDM_CRL_FREQ_1 (0x60) +- +-/* 0x63 OFDM_CHC_CTL_1 */ +-#define OFDM_CHC_CTL_1 (0x63) +-#define MANMEAN1 (0xF0); +-#define CHCFIR (0x01) +- +-/* 0x64 OFDM_CHC_SNR */ +-#define OFDM_CHC_SNR (0x64) +- +-/* 0x65 OFDM_BDI_CTL */ +-#define OFDM_BDI_CTL (0x65) +-#define LP_SELECT (0x02) +- +-/* 0x67 OFDM_TPS_RCVD_1 */ +-#define OFDM_TPS_RCVD_1 (0x67) +-#define TPSFRAME (0x03) +- +-/* 0x68 OFDM_TPS_RCVD_2 */ +-#define OFDM_TPS_RCVD_2 (0x68) +- +-/* 0x69 OFDM_TPS_RCVD_3 */ +-#define OFDM_TPS_RCVD_3 (0x69) +- +-/* 0x6A OFDM_TPS_RCVD_4 */ +-#define OFDM_TPS_RCVD_4 (0x6A) +- +-/* 0x6B OFDM_TPS_RESERVED_1 */ +-#define OFDM_TPS_RESERVED_1 (0x6B) +- +-/* 0x6C OFDM_TPS_RESERVED_2 */ +-#define OFDM_TPS_RESERVED_2 (0x6C) +- +-/* 0x73 OFDM_MSC_REV */ +-#define OFDM_MSC_REV (0x73) +- +-/* 0x76 OFDM_SNR_CARRIER_2 */ +-#define OFDM_SNR_CARRIER_2 (0x76) +-#define MEAN_MASK (0x80) +-#define MEANBIT (0x80) +- +-/* 0x80 ANALOG_CONTROL_0 */ +-#define ANALOG_CONTROL_0 (0x80) +-#define POWER_DOWN_ADC (0x40) +- +-/* 0x81 ENABLE_TUNER_IIC */ +-#define ENABLE_TUNER_IIC (0x81) +-#define ENABLE_TUNER_BIT (0x01) +- +-/* 0x82 EN_DMD_RACQ */ +-#define EN_DMD_RACQ (0x82) +-#define EN_DMD_RACQ_REG_VAL (0x81) +-#define EN_DMD_RACQ_REG_VAL_14 (0x01) +- +-/* 0x84 SNR_COMMAND */ +-#define SNR_COMMAND (0x84) +-#define SNRStat (0x80) +- +-/* 0x85 SNRCARRIERNUMBER_LSB */ +-#define SNRCARRIERNUMBER_LSB (0x85) +- +-/* 0x87 SNRMINTHRESHOLD_LSB */ +-#define SNRMINTHRESHOLD_LSB (0x87) +- +-/* 0x89 SNR_PER_CARRIER_LSB */ +-#define SNR_PER_CARRIER_LSB (0x89) +- +-/* 0x8B SNRBELOWTHRESHOLD_LSB */ +-#define SNRBELOWTHRESHOLD_LSB (0x8B) +- +-/* 0x91 RF_AGC_VAL_1 */ +-#define RF_AGC_VAL_1 (0x91) +- +-/* 0x92 RF_AGC_STATUS */ +-#define RF_AGC_STATUS (0x92) +- +-/* 0x98 DIAG_CONFIG */ +-#define DIAG_CONFIG (0x98) +-#define DIAG_MASK (0x70) +-#define TB_SET (0x10) +-#define TRAN_SELECT (0x07) +-#define SERIAL_SELECT (0x01) +- +-/* 0x99 SUB_DIAG_MODE_SEL */ +-#define SUB_DIAG_MODE_SEL (0x99) +-#define CLKINVERSION (0x01) +- +-/* 0x9A TS_FORMAT */ +-#define TS_FORMAT (0x9A) +-#define ERROR_SENSE (0x08) +-#define VALID_SENSE (0x04) +-#define SYNC_SENSE (0x02) +-#define GATED_CLOCK (0x01) +- +-#define NXT6000ASICDEVICE (0x0b) +diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c +deleted file mode 100644 +index 5ef9218..0000000 +--- a/drivers/media/dvb/frontends/or51132.c ++++ /dev/null +@@ -1,631 +0,0 @@ +-/* +- * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM +- * +- * +- * Copyright (C) 2007 Trent Piepho +- * +- * Copyright (C) 2005 Kirk Lapray +- * +- * Based on code from Jack Kelliher (kelliher@xmission.com) +- * Copyright (C) 2002 & pcHDTV, inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +-*/ +- +-/* +- * This driver needs two external firmware files. Please copy +- * "dvb-fe-or51132-vsb.fw" and "dvb-fe-or51132-qam.fw" to +- * /usr/lib/hotplug/firmware/ or /lib/firmware/ +- * (depending on configuration of firmware hotplug). +- */ +-#define OR51132_VSB_FIRMWARE "dvb-fe-or51132-vsb.fw" +-#define OR51132_QAM_FIRMWARE "dvb-fe-or51132-qam.fw" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_math.h" +-#include "dvb_frontend.h" +-#include "or51132.h" +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "or51132: " args); \ +- } while (0) +- +- +-struct or51132_state +-{ +- struct i2c_adapter* i2c; +- +- /* Configuration settings */ +- const struct or51132_config* config; +- +- struct dvb_frontend frontend; +- +- /* Demodulator private data */ +- fe_modulation_t current_modulation; +- u32 snr; /* Result of last SNR calculation */ +- +- /* Tuner private data */ +- u32 current_frequency; +-}; +- +- +-/* Write buffer to demod */ +-static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) +-{ +- int err; +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = 0, .buf = (u8*)buf, .len = len }; +- +- /* msleep(20); */ /* doesn't appear to be necessary */ +- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { +- printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", +- msg.addr, msg.len, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); +- Less code and more efficient that loading a buffer on the stack with +- the bytes to send and then calling or51132_writebuf() on that. */ +-#define or51132_writebytes(state, data...) \ +- ({ static const u8 _data[] = {data}; \ +- or51132_writebuf(state, _data, sizeof(_data)); }) +- +-/* Read data from demod into buffer. Returns 0 on success. */ +-static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) +-{ +- int err; +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = I2C_M_RD, .buf = buf, .len = len }; +- +- /* msleep(20); */ /* doesn't appear to be necessary */ +- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { +- printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", +- msg.addr, msg.len, err); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-/* Reads a 16-bit demod register. Returns <0 on error. */ +-static int or51132_readreg(struct or51132_state *state, u8 reg) +-{ +- u8 buf[2] = { 0x04, reg }; +- struct i2c_msg msg[2] = { +- {.addr = state->config->demod_address, .flags = 0, +- .buf = buf, .len = 2 }, +- {.addr = state->config->demod_address, .flags = I2C_M_RD, +- .buf = buf, .len = 2 }}; +- int err; +- +- if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { +- printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", +- reg, err); +- return -EREMOTEIO; +- } +- return buf[0] | (buf[1] << 8); +-} +- +-static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) +-{ +- struct or51132_state* state = fe->demodulator_priv; +- static const u8 run_buf[] = {0x7F,0x01}; +- u8 rec_buf[8]; +- u32 firmwareAsize, firmwareBsize; +- int i,ret; +- +- dprintk("Firmware is %Zd bytes\n",fw->size); +- +- /* Get size of firmware A and B */ +- firmwareAsize = le32_to_cpu(*((__le32*)fw->data)); +- dprintk("FirmwareA is %i bytes\n",firmwareAsize); +- firmwareBsize = le32_to_cpu(*((__le32*)(fw->data+4))); +- dprintk("FirmwareB is %i bytes\n",firmwareBsize); +- +- /* Upload firmware */ +- if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { +- printk(KERN_WARNING "or51132: load_firmware error 1\n"); +- return ret; +- } +- if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], +- firmwareBsize))) { +- printk(KERN_WARNING "or51132: load_firmware error 2\n"); +- return ret; +- } +- +- if ((ret = or51132_writebuf(state, run_buf, 2))) { +- printk(KERN_WARNING "or51132: load_firmware error 3\n"); +- return ret; +- } +- if ((ret = or51132_writebuf(state, run_buf, 2))) { +- printk(KERN_WARNING "or51132: load_firmware error 4\n"); +- return ret; +- } +- +- /* 50ms for operation to begin */ +- msleep(50); +- +- /* Read back ucode version to besure we loaded correctly and are really up and running */ +- /* Get uCode version */ +- if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { +- printk(KERN_WARNING "or51132: load_firmware error a\n"); +- return ret; +- } +- if ((ret = or51132_writebytes(state, 0x04, 0x17))) { +- printk(KERN_WARNING "or51132: load_firmware error b\n"); +- return ret; +- } +- if ((ret = or51132_writebytes(state, 0x00, 0x00))) { +- printk(KERN_WARNING "or51132: load_firmware error c\n"); +- return ret; +- } +- for (i=0;i<4;i++) { +- /* Once upon a time, this command might have had something +- to do with getting the firmware version, but it's +- not used anymore: +- {0x04,0x00,0x30,0x00,i+1} */ +- /* Read 8 bytes, two bytes at a time */ +- if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { +- printk(KERN_WARNING +- "or51132: load_firmware error d - %d\n",i); +- return ret; +- } +- } +- +- printk(KERN_WARNING +- "or51132: Version: %02X%02X%02X%02X-%02X%02X%02X%02X (%02X%01X-%01X-%02X%01X-%01X)\n", +- rec_buf[1],rec_buf[0],rec_buf[3],rec_buf[2], +- rec_buf[5],rec_buf[4],rec_buf[7],rec_buf[6], +- rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, +- rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); +- +- if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { +- printk(KERN_WARNING "or51132: load_firmware error e\n"); +- return ret; +- } +- return 0; +-}; +- +-static int or51132_init(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int or51132_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- *ber = 0; +- return 0; +-} +- +-static int or51132_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- *ucblocks = 0; +- return 0; +-} +- +-static int or51132_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int or51132_setmode(struct dvb_frontend* fe) +-{ +- struct or51132_state* state = fe->demodulator_priv; +- u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; +- u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; +- +- dprintk("setmode %d\n",(int)state->current_modulation); +- +- switch (state->current_modulation) { +- case VSB_8: +- /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ +- cmd_buf1[2] = 0x50; +- /* REC MODE inv IF spectrum, Normal */ +- cmd_buf2[1] = 0x03; +- /* Channel MODE ATSC/VSB8 */ +- cmd_buf2[2] = 0x06; +- break; +- /* All QAM modes are: +- Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high +- REC MODE Normal Carrier Lock */ +- case QAM_AUTO: +- /* Channel MODE Auto QAM64/256 */ +- cmd_buf2[2] = 0x4f; +- break; +- case QAM_256: +- /* Channel MODE QAM256 */ +- cmd_buf2[2] = 0x45; +- break; +- case QAM_64: +- /* Channel MODE QAM64 */ +- cmd_buf2[2] = 0x43; +- break; +- default: +- printk(KERN_WARNING +- "or51132: setmode: Modulation set to unsupported value (%d)\n", +- state->current_modulation); +- return -EINVAL; +- } +- +- /* Set Receiver 1 register */ +- if (or51132_writebuf(state, cmd_buf1, 3)) { +- printk(KERN_WARNING "or51132: set_mode error 1\n"); +- return -EREMOTEIO; +- } +- dprintk("set #1 to %02x\n", cmd_buf1[2]); +- +- /* Set operation mode in Receiver 6 register */ +- if (or51132_writebuf(state, cmd_buf2, 3)) { +- printk(KERN_WARNING "or51132: set_mode error 2\n"); +- return -EREMOTEIO; +- } +- dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); +- +- return 0; +-} +- +-/* Some modulations use the same firmware. This classifies modulations +- by the firmware they use. */ +-#define MOD_FWCLASS_UNKNOWN 0 +-#define MOD_FWCLASS_VSB 1 +-#define MOD_FWCLASS_QAM 2 +-static int modulation_fw_class(fe_modulation_t modulation) +-{ +- switch(modulation) { +- case VSB_8: +- return MOD_FWCLASS_VSB; +- case QAM_AUTO: +- case QAM_64: +- case QAM_256: +- return MOD_FWCLASS_QAM; +- default: +- return MOD_FWCLASS_UNKNOWN; +- } +-} +- +-static int or51132_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- int ret; +- struct or51132_state* state = fe->demodulator_priv; +- const struct firmware *fw; +- const char *fwname; +- int clock_mode; +- +- /* Upload new firmware only if we need a different one */ +- if (modulation_fw_class(state->current_modulation) != +- modulation_fw_class(p->modulation)) { +- switch (modulation_fw_class(p->modulation)) { +- case MOD_FWCLASS_VSB: +- dprintk("set_parameters VSB MODE\n"); +- fwname = OR51132_VSB_FIRMWARE; +- +- /* Set non-punctured clock for VSB */ +- clock_mode = 0; +- break; +- case MOD_FWCLASS_QAM: +- dprintk("set_parameters QAM MODE\n"); +- fwname = OR51132_QAM_FIRMWARE; +- +- /* Set punctured clock for QAM */ +- clock_mode = 1; +- break; +- default: +- printk("or51132: Modulation type(%d) UNSUPPORTED\n", +- p->modulation); +- return -1; +- } +- printk("or51132: Waiting for firmware upload(%s)...\n", +- fwname); +- ret = request_firmware(&fw, fwname, state->i2c->dev.parent); +- if (ret) { +- printk(KERN_WARNING "or51132: No firmware up" +- "loaded(timeout or file not found?)\n"); +- return ret; +- } +- ret = or51132_load_firmware(fe, fw); +- release_firmware(fw); +- if (ret) { +- printk(KERN_WARNING "or51132: Writing firmware to " +- "device failed!\n"); +- return ret; +- } +- printk("or51132: Firmware upload complete.\n"); +- state->config->set_ts_params(fe, clock_mode); +- } +- /* Change only if we are actually changing the modulation */ +- if (state->current_modulation != p->modulation) { +- state->current_modulation = p->modulation; +- or51132_setmode(fe); +- } +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Set to current mode */ +- or51132_setmode(fe); +- +- /* Update current frequency */ +- state->current_frequency = p->frequency; +- return 0; +-} +- +-static int or51132_get_parameters(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct or51132_state* state = fe->demodulator_priv; +- int status; +- int retry = 1; +- +-start: +- /* Receiver Status */ +- if ((status = or51132_readreg(state, 0x00)) < 0) { +- printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); +- return -EREMOTEIO; +- } +- switch(status&0xff) { +- case 0x06: +- p->modulation = VSB_8; +- break; +- case 0x43: +- p->modulation = QAM_64; +- break; +- case 0x45: +- p->modulation = QAM_256; +- break; +- default: +- if (retry--) +- goto start; +- printk(KERN_WARNING "or51132: unknown status 0x%02x\n", +- status&0xff); +- return -EREMOTEIO; +- } +- +- /* FIXME: Read frequency from frontend, take AFC into account */ +- p->frequency = state->current_frequency; +- +- /* FIXME: How to read inversion setting? Receiver 6 register? */ +- p->inversion = INVERSION_AUTO; +- +- return 0; +-} +- +-static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct or51132_state* state = fe->demodulator_priv; +- int reg; +- +- /* Receiver Status */ +- if ((reg = or51132_readreg(state, 0x00)) < 0) { +- printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); +- *status = 0; +- return -EREMOTEIO; +- } +- dprintk("%s: read_status %04x\n", __func__, reg); +- +- if (reg & 0x0100) /* Receiver Lock */ +- *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| +- FE_HAS_SYNC|FE_HAS_LOCK; +- else +- *status = 0; +- return 0; +-} +- +-/* Calculate SNR estimation (scaled by 2^24) +- +- 8-VSB SNR and QAM equations from Oren datasheets +- +- For 8-VSB: +- SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K +- +- Where K = 0 if NTSC rejection filter is OFF; and +- K = 3 if NTSC rejection filter is ON +- +- For QAM64: +- SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) +- +- For QAM256: +- SNR[dB] = 10 * log10(907832426.314266 / MSE^2 ) +- +- We re-write the snr equation as: +- SNR * 2^24 = 10*(c - 2*intlog10(MSE)) +- Where for QAM256, c = log10(907832426.314266) * 2^24 +- and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */ +- +-static u32 calculate_snr(u32 mse, u32 c) +-{ +- if (mse == 0) /* No signal */ +- return 0; +- +- mse = 2*intlog10(mse); +- if (mse > c) { +- /* Negative SNR, which is possible, but realisticly the +- demod will lose lock before the signal gets this bad. The +- API only allows for unsigned values, so just return 0 */ +- return 0; +- } +- return 10*(c - mse); +-} +- +-static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct or51132_state* state = fe->demodulator_priv; +- int noise, reg; +- u32 c, usK = 0; +- int retry = 1; +- +-start: +- /* SNR after Equalizer */ +- noise = or51132_readreg(state, 0x02); +- if (noise < 0) { +- printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); +- return -EREMOTEIO; +- } +- dprintk("read_snr noise (%d)\n", noise); +- +- /* Read status, contains modulation type for QAM_AUTO and +- NTSC filter for VSB */ +- reg = or51132_readreg(state, 0x00); +- if (reg < 0) { +- printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); +- return -EREMOTEIO; +- } +- +- switch (reg&0xff) { +- case 0x06: +- if (reg & 0x1000) usK = 3 << 24; +- /* Fall through to QAM64 case */ +- case 0x43: +- c = 150204167; +- break; +- case 0x45: +- c = 150290396; +- break; +- default: +- printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); +- if (retry--) goto start; +- return -EREMOTEIO; +- } +- dprintk("%s: modulation %02x, NTSC rej O%s\n", __func__, +- reg&0xff, reg&0x1000?"n":"ff"); +- +- /* Calculate SNR using noise, c, and NTSC rejection correction */ +- state->snr = calculate_snr(noise, c) - usK; +- *snr = (state->snr) >> 16; +- +- dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __func__, noise, +- state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); +- +- return 0; +-} +- +-static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- /* Calculate Strength from SNR up to 35dB */ +- /* Even though the SNR can go higher than 35dB, there is some comfort */ +- /* factor in having a range of strong signals that can show at 100% */ +- struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv; +- u16 snr; +- int ret; +- +- ret = fe->ops.read_snr(fe, &snr); +- if (ret != 0) +- return ret; +- /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ +- /* scale the range 0 - 35*2^24 into 0 - 65535 */ +- if (state->snr >= 8960 * 0x10000) +- *strength = 0xffff; +- else +- *strength = state->snr / 8960; +- +- return 0; +-} +- +-static int or51132_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fe_tune_settings) +-{ +- fe_tune_settings->min_delay_ms = 500; +- fe_tune_settings->step_size = 0; +- fe_tune_settings->max_drift = 0; +- +- return 0; +-} +- +-static void or51132_release(struct dvb_frontend* fe) +-{ +- struct or51132_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops or51132_ops; +- +-struct dvb_frontend* or51132_attach(const struct or51132_config* config, +- struct i2c_adapter* i2c) +-{ +- struct or51132_state* state = NULL; +- +- /* Allocate memory for the internal state */ +- state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- /* Setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->current_frequency = -1; +- state->current_modulation = -1; +- +- /* Create dvb_frontend */ +- memcpy(&state->frontend.ops, &or51132_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-static struct dvb_frontend_ops or51132_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Oren OR51132 VSB/QAM Frontend", +- .frequency_min = 44000000, +- .frequency_max = 958000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | +- FE_CAN_8VSB +- }, +- +- .release = or51132_release, +- +- .init = or51132_init, +- .sleep = or51132_sleep, +- +- .set_frontend = or51132_set_parameters, +- .get_frontend = or51132_get_parameters, +- .get_tune_settings = or51132_get_tune_settings, +- +- .read_status = or51132_read_status, +- .read_ber = or51132_read_ber, +- .read_signal_strength = or51132_read_signal_strength, +- .read_snr = or51132_read_snr, +- .read_ucblocks = or51132_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); +-MODULE_AUTHOR("Kirk Lapray"); +-MODULE_AUTHOR("Trent Piepho"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(or51132_attach); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h +deleted file mode 100644 +index 1b8e04d..0000000 +--- a/drivers/media/dvb/frontends/or51132.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM +- * +- * Copyright (C) 2005 Kirk Lapray +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +-*/ +- +-#ifndef OR51132_H +-#define OR51132_H +- +-#include +-#include +- +-struct or51132_config +-{ +- /* The demodulator's i2c address */ +- u8 demod_address; +- +- /* Need to set device param for start_dma */ +- int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); +-}; +- +-#if defined(CONFIG_DVB_OR51132) || (defined(CONFIG_DVB_OR51132_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* or51132_attach(const struct or51132_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_OR51132 +- +-#endif // OR51132_H +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- * End: +- */ +diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c +deleted file mode 100644 +index c625b57..0000000 +--- a/drivers/media/dvb/frontends/or51211.c ++++ /dev/null +@@ -1,581 +0,0 @@ +-/* +- * Support for OR51211 (pcHDTV HD-2000) - VSB +- * +- * Copyright (C) 2005 Kirk Lapray +- * +- * Based on code from Jack Kelliher (kelliher@xmission.com) +- * Copyright (C) 2002 & pcHDTV, inc. +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +-*/ +- +-/* +- * This driver needs external firmware. Please use the command +- * "/Documentation/dvb/get_dvb_firmware or51211" to +- * download/extract it, and then copy it to /usr/lib/hotplug/firmware +- * or /lib/firmware (depending on configuration of firmware hotplug). +- */ +-#define OR51211_DEFAULT_FIRMWARE "dvb-fe-or51211.fw" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_math.h" +-#include "dvb_frontend.h" +-#include "or51211.h" +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "or51211: " args); \ +- } while (0) +- +-static u8 run_buf[] = {0x7f,0x01}; +-static u8 cmd_buf[] = {0x04,0x01,0x50,0x80,0x06}; // ATSC +- +-struct or51211_state { +- +- struct i2c_adapter* i2c; +- +- /* Configuration settings */ +- const struct or51211_config* config; +- +- struct dvb_frontend frontend; +- struct bt878* bt; +- +- /* Demodulator private data */ +- u8 initialized:1; +- u32 snr; /* Result of last SNR claculation */ +- +- /* Tuner private data */ +- u32 current_frequency; +-}; +- +-static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf, +- int len) +-{ +- int err; +- struct i2c_msg msg; +- msg.addr = reg; +- msg.flags = 0; +- msg.len = len; +- msg.buf = (u8 *)buf; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk(KERN_WARNING "or51211: i2c_writebytes error " +- "(addr %02x, err == %i)\n", reg, err); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int i2c_readbytes(struct or51211_state *state, u8 reg, u8 *buf, int len) +-{ +- int err; +- struct i2c_msg msg; +- msg.addr = reg; +- msg.flags = I2C_M_RD; +- msg.len = len; +- msg.buf = buf; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk(KERN_WARNING "or51211: i2c_readbytes error " +- "(addr %02x, err == %i)\n", reg, err); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int or51211_load_firmware (struct dvb_frontend* fe, +- const struct firmware *fw) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- u8 tudata[585]; +- int i; +- +- dprintk("Firmware is %zd bytes\n",fw->size); +- +- /* Get eprom data */ +- tudata[0] = 17; +- if (i2c_writebytes(state,0x50,tudata,1)) { +- printk(KERN_WARNING "or51211:load_firmware error eprom addr\n"); +- return -1; +- } +- if (i2c_readbytes(state,0x50,&tudata[145],192)) { +- printk(KERN_WARNING "or51211: load_firmware error eprom\n"); +- return -1; +- } +- +- /* Create firmware buffer */ +- for (i = 0; i < 145; i++) +- tudata[i] = fw->data[i]; +- +- for (i = 0; i < 248; i++) +- tudata[i+337] = fw->data[145+i]; +- +- state->config->reset(fe); +- +- if (i2c_writebytes(state,state->config->demod_address,tudata,585)) { +- printk(KERN_WARNING "or51211: load_firmware error 1\n"); +- return -1; +- } +- msleep(1); +- +- if (i2c_writebytes(state,state->config->demod_address, +- &fw->data[393],8125)) { +- printk(KERN_WARNING "or51211: load_firmware error 2\n"); +- return -1; +- } +- msleep(1); +- +- if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { +- printk(KERN_WARNING "or51211: load_firmware error 3\n"); +- return -1; +- } +- +- /* Wait at least 5 msec */ +- msleep(10); +- if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { +- printk(KERN_WARNING "or51211: load_firmware error 4\n"); +- return -1; +- } +- msleep(10); +- +- printk("or51211: Done.\n"); +- return 0; +-}; +- +-static int or51211_setmode(struct dvb_frontend* fe, int mode) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- u8 rec_buf[14]; +- +- state->config->setmode(fe, mode); +- +- if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { +- printk(KERN_WARNING "or51211: setmode error 1\n"); +- return -1; +- } +- +- /* Wait at least 5 msec */ +- msleep(10); +- if (i2c_writebytes(state,state->config->demod_address,run_buf,2)) { +- printk(KERN_WARNING "or51211: setmode error 2\n"); +- return -1; +- } +- +- msleep(10); +- +- /* Set operation mode in Receiver 1 register; +- * type 1: +- * data 0x50h Automatic sets receiver channel conditions +- * Automatic NTSC rejection filter +- * Enable MPEG serial data output +- * MPEG2tr +- * High tuner phase noise +- * normal +/-150kHz Carrier acquisition range +- */ +- if (i2c_writebytes(state,state->config->demod_address,cmd_buf,3)) { +- printk(KERN_WARNING "or51211: setmode error 3\n"); +- return -1; +- } +- +- rec_buf[0] = 0x04; +- rec_buf[1] = 0x00; +- rec_buf[2] = 0x03; +- rec_buf[3] = 0x00; +- msleep(20); +- if (i2c_writebytes(state,state->config->demod_address,rec_buf,3)) { +- printk(KERN_WARNING "or51211: setmode error 5\n"); +- } +- msleep(3); +- if (i2c_readbytes(state,state->config->demod_address,&rec_buf[10],2)) { +- printk(KERN_WARNING "or51211: setmode error 6"); +- return -1; +- } +- dprintk("setmode rec status %02x %02x\n",rec_buf[10],rec_buf[11]); +- +- return 0; +-} +- +-static int or51211_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct or51211_state* state = fe->demodulator_priv; +- +- /* Change only if we are actually changing the channel */ +- if (state->current_frequency != p->frequency) { +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Set to ATSC mode */ +- or51211_setmode(fe,0); +- +- /* Update current frequency */ +- state->current_frequency = p->frequency; +- } +- return 0; +-} +- +-static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- unsigned char rec_buf[2]; +- unsigned char snd_buf[] = {0x04,0x00,0x03,0x00}; +- *status = 0; +- +- /* Receiver Status */ +- if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { +- printk(KERN_WARNING "or51132: read_status write error\n"); +- return -1; +- } +- msleep(3); +- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { +- printk(KERN_WARNING "or51132: read_status read error\n"); +- return -1; +- } +- dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); +- +- if (rec_buf[0] & 0x01) { /* Receiver Lock */ +- *status |= FE_HAS_SIGNAL; +- *status |= FE_HAS_CARRIER; +- *status |= FE_HAS_VITERBI; +- *status |= FE_HAS_SYNC; +- *status |= FE_HAS_LOCK; +- } +- return 0; +-} +- +-/* Calculate SNR estimation (scaled by 2^24) +- +- 8-VSB SNR equation from Oren datasheets +- +- For 8-VSB: +- SNR[dB] = 10 * log10(219037.9454 / MSE^2 ) +- +- We re-write the snr equation as: +- SNR * 2^24 = 10*(c - 2*intlog10(MSE)) +- Where for 8-VSB, c = log10(219037.9454) * 2^24 */ +- +-static u32 calculate_snr(u32 mse, u32 c) +-{ +- if (mse == 0) /* No signal */ +- return 0; +- +- mse = 2*intlog10(mse); +- if (mse > c) { +- /* Negative SNR, which is possible, but realisticly the +- demod will lose lock before the signal gets this bad. The +- API only allows for unsigned values, so just return 0 */ +- return 0; +- } +- return 10*(c - mse); +-} +- +-static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- u8 rec_buf[2]; +- u8 snd_buf[3]; +- +- /* SNR after Equalizer */ +- snd_buf[0] = 0x04; +- snd_buf[1] = 0x00; +- snd_buf[2] = 0x04; +- +- if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { +- printk(KERN_WARNING "%s: error writing snr reg\n", +- __func__); +- return -1; +- } +- if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { +- printk(KERN_WARNING "%s: read_status read error\n", +- __func__); +- return -1; +- } +- +- state->snr = calculate_snr(rec_buf[0], 89599047); +- *snr = (state->snr) >> 16; +- +- dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __func__, rec_buf[0], +- state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); +- +- return 0; +-} +- +-static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- /* Calculate Strength from SNR up to 35dB */ +- /* Even though the SNR can go higher than 35dB, there is some comfort */ +- /* factor in having a range of strong signals that can show at 100% */ +- struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv; +- u16 snr; +- int ret; +- +- ret = fe->ops.read_snr(fe, &snr); +- if (ret != 0) +- return ret; +- /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ +- /* scale the range 0 - 35*2^24 into 0 - 65535 */ +- if (state->snr >= 8960 * 0x10000) +- *strength = 0xffff; +- else +- *strength = state->snr / 8960; +- +- return 0; +-} +- +-static int or51211_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- *ber = -ENOSYS; +- return 0; +-} +- +-static int or51211_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- *ucblocks = -ENOSYS; +- return 0; +-} +- +-static int or51211_sleep(struct dvb_frontend* fe) +-{ +- return 0; +-} +- +-static int or51211_init(struct dvb_frontend* fe) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- const struct or51211_config* config = state->config; +- const struct firmware* fw; +- unsigned char get_ver_buf[] = {0x04,0x00,0x30,0x00,0x00}; +- unsigned char rec_buf[14]; +- int ret,i; +- +- if (!state->initialized) { +- /* Request the firmware, this will block until it uploads */ +- printk(KERN_INFO "or51211: Waiting for firmware upload " +- "(%s)...\n", OR51211_DEFAULT_FIRMWARE); +- ret = config->request_firmware(fe, &fw, +- OR51211_DEFAULT_FIRMWARE); +- printk(KERN_INFO "or51211:Got Hotplug firmware\n"); +- if (ret) { +- printk(KERN_WARNING "or51211: No firmware uploaded " +- "(timeout or file not found?)\n"); +- return ret; +- } +- +- ret = or51211_load_firmware(fe, fw); +- release_firmware(fw); +- if (ret) { +- printk(KERN_WARNING "or51211: Writing firmware to " +- "device failed!\n"); +- return ret; +- } +- printk(KERN_INFO "or51211: Firmware upload complete.\n"); +- +- /* Set operation mode in Receiver 1 register; +- * type 1: +- * data 0x50h Automatic sets receiver channel conditions +- * Automatic NTSC rejection filter +- * Enable MPEG serial data output +- * MPEG2tr +- * High tuner phase noise +- * normal +/-150kHz Carrier acquisition range +- */ +- if (i2c_writebytes(state,state->config->demod_address, +- cmd_buf,3)) { +- printk(KERN_WARNING "or51211: Load DVR Error 5\n"); +- return -1; +- } +- +- /* Read back ucode version to besure we loaded correctly */ +- /* and are really up and running */ +- rec_buf[0] = 0x04; +- rec_buf[1] = 0x00; +- rec_buf[2] = 0x03; +- rec_buf[3] = 0x00; +- msleep(30); +- if (i2c_writebytes(state,state->config->demod_address, +- rec_buf,3)) { +- printk(KERN_WARNING "or51211: Load DVR Error A\n"); +- return -1; +- } +- msleep(3); +- if (i2c_readbytes(state,state->config->demod_address, +- &rec_buf[10],2)) { +- printk(KERN_WARNING "or51211: Load DVR Error B\n"); +- return -1; +- } +- +- rec_buf[0] = 0x04; +- rec_buf[1] = 0x00; +- rec_buf[2] = 0x01; +- rec_buf[3] = 0x00; +- msleep(20); +- if (i2c_writebytes(state,state->config->demod_address, +- rec_buf,3)) { +- printk(KERN_WARNING "or51211: Load DVR Error C\n"); +- return -1; +- } +- msleep(3); +- if (i2c_readbytes(state,state->config->demod_address, +- &rec_buf[12],2)) { +- printk(KERN_WARNING "or51211: Load DVR Error D\n"); +- return -1; +- } +- +- for (i = 0; i < 8; i++) +- rec_buf[i]=0xed; +- +- for (i = 0; i < 5; i++) { +- msleep(30); +- get_ver_buf[4] = i+1; +- if (i2c_writebytes(state,state->config->demod_address, +- get_ver_buf,5)) { +- printk(KERN_WARNING "or51211:Load DVR Error 6" +- " - %d\n",i); +- return -1; +- } +- msleep(3); +- +- if (i2c_readbytes(state,state->config->demod_address, +- &rec_buf[i*2],2)) { +- printk(KERN_WARNING "or51211:Load DVR Error 7" +- " - %d\n",i); +- return -1; +- } +- /* If we didn't receive the right index, try again */ +- if ((int)rec_buf[i*2+1]!=i+1){ +- i--; +- } +- } +- dprintk("read_fwbits %x %x %x %x %x %x %x %x %x %x\n", +- rec_buf[0], rec_buf[1], rec_buf[2], rec_buf[3], +- rec_buf[4], rec_buf[5], rec_buf[6], rec_buf[7], +- rec_buf[8], rec_buf[9]); +- +- printk(KERN_INFO "or51211: ver TU%02x%02x%02x VSB mode %02x" +- " Status %02x\n", +- rec_buf[2], rec_buf[4],rec_buf[6], +- rec_buf[12],rec_buf[10]); +- +- rec_buf[0] = 0x04; +- rec_buf[1] = 0x00; +- rec_buf[2] = 0x03; +- rec_buf[3] = 0x00; +- msleep(20); +- if (i2c_writebytes(state,state->config->demod_address, +- rec_buf,3)) { +- printk(KERN_WARNING "or51211: Load DVR Error 8\n"); +- return -1; +- } +- msleep(20); +- if (i2c_readbytes(state,state->config->demod_address, +- &rec_buf[8],2)) { +- printk(KERN_WARNING "or51211: Load DVR Error 9\n"); +- return -1; +- } +- state->initialized = 1; +- } +- +- return 0; +-} +- +-static int or51211_get_tune_settings(struct dvb_frontend* fe, +- struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 500; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void or51211_release(struct dvb_frontend* fe) +-{ +- struct or51211_state* state = fe->demodulator_priv; +- state->config->sleep(fe); +- kfree(state); +-} +- +-static struct dvb_frontend_ops or51211_ops; +- +-struct dvb_frontend* or51211_attach(const struct or51211_config* config, +- struct i2c_adapter* i2c) +-{ +- struct or51211_state* state = NULL; +- +- /* Allocate memory for the internal state */ +- state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- /* Setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialized = 0; +- state->current_frequency = 0; +- +- /* Create dvb_frontend */ +- memcpy(&state->frontend.ops, &or51211_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-static struct dvb_frontend_ops or51211_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Oren OR51211 VSB Frontend", +- .frequency_min = 44000000, +- .frequency_max = 958000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_8VSB +- }, +- +- .release = or51211_release, +- +- .init = or51211_init, +- .sleep = or51211_sleep, +- +- .set_frontend = or51211_set_parameters, +- .get_tune_settings = or51211_get_tune_settings, +- +- .read_status = or51211_read_status, +- .read_ber = or51211_read_ber, +- .read_signal_strength = or51211_read_signal_strength, +- .read_snr = or51211_read_snr, +- .read_ucblocks = or51211_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver"); +-MODULE_AUTHOR("Kirk Lapray"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(or51211_attach); +- +diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h +deleted file mode 100644 +index 3ce0508..0000000 +--- a/drivers/media/dvb/frontends/or51211.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * Support for OR51211 (pcHDTV HD-2000) - VSB +- * +- * Copyright (C) 2005 Kirk Lapray +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +-*/ +- +-#ifndef OR51211_H +-#define OR51211_H +- +-#include +-#include +- +-struct or51211_config +-{ +- /* The demodulator's i2c address */ +- u8 demod_address; +- +- /* Request firmware for device */ +- int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +- void (*setmode)(struct dvb_frontend * fe, int mode); +- void (*reset)(struct dvb_frontend * fe); +- void (*sleep)(struct dvb_frontend * fe); +-}; +- +-#if defined(CONFIG_DVB_OR51211) || (defined(CONFIG_DVB_OR51211_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* or51211_attach(const struct or51211_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_OR51211 +- +-#endif // OR51211_H +- +diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c +deleted file mode 100644 +index f71b062..0000000 +--- a/drivers/media/dvb/frontends/s5h1409.c ++++ /dev/null +@@ -1,1029 +0,0 @@ +-/* +- Samsung S5H1409 VSB/QAM demodulator driver +- +- Copyright (C) 2006 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "s5h1409.h" +- +-struct s5h1409_state { +- +- struct i2c_adapter *i2c; +- +- /* configuration settings */ +- const struct s5h1409_config *config; +- +- struct dvb_frontend frontend; +- +- /* previous uncorrected block counter */ +- fe_modulation_t current_modulation; +- +- u32 current_frequency; +- int if_freq; +- +- u32 is_qam_locked; +- +- /* QAM tuning state goes through the following state transitions */ +-#define QAM_STATE_UNTUNED 0 +-#define QAM_STATE_TUNING_STARTED 1 +-#define QAM_STATE_INTERLEAVE_SET 2 +-#define QAM_STATE_QAM_OPTIMIZED_L1 3 +-#define QAM_STATE_QAM_OPTIMIZED_L2 4 +-#define QAM_STATE_QAM_OPTIMIZED_L3 5 +- u8 qam_state; +-}; +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-#define dprintk if (debug) printk +- +-/* Register values to initialise the demod, this will set VSB by default */ +-static struct init_tab { +- u8 reg; +- u16 data; +-} init_tab[] = { +- { 0x00, 0x0071, }, +- { 0x01, 0x3213, }, +- { 0x09, 0x0025, }, +- { 0x1c, 0x001d, }, +- { 0x1f, 0x002d, }, +- { 0x20, 0x001d, }, +- { 0x22, 0x0022, }, +- { 0x23, 0x0020, }, +- { 0x29, 0x110f, }, +- { 0x2a, 0x10b4, }, +- { 0x2b, 0x10ae, }, +- { 0x2c, 0x0031, }, +- { 0x31, 0x010d, }, +- { 0x32, 0x0100, }, +- { 0x44, 0x0510, }, +- { 0x54, 0x0104, }, +- { 0x58, 0x2222, }, +- { 0x59, 0x1162, }, +- { 0x5a, 0x3211, }, +- { 0x5d, 0x0370, }, +- { 0x5e, 0x0296, }, +- { 0x61, 0x0010, }, +- { 0x63, 0x4a00, }, +- { 0x65, 0x0800, }, +- { 0x71, 0x0003, }, +- { 0x72, 0x0470, }, +- { 0x81, 0x0002, }, +- { 0x82, 0x0600, }, +- { 0x86, 0x0002, }, +- { 0x8a, 0x2c38, }, +- { 0x8b, 0x2a37, }, +- { 0x92, 0x302f, }, +- { 0x93, 0x3332, }, +- { 0x96, 0x000c, }, +- { 0x99, 0x0101, }, +- { 0x9c, 0x2e37, }, +- { 0x9d, 0x2c37, }, +- { 0x9e, 0x2c37, }, +- { 0xab, 0x0100, }, +- { 0xac, 0x1003, }, +- { 0xad, 0x103f, }, +- { 0xe2, 0x0100, }, +- { 0xe3, 0x1000, }, +- { 0x28, 0x1010, }, +- { 0xb1, 0x000e, }, +-}; +- +-/* VSB SNR lookup table */ +-static struct vsb_snr_tab { +- u16 val; +- u16 data; +-} vsb_snr_tab[] = { +- { 924, 300, }, +- { 923, 300, }, +- { 918, 295, }, +- { 915, 290, }, +- { 911, 285, }, +- { 906, 280, }, +- { 901, 275, }, +- { 896, 270, }, +- { 891, 265, }, +- { 885, 260, }, +- { 879, 255, }, +- { 873, 250, }, +- { 864, 245, }, +- { 858, 240, }, +- { 850, 235, }, +- { 841, 230, }, +- { 832, 225, }, +- { 823, 220, }, +- { 812, 215, }, +- { 802, 210, }, +- { 788, 205, }, +- { 778, 200, }, +- { 767, 195, }, +- { 753, 190, }, +- { 740, 185, }, +- { 725, 180, }, +- { 707, 175, }, +- { 689, 170, }, +- { 671, 165, }, +- { 656, 160, }, +- { 637, 155, }, +- { 616, 150, }, +- { 542, 145, }, +- { 519, 140, }, +- { 507, 135, }, +- { 497, 130, }, +- { 492, 125, }, +- { 474, 120, }, +- { 300, 111, }, +- { 0, 0, }, +-}; +- +-/* QAM64 SNR lookup table */ +-static struct qam64_snr_tab { +- u16 val; +- u16 data; +-} qam64_snr_tab[] = { +- { 1, 0, }, +- { 12, 300, }, +- { 15, 290, }, +- { 18, 280, }, +- { 22, 270, }, +- { 23, 268, }, +- { 24, 266, }, +- { 25, 264, }, +- { 27, 262, }, +- { 28, 260, }, +- { 29, 258, }, +- { 30, 256, }, +- { 32, 254, }, +- { 33, 252, }, +- { 34, 250, }, +- { 35, 249, }, +- { 36, 248, }, +- { 37, 247, }, +- { 38, 246, }, +- { 39, 245, }, +- { 40, 244, }, +- { 41, 243, }, +- { 42, 241, }, +- { 43, 240, }, +- { 44, 239, }, +- { 45, 238, }, +- { 46, 237, }, +- { 47, 236, }, +- { 48, 235, }, +- { 49, 234, }, +- { 50, 233, }, +- { 51, 232, }, +- { 52, 231, }, +- { 53, 230, }, +- { 55, 229, }, +- { 56, 228, }, +- { 57, 227, }, +- { 58, 226, }, +- { 59, 225, }, +- { 60, 224, }, +- { 62, 223, }, +- { 63, 222, }, +- { 65, 221, }, +- { 66, 220, }, +- { 68, 219, }, +- { 69, 218, }, +- { 70, 217, }, +- { 72, 216, }, +- { 73, 215, }, +- { 75, 214, }, +- { 76, 213, }, +- { 78, 212, }, +- { 80, 211, }, +- { 81, 210, }, +- { 83, 209, }, +- { 84, 208, }, +- { 85, 207, }, +- { 87, 206, }, +- { 89, 205, }, +- { 91, 204, }, +- { 93, 203, }, +- { 95, 202, }, +- { 96, 201, }, +- { 104, 200, }, +- { 255, 0, }, +-}; +- +-/* QAM256 SNR lookup table */ +-static struct qam256_snr_tab { +- u16 val; +- u16 data; +-} qam256_snr_tab[] = { +- { 1, 0, }, +- { 12, 400, }, +- { 13, 390, }, +- { 15, 380, }, +- { 17, 360, }, +- { 19, 350, }, +- { 22, 348, }, +- { 23, 346, }, +- { 24, 344, }, +- { 25, 342, }, +- { 26, 340, }, +- { 27, 336, }, +- { 28, 334, }, +- { 29, 332, }, +- { 30, 330, }, +- { 31, 328, }, +- { 32, 326, }, +- { 33, 325, }, +- { 34, 322, }, +- { 35, 320, }, +- { 37, 318, }, +- { 39, 316, }, +- { 40, 314, }, +- { 41, 312, }, +- { 42, 310, }, +- { 43, 308, }, +- { 46, 306, }, +- { 47, 304, }, +- { 49, 302, }, +- { 51, 300, }, +- { 53, 298, }, +- { 54, 297, }, +- { 55, 296, }, +- { 56, 295, }, +- { 57, 294, }, +- { 59, 293, }, +- { 60, 292, }, +- { 61, 291, }, +- { 63, 290, }, +- { 64, 289, }, +- { 65, 288, }, +- { 66, 287, }, +- { 68, 286, }, +- { 69, 285, }, +- { 71, 284, }, +- { 72, 283, }, +- { 74, 282, }, +- { 75, 281, }, +- { 76, 280, }, +- { 77, 279, }, +- { 78, 278, }, +- { 81, 277, }, +- { 83, 276, }, +- { 84, 275, }, +- { 86, 274, }, +- { 87, 273, }, +- { 89, 272, }, +- { 90, 271, }, +- { 92, 270, }, +- { 93, 269, }, +- { 95, 268, }, +- { 96, 267, }, +- { 98, 266, }, +- { 100, 265, }, +- { 102, 264, }, +- { 104, 263, }, +- { 105, 262, }, +- { 106, 261, }, +- { 110, 260, }, +- { 255, 0, }, +-}; +- +-/* 8 bit registers, 16 bit values */ +-static int s5h1409_writereg(struct s5h1409_state *state, u8 reg, u16 data) +-{ +- int ret; +- u8 buf[] = { reg, data >> 8, data & 0xff }; +- +- struct i2c_msg msg = { .addr = state->config->demod_address, +- .flags = 0, .buf = buf, .len = 3 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk(KERN_ERR "%s: error (reg == 0x%02x, val == 0x%04x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static u16 s5h1409_readreg(struct s5h1409_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0, 0 }; +- +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, +- .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, +- .buf = b1, .len = 2 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk("%s: readreg error (ret == %i)\n", __func__, ret); +- return (b1[0] << 8) | b1[1]; +-} +- +-static int s5h1409_softreset(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- s5h1409_writereg(state, 0xf5, 0); +- s5h1409_writereg(state, 0xf5, 1); +- state->is_qam_locked = 0; +- state->qam_state = QAM_STATE_UNTUNED; +- return 0; +-} +- +-#define S5H1409_VSB_IF_FREQ 5380 +-#define S5H1409_QAM_IF_FREQ (state->config->qam_if) +- +-static int s5h1409_set_if_freq(struct dvb_frontend *fe, int KHz) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d KHz)\n", __func__, KHz); +- +- switch (KHz) { +- case 4000: +- s5h1409_writereg(state, 0x87, 0x014b); +- s5h1409_writereg(state, 0x88, 0x0cb5); +- s5h1409_writereg(state, 0x89, 0x03e2); +- break; +- case 5380: +- case 44000: +- default: +- s5h1409_writereg(state, 0x87, 0x01be); +- s5h1409_writereg(state, 0x88, 0x0436); +- s5h1409_writereg(state, 0x89, 0x054d); +- break; +- } +- state->if_freq = KHz; +- +- return 0; +-} +- +-static int s5h1409_set_spectralinversion(struct dvb_frontend *fe, int inverted) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, inverted); +- +- if (inverted == 1) +- return s5h1409_writereg(state, 0x1b, 0x1101); /* Inverted */ +- else +- return s5h1409_writereg(state, 0x1b, 0x0110); /* Normal */ +-} +- +-static int s5h1409_enable_modulation(struct dvb_frontend *fe, +- fe_modulation_t m) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(0x%08x)\n", __func__, m); +- +- switch (m) { +- case VSB_8: +- dprintk("%s() VSB_8\n", __func__); +- if (state->if_freq != S5H1409_VSB_IF_FREQ) +- s5h1409_set_if_freq(fe, S5H1409_VSB_IF_FREQ); +- s5h1409_writereg(state, 0xf4, 0); +- break; +- case QAM_64: +- case QAM_256: +- case QAM_AUTO: +- dprintk("%s() QAM_AUTO (64/256)\n", __func__); +- if (state->if_freq != S5H1409_QAM_IF_FREQ) +- s5h1409_set_if_freq(fe, S5H1409_QAM_IF_FREQ); +- s5h1409_writereg(state, 0xf4, 1); +- s5h1409_writereg(state, 0x85, 0x110); +- break; +- default: +- dprintk("%s() Invalid modulation\n", __func__); +- return -EINVAL; +- } +- +- state->current_modulation = m; +- s5h1409_softreset(fe); +- +- return 0; +-} +- +-static int s5h1409_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- if (enable) +- return s5h1409_writereg(state, 0xf3, 1); +- else +- return s5h1409_writereg(state, 0xf3, 0); +-} +- +-static int s5h1409_set_gpio(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- if (enable) +- return s5h1409_writereg(state, 0xe3, +- s5h1409_readreg(state, 0xe3) | 0x1100); +- else +- return s5h1409_writereg(state, 0xe3, +- s5h1409_readreg(state, 0xe3) & 0xfeff); +-} +- +-static int s5h1409_sleep(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- return s5h1409_writereg(state, 0xf2, enable); +-} +- +-static int s5h1409_register_reset(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- return s5h1409_writereg(state, 0xfa, 0); +-} +- +-static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg; +- +- if (state->qam_state < QAM_STATE_INTERLEAVE_SET) { +- /* We should not perform amhum optimization until +- the interleave mode has been configured */ +- return; +- } +- +- if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) { +- /* We've already reached the maximum optimization level, so +- dont bother banging on the status registers */ +- return; +- } +- +- /* QAM EQ lock check */ +- reg = s5h1409_readreg(state, 0xf0); +- +- if ((reg >> 13) & 0x1) { +- reg &= 0xff; +- +- s5h1409_writereg(state, 0x96, 0x000c); +- if (reg < 0x68) { +- if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) { +- dprintk("%s() setting QAM state to OPT_L3\n", +- __func__); +- s5h1409_writereg(state, 0x93, 0x3130); +- s5h1409_writereg(state, 0x9e, 0x2836); +- state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3; +- } +- } else { +- if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) { +- dprintk("%s() setting QAM state to OPT_L2\n", +- __func__); +- s5h1409_writereg(state, 0x93, 0x3332); +- s5h1409_writereg(state, 0x9e, 0x2c37); +- state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2; +- } +- } +- +- } else { +- if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) { +- dprintk("%s() setting QAM state to OPT_L1\n", __func__); +- s5h1409_writereg(state, 0x96, 0x0008); +- s5h1409_writereg(state, 0x93, 0x3332); +- s5h1409_writereg(state, 0x9e, 0x2c37); +- state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1; +- } +- } +-} +- +-static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg; +- +- if (state->is_qam_locked) +- return; +- +- /* QAM EQ lock check */ +- reg = s5h1409_readreg(state, 0xf0); +- +- if ((reg >> 13) & 0x1) { +- +- state->is_qam_locked = 1; +- reg &= 0xff; +- +- s5h1409_writereg(state, 0x96, 0x00c); +- if ((reg < 0x38) || (reg > 0x68)) { +- s5h1409_writereg(state, 0x93, 0x3332); +- s5h1409_writereg(state, 0x9e, 0x2c37); +- } else { +- s5h1409_writereg(state, 0x93, 0x3130); +- s5h1409_writereg(state, 0x9e, 0x2836); +- } +- +- } else { +- s5h1409_writereg(state, 0x96, 0x0008); +- s5h1409_writereg(state, 0x93, 0x3332); +- s5h1409_writereg(state, 0x9e, 0x2c37); +- } +-} +- +-static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg, reg1, reg2; +- +- if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) { +- /* We've done the optimization already */ +- return; +- } +- +- reg = s5h1409_readreg(state, 0xf1); +- +- /* Master lock */ +- if ((reg >> 15) & 0x1) { +- if (state->qam_state == QAM_STATE_UNTUNED || +- state->qam_state == QAM_STATE_TUNING_STARTED) { +- dprintk("%s() setting QAM state to INTERLEAVE_SET\n", +- __func__); +- reg1 = s5h1409_readreg(state, 0xb2); +- reg2 = s5h1409_readreg(state, 0xad); +- +- s5h1409_writereg(state, 0x96, 0x0020); +- s5h1409_writereg(state, 0xad, +- (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); +- state->qam_state = QAM_STATE_INTERLEAVE_SET; +- } +- } else { +- if (state->qam_state == QAM_STATE_UNTUNED) { +- dprintk("%s() setting QAM state to TUNING_STARTED\n", +- __func__); +- s5h1409_writereg(state, 0x96, 0x08); +- s5h1409_writereg(state, 0xab, +- s5h1409_readreg(state, 0xab) | 0x1001); +- state->qam_state = QAM_STATE_TUNING_STARTED; +- } +- } +-} +- +-static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg, reg1, reg2; +- +- reg = s5h1409_readreg(state, 0xf1); +- +- /* Master lock */ +- if ((reg >> 15) & 0x1) { +- if (state->qam_state != 2) { +- state->qam_state = 2; +- reg1 = s5h1409_readreg(state, 0xb2); +- reg2 = s5h1409_readreg(state, 0xad); +- +- s5h1409_writereg(state, 0x96, 0x20); +- s5h1409_writereg(state, 0xad, +- (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); +- s5h1409_writereg(state, 0xab, +- s5h1409_readreg(state, 0xab) & 0xeffe); +- } +- } else { +- if (state->qam_state != 1) { +- state->qam_state = 1; +- s5h1409_writereg(state, 0x96, 0x08); +- s5h1409_writereg(state, 0xab, +- s5h1409_readreg(state, 0xab) | 0x1001); +- } +- } +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-static int s5h1409_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1409_state *state = fe->demodulator_priv; +- +- dprintk("%s(frequency=%d)\n", __func__, p->frequency); +- +- s5h1409_softreset(fe); +- +- state->current_frequency = p->frequency; +- +- s5h1409_enable_modulation(fe, p->modulation); +- +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Issue a reset to the demod so it knows to resync against the +- newly tuned frequency */ +- s5h1409_softreset(fe); +- +- /* Optimize the demod for QAM */ +- if (state->current_modulation != VSB_8) { +- /* This almost certainly applies to all boards, but for now +- only do it for the HVR-1600. Once the other boards are +- tested, the "legacy" versions can just go away */ +- if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { +- s5h1409_set_qam_interleave_mode(fe); +- s5h1409_set_qam_amhum_mode(fe); +- } else { +- s5h1409_set_qam_amhum_mode_legacy(fe); +- s5h1409_set_qam_interleave_mode_legacy(fe); +- } +- } +- +- return 0; +-} +- +-static int s5h1409_set_mpeg_timing(struct dvb_frontend *fe, int mode) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 val; +- +- dprintk("%s(%d)\n", __func__, mode); +- +- val = s5h1409_readreg(state, 0xac) & 0xcfff; +- switch (mode) { +- case S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: +- val |= 0x0000; +- break; +- case S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: +- dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); +- val |= 0x1000; +- break; +- case S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: +- val |= 0x2000; +- break; +- case S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: +- val |= 0x3000; +- break; +- default: +- return -EINVAL; +- } +- +- /* Configure MPEG Signal Timing charactistics */ +- return s5h1409_writereg(state, 0xac, val); +-} +- +-/* Reset the demod hardware and reset all of the configuration registers +- to a default state. */ +-static int s5h1409_init(struct dvb_frontend *fe) +-{ +- int i; +- +- struct s5h1409_state *state = fe->demodulator_priv; +- dprintk("%s()\n", __func__); +- +- s5h1409_sleep(fe, 0); +- s5h1409_register_reset(fe); +- +- for (i = 0; i < ARRAY_SIZE(init_tab); i++) +- s5h1409_writereg(state, init_tab[i].reg, init_tab[i].data); +- +- /* The datasheet says that after initialisation, VSB is default */ +- state->current_modulation = VSB_8; +- +- /* Optimize for the HVR-1600 if appropriate. Note that some of these +- may get folded into the generic case after testing with other +- devices */ +- if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { +- /* VSB AGC REF */ +- s5h1409_writereg(state, 0x09, 0x0050); +- +- /* Unknown but Windows driver does it... */ +- s5h1409_writereg(state, 0x21, 0x0001); +- s5h1409_writereg(state, 0x50, 0x030e); +- +- /* QAM AGC REF */ +- s5h1409_writereg(state, 0x82, 0x0800); +- } +- +- if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) +- s5h1409_writereg(state, 0xab, +- s5h1409_readreg(state, 0xab) | 0x100); /* Serial */ +- else +- s5h1409_writereg(state, 0xab, +- s5h1409_readreg(state, 0xab) & 0xfeff); /* Parallel */ +- +- s5h1409_set_spectralinversion(fe, state->config->inversion); +- s5h1409_set_if_freq(fe, state->if_freq); +- s5h1409_set_gpio(fe, state->config->gpio); +- s5h1409_set_mpeg_timing(fe, state->config->mpeg_timing); +- s5h1409_softreset(fe); +- +- /* Note: Leaving the I2C gate closed. */ +- s5h1409_i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg; +- u32 tuner_status = 0; +- +- *status = 0; +- +- /* Optimize the demod for QAM */ +- if (state->current_modulation != VSB_8) { +- /* This almost certainly applies to all boards, but for now +- only do it for the HVR-1600. Once the other boards are +- tested, the "legacy" versions can just go away */ +- if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { +- s5h1409_set_qam_interleave_mode(fe); +- s5h1409_set_qam_amhum_mode(fe); +- } +- } +- +- /* Get the demodulator status */ +- reg = s5h1409_readreg(state, 0xf1); +- if (reg & 0x1000) +- *status |= FE_HAS_VITERBI; +- if (reg & 0x8000) +- *status |= FE_HAS_LOCK | FE_HAS_SYNC; +- +- switch (state->config->status_mode) { +- case S5H1409_DEMODLOCKING: +- if (*status & FE_HAS_VITERBI) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- case S5H1409_TUNERLOCKING: +- /* Get the tuner status */ +- if (fe->ops.tuner_ops.get_status) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- fe->ops.tuner_ops.get_status(fe, &tuner_status); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (tuner_status) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- } +- +- dprintk("%s() status 0x%08x\n", __func__, *status); +- +- return 0; +-} +- +-static int s5h1409_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { +- if (v < qam256_snr_tab[i].val) { +- *snr = qam256_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- return ret; +-} +- +-static int s5h1409_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { +- if (v < qam64_snr_tab[i].val) { +- *snr = qam64_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- return ret; +-} +- +-static int s5h1409_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { +- if (v > vsb_snr_tab[i].val) { +- *snr = vsb_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- dprintk("%s() snr=%d\n", __func__, *snr); +- return ret; +-} +- +-static int s5h1409_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- u16 reg; +- dprintk("%s()\n", __func__); +- +- switch (state->current_modulation) { +- case QAM_64: +- reg = s5h1409_readreg(state, 0xf0) & 0xff; +- return s5h1409_qam64_lookup_snr(fe, snr, reg); +- case QAM_256: +- reg = s5h1409_readreg(state, 0xf0) & 0xff; +- return s5h1409_qam256_lookup_snr(fe, snr, reg); +- case VSB_8: +- reg = s5h1409_readreg(state, 0xf1) & 0x3ff; +- return s5h1409_vsb_lookup_snr(fe, snr, reg); +- default: +- break; +- } +- +- return -EINVAL; +-} +- +-static int s5h1409_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- /* borrowed from lgdt330x.c +- * +- * Calculate strength from SNR up to 35dB +- * Even though the SNR can go higher than 35dB, +- * there is some comfort factor in having a range of +- * strong signals that can show at 100% +- */ +- u16 snr; +- u32 tmp; +- int ret = s5h1409_read_snr(fe, &snr); +- +- *signal_strength = 0; +- +- if (0 == ret) { +- /* The following calculation method was chosen +- * purely for the sake of code re-use from the +- * other demod drivers that use this method */ +- +- /* Convert from SNR in dB * 10 to 8.24 fixed-point */ +- tmp = (snr * ((1 << 24) / 10)); +- +- /* Convert from 8.24 fixed-point to +- * scale the range 0 - 35*2^24 into 0 - 65535*/ +- if (tmp >= 8960 * 0x10000) +- *signal_strength = 0xffff; +- else +- *signal_strength = tmp / 8960; +- } +- +- return ret; +-} +- +-static int s5h1409_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- +- *ucblocks = s5h1409_readreg(state, 0xb5); +- +- return 0; +-} +- +-static int s5h1409_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- return s5h1409_read_ucblocks(fe, ber); +-} +- +-static int s5h1409_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1409_state *state = fe->demodulator_priv; +- +- p->frequency = state->current_frequency; +- p->modulation = state->current_modulation; +- +- return 0; +-} +- +-static int s5h1409_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void s5h1409_release(struct dvb_frontend *fe) +-{ +- struct s5h1409_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops s5h1409_ops; +- +-struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, +- struct i2c_adapter *i2c) +-{ +- struct s5h1409_state *state = NULL; +- u16 reg; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->current_modulation = 0; +- state->if_freq = S5H1409_VSB_IF_FREQ; +- +- /* check if the demod exists */ +- reg = s5h1409_readreg(state, 0x04); +- if ((reg != 0x0066) && (reg != 0x007f)) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &s5h1409_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- if (s5h1409_init(&state->frontend) != 0) { +- printk(KERN_ERR "%s: Failed to initialize correctly\n", +- __func__); +- goto error; +- } +- +- /* Note: Leaving the I2C gate open here. */ +- s5h1409_i2c_gate_ctrl(&state->frontend, 1); +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(s5h1409_attach); +- +-static struct dvb_frontend_ops s5h1409_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Samsung S5H1409 QAM/8VSB Frontend", +- .frequency_min = 54000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- +- .init = s5h1409_init, +- .i2c_gate_ctrl = s5h1409_i2c_gate_ctrl, +- .set_frontend = s5h1409_set_frontend, +- .get_frontend = s5h1409_get_frontend, +- .get_tune_settings = s5h1409_get_tune_settings, +- .read_status = s5h1409_read_status, +- .read_ber = s5h1409_read_ber, +- .read_signal_strength = s5h1409_read_signal_strength, +- .read_snr = s5h1409_read_snr, +- .read_ucblocks = s5h1409_read_ucblocks, +- .release = s5h1409_release, +-}; +- +-MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +- +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb/frontends/s5h1409.h +deleted file mode 100644 +index 91f2ebd..0000000 +--- a/drivers/media/dvb/frontends/s5h1409.h ++++ /dev/null +@@ -1,88 +0,0 @@ +-/* +- Samsung S5H1409 VSB/QAM demodulator driver +- +- Copyright (C) 2006 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef __S5H1409_H__ +-#define __S5H1409_H__ +- +-#include +- +-struct s5h1409_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* serial/parallel output */ +-#define S5H1409_PARALLEL_OUTPUT 0 +-#define S5H1409_SERIAL_OUTPUT 1 +- u8 output_mode; +- +- /* GPIO Setting */ +-#define S5H1409_GPIO_OFF 0 +-#define S5H1409_GPIO_ON 1 +- u8 gpio; +- +- /* IF Freq for QAM in KHz, VSB is hardcoded to 5380 */ +- u16 qam_if; +- +- /* Spectral Inversion */ +-#define S5H1409_INVERSION_OFF 0 +-#define S5H1409_INVERSION_ON 1 +- u8 inversion; +- +- /* Return lock status based on tuner lock, or demod lock */ +-#define S5H1409_TUNERLOCKING 0 +-#define S5H1409_DEMODLOCKING 1 +- u8 status_mode; +- +- /* MPEG signal timing */ +-#define S5H1409_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 +-#define S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 +-#define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 +-#define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +- u16 mpeg_timing; +- +- /* HVR-1600 optimizations (to better work with MXL5005s) +- Note: some of these are likely to be folded into the generic driver +- after being regression tested with other boards */ +-#define S5H1409_HVR1600_NOOPTIMIZE 0 +-#define S5H1409_HVR1600_OPTIMIZE 1 +- u8 hvr1600_opt; +-}; +- +-#if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *s5h1409_attach( +- const struct s5h1409_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_S5H1409 */ +- +-#endif /* __S5H1409_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c +deleted file mode 100644 +index 6cc4b7a..0000000 +--- a/drivers/media/dvb/frontends/s5h1411.c ++++ /dev/null +@@ -1,951 +0,0 @@ +-/* +- Samsung S5H1411 VSB/QAM demodulator driver +- +- Copyright (C) 2008 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "s5h1411.h" +- +-struct s5h1411_state { +- +- struct i2c_adapter *i2c; +- +- /* configuration settings */ +- const struct s5h1411_config *config; +- +- struct dvb_frontend frontend; +- +- fe_modulation_t current_modulation; +- unsigned int first_tune:1; +- +- u32 current_frequency; +- int if_freq; +- +- u8 inversion; +-}; +- +-static int debug; +- +-#define dprintk(arg...) do { \ +- if (debug) \ +- printk(arg); \ +- } while (0) +- +-/* Register values to initialise the demod, defaults to VSB */ +-static struct init_tab { +- u8 addr; +- u8 reg; +- u16 data; +-} init_tab[] = { +- { S5H1411_I2C_TOP_ADDR, 0x00, 0x0071, }, +- { S5H1411_I2C_TOP_ADDR, 0x08, 0x0047, }, +- { S5H1411_I2C_TOP_ADDR, 0x1c, 0x0400, }, +- { S5H1411_I2C_TOP_ADDR, 0x1e, 0x0370, }, +- { S5H1411_I2C_TOP_ADDR, 0x1f, 0x342c, }, +- { S5H1411_I2C_TOP_ADDR, 0x24, 0x0231, }, +- { S5H1411_I2C_TOP_ADDR, 0x25, 0x1011, }, +- { S5H1411_I2C_TOP_ADDR, 0x26, 0x0f07, }, +- { S5H1411_I2C_TOP_ADDR, 0x27, 0x0f04, }, +- { S5H1411_I2C_TOP_ADDR, 0x28, 0x070f, }, +- { S5H1411_I2C_TOP_ADDR, 0x29, 0x2820, }, +- { S5H1411_I2C_TOP_ADDR, 0x2a, 0x102e, }, +- { S5H1411_I2C_TOP_ADDR, 0x2b, 0x0220, }, +- { S5H1411_I2C_TOP_ADDR, 0x2e, 0x0d0e, }, +- { S5H1411_I2C_TOP_ADDR, 0x2f, 0x1013, }, +- { S5H1411_I2C_TOP_ADDR, 0x31, 0x171b, }, +- { S5H1411_I2C_TOP_ADDR, 0x32, 0x0e0f, }, +- { S5H1411_I2C_TOP_ADDR, 0x33, 0x0f10, }, +- { S5H1411_I2C_TOP_ADDR, 0x34, 0x170e, }, +- { S5H1411_I2C_TOP_ADDR, 0x35, 0x4b10, }, +- { S5H1411_I2C_TOP_ADDR, 0x36, 0x0f17, }, +- { S5H1411_I2C_TOP_ADDR, 0x3c, 0x1577, }, +- { S5H1411_I2C_TOP_ADDR, 0x3d, 0x081a, }, +- { S5H1411_I2C_TOP_ADDR, 0x3e, 0x77ee, }, +- { S5H1411_I2C_TOP_ADDR, 0x40, 0x1e09, }, +- { S5H1411_I2C_TOP_ADDR, 0x41, 0x0f0c, }, +- { S5H1411_I2C_TOP_ADDR, 0x42, 0x1f10, }, +- { S5H1411_I2C_TOP_ADDR, 0x4d, 0x0509, }, +- { S5H1411_I2C_TOP_ADDR, 0x4e, 0x0a00, }, +- { S5H1411_I2C_TOP_ADDR, 0x50, 0x0000, }, +- { S5H1411_I2C_TOP_ADDR, 0x5b, 0x0000, }, +- { S5H1411_I2C_TOP_ADDR, 0x5c, 0x0008, }, +- { S5H1411_I2C_TOP_ADDR, 0x57, 0x1101, }, +- { S5H1411_I2C_TOP_ADDR, 0x65, 0x007c, }, +- { S5H1411_I2C_TOP_ADDR, 0x68, 0x0512, }, +- { S5H1411_I2C_TOP_ADDR, 0x69, 0x0258, }, +- { S5H1411_I2C_TOP_ADDR, 0x70, 0x0004, }, +- { S5H1411_I2C_TOP_ADDR, 0x71, 0x0007, }, +- { S5H1411_I2C_TOP_ADDR, 0x76, 0x00a9, }, +- { S5H1411_I2C_TOP_ADDR, 0x78, 0x3141, }, +- { S5H1411_I2C_TOP_ADDR, 0x7a, 0x3141, }, +- { S5H1411_I2C_TOP_ADDR, 0xb3, 0x8003, }, +- { S5H1411_I2C_TOP_ADDR, 0xb5, 0xa6bb, }, +- { S5H1411_I2C_TOP_ADDR, 0xb6, 0x0609, }, +- { S5H1411_I2C_TOP_ADDR, 0xb7, 0x2f06, }, +- { S5H1411_I2C_TOP_ADDR, 0xb8, 0x003f, }, +- { S5H1411_I2C_TOP_ADDR, 0xb9, 0x2700, }, +- { S5H1411_I2C_TOP_ADDR, 0xba, 0xfac8, }, +- { S5H1411_I2C_TOP_ADDR, 0xbe, 0x1003, }, +- { S5H1411_I2C_TOP_ADDR, 0xbf, 0x103f, }, +- { S5H1411_I2C_TOP_ADDR, 0xce, 0x2000, }, +- { S5H1411_I2C_TOP_ADDR, 0xcf, 0x0800, }, +- { S5H1411_I2C_TOP_ADDR, 0xd0, 0x0800, }, +- { S5H1411_I2C_TOP_ADDR, 0xd1, 0x0400, }, +- { S5H1411_I2C_TOP_ADDR, 0xd2, 0x0800, }, +- { S5H1411_I2C_TOP_ADDR, 0xd3, 0x2000, }, +- { S5H1411_I2C_TOP_ADDR, 0xd4, 0x3000, }, +- { S5H1411_I2C_TOP_ADDR, 0xdb, 0x4a9b, }, +- { S5H1411_I2C_TOP_ADDR, 0xdc, 0x1000, }, +- { S5H1411_I2C_TOP_ADDR, 0xde, 0x0001, }, +- { S5H1411_I2C_TOP_ADDR, 0xdf, 0x0000, }, +- { S5H1411_I2C_TOP_ADDR, 0xe3, 0x0301, }, +- { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0000, }, +- { S5H1411_I2C_QAM_ADDR, 0xf3, 0x0001, }, +- { S5H1411_I2C_QAM_ADDR, 0x08, 0x0600, }, +- { S5H1411_I2C_QAM_ADDR, 0x18, 0x4201, }, +- { S5H1411_I2C_QAM_ADDR, 0x1e, 0x6476, }, +- { S5H1411_I2C_QAM_ADDR, 0x21, 0x0830, }, +- { S5H1411_I2C_QAM_ADDR, 0x0c, 0x5679, }, +- { S5H1411_I2C_QAM_ADDR, 0x0d, 0x579b, }, +- { S5H1411_I2C_QAM_ADDR, 0x24, 0x0102, }, +- { S5H1411_I2C_QAM_ADDR, 0x31, 0x7488, }, +- { S5H1411_I2C_QAM_ADDR, 0x32, 0x0a08, }, +- { S5H1411_I2C_QAM_ADDR, 0x3d, 0x8689, }, +- { S5H1411_I2C_QAM_ADDR, 0x49, 0x0048, }, +- { S5H1411_I2C_QAM_ADDR, 0x57, 0x2012, }, +- { S5H1411_I2C_QAM_ADDR, 0x5d, 0x7676, }, +- { S5H1411_I2C_QAM_ADDR, 0x04, 0x0400, }, +- { S5H1411_I2C_QAM_ADDR, 0x58, 0x00c0, }, +- { S5H1411_I2C_QAM_ADDR, 0x5b, 0x0100, }, +-}; +- +-/* VSB SNR lookup table */ +-static struct vsb_snr_tab { +- u16 val; +- u16 data; +-} vsb_snr_tab[] = { +- { 0x39f, 300, }, +- { 0x39b, 295, }, +- { 0x397, 290, }, +- { 0x394, 285, }, +- { 0x38f, 280, }, +- { 0x38b, 275, }, +- { 0x387, 270, }, +- { 0x382, 265, }, +- { 0x37d, 260, }, +- { 0x377, 255, }, +- { 0x370, 250, }, +- { 0x36a, 245, }, +- { 0x364, 240, }, +- { 0x35b, 235, }, +- { 0x353, 230, }, +- { 0x349, 225, }, +- { 0x340, 320, }, +- { 0x337, 215, }, +- { 0x327, 210, }, +- { 0x31b, 205, }, +- { 0x310, 200, }, +- { 0x302, 195, }, +- { 0x2f3, 190, }, +- { 0x2e4, 185, }, +- { 0x2d7, 180, }, +- { 0x2cd, 175, }, +- { 0x2bb, 170, }, +- { 0x2a9, 165, }, +- { 0x29e, 160, }, +- { 0x284, 155, }, +- { 0x27a, 150, }, +- { 0x260, 145, }, +- { 0x23a, 140, }, +- { 0x224, 135, }, +- { 0x213, 130, }, +- { 0x204, 125, }, +- { 0x1fe, 120, }, +- { 0, 0, }, +-}; +- +-/* QAM64 SNR lookup table */ +-static struct qam64_snr_tab { +- u16 val; +- u16 data; +-} qam64_snr_tab[] = { +- { 0x0001, 0, }, +- { 0x0af0, 300, }, +- { 0x0d80, 290, }, +- { 0x10a0, 280, }, +- { 0x14b5, 270, }, +- { 0x1590, 268, }, +- { 0x1680, 266, }, +- { 0x17b0, 264, }, +- { 0x18c0, 262, }, +- { 0x19b0, 260, }, +- { 0x1ad0, 258, }, +- { 0x1d00, 256, }, +- { 0x1da0, 254, }, +- { 0x1ef0, 252, }, +- { 0x2050, 250, }, +- { 0x20f0, 249, }, +- { 0x21d0, 248, }, +- { 0x22b0, 247, }, +- { 0x23a0, 246, }, +- { 0x2470, 245, }, +- { 0x24f0, 244, }, +- { 0x25a0, 243, }, +- { 0x26c0, 242, }, +- { 0x27b0, 241, }, +- { 0x28d0, 240, }, +- { 0x29b0, 239, }, +- { 0x2ad0, 238, }, +- { 0x2ba0, 237, }, +- { 0x2c80, 236, }, +- { 0x2d20, 235, }, +- { 0x2e00, 234, }, +- { 0x2f10, 233, }, +- { 0x3050, 232, }, +- { 0x3190, 231, }, +- { 0x3300, 230, }, +- { 0x3340, 229, }, +- { 0x3200, 228, }, +- { 0x3550, 227, }, +- { 0x3610, 226, }, +- { 0x3600, 225, }, +- { 0x3700, 224, }, +- { 0x3800, 223, }, +- { 0x3920, 222, }, +- { 0x3a20, 221, }, +- { 0x3b30, 220, }, +- { 0x3d00, 219, }, +- { 0x3e00, 218, }, +- { 0x4000, 217, }, +- { 0x4100, 216, }, +- { 0x4300, 215, }, +- { 0x4400, 214, }, +- { 0x4600, 213, }, +- { 0x4700, 212, }, +- { 0x4800, 211, }, +- { 0x4a00, 210, }, +- { 0x4b00, 209, }, +- { 0x4d00, 208, }, +- { 0x4f00, 207, }, +- { 0x5050, 206, }, +- { 0x5200, 205, }, +- { 0x53c0, 204, }, +- { 0x5450, 203, }, +- { 0x5650, 202, }, +- { 0x5820, 201, }, +- { 0x6000, 200, }, +- { 0xffff, 0, }, +-}; +- +-/* QAM256 SNR lookup table */ +-static struct qam256_snr_tab { +- u16 val; +- u16 data; +-} qam256_snr_tab[] = { +- { 0x0001, 0, }, +- { 0x0970, 400, }, +- { 0x0a90, 390, }, +- { 0x0b90, 380, }, +- { 0x0d90, 370, }, +- { 0x0ff0, 360, }, +- { 0x1240, 350, }, +- { 0x1345, 348, }, +- { 0x13c0, 346, }, +- { 0x14c0, 344, }, +- { 0x1500, 342, }, +- { 0x1610, 340, }, +- { 0x1700, 338, }, +- { 0x1800, 336, }, +- { 0x18b0, 334, }, +- { 0x1900, 332, }, +- { 0x1ab0, 330, }, +- { 0x1bc0, 328, }, +- { 0x1cb0, 326, }, +- { 0x1db0, 324, }, +- { 0x1eb0, 322, }, +- { 0x2030, 320, }, +- { 0x2200, 318, }, +- { 0x2280, 316, }, +- { 0x2410, 314, }, +- { 0x25b0, 312, }, +- { 0x27a0, 310, }, +- { 0x2840, 308, }, +- { 0x29d0, 306, }, +- { 0x2b10, 304, }, +- { 0x2d30, 302, }, +- { 0x2f20, 300, }, +- { 0x30c0, 298, }, +- { 0x3260, 297, }, +- { 0x32c0, 296, }, +- { 0x3300, 295, }, +- { 0x33b0, 294, }, +- { 0x34b0, 293, }, +- { 0x35a0, 292, }, +- { 0x3650, 291, }, +- { 0x3800, 290, }, +- { 0x3900, 289, }, +- { 0x3a50, 288, }, +- { 0x3b30, 287, }, +- { 0x3cb0, 286, }, +- { 0x3e20, 285, }, +- { 0x3fa0, 284, }, +- { 0x40a0, 283, }, +- { 0x41c0, 282, }, +- { 0x42f0, 281, }, +- { 0x44a0, 280, }, +- { 0x4600, 279, }, +- { 0x47b0, 278, }, +- { 0x4900, 277, }, +- { 0x4a00, 276, }, +- { 0x4ba0, 275, }, +- { 0x4d00, 274, }, +- { 0x4f00, 273, }, +- { 0x5000, 272, }, +- { 0x51f0, 272, }, +- { 0x53a0, 270, }, +- { 0x5520, 269, }, +- { 0x5700, 268, }, +- { 0x5800, 267, }, +- { 0x5a00, 266, }, +- { 0x5c00, 265, }, +- { 0x5d00, 264, }, +- { 0x5f00, 263, }, +- { 0x6000, 262, }, +- { 0x6200, 261, }, +- { 0x6400, 260, }, +- { 0xffff, 0, }, +-}; +- +-/* 8 bit registers, 16 bit values */ +-static int s5h1411_writereg(struct s5h1411_state *state, +- u8 addr, u8 reg, u16 data) +-{ +- int ret; +- u8 buf[] = { reg, data >> 8, data & 0xff }; +- +- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, " +- "ret == %i)\n", __func__, addr, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static u16 s5h1411_readreg(struct s5h1411_state *state, u8 addr, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0, 0 }; +- +- struct i2c_msg msg[] = { +- { .addr = addr, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk(KERN_ERR "%s: readreg error (ret == %i)\n", +- __func__, ret); +- return (b1[0] << 8) | b1[1]; +-} +- +-static int s5h1411_softreset(struct dvb_frontend *fe) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 0); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf7, 1); +- return 0; +-} +- +-static int s5h1411_set_if_freq(struct dvb_frontend *fe, int KHz) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d KHz)\n", __func__, KHz); +- +- switch (KHz) { +- case 3250: +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x10d5); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x5342); +- s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x10d9); +- break; +- case 3500: +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1225); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x1e96); +- s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1225); +- break; +- case 4000: +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x14bc); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0xb53e); +- s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x14bd); +- break; +- default: +- dprintk("%s(%d KHz) Invalid, defaulting to 5380\n", +- __func__, KHz); +- /* no break, need to continue */ +- case 5380: +- case 44000: +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x38, 0x1be4); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x39, 0x3655); +- s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x2c, 0x1be4); +- break; +- } +- +- state->if_freq = KHz; +- +- return 0; +-} +- +-static int s5h1411_set_mpeg_timing(struct dvb_frontend *fe, int mode) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 val; +- +- dprintk("%s(%d)\n", __func__, mode); +- +- val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbe) & 0xcfff; +- switch (mode) { +- case S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK: +- val |= 0x0000; +- break; +- case S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK: +- dprintk("%s(%d) Mode1 or Defaulting\n", __func__, mode); +- val |= 0x1000; +- break; +- case S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK: +- val |= 0x2000; +- break; +- case S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK: +- val |= 0x3000; +- break; +- default: +- return -EINVAL; +- } +- +- /* Configure MPEG Signal Timing charactistics */ +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbe, val); +-} +- +-static int s5h1411_set_spectralinversion(struct dvb_frontend *fe, int inversion) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 val; +- +- dprintk("%s(%d)\n", __func__, inversion); +- val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x24) & ~0x1000; +- +- if (inversion == 1) +- val |= 0x1000; /* Inverted */ +- +- state->inversion = inversion; +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x24, val); +-} +- +-static int s5h1411_set_serialmode(struct dvb_frontend *fe, int serial) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 val; +- +- dprintk("%s(%d)\n", __func__, serial); +- val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xbd) & ~0x100; +- +- if (serial == 1) +- val |= 0x100; +- +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xbd, val); +-} +- +-static int s5h1411_enable_modulation(struct dvb_frontend *fe, +- fe_modulation_t m) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s(0x%08x)\n", __func__, m); +- +- if ((state->first_tune == 0) && (m == state->current_modulation)) { +- dprintk("%s() Already at desired modulation. Skipping...\n", +- __func__); +- return 0; +- } +- +- switch (m) { +- case VSB_8: +- dprintk("%s() VSB_8\n", __func__); +- s5h1411_set_if_freq(fe, state->config->vsb_if); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x71); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x00); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0xf1); +- break; +- case QAM_64: +- case QAM_256: +- case QAM_AUTO: +- dprintk("%s() QAM_AUTO (64/256)\n", __func__); +- s5h1411_set_if_freq(fe, state->config->qam_if); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0x00, 0x0171); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf6, 0x0001); +- s5h1411_writereg(state, S5H1411_I2C_QAM_ADDR, 0x16, 0x1101); +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xcd, 0x00f0); +- break; +- default: +- dprintk("%s() Invalid modulation\n", __func__); +- return -EINVAL; +- } +- +- state->current_modulation = m; +- state->first_tune = 0; +- s5h1411_softreset(fe); +- +- return 0; +-} +- +-static int s5h1411_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- if (enable) +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); +- else +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 0); +-} +- +-static int s5h1411_set_gpio(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 val; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- val = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xe0) & ~0x02; +- +- if (enable) +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, +- val | 0x02); +- else +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xe0, val); +-} +- +-static int s5h1411_set_powerstate(struct dvb_frontend *fe, int enable) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s(%d)\n", __func__, enable); +- +- if (enable) +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 1); +- else { +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf4, 0); +- s5h1411_softreset(fe); +- } +- +- return 0; +-} +- +-static int s5h1411_sleep(struct dvb_frontend *fe) +-{ +- return s5h1411_set_powerstate(fe, 1); +-} +- +-static int s5h1411_register_reset(struct dvb_frontend *fe) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s()\n", __func__); +- +- return s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf3, 0); +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-static int s5h1411_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1411_state *state = fe->demodulator_priv; +- +- dprintk("%s(frequency=%d)\n", __func__, p->frequency); +- +- s5h1411_softreset(fe); +- +- state->current_frequency = p->frequency; +- +- s5h1411_enable_modulation(fe, p->modulation); +- +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- fe->ops.tuner_ops.set_params(fe); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Issue a reset to the demod so it knows to resync against the +- newly tuned frequency */ +- s5h1411_softreset(fe); +- +- return 0; +-} +- +-/* Reset the demod hardware and reset all of the configuration registers +- to a default state. */ +-static int s5h1411_init(struct dvb_frontend *fe) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- int i; +- +- dprintk("%s()\n", __func__); +- +- s5h1411_set_powerstate(fe, 0); +- s5h1411_register_reset(fe); +- +- for (i = 0; i < ARRAY_SIZE(init_tab); i++) +- s5h1411_writereg(state, init_tab[i].addr, +- init_tab[i].reg, +- init_tab[i].data); +- +- /* The datasheet says that after initialisation, VSB is default */ +- state->current_modulation = VSB_8; +- +- /* Although the datasheet says it's in VSB, empirical evidence +- shows problems getting lock on the first tuning request. Make +- sure we call enable_modulation the first time around */ +- state->first_tune = 1; +- +- if (state->config->output_mode == S5H1411_SERIAL_OUTPUT) +- /* Serial */ +- s5h1411_set_serialmode(fe, 1); +- else +- /* Parallel */ +- s5h1411_set_serialmode(fe, 0); +- +- s5h1411_set_spectralinversion(fe, state->config->inversion); +- s5h1411_set_if_freq(fe, state->config->vsb_if); +- s5h1411_set_gpio(fe, state->config->gpio); +- s5h1411_set_mpeg_timing(fe, state->config->mpeg_timing); +- s5h1411_softreset(fe); +- +- /* Note: Leaving the I2C gate closed. */ +- s5h1411_i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int s5h1411_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 reg; +- u32 tuner_status = 0; +- +- *status = 0; +- +- /* Register F2 bit 15 = Master Lock, removed */ +- +- switch (state->current_modulation) { +- case QAM_64: +- case QAM_256: +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf0); +- if (reg & 0x10) /* QAM FEC Lock */ +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- if (reg & 0x100) /* QAM EQ Lock */ +- *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; +- +- break; +- case VSB_8: +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf2); +- if (reg & 0x1000) /* FEC Lock */ +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- if (reg & 0x2000) /* EQ Lock */ +- *status |= FE_HAS_VITERBI | FE_HAS_CARRIER | FE_HAS_SIGNAL; +- +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x53); +- if (reg & 0x1) /* AFC Lock */ +- *status |= FE_HAS_SIGNAL; +- +- break; +- default: +- return -EINVAL; +- } +- +- switch (state->config->status_mode) { +- case S5H1411_DEMODLOCKING: +- if (*status & FE_HAS_VITERBI) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- case S5H1411_TUNERLOCKING: +- /* Get the tuner status */ +- if (fe->ops.tuner_ops.get_status) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- fe->ops.tuner_ops.get_status(fe, &tuner_status); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (tuner_status) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- break; +- } +- +- dprintk("%s() status 0x%08x\n", __func__, *status); +- +- return 0; +-} +- +-static int s5h1411_qam256_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(qam256_snr_tab); i++) { +- if (v < qam256_snr_tab[i].val) { +- *snr = qam256_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- return ret; +-} +- +-static int s5h1411_qam64_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(qam64_snr_tab); i++) { +- if (v < qam64_snr_tab[i].val) { +- *snr = qam64_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- return ret; +-} +- +-static int s5h1411_vsb_lookup_snr(struct dvb_frontend *fe, u16 *snr, u16 v) +-{ +- int i, ret = -EINVAL; +- dprintk("%s()\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(vsb_snr_tab); i++) { +- if (v > vsb_snr_tab[i].val) { +- *snr = vsb_snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- dprintk("%s() snr=%d\n", __func__, *snr); +- return ret; +-} +- +-static int s5h1411_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- u16 reg; +- dprintk("%s()\n", __func__); +- +- switch (state->current_modulation) { +- case QAM_64: +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); +- return s5h1411_qam64_lookup_snr(fe, snr, reg); +- case QAM_256: +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xf1); +- return s5h1411_qam256_lookup_snr(fe, snr, reg); +- case VSB_8: +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, +- 0xf2) & 0x3ff; +- return s5h1411_vsb_lookup_snr(fe, snr, reg); +- default: +- break; +- } +- +- return -EINVAL; +-} +- +-static int s5h1411_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- /* borrowed from lgdt330x.c +- * +- * Calculate strength from SNR up to 35dB +- * Even though the SNR can go higher than 35dB, +- * there is some comfort factor in having a range of +- * strong signals that can show at 100% +- */ +- u16 snr; +- u32 tmp; +- int ret = s5h1411_read_snr(fe, &snr); +- +- *signal_strength = 0; +- +- if (0 == ret) { +- /* The following calculation method was chosen +- * purely for the sake of code re-use from the +- * other demod drivers that use this method */ +- +- /* Convert from SNR in dB * 10 to 8.24 fixed-point */ +- tmp = (snr * ((1 << 24) / 10)); +- +- /* Convert from 8.24 fixed-point to +- * scale the range 0 - 35*2^24 into 0 - 65535*/ +- if (tmp >= 8960 * 0x10000) +- *signal_strength = 0xffff; +- else +- *signal_strength = tmp / 8960; +- } +- +- return ret; +-} +- +-static int s5h1411_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- +- *ucblocks = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0xc9); +- +- return 0; +-} +- +-static int s5h1411_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- return s5h1411_read_ucblocks(fe, ber); +-} +- +-static int s5h1411_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1411_state *state = fe->demodulator_priv; +- +- p->frequency = state->current_frequency; +- p->modulation = state->current_modulation; +- +- return 0; +-} +- +-static int s5h1411_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void s5h1411_release(struct dvb_frontend *fe) +-{ +- struct s5h1411_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops s5h1411_ops; +- +-struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, +- struct i2c_adapter *i2c) +-{ +- struct s5h1411_state *state = NULL; +- u16 reg; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->current_modulation = VSB_8; +- state->inversion = state->config->inversion; +- +- /* check if the demod exists */ +- reg = s5h1411_readreg(state, S5H1411_I2C_TOP_ADDR, 0x05); +- if (reg != 0x0066) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &s5h1411_ops, +- sizeof(struct dvb_frontend_ops)); +- +- state->frontend.demodulator_priv = state; +- +- if (s5h1411_init(&state->frontend) != 0) { +- printk(KERN_ERR "%s: Failed to initialize correctly\n", +- __func__); +- goto error; +- } +- +- /* Note: Leaving the I2C gate open here. */ +- s5h1411_writereg(state, S5H1411_I2C_TOP_ADDR, 0xf5, 1); +- +- /* Put the device into low-power mode until first use */ +- s5h1411_set_powerstate(&state->frontend, 1); +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(s5h1411_attach); +- +-static struct dvb_frontend_ops s5h1411_ops = { +- .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, +- .info = { +- .name = "Samsung S5H1411 QAM/8VSB Frontend", +- .frequency_min = 54000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB +- }, +- +- .init = s5h1411_init, +- .sleep = s5h1411_sleep, +- .i2c_gate_ctrl = s5h1411_i2c_gate_ctrl, +- .set_frontend = s5h1411_set_frontend, +- .get_frontend = s5h1411_get_frontend, +- .get_tune_settings = s5h1411_get_tune_settings, +- .read_status = s5h1411_read_status, +- .read_ber = s5h1411_read_ber, +- .read_signal_strength = s5h1411_read_signal_strength, +- .read_snr = s5h1411_read_snr, +- .read_ucblocks = s5h1411_read_ucblocks, +- .release = s5h1411_release, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/frontends/s5h1411.h b/drivers/media/dvb/frontends/s5h1411.h +deleted file mode 100644 +index 45ec0f8..0000000 +--- a/drivers/media/dvb/frontends/s5h1411.h ++++ /dev/null +@@ -1,90 +0,0 @@ +-/* +- Samsung S5H1411 VSB/QAM demodulator driver +- +- Copyright (C) 2008 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef __S5H1411_H__ +-#define __S5H1411_H__ +- +-#include +- +-#define S5H1411_I2C_TOP_ADDR (0x32 >> 1) +-#define S5H1411_I2C_QAM_ADDR (0x34 >> 1) +- +-struct s5h1411_config { +- +- /* serial/parallel output */ +-#define S5H1411_PARALLEL_OUTPUT 0 +-#define S5H1411_SERIAL_OUTPUT 1 +- u8 output_mode; +- +- /* GPIO Setting */ +-#define S5H1411_GPIO_OFF 0 +-#define S5H1411_GPIO_ON 1 +- u8 gpio; +- +- /* MPEG signal timing */ +-#define S5H1411_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 +-#define S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 +-#define S5H1411_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 +-#define S5H1411_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +- u16 mpeg_timing; +- +- /* IF Freq for QAM and VSB in KHz */ +-#define S5H1411_IF_3250 3250 +-#define S5H1411_IF_3500 3500 +-#define S5H1411_IF_4000 4000 +-#define S5H1411_IF_5380 5380 +-#define S5H1411_IF_44000 44000 +-#define S5H1411_VSB_IF_DEFAULT S5H1411_IF_44000 +-#define S5H1411_QAM_IF_DEFAULT S5H1411_IF_44000 +- u16 qam_if; +- u16 vsb_if; +- +- /* Spectral Inversion */ +-#define S5H1411_INVERSION_OFF 0 +-#define S5H1411_INVERSION_ON 1 +- u8 inversion; +- +- /* Return lock status based on tuner lock, or demod lock */ +-#define S5H1411_TUNERLOCKING 0 +-#define S5H1411_DEMODLOCKING 1 +- u8 status_mode; +-}; +- +-#if defined(CONFIG_DVB_S5H1411) || \ +- (defined(CONFIG_DVB_S5H1411_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *s5h1411_attach( +- const struct s5h1411_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_S5H1411 */ +- +-#endif /* __S5H1411_H__ */ +- +-/* +- * Local variables: +- * c-basic-offset: 8 +- */ +diff --git a/drivers/media/dvb/frontends/s5h1420.c b/drivers/media/dvb/frontends/s5h1420.c +deleted file mode 100644 +index 2322257..0000000 +--- a/drivers/media/dvb/frontends/s5h1420.c ++++ /dev/null +@@ -1,980 +0,0 @@ +-/* +- * Driver for +- * Samsung S5H1420 and +- * PnpNetwork PN1010 QPSK Demodulator +- * +- * Copyright (C) 2005 Andrew de Quincey +- * Copyright (C) 2005-8 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +- +-#include "dvb_frontend.h" +-#include "s5h1420.h" +-#include "s5h1420_priv.h" +- +-#define TONE_FREQ 22000 +- +-struct s5h1420_state { +- struct i2c_adapter* i2c; +- const struct s5h1420_config* config; +- +- struct dvb_frontend frontend; +- struct i2c_adapter tuner_i2c_adapter; +- +- u8 CON_1_val; +- +- u8 postlocked:1; +- u32 fclk; +- u32 tunedfreq; +- fe_code_rate_t fec_inner; +- u32 symbol_rate; +- +- /* FIXME: ugly workaround for flexcop's incapable i2c-controller +- * it does not support repeated-start, workaround: write addr-1 +- * and then read +- */ +- u8 shadow[256]; +-}; +- +-static u32 s5h1420_getsymbolrate(struct s5h1420_state* state); +-static int s5h1420_get_tune_settings(struct dvb_frontend* fe, +- struct dvb_frontend_tune_settings* fesettings); +- +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "enable debugging"); +- +-#define dprintk(x...) do { \ +- if (debug) \ +- printk(KERN_DEBUG "S5H1420: " x); \ +-} while (0) +- +-static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg) +-{ +- int ret; +- u8 b[2]; +- struct i2c_msg msg[] = { +- { .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 }, +- { .addr = state->config->demod_address, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 }, +- }; +- +- b[0] = (reg - 1) & 0xff; +- b[1] = state->shadow[(reg - 1) & 0xff]; +- +- if (state->config->repeated_start_workaround) { +- ret = i2c_transfer(state->i2c, msg, 3); +- if (ret != 3) +- return ret; +- } else { +- ret = i2c_transfer(state->i2c, &msg[1], 1); +- if (ret != 1) +- return ret; +- ret = i2c_transfer(state->i2c, &msg[2], 1); +- if (ret != 1) +- return ret; +- } +- +- /* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */ +- +- return b[0]; +-} +- +-static int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- int err; +- +- /* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */ +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- state->shadow[reg] = data; +- +- return 0; +-} +- +-static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- dprintk("enter %s\n", __func__); +- +- switch(voltage) { +- case SEC_VOLTAGE_13: +- s5h1420_writereg(state, 0x3c, +- (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02); +- break; +- +- case SEC_VOLTAGE_18: +- s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03); +- break; +- +- case SEC_VOLTAGE_OFF: +- s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd); +- break; +- } +- +- dprintk("leave %s\n", __func__); +- return 0; +-} +- +-static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- dprintk("enter %s\n", __func__); +- switch(tone) { +- case SEC_TONE_ON: +- s5h1420_writereg(state, 0x3b, +- (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08); +- break; +- +- case SEC_TONE_OFF: +- s5h1420_writereg(state, 0x3b, +- (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01); +- break; +- } +- dprintk("leave %s\n", __func__); +- +- return 0; +-} +- +-static int s5h1420_send_master_cmd (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd* cmd) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- u8 val; +- int i; +- unsigned long timeout; +- int result = 0; +- +- dprintk("enter %s\n", __func__); +- if (cmd->msg_len > 8) +- return -EINVAL; +- +- /* setup for DISEQC */ +- val = s5h1420_readreg(state, 0x3b); +- s5h1420_writereg(state, 0x3b, 0x02); +- msleep(15); +- +- /* write the DISEQC command bytes */ +- for(i=0; i< cmd->msg_len; i++) { +- s5h1420_writereg(state, 0x3d + i, cmd->msg[i]); +- } +- +- /* kick off transmission */ +- s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | +- ((cmd->msg_len-1) << 4) | 0x08); +- +- /* wait for transmission to complete */ +- timeout = jiffies + ((100*HZ) / 1000); +- while(time_before(jiffies, timeout)) { +- if (!(s5h1420_readreg(state, 0x3b) & 0x08)) +- break; +- +- msleep(5); +- } +- if (time_after(jiffies, timeout)) +- result = -ETIMEDOUT; +- +- /* restore original settings */ +- s5h1420_writereg(state, 0x3b, val); +- msleep(15); +- dprintk("leave %s\n", __func__); +- return result; +-} +- +-static int s5h1420_recv_slave_reply (struct dvb_frontend* fe, +- struct dvb_diseqc_slave_reply* reply) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- u8 val; +- int i; +- int length; +- unsigned long timeout; +- int result = 0; +- +- /* setup for DISEQC receive */ +- val = s5h1420_readreg(state, 0x3b); +- s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */ +- msleep(15); +- +- /* wait for reception to complete */ +- timeout = jiffies + ((reply->timeout*HZ) / 1000); +- while(time_before(jiffies, timeout)) { +- if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */ +- break; +- +- msleep(5); +- } +- if (time_after(jiffies, timeout)) { +- result = -ETIMEDOUT; +- goto exit; +- } +- +- /* check error flag - FIXME: not sure what this does - docs do not describe +- * beyond "error flag for diseqc receive data :( */ +- if (s5h1420_readreg(state, 0x49)) { +- result = -EIO; +- goto exit; +- } +- +- /* check length */ +- length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4; +- if (length > sizeof(reply->msg)) { +- result = -EOVERFLOW; +- goto exit; +- } +- reply->msg_len = length; +- +- /* extract data */ +- for(i=0; i< length; i++) { +- reply->msg[i] = s5h1420_readreg(state, 0x3d + i); +- } +- +-exit: +- /* restore original settings */ +- s5h1420_writereg(state, 0x3b, val); +- msleep(15); +- return result; +-} +- +-static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- u8 val; +- int result = 0; +- unsigned long timeout; +- +- /* setup for tone burst */ +- val = s5h1420_readreg(state, 0x3b); +- s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01); +- +- /* set value for B position if requested */ +- if (minicmd == SEC_MINI_B) { +- s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04); +- } +- msleep(15); +- +- /* start transmission */ +- s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08); +- +- /* wait for transmission to complete */ +- timeout = jiffies + ((100*HZ) / 1000); +- while(time_before(jiffies, timeout)) { +- if (!(s5h1420_readreg(state, 0x3b) & 0x08)) +- break; +- +- msleep(5); +- } +- if (time_after(jiffies, timeout)) +- result = -ETIMEDOUT; +- +- /* restore original settings */ +- s5h1420_writereg(state, 0x3b, val); +- msleep(15); +- return result; +-} +- +-static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state) +-{ +- u8 val; +- fe_status_t status = 0; +- +- val = s5h1420_readreg(state, 0x14); +- if (val & 0x02) +- status |= FE_HAS_SIGNAL; +- if (val & 0x01) +- status |= FE_HAS_CARRIER; +- val = s5h1420_readreg(state, 0x36); +- if (val & 0x01) +- status |= FE_HAS_VITERBI; +- if (val & 0x20) +- status |= FE_HAS_SYNC; +- if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC)) +- status |= FE_HAS_LOCK; +- +- return status; +-} +- +-static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- u8 val; +- +- dprintk("enter %s\n", __func__); +- +- if (status == NULL) +- return -EINVAL; +- +- /* determine lock state */ +- *status = s5h1420_get_status_bits(state); +- +- /* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert +- the inversion, wait a bit and check again */ +- if (*status == (FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI)) { +- val = s5h1420_readreg(state, Vit10); +- if ((val & 0x07) == 0x03) { +- if (val & 0x08) +- s5h1420_writereg(state, Vit09, 0x13); +- else +- s5h1420_writereg(state, Vit09, 0x1b); +- +- /* wait a bit then update lock status */ +- mdelay(200); +- *status = s5h1420_get_status_bits(state); +- } +- } +- +- /* perform post lock setup */ +- if ((*status & FE_HAS_LOCK) && !state->postlocked) { +- +- /* calculate the data rate */ +- u32 tmp = s5h1420_getsymbolrate(state); +- switch (s5h1420_readreg(state, Vit10) & 0x07) { +- case 0: tmp = (tmp * 2 * 1) / 2; break; +- case 1: tmp = (tmp * 2 * 2) / 3; break; +- case 2: tmp = (tmp * 2 * 3) / 4; break; +- case 3: tmp = (tmp * 2 * 5) / 6; break; +- case 4: tmp = (tmp * 2 * 6) / 7; break; +- case 5: tmp = (tmp * 2 * 7) / 8; break; +- } +- +- if (tmp == 0) { +- printk(KERN_ERR "s5h1420: avoided division by 0\n"); +- tmp = 1; +- } +- tmp = state->fclk / tmp; +- +- +- /* set the MPEG_CLK_INTL for the calculated data rate */ +- if (tmp < 2) +- val = 0x00; +- else if (tmp < 5) +- val = 0x01; +- else if (tmp < 9) +- val = 0x02; +- else if (tmp < 13) +- val = 0x03; +- else if (tmp < 17) +- val = 0x04; +- else if (tmp < 25) +- val = 0x05; +- else if (tmp < 33) +- val = 0x06; +- else +- val = 0x07; +- dprintk("for MPEG_CLK_INTL %d %x\n", tmp, val); +- +- s5h1420_writereg(state, FEC01, 0x18); +- s5h1420_writereg(state, FEC01, 0x10); +- s5h1420_writereg(state, FEC01, val); +- +- /* Enable "MPEG_Out" */ +- val = s5h1420_readreg(state, Mpeg02); +- s5h1420_writereg(state, Mpeg02, val | (1 << 6)); +- +- /* kicker disable */ +- val = s5h1420_readreg(state, QPSK01) & 0x7f; +- s5h1420_writereg(state, QPSK01, val); +- +- /* DC freeze TODO it was never activated by default or it can stay activated */ +- +- if (s5h1420_getsymbolrate(state) >= 20000000) { +- s5h1420_writereg(state, Loop04, 0x8a); +- s5h1420_writereg(state, Loop05, 0x6a); +- } else { +- s5h1420_writereg(state, Loop04, 0x58); +- s5h1420_writereg(state, Loop05, 0x27); +- } +- +- /* post-lock processing has been done! */ +- state->postlocked = 1; +- } +- +- dprintk("leave %s\n", __func__); +- +- return 0; +-} +- +-static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- s5h1420_writereg(state, 0x46, 0x1d); +- mdelay(25); +- +- *ber = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); +- +- return 0; +-} +- +-static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- u8 val = s5h1420_readreg(state, 0x15); +- +- *strength = (u16) ((val << 8) | val); +- +- return 0; +-} +- +-static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- s5h1420_writereg(state, 0x46, 0x1f); +- mdelay(25); +- +- *ucblocks = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47); +- +- return 0; +-} +- +-static void s5h1420_reset(struct s5h1420_state* state) +-{ +- dprintk("%s\n", __func__); +- s5h1420_writereg (state, 0x01, 0x08); +- s5h1420_writereg (state, 0x01, 0x00); +- udelay(10); +-} +- +-static void s5h1420_setsymbolrate(struct s5h1420_state* state, +- struct dtv_frontend_properties *p) +-{ +- u8 v; +- u64 val; +- +- dprintk("enter %s\n", __func__); +- +- val = ((u64) p->symbol_rate / 1000ULL) * (1ULL<<24); +- if (p->symbol_rate < 29000000) +- val *= 2; +- do_div(val, (state->fclk / 1000)); +- +- dprintk("symbol rate register: %06llx\n", (unsigned long long)val); +- +- v = s5h1420_readreg(state, Loop01); +- s5h1420_writereg(state, Loop01, v & 0x7f); +- s5h1420_writereg(state, Tnco01, val >> 16); +- s5h1420_writereg(state, Tnco02, val >> 8); +- s5h1420_writereg(state, Tnco03, val & 0xff); +- s5h1420_writereg(state, Loop01, v | 0x80); +- dprintk("leave %s\n", __func__); +-} +- +-static u32 s5h1420_getsymbolrate(struct s5h1420_state* state) +-{ +- return state->symbol_rate; +-} +- +-static void s5h1420_setfreqoffset(struct s5h1420_state* state, int freqoffset) +-{ +- int val; +- u8 v; +- +- dprintk("enter %s\n", __func__); +- +- /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so +- * divide fclk by 1000000 to get the correct value. */ +- val = -(int) ((freqoffset * (1<<24)) / (state->fclk / 1000000)); +- +- dprintk("phase rotator/freqoffset: %d %06x\n", freqoffset, val); +- +- v = s5h1420_readreg(state, Loop01); +- s5h1420_writereg(state, Loop01, v & 0xbf); +- s5h1420_writereg(state, Pnco01, val >> 16); +- s5h1420_writereg(state, Pnco02, val >> 8); +- s5h1420_writereg(state, Pnco03, val & 0xff); +- s5h1420_writereg(state, Loop01, v | 0x40); +- dprintk("leave %s\n", __func__); +-} +- +-static int s5h1420_getfreqoffset(struct s5h1420_state* state) +-{ +- int val; +- +- s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) | 0x08); +- val = s5h1420_readreg(state, 0x0e) << 16; +- val |= s5h1420_readreg(state, 0x0f) << 8; +- val |= s5h1420_readreg(state, 0x10); +- s5h1420_writereg(state, 0x06, s5h1420_readreg(state, 0x06) & 0xf7); +- +- if (val & 0x800000) +- val |= 0xff000000; +- +- /* remember freqoffset is in kHz, but the chip wants the offset in Hz, so +- * divide fclk by 1000000 to get the correct value. */ +- val = (((-val) * (state->fclk/1000000)) / (1<<24)); +- +- return val; +-} +- +-static void s5h1420_setfec_inversion(struct s5h1420_state* state, +- struct dtv_frontend_properties *p) +-{ +- u8 inversion = 0; +- u8 vit08, vit09; +- +- dprintk("enter %s\n", __func__); +- +- if (p->inversion == INVERSION_OFF) +- inversion = state->config->invert ? 0x08 : 0; +- else if (p->inversion == INVERSION_ON) +- inversion = state->config->invert ? 0 : 0x08; +- +- if ((p->fec_inner == FEC_AUTO) || (p->inversion == INVERSION_AUTO)) { +- vit08 = 0x3f; +- vit09 = 0; +- } else { +- switch (p->fec_inner) { +- case FEC_1_2: +- vit08 = 0x01; vit09 = 0x10; +- break; +- +- case FEC_2_3: +- vit08 = 0x02; vit09 = 0x11; +- break; +- +- case FEC_3_4: +- vit08 = 0x04; vit09 = 0x12; +- break; +- +- case FEC_5_6: +- vit08 = 0x08; vit09 = 0x13; +- break; +- +- case FEC_6_7: +- vit08 = 0x10; vit09 = 0x14; +- break; +- +- case FEC_7_8: +- vit08 = 0x20; vit09 = 0x15; +- break; +- +- default: +- return; +- } +- } +- vit09 |= inversion; +- dprintk("fec: %02x %02x\n", vit08, vit09); +- s5h1420_writereg(state, Vit08, vit08); +- s5h1420_writereg(state, Vit09, vit09); +- dprintk("leave %s\n", __func__); +-} +- +-static fe_code_rate_t s5h1420_getfec(struct s5h1420_state* state) +-{ +- switch(s5h1420_readreg(state, 0x32) & 0x07) { +- case 0: +- return FEC_1_2; +- +- case 1: +- return FEC_2_3; +- +- case 2: +- return FEC_3_4; +- +- case 3: +- return FEC_5_6; +- +- case 4: +- return FEC_6_7; +- +- case 5: +- return FEC_7_8; +- } +- +- return FEC_NONE; +-} +- +-static fe_spectral_inversion_t s5h1420_getinversion(struct s5h1420_state* state) +-{ +- if (s5h1420_readreg(state, 0x32) & 0x08) +- return INVERSION_ON; +- +- return INVERSION_OFF; +-} +- +-static int s5h1420_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1420_state* state = fe->demodulator_priv; +- int frequency_delta; +- struct dvb_frontend_tune_settings fesettings; +- uint8_t clock_setting; +- +- dprintk("enter %s\n", __func__); +- +- /* check if we should do a fast-tune */ +- s5h1420_get_tune_settings(fe, &fesettings); +- frequency_delta = p->frequency - state->tunedfreq; +- if ((frequency_delta > -fesettings.max_drift) && +- (frequency_delta < fesettings.max_drift) && +- (frequency_delta != 0) && +- (state->fec_inner == p->fec_inner) && +- (state->symbol_rate == p->symbol_rate)) { +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (fe->ops.tuner_ops.get_frequency) { +- u32 tmp; +- fe->ops.tuner_ops.get_frequency(fe, &tmp); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- s5h1420_setfreqoffset(state, p->frequency - tmp); +- } else { +- s5h1420_setfreqoffset(state, 0); +- } +- dprintk("simple tune\n"); +- return 0; +- } +- dprintk("tuning demod\n"); +- +- /* first of all, software reset */ +- s5h1420_reset(state); +- +- /* set s5h1420 fclk PLL according to desired symbol rate */ +- if (p->symbol_rate > 33000000) +- state->fclk = 80000000; +- else if (p->symbol_rate > 28500000) +- state->fclk = 59000000; +- else if (p->symbol_rate > 25000000) +- state->fclk = 86000000; +- else if (p->symbol_rate > 1900000) +- state->fclk = 88000000; +- else +- state->fclk = 44000000; +- +- /* Clock */ +- switch (state->fclk) { +- default: +- case 88000000: +- clock_setting = 80; +- break; +- case 86000000: +- clock_setting = 78; +- break; +- case 80000000: +- clock_setting = 72; +- break; +- case 59000000: +- clock_setting = 51; +- break; +- case 44000000: +- clock_setting = 36; +- break; +- } +- dprintk("pll01: %d, ToneFreq: %d\n", state->fclk/1000000 - 8, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); +- s5h1420_writereg(state, PLL01, state->fclk/1000000 - 8); +- s5h1420_writereg(state, PLL02, 0x40); +- s5h1420_writereg(state, DiS01, (state->fclk + (TONE_FREQ * 32) - 1) / (TONE_FREQ * 32)); +- +- /* TODO DC offset removal, config parameter ? */ +- if (p->symbol_rate > 29000000) +- s5h1420_writereg(state, QPSK01, 0xae | 0x10); +- else +- s5h1420_writereg(state, QPSK01, 0xac | 0x10); +- +- /* set misc registers */ +- s5h1420_writereg(state, CON_1, 0x00); +- s5h1420_writereg(state, QPSK02, 0x00); +- s5h1420_writereg(state, Pre01, 0xb0); +- +- s5h1420_writereg(state, Loop01, 0xF0); +- s5h1420_writereg(state, Loop02, 0x2a); /* e7 for s5h1420 */ +- s5h1420_writereg(state, Loop03, 0x79); /* 78 for s5h1420 */ +- if (p->symbol_rate > 20000000) +- s5h1420_writereg(state, Loop04, 0x79); +- else +- s5h1420_writereg(state, Loop04, 0x58); +- s5h1420_writereg(state, Loop05, 0x6b); +- +- if (p->symbol_rate >= 8000000) +- s5h1420_writereg(state, Post01, (0 << 6) | 0x10); +- else if (p->symbol_rate >= 4000000) +- s5h1420_writereg(state, Post01, (1 << 6) | 0x10); +- else +- s5h1420_writereg(state, Post01, (3 << 6) | 0x10); +- +- s5h1420_writereg(state, Monitor12, 0x00); /* unfreeze DC compensation */ +- +- s5h1420_writereg(state, Sync01, 0x33); +- s5h1420_writereg(state, Mpeg01, state->config->cdclk_polarity); +- s5h1420_writereg(state, Mpeg02, 0x3d); /* Parallel output more, disabled -> enabled later */ +- s5h1420_writereg(state, Err01, 0x03); /* 0x1d for s5h1420 */ +- +- s5h1420_writereg(state, Vit06, 0x6e); /* 0x8e for s5h1420 */ +- s5h1420_writereg(state, DiS03, 0x00); +- s5h1420_writereg(state, Rf01, 0x61); /* Tuner i2c address - for the gate controller */ +- +- /* set tuner PLL */ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- s5h1420_setfreqoffset(state, 0); +- } +- +- /* set the reset of the parameters */ +- s5h1420_setsymbolrate(state, p); +- s5h1420_setfec_inversion(state, p); +- +- /* start QPSK */ +- s5h1420_writereg(state, QPSK01, s5h1420_readreg(state, QPSK01) | 1); +- +- state->fec_inner = p->fec_inner; +- state->symbol_rate = p->symbol_rate; +- state->postlocked = 0; +- state->tunedfreq = p->frequency; +- +- dprintk("leave %s\n", __func__); +- return 0; +-} +- +-static int s5h1420_get_frontend(struct dvb_frontend* fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s5h1420_state* state = fe->demodulator_priv; +- +- p->frequency = state->tunedfreq + s5h1420_getfreqoffset(state); +- p->inversion = s5h1420_getinversion(state); +- p->symbol_rate = s5h1420_getsymbolrate(state); +- p->fec_inner = s5h1420_getfec(state); +- +- return 0; +-} +- +-static int s5h1420_get_tune_settings(struct dvb_frontend* fe, +- struct dvb_frontend_tune_settings* fesettings) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- if (p->symbol_rate > 20000000) { +- fesettings->min_delay_ms = 50; +- fesettings->step_size = 2000; +- fesettings->max_drift = 8000; +- } else if (p->symbol_rate > 12000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 1500; +- fesettings->max_drift = 9000; +- } else if (p->symbol_rate > 8000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 1000; +- fesettings->max_drift = 8000; +- } else if (p->symbol_rate > 4000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 500; +- fesettings->max_drift = 7000; +- } else if (p->symbol_rate > 2000000) { +- fesettings->min_delay_ms = 200; +- fesettings->step_size = (p->symbol_rate / 8000); +- fesettings->max_drift = 14 * fesettings->step_size; +- } else { +- fesettings->min_delay_ms = 200; +- fesettings->step_size = (p->symbol_rate / 8000); +- fesettings->max_drift = 18 * fesettings->step_size; +- } +- +- return 0; +-} +- +-static int s5h1420_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- if (enable) +- return s5h1420_writereg(state, 0x02, state->CON_1_val | 1); +- else +- return s5h1420_writereg(state, 0x02, state->CON_1_val & 0xfe); +-} +- +-static int s5h1420_init (struct dvb_frontend* fe) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- +- /* disable power down and do reset */ +- state->CON_1_val = state->config->serial_mpeg << 4; +- s5h1420_writereg(state, 0x02, state->CON_1_val); +- msleep(10); +- s5h1420_reset(state); +- +- return 0; +-} +- +-static int s5h1420_sleep(struct dvb_frontend* fe) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- state->CON_1_val = 0x12; +- return s5h1420_writereg(state, 0x02, state->CON_1_val); +-} +- +-static void s5h1420_release(struct dvb_frontend* fe) +-{ +- struct s5h1420_state* state = fe->demodulator_priv; +- i2c_del_adapter(&state->tuner_i2c_adapter); +- kfree(state); +-} +- +-static u32 s5h1420_tuner_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +-static int s5h1420_tuner_i2c_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num) +-{ +- struct s5h1420_state *state = i2c_get_adapdata(i2c_adap); +- struct i2c_msg m[1 + num]; +- u8 tx_open[2] = { CON_1, state->CON_1_val | 1 }; /* repeater stops once there was a stop condition */ +- +- memset(m, 0, sizeof(struct i2c_msg) * (1 + num)); +- +- m[0].addr = state->config->demod_address; +- m[0].buf = tx_open; +- m[0].len = 2; +- +- memcpy(&m[1], msg, sizeof(struct i2c_msg) * num); +- +- return i2c_transfer(state->i2c, m, 1+num) == 1 + num ? num : -EIO; +-} +- +-static struct i2c_algorithm s5h1420_tuner_i2c_algo = { +- .master_xfer = s5h1420_tuner_i2c_tuner_xfer, +- .functionality = s5h1420_tuner_i2c_func, +-}; +- +-struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- struct s5h1420_state *state = fe->demodulator_priv; +- return &state->tuner_i2c_adapter; +-} +-EXPORT_SYMBOL(s5h1420_get_tuner_i2c_adapter); +- +-static struct dvb_frontend_ops s5h1420_ops; +- +-struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, +- struct i2c_adapter *i2c) +-{ +- /* allocate memory for the internal state */ +- struct s5h1420_state *state = kzalloc(sizeof(struct s5h1420_state), GFP_KERNEL); +- u8 i; +- +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->postlocked = 0; +- state->fclk = 88000000; +- state->tunedfreq = 0; +- state->fec_inner = FEC_NONE; +- state->symbol_rate = 0; +- +- /* check if the demod is there + identify it */ +- i = s5h1420_readreg(state, ID01); +- if (i != 0x03) +- goto error; +- +- memset(state->shadow, 0xff, sizeof(state->shadow)); +- +- for (i = 0; i < 0x50; i++) +- state->shadow[i] = s5h1420_readreg(state, i); +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &s5h1420_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- /* create tuner i2c adapter */ +- strlcpy(state->tuner_i2c_adapter.name, "S5H1420-PN1010 tuner I2C bus", +- sizeof(state->tuner_i2c_adapter.name)); +- state->tuner_i2c_adapter.algo = &s5h1420_tuner_i2c_algo; +- state->tuner_i2c_adapter.algo_data = NULL; +- i2c_set_adapdata(&state->tuner_i2c_adapter, state); +- if (i2c_add_adapter(&state->tuner_i2c_adapter) < 0) { +- printk(KERN_ERR "S5H1420/PN1010: tuner i2c bus could not be initialized\n"); +- goto error; +- } +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(s5h1420_attach); +- +-static struct dvb_frontend_ops s5h1420_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Samsung S5H1420/PnpNetwork PN1010 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- .frequency_tolerance = 29500, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- /* .symbol_rate_tolerance = ???,*/ +- .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_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK +- }, +- +- .release = s5h1420_release, +- +- .init = s5h1420_init, +- .sleep = s5h1420_sleep, +- .i2c_gate_ctrl = s5h1420_i2c_gate_ctrl, +- +- .set_frontend = s5h1420_set_frontend, +- .get_frontend = s5h1420_get_frontend, +- .get_tune_settings = s5h1420_get_tune_settings, +- +- .read_status = s5h1420_read_status, +- .read_ber = s5h1420_read_ber, +- .read_signal_strength = s5h1420_read_signal_strength, +- .read_ucblocks = s5h1420_read_ucblocks, +- +- .diseqc_send_master_cmd = s5h1420_send_master_cmd, +- .diseqc_recv_slave_reply = s5h1420_recv_slave_reply, +- .diseqc_send_burst = s5h1420_send_burst, +- .set_tone = s5h1420_set_tone, +- .set_voltage = s5h1420_set_voltage, +-}; +- +-MODULE_DESCRIPTION("Samsung S5H1420/PnpNetwork PN1010 DVB-S Demodulator driver"); +-MODULE_AUTHOR("Andrew de Quincey, Patrick Boettcher"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb/frontends/s5h1420.h +deleted file mode 100644 +index ff30813..0000000 +--- a/drivers/media/dvb/frontends/s5h1420.h ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- * Driver for +- * Samsung S5H1420 and +- * PnpNetwork PN1010 QPSK Demodulator +- * +- * Copyright (C) 2005 Andrew de Quincey +- * Copyright (C) 2005-8 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +-#ifndef S5H1420_H +-#define S5H1420_H +- +-#include +- +-struct s5h1420_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* does the inversion require inversion? */ +- u8 invert:1; +- +- u8 repeated_start_workaround:1; +- u8 cdclk_polarity:1; /* 1 == falling edge, 0 == raising edge */ +- +- u8 serial_mpeg:1; +-}; +- +-#if defined(CONFIG_DVB_S5H1420) || (defined(CONFIG_DVB_S5H1420_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, +- struct i2c_adapter *i2c); +-extern struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe); +-#else +-static inline struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline struct i2c_adapter *s5h1420_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- return NULL; +-} +-#endif // CONFIG_DVB_S5H1420 +- +-#endif // S5H1420_H +diff --git a/drivers/media/dvb/frontends/s5h1420_priv.h b/drivers/media/dvb/frontends/s5h1420_priv.h +deleted file mode 100644 +index d9c58d2..0000000 +--- a/drivers/media/dvb/frontends/s5h1420_priv.h ++++ /dev/null +@@ -1,102 +0,0 @@ +-/* +- * Driver for +- * Samsung S5H1420 and +- * PnpNetwork PN1010 QPSK Demodulator +- * +- * Copyright (C) 2005 Andrew de Quincey +- * Copyright (C) 2005 Patrick Boettcher +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License as published by the Free +- * Software Foundation; either version 2 of the License, or (at your option) +- * any later version. +- * +- * This program is distributed in the hope that it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along with +- * this program; if not, write to the Free Software Foundation, Inc., 675 Mass +- * Ave, Cambridge, MA 02139, USA. +- */ +-#ifndef S5H1420_PRIV +-#define S5H1420_PRIV +- +-#include +- +-enum s5h1420_register { +- ID01 = 0x00, +- CON_0 = 0x01, +- CON_1 = 0x02, +- PLL01 = 0x03, +- PLL02 = 0x04, +- QPSK01 = 0x05, +- QPSK02 = 0x06, +- Pre01 = 0x07, +- Post01 = 0x08, +- Loop01 = 0x09, +- Loop02 = 0x0a, +- Loop03 = 0x0b, +- Loop04 = 0x0c, +- Loop05 = 0x0d, +- Pnco01 = 0x0e, +- Pnco02 = 0x0f, +- Pnco03 = 0x10, +- Tnco01 = 0x11, +- Tnco02 = 0x12, +- Tnco03 = 0x13, +- Monitor01 = 0x14, +- Monitor02 = 0x15, +- Monitor03 = 0x16, +- Monitor04 = 0x17, +- Monitor05 = 0x18, +- Monitor06 = 0x19, +- Monitor07 = 0x1a, +- Monitor12 = 0x1f, +- +- FEC01 = 0x22, +- Soft01 = 0x23, +- Soft02 = 0x24, +- Soft03 = 0x25, +- Soft04 = 0x26, +- Soft05 = 0x27, +- Soft06 = 0x28, +- Vit01 = 0x29, +- Vit02 = 0x2a, +- Vit03 = 0x2b, +- Vit04 = 0x2c, +- Vit05 = 0x2d, +- Vit06 = 0x2e, +- Vit07 = 0x2f, +- Vit08 = 0x30, +- Vit09 = 0x31, +- Vit10 = 0x32, +- Vit11 = 0x33, +- Vit12 = 0x34, +- Sync01 = 0x35, +- Sync02 = 0x36, +- Rs01 = 0x37, +- Mpeg01 = 0x38, +- Mpeg02 = 0x39, +- DiS01 = 0x3a, +- DiS02 = 0x3b, +- DiS03 = 0x3c, +- DiS04 = 0x3d, +- DiS05 = 0x3e, +- DiS06 = 0x3f, +- DiS07 = 0x40, +- DiS08 = 0x41, +- DiS09 = 0x42, +- DiS10 = 0x43, +- DiS11 = 0x44, +- Rf01 = 0x45, +- Err01 = 0x46, +- Err02 = 0x47, +- Err03 = 0x48, +- Err04 = 0x49, +-}; +- +- +-#endif +diff --git a/drivers/media/dvb/frontends/s5h1432.c b/drivers/media/dvb/frontends/s5h1432.c +deleted file mode 100644 +index 8352ce1..0000000 +--- a/drivers/media/dvb/frontends/s5h1432.c ++++ /dev/null +@@ -1,407 +0,0 @@ +-/* +- * Samsung s5h1432 DVB-T demodulator driver +- * +- * Copyright (C) 2009 Bill Liu +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "s5h1432.h" +- +-struct s5h1432_state { +- +- struct i2c_adapter *i2c; +- +- /* configuration settings */ +- const struct s5h1432_config *config; +- +- struct dvb_frontend frontend; +- +- fe_modulation_t current_modulation; +- unsigned int first_tune:1; +- +- u32 current_frequency; +- int if_freq; +- +- u8 inversion; +-}; +- +-static int debug; +- +-#define dprintk(arg...) do { \ +- if (debug) \ +- printk(arg); \ +- } while (0) +- +-static int s5h1432_writereg(struct s5h1432_state *state, +- u8 addr, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- +- struct i2c_msg msg = {.addr = addr, .flags = 0, .buf = buf, .len = 2 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk(KERN_ERR "%s: writereg error 0x%02x 0x%02x 0x%04x, " +- "ret == %i)\n", __func__, addr, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static u8 s5h1432_readreg(struct s5h1432_state *state, u8 addr, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- +- struct i2c_msg msg[] = { +- {.addr = addr, .flags = 0, .buf = b0, .len = 1}, +- {.addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 1} +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk(KERN_ERR "%s: readreg error (ret == %i)\n", +- __func__, ret); +- return b1[0]; +-} +- +-static int s5h1432_sleep(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int s5h1432_set_channel_bandwidth(struct dvb_frontend *fe, +- u32 bandwidth) +-{ +- struct s5h1432_state *state = fe->demodulator_priv; +- +- u8 reg = 0; +- +- /* Register [0x2E] bit 3:2 : 8MHz = 0; 7MHz = 1; 6MHz = 2 */ +- reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x2E); +- reg &= ~(0x0C); +- switch (bandwidth) { +- case 6: +- reg |= 0x08; +- break; +- case 7: +- reg |= 0x04; +- break; +- case 8: +- reg |= 0x00; +- break; +- default: +- return 0; +- } +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2E, reg); +- return 1; +-} +- +-static int s5h1432_set_IF(struct dvb_frontend *fe, u32 ifFreqHz) +-{ +- struct s5h1432_state *state = fe->demodulator_priv; +- +- switch (ifFreqHz) { +- case TAIWAN_HI_IF_FREQ_44_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x15); +- break; +- case EUROPE_HI_IF_FREQ_36_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0x40); +- break; +- case IF_FREQ_6_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x00); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x00); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xe0); +- break; +- case IF_FREQ_3point3_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); +- break; +- case IF_FREQ_3point5_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x55); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x55); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xED); +- break; +- case IF_FREQ_4_MHZ: +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0xAA); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0xAA); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEA); +- break; +- default: +- { +- u32 value = 0; +- value = (u32) (((48000 - (ifFreqHz / 1000)) * 512 * +- (u32) 32768) / (48 * 1000)); +- printk(KERN_INFO +- "Default IFFreq %d :reg value = 0x%x\n", +- ifFreqHz, value); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, +- (u8) value & 0xFF); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, +- (u8) (value >> 8) & 0xFF); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, +- (u8) (value >> 16) & 0xFF); +- break; +- } +- +- } +- +- return 1; +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-static int s5h1432_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- u32 dvb_bandwidth = 8; +- struct s5h1432_state *state = fe->demodulator_priv; +- +- if (p->frequency == state->current_frequency) { +- /*current_frequency = p->frequency; */ +- /*state->current_frequency = p->frequency; */ +- } else { +- fe->ops.tuner_ops.set_params(fe); +- msleep(300); +- s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); +- switch (p->bandwidth_hz) { +- case 6000000: +- dvb_bandwidth = 6; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- case 7000000: +- dvb_bandwidth = 7; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- case 8000000: +- dvb_bandwidth = 8; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- default: +- return 0; +- } +- /*fe->ops.tuner_ops.set_params(fe); */ +-/*Soft Reset chip*/ +- msleep(30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); +- msleep(30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); +- +- s5h1432_set_channel_bandwidth(fe, dvb_bandwidth); +- switch (p->bandwidth_hz) { +- case 6000000: +- dvb_bandwidth = 6; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- case 7000000: +- dvb_bandwidth = 7; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- case 8000000: +- dvb_bandwidth = 8; +- s5h1432_set_IF(fe, IF_FREQ_4_MHZ); +- break; +- default: +- return 0; +- } +- /*fe->ops.tuner_ops.set_params(fe); */ +- /*Soft Reset chip*/ +- msleep(30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); +- msleep(30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); +- +- } +- +- state->current_frequency = p->frequency; +- +- return 0; +-} +- +-static int s5h1432_init(struct dvb_frontend *fe) +-{ +- struct s5h1432_state *state = fe->demodulator_priv; +- +- u8 reg = 0; +- state->current_frequency = 0; +- printk(KERN_INFO " s5h1432_init().\n"); +- +- /*Set VSB mode as default, this also does a soft reset */ +- /*Initialize registers */ +- +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x04, 0xa8); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x05, 0x01); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x07, 0x70); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x19, 0x80); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1b, 0x9D); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1c, 0x30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1d, 0x20); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x1B); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x2e, 0x40); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, 0x84); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x50, 0x5a); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x5a, 0xd3); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x68, 0x50); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xb8, 0x3c); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xc4, 0x10); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xcc, 0x9c); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xDA, 0x00); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe1, 0x94); +- /* s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf4, 0xa1); */ +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xf9, 0x00); +- +- /*For NXP tuner*/ +- +- /*Set 3.3MHz as default IF frequency */ +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe4, 0x66); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe5, 0x66); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0xe7, 0xEE); +- /* Set reg 0x1E to get the full dynamic range */ +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x1e, 0x31); +- +- /* Mode setting in demod */ +- reg = s5h1432_readreg(state, S5H1432_I2C_TOP_ADDR, 0x42); +- reg |= 0x80; +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x42, reg); +- /* Serial mode */ +- +- /* Soft Reset chip */ +- +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1a); +- msleep(30); +- s5h1432_writereg(state, S5H1432_I2C_TOP_ADDR, 0x09, 0x1b); +- +- +- return 0; +-} +- +-static int s5h1432_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- return 0; +-} +- +-static int s5h1432_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- return 0; +-} +- +-static int s5h1432_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- return 0; +-} +- +-static int s5h1432_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- +- return 0; +-} +- +-static int s5h1432_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- return 0; +-} +- +-static int s5h1432_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- return 0; +-} +- +-static void s5h1432_release(struct dvb_frontend *fe) +-{ +- struct s5h1432_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops s5h1432_ops; +- +-struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, +- struct i2c_adapter *i2c) +-{ +- struct s5h1432_state *state = NULL; +- +- printk(KERN_INFO " Enter s5h1432_attach(). attach success!\n"); +- /* allocate memory for the internal state */ +- state = kmalloc(sizeof(struct s5h1432_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->current_modulation = QAM_16; +- state->inversion = state->config->inversion; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &s5h1432_ops, +- sizeof(struct dvb_frontend_ops)); +- +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(s5h1432_attach); +- +-static struct dvb_frontend_ops s5h1432_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Samsung s5h1432 DVB-T Frontend", +- .frequency_min = 177000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER}, +- +- .init = s5h1432_init, +- .sleep = s5h1432_sleep, +- .set_frontend = s5h1432_set_frontend, +- .get_tune_settings = s5h1432_get_tune_settings, +- .read_status = s5h1432_read_status, +- .read_ber = s5h1432_read_ber, +- .read_signal_strength = s5h1432_read_signal_strength, +- .read_snr = s5h1432_read_snr, +- .read_ucblocks = s5h1432_read_ucblocks, +- .release = s5h1432_release, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-MODULE_DESCRIPTION("Samsung s5h1432 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Bill Liu"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/s5h1432.h b/drivers/media/dvb/frontends/s5h1432.h +deleted file mode 100644 +index b57438c..0000000 +--- a/drivers/media/dvb/frontends/s5h1432.h ++++ /dev/null +@@ -1,91 +0,0 @@ +-/* +- * Samsung s5h1432 VSB/QAM demodulator driver +- * +- * Copyright (C) 2009 Bill Liu +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef __S5H1432_H__ +-#define __S5H1432_H__ +- +-#include +- +-#define S5H1432_I2C_TOP_ADDR (0x02 >> 1) +- +-#define TAIWAN_HI_IF_FREQ_44_MHZ 44000000 +-#define EUROPE_HI_IF_FREQ_36_MHZ 36000000 +-#define IF_FREQ_6_MHZ 6000000 +-#define IF_FREQ_3point3_MHZ 3300000 +-#define IF_FREQ_3point5_MHZ 3500000 +-#define IF_FREQ_4_MHZ 4000000 +- +-struct s5h1432_config { +- +- /* serial/parallel output */ +-#define S5H1432_PARALLEL_OUTPUT 0 +-#define S5H1432_SERIAL_OUTPUT 1 +- u8 output_mode; +- +- /* GPIO Setting */ +-#define S5H1432_GPIO_OFF 0 +-#define S5H1432_GPIO_ON 1 +- u8 gpio; +- +- /* MPEG signal timing */ +-#define S5H1432_MPEGTIMING_CONTINOUS_INVERTING_CLOCK 0 +-#define S5H1432_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK 1 +-#define S5H1432_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 +-#define S5H1432_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 +- u16 mpeg_timing; +- +- /* IF Freq for QAM and VSB in KHz */ +-#define S5H1432_IF_3250 3250 +-#define S5H1432_IF_3500 3500 +-#define S5H1432_IF_4000 4000 +-#define S5H1432_IF_5380 5380 +-#define S5H1432_IF_44000 44000 +-#define S5H1432_VSB_IF_DEFAULT s5h1432_IF_44000 +-#define S5H1432_QAM_IF_DEFAULT s5h1432_IF_44000 +- u16 qam_if; +- u16 vsb_if; +- +- /* Spectral Inversion */ +-#define S5H1432_INVERSION_OFF 0 +-#define S5H1432_INVERSION_ON 1 +- u8 inversion; +- +- /* Return lock status based on tuner lock, or demod lock */ +-#define S5H1432_TUNERLOCKING 0 +-#define S5H1432_DEMODLOCKING 1 +- u8 status_mode; +-}; +- +-#if defined(CONFIG_DVB_S5H1432) || \ +- (defined(CONFIG_DVB_S5H1432_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *s5h1432_attach(const struct s5h1432_config +- *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_s5h1432 */ +- +-#endif /* __s5h1432_H__ */ +diff --git a/drivers/media/dvb/frontends/s921.c b/drivers/media/dvb/frontends/s921.c +deleted file mode 100644 +index cd2288c..0000000 +--- a/drivers/media/dvb/frontends/s921.c ++++ /dev/null +@@ -1,549 +0,0 @@ +-/* +- * Sharp VA3A5JZ921 One Seg Broadcast Module driver +- * This device is labeled as just S. 921 at the top of the frontend can +- * +- * Copyright (C) 2009-2010 Mauro Carvalho Chehab +- * Copyright (C) 2009-2010 Douglas Landgraf +- * +- * Developed for Leadership SBTVD 1seg device sold in Brazil +- * +- * Frontend module based on cx24123 driver, getting some info from +- * the old s921 driver. +- * +- * FIXME: Need to port to DVB v5.2 API +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- */ +- +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "s921.h" +- +-static int debug = 1; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); +- +-#define rc(args...) do { \ +- printk(KERN_ERR "s921: " args); \ +-} while (0) +- +-#define dprintk(args...) \ +- do { \ +- if (debug) { \ +- printk(KERN_DEBUG "s921: %s: ", __func__); \ +- printk(args); \ +- } \ +- } while (0) +- +-struct s921_state { +- struct i2c_adapter *i2c; +- const struct s921_config *config; +- +- struct dvb_frontend frontend; +- +- /* The Demod can't easily provide these, we cache them */ +- u32 currentfreq; +-}; +- +-/* +- * Various tuner defaults need to be established for a given frequency kHz. +- * fixme: The bounds on the bands do not match the doc in real life. +- * fixme: Some of them have been moved, other might need adjustment. +- */ +-static struct s921_bandselect_val { +- u32 freq_low; +- u8 band_reg; +-} s921_bandselect[] = { +- { 0, 0x7b }, +- { 485140000, 0x5b }, +- { 515140000, 0x3b }, +- { 545140000, 0x1b }, +- { 599140000, 0xfb }, +- { 623140000, 0xdb }, +- { 659140000, 0xbb }, +- { 713140000, 0x9b }, +-}; +- +-struct regdata { +- u8 reg; +- u8 data; +-}; +- +-static struct regdata s921_init[] = { +- { 0x01, 0x80 }, /* Probably, a reset sequence */ +- { 0x01, 0x40 }, +- { 0x01, 0x80 }, +- { 0x01, 0x40 }, +- +- { 0x02, 0x00 }, +- { 0x03, 0x40 }, +- { 0x04, 0x01 }, +- { 0x05, 0x00 }, +- { 0x06, 0x00 }, +- { 0x07, 0x00 }, +- { 0x08, 0x00 }, +- { 0x09, 0x00 }, +- { 0x0a, 0x00 }, +- { 0x0b, 0x5a }, +- { 0x0c, 0x00 }, +- { 0x0d, 0x00 }, +- { 0x0f, 0x00 }, +- { 0x13, 0x1b }, +- { 0x14, 0x80 }, +- { 0x15, 0x40 }, +- { 0x17, 0x70 }, +- { 0x18, 0x01 }, +- { 0x19, 0x12 }, +- { 0x1a, 0x01 }, +- { 0x1b, 0x12 }, +- { 0x1c, 0xa0 }, +- { 0x1d, 0x00 }, +- { 0x1e, 0x0a }, +- { 0x1f, 0x08 }, +- { 0x20, 0x40 }, +- { 0x21, 0xff }, +- { 0x22, 0x4c }, +- { 0x23, 0x4e }, +- { 0x24, 0x4c }, +- { 0x25, 0x00 }, +- { 0x26, 0x00 }, +- { 0x27, 0xf4 }, +- { 0x28, 0x60 }, +- { 0x29, 0x88 }, +- { 0x2a, 0x40 }, +- { 0x2b, 0x40 }, +- { 0x2c, 0xff }, +- { 0x2d, 0x00 }, +- { 0x2e, 0xff }, +- { 0x2f, 0x00 }, +- { 0x30, 0x20 }, +- { 0x31, 0x06 }, +- { 0x32, 0x0c }, +- { 0x34, 0x0f }, +- { 0x37, 0xfe }, +- { 0x38, 0x00 }, +- { 0x39, 0x63 }, +- { 0x3a, 0x10 }, +- { 0x3b, 0x10 }, +- { 0x47, 0x00 }, +- { 0x49, 0xe5 }, +- { 0x4b, 0x00 }, +- { 0x50, 0xc0 }, +- { 0x52, 0x20 }, +- { 0x54, 0x5a }, +- { 0x55, 0x5b }, +- { 0x56, 0x40 }, +- { 0x57, 0x70 }, +- { 0x5c, 0x50 }, +- { 0x5d, 0x00 }, +- { 0x62, 0x17 }, +- { 0x63, 0x2f }, +- { 0x64, 0x6f }, +- { 0x68, 0x00 }, +- { 0x69, 0x89 }, +- { 0x6a, 0x00 }, +- { 0x6b, 0x00 }, +- { 0x6c, 0x00 }, +- { 0x6d, 0x00 }, +- { 0x6e, 0x00 }, +- { 0x70, 0x10 }, +- { 0x71, 0x00 }, +- { 0x75, 0x00 }, +- { 0x76, 0x30 }, +- { 0x77, 0x01 }, +- { 0xaf, 0x00 }, +- { 0xb0, 0xa0 }, +- { 0xb2, 0x3d }, +- { 0xb3, 0x25 }, +- { 0xb4, 0x8b }, +- { 0xb5, 0x4b }, +- { 0xb6, 0x3f }, +- { 0xb7, 0xff }, +- { 0xb8, 0xff }, +- { 0xb9, 0xfc }, +- { 0xba, 0x00 }, +- { 0xbb, 0x00 }, +- { 0xbc, 0x00 }, +- { 0xd0, 0x30 }, +- { 0xe4, 0x84 }, +- { 0xf0, 0x48 }, +- { 0xf1, 0x19 }, +- { 0xf2, 0x5a }, +- { 0xf3, 0x8e }, +- { 0xf4, 0x2d }, +- { 0xf5, 0x07 }, +- { 0xf6, 0x5a }, +- { 0xf7, 0xba }, +- { 0xf8, 0xd7 }, +-}; +- +-static struct regdata s921_prefreq[] = { +- { 0x47, 0x60 }, +- { 0x68, 0x00 }, +- { 0x69, 0x89 }, +- { 0xf0, 0x48 }, +- { 0xf1, 0x19 }, +-}; +- +-static struct regdata s921_postfreq[] = { +- { 0xf5, 0xae }, +- { 0xf6, 0xb7 }, +- { 0xf7, 0xba }, +- { 0xf8, 0xd7 }, +- { 0x68, 0x0a }, +- { 0x69, 0x09 }, +-}; +- +-static int s921_i2c_writereg(struct s921_state *state, +- u8 i2c_addr, int reg, int data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = i2c_addr, .flags = 0, .buf = buf, .len = 2 +- }; +- int rc; +- +- rc = i2c_transfer(state->i2c, &msg, 1); +- if (rc != 1) { +- printk("%s: writereg rcor(rc == %i, reg == 0x%02x," +- " data == 0x%02x)\n", __func__, rc, reg, data); +- return rc; +- } +- +- return 0; +-} +- +-static int s921_i2c_writeregdata(struct s921_state *state, u8 i2c_addr, +- struct regdata *rd, int size) +-{ +- int i, rc; +- +- for (i = 0; i < size; i++) { +- rc = s921_i2c_writereg(state, i2c_addr, rd[i].reg, rd[i].data); +- if (rc < 0) +- return rc; +- } +- return 0; +-} +- +-static int s921_i2c_readreg(struct s921_state *state, u8 i2c_addr, u8 reg) +-{ +- u8 val; +- int rc; +- struct i2c_msg msg[] = { +- { .addr = i2c_addr, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = i2c_addr, .flags = I2C_M_RD, .buf = &val, .len = 1 } +- }; +- +- rc = i2c_transfer(state->i2c, msg, 2); +- +- if (rc != 2) { +- rc("%s: reg=0x%x (rcor=%d)\n", __func__, reg, rc); +- return rc; +- } +- +- return val; +-} +- +-#define s921_readreg(state, reg) \ +- s921_i2c_readreg(state, state->config->demod_address, reg) +-#define s921_writereg(state, reg, val) \ +- s921_i2c_writereg(state, state->config->demod_address, reg, val) +-#define s921_writeregdata(state, regdata) \ +- s921_i2c_writeregdata(state, state->config->demod_address, \ +- regdata, ARRAY_SIZE(regdata)) +- +-static int s921_pll_tune(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s921_state *state = fe->demodulator_priv; +- int band, rc, i; +- unsigned long f_offset; +- u8 f_switch; +- u64 offset; +- +- dprintk("frequency=%i\n", p->frequency); +- +- for (band = 0; band < ARRAY_SIZE(s921_bandselect); band++) +- if (p->frequency < s921_bandselect[band].freq_low) +- break; +- band--; +- +- if (band < 0) { +- rc("%s: frequency out of range\n", __func__); +- return -EINVAL; +- } +- +- f_switch = s921_bandselect[band].band_reg; +- +- offset = ((u64)p->frequency) * 258; +- do_div(offset, 6000000); +- f_offset = ((unsigned long)offset) + 2321; +- +- rc = s921_writeregdata(state, s921_prefreq); +- if (rc < 0) +- return rc; +- +- rc = s921_writereg(state, 0xf2, (f_offset >> 8) & 0xff); +- if (rc < 0) +- return rc; +- +- rc = s921_writereg(state, 0xf3, f_offset & 0xff); +- if (rc < 0) +- return rc; +- +- rc = s921_writereg(state, 0xf4, f_switch); +- if (rc < 0) +- return rc; +- +- rc = s921_writeregdata(state, s921_postfreq); +- if (rc < 0) +- return rc; +- +- for (i = 0 ; i < 6; i++) { +- rc = s921_readreg(state, 0x80); +- dprintk("status 0x80: %02x\n", rc); +- } +- rc = s921_writereg(state, 0x01, 0x40); +- if (rc < 0) +- return rc; +- +- rc = s921_readreg(state, 0x01); +- dprintk("status 0x01: %02x\n", rc); +- +- rc = s921_readreg(state, 0x80); +- dprintk("status 0x80: %02x\n", rc); +- +- rc = s921_readreg(state, 0x80); +- dprintk("status 0x80: %02x\n", rc); +- +- rc = s921_readreg(state, 0x32); +- dprintk("status 0x32: %02x\n", rc); +- +- dprintk("pll tune band=%d, pll=%d\n", f_switch, (int)f_offset); +- +- return 0; +-} +- +-static int s921_initfe(struct dvb_frontend *fe) +-{ +- struct s921_state *state = fe->demodulator_priv; +- int rc; +- +- dprintk("\n"); +- +- rc = s921_writeregdata(state, s921_init); +- if (rc < 0) +- return rc; +- +- return 0; +-} +- +-static int s921_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct s921_state *state = fe->demodulator_priv; +- int regstatus, rc; +- +- *status = 0; +- +- rc = s921_readreg(state, 0x81); +- if (rc < 0) +- return rc; +- +- regstatus = rc << 8; +- +- rc = s921_readreg(state, 0x82); +- if (rc < 0) +- return rc; +- +- regstatus |= rc; +- +- dprintk("status = %04x\n", regstatus); +- +- /* Full Sync - We don't know what each bit means on regs 0x81/0x82 */ +- if ((regstatus & 0xff) == 0x40) { +- *status = FE_HAS_SIGNAL | +- FE_HAS_CARRIER | +- FE_HAS_VITERBI | +- FE_HAS_SYNC | +- FE_HAS_LOCK; +- } else if (regstatus & 0x40) { +- /* This is close to Full Sync, but not enough to get useful info */ +- *status = FE_HAS_SIGNAL | +- FE_HAS_CARRIER | +- FE_HAS_VITERBI | +- FE_HAS_SYNC; +- } +- +- return 0; +-} +- +-static int s921_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- fe_status_t status; +- struct s921_state *state = fe->demodulator_priv; +- int rc; +- +- /* FIXME: Use the proper register for it... 0x80? */ +- rc = s921_read_status(fe, &status); +- if (rc < 0) +- return rc; +- +- *strength = (status & FE_HAS_LOCK) ? 0xffff : 0; +- +- dprintk("strength = 0x%04x\n", *strength); +- +- rc = s921_readreg(state, 0x01); +- dprintk("status 0x01: %02x\n", rc); +- +- rc = s921_readreg(state, 0x80); +- dprintk("status 0x80: %02x\n", rc); +- +- rc = s921_readreg(state, 0x32); +- dprintk("status 0x32: %02x\n", rc); +- +- return 0; +-} +- +-static int s921_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s921_state *state = fe->demodulator_priv; +- int rc; +- +- dprintk("\n"); +- +- /* FIXME: We don't know how to use non-auto mode */ +- +- rc = s921_pll_tune(fe); +- if (rc < 0) +- return rc; +- +- state->currentfreq = p->frequency; +- +- return 0; +-} +- +-static int s921_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct s921_state *state = fe->demodulator_priv; +- +- /* FIXME: Probably it is possible to get it from regs f1 and f2 */ +- p->frequency = state->currentfreq; +- p->delivery_system = SYS_ISDBT; +- +- return 0; +-} +- +-static int s921_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, +- unsigned int *delay, +- fe_status_t *status) +-{ +- int rc = 0; +- +- dprintk("\n"); +- +- if (re_tune) +- rc = s921_set_frontend(fe); +- +- if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) +- s921_read_status(fe, status); +- +- return rc; +-} +- +-static int s921_get_algo(struct dvb_frontend *fe) +-{ +- return 1; /* FE_ALGO_HW */ +-} +- +-static void s921_release(struct dvb_frontend *fe) +-{ +- struct s921_state *state = fe->demodulator_priv; +- +- dprintk("\n"); +- kfree(state); +-} +- +-static struct dvb_frontend_ops s921_ops; +- +-struct dvb_frontend *s921_attach(const struct s921_config *config, +- struct i2c_adapter *i2c) +-{ +- /* allocate memory for the internal state */ +- struct s921_state *state = +- kzalloc(sizeof(struct s921_state), GFP_KERNEL); +- +- dprintk("\n"); +- if (state == NULL) { +- rc("Unable to kzalloc\n"); +- goto rcor; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &s921_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +- +-rcor: +- kfree(state); +- +- return NULL; +-} +-EXPORT_SYMBOL(s921_attach); +- +-static struct dvb_frontend_ops s921_ops = { +- .delsys = { SYS_ISDBT }, +- /* Use dib8000 values per default */ +- .info = { +- .name = "Sharp S921", +- .frequency_min = 470000000, +- /* +- * Max should be 770MHz instead, according with Sharp docs, +- * but Leadership doc says it works up to 806 MHz. This is +- * required to get channel 69, used in Brazil +- */ +- .frequency_max = 806000000, +- .frequency_tolerance = 0, +- .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 = s921_release, +- +- .init = s921_initfe, +- .set_frontend = s921_set_frontend, +- .get_frontend = s921_get_frontend, +- .read_status = s921_read_status, +- .read_signal_strength = s921_read_signal_strength, +- .tune = s921_tune, +- .get_frontend_algo = s921_get_algo, +-}; +- +-MODULE_DESCRIPTION("DVB Frontend module for Sharp S921 hardware"); +-MODULE_AUTHOR("Mauro Carvalho Chehab "); +-MODULE_AUTHOR("Douglas Landgraf "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/s921.h b/drivers/media/dvb/frontends/s921.h +deleted file mode 100644 +index f220d82..0000000 +--- a/drivers/media/dvb/frontends/s921.h ++++ /dev/null +@@ -1,47 +0,0 @@ +-/* +- * Sharp s921 driver +- * +- * Copyright (C) 2009 Mauro Carvalho Chehab +- * Copyright (C) 2009 Douglas Landgraf +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation version 2. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +- * General Public License for more details. +- */ +- +-#ifndef S921_H +-#define S921_H +- +-#include +- +-struct s921_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-#if defined(CONFIG_DVB_S921) || (defined(CONFIG_DVB_S921_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *s921_attach(const struct s921_config *config, +- struct i2c_adapter *i2c); +-extern struct i2c_adapter *s921_get_tuner_i2c_adapter(struct dvb_frontend *); +-#else +-static inline struct dvb_frontend *s921_attach( +- const struct s921_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static struct i2c_adapter * +- s921_get_tuner_i2c_adapter(struct dvb_frontend *fe) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* S921_H */ +diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c +deleted file mode 100644 +index a68a648..0000000 +--- a/drivers/media/dvb/frontends/si21xx.c ++++ /dev/null +@@ -1,951 +0,0 @@ +-/* DVB compliant Linux driver for the DVB-S si2109/2110 demodulator +-* +-* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +-* +-* This program is free software; you can redistribute it and/or modify +-* it under the terms of the GNU General Public License as published by +-* the Free Software Foundation; either version 2 of the License, or +-* (at your option) any later version. +-* +-*/ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "si21xx.h" +- +-#define REVISION_REG 0x00 +-#define SYSTEM_MODE_REG 0x01 +-#define TS_CTRL_REG_1 0x02 +-#define TS_CTRL_REG_2 0x03 +-#define PIN_CTRL_REG_1 0x04 +-#define PIN_CTRL_REG_2 0x05 +-#define LOCK_STATUS_REG_1 0x0f +-#define LOCK_STATUS_REG_2 0x10 +-#define ACQ_STATUS_REG 0x11 +-#define ACQ_CTRL_REG_1 0x13 +-#define ACQ_CTRL_REG_2 0x14 +-#define PLL_DIVISOR_REG 0x15 +-#define COARSE_TUNE_REG 0x16 +-#define FINE_TUNE_REG_L 0x17 +-#define FINE_TUNE_REG_H 0x18 +- +-#define ANALOG_AGC_POWER_LEVEL_REG 0x28 +-#define CFO_ESTIMATOR_CTRL_REG_1 0x29 +-#define CFO_ESTIMATOR_CTRL_REG_2 0x2a +-#define CFO_ESTIMATOR_CTRL_REG_3 0x2b +- +-#define SYM_RATE_ESTIMATE_REG_L 0x31 +-#define SYM_RATE_ESTIMATE_REG_M 0x32 +-#define SYM_RATE_ESTIMATE_REG_H 0x33 +- +-#define CFO_ESTIMATOR_OFFSET_REG_L 0x36 +-#define CFO_ESTIMATOR_OFFSET_REG_H 0x37 +-#define CFO_ERROR_REG_L 0x38 +-#define CFO_ERROR_REG_H 0x39 +-#define SYM_RATE_ESTIMATOR_CTRL_REG 0x3a +- +-#define SYM_RATE_REG_L 0x3f +-#define SYM_RATE_REG_M 0x40 +-#define SYM_RATE_REG_H 0x41 +-#define SYM_RATE_ESTIMATOR_MAXIMUM_REG 0x42 +-#define SYM_RATE_ESTIMATOR_MINIMUM_REG 0x43 +- +-#define C_N_ESTIMATOR_CTRL_REG 0x7c +-#define C_N_ESTIMATOR_THRSHLD_REG 0x7d +-#define C_N_ESTIMATOR_LEVEL_REG_L 0x7e +-#define C_N_ESTIMATOR_LEVEL_REG_H 0x7f +- +-#define BLIND_SCAN_CTRL_REG 0x80 +- +-#define LSA_CTRL_REG_1 0x8D +-#define SPCTRM_TILT_CORR_THRSHLD_REG 0x8f +-#define ONE_DB_BNDWDTH_THRSHLD_REG 0x90 +-#define TWO_DB_BNDWDTH_THRSHLD_REG 0x91 +-#define THREE_DB_BNDWDTH_THRSHLD_REG 0x92 +-#define INBAND_POWER_THRSHLD_REG 0x93 +-#define REF_NOISE_LVL_MRGN_THRSHLD_REG 0x94 +- +-#define VIT_SRCH_CTRL_REG_1 0xa0 +-#define VIT_SRCH_CTRL_REG_2 0xa1 +-#define VIT_SRCH_CTRL_REG_3 0xa2 +-#define VIT_SRCH_STATUS_REG 0xa3 +-#define VITERBI_BER_COUNT_REG_L 0xab +-#define REED_SOLOMON_CTRL_REG 0xb0 +-#define REED_SOLOMON_ERROR_COUNT_REG_L 0xb1 +-#define PRBS_CTRL_REG 0xb5 +- +-#define LNB_CTRL_REG_1 0xc0 +-#define LNB_CTRL_REG_2 0xc1 +-#define LNB_CTRL_REG_3 0xc2 +-#define LNB_CTRL_REG_4 0xc3 +-#define LNB_CTRL_STATUS_REG 0xc4 +-#define LNB_FIFO_REGS_0 0xc5 +-#define LNB_FIFO_REGS_1 0xc6 +-#define LNB_FIFO_REGS_2 0xc7 +-#define LNB_FIFO_REGS_3 0xc8 +-#define LNB_FIFO_REGS_4 0xc9 +-#define LNB_FIFO_REGS_5 0xca +-#define LNB_SUPPLY_CTRL_REG_1 0xcb +-#define LNB_SUPPLY_CTRL_REG_2 0xcc +-#define LNB_SUPPLY_CTRL_REG_3 0xcd +-#define LNB_SUPPLY_CTRL_REG_4 0xce +-#define LNB_SUPPLY_STATUS_REG 0xcf +- +-#define FAIL -1 +-#define PASS 0 +- +-#define ALLOWABLE_FS_COUNT 10 +-#define STATUS_BER 0 +-#define STATUS_UCBLOCKS 1 +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "si21xx: " args); \ +- } while (0) +- +-enum { +- ACTIVE_HIGH, +- ACTIVE_LOW +-}; +-enum { +- BYTE_WIDE, +- BIT_WIDE +-}; +-enum { +- CLK_GAPPED_MODE, +- CLK_CONTINUOUS_MODE +-}; +-enum { +- RISING_EDGE, +- FALLING_EDGE +-}; +-enum { +- MSB_FIRST, +- LSB_FIRST +-}; +-enum { +- SERIAL, +- PARALLEL +-}; +- +-struct si21xx_state { +- struct i2c_adapter *i2c; +- const struct si21xx_config *config; +- struct dvb_frontend frontend; +- u8 initialised:1; +- int errmode; +- int fs; /*Sampling rate of the ADC in MHz*/ +-}; +- +-/* register default initialization */ +-static u8 serit_sp1511lhb_inittab[] = { +- 0x01, 0x28, /* set i2c_inc_disable */ +- 0x20, 0x03, +- 0x27, 0x20, +- 0xe0, 0x45, +- 0xe1, 0x08, +- 0xfe, 0x01, +- 0x01, 0x28, +- 0x89, 0x09, +- 0x04, 0x80, +- 0x05, 0x01, +- 0x06, 0x00, +- 0x20, 0x03, +- 0x24, 0x88, +- 0x29, 0x09, +- 0x2a, 0x0f, +- 0x2c, 0x10, +- 0x2d, 0x19, +- 0x2e, 0x08, +- 0x2f, 0x10, +- 0x30, 0x19, +- 0x34, 0x20, +- 0x35, 0x03, +- 0x45, 0x02, +- 0x46, 0x45, +- 0x47, 0xd0, +- 0x48, 0x00, +- 0x49, 0x40, +- 0x4a, 0x03, +- 0x4c, 0xfd, +- 0x4f, 0x2e, +- 0x50, 0x2e, +- 0x51, 0x10, +- 0x52, 0x10, +- 0x56, 0x92, +- 0x59, 0x00, +- 0x5a, 0x2d, +- 0x5b, 0x33, +- 0x5c, 0x1f, +- 0x5f, 0x76, +- 0x62, 0xc0, +- 0x63, 0xc0, +- 0x64, 0xf3, +- 0x65, 0xf3, +- 0x79, 0x40, +- 0x6a, 0x40, +- 0x6b, 0x0a, +- 0x6c, 0x80, +- 0x6d, 0x27, +- 0x71, 0x06, +- 0x75, 0x60, +- 0x78, 0x00, +- 0x79, 0xb5, +- 0x7c, 0x05, +- 0x7d, 0x1a, +- 0x87, 0x55, +- 0x88, 0x72, +- 0x8f, 0x08, +- 0x90, 0xe0, +- 0x94, 0x40, +- 0xa0, 0x3f, +- 0xa1, 0xc0, +- 0xa4, 0xcc, +- 0xa5, 0x66, +- 0xa6, 0x66, +- 0xa7, 0x7b, +- 0xa8, 0x7b, +- 0xa9, 0x7b, +- 0xaa, 0x9a, +- 0xed, 0x04, +- 0xad, 0x00, +- 0xae, 0x03, +- 0xcc, 0xab, +- 0x01, 0x08, +- 0xff, 0xff +-}; +- +-/* low level read/writes */ +-static int si21_writeregs(struct si21xx_state *state, u8 reg1, +- u8 *data, int len) +-{ +- int ret; +- u8 buf[60];/* = { reg1, data };*/ +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = len + 1 +- }; +- +- msg.buf[0] = reg1; +- memcpy(msg.buf + 1, data, len); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: writereg error (reg1 == 0x%02x, data == 0x%02x, " +- "ret == %i)\n", __func__, reg1, data[0], ret); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int si21_writereg(struct si21xx_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 +- }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: writereg error (reg == 0x%02x, data == 0x%02x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int si21_write(struct dvb_frontend *fe, const u8 buf[], int len) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- if (len != 2) +- return -EINVAL; +- +- return si21_writereg(state, buf[0], buf[1]); +-} +- +-static u8 si21_readreg(struct si21xx_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", +- __func__, reg, ret); +- +- return b1[0]; +-} +- +-static int si21_readregs(struct si21xx_state *state, u8 reg1, u8 *b, u8 len) +-{ +- int ret; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = ®1, +- .len = 1 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b, +- .len = len +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk("%s: readreg error (ret == %i)\n", __func__, ret); +- +- return ret == 2 ? 0 : -1; +-} +- +-static int si21xx_wait_diseqc_idle(struct si21xx_state *state, int timeout) +-{ +- unsigned long start = jiffies; +- +- dprintk("%s\n", __func__); +- +- while ((si21_readreg(state, LNB_CTRL_REG_1) & 0x8) == 8) { +- if (jiffies - start > timeout) { +- dprintk("%s: timeout!!\n", __func__); +- return -ETIMEDOUT; +- } +- msleep(10); +- }; +- +- return 0; +-} +- +-static int si21xx_set_symbolrate(struct dvb_frontend *fe, u32 srate) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u32 sym_rate, data_rate; +- int i; +- u8 sym_rate_bytes[3]; +- +- dprintk("%s : srate = %i\n", __func__ , srate); +- +- if ((srate < 1000000) || (srate > 45000000)) +- return -EINVAL; +- +- data_rate = srate; +- sym_rate = 0; +- +- for (i = 0; i < 4; ++i) { +- sym_rate /= 100; +- sym_rate = sym_rate + ((data_rate % 100) * 0x800000) / +- state->fs; +- data_rate /= 100; +- } +- for (i = 0; i < 3; ++i) +- sym_rate_bytes[i] = (u8)((sym_rate >> (i * 8)) & 0xff); +- +- si21_writeregs(state, SYM_RATE_REG_L, sym_rate_bytes, 0x03); +- +- return 0; +-} +- +-static int si21xx_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u8 lnb_status; +- u8 LNB_CTRL_1; +- int status; +- +- dprintk("%s\n", __func__); +- +- status = PASS; +- LNB_CTRL_1 = 0; +- +- status |= si21_readregs(state, LNB_CTRL_STATUS_REG, &lnb_status, 0x01); +- status |= si21_readregs(state, LNB_CTRL_REG_1, &lnb_status, 0x01); +- +- /*fill the FIFO*/ +- status |= si21_writeregs(state, LNB_FIFO_REGS_0, m->msg, m->msg_len); +- +- LNB_CTRL_1 = (lnb_status & 0x70); +- LNB_CTRL_1 |= m->msg_len; +- +- LNB_CTRL_1 |= 0x80; /* begin LNB signaling */ +- +- status |= si21_writeregs(state, LNB_CTRL_REG_1, &LNB_CTRL_1, 0x01); +- +- return status; +-} +- +-static int si21xx_send_diseqc_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u8 val; +- +- dprintk("%s\n", __func__); +- +- if (si21xx_wait_diseqc_idle(state, 100) < 0) +- return -ETIMEDOUT; +- +- val = (0x80 | si21_readreg(state, 0xc1)); +- if (si21_writereg(state, LNB_CTRL_REG_1, +- burst == SEC_MINI_A ? (val & ~0x10) : (val | 0x10))) +- return -EREMOTEIO; +- +- if (si21xx_wait_diseqc_idle(state, 100) < 0) +- return -ETIMEDOUT; +- +- if (si21_writereg(state, LNB_CTRL_REG_1, val)) +- return -EREMOTEIO; +- +- return 0; +-} +-/* 30.06.2008 */ +-static int si21xx_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u8 val; +- +- dprintk("%s\n", __func__); +- val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); +- +- switch (tone) { +- case SEC_TONE_ON: +- return si21_writereg(state, LNB_CTRL_REG_1, val | 0x20); +- +- case SEC_TONE_OFF: +- return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x20)); +- +- default: +- return -EINVAL; +- } +-} +- +-static int si21xx_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- u8 val; +- dprintk("%s: %s\n", __func__, +- volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : +- volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); +- +- +- val = (0x80 | si21_readreg(state, LNB_CTRL_REG_1)); +- +- switch (volt) { +- case SEC_VOLTAGE_18: +- return si21_writereg(state, LNB_CTRL_REG_1, val | 0x40); +- break; +- case SEC_VOLTAGE_13: +- return si21_writereg(state, LNB_CTRL_REG_1, (val & ~0x40)); +- break; +- default: +- return -EINVAL; +- }; +-} +- +-static int si21xx_init(struct dvb_frontend *fe) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- int i; +- int status = 0; +- u8 reg1; +- u8 val; +- u8 reg2[2]; +- +- dprintk("%s\n", __func__); +- +- for (i = 0; ; i += 2) { +- reg1 = serit_sp1511lhb_inittab[i]; +- val = serit_sp1511lhb_inittab[i+1]; +- if (reg1 == 0xff && val == 0xff) +- break; +- si21_writeregs(state, reg1, &val, 1); +- } +- +- /*DVB QPSK SYSTEM MODE REG*/ +- reg1 = 0x08; +- si21_writeregs(state, SYSTEM_MODE_REG, ®1, 0x01); +- +- /*transport stream config*/ +- /* +- mode = PARALLEL; +- sdata_form = LSB_FIRST; +- clk_edge = FALLING_EDGE; +- clk_mode = CLK_GAPPED_MODE; +- strt_len = BYTE_WIDE; +- sync_pol = ACTIVE_HIGH; +- val_pol = ACTIVE_HIGH; +- err_pol = ACTIVE_HIGH; +- sclk_rate = 0x00; +- parity = 0x00 ; +- data_delay = 0x00; +- clk_delay = 0x00; +- pclk_smooth = 0x00; +- */ +- reg2[0] = +- PARALLEL + (LSB_FIRST << 1) +- + (FALLING_EDGE << 2) + (CLK_GAPPED_MODE << 3) +- + (BYTE_WIDE << 4) + (ACTIVE_HIGH << 5) +- + (ACTIVE_HIGH << 6) + (ACTIVE_HIGH << 7); +- +- reg2[1] = 0; +- /* sclk_rate + (parity << 2) +- + (data_delay << 3) + (clk_delay << 4) +- + (pclk_smooth << 5); +- */ +- status |= si21_writeregs(state, TS_CTRL_REG_1, reg2, 0x02); +- if (status != 0) +- dprintk(" %s : TS Set Error\n", __func__); +- +- return 0; +- +-} +- +-static int si21_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u8 regs_read[2]; +- u8 reg_read; +- u8 i; +- u8 lock; +- u8 signal = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG); +- +- si21_readregs(state, LOCK_STATUS_REG_1, regs_read, 0x02); +- reg_read = 0; +- +- for (i = 0; i < 7; ++i) +- reg_read |= ((regs_read[0] >> i) & 0x01) << (6 - i); +- +- lock = ((reg_read & 0x7f) | (regs_read[1] & 0x80)); +- +- dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, lock); +- *status = 0; +- +- if (signal > 10) +- *status |= FE_HAS_SIGNAL; +- +- if (lock & 0x2) +- *status |= FE_HAS_CARRIER; +- +- if (lock & 0x20) +- *status |= FE_HAS_VITERBI; +- +- if (lock & 0x40) +- *status |= FE_HAS_SYNC; +- +- if ((lock & 0x7b) == 0x7b) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int si21_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- /*status = si21_readreg(state, ANALOG_AGC_POWER_LEVEL_REG, +- (u8*)agclevel, 0x01);*/ +- +- u16 signal = (3 * si21_readreg(state, 0x27) * +- si21_readreg(state, 0x28)); +- +- dprintk("%s : AGCPWR: 0x%02x%02x, signal=0x%04x\n", __func__, +- si21_readreg(state, 0x27), +- si21_readreg(state, 0x28), (int) signal); +- +- signal <<= 4; +- *strength = signal; +- +- return 0; +-} +- +-static int si21_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (state->errmode != STATUS_BER) +- return 0; +- +- *ber = (si21_readreg(state, 0x1d) << 8) | +- si21_readreg(state, 0x1e); +- +- return 0; +-} +- +-static int si21_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- s32 xsnr = 0xffff - ((si21_readreg(state, 0x24) << 8) | +- si21_readreg(state, 0x25)); +- xsnr = 3 * (xsnr - 0xa100); +- *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; +- +- dprintk("%s\n", __func__); +- +- return 0; +-} +- +-static int si21_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (state->errmode != STATUS_UCBLOCKS) +- *ucblocks = 0; +- else +- *ucblocks = (si21_readreg(state, 0x1d) << 8) | +- si21_readreg(state, 0x1e); +- +- return 0; +-} +- +-/* initiates a channel acquisition sequence +- using the specified symbol rate and code rate */ +-static int si21xx_setacquire(struct dvb_frontend *fe, int symbrate, +- fe_code_rate_t crate) +-{ +- +- struct si21xx_state *state = fe->demodulator_priv; +- u8 coderates[] = { +- 0x0, 0x01, 0x02, 0x04, 0x00, +- 0x8, 0x10, 0x20, 0x00, 0x3f +- }; +- +- u8 coderate_ptr; +- int status; +- u8 start_acq = 0x80; +- u8 reg, regs[3]; +- +- dprintk("%s\n", __func__); +- +- status = PASS; +- coderate_ptr = coderates[crate]; +- +- si21xx_set_symbolrate(fe, symbrate); +- +- /* write code rates to use in the Viterbi search */ +- status |= si21_writeregs(state, +- VIT_SRCH_CTRL_REG_1, +- &coderate_ptr, 0x01); +- +- /* clear acq_start bit */ +- status |= si21_readregs(state, ACQ_CTRL_REG_2, ®, 0x01); +- reg &= ~start_acq; +- status |= si21_writeregs(state, ACQ_CTRL_REG_2, ®, 0x01); +- +- /* use new Carrier Frequency Offset Estimator (QuickLock) */ +- regs[0] = 0xCB; +- regs[1] = 0x40; +- regs[2] = 0xCB; +- +- status |= si21_writeregs(state, +- TWO_DB_BNDWDTH_THRSHLD_REG, +- ®s[0], 0x03); +- reg = 0x56; +- status |= si21_writeregs(state, +- LSA_CTRL_REG_1, ®, 1); +- reg = 0x05; +- status |= si21_writeregs(state, +- BLIND_SCAN_CTRL_REG, ®, 1); +- /* start automatic acq */ +- status |= si21_writeregs(state, +- ACQ_CTRL_REG_2, &start_acq, 0x01); +- +- return status; +-} +- +-static int si21xx_set_frontend(struct dvb_frontend *fe) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- /* freq Channel carrier frequency in KHz (i.e. 1550000 KHz) +- datarate Channel symbol rate in Sps (i.e. 22500000 Sps)*/ +- +- /* in MHz */ +- unsigned char coarse_tune_freq; +- int fine_tune_freq; +- unsigned char sample_rate = 0; +- /* boolean */ +- bool inband_interferer_ind; +- +- /* INTERMEDIATE VALUES */ +- int icoarse_tune_freq; /* MHz */ +- int ifine_tune_freq; /* MHz */ +- unsigned int band_high; +- unsigned int band_low; +- unsigned int x1; +- unsigned int x2; +- int i; +- bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; +- bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; +- int status; +- +- /* allowable sample rates for ADC in MHz */ +- int afs[ALLOWABLE_FS_COUNT] = { 200, 192, 193, 194, 195, +- 196, 204, 205, 206, 207 +- }; +- /* in MHz */ +- int if_limit_high; +- int if_limit_low; +- int lnb_lo; +- int lnb_uncertanity; +- +- int rf_freq; +- int data_rate; +- unsigned char regs[4]; +- +- dprintk("%s : FE_SET_FRONTEND\n", __func__); +- +- if (c->delivery_system != SYS_DVBS) { +- dprintk("%s: unsupported delivery system selected (%d)\n", +- __func__, c->delivery_system); +- return -EOPNOTSUPP; +- } +- +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) +- inband_interferer_div2[i] = inband_interferer_div4[i] = false; +- +- if_limit_high = -700000; +- if_limit_low = -100000; +- /* in MHz */ +- lnb_lo = 0; +- lnb_uncertanity = 0; +- +- rf_freq = 10 * c->frequency ; +- data_rate = c->symbol_rate / 100; +- +- status = PASS; +- +- band_low = (rf_freq - lnb_lo) - ((lnb_uncertanity * 200) +- + (data_rate * 135)) / 200; +- +- band_high = (rf_freq - lnb_lo) + ((lnb_uncertanity * 200) +- + (data_rate * 135)) / 200; +- +- +- icoarse_tune_freq = 100000 * +- (((rf_freq - lnb_lo) - +- (if_limit_low + if_limit_high) / 2) +- / 100000); +- +- ifine_tune_freq = (rf_freq - lnb_lo) - icoarse_tune_freq ; +- +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { +- x1 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * +- (afs[i] * 2500) + afs[i] * 2500; +- +- x2 = ((rf_freq - lnb_lo) / (afs[i] * 2500)) * +- (afs[i] * 2500); +- +- if (((band_low < x1) && (x1 < band_high)) || +- ((band_low < x2) && (x2 < band_high))) +- inband_interferer_div4[i] = true; +- +- } +- +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { +- x1 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * +- (afs[i] * 5000) + afs[i] * 5000; +- +- x2 = ((rf_freq - lnb_lo) / (afs[i] * 5000)) * +- (afs[i] * 5000); +- +- if (((band_low < x1) && (x1 < band_high)) || +- ((band_low < x2) && (x2 < band_high))) +- inband_interferer_div2[i] = true; +- } +- +- inband_interferer_ind = true; +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { +- if (inband_interferer_div2[i] || inband_interferer_div4[i]) { +- inband_interferer_ind = false; +- break; +- } +- } +- +- if (inband_interferer_ind) { +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { +- if (!inband_interferer_div2[i]) { +- sample_rate = (u8) afs[i]; +- break; +- } +- } +- } else { +- for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { +- if ((inband_interferer_div2[i] || +- !inband_interferer_div4[i])) { +- sample_rate = (u8) afs[i]; +- break; +- } +- } +- +- } +- +- if (sample_rate > 207 || sample_rate < 192) +- sample_rate = 200; +- +- fine_tune_freq = ((0x4000 * (ifine_tune_freq / 10)) / +- ((sample_rate) * 1000)); +- +- coarse_tune_freq = (u8)(icoarse_tune_freq / 100000); +- +- regs[0] = sample_rate; +- regs[1] = coarse_tune_freq; +- regs[2] = fine_tune_freq & 0xFF; +- regs[3] = fine_tune_freq >> 8 & 0xFF; +- +- status |= si21_writeregs(state, PLL_DIVISOR_REG, ®s[0], 0x04); +- +- state->fs = sample_rate;/*ADC MHz*/ +- si21xx_setacquire(fe, c->symbol_rate, c->fec_inner); +- +- return 0; +-} +- +-static int si21xx_sleep(struct dvb_frontend *fe) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- u8 regdata; +- +- dprintk("%s\n", __func__); +- +- si21_readregs(state, SYSTEM_MODE_REG, ®data, 0x01); +- regdata |= 1 << 6; +- si21_writeregs(state, SYSTEM_MODE_REG, ®data, 0x01); +- state->initialised = 0; +- +- return 0; +-} +- +-static void si21xx_release(struct dvb_frontend *fe) +-{ +- struct si21xx_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- kfree(state); +-} +- +-static struct dvb_frontend_ops si21xx_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "SL SI21XX DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, /* ppm */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_QPSK | +- FE_CAN_FEC_AUTO +- }, +- +- .release = si21xx_release, +- .init = si21xx_init, +- .sleep = si21xx_sleep, +- .write = si21_write, +- .read_status = si21_read_status, +- .read_ber = si21_read_ber, +- .read_signal_strength = si21_read_signal_strength, +- .read_snr = si21_read_snr, +- .read_ucblocks = si21_read_ucblocks, +- .diseqc_send_master_cmd = si21xx_send_diseqc_msg, +- .diseqc_send_burst = si21xx_send_diseqc_burst, +- .set_tone = si21xx_set_tone, +- .set_voltage = si21xx_set_voltage, +- +- .set_frontend = si21xx_set_frontend, +-}; +- +-struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, +- struct i2c_adapter *i2c) +-{ +- struct si21xx_state *state = NULL; +- int id; +- +- dprintk("%s\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- state->errmode = STATUS_BER; +- +- /* check if the demod is there */ +- id = si21_readreg(state, SYSTEM_MODE_REG); +- si21_writereg(state, SYSTEM_MODE_REG, id | 0x40); /* standby off */ +- msleep(200); +- id = si21_readreg(state, 0x00); +- +- /* register 0x00 contains: +- 0x34 for SI2107 +- 0x24 for SI2108 +- 0x14 for SI2109 +- 0x04 for SI2110 +- */ +- if (id != 0x04 && id != 0x14) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &si21xx_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(si21xx_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("SL SI21XX DVB Demodulator driver"); +-MODULE_AUTHOR("Igor M. Liplianin"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/si21xx.h b/drivers/media/dvb/frontends/si21xx.h +deleted file mode 100644 +index 141b5b8..0000000 +--- a/drivers/media/dvb/frontends/si21xx.h ++++ /dev/null +@@ -1,37 +0,0 @@ +-#ifndef SI21XX_H +-#define SI21XX_H +- +-#include +-#include "dvb_frontend.h" +- +-struct si21xx_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* minimum delay before retuning */ +- int min_delay_ms; +-}; +- +-#if defined(CONFIG_DVB_SI21XX) || \ +- (defined(CONFIG_DVB_SI21XX_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *si21xx_attach( +- const struct si21xx_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-static inline int si21xx_writeregister(struct dvb_frontend *fe, u8 reg, u8 val) +-{ +- int r = 0; +- u8 buf[] = {reg, val}; +- if (fe->ops.write) +- r = fe->ops.write(fe, buf, 2); +- return r; +-} +- +-#endif +diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c +deleted file mode 100644 +index e37274c..0000000 +--- a/drivers/media/dvb/frontends/sp8870.c ++++ /dev/null +@@ -1,620 +0,0 @@ +-/* +- Driver for Spase SP8870 demodulator +- +- Copyright (C) 1999 Juergen Peitz +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +-/* +- * This driver needs external firmware. Please use the command +- * "/Documentation/dvb/get_dvb_firmware alps_tdlb7" to +- * download/extract it, and then copy it to /usr/lib/hotplug/firmware +- * or /lib/firmware (depending on configuration of firmware hotplug). +- */ +-#define SP8870_DEFAULT_FIRMWARE "dvb-fe-sp8870.fw" +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "sp8870.h" +- +- +-struct sp8870_state { +- +- struct i2c_adapter* i2c; +- +- const struct sp8870_config* config; +- +- struct dvb_frontend frontend; +- +- /* demodulator private data */ +- u8 initialised:1; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "sp8870: " args); \ +- } while (0) +- +-/* firmware size for sp8870 */ +-#define SP8870_FIRMWARE_SIZE 16382 +- +-/* starting point for firmware in file 'Sc_main.mc' */ +-#define SP8870_FIRMWARE_OFFSET 0x0A +- +-static int sp8870_writereg (struct sp8870_state* state, u16 reg, u16 data) +-{ +- u8 buf [] = { reg >> 8, reg & 0xff, data >> 8, data & 0xff }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 4 }; +- int err; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int sp8870_readreg (struct sp8870_state* state, u16 reg) +-{ +- int ret; +- u8 b0 [] = { reg >> 8 , reg & 0xff }; +- u8 b1 [] = { 0, 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 } }; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- +- if (ret != 2) { +- dprintk("%s: readreg error (ret == %i)\n", __func__, ret); +- return -1; +- } +- +- return (b1[0] << 8 | b1[1]); +-} +- +-static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw) +-{ +- struct i2c_msg msg; +- const char *fw_buf = fw->data; +- int fw_pos; +- u8 tx_buf[255]; +- int tx_len; +- int err = 0; +- +- dprintk ("%s: ...\n", __func__); +- +- if (fw->size < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET) +- return -EINVAL; +- +- // system controller stop +- sp8870_writereg(state, 0x0F00, 0x0000); +- +- // instruction RAM register hiword +- sp8870_writereg(state, 0x8F08, ((SP8870_FIRMWARE_SIZE / 2) & 0xFFFF)); +- +- // instruction RAM MWR +- sp8870_writereg(state, 0x8F0A, ((SP8870_FIRMWARE_SIZE / 2) >> 16)); +- +- // do firmware upload +- fw_pos = SP8870_FIRMWARE_OFFSET; +- while (fw_pos < SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET){ +- tx_len = (fw_pos <= SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - 252) ? 252 : SP8870_FIRMWARE_SIZE + SP8870_FIRMWARE_OFFSET - fw_pos; +- // write register 0xCF0A +- tx_buf[0] = 0xCF; +- tx_buf[1] = 0x0A; +- memcpy(&tx_buf[2], fw_buf + fw_pos, tx_len); +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.buf = tx_buf; +- msg.len = tx_len + 2; +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk("%s: firmware upload failed!\n", __func__); +- printk ("%s: i2c error (err == %i)\n", __func__, err); +- return err; +- } +- fw_pos += tx_len; +- } +- +- dprintk ("%s: done!\n", __func__); +- return 0; +-}; +- +-static void sp8870_microcontroller_stop (struct sp8870_state* state) +-{ +- sp8870_writereg(state, 0x0F08, 0x000); +- sp8870_writereg(state, 0x0F09, 0x000); +- +- // microcontroller STOP +- sp8870_writereg(state, 0x0F00, 0x000); +-} +- +-static void sp8870_microcontroller_start (struct sp8870_state* state) +-{ +- sp8870_writereg(state, 0x0F08, 0x000); +- sp8870_writereg(state, 0x0F09, 0x000); +- +- // microcontroller START +- sp8870_writereg(state, 0x0F00, 0x001); +- // not documented but if we don't read 0x0D01 out here +- // we don't get a correct data valid signal +- sp8870_readreg(state, 0x0D01); +-} +- +-static int sp8870_read_data_valid_signal(struct sp8870_state* state) +-{ +- return (sp8870_readreg(state, 0x0D02) > 0); +-} +- +-static int configure_reg0xc05 (struct dtv_frontend_properties *p, u16 *reg0xc05) +-{ +- int known_parameters = 1; +- +- *reg0xc05 = 0x000; +- +- switch (p->modulation) { +- case QPSK: +- break; +- case QAM_16: +- *reg0xc05 |= (1 << 10); +- break; +- case QAM_64: +- *reg0xc05 |= (2 << 10); +- break; +- case QAM_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- switch (p->hierarchy) { +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- *reg0xc05 |= (1 << 7); +- break; +- case HIERARCHY_2: +- *reg0xc05 |= (2 << 7); +- break; +- case HIERARCHY_4: +- *reg0xc05 |= (3 << 7); +- break; +- case HIERARCHY_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- switch (p->code_rate_HP) { +- case FEC_1_2: +- break; +- case FEC_2_3: +- *reg0xc05 |= (1 << 3); +- break; +- case FEC_3_4: +- *reg0xc05 |= (2 << 3); +- break; +- case FEC_5_6: +- *reg0xc05 |= (3 << 3); +- break; +- case FEC_7_8: +- *reg0xc05 |= (4 << 3); +- break; +- case FEC_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- if (known_parameters) +- *reg0xc05 |= (2 << 1); /* use specified parameters */ +- else +- *reg0xc05 |= (1 << 1); /* enable autoprobing */ +- +- return 0; +-} +- +-static int sp8870_wake_up(struct sp8870_state* state) +-{ +- // enable TS output and interface pins +- return sp8870_writereg(state, 0xC18, 0x00D); +-} +- +-static int sp8870_set_frontend_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct sp8870_state* state = fe->demodulator_priv; +- int err; +- u16 reg0xc05; +- +- if ((err = configure_reg0xc05(p, ®0xc05))) +- return err; +- +- // system controller stop +- sp8870_microcontroller_stop(state); +- +- // set tuner parameters +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- // sample rate correction bit [23..17] +- sp8870_writereg(state, 0x0319, 0x000A); +- +- // sample rate correction bit [16..0] +- sp8870_writereg(state, 0x031A, 0x0AAB); +- +- // integer carrier offset +- sp8870_writereg(state, 0x0309, 0x0400); +- +- // fractional carrier offset +- sp8870_writereg(state, 0x030A, 0x0000); +- +- // filter for 6/7/8 Mhz channel +- if (p->bandwidth_hz == 6000000) +- sp8870_writereg(state, 0x0311, 0x0002); +- else if (p->bandwidth_hz == 7000000) +- sp8870_writereg(state, 0x0311, 0x0001); +- else +- sp8870_writereg(state, 0x0311, 0x0000); +- +- // scan order: 2k first = 0x0000, 8k first = 0x0001 +- if (p->transmission_mode == TRANSMISSION_MODE_2K) +- sp8870_writereg(state, 0x0338, 0x0000); +- else +- sp8870_writereg(state, 0x0338, 0x0001); +- +- sp8870_writereg(state, 0xc05, reg0xc05); +- +- // read status reg in order to clear pending irqs +- sp8870_readreg(state, 0x200); +- +- // system controller start +- sp8870_microcontroller_start(state); +- +- return 0; +-} +- +-static int sp8870_init (struct dvb_frontend* fe) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- const struct firmware *fw = NULL; +- +- sp8870_wake_up(state); +- if (state->initialised) return 0; +- state->initialised = 1; +- +- dprintk ("%s\n", __func__); +- +- +- /* request the firmware, this will block until someone uploads it */ +- printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); +- if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { +- printk("sp8870: no firmware upload (timeout or file not found?)\n"); +- return -EIO; +- } +- +- if (sp8870_firmware_upload(state, fw)) { +- printk("sp8870: writing firmware to device failed\n"); +- release_firmware(fw); +- return -EIO; +- } +- release_firmware(fw); +- printk("sp8870: firmware upload complete\n"); +- +- /* enable TS output and interface pins */ +- sp8870_writereg(state, 0xc18, 0x00d); +- +- // system controller stop +- sp8870_microcontroller_stop(state); +- +- // ADC mode +- sp8870_writereg(state, 0x0301, 0x0003); +- +- // Reed Solomon parity bytes passed to output +- sp8870_writereg(state, 0x0C13, 0x0001); +- +- // MPEG clock is suppressed if no valid data +- sp8870_writereg(state, 0x0C14, 0x0001); +- +- /* bit 0x010: enable data valid signal */ +- sp8870_writereg(state, 0x0D00, 0x010); +- sp8870_writereg(state, 0x0D01, 0x000); +- +- return 0; +-} +- +-static int sp8870_read_status (struct dvb_frontend* fe, fe_status_t * fe_status) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- int status; +- int signal; +- +- *fe_status = 0; +- +- status = sp8870_readreg (state, 0x0200); +- if (status < 0) +- return -EIO; +- +- signal = sp8870_readreg (state, 0x0303); +- if (signal < 0) +- return -EIO; +- +- if (signal > 0x0F) +- *fe_status |= FE_HAS_SIGNAL; +- if (status & 0x08) +- *fe_status |= FE_HAS_SYNC; +- if (status & 0x04) +- *fe_status |= FE_HAS_LOCK | FE_HAS_CARRIER | FE_HAS_VITERBI; +- +- return 0; +-} +- +-static int sp8870_read_ber (struct dvb_frontend* fe, u32 * ber) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- int ret; +- u32 tmp; +- +- *ber = 0; +- +- ret = sp8870_readreg(state, 0xC08); +- if (ret < 0) +- return -EIO; +- +- tmp = ret & 0x3F; +- +- ret = sp8870_readreg(state, 0xC07); +- if (ret < 0) +- return -EIO; +- +- tmp = ret << 6; +- +- if (tmp >= 0x3FFF0) +- tmp = ~0; +- +- *ber = tmp; +- +- return 0; +-} +- +-static int sp8870_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- int ret; +- u16 tmp; +- +- *signal = 0; +- +- ret = sp8870_readreg (state, 0x306); +- if (ret < 0) +- return -EIO; +- +- tmp = ret << 8; +- +- ret = sp8870_readreg (state, 0x303); +- if (ret < 0) +- return -EIO; +- +- tmp |= ret; +- +- if (tmp) +- *signal = 0xFFFF - tmp; +- +- return 0; +-} +- +-static int sp8870_read_uncorrected_blocks (struct dvb_frontend* fe, u32* ublocks) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- int ret; +- +- *ublocks = 0; +- +- ret = sp8870_readreg(state, 0xC0C); +- if (ret < 0) +- return -EIO; +- +- if (ret == 0xFFFF) +- ret = ~0; +- +- *ublocks = ret; +- +- return 0; +-} +- +-/* number of trials to recover from lockup */ +-#define MAXTRIALS 5 +-/* maximum checks for data valid signal */ +-#define MAXCHECKS 100 +- +-/* only for debugging: counter for detected lockups */ +-static int lockups; +-/* only for debugging: counter for channel switches */ +-static int switches; +- +-static int sp8870_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct sp8870_state* state = fe->demodulator_priv; +- +- /* +- The firmware of the sp8870 sometimes locks up after setting frontend parameters. +- We try to detect this by checking the data valid signal. +- If it is not set after MAXCHECKS we try to recover the lockup by setting +- the frontend parameters again. +- */ +- +- int err = 0; +- int valid = 0; +- int trials = 0; +- int check_count = 0; +- +- dprintk("%s: frequency = %i\n", __func__, p->frequency); +- +- for (trials = 1; trials <= MAXTRIALS; trials++) { +- +- err = sp8870_set_frontend_parameters(fe); +- if (err) +- return err; +- +- for (check_count = 0; check_count < MAXCHECKS; check_count++) { +-// valid = ((sp8870_readreg(i2c, 0x0200) & 4) == 0); +- valid = sp8870_read_data_valid_signal(state); +- if (valid) { +- dprintk("%s: delay = %i usec\n", +- __func__, check_count * 10); +- break; +- } +- udelay(10); +- } +- if (valid) +- break; +- } +- +- if (!valid) { +- printk("%s: firmware crash!!!!!!\n", __func__); +- return -EIO; +- } +- +- if (debug) { +- if (valid) { +- if (trials > 1) { +- printk("%s: firmware lockup!!!\n", __func__); +- printk("%s: recovered after %i trial(s))\n", __func__, trials - 1); +- lockups++; +- } +- } +- switches++; +- printk("%s: switches = %i lockups = %i\n", __func__, switches, lockups); +- } +- +- return 0; +-} +- +-static int sp8870_sleep(struct dvb_frontend* fe) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- +- // tristate TS output and disable interface pins +- return sp8870_writereg(state, 0xC18, 0x000); +-} +- +-static int sp8870_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 350; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static int sp8870_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- +- if (enable) { +- return sp8870_writereg(state, 0x206, 0x001); +- } else { +- return sp8870_writereg(state, 0x206, 0x000); +- } +-} +- +-static void sp8870_release(struct dvb_frontend* fe) +-{ +- struct sp8870_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops sp8870_ops; +- +-struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, +- struct i2c_adapter* i2c) +-{ +- struct sp8870_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- +- /* check if the demod is there */ +- if (sp8870_readreg(state, 0x0200) < 0) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &sp8870_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops sp8870_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Spase SP8870 DVB-T", +- .frequency_min = 470000000, +- .frequency_max = 860000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | +- FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER +- }, +- +- .release = sp8870_release, +- +- .init = sp8870_init, +- .sleep = sp8870_sleep, +- .i2c_gate_ctrl = sp8870_i2c_gate_ctrl, +- +- .set_frontend = sp8870_set_frontend, +- .get_tune_settings = sp8870_get_tune_settings, +- +- .read_status = sp8870_read_status, +- .read_ber = sp8870_read_ber, +- .read_signal_strength = sp8870_read_signal_strength, +- .read_ucblocks = sp8870_read_uncorrected_blocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); +-MODULE_AUTHOR("Juergen Peitz"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(sp8870_attach); +diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h +deleted file mode 100644 +index a764a79..0000000 +--- a/drivers/media/dvb/frontends/sp8870.h ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* +- Driver for Spase SP8870 demodulator +- +- Copyright (C) 1999 Juergen Peitz +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef SP8870_H +-#define SP8870_H +- +-#include +-#include +- +-struct sp8870_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* request firmware for device */ +- int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +-}; +- +-#if defined(CONFIG_DVB_SP8870) || (defined(CONFIG_DVB_SP8870_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_SP8870 +- +-#endif // SP8870_H +diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c +deleted file mode 100644 +index f4096cc..0000000 +--- a/drivers/media/dvb/frontends/sp887x.c ++++ /dev/null +@@ -1,629 +0,0 @@ +-/* +- Driver for the Spase sp887x demodulator +-*/ +- +-/* +- * This driver needs external firmware. Please use the command +- * "/Documentation/dvb/get_dvb_firmware sp887x" to +- * download/extract it, and then copy it to /usr/lib/hotplug/firmware +- * or /lib/firmware (depending on configuration of firmware hotplug). +- */ +-#define SP887X_DEFAULT_FIRMWARE "dvb-fe-sp887x.fw" +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "sp887x.h" +- +- +-struct sp887x_state { +- struct i2c_adapter* i2c; +- const struct sp887x_config* config; +- struct dvb_frontend frontend; +- +- /* demodulator private data */ +- u8 initialised:1; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "sp887x: " args); \ +- } while (0) +- +-static int i2c_writebytes (struct sp887x_state* state, u8 *buf, u8 len) +-{ +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = len }; +- int err; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- printk ("%s: i2c write error (addr %02x, err == %i)\n", +- __func__, state->config->demod_address, err); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int sp887x_writereg (struct sp887x_state* state, u16 reg, u16 data) +-{ +- u8 b0 [] = { reg >> 8 , reg & 0xff, data >> 8, data & 0xff }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 4 }; +- int ret; +- +- if ((ret = i2c_transfer(state->i2c, &msg, 1)) != 1) { +- /** +- * in case of soft reset we ignore ACK errors... +- */ +- if (!(reg == 0xf1a && data == 0x000 && +- (ret == -EREMOTEIO || ret == -EFAULT))) +- { +- printk("%s: writereg error " +- "(reg %03x, data %03x, ret == %i)\n", +- __func__, reg & 0xffff, data & 0xffff, ret); +- return ret; +- } +- } +- +- return 0; +-} +- +-static int sp887x_readreg (struct sp887x_state* state, u16 reg) +-{ +- u8 b0 [] = { reg >> 8 , reg & 0xff }; +- u8 b1 [2]; +- int ret; +- struct i2c_msg msg[] = {{ .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 2 }}; +- +- if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { +- printk("%s: readreg error (ret == %i)\n", __func__, ret); +- return -1; +- } +- +- return (((b1[0] << 8) | b1[1]) & 0xfff); +-} +- +-static void sp887x_microcontroller_stop (struct sp887x_state* state) +-{ +- dprintk("%s\n", __func__); +- sp887x_writereg(state, 0xf08, 0x000); +- sp887x_writereg(state, 0xf09, 0x000); +- +- /* microcontroller STOP */ +- sp887x_writereg(state, 0xf00, 0x000); +-} +- +-static void sp887x_microcontroller_start (struct sp887x_state* state) +-{ +- dprintk("%s\n", __func__); +- sp887x_writereg(state, 0xf08, 0x000); +- sp887x_writereg(state, 0xf09, 0x000); +- +- /* microcontroller START */ +- sp887x_writereg(state, 0xf00, 0x001); +-} +- +-static void sp887x_setup_agc (struct sp887x_state* state) +-{ +- /* setup AGC parameters */ +- dprintk("%s\n", __func__); +- sp887x_writereg(state, 0x33c, 0x054); +- sp887x_writereg(state, 0x33b, 0x04c); +- sp887x_writereg(state, 0x328, 0x000); +- sp887x_writereg(state, 0x327, 0x005); +- sp887x_writereg(state, 0x326, 0x001); +- sp887x_writereg(state, 0x325, 0x001); +- sp887x_writereg(state, 0x324, 0x001); +- sp887x_writereg(state, 0x318, 0x050); +- sp887x_writereg(state, 0x317, 0x3fe); +- sp887x_writereg(state, 0x316, 0x001); +- sp887x_writereg(state, 0x313, 0x005); +- sp887x_writereg(state, 0x312, 0x002); +- sp887x_writereg(state, 0x306, 0x000); +- sp887x_writereg(state, 0x303, 0x000); +-} +- +-#define BLOCKSIZE 30 +-#define FW_SIZE 0x4000 +-/** +- * load firmware and setup MPEG interface... +- */ +-static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware *fw) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- u8 buf [BLOCKSIZE+2]; +- int i; +- int fw_size = fw->size; +- const unsigned char *mem = fw->data; +- +- dprintk("%s\n", __func__); +- +- /* ignore the first 10 bytes, then we expect 0x4000 bytes of firmware */ +- if (fw_size < FW_SIZE+10) +- return -ENODEV; +- +- mem = fw->data + 10; +- +- /* soft reset */ +- sp887x_writereg(state, 0xf1a, 0x000); +- +- sp887x_microcontroller_stop (state); +- +- printk ("%s: firmware upload... ", __func__); +- +- /* setup write pointer to -1 (end of memory) */ +- /* bit 0x8000 in address is set to enable 13bit mode */ +- sp887x_writereg(state, 0x8f08, 0x1fff); +- +- /* dummy write (wrap around to start of memory) */ +- sp887x_writereg(state, 0x8f0a, 0x0000); +- +- for (i = 0; i < FW_SIZE; i += BLOCKSIZE) { +- int c = BLOCKSIZE; +- int err; +- +- if (i+c > FW_SIZE) +- c = FW_SIZE - i; +- +- /* bit 0x8000 in address is set to enable 13bit mode */ +- /* bit 0x4000 enables multibyte read/write transfers */ +- /* write register is 0xf0a */ +- buf[0] = 0xcf; +- buf[1] = 0x0a; +- +- memcpy(&buf[2], mem + i, c); +- +- if ((err = i2c_writebytes (state, buf, c+2)) < 0) { +- printk ("failed.\n"); +- printk ("%s: i2c error (err == %i)\n", __func__, err); +- return err; +- } +- } +- +- /* don't write RS bytes between packets */ +- sp887x_writereg(state, 0xc13, 0x001); +- +- /* suppress clock if (!data_valid) */ +- sp887x_writereg(state, 0xc14, 0x000); +- +- /* setup MPEG interface... */ +- sp887x_writereg(state, 0xc1a, 0x872); +- sp887x_writereg(state, 0xc1b, 0x001); +- sp887x_writereg(state, 0xc1c, 0x000); /* parallel mode (serial mode == 1) */ +- sp887x_writereg(state, 0xc1a, 0x871); +- +- /* ADC mode, 2 for MT8872, 3 for SP8870/SP8871 */ +- sp887x_writereg(state, 0x301, 0x002); +- +- sp887x_setup_agc(state); +- +- /* bit 0x010: enable data valid signal */ +- sp887x_writereg(state, 0xd00, 0x010); +- sp887x_writereg(state, 0x0d1, 0x000); +- return 0; +-}; +- +-static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05) +-{ +- int known_parameters = 1; +- +- *reg0xc05 = 0x000; +- +- switch (p->modulation) { +- case QPSK: +- break; +- case QAM_16: +- *reg0xc05 |= (1 << 10); +- break; +- case QAM_64: +- *reg0xc05 |= (2 << 10); +- break; +- case QAM_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- switch (p->hierarchy) { +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- *reg0xc05 |= (1 << 7); +- break; +- case HIERARCHY_2: +- *reg0xc05 |= (2 << 7); +- break; +- case HIERARCHY_4: +- *reg0xc05 |= (3 << 7); +- break; +- case HIERARCHY_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- switch (p->code_rate_HP) { +- case FEC_1_2: +- break; +- case FEC_2_3: +- *reg0xc05 |= (1 << 3); +- break; +- case FEC_3_4: +- *reg0xc05 |= (2 << 3); +- break; +- case FEC_5_6: +- *reg0xc05 |= (3 << 3); +- break; +- case FEC_7_8: +- *reg0xc05 |= (4 << 3); +- break; +- case FEC_AUTO: +- known_parameters = 0; +- break; +- default: +- return -EINVAL; +- }; +- +- if (known_parameters) +- *reg0xc05 |= (2 << 1); /* use specified parameters */ +- else +- *reg0xc05 |= (1 << 1); /* enable autoprobing */ +- +- return 0; +-} +- +-/** +- * estimates division of two 24bit numbers, +- * derived from the ves1820/stv0299 driver code +- */ +-static void divide (int n, int d, int *quotient_i, int *quotient_f) +-{ +- unsigned int q, r; +- +- r = (n % d) << 8; +- q = (r / d); +- +- if (quotient_i) +- *quotient_i = q; +- +- if (quotient_f) { +- r = (r % d) << 8; +- q = (q << 8) | (r / d); +- r = (r % d) << 8; +- *quotient_f = (q << 8) | (r / d); +- } +-} +- +-static void sp887x_correct_offsets (struct sp887x_state* state, +- struct dtv_frontend_properties *p, +- int actual_freq) +-{ +- static const u32 srate_correction [] = { 1879617, 4544878, 8098561 }; +- int bw_index; +- int freq_offset = actual_freq - p->frequency; +- int sysclock = 61003; //[kHz] +- int ifreq = 36000000; +- int freq; +- int frequency_shift; +- +- switch (p->bandwidth_hz) { +- default: +- case 8000000: +- bw_index = 0; +- break; +- case 7000000: +- bw_index = 1; +- break; +- case 6000000: +- bw_index = 2; +- break; +- } +- +- if (p->inversion == INVERSION_ON) +- freq = ifreq - freq_offset; +- else +- freq = ifreq + freq_offset; +- +- divide(freq / 333, sysclock, NULL, &frequency_shift); +- +- if (p->inversion == INVERSION_ON) +- frequency_shift = -frequency_shift; +- +- /* sample rate correction */ +- sp887x_writereg(state, 0x319, srate_correction[bw_index] >> 12); +- sp887x_writereg(state, 0x31a, srate_correction[bw_index] & 0xfff); +- +- /* carrier offset correction */ +- sp887x_writereg(state, 0x309, frequency_shift >> 12); +- sp887x_writereg(state, 0x30a, frequency_shift & 0xfff); +-} +- +-static int sp887x_setup_frontend_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct sp887x_state* state = fe->demodulator_priv; +- unsigned actual_freq; +- int err; +- u16 val, reg0xc05; +- +- if (p->bandwidth_hz != 8000000 && +- p->bandwidth_hz != 7000000 && +- p->bandwidth_hz != 6000000) +- return -EINVAL; +- +- if ((err = configure_reg0xc05(p, ®0xc05))) +- return err; +- +- sp887x_microcontroller_stop(state); +- +- /* setup the PLL */ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- if (fe->ops.tuner_ops.get_frequency) { +- fe->ops.tuner_ops.get_frequency(fe, &actual_freq); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } else { +- actual_freq = p->frequency; +- } +- +- /* read status reg in order to clear bandwidth_hz == 6000000) +- val = 2; +- else if (p->bandwidth_hz == 7000000) +- val = 1; +- else +- val = 0; +- +- sp887x_writereg(state, 0x311, val); +- +- /* scan order: 2k first = 0, 8k first = 1 */ +- if (p->transmission_mode == TRANSMISSION_MODE_2K) +- sp887x_writereg(state, 0x338, 0x000); +- else +- sp887x_writereg(state, 0x338, 0x001); +- +- sp887x_writereg(state, 0xc05, reg0xc05); +- +- if (p->bandwidth_hz == 6000000) +- val = 2 << 3; +- else if (p->bandwidth_hz == 7000000) +- val = 3 << 3; +- else +- val = 0 << 3; +- +- /* enable OFDM and SAW bits as lock indicators in sync register 0xf17, +- * optimize algorithm for given bandwidth... +- */ +- sp887x_writereg(state, 0xf14, 0x160 | val); +- sp887x_writereg(state, 0xf15, 0x000); +- +- sp887x_microcontroller_start(state); +- return 0; +-} +- +-static int sp887x_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- u16 snr12 = sp887x_readreg(state, 0xf16); +- u16 sync0x200 = sp887x_readreg(state, 0x200); +- u16 sync0xf17 = sp887x_readreg(state, 0xf17); +- +- *status = 0; +- +- if (snr12 > 0x00f) +- *status |= FE_HAS_SIGNAL; +- +- //if (sync0x200 & 0x004) +- // *status |= FE_HAS_SYNC | FE_HAS_CARRIER; +- +- //if (sync0x200 & 0x008) +- // *status |= FE_HAS_VITERBI; +- +- if ((sync0xf17 & 0x00f) == 0x002) { +- *status |= FE_HAS_LOCK; +- *status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_CARRIER; +- } +- +- if (sync0x200 & 0x001) { /* tuner adjustment requested...*/ +- int steps = (sync0x200 >> 4) & 0x00f; +- if (steps & 0x008) +- steps = -steps; +- dprintk("sp887x: implement tuner adjustment (%+i steps)!!\n", +- steps); +- } +- +- return 0; +-} +- +-static int sp887x_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- *ber = (sp887x_readreg(state, 0xc08) & 0x3f) | +- (sp887x_readreg(state, 0xc07) << 6); +- sp887x_writereg(state, 0xc08, 0x000); +- sp887x_writereg(state, 0xc07, 0x000); +- if (*ber >= 0x3fff0) +- *ber = ~0; +- +- return 0; +-} +- +-static int sp887x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- u16 snr12 = sp887x_readreg(state, 0xf16); +- u32 signal = 3 * (snr12 << 4); +- *strength = (signal < 0xffff) ? signal : 0xffff; +- +- return 0; +-} +- +-static int sp887x_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- u16 snr12 = sp887x_readreg(state, 0xf16); +- *snr = (snr12 << 4) | (snr12 >> 8); +- +- return 0; +-} +- +-static int sp887x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- *ucblocks = sp887x_readreg(state, 0xc0c); +- if (*ucblocks == 0xfff) +- *ucblocks = ~0; +- +- return 0; +-} +- +-static int sp887x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- if (enable) { +- return sp887x_writereg(state, 0x206, 0x001); +- } else { +- return sp887x_writereg(state, 0x206, 0x000); +- } +-} +- +-static int sp887x_sleep(struct dvb_frontend* fe) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- +- /* tristate TS output and disable interface pins */ +- sp887x_writereg(state, 0xc18, 0x000); +- +- return 0; +-} +- +-static int sp887x_init(struct dvb_frontend* fe) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- const struct firmware *fw = NULL; +- int ret; +- +- if (!state->initialised) { +- /* request the firmware, this will block until someone uploads it */ +- printk("sp887x: waiting for firmware upload (%s)...\n", SP887X_DEFAULT_FIRMWARE); +- ret = state->config->request_firmware(fe, &fw, SP887X_DEFAULT_FIRMWARE); +- if (ret) { +- printk("sp887x: no firmware upload (timeout or file not found?)\n"); +- return ret; +- } +- +- ret = sp887x_initial_setup(fe, fw); +- release_firmware(fw); +- if (ret) { +- printk("sp887x: writing firmware to device failed\n"); +- return ret; +- } +- printk("sp887x: firmware upload complete\n"); +- state->initialised = 1; +- } +- +- /* enable TS output and interface pins */ +- sp887x_writereg(state, 0xc18, 0x00d); +- +- return 0; +-} +- +-static int sp887x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 350; +- fesettings->step_size = 166666*2; +- fesettings->max_drift = (166666*2)+1; +- return 0; +-} +- +-static void sp887x_release(struct dvb_frontend* fe) +-{ +- struct sp887x_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops sp887x_ops; +- +-struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, +- struct i2c_adapter* i2c) +-{ +- struct sp887x_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- +- /* check if the demod is there */ +- if (sp887x_readreg(state, 0x0200) < 0) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &sp887x_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops sp887x_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Spase SP887x DVB-T", +- .frequency_min = 50500000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_RECOVER +- }, +- +- .release = sp887x_release, +- +- .init = sp887x_init, +- .sleep = sp887x_sleep, +- .i2c_gate_ctrl = sp887x_i2c_gate_ctrl, +- +- .set_frontend = sp887x_setup_frontend_parameters, +- .get_tune_settings = sp887x_get_tune_settings, +- +- .read_status = sp887x_read_status, +- .read_ber = sp887x_read_ber, +- .read_signal_strength = sp887x_read_signal_strength, +- .read_snr = sp887x_read_snr, +- .read_ucblocks = sp887x_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(sp887x_attach); +diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h +deleted file mode 100644 +index 04eff6e..0000000 +--- a/drivers/media/dvb/frontends/sp887x.h ++++ /dev/null +@@ -1,32 +0,0 @@ +-/* +- Driver for the Spase sp887x demodulator +-*/ +- +-#ifndef SP887X_H +-#define SP887X_H +- +-#include +-#include +- +-struct sp887x_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* request firmware for device */ +- int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +-}; +- +-#if defined(CONFIG_DVB_SP887X) || (defined(CONFIG_DVB_SP887X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_SP887X +- +-#endif // SP887X_H +diff --git a/drivers/media/dvb/frontends/stb0899_algo.c b/drivers/media/dvb/frontends/stb0899_algo.c +deleted file mode 100644 +index 117a569..0000000 +--- a/drivers/media/dvb/frontends/stb0899_algo.c ++++ /dev/null +@@ -1,1525 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- 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 "stb0899_drv.h" +-#include "stb0899_priv.h" +-#include "stb0899_reg.h" +- +-static inline u32 stb0899_do_div(u64 n, u32 d) +-{ +- /* wrap do_div() for ease of use */ +- +- do_div(n, d); +- return n; +-} +- +-#if 0 +-/* These functions are currently unused */ +-/* +- * stb0899_calc_srate +- * Compute symbol rate +- */ +-static u32 stb0899_calc_srate(u32 master_clk, u8 *sfr) +-{ +- u64 tmp; +- +- /* srate = (SFR * master_clk) >> 20 */ +- +- /* sfr is of size 20 bit, stored with an offset of 4 bit */ +- tmp = (((u32)sfr[0]) << 16) | (((u32)sfr[1]) << 8) | sfr[2]; +- tmp &= ~0xf; +- tmp *= master_clk; +- tmp >>= 24; +- +- return tmp; +-} +- +-/* +- * stb0899_get_srate +- * Get the current symbol rate +- */ +-static u32 stb0899_get_srate(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- u8 sfr[3]; +- +- stb0899_read_regs(state, STB0899_SFRH, sfr, 3); +- +- return stb0899_calc_srate(internal->master_clk, sfr); +-} +-#endif +- +-/* +- * stb0899_set_srate +- * Set symbol frequency +- * MasterClock: master clock frequency (hz) +- * SymbolRate: symbol rate (bauds) +- * return symbol frequency +- */ +-static u32 stb0899_set_srate(struct stb0899_state *state, u32 master_clk, u32 srate) +-{ +- u32 tmp; +- u8 sfr[3]; +- +- dprintk(state->verbose, FE_DEBUG, 1, "-->"); +- /* +- * in order to have the maximum precision, the symbol rate entered into +- * the chip is computed as the closest value of the "true value". +- * In this purpose, the symbol rate value is rounded (1 is added on the bit +- * below the LSB ) +- * +- * srate = (SFR * master_clk) >> 20 +- * <=> +- * SFR = srate << 20 / master_clk +- * +- * rounded: +- * SFR = (srate << 21 + master_clk) / (2 * master_clk) +- * +- * stored as 20 bit number with an offset of 4 bit: +- * sfr = SFR << 4; +- */ +- +- tmp = stb0899_do_div((((u64)srate) << 21) + master_clk, 2 * master_clk); +- tmp <<= 4; +- +- sfr[0] = tmp >> 16; +- sfr[1] = tmp >> 8; +- sfr[2] = tmp; +- +- stb0899_write_regs(state, STB0899_SFRH, sfr, 3); +- +- return srate; +-} +- +-/* +- * stb0899_calc_derot_time +- * Compute the amount of time needed by the derotator to lock +- * SymbolRate: Symbol rate +- * return: derotator time constant (ms) +- */ +-static long stb0899_calc_derot_time(long srate) +-{ +- if (srate > 0) +- return (100000 / (srate / 1000)); +- else +- return 0; +-} +- +-/* +- * stb0899_carr_width +- * Compute the width of the carrier +- * return: width of carrier (kHz or Mhz) +- */ +-long stb0899_carr_width(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- +- return (internal->srate + (internal->srate * internal->rolloff) / 100); +-} +- +-/* +- * stb0899_first_subrange +- * Compute the first subrange of the search +- */ +-static void stb0899_first_subrange(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- struct stb0899_config *config = state->config; +- +- int range = 0; +- u32 bandwidth = 0; +- +- if (config->tuner_get_bandwidth) { +- stb0899_i2c_gate_ctrl(&state->frontend, 1); +- config->tuner_get_bandwidth(&state->frontend, &bandwidth); +- stb0899_i2c_gate_ctrl(&state->frontend, 0); +- range = bandwidth - stb0899_carr_width(state) / 2; +- } +- +- if (range > 0) +- internal->sub_range = min(internal->srch_range, range); +- else +- internal->sub_range = 0; +- +- internal->freq = params->freq; +- internal->tuner_offst = 0L; +- internal->sub_dir = 1; +-} +- +-/* +- * stb0899_check_tmg +- * check for timing lock +- * internal.Ttiming: time to wait for loop lock +- */ +-static enum stb0899_status stb0899_check_tmg(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- int lock; +- u8 reg; +- s8 timing; +- +- msleep(internal->t_derot); +- +- stb0899_write_reg(state, STB0899_RTF, 0xf2); +- reg = stb0899_read_reg(state, STB0899_TLIR); +- lock = STB0899_GETFIELD(TLIR_TMG_LOCK_IND, reg); +- timing = stb0899_read_reg(state, STB0899_RTF); +- +- if (lock >= 42) { +- if ((lock > 48) && (abs(timing) >= 110)) { +- internal->status = ANALOGCARRIER; +- dprintk(state->verbose, FE_DEBUG, 1, "-->ANALOG Carrier !"); +- } else { +- internal->status = TIMINGOK; +- dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK !"); +- } +- } else { +- internal->status = NOTIMING; +- dprintk(state->verbose, FE_DEBUG, 1, "-->NO TIMING !"); +- } +- return internal->status; +-} +- +-/* +- * stb0899_search_tmg +- * perform a fs/2 zig-zag to find timing +- */ +-static enum stb0899_status stb0899_search_tmg(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- +- short int derot_step, derot_freq = 0, derot_limit, next_loop = 3; +- int index = 0; +- u8 cfr[2]; +- +- internal->status = NOTIMING; +- +- /* timing loop computation & symbol rate optimisation */ +- derot_limit = (internal->sub_range / 2L) / internal->mclk; +- derot_step = (params->srate / 2L) / internal->mclk; +- +- while ((stb0899_check_tmg(state) != TIMINGOK) && next_loop) { +- index++; +- derot_freq += index * internal->direction * derot_step; /* next derot zig zag position */ +- +- if (abs(derot_freq) > derot_limit) +- next_loop--; +- +- if (next_loop) { +- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); +- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); +- stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ +- } +- internal->direction = -internal->direction; /* Change zigzag direction */ +- } +- +- if (internal->status == TIMINGOK) { +- stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ +- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); +- dprintk(state->verbose, FE_DEBUG, 1, "------->TIMING OK ! Derot Freq = %d", internal->derot_freq); +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_check_carrier +- * Check for carrier found +- */ +-static enum stb0899_status stb0899_check_carrier(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- u8 reg; +- +- msleep(internal->t_derot); /* wait for derotator ok */ +- +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- reg = stb0899_read_reg(state, STB0899_DSTATUS); +- dprintk(state->verbose, FE_DEBUG, 1, "--------------------> STB0899_DSTATUS=[0x%02x]", reg); +- if (STB0899_GETFIELD(CARRIER_FOUND, reg)) { +- internal->status = CARRIEROK; +- dprintk(state->verbose, FE_DEBUG, 1, "-------------> CARRIEROK !"); +- } else { +- internal->status = NOCARRIER; +- dprintk(state->verbose, FE_DEBUG, 1, "-------------> NOCARRIER !"); +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_search_carrier +- * Search for a QPSK carrier with the derotator +- */ +-static enum stb0899_status stb0899_search_carrier(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- +- short int derot_freq = 0, last_derot_freq = 0, derot_limit, next_loop = 3; +- int index = 0; +- u8 cfr[2]; +- u8 reg; +- +- internal->status = NOCARRIER; +- derot_limit = (internal->sub_range / 2L) / internal->mclk; +- derot_freq = internal->derot_freq; +- +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- do { +- dprintk(state->verbose, FE_DEBUG, 1, "Derot Freq=%d, mclk=%d", derot_freq, internal->mclk); +- if (stb0899_check_carrier(state) == NOCARRIER) { +- index++; +- last_derot_freq = derot_freq; +- derot_freq += index * internal->direction * internal->derot_step; /* next zig zag derotator position */ +- +- if(abs(derot_freq) > derot_limit) +- next_loop--; +- +- if (next_loop) { +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); +- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); +- stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ +- } +- } +- +- internal->direction = -internal->direction; /* Change zigzag direction */ +- } while ((internal->status != CARRIEROK) && next_loop); +- +- if (internal->status == CARRIEROK) { +- stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ +- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); +- dprintk(state->verbose, FE_DEBUG, 1, "----> CARRIER OK !, Derot Freq=%d", internal->derot_freq); +- } else { +- internal->derot_freq = last_derot_freq; +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_check_data +- * Check for data found +- */ +-static enum stb0899_status stb0899_check_data(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- +- int lock = 0, index = 0, dataTime = 500, loop; +- u8 reg; +- +- internal->status = NODATA; +- +- /* RESET FEC */ +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESACS, reg, 1); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- msleep(1); +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESACS, reg, 0); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- if (params->srate <= 2000000) +- dataTime = 2000; +- else if (params->srate <= 5000000) +- dataTime = 1500; +- else if (params->srate <= 15000000) +- dataTime = 1000; +- else +- dataTime = 500; +- +- /* clear previous failed END_LOOPVIT */ +- stb0899_read_reg(state, STB0899_VSTATUS); +- +- stb0899_write_reg(state, STB0899_DSTATUS2, 0x00); /* force search loop */ +- while (1) { +- /* WARNING! VIT LOCKED has to be tested before VIT_END_LOOOP */ +- reg = stb0899_read_reg(state, STB0899_VSTATUS); +- lock = STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg); +- loop = STB0899_GETFIELD(VSTATUS_END_LOOPVIT, reg); +- +- if (lock || loop || (index > dataTime)) +- break; +- index++; +- } +- +- if (lock) { /* DATA LOCK indicator */ +- internal->status = DATAOK; +- dprintk(state->verbose, FE_DEBUG, 1, "-----------------> DATA OK !"); +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_search_data +- * Search for a QPSK carrier with the derotator +- */ +-static enum stb0899_status stb0899_search_data(struct stb0899_state *state) +-{ +- short int derot_freq, derot_step, derot_limit, next_loop = 3; +- u8 cfr[2]; +- u8 reg; +- int index = 1; +- +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- +- derot_step = (params->srate / 4L) / internal->mclk; +- derot_limit = (internal->sub_range / 2L) / internal->mclk; +- derot_freq = internal->derot_freq; +- +- do { +- if ((internal->status != CARRIEROK) || (stb0899_check_data(state) != DATAOK)) { +- +- derot_freq += index * internal->direction * derot_step; /* next zig zag derotator position */ +- if (abs(derot_freq) > derot_limit) +- next_loop--; +- +- if (next_loop) { +- dprintk(state->verbose, FE_DEBUG, 1, "Derot freq=%d, mclk=%d", derot_freq, internal->mclk); +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- STB0899_SETFIELD_VAL(CFRM, cfr[0], MSB(state->config->inversion * derot_freq)); +- STB0899_SETFIELD_VAL(CFRL, cfr[1], LSB(state->config->inversion * derot_freq)); +- stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* derotator frequency */ +- +- stb0899_check_carrier(state); +- index++; +- } +- } +- internal->direction = -internal->direction; /* change zig zag direction */ +- } while ((internal->status != DATAOK) && next_loop); +- +- if (internal->status == DATAOK) { +- stb0899_read_regs(state, STB0899_CFRM, cfr, 2); /* get derotator frequency */ +- internal->derot_freq = state->config->inversion * MAKEWORD16(cfr[0], cfr[1]); +- dprintk(state->verbose, FE_DEBUG, 1, "------> DATAOK ! Derot Freq=%d", internal->derot_freq); +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_check_range +- * check if the found frequency is in the correct range +- */ +-static enum stb0899_status stb0899_check_range(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- +- int range_offst, tp_freq; +- +- range_offst = internal->srch_range / 2000; +- tp_freq = internal->freq + (internal->derot_freq * internal->mclk) / 1000; +- +- if ((tp_freq >= params->freq - range_offst) && (tp_freq <= params->freq + range_offst)) { +- internal->status = RANGEOK; +- dprintk(state->verbose, FE_DEBUG, 1, "----> RANGEOK !"); +- } else { +- internal->status = OUTOFRANGE; +- dprintk(state->verbose, FE_DEBUG, 1, "----> OUT OF RANGE !"); +- } +- +- return internal->status; +-} +- +-/* +- * NextSubRange +- * Compute the next subrange of the search +- */ +-static void next_sub_range(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_params *params = &state->params; +- +- long old_sub_range; +- +- if (internal->sub_dir > 0) { +- old_sub_range = internal->sub_range; +- internal->sub_range = min((internal->srch_range / 2) - +- (internal->tuner_offst + internal->sub_range / 2), +- internal->sub_range); +- +- if (internal->sub_range < 0) +- internal->sub_range = 0; +- +- internal->tuner_offst += (old_sub_range + internal->sub_range) / 2; +- } +- +- internal->freq = params->freq + (internal->sub_dir * internal->tuner_offst) / 1000; +- internal->sub_dir = -internal->sub_dir; +-} +- +-/* +- * stb0899_dvbs_algo +- * Search for a signal, timing, carrier and data for a +- * given frequency in a given range +- */ +-enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state) +-{ +- struct stb0899_params *params = &state->params; +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- +- u8 bclc, reg; +- u8 cfr[2]; +- u8 eq_const[10]; +- s32 clnI = 3; +- u32 bandwidth = 0; +- +- /* BETA values rated @ 99MHz */ +- s32 betaTab[5][4] = { +- /* 5 10 20 30MBps */ +- { 37, 34, 32, 31 }, /* QPSK 1/2 */ +- { 37, 35, 33, 31 }, /* QPSK 2/3 */ +- { 37, 35, 33, 31 }, /* QPSK 3/4 */ +- { 37, 36, 33, 32 }, /* QPSK 5/6 */ +- { 37, 36, 33, 32 } /* QPSK 7/8 */ +- }; +- +- internal->direction = 1; +- +- stb0899_set_srate(state, internal->master_clk, params->srate); +- /* Carrier loop optimization versus symbol rate for acquisition*/ +- if (params->srate <= 5000000) { +- stb0899_write_reg(state, STB0899_ACLC, 0x89); +- bclc = stb0899_read_reg(state, STB0899_BCLC); +- STB0899_SETFIELD_VAL(BETA, bclc, 0x1c); +- stb0899_write_reg(state, STB0899_BCLC, bclc); +- clnI = 0; +- } else if (params->srate <= 15000000) { +- stb0899_write_reg(state, STB0899_ACLC, 0xc9); +- bclc = stb0899_read_reg(state, STB0899_BCLC); +- STB0899_SETFIELD_VAL(BETA, bclc, 0x22); +- stb0899_write_reg(state, STB0899_BCLC, bclc); +- clnI = 1; +- } else if(params->srate <= 25000000) { +- stb0899_write_reg(state, STB0899_ACLC, 0x89); +- bclc = stb0899_read_reg(state, STB0899_BCLC); +- STB0899_SETFIELD_VAL(BETA, bclc, 0x27); +- stb0899_write_reg(state, STB0899_BCLC, bclc); +- clnI = 2; +- } else { +- stb0899_write_reg(state, STB0899_ACLC, 0xc8); +- bclc = stb0899_read_reg(state, STB0899_BCLC); +- STB0899_SETFIELD_VAL(BETA, bclc, 0x29); +- stb0899_write_reg(state, STB0899_BCLC, bclc); +- clnI = 3; +- } +- +- dprintk(state->verbose, FE_DEBUG, 1, "Set the timing loop to acquisition"); +- /* Set the timing loop to acquisition */ +- stb0899_write_reg(state, STB0899_RTC, 0x46); +- stb0899_write_reg(state, STB0899_CFD, 0xee); +- +- /* !! WARNING !! +- * Do not read any status variables while acquisition, +- * If any needed, read before the acquisition starts +- * querying status while acquiring causes the +- * acquisition to go bad and hence no locks. +- */ +- dprintk(state->verbose, FE_DEBUG, 1, "Derot Percent=%d Srate=%d mclk=%d", +- internal->derot_percent, params->srate, internal->mclk); +- +- /* Initial calculations */ +- internal->derot_step = internal->derot_percent * (params->srate / 1000L) / internal->mclk; /* DerotStep/1000 * Fsymbol */ +- internal->t_derot = stb0899_calc_derot_time(params->srate); +- internal->t_data = 500; +- +- dprintk(state->verbose, FE_DEBUG, 1, "RESET stream merger"); +- /* RESET Stream merger */ +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESRS, reg, 1); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- /* +- * Set KDIVIDER to an intermediate value between +- * 1/2 and 7/8 for acquisition +- */ +- reg = stb0899_read_reg(state, STB0899_DEMAPVIT); +- STB0899_SETFIELD_VAL(DEMAPVIT_KDIVIDER, reg, 60); +- stb0899_write_reg(state, STB0899_DEMAPVIT, reg); +- +- stb0899_write_reg(state, STB0899_EQON, 0x01); /* Equalizer OFF while acquiring */ +- stb0899_write_reg(state, STB0899_VITSYNC, 0x19); +- +- stb0899_first_subrange(state); +- do { +- /* Initialisations */ +- cfr[0] = cfr[1] = 0; +- stb0899_write_regs(state, STB0899_CFRM, cfr, 2); /* RESET derotator frequency */ +- +- stb0899_write_reg(state, STB0899_RTF, 0); +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 1); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- internal->derot_freq = 0; +- internal->status = NOAGC1; +- +- /* enable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 1); +- +- /* Move tuner to frequency */ +- dprintk(state->verbose, FE_DEBUG, 1, "Tuner set frequency"); +- if (state->config->tuner_set_frequency) +- state->config->tuner_set_frequency(&state->frontend, internal->freq); +- +- if (state->config->tuner_get_frequency) +- state->config->tuner_get_frequency(&state->frontend, &internal->freq); +- +- msleep(internal->t_agc1 + internal->t_agc2 + internal->t_derot); /* AGC1, AGC2 and timing loop */ +- dprintk(state->verbose, FE_DEBUG, 1, "current derot freq=%d", internal->derot_freq); +- internal->status = AGC1OK; +- +- /* There is signal in the band */ +- if (config->tuner_get_bandwidth) +- config->tuner_get_bandwidth(&state->frontend, &bandwidth); +- +- /* disable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 0); +- +- if (params->srate <= bandwidth / 2) +- stb0899_search_tmg(state); /* For low rates (SCPC) */ +- else +- stb0899_check_tmg(state); /* For high rates (MCPC) */ +- +- if (internal->status == TIMINGOK) { +- dprintk(state->verbose, FE_DEBUG, 1, +- "TIMING OK ! Derot freq=%d, mclk=%d", +- internal->derot_freq, internal->mclk); +- +- if (stb0899_search_carrier(state) == CARRIEROK) { /* Search for carrier */ +- dprintk(state->verbose, FE_DEBUG, 1, +- "CARRIER OK ! Derot freq=%d, mclk=%d", +- internal->derot_freq, internal->mclk); +- +- if (stb0899_search_data(state) == DATAOK) { /* Check for data */ +- dprintk(state->verbose, FE_DEBUG, 1, +- "DATA OK ! Derot freq=%d, mclk=%d", +- internal->derot_freq, internal->mclk); +- +- if (stb0899_check_range(state) == RANGEOK) { +- dprintk(state->verbose, FE_DEBUG, 1, +- "RANGE OK ! derot freq=%d, mclk=%d", +- internal->derot_freq, internal->mclk); +- +- internal->freq = params->freq + ((internal->derot_freq * internal->mclk) / 1000); +- reg = stb0899_read_reg(state, STB0899_PLPARM); +- internal->fecrate = STB0899_GETFIELD(VITCURPUN, reg); +- dprintk(state->verbose, FE_DEBUG, 1, +- "freq=%d, internal resultant freq=%d", +- params->freq, internal->freq); +- +- dprintk(state->verbose, FE_DEBUG, 1, +- "internal puncture rate=%d", +- internal->fecrate); +- } +- } +- } +- } +- if (internal->status != RANGEOK) +- next_sub_range(state); +- +- } while (internal->sub_range && internal->status != RANGEOK); +- +- /* Set the timing loop to tracking */ +- stb0899_write_reg(state, STB0899_RTC, 0x33); +- stb0899_write_reg(state, STB0899_CFD, 0xf7); +- /* if locked and range ok, set Kdiv */ +- if (internal->status == RANGEOK) { +- dprintk(state->verbose, FE_DEBUG, 1, "Locked & Range OK !"); +- stb0899_write_reg(state, STB0899_EQON, 0x41); /* Equalizer OFF while acquiring */ +- stb0899_write_reg(state, STB0899_VITSYNC, 0x39); /* SN to b'11 for acquisition */ +- +- /* +- * Carrier loop optimization versus +- * symbol Rate/Puncture Rate for Tracking +- */ +- reg = stb0899_read_reg(state, STB0899_BCLC); +- switch (internal->fecrate) { +- case STB0899_FEC_1_2: /* 13 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 0x1a); +- STB0899_SETFIELD_VAL(BETA, reg, betaTab[0][clnI]); +- stb0899_write_reg(state, STB0899_BCLC, reg); +- break; +- case STB0899_FEC_2_3: /* 18 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 44); +- STB0899_SETFIELD_VAL(BETA, reg, betaTab[1][clnI]); +- stb0899_write_reg(state, STB0899_BCLC, reg); +- break; +- case STB0899_FEC_3_4: /* 21 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 60); +- STB0899_SETFIELD_VAL(BETA, reg, betaTab[2][clnI]); +- stb0899_write_reg(state, STB0899_BCLC, reg); +- break; +- case STB0899_FEC_5_6: /* 24 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 75); +- STB0899_SETFIELD_VAL(BETA, reg, betaTab[3][clnI]); +- stb0899_write_reg(state, STB0899_BCLC, reg); +- break; +- case STB0899_FEC_6_7: /* 25 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 88); +- stb0899_write_reg(state, STB0899_ACLC, 0x88); +- stb0899_write_reg(state, STB0899_BCLC, 0x9a); +- break; +- case STB0899_FEC_7_8: /* 26 */ +- stb0899_write_reg(state, STB0899_DEMAPVIT, 94); +- STB0899_SETFIELD_VAL(BETA, reg, betaTab[4][clnI]); +- stb0899_write_reg(state, STB0899_BCLC, reg); +- break; +- default: +- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported Puncture Rate"); +- break; +- } +- /* release stream merger RESET */ +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESRS, reg, 0); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- /* disable carrier detector */ +- reg = stb0899_read_reg(state, STB0899_CFD); +- STB0899_SETFIELD_VAL(CFD_ON, reg, 0); +- stb0899_write_reg(state, STB0899_CFD, reg); +- +- stb0899_read_regs(state, STB0899_EQUAI1, eq_const, 10); +- } +- +- return internal->status; +-} +- +-/* +- * stb0899_dvbs2_config_uwp +- * Configure UWP state machine +- */ +-static void stb0899_dvbs2_config_uwp(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- u32 uwp1, uwp2, uwp3, reg; +- +- uwp1 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); +- uwp2 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL2); +- uwp3 = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL3); +- +- STB0899_SETFIELD_VAL(UWP_ESN0_AVE, uwp1, config->esno_ave); +- STB0899_SETFIELD_VAL(UWP_ESN0_QUANT, uwp1, config->esno_quant); +- STB0899_SETFIELD_VAL(UWP_TH_SOF, uwp1, config->uwp_threshold_sof); +- +- STB0899_SETFIELD_VAL(FE_COARSE_TRK, uwp2, internal->av_frame_coarse); +- STB0899_SETFIELD_VAL(FE_FINE_TRK, uwp2, internal->av_frame_fine); +- STB0899_SETFIELD_VAL(UWP_MISS_TH, uwp2, config->miss_threshold); +- +- STB0899_SETFIELD_VAL(UWP_TH_ACQ, uwp3, config->uwp_threshold_acq); +- STB0899_SETFIELD_VAL(UWP_TH_TRACK, uwp3, config->uwp_threshold_track); +- +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL1, STB0899_OFF0_UWP_CNTRL1, uwp1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL2, STB0899_OFF0_UWP_CNTRL2, uwp2); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_UWP_CNTRL3, STB0899_OFF0_UWP_CNTRL3, uwp3); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, SOF_SRCH_TO); +- STB0899_SETFIELD_VAL(SOF_SEARCH_TIMEOUT, reg, config->sof_search_timeout); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_SOF_SRCH_TO, STB0899_OFF0_SOF_SRCH_TO, reg); +-} +- +-/* +- * stb0899_dvbs2_config_csm_auto +- * Set CSM to AUTO mode +- */ +-static void stb0899_dvbs2_config_csm_auto(struct stb0899_state *state) +-{ +- u32 reg; +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); +- STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, reg, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, reg); +-} +- +-static long Log2Int(int number) +-{ +- int i; +- +- i = 0; +- while ((1 << i) <= abs(number)) +- i++; +- +- if (number == 0) +- i = 1; +- +- return i - 1; +-} +- +-/* +- * stb0899_dvbs2_calc_srate +- * compute BTR_NOM_FREQ for the symbol rate +- */ +-static u32 stb0899_dvbs2_calc_srate(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- +- u32 dec_ratio, dec_rate, decim, remain, intval, btr_nom_freq; +- u32 master_clk, srate; +- +- dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); +- dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; +- dec_rate = Log2Int(dec_ratio); +- decim = 1 << dec_rate; +- master_clk = internal->master_clk / 1000; +- srate = internal->srate / 1000; +- +- if (decim <= 4) { +- intval = (decim * (1 << (config->btr_nco_bits - 1))) / master_clk; +- remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; +- } else { +- intval = (1 << (config->btr_nco_bits - 1)) / (master_clk / 100) * decim / 100; +- remain = (decim * (1 << (config->btr_nco_bits - 1))) % master_clk; +- } +- btr_nom_freq = (intval * srate) + ((remain * srate) / master_clk); +- +- return btr_nom_freq; +-} +- +-/* +- * stb0899_dvbs2_calc_dev +- * compute the correction to be applied to symbol rate +- */ +-static u32 stb0899_dvbs2_calc_dev(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- u32 dec_ratio, correction, master_clk, srate; +- +- dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); +- dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; +- +- master_clk = internal->master_clk / 1000; /* for integer Caculation*/ +- srate = internal->srate / 1000; /* for integer Caculation*/ +- correction = (512 * master_clk) / (2 * dec_ratio * srate); +- +- return correction; +-} +- +-/* +- * stb0899_dvbs2_set_srate +- * Set DVBS2 symbol rate +- */ +-static void stb0899_dvbs2_set_srate(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- +- u32 dec_ratio, dec_rate, win_sel, decim, f_sym, btr_nom_freq; +- u32 correction, freq_adj, band_lim, decim_cntrl, reg; +- u8 anti_alias; +- +- /*set decimation to 1*/ +- dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); +- dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; +- dec_rate = Log2Int(dec_ratio); +- +- win_sel = 0; +- if (dec_rate >= 5) +- win_sel = dec_rate - 4; +- +- decim = (1 << dec_rate); +- /* (FSamp/Fsymbol *100) for integer Caculation */ +- f_sym = internal->master_clk / ((decim * internal->srate) / 1000); +- +- if (f_sym <= 2250) /* don't band limit signal going into btr block*/ +- band_lim = 1; +- else +- band_lim = 0; /* band limit signal going into btr block*/ +- +- decim_cntrl = ((win_sel << 3) & 0x18) + ((band_lim << 5) & 0x20) + (dec_rate & 0x7); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DECIM_CNTRL, STB0899_OFF0_DECIM_CNTRL, decim_cntrl); +- +- if (f_sym <= 3450) +- anti_alias = 0; +- else if (f_sym <= 4250) +- anti_alias = 1; +- else +- anti_alias = 2; +- +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ANTI_ALIAS_SEL, STB0899_OFF0_ANTI_ALIAS_SEL, anti_alias); +- btr_nom_freq = stb0899_dvbs2_calc_srate(state); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_NOM_FREQ, STB0899_OFF0_BTR_NOM_FREQ, btr_nom_freq); +- +- correction = stb0899_dvbs2_calc_dev(state); +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); +- STB0899_SETFIELD_VAL(BTR_FREQ_CORR, reg, correction); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); +- +- /* scale UWP+CSM frequency to sample rate*/ +- freq_adj = internal->srate / (internal->master_clk / 4096); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_FREQ_ADJ_SCALE, STB0899_OFF0_FREQ_ADJ_SCALE, freq_adj); +-} +- +-/* +- * stb0899_dvbs2_set_btr_loopbw +- * set bit timing loop bandwidth as a percentage of the symbol rate +- */ +-static void stb0899_dvbs2_set_btr_loopbw(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- +- u32 sym_peak = 23, zeta = 707, loopbw_percent = 60; +- s32 dec_ratio, dec_rate, k_btr1_rshft, k_btr1, k_btr0_rshft; +- s32 k_btr0, k_btr2_rshft, k_direct_shift, k_indirect_shift; +- u32 decim, K, wn, k_direct, k_indirect; +- u32 reg; +- +- dec_ratio = (internal->master_clk * 2) / (5 * internal->srate); +- dec_ratio = (dec_ratio == 0) ? 1 : dec_ratio; +- dec_rate = Log2Int(dec_ratio); +- decim = (1 << dec_rate); +- +- sym_peak *= 576000; +- K = (1 << config->btr_nco_bits) / (internal->master_clk / 1000); +- K *= (internal->srate / 1000000) * decim; /*k=k 10^-8*/ +- +- if (K != 0) { +- K = sym_peak / K; +- wn = (4 * zeta * zeta) + 1000000; +- wn = (2 * (loopbw_percent * 1000) * 40 * zeta) /wn; /*wn =wn 10^-8*/ +- +- k_indirect = (wn * wn) / K; +- k_indirect = k_indirect; /*kindirect = kindirect 10^-6*/ +- k_direct = (2 * wn * zeta) / K; /*kDirect = kDirect 10^-2*/ +- k_direct *= 100; +- +- k_direct_shift = Log2Int(k_direct) - Log2Int(10000) - 2; +- k_btr1_rshft = (-1 * k_direct_shift) + config->btr_gain_shift_offset; +- k_btr1 = k_direct / (1 << k_direct_shift); +- k_btr1 /= 10000; +- +- k_indirect_shift = Log2Int(k_indirect + 15) - 20 /*- 2*/; +- k_btr0_rshft = (-1 * k_indirect_shift) + config->btr_gain_shift_offset; +- k_btr0 = k_indirect * (1 << (-k_indirect_shift)); +- k_btr0 /= 1000000; +- +- k_btr2_rshft = 0; +- if (k_btr0_rshft > 15) { +- k_btr2_rshft = k_btr0_rshft - 15; +- k_btr0_rshft = 15; +- } +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_LOOP_GAIN); +- STB0899_SETFIELD_VAL(KBTR0_RSHFT, reg, k_btr0_rshft); +- STB0899_SETFIELD_VAL(KBTR0, reg, k_btr0); +- STB0899_SETFIELD_VAL(KBTR1_RSHFT, reg, k_btr1_rshft); +- STB0899_SETFIELD_VAL(KBTR1, reg, k_btr1); +- STB0899_SETFIELD_VAL(KBTR2_RSHFT, reg, k_btr2_rshft); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, reg); +- } else +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_LOOP_GAIN, STB0899_OFF0_BTR_LOOP_GAIN, 0xc4c4f); +-} +- +-/* +- * stb0899_dvbs2_set_carr_freq +- * set nominal frequency for carrier search +- */ +-static void stb0899_dvbs2_set_carr_freq(struct stb0899_state *state, s32 carr_freq, u32 master_clk) +-{ +- struct stb0899_config *config = state->config; +- s32 crl_nom_freq; +- u32 reg; +- +- crl_nom_freq = (1 << config->crl_nco_bits) / master_clk; +- crl_nom_freq *= carr_freq; +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); +- STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, crl_nom_freq); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); +-} +- +-/* +- * stb0899_dvbs2_init_calc +- * Initialize DVBS2 UWP, CSM, carrier and timing loops +- */ +-static void stb0899_dvbs2_init_calc(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- s32 steps, step_size; +- u32 range, reg; +- +- /* config uwp and csm */ +- stb0899_dvbs2_config_uwp(state); +- stb0899_dvbs2_config_csm_auto(state); +- +- /* initialize BTR */ +- stb0899_dvbs2_set_srate(state); +- stb0899_dvbs2_set_btr_loopbw(state); +- +- if (internal->srate / 1000000 >= 15) +- step_size = (1 << 17) / 5; +- else if (internal->srate / 1000000 >= 10) +- step_size = (1 << 17) / 7; +- else if (internal->srate / 1000000 >= 5) +- step_size = (1 << 17) / 10; +- else +- step_size = (1 << 17) / 4; +- +- range = internal->srch_range / 1000000; +- steps = (10 * range * (1 << 17)) / (step_size * (internal->srate / 1000000)); +- steps = (steps + 6) / 10; +- steps = (steps == 0) ? 1 : steps; +- if (steps % 2 == 0) +- stb0899_dvbs2_set_carr_freq(state, internal->center_freq - +- (internal->step_size * (internal->srate / 20000000)), +- (internal->master_clk) / 1000000); +- else +- stb0899_dvbs2_set_carr_freq(state, internal->center_freq, (internal->master_clk) / 1000000); +- +- /*Set Carrier Search params (zigzag, num steps and freq step size*/ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, ACQ_CNTRL2); +- STB0899_SETFIELD_VAL(ZIGZAG, reg, 1); +- STB0899_SETFIELD_VAL(NUM_STEPS, reg, steps); +- STB0899_SETFIELD_VAL(FREQ_STEPSIZE, reg, step_size); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQ_CNTRL2, STB0899_OFF0_ACQ_CNTRL2, reg); +-} +- +-/* +- * stb0899_dvbs2_btr_init +- * initialize the timing loop +- */ +-static void stb0899_dvbs2_btr_init(struct stb0899_state *state) +-{ +- u32 reg; +- +- /* set enable BTR loopback */ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_CNTRL); +- STB0899_SETFIELD_VAL(INTRP_PHS_SENSE, reg, 1); +- STB0899_SETFIELD_VAL(BTR_ERR_ENA, reg, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_CNTRL, STB0899_OFF0_BTR_CNTRL, reg); +- +- /* fix btr freq accum at 0 */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x10000000); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_FREQ_INIT, STB0899_OFF0_BTR_FREQ_INIT, 0x00000000); +- +- /* fix btr freq accum at 0 */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x10000000); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_BTR_PHS_INIT, STB0899_OFF0_BTR_PHS_INIT, 0x00000000); +-} +- +-/* +- * stb0899_dvbs2_reacquire +- * trigger a DVB-S2 acquisition +- */ +-static void stb0899_dvbs2_reacquire(struct stb0899_state *state) +-{ +- u32 reg = 0; +- +- /* demod soft reset */ +- STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); +- +- /*Reset Timing Loop */ +- stb0899_dvbs2_btr_init(state); +- +- /* reset Carrier loop */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, (1 << 30)); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_FREQ_INIT, STB0899_OFF0_CRL_FREQ_INIT, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_LOOP_GAIN, STB0899_OFF0_CRL_LOOP_GAIN, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, (1 << 30)); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_PHS_INIT, STB0899_OFF0_CRL_PHS_INIT, 0); +- +- /*release demod soft reset */ +- reg = 0; +- STB0899_SETFIELD_VAL(DVBS2_RESET, reg, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_RESET_CNTRL, STB0899_OFF0_RESET_CNTRL, reg); +- +- /* start acquisition process */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_ACQUIRE_TRIG, STB0899_OFF0_ACQUIRE_TRIG, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_LOCK_LOST, STB0899_OFF0_LOCK_LOST, 0); +- +- /* equalizer Init */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 1); +- +- /*Start equilizer */ +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQUALIZER_INIT, STB0899_OFF0_EQUALIZER_INIT, 0); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); +- STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0); +- STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 0); +- STB0899_SETFIELD_VAL(EQ_DELAY, reg, 0x05); +- STB0899_SETFIELD_VAL(EQ_ADAPT_MODE, reg, 0x01); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); +- +- /* RESET Packet delineator */ +- stb0899_write_reg(state, STB0899_PDELCTRL, 0x4a); +-} +- +-/* +- * stb0899_dvbs2_get_dmd_status +- * get DVB-S2 Demod LOCK status +- */ +-static enum stb0899_status stb0899_dvbs2_get_dmd_status(struct stb0899_state *state, int timeout) +-{ +- int time = -10, lock = 0, uwp, csm; +- u32 reg; +- +- do { +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STATUS); +- dprintk(state->verbose, FE_DEBUG, 1, "DMD_STATUS=[0x%02x]", reg); +- if (STB0899_GETFIELD(IF_AGC_LOCK, reg)) +- dprintk(state->verbose, FE_DEBUG, 1, "------------->IF AGC LOCKED !"); +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); +- dprintk(state->verbose, FE_DEBUG, 1, "----------->DMD STAT2=[0x%02x]", reg); +- uwp = STB0899_GETFIELD(UWP_LOCK, reg); +- csm = STB0899_GETFIELD(CSM_LOCK, reg); +- if (uwp && csm) +- lock = 1; +- +- time += 10; +- msleep(10); +- +- } while ((!lock) && (time <= timeout)); +- +- if (lock) { +- dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 LOCK !"); +- return DVBS2_DEMOD_LOCK; +- } else { +- return DVBS2_DEMOD_NOLOCK; +- } +-} +- +-/* +- * stb0899_dvbs2_get_data_lock +- * get FEC status +- */ +-static int stb0899_dvbs2_get_data_lock(struct stb0899_state *state, int timeout) +-{ +- int time = 0, lock = 0; +- u8 reg; +- +- while ((!lock) && (time < timeout)) { +- reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); +- dprintk(state->verbose, FE_DEBUG, 1, "---------> CFGPDELSTATUS=[0x%02x]", reg); +- lock = STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg); +- time++; +- } +- +- return lock; +-} +- +-/* +- * stb0899_dvbs2_get_fec_status +- * get DVB-S2 FEC LOCK status +- */ +-static enum stb0899_status stb0899_dvbs2_get_fec_status(struct stb0899_state *state, int timeout) +-{ +- int time = 0, Locked; +- +- do { +- Locked = stb0899_dvbs2_get_data_lock(state, 1); +- time++; +- msleep(1); +- +- } while ((!Locked) && (time < timeout)); +- +- if (Locked) { +- dprintk(state->verbose, FE_DEBUG, 1, "---------->DVB-S2 FEC LOCK !"); +- return DVBS2_FEC_LOCK; +- } else { +- return DVBS2_FEC_NOLOCK; +- } +-} +- +- +-/* +- * stb0899_dvbs2_init_csm +- * set parameters for manual mode +- */ +-static void stb0899_dvbs2_init_csm(struct stb0899_state *state, int pilots, enum stb0899_modcod modcod) +-{ +- struct stb0899_internal *internal = &state->internal; +- +- s32 dvt_tbl = 1, two_pass = 0, agc_gain = 6, agc_shift = 0, loop_shift = 0, phs_diff_thr = 0x80; +- s32 gamma_acq, gamma_rho_acq, gamma_trk, gamma_rho_trk, lock_count_thr; +- u32 csm1, csm2, csm3, csm4; +- +- if (((internal->master_clk / internal->srate) <= 4) && (modcod <= 11) && (pilots == 1)) { +- switch (modcod) { +- case STB0899_QPSK_12: +- gamma_acq = 25; +- gamma_rho_acq = 2700; +- gamma_trk = 12; +- gamma_rho_trk = 180; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_35: +- gamma_acq = 38; +- gamma_rho_acq = 7182; +- gamma_trk = 14; +- gamma_rho_trk = 308; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_23: +- gamma_acq = 42; +- gamma_rho_acq = 9408; +- gamma_trk = 17; +- gamma_rho_trk = 476; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_34: +- gamma_acq = 53; +- gamma_rho_acq = 16642; +- gamma_trk = 19; +- gamma_rho_trk = 646; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_45: +- gamma_acq = 53; +- gamma_rho_acq = 17119; +- gamma_trk = 22; +- gamma_rho_trk = 880; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_56: +- gamma_acq = 55; +- gamma_rho_acq = 19250; +- gamma_trk = 23; +- gamma_rho_trk = 989; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_89: +- gamma_acq = 60; +- gamma_rho_acq = 24240; +- gamma_trk = 24; +- gamma_rho_trk = 1176; +- lock_count_thr = 8; +- break; +- case STB0899_QPSK_910: +- gamma_acq = 66; +- gamma_rho_acq = 29634; +- gamma_trk = 24; +- gamma_rho_trk = 1176; +- lock_count_thr = 8; +- break; +- default: +- gamma_acq = 66; +- gamma_rho_acq = 29634; +- gamma_trk = 24; +- gamma_rho_trk = 1176; +- lock_count_thr = 8; +- break; +- } +- +- csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); +- STB0899_SETFIELD_VAL(CSM_AUTO_PARAM, csm1, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); +- +- csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); +- csm2 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL2); +- csm3 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL3); +- csm4 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL4); +- +- STB0899_SETFIELD_VAL(CSM_DVT_TABLE, csm1, dvt_tbl); +- STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, two_pass); +- STB0899_SETFIELD_VAL(CSM_AGC_GAIN, csm1, agc_gain); +- STB0899_SETFIELD_VAL(CSM_AGC_SHIFT, csm1, agc_shift); +- STB0899_SETFIELD_VAL(FE_LOOP_SHIFT, csm1, loop_shift); +- STB0899_SETFIELD_VAL(CSM_GAMMA_ACQ, csm2, gamma_acq); +- STB0899_SETFIELD_VAL(CSM_GAMMA_RHOACQ, csm2, gamma_rho_acq); +- STB0899_SETFIELD_VAL(CSM_GAMMA_TRACK, csm3, gamma_trk); +- STB0899_SETFIELD_VAL(CSM_GAMMA_RHOTRACK, csm3, gamma_rho_trk); +- STB0899_SETFIELD_VAL(CSM_LOCKCOUNT_THRESH, csm4, lock_count_thr); +- STB0899_SETFIELD_VAL(CSM_PHASEDIFF_THRESH, csm4, phs_diff_thr); +- +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL2, STB0899_OFF0_CSM_CNTRL2, csm2); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL3, STB0899_OFF0_CSM_CNTRL3, csm3); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL4, STB0899_OFF0_CSM_CNTRL4, csm4); +- } +-} +- +-/* +- * stb0899_dvbs2_get_srate +- * get DVB-S2 Symbol Rate +- */ +-static u32 stb0899_dvbs2_get_srate(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- +- u32 bTrNomFreq, srate, decimRate, intval1, intval2, reg; +- int div1, div2, rem1, rem2; +- +- div1 = config->btr_nco_bits / 2; +- div2 = config->btr_nco_bits - div1 - 1; +- +- bTrNomFreq = STB0899_READ_S2REG(STB0899_S2DEMOD, BTR_NOM_FREQ); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DECIM_CNTRL); +- decimRate = STB0899_GETFIELD(DECIM_RATE, reg); +- decimRate = (1 << decimRate); +- +- intval1 = internal->master_clk / (1 << div1); +- intval2 = bTrNomFreq / (1 << div2); +- +- rem1 = internal->master_clk % (1 << div1); +- rem2 = bTrNomFreq % (1 << div2); +- /* only for integer calculation */ +- srate = (intval1 * intval2) + ((intval1 * rem2) / (1 << div2)) + ((intval2 * rem1) / (1 << div1)); +- srate /= decimRate; /*symbrate = (btrnomfreq_register_val*MasterClock)/2^(27+decim_rate_field) */ +- +- return srate; +-} +- +-/* +- * stb0899_dvbs2_algo +- * Search for signal, timing, carrier and data for a given +- * frequency in a given range +- */ +-enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- enum stb0899_modcod modcod; +- +- s32 offsetfreq, searchTime, FecLockTime, pilots, iqSpectrum; +- int i = 0; +- u32 reg, csm1; +- +- if (internal->srate <= 2000000) { +- searchTime = 5000; /* 5000 ms max time to lock UWP and CSM, SYMB <= 2Mbs */ +- FecLockTime = 350; /* 350 ms max time to lock FEC, SYMB <= 2Mbs */ +- } else if (internal->srate <= 5000000) { +- searchTime = 2500; /* 2500 ms max time to lock UWP and CSM, 2Mbs < SYMB <= 5Mbs */ +- FecLockTime = 170; /* 170 ms max time to lock FEC, 2Mbs< SYMB <= 5Mbs */ +- } else if (internal->srate <= 10000000) { +- searchTime = 1500; /* 1500 ms max time to lock UWP and CSM, 5Mbs srate <= 15000000) { +- searchTime = 500; /* 500 ms max time to lock UWP and CSM, 10Mbs srate <= 20000000) { +- searchTime = 300; /* 300 ms max time to lock UWP and CSM, 15Mbs < SYMB <= 20Mbs */ +- FecLockTime = 30; /* 50 ms max time to lock FEC, 15Mbs< SYMB <= 20Mbs */ +- } else if (internal->srate <= 25000000) { +- searchTime = 250; /* 250 ms max time to lock UWP and CSM, 20 Mbs < SYMB <= 25Mbs */ +- FecLockTime = 25; /* 25 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ +- } else { +- searchTime = 150; /* 150 ms max time to lock UWP and CSM, SYMB > 25Mbs */ +- FecLockTime = 20; /* 20 ms max time to lock FEC, 20Mbs< SYMB <= 25Mbs */ +- } +- +- /* Maintain Stream Merger in reset during acquisition */ +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESRS, reg, 1); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- /* enable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 1); +- +- /* Move tuner to frequency */ +- if (state->config->tuner_set_frequency) +- state->config->tuner_set_frequency(&state->frontend, internal->freq); +- if (state->config->tuner_get_frequency) +- state->config->tuner_get_frequency(&state->frontend, &internal->freq); +- +- /* disable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 0); +- +- /* Set IF AGC to acquisition */ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); +- STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 4); +- STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 32); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); +- STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); +- +- /* Initialisation */ +- stb0899_dvbs2_init_calc(state); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); +- switch (internal->inversion) { +- case IQ_SWAP_OFF: +- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 0); +- break; +- case IQ_SWAP_ON: +- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); +- break; +- case IQ_SWAP_AUTO: /* use last successful search first */ +- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, 1); +- break; +- } +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); +- stb0899_dvbs2_reacquire(state); +- +- /* Wait for demod lock (UWP and CSM) */ +- internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); +- +- if (internal->status == DVBS2_DEMOD_LOCK) { +- dprintk(state->verbose, FE_DEBUG, 1, "------------> DVB-S2 DEMOD LOCK !"); +- i = 0; +- /* Demod Locked, check FEC status */ +- internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); +- +- /*If false lock (UWP and CSM Locked but no FEC) try 3 time max*/ +- while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { +- /* Read the frequency offset*/ +- offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); +- +- /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); +- STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); +- stb0899_dvbs2_reacquire(state); +- internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); +- i++; +- } +- } +- +- if (internal->status != DVBS2_FEC_LOCK) { +- if (internal->inversion == IQ_SWAP_AUTO) { +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); +- iqSpectrum = STB0899_GETFIELD(SPECTRUM_INVERT, reg); +- /* IQ Spectrum Inversion */ +- STB0899_SETFIELD_VAL(SPECTRUM_INVERT, reg, !iqSpectrum); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_DMD_CNTRL2, STB0899_OFF0_DMD_CNTRL2, reg); +- /* start acquistion process */ +- stb0899_dvbs2_reacquire(state); +- +- /* Wait for demod lock (UWP and CSM) */ +- internal->status = stb0899_dvbs2_get_dmd_status(state, searchTime); +- if (internal->status == DVBS2_DEMOD_LOCK) { +- i = 0; +- /* Demod Locked, check FEC */ +- internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); +- /*try thrice for false locks, (UWP and CSM Locked but no FEC) */ +- while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { +- /* Read the frequency offset*/ +- offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); +- +- /* Set the Nominal frequency to the found frequency offset for the next reacquire*/ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_NOM_FREQ); +- STB0899_SETFIELD_VAL(CRL_NOM_FREQ, reg, offsetfreq); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CRL_NOM_FREQ, STB0899_OFF0_CRL_NOM_FREQ, reg); +- +- stb0899_dvbs2_reacquire(state); +- internal->status = stb0899_dvbs2_get_fec_status(state, searchTime); +- i++; +- } +- } +-/* +- if (pParams->DVBS2State == FE_DVBS2_FEC_LOCKED) +- pParams->IQLocked = !iqSpectrum; +-*/ +- } +- } +- if (internal->status == DVBS2_FEC_LOCK) { +- dprintk(state->verbose, FE_DEBUG, 1, "----------------> DVB-S2 FEC Lock !"); +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); +- modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; +- pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; +- +- if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && +- (INRANGE(STB0899_QPSK_23, modcod, STB0899_QPSK_910)) && +- (pilots == 1)) { +- +- stb0899_dvbs2_init_csm(state, pilots, modcod); +- /* Wait for UWP,CSM and data LOCK 20ms max */ +- internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); +- +- i = 0; +- while ((internal->status != DVBS2_FEC_LOCK) && (i < 3)) { +- csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); +- STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); +- csm1 = STB0899_READ_S2REG(STB0899_S2DEMOD, CSM_CNTRL1); +- STB0899_SETFIELD_VAL(CSM_TWO_PASS, csm1, 0); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_CSM_CNTRL1, STB0899_OFF0_CSM_CNTRL1, csm1); +- +- internal->status = stb0899_dvbs2_get_fec_status(state, FecLockTime); +- i++; +- } +- } +- +- if ((((10 * internal->master_clk) / (internal->srate / 10)) <= 410) && +- (INRANGE(STB0899_QPSK_12, modcod, STB0899_QPSK_35)) && +- (pilots == 1)) { +- +- /* Equalizer Disable update */ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); +- STB0899_SETFIELD_VAL(EQ_DISABLE_UPDATE, reg, 1); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); +- } +- +- /* slow down the Equalizer once locked */ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, EQ_CNTRL); +- STB0899_SETFIELD_VAL(EQ_SHIFT, reg, 0x02); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_EQ_CNTRL, STB0899_OFF0_EQ_CNTRL, reg); +- +- /* Store signal parameters */ +- offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); +- +- offsetfreq = offsetfreq / ((1 << 30) / 1000); +- offsetfreq *= (internal->master_clk / 1000000); +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CNTRL2); +- if (STB0899_GETFIELD(SPECTRUM_INVERT, reg)) +- offsetfreq *= -1; +- +- internal->freq = internal->freq - offsetfreq; +- internal->srate = stb0899_dvbs2_get_srate(state); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); +- internal->modcod = STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 2; +- internal->pilots = STB0899_GETFIELD(UWP_DECODE_MOD, reg) & 0x01; +- internal->frame_length = (STB0899_GETFIELD(UWP_DECODE_MOD, reg) >> 1) & 0x01; +- +- /* Set IF AGC to tracking */ +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); +- STB0899_SETFIELD_VAL(IF_LOOP_GAIN, reg, 3); +- +- /* if QPSK 1/2,QPSK 3/5 or QPSK 2/3 set IF AGC reference to 16 otherwise 32*/ +- if (INRANGE(STB0899_QPSK_12, internal->modcod, STB0899_QPSK_23)) +- STB0899_SETFIELD_VAL(IF_AGC_REF, reg, 16); +- +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL2); +- STB0899_SETFIELD_VAL(IF_AGC_DUMP_PER, reg, 7); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL2, STB0899_OFF0_IF_AGC_CNTRL2, reg); +- } +- +- /* Release Stream Merger Reset */ +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESRS, reg, 0); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- return internal->status; +-} +diff --git a/drivers/media/dvb/frontends/stb0899_cfg.h b/drivers/media/dvb/frontends/stb0899_cfg.h +deleted file mode 100644 +index 0867906..0000000 +--- a/drivers/media/dvb/frontends/stb0899_cfg.h ++++ /dev/null +@@ -1,287 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STB0899_CFG_H +-#define __STB0899_CFG_H +- +-static const struct stb0899_s2_reg stb0899_s2_init_2[] = { +- +- { STB0899_OFF0_DMD_STATUS , STB0899_BASE_DMD_STATUS , 0x00000103 }, /* DMDSTATUS */ +- { STB0899_OFF0_CRL_FREQ , STB0899_BASE_CRL_FREQ , 0x3ed1da56 }, /* CRLFREQ */ +- { STB0899_OFF0_BTR_FREQ , STB0899_BASE_BTR_FREQ , 0x00004000 }, /* BTRFREQ */ +- { STB0899_OFF0_IF_AGC_GAIN , STB0899_BASE_IF_AGC_GAIN , 0x00002ade }, /* IFAGCGAIN */ +- { STB0899_OFF0_BB_AGC_GAIN , STB0899_BASE_BB_AGC_GAIN , 0x000001bc }, /* BBAGCGAIN */ +- { STB0899_OFF0_DC_OFFSET , STB0899_BASE_DC_OFFSET , 0x00000200 }, /* DCOFFSET */ +- { STB0899_OFF0_DMD_CNTRL , STB0899_BASE_DMD_CNTRL , 0x0000000f }, /* DMDCNTRL */ +- +- { STB0899_OFF0_IF_AGC_CNTRL , STB0899_BASE_IF_AGC_CNTRL , 0x03fb4a20 }, /* IFAGCCNTRL */ +- { STB0899_OFF0_BB_AGC_CNTRL , STB0899_BASE_BB_AGC_CNTRL , 0x00200c97 }, /* BBAGCCNTRL */ +- +- { STB0899_OFF0_CRL_CNTRL , STB0899_BASE_CRL_CNTRL , 0x00000016 }, /* CRLCNTRL */ +- { STB0899_OFF0_CRL_PHS_INIT , STB0899_BASE_CRL_PHS_INIT , 0x00000000 }, /* CRLPHSINIT */ +- { STB0899_OFF0_CRL_FREQ_INIT , STB0899_BASE_CRL_FREQ_INIT , 0x00000000 }, /* CRLFREQINIT */ +- { STB0899_OFF0_CRL_LOOP_GAIN , STB0899_BASE_CRL_LOOP_GAIN , 0x00000000 }, /* CRLLOOPGAIN */ +- { STB0899_OFF0_CRL_NOM_FREQ , STB0899_BASE_CRL_NOM_FREQ , 0x3ed097b6 }, /* CRLNOMFREQ */ +- { STB0899_OFF0_CRL_SWP_RATE , STB0899_BASE_CRL_SWP_RATE , 0x00000000 }, /* CRLSWPRATE */ +- { STB0899_OFF0_CRL_MAX_SWP , STB0899_BASE_CRL_MAX_SWP , 0x00000000 }, /* CRLMAXSWP */ +- { STB0899_OFF0_CRL_LK_CNTRL , STB0899_BASE_CRL_LK_CNTRL , 0x0f6cdc01 }, /* CRLLKCNTRL */ +- { STB0899_OFF0_DECIM_CNTRL , STB0899_BASE_DECIM_CNTRL , 0x00000000 }, /* DECIMCNTRL */ +- { STB0899_OFF0_BTR_CNTRL , STB0899_BASE_BTR_CNTRL , 0x00003993 }, /* BTRCNTRL */ +- { STB0899_OFF0_BTR_LOOP_GAIN , STB0899_BASE_BTR_LOOP_GAIN , 0x000d3c6f }, /* BTRLOOPGAIN */ +- { STB0899_OFF0_BTR_PHS_INIT , STB0899_BASE_BTR_PHS_INIT , 0x00000000 }, /* BTRPHSINIT */ +- { STB0899_OFF0_BTR_FREQ_INIT , STB0899_BASE_BTR_FREQ_INIT , 0x00000000 }, /* BTRFREQINIT */ +- { STB0899_OFF0_BTR_NOM_FREQ , STB0899_BASE_BTR_NOM_FREQ , 0x0238e38e }, /* BTRNOMFREQ */ +- { STB0899_OFF0_BTR_LK_CNTRL , STB0899_BASE_BTR_LK_CNTRL , 0x00000000 }, /* BTRLKCNTRL */ +- { STB0899_OFF0_DECN_CNTRL , STB0899_BASE_DECN_CNTRL , 0x00000000 }, /* DECNCNTRL */ +- { STB0899_OFF0_TP_CNTRL , STB0899_BASE_TP_CNTRL , 0x00000000 }, /* TPCNTRL */ +- { STB0899_OFF0_TP_BUF_STATUS , STB0899_BASE_TP_BUF_STATUS , 0x00000000 }, /* TPBUFSTATUS */ +- { STB0899_OFF0_DC_ESTIM , STB0899_BASE_DC_ESTIM , 0x00000000 }, /* DCESTIM */ +- { STB0899_OFF0_FLL_CNTRL , STB0899_BASE_FLL_CNTRL , 0x00000000 }, /* FLLCNTRL */ +- { STB0899_OFF0_FLL_FREQ_WD , STB0899_BASE_FLL_FREQ_WD , 0x40070000 }, /* FLLFREQWD */ +- { STB0899_OFF0_ANTI_ALIAS_SEL , STB0899_BASE_ANTI_ALIAS_SEL , 0x00000001 }, /* ANTIALIASSEL */ +- { STB0899_OFF0_RRC_ALPHA , STB0899_BASE_RRC_ALPHA , 0x00000002 }, /* RRCALPHA */ +- { STB0899_OFF0_DC_ADAPT_LSHFT , STB0899_BASE_DC_ADAPT_LSHFT , 0x00000000 }, /* DCADAPTISHFT */ +- { STB0899_OFF0_IMB_OFFSET , STB0899_BASE_IMB_OFFSET , 0x0000fe01 }, /* IMBOFFSET */ +- { STB0899_OFF0_IMB_ESTIMATE , STB0899_BASE_IMB_ESTIMATE , 0x00000000 }, /* IMBESTIMATE */ +- { STB0899_OFF0_IMB_CNTRL , STB0899_BASE_IMB_CNTRL , 0x00000001 }, /* IMBCNTRL */ +- { STB0899_OFF0_IF_AGC_CNTRL2 , STB0899_BASE_IF_AGC_CNTRL2 , 0x00005007 }, /* IFAGCCNTRL2 */ +- { STB0899_OFF0_DMD_CNTRL2 , STB0899_BASE_DMD_CNTRL2 , 0x00000002 }, /* DMDCNTRL2 */ +- { STB0899_OFF0_TP_BUFFER , STB0899_BASE_TP_BUFFER , 0x00000000 }, /* TPBUFFER */ +- { STB0899_OFF0_TP_BUFFER1 , STB0899_BASE_TP_BUFFER1 , 0x00000000 }, /* TPBUFFER1 */ +- { STB0899_OFF0_TP_BUFFER2 , STB0899_BASE_TP_BUFFER2 , 0x00000000 }, /* TPBUFFER2 */ +- { STB0899_OFF0_TP_BUFFER3 , STB0899_BASE_TP_BUFFER3 , 0x00000000 }, /* TPBUFFER3 */ +- { STB0899_OFF0_TP_BUFFER4 , STB0899_BASE_TP_BUFFER4 , 0x00000000 }, /* TPBUFFER4 */ +- { STB0899_OFF0_TP_BUFFER5 , STB0899_BASE_TP_BUFFER5 , 0x00000000 }, /* TPBUFFER5 */ +- { STB0899_OFF0_TP_BUFFER6 , STB0899_BASE_TP_BUFFER6 , 0x00000000 }, /* TPBUFFER6 */ +- { STB0899_OFF0_TP_BUFFER7 , STB0899_BASE_TP_BUFFER7 , 0x00000000 }, /* TPBUFFER7 */ +- { STB0899_OFF0_TP_BUFFER8 , STB0899_BASE_TP_BUFFER8 , 0x00000000 }, /* TPBUFFER8 */ +- { STB0899_OFF0_TP_BUFFER9 , STB0899_BASE_TP_BUFFER9 , 0x00000000 }, /* TPBUFFER9 */ +- { STB0899_OFF0_TP_BUFFER10 , STB0899_BASE_TP_BUFFER10 , 0x00000000 }, /* TPBUFFER10 */ +- { STB0899_OFF0_TP_BUFFER11 , STB0899_BASE_TP_BUFFER11 , 0x00000000 }, /* TPBUFFER11 */ +- { STB0899_OFF0_TP_BUFFER12 , STB0899_BASE_TP_BUFFER12 , 0x00000000 }, /* TPBUFFER12 */ +- { STB0899_OFF0_TP_BUFFER13 , STB0899_BASE_TP_BUFFER13 , 0x00000000 }, /* TPBUFFER13 */ +- { STB0899_OFF0_TP_BUFFER14 , STB0899_BASE_TP_BUFFER14 , 0x00000000 }, /* TPBUFFER14 */ +- { STB0899_OFF0_TP_BUFFER15 , STB0899_BASE_TP_BUFFER15 , 0x00000000 }, /* TPBUFFER15 */ +- { STB0899_OFF0_TP_BUFFER16 , STB0899_BASE_TP_BUFFER16 , 0x0000ff00 }, /* TPBUFFER16 */ +- { STB0899_OFF0_TP_BUFFER17 , STB0899_BASE_TP_BUFFER17 , 0x00000100 }, /* TPBUFFER17 */ +- { STB0899_OFF0_TP_BUFFER18 , STB0899_BASE_TP_BUFFER18 , 0x0000fe01 }, /* TPBUFFER18 */ +- { STB0899_OFF0_TP_BUFFER19 , STB0899_BASE_TP_BUFFER19 , 0x000004fe }, /* TPBUFFER19 */ +- { STB0899_OFF0_TP_BUFFER20 , STB0899_BASE_TP_BUFFER20 , 0x0000cfe7 }, /* TPBUFFER20 */ +- { STB0899_OFF0_TP_BUFFER21 , STB0899_BASE_TP_BUFFER21 , 0x0000bec6 }, /* TPBUFFER21 */ +- { STB0899_OFF0_TP_BUFFER22 , STB0899_BASE_TP_BUFFER22 , 0x0000c2bf }, /* TPBUFFER22 */ +- { STB0899_OFF0_TP_BUFFER23 , STB0899_BASE_TP_BUFFER23 , 0x0000c1c1 }, /* TPBUFFER23 */ +- { STB0899_OFF0_TP_BUFFER24 , STB0899_BASE_TP_BUFFER24 , 0x0000c1c1 }, /* TPBUFFER24 */ +- { STB0899_OFF0_TP_BUFFER25 , STB0899_BASE_TP_BUFFER25 , 0x0000c1c1 }, /* TPBUFFER25 */ +- { STB0899_OFF0_TP_BUFFER26 , STB0899_BASE_TP_BUFFER26 , 0x0000c1c1 }, /* TPBUFFER26 */ +- { STB0899_OFF0_TP_BUFFER27 , STB0899_BASE_TP_BUFFER27 , 0x0000c1c0 }, /* TPBUFFER27 */ +- { STB0899_OFF0_TP_BUFFER28 , STB0899_BASE_TP_BUFFER28 , 0x0000c0c0 }, /* TPBUFFER28 */ +- { STB0899_OFF0_TP_BUFFER29 , STB0899_BASE_TP_BUFFER29 , 0x0000c1c1 }, /* TPBUFFER29 */ +- { STB0899_OFF0_TP_BUFFER30 , STB0899_BASE_TP_BUFFER30 , 0x0000c1c1 }, /* TPBUFFER30 */ +- { STB0899_OFF0_TP_BUFFER31 , STB0899_BASE_TP_BUFFER31 , 0x0000c0c1 }, /* TPBUFFER31 */ +- { STB0899_OFF0_TP_BUFFER32 , STB0899_BASE_TP_BUFFER32 , 0x0000c0c1 }, /* TPBUFFER32 */ +- { STB0899_OFF0_TP_BUFFER33 , STB0899_BASE_TP_BUFFER33 , 0x0000c1c1 }, /* TPBUFFER33 */ +- { STB0899_OFF0_TP_BUFFER34 , STB0899_BASE_TP_BUFFER34 , 0x0000c1c1 }, /* TPBUFFER34 */ +- { STB0899_OFF0_TP_BUFFER35 , STB0899_BASE_TP_BUFFER35 , 0x0000c0c1 }, /* TPBUFFER35 */ +- { STB0899_OFF0_TP_BUFFER36 , STB0899_BASE_TP_BUFFER36 , 0x0000c1c1 }, /* TPBUFFER36 */ +- { STB0899_OFF0_TP_BUFFER37 , STB0899_BASE_TP_BUFFER37 , 0x0000c0c1 }, /* TPBUFFER37 */ +- { STB0899_OFF0_TP_BUFFER38 , STB0899_BASE_TP_BUFFER38 , 0x0000c1c1 }, /* TPBUFFER38 */ +- { STB0899_OFF0_TP_BUFFER39 , STB0899_BASE_TP_BUFFER39 , 0x0000c0c0 }, /* TPBUFFER39 */ +- { STB0899_OFF0_TP_BUFFER40 , STB0899_BASE_TP_BUFFER40 , 0x0000c1c0 }, /* TPBUFFER40 */ +- { STB0899_OFF0_TP_BUFFER41 , STB0899_BASE_TP_BUFFER41 , 0x0000c1c1 }, /* TPBUFFER41 */ +- { STB0899_OFF0_TP_BUFFER42 , STB0899_BASE_TP_BUFFER42 , 0x0000c0c0 }, /* TPBUFFER42 */ +- { STB0899_OFF0_TP_BUFFER43 , STB0899_BASE_TP_BUFFER43 , 0x0000c1c0 }, /* TPBUFFER43 */ +- { STB0899_OFF0_TP_BUFFER44 , STB0899_BASE_TP_BUFFER44 , 0x0000c0c1 }, /* TPBUFFER44 */ +- { STB0899_OFF0_TP_BUFFER45 , STB0899_BASE_TP_BUFFER45 , 0x0000c1be }, /* TPBUFFER45 */ +- { STB0899_OFF0_TP_BUFFER46 , STB0899_BASE_TP_BUFFER46 , 0x0000c1c9 }, /* TPBUFFER46 */ +- { STB0899_OFF0_TP_BUFFER47 , STB0899_BASE_TP_BUFFER47 , 0x0000c0da }, /* TPBUFFER47 */ +- { STB0899_OFF0_TP_BUFFER48 , STB0899_BASE_TP_BUFFER48 , 0x0000c0ba }, /* TPBUFFER48 */ +- { STB0899_OFF0_TP_BUFFER49 , STB0899_BASE_TP_BUFFER49 , 0x0000c1c4 }, /* TPBUFFER49 */ +- { STB0899_OFF0_TP_BUFFER50 , STB0899_BASE_TP_BUFFER50 , 0x0000c1bf }, /* TPBUFFER50 */ +- { STB0899_OFF0_TP_BUFFER51 , STB0899_BASE_TP_BUFFER51 , 0x0000c0c1 }, /* TPBUFFER51 */ +- { STB0899_OFF0_TP_BUFFER52 , STB0899_BASE_TP_BUFFER52 , 0x0000c1c0 }, /* TPBUFFER52 */ +- { STB0899_OFF0_TP_BUFFER53 , STB0899_BASE_TP_BUFFER53 , 0x0000c0c1 }, /* TPBUFFER53 */ +- { STB0899_OFF0_TP_BUFFER54 , STB0899_BASE_TP_BUFFER54 , 0x0000c1c1 }, /* TPBUFFER54 */ +- { STB0899_OFF0_TP_BUFFER55 , STB0899_BASE_TP_BUFFER55 , 0x0000c1c1 }, /* TPBUFFER55 */ +- { STB0899_OFF0_TP_BUFFER56 , STB0899_BASE_TP_BUFFER56 , 0x0000c1c1 }, /* TPBUFFER56 */ +- { STB0899_OFF0_TP_BUFFER57 , STB0899_BASE_TP_BUFFER57 , 0x0000c1c1 }, /* TPBUFFER57 */ +- { STB0899_OFF0_TP_BUFFER58 , STB0899_BASE_TP_BUFFER58 , 0x0000c1c1 }, /* TPBUFFER58 */ +- { STB0899_OFF0_TP_BUFFER59 , STB0899_BASE_TP_BUFFER59 , 0x0000c1c1 }, /* TPBUFFER59 */ +- { STB0899_OFF0_TP_BUFFER60 , STB0899_BASE_TP_BUFFER60 , 0x0000c1c1 }, /* TPBUFFER60 */ +- { STB0899_OFF0_TP_BUFFER61 , STB0899_BASE_TP_BUFFER61 , 0x0000c1c1 }, /* TPBUFFER61 */ +- { STB0899_OFF0_TP_BUFFER62 , STB0899_BASE_TP_BUFFER62 , 0x0000c1c1 }, /* TPBUFFER62 */ +- { STB0899_OFF0_TP_BUFFER63 , STB0899_BASE_TP_BUFFER63 , 0x0000c1c0 }, /* TPBUFFER63 */ +- { STB0899_OFF0_RESET_CNTRL , STB0899_BASE_RESET_CNTRL , 0x00000001 }, /* RESETCNTRL */ +- { STB0899_OFF0_ACM_ENABLE , STB0899_BASE_ACM_ENABLE , 0x00005654 }, /* ACMENABLE */ +- { STB0899_OFF0_DESCR_CNTRL , STB0899_BASE_DESCR_CNTRL , 0x00000000 }, /* DESCRCNTRL */ +- { STB0899_OFF0_CSM_CNTRL1 , STB0899_BASE_CSM_CNTRL1 , 0x00020019 }, /* CSMCNTRL1 */ +- { STB0899_OFF0_CSM_CNTRL2 , STB0899_BASE_CSM_CNTRL2 , 0x004b3237 }, /* CSMCNTRL2 */ +- { STB0899_OFF0_CSM_CNTRL3 , STB0899_BASE_CSM_CNTRL3 , 0x0003dd17 }, /* CSMCNTRL3 */ +- { STB0899_OFF0_CSM_CNTRL4 , STB0899_BASE_CSM_CNTRL4 , 0x00008008 }, /* CSMCNTRL4 */ +- { STB0899_OFF0_UWP_CNTRL1 , STB0899_BASE_UWP_CNTRL1 , 0x002a3106 }, /* UWPCNTRL1 */ +- { STB0899_OFF0_UWP_CNTRL2 , STB0899_BASE_UWP_CNTRL2 , 0x0006140a }, /* UWPCNTRL2 */ +- { STB0899_OFF0_UWP_STAT1 , STB0899_BASE_UWP_STAT1 , 0x00008000 }, /* UWPSTAT1 */ +- { STB0899_OFF0_UWP_STAT2 , STB0899_BASE_UWP_STAT2 , 0x00000000 }, /* UWPSTAT2 */ +- { STB0899_OFF0_DMD_STAT2 , STB0899_BASE_DMD_STAT2 , 0x00000000 }, /* DMDSTAT2 */ +- { STB0899_OFF0_FREQ_ADJ_SCALE , STB0899_BASE_FREQ_ADJ_SCALE , 0x00000471 }, /* FREQADJSCALE */ +- { STB0899_OFF0_UWP_CNTRL3 , STB0899_BASE_UWP_CNTRL3 , 0x017b0465 }, /* UWPCNTRL3 */ +- { STB0899_OFF0_SYM_CLK_SEL , STB0899_BASE_SYM_CLK_SEL , 0x00000002 }, /* SYMCLKSEL */ +- { STB0899_OFF0_SOF_SRCH_TO , STB0899_BASE_SOF_SRCH_TO , 0x00196464 }, /* SOFSRCHTO */ +- { STB0899_OFF0_ACQ_CNTRL1 , STB0899_BASE_ACQ_CNTRL1 , 0x00000603 }, /* ACQCNTRL1 */ +- { STB0899_OFF0_ACQ_CNTRL2 , STB0899_BASE_ACQ_CNTRL2 , 0x02046666 }, /* ACQCNTRL2 */ +- { STB0899_OFF0_ACQ_CNTRL3 , STB0899_BASE_ACQ_CNTRL3 , 0x10046583 }, /* ACQCNTRL3 */ +- { STB0899_OFF0_FE_SETTLE , STB0899_BASE_FE_SETTLE , 0x00010404 }, /* FESETTLE */ +- { STB0899_OFF0_AC_DWELL , STB0899_BASE_AC_DWELL , 0x0002aa8a }, /* ACDWELL */ +- { STB0899_OFF0_ACQUIRE_TRIG , STB0899_BASE_ACQUIRE_TRIG , 0x00000000 }, /* ACQUIRETRIG */ +- { STB0899_OFF0_LOCK_LOST , STB0899_BASE_LOCK_LOST , 0x00000001 }, /* LOCKLOST */ +- { STB0899_OFF0_ACQ_STAT1 , STB0899_BASE_ACQ_STAT1 , 0x00000500 }, /* ACQSTAT1 */ +- { STB0899_OFF0_ACQ_TIMEOUT , STB0899_BASE_ACQ_TIMEOUT , 0x0028a0a0 }, /* ACQTIMEOUT */ +- { STB0899_OFF0_ACQ_TIME , STB0899_BASE_ACQ_TIME , 0x00000000 }, /* ACQTIME */ +- { STB0899_OFF0_FINAL_AGC_CNTRL , STB0899_BASE_FINAL_AGC_CNTRL , 0x00800c17 }, /* FINALAGCCNTRL*/ +- { STB0899_OFF0_FINAL_AGC_GAIN , STB0899_BASE_FINAL_AGC_GAIN , 0x00000000 }, /* FINALAGCCGAIN*/ +- { STB0899_OFF0_EQUALIZER_INIT , STB0899_BASE_EQUALIZER_INIT , 0x00000000 }, /* EQUILIZERINIT*/ +- { STB0899_OFF0_EQ_CNTRL , STB0899_BASE_EQ_CNTRL , 0x00054802 }, /* EQCNTL */ +- { STB0899_OFF0_EQ_I_INIT_COEFF_0, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF0 */ +- { STB0899_OFF1_EQ_I_INIT_COEFF_1, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF1 */ +- { STB0899_OFF2_EQ_I_INIT_COEFF_2, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF2 */ +- { STB0899_OFF3_EQ_I_INIT_COEFF_3, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF3 */ +- { STB0899_OFF4_EQ_I_INIT_COEFF_4, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF4 */ +- { STB0899_OFF5_EQ_I_INIT_COEFF_5, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000400 }, /* EQIINITCOEFF5 */ +- { STB0899_OFF6_EQ_I_INIT_COEFF_6, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF6 */ +- { STB0899_OFF7_EQ_I_INIT_COEFF_7, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF7 */ +- { STB0899_OFF8_EQ_I_INIT_COEFF_8, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF8 */ +- { STB0899_OFF9_EQ_I_INIT_COEFF_9, STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF9 */ +- { STB0899_OFFa_EQ_I_INIT_COEFF_10,STB0899_BASE_EQ_I_INIT_COEFF_N, 0x00000000 }, /* EQIINITCOEFF10*/ +- { STB0899_OFF0_EQ_Q_INIT_COEFF_0, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF0 */ +- { STB0899_OFF1_EQ_Q_INIT_COEFF_1, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF1 */ +- { STB0899_OFF2_EQ_Q_INIT_COEFF_2, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF2 */ +- { STB0899_OFF3_EQ_Q_INIT_COEFF_3, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF3 */ +- { STB0899_OFF4_EQ_Q_INIT_COEFF_4, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF4 */ +- { STB0899_OFF5_EQ_Q_INIT_COEFF_5, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF5 */ +- { STB0899_OFF6_EQ_Q_INIT_COEFF_6, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF6 */ +- { STB0899_OFF7_EQ_Q_INIT_COEFF_7, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF7 */ +- { STB0899_OFF8_EQ_Q_INIT_COEFF_8, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF8 */ +- { STB0899_OFF9_EQ_Q_INIT_COEFF_9, STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF9 */ +- { STB0899_OFFa_EQ_Q_INIT_COEFF_10,STB0899_BASE_EQ_Q_INIT_COEFF_N, 0x00000000 }, /* EQQINITCOEFF10*/ +- { STB0899_OFF0_EQ_I_OUT_COEFF_0 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT0 */ +- { STB0899_OFF1_EQ_I_OUT_COEFF_1 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT1 */ +- { STB0899_OFF2_EQ_I_OUT_COEFF_2 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT2 */ +- { STB0899_OFF3_EQ_I_OUT_COEFF_3 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT3 */ +- { STB0899_OFF4_EQ_I_OUT_COEFF_4 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT4 */ +- { STB0899_OFF5_EQ_I_OUT_COEFF_5 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT5 */ +- { STB0899_OFF6_EQ_I_OUT_COEFF_6 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT6 */ +- { STB0899_OFF7_EQ_I_OUT_COEFF_7 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT7 */ +- { STB0899_OFF8_EQ_I_OUT_COEFF_8 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT8 */ +- { STB0899_OFF9_EQ_I_OUT_COEFF_9 , STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT9 */ +- { STB0899_OFFa_EQ_I_OUT_COEFF_10,STB0899_BASE_EQ_I_OUT_COEFF_N , 0x00000000 }, /* EQICOEFFSOUT10*/ +- { STB0899_OFF0_EQ_Q_OUT_COEFF_0 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT0 */ +- { STB0899_OFF1_EQ_Q_OUT_COEFF_1 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT1 */ +- { STB0899_OFF2_EQ_Q_OUT_COEFF_2 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT2 */ +- { STB0899_OFF3_EQ_Q_OUT_COEFF_3 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT3 */ +- { STB0899_OFF4_EQ_Q_OUT_COEFF_4 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT4 */ +- { STB0899_OFF5_EQ_Q_OUT_COEFF_5 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT5 */ +- { STB0899_OFF6_EQ_Q_OUT_COEFF_6 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT6 */ +- { STB0899_OFF7_EQ_Q_OUT_COEFF_7 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT7 */ +- { STB0899_OFF8_EQ_Q_OUT_COEFF_8 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT8 */ +- { STB0899_OFF9_EQ_Q_OUT_COEFF_9 , STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT9 */ +- { STB0899_OFFa_EQ_Q_OUT_COEFF_10, STB0899_BASE_EQ_Q_OUT_COEFF_N , 0x00000000 }, /* EQQCOEFFSOUT10*/ +- { 0xffff , 0xffffffff , 0xffffffff }, +-}; +-static const struct stb0899_s2_reg stb0899_s2_init_4[] = { +- { STB0899_OFF0_BLOCK_LNGTH , STB0899_BASE_BLOCK_LNGTH , 0x00000008 }, /* BLOCKLNGTH */ +- { STB0899_OFF0_ROW_STR , STB0899_BASE_ROW_STR , 0x000000b4 }, /* ROWSTR */ +- { STB0899_OFF0_BN_END_ADDR , STB0899_BASE_BN_END_ADDR , 0x000004b5 }, /* BNANDADDR */ +- { STB0899_OFF0_CN_END_ADDR , STB0899_BASE_CN_END_ADDR , 0x00000b4b }, /* CNANDADDR */ +- { STB0899_OFF0_INFO_LENGTH , STB0899_BASE_INFO_LENGTH , 0x00000078 }, /* INFOLENGTH */ +- { STB0899_OFF0_BOT_ADDR , STB0899_BASE_BOT_ADDR , 0x000001e0 }, /* BOT_ADDR */ +- { STB0899_OFF0_BCH_BLK_LN , STB0899_BASE_BCH_BLK_LN , 0x0000a8c0 }, /* BCHBLKLN */ +- { STB0899_OFF0_BCH_T , STB0899_BASE_BCH_T , 0x0000000c }, /* BCHT */ +- { STB0899_OFF0_CNFG_MODE , STB0899_BASE_CNFG_MODE , 0x00000001 }, /* CNFGMODE */ +- { STB0899_OFF0_LDPC_STAT , STB0899_BASE_LDPC_STAT , 0x0000000d }, /* LDPCSTAT */ +- { STB0899_OFF0_ITER_SCALE , STB0899_BASE_ITER_SCALE , 0x00000040 }, /* ITERSCALE */ +- { STB0899_OFF0_INPUT_MODE , STB0899_BASE_INPUT_MODE , 0x00000000 }, /* INPUTMODE */ +- { STB0899_OFF0_LDPCDECRST , STB0899_BASE_LDPCDECRST , 0x00000000 }, /* LDPCDECRST */ +- { STB0899_OFF0_CLK_PER_BYTE_RW , STB0899_BASE_CLK_PER_BYTE_RW , 0x00000008 }, /* CLKPERBYTE */ +- { STB0899_OFF0_BCH_ERRORS , STB0899_BASE_BCH_ERRORS , 0x00000000 }, /* BCHERRORS */ +- { STB0899_OFF0_LDPC_ERRORS , STB0899_BASE_LDPC_ERRORS , 0x00000000 }, /* LDPCERRORS */ +- { STB0899_OFF0_BCH_MODE , STB0899_BASE_BCH_MODE , 0x00000000 }, /* BCHMODE */ +- { STB0899_OFF0_ERR_ACC_PER , STB0899_BASE_ERR_ACC_PER , 0x00000008 }, /* ERRACCPER */ +- { STB0899_OFF0_BCH_ERR_ACC , STB0899_BASE_BCH_ERR_ACC , 0x00000000 }, /* BCHERRACC */ +- { STB0899_OFF0_FEC_TP_SEL , STB0899_BASE_FEC_TP_SEL , 0x00000000 }, /* FECTPSEL */ +- { 0xffff , 0xffffffff , 0xffffffff }, +-}; +- +-static const struct stb0899_s1_reg stb0899_s1_init_5[] = { +- { STB0899_TSTCK , 0x00 }, +- { STB0899_TSTRES , 0x00 }, +- { STB0899_TSTOUT , 0x00 }, +- { STB0899_TSTIN , 0x00 }, +- { STB0899_TSTSYS , 0x00 }, +- { STB0899_TSTCHIP , 0x00 }, +- { STB0899_TSTFREE , 0x00 }, +- { STB0899_TSTI2C , 0x00 }, +- { STB0899_BITSPEEDM , 0x00 }, +- { STB0899_BITSPEEDL , 0x00 }, +- { STB0899_TBUSBIT , 0x00 }, +- { STB0899_TSTDIS , 0x00 }, +- { STB0899_TSTDISRX , 0x00 }, +- { STB0899_TSTJETON , 0x00 }, +- { STB0899_TSTDCADJ , 0x00 }, +- { STB0899_TSTAGC1 , 0x00 }, +- { STB0899_TSTAGC1N , 0x00 }, +- { STB0899_TSTPOLYPH , 0x00 }, +- { STB0899_TSTR , 0x00 }, +- { STB0899_TSTAGC2 , 0x00 }, +- { STB0899_TSTCTL1 , 0x00 }, +- { STB0899_TSTCTL2 , 0x00 }, +- { STB0899_TSTCTL3 , 0x00 }, +- { STB0899_TSTDEMAP , 0x00 }, +- { STB0899_TSTDEMAP2 , 0x00 }, +- { STB0899_TSTDEMMON , 0x00 }, +- { STB0899_TSTRATE , 0x00 }, +- { STB0899_TSTSELOUT , 0x00 }, +- { STB0899_TSYNC , 0x00 }, +- { STB0899_TSTERR , 0x00 }, +- { STB0899_TSTRAM1 , 0x00 }, +- { STB0899_TSTVSELOUT , 0x00 }, +- { STB0899_TSTFORCEIN , 0x00 }, +- { STB0899_TSTRS1 , 0x00 }, +- { STB0899_TSTRS2 , 0x00 }, +- { STB0899_TSTRS3 , 0x00 }, +- { STB0899_GHOSTREG , 0x81 }, +- { 0xffff , 0xff }, +-}; +- +-#define STB0899_DVBS2_ESNO_AVE 3 +-#define STB0899_DVBS2_ESNO_QUANT 32 +-#define STB0899_DVBS2_AVFRAMES_COARSE 10 +-#define STB0899_DVBS2_AVFRAMES_FINE 20 +-#define STB0899_DVBS2_MISS_THRESHOLD 6 +-#define STB0899_DVBS2_UWP_THRESHOLD_ACQ 1125 +-#define STB0899_DVBS2_UWP_THRESHOLD_TRACK 758 +-#define STB0899_DVBS2_UWP_THRESHOLD_SOF 1350 +-#define STB0899_DVBS2_SOF_SEARCH_TIMEOUT 1664100 +- +-#define STB0899_DVBS2_BTR_NCO_BITS 28 +-#define STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET 15 +-#define STB0899_DVBS2_CRL_NCO_BITS 30 +-#define STB0899_DVBS2_LDPC_MAX_ITER 70 +- +-#endif //__STB0899_CFG_H +diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c +deleted file mode 100644 +index 38565be..0000000 +--- a/drivers/media/dvb/frontends/stb0899_drv.c ++++ /dev/null +@@ -1,1665 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include "dvb_frontend.h" +- +-#include "stb0899_drv.h" +-#include "stb0899_priv.h" +-#include "stb0899_reg.h" +- +-static unsigned int verbose = 0;//1; +-module_param(verbose, int, 0644); +- +-/* C/N in dB/10, NIRM/NIRL */ +-static const struct stb0899_tab stb0899_cn_tab[] = { +- { 200, 2600 }, +- { 190, 2700 }, +- { 180, 2860 }, +- { 170, 3020 }, +- { 160, 3210 }, +- { 150, 3440 }, +- { 140, 3710 }, +- { 130, 4010 }, +- { 120, 4360 }, +- { 110, 4740 }, +- { 100, 5190 }, +- { 90, 5670 }, +- { 80, 6200 }, +- { 70, 6770 }, +- { 60, 7360 }, +- { 50, 7970 }, +- { 40, 8250 }, +- { 30, 9000 }, +- { 20, 9450 }, +- { 15, 9600 }, +-}; +- +-/* DVB-S AGCIQ_VALUE vs. signal level in dBm/10. +- * As measured, connected to a modulator. +- * -8.0 to -50.0 dBm directly connected, +- * -52.0 to -74.8 with extra attenuation. +- * Cut-off to AGCIQ_VALUE = 0x80 below -74.8dBm. +- * Crude linear extrapolation below -84.8dBm and above -8.0dBm. +- */ +-static const struct stb0899_tab stb0899_dvbsrf_tab[] = { +- { -950, -128 }, +- { -748, -94 }, +- { -745, -92 }, +- { -735, -90 }, +- { -720, -87 }, +- { -670, -77 }, +- { -640, -70 }, +- { -610, -62 }, +- { -600, -60 }, +- { -590, -56 }, +- { -560, -41 }, +- { -540, -25 }, +- { -530, -17 }, +- { -520, -11 }, +- { -500, 1 }, +- { -490, 6 }, +- { -480, 10 }, +- { -440, 22 }, +- { -420, 27 }, +- { -400, 31 }, +- { -380, 34 }, +- { -340, 40 }, +- { -320, 43 }, +- { -280, 48 }, +- { -250, 52 }, +- { -230, 55 }, +- { -180, 61 }, +- { -140, 66 }, +- { -90, 73 }, +- { -80, 74 }, +- { 500, 127 } +-}; +- +-/* DVB-S2 IF_AGC_GAIN vs. signal level in dBm/10. +- * As measured, connected to a modulator. +- * -8.0 to -50.1 dBm directly connected, +- * -53.0 to -76.6 with extra attenuation. +- * Cut-off to IF_AGC_GAIN = 0x3fff below -76.6dBm. +- * Crude linear extrapolation below -76.6dBm and above -8.0dBm. +- */ +-static const struct stb0899_tab stb0899_dvbs2rf_tab[] = { +- { 700, 0 }, +- { -80, 3217 }, +- { -150, 3893 }, +- { -190, 4217 }, +- { -240, 4621 }, +- { -280, 4945 }, +- { -320, 5273 }, +- { -350, 5545 }, +- { -370, 5741 }, +- { -410, 6147 }, +- { -450, 6671 }, +- { -490, 7413 }, +- { -501, 7665 }, +- { -530, 8767 }, +- { -560, 10219 }, +- { -580, 10939 }, +- { -590, 11518 }, +- { -600, 11723 }, +- { -650, 12659 }, +- { -690, 13219 }, +- { -730, 13645 }, +- { -750, 13909 }, +- { -766, 14153 }, +- { -999, 16383 } +-}; +- +-/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/ +-static struct stb0899_tab stb0899_quant_tab[] = { +- { 0, 0 }, +- { 0, 100 }, +- { 600, 200 }, +- { 950, 299 }, +- { 1200, 398 }, +- { 1400, 501 }, +- { 1560, 603 }, +- { 1690, 700 }, +- { 1810, 804 }, +- { 1910, 902 }, +- { 2000, 1000 }, +- { 2080, 1096 }, +- { 2160, 1202 }, +- { 2230, 1303 }, +- { 2350, 1496 }, +- { 2410, 1603 }, +- { 2460, 1698 }, +- { 2510, 1799 }, +- { 2600, 1995 }, +- { 2650, 2113 }, +- { 2690, 2213 }, +- { 2720, 2291 }, +- { 2760, 2399 }, +- { 2800, 2512 }, +- { 2860, 2692 }, +- { 2930, 2917 }, +- { 2960, 3020 }, +- { 3010, 3199 }, +- { 3040, 3311 }, +- { 3060, 3388 }, +- { 3120, 3631 }, +- { 3190, 3936 }, +- { 3400, 5012 }, +- { 3610, 6383 }, +- { 3800, 7943 }, +- { 4210, 12735 }, +- { 4500, 17783 }, +- { 4690, 22131 }, +- { 4810, 25410 } +-}; +- +-/* DVB-S2 Es/N0 estimate in dB/100 vs read value */ +-static struct stb0899_tab stb0899_est_tab[] = { +- { 0, 0 }, +- { 0, 1 }, +- { 301, 2 }, +- { 1204, 16 }, +- { 1806, 64 }, +- { 2408, 256 }, +- { 2709, 512 }, +- { 3010, 1023 }, +- { 3311, 2046 }, +- { 3612, 4093 }, +- { 3823, 6653 }, +- { 3913, 8185 }, +- { 4010, 10233 }, +- { 4107, 12794 }, +- { 4214, 16368 }, +- { 4266, 18450 }, +- { 4311, 20464 }, +- { 4353, 22542 }, +- { 4391, 24604 }, +- { 4425, 26607 }, +- { 4457, 28642 }, +- { 4487, 30690 }, +- { 4515, 32734 }, +- { 4612, 40926 }, +- { 4692, 49204 }, +- { 4816, 65464 }, +- { 4913, 81846 }, +- { 4993, 98401 }, +- { 5060, 114815 }, +- { 5118, 131220 }, +- { 5200, 158489 }, +- { 5300, 199526 }, +- { 5400, 251189 }, +- { 5500, 316228 }, +- { 5600, 398107 }, +- { 5720, 524807 }, +- { 5721, 526017 }, +-}; +- +-static int _stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +-{ +- int ret; +- +- u8 b0[] = { reg >> 8, reg & 0xff }; +- u8 buf; +- +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 2 +- },{ +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = &buf, +- .len = 1 +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) { +- if (ret != -ERESTARTSYS) +- dprintk(state->verbose, FE_ERROR, 1, +- "Read error, Reg=[0x%02x], Status=%d", +- reg, ret); +- +- return ret < 0 ? ret : -EREMOTEIO; +- } +- if (unlikely(*state->verbose >= FE_DEBUGREG)) +- dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%02x], data=%02x", +- reg, buf); +- +- return (unsigned int)buf; +-} +- +-int stb0899_read_reg(struct stb0899_state *state, unsigned int reg) +-{ +- int result; +- +- result = _stb0899_read_reg(state, reg); +- /* +- * Bug ID 9: +- * access to 0xf2xx/0xf6xx +- * must be followed by read from 0xf2ff/0xf6ff. +- */ +- if ((reg != 0xf2ff) && (reg != 0xf6ff) && +- (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) +- _stb0899_read_reg(state, (reg | 0x00ff)); +- +- return result; +-} +- +-u32 _stb0899_read_s2reg(struct stb0899_state *state, +- u32 stb0899_i2cdev, +- u32 stb0899_base_addr, +- u16 stb0899_reg_offset) +-{ +- int status; +- u32 data; +- u8 buf[7] = { 0 }; +- u16 tmpaddr; +- +- u8 buf_0[] = { +- GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ +- GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ +- GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ +- }; +- u8 buf_1[] = { +- 0x00, /* 0xf3 Reg Offset */ +- 0x00, /* 0x44 Reg Offset */ +- }; +- +- struct i2c_msg msg_0 = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf_0, +- .len = 6 +- }; +- +- struct i2c_msg msg_1 = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf_1, +- .len = 2 +- }; +- +- struct i2c_msg msg_r = { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = buf, +- .len = 4 +- }; +- +- tmpaddr = stb0899_reg_offset & 0xff00; +- if (!(stb0899_reg_offset & 0x8)) +- tmpaddr = stb0899_reg_offset | 0x20; +- +- buf_1[0] = GETBYTE(tmpaddr, BYTE1); +- buf_1[1] = GETBYTE(tmpaddr, BYTE0); +- +- status = i2c_transfer(state->i2c, &msg_0, 1); +- if (status < 1) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s ERR(1), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); +- +- goto err; +- } +- +- /* Dummy */ +- status = i2c_transfer(state->i2c, &msg_1, 1); +- if (status < 1) +- goto err; +- +- status = i2c_transfer(state->i2c, &msg_r, 1); +- if (status < 1) +- goto err; +- +- buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); +- buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); +- +- /* Actual */ +- status = i2c_transfer(state->i2c, &msg_1, 1); +- if (status < 1) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s ERR(2), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); +- goto err; +- } +- +- status = i2c_transfer(state->i2c, &msg_r, 1); +- if (status < 1) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s ERR(3), Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Status=%d\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, status); +- return status < 0 ? status : -EREMOTEIO; +- } +- +- data = MAKEWORD32(buf[3], buf[2], buf[1], buf[0]); +- if (unlikely(*state->verbose >= FE_DEBUGREG)) +- printk(KERN_DEBUG "%s Device=[0x%04x], Base address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, data); +- +- return data; +- +-err: +- return status < 0 ? status : -EREMOTEIO; +-} +- +-int stb0899_write_s2reg(struct stb0899_state *state, +- u32 stb0899_i2cdev, +- u32 stb0899_base_addr, +- u16 stb0899_reg_offset, +- u32 stb0899_data) +-{ +- int status; +- +- /* Base Address Setup */ +- u8 buf_0[] = { +- GETBYTE(stb0899_i2cdev, BYTE1), /* 0xf3 S2 Base Address (MSB) */ +- GETBYTE(stb0899_i2cdev, BYTE0), /* 0xfc S2 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE0), /* 0x00 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE1), /* 0x04 Base Address (LSB) */ +- GETBYTE(stb0899_base_addr, BYTE2), /* 0x00 Base Address (MSB) */ +- GETBYTE(stb0899_base_addr, BYTE3), /* 0x00 Base Address (MSB) */ +- }; +- u8 buf_1[] = { +- 0x00, /* 0xf3 Reg Offset */ +- 0x00, /* 0x44 Reg Offset */ +- 0x00, /* data */ +- 0x00, /* data */ +- 0x00, /* data */ +- 0x00, /* data */ +- }; +- +- struct i2c_msg msg_0 = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf_0, +- .len = 6 +- }; +- +- struct i2c_msg msg_1 = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf_1, +- .len = 6 +- }; +- +- buf_1[0] = GETBYTE(stb0899_reg_offset, BYTE1); +- buf_1[1] = GETBYTE(stb0899_reg_offset, BYTE0); +- buf_1[2] = GETBYTE(stb0899_data, BYTE0); +- buf_1[3] = GETBYTE(stb0899_data, BYTE1); +- buf_1[4] = GETBYTE(stb0899_data, BYTE2); +- buf_1[5] = GETBYTE(stb0899_data, BYTE3); +- +- if (unlikely(*state->verbose >= FE_DEBUGREG)) +- printk(KERN_DEBUG "%s Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x]\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data); +- +- status = i2c_transfer(state->i2c, &msg_0, 1); +- if (unlikely(status < 1)) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s ERR (1), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); +- goto err; +- } +- status = i2c_transfer(state->i2c, &msg_1, 1); +- if (unlikely(status < 1)) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s ERR (2), Device=[0x%04x], Base Address=[0x%08x], Offset=[0x%04x], Data=[0x%08x], status=%d\n", +- __func__, stb0899_i2cdev, stb0899_base_addr, stb0899_reg_offset, stb0899_data, status); +- +- return status < 0 ? status : -EREMOTEIO; +- } +- +- return 0; +- +-err: +- return status < 0 ? status : -EREMOTEIO; +-} +- +-int stb0899_read_regs(struct stb0899_state *state, unsigned int reg, u8 *buf, u32 count) +-{ +- int status; +- +- u8 b0[] = { reg >> 8, reg & 0xff }; +- +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 2 +- },{ +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = buf, +- .len = count +- } +- }; +- +- status = i2c_transfer(state->i2c, msg, 2); +- if (status != 2) { +- if (status != -ERESTARTSYS) +- printk(KERN_ERR "%s Read error, Reg=[0x%04x], Count=%u, Status=%d\n", +- __func__, reg, count, status); +- goto err; +- } +- /* +- * Bug ID 9: +- * access to 0xf2xx/0xf6xx +- * must be followed by read from 0xf2ff/0xf6ff. +- */ +- if ((reg != 0xf2ff) && (reg != 0xf6ff) && +- (((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) +- _stb0899_read_reg(state, (reg | 0x00ff)); +- +- if (unlikely(*state->verbose >= FE_DEBUGREG)) { +- int i; +- +- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); +- for (i = 0; i < count; i++) { +- printk(" %02x", buf[i]); +- } +- printk("\n"); +- } +- +- return 0; +-err: +- return status < 0 ? status : -EREMOTEIO; +-} +- +-int stb0899_write_regs(struct stb0899_state *state, unsigned int reg, u8 *data, u32 count) +-{ +- int ret; +- u8 buf[2 + count]; +- struct i2c_msg i2c_msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 + count +- }; +- +- buf[0] = reg >> 8; +- buf[1] = reg & 0xff; +- memcpy(&buf[2], data, count); +- +- if (unlikely(*state->verbose >= FE_DEBUGREG)) { +- int i; +- +- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); +- for (i = 0; i < count; i++) +- printk(" %02x", data[i]); +- printk("\n"); +- } +- ret = i2c_transfer(state->i2c, &i2c_msg, 1); +- +- /* +- * Bug ID 9: +- * access to 0xf2xx/0xf6xx +- * must be followed by read from 0xf2ff/0xf6ff. +- */ +- if ((((reg & 0xff00) == 0xf200) || ((reg & 0xff00) == 0xf600))) +- stb0899_read_reg(state, (reg | 0x00ff)); +- +- if (ret != 1) { +- if (ret != -ERESTARTSYS) +- dprintk(state->verbose, FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", +- reg, data[0], count, ret); +- return ret < 0 ? ret : -EREMOTEIO; +- } +- +- return 0; +-} +- +-int stb0899_write_reg(struct stb0899_state *state, unsigned int reg, u8 data) +-{ +- return stb0899_write_regs(state, reg, &data, 1); +-} +- +-/* +- * stb0899_get_mclk +- * Get STB0899 master clock frequency +- * ExtClk: external clock frequency (Hz) +- */ +-static u32 stb0899_get_mclk(struct stb0899_state *state) +-{ +- u32 mclk = 0, div = 0; +- +- div = stb0899_read_reg(state, STB0899_NCOARSE); +- mclk = (div + 1) * state->config->xtal_freq / 6; +- dprintk(state->verbose, FE_DEBUG, 1, "div=%d, mclk=%d", div, mclk); +- +- return mclk; +-} +- +-/* +- * stb0899_set_mclk +- * Set STB0899 master Clock frequency +- * Mclk: demodulator master clock +- * ExtClk: external clock frequency (Hz) +- */ +-static void stb0899_set_mclk(struct stb0899_state *state, u32 Mclk) +-{ +- struct stb0899_internal *internal = &state->internal; +- u8 mdiv = 0; +- +- dprintk(state->verbose, FE_DEBUG, 1, "state->config=%p", state->config); +- mdiv = ((6 * Mclk) / state->config->xtal_freq) - 1; +- dprintk(state->verbose, FE_DEBUG, 1, "mdiv=%d", mdiv); +- +- stb0899_write_reg(state, STB0899_NCOARSE, mdiv); +- internal->master_clk = stb0899_get_mclk(state); +- +- dprintk(state->verbose, FE_DEBUG, 1, "MasterCLOCK=%d", internal->master_clk); +-} +- +-static int stb0899_postproc(struct stb0899_state *state, u8 ctl, int enable) +-{ +- struct stb0899_config *config = state->config; +- const struct stb0899_postproc *postproc = config->postproc; +- +- /* post process event */ +- if (postproc) { +- if (enable) { +- if (postproc[ctl].level == STB0899_GPIOPULLUP) +- stb0899_write_reg(state, postproc[ctl].gpio, 0x02); +- else +- stb0899_write_reg(state, postproc[ctl].gpio, 0x82); +- } else { +- if (postproc[ctl].level == STB0899_GPIOPULLUP) +- stb0899_write_reg(state, postproc[ctl].gpio, 0x82); +- else +- stb0899_write_reg(state, postproc[ctl].gpio, 0x02); +- } +- } +- return 0; +-} +- +-static void stb0899_release(struct dvb_frontend *fe) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- +- dprintk(state->verbose, FE_DEBUG, 1, "Release Frontend"); +- /* post process event */ +- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); +- kfree(state); +-} +- +-/* +- * stb0899_get_alpha +- * return: rolloff +- */ +-static int stb0899_get_alpha(struct stb0899_state *state) +-{ +- u8 mode_coeff; +- +- mode_coeff = stb0899_read_reg(state, STB0899_DEMOD); +- +- if (STB0899_GETFIELD(MODECOEFF, mode_coeff) == 1) +- return 20; +- else +- return 35; +-} +- +-/* +- * stb0899_init_calc +- */ +-static void stb0899_init_calc(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- int master_clk; +- u8 agc[2]; +- u8 agc1cn; +- u32 reg; +- +- /* Read registers (in burst mode) */ +- agc1cn = stb0899_read_reg(state, STB0899_AGC1CN); +- stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ +- +- /* Initial calculations */ +- master_clk = stb0899_get_mclk(state); +- internal->t_agc1 = 0; +- internal->t_agc2 = 0; +- internal->master_clk = master_clk; +- internal->mclk = master_clk / 65536L; +- internal->rolloff = stb0899_get_alpha(state); +- +- /* DVBS2 Initial calculations */ +- /* Set AGC value to the middle */ +- internal->agc_gain = 8154; +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_CNTRL); +- STB0899_SETFIELD_VAL(IF_GAIN_INIT, reg, internal->agc_gain); +- stb0899_write_s2reg(state, STB0899_S2DEMOD, STB0899_BASE_IF_AGC_CNTRL, STB0899_OFF0_IF_AGC_CNTRL, reg); +- +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, RRC_ALPHA); +- internal->rrc_alpha = STB0899_GETFIELD(RRC_ALPHA, reg); +- +- internal->center_freq = 0; +- internal->av_frame_coarse = 10; +- internal->av_frame_fine = 20; +- internal->step_size = 2; +-/* +- if ((pParams->SpectralInv == FE_IQ_NORMAL) || (pParams->SpectralInv == FE_IQ_AUTO)) +- pParams->IQLocked = 0; +- else +- pParams->IQLocked = 1; +-*/ +-} +- +-static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeout) +-{ +- u8 reg = 0; +- unsigned long start = jiffies; +- +- while (1) { +- reg = stb0899_read_reg(state, STB0899_DISSTATUS); +- if (!STB0899_GETFIELD(FIFOFULL, reg)) +- break; +- if ((jiffies - start) > timeout) { +- dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); +- return -ETIMEDOUT; +- } +- } +- +- return 0; +-} +- +-static int stb0899_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- u8 reg, i; +- +- if (cmd->msg_len > 8) +- return -EINVAL; +- +- /* enable FIFO precharge */ +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 1); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- for (i = 0; i < cmd->msg_len; i++) { +- /* wait for FIFO empty */ +- if (stb0899_wait_diseqc_fifo_empty(state, 100) < 0) +- return -ETIMEDOUT; +- +- stb0899_write_reg(state, STB0899_DISFIFO, cmd->msg[i]); +- } +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- msleep(100); +- return 0; +-} +- +-static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) +-{ +- u8 reg = 0; +- unsigned long start = jiffies; +- +- while (!STB0899_GETFIELD(RXEND, reg)) { +- reg = stb0899_read_reg(state, STB0899_DISRX_ST0); +- if (jiffies - start > timeout) { +- dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); +- return -ETIMEDOUT; +- } +- msleep(10); +- } +- +- return 0; +-} +- +-static int stb0899_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- u8 reg, length = 0, i; +- int result; +- +- if (stb0899_wait_diseqc_rxidle(state, 100) < 0) +- return -ETIMEDOUT; +- +- reg = stb0899_read_reg(state, STB0899_DISRX_ST0); +- if (STB0899_GETFIELD(RXEND, reg)) { +- +- reg = stb0899_read_reg(state, STB0899_DISRX_ST1); +- length = STB0899_GETFIELD(FIFOBYTENBR, reg); +- +- if (length > sizeof (reply->msg)) { +- result = -EOVERFLOW; +- goto exit; +- } +- reply->msg_len = length; +- +- /* extract data */ +- for (i = 0; i < length; i++) +- reply->msg[i] = stb0899_read_reg(state, STB0899_DISFIFO); +- } +- +- return 0; +-exit: +- +- return result; +-} +- +-static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) +-{ +- u8 reg = 0; +- unsigned long start = jiffies; +- +- while (!STB0899_GETFIELD(TXIDLE, reg)) { +- reg = stb0899_read_reg(state, STB0899_DISSTATUS); +- if (jiffies - start > timeout) { +- dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); +- return -ETIMEDOUT; +- } +- msleep(10); +- } +- return 0; +-} +- +-static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- u8 reg, old_state; +- +- /* wait for diseqc idle */ +- if (stb0899_wait_diseqc_txidle(state, 100) < 0) +- return -ETIMEDOUT; +- +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- old_state = reg; +- /* set to burst mode */ +- STB0899_SETFIELD_VAL(DISEQCMODE, reg, 0x03); +- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x01); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- switch (burst) { +- case SEC_MINI_A: +- /* unmodulated */ +- stb0899_write_reg(state, STB0899_DISFIFO, 0x00); +- break; +- case SEC_MINI_B: +- /* modulated */ +- stb0899_write_reg(state, STB0899_DISFIFO, 0xff); +- break; +- } +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- STB0899_SETFIELD_VAL(DISPRECHARGE, reg, 0x00); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- /* wait for diseqc idle */ +- if (stb0899_wait_diseqc_txidle(state, 100) < 0) +- return -ETIMEDOUT; +- +- /* restore state */ +- stb0899_write_reg(state, STB0899_DISCNTRL1, old_state); +- +- return 0; +-} +- +-static int stb0899_diseqc_init(struct stb0899_state *state) +-{ +- struct dvb_diseqc_master_cmd tx_data; +-/* +- struct dvb_diseqc_slave_reply rx_data; +-*/ +- u8 f22_tx, f22_rx, reg; +- +- u32 mclk, tx_freq = 22000;/* count = 0, i; */ +- tx_data.msg[0] = 0xe2; +- tx_data.msg_len = 3; +- reg = stb0899_read_reg(state, STB0899_DISCNTRL2); +- STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); +- stb0899_write_reg(state, STB0899_DISCNTRL2, reg); +- +- /* disable Tx spy */ +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- STB0899_SETFIELD_VAL(DISEQCRESET, reg, 1); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- +- reg = stb0899_read_reg(state, STB0899_DISCNTRL1); +- STB0899_SETFIELD_VAL(DISEQCRESET, reg, 0); +- stb0899_write_reg(state, STB0899_DISCNTRL1, reg); +- +- mclk = stb0899_get_mclk(state); +- f22_tx = mclk / (tx_freq * 32); +- stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ +- state->rx_freq = 20000; +- f22_rx = mclk / (state->rx_freq * 32); +- +- return 0; +-} +- +-static int stb0899_sleep(struct dvb_frontend *fe) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +-/* +- u8 reg; +-*/ +- dprintk(state->verbose, FE_DEBUG, 1, "Going to Sleep .. (Really tired .. :-))"); +- /* post process event */ +- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 0); +- +- return 0; +-} +- +-static int stb0899_wakeup(struct dvb_frontend *fe) +-{ +- int rc; +- struct stb0899_state *state = fe->demodulator_priv; +- +- if ((rc = stb0899_write_reg(state, STB0899_SYNTCTRL, STB0899_SELOSCI))) +- return rc; +- /* Activate all clocks; DVB-S2 registers are inaccessible otherwise. */ +- if ((rc = stb0899_write_reg(state, STB0899_STOPCLK1, 0x00))) +- return rc; +- if ((rc = stb0899_write_reg(state, STB0899_STOPCLK2, 0x00))) +- return rc; +- +- /* post process event */ +- stb0899_postproc(state, STB0899_POSTPROC_GPIO_POWER, 1); +- +- return 0; +-} +- +-static int stb0899_init(struct dvb_frontend *fe) +-{ +- int i; +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_config *config = state->config; +- +- dprintk(state->verbose, FE_DEBUG, 1, "Initializing STB0899 ... "); +- +- /* init device */ +- dprintk(state->verbose, FE_DEBUG, 1, "init device"); +- for (i = 0; config->init_dev[i].address != 0xffff; i++) +- stb0899_write_reg(state, config->init_dev[i].address, config->init_dev[i].data); +- +- dprintk(state->verbose, FE_DEBUG, 1, "init S2 demod"); +- /* init S2 demod */ +- for (i = 0; config->init_s2_demod[i].offset != 0xffff; i++) +- stb0899_write_s2reg(state, STB0899_S2DEMOD, +- config->init_s2_demod[i].base_address, +- config->init_s2_demod[i].offset, +- config->init_s2_demod[i].data); +- +- dprintk(state->verbose, FE_DEBUG, 1, "init S1 demod"); +- /* init S1 demod */ +- for (i = 0; config->init_s1_demod[i].address != 0xffff; i++) +- stb0899_write_reg(state, config->init_s1_demod[i].address, config->init_s1_demod[i].data); +- +- dprintk(state->verbose, FE_DEBUG, 1, "init S2 FEC"); +- /* init S2 fec */ +- for (i = 0; config->init_s2_fec[i].offset != 0xffff; i++) +- stb0899_write_s2reg(state, STB0899_S2FEC, +- config->init_s2_fec[i].base_address, +- config->init_s2_fec[i].offset, +- config->init_s2_fec[i].data); +- +- dprintk(state->verbose, FE_DEBUG, 1, "init TST"); +- /* init test */ +- for (i = 0; config->init_tst[i].address != 0xffff; i++) +- stb0899_write_reg(state, config->init_tst[i].address, config->init_tst[i].data); +- +- stb0899_init_calc(state); +- stb0899_diseqc_init(state); +- +- return 0; +-} +- +-static int stb0899_table_lookup(const struct stb0899_tab *tab, int max, int val) +-{ +- int res = 0; +- int min = 0, med; +- +- if (val < tab[min].read) +- res = tab[min].real; +- else if (val >= tab[max].read) +- res = tab[max].real; +- else { +- while ((max - min) > 1) { +- med = (max + min) / 2; +- if (val >= tab[min].read && val < tab[med].read) +- max = med; +- else +- min = med; +- } +- res = ((val - tab[min].read) * +- (tab[max].real - tab[min].real) / +- (tab[max].read - tab[min].read)) + +- tab[min].real; +- } +- +- return res; +-} +- +-static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- +- int val; +- u32 reg; +- switch (state->delsys) { +- case SYS_DVBS: +- case SYS_DSS: +- if (internal->lock) { +- reg = stb0899_read_reg(state, STB0899_VSTATUS); +- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { +- +- reg = stb0899_read_reg(state, STB0899_AGCIQIN); +- val = (s32)(s8)STB0899_GETFIELD(AGCIQVALUE, reg); +- +- *strength = stb0899_table_lookup(stb0899_dvbsrf_tab, ARRAY_SIZE(stb0899_dvbsrf_tab) - 1, val); +- *strength += 750; +- dprintk(state->verbose, FE_DEBUG, 1, "AGCIQVALUE = 0x%02x, C = %d * 0.1 dBm", +- val & 0xff, *strength); +- } +- } +- break; +- case SYS_DVBS2: +- if (internal->lock) { +- reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN); +- val = STB0899_GETFIELD(IF_AGC_GAIN, reg); +- +- *strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val); +- *strength += 750; +- dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm", +- val & 0x3fff, *strength); +- } +- break; +- default: +- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- +- unsigned int val, quant, quantn = -1, est, estn = -1; +- u8 buf[2]; +- u32 reg; +- +- reg = stb0899_read_reg(state, STB0899_VSTATUS); +- switch (state->delsys) { +- case SYS_DVBS: +- case SYS_DSS: +- if (internal->lock) { +- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { +- +- stb0899_read_regs(state, STB0899_NIRM, buf, 2); +- val = MAKEWORD16(buf[0], buf[1]); +- +- *snr = stb0899_table_lookup(stb0899_cn_tab, ARRAY_SIZE(stb0899_cn_tab) - 1, val); +- dprintk(state->verbose, FE_DEBUG, 1, "NIR = 0x%02x%02x = %u, C/N = %d * 0.1 dBm\n", +- buf[0], buf[1], val, *snr); +- } +- } +- break; +- case SYS_DVBS2: +- if (internal->lock) { +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_CNTRL1); +- quant = STB0899_GETFIELD(UWP_ESN0_QUANT, reg); +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, UWP_STAT2); +- est = STB0899_GETFIELD(ESN0_EST, reg); +- if (est == 1) +- val = 301; /* C/N = 30.1 dB */ +- else if (est == 2) +- val = 270; /* C/N = 27.0 dB */ +- else { +- /* quantn = 100 * log(quant^2) */ +- quantn = stb0899_table_lookup(stb0899_quant_tab, ARRAY_SIZE(stb0899_quant_tab) - 1, quant * 100); +- /* estn = 100 * log(est) */ +- estn = stb0899_table_lookup(stb0899_est_tab, ARRAY_SIZE(stb0899_est_tab) - 1, est); +- /* snr(dBm/10) = -10*(log(est)-log(quant^2)) => snr(dBm/10) = (100*log(quant^2)-100*log(est))/10 */ +- val = (quantn - estn) / 10; +- } +- *snr = val; +- dprintk(state->verbose, FE_DEBUG, 1, "Es/N0 quant = %d (%d) estimate = %u (%d), C/N = %d * 0.1 dBm", +- quant, quantn, est, estn, val); +- } +- break; +- default: +- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- u8 reg; +- *status = 0; +- +- switch (state->delsys) { +- case SYS_DVBS: +- case SYS_DSS: +- dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S/DSS"); +- if (internal->lock) { +- reg = stb0899_read_reg(state, STB0899_VSTATUS); +- if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) { +- dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK"); +- *status |= FE_HAS_CARRIER | FE_HAS_LOCK; +- +- reg = stb0899_read_reg(state, STB0899_PLPARM); +- if (STB0899_GETFIELD(VITCURPUN, reg)) { +- dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_VITERBI | FE_HAS_SYNC"); +- *status |= FE_HAS_VITERBI | FE_HAS_SYNC; +- /* post process event */ +- stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); +- } +- } +- } +- break; +- case SYS_DVBS2: +- dprintk(state->verbose, FE_DEBUG, 1, "Delivery system DVB-S2"); +- if (internal->lock) { +- reg = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_STAT2); +- if (STB0899_GETFIELD(UWP_LOCK, reg) && STB0899_GETFIELD(CSM_LOCK, reg)) { +- *status |= FE_HAS_CARRIER; +- dprintk(state->verbose, FE_DEBUG, 1, +- "UWP & CSM Lock ! ---> DVB-S2 FE_HAS_CARRIER"); +- +- reg = stb0899_read_reg(state, STB0899_CFGPDELSTATUS1); +- if (STB0899_GETFIELD(CFGPDELSTATUS_LOCK, reg)) { +- *status |= FE_HAS_LOCK; +- dprintk(state->verbose, FE_DEBUG, 1, +- "Packet Delineator Locked ! -----> DVB-S2 FE_HAS_LOCK"); +- +- } +- if (STB0899_GETFIELD(CONTINUOUS_STREAM, reg)) { +- *status |= FE_HAS_VITERBI; +- dprintk(state->verbose, FE_DEBUG, 1, +- "Packet Delineator found VITERBI ! -----> DVB-S2 FE_HAS_VITERBI"); +- } +- if (STB0899_GETFIELD(ACCEPTED_STREAM, reg)) { +- *status |= FE_HAS_SYNC; +- dprintk(state->verbose, FE_DEBUG, 1, +- "Packet Delineator found SYNC ! -----> DVB-S2 FE_HAS_SYNC"); +- /* post process event */ +- stb0899_postproc(state, STB0899_POSTPROC_GPIO_LOCK, 1); +- } +- } +- } +- break; +- default: +- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); +- return -EINVAL; +- } +- return 0; +-} +- +-/* +- * stb0899_get_error +- * viterbi error for DVB-S/DSS +- * packet error for DVB-S2 +- * Bit Error Rate or Packet Error Rate * 10 ^ 7 +- */ +-static int stb0899_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- +- u8 lsb, msb; +- u32 i; +- +- *ber = 0; +- +- switch (state->delsys) { +- case SYS_DVBS: +- case SYS_DSS: +- if (internal->lock) { +- /* average 5 BER values */ +- for (i = 0; i < 5; i++) { +- msleep(100); +- lsb = stb0899_read_reg(state, STB0899_ECNT1L); +- msb = stb0899_read_reg(state, STB0899_ECNT1M); +- *ber += MAKEWORD16(msb, lsb); +- } +- *ber /= 5; +- /* Viterbi Check */ +- if (STB0899_GETFIELD(VSTATUS_PRFVIT, internal->v_status)) { +- /* Error Rate */ +- *ber *= 9766; +- /* ber = ber * 10 ^ 7 */ +- *ber /= (-1 + (1 << (2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); +- *ber /= 8; +- } +- } +- break; +- case SYS_DVBS2: +- if (internal->lock) { +- /* Average 5 PER values */ +- for (i = 0; i < 5; i++) { +- msleep(100); +- lsb = stb0899_read_reg(state, STB0899_ECNT1L); +- msb = stb0899_read_reg(state, STB0899_ECNT1M); +- *ber += MAKEWORD16(msb, lsb); +- } +- /* ber = ber * 10 ^ 7 */ +- *ber *= 10000000; +- *ber /= (-1 + (1 << (4 + 2 * STB0899_GETFIELD(NOE, internal->err_ctrl)))); +- } +- break; +- default: +- dprintk(state->verbose, FE_DEBUG, 1, "Unsupported delivery system"); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int stb0899_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); +- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); +- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x00); +- break; +- case SEC_VOLTAGE_18: +- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x02); +- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x02); +- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); +- break; +- case SEC_VOLTAGE_OFF: +- stb0899_write_reg(state, STB0899_GPIO00CFG, 0x82); +- stb0899_write_reg(state, STB0899_GPIO01CFG, 0x82); +- stb0899_write_reg(state, STB0899_GPIO02CFG, 0x82); +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int stb0899_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- +- u8 div, reg; +- +- /* wait for diseqc idle */ +- if (stb0899_wait_diseqc_txidle(state, 100) < 0) +- return -ETIMEDOUT; +- +- switch (tone) { +- case SEC_TONE_ON: +- div = (internal->master_clk / 100) / 5632; +- div = (div + 5) / 10; +- stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x66); +- reg = stb0899_read_reg(state, STB0899_ACRPRESC); +- STB0899_SETFIELD_VAL(ACRPRESC, reg, 0x03); +- stb0899_write_reg(state, STB0899_ACRPRESC, reg); +- stb0899_write_reg(state, STB0899_ACRDIV1, div); +- break; +- case SEC_TONE_OFF: +- stb0899_write_reg(state, STB0899_DISEQCOCFG, 0x20); +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- int i2c_stat; +- struct stb0899_state *state = fe->demodulator_priv; +- +- i2c_stat = stb0899_read_reg(state, STB0899_I2CRPT); +- if (i2c_stat < 0) +- goto err; +- +- if (enable) { +- dprintk(state->verbose, FE_DEBUG, 1, "Enabling I2C Repeater ..."); +- i2c_stat |= STB0899_I2CTON; +- if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) +- goto err; +- } else { +- dprintk(state->verbose, FE_DEBUG, 1, "Disabling I2C Repeater ..."); +- i2c_stat &= ~STB0899_I2CTON; +- if (stb0899_write_reg(state, STB0899_I2CRPT, i2c_stat) < 0) +- goto err; +- } +- return 0; +-err: +- dprintk(state->verbose, FE_ERROR, 1, "I2C Repeater control failed"); +- return -EREMOTEIO; +-} +- +- +-static inline void CONVERT32(u32 x, char *str) +-{ +- *str++ = (x >> 24) & 0xff; +- *str++ = (x >> 16) & 0xff; +- *str++ = (x >> 8) & 0xff; +- *str++ = (x >> 0) & 0xff; +- *str = '\0'; +-} +- +-int stb0899_get_dev_id(struct stb0899_state *state) +-{ +- u8 chip_id, release; +- u16 id; +- u32 demod_ver = 0, fec_ver = 0; +- char demod_str[5] = { 0 }; +- char fec_str[5] = { 0 }; +- +- id = stb0899_read_reg(state, STB0899_DEV_ID); +- dprintk(state->verbose, FE_DEBUG, 1, "ID reg=[0x%02x]", id); +- chip_id = STB0899_GETFIELD(CHIP_ID, id); +- release = STB0899_GETFIELD(CHIP_REL, id); +- +- dprintk(state->verbose, FE_ERROR, 1, "Device ID=[%d], Release=[%d]", +- chip_id, release); +- +- CONVERT32(STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_CORE_ID), (char *)&demod_str); +- +- demod_ver = STB0899_READ_S2REG(STB0899_S2DEMOD, DMD_VERSION_ID); +- dprintk(state->verbose, FE_ERROR, 1, "Demodulator Core ID=[%s], Version=[%d]", (char *) &demod_str, demod_ver); +- CONVERT32(STB0899_READ_S2REG(STB0899_S2FEC, FEC_CORE_ID_REG), (char *)&fec_str); +- fec_ver = STB0899_READ_S2REG(STB0899_S2FEC, FEC_VER_ID_REG); +- if (! (chip_id > 0)) { +- dprintk(state->verbose, FE_ERROR, 1, "couldn't find a STB 0899"); +- +- return -ENODEV; +- } +- dprintk(state->verbose, FE_ERROR, 1, "FEC Core ID=[%s], Version=[%d]", (char*) &fec_str, fec_ver); +- +- return 0; +-} +- +-static void stb0899_set_delivery(struct stb0899_state *state) +-{ +- u8 reg; +- u8 stop_clk[2]; +- +- stop_clk[0] = stb0899_read_reg(state, STB0899_STOPCLK1); +- stop_clk[1] = stb0899_read_reg(state, STB0899_STOPCLK2); +- +- switch (state->delsys) { +- case SYS_DVBS: +- dprintk(state->verbose, FE_DEBUG, 1, "Delivery System -- DVB-S"); +- /* FECM/Viterbi ON */ +- reg = stb0899_read_reg(state, STB0899_FECM); +- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); +- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); +- stb0899_write_reg(state, STB0899_FECM, reg); +- +- stb0899_write_reg(state, STB0899_RSULC, 0xb1); +- stb0899_write_reg(state, STB0899_TSULC, 0x40); +- stb0899_write_reg(state, STB0899_RSLLC, 0x42); +- stb0899_write_reg(state, STB0899_TSLPL, 0x12); +- +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); +- +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); +- +- STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); +- +- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); +- break; +- case SYS_DVBS2: +- /* FECM/Viterbi OFF */ +- reg = stb0899_read_reg(state, STB0899_FECM); +- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 0); +- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 0); +- stb0899_write_reg(state, STB0899_FECM, reg); +- +- stb0899_write_reg(state, STB0899_RSULC, 0xb1); +- stb0899_write_reg(state, STB0899_TSULC, 0x42); +- stb0899_write_reg(state, STB0899_RSLLC, 0x40); +- stb0899_write_reg(state, STB0899_TSLPL, 0x02); +- +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESLDPC, reg, 0); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 0); +- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 0); +- +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 0); +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 0); +- +- STB0899_SETFIELD_VAL(STOP_CKINTBUF216, stop_clk[0], 0); +- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); +- +- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 0); +- break; +- case SYS_DSS: +- /* FECM/Viterbi ON */ +- reg = stb0899_read_reg(state, STB0899_FECM); +- STB0899_SETFIELD_VAL(FECM_RSVD0, reg, 1); +- STB0899_SETFIELD_VAL(FECM_VITERBI_ON, reg, 1); +- stb0899_write_reg(state, STB0899_FECM, reg); +- +- stb0899_write_reg(state, STB0899_RSULC, 0xa1); +- stb0899_write_reg(state, STB0899_TSULC, 0x61); +- stb0899_write_reg(state, STB0899_RSLLC, 0x42); +- +- reg = stb0899_read_reg(state, STB0899_TSTRES); +- STB0899_SETFIELD_VAL(FRESLDPC, reg, 1); +- stb0899_write_reg(state, STB0899_TSTRES, reg); +- +- STB0899_SETFIELD_VAL(STOP_CHK8PSK, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKFEC108, stop_clk[0], 1); +- STB0899_SETFIELD_VAL(STOP_CKFEC216, stop_clk[0], 1); +- +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN108, stop_clk[1], 1); +- STB0899_SETFIELD_VAL(STOP_CKPKDLIN216, stop_clk[1], 1); +- +- STB0899_SETFIELD_VAL(STOP_CKCORE216, stop_clk[0], 0); +- +- STB0899_SETFIELD_VAL(STOP_CKS2DMD108, stop_clk[1], 1); +- break; +- default: +- dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); +- break; +- } +- STB0899_SETFIELD_VAL(STOP_CKADCI108, stop_clk[0], 0); +- stb0899_write_regs(state, STB0899_STOPCLK1, stop_clk, 2); +-} +- +-/* +- * stb0899_set_iterations +- * set the LDPC iteration scale function +- */ +-static void stb0899_set_iterations(struct stb0899_state *state) +-{ +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- +- s32 iter_scale; +- u32 reg; +- +- iter_scale = 17 * (internal->master_clk / 1000); +- iter_scale += 410000; +- iter_scale /= (internal->srate / 1000000); +- iter_scale /= 1000; +- +- if (iter_scale > config->ldpc_max_iter) +- iter_scale = config->ldpc_max_iter; +- +- reg = STB0899_READ_S2REG(STB0899_S2FEC, MAX_ITER); +- STB0899_SETFIELD_VAL(MAX_ITERATIONS, reg, iter_scale); +- stb0899_write_s2reg(state, STB0899_S2FEC, STB0899_BASE_MAX_ITER, STB0899_OFF0_MAX_ITER, reg); +-} +- +-static enum dvbfe_search stb0899_search(struct dvb_frontend *fe) +-{ +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_params *i_params = &state->params; +- struct stb0899_internal *internal = &state->internal; +- struct stb0899_config *config = state->config; +- struct dtv_frontend_properties *props = &fe->dtv_property_cache; +- +- u32 SearchRange, gain; +- +- i_params->freq = props->frequency; +- i_params->srate = props->symbol_rate; +- state->delsys = props->delivery_system; +- dprintk(state->verbose, FE_DEBUG, 1, "delivery system=%d", state->delsys); +- +- SearchRange = 10000000; +- dprintk(state->verbose, FE_DEBUG, 1, "Frequency=%d, Srate=%d", i_params->freq, i_params->srate); +- /* checking Search Range is meaningless for a fixed 3 Mhz */ +- if (INRANGE(i_params->srate, 1000000, 45000000)) { +- dprintk(state->verbose, FE_DEBUG, 1, "Parameters IN RANGE"); +- stb0899_set_delivery(state); +- +- if (state->config->tuner_set_rfsiggain) { +- if (internal->srate > 15000000) +- gain = 8; /* 15Mb < srate < 45Mb, gain = 8dB */ +- else if (internal->srate > 5000000) +- gain = 12; /* 5Mb < srate < 15Mb, gain = 12dB */ +- else +- gain = 14; /* 1Mb < srate < 5Mb, gain = 14db */ +- state->config->tuner_set_rfsiggain(fe, gain); +- } +- +- if (i_params->srate <= 5000000) +- stb0899_set_mclk(state, config->lo_clk); +- else +- stb0899_set_mclk(state, config->hi_clk); +- +- switch (state->delsys) { +- case SYS_DVBS: +- case SYS_DSS: +- dprintk(state->verbose, FE_DEBUG, 1, "DVB-S delivery system"); +- internal->freq = i_params->freq; +- internal->srate = i_params->srate; +- /* +- * search = user search range + +- * 500Khz + +- * 2 * Tuner_step_size + +- * 10% of the symbol rate +- */ +- internal->srch_range = SearchRange + 1500000 + (i_params->srate / 5); +- internal->derot_percent = 30; +- +- /* What to do for tuners having no bandwidth setup ? */ +- /* enable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 1); +- +- if (state->config->tuner_set_bandwidth) +- state->config->tuner_set_bandwidth(fe, (13 * (stb0899_carr_width(state) + SearchRange)) / 10); +- if (state->config->tuner_get_bandwidth) +- state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); +- +- /* disable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 0); +- +- /* Set DVB-S1 AGC */ +- stb0899_write_reg(state, STB0899_AGCRFCFG, 0x11); +- +- /* Run the search algorithm */ +- dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S search algo .."); +- if (stb0899_dvbs_algo(state) == RANGEOK) { +- internal->lock = 1; +- dprintk(state->verbose, FE_DEBUG, 1, +- "-------------------------------------> DVB-S LOCK !"); +- +-// stb0899_write_reg(state, STB0899_ERRCTRL1, 0x3d); /* Viterbi Errors */ +-// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +-// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); +-// dprintk(state->verbose, FE_DEBUG, 1, "VSTATUS=0x%02x", internal->v_status); +-// dprintk(state->verbose, FE_DEBUG, 1, "ERR_CTRL=0x%02x", internal->err_ctrl); +- +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } else { +- internal->lock = 0; +- +- return DVBFE_ALGO_SEARCH_FAILED; +- } +- break; +- case SYS_DVBS2: +- internal->freq = i_params->freq; +- internal->srate = i_params->srate; +- internal->srch_range = SearchRange; +- +- /* enable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 1); +- +- if (state->config->tuner_set_bandwidth) +- state->config->tuner_set_bandwidth(fe, (stb0899_carr_width(state) + SearchRange)); +- if (state->config->tuner_get_bandwidth) +- state->config->tuner_get_bandwidth(fe, &internal->tuner_bw); +- +- /* disable tuner I/O */ +- stb0899_i2c_gate_ctrl(&state->frontend, 0); +- +-// pParams->SpectralInv = pSearch->IQ_Inversion; +- +- /* Set DVB-S2 AGC */ +- stb0899_write_reg(state, STB0899_AGCRFCFG, 0x1c); +- +- /* Set IterScale =f(MCLK,SYMB) */ +- stb0899_set_iterations(state); +- +- /* Run the search algorithm */ +- dprintk(state->verbose, FE_DEBUG, 1, "running DVB-S2 search algo .."); +- if (stb0899_dvbs2_algo(state) == DVBS2_FEC_LOCK) { +- internal->lock = 1; +- dprintk(state->verbose, FE_DEBUG, 1, +- "-------------------------------------> DVB-S2 LOCK !"); +- +-// stb0899_write_reg(state, STB0899_ERRCTRL1, 0xb6); /* Packet Errors */ +-// internal->v_status = stb0899_read_reg(state, STB0899_VSTATUS); +-// internal->err_ctrl = stb0899_read_reg(state, STB0899_ERRCTRL1); +- +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } else { +- internal->lock = 0; +- +- return DVBFE_ALGO_SEARCH_FAILED; +- } +- break; +- default: +- dprintk(state->verbose, FE_ERROR, 1, "Unsupported delivery system"); +- return DVBFE_ALGO_SEARCH_INVALID; +- } +- } +- +- return DVBFE_ALGO_SEARCH_ERROR; +-} +- +-static int stb0899_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stb0899_state *state = fe->demodulator_priv; +- struct stb0899_internal *internal = &state->internal; +- +- dprintk(state->verbose, FE_DEBUG, 1, "Get params"); +- p->symbol_rate = internal->srate; +- +- return 0; +-} +- +-static enum dvbfe_algo stb0899_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-static struct dvb_frontend_ops stb0899_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, +- .info = { +- .name = "STB0899 Multistandard", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 0, +- .frequency_tolerance = 0, +- .symbol_rate_min = 5000000, +- .symbol_rate_max = 45000000, +- +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_AUTO | +- FE_CAN_2G_MODULATION | +- FE_CAN_QPSK +- }, +- +- .release = stb0899_release, +- .init = stb0899_init, +- .sleep = stb0899_sleep, +-// .wakeup = stb0899_wakeup, +- +- .i2c_gate_ctrl = stb0899_i2c_gate_ctrl, +- +- .get_frontend_algo = stb0899_frontend_algo, +- .search = stb0899_search, +- .get_frontend = stb0899_get_frontend, +- +- +- .read_status = stb0899_read_status, +- .read_snr = stb0899_read_snr, +- .read_signal_strength = stb0899_read_signal_strength, +- .read_ber = stb0899_read_ber, +- +- .set_voltage = stb0899_set_voltage, +- .set_tone = stb0899_set_tone, +- +- .diseqc_send_master_cmd = stb0899_send_diseqc_msg, +- .diseqc_recv_slave_reply = stb0899_recv_slave_reply, +- .diseqc_send_burst = stb0899_send_diseqc_burst, +-}; +- +-struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_adapter *i2c) +-{ +- struct stb0899_state *state = NULL; +- enum stb0899_inversion inversion; +- +- state = kzalloc(sizeof (struct stb0899_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- inversion = config->inversion; +- state->verbose = &verbose; +- state->config = config; +- state->i2c = i2c; +- state->frontend.ops = stb0899_ops; +- state->frontend.demodulator_priv = state; +- state->internal.inversion = inversion; +- +- stb0899_wakeup(&state->frontend); +- if (stb0899_get_dev_id(state) == -ENODEV) { +- printk("%s: Exiting .. !\n", __func__); +- goto error; +- } +- +- printk("%s: Attaching STB0899 \n", __func__); +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(stb0899_attach); +-MODULE_PARM_DESC(verbose, "Set Verbosity level"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stb0899_drv.h b/drivers/media/dvb/frontends/stb0899_drv.h +deleted file mode 100644 +index 98b200c..0000000 +--- a/drivers/media/dvb/frontends/stb0899_drv.h ++++ /dev/null +@@ -1,162 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STB0899_DRV_H +-#define __STB0899_DRV_H +- +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#define STB0899_TSMODE_SERIAL 1 +-#define STB0899_CLKPOL_FALLING 2 +-#define STB0899_CLKNULL_PARITY 3 +-#define STB0899_SYNC_FORCED 4 +-#define STB0899_FECMODE_DSS 5 +- +-struct stb0899_s1_reg { +- u16 address; +- u8 data; +-}; +- +-struct stb0899_s2_reg { +- u16 offset; +- u32 base_address; +- u32 data; +-}; +- +-enum stb0899_inversion { +- IQ_SWAP_OFF = 0, +- IQ_SWAP_ON, +- IQ_SWAP_AUTO +-}; +- +-#define STB0899_GPIO00 0xf140 +-#define STB0899_GPIO01 0xf141 +-#define STB0899_GPIO02 0xf142 +-#define STB0899_GPIO03 0xf143 +-#define STB0899_GPIO04 0xf144 +-#define STB0899_GPIO05 0xf145 +-#define STB0899_GPIO06 0xf146 +-#define STB0899_GPIO07 0xf147 +-#define STB0899_GPIO08 0xf148 +-#define STB0899_GPIO09 0xf149 +-#define STB0899_GPIO10 0xf14a +-#define STB0899_GPIO11 0xf14b +-#define STB0899_GPIO12 0xf14c +-#define STB0899_GPIO13 0xf14d +-#define STB0899_GPIO14 0xf14e +-#define STB0899_GPIO15 0xf14f +-#define STB0899_GPIO16 0xf150 +-#define STB0899_GPIO17 0xf151 +-#define STB0899_GPIO18 0xf152 +-#define STB0899_GPIO19 0xf153 +-#define STB0899_GPIO20 0xf154 +- +-#define STB0899_GPIOPULLUP 0x01 /* Output device is connected to Vdd */ +-#define STB0899_GPIOPULLDN 0x00 /* Output device is connected to Vss */ +- +-#define STB0899_POSTPROC_GPIO_POWER 0x00 +-#define STB0899_POSTPROC_GPIO_LOCK 0x01 +- +-/* +- * Post process output configuration control +- * 1. POWER ON/OFF (index 0) +- * 2. FE_HAS_LOCK/LOCK_LOSS (index 1) +- * +- * @gpio = one of the above listed GPIO's +- * @level = output state: pulled up or low +- */ +-struct stb0899_postproc { +- u16 gpio; +- u8 level; +-}; +- +-struct stb0899_config { +- const struct stb0899_s1_reg *init_dev; +- const struct stb0899_s2_reg *init_s2_demod; +- const struct stb0899_s1_reg *init_s1_demod; +- const struct stb0899_s2_reg *init_s2_fec; +- const struct stb0899_s1_reg *init_tst; +- +- const struct stb0899_postproc *postproc; +- +- enum stb0899_inversion inversion; +- +- u32 xtal_freq; +- +- u8 demod_address; +- u8 ts_output_mode; +- u8 block_sync_mode; +- u8 ts_pfbit_toggle; +- +- u8 clock_polarity; +- u8 data_clk_parity; +- u8 fec_mode; +- u8 data_output_ctl; +- u8 data_fifo_mode; +- u8 out_rate_comp; +- u8 i2c_repeater; +-// int inversion; +- int lo_clk; +- int hi_clk; +- +- u32 esno_ave; +- u32 esno_quant; +- u32 avframes_coarse; +- u32 avframes_fine; +- u32 miss_threshold; +- u32 uwp_threshold_acq; +- u32 uwp_threshold_track; +- u32 uwp_threshold_sof; +- u32 sof_search_timeout; +- +- u32 btr_nco_bits; +- u32 btr_gain_shift_offset; +- u32 crl_nco_bits; +- u32 ldpc_max_iter; +- +- int (*tuner_set_frequency)(struct dvb_frontend *fe, u32 frequency); +- int (*tuner_get_frequency)(struct dvb_frontend *fe, u32 *frequency); +- int (*tuner_set_bandwidth)(struct dvb_frontend *fe, u32 bandwidth); +- int (*tuner_get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); +- int (*tuner_set_rfsiggain)(struct dvb_frontend *fe, u32 rf_gain); +-}; +- +-#if defined(CONFIG_DVB_STB0899) || (defined(CONFIG_DVB_STB0899_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *stb0899_attach(struct stb0899_config *config, +- struct i2c_adapter *i2c); +- +-#else +- +-static inline struct dvb_frontend *stb0899_attach(struct stb0899_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif //CONFIG_DVB_STB0899 +- +- +-#endif +diff --git a/drivers/media/dvb/frontends/stb0899_priv.h b/drivers/media/dvb/frontends/stb0899_priv.h +deleted file mode 100644 +index 82395b9..0000000 +--- a/drivers/media/dvb/frontends/stb0899_priv.h ++++ /dev/null +@@ -1,263 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STB0899_PRIV_H +-#define __STB0899_PRIV_H +- +-#include "dvb_frontend.h" +-#include "stb0899_drv.h" +- +-#define FE_ERROR 0 +-#define FE_NOTICE 1 +-#define FE_INFO 2 +-#define FE_DEBUG 3 +-#define FE_DEBUGREG 4 +- +-#define dprintk(x, y, z, format, arg...) do { \ +- if (z) { \ +- if ((*x > FE_ERROR) && (*x > y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((*x > FE_NOTICE) && (*x > y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((*x > FE_INFO) && (*x > y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((*x > FE_DEBUG) && (*x > y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (*x > y) \ +- printk(format, ##arg); \ +- } \ +-} while(0) +- +-#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ +- ((y <= val) && (val <= x)) ? 1 : 0) +- +-#define BYTE0 0 +-#define BYTE1 8 +-#define BYTE2 16 +-#define BYTE3 24 +- +-#define GETBYTE(x, y) (((x) >> (y)) & 0xff) +-#define MAKEWORD32(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +-#define MAKEWORD16(a, b) (((a) << 8) | (b)) +- +-#define LSB(x) ((x & 0xff)) +-#define MSB(y) ((y >> 8) & 0xff) +- +- +-#define STB0899_GETFIELD(bitf, val) ((val >> STB0899_OFFST_##bitf) & ((1 << STB0899_WIDTH_##bitf) - 1)) +- +- +-#define STB0899_SETFIELD(mask, val, width, offset) (mask & (~(((1 << width) - 1) << \ +- offset))) | ((val & \ +- ((1 << width) - 1)) << offset) +- +-#define STB0899_SETFIELD_VAL(bitf, mask, val) (mask = (mask & (~(((1 << STB0899_WIDTH_##bitf) - 1) <<\ +- STB0899_OFFST_##bitf))) | \ +- (val << STB0899_OFFST_##bitf)) +- +- +-enum stb0899_status { +- NOAGC1 = 0, +- AGC1OK, +- NOTIMING, +- ANALOGCARRIER, +- TIMINGOK, +- NOAGC2, +- AGC2OK, +- NOCARRIER, +- CARRIEROK, +- NODATA, +- FALSELOCK, +- DATAOK, +- OUTOFRANGE, +- RANGEOK, +- DVBS2_DEMOD_LOCK, +- DVBS2_DEMOD_NOLOCK, +- DVBS2_FEC_LOCK, +- DVBS2_FEC_NOLOCK +-}; +- +-enum stb0899_modcod { +- STB0899_DUMMY_PLF, +- STB0899_QPSK_14, +- STB0899_QPSK_13, +- STB0899_QPSK_25, +- STB0899_QPSK_12, +- STB0899_QPSK_35, +- STB0899_QPSK_23, +- STB0899_QPSK_34, +- STB0899_QPSK_45, +- STB0899_QPSK_56, +- STB0899_QPSK_89, +- STB0899_QPSK_910, +- STB0899_8PSK_35, +- STB0899_8PSK_23, +- STB0899_8PSK_34, +- STB0899_8PSK_56, +- STB0899_8PSK_89, +- STB0899_8PSK_910, +- STB0899_16APSK_23, +- STB0899_16APSK_34, +- STB0899_16APSK_45, +- STB0899_16APSK_56, +- STB0899_16APSK_89, +- STB0899_16APSK_910, +- STB0899_32APSK_34, +- STB0899_32APSK_45, +- STB0899_32APSK_56, +- STB0899_32APSK_89, +- STB0899_32APSK_910 +-}; +- +-enum stb0899_frame { +- STB0899_LONG_FRAME, +- STB0899_SHORT_FRAME +-}; +- +-enum stb0899_alpha { +- RRC_20, +- RRC_25, +- RRC_35 +-}; +- +-struct stb0899_tab { +- s32 real; +- s32 read; +-}; +- +-enum stb0899_fec { +- STB0899_FEC_1_2 = 13, +- STB0899_FEC_2_3 = 18, +- STB0899_FEC_3_4 = 21, +- STB0899_FEC_5_6 = 24, +- STB0899_FEC_6_7 = 25, +- STB0899_FEC_7_8 = 26 +-}; +- +-struct stb0899_params { +- u32 freq; /* Frequency */ +- u32 srate; /* Symbol rate */ +- enum fe_code_rate fecrate; +-}; +- +-struct stb0899_internal { +- u32 master_clk; +- u32 freq; /* Demod internal Frequency */ +- u32 srate; /* Demod internal Symbol rate */ +- enum stb0899_fec fecrate; /* Demod internal FEC rate */ +- s32 srch_range; /* Demod internal Search Range */ +- s32 sub_range; /* Demod current sub range (Hz) */ +- s32 tuner_step; /* Tuner step (Hz) */ +- s32 tuner_offst; /* Relative offset to carrier (Hz) */ +- u32 tuner_bw; /* Current bandwidth of the tuner (Hz) */ +- +- s32 mclk; /* Masterclock Divider factor (binary) */ +- s32 rolloff; /* Current RollOff of the filter (x100) */ +- +- s16 derot_freq; /* Current derotator frequency (Hz) */ +- s16 derot_percent; +- +- s16 direction; /* Current derotator search direction */ +- s16 derot_step; /* Derotator step (binary value) */ +- s16 t_derot; /* Derotator time constant (ms) */ +- s16 t_data; /* Data recovery time constant (ms) */ +- s16 sub_dir; /* Direction of the next sub range */ +- +- s16 t_agc1; /* Agc1 time constant (ms) */ +- s16 t_agc2; /* Agc2 time constant (ms) */ +- +- u32 lock; /* Demod internal lock state */ +- enum stb0899_status status; /* Demod internal status */ +- +- /* DVB-S2 */ +- s32 agc_gain; /* RF AGC Gain */ +- s32 center_freq; /* Nominal carrier frequency */ +- s32 av_frame_coarse; /* Coarse carrier freq search frames */ +- s32 av_frame_fine; /* Fine carrier freq search frames */ +- +- s16 step_size; /* Carrier frequency search step size */ +- +- enum stb0899_alpha rrc_alpha; +- enum stb0899_inversion inversion; +- enum stb0899_modcod modcod; +- u8 pilots; /* Pilots found */ +- +- enum stb0899_frame frame_length; +- u8 v_status; /* VSTATUS */ +- u8 err_ctrl; /* ERRCTRLn */ +-}; +- +-struct stb0899_state { +- struct i2c_adapter *i2c; +- struct stb0899_config *config; +- struct dvb_frontend frontend; +- +- u32 *verbose; /* Cached module verbosity level */ +- +- struct stb0899_internal internal; /* Device internal parameters */ +- +- /* cached params from API */ +- enum fe_delivery_system delsys; +- struct stb0899_params params; +- +- u32 rx_freq; /* DiSEqC 2.0 receiver freq */ +- struct mutex search_lock; +-}; +-/* stb0899.c */ +-extern int stb0899_read_reg(struct stb0899_state *state, +- unsigned int reg); +- +-extern u32 _stb0899_read_s2reg(struct stb0899_state *state, +- u32 stb0899_i2cdev, +- u32 stb0899_base_addr, +- u16 stb0899_reg_offset); +- +-extern int stb0899_read_regs(struct stb0899_state *state, +- unsigned int reg, u8 *buf, +- u32 count); +- +-extern int stb0899_write_regs(struct stb0899_state *state, +- unsigned int reg, u8 *data, +- u32 count); +- +-extern int stb0899_write_reg(struct stb0899_state *state, +- unsigned int reg, +- u8 data); +- +-extern int stb0899_write_s2reg(struct stb0899_state *state, +- u32 stb0899_i2cdev, +- u32 stb0899_base_addr, +- u16 stb0899_reg_offset, +- u32 stb0899_data); +- +-extern int stb0899_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); +- +- +-#define STB0899_READ_S2REG(DEVICE, REG) (_stb0899_read_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG)) +-//#define STB0899_WRITE_S2REG(DEVICE, REG, DATA) (_stb0899_write_s2reg(state, DEVICE, STB0899_BASE_##REG, STB0899_OFF0_##REG, DATA)) +- +-/* stb0899_algo.c */ +-extern enum stb0899_status stb0899_dvbs_algo(struct stb0899_state *state); +-extern enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state); +-extern long stb0899_carr_width(struct stb0899_state *state); +- +-#endif //__STB0899_PRIV_H +diff --git a/drivers/media/dvb/frontends/stb0899_reg.h b/drivers/media/dvb/frontends/stb0899_reg.h +deleted file mode 100644 +index ba1ed56..0000000 +--- a/drivers/media/dvb/frontends/stb0899_reg.h ++++ /dev/null +@@ -1,2027 +0,0 @@ +-/* +- STB0899 Multistandard Frontend driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STB0899_REG_H +-#define __STB0899_REG_H +- +-/* S1 */ +-#define STB0899_DEV_ID 0xf000 +-#define STB0899_CHIP_ID (0x0f << 4) +-#define STB0899_OFFST_CHIP_ID 4 +-#define STB0899_WIDTH_CHIP_ID 4 +-#define STB0899_CHIP_REL (0x0f << 0) +-#define STB0899_OFFST_CHIP_REL 0 +-#define STB0899_WIDTH_CHIP_REL 4 +- +-#define STB0899_DEMOD 0xf40e +-#define STB0899_MODECOEFF (0x01 << 0) +-#define STB0899_OFFST_MODECOEFF 0 +-#define STB0899_WIDTH_MODECOEFF 1 +- +-#define STB0899_RCOMPC 0xf410 +-#define STB0899_AGC1CN 0xf412 +-#define STB0899_AGC1REF 0xf413 +-#define STB0899_RTC 0xf417 +-#define STB0899_TMGCFG 0xf418 +-#define STB0899_AGC2REF 0xf419 +-#define STB0899_TLSR 0xf41a +- +-#define STB0899_CFD 0xf41b +-#define STB0899_CFD_ON (0x01 << 7) +-#define STB0899_OFFST_CFD_ON 7 +-#define STB0899_WIDTH_CFD_ON 1 +- +-#define STB0899_ACLC 0xf41c +- +-#define STB0899_BCLC 0xf41d +-#define STB0899_OFFST_ALGO 6 +-#define STB0899_WIDTH_ALGO_QPSK2 2 +-#define STB0899_ALGO_QPSK2 (2 << 6) +-#define STB0899_ALGO_QPSK1 (1 << 6) +-#define STB0899_ALGO_BPSK (0 << 6) +-#define STB0899_OFFST_BETA 0 +-#define STB0899_WIDTH_BETA 6 +- +-#define STB0899_EQON 0xf41e +-#define STB0899_LDT 0xf41f +-#define STB0899_LDT2 0xf420 +-#define STB0899_EQUALREF 0xf425 +-#define STB0899_TMGRAMP 0xf426 +-#define STB0899_TMGTHD 0xf427 +-#define STB0899_IDCCOMP 0xf428 +-#define STB0899_QDCCOMP 0xf429 +-#define STB0899_POWERI 0xf42a +-#define STB0899_POWERQ 0xf42b +-#define STB0899_RCOMP 0xf42c +- +-#define STB0899_AGCIQIN 0xf42e +-#define STB0899_AGCIQVALUE (0xff << 0) +-#define STB0899_OFFST_AGCIQVALUE 0 +-#define STB0899_WIDTH_AGCIQVALUE 8 +- +-#define STB0899_AGC2I1 0xf436 +-#define STB0899_AGC2I2 0xf437 +- +-#define STB0899_TLIR 0xf438 +-#define STB0899_TLIR_TMG_LOCK_IND (0xff << 0) +-#define STB0899_OFFST_TLIR_TMG_LOCK_IND 0 +-#define STB0899_WIDTH_TLIR_TMG_LOCK_IND 8 +- +-#define STB0899_RTF 0xf439 +-#define STB0899_RTF_TIMING_LOOP_FREQ (0xff << 0) +-#define STB0899_OFFST_RTF_TIMING_LOOP_FREQ 0 +-#define STB0899_WIDTH_RTF_TIMING_LOOP_FREQ 8 +- +-#define STB0899_DSTATUS 0xf43a +-#define STB0899_CARRIER_FOUND (0x01 << 7) +-#define STB0899_OFFST_CARRIER_FOUND 7 +-#define STB0899_WIDTH_CARRIER_FOUND 1 +-#define STB0899_TMG_LOCK (0x01 << 6) +-#define STB0899_OFFST_TMG_LOCK 6 +-#define STB0899_WIDTH_TMG_LOCK 1 +-#define STB0899_DEMOD_LOCK (0x01 << 5) +-#define STB0899_OFFST_DEMOD_LOCK 5 +-#define STB0899_WIDTH_DEMOD_LOCK 1 +-#define STB0899_TMG_AUTO (0x01 << 4) +-#define STB0899_OFFST_TMG_AUTO 4 +-#define STB0899_WIDTH_TMG_AUTO 1 +-#define STB0899_END_MAIN (0x01 << 3) +-#define STB0899_OFFST_END_MAIN 3 +-#define STB0899_WIDTH_END_MAIN 1 +- +-#define STB0899_LDI 0xf43b +-#define STB0899_OFFST_LDI 0 +-#define STB0899_WIDTH_LDI 8 +- +-#define STB0899_CFRM 0xf43e +-#define STB0899_OFFST_CFRM 0 +-#define STB0899_WIDTH_CFRM 8 +- +-#define STB0899_CFRL 0xf43f +-#define STB0899_OFFST_CFRL 0 +-#define STB0899_WIDTH_CFRL 8 +- +-#define STB0899_NIRM 0xf440 +-#define STB0899_OFFST_NIRM 0 +-#define STB0899_WIDTH_NIRM 8 +- +-#define STB0899_NIRL 0xf441 +-#define STB0899_OFFST_NIRL 0 +-#define STB0899_WIDTH_NIRL 8 +- +-#define STB0899_ISYMB 0xf444 +-#define STB0899_QSYMB 0xf445 +- +-#define STB0899_SFRH 0xf446 +-#define STB0899_OFFST_SFRH 0 +-#define STB0899_WIDTH_SFRH 8 +- +-#define STB0899_SFRM 0xf447 +-#define STB0899_OFFST_SFRM 0 +-#define STB0899_WIDTH_SFRM 8 +- +-#define STB0899_SFRL 0xf448 +-#define STB0899_OFFST_SFRL 4 +-#define STB0899_WIDTH_SFRL 4 +- +-#define STB0899_SFRUPH 0xf44c +-#define STB0899_SFRUPM 0xf44d +-#define STB0899_SFRUPL 0xf44e +- +-#define STB0899_EQUAI1 0xf4e0 +-#define STB0899_EQUAQ1 0xf4e1 +-#define STB0899_EQUAI2 0xf4e2 +-#define STB0899_EQUAQ2 0xf4e3 +-#define STB0899_EQUAI3 0xf4e4 +-#define STB0899_EQUAQ3 0xf4e5 +-#define STB0899_EQUAI4 0xf4e6 +-#define STB0899_EQUAQ4 0xf4e7 +-#define STB0899_EQUAI5 0xf4e8 +-#define STB0899_EQUAQ5 0xf4e9 +- +-#define STB0899_DSTATUS2 0xf50c +-#define STB0899_DS2_TMG_AUTOSRCH (0x01 << 7) +-#define STB8999_OFFST_DS2_TMG_AUTOSRCH 7 +-#define STB0899_WIDTH_DS2_TMG_AUTOSRCH 1 +-#define STB0899_DS2_END_MAINLOOP (0x01 << 6) +-#define STB0899_OFFST_DS2_END_MAINLOOP 6 +-#define STB0899_WIDTH_DS2_END_MAINLOOP 1 +-#define STB0899_DS2_CFSYNC (0x01 << 5) +-#define STB0899_OFFST_DS2_CFSYNC 5 +-#define STB0899_WIDTH_DS2_CFSYNC 1 +-#define STB0899_DS2_TMGLOCK (0x01 << 4) +-#define STB0899_OFFST_DS2_TMGLOCK 4 +-#define STB0899_WIDTH_DS2_TMGLOCK 1 +-#define STB0899_DS2_DEMODWAIT (0x01 << 3) +-#define STB0899_OFFST_DS2_DEMODWAIT 3 +-#define STB0899_WIDTH_DS2_DEMODWAIT 1 +-#define STB0899_DS2_FECON (0x01 << 1) +-#define STB0899_OFFST_DS2_FECON 1 +-#define STB0899_WIDTH_DS2_FECON 1 +- +-/* S1 FEC */ +-#define STB0899_VSTATUS 0xf50d +-#define STB0899_VSTATUS_VITERBI_ON (0x01 << 7) +-#define STB0899_OFFST_VSTATUS_VITERBI_ON 7 +-#define STB0899_WIDTH_VSTATUS_VITERBI_ON 1 +-#define STB0899_VSTATUS_END_LOOPVIT (0x01 << 6) +-#define STB0899_OFFST_VSTATUS_END_LOOPVIT 6 +-#define STB0899_WIDTH_VSTATUS_END_LOOPVIT 1 +-#define STB0899_VSTATUS_PRFVIT (0x01 << 4) +-#define STB0899_OFFST_VSTATUS_PRFVIT 4 +-#define STB0899_WIDTH_VSTATUS_PRFVIT 1 +-#define STB0899_VSTATUS_LOCKEDVIT (0x01 << 3) +-#define STB0899_OFFST_VSTATUS_LOCKEDVIT 3 +-#define STB0899_WIDTH_VSTATUS_LOCKEDVIT 1 +- +-#define STB0899_VERROR 0xf50f +- +-#define STB0899_IQSWAP 0xf523 +-#define STB0899_SYM (0x01 << 3) +-#define STB0899_OFFST_SYM 3 +-#define STB0899_WIDTH_SYM 1 +- +-#define STB0899_FECAUTO1 0xf530 +-#define STB0899_DSSSRCH (0x01 << 3) +-#define STB0899_OFFST_DSSSRCH 3 +-#define STB0899_WIDTH_DSSSRCH 1 +-#define STB0899_SYMSRCH (0x01 << 2) +-#define STB0899_OFFST_SYMSRCH 2 +-#define STB0899_WIDTH_SYMSRCH 1 +-#define STB0899_QPSKSRCH (0x01 << 1) +-#define STB0899_OFFST_QPSKSRCH 1 +-#define STB0899_WIDTH_QPSKSRCH 1 +-#define STB0899_BPSKSRCH (0x01 << 0) +-#define STB0899_OFFST_BPSKSRCH 0 +-#define STB0899_WIDTH_BPSKSRCH 1 +- +-#define STB0899_FECM 0xf533 +-#define STB0899_FECM_NOT_DVB (0x01 << 7) +-#define STB0899_OFFST_FECM_NOT_DVB 7 +-#define STB0899_WIDTH_FECM_NOT_DVB 1 +-#define STB0899_FECM_RSVD1 (0x07 << 4) +-#define STB0899_OFFST_FECM_RSVD1 4 +-#define STB0899_WIDTH_FECM_RSVD1 3 +-#define STB0899_FECM_VITERBI_ON (0x01 << 3) +-#define STB0899_OFFST_FECM_VITERBI_ON 3 +-#define STB0899_WIDTH_FECM_VITERBI_ON 1 +-#define STB0899_FECM_RSVD0 (0x01 << 2) +-#define STB0899_OFFST_FECM_RSVD0 2 +-#define STB0899_WIDTH_FECM_RSVD0 1 +-#define STB0899_FECM_SYNCDIS (0x01 << 1) +-#define STB0899_OFFST_FECM_SYNCDIS 1 +-#define STB0899_WIDTH_FECM_SYNCDIS 1 +-#define STB0899_FECM_SYMI (0x01 << 0) +-#define STB0899_OFFST_FECM_SYMI 0 +-#define STB0899_WIDTH_FECM_SYMI 1 +- +-#define STB0899_VTH12 0xf534 +-#define STB0899_VTH23 0xf535 +-#define STB0899_VTH34 0xf536 +-#define STB0899_VTH56 0xf537 +-#define STB0899_VTH67 0xf538 +-#define STB0899_VTH78 0xf539 +- +-#define STB0899_PRVIT 0xf53c +-#define STB0899_PR_7_8 (0x01 << 5) +-#define STB0899_OFFST_PR_7_8 5 +-#define STB0899_WIDTH_PR_7_8 1 +-#define STB0899_PR_6_7 (0x01 << 4) +-#define STB0899_OFFST_PR_6_7 4 +-#define STB0899_WIDTH_PR_6_7 1 +-#define STB0899_PR_5_6 (0x01 << 3) +-#define STB0899_OFFST_PR_5_6 3 +-#define STB0899_WIDTH_PR_5_6 1 +-#define STB0899_PR_3_4 (0x01 << 2) +-#define STB0899_OFFST_PR_3_4 2 +-#define STB0899_WIDTH_PR_3_4 1 +-#define STB0899_PR_2_3 (0x01 << 1) +-#define STB0899_OFFST_PR_2_3 1 +-#define STB0899_WIDTH_PR_2_3 1 +-#define STB0899_PR_1_2 (0x01 << 0) +-#define STB0899_OFFST_PR_1_2 0 +-#define STB0899_WIDTH_PR_1_2 1 +- +-#define STB0899_VITSYNC 0xf53d +-#define STB0899_AM (0x01 << 7) +-#define STB0899_OFFST_AM 7 +-#define STB0899_WIDTH_AM 1 +-#define STB0899_FREEZE (0x01 << 6) +-#define STB0899_OFFST_FREEZE 6 +-#define STB0899_WIDTH_FREEZE 1 +-#define STB0899_SN_65536 (0x03 << 4) +-#define STB0899_OFFST_SN_65536 4 +-#define STB0899_WIDTH_SN_65536 2 +-#define STB0899_SN_16384 (0x01 << 5) +-#define STB0899_OFFST_SN_16384 5 +-#define STB0899_WIDTH_SN_16384 1 +-#define STB0899_SN_4096 (0x01 << 4) +-#define STB0899_OFFST_SN_4096 4 +-#define STB0899_WIDTH_SN_4096 1 +-#define STB0899_SN_1024 (0x00 << 4) +-#define STB0899_OFFST_SN_1024 4 +-#define STB0899_WIDTH_SN_1024 0 +-#define STB0899_TO_128 (0x03 << 2) +-#define STB0899_OFFST_TO_128 2 +-#define STB0899_WIDTH_TO_128 2 +-#define STB0899_TO_64 (0x01 << 3) +-#define STB0899_OFFST_TO_64 3 +-#define STB0899_WIDTH_TO_64 1 +-#define STB0899_TO_32 (0x01 << 2) +-#define STB0899_OFFST_TO_32 2 +-#define STB0899_WIDTH_TO_32 1 +-#define STB0899_TO_16 (0x00 << 2) +-#define STB0899_OFFST_TO_16 2 +-#define STB0899_WIDTH_TO_16 0 +-#define STB0899_HYST_128 (0x03 << 1) +-#define STB0899_OFFST_HYST_128 1 +-#define STB0899_WIDTH_HYST_128 2 +-#define STB0899_HYST_64 (0x01 << 1) +-#define STB0899_OFFST_HYST_64 1 +-#define STB0899_WIDTH_HYST_64 1 +-#define STB0899_HYST_32 (0x01 << 0) +-#define STB0899_OFFST_HYST_32 0 +-#define STB0899_WIDTH_HYST_32 1 +-#define STB0899_HYST_16 (0x00 << 0) +-#define STB0899_OFFST_HYST_16 0 +-#define STB0899_WIDTH_HYST_16 0 +- +-#define STB0899_RSULC 0xf548 +-#define STB0899_ULDIL_ON (0x01 << 7) +-#define STB0899_OFFST_ULDIL_ON 7 +-#define STB0899_WIDTH_ULDIL_ON 1 +-#define STB0899_ULAUTO_ON (0x01 << 6) +-#define STB0899_OFFST_ULAUTO_ON 6 +-#define STB0899_WIDTH_ULAUTO_ON 1 +-#define STB0899_ULRS_ON (0x01 << 5) +-#define STB0899_OFFST_ULRS_ON 5 +-#define STB0899_WIDTH_ULRS_ON 1 +-#define STB0899_ULDESCRAM_ON (0x01 << 4) +-#define STB0899_OFFST_ULDESCRAM_ON 4 +-#define STB0899_WIDTH_ULDESCRAM_ON 1 +-#define STB0899_UL_DISABLE (0x01 << 2) +-#define STB0899_OFFST_UL_DISABLE 2 +-#define STB0899_WIDTH_UL_DISABLE 1 +-#define STB0899_NOFTHRESHOLD (0x01 << 0) +-#define STB0899_OFFST_NOFTHRESHOLD 0 +-#define STB0899_WIDTH_NOFTHRESHOLD 1 +- +-#define STB0899_RSLLC 0xf54a +-#define STB0899_DEMAPVIT 0xf583 +-#define STB0899_DEMAPVIT_RSVD (0x01 << 7) +-#define STB0899_OFFST_DEMAPVIT_RSVD 7 +-#define STB0899_WIDTH_DEMAPVIT_RSVD 1 +-#define STB0899_DEMAPVIT_KDIVIDER (0x7f << 0) +-#define STB0899_OFFST_DEMAPVIT_KDIVIDER 0 +-#define STB0899_WIDTH_DEMAPVIT_KDIVIDER 7 +- +-#define STB0899_PLPARM 0xf58c +-#define STB0899_VITMAPPING (0x07 << 5) +-#define STB0899_OFFST_VITMAPPING 5 +-#define STB0899_WIDTH_VITMAPPING 3 +-#define STB0899_VITMAPPING_BPSK (0x01 << 5) +-#define STB0899_OFFST_VITMAPPING_BPSK 5 +-#define STB0899_WIDTH_VITMAPPING_BPSK 1 +-#define STB0899_VITMAPPING_QPSK (0x00 << 5) +-#define STB0899_OFFST_VITMAPPING_QPSK 5 +-#define STB0899_WIDTH_VITMAPPING_QPSK 0 +-#define STB0899_VITCURPUN (0x1f << 0) +-#define STB0899_OFFST_VITCURPUN 0 +-#define STB0899_WIDTH_VITCURPUN 5 +-#define STB0899_VITCURPUN_1_2 (0x0d << 0) +-#define STB0899_VITCURPUN_2_3 (0x12 << 0) +-#define STB0899_VITCURPUN_3_4 (0x15 << 0) +-#define STB0899_VITCURPUN_5_6 (0x18 << 0) +-#define STB0899_VITCURPUN_6_7 (0x19 << 0) +-#define STB0899_VITCURPUN_7_8 (0x1a << 0) +- +-/* S2 DEMOD */ +-#define STB0899_OFF0_DMD_STATUS 0xf300 +-#define STB0899_BASE_DMD_STATUS 0x00000000 +-#define STB0899_IF_AGC_LOCK (0x01 << 8) +-#define STB0899_OFFST_IF_AGC_LOCK 0 +-#define STB0899_WIDTH_IF_AGC_LOCK 1 +- +-#define STB0899_OFF0_CRL_FREQ 0xf304 +-#define STB0899_BASE_CRL_FREQ 0x00000000 +-#define STB0899_CARR_FREQ (0x3fffffff << 0) +-#define STB0899_OFFST_CARR_FREQ 0 +-#define STB0899_WIDTH_CARR_FREQ 30 +- +-#define STB0899_OFF0_BTR_FREQ 0xf308 +-#define STB0899_BASE_BTR_FREQ 0x00000000 +-#define STB0899_BTR_FREQ (0xfffffff << 0) +-#define STB0899_OFFST_BTR_FREQ 0 +-#define STB0899_WIDTH_BTR_FREQ 28 +- +-#define STB0899_OFF0_IF_AGC_GAIN 0xf30c +-#define STB0899_BASE_IF_AGC_GAIN 0x00000000 +-#define STB0899_IF_AGC_GAIN (0x3fff < 0) +-#define STB0899_OFFST_IF_AGC_GAIN 0 +-#define STB0899_WIDTH_IF_AGC_GAIN 14 +- +-#define STB0899_OFF0_BB_AGC_GAIN 0xf310 +-#define STB0899_BASE_BB_AGC_GAIN 0x00000000 +-#define STB0899_BB_AGC_GAIN (0x3fff < 0) +-#define STB0899_OFFST_BB_AGC_GAIN 0 +-#define STB0899_WIDTH_BB_AGC_GAIN 14 +- +-#define STB0899_OFF0_DC_OFFSET 0xf314 +-#define STB0899_BASE_DC_OFFSET 0x00000000 +-#define STB0899_I (0xff < 8) +-#define STB0899_OFFST_I 8 +-#define STB0899_WIDTH_I 8 +-#define STB0899_Q (0xff < 0) +-#define STB0899_OFFST_Q 8 +-#define STB0899_WIDTH_Q 8 +- +-#define STB0899_OFF0_DMD_CNTRL 0xf31c +-#define STB0899_BASE_DMD_CNTRL 0x00000000 +-#define STB0899_ADC0_PINS1IN (0x01 << 6) +-#define STB0899_OFFST_ADC0_PINS1IN 6 +-#define STB0899_WIDTH_ADC0_PINS1IN 1 +-#define STB0899_IN2COMP1_OFFBIN0 (0x01 << 3) +-#define STB0899_OFFST_IN2COMP1_OFFBIN0 3 +-#define STB0899_WIDTH_IN2COMP1_OFFBIN0 1 +-#define STB0899_DC_COMP (0x01 << 2) +-#define STB0899_OFFST_DC_COMP 2 +-#define STB0899_WIDTH_DC_COMP 1 +-#define STB0899_MODMODE (0x03 << 0) +-#define STB0899_OFFST_MODMODE 0 +-#define STB0899_WIDTH_MODMODE 2 +- +-#define STB0899_OFF0_IF_AGC_CNTRL 0xf320 +-#define STB0899_BASE_IF_AGC_CNTRL 0x00000000 +-#define STB0899_IF_GAIN_INIT (0x3fff << 13) +-#define STB0899_OFFST_IF_GAIN_INIT 13 +-#define STB0899_WIDTH_IF_GAIN_INIT 14 +-#define STB0899_IF_GAIN_SENSE (0x01 << 12) +-#define STB0899_OFFST_IF_GAIN_SENSE 12 +-#define STB0899_WIDTH_IF_GAIN_SENSE 1 +-#define STB0899_IF_LOOP_GAIN (0x0f << 8) +-#define STB0899_OFFST_IF_LOOP_GAIN 8 +-#define STB0899_WIDTH_IF_LOOP_GAIN 4 +-#define STB0899_IF_LD_GAIN_INIT (0x01 << 7) +-#define STB0899_OFFST_IF_LD_GAIN_INIT 7 +-#define STB0899_WIDTH_IF_LD_GAIN_INIT 1 +-#define STB0899_IF_AGC_REF (0x7f << 0) +-#define STB0899_OFFST_IF_AGC_REF 0 +-#define STB0899_WIDTH_IF_AGC_REF 7 +- +-#define STB0899_OFF0_BB_AGC_CNTRL 0xf324 +-#define STB0899_BASE_BB_AGC_CNTRL 0x00000000 +-#define STB0899_BB_GAIN_INIT (0x3fff << 12) +-#define STB0899_OFFST_BB_GAIN_INIT 12 +-#define STB0899_WIDTH_BB_GAIN_INIT 14 +-#define STB0899_BB_LOOP_GAIN (0x0f << 8) +-#define STB0899_OFFST_BB_LOOP_GAIN 8 +-#define STB0899_WIDTH_BB_LOOP_GAIN 4 +-#define STB0899_BB_LD_GAIN_INIT (0x01 << 7) +-#define STB0899_OFFST_BB_LD_GAIN_INIT 7 +-#define STB0899_WIDTH_BB_LD_GAIN_INIT 1 +-#define STB0899_BB_AGC_REF (0x7f << 0) +-#define STB0899_OFFST_BB_AGC_REF 0 +-#define STB0899_WIDTH_BB_AGC_REF 7 +- +-#define STB0899_OFF0_CRL_CNTRL 0xf328 +-#define STB0899_BASE_CRL_CNTRL 0x00000000 +-#define STB0899_CRL_LOCK_CLEAR (0x01 << 5) +-#define STB0899_OFFST_CRL_LOCK_CLEAR 5 +-#define STB0899_WIDTH_CRL_LOCK_CLEAR 1 +-#define STB0899_CRL_SWPR_CLEAR (0x01 << 4) +-#define STB0899_OFFST_CRL_SWPR_CLEAR 4 +-#define STB0899_WIDTH_CRL_SWPR_CLEAR 1 +-#define STB0899_CRL_SWP_ENA (0x01 << 3) +-#define STB0899_OFFST_CRL_SWP_ENA 3 +-#define STB0899_WIDTH_CRL_SWP_ENA 1 +-#define STB0899_CRL_DET_SEL (0x01 << 2) +-#define STB0899_OFFST_CRL_DET_SEL 2 +-#define STB0899_WIDTH_CRL_DET_SEL 1 +-#define STB0899_CRL_SENSE (0x01 << 1) +-#define STB0899_OFFST_CRL_SENSE 1 +-#define STB0899_WIDTH_CRL_SENSE 1 +-#define STB0899_CRL_PHSERR_CLEAR (0x01 << 0) +-#define STB0899_OFFST_CRL_PHSERR_CLEAR 0 +-#define STB0899_WIDTH_CRL_PHSERR_CLEAR 1 +- +-#define STB0899_OFF0_CRL_PHS_INIT 0xf32c +-#define STB0899_BASE_CRL_PHS_INIT 0x00000000 +-#define STB0899_CRL_PHS_INIT_31 (0x1 << 30) +-#define STB0899_OFFST_CRL_PHS_INIT_31 30 +-#define STB0899_WIDTH_CRL_PHS_INIT_31 1 +-#define STB0899_CRL_LD_INIT_PHASE (0x1 << 24) +-#define STB0899_OFFST_CRL_LD_INIT_PHASE 24 +-#define STB0899_WIDTH_CRL_LD_INIT_PHASE 1 +-#define STB0899_CRL_INIT_PHASE (0xffffff << 0) +-#define STB0899_OFFST_CRL_INIT_PHASE 0 +-#define STB0899_WIDTH_CRL_INIT_PHASE 24 +- +-#define STB0899_OFF0_CRL_FREQ_INIT 0xf330 +-#define STB0899_BASE_CRL_FREQ_INIT 0x00000000 +-#define STB0899_CRL_FREQ_INIT_31 (0x1 << 30) +-#define STB0899_OFFST_CRL_FREQ_INIT_31 30 +-#define STB0899_WIDTH_CRL_FREQ_INIT_31 1 +-#define STB0899_CRL_LD_FREQ_INIT (0x1 << 24) +-#define STB0899_OFFST_CRL_LD_FREQ_INIT 24 +-#define STB0899_WIDTH_CRL_LD_FREQ_INIT 1 +-#define STB0899_CRL_FREQ_INIT (0xffffff << 0) +-#define STB0899_OFFST_CRL_FREQ_INIT 0 +-#define STB0899_WIDTH_CRL_FREQ_INIT 24 +- +-#define STB0899_OFF0_CRL_LOOP_GAIN 0xf334 +-#define STB0899_BASE_CRL_LOOP_GAIN 0x00000000 +-#define STB0899_KCRL2_RSHFT (0xf << 16) +-#define STB0899_OFFST_KCRL2_RSHFT 16 +-#define STB0899_WIDTH_KCRL2_RSHFT 4 +-#define STB0899_KCRL1 (0xf << 12) +-#define STB0899_OFFST_KCRL1 12 +-#define STB0899_WIDTH_KCRL1 4 +-#define STB0899_KCRL1_RSHFT (0xf << 8) +-#define STB0899_OFFST_KCRL1_RSHFT 8 +-#define STB0899_WIDTH_KCRL1_RSHFT 4 +-#define STB0899_KCRL0 (0xf << 4) +-#define STB0899_OFFST_KCRL0 4 +-#define STB0899_WIDTH_KCRL0 4 +-#define STB0899_KCRL0_RSHFT (0xf << 0) +-#define STB0899_OFFST_KCRL0_RSHFT 0 +-#define STB0899_WIDTH_KCRL0_RSHFT 4 +- +-#define STB0899_OFF0_CRL_NOM_FREQ 0xf338 +-#define STB0899_BASE_CRL_NOM_FREQ 0x00000000 +-#define STB0899_CRL_NOM_FREQ (0x3fffffff << 0) +-#define STB0899_OFFST_CRL_NOM_FREQ 0 +-#define STB0899_WIDTH_CRL_NOM_FREQ 30 +- +-#define STB0899_OFF0_CRL_SWP_RATE 0xf33c +-#define STB0899_BASE_CRL_SWP_RATE 0x00000000 +-#define STB0899_CRL_SWP_RATE (0x3fffffff << 0) +-#define STB0899_OFFST_CRL_SWP_RATE 0 +-#define STB0899_WIDTH_CRL_SWP_RATE 30 +- +-#define STB0899_OFF0_CRL_MAX_SWP 0xf340 +-#define STB0899_BASE_CRL_MAX_SWP 0x00000000 +-#define STB0899_CRL_MAX_SWP (0x3fffffff << 0) +-#define STB0899_OFFST_CRL_MAX_SWP 0 +-#define STB0899_WIDTH_CRL_MAX_SWP 30 +- +-#define STB0899_OFF0_CRL_LK_CNTRL 0xf344 +-#define STB0899_BASE_CRL_LK_CNTRL 0x00000000 +- +-#define STB0899_OFF0_DECIM_CNTRL 0xf348 +-#define STB0899_BASE_DECIM_CNTRL 0x00000000 +-#define STB0899_BAND_LIMIT_B (0x01 << 5) +-#define STB0899_OFFST_BAND_LIMIT_B 5 +-#define STB0899_WIDTH_BAND_LIMIT_B 1 +-#define STB0899_WIN_SEL (0x03 << 3) +-#define STB0899_OFFST_WIN_SEL 3 +-#define STB0899_WIDTH_WIN_SEL 2 +-#define STB0899_DECIM_RATE (0x07 << 0) +-#define STB0899_OFFST_DECIM_RATE 0 +-#define STB0899_WIDTH_DECIM_RATE 3 +- +-#define STB0899_OFF0_BTR_CNTRL 0xf34c +-#define STB0899_BASE_BTR_CNTRL 0x00000000 +-#define STB0899_BTR_FREQ_CORR (0x7ff << 4) +-#define STB0899_OFFST_BTR_FREQ_CORR 4 +-#define STB0899_WIDTH_BTR_FREQ_CORR 11 +-#define STB0899_BTR_CLR_LOCK (0x01 << 3) +-#define STB0899_OFFST_BTR_CLR_LOCK 3 +-#define STB0899_WIDTH_BTR_CLR_LOCK 1 +-#define STB0899_BTR_SENSE (0x01 << 2) +-#define STB0899_OFFST_BTR_SENSE 2 +-#define STB0899_WIDTH_BTR_SENSE 1 +-#define STB0899_BTR_ERR_ENA (0x01 << 1) +-#define STB0899_OFFST_BTR_ERR_ENA 1 +-#define STB0899_WIDTH_BTR_ERR_ENA 1 +-#define STB0899_INTRP_PHS_SENSE (0x01 << 0) +-#define STB0899_OFFST_INTRP_PHS_SENSE 0 +-#define STB0899_WIDTH_INTRP_PHS_SENSE 1 +- +-#define STB0899_OFF0_BTR_LOOP_GAIN 0xf350 +-#define STB0899_BASE_BTR_LOOP_GAIN 0x00000000 +-#define STB0899_KBTR2_RSHFT (0x0f << 16) +-#define STB0899_OFFST_KBTR2_RSHFT 16 +-#define STB0899_WIDTH_KBTR2_RSHFT 4 +-#define STB0899_KBTR1 (0x0f << 12) +-#define STB0899_OFFST_KBTR1 12 +-#define STB0899_WIDTH_KBTR1 4 +-#define STB0899_KBTR1_RSHFT (0x0f << 8) +-#define STB0899_OFFST_KBTR1_RSHFT 8 +-#define STB0899_WIDTH_KBTR1_RSHFT 4 +-#define STB0899_KBTR0 (0x0f << 4) +-#define STB0899_OFFST_KBTR0 4 +-#define STB0899_WIDTH_KBTR0 4 +-#define STB0899_KBTR0_RSHFT (0x0f << 0) +-#define STB0899_OFFST_KBTR0_RSHFT 0 +-#define STB0899_WIDTH_KBTR0_RSHFT 4 +- +-#define STB0899_OFF0_BTR_PHS_INIT 0xf354 +-#define STB0899_BASE_BTR_PHS_INIT 0x00000000 +-#define STB0899_BTR_LD_PHASE_INIT (0x01 << 28) +-#define STB0899_OFFST_BTR_LD_PHASE_INIT 28 +-#define STB0899_WIDTH_BTR_LD_PHASE_INIT 1 +-#define STB0899_BTR_INIT_PHASE (0xfffffff << 0) +-#define STB0899_OFFST_BTR_INIT_PHASE 0 +-#define STB0899_WIDTH_BTR_INIT_PHASE 28 +- +-#define STB0899_OFF0_BTR_FREQ_INIT 0xf358 +-#define STB0899_BASE_BTR_FREQ_INIT 0x00000000 +-#define STB0899_BTR_LD_FREQ_INIT (1 << 28) +-#define STB0899_OFFST_BTR_LD_FREQ_INIT 28 +-#define STB0899_WIDTH_BTR_LD_FREQ_INIT 1 +-#define STB0899_BTR_FREQ_INIT (0xfffffff << 0) +-#define STB0899_OFFST_BTR_FREQ_INIT 0 +-#define STB0899_WIDTH_BTR_FREQ_INIT 28 +- +-#define STB0899_OFF0_BTR_NOM_FREQ 0xf35c +-#define STB0899_BASE_BTR_NOM_FREQ 0x00000000 +-#define STB0899_BTR_NOM_FREQ (0xfffffff << 0) +-#define STB0899_OFFST_BTR_NOM_FREQ 0 +-#define STB0899_WIDTH_BTR_NOM_FREQ 28 +- +-#define STB0899_OFF0_BTR_LK_CNTRL 0xf360 +-#define STB0899_BASE_BTR_LK_CNTRL 0x00000000 +-#define STB0899_BTR_MIN_ENERGY (0x0f << 24) +-#define STB0899_OFFST_BTR_MIN_ENERGY 24 +-#define STB0899_WIDTH_BTR_MIN_ENERGY 4 +-#define STB0899_BTR_LOCK_TH_LO (0xff << 16) +-#define STB0899_OFFST_BTR_LOCK_TH_LO 16 +-#define STB0899_WIDTH_BTR_LOCK_TH_LO 8 +-#define STB0899_BTR_LOCK_TH_HI (0xff << 8) +-#define STB0899_OFFST_BTR_LOCK_TH_HI 8 +-#define STB0899_WIDTH_BTR_LOCK_TH_HI 8 +-#define STB0899_BTR_LOCK_GAIN (0x03 << 6) +-#define STB0899_OFFST_BTR_LOCK_GAIN 6 +-#define STB0899_WIDTH_BTR_LOCK_GAIN 2 +-#define STB0899_BTR_LOCK_LEAK (0x3f << 0) +-#define STB0899_OFFST_BTR_LOCK_LEAK 0 +-#define STB0899_WIDTH_BTR_LOCK_LEAK 6 +- +-#define STB0899_OFF0_DECN_CNTRL 0xf364 +-#define STB0899_BASE_DECN_CNTRL 0x00000000 +- +-#define STB0899_OFF0_TP_CNTRL 0xf368 +-#define STB0899_BASE_TP_CNTRL 0x00000000 +- +-#define STB0899_OFF0_TP_BUF_STATUS 0xf36c +-#define STB0899_BASE_TP_BUF_STATUS 0x00000000 +-#define STB0899_TP_BUFFER_FULL (1 << 0) +- +-#define STB0899_OFF0_DC_ESTIM 0xf37c +-#define STB0899_BASE_DC_ESTIM 0x0000 +-#define STB0899_I_DC_ESTIMATE (0xff << 8) +-#define STB0899_OFFST_I_DC_ESTIMATE 8 +-#define STB0899_WIDTH_I_DC_ESTIMATE 8 +-#define STB0899_Q_DC_ESTIMATE (0xff << 0) +-#define STB0899_OFFST_Q_DC_ESTIMATE 0 +-#define STB0899_WIDTH_Q_DC_ESTIMATE 8 +- +-#define STB0899_OFF0_FLL_CNTRL 0xf310 +-#define STB0899_BASE_FLL_CNTRL 0x00000020 +-#define STB0899_CRL_FLL_ACC (0x01 << 4) +-#define STB0899_OFFST_CRL_FLL_ACC 4 +-#define STB0899_WIDTH_CRL_FLL_ACC 1 +-#define STB0899_FLL_AVG_PERIOD (0x0f << 0) +-#define STB0899_OFFST_FLL_AVG_PERIOD 0 +-#define STB0899_WIDTH_FLL_AVG_PERIOD 4 +- +-#define STB0899_OFF0_FLL_FREQ_WD 0xf314 +-#define STB0899_BASE_FLL_FREQ_WD 0x00000020 +-#define STB0899_FLL_FREQ_WD (0xffffffff << 0) +-#define STB0899_OFFST_FLL_FREQ_WD 0 +-#define STB0899_WIDTH_FLL_FREQ_WD 32 +- +-#define STB0899_OFF0_ANTI_ALIAS_SEL 0xf358 +-#define STB0899_BASE_ANTI_ALIAS_SEL 0x00000020 +-#define STB0899_ANTI_ALIAS_SELB (0x03 << 0) +-#define STB0899_OFFST_ANTI_ALIAS_SELB 0 +-#define STB0899_WIDTH_ANTI_ALIAS_SELB 2 +- +-#define STB0899_OFF0_RRC_ALPHA 0xf35c +-#define STB0899_BASE_RRC_ALPHA 0x00000020 +-#define STB0899_RRC_ALPHA (0x03 << 0) +-#define STB0899_OFFST_RRC_ALPHA 0 +-#define STB0899_WIDTH_RRC_ALPHA 2 +- +-#define STB0899_OFF0_DC_ADAPT_LSHFT 0xf360 +-#define STB0899_BASE_DC_ADAPT_LSHFT 0x00000020 +-#define STB0899_DC_ADAPT_LSHFT (0x077 << 0) +-#define STB0899_OFFST_DC_ADAPT_LSHFT 0 +-#define STB0899_WIDTH_DC_ADAPT_LSHFT 3 +- +-#define STB0899_OFF0_IMB_OFFSET 0xf364 +-#define STB0899_BASE_IMB_OFFSET 0x00000020 +-#define STB0899_PHS_IMB_COMP (0xff << 8) +-#define STB0899_OFFST_PHS_IMB_COMP 8 +-#define STB0899_WIDTH_PHS_IMB_COMP 8 +-#define STB0899_AMPL_IMB_COMP (0xff << 0) +-#define STB0899_OFFST_AMPL_IMB_COMP 0 +-#define STB0899_WIDTH_AMPL_IMB_COMP 8 +- +-#define STB0899_OFF0_IMB_ESTIMATE 0xf368 +-#define STB0899_BASE_IMB_ESTIMATE 0x00000020 +-#define STB0899_PHS_IMB_ESTIMATE (0xff << 8) +-#define STB0899_OFFST_PHS_IMB_ESTIMATE 8 +-#define STB0899_WIDTH_PHS_IMB_ESTIMATE 8 +-#define STB0899_AMPL_IMB_ESTIMATE (0xff << 0) +-#define STB0899_OFFST_AMPL_IMB_ESTIMATE 0 +-#define STB0899_WIDTH_AMPL_IMB_ESTIMATE 8 +- +-#define STB0899_OFF0_IMB_CNTRL 0xf36c +-#define STB0899_BASE_IMB_CNTRL 0x00000020 +-#define STB0899_PHS_ADAPT_LSHFT (0x07 << 4) +-#define STB0899_OFFST_PHS_ADAPT_LSHFT 4 +-#define STB0899_WIDTH_PHS_ADAPT_LSHFT 3 +-#define STB0899_AMPL_ADAPT_LSHFT (0x07 << 1) +-#define STB0899_OFFST_AMPL_ADAPT_LSHFT 1 +-#define STB0899_WIDTH_AMPL_ADAPT_LSHFT 3 +-#define STB0899_IMB_COMP (0x01 << 0) +-#define STB0899_OFFST_IMB_COMP 0 +-#define STB0899_WIDTH_IMB_COMP 1 +- +-#define STB0899_OFF0_IF_AGC_CNTRL2 0xf374 +-#define STB0899_BASE_IF_AGC_CNTRL2 0x00000020 +-#define STB0899_IF_AGC_LOCK_TH (0xff << 11) +-#define STB0899_OFFST_IF_AGC_LOCK_TH 11 +-#define STB0899_WIDTH_IF_AGC_LOCK_TH 8 +-#define STB0899_IF_AGC_SD_DIV (0xff << 3) +-#define STB0899_OFFST_IF_AGC_SD_DIV 3 +-#define STB0899_WIDTH_IF_AGC_SD_DIV 8 +-#define STB0899_IF_AGC_DUMP_PER (0x07 << 0) +-#define STB0899_OFFST_IF_AGC_DUMP_PER 0 +-#define STB0899_WIDTH_IF_AGC_DUMP_PER 3 +- +-#define STB0899_OFF0_DMD_CNTRL2 0xf378 +-#define STB0899_BASE_DMD_CNTRL2 0x00000020 +-#define STB0899_SPECTRUM_INVERT (0x01 << 2) +-#define STB0899_OFFST_SPECTRUM_INVERT 2 +-#define STB0899_WIDTH_SPECTRUM_INVERT 1 +-#define STB0899_AGC_MODE (0x01 << 1) +-#define STB0899_OFFST_AGC_MODE 1 +-#define STB0899_WIDTH_AGC_MODE 1 +-#define STB0899_CRL_FREQ_ADJ (0x01 << 0) +-#define STB0899_OFFST_CRL_FREQ_ADJ 0 +-#define STB0899_WIDTH_CRL_FREQ_ADJ 1 +- +-#define STB0899_OFF0_TP_BUFFER 0xf300 +-#define STB0899_BASE_TP_BUFFER 0x00000040 +-#define STB0899_TP_BUFFER_IN (0xffff << 0) +-#define STB0899_OFFST_TP_BUFFER_IN 0 +-#define STB0899_WIDTH_TP_BUFFER_IN 16 +- +-#define STB0899_OFF0_TP_BUFFER1 0xf304 +-#define STB0899_BASE_TP_BUFFER1 0x00000040 +-#define STB0899_OFF0_TP_BUFFER2 0xf308 +-#define STB0899_BASE_TP_BUFFER2 0x00000040 +-#define STB0899_OFF0_TP_BUFFER3 0xf30c +-#define STB0899_BASE_TP_BUFFER3 0x00000040 +-#define STB0899_OFF0_TP_BUFFER4 0xf310 +-#define STB0899_BASE_TP_BUFFER4 0x00000040 +-#define STB0899_OFF0_TP_BUFFER5 0xf314 +-#define STB0899_BASE_TP_BUFFER5 0x00000040 +-#define STB0899_OFF0_TP_BUFFER6 0xf318 +-#define STB0899_BASE_TP_BUFFER6 0x00000040 +-#define STB0899_OFF0_TP_BUFFER7 0xf31c +-#define STB0899_BASE_TP_BUFFER7 0x00000040 +-#define STB0899_OFF0_TP_BUFFER8 0xf320 +-#define STB0899_BASE_TP_BUFFER8 0x00000040 +-#define STB0899_OFF0_TP_BUFFER9 0xf324 +-#define STB0899_BASE_TP_BUFFER9 0x00000040 +-#define STB0899_OFF0_TP_BUFFER10 0xf328 +-#define STB0899_BASE_TP_BUFFER10 0x00000040 +-#define STB0899_OFF0_TP_BUFFER11 0xf32c +-#define STB0899_BASE_TP_BUFFER11 0x00000040 +-#define STB0899_OFF0_TP_BUFFER12 0xf330 +-#define STB0899_BASE_TP_BUFFER12 0x00000040 +-#define STB0899_OFF0_TP_BUFFER13 0xf334 +-#define STB0899_BASE_TP_BUFFER13 0x00000040 +-#define STB0899_OFF0_TP_BUFFER14 0xf338 +-#define STB0899_BASE_TP_BUFFER14 0x00000040 +-#define STB0899_OFF0_TP_BUFFER15 0xf33c +-#define STB0899_BASE_TP_BUFFER15 0x00000040 +-#define STB0899_OFF0_TP_BUFFER16 0xf340 +-#define STB0899_BASE_TP_BUFFER16 0x00000040 +-#define STB0899_OFF0_TP_BUFFER17 0xf344 +-#define STB0899_BASE_TP_BUFFER17 0x00000040 +-#define STB0899_OFF0_TP_BUFFER18 0xf348 +-#define STB0899_BASE_TP_BUFFER18 0x00000040 +-#define STB0899_OFF0_TP_BUFFER19 0xf34c +-#define STB0899_BASE_TP_BUFFER19 0x00000040 +-#define STB0899_OFF0_TP_BUFFER20 0xf350 +-#define STB0899_BASE_TP_BUFFER20 0x00000040 +-#define STB0899_OFF0_TP_BUFFER21 0xf354 +-#define STB0899_BASE_TP_BUFFER21 0x00000040 +-#define STB0899_OFF0_TP_BUFFER22 0xf358 +-#define STB0899_BASE_TP_BUFFER22 0x00000040 +-#define STB0899_OFF0_TP_BUFFER23 0xf35c +-#define STB0899_BASE_TP_BUFFER23 0x00000040 +-#define STB0899_OFF0_TP_BUFFER24 0xf360 +-#define STB0899_BASE_TP_BUFFER24 0x00000040 +-#define STB0899_OFF0_TP_BUFFER25 0xf364 +-#define STB0899_BASE_TP_BUFFER25 0x00000040 +-#define STB0899_OFF0_TP_BUFFER26 0xf368 +-#define STB0899_BASE_TP_BUFFER26 0x00000040 +-#define STB0899_OFF0_TP_BUFFER27 0xf36c +-#define STB0899_BASE_TP_BUFFER27 0x00000040 +-#define STB0899_OFF0_TP_BUFFER28 0xf370 +-#define STB0899_BASE_TP_BUFFER28 0x00000040 +-#define STB0899_OFF0_TP_BUFFER29 0xf374 +-#define STB0899_BASE_TP_BUFFER29 0x00000040 +-#define STB0899_OFF0_TP_BUFFER30 0xf378 +-#define STB0899_BASE_TP_BUFFER30 0x00000040 +-#define STB0899_OFF0_TP_BUFFER31 0xf37c +-#define STB0899_BASE_TP_BUFFER31 0x00000040 +-#define STB0899_OFF0_TP_BUFFER32 0xf300 +-#define STB0899_BASE_TP_BUFFER32 0x00000060 +-#define STB0899_OFF0_TP_BUFFER33 0xf304 +-#define STB0899_BASE_TP_BUFFER33 0x00000060 +-#define STB0899_OFF0_TP_BUFFER34 0xf308 +-#define STB0899_BASE_TP_BUFFER34 0x00000060 +-#define STB0899_OFF0_TP_BUFFER35 0xf30c +-#define STB0899_BASE_TP_BUFFER35 0x00000060 +-#define STB0899_OFF0_TP_BUFFER36 0xf310 +-#define STB0899_BASE_TP_BUFFER36 0x00000060 +-#define STB0899_OFF0_TP_BUFFER37 0xf314 +-#define STB0899_BASE_TP_BUFFER37 0x00000060 +-#define STB0899_OFF0_TP_BUFFER38 0xf318 +-#define STB0899_BASE_TP_BUFFER38 0x00000060 +-#define STB0899_OFF0_TP_BUFFER39 0xf31c +-#define STB0899_BASE_TP_BUFFER39 0x00000060 +-#define STB0899_OFF0_TP_BUFFER40 0xf320 +-#define STB0899_BASE_TP_BUFFER40 0x00000060 +-#define STB0899_OFF0_TP_BUFFER41 0xf324 +-#define STB0899_BASE_TP_BUFFER41 0x00000060 +-#define STB0899_OFF0_TP_BUFFER42 0xf328 +-#define STB0899_BASE_TP_BUFFER42 0x00000060 +-#define STB0899_OFF0_TP_BUFFER43 0xf32c +-#define STB0899_BASE_TP_BUFFER43 0x00000060 +-#define STB0899_OFF0_TP_BUFFER44 0xf330 +-#define STB0899_BASE_TP_BUFFER44 0x00000060 +-#define STB0899_OFF0_TP_BUFFER45 0xf334 +-#define STB0899_BASE_TP_BUFFER45 0x00000060 +-#define STB0899_OFF0_TP_BUFFER46 0xf338 +-#define STB0899_BASE_TP_BUFFER46 0x00000060 +-#define STB0899_OFF0_TP_BUFFER47 0xf33c +-#define STB0899_BASE_TP_BUFFER47 0x00000060 +-#define STB0899_OFF0_TP_BUFFER48 0xf340 +-#define STB0899_BASE_TP_BUFFER48 0x00000060 +-#define STB0899_OFF0_TP_BUFFER49 0xf344 +-#define STB0899_BASE_TP_BUFFER49 0x00000060 +-#define STB0899_OFF0_TP_BUFFER50 0xf348 +-#define STB0899_BASE_TP_BUFFER50 0x00000060 +-#define STB0899_OFF0_TP_BUFFER51 0xf34c +-#define STB0899_BASE_TP_BUFFER51 0x00000060 +-#define STB0899_OFF0_TP_BUFFER52 0xf350 +-#define STB0899_BASE_TP_BUFFER52 0x00000060 +-#define STB0899_OFF0_TP_BUFFER53 0xf354 +-#define STB0899_BASE_TP_BUFFER53 0x00000060 +-#define STB0899_OFF0_TP_BUFFER54 0xf358 +-#define STB0899_BASE_TP_BUFFER54 0x00000060 +-#define STB0899_OFF0_TP_BUFFER55 0xf35c +-#define STB0899_BASE_TP_BUFFER55 0x00000060 +-#define STB0899_OFF0_TP_BUFFER56 0xf360 +-#define STB0899_BASE_TP_BUFFER56 0x00000060 +-#define STB0899_OFF0_TP_BUFFER57 0xf364 +-#define STB0899_BASE_TP_BUFFER57 0x00000060 +-#define STB0899_OFF0_TP_BUFFER58 0xf368 +-#define STB0899_BASE_TP_BUFFER58 0x00000060 +-#define STB0899_OFF0_TP_BUFFER59 0xf36c +-#define STB0899_BASE_TP_BUFFER59 0x00000060 +-#define STB0899_OFF0_TP_BUFFER60 0xf370 +-#define STB0899_BASE_TP_BUFFER60 0x00000060 +-#define STB0899_OFF0_TP_BUFFER61 0xf374 +-#define STB0899_BASE_TP_BUFFER61 0x00000060 +-#define STB0899_OFF0_TP_BUFFER62 0xf378 +-#define STB0899_BASE_TP_BUFFER62 0x00000060 +-#define STB0899_OFF0_TP_BUFFER63 0xf37c +-#define STB0899_BASE_TP_BUFFER63 0x00000060 +- +-#define STB0899_OFF0_RESET_CNTRL 0xf300 +-#define STB0899_BASE_RESET_CNTRL 0x00000400 +-#define STB0899_DVBS2_RESET (0x01 << 0) +-#define STB0899_OFFST_DVBS2_RESET 0 +-#define STB0899_WIDTH_DVBS2_RESET 1 +- +-#define STB0899_OFF0_ACM_ENABLE 0xf304 +-#define STB0899_BASE_ACM_ENABLE 0x00000400 +-#define STB0899_ACM_ENABLE 1 +- +-#define STB0899_OFF0_DESCR_CNTRL 0xf30c +-#define STB0899_BASE_DESCR_CNTRL 0x00000400 +-#define STB0899_OFFST_DESCR_CNTRL 0 +-#define STB0899_WIDTH_DESCR_CNTRL 16 +- +-#define STB0899_OFF0_UWP_CNTRL1 0xf320 +-#define STB0899_BASE_UWP_CNTRL1 0x00000400 +-#define STB0899_UWP_TH_SOF (0x7fff << 11) +-#define STB0899_OFFST_UWP_TH_SOF 11 +-#define STB0899_WIDTH_UWP_TH_SOF 15 +-#define STB0899_UWP_ESN0_QUANT (0xff << 3) +-#define STB0899_OFFST_UWP_ESN0_QUANT 3 +-#define STB0899_WIDTH_UWP_ESN0_QUANT 8 +-#define STB0899_UWP_ESN0_AVE (0x03 << 1) +-#define STB0899_OFFST_UWP_ESN0_AVE 1 +-#define STB0899_WIDTH_UWP_ESN0_AVE 2 +-#define STB0899_UWP_START (0x01 << 0) +-#define STB0899_OFFST_UWP_START 0 +-#define STB0899_WIDTH_UWP_START 1 +- +-#define STB0899_OFF0_UWP_CNTRL2 0xf324 +-#define STB0899_BASE_UWP_CNTRL2 0x00000400 +-#define STB0899_UWP_MISS_TH (0xff << 16) +-#define STB0899_OFFST_UWP_MISS_TH 16 +-#define STB0899_WIDTH_UWP_MISS_TH 8 +-#define STB0899_FE_FINE_TRK (0xff << 8) +-#define STB0899_OFFST_FE_FINE_TRK 8 +-#define STB0899_WIDTH_FE_FINE_TRK 8 +-#define STB0899_FE_COARSE_TRK (0xff << 0) +-#define STB0899_OFFST_FE_COARSE_TRK 0 +-#define STB0899_WIDTH_FE_COARSE_TRK 8 +- +-#define STB0899_OFF0_UWP_STAT1 0xf328 +-#define STB0899_BASE_UWP_STAT1 0x00000400 +-#define STB0899_UWP_STATE (0x03ff << 15) +-#define STB0899_OFFST_UWP_STATE 15 +-#define STB0899_WIDTH_UWP_STATE 10 +-#define STB0899_UW_MAX_PEAK (0x7fff << 0) +-#define STB0899_OFFST_UW_MAX_PEAK 0 +-#define STB0899_WIDTH_UW_MAX_PEAK 15 +- +-#define STB0899_OFF0_UWP_STAT2 0xf32c +-#define STB0899_BASE_UWP_STAT2 0x00000400 +-#define STB0899_ESNO_EST (0x07ffff << 7) +-#define STB0899_OFFST_ESN0_EST 7 +-#define STB0899_WIDTH_ESN0_EST 19 +-#define STB0899_UWP_DECODE_MOD (0x7f << 0) +-#define STB0899_OFFST_UWP_DECODE_MOD 0 +-#define STB0899_WIDTH_UWP_DECODE_MOD 7 +- +-#define STB0899_OFF0_DMD_CORE_ID 0xf334 +-#define STB0899_BASE_DMD_CORE_ID 0x00000400 +-#define STB0899_CORE_ID (0xffffffff << 0) +-#define STB0899_OFFST_CORE_ID 0 +-#define STB0899_WIDTH_CORE_ID 32 +- +-#define STB0899_OFF0_DMD_VERSION_ID 0xf33c +-#define STB0899_BASE_DMD_VERSION_ID 0x00000400 +-#define STB0899_VERSION_ID (0xff << 0) +-#define STB0899_OFFST_VERSION_ID 0 +-#define STB0899_WIDTH_VERSION_ID 8 +- +-#define STB0899_OFF0_DMD_STAT2 0xf340 +-#define STB0899_BASE_DMD_STAT2 0x00000400 +-#define STB0899_CSM_LOCK (0x01 << 1) +-#define STB0899_OFFST_CSM_LOCK 1 +-#define STB0899_WIDTH_CSM_LOCK 1 +-#define STB0899_UWP_LOCK (0x01 << 0) +-#define STB0899_OFFST_UWP_LOCK 0 +-#define STB0899_WIDTH_UWP_LOCK 1 +- +-#define STB0899_OFF0_FREQ_ADJ_SCALE 0xf344 +-#define STB0899_BASE_FREQ_ADJ_SCALE 0x00000400 +-#define STB0899_FREQ_ADJ_SCALE (0x0fff << 0) +-#define STB0899_OFFST_FREQ_ADJ_SCALE 0 +-#define STB0899_WIDTH_FREQ_ADJ_SCALE 12 +- +-#define STB0899_OFF0_UWP_CNTRL3 0xf34c +-#define STB0899_BASE_UWP_CNTRL3 0x00000400 +-#define STB0899_UWP_TH_TRACK (0x7fff << 15) +-#define STB0899_OFFST_UWP_TH_TRACK 15 +-#define STB0899_WIDTH_UWP_TH_TRACK 15 +-#define STB0899_UWP_TH_ACQ (0x7fff << 0) +-#define STB0899_OFFST_UWP_TH_ACQ 0 +-#define STB0899_WIDTH_UWP_TH_ACQ 15 +- +-#define STB0899_OFF0_SYM_CLK_SEL 0xf350 +-#define STB0899_BASE_SYM_CLK_SEL 0x00000400 +-#define STB0899_SYM_CLK_SEL (0x03 << 0) +-#define STB0899_OFFST_SYM_CLK_SEL 0 +-#define STB0899_WIDTH_SYM_CLK_SEL 2 +- +-#define STB0899_OFF0_SOF_SRCH_TO 0xf354 +-#define STB0899_BASE_SOF_SRCH_TO 0x00000400 +-#define STB0899_SOF_SEARCH_TIMEOUT (0x3fffff << 0) +-#define STB0899_OFFST_SOF_SEARCH_TIMEOUT 0 +-#define STB0899_WIDTH_SOF_SEARCH_TIMEOUT 22 +- +-#define STB0899_OFF0_ACQ_CNTRL1 0xf358 +-#define STB0899_BASE_ACQ_CNTRL1 0x00000400 +-#define STB0899_FE_FINE_ACQ (0xff << 8) +-#define STB0899_OFFST_FE_FINE_ACQ 8 +-#define STB0899_WIDTH_FE_FINE_ACQ 8 +-#define STB0899_FE_COARSE_ACQ (0xff << 0) +-#define STB0899_OFFST_FE_COARSE_ACQ 0 +-#define STB0899_WIDTH_FE_COARSE_ACQ 8 +- +-#define STB0899_OFF0_ACQ_CNTRL2 0xf35c +-#define STB0899_BASE_ACQ_CNTRL2 0x00000400 +-#define STB0899_ZIGZAG (0x01 << 25) +-#define STB0899_OFFST_ZIGZAG 25 +-#define STB0899_WIDTH_ZIGZAG 1 +-#define STB0899_NUM_STEPS (0xff << 17) +-#define STB0899_OFFST_NUM_STEPS 17 +-#define STB0899_WIDTH_NUM_STEPS 8 +-#define STB0899_FREQ_STEPSIZE (0x1ffff << 0) +-#define STB0899_OFFST_FREQ_STEPSIZE 0 +-#define STB0899_WIDTH_FREQ_STEPSIZE 17 +- +-#define STB0899_OFF0_ACQ_CNTRL3 0xf360 +-#define STB0899_BASE_ACQ_CNTRL3 0x00000400 +-#define STB0899_THRESHOLD_SCL (0x3f << 23) +-#define STB0899_OFFST_THRESHOLD_SCL 23 +-#define STB0899_WIDTH_THRESHOLD_SCL 6 +-#define STB0899_UWP_TH_SRCH (0x7fff << 8) +-#define STB0899_OFFST_UWP_TH_SRCH 8 +-#define STB0899_WIDTH_UWP_TH_SRCH 15 +-#define STB0899_AUTO_REACQUIRE (0x01 << 7) +-#define STB0899_OFFST_AUTO_REACQUIRE 7 +-#define STB0899_WIDTH_AUTO_REACQUIRE 1 +-#define STB0899_TRACK_LOCK_SEL (0x01 << 6) +-#define STB0899_OFFST_TRACK_LOCK_SEL 6 +-#define STB0899_WIDTH_TRACK_LOCK_SEL 1 +-#define STB0899_ACQ_SEARCH_MODE (0x03 << 4) +-#define STB0899_OFFST_ACQ_SEARCH_MODE 4 +-#define STB0899_WIDTH_ACQ_SEARCH_MODE 2 +-#define STB0899_CONFIRM_FRAMES (0x0f << 0) +-#define STB0899_OFFST_CONFIRM_FRAMES 0 +-#define STB0899_WIDTH_CONFIRM_FRAMES 4 +- +-#define STB0899_OFF0_FE_SETTLE 0xf364 +-#define STB0899_BASE_FE_SETTLE 0x00000400 +-#define STB0899_SETTLING_TIME (0x3fffff << 0) +-#define STB0899_OFFST_SETTLING_TIME 0 +-#define STB0899_WIDTH_SETTLING_TIME 22 +- +-#define STB0899_OFF0_AC_DWELL 0xf368 +-#define STB0899_BASE_AC_DWELL 0x00000400 +-#define STB0899_DWELL_TIME (0x3fffff << 0) +-#define STB0899_OFFST_DWELL_TIME 0 +-#define STB0899_WIDTH_DWELL_TIME 22 +- +-#define STB0899_OFF0_ACQUIRE_TRIG 0xf36c +-#define STB0899_BASE_ACQUIRE_TRIG 0x00000400 +-#define STB0899_ACQUIRE (0x01 << 0) +-#define STB0899_OFFST_ACQUIRE 0 +-#define STB0899_WIDTH_ACQUIRE 1 +- +-#define STB0899_OFF0_LOCK_LOST 0xf370 +-#define STB0899_BASE_LOCK_LOST 0x00000400 +-#define STB0899_LOCK_LOST (0x01 << 0) +-#define STB0899_OFFST_LOCK_LOST 0 +-#define STB0899_WIDTH_LOCK_LOST 1 +- +-#define STB0899_OFF0_ACQ_STAT1 0xf374 +-#define STB0899_BASE_ACQ_STAT1 0x00000400 +-#define STB0899_STEP_FREQ (0x1fffff << 11) +-#define STB0899_OFFST_STEP_FREQ 11 +-#define STB0899_WIDTH_STEP_FREQ 21 +-#define STB0899_ACQ_STATE (0x07 << 8) +-#define STB0899_OFFST_ACQ_STATE 8 +-#define STB0899_WIDTH_ACQ_STATE 3 +-#define STB0899_UW_DETECT_COUNT (0xff << 0) +-#define STB0899_OFFST_UW_DETECT_COUNT 0 +-#define STB0899_WIDTH_UW_DETECT_COUNT 8 +- +-#define STB0899_OFF0_ACQ_TIMEOUT 0xf378 +-#define STB0899_BASE_ACQ_TIMEOUT 0x00000400 +-#define STB0899_ACQ_TIMEOUT (0x3fffff << 0) +-#define STB0899_OFFST_ACQ_TIMEOUT 0 +-#define STB0899_WIDTH_ACQ_TIMEOUT 22 +- +-#define STB0899_OFF0_ACQ_TIME 0xf37c +-#define STB0899_BASE_ACQ_TIME 0x00000400 +-#define STB0899_ACQ_TIME_SYM (0xffffff << 0) +-#define STB0899_OFFST_ACQ_TIME_SYM 0 +-#define STB0899_WIDTH_ACQ_TIME_SYM 24 +- +-#define STB0899_OFF0_FINAL_AGC_CNTRL 0xf308 +-#define STB0899_BASE_FINAL_AGC_CNTRL 0x00000440 +-#define STB0899_FINAL_GAIN_INIT (0x3fff << 12) +-#define STB0899_OFFST_FINAL_GAIN_INIT 12 +-#define STB0899_WIDTH_FINAL_GAIN_INIT 14 +-#define STB0899_FINAL_LOOP_GAIN (0x0f << 8) +-#define STB0899_OFFST_FINAL_LOOP_GAIN 8 +-#define STB0899_WIDTH_FINAL_LOOP_GAIN 4 +-#define STB0899_FINAL_LD_GAIN_INIT (0x01 << 7) +-#define STB0899_OFFST_FINAL_LD_GAIN_INIT 7 +-#define STB0899_WIDTH_FINAL_LD_GAIN_INIT 1 +-#define STB0899_FINAL_AGC_REF (0x7f << 0) +-#define STB0899_OFFST_FINAL_AGC_REF 0 +-#define STB0899_WIDTH_FINAL_AGC_REF 7 +- +-#define STB0899_OFF0_FINAL_AGC_GAIN 0xf30c +-#define STB0899_BASE_FINAL_AGC_GAIN 0x00000440 +-#define STB0899_FINAL_AGC_GAIN (0x3fff << 0) +-#define STB0899_OFFST_FINAL_AGC_GAIN 0 +-#define STB0899_WIDTH_FINAL_AGC_GAIN 14 +- +-#define STB0899_OFF0_EQUALIZER_INIT 0xf310 +-#define STB0899_BASE_EQUALIZER_INIT 0x00000440 +-#define STB0899_EQ_SRST (0x01 << 1) +-#define STB0899_OFFST_EQ_SRST 1 +-#define STB0899_WIDTH_EQ_SRST 1 +-#define STB0899_EQ_INIT (0x01 << 0) +-#define STB0899_OFFST_EQ_INIT 0 +-#define STB0899_WIDTH_EQ_INIT 1 +- +-#define STB0899_OFF0_EQ_CNTRL 0xf314 +-#define STB0899_BASE_EQ_CNTRL 0x00000440 +-#define STB0899_EQ_ADAPT_MODE (0x01 << 18) +-#define STB0899_OFFST_EQ_ADAPT_MODE 18 +-#define STB0899_WIDTH_EQ_ADAPT_MODE 1 +-#define STB0899_EQ_DELAY (0x0f << 14) +-#define STB0899_OFFST_EQ_DELAY 14 +-#define STB0899_WIDTH_EQ_DELAY 4 +-#define STB0899_EQ_QUANT_LEVEL (0xff << 6) +-#define STB0899_OFFST_EQ_QUANT_LEVEL 6 +-#define STB0899_WIDTH_EQ_QUANT_LEVEL 8 +-#define STB0899_EQ_DISABLE_UPDATE (0x01 << 5) +-#define STB0899_OFFST_EQ_DISABLE_UPDATE 5 +-#define STB0899_WIDTH_EQ_DISABLE_UPDATE 1 +-#define STB0899_EQ_BYPASS (0x01 << 4) +-#define STB0899_OFFST_EQ_BYPASS 4 +-#define STB0899_WIDTH_EQ_BYPASS 1 +-#define STB0899_EQ_SHIFT (0x0f << 0) +-#define STB0899_OFFST_EQ_SHIFT 0 +-#define STB0899_WIDTH_EQ_SHIFT 4 +- +-#define STB0899_OFF0_EQ_I_INIT_COEFF_0 0xf320 +-#define STB0899_OFF1_EQ_I_INIT_COEFF_1 0xf324 +-#define STB0899_OFF2_EQ_I_INIT_COEFF_2 0xf328 +-#define STB0899_OFF3_EQ_I_INIT_COEFF_3 0xf32c +-#define STB0899_OFF4_EQ_I_INIT_COEFF_4 0xf330 +-#define STB0899_OFF5_EQ_I_INIT_COEFF_5 0xf334 +-#define STB0899_OFF6_EQ_I_INIT_COEFF_6 0xf338 +-#define STB0899_OFF7_EQ_I_INIT_COEFF_7 0xf33c +-#define STB0899_OFF8_EQ_I_INIT_COEFF_8 0xf340 +-#define STB0899_OFF9_EQ_I_INIT_COEFF_9 0xf344 +-#define STB0899_OFFa_EQ_I_INIT_COEFF_10 0xf348 +-#define STB0899_BASE_EQ_I_INIT_COEFF_N 0x00000440 +-#define STB0899_EQ_I_INIT_COEFF_N (0x0fff << 0) +-#define STB0899_OFFST_EQ_I_INIT_COEFF_N 0 +-#define STB0899_WIDTH_EQ_I_INIT_COEFF_N 12 +- +-#define STB0899_OFF0_EQ_Q_INIT_COEFF_0 0xf350 +-#define STB0899_OFF1_EQ_Q_INIT_COEFF_1 0xf354 +-#define STB0899_OFF2_EQ_Q_INIT_COEFF_2 0xf358 +-#define STB0899_OFF3_EQ_Q_INIT_COEFF_3 0xf35c +-#define STB0899_OFF4_EQ_Q_INIT_COEFF_4 0xf360 +-#define STB0899_OFF5_EQ_Q_INIT_COEFF_5 0xf364 +-#define STB0899_OFF6_EQ_Q_INIT_COEFF_6 0xf368 +-#define STB0899_OFF7_EQ_Q_INIT_COEFF_7 0xf36c +-#define STB0899_OFF8_EQ_Q_INIT_COEFF_8 0xf370 +-#define STB0899_OFF9_EQ_Q_INIT_COEFF_9 0xf374 +-#define STB0899_OFFa_EQ_Q_INIT_COEFF_10 0xf378 +-#define STB0899_BASE_EQ_Q_INIT_COEFF_N 0x00000440 +-#define STB0899_EQ_Q_INIT_COEFF_N (0x0fff << 0) +-#define STB0899_OFFST_EQ_Q_INIT_COEFF_N 0 +-#define STB0899_WIDTH_EQ_Q_INIT_COEFF_N 12 +- +-#define STB0899_OFF0_EQ_I_OUT_COEFF_0 0xf300 +-#define STB0899_OFF1_EQ_I_OUT_COEFF_1 0xf304 +-#define STB0899_OFF2_EQ_I_OUT_COEFF_2 0xf308 +-#define STB0899_OFF3_EQ_I_OUT_COEFF_3 0xf30c +-#define STB0899_OFF4_EQ_I_OUT_COEFF_4 0xf310 +-#define STB0899_OFF5_EQ_I_OUT_COEFF_5 0xf314 +-#define STB0899_OFF6_EQ_I_OUT_COEFF_6 0xf318 +-#define STB0899_OFF7_EQ_I_OUT_COEFF_7 0xf31c +-#define STB0899_OFF8_EQ_I_OUT_COEFF_8 0xf320 +-#define STB0899_OFF9_EQ_I_OUT_COEFF_9 0xf324 +-#define STB0899_OFFa_EQ_I_OUT_COEFF_10 0xf328 +-#define STB0899_BASE_EQ_I_OUT_COEFF_N 0x00000460 +-#define STB0899_EQ_I_OUT_COEFF_N (0x0fff << 0) +-#define STB0899_OFFST_EQ_I_OUT_COEFF_N 0 +-#define STB0899_WIDTH_EQ_I_OUT_COEFF_N 12 +- +-#define STB0899_OFF0_EQ_Q_OUT_COEFF_0 0xf330 +-#define STB0899_OFF1_EQ_Q_OUT_COEFF_1 0xf334 +-#define STB0899_OFF2_EQ_Q_OUT_COEFF_2 0xf338 +-#define STB0899_OFF3_EQ_Q_OUT_COEFF_3 0xf33c +-#define STB0899_OFF4_EQ_Q_OUT_COEFF_4 0xf340 +-#define STB0899_OFF5_EQ_Q_OUT_COEFF_5 0xf344 +-#define STB0899_OFF6_EQ_Q_OUT_COEFF_6 0xf348 +-#define STB0899_OFF7_EQ_Q_OUT_COEFF_7 0xf34c +-#define STB0899_OFF8_EQ_Q_OUT_COEFF_8 0xf350 +-#define STB0899_OFF9_EQ_Q_OUT_COEFF_9 0xf354 +-#define STB0899_OFFa_EQ_Q_OUT_COEFF_10 0xf358 +-#define STB0899_BASE_EQ_Q_OUT_COEFF_N 0x00000460 +-#define STB0899_EQ_Q_OUT_COEFF_N (0x0fff << 0) +-#define STB0899_OFFST_EQ_Q_OUT_COEFF_N 0 +-#define STB0899_WIDTH_EQ_Q_OUT_COEFF_N 12 +- +-/* S2 FEC */ +-#define STB0899_OFF0_BLOCK_LNGTH 0xfa04 +-#define STB0899_BASE_BLOCK_LNGTH 0x00000000 +-#define STB0899_BLOCK_LENGTH (0xff << 0) +-#define STB0899_OFFST_BLOCK_LENGTH 0 +-#define STB0899_WIDTH_BLOCK_LENGTH 8 +- +-#define STB0899_OFF0_ROW_STR 0xfa08 +-#define STB0899_BASE_ROW_STR 0x00000000 +-#define STB0899_ROW_STRIDE (0xff << 0) +-#define STB0899_OFFST_ROW_STRIDE 0 +-#define STB0899_WIDTH_ROW_STRIDE 8 +- +-#define STB0899_OFF0_MAX_ITER 0xfa0c +-#define STB0899_BASE_MAX_ITER 0x00000000 +-#define STB0899_MAX_ITERATIONS (0xff << 0) +-#define STB0899_OFFST_MAX_ITERATIONS 0 +-#define STB0899_WIDTH_MAX_ITERATIONS 8 +- +-#define STB0899_OFF0_BN_END_ADDR 0xfa10 +-#define STB0899_BASE_BN_END_ADDR 0x00000000 +-#define STB0899_BN_END_ADDR (0x0fff << 0) +-#define STB0899_OFFST_BN_END_ADDR 0 +-#define STB0899_WIDTH_BN_END_ADDR 12 +- +-#define STB0899_OFF0_CN_END_ADDR 0xfa14 +-#define STB0899_BASE_CN_END_ADDR 0x00000000 +-#define STB0899_CN_END_ADDR (0x0fff << 0) +-#define STB0899_OFFST_CN_END_ADDR 0 +-#define STB0899_WIDTH_CN_END_ADDR 12 +- +-#define STB0899_OFF0_INFO_LENGTH 0xfa1c +-#define STB0899_BASE_INFO_LENGTH 0x00000000 +-#define STB0899_INFO_LENGTH (0xff << 0) +-#define STB0899_OFFST_INFO_LENGTH 0 +-#define STB0899_WIDTH_INFO_LENGTH 8 +- +-#define STB0899_OFF0_BOT_ADDR 0xfa20 +-#define STB0899_BASE_BOT_ADDR 0x00000000 +-#define STB0899_BOTTOM_BASE_ADDR (0x03ff << 0) +-#define STB0899_OFFST_BOTTOM_BASE_ADDR 0 +-#define STB0899_WIDTH_BOTTOM_BASE_ADDR 10 +- +-#define STB0899_OFF0_BCH_BLK_LN 0xfa24 +-#define STB0899_BASE_BCH_BLK_LN 0x00000000 +-#define STB0899_BCH_BLOCK_LENGTH (0xffff << 0) +-#define STB0899_OFFST_BCH_BLOCK_LENGTH 0 +-#define STB0899_WIDTH_BCH_BLOCK_LENGTH 16 +- +-#define STB0899_OFF0_BCH_T 0xfa28 +-#define STB0899_BASE_BCH_T 0x00000000 +-#define STB0899_BCH_T (0x0f << 0) +-#define STB0899_OFFST_BCH_T 0 +-#define STB0899_WIDTH_BCH_T 4 +- +-#define STB0899_OFF0_CNFG_MODE 0xfa00 +-#define STB0899_BASE_CNFG_MODE 0x00000800 +-#define STB0899_MODCOD (0x1f << 2) +-#define STB0899_OFFST_MODCOD 2 +-#define STB0899_WIDTH_MODCOD 5 +-#define STB0899_MODCOD_SEL (0x01 << 1) +-#define STB0899_OFFST_MODCOD_SEL 1 +-#define STB0899_WIDTH_MODCOD_SEL 1 +-#define STB0899_CONFIG_MODE (0x01 << 0) +-#define STB0899_OFFST_CONFIG_MODE 0 +-#define STB0899_WIDTH_CONFIG_MODE 1 +- +-#define STB0899_OFF0_LDPC_STAT 0xfa04 +-#define STB0899_BASE_LDPC_STAT 0x00000800 +-#define STB0899_ITERATION (0xff << 3) +-#define STB0899_OFFST_ITERATION 3 +-#define STB0899_WIDTH_ITERATION 8 +-#define STB0899_LDPC_DEC_STATE (0x07 << 0) +-#define STB0899_OFFST_LDPC_DEC_STATE 0 +-#define STB0899_WIDTH_LDPC_DEC_STATE 3 +- +-#define STB0899_OFF0_ITER_SCALE 0xfa08 +-#define STB0899_BASE_ITER_SCALE 0x00000800 +-#define STB0899_ITERATION_SCALE (0xff << 0) +-#define STB0899_OFFST_ITERATION_SCALE 0 +-#define STB0899_WIDTH_ITERATION_SCALE 8 +- +-#define STB0899_OFF0_INPUT_MODE 0xfa0c +-#define STB0899_BASE_INPUT_MODE 0x00000800 +-#define STB0899_SD_BLOCK1_STREAM0 (0x01 << 0) +-#define STB0899_OFFST_SD_BLOCK1_STREAM0 0 +-#define STB0899_WIDTH_SD_BLOCK1_STREAM0 1 +- +-#define STB0899_OFF0_LDPCDECRST 0xfa10 +-#define STB0899_BASE_LDPCDECRST 0x00000800 +-#define STB0899_LDPC_DEC_RST (0x01 << 0) +-#define STB0899_OFFST_LDPC_DEC_RST 0 +-#define STB0899_WIDTH_LDPC_DEC_RST 1 +- +-#define STB0899_OFF0_CLK_PER_BYTE_RW 0xfa14 +-#define STB0899_BASE_CLK_PER_BYTE_RW 0x00000800 +-#define STB0899_CLKS_PER_BYTE (0x0f << 0) +-#define STB0899_OFFST_CLKS_PER_BYTE 0 +-#define STB0899_WIDTH_CLKS_PER_BYTE 5 +- +-#define STB0899_OFF0_BCH_ERRORS 0xfa18 +-#define STB0899_BASE_BCH_ERRORS 0x00000800 +-#define STB0899_BCH_ERRORS (0x0f << 0) +-#define STB0899_OFFST_BCH_ERRORS 0 +-#define STB0899_WIDTH_BCH_ERRORS 4 +- +-#define STB0899_OFF0_LDPC_ERRORS 0xfa1c +-#define STB0899_BASE_LDPC_ERRORS 0x00000800 +-#define STB0899_LDPC_ERRORS (0xffff << 0) +-#define STB0899_OFFST_LDPC_ERRORS 0 +-#define STB0899_WIDTH_LDPC_ERRORS 16 +- +-#define STB0899_OFF0_BCH_MODE 0xfa20 +-#define STB0899_BASE_BCH_MODE 0x00000800 +-#define STB0899_BCH_CORRECT_N (0x01 << 1) +-#define STB0899_OFFST_BCH_CORRECT_N 1 +-#define STB0899_WIDTH_BCH_CORRECT_N 1 +-#define STB0899_FULL_BYPASS (0x01 << 0) +-#define STB0899_OFFST_FULL_BYPASS 0 +-#define STB0899_WIDTH_FULL_BYPASS 1 +- +-#define STB0899_OFF0_ERR_ACC_PER 0xfa24 +-#define STB0899_BASE_ERR_ACC_PER 0x00000800 +-#define STB0899_BCH_ERR_ACC_PERIOD (0x0f << 0) +-#define STB0899_OFFST_BCH_ERR_ACC_PERIOD 0 +-#define STB0899_WIDTH_BCH_ERR_ACC_PERIOD 4 +- +-#define STB0899_OFF0_BCH_ERR_ACC 0xfa28 +-#define STB0899_BASE_BCH_ERR_ACC 0x00000800 +-#define STB0899_BCH_ERR_ACCUM (0xff << 0) +-#define STB0899_OFFST_BCH_ERR_ACCUM 0 +-#define STB0899_WIDTH_BCH_ERR_ACCUM 8 +- +-#define STB0899_OFF0_FEC_CORE_ID_REG 0xfa2c +-#define STB0899_BASE_FEC_CORE_ID_REG 0x00000800 +-#define STB0899_FEC_CORE_ID (0xffffffff << 0) +-#define STB0899_OFFST_FEC_CORE_ID 0 +-#define STB0899_WIDTH_FEC_CORE_ID 32 +- +-#define STB0899_OFF0_FEC_VER_ID_REG 0xfa34 +-#define STB0899_BASE_FEC_VER_ID_REG 0x00000800 +-#define STB0899_FEC_VER_ID (0xff << 0) +-#define STB0899_OFFST_FEC_VER_ID 0 +-#define STB0899_WIDTH_FEC_VER_ID 8 +- +-#define STB0899_OFF0_FEC_TP_SEL 0xfa38 +-#define STB0899_BASE_FEC_TP_SEL 0x00000800 +- +-#define STB0899_OFF0_CSM_CNTRL1 0xf310 +-#define STB0899_BASE_CSM_CNTRL1 0x00000400 +-#define STB0899_CSM_FORCE_FREQLOCK (0x01 << 19) +-#define STB0899_OFFST_CSM_FORCE_FREQLOCK 19 +-#define STB0899_WIDTH_CSM_FORCE_FREQLOCK 1 +-#define STB0899_CSM_FREQ_LOCKSTATE (0x01 << 18) +-#define STB0899_OFFST_CSM_FREQ_LOCKSTATE 18 +-#define STB0899_WIDTH_CSM_FREQ_LOCKSTATE 1 +-#define STB0899_CSM_AUTO_PARAM (0x01 << 17) +-#define STB0899_OFFST_CSM_AUTO_PARAM 17 +-#define STB0899_WIDTH_CSM_AUTO_PARAM 1 +-#define STB0899_FE_LOOP_SHIFT (0x07 << 14) +-#define STB0899_OFFST_FE_LOOP_SHIFT 14 +-#define STB0899_WIDTH_FE_LOOP_SHIFT 3 +-#define STB0899_CSM_AGC_SHIFT (0x07 << 11) +-#define STB0899_OFFST_CSM_AGC_SHIFT 11 +-#define STB0899_WIDTH_CSM_AGC_SHIFT 3 +-#define STB0899_CSM_AGC_GAIN (0x1ff << 2) +-#define STB0899_OFFST_CSM_AGC_GAIN 2 +-#define STB0899_WIDTH_CSM_AGC_GAIN 9 +-#define STB0899_CSM_TWO_PASS (0x01 << 1) +-#define STB0899_OFFST_CSM_TWO_PASS 1 +-#define STB0899_WIDTH_CSM_TWO_PASS 1 +-#define STB0899_CSM_DVT_TABLE (0x01 << 0) +-#define STB0899_OFFST_CSM_DVT_TABLE 0 +-#define STB0899_WIDTH_CSM_DVT_TABLE 1 +- +-#define STB0899_OFF0_CSM_CNTRL2 0xf314 +-#define STB0899_BASE_CSM_CNTRL2 0x00000400 +-#define STB0899_CSM_GAMMA_RHO_ACQ (0x1ff << 9) +-#define STB0899_OFFST_CSM_GAMMA_RHOACQ 9 +-#define STB0899_WIDTH_CSM_GAMMA_RHOACQ 9 +-#define STB0899_CSM_GAMMA_ACQ (0x1ff << 0) +-#define STB0899_OFFST_CSM_GAMMA_ACQ 0 +-#define STB0899_WIDTH_CSM_GAMMA_ACQ 9 +- +-#define STB0899_OFF0_CSM_CNTRL3 0xf318 +-#define STB0899_BASE_CSM_CNTRL3 0x00000400 +-#define STB0899_CSM_GAMMA_RHO_TRACK (0x1ff << 9) +-#define STB0899_OFFST_CSM_GAMMA_RHOTRACK 9 +-#define STB0899_WIDTH_CSM_GAMMA_RHOTRACK 9 +-#define STB0899_CSM_GAMMA_TRACK (0x1ff << 0) +-#define STB0899_OFFST_CSM_GAMMA_TRACK 0 +-#define STB0899_WIDTH_CSM_GAMMA_TRACK 9 +- +-#define STB0899_OFF0_CSM_CNTRL4 0xf31c +-#define STB0899_BASE_CSM_CNTRL4 0x00000400 +-#define STB0899_CSM_PHASEDIFF_THRESH (0x0f << 8) +-#define STB0899_OFFST_CSM_PHASEDIFF_THRESH 8 +-#define STB0899_WIDTH_CSM_PHASEDIFF_THRESH 4 +-#define STB0899_CSM_LOCKCOUNT_THRESH (0xff << 0) +-#define STB0899_OFFST_CSM_LOCKCOUNT_THRESH 0 +-#define STB0899_WIDTH_CSM_LOCKCOUNT_THRESH 8 +- +-/* Check on chapter 8 page 42 */ +-#define STB0899_ERRCTRL1 0xf574 +-#define STB0899_ERRCTRL2 0xf575 +-#define STB0899_ERRCTRL3 0xf576 +-#define STB0899_ERR_SRC_S1 (0x1f << 3) +-#define STB0899_OFFST_ERR_SRC_S1 3 +-#define STB0899_WIDTH_ERR_SRC_S1 5 +-#define STB0899_ERR_SRC_S2 (0x0f << 0) +-#define STB0899_OFFST_ERR_SRC_S2 0 +-#define STB0899_WIDTH_ERR_SRC_S2 4 +-#define STB0899_NOE (0x07 << 0) +-#define STB0899_OFFST_NOE 0 +-#define STB0899_WIDTH_NOE 3 +- +-#define STB0899_ECNT1M 0xf524 +-#define STB0899_ECNT1L 0xf525 +-#define STB0899_ECNT2M 0xf526 +-#define STB0899_ECNT2L 0xf527 +-#define STB0899_ECNT3M 0xf528 +-#define STB0899_ECNT3L 0xf529 +- +-#define STB0899_DMONMSK1 0xf57b +-#define STB0899_DMONMSK1_WAIT_1STEP (1 << 7) +-#define STB0899_DMONMSK1_FREE_14 (1 << 6) +-#define STB0899_DMONMSK1_AVRGVIT_CALC (1 << 5) +-#define STB0899_DMONMSK1_FREE_12 (1 << 4) +-#define STB0899_DMONMSK1_FREE_11 (1 << 3) +-#define STB0899_DMONMSK1_B0DIV_CALC (1 << 2) +-#define STB0899_DMONMSK1_KDIVB1_CALC (1 << 1) +-#define STB0899_DMONMSK1_KDIVB2_CALC (1 << 0) +- +-#define STB0899_DMONMSK0 0xf57c +-#define STB0899_DMONMSK0_SMOTTH_CALC (1 << 7) +-#define STB0899_DMONMSK0_FREE_6 (1 << 6) +-#define STB0899_DMONMSK0_SIGPOWER_CALC (1 << 5) +-#define STB0899_DMONMSK0_QSEUIL_CALC (1 << 4) +-#define STB0899_DMONMSK0_FREE_3 (1 << 3) +-#define STB0899_DMONMSK0_FREE_2 (1 << 2) +-#define STB0899_DMONMSK0_KVDIVB1_CALC (1 << 1) +-#define STB0899_DMONMSK0_KVDIVB2_CALC (1 << 0) +- +-#define STB0899_TSULC 0xf549 +-#define STB0899_ULNOSYNCBYTES (0x01 << 7) +-#define STB0899_OFFST_ULNOSYNCBYTES 7 +-#define STB0899_WIDTH_ULNOSYNCBYTES 1 +-#define STB0899_ULPARITY_ON (0x01 << 6) +-#define STB0899_OFFST_ULPARITY_ON 6 +-#define STB0899_WIDTH_ULPARITY_ON 1 +-#define STB0899_ULSYNCOUTRS (0x01 << 5) +-#define STB0899_OFFST_ULSYNCOUTRS 5 +-#define STB0899_WIDTH_ULSYNCOUTRS 1 +-#define STB0899_ULDSS_PACKETS (0x01 << 0) +-#define STB0899_OFFST_ULDSS_PACKETS 0 +-#define STB0899_WIDTH_ULDSS_PACKETS 1 +- +-#define STB0899_TSLPL 0xf54b +-#define STB0899_LLDVBS2_MODE (0x01 << 4) +-#define STB0899_OFFST_LLDVBS2_MODE 4 +-#define STB0899_WIDTH_LLDVBS2_MODE 1 +-#define STB0899_LLISSYI_ON (0x01 << 3) +-#define STB0899_OFFST_LLISSYI_ON 3 +-#define STB0899_WIDTH_LLISSYI_ON 1 +-#define STB0899_LLNPD_ON (0x01 << 2) +-#define STB0899_OFFST_LLNPD_ON 2 +-#define STB0899_WIDTH_LLNPD_ON 1 +-#define STB0899_LLCRC8_ON (0x01 << 1) +-#define STB0899_OFFST_LLCRC8_ON 1 +-#define STB0899_WIDTH_LLCRC8_ON 1 +- +-#define STB0899_TSCFGH 0xf54c +-#define STB0899_OUTRS_PS (0x01 << 6) +-#define STB0899_OFFST_OUTRS_PS 6 +-#define STB0899_WIDTH_OUTRS_PS 1 +-#define STB0899_SYNCBYTE (0x01 << 5) +-#define STB0899_OFFST_SYNCBYTE 5 +-#define STB0899_WIDTH_SYNCBYTE 1 +-#define STB0899_PFBIT (0x01 << 4) +-#define STB0899_OFFST_PFBIT 4 +-#define STB0899_WIDTH_PFBIT 1 +-#define STB0899_ERR_BIT (0x01 << 3) +-#define STB0899_OFFST_ERR_BIT 3 +-#define STB0899_WIDTH_ERR_BIT 1 +-#define STB0899_MPEG (0x01 << 2) +-#define STB0899_OFFST_MPEG 2 +-#define STB0899_WIDTH_MPEG 1 +-#define STB0899_CLK_POL (0x01 << 1) +-#define STB0899_OFFST_CLK_POL 1 +-#define STB0899_WIDTH_CLK_POL 1 +-#define STB0899_FORCE0 (0x01 << 0) +-#define STB0899_OFFST_FORCE0 0 +-#define STB0899_WIDTH_FORCE0 1 +- +-#define STB0899_TSCFGM 0xf54d +-#define STB0899_LLPRIORITY (0x01 << 3) +-#define STB0899_OFFST_LLPRIORIY 3 +-#define STB0899_WIDTH_LLPRIORITY 1 +-#define STB0899_EN188 (0x01 << 2) +-#define STB0899_OFFST_EN188 2 +-#define STB0899_WIDTH_EN188 1 +- +-#define STB0899_TSCFGL 0xf54e +-#define STB0899_DEL_ERRPCK (0x01 << 7) +-#define STB0899_OFFST_DEL_ERRPCK 7 +-#define STB0899_WIDTH_DEL_ERRPCK 1 +-#define STB0899_ERRFLAGSTD (0x01 << 5) +-#define STB0899_OFFST_ERRFLAGSTD 5 +-#define STB0899_WIDTH_ERRFLAGSTD 1 +-#define STB0899_MPEGERR (0x01 << 4) +-#define STB0899_OFFST_MPEGERR 4 +-#define STB0899_WIDTH_MPEGERR 1 +-#define STB0899_BCH_CHK (0x01 << 3) +-#define STB0899_OFFST_BCH_CHK 5 +-#define STB0899_WIDTH_BCH_CHK 1 +-#define STB0899_CRC8CHK (0x01 << 2) +-#define STB0899_OFFST_CRC8CHK 2 +-#define STB0899_WIDTH_CRC8CHK 1 +-#define STB0899_SPEC_INFO (0x01 << 1) +-#define STB0899_OFFST_SPEC_INFO 1 +-#define STB0899_WIDTH_SPEC_INFO 1 +-#define STB0899_LOW_PRIO_CLK (0x01 << 0) +-#define STB0899_OFFST_LOW_PRIO_CLK 0 +-#define STB0899_WIDTH_LOW_PRIO_CLK 1 +-#define STB0899_ERROR_NORM (0x00 << 0) +-#define STB0899_OFFST_ERROR_NORM 0 +-#define STB0899_WIDTH_ERROR_NORM 0 +- +-#define STB0899_TSOUT 0xf54f +-#define STB0899_RSSYNCDEL 0xf550 +-#define STB0899_TSINHDELH 0xf551 +-#define STB0899_TSINHDELM 0xf552 +-#define STB0899_TSINHDELL 0xf553 +-#define STB0899_TSLLSTKM 0xf55a +-#define STB0899_TSLLSTKL 0xf55b +-#define STB0899_TSULSTKM 0xf55c +-#define STB0899_TSULSTKL 0xf55d +-#define STB0899_TSSTATUS 0xf561 +- +-#define STB0899_PDELCTRL 0xf600 +-#define STB0899_INVERT_RES (0x01 << 7) +-#define STB0899_OFFST_INVERT_RES 7 +-#define STB0899_WIDTH_INVERT_RES 1 +-#define STB0899_FORCE_ACCEPTED (0x01 << 6) +-#define STB0899_OFFST_FORCE_ACCEPTED 6 +-#define STB0899_WIDTH_FORCE_ACCEPTED 1 +-#define STB0899_FILTER_EN (0x01 << 5) +-#define STB0899_OFFST_FILTER_EN 5 +-#define STB0899_WIDTH_FILTER_EN 1 +-#define STB0899_LOCKFALL_THRESH (0x01 << 4) +-#define STB0899_OFFST_LOCKFALL_THRESH 4 +-#define STB0899_WIDTH_LOCKFALL_THRESH 1 +-#define STB0899_HYST_EN (0x01 << 3) +-#define STB0899_OFFST_HYST_EN 3 +-#define STB0899_WIDTH_HYST_EN 1 +-#define STB0899_HYST_SWRST (0x01 << 2) +-#define STB0899_OFFST_HYST_SWRST 2 +-#define STB0899_WIDTH_HYST_SWRST 1 +-#define STB0899_ALGO_EN (0x01 << 1) +-#define STB0899_OFFST_ALGO_EN 1 +-#define STB0899_WIDTH_ALGO_EN 1 +-#define STB0899_ALGO_SWRST (0x01 << 0) +-#define STB0899_OFFST_ALGO_SWRST 0 +-#define STB0899_WIDTH_ALGO_SWRST 1 +- +-#define STB0899_PDELCTRL2 0xf601 +-#define STB0899_BBHCTRL1 0xf602 +-#define STB0899_BBHCTRL2 0xf603 +-#define STB0899_HYSTTHRESH 0xf604 +- +-#define STB0899_MATCSTM 0xf605 +-#define STB0899_MATCSTL 0xf606 +-#define STB0899_UPLCSTM 0xf607 +-#define STB0899_UPLCSTL 0xf608 +-#define STB0899_DFLCSTM 0xf609 +-#define STB0899_DFLCSTL 0xf60a +-#define STB0899_SYNCCST 0xf60b +-#define STB0899_SYNCDCSTM 0xf60c +-#define STB0899_SYNCDCSTL 0xf60d +-#define STB0899_ISI_ENTRY 0xf60e +-#define STB0899_ISI_BIT_EN 0xf60f +-#define STB0899_MATSTRM 0xf610 +-#define STB0899_MATSTRL 0xf611 +-#define STB0899_UPLSTRM 0xf612 +-#define STB0899_UPLSTRL 0xf613 +-#define STB0899_DFLSTRM 0xf614 +-#define STB0899_DFLSTRL 0xf615 +-#define STB0899_SYNCSTR 0xf616 +-#define STB0899_SYNCDSTRM 0xf617 +-#define STB0899_SYNCDSTRL 0xf618 +- +-#define STB0899_CFGPDELSTATUS1 0xf619 +-#define STB0899_BADDFL (0x01 << 6) +-#define STB0899_OFFST_BADDFL 6 +-#define STB0899_WIDTH_BADDFL 1 +-#define STB0899_CONTINUOUS_STREAM (0x01 << 5) +-#define STB0899_OFFST_CONTINUOUS_STREAM 5 +-#define STB0899_WIDTH_CONTINUOUS_STREAM 1 +-#define STB0899_ACCEPTED_STREAM (0x01 << 4) +-#define STB0899_OFFST_ACCEPTED_STREAM 4 +-#define STB0899_WIDTH_ACCEPTED_STREAM 1 +-#define STB0899_BCH_ERRFLAG (0x01 << 3) +-#define STB0899_OFFST_BCH_ERRFLAG 3 +-#define STB0899_WIDTH_BCH_ERRFLAG 1 +-#define STB0899_CRCRES (0x01 << 2) +-#define STB0899_OFFST_CRCRES 2 +-#define STB0899_WIDTH_CRCRES 1 +-#define STB0899_CFGPDELSTATUS_LOCK (0x01 << 1) +-#define STB0899_OFFST_CFGPDELSTATUS_LOCK 1 +-#define STB0899_WIDTH_CFGPDELSTATUS_LOCK 1 +-#define STB0899_1STLOCK (0x01 << 0) +-#define STB0899_OFFST_1STLOCK 0 +-#define STB0899_WIDTH_1STLOCK 1 +- +-#define STB0899_CFGPDELSTATUS2 0xf61a +-#define STB0899_BBFERRORM 0xf61b +-#define STB0899_BBFERRORL 0xf61c +-#define STB0899_UPKTERRORM 0xf61d +-#define STB0899_UPKTERRORL 0xf61e +- +-#define STB0899_TSTCK 0xff10 +- +-#define STB0899_TSTRES 0xff11 +-#define STB0899_FRESLDPC (0x01 << 7) +-#define STB0899_OFFST_FRESLDPC 7 +-#define STB0899_WIDTH_FRESLDPC 1 +-#define STB0899_FRESRS (0x01 << 6) +-#define STB0899_OFFST_FRESRS 6 +-#define STB0899_WIDTH_FRESRS 1 +-#define STB0899_FRESVIT (0x01 << 5) +-#define STB0899_OFFST_FRESVIT 5 +-#define STB0899_WIDTH_FRESVIT 1 +-#define STB0899_FRESMAS1_2 (0x01 << 4) +-#define STB0899_OFFST_FRESMAS1_2 4 +-#define STB0899_WIDTH_FRESMAS1_2 1 +-#define STB0899_FRESACS (0x01 << 3) +-#define STB0899_OFFST_FRESACS 3 +-#define STB0899_WIDTH_FRESACS 1 +-#define STB0899_FRESSYM (0x01 << 2) +-#define STB0899_OFFST_FRESSYM 2 +-#define STB0899_WIDTH_FRESSYM 1 +-#define STB0899_FRESMAS (0x01 << 1) +-#define STB0899_OFFST_FRESMAS 1 +-#define STB0899_WIDTH_FRESMAS 1 +-#define STB0899_FRESINT (0x01 << 0) +-#define STB0899_OFFST_FRESINIT 0 +-#define STB0899_WIDTH_FRESINIT 1 +- +-#define STB0899_TSTOUT 0xff12 +-#define STB0899_EN_SIGNATURE (0x01 << 7) +-#define STB0899_OFFST_EN_SIGNATURE 7 +-#define STB0899_WIDTH_EN_SIGNATURE 1 +-#define STB0899_BCLK_CLK (0x01 << 6) +-#define STB0899_OFFST_BCLK_CLK 6 +-#define STB0899_WIDTH_BCLK_CLK 1 +-#define STB0899_SGNL_OUT (0x01 << 5) +-#define STB0899_OFFST_SGNL_OUT 5 +-#define STB0899_WIDTH_SGNL_OUT 1 +-#define STB0899_TS (0x01 << 4) +-#define STB0899_OFFST_TS 4 +-#define STB0899_WIDTH_TS 1 +-#define STB0899_CTEST (0x01 << 0) +-#define STB0899_OFFST_CTEST 0 +-#define STB0899_WIDTH_CTEST 1 +- +-#define STB0899_TSTIN 0xff13 +-#define STB0899_TEST_IN (0x01 << 7) +-#define STB0899_OFFST_TEST_IN 7 +-#define STB0899_WIDTH_TEST_IN 1 +-#define STB0899_EN_ADC (0x01 << 6) +-#define STB0899_OFFST_EN_ADC 6 +-#define STB0899_WIDTH_ENADC 1 +-#define STB0899_SGN_ADC (0x01 << 5) +-#define STB0899_OFFST_SGN_ADC 5 +-#define STB0899_WIDTH_SGN_ADC 1 +-#define STB0899_BCLK_IN (0x01 << 4) +-#define STB0899_OFFST_BCLK_IN 4 +-#define STB0899_WIDTH_BCLK_IN 1 +-#define STB0899_JETONIN_MODE (0x01 << 3) +-#define STB0899_OFFST_JETONIN_MODE 3 +-#define STB0899_WIDTH_JETONIN_MODE 1 +-#define STB0899_BCLK_VALUE (0x01 << 2) +-#define STB0899_OFFST_BCLK_VALUE 2 +-#define STB0899_WIDTH_BCLK_VALUE 1 +-#define STB0899_SGNRST_T12 (0x01 << 1) +-#define STB0899_OFFST_SGNRST_T12 1 +-#define STB0899_WIDTH_SGNRST_T12 1 +-#define STB0899_LOWSP_ENAX (0x01 << 0) +-#define STB0899_OFFST_LOWSP_ENAX 0 +-#define STB0899_WIDTH_LOWSP_ENAX 1 +- +-#define STB0899_TSTSYS 0xff14 +-#define STB0899_TSTCHIP 0xff15 +-#define STB0899_TSTFREE 0xff16 +-#define STB0899_TSTI2C 0xff17 +-#define STB0899_BITSPEEDM 0xff1c +-#define STB0899_BITSPEEDL 0xff1d +-#define STB0899_TBUSBIT 0xff1e +-#define STB0899_TSTDIS 0xff24 +-#define STB0899_TSTDISRX 0xff25 +-#define STB0899_TSTJETON 0xff28 +-#define STB0899_TSTDCADJ 0xff40 +-#define STB0899_TSTAGC1 0xff41 +-#define STB0899_TSTAGC1N 0xff42 +-#define STB0899_TSTPOLYPH 0xff48 +-#define STB0899_TSTR 0xff49 +-#define STB0899_TSTAGC2 0xff4a +-#define STB0899_TSTCTL1 0xff4b +-#define STB0899_TSTCTL2 0xff4c +-#define STB0899_TSTCTL3 0xff4d +-#define STB0899_TSTDEMAP 0xff50 +-#define STB0899_TSTDEMAP2 0xff51 +-#define STB0899_TSTDEMMON 0xff52 +-#define STB0899_TSTRATE 0xff53 +-#define STB0899_TSTSELOUT 0xff54 +-#define STB0899_TSYNC 0xff55 +-#define STB0899_TSTERR 0xff56 +-#define STB0899_TSTRAM1 0xff58 +-#define STB0899_TSTVSELOUT 0xff59 +-#define STB0899_TSTFORCEIN 0xff5a +-#define STB0899_TSTRS1 0xff5c +-#define STB0899_TSTRS2 0xff5d +-#define STB0899_TSTRS3 0xff53 +- +-#define STB0899_INTBUFSTATUS 0xf200 +-#define STB0899_INTBUFCTRL 0xf201 +-#define STB0899_PCKLENUL 0xf55e +-#define STB0899_PCKLENLL 0xf55f +-#define STB0899_RSPCKLEN 0xf560 +- +-/* 2 registers */ +-#define STB0899_SYNCDCST 0xf60c +- +-/* DiSEqC */ +-#define STB0899_DISCNTRL1 0xf0a0 +-#define STB0899_TIMOFF (0x01 << 7) +-#define STB0899_OFFST_TIMOFF 7 +-#define STB0899_WIDTH_TIMOFF 1 +-#define STB0899_DISEQCRESET (0x01 << 6) +-#define STB0899_OFFST_DISEQCRESET 6 +-#define STB0899_WIDTH_DISEQCRESET 1 +-#define STB0899_TIMCMD (0x03 << 4) +-#define STB0899_OFFST_TIMCMD 4 +-#define STB0899_WIDTH_TIMCMD 2 +-#define STB0899_DISPRECHARGE (0x01 << 2) +-#define STB0899_OFFST_DISPRECHARGE 2 +-#define STB0899_WIDTH_DISPRECHARGE 1 +-#define STB0899_DISEQCMODE (0x03 << 0) +-#define STB0899_OFFST_DISEQCMODE 0 +-#define STB0899_WIDTH_DISEQCMODE 2 +- +-#define STB0899_DISCNTRL2 0xf0a1 +-#define STB0899_RECEIVER_ON (0x01 << 7) +-#define STB0899_OFFST_RECEIVER_ON 7 +-#define STB0899_WIDTH_RECEIVER_ON 1 +-#define STB0899_IGNO_SHORT_22K (0x01 << 6) +-#define STB0899_OFFST_IGNO_SHORT_22K 6 +-#define STB0899_WIDTH_IGNO_SHORT_22K 1 +-#define STB0899_ONECHIP_TRX (0x01 << 5) +-#define STB0899_OFFST_ONECHIP_TRX 5 +-#define STB0899_WIDTH_ONECHIP_TRX 1 +-#define STB0899_EXT_ENVELOP (0x01 << 4) +-#define STB0899_OFFST_EXT_ENVELOP 4 +-#define STB0899_WIDTH_EXT_ENVELOP 1 +-#define STB0899_PIN_SELECT (0x03 << 2) +-#define STB0899_OFFST_PIN_SELCT 2 +-#define STB0899_WIDTH_PIN_SELCT 2 +-#define STB0899_IRQ_RXEND (0x01 << 1) +-#define STB0899_OFFST_IRQ_RXEND 1 +-#define STB0899_WIDTH_IRQ_RXEND 1 +-#define STB0899_IRQ_4NBYTES (0x01 << 0) +-#define STB0899_OFFST_IRQ_4NBYTES 0 +-#define STB0899_WIDTH_IRQ_4NBYTES 1 +- +-#define STB0899_DISRX_ST0 0xf0a4 +-#define STB0899_RXEND (0x01 << 7) +-#define STB0899_OFFST_RXEND 7 +-#define STB0899_WIDTH_RXEND 1 +-#define STB0899_RXACTIVE (0x01 << 6) +-#define STB0899_OFFST_RXACTIVE 6 +-#define STB0899_WIDTH_RXACTIVE 1 +-#define STB0899_SHORT22K (0x01 << 5) +-#define STB0899_OFFST_SHORT22K 5 +-#define STB0899_WIDTH_SHORT22K 1 +-#define STB0899_CONTTONE (0x01 << 4) +-#define STB0899_OFFST_CONTTONE 4 +-#define STB0899_WIDTH_CONTONE 1 +-#define STB0899_4BFIFOREDY (0x01 << 3) +-#define STB0899_OFFST_4BFIFOREDY 3 +-#define STB0899_WIDTH_4BFIFOREDY 1 +-#define STB0899_FIFOEMPTY (0x01 << 2) +-#define STB0899_OFFST_FIFOEMPTY 2 +-#define STB0899_WIDTH_FIFOEMPTY 1 +-#define STB0899_ABORTTRX (0x01 << 0) +-#define STB0899_OFFST_ABORTTRX 0 +-#define STB0899_WIDTH_ABORTTRX 1 +- +-#define STB0899_DISRX_ST1 0xf0a5 +-#define STB0899_RXFAIL (0x01 << 7) +-#define STB0899_OFFST_RXFAIL 7 +-#define STB0899_WIDTH_RXFAIL 1 +-#define STB0899_FIFOPFAIL (0x01 << 6) +-#define STB0899_OFFST_FIFOPFAIL 6 +-#define STB0899_WIDTH_FIFOPFAIL 1 +-#define STB0899_RXNONBYTES (0x01 << 5) +-#define STB0899_OFFST_RXNONBYTES 5 +-#define STB0899_WIDTH_RXNONBYTES 1 +-#define STB0899_FIFOOVF (0x01 << 4) +-#define STB0899_OFFST_FIFOOVF 4 +-#define STB0899_WIDTH_FIFOOVF 1 +-#define STB0899_FIFOBYTENBR (0x0f << 0) +-#define STB0899_OFFST_FIFOBYTENBR 0 +-#define STB0899_WIDTH_FIFOBYTENBR 4 +- +-#define STB0899_DISPARITY 0xf0a6 +- +-#define STB0899_DISFIFO 0xf0a7 +- +-#define STB0899_DISSTATUS 0xf0a8 +-#define STB0899_FIFOFULL (0x01 << 6) +-#define STB0899_OFFST_FIFOFULL 6 +-#define STB0899_WIDTH_FIFOFULL 1 +-#define STB0899_TXIDLE (0x01 << 5) +-#define STB0899_OFFST_TXIDLE 5 +-#define STB0899_WIDTH_TXIDLE 1 +-#define STB0899_GAPBURST (0x01 << 4) +-#define STB0899_OFFST_GAPBURST 4 +-#define STB0899_WIDTH_GAPBURST 1 +-#define STB0899_TXFIFOBYTES (0x0f << 0) +-#define STB0899_OFFST_TXFIFOBYTES 0 +-#define STB0899_WIDTH_TXFIFOBYTES 4 +-#define STB0899_DISF22 0xf0a9 +- +-#define STB0899_DISF22RX 0xf0aa +- +-/* General Purpose */ +-#define STB0899_SYSREG 0xf101 +-#define STB0899_ACRPRESC 0xf110 +-#define STB0899_OFFST_RSVD2 7 +-#define STB0899_WIDTH_RSVD2 1 +-#define STB0899_OFFST_ACRPRESC 4 +-#define STB0899_WIDTH_ACRPRESC 3 +-#define STB0899_OFFST_RSVD1 3 +-#define STB0899_WIDTH_RSVD1 1 +-#define STB0899_OFFST_ACRPRESC2 0 +-#define STB0899_WIDTH_ACRPRESC2 3 +- +-#define STB0899_ACRDIV1 0xf111 +-#define STB0899_ACRDIV2 0xf112 +-#define STB0899_DACR1 0xf113 +-#define STB0899_DACR2 0xf114 +-#define STB0899_OUTCFG 0xf11c +-#define STB0899_MODECFG 0xf11d +-#define STB0899_NCOARSE 0xf1b3 +- +-#define STB0899_SYNTCTRL 0xf1b6 +-#define STB0899_STANDBY (0x01 << 7) +-#define STB0899_OFFST_STANDBY 7 +-#define STB0899_WIDTH_STANDBY 1 +-#define STB0899_BYPASSPLL (0x01 << 6) +-#define STB0899_OFFST_BYPASSPLL 6 +-#define STB0899_WIDTH_BYPASSPLL 1 +-#define STB0899_SEL1XRATIO (0x01 << 5) +-#define STB0899_OFFST_SEL1XRATIO 5 +-#define STB0899_WIDTH_SEL1XRATIO 1 +-#define STB0899_SELOSCI (0x01 << 1) +-#define STB0899_OFFST_SELOSCI 1 +-#define STB0899_WIDTH_SELOSCI 1 +- +-#define STB0899_FILTCTRL 0xf1b7 +-#define STB0899_SYSCTRL 0xf1b8 +- +-#define STB0899_STOPCLK1 0xf1c2 +-#define STB0899_STOP_CKINTBUF108 (0x01 << 7) +-#define STB0899_OFFST_STOP_CKINTBUF108 7 +-#define STB0899_WIDTH_STOP_CKINTBUF108 1 +-#define STB0899_STOP_CKINTBUF216 (0x01 << 6) +-#define STB0899_OFFST_STOP_CKINTBUF216 6 +-#define STB0899_WIDTH_STOP_CKINTBUF216 1 +-#define STB0899_STOP_CHK8PSK (0x01 << 5) +-#define STB0899_OFFST_STOP_CHK8PSK 5 +-#define STB0899_WIDTH_STOP_CHK8PSK 1 +-#define STB0899_STOP_CKFEC108 (0x01 << 4) +-#define STB0899_OFFST_STOP_CKFEC108 4 +-#define STB0899_WIDTH_STOP_CKFEC108 1 +-#define STB0899_STOP_CKFEC216 (0x01 << 3) +-#define STB0899_OFFST_STOP_CKFEC216 3 +-#define STB0899_WIDTH_STOP_CKFEC216 1 +-#define STB0899_STOP_CKCORE216 (0x01 << 2) +-#define STB0899_OFFST_STOP_CKCORE216 2 +-#define STB0899_WIDTH_STOP_CKCORE216 1 +-#define STB0899_STOP_CKADCI108 (0x01 << 1) +-#define STB0899_OFFST_STOP_CKADCI108 1 +-#define STB0899_WIDTH_STOP_CKADCI108 1 +-#define STB0899_STOP_INVCKADCI108 (0x01 << 0) +-#define STB0899_OFFST_STOP_INVCKADCI108 0 +-#define STB0899_WIDTH_STOP_INVCKADCI108 1 +- +-#define STB0899_STOPCLK2 0xf1c3 +-#define STB0899_STOP_CKS2DMD108 (0x01 << 2) +-#define STB0899_OFFST_STOP_CKS2DMD108 2 +-#define STB0899_WIDTH_STOP_CKS2DMD108 1 +-#define STB0899_STOP_CKPKDLIN108 (0x01 << 1) +-#define STB0899_OFFST_STOP_CKPKDLIN108 1 +-#define STB0899_WIDTH_STOP_CKPKDLIN108 1 +-#define STB0899_STOP_CKPKDLIN216 (0x01 << 0) +-#define STB0899_OFFST_STOP_CKPKDLIN216 0 +-#define STB0899_WIDTH_STOP_CKPKDLIN216 1 +- +-#define STB0899_TSTTNR1 0xf1e0 +-#define STB0899_BYPASS_ADC (0x01 << 7) +-#define STB0899_OFFST_BYPASS_ADC 7 +-#define STB0899_WIDTH_BYPASS_ADC 1 +-#define STB0899_INVADCICKOUT (0x01 << 6) +-#define STB0899_OFFST_INVADCICKOUT 6 +-#define STB0899_WIDTH_INVADCICKOUT 1 +-#define STB0899_ADCTEST_VOLTAGE (0x03 << 4) +-#define STB0899_OFFST_ADCTEST_VOLTAGE 4 +-#define STB0899_WIDTH_ADCTEST_VOLTAGE 1 +-#define STB0899_ADC_RESET (0x01 << 3) +-#define STB0899_OFFST_ADC_RESET 3 +-#define STB0899_WIDTH_ADC_RESET 1 +-#define STB0899_TSTTNR1_2 (0x01 << 2) +-#define STB0899_OFFST_TSTTNR1_2 2 +-#define STB0899_WIDTH_TSTTNR1_2 1 +-#define STB0899_ADCPON (0x01 << 1) +-#define STB0899_OFFST_ADCPON 1 +-#define STB0899_WIDTH_ADCPON 1 +-#define STB0899_ADCIN_MODE (0x01 << 0) +-#define STB0899_OFFST_ADCIN_MODE 0 +-#define STB0899_WIDTH_ADCIN_MODE 1 +- +-#define STB0899_TSTTNR2 0xf1e1 +-#define STB0899_TSTTNR2_7 (0x01 << 7) +-#define STB0899_OFFST_TSTTNR2_7 7 +-#define STB0899_WIDTH_TSTTNR2_7 1 +-#define STB0899_NOT_DISRX_WIRED (0x01 << 6) +-#define STB0899_OFFST_NOT_DISRX_WIRED 6 +-#define STB0899_WIDTH_NOT_DISRX_WIRED 1 +-#define STB0899_DISEQC_DCURRENT (0x01 << 5) +-#define STB0899_OFFST_DISEQC_DCURRENT 5 +-#define STB0899_WIDTH_DISEQC_DCURRENT 1 +-#define STB0899_DISEQC_ZCURRENT (0x01 << 4) +-#define STB0899_OFFST_DISEQC_ZCURRENT 4 +-#define STB0899_WIDTH_DISEQC_ZCURRENT 1 +-#define STB0899_DISEQC_SINC_SOURCE (0x03 << 2) +-#define STB0899_OFFST_DISEQC_SINC_SOURCE 2 +-#define STB0899_WIDTH_DISEQC_SINC_SOURCE 2 +-#define STB0899_SELIQSRC (0x03 << 0) +-#define STB0899_OFFST_SELIQSRC 0 +-#define STB0899_WIDTH_SELIQSRC 2 +- +-#define STB0899_TSTTNR3 0xf1e2 +- +-#define STB0899_I2CCFG 0xf129 +-#define STB0899_I2CCFGRSVD (0x0f << 4) +-#define STB0899_OFFST_I2CCFGRSVD 4 +-#define STB0899_WIDTH_I2CCFGRSVD 4 +-#define STB0899_I2CFASTMODE (0x01 << 3) +-#define STB0899_OFFST_I2CFASTMODE 3 +-#define STB0899_WIDTH_I2CFASTMODE 1 +-#define STB0899_STATUSWR (0x01 << 2) +-#define STB0899_OFFST_STATUSWR 2 +-#define STB0899_WIDTH_STATUSWR 1 +-#define STB0899_I2CADDRINC (0x03 << 0) +-#define STB0899_OFFST_I2CADDRINC 0 +-#define STB0899_WIDTH_I2CADDRINC 2 +- +-#define STB0899_I2CRPT 0xf12a +-#define STB0899_I2CTON (0x01 << 7) +-#define STB0899_OFFST_I2CTON 7 +-#define STB0899_WIDTH_I2CTON 1 +-#define STB0899_ENARPTLEVEL (0x01 << 6) +-#define STB0899_OFFST_ENARPTLEVEL 6 +-#define STB0899_WIDTH_ENARPTLEVEL 2 +-#define STB0899_SCLTDELAY (0x01 << 3) +-#define STB0899_OFFST_SCLTDELAY 3 +-#define STB0899_WIDTH_SCLTDELAY 1 +-#define STB0899_STOPENA (0x01 << 2) +-#define STB0899_OFFST_STOPENA 2 +-#define STB0899_WIDTH_STOPENA 1 +-#define STB0899_STOPSDAT2SDA (0x01 << 1) +-#define STB0899_OFFST_STOPSDAT2SDA 1 +-#define STB0899_WIDTH_STOPSDAT2SDA 1 +- +-#define STB0899_IOPVALUE8 0xf136 +-#define STB0899_IOPVALUE7 0xf137 +-#define STB0899_IOPVALUE6 0xf138 +-#define STB0899_IOPVALUE5 0xf139 +-#define STB0899_IOPVALUE4 0xf13a +-#define STB0899_IOPVALUE3 0xf13b +-#define STB0899_IOPVALUE2 0xf13c +-#define STB0899_IOPVALUE1 0xf13d +-#define STB0899_IOPVALUE0 0xf13e +- +-#define STB0899_GPIO00CFG 0xf140 +- +-#define STB0899_GPIO01CFG 0xf141 +-#define STB0899_GPIO02CFG 0xf142 +-#define STB0899_GPIO03CFG 0xf143 +-#define STB0899_GPIO04CFG 0xf144 +-#define STB0899_GPIO05CFG 0xf145 +-#define STB0899_GPIO06CFG 0xf146 +-#define STB0899_GPIO07CFG 0xf147 +-#define STB0899_GPIO08CFG 0xf148 +-#define STB0899_GPIO09CFG 0xf149 +-#define STB0899_GPIO10CFG 0xf14a +-#define STB0899_GPIO11CFG 0xf14b +-#define STB0899_GPIO12CFG 0xf14c +-#define STB0899_GPIO13CFG 0xf14d +-#define STB0899_GPIO14CFG 0xf14e +-#define STB0899_GPIO15CFG 0xf14f +-#define STB0899_GPIO16CFG 0xf150 +-#define STB0899_GPIO17CFG 0xf151 +-#define STB0899_GPIO18CFG 0xf152 +-#define STB0899_GPIO19CFG 0xf153 +-#define STB0899_GPIO20CFG 0xf154 +- +-#define STB0899_SDATCFG 0xf155 +-#define STB0899_SCLTCFG 0xf156 +-#define STB0899_AGCRFCFG 0xf157 +-#define STB0899_GPIO22 0xf158 /* AGCBB2CFG */ +-#define STB0899_GPIO21 0xf159 /* AGCBB1CFG */ +-#define STB0899_DIRCLKCFG 0xf15a +-#define STB0899_CLKOUT27CFG 0xf15b +-#define STB0899_STDBYCFG 0xf15c +-#define STB0899_CS0CFG 0xf15d +-#define STB0899_CS1CFG 0xf15e +-#define STB0899_DISEQCOCFG 0xf15f +- +-#define STB0899_GPIO32CFG 0xf160 +-#define STB0899_GPIO33CFG 0xf161 +-#define STB0899_GPIO34CFG 0xf162 +-#define STB0899_GPIO35CFG 0xf163 +-#define STB0899_GPIO36CFG 0xf164 +-#define STB0899_GPIO37CFG 0xf165 +-#define STB0899_GPIO38CFG 0xf166 +-#define STB0899_GPIO39CFG 0xf167 +- +-#define STB0899_IRQSTATUS_3 0xf120 +-#define STB0899_IRQSTATUS_2 0xf121 +-#define STB0899_IRQSTATUS_1 0xf122 +-#define STB0899_IRQSTATUS_0 0xf123 +- +-#define STB0899_IRQMSK_3 0xf124 +-#define STB0899_IRQMSK_2 0xf125 +-#define STB0899_IRQMSK_1 0xf126 +-#define STB0899_IRQMSK_0 0xf127 +- +-#define STB0899_IRQCFG 0xf128 +- +-#define STB0899_GHOSTREG 0xf000 +- +-#define STB0899_S2DEMOD 0xf3fc +-#define STB0899_S2FEC 0xfafc +- +- +-#endif +diff --git a/drivers/media/dvb/frontends/stb6000.c b/drivers/media/dvb/frontends/stb6000.c +deleted file mode 100644 +index a0c3c52..0000000 +--- a/drivers/media/dvb/frontends/stb6000.c ++++ /dev/null +@@ -1,256 +0,0 @@ +- /* +- Driver for ST STB6000 DVBS Silicon tuner +- +- Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#include +-#include +-#include +-#include +- +-#include "stb6000.h" +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "stb6000: " args); \ +- } while (0) +- +-struct stb6000_priv { +- /* i2c details */ +- int i2c_address; +- struct i2c_adapter *i2c; +- u32 frequency; +-}; +- +-static int stb6000_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int stb6000_sleep(struct dvb_frontend *fe) +-{ +- struct stb6000_priv *priv = fe->tuner_priv; +- int ret; +- u8 buf[] = { 10, 0 }; +- struct i2c_msg msg = { +- .addr = priv->i2c_address, +- .flags = 0, +- .buf = buf, +- .len = 2 +- }; +- +- dprintk("%s:\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = i2c_transfer(priv->i2c, &msg, 1); +- if (ret != 1) +- dprintk("%s: i2c error\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return (ret == 1) ? 0 : ret; +-} +- +-static int stb6000_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stb6000_priv *priv = fe->tuner_priv; +- unsigned int n, m; +- int ret; +- u32 freq_mhz; +- int bandwidth; +- u8 buf[12]; +- struct i2c_msg msg = { +- .addr = priv->i2c_address, +- .flags = 0, +- .buf = buf, +- .len = 12 +- }; +- +- dprintk("%s:\n", __func__); +- +- freq_mhz = p->frequency / 1000; +- bandwidth = p->symbol_rate / 1000000; +- +- if (bandwidth > 31) +- bandwidth = 31; +- +- if ((freq_mhz > 949) && (freq_mhz < 2151)) { +- buf[0] = 0x01; +- buf[1] = 0xac; +- if (freq_mhz < 1950) +- buf[1] = 0xaa; +- if (freq_mhz < 1800) +- buf[1] = 0xa8; +- if (freq_mhz < 1650) +- buf[1] = 0xa6; +- if (freq_mhz < 1530) +- buf[1] = 0xa5; +- if (freq_mhz < 1470) +- buf[1] = 0xa4; +- if (freq_mhz < 1370) +- buf[1] = 0xa2; +- if (freq_mhz < 1300) +- buf[1] = 0xa1; +- if (freq_mhz < 1200) +- buf[1] = 0xa0; +- if (freq_mhz < 1075) +- buf[1] = 0xbc; +- if (freq_mhz < 1000) +- buf[1] = 0xba; +- if (freq_mhz < 1075) { +- n = freq_mhz / 8; /* vco=lo*4 */ +- m = 2; +- } else { +- n = freq_mhz / 16; /* vco=lo*2 */ +- m = 1; +- } +- buf[2] = n >> 1; +- buf[3] = (unsigned char)(((n & 1) << 7) | +- (m * freq_mhz - n * 16) | 0x60); +- buf[4] = 0x04; +- buf[5] = 0x0e; +- +- buf[6] = (unsigned char)(bandwidth); +- +- buf[7] = 0xd8; +- buf[8] = 0xd0; +- buf[9] = 0x50; +- buf[10] = 0xeb; +- buf[11] = 0x4f; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = i2c_transfer(priv->i2c, &msg, 1); +- if (ret != 1) +- dprintk("%s: i2c error\n", __func__); +- +- udelay(10); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- buf[0] = 0x07; +- buf[1] = 0xdf; +- buf[2] = 0xd0; +- buf[3] = 0x50; +- buf[4] = 0xfb; +- msg.len = 5; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = i2c_transfer(priv->i2c, &msg, 1); +- if (ret != 1) +- dprintk("%s: i2c error\n", __func__); +- +- udelay(10); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- priv->frequency = freq_mhz * 1000; +- +- return (ret == 1) ? 0 : ret; +- } +- return -1; +-} +- +-static int stb6000_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct stb6000_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static struct dvb_tuner_ops stb6000_tuner_ops = { +- .info = { +- .name = "ST STB6000", +- .frequency_min = 950000, +- .frequency_max = 2150000 +- }, +- .release = stb6000_release, +- .sleep = stb6000_sleep, +- .set_params = stb6000_set_params, +- .get_frequency = stb6000_get_frequency, +-}; +- +-struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, +- struct i2c_adapter *i2c) +-{ +- struct stb6000_priv *priv = NULL; +- u8 b0[] = { 0 }; +- u8 b1[] = { 0, 0 }; +- struct i2c_msg msg[2] = { +- { +- .addr = addr, +- .flags = 0, +- .buf = b0, +- .len = 0 +- }, { +- .addr = addr, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 2 +- } +- }; +- int ret; +- +- dprintk("%s:\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- /* is some i2c device here ? */ +- ret = i2c_transfer(i2c, msg, 2); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret != 2) +- return NULL; +- +- priv = kzalloc(sizeof(struct stb6000_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->i2c_address = addr; +- priv->i2c = i2c; +- +- memcpy(&fe->ops.tuner_ops, &stb6000_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- +- return fe; +-} +-EXPORT_SYMBOL(stb6000_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("DVB STB6000 driver"); +-MODULE_AUTHOR("Igor M. Liplianin "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stb6000.h b/drivers/media/dvb/frontends/stb6000.h +deleted file mode 100644 +index 7be479c..0000000 +--- a/drivers/media/dvb/frontends/stb6000.h ++++ /dev/null +@@ -1,51 +0,0 @@ +- /* +- Driver for ST stb6000 DVBS Silicon tuner +- +- Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#ifndef __DVB_STB6000_H__ +-#define __DVB_STB6000_H__ +- +-#include +-#include "dvb_frontend.h" +- +-/** +- * Attach a stb6000 tuner to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param addr i2c address of the tuner. +- * @param i2c i2c adapter to use. +- * @return FE pointer on success, NULL on failure. +- */ +-#if defined(CONFIG_DVB_STB6000) || (defined(CONFIG_DVB_STB6000_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, +- int addr, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_STB6000 */ +- +-#endif /* __DVB_STB6000_H__ */ +diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c +deleted file mode 100644 +index def88ab..0000000 +--- a/drivers/media/dvb/frontends/stb6100.c ++++ /dev/null +@@ -1,612 +0,0 @@ +-/* +- STB6100 Silicon Tuner +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "stb6100.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +- +- +-#define FE_ERROR 0 +-#define FE_NOTICE 1 +-#define FE_INFO 2 +-#define FE_DEBUG 3 +- +-#define dprintk(x, y, z, format, arg...) do { \ +- if (z) { \ +- if ((x > FE_ERROR) && (x > y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > FE_NOTICE) && (x > y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > FE_INFO) && (x > y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((x > FE_DEBUG) && (x > y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (x > y) \ +- printk(format, ##arg); \ +- } \ +-} while (0) +- +-struct stb6100_lkup { +- u32 val_low; +- u32 val_high; +- u8 reg; +-}; +- +-static int stb6100_release(struct dvb_frontend *fe); +- +-static const struct stb6100_lkup lkup[] = { +- { 0, 950000, 0x0a }, +- { 950000, 1000000, 0x0a }, +- { 1000000, 1075000, 0x0c }, +- { 1075000, 1200000, 0x00 }, +- { 1200000, 1300000, 0x01 }, +- { 1300000, 1370000, 0x02 }, +- { 1370000, 1470000, 0x04 }, +- { 1470000, 1530000, 0x05 }, +- { 1530000, 1650000, 0x06 }, +- { 1650000, 1800000, 0x08 }, +- { 1800000, 1950000, 0x0a }, +- { 1950000, 2150000, 0x0c }, +- { 2150000, 9999999, 0x0c }, +- { 0, 0, 0x00 } +-}; +- +-/* Register names for easy debugging. */ +-static const char *stb6100_regnames[] = { +- [STB6100_LD] = "LD", +- [STB6100_VCO] = "VCO", +- [STB6100_NI] = "NI", +- [STB6100_NF_LSB] = "NF", +- [STB6100_K] = "K", +- [STB6100_G] = "G", +- [STB6100_F] = "F", +- [STB6100_DLB] = "DLB", +- [STB6100_TEST1] = "TEST1", +- [STB6100_FCCK] = "FCCK", +- [STB6100_LPEN] = "LPEN", +- [STB6100_TEST3] = "TEST3", +-}; +- +-/* Template for normalisation, i.e. setting unused or undocumented +- * bits as required according to the documentation. +- */ +-struct stb6100_regmask { +- u8 mask; +- u8 set; +-}; +- +-static const struct stb6100_regmask stb6100_template[] = { +- [STB6100_LD] = { 0xff, 0x00 }, +- [STB6100_VCO] = { 0xff, 0x00 }, +- [STB6100_NI] = { 0xff, 0x00 }, +- [STB6100_NF_LSB] = { 0xff, 0x00 }, +- [STB6100_K] = { 0xc7, 0x38 }, +- [STB6100_G] = { 0xef, 0x10 }, +- [STB6100_F] = { 0x1f, 0xc0 }, +- [STB6100_DLB] = { 0x38, 0xc4 }, +- [STB6100_TEST1] = { 0x00, 0x8f }, +- [STB6100_FCCK] = { 0x40, 0x0d }, +- [STB6100_LPEN] = { 0xf0, 0x0b }, +- [STB6100_TEST3] = { 0x00, 0xde }, +-}; +- +-/* +- * Currently unused. Some boards might need it in the future +- */ +-static inline void stb6100_normalise_regs(u8 regs[]) +-{ +- int i; +- +- for (i = 0; i < STB6100_NUMREGS; i++) +- regs[i] = (regs[i] & stb6100_template[i].mask) | stb6100_template[i].set; +-} +- +-static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) +-{ +- int rc; +- struct i2c_msg msg = { +- .addr = state->config->tuner_address, +- .flags = I2C_M_RD, +- .buf = regs, +- .len = STB6100_NUMREGS +- }; +- +- rc = i2c_transfer(state->i2c, &msg, 1); +- if (unlikely(rc != 1)) { +- dprintk(verbose, FE_ERROR, 1, "Read (0x%x) err, rc=[%d]", +- state->config->tuner_address, rc); +- +- return -EREMOTEIO; +- } +- if (unlikely(verbose > FE_DEBUG)) { +- int i; +- +- dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); +- for (i = 0; i < STB6100_NUMREGS; i++) +- dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[i], regs[i]); +- } +- return 0; +-} +- +-static int stb6100_read_reg(struct stb6100_state *state, u8 reg) +-{ +- u8 regs[STB6100_NUMREGS]; +- int rc; +- +- struct i2c_msg msg = { +- .addr = state->config->tuner_address + reg, +- .flags = I2C_M_RD, +- .buf = regs, +- .len = 1 +- }; +- +- rc = i2c_transfer(state->i2c, &msg, 1); +- +- if (unlikely(reg >= STB6100_NUMREGS)) { +- dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); +- return -EINVAL; +- } +- if (unlikely(verbose > FE_DEBUG)) { +- dprintk(verbose, FE_DEBUG, 1, " Read from 0x%02x", state->config->tuner_address); +- dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[reg], regs[0]); +- } +- +- return (unsigned int)regs[0]; +-} +- +-static int stb6100_write_reg_range(struct stb6100_state *state, u8 buf[], int start, int len) +-{ +- int rc; +- u8 cmdbuf[len + 1]; +- struct i2c_msg msg = { +- .addr = state->config->tuner_address, +- .flags = 0, +- .buf = cmdbuf, +- .len = len + 1 +- }; +- +- if (unlikely(start < 1 || start + len > STB6100_NUMREGS)) { +- dprintk(verbose, FE_ERROR, 1, "Invalid register range %d:%d", +- start, len); +- return -EINVAL; +- } +- memcpy(&cmdbuf[1], buf, len); +- cmdbuf[0] = start; +- +- if (unlikely(verbose > FE_DEBUG)) { +- int i; +- +- dprintk(verbose, FE_DEBUG, 1, " Write @ 0x%02x: [%d:%d]", state->config->tuner_address, start, len); +- for (i = 0; i < len; i++) +- dprintk(verbose, FE_DEBUG, 1, " %s: 0x%02x", stb6100_regnames[start + i], buf[i]); +- } +- rc = i2c_transfer(state->i2c, &msg, 1); +- if (unlikely(rc != 1)) { +- dprintk(verbose, FE_ERROR, 1, "(0x%x) write err [%d:%d], rc=[%d]", +- (unsigned int)state->config->tuner_address, start, len, rc); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int stb6100_write_reg(struct stb6100_state *state, u8 reg, u8 data) +-{ +- if (unlikely(reg >= STB6100_NUMREGS)) { +- dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); +- return -EREMOTEIO; +- } +- data = (data & stb6100_template[reg].mask) | stb6100_template[reg].set; +- return stb6100_write_reg_range(state, &data, reg, 1); +-} +- +- +-static int stb6100_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- int rc; +- struct stb6100_state *state = fe->tuner_priv; +- +- rc = stb6100_read_reg(state, STB6100_LD); +- if (rc < 0) { +- dprintk(verbose, FE_ERROR, 1, "%s failed", __func__); +- return rc; +- } +- return (rc & STB6100_LD_LOCK) ? TUNER_STATUS_LOCKED : 0; +-} +- +-static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- int rc; +- u8 f; +- struct stb6100_state *state = fe->tuner_priv; +- +- rc = stb6100_read_reg(state, STB6100_F); +- if (rc < 0) +- return rc; +- f = rc & STB6100_F_F; +- +- state->status.bandwidth = (f + 5) * 2000; /* x2 for ZIF */ +- +- *bandwidth = state->bandwidth = state->status.bandwidth * 1000; +- dprintk(verbose, FE_DEBUG, 1, "bandwidth = %u Hz", state->bandwidth); +- return 0; +-} +- +-static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +-{ +- u32 tmp; +- int rc; +- struct stb6100_state *state = fe->tuner_priv; +- +- dprintk(verbose, FE_DEBUG, 1, "set bandwidth to %u Hz", bandwidth); +- +- bandwidth /= 2; /* ZIF */ +- +- if (bandwidth >= 36000000) /* F[4:0] BW/2 max =31+5=36 mhz for F=31 */ +- tmp = 31; +- else if (bandwidth <= 5000000) /* bw/2 min = 5Mhz for F=0 */ +- tmp = 0; +- else /* if 5 < bw/2 < 36 */ +- tmp = (bandwidth + 500000) / 1000000 - 5; +- +- /* Turn on LPF bandwidth setting clock control, +- * set bandwidth, wait 10ms, turn off. +- */ +- rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d | STB6100_FCCK_FCCK); +- if (rc < 0) +- return rc; +- rc = stb6100_write_reg(state, STB6100_F, 0xc0 | tmp); +- if (rc < 0) +- return rc; +- +- msleep(5); /* This is dangerous as another (related) thread may start */ +- +- rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); +- if (rc < 0) +- return rc; +- +- msleep(10); /* This is dangerous as another (related) thread may start */ +- +- return 0; +-} +- +-static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- int rc; +- u32 nint, nfrac, fvco; +- int psd2, odiv; +- struct stb6100_state *state = fe->tuner_priv; +- u8 regs[STB6100_NUMREGS]; +- +- rc = stb6100_read_regs(state, regs); +- if (rc < 0) +- return rc; +- +- odiv = (regs[STB6100_VCO] & STB6100_VCO_ODIV) >> STB6100_VCO_ODIV_SHIFT; +- psd2 = (regs[STB6100_K] & STB6100_K_PSD2) >> STB6100_K_PSD2_SHIFT; +- nint = regs[STB6100_NI]; +- nfrac = ((regs[STB6100_K] & STB6100_K_NF_MSB) << 8) | regs[STB6100_NF_LSB]; +- fvco = (nfrac * state->reference >> (9 - psd2)) + (nint * state->reference << psd2); +- *frequency = state->frequency = fvco >> (odiv + 1); +- +- dprintk(verbose, FE_DEBUG, 1, +- "frequency = %u kHz, odiv = %u, psd2 = %u, fxtal = %u kHz, fvco = %u kHz, N(I) = %u, N(F) = %u", +- state->frequency, odiv, psd2, state->reference, fvco, nint, nfrac); +- return 0; +-} +- +- +-static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +-{ +- int rc; +- const struct stb6100_lkup *ptr; +- struct stb6100_state *state = fe->tuner_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- u32 srate = 0, fvco, nint, nfrac; +- u8 regs[STB6100_NUMREGS]; +- u8 g, psd2, odiv; +- +- dprintk(verbose, FE_DEBUG, 1, "Version 2010-8-14 13:51"); +- +- if (fe->ops.get_frontend) { +- dprintk(verbose, FE_DEBUG, 1, "Get frontend parameters"); +- fe->ops.get_frontend(fe); +- } +- srate = p->symbol_rate; +- +- /* Set up tuner cleanly, LPF calibration on */ +- rc = stb6100_write_reg(state, STB6100_FCCK, 0x4d | STB6100_FCCK_FCCK); +- if (rc < 0) +- return rc; /* allow LPF calibration */ +- +- /* PLL Loop disabled, bias on, VCO on, synth on */ +- regs[STB6100_LPEN] = 0xeb; +- rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); +- if (rc < 0) +- return rc; +- +- /* Program the registers with their data values */ +- +- /* VCO divide ratio (LO divide ratio, VCO prescaler enable). */ +- if (frequency <= 1075000) +- odiv = 1; +- else +- odiv = 0; +- +- /* VCO enabled, search clock off as per LL3.7, 3.4.1 */ +- regs[STB6100_VCO] = 0xe0 | (odiv << STB6100_VCO_ODIV_SHIFT); +- +- /* OSM */ +- for (ptr = lkup; +- (ptr->val_high != 0) && !CHKRANGE(frequency, ptr->val_low, ptr->val_high); +- ptr++); +- +- if (ptr->val_high == 0) { +- printk(KERN_ERR "%s: frequency out of range: %u kHz\n", __func__, frequency); +- return -EINVAL; +- } +- regs[STB6100_VCO] = (regs[STB6100_VCO] & ~STB6100_VCO_OSM) | ptr->reg; +- rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); +- if (rc < 0) +- return rc; +- +- if ((frequency > 1075000) && (frequency <= 1325000)) +- psd2 = 0; +- else +- psd2 = 1; +- /* F(VCO) = F(LO) * (ODIV == 0 ? 2 : 4) */ +- fvco = frequency << (1 + odiv); +- /* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */ +- nint = fvco / (state->reference << psd2); +- /* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */ +- nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2)) +- << (9 - psd2), state->reference); +- +- /* NI */ +- regs[STB6100_NI] = nint; +- rc = stb6100_write_reg(state, STB6100_NI, regs[STB6100_NI]); +- if (rc < 0) +- return rc; +- +- /* NF */ +- regs[STB6100_NF_LSB] = nfrac; +- rc = stb6100_write_reg(state, STB6100_NF_LSB, regs[STB6100_NF_LSB]); +- if (rc < 0) +- return rc; +- +- /* K */ +- regs[STB6100_K] = (0x38 & ~STB6100_K_PSD2) | (psd2 << STB6100_K_PSD2_SHIFT); +- regs[STB6100_K] = (regs[STB6100_K] & ~STB6100_K_NF_MSB) | ((nfrac >> 8) & STB6100_K_NF_MSB); +- rc = stb6100_write_reg(state, STB6100_K, regs[STB6100_K]); +- if (rc < 0) +- return rc; +- +- /* G Baseband gain. */ +- if (srate >= 15000000) +- g = 9; /* +4 dB */ +- else if (srate >= 5000000) +- g = 11; /* +8 dB */ +- else +- g = 14; /* +14 dB */ +- +- regs[STB6100_G] = (0x10 & ~STB6100_G_G) | g; +- regs[STB6100_G] &= ~STB6100_G_GCT; /* mask GCT */ +- regs[STB6100_G] |= (1 << 5); /* 2Vp-p Mode */ +- rc = stb6100_write_reg(state, STB6100_G, regs[STB6100_G]); +- if (rc < 0) +- return rc; +- +- /* F we don't write as it is set up in BW set */ +- +- /* DLB set DC servo loop BW to 160Hz (LLA 3.8 / 2.1) */ +- regs[STB6100_DLB] = 0xcc; +- rc = stb6100_write_reg(state, STB6100_DLB, regs[STB6100_DLB]); +- if (rc < 0) +- return rc; +- +- dprintk(verbose, FE_DEBUG, 1, +- "frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u", +- frequency, srate, (unsigned int)g, (unsigned int)odiv, +- (unsigned int)psd2, state->reference, +- ptr->reg, fvco, nint, nfrac); +- +- /* Set up the test registers */ +- regs[STB6100_TEST1] = 0x8f; +- rc = stb6100_write_reg(state, STB6100_TEST1, regs[STB6100_TEST1]); +- if (rc < 0) +- return rc; +- regs[STB6100_TEST3] = 0xde; +- rc = stb6100_write_reg(state, STB6100_TEST3, regs[STB6100_TEST3]); +- if (rc < 0) +- return rc; +- +- /* Bring up tuner according to LLA 3.7 3.4.1, step 2 */ +- regs[STB6100_LPEN] = 0xfb; /* PLL Loop enabled, bias on, VCO on, synth on */ +- rc = stb6100_write_reg(state, STB6100_LPEN, regs[STB6100_LPEN]); +- if (rc < 0) +- return rc; +- +- msleep(2); +- +- /* Bring up tuner according to LLA 3.7 3.4.1, step 3 */ +- regs[STB6100_VCO] &= ~STB6100_VCO_OCK; /* VCO fast search */ +- rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); +- if (rc < 0) +- return rc; +- +- msleep(10); /* This is dangerous as another (related) thread may start */ /* wait for LO to lock */ +- +- regs[STB6100_VCO] &= ~STB6100_VCO_OSCH; /* vco search disabled */ +- regs[STB6100_VCO] |= STB6100_VCO_OCK; /* search clock off */ +- rc = stb6100_write_reg(state, STB6100_VCO, regs[STB6100_VCO]); +- if (rc < 0) +- return rc; +- +- rc = stb6100_write_reg(state, STB6100_FCCK, 0x0d); +- if (rc < 0) +- return rc; /* Stop LPF calibration */ +- +- msleep(10); /* This is dangerous as another (related) thread may start */ +- /* wait for stabilisation, (should not be necessary) */ +- return 0; +-} +- +-static int stb6100_sleep(struct dvb_frontend *fe) +-{ +- /* TODO: power down */ +- return 0; +-} +- +-static int stb6100_init(struct dvb_frontend *fe) +-{ +- struct stb6100_state *state = fe->tuner_priv; +- struct tuner_state *status = &state->status; +- +- status->tunerstep = 125000; +- status->ifreq = 0; +- status->refclock = 27000000; /* Hz */ +- status->iqsense = 1; +- status->bandwidth = 36000; /* kHz */ +- state->bandwidth = status->bandwidth * 1000; /* Hz */ +- state->reference = status->refclock / 1000; /* kHz */ +- +- /* Set default bandwidth. Modified, PN 13-May-10 */ +- return 0; +-} +- +-static int stb6100_get_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *state) +-{ +- switch (param) { +- case DVBFE_TUNER_FREQUENCY: +- stb6100_get_frequency(fe, &state->frequency); +- break; +- case DVBFE_TUNER_TUNERSTEP: +- break; +- case DVBFE_TUNER_IFFREQ: +- break; +- case DVBFE_TUNER_BANDWIDTH: +- stb6100_get_bandwidth(fe, &state->bandwidth); +- break; +- case DVBFE_TUNER_REFCLOCK: +- break; +- default: +- break; +- } +- +- return 0; +-} +- +-static int stb6100_set_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *state) +-{ +- struct stb6100_state *tstate = fe->tuner_priv; +- +- switch (param) { +- case DVBFE_TUNER_FREQUENCY: +- stb6100_set_frequency(fe, state->frequency); +- tstate->frequency = state->frequency; +- break; +- case DVBFE_TUNER_TUNERSTEP: +- break; +- case DVBFE_TUNER_IFFREQ: +- break; +- case DVBFE_TUNER_BANDWIDTH: +- stb6100_set_bandwidth(fe, state->bandwidth); +- tstate->bandwidth = state->bandwidth; +- break; +- case DVBFE_TUNER_REFCLOCK: +- break; +- default: +- break; +- } +- +- return 0; +-} +- +-static struct dvb_tuner_ops stb6100_ops = { +- .info = { +- .name = "STB6100 Silicon Tuner", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 0, +- }, +- +- .init = stb6100_init, +- .sleep = stb6100_sleep, +- .get_status = stb6100_get_status, +- .get_state = stb6100_get_state, +- .set_state = stb6100_set_state, +- .release = stb6100_release +-}; +- +-struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, +- const struct stb6100_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stb6100_state *state = NULL; +- +- state = kzalloc(sizeof (struct stb6100_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->config = config; +- state->i2c = i2c; +- state->frontend = fe; +- state->reference = config->refclock / 1000; /* kHz */ +- fe->tuner_priv = state; +- fe->ops.tuner_ops = stb6100_ops; +- +- printk("%s: Attaching STB6100 \n", __func__); +- return fe; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static int stb6100_release(struct dvb_frontend *fe) +-{ +- struct stb6100_state *state = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(state); +- +- return 0; +-} +- +-EXPORT_SYMBOL(stb6100_attach); +-MODULE_PARM_DESC(verbose, "Set Verbosity level"); +- +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_DESCRIPTION("STB6100 Silicon tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stb6100.h b/drivers/media/dvb/frontends/stb6100.h +deleted file mode 100644 +index 2ab0966..0000000 +--- a/drivers/media/dvb/frontends/stb6100.h ++++ /dev/null +@@ -1,115 +0,0 @@ +-/* +- STB6100 Silicon Tuner +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STB_6100_REG_H +-#define __STB_6100_REG_H +- +-#include +-#include "dvb_frontend.h" +- +-#define STB6100_LD 0x00 +-#define STB6100_LD_LOCK (1 << 0) +- +-#define STB6100_VCO 0x01 +-#define STB6100_VCO_OSCH (0x01 << 7) +-#define STB6100_VCO_OSCH_SHIFT 7 +-#define STB6100_VCO_OCK (0x03 << 5) +-#define STB6100_VCO_OCK_SHIFT 5 +-#define STB6100_VCO_ODIV (0x01 << 4) +-#define STB6100_VCO_ODIV_SHIFT 4 +-#define STB6100_VCO_OSM (0x0f << 0) +- +-#define STB6100_NI 0x02 +-#define STB6100_NF_LSB 0x03 +- +-#define STB6100_K 0x04 +-#define STB6100_K_PSD2 (0x01 << 2) +-#define STB6100_K_PSD2_SHIFT 2 +-#define STB6100_K_NF_MSB (0x03 << 0) +- +-#define STB6100_G 0x05 +-#define STB6100_G_G (0x0f << 0) +-#define STB6100_G_GCT (0x07 << 5) +- +-#define STB6100_F 0x06 +-#define STB6100_F_F (0x1f << 0) +- +-#define STB6100_DLB 0x07 +- +-#define STB6100_TEST1 0x08 +- +-#define STB6100_FCCK 0x09 +-#define STB6100_FCCK_FCCK (0x01 << 6) +- +-#define STB6100_LPEN 0x0a +-#define STB6100_LPEN_LPEN (0x01 << 4) +-#define STB6100_LPEN_SYNP (0x01 << 5) +-#define STB6100_LPEN_OSCP (0x01 << 6) +-#define STB6100_LPEN_BEN (0x01 << 7) +- +-#define STB6100_TEST3 0x0b +- +-#define STB6100_NUMREGS 0x0c +- +- +-#define INRANGE(val, x, y) (((x <= val) && (val <= y)) || \ +- ((y <= val) && (val <= x)) ? 1 : 0) +- +-#define CHKRANGE(val, x, y) (((val >= x) && (val < y)) ? 1 : 0) +- +-struct stb6100_config { +- u8 tuner_address; +- u32 refclock; +-}; +- +-struct stb6100_state { +- struct i2c_adapter *i2c; +- +- const struct stb6100_config *config; +- struct dvb_tuner_ops ops; +- struct dvb_frontend *frontend; +- struct tuner_state status; +- +- u32 frequency; +- u32 srate; +- u32 bandwidth; +- u32 reference; +-}; +- +-#if defined(CONFIG_DVB_STB6100) || (defined(CONFIG_DVB_STB6100_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, +- const struct stb6100_config *config, +- struct i2c_adapter *i2c); +- +-#else +- +-static inline struct dvb_frontend *stb6100_attach(struct dvb_frontend *fe, +- const struct stb6100_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif //CONFIG_DVB_STB6100 +- +-#endif +diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb/frontends/stb6100_cfg.h +deleted file mode 100644 +index 6314d18..0000000 +--- a/drivers/media/dvb/frontends/stb6100_cfg.h ++++ /dev/null +@@ -1,104 +0,0 @@ +-/* +- STB6100 Silicon Tuner +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- Copyright (C) ST Microelectronics +- +- 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. +-*/ +- +-static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state t_state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- *frequency = t_state.frequency; +- } +- return 0; +-} +- +-static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state t_state; +- int err = 0; +- +- t_state.frequency = frequency; +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->set_state) { +- if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- } +- return 0; +-} +- +-static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = &fe->ops; +- struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; +- struct tuner_state t_state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- *bandwidth = t_state.bandwidth; +- } +- return 0; +-} +- +-static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state t_state; +- int err = 0; +- +- t_state.bandwidth = bandwidth; +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->set_state) { +- if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- } +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb/frontends/stb6100_proc.h +deleted file mode 100644 +index 112163a..0000000 +--- a/drivers/media/dvb/frontends/stb6100_proc.h ++++ /dev/null +@@ -1,138 +0,0 @@ +-/* +- STB6100 Silicon Tuner wrapper +- Copyright (C)2009 Igor M. Liplianin (liplianin@me.by) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; 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. +-*/ +- +-static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 1); +- +- err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state); +- if (err < 0) { +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- return err; +- } +- +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 0); +- +- *frequency = state.frequency; +- } +- +- return 0; +-} +- +-static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state state; +- int err = 0; +- +- state.frequency = frequency; +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->set_state) { +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 1); +- +- err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state); +- if (err < 0) { +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- return err; +- } +- +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 0); +- +- } +- +- return 0; +-} +- +-static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 1); +- +- err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state); +- if (err < 0) { +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- return err; +- } +- +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 0); +- +- *bandwidth = state.bandwidth; +- } +- +- return 0; +-} +- +-static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state state; +- int err = 0; +- +- state.bandwidth = bandwidth; +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->set_state) { +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 1); +- +- err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state); +- if (err < 0) { +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- return err; +- } +- +- if (frontend_ops->i2c_gate_ctrl) +- frontend_ops->i2c_gate_ctrl(fe, 0); +- +- } +- +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c +deleted file mode 100644 +index fb5548a..0000000 +--- a/drivers/media/dvb/frontends/stv0288.c ++++ /dev/null +@@ -1,626 +0,0 @@ +-/* +- Driver for ST STV0288 demodulator +- Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de +- for Reel Multimedia +- Copyright (C) 2008 TurboSight.com, Bob Liu +- Copyright (C) 2008 Igor M. Liplianin +- Removed stb6000 specific tuner code and revised some +- procedures. +- 2010-09-01 Josef Pavlik +- Fixed diseqc_msg, diseqc_burst and set_tone problems +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "stv0288.h" +- +-struct stv0288_state { +- struct i2c_adapter *i2c; +- const struct stv0288_config *config; +- struct dvb_frontend frontend; +- +- u8 initialised:1; +- u32 tuner_frequency; +- u32 symbol_rate; +- fe_code_rate_t fec_inner; +- int errmode; +-}; +- +-#define STATUS_BER 0 +-#define STATUS_UCBLOCKS 1 +- +-static int debug; +-static int debug_legacy_dish_switch; +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG "stv0288: " args); \ +- } while (0) +- +- +-static int stv0288_writeregI(struct stv0288_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = 2 +- }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int stv0288_write(struct dvb_frontend *fe, const u8 buf[], int len) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- if (len != 2) +- return -EINVAL; +- +- return stv0288_writeregI(state, buf[0], buf[1]); +-} +- +-static u8 stv0288_readreg(struct stv0288_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 1 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", +- __func__, reg, ret); +- +- return b1[0]; +-} +- +-static int stv0288_set_symbolrate(struct dvb_frontend *fe, u32 srate) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- unsigned int temp; +- unsigned char b[3]; +- +- if ((srate < 1000000) || (srate > 45000000)) +- return -EINVAL; +- +- stv0288_writeregI(state, 0x22, 0); +- stv0288_writeregI(state, 0x23, 0); +- stv0288_writeregI(state, 0x2b, 0xff); +- stv0288_writeregI(state, 0x2c, 0xf7); +- +- temp = (unsigned int)srate / 1000; +- +- temp = temp * 32768; +- temp = temp / 25; +- temp = temp / 125; +- b[0] = (unsigned char)((temp >> 12) & 0xff); +- b[1] = (unsigned char)((temp >> 4) & 0xff); +- b[2] = (unsigned char)((temp << 4) & 0xf0); +- stv0288_writeregI(state, 0x28, 0x80); /* SFRH */ +- stv0288_writeregI(state, 0x29, 0); /* SFRM */ +- stv0288_writeregI(state, 0x2a, 0); /* SFRL */ +- +- stv0288_writeregI(state, 0x28, b[0]); +- stv0288_writeregI(state, 0x29, b[1]); +- stv0288_writeregI(state, 0x2a, b[2]); +- dprintk("stv0288: stv0288_set_symbolrate\n"); +- +- return 0; +-} +- +-static int stv0288_send_diseqc_msg(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- int i; +- +- dprintk("%s\n", __func__); +- +- stv0288_writeregI(state, 0x09, 0); +- msleep(30); +- stv0288_writeregI(state, 0x05, 0x12);/* modulated mode, single shot */ +- +- for (i = 0; i < m->msg_len; i++) { +- if (stv0288_writeregI(state, 0x06, m->msg[i])) +- return -EREMOTEIO; +- } +- msleep(m->msg_len*12); +- return 0; +-} +- +-static int stv0288_send_diseqc_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t burst) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (stv0288_writeregI(state, 0x05, 0x03))/* burst mode, single shot */ +- return -EREMOTEIO; +- +- if (stv0288_writeregI(state, 0x06, burst == SEC_MINI_A ? 0x00 : 0xff)) +- return -EREMOTEIO; +- +- msleep(15); +- if (stv0288_writeregI(state, 0x05, 0x12)) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int stv0288_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- if (stv0288_writeregI(state, 0x05, 0x10))/* cont carrier */ +- return -EREMOTEIO; +- break; +- +- case SEC_TONE_OFF: +- if (stv0288_writeregI(state, 0x05, 0x12))/* burst mode off*/ +- return -EREMOTEIO; +- break; +- +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-static u8 stv0288_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x20, +- 0x09, 0x0, +- 0x0a, 0x4, +- 0x0b, 0x0, +- 0x0c, 0x0, +- 0x0d, 0x0, +- 0x0e, 0xd4, +- 0x0f, 0x30, +- 0x11, 0x80, +- 0x12, 0x03, +- 0x13, 0x48, +- 0x14, 0x84, +- 0x15, 0x45, +- 0x16, 0xb7, +- 0x17, 0x9c, +- 0x18, 0x0, +- 0x19, 0xa6, +- 0x1a, 0x88, +- 0x1b, 0x8f, +- 0x1c, 0xf0, +- 0x20, 0x0b, +- 0x21, 0x54, +- 0x22, 0x0, +- 0x23, 0x0, +- 0x2b, 0xff, +- 0x2c, 0xf7, +- 0x30, 0x0, +- 0x31, 0x1e, +- 0x32, 0x14, +- 0x33, 0x0f, +- 0x34, 0x09, +- 0x35, 0x0c, +- 0x36, 0x05, +- 0x37, 0x2f, +- 0x38, 0x16, +- 0x39, 0xbe, +- 0x3a, 0x0, +- 0x3b, 0x13, +- 0x3c, 0x11, +- 0x3d, 0x30, +- 0x40, 0x63, +- 0x41, 0x04, +- 0x42, 0x20, +- 0x43, 0x00, +- 0x44, 0x00, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x47, 0x00, +- 0x4a, 0x00, +- 0x50, 0x10, +- 0x51, 0x38, +- 0x52, 0x21, +- 0x58, 0x54, +- 0x59, 0x86, +- 0x5a, 0x0, +- 0x5b, 0x9b, +- 0x5c, 0x08, +- 0x5d, 0x7f, +- 0x5e, 0x0, +- 0x5f, 0xff, +- 0x70, 0x0, +- 0x71, 0x0, +- 0x72, 0x0, +- 0x74, 0x0, +- 0x75, 0x0, +- 0x76, 0x0, +- 0x81, 0x0, +- 0x82, 0x3f, +- 0x83, 0x3f, +- 0x84, 0x0, +- 0x85, 0x0, +- 0x88, 0x0, +- 0x89, 0x0, +- 0x8a, 0x0, +- 0x8b, 0x0, +- 0x8c, 0x0, +- 0x90, 0x0, +- 0x91, 0x0, +- 0x92, 0x0, +- 0x93, 0x0, +- 0x94, 0x1c, +- 0x97, 0x0, +- 0xa0, 0x48, +- 0xa1, 0x0, +- 0xb0, 0xb8, +- 0xb1, 0x3a, +- 0xb2, 0x10, +- 0xb3, 0x82, +- 0xb4, 0x80, +- 0xb5, 0x82, +- 0xb6, 0x82, +- 0xb7, 0x82, +- 0xb8, 0x20, +- 0xb9, 0x0, +- 0xf0, 0x0, +- 0xf1, 0x0, +- 0xf2, 0xc0, +- 0x51, 0x36, +- 0x52, 0x09, +- 0x53, 0x94, +- 0x54, 0x62, +- 0x55, 0x29, +- 0x56, 0x64, +- 0x57, 0x2b, +- 0xff, 0xff, +-}; +- +-static int stv0288_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) +-{ +- dprintk("%s: %s\n", __func__, +- volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : +- volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); +- +- return 0; +-} +- +-static int stv0288_init(struct dvb_frontend *fe) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- int i; +- u8 reg; +- u8 val; +- +- dprintk("stv0288: init chip\n"); +- stv0288_writeregI(state, 0x41, 0x04); +- msleep(50); +- +- /* we have default inittab */ +- if (state->config->inittab == NULL) { +- for (i = 0; !(stv0288_inittab[i] == 0xff && +- stv0288_inittab[i + 1] == 0xff); i += 2) +- stv0288_writeregI(state, stv0288_inittab[i], +- stv0288_inittab[i + 1]); +- } else { +- for (i = 0; ; i += 2) { +- reg = state->config->inittab[i]; +- val = state->config->inittab[i+1]; +- if (reg == 0xff && val == 0xff) +- break; +- stv0288_writeregI(state, reg, val); +- } +- } +- return 0; +-} +- +-static int stv0288_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- u8 sync = stv0288_readreg(state, 0x24); +- if (sync == 255) +- sync = 0; +- +- dprintk("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); +- +- *status = 0; +- if (sync & 0x80) +- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL; +- if (sync & 0x10) +- *status |= FE_HAS_VITERBI; +- if (sync & 0x08) { +- *status |= FE_HAS_LOCK; +- dprintk("stv0288 has locked\n"); +- } +- +- return 0; +-} +- +-static int stv0288_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- if (state->errmode != STATUS_BER) +- return 0; +- *ber = (stv0288_readreg(state, 0x26) << 8) | +- stv0288_readreg(state, 0x27); +- dprintk("stv0288_read_ber %d\n", *ber); +- +- return 0; +-} +- +- +-static int stv0288_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- s32 signal = 0xffff - ((stv0288_readreg(state, 0x10) << 8)); +- +- +- signal = signal * 5 / 4; +- *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; +- dprintk("stv0288_read_signal_strength %d\n", *strength); +- +- return 0; +-} +-static int stv0288_sleep(struct dvb_frontend *fe) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- stv0288_writeregI(state, 0x41, 0x84); +- state->initialised = 0; +- +- return 0; +-} +-static int stv0288_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- s32 xsnr = 0xffff - ((stv0288_readreg(state, 0x2d) << 8) +- | stv0288_readreg(state, 0x2e)); +- xsnr = 3 * (xsnr - 0xa100); +- *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; +- dprintk("stv0288_read_snr %d\n", *snr); +- +- return 0; +-} +- +-static int stv0288_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- if (state->errmode != STATUS_BER) +- return 0; +- *ucblocks = (stv0288_readreg(state, 0x26) << 8) | +- stv0288_readreg(state, 0x27); +- dprintk("stv0288_read_ber %d\n", *ucblocks); +- +- return 0; +-} +- +-static int stv0288_set_property(struct dvb_frontend *fe, struct dtv_property *p) +-{ +- dprintk("%s(..)\n", __func__); +- return 0; +-} +- +-static int stv0288_set_frontend(struct dvb_frontend *fe) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- char tm; +- unsigned char tda[3]; +- u8 reg, time_out = 0; +- +- dprintk("%s : FE_SET_FRONTEND\n", __func__); +- +- if (c->delivery_system != SYS_DVBS) { +- dprintk("%s: unsupported delivery " +- "system selected (%d)\n", +- __func__, c->delivery_system); +- return -EOPNOTSUPP; +- } +- +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- +- /* only frequency & symbol_rate are used for tuner*/ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- udelay(10); +- stv0288_set_symbolrate(fe, c->symbol_rate); +- /* Carrier lock control register */ +- stv0288_writeregI(state, 0x15, 0xc5); +- +- tda[2] = 0x0; /* CFRL */ +- for (tm = -9; tm < 7;) { +- /* Viterbi status */ +- reg = stv0288_readreg(state, 0x24); +- if (reg & 0x8) +- break; +- if (reg & 0x80) { +- time_out++; +- if (time_out > 10) +- break; +- tda[2] += 40; +- if (tda[2] < 40) +- tm++; +- } else { +- tm++; +- tda[2] = 0; +- time_out = 0; +- } +- tda[1] = (unsigned char)tm; +- stv0288_writeregI(state, 0x2b, tda[1]); +- stv0288_writeregI(state, 0x2c, tda[2]); +- udelay(30); +- } +- state->tuner_frequency = c->frequency; +- state->fec_inner = FEC_AUTO; +- state->symbol_rate = c->symbol_rate; +- +- return 0; +-} +- +-static int stv0288_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- +- if (enable) +- stv0288_writeregI(state, 0x01, 0xb5); +- else +- stv0288_writeregI(state, 0x01, 0x35); +- +- udelay(1); +- +- return 0; +-} +- +-static void stv0288_release(struct dvb_frontend *fe) +-{ +- struct stv0288_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops stv0288_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "ST STV0288 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1000, /* kHz for QPSK frontends */ +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, /* ppm */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_QPSK | +- FE_CAN_FEC_AUTO +- }, +- +- .release = stv0288_release, +- .init = stv0288_init, +- .sleep = stv0288_sleep, +- .write = stv0288_write, +- .i2c_gate_ctrl = stv0288_i2c_gate_ctrl, +- .read_status = stv0288_read_status, +- .read_ber = stv0288_read_ber, +- .read_signal_strength = stv0288_read_signal_strength, +- .read_snr = stv0288_read_snr, +- .read_ucblocks = stv0288_read_ucblocks, +- .diseqc_send_master_cmd = stv0288_send_diseqc_msg, +- .diseqc_send_burst = stv0288_send_diseqc_burst, +- .set_tone = stv0288_set_tone, +- .set_voltage = stv0288_set_voltage, +- +- .set_property = stv0288_set_property, +- .set_frontend = stv0288_set_frontend, +-}; +- +-struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv0288_state *state = NULL; +- int id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- state->tuner_frequency = 0; +- state->symbol_rate = 0; +- state->fec_inner = 0; +- state->errmode = STATUS_BER; +- +- stv0288_writeregI(state, 0x41, 0x04); +- msleep(200); +- id = stv0288_readreg(state, 0x00); +- dprintk("stv0288 id %x\n", id); +- +- /* register 0x00 contains 0x11 for STV0288 */ +- if (id != 0x11) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &stv0288_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- +- return NULL; +-} +-EXPORT_SYMBOL(stv0288_attach); +- +-module_param(debug_legacy_dish_switch, int, 0444); +-MODULE_PARM_DESC(debug_legacy_dish_switch, +- "Enable timing analysis for Dish Network legacy switches"); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("ST STV0288 DVB Demodulator driver"); +-MODULE_AUTHOR("Georg Acher, Bob Liu, Igor liplianin"); +-MODULE_LICENSE("GPL"); +- +diff --git a/drivers/media/dvb/frontends/stv0288.h b/drivers/media/dvb/frontends/stv0288.h +deleted file mode 100644 +index f2b53db..0000000 +--- a/drivers/media/dvb/frontends/stv0288.h ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- Driver for ST STV0288 demodulator +- +- Copyright (C) 2006 Georg Acher, BayCom GmbH, acher (at) baycom (dot) de +- for Reel Multimedia +- Copyright (C) 2008 TurboSight.com, +- Copyright (C) 2008 Igor M. Liplianin +- Removed stb6000 specific tuner code and revised some +- procedures. +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef STV0288_H +-#define STV0288_H +- +-#include +-#include "dvb_frontend.h" +- +-struct stv0288_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- u8* inittab; +- +- /* minimum delay before retuning */ +- int min_delay_ms; +- +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +-}; +- +-#if defined(CONFIG_DVB_STV0288) || (defined(CONFIG_DVB_STV0288_MODULE) && \ +- defined(MODULE)) +-extern struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_STV0288 */ +- +-static inline int stv0288_writereg(struct dvb_frontend *fe, u8 reg, u8 val) +-{ +- int r = 0; +- u8 buf[] = { reg, val }; +- if (fe->ops.write) +- r = fe->ops.write(fe, buf, 2); +- return r; +-} +- +-#endif /* STV0288_H */ +diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c +deleted file mode 100644 +index 85c157a..0000000 +--- a/drivers/media/dvb/frontends/stv0297.c ++++ /dev/null +@@ -1,724 +0,0 @@ +-/* +- Driver for STV0297 demodulator +- +- Copyright (C) 2004 Andrew de Quincey +- Copyright (C) 2003-2004 Dennis Noermann +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "stv0297.h" +- +-struct stv0297_state { +- struct i2c_adapter *i2c; +- const struct stv0297_config *config; +- struct dvb_frontend frontend; +- +- unsigned long last_ber; +- unsigned long base_freq; +-}; +- +-#if 1 +-#define dprintk(x...) printk(x) +-#else +-#define dprintk(x...) +-#endif +- +-#define STV0297_CLOCK_KHZ 28900 +- +- +-static int stv0297_writereg(struct stv0297_state *state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 2 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static int stv0297_readreg(struct stv0297_state *state, u8 reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 1}, +- {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} +- }; +- +- // this device needs a STOP between the register and data +- if (state->config->stop_during_read) { +- if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); +- return -1; +- } +- if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); +- return -1; +- } +- } else { +- if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg, ret); +- return -1; +- } +- } +- +- return b1[0]; +-} +- +-static int stv0297_writereg_mask(struct stv0297_state *state, u8 reg, u8 mask, u8 data) +-{ +- int val; +- +- val = stv0297_readreg(state, reg); +- val &= ~mask; +- val |= (data & mask); +- stv0297_writereg(state, reg, val); +- +- return 0; +-} +- +-static int stv0297_readregs(struct stv0297_state *state, u8 reg1, u8 * b, u8 len) +-{ +- int ret; +- struct i2c_msg msg[] = { {.addr = state->config->demod_address,.flags = 0,.buf = +- ®1,.len = 1}, +- {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b,.len = len} +- }; +- +- // this device needs a STOP between the register and data +- if (state->config->stop_during_read) { +- if ((ret = i2c_transfer(state->i2c, &msg[0], 1)) != 1) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); +- return -1; +- } +- if ((ret = i2c_transfer(state->i2c, &msg[1], 1)) != 1) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); +- return -1; +- } +- } else { +- if ((ret = i2c_transfer(state->i2c, msg, 2)) != 2) { +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", __func__, reg1, ret); +- return -1; +- } +- } +- +- return 0; +-} +- +-static u32 stv0297_get_symbolrate(struct stv0297_state *state) +-{ +- u64 tmp; +- +- tmp = stv0297_readreg(state, 0x55); +- tmp |= stv0297_readreg(state, 0x56) << 8; +- tmp |= stv0297_readreg(state, 0x57) << 16; +- tmp |= stv0297_readreg(state, 0x58) << 24; +- +- tmp *= STV0297_CLOCK_KHZ; +- tmp >>= 32; +- +- return (u32) tmp; +-} +- +-static void stv0297_set_symbolrate(struct stv0297_state *state, u32 srate) +-{ +- long tmp; +- +- tmp = 131072L * srate; /* 131072 = 2^17 */ +- tmp = tmp / (STV0297_CLOCK_KHZ / 4); /* 1/4 = 2^-2 */ +- tmp = tmp * 8192L; /* 8192 = 2^13 */ +- +- stv0297_writereg(state, 0x55, (unsigned char) (tmp & 0xFF)); +- stv0297_writereg(state, 0x56, (unsigned char) (tmp >> 8)); +- stv0297_writereg(state, 0x57, (unsigned char) (tmp >> 16)); +- stv0297_writereg(state, 0x58, (unsigned char) (tmp >> 24)); +-} +- +-static void stv0297_set_sweeprate(struct stv0297_state *state, short fshift, long symrate) +-{ +- long tmp; +- +- tmp = (long) fshift *262144L; /* 262144 = 2*18 */ +- tmp /= symrate; +- tmp *= 1024; /* 1024 = 2*10 */ +- +- // adjust +- if (tmp >= 0) { +- tmp += 500000; +- } else { +- tmp -= 500000; +- } +- tmp /= 1000000; +- +- stv0297_writereg(state, 0x60, tmp & 0xFF); +- stv0297_writereg_mask(state, 0x69, 0xF0, (tmp >> 4) & 0xf0); +-} +- +-static void stv0297_set_carrieroffset(struct stv0297_state *state, long offset) +-{ +- long tmp; +- +- /* symrate is hardcoded to 10000 */ +- tmp = offset * 26844L; /* (2**28)/10000 */ +- if (tmp < 0) +- tmp += 0x10000000; +- tmp &= 0x0FFFFFFF; +- +- stv0297_writereg(state, 0x66, (unsigned char) (tmp & 0xFF)); +- stv0297_writereg(state, 0x67, (unsigned char) (tmp >> 8)); +- stv0297_writereg(state, 0x68, (unsigned char) (tmp >> 16)); +- stv0297_writereg_mask(state, 0x69, 0x0F, (tmp >> 24) & 0x0f); +-} +- +-/* +-static long stv0297_get_carrieroffset(struct stv0297_state *state) +-{ +- s64 tmp; +- +- stv0297_writereg(state, 0x6B, 0x00); +- +- tmp = stv0297_readreg(state, 0x66); +- tmp |= (stv0297_readreg(state, 0x67) << 8); +- tmp |= (stv0297_readreg(state, 0x68) << 16); +- tmp |= (stv0297_readreg(state, 0x69) & 0x0F) << 24; +- +- tmp *= stv0297_get_symbolrate(state); +- tmp >>= 28; +- +- return (s32) tmp; +-} +-*/ +- +-static void stv0297_set_initialdemodfreq(struct stv0297_state *state, long freq) +-{ +- s32 tmp; +- +- if (freq > 10000) +- freq -= STV0297_CLOCK_KHZ; +- +- tmp = (STV0297_CLOCK_KHZ * 1000) / (1 << 16); +- tmp = (freq * 1000) / tmp; +- if (tmp > 0xffff) +- tmp = 0xffff; +- +- stv0297_writereg_mask(state, 0x25, 0x80, 0x80); +- stv0297_writereg(state, 0x21, tmp >> 8); +- stv0297_writereg(state, 0x20, tmp); +-} +- +-static int stv0297_set_qam(struct stv0297_state *state, fe_modulation_t modulation) +-{ +- int val = 0; +- +- switch (modulation) { +- case QAM_16: +- val = 0; +- break; +- +- case QAM_32: +- val = 1; +- break; +- +- case QAM_64: +- val = 4; +- break; +- +- case QAM_128: +- val = 2; +- break; +- +- case QAM_256: +- val = 3; +- break; +- +- default: +- return -EINVAL; +- } +- +- stv0297_writereg_mask(state, 0x00, 0x70, val << 4); +- +- return 0; +-} +- +-static int stv0297_set_inversion(struct stv0297_state *state, fe_spectral_inversion_t inversion) +-{ +- int val = 0; +- +- switch (inversion) { +- case INVERSION_OFF: +- val = 0; +- break; +- +- case INVERSION_ON: +- val = 1; +- break; +- +- default: +- return -EINVAL; +- } +- +- stv0297_writereg_mask(state, 0x83, 0x08, val << 3); +- +- return 0; +-} +- +-static int stv0297_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- +- if (enable) { +- stv0297_writereg(state, 0x87, 0x78); +- stv0297_writereg(state, 0x86, 0xc8); +- } +- +- return 0; +-} +- +-static int stv0297_init(struct dvb_frontend *fe) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- int i; +- +- /* load init table */ +- for (i=0; !(state->config->inittab[i] == 0xff && state->config->inittab[i+1] == 0xff); i+=2) +- stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); +- msleep(200); +- +- state->last_ber = 0; +- +- return 0; +-} +- +-static int stv0297_sleep(struct dvb_frontend *fe) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- +- stv0297_writereg_mask(state, 0x80, 1, 1); +- +- return 0; +-} +- +-static int stv0297_read_status(struct dvb_frontend *fe, fe_status_t * status) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- +- u8 sync = stv0297_readreg(state, 0xDF); +- +- *status = 0; +- if (sync & 0x80) +- *status |= +- FE_HAS_SYNC | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; +- return 0; +-} +- +-static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- u8 BER[3]; +- +- stv0297_readregs(state, 0xA0, BER, 3); +- if (!(BER[0] & 0x80)) { +- state->last_ber = BER[2] << 8 | BER[1]; +- stv0297_writereg_mask(state, 0xA0, 0x80, 0x80); +- } +- +- *ber = state->last_ber; +- +- return 0; +-} +- +- +-static int stv0297_read_signal_strength(struct dvb_frontend *fe, u16 * strength) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- u8 STRENGTH[3]; +- u16 tmp; +- +- stv0297_readregs(state, 0x41, STRENGTH, 3); +- tmp = (STRENGTH[1] & 0x03) << 8 | STRENGTH[0]; +- if (STRENGTH[2] & 0x20) { +- if (tmp < 0x200) +- tmp = 0; +- else +- tmp = tmp - 0x200; +- } else { +- if (tmp > 0x1ff) +- tmp = 0; +- else +- tmp = 0x1ff - tmp; +- } +- *strength = (tmp << 7) | (tmp >> 2); +- return 0; +-} +- +-static int stv0297_read_snr(struct dvb_frontend *fe, u16 * snr) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- u8 SNR[2]; +- +- stv0297_readregs(state, 0x07, SNR, 2); +- *snr = SNR[1] << 8 | SNR[0]; +- +- return 0; +-} +- +-static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- +- stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */ +- +- *ucblocks = (stv0297_readreg(state, 0xD5) << 8) +- | stv0297_readreg(state, 0xD4); +- +- stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */ +- stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */ +- +- return 0; +-} +- +-static int stv0297_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0297_state *state = fe->demodulator_priv; +- int u_threshold; +- int initial_u; +- int blind_u; +- int delay; +- int sweeprate; +- int carrieroffset; +- unsigned long starttime; +- unsigned long timeout; +- fe_spectral_inversion_t inversion; +- +- switch (p->modulation) { +- case QAM_16: +- case QAM_32: +- case QAM_64: +- delay = 100; +- sweeprate = 1000; +- break; +- +- case QAM_128: +- case QAM_256: +- delay = 200; +- sweeprate = 500; +- break; +- +- default: +- return -EINVAL; +- } +- +- // determine inversion dependent parameters +- inversion = p->inversion; +- if (state->config->invert) +- inversion = (inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; +- carrieroffset = -330; +- switch (inversion) { +- case INVERSION_OFF: +- break; +- +- case INVERSION_ON: +- sweeprate = -sweeprate; +- carrieroffset = -carrieroffset; +- break; +- +- default: +- return -EINVAL; +- } +- +- stv0297_init(fe); +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* clear software interrupts */ +- stv0297_writereg(state, 0x82, 0x0); +- +- /* set initial demodulation frequency */ +- stv0297_set_initialdemodfreq(state, 7250); +- +- /* setup AGC */ +- stv0297_writereg_mask(state, 0x43, 0x10, 0x00); +- stv0297_writereg(state, 0x41, 0x00); +- stv0297_writereg_mask(state, 0x42, 0x03, 0x01); +- stv0297_writereg_mask(state, 0x36, 0x60, 0x00); +- stv0297_writereg_mask(state, 0x36, 0x18, 0x00); +- stv0297_writereg_mask(state, 0x71, 0x80, 0x80); +- stv0297_writereg(state, 0x72, 0x00); +- stv0297_writereg(state, 0x73, 0x00); +- stv0297_writereg_mask(state, 0x74, 0x0F, 0x00); +- stv0297_writereg_mask(state, 0x43, 0x08, 0x00); +- stv0297_writereg_mask(state, 0x71, 0x80, 0x00); +- +- /* setup STL */ +- stv0297_writereg_mask(state, 0x5a, 0x20, 0x20); +- stv0297_writereg_mask(state, 0x5b, 0x02, 0x02); +- stv0297_writereg_mask(state, 0x5b, 0x02, 0x00); +- stv0297_writereg_mask(state, 0x5b, 0x01, 0x00); +- stv0297_writereg_mask(state, 0x5a, 0x40, 0x40); +- +- /* disable frequency sweep */ +- stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); +- +- /* reset deinterleaver */ +- stv0297_writereg_mask(state, 0x81, 0x01, 0x01); +- stv0297_writereg_mask(state, 0x81, 0x01, 0x00); +- +- /* ??? */ +- stv0297_writereg_mask(state, 0x83, 0x20, 0x20); +- stv0297_writereg_mask(state, 0x83, 0x20, 0x00); +- +- /* reset equaliser */ +- u_threshold = stv0297_readreg(state, 0x00) & 0xf; +- initial_u = stv0297_readreg(state, 0x01) >> 4; +- blind_u = stv0297_readreg(state, 0x01) & 0xf; +- stv0297_writereg_mask(state, 0x84, 0x01, 0x01); +- stv0297_writereg_mask(state, 0x84, 0x01, 0x00); +- stv0297_writereg_mask(state, 0x00, 0x0f, u_threshold); +- stv0297_writereg_mask(state, 0x01, 0xf0, initial_u << 4); +- stv0297_writereg_mask(state, 0x01, 0x0f, blind_u); +- +- /* data comes from internal A/D */ +- stv0297_writereg_mask(state, 0x87, 0x80, 0x00); +- +- /* clear phase registers */ +- stv0297_writereg(state, 0x63, 0x00); +- stv0297_writereg(state, 0x64, 0x00); +- stv0297_writereg(state, 0x65, 0x00); +- stv0297_writereg(state, 0x66, 0x00); +- stv0297_writereg(state, 0x67, 0x00); +- stv0297_writereg(state, 0x68, 0x00); +- stv0297_writereg_mask(state, 0x69, 0x0f, 0x00); +- +- /* set parameters */ +- stv0297_set_qam(state, p->modulation); +- stv0297_set_symbolrate(state, p->symbol_rate / 1000); +- stv0297_set_sweeprate(state, sweeprate, p->symbol_rate / 1000); +- stv0297_set_carrieroffset(state, carrieroffset); +- stv0297_set_inversion(state, inversion); +- +- /* kick off lock */ +- /* Disable corner detection for higher QAMs */ +- if (p->modulation == QAM_128 || +- p->modulation == QAM_256) +- stv0297_writereg_mask(state, 0x88, 0x08, 0x00); +- else +- stv0297_writereg_mask(state, 0x88, 0x08, 0x08); +- +- stv0297_writereg_mask(state, 0x5a, 0x20, 0x00); +- stv0297_writereg_mask(state, 0x6a, 0x01, 0x01); +- stv0297_writereg_mask(state, 0x43, 0x40, 0x40); +- stv0297_writereg_mask(state, 0x5b, 0x30, 0x00); +- stv0297_writereg_mask(state, 0x03, 0x0c, 0x0c); +- stv0297_writereg_mask(state, 0x03, 0x03, 0x03); +- stv0297_writereg_mask(state, 0x43, 0x10, 0x10); +- +- /* wait for WGAGC lock */ +- starttime = jiffies; +- timeout = jiffies + msecs_to_jiffies(2000); +- while (time_before(jiffies, timeout)) { +- msleep(10); +- if (stv0297_readreg(state, 0x43) & 0x08) +- break; +- } +- if (time_after(jiffies, timeout)) { +- goto timeout; +- } +- msleep(20); +- +- /* wait for equaliser partial convergence */ +- timeout = jiffies + msecs_to_jiffies(500); +- while (time_before(jiffies, timeout)) { +- msleep(10); +- +- if (stv0297_readreg(state, 0x82) & 0x04) { +- break; +- } +- } +- if (time_after(jiffies, timeout)) { +- goto timeout; +- } +- +- /* wait for equaliser full convergence */ +- timeout = jiffies + msecs_to_jiffies(delay); +- while (time_before(jiffies, timeout)) { +- msleep(10); +- +- if (stv0297_readreg(state, 0x82) & 0x08) { +- break; +- } +- } +- if (time_after(jiffies, timeout)) { +- goto timeout; +- } +- +- /* disable sweep */ +- stv0297_writereg_mask(state, 0x6a, 1, 0); +- stv0297_writereg_mask(state, 0x88, 8, 0); +- +- /* wait for main lock */ +- timeout = jiffies + msecs_to_jiffies(20); +- while (time_before(jiffies, timeout)) { +- msleep(10); +- +- if (stv0297_readreg(state, 0xDF) & 0x80) { +- break; +- } +- } +- if (time_after(jiffies, timeout)) { +- goto timeout; +- } +- msleep(100); +- +- /* is it still locked after that delay? */ +- if (!(stv0297_readreg(state, 0xDF) & 0x80)) { +- goto timeout; +- } +- +- /* success!! */ +- stv0297_writereg_mask(state, 0x5a, 0x40, 0x00); +- state->base_freq = p->frequency; +- return 0; +- +-timeout: +- stv0297_writereg_mask(state, 0x6a, 0x01, 0x00); +- return 0; +-} +- +-static int stv0297_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0297_state *state = fe->demodulator_priv; +- int reg_00, reg_83; +- +- reg_00 = stv0297_readreg(state, 0x00); +- reg_83 = stv0297_readreg(state, 0x83); +- +- p->frequency = state->base_freq; +- p->inversion = (reg_83 & 0x08) ? INVERSION_ON : INVERSION_OFF; +- if (state->config->invert) +- p->inversion = (p->inversion == INVERSION_ON) ? INVERSION_OFF : INVERSION_ON; +- p->symbol_rate = stv0297_get_symbolrate(state) * 1000; +- p->fec_inner = FEC_NONE; +- +- switch ((reg_00 >> 4) & 0x7) { +- case 0: +- p->modulation = QAM_16; +- break; +- case 1: +- p->modulation = QAM_32; +- break; +- case 2: +- p->modulation = QAM_128; +- break; +- case 3: +- p->modulation = QAM_256; +- break; +- case 4: +- p->modulation = QAM_64; +- break; +- } +- +- return 0; +-} +- +-static void stv0297_release(struct dvb_frontend *fe) +-{ +- struct stv0297_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops stv0297_ops; +- +-struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv0297_state *state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->last_ber = 0; +- state->base_freq = 0; +- +- /* check if the demod is there */ +- if ((stv0297_readreg(state, 0x80) & 0x70) != 0x20) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &stv0297_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops stv0297_ops = { +- .delsys = { SYS_DVBC_ANNEX_A }, +- .info = { +- .name = "ST STV0297 DVB-C", +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 62500, +- .symbol_rate_min = 870000, +- .symbol_rate_max = 11700000, +- .caps = FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_FEC_AUTO}, +- +- .release = stv0297_release, +- +- .init = stv0297_init, +- .sleep = stv0297_sleep, +- .i2c_gate_ctrl = stv0297_i2c_gate_ctrl, +- +- .set_frontend = stv0297_set_frontend, +- .get_frontend = stv0297_get_frontend, +- +- .read_status = stv0297_read_status, +- .read_ber = stv0297_read_ber, +- .read_signal_strength = stv0297_read_signal_strength, +- .read_snr = stv0297_read_snr, +- .read_ucblocks = stv0297_read_ucblocks, +-}; +- +-MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); +-MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(stv0297_attach); +diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h +deleted file mode 100644 +index 3f8f946..0000000 +--- a/drivers/media/dvb/frontends/stv0297.h ++++ /dev/null +@@ -1,57 +0,0 @@ +-/* +- Driver for STV0297 demodulator +- +- Copyright (C) 2003-2004 Dennis Noermann +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef STV0297_H +-#define STV0297_H +- +-#include +-#include "dvb_frontend.h" +- +-struct stv0297_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* inittab - array of pairs of values. +- * First of each pair is the register, second is the value. +- * List should be terminated with an 0xff, 0xff pair. +- */ +- u8* inittab; +- +- /* does the "inversion" need inverted? */ +- u8 invert:1; +- +- /* set to 1 if the device requires an i2c STOP during reading */ +- u8 stop_during_read:1; +-}; +- +-#if defined(CONFIG_DVB_STV0297) || (defined(CONFIG_DVB_STV0297_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_STV0297 +- +-#endif // STV0297_H +diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c +deleted file mode 100644 +index 057b5f8..0000000 +--- a/drivers/media/dvb/frontends/stv0299.c ++++ /dev/null +@@ -1,762 +0,0 @@ +-/* +- Driver for ST STV0299 demodulator +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- , +- , +- +- +- +- Philips SU1278/SH +- +- Copyright (C) 2002 by Peter Schildmann +- +- +- LG TDQF-S001F +- +- Copyright (C) 2002 Felix Domke +- & Andreas Oberritter +- +- +- Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B +- +- Copyright (C) 2003 Vadim Catana : +- +- Support for Philips SU1278 on Technotrend hardware +- +- Copyright (C) 2004 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "stv0299.h" +- +-struct stv0299_state { +- struct i2c_adapter* i2c; +- const struct stv0299_config* config; +- struct dvb_frontend frontend; +- +- u8 initialised:1; +- u32 tuner_frequency; +- u32 symbol_rate; +- fe_code_rate_t fec_inner; +- int errmode; +- u32 ucblocks; +- u8 mcr_reg; +-}; +- +-#define STATUS_BER 0 +-#define STATUS_UCBLOCKS 1 +- +-static int debug; +-static int debug_legacy_dish_switch; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "stv0299: " args); \ +- } while (0) +- +- +-static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf [] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- +- ret = i2c_transfer (state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: writereg error (reg == 0x%02x, val == 0x%02x, " +- "ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int stv0299_write(struct dvb_frontend* fe, const u8 buf[], int len) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- if (len != 2) +- return -EINVAL; +- +- return stv0299_writeregI(state, buf[0], buf[1]); +-} +- +-static u8 stv0299_readreg (struct stv0299_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk("%s: readreg error (reg == 0x%02x, ret == %i)\n", +- __func__, reg, ret); +- +- return b1[0]; +-} +- +-static int stv0299_readregs (struct stv0299_state* state, u8 reg1, u8 *b, u8 len) +-{ +- int ret; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk("%s: readreg error (ret == %i)\n", __func__, ret); +- +- return ret == 2 ? 0 : ret; +-} +- +-static int stv0299_set_FEC (struct stv0299_state* state, fe_code_rate_t fec) +-{ +- dprintk ("%s\n", __func__); +- +- switch (fec) { +- case FEC_AUTO: +- { +- return stv0299_writeregI (state, 0x31, 0x1f); +- } +- case FEC_1_2: +- { +- return stv0299_writeregI (state, 0x31, 0x01); +- } +- case FEC_2_3: +- { +- return stv0299_writeregI (state, 0x31, 0x02); +- } +- case FEC_3_4: +- { +- return stv0299_writeregI (state, 0x31, 0x04); +- } +- case FEC_5_6: +- { +- return stv0299_writeregI (state, 0x31, 0x08); +- } +- case FEC_7_8: +- { +- return stv0299_writeregI (state, 0x31, 0x10); +- } +- default: +- { +- return -EINVAL; +- } +- } +-} +- +-static fe_code_rate_t stv0299_get_fec (struct stv0299_state* state) +-{ +- static fe_code_rate_t fec_tab [] = { FEC_2_3, FEC_3_4, FEC_5_6, +- FEC_7_8, FEC_1_2 }; +- u8 index; +- +- dprintk ("%s\n", __func__); +- +- index = stv0299_readreg (state, 0x1b); +- index &= 0x7; +- +- if (index > 4) +- return FEC_AUTO; +- +- return fec_tab [index]; +-} +- +-static int stv0299_wait_diseqc_fifo (struct stv0299_state* state, int timeout) +-{ +- unsigned long start = jiffies; +- +- dprintk ("%s\n", __func__); +- +- while (stv0299_readreg(state, 0x0a) & 1) { +- if (jiffies - start > timeout) { +- dprintk ("%s: timeout!!\n", __func__); +- return -ETIMEDOUT; +- } +- msleep(10); +- }; +- +- return 0; +-} +- +-static int stv0299_wait_diseqc_idle (struct stv0299_state* state, int timeout) +-{ +- unsigned long start = jiffies; +- +- dprintk ("%s\n", __func__); +- +- while ((stv0299_readreg(state, 0x0a) & 3) != 2 ) { +- if (jiffies - start > timeout) { +- dprintk ("%s: timeout!!\n", __func__); +- return -ETIMEDOUT; +- } +- msleep(10); +- }; +- +- return 0; +-} +- +-static int stv0299_set_symbolrate (struct dvb_frontend* fe, u32 srate) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u64 big = srate; +- u32 ratio; +- +- // check rate is within limits +- if ((srate < 1000000) || (srate > 45000000)) return -EINVAL; +- +- // calculate value to program +- big = big << 20; +- big += (state->config->mclk-1); // round correctly +- do_div(big, state->config->mclk); +- ratio = big << 4; +- +- return state->config->set_symbol_rate(fe, srate, ratio); +-} +- +-static int stv0299_get_symbolrate (struct stv0299_state* state) +-{ +- u32 Mclk = state->config->mclk / 4096L; +- u32 srate; +- s32 offset; +- u8 sfr[3]; +- s8 rtf; +- +- dprintk ("%s\n", __func__); +- +- stv0299_readregs (state, 0x1f, sfr, 3); +- stv0299_readregs (state, 0x1a, (u8 *)&rtf, 1); +- +- srate = (sfr[0] << 8) | sfr[1]; +- srate *= Mclk; +- srate /= 16; +- srate += (sfr[2] >> 4) * Mclk / 256; +- offset = (s32) rtf * (srate / 4096L); +- offset /= 128; +- +- dprintk ("%s : srate = %i\n", __func__, srate); +- dprintk ("%s : ofset = %i\n", __func__, offset); +- +- srate += offset; +- +- srate += 1000; +- srate /= 2000; +- srate *= 2000; +- +- return srate; +-} +- +-static int stv0299_send_diseqc_msg (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u8 val; +- int i; +- +- dprintk ("%s\n", __func__); +- +- if (stv0299_wait_diseqc_idle (state, 100) < 0) +- return -ETIMEDOUT; +- +- val = stv0299_readreg (state, 0x08); +- +- if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x6)) /* DiSEqC mode */ +- return -EREMOTEIO; +- +- for (i=0; imsg_len; i++) { +- if (stv0299_wait_diseqc_fifo (state, 100) < 0) +- return -ETIMEDOUT; +- +- if (stv0299_writeregI (state, 0x09, m->msg[i])) +- return -EREMOTEIO; +- } +- +- if (stv0299_wait_diseqc_idle (state, 100) < 0) +- return -ETIMEDOUT; +- +- return 0; +-} +- +-static int stv0299_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u8 val; +- +- dprintk ("%s\n", __func__); +- +- if (stv0299_wait_diseqc_idle (state, 100) < 0) +- return -ETIMEDOUT; +- +- val = stv0299_readreg (state, 0x08); +- +- if (stv0299_writeregI (state, 0x08, (val & ~0x7) | 0x2)) /* burst mode */ +- return -EREMOTEIO; +- +- if (stv0299_writeregI (state, 0x09, burst == SEC_MINI_A ? 0x00 : 0xff)) +- return -EREMOTEIO; +- +- if (stv0299_wait_diseqc_idle (state, 100) < 0) +- return -ETIMEDOUT; +- +- if (stv0299_writeregI (state, 0x08, val)) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int stv0299_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u8 val; +- +- if (stv0299_wait_diseqc_idle (state, 100) < 0) +- return -ETIMEDOUT; +- +- val = stv0299_readreg (state, 0x08); +- +- switch (tone) { +- case SEC_TONE_ON: +- return stv0299_writeregI (state, 0x08, val | 0x3); +- +- case SEC_TONE_OFF: +- return stv0299_writeregI (state, 0x08, (val & ~0x3) | 0x02); +- +- default: +- return -EINVAL; +- } +-} +- +-static int stv0299_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u8 reg0x08; +- u8 reg0x0c; +- +- dprintk("%s: %s\n", __func__, +- voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : +- voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); +- +- reg0x08 = stv0299_readreg (state, 0x08); +- reg0x0c = stv0299_readreg (state, 0x0c); +- +- /** +- * H/V switching over OP0, OP1 and OP2 are LNB power enable bits +- */ +- reg0x0c &= 0x0f; +- reg0x08 = (reg0x08 & 0x3f) | (state->config->lock_output << 6); +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) +- reg0x0c |= 0x10; /* OP1 off, OP0 on */ +- else +- reg0x0c |= 0x40; /* OP1 on, OP0 off */ +- break; +- case SEC_VOLTAGE_18: +- reg0x0c |= 0x50; /* OP1 on, OP0 on */ +- break; +- case SEC_VOLTAGE_OFF: +- /* LNB power off! */ +- reg0x08 = 0x00; +- reg0x0c = 0x00; +- break; +- default: +- return -EINVAL; +- }; +- +- if (state->config->op0_off) +- reg0x0c &= ~0x10; +- +- stv0299_writeregI(state, 0x08, reg0x08); +- return stv0299_writeregI(state, 0x0c, reg0x0c); +-} +- +-static int stv0299_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long cmd) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- u8 reg0x08; +- u8 reg0x0c; +- u8 lv_mask = 0x40; +- u8 last = 1; +- int i; +- struct timeval nexttime; +- struct timeval tv[10]; +- +- reg0x08 = stv0299_readreg (state, 0x08); +- reg0x0c = stv0299_readreg (state, 0x0c); +- reg0x0c &= 0x0f; +- stv0299_writeregI (state, 0x08, (reg0x08 & 0x3f) | (state->config->lock_output << 6)); +- if (state->config->volt13_op0_op1 == STV0299_VOLT13_OP0) +- lv_mask = 0x10; +- +- cmd = cmd << 1; +- if (debug_legacy_dish_switch) +- printk ("%s switch command: 0x%04lx\n",__func__, cmd); +- +- do_gettimeofday (&nexttime); +- if (debug_legacy_dish_switch) +- memcpy (&tv[0], &nexttime, sizeof (struct timeval)); +- stv0299_writeregI (state, 0x0c, reg0x0c | 0x50); /* set LNB to 18V */ +- +- dvb_frontend_sleep_until(&nexttime, 32000); +- +- for (i=0; i<9; i++) { +- if (debug_legacy_dish_switch) +- do_gettimeofday (&tv[i+1]); +- if((cmd & 0x01) != last) { +- /* set voltage to (last ? 13V : 18V) */ +- stv0299_writeregI (state, 0x0c, reg0x0c | (last ? lv_mask : 0x50)); +- last = (last) ? 0 : 1; +- } +- +- cmd = cmd >> 1; +- +- if (i != 8) +- dvb_frontend_sleep_until(&nexttime, 8000); +- } +- if (debug_legacy_dish_switch) { +- printk ("%s(%d): switch delay (should be 32k followed by all 8k\n", +- __func__, fe->dvb->num); +- for (i = 1; i < 10; i++) +- printk ("%d: %d\n", i, timeval_usec_diff(tv[i-1] , tv[i])); +- } +- +- return 0; +-} +- +-static int stv0299_init (struct dvb_frontend* fe) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- int i; +- u8 reg; +- u8 val; +- +- dprintk("stv0299: init chip\n"); +- +- stv0299_writeregI(state, 0x02, 0x30 | state->mcr_reg); +- msleep(50); +- +- for (i = 0; ; i += 2) { +- reg = state->config->inittab[i]; +- val = state->config->inittab[i+1]; +- if (reg == 0xff && val == 0xff) +- break; +- if (reg == 0x0c && state->config->op0_off) +- val &= ~0x10; +- if (reg == 0x2) +- state->mcr_reg = val & 0xf; +- stv0299_writeregI(state, reg, val); +- } +- +- return 0; +-} +- +-static int stv0299_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- u8 signal = 0xff - stv0299_readreg (state, 0x18); +- u8 sync = stv0299_readreg (state, 0x1b); +- +- dprintk ("%s : FE_READ_STATUS : VSTATUS: 0x%02x\n", __func__, sync); +- *status = 0; +- +- if (signal > 10) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 0x80) +- *status |= FE_HAS_CARRIER; +- +- if (sync & 0x10) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 0x08) +- *status |= FE_HAS_SYNC; +- +- if ((sync & 0x98) == 0x98) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int stv0299_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- if (state->errmode != STATUS_BER) +- return -ENOSYS; +- +- *ber = stv0299_readreg(state, 0x1e) | (stv0299_readreg(state, 0x1d) << 8); +- +- return 0; +-} +- +-static int stv0299_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- s32 signal = 0xffff - ((stv0299_readreg (state, 0x18) << 8) +- | stv0299_readreg (state, 0x19)); +- +- dprintk ("%s : FE_READ_SIGNAL_STRENGTH : AGC2I: 0x%02x%02x, signal=0x%04x\n", __func__, +- stv0299_readreg (state, 0x18), +- stv0299_readreg (state, 0x19), (int) signal); +- +- signal = signal * 5 / 4; +- *strength = (signal > 0xffff) ? 0xffff : (signal < 0) ? 0 : signal; +- +- return 0; +-} +- +-static int stv0299_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- s32 xsnr = 0xffff - ((stv0299_readreg (state, 0x24) << 8) +- | stv0299_readreg (state, 0x25)); +- xsnr = 3 * (xsnr - 0xa100); +- *snr = (xsnr > 0xffff) ? 0xffff : (xsnr < 0) ? 0 : xsnr; +- +- return 0; +-} +- +-static int stv0299_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- if (state->errmode != STATUS_UCBLOCKS) +- return -ENOSYS; +- +- state->ucblocks += stv0299_readreg(state, 0x1e); +- state->ucblocks += (stv0299_readreg(state, 0x1d) << 8); +- *ucblocks = state->ucblocks; +- +- return 0; +-} +- +-static int stv0299_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0299_state* state = fe->demodulator_priv; +- int invval = 0; +- +- dprintk ("%s : FE_SET_FRONTEND\n", __func__); +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- +- // set the inversion +- if (p->inversion == INVERSION_OFF) invval = 0; +- else if (p->inversion == INVERSION_ON) invval = 1; +- else { +- printk("stv0299 does not support auto-inversion\n"); +- return -EINVAL; +- } +- if (state->config->invert) invval = (~invval) & 1; +- stv0299_writeregI(state, 0x0c, (stv0299_readreg(state, 0x0c) & 0xfe) | invval); +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- stv0299_set_FEC(state, p->fec_inner); +- stv0299_set_symbolrate(fe, p->symbol_rate); +- stv0299_writeregI(state, 0x22, 0x00); +- stv0299_writeregI(state, 0x23, 0x00); +- +- state->tuner_frequency = p->frequency; +- state->fec_inner = p->fec_inner; +- state->symbol_rate = p->symbol_rate; +- +- return 0; +-} +- +-static int stv0299_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0299_state* state = fe->demodulator_priv; +- s32 derot_freq; +- int invval; +- +- derot_freq = (s32)(s16) ((stv0299_readreg (state, 0x22) << 8) +- | stv0299_readreg (state, 0x23)); +- +- derot_freq *= (state->config->mclk >> 16); +- derot_freq += 500; +- derot_freq /= 1000; +- +- p->frequency += derot_freq; +- +- invval = stv0299_readreg (state, 0x0c) & 1; +- if (state->config->invert) invval = (~invval) & 1; +- p->inversion = invval ? INVERSION_ON : INVERSION_OFF; +- +- p->fec_inner = stv0299_get_fec(state); +- p->symbol_rate = stv0299_get_symbolrate(state); +- +- return 0; +-} +- +-static int stv0299_sleep(struct dvb_frontend* fe) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- stv0299_writeregI(state, 0x02, 0xb0 | state->mcr_reg); +- state->initialised = 0; +- +- return 0; +-} +- +-static int stv0299_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- +- if (enable) { +- stv0299_writeregI(state, 0x05, 0xb5); +- } else { +- stv0299_writeregI(state, 0x05, 0x35); +- } +- udelay(1); +- return 0; +-} +- +-static int stv0299_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- fesettings->min_delay_ms = state->config->min_delay_ms; +- if (p->symbol_rate < 10000000) { +- fesettings->step_size = p->symbol_rate / 32000; +- fesettings->max_drift = 5000; +- } else { +- fesettings->step_size = p->symbol_rate / 16000; +- fesettings->max_drift = p->symbol_rate / 2000; +- } +- return 0; +-} +- +-static void stv0299_release(struct dvb_frontend* fe) +-{ +- struct stv0299_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops stv0299_ops; +- +-struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, +- struct i2c_adapter* i2c) +-{ +- struct stv0299_state* state = NULL; +- int id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->initialised = 0; +- state->tuner_frequency = 0; +- state->symbol_rate = 0; +- state->fec_inner = 0; +- state->errmode = STATUS_BER; +- +- /* check if the demod is there */ +- stv0299_writeregI(state, 0x02, 0x30); /* standby off */ +- msleep(200); +- id = stv0299_readreg(state, 0x00); +- +- /* register 0x00 contains 0xa1 for STV0299 and STV0299B */ +- /* register 0x00 might contain 0x80 when returning from standby */ +- if (id != 0xa1 && id != 0x80) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &stv0299_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops stv0299_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "ST STV0299 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, /* ppm */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_QPSK | +- FE_CAN_FEC_AUTO +- }, +- +- .release = stv0299_release, +- +- .init = stv0299_init, +- .sleep = stv0299_sleep, +- .write = stv0299_write, +- .i2c_gate_ctrl = stv0299_i2c_gate_ctrl, +- +- .set_frontend = stv0299_set_frontend, +- .get_frontend = stv0299_get_frontend, +- .get_tune_settings = stv0299_get_tune_settings, +- +- .read_status = stv0299_read_status, +- .read_ber = stv0299_read_ber, +- .read_signal_strength = stv0299_read_signal_strength, +- .read_snr = stv0299_read_snr, +- .read_ucblocks = stv0299_read_ucblocks, +- +- .diseqc_send_master_cmd = stv0299_send_diseqc_msg, +- .diseqc_send_burst = stv0299_send_diseqc_burst, +- .set_tone = stv0299_set_tone, +- .set_voltage = stv0299_set_voltage, +- .dishnetwork_send_legacy_command = stv0299_send_legacy_dish_cmd, +-}; +- +-module_param(debug_legacy_dish_switch, int, 0444); +-MODULE_PARM_DESC(debug_legacy_dish_switch, "Enable timing analysis for Dish Network legacy switches"); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); +-MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, " +- "Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(stv0299_attach); +diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h +deleted file mode 100644 +index ba219b7..0000000 +--- a/drivers/media/dvb/frontends/stv0299.h ++++ /dev/null +@@ -1,118 +0,0 @@ +-/* +- Driver for ST STV0299 demodulator +- +- Copyright (C) 2001-2002 Convergence Integrated Media GmbH +- , +- , +- +- +- +- Philips SU1278/SH +- +- Copyright (C) 2002 by Peter Schildmann +- +- +- LG TDQF-S001F +- +- Copyright (C) 2002 Felix Domke +- & Andreas Oberritter +- +- +- Support for Samsung TBMU24112IMB used on Technisat SkyStar2 rev. 2.6B +- +- Copyright (C) 2003 Vadim Catana : +- +- Support for Philips SU1278 on Technotrend hardware +- +- Copyright (C) 2004 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef STV0299_H +-#define STV0299_H +- +-#include +-#include "dvb_frontend.h" +- +-#define STV0299_LOCKOUTPUT_0 0 +-#define STV0299_LOCKOUTPUT_1 1 +-#define STV0299_LOCKOUTPUT_CF 2 +-#define STV0299_LOCKOUTPUT_LK 3 +- +-#define STV0299_VOLT13_OP0 0 +-#define STV0299_VOLT13_OP1 1 +- +-struct stv0299_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* inittab - array of pairs of values. +- * First of each pair is the register, second is the value. +- * List should be terminated with an 0xff, 0xff pair. +- */ +- const u8* inittab; +- +- /* master clock to use */ +- u32 mclk; +- +- /* does the inversion require inversion? */ +- u8 invert:1; +- +- /* Skip reinitialisation? */ +- u8 skip_reinit:1; +- +- /* LOCK OUTPUT setting */ +- u8 lock_output:2; +- +- /* Is 13v controlled by OP0 or OP1? */ +- u8 volt13_op0_op1:1; +- +- /* Turn-off OP0? */ +- u8 op0_off:1; +- +- /* minimum delay before retuning */ +- int min_delay_ms; +- +- /* Set the symbol rate */ +- int (*set_symbol_rate)(struct dvb_frontend *fe, u32 srate, u32 ratio); +- +- /* Set device param to start dma */ +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +-}; +- +-#if defined(CONFIG_DVB_STV0299) || (defined(CONFIG_DVB_STV0299_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *stv0299_attach(const struct stv0299_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_STV0299 +- +-static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { +- int r = 0; +- u8 buf[] = {reg, val}; +- if (fe->ops.write) +- r = fe->ops.write(fe, buf, 2); +- return r; +-} +- +-#endif // STV0299_H +diff --git a/drivers/media/dvb/frontends/stv0367.c b/drivers/media/dvb/frontends/stv0367.c +deleted file mode 100644 +index fdd20c7..0000000 +--- a/drivers/media/dvb/frontends/stv0367.c ++++ /dev/null +@@ -1,3453 +0,0 @@ +-/* +- * stv0367.c +- * +- * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2010,2011 NetUP Inc. +- * Copyright (C) 2010,2011 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "stv0367.h" +-#include "stv0367_regs.h" +-#include "stv0367_priv.h" +- +-static int stvdebug; +-module_param_named(debug, stvdebug, int, 0644); +- +-static int i2cdebug; +-module_param_named(i2c_debug, i2cdebug, int, 0644); +- +-#define dprintk(args...) \ +- do { \ +- if (stvdebug) \ +- printk(KERN_DEBUG args); \ +- } while (0) +- /* DVB-C */ +- +-struct stv0367cab_state { +- enum stv0367_cab_signal_type state; +- u32 mclk; +- u32 adc_clk; +- s32 search_range; +- s32 derot_offset; +- /* results */ +- int locked; /* channel found */ +- u32 freq_khz; /* found frequency (in kHz) */ +- u32 symbol_rate; /* found symbol rate (in Bds) */ +- enum stv0367cab_mod modulation; /* modulation */ +- fe_spectral_inversion_t spect_inv; /* Spectrum Inversion */ +-}; +- +-struct stv0367ter_state { +- /* DVB-T */ +- enum stv0367_ter_signal_type state; +- enum stv0367_ter_if_iq_mode if_iq_mode; +- enum stv0367_ter_mode mode;/* mode 2K or 8K */ +- fe_guard_interval_t guard; +- enum stv0367_ter_hierarchy hierarchy; +- u32 frequency; +- fe_spectral_inversion_t sense; /* current search spectrum */ +- u8 force; /* force mode/guard */ +- u8 bw; /* channel width 6, 7 or 8 in MHz */ +- u8 pBW; /* channel width used during previous lock */ +- u32 pBER; +- u32 pPER; +- u32 ucblocks; +- s8 echo_pos; /* echo position */ +- u8 first_lock; +- u8 unlock_counter; +- u32 agc_val; +-}; +- +-struct stv0367_state { +- struct dvb_frontend fe; +- struct i2c_adapter *i2c; +- /* config settings */ +- const struct stv0367_config *config; +- u8 chip_id; +- /* DVB-C */ +- struct stv0367cab_state *cab_state; +- /* DVB-T */ +- struct stv0367ter_state *ter_state; +-}; +- +-struct st_register { +- u16 addr; +- u8 value; +-}; +- +-/* values for STV4100 XTAL=30M int clk=53.125M*/ +-static struct st_register def0367ter[STV0367TER_NBREGS] = { +- {R367TER_ID, 0x60}, +- {R367TER_I2CRPT, 0xa0}, +- /* {R367TER_I2CRPT, 0x22},*/ +- {R367TER_TOPCTRL, 0x00},/* for xc5000; was 0x02 */ +- {R367TER_IOCFG0, 0x40}, +- {R367TER_DAC0R, 0x00}, +- {R367TER_IOCFG1, 0x00}, +- {R367TER_DAC1R, 0x00}, +- {R367TER_IOCFG2, 0x62}, +- {R367TER_SDFR, 0x00}, +- {R367TER_STATUS, 0xf8}, +- {R367TER_AUX_CLK, 0x0a}, +- {R367TER_FREESYS1, 0x00}, +- {R367TER_FREESYS2, 0x00}, +- {R367TER_FREESYS3, 0x00}, +- {R367TER_GPIO_CFG, 0x55}, +- {R367TER_GPIO_CMD, 0x00}, +- {R367TER_AGC2MAX, 0xff}, +- {R367TER_AGC2MIN, 0x00}, +- {R367TER_AGC1MAX, 0xff}, +- {R367TER_AGC1MIN, 0x00}, +- {R367TER_AGCR, 0xbc}, +- {R367TER_AGC2TH, 0x00}, +- {R367TER_AGC12C, 0x00}, +- {R367TER_AGCCTRL1, 0x85}, +- {R367TER_AGCCTRL2, 0x1f}, +- {R367TER_AGC1VAL1, 0x00}, +- {R367TER_AGC1VAL2, 0x00}, +- {R367TER_AGC2VAL1, 0x6f}, +- {R367TER_AGC2VAL2, 0x05}, +- {R367TER_AGC2PGA, 0x00}, +- {R367TER_OVF_RATE1, 0x00}, +- {R367TER_OVF_RATE2, 0x00}, +- {R367TER_GAIN_SRC1, 0xaa},/* for xc5000; was 0x2b */ +- {R367TER_GAIN_SRC2, 0xd6},/* for xc5000; was 0x04 */ +- {R367TER_INC_DEROT1, 0x55}, +- {R367TER_INC_DEROT2, 0x55}, +- {R367TER_PPM_CPAMP_DIR, 0x2c}, +- {R367TER_PPM_CPAMP_INV, 0x00}, +- {R367TER_FREESTFE_1, 0x00}, +- {R367TER_FREESTFE_2, 0x1c}, +- {R367TER_DCOFFSET, 0x00}, +- {R367TER_EN_PROCESS, 0x05}, +- {R367TER_SDI_SMOOTHER, 0x80}, +- {R367TER_FE_LOOP_OPEN, 0x1c}, +- {R367TER_FREQOFF1, 0x00}, +- {R367TER_FREQOFF2, 0x00}, +- {R367TER_FREQOFF3, 0x00}, +- {R367TER_TIMOFF1, 0x00}, +- {R367TER_TIMOFF2, 0x00}, +- {R367TER_EPQ, 0x02}, +- {R367TER_EPQAUTO, 0x01}, +- {R367TER_SYR_UPDATE, 0xf5}, +- {R367TER_CHPFREE, 0x00}, +- {R367TER_PPM_STATE_MAC, 0x23}, +- {R367TER_INR_THRESHOLD, 0xff}, +- {R367TER_EPQ_TPS_ID_CELL, 0xf9}, +- {R367TER_EPQ_CFG, 0x00}, +- {R367TER_EPQ_STATUS, 0x01}, +- {R367TER_AUTORELOCK, 0x81}, +- {R367TER_BER_THR_VMSB, 0x00}, +- {R367TER_BER_THR_MSB, 0x00}, +- {R367TER_BER_THR_LSB, 0x00}, +- {R367TER_CCD, 0x83}, +- {R367TER_SPECTR_CFG, 0x00}, +- {R367TER_CHC_DUMMY, 0x18}, +- {R367TER_INC_CTL, 0x88}, +- {R367TER_INCTHRES_COR1, 0xb4}, +- {R367TER_INCTHRES_COR2, 0x96}, +- {R367TER_INCTHRES_DET1, 0x0e}, +- {R367TER_INCTHRES_DET2, 0x11}, +- {R367TER_IIR_CELLNB, 0x8d}, +- {R367TER_IIRCX_COEFF1_MSB, 0x00}, +- {R367TER_IIRCX_COEFF1_LSB, 0x00}, +- {R367TER_IIRCX_COEFF2_MSB, 0x09}, +- {R367TER_IIRCX_COEFF2_LSB, 0x18}, +- {R367TER_IIRCX_COEFF3_MSB, 0x14}, +- {R367TER_IIRCX_COEFF3_LSB, 0x9c}, +- {R367TER_IIRCX_COEFF4_MSB, 0x00}, +- {R367TER_IIRCX_COEFF4_LSB, 0x00}, +- {R367TER_IIRCX_COEFF5_MSB, 0x36}, +- {R367TER_IIRCX_COEFF5_LSB, 0x42}, +- {R367TER_FEPATH_CFG, 0x00}, +- {R367TER_PMC1_FUNC, 0x65}, +- {R367TER_PMC1_FOR, 0x00}, +- {R367TER_PMC2_FUNC, 0x00}, +- {R367TER_STATUS_ERR_DA, 0xe0}, +- {R367TER_DIG_AGC_R, 0xfe}, +- {R367TER_COMAGC_TARMSB, 0x0b}, +- {R367TER_COM_AGC_TAR_ENMODE, 0x41}, +- {R367TER_COM_AGC_CFG, 0x3e}, +- {R367TER_COM_AGC_GAIN1, 0x39}, +- {R367TER_AUT_AGC_TARGETMSB, 0x0b}, +- {R367TER_LOCK_DET_MSB, 0x01}, +- {R367TER_AGCTAR_LOCK_LSBS, 0x40}, +- {R367TER_AUT_GAIN_EN, 0xf4}, +- {R367TER_AUT_CFG, 0xf0}, +- {R367TER_LOCKN, 0x23}, +- {R367TER_INT_X_3, 0x00}, +- {R367TER_INT_X_2, 0x03}, +- {R367TER_INT_X_1, 0x8d}, +- {R367TER_INT_X_0, 0xa0}, +- {R367TER_MIN_ERRX_MSB, 0x00}, +- {R367TER_COR_CTL, 0x23}, +- {R367TER_COR_STAT, 0xf6}, +- {R367TER_COR_INTEN, 0x00}, +- {R367TER_COR_INTSTAT, 0x3f}, +- {R367TER_COR_MODEGUARD, 0x03}, +- {R367TER_AGC_CTL, 0x08}, +- {R367TER_AGC_MANUAL1, 0x00}, +- {R367TER_AGC_MANUAL2, 0x00}, +- {R367TER_AGC_TARG, 0x16}, +- {R367TER_AGC_GAIN1, 0x53}, +- {R367TER_AGC_GAIN2, 0x1d}, +- {R367TER_RESERVED_1, 0x00}, +- {R367TER_RESERVED_2, 0x00}, +- {R367TER_RESERVED_3, 0x00}, +- {R367TER_CAS_CTL, 0x44}, +- {R367TER_CAS_FREQ, 0xb3}, +- {R367TER_CAS_DAGCGAIN, 0x12}, +- {R367TER_SYR_CTL, 0x04}, +- {R367TER_SYR_STAT, 0x10}, +- {R367TER_SYR_NCO1, 0x00}, +- {R367TER_SYR_NCO2, 0x00}, +- {R367TER_SYR_OFFSET1, 0x00}, +- {R367TER_SYR_OFFSET2, 0x00}, +- {R367TER_FFT_CTL, 0x00}, +- {R367TER_SCR_CTL, 0x70}, +- {R367TER_PPM_CTL1, 0xf8}, +- {R367TER_TRL_CTL, 0x14},/* for xc5000; was 0xac */ +- {R367TER_TRL_NOMRATE1, 0xae},/* for xc5000; was 0x1e */ +- {R367TER_TRL_NOMRATE2, 0x56},/* for xc5000; was 0x58 */ +- {R367TER_TRL_TIME1, 0x1d}, +- {R367TER_TRL_TIME2, 0xfc}, +- {R367TER_CRL_CTL, 0x24}, +- {R367TER_CRL_FREQ1, 0xad}, +- {R367TER_CRL_FREQ2, 0x9d}, +- {R367TER_CRL_FREQ3, 0xff}, +- {R367TER_CHC_CTL, 0x01}, +- {R367TER_CHC_SNR, 0xf0}, +- {R367TER_BDI_CTL, 0x00}, +- {R367TER_DMP_CTL, 0x00}, +- {R367TER_TPS_RCVD1, 0x30}, +- {R367TER_TPS_RCVD2, 0x02}, +- {R367TER_TPS_RCVD3, 0x01}, +- {R367TER_TPS_RCVD4, 0x00}, +- {R367TER_TPS_ID_CELL1, 0x00}, +- {R367TER_TPS_ID_CELL2, 0x00}, +- {R367TER_TPS_RCVD5_SET1, 0x02}, +- {R367TER_TPS_SET2, 0x02}, +- {R367TER_TPS_SET3, 0x01}, +- {R367TER_TPS_CTL, 0x00}, +- {R367TER_CTL_FFTOSNUM, 0x34}, +- {R367TER_TESTSELECT, 0x09}, +- {R367TER_MSC_REV, 0x0a}, +- {R367TER_PIR_CTL, 0x00}, +- {R367TER_SNR_CARRIER1, 0xa1}, +- {R367TER_SNR_CARRIER2, 0x9a}, +- {R367TER_PPM_CPAMP, 0x2c}, +- {R367TER_TSM_AP0, 0x00}, +- {R367TER_TSM_AP1, 0x00}, +- {R367TER_TSM_AP2 , 0x00}, +- {R367TER_TSM_AP3, 0x00}, +- {R367TER_TSM_AP4, 0x00}, +- {R367TER_TSM_AP5, 0x00}, +- {R367TER_TSM_AP6, 0x00}, +- {R367TER_TSM_AP7, 0x00}, +- {R367TER_TSTRES, 0x00}, +- {R367TER_ANACTRL, 0x0D},/* PLL stoped, restart at init!!! */ +- {R367TER_TSTBUS, 0x00}, +- {R367TER_TSTRATE, 0x00}, +- {R367TER_CONSTMODE, 0x01}, +- {R367TER_CONSTCARR1, 0x00}, +- {R367TER_CONSTCARR2, 0x00}, +- {R367TER_ICONSTEL, 0x0a}, +- {R367TER_QCONSTEL, 0x15}, +- {R367TER_TSTBISTRES0, 0x00}, +- {R367TER_TSTBISTRES1, 0x00}, +- {R367TER_TSTBISTRES2, 0x28}, +- {R367TER_TSTBISTRES3, 0x00}, +- {R367TER_RF_AGC1, 0xff}, +- {R367TER_RF_AGC2, 0x83}, +- {R367TER_ANADIGCTRL, 0x19}, +- {R367TER_PLLMDIV, 0x01},/* for xc5000; was 0x0c */ +- {R367TER_PLLNDIV, 0x06},/* for xc5000; was 0x55 */ +- {R367TER_PLLSETUP, 0x18}, +- {R367TER_DUAL_AD12, 0x0C},/* for xc5000 AGC voltage 1.6V */ +- {R367TER_TSTBIST, 0x00}, +- {R367TER_PAD_COMP_CTRL, 0x00}, +- {R367TER_PAD_COMP_WR, 0x00}, +- {R367TER_PAD_COMP_RD, 0xe0}, +- {R367TER_SYR_TARGET_FFTADJT_MSB, 0x00}, +- {R367TER_SYR_TARGET_FFTADJT_LSB, 0x00}, +- {R367TER_SYR_TARGET_CHCADJT_MSB, 0x00}, +- {R367TER_SYR_TARGET_CHCADJT_LSB, 0x00}, +- {R367TER_SYR_FLAG, 0x00}, +- {R367TER_CRL_TARGET1, 0x00}, +- {R367TER_CRL_TARGET2, 0x00}, +- {R367TER_CRL_TARGET3, 0x00}, +- {R367TER_CRL_TARGET4, 0x00}, +- {R367TER_CRL_FLAG, 0x00}, +- {R367TER_TRL_TARGET1, 0x00}, +- {R367TER_TRL_TARGET2, 0x00}, +- {R367TER_TRL_CHC, 0x00}, +- {R367TER_CHC_SNR_TARG, 0x00}, +- {R367TER_TOP_TRACK, 0x00}, +- {R367TER_TRACKER_FREE1, 0x00}, +- {R367TER_ERROR_CRL1, 0x00}, +- {R367TER_ERROR_CRL2, 0x00}, +- {R367TER_ERROR_CRL3, 0x00}, +- {R367TER_ERROR_CRL4, 0x00}, +- {R367TER_DEC_NCO1, 0x2c}, +- {R367TER_DEC_NCO2, 0x0f}, +- {R367TER_DEC_NCO3, 0x20}, +- {R367TER_SNR, 0xf1}, +- {R367TER_SYR_FFTADJ1, 0x00}, +- {R367TER_SYR_FFTADJ2, 0x00}, +- {R367TER_SYR_CHCADJ1, 0x00}, +- {R367TER_SYR_CHCADJ2, 0x00}, +- {R367TER_SYR_OFF, 0x00}, +- {R367TER_PPM_OFFSET1, 0x00}, +- {R367TER_PPM_OFFSET2, 0x03}, +- {R367TER_TRACKER_FREE2, 0x00}, +- {R367TER_DEBG_LT10, 0x00}, +- {R367TER_DEBG_LT11, 0x00}, +- {R367TER_DEBG_LT12, 0x00}, +- {R367TER_DEBG_LT13, 0x00}, +- {R367TER_DEBG_LT14, 0x00}, +- {R367TER_DEBG_LT15, 0x00}, +- {R367TER_DEBG_LT16, 0x00}, +- {R367TER_DEBG_LT17, 0x00}, +- {R367TER_DEBG_LT18, 0x00}, +- {R367TER_DEBG_LT19, 0x00}, +- {R367TER_DEBG_LT1A, 0x00}, +- {R367TER_DEBG_LT1B, 0x00}, +- {R367TER_DEBG_LT1C, 0x00}, +- {R367TER_DEBG_LT1D, 0x00}, +- {R367TER_DEBG_LT1E, 0x00}, +- {R367TER_DEBG_LT1F, 0x00}, +- {R367TER_RCCFGH, 0x00}, +- {R367TER_RCCFGM, 0x00}, +- {R367TER_RCCFGL, 0x00}, +- {R367TER_RCINSDELH, 0x00}, +- {R367TER_RCINSDELM, 0x00}, +- {R367TER_RCINSDELL, 0x00}, +- {R367TER_RCSTATUS, 0x00}, +- {R367TER_RCSPEED, 0x6f}, +- {R367TER_RCDEBUGM, 0xe7}, +- {R367TER_RCDEBUGL, 0x9b}, +- {R367TER_RCOBSCFG, 0x00}, +- {R367TER_RCOBSM, 0x00}, +- {R367TER_RCOBSL, 0x00}, +- {R367TER_RCFECSPY, 0x00}, +- {R367TER_RCFSPYCFG, 0x00}, +- {R367TER_RCFSPYDATA, 0x00}, +- {R367TER_RCFSPYOUT, 0x00}, +- {R367TER_RCFSTATUS, 0x00}, +- {R367TER_RCFGOODPACK, 0x00}, +- {R367TER_RCFPACKCNT, 0x00}, +- {R367TER_RCFSPYMISC, 0x00}, +- {R367TER_RCFBERCPT4, 0x00}, +- {R367TER_RCFBERCPT3, 0x00}, +- {R367TER_RCFBERCPT2, 0x00}, +- {R367TER_RCFBERCPT1, 0x00}, +- {R367TER_RCFBERCPT0, 0x00}, +- {R367TER_RCFBERERR2, 0x00}, +- {R367TER_RCFBERERR1, 0x00}, +- {R367TER_RCFBERERR0, 0x00}, +- {R367TER_RCFSTATESM, 0x00}, +- {R367TER_RCFSTATESL, 0x00}, +- {R367TER_RCFSPYBER, 0x00}, +- {R367TER_RCFSPYDISTM, 0x00}, +- {R367TER_RCFSPYDISTL, 0x00}, +- {R367TER_RCFSPYOBS7, 0x00}, +- {R367TER_RCFSPYOBS6, 0x00}, +- {R367TER_RCFSPYOBS5, 0x00}, +- {R367TER_RCFSPYOBS4, 0x00}, +- {R367TER_RCFSPYOBS3, 0x00}, +- {R367TER_RCFSPYOBS2, 0x00}, +- {R367TER_RCFSPYOBS1, 0x00}, +- {R367TER_RCFSPYOBS0, 0x00}, +- {R367TER_TSGENERAL, 0x00}, +- {R367TER_RC1SPEED, 0x6f}, +- {R367TER_TSGSTATUS, 0x18}, +- {R367TER_FECM, 0x01}, +- {R367TER_VTH12, 0xff}, +- {R367TER_VTH23, 0xa1}, +- {R367TER_VTH34, 0x64}, +- {R367TER_VTH56, 0x40}, +- {R367TER_VTH67, 0x00}, +- {R367TER_VTH78, 0x2c}, +- {R367TER_VITCURPUN, 0x12}, +- {R367TER_VERROR, 0x01}, +- {R367TER_PRVIT, 0x3f}, +- {R367TER_VAVSRVIT, 0x00}, +- {R367TER_VSTATUSVIT, 0xbd}, +- {R367TER_VTHINUSE, 0xa1}, +- {R367TER_KDIV12, 0x20}, +- {R367TER_KDIV23, 0x40}, +- {R367TER_KDIV34, 0x20}, +- {R367TER_KDIV56, 0x30}, +- {R367TER_KDIV67, 0x00}, +- {R367TER_KDIV78, 0x30}, +- {R367TER_SIGPOWER, 0x54}, +- {R367TER_DEMAPVIT, 0x40}, +- {R367TER_VITSCALE, 0x00}, +- {R367TER_FFEC1PRG, 0x00}, +- {R367TER_FVITCURPUN, 0x12}, +- {R367TER_FVERROR, 0x01}, +- {R367TER_FVSTATUSVIT, 0xbd}, +- {R367TER_DEBUG_LT1, 0x00}, +- {R367TER_DEBUG_LT2, 0x00}, +- {R367TER_DEBUG_LT3, 0x00}, +- {R367TER_TSTSFMET, 0x00}, +- {R367TER_SELOUT, 0x00}, +- {R367TER_TSYNC, 0x00}, +- {R367TER_TSTERR, 0x00}, +- {R367TER_TSFSYNC, 0x00}, +- {R367TER_TSTSFERR, 0x00}, +- {R367TER_TSTTSSF1, 0x01}, +- {R367TER_TSTTSSF2, 0x1f}, +- {R367TER_TSTTSSF3, 0x00}, +- {R367TER_TSTTS1, 0x00}, +- {R367TER_TSTTS2, 0x1f}, +- {R367TER_TSTTS3, 0x01}, +- {R367TER_TSTTS4, 0x00}, +- {R367TER_TSTTSRC, 0x00}, +- {R367TER_TSTTSRS, 0x00}, +- {R367TER_TSSTATEM, 0xb0}, +- {R367TER_TSSTATEL, 0x40}, +- {R367TER_TSCFGH, 0xC0}, +- {R367TER_TSCFGM, 0xc0},/* for xc5000; was 0x00 */ +- {R367TER_TSCFGL, 0x20}, +- {R367TER_TSSYNC, 0x00}, +- {R367TER_TSINSDELH, 0x00}, +- {R367TER_TSINSDELM, 0x00}, +- {R367TER_TSINSDELL, 0x00}, +- {R367TER_TSDIVN, 0x03}, +- {R367TER_TSDIVPM, 0x00}, +- {R367TER_TSDIVPL, 0x00}, +- {R367TER_TSDIVQM, 0x00}, +- {R367TER_TSDIVQL, 0x00}, +- {R367TER_TSDILSTKM, 0x00}, +- {R367TER_TSDILSTKL, 0x00}, +- {R367TER_TSSPEED, 0x40},/* for xc5000; was 0x6f */ +- {R367TER_TSSTATUS, 0x81}, +- {R367TER_TSSTATUS2, 0x6a}, +- {R367TER_TSBITRATEM, 0x0f}, +- {R367TER_TSBITRATEL, 0xc6}, +- {R367TER_TSPACKLENM, 0x00}, +- {R367TER_TSPACKLENL, 0xfc}, +- {R367TER_TSBLOCLENM, 0x0a}, +- {R367TER_TSBLOCLENL, 0x80}, +- {R367TER_TSDLYH, 0x90}, +- {R367TER_TSDLYM, 0x68}, +- {R367TER_TSDLYL, 0x01}, +- {R367TER_TSNPDAV, 0x00}, +- {R367TER_TSBUFSTATH, 0x00}, +- {R367TER_TSBUFSTATM, 0x00}, +- {R367TER_TSBUFSTATL, 0x00}, +- {R367TER_TSDEBUGM, 0xcf}, +- {R367TER_TSDEBUGL, 0x1e}, +- {R367TER_TSDLYSETH, 0x00}, +- {R367TER_TSDLYSETM, 0x68}, +- {R367TER_TSDLYSETL, 0x00}, +- {R367TER_TSOBSCFG, 0x00}, +- {R367TER_TSOBSM, 0x47}, +- {R367TER_TSOBSL, 0x1f}, +- {R367TER_ERRCTRL1, 0x95}, +- {R367TER_ERRCNT1H, 0x80}, +- {R367TER_ERRCNT1M, 0x00}, +- {R367TER_ERRCNT1L, 0x00}, +- {R367TER_ERRCTRL2, 0x95}, +- {R367TER_ERRCNT2H, 0x00}, +- {R367TER_ERRCNT2M, 0x00}, +- {R367TER_ERRCNT2L, 0x00}, +- {R367TER_FECSPY, 0x88}, +- {R367TER_FSPYCFG, 0x2c}, +- {R367TER_FSPYDATA, 0x3a}, +- {R367TER_FSPYOUT, 0x06}, +- {R367TER_FSTATUS, 0x61}, +- {R367TER_FGOODPACK, 0xff}, +- {R367TER_FPACKCNT, 0xff}, +- {R367TER_FSPYMISC, 0x66}, +- {R367TER_FBERCPT4, 0x00}, +- {R367TER_FBERCPT3, 0x00}, +- {R367TER_FBERCPT2, 0x36}, +- {R367TER_FBERCPT1, 0x36}, +- {R367TER_FBERCPT0, 0x14}, +- {R367TER_FBERERR2, 0x00}, +- {R367TER_FBERERR1, 0x03}, +- {R367TER_FBERERR0, 0x28}, +- {R367TER_FSTATESM, 0x00}, +- {R367TER_FSTATESL, 0x02}, +- {R367TER_FSPYBER, 0x00}, +- {R367TER_FSPYDISTM, 0x01}, +- {R367TER_FSPYDISTL, 0x9f}, +- {R367TER_FSPYOBS7, 0xc9}, +- {R367TER_FSPYOBS6, 0x99}, +- {R367TER_FSPYOBS5, 0x08}, +- {R367TER_FSPYOBS4, 0xec}, +- {R367TER_FSPYOBS3, 0x01}, +- {R367TER_FSPYOBS2, 0x0f}, +- {R367TER_FSPYOBS1, 0xf5}, +- {R367TER_FSPYOBS0, 0x08}, +- {R367TER_SFDEMAP, 0x40}, +- {R367TER_SFERROR, 0x00}, +- {R367TER_SFAVSR, 0x30}, +- {R367TER_SFECSTATUS, 0xcc}, +- {R367TER_SFKDIV12, 0x20}, +- {R367TER_SFKDIV23, 0x40}, +- {R367TER_SFKDIV34, 0x20}, +- {R367TER_SFKDIV56, 0x20}, +- {R367TER_SFKDIV67, 0x00}, +- {R367TER_SFKDIV78, 0x20}, +- {R367TER_SFDILSTKM, 0x00}, +- {R367TER_SFDILSTKL, 0x00}, +- {R367TER_SFSTATUS, 0xb5}, +- {R367TER_SFDLYH, 0x90}, +- {R367TER_SFDLYM, 0x60}, +- {R367TER_SFDLYL, 0x01}, +- {R367TER_SFDLYSETH, 0xc0}, +- {R367TER_SFDLYSETM, 0x60}, +- {R367TER_SFDLYSETL, 0x00}, +- {R367TER_SFOBSCFG, 0x00}, +- {R367TER_SFOBSM, 0x47}, +- {R367TER_SFOBSL, 0x05}, +- {R367TER_SFECINFO, 0x40}, +- {R367TER_SFERRCTRL, 0x74}, +- {R367TER_SFERRCNTH, 0x80}, +- {R367TER_SFERRCNTM , 0x00}, +- {R367TER_SFERRCNTL, 0x00}, +- {R367TER_SYMBRATEM, 0x2f}, +- {R367TER_SYMBRATEL, 0x50}, +- {R367TER_SYMBSTATUS, 0x7f}, +- {R367TER_SYMBCFG, 0x00}, +- {R367TER_SYMBFIFOM, 0xf4}, +- {R367TER_SYMBFIFOL, 0x0d}, +- {R367TER_SYMBOFFSM, 0xf0}, +- {R367TER_SYMBOFFSL, 0x2d}, +- {R367TER_DEBUG_LT4, 0x00}, +- {R367TER_DEBUG_LT5, 0x00}, +- {R367TER_DEBUG_LT6, 0x00}, +- {R367TER_DEBUG_LT7, 0x00}, +- {R367TER_DEBUG_LT8, 0x00}, +- {R367TER_DEBUG_LT9, 0x00}, +-}; +- +-#define RF_LOOKUP_TABLE_SIZE 31 +-#define RF_LOOKUP_TABLE2_SIZE 16 +-/* RF Level (for RF AGC->AGC1) Lookup Table, depends on the board and tuner.*/ +-s32 stv0367cab_RF_LookUp1[RF_LOOKUP_TABLE_SIZE][RF_LOOKUP_TABLE_SIZE] = { +- {/*AGC1*/ +- 48, 50, 51, 53, 54, 56, 57, 58, 60, 61, 62, 63, +- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, +- 76, 77, 78, 80, 83, 85, 88, +- }, {/*RF(dbm)*/ +- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, +- 34, 35, 36, 37, 38, 39, 41, 42, 43, 44, 46, 47, +- 49, 50, 52, 53, 54, 55, 56, +- } +-}; +-/* RF Level (for IF AGC->AGC2) Lookup Table, depends on the board and tuner.*/ +-s32 stv0367cab_RF_LookUp2[RF_LOOKUP_TABLE2_SIZE][RF_LOOKUP_TABLE2_SIZE] = { +- {/*AGC2*/ +- 28, 29, 31, 32, 34, 35, 36, 37, +- 38, 39, 40, 41, 42, 43, 44, 45, +- }, {/*RF(dbm)*/ +- 57, 58, 59, 60, 61, 62, 63, 64, +- 65, 66, 67, 68, 69, 70, 71, 72, +- } +-}; +- +-static struct st_register def0367cab[STV0367CAB_NBREGS] = { +- {R367CAB_ID, 0x60}, +- {R367CAB_I2CRPT, 0xa0}, +- /*{R367CAB_I2CRPT, 0x22},*/ +- {R367CAB_TOPCTRL, 0x10}, +- {R367CAB_IOCFG0, 0x80}, +- {R367CAB_DAC0R, 0x00}, +- {R367CAB_IOCFG1, 0x00}, +- {R367CAB_DAC1R, 0x00}, +- {R367CAB_IOCFG2, 0x00}, +- {R367CAB_SDFR, 0x00}, +- {R367CAB_AUX_CLK, 0x00}, +- {R367CAB_FREESYS1, 0x00}, +- {R367CAB_FREESYS2, 0x00}, +- {R367CAB_FREESYS3, 0x00}, +- {R367CAB_GPIO_CFG, 0x55}, +- {R367CAB_GPIO_CMD, 0x01}, +- {R367CAB_TSTRES, 0x00}, +- {R367CAB_ANACTRL, 0x0d},/* was 0x00 need to check - I.M.L.*/ +- {R367CAB_TSTBUS, 0x00}, +- {R367CAB_RF_AGC1, 0xea}, +- {R367CAB_RF_AGC2, 0x82}, +- {R367CAB_ANADIGCTRL, 0x0b}, +- {R367CAB_PLLMDIV, 0x01}, +- {R367CAB_PLLNDIV, 0x08}, +- {R367CAB_PLLSETUP, 0x18}, +- {R367CAB_DUAL_AD12, 0x0C}, /* for xc5000 AGC voltage 1.6V */ +- {R367CAB_TSTBIST, 0x00}, +- {R367CAB_CTRL_1, 0x00}, +- {R367CAB_CTRL_2, 0x03}, +- {R367CAB_IT_STATUS1, 0x2b}, +- {R367CAB_IT_STATUS2, 0x08}, +- {R367CAB_IT_EN1, 0x00}, +- {R367CAB_IT_EN2, 0x00}, +- {R367CAB_CTRL_STATUS, 0x04}, +- {R367CAB_TEST_CTL, 0x00}, +- {R367CAB_AGC_CTL, 0x73}, +- {R367CAB_AGC_IF_CFG, 0x50}, +- {R367CAB_AGC_RF_CFG, 0x00}, +- {R367CAB_AGC_PWM_CFG, 0x03}, +- {R367CAB_AGC_PWR_REF_L, 0x5a}, +- {R367CAB_AGC_PWR_REF_H, 0x00}, +- {R367CAB_AGC_RF_TH_L, 0xff}, +- {R367CAB_AGC_RF_TH_H, 0x07}, +- {R367CAB_AGC_IF_LTH_L, 0x00}, +- {R367CAB_AGC_IF_LTH_H, 0x08}, +- {R367CAB_AGC_IF_HTH_L, 0xff}, +- {R367CAB_AGC_IF_HTH_H, 0x07}, +- {R367CAB_AGC_PWR_RD_L, 0xa0}, +- {R367CAB_AGC_PWR_RD_M, 0xe9}, +- {R367CAB_AGC_PWR_RD_H, 0x03}, +- {R367CAB_AGC_PWM_IFCMD_L, 0xe4}, +- {R367CAB_AGC_PWM_IFCMD_H, 0x00}, +- {R367CAB_AGC_PWM_RFCMD_L, 0xff}, +- {R367CAB_AGC_PWM_RFCMD_H, 0x07}, +- {R367CAB_IQDEM_CFG, 0x01}, +- {R367CAB_MIX_NCO_LL, 0x22}, +- {R367CAB_MIX_NCO_HL, 0x96}, +- {R367CAB_MIX_NCO_HH, 0x55}, +- {R367CAB_SRC_NCO_LL, 0xff}, +- {R367CAB_SRC_NCO_LH, 0x0c}, +- {R367CAB_SRC_NCO_HL, 0xf5}, +- {R367CAB_SRC_NCO_HH, 0x20}, +- {R367CAB_IQDEM_GAIN_SRC_L, 0x06}, +- {R367CAB_IQDEM_GAIN_SRC_H, 0x01}, +- {R367CAB_IQDEM_DCRM_CFG_LL, 0xfe}, +- {R367CAB_IQDEM_DCRM_CFG_LH, 0xff}, +- {R367CAB_IQDEM_DCRM_CFG_HL, 0x0f}, +- {R367CAB_IQDEM_DCRM_CFG_HH, 0x00}, +- {R367CAB_IQDEM_ADJ_COEFF0, 0x34}, +- {R367CAB_IQDEM_ADJ_COEFF1, 0xae}, +- {R367CAB_IQDEM_ADJ_COEFF2, 0x46}, +- {R367CAB_IQDEM_ADJ_COEFF3, 0x77}, +- {R367CAB_IQDEM_ADJ_COEFF4, 0x96}, +- {R367CAB_IQDEM_ADJ_COEFF5, 0x69}, +- {R367CAB_IQDEM_ADJ_COEFF6, 0xc7}, +- {R367CAB_IQDEM_ADJ_COEFF7, 0x01}, +- {R367CAB_IQDEM_ADJ_EN, 0x04}, +- {R367CAB_IQDEM_ADJ_AGC_REF, 0x94}, +- {R367CAB_ALLPASSFILT1, 0xc9}, +- {R367CAB_ALLPASSFILT2, 0x2d}, +- {R367CAB_ALLPASSFILT3, 0xa3}, +- {R367CAB_ALLPASSFILT4, 0xfb}, +- {R367CAB_ALLPASSFILT5, 0xf6}, +- {R367CAB_ALLPASSFILT6, 0x45}, +- {R367CAB_ALLPASSFILT7, 0x6f}, +- {R367CAB_ALLPASSFILT8, 0x7e}, +- {R367CAB_ALLPASSFILT9, 0x05}, +- {R367CAB_ALLPASSFILT10, 0x0a}, +- {R367CAB_ALLPASSFILT11, 0x51}, +- {R367CAB_TRL_AGC_CFG, 0x20}, +- {R367CAB_TRL_LPF_CFG, 0x28}, +- {R367CAB_TRL_LPF_ACQ_GAIN, 0x44}, +- {R367CAB_TRL_LPF_TRK_GAIN, 0x22}, +- {R367CAB_TRL_LPF_OUT_GAIN, 0x03}, +- {R367CAB_TRL_LOCKDET_LTH, 0x04}, +- {R367CAB_TRL_LOCKDET_HTH, 0x11}, +- {R367CAB_TRL_LOCKDET_TRGVAL, 0x20}, +- {R367CAB_IQ_QAM, 0x01}, +- {R367CAB_FSM_STATE, 0xa0}, +- {R367CAB_FSM_CTL, 0x08}, +- {R367CAB_FSM_STS, 0x0c}, +- {R367CAB_FSM_SNR0_HTH, 0x00}, +- {R367CAB_FSM_SNR1_HTH, 0x00}, +- {R367CAB_FSM_SNR2_HTH, 0x23},/* 0x00 */ +- {R367CAB_FSM_SNR0_LTH, 0x00}, +- {R367CAB_FSM_SNR1_LTH, 0x00}, +- {R367CAB_FSM_EQA1_HTH, 0x00}, +- {R367CAB_FSM_TEMPO, 0x32}, +- {R367CAB_FSM_CONFIG, 0x03}, +- {R367CAB_EQU_I_TESTTAP_L, 0x11}, +- {R367CAB_EQU_I_TESTTAP_M, 0x00}, +- {R367CAB_EQU_I_TESTTAP_H, 0x00}, +- {R367CAB_EQU_TESTAP_CFG, 0x00}, +- {R367CAB_EQU_Q_TESTTAP_L, 0xff}, +- {R367CAB_EQU_Q_TESTTAP_M, 0x00}, +- {R367CAB_EQU_Q_TESTTAP_H, 0x00}, +- {R367CAB_EQU_TAP_CTRL, 0x00}, +- {R367CAB_EQU_CTR_CRL_CONTROL_L, 0x11}, +- {R367CAB_EQU_CTR_CRL_CONTROL_H, 0x05}, +- {R367CAB_EQU_CTR_HIPOW_L, 0x00}, +- {R367CAB_EQU_CTR_HIPOW_H, 0x00}, +- {R367CAB_EQU_I_EQU_LO, 0xef}, +- {R367CAB_EQU_I_EQU_HI, 0x00}, +- {R367CAB_EQU_Q_EQU_LO, 0xee}, +- {R367CAB_EQU_Q_EQU_HI, 0x00}, +- {R367CAB_EQU_MAPPER, 0xc5}, +- {R367CAB_EQU_SWEEP_RATE, 0x80}, +- {R367CAB_EQU_SNR_LO, 0x64}, +- {R367CAB_EQU_SNR_HI, 0x03}, +- {R367CAB_EQU_GAMMA_LO, 0x00}, +- {R367CAB_EQU_GAMMA_HI, 0x00}, +- {R367CAB_EQU_ERR_GAIN, 0x36}, +- {R367CAB_EQU_RADIUS, 0xaa}, +- {R367CAB_EQU_FFE_MAINTAP, 0x00}, +- {R367CAB_EQU_FFE_LEAKAGE, 0x63}, +- {R367CAB_EQU_FFE_MAINTAP_POS, 0xdf}, +- {R367CAB_EQU_GAIN_WIDE, 0x88}, +- {R367CAB_EQU_GAIN_NARROW, 0x41}, +- {R367CAB_EQU_CTR_LPF_GAIN, 0xd1}, +- {R367CAB_EQU_CRL_LPF_GAIN, 0xa7}, +- {R367CAB_EQU_GLOBAL_GAIN, 0x06}, +- {R367CAB_EQU_CRL_LD_SEN, 0x85}, +- {R367CAB_EQU_CRL_LD_VAL, 0xe2}, +- {R367CAB_EQU_CRL_TFR, 0x20}, +- {R367CAB_EQU_CRL_BISTH_LO, 0x00}, +- {R367CAB_EQU_CRL_BISTH_HI, 0x00}, +- {R367CAB_EQU_SWEEP_RANGE_LO, 0x00}, +- {R367CAB_EQU_SWEEP_RANGE_HI, 0x00}, +- {R367CAB_EQU_CRL_LIMITER, 0x40}, +- {R367CAB_EQU_MODULUS_MAP, 0x90}, +- {R367CAB_EQU_PNT_GAIN, 0xa7}, +- {R367CAB_FEC_AC_CTR_0, 0x16}, +- {R367CAB_FEC_AC_CTR_1, 0x0b}, +- {R367CAB_FEC_AC_CTR_2, 0x88}, +- {R367CAB_FEC_AC_CTR_3, 0x02}, +- {R367CAB_FEC_STATUS, 0x12}, +- {R367CAB_RS_COUNTER_0, 0x7d}, +- {R367CAB_RS_COUNTER_1, 0xd0}, +- {R367CAB_RS_COUNTER_2, 0x19}, +- {R367CAB_RS_COUNTER_3, 0x0b}, +- {R367CAB_RS_COUNTER_4, 0xa3}, +- {R367CAB_RS_COUNTER_5, 0x00}, +- {R367CAB_BERT_0, 0x01}, +- {R367CAB_BERT_1, 0x25}, +- {R367CAB_BERT_2, 0x41}, +- {R367CAB_BERT_3, 0x39}, +- {R367CAB_OUTFORMAT_0, 0xc2}, +- {R367CAB_OUTFORMAT_1, 0x22}, +- {R367CAB_SMOOTHER_2, 0x28}, +- {R367CAB_TSMF_CTRL_0, 0x01}, +- {R367CAB_TSMF_CTRL_1, 0xc6}, +- {R367CAB_TSMF_CTRL_3, 0x43}, +- {R367CAB_TS_ON_ID_0, 0x00}, +- {R367CAB_TS_ON_ID_1, 0x00}, +- {R367CAB_TS_ON_ID_2, 0x00}, +- {R367CAB_TS_ON_ID_3, 0x00}, +- {R367CAB_RE_STATUS_0, 0x00}, +- {R367CAB_RE_STATUS_1, 0x00}, +- {R367CAB_RE_STATUS_2, 0x00}, +- {R367CAB_RE_STATUS_3, 0x00}, +- {R367CAB_TS_STATUS_0, 0x00}, +- {R367CAB_TS_STATUS_1, 0x00}, +- {R367CAB_TS_STATUS_2, 0xa0}, +- {R367CAB_TS_STATUS_3, 0x00}, +- {R367CAB_T_O_ID_0, 0x00}, +- {R367CAB_T_O_ID_1, 0x00}, +- {R367CAB_T_O_ID_2, 0x00}, +- {R367CAB_T_O_ID_3, 0x00}, +-}; +- +-static +-int stv0367_writeregs(struct stv0367_state *state, u16 reg, u8 *data, int len) +-{ +- u8 buf[len + 2]; +- struct i2c_msg msg = { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = buf, +- .len = len + 2 +- }; +- int ret; +- +- buf[0] = MSB(reg); +- buf[1] = LSB(reg); +- memcpy(buf + 2, data, len); +- +- if (i2cdebug) +- printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, buf[2]); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) +- printk(KERN_ERR "%s: i2c write error!\n", __func__); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static int stv0367_writereg(struct stv0367_state *state, u16 reg, u8 data) +-{ +- return stv0367_writeregs(state, reg, &data, 1); +-} +- +-static u8 stv0367_readreg(struct stv0367_state *state, u16 reg) +-{ +- u8 b0[] = { 0, 0 }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { +- .addr = state->config->demod_address, +- .flags = 0, +- .buf = b0, +- .len = 2 +- }, { +- .addr = state->config->demod_address, +- .flags = I2C_M_RD, +- .buf = b1, +- .len = 1 +- } +- }; +- int ret; +- +- b0[0] = MSB(reg); +- b0[1] = LSB(reg); +- +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) +- printk(KERN_ERR "%s: i2c read error\n", __func__); +- +- if (i2cdebug) +- printk(KERN_DEBUG "%s: %02x: %02x\n", __func__, reg, b1[0]); +- +- return b1[0]; +-} +- +-static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +-{ +- u8 position = 0, i = 0; +- +- (*mask) = label & 0xff; +- +- while ((position == 0) && (i < 8)) { +- position = ((*mask) >> i) & 0x01; +- i++; +- } +- +- (*pos) = (i - 1); +-} +- +-static void stv0367_writebits(struct stv0367_state *state, u32 label, u8 val) +-{ +- u8 reg, mask, pos; +- +- reg = stv0367_readreg(state, (label >> 16) & 0xffff); +- extract_mask_pos(label, &mask, &pos); +- +- val = mask & (val << pos); +- +- reg = (reg & (~mask)) | val; +- stv0367_writereg(state, (label >> 16) & 0xffff, reg); +- +-} +- +-static void stv0367_setbits(u8 *reg, u32 label, u8 val) +-{ +- u8 mask, pos; +- +- extract_mask_pos(label, &mask, &pos); +- +- val = mask & (val << pos); +- +- (*reg) = ((*reg) & (~mask)) | val; +-} +- +-static u8 stv0367_readbits(struct stv0367_state *state, u32 label) +-{ +- u8 val = 0xff; +- u8 mask, pos; +- +- extract_mask_pos(label, &mask, &pos); +- +- val = stv0367_readreg(state, label >> 16); +- val = (val & mask) >> pos; +- +- return val; +-} +- +-u8 stv0367_getbits(u8 reg, u32 label) +-{ +- u8 mask, pos; +- +- extract_mask_pos(label, &mask, &pos); +- +- return (reg & mask) >> pos; +-} +- +-static int stv0367ter_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- u8 tmp = stv0367_readreg(state, R367TER_I2CRPT); +- +- dprintk("%s:\n", __func__); +- +- if (enable) { +- stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 0); +- stv0367_setbits(&tmp, F367TER_I2CT_ON, 1); +- } else { +- stv0367_setbits(&tmp, F367TER_STOP_ENABLE, 1); +- stv0367_setbits(&tmp, F367TER_I2CT_ON, 0); +- } +- +- stv0367_writereg(state, R367TER_I2CRPT, tmp); +- +- return 0; +-} +- +-static u32 stv0367_get_tuner_freq(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- u32 freq = 0; +- int err = 0; +- +- dprintk("%s:\n", __func__); +- +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_frequency) { +- err = tuner_ops->get_frequency(fe, &freq); +- if (err < 0) { +- printk(KERN_ERR "%s: Invalid parameter\n", __func__); +- return err; +- } +- +- dprintk("%s: frequency=%d\n", __func__, freq); +- +- } else +- return -1; +- +- return freq; +-} +- +-static u16 CellsCoeffs_8MHz_367cofdm[3][6][5] = { +- { +- {0x10EF, 0xE205, 0x10EF, 0xCE49, 0x6DA7}, /* CELL 1 COEFFS 27M*/ +- {0x2151, 0xc557, 0x2151, 0xc705, 0x6f93}, /* CELL 2 COEFFS */ +- {0x2503, 0xc000, 0x2503, 0xc375, 0x7194}, /* CELL 3 COEFFS */ +- {0x20E9, 0xca94, 0x20e9, 0xc153, 0x7194}, /* CELL 4 COEFFS */ +- {0x06EF, 0xF852, 0x06EF, 0xC057, 0x7207}, /* CELL 5 COEFFS */ +- {0x0000, 0x0ECC, 0x0ECC, 0x0000, 0x3647} /* CELL 6 COEFFS */ +- }, { +- {0x10A0, 0xE2AF, 0x10A1, 0xCE76, 0x6D6D}, /* CELL 1 COEFFS 25M*/ +- {0x20DC, 0xC676, 0x20D9, 0xC80A, 0x6F29}, +- {0x2532, 0xC000, 0x251D, 0xC391, 0x706F}, +- {0x1F7A, 0xCD2B, 0x2032, 0xC15E, 0x711F}, +- {0x0698, 0xFA5E, 0x0568, 0xC059, 0x7193}, +- {0x0000, 0x0918, 0x149C, 0x0000, 0x3642} /* CELL 6 COEFFS */ +- }, { +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} +- } +-}; +- +-static u16 CellsCoeffs_7MHz_367cofdm[3][6][5] = { +- { +- {0x12CA, 0xDDAF, 0x12CA, 0xCCEB, 0x6FB1}, /* CELL 1 COEFFS 27M*/ +- {0x2329, 0xC000, 0x2329, 0xC6B0, 0x725F}, /* CELL 2 COEFFS */ +- {0x2394, 0xC000, 0x2394, 0xC2C7, 0x7410}, /* CELL 3 COEFFS */ +- {0x251C, 0xC000, 0x251C, 0xC103, 0x74D9}, /* CELL 4 COEFFS */ +- {0x0804, 0xF546, 0x0804, 0xC040, 0x7544}, /* CELL 5 COEFFS */ +- {0x0000, 0x0CD9, 0x0CD9, 0x0000, 0x370A} /* CELL 6 COEFFS */ +- }, { +- {0x1285, 0xDE47, 0x1285, 0xCD17, 0x6F76}, /*25M*/ +- {0x234C, 0xC000, 0x2348, 0xC6DA, 0x7206}, +- {0x23B4, 0xC000, 0x23AC, 0xC2DB, 0x73B3}, +- {0x253D, 0xC000, 0x25B6, 0xC10B, 0x747F}, +- {0x0721, 0xF79C, 0x065F, 0xC041, 0x74EB}, +- {0x0000, 0x08FA, 0x1162, 0x0000, 0x36FF} +- }, { +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} +- } +-}; +- +-static u16 CellsCoeffs_6MHz_367cofdm[3][6][5] = { +- { +- {0x1699, 0xD5B8, 0x1699, 0xCBC3, 0x713B}, /* CELL 1 COEFFS 27M*/ +- {0x2245, 0xC000, 0x2245, 0xC568, 0x74D5}, /* CELL 2 COEFFS */ +- {0x227F, 0xC000, 0x227F, 0xC1FC, 0x76C6}, /* CELL 3 COEFFS */ +- {0x235E, 0xC000, 0x235E, 0xC0A7, 0x778A}, /* CELL 4 COEFFS */ +- {0x0ECB, 0xEA0B, 0x0ECB, 0xC027, 0x77DD}, /* CELL 5 COEFFS */ +- {0x0000, 0x0B68, 0x0B68, 0x0000, 0xC89A}, /* CELL 6 COEFFS */ +- }, { +- {0x1655, 0xD64E, 0x1658, 0xCBEF, 0x70FE}, /*25M*/ +- {0x225E, 0xC000, 0x2256, 0xC589, 0x7489}, +- {0x2293, 0xC000, 0x2295, 0xC209, 0x767E}, +- {0x2377, 0xC000, 0x23AA, 0xC0AB, 0x7746}, +- {0x0DC7, 0xEBC8, 0x0D07, 0xC027, 0x7799}, +- {0x0000, 0x0888, 0x0E9C, 0x0000, 0x3757} +- +- }, { +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /* 30M */ +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, +- {0x0000, 0x0000, 0x0000, 0x0000, 0x0000} +- } +-}; +- +-static u32 stv0367ter_get_mclk(struct stv0367_state *state, u32 ExtClk_Hz) +-{ +- u32 mclk_Hz = 0; /* master clock frequency (Hz) */ +- u32 m, n, p; +- +- dprintk("%s:\n", __func__); +- +- if (stv0367_readbits(state, F367TER_BYPASS_PLLXN) == 0) { +- n = (u32)stv0367_readbits(state, F367TER_PLL_NDIV); +- if (n == 0) +- n = n + 1; +- +- m = (u32)stv0367_readbits(state, F367TER_PLL_MDIV); +- if (m == 0) +- m = m + 1; +- +- p = (u32)stv0367_readbits(state, F367TER_PLL_PDIV); +- if (p > 5) +- p = 5; +- +- mclk_Hz = ((ExtClk_Hz / 2) * n) / (m * (1 << p)); +- +- dprintk("N=%d M=%d P=%d mclk_Hz=%d ExtClk_Hz=%d\n", +- n, m, p, mclk_Hz, ExtClk_Hz); +- } else +- mclk_Hz = ExtClk_Hz; +- +- dprintk("%s: mclk_Hz=%d\n", __func__, mclk_Hz); +- +- return mclk_Hz; +-} +- +-static int stv0367ter_filt_coeff_init(struct stv0367_state *state, +- u16 CellsCoeffs[3][6][5], u32 DemodXtal) +-{ +- int i, j, k, freq; +- +- dprintk("%s:\n", __func__); +- +- freq = stv0367ter_get_mclk(state, DemodXtal); +- +- if (freq == 53125000) +- k = 1; /* equivalent to Xtal 25M on 362*/ +- else if (freq == 54000000) +- k = 0; /* equivalent to Xtal 27M on 362*/ +- else if (freq == 52500000) +- k = 2; /* equivalent to Xtal 30M on 362*/ +- else +- return 0; +- +- for (i = 1; i <= 6; i++) { +- stv0367_writebits(state, F367TER_IIR_CELL_NB, i - 1); +- +- for (j = 1; j <= 5; j++) { +- stv0367_writereg(state, +- (R367TER_IIRCX_COEFF1_MSB + 2 * (j - 1)), +- MSB(CellsCoeffs[k][i-1][j-1])); +- stv0367_writereg(state, +- (R367TER_IIRCX_COEFF1_LSB + 2 * (j - 1)), +- LSB(CellsCoeffs[k][i-1][j-1])); +- } +- } +- +- return 1; +- +-} +- +-static void stv0367ter_agc_iir_lock_detect_set(struct stv0367_state *state) +-{ +- dprintk("%s:\n", __func__); +- +- stv0367_writebits(state, F367TER_LOCK_DETECT_LSB, 0x00); +- +- /* Lock detect 1 */ +- stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x00); +- stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); +- +- /* Lock detect 2 */ +- stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x01); +- stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x06); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x04); +- +- /* Lock detect 3 */ +- stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x02); +- stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); +- +- /* Lock detect 4 */ +- stv0367_writebits(state, F367TER_LOCK_DETECT_CHOICE, 0x03); +- stv0367_writebits(state, F367TER_LOCK_DETECT_MSB, 0x01); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_LSB, 0x00); +- +-} +- +-static int stv0367_iir_filt_init(struct stv0367_state *state, u8 Bandwidth, +- u32 DemodXtalValue) +-{ +- dprintk("%s:\n", __func__); +- +- stv0367_writebits(state, F367TER_NRST_IIR, 0); +- +- switch (Bandwidth) { +- case 6: +- if (!stv0367ter_filt_coeff_init(state, +- CellsCoeffs_6MHz_367cofdm, +- DemodXtalValue)) +- return 0; +- break; +- case 7: +- if (!stv0367ter_filt_coeff_init(state, +- CellsCoeffs_7MHz_367cofdm, +- DemodXtalValue)) +- return 0; +- break; +- case 8: +- if (!stv0367ter_filt_coeff_init(state, +- CellsCoeffs_8MHz_367cofdm, +- DemodXtalValue)) +- return 0; +- break; +- default: +- return 0; +- } +- +- stv0367_writebits(state, F367TER_NRST_IIR, 1); +- +- return 1; +-} +- +-static void stv0367ter_agc_iir_rst(struct stv0367_state *state) +-{ +- +- u8 com_n; +- +- dprintk("%s:\n", __func__); +- +- com_n = stv0367_readbits(state, F367TER_COM_N); +- +- stv0367_writebits(state, F367TER_COM_N, 0x07); +- +- stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x00); +- stv0367_writebits(state, F367TER_COM_AGC_ON, 0x00); +- +- stv0367_writebits(state, F367TER_COM_SOFT_RSTN, 0x01); +- stv0367_writebits(state, F367TER_COM_AGC_ON, 0x01); +- +- stv0367_writebits(state, F367TER_COM_N, com_n); +- +-} +- +-static int stv0367ter_duration(s32 mode, int tempo1, int tempo2, int tempo3) +-{ +- int local_tempo = 0; +- switch (mode) { +- case 0: +- local_tempo = tempo1; +- break; +- case 1: +- local_tempo = tempo2; +- break ; +- +- case 2: +- local_tempo = tempo3; +- break; +- +- default: +- break; +- } +- /* msleep(local_tempo); */ +- return local_tempo; +-} +- +-static enum +-stv0367_ter_signal_type stv0367ter_check_syr(struct stv0367_state *state) +-{ +- int wd = 100; +- unsigned short int SYR_var; +- s32 SYRStatus; +- +- dprintk("%s:\n", __func__); +- +- SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); +- +- while ((!SYR_var) && (wd > 0)) { +- usleep_range(2000, 3000); +- wd -= 2; +- SYR_var = stv0367_readbits(state, F367TER_SYR_LOCK); +- } +- +- if (!SYR_var) +- SYRStatus = FE_TER_NOSYMBOL; +- else +- SYRStatus = FE_TER_SYMBOLOK; +- +- dprintk("stv0367ter_check_syr SYRStatus %s\n", +- SYR_var == 0 ? "No Symbol" : "OK"); +- +- return SYRStatus; +-} +- +-static enum +-stv0367_ter_signal_type stv0367ter_check_cpamp(struct stv0367_state *state, +- s32 FFTmode) +-{ +- +- s32 CPAMPvalue = 0, CPAMPStatus, CPAMPMin; +- int wd = 0; +- +- dprintk("%s:\n", __func__); +- +- switch (FFTmode) { +- case 0: /*2k mode*/ +- CPAMPMin = 20; +- wd = 10; +- break; +- case 1: /*8k mode*/ +- CPAMPMin = 80; +- wd = 55; +- break; +- case 2: /*4k mode*/ +- CPAMPMin = 40; +- wd = 30; +- break; +- default: +- CPAMPMin = 0xffff; /*drives to NOCPAMP */ +- break; +- } +- +- dprintk("%s: CPAMPMin=%d wd=%d\n", __func__, CPAMPMin, wd); +- +- CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); +- while ((CPAMPvalue < CPAMPMin) && (wd > 0)) { +- usleep_range(1000, 2000); +- wd -= 1; +- CPAMPvalue = stv0367_readbits(state, F367TER_PPM_CPAMP_DIRECT); +- /*dprintk("CPAMPvalue= %d at wd=%d\n",CPAMPvalue,wd); */ +- } +- dprintk("******last CPAMPvalue= %d at wd=%d\n", CPAMPvalue, wd); +- if (CPAMPvalue < CPAMPMin) { +- CPAMPStatus = FE_TER_NOCPAMP; +- printk(KERN_ERR "CPAMP failed\n"); +- } else { +- printk(KERN_ERR "CPAMP OK !\n"); +- CPAMPStatus = FE_TER_CPAMPOK; +- } +- +- return CPAMPStatus; +-} +- +-enum +-stv0367_ter_signal_type stv0367ter_lock_algo(struct stv0367_state *state) +-{ +- enum stv0367_ter_signal_type ret_flag; +- short int wd, tempo; +- u8 try, u_var1 = 0, u_var2 = 0, u_var3 = 0, u_var4 = 0, mode, guard; +- u8 tmp, tmp2; +- +- dprintk("%s:\n", __func__); +- +- if (state == NULL) +- return FE_TER_SWNOK; +- +- try = 0; +- do { +- ret_flag = FE_TER_LOCKOK; +- +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); +- +- if (state->config->if_iq_mode != 0) +- stv0367_writebits(state, F367TER_COM_N, 0x07); +- +- stv0367_writebits(state, F367TER_GUARD, 3);/* suggest 2k 1/4 */ +- stv0367_writebits(state, F367TER_MODE, 0); +- stv0367_writebits(state, F367TER_SYR_TR_DIS, 0); +- usleep_range(5000, 10000); +- +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); +- +- +- if (stv0367ter_check_syr(state) == FE_TER_NOSYMBOL) +- return FE_TER_NOSYMBOL; +- else { /* +- if chip locked on wrong mode first try, +- it must lock correctly second try */ +- mode = stv0367_readbits(state, F367TER_SYR_MODE); +- if (stv0367ter_check_cpamp(state, mode) == +- FE_TER_NOCPAMP) { +- if (try == 0) +- ret_flag = FE_TER_NOCPAMP; +- +- } +- } +- +- try++; +- } while ((try < 10) && (ret_flag != FE_TER_LOCKOK)); +- +- tmp = stv0367_readreg(state, R367TER_SYR_STAT); +- tmp2 = stv0367_readreg(state, R367TER_STATUS); +- dprintk("state=%p\n", state); +- dprintk("LOCK OK! mode=%d SYR_STAT=0x%x R367TER_STATUS=0x%x\n", +- mode, tmp, tmp2); +- +- tmp = stv0367_readreg(state, R367TER_PRVIT); +- tmp2 = stv0367_readreg(state, R367TER_I2CRPT); +- dprintk("PRVIT=0x%x I2CRPT=0x%x\n", tmp, tmp2); +- +- tmp = stv0367_readreg(state, R367TER_GAIN_SRC1); +- dprintk("GAIN_SRC1=0x%x\n", tmp); +- +- if ((mode != 0) && (mode != 1) && (mode != 2)) +- return FE_TER_SWNOK; +- +- /*guard=stv0367_readbits(state,F367TER_SYR_GUARD); */ +- +- /*suppress EPQ auto for SYR_GARD 1/16 or 1/32 +- and set channel predictor in automatic */ +-#if 0 +- switch (guard) { +- +- case 0: +- case 1: +- stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); +- stv0367_writereg(state, R367TER_CHC_CTL, 0x01); +- break; +- case 2: +- case 3: +- stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); +- stv0367_writereg(state, R367TER_CHC_CTL, 0x11); +- break; +- +- default: +- return FE_TER_SWNOK; +- } +-#endif +- +- /*reset fec an reedsolo FOR 367 only*/ +- stv0367_writebits(state, F367TER_RST_SFEC, 1); +- stv0367_writebits(state, F367TER_RST_REEDSOLO, 1); +- usleep_range(1000, 2000); +- stv0367_writebits(state, F367TER_RST_SFEC, 0); +- stv0367_writebits(state, F367TER_RST_REEDSOLO, 0); +- +- u_var1 = stv0367_readbits(state, F367TER_LK); +- u_var2 = stv0367_readbits(state, F367TER_PRF); +- u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); +- /* u_var4=stv0367_readbits(state,F367TER_TSFIFO_LINEOK); */ +- +- wd = stv0367ter_duration(mode, 125, 500, 250); +- tempo = stv0367ter_duration(mode, 4, 16, 8); +- +- /*while ( ((!u_var1)||(!u_var2)||(!u_var3)||(!u_var4)) && (wd>=0)) */ +- while (((!u_var1) || (!u_var2) || (!u_var3)) && (wd >= 0)) { +- usleep_range(1000 * tempo, 1000 * (tempo + 1)); +- wd -= tempo; +- u_var1 = stv0367_readbits(state, F367TER_LK); +- u_var2 = stv0367_readbits(state, F367TER_PRF); +- u_var3 = stv0367_readbits(state, F367TER_TPS_LOCK); +- /*u_var4=stv0367_readbits(state, F367TER_TSFIFO_LINEOK); */ +- } +- +- if (!u_var1) +- return FE_TER_NOLOCK; +- +- +- if (!u_var2) +- return FE_TER_NOPRFOUND; +- +- if (!u_var3) +- return FE_TER_NOTPS; +- +- guard = stv0367_readbits(state, F367TER_SYR_GUARD); +- stv0367_writereg(state, R367TER_CHC_CTL, 0x11); +- switch (guard) { +- case 0: +- case 1: +- stv0367_writebits(state, F367TER_AUTO_LE_EN, 0); +- /*stv0367_writereg(state,R367TER_CHC_CTL, 0x1);*/ +- stv0367_writebits(state, F367TER_SYR_FILTER, 0); +- break; +- case 2: +- case 3: +- stv0367_writebits(state, F367TER_AUTO_LE_EN, 1); +- /*stv0367_writereg(state,R367TER_CHC_CTL, 0x11);*/ +- stv0367_writebits(state, F367TER_SYR_FILTER, 1); +- break; +- +- default: +- return FE_TER_SWNOK; +- } +- +- /* apply Sfec workaround if 8K 64QAM CR!=1/2*/ +- if ((stv0367_readbits(state, F367TER_TPS_CONST) == 2) && +- (mode == 1) && +- (stv0367_readbits(state, F367TER_TPS_HPCODE) != 0)) { +- stv0367_writereg(state, R367TER_SFDLYSETH, 0xc0); +- stv0367_writereg(state, R367TER_SFDLYSETM, 0x60); +- stv0367_writereg(state, R367TER_SFDLYSETL, 0x0); +- } else +- stv0367_writereg(state, R367TER_SFDLYSETH, 0x0); +- +- wd = stv0367ter_duration(mode, 125, 500, 250); +- u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); +- +- while ((!u_var4) && (wd >= 0)) { +- usleep_range(1000 * tempo, 1000 * (tempo + 1)); +- wd -= tempo; +- u_var4 = stv0367_readbits(state, F367TER_TSFIFO_LINEOK); +- } +- +- if (!u_var4) +- return FE_TER_NOLOCK; +- +- /* for 367 leave COM_N at 0x7 for IQ_mode*/ +- /*if(ter_state->if_iq_mode!=FE_TER_NORMAL_IF_TUNER) { +- tempo=0; +- while ((stv0367_readbits(state,F367TER_COM_USEGAINTRK)!=1) && +- (stv0367_readbits(state,F367TER_COM_AGCLOCK)!=1)&&(tempo<100)) { +- ChipWaitOrAbort(state,1); +- tempo+=1; +- } +- +- stv0367_writebits(state,F367TER_COM_N,0x17); +- } */ +- +- stv0367_writebits(state, F367TER_SYR_TR_DIS, 1); +- +- dprintk("FE_TER_LOCKOK !!!\n"); +- +- return FE_TER_LOCKOK; +- +-} +- +-static void stv0367ter_set_ts_mode(struct stv0367_state *state, +- enum stv0367_ts_mode PathTS) +-{ +- +- dprintk("%s:\n", __func__); +- +- if (state == NULL) +- return; +- +- stv0367_writebits(state, F367TER_TS_DIS, 0); +- switch (PathTS) { +- default: +- /*for removing warning :default we can assume in parallel mode*/ +- case STV0367_PARALLEL_PUNCT_CLOCK: +- stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 0); +- stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 0); +- break; +- case STV0367_SERIAL_PUNCT_CLOCK: +- stv0367_writebits(state, F367TER_TSFIFO_SERIAL, 1); +- stv0367_writebits(state, F367TER_TSFIFO_DVBCI, 1); +- break; +- } +-} +- +-static void stv0367ter_set_clk_pol(struct stv0367_state *state, +- enum stv0367_clk_pol clock) +-{ +- +- dprintk("%s:\n", __func__); +- +- if (state == NULL) +- return; +- +- switch (clock) { +- case STV0367_RISINGEDGE_CLOCK: +- stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 1); +- break; +- case STV0367_FALLINGEDGE_CLOCK: +- stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); +- break; +- /*case FE_TER_CLOCK_POLARITY_DEFAULT:*/ +- default: +- stv0367_writebits(state, F367TER_TS_BYTE_CLK_INV, 0); +- break; +- } +-} +- +-#if 0 +-static void stv0367ter_core_sw(struct stv0367_state *state) +-{ +- +- dprintk("%s:\n", __func__); +- +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); +- msleep(350); +-} +-#endif +-static int stv0367ter_standby(struct dvb_frontend *fe, u8 standby_on) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- dprintk("%s:\n", __func__); +- +- if (standby_on) { +- stv0367_writebits(state, F367TER_STDBY, 1); +- stv0367_writebits(state, F367TER_STDBY_FEC, 1); +- stv0367_writebits(state, F367TER_STDBY_CORE, 1); +- } else { +- stv0367_writebits(state, F367TER_STDBY, 0); +- stv0367_writebits(state, F367TER_STDBY_FEC, 0); +- stv0367_writebits(state, F367TER_STDBY_CORE, 0); +- } +- +- return 0; +-} +- +-static int stv0367ter_sleep(struct dvb_frontend *fe) +-{ +- return stv0367ter_standby(fe, 1); +-} +- +-int stv0367ter_init(struct dvb_frontend *fe) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- int i; +- +- dprintk("%s:\n", __func__); +- +- ter_state->pBER = 0; +- +- for (i = 0; i < STV0367TER_NBREGS; i++) +- stv0367_writereg(state, def0367ter[i].addr, +- def0367ter[i].value); +- +- switch (state->config->xtal) { +- /*set internal freq to 53.125MHz */ +- case 25000000: +- stv0367_writereg(state, R367TER_PLLMDIV, 0xa); +- stv0367_writereg(state, R367TER_PLLNDIV, 0x55); +- stv0367_writereg(state, R367TER_PLLSETUP, 0x18); +- break; +- default: +- case 27000000: +- dprintk("FE_STV0367TER_SetCLKgen for 27Mhz\n"); +- stv0367_writereg(state, R367TER_PLLMDIV, 0x1); +- stv0367_writereg(state, R367TER_PLLNDIV, 0x8); +- stv0367_writereg(state, R367TER_PLLSETUP, 0x18); +- break; +- case 30000000: +- stv0367_writereg(state, R367TER_PLLMDIV, 0xc); +- stv0367_writereg(state, R367TER_PLLNDIV, 0x55); +- stv0367_writereg(state, R367TER_PLLSETUP, 0x18); +- break; +- } +- +- stv0367_writereg(state, R367TER_I2CRPT, 0xa0); +- stv0367_writereg(state, R367TER_ANACTRL, 0x00); +- +- /*Set TS1 and TS2 to serial or parallel mode */ +- stv0367ter_set_ts_mode(state, state->config->ts_mode); +- stv0367ter_set_clk_pol(state, state->config->clk_pol); +- +- state->chip_id = stv0367_readreg(state, R367TER_ID); +- ter_state->first_lock = 0; +- ter_state->unlock_counter = 2; +- +- return 0; +-} +- +-static int stv0367ter_algo(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- int offset = 0, tempo = 0; +- u8 u_var; +- u8 /*constell,*/ counter, tps_rcvd[2]; +- s8 step; +- s32 timing_offset = 0; +- u32 trl_nomrate = 0, InternalFreq = 0, temp = 0; +- +- dprintk("%s:\n", __func__); +- +- ter_state->frequency = p->frequency; +- ter_state->force = FE_TER_FORCENONE +- + stv0367_readbits(state, F367TER_FORCE) * 2; +- ter_state->if_iq_mode = state->config->if_iq_mode; +- switch (state->config->if_iq_mode) { +- case FE_TER_NORMAL_IF_TUNER: /* Normal IF mode */ +- dprintk("ALGO: FE_TER_NORMAL_IF_TUNER selected\n"); +- stv0367_writebits(state, F367TER_TUNER_BB, 0); +- stv0367_writebits(state, F367TER_LONGPATH_IF, 0); +- stv0367_writebits(state, F367TER_DEMUX_SWAP, 0); +- break; +- case FE_TER_LONGPATH_IF_TUNER: /* Long IF mode */ +- dprintk("ALGO: FE_TER_LONGPATH_IF_TUNER selected\n"); +- stv0367_writebits(state, F367TER_TUNER_BB, 0); +- stv0367_writebits(state, F367TER_LONGPATH_IF, 1); +- stv0367_writebits(state, F367TER_DEMUX_SWAP, 1); +- break; +- case FE_TER_IQ_TUNER: /* IQ mode */ +- dprintk("ALGO: FE_TER_IQ_TUNER selected\n"); +- stv0367_writebits(state, F367TER_TUNER_BB, 1); +- stv0367_writebits(state, F367TER_PPM_INVSEL, 0); +- break; +- default: +- printk(KERN_ERR "ALGO: wrong TUNER type selected\n"); +- return -EINVAL; +- } +- +- usleep_range(5000, 7000); +- +- switch (p->inversion) { +- case INVERSION_AUTO: +- default: +- dprintk("%s: inversion AUTO\n", __func__); +- if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) +- stv0367_writebits(state, F367TER_IQ_INVERT, +- ter_state->sense); +- else +- stv0367_writebits(state, F367TER_INV_SPECTR, +- ter_state->sense); +- +- break; +- case INVERSION_ON: +- case INVERSION_OFF: +- if (ter_state->if_iq_mode == FE_TER_IQ_TUNER) +- stv0367_writebits(state, F367TER_IQ_INVERT, +- p->inversion); +- else +- stv0367_writebits(state, F367TER_INV_SPECTR, +- p->inversion); +- +- break; +- } +- +- if ((ter_state->if_iq_mode != FE_TER_NORMAL_IF_TUNER) && +- (ter_state->pBW != ter_state->bw)) { +- stv0367ter_agc_iir_lock_detect_set(state); +- +- /*set fine agc target to 180 for LPIF or IQ mode*/ +- /* set Q_AGCTarget */ +- stv0367_writebits(state, F367TER_SEL_IQNTAR, 1); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); +- /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ +- +- /* set Q_AGCTarget */ +- stv0367_writebits(state, F367TER_SEL_IQNTAR, 0); +- stv0367_writebits(state, F367TER_AUT_AGC_TARGET_MSB, 0xB); +- /*stv0367_writebits(state,AUT_AGC_TARGET_LSB,0x04); */ +- +- if (!stv0367_iir_filt_init(state, ter_state->bw, +- state->config->xtal)) +- return -EINVAL; +- /*set IIR filter once for 6,7 or 8MHz BW*/ +- ter_state->pBW = ter_state->bw; +- +- stv0367ter_agc_iir_rst(state); +- } +- +- if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) +- stv0367_writebits(state, F367TER_BDI_LPSEL, 0x01); +- else +- stv0367_writebits(state, F367TER_BDI_LPSEL, 0x00); +- +- InternalFreq = stv0367ter_get_mclk(state, state->config->xtal) / 1000; +- temp = (int) +- ((((ter_state->bw * 64 * (1 << 15) * 100) +- / (InternalFreq)) * 10) / 7); +- +- stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, temp % 2); +- temp = temp / 2; +- stv0367_writebits(state, F367TER_TRL_NOMRATE_HI, temp / 256); +- stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, temp % 256); +- +- temp = stv0367_readbits(state, F367TER_TRL_NOMRATE_HI) * 512 + +- stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 + +- stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB); +- temp = (int)(((1 << 17) * ter_state->bw * 1000) / (7 * (InternalFreq))); +- stv0367_writebits(state, F367TER_GAIN_SRC_HI, temp / 256); +- stv0367_writebits(state, F367TER_GAIN_SRC_LO, temp % 256); +- temp = stv0367_readbits(state, F367TER_GAIN_SRC_HI) * 256 + +- stv0367_readbits(state, F367TER_GAIN_SRC_LO); +- +- temp = (int) +- ((InternalFreq - state->config->if_khz) * (1 << 16) +- / (InternalFreq)); +- +- dprintk("DEROT temp=0x%x\n", temp); +- stv0367_writebits(state, F367TER_INC_DEROT_HI, temp / 256); +- stv0367_writebits(state, F367TER_INC_DEROT_LO, temp % 256); +- +- ter_state->echo_pos = 0; +- ter_state->ucblocks = 0; /* liplianin */ +- ter_state->pBER = 0; /* liplianin */ +- stv0367_writebits(state, F367TER_LONG_ECHO, ter_state->echo_pos); +- +- if (stv0367ter_lock_algo(state) != FE_TER_LOCKOK) +- return 0; +- +- ter_state->state = FE_TER_LOCKOK; +- /* update results */ +- tps_rcvd[0] = stv0367_readreg(state, R367TER_TPS_RCVD2); +- tps_rcvd[1] = stv0367_readreg(state, R367TER_TPS_RCVD3); +- +- ter_state->mode = stv0367_readbits(state, F367TER_SYR_MODE); +- ter_state->guard = stv0367_readbits(state, F367TER_SYR_GUARD); +- +- ter_state->first_lock = 1; /* we know sense now :) */ +- +- ter_state->agc_val = +- (stv0367_readbits(state, F367TER_AGC1_VAL_LO) << 16) + +- (stv0367_readbits(state, F367TER_AGC1_VAL_HI) << 24) + +- stv0367_readbits(state, F367TER_AGC2_VAL_LO) + +- (stv0367_readbits(state, F367TER_AGC2_VAL_HI) << 8); +- +- /* Carrier offset calculation */ +- stv0367_writebits(state, F367TER_FREEZE, 1); +- offset = (stv0367_readbits(state, F367TER_CRL_FOFFSET_VHI) << 16) ; +- offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_HI) << 8); +- offset += (stv0367_readbits(state, F367TER_CRL_FOFFSET_LO)); +- stv0367_writebits(state, F367TER_FREEZE, 0); +- if (offset > 8388607) +- offset -= 16777216; +- +- offset = offset * 2 / 16384; +- +- if (ter_state->mode == FE_TER_MODE_2K) +- offset = (offset * 4464) / 1000;/*** 1 FFT BIN=4.464khz***/ +- else if (ter_state->mode == FE_TER_MODE_4K) +- offset = (offset * 223) / 100;/*** 1 FFT BIN=2.23khz***/ +- else if (ter_state->mode == FE_TER_MODE_8K) +- offset = (offset * 111) / 100;/*** 1 FFT BIN=1.1khz***/ +- +- if (stv0367_readbits(state, F367TER_PPM_INVSEL) == 1) { +- if ((stv0367_readbits(state, F367TER_INV_SPECTR) == +- (stv0367_readbits(state, +- F367TER_STATUS_INV_SPECRUM) == 1))) +- offset = offset * -1; +- } +- +- if (ter_state->bw == 6) +- offset = (offset * 6) / 8; +- else if (ter_state->bw == 7) +- offset = (offset * 7) / 8; +- +- ter_state->frequency += offset; +- +- tempo = 10; /* exit even if timing_offset stays null */ +- while ((timing_offset == 0) && (tempo > 0)) { +- usleep_range(10000, 20000); /*was 20ms */ +- /* fine tuning of timing offset if required */ +- timing_offset = stv0367_readbits(state, F367TER_TRL_TOFFSET_LO) +- + 256 * stv0367_readbits(state, +- F367TER_TRL_TOFFSET_HI); +- if (timing_offset >= 32768) +- timing_offset -= 65536; +- trl_nomrate = (512 * stv0367_readbits(state, +- F367TER_TRL_NOMRATE_HI) +- + stv0367_readbits(state, F367TER_TRL_NOMRATE_LO) * 2 +- + stv0367_readbits(state, F367TER_TRL_NOMRATE_LSB)); +- +- timing_offset = ((signed)(1000000 / trl_nomrate) * +- timing_offset) / 2048; +- tempo--; +- } +- +- if (timing_offset <= 0) { +- timing_offset = (timing_offset - 11) / 22; +- step = -1; +- } else { +- timing_offset = (timing_offset + 11) / 22; +- step = 1; +- } +- +- for (counter = 0; counter < abs(timing_offset); counter++) { +- trl_nomrate += step; +- stv0367_writebits(state, F367TER_TRL_NOMRATE_LSB, +- trl_nomrate % 2); +- stv0367_writebits(state, F367TER_TRL_NOMRATE_LO, +- trl_nomrate / 2); +- usleep_range(1000, 2000); +- } +- +- usleep_range(5000, 6000); +- /* unlocks could happen in case of trl centring big step, +- then a core off/on restarts demod */ +- u_var = stv0367_readbits(state, F367TER_LK); +- +- if (!u_var) { +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); +- msleep(20); +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); +- } +- +- return 0; +-} +- +-static int stv0367ter_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- +- /*u8 trials[2]; */ +- s8 num_trials, index; +- u8 SenseTrials[] = { INVERSION_ON, INVERSION_OFF }; +- +- stv0367ter_init(fe); +- +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- switch (p->transmission_mode) { +- default: +- case TRANSMISSION_MODE_AUTO: +- case TRANSMISSION_MODE_2K: +- ter_state->mode = FE_TER_MODE_2K; +- break; +-/* case TRANSMISSION_MODE_4K: +- pLook.mode = FE_TER_MODE_4K; +- break;*/ +- case TRANSMISSION_MODE_8K: +- ter_state->mode = FE_TER_MODE_8K; +- break; +- } +- +- switch (p->guard_interval) { +- default: +- case GUARD_INTERVAL_1_32: +- case GUARD_INTERVAL_1_16: +- case GUARD_INTERVAL_1_8: +- case GUARD_INTERVAL_1_4: +- ter_state->guard = p->guard_interval; +- break; +- case GUARD_INTERVAL_AUTO: +- ter_state->guard = GUARD_INTERVAL_1_32; +- break; +- } +- +- switch (p->bandwidth_hz) { +- case 6000000: +- ter_state->bw = FE_TER_CHAN_BW_6M; +- break; +- case 7000000: +- ter_state->bw = FE_TER_CHAN_BW_7M; +- break; +- case 8000000: +- default: +- ter_state->bw = FE_TER_CHAN_BW_8M; +- } +- +- ter_state->hierarchy = FE_TER_HIER_NONE; +- +- switch (p->inversion) { +- case INVERSION_OFF: +- case INVERSION_ON: +- num_trials = 1; +- break; +- default: +- num_trials = 2; +- if (ter_state->first_lock) +- num_trials = 1; +- break; +- } +- +- ter_state->state = FE_TER_NOLOCK; +- index = 0; +- +- while (((index) < num_trials) && (ter_state->state != FE_TER_LOCKOK)) { +- if (!ter_state->first_lock) { +- if (p->inversion == INVERSION_AUTO) +- ter_state->sense = SenseTrials[index]; +- +- } +- stv0367ter_algo(fe); +- +- if ((ter_state->state == FE_TER_LOCKOK) && +- (p->inversion == INVERSION_AUTO) && +- (index == 1)) { +- /* invert spectrum sense */ +- SenseTrials[index] = SenseTrials[0]; +- SenseTrials[(index + 1) % 2] = (SenseTrials[1] + 1) % 2; +- } +- +- index++; +- } +- +- return 0; +-} +- +-static int stv0367ter_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- u32 errs = 0; +- +- /*wait for counting completion*/ +- if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) { +- errs = +- ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) +- * (1 << 16)) +- + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) +- * (1 << 8)) +- + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); +- ter_state->ucblocks = errs; +- } +- +- (*ucblocks) = ter_state->ucblocks; +- +- return 0; +-} +- +-static int stv0367ter_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- +- int error = 0; +- enum stv0367_ter_mode mode; +- int constell = 0,/* snr = 0,*/ Data = 0; +- +- p->frequency = stv0367_get_tuner_freq(fe); +- if ((int)p->frequency < 0) +- p->frequency = -p->frequency; +- +- constell = stv0367_readbits(state, F367TER_TPS_CONST); +- if (constell == 0) +- p->modulation = QPSK; +- else if (constell == 1) +- p->modulation = QAM_16; +- else +- p->modulation = QAM_64; +- +- p->inversion = stv0367_readbits(state, F367TER_INV_SPECTR); +- +- /* Get the Hierarchical mode */ +- Data = stv0367_readbits(state, F367TER_TPS_HIERMODE); +- +- switch (Data) { +- case 0: +- p->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- p->hierarchy = HIERARCHY_1; +- break; +- case 2: +- p->hierarchy = HIERARCHY_2; +- break; +- case 3: +- p->hierarchy = HIERARCHY_4; +- break; +- default: +- p->hierarchy = HIERARCHY_AUTO; +- break; /* error */ +- } +- +- /* Get the FEC Rate */ +- if (ter_state->hierarchy == FE_TER_HIER_LOW_PRIO) +- Data = stv0367_readbits(state, F367TER_TPS_LPCODE); +- else +- Data = stv0367_readbits(state, F367TER_TPS_HPCODE); +- +- switch (Data) { +- case 0: +- p->code_rate_HP = FEC_1_2; +- break; +- case 1: +- p->code_rate_HP = FEC_2_3; +- break; +- case 2: +- p->code_rate_HP = FEC_3_4; +- break; +- case 3: +- p->code_rate_HP = FEC_5_6; +- break; +- case 4: +- p->code_rate_HP = FEC_7_8; +- break; +- default: +- p->code_rate_HP = FEC_AUTO; +- break; /* error */ +- } +- +- mode = stv0367_readbits(state, F367TER_SYR_MODE); +- +- switch (mode) { +- case FE_TER_MODE_2K: +- p->transmission_mode = TRANSMISSION_MODE_2K; +- break; +-/* case FE_TER_MODE_4K: +- p->transmission_mode = TRANSMISSION_MODE_4K; +- break;*/ +- case FE_TER_MODE_8K: +- p->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- default: +- p->transmission_mode = TRANSMISSION_MODE_AUTO; +- } +- +- p->guard_interval = stv0367_readbits(state, F367TER_SYR_GUARD); +- +- return error; +-} +- +-static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- u32 snru32 = 0; +- int cpt = 0; +- u8 cut = stv0367_readbits(state, F367TER_IDENTIFICATIONREG); +- +- while (cpt < 10) { +- usleep_range(2000, 3000); +- if (cut == 0x50) /*cut 1.0 cut 1.1*/ +- snru32 += stv0367_readbits(state, F367TER_CHCSNR) / 4; +- else /*cu2.0*/ +- snru32 += 125 * stv0367_readbits(state, F367TER_CHCSNR); +- +- cpt++; +- } +- +- snru32 /= 10;/*average on 10 values*/ +- +- *snr = snru32 / 1000; +- +- return 0; +-} +- +-#if 0 +-static int stv0367ter_status(struct dvb_frontend *fe) +-{ +- +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- int locked = FALSE; +- +- locked = (stv0367_readbits(state, F367TER_LK)); +- if (!locked) +- ter_state->unlock_counter += 1; +- else +- ter_state->unlock_counter = 0; +- +- if (ter_state->unlock_counter > 2) { +- if (!stv0367_readbits(state, F367TER_TPS_LOCK) || +- (!stv0367_readbits(state, F367TER_LK))) { +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 0); +- usleep_range(2000, 3000); +- stv0367_writebits(state, F367TER_CORE_ACTIVE, 1); +- msleep(350); +- locked = (stv0367_readbits(state, F367TER_TPS_LOCK)) && +- (stv0367_readbits(state, F367TER_LK)); +- } +- +- } +- +- return locked; +-} +-#endif +-static int stv0367ter_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- dprintk("%s:\n", __func__); +- +- *status = 0; +- +- if (stv0367_readbits(state, F367TER_LK)) { +- *status |= FE_HAS_LOCK; +- dprintk("%s: stv0367 has locked\n", __func__); +- } +- +- return 0; +-} +- +-static int stv0367ter_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367ter_state *ter_state = state->ter_state; +- u32 Errors = 0, tber = 0, temporary = 0; +- int abc = 0, def = 0; +- +- +- /*wait for counting completion*/ +- if (stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 0) +- Errors = ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT) +- * (1 << 16)) +- + ((u32)stv0367_readbits(state, F367TER_SFEC_ERR_CNT_HI) +- * (1 << 8)) +- + ((u32)stv0367_readbits(state, +- F367TER_SFEC_ERR_CNT_LO)); +- /*measurement not completed, load previous value*/ +- else { +- tber = ter_state->pBER; +- return 0; +- } +- +- abc = stv0367_readbits(state, F367TER_SFEC_ERR_SOURCE); +- def = stv0367_readbits(state, F367TER_SFEC_NUM_EVENT); +- +- if (Errors == 0) { +- tber = 0; +- } else if (abc == 0x7) { +- if (Errors <= 4) { +- temporary = (Errors * 1000000000) / (8 * (1 << 14)); +- temporary = temporary; +- } else if (Errors <= 42) { +- temporary = (Errors * 100000000) / (8 * (1 << 14)); +- temporary = temporary * 10; +- } else if (Errors <= 429) { +- temporary = (Errors * 10000000) / (8 * (1 << 14)); +- temporary = temporary * 100; +- } else if (Errors <= 4294) { +- temporary = (Errors * 1000000) / (8 * (1 << 14)); +- temporary = temporary * 1000; +- } else if (Errors <= 42949) { +- temporary = (Errors * 100000) / (8 * (1 << 14)); +- temporary = temporary * 10000; +- } else if (Errors <= 429496) { +- temporary = (Errors * 10000) / (8 * (1 << 14)); +- temporary = temporary * 100000; +- } else { /*if (Errors<4294967) 2^22 max error*/ +- temporary = (Errors * 1000) / (8 * (1 << 14)); +- temporary = temporary * 100000; /* still to *10 */ +- } +- +- /* Byte error*/ +- if (def == 2) +- /*tber=Errors/(8*(1 <<14));*/ +- tber = temporary; +- else if (def == 3) +- /*tber=Errors/(8*(1 <<16));*/ +- tber = temporary / 4; +- else if (def == 4) +- /*tber=Errors/(8*(1 <<18));*/ +- tber = temporary / 16; +- else if (def == 5) +- /*tber=Errors/(8*(1 <<20));*/ +- tber = temporary / 64; +- else if (def == 6) +- /*tber=Errors/(8*(1 <<22));*/ +- tber = temporary / 256; +- else +- /* should not pass here*/ +- tber = 0; +- +- if ((Errors < 4294967) && (Errors > 429496)) +- tber *= 10; +- +- } +- +- /* save actual value */ +- ter_state->pBER = tber; +- +- (*ber) = tber; +- +- return 0; +-} +-#if 0 +-static u32 stv0367ter_get_per(struct stv0367_state *state) +-{ +- struct stv0367ter_state *ter_state = state->ter_state; +- u32 Errors = 0, Per = 0, temporary = 0; +- int abc = 0, def = 0, cpt = 0; +- +- while (((stv0367_readbits(state, F367TER_SFERRC_OLDVALUE) == 1) && +- (cpt < 400)) || ((Errors == 0) && (cpt < 400))) { +- usleep_range(1000, 2000); +- Errors = ((u32)stv0367_readbits(state, F367TER_ERR_CNT1) +- * (1 << 16)) +- + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_HI) +- * (1 << 8)) +- + ((u32)stv0367_readbits(state, F367TER_ERR_CNT1_LO)); +- cpt++; +- } +- abc = stv0367_readbits(state, F367TER_ERR_SRC1); +- def = stv0367_readbits(state, F367TER_NUM_EVT1); +- +- if (Errors == 0) +- Per = 0; +- else if (abc == 0x9) { +- if (Errors <= 4) { +- temporary = (Errors * 1000000000) / (8 * (1 << 8)); +- temporary = temporary; +- } else if (Errors <= 42) { +- temporary = (Errors * 100000000) / (8 * (1 << 8)); +- temporary = temporary * 10; +- } else if (Errors <= 429) { +- temporary = (Errors * 10000000) / (8 * (1 << 8)); +- temporary = temporary * 100; +- } else if (Errors <= 4294) { +- temporary = (Errors * 1000000) / (8 * (1 << 8)); +- temporary = temporary * 1000; +- } else if (Errors <= 42949) { +- temporary = (Errors * 100000) / (8 * (1 << 8)); +- temporary = temporary * 10000; +- } else { /*if(Errors<=429496) 2^16 errors max*/ +- temporary = (Errors * 10000) / (8 * (1 << 8)); +- temporary = temporary * 100000; +- } +- +- /* pkt error*/ +- if (def == 2) +- /*Per=Errors/(1 << 8);*/ +- Per = temporary; +- else if (def == 3) +- /*Per=Errors/(1 << 10);*/ +- Per = temporary / 4; +- else if (def == 4) +- /*Per=Errors/(1 << 12);*/ +- Per = temporary / 16; +- else if (def == 5) +- /*Per=Errors/(1 << 14);*/ +- Per = temporary / 64; +- else if (def == 6) +- /*Per=Errors/(1 << 16);*/ +- Per = temporary / 256; +- else +- Per = 0; +- +- } +- /* save actual value */ +- ter_state->pPER = Per; +- +- return Per; +-} +-#endif +-static int stv0367_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings +- *fe_tune_settings) +-{ +- fe_tune_settings->min_delay_ms = 1000; +- fe_tune_settings->step_size = 0; +- fe_tune_settings->max_drift = 0; +- +- return 0; +-} +- +-static void stv0367_release(struct dvb_frontend *fe) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- kfree(state->ter_state); +- kfree(state->cab_state); +- kfree(state); +-} +- +-static struct dvb_frontend_ops stv0367ter_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "ST STV0367 DVB-T", +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 15625, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | +- FE_CAN_INVERSION_AUTO | +- FE_CAN_MUTE_TS +- }, +- .release = stv0367_release, +- .init = stv0367ter_init, +- .sleep = stv0367ter_sleep, +- .i2c_gate_ctrl = stv0367ter_gate_ctrl, +- .set_frontend = stv0367ter_set_frontend, +- .get_frontend = stv0367ter_get_frontend, +- .get_tune_settings = stv0367_get_tune_settings, +- .read_status = stv0367ter_read_status, +- .read_ber = stv0367ter_read_ber,/* too slow */ +-/* .read_signal_strength = stv0367_read_signal_strength,*/ +- .read_snr = stv0367ter_read_snr, +- .read_ucblocks = stv0367ter_read_ucblocks, +-}; +- +-struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv0367_state *state = NULL; +- struct stv0367ter_state *ter_state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- ter_state = kzalloc(sizeof(struct stv0367ter_state), GFP_KERNEL); +- if (ter_state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- state->config = config; +- state->ter_state = ter_state; +- state->fe.ops = stv0367ter_ops; +- state->fe.demodulator_priv = state; +- state->chip_id = stv0367_readreg(state, 0xf000); +- +- dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); +- +- /* check if the demod is there */ +- if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) +- goto error; +- +- return &state->fe; +- +-error: +- kfree(ter_state); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(stv0367ter_attach); +- +-static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- dprintk("%s:\n", __func__); +- +- stv0367_writebits(state, F367CAB_I2CT_ON, (enable > 0) ? 1 : 0); +- +- return 0; +-} +- +-static u32 stv0367cab_get_mclk(struct dvb_frontend *fe, u32 ExtClk_Hz) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- u32 mclk_Hz = 0;/* master clock frequency (Hz) */ +- u32 M, N, P; +- +- +- if (stv0367_readbits(state, F367CAB_BYPASS_PLLXN) == 0) { +- N = (u32)stv0367_readbits(state, F367CAB_PLL_NDIV); +- if (N == 0) +- N = N + 1; +- +- M = (u32)stv0367_readbits(state, F367CAB_PLL_MDIV); +- if (M == 0) +- M = M + 1; +- +- P = (u32)stv0367_readbits(state, F367CAB_PLL_PDIV); +- +- if (P > 5) +- P = 5; +- +- mclk_Hz = ((ExtClk_Hz / 2) * N) / (M * (1 << P)); +- dprintk("stv0367cab_get_mclk BYPASS_PLLXN mclk_Hz=%d\n", +- mclk_Hz); +- } else +- mclk_Hz = ExtClk_Hz; +- +- dprintk("stv0367cab_get_mclk final mclk_Hz=%d\n", mclk_Hz); +- +- return mclk_Hz; +-} +- +-static u32 stv0367cab_get_adc_freq(struct dvb_frontend *fe, u32 ExtClk_Hz) +-{ +- u32 ADCClk_Hz = ExtClk_Hz; +- +- ADCClk_Hz = stv0367cab_get_mclk(fe, ExtClk_Hz); +- +- return ADCClk_Hz; +-} +- +-enum stv0367cab_mod stv0367cab_SetQamSize(struct stv0367_state *state, +- u32 SymbolRate, +- enum stv0367cab_mod QAMSize) +-{ +- /* Set QAM size */ +- stv0367_writebits(state, F367CAB_QAM_MODE, QAMSize); +- +- /* Set Registers settings specific to the QAM size */ +- switch (QAMSize) { +- case FE_CAB_MOD_QAM4: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- break; +- case FE_CAB_MOD_QAM16: +- stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x64); +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); +- stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); +- stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); +- stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x8a); +- break; +- case FE_CAB_MOD_QAM32: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x6e); +- stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xb7); +- stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x9d); +- stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); +- stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); +- break; +- case FE_CAB_MOD_QAM64: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x82); +- stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); +- if (SymbolRate > 45000000) { +- stv0367_writereg(state, R367CAB_FSM_STATE, 0xb0); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa5); +- } else if (SymbolRate > 25000000) { +- stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); +- } else { +- stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); +- } +- stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x95); +- stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); +- stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0x99); +- break; +- case FE_CAB_MOD_QAM128: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x76); +- stv0367_writereg(state, R367CAB_FSM_STATE, 0x90); +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xb1); +- if (SymbolRate > 45000000) +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); +- else if (SymbolRate > 25000000) +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa6); +- else +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0x97); +- +- stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x8e); +- stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x7f); +- stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); +- break; +- case FE_CAB_MOD_QAM256: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x94); +- stv0367_writereg(state, R367CAB_AGC_PWR_REF_L, 0x5a); +- stv0367_writereg(state, R367CAB_FSM_STATE, 0xa0); +- if (SymbolRate > 45000000) +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- else if (SymbolRate > 25000000) +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xc1); +- else +- stv0367_writereg(state, R367CAB_EQU_CTR_LPF_GAIN, 0xd1); +- +- stv0367_writereg(state, R367CAB_EQU_CRL_LPF_GAIN, 0xa7); +- stv0367_writereg(state, R367CAB_EQU_CRL_LD_SEN, 0x85); +- stv0367_writereg(state, R367CAB_EQU_CRL_LIMITER, 0x40); +- stv0367_writereg(state, R367CAB_EQU_PNT_GAIN, 0xa7); +- break; +- case FE_CAB_MOD_QAM512: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- break; +- case FE_CAB_MOD_QAM1024: +- stv0367_writereg(state, R367CAB_IQDEM_ADJ_AGC_REF, 0x00); +- break; +- default: +- break; +- } +- +- return QAMSize; +-} +- +-static u32 stv0367cab_set_derot_freq(struct stv0367_state *state, +- u32 adc_hz, s32 derot_hz) +-{ +- u32 sampled_if = 0; +- u32 adc_khz; +- +- adc_khz = adc_hz / 1000; +- +- dprintk("%s: adc_hz=%d derot_hz=%d\n", __func__, adc_hz, derot_hz); +- +- if (adc_khz != 0) { +- if (derot_hz < 1000000) +- derot_hz = adc_hz / 4; /* ZIF operation */ +- if (derot_hz > adc_hz) +- derot_hz = derot_hz - adc_hz; +- sampled_if = (u32)derot_hz / 1000; +- sampled_if *= 32768; +- sampled_if /= adc_khz; +- sampled_if *= 256; +- } +- +- if (sampled_if > 8388607) +- sampled_if = 8388607; +- +- dprintk("%s: sampled_if=0x%x\n", __func__, sampled_if); +- +- stv0367_writereg(state, R367CAB_MIX_NCO_LL, sampled_if); +- stv0367_writereg(state, R367CAB_MIX_NCO_HL, (sampled_if >> 8)); +- stv0367_writebits(state, F367CAB_MIX_NCO_INC_HH, (sampled_if >> 16)); +- +- return derot_hz; +-} +- +-static u32 stv0367cab_get_derot_freq(struct stv0367_state *state, u32 adc_hz) +-{ +- u32 sampled_if; +- +- sampled_if = stv0367_readbits(state, F367CAB_MIX_NCO_INC_LL) + +- (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HL) << 8) + +- (stv0367_readbits(state, F367CAB_MIX_NCO_INC_HH) << 16); +- +- sampled_if /= 256; +- sampled_if *= (adc_hz / 1000); +- sampled_if += 1; +- sampled_if /= 32768; +- +- return sampled_if; +-} +- +-static u32 stv0367cab_set_srate(struct stv0367_state *state, u32 adc_hz, +- u32 mclk_hz, u32 SymbolRate, +- enum stv0367cab_mod QAMSize) +-{ +- u32 QamSizeCorr = 0; +- u32 u32_tmp = 0, u32_tmp1 = 0; +- u32 adp_khz; +- +- dprintk("%s:\n", __func__); +- +- /* Set Correction factor of SRC gain */ +- switch (QAMSize) { +- case FE_CAB_MOD_QAM4: +- QamSizeCorr = 1110; +- break; +- case FE_CAB_MOD_QAM16: +- QamSizeCorr = 1032; +- break; +- case FE_CAB_MOD_QAM32: +- QamSizeCorr = 954; +- break; +- case FE_CAB_MOD_QAM64: +- QamSizeCorr = 983; +- break; +- case FE_CAB_MOD_QAM128: +- QamSizeCorr = 957; +- break; +- case FE_CAB_MOD_QAM256: +- QamSizeCorr = 948; +- break; +- case FE_CAB_MOD_QAM512: +- QamSizeCorr = 0; +- break; +- case FE_CAB_MOD_QAM1024: +- QamSizeCorr = 944; +- break; +- default: +- break; +- } +- +- /* Transfer ratio calculation */ +- if (adc_hz != 0) { +- u32_tmp = 256 * SymbolRate; +- u32_tmp = u32_tmp / adc_hz; +- } +- stv0367_writereg(state, R367CAB_EQU_CRL_TFR, (u8)u32_tmp); +- +- /* Symbol rate and SRC gain calculation */ +- adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ +- if (adp_khz != 0) { +- u32_tmp = SymbolRate; +- u32_tmp1 = SymbolRate; +- +- if (u32_tmp < 2097152) { /* 2097152 = 2^21 */ +- /* Symbol rate calculation */ +- u32_tmp *= 2048; /* 2048 = 2^11 */ +- u32_tmp = u32_tmp / adp_khz; +- u32_tmp = u32_tmp * 16384; /* 16384 = 2^14 */ +- u32_tmp /= 125 ; /* 125 = 1000/2^3 */ +- u32_tmp = u32_tmp * 8; /* 8 = 2^3 */ +- +- /* SRC Gain Calculation */ +- u32_tmp1 *= 2048; /* *2*2^10 */ +- u32_tmp1 /= 439; /* *2/878 */ +- u32_tmp1 *= 256; /* *2^8 */ +- u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ +- u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ +- u32_tmp1 = u32_tmp1 / 10000000; +- +- } else if (u32_tmp < 4194304) { /* 4194304 = 2**22 */ +- /* Symbol rate calculation */ +- u32_tmp *= 1024 ; /* 1024 = 2**10 */ +- u32_tmp = u32_tmp / adp_khz; +- u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ +- u32_tmp /= 125 ; /* 125 = 1000/2**3 */ +- u32_tmp = u32_tmp * 16; /* 16 = 2**4 */ +- +- /* SRC Gain Calculation */ +- u32_tmp1 *= 1024; /* *2*2^9 */ +- u32_tmp1 /= 439; /* *2/878 */ +- u32_tmp1 *= 256; /* *2^8 */ +- u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz)*/ +- u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ +- u32_tmp1 = u32_tmp1 / 5000000; +- } else if (u32_tmp < 8388607) { /* 8388607 = 2**23 */ +- /* Symbol rate calculation */ +- u32_tmp *= 512 ; /* 512 = 2**9 */ +- u32_tmp = u32_tmp / adp_khz; +- u32_tmp = u32_tmp * 16384; /* 16384 = 2**14 */ +- u32_tmp /= 125 ; /* 125 = 1000/2**3 */ +- u32_tmp = u32_tmp * 32; /* 32 = 2**5 */ +- +- /* SRC Gain Calculation */ +- u32_tmp1 *= 512; /* *2*2^8 */ +- u32_tmp1 /= 439; /* *2/878 */ +- u32_tmp1 *= 256; /* *2^8 */ +- u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ +- u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ +- u32_tmp1 = u32_tmp1 / 2500000; +- } else { +- /* Symbol rate calculation */ +- u32_tmp *= 256 ; /* 256 = 2**8 */ +- u32_tmp = u32_tmp / adp_khz; +- u32_tmp = u32_tmp * 16384; /* 16384 = 2**13 */ +- u32_tmp /= 125 ; /* 125 = 1000/2**3 */ +- u32_tmp = u32_tmp * 64; /* 64 = 2**6 */ +- +- /* SRC Gain Calculation */ +- u32_tmp1 *= 256; /* 2*2^7 */ +- u32_tmp1 /= 439; /* *2/878 */ +- u32_tmp1 *= 256; /* *2^8 */ +- u32_tmp1 = u32_tmp1 / adp_khz; /* /(AdpClk in kHz) */ +- u32_tmp1 *= QamSizeCorr * 9; /* *1000*corr factor */ +- u32_tmp1 = u32_tmp1 / 1250000; +- } +- } +-#if 0 +- /* Filters' coefficients are calculated and written +- into registers only if the filters are enabled */ +- if (stv0367_readbits(state, F367CAB_ADJ_EN)) { +- stv0367cab_SetIirAdjacentcoefficient(state, mclk_hz, +- SymbolRate); +- /* AllPass filter must be enabled +- when the adjacents filter is used */ +- stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 1); +- stv0367cab_SetAllPasscoefficient(state, mclk_hz, SymbolRate); +- } else +- /* AllPass filter must be disabled +- when the adjacents filter is not used */ +-#endif +- stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); +- +- stv0367_writereg(state, R367CAB_SRC_NCO_LL, u32_tmp); +- stv0367_writereg(state, R367CAB_SRC_NCO_LH, (u32_tmp >> 8)); +- stv0367_writereg(state, R367CAB_SRC_NCO_HL, (u32_tmp >> 16)); +- stv0367_writereg(state, R367CAB_SRC_NCO_HH, (u32_tmp >> 24)); +- +- stv0367_writereg(state, R367CAB_IQDEM_GAIN_SRC_L, u32_tmp1 & 0x00ff); +- stv0367_writebits(state, F367CAB_GAIN_SRC_HI, (u32_tmp1 >> 8) & 0x00ff); +- +- return SymbolRate ; +-} +- +-static u32 stv0367cab_GetSymbolRate(struct stv0367_state *state, u32 mclk_hz) +-{ +- u32 regsym; +- u32 adp_khz; +- +- regsym = stv0367_readreg(state, R367CAB_SRC_NCO_LL) + +- (stv0367_readreg(state, R367CAB_SRC_NCO_LH) << 8) + +- (stv0367_readreg(state, R367CAB_SRC_NCO_HL) << 16) + +- (stv0367_readreg(state, R367CAB_SRC_NCO_HH) << 24); +- +- adp_khz = (mclk_hz >> 1) / 1000;/* TRL works at half the system clock */ +- +- if (regsym < 134217728) { /* 134217728L = 2**27*/ +- regsym = regsym * 32; /* 32 = 2**5 */ +- regsym = regsym / 32768; /* 32768L = 2**15 */ +- regsym = adp_khz * regsym; /* AdpClk in kHz */ +- regsym = regsym / 128; /* 128 = 2**7 */ +- regsym *= 125 ; /* 125 = 1000/2**3 */ +- regsym /= 2048 ; /* 2048 = 2**11 */ +- } else if (regsym < 268435456) { /* 268435456L = 2**28 */ +- regsym = regsym * 16; /* 16 = 2**4 */ +- regsym = regsym / 32768; /* 32768L = 2**15 */ +- regsym = adp_khz * regsym; /* AdpClk in kHz */ +- regsym = regsym / 128; /* 128 = 2**7 */ +- regsym *= 125 ; /* 125 = 1000/2**3*/ +- regsym /= 1024 ; /* 256 = 2**10*/ +- } else if (regsym < 536870912) { /* 536870912L = 2**29*/ +- regsym = regsym * 8; /* 8 = 2**3 */ +- regsym = regsym / 32768; /* 32768L = 2**15 */ +- regsym = adp_khz * regsym; /* AdpClk in kHz */ +- regsym = regsym / 128; /* 128 = 2**7 */ +- regsym *= 125 ; /* 125 = 1000/2**3 */ +- regsym /= 512 ; /* 128 = 2**9 */ +- } else { +- regsym = regsym * 4; /* 4 = 2**2 */ +- regsym = regsym / 32768; /* 32768L = 2**15 */ +- regsym = adp_khz * regsym; /* AdpClk in kHz */ +- regsym = regsym / 128; /* 128 = 2**7 */ +- regsym *= 125 ; /* 125 = 1000/2**3 */ +- regsym /= 256 ; /* 64 = 2**8 */ +- } +- +- return regsym; +-} +- +-static int stv0367cab_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- dprintk("%s:\n", __func__); +- +- *status = 0; +- +- if (stv0367_readbits(state, F367CAB_QAMFEC_LOCK)) { +- *status |= FE_HAS_LOCK; +- dprintk("%s: stv0367 has locked\n", __func__); +- } +- +- return 0; +-} +- +-static int stv0367cab_standby(struct dvb_frontend *fe, u8 standby_on) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- dprintk("%s:\n", __func__); +- +- if (standby_on) { +- stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x03); +- stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x01); +- stv0367_writebits(state, F367CAB_STDBY, 1); +- stv0367_writebits(state, F367CAB_STDBY_CORE, 1); +- stv0367_writebits(state, F367CAB_EN_BUFFER_I, 0); +- stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 0); +- stv0367_writebits(state, F367CAB_POFFQ, 1); +- stv0367_writebits(state, F367CAB_POFFI, 1); +- } else { +- stv0367_writebits(state, F367CAB_STDBY_PLLXN, 0x00); +- stv0367_writebits(state, F367CAB_BYPASS_PLLXN, 0x00); +- stv0367_writebits(state, F367CAB_STDBY, 0); +- stv0367_writebits(state, F367CAB_STDBY_CORE, 0); +- stv0367_writebits(state, F367CAB_EN_BUFFER_I, 1); +- stv0367_writebits(state, F367CAB_EN_BUFFER_Q, 1); +- stv0367_writebits(state, F367CAB_POFFQ, 0); +- stv0367_writebits(state, F367CAB_POFFI, 0); +- } +- +- return 0; +-} +- +-static int stv0367cab_sleep(struct dvb_frontend *fe) +-{ +- return stv0367cab_standby(fe, 1); +-} +- +-int stv0367cab_init(struct dvb_frontend *fe) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367cab_state *cab_state = state->cab_state; +- int i; +- +- dprintk("%s:\n", __func__); +- +- for (i = 0; i < STV0367CAB_NBREGS; i++) +- stv0367_writereg(state, def0367cab[i].addr, +- def0367cab[i].value); +- +- switch (state->config->ts_mode) { +- case STV0367_DVBCI_CLOCK: +- dprintk("Setting TSMode = STV0367_DVBCI_CLOCK\n"); +- stv0367_writebits(state, F367CAB_OUTFORMAT, 0x03); +- break; +- case STV0367_SERIAL_PUNCT_CLOCK: +- case STV0367_SERIAL_CONT_CLOCK: +- stv0367_writebits(state, F367CAB_OUTFORMAT, 0x01); +- break; +- case STV0367_PARALLEL_PUNCT_CLOCK: +- case STV0367_OUTPUTMODE_DEFAULT: +- stv0367_writebits(state, F367CAB_OUTFORMAT, 0x00); +- break; +- } +- +- switch (state->config->clk_pol) { +- case STV0367_RISINGEDGE_CLOCK: +- stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x00); +- break; +- case STV0367_FALLINGEDGE_CLOCK: +- case STV0367_CLOCKPOLARITY_DEFAULT: +- stv0367_writebits(state, F367CAB_CLK_POLARITY, 0x01); +- break; +- } +- +- stv0367_writebits(state, F367CAB_SYNC_STRIP, 0x00); +- +- stv0367_writebits(state, F367CAB_CT_NBST, 0x01); +- +- stv0367_writebits(state, F367CAB_TS_SWAP, 0x01); +- +- stv0367_writebits(state, F367CAB_FIFO_BYPASS, 0x00); +- +- stv0367_writereg(state, R367CAB_ANACTRL, 0x00);/*PLL enabled and used */ +- +- cab_state->mclk = stv0367cab_get_mclk(fe, state->config->xtal); +- cab_state->adc_clk = stv0367cab_get_adc_freq(fe, state->config->xtal); +- +- return 0; +-} +-static +-enum stv0367_cab_signal_type stv0367cab_algo(struct stv0367_state *state, +- struct dtv_frontend_properties *p) +-{ +- struct stv0367cab_state *cab_state = state->cab_state; +- enum stv0367_cab_signal_type signalType = FE_CAB_NOAGC; +- u32 QAMFEC_Lock, QAM_Lock, u32_tmp, +- LockTime, TRLTimeOut, AGCTimeOut, CRLSymbols, +- CRLTimeOut, EQLTimeOut, DemodTimeOut, FECTimeOut; +- u8 TrackAGCAccum; +- s32 tmp; +- +- dprintk("%s:\n", __func__); +- +- /* Timeouts calculation */ +- /* A max lock time of 25 ms is allowed for delayed AGC */ +- AGCTimeOut = 25; +- /* 100000 symbols needed by the TRL as a maximum value */ +- TRLTimeOut = 100000000 / p->symbol_rate; +- /* CRLSymbols is the needed number of symbols to achieve a lock +- within [-4%, +4%] of the symbol rate. +- CRL timeout is calculated +- for a lock within [-search_range, +search_range]. +- EQL timeout can be changed depending on +- the micro-reflections we want to handle. +- A characterization must be performed +- with these echoes to get new timeout values. +- */ +- switch (p->modulation) { +- case QAM_16: +- CRLSymbols = 150000; +- EQLTimeOut = 100; +- break; +- case QAM_32: +- CRLSymbols = 250000; +- EQLTimeOut = 100; +- break; +- case QAM_64: +- CRLSymbols = 200000; +- EQLTimeOut = 100; +- break; +- case QAM_128: +- CRLSymbols = 250000; +- EQLTimeOut = 100; +- break; +- case QAM_256: +- CRLSymbols = 250000; +- EQLTimeOut = 100; +- break; +- default: +- CRLSymbols = 200000; +- EQLTimeOut = 100; +- break; +- } +-#if 0 +- if (pIntParams->search_range < 0) { +- CRLTimeOut = (25 * CRLSymbols * +- (-pIntParams->search_range / 1000)) / +- (pIntParams->symbol_rate / 1000); +- } else +-#endif +- CRLTimeOut = (25 * CRLSymbols * (cab_state->search_range / 1000)) / +- (p->symbol_rate / 1000); +- +- CRLTimeOut = (1000 * CRLTimeOut) / p->symbol_rate; +- /* Timeouts below 50ms are coerced */ +- if (CRLTimeOut < 50) +- CRLTimeOut = 50; +- /* A maximum of 100 TS packets is needed to get FEC lock even in case +- the spectrum inversion needs to be changed. +- This is equal to 20 ms in case of the lowest symbol rate of 0.87Msps +- */ +- FECTimeOut = 20; +- DemodTimeOut = AGCTimeOut + TRLTimeOut + CRLTimeOut + EQLTimeOut; +- +- dprintk("%s: DemodTimeOut=%d\n", __func__, DemodTimeOut); +- +- /* Reset the TRL to ensure nothing starts until the +- AGC is stable which ensures a better lock time +- */ +- stv0367_writereg(state, R367CAB_CTRL_1, 0x04); +- /* Set AGC accumulation time to minimum and lock threshold to maximum +- in order to speed up the AGC lock */ +- TrackAGCAccum = stv0367_readbits(state, F367CAB_AGC_ACCUMRSTSEL); +- stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, 0x0); +- /* Modulus Mapper is disabled */ +- stv0367_writebits(state, F367CAB_MODULUSMAP_EN, 0); +- /* Disable the sweep function */ +- stv0367_writebits(state, F367CAB_SWEEP_EN, 0); +- /* The sweep function is never used, Sweep rate must be set to 0 */ +- /* Set the derotator frequency in Hz */ +- stv0367cab_set_derot_freq(state, cab_state->adc_clk, +- (1000 * (s32)state->config->if_khz + cab_state->derot_offset)); +- /* Disable the Allpass Filter when the symbol rate is out of range */ +- if ((p->symbol_rate > 10800000) | (p->symbol_rate < 1800000)) { +- stv0367_writebits(state, F367CAB_ADJ_EN, 0); +- stv0367_writebits(state, F367CAB_ALLPASSFILT_EN, 0); +- } +-#if 0 +- /* Check if the tuner is locked */ +- tuner_lock = stv0367cab_tuner_get_status(fe); +- if (tuner_lock == 0) +- return FE_367CAB_NOTUNER; +-#endif +- /* Relase the TRL to start demodulator acquisition */ +- /* Wait for QAM lock */ +- LockTime = 0; +- stv0367_writereg(state, R367CAB_CTRL_1, 0x00); +- do { +- QAM_Lock = stv0367_readbits(state, F367CAB_FSM_STATUS); +- if ((LockTime >= (DemodTimeOut - EQLTimeOut)) && +- (QAM_Lock == 0x04)) +- /* +- * We don't wait longer, the frequency/phase offset +- * must be too big +- */ +- LockTime = DemodTimeOut; +- else if ((LockTime >= (AGCTimeOut + TRLTimeOut)) && +- (QAM_Lock == 0x02)) +- /* +- * We don't wait longer, either there is no signal or +- * it is not the right symbol rate or it is an analog +- * carrier +- */ +- { +- LockTime = DemodTimeOut; +- u32_tmp = stv0367_readbits(state, +- F367CAB_AGC_PWR_WORD_LO) + +- (stv0367_readbits(state, +- F367CAB_AGC_PWR_WORD_ME) << 8) + +- (stv0367_readbits(state, +- F367CAB_AGC_PWR_WORD_HI) << 16); +- if (u32_tmp >= 131072) +- u32_tmp = 262144 - u32_tmp; +- u32_tmp = u32_tmp / (1 << (11 - stv0367_readbits(state, +- F367CAB_AGC_IF_BWSEL))); +- +- if (u32_tmp < stv0367_readbits(state, +- F367CAB_AGC_PWRREF_LO) + +- 256 * stv0367_readbits(state, +- F367CAB_AGC_PWRREF_HI) - 10) +- QAM_Lock = 0x0f; +- } else { +- usleep_range(10000, 20000); +- LockTime += 10; +- } +- dprintk("QAM_Lock=0x%x LockTime=%d\n", QAM_Lock, LockTime); +- tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); +- +- dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); +- +- } while (((QAM_Lock != 0x0c) && (QAM_Lock != 0x0b)) && +- (LockTime < DemodTimeOut)); +- +- dprintk("QAM_Lock=0x%x\n", QAM_Lock); +- +- tmp = stv0367_readreg(state, R367CAB_IT_STATUS1); +- dprintk("R367CAB_IT_STATUS1=0x%x\n", tmp); +- tmp = stv0367_readreg(state, R367CAB_IT_STATUS2); +- dprintk("R367CAB_IT_STATUS2=0x%x\n", tmp); +- +- tmp = stv0367cab_get_derot_freq(state, cab_state->adc_clk); +- dprintk("stv0367cab_get_derot_freq=0x%x\n", tmp); +- +- if ((QAM_Lock == 0x0c) || (QAM_Lock == 0x0b)) { +- /* Wait for FEC lock */ +- LockTime = 0; +- do { +- usleep_range(5000, 7000); +- LockTime += 5; +- QAMFEC_Lock = stv0367_readbits(state, +- F367CAB_QAMFEC_LOCK); +- } while (!QAMFEC_Lock && (LockTime < FECTimeOut)); +- } else +- QAMFEC_Lock = 0; +- +- if (QAMFEC_Lock) { +- signalType = FE_CAB_DATAOK; +- cab_state->modulation = p->modulation; +- cab_state->spect_inv = stv0367_readbits(state, +- F367CAB_QUAD_INV); +-#if 0 +-/* not clear for me */ +- if (state->config->if_khz != 0) { +- if (state->config->if_khz > cab_state->adc_clk / 1000) { +- cab_state->freq_khz = +- FE_Cab_TunerGetFrequency(pIntParams->hTuner) +- - stv0367cab_get_derot_freq(state, cab_state->adc_clk) +- - cab_state->adc_clk / 1000 + state->config->if_khz; +- } else { +- cab_state->freq_khz = +- FE_Cab_TunerGetFrequency(pIntParams->hTuner) +- - stv0367cab_get_derot_freq(state, cab_state->adc_clk) +- + state->config->if_khz; +- } +- } else { +- cab_state->freq_khz = +- FE_Cab_TunerGetFrequency(pIntParams->hTuner) + +- stv0367cab_get_derot_freq(state, +- cab_state->adc_clk) - +- cab_state->adc_clk / 4000; +- } +-#endif +- cab_state->symbol_rate = stv0367cab_GetSymbolRate(state, +- cab_state->mclk); +- cab_state->locked = 1; +- +- /* stv0367_setbits(state, F367CAB_AGC_ACCUMRSTSEL,7);*/ +- } else { +- switch (QAM_Lock) { +- case 1: +- signalType = FE_CAB_NOAGC; +- break; +- case 2: +- signalType = FE_CAB_NOTIMING; +- break; +- case 3: +- signalType = FE_CAB_TIMINGOK; +- break; +- case 4: +- signalType = FE_CAB_NOCARRIER; +- break; +- case 5: +- signalType = FE_CAB_CARRIEROK; +- break; +- case 7: +- signalType = FE_CAB_NOBLIND; +- break; +- case 8: +- signalType = FE_CAB_BLINDOK; +- break; +- case 10: +- signalType = FE_CAB_NODEMOD; +- break; +- case 11: +- signalType = FE_CAB_DEMODOK; +- break; +- case 12: +- signalType = FE_CAB_DEMODOK; +- break; +- case 13: +- signalType = FE_CAB_NODEMOD; +- break; +- case 14: +- signalType = FE_CAB_NOBLIND; +- break; +- case 15: +- signalType = FE_CAB_NOSIGNAL; +- break; +- default: +- break; +- } +- +- } +- +- /* Set the AGC control values to tracking values */ +- stv0367_writebits(state, F367CAB_AGC_ACCUMRSTSEL, TrackAGCAccum); +- return signalType; +-} +- +-static int stv0367cab_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367cab_state *cab_state = state->cab_state; +- enum stv0367cab_mod QAMSize = 0; +- +- dprintk("%s: freq = %d, srate = %d\n", __func__, +- p->frequency, p->symbol_rate); +- +- cab_state->derot_offset = 0; +- +- switch (p->modulation) { +- case QAM_16: +- QAMSize = FE_CAB_MOD_QAM16; +- break; +- case QAM_32: +- QAMSize = FE_CAB_MOD_QAM32; +- break; +- case QAM_64: +- QAMSize = FE_CAB_MOD_QAM64; +- break; +- case QAM_128: +- QAMSize = FE_CAB_MOD_QAM128; +- break; +- case QAM_256: +- QAMSize = FE_CAB_MOD_QAM256; +- break; +- default: +- break; +- } +- +- stv0367cab_init(fe); +- +- /* Tuner Frequency Setting */ +- if (fe->ops.tuner_ops.set_params) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- stv0367cab_SetQamSize( +- state, +- p->symbol_rate, +- QAMSize); +- +- stv0367cab_set_srate(state, +- cab_state->adc_clk, +- cab_state->mclk, +- p->symbol_rate, +- QAMSize); +- /* Search algorithm launch, [-1.1*RangeOffset, +1.1*RangeOffset] scan */ +- cab_state->state = stv0367cab_algo(state, p); +- return 0; +-} +- +-static int stv0367cab_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0367_state *state = fe->demodulator_priv; +- struct stv0367cab_state *cab_state = state->cab_state; +- +- enum stv0367cab_mod QAMSize; +- +- dprintk("%s:\n", __func__); +- +- p->symbol_rate = stv0367cab_GetSymbolRate(state, cab_state->mclk); +- +- QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); +- switch (QAMSize) { +- case FE_CAB_MOD_QAM16: +- p->modulation = QAM_16; +- break; +- case FE_CAB_MOD_QAM32: +- p->modulation = QAM_32; +- break; +- case FE_CAB_MOD_QAM64: +- p->modulation = QAM_64; +- break; +- case FE_CAB_MOD_QAM128: +- p->modulation = QAM_128; +- break; +- case QAM_256: +- p->modulation = QAM_256; +- break; +- default: +- break; +- } +- +- p->frequency = stv0367_get_tuner_freq(fe); +- +- dprintk("%s: tuner frequency = %d\n", __func__, p->frequency); +- +- if (state->config->if_khz == 0) { +- p->frequency += +- (stv0367cab_get_derot_freq(state, cab_state->adc_clk) - +- cab_state->adc_clk / 4000); +- return 0; +- } +- +- if (state->config->if_khz > cab_state->adc_clk / 1000) +- p->frequency += (state->config->if_khz +- - stv0367cab_get_derot_freq(state, cab_state->adc_clk) +- - cab_state->adc_clk / 1000); +- else +- p->frequency += (state->config->if_khz +- - stv0367cab_get_derot_freq(state, cab_state->adc_clk)); +- +- return 0; +-} +- +-#if 0 +-void stv0367cab_GetErrorCount(state, enum stv0367cab_mod QAMSize, +- u32 symbol_rate, FE_367qam_Monitor *Monitor_results) +-{ +- stv0367cab_OptimiseNByteAndGetBER(state, QAMSize, symbol_rate, Monitor_results); +- stv0367cab_GetPacketsCount(state, Monitor_results); +- +- return; +-} +- +-static int stv0367cab_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- return 0; +-} +-#endif +-static s32 stv0367cab_get_rf_lvl(struct stv0367_state *state) +-{ +- s32 rfLevel = 0; +- s32 RfAgcPwm = 0, IfAgcPwm = 0; +- u8 i; +- +- stv0367_writebits(state, F367CAB_STDBY_ADCGP, 0x0); +- +- RfAgcPwm = +- (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_LO) & 0x03) + +- (stv0367_readbits(state, F367CAB_RF_AGC1_LEVEL_HI) << 2); +- RfAgcPwm = 100 * RfAgcPwm / 1023; +- +- IfAgcPwm = +- stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_LO) + +- (stv0367_readbits(state, F367CAB_AGC_IF_PWMCMD_HI) << 8); +- if (IfAgcPwm >= 2048) +- IfAgcPwm -= 2048; +- else +- IfAgcPwm += 2048; +- +- IfAgcPwm = 100 * IfAgcPwm / 4095; +- +- /* For DTT75467 on NIM */ +- if (RfAgcPwm < 90 && IfAgcPwm < 28) { +- for (i = 0; i < RF_LOOKUP_TABLE_SIZE; i++) { +- if (RfAgcPwm <= stv0367cab_RF_LookUp1[0][i]) { +- rfLevel = (-1) * stv0367cab_RF_LookUp1[1][i]; +- break; +- } +- } +- if (i == RF_LOOKUP_TABLE_SIZE) +- rfLevel = -56; +- } else { /*if IF AGC>10*/ +- for (i = 0; i < RF_LOOKUP_TABLE2_SIZE; i++) { +- if (IfAgcPwm <= stv0367cab_RF_LookUp2[0][i]) { +- rfLevel = (-1) * stv0367cab_RF_LookUp2[1][i]; +- break; +- } +- } +- if (i == RF_LOOKUP_TABLE2_SIZE) +- rfLevel = -72; +- } +- return rfLevel; +-} +- +-static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- +- s32 signal = stv0367cab_get_rf_lvl(state); +- +- dprintk("%s: signal=%d dBm\n", __func__, signal); +- +- if (signal <= -72) +- *strength = 65535; +- else +- *strength = (22 + signal) * (-1311); +- +- dprintk("%s: strength=%d\n", __func__, (*strength)); +- +- return 0; +-} +- +-static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- u32 noisepercentage; +- enum stv0367cab_mod QAMSize; +- u32 regval = 0, temp = 0; +- int power, i; +- +- QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); +- switch (QAMSize) { +- case FE_CAB_MOD_QAM4: +- power = 21904; +- break; +- case FE_CAB_MOD_QAM16: +- power = 20480; +- break; +- case FE_CAB_MOD_QAM32: +- power = 23040; +- break; +- case FE_CAB_MOD_QAM64: +- power = 21504; +- break; +- case FE_CAB_MOD_QAM128: +- power = 23616; +- break; +- case FE_CAB_MOD_QAM256: +- power = 21760; +- break; +- case FE_CAB_MOD_QAM512: +- power = 1; +- break; +- case FE_CAB_MOD_QAM1024: +- power = 21280; +- break; +- default: +- power = 1; +- break; +- } +- +- for (i = 0; i < 10; i++) { +- regval += (stv0367_readbits(state, F367CAB_SNR_LO) +- + 256 * stv0367_readbits(state, F367CAB_SNR_HI)); +- } +- +- regval /= 10; /*for average over 10 times in for loop above*/ +- if (regval != 0) { +- temp = power +- * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER))); +- temp /= regval; +- } +- +- /* table values, not needed to calculate logarithms */ +- if (temp >= 5012) +- noisepercentage = 100; +- else if (temp >= 3981) +- noisepercentage = 93; +- else if (temp >= 3162) +- noisepercentage = 86; +- else if (temp >= 2512) +- noisepercentage = 79; +- else if (temp >= 1995) +- noisepercentage = 72; +- else if (temp >= 1585) +- noisepercentage = 65; +- else if (temp >= 1259) +- noisepercentage = 58; +- else if (temp >= 1000) +- noisepercentage = 50; +- else if (temp >= 794) +- noisepercentage = 43; +- else if (temp >= 501) +- noisepercentage = 36; +- else if (temp >= 316) +- noisepercentage = 29; +- else if (temp >= 200) +- noisepercentage = 22; +- else if (temp >= 158) +- noisepercentage = 14; +- else if (temp >= 126) +- noisepercentage = 7; +- else +- noisepercentage = 0; +- +- dprintk("%s: noisepercentage=%d\n", __func__, noisepercentage); +- +- *snr = (noisepercentage * 65535) / 100; +- +- return 0; +-} +- +-static int stv0367cab_read_ucblcks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct stv0367_state *state = fe->demodulator_priv; +- int corrected, tscount; +- +- *ucblocks = (stv0367_readreg(state, R367CAB_RS_COUNTER_5) << 8) +- | stv0367_readreg(state, R367CAB_RS_COUNTER_4); +- corrected = (stv0367_readreg(state, R367CAB_RS_COUNTER_3) << 8) +- | stv0367_readreg(state, R367CAB_RS_COUNTER_2); +- tscount = (stv0367_readreg(state, R367CAB_RS_COUNTER_2) << 8) +- | stv0367_readreg(state, R367CAB_RS_COUNTER_1); +- +- dprintk("%s: uncorrected blocks=%d corrected blocks=%d tscount=%d\n", +- __func__, *ucblocks, corrected, tscount); +- +- return 0; +-}; +- +-static struct dvb_frontend_ops stv0367cab_ops = { +- .delsys = { SYS_DVBC_ANNEX_A }, +- .info = { +- .name = "ST STV0367 DVB-C", +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 62500, +- .symbol_rate_min = 870000, +- .symbol_rate_max = 11700000, +- .caps = 0x400 |/* FE_CAN_QAM_4 */ +- FE_CAN_QAM_16 | FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | FE_CAN_QAM_128 | +- FE_CAN_QAM_256 | FE_CAN_FEC_AUTO +- }, +- .release = stv0367_release, +- .init = stv0367cab_init, +- .sleep = stv0367cab_sleep, +- .i2c_gate_ctrl = stv0367cab_gate_ctrl, +- .set_frontend = stv0367cab_set_frontend, +- .get_frontend = stv0367cab_get_frontend, +- .read_status = stv0367cab_read_status, +-/* .read_ber = stv0367cab_read_ber, */ +- .read_signal_strength = stv0367cab_read_strength, +- .read_snr = stv0367cab_read_snr, +- .read_ucblocks = stv0367cab_read_ucblcks, +- .get_tune_settings = stv0367_get_tune_settings, +-}; +- +-struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv0367_state *state = NULL; +- struct stv0367cab_state *cab_state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct stv0367_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- cab_state = kzalloc(sizeof(struct stv0367cab_state), GFP_KERNEL); +- if (cab_state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- state->config = config; +- cab_state->search_range = 280000; +- state->cab_state = cab_state; +- state->fe.ops = stv0367cab_ops; +- state->fe.demodulator_priv = state; +- state->chip_id = stv0367_readreg(state, 0xf000); +- +- dprintk("%s: chip_id = 0x%x\n", __func__, state->chip_id); +- +- /* check if the demod is there */ +- if ((state->chip_id != 0x50) && (state->chip_id != 0x60)) +- goto error; +- +- return &state->fe; +- +-error: +- kfree(cab_state); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(stv0367cab_attach); +- +-MODULE_PARM_DESC(debug, "Set debug"); +-MODULE_PARM_DESC(i2c_debug, "Set i2c debug"); +- +-MODULE_AUTHOR("Igor M. Liplianin"); +-MODULE_DESCRIPTION("ST STV0367 DVB-C/T demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stv0367.h b/drivers/media/dvb/frontends/stv0367.h +deleted file mode 100644 +index 93cc4a5..0000000 +--- a/drivers/media/dvb/frontends/stv0367.h ++++ /dev/null +@@ -1,66 +0,0 @@ +-/* +- * stv0367.h +- * +- * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2010,2011 NetUP Inc. +- * Copyright (C) 2010,2011 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0367_H +-#define STV0367_H +- +-#include +-#include "dvb_frontend.h" +- +-struct stv0367_config { +- u8 demod_address; +- u32 xtal; +- u32 if_khz;/*4500*/ +- int if_iq_mode; +- int ts_mode; +- int clk_pol; +-}; +- +-#if defined(CONFIG_DVB_STV0367) || (defined(CONFIG_DVB_STV0367_MODULE) \ +- && defined(MODULE)) +-extern struct +-dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c); +-extern struct +-dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct +-dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline struct +-dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/stv0367_priv.h b/drivers/media/dvb/frontends/stv0367_priv.h +deleted file mode 100644 +index 995db06..0000000 +--- a/drivers/media/dvb/frontends/stv0367_priv.h ++++ /dev/null +@@ -1,212 +0,0 @@ +-/* +- * stv0367_priv.h +- * +- * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2010,2011 NetUP Inc. +- * Copyright (C) 2010,2011 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +-/* Common driver error constants */ +- +-#ifndef STV0367_PRIV_H +-#define STV0367_PRIV_H +- +-#ifndef TRUE +- #define TRUE (1 == 1) +-#endif +-#ifndef FALSE +- #define FALSE (!TRUE) +-#endif +- +-#ifndef NULL +-#define NULL 0 +-#endif +- +-/* MACRO definitions */ +-#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) +-#define MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +-#define MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) +-#define INRANGE(X, Y, Z) \ +- ((((X) <= (Y)) && ((Y) <= (Z))) || \ +- (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) +- +-#ifndef MAKEWORD +-#define MAKEWORD(X, Y) (((X) << 8) + (Y)) +-#endif +- +-#define LSB(X) (((X) & 0xff)) +-#define MSB(Y) (((Y) >> 8) & 0xff) +-#define MMSB(Y)(((Y) >> 16) & 0xff) +- +-enum stv0367_ter_signal_type { +- FE_TER_NOAGC = 0, +- FE_TER_AGCOK = 5, +- FE_TER_NOTPS = 6, +- FE_TER_TPSOK = 7, +- FE_TER_NOSYMBOL = 8, +- FE_TER_BAD_CPQ = 9, +- FE_TER_PRFOUNDOK = 10, +- FE_TER_NOPRFOUND = 11, +- FE_TER_LOCKOK = 12, +- FE_TER_NOLOCK = 13, +- FE_TER_SYMBOLOK = 15, +- FE_TER_CPAMPOK = 16, +- FE_TER_NOCPAMP = 17, +- FE_TER_SWNOK = 18 +-}; +- +-enum stv0367_ts_mode { +- STV0367_OUTPUTMODE_DEFAULT, +- STV0367_SERIAL_PUNCT_CLOCK, +- STV0367_SERIAL_CONT_CLOCK, +- STV0367_PARALLEL_PUNCT_CLOCK, +- STV0367_DVBCI_CLOCK +-}; +- +-enum stv0367_clk_pol { +- STV0367_CLOCKPOLARITY_DEFAULT, +- STV0367_RISINGEDGE_CLOCK, +- STV0367_FALLINGEDGE_CLOCK +-}; +- +-enum stv0367_ter_bw { +- FE_TER_CHAN_BW_6M = 6, +- FE_TER_CHAN_BW_7M = 7, +- FE_TER_CHAN_BW_8M = 8 +-}; +- +-#if 0 +-enum FE_TER_Rate_TPS { +- FE_TER_TPS_1_2 = 0, +- FE_TER_TPS_2_3 = 1, +- FE_TER_TPS_3_4 = 2, +- FE_TER_TPS_5_6 = 3, +- FE_TER_TPS_7_8 = 4 +-}; +-#endif +- +-enum stv0367_ter_mode { +- FE_TER_MODE_2K, +- FE_TER_MODE_8K, +- FE_TER_MODE_4K +-}; +-#if 0 +-enum FE_TER_Hierarchy_Alpha { +- FE_TER_HIER_ALPHA_NONE, /* Regular modulation */ +- FE_TER_HIER_ALPHA_1, /* Hierarchical modulation a = 1*/ +- FE_TER_HIER_ALPHA_2, /* Hierarchical modulation a = 2*/ +- FE_TER_HIER_ALPHA_4 /* Hierarchical modulation a = 4*/ +-}; +-#endif +-enum stv0367_ter_hierarchy { +- FE_TER_HIER_NONE, /*Hierarchy None*/ +- FE_TER_HIER_LOW_PRIO, /*Hierarchy : Low Priority*/ +- FE_TER_HIER_HIGH_PRIO, /*Hierarchy : High Priority*/ +- FE_TER_HIER_PRIO_ANY /*Hierarchy :Any*/ +-}; +- +-#if 0 +-enum fe_stv0367_ter_spec { +- FE_TER_INVERSION_NONE = 0, +- FE_TER_INVERSION = 1, +- FE_TER_INVERSION_AUTO = 2, +- FE_TER_INVERSION_UNK = 4 +-}; +-#endif +- +-enum stv0367_ter_if_iq_mode { +- FE_TER_NORMAL_IF_TUNER = 0, +- FE_TER_LONGPATH_IF_TUNER = 1, +- FE_TER_IQ_TUNER = 2 +- +-}; +- +-#if 0 +-enum FE_TER_FECRate { +- FE_TER_FEC_NONE = 0x00, /* no FEC rate specified */ +- FE_TER_FEC_ALL = 0xFF, /* Logical OR of all FECs */ +- FE_TER_FEC_1_2 = 1, +- FE_TER_FEC_2_3 = (1 << 1), +- FE_TER_FEC_3_4 = (1 << 2), +- FE_TER_FEC_4_5 = (1 << 3), +- FE_TER_FEC_5_6 = (1 << 4), +- FE_TER_FEC_6_7 = (1 << 5), +- FE_TER_FEC_7_8 = (1 << 6), +- FE_TER_FEC_8_9 = (1 << 7) +-}; +- +-enum FE_TER_Rate { +- FE_TER_FE_1_2 = 0, +- FE_TER_FE_2_3 = 1, +- FE_TER_FE_3_4 = 2, +- FE_TER_FE_5_6 = 3, +- FE_TER_FE_6_7 = 4, +- FE_TER_FE_7_8 = 5 +-}; +-#endif +- +-enum stv0367_ter_force { +- FE_TER_FORCENONE = 0, +- FE_TER_FORCE_M_G = 1 +-}; +- +-enum stv0367cab_mod { +- FE_CAB_MOD_QAM4, +- FE_CAB_MOD_QAM16, +- FE_CAB_MOD_QAM32, +- FE_CAB_MOD_QAM64, +- FE_CAB_MOD_QAM128, +- FE_CAB_MOD_QAM256, +- FE_CAB_MOD_QAM512, +- FE_CAB_MOD_QAM1024 +-}; +-#if 0 +-enum { +- FE_CAB_FEC_A = 1, /* J83 Annex A */ +- FE_CAB_FEC_B = (1 << 1),/* J83 Annex B */ +- FE_CAB_FEC_C = (1 << 2) /* J83 Annex C */ +-} FE_CAB_FECType_t; +-#endif +-struct stv0367_cab_signal_info { +- int locked; +- u32 frequency; /* kHz */ +- u32 symbol_rate; /* Mbds */ +- enum stv0367cab_mod modulation; +- fe_spectral_inversion_t spect_inv; +- s32 Power_dBmx10; /* Power of the RF signal (dBm x 10) */ +- u32 CN_dBx10; /* Carrier to noise ratio (dB x 10) */ +- u32 BER; /* Bit error rate (x 10000000) */ +-}; +- +-enum stv0367_cab_signal_type { +- FE_CAB_NOTUNER, +- FE_CAB_NOAGC, +- FE_CAB_NOSIGNAL, +- FE_CAB_NOTIMING, +- FE_CAB_TIMINGOK, +- FE_CAB_NOCARRIER, +- FE_CAB_CARRIEROK, +- FE_CAB_NOBLIND, +- FE_CAB_BLINDOK, +- FE_CAB_NODEMOD, +- FE_CAB_DEMODOK, +- FE_CAB_DATAOK +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/stv0367_regs.h b/drivers/media/dvb/frontends/stv0367_regs.h +deleted file mode 100644 +index a96fbdc..0000000 +--- a/drivers/media/dvb/frontends/stv0367_regs.h ++++ /dev/null +@@ -1,3614 +0,0 @@ +-/* +- * stv0367_regs.h +- * +- * Driver for ST STV0367 DVB-T & DVB-C demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2010,2011 NetUP Inc. +- * Copyright (C) 2010,2011 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0367_REGS_H +-#define STV0367_REGS_H +- +-/* ID */ +-#define R367TER_ID 0xf000 +-#define F367TER_IDENTIFICATIONREG 0xf00000ff +- +-/* I2CRPT */ +-#define R367TER_I2CRPT 0xf001 +-#define F367TER_I2CT_ON 0xf0010080 +-#define F367TER_ENARPT_LEVEL 0xf0010070 +-#define F367TER_SCLT_DELAY 0xf0010008 +-#define F367TER_SCLT_NOD 0xf0010004 +-#define F367TER_STOP_ENABLE 0xf0010002 +-#define F367TER_SDAT_NOD 0xf0010001 +- +-/* TOPCTRL */ +-#define R367TER_TOPCTRL 0xf002 +-#define F367TER_STDBY 0xf0020080 +-#define F367TER_STDBY_FEC 0xf0020040 +-#define F367TER_STDBY_CORE 0xf0020020 +-#define F367TER_QAM_COFDM 0xf0020010 +-#define F367TER_TS_DIS 0xf0020008 +-#define F367TER_DIR_CLK_216 0xf0020004 +-#define F367TER_TUNER_BB 0xf0020002 +-#define F367TER_DVBT_H 0xf0020001 +- +-/* IOCFG0 */ +-#define R367TER_IOCFG0 0xf003 +-#define F367TER_OP0_SD 0xf0030080 +-#define F367TER_OP0_VAL 0xf0030040 +-#define F367TER_OP0_OD 0xf0030020 +-#define F367TER_OP0_INV 0xf0030010 +-#define F367TER_OP0_DACVALUE_HI 0xf003000f +- +-/* DAc0R */ +-#define R367TER_DAC0R 0xf004 +-#define F367TER_OP0_DACVALUE_LO 0xf00400ff +- +-/* IOCFG1 */ +-#define R367TER_IOCFG1 0xf005 +-#define F367TER_IP0 0xf0050040 +-#define F367TER_OP1_OD 0xf0050020 +-#define F367TER_OP1_INV 0xf0050010 +-#define F367TER_OP1_DACVALUE_HI 0xf005000f +- +-/* DAC1R */ +-#define R367TER_DAC1R 0xf006 +-#define F367TER_OP1_DACVALUE_LO 0xf00600ff +- +-/* IOCFG2 */ +-#define R367TER_IOCFG2 0xf007 +-#define F367TER_OP2_LOCK_CONF 0xf00700e0 +-#define F367TER_OP2_OD 0xf0070010 +-#define F367TER_OP2_VAL 0xf0070008 +-#define F367TER_OP1_LOCK_CONF 0xf0070007 +- +-/* SDFR */ +-#define R367TER_SDFR 0xf008 +-#define F367TER_OP0_FREQ 0xf00800f0 +-#define F367TER_OP1_FREQ 0xf008000f +- +-/* STATUS */ +-#define R367TER_STATUS 0xf009 +-#define F367TER_TPS_LOCK 0xf0090080 +-#define F367TER_SYR_LOCK 0xf0090040 +-#define F367TER_AGC_LOCK 0xf0090020 +-#define F367TER_PRF 0xf0090010 +-#define F367TER_LK 0xf0090008 +-#define F367TER_PR 0xf0090007 +- +-/* AUX_CLK */ +-#define R367TER_AUX_CLK 0xf00a +-#define F367TER_AUXFEC_CTL 0xf00a00c0 +-#define F367TER_DIS_CKX4 0xf00a0020 +-#define F367TER_CKSEL 0xf00a0018 +-#define F367TER_CKDIV_PROG 0xf00a0006 +-#define F367TER_AUXCLK_ENA 0xf00a0001 +- +-/* FREESYS1 */ +-#define R367TER_FREESYS1 0xf00b +-#define F367TER_FREE_SYS1 0xf00b00ff +- +-/* FREESYS2 */ +-#define R367TER_FREESYS2 0xf00c +-#define F367TER_FREE_SYS2 0xf00c00ff +- +-/* FREESYS3 */ +-#define R367TER_FREESYS3 0xf00d +-#define F367TER_FREE_SYS3 0xf00d00ff +- +-/* GPIO_CFG */ +-#define R367TER_GPIO_CFG 0xf00e +-#define F367TER_GPIO7_NOD 0xf00e0080 +-#define F367TER_GPIO7_CFG 0xf00e0040 +-#define F367TER_GPIO6_NOD 0xf00e0020 +-#define F367TER_GPIO6_CFG 0xf00e0010 +-#define F367TER_GPIO5_NOD 0xf00e0008 +-#define F367TER_GPIO5_CFG 0xf00e0004 +-#define F367TER_GPIO4_NOD 0xf00e0002 +-#define F367TER_GPIO4_CFG 0xf00e0001 +- +-/* GPIO_CMD */ +-#define R367TER_GPIO_CMD 0xf00f +-#define F367TER_GPIO7_VAL 0xf00f0008 +-#define F367TER_GPIO6_VAL 0xf00f0004 +-#define F367TER_GPIO5_VAL 0xf00f0002 +-#define F367TER_GPIO4_VAL 0xf00f0001 +- +-/* AGC2MAX */ +-#define R367TER_AGC2MAX 0xf010 +-#define F367TER_AGC2_MAX 0xf01000ff +- +-/* AGC2MIN */ +-#define R367TER_AGC2MIN 0xf011 +-#define F367TER_AGC2_MIN 0xf01100ff +- +-/* AGC1MAX */ +-#define R367TER_AGC1MAX 0xf012 +-#define F367TER_AGC1_MAX 0xf01200ff +- +-/* AGC1MIN */ +-#define R367TER_AGC1MIN 0xf013 +-#define F367TER_AGC1_MIN 0xf01300ff +- +-/* AGCR */ +-#define R367TER_AGCR 0xf014 +-#define F367TER_RATIO_A 0xf01400e0 +-#define F367TER_RATIO_B 0xf0140018 +-#define F367TER_RATIO_C 0xf0140007 +- +-/* AGC2TH */ +-#define R367TER_AGC2TH 0xf015 +-#define F367TER_AGC2_THRES 0xf01500ff +- +-/* AGC12c */ +-#define R367TER_AGC12C 0xf016 +-#define F367TER_AGC1_IV 0xf0160080 +-#define F367TER_AGC1_OD 0xf0160040 +-#define F367TER_AGC1_LOAD 0xf0160020 +-#define F367TER_AGC2_IV 0xf0160010 +-#define F367TER_AGC2_OD 0xf0160008 +-#define F367TER_AGC2_LOAD 0xf0160004 +-#define F367TER_AGC12_MODE 0xf0160003 +- +-/* AGCCTRL1 */ +-#define R367TER_AGCCTRL1 0xf017 +-#define F367TER_DAGC_ON 0xf0170080 +-#define F367TER_INVERT_AGC12 0xf0170040 +-#define F367TER_AGC1_MODE 0xf0170008 +-#define F367TER_AGC2_MODE 0xf0170007 +- +-/* AGCCTRL2 */ +-#define R367TER_AGCCTRL2 0xf018 +-#define F367TER_FRZ2_CTRL 0xf0180060 +-#define F367TER_FRZ1_CTRL 0xf0180018 +-#define F367TER_TIME_CST 0xf0180007 +- +-/* AGC1VAL1 */ +-#define R367TER_AGC1VAL1 0xf019 +-#define F367TER_AGC1_VAL_LO 0xf01900ff +- +-/* AGC1VAL2 */ +-#define R367TER_AGC1VAL2 0xf01a +-#define F367TER_AGC1_VAL_HI 0xf01a000f +- +-/* AGC2VAL1 */ +-#define R367TER_AGC2VAL1 0xf01b +-#define F367TER_AGC2_VAL_LO 0xf01b00ff +- +-/* AGC2VAL2 */ +-#define R367TER_AGC2VAL2 0xf01c +-#define F367TER_AGC2_VAL_HI 0xf01c000f +- +-/* AGC2PGA */ +-#define R367TER_AGC2PGA 0xf01d +-#define F367TER_AGC2_PGA 0xf01d00ff +- +-/* OVF_RATE1 */ +-#define R367TER_OVF_RATE1 0xf01e +-#define F367TER_OVF_RATE_HI 0xf01e000f +- +-/* OVF_RATE2 */ +-#define R367TER_OVF_RATE2 0xf01f +-#define F367TER_OVF_RATE_LO 0xf01f00ff +- +-/* GAIN_SRC1 */ +-#define R367TER_GAIN_SRC1 0xf020 +-#define F367TER_INV_SPECTR 0xf0200080 +-#define F367TER_IQ_INVERT 0xf0200040 +-#define F367TER_INR_BYPASS 0xf0200020 +-#define F367TER_STATUS_INV_SPECRUM 0xf0200010 +-#define F367TER_GAIN_SRC_HI 0xf020000f +- +-/* GAIN_SRC2 */ +-#define R367TER_GAIN_SRC2 0xf021 +-#define F367TER_GAIN_SRC_LO 0xf02100ff +- +-/* INC_DEROT1 */ +-#define R367TER_INC_DEROT1 0xf022 +-#define F367TER_INC_DEROT_HI 0xf02200ff +- +-/* INC_DEROT2 */ +-#define R367TER_INC_DEROT2 0xf023 +-#define F367TER_INC_DEROT_LO 0xf02300ff +- +-/* PPM_CPAMP_DIR */ +-#define R367TER_PPM_CPAMP_DIR 0xf024 +-#define F367TER_PPM_CPAMP_DIRECT 0xf02400ff +- +-/* PPM_CPAMP_INV */ +-#define R367TER_PPM_CPAMP_INV 0xf025 +-#define F367TER_PPM_CPAMP_INVER 0xf02500ff +- +-/* FREESTFE_1 */ +-#define R367TER_FREESTFE_1 0xf026 +-#define F367TER_SYMBOL_NUMBER_INC 0xf02600c0 +-#define F367TER_SEL_LSB 0xf0260004 +-#define F367TER_AVERAGE_ON 0xf0260002 +-#define F367TER_DC_ADJ 0xf0260001 +- +-/* FREESTFE_2 */ +-#define R367TER_FREESTFE_2 0xf027 +-#define F367TER_SEL_SRCOUT 0xf02700c0 +-#define F367TER_SEL_SYRTHR 0xf027001f +- +-/* DCOFFSET */ +-#define R367TER_DCOFFSET 0xf028 +-#define F367TER_SELECT_I_Q 0xf0280080 +-#define F367TER_DC_OFFSET 0xf028007f +- +-/* EN_PROCESS */ +-#define R367TER_EN_PROCESS 0xf029 +-#define F367TER_FREE 0xf02900f0 +-#define F367TER_ENAB_MANUAL 0xf0290001 +- +-/* SDI_SMOOTHER */ +-#define R367TER_SDI_SMOOTHER 0xf02a +-#define F367TER_DIS_SMOOTH 0xf02a0080 +-#define F367TER_SDI_INC_SMOOTHER 0xf02a007f +- +-/* FE_LOOP_OPEN */ +-#define R367TER_FE_LOOP_OPEN 0xf02b +-#define F367TER_TRL_LOOP_OP 0xf02b0002 +-#define F367TER_CRL_LOOP_OP 0xf02b0001 +- +-/* FREQOFF1 */ +-#define R367TER_FREQOFF1 0xf02c +-#define F367TER_FREQ_OFFSET_LOOP_OPEN_VHI 0xf02c00ff +- +-/* FREQOFF2 */ +-#define R367TER_FREQOFF2 0xf02d +-#define F367TER_FREQ_OFFSET_LOOP_OPEN_HI 0xf02d00ff +- +-/* FREQOFF3 */ +-#define R367TER_FREQOFF3 0xf02e +-#define F367TER_FREQ_OFFSET_LOOP_OPEN_LO 0xf02e00ff +- +-/* TIMOFF1 */ +-#define R367TER_TIMOFF1 0xf02f +-#define F367TER_TIM_OFFSET_LOOP_OPEN_HI 0xf02f00ff +- +-/* TIMOFF2 */ +-#define R367TER_TIMOFF2 0xf030 +-#define F367TER_TIM_OFFSET_LOOP_OPEN_LO 0xf03000ff +- +-/* EPQ */ +-#define R367TER_EPQ 0xf031 +-#define F367TER_EPQ1 0xf03100ff +- +-/* EPQAUTO */ +-#define R367TER_EPQAUTO 0xf032 +-#define F367TER_EPQ2 0xf03200ff +- +-/* SYR_UPDATE */ +-#define R367TER_SYR_UPDATE 0xf033 +-#define F367TER_SYR_PROTV 0xf0330080 +-#define F367TER_SYR_PROTV_GAIN 0xf0330060 +-#define F367TER_SYR_FILTER 0xf0330010 +-#define F367TER_SYR_TRACK_THRES 0xf033000c +- +-/* CHPFREE */ +-#define R367TER_CHPFREE 0xf034 +-#define F367TER_CHP_FREE 0xf03400ff +- +-/* PPM_STATE_MAC */ +-#define R367TER_PPM_STATE_MAC 0xf035 +-#define F367TER_PPM_STATE_MACHINE_DECODER 0xf035003f +- +-/* INR_THRESHOLD */ +-#define R367TER_INR_THRESHOLD 0xf036 +-#define F367TER_INR_THRESH 0xf03600ff +- +-/* EPQ_TPS_ID_CELL */ +-#define R367TER_EPQ_TPS_ID_CELL 0xf037 +-#define F367TER_ENABLE_LGTH_TO_CF 0xf0370080 +-#define F367TER_DIS_TPS_RSVD 0xf0370040 +-#define F367TER_DIS_BCH 0xf0370020 +-#define F367TER_DIS_ID_CEL 0xf0370010 +-#define F367TER_TPS_ADJUST_SYM 0xf037000f +- +-/* EPQ_CFG */ +-#define R367TER_EPQ_CFG 0xf038 +-#define F367TER_EPQ_RANGE 0xf0380002 +-#define F367TER_EPQ_SOFT 0xf0380001 +- +-/* EPQ_STATUS */ +-#define R367TER_EPQ_STATUS 0xf039 +-#define F367TER_SLOPE_INC 0xf03900fc +-#define F367TER_TPS_FIELD 0xf0390003 +- +-/* AUTORELOCK */ +-#define R367TER_AUTORELOCK 0xf03a +-#define F367TER_BYPASS_BER_TEMPO 0xf03a0080 +-#define F367TER_BER_TEMPO 0xf03a0070 +-#define F367TER_BYPASS_COFDM_TEMPO 0xf03a0008 +-#define F367TER_COFDM_TEMPO 0xf03a0007 +- +-/* BER_THR_VMSB */ +-#define R367TER_BER_THR_VMSB 0xf03b +-#define F367TER_BER_THRESHOLD_HI 0xf03b00ff +- +-/* BER_THR_MSB */ +-#define R367TER_BER_THR_MSB 0xf03c +-#define F367TER_BER_THRESHOLD_MID 0xf03c00ff +- +-/* BER_THR_LSB */ +-#define R367TER_BER_THR_LSB 0xf03d +-#define F367TER_BER_THRESHOLD_LO 0xf03d00ff +- +-/* CCD */ +-#define R367TER_CCD 0xf03e +-#define F367TER_CCD_DETECTED 0xf03e0080 +-#define F367TER_CCD_RESET 0xf03e0040 +-#define F367TER_CCD_THRESHOLD 0xf03e000f +- +-/* SPECTR_CFG */ +-#define R367TER_SPECTR_CFG 0xf03f +-#define F367TER_SPECT_CFG 0xf03f0003 +- +-/* CONSTMU_MSB */ +-#define R367TER_CONSTMU_MSB 0xf040 +-#define F367TER_CONSTMU_FREEZE 0xf0400080 +-#define F367TER_CONSTNU_FORCE_EN 0xf0400040 +-#define F367TER_CONST_MU_MSB 0xf040003f +- +-/* CONSTMU_LSB */ +-#define R367TER_CONSTMU_LSB 0xf041 +-#define F367TER_CONST_MU_LSB 0xf04100ff +- +-/* CONSTMU_MAX_MSB */ +-#define R367TER_CONSTMU_MAX_MSB 0xf042 +-#define F367TER_CONST_MU_MAX_MSB 0xf042003f +- +-/* CONSTMU_MAX_LSB */ +-#define R367TER_CONSTMU_MAX_LSB 0xf043 +-#define F367TER_CONST_MU_MAX_LSB 0xf04300ff +- +-/* ALPHANOISE */ +-#define R367TER_ALPHANOISE 0xf044 +-#define F367TER_USE_ALLFILTER 0xf0440080 +-#define F367TER_INTER_ON 0xf0440040 +-#define F367TER_ALPHA_NOISE 0xf044001f +- +-/* MAXGP_MSB */ +-#define R367TER_MAXGP_MSB 0xf045 +-#define F367TER_MUFILTER_LENGTH 0xf04500f0 +-#define F367TER_MAX_GP_MSB 0xf045000f +- +-/* MAXGP_LSB */ +-#define R367TER_MAXGP_LSB 0xf046 +-#define F367TER_MAX_GP_LSB 0xf04600ff +- +-/* ALPHAMSB */ +-#define R367TER_ALPHAMSB 0xf047 +-#define F367TER_CHC_DATARATE 0xf04700c0 +-#define F367TER_ALPHA_MSB 0xf047003f +- +-/* ALPHALSB */ +-#define R367TER_ALPHALSB 0xf048 +-#define F367TER_ALPHA_LSB 0xf04800ff +- +-/* PILOT_ACCU */ +-#define R367TER_PILOT_ACCU 0xf049 +-#define F367TER_USE_SCAT4ADDAPT 0xf0490080 +-#define F367TER_PILOT_ACC 0xf049001f +- +-/* PILOTMU_ACCU */ +-#define R367TER_PILOTMU_ACCU 0xf04a +-#define F367TER_DISCARD_BAD_SP 0xf04a0080 +-#define F367TER_DISCARD_BAD_CP 0xf04a0040 +-#define F367TER_PILOT_MU_ACCU 0xf04a001f +- +-/* FILT_CHANNEL_EST */ +-#define R367TER_FILT_CHANNEL_EST 0xf04b +-#define F367TER_USE_FILT_PILOT 0xf04b0080 +-#define F367TER_FILT_CHANNEL 0xf04b007f +- +-/* ALPHA_NOPISE_FREQ */ +-#define R367TER_ALPHA_NOPISE_FREQ 0xf04c +-#define F367TER_NOISE_FREQ_FILT 0xf04c0040 +-#define F367TER_ALPHA_NOISE_FREQ 0xf04c003f +- +-/* RATIO_PILOT */ +-#define R367TER_RATIO_PILOT 0xf04d +-#define F367TER_RATIO_MEAN_SP 0xf04d00f0 +-#define F367TER_RATIO_MEAN_CP 0xf04d000f +- +-/* CHC_CTL */ +-#define R367TER_CHC_CTL 0xf04e +-#define F367TER_TRACK_EN 0xf04e0080 +-#define F367TER_NOISE_NORM_EN 0xf04e0040 +-#define F367TER_FORCE_CHC_RESET 0xf04e0020 +-#define F367TER_SHORT_TIME 0xf04e0010 +-#define F367TER_FORCE_STATE_EN 0xf04e0008 +-#define F367TER_FORCE_STATE 0xf04e0007 +- +-/* EPQ_ADJUST */ +-#define R367TER_EPQ_ADJUST 0xf04f +-#define F367TER_ADJUST_SCAT_IND 0xf04f00c0 +-#define F367TER_ONE_SYMBOL 0xf04f0010 +-#define F367TER_EPQ_DECAY 0xf04f000e +-#define F367TER_HOLD_SLOPE 0xf04f0001 +- +-/* EPQ_THRES */ +-#define R367TER_EPQ_THRES 0xf050 +-#define F367TER_EPQ_THR 0xf05000ff +- +-/* OMEGA_CTL */ +-#define R367TER_OMEGA_CTL 0xf051 +-#define F367TER_OMEGA_RST 0xf0510080 +-#define F367TER_FREEZE_OMEGA 0xf0510040 +-#define F367TER_OMEGA_SEL 0xf051003f +- +-/* GP_CTL */ +-#define R367TER_GP_CTL 0xf052 +-#define F367TER_CHC_STATE 0xf05200e0 +-#define F367TER_FREEZE_GP 0xf0520010 +-#define F367TER_GP_SEL 0xf052000f +- +-/* MUMSB */ +-#define R367TER_MUMSB 0xf053 +-#define F367TER_MU_MSB 0xf053007f +- +-/* MULSB */ +-#define R367TER_MULSB 0xf054 +-#define F367TER_MU_LSB 0xf05400ff +- +-/* GPMSB */ +-#define R367TER_GPMSB 0xf055 +-#define F367TER_CSI_THRESHOLD 0xf05500e0 +-#define F367TER_GP_MSB 0xf055000f +- +-/* GPLSB */ +-#define R367TER_GPLSB 0xf056 +-#define F367TER_GP_LSB 0xf05600ff +- +-/* OMEGAMSB */ +-#define R367TER_OMEGAMSB 0xf057 +-#define F367TER_OMEGA_MSB 0xf057007f +- +-/* OMEGALSB */ +-#define R367TER_OMEGALSB 0xf058 +-#define F367TER_OMEGA_LSB 0xf05800ff +- +-/* SCAT_NB */ +-#define R367TER_SCAT_NB 0xf059 +-#define F367TER_CHC_TEST 0xf05900f8 +-#define F367TER_SCAT_NUMB 0xf0590003 +- +-/* CHC_DUMMY */ +-#define R367TER_CHC_DUMMY 0xf05a +-#define F367TER_CHC_DUM 0xf05a00ff +- +-/* INC_CTL */ +-#define R367TER_INC_CTL 0xf05b +-#define F367TER_INC_BYPASS 0xf05b0080 +-#define F367TER_INC_NDEPTH 0xf05b000c +-#define F367TER_INC_MADEPTH 0xf05b0003 +- +-/* INCTHRES_COR1 */ +-#define R367TER_INCTHRES_COR1 0xf05c +-#define F367TER_INC_THRES_COR1 0xf05c00ff +- +-/* INCTHRES_COR2 */ +-#define R367TER_INCTHRES_COR2 0xf05d +-#define F367TER_INC_THRES_COR2 0xf05d00ff +- +-/* INCTHRES_DET1 */ +-#define R367TER_INCTHRES_DET1 0xf05e +-#define F367TER_INC_THRES_DET1 0xf05e003f +- +-/* INCTHRES_DET2 */ +-#define R367TER_INCTHRES_DET2 0xf05f +-#define F367TER_INC_THRES_DET2 0xf05f003f +- +-/* IIR_CELLNB */ +-#define R367TER_IIR_CELLNB 0xf060 +-#define F367TER_NRST_IIR 0xf0600080 +-#define F367TER_IIR_CELL_NB 0xf0600007 +- +-/* IIRCX_COEFF1_MSB */ +-#define R367TER_IIRCX_COEFF1_MSB 0xf061 +-#define F367TER_IIR_CX_COEFF1_MSB 0xf06100ff +- +-/* IIRCX_COEFF1_LSB */ +-#define R367TER_IIRCX_COEFF1_LSB 0xf062 +-#define F367TER_IIR_CX_COEFF1_LSB 0xf06200ff +- +-/* IIRCX_COEFF2_MSB */ +-#define R367TER_IIRCX_COEFF2_MSB 0xf063 +-#define F367TER_IIR_CX_COEFF2_MSB 0xf06300ff +- +-/* IIRCX_COEFF2_LSB */ +-#define R367TER_IIRCX_COEFF2_LSB 0xf064 +-#define F367TER_IIR_CX_COEFF2_LSB 0xf06400ff +- +-/* IIRCX_COEFF3_MSB */ +-#define R367TER_IIRCX_COEFF3_MSB 0xf065 +-#define F367TER_IIR_CX_COEFF3_MSB 0xf06500ff +- +-/* IIRCX_COEFF3_LSB */ +-#define R367TER_IIRCX_COEFF3_LSB 0xf066 +-#define F367TER_IIR_CX_COEFF3_LSB 0xf06600ff +- +-/* IIRCX_COEFF4_MSB */ +-#define R367TER_IIRCX_COEFF4_MSB 0xf067 +-#define F367TER_IIR_CX_COEFF4_MSB 0xf06700ff +- +-/* IIRCX_COEFF4_LSB */ +-#define R367TER_IIRCX_COEFF4_LSB 0xf068 +-#define F367TER_IIR_CX_COEFF4_LSB 0xf06800ff +- +-/* IIRCX_COEFF5_MSB */ +-#define R367TER_IIRCX_COEFF5_MSB 0xf069 +-#define F367TER_IIR_CX_COEFF5_MSB 0xf06900ff +- +-/* IIRCX_COEFF5_LSB */ +-#define R367TER_IIRCX_COEFF5_LSB 0xf06a +-#define F367TER_IIR_CX_COEFF5_LSB 0xf06a00ff +- +-/* FEPATH_CFG */ +-#define R367TER_FEPATH_CFG 0xf06b +-#define F367TER_DEMUX_SWAP 0xf06b0004 +-#define F367TER_DIGAGC_SWAP 0xf06b0002 +-#define F367TER_LONGPATH_IF 0xf06b0001 +- +-/* PMC1_FUNC */ +-#define R367TER_PMC1_FUNC 0xf06c +-#define F367TER_SOFT_RSTN 0xf06c0080 +-#define F367TER_PMC1_AVERAGE_TIME 0xf06c0078 +-#define F367TER_PMC1_WAIT_TIME 0xf06c0006 +-#define F367TER_PMC1_2N_SEL 0xf06c0001 +- +-/* PMC1_FOR */ +-#define R367TER_PMC1_FOR 0xf06d +-#define F367TER_PMC1_FORCE 0xf06d0080 +-#define F367TER_PMC1_FORCE_VALUE 0xf06d007c +- +-/* PMC2_FUNC */ +-#define R367TER_PMC2_FUNC 0xf06e +-#define F367TER_PMC2_SOFT_STN 0xf06e0080 +-#define F367TER_PMC2_ACCU_TIME 0xf06e0070 +-#define F367TER_PMC2_CMDP_MN 0xf06e0008 +-#define F367TER_PMC2_SWAP 0xf06e0004 +- +-/* STATUS_ERR_DA */ +-#define R367TER_STATUS_ERR_DA 0xf06f +-#define F367TER_COM_USEGAINTRK 0xf06f0080 +-#define F367TER_COM_AGCLOCK 0xf06f0040 +-#define F367TER_AUT_AGCLOCK 0xf06f0020 +-#define F367TER_MIN_ERR_X_LSB 0xf06f000f +- +-/* DIG_AGC_R */ +-#define R367TER_DIG_AGC_R 0xf070 +-#define F367TER_COM_SOFT_RSTN 0xf0700080 +-#define F367TER_COM_AGC_ON 0xf0700040 +-#define F367TER_COM_EARLY 0xf0700020 +-#define F367TER_AUT_SOFT_RESETN 0xf0700010 +-#define F367TER_AUT_AGC_ON 0xf0700008 +-#define F367TER_AUT_EARLY 0xf0700004 +-#define F367TER_AUT_ROT_EN 0xf0700002 +-#define F367TER_LOCK_SOFT_RESETN 0xf0700001 +- +-/* COMAGC_TARMSB */ +-#define R367TER_COMAGC_TARMSB 0xf071 +-#define F367TER_COM_AGC_TARGET_MSB 0xf07100ff +- +-/* COM_AGC_TAR_ENMODE */ +-#define R367TER_COM_AGC_TAR_ENMODE 0xf072 +-#define F367TER_COM_AGC_TARGET_LSB 0xf07200f0 +-#define F367TER_COM_ENMODE 0xf072000f +- +-/* COM_AGC_CFG */ +-#define R367TER_COM_AGC_CFG 0xf073 +-#define F367TER_COM_N 0xf07300f8 +-#define F367TER_COM_STABMODE 0xf0730006 +-#define F367TER_ERR_SEL 0xf0730001 +- +-/* COM_AGC_GAIN1 */ +-#define R367TER_COM_AGC_GAIN1 0xf074 +-#define F367TER_COM_GAIN1aCK 0xf07400f0 +-#define F367TER_COM_GAIN1TRK 0xf074000f +- +-/* AUT_AGC_TARGETMSB */ +-#define R367TER_AUT_AGC_TARGETMSB 0xf075 +-#define F367TER_AUT_AGC_TARGET_MSB 0xf07500ff +- +-/* LOCK_DET_MSB */ +-#define R367TER_LOCK_DET_MSB 0xf076 +-#define F367TER_LOCK_DETECT_MSB 0xf07600ff +- +-/* AGCTAR_LOCK_LSBS */ +-#define R367TER_AGCTAR_LOCK_LSBS 0xf077 +-#define F367TER_AUT_AGC_TARGET_LSB 0xf07700f0 +-#define F367TER_LOCK_DETECT_LSB 0xf077000f +- +-/* AUT_GAIN_EN */ +-#define R367TER_AUT_GAIN_EN 0xf078 +-#define F367TER_AUT_ENMODE 0xf07800f0 +-#define F367TER_AUT_GAIN2 0xf078000f +- +-/* AUT_CFG */ +-#define R367TER_AUT_CFG 0xf079 +-#define F367TER_AUT_N 0xf07900f8 +-#define F367TER_INT_CHOICE 0xf0790006 +-#define F367TER_INT_LOAD 0xf0790001 +- +-/* LOCKN */ +-#define R367TER_LOCKN 0xf07a +-#define F367TER_LOCK_N 0xf07a00f8 +-#define F367TER_SEL_IQNTAR 0xf07a0004 +-#define F367TER_LOCK_DETECT_CHOICE 0xf07a0003 +- +-/* INT_X_3 */ +-#define R367TER_INT_X_3 0xf07b +-#define F367TER_INT_X3 0xf07b00ff +- +-/* INT_X_2 */ +-#define R367TER_INT_X_2 0xf07c +-#define F367TER_INT_X2 0xf07c00ff +- +-/* INT_X_1 */ +-#define R367TER_INT_X_1 0xf07d +-#define F367TER_INT_X1 0xf07d00ff +- +-/* INT_X_0 */ +-#define R367TER_INT_X_0 0xf07e +-#define F367TER_INT_X0 0xf07e00ff +- +-/* MIN_ERRX_MSB */ +-#define R367TER_MIN_ERRX_MSB 0xf07f +-#define F367TER_MIN_ERR_X_MSB 0xf07f00ff +- +-/* COR_CTL */ +-#define R367TER_COR_CTL 0xf080 +-#define F367TER_CORE_ACTIVE 0xf0800020 +-#define F367TER_HOLD 0xf0800010 +-#define F367TER_CORE_STATE_CTL 0xf080000f +- +-/* COR_STAT */ +-#define R367TER_COR_STAT 0xf081 +-#define F367TER_SCATT_LOCKED 0xf0810080 +-#define F367TER_TPS_LOCKED 0xf0810040 +-#define F367TER_SYR_LOCKED_COR 0xf0810020 +-#define F367TER_AGC_LOCKED_STAT 0xf0810010 +-#define F367TER_CORE_STATE_STAT 0xf081000f +- +-/* COR_INTEN */ +-#define R367TER_COR_INTEN 0xf082 +-#define F367TER_INTEN 0xf0820080 +-#define F367TER_INTEN_SYR 0xf0820020 +-#define F367TER_INTEN_FFT 0xf0820010 +-#define F367TER_INTEN_AGC 0xf0820008 +-#define F367TER_INTEN_TPS1 0xf0820004 +-#define F367TER_INTEN_TPS2 0xf0820002 +-#define F367TER_INTEN_TPS3 0xf0820001 +- +-/* COR_INTSTAT */ +-#define R367TER_COR_INTSTAT 0xf083 +-#define F367TER_INTSTAT_SYR 0xf0830020 +-#define F367TER_INTSTAT_FFT 0xf0830010 +-#define F367TER_INTSAT_AGC 0xf0830008 +-#define F367TER_INTSTAT_TPS1 0xf0830004 +-#define F367TER_INTSTAT_TPS2 0xf0830002 +-#define F367TER_INTSTAT_TPS3 0xf0830001 +- +-/* COR_MODEGUARD */ +-#define R367TER_COR_MODEGUARD 0xf084 +-#define F367TER_FORCE 0xf0840010 +-#define F367TER_MODE 0xf084000c +-#define F367TER_GUARD 0xf0840003 +- +-/* AGC_CTL */ +-#define R367TER_AGC_CTL 0xf085 +-#define F367TER_AGC_TIMING_FACTOR 0xf08500e0 +-#define F367TER_AGC_LAST 0xf0850010 +-#define F367TER_AGC_GAIN 0xf085000c +-#define F367TER_AGC_NEG 0xf0850002 +-#define F367TER_AGC_SET 0xf0850001 +- +-/* AGC_MANUAL1 */ +-#define R367TER_AGC_MANUAL1 0xf086 +-#define F367TER_AGC_VAL_LO 0xf08600ff +- +-/* AGC_MANUAL2 */ +-#define R367TER_AGC_MANUAL2 0xf087 +-#define F367TER_AGC_VAL_HI 0xf087000f +- +-/* AGC_TARG */ +-#define R367TER_AGC_TARG 0xf088 +-#define F367TER_AGC_TARGET 0xf08800ff +- +-/* AGC_GAIN1 */ +-#define R367TER_AGC_GAIN1 0xf089 +-#define F367TER_AGC_GAIN_LO 0xf08900ff +- +-/* AGC_GAIN2 */ +-#define R367TER_AGC_GAIN2 0xf08a +-#define F367TER_AGC_LOCKED_GAIN2 0xf08a0010 +-#define F367TER_AGC_GAIN_HI 0xf08a000f +- +-/* RESERVED_1 */ +-#define R367TER_RESERVED_1 0xf08b +-#define F367TER_RESERVED1 0xf08b00ff +- +-/* RESERVED_2 */ +-#define R367TER_RESERVED_2 0xf08c +-#define F367TER_RESERVED2 0xf08c00ff +- +-/* RESERVED_3 */ +-#define R367TER_RESERVED_3 0xf08d +-#define F367TER_RESERVED3 0xf08d00ff +- +-/* CAS_CTL */ +-#define R367TER_CAS_CTL 0xf08e +-#define F367TER_CCS_ENABLE 0xf08e0080 +-#define F367TER_ACS_DISABLE 0xf08e0040 +-#define F367TER_DAGC_DIS 0xf08e0020 +-#define F367TER_DAGC_GAIN 0xf08e0018 +-#define F367TER_CCSMU 0xf08e0007 +- +-/* CAS_FREQ */ +-#define R367TER_CAS_FREQ 0xf08f +-#define F367TER_CCS_FREQ 0xf08f00ff +- +-/* CAS_DAGCGAIN */ +-#define R367TER_CAS_DAGCGAIN 0xf090 +-#define F367TER_CAS_DAGC_GAIN 0xf09000ff +- +-/* SYR_CTL */ +-#define R367TER_SYR_CTL 0xf091 +-#define F367TER_SICTH_ENABLE 0xf0910080 +-#define F367TER_LONG_ECHO 0xf0910078 +-#define F367TER_AUTO_LE_EN 0xf0910004 +-#define F367TER_SYR_BYPASS 0xf0910002 +-#define F367TER_SYR_TR_DIS 0xf0910001 +- +-/* SYR_STAT */ +-#define R367TER_SYR_STAT 0xf092 +-#define F367TER_SYR_LOCKED_STAT 0xf0920010 +-#define F367TER_SYR_MODE 0xf092000c +-#define F367TER_SYR_GUARD 0xf0920003 +- +-/* SYR_NCO1 */ +-#define R367TER_SYR_NCO1 0xf093 +-#define F367TER_SYR_NCO_LO 0xf09300ff +- +-/* SYR_NCO2 */ +-#define R367TER_SYR_NCO2 0xf094 +-#define F367TER_SYR_NCO_HI 0xf094003f +- +-/* SYR_OFFSET1 */ +-#define R367TER_SYR_OFFSET1 0xf095 +-#define F367TER_SYR_OFFSET_LO 0xf09500ff +- +-/* SYR_OFFSET2 */ +-#define R367TER_SYR_OFFSET2 0xf096 +-#define F367TER_SYR_OFFSET_HI 0xf096003f +- +-/* FFT_CTL */ +-#define R367TER_FFT_CTL 0xf097 +-#define F367TER_SHIFT_FFT_TRIG 0xf0970018 +-#define F367TER_FFT_TRIGGER 0xf0970004 +-#define F367TER_FFT_MANUAL 0xf0970002 +-#define F367TER_IFFT_MODE 0xf0970001 +- +-/* SCR_CTL */ +-#define R367TER_SCR_CTL 0xf098 +-#define F367TER_SYRADJDECAY 0xf0980070 +-#define F367TER_SCR_CPEDIS 0xf0980002 +-#define F367TER_SCR_DIS 0xf0980001 +- +-/* PPM_CTL1 */ +-#define R367TER_PPM_CTL1 0xf099 +-#define F367TER_PPM_MAXFREQ 0xf0990030 +-#define F367TER_PPM_MAXTIM 0xf0990008 +-#define F367TER_PPM_INVSEL 0xf0990004 +-#define F367TER_PPM_SCATDIS 0xf0990002 +-#define F367TER_PPM_BYP 0xf0990001 +- +-/* TRL_CTL */ +-#define R367TER_TRL_CTL 0xf09a +-#define F367TER_TRL_NOMRATE_LSB 0xf09a0080 +-#define F367TER_TRL_GAIN_FACTOR 0xf09a0078 +-#define F367TER_TRL_LOOPGAIN 0xf09a0007 +- +-/* TRL_NOMRATE1 */ +-#define R367TER_TRL_NOMRATE1 0xf09b +-#define F367TER_TRL_NOMRATE_LO 0xf09b00ff +- +-/* TRL_NOMRATE2 */ +-#define R367TER_TRL_NOMRATE2 0xf09c +-#define F367TER_TRL_NOMRATE_HI 0xf09c00ff +- +-/* TRL_TIME1 */ +-#define R367TER_TRL_TIME1 0xf09d +-#define F367TER_TRL_TOFFSET_LO 0xf09d00ff +- +-/* TRL_TIME2 */ +-#define R367TER_TRL_TIME2 0xf09e +-#define F367TER_TRL_TOFFSET_HI 0xf09e00ff +- +-/* CRL_CTL */ +-#define R367TER_CRL_CTL 0xf09f +-#define F367TER_CRL_DIS 0xf09f0080 +-#define F367TER_CRL_GAIN_FACTOR 0xf09f0078 +-#define F367TER_CRL_LOOPGAIN 0xf09f0007 +- +-/* CRL_FREQ1 */ +-#define R367TER_CRL_FREQ1 0xf0a0 +-#define F367TER_CRL_FOFFSET_LO 0xf0a000ff +- +-/* CRL_FREQ2 */ +-#define R367TER_CRL_FREQ2 0xf0a1 +-#define F367TER_CRL_FOFFSET_HI 0xf0a100ff +- +-/* CRL_FREQ3 */ +-#define R367TER_CRL_FREQ3 0xf0a2 +-#define F367TER_CRL_FOFFSET_VHI 0xf0a200ff +- +-/* TPS_SFRAME_CTL */ +-#define R367TER_TPS_SFRAME_CTL 0xf0a3 +-#define F367TER_TPS_SFRAME_SYNC 0xf0a30001 +- +-/* CHC_SNR */ +-#define R367TER_CHC_SNR 0xf0a4 +-#define F367TER_CHCSNR 0xf0a400ff +- +-/* BDI_CTL */ +-#define R367TER_BDI_CTL 0xf0a5 +-#define F367TER_BDI_LPSEL 0xf0a50002 +-#define F367TER_BDI_SERIAL 0xf0a50001 +- +-/* DMP_CTL */ +-#define R367TER_DMP_CTL 0xf0a6 +-#define F367TER_DMP_SCALING_FACTOR 0xf0a6001e +-#define F367TER_DMP_SDDIS 0xf0a60001 +- +-/* TPS_RCVD1 */ +-#define R367TER_TPS_RCVD1 0xf0a7 +-#define F367TER_TPS_CHANGE 0xf0a70040 +-#define F367TER_BCH_OK 0xf0a70020 +-#define F367TER_TPS_SYNC 0xf0a70010 +-#define F367TER_TPS_FRAME 0xf0a70003 +- +-/* TPS_RCVD2 */ +-#define R367TER_TPS_RCVD2 0xf0a8 +-#define F367TER_TPS_HIERMODE 0xf0a80070 +-#define F367TER_TPS_CONST 0xf0a80003 +- +-/* TPS_RCVD3 */ +-#define R367TER_TPS_RCVD3 0xf0a9 +-#define F367TER_TPS_LPCODE 0xf0a90070 +-#define F367TER_TPS_HPCODE 0xf0a90007 +- +-/* TPS_RCVD4 */ +-#define R367TER_TPS_RCVD4 0xf0aa +-#define F367TER_TPS_GUARD 0xf0aa0030 +-#define F367TER_TPS_MODE 0xf0aa0003 +- +-/* TPS_ID_CELL1 */ +-#define R367TER_TPS_ID_CELL1 0xf0ab +-#define F367TER_TPS_ID_CELL_LO 0xf0ab00ff +- +-/* TPS_ID_CELL2 */ +-#define R367TER_TPS_ID_CELL2 0xf0ac +-#define F367TER_TPS_ID_CELL_HI 0xf0ac00ff +- +-/* TPS_RCVD5_SET1 */ +-#define R367TER_TPS_RCVD5_SET1 0xf0ad +-#define F367TER_TPS_NA 0xf0ad00fC +-#define F367TER_TPS_SETFRAME 0xf0ad0003 +- +-/* TPS_SET2 */ +-#define R367TER_TPS_SET2 0xf0ae +-#define F367TER_TPS_SETHIERMODE 0xf0ae0070 +-#define F367TER_TPS_SETCONST 0xf0ae0003 +- +-/* TPS_SET3 */ +-#define R367TER_TPS_SET3 0xf0af +-#define F367TER_TPS_SETLPCODE 0xf0af0070 +-#define F367TER_TPS_SETHPCODE 0xf0af0007 +- +-/* TPS_CTL */ +-#define R367TER_TPS_CTL 0xf0b0 +-#define F367TER_TPS_IMM 0xf0b00004 +-#define F367TER_TPS_BCHDIS 0xf0b00002 +-#define F367TER_TPS_UPDDIS 0xf0b00001 +- +-/* CTL_FFTOSNUM */ +-#define R367TER_CTL_FFTOSNUM 0xf0b1 +-#define F367TER_SYMBOL_NUMBER 0xf0b1007f +- +-/* TESTSELECT */ +-#define R367TER_TESTSELECT 0xf0b2 +-#define F367TER_TEST_SELECT 0xf0b2001f +- +-/* MSC_REV */ +-#define R367TER_MSC_REV 0xf0b3 +-#define F367TER_REV_NUMBER 0xf0b300ff +- +-/* PIR_CTL */ +-#define R367TER_PIR_CTL 0xf0b4 +-#define F367TER_FREEZE 0xf0b40001 +- +-/* SNR_CARRIER1 */ +-#define R367TER_SNR_CARRIER1 0xf0b5 +-#define F367TER_SNR_CARRIER_LO 0xf0b500ff +- +-/* SNR_CARRIER2 */ +-#define R367TER_SNR_CARRIER2 0xf0b6 +-#define F367TER_MEAN 0xf0b600c0 +-#define F367TER_SNR_CARRIER_HI 0xf0b6001f +- +-/* PPM_CPAMP */ +-#define R367TER_PPM_CPAMP 0xf0b7 +-#define F367TER_PPM_CPC 0xf0b700ff +- +-/* TSM_AP0 */ +-#define R367TER_TSM_AP0 0xf0b8 +-#define F367TER_ADDRESS_BYTE_0 0xf0b800ff +- +-/* TSM_AP1 */ +-#define R367TER_TSM_AP1 0xf0b9 +-#define F367TER_ADDRESS_BYTE_1 0xf0b900ff +- +-/* TSM_AP2 */ +-#define R367TER_TSM_AP2 0xf0bA +-#define F367TER_DATA_BYTE_0 0xf0ba00ff +- +-/* TSM_AP3 */ +-#define R367TER_TSM_AP3 0xf0bB +-#define F367TER_DATA_BYTE_1 0xf0bb00ff +- +-/* TSM_AP4 */ +-#define R367TER_TSM_AP4 0xf0bC +-#define F367TER_DATA_BYTE_2 0xf0bc00ff +- +-/* TSM_AP5 */ +-#define R367TER_TSM_AP5 0xf0bD +-#define F367TER_DATA_BYTE_3 0xf0bd00ff +- +-/* TSM_AP6 */ +-#define R367TER_TSM_AP6 0xf0bE +-#define F367TER_TSM_AP_6 0xf0be00ff +- +-/* TSM_AP7 */ +-#define R367TER_TSM_AP7 0xf0bF +-#define F367TER_MEM_SELECT_BYTE 0xf0bf00ff +- +-/* TSTRES */ +-#define R367TER_TSTRES 0xf0c0 +-#define F367TER_FRES_DISPLAY 0xf0c00080 +-#define F367TER_FRES_FIFO_AD 0xf0c00020 +-#define F367TER_FRESRS 0xf0c00010 +-#define F367TER_FRESACS 0xf0c00008 +-#define F367TER_FRESFEC 0xf0c00004 +-#define F367TER_FRES_PRIF 0xf0c00002 +-#define F367TER_FRESCORE 0xf0c00001 +- +-/* ANACTRL */ +-#define R367TER_ANACTRL 0xf0c1 +-#define F367TER_BYPASS_XTAL 0xf0c10040 +-#define F367TER_BYPASS_PLLXN 0xf0c1000c +-#define F367TER_DIS_PAD_OSC 0xf0c10002 +-#define F367TER_STDBY_PLLXN 0xf0c10001 +- +-/* TSTBUS */ +-#define R367TER_TSTBUS 0xf0c2 +-#define F367TER_TS_BYTE_CLK_INV 0xf0c20080 +-#define F367TER_CFG_IP 0xf0c20070 +-#define F367TER_CFG_TST 0xf0c2000f +- +-/* TSTRATE */ +-#define R367TER_TSTRATE 0xf0c6 +-#define F367TER_FORCEPHA 0xf0c60080 +-#define F367TER_FNEWPHA 0xf0c60010 +-#define F367TER_FROT90 0xf0c60008 +-#define F367TER_FR 0xf0c60007 +- +-/* CONSTMODE */ +-#define R367TER_CONSTMODE 0xf0cb +-#define F367TER_TST_PRIF 0xf0cb00e0 +-#define F367TER_CAR_TYPE 0xf0cb0018 +-#define F367TER_CONST_MODE 0xf0cb0003 +- +-/* CONSTCARR1 */ +-#define R367TER_CONSTCARR1 0xf0cc +-#define F367TER_CONST_CARR_LO 0xf0cc00ff +- +-/* CONSTCARR2 */ +-#define R367TER_CONSTCARR2 0xf0cd +-#define F367TER_CONST_CARR_HI 0xf0cd001f +- +-/* ICONSTEL */ +-#define R367TER_ICONSTEL 0xf0ce +-#define F367TER_PICONSTEL 0xf0ce00ff +- +-/* QCONSTEL */ +-#define R367TER_QCONSTEL 0xf0cf +-#define F367TER_PQCONSTEL 0xf0cf00ff +- +-/* TSTBISTRES0 */ +-#define R367TER_TSTBISTRES0 0xf0d0 +-#define F367TER_BEND_PPM 0xf0d00080 +-#define F367TER_BBAD_PPM 0xf0d00040 +-#define F367TER_BEND_FFTW 0xf0d00020 +-#define F367TER_BBAD_FFTW 0xf0d00010 +-#define F367TER_BEND_FFT_BUF 0xf0d00008 +-#define F367TER_BBAD_FFT_BUF 0xf0d00004 +-#define F367TER_BEND_SYR 0xf0d00002 +-#define F367TER_BBAD_SYR 0xf0d00001 +- +-/* TSTBISTRES1 */ +-#define R367TER_TSTBISTRES1 0xf0d1 +-#define F367TER_BEND_CHC_CP 0xf0d10080 +-#define F367TER_BBAD_CHC_CP 0xf0d10040 +-#define F367TER_BEND_CHCI 0xf0d10020 +-#define F367TER_BBAD_CHCI 0xf0d10010 +-#define F367TER_BEND_BDI 0xf0d10008 +-#define F367TER_BBAD_BDI 0xf0d10004 +-#define F367TER_BEND_SDI 0xf0d10002 +-#define F367TER_BBAD_SDI 0xf0d10001 +- +-/* TSTBISTRES2 */ +-#define R367TER_TSTBISTRES2 0xf0d2 +-#define F367TER_BEND_CHC_INC 0xf0d20080 +-#define F367TER_BBAD_CHC_INC 0xf0d20040 +-#define F367TER_BEND_CHC_SPP 0xf0d20020 +-#define F367TER_BBAD_CHC_SPP 0xf0d20010 +-#define F367TER_BEND_CHC_CPP 0xf0d20008 +-#define F367TER_BBAD_CHC_CPP 0xf0d20004 +-#define F367TER_BEND_CHC_SP 0xf0d20002 +-#define F367TER_BBAD_CHC_SP 0xf0d20001 +- +-/* TSTBISTRES3 */ +-#define R367TER_TSTBISTRES3 0xf0d3 +-#define F367TER_BEND_QAM 0xf0d30080 +-#define F367TER_BBAD_QAM 0xf0d30040 +-#define F367TER_BEND_SFEC_VIT 0xf0d30020 +-#define F367TER_BBAD_SFEC_VIT 0xf0d30010 +-#define F367TER_BEND_SFEC_DLINE 0xf0d30008 +-#define F367TER_BBAD_SFEC_DLINE 0xf0d30004 +-#define F367TER_BEND_SFEC_HW 0xf0d30002 +-#define F367TER_BBAD_SFEC_HW 0xf0d30001 +- +-/* RF_AGC1 */ +-#define R367TER_RF_AGC1 0xf0d4 +-#define F367TER_RF_AGC1_LEVEL_HI 0xf0d400ff +- +-/* RF_AGC2 */ +-#define R367TER_RF_AGC2 0xf0d5 +-#define F367TER_REF_ADGP 0xf0d50080 +-#define F367TER_STDBY_ADCGP 0xf0d50020 +-#define F367TER_CHANNEL_SEL 0xf0d5001c +-#define F367TER_RF_AGC1_LEVEL_LO 0xf0d50003 +- +-/* ANADIGCTRL */ +-#define R367TER_ANADIGCTRL 0xf0d7 +-#define F367TER_SEL_CLKDEM 0xf0d70020 +-#define F367TER_EN_BUFFER_Q 0xf0d70010 +-#define F367TER_EN_BUFFER_I 0xf0d70008 +-#define F367TER_ADC_RIS_EGDE 0xf0d70004 +-#define F367TER_SGN_ADC 0xf0d70002 +-#define F367TER_SEL_AD12_SYNC 0xf0d70001 +- +-/* PLLMDIV */ +-#define R367TER_PLLMDIV 0xf0d8 +-#define F367TER_PLL_MDIV 0xf0d800ff +- +-/* PLLNDIV */ +-#define R367TER_PLLNDIV 0xf0d9 +-#define F367TER_PLL_NDIV 0xf0d900ff +- +-/* PLLSETUP */ +-#define R367TER_PLLSETUP 0xf0dA +-#define F367TER_PLL_PDIV 0xf0da0070 +-#define F367TER_PLL_KDIV 0xf0da000f +- +-/* DUAL_AD12 */ +-#define R367TER_DUAL_AD12 0xf0dB +-#define F367TER_FS20M 0xf0db0020 +-#define F367TER_FS50M 0xf0db0010 +-#define F367TER_INMODe0 0xf0db0008 +-#define F367TER_POFFQ 0xf0db0004 +-#define F367TER_POFFI 0xf0db0002 +-#define F367TER_INMODE1 0xf0db0001 +- +-/* TSTBIST */ +-#define R367TER_TSTBIST 0xf0dC +-#define F367TER_TST_BYP_CLK 0xf0dc0080 +-#define F367TER_TST_GCLKENA_STD 0xf0dc0040 +-#define F367TER_TST_GCLKENA 0xf0dc0020 +-#define F367TER_TST_MEMBIST 0xf0dc001f +- +-/* PAD_COMP_CTRL */ +-#define R367TER_PAD_COMP_CTRL 0xf0dD +-#define F367TER_COMPTQ 0xf0dd0010 +-#define F367TER_COMPEN 0xf0dd0008 +-#define F367TER_FREEZE2 0xf0dd0004 +-#define F367TER_SLEEP_INHBT 0xf0dd0002 +-#define F367TER_CHIP_SLEEP 0xf0dd0001 +- +-/* PAD_COMP_WR */ +-#define R367TER_PAD_COMP_WR 0xf0de +-#define F367TER_WR_ASRC 0xf0de007f +- +-/* PAD_COMP_RD */ +-#define R367TER_PAD_COMP_RD 0xf0df +-#define F367TER_COMPOK 0xf0df0080 +-#define F367TER_RD_ASRC 0xf0df007f +- +-/* SYR_TARGET_FFTADJT_MSB */ +-#define R367TER_SYR_TARGET_FFTADJT_MSB 0xf100 +-#define F367TER_SYR_START 0xf1000080 +-#define F367TER_SYR_TARGET_FFTADJ_HI 0xf100000f +- +-/* SYR_TARGET_FFTADJT_LSB */ +-#define R367TER_SYR_TARGET_FFTADJT_LSB 0xf101 +-#define F367TER_SYR_TARGET_FFTADJ_LO 0xf10100ff +- +-/* SYR_TARGET_CHCADJT_MSB */ +-#define R367TER_SYR_TARGET_CHCADJT_MSB 0xf102 +-#define F367TER_SYR_TARGET_CHCADJ_HI 0xf102000f +- +-/* SYR_TARGET_CHCADJT_LSB */ +-#define R367TER_SYR_TARGET_CHCADJT_LSB 0xf103 +-#define F367TER_SYR_TARGET_CHCADJ_LO 0xf10300ff +- +-/* SYR_FLAG */ +-#define R367TER_SYR_FLAG 0xf104 +-#define F367TER_TRIG_FLG1 0xf1040080 +-#define F367TER_TRIG_FLG0 0xf1040040 +-#define F367TER_FFT_FLG1 0xf1040008 +-#define F367TER_FFT_FLG0 0xf1040004 +-#define F367TER_CHC_FLG1 0xf1040002 +-#define F367TER_CHC_FLG0 0xf1040001 +- +-/* CRL_TARGET1 */ +-#define R367TER_CRL_TARGET1 0xf105 +-#define F367TER_CRL_START 0xf1050080 +-#define F367TER_CRL_TARGET_VHI 0xf105000f +- +-/* CRL_TARGET2 */ +-#define R367TER_CRL_TARGET2 0xf106 +-#define F367TER_CRL_TARGET_HI 0xf10600ff +- +-/* CRL_TARGET3 */ +-#define R367TER_CRL_TARGET3 0xf107 +-#define F367TER_CRL_TARGET_LO 0xf10700ff +- +-/* CRL_TARGET4 */ +-#define R367TER_CRL_TARGET4 0xf108 +-#define F367TER_CRL_TARGET_VLO 0xf10800ff +- +-/* CRL_FLAG */ +-#define R367TER_CRL_FLAG 0xf109 +-#define F367TER_CRL_FLAG1 0xf1090002 +-#define F367TER_CRL_FLAG0 0xf1090001 +- +-/* TRL_TARGET1 */ +-#define R367TER_TRL_TARGET1 0xf10a +-#define F367TER_TRL_TARGET_HI 0xf10a00ff +- +-/* TRL_TARGET2 */ +-#define R367TER_TRL_TARGET2 0xf10b +-#define F367TER_TRL_TARGET_LO 0xf10b00ff +- +-/* TRL_CHC */ +-#define R367TER_TRL_CHC 0xf10c +-#define F367TER_TRL_START 0xf10c0080 +-#define F367TER_CHC_START 0xf10c0040 +-#define F367TER_TRL_FLAG1 0xf10c0002 +-#define F367TER_TRL_FLAG0 0xf10c0001 +- +-/* CHC_SNR_TARG */ +-#define R367TER_CHC_SNR_TARG 0xf10d +-#define F367TER_CHC_SNR_TARGET 0xf10d00ff +- +-/* TOP_TRACK */ +-#define R367TER_TOP_TRACK 0xf10e +-#define F367TER_TOP_START 0xf10e0080 +-#define F367TER_FIRST_FLAG 0xf10e0070 +-#define F367TER_TOP_FLAG1 0xf10e0008 +-#define F367TER_TOP_FLAG0 0xf10e0004 +-#define F367TER_CHC_FLAG1 0xf10e0002 +-#define F367TER_CHC_FLAG0 0xf10e0001 +- +-/* TRACKER_FREE1 */ +-#define R367TER_TRACKER_FREE1 0xf10f +-#define F367TER_TRACKER_FREE_1 0xf10f00ff +- +-/* ERROR_CRL1 */ +-#define R367TER_ERROR_CRL1 0xf110 +-#define F367TER_ERROR_CRL_VHI 0xf11000ff +- +-/* ERROR_CRL2 */ +-#define R367TER_ERROR_CRL2 0xf111 +-#define F367TER_ERROR_CRL_HI 0xf11100ff +- +-/* ERROR_CRL3 */ +-#define R367TER_ERROR_CRL3 0xf112 +-#define F367TER_ERROR_CRL_LOI 0xf11200ff +- +-/* ERROR_CRL4 */ +-#define R367TER_ERROR_CRL4 0xf113 +-#define F367TER_ERROR_CRL_VLO 0xf11300ff +- +-/* DEC_NCO1 */ +-#define R367TER_DEC_NCO1 0xf114 +-#define F367TER_DEC_NCO_VHI 0xf11400ff +- +-/* DEC_NCO2 */ +-#define R367TER_DEC_NCO2 0xf115 +-#define F367TER_DEC_NCO_HI 0xf11500ff +- +-/* DEC_NCO3 */ +-#define R367TER_DEC_NCO3 0xf116 +-#define F367TER_DEC_NCO_LO 0xf11600ff +- +-/* SNR */ +-#define R367TER_SNR 0xf117 +-#define F367TER_SNRATIO 0xf11700ff +- +-/* SYR_FFTADJ1 */ +-#define R367TER_SYR_FFTADJ1 0xf118 +-#define F367TER_SYR_FFTADJ_HI 0xf11800ff +- +-/* SYR_FFTADJ2 */ +-#define R367TER_SYR_FFTADJ2 0xf119 +-#define F367TER_SYR_FFTADJ_LO 0xf11900ff +- +-/* SYR_CHCADJ1 */ +-#define R367TER_SYR_CHCADJ1 0xf11a +-#define F367TER_SYR_CHCADJ_HI 0xf11a00ff +- +-/* SYR_CHCADJ2 */ +-#define R367TER_SYR_CHCADJ2 0xf11b +-#define F367TER_SYR_CHCADJ_LO 0xf11b00ff +- +-/* SYR_OFF */ +-#define R367TER_SYR_OFF 0xf11c +-#define F367TER_SYR_OFFSET 0xf11c00ff +- +-/* PPM_OFFSET1 */ +-#define R367TER_PPM_OFFSET1 0xf11d +-#define F367TER_PPM_OFFSET_HI 0xf11d00ff +- +-/* PPM_OFFSET2 */ +-#define R367TER_PPM_OFFSET2 0xf11e +-#define F367TER_PPM_OFFSET_LO 0xf11e00ff +- +-/* TRACKER_FREE2 */ +-#define R367TER_TRACKER_FREE2 0xf11f +-#define F367TER_TRACKER_FREE_2 0xf11f00ff +- +-/* DEBG_LT10 */ +-#define R367TER_DEBG_LT10 0xf120 +-#define F367TER_DEBUG_LT10 0xf12000ff +- +-/* DEBG_LT11 */ +-#define R367TER_DEBG_LT11 0xf121 +-#define F367TER_DEBUG_LT11 0xf12100ff +- +-/* DEBG_LT12 */ +-#define R367TER_DEBG_LT12 0xf122 +-#define F367TER_DEBUG_LT12 0xf12200ff +- +-/* DEBG_LT13 */ +-#define R367TER_DEBG_LT13 0xf123 +-#define F367TER_DEBUG_LT13 0xf12300ff +- +-/* DEBG_LT14 */ +-#define R367TER_DEBG_LT14 0xf124 +-#define F367TER_DEBUG_LT14 0xf12400ff +- +-/* DEBG_LT15 */ +-#define R367TER_DEBG_LT15 0xf125 +-#define F367TER_DEBUG_LT15 0xf12500ff +- +-/* DEBG_LT16 */ +-#define R367TER_DEBG_LT16 0xf126 +-#define F367TER_DEBUG_LT16 0xf12600ff +- +-/* DEBG_LT17 */ +-#define R367TER_DEBG_LT17 0xf127 +-#define F367TER_DEBUG_LT17 0xf12700ff +- +-/* DEBG_LT18 */ +-#define R367TER_DEBG_LT18 0xf128 +-#define F367TER_DEBUG_LT18 0xf12800ff +- +-/* DEBG_LT19 */ +-#define R367TER_DEBG_LT19 0xf129 +-#define F367TER_DEBUG_LT19 0xf12900ff +- +-/* DEBG_LT1a */ +-#define R367TER_DEBG_LT1A 0xf12a +-#define F367TER_DEBUG_LT1A 0xf12a00ff +- +-/* DEBG_LT1b */ +-#define R367TER_DEBG_LT1B 0xf12b +-#define F367TER_DEBUG_LT1B 0xf12b00ff +- +-/* DEBG_LT1c */ +-#define R367TER_DEBG_LT1C 0xf12c +-#define F367TER_DEBUG_LT1C 0xf12c00ff +- +-/* DEBG_LT1D */ +-#define R367TER_DEBG_LT1D 0xf12d +-#define F367TER_DEBUG_LT1D 0xf12d00ff +- +-/* DEBG_LT1E */ +-#define R367TER_DEBG_LT1E 0xf12e +-#define F367TER_DEBUG_LT1E 0xf12e00ff +- +-/* DEBG_LT1F */ +-#define R367TER_DEBG_LT1F 0xf12f +-#define F367TER_DEBUG_LT1F 0xf12f00ff +- +-/* RCCFGH */ +-#define R367TER_RCCFGH 0xf200 +-#define F367TER_TSRCFIFO_DVBCI 0xf2000080 +-#define F367TER_TSRCFIFO_SERIAL 0xf2000040 +-#define F367TER_TSRCFIFO_DISABLE 0xf2000020 +-#define F367TER_TSFIFO_2TORC 0xf2000010 +-#define F367TER_TSRCFIFO_HSGNLOUT 0xf2000008 +-#define F367TER_TSRCFIFO_ERRMODE 0xf2000006 +-#define F367TER_RCCFGH_0 0xf2000001 +- +-/* RCCFGM */ +-#define R367TER_RCCFGM 0xf201 +-#define F367TER_TSRCFIFO_MANSPEED 0xf20100c0 +-#define F367TER_TSRCFIFO_PERMDATA 0xf2010020 +-#define F367TER_TSRCFIFO_NONEWSGNL 0xf2010010 +-#define F367TER_RCBYTE_OVERSAMPLING 0xf201000e +-#define F367TER_TSRCFIFO_INVDATA 0xf2010001 +- +-/* RCCFGL */ +-#define R367TER_RCCFGL 0xf202 +-#define F367TER_TSRCFIFO_BCLKDEL1cK 0xf20200c0 +-#define F367TER_RCCFGL_5 0xf2020020 +-#define F367TER_TSRCFIFO_DUTY50 0xf2020010 +-#define F367TER_TSRCFIFO_NSGNL2dATA 0xf2020008 +-#define F367TER_TSRCFIFO_DISSERMUX 0xf2020004 +-#define F367TER_RCCFGL_1 0xf2020002 +-#define F367TER_TSRCFIFO_STOPCKDIS 0xf2020001 +- +-/* RCINSDELH */ +-#define R367TER_RCINSDELH 0xf203 +-#define F367TER_TSRCDEL_SYNCBYTE 0xf2030080 +-#define F367TER_TSRCDEL_XXHEADER 0xf2030040 +-#define F367TER_TSRCDEL_BBHEADER 0xf2030020 +-#define F367TER_TSRCDEL_DATAFIELD 0xf2030010 +-#define F367TER_TSRCINSDEL_ISCR 0xf2030008 +-#define F367TER_TSRCINSDEL_NPD 0xf2030004 +-#define F367TER_TSRCINSDEL_RSPARITY 0xf2030002 +-#define F367TER_TSRCINSDEL_CRC8 0xf2030001 +- +-/* RCINSDELM */ +-#define R367TER_RCINSDELM 0xf204 +-#define F367TER_TSRCINS_BBPADDING 0xf2040080 +-#define F367TER_TSRCINS_BCHFEC 0xf2040040 +-#define F367TER_TSRCINS_LDPCFEC 0xf2040020 +-#define F367TER_TSRCINS_EMODCOD 0xf2040010 +-#define F367TER_TSRCINS_TOKEN 0xf2040008 +-#define F367TER_TSRCINS_XXXERR 0xf2040004 +-#define F367TER_TSRCINS_MATYPE 0xf2040002 +-#define F367TER_TSRCINS_UPL 0xf2040001 +- +-/* RCINSDELL */ +-#define R367TER_RCINSDELL 0xf205 +-#define F367TER_TSRCINS_DFL 0xf2050080 +-#define F367TER_TSRCINS_SYNCD 0xf2050040 +-#define F367TER_TSRCINS_BLOCLEN 0xf2050020 +-#define F367TER_TSRCINS_SIGPCOUNT 0xf2050010 +-#define F367TER_TSRCINS_FIFO 0xf2050008 +-#define F367TER_TSRCINS_REALPACK 0xf2050004 +-#define F367TER_TSRCINS_TSCONFIG 0xf2050002 +-#define F367TER_TSRCINS_LATENCY 0xf2050001 +- +-/* RCSTATUS */ +-#define R367TER_RCSTATUS 0xf206 +-#define F367TER_TSRCFIFO_LINEOK 0xf2060080 +-#define F367TER_TSRCFIFO_ERROR 0xf2060040 +-#define F367TER_TSRCFIFO_DATA7 0xf2060020 +-#define F367TER_RCSTATUS_4 0xf2060010 +-#define F367TER_TSRCFIFO_DEMODSEL 0xf2060008 +-#define F367TER_TSRC1FIFOSPEED_STORE 0xf2060004 +-#define F367TER_RCSTATUS_1 0xf2060002 +-#define F367TER_TSRCSERIAL_IMPOSSIBLE 0xf2060001 +- +-/* RCSPEED */ +-#define R367TER_RCSPEED 0xf207 +-#define F367TER_TSRCFIFO_OUTSPEED 0xf20700ff +- +-/* RCDEBUGM */ +-#define R367TER_RCDEBUGM 0xf208 +-#define F367TER_SD_UNSYNC 0xf2080080 +-#define F367TER_ULFLOCK_DETECTM 0xf2080040 +-#define F367TER_SUL_SELECTOS 0xf2080020 +-#define F367TER_DILUL_NOSCRBLE 0xf2080010 +-#define F367TER_NUL_SCRB 0xf2080008 +-#define F367TER_UL_SCRB 0xf2080004 +-#define F367TER_SCRAULBAD 0xf2080002 +-#define F367TER_SCRAUL_UNSYNC 0xf2080001 +- +-/* RCDEBUGL */ +-#define R367TER_RCDEBUGL 0xf209 +-#define F367TER_RS_ERR 0xf2090080 +-#define F367TER_LLFLOCK_DETECTM 0xf2090040 +-#define F367TER_NOT_SUL_SELECTOS 0xf2090020 +-#define F367TER_DILLL_NOSCRBLE 0xf2090010 +-#define F367TER_NLL_SCRB 0xf2090008 +-#define F367TER_LL_SCRB 0xf2090004 +-#define F367TER_SCRALLBAD 0xf2090002 +-#define F367TER_SCRALL_UNSYNC 0xf2090001 +- +-/* RCOBSCFG */ +-#define R367TER_RCOBSCFG 0xf20a +-#define F367TER_TSRCFIFO_OBSCFG 0xf20a00ff +- +-/* RCOBSM */ +-#define R367TER_RCOBSM 0xf20b +-#define F367TER_TSRCFIFO_OBSDATA_HI 0xf20b00ff +- +-/* RCOBSL */ +-#define R367TER_RCOBSL 0xf20c +-#define F367TER_TSRCFIFO_OBSDATA_LO 0xf20c00ff +- +-/* RCFECSPY */ +-#define R367TER_RCFECSPY 0xf210 +-#define F367TER_SPYRC_ENABLE 0xf2100080 +-#define F367TER_RCNO_SYNCBYTE 0xf2100040 +-#define F367TER_RCSERIAL_MODE 0xf2100020 +-#define F367TER_RCUNUSUAL_PACKET 0xf2100010 +-#define F367TER_BERRCMETER_DATAMODE 0xf210000c +-#define F367TER_BERRCMETER_LMODE 0xf2100002 +-#define F367TER_BERRCMETER_RESET 0xf2100001 +- +-/* RCFSPYCFG */ +-#define R367TER_RCFSPYCFG 0xf211 +-#define F367TER_FECSPYRC_INPUT 0xf21100c0 +-#define F367TER_RCRST_ON_ERROR 0xf2110020 +-#define F367TER_RCONE_SHOT 0xf2110010 +-#define F367TER_RCI2C_MODE 0xf211000c +-#define F367TER_SPYRC_HSTERESIS 0xf2110003 +- +-/* RCFSPYDATA */ +-#define R367TER_RCFSPYDATA 0xf212 +-#define F367TER_SPYRC_STUFFING 0xf2120080 +-#define F367TER_RCNOERR_PKTJITTER 0xf2120040 +-#define F367TER_SPYRC_CNULLPKT 0xf2120020 +-#define F367TER_SPYRC_OUTDATA_MODE 0xf212001f +- +-/* RCFSPYOUT */ +-#define R367TER_RCFSPYOUT 0xf213 +-#define F367TER_FSPYRC_DIRECT 0xf2130080 +-#define F367TER_RCFSPYOUT_6 0xf2130040 +-#define F367TER_SPYRC_OUTDATA_BUS 0xf2130038 +-#define F367TER_RCSTUFF_MODE 0xf2130007 +- +-/* RCFSTATUS */ +-#define R367TER_RCFSTATUS 0xf214 +-#define F367TER_SPYRC_ENDSIM 0xf2140080 +-#define F367TER_RCVALID_SIM 0xf2140040 +-#define F367TER_RCFOUND_SIGNAL 0xf2140020 +-#define F367TER_RCDSS_SYNCBYTE 0xf2140010 +-#define F367TER_RCRESULT_STATE 0xf214000f +- +-/* RCFGOODPACK */ +-#define R367TER_RCFGOODPACK 0xf215 +-#define F367TER_RCGOOD_PACKET 0xf21500ff +- +-/* RCFPACKCNT */ +-#define R367TER_RCFPACKCNT 0xf216 +-#define F367TER_RCPACKET_COUNTER 0xf21600ff +- +-/* RCFSPYMISC */ +-#define R367TER_RCFSPYMISC 0xf217 +-#define F367TER_RCLABEL_COUNTER 0xf21700ff +- +-/* RCFBERCPT4 */ +-#define R367TER_RCFBERCPT4 0xf218 +-#define F367TER_FBERRCMETER_CPT_MMMMSB 0xf21800ff +- +-/* RCFBERCPT3 */ +-#define R367TER_RCFBERCPT3 0xf219 +-#define F367TER_FBERRCMETER_CPT_MMMSB 0xf21900ff +- +-/* RCFBERCPT2 */ +-#define R367TER_RCFBERCPT2 0xf21a +-#define F367TER_FBERRCMETER_CPT_MMSB 0xf21a00ff +- +-/* RCFBERCPT1 */ +-#define R367TER_RCFBERCPT1 0xf21b +-#define F367TER_FBERRCMETER_CPT_MSB 0xf21b00ff +- +-/* RCFBERCPT0 */ +-#define R367TER_RCFBERCPT0 0xf21c +-#define F367TER_FBERRCMETER_CPT_LSB 0xf21c00ff +- +-/* RCFBERERR2 */ +-#define R367TER_RCFBERERR2 0xf21d +-#define F367TER_FBERRCMETER_ERR_HI 0xf21d00ff +- +-/* RCFBERERR1 */ +-#define R367TER_RCFBERERR1 0xf21e +-#define F367TER_FBERRCMETER_ERR 0xf21e00ff +- +-/* RCFBERERR0 */ +-#define R367TER_RCFBERERR0 0xf21f +-#define F367TER_FBERRCMETER_ERR_LO 0xf21f00ff +- +-/* RCFSTATESM */ +-#define R367TER_RCFSTATESM 0xf220 +-#define F367TER_RCRSTATE_F 0xf2200080 +-#define F367TER_RCRSTATE_E 0xf2200040 +-#define F367TER_RCRSTATE_D 0xf2200020 +-#define F367TER_RCRSTATE_C 0xf2200010 +-#define F367TER_RCRSTATE_B 0xf2200008 +-#define F367TER_RCRSTATE_A 0xf2200004 +-#define F367TER_RCRSTATE_9 0xf2200002 +-#define F367TER_RCRSTATE_8 0xf2200001 +- +-/* RCFSTATESL */ +-#define R367TER_RCFSTATESL 0xf221 +-#define F367TER_RCRSTATE_7 0xf2210080 +-#define F367TER_RCRSTATE_6 0xf2210040 +-#define F367TER_RCRSTATE_5 0xf2210020 +-#define F367TER_RCRSTATE_4 0xf2210010 +-#define F367TER_RCRSTATE_3 0xf2210008 +-#define F367TER_RCRSTATE_2 0xf2210004 +-#define F367TER_RCRSTATE_1 0xf2210002 +-#define F367TER_RCRSTATE_0 0xf2210001 +- +-/* RCFSPYBER */ +-#define R367TER_RCFSPYBER 0xf222 +-#define F367TER_RCFSPYBER_7 0xf2220080 +-#define F367TER_SPYRCOBS_XORREAD 0xf2220040 +-#define F367TER_FSPYRCBER_OBSMODE 0xf2220020 +-#define F367TER_FSPYRCBER_SYNCBYT 0xf2220010 +-#define F367TER_FSPYRCBER_UNSYNC 0xf2220008 +-#define F367TER_FSPYRCBER_CTIME 0xf2220007 +- +-/* RCFSPYDISTM */ +-#define R367TER_RCFSPYDISTM 0xf223 +-#define F367TER_RCPKTTIME_DISTANCE_HI 0xf22300ff +- +-/* RCFSPYDISTL */ +-#define R367TER_RCFSPYDISTL 0xf224 +-#define F367TER_RCPKTTIME_DISTANCE_LO 0xf22400ff +- +-/* RCFSPYOBS7 */ +-#define R367TER_RCFSPYOBS7 0xf228 +-#define F367TER_RCSPYOBS_SPYFAIL 0xf2280080 +-#define F367TER_RCSPYOBS_SPYFAIL1 0xf2280040 +-#define F367TER_RCSPYOBS_ERROR 0xf2280020 +-#define F367TER_RCSPYOBS_STROUT 0xf2280010 +-#define F367TER_RCSPYOBS_RESULTSTATE1 0xf228000f +- +-/* RCFSPYOBS6 */ +-#define R367TER_RCFSPYOBS6 0xf229 +-#define F367TER_RCSPYOBS_RESULTSTATe0 0xf22900f0 +-#define F367TER_RCSPYOBS_RESULTSTATEM1 0xf229000f +- +-/* RCFSPYOBS5 */ +-#define R367TER_RCFSPYOBS5 0xf22a +-#define F367TER_RCSPYOBS_BYTEOFPACKET1 0xf22a00ff +- +-/* RCFSPYOBS4 */ +-#define R367TER_RCFSPYOBS4 0xf22b +-#define F367TER_RCSPYOBS_BYTEVALUE1 0xf22b00ff +- +-/* RCFSPYOBS3 */ +-#define R367TER_RCFSPYOBS3 0xf22c +-#define F367TER_RCSPYOBS_DATA1 0xf22c00ff +- +-/* RCFSPYOBS2 */ +-#define R367TER_RCFSPYOBS2 0xf22d +-#define F367TER_RCSPYOBS_DATa0 0xf22d00ff +- +-/* RCFSPYOBS1 */ +-#define R367TER_RCFSPYOBS1 0xf22e +-#define F367TER_RCSPYOBS_DATAM1 0xf22e00ff +- +-/* RCFSPYOBS0 */ +-#define R367TER_RCFSPYOBS0 0xf22f +-#define F367TER_RCSPYOBS_DATAM2 0xf22f00ff +- +-/* TSGENERAL */ +-#define R367TER_TSGENERAL 0xf230 +-#define F367TER_TSGENERAL_7 0xf2300080 +-#define F367TER_TSGENERAL_6 0xf2300040 +-#define F367TER_TSFIFO_BCLK1aLL 0xf2300020 +-#define F367TER_TSGENERAL_4 0xf2300010 +-#define F367TER_MUXSTREAM_OUTMODE 0xf2300008 +-#define F367TER_TSFIFO_PERMPARAL 0xf2300006 +-#define F367TER_RST_REEDSOLO 0xf2300001 +- +-/* RC1SPEED */ +-#define R367TER_RC1SPEED 0xf231 +-#define F367TER_TSRCFIFO1_OUTSPEED 0xf23100ff +- +-/* TSGSTATUS */ +-#define R367TER_TSGSTATUS 0xf232 +-#define F367TER_TSGSTATUS_7 0xf2320080 +-#define F367TER_TSGSTATUS_6 0xf2320040 +-#define F367TER_RSMEM_FULL 0xf2320020 +-#define F367TER_RS_MULTCALC 0xf2320010 +-#define F367TER_RSIN_OVERTIME 0xf2320008 +-#define F367TER_TSFIFO3_DEMODSEL 0xf2320004 +-#define F367TER_TSFIFO2_DEMODSEL 0xf2320002 +-#define F367TER_TSFIFO1_DEMODSEL 0xf2320001 +- +- +-/* FECM */ +-#define R367TER_FECM 0xf233 +-#define F367TER_DSS_DVB 0xf2330080 +-#define F367TER_DEMOD_BYPASS 0xf2330040 +-#define F367TER_CMP_SLOWMODE 0xf2330020 +-#define F367TER_DSS_SRCH 0xf2330010 +-#define F367TER_FECM_3 0xf2330008 +-#define F367TER_DIFF_MODEVIT 0xf2330004 +-#define F367TER_SYNCVIT 0xf2330002 +-#define F367TER_I2CSYM 0xf2330001 +- +-/* VTH12 */ +-#define R367TER_VTH12 0xf234 +-#define F367TER_VTH_12 0xf23400ff +- +-/* VTH23 */ +-#define R367TER_VTH23 0xf235 +-#define F367TER_VTH_23 0xf23500ff +- +-/* VTH34 */ +-#define R367TER_VTH34 0xf236 +-#define F367TER_VTH_34 0xf23600ff +- +-/* VTH56 */ +-#define R367TER_VTH56 0xf237 +-#define F367TER_VTH_56 0xf23700ff +- +-/* VTH67 */ +-#define R367TER_VTH67 0xf238 +-#define F367TER_VTH_67 0xf23800ff +- +-/* VTH78 */ +-#define R367TER_VTH78 0xf239 +-#define F367TER_VTH_78 0xf23900ff +- +-/* VITCURPUN */ +-#define R367TER_VITCURPUN 0xf23a +-#define F367TER_VIT_MAPPING 0xf23a00e0 +-#define F367TER_VIT_CURPUN 0xf23a001f +- +-/* VERROR */ +-#define R367TER_VERROR 0xf23b +-#define F367TER_REGERR_VIT 0xf23b00ff +- +-/* PRVIT */ +-#define R367TER_PRVIT 0xf23c +-#define F367TER_PRVIT_7 0xf23c0080 +-#define F367TER_DIS_VTHLOCK 0xf23c0040 +-#define F367TER_E7_8VIT 0xf23c0020 +-#define F367TER_E6_7VIT 0xf23c0010 +-#define F367TER_E5_6VIT 0xf23c0008 +-#define F367TER_E3_4VIT 0xf23c0004 +-#define F367TER_E2_3VIT 0xf23c0002 +-#define F367TER_E1_2VIT 0xf23c0001 +- +-/* VAVSRVIT */ +-#define R367TER_VAVSRVIT 0xf23d +-#define F367TER_AMVIT 0xf23d0080 +-#define F367TER_FROZENVIT 0xf23d0040 +-#define F367TER_SNVIT 0xf23d0030 +-#define F367TER_TOVVIT 0xf23d000c +-#define F367TER_HYPVIT 0xf23d0003 +- +-/* VSTATUSVIT */ +-#define R367TER_VSTATUSVIT 0xf23e +-#define F367TER_VITERBI_ON 0xf23e0080 +-#define F367TER_END_LOOPVIT 0xf23e0040 +-#define F367TER_VITERBI_DEPRF 0xf23e0020 +-#define F367TER_PRFVIT 0xf23e0010 +-#define F367TER_LOCKEDVIT 0xf23e0008 +-#define F367TER_VITERBI_DELOCK 0xf23e0004 +-#define F367TER_VIT_DEMODSEL 0xf23e0002 +-#define F367TER_VITERBI_COMPOUT 0xf23e0001 +- +-/* VTHINUSE */ +-#define R367TER_VTHINUSE 0xf23f +-#define F367TER_VIT_INUSE 0xf23f00ff +- +-/* KDIV12 */ +-#define R367TER_KDIV12 0xf240 +-#define F367TER_KDIV12_MANUAL 0xf2400080 +-#define F367TER_K_DIVIDER_12 0xf240007f +- +-/* KDIV23 */ +-#define R367TER_KDIV23 0xf241 +-#define F367TER_KDIV23_MANUAL 0xf2410080 +-#define F367TER_K_DIVIDER_23 0xf241007f +- +-/* KDIV34 */ +-#define R367TER_KDIV34 0xf242 +-#define F367TER_KDIV34_MANUAL 0xf2420080 +-#define F367TER_K_DIVIDER_34 0xf242007f +- +-/* KDIV56 */ +-#define R367TER_KDIV56 0xf243 +-#define F367TER_KDIV56_MANUAL 0xf2430080 +-#define F367TER_K_DIVIDER_56 0xf243007f +- +-/* KDIV67 */ +-#define R367TER_KDIV67 0xf244 +-#define F367TER_KDIV67_MANUAL 0xf2440080 +-#define F367TER_K_DIVIDER_67 0xf244007f +- +-/* KDIV78 */ +-#define R367TER_KDIV78 0xf245 +-#define F367TER_KDIV78_MANUAL 0xf2450080 +-#define F367TER_K_DIVIDER_78 0xf245007f +- +-/* SIGPOWER */ +-#define R367TER_SIGPOWER 0xf246 +-#define F367TER_SIGPOWER_MANUAL 0xf2460080 +-#define F367TER_SIG_POWER 0xf246007f +- +-/* DEMAPVIT */ +-#define R367TER_DEMAPVIT 0xf247 +-#define F367TER_DEMAPVIT_7 0xf2470080 +-#define F367TER_K_DIVIDER_VIT 0xf247007f +- +-/* VITSCALE */ +-#define R367TER_VITSCALE 0xf248 +-#define F367TER_NVTH_NOSRANGE 0xf2480080 +-#define F367TER_VERROR_MAXMODE 0xf2480040 +-#define F367TER_KDIV_MODE 0xf2480030 +-#define F367TER_NSLOWSN_LOCKED 0xf2480008 +-#define F367TER_DELOCK_PRFLOSS 0xf2480004 +-#define F367TER_DIS_RSFLOCK 0xf2480002 +-#define F367TER_VITSCALE_0 0xf2480001 +- +-/* FFEC1PRG */ +-#define R367TER_FFEC1PRG 0xf249 +-#define F367TER_FDSS_DVB 0xf2490080 +-#define F367TER_FDSS_SRCH 0xf2490040 +-#define F367TER_FFECPROG_5 0xf2490020 +-#define F367TER_FFECPROG_4 0xf2490010 +-#define F367TER_FFECPROG_3 0xf2490008 +-#define F367TER_FFECPROG_2 0xf2490004 +-#define F367TER_FTS1_DISABLE 0xf2490002 +-#define F367TER_FTS2_DISABLE 0xf2490001 +- +-/* FVITCURPUN */ +-#define R367TER_FVITCURPUN 0xf24a +-#define F367TER_FVIT_MAPPING 0xf24a00e0 +-#define F367TER_FVIT_CURPUN 0xf24a001f +- +-/* FVERROR */ +-#define R367TER_FVERROR 0xf24b +-#define F367TER_FREGERR_VIT 0xf24b00ff +- +-/* FVSTATUSVIT */ +-#define R367TER_FVSTATUSVIT 0xf24c +-#define F367TER_FVITERBI_ON 0xf24c0080 +-#define F367TER_F1END_LOOPVIT 0xf24c0040 +-#define F367TER_FVITERBI_DEPRF 0xf24c0020 +-#define F367TER_FPRFVIT 0xf24c0010 +-#define F367TER_FLOCKEDVIT 0xf24c0008 +-#define F367TER_FVITERBI_DELOCK 0xf24c0004 +-#define F367TER_FVIT_DEMODSEL 0xf24c0002 +-#define F367TER_FVITERBI_COMPOUT 0xf24c0001 +- +-/* DEBUG_LT1 */ +-#define R367TER_DEBUG_LT1 0xf24d +-#define F367TER_DBG_LT1 0xf24d00ff +- +-/* DEBUG_LT2 */ +-#define R367TER_DEBUG_LT2 0xf24e +-#define F367TER_DBG_LT2 0xf24e00ff +- +-/* DEBUG_LT3 */ +-#define R367TER_DEBUG_LT3 0xf24f +-#define F367TER_DBG_LT3 0xf24f00ff +- +-/* TSTSFMET */ +-#define R367TER_TSTSFMET 0xf250 +-#define F367TER_TSTSFEC_METRIQUES 0xf25000ff +- +-/* SELOUT */ +-#define R367TER_SELOUT 0xf252 +-#define F367TER_EN_SYNC 0xf2520080 +-#define F367TER_EN_TBUSDEMAP 0xf2520040 +-#define F367TER_SELOUT_5 0xf2520020 +-#define F367TER_SELOUT_4 0xf2520010 +-#define F367TER_TSTSYNCHRO_MODE 0xf2520002 +- +-/* TSYNC */ +-#define R367TER_TSYNC 0xf253 +-#define F367TER_CURPUN_INCMODE 0xf2530080 +-#define F367TER_CERR_TSTMODE 0xf2530040 +-#define F367TER_SHIFTSOF_MODE 0xf2530030 +-#define F367TER_SLOWPHA_MODE 0xf2530008 +-#define F367TER_PXX_BYPALL 0xf2530004 +-#define F367TER_FROTA45_FIRST 0xf2530002 +-#define F367TER_TST_BCHERROR 0xf2530001 +- +-/* TSTERR */ +-#define R367TER_TSTERR 0xf254 +-#define F367TER_TST_LONGPKT 0xf2540080 +-#define F367TER_TST_ISSYION 0xf2540040 +-#define F367TER_TST_NPDON 0xf2540020 +-#define F367TER_TSTERR_4 0xf2540010 +-#define F367TER_TRACEBACK_MODE 0xf2540008 +-#define F367TER_TST_RSPARITY 0xf2540004 +-#define F367TER_METRIQUE_MODE 0xf2540003 +- +-/* TSFSYNC */ +-#define R367TER_TSFSYNC 0xf255 +-#define F367TER_EN_SFECSYNC 0xf2550080 +-#define F367TER_EN_SFECDEMAP 0xf2550040 +-#define F367TER_SFCERR_TSTMODE 0xf2550020 +-#define F367TER_SFECPXX_BYPALL 0xf2550010 +-#define F367TER_SFECTSTSYNCHRO_MODE 0xf255000f +- +-/* TSTSFERR */ +-#define R367TER_TSTSFERR 0xf256 +-#define F367TER_TSTSTERR_7 0xf2560080 +-#define F367TER_TSTSTERR_6 0xf2560040 +-#define F367TER_TSTSTERR_5 0xf2560020 +-#define F367TER_TSTSTERR_4 0xf2560010 +-#define F367TER_SFECTRACEBACK_MODE 0xf2560008 +-#define F367TER_SFEC_NCONVPROG 0xf2560004 +-#define F367TER_SFECMETRIQUE_MODE 0xf2560003 +- +-/* TSTTSSF1 */ +-#define R367TER_TSTTSSF1 0xf258 +-#define F367TER_TSTERSSF 0xf2580080 +-#define F367TER_TSTTSSFEN 0xf2580040 +-#define F367TER_SFEC_OUTMODE 0xf2580030 +-#define F367TER_XLSF_NOFTHRESHOLD 0xf2580008 +-#define F367TER_TSTTSSF_STACKSEL 0xf2580007 +- +-/* TSTTSSF2 */ +-#define R367TER_TSTTSSF2 0xf259 +-#define F367TER_DILSF_DBBHEADER 0xf2590080 +-#define F367TER_TSTTSSF_DISBUG 0xf2590040 +-#define F367TER_TSTTSSF_NOBADSTART 0xf2590020 +-#define F367TER_TSTTSSF_SELECT 0xf259001f +- +-/* TSTTSSF3 */ +-#define R367TER_TSTTSSF3 0xf25a +-#define F367TER_TSTTSSF3_7 0xf25a0080 +-#define F367TER_TSTTSSF3_6 0xf25a0040 +-#define F367TER_TSTTSSF3_5 0xf25a0020 +-#define F367TER_TSTTSSF3_4 0xf25a0010 +-#define F367TER_TSTTSSF3_3 0xf25a0008 +-#define F367TER_TSTTSSF3_2 0xf25a0004 +-#define F367TER_TSTTSSF3_1 0xf25a0002 +-#define F367TER_DISSF_CLKENABLE 0xf25a0001 +- +-/* TSTTS1 */ +-#define R367TER_TSTTS1 0xf25c +-#define F367TER_TSTERS 0xf25c0080 +-#define F367TER_TSFIFO_DSSSYNCB 0xf25c0040 +-#define F367TER_TSTTS_FSPYBEFRS 0xf25c0020 +-#define F367TER_NFORCE_SYNCBYTE 0xf25c0010 +-#define F367TER_XL_NOFTHRESHOLD 0xf25c0008 +-#define F367TER_TSTTS_FRFORCEPKT 0xf25c0004 +-#define F367TER_DESCR_NOTAUTO 0xf25c0002 +-#define F367TER_TSTTSEN 0xf25c0001 +- +-/* TSTTS2 */ +-#define R367TER_TSTTS2 0xf25d +-#define F367TER_DIL_DBBHEADER 0xf25d0080 +-#define F367TER_TSTTS_NOBADXXX 0xf25d0040 +-#define F367TER_TSFIFO_DELSPEEDUP 0xf25d0020 +-#define F367TER_TSTTS_SELECT 0xf25d001f +- +-/* TSTTS3 */ +-#define R367TER_TSTTS3 0xf25e +-#define F367TER_TSTTS_NOPKTGAIN 0xf25e0080 +-#define F367TER_TSTTS_NOPKTENE 0xf25e0040 +-#define F367TER_TSTTS_ISOLATION 0xf25e0020 +-#define F367TER_TSTTS_DISBUG 0xf25e0010 +-#define F367TER_TSTTS_NOBADSTART 0xf25e0008 +-#define F367TER_TSTTS_STACKSEL 0xf25e0007 +- +-/* TSTTS4 */ +-#define R367TER_TSTTS4 0xf25f +-#define F367TER_TSTTS4_7 0xf25f0080 +-#define F367TER_TSTTS4_6 0xf25f0040 +-#define F367TER_TSTTS4_5 0xf25f0020 +-#define F367TER_TSTTS_DISDSTATE 0xf25f0010 +-#define F367TER_TSTTS_FASTNOSYNC 0xf25f0008 +-#define F367TER_EXT_FECSPYIN 0xf25f0004 +-#define F367TER_TSTTS_NODPZERO 0xf25f0002 +-#define F367TER_TSTTS_NODIV3 0xf25f0001 +- +-/* TSTTSRC */ +-#define R367TER_TSTTSRC 0xf26c +-#define F367TER_TSTTSRC_7 0xf26c0080 +-#define F367TER_TSRCFIFO_DSSSYNCB 0xf26c0040 +-#define F367TER_TSRCFIFO_DPUNACTIVE 0xf26c0020 +-#define F367TER_TSRCFIFO_DELSPEEDUP 0xf26c0010 +-#define F367TER_TSTTSRC_NODIV3 0xf26c0008 +-#define F367TER_TSTTSRC_FRFORCEPKT 0xf26c0004 +-#define F367TER_SAT25_SDDORIGINE 0xf26c0002 +-#define F367TER_TSTTSRC_INACTIVE 0xf26c0001 +- +-/* TSTTSRS */ +-#define R367TER_TSTTSRS 0xf26d +-#define F367TER_TSTTSRS_7 0xf26d0080 +-#define F367TER_TSTTSRS_6 0xf26d0040 +-#define F367TER_TSTTSRS_5 0xf26d0020 +-#define F367TER_TSTTSRS_4 0xf26d0010 +-#define F367TER_TSTTSRS_3 0xf26d0008 +-#define F367TER_TSTTSRS_2 0xf26d0004 +-#define F367TER_TSTRS_DISRS2 0xf26d0002 +-#define F367TER_TSTRS_DISRS1 0xf26d0001 +- +-/* TSSTATEM */ +-#define R367TER_TSSTATEM 0xf270 +-#define F367TER_TSDIL_ON 0xf2700080 +-#define F367TER_TSSKIPRS_ON 0xf2700040 +-#define F367TER_TSRS_ON 0xf2700020 +-#define F367TER_TSDESCRAMB_ON 0xf2700010 +-#define F367TER_TSFRAME_MODE 0xf2700008 +-#define F367TER_TS_DISABLE 0xf2700004 +-#define F367TER_TSACM_MODE 0xf2700002 +-#define F367TER_TSOUT_NOSYNC 0xf2700001 +- +-/* TSSTATEL */ +-#define R367TER_TSSTATEL 0xf271 +-#define F367TER_TSNOSYNCBYTE 0xf2710080 +-#define F367TER_TSPARITY_ON 0xf2710040 +-#define F367TER_TSSYNCOUTRS_ON 0xf2710020 +-#define F367TER_TSDVBS2_MODE 0xf2710010 +-#define F367TER_TSISSYI_ON 0xf2710008 +-#define F367TER_TSNPD_ON 0xf2710004 +-#define F367TER_TSCRC8_ON 0xf2710002 +-#define F367TER_TSDSS_PACKET 0xf2710001 +- +-/* TSCFGH */ +-#define R367TER_TSCFGH 0xf272 +-#define F367TER_TSFIFO_DVBCI 0xf2720080 +-#define F367TER_TSFIFO_SERIAL 0xf2720040 +-#define F367TER_TSFIFO_TEIUPDATE 0xf2720020 +-#define F367TER_TSFIFO_DUTY50 0xf2720010 +-#define F367TER_TSFIFO_HSGNLOUT 0xf2720008 +-#define F367TER_TSFIFO_ERRMODE 0xf2720006 +-#define F367TER_RST_HWARE 0xf2720001 +- +-/* TSCFGM */ +-#define R367TER_TSCFGM 0xf273 +-#define F367TER_TSFIFO_MANSPEED 0xf27300c0 +-#define F367TER_TSFIFO_PERMDATA 0xf2730020 +-#define F367TER_TSFIFO_NONEWSGNL 0xf2730010 +-#define F367TER_TSFIFO_BITSPEED 0xf2730008 +-#define F367TER_NPD_SPECDVBS2 0xf2730004 +-#define F367TER_TSFIFO_STOPCKDIS 0xf2730002 +-#define F367TER_TSFIFO_INVDATA 0xf2730001 +- +-/* TSCFGL */ +-#define R367TER_TSCFGL 0xf274 +-#define F367TER_TSFIFO_BCLKDEL1cK 0xf27400c0 +-#define F367TER_BCHERROR_MODE 0xf2740030 +-#define F367TER_TSFIFO_NSGNL2dATA 0xf2740008 +-#define F367TER_TSFIFO_EMBINDVB 0xf2740004 +-#define F367TER_TSFIFO_DPUNACT 0xf2740002 +-#define F367TER_TSFIFO_NPDOFF 0xf2740001 +- +-/* TSSYNC */ +-#define R367TER_TSSYNC 0xf275 +-#define F367TER_TSFIFO_PERMUTE 0xf2750080 +-#define F367TER_TSFIFO_FISCR3B 0xf2750060 +-#define F367TER_TSFIFO_SYNCMODE 0xf2750018 +-#define F367TER_TSFIFO_SYNCSEL 0xf2750007 +- +-/* TSINSDELH */ +-#define R367TER_TSINSDELH 0xf276 +-#define F367TER_TSDEL_SYNCBYTE 0xf2760080 +-#define F367TER_TSDEL_XXHEADER 0xf2760040 +-#define F367TER_TSDEL_BBHEADER 0xf2760020 +-#define F367TER_TSDEL_DATAFIELD 0xf2760010 +-#define F367TER_TSINSDEL_ISCR 0xf2760008 +-#define F367TER_TSINSDEL_NPD 0xf2760004 +-#define F367TER_TSINSDEL_RSPARITY 0xf2760002 +-#define F367TER_TSINSDEL_CRC8 0xf2760001 +- +-/* TSINSDELM */ +-#define R367TER_TSINSDELM 0xf277 +-#define F367TER_TSINS_BBPADDING 0xf2770080 +-#define F367TER_TSINS_BCHFEC 0xf2770040 +-#define F367TER_TSINS_LDPCFEC 0xf2770020 +-#define F367TER_TSINS_EMODCOD 0xf2770010 +-#define F367TER_TSINS_TOKEN 0xf2770008 +-#define F367TER_TSINS_XXXERR 0xf2770004 +-#define F367TER_TSINS_MATYPE 0xf2770002 +-#define F367TER_TSINS_UPL 0xf2770001 +- +-/* TSINSDELL */ +-#define R367TER_TSINSDELL 0xf278 +-#define F367TER_TSINS_DFL 0xf2780080 +-#define F367TER_TSINS_SYNCD 0xf2780040 +-#define F367TER_TSINS_BLOCLEN 0xf2780020 +-#define F367TER_TSINS_SIGPCOUNT 0xf2780010 +-#define F367TER_TSINS_FIFO 0xf2780008 +-#define F367TER_TSINS_REALPACK 0xf2780004 +-#define F367TER_TSINS_TSCONFIG 0xf2780002 +-#define F367TER_TSINS_LATENCY 0xf2780001 +- +-/* TSDIVN */ +-#define R367TER_TSDIVN 0xf279 +-#define F367TER_TSFIFO_LOWSPEED 0xf2790080 +-#define F367TER_BYTE_OVERSAMPLING 0xf2790070 +-#define F367TER_TSMANUAL_PACKETNBR 0xf279000f +- +-/* TSDIVPM */ +-#define R367TER_TSDIVPM 0xf27a +-#define F367TER_TSMANUAL_P_HI 0xf27a00ff +- +-/* TSDIVPL */ +-#define R367TER_TSDIVPL 0xf27b +-#define F367TER_TSMANUAL_P_LO 0xf27b00ff +- +-/* TSDIVQM */ +-#define R367TER_TSDIVQM 0xf27c +-#define F367TER_TSMANUAL_Q_HI 0xf27c00ff +- +-/* TSDIVQL */ +-#define R367TER_TSDIVQL 0xf27d +-#define F367TER_TSMANUAL_Q_LO 0xf27d00ff +- +-/* TSDILSTKM */ +-#define R367TER_TSDILSTKM 0xf27e +-#define F367TER_TSFIFO_DILSTK_HI 0xf27e00ff +- +-/* TSDILSTKL */ +-#define R367TER_TSDILSTKL 0xf27f +-#define F367TER_TSFIFO_DILSTK_LO 0xf27f00ff +- +-/* TSSPEED */ +-#define R367TER_TSSPEED 0xf280 +-#define F367TER_TSFIFO_OUTSPEED 0xf28000ff +- +-/* TSSTATUS */ +-#define R367TER_TSSTATUS 0xf281 +-#define F367TER_TSFIFO_LINEOK 0xf2810080 +-#define F367TER_TSFIFO_ERROR 0xf2810040 +-#define F367TER_TSFIFO_DATA7 0xf2810020 +-#define F367TER_TSFIFO_NOSYNC 0xf2810010 +-#define F367TER_ISCR_INITIALIZED 0xf2810008 +-#define F367TER_ISCR_UPDATED 0xf2810004 +-#define F367TER_SOFFIFO_UNREGUL 0xf2810002 +-#define F367TER_DIL_READY 0xf2810001 +- +-/* TSSTATUS2 */ +-#define R367TER_TSSTATUS2 0xf282 +-#define F367TER_TSFIFO_DEMODSEL 0xf2820080 +-#define F367TER_TSFIFOSPEED_STORE 0xf2820040 +-#define F367TER_DILXX_RESET 0xf2820020 +-#define F367TER_TSSERIAL_IMPOSSIBLE 0xf2820010 +-#define F367TER_TSFIFO_UNDERSPEED 0xf2820008 +-#define F367TER_BITSPEED_EVENT 0xf2820004 +-#define F367TER_UL_SCRAMBDETECT 0xf2820002 +-#define F367TER_ULDTV67_FALSELOCK 0xf2820001 +- +-/* TSBITRATEM */ +-#define R367TER_TSBITRATEM 0xf283 +-#define F367TER_TSFIFO_BITRATE_HI 0xf28300ff +- +-/* TSBITRATEL */ +-#define R367TER_TSBITRATEL 0xf284 +-#define F367TER_TSFIFO_BITRATE_LO 0xf28400ff +- +-/* TSPACKLENM */ +-#define R367TER_TSPACKLENM 0xf285 +-#define F367TER_TSFIFO_PACKCPT 0xf28500e0 +-#define F367TER_DIL_RPLEN_HI 0xf285001f +- +-/* TSPACKLENL */ +-#define R367TER_TSPACKLENL 0xf286 +-#define F367TER_DIL_RPLEN_LO 0xf28600ff +- +-/* TSBLOCLENM */ +-#define R367TER_TSBLOCLENM 0xf287 +-#define F367TER_TSFIFO_PFLEN_HI 0xf28700ff +- +-/* TSBLOCLENL */ +-#define R367TER_TSBLOCLENL 0xf288 +-#define F367TER_TSFIFO_PFLEN_LO 0xf28800ff +- +-/* TSDLYH */ +-#define R367TER_TSDLYH 0xf289 +-#define F367TER_SOFFIFO_TSTIMEVALID 0xf2890080 +-#define F367TER_SOFFIFO_SPEEDUP 0xf2890040 +-#define F367TER_SOFFIFO_STOP 0xf2890020 +-#define F367TER_SOFFIFO_REGULATED 0xf2890010 +-#define F367TER_SOFFIFO_REALSBOFF_HI 0xf289000f +- +-/* TSDLYM */ +-#define R367TER_TSDLYM 0xf28a +-#define F367TER_SOFFIFO_REALSBOFF_MED 0xf28a00ff +- +-/* TSDLYL */ +-#define R367TER_TSDLYL 0xf28b +-#define F367TER_SOFFIFO_REALSBOFF_LO 0xf28b00ff +- +-/* TSNPDAV */ +-#define R367TER_TSNPDAV 0xf28c +-#define F367TER_TSNPD_AVERAGE 0xf28c00ff +- +-/* TSBUFSTATH */ +-#define R367TER_TSBUFSTATH 0xf28d +-#define F367TER_TSISCR_3BYTES 0xf28d0080 +-#define F367TER_TSISCR_NEWDATA 0xf28d0040 +-#define F367TER_TSISCR_BUFSTAT_HI 0xf28d003f +- +-/* TSBUFSTATM */ +-#define R367TER_TSBUFSTATM 0xf28e +-#define F367TER_TSISCR_BUFSTAT_MED 0xf28e00ff +- +-/* TSBUFSTATL */ +-#define R367TER_TSBUFSTATL 0xf28f +-#define F367TER_TSISCR_BUFSTAT_LO 0xf28f00ff +- +-/* TSDEBUGM */ +-#define R367TER_TSDEBUGM 0xf290 +-#define F367TER_TSFIFO_ILLPACKET 0xf2900080 +-#define F367TER_DIL_NOSYNC 0xf2900040 +-#define F367TER_DIL_ISCR 0xf2900020 +-#define F367TER_DILOUT_BSYNCB 0xf2900010 +-#define F367TER_TSFIFO_EMPTYPKT 0xf2900008 +-#define F367TER_TSFIFO_EMPTYRD 0xf2900004 +-#define F367TER_SOFFIFO_STOPM 0xf2900002 +-#define F367TER_SOFFIFO_SPEEDUPM 0xf2900001 +- +-/* TSDEBUGL */ +-#define R367TER_TSDEBUGL 0xf291 +-#define F367TER_TSFIFO_PACKLENFAIL 0xf2910080 +-#define F367TER_TSFIFO_SYNCBFAIL 0xf2910040 +-#define F367TER_TSFIFO_VITLIBRE 0xf2910020 +-#define F367TER_TSFIFO_BOOSTSPEEDM 0xf2910010 +-#define F367TER_TSFIFO_UNDERSPEEDM 0xf2910008 +-#define F367TER_TSFIFO_ERROR_EVNT 0xf2910004 +-#define F367TER_TSFIFO_FULL 0xf2910002 +-#define F367TER_TSFIFO_OVERFLOWM 0xf2910001 +- +-/* TSDLYSETH */ +-#define R367TER_TSDLYSETH 0xf292 +-#define F367TER_SOFFIFO_OFFSET 0xf29200e0 +-#define F367TER_SOFFIFO_SYMBOFFSET_HI 0xf292001f +- +-/* TSDLYSETM */ +-#define R367TER_TSDLYSETM 0xf293 +-#define F367TER_SOFFIFO_SYMBOFFSET_MED 0xf29300ff +- +-/* TSDLYSETL */ +-#define R367TER_TSDLYSETL 0xf294 +-#define F367TER_SOFFIFO_SYMBOFFSET_LO 0xf29400ff +- +-/* TSOBSCFG */ +-#define R367TER_TSOBSCFG 0xf295 +-#define F367TER_TSFIFO_OBSCFG 0xf29500ff +- +-/* TSOBSM */ +-#define R367TER_TSOBSM 0xf296 +-#define F367TER_TSFIFO_OBSDATA_HI 0xf29600ff +- +-/* TSOBSL */ +-#define R367TER_TSOBSL 0xf297 +-#define F367TER_TSFIFO_OBSDATA_LO 0xf29700ff +- +-/* ERRCTRL1 */ +-#define R367TER_ERRCTRL1 0xf298 +-#define F367TER_ERR_SRC1 0xf29800f0 +-#define F367TER_ERRCTRL1_3 0xf2980008 +-#define F367TER_NUM_EVT1 0xf2980007 +- +-/* ERRCNT1H */ +-#define R367TER_ERRCNT1H 0xf299 +-#define F367TER_ERRCNT1_OLDVALUE 0xf2990080 +-#define F367TER_ERR_CNT1 0xf299007f +- +-/* ERRCNT1M */ +-#define R367TER_ERRCNT1M 0xf29a +-#define F367TER_ERR_CNT1_HI 0xf29a00ff +- +-/* ERRCNT1L */ +-#define R367TER_ERRCNT1L 0xf29b +-#define F367TER_ERR_CNT1_LO 0xf29b00ff +- +-/* ERRCTRL2 */ +-#define R367TER_ERRCTRL2 0xf29c +-#define F367TER_ERR_SRC2 0xf29c00f0 +-#define F367TER_ERRCTRL2_3 0xf29c0008 +-#define F367TER_NUM_EVT2 0xf29c0007 +- +-/* ERRCNT2H */ +-#define R367TER_ERRCNT2H 0xf29d +-#define F367TER_ERRCNT2_OLDVALUE 0xf29d0080 +-#define F367TER_ERR_CNT2_HI 0xf29d007f +- +-/* ERRCNT2M */ +-#define R367TER_ERRCNT2M 0xf29e +-#define F367TER_ERR_CNT2_MED 0xf29e00ff +- +-/* ERRCNT2L */ +-#define R367TER_ERRCNT2L 0xf29f +-#define F367TER_ERR_CNT2_LO 0xf29f00ff +- +-/* FECSPY */ +-#define R367TER_FECSPY 0xf2a0 +-#define F367TER_SPY_ENABLE 0xf2a00080 +-#define F367TER_NO_SYNCBYTE 0xf2a00040 +-#define F367TER_SERIAL_MODE 0xf2a00020 +-#define F367TER_UNUSUAL_PACKET 0xf2a00010 +-#define F367TER_BERMETER_DATAMODE 0xf2a0000c +-#define F367TER_BERMETER_LMODE 0xf2a00002 +-#define F367TER_BERMETER_RESET 0xf2a00001 +- +-/* FSPYCFG */ +-#define R367TER_FSPYCFG 0xf2a1 +-#define F367TER_FECSPY_INPUT 0xf2a100c0 +-#define F367TER_RST_ON_ERROR 0xf2a10020 +-#define F367TER_ONE_SHOT 0xf2a10010 +-#define F367TER_I2C_MOD 0xf2a1000c +-#define F367TER_SPY_HYSTERESIS 0xf2a10003 +- +-/* FSPYDATA */ +-#define R367TER_FSPYDATA 0xf2a2 +-#define F367TER_SPY_STUFFING 0xf2a20080 +-#define F367TER_NOERROR_PKTJITTER 0xf2a20040 +-#define F367TER_SPY_CNULLPKT 0xf2a20020 +-#define F367TER_SPY_OUTDATA_MODE 0xf2a2001f +- +-/* FSPYOUT */ +-#define R367TER_FSPYOUT 0xf2a3 +-#define F367TER_FSPY_DIRECT 0xf2a30080 +-#define F367TER_FSPYOUT_6 0xf2a30040 +-#define F367TER_SPY_OUTDATA_BUS 0xf2a30038 +-#define F367TER_STUFF_MODE 0xf2a30007 +- +-/* FSTATUS */ +-#define R367TER_FSTATUS 0xf2a4 +-#define F367TER_SPY_ENDSIM 0xf2a40080 +-#define F367TER_VALID_SIM 0xf2a40040 +-#define F367TER_FOUND_SIGNAL 0xf2a40020 +-#define F367TER_DSS_SYNCBYTE 0xf2a40010 +-#define F367TER_RESULT_STATE 0xf2a4000f +- +-/* FGOODPACK */ +-#define R367TER_FGOODPACK 0xf2a5 +-#define F367TER_FGOOD_PACKET 0xf2a500ff +- +-/* FPACKCNT */ +-#define R367TER_FPACKCNT 0xf2a6 +-#define F367TER_FPACKET_COUNTER 0xf2a600ff +- +-/* FSPYMISC */ +-#define R367TER_FSPYMISC 0xf2a7 +-#define F367TER_FLABEL_COUNTER 0xf2a700ff +- +-/* FBERCPT4 */ +-#define R367TER_FBERCPT4 0xf2a8 +-#define F367TER_FBERMETER_CPT5 0xf2a800ff +- +-/* FBERCPT3 */ +-#define R367TER_FBERCPT3 0xf2a9 +-#define F367TER_FBERMETER_CPT4 0xf2a900ff +- +-/* FBERCPT2 */ +-#define R367TER_FBERCPT2 0xf2aa +-#define F367TER_FBERMETER_CPT3 0xf2aa00ff +- +-/* FBERCPT1 */ +-#define R367TER_FBERCPT1 0xf2ab +-#define F367TER_FBERMETER_CPT2 0xf2ab00ff +- +-/* FBERCPT0 */ +-#define R367TER_FBERCPT0 0xf2ac +-#define F367TER_FBERMETER_CPT1 0xf2ac00ff +- +-/* FBERERR2 */ +-#define R367TER_FBERERR2 0xf2ad +-#define F367TER_FBERMETER_ERR_HI 0xf2ad00ff +- +-/* FBERERR1 */ +-#define R367TER_FBERERR1 0xf2ae +-#define F367TER_FBERMETER_ERR_MED 0xf2ae00ff +- +-/* FBERERR0 */ +-#define R367TER_FBERERR0 0xf2af +-#define F367TER_FBERMETER_ERR_LO 0xf2af00ff +- +-/* FSTATESM */ +-#define R367TER_FSTATESM 0xf2b0 +-#define F367TER_RSTATE_F 0xf2b00080 +-#define F367TER_RSTATE_E 0xf2b00040 +-#define F367TER_RSTATE_D 0xf2b00020 +-#define F367TER_RSTATE_C 0xf2b00010 +-#define F367TER_RSTATE_B 0xf2b00008 +-#define F367TER_RSTATE_A 0xf2b00004 +-#define F367TER_RSTATE_9 0xf2b00002 +-#define F367TER_RSTATE_8 0xf2b00001 +- +-/* FSTATESL */ +-#define R367TER_FSTATESL 0xf2b1 +-#define F367TER_RSTATE_7 0xf2b10080 +-#define F367TER_RSTATE_6 0xf2b10040 +-#define F367TER_RSTATE_5 0xf2b10020 +-#define F367TER_RSTATE_4 0xf2b10010 +-#define F367TER_RSTATE_3 0xf2b10008 +-#define F367TER_RSTATE_2 0xf2b10004 +-#define F367TER_RSTATE_1 0xf2b10002 +-#define F367TER_RSTATE_0 0xf2b10001 +- +-/* FSPYBER */ +-#define R367TER_FSPYBER 0xf2b2 +-#define F367TER_FSPYBER_7 0xf2b20080 +-#define F367TER_FSPYOBS_XORREAD 0xf2b20040 +-#define F367TER_FSPYBER_OBSMODE 0xf2b20020 +-#define F367TER_FSPYBER_SYNCBYTE 0xf2b20010 +-#define F367TER_FSPYBER_UNSYNC 0xf2b20008 +-#define F367TER_FSPYBER_CTIME 0xf2b20007 +- +-/* FSPYDISTM */ +-#define R367TER_FSPYDISTM 0xf2b3 +-#define F367TER_PKTTIME_DISTANCE_HI 0xf2b300ff +- +-/* FSPYDISTL */ +-#define R367TER_FSPYDISTL 0xf2b4 +-#define F367TER_PKTTIME_DISTANCE_LO 0xf2b400ff +- +-/* FSPYOBS7 */ +-#define R367TER_FSPYOBS7 0xf2b8 +-#define F367TER_FSPYOBS_SPYFAIL 0xf2b80080 +-#define F367TER_FSPYOBS_SPYFAIL1 0xf2b80040 +-#define F367TER_FSPYOBS_ERROR 0xf2b80020 +-#define F367TER_FSPYOBS_STROUT 0xf2b80010 +-#define F367TER_FSPYOBS_RESULTSTATE1 0xf2b8000f +- +-/* FSPYOBS6 */ +-#define R367TER_FSPYOBS6 0xf2b9 +-#define F367TER_FSPYOBS_RESULTSTATe0 0xf2b900f0 +-#define F367TER_FSPYOBS_RESULTSTATEM1 0xf2b9000f +- +-/* FSPYOBS5 */ +-#define R367TER_FSPYOBS5 0xf2ba +-#define F367TER_FSPYOBS_BYTEOFPACKET1 0xf2ba00ff +- +-/* FSPYOBS4 */ +-#define R367TER_FSPYOBS4 0xf2bb +-#define F367TER_FSPYOBS_BYTEVALUE1 0xf2bb00ff +- +-/* FSPYOBS3 */ +-#define R367TER_FSPYOBS3 0xf2bc +-#define F367TER_FSPYOBS_DATA1 0xf2bc00ff +- +-/* FSPYOBS2 */ +-#define R367TER_FSPYOBS2 0xf2bd +-#define F367TER_FSPYOBS_DATa0 0xf2bd00ff +- +-/* FSPYOBS1 */ +-#define R367TER_FSPYOBS1 0xf2be +-#define F367TER_FSPYOBS_DATAM1 0xf2be00ff +- +-/* FSPYOBS0 */ +-#define R367TER_FSPYOBS0 0xf2bf +-#define F367TER_FSPYOBS_DATAM2 0xf2bf00ff +- +-/* SFDEMAP */ +-#define R367TER_SFDEMAP 0xf2c0 +-#define F367TER_SFDEMAP_7 0xf2c00080 +-#define F367TER_SFEC_K_DIVIDER_VIT 0xf2c0007f +- +-/* SFERROR */ +-#define R367TER_SFERROR 0xf2c1 +-#define F367TER_SFEC_REGERR_VIT 0xf2c100ff +- +-/* SFAVSR */ +-#define R367TER_SFAVSR 0xf2c2 +-#define F367TER_SFEC_SUMERRORS 0xf2c20080 +-#define F367TER_SERROR_MAXMODE 0xf2c20040 +-#define F367TER_SN_SFEC 0xf2c20030 +-#define F367TER_KDIV_MODE_SFEC 0xf2c2000c +-#define F367TER_SFAVSR_1 0xf2c20002 +-#define F367TER_SFAVSR_0 0xf2c20001 +- +-/* SFECSTATUS */ +-#define R367TER_SFECSTATUS 0xf2c3 +-#define F367TER_SFEC_ON 0xf2c30080 +-#define F367TER_SFSTATUS_6 0xf2c30040 +-#define F367TER_SFSTATUS_5 0xf2c30020 +-#define F367TER_SFSTATUS_4 0xf2c30010 +-#define F367TER_LOCKEDSFEC 0xf2c30008 +-#define F367TER_SFEC_DELOCK 0xf2c30004 +-#define F367TER_SFEC_DEMODSEL1 0xf2c30002 +-#define F367TER_SFEC_OVFON 0xf2c30001 +- +-/* SFKDIV12 */ +-#define R367TER_SFKDIV12 0xf2c4 +-#define F367TER_SFECKDIV12_MAN 0xf2c40080 +-#define F367TER_SFEC_K_DIVIDER_12 0xf2c4007f +- +-/* SFKDIV23 */ +-#define R367TER_SFKDIV23 0xf2c5 +-#define F367TER_SFECKDIV23_MAN 0xf2c50080 +-#define F367TER_SFEC_K_DIVIDER_23 0xf2c5007f +- +-/* SFKDIV34 */ +-#define R367TER_SFKDIV34 0xf2c6 +-#define F367TER_SFECKDIV34_MAN 0xf2c60080 +-#define F367TER_SFEC_K_DIVIDER_34 0xf2c6007f +- +-/* SFKDIV56 */ +-#define R367TER_SFKDIV56 0xf2c7 +-#define F367TER_SFECKDIV56_MAN 0xf2c70080 +-#define F367TER_SFEC_K_DIVIDER_56 0xf2c7007f +- +-/* SFKDIV67 */ +-#define R367TER_SFKDIV67 0xf2c8 +-#define F367TER_SFECKDIV67_MAN 0xf2c80080 +-#define F367TER_SFEC_K_DIVIDER_67 0xf2c8007f +- +-/* SFKDIV78 */ +-#define R367TER_SFKDIV78 0xf2c9 +-#define F367TER_SFECKDIV78_MAN 0xf2c90080 +-#define F367TER_SFEC_K_DIVIDER_78 0xf2c9007f +- +-/* SFDILSTKM */ +-#define R367TER_SFDILSTKM 0xf2ca +-#define F367TER_SFEC_PACKCPT 0xf2ca00e0 +-#define F367TER_SFEC_DILSTK_HI 0xf2ca001f +- +-/* SFDILSTKL */ +-#define R367TER_SFDILSTKL 0xf2cb +-#define F367TER_SFEC_DILSTK_LO 0xf2cb00ff +- +-/* SFSTATUS */ +-#define R367TER_SFSTATUS 0xf2cc +-#define F367TER_SFEC_LINEOK 0xf2cc0080 +-#define F367TER_SFEC_ERROR 0xf2cc0040 +-#define F367TER_SFEC_DATA7 0xf2cc0020 +-#define F367TER_SFEC_OVERFLOW 0xf2cc0010 +-#define F367TER_SFEC_DEMODSEL2 0xf2cc0008 +-#define F367TER_SFEC_NOSYNC 0xf2cc0004 +-#define F367TER_SFEC_UNREGULA 0xf2cc0002 +-#define F367TER_SFEC_READY 0xf2cc0001 +- +-/* SFDLYH */ +-#define R367TER_SFDLYH 0xf2cd +-#define F367TER_SFEC_TSTIMEVALID 0xf2cd0080 +-#define F367TER_SFEC_SPEEDUP 0xf2cd0040 +-#define F367TER_SFEC_STOP 0xf2cd0020 +-#define F367TER_SFEC_REGULATED 0xf2cd0010 +-#define F367TER_SFEC_REALSYMBOFFSET 0xf2cd000f +- +-/* SFDLYM */ +-#define R367TER_SFDLYM 0xf2ce +-#define F367TER_SFEC_REALSYMBOFFSET_HI 0xf2ce00ff +- +-/* SFDLYL */ +-#define R367TER_SFDLYL 0xf2cf +-#define F367TER_SFEC_REALSYMBOFFSET_LO 0xf2cf00ff +- +-/* SFDLYSETH */ +-#define R367TER_SFDLYSETH 0xf2d0 +-#define F367TER_SFEC_OFFSET 0xf2d000e0 +-#define F367TER_SFECDLYSETH_4 0xf2d00010 +-#define F367TER_RST_SFEC 0xf2d00008 +-#define F367TER_SFECDLYSETH_2 0xf2d00004 +-#define F367TER_SFEC_DISABLE 0xf2d00002 +-#define F367TER_SFEC_UNREGUL 0xf2d00001 +- +-/* SFDLYSETM */ +-#define R367TER_SFDLYSETM 0xf2d1 +-#define F367TER_SFECDLYSETM_7 0xf2d10080 +-#define F367TER_SFEC_SYMBOFFSET_HI 0xf2d1007f +- +-/* SFDLYSETL */ +-#define R367TER_SFDLYSETL 0xf2d2 +-#define F367TER_SFEC_SYMBOFFSET_LO 0xf2d200ff +- +-/* SFOBSCFG */ +-#define R367TER_SFOBSCFG 0xf2d3 +-#define F367TER_SFEC_OBSCFG 0xf2d300ff +- +-/* SFOBSM */ +-#define R367TER_SFOBSM 0xf2d4 +-#define F367TER_SFEC_OBSDATA_HI 0xf2d400ff +- +-/* SFOBSL */ +-#define R367TER_SFOBSL 0xf2d5 +-#define F367TER_SFEC_OBSDATA_LO 0xf2d500ff +- +-/* SFECINFO */ +-#define R367TER_SFECINFO 0xf2d6 +-#define F367TER_SFECINFO_7 0xf2d60080 +-#define F367TER_SFEC_SYNCDLSB 0xf2d60070 +-#define F367TER_SFCE_S1cPHASE 0xf2d6000f +- +-/* SFERRCTRL */ +-#define R367TER_SFERRCTRL 0xf2d8 +-#define F367TER_SFEC_ERR_SOURCE 0xf2d800f0 +-#define F367TER_SFERRCTRL_3 0xf2d80008 +-#define F367TER_SFEC_NUM_EVENT 0xf2d80007 +- +-/* SFERRCNTH */ +-#define R367TER_SFERRCNTH 0xf2d9 +-#define F367TER_SFERRC_OLDVALUE 0xf2d90080 +-#define F367TER_SFEC_ERR_CNT 0xf2d9007f +- +-/* SFERRCNTM */ +-#define R367TER_SFERRCNTM 0xf2da +-#define F367TER_SFEC_ERR_CNT_HI 0xf2da00ff +- +-/* SFERRCNTL */ +-#define R367TER_SFERRCNTL 0xf2db +-#define F367TER_SFEC_ERR_CNT_LO 0xf2db00ff +- +-/* SYMBRATEM */ +-#define R367TER_SYMBRATEM 0xf2e0 +-#define F367TER_DEFGEN_SYMBRATE_HI 0xf2e000ff +- +-/* SYMBRATEL */ +-#define R367TER_SYMBRATEL 0xf2e1 +-#define F367TER_DEFGEN_SYMBRATE_LO 0xf2e100ff +- +-/* SYMBSTATUS */ +-#define R367TER_SYMBSTATUS 0xf2e2 +-#define F367TER_SYMBDLINE2_OFF 0xf2e20080 +-#define F367TER_SDDL_REINIT1 0xf2e20040 +-#define F367TER_SDD_REINIT1 0xf2e20020 +-#define F367TER_TOKENID_ERROR 0xf2e20010 +-#define F367TER_SYMBRATE_OVERFLOW 0xf2e20008 +-#define F367TER_SYMBRATE_UNDERFLOW 0xf2e20004 +-#define F367TER_TOKENID_RSTEVENT 0xf2e20002 +-#define F367TER_TOKENID_RESET1 0xf2e20001 +- +-/* SYMBCFG */ +-#define R367TER_SYMBCFG 0xf2e3 +-#define F367TER_SYMBCFG_7 0xf2e30080 +-#define F367TER_SYMBCFG_6 0xf2e30040 +-#define F367TER_SYMBCFG_5 0xf2e30020 +-#define F367TER_SYMBCFG_4 0xf2e30010 +-#define F367TER_SYMRATE_FSPEED 0xf2e3000c +-#define F367TER_SYMRATE_SSPEED 0xf2e30003 +- +-/* SYMBFIFOM */ +-#define R367TER_SYMBFIFOM 0xf2e4 +-#define F367TER_SYMBFIFOM_7 0xf2e40080 +-#define F367TER_SYMBFIFOM_6 0xf2e40040 +-#define F367TER_DEFGEN_SYMFIFO_HI 0xf2e4003f +- +-/* SYMBFIFOL */ +-#define R367TER_SYMBFIFOL 0xf2e5 +-#define F367TER_DEFGEN_SYMFIFO_LO 0xf2e500ff +- +-/* SYMBOFFSM */ +-#define R367TER_SYMBOFFSM 0xf2e6 +-#define F367TER_TOKENID_RESET2 0xf2e60080 +-#define F367TER_SDDL_REINIT2 0xf2e60040 +-#define F367TER_SDD_REINIT2 0xf2e60020 +-#define F367TER_SYMBOFFSM_4 0xf2e60010 +-#define F367TER_SYMBOFFSM_3 0xf2e60008 +-#define F367TER_DEFGEN_SYMBOFFSET_HI 0xf2e60007 +- +-/* SYMBOFFSL */ +-#define R367TER_SYMBOFFSL 0xf2e7 +-#define F367TER_DEFGEN_SYMBOFFSET_LO 0xf2e700ff +- +-/* DEBUG_LT4 */ +-#define R367TER_DEBUG_LT4 0xf400 +-#define F367TER_F_DEBUG_LT4 0xf40000ff +- +-/* DEBUG_LT5 */ +-#define R367TER_DEBUG_LT5 0xf401 +-#define F367TER_F_DEBUG_LT5 0xf40100ff +- +-/* DEBUG_LT6 */ +-#define R367TER_DEBUG_LT6 0xf402 +-#define F367TER_F_DEBUG_LT6 0xf40200ff +- +-/* DEBUG_LT7 */ +-#define R367TER_DEBUG_LT7 0xf403 +-#define F367TER_F_DEBUG_LT7 0xf40300ff +- +-/* DEBUG_LT8 */ +-#define R367TER_DEBUG_LT8 0xf404 +-#define F367TER_F_DEBUG_LT8 0xf40400ff +- +-/* DEBUG_LT9 */ +-#define R367TER_DEBUG_LT9 0xf405 +-#define F367TER_F_DEBUG_LT9 0xf40500ff +- +-#define STV0367TER_NBREGS 445 +- +-/* ID */ +-#define R367CAB_ID 0xf000 +-#define F367CAB_IDENTIFICATIONREGISTER 0xf00000ff +- +-/* I2CRPT */ +-#define R367CAB_I2CRPT 0xf001 +-#define F367CAB_I2CT_ON 0xf0010080 +-#define F367CAB_ENARPT_LEVEL 0xf0010070 +-#define F367CAB_SCLT_DELAY 0xf0010008 +-#define F367CAB_SCLT_NOD 0xf0010004 +-#define F367CAB_STOP_ENABLE 0xf0010002 +-#define F367CAB_SDAT_NOD 0xf0010001 +- +-/* TOPCTRL */ +-#define R367CAB_TOPCTRL 0xf002 +-#define F367CAB_STDBY 0xf0020080 +-#define F367CAB_STDBY_CORE 0xf0020020 +-#define F367CAB_QAM_COFDM 0xf0020010 +-#define F367CAB_TS_DIS 0xf0020008 +-#define F367CAB_DIR_CLK_216 0xf0020004 +- +-/* IOCFG0 */ +-#define R367CAB_IOCFG0 0xf003 +-#define F367CAB_OP0_SD 0xf0030080 +-#define F367CAB_OP0_VAL 0xf0030040 +-#define F367CAB_OP0_OD 0xf0030020 +-#define F367CAB_OP0_INV 0xf0030010 +-#define F367CAB_OP0_DACVALUE_HI 0xf003000f +- +-/* DAc0R */ +-#define R367CAB_DAC0R 0xf004 +-#define F367CAB_OP0_DACVALUE_LO 0xf00400ff +- +-/* IOCFG1 */ +-#define R367CAB_IOCFG1 0xf005 +-#define F367CAB_IP0 0xf0050040 +-#define F367CAB_OP1_OD 0xf0050020 +-#define F367CAB_OP1_INV 0xf0050010 +-#define F367CAB_OP1_DACVALUE_HI 0xf005000f +- +-/* DAC1R */ +-#define R367CAB_DAC1R 0xf006 +-#define F367CAB_OP1_DACVALUE_LO 0xf00600ff +- +-/* IOCFG2 */ +-#define R367CAB_IOCFG2 0xf007 +-#define F367CAB_OP2_LOCK_CONF 0xf00700e0 +-#define F367CAB_OP2_OD 0xf0070010 +-#define F367CAB_OP2_VAL 0xf0070008 +-#define F367CAB_OP1_LOCK_CONF 0xf0070007 +- +-/* SDFR */ +-#define R367CAB_SDFR 0xf008 +-#define F367CAB_OP0_FREQ 0xf00800f0 +-#define F367CAB_OP1_FREQ 0xf008000f +- +-/* AUX_CLK */ +-#define R367CAB_AUX_CLK 0xf00a +-#define F367CAB_AUXFEC_CTL 0xf00a00c0 +-#define F367CAB_DIS_CKX4 0xf00a0020 +-#define F367CAB_CKSEL 0xf00a0018 +-#define F367CAB_CKDIV_PROG 0xf00a0006 +-#define F367CAB_AUXCLK_ENA 0xf00a0001 +- +-/* FREESYS1 */ +-#define R367CAB_FREESYS1 0xf00b +-#define F367CAB_FREESYS_1 0xf00b00ff +- +-/* FREESYS2 */ +-#define R367CAB_FREESYS2 0xf00c +-#define F367CAB_FREESYS_2 0xf00c00ff +- +-/* FREESYS3 */ +-#define R367CAB_FREESYS3 0xf00d +-#define F367CAB_FREESYS_3 0xf00d00ff +- +-/* GPIO_CFG */ +-#define R367CAB_GPIO_CFG 0xf00e +-#define F367CAB_GPIO7_OD 0xf00e0080 +-#define F367CAB_GPIO7_CFG 0xf00e0040 +-#define F367CAB_GPIO6_OD 0xf00e0020 +-#define F367CAB_GPIO6_CFG 0xf00e0010 +-#define F367CAB_GPIO5_OD 0xf00e0008 +-#define F367CAB_GPIO5_CFG 0xf00e0004 +-#define F367CAB_GPIO4_OD 0xf00e0002 +-#define F367CAB_GPIO4_CFG 0xf00e0001 +- +-/* GPIO_CMD */ +-#define R367CAB_GPIO_CMD 0xf00f +-#define F367CAB_GPIO7_VAL 0xf00f0008 +-#define F367CAB_GPIO6_VAL 0xf00f0004 +-#define F367CAB_GPIO5_VAL 0xf00f0002 +-#define F367CAB_GPIO4_VAL 0xf00f0001 +- +-/* TSTRES */ +-#define R367CAB_TSTRES 0xf0c0 +-#define F367CAB_FRES_DISPLAY 0xf0c00080 +-#define F367CAB_FRES_FIFO_AD 0xf0c00020 +-#define F367CAB_FRESRS 0xf0c00010 +-#define F367CAB_FRESACS 0xf0c00008 +-#define F367CAB_FRESFEC 0xf0c00004 +-#define F367CAB_FRES_PRIF 0xf0c00002 +-#define F367CAB_FRESCORE 0xf0c00001 +- +-/* ANACTRL */ +-#define R367CAB_ANACTRL 0xf0c1 +-#define F367CAB_BYPASS_XTAL 0xf0c10040 +-#define F367CAB_BYPASS_PLLXN 0xf0c1000c +-#define F367CAB_DIS_PAD_OSC 0xf0c10002 +-#define F367CAB_STDBY_PLLXN 0xf0c10001 +- +-/* TSTBUS */ +-#define R367CAB_TSTBUS 0xf0c2 +-#define F367CAB_TS_BYTE_CLK_INV 0xf0c20080 +-#define F367CAB_CFG_IP 0xf0c20070 +-#define F367CAB_CFG_TST 0xf0c2000f +- +-/* RF_AGC1 */ +-#define R367CAB_RF_AGC1 0xf0d4 +-#define F367CAB_RF_AGC1_LEVEL_HI 0xf0d400ff +- +-/* RF_AGC2 */ +-#define R367CAB_RF_AGC2 0xf0d5 +-#define F367CAB_REF_ADGP 0xf0d50080 +-#define F367CAB_STDBY_ADCGP 0xf0d50020 +-#define F367CAB_RF_AGC1_LEVEL_LO 0xf0d50003 +- +-/* ANADIGCTRL */ +-#define R367CAB_ANADIGCTRL 0xf0d7 +-#define F367CAB_SEL_CLKDEM 0xf0d70020 +-#define F367CAB_EN_BUFFER_Q 0xf0d70010 +-#define F367CAB_EN_BUFFER_I 0xf0d70008 +-#define F367CAB_ADC_RIS_EGDE 0xf0d70004 +-#define F367CAB_SGN_ADC 0xf0d70002 +-#define F367CAB_SEL_AD12_SYNC 0xf0d70001 +- +-/* PLLMDIV */ +-#define R367CAB_PLLMDIV 0xf0d8 +-#define F367CAB_PLL_MDIV 0xf0d800ff +- +-/* PLLNDIV */ +-#define R367CAB_PLLNDIV 0xf0d9 +-#define F367CAB_PLL_NDIV 0xf0d900ff +- +-/* PLLSETUP */ +-#define R367CAB_PLLSETUP 0xf0da +-#define F367CAB_PLL_PDIV 0xf0da0070 +-#define F367CAB_PLL_KDIV 0xf0da000f +- +-/* DUAL_AD12 */ +-#define R367CAB_DUAL_AD12 0xf0db +-#define F367CAB_FS20M 0xf0db0020 +-#define F367CAB_FS50M 0xf0db0010 +-#define F367CAB_INMODe0 0xf0db0008 +-#define F367CAB_POFFQ 0xf0db0004 +-#define F367CAB_POFFI 0xf0db0002 +-#define F367CAB_INMODE1 0xf0db0001 +- +-/* TSTBIST */ +-#define R367CAB_TSTBIST 0xf0dc +-#define F367CAB_TST_BYP_CLK 0xf0dc0080 +-#define F367CAB_TST_GCLKENA_STD 0xf0dc0040 +-#define F367CAB_TST_GCLKENA 0xf0dc0020 +-#define F367CAB_TST_MEMBIST 0xf0dc001f +- +-/* CTRL_1 */ +-#define R367CAB_CTRL_1 0xf402 +-#define F367CAB_SOFT_RST 0xf4020080 +-#define F367CAB_EQU_RST 0xf4020008 +-#define F367CAB_CRL_RST 0xf4020004 +-#define F367CAB_TRL_RST 0xf4020002 +-#define F367CAB_AGC_RST 0xf4020001 +- +-/* CTRL_2 */ +-#define R367CAB_CTRL_2 0xf403 +-#define F367CAB_DEINT_RST 0xf4030008 +-#define F367CAB_RS_RST 0xf4030004 +- +-/* IT_STATUS1 */ +-#define R367CAB_IT_STATUS1 0xf408 +-#define F367CAB_SWEEP_OUT 0xf4080080 +-#define F367CAB_FSM_CRL 0xf4080040 +-#define F367CAB_CRL_LOCK 0xf4080020 +-#define F367CAB_MFSM 0xf4080010 +-#define F367CAB_TRL_LOCK 0xf4080008 +-#define F367CAB_TRL_AGC_LIMIT 0xf4080004 +-#define F367CAB_ADJ_AGC_LOCK 0xf4080002 +-#define F367CAB_AGC_QAM_LOCK 0xf4080001 +- +-/* IT_STATUS2 */ +-#define R367CAB_IT_STATUS2 0xf409 +-#define F367CAB_TSMF_CNT 0xf4090080 +-#define F367CAB_TSMF_EOF 0xf4090040 +-#define F367CAB_TSMF_RDY 0xf4090020 +-#define F367CAB_FEC_NOCORR 0xf4090010 +-#define F367CAB_SYNCSTATE 0xf4090008 +-#define F367CAB_DEINT_LOCK 0xf4090004 +-#define F367CAB_FADDING_FRZ 0xf4090002 +-#define F367CAB_TAPMON_ALARM 0xf4090001 +- +-/* IT_EN1 */ +-#define R367CAB_IT_EN1 0xf40a +-#define F367CAB_SWEEP_OUTE 0xf40a0080 +-#define F367CAB_FSM_CRLE 0xf40a0040 +-#define F367CAB_CRL_LOCKE 0xf40a0020 +-#define F367CAB_MFSME 0xf40a0010 +-#define F367CAB_TRL_LOCKE 0xf40a0008 +-#define F367CAB_TRL_AGC_LIMITE 0xf40a0004 +-#define F367CAB_ADJ_AGC_LOCKE 0xf40a0002 +-#define F367CAB_AGC_LOCKE 0xf40a0001 +- +-/* IT_EN2 */ +-#define R367CAB_IT_EN2 0xf40b +-#define F367CAB_TSMF_CNTE 0xf40b0080 +-#define F367CAB_TSMF_EOFE 0xf40b0040 +-#define F367CAB_TSMF_RDYE 0xf40b0020 +-#define F367CAB_FEC_NOCORRE 0xf40b0010 +-#define F367CAB_SYNCSTATEE 0xf40b0008 +-#define F367CAB_DEINT_LOCKE 0xf40b0004 +-#define F367CAB_FADDING_FRZE 0xf40b0002 +-#define F367CAB_TAPMON_ALARME 0xf40b0001 +- +-/* CTRL_STATUS */ +-#define R367CAB_CTRL_STATUS 0xf40c +-#define F367CAB_QAMFEC_LOCK 0xf40c0004 +-#define F367CAB_TSMF_LOCK 0xf40c0002 +-#define F367CAB_TSMF_ERROR 0xf40c0001 +- +-/* TEST_CTL */ +-#define R367CAB_TEST_CTL 0xf40f +-#define F367CAB_TST_BLK_SEL 0xf40f0060 +-#define F367CAB_TST_BUS_SEL 0xf40f001f +- +-/* AGC_CTL */ +-#define R367CAB_AGC_CTL 0xf410 +-#define F367CAB_AGC_LCK_TH 0xf41000f0 +-#define F367CAB_AGC_ACCUMRSTSEL 0xf4100007 +- +-/* AGC_IF_CFG */ +-#define R367CAB_AGC_IF_CFG 0xf411 +-#define F367CAB_AGC_IF_BWSEL 0xf41100f0 +-#define F367CAB_AGC_IF_FREEZE 0xf4110002 +- +-/* AGC_RF_CFG */ +-#define R367CAB_AGC_RF_CFG 0xf412 +-#define F367CAB_AGC_RF_BWSEL 0xf4120070 +-#define F367CAB_AGC_RF_FREEZE 0xf4120002 +- +-/* AGC_PWM_CFG */ +-#define R367CAB_AGC_PWM_CFG 0xf413 +-#define F367CAB_AGC_RF_PWM_TST 0xf4130080 +-#define F367CAB_AGC_RF_PWM_INV 0xf4130040 +-#define F367CAB_AGC_IF_PWM_TST 0xf4130008 +-#define F367CAB_AGC_IF_PWM_INV 0xf4130004 +-#define F367CAB_AGC_PWM_CLKDIV 0xf4130003 +- +-/* AGC_PWR_REF_L */ +-#define R367CAB_AGC_PWR_REF_L 0xf414 +-#define F367CAB_AGC_PWRREF_LO 0xf41400ff +- +-/* AGC_PWR_REF_H */ +-#define R367CAB_AGC_PWR_REF_H 0xf415 +-#define F367CAB_AGC_PWRREF_HI 0xf4150003 +- +-/* AGC_RF_TH_L */ +-#define R367CAB_AGC_RF_TH_L 0xf416 +-#define F367CAB_AGC_RF_TH_LO 0xf41600ff +- +-/* AGC_RF_TH_H */ +-#define R367CAB_AGC_RF_TH_H 0xf417 +-#define F367CAB_AGC_RF_TH_HI 0xf417000f +- +-/* AGC_IF_LTH_L */ +-#define R367CAB_AGC_IF_LTH_L 0xf418 +-#define F367CAB_AGC_IF_THLO_LO 0xf41800ff +- +-/* AGC_IF_LTH_H */ +-#define R367CAB_AGC_IF_LTH_H 0xf419 +-#define F367CAB_AGC_IF_THLO_HI 0xf419000f +- +-/* AGC_IF_HTH_L */ +-#define R367CAB_AGC_IF_HTH_L 0xf41a +-#define F367CAB_AGC_IF_THHI_LO 0xf41a00ff +- +-/* AGC_IF_HTH_H */ +-#define R367CAB_AGC_IF_HTH_H 0xf41b +-#define F367CAB_AGC_IF_THHI_HI 0xf41b000f +- +-/* AGC_PWR_RD_L */ +-#define R367CAB_AGC_PWR_RD_L 0xf41c +-#define F367CAB_AGC_PWR_WORD_LO 0xf41c00ff +- +-/* AGC_PWR_RD_M */ +-#define R367CAB_AGC_PWR_RD_M 0xf41d +-#define F367CAB_AGC_PWR_WORD_ME 0xf41d00ff +- +-/* AGC_PWR_RD_H */ +-#define R367CAB_AGC_PWR_RD_H 0xf41e +-#define F367CAB_AGC_PWR_WORD_HI 0xf41e0003 +- +-/* AGC_PWM_IFCMD_L */ +-#define R367CAB_AGC_PWM_IFCMD_L 0xf420 +-#define F367CAB_AGC_IF_PWMCMD_LO 0xf42000ff +- +-/* AGC_PWM_IFCMD_H */ +-#define R367CAB_AGC_PWM_IFCMD_H 0xf421 +-#define F367CAB_AGC_IF_PWMCMD_HI 0xf421000f +- +-/* AGC_PWM_RFCMD_L */ +-#define R367CAB_AGC_PWM_RFCMD_L 0xf422 +-#define F367CAB_AGC_RF_PWMCMD_LO 0xf42200ff +- +-/* AGC_PWM_RFCMD_H */ +-#define R367CAB_AGC_PWM_RFCMD_H 0xf423 +-#define F367CAB_AGC_RF_PWMCMD_HI 0xf423000f +- +-/* IQDEM_CFG */ +-#define R367CAB_IQDEM_CFG 0xf424 +-#define F367CAB_IQDEM_CLK_SEL 0xf4240004 +-#define F367CAB_IQDEM_INVIQ 0xf4240002 +-#define F367CAB_IQDEM_A2dTYPE 0xf4240001 +- +-/* MIX_NCO_LL */ +-#define R367CAB_MIX_NCO_LL 0xf425 +-#define F367CAB_MIX_NCO_INC_LL 0xf42500ff +- +-/* MIX_NCO_HL */ +-#define R367CAB_MIX_NCO_HL 0xf426 +-#define F367CAB_MIX_NCO_INC_HL 0xf42600ff +- +-/* MIX_NCO_HH */ +-#define R367CAB_MIX_NCO_HH 0xf427 +-#define F367CAB_MIX_NCO_INVCNST 0xf4270080 +-#define F367CAB_MIX_NCO_INC_HH 0xf427007f +- +-/* SRC_NCO_LL */ +-#define R367CAB_SRC_NCO_LL 0xf428 +-#define F367CAB_SRC_NCO_INC_LL 0xf42800ff +- +-/* SRC_NCO_LH */ +-#define R367CAB_SRC_NCO_LH 0xf429 +-#define F367CAB_SRC_NCO_INC_LH 0xf42900ff +- +-/* SRC_NCO_HL */ +-#define R367CAB_SRC_NCO_HL 0xf42a +-#define F367CAB_SRC_NCO_INC_HL 0xf42a00ff +- +-/* SRC_NCO_HH */ +-#define R367CAB_SRC_NCO_HH 0xf42b +-#define F367CAB_SRC_NCO_INC_HH 0xf42b007f +- +-/* IQDEM_GAIN_SRC_L */ +-#define R367CAB_IQDEM_GAIN_SRC_L 0xf42c +-#define F367CAB_GAIN_SRC_LO 0xf42c00ff +- +-/* IQDEM_GAIN_SRC_H */ +-#define R367CAB_IQDEM_GAIN_SRC_H 0xf42d +-#define F367CAB_GAIN_SRC_HI 0xf42d0003 +- +-/* IQDEM_DCRM_CFG_LL */ +-#define R367CAB_IQDEM_DCRM_CFG_LL 0xf430 +-#define F367CAB_DCRM0_DCIN_L 0xf43000ff +- +-/* IQDEM_DCRM_CFG_LH */ +-#define R367CAB_IQDEM_DCRM_CFG_LH 0xf431 +-#define F367CAB_DCRM1_I_DCIN_L 0xf43100fc +-#define F367CAB_DCRM0_DCIN_H 0xf4310003 +- +-/* IQDEM_DCRM_CFG_HL */ +-#define R367CAB_IQDEM_DCRM_CFG_HL 0xf432 +-#define F367CAB_DCRM1_Q_DCIN_L 0xf43200f0 +-#define F367CAB_DCRM1_I_DCIN_H 0xf432000f +- +-/* IQDEM_DCRM_CFG_HH */ +-#define R367CAB_IQDEM_DCRM_CFG_HH 0xf433 +-#define F367CAB_DCRM1_FRZ 0xf4330080 +-#define F367CAB_DCRM0_FRZ 0xf4330040 +-#define F367CAB_DCRM1_Q_DCIN_H 0xf433003f +- +-/* IQDEM_ADJ_COEFf0 */ +-#define R367CAB_IQDEM_ADJ_COEFF0 0xf434 +-#define F367CAB_ADJIIR_COEFF10_L 0xf43400ff +- +-/* IQDEM_ADJ_COEFF1 */ +-#define R367CAB_IQDEM_ADJ_COEFF1 0xf435 +-#define F367CAB_ADJIIR_COEFF11_L 0xf43500fc +-#define F367CAB_ADJIIR_COEFF10_H 0xf4350003 +- +-/* IQDEM_ADJ_COEFF2 */ +-#define R367CAB_IQDEM_ADJ_COEFF2 0xf436 +-#define F367CAB_ADJIIR_COEFF12_L 0xf43600f0 +-#define F367CAB_ADJIIR_COEFF11_H 0xf436000f +- +-/* IQDEM_ADJ_COEFF3 */ +-#define R367CAB_IQDEM_ADJ_COEFF3 0xf437 +-#define F367CAB_ADJIIR_COEFF20_L 0xf43700c0 +-#define F367CAB_ADJIIR_COEFF12_H 0xf437003f +- +-/* IQDEM_ADJ_COEFF4 */ +-#define R367CAB_IQDEM_ADJ_COEFF4 0xf438 +-#define F367CAB_ADJIIR_COEFF20_H 0xf43800ff +- +-/* IQDEM_ADJ_COEFF5 */ +-#define R367CAB_IQDEM_ADJ_COEFF5 0xf439 +-#define F367CAB_ADJIIR_COEFF21_L 0xf43900ff +- +-/* IQDEM_ADJ_COEFF6 */ +-#define R367CAB_IQDEM_ADJ_COEFF6 0xf43a +-#define F367CAB_ADJIIR_COEFF22_L 0xf43a00fc +-#define F367CAB_ADJIIR_COEFF21_H 0xf43a0003 +- +-/* IQDEM_ADJ_COEFF7 */ +-#define R367CAB_IQDEM_ADJ_COEFF7 0xf43b +-#define F367CAB_ADJIIR_COEFF22_H 0xf43b000f +- +-/* IQDEM_ADJ_EN */ +-#define R367CAB_IQDEM_ADJ_EN 0xf43c +-#define F367CAB_ALLPASSFILT_EN 0xf43c0008 +-#define F367CAB_ADJ_AGC_EN 0xf43c0004 +-#define F367CAB_ADJ_COEFF_FRZ 0xf43c0002 +-#define F367CAB_ADJ_EN 0xf43c0001 +- +-/* IQDEM_ADJ_AGC_REF */ +-#define R367CAB_IQDEM_ADJ_AGC_REF 0xf43d +-#define F367CAB_ADJ_AGC_REF 0xf43d00ff +- +-/* ALLPASSFILT1 */ +-#define R367CAB_ALLPASSFILT1 0xf440 +-#define F367CAB_ALLPASSFILT_COEFF1_LO 0xf44000ff +- +-/* ALLPASSFILT2 */ +-#define R367CAB_ALLPASSFILT2 0xf441 +-#define F367CAB_ALLPASSFILT_COEFF1_ME 0xf44100ff +- +-/* ALLPASSFILT3 */ +-#define R367CAB_ALLPASSFILT3 0xf442 +-#define F367CAB_ALLPASSFILT_COEFF2_LO 0xf44200c0 +-#define F367CAB_ALLPASSFILT_COEFF1_HI 0xf442003f +- +-/* ALLPASSFILT4 */ +-#define R367CAB_ALLPASSFILT4 0xf443 +-#define F367CAB_ALLPASSFILT_COEFF2_MEL 0xf44300ff +- +-/* ALLPASSFILT5 */ +-#define R367CAB_ALLPASSFILT5 0xf444 +-#define F367CAB_ALLPASSFILT_COEFF2_MEH 0xf44400ff +- +-/* ALLPASSFILT6 */ +-#define R367CAB_ALLPASSFILT6 0xf445 +-#define F367CAB_ALLPASSFILT_COEFF3_LO 0xf44500f0 +-#define F367CAB_ALLPASSFILT_COEFF2_HI 0xf445000f +- +-/* ALLPASSFILT7 */ +-#define R367CAB_ALLPASSFILT7 0xf446 +-#define F367CAB_ALLPASSFILT_COEFF3_MEL 0xf44600ff +- +-/* ALLPASSFILT8 */ +-#define R367CAB_ALLPASSFILT8 0xf447 +-#define F367CAB_ALLPASSFILT_COEFF3_MEH 0xf44700ff +- +-/* ALLPASSFILT9 */ +-#define R367CAB_ALLPASSFILT9 0xf448 +-#define F367CAB_ALLPASSFILT_COEFF4_LO 0xf44800fc +-#define F367CAB_ALLPASSFILT_COEFF3_HI 0xf4480003 +- +-/* ALLPASSFILT10 */ +-#define R367CAB_ALLPASSFILT10 0xf449 +-#define F367CAB_ALLPASSFILT_COEFF4_ME 0xf44900ff +- +-/* ALLPASSFILT11 */ +-#define R367CAB_ALLPASSFILT11 0xf44a +-#define F367CAB_ALLPASSFILT_COEFF4_HI 0xf44a00ff +- +-/* TRL_AGC_CFG */ +-#define R367CAB_TRL_AGC_CFG 0xf450 +-#define F367CAB_TRL_AGC_FREEZE 0xf4500080 +-#define F367CAB_TRL_AGC_REF 0xf450007f +- +-/* TRL_LPF_CFG */ +-#define R367CAB_TRL_LPF_CFG 0xf454 +-#define F367CAB_NYQPOINT_INV 0xf4540040 +-#define F367CAB_TRL_SHIFT 0xf4540030 +-#define F367CAB_NYQ_COEFF_SEL 0xf454000c +-#define F367CAB_TRL_LPF_FREEZE 0xf4540002 +-#define F367CAB_TRL_LPF_CRT 0xf4540001 +- +-/* TRL_LPF_ACQ_GAIN */ +-#define R367CAB_TRL_LPF_ACQ_GAIN 0xf455 +-#define F367CAB_TRL_GDIR_ACQ 0xf4550070 +-#define F367CAB_TRL_GINT_ACQ 0xf4550007 +- +-/* TRL_LPF_TRK_GAIN */ +-#define R367CAB_TRL_LPF_TRK_GAIN 0xf456 +-#define F367CAB_TRL_GDIR_TRK 0xf4560070 +-#define F367CAB_TRL_GINT_TRK 0xf4560007 +- +-/* TRL_LPF_OUT_GAIN */ +-#define R367CAB_TRL_LPF_OUT_GAIN 0xf457 +-#define F367CAB_TRL_GAIN_OUT 0xf4570007 +- +-/* TRL_LOCKDET_LTH */ +-#define R367CAB_TRL_LOCKDET_LTH 0xf458 +-#define F367CAB_TRL_LCK_THLO 0xf4580007 +- +-/* TRL_LOCKDET_HTH */ +-#define R367CAB_TRL_LOCKDET_HTH 0xf459 +-#define F367CAB_TRL_LCK_THHI 0xf45900ff +- +-/* TRL_LOCKDET_TRGVAL */ +-#define R367CAB_TRL_LOCKDET_TRGVAL 0xf45a +-#define F367CAB_TRL_LCK_TRG 0xf45a00ff +- +-/* IQ_QAM */ +-#define R367CAB_IQ_QAM 0xf45c +-#define F367CAB_IQ_INPUT 0xf45c0008 +-#define F367CAB_DETECT_MODE 0xf45c0007 +- +-/* FSM_STATE */ +-#define R367CAB_FSM_STATE 0xf460 +-#define F367CAB_CRL_DFE 0xf4600080 +-#define F367CAB_DFE_START 0xf4600040 +-#define F367CAB_CTRLG_START 0xf4600030 +-#define F367CAB_FSM_FORCESTATE 0xf460000f +- +-/* FSM_CTL */ +-#define R367CAB_FSM_CTL 0xf461 +-#define F367CAB_FEC2_EN 0xf4610040 +-#define F367CAB_SIT_EN 0xf4610020 +-#define F367CAB_TRL_AHEAD 0xf4610010 +-#define F367CAB_TRL2_EN 0xf4610008 +-#define F367CAB_FSM_EQA1_EN 0xf4610004 +-#define F367CAB_FSM_BKP_DIS 0xf4610002 +-#define F367CAB_FSM_FORCE_EN 0xf4610001 +- +-/* FSM_STS */ +-#define R367CAB_FSM_STS 0xf462 +-#define F367CAB_FSM_STATUS 0xf462000f +- +-/* FSM_SNR0_HTH */ +-#define R367CAB_FSM_SNR0_HTH 0xf463 +-#define F367CAB_SNR0_HTH 0xf46300ff +- +-/* FSM_SNR1_HTH */ +-#define R367CAB_FSM_SNR1_HTH 0xf464 +-#define F367CAB_SNR1_HTH 0xf46400ff +- +-/* FSM_SNR2_HTH */ +-#define R367CAB_FSM_SNR2_HTH 0xf465 +-#define F367CAB_SNR2_HTH 0xf46500ff +- +-/* FSM_SNR0_LTH */ +-#define R367CAB_FSM_SNR0_LTH 0xf466 +-#define F367CAB_SNR0_LTH 0xf46600ff +- +-/* FSM_SNR1_LTH */ +-#define R367CAB_FSM_SNR1_LTH 0xf467 +-#define F367CAB_SNR1_LTH 0xf46700ff +- +-/* FSM_EQA1_HTH */ +-#define R367CAB_FSM_EQA1_HTH 0xf468 +-#define F367CAB_SNR3_HTH_LO 0xf46800f0 +-#define F367CAB_EQA1_HTH 0xf468000f +- +-/* FSM_TEMPO */ +-#define R367CAB_FSM_TEMPO 0xf469 +-#define F367CAB_SIT 0xf46900c0 +-#define F367CAB_WST 0xf4690038 +-#define F367CAB_ELT 0xf4690006 +-#define F367CAB_SNR3_HTH_HI 0xf4690001 +- +-/* FSM_CONFIG */ +-#define R367CAB_FSM_CONFIG 0xf46a +-#define F367CAB_FEC2_DFEOFF 0xf46a0004 +-#define F367CAB_PRIT_STATE 0xf46a0002 +-#define F367CAB_MODMAP_STATE 0xf46a0001 +- +-/* EQU_I_TESTTAP_L */ +-#define R367CAB_EQU_I_TESTTAP_L 0xf474 +-#define F367CAB_I_TEST_TAP_L 0xf47400ff +- +-/* EQU_I_TESTTAP_M */ +-#define R367CAB_EQU_I_TESTTAP_M 0xf475 +-#define F367CAB_I_TEST_TAP_M 0xf47500ff +- +-/* EQU_I_TESTTAP_H */ +-#define R367CAB_EQU_I_TESTTAP_H 0xf476 +-#define F367CAB_I_TEST_TAP_H 0xf476001f +- +-/* EQU_TESTAP_CFG */ +-#define R367CAB_EQU_TESTAP_CFG 0xf477 +-#define F367CAB_TEST_FFE_DFE_SEL 0xf4770040 +-#define F367CAB_TEST_TAP_SELECT 0xf477003f +- +-/* EQU_Q_TESTTAP_L */ +-#define R367CAB_EQU_Q_TESTTAP_L 0xf478 +-#define F367CAB_Q_TEST_TAP_L 0xf47800ff +- +-/* EQU_Q_TESTTAP_M */ +-#define R367CAB_EQU_Q_TESTTAP_M 0xf479 +-#define F367CAB_Q_TEST_TAP_M 0xf47900ff +- +-/* EQU_Q_TESTTAP_H */ +-#define R367CAB_EQU_Q_TESTTAP_H 0xf47a +-#define F367CAB_Q_TEST_TAP_H 0xf47a001f +- +-/* EQU_TAP_CTRL */ +-#define R367CAB_EQU_TAP_CTRL 0xf47b +-#define F367CAB_MTAP_FRZ 0xf47b0010 +-#define F367CAB_PRE_FREEZE 0xf47b0008 +-#define F367CAB_DFE_TAPMON_EN 0xf47b0004 +-#define F367CAB_FFE_TAPMON_EN 0xf47b0002 +-#define F367CAB_MTAP_ONLY 0xf47b0001 +- +-/* EQU_CTR_CRL_CONTROL_L */ +-#define R367CAB_EQU_CTR_CRL_CONTROL_L 0xf47c +-#define F367CAB_EQU_CTR_CRL_CONTROL_LO 0xf47c00ff +- +-/* EQU_CTR_CRL_CONTROL_H */ +-#define R367CAB_EQU_CTR_CRL_CONTROL_H 0xf47d +-#define F367CAB_EQU_CTR_CRL_CONTROL_HI 0xf47d00ff +- +-/* EQU_CTR_HIPOW_L */ +-#define R367CAB_EQU_CTR_HIPOW_L 0xf47e +-#define F367CAB_CTR_HIPOW_L 0xf47e00ff +- +-/* EQU_CTR_HIPOW_H */ +-#define R367CAB_EQU_CTR_HIPOW_H 0xf47f +-#define F367CAB_CTR_HIPOW_H 0xf47f00ff +- +-/* EQU_I_EQU_LO */ +-#define R367CAB_EQU_I_EQU_LO 0xf480 +-#define F367CAB_EQU_I_EQU_L 0xf48000ff +- +-/* EQU_I_EQU_HI */ +-#define R367CAB_EQU_I_EQU_HI 0xf481 +-#define F367CAB_EQU_I_EQU_H 0xf4810003 +- +-/* EQU_Q_EQU_LO */ +-#define R367CAB_EQU_Q_EQU_LO 0xf482 +-#define F367CAB_EQU_Q_EQU_L 0xf48200ff +- +-/* EQU_Q_EQU_HI */ +-#define R367CAB_EQU_Q_EQU_HI 0xf483 +-#define F367CAB_EQU_Q_EQU_H 0xf4830003 +- +-/* EQU_MAPPER */ +-#define R367CAB_EQU_MAPPER 0xf484 +-#define F367CAB_QUAD_AUTO 0xf4840080 +-#define F367CAB_QUAD_INV 0xf4840040 +-#define F367CAB_QAM_MODE 0xf4840007 +- +-/* EQU_SWEEP_RATE */ +-#define R367CAB_EQU_SWEEP_RATE 0xf485 +-#define F367CAB_SNR_PER 0xf48500c0 +-#define F367CAB_SWEEP_RATE 0xf485003f +- +-/* EQU_SNR_LO */ +-#define R367CAB_EQU_SNR_LO 0xf486 +-#define F367CAB_SNR_LO 0xf48600ff +- +-/* EQU_SNR_HI */ +-#define R367CAB_EQU_SNR_HI 0xf487 +-#define F367CAB_SNR_HI 0xf48700ff +- +-/* EQU_GAMMA_LO */ +-#define R367CAB_EQU_GAMMA_LO 0xf488 +-#define F367CAB_GAMMA_LO 0xf48800ff +- +-/* EQU_GAMMA_HI */ +-#define R367CAB_EQU_GAMMA_HI 0xf489 +-#define F367CAB_GAMMA_ME 0xf48900ff +- +-/* EQU_ERR_GAIN */ +-#define R367CAB_EQU_ERR_GAIN 0xf48a +-#define F367CAB_EQA1MU 0xf48a0070 +-#define F367CAB_CRL2MU 0xf48a000e +-#define F367CAB_GAMMA_HI 0xf48a0001 +- +-/* EQU_RADIUS */ +-#define R367CAB_EQU_RADIUS 0xf48b +-#define F367CAB_RADIUS 0xf48b00ff +- +-/* EQU_FFE_MAINTAP */ +-#define R367CAB_EQU_FFE_MAINTAP 0xf48c +-#define F367CAB_FFE_MAINTAP_INIT 0xf48c00ff +- +-/* EQU_FFE_LEAKAGE */ +-#define R367CAB_EQU_FFE_LEAKAGE 0xf48e +-#define F367CAB_LEAK_PER 0xf48e00f0 +-#define F367CAB_EQU_OUTSEL 0xf48e0002 +-#define F367CAB_PNT2dFE 0xf48e0001 +- +-/* EQU_FFE_MAINTAP_POS */ +-#define R367CAB_EQU_FFE_MAINTAP_POS 0xf48f +-#define F367CAB_FFE_LEAK_EN 0xf48f0080 +-#define F367CAB_DFE_LEAK_EN 0xf48f0040 +-#define F367CAB_FFE_MAINTAP_POS 0xf48f003f +- +-/* EQU_GAIN_WIDE */ +-#define R367CAB_EQU_GAIN_WIDE 0xf490 +-#define F367CAB_DFE_GAIN_WIDE 0xf49000f0 +-#define F367CAB_FFE_GAIN_WIDE 0xf490000f +- +-/* EQU_GAIN_NARROW */ +-#define R367CAB_EQU_GAIN_NARROW 0xf491 +-#define F367CAB_DFE_GAIN_NARROW 0xf49100f0 +-#define F367CAB_FFE_GAIN_NARROW 0xf491000f +- +-/* EQU_CTR_LPF_GAIN */ +-#define R367CAB_EQU_CTR_LPF_GAIN 0xf492 +-#define F367CAB_CTR_GTO 0xf4920080 +-#define F367CAB_CTR_GDIR 0xf4920070 +-#define F367CAB_SWEEP_EN 0xf4920008 +-#define F367CAB_CTR_GINT 0xf4920007 +- +-/* EQU_CRL_LPF_GAIN */ +-#define R367CAB_EQU_CRL_LPF_GAIN 0xf493 +-#define F367CAB_CRL_GTO 0xf4930080 +-#define F367CAB_CRL_GDIR 0xf4930070 +-#define F367CAB_SWEEP_DIR 0xf4930008 +-#define F367CAB_CRL_GINT 0xf4930007 +- +-/* EQU_GLOBAL_GAIN */ +-#define R367CAB_EQU_GLOBAL_GAIN 0xf494 +-#define F367CAB_CRL_GAIN 0xf49400f8 +-#define F367CAB_CTR_INC_GAIN 0xf4940004 +-#define F367CAB_CTR_FRAC 0xf4940003 +- +-/* EQU_CRL_LD_SEN */ +-#define R367CAB_EQU_CRL_LD_SEN 0xf495 +-#define F367CAB_CTR_BADPOINT_EN 0xf4950080 +-#define F367CAB_CTR_GAIN 0xf4950070 +-#define F367CAB_LIMANEN 0xf4950008 +-#define F367CAB_CRL_LD_SEN 0xf4950007 +- +-/* EQU_CRL_LD_VAL */ +-#define R367CAB_EQU_CRL_LD_VAL 0xf496 +-#define F367CAB_CRL_BISTH_LIMIT 0xf4960080 +-#define F367CAB_CARE_EN 0xf4960040 +-#define F367CAB_CRL_LD_PER 0xf4960030 +-#define F367CAB_CRL_LD_WST 0xf496000c +-#define F367CAB_CRL_LD_TFS 0xf4960003 +- +-/* EQU_CRL_TFR */ +-#define R367CAB_EQU_CRL_TFR 0xf497 +-#define F367CAB_CRL_LD_TFR 0xf49700ff +- +-/* EQU_CRL_BISTH_LO */ +-#define R367CAB_EQU_CRL_BISTH_LO 0xf498 +-#define F367CAB_CRL_BISTH_LO 0xf49800ff +- +-/* EQU_CRL_BISTH_HI */ +-#define R367CAB_EQU_CRL_BISTH_HI 0xf499 +-#define F367CAB_CRL_BISTH_HI 0xf49900ff +- +-/* EQU_SWEEP_RANGE_LO */ +-#define R367CAB_EQU_SWEEP_RANGE_LO 0xf49a +-#define F367CAB_SWEEP_RANGE_LO 0xf49a00ff +- +-/* EQU_SWEEP_RANGE_HI */ +-#define R367CAB_EQU_SWEEP_RANGE_HI 0xf49b +-#define F367CAB_SWEEP_RANGE_HI 0xf49b00ff +- +-/* EQU_CRL_LIMITER */ +-#define R367CAB_EQU_CRL_LIMITER 0xf49c +-#define F367CAB_BISECTOR_EN 0xf49c0080 +-#define F367CAB_PHEST128_EN 0xf49c0040 +-#define F367CAB_CRL_LIM 0xf49c003f +- +-/* EQU_MODULUS_MAP */ +-#define R367CAB_EQU_MODULUS_MAP 0xf49d +-#define F367CAB_PNT_DEPTH 0xf49d00e0 +-#define F367CAB_MODULUS_CMP 0xf49d001f +- +-/* EQU_PNT_GAIN */ +-#define R367CAB_EQU_PNT_GAIN 0xf49e +-#define F367CAB_PNT_EN 0xf49e0080 +-#define F367CAB_MODULUSMAP_EN 0xf49e0040 +-#define F367CAB_PNT_GAIN 0xf49e003f +- +-/* FEC_AC_CTR_0 */ +-#define R367CAB_FEC_AC_CTR_0 0xf4a8 +-#define F367CAB_BE_BYPASS 0xf4a80020 +-#define F367CAB_REFRESH47 0xf4a80010 +-#define F367CAB_CT_NBST 0xf4a80008 +-#define F367CAB_TEI_ENA 0xf4a80004 +-#define F367CAB_DS_ENA 0xf4a80002 +-#define F367CAB_TSMF_EN 0xf4a80001 +- +-/* FEC_AC_CTR_1 */ +-#define R367CAB_FEC_AC_CTR_1 0xf4a9 +-#define F367CAB_DEINT_DEPTH 0xf4a900ff +- +-/* FEC_AC_CTR_2 */ +-#define R367CAB_FEC_AC_CTR_2 0xf4aa +-#define F367CAB_DEINT_M 0xf4aa00f8 +-#define F367CAB_DIS_UNLOCK 0xf4aa0004 +-#define F367CAB_DESCR_MODE 0xf4aa0003 +- +-/* FEC_AC_CTR_3 */ +-#define R367CAB_FEC_AC_CTR_3 0xf4ab +-#define F367CAB_DI_UNLOCK 0xf4ab0080 +-#define F367CAB_DI_FREEZE 0xf4ab0040 +-#define F367CAB_MISMATCH 0xf4ab0030 +-#define F367CAB_ACQ_MODE 0xf4ab000c +-#define F367CAB_TRK_MODE 0xf4ab0003 +- +-/* FEC_STATUS */ +-#define R367CAB_FEC_STATUS 0xf4ac +-#define F367CAB_DEINT_SMCNTR 0xf4ac00e0 +-#define F367CAB_DEINT_SYNCSTATE 0xf4ac0018 +-#define F367CAB_DEINT_SYNLOST 0xf4ac0004 +-#define F367CAB_DESCR_SYNCSTATE 0xf4ac0002 +- +-/* RS_COUNTER_0 */ +-#define R367CAB_RS_COUNTER_0 0xf4ae +-#define F367CAB_BK_CT_L 0xf4ae00ff +- +-/* RS_COUNTER_1 */ +-#define R367CAB_RS_COUNTER_1 0xf4af +-#define F367CAB_BK_CT_H 0xf4af00ff +- +-/* RS_COUNTER_2 */ +-#define R367CAB_RS_COUNTER_2 0xf4b0 +-#define F367CAB_CORR_CT_L 0xf4b000ff +- +-/* RS_COUNTER_3 */ +-#define R367CAB_RS_COUNTER_3 0xf4b1 +-#define F367CAB_CORR_CT_H 0xf4b100ff +- +-/* RS_COUNTER_4 */ +-#define R367CAB_RS_COUNTER_4 0xf4b2 +-#define F367CAB_UNCORR_CT_L 0xf4b200ff +- +-/* RS_COUNTER_5 */ +-#define R367CAB_RS_COUNTER_5 0xf4b3 +-#define F367CAB_UNCORR_CT_H 0xf4b300ff +- +-/* BERT_0 */ +-#define R367CAB_BERT_0 0xf4b4 +-#define F367CAB_RS_NOCORR 0xf4b40004 +-#define F367CAB_CT_HOLD 0xf4b40002 +-#define F367CAB_CT_CLEAR 0xf4b40001 +- +-/* BERT_1 */ +-#define R367CAB_BERT_1 0xf4b5 +-#define F367CAB_BERT_ON 0xf4b50020 +-#define F367CAB_BERT_ERR_SRC 0xf4b50010 +-#define F367CAB_BERT_ERR_MODE 0xf4b50008 +-#define F367CAB_BERT_NBYTE 0xf4b50007 +- +-/* BERT_2 */ +-#define R367CAB_BERT_2 0xf4b6 +-#define F367CAB_BERT_ERRCOUNT_L 0xf4b600ff +- +-/* BERT_3 */ +-#define R367CAB_BERT_3 0xf4b7 +-#define F367CAB_BERT_ERRCOUNT_H 0xf4b700ff +- +-/* OUTFORMAT_0 */ +-#define R367CAB_OUTFORMAT_0 0xf4b8 +-#define F367CAB_CLK_POLARITY 0xf4b80080 +-#define F367CAB_FEC_TYPE 0xf4b80040 +-#define F367CAB_SYNC_STRIP 0xf4b80008 +-#define F367CAB_TS_SWAP 0xf4b80004 +-#define F367CAB_OUTFORMAT 0xf4b80003 +- +-/* OUTFORMAT_1 */ +-#define R367CAB_OUTFORMAT_1 0xf4b9 +-#define F367CAB_CI_DIVRANGE 0xf4b900ff +- +-/* SMOOTHER_2 */ +-#define R367CAB_SMOOTHER_2 0xf4be +-#define F367CAB_FIFO_BYPASS 0xf4be0020 +- +-/* TSMF_CTRL_0 */ +-#define R367CAB_TSMF_CTRL_0 0xf4c0 +-#define F367CAB_TS_NUMBER 0xf4c0001e +-#define F367CAB_SEL_MODE 0xf4c00001 +- +-/* TSMF_CTRL_1 */ +-#define R367CAB_TSMF_CTRL_1 0xf4c1 +-#define F367CAB_CHECK_ERROR_BIT 0xf4c10080 +-#define F367CAB_CHCK_F_SYNC 0xf4c10040 +-#define F367CAB_H_MODE 0xf4c10008 +-#define F367CAB_D_V_MODE 0xf4c10004 +-#define F367CAB_MODE 0xf4c10003 +- +-/* TSMF_CTRL_3 */ +-#define R367CAB_TSMF_CTRL_3 0xf4c3 +-#define F367CAB_SYNC_IN_COUNT 0xf4c300f0 +-#define F367CAB_SYNC_OUT_COUNT 0xf4c3000f +- +-/* TS_ON_ID_0 */ +-#define R367CAB_TS_ON_ID_0 0xf4c4 +-#define F367CAB_TS_ID_L 0xf4c400ff +- +-/* TS_ON_ID_1 */ +-#define R367CAB_TS_ON_ID_1 0xf4c5 +-#define F367CAB_TS_ID_H 0xf4c500ff +- +-/* TS_ON_ID_2 */ +-#define R367CAB_TS_ON_ID_2 0xf4c6 +-#define F367CAB_ON_ID_L 0xf4c600ff +- +-/* TS_ON_ID_3 */ +-#define R367CAB_TS_ON_ID_3 0xf4c7 +-#define F367CAB_ON_ID_H 0xf4c700ff +- +-/* RE_STATUS_0 */ +-#define R367CAB_RE_STATUS_0 0xf4c8 +-#define F367CAB_RECEIVE_STATUS_L 0xf4c800ff +- +-/* RE_STATUS_1 */ +-#define R367CAB_RE_STATUS_1 0xf4c9 +-#define F367CAB_RECEIVE_STATUS_LH 0xf4c900ff +- +-/* RE_STATUS_2 */ +-#define R367CAB_RE_STATUS_2 0xf4ca +-#define F367CAB_RECEIVE_STATUS_HL 0xf4ca00ff +- +-/* RE_STATUS_3 */ +-#define R367CAB_RE_STATUS_3 0xf4cb +-#define F367CAB_RECEIVE_STATUS_HH 0xf4cb003f +- +-/* TS_STATUS_0 */ +-#define R367CAB_TS_STATUS_0 0xf4cc +-#define F367CAB_TS_STATUS_L 0xf4cc00ff +- +-/* TS_STATUS_1 */ +-#define R367CAB_TS_STATUS_1 0xf4cd +-#define F367CAB_TS_STATUS_H 0xf4cd007f +- +-/* TS_STATUS_2 */ +-#define R367CAB_TS_STATUS_2 0xf4ce +-#define F367CAB_ERROR 0xf4ce0080 +-#define F367CAB_EMERGENCY 0xf4ce0040 +-#define F367CAB_CRE_TS 0xf4ce0030 +-#define F367CAB_VER 0xf4ce000e +-#define F367CAB_M_LOCK 0xf4ce0001 +- +-/* TS_STATUS_3 */ +-#define R367CAB_TS_STATUS_3 0xf4cf +-#define F367CAB_UPDATE_READY 0xf4cf0080 +-#define F367CAB_END_FRAME_HEADER 0xf4cf0040 +-#define F367CAB_CONTCNT 0xf4cf0020 +-#define F367CAB_TS_IDENTIFIER_SEL 0xf4cf000f +- +-/* T_O_ID_0 */ +-#define R367CAB_T_O_ID_0 0xf4d0 +-#define F367CAB_ON_ID_I_L 0xf4d000ff +- +-/* T_O_ID_1 */ +-#define R367CAB_T_O_ID_1 0xf4d1 +-#define F367CAB_ON_ID_I_H 0xf4d100ff +- +-/* T_O_ID_2 */ +-#define R367CAB_T_O_ID_2 0xf4d2 +-#define F367CAB_TS_ID_I_L 0xf4d200ff +- +-/* T_O_ID_3 */ +-#define R367CAB_T_O_ID_3 0xf4d3 +-#define F367CAB_TS_ID_I_H 0xf4d300ff +- +-#define STV0367CAB_NBREGS 187 +- +-#endif +diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h +deleted file mode 100644 +index 91c7ee8..0000000 +--- a/drivers/media/dvb/frontends/stv0900.h ++++ /dev/null +@@ -1,74 +0,0 @@ +-/* +- * stv0900.h +- * +- * Driver for ST STV0900 satellite demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0900_H +-#define STV0900_H +- +-#include +-#include "dvb_frontend.h" +- +-struct stv0900_reg { +- u16 addr; +- u8 val; +-}; +- +-struct stv0900_config { +- u8 demod_address; +- u8 demod_mode; +- u32 xtal; +- u8 clkmode;/* 0 for CLKI, 2 for XTALI */ +- +- u8 diseqc_mode; +- +- u8 path1_mode; +- u8 path2_mode; +- struct stv0900_reg *ts_config_regs; +- u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */ +- u8 tun2_maddress; +- u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ +- u8 tun2_adc; +- u8 tun1_type;/* for now 3 for stb6100 auto, else - software */ +- u8 tun2_type; +- /* Set device param to start dma */ +- int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); +- /* Hook for Lock LED */ +- void (*set_lock_led)(struct dvb_frontend *fe, int offon); +-}; +- +-#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, +- struct i2c_adapter *i2c, int demod); +-#else +-static inline struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, +- struct i2c_adapter *i2c, int demod) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +- +diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c +deleted file mode 100644 +index 7f1bada..0000000 +--- a/drivers/media/dvb/frontends/stv0900_core.c ++++ /dev/null +@@ -1,1959 +0,0 @@ +-/* +- * stv0900_core.c +- * +- * Driver for ST STV0900 satellite demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "stv0900.h" +-#include "stv0900_reg.h" +-#include "stv0900_priv.h" +-#include "stv0900_init.h" +- +-int stvdebug = 1; +-module_param_named(debug, stvdebug, int, 0644); +- +-/* internal params node */ +-struct stv0900_inode { +- /* pointer for internal params, one for each pair of demods */ +- struct stv0900_internal *internal; +- struct stv0900_inode *next_inode; +-}; +- +-/* first internal params */ +-static struct stv0900_inode *stv0900_first_inode; +- +-/* find chip by i2c adapter and i2c address */ +-static struct stv0900_inode *find_inode(struct i2c_adapter *i2c_adap, +- u8 i2c_addr) +-{ +- struct stv0900_inode *temp_chip = stv0900_first_inode; +- +- if (temp_chip != NULL) { +- /* +- Search of the last stv0900 chip or +- find it by i2c adapter and i2c address */ +- while ((temp_chip != NULL) && +- ((temp_chip->internal->i2c_adap != i2c_adap) || +- (temp_chip->internal->i2c_addr != i2c_addr))) +- +- temp_chip = temp_chip->next_inode; +- +- } +- +- return temp_chip; +-} +- +-/* deallocating chip */ +-static void remove_inode(struct stv0900_internal *internal) +-{ +- struct stv0900_inode *prev_node = stv0900_first_inode; +- struct stv0900_inode *del_node = find_inode(internal->i2c_adap, +- internal->i2c_addr); +- +- if (del_node != NULL) { +- if (del_node == stv0900_first_inode) { +- stv0900_first_inode = del_node->next_inode; +- } else { +- while (prev_node->next_inode != del_node) +- prev_node = prev_node->next_inode; +- +- if (del_node->next_inode == NULL) +- prev_node->next_inode = NULL; +- else +- prev_node->next_inode = +- prev_node->next_inode->next_inode; +- } +- +- kfree(del_node); +- } +-} +- +-/* allocating new chip */ +-static struct stv0900_inode *append_internal(struct stv0900_internal *internal) +-{ +- struct stv0900_inode *new_node = stv0900_first_inode; +- +- if (new_node == NULL) { +- new_node = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL); +- stv0900_first_inode = new_node; +- } else { +- while (new_node->next_inode != NULL) +- new_node = new_node->next_inode; +- +- new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), +- GFP_KERNEL); +- if (new_node->next_inode != NULL) +- new_node = new_node->next_inode; +- else +- new_node = NULL; +- } +- +- if (new_node != NULL) { +- new_node->internal = internal; +- new_node->next_inode = NULL; +- } +- +- return new_node; +-} +- +-s32 ge2comp(s32 a, s32 width) +-{ +- if (width == 32) +- return a; +- else +- return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a; +-} +- +-void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr, +- u8 reg_data) +-{ +- u8 data[3]; +- int ret; +- struct i2c_msg i2cmsg = { +- .addr = intp->i2c_addr, +- .flags = 0, +- .len = 3, +- .buf = data, +- }; +- +- data[0] = MSB(reg_addr); +- data[1] = LSB(reg_addr); +- data[2] = reg_data; +- +- ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1); +- if (ret != 1) +- dprintk("%s: i2c error %d\n", __func__, ret); +-} +- +-u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) +-{ +- int ret; +- u8 b0[] = { MSB(reg), LSB(reg) }; +- u8 buf = 0; +- struct i2c_msg msg[] = { +- { +- .addr = intp->i2c_addr, +- .flags = 0, +- .buf = b0, +- .len = 2, +- }, { +- .addr = intp->i2c_addr, +- .flags = I2C_M_RD, +- .buf = &buf, +- .len = 1, +- }, +- }; +- +- ret = i2c_transfer(intp->i2c_adap, msg, 2); +- if (ret != 2) +- dprintk("%s: i2c error %d, reg[0x%02x]\n", +- __func__, ret, reg); +- +- return buf; +-} +- +-static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +-{ +- u8 position = 0, i = 0; +- +- (*mask) = label & 0xff; +- +- while ((position == 0) && (i < 8)) { +- position = ((*mask) >> i) & 0x01; +- i++; +- } +- +- (*pos) = (i - 1); +-} +- +-void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) +-{ +- u8 reg, mask, pos; +- +- reg = stv0900_read_reg(intp, (label >> 16) & 0xffff); +- extract_mask_pos(label, &mask, &pos); +- +- val = mask & (val << pos); +- +- reg = (reg & (~mask)) | val; +- stv0900_write_reg(intp, (label >> 16) & 0xffff, reg); +- +-} +- +-u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) +-{ +- u8 val = 0xff; +- u8 mask, pos; +- +- extract_mask_pos(label, &mask, &pos); +- +- val = stv0900_read_reg(intp, label >> 16); +- val = (val & mask) >> pos; +- +- return val; +-} +- +-static enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) +-{ +- s32 i; +- +- if (intp == NULL) +- return STV0900_INVALID_HANDLE; +- +- intp->chip_id = stv0900_read_reg(intp, R0900_MID); +- +- if (intp->errs != STV0900_NO_ERROR) +- return intp->errs; +- +- /*Startup sequence*/ +- stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c); +- stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c); +- msleep(3); +- stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c); +- stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f); +- stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20); +- stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20); +- stv0900_write_reg(intp, R0900_NCOARSE, 0x13); +- msleep(3); +- stv0900_write_reg(intp, R0900_I2CCFG, 0x08); +- +- switch (intp->clkmode) { +- case 0: +- case 2: +- stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 +- | intp->clkmode); +- break; +- default: +- /* preserve SELOSCI bit */ +- i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); +- stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i); +- break; +- } +- +- msleep(3); +- for (i = 0; i < 181; i++) +- stv0900_write_reg(intp, STV0900_InitVal[i][0], +- STV0900_InitVal[i][1]); +- +- if (stv0900_read_reg(intp, R0900_MID) >= 0x20) { +- stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c); +- for (i = 0; i < 32; i++) +- stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0], +- STV0900_Cut20_AddOnVal[i][1]); +- } +- +- stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c); +- stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c); +- +- stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01); +- stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21); +- +- stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20); +- stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20); +- +- stv0900_write_reg(intp, R0900_TSTRES0, 0x80); +- stv0900_write_reg(intp, R0900_TSTRES0, 0x00); +- +- return STV0900_NO_ERROR; +-} +- +-static u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) +-{ +- u32 mclk = 90000000, div = 0, ad_div = 0; +- +- div = stv0900_get_bits(intp, F0900_M_DIV); +- ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); +- +- mclk = (div + 1) * ext_clk / ad_div; +- +- dprintk("%s: Calculated Mclk = %d\n", __func__, mclk); +- +- return mclk; +-} +- +-static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) +-{ +- u32 m_div, clk_sel; +- +- dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, +- intp->quartz); +- +- if (intp == NULL) +- return STV0900_INVALID_HANDLE; +- +- if (intp->errs) +- return STV0900_I2C_ERROR; +- +- clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); +- m_div = ((clk_sel * mclk) / intp->quartz) - 1; +- stv0900_write_bits(intp, F0900_M_DIV, m_div); +- intp->mclk = stv0900_get_mclk_freq(intp, +- intp->quartz); +- +- /*Set the DiseqC frequency to 22KHz */ +- /* +- Formula: +- DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) +- DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) +- */ +- m_div = intp->mclk / 704000; +- stv0900_write_reg(intp, R0900_P1_F22TX, m_div); +- stv0900_write_reg(intp, R0900_P1_F22RX, m_div); +- +- stv0900_write_reg(intp, R0900_P2_F22TX, m_div); +- stv0900_write_reg(intp, R0900_P2_F22RX, m_div); +- +- if ((intp->errs)) +- return STV0900_I2C_ERROR; +- +- return STV0900_NO_ERROR; +-} +- +-static u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, +- enum fe_stv0900_demod_num demod) +-{ +- u32 lsb, msb, hsb, err_val; +- +- switch (cntr) { +- case 0: +- default: +- hsb = stv0900_get_bits(intp, ERR_CNT12); +- msb = stv0900_get_bits(intp, ERR_CNT11); +- lsb = stv0900_get_bits(intp, ERR_CNT10); +- break; +- case 1: +- hsb = stv0900_get_bits(intp, ERR_CNT22); +- msb = stv0900_get_bits(intp, ERR_CNT21); +- lsb = stv0900_get_bits(intp, ERR_CNT20); +- break; +- } +- +- err_val = (hsb << 16) + (msb << 8) + (lsb); +- +- return err_val; +-} +- +-static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- stv0900_write_bits(intp, I2CT_ON, enable); +- +- return 0; +-} +- +-static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp, +- enum fe_stv0900_clock_type path1_ts, +- enum fe_stv0900_clock_type path2_ts) +-{ +- +- dprintk("%s\n", __func__); +- +- if (intp->chip_id >= 0x20) { +- switch (path1_ts) { +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- switch (path2_ts) { +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- stv0900_write_reg(intp, R0900_TSGENERAL, +- 0x00); +- break; +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- stv0900_write_reg(intp, R0900_TSGENERAL, +- 0x06); +- stv0900_write_bits(intp, +- F0900_P1_TSFIFO_MANSPEED, 3); +- stv0900_write_bits(intp, +- F0900_P2_TSFIFO_MANSPEED, 0); +- stv0900_write_reg(intp, +- R0900_P1_TSSPEED, 0x14); +- stv0900_write_reg(intp, +- R0900_P2_TSSPEED, 0x28); +- break; +- } +- break; +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- switch (path2_ts) { +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- stv0900_write_reg(intp, +- R0900_TSGENERAL, 0x0C); +- break; +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- stv0900_write_reg(intp, +- R0900_TSGENERAL, 0x0A); +- dprintk("%s: 0x0a\n", __func__); +- break; +- } +- break; +- } +- } else { +- switch (path1_ts) { +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- switch (path2_ts) { +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- stv0900_write_reg(intp, R0900_TSGENERAL1X, +- 0x10); +- break; +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- stv0900_write_reg(intp, R0900_TSGENERAL1X, +- 0x16); +- stv0900_write_bits(intp, +- F0900_P1_TSFIFO_MANSPEED, 3); +- stv0900_write_bits(intp, +- F0900_P2_TSFIFO_MANSPEED, 0); +- stv0900_write_reg(intp, R0900_P1_TSSPEED, +- 0x14); +- stv0900_write_reg(intp, R0900_P2_TSSPEED, +- 0x28); +- break; +- } +- +- break; +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- switch (path2_ts) { +- case STV0900_SERIAL_PUNCT_CLOCK: +- case STV0900_SERIAL_CONT_CLOCK: +- default: +- stv0900_write_reg(intp, R0900_TSGENERAL1X, +- 0x14); +- break; +- case STV0900_PARALLEL_PUNCT_CLOCK: +- case STV0900_DVBCI_CLOCK: +- stv0900_write_reg(intp, R0900_TSGENERAL1X, +- 0x12); +- dprintk("%s: 0x12\n", __func__); +- break; +- } +- +- break; +- } +- } +- +- switch (path1_ts) { +- case STV0900_PARALLEL_PUNCT_CLOCK: +- stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); +- stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); +- break; +- case STV0900_DVBCI_CLOCK: +- stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); +- stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); +- break; +- case STV0900_SERIAL_PUNCT_CLOCK: +- stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); +- stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); +- break; +- case STV0900_SERIAL_CONT_CLOCK: +- stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); +- stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); +- break; +- default: +- break; +- } +- +- switch (path2_ts) { +- case STV0900_PARALLEL_PUNCT_CLOCK: +- stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); +- stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); +- break; +- case STV0900_DVBCI_CLOCK: +- stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); +- stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); +- break; +- case STV0900_SERIAL_PUNCT_CLOCK: +- stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); +- stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); +- break; +- case STV0900_SERIAL_CONT_CLOCK: +- stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); +- stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); +- break; +- default: +- break; +- } +- +- stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); +- stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); +- stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); +- stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); +-} +- +-void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, +- u32 bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- +- if (tuner_ops->set_frequency) { +- if ((tuner_ops->set_frequency(fe, frequency)) < 0) +- dprintk("%s: Invalid parameter\n", __func__); +- else +- dprintk("%s: Frequency=%d\n", __func__, frequency); +- +- } +- +- if (tuner_ops->set_bandwidth) { +- if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) +- dprintk("%s: Invalid parameter\n", __func__); +- else +- dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); +- +- } +-} +- +-void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- +- if (tuner_ops->set_bandwidth) { +- if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0) +- dprintk("%s: Invalid parameter\n", __func__); +- else +- dprintk("%s: Bandwidth=%d\n", __func__, bandwidth); +- +- } +-} +- +-u32 stv0900_get_freq_auto(struct stv0900_internal *intp, int demod) +-{ +- u32 freq, round; +- /* Formulat : +- Tuner_Frequency(MHz) = Regs / 64 +- Tuner_granularity(MHz) = Regs / 2048 +- real_Tuner_Frequency = Tuner_Frequency(MHz) - Tuner_granularity(MHz) +- */ +- freq = (stv0900_get_bits(intp, TUN_RFFREQ2) << 10) + +- (stv0900_get_bits(intp, TUN_RFFREQ1) << 2) + +- stv0900_get_bits(intp, TUN_RFFREQ0); +- +- freq = (freq * 1000) / 64; +- +- round = (stv0900_get_bits(intp, TUN_RFRESTE1) >> 2) + +- stv0900_get_bits(intp, TUN_RFRESTE0); +- +- round = (round * 1000) / 2048; +- +- return freq + round; +-} +- +-void stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, +- u32 Bandwidth, int demod) +-{ +- u32 tunerFrequency; +- /* Formulat: +- Tuner_frequency_reg= Frequency(MHz)*64 +- */ +- tunerFrequency = (Frequency * 64) / 1000; +- +- stv0900_write_bits(intp, TUN_RFFREQ2, (tunerFrequency >> 10)); +- stv0900_write_bits(intp, TUN_RFFREQ1, (tunerFrequency >> 2) & 0xff); +- stv0900_write_bits(intp, TUN_RFFREQ0, (tunerFrequency & 0x03)); +- /* Low Pass Filter = BW /2 (MHz)*/ +- stv0900_write_bits(intp, TUN_BW, Bandwidth / 2000000); +- /* Tuner Write trig */ +- stv0900_write_reg(intp, TNRLD, 1); +-} +- +-static s32 stv0900_get_rf_level(struct stv0900_internal *intp, +- const struct stv0900_table *lookup, +- enum fe_stv0900_demod_num demod) +-{ +- s32 agc_gain = 0, +- imin, +- imax, +- i, +- rf_lvl = 0; +- +- dprintk("%s\n", __func__); +- +- if ((lookup == NULL) || (lookup->size <= 0)) +- return 0; +- +- agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), +- stv0900_get_bits(intp, AGCIQ_VALUE0)); +- +- imin = 0; +- imax = lookup->size - 1; +- if (INRANGE(lookup->table[imin].regval, agc_gain, +- lookup->table[imax].regval)) { +- while ((imax - imin) > 1) { +- i = (imax + imin) >> 1; +- +- if (INRANGE(lookup->table[imin].regval, +- agc_gain, +- lookup->table[i].regval)) +- imax = i; +- else +- imin = i; +- } +- +- rf_lvl = (s32)agc_gain - lookup->table[imin].regval; +- rf_lvl *= (lookup->table[imax].realval - +- lookup->table[imin].realval); +- rf_lvl /= (lookup->table[imax].regval - +- lookup->table[imin].regval); +- rf_lvl += lookup->table[imin].realval; +- } else if (agc_gain > lookup->table[0].regval) +- rf_lvl = 5; +- else if (agc_gain < lookup->table[lookup->size-1].regval) +- rf_lvl = -100; +- +- dprintk("%s: RFLevel = %d\n", __func__, rf_lvl); +- +- return rf_lvl; +-} +- +-static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *internal = state->internal; +- s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf, +- state->demod); +- +- rflevel = (rflevel + 100) * (65535 / 70); +- if (rflevel < 0) +- rflevel = 0; +- +- if (rflevel > 65535) +- rflevel = 65535; +- +- *strength = rflevel; +- +- return 0; +-} +- +-static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, +- const struct stv0900_table *lookup) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- s32 c_n = -100, +- regval, +- imin, +- imax, +- i, +- noise_field1, +- noise_field0; +- +- dprintk("%s\n", __func__); +- +- if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { +- noise_field1 = NOSPLHT_NORMED1; +- noise_field0 = NOSPLHT_NORMED0; +- } else { +- noise_field1 = NOSDATAT_NORMED1; +- noise_field0 = NOSDATAT_NORMED0; +- } +- +- if (stv0900_get_bits(intp, LOCK_DEFINITIF)) { +- if ((lookup != NULL) && lookup->size) { +- regval = 0; +- msleep(5); +- for (i = 0; i < 16; i++) { +- regval += MAKEWORD(stv0900_get_bits(intp, +- noise_field1), +- stv0900_get_bits(intp, +- noise_field0)); +- msleep(1); +- } +- +- regval /= 16; +- imin = 0; +- imax = lookup->size - 1; +- if (INRANGE(lookup->table[imin].regval, +- regval, +- lookup->table[imax].regval)) { +- while ((imax - imin) > 1) { +- i = (imax + imin) >> 1; +- if (INRANGE(lookup->table[imin].regval, +- regval, +- lookup->table[i].regval)) +- imax = i; +- else +- imin = i; +- } +- +- c_n = ((regval - lookup->table[imin].regval) +- * (lookup->table[imax].realval +- - lookup->table[imin].realval) +- / (lookup->table[imax].regval +- - lookup->table[imin].regval)) +- + lookup->table[imin].realval; +- } else if (regval < lookup->table[imin].regval) +- c_n = 1000; +- } +- } +- +- return c_n; +-} +- +-static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- u8 err_val1, err_val0; +- u32 header_err_val = 0; +- +- *ucblocks = 0x0; +- if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { +- /* DVB-S2 delineator errors count */ +- +- /* retreiving number for errnous headers */ +- err_val1 = stv0900_read_reg(intp, BBFCRCKO1); +- err_val0 = stv0900_read_reg(intp, BBFCRCKO0); +- header_err_val = (err_val1 << 8) | err_val0; +- +- /* retreiving number for errnous packets */ +- err_val1 = stv0900_read_reg(intp, UPCRCKO1); +- err_val0 = stv0900_read_reg(intp, UPCRCKO0); +- *ucblocks = (err_val1 << 8) | err_val0; +- *ucblocks += header_err_val; +- } +- +- return 0; +-} +- +-static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- s32 snrlcl = stv0900_carr_get_quality(fe, +- (const struct stv0900_table *)&stv0900_s2_cn); +- snrlcl = (snrlcl + 30) * 384; +- if (snrlcl < 0) +- snrlcl = 0; +- +- if (snrlcl > 65535) +- snrlcl = 65535; +- +- *snr = snrlcl; +- +- return 0; +-} +- +-static u32 stv0900_get_ber(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- u32 ber = 10000000, i; +- s32 demod_state; +- +- demod_state = stv0900_get_bits(intp, HEADER_MODE); +- +- switch (demod_state) { +- case STV0900_SEARCH: +- case STV0900_PLH_DETECTED: +- default: +- ber = 10000000; +- break; +- case STV0900_DVBS_FOUND: +- ber = 0; +- for (i = 0; i < 5; i++) { +- msleep(5); +- ber += stv0900_get_err_count(intp, 0, demod); +- } +- +- ber /= 5; +- if (stv0900_get_bits(intp, PRFVIT)) { +- ber *= 9766; +- ber = ber >> 13; +- } +- +- break; +- case STV0900_DVBS2_FOUND: +- ber = 0; +- for (i = 0; i < 5; i++) { +- msleep(5); +- ber += stv0900_get_err_count(intp, 0, demod); +- } +- +- ber /= 5; +- if (stv0900_get_bits(intp, PKTDELIN_LOCK)) { +- ber *= 9766; +- ber = ber >> 13; +- } +- +- break; +- } +- +- return ber; +-} +- +-static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *internal = state->internal; +- +- *ber = stv0900_get_ber(internal, state->demod); +- +- return 0; +-} +- +-int stv0900_get_demod_lock(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod, s32 time_out) +-{ +- s32 timer = 0, +- lock = 0; +- +- enum fe_stv0900_search_state dmd_state; +- +- while ((timer < time_out) && (lock == 0)) { +- dmd_state = stv0900_get_bits(intp, HEADER_MODE); +- dprintk("Demod State = %d\n", dmd_state); +- switch (dmd_state) { +- case STV0900_SEARCH: +- case STV0900_PLH_DETECTED: +- default: +- lock = 0; +- break; +- case STV0900_DVBS2_FOUND: +- case STV0900_DVBS_FOUND: +- lock = stv0900_get_bits(intp, LOCK_DEFINITIF); +- break; +- } +- +- if (lock == 0) +- msleep(10); +- +- timer += 10; +- } +- +- if (lock) +- dprintk("DEMOD LOCK OK\n"); +- else +- dprintk("DEMOD LOCK FAIL\n"); +- +- return lock; +-} +- +-void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- s32 regflist, +- i; +- +- dprintk("%s\n", __func__); +- +- regflist = MODCODLST0; +- +- for (i = 0; i < 16; i++) +- stv0900_write_reg(intp, regflist + i, 0xff); +-} +- +-void stv0900_activate_s2_modcod(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- u32 matype, +- mod_code, +- fmod, +- reg_index, +- field_index; +- +- dprintk("%s\n", __func__); +- +- if (intp->chip_id <= 0x11) { +- msleep(5); +- +- mod_code = stv0900_read_reg(intp, PLHMODCOD); +- matype = mod_code & 0x3; +- mod_code = (mod_code & 0x7f) >> 2; +- +- reg_index = MODCODLSTF - mod_code / 2; +- field_index = mod_code % 2; +- +- switch (matype) { +- case 0: +- default: +- fmod = 14; +- break; +- case 1: +- fmod = 13; +- break; +- case 2: +- fmod = 11; +- break; +- case 3: +- fmod = 7; +- break; +- } +- +- if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910)) +- && (matype <= 1)) { +- if (field_index == 0) +- stv0900_write_reg(intp, reg_index, +- 0xf0 | fmod); +- else +- stv0900_write_reg(intp, reg_index, +- (fmod << 4) | 0xf); +- } +- +- } else if (intp->chip_id >= 0x12) { +- for (reg_index = 0; reg_index < 7; reg_index++) +- stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff); +- +- stv0900_write_reg(intp, MODCODLSTE, 0xff); +- stv0900_write_reg(intp, MODCODLSTF, 0xcf); +- for (reg_index = 0; reg_index < 8; reg_index++) +- stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc); +- +- +- } +-} +- +-void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- u32 reg_index; +- +- dprintk("%s\n", __func__); +- +- stv0900_write_reg(intp, MODCODLST0, 0xff); +- stv0900_write_reg(intp, MODCODLST1, 0xf0); +- stv0900_write_reg(intp, MODCODLSTF, 0x0f); +- for (reg_index = 0; reg_index < 13; reg_index++) +- stv0900_write_reg(intp, MODCODLST2 + reg_index, 0); +- +-} +- +-static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-void stv0900_start_search(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- u32 freq; +- s16 freq_s16 ; +- +- stv0900_write_bits(intp, DEMOD_MODE, 0x1f); +- if (intp->chip_id == 0x10) +- stv0900_write_reg(intp, CORRELEXP, 0xaa); +- +- if (intp->chip_id < 0x20) +- stv0900_write_reg(intp, CARHDR, 0x55); +- +- if (intp->chip_id <= 0x20) { +- if (intp->symbol_rate[0] <= 5000000) { +- stv0900_write_reg(intp, CARCFG, 0x44); +- stv0900_write_reg(intp, CFRUP1, 0x0f); +- stv0900_write_reg(intp, CFRUP0, 0xff); +- stv0900_write_reg(intp, CFRLOW1, 0xf0); +- stv0900_write_reg(intp, CFRLOW0, 0x00); +- stv0900_write_reg(intp, RTCS2, 0x68); +- } else { +- stv0900_write_reg(intp, CARCFG, 0xc4); +- stv0900_write_reg(intp, RTCS2, 0x44); +- } +- +- } else { /*cut 3.0 above*/ +- if (intp->symbol_rate[demod] <= 5000000) +- stv0900_write_reg(intp, RTCS2, 0x68); +- else +- stv0900_write_reg(intp, RTCS2, 0x44); +- +- stv0900_write_reg(intp, CARCFG, 0x46); +- if (intp->srch_algo[demod] == STV0900_WARM_START) { +- freq = 1000 << 16; +- freq /= (intp->mclk / 1000); +- freq_s16 = (s16)freq; +- } else { +- freq = (intp->srch_range[demod] / 2000); +- if (intp->symbol_rate[demod] <= 5000000) +- freq += 80; +- else +- freq += 600; +- +- freq = freq << 16; +- freq /= (intp->mclk / 1000); +- freq_s16 = (s16)freq; +- } +- +- stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16)); +- stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16)); +- freq_s16 *= (-1); +- stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16)); +- stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16)); +- } +- +- stv0900_write_reg(intp, CFRINIT1, 0); +- stv0900_write_reg(intp, CFRINIT0, 0); +- +- if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, EQUALCFG, 0x41); +- stv0900_write_reg(intp, FFECFG, 0x41); +- +- if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || +- (intp->srch_standard[demod] == STV0900_SEARCH_DSS) || +- (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) { +- stv0900_write_reg(intp, VITSCALE, +- 0x82); +- stv0900_write_reg(intp, VAVSRVIT, 0x0); +- } +- } +- +- stv0900_write_reg(intp, SFRSTEP, 0x00); +- stv0900_write_reg(intp, TMGTHRISE, 0xe0); +- stv0900_write_reg(intp, TMGTHFALL, 0xc0); +- stv0900_write_bits(intp, SCAN_ENABLE, 0); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0); +- stv0900_write_reg(intp, RTC, 0x88); +- if (intp->chip_id >= 0x20) { +- if (intp->symbol_rate[demod] < 2000000) { +- if (intp->chip_id <= 0x20) +- stv0900_write_reg(intp, CARFREQ, 0x39); +- else /*cut 3.0*/ +- stv0900_write_reg(intp, CARFREQ, 0x89); +- +- stv0900_write_reg(intp, CARHDR, 0x40); +- } else if (intp->symbol_rate[demod] < 10000000) { +- stv0900_write_reg(intp, CARFREQ, 0x4c); +- stv0900_write_reg(intp, CARHDR, 0x20); +- } else { +- stv0900_write_reg(intp, CARFREQ, 0x4b); +- stv0900_write_reg(intp, CARHDR, 0x20); +- } +- +- } else { +- if (intp->symbol_rate[demod] < 10000000) +- stv0900_write_reg(intp, CARFREQ, 0xef); +- else +- stv0900_write_reg(intp, CARFREQ, 0xed); +- } +- +- switch (intp->srch_algo[demod]) { +- case STV0900_WARM_START: +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- break; +- case STV0900_COLD_START: +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, DMDISTATE, 0x15); +- break; +- default: +- break; +- } +-} +- +-u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, +- s32 pilot, u8 chip_id) +-{ +- u8 aclc_value = 0x29; +- s32 i; +- const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; +- +- dprintk("%s\n", __func__); +- +- if (chip_id <= 0x12) { +- cls2 = FE_STV0900_S2CarLoop; +- cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; +- cllas2 = FE_STV0900_S2APSKCarLoopCut30; +- } else if (chip_id == 0x20) { +- cls2 = FE_STV0900_S2CarLoopCut20; +- cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; +- cllas2 = FE_STV0900_S2APSKCarLoopCut20; +- } else { +- cls2 = FE_STV0900_S2CarLoopCut30; +- cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; +- cllas2 = FE_STV0900_S2APSKCarLoopCut30; +- } +- +- if (modcode < STV0900_QPSK_12) { +- i = 0; +- while ((i < 3) && (modcode != cllqs2[i].modcode)) +- i++; +- +- if (i >= 3) +- i = 2; +- } else { +- i = 0; +- while ((i < 14) && (modcode != cls2[i].modcode)) +- i++; +- +- if (i >= 14) { +- i = 0; +- while ((i < 11) && (modcode != cllas2[i].modcode)) +- i++; +- +- if (i >= 11) +- i = 10; +- } +- } +- +- if (modcode <= STV0900_QPSK_25) { +- if (pilot) { +- if (srate <= 3000000) +- aclc_value = cllqs2[i].car_loop_pilots_on_2; +- else if (srate <= 7000000) +- aclc_value = cllqs2[i].car_loop_pilots_on_5; +- else if (srate <= 15000000) +- aclc_value = cllqs2[i].car_loop_pilots_on_10; +- else if (srate <= 25000000) +- aclc_value = cllqs2[i].car_loop_pilots_on_20; +- else +- aclc_value = cllqs2[i].car_loop_pilots_on_30; +- } else { +- if (srate <= 3000000) +- aclc_value = cllqs2[i].car_loop_pilots_off_2; +- else if (srate <= 7000000) +- aclc_value = cllqs2[i].car_loop_pilots_off_5; +- else if (srate <= 15000000) +- aclc_value = cllqs2[i].car_loop_pilots_off_10; +- else if (srate <= 25000000) +- aclc_value = cllqs2[i].car_loop_pilots_off_20; +- else +- aclc_value = cllqs2[i].car_loop_pilots_off_30; +- } +- +- } else if (modcode <= STV0900_8PSK_910) { +- if (pilot) { +- if (srate <= 3000000) +- aclc_value = cls2[i].car_loop_pilots_on_2; +- else if (srate <= 7000000) +- aclc_value = cls2[i].car_loop_pilots_on_5; +- else if (srate <= 15000000) +- aclc_value = cls2[i].car_loop_pilots_on_10; +- else if (srate <= 25000000) +- aclc_value = cls2[i].car_loop_pilots_on_20; +- else +- aclc_value = cls2[i].car_loop_pilots_on_30; +- } else { +- if (srate <= 3000000) +- aclc_value = cls2[i].car_loop_pilots_off_2; +- else if (srate <= 7000000) +- aclc_value = cls2[i].car_loop_pilots_off_5; +- else if (srate <= 15000000) +- aclc_value = cls2[i].car_loop_pilots_off_10; +- else if (srate <= 25000000) +- aclc_value = cls2[i].car_loop_pilots_off_20; +- else +- aclc_value = cls2[i].car_loop_pilots_off_30; +- } +- +- } else { +- if (srate <= 3000000) +- aclc_value = cllas2[i].car_loop_pilots_on_2; +- else if (srate <= 7000000) +- aclc_value = cllas2[i].car_loop_pilots_on_5; +- else if (srate <= 15000000) +- aclc_value = cllas2[i].car_loop_pilots_on_10; +- else if (srate <= 25000000) +- aclc_value = cllas2[i].car_loop_pilots_on_20; +- else +- aclc_value = cllas2[i].car_loop_pilots_on_30; +- } +- +- return aclc_value; +-} +- +-u8 stv0900_get_optim_short_carr_loop(s32 srate, +- enum fe_stv0900_modulation modulation, +- u8 chip_id) +-{ +- const struct stv0900_short_frames_car_loop_optim *s2scl; +- const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30; +- s32 mod_index = 0; +- u8 aclc_value = 0x0b; +- +- dprintk("%s\n", __func__); +- +- s2scl = FE_STV0900_S2ShortCarLoop; +- s2sclc30 = FE_STV0900_S2ShortCarLoopCut30; +- +- switch (modulation) { +- case STV0900_QPSK: +- default: +- mod_index = 0; +- break; +- case STV0900_8PSK: +- mod_index = 1; +- break; +- case STV0900_16APSK: +- mod_index = 2; +- break; +- case STV0900_32APSK: +- mod_index = 3; +- break; +- } +- +- if (chip_id >= 0x30) { +- if (srate <= 3000000) +- aclc_value = s2sclc30[mod_index].car_loop_2; +- else if (srate <= 7000000) +- aclc_value = s2sclc30[mod_index].car_loop_5; +- else if (srate <= 15000000) +- aclc_value = s2sclc30[mod_index].car_loop_10; +- else if (srate <= 25000000) +- aclc_value = s2sclc30[mod_index].car_loop_20; +- else +- aclc_value = s2sclc30[mod_index].car_loop_30; +- +- } else if (chip_id >= 0x20) { +- if (srate <= 3000000) +- aclc_value = s2scl[mod_index].car_loop_cut20_2; +- else if (srate <= 7000000) +- aclc_value = s2scl[mod_index].car_loop_cut20_5; +- else if (srate <= 15000000) +- aclc_value = s2scl[mod_index].car_loop_cut20_10; +- else if (srate <= 25000000) +- aclc_value = s2scl[mod_index].car_loop_cut20_20; +- else +- aclc_value = s2scl[mod_index].car_loop_cut20_30; +- +- } else { +- if (srate <= 3000000) +- aclc_value = s2scl[mod_index].car_loop_cut12_2; +- else if (srate <= 7000000) +- aclc_value = s2scl[mod_index].car_loop_cut12_5; +- else if (srate <= 15000000) +- aclc_value = s2scl[mod_index].car_loop_cut12_10; +- else if (srate <= 25000000) +- aclc_value = s2scl[mod_index].car_loop_cut12_20; +- else +- aclc_value = s2scl[mod_index].car_loop_cut12_30; +- +- } +- +- return aclc_value; +-} +- +-static +-enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, +- enum fe_stv0900_demod_mode LDPC_Mode, +- enum fe_stv0900_demod_num demod) +-{ +- enum fe_stv0900_error error = STV0900_NO_ERROR; +- s32 reg_ind; +- +- dprintk("%s\n", __func__); +- +- switch (LDPC_Mode) { +- case STV0900_DUAL: +- default: +- if ((intp->demod_mode != STV0900_DUAL) +- || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) { +- stv0900_write_reg(intp, R0900_GENCFG, 0x1d); +- +- intp->demod_mode = STV0900_DUAL; +- +- stv0900_write_bits(intp, F0900_FRESFEC, 1); +- stv0900_write_bits(intp, F0900_FRESFEC, 0); +- +- for (reg_ind = 0; reg_ind < 7; reg_ind++) +- stv0900_write_reg(intp, +- R0900_P1_MODCODLST0 + reg_ind, +- 0xff); +- for (reg_ind = 0; reg_ind < 8; reg_ind++) +- stv0900_write_reg(intp, +- R0900_P1_MODCODLST7 + reg_ind, +- 0xcc); +- +- stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff); +- stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf); +- +- for (reg_ind = 0; reg_ind < 7; reg_ind++) +- stv0900_write_reg(intp, +- R0900_P2_MODCODLST0 + reg_ind, +- 0xff); +- for (reg_ind = 0; reg_ind < 8; reg_ind++) +- stv0900_write_reg(intp, +- R0900_P2_MODCODLST7 + reg_ind, +- 0xcc); +- +- stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff); +- stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf); +- } +- +- break; +- case STV0900_SINGLE: +- if (demod == STV0900_DEMOD_2) { +- stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1); +- stv0900_activate_s2_modcod_single(intp, +- STV0900_DEMOD_2); +- stv0900_write_reg(intp, R0900_GENCFG, 0x06); +- } else { +- stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2); +- stv0900_activate_s2_modcod_single(intp, +- STV0900_DEMOD_1); +- stv0900_write_reg(intp, R0900_GENCFG, 0x04); +- } +- +- intp->demod_mode = STV0900_SINGLE; +- +- stv0900_write_bits(intp, F0900_FRESFEC, 1); +- stv0900_write_bits(intp, F0900_FRESFEC, 0); +- stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1); +- stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0); +- stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1); +- stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0); +- break; +- } +- +- return error; +-} +- +-static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, +- struct stv0900_init_params *p_init) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- enum fe_stv0900_error error = STV0900_NO_ERROR; +- enum fe_stv0900_error demodError = STV0900_NO_ERROR; +- struct stv0900_internal *intp = NULL; +- int selosci, i; +- +- struct stv0900_inode *temp_int = find_inode(state->i2c_adap, +- state->config->demod_address); +- +- dprintk("%s\n", __func__); +- +- if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) { +- state->internal = temp_int->internal; +- (state->internal->dmds_used)++; +- dprintk("%s: Find Internal Structure!\n", __func__); +- return STV0900_NO_ERROR; +- } else { +- state->internal = kmalloc(sizeof(struct stv0900_internal), +- GFP_KERNEL); +- if (state->internal == NULL) +- return STV0900_INVALID_HANDLE; +- temp_int = append_internal(state->internal); +- if (temp_int == NULL) { +- kfree(state->internal); +- state->internal = NULL; +- return STV0900_INVALID_HANDLE; +- } +- state->internal->dmds_used = 1; +- state->internal->i2c_adap = state->i2c_adap; +- state->internal->i2c_addr = state->config->demod_address; +- state->internal->clkmode = state->config->clkmode; +- state->internal->errs = STV0900_NO_ERROR; +- dprintk("%s: Create New Internal Structure!\n", __func__); +- } +- +- if (state->internal == NULL) { +- error = STV0900_INVALID_HANDLE; +- return error; +- } +- +- demodError = stv0900_initialize(state->internal); +- if (demodError == STV0900_NO_ERROR) { +- error = STV0900_NO_ERROR; +- } else { +- if (demodError == STV0900_INVALID_HANDLE) +- error = STV0900_INVALID_HANDLE; +- else +- error = STV0900_I2C_ERROR; +- +- return error; +- } +- +- intp = state->internal; +- +- intp->demod_mode = p_init->demod_mode; +- stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1); +- intp->chip_id = stv0900_read_reg(intp, R0900_MID); +- intp->rolloff = p_init->rolloff; +- intp->quartz = p_init->dmd_ref_clk; +- +- stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); +- stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); +- +- intp->ts_config = p_init->ts_config; +- if (intp->ts_config == NULL) +- stv0900_set_ts_parallel_serial(intp, +- p_init->path1_ts_clock, +- p_init->path2_ts_clock); +- else { +- for (i = 0; intp->ts_config[i].addr != 0xffff; i++) +- stv0900_write_reg(intp, +- intp->ts_config[i].addr, +- intp->ts_config[i].val); +- +- stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); +- stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); +- stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); +- stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); +- } +- +- intp->tuner_type[0] = p_init->tuner1_type; +- intp->tuner_type[1] = p_init->tuner2_type; +- /* tuner init */ +- switch (p_init->tuner1_type) { +- case 3: /*FE_AUTO_STB6100:*/ +- stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x3c); +- stv0900_write_reg(intp, R0900_P1_TNRCFG2, 0x86); +- stv0900_write_reg(intp, R0900_P1_TNRCFG3, 0x18); +- stv0900_write_reg(intp, R0900_P1_TNRXTAL, 27); /* 27MHz */ +- stv0900_write_reg(intp, R0900_P1_TNRSTEPS, 0x05); +- stv0900_write_reg(intp, R0900_P1_TNRGAIN, 0x17); +- stv0900_write_reg(intp, R0900_P1_TNRADJ, 0x1f); +- stv0900_write_reg(intp, R0900_P1_TNRCTL2, 0x0); +- stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 3); +- break; +- /* case FE_SW_TUNER: */ +- default: +- stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 6); +- break; +- } +- +- stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); +- switch (p_init->tuner1_adc) { +- case 1: +- stv0900_write_reg(intp, R0900_TSTTNR1, 0x26); +- break; +- default: +- break; +- } +- +- stv0900_write_reg(intp, R0900_P1_TNRLD, 1); /* hw tuner */ +- +- /* tuner init */ +- switch (p_init->tuner2_type) { +- case 3: /*FE_AUTO_STB6100:*/ +- stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x3c); +- stv0900_write_reg(intp, R0900_P2_TNRCFG2, 0x86); +- stv0900_write_reg(intp, R0900_P2_TNRCFG3, 0x18); +- stv0900_write_reg(intp, R0900_P2_TNRXTAL, 27); /* 27MHz */ +- stv0900_write_reg(intp, R0900_P2_TNRSTEPS, 0x05); +- stv0900_write_reg(intp, R0900_P2_TNRGAIN, 0x17); +- stv0900_write_reg(intp, R0900_P2_TNRADJ, 0x1f); +- stv0900_write_reg(intp, R0900_P2_TNRCTL2, 0x0); +- stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 3); +- break; +- /* case FE_SW_TUNER: */ +- default: +- stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 6); +- break; +- } +- +- stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); +- switch (p_init->tuner2_adc) { +- case 1: +- stv0900_write_reg(intp, R0900_TSTTNR3, 0x26); +- break; +- default: +- break; +- } +- +- stv0900_write_reg(intp, R0900_P2_TNRLD, 1); /* hw tuner */ +- +- stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); +- stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); +- stv0900_set_mclk(intp, 135000000); +- msleep(3); +- +- switch (intp->clkmode) { +- case 0: +- case 2: +- stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode); +- break; +- default: +- selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); +- stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci); +- break; +- } +- msleep(3); +- +- intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz); +- if (intp->errs) +- error = STV0900_I2C_ERROR; +- +- return error; +-} +- +-static int stv0900_status(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- enum fe_stv0900_search_state demod_state; +- int locked = FALSE; +- u8 tsbitrate0_val, tsbitrate1_val; +- s32 bitrate; +- +- demod_state = stv0900_get_bits(intp, HEADER_MODE); +- switch (demod_state) { +- case STV0900_SEARCH: +- case STV0900_PLH_DETECTED: +- default: +- locked = FALSE; +- break; +- case STV0900_DVBS2_FOUND: +- locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && +- stv0900_get_bits(intp, PKTDELIN_LOCK) && +- stv0900_get_bits(intp, TSFIFO_LINEOK); +- break; +- case STV0900_DVBS_FOUND: +- locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && +- stv0900_get_bits(intp, LOCKEDVIT) && +- stv0900_get_bits(intp, TSFIFO_LINEOK); +- break; +- } +- +- dprintk("%s: locked = %d\n", __func__, locked); +- +- if (stvdebug) { +- /* Print TS bitrate */ +- tsbitrate0_val = stv0900_read_reg(intp, TSBITRATE0); +- tsbitrate1_val = stv0900_read_reg(intp, TSBITRATE1); +- /* Formula Bit rate = Mclk * px_tsfifo_bitrate / 16384 */ +- bitrate = (stv0900_get_mclk_freq(intp, intp->quartz)/1000000) +- * (tsbitrate1_val << 8 | tsbitrate0_val); +- bitrate /= 16384; +- dprintk("TS bitrate = %d Mbit/sec \n", bitrate); +- }; +- +- return locked; +-} +- +-static enum dvbfe_search stv0900_search(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- +- struct stv0900_search_params p_search; +- struct stv0900_signal_info p_result = intp->result[demod]; +- +- enum fe_stv0900_error error = STV0900_NO_ERROR; +- +- dprintk("%s: ", __func__); +- +- if (!(INRANGE(100000, c->symbol_rate, 70000000))) +- return DVBFE_ALGO_SEARCH_FAILED; +- +- if (state->config->set_ts_params) +- state->config->set_ts_params(fe, 0); +- +- p_result.locked = FALSE; +- p_search.path = demod; +- p_search.frequency = c->frequency; +- p_search.symbol_rate = c->symbol_rate; +- p_search.search_range = 10000000; +- p_search.fec = STV0900_FEC_UNKNOWN; +- p_search.standard = STV0900_AUTO_SEARCH; +- p_search.iq_inversion = STV0900_IQ_AUTO; +- p_search.search_algo = STV0900_BLIND_SEARCH; +- /* Speeds up DVB-S searching */ +- if (c->delivery_system == SYS_DVBS) +- p_search.standard = STV0900_SEARCH_DVBS1; +- +- intp->srch_standard[demod] = p_search.standard; +- intp->symbol_rate[demod] = p_search.symbol_rate; +- intp->srch_range[demod] = p_search.search_range; +- intp->freq[demod] = p_search.frequency; +- intp->srch_algo[demod] = p_search.search_algo; +- intp->srch_iq_inv[demod] = p_search.iq_inversion; +- intp->fec[demod] = p_search.fec; +- if ((stv0900_algo(fe) == STV0900_RANGEOK) && +- (intp->errs == STV0900_NO_ERROR)) { +- p_result.locked = intp->result[demod].locked; +- p_result.standard = intp->result[demod].standard; +- p_result.frequency = intp->result[demod].frequency; +- p_result.symbol_rate = intp->result[demod].symbol_rate; +- p_result.fec = intp->result[demod].fec; +- p_result.modcode = intp->result[demod].modcode; +- p_result.pilot = intp->result[demod].pilot; +- p_result.frame_len = intp->result[demod].frame_len; +- p_result.spectrum = intp->result[demod].spectrum; +- p_result.rolloff = intp->result[demod].rolloff; +- p_result.modulation = intp->result[demod].modulation; +- } else { +- p_result.locked = FALSE; +- switch (intp->err[demod]) { +- case STV0900_I2C_ERROR: +- error = STV0900_I2C_ERROR; +- break; +- case STV0900_NO_ERROR: +- default: +- error = STV0900_SEARCH_FAILED; +- break; +- } +- } +- +- if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) { +- dprintk("Search Success\n"); +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } else { +- dprintk("Search Fail\n"); +- return DVBFE_ALGO_SEARCH_FAILED; +- } +- +-} +- +-static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- +- dprintk("%s: ", __func__); +- +- if ((stv0900_status(state->internal, state->demod)) == TRUE) { +- dprintk("DEMOD LOCK OK\n"); +- *status = FE_HAS_CARRIER +- | FE_HAS_VITERBI +- | FE_HAS_SYNC +- | FE_HAS_LOCK; +- if (state->config->set_lock_led) +- state->config->set_lock_led(fe, 1); +- } else { +- *status = 0; +- if (state->config->set_lock_led) +- state->config->set_lock_led(fe, 0); +- dprintk("DEMOD LOCK FAIL\n"); +- } +- +- return 0; +-} +- +-static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) +-{ +- +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- if (stop_ts == TRUE) +- stv0900_write_bits(intp, RST_HWARE, 1); +- else +- stv0900_write_bits(intp, RST_HWARE, 0); +- +- return 0; +-} +- +-static int stv0900_diseqc_init(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode); +- stv0900_write_bits(intp, DISEQC_RESET, 1); +- stv0900_write_bits(intp, DISEQC_RESET, 0); +- +- return 0; +-} +- +-static int stv0900_init(struct dvb_frontend *fe) +-{ +- dprintk("%s\n", __func__); +- +- stv0900_stop_ts(fe, 1); +- stv0900_diseqc_init(fe); +- +- return 0; +-} +- +-static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data, +- u32 NbData, enum fe_stv0900_demod_num demod) +-{ +- s32 i = 0; +- +- stv0900_write_bits(intp, DIS_PRECHARGE, 1); +- while (i < NbData) { +- while (stv0900_get_bits(intp, FIFO_FULL)) +- ;/* checkpatch complains */ +- stv0900_write_reg(intp, DISTXDATA, data[i]); +- i++; +- } +- +- stv0900_write_bits(intp, DIS_PRECHARGE, 0); +- i = 0; +- while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) { +- msleep(10); +- i++; +- } +- +- return 0; +-} +- +-static int stv0900_send_master_cmd(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *cmd) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- +- return stv0900_diseqc_send(state->internal, +- cmd->msg, +- cmd->msg_len, +- state->demod); +-} +- +-static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- u8 data; +- +- +- switch (burst) { +- case SEC_MINI_A: +- stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */ +- data = 0x00; +- stv0900_diseqc_send(intp, &data, 1, state->demod); +- break; +- case SEC_MINI_B: +- stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */ +- data = 0xff; +- stv0900_diseqc_send(intp, &data, 1, state->demod); +- break; +- } +- +- return 0; +-} +- +-static int stv0900_recv_slave_reply(struct dvb_frontend *fe, +- struct dvb_diseqc_slave_reply *reply) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- s32 i = 0; +- +- reply->msg_len = 0; +- +- while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) { +- msleep(10); +- i++; +- } +- +- if (stv0900_get_bits(intp, RX_END)) { +- reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR); +- +- for (i = 0; i < reply->msg_len; i++) +- reply->msg[i] = stv0900_read_reg(intp, DISRXDATA); +- } +- +- return 0; +-} +- +-static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off")); +- +- switch (toneoff) { +- case SEC_TONE_ON: +- /*Set the DiseqC mode to 22Khz _continues_ tone*/ +- stv0900_write_bits(intp, DISTX_MODE, 0); +- stv0900_write_bits(intp, DISEQC_RESET, 1); +- /*release DiseqC reset to enable the 22KHz tone*/ +- stv0900_write_bits(intp, DISEQC_RESET, 0); +- break; +- case SEC_TONE_OFF: +- /*return diseqc mode to config->diseqc_mode. +- Usually it's without _continues_ tone */ +- stv0900_write_bits(intp, DISTX_MODE, +- state->config->diseqc_mode); +- /*maintain the DiseqC reset to disable the 22KHz tone*/ +- stv0900_write_bits(intp, DISEQC_RESET, 1); +- stv0900_write_bits(intp, DISEQC_RESET, 0); +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void stv0900_release(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (state->config->set_lock_led) +- state->config->set_lock_led(fe, 0); +- +- if ((--(state->internal->dmds_used)) <= 0) { +- +- dprintk("%s: Actually removing\n", __func__); +- +- remove_inode(state->internal); +- kfree(state->internal); +- } +- +- kfree(state); +-} +- +-static int stv0900_sleep(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (state->config->set_lock_led) +- state->config->set_lock_led(fe, 0); +- +- return 0; +-} +- +-static int stv0900_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- struct stv0900_signal_info p_result = intp->result[demod]; +- +- p->frequency = p_result.locked ? p_result.frequency : 0; +- p->symbol_rate = p_result.locked ? p_result.symbol_rate : 0; +- return 0; +-} +- +-static struct dvb_frontend_ops stv0900_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, +- .info = { +- .name = "STV0900 frontend", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .symbol_rate_tolerance = 500, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | +- FE_CAN_FEC_7_8 | FE_CAN_QPSK | +- FE_CAN_2G_MODULATION | +- FE_CAN_FEC_AUTO +- }, +- .release = stv0900_release, +- .init = stv0900_init, +- .get_frontend = stv0900_get_frontend, +- .sleep = stv0900_sleep, +- .get_frontend_algo = stv0900_frontend_algo, +- .i2c_gate_ctrl = stv0900_i2c_gate_ctrl, +- .diseqc_send_master_cmd = stv0900_send_master_cmd, +- .diseqc_send_burst = stv0900_send_burst, +- .diseqc_recv_slave_reply = stv0900_recv_slave_reply, +- .set_tone = stv0900_set_tone, +- .search = stv0900_search, +- .read_status = stv0900_read_status, +- .read_ber = stv0900_read_ber, +- .read_signal_strength = stv0900_read_signal_strength, +- .read_snr = stv0900_read_snr, +- .read_ucblocks = stv0900_read_ucblocks, +-}; +- +-struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, +- struct i2c_adapter *i2c, +- int demod) +-{ +- struct stv0900_state *state = NULL; +- struct stv0900_init_params init_params; +- enum fe_stv0900_error err_stv0900; +- +- state = kzalloc(sizeof(struct stv0900_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->demod = demod; +- state->config = config; +- state->i2c_adap = i2c; +- +- memcpy(&state->frontend.ops, &stv0900_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- switch (demod) { +- case 0: +- case 1: +- init_params.dmd_ref_clk = config->xtal; +- init_params.demod_mode = config->demod_mode; +- init_params.rolloff = STV0900_35; +- init_params.path1_ts_clock = config->path1_mode; +- init_params.tun1_maddress = config->tun1_maddress; +- init_params.tun1_iq_inv = STV0900_IQ_NORMAL; +- init_params.tuner1_adc = config->tun1_adc; +- init_params.tuner1_type = config->tun1_type; +- init_params.path2_ts_clock = config->path2_mode; +- init_params.ts_config = config->ts_config_regs; +- init_params.tun2_maddress = config->tun2_maddress; +- init_params.tuner2_adc = config->tun2_adc; +- init_params.tuner2_type = config->tun2_type; +- init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; +- +- err_stv0900 = stv0900_init_internal(&state->frontend, +- &init_params); +- +- if (err_stv0900) +- goto error; +- +- break; +- default: +- goto error; +- break; +- } +- +- dprintk("%s: Attaching STV0900 demodulator(%d) \n", __func__, demod); +- return &state->frontend; +- +-error: +- dprintk("%s: Failed to attach STV0900 demodulator(%d) \n", +- __func__, demod); +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(stv0900_attach); +- +-MODULE_PARM_DESC(debug, "Set debug"); +- +-MODULE_AUTHOR("Igor M. Liplianin"); +-MODULE_DESCRIPTION("ST STV0900 frontend"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb/frontends/stv0900_init.h +deleted file mode 100644 +index b684df9..0000000 +--- a/drivers/media/dvb/frontends/stv0900_init.h ++++ /dev/null +@@ -1,584 +0,0 @@ +-/* +- * stv0900_init.h +- * +- * Driver for ST STV0900 satellite demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0900_INIT_H +-#define STV0900_INIT_H +- +-#include "stv0900_priv.h" +- +-/* DVBS2 C/N Look-Up table */ +-static const struct stv0900_table stv0900_s2_cn = { +- 55, +- { +- { -30, 13348 }, /*C/N=-3dB*/ +- { -20, 12640 }, /*C/N=-2dB*/ +- { -10, 11883 }, /*C/N=-1dB*/ +- { 0, 11101 }, /*C/N=-0dB*/ +- { 5, 10718 }, /*C/N=0.5dB*/ +- { 10, 10339 }, /*C/N=1.0dB*/ +- { 15, 9947 }, /*C/N=1.5dB*/ +- { 20, 9552 }, /*C/N=2.0dB*/ +- { 25, 9183 }, /*C/N=2.5dB*/ +- { 30, 8799 }, /*C/N=3.0dB*/ +- { 35, 8422 }, /*C/N=3.5dB*/ +- { 40, 8062 }, /*C/N=4.0dB*/ +- { 45, 7707 }, /*C/N=4.5dB*/ +- { 50, 7353 }, /*C/N=5.0dB*/ +- { 55, 7025 }, /*C/N=5.5dB*/ +- { 60, 6684 }, /*C/N=6.0dB*/ +- { 65, 6331 }, /*C/N=6.5dB*/ +- { 70, 6036 }, /*C/N=7.0dB*/ +- { 75, 5727 }, /*C/N=7.5dB*/ +- { 80, 5437 }, /*C/N=8.0dB*/ +- { 85, 5164 }, /*C/N=8.5dB*/ +- { 90, 4902 }, /*C/N=9.0dB*/ +- { 95, 4653 }, /*C/N=9.5dB*/ +- { 100, 4408 }, /*C/N=10.0dB*/ +- { 105, 4187 }, /*C/N=10.5dB*/ +- { 110, 3961 }, /*C/N=11.0dB*/ +- { 115, 3751 }, /*C/N=11.5dB*/ +- { 120, 3558 }, /*C/N=12.0dB*/ +- { 125, 3368 }, /*C/N=12.5dB*/ +- { 130, 3191 }, /*C/N=13.0dB*/ +- { 135, 3017 }, /*C/N=13.5dB*/ +- { 140, 2862 }, /*C/N=14.0dB*/ +- { 145, 2710 }, /*C/N=14.5dB*/ +- { 150, 2565 }, /*C/N=15.0dB*/ +- { 160, 2300 }, /*C/N=16.0dB*/ +- { 170, 2058 }, /*C/N=17.0dB*/ +- { 180, 1849 }, /*C/N=18.0dB*/ +- { 190, 1663 }, /*C/N=19.0dB*/ +- { 200, 1495 }, /*C/N=20.0dB*/ +- { 210, 1349 }, /*C/N=21.0dB*/ +- { 220, 1222 }, /*C/N=22.0dB*/ +- { 230, 1110 }, /*C/N=23.0dB*/ +- { 240, 1011 }, /*C/N=24.0dB*/ +- { 250, 925 }, /*C/N=25.0dB*/ +- { 260, 853 }, /*C/N=26.0dB*/ +- { 270, 789 }, /*C/N=27.0dB*/ +- { 280, 734 }, /*C/N=28.0dB*/ +- { 290, 690 }, /*C/N=29.0dB*/ +- { 300, 650 }, /*C/N=30.0dB*/ +- { 310, 619 }, /*C/N=31.0dB*/ +- { 320, 593 }, /*C/N=32.0dB*/ +- { 330, 571 }, /*C/N=33.0dB*/ +- { 400, 498 }, /*C/N=40.0dB*/ +- { 450, 484 }, /*C/N=45.0dB*/ +- { 500, 481 } /*C/N=50.0dB*/ +- } +-}; +- +-/* RF level C/N Look-Up table */ +-static const struct stv0900_table stv0900_rf = { +- 14, +- { +- { -5, 0xCAA1 }, /*-5dBm*/ +- { -10, 0xC229 }, /*-10dBm*/ +- { -15, 0xBB08 }, /*-15dBm*/ +- { -20, 0xB4BC }, /*-20dBm*/ +- { -25, 0xAD5A }, /*-25dBm*/ +- { -30, 0xA298 }, /*-30dBm*/ +- { -35, 0x98A8 }, /*-35dBm*/ +- { -40, 0x8389 }, /*-40dBm*/ +- { -45, 0x59BE }, /*-45dBm*/ +- { -50, 0x3A14 }, /*-50dBm*/ +- { -55, 0x2D11 }, /*-55dBm*/ +- { -60, 0x210D }, /*-60dBm*/ +- { -65, 0xA14F }, /*-65dBm*/ +- { -70, 0x7AA } /*-70dBm*/ +- } +-}; +- +-struct stv0900_car_loop_optim { +- enum fe_stv0900_modcode modcode; +- u8 car_loop_pilots_on_2; +- u8 car_loop_pilots_off_2; +- u8 car_loop_pilots_on_5; +- u8 car_loop_pilots_off_5; +- u8 car_loop_pilots_on_10; +- u8 car_loop_pilots_off_10; +- u8 car_loop_pilots_on_20; +- u8 car_loop_pilots_off_20; +- u8 car_loop_pilots_on_30; +- u8 car_loop_pilots_off_30; +- +-}; +- +-struct stv0900_short_frames_car_loop_optim { +- enum fe_stv0900_modulation modulation; +- u8 car_loop_cut12_2; /* Cut 1.2, SR<=3msps */ +- u8 car_loop_cut20_2; /* Cut 2.0, SR<3msps */ +- u8 car_loop_cut12_5; /* Cut 1.2, 3 +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0900_PRIV_H +-#define STV0900_PRIV_H +- +-#include +- +-#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X)) +-#define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \ +- || (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0) +- +-#ifndef MAKEWORD +-#define MAKEWORD(X, Y) (((X) << 8) + (Y)) +-#endif +- +-#define LSB(X) (((X) & 0xFF)) +-#define MSB(Y) (((Y) >> 8) & 0xFF) +- +-#ifndef TRUE +-#define TRUE (1 == 1) +-#endif +-#ifndef FALSE +-#define FALSE (!TRUE) +-#endif +- +-#define dprintk(args...) \ +- do { \ +- if (stvdebug) \ +- printk(KERN_DEBUG args); \ +- } while (0) +- +-#define STV0900_MAXLOOKUPSIZE 500 +-#define STV0900_BLIND_SEARCH_AGC2_TH 700 +-#define STV0900_BLIND_SEARCH_AGC2_TH_CUT30 1400 +-#define IQPOWER_THRESHOLD 30 +- +-/* One point of the lookup table */ +-struct stv000_lookpoint { +- s32 realval;/* real value */ +- s32 regval;/* binary value */ +-}; +- +-/* Lookup table definition */ +-struct stv0900_table{ +- s32 size;/* Size of the lookup table */ +- struct stv000_lookpoint table[STV0900_MAXLOOKUPSIZE];/* Lookup table */ +-}; +- +-enum fe_stv0900_error { +- STV0900_NO_ERROR = 0, +- STV0900_INVALID_HANDLE, +- STV0900_BAD_PARAMETER, +- STV0900_I2C_ERROR, +- STV0900_SEARCH_FAILED, +-}; +- +-enum fe_stv0900_clock_type { +- STV0900_USE_REGISTERS_DEFAULT, +- STV0900_SERIAL_PUNCT_CLOCK,/*Serial punctured clock */ +- STV0900_SERIAL_CONT_CLOCK,/*Serial continues clock */ +- STV0900_PARALLEL_PUNCT_CLOCK,/*Parallel punctured clock */ +- STV0900_DVBCI_CLOCK/*Parallel continues clock : DVBCI */ +-}; +- +-enum fe_stv0900_search_state { +- STV0900_SEARCH = 0, +- STV0900_PLH_DETECTED, +- STV0900_DVBS2_FOUND, +- STV0900_DVBS_FOUND +- +-}; +- +-enum fe_stv0900_ldpc_state { +- STV0900_PATH1_OFF_PATH2_OFF = 0, +- STV0900_PATH1_ON_PATH2_OFF = 1, +- STV0900_PATH1_OFF_PATH2_ON = 2, +- STV0900_PATH1_ON_PATH2_ON = 3 +-}; +- +-enum fe_stv0900_signal_type { +- STV0900_NOAGC1 = 0, +- STV0900_AGC1OK, +- STV0900_NOTIMING, +- STV0900_ANALOGCARRIER, +- STV0900_TIMINGOK, +- STV0900_NOAGC2, +- STV0900_AGC2OK, +- STV0900_NOCARRIER, +- STV0900_CARRIEROK, +- STV0900_NODATA, +- STV0900_DATAOK, +- STV0900_OUTOFRANGE, +- STV0900_RANGEOK +-}; +- +-enum fe_stv0900_demod_num { +- STV0900_DEMOD_1, +- STV0900_DEMOD_2 +-}; +- +-enum fe_stv0900_tracking_standard { +- STV0900_DVBS1_STANDARD,/* Found Standard*/ +- STV0900_DVBS2_STANDARD, +- STV0900_DSS_STANDARD, +- STV0900_TURBOCODE_STANDARD, +- STV0900_UNKNOWN_STANDARD +-}; +- +-enum fe_stv0900_search_standard { +- STV0900_AUTO_SEARCH, +- STV0900_SEARCH_DVBS1,/* Search Standard*/ +- STV0900_SEARCH_DVBS2, +- STV0900_SEARCH_DSS, +- STV0900_SEARCH_TURBOCODE +-}; +- +-enum fe_stv0900_search_algo { +- STV0900_BLIND_SEARCH,/* offset freq and SR are Unknown */ +- STV0900_COLD_START,/* only the SR is known */ +- STV0900_WARM_START/* offset freq and SR are known */ +-}; +- +-enum fe_stv0900_modulation { +- STV0900_QPSK, +- STV0900_8PSK, +- STV0900_16APSK, +- STV0900_32APSK, +- STV0900_UNKNOWN +-}; +- +-enum fe_stv0900_modcode { +- STV0900_DUMMY_PLF, +- STV0900_QPSK_14, +- STV0900_QPSK_13, +- STV0900_QPSK_25, +- STV0900_QPSK_12, +- STV0900_QPSK_35, +- STV0900_QPSK_23, +- STV0900_QPSK_34, +- STV0900_QPSK_45, +- STV0900_QPSK_56, +- STV0900_QPSK_89, +- STV0900_QPSK_910, +- STV0900_8PSK_35, +- STV0900_8PSK_23, +- STV0900_8PSK_34, +- STV0900_8PSK_56, +- STV0900_8PSK_89, +- STV0900_8PSK_910, +- STV0900_16APSK_23, +- STV0900_16APSK_34, +- STV0900_16APSK_45, +- STV0900_16APSK_56, +- STV0900_16APSK_89, +- STV0900_16APSK_910, +- STV0900_32APSK_34, +- STV0900_32APSK_45, +- STV0900_32APSK_56, +- STV0900_32APSK_89, +- STV0900_32APSK_910, +- STV0900_MODCODE_UNKNOWN +-}; +- +-enum fe_stv0900_fec {/*DVBS1, DSS and turbo code puncture rate*/ +- STV0900_FEC_1_2 = 0, +- STV0900_FEC_2_3, +- STV0900_FEC_3_4, +- STV0900_FEC_4_5,/*for turbo code only*/ +- STV0900_FEC_5_6, +- STV0900_FEC_6_7,/*for DSS only */ +- STV0900_FEC_7_8, +- STV0900_FEC_8_9,/*for turbo code only*/ +- STV0900_FEC_UNKNOWN +-}; +- +-enum fe_stv0900_frame_length { +- STV0900_LONG_FRAME, +- STV0900_SHORT_FRAME +-}; +- +-enum fe_stv0900_pilot { +- STV0900_PILOTS_OFF, +- STV0900_PILOTS_ON +-}; +- +-enum fe_stv0900_rolloff { +- STV0900_35, +- STV0900_25, +- STV0900_20 +-}; +- +-enum fe_stv0900_search_iq { +- STV0900_IQ_AUTO, +- STV0900_IQ_AUTO_NORMAL_FIRST, +- STV0900_IQ_FORCE_NORMAL, +- STV0900_IQ_FORCE_SWAPPED +-}; +- +-enum stv0900_iq_inversion { +- STV0900_IQ_NORMAL, +- STV0900_IQ_SWAPPED +-}; +- +-enum fe_stv0900_diseqc_mode { +- STV0900_22KHZ_Continues = 0, +- STV0900_DISEQC_2_3_PWM = 2, +- STV0900_DISEQC_3_3_PWM = 3, +- STV0900_DISEQC_2_3_ENVELOP = 4, +- STV0900_DISEQC_3_3_ENVELOP = 5 +-}; +- +-enum fe_stv0900_demod_mode { +- STV0900_SINGLE = 0, +- STV0900_DUAL +-}; +- +-struct stv0900_init_params{ +- u32 dmd_ref_clk;/* Reference,Input clock for the demod in Hz */ +- +- /* Demodulator Type (single demod or dual demod) */ +- enum fe_stv0900_demod_mode demod_mode; +- enum fe_stv0900_rolloff rolloff; +- enum fe_stv0900_clock_type path1_ts_clock; +- +- u8 tun1_maddress; +- int tuner1_adc; +- int tuner1_type; +- +- /* IQ from the tuner1 to the demod */ +- enum stv0900_iq_inversion tun1_iq_inv; +- enum fe_stv0900_clock_type path2_ts_clock; +- +- u8 tun2_maddress; +- int tuner2_adc; +- int tuner2_type; +- +- /* IQ from the tuner2 to the demod */ +- enum stv0900_iq_inversion tun2_iq_inv; +- struct stv0900_reg *ts_config; +-}; +- +-struct stv0900_search_params { +- enum fe_stv0900_demod_num path;/* Path Used demod1 or 2 */ +- +- u32 frequency;/* Transponder frequency (in KHz) */ +- u32 symbol_rate;/* Transponder symbol rate (in bds)*/ +- u32 search_range;/* Range of the search (in Hz) */ +- +- enum fe_stv0900_search_standard standard; +- enum fe_stv0900_modulation modulation; +- enum fe_stv0900_fec fec; +- enum fe_stv0900_modcode modcode; +- enum fe_stv0900_search_iq iq_inversion; +- enum fe_stv0900_search_algo search_algo; +- +-}; +- +-struct stv0900_signal_info { +- int locked;/* Transponder locked */ +- u32 frequency;/* Transponder frequency (in KHz) */ +- u32 symbol_rate;/* Transponder symbol rate (in Mbds) */ +- +- enum fe_stv0900_tracking_standard standard; +- enum fe_stv0900_fec fec; +- enum fe_stv0900_modcode modcode; +- enum fe_stv0900_modulation modulation; +- enum fe_stv0900_pilot pilot; +- enum fe_stv0900_frame_length frame_len; +- enum stv0900_iq_inversion spectrum; +- enum fe_stv0900_rolloff rolloff; +- +- s32 Power;/* Power of the RF signal (dBm) */ +- s32 C_N;/* Carrier to noise ratio (dB x10)*/ +- u32 BER;/* Bit error rate (x10^7) */ +- +-}; +- +-struct stv0900_internal{ +- s32 quartz; +- s32 mclk; +- /* manual RollOff for DVBS1/DSS only */ +- enum fe_stv0900_rolloff rolloff; +- /* Demodulator use for single demod or for dual demod) */ +- enum fe_stv0900_demod_mode demod_mode; +- +- /*Demods */ +- s32 freq[2]; +- s32 bw[2]; +- s32 symbol_rate[2]; +- s32 srch_range[2]; +- /* for software/auto tuner */ +- int tuner_type[2]; +- +- /* algorithm for search Blind, Cold or Warm*/ +- enum fe_stv0900_search_algo srch_algo[2]; +- /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/ +- enum fe_stv0900_search_standard srch_standard[2]; +- /* inversion search : auto, auto norma first, normal or inverted */ +- enum fe_stv0900_search_iq srch_iq_inv[2]; +- enum fe_stv0900_modcode modcode[2]; +- enum fe_stv0900_modulation modulation[2]; +- enum fe_stv0900_fec fec[2]; +- +- struct stv0900_signal_info result[2]; +- enum fe_stv0900_error err[2]; +- +- +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +- u8 clkmode;/* 0 for CLKI, 2 for XTALI */ +- u8 chip_id; +- struct stv0900_reg *ts_config; +- enum fe_stv0900_error errs; +- int dmds_used; +-}; +- +-/* state for each demod */ +-struct stv0900_state { +- /* pointer for internal params, one for each pair of demods */ +- struct stv0900_internal *internal; +- struct i2c_adapter *i2c_adap; +- const struct stv0900_config *config; +- struct dvb_frontend frontend; +- int demod; +-}; +- +-extern int stvdebug; +- +-extern s32 ge2comp(s32 a, s32 width); +- +-extern void stv0900_write_reg(struct stv0900_internal *i_params, +- u16 reg_addr, u8 reg_data); +- +-extern u8 stv0900_read_reg(struct stv0900_internal *i_params, +- u16 reg_addr); +- +-extern void stv0900_write_bits(struct stv0900_internal *i_params, +- u32 label, u8 val); +- +-extern u8 stv0900_get_bits(struct stv0900_internal *i_params, +- u32 label); +- +-extern int stv0900_get_demod_lock(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod, s32 time_out); +-extern int stv0900_check_signal_presence(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod); +- +-extern enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe); +- +-extern void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, +- u32 bandwidth); +-extern void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth); +- +-extern void stv0900_start_search(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod); +- +-extern u8 stv0900_get_optim_carr_loop(s32 srate, +- enum fe_stv0900_modcode modcode, +- s32 pilot, u8 chip_id); +- +-extern u8 stv0900_get_optim_short_carr_loop(s32 srate, +- enum fe_stv0900_modulation modulation, +- u8 chip_id); +- +-extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod); +- +-extern void stv0900_activate_s2_modcod(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod); +- +-extern void stv0900_activate_s2_modcod_single(struct stv0900_internal *i_params, +- enum fe_stv0900_demod_num demod); +- +-extern enum +-fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, +- enum fe_stv0900_demod_num demod); +- +-extern u32 +-stv0900_get_freq_auto(struct stv0900_internal *intp, int demod); +- +-extern void +-stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, +- u32 Bandwidth, int demod); +- +-#endif +diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h +deleted file mode 100644 +index 731afe9..0000000 +--- a/drivers/media/dvb/frontends/stv0900_reg.h ++++ /dev/null +@@ -1,3981 +0,0 @@ +-/* +- * stv0900_reg.h +- * +- * Driver for ST STV0900 satellite demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef STV0900_REG_H +-#define STV0900_REG_H +- +-extern s32 shiftx(s32 x, int demod, s32 shift); +- +-#define REGx(x) shiftx(x, demod, 0x200) +-#define FLDx(x) shiftx(x, demod, 0x2000000) +- +-/*MID*/ +-#define R0900_MID 0xf100 +-#define F0900_MCHIP_IDENT 0xf10000f0 +-#define F0900_MRELEASE 0xf100000f +- +-/*DACR1*/ +-#define R0900_DACR1 0xf113 +-#define F0900_DAC_MODE 0xf11300e0 +-#define F0900_DAC_VALUE1 0xf113000f +- +-/*DACR2*/ +-#define R0900_DACR2 0xf114 +-#define F0900_DAC_VALUE0 0xf11400ff +- +-/*OUTCFG*/ +-#define R0900_OUTCFG 0xf11c +-#define F0900_OUTSERRS1_HZ 0xf11c0040 +-#define F0900_OUTSERRS2_HZ 0xf11c0020 +-#define F0900_OUTSERRS3_HZ 0xf11c0010 +-#define F0900_OUTPARRS3_HZ 0xf11c0008 +- +-/*IRQSTATUS3*/ +-#define R0900_IRQSTATUS3 0xf120 +-#define F0900_SPLL_LOCK 0xf1200020 +-#define F0900_SSTREAM_LCK_3 0xf1200010 +-#define F0900_SSTREAM_LCK_2 0xf1200008 +-#define F0900_SSTREAM_LCK_1 0xf1200004 +-#define F0900_SDVBS1_PRF_2 0xf1200002 +-#define F0900_SDVBS1_PRF_1 0xf1200001 +- +-/*IRQSTATUS2*/ +-#define R0900_IRQSTATUS2 0xf121 +-#define F0900_SSPY_ENDSIM_3 0xf1210080 +-#define F0900_SSPY_ENDSIM_2 0xf1210040 +-#define F0900_SSPY_ENDSIM_1 0xf1210020 +-#define F0900_SPKTDEL_ERROR_2 0xf1210010 +-#define F0900_SPKTDEL_LOCKB_2 0xf1210008 +-#define F0900_SPKTDEL_LOCK_2 0xf1210004 +-#define F0900_SPKTDEL_ERROR_1 0xf1210002 +-#define F0900_SPKTDEL_LOCKB_1 0xf1210001 +- +-/*IRQSTATUS1*/ +-#define R0900_IRQSTATUS1 0xf122 +-#define F0900_SPKTDEL_LOCK_1 0xf1220080 +-#define F0900_SDEMOD_LOCKB_2 0xf1220004 +-#define F0900_SDEMOD_LOCK_2 0xf1220002 +-#define F0900_SDEMOD_IRQ_2 0xf1220001 +- +-/*IRQSTATUS0*/ +-#define R0900_IRQSTATUS0 0xf123 +-#define F0900_SDEMOD_LOCKB_1 0xf1230080 +-#define F0900_SDEMOD_LOCK_1 0xf1230040 +-#define F0900_SDEMOD_IRQ_1 0xf1230020 +-#define F0900_SBCH_ERRFLAG 0xf1230010 +-#define F0900_SDISEQC2RX_IRQ 0xf1230008 +-#define F0900_SDISEQC2TX_IRQ 0xf1230004 +-#define F0900_SDISEQC1RX_IRQ 0xf1230002 +-#define F0900_SDISEQC1TX_IRQ 0xf1230001 +- +-/*IRQMASK3*/ +-#define R0900_IRQMASK3 0xf124 +-#define F0900_MPLL_LOCK 0xf1240020 +-#define F0900_MSTREAM_LCK_3 0xf1240010 +-#define F0900_MSTREAM_LCK_2 0xf1240008 +-#define F0900_MSTREAM_LCK_1 0xf1240004 +-#define F0900_MDVBS1_PRF_2 0xf1240002 +-#define F0900_MDVBS1_PRF_1 0xf1240001 +- +-/*IRQMASK2*/ +-#define R0900_IRQMASK2 0xf125 +-#define F0900_MSPY_ENDSIM_3 0xf1250080 +-#define F0900_MSPY_ENDSIM_2 0xf1250040 +-#define F0900_MSPY_ENDSIM_1 0xf1250020 +-#define F0900_MPKTDEL_ERROR_2 0xf1250010 +-#define F0900_MPKTDEL_LOCKB_2 0xf1250008 +-#define F0900_MPKTDEL_LOCK_2 0xf1250004 +-#define F0900_MPKTDEL_ERROR_1 0xf1250002 +-#define F0900_MPKTDEL_LOCKB_1 0xf1250001 +- +-/*IRQMASK1*/ +-#define R0900_IRQMASK1 0xf126 +-#define F0900_MPKTDEL_LOCK_1 0xf1260080 +-#define F0900_MEXTPINB2 0xf1260040 +-#define F0900_MEXTPIN2 0xf1260020 +-#define F0900_MEXTPINB1 0xf1260010 +-#define F0900_MEXTPIN1 0xf1260008 +-#define F0900_MDEMOD_LOCKB_2 0xf1260004 +-#define F0900_MDEMOD_LOCK_2 0xf1260002 +-#define F0900_MDEMOD_IRQ_2 0xf1260001 +- +-/*IRQMASK0*/ +-#define R0900_IRQMASK0 0xf127 +-#define F0900_MDEMOD_LOCKB_1 0xf1270080 +-#define F0900_MDEMOD_LOCK_1 0xf1270040 +-#define F0900_MDEMOD_IRQ_1 0xf1270020 +-#define F0900_MBCH_ERRFLAG 0xf1270010 +-#define F0900_MDISEQC2RX_IRQ 0xf1270008 +-#define F0900_MDISEQC2TX_IRQ 0xf1270004 +-#define F0900_MDISEQC1RX_IRQ 0xf1270002 +-#define F0900_MDISEQC1TX_IRQ 0xf1270001 +- +-/*I2CCFG*/ +-#define R0900_I2CCFG 0xf129 +-#define F0900_I2C_FASTMODE 0xf1290008 +-#define F0900_I2CADDR_INC 0xf1290003 +- +-/*P1_I2CRPT*/ +-#define R0900_P1_I2CRPT 0xf12a +-#define I2CRPT shiftx(R0900_P1_I2CRPT, demod, -1) +-#define F0900_P1_I2CT_ON 0xf12a0080 +-#define I2CT_ON shiftx(F0900_P1_I2CT_ON, demod, -0x10000) +-#define F0900_P1_ENARPT_LEVEL 0xf12a0070 +-#define F0900_P1_SCLT_DELAY 0xf12a0008 +-#define F0900_P1_STOP_ENABLE 0xf12a0004 +-#define F0900_P1_STOP_SDAT2SDA 0xf12a0002 +- +-/*P2_I2CRPT*/ +-#define R0900_P2_I2CRPT 0xf12b +-#define F0900_P2_I2CT_ON 0xf12b0080 +-#define F0900_P2_ENARPT_LEVEL 0xf12b0070 +-#define F0900_P2_SCLT_DELAY 0xf12b0008 +-#define F0900_P2_STOP_ENABLE 0xf12b0004 +-#define F0900_P2_STOP_SDAT2SDA 0xf12b0002 +- +-/*IOPVALUE6*/ +-#define R0900_IOPVALUE6 0xf138 +-#define F0900_VSCL 0xf1380004 +-#define F0900_VSDA 0xf1380002 +-#define F0900_VDATA3_0 0xf1380001 +- +-/*IOPVALUE5*/ +-#define R0900_IOPVALUE5 0xf139 +-#define F0900_VDATA3_1 0xf1390080 +-#define F0900_VDATA3_2 0xf1390040 +-#define F0900_VDATA3_3 0xf1390020 +-#define F0900_VDATA3_4 0xf1390010 +-#define F0900_VDATA3_5 0xf1390008 +-#define F0900_VDATA3_6 0xf1390004 +-#define F0900_VDATA3_7 0xf1390002 +-#define F0900_VCLKOUT3 0xf1390001 +- +-/*IOPVALUE4*/ +-#define R0900_IOPVALUE4 0xf13a +-#define F0900_VSTROUT3 0xf13a0080 +-#define F0900_VDPN3 0xf13a0040 +-#define F0900_VERROR3 0xf13a0020 +-#define F0900_VDATA2_7 0xf13a0010 +-#define F0900_VCLKOUT2 0xf13a0008 +-#define F0900_VSTROUT2 0xf13a0004 +-#define F0900_VDPN2 0xf13a0002 +-#define F0900_VERROR2 0xf13a0001 +- +-/*IOPVALUE3*/ +-#define R0900_IOPVALUE3 0xf13b +-#define F0900_VDATA1_7 0xf13b0080 +-#define F0900_VCLKOUT1 0xf13b0040 +-#define F0900_VSTROUT1 0xf13b0020 +-#define F0900_VDPN1 0xf13b0010 +-#define F0900_VERROR1 0xf13b0008 +-#define F0900_VCLKOUT27 0xf13b0004 +-#define F0900_VDISEQCOUT2 0xf13b0002 +-#define F0900_VSCLT2 0xf13b0001 +- +-/*IOPVALUE2*/ +-#define R0900_IOPVALUE2 0xf13c +-#define F0900_VSDAT2 0xf13c0080 +-#define F0900_VAGCRF2 0xf13c0040 +-#define F0900_VDISEQCOUT1 0xf13c0020 +-#define F0900_VSCLT1 0xf13c0010 +-#define F0900_VSDAT1 0xf13c0008 +-#define F0900_VAGCRF1 0xf13c0004 +-#define F0900_VDIRCLK 0xf13c0002 +-#define F0900_VSTDBY 0xf13c0001 +- +-/*IOPVALUE1*/ +-#define R0900_IOPVALUE1 0xf13d +-#define F0900_VCS1 0xf13d0080 +-#define F0900_VCS0 0xf13d0040 +-#define F0900_VGPIO13 0xf13d0020 +-#define F0900_VGPIO12 0xf13d0010 +-#define F0900_VGPIO11 0xf13d0008 +-#define F0900_VGPIO10 0xf13d0004 +-#define F0900_VGPIO9 0xf13d0002 +-#define F0900_VGPIO8 0xf13d0001 +- +-/*IOPVALUE0*/ +-#define R0900_IOPVALUE0 0xf13e +-#define F0900_VGPIO7 0xf13e0080 +-#define F0900_VGPIO6 0xf13e0040 +-#define F0900_VGPIO5 0xf13e0020 +-#define F0900_VGPIO4 0xf13e0010 +-#define F0900_VGPIO3 0xf13e0008 +-#define F0900_VGPIO2 0xf13e0004 +-#define F0900_VGPIO1 0xf13e0002 +-#define F0900_VCLKI2 0xf13e0001 +- +-/*CLKI2CFG*/ +-#define R0900_CLKI2CFG 0xf140 +-#define F0900_CLKI2_OPD 0xf1400080 +-#define F0900_CLKI2_CONFIG 0xf140007e +-#define F0900_CLKI2_XOR 0xf1400001 +- +-/*GPIO1CFG*/ +-#define R0900_GPIO1CFG 0xf141 +-#define F0900_GPIO1_OPD 0xf1410080 +-#define F0900_GPIO1_CONFIG 0xf141007e +-#define F0900_GPIO1_XOR 0xf1410001 +- +-/*GPIO2CFG*/ +-#define R0900_GPIO2CFG 0xf142 +-#define F0900_GPIO2_OPD 0xf1420080 +-#define F0900_GPIO2_CONFIG 0xf142007e +-#define F0900_GPIO2_XOR 0xf1420001 +- +-/*GPIO3CFG*/ +-#define R0900_GPIO3CFG 0xf143 +-#define F0900_GPIO3_OPD 0xf1430080 +-#define F0900_GPIO3_CONFIG 0xf143007e +-#define F0900_GPIO3_XOR 0xf1430001 +- +-/*GPIO4CFG*/ +-#define R0900_GPIO4CFG 0xf144 +-#define F0900_GPIO4_OPD 0xf1440080 +-#define F0900_GPIO4_CONFIG 0xf144007e +-#define F0900_GPIO4_XOR 0xf1440001 +- +-/*GPIO5CFG*/ +-#define R0900_GPIO5CFG 0xf145 +-#define F0900_GPIO5_OPD 0xf1450080 +-#define F0900_GPIO5_CONFIG 0xf145007e +-#define F0900_GPIO5_XOR 0xf1450001 +- +-/*GPIO6CFG*/ +-#define R0900_GPIO6CFG 0xf146 +-#define F0900_GPIO6_OPD 0xf1460080 +-#define F0900_GPIO6_CONFIG 0xf146007e +-#define F0900_GPIO6_XOR 0xf1460001 +- +-/*GPIO7CFG*/ +-#define R0900_GPIO7CFG 0xf147 +-#define F0900_GPIO7_OPD 0xf1470080 +-#define F0900_GPIO7_CONFIG 0xf147007e +-#define F0900_GPIO7_XOR 0xf1470001 +- +-/*GPIO8CFG*/ +-#define R0900_GPIO8CFG 0xf148 +-#define F0900_GPIO8_OPD 0xf1480080 +-#define F0900_GPIO8_CONFIG 0xf148007e +-#define F0900_GPIO8_XOR 0xf1480001 +- +-/*GPIO9CFG*/ +-#define R0900_GPIO9CFG 0xf149 +-#define F0900_GPIO9_OPD 0xf1490080 +-#define F0900_GPIO9_CONFIG 0xf149007e +-#define F0900_GPIO9_XOR 0xf1490001 +- +-/*GPIO10CFG*/ +-#define R0900_GPIO10CFG 0xf14a +-#define F0900_GPIO10_OPD 0xf14a0080 +-#define F0900_GPIO10_CONFIG 0xf14a007e +-#define F0900_GPIO10_XOR 0xf14a0001 +- +-/*GPIO11CFG*/ +-#define R0900_GPIO11CFG 0xf14b +-#define F0900_GPIO11_OPD 0xf14b0080 +-#define F0900_GPIO11_CONFIG 0xf14b007e +-#define F0900_GPIO11_XOR 0xf14b0001 +- +-/*GPIO12CFG*/ +-#define R0900_GPIO12CFG 0xf14c +-#define F0900_GPIO12_OPD 0xf14c0080 +-#define F0900_GPIO12_CONFIG 0xf14c007e +-#define F0900_GPIO12_XOR 0xf14c0001 +- +-/*GPIO13CFG*/ +-#define R0900_GPIO13CFG 0xf14d +-#define F0900_GPIO13_OPD 0xf14d0080 +-#define F0900_GPIO13_CONFIG 0xf14d007e +-#define F0900_GPIO13_XOR 0xf14d0001 +- +-/*CS0CFG*/ +-#define R0900_CS0CFG 0xf14e +-#define F0900_CS0_OPD 0xf14e0080 +-#define F0900_CS0_CONFIG 0xf14e007e +-#define F0900_CS0_XOR 0xf14e0001 +- +-/*CS1CFG*/ +-#define R0900_CS1CFG 0xf14f +-#define F0900_CS1_OPD 0xf14f0080 +-#define F0900_CS1_CONFIG 0xf14f007e +-#define F0900_CS1_XOR 0xf14f0001 +- +-/*STDBYCFG*/ +-#define R0900_STDBYCFG 0xf150 +-#define F0900_STDBY_OPD 0xf1500080 +-#define F0900_STDBY_CONFIG 0xf150007e +-#define F0900_STBDY_XOR 0xf1500001 +- +-/*DIRCLKCFG*/ +-#define R0900_DIRCLKCFG 0xf151 +-#define F0900_DIRCLK_OPD 0xf1510080 +-#define F0900_DIRCLK_CONFIG 0xf151007e +-#define F0900_DIRCLK_XOR 0xf1510001 +- +-/*AGCRF1CFG*/ +-#define R0900_AGCRF1CFG 0xf152 +-#define F0900_AGCRF1_OPD 0xf1520080 +-#define F0900_AGCRF1_CONFIG 0xf152007e +-#define F0900_AGCRF1_XOR 0xf1520001 +- +-/*SDAT1CFG*/ +-#define R0900_SDAT1CFG 0xf153 +-#define F0900_SDAT1_OPD 0xf1530080 +-#define F0900_SDAT1_CONFIG 0xf153007e +-#define F0900_SDAT1_XOR 0xf1530001 +- +-/*SCLT1CFG*/ +-#define R0900_SCLT1CFG 0xf154 +-#define F0900_SCLT1_OPD 0xf1540080 +-#define F0900_SCLT1_CONFIG 0xf154007e +-#define F0900_SCLT1_XOR 0xf1540001 +- +-/*DISEQCO1CFG*/ +-#define R0900_DISEQCO1CFG 0xf155 +-#define F0900_DISEQCO1_OPD 0xf1550080 +-#define F0900_DISEQCO1_CONFIG 0xf155007e +-#define F0900_DISEQC1_XOR 0xf1550001 +- +-/*AGCRF2CFG*/ +-#define R0900_AGCRF2CFG 0xf156 +-#define F0900_AGCRF2_OPD 0xf1560080 +-#define F0900_AGCRF2_CONFIG 0xf156007e +-#define F0900_AGCRF2_XOR 0xf1560001 +- +-/*SDAT2CFG*/ +-#define R0900_SDAT2CFG 0xf157 +-#define F0900_SDAT2_OPD 0xf1570080 +-#define F0900_SDAT2_CONFIG 0xf157007e +-#define F0900_SDAT2_XOR 0xf1570001 +- +-/*SCLT2CFG*/ +-#define R0900_SCLT2CFG 0xf158 +-#define F0900_SCLT2_OPD 0xf1580080 +-#define F0900_SCLT2_CONFIG 0xf158007e +-#define F0900_SCLT2_XOR 0xf1580001 +- +-/*DISEQCO2CFG*/ +-#define R0900_DISEQCO2CFG 0xf159 +-#define F0900_DISEQCO2_OPD 0xf1590080 +-#define F0900_DISEQCO2_CONFIG 0xf159007e +-#define F0900_DISEQC2_XOR 0xf1590001 +- +-/*CLKOUT27CFG*/ +-#define R0900_CLKOUT27CFG 0xf15a +-#define F0900_CLKOUT27_OPD 0xf15a0080 +-#define F0900_CLKOUT27_CONFIG 0xf15a007e +-#define F0900_CLKOUT27_XOR 0xf15a0001 +- +-/*ERROR1CFG*/ +-#define R0900_ERROR1CFG 0xf15b +-#define F0900_ERROR1_OPD 0xf15b0080 +-#define F0900_ERROR1_CONFIG 0xf15b007e +-#define F0900_ERROR1_XOR 0xf15b0001 +- +-/*DPN1CFG*/ +-#define R0900_DPN1CFG 0xf15c +-#define F0900_DPN1_OPD 0xf15c0080 +-#define F0900_DPN1_CONFIG 0xf15c007e +-#define F0900_DPN1_XOR 0xf15c0001 +- +-/*STROUT1CFG*/ +-#define R0900_STROUT1CFG 0xf15d +-#define F0900_STROUT1_OPD 0xf15d0080 +-#define F0900_STROUT1_CONFIG 0xf15d007e +-#define F0900_STROUT1_XOR 0xf15d0001 +- +-/*CLKOUT1CFG*/ +-#define R0900_CLKOUT1CFG 0xf15e +-#define F0900_CLKOUT1_OPD 0xf15e0080 +-#define F0900_CLKOUT1_CONFIG 0xf15e007e +-#define F0900_CLKOUT1_XOR 0xf15e0001 +- +-/*DATA71CFG*/ +-#define R0900_DATA71CFG 0xf15f +-#define F0900_DATA71_OPD 0xf15f0080 +-#define F0900_DATA71_CONFIG 0xf15f007e +-#define F0900_DATA71_XOR 0xf15f0001 +- +-/*ERROR2CFG*/ +-#define R0900_ERROR2CFG 0xf160 +-#define F0900_ERROR2_OPD 0xf1600080 +-#define F0900_ERROR2_CONFIG 0xf160007e +-#define F0900_ERROR2_XOR 0xf1600001 +- +-/*DPN2CFG*/ +-#define R0900_DPN2CFG 0xf161 +-#define F0900_DPN2_OPD 0xf1610080 +-#define F0900_DPN2_CONFIG 0xf161007e +-#define F0900_DPN2_XOR 0xf1610001 +- +-/*STROUT2CFG*/ +-#define R0900_STROUT2CFG 0xf162 +-#define F0900_STROUT2_OPD 0xf1620080 +-#define F0900_STROUT2_CONFIG 0xf162007e +-#define F0900_STROUT2_XOR 0xf1620001 +- +-/*CLKOUT2CFG*/ +-#define R0900_CLKOUT2CFG 0xf163 +-#define F0900_CLKOUT2_OPD 0xf1630080 +-#define F0900_CLKOUT2_CONFIG 0xf163007e +-#define F0900_CLKOUT2_XOR 0xf1630001 +- +-/*DATA72CFG*/ +-#define R0900_DATA72CFG 0xf164 +-#define F0900_DATA72_OPD 0xf1640080 +-#define F0900_DATA72_CONFIG 0xf164007e +-#define F0900_DATA72_XOR 0xf1640001 +- +-/*ERROR3CFG*/ +-#define R0900_ERROR3CFG 0xf165 +-#define F0900_ERROR3_OPD 0xf1650080 +-#define F0900_ERROR3_CONFIG 0xf165007e +-#define F0900_ERROR3_XOR 0xf1650001 +- +-/*DPN3CFG*/ +-#define R0900_DPN3CFG 0xf166 +-#define F0900_DPN3_OPD 0xf1660080 +-#define F0900_DPN3_CONFIG 0xf166007e +-#define F0900_DPN3_XOR 0xf1660001 +- +-/*STROUT3CFG*/ +-#define R0900_STROUT3CFG 0xf167 +-#define F0900_STROUT3_OPD 0xf1670080 +-#define F0900_STROUT3_CONFIG 0xf167007e +-#define F0900_STROUT3_XOR 0xf1670001 +- +-/*CLKOUT3CFG*/ +-#define R0900_CLKOUT3CFG 0xf168 +-#define F0900_CLKOUT3_OPD 0xf1680080 +-#define F0900_CLKOUT3_CONFIG 0xf168007e +-#define F0900_CLKOUT3_XOR 0xf1680001 +- +-/*DATA73CFG*/ +-#define R0900_DATA73CFG 0xf169 +-#define F0900_DATA73_OPD 0xf1690080 +-#define F0900_DATA73_CONFIG 0xf169007e +-#define F0900_DATA73_XOR 0xf1690001 +- +-/*STRSTATUS1*/ +-#define R0900_STRSTATUS1 0xf16a +-#define F0900_STRSTATUS_SEL2 0xf16a00f0 +-#define F0900_STRSTATUS_SEL1 0xf16a000f +- +-/*STRSTATUS2*/ +-#define R0900_STRSTATUS2 0xf16b +-#define F0900_STRSTATUS_SEL4 0xf16b00f0 +-#define F0900_STRSTATUS_SEL3 0xf16b000f +- +-/*STRSTATUS3*/ +-#define R0900_STRSTATUS3 0xf16c +-#define F0900_STRSTATUS_SEL6 0xf16c00f0 +-#define F0900_STRSTATUS_SEL5 0xf16c000f +- +-/*FSKTFC2*/ +-#define R0900_FSKTFC2 0xf170 +-#define F0900_FSKT_KMOD 0xf17000fc +-#define F0900_FSKT_CAR2 0xf1700003 +- +-/*FSKTFC1*/ +-#define R0900_FSKTFC1 0xf171 +-#define F0900_FSKT_CAR1 0xf17100ff +- +-/*FSKTFC0*/ +-#define R0900_FSKTFC0 0xf172 +-#define F0900_FSKT_CAR0 0xf17200ff +- +-/*FSKTDELTAF1*/ +-#define R0900_FSKTDELTAF1 0xf173 +-#define F0900_FSKT_DELTAF1 0xf173000f +- +-/*FSKTDELTAF0*/ +-#define R0900_FSKTDELTAF0 0xf174 +-#define F0900_FSKT_DELTAF0 0xf17400ff +- +-/*FSKTCTRL*/ +-#define R0900_FSKTCTRL 0xf175 +-#define F0900_FSKT_EN_SGN 0xf1750040 +-#define F0900_FSKT_MOD_SGN 0xf1750020 +-#define F0900_FSKT_MOD_EN 0xf175001c +-#define F0900_FSKT_DACMODE 0xf1750003 +- +-/*FSKRFC2*/ +-#define R0900_FSKRFC2 0xf176 +-#define F0900_FSKR_DETSGN 0xf1760040 +-#define F0900_FSKR_OUTSGN 0xf1760020 +-#define F0900_FSKR_KAGC 0xf176001c +-#define F0900_FSKR_CAR2 0xf1760003 +- +-/*FSKRFC1*/ +-#define R0900_FSKRFC1 0xf177 +-#define F0900_FSKR_CAR1 0xf17700ff +- +-/*FSKRFC0*/ +-#define R0900_FSKRFC0 0xf178 +-#define F0900_FSKR_CAR0 0xf17800ff +- +-/*FSKRK1*/ +-#define R0900_FSKRK1 0xf179 +-#define F0900_FSKR_K1_EXP 0xf17900e0 +-#define F0900_FSKR_K1_MANT 0xf179001f +- +-/*FSKRK2*/ +-#define R0900_FSKRK2 0xf17a +-#define F0900_FSKR_K2_EXP 0xf17a00e0 +-#define F0900_FSKR_K2_MANT 0xf17a001f +- +-/*FSKRAGCR*/ +-#define R0900_FSKRAGCR 0xf17b +-#define F0900_FSKR_OUTCTL 0xf17b00c0 +-#define F0900_FSKR_AGC_REF 0xf17b003f +- +-/*FSKRAGC*/ +-#define R0900_FSKRAGC 0xf17c +-#define F0900_FSKR_AGC_ACCU 0xf17c00ff +- +-/*FSKRALPHA*/ +-#define R0900_FSKRALPHA 0xf17d +-#define F0900_FSKR_ALPHA_EXP 0xf17d001c +-#define F0900_FSKR_ALPHA_M 0xf17d0003 +- +-/*FSKRPLTH1*/ +-#define R0900_FSKRPLTH1 0xf17e +-#define F0900_FSKR_BETA 0xf17e00f0 +-#define F0900_FSKR_PLL_TRESH1 0xf17e000f +- +-/*FSKRPLTH0*/ +-#define R0900_FSKRPLTH0 0xf17f +-#define F0900_FSKR_PLL_TRESH0 0xf17f00ff +- +-/*FSKRDF1*/ +-#define R0900_FSKRDF1 0xf180 +-#define F0900_FSKR_OUT 0xf1800080 +-#define F0900_FSKR_DELTAF1 0xf180001f +- +-/*FSKRDF0*/ +-#define R0900_FSKRDF0 0xf181 +-#define F0900_FSKR_DELTAF0 0xf18100ff +- +-/*FSKRSTEPP*/ +-#define R0900_FSKRSTEPP 0xf182 +-#define F0900_FSKR_STEP_PLUS 0xf18200ff +- +-/*FSKRSTEPM*/ +-#define R0900_FSKRSTEPM 0xf183 +-#define F0900_FSKR_STEP_MINUS 0xf18300ff +- +-/*FSKRDET1*/ +-#define R0900_FSKRDET1 0xf184 +-#define F0900_FSKR_DETECT 0xf1840080 +-#define F0900_FSKR_CARDET_ACCU1 0xf184000f +- +-/*FSKRDET0*/ +-#define R0900_FSKRDET0 0xf185 +-#define F0900_FSKR_CARDET_ACCU0 0xf18500ff +- +-/*FSKRDTH1*/ +-#define R0900_FSKRDTH1 0xf186 +-#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0 +-#define F0900_FSKR_CARDET_THRESH1 0xf186000f +- +-/*FSKRDTH0*/ +-#define R0900_FSKRDTH0 0xf187 +-#define F0900_FSKR_CARDET_THRESH0 0xf18700ff +- +-/*FSKRLOSS*/ +-#define R0900_FSKRLOSS 0xf188 +-#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff +- +-/*P2_DISTXCTL*/ +-#define R0900_P2_DISTXCTL 0xf190 +-#define F0900_P2_TIM_OFF 0xf1900080 +-#define F0900_P2_DISEQC_RESET 0xf1900040 +-#define F0900_P2_TIM_CMD 0xf1900030 +-#define F0900_P2_DIS_PRECHARGE 0xf1900008 +-#define F0900_P2_DISTX_MODE 0xf1900007 +- +-/*P2_DISRXCTL*/ +-#define R0900_P2_DISRXCTL 0xf191 +-#define F0900_P2_RECEIVER_ON 0xf1910080 +-#define F0900_P2_IGNO_SHORT22K 0xf1910040 +-#define F0900_P2_ONECHIP_TRX 0xf1910020 +-#define F0900_P2_EXT_ENVELOP 0xf1910010 +-#define F0900_P2_PIN_SELECT0 0xf191000c +-#define F0900_P2_IRQ_RXEND 0xf1910002 +-#define F0900_P2_IRQ_4NBYTES 0xf1910001 +- +-/*P2_DISRX_ST0*/ +-#define R0900_P2_DISRX_ST0 0xf194 +-#define F0900_P2_RX_END 0xf1940080 +-#define F0900_P2_RX_ACTIVE 0xf1940040 +-#define F0900_P2_SHORT_22KHZ 0xf1940020 +-#define F0900_P2_CONT_TONE 0xf1940010 +-#define F0900_P2_FIFO_4BREADY 0xf1940008 +-#define F0900_P2_FIFO_EMPTY 0xf1940004 +-#define F0900_P2_ABORT_DISRX 0xf1940001 +- +-/*P2_DISRX_ST1*/ +-#define R0900_P2_DISRX_ST1 0xf195 +-#define F0900_P2_RX_FAIL 0xf1950080 +-#define F0900_P2_FIFO_PARITYFAIL 0xf1950040 +-#define F0900_P2_RX_NONBYTE 0xf1950020 +-#define F0900_P2_FIFO_OVERFLOW 0xf1950010 +-#define F0900_P2_FIFO_BYTENBR 0xf195000f +- +-/*P2_DISRXDATA*/ +-#define R0900_P2_DISRXDATA 0xf196 +-#define F0900_P2_DISRX_DATA 0xf19600ff +- +-/*P2_DISTXDATA*/ +-#define R0900_P2_DISTXDATA 0xf197 +-#define F0900_P2_DISEQC_FIFO 0xf19700ff +- +-/*P2_DISTXSTATUS*/ +-#define R0900_P2_DISTXSTATUS 0xf198 +-#define F0900_P2_TX_FAIL 0xf1980080 +-#define F0900_P2_FIFO_FULL 0xf1980040 +-#define F0900_P2_TX_IDLE 0xf1980020 +-#define F0900_P2_GAP_BURST 0xf1980010 +-#define F0900_P2_TXFIFO_BYTES 0xf198000f +- +-/*P2_F22TX*/ +-#define R0900_P2_F22TX 0xf199 +-#define F0900_P2_F22_REG 0xf19900ff +- +-/*P2_F22RX*/ +-#define R0900_P2_F22RX 0xf19a +-#define F0900_P2_F22RX_REG 0xf19a00ff +- +-/*P2_ACRPRESC*/ +-#define R0900_P2_ACRPRESC 0xf19c +-#define F0900_P2_ACR_PRESC 0xf19c0007 +- +-/*P2_ACRDIV*/ +-#define R0900_P2_ACRDIV 0xf19d +-#define F0900_P2_ACR_DIV 0xf19d00ff +- +-/*P1_DISTXCTL*/ +-#define R0900_P1_DISTXCTL 0xf1a0 +-#define DISTXCTL shiftx(R0900_P1_DISTXCTL, demod, 0x10) +-#define F0900_P1_TIM_OFF 0xf1a00080 +-#define F0900_P1_DISEQC_RESET 0xf1a00040 +-#define DISEQC_RESET shiftx(F0900_P1_DISEQC_RESET, demod, 0x100000) +-#define F0900_P1_TIM_CMD 0xf1a00030 +-#define F0900_P1_DIS_PRECHARGE 0xf1a00008 +-#define DIS_PRECHARGE shiftx(F0900_P1_DIS_PRECHARGE, demod, 0x100000) +-#define F0900_P1_DISTX_MODE 0xf1a00007 +-#define DISTX_MODE shiftx(F0900_P1_DISTX_MODE, demod, 0x100000) +- +-/*P1_DISRXCTL*/ +-#define R0900_P1_DISRXCTL 0xf1a1 +-#define DISRXCTL shiftx(R0900_P1_DISRXCTL, demod, 0x10) +-#define F0900_P1_RECEIVER_ON 0xf1a10080 +-#define F0900_P1_IGNO_SHORT22K 0xf1a10040 +-#define F0900_P1_ONECHIP_TRX 0xf1a10020 +-#define F0900_P1_EXT_ENVELOP 0xf1a10010 +-#define F0900_P1_PIN_SELECT0 0xf1a1000c +-#define F0900_P1_IRQ_RXEND 0xf1a10002 +-#define F0900_P1_IRQ_4NBYTES 0xf1a10001 +- +-/*P1_DISRX_ST0*/ +-#define R0900_P1_DISRX_ST0 0xf1a4 +-#define DISRX_ST0 shiftx(R0900_P1_DISRX_ST0, demod, 0x10) +-#define F0900_P1_RX_END 0xf1a40080 +-#define RX_END shiftx(F0900_P1_RX_END, demod, 0x100000) +-#define F0900_P1_RX_ACTIVE 0xf1a40040 +-#define F0900_P1_SHORT_22KHZ 0xf1a40020 +-#define F0900_P1_CONT_TONE 0xf1a40010 +-#define F0900_P1_FIFO_4BREADY 0xf1a40008 +-#define F0900_P1_FIFO_EMPTY 0xf1a40004 +-#define F0900_P1_ABORT_DISRX 0xf1a40001 +- +-/*P1_DISRX_ST1*/ +-#define R0900_P1_DISRX_ST1 0xf1a5 +-#define DISRX_ST1 shiftx(R0900_P1_DISRX_ST1, demod, 0x10) +-#define F0900_P1_RX_FAIL 0xf1a50080 +-#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040 +-#define F0900_P1_RX_NONBYTE 0xf1a50020 +-#define F0900_P1_FIFO_OVERFLOW 0xf1a50010 +-#define F0900_P1_FIFO_BYTENBR 0xf1a5000f +-#define FIFO_BYTENBR shiftx(F0900_P1_FIFO_BYTENBR, demod, 0x100000) +- +-/*P1_DISRXDATA*/ +-#define R0900_P1_DISRXDATA 0xf1a6 +-#define DISRXDATA shiftx(R0900_P1_DISRXDATA, demod, 0x10) +-#define F0900_P1_DISRX_DATA 0xf1a600ff +- +-/*P1_DISTXDATA*/ +-#define R0900_P1_DISTXDATA 0xf1a7 +-#define DISTXDATA shiftx(R0900_P1_DISTXDATA, demod, 0x10) +-#define F0900_P1_DISEQC_FIFO 0xf1a700ff +- +-/*P1_DISTXSTATUS*/ +-#define R0900_P1_DISTXSTATUS 0xf1a8 +-#define F0900_P1_TX_FAIL 0xf1a80080 +-#define F0900_P1_FIFO_FULL 0xf1a80040 +-#define FIFO_FULL shiftx(F0900_P1_FIFO_FULL, demod, 0x100000) +-#define F0900_P1_TX_IDLE 0xf1a80020 +-#define TX_IDLE shiftx(F0900_P1_TX_IDLE, demod, 0x100000) +-#define F0900_P1_GAP_BURST 0xf1a80010 +-#define F0900_P1_TXFIFO_BYTES 0xf1a8000f +- +-/*P1_F22TX*/ +-#define R0900_P1_F22TX 0xf1a9 +-#define F22TX shiftx(R0900_P1_F22TX, demod, 0x10) +-#define F0900_P1_F22_REG 0xf1a900ff +- +-/*P1_F22RX*/ +-#define R0900_P1_F22RX 0xf1aa +-#define F22RX shiftx(R0900_P1_F22RX, demod, 0x10) +-#define F0900_P1_F22RX_REG 0xf1aa00ff +- +-/*P1_ACRPRESC*/ +-#define R0900_P1_ACRPRESC 0xf1ac +-#define ACRPRESC shiftx(R0900_P1_ACRPRESC, demod, 0x10) +-#define F0900_P1_ACR_PRESC 0xf1ac0007 +- +-/*P1_ACRDIV*/ +-#define R0900_P1_ACRDIV 0xf1ad +-#define ACRDIV shiftx(R0900_P1_ACRDIV, demod, 0x10) +-#define F0900_P1_ACR_DIV 0xf1ad00ff +- +-/*NCOARSE*/ +-#define R0900_NCOARSE 0xf1b3 +-#define F0900_M_DIV 0xf1b300ff +- +-/*SYNTCTRL*/ +-#define R0900_SYNTCTRL 0xf1b6 +-#define F0900_STANDBY 0xf1b60080 +-#define F0900_BYPASSPLLCORE 0xf1b60040 +-#define F0900_SELX1RATIO 0xf1b60020 +-#define F0900_STOP_PLL 0xf1b60008 +-#define F0900_BYPASSPLLFSK 0xf1b60004 +-#define F0900_SELOSCI 0xf1b60002 +-#define F0900_BYPASSPLLADC 0xf1b60001 +- +-/*FILTCTRL*/ +-#define R0900_FILTCTRL 0xf1b7 +-#define F0900_INV_CLK135 0xf1b70080 +-#define F0900_SEL_FSKCKDIV 0xf1b70004 +-#define F0900_INV_CLKFSK 0xf1b70002 +-#define F0900_BYPASS_APPLI 0xf1b70001 +- +-/*PLLSTAT*/ +-#define R0900_PLLSTAT 0xf1b8 +-#define F0900_PLLLOCK 0xf1b80001 +- +-/*STOPCLK1*/ +-#define R0900_STOPCLK1 0xf1c2 +-#define F0900_STOP_CLKPKDT2 0xf1c20040 +-#define F0900_STOP_CLKPKDT1 0xf1c20020 +-#define F0900_STOP_CLKFEC 0xf1c20010 +-#define F0900_STOP_CLKADCI2 0xf1c20008 +-#define F0900_INV_CLKADCI2 0xf1c20004 +-#define F0900_STOP_CLKADCI1 0xf1c20002 +-#define F0900_INV_CLKADCI1 0xf1c20001 +- +-/*STOPCLK2*/ +-#define R0900_STOPCLK2 0xf1c3 +-#define F0900_STOP_CLKSAMP2 0xf1c30010 +-#define F0900_STOP_CLKSAMP1 0xf1c30008 +-#define F0900_STOP_CLKVIT2 0xf1c30004 +-#define F0900_STOP_CLKVIT1 0xf1c30002 +-#define STOP_CLKVIT shiftx(F0900_STOP_CLKVIT1, demod, -2) +-#define F0900_STOP_CLKTS 0xf1c30001 +- +-/*TSTTNR0*/ +-#define R0900_TSTTNR0 0xf1df +-#define F0900_SEL_FSK 0xf1df0080 +-#define F0900_FSK_PON 0xf1df0004 +- +-/*TSTTNR1*/ +-#define R0900_TSTTNR1 0xf1e0 +-#define F0900_ADC1_PON 0xf1e00002 +-#define F0900_ADC1_INMODE 0xf1e00001 +- +-/*TSTTNR2*/ +-#define R0900_TSTTNR2 0xf1e1 +-#define F0900_DISEQC1_PON 0xf1e10020 +- +-/*TSTTNR3*/ +-#define R0900_TSTTNR3 0xf1e2 +-#define F0900_ADC2_PON 0xf1e20002 +-#define F0900_ADC2_INMODE 0xf1e20001 +- +-/*TSTTNR4*/ +-#define R0900_TSTTNR4 0xf1e3 +-#define F0900_DISEQC2_PON 0xf1e30020 +- +-/*P2_IQCONST*/ +-#define R0900_P2_IQCONST 0xf200 +-#define F0900_P2_CONSTEL_SELECT 0xf2000060 +-#define F0900_P2_IQSYMB_SEL 0xf200001f +- +-/*P2_NOSCFG*/ +-#define R0900_P2_NOSCFG 0xf201 +-#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020 +-#define F0900_P2_NOSPLH_BETA 0xf2010018 +-#define F0900_P2_NOSDATA_BETA 0xf2010007 +- +-/*P2_ISYMB*/ +-#define R0900_P2_ISYMB 0xf202 +-#define F0900_P2_I_SYMBOL 0xf20201ff +- +-/*P2_QSYMB*/ +-#define R0900_P2_QSYMB 0xf203 +-#define F0900_P2_Q_SYMBOL 0xf20301ff +- +-/*P2_AGC1CFG*/ +-#define R0900_P2_AGC1CFG 0xf204 +-#define F0900_P2_DC_FROZEN 0xf2040080 +-#define F0900_P2_DC_CORRECT 0xf2040040 +-#define F0900_P2_AMM_FROZEN 0xf2040020 +-#define F0900_P2_AMM_CORRECT 0xf2040010 +-#define F0900_P2_QUAD_FROZEN 0xf2040008 +-#define F0900_P2_QUAD_CORRECT 0xf2040004 +- +-/*P2_AGC1CN*/ +-#define R0900_P2_AGC1CN 0xf206 +-#define F0900_P2_AGC1_LOCKED 0xf2060080 +-#define F0900_P2_AGC1_MINPOWER 0xf2060010 +-#define F0900_P2_AGCOUT_FAST 0xf2060008 +-#define F0900_P2_AGCIQ_BETA 0xf2060007 +- +-/*P2_AGC1REF*/ +-#define R0900_P2_AGC1REF 0xf207 +-#define F0900_P2_AGCIQ_REF 0xf20700ff +- +-/*P2_IDCCOMP*/ +-#define R0900_P2_IDCCOMP 0xf208 +-#define F0900_P2_IAVERAGE_ADJ 0xf20801ff +- +-/*P2_QDCCOMP*/ +-#define R0900_P2_QDCCOMP 0xf209 +-#define F0900_P2_QAVERAGE_ADJ 0xf20901ff +- +-/*P2_POWERI*/ +-#define R0900_P2_POWERI 0xf20a +-#define F0900_P2_POWER_I 0xf20a00ff +- +-/*P2_POWERQ*/ +-#define R0900_P2_POWERQ 0xf20b +-#define F0900_P2_POWER_Q 0xf20b00ff +- +-/*P2_AGC1AMM*/ +-#define R0900_P2_AGC1AMM 0xf20c +-#define F0900_P2_AMM_VALUE 0xf20c00ff +- +-/*P2_AGC1QUAD*/ +-#define R0900_P2_AGC1QUAD 0xf20d +-#define F0900_P2_QUAD_VALUE 0xf20d01ff +- +-/*P2_AGCIQIN1*/ +-#define R0900_P2_AGCIQIN1 0xf20e +-#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff +- +-/*P2_AGCIQIN0*/ +-#define R0900_P2_AGCIQIN0 0xf20f +-#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff +- +-/*P2_DEMOD*/ +-#define R0900_P2_DEMOD 0xf210 +-#define F0900_P2_MANUALS2_ROLLOFF 0xf2100080 +-#define F0900_P2_SPECINV_CONTROL 0xf2100030 +-#define F0900_P2_FORCE_ENASAMP 0xf2100008 +-#define F0900_P2_MANUALSX_ROLLOFF 0xf2100004 +-#define F0900_P2_ROLLOFF_CONTROL 0xf2100003 +- +-/*P2_DMDMODCOD*/ +-#define R0900_P2_DMDMODCOD 0xf211 +-#define F0900_P2_MANUAL_MODCOD 0xf2110080 +-#define F0900_P2_DEMOD_MODCOD 0xf211007c +-#define F0900_P2_DEMOD_TYPE 0xf2110003 +- +-/*P2_DSTATUS*/ +-#define R0900_P2_DSTATUS 0xf212 +-#define F0900_P2_CAR_LOCK 0xf2120080 +-#define F0900_P2_TMGLOCK_QUALITY 0xf2120060 +-#define F0900_P2_LOCK_DEFINITIF 0xf2120008 +-#define F0900_P2_OVADC_DETECT 0xf2120001 +- +-/*P2_DSTATUS2*/ +-#define R0900_P2_DSTATUS2 0xf213 +-#define F0900_P2_DEMOD_DELOCK 0xf2130080 +-#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008 +-#define F0900_P2_AGC2_OVERFLOW 0xf2130004 +-#define F0900_P2_CFR_OVERFLOW 0xf2130002 +-#define F0900_P2_GAMMA_OVERUNDER 0xf2130001 +- +-/*P2_DMDCFGMD*/ +-#define R0900_P2_DMDCFGMD 0xf214 +-#define F0900_P2_DVBS2_ENABLE 0xf2140080 +-#define F0900_P2_DVBS1_ENABLE 0xf2140040 +-#define F0900_P2_SCAN_ENABLE 0xf2140010 +-#define F0900_P2_CFR_AUTOSCAN 0xf2140008 +-#define F0900_P2_TUN_RNG 0xf2140003 +- +-/*P2_DMDCFG2*/ +-#define R0900_P2_DMDCFG2 0xf215 +-#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040 +-#define F0900_P2_INFINITE_RELOCK 0xf2150010 +- +-/*P2_DMDISTATE*/ +-#define R0900_P2_DMDISTATE 0xf216 +-#define F0900_P2_I2C_DEMOD_MODE 0xf216001f +- +-/*P2_DMDT0M*/ +-#define R0900_P2_DMDT0M 0xf217 +-#define F0900_P2_DMDT0_MIN 0xf21700ff +- +-/*P2_DMDSTATE*/ +-#define R0900_P2_DMDSTATE 0xf21b +-#define F0900_P2_HEADER_MODE 0xf21b0060 +- +-/*P2_DMDFLYW*/ +-#define R0900_P2_DMDFLYW 0xf21c +-#define F0900_P2_I2C_IRQVAL 0xf21c00f0 +-#define F0900_P2_FLYWHEEL_CPT 0xf21c000f +- +-/*P2_DSTATUS3*/ +-#define R0900_P2_DSTATUS3 0xf21d +-#define F0900_P2_DEMOD_CFGMODE 0xf21d0060 +- +-/*P2_DMDCFG3*/ +-#define R0900_P2_DMDCFG3 0xf21e +-#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008 +- +-/*P2_DMDCFG4*/ +-#define R0900_P2_DMDCFG4 0xf21f +-#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008 +- +-/*P2_CORRELMANT*/ +-#define R0900_P2_CORRELMANT 0xf220 +-#define F0900_P2_CORREL_MANT 0xf22000ff +- +-/*P2_CORRELABS*/ +-#define R0900_P2_CORRELABS 0xf221 +-#define F0900_P2_CORREL_ABS 0xf22100ff +- +-/*P2_CORRELEXP*/ +-#define R0900_P2_CORRELEXP 0xf222 +-#define F0900_P2_CORREL_ABSEXP 0xf22200f0 +-#define F0900_P2_CORREL_EXP 0xf222000f +- +-/*P2_PLHMODCOD*/ +-#define R0900_P2_PLHMODCOD 0xf224 +-#define F0900_P2_SPECINV_DEMOD 0xf2240080 +-#define F0900_P2_PLH_MODCOD 0xf224007c +-#define F0900_P2_PLH_TYPE 0xf2240003 +- +-/*P2_DMDREG*/ +-#define R0900_P2_DMDREG 0xf225 +-#define F0900_P2_DECIM_PLFRAMES 0xf2250001 +- +-/*P2_AGC2O*/ +-#define R0900_P2_AGC2O 0xf22c +-#define F0900_P2_AGC2_COEF 0xf22c0007 +- +-/*P2_AGC2REF*/ +-#define R0900_P2_AGC2REF 0xf22d +-#define F0900_P2_AGC2_REF 0xf22d00ff +- +-/*P2_AGC1ADJ*/ +-#define R0900_P2_AGC1ADJ 0xf22e +-#define F0900_P2_AGC1_ADJUSTED 0xf22e007f +- +-/*P2_AGC2I1*/ +-#define R0900_P2_AGC2I1 0xf236 +-#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff +- +-/*P2_AGC2I0*/ +-#define R0900_P2_AGC2I0 0xf237 +-#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff +- +-/*P2_CARCFG*/ +-#define R0900_P2_CARCFG 0xf238 +-#define F0900_P2_CFRUPLOW_AUTO 0xf2380080 +-#define F0900_P2_CFRUPLOW_TEST 0xf2380040 +-#define F0900_P2_ROTAON 0xf2380004 +-#define F0900_P2_PH_DET_ALGO 0xf2380003 +- +-/*P2_ACLC*/ +-#define R0900_P2_ACLC 0xf239 +-#define F0900_P2_CAR_ALPHA_MANT 0xf2390030 +-#define F0900_P2_CAR_ALPHA_EXP 0xf239000f +- +-/*P2_BCLC*/ +-#define R0900_P2_BCLC 0xf23a +-#define F0900_P2_CAR_BETA_MANT 0xf23a0030 +-#define F0900_P2_CAR_BETA_EXP 0xf23a000f +- +-/*P2_CARFREQ*/ +-#define R0900_P2_CARFREQ 0xf23d +-#define F0900_P2_KC_COARSE_EXP 0xf23d00f0 +-#define F0900_P2_BETA_FREQ 0xf23d000f +- +-/*P2_CARHDR*/ +-#define R0900_P2_CARHDR 0xf23e +-#define F0900_P2_K_FREQ_HDR 0xf23e00ff +- +-/*P2_LDT*/ +-#define R0900_P2_LDT 0xf23f +-#define F0900_P2_CARLOCK_THRES 0xf23f01ff +- +-/*P2_LDT2*/ +-#define R0900_P2_LDT2 0xf240 +-#define F0900_P2_CARLOCK_THRES2 0xf24001ff +- +-/*P2_CFRICFG*/ +-#define R0900_P2_CFRICFG 0xf241 +-#define F0900_P2_NEG_CFRSTEP 0xf2410001 +- +-/*P2_CFRUP1*/ +-#define R0900_P2_CFRUP1 0xf242 +-#define F0900_P2_CFR_UP1 0xf24201ff +- +-/*P2_CFRUP0*/ +-#define R0900_P2_CFRUP0 0xf243 +-#define F0900_P2_CFR_UP0 0xf24300ff +- +-/*P2_CFRLOW1*/ +-#define R0900_P2_CFRLOW1 0xf246 +-#define F0900_P2_CFR_LOW1 0xf24601ff +- +-/*P2_CFRLOW0*/ +-#define R0900_P2_CFRLOW0 0xf247 +-#define F0900_P2_CFR_LOW0 0xf24700ff +- +-/*P2_CFRINIT1*/ +-#define R0900_P2_CFRINIT1 0xf248 +-#define F0900_P2_CFR_INIT1 0xf24801ff +- +-/*P2_CFRINIT0*/ +-#define R0900_P2_CFRINIT0 0xf249 +-#define F0900_P2_CFR_INIT0 0xf24900ff +- +-/*P2_CFRINC1*/ +-#define R0900_P2_CFRINC1 0xf24a +-#define F0900_P2_MANUAL_CFRINC 0xf24a0080 +-#define F0900_P2_CFR_INC1 0xf24a003f +- +-/*P2_CFRINC0*/ +-#define R0900_P2_CFRINC0 0xf24b +-#define F0900_P2_CFR_INC0 0xf24b00f8 +- +-/*P2_CFR2*/ +-#define R0900_P2_CFR2 0xf24c +-#define F0900_P2_CAR_FREQ2 0xf24c01ff +- +-/*P2_CFR1*/ +-#define R0900_P2_CFR1 0xf24d +-#define F0900_P2_CAR_FREQ1 0xf24d00ff +- +-/*P2_CFR0*/ +-#define R0900_P2_CFR0 0xf24e +-#define F0900_P2_CAR_FREQ0 0xf24e00ff +- +-/*P2_LDI*/ +-#define R0900_P2_LDI 0xf24f +-#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff +- +-/*P2_TMGCFG*/ +-#define R0900_P2_TMGCFG 0xf250 +-#define F0900_P2_TMGLOCK_BETA 0xf25000c0 +-#define F0900_P2_DO_TIMING_CORR 0xf2500010 +-#define F0900_P2_TMG_MINFREQ 0xf2500003 +- +-/*P2_RTC*/ +-#define R0900_P2_RTC 0xf251 +-#define F0900_P2_TMGALPHA_EXP 0xf25100f0 +-#define F0900_P2_TMGBETA_EXP 0xf251000f +- +-/*P2_RTCS2*/ +-#define R0900_P2_RTCS2 0xf252 +-#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0 +-#define F0900_P2_TMGBETAS2_EXP 0xf252000f +- +-/*P2_TMGTHRISE*/ +-#define R0900_P2_TMGTHRISE 0xf253 +-#define F0900_P2_TMGLOCK_THRISE 0xf25300ff +- +-/*P2_TMGTHFALL*/ +-#define R0900_P2_TMGTHFALL 0xf254 +-#define F0900_P2_TMGLOCK_THFALL 0xf25400ff +- +-/*P2_SFRUPRATIO*/ +-#define R0900_P2_SFRUPRATIO 0xf255 +-#define F0900_P2_SFR_UPRATIO 0xf25500ff +- +-/*P2_SFRLOWRATIO*/ +-#define R0900_P2_SFRLOWRATIO 0xf256 +-#define F0900_P2_SFR_LOWRATIO 0xf25600ff +- +-/*P2_KREFTMG*/ +-#define R0900_P2_KREFTMG 0xf258 +-#define F0900_P2_KREF_TMG 0xf25800ff +- +-/*P2_SFRSTEP*/ +-#define R0900_P2_SFRSTEP 0xf259 +-#define F0900_P2_SFR_SCANSTEP 0xf25900f0 +-#define F0900_P2_SFR_CENTERSTEP 0xf259000f +- +-/*P2_TMGCFG2*/ +-#define R0900_P2_TMGCFG2 0xf25a +-#define F0900_P2_SFRRATIO_FINE 0xf25a0001 +- +-/*P2_KREFTMG2*/ +-#define R0900_P2_KREFTMG2 0xf25b +-#define F0900_P2_KREF_TMG2 0xf25b00ff +- +-/*P2_SFRINIT1*/ +-#define R0900_P2_SFRINIT1 0xf25e +-#define F0900_P2_SFR_INIT1 0xf25e007f +- +-/*P2_SFRINIT0*/ +-#define R0900_P2_SFRINIT0 0xf25f +-#define F0900_P2_SFR_INIT0 0xf25f00ff +- +-/*P2_SFRUP1*/ +-#define R0900_P2_SFRUP1 0xf260 +-#define F0900_P2_AUTO_GUP 0xf2600080 +-#define F0900_P2_SYMB_FREQ_UP1 0xf260007f +- +-/*P2_SFRUP0*/ +-#define R0900_P2_SFRUP0 0xf261 +-#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff +- +-/*P2_SFRLOW1*/ +-#define R0900_P2_SFRLOW1 0xf262 +-#define F0900_P2_AUTO_GLOW 0xf2620080 +-#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f +- +-/*P2_SFRLOW0*/ +-#define R0900_P2_SFRLOW0 0xf263 +-#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff +- +-/*P2_SFR3*/ +-#define R0900_P2_SFR3 0xf264 +-#define F0900_P2_SYMB_FREQ3 0xf26400ff +- +-/*P2_SFR2*/ +-#define R0900_P2_SFR2 0xf265 +-#define F0900_P2_SYMB_FREQ2 0xf26500ff +- +-/*P2_SFR1*/ +-#define R0900_P2_SFR1 0xf266 +-#define F0900_P2_SYMB_FREQ1 0xf26600ff +- +-/*P2_SFR0*/ +-#define R0900_P2_SFR0 0xf267 +-#define F0900_P2_SYMB_FREQ0 0xf26700ff +- +-/*P2_TMGREG2*/ +-#define R0900_P2_TMGREG2 0xf268 +-#define F0900_P2_TMGREG2 0xf26800ff +- +-/*P2_TMGREG1*/ +-#define R0900_P2_TMGREG1 0xf269 +-#define F0900_P2_TMGREG1 0xf26900ff +- +-/*P2_TMGREG0*/ +-#define R0900_P2_TMGREG0 0xf26a +-#define F0900_P2_TMGREG0 0xf26a00ff +- +-/*P2_TMGLOCK1*/ +-#define R0900_P2_TMGLOCK1 0xf26b +-#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff +- +-/*P2_TMGLOCK0*/ +-#define R0900_P2_TMGLOCK0 0xf26c +-#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff +- +-/*P2_TMGOBS*/ +-#define R0900_P2_TMGOBS 0xf26d +-#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0 +- +-/*P2_EQUALCFG*/ +-#define R0900_P2_EQUALCFG 0xf26f +-#define F0900_P2_EQUAL_ON 0xf26f0040 +-#define F0900_P2_MU_EQUALDFE 0xf26f0007 +- +-/*P2_EQUAI1*/ +-#define R0900_P2_EQUAI1 0xf270 +-#define F0900_P2_EQUA_ACCI1 0xf27001ff +- +-/*P2_EQUAQ1*/ +-#define R0900_P2_EQUAQ1 0xf271 +-#define F0900_P2_EQUA_ACCQ1 0xf27101ff +- +-/*P2_EQUAI2*/ +-#define R0900_P2_EQUAI2 0xf272 +-#define F0900_P2_EQUA_ACCI2 0xf27201ff +- +-/*P2_EQUAQ2*/ +-#define R0900_P2_EQUAQ2 0xf273 +-#define F0900_P2_EQUA_ACCQ2 0xf27301ff +- +-/*P2_EQUAI3*/ +-#define R0900_P2_EQUAI3 0xf274 +-#define F0900_P2_EQUA_ACCI3 0xf27401ff +- +-/*P2_EQUAQ3*/ +-#define R0900_P2_EQUAQ3 0xf275 +-#define F0900_P2_EQUA_ACCQ3 0xf27501ff +- +-/*P2_EQUAI4*/ +-#define R0900_P2_EQUAI4 0xf276 +-#define F0900_P2_EQUA_ACCI4 0xf27601ff +- +-/*P2_EQUAQ4*/ +-#define R0900_P2_EQUAQ4 0xf277 +-#define F0900_P2_EQUA_ACCQ4 0xf27701ff +- +-/*P2_EQUAI5*/ +-#define R0900_P2_EQUAI5 0xf278 +-#define F0900_P2_EQUA_ACCI5 0xf27801ff +- +-/*P2_EQUAQ5*/ +-#define R0900_P2_EQUAQ5 0xf279 +-#define F0900_P2_EQUA_ACCQ5 0xf27901ff +- +-/*P2_EQUAI6*/ +-#define R0900_P2_EQUAI6 0xf27a +-#define F0900_P2_EQUA_ACCI6 0xf27a01ff +- +-/*P2_EQUAQ6*/ +-#define R0900_P2_EQUAQ6 0xf27b +-#define F0900_P2_EQUA_ACCQ6 0xf27b01ff +- +-/*P2_EQUAI7*/ +-#define R0900_P2_EQUAI7 0xf27c +-#define F0900_P2_EQUA_ACCI7 0xf27c01ff +- +-/*P2_EQUAQ7*/ +-#define R0900_P2_EQUAQ7 0xf27d +-#define F0900_P2_EQUA_ACCQ7 0xf27d01ff +- +-/*P2_EQUAI8*/ +-#define R0900_P2_EQUAI8 0xf27e +-#define F0900_P2_EQUA_ACCI8 0xf27e01ff +- +-/*P2_EQUAQ8*/ +-#define R0900_P2_EQUAQ8 0xf27f +-#define F0900_P2_EQUA_ACCQ8 0xf27f01ff +- +-/*P2_NNOSDATAT1*/ +-#define R0900_P2_NNOSDATAT1 0xf280 +-#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff +- +-/*P2_NNOSDATAT0*/ +-#define R0900_P2_NNOSDATAT0 0xf281 +-#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff +- +-/*P2_NNOSDATA1*/ +-#define R0900_P2_NNOSDATA1 0xf282 +-#define F0900_P2_NOSDATA_NORMED1 0xf28200ff +- +-/*P2_NNOSDATA0*/ +-#define R0900_P2_NNOSDATA0 0xf283 +-#define F0900_P2_NOSDATA_NORMED0 0xf28300ff +- +-/*P2_NNOSPLHT1*/ +-#define R0900_P2_NNOSPLHT1 0xf284 +-#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff +- +-/*P2_NNOSPLHT0*/ +-#define R0900_P2_NNOSPLHT0 0xf285 +-#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff +- +-/*P2_NNOSPLH1*/ +-#define R0900_P2_NNOSPLH1 0xf286 +-#define F0900_P2_NOSPLH_NORMED1 0xf28600ff +- +-/*P2_NNOSPLH0*/ +-#define R0900_P2_NNOSPLH0 0xf287 +-#define F0900_P2_NOSPLH_NORMED0 0xf28700ff +- +-/*P2_NOSDATAT1*/ +-#define R0900_P2_NOSDATAT1 0xf288 +-#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff +- +-/*P2_NOSDATAT0*/ +-#define R0900_P2_NOSDATAT0 0xf289 +-#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff +- +-/*P2_NOSDATA1*/ +-#define R0900_P2_NOSDATA1 0xf28a +-#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff +- +-/*P2_NOSDATA0*/ +-#define R0900_P2_NOSDATA0 0xf28b +-#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff +- +-/*P2_NOSPLHT1*/ +-#define R0900_P2_NOSPLHT1 0xf28c +-#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff +- +-/*P2_NOSPLHT0*/ +-#define R0900_P2_NOSPLHT0 0xf28d +-#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff +- +-/*P2_NOSPLH1*/ +-#define R0900_P2_NOSPLH1 0xf28e +-#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff +- +-/*P2_NOSPLH0*/ +-#define R0900_P2_NOSPLH0 0xf28f +-#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff +- +-/*P2_CAR2CFG*/ +-#define R0900_P2_CAR2CFG 0xf290 +-#define F0900_P2_CARRIER3_DISABLE 0xf2900040 +-#define F0900_P2_ROTA2ON 0xf2900004 +-#define F0900_P2_PH_DET_ALGO2 0xf2900003 +- +-/*P2_CFR2CFR1*/ +-#define R0900_P2_CFR2CFR1 0xf291 +-#define F0900_P2_CFR2TOCFR1_DVBS1 0xf29100c0 +-#define F0900_P2_EN_S2CAR2CENTER 0xf2910020 +-#define F0900_P2_DIS_BCHERRCFR2 0xf2910010 +-#define F0900_P2_CFR2TOCFR1_BETA 0xf2910007 +- +-/*P2_CFR22*/ +-#define R0900_P2_CFR22 0xf293 +-#define F0900_P2_CAR2_FREQ2 0xf29301ff +- +-/*P2_CFR21*/ +-#define R0900_P2_CFR21 0xf294 +-#define F0900_P2_CAR2_FREQ1 0xf29400ff +- +-/*P2_CFR20*/ +-#define R0900_P2_CFR20 0xf295 +-#define F0900_P2_CAR2_FREQ0 0xf29500ff +- +-/*P2_ACLC2S2Q*/ +-#define R0900_P2_ACLC2S2Q 0xf297 +-#define F0900_P2_ENAB_SPSKSYMB 0xf2970080 +-#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030 +-#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f +- +-/*P2_ACLC2S28*/ +-#define R0900_P2_ACLC2S28 0xf298 +-#define F0900_P2_OLDI3Q_MODE 0xf2980080 +-#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030 +-#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f +- +-/*P2_ACLC2S216A*/ +-#define R0900_P2_ACLC2S216A 0xf299 +-#define F0900_P2_DIS_C3STOPA2 0xf2990080 +-#define F0900_P2_CAR2S2_16ADERAT 0xf2990040 +-#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030 +-#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f +- +-/*P2_ACLC2S232A*/ +-#define R0900_P2_ACLC2S232A 0xf29a +-#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040 +-#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030 +-#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f +- +-/*P2_BCLC2S2Q*/ +-#define R0900_P2_BCLC2S2Q 0xf29c +-#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030 +-#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f +- +-/*P2_BCLC2S28*/ +-#define R0900_P2_BCLC2S28 0xf29d +-#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030 +-#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f +- +-/*P2_BCLC2S216A*/ +-#define R0900_P2_BCLC2S216A 0xf29e +- +-/*P2_BCLC2S232A*/ +-#define R0900_P2_BCLC2S232A 0xf29f +- +-/*P2_PLROOT2*/ +-#define R0900_P2_PLROOT2 0xf2ac +-#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c +-#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003 +- +-/*P2_PLROOT1*/ +-#define R0900_P2_PLROOT1 0xf2ad +-#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff +- +-/*P2_PLROOT0*/ +-#define R0900_P2_PLROOT0 0xf2ae +-#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff +- +-/*P2_MODCODLST0*/ +-#define R0900_P2_MODCODLST0 0xf2b0 +- +-/*P2_MODCODLST1*/ +-#define R0900_P2_MODCODLST1 0xf2b1 +-#define F0900_P2_DIS_MODCOD29 0xf2b100f0 +-#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f +- +-/*P2_MODCODLST2*/ +-#define R0900_P2_MODCODLST2 0xf2b2 +-#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0 +-#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f +- +-/*P2_MODCODLST3*/ +-#define R0900_P2_MODCODLST3 0xf2b3 +-#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0 +-#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f +- +-/*P2_MODCODLST4*/ +-#define R0900_P2_MODCODLST4 0xf2b4 +-#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0 +-#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f +- +-/*P2_MODCODLST5*/ +-#define R0900_P2_MODCODLST5 0xf2b5 +-#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0 +-#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f +- +-/*P2_MODCODLST6*/ +-#define R0900_P2_MODCODLST6 0xf2b6 +-#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0 +-#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f +- +-/*P2_MODCODLST7*/ +-#define R0900_P2_MODCODLST7 0xf2b7 +-#define F0900_P2_DIS_8P_9_10 0xf2b700f0 +-#define F0900_P2_DIS_8P_8_9 0xf2b7000f +- +-/*P2_MODCODLST8*/ +-#define R0900_P2_MODCODLST8 0xf2b8 +-#define F0900_P2_DIS_8P_5_6 0xf2b800f0 +-#define F0900_P2_DIS_8P_3_4 0xf2b8000f +- +-/*P2_MODCODLST9*/ +-#define R0900_P2_MODCODLST9 0xf2b9 +-#define F0900_P2_DIS_8P_2_3 0xf2b900f0 +-#define F0900_P2_DIS_8P_3_5 0xf2b9000f +- +-/*P2_MODCODLSTA*/ +-#define R0900_P2_MODCODLSTA 0xf2ba +-#define F0900_P2_DIS_QP_9_10 0xf2ba00f0 +-#define F0900_P2_DIS_QP_8_9 0xf2ba000f +- +-/*P2_MODCODLSTB*/ +-#define R0900_P2_MODCODLSTB 0xf2bb +-#define F0900_P2_DIS_QP_5_6 0xf2bb00f0 +-#define F0900_P2_DIS_QP_4_5 0xf2bb000f +- +-/*P2_MODCODLSTC*/ +-#define R0900_P2_MODCODLSTC 0xf2bc +-#define F0900_P2_DIS_QP_3_4 0xf2bc00f0 +-#define F0900_P2_DIS_QP_2_3 0xf2bc000f +- +-/*P2_MODCODLSTD*/ +-#define R0900_P2_MODCODLSTD 0xf2bd +-#define F0900_P2_DIS_QP_3_5 0xf2bd00f0 +-#define F0900_P2_DIS_QP_1_2 0xf2bd000f +- +-/*P2_MODCODLSTE*/ +-#define R0900_P2_MODCODLSTE 0xf2be +-#define F0900_P2_DIS_QP_2_5 0xf2be00f0 +-#define F0900_P2_DIS_QP_1_3 0xf2be000f +- +-/*P2_MODCODLSTF*/ +-#define R0900_P2_MODCODLSTF 0xf2bf +-#define F0900_P2_DIS_QP_1_4 0xf2bf00f0 +- +-/*P2_GAUSSR0*/ +-#define R0900_P2_GAUSSR0 0xf2c0 +-#define F0900_P2_EN_CCIMODE 0xf2c00080 +-#define F0900_P2_R0_GAUSSIEN 0xf2c0007f +- +-/*P2_CCIR0*/ +-#define R0900_P2_CCIR0 0xf2c1 +-#define F0900_P2_CCIDETECT_PLHONLY 0xf2c10080 +-#define F0900_P2_R0_CCI 0xf2c1007f +- +-/*P2_CCIQUANT*/ +-#define R0900_P2_CCIQUANT 0xf2c2 +-#define F0900_P2_CCI_BETA 0xf2c200e0 +-#define F0900_P2_CCI_QUANT 0xf2c2001f +- +-/*P2_CCITHRES*/ +-#define R0900_P2_CCITHRES 0xf2c3 +-#define F0900_P2_CCI_THRESHOLD 0xf2c300ff +- +-/*P2_CCIACC*/ +-#define R0900_P2_CCIACC 0xf2c4 +-#define F0900_P2_CCI_VALUE 0xf2c400ff +- +-/*P2_DMDRESCFG*/ +-#define R0900_P2_DMDRESCFG 0xf2c6 +-#define F0900_P2_DMDRES_RESET 0xf2c60080 +-#define F0900_P2_DMDRES_STRALL 0xf2c60008 +-#define F0900_P2_DMDRES_NEWONLY 0xf2c60004 +-#define F0900_P2_DMDRES_NOSTORE 0xf2c60002 +- +-/*P2_DMDRESADR*/ +-#define R0900_P2_DMDRESADR 0xf2c7 +-#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040 +-#define F0900_P2_DMDRES_MEMFULL 0xf2c70030 +-#define F0900_P2_DMDRES_RESNBR 0xf2c7000f +- +-/*P2_DMDRESDATA7*/ +-#define R0900_P2_DMDRESDATA7 0xf2c8 +-#define F0900_P2_DMDRES_DATA7 0xf2c800ff +- +-/*P2_DMDRESDATA6*/ +-#define R0900_P2_DMDRESDATA6 0xf2c9 +-#define F0900_P2_DMDRES_DATA6 0xf2c900ff +- +-/*P2_DMDRESDATA5*/ +-#define R0900_P2_DMDRESDATA5 0xf2ca +-#define F0900_P2_DMDRES_DATA5 0xf2ca00ff +- +-/*P2_DMDRESDATA4*/ +-#define R0900_P2_DMDRESDATA4 0xf2cb +-#define F0900_P2_DMDRES_DATA4 0xf2cb00ff +- +-/*P2_DMDRESDATA3*/ +-#define R0900_P2_DMDRESDATA3 0xf2cc +-#define F0900_P2_DMDRES_DATA3 0xf2cc00ff +- +-/*P2_DMDRESDATA2*/ +-#define R0900_P2_DMDRESDATA2 0xf2cd +-#define F0900_P2_DMDRES_DATA2 0xf2cd00ff +- +-/*P2_DMDRESDATA1*/ +-#define R0900_P2_DMDRESDATA1 0xf2ce +-#define F0900_P2_DMDRES_DATA1 0xf2ce00ff +- +-/*P2_DMDRESDATA0*/ +-#define R0900_P2_DMDRESDATA0 0xf2cf +-#define F0900_P2_DMDRES_DATA0 0xf2cf00ff +- +-/*P2_FFEI1*/ +-#define R0900_P2_FFEI1 0xf2d0 +-#define F0900_P2_FFE_ACCI1 0xf2d001ff +- +-/*P2_FFEQ1*/ +-#define R0900_P2_FFEQ1 0xf2d1 +-#define F0900_P2_FFE_ACCQ1 0xf2d101ff +- +-/*P2_FFEI2*/ +-#define R0900_P2_FFEI2 0xf2d2 +-#define F0900_P2_FFE_ACCI2 0xf2d201ff +- +-/*P2_FFEQ2*/ +-#define R0900_P2_FFEQ2 0xf2d3 +-#define F0900_P2_FFE_ACCQ2 0xf2d301ff +- +-/*P2_FFEI3*/ +-#define R0900_P2_FFEI3 0xf2d4 +-#define F0900_P2_FFE_ACCI3 0xf2d401ff +- +-/*P2_FFEQ3*/ +-#define R0900_P2_FFEQ3 0xf2d5 +-#define F0900_P2_FFE_ACCQ3 0xf2d501ff +- +-/*P2_FFEI4*/ +-#define R0900_P2_FFEI4 0xf2d6 +-#define F0900_P2_FFE_ACCI4 0xf2d601ff +- +-/*P2_FFEQ4*/ +-#define R0900_P2_FFEQ4 0xf2d7 +-#define F0900_P2_FFE_ACCQ4 0xf2d701ff +- +-/*P2_FFECFG*/ +-#define R0900_P2_FFECFG 0xf2d8 +-#define F0900_P2_EQUALFFE_ON 0xf2d80040 +-#define F0900_P2_MU_EQUALFFE 0xf2d80007 +- +-/*P2_TNRCFG*/ +-#define R0900_P2_TNRCFG 0xf2e0 +-#define F0900_P2_TUN_ACKFAIL 0xf2e00080 +-#define F0900_P2_TUN_TYPE 0xf2e00070 +-#define F0900_P2_TUN_SECSTOP 0xf2e00008 +-#define F0900_P2_TUN_VCOSRCH 0xf2e00004 +-#define F0900_P2_TUN_MADDRESS 0xf2e00003 +- +-/*P2_TNRCFG2*/ +-#define R0900_P2_TNRCFG2 0xf2e1 +-#define F0900_P2_TUN_IQSWAP 0xf2e10080 +-#define F0900_P2_DIS_BWCALC 0xf2e10004 +-#define F0900_P2_SHORT_WAITSTATES 0xf2e10002 +- +-/*P2_TNRXTAL*/ +-#define R0900_P2_TNRXTAL 0xf2e4 +-#define F0900_P2_TUN_XTALFREQ 0xf2e4001f +- +-/*P2_TNRSTEPS*/ +-#define R0900_P2_TNRSTEPS 0xf2e7 +-#define F0900_P2_TUNER_BW0P125 0xf2e70080 +-#define F0900_P2_BWINC_OFFSET 0xf2e70170 +-#define F0900_P2_SOFTSTEP_RNG 0xf2e70008 +-#define F0900_P2_TUN_BWOFFSET 0xf2e70007 +- +-/*P2_TNRGAIN*/ +-#define R0900_P2_TNRGAIN 0xf2e8 +-#define F0900_P2_TUN_KDIVEN 0xf2e800c0 +-#define F0900_P2_STB6X00_OCK 0xf2e80030 +-#define F0900_P2_TUN_GAIN 0xf2e8000f +- +-/*P2_TNRRF1*/ +-#define R0900_P2_TNRRF1 0xf2e9 +-#define F0900_P2_TUN_RFFREQ2 0xf2e900ff +- +-/*P2_TNRRF0*/ +-#define R0900_P2_TNRRF0 0xf2ea +-#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff +- +-/*P2_TNRBW*/ +-#define R0900_P2_TNRBW 0xf2eb +-#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0 +-#define F0900_P2_TUN_BW 0xf2eb003f +- +-/*P2_TNRADJ*/ +-#define R0900_P2_TNRADJ 0xf2ec +-#define F0900_P2_STB61X0_CALTIME 0xf2ec0040 +- +-/*P2_TNRCTL2*/ +-#define R0900_P2_TNRCTL2 0xf2ed +-#define F0900_P2_STB61X0_RCCKOFF 0xf2ed0080 +-#define F0900_P2_STB61X0_ICP_SDOFF 0xf2ed0040 +-#define F0900_P2_STB61X0_DCLOOPOFF 0xf2ed0020 +-#define F0900_P2_STB61X0_REFOUTSEL 0xf2ed0010 +-#define F0900_P2_STB61X0_CALOFF 0xf2ed0008 +-#define F0900_P2_STB6XX0_LPT_BEN 0xf2ed0004 +-#define F0900_P2_STB6XX0_RX_OSCP 0xf2ed0002 +-#define F0900_P2_STB6XX0_SYN 0xf2ed0001 +- +-/*P2_TNRCFG3*/ +-#define R0900_P2_TNRCFG3 0xf2ee +-#define F0900_P2_TUN_PLLFREQ 0xf2ee001c +-#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003 +- +-/*P2_TNRLAUNCH*/ +-#define R0900_P2_TNRLAUNCH 0xf2f0 +- +-/*P2_TNRLD*/ +-#define R0900_P2_TNRLD 0xf2f0 +-#define F0900_P2_TUNLD_VCOING 0xf2f00080 +-#define F0900_P2_TUN_REG1FAIL 0xf2f00040 +-#define F0900_P2_TUN_REG2FAIL 0xf2f00020 +-#define F0900_P2_TUN_REG3FAIL 0xf2f00010 +-#define F0900_P2_TUN_REG4FAIL 0xf2f00008 +-#define F0900_P2_TUN_REG5FAIL 0xf2f00004 +-#define F0900_P2_TUN_BWING 0xf2f00002 +-#define F0900_P2_TUN_LOCKED 0xf2f00001 +- +-/*P2_TNROBSL*/ +-#define R0900_P2_TNROBSL 0xf2f6 +-#define F0900_P2_TUN_I2CABORTED 0xf2f60080 +-#define F0900_P2_TUN_LPEN 0xf2f60040 +-#define F0900_P2_TUN_FCCK 0xf2f60020 +-#define F0900_P2_TUN_I2CLOCKED 0xf2f60010 +-#define F0900_P2_TUN_PROGDONE 0xf2f6000c +-#define F0900_P2_TUN_RFRESTE1 0xf2f60003 +- +-/*P2_TNRRESTE*/ +-#define R0900_P2_TNRRESTE 0xf2f7 +-#define F0900_P2_TUN_RFRESTE0 0xf2f700ff +- +-/*P2_SMAPCOEF7*/ +-#define R0900_P2_SMAPCOEF7 0xf300 +-#define F0900_P2_DIS_QSCALE 0xf3000080 +-#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f +- +-/*P2_SMAPCOEF6*/ +-#define R0900_P2_SMAPCOEF6 0xf301 +-#define F0900_P2_ADJ_8PSKLLR1 0xf3010004 +-#define F0900_P2_OLD_8PSKLLR1 0xf3010002 +-#define F0900_P2_DIS_AB8PSK 0xf3010001 +- +-/*P2_SMAPCOEF5*/ +-#define R0900_P2_SMAPCOEF5 0xf302 +-#define F0900_P2_DIS_8SCALE 0xf3020080 +-#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f +- +-/*P2_NCO2MAX1*/ +-#define R0900_P2_NCO2MAX1 0xf314 +-#define F0900_P2_TETA2_MAXVABS1 0xf31400ff +- +-/*P2_NCO2MAX0*/ +-#define R0900_P2_NCO2MAX0 0xf315 +-#define F0900_P2_TETA2_MAXVABS0 0xf31500ff +- +-/*P2_NCO2FR1*/ +-#define R0900_P2_NCO2FR1 0xf316 +-#define F0900_P2_NCO2FINAL_ANGLE1 0xf31600ff +- +-/*P2_NCO2FR0*/ +-#define R0900_P2_NCO2FR0 0xf317 +-#define F0900_P2_NCO2FINAL_ANGLE0 0xf31700ff +- +-/*P2_CFR2AVRGE1*/ +-#define R0900_P2_CFR2AVRGE1 0xf318 +-#define F0900_P2_I2C_CFR2AVERAGE1 0xf31800ff +- +-/*P2_CFR2AVRGE0*/ +-#define R0900_P2_CFR2AVRGE0 0xf319 +-#define F0900_P2_I2C_CFR2AVERAGE0 0xf31900ff +- +-/*P2_DMDPLHSTAT*/ +-#define R0900_P2_DMDPLHSTAT 0xf320 +-#define F0900_P2_PLH_STATISTIC 0xf32000ff +- +-/*P2_LOCKTIME3*/ +-#define R0900_P2_LOCKTIME3 0xf322 +-#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff +- +-/*P2_LOCKTIME2*/ +-#define R0900_P2_LOCKTIME2 0xf323 +-#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff +- +-/*P2_LOCKTIME1*/ +-#define R0900_P2_LOCKTIME1 0xf324 +-#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff +- +-/*P2_LOCKTIME0*/ +-#define R0900_P2_LOCKTIME0 0xf325 +-#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff +- +-/*P2_VITSCALE*/ +-#define R0900_P2_VITSCALE 0xf332 +-#define F0900_P2_NVTH_NOSRANGE 0xf3320080 +-#define F0900_P2_VERROR_MAXMODE 0xf3320040 +-#define F0900_P2_NSLOWSN_LOCKED 0xf3320008 +-#define F0900_P2_DIS_RSFLOCK 0xf3320002 +- +-/*P2_FECM*/ +-#define R0900_P2_FECM 0xf333 +-#define F0900_P2_DSS_DVB 0xf3330080 +-#define F0900_P2_DSS_SRCH 0xf3330010 +-#define F0900_P2_SYNCVIT 0xf3330002 +-#define F0900_P2_IQINV 0xf3330001 +- +-/*P2_VTH12*/ +-#define R0900_P2_VTH12 0xf334 +-#define F0900_P2_VTH12 0xf33400ff +- +-/*P2_VTH23*/ +-#define R0900_P2_VTH23 0xf335 +-#define F0900_P2_VTH23 0xf33500ff +- +-/*P2_VTH34*/ +-#define R0900_P2_VTH34 0xf336 +-#define F0900_P2_VTH34 0xf33600ff +- +-/*P2_VTH56*/ +-#define R0900_P2_VTH56 0xf337 +-#define F0900_P2_VTH56 0xf33700ff +- +-/*P2_VTH67*/ +-#define R0900_P2_VTH67 0xf338 +-#define F0900_P2_VTH67 0xf33800ff +- +-/*P2_VTH78*/ +-#define R0900_P2_VTH78 0xf339 +-#define F0900_P2_VTH78 0xf33900ff +- +-/*P2_VITCURPUN*/ +-#define R0900_P2_VITCURPUN 0xf33a +-#define F0900_P2_VIT_CURPUN 0xf33a001f +- +-/*P2_VERROR*/ +-#define R0900_P2_VERROR 0xf33b +-#define F0900_P2_REGERR_VIT 0xf33b00ff +- +-/*P2_PRVIT*/ +-#define R0900_P2_PRVIT 0xf33c +-#define F0900_P2_DIS_VTHLOCK 0xf33c0040 +-#define F0900_P2_E7_8VIT 0xf33c0020 +-#define F0900_P2_E6_7VIT 0xf33c0010 +-#define F0900_P2_E5_6VIT 0xf33c0008 +-#define F0900_P2_E3_4VIT 0xf33c0004 +-#define F0900_P2_E2_3VIT 0xf33c0002 +-#define F0900_P2_E1_2VIT 0xf33c0001 +- +-/*P2_VAVSRVIT*/ +-#define R0900_P2_VAVSRVIT 0xf33d +-#define F0900_P2_AMVIT 0xf33d0080 +-#define F0900_P2_FROZENVIT 0xf33d0040 +-#define F0900_P2_SNVIT 0xf33d0030 +-#define F0900_P2_TOVVIT 0xf33d000c +-#define F0900_P2_HYPVIT 0xf33d0003 +- +-/*P2_VSTATUSVIT*/ +-#define R0900_P2_VSTATUSVIT 0xf33e +-#define F0900_P2_PRFVIT 0xf33e0010 +-#define F0900_P2_LOCKEDVIT 0xf33e0008 +- +-/*P2_VTHINUSE*/ +-#define R0900_P2_VTHINUSE 0xf33f +-#define F0900_P2_VIT_INUSE 0xf33f00ff +- +-/*P2_KDIV12*/ +-#define R0900_P2_KDIV12 0xf340 +-#define F0900_P2_K_DIVIDER_12 0xf340007f +- +-/*P2_KDIV23*/ +-#define R0900_P2_KDIV23 0xf341 +-#define F0900_P2_K_DIVIDER_23 0xf341007f +- +-/*P2_KDIV34*/ +-#define R0900_P2_KDIV34 0xf342 +-#define F0900_P2_K_DIVIDER_34 0xf342007f +- +-/*P2_KDIV56*/ +-#define R0900_P2_KDIV56 0xf343 +-#define F0900_P2_K_DIVIDER_56 0xf343007f +- +-/*P2_KDIV67*/ +-#define R0900_P2_KDIV67 0xf344 +-#define F0900_P2_K_DIVIDER_67 0xf344007f +- +-/*P2_KDIV78*/ +-#define R0900_P2_KDIV78 0xf345 +-#define F0900_P2_K_DIVIDER_78 0xf345007f +- +-/*P2_PDELCTRL1*/ +-#define R0900_P2_PDELCTRL1 0xf350 +-#define F0900_P2_INV_MISMASK 0xf3500080 +-#define F0900_P2_FILTER_EN 0xf3500020 +-#define F0900_P2_EN_MIS00 0xf3500002 +-#define F0900_P2_ALGOSWRST 0xf3500001 +- +-/*P2_PDELCTRL2*/ +-#define R0900_P2_PDELCTRL2 0xf351 +-#define F0900_P2_RESET_UPKO_COUNT 0xf3510040 +-#define F0900_P2_FRAME_MODE 0xf3510002 +-#define F0900_P2_NOBCHERRFLG_USE 0xf3510001 +- +-/*P2_HYSTTHRESH*/ +-#define R0900_P2_HYSTTHRESH 0xf354 +-#define F0900_P2_UNLCK_THRESH 0xf35400f0 +-#define F0900_P2_DELIN_LCK_THRESH 0xf354000f +- +-/*P2_ISIENTRY*/ +-#define R0900_P2_ISIENTRY 0xf35e +-#define F0900_P2_ISI_ENTRY 0xf35e00ff +- +-/*P2_ISIBITENA*/ +-#define R0900_P2_ISIBITENA 0xf35f +-#define F0900_P2_ISI_BIT_EN 0xf35f00ff +- +-/*P2_MATSTR1*/ +-#define R0900_P2_MATSTR1 0xf360 +-#define F0900_P2_MATYPE_CURRENT1 0xf36000ff +- +-/*P2_MATSTR0*/ +-#define R0900_P2_MATSTR0 0xf361 +-#define F0900_P2_MATYPE_CURRENT0 0xf36100ff +- +-/*P2_UPLSTR1*/ +-#define R0900_P2_UPLSTR1 0xf362 +-#define F0900_P2_UPL_CURRENT1 0xf36200ff +- +-/*P2_UPLSTR0*/ +-#define R0900_P2_UPLSTR0 0xf363 +-#define F0900_P2_UPL_CURRENT0 0xf36300ff +- +-/*P2_DFLSTR1*/ +-#define R0900_P2_DFLSTR1 0xf364 +-#define F0900_P2_DFL_CURRENT1 0xf36400ff +- +-/*P2_DFLSTR0*/ +-#define R0900_P2_DFLSTR0 0xf365 +-#define F0900_P2_DFL_CURRENT0 0xf36500ff +- +-/*P2_SYNCSTR*/ +-#define R0900_P2_SYNCSTR 0xf366 +-#define F0900_P2_SYNC_CURRENT 0xf36600ff +- +-/*P2_SYNCDSTR1*/ +-#define R0900_P2_SYNCDSTR1 0xf367 +-#define F0900_P2_SYNCD_CURRENT1 0xf36700ff +- +-/*P2_SYNCDSTR0*/ +-#define R0900_P2_SYNCDSTR0 0xf368 +-#define F0900_P2_SYNCD_CURRENT0 0xf36800ff +- +-/*P2_PDELSTATUS1*/ +-#define R0900_P2_PDELSTATUS1 0xf369 +-#define F0900_P2_PKTDELIN_DELOCK 0xf3690080 +-#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040 +-#define F0900_P2_CONTINUOUS_STREAM 0xf3690020 +-#define F0900_P2_UNACCEPTED_STREAM 0xf3690010 +-#define F0900_P2_BCH_ERROR_FLAG 0xf3690008 +-#define F0900_P2_PKTDELIN_LOCK 0xf3690002 +-#define F0900_P2_FIRST_LOCK 0xf3690001 +- +-/*P2_PDELSTATUS2*/ +-#define R0900_P2_PDELSTATUS2 0xf36a +-#define F0900_P2_FRAME_MODCOD 0xf36a007c +-#define F0900_P2_FRAME_TYPE 0xf36a0003 +- +-/*P2_BBFCRCKO1*/ +-#define R0900_P2_BBFCRCKO1 0xf36b +-#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff +- +-/*P2_BBFCRCKO0*/ +-#define R0900_P2_BBFCRCKO0 0xf36c +-#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff +- +-/*P2_UPCRCKO1*/ +-#define R0900_P2_UPCRCKO1 0xf36d +-#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff +- +-/*P2_UPCRCKO0*/ +-#define R0900_P2_UPCRCKO0 0xf36e +-#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff +- +-/*P2_PDELCTRL3*/ +-#define R0900_P2_PDELCTRL3 0xf36f +-#define F0900_P2_PKTDEL_CONTFAIL 0xf36f0080 +-#define F0900_P2_NOFIFO_BCHERR 0xf36f0020 +- +-/*P2_TSSTATEM*/ +-#define R0900_P2_TSSTATEM 0xf370 +-#define F0900_P2_TSDIL_ON 0xf3700080 +-#define F0900_P2_TSRS_ON 0xf3700020 +-#define F0900_P2_TSDESCRAMB_ON 0xf3700010 +-#define F0900_P2_TSFRAME_MODE 0xf3700008 +-#define F0900_P2_TS_DISABLE 0xf3700004 +-#define F0900_P2_TSOUT_NOSYNC 0xf3700001 +- +-/*P2_TSCFGH*/ +-#define R0900_P2_TSCFGH 0xf372 +-#define F0900_P2_TSFIFO_DVBCI 0xf3720080 +-#define F0900_P2_TSFIFO_SERIAL 0xf3720040 +-#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020 +-#define F0900_P2_TSFIFO_DUTY50 0xf3720010 +-#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008 +-#define F0900_P2_TSFIFO_ERRMODE 0xf3720006 +-#define F0900_P2_RST_HWARE 0xf3720001 +- +-/*P2_TSCFGM*/ +-#define R0900_P2_TSCFGM 0xf373 +-#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0 +-#define F0900_P2_TSFIFO_PERMDATA 0xf3730020 +-#define F0900_P2_TSFIFO_DPUNACT 0xf3730002 +-#define F0900_P2_TSFIFO_INVDATA 0xf3730001 +- +-/*P2_TSCFGL*/ +-#define R0900_P2_TSCFGL 0xf374 +-#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0 +-#define F0900_P2_BCHERROR_MODE 0xf3740030 +-#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008 +-#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004 +-#define F0900_P2_TSFIFO_BITSPEED 0xf3740003 +- +-/*P2_TSINSDELH*/ +-#define R0900_P2_TSINSDELH 0xf376 +-#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080 +-#define F0900_P2_TSDEL_XXHEADER 0xf3760040 +-#define F0900_P2_TSDEL_BBHEADER 0xf3760020 +-#define F0900_P2_TSDEL_DATAFIELD 0xf3760010 +-#define F0900_P2_TSINSDEL_ISCR 0xf3760008 +-#define F0900_P2_TSINSDEL_NPD 0xf3760004 +-#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002 +-#define F0900_P2_TSINSDEL_CRC8 0xf3760001 +- +-/*P2_TSDIVN*/ +-#define R0900_P2_TSDIVN 0xf379 +-#define F0900_P2_TSFIFO_SPEEDMODE 0xf37900c0 +- +-/*P2_TSCFG4*/ +-#define R0900_P2_TSCFG4 0xf37a +-#define F0900_P2_TSFIFO_TSSPEEDMODE 0xf37a00c0 +- +-/*P2_TSSPEED*/ +-#define R0900_P2_TSSPEED 0xf380 +-#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff +- +-/*P2_TSSTATUS*/ +-#define R0900_P2_TSSTATUS 0xf381 +-#define F0900_P2_TSFIFO_LINEOK 0xf3810080 +-#define F0900_P2_TSFIFO_ERROR 0xf3810040 +-#define F0900_P2_DIL_READY 0xf3810001 +- +-/*P2_TSSTATUS2*/ +-#define R0900_P2_TSSTATUS2 0xf382 +-#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080 +-#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040 +-#define F0900_P2_DILXX_RESET 0xf3820020 +-#define F0900_P2_TSSERIAL_IMPOS 0xf3820010 +-#define F0900_P2_SCRAMBDETECT 0xf3820002 +- +-/*P2_TSBITRATE1*/ +-#define R0900_P2_TSBITRATE1 0xf383 +-#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff +- +-/*P2_TSBITRATE0*/ +-#define R0900_P2_TSBITRATE0 0xf384 +-#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff +- +-/*P2_ERRCTRL1*/ +-#define R0900_P2_ERRCTRL1 0xf398 +-#define F0900_P2_ERR_SOURCE1 0xf39800f0 +-#define F0900_P2_NUM_EVENT1 0xf3980007 +- +-/*P2_ERRCNT12*/ +-#define R0900_P2_ERRCNT12 0xf399 +-#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080 +-#define F0900_P2_ERR_CNT12 0xf399007f +- +-/*P2_ERRCNT11*/ +-#define R0900_P2_ERRCNT11 0xf39a +-#define F0900_P2_ERR_CNT11 0xf39a00ff +- +-/*P2_ERRCNT10*/ +-#define R0900_P2_ERRCNT10 0xf39b +-#define F0900_P2_ERR_CNT10 0xf39b00ff +- +-/*P2_ERRCTRL2*/ +-#define R0900_P2_ERRCTRL2 0xf39c +-#define F0900_P2_ERR_SOURCE2 0xf39c00f0 +-#define F0900_P2_NUM_EVENT2 0xf39c0007 +- +-/*P2_ERRCNT22*/ +-#define R0900_P2_ERRCNT22 0xf39d +-#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080 +-#define F0900_P2_ERR_CNT22 0xf39d007f +- +-/*P2_ERRCNT21*/ +-#define R0900_P2_ERRCNT21 0xf39e +-#define F0900_P2_ERR_CNT21 0xf39e00ff +- +-/*P2_ERRCNT20*/ +-#define R0900_P2_ERRCNT20 0xf39f +-#define F0900_P2_ERR_CNT20 0xf39f00ff +- +-/*P2_FECSPY*/ +-#define R0900_P2_FECSPY 0xf3a0 +-#define F0900_P2_SPY_ENABLE 0xf3a00080 +-#define F0900_P2_NO_SYNCBYTE 0xf3a00040 +-#define F0900_P2_SERIAL_MODE 0xf3a00020 +-#define F0900_P2_UNUSUAL_PACKET 0xf3a00010 +-#define F0900_P2_BERMETER_DATAMODE 0xf3a00008 +-#define F0900_P2_BERMETER_LMODE 0xf3a00002 +-#define F0900_P2_BERMETER_RESET 0xf3a00001 +- +-/*P2_FSPYCFG*/ +-#define R0900_P2_FSPYCFG 0xf3a1 +-#define F0900_P2_FECSPY_INPUT 0xf3a100c0 +-#define F0900_P2_RST_ON_ERROR 0xf3a10020 +-#define F0900_P2_ONE_SHOT 0xf3a10010 +-#define F0900_P2_I2C_MODE 0xf3a1000c +-#define F0900_P2_SPY_HYSTERESIS 0xf3a10003 +- +-/*P2_FSPYDATA*/ +-#define R0900_P2_FSPYDATA 0xf3a2 +-#define F0900_P2_SPY_STUFFING 0xf3a20080 +-#define F0900_P2_SPY_CNULLPKT 0xf3a20020 +-#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f +- +-/*P2_FSPYOUT*/ +-#define R0900_P2_FSPYOUT 0xf3a3 +-#define F0900_P2_FSPY_DIRECT 0xf3a30080 +-#define F0900_P2_STUFF_MODE 0xf3a30007 +- +-/*P2_FSTATUS*/ +-#define R0900_P2_FSTATUS 0xf3a4 +-#define F0900_P2_SPY_ENDSIM 0xf3a40080 +-#define F0900_P2_VALID_SIM 0xf3a40040 +-#define F0900_P2_FOUND_SIGNAL 0xf3a40020 +-#define F0900_P2_DSS_SYNCBYTE 0xf3a40010 +-#define F0900_P2_RESULT_STATE 0xf3a4000f +- +-/*P2_FBERCPT4*/ +-#define R0900_P2_FBERCPT4 0xf3a8 +-#define F0900_P2_FBERMETER_CPT4 0xf3a800ff +- +-/*P2_FBERCPT3*/ +-#define R0900_P2_FBERCPT3 0xf3a9 +-#define F0900_P2_FBERMETER_CPT3 0xf3a900ff +- +-/*P2_FBERCPT2*/ +-#define R0900_P2_FBERCPT2 0xf3aa +-#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff +- +-/*P2_FBERCPT1*/ +-#define R0900_P2_FBERCPT1 0xf3ab +-#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff +- +-/*P2_FBERCPT0*/ +-#define R0900_P2_FBERCPT0 0xf3ac +-#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff +- +-/*P2_FBERERR2*/ +-#define R0900_P2_FBERERR2 0xf3ad +-#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff +- +-/*P2_FBERERR1*/ +-#define R0900_P2_FBERERR1 0xf3ae +-#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff +- +-/*P2_FBERERR0*/ +-#define R0900_P2_FBERERR0 0xf3af +-#define F0900_P2_FBERMETER_ERR0 0xf3af00ff +- +-/*P2_FSPYBER*/ +-#define R0900_P2_FSPYBER 0xf3b2 +-#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010 +-#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008 +-#define F0900_P2_FSPYBER_CTIME 0xf3b20007 +- +-/*P1_IQCONST*/ +-#define R0900_P1_IQCONST 0xf400 +-#define IQCONST REGx(R0900_P1_IQCONST) +-#define F0900_P1_CONSTEL_SELECT 0xf4000060 +-#define F0900_P1_IQSYMB_SEL 0xf400001f +- +-/*P1_NOSCFG*/ +-#define R0900_P1_NOSCFG 0xf401 +-#define NOSCFG REGx(R0900_P1_NOSCFG) +-#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020 +-#define F0900_P1_NOSPLH_BETA 0xf4010018 +-#define F0900_P1_NOSDATA_BETA 0xf4010007 +- +-/*P1_ISYMB*/ +-#define R0900_P1_ISYMB 0xf402 +-#define ISYMB REGx(R0900_P1_ISYMB) +-#define F0900_P1_I_SYMBOL 0xf40201ff +- +-/*P1_QSYMB*/ +-#define R0900_P1_QSYMB 0xf403 +-#define QSYMB REGx(R0900_P1_QSYMB) +-#define F0900_P1_Q_SYMBOL 0xf40301ff +- +-/*P1_AGC1CFG*/ +-#define R0900_P1_AGC1CFG 0xf404 +-#define AGC1CFG REGx(R0900_P1_AGC1CFG) +-#define F0900_P1_DC_FROZEN 0xf4040080 +-#define F0900_P1_DC_CORRECT 0xf4040040 +-#define F0900_P1_AMM_FROZEN 0xf4040020 +-#define F0900_P1_AMM_CORRECT 0xf4040010 +-#define F0900_P1_QUAD_FROZEN 0xf4040008 +-#define F0900_P1_QUAD_CORRECT 0xf4040004 +- +-/*P1_AGC1CN*/ +-#define R0900_P1_AGC1CN 0xf406 +-#define AGC1CN REGx(R0900_P1_AGC1CN) +-#define F0900_P1_AGC1_LOCKED 0xf4060080 +-#define F0900_P1_AGC1_MINPOWER 0xf4060010 +-#define F0900_P1_AGCOUT_FAST 0xf4060008 +-#define F0900_P1_AGCIQ_BETA 0xf4060007 +- +-/*P1_AGC1REF*/ +-#define R0900_P1_AGC1REF 0xf407 +-#define AGC1REF REGx(R0900_P1_AGC1REF) +-#define F0900_P1_AGCIQ_REF 0xf40700ff +- +-/*P1_IDCCOMP*/ +-#define R0900_P1_IDCCOMP 0xf408 +-#define IDCCOMP REGx(R0900_P1_IDCCOMP) +-#define F0900_P1_IAVERAGE_ADJ 0xf40801ff +- +-/*P1_QDCCOMP*/ +-#define R0900_P1_QDCCOMP 0xf409 +-#define QDCCOMP REGx(R0900_P1_QDCCOMP) +-#define F0900_P1_QAVERAGE_ADJ 0xf40901ff +- +-/*P1_POWERI*/ +-#define R0900_P1_POWERI 0xf40a +-#define POWERI REGx(R0900_P1_POWERI) +-#define F0900_P1_POWER_I 0xf40a00ff +-#define POWER_I FLDx(F0900_P1_POWER_I) +- +-/*P1_POWERQ*/ +-#define R0900_P1_POWERQ 0xf40b +-#define POWERQ REGx(R0900_P1_POWERQ) +-#define F0900_P1_POWER_Q 0xf40b00ff +-#define POWER_Q FLDx(F0900_P1_POWER_Q) +- +-/*P1_AGC1AMM*/ +-#define R0900_P1_AGC1AMM 0xf40c +-#define AGC1AMM REGx(R0900_P1_AGC1AMM) +-#define F0900_P1_AMM_VALUE 0xf40c00ff +- +-/*P1_AGC1QUAD*/ +-#define R0900_P1_AGC1QUAD 0xf40d +-#define AGC1QUAD REGx(R0900_P1_AGC1QUAD) +-#define F0900_P1_QUAD_VALUE 0xf40d01ff +- +-/*P1_AGCIQIN1*/ +-#define R0900_P1_AGCIQIN1 0xf40e +-#define AGCIQIN1 REGx(R0900_P1_AGCIQIN1) +-#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff +-#define AGCIQ_VALUE1 FLDx(F0900_P1_AGCIQ_VALUE1) +- +-/*P1_AGCIQIN0*/ +-#define R0900_P1_AGCIQIN0 0xf40f +-#define AGCIQIN0 REGx(R0900_P1_AGCIQIN0) +-#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff +-#define AGCIQ_VALUE0 FLDx(F0900_P1_AGCIQ_VALUE0) +- +-/*P1_DEMOD*/ +-#define R0900_P1_DEMOD 0xf410 +-#define DEMOD REGx(R0900_P1_DEMOD) +-#define F0900_P1_MANUALS2_ROLLOFF 0xf4100080 +-#define MANUALS2_ROLLOFF FLDx(F0900_P1_MANUALS2_ROLLOFF) +- +-#define F0900_P1_SPECINV_CONTROL 0xf4100030 +-#define SPECINV_CONTROL FLDx(F0900_P1_SPECINV_CONTROL) +-#define F0900_P1_FORCE_ENASAMP 0xf4100008 +-#define F0900_P1_MANUALSX_ROLLOFF 0xf4100004 +-#define MANUALSX_ROLLOFF FLDx(F0900_P1_MANUALSX_ROLLOFF) +-#define F0900_P1_ROLLOFF_CONTROL 0xf4100003 +-#define ROLLOFF_CONTROL FLDx(F0900_P1_ROLLOFF_CONTROL) +- +-/*P1_DMDMODCOD*/ +-#define R0900_P1_DMDMODCOD 0xf411 +-#define DMDMODCOD REGx(R0900_P1_DMDMODCOD) +-#define F0900_P1_MANUAL_MODCOD 0xf4110080 +-#define F0900_P1_DEMOD_MODCOD 0xf411007c +-#define DEMOD_MODCOD FLDx(F0900_P1_DEMOD_MODCOD) +-#define F0900_P1_DEMOD_TYPE 0xf4110003 +-#define DEMOD_TYPE FLDx(F0900_P1_DEMOD_TYPE) +- +-/*P1_DSTATUS*/ +-#define R0900_P1_DSTATUS 0xf412 +-#define DSTATUS REGx(R0900_P1_DSTATUS) +-#define F0900_P1_CAR_LOCK 0xf4120080 +-#define F0900_P1_TMGLOCK_QUALITY 0xf4120060 +-#define TMGLOCK_QUALITY FLDx(F0900_P1_TMGLOCK_QUALITY) +-#define F0900_P1_LOCK_DEFINITIF 0xf4120008 +-#define LOCK_DEFINITIF FLDx(F0900_P1_LOCK_DEFINITIF) +-#define F0900_P1_OVADC_DETECT 0xf4120001 +- +-/*P1_DSTATUS2*/ +-#define R0900_P1_DSTATUS2 0xf413 +-#define DSTATUS2 REGx(R0900_P1_DSTATUS2) +-#define F0900_P1_DEMOD_DELOCK 0xf4130080 +-#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008 +-#define F0900_P1_AGC2_OVERFLOW 0xf4130004 +-#define F0900_P1_CFR_OVERFLOW 0xf4130002 +-#define F0900_P1_GAMMA_OVERUNDER 0xf4130001 +- +-/*P1_DMDCFGMD*/ +-#define R0900_P1_DMDCFGMD 0xf414 +-#define DMDCFGMD REGx(R0900_P1_DMDCFGMD) +-#define F0900_P1_DVBS2_ENABLE 0xf4140080 +-#define DVBS2_ENABLE FLDx(F0900_P1_DVBS2_ENABLE) +-#define F0900_P1_DVBS1_ENABLE 0xf4140040 +-#define DVBS1_ENABLE FLDx(F0900_P1_DVBS1_ENABLE) +-#define F0900_P1_SCAN_ENABLE 0xf4140010 +-#define SCAN_ENABLE FLDx(F0900_P1_SCAN_ENABLE) +-#define F0900_P1_CFR_AUTOSCAN 0xf4140008 +-#define CFR_AUTOSCAN FLDx(F0900_P1_CFR_AUTOSCAN) +-#define F0900_P1_TUN_RNG 0xf4140003 +- +-/*P1_DMDCFG2*/ +-#define R0900_P1_DMDCFG2 0xf415 +-#define DMDCFG2 REGx(R0900_P1_DMDCFG2) +-#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040 +-#define S1S2_SEQUENTIAL FLDx(F0900_P1_S1S2_SEQUENTIAL) +-#define F0900_P1_INFINITE_RELOCK 0xf4150010 +- +-/*P1_DMDISTATE*/ +-#define R0900_P1_DMDISTATE 0xf416 +-#define DMDISTATE REGx(R0900_P1_DMDISTATE) +-#define F0900_P1_I2C_DEMOD_MODE 0xf416001f +-#define DEMOD_MODE FLDx(F0900_P1_I2C_DEMOD_MODE) +- +-/*P1_DMDT0M*/ +-#define R0900_P1_DMDT0M 0xf417 +-#define DMDT0M REGx(R0900_P1_DMDT0M) +-#define F0900_P1_DMDT0_MIN 0xf41700ff +- +-/*P1_DMDSTATE*/ +-#define R0900_P1_DMDSTATE 0xf41b +-#define DMDSTATE REGx(R0900_P1_DMDSTATE) +-#define F0900_P1_HEADER_MODE 0xf41b0060 +-#define HEADER_MODE FLDx(F0900_P1_HEADER_MODE) +- +-/*P1_DMDFLYW*/ +-#define R0900_P1_DMDFLYW 0xf41c +-#define DMDFLYW REGx(R0900_P1_DMDFLYW) +-#define F0900_P1_I2C_IRQVAL 0xf41c00f0 +-#define F0900_P1_FLYWHEEL_CPT 0xf41c000f +-#define FLYWHEEL_CPT FLDx(F0900_P1_FLYWHEEL_CPT) +- +-/*P1_DSTATUS3*/ +-#define R0900_P1_DSTATUS3 0xf41d +-#define DSTATUS3 REGx(R0900_P1_DSTATUS3) +-#define F0900_P1_DEMOD_CFGMODE 0xf41d0060 +- +-/*P1_DMDCFG3*/ +-#define R0900_P1_DMDCFG3 0xf41e +-#define DMDCFG3 REGx(R0900_P1_DMDCFG3) +-#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008 +- +-/*P1_DMDCFG4*/ +-#define R0900_P1_DMDCFG4 0xf41f +-#define DMDCFG4 REGx(R0900_P1_DMDCFG4) +-#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008 +- +-/*P1_CORRELMANT*/ +-#define R0900_P1_CORRELMANT 0xf420 +-#define CORRELMANT REGx(R0900_P1_CORRELMANT) +-#define F0900_P1_CORREL_MANT 0xf42000ff +- +-/*P1_CORRELABS*/ +-#define R0900_P1_CORRELABS 0xf421 +-#define CORRELABS REGx(R0900_P1_CORRELABS) +-#define F0900_P1_CORREL_ABS 0xf42100ff +- +-/*P1_CORRELEXP*/ +-#define R0900_P1_CORRELEXP 0xf422 +-#define CORRELEXP REGx(R0900_P1_CORRELEXP) +-#define F0900_P1_CORREL_ABSEXP 0xf42200f0 +-#define F0900_P1_CORREL_EXP 0xf422000f +- +-/*P1_PLHMODCOD*/ +-#define R0900_P1_PLHMODCOD 0xf424 +-#define PLHMODCOD REGx(R0900_P1_PLHMODCOD) +-#define F0900_P1_SPECINV_DEMOD 0xf4240080 +-#define SPECINV_DEMOD FLDx(F0900_P1_SPECINV_DEMOD) +-#define F0900_P1_PLH_MODCOD 0xf424007c +-#define F0900_P1_PLH_TYPE 0xf4240003 +- +-/*P1_DMDREG*/ +-#define R0900_P1_DMDREG 0xf425 +-#define DMDREG REGx(R0900_P1_DMDREG) +-#define F0900_P1_DECIM_PLFRAMES 0xf4250001 +- +-/*P1_AGC2O*/ +-#define R0900_P1_AGC2O 0xf42c +-#define AGC2O REGx(R0900_P1_AGC2O) +-#define F0900_P1_AGC2_COEF 0xf42c0007 +- +-/*P1_AGC2REF*/ +-#define R0900_P1_AGC2REF 0xf42d +-#define AGC2REF REGx(R0900_P1_AGC2REF) +-#define F0900_P1_AGC2_REF 0xf42d00ff +- +-/*P1_AGC1ADJ*/ +-#define R0900_P1_AGC1ADJ 0xf42e +-#define AGC1ADJ REGx(R0900_P1_AGC1ADJ) +-#define F0900_P1_AGC1_ADJUSTED 0xf42e007f +- +-/*P1_AGC2I1*/ +-#define R0900_P1_AGC2I1 0xf436 +-#define AGC2I1 REGx(R0900_P1_AGC2I1) +-#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff +- +-/*P1_AGC2I0*/ +-#define R0900_P1_AGC2I0 0xf437 +-#define AGC2I0 REGx(R0900_P1_AGC2I0) +-#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff +- +-/*P1_CARCFG*/ +-#define R0900_P1_CARCFG 0xf438 +-#define CARCFG REGx(R0900_P1_CARCFG) +-#define F0900_P1_CFRUPLOW_AUTO 0xf4380080 +-#define F0900_P1_CFRUPLOW_TEST 0xf4380040 +-#define F0900_P1_ROTAON 0xf4380004 +-#define F0900_P1_PH_DET_ALGO 0xf4380003 +- +-/*P1_ACLC*/ +-#define R0900_P1_ACLC 0xf439 +-#define ACLC REGx(R0900_P1_ACLC) +-#define F0900_P1_CAR_ALPHA_MANT 0xf4390030 +-#define F0900_P1_CAR_ALPHA_EXP 0xf439000f +- +-/*P1_BCLC*/ +-#define R0900_P1_BCLC 0xf43a +-#define BCLC REGx(R0900_P1_BCLC) +-#define F0900_P1_CAR_BETA_MANT 0xf43a0030 +-#define F0900_P1_CAR_BETA_EXP 0xf43a000f +- +-/*P1_CARFREQ*/ +-#define R0900_P1_CARFREQ 0xf43d +-#define CARFREQ REGx(R0900_P1_CARFREQ) +-#define F0900_P1_KC_COARSE_EXP 0xf43d00f0 +-#define F0900_P1_BETA_FREQ 0xf43d000f +- +-/*P1_CARHDR*/ +-#define R0900_P1_CARHDR 0xf43e +-#define CARHDR REGx(R0900_P1_CARHDR) +-#define F0900_P1_K_FREQ_HDR 0xf43e00ff +- +-/*P1_LDT*/ +-#define R0900_P1_LDT 0xf43f +-#define LDT REGx(R0900_P1_LDT) +-#define F0900_P1_CARLOCK_THRES 0xf43f01ff +- +-/*P1_LDT2*/ +-#define R0900_P1_LDT2 0xf440 +-#define LDT2 REGx(R0900_P1_LDT2) +-#define F0900_P1_CARLOCK_THRES2 0xf44001ff +- +-/*P1_CFRICFG*/ +-#define R0900_P1_CFRICFG 0xf441 +-#define CFRICFG REGx(R0900_P1_CFRICFG) +-#define F0900_P1_NEG_CFRSTEP 0xf4410001 +- +-/*P1_CFRUP1*/ +-#define R0900_P1_CFRUP1 0xf442 +-#define CFRUP1 REGx(R0900_P1_CFRUP1) +-#define F0900_P1_CFR_UP1 0xf44201ff +-#define CFR_UP1 FLDx(F0900_P1_CFR_UP1) +- +-/*P1_CFRUP0*/ +-#define R0900_P1_CFRUP0 0xf443 +-#define CFRUP0 REGx(R0900_P1_CFRUP0) +-#define F0900_P1_CFR_UP0 0xf44300ff +-#define CFR_UP0 FLDx(F0900_P1_CFR_UP0) +- +-/*P1_CFRLOW1*/ +-#define R0900_P1_CFRLOW1 0xf446 +-#define CFRLOW1 REGx(R0900_P1_CFRLOW1) +-#define F0900_P1_CFR_LOW1 0xf44601ff +-#define CFR_LOW1 FLDx(F0900_P1_CFR_LOW1) +- +-/*P1_CFRLOW0*/ +-#define R0900_P1_CFRLOW0 0xf447 +-#define CFRLOW0 REGx(R0900_P1_CFRLOW0) +-#define F0900_P1_CFR_LOW0 0xf44700ff +-#define CFR_LOW0 FLDx(F0900_P1_CFR_LOW0) +- +-/*P1_CFRINIT1*/ +-#define R0900_P1_CFRINIT1 0xf448 +-#define CFRINIT1 REGx(R0900_P1_CFRINIT1) +-#define F0900_P1_CFR_INIT1 0xf44801ff +-#define CFR_INIT1 FLDx(F0900_P1_CFR_INIT1) +- +-/*P1_CFRINIT0*/ +-#define R0900_P1_CFRINIT0 0xf449 +-#define CFRINIT0 REGx(R0900_P1_CFRINIT0) +-#define F0900_P1_CFR_INIT0 0xf44900ff +-#define CFR_INIT0 FLDx(F0900_P1_CFR_INIT0) +- +-/*P1_CFRINC1*/ +-#define R0900_P1_CFRINC1 0xf44a +-#define CFRINC1 REGx(R0900_P1_CFRINC1) +-#define F0900_P1_MANUAL_CFRINC 0xf44a0080 +-#define F0900_P1_CFR_INC1 0xf44a003f +- +-/*P1_CFRINC0*/ +-#define R0900_P1_CFRINC0 0xf44b +-#define CFRINC0 REGx(R0900_P1_CFRINC0) +-#define F0900_P1_CFR_INC0 0xf44b00f8 +- +-/*P1_CFR2*/ +-#define R0900_P1_CFR2 0xf44c +-#define CFR2 REGx(R0900_P1_CFR2) +-#define F0900_P1_CAR_FREQ2 0xf44c01ff +-#define CAR_FREQ2 FLDx(F0900_P1_CAR_FREQ2) +- +-/*P1_CFR1*/ +-#define R0900_P1_CFR1 0xf44d +-#define CFR1 REGx(R0900_P1_CFR1) +-#define F0900_P1_CAR_FREQ1 0xf44d00ff +-#define CAR_FREQ1 FLDx(F0900_P1_CAR_FREQ1) +- +-/*P1_CFR0*/ +-#define R0900_P1_CFR0 0xf44e +-#define CFR0 REGx(R0900_P1_CFR0) +-#define F0900_P1_CAR_FREQ0 0xf44e00ff +-#define CAR_FREQ0 FLDx(F0900_P1_CAR_FREQ0) +- +-/*P1_LDI*/ +-#define R0900_P1_LDI 0xf44f +-#define LDI REGx(R0900_P1_LDI) +-#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff +- +-/*P1_TMGCFG*/ +-#define R0900_P1_TMGCFG 0xf450 +-#define TMGCFG REGx(R0900_P1_TMGCFG) +-#define F0900_P1_TMGLOCK_BETA 0xf45000c0 +-#define F0900_P1_DO_TIMING_CORR 0xf4500010 +-#define F0900_P1_TMG_MINFREQ 0xf4500003 +- +-/*P1_RTC*/ +-#define R0900_P1_RTC 0xf451 +-#define RTC REGx(R0900_P1_RTC) +-#define F0900_P1_TMGALPHA_EXP 0xf45100f0 +-#define F0900_P1_TMGBETA_EXP 0xf451000f +- +-/*P1_RTCS2*/ +-#define R0900_P1_RTCS2 0xf452 +-#define RTCS2 REGx(R0900_P1_RTCS2) +-#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0 +-#define F0900_P1_TMGBETAS2_EXP 0xf452000f +- +-/*P1_TMGTHRISE*/ +-#define R0900_P1_TMGTHRISE 0xf453 +-#define TMGTHRISE REGx(R0900_P1_TMGTHRISE) +-#define F0900_P1_TMGLOCK_THRISE 0xf45300ff +- +-/*P1_TMGTHFALL*/ +-#define R0900_P1_TMGTHFALL 0xf454 +-#define TMGTHFALL REGx(R0900_P1_TMGTHFALL) +-#define F0900_P1_TMGLOCK_THFALL 0xf45400ff +- +-/*P1_SFRUPRATIO*/ +-#define R0900_P1_SFRUPRATIO 0xf455 +-#define SFRUPRATIO REGx(R0900_P1_SFRUPRATIO) +-#define F0900_P1_SFR_UPRATIO 0xf45500ff +- +-/*P1_SFRLOWRATIO*/ +-#define R0900_P1_SFRLOWRATIO 0xf456 +-#define F0900_P1_SFR_LOWRATIO 0xf45600ff +- +-/*P1_KREFTMG*/ +-#define R0900_P1_KREFTMG 0xf458 +-#define KREFTMG REGx(R0900_P1_KREFTMG) +-#define F0900_P1_KREF_TMG 0xf45800ff +- +-/*P1_SFRSTEP*/ +-#define R0900_P1_SFRSTEP 0xf459 +-#define SFRSTEP REGx(R0900_P1_SFRSTEP) +-#define F0900_P1_SFR_SCANSTEP 0xf45900f0 +-#define F0900_P1_SFR_CENTERSTEP 0xf459000f +- +-/*P1_TMGCFG2*/ +-#define R0900_P1_TMGCFG2 0xf45a +-#define TMGCFG2 REGx(R0900_P1_TMGCFG2) +-#define F0900_P1_SFRRATIO_FINE 0xf45a0001 +- +-/*P1_KREFTMG2*/ +-#define R0900_P1_KREFTMG2 0xf45b +-#define KREFTMG2 REGx(R0900_P1_KREFTMG2) +-#define F0900_P1_KREF_TMG2 0xf45b00ff +- +-/*P1_SFRINIT1*/ +-#define R0900_P1_SFRINIT1 0xf45e +-#define SFRINIT1 REGx(R0900_P1_SFRINIT1) +-#define F0900_P1_SFR_INIT1 0xf45e007f +- +-/*P1_SFRINIT0*/ +-#define R0900_P1_SFRINIT0 0xf45f +-#define SFRINIT0 REGx(R0900_P1_SFRINIT0) +-#define F0900_P1_SFR_INIT0 0xf45f00ff +- +-/*P1_SFRUP1*/ +-#define R0900_P1_SFRUP1 0xf460 +-#define SFRUP1 REGx(R0900_P1_SFRUP1) +-#define F0900_P1_AUTO_GUP 0xf4600080 +-#define AUTO_GUP FLDx(F0900_P1_AUTO_GUP) +-#define F0900_P1_SYMB_FREQ_UP1 0xf460007f +- +-/*P1_SFRUP0*/ +-#define R0900_P1_SFRUP0 0xf461 +-#define SFRUP0 REGx(R0900_P1_SFRUP0) +-#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff +- +-/*P1_SFRLOW1*/ +-#define R0900_P1_SFRLOW1 0xf462 +-#define SFRLOW1 REGx(R0900_P1_SFRLOW1) +-#define F0900_P1_AUTO_GLOW 0xf4620080 +-#define AUTO_GLOW FLDx(F0900_P1_AUTO_GLOW) +-#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f +- +-/*P1_SFRLOW0*/ +-#define R0900_P1_SFRLOW0 0xf463 +-#define SFRLOW0 REGx(R0900_P1_SFRLOW0) +-#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff +- +-/*P1_SFR3*/ +-#define R0900_P1_SFR3 0xf464 +-#define SFR3 REGx(R0900_P1_SFR3) +-#define F0900_P1_SYMB_FREQ3 0xf46400ff +-#define SYMB_FREQ3 FLDx(F0900_P1_SYMB_FREQ3) +- +-/*P1_SFR2*/ +-#define R0900_P1_SFR2 0xf465 +-#define SFR2 REGx(R0900_P1_SFR2) +-#define F0900_P1_SYMB_FREQ2 0xf46500ff +-#define SYMB_FREQ2 FLDx(F0900_P1_SYMB_FREQ2) +- +-/*P1_SFR1*/ +-#define R0900_P1_SFR1 0xf466 +-#define SFR1 REGx(R0900_P1_SFR1) +-#define F0900_P1_SYMB_FREQ1 0xf46600ff +-#define SYMB_FREQ1 FLDx(F0900_P1_SYMB_FREQ1) +- +-/*P1_SFR0*/ +-#define R0900_P1_SFR0 0xf467 +-#define SFR0 REGx(R0900_P1_SFR0) +-#define F0900_P1_SYMB_FREQ0 0xf46700ff +-#define SYMB_FREQ0 FLDx(F0900_P1_SYMB_FREQ0) +- +-/*P1_TMGREG2*/ +-#define R0900_P1_TMGREG2 0xf468 +-#define TMGREG2 REGx(R0900_P1_TMGREG2) +-#define F0900_P1_TMGREG2 0xf46800ff +- +-/*P1_TMGREG1*/ +-#define R0900_P1_TMGREG1 0xf469 +-#define TMGREG1 REGx(R0900_P1_TMGREG1) +-#define F0900_P1_TMGREG1 0xf46900ff +- +-/*P1_TMGREG0*/ +-#define R0900_P1_TMGREG0 0xf46a +-#define TMGREG0 REGx(R0900_P1_TMGREG0) +-#define F0900_P1_TMGREG0 0xf46a00ff +- +-/*P1_TMGLOCK1*/ +-#define R0900_P1_TMGLOCK1 0xf46b +-#define TMGLOCK1 REGx(R0900_P1_TMGLOCK1) +-#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff +- +-/*P1_TMGLOCK0*/ +-#define R0900_P1_TMGLOCK0 0xf46c +-#define TMGLOCK0 REGx(R0900_P1_TMGLOCK0) +-#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff +- +-/*P1_TMGOBS*/ +-#define R0900_P1_TMGOBS 0xf46d +-#define TMGOBS REGx(R0900_P1_TMGOBS) +-#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0 +-#define ROLLOFF_STATUS FLDx(F0900_P1_ROLLOFF_STATUS) +- +-/*P1_EQUALCFG*/ +-#define R0900_P1_EQUALCFG 0xf46f +-#define EQUALCFG REGx(R0900_P1_EQUALCFG) +-#define F0900_P1_EQUAL_ON 0xf46f0040 +-#define F0900_P1_MU_EQUALDFE 0xf46f0007 +- +-/*P1_EQUAI1*/ +-#define R0900_P1_EQUAI1 0xf470 +-#define EQUAI1 REGx(R0900_P1_EQUAI1) +-#define F0900_P1_EQUA_ACCI1 0xf47001ff +- +-/*P1_EQUAQ1*/ +-#define R0900_P1_EQUAQ1 0xf471 +-#define EQUAQ1 REGx(R0900_P1_EQUAQ1) +-#define F0900_P1_EQUA_ACCQ1 0xf47101ff +- +-/*P1_EQUAI2*/ +-#define R0900_P1_EQUAI2 0xf472 +-#define EQUAI2 REGx(R0900_P1_EQUAI2) +-#define F0900_P1_EQUA_ACCI2 0xf47201ff +- +-/*P1_EQUAQ2*/ +-#define R0900_P1_EQUAQ2 0xf473 +-#define EQUAQ2 REGx(R0900_P1_EQUAQ2) +-#define F0900_P1_EQUA_ACCQ2 0xf47301ff +- +-/*P1_EQUAI3*/ +-#define R0900_P1_EQUAI3 0xf474 +-#define EQUAI3 REGx(R0900_P1_EQUAI3) +-#define F0900_P1_EQUA_ACCI3 0xf47401ff +- +-/*P1_EQUAQ3*/ +-#define R0900_P1_EQUAQ3 0xf475 +-#define EQUAQ3 REGx(R0900_P1_EQUAQ3) +-#define F0900_P1_EQUA_ACCQ3 0xf47501ff +- +-/*P1_EQUAI4*/ +-#define R0900_P1_EQUAI4 0xf476 +-#define EQUAI4 REGx(R0900_P1_EQUAI4) +-#define F0900_P1_EQUA_ACCI4 0xf47601ff +- +-/*P1_EQUAQ4*/ +-#define R0900_P1_EQUAQ4 0xf477 +-#define EQUAQ4 REGx(R0900_P1_EQUAQ4) +-#define F0900_P1_EQUA_ACCQ4 0xf47701ff +- +-/*P1_EQUAI5*/ +-#define R0900_P1_EQUAI5 0xf478 +-#define EQUAI5 REGx(R0900_P1_EQUAI5) +-#define F0900_P1_EQUA_ACCI5 0xf47801ff +- +-/*P1_EQUAQ5*/ +-#define R0900_P1_EQUAQ5 0xf479 +-#define EQUAQ5 REGx(R0900_P1_EQUAQ5) +-#define F0900_P1_EQUA_ACCQ5 0xf47901ff +- +-/*P1_EQUAI6*/ +-#define R0900_P1_EQUAI6 0xf47a +-#define EQUAI6 REGx(R0900_P1_EQUAI6) +-#define F0900_P1_EQUA_ACCI6 0xf47a01ff +- +-/*P1_EQUAQ6*/ +-#define R0900_P1_EQUAQ6 0xf47b +-#define EQUAQ6 REGx(R0900_P1_EQUAQ6) +-#define F0900_P1_EQUA_ACCQ6 0xf47b01ff +- +-/*P1_EQUAI7*/ +-#define R0900_P1_EQUAI7 0xf47c +-#define EQUAI7 REGx(R0900_P1_EQUAI7) +-#define F0900_P1_EQUA_ACCI7 0xf47c01ff +- +-/*P1_EQUAQ7*/ +-#define R0900_P1_EQUAQ7 0xf47d +-#define EQUAQ7 REGx(R0900_P1_EQUAQ7) +-#define F0900_P1_EQUA_ACCQ7 0xf47d01ff +- +-/*P1_EQUAI8*/ +-#define R0900_P1_EQUAI8 0xf47e +-#define EQUAI8 REGx(R0900_P1_EQUAI8) +-#define F0900_P1_EQUA_ACCI8 0xf47e01ff +- +-/*P1_EQUAQ8*/ +-#define R0900_P1_EQUAQ8 0xf47f +-#define EQUAQ8 REGx(R0900_P1_EQUAQ8) +-#define F0900_P1_EQUA_ACCQ8 0xf47f01ff +- +-/*P1_NNOSDATAT1*/ +-#define R0900_P1_NNOSDATAT1 0xf480 +-#define NNOSDATAT1 REGx(R0900_P1_NNOSDATAT1) +-#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff +-#define NOSDATAT_NORMED1 FLDx(F0900_P1_NOSDATAT_NORMED1) +- +-/*P1_NNOSDATAT0*/ +-#define R0900_P1_NNOSDATAT0 0xf481 +-#define NNOSDATAT0 REGx(R0900_P1_NNOSDATAT0) +-#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff +-#define NOSDATAT_NORMED0 FLDx(F0900_P1_NOSDATAT_NORMED0) +- +-/*P1_NNOSDATA1*/ +-#define R0900_P1_NNOSDATA1 0xf482 +-#define NNOSDATA1 REGx(R0900_P1_NNOSDATA1) +-#define F0900_P1_NOSDATA_NORMED1 0xf48200ff +- +-/*P1_NNOSDATA0*/ +-#define R0900_P1_NNOSDATA0 0xf483 +-#define NNOSDATA0 REGx(R0900_P1_NNOSDATA0) +-#define F0900_P1_NOSDATA_NORMED0 0xf48300ff +- +-/*P1_NNOSPLHT1*/ +-#define R0900_P1_NNOSPLHT1 0xf484 +-#define NNOSPLHT1 REGx(R0900_P1_NNOSPLHT1) +-#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff +-#define NOSPLHT_NORMED1 FLDx(F0900_P1_NOSPLHT_NORMED1) +- +-/*P1_NNOSPLHT0*/ +-#define R0900_P1_NNOSPLHT0 0xf485 +-#define NNOSPLHT0 REGx(R0900_P1_NNOSPLHT0) +-#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff +-#define NOSPLHT_NORMED0 FLDx(F0900_P1_NOSPLHT_NORMED0) +- +-/*P1_NNOSPLH1*/ +-#define R0900_P1_NNOSPLH1 0xf486 +-#define NNOSPLH1 REGx(R0900_P1_NNOSPLH1) +-#define F0900_P1_NOSPLH_NORMED1 0xf48600ff +- +-/*P1_NNOSPLH0*/ +-#define R0900_P1_NNOSPLH0 0xf487 +-#define NNOSPLH0 REGx(R0900_P1_NNOSPLH0) +-#define F0900_P1_NOSPLH_NORMED0 0xf48700ff +- +-/*P1_NOSDATAT1*/ +-#define R0900_P1_NOSDATAT1 0xf488 +-#define NOSDATAT1 REGx(R0900_P1_NOSDATAT1) +-#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff +- +-/*P1_NOSDATAT0*/ +-#define R0900_P1_NOSDATAT0 0xf489 +-#define NOSDATAT0 REGx(R0900_P1_NOSDATAT0) +-#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff +- +-/*P1_NOSDATA1*/ +-#define R0900_P1_NOSDATA1 0xf48a +-#define NOSDATA1 REGx(R0900_P1_NOSDATA1) +-#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff +- +-/*P1_NOSDATA0*/ +-#define R0900_P1_NOSDATA0 0xf48b +-#define NOSDATA0 REGx(R0900_P1_NOSDATA0) +-#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff +- +-/*P1_NOSPLHT1*/ +-#define R0900_P1_NOSPLHT1 0xf48c +-#define NOSPLHT1 REGx(R0900_P1_NOSPLHT1) +-#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff +- +-/*P1_NOSPLHT0*/ +-#define R0900_P1_NOSPLHT0 0xf48d +-#define NOSPLHT0 REGx(R0900_P1_NOSPLHT0) +-#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff +- +-/*P1_NOSPLH1*/ +-#define R0900_P1_NOSPLH1 0xf48e +-#define NOSPLH1 REGx(R0900_P1_NOSPLH1) +-#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff +- +-/*P1_NOSPLH0*/ +-#define R0900_P1_NOSPLH0 0xf48f +-#define NOSPLH0 REGx(R0900_P1_NOSPLH0) +-#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff +- +-/*P1_CAR2CFG*/ +-#define R0900_P1_CAR2CFG 0xf490 +-#define CAR2CFG REGx(R0900_P1_CAR2CFG) +-#define F0900_P1_CARRIER3_DISABLE 0xf4900040 +-#define F0900_P1_ROTA2ON 0xf4900004 +-#define F0900_P1_PH_DET_ALGO2 0xf4900003 +- +-/*P1_CFR2CFR1*/ +-#define R0900_P1_CFR2CFR1 0xf491 +-#define CFR2CFR1 REGx(R0900_P1_CFR2CFR1) +-#define F0900_P1_CFR2TOCFR1_DVBS1 0xf49100c0 +-#define F0900_P1_EN_S2CAR2CENTER 0xf4910020 +-#define F0900_P1_DIS_BCHERRCFR2 0xf4910010 +-#define F0900_P1_CFR2TOCFR1_BETA 0xf4910007 +- +-/*P1_CFR22*/ +-#define R0900_P1_CFR22 0xf493 +-#define CFR22 REGx(R0900_P1_CFR22) +-#define F0900_P1_CAR2_FREQ2 0xf49301ff +- +-/*P1_CFR21*/ +-#define R0900_P1_CFR21 0xf494 +-#define CFR21 REGx(R0900_P1_CFR21) +-#define F0900_P1_CAR2_FREQ1 0xf49400ff +- +-/*P1_CFR20*/ +-#define R0900_P1_CFR20 0xf495 +-#define CFR20 REGx(R0900_P1_CFR20) +-#define F0900_P1_CAR2_FREQ0 0xf49500ff +- +-/*P1_ACLC2S2Q*/ +-#define R0900_P1_ACLC2S2Q 0xf497 +-#define ACLC2S2Q REGx(R0900_P1_ACLC2S2Q) +-#define F0900_P1_ENAB_SPSKSYMB 0xf4970080 +-#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030 +-#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f +- +-/*P1_ACLC2S28*/ +-#define R0900_P1_ACLC2S28 0xf498 +-#define ACLC2S28 REGx(R0900_P1_ACLC2S28) +-#define F0900_P1_OLDI3Q_MODE 0xf4980080 +-#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030 +-#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f +- +-/*P1_ACLC2S216A*/ +-#define R0900_P1_ACLC2S216A 0xf499 +-#define ACLC2S216A REGx(R0900_P1_ACLC2S216A) +-#define F0900_P1_DIS_C3STOPA2 0xf4990080 +-#define F0900_P1_CAR2S2_16ADERAT 0xf4990040 +-#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030 +-#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f +- +-/*P1_ACLC2S232A*/ +-#define R0900_P1_ACLC2S232A 0xf49a +-#define ACLC2S232A REGx(R0900_P1_ACLC2S232A) +-#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040 +-#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030 +-#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f +- +-/*P1_BCLC2S2Q*/ +-#define R0900_P1_BCLC2S2Q 0xf49c +-#define BCLC2S2Q REGx(R0900_P1_BCLC2S2Q) +-#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030 +-#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f +- +-/*P1_BCLC2S28*/ +-#define R0900_P1_BCLC2S28 0xf49d +-#define BCLC2S28 REGx(R0900_P1_BCLC2S28) +-#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030 +-#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f +- +-/*P1_BCLC2S216A*/ +-#define R0900_P1_BCLC2S216A 0xf49e +-#define BCLC2S216A REGx(R0900_P1_BCLC2S216A) +- +-/*P1_BCLC2S232A*/ +-#define R0900_P1_BCLC2S232A 0xf49f +-#define BCLC2S232A REGx(R0900_P1_BCLC2S232A) +- +-/*P1_PLROOT2*/ +-#define R0900_P1_PLROOT2 0xf4ac +-#define PLROOT2 REGx(R0900_P1_PLROOT2) +-#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c +-#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003 +- +-/*P1_PLROOT1*/ +-#define R0900_P1_PLROOT1 0xf4ad +-#define PLROOT1 REGx(R0900_P1_PLROOT1) +-#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff +- +-/*P1_PLROOT0*/ +-#define R0900_P1_PLROOT0 0xf4ae +-#define PLROOT0 REGx(R0900_P1_PLROOT0) +-#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff +- +-/*P1_MODCODLST0*/ +-#define R0900_P1_MODCODLST0 0xf4b0 +-#define MODCODLST0 REGx(R0900_P1_MODCODLST0) +- +-/*P1_MODCODLST1*/ +-#define R0900_P1_MODCODLST1 0xf4b1 +-#define MODCODLST1 REGx(R0900_P1_MODCODLST1) +-#define F0900_P1_DIS_MODCOD29 0xf4b100f0 +-#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f +- +-/*P1_MODCODLST2*/ +-#define R0900_P1_MODCODLST2 0xf4b2 +-#define MODCODLST2 REGx(R0900_P1_MODCODLST2) +-#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0 +-#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f +- +-/*P1_MODCODLST3*/ +-#define R0900_P1_MODCODLST3 0xf4b3 +-#define MODCODLST3 REGx(R0900_P1_MODCODLST3) +-#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0 +-#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f +- +-/*P1_MODCODLST4*/ +-#define R0900_P1_MODCODLST4 0xf4b4 +-#define MODCODLST4 REGx(R0900_P1_MODCODLST4) +-#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0 +-#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f +- +-/*P1_MODCODLST5*/ +-#define R0900_P1_MODCODLST5 0xf4b5 +-#define MODCODLST5 REGx(R0900_P1_MODCODLST5) +-#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0 +-#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f +- +-/*P1_MODCODLST6*/ +-#define R0900_P1_MODCODLST6 0xf4b6 +-#define MODCODLST6 REGx(R0900_P1_MODCODLST6) +-#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0 +-#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f +- +-/*P1_MODCODLST7*/ +-#define R0900_P1_MODCODLST7 0xf4b7 +-#define MODCODLST7 REGx(R0900_P1_MODCODLST7) +-#define F0900_P1_DIS_8P_9_10 0xf4b700f0 +-#define F0900_P1_DIS_8P_8_9 0xf4b7000f +- +-/*P1_MODCODLST8*/ +-#define R0900_P1_MODCODLST8 0xf4b8 +-#define MODCODLST8 REGx(R0900_P1_MODCODLST8) +-#define F0900_P1_DIS_8P_5_6 0xf4b800f0 +-#define F0900_P1_DIS_8P_3_4 0xf4b8000f +- +-/*P1_MODCODLST9*/ +-#define R0900_P1_MODCODLST9 0xf4b9 +-#define MODCODLST9 REGx(R0900_P1_MODCODLST9) +-#define F0900_P1_DIS_8P_2_3 0xf4b900f0 +-#define F0900_P1_DIS_8P_3_5 0xf4b9000f +- +-/*P1_MODCODLSTA*/ +-#define R0900_P1_MODCODLSTA 0xf4ba +-#define MODCODLSTA REGx(R0900_P1_MODCODLSTA) +-#define F0900_P1_DIS_QP_9_10 0xf4ba00f0 +-#define F0900_P1_DIS_QP_8_9 0xf4ba000f +- +-/*P1_MODCODLSTB*/ +-#define R0900_P1_MODCODLSTB 0xf4bb +-#define MODCODLSTB REGx(R0900_P1_MODCODLSTB) +-#define F0900_P1_DIS_QP_5_6 0xf4bb00f0 +-#define F0900_P1_DIS_QP_4_5 0xf4bb000f +- +-/*P1_MODCODLSTC*/ +-#define R0900_P1_MODCODLSTC 0xf4bc +-#define MODCODLSTC REGx(R0900_P1_MODCODLSTC) +-#define F0900_P1_DIS_QP_3_4 0xf4bc00f0 +-#define F0900_P1_DIS_QP_2_3 0xf4bc000f +- +-/*P1_MODCODLSTD*/ +-#define R0900_P1_MODCODLSTD 0xf4bd +-#define MODCODLSTD REGx(R0900_P1_MODCODLSTD) +-#define F0900_P1_DIS_QP_3_5 0xf4bd00f0 +-#define F0900_P1_DIS_QP_1_2 0xf4bd000f +- +-/*P1_MODCODLSTE*/ +-#define R0900_P1_MODCODLSTE 0xf4be +-#define MODCODLSTE REGx(R0900_P1_MODCODLSTE) +-#define F0900_P1_DIS_QP_2_5 0xf4be00f0 +-#define F0900_P1_DIS_QP_1_3 0xf4be000f +- +-/*P1_MODCODLSTF*/ +-#define R0900_P1_MODCODLSTF 0xf4bf +-#define MODCODLSTF REGx(R0900_P1_MODCODLSTF) +-#define F0900_P1_DIS_QP_1_4 0xf4bf00f0 +- +-/*P1_GAUSSR0*/ +-#define R0900_P1_GAUSSR0 0xf4c0 +-#define GAUSSR0 REGx(R0900_P1_GAUSSR0) +-#define F0900_P1_EN_CCIMODE 0xf4c00080 +-#define F0900_P1_R0_GAUSSIEN 0xf4c0007f +- +-/*P1_CCIR0*/ +-#define R0900_P1_CCIR0 0xf4c1 +-#define CCIR0 REGx(R0900_P1_CCIR0) +-#define F0900_P1_CCIDETECT_PLHONLY 0xf4c10080 +-#define F0900_P1_R0_CCI 0xf4c1007f +- +-/*P1_CCIQUANT*/ +-#define R0900_P1_CCIQUANT 0xf4c2 +-#define CCIQUANT REGx(R0900_P1_CCIQUANT) +-#define F0900_P1_CCI_BETA 0xf4c200e0 +-#define F0900_P1_CCI_QUANT 0xf4c2001f +- +-/*P1_CCITHRES*/ +-#define R0900_P1_CCITHRES 0xf4c3 +-#define CCITHRES REGx(R0900_P1_CCITHRES) +-#define F0900_P1_CCI_THRESHOLD 0xf4c300ff +- +-/*P1_CCIACC*/ +-#define R0900_P1_CCIACC 0xf4c4 +-#define CCIACC REGx(R0900_P1_CCIACC) +-#define F0900_P1_CCI_VALUE 0xf4c400ff +- +-/*P1_DMDRESCFG*/ +-#define R0900_P1_DMDRESCFG 0xf4c6 +-#define DMDRESCFG REGx(R0900_P1_DMDRESCFG) +-#define F0900_P1_DMDRES_RESET 0xf4c60080 +-#define F0900_P1_DMDRES_STRALL 0xf4c60008 +-#define F0900_P1_DMDRES_NEWONLY 0xf4c60004 +-#define F0900_P1_DMDRES_NOSTORE 0xf4c60002 +- +-/*P1_DMDRESADR*/ +-#define R0900_P1_DMDRESADR 0xf4c7 +-#define DMDRESADR REGx(R0900_P1_DMDRESADR) +-#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040 +-#define F0900_P1_DMDRES_MEMFULL 0xf4c70030 +-#define F0900_P1_DMDRES_RESNBR 0xf4c7000f +- +-/*P1_DMDRESDATA7*/ +-#define R0900_P1_DMDRESDATA7 0xf4c8 +-#define F0900_P1_DMDRES_DATA7 0xf4c800ff +- +-/*P1_DMDRESDATA6*/ +-#define R0900_P1_DMDRESDATA6 0xf4c9 +-#define F0900_P1_DMDRES_DATA6 0xf4c900ff +- +-/*P1_DMDRESDATA5*/ +-#define R0900_P1_DMDRESDATA5 0xf4ca +-#define F0900_P1_DMDRES_DATA5 0xf4ca00ff +- +-/*P1_DMDRESDATA4*/ +-#define R0900_P1_DMDRESDATA4 0xf4cb +-#define F0900_P1_DMDRES_DATA4 0xf4cb00ff +- +-/*P1_DMDRESDATA3*/ +-#define R0900_P1_DMDRESDATA3 0xf4cc +-#define F0900_P1_DMDRES_DATA3 0xf4cc00ff +- +-/*P1_DMDRESDATA2*/ +-#define R0900_P1_DMDRESDATA2 0xf4cd +-#define F0900_P1_DMDRES_DATA2 0xf4cd00ff +- +-/*P1_DMDRESDATA1*/ +-#define R0900_P1_DMDRESDATA1 0xf4ce +-#define F0900_P1_DMDRES_DATA1 0xf4ce00ff +- +-/*P1_DMDRESDATA0*/ +-#define R0900_P1_DMDRESDATA0 0xf4cf +-#define F0900_P1_DMDRES_DATA0 0xf4cf00ff +- +-/*P1_FFEI1*/ +-#define R0900_P1_FFEI1 0xf4d0 +-#define FFEI1 REGx(R0900_P1_FFEI1) +-#define F0900_P1_FFE_ACCI1 0xf4d001ff +- +-/*P1_FFEQ1*/ +-#define R0900_P1_FFEQ1 0xf4d1 +-#define FFEQ1 REGx(R0900_P1_FFEQ1) +-#define F0900_P1_FFE_ACCQ1 0xf4d101ff +- +-/*P1_FFEI2*/ +-#define R0900_P1_FFEI2 0xf4d2 +-#define FFEI2 REGx(R0900_P1_FFEI2) +-#define F0900_P1_FFE_ACCI2 0xf4d201ff +- +-/*P1_FFEQ2*/ +-#define R0900_P1_FFEQ2 0xf4d3 +-#define FFEQ2 REGx(R0900_P1_FFEQ2) +-#define F0900_P1_FFE_ACCQ2 0xf4d301ff +- +-/*P1_FFEI3*/ +-#define R0900_P1_FFEI3 0xf4d4 +-#define FFEI3 REGx(R0900_P1_FFEI3) +-#define F0900_P1_FFE_ACCI3 0xf4d401ff +- +-/*P1_FFEQ3*/ +-#define R0900_P1_FFEQ3 0xf4d5 +-#define FFEQ3 REGx(R0900_P1_FFEQ3) +-#define F0900_P1_FFE_ACCQ3 0xf4d501ff +- +-/*P1_FFEI4*/ +-#define R0900_P1_FFEI4 0xf4d6 +-#define FFEI4 REGx(R0900_P1_FFEI4) +-#define F0900_P1_FFE_ACCI4 0xf4d601ff +- +-/*P1_FFEQ4*/ +-#define R0900_P1_FFEQ4 0xf4d7 +-#define FFEQ4 REGx(R0900_P1_FFEQ4) +-#define F0900_P1_FFE_ACCQ4 0xf4d701ff +- +-/*P1_FFECFG*/ +-#define R0900_P1_FFECFG 0xf4d8 +-#define FFECFG REGx(R0900_P1_FFECFG) +-#define F0900_P1_EQUALFFE_ON 0xf4d80040 +-#define F0900_P1_MU_EQUALFFE 0xf4d80007 +- +-/*P1_TNRCFG*/ +-#define R0900_P1_TNRCFG 0xf4e0 +-#define TNRCFG REGx(R0900_P1_TNRCFG) +-#define F0900_P1_TUN_ACKFAIL 0xf4e00080 +-#define F0900_P1_TUN_TYPE 0xf4e00070 +-#define F0900_P1_TUN_SECSTOP 0xf4e00008 +-#define F0900_P1_TUN_VCOSRCH 0xf4e00004 +-#define F0900_P1_TUN_MADDRESS 0xf4e00003 +- +-/*P1_TNRCFG2*/ +-#define R0900_P1_TNRCFG2 0xf4e1 +-#define TNRCFG2 REGx(R0900_P1_TNRCFG2) +-#define F0900_P1_TUN_IQSWAP 0xf4e10080 +-#define F0900_P1_DIS_BWCALC 0xf4e10004 +-#define F0900_P1_SHORT_WAITSTATES 0xf4e10002 +- +-/*P1_TNRXTAL*/ +-#define R0900_P1_TNRXTAL 0xf4e4 +-#define TNRXTAL REGx(R0900_P1_TNRXTAL) +-#define F0900_P1_TUN_XTALFREQ 0xf4e4001f +- +-/*P1_TNRSTEPS*/ +-#define R0900_P1_TNRSTEPS 0xf4e7 +-#define TNRSTEPS REGx(R0900_P1_TNRSTEPS) +-#define F0900_P1_TUNER_BW0P125 0xf4e70080 +-#define F0900_P1_BWINC_OFFSET 0xf4e70170 +-#define F0900_P1_SOFTSTEP_RNG 0xf4e70008 +-#define F0900_P1_TUN_BWOFFSET 0xf4e70007 +- +-/*P1_TNRGAIN*/ +-#define R0900_P1_TNRGAIN 0xf4e8 +-#define TNRGAIN REGx(R0900_P1_TNRGAIN) +-#define F0900_P1_TUN_KDIVEN 0xf4e800c0 +-#define F0900_P1_STB6X00_OCK 0xf4e80030 +-#define F0900_P1_TUN_GAIN 0xf4e8000f +- +-/*P1_TNRRF1*/ +-#define R0900_P1_TNRRF1 0xf4e9 +-#define TNRRF1 REGx(R0900_P1_TNRRF1) +-#define F0900_P1_TUN_RFFREQ2 0xf4e900ff +-#define TUN_RFFREQ2 FLDx(F0900_P1_TUN_RFFREQ2) +- +-/*P1_TNRRF0*/ +-#define R0900_P1_TNRRF0 0xf4ea +-#define TNRRF0 REGx(R0900_P1_TNRRF0) +-#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff +-#define TUN_RFFREQ1 FLDx(F0900_P1_TUN_RFFREQ1) +- +-/*P1_TNRBW*/ +-#define R0900_P1_TNRBW 0xf4eb +-#define TNRBW REGx(R0900_P1_TNRBW) +-#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 +-#define TUN_RFFREQ0 FLDx(F0900_P1_TUN_RFFREQ0) +-#define F0900_P1_TUN_BW 0xf4eb003f +-#define TUN_BW FLDx(F0900_P1_TUN_BW) +- +-/*P1_TNRADJ*/ +-#define R0900_P1_TNRADJ 0xf4ec +-#define TNRADJ REGx(R0900_P1_TNRADJ) +-#define F0900_P1_STB61X0_CALTIME 0xf4ec0040 +- +-/*P1_TNRCTL2*/ +-#define R0900_P1_TNRCTL2 0xf4ed +-#define TNRCTL2 REGx(R0900_P1_TNRCTL2) +-#define F0900_P1_STB61X0_RCCKOFF 0xf4ed0080 +-#define F0900_P1_STB61X0_ICP_SDOFF 0xf4ed0040 +-#define F0900_P1_STB61X0_DCLOOPOFF 0xf4ed0020 +-#define F0900_P1_STB61X0_REFOUTSEL 0xf4ed0010 +-#define F0900_P1_STB61X0_CALOFF 0xf4ed0008 +-#define F0900_P1_STB6XX0_LPT_BEN 0xf4ed0004 +-#define F0900_P1_STB6XX0_RX_OSCP 0xf4ed0002 +-#define F0900_P1_STB6XX0_SYN 0xf4ed0001 +- +-/*P1_TNRCFG3*/ +-#define R0900_P1_TNRCFG3 0xf4ee +-#define TNRCFG3 REGx(R0900_P1_TNRCFG3) +-#define F0900_P1_TUN_PLLFREQ 0xf4ee001c +-#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003 +- +-/*P1_TNRLAUNCH*/ +-#define R0900_P1_TNRLAUNCH 0xf4f0 +-#define TNRLAUNCH REGx(R0900_P1_TNRLAUNCH) +- +-/*P1_TNRLD*/ +-#define R0900_P1_TNRLD 0xf4f0 +-#define TNRLD REGx(R0900_P1_TNRLD) +-#define F0900_P1_TUNLD_VCOING 0xf4f00080 +-#define F0900_P1_TUN_REG1FAIL 0xf4f00040 +-#define F0900_P1_TUN_REG2FAIL 0xf4f00020 +-#define F0900_P1_TUN_REG3FAIL 0xf4f00010 +-#define F0900_P1_TUN_REG4FAIL 0xf4f00008 +-#define F0900_P1_TUN_REG5FAIL 0xf4f00004 +-#define F0900_P1_TUN_BWING 0xf4f00002 +-#define F0900_P1_TUN_LOCKED 0xf4f00001 +- +-/*P1_TNROBSL*/ +-#define R0900_P1_TNROBSL 0xf4f6 +-#define TNROBSL REGx(R0900_P1_TNROBSL) +-#define F0900_P1_TUN_I2CABORTED 0xf4f60080 +-#define F0900_P1_TUN_LPEN 0xf4f60040 +-#define F0900_P1_TUN_FCCK 0xf4f60020 +-#define F0900_P1_TUN_I2CLOCKED 0xf4f60010 +-#define F0900_P1_TUN_PROGDONE 0xf4f6000c +-#define F0900_P1_TUN_RFRESTE1 0xf4f60003 +-#define TUN_RFRESTE1 FLDx(F0900_P1_TUN_RFRESTE1) +- +-/*P1_TNRRESTE*/ +-#define R0900_P1_TNRRESTE 0xf4f7 +-#define TNRRESTE REGx(R0900_P1_TNRRESTE) +-#define F0900_P1_TUN_RFRESTE0 0xf4f700ff +-#define TUN_RFRESTE0 FLDx(F0900_P1_TUN_RFRESTE0) +- +-/*P1_SMAPCOEF7*/ +-#define R0900_P1_SMAPCOEF7 0xf500 +-#define SMAPCOEF7 REGx(R0900_P1_SMAPCOEF7) +-#define F0900_P1_DIS_QSCALE 0xf5000080 +-#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f +- +-/*P1_SMAPCOEF6*/ +-#define R0900_P1_SMAPCOEF6 0xf501 +-#define SMAPCOEF6 REGx(R0900_P1_SMAPCOEF6) +-#define F0900_P1_ADJ_8PSKLLR1 0xf5010004 +-#define F0900_P1_OLD_8PSKLLR1 0xf5010002 +-#define F0900_P1_DIS_AB8PSK 0xf5010001 +- +-/*P1_SMAPCOEF5*/ +-#define R0900_P1_SMAPCOEF5 0xf502 +-#define SMAPCOEF5 REGx(R0900_P1_SMAPCOEF5) +-#define F0900_P1_DIS_8SCALE 0xf5020080 +-#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f +- +-/*P1_NCO2MAX1*/ +-#define R0900_P1_NCO2MAX1 0xf514 +-#define NCO2MAX1 REGx(R0900_P1_NCO2MAX1) +-#define F0900_P1_TETA2_MAXVABS1 0xf51400ff +- +-/*P1_NCO2MAX0*/ +-#define R0900_P1_NCO2MAX0 0xf515 +-#define NCO2MAX0 REGx(R0900_P1_NCO2MAX0) +-#define F0900_P1_TETA2_MAXVABS0 0xf51500ff +- +-/*P1_NCO2FR1*/ +-#define R0900_P1_NCO2FR1 0xf516 +-#define NCO2FR1 REGx(R0900_P1_NCO2FR1) +-#define F0900_P1_NCO2FINAL_ANGLE1 0xf51600ff +- +-/*P1_NCO2FR0*/ +-#define R0900_P1_NCO2FR0 0xf517 +-#define NCO2FR0 REGx(R0900_P1_NCO2FR0) +-#define F0900_P1_NCO2FINAL_ANGLE0 0xf51700ff +- +-/*P1_CFR2AVRGE1*/ +-#define R0900_P1_CFR2AVRGE1 0xf518 +-#define CFR2AVRGE1 REGx(R0900_P1_CFR2AVRGE1) +-#define F0900_P1_I2C_CFR2AVERAGE1 0xf51800ff +- +-/*P1_CFR2AVRGE0*/ +-#define R0900_P1_CFR2AVRGE0 0xf519 +-#define CFR2AVRGE0 REGx(R0900_P1_CFR2AVRGE0) +-#define F0900_P1_I2C_CFR2AVERAGE0 0xf51900ff +- +-/*P1_DMDPLHSTAT*/ +-#define R0900_P1_DMDPLHSTAT 0xf520 +-#define DMDPLHSTAT REGx(R0900_P1_DMDPLHSTAT) +-#define F0900_P1_PLH_STATISTIC 0xf52000ff +- +-/*P1_LOCKTIME3*/ +-#define R0900_P1_LOCKTIME3 0xf522 +-#define LOCKTIME3 REGx(R0900_P1_LOCKTIME3) +-#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff +- +-/*P1_LOCKTIME2*/ +-#define R0900_P1_LOCKTIME2 0xf523 +-#define LOCKTIME2 REGx(R0900_P1_LOCKTIME2) +-#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff +- +-/*P1_LOCKTIME1*/ +-#define R0900_P1_LOCKTIME1 0xf524 +-#define LOCKTIME1 REGx(R0900_P1_LOCKTIME1) +-#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff +- +-/*P1_LOCKTIME0*/ +-#define R0900_P1_LOCKTIME0 0xf525 +-#define LOCKTIME0 REGx(R0900_P1_LOCKTIME0) +-#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff +- +-/*P1_VITSCALE*/ +-#define R0900_P1_VITSCALE 0xf532 +-#define VITSCALE REGx(R0900_P1_VITSCALE) +-#define F0900_P1_NVTH_NOSRANGE 0xf5320080 +-#define F0900_P1_VERROR_MAXMODE 0xf5320040 +-#define F0900_P1_NSLOWSN_LOCKED 0xf5320008 +-#define F0900_P1_DIS_RSFLOCK 0xf5320002 +- +-/*P1_FECM*/ +-#define R0900_P1_FECM 0xf533 +-#define FECM REGx(R0900_P1_FECM) +-#define F0900_P1_DSS_DVB 0xf5330080 +-#define DSS_DVB FLDx(F0900_P1_DSS_DVB) +-#define F0900_P1_DSS_SRCH 0xf5330010 +-#define F0900_P1_SYNCVIT 0xf5330002 +-#define F0900_P1_IQINV 0xf5330001 +-#define IQINV FLDx(F0900_P1_IQINV) +- +-/*P1_VTH12*/ +-#define R0900_P1_VTH12 0xf534 +-#define VTH12 REGx(R0900_P1_VTH12) +-#define F0900_P1_VTH12 0xf53400ff +- +-/*P1_VTH23*/ +-#define R0900_P1_VTH23 0xf535 +-#define VTH23 REGx(R0900_P1_VTH23) +-#define F0900_P1_VTH23 0xf53500ff +- +-/*P1_VTH34*/ +-#define R0900_P1_VTH34 0xf536 +-#define VTH34 REGx(R0900_P1_VTH34) +-#define F0900_P1_VTH34 0xf53600ff +- +-/*P1_VTH56*/ +-#define R0900_P1_VTH56 0xf537 +-#define VTH56 REGx(R0900_P1_VTH56) +-#define F0900_P1_VTH56 0xf53700ff +- +-/*P1_VTH67*/ +-#define R0900_P1_VTH67 0xf538 +-#define VTH67 REGx(R0900_P1_VTH67) +-#define F0900_P1_VTH67 0xf53800ff +- +-/*P1_VTH78*/ +-#define R0900_P1_VTH78 0xf539 +-#define VTH78 REGx(R0900_P1_VTH78) +-#define F0900_P1_VTH78 0xf53900ff +- +-/*P1_VITCURPUN*/ +-#define R0900_P1_VITCURPUN 0xf53a +-#define VITCURPUN REGx(R0900_P1_VITCURPUN) +-#define F0900_P1_VIT_CURPUN 0xf53a001f +-#define VIT_CURPUN FLDx(F0900_P1_VIT_CURPUN) +- +-/*P1_VERROR*/ +-#define R0900_P1_VERROR 0xf53b +-#define VERROR REGx(R0900_P1_VERROR) +-#define F0900_P1_REGERR_VIT 0xf53b00ff +- +-/*P1_PRVIT*/ +-#define R0900_P1_PRVIT 0xf53c +-#define PRVIT REGx(R0900_P1_PRVIT) +-#define F0900_P1_DIS_VTHLOCK 0xf53c0040 +-#define F0900_P1_E7_8VIT 0xf53c0020 +-#define F0900_P1_E6_7VIT 0xf53c0010 +-#define F0900_P1_E5_6VIT 0xf53c0008 +-#define F0900_P1_E3_4VIT 0xf53c0004 +-#define F0900_P1_E2_3VIT 0xf53c0002 +-#define F0900_P1_E1_2VIT 0xf53c0001 +- +-/*P1_VAVSRVIT*/ +-#define R0900_P1_VAVSRVIT 0xf53d +-#define VAVSRVIT REGx(R0900_P1_VAVSRVIT) +-#define F0900_P1_AMVIT 0xf53d0080 +-#define F0900_P1_FROZENVIT 0xf53d0040 +-#define F0900_P1_SNVIT 0xf53d0030 +-#define F0900_P1_TOVVIT 0xf53d000c +-#define F0900_P1_HYPVIT 0xf53d0003 +- +-/*P1_VSTATUSVIT*/ +-#define R0900_P1_VSTATUSVIT 0xf53e +-#define VSTATUSVIT REGx(R0900_P1_VSTATUSVIT) +-#define F0900_P1_PRFVIT 0xf53e0010 +-#define PRFVIT FLDx(F0900_P1_PRFVIT) +-#define F0900_P1_LOCKEDVIT 0xf53e0008 +-#define LOCKEDVIT FLDx(F0900_P1_LOCKEDVIT) +- +-/*P1_VTHINUSE*/ +-#define R0900_P1_VTHINUSE 0xf53f +-#define VTHINUSE REGx(R0900_P1_VTHINUSE) +-#define F0900_P1_VIT_INUSE 0xf53f00ff +- +-/*P1_KDIV12*/ +-#define R0900_P1_KDIV12 0xf540 +-#define KDIV12 REGx(R0900_P1_KDIV12) +-#define F0900_P1_K_DIVIDER_12 0xf540007f +- +-/*P1_KDIV23*/ +-#define R0900_P1_KDIV23 0xf541 +-#define KDIV23 REGx(R0900_P1_KDIV23) +-#define F0900_P1_K_DIVIDER_23 0xf541007f +- +-/*P1_KDIV34*/ +-#define R0900_P1_KDIV34 0xf542 +-#define KDIV34 REGx(R0900_P1_KDIV34) +-#define F0900_P1_K_DIVIDER_34 0xf542007f +- +-/*P1_KDIV56*/ +-#define R0900_P1_KDIV56 0xf543 +-#define KDIV56 REGx(R0900_P1_KDIV56) +-#define F0900_P1_K_DIVIDER_56 0xf543007f +- +-/*P1_KDIV67*/ +-#define R0900_P1_KDIV67 0xf544 +-#define KDIV67 REGx(R0900_P1_KDIV67) +-#define F0900_P1_K_DIVIDER_67 0xf544007f +- +-/*P1_KDIV78*/ +-#define R0900_P1_KDIV78 0xf545 +-#define KDIV78 REGx(R0900_P1_KDIV78) +-#define F0900_P1_K_DIVIDER_78 0xf545007f +- +-/*P1_PDELCTRL1*/ +-#define R0900_P1_PDELCTRL1 0xf550 +-#define PDELCTRL1 REGx(R0900_P1_PDELCTRL1) +-#define F0900_P1_INV_MISMASK 0xf5500080 +-#define F0900_P1_FILTER_EN 0xf5500020 +-#define F0900_P1_EN_MIS00 0xf5500002 +-#define F0900_P1_ALGOSWRST 0xf5500001 +-#define ALGOSWRST FLDx(F0900_P1_ALGOSWRST) +- +-/*P1_PDELCTRL2*/ +-#define R0900_P1_PDELCTRL2 0xf551 +-#define PDELCTRL2 REGx(R0900_P1_PDELCTRL2) +-#define F0900_P1_RESET_UPKO_COUNT 0xf5510040 +-#define RESET_UPKO_COUNT FLDx(F0900_P1_RESET_UPKO_COUNT) +-#define F0900_P1_FRAME_MODE 0xf5510002 +-#define F0900_P1_NOBCHERRFLG_USE 0xf5510001 +- +-/*P1_HYSTTHRESH*/ +-#define R0900_P1_HYSTTHRESH 0xf554 +-#define HYSTTHRESH REGx(R0900_P1_HYSTTHRESH) +-#define F0900_P1_UNLCK_THRESH 0xf55400f0 +-#define F0900_P1_DELIN_LCK_THRESH 0xf554000f +- +-/*P1_ISIENTRY*/ +-#define R0900_P1_ISIENTRY 0xf55e +-#define ISIENTRY REGx(R0900_P1_ISIENTRY) +-#define F0900_P1_ISI_ENTRY 0xf55e00ff +- +-/*P1_ISIBITENA*/ +-#define R0900_P1_ISIBITENA 0xf55f +-#define ISIBITENA REGx(R0900_P1_ISIBITENA) +-#define F0900_P1_ISI_BIT_EN 0xf55f00ff +- +-/*P1_MATSTR1*/ +-#define R0900_P1_MATSTR1 0xf560 +-#define MATSTR1 REGx(R0900_P1_MATSTR1) +-#define F0900_P1_MATYPE_CURRENT1 0xf56000ff +- +-/*P1_MATSTR0*/ +-#define R0900_P1_MATSTR0 0xf561 +-#define MATSTR0 REGx(R0900_P1_MATSTR0) +-#define F0900_P1_MATYPE_CURRENT0 0xf56100ff +- +-/*P1_UPLSTR1*/ +-#define R0900_P1_UPLSTR1 0xf562 +-#define UPLSTR1 REGx(R0900_P1_UPLSTR1) +-#define F0900_P1_UPL_CURRENT1 0xf56200ff +- +-/*P1_UPLSTR0*/ +-#define R0900_P1_UPLSTR0 0xf563 +-#define UPLSTR0 REGx(R0900_P1_UPLSTR0) +-#define F0900_P1_UPL_CURRENT0 0xf56300ff +- +-/*P1_DFLSTR1*/ +-#define R0900_P1_DFLSTR1 0xf564 +-#define DFLSTR1 REGx(R0900_P1_DFLSTR1) +-#define F0900_P1_DFL_CURRENT1 0xf56400ff +- +-/*P1_DFLSTR0*/ +-#define R0900_P1_DFLSTR0 0xf565 +-#define DFLSTR0 REGx(R0900_P1_DFLSTR0) +-#define F0900_P1_DFL_CURRENT0 0xf56500ff +- +-/*P1_SYNCSTR*/ +-#define R0900_P1_SYNCSTR 0xf566 +-#define SYNCSTR REGx(R0900_P1_SYNCSTR) +-#define F0900_P1_SYNC_CURRENT 0xf56600ff +- +-/*P1_SYNCDSTR1*/ +-#define R0900_P1_SYNCDSTR1 0xf567 +-#define SYNCDSTR1 REGx(R0900_P1_SYNCDSTR1) +-#define F0900_P1_SYNCD_CURRENT1 0xf56700ff +- +-/*P1_SYNCDSTR0*/ +-#define R0900_P1_SYNCDSTR0 0xf568 +-#define SYNCDSTR0 REGx(R0900_P1_SYNCDSTR0) +-#define F0900_P1_SYNCD_CURRENT0 0xf56800ff +- +-/*P1_PDELSTATUS1*/ +-#define R0900_P1_PDELSTATUS1 0xf569 +-#define F0900_P1_PKTDELIN_DELOCK 0xf5690080 +-#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040 +-#define F0900_P1_CONTINUOUS_STREAM 0xf5690020 +-#define F0900_P1_UNACCEPTED_STREAM 0xf5690010 +-#define F0900_P1_BCH_ERROR_FLAG 0xf5690008 +-#define F0900_P1_PKTDELIN_LOCK 0xf5690002 +-#define PKTDELIN_LOCK FLDx(F0900_P1_PKTDELIN_LOCK) +-#define F0900_P1_FIRST_LOCK 0xf5690001 +- +-/*P1_PDELSTATUS2*/ +-#define R0900_P1_PDELSTATUS2 0xf56a +-#define F0900_P1_FRAME_MODCOD 0xf56a007c +-#define F0900_P1_FRAME_TYPE 0xf56a0003 +- +-/*P1_BBFCRCKO1*/ +-#define R0900_P1_BBFCRCKO1 0xf56b +-#define BBFCRCKO1 REGx(R0900_P1_BBFCRCKO1) +-#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff +- +-/*P1_BBFCRCKO0*/ +-#define R0900_P1_BBFCRCKO0 0xf56c +-#define BBFCRCKO0 REGx(R0900_P1_BBFCRCKO0) +-#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff +- +-/*P1_UPCRCKO1*/ +-#define R0900_P1_UPCRCKO1 0xf56d +-#define UPCRCKO1 REGx(R0900_P1_UPCRCKO1) +-#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff +- +-/*P1_UPCRCKO0*/ +-#define R0900_P1_UPCRCKO0 0xf56e +-#define UPCRCKO0 REGx(R0900_P1_UPCRCKO0) +-#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff +- +-/*P1_PDELCTRL3*/ +-#define R0900_P1_PDELCTRL3 0xf56f +-#define PDELCTRL3 REGx(R0900_P1_PDELCTRL3) +-#define F0900_P1_PKTDEL_CONTFAIL 0xf56f0080 +-#define F0900_P1_NOFIFO_BCHERR 0xf56f0020 +- +-/*P1_TSSTATEM*/ +-#define R0900_P1_TSSTATEM 0xf570 +-#define TSSTATEM REGx(R0900_P1_TSSTATEM) +-#define F0900_P1_TSDIL_ON 0xf5700080 +-#define F0900_P1_TSRS_ON 0xf5700020 +-#define F0900_P1_TSDESCRAMB_ON 0xf5700010 +-#define F0900_P1_TSFRAME_MODE 0xf5700008 +-#define F0900_P1_TS_DISABLE 0xf5700004 +-#define F0900_P1_TSOUT_NOSYNC 0xf5700001 +- +-/*P1_TSCFGH*/ +-#define R0900_P1_TSCFGH 0xf572 +-#define TSCFGH REGx(R0900_P1_TSCFGH) +-#define F0900_P1_TSFIFO_DVBCI 0xf5720080 +-#define F0900_P1_TSFIFO_SERIAL 0xf5720040 +-#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020 +-#define F0900_P1_TSFIFO_DUTY50 0xf5720010 +-#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008 +-#define F0900_P1_TSFIFO_ERRMODE 0xf5720006 +-#define F0900_P1_RST_HWARE 0xf5720001 +-#define RST_HWARE FLDx(F0900_P1_RST_HWARE) +- +-/*P1_TSCFGM*/ +-#define R0900_P1_TSCFGM 0xf573 +-#define TSCFGM REGx(R0900_P1_TSCFGM) +-#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0 +-#define F0900_P1_TSFIFO_PERMDATA 0xf5730020 +-#define F0900_P1_TSFIFO_DPUNACT 0xf5730002 +-#define F0900_P1_TSFIFO_INVDATA 0xf5730001 +- +-/*P1_TSCFGL*/ +-#define R0900_P1_TSCFGL 0xf574 +-#define TSCFGL REGx(R0900_P1_TSCFGL) +-#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0 +-#define F0900_P1_BCHERROR_MODE 0xf5740030 +-#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008 +-#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004 +-#define F0900_P1_TSFIFO_BITSPEED 0xf5740003 +- +-/*P1_TSINSDELH*/ +-#define R0900_P1_TSINSDELH 0xf576 +-#define TSINSDELH REGx(R0900_P1_TSINSDELH) +-#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080 +-#define F0900_P1_TSDEL_XXHEADER 0xf5760040 +-#define F0900_P1_TSDEL_BBHEADER 0xf5760020 +-#define F0900_P1_TSDEL_DATAFIELD 0xf5760010 +-#define F0900_P1_TSINSDEL_ISCR 0xf5760008 +-#define F0900_P1_TSINSDEL_NPD 0xf5760004 +-#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002 +-#define F0900_P1_TSINSDEL_CRC8 0xf5760001 +- +-/*P1_TSDIVN*/ +-#define R0900_P1_TSDIVN 0xf579 +-#define TSDIVN REGx(R0900_P1_TSDIVN) +-#define F0900_P1_TSFIFO_SPEEDMODE 0xf57900c0 +- +-/*P1_TSCFG4*/ +-#define R0900_P1_TSCFG4 0xf57a +-#define TSCFG4 REGx(R0900_P1_TSCFG4) +-#define F0900_P1_TSFIFO_TSSPEEDMODE 0xf57a00c0 +- +-/*P1_TSSPEED*/ +-#define R0900_P1_TSSPEED 0xf580 +-#define TSSPEED REGx(R0900_P1_TSSPEED) +-#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff +- +-/*P1_TSSTATUS*/ +-#define R0900_P1_TSSTATUS 0xf581 +-#define TSSTATUS REGx(R0900_P1_TSSTATUS) +-#define F0900_P1_TSFIFO_LINEOK 0xf5810080 +-#define TSFIFO_LINEOK FLDx(F0900_P1_TSFIFO_LINEOK) +-#define F0900_P1_TSFIFO_ERROR 0xf5810040 +-#define F0900_P1_DIL_READY 0xf5810001 +- +-/*P1_TSSTATUS2*/ +-#define R0900_P1_TSSTATUS2 0xf582 +-#define TSSTATUS2 REGx(R0900_P1_TSSTATUS2) +-#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080 +-#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040 +-#define F0900_P1_DILXX_RESET 0xf5820020 +-#define F0900_P1_TSSERIAL_IMPOS 0xf5820010 +-#define F0900_P1_SCRAMBDETECT 0xf5820002 +- +-/*P1_TSBITRATE1*/ +-#define R0900_P1_TSBITRATE1 0xf583 +-#define TSBITRATE1 REGx(R0900_P1_TSBITRATE1) +-#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff +- +-/*P1_TSBITRATE0*/ +-#define R0900_P1_TSBITRATE0 0xf584 +-#define TSBITRATE0 REGx(R0900_P1_TSBITRATE0) +-#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff +- +-/*P1_ERRCTRL1*/ +-#define R0900_P1_ERRCTRL1 0xf598 +-#define ERRCTRL1 REGx(R0900_P1_ERRCTRL1) +-#define F0900_P1_ERR_SOURCE1 0xf59800f0 +-#define F0900_P1_NUM_EVENT1 0xf5980007 +- +-/*P1_ERRCNT12*/ +-#define R0900_P1_ERRCNT12 0xf599 +-#define ERRCNT12 REGx(R0900_P1_ERRCNT12) +-#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080 +-#define F0900_P1_ERR_CNT12 0xf599007f +-#define ERR_CNT12 FLDx(F0900_P1_ERR_CNT12) +- +-/*P1_ERRCNT11*/ +-#define R0900_P1_ERRCNT11 0xf59a +-#define ERRCNT11 REGx(R0900_P1_ERRCNT11) +-#define F0900_P1_ERR_CNT11 0xf59a00ff +-#define ERR_CNT11 FLDx(F0900_P1_ERR_CNT11) +- +-/*P1_ERRCNT10*/ +-#define R0900_P1_ERRCNT10 0xf59b +-#define ERRCNT10 REGx(R0900_P1_ERRCNT10) +-#define F0900_P1_ERR_CNT10 0xf59b00ff +-#define ERR_CNT10 FLDx(F0900_P1_ERR_CNT10) +- +-/*P1_ERRCTRL2*/ +-#define R0900_P1_ERRCTRL2 0xf59c +-#define ERRCTRL2 REGx(R0900_P1_ERRCTRL2) +-#define F0900_P1_ERR_SOURCE2 0xf59c00f0 +-#define F0900_P1_NUM_EVENT2 0xf59c0007 +- +-/*P1_ERRCNT22*/ +-#define R0900_P1_ERRCNT22 0xf59d +-#define ERRCNT22 REGx(R0900_P1_ERRCNT22) +-#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080 +-#define F0900_P1_ERR_CNT22 0xf59d007f +-#define ERR_CNT22 FLDx(F0900_P1_ERR_CNT22) +- +-/*P1_ERRCNT21*/ +-#define R0900_P1_ERRCNT21 0xf59e +-#define ERRCNT21 REGx(R0900_P1_ERRCNT21) +-#define F0900_P1_ERR_CNT21 0xf59e00ff +-#define ERR_CNT21 FLDx(F0900_P1_ERR_CNT21) +- +-/*P1_ERRCNT20*/ +-#define R0900_P1_ERRCNT20 0xf59f +-#define ERRCNT20 REGx(R0900_P1_ERRCNT20) +-#define F0900_P1_ERR_CNT20 0xf59f00ff +-#define ERR_CNT20 FLDx(F0900_P1_ERR_CNT20) +- +-/*P1_FECSPY*/ +-#define R0900_P1_FECSPY 0xf5a0 +-#define FECSPY REGx(R0900_P1_FECSPY) +-#define F0900_P1_SPY_ENABLE 0xf5a00080 +-#define F0900_P1_NO_SYNCBYTE 0xf5a00040 +-#define F0900_P1_SERIAL_MODE 0xf5a00020 +-#define F0900_P1_UNUSUAL_PACKET 0xf5a00010 +-#define F0900_P1_BERMETER_DATAMODE 0xf5a00008 +-#define F0900_P1_BERMETER_LMODE 0xf5a00002 +-#define F0900_P1_BERMETER_RESET 0xf5a00001 +- +-/*P1_FSPYCFG*/ +-#define R0900_P1_FSPYCFG 0xf5a1 +-#define FSPYCFG REGx(R0900_P1_FSPYCFG) +-#define F0900_P1_FECSPY_INPUT 0xf5a100c0 +-#define F0900_P1_RST_ON_ERROR 0xf5a10020 +-#define F0900_P1_ONE_SHOT 0xf5a10010 +-#define F0900_P1_I2C_MODE 0xf5a1000c +-#define F0900_P1_SPY_HYSTERESIS 0xf5a10003 +- +-/*P1_FSPYDATA*/ +-#define R0900_P1_FSPYDATA 0xf5a2 +-#define FSPYDATA REGx(R0900_P1_FSPYDATA) +-#define F0900_P1_SPY_STUFFING 0xf5a20080 +-#define F0900_P1_SPY_CNULLPKT 0xf5a20020 +-#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f +- +-/*P1_FSPYOUT*/ +-#define R0900_P1_FSPYOUT 0xf5a3 +-#define FSPYOUT REGx(R0900_P1_FSPYOUT) +-#define F0900_P1_FSPY_DIRECT 0xf5a30080 +-#define F0900_P1_STUFF_MODE 0xf5a30007 +- +-/*P1_FSTATUS*/ +-#define R0900_P1_FSTATUS 0xf5a4 +-#define FSTATUS REGx(R0900_P1_FSTATUS) +-#define F0900_P1_SPY_ENDSIM 0xf5a40080 +-#define F0900_P1_VALID_SIM 0xf5a40040 +-#define F0900_P1_FOUND_SIGNAL 0xf5a40020 +-#define F0900_P1_DSS_SYNCBYTE 0xf5a40010 +-#define F0900_P1_RESULT_STATE 0xf5a4000f +- +-/*P1_FBERCPT4*/ +-#define R0900_P1_FBERCPT4 0xf5a8 +-#define FBERCPT4 REGx(R0900_P1_FBERCPT4) +-#define F0900_P1_FBERMETER_CPT4 0xf5a800ff +- +-/*P1_FBERCPT3*/ +-#define R0900_P1_FBERCPT3 0xf5a9 +-#define FBERCPT3 REGx(R0900_P1_FBERCPT3) +-#define F0900_P1_FBERMETER_CPT3 0xf5a900ff +- +-/*P1_FBERCPT2*/ +-#define R0900_P1_FBERCPT2 0xf5aa +-#define FBERCPT2 REGx(R0900_P1_FBERCPT2) +-#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff +- +-/*P1_FBERCPT1*/ +-#define R0900_P1_FBERCPT1 0xf5ab +-#define FBERCPT1 REGx(R0900_P1_FBERCPT1) +-#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff +- +-/*P1_FBERCPT0*/ +-#define R0900_P1_FBERCPT0 0xf5ac +-#define FBERCPT0 REGx(R0900_P1_FBERCPT0) +-#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff +- +-/*P1_FBERERR2*/ +-#define R0900_P1_FBERERR2 0xf5ad +-#define FBERERR2 REGx(R0900_P1_FBERERR2) +-#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff +- +-/*P1_FBERERR1*/ +-#define R0900_P1_FBERERR1 0xf5ae +-#define FBERERR1 REGx(R0900_P1_FBERERR1) +-#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff +- +-/*P1_FBERERR0*/ +-#define R0900_P1_FBERERR0 0xf5af +-#define FBERERR0 REGx(R0900_P1_FBERERR0) +-#define F0900_P1_FBERMETER_ERR0 0xf5af00ff +- +-/*P1_FSPYBER*/ +-#define R0900_P1_FSPYBER 0xf5b2 +-#define FSPYBER REGx(R0900_P1_FSPYBER) +-#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010 +-#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008 +-#define F0900_P1_FSPYBER_CTIME 0xf5b20007 +- +-/*RCCFG2*/ +-#define R0900_RCCFG2 0xf600 +- +-/*TSGENERAL*/ +-#define R0900_TSGENERAL 0xf630 +-#define F0900_TSFIFO_DISTS2PAR 0xf6300040 +-#define F0900_MUXSTREAM_OUTMODE 0xf6300008 +-#define F0900_TSFIFO_PERMPARAL 0xf6300006 +- +-/*TSGENERAL1X*/ +-#define R0900_TSGENERAL1X 0xf670 +- +-/*NBITER_NF4*/ +-#define R0900_NBITER_NF4 0xfa03 +-#define F0900_NBITER_NF_QP_1_2 0xfa0300ff +- +-/*NBITER_NF5*/ +-#define R0900_NBITER_NF5 0xfa04 +-#define F0900_NBITER_NF_QP_3_5 0xfa0400ff +- +-/*NBITER_NF6*/ +-#define R0900_NBITER_NF6 0xfa05 +-#define F0900_NBITER_NF_QP_2_3 0xfa0500ff +- +-/*NBITER_NF7*/ +-#define R0900_NBITER_NF7 0xfa06 +-#define F0900_NBITER_NF_QP_3_4 0xfa0600ff +- +-/*NBITER_NF8*/ +-#define R0900_NBITER_NF8 0xfa07 +-#define F0900_NBITER_NF_QP_4_5 0xfa0700ff +- +-/*NBITER_NF9*/ +-#define R0900_NBITER_NF9 0xfa08 +-#define F0900_NBITER_NF_QP_5_6 0xfa0800ff +- +-/*NBITER_NF10*/ +-#define R0900_NBITER_NF10 0xfa09 +-#define F0900_NBITER_NF_QP_8_9 0xfa0900ff +- +-/*NBITER_NF11*/ +-#define R0900_NBITER_NF11 0xfa0a +-#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff +- +-/*NBITER_NF12*/ +-#define R0900_NBITER_NF12 0xfa0b +-#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff +- +-/*NBITER_NF13*/ +-#define R0900_NBITER_NF13 0xfa0c +-#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff +- +-/*NBITER_NF14*/ +-#define R0900_NBITER_NF14 0xfa0d +-#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff +- +-/*NBITER_NF15*/ +-#define R0900_NBITER_NF15 0xfa0e +-#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff +- +-/*NBITER_NF16*/ +-#define R0900_NBITER_NF16 0xfa0f +-#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff +- +-/*NBITER_NF17*/ +-#define R0900_NBITER_NF17 0xfa10 +-#define F0900_NBITER_NF_8P_9_10 0xfa1000ff +- +-/*NBITERNOERR*/ +-#define R0900_NBITERNOERR 0xfa3f +-#define F0900_NBITER_STOP_CRIT 0xfa3f000f +- +-/*GAINLLR_NF4*/ +-#define R0900_GAINLLR_NF4 0xfa43 +-#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f +- +-/*GAINLLR_NF5*/ +-#define R0900_GAINLLR_NF5 0xfa44 +-#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f +- +-/*GAINLLR_NF6*/ +-#define R0900_GAINLLR_NF6 0xfa45 +-#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f +- +-/*GAINLLR_NF7*/ +-#define R0900_GAINLLR_NF7 0xfa46 +-#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f +- +-/*GAINLLR_NF8*/ +-#define R0900_GAINLLR_NF8 0xfa47 +-#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f +- +-/*GAINLLR_NF9*/ +-#define R0900_GAINLLR_NF9 0xfa48 +-#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f +- +-/*GAINLLR_NF10*/ +-#define R0900_GAINLLR_NF10 0xfa49 +-#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f +- +-/*GAINLLR_NF11*/ +-#define R0900_GAINLLR_NF11 0xfa4a +-#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f +- +-/*GAINLLR_NF12*/ +-#define R0900_GAINLLR_NF12 0xfa4b +-#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f +- +-/*GAINLLR_NF13*/ +-#define R0900_GAINLLR_NF13 0xfa4c +-#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f +- +-/*GAINLLR_NF14*/ +-#define R0900_GAINLLR_NF14 0xfa4d +-#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f +- +-/*GAINLLR_NF15*/ +-#define R0900_GAINLLR_NF15 0xfa4e +-#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f +- +-/*GAINLLR_NF16*/ +-#define R0900_GAINLLR_NF16 0xfa4f +-#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f +- +-/*GAINLLR_NF17*/ +-#define R0900_GAINLLR_NF17 0xfa50 +-#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f +- +-/*CFGEXT*/ +-#define R0900_CFGEXT 0xfa80 +-#define F0900_STAGMODE 0xfa800080 +-#define F0900_BYPBCH 0xfa800040 +-#define F0900_BYPLDPC 0xfa800020 +-#define F0900_LDPCMODE 0xfa800010 +-#define F0900_INVLLRSIGN 0xfa800008 +-#define F0900_SHORTMULT 0xfa800004 +-#define F0900_EXTERNTX 0xfa800001 +- +-/*GENCFG*/ +-#define R0900_GENCFG 0xfa86 +-#define F0900_BROADCAST 0xfa860010 +-#define F0900_PRIORITY 0xfa860002 +-#define F0900_DDEMOD 0xfa860001 +- +-/*LDPCERR1*/ +-#define R0900_LDPCERR1 0xfa96 +-#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff +- +-/*LDPCERR0*/ +-#define R0900_LDPCERR0 0xfa97 +-#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff +- +-/*BCHERR*/ +-#define R0900_BCHERR 0xfa98 +-#define F0900_ERRORFLAG 0xfa980010 +-#define F0900_BCH_ERRORS_COUNTER 0xfa98000f +- +-/*TSTRES0*/ +-#define R0900_TSTRES0 0xff11 +-#define F0900_FRESFEC 0xff110080 +- +-/*P2_TCTL4*/ +-#define R0900_P2_TCTL4 0xff28 +-#define F0900_P2_PN4_SELECT 0xff280020 +- +-/*P1_TCTL4*/ +-#define R0900_P1_TCTL4 0xff48 +-#define TCTL4 shiftx(R0900_P1_TCTL4, demod, 0x20) +-#define F0900_P1_PN4_SELECT 0xff480020 +- +-/*P2_TSTDISRX*/ +-#define R0900_P2_TSTDISRX 0xff65 +-#define F0900_P2_PIN_SELECT1 0xff650008 +- +-/*P1_TSTDISRX*/ +-#define R0900_P1_TSTDISRX 0xff67 +-#define TSTDISRX shiftx(R0900_P1_TSTDISRX, demod, 2) +-#define F0900_P1_PIN_SELECT1 0xff670008 +-#define PIN_SELECT1 shiftx(F0900_P1_PIN_SELECT1, demod, 0x20000) +- +-#define STV0900_NBREGS 723 +-#define STV0900_NBFIELDS 1420 +- +-#endif +- +diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c +deleted file mode 100644 +index ba0709b..0000000 +--- a/drivers/media/dvb/frontends/stv0900_sw.c ++++ /dev/null +@@ -1,2034 +0,0 @@ +-/* +- * stv0900_sw.c +- * +- * Driver for ST STV0900 satellite demodulator IC. +- * +- * Copyright (C) ST Microelectronics. +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include "stv0900.h" +-#include "stv0900_reg.h" +-#include "stv0900_priv.h" +- +-s32 shiftx(s32 x, int demod, s32 shift) +-{ +- if (demod == 1) +- return x - shift; +- +- return x; +-} +- +-int stv0900_check_signal_presence(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- s32 carr_offset, +- agc2_integr, +- max_carrier; +- +- int no_signal = FALSE; +- +- carr_offset = (stv0900_read_reg(intp, CFR2) << 8) +- | stv0900_read_reg(intp, CFR1); +- carr_offset = ge2comp(carr_offset, 16); +- agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8) +- | stv0900_read_reg(intp, AGC2I0); +- max_carrier = intp->srch_range[demod] / 1000; +- +- max_carrier += (max_carrier / 10); +- max_carrier = 65536 * (max_carrier / 2); +- max_carrier /= intp->mclk / 1000; +- if (max_carrier > 0x4000) +- max_carrier = 0x4000; +- +- if ((agc2_integr > 0x2000) +- || (carr_offset > (2 * max_carrier)) +- || (carr_offset < (-2 * max_carrier))) +- no_signal = TRUE; +- +- return no_signal; +-} +- +-static void stv0900_get_sw_loop_params(struct stv0900_internal *intp, +- s32 *frequency_inc, s32 *sw_timeout, +- s32 *steps, +- enum fe_stv0900_demod_num demod) +-{ +- s32 timeout, freq_inc, max_steps, srate, max_carrier; +- +- enum fe_stv0900_search_standard standard; +- +- srate = intp->symbol_rate[demod]; +- max_carrier = intp->srch_range[demod] / 1000; +- max_carrier += max_carrier / 10; +- standard = intp->srch_standard[demod]; +- +- max_carrier = 65536 * (max_carrier / 2); +- max_carrier /= intp->mclk / 1000; +- +- if (max_carrier > 0x4000) +- max_carrier = 0x4000; +- +- freq_inc = srate; +- freq_inc /= intp->mclk >> 10; +- freq_inc = freq_inc << 6; +- +- switch (standard) { +- case STV0900_SEARCH_DVBS1: +- case STV0900_SEARCH_DSS: +- freq_inc *= 3; +- timeout = 20; +- break; +- case STV0900_SEARCH_DVBS2: +- freq_inc *= 4; +- timeout = 25; +- break; +- case STV0900_AUTO_SEARCH: +- default: +- freq_inc *= 3; +- timeout = 25; +- break; +- } +- +- freq_inc /= 100; +- +- if ((freq_inc > max_carrier) || (freq_inc < 0)) +- freq_inc = max_carrier / 2; +- +- timeout *= 27500; +- +- if (srate > 0) +- timeout /= srate / 1000; +- +- if ((timeout > 100) || (timeout < 0)) +- timeout = 100; +- +- max_steps = (max_carrier / freq_inc) + 1; +- +- if ((max_steps > 100) || (max_steps < 0)) { +- max_steps = 100; +- freq_inc = max_carrier / max_steps; +- } +- +- *frequency_inc = freq_inc; +- *sw_timeout = timeout; +- *steps = max_steps; +- +-} +- +-static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, +- s32 FreqIncr, s32 Timeout, int zigzag, +- s32 MaxStep, enum fe_stv0900_demod_num demod) +-{ +- int no_signal, +- lock = FALSE; +- s32 stepCpt, +- freqOffset, +- max_carrier; +- +- max_carrier = intp->srch_range[demod] / 1000; +- max_carrier += (max_carrier / 10); +- +- max_carrier = 65536 * (max_carrier / 2); +- max_carrier /= intp->mclk / 1000; +- +- if (max_carrier > 0x4000) +- max_carrier = 0x4000; +- +- if (zigzag == TRUE) +- freqOffset = 0; +- else +- freqOffset = -max_carrier + FreqIncr; +- +- stepCpt = 0; +- +- do { +- stv0900_write_reg(intp, DMDISTATE, 0x1c); +- stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff); +- stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- stv0900_write_bits(intp, ALGOSWRST, 1); +- +- if (intp->chip_id == 0x12) { +- stv0900_write_bits(intp, RST_HWARE, 1); +- stv0900_write_bits(intp, RST_HWARE, 0); +- } +- +- if (zigzag == TRUE) { +- if (freqOffset >= 0) +- freqOffset = -freqOffset - 2 * FreqIncr; +- else +- freqOffset = -freqOffset; +- } else +- freqOffset += + 2 * FreqIncr; +- +- stepCpt++; +- lock = stv0900_get_demod_lock(intp, demod, Timeout); +- no_signal = stv0900_check_signal_presence(intp, demod); +- +- } while ((lock == FALSE) +- && (no_signal == FALSE) +- && ((freqOffset - FreqIncr) < max_carrier) +- && ((freqOffset + FreqIncr) > -max_carrier) +- && (stepCpt < MaxStep)); +- +- stv0900_write_bits(intp, ALGOSWRST, 0); +- +- return lock; +-} +- +-static int stv0900_sw_algo(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- int lock = FALSE, +- no_signal, +- zigzag; +- s32 s2fw, +- fqc_inc, +- sft_stp_tout, +- trial_cntr, +- max_steps; +- +- stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout, +- &max_steps, demod); +- switch (intp->srch_standard[demod]) { +- case STV0900_SEARCH_DVBS1: +- case STV0900_SEARCH_DSS: +- if (intp->chip_id >= 0x20) +- stv0900_write_reg(intp, CARFREQ, 0x3b); +- else +- stv0900_write_reg(intp, CARFREQ, 0xef); +- +- stv0900_write_reg(intp, DMDCFGMD, 0x49); +- zigzag = FALSE; +- break; +- case STV0900_SEARCH_DVBS2: +- if (intp->chip_id >= 0x20) +- stv0900_write_reg(intp, CORRELABS, 0x79); +- else +- stv0900_write_reg(intp, CORRELABS, 0x68); +- +- stv0900_write_reg(intp, DMDCFGMD, 0x89); +- +- zigzag = TRUE; +- break; +- case STV0900_AUTO_SEARCH: +- default: +- if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, CARFREQ, 0x3b); +- stv0900_write_reg(intp, CORRELABS, 0x79); +- } else { +- stv0900_write_reg(intp, CARFREQ, 0xef); +- stv0900_write_reg(intp, CORRELABS, 0x68); +- } +- +- stv0900_write_reg(intp, DMDCFGMD, 0xc9); +- zigzag = FALSE; +- break; +- } +- +- trial_cntr = 0; +- do { +- lock = stv0900_search_carr_sw_loop(intp, +- fqc_inc, +- sft_stp_tout, +- zigzag, +- max_steps, +- demod); +- no_signal = stv0900_check_signal_presence(intp, demod); +- trial_cntr++; +- if ((lock == TRUE) +- || (no_signal == TRUE) +- || (trial_cntr == 2)) { +- +- if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, CARFREQ, 0x49); +- stv0900_write_reg(intp, CORRELABS, 0x9e); +- } else { +- stv0900_write_reg(intp, CARFREQ, 0xed); +- stv0900_write_reg(intp, CORRELABS, 0x88); +- } +- +- if ((stv0900_get_bits(intp, HEADER_MODE) == +- STV0900_DVBS2_FOUND) && +- (lock == TRUE)) { +- msleep(sft_stp_tout); +- s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT); +- +- if (s2fw < 0xd) { +- msleep(sft_stp_tout); +- s2fw = stv0900_get_bits(intp, +- FLYWHEEL_CPT); +- } +- +- if (s2fw < 0xd) { +- lock = FALSE; +- +- if (trial_cntr < 2) { +- if (intp->chip_id >= 0x20) +- stv0900_write_reg(intp, +- CORRELABS, +- 0x79); +- else +- stv0900_write_reg(intp, +- CORRELABS, +- 0x68); +- +- stv0900_write_reg(intp, +- DMDCFGMD, +- 0x89); +- } +- } +- } +- } +- +- } while ((lock == FALSE) +- && (trial_cntr < 2) +- && (no_signal == FALSE)); +- +- return lock; +-} +- +-static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp, +- u32 mclk, +- enum fe_stv0900_demod_num demod) +-{ +- s32 rem1, rem2, intval1, intval2, srate; +- +- srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) + +- (stv0900_get_bits(intp, SYMB_FREQ2) << 16) + +- (stv0900_get_bits(intp, SYMB_FREQ1) << 8) + +- (stv0900_get_bits(intp, SYMB_FREQ0)); +- dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n", +- srate, stv0900_get_bits(intp, SYMB_FREQ0), +- stv0900_get_bits(intp, SYMB_FREQ1), +- stv0900_get_bits(intp, SYMB_FREQ2), +- stv0900_get_bits(intp, SYMB_FREQ3)); +- +- intval1 = (mclk) >> 16; +- intval2 = (srate) >> 16; +- +- rem1 = (mclk) % 0x10000; +- rem2 = (srate) % 0x10000; +- srate = (intval1 * intval2) + +- ((intval1 * rem2) >> 16) + +- ((intval2 * rem1) >> 16); +- +- return srate; +-} +- +-static void stv0900_set_symbol_rate(struct stv0900_internal *intp, +- u32 mclk, u32 srate, +- enum fe_stv0900_demod_num demod) +-{ +- u32 symb; +- +- dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, +- srate, demod); +- +- if (srate > 60000000) { +- symb = srate << 4; +- symb /= (mclk >> 12); +- } else if (srate > 6000000) { +- symb = srate << 6; +- symb /= (mclk >> 10); +- } else { +- symb = srate << 9; +- symb /= (mclk >> 7); +- } +- +- stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f); +- stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff)); +-} +- +-static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp, +- u32 mclk, u32 srate, +- enum fe_stv0900_demod_num demod) +-{ +- u32 symb; +- +- srate = 105 * (srate / 100); +- +- if (srate > 60000000) { +- symb = srate << 4; +- symb /= (mclk >> 12); +- } else if (srate > 6000000) { +- symb = srate << 6; +- symb /= (mclk >> 10); +- } else { +- symb = srate << 9; +- symb /= (mclk >> 7); +- } +- +- if (symb < 0x7fff) { +- stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f); +- stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff)); +- } else { +- stv0900_write_reg(intp, SFRUP1, 0x7f); +- stv0900_write_reg(intp, SFRUP1 + 1, 0xff); +- } +-} +- +-static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp, +- u32 mclk, u32 srate, +- enum fe_stv0900_demod_num demod) +-{ +- u32 symb; +- +- srate = 95 * (srate / 100); +- if (srate > 60000000) { +- symb = srate << 4; +- symb /= (mclk >> 12); +- +- } else if (srate > 6000000) { +- symb = srate << 6; +- symb /= (mclk >> 10); +- +- } else { +- symb = srate << 9; +- symb /= (mclk >> 7); +- } +- +- stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff); +- stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff)); +-} +- +-static s32 stv0900_get_timing_offst(struct stv0900_internal *intp, +- u32 srate, +- enum fe_stv0900_demod_num demod) +-{ +- s32 timingoffset; +- +- +- timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) + +- (stv0900_read_reg(intp, TMGREG2 + 1) << 8) + +- (stv0900_read_reg(intp, TMGREG2 + 2)); +- +- timingoffset = ge2comp(timingoffset, 24); +- +- +- if (timingoffset == 0) +- timingoffset = 1; +- +- timingoffset = ((s32)srate * 10) / ((s32)0x1000000 / timingoffset); +- timingoffset /= 320; +- +- return timingoffset; +-} +- +-static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- s32 rolloff; +- +- if (intp->chip_id == 0x10) { +- stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); +- rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03; +- stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff); +- } else if (intp->chip_id <= 0x20) +- stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0); +- else /* cut 3.0 */ +- stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0); +-} +- +-static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) +-{ +- u32 rolloff; +- +- switch (ro) { +- case STV0900_20: +- rolloff = 20; +- break; +- case STV0900_25: +- rolloff = 25; +- break; +- case STV0900_35: +- default: +- rolloff = 35; +- break; +- } +- +- return srate + (srate * rolloff) / 100; +-} +- +-static int stv0900_check_timing_lock(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- int timingLock = FALSE; +- s32 i, +- timingcpt = 0; +- u8 car_freq, +- tmg_th_high, +- tmg_th_low; +- +- car_freq = stv0900_read_reg(intp, CARFREQ); +- tmg_th_high = stv0900_read_reg(intp, TMGTHRISE); +- tmg_th_low = stv0900_read_reg(intp, TMGTHFALL); +- stv0900_write_reg(intp, TMGTHRISE, 0x20); +- stv0900_write_reg(intp, TMGTHFALL, 0x0); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- stv0900_write_reg(intp, RTC, 0x80); +- stv0900_write_reg(intp, RTCS2, 0x40); +- stv0900_write_reg(intp, CARFREQ, 0x0); +- stv0900_write_reg(intp, CFRINIT1, 0x0); +- stv0900_write_reg(intp, CFRINIT0, 0x0); +- stv0900_write_reg(intp, AGC2REF, 0x65); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- msleep(7); +- +- for (i = 0; i < 10; i++) { +- if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) +- timingcpt++; +- +- msleep(1); +- } +- +- if (timingcpt >= 3) +- timingLock = TRUE; +- +- stv0900_write_reg(intp, AGC2REF, 0x38); +- stv0900_write_reg(intp, RTC, 0x88); +- stv0900_write_reg(intp, RTCS2, 0x68); +- stv0900_write_reg(intp, CARFREQ, car_freq); +- stv0900_write_reg(intp, TMGTHRISE, tmg_th_high); +- stv0900_write_reg(intp, TMGTHFALL, tmg_th_low); +- +- return timingLock; +-} +- +-static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, +- s32 demod_timeout) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- int lock = FALSE, +- d = demod; +- s32 srate, +- search_range, +- locktimeout, +- currier_step, +- nb_steps, +- current_step, +- direction, +- tuner_freq, +- timeout, +- freq; +- +- srate = intp->symbol_rate[d]; +- search_range = intp->srch_range[d]; +- +- if (srate >= 10000000) +- locktimeout = demod_timeout / 3; +- else +- locktimeout = demod_timeout / 2; +- +- lock = stv0900_get_demod_lock(intp, d, locktimeout); +- +- if (lock != FALSE) +- return lock; +- +- if (srate >= 10000000) { +- if (stv0900_check_timing_lock(intp, d) == TRUE) { +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, DMDISTATE, 0x15); +- lock = stv0900_get_demod_lock(intp, d, demod_timeout); +- } else +- lock = FALSE; +- +- return lock; +- } +- +- if (intp->chip_id <= 0x20) { +- if (srate <= 1000000) +- currier_step = 500; +- else if (srate <= 4000000) +- currier_step = 1000; +- else if (srate <= 7000000) +- currier_step = 2000; +- else if (srate <= 10000000) +- currier_step = 3000; +- else +- currier_step = 5000; +- +- if (srate >= 2000000) { +- timeout = (demod_timeout / 3); +- if (timeout > 1000) +- timeout = 1000; +- } else +- timeout = (demod_timeout / 2); +- } else { +- /*cut 3.0 */ +- currier_step = srate / 4000; +- timeout = (demod_timeout * 3) / 4; +- } +- +- nb_steps = ((search_range / 1000) / currier_step); +- +- if ((nb_steps % 2) != 0) +- nb_steps += 1; +- +- if (nb_steps <= 0) +- nb_steps = 2; +- else if (nb_steps > 12) +- nb_steps = 12; +- +- current_step = 1; +- direction = 1; +- +- if (intp->chip_id <= 0x20) { +- tuner_freq = intp->freq[d]; +- intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d], +- intp->rolloff) + intp->symbol_rate[d]; +- } else +- tuner_freq = 0; +- +- while ((current_step <= nb_steps) && (lock == FALSE)) { +- if (direction > 0) +- tuner_freq += (current_step * currier_step); +- else +- tuner_freq -= (current_step * currier_step); +- +- if (intp->chip_id <= 0x20) { +- if (intp->tuner_type[d] == 3) +- stv0900_set_tuner_auto(intp, tuner_freq, +- intp->bw[d], demod); +- else +- stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); +- +- stv0900_write_reg(intp, DMDISTATE, 0x1c); +- stv0900_write_reg(intp, CFRINIT1, 0); +- stv0900_write_reg(intp, CFRINIT0, 0); +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, DMDISTATE, 0x15); +- } else { +- stv0900_write_reg(intp, DMDISTATE, 0x1c); +- freq = (tuner_freq * 65536) / (intp->mclk / 1000); +- stv0900_write_bits(intp, CFR_INIT1, MSB(freq)); +- stv0900_write_bits(intp, CFR_INIT0, LSB(freq)); +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, DMDISTATE, 0x05); +- } +- +- lock = stv0900_get_demod_lock(intp, d, timeout); +- direction *= -1; +- current_step++; +- } +- +- return lock; +-} +- +-static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, +- s32 srate, +- enum fe_stv0900_search_algo algo) +-{ +- switch (algo) { +- case STV0900_BLIND_SEARCH: +- if (srate <= 1500000) { +- (*demod_timeout) = 1500; +- (*fec_timeout) = 400; +- } else if (srate <= 5000000) { +- (*demod_timeout) = 1000; +- (*fec_timeout) = 300; +- } else { +- (*demod_timeout) = 700; +- (*fec_timeout) = 100; +- } +- +- break; +- case STV0900_COLD_START: +- case STV0900_WARM_START: +- default: +- if (srate <= 1000000) { +- (*demod_timeout) = 3000; +- (*fec_timeout) = 1700; +- } else if (srate <= 2000000) { +- (*demod_timeout) = 2500; +- (*fec_timeout) = 1100; +- } else if (srate <= 5000000) { +- (*demod_timeout) = 1000; +- (*fec_timeout) = 550; +- } else if (srate <= 10000000) { +- (*demod_timeout) = 700; +- (*fec_timeout) = 250; +- } else if (srate <= 20000000) { +- (*demod_timeout) = 400; +- (*fec_timeout) = 130; +- } else { +- (*demod_timeout) = 300; +- (*fec_timeout) = 100; +- } +- +- break; +- +- } +- +- if (algo == STV0900_WARM_START) +- (*demod_timeout) /= 2; +-} +- +-static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- +- s32 vth_reg = VTH12; +- +- dprintk("%s\n", __func__); +- +- stv0900_write_reg(intp, vth_reg++, 0xd0); +- stv0900_write_reg(intp, vth_reg++, 0x7d); +- stv0900_write_reg(intp, vth_reg++, 0x53); +- stv0900_write_reg(intp, vth_reg++, 0x2f); +- stv0900_write_reg(intp, vth_reg++, 0x24); +- stv0900_write_reg(intp, vth_reg++, 0x1f); +-} +- +-static void stv0900_set_viterbi_standard(struct stv0900_internal *intp, +- enum fe_stv0900_search_standard standard, +- enum fe_stv0900_fec fec, +- enum fe_stv0900_demod_num demod) +-{ +- dprintk("%s: ViterbiStandard = ", __func__); +- +- switch (standard) { +- case STV0900_AUTO_SEARCH: +- dprintk("Auto\n"); +- stv0900_write_reg(intp, FECM, 0x10); +- stv0900_write_reg(intp, PRVIT, 0x3f); +- break; +- case STV0900_SEARCH_DVBS1: +- dprintk("DVBS1\n"); +- stv0900_write_reg(intp, FECM, 0x00); +- switch (fec) { +- case STV0900_FEC_UNKNOWN: +- default: +- stv0900_write_reg(intp, PRVIT, 0x2f); +- break; +- case STV0900_FEC_1_2: +- stv0900_write_reg(intp, PRVIT, 0x01); +- break; +- case STV0900_FEC_2_3: +- stv0900_write_reg(intp, PRVIT, 0x02); +- break; +- case STV0900_FEC_3_4: +- stv0900_write_reg(intp, PRVIT, 0x04); +- break; +- case STV0900_FEC_5_6: +- stv0900_write_reg(intp, PRVIT, 0x08); +- break; +- case STV0900_FEC_7_8: +- stv0900_write_reg(intp, PRVIT, 0x20); +- break; +- } +- +- break; +- case STV0900_SEARCH_DSS: +- dprintk("DSS\n"); +- stv0900_write_reg(intp, FECM, 0x80); +- switch (fec) { +- case STV0900_FEC_UNKNOWN: +- default: +- stv0900_write_reg(intp, PRVIT, 0x13); +- break; +- case STV0900_FEC_1_2: +- stv0900_write_reg(intp, PRVIT, 0x01); +- break; +- case STV0900_FEC_2_3: +- stv0900_write_reg(intp, PRVIT, 0x02); +- break; +- case STV0900_FEC_6_7: +- stv0900_write_reg(intp, PRVIT, 0x10); +- break; +- } +- break; +- default: +- break; +- } +-} +- +-static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- enum fe_stv0900_fec prate; +- s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN); +- +- switch (rate_fld) { +- case 13: +- prate = STV0900_FEC_1_2; +- break; +- case 18: +- prate = STV0900_FEC_2_3; +- break; +- case 21: +- prate = STV0900_FEC_3_4; +- break; +- case 24: +- prate = STV0900_FEC_5_6; +- break; +- case 25: +- prate = STV0900_FEC_6_7; +- break; +- case 26: +- prate = STV0900_FEC_7_8; +- break; +- default: +- prate = STV0900_FEC_UNKNOWN; +- break; +- } +- +- return prate; +-} +- +-static void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod, +- u32 srate) +-{ +- if (intp->chip_id >= 0x30) { +- if (srate >= 15000000) { +- stv0900_write_reg(intp, ACLC, 0x2b); +- stv0900_write_reg(intp, BCLC, 0x1a); +- } else if ((srate >= 7000000) && (15000000 > srate)) { +- stv0900_write_reg(intp, ACLC, 0x0c); +- stv0900_write_reg(intp, BCLC, 0x1b); +- } else if (srate < 7000000) { +- stv0900_write_reg(intp, ACLC, 0x2c); +- stv0900_write_reg(intp, BCLC, 0x1c); +- } +- +- } else { /*cut 2.0 and 1.x*/ +- stv0900_write_reg(intp, ACLC, 0x1a); +- stv0900_write_reg(intp, BCLC, 0x09); +- } +- +-} +- +-static void stv0900_track_optimization(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- s32 srate, +- pilots, +- aclc, +- freq1, +- freq0, +- i = 0, +- timed, +- timef, +- blind_tun_sw = 0, +- modulation; +- +- enum fe_stv0900_rolloff rolloff; +- enum fe_stv0900_modcode foundModcod; +- +- dprintk("%s\n", __func__); +- +- srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); +- srate += stv0900_get_timing_offst(intp, srate, demod); +- +- switch (intp->result[demod].standard) { +- case STV0900_DVBS1_STANDARD: +- case STV0900_DSS_STANDARD: +- dprintk("%s: found DVB-S or DSS\n", __func__); +- if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) { +- stv0900_write_bits(intp, DVBS1_ENABLE, 1); +- stv0900_write_bits(intp, DVBS2_ENABLE, 0); +- } +- +- stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff); +- stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); +- +- if (intp->chip_id < 0x30) { +- stv0900_write_reg(intp, ERRCTRL1, 0x75); +- break; +- } +- +- if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) { +- stv0900_write_reg(intp, GAUSSR0, 0x98); +- stv0900_write_reg(intp, CCIR0, 0x18); +- } else { +- stv0900_write_reg(intp, GAUSSR0, 0x18); +- stv0900_write_reg(intp, CCIR0, 0x18); +- } +- +- stv0900_write_reg(intp, ERRCTRL1, 0x75); +- break; +- case STV0900_DVBS2_STANDARD: +- dprintk("%s: found DVB-S2\n", __func__); +- stv0900_write_bits(intp, DVBS1_ENABLE, 0); +- stv0900_write_bits(intp, DVBS2_ENABLE, 1); +- stv0900_write_reg(intp, ACLC, 0); +- stv0900_write_reg(intp, BCLC, 0); +- if (intp->result[demod].frame_len == STV0900_LONG_FRAME) { +- foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD); +- pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; +- aclc = stv0900_get_optim_carr_loop(srate, +- foundModcod, +- pilots, +- intp->chip_id); +- if (foundModcod <= STV0900_QPSK_910) +- stv0900_write_reg(intp, ACLC2S2Q, aclc); +- else if (foundModcod <= STV0900_8PSK_910) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S28, aclc); +- } +- +- if ((intp->demod_mode == STV0900_SINGLE) && +- (foundModcod > STV0900_8PSK_910)) { +- if (foundModcod <= STV0900_16APSK_910) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S216A, +- aclc); +- } else if (foundModcod <= STV0900_32APSK_910) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S232A, +- aclc); +- } +- } +- +- } else { +- modulation = intp->result[demod].modulation; +- aclc = stv0900_get_optim_short_carr_loop(srate, +- modulation, intp->chip_id); +- if (modulation == STV0900_QPSK) +- stv0900_write_reg(intp, ACLC2S2Q, aclc); +- else if (modulation == STV0900_8PSK) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S28, aclc); +- } else if (modulation == STV0900_16APSK) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S216A, aclc); +- } else if (modulation == STV0900_32APSK) { +- stv0900_write_reg(intp, ACLC2S2Q, 0x2a); +- stv0900_write_reg(intp, ACLC2S232A, aclc); +- } +- +- } +- +- if (intp->chip_id <= 0x11) { +- if (intp->demod_mode != STV0900_SINGLE) +- stv0900_activate_s2_modcod(intp, demod); +- +- } +- +- stv0900_write_reg(intp, ERRCTRL1, 0x67); +- break; +- case STV0900_UNKNOWN_STANDARD: +- default: +- dprintk("%s: found unknown standard\n", __func__); +- stv0900_write_bits(intp, DVBS1_ENABLE, 1); +- stv0900_write_bits(intp, DVBS2_ENABLE, 1); +- break; +- } +- +- freq1 = stv0900_read_reg(intp, CFR2); +- freq0 = stv0900_read_reg(intp, CFR1); +- rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); +- if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { +- stv0900_write_reg(intp, SFRSTEP, 0x00); +- stv0900_write_bits(intp, SCAN_ENABLE, 0); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- stv0900_write_reg(intp, TMGCFG2, 0xc1); +- stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); +- blind_tun_sw = 1; +- if (intp->result[demod].standard != STV0900_DVBS2_STANDARD) +- stv0900_set_dvbs1_track_car_loop(intp, demod, srate); +- +- } +- +- if (intp->chip_id >= 0x20) { +- if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || +- (intp->srch_standard[demod] == +- STV0900_SEARCH_DSS) || +- (intp->srch_standard[demod] == +- STV0900_AUTO_SEARCH)) { +- stv0900_write_reg(intp, VAVSRVIT, 0x0a); +- stv0900_write_reg(intp, VITSCALE, 0x0); +- } +- } +- +- if (intp->chip_id < 0x20) +- stv0900_write_reg(intp, CARHDR, 0x08); +- +- if (intp->chip_id == 0x10) +- stv0900_write_reg(intp, CORRELEXP, 0x0a); +- +- stv0900_write_reg(intp, AGC2REF, 0x38); +- +- if ((intp->chip_id >= 0x20) || +- (blind_tun_sw == 1) || +- (intp->symbol_rate[demod] < 10000000)) { +- stv0900_write_reg(intp, CFRINIT1, freq1); +- stv0900_write_reg(intp, CFRINIT0, freq0); +- intp->bw[demod] = stv0900_carrier_width(srate, +- intp->rolloff) + 10000000; +- +- if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { +- if (intp->srch_algo[demod] != STV0900_WARM_START) { +- if (intp->tuner_type[demod] == 3) +- stv0900_set_tuner_auto(intp, +- intp->freq[demod], +- intp->bw[demod], +- demod); +- else +- stv0900_set_bandwidth(fe, +- intp->bw[demod]); +- } +- } +- +- if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || +- (intp->symbol_rate[demod] < 10000000)) +- msleep(50); +- else +- msleep(5); +- +- stv0900_get_lock_timeout(&timed, &timef, srate, +- STV0900_WARM_START); +- +- if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) { +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, CFRINIT1, freq1); +- stv0900_write_reg(intp, CFRINIT0, freq0); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- i = 0; +- while ((stv0900_get_demod_lock(intp, +- demod, +- timed / 2) == FALSE) && +- (i <= 2)) { +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, CFRINIT1, freq1); +- stv0900_write_reg(intp, CFRINIT0, freq0); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- i++; +- } +- } +- +- } +- +- if (intp->chip_id >= 0x20) +- stv0900_write_reg(intp, CARFREQ, 0x49); +- +- if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) || +- (intp->result[demod].standard == STV0900_DSS_STANDARD)) +- stv0900_set_viterbi_tracq(intp, demod); +- +-} +- +-static int stv0900_get_fec_lock(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod, s32 time_out) +-{ +- s32 timer = 0, lock = 0; +- +- enum fe_stv0900_search_state dmd_state; +- +- dprintk("%s\n", __func__); +- +- dmd_state = stv0900_get_bits(intp, HEADER_MODE); +- +- while ((timer < time_out) && (lock == 0)) { +- switch (dmd_state) { +- case STV0900_SEARCH: +- case STV0900_PLH_DETECTED: +- default: +- lock = 0; +- break; +- case STV0900_DVBS2_FOUND: +- lock = stv0900_get_bits(intp, PKTDELIN_LOCK); +- break; +- case STV0900_DVBS_FOUND: +- lock = stv0900_get_bits(intp, LOCKEDVIT); +- break; +- } +- +- if (lock == 0) { +- msleep(10); +- timer += 10; +- } +- } +- +- if (lock) +- dprintk("%s: DEMOD FEC LOCK OK\n", __func__); +- else +- dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__); +- +- return lock; +-} +- +-static int stv0900_wait_for_lock(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod, +- s32 dmd_timeout, s32 fec_timeout) +-{ +- +- s32 timer = 0, lock = 0; +- +- dprintk("%s\n", __func__); +- +- lock = stv0900_get_demod_lock(intp, demod, dmd_timeout); +- +- if (lock) +- lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout); +- +- if (lock) { +- lock = 0; +- +- dprintk("%s: Timer = %d, time_out = %d\n", +- __func__, timer, fec_timeout); +- +- while ((timer < fec_timeout) && (lock == 0)) { +- lock = stv0900_get_bits(intp, TSFIFO_LINEOK); +- msleep(1); +- timer++; +- } +- } +- +- if (lock) +- dprintk("%s: DEMOD LOCK OK\n", __func__); +- else +- dprintk("%s: DEMOD LOCK FAIL\n", __func__); +- +- if (lock) +- return TRUE; +- else +- return FALSE; +-} +- +-enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, +- enum fe_stv0900_demod_num demod) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_tracking_standard fnd_standard; +- +- int hdr_mode = stv0900_get_bits(intp, HEADER_MODE); +- +- switch (hdr_mode) { +- case 2: +- fnd_standard = STV0900_DVBS2_STANDARD; +- break; +- case 3: +- if (stv0900_get_bits(intp, DSS_DVB) == 1) +- fnd_standard = STV0900_DSS_STANDARD; +- else +- fnd_standard = STV0900_DVBS1_STANDARD; +- +- break; +- default: +- fnd_standard = STV0900_UNKNOWN_STANDARD; +- } +- +- dprintk("%s: standard %d\n", __func__, fnd_standard); +- +- return fnd_standard; +-} +- +-static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk, +- enum fe_stv0900_demod_num demod) +-{ +- s32 derot, +- rem1, +- rem2, +- intval1, +- intval2; +- +- derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) + +- (stv0900_get_bits(intp, CAR_FREQ1) << 8) + +- (stv0900_get_bits(intp, CAR_FREQ0)); +- +- derot = ge2comp(derot, 24); +- intval1 = mclk >> 12; +- intval2 = derot >> 12; +- rem1 = mclk % 0x1000; +- rem2 = derot % 0x1000; +- derot = (intval1 * intval2) + +- ((intval1 * rem2) >> 12) + +- ((intval2 * rem1) >> 12); +- +- return derot; +-} +- +-static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- u32 freq = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- +- if (tuner_ops->get_frequency) { +- if ((tuner_ops->get_frequency(fe, &freq)) < 0) +- dprintk("%s: Invalid parameter\n", __func__); +- else +- dprintk("%s: Frequency=%d\n", __func__, freq); +- +- } +- +- return freq; +-} +- +-static enum +-fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE; +- struct stv0900_signal_info *result = &intp->result[demod]; +- s32 offsetFreq, +- srate_offset; +- int i = 0, +- d = demod; +- +- u8 timing; +- +- msleep(5); +- if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) { +- timing = stv0900_read_reg(intp, TMGREG2); +- i = 0; +- stv0900_write_reg(intp, SFRSTEP, 0x5c); +- +- while ((i <= 50) && (timing != 0) && (timing != 0xff)) { +- timing = stv0900_read_reg(intp, TMGREG2); +- msleep(5); +- i += 5; +- } +- } +- +- result->standard = stv0900_get_standard(fe, d); +- if (intp->tuner_type[demod] == 3) +- result->frequency = stv0900_get_freq_auto(intp, d); +- else +- result->frequency = stv0900_get_tuner_freq(fe); +- +- offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; +- result->frequency += offsetFreq; +- result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); +- srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d); +- result->symbol_rate += srate_offset; +- result->fec = stv0900_get_vit_fec(intp, d); +- result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD); +- result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; +- result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; +- result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); +- +- dprintk("%s: modcode=0x%x \n", __func__, result->modcode); +- +- switch (result->standard) { +- case STV0900_DVBS2_STANDARD: +- result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); +- if (result->modcode <= STV0900_QPSK_910) +- result->modulation = STV0900_QPSK; +- else if (result->modcode <= STV0900_8PSK_910) +- result->modulation = STV0900_8PSK; +- else if (result->modcode <= STV0900_16APSK_910) +- result->modulation = STV0900_16APSK; +- else if (result->modcode <= STV0900_32APSK_910) +- result->modulation = STV0900_32APSK; +- else +- result->modulation = STV0900_UNKNOWN; +- break; +- case STV0900_DVBS1_STANDARD: +- case STV0900_DSS_STANDARD: +- result->spectrum = stv0900_get_bits(intp, IQINV); +- result->modulation = STV0900_QPSK; +- break; +- default: +- break; +- } +- +- if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || +- (intp->symbol_rate[d] < 10000000)) { +- offsetFreq = result->frequency - intp->freq[d]; +- if (intp->tuner_type[demod] == 3) +- intp->freq[d] = stv0900_get_freq_auto(intp, d); +- else +- intp->freq[d] = stv0900_get_tuner_freq(fe); +- +- if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) +- range = STV0900_RANGEOK; +- else if (ABS(offsetFreq) <= +- (stv0900_carrier_width(result->symbol_rate, +- result->rolloff) / 2000)) +- range = STV0900_RANGEOK; +- +- } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) +- range = STV0900_RANGEOK; +- +- dprintk("%s: range %d\n", __func__, range); +- +- return range; +-} +- +-static enum +-fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- enum fe_stv0900_signal_type signal_type = STV0900_NODATA; +- +- s32 srate, +- demod_timeout, +- fec_timeout, +- freq1, +- freq0; +- +- intp->result[demod].locked = FALSE; +- +- if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) { +- srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); +- srate += stv0900_get_timing_offst(intp, srate, demod); +- if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) +- stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); +- +- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, +- srate, STV0900_WARM_START); +- freq1 = stv0900_read_reg(intp, CFR2); +- freq0 = stv0900_read_reg(intp, CFR1); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- stv0900_write_bits(intp, SPECINV_CONTROL, +- STV0900_IQ_FORCE_SWAPPED); +- stv0900_write_reg(intp, DMDISTATE, 0x1c); +- stv0900_write_reg(intp, CFRINIT1, freq1); +- stv0900_write_reg(intp, CFRINIT0, freq0); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- if (stv0900_wait_for_lock(intp, demod, +- demod_timeout, fec_timeout) == TRUE) { +- intp->result[demod].locked = TRUE; +- signal_type = stv0900_get_signal_params(fe); +- stv0900_track_optimization(fe); +- } else { +- stv0900_write_bits(intp, SPECINV_CONTROL, +- STV0900_IQ_FORCE_NORMAL); +- stv0900_write_reg(intp, DMDISTATE, 0x1c); +- stv0900_write_reg(intp, CFRINIT1, freq1); +- stv0900_write_reg(intp, CFRINIT0, freq0); +- stv0900_write_reg(intp, DMDISTATE, 0x18); +- if (stv0900_wait_for_lock(intp, demod, +- demod_timeout, fec_timeout) == TRUE) { +- intp->result[demod].locked = TRUE; +- signal_type = stv0900_get_signal_params(fe); +- stv0900_track_optimization(fe); +- } +- +- } +- +- } else +- intp->result[demod].locked = FALSE; +- +- return signal_type; +-} +- +-static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- u32 minagc2level = 0xffff, +- agc2level, +- init_freq, freq_step; +- +- s32 i, j, nb_steps, direction; +- +- dprintk("%s\n", __func__); +- +- stv0900_write_reg(intp, AGC2REF, 0x38); +- stv0900_write_bits(intp, SCAN_ENABLE, 0); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- +- stv0900_write_bits(intp, AUTO_GUP, 1); +- stv0900_write_bits(intp, AUTO_GLOW, 1); +- +- stv0900_write_reg(intp, DMDT0M, 0x0); +- +- stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); +- nb_steps = -1 + (intp->srch_range[demod] / 1000000); +- nb_steps /= 2; +- nb_steps = (2 * nb_steps) + 1; +- +- if (nb_steps < 0) +- nb_steps = 1; +- +- direction = 1; +- +- freq_step = (1000000 << 8) / (intp->mclk >> 8); +- +- init_freq = 0; +- +- for (i = 0; i < nb_steps; i++) { +- if (direction > 0) +- init_freq = init_freq + (freq_step * i); +- else +- init_freq = init_freq - (freq_step * i); +- +- direction *= -1; +- stv0900_write_reg(intp, DMDISTATE, 0x5C); +- stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff); +- stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff); +- stv0900_write_reg(intp, DMDISTATE, 0x58); +- msleep(10); +- agc2level = 0; +- +- for (j = 0; j < 10; j++) +- agc2level += (stv0900_read_reg(intp, AGC2I1) << 8) +- | stv0900_read_reg(intp, AGC2I0); +- +- agc2level /= 10; +- +- if (agc2level < minagc2level) +- minagc2level = agc2level; +- +- } +- +- return (u16)minagc2level; +-} +- +-static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- int timing_lck = FALSE; +- s32 i, timingcpt = 0, +- direction = 1, +- nb_steps, +- current_step = 0, +- tuner_freq; +- u32 agc2_th, +- coarse_srate = 0, +- agc2_integr = 0, +- currier_step = 1200; +- +- if (intp->chip_id >= 0x30) +- agc2_th = 0x2e00; +- else +- agc2_th = 0x1f00; +- +- stv0900_write_bits(intp, DEMOD_MODE, 0x1f); +- stv0900_write_reg(intp, TMGCFG, 0x12); +- stv0900_write_reg(intp, TMGTHRISE, 0xf0); +- stv0900_write_reg(intp, TMGTHFALL, 0xe0); +- stv0900_write_bits(intp, SCAN_ENABLE, 1); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 1); +- stv0900_write_reg(intp, SFRUP1, 0x83); +- stv0900_write_reg(intp, SFRUP0, 0xc0); +- stv0900_write_reg(intp, SFRLOW1, 0x82); +- stv0900_write_reg(intp, SFRLOW0, 0xa0); +- stv0900_write_reg(intp, DMDT0M, 0x0); +- stv0900_write_reg(intp, AGC2REF, 0x50); +- +- if (intp->chip_id >= 0x30) { +- stv0900_write_reg(intp, CARFREQ, 0x99); +- stv0900_write_reg(intp, SFRSTEP, 0x98); +- } else if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, CARFREQ, 0x6a); +- stv0900_write_reg(intp, SFRSTEP, 0x95); +- } else { +- stv0900_write_reg(intp, CARFREQ, 0xed); +- stv0900_write_reg(intp, SFRSTEP, 0x73); +- } +- +- if (intp->symbol_rate[demod] <= 2000000) +- currier_step = 1000; +- else if (intp->symbol_rate[demod] <= 5000000) +- currier_step = 2000; +- else if (intp->symbol_rate[demod] <= 12000000) +- currier_step = 3000; +- else +- currier_step = 5000; +- +- nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step); +- nb_steps /= 2; +- nb_steps = (2 * nb_steps) + 1; +- +- if (nb_steps < 0) +- nb_steps = 1; +- else if (nb_steps > 10) { +- nb_steps = 11; +- currier_step = (intp->srch_range[demod] / 1000) / 10; +- } +- +- current_step = 0; +- direction = 1; +- +- tuner_freq = intp->freq[demod]; +- +- while ((timing_lck == FALSE) && (current_step < nb_steps)) { +- stv0900_write_reg(intp, DMDISTATE, 0x5f); +- stv0900_write_bits(intp, DEMOD_MODE, 0); +- +- msleep(50); +- +- for (i = 0; i < 10; i++) { +- if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) +- timingcpt++; +- +- agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) | +- stv0900_read_reg(intp, AGC2I0); +- } +- +- agc2_integr /= 10; +- coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); +- current_step++; +- direction *= -1; +- +- dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started." +- " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", +- tuner_freq, agc2_integr, coarse_srate, timingcpt); +- +- if ((timingcpt >= 5) && +- (agc2_integr < agc2_th) && +- (coarse_srate < 55000000) && +- (coarse_srate > 850000)) +- timing_lck = TRUE; +- else if (current_step < nb_steps) { +- if (direction > 0) +- tuner_freq += (current_step * currier_step); +- else +- tuner_freq -= (current_step * currier_step); +- +- if (intp->tuner_type[demod] == 3) +- stv0900_set_tuner_auto(intp, tuner_freq, +- intp->bw[demod], demod); +- else +- stv0900_set_tuner(fe, tuner_freq, +- intp->bw[demod]); +- } +- } +- +- if (timing_lck == FALSE) +- coarse_srate = 0; +- else +- coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); +- +- return coarse_srate; +-} +- +-static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- u32 coarse_srate, +- coarse_freq, +- symb, +- symbmax, +- symbmin, +- symbcomp; +- +- coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); +- +- if (coarse_srate > 3000000) { +- symbmax = 13 * (coarse_srate / 10); +- symbmax = (symbmax / 1000) * 65536; +- symbmax /= (intp->mclk / 1000); +- +- symbmin = 10 * (coarse_srate / 13); +- symbmin = (symbmin / 1000)*65536; +- symbmin /= (intp->mclk / 1000); +- +- symb = (coarse_srate / 1000) * 65536; +- symb /= (intp->mclk / 1000); +- } else { +- symbmax = 13 * (coarse_srate / 10); +- symbmax = (symbmax / 100) * 65536; +- symbmax /= (intp->mclk / 100); +- +- symbmin = 10 * (coarse_srate / 14); +- symbmin = (symbmin / 100) * 65536; +- symbmin /= (intp->mclk / 100); +- +- symb = (coarse_srate / 100) * 65536; +- symb /= (intp->mclk / 100); +- } +- +- symbcomp = 13 * (coarse_srate / 10); +- coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) +- | stv0900_read_reg(intp, CFR1); +- +- if (symbcomp < intp->symbol_rate[demod]) +- coarse_srate = 0; +- else { +- stv0900_write_reg(intp, DMDISTATE, 0x1f); +- stv0900_write_reg(intp, TMGCFG2, 0xc1); +- stv0900_write_reg(intp, TMGTHRISE, 0x20); +- stv0900_write_reg(intp, TMGTHFALL, 0x00); +- stv0900_write_reg(intp, TMGCFG, 0xd2); +- stv0900_write_bits(intp, CFR_AUTOSCAN, 0); +- stv0900_write_reg(intp, AGC2REF, 0x38); +- +- if (intp->chip_id >= 0x30) +- stv0900_write_reg(intp, CARFREQ, 0x79); +- else if (intp->chip_id >= 0x20) +- stv0900_write_reg(intp, CARFREQ, 0x49); +- else +- stv0900_write_reg(intp, CARFREQ, 0xed); +- +- stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f); +- stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff)); +- +- stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f); +- stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff)); +- +- stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff); +- stv0900_write_reg(intp, SFRINIT0, (symb & 0xff)); +- +- stv0900_write_reg(intp, DMDT0M, 0x20); +- stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff); +- stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff); +- stv0900_write_reg(intp, DMDISTATE, 0x15); +- } +- +- return coarse_srate; +-} +- +-static int stv0900_blind_search_algo(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- u8 k_ref_tmg, +- k_ref_tmg_max, +- k_ref_tmg_min; +- u32 coarse_srate, +- agc2_th; +- int lock = FALSE, +- coarse_fail = FALSE; +- s32 demod_timeout = 500, +- fec_timeout = 50, +- fail_cpt, +- i, +- agc2_overflow; +- u16 agc2_int; +- u8 dstatus2; +- +- dprintk("%s\n", __func__); +- +- if (intp->chip_id < 0x20) { +- k_ref_tmg_max = 233; +- k_ref_tmg_min = 143; +- } else { +- k_ref_tmg_max = 110; +- k_ref_tmg_min = 10; +- } +- +- if (intp->chip_id <= 0x20) +- agc2_th = STV0900_BLIND_SEARCH_AGC2_TH; +- else +- agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30; +- +- agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); +- +- dprintk("%s agc2_int=%d agc2_th=%d \n", __func__, agc2_int, agc2_th); +- if (agc2_int > agc2_th) +- return FALSE; +- +- if (intp->chip_id == 0x10) +- stv0900_write_reg(intp, CORRELEXP, 0xaa); +- +- if (intp->chip_id < 0x20) +- stv0900_write_reg(intp, CARHDR, 0x55); +- else +- stv0900_write_reg(intp, CARHDR, 0x20); +- +- if (intp->chip_id <= 0x20) +- stv0900_write_reg(intp, CARCFG, 0xc4); +- else +- stv0900_write_reg(intp, CARCFG, 0x6); +- +- stv0900_write_reg(intp, RTCS2, 0x44); +- +- if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, EQUALCFG, 0x41); +- stv0900_write_reg(intp, FFECFG, 0x41); +- stv0900_write_reg(intp, VITSCALE, 0x82); +- stv0900_write_reg(intp, VAVSRVIT, 0x0); +- } +- +- k_ref_tmg = k_ref_tmg_max; +- +- do { +- stv0900_write_reg(intp, KREFTMG, k_ref_tmg); +- if (stv0900_search_srate_coarse(fe) != 0) { +- coarse_srate = stv0900_search_srate_fine(fe); +- +- if (coarse_srate != 0) { +- stv0900_get_lock_timeout(&demod_timeout, +- &fec_timeout, +- coarse_srate, +- STV0900_BLIND_SEARCH); +- lock = stv0900_get_demod_lock(intp, +- demod, +- demod_timeout); +- } else +- lock = FALSE; +- } else { +- fail_cpt = 0; +- agc2_overflow = 0; +- +- for (i = 0; i < 10; i++) { +- agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8) +- | stv0900_read_reg(intp, AGC2I0); +- +- if (agc2_int >= 0xff00) +- agc2_overflow++; +- +- dstatus2 = stv0900_read_reg(intp, DSTATUS2); +- +- if (((dstatus2 & 0x1) == 0x1) && +- ((dstatus2 >> 7) == 1)) +- fail_cpt++; +- } +- +- if ((fail_cpt > 7) || (agc2_overflow > 7)) +- coarse_fail = TRUE; +- +- lock = FALSE; +- } +- k_ref_tmg -= 30; +- } while ((k_ref_tmg >= k_ref_tmg_min) && +- (lock == FALSE) && +- (coarse_fail == FALSE)); +- +- return lock; +-} +- +-static void stv0900_set_viterbi_acq(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- s32 vth_reg = VTH12; +- +- dprintk("%s\n", __func__); +- +- stv0900_write_reg(intp, vth_reg++, 0x96); +- stv0900_write_reg(intp, vth_reg++, 0x64); +- stv0900_write_reg(intp, vth_reg++, 0x36); +- stv0900_write_reg(intp, vth_reg++, 0x23); +- stv0900_write_reg(intp, vth_reg++, 0x1e); +- stv0900_write_reg(intp, vth_reg++, 0x19); +-} +- +-static void stv0900_set_search_standard(struct stv0900_internal *intp, +- enum fe_stv0900_demod_num demod) +-{ +- +- dprintk("%s\n", __func__); +- +- switch (intp->srch_standard[demod]) { +- case STV0900_SEARCH_DVBS1: +- dprintk("Search Standard = DVBS1\n"); +- break; +- case STV0900_SEARCH_DSS: +- dprintk("Search Standard = DSS\n"); +- case STV0900_SEARCH_DVBS2: +- break; +- dprintk("Search Standard = DVBS2\n"); +- case STV0900_AUTO_SEARCH: +- default: +- dprintk("Search Standard = AUTO\n"); +- break; +- } +- +- switch (intp->srch_standard[demod]) { +- case STV0900_SEARCH_DVBS1: +- case STV0900_SEARCH_DSS: +- stv0900_write_bits(intp, DVBS1_ENABLE, 1); +- stv0900_write_bits(intp, DVBS2_ENABLE, 0); +- stv0900_write_bits(intp, STOP_CLKVIT, 0); +- stv0900_set_dvbs1_track_car_loop(intp, +- demod, +- intp->symbol_rate[demod]); +- stv0900_write_reg(intp, CAR2CFG, 0x22); +- +- stv0900_set_viterbi_acq(intp, demod); +- stv0900_set_viterbi_standard(intp, +- intp->srch_standard[demod], +- intp->fec[demod], demod); +- +- break; +- case STV0900_SEARCH_DVBS2: +- stv0900_write_bits(intp, DVBS1_ENABLE, 0); +- stv0900_write_bits(intp, DVBS2_ENABLE, 1); +- stv0900_write_bits(intp, STOP_CLKVIT, 1); +- stv0900_write_reg(intp, ACLC, 0x1a); +- stv0900_write_reg(intp, BCLC, 0x09); +- if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ +- stv0900_write_reg(intp, CAR2CFG, 0x26); +- else +- stv0900_write_reg(intp, CAR2CFG, 0x66); +- +- if (intp->demod_mode != STV0900_SINGLE) { +- if (intp->chip_id <= 0x11) +- stv0900_stop_all_s2_modcod(intp, demod); +- else +- stv0900_activate_s2_modcod(intp, demod); +- +- } else +- stv0900_activate_s2_modcod_single(intp, demod); +- +- stv0900_set_viterbi_tracq(intp, demod); +- +- break; +- case STV0900_AUTO_SEARCH: +- default: +- stv0900_write_bits(intp, DVBS1_ENABLE, 1); +- stv0900_write_bits(intp, DVBS2_ENABLE, 1); +- stv0900_write_bits(intp, STOP_CLKVIT, 0); +- stv0900_write_reg(intp, ACLC, 0x1a); +- stv0900_write_reg(intp, BCLC, 0x09); +- stv0900_set_dvbs1_track_car_loop(intp, +- demod, +- intp->symbol_rate[demod]); +- if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ +- stv0900_write_reg(intp, CAR2CFG, 0x26); +- else +- stv0900_write_reg(intp, CAR2CFG, 0x66); +- +- if (intp->demod_mode != STV0900_SINGLE) { +- if (intp->chip_id <= 0x11) +- stv0900_stop_all_s2_modcod(intp, demod); +- else +- stv0900_activate_s2_modcod(intp, demod); +- +- } else +- stv0900_activate_s2_modcod_single(intp, demod); +- +- stv0900_set_viterbi_tracq(intp, demod); +- stv0900_set_viterbi_standard(intp, +- intp->srch_standard[demod], +- intp->fec[demod], demod); +- +- break; +- } +-} +- +-enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) +-{ +- struct stv0900_state *state = fe->demodulator_priv; +- struct stv0900_internal *intp = state->internal; +- enum fe_stv0900_demod_num demod = state->demod; +- +- s32 demod_timeout = 500, fec_timeout = 50; +- s32 aq_power, agc1_power, i; +- +- int lock = FALSE, low_sr = FALSE; +- +- enum fe_stv0900_signal_type signal_type = STV0900_NOCARRIER; +- enum fe_stv0900_search_algo algo; +- int no_signal = FALSE; +- +- dprintk("%s\n", __func__); +- +- algo = intp->srch_algo[demod]; +- stv0900_write_bits(intp, RST_HWARE, 1); +- stv0900_write_reg(intp, DMDISTATE, 0x5c); +- if (intp->chip_id >= 0x20) { +- if (intp->symbol_rate[demod] > 5000000) +- stv0900_write_reg(intp, CORRELABS, 0x9e); +- else +- stv0900_write_reg(intp, CORRELABS, 0x82); +- } else +- stv0900_write_reg(intp, CORRELABS, 0x88); +- +- stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, +- intp->symbol_rate[demod], +- intp->srch_algo[demod]); +- +- if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { +- intp->bw[demod] = 2 * 36000000; +- +- stv0900_write_reg(intp, TMGCFG2, 0xc0); +- stv0900_write_reg(intp, CORRELMANT, 0x70); +- +- stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); +- } else { +- stv0900_write_reg(intp, DMDT0M, 0x20); +- stv0900_write_reg(intp, TMGCFG, 0xd2); +- +- if (intp->symbol_rate[demod] < 2000000) +- stv0900_write_reg(intp, CORRELMANT, 0x63); +- else +- stv0900_write_reg(intp, CORRELMANT, 0x70); +- +- stv0900_write_reg(intp, AGC2REF, 0x38); +- +- intp->bw[demod] = +- stv0900_carrier_width(intp->symbol_rate[demod], +- intp->rolloff); +- if (intp->chip_id >= 0x20) { +- stv0900_write_reg(intp, KREFTMG, 0x5a); +- +- if (intp->srch_algo[demod] == STV0900_COLD_START) { +- intp->bw[demod] += 10000000; +- intp->bw[demod] *= 15; +- intp->bw[demod] /= 10; +- } else if (intp->srch_algo[demod] == STV0900_WARM_START) +- intp->bw[demod] += 10000000; +- +- } else { +- stv0900_write_reg(intp, KREFTMG, 0xc1); +- intp->bw[demod] += 10000000; +- intp->bw[demod] *= 15; +- intp->bw[demod] /= 10; +- } +- +- stv0900_write_reg(intp, TMGCFG2, 0xc1); +- +- stv0900_set_symbol_rate(intp, intp->mclk, +- intp->symbol_rate[demod], demod); +- stv0900_set_max_symbol_rate(intp, intp->mclk, +- intp->symbol_rate[demod], demod); +- stv0900_set_min_symbol_rate(intp, intp->mclk, +- intp->symbol_rate[demod], demod); +- if (intp->symbol_rate[demod] >= 10000000) +- low_sr = FALSE; +- else +- low_sr = TRUE; +- +- } +- +- if (intp->tuner_type[demod] == 3) +- stv0900_set_tuner_auto(intp, intp->freq[demod], +- intp->bw[demod], demod); +- else +- stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); +- +- agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), +- stv0900_get_bits(intp, AGCIQ_VALUE0)); +- +- aq_power = 0; +- +- if (agc1_power == 0) { +- for (i = 0; i < 5; i++) +- aq_power += (stv0900_get_bits(intp, POWER_I) + +- stv0900_get_bits(intp, POWER_Q)) / 2; +- +- aq_power /= 5; +- } +- +- if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) { +- intp->result[demod].locked = FALSE; +- signal_type = STV0900_NOAGC1; +- dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__); +- } else { +- stv0900_write_bits(intp, SPECINV_CONTROL, +- intp->srch_iq_inv[demod]); +- if (intp->chip_id <= 0x20) /*cut 2.0*/ +- stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); +- else /*cut 3.0*/ +- stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1); +- +- stv0900_set_search_standard(intp, demod); +- +- if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH) +- stv0900_start_search(intp, demod); +- } +- +- if (signal_type == STV0900_NOAGC1) +- return signal_type; +- +- if (intp->chip_id == 0x12) { +- stv0900_write_bits(intp, RST_HWARE, 0); +- msleep(3); +- stv0900_write_bits(intp, RST_HWARE, 1); +- stv0900_write_bits(intp, RST_HWARE, 0); +- } +- +- if (algo == STV0900_BLIND_SEARCH) +- lock = stv0900_blind_search_algo(fe); +- else if (algo == STV0900_COLD_START) +- lock = stv0900_get_demod_cold_lock(fe, demod_timeout); +- else if (algo == STV0900_WARM_START) +- lock = stv0900_get_demod_lock(intp, demod, demod_timeout); +- +- if ((lock == FALSE) && (algo == STV0900_COLD_START)) { +- if (low_sr == FALSE) { +- if (stv0900_check_timing_lock(intp, demod) == TRUE) +- lock = stv0900_sw_algo(intp, demod); +- } +- } +- +- if (lock == TRUE) +- signal_type = stv0900_get_signal_params(fe); +- +- if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) { +- stv0900_track_optimization(fe); +- if (intp->chip_id <= 0x11) { +- if ((stv0900_get_standard(fe, 0) == +- STV0900_DVBS1_STANDARD) && +- (stv0900_get_standard(fe, 1) == +- STV0900_DVBS1_STANDARD)) { +- msleep(20); +- stv0900_write_bits(intp, RST_HWARE, 0); +- } else { +- stv0900_write_bits(intp, RST_HWARE, 0); +- msleep(3); +- stv0900_write_bits(intp, RST_HWARE, 1); +- stv0900_write_bits(intp, RST_HWARE, 0); +- } +- +- } else if (intp->chip_id >= 0x20) { +- stv0900_write_bits(intp, RST_HWARE, 0); +- msleep(3); +- stv0900_write_bits(intp, RST_HWARE, 1); +- stv0900_write_bits(intp, RST_HWARE, 0); +- } +- +- if (stv0900_wait_for_lock(intp, demod, +- fec_timeout, fec_timeout) == TRUE) { +- lock = TRUE; +- intp->result[demod].locked = TRUE; +- if (intp->result[demod].standard == +- STV0900_DVBS2_STANDARD) { +- stv0900_set_dvbs2_rolloff(intp, demod); +- stv0900_write_bits(intp, RESET_UPKO_COUNT, 1); +- stv0900_write_bits(intp, RESET_UPKO_COUNT, 0); +- stv0900_write_reg(intp, ERRCTRL1, 0x67); +- } else { +- stv0900_write_reg(intp, ERRCTRL1, 0x75); +- } +- +- stv0900_write_reg(intp, FBERCPT4, 0); +- stv0900_write_reg(intp, ERRCTRL2, 0xc1); +- } else { +- lock = FALSE; +- signal_type = STV0900_NODATA; +- no_signal = stv0900_check_signal_presence(intp, demod); +- +- intp->result[demod].locked = FALSE; +- } +- } +- +- if ((signal_type != STV0900_NODATA) || (no_signal != FALSE)) +- return signal_type; +- +- if (intp->chip_id > 0x11) { +- intp->result[demod].locked = FALSE; +- return signal_type; +- } +- +- if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) && +- (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST)) +- signal_type = stv0900_dvbs1_acq_workaround(fe); +- +- return signal_type; +-} +- +diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c +deleted file mode 100644 +index 4aef187..0000000 +--- a/drivers/media/dvb/frontends/stv090x.c ++++ /dev/null +@@ -1,4825 +0,0 @@ +-/* +- STV0900/0903 Multistandard Broadcast Frontend driver +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include "dvb_frontend.h" +- +-#include "stv6110x.h" /* for demodulator internal modes */ +- +-#include "stv090x_reg.h" +-#include "stv090x.h" +-#include "stv090x_priv.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +- +-/* internal params node */ +-struct stv090x_dev { +- /* pointer for internal params, one for each pair of demods */ +- struct stv090x_internal *internal; +- struct stv090x_dev *next_dev; +-}; +- +-/* first internal params */ +-static struct stv090x_dev *stv090x_first_dev; +- +-/* find chip by i2c adapter and i2c address */ +-static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap, +- u8 i2c_addr) +-{ +- struct stv090x_dev *temp_dev = stv090x_first_dev; +- +- /* +- Search of the last stv0900 chip or +- find it by i2c adapter and i2c address */ +- while ((temp_dev != NULL) && +- ((temp_dev->internal->i2c_adap != i2c_adap) || +- (temp_dev->internal->i2c_addr != i2c_addr))) { +- +- temp_dev = temp_dev->next_dev; +- } +- +- return temp_dev; +-} +- +-/* deallocating chip */ +-static void remove_dev(struct stv090x_internal *internal) +-{ +- struct stv090x_dev *prev_dev = stv090x_first_dev; +- struct stv090x_dev *del_dev = find_dev(internal->i2c_adap, +- internal->i2c_addr); +- +- if (del_dev != NULL) { +- if (del_dev == stv090x_first_dev) { +- stv090x_first_dev = del_dev->next_dev; +- } else { +- while (prev_dev->next_dev != del_dev) +- prev_dev = prev_dev->next_dev; +- +- prev_dev->next_dev = del_dev->next_dev; +- } +- +- kfree(del_dev); +- } +-} +- +-/* allocating new chip */ +-static struct stv090x_dev *append_internal(struct stv090x_internal *internal) +-{ +- struct stv090x_dev *new_dev; +- struct stv090x_dev *temp_dev; +- +- new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL); +- if (new_dev != NULL) { +- new_dev->internal = internal; +- new_dev->next_dev = NULL; +- +- /* append to list */ +- if (stv090x_first_dev == NULL) { +- stv090x_first_dev = new_dev; +- } else { +- temp_dev = stv090x_first_dev; +- while (temp_dev->next_dev != NULL) +- temp_dev = temp_dev->next_dev; +- +- temp_dev->next_dev = new_dev; +- } +- } +- +- return new_dev; +-} +- +- +-/* DVBS1 and DSS C/N Lookup table */ +-static const struct stv090x_tab stv090x_s1cn_tab[] = { +- { 0, 8917 }, /* 0.0dB */ +- { 5, 8801 }, /* 0.5dB */ +- { 10, 8667 }, /* 1.0dB */ +- { 15, 8522 }, /* 1.5dB */ +- { 20, 8355 }, /* 2.0dB */ +- { 25, 8175 }, /* 2.5dB */ +- { 30, 7979 }, /* 3.0dB */ +- { 35, 7763 }, /* 3.5dB */ +- { 40, 7530 }, /* 4.0dB */ +- { 45, 7282 }, /* 4.5dB */ +- { 50, 7026 }, /* 5.0dB */ +- { 55, 6781 }, /* 5.5dB */ +- { 60, 6514 }, /* 6.0dB */ +- { 65, 6241 }, /* 6.5dB */ +- { 70, 5965 }, /* 7.0dB */ +- { 75, 5690 }, /* 7.5dB */ +- { 80, 5424 }, /* 8.0dB */ +- { 85, 5161 }, /* 8.5dB */ +- { 90, 4902 }, /* 9.0dB */ +- { 95, 4654 }, /* 9.5dB */ +- { 100, 4417 }, /* 10.0dB */ +- { 105, 4186 }, /* 10.5dB */ +- { 110, 3968 }, /* 11.0dB */ +- { 115, 3757 }, /* 11.5dB */ +- { 120, 3558 }, /* 12.0dB */ +- { 125, 3366 }, /* 12.5dB */ +- { 130, 3185 }, /* 13.0dB */ +- { 135, 3012 }, /* 13.5dB */ +- { 140, 2850 }, /* 14.0dB */ +- { 145, 2698 }, /* 14.5dB */ +- { 150, 2550 }, /* 15.0dB */ +- { 160, 2283 }, /* 16.0dB */ +- { 170, 2042 }, /* 17.0dB */ +- { 180, 1827 }, /* 18.0dB */ +- { 190, 1636 }, /* 19.0dB */ +- { 200, 1466 }, /* 20.0dB */ +- { 210, 1315 }, /* 21.0dB */ +- { 220, 1181 }, /* 22.0dB */ +- { 230, 1064 }, /* 23.0dB */ +- { 240, 960 }, /* 24.0dB */ +- { 250, 869 }, /* 25.0dB */ +- { 260, 792 }, /* 26.0dB */ +- { 270, 724 }, /* 27.0dB */ +- { 280, 665 }, /* 28.0dB */ +- { 290, 616 }, /* 29.0dB */ +- { 300, 573 }, /* 30.0dB */ +- { 310, 537 }, /* 31.0dB */ +- { 320, 507 }, /* 32.0dB */ +- { 330, 483 }, /* 33.0dB */ +- { 400, 398 }, /* 40.0dB */ +- { 450, 381 }, /* 45.0dB */ +- { 500, 377 } /* 50.0dB */ +-}; +- +-/* DVBS2 C/N Lookup table */ +-static const struct stv090x_tab stv090x_s2cn_tab[] = { +- { -30, 13348 }, /* -3.0dB */ +- { -20, 12640 }, /* -2d.0B */ +- { -10, 11883 }, /* -1.0dB */ +- { 0, 11101 }, /* -0.0dB */ +- { 5, 10718 }, /* 0.5dB */ +- { 10, 10339 }, /* 1.0dB */ +- { 15, 9947 }, /* 1.5dB */ +- { 20, 9552 }, /* 2.0dB */ +- { 25, 9183 }, /* 2.5dB */ +- { 30, 8799 }, /* 3.0dB */ +- { 35, 8422 }, /* 3.5dB */ +- { 40, 8062 }, /* 4.0dB */ +- { 45, 7707 }, /* 4.5dB */ +- { 50, 7353 }, /* 5.0dB */ +- { 55, 7025 }, /* 5.5dB */ +- { 60, 6684 }, /* 6.0dB */ +- { 65, 6331 }, /* 6.5dB */ +- { 70, 6036 }, /* 7.0dB */ +- { 75, 5727 }, /* 7.5dB */ +- { 80, 5437 }, /* 8.0dB */ +- { 85, 5164 }, /* 8.5dB */ +- { 90, 4902 }, /* 9.0dB */ +- { 95, 4653 }, /* 9.5dB */ +- { 100, 4408 }, /* 10.0dB */ +- { 105, 4187 }, /* 10.5dB */ +- { 110, 3961 }, /* 11.0dB */ +- { 115, 3751 }, /* 11.5dB */ +- { 120, 3558 }, /* 12.0dB */ +- { 125, 3368 }, /* 12.5dB */ +- { 130, 3191 }, /* 13.0dB */ +- { 135, 3017 }, /* 13.5dB */ +- { 140, 2862 }, /* 14.0dB */ +- { 145, 2710 }, /* 14.5dB */ +- { 150, 2565 }, /* 15.0dB */ +- { 160, 2300 }, /* 16.0dB */ +- { 170, 2058 }, /* 17.0dB */ +- { 180, 1849 }, /* 18.0dB */ +- { 190, 1663 }, /* 19.0dB */ +- { 200, 1495 }, /* 20.0dB */ +- { 210, 1349 }, /* 21.0dB */ +- { 220, 1222 }, /* 22.0dB */ +- { 230, 1110 }, /* 23.0dB */ +- { 240, 1011 }, /* 24.0dB */ +- { 250, 925 }, /* 25.0dB */ +- { 260, 853 }, /* 26.0dB */ +- { 270, 789 }, /* 27.0dB */ +- { 280, 734 }, /* 28.0dB */ +- { 290, 690 }, /* 29.0dB */ +- { 300, 650 }, /* 30.0dB */ +- { 310, 619 }, /* 31.0dB */ +- { 320, 593 }, /* 32.0dB */ +- { 330, 571 }, /* 33.0dB */ +- { 400, 498 }, /* 40.0dB */ +- { 450, 484 }, /* 45.0dB */ +- { 500, 481 } /* 50.0dB */ +-}; +- +-/* RF level C/N lookup table */ +-static const struct stv090x_tab stv090x_rf_tab[] = { +- { -5, 0xcaa1 }, /* -5dBm */ +- { -10, 0xc229 }, /* -10dBm */ +- { -15, 0xbb08 }, /* -15dBm */ +- { -20, 0xb4bc }, /* -20dBm */ +- { -25, 0xad5a }, /* -25dBm */ +- { -30, 0xa298 }, /* -30dBm */ +- { -35, 0x98a8 }, /* -35dBm */ +- { -40, 0x8389 }, /* -40dBm */ +- { -45, 0x59be }, /* -45dBm */ +- { -50, 0x3a14 }, /* -50dBm */ +- { -55, 0x2d11 }, /* -55dBm */ +- { -60, 0x210d }, /* -60dBm */ +- { -65, 0xa14f }, /* -65dBm */ +- { -70, 0x07aa } /* -70dBm */ +-}; +- +- +-static struct stv090x_reg stv0900_initval[] = { +- +- { STV090x_OUTCFG, 0x00 }, +- { STV090x_MODECFG, 0xff }, +- { STV090x_AGCRF1CFG, 0x11 }, +- { STV090x_AGCRF2CFG, 0x13 }, +- { STV090x_TSGENERAL1X, 0x14 }, +- { STV090x_TSTTNR2, 0x21 }, +- { STV090x_TSTTNR4, 0x21 }, +- { STV090x_P2_DISTXCTL, 0x22 }, +- { STV090x_P2_F22TX, 0xc0 }, +- { STV090x_P2_F22RX, 0xc0 }, +- { STV090x_P2_DISRXCTL, 0x00 }, +- { STV090x_P2_DMDCFGMD, 0xF9 }, +- { STV090x_P2_DEMOD, 0x08 }, +- { STV090x_P2_DMDCFG3, 0xc4 }, +- { STV090x_P2_CARFREQ, 0xed }, +- { STV090x_P2_LDT, 0xd0 }, +- { STV090x_P2_LDT2, 0xb8 }, +- { STV090x_P2_TMGCFG, 0xd2 }, +- { STV090x_P2_TMGTHRISE, 0x20 }, +- { STV090x_P1_TMGCFG, 0xd2 }, +- +- { STV090x_P2_TMGTHFALL, 0x00 }, +- { STV090x_P2_FECSPY, 0x88 }, +- { STV090x_P2_FSPYDATA, 0x3a }, +- { STV090x_P2_FBERCPT4, 0x00 }, +- { STV090x_P2_FSPYBER, 0x10 }, +- { STV090x_P2_ERRCTRL1, 0x35 }, +- { STV090x_P2_ERRCTRL2, 0xc1 }, +- { STV090x_P2_CFRICFG, 0xf8 }, +- { STV090x_P2_NOSCFG, 0x1c }, +- { STV090x_P2_DMDTOM, 0x20 }, +- { STV090x_P2_CORRELMANT, 0x70 }, +- { STV090x_P2_CORRELABS, 0x88 }, +- { STV090x_P2_AGC2O, 0x5b }, +- { STV090x_P2_AGC2REF, 0x38 }, +- { STV090x_P2_CARCFG, 0xe4 }, +- { STV090x_P2_ACLC, 0x1A }, +- { STV090x_P2_BCLC, 0x09 }, +- { STV090x_P2_CARHDR, 0x08 }, +- { STV090x_P2_KREFTMG, 0xc1 }, +- { STV090x_P2_SFRUPRATIO, 0xf0 }, +- { STV090x_P2_SFRLOWRATIO, 0x70 }, +- { STV090x_P2_SFRSTEP, 0x58 }, +- { STV090x_P2_TMGCFG2, 0x01 }, +- { STV090x_P2_CAR2CFG, 0x26 }, +- { STV090x_P2_BCLC2S2Q, 0x86 }, +- { STV090x_P2_BCLC2S28, 0x86 }, +- { STV090x_P2_SMAPCOEF7, 0x77 }, +- { STV090x_P2_SMAPCOEF6, 0x85 }, +- { STV090x_P2_SMAPCOEF5, 0x77 }, +- { STV090x_P2_TSCFGL, 0x20 }, +- { STV090x_P2_DMDCFG2, 0x3b }, +- { STV090x_P2_MODCODLST0, 0xff }, +- { STV090x_P2_MODCODLST1, 0xff }, +- { STV090x_P2_MODCODLST2, 0xff }, +- { STV090x_P2_MODCODLST3, 0xff }, +- { STV090x_P2_MODCODLST4, 0xff }, +- { STV090x_P2_MODCODLST5, 0xff }, +- { STV090x_P2_MODCODLST6, 0xff }, +- { STV090x_P2_MODCODLST7, 0xcc }, +- { STV090x_P2_MODCODLST8, 0xcc }, +- { STV090x_P2_MODCODLST9, 0xcc }, +- { STV090x_P2_MODCODLSTA, 0xcc }, +- { STV090x_P2_MODCODLSTB, 0xcc }, +- { STV090x_P2_MODCODLSTC, 0xcc }, +- { STV090x_P2_MODCODLSTD, 0xcc }, +- { STV090x_P2_MODCODLSTE, 0xcc }, +- { STV090x_P2_MODCODLSTF, 0xcf }, +- { STV090x_P1_DISTXCTL, 0x22 }, +- { STV090x_P1_F22TX, 0xc0 }, +- { STV090x_P1_F22RX, 0xc0 }, +- { STV090x_P1_DISRXCTL, 0x00 }, +- { STV090x_P1_DMDCFGMD, 0xf9 }, +- { STV090x_P1_DEMOD, 0x08 }, +- { STV090x_P1_DMDCFG3, 0xc4 }, +- { STV090x_P1_DMDTOM, 0x20 }, +- { STV090x_P1_CARFREQ, 0xed }, +- { STV090x_P1_LDT, 0xd0 }, +- { STV090x_P1_LDT2, 0xb8 }, +- { STV090x_P1_TMGCFG, 0xd2 }, +- { STV090x_P1_TMGTHRISE, 0x20 }, +- { STV090x_P1_TMGTHFALL, 0x00 }, +- { STV090x_P1_SFRUPRATIO, 0xf0 }, +- { STV090x_P1_SFRLOWRATIO, 0x70 }, +- { STV090x_P1_TSCFGL, 0x20 }, +- { STV090x_P1_FECSPY, 0x88 }, +- { STV090x_P1_FSPYDATA, 0x3a }, +- { STV090x_P1_FBERCPT4, 0x00 }, +- { STV090x_P1_FSPYBER, 0x10 }, +- { STV090x_P1_ERRCTRL1, 0x35 }, +- { STV090x_P1_ERRCTRL2, 0xc1 }, +- { STV090x_P1_CFRICFG, 0xf8 }, +- { STV090x_P1_NOSCFG, 0x1c }, +- { STV090x_P1_CORRELMANT, 0x70 }, +- { STV090x_P1_CORRELABS, 0x88 }, +- { STV090x_P1_AGC2O, 0x5b }, +- { STV090x_P1_AGC2REF, 0x38 }, +- { STV090x_P1_CARCFG, 0xe4 }, +- { STV090x_P1_ACLC, 0x1A }, +- { STV090x_P1_BCLC, 0x09 }, +- { STV090x_P1_CARHDR, 0x08 }, +- { STV090x_P1_KREFTMG, 0xc1 }, +- { STV090x_P1_SFRSTEP, 0x58 }, +- { STV090x_P1_TMGCFG2, 0x01 }, +- { STV090x_P1_CAR2CFG, 0x26 }, +- { STV090x_P1_BCLC2S2Q, 0x86 }, +- { STV090x_P1_BCLC2S28, 0x86 }, +- { STV090x_P1_SMAPCOEF7, 0x77 }, +- { STV090x_P1_SMAPCOEF6, 0x85 }, +- { STV090x_P1_SMAPCOEF5, 0x77 }, +- { STV090x_P1_DMDCFG2, 0x3b }, +- { STV090x_P1_MODCODLST0, 0xff }, +- { STV090x_P1_MODCODLST1, 0xff }, +- { STV090x_P1_MODCODLST2, 0xff }, +- { STV090x_P1_MODCODLST3, 0xff }, +- { STV090x_P1_MODCODLST4, 0xff }, +- { STV090x_P1_MODCODLST5, 0xff }, +- { STV090x_P1_MODCODLST6, 0xff }, +- { STV090x_P1_MODCODLST7, 0xcc }, +- { STV090x_P1_MODCODLST8, 0xcc }, +- { STV090x_P1_MODCODLST9, 0xcc }, +- { STV090x_P1_MODCODLSTA, 0xcc }, +- { STV090x_P1_MODCODLSTB, 0xcc }, +- { STV090x_P1_MODCODLSTC, 0xcc }, +- { STV090x_P1_MODCODLSTD, 0xcc }, +- { STV090x_P1_MODCODLSTE, 0xcc }, +- { STV090x_P1_MODCODLSTF, 0xcf }, +- { STV090x_GENCFG, 0x1d }, +- { STV090x_NBITER_NF4, 0x37 }, +- { STV090x_NBITER_NF5, 0x29 }, +- { STV090x_NBITER_NF6, 0x37 }, +- { STV090x_NBITER_NF7, 0x33 }, +- { STV090x_NBITER_NF8, 0x31 }, +- { STV090x_NBITER_NF9, 0x2f }, +- { STV090x_NBITER_NF10, 0x39 }, +- { STV090x_NBITER_NF11, 0x3a }, +- { STV090x_NBITER_NF12, 0x29 }, +- { STV090x_NBITER_NF13, 0x37 }, +- { STV090x_NBITER_NF14, 0x33 }, +- { STV090x_NBITER_NF15, 0x2f }, +- { STV090x_NBITER_NF16, 0x39 }, +- { STV090x_NBITER_NF17, 0x3a }, +- { STV090x_NBITERNOERR, 0x04 }, +- { STV090x_GAINLLR_NF4, 0x0C }, +- { STV090x_GAINLLR_NF5, 0x0F }, +- { STV090x_GAINLLR_NF6, 0x11 }, +- { STV090x_GAINLLR_NF7, 0x14 }, +- { STV090x_GAINLLR_NF8, 0x17 }, +- { STV090x_GAINLLR_NF9, 0x19 }, +- { STV090x_GAINLLR_NF10, 0x20 }, +- { STV090x_GAINLLR_NF11, 0x21 }, +- { STV090x_GAINLLR_NF12, 0x0D }, +- { STV090x_GAINLLR_NF13, 0x0F }, +- { STV090x_GAINLLR_NF14, 0x13 }, +- { STV090x_GAINLLR_NF15, 0x1A }, +- { STV090x_GAINLLR_NF16, 0x1F }, +- { STV090x_GAINLLR_NF17, 0x21 }, +- { STV090x_RCCFGH, 0x20 }, +- { STV090x_P1_FECM, 0x01 }, /* disable DSS modes */ +- { STV090x_P2_FECM, 0x01 }, /* disable DSS modes */ +- { STV090x_P1_PRVIT, 0x2F }, /* disable PR 6/7 */ +- { STV090x_P2_PRVIT, 0x2F }, /* disable PR 6/7 */ +-}; +- +-static struct stv090x_reg stv0903_initval[] = { +- { STV090x_OUTCFG, 0x00 }, +- { STV090x_AGCRF1CFG, 0x11 }, +- { STV090x_STOPCLK1, 0x48 }, +- { STV090x_STOPCLK2, 0x14 }, +- { STV090x_TSTTNR1, 0x27 }, +- { STV090x_TSTTNR2, 0x21 }, +- { STV090x_P1_DISTXCTL, 0x22 }, +- { STV090x_P1_F22TX, 0xc0 }, +- { STV090x_P1_F22RX, 0xc0 }, +- { STV090x_P1_DISRXCTL, 0x00 }, +- { STV090x_P1_DMDCFGMD, 0xF9 }, +- { STV090x_P1_DEMOD, 0x08 }, +- { STV090x_P1_DMDCFG3, 0xc4 }, +- { STV090x_P1_CARFREQ, 0xed }, +- { STV090x_P1_TNRCFG2, 0x82 }, +- { STV090x_P1_LDT, 0xd0 }, +- { STV090x_P1_LDT2, 0xb8 }, +- { STV090x_P1_TMGCFG, 0xd2 }, +- { STV090x_P1_TMGTHRISE, 0x20 }, +- { STV090x_P1_TMGTHFALL, 0x00 }, +- { STV090x_P1_SFRUPRATIO, 0xf0 }, +- { STV090x_P1_SFRLOWRATIO, 0x70 }, +- { STV090x_P1_TSCFGL, 0x20 }, +- { STV090x_P1_FECSPY, 0x88 }, +- { STV090x_P1_FSPYDATA, 0x3a }, +- { STV090x_P1_FBERCPT4, 0x00 }, +- { STV090x_P1_FSPYBER, 0x10 }, +- { STV090x_P1_ERRCTRL1, 0x35 }, +- { STV090x_P1_ERRCTRL2, 0xc1 }, +- { STV090x_P1_CFRICFG, 0xf8 }, +- { STV090x_P1_NOSCFG, 0x1c }, +- { STV090x_P1_DMDTOM, 0x20 }, +- { STV090x_P1_CORRELMANT, 0x70 }, +- { STV090x_P1_CORRELABS, 0x88 }, +- { STV090x_P1_AGC2O, 0x5b }, +- { STV090x_P1_AGC2REF, 0x38 }, +- { STV090x_P1_CARCFG, 0xe4 }, +- { STV090x_P1_ACLC, 0x1A }, +- { STV090x_P1_BCLC, 0x09 }, +- { STV090x_P1_CARHDR, 0x08 }, +- { STV090x_P1_KREFTMG, 0xc1 }, +- { STV090x_P1_SFRSTEP, 0x58 }, +- { STV090x_P1_TMGCFG2, 0x01 }, +- { STV090x_P1_CAR2CFG, 0x26 }, +- { STV090x_P1_BCLC2S2Q, 0x86 }, +- { STV090x_P1_BCLC2S28, 0x86 }, +- { STV090x_P1_SMAPCOEF7, 0x77 }, +- { STV090x_P1_SMAPCOEF6, 0x85 }, +- { STV090x_P1_SMAPCOEF5, 0x77 }, +- { STV090x_P1_DMDCFG2, 0x3b }, +- { STV090x_P1_MODCODLST0, 0xff }, +- { STV090x_P1_MODCODLST1, 0xff }, +- { STV090x_P1_MODCODLST2, 0xff }, +- { STV090x_P1_MODCODLST3, 0xff }, +- { STV090x_P1_MODCODLST4, 0xff }, +- { STV090x_P1_MODCODLST5, 0xff }, +- { STV090x_P1_MODCODLST6, 0xff }, +- { STV090x_P1_MODCODLST7, 0xcc }, +- { STV090x_P1_MODCODLST8, 0xcc }, +- { STV090x_P1_MODCODLST9, 0xcc }, +- { STV090x_P1_MODCODLSTA, 0xcc }, +- { STV090x_P1_MODCODLSTB, 0xcc }, +- { STV090x_P1_MODCODLSTC, 0xcc }, +- { STV090x_P1_MODCODLSTD, 0xcc }, +- { STV090x_P1_MODCODLSTE, 0xcc }, +- { STV090x_P1_MODCODLSTF, 0xcf }, +- { STV090x_GENCFG, 0x1c }, +- { STV090x_NBITER_NF4, 0x37 }, +- { STV090x_NBITER_NF5, 0x29 }, +- { STV090x_NBITER_NF6, 0x37 }, +- { STV090x_NBITER_NF7, 0x33 }, +- { STV090x_NBITER_NF8, 0x31 }, +- { STV090x_NBITER_NF9, 0x2f }, +- { STV090x_NBITER_NF10, 0x39 }, +- { STV090x_NBITER_NF11, 0x3a }, +- { STV090x_NBITER_NF12, 0x29 }, +- { STV090x_NBITER_NF13, 0x37 }, +- { STV090x_NBITER_NF14, 0x33 }, +- { STV090x_NBITER_NF15, 0x2f }, +- { STV090x_NBITER_NF16, 0x39 }, +- { STV090x_NBITER_NF17, 0x3a }, +- { STV090x_NBITERNOERR, 0x04 }, +- { STV090x_GAINLLR_NF4, 0x0C }, +- { STV090x_GAINLLR_NF5, 0x0F }, +- { STV090x_GAINLLR_NF6, 0x11 }, +- { STV090x_GAINLLR_NF7, 0x14 }, +- { STV090x_GAINLLR_NF8, 0x17 }, +- { STV090x_GAINLLR_NF9, 0x19 }, +- { STV090x_GAINLLR_NF10, 0x20 }, +- { STV090x_GAINLLR_NF11, 0x21 }, +- { STV090x_GAINLLR_NF12, 0x0D }, +- { STV090x_GAINLLR_NF13, 0x0F }, +- { STV090x_GAINLLR_NF14, 0x13 }, +- { STV090x_GAINLLR_NF15, 0x1A }, +- { STV090x_GAINLLR_NF16, 0x1F }, +- { STV090x_GAINLLR_NF17, 0x21 }, +- { STV090x_RCCFGH, 0x20 }, +- { STV090x_P1_FECM, 0x01 }, /*disable the DSS mode */ +- { STV090x_P1_PRVIT, 0x2f } /*disable puncture rate 6/7*/ +-}; +- +-static struct stv090x_reg stv0900_cut20_val[] = { +- +- { STV090x_P2_DMDCFG3, 0xe8 }, +- { STV090x_P2_DMDCFG4, 0x10 }, +- { STV090x_P2_CARFREQ, 0x38 }, +- { STV090x_P2_CARHDR, 0x20 }, +- { STV090x_P2_KREFTMG, 0x5a }, +- { STV090x_P2_SMAPCOEF7, 0x06 }, +- { STV090x_P2_SMAPCOEF6, 0x00 }, +- { STV090x_P2_SMAPCOEF5, 0x04 }, +- { STV090x_P2_NOSCFG, 0x0c }, +- { STV090x_P1_DMDCFG3, 0xe8 }, +- { STV090x_P1_DMDCFG4, 0x10 }, +- { STV090x_P1_CARFREQ, 0x38 }, +- { STV090x_P1_CARHDR, 0x20 }, +- { STV090x_P1_KREFTMG, 0x5a }, +- { STV090x_P1_SMAPCOEF7, 0x06 }, +- { STV090x_P1_SMAPCOEF6, 0x00 }, +- { STV090x_P1_SMAPCOEF5, 0x04 }, +- { STV090x_P1_NOSCFG, 0x0c }, +- { STV090x_GAINLLR_NF4, 0x21 }, +- { STV090x_GAINLLR_NF5, 0x21 }, +- { STV090x_GAINLLR_NF6, 0x20 }, +- { STV090x_GAINLLR_NF7, 0x1F }, +- { STV090x_GAINLLR_NF8, 0x1E }, +- { STV090x_GAINLLR_NF9, 0x1E }, +- { STV090x_GAINLLR_NF10, 0x1D }, +- { STV090x_GAINLLR_NF11, 0x1B }, +- { STV090x_GAINLLR_NF12, 0x20 }, +- { STV090x_GAINLLR_NF13, 0x20 }, +- { STV090x_GAINLLR_NF14, 0x20 }, +- { STV090x_GAINLLR_NF15, 0x20 }, +- { STV090x_GAINLLR_NF16, 0x20 }, +- { STV090x_GAINLLR_NF17, 0x21 }, +-}; +- +-static struct stv090x_reg stv0903_cut20_val[] = { +- { STV090x_P1_DMDCFG3, 0xe8 }, +- { STV090x_P1_DMDCFG4, 0x10 }, +- { STV090x_P1_CARFREQ, 0x38 }, +- { STV090x_P1_CARHDR, 0x20 }, +- { STV090x_P1_KREFTMG, 0x5a }, +- { STV090x_P1_SMAPCOEF7, 0x06 }, +- { STV090x_P1_SMAPCOEF6, 0x00 }, +- { STV090x_P1_SMAPCOEF5, 0x04 }, +- { STV090x_P1_NOSCFG, 0x0c }, +- { STV090x_GAINLLR_NF4, 0x21 }, +- { STV090x_GAINLLR_NF5, 0x21 }, +- { STV090x_GAINLLR_NF6, 0x20 }, +- { STV090x_GAINLLR_NF7, 0x1F }, +- { STV090x_GAINLLR_NF8, 0x1E }, +- { STV090x_GAINLLR_NF9, 0x1E }, +- { STV090x_GAINLLR_NF10, 0x1D }, +- { STV090x_GAINLLR_NF11, 0x1B }, +- { STV090x_GAINLLR_NF12, 0x20 }, +- { STV090x_GAINLLR_NF13, 0x20 }, +- { STV090x_GAINLLR_NF14, 0x20 }, +- { STV090x_GAINLLR_NF15, 0x20 }, +- { STV090x_GAINLLR_NF16, 0x20 }, +- { STV090x_GAINLLR_NF17, 0x21 } +-}; +- +-/* Cut 2.0 Long Frame Tracking CR loop */ +-static struct stv090x_long_frame_crloop stv090x_s2_crl_cut20[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_QPSK_12, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x1e }, +- { STV090x_QPSK_35, 0x2f, 0x3f, 0x2e, 0x2f, 0x3d, 0x0f, 0x0e, 0x2e, 0x3d, 0x0e }, +- { STV090x_QPSK_23, 0x2f, 0x3f, 0x2e, 0x2f, 0x0e, 0x0f, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_QPSK_34, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_QPSK_45, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_QPSK_56, 0x3f, 0x3f, 0x3e, 0x1f, 0x0e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_QPSK_89, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_QPSK_910, 0x3f, 0x3f, 0x3e, 0x1f, 0x1e, 0x3e, 0x0e, 0x1e, 0x3d, 0x3d }, +- { STV090x_8PSK_35, 0x3c, 0x3e, 0x1c, 0x2e, 0x0c, 0x1e, 0x2b, 0x2d, 0x1b, 0x1d }, +- { STV090x_8PSK_23, 0x1d, 0x3e, 0x3c, 0x2e, 0x2c, 0x1e, 0x0c, 0x2d, 0x2b, 0x1d }, +- { STV090x_8PSK_34, 0x0e, 0x3e, 0x3d, 0x2e, 0x0d, 0x1e, 0x2c, 0x2d, 0x0c, 0x1d }, +- { STV090x_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d }, +- { STV090x_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d }, +- { STV090x_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d } +-}; +- +-/* Cut 3.0 Long Frame Tracking CR loop */ +-static struct stv090x_long_frame_crloop stv090x_s2_crl_cut30[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_QPSK_12, 0x3c, 0x2c, 0x0c, 0x2c, 0x1b, 0x2c, 0x1b, 0x1c, 0x0b, 0x3b }, +- { STV090x_QPSK_35, 0x0d, 0x0d, 0x0c, 0x0d, 0x1b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, +- { STV090x_QPSK_23, 0x1d, 0x0d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, +- { STV090x_QPSK_34, 0x1d, 0x1d, 0x0c, 0x1d, 0x2b, 0x3c, 0x1b, 0x1c, 0x0b, 0x3b }, +- { STV090x_QPSK_45, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, +- { STV090x_QPSK_56, 0x2d, 0x1d, 0x1c, 0x1d, 0x2b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, +- { STV090x_QPSK_89, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, +- { STV090x_QPSK_910, 0x3d, 0x2d, 0x1c, 0x1d, 0x3b, 0x3c, 0x2b, 0x0c, 0x1b, 0x3b }, +- { STV090x_8PSK_35, 0x39, 0x29, 0x39, 0x19, 0x19, 0x19, 0x19, 0x19, 0x09, 0x19 }, +- { STV090x_8PSK_23, 0x2a, 0x39, 0x1a, 0x0a, 0x39, 0x0a, 0x29, 0x39, 0x29, 0x0a }, +- { STV090x_8PSK_34, 0x2b, 0x3a, 0x1b, 0x1b, 0x3a, 0x1b, 0x1a, 0x0b, 0x1a, 0x3a }, +- { STV090x_8PSK_56, 0x0c, 0x1b, 0x3b, 0x3b, 0x1b, 0x3b, 0x3a, 0x3b, 0x3a, 0x1b }, +- { STV090x_8PSK_89, 0x0d, 0x3c, 0x2c, 0x2c, 0x2b, 0x0c, 0x0b, 0x3b, 0x0b, 0x1b }, +- { STV090x_8PSK_910, 0x0d, 0x0d, 0x2c, 0x3c, 0x3b, 0x1c, 0x0b, 0x3b, 0x0b, 0x1b } +-}; +- +-/* Cut 2.0 Long Frame Tracking CR Loop */ +-static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut20[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_16APSK_23, 0x0c, 0x0c, 0x0c, 0x0c, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x0c }, +- { STV090x_16APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0c, 0x2d, 0x0c, 0x1d, 0x0c }, +- { STV090x_16APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, +- { STV090x_16APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x1e, 0x0c, 0x3d, 0x0c, 0x2d, 0x0c }, +- { STV090x_16APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, +- { STV090x_16APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x2e, 0x0c, 0x0e, 0x0c, 0x3d, 0x0c }, +- { STV090x_32APSK_34, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, +- { STV090x_32APSK_45, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, +- { STV090x_32APSK_56, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, +- { STV090x_32APSK_89, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c }, +- { STV090x_32APSK_910, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c } +-}; +- +-/* Cut 3.0 Long Frame Tracking CR Loop */ +-static struct stv090x_long_frame_crloop stv090x_s2_apsk_crl_cut30[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_16APSK_23, 0x0a, 0x0a, 0x0a, 0x0a, 0x1a, 0x0a, 0x3a, 0x0a, 0x2a, 0x0a }, +- { STV090x_16APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0a, 0x3b, 0x0a, 0x1b, 0x0a }, +- { STV090x_16APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, +- { STV090x_16APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x1b, 0x0a, 0x3b, 0x0a, 0x2b, 0x0a }, +- { STV090x_16APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, +- { STV090x_16APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x2b, 0x0a, 0x0c, 0x0a, 0x3b, 0x0a }, +- { STV090x_32APSK_34, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, +- { STV090x_32APSK_45, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, +- { STV090x_32APSK_56, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, +- { STV090x_32APSK_89, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a }, +- { STV090x_32APSK_910, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a } +-}; +- +-static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut20[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_QPSK_14, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x2d, 0x1f, 0x3d, 0x3e }, +- { STV090x_QPSK_13, 0x0f, 0x3f, 0x0e, 0x3f, 0x2d, 0x2f, 0x3d, 0x0f, 0x3d, 0x2e }, +- { STV090x_QPSK_25, 0x1f, 0x3f, 0x1e, 0x3f, 0x3d, 0x1f, 0x3d, 0x3e, 0x3d, 0x2e } +-}; +- +-static struct stv090x_long_frame_crloop stv090x_s2_lowqpsk_crl_cut30[] = { +- /* MODCOD 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */ +- { STV090x_QPSK_14, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x2a, 0x1c, 0x3a, 0x3b }, +- { STV090x_QPSK_13, 0x0c, 0x3c, 0x0b, 0x3c, 0x2a, 0x2c, 0x3a, 0x0c, 0x3a, 0x2b }, +- { STV090x_QPSK_25, 0x1c, 0x3c, 0x1b, 0x3c, 0x3a, 0x1c, 0x3a, 0x3b, 0x3a, 0x2b } +-}; +- +-/* Cut 2.0 Short Frame Tracking CR Loop */ +-static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut20[] = { +- /* MODCOD 2M 5M 10M 20M 30M */ +- { STV090x_QPSK, 0x2f, 0x2e, 0x0e, 0x0e, 0x3d }, +- { STV090x_8PSK, 0x3e, 0x0e, 0x2d, 0x0d, 0x3c }, +- { STV090x_16APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d }, +- { STV090x_32APSK, 0x1e, 0x1e, 0x1e, 0x3d, 0x2d } +-}; +- +-/* Cut 3.0 Short Frame Tracking CR Loop */ +-static struct stv090x_short_frame_crloop stv090x_s2_short_crl_cut30[] = { +- /* MODCOD 2M 5M 10M 20M 30M */ +- { STV090x_QPSK, 0x2C, 0x2B, 0x0B, 0x0B, 0x3A }, +- { STV090x_8PSK, 0x3B, 0x0B, 0x2A, 0x0A, 0x39 }, +- { STV090x_16APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A }, +- { STV090x_32APSK, 0x1B, 0x1B, 0x1B, 0x3A, 0x2A } +-}; +- +-static inline s32 comp2(s32 __x, s32 __width) +-{ +- if (__width == 32) +- return __x; +- else +- return (__x >= (1 << (__width - 1))) ? (__x - (1 << __width)) : __x; +-} +- +-static int stv090x_read_reg(struct stv090x_state *state, unsigned int reg) +-{ +- const struct stv090x_config *config = state->config; +- int ret; +- +- u8 b0[] = { reg >> 8, reg & 0xff }; +- u8 buf; +- +- struct i2c_msg msg[] = { +- { .addr = config->address, .flags = 0, .buf = b0, .len = 2 }, +- { .addr = config->address, .flags = I2C_M_RD, .buf = &buf, .len = 1 } +- }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- if (ret != 2) { +- if (ret != -ERESTARTSYS) +- dprintk(FE_ERROR, 1, +- "Read error, Reg=[0x%02x], Status=%d", +- reg, ret); +- +- return ret < 0 ? ret : -EREMOTEIO; +- } +- if (unlikely(*state->verbose >= FE_DEBUGREG)) +- dprintk(FE_ERROR, 1, "Reg=[0x%02x], data=%02x", +- reg, buf); +- +- return (unsigned int) buf; +-} +- +-static int stv090x_write_regs(struct stv090x_state *state, unsigned int reg, u8 *data, u32 count) +-{ +- const struct stv090x_config *config = state->config; +- int ret; +- u8 buf[2 + count]; +- struct i2c_msg i2c_msg = { .addr = config->address, .flags = 0, .buf = buf, .len = 2 + count }; +- +- buf[0] = reg >> 8; +- buf[1] = reg & 0xff; +- memcpy(&buf[2], data, count); +- +- if (unlikely(*state->verbose >= FE_DEBUGREG)) { +- int i; +- +- printk(KERN_DEBUG "%s [0x%04x]:", __func__, reg); +- for (i = 0; i < count; i++) +- printk(" %02x", data[i]); +- printk("\n"); +- } +- +- ret = i2c_transfer(state->i2c, &i2c_msg, 1); +- if (ret != 1) { +- if (ret != -ERESTARTSYS) +- dprintk(FE_ERROR, 1, "Reg=[0x%04x], Data=[0x%02x ...], Count=%u, Status=%d", +- reg, data[0], count, ret); +- return ret < 0 ? ret : -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int stv090x_write_reg(struct stv090x_state *state, unsigned int reg, u8 data) +-{ +- return stv090x_write_regs(state, reg, &data, 1); +-} +- +-static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) +-{ +- u32 reg; +- +- /* +- * NOTE! A lock is used as a FSM to control the state in which +- * access is serialized between two tuners on the same demod. +- * This has nothing to do with a lock to protect a critical section +- * which may in some other cases be confused with protecting I/O +- * access to the demodulator gate. +- * In case of any error, the lock is unlocked and exit within the +- * relevant operations themselves. +- */ +- if (enable) { +- if (state->config->tuner_i2c_lock) +- state->config->tuner_i2c_lock(&state->frontend, 1); +- else +- mutex_lock(&state->internal->tuner_lock); +- } +- +- reg = STV090x_READ_DEMOD(state, I2CRPT); +- if (enable) { +- dprintk(FE_DEBUG, 1, "Enable Gate"); +- STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0) +- goto err; +- +- } else { +- dprintk(FE_DEBUG, 1, "Disable Gate"); +- STV090x_SETFIELD_Px(reg, I2CT_ON_FIELD, 0); +- if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0) +- goto err; +- } +- +- if (!enable) { +- if (state->config->tuner_i2c_lock) +- state->config->tuner_i2c_lock(&state->frontend, 0); +- else +- mutex_unlock(&state->internal->tuner_lock); +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- if (state->config->tuner_i2c_lock) +- state->config->tuner_i2c_lock(&state->frontend, 0); +- else +- mutex_unlock(&state->internal->tuner_lock); +- return -1; +-} +- +-static void stv090x_get_lock_tmg(struct stv090x_state *state) +-{ +- switch (state->algo) { +- case STV090x_BLIND_SEARCH: +- dprintk(FE_DEBUG, 1, "Blind Search"); +- if (state->srate <= 1500000) { /*10Msps< SR <=15Msps*/ +- state->DemodTimeout = 1500; +- state->FecTimeout = 400; +- } else if (state->srate <= 5000000) { /*10Msps< SR <=15Msps*/ +- state->DemodTimeout = 1000; +- state->FecTimeout = 300; +- } else { /*SR >20Msps*/ +- state->DemodTimeout = 700; +- state->FecTimeout = 100; +- } +- break; +- +- case STV090x_COLD_SEARCH: +- case STV090x_WARM_SEARCH: +- default: +- dprintk(FE_DEBUG, 1, "Normal Search"); +- if (state->srate <= 1000000) { /*SR <=1Msps*/ +- state->DemodTimeout = 4500; +- state->FecTimeout = 1700; +- } else if (state->srate <= 2000000) { /*1Msps < SR <= 2Msps */ +- state->DemodTimeout = 2500; +- state->FecTimeout = 1100; +- } else if (state->srate <= 5000000) { /*2Msps < SR <= 5Msps */ +- state->DemodTimeout = 1000; +- state->FecTimeout = 550; +- } else if (state->srate <= 10000000) { /*5Msps < SR <= 10Msps */ +- state->DemodTimeout = 700; +- state->FecTimeout = 250; +- } else if (state->srate <= 20000000) { /*10Msps < SR <= 20Msps */ +- state->DemodTimeout = 400; +- state->FecTimeout = 130; +- } else { /*SR >20Msps*/ +- state->DemodTimeout = 300; +- state->FecTimeout = 100; +- } +- break; +- } +- +- if (state->algo == STV090x_WARM_SEARCH) +- state->DemodTimeout /= 2; +-} +- +-static int stv090x_set_srate(struct stv090x_state *state, u32 srate) +-{ +- u32 sym; +- +- if (srate > 60000000) { +- sym = (srate << 4); /* SR * 2^16 / master_clk */ +- sym /= (state->internal->mclk >> 12); +- } else if (srate > 6000000) { +- sym = (srate << 6); +- sym /= (state->internal->mclk >> 10); +- } else { +- sym = (srate << 9); +- sym /= (state->internal->mclk >> 7); +- } +- +- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRINIT0, (sym & 0xff)) < 0) /* LSB */ +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate) +-{ +- u32 sym; +- +- srate = 105 * (srate / 100); +- if (srate > 60000000) { +- sym = (srate << 4); /* SR * 2^16 / master_clk */ +- sym /= (state->internal->mclk >> 12); +- } else if (srate > 6000000) { +- sym = (srate << 6); +- sym /= (state->internal->mclk >> 10); +- } else { +- sym = (srate << 9); +- sym /= (state->internal->mclk >> 7); +- } +- +- if (sym < 0x7fff) { +- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) /* MSB */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) /* LSB */ +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x7f) < 0) /* MSB */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xff) < 0) /* LSB */ +- goto err; +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate) +-{ +- u32 sym; +- +- srate = 95 * (srate / 100); +- if (srate > 60000000) { +- sym = (srate << 4); /* SR * 2^16 / master_clk */ +- sym /= (state->internal->mclk >> 12); +- } else if (srate > 6000000) { +- sym = (srate << 6); +- sym /= (state->internal->mclk >> 10); +- } else { +- sym = (srate << 9); +- sym /= (state->internal->mclk >> 7); +- } +- +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */ +- goto err; +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static u32 stv090x_car_width(u32 srate, enum stv090x_rolloff rolloff) +-{ +- u32 ro; +- +- switch (rolloff) { +- case STV090x_RO_20: +- ro = 20; +- break; +- case STV090x_RO_25: +- ro = 25; +- break; +- case STV090x_RO_35: +- default: +- ro = 35; +- break; +- } +- +- return srate + (srate * ro) / 100; +-} +- +-static int stv090x_set_vit_thacq(struct stv090x_state *state) +-{ +- if (STV090x_WRITE_DEMOD(state, VTH12, 0x96) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH23, 0x64) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH34, 0x36) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH56, 0x23) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH67, 0x1e) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH78, 0x19) < 0) +- goto err; +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_set_vit_thtracq(struct stv090x_state *state) +-{ +- if (STV090x_WRITE_DEMOD(state, VTH12, 0xd0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH23, 0x7d) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH34, 0x53) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH56, 0x2f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH67, 0x24) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VTH78, 0x1f) < 0) +- goto err; +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_set_viterbi(struct stv090x_state *state) +-{ +- switch (state->search_mode) { +- case STV090x_SEARCH_AUTO: +- if (STV090x_WRITE_DEMOD(state, FECM, 0x10) < 0) /* DVB-S and DVB-S2 */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x3f) < 0) /* all puncture rate */ +- goto err; +- break; +- case STV090x_SEARCH_DVBS1: +- if (STV090x_WRITE_DEMOD(state, FECM, 0x00) < 0) /* disable DSS */ +- goto err; +- switch (state->fec) { +- case STV090x_PR12: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) +- goto err; +- break; +- +- case STV090x_PR23: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) +- goto err; +- break; +- +- case STV090x_PR34: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x04) < 0) +- goto err; +- break; +- +- case STV090x_PR56: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x08) < 0) +- goto err; +- break; +- +- case STV090x_PR78: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x20) < 0) +- goto err; +- break; +- +- default: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x2f) < 0) /* all */ +- goto err; +- break; +- } +- break; +- case STV090x_SEARCH_DSS: +- if (STV090x_WRITE_DEMOD(state, FECM, 0x80) < 0) +- goto err; +- switch (state->fec) { +- case STV090x_PR12: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x01) < 0) +- goto err; +- break; +- +- case STV090x_PR23: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x02) < 0) +- goto err; +- break; +- +- case STV090x_PR67: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x10) < 0) +- goto err; +- break; +- +- default: +- if (STV090x_WRITE_DEMOD(state, PRVIT, 0x13) < 0) /* 1/2, 2/3, 6/7 */ +- goto err; +- break; +- } +- break; +- default: +- break; +- } +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_stop_modcod(struct stv090x_state *state) +-{ +- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xff) < 0) +- goto err; +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_activate_modcod(struct stv090x_state *state) +-{ +- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xfc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_activate_modcod_single(struct stv090x_state *state) +-{ +- +- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xf0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0x0f) < 0) +- goto err; +- +- return 0; +- +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable) +-{ +- u32 reg; +- +- switch (state->demod) { +- case STV090x_DEMODULATOR_0: +- mutex_lock(&state->internal->demod_lock); +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- mutex_unlock(&state->internal->demod_lock); +- break; +- +- case STV090x_DEMODULATOR_1: +- mutex_lock(&state->internal->demod_lock); +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- mutex_unlock(&state->internal->demod_lock); +- break; +- +- default: +- dprintk(FE_ERROR, 1, "Wrong demodulator!"); +- break; +- } +- return 0; +-err: +- mutex_unlock(&state->internal->demod_lock); +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_dvbs_track_crl(struct stv090x_state *state) +-{ +- if (state->internal->dev_ver >= 0x30) { +- /* Set ACLC BCLC optimised value vs SR */ +- if (state->srate >= 15000000) { +- if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1a) < 0) +- goto err; +- } else if ((state->srate >= 7000000) && (15000000 > state->srate)) { +- if (STV090x_WRITE_DEMOD(state, ACLC, 0x0c) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1b) < 0) +- goto err; +- } else if (state->srate < 7000000) { +- if (STV090x_WRITE_DEMOD(state, ACLC, 0x2c) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0x1c) < 0) +- goto err; +- } +- +- } else { +- /* Cut 2.0 */ +- if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) +- goto err; +- } +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_delivery_search(struct stv090x_state *state) +-{ +- u32 reg; +- +- switch (state->search_mode) { +- case STV090x_SEARCH_DVBS1: +- case STV090x_SEARCH_DSS: +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- /* Activate Viterbi decoder in legacy search, +- * do not use FRESVIT1, might impact VITERBI2 +- */ +- if (stv090x_vitclk_ctl(state, 0) < 0) +- goto err; +- +- if (stv090x_dvbs_track_crl(state) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x22) < 0) /* disable DVB-S2 */ +- goto err; +- +- if (stv090x_set_vit_thacq(state) < 0) +- goto err; +- if (stv090x_set_viterbi(state) < 0) +- goto err; +- break; +- +- case STV090x_SEARCH_DVBS2: +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- if (stv090x_vitclk_ctl(state, 1) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, ACLC, 0x1a) < 0) /* stop DVB-S CR loop */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) +- goto err; +- +- if (state->internal->dev_ver <= 0x20) { +- /* enable S2 carrier loop */ +- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) +- goto err; +- } else { +- /* > Cut 3: Stop carrier 3 */ +- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) +- goto err; +- } +- +- if (state->demod_mode != STV090x_SINGLE) { +- /* Cut 2: enable link during search */ +- if (stv090x_activate_modcod(state) < 0) +- goto err; +- } else { +- /* Single demodulator +- * Authorize SHORT and LONG frames, +- * QPSK, 8PSK, 16APSK and 32APSK +- */ +- if (stv090x_activate_modcod_single(state) < 0) +- goto err; +- } +- +- if (stv090x_set_vit_thtracq(state) < 0) +- goto err; +- break; +- +- case STV090x_SEARCH_AUTO: +- default: +- /* enable DVB-S2 and DVB-S2 in Auto MODE */ +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- if (stv090x_vitclk_ctl(state, 0) < 0) +- goto err; +- +- if (stv090x_dvbs_track_crl(state) < 0) +- goto err; +- +- if (state->internal->dev_ver <= 0x20) { +- /* enable S2 carrier loop */ +- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) +- goto err; +- } else { +- /* > Cut 3: Stop carrier 3 */ +- if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x66) < 0) +- goto err; +- } +- +- if (state->demod_mode != STV090x_SINGLE) { +- /* Cut 2: enable link during search */ +- if (stv090x_activate_modcod(state) < 0) +- goto err; +- } else { +- /* Single demodulator +- * Authorize SHORT and LONG frames, +- * QPSK, 8PSK, 16APSK and 32APSK +- */ +- if (stv090x_activate_modcod_single(state) < 0) +- goto err; +- } +- +- if (stv090x_set_vit_thacq(state) < 0) +- goto err; +- +- if (stv090x_set_viterbi(state) < 0) +- goto err; +- break; +- } +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_start_search(struct stv090x_state *state) +-{ +- u32 reg, freq_abs; +- s16 freq; +- +- /* Reset demodulator */ +- reg = STV090x_READ_DEMOD(state, DMDISTATE); +- STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) +- goto err; +- +- if (state->internal->dev_ver <= 0x20) { +- if (state->srate <= 5000000) { +- if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0) +- goto err; +- +- /*enlarge the timing bandwidth for Low SR*/ +- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) +- goto err; +- } else { +- /* If the symbol rate is >5 Msps +- Set The carrier search up and low to auto mode */ +- if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) +- goto err; +- /*reduce the timing bandwidth for high SR*/ +- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) +- goto err; +- } +- } else { +- /* >= Cut 3 */ +- if (state->srate <= 5000000) { +- /* enlarge the timing bandwidth for Low SR */ +- STV090x_WRITE_DEMOD(state, RTCS2, 0x68); +- } else { +- /* reduce timing bandwidth for high SR */ +- STV090x_WRITE_DEMOD(state, RTCS2, 0x44); +- } +- +- /* Set CFR min and max to manual mode */ +- STV090x_WRITE_DEMOD(state, CARCFG, 0x46); +- +- if (state->algo == STV090x_WARM_SEARCH) { +- /* WARM Start +- * CFR min = -1MHz, +- * CFR max = +1MHz +- */ +- freq_abs = 1000 << 16; +- freq_abs /= (state->internal->mclk / 1000); +- freq = (s16) freq_abs; +- } else { +- /* COLD Start +- * CFR min =- (SearchRange / 2 + 600KHz) +- * CFR max = +(SearchRange / 2 + 600KHz) +- * (600KHz for the tuner step size) +- */ +- freq_abs = (state->search_range / 2000) + 600; +- freq_abs = freq_abs << 16; +- freq_abs /= (state->internal->mclk / 1000); +- freq = (s16) freq_abs; +- } +- +- if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0) +- goto err; +- +- freq *= -1; +- +- if (STV090x_WRITE_DEMOD(state, CFRLOW1, MSB(freq)) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRLOW0, LSB(freq)) < 0) +- goto err; +- +- } +- +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) +- goto err; +- +- if ((state->search_mode == STV090x_SEARCH_DVBS1) || +- (state->search_mode == STV090x_SEARCH_DSS) || +- (state->search_mode == STV090x_SEARCH_AUTO)) { +- +- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) +- goto err; +- } +- } +- +- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xe0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xc0) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- reg = STV090x_READ_DEMOD(state, DMDCFG2); +- STV090x_SETFIELD_Px(reg, S1S2_SEQUENTIAL_FIELD, 0x0); +- if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x20) { +- /*Frequency offset detector setting*/ +- if (state->srate < 2000000) { +- if (state->internal->dev_ver <= 0x20) { +- /* Cut 2 */ +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) +- goto err; +- } else { +- /* Cut 3 */ +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0) +- goto err; +- } +- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0) +- goto err; +- } else if (state->srate < 10000000) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) +- goto err; +- } +- } else { +- if (state->srate < 10000000) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xef) < 0) +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0xed) < 0) +- goto err; +- } +- } +- +- switch (state->algo) { +- case STV090x_WARM_SEARCH: +- /* The symbol rate and the exact +- * carrier Frequency are known +- */ +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) +- goto err; +- break; +- +- case STV090x_COLD_SEARCH: +- /* The symbol rate is known */ +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) +- goto err; +- break; +- +- default: +- break; +- } +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_get_agc2_min_level(struct stv090x_state *state) +-{ +- u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg; +- s32 i, j, steps, dir; +- +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) +- goto err; +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) /* SR = 65 Msps Max */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) /* SR= 400 ksps Min */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) /* stop acq @ coarse carrier state */ +- goto err; +- if (stv090x_set_srate(state, 1000000) < 0) +- goto err; +- +- steps = state->search_range / 1000000; +- if (steps <= 0) +- steps = 1; +- +- dir = 1; +- freq_step = (1000000 * 256) / (state->internal->mclk / 256); +- freq_init = 0; +- +- for (i = 0; i < steps; i++) { +- if (dir > 0) +- freq_init = freq_init + (freq_step * i); +- else +- freq_init = freq_init - (freq_step * i); +- +- dir *= -1; +- +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_init >> 8) & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_init & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */ +- goto err; +- msleep(10); +- +- agc2 = 0; +- for (j = 0; j < 10; j++) { +- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | +- STV090x_READ_DEMOD(state, AGC2I0); +- } +- agc2 /= 10; +- if (agc2 < agc2_min) +- agc2_min = agc2; +- } +- +- return agc2_min; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static u32 stv090x_get_srate(struct stv090x_state *state, u32 clk) +-{ +- u8 r3, r2, r1, r0; +- s32 srate, int_1, int_2, tmp_1, tmp_2; +- +- r3 = STV090x_READ_DEMOD(state, SFR3); +- r2 = STV090x_READ_DEMOD(state, SFR2); +- r1 = STV090x_READ_DEMOD(state, SFR1); +- r0 = STV090x_READ_DEMOD(state, SFR0); +- +- srate = ((r3 << 24) | (r2 << 16) | (r1 << 8) | r0); +- +- int_1 = clk >> 16; +- int_2 = srate >> 16; +- +- tmp_1 = clk % 0x10000; +- tmp_2 = srate % 0x10000; +- +- srate = (int_1 * int_2) + +- ((int_1 * tmp_2) >> 16) + +- ((int_2 * tmp_1) >> 16); +- +- return srate; +-} +- +-static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) +-{ +- struct dvb_frontend *fe = &state->frontend; +- +- int tmg_lock = 0, i; +- s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq; +- u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; +- u32 agc2th; +- +- if (state->internal->dev_ver >= 0x30) +- agc2th = 0x2e00; +- else +- agc2th = 0x1f00; +- +- reg = STV090x_READ_DEMOD(state, DMDISTATE); +- STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */ +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0) +- goto err; +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x83) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, 0xc0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x82) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW0, 0xa0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x30) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) +- goto err; +- +- } else if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) +- goto err; +- } +- +- if (state->srate <= 2000000) +- car_step = 1000; +- else if (state->srate <= 5000000) +- car_step = 2000; +- else if (state->srate <= 12000000) +- car_step = 3000; +- else +- car_step = 5000; +- +- steps = -1 + ((state->search_range / 1000) / car_step); +- steps /= 2; +- steps = (2 * steps) + 1; +- if (steps < 0) +- steps = 1; +- else if (steps > 10) { +- steps = 11; +- car_step = (state->search_range / 1000) / 10; +- } +- cur_step = 0; +- dir = 1; +- freq = state->frequency; +- +- while ((!tmg_lock) && (cur_step < steps)) { +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0) +- goto err; +- /* trigger acquisition */ +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0) +- goto err; +- msleep(50); +- for (i = 0; i < 10; i++) { +- reg = STV090x_READ_DEMOD(state, DSTATUS); +- if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) +- tmg_cpt++; +- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | +- STV090x_READ_DEMOD(state, AGC2I0); +- } +- agc2 /= 10; +- srate_coarse = stv090x_get_srate(state, state->internal->mclk); +- cur_step++; +- dir *= -1; +- if ((tmg_cpt >= 5) && (agc2 < agc2th) && +- (srate_coarse < 50000000) && (srate_coarse > 850000)) +- tmg_lock = 1; +- else if (cur_step < steps) { +- if (dir > 0) +- freq += cur_step * car_step; +- else +- freq -= cur_step * car_step; +- +- /* Setup tuner */ +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_set_frequency) { +- if (state->config->tuner_set_frequency(fe, freq) < 0) +- goto err_gateoff; +- } +- +- if (state->config->tuner_set_bandwidth) { +- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- msleep(50); +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_get_status) { +- if (state->config->tuner_get_status(fe, ®) < 0) +- goto err_gateoff; +- } +- +- if (reg) +- dprintk(FE_DEBUG, 1, "Tuner phase locked"); +- else +- dprintk(FE_DEBUG, 1, "Tuner unlocked"); +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- } +- } +- if (!tmg_lock) +- srate_coarse = 0; +- else +- srate_coarse = stv090x_get_srate(state, state->internal->mclk); +- +- return srate_coarse; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static u32 stv090x_srate_srch_fine(struct stv090x_state *state) +-{ +- u32 srate_coarse, freq_coarse, sym, reg; +- +- srate_coarse = stv090x_get_srate(state, state->internal->mclk); +- freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8; +- freq_coarse |= STV090x_READ_DEMOD(state, CFR1); +- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ +- +- if (sym < state->srate) +- srate_coarse = 0; +- else { +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) +- goto err; +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x30) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) +- goto err; +- } else if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) +- goto err; +- } +- +- if (srate_coarse > 3000000) { +- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ +- sym = (sym / 1000) * 65536; +- sym /= (state->internal->mclk / 1000); +- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) +- goto err; +- sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */ +- sym = (sym / 1000) * 65536; +- sym /= (state->internal->mclk / 1000); +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) +- goto err; +- sym = (srate_coarse / 1000) * 65536; +- sym /= (state->internal->mclk / 1000); +- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) +- goto err; +- } else { +- sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ +- sym = (sym / 100) * 65536; +- sym /= (state->internal->mclk / 100); +- if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) +- goto err; +- sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */ +- sym = (sym / 100) * 65536; +- sym /= (state->internal->mclk / 100); +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) +- goto err; +- sym = (srate_coarse / 100) * 65536; +- sym /= (state->internal->mclk / 100); +- if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) +- goto err; +- } +- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, (freq_coarse >> 8) & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, freq_coarse & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) /* trigger acquisition */ +- goto err; +- } +- +- return srate_coarse; +- +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout) +-{ +- s32 timer = 0, lock = 0; +- u32 reg; +- u8 stat; +- +- while ((timer < timeout) && (!lock)) { +- reg = STV090x_READ_DEMOD(state, DMDSTATE); +- stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); +- +- switch (stat) { +- case 0: /* searching */ +- case 1: /* first PLH detected */ +- default: +- dprintk(FE_DEBUG, 1, "Demodulator searching .."); +- lock = 0; +- break; +- case 2: /* DVB-S2 mode */ +- case 3: /* DVB-S1/legacy mode */ +- reg = STV090x_READ_DEMOD(state, DSTATUS); +- lock = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); +- break; +- } +- +- if (!lock) +- msleep(10); +- else +- dprintk(FE_DEBUG, 1, "Demodulator acquired LOCK"); +- +- timer += 10; +- } +- return lock; +-} +- +-static int stv090x_blind_search(struct stv090x_state *state) +-{ +- u32 agc2, reg, srate_coarse; +- s32 cpt_fail, agc2_ovflw, i; +- u8 k_ref, k_max, k_min; +- int coarse_fail = 0; +- int lock; +- +- k_max = 110; +- k_min = 10; +- +- agc2 = stv090x_get_agc2_min_level(state); +- +- if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) { +- lock = 0; +- } else { +- +- if (state->internal->dev_ver <= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) +- goto err; +- } else { +- /* > Cut 3 */ +- if (STV090x_WRITE_DEMOD(state, CARCFG, 0x06) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x82) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x00) < 0) /* set viterbi hysteresis */ +- goto err; +- } +- +- k_ref = k_max; +- do { +- if (STV090x_WRITE_DEMOD(state, KREFTMG, k_ref) < 0) +- goto err; +- if (stv090x_srate_srch_coarse(state) != 0) { +- srate_coarse = stv090x_srate_srch_fine(state); +- if (srate_coarse != 0) { +- stv090x_get_lock_tmg(state); +- lock = stv090x_get_dmdlock(state, +- state->DemodTimeout); +- } else { +- lock = 0; +- } +- } else { +- cpt_fail = 0; +- agc2_ovflw = 0; +- for (i = 0; i < 10; i++) { +- agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | +- STV090x_READ_DEMOD(state, AGC2I0); +- if (agc2 >= 0xff00) +- agc2_ovflw++; +- reg = STV090x_READ_DEMOD(state, DSTATUS2); +- if ((STV090x_GETFIELD_Px(reg, CFR_OVERFLOW_FIELD) == 0x01) && +- (STV090x_GETFIELD_Px(reg, DEMOD_DELOCK_FIELD) == 0x01)) +- +- cpt_fail++; +- } +- if ((cpt_fail > 7) || (agc2_ovflw > 7)) +- coarse_fail = 1; +- +- lock = 0; +- } +- k_ref -= 20; +- } while ((k_ref >= k_min) && (!lock) && (!coarse_fail)); +- } +- +- return lock; +- +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_chk_tmg(struct stv090x_state *state) +-{ +- u32 reg; +- s32 tmg_cpt = 0, i; +- u8 freq, tmg_thh, tmg_thl; +- int tmg_lock = 0; +- +- freq = STV090x_READ_DEMOD(state, CARFREQ); +- tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE); +- tmg_thl = STV090x_READ_DEMOD(state, TMGTHFALL); +- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0x00) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); /* stop carrier offset search */ +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, RTC, 0x80) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x40) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x00) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) /* set car ofset to 0 */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x65) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) /* trigger acquisition */ +- goto err; +- msleep(10); +- +- for (i = 0; i < 10; i++) { +- reg = STV090x_READ_DEMOD(state, DSTATUS); +- if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) +- tmg_cpt++; +- msleep(1); +- } +- if (tmg_cpt >= 3) +- tmg_lock = 1; +- +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) /* DVB-S1 timing */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) /* DVB-S2 timing */ +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, CARFREQ, freq) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHRISE, tmg_thh) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGTHFALL, tmg_thl) < 0) +- goto err; +- +- return tmg_lock; +- +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) +-{ +- struct dvb_frontend *fe = &state->frontend; +- +- u32 reg; +- s32 car_step, steps, cur_step, dir, freq, timeout_lock; +- int lock = 0; +- +- if (state->srate >= 10000000) +- timeout_lock = timeout_dmd / 3; +- else +- timeout_lock = timeout_dmd / 2; +- +- lock = stv090x_get_dmdlock(state, timeout_lock); /* cold start wait */ +- if (!lock) { +- if (state->srate >= 10000000) { +- if (stv090x_chk_tmg(state)) { +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) +- goto err; +- lock = stv090x_get_dmdlock(state, timeout_dmd); +- } else { +- lock = 0; +- } +- } else { +- if (state->srate <= 4000000) +- car_step = 1000; +- else if (state->srate <= 7000000) +- car_step = 2000; +- else if (state->srate <= 10000000) +- car_step = 3000; +- else +- car_step = 5000; +- +- steps = (state->search_range / 1000) / car_step; +- steps /= 2; +- steps = 2 * (steps + 1); +- if (steps < 0) +- steps = 2; +- else if (steps > 12) +- steps = 12; +- +- cur_step = 1; +- dir = 1; +- +- if (!lock) { +- freq = state->frequency; +- state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + state->srate; +- while ((cur_step <= steps) && (!lock)) { +- if (dir > 0) +- freq += cur_step * car_step; +- else +- freq -= cur_step * car_step; +- +- /* Setup tuner */ +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_set_frequency) { +- if (state->config->tuner_set_frequency(fe, freq) < 0) +- goto err_gateoff; +- } +- +- if (state->config->tuner_set_bandwidth) { +- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- msleep(50); +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_get_status) { +- if (state->config->tuner_get_status(fe, ®) < 0) +- goto err_gateoff; +- } +- +- if (reg) +- dprintk(FE_DEBUG, 1, "Tuner phase locked"); +- else +- dprintk(FE_DEBUG, 1, "Tuner unlocked"); +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x15) < 0) +- goto err; +- lock = stv090x_get_dmdlock(state, (timeout_dmd / 3)); +- +- dir *= -1; +- cur_step++; +- } +- } +- } +- } +- +- return lock; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s32 *timeout_sw, s32 *steps) +-{ +- s32 timeout, inc, steps_max, srate, car_max; +- +- srate = state->srate; +- car_max = state->search_range / 1000; +- car_max += car_max / 10; +- car_max = 65536 * (car_max / 2); +- car_max /= (state->internal->mclk / 1000); +- +- if (car_max > 0x4000) +- car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */ +- +- inc = srate; +- inc /= state->internal->mclk / 1000; +- inc *= 256; +- inc *= 256; +- inc /= 1000; +- +- switch (state->search_mode) { +- case STV090x_SEARCH_DVBS1: +- case STV090x_SEARCH_DSS: +- inc *= 3; /* freq step = 3% of srate */ +- timeout = 20; +- break; +- +- case STV090x_SEARCH_DVBS2: +- inc *= 4; +- timeout = 25; +- break; +- +- case STV090x_SEARCH_AUTO: +- default: +- inc *= 3; +- timeout = 25; +- break; +- } +- inc /= 100; +- if ((inc > car_max) || (inc < 0)) +- inc = car_max / 2; /* increment <= 1/8 Mclk */ +- +- timeout *= 27500; /* 27.5 Msps reference */ +- if (srate > 0) +- timeout /= (srate / 1000); +- +- if ((timeout > 100) || (timeout < 0)) +- timeout = 100; +- +- steps_max = (car_max / inc) + 1; /* min steps = 3 */ +- if ((steps_max > 100) || (steps_max < 0)) { +- steps_max = 100; /* max steps <= 100 */ +- inc = car_max / steps_max; +- } +- *freq_inc = inc; +- *timeout_sw = timeout; +- *steps = steps_max; +- +- return 0; +-} +- +-static int stv090x_chk_signal(struct stv090x_state *state) +-{ +- s32 offst_car, agc2, car_max; +- int no_signal; +- +- offst_car = STV090x_READ_DEMOD(state, CFR2) << 8; +- offst_car |= STV090x_READ_DEMOD(state, CFR1); +- offst_car = comp2(offst_car, 16); +- +- agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8; +- agc2 |= STV090x_READ_DEMOD(state, AGC2I0); +- car_max = state->search_range / 1000; +- +- car_max += (car_max / 10); /* 10% margin */ +- car_max = (65536 * car_max / 2); +- car_max /= state->internal->mclk / 1000; +- +- if (car_max > 0x4000) +- car_max = 0x4000; +- +- if ((agc2 > 0x2000) || (offst_car > 2 * car_max) || (offst_car < -2 * car_max)) { +- no_signal = 1; +- dprintk(FE_DEBUG, 1, "No Signal"); +- } else { +- no_signal = 0; +- dprintk(FE_DEBUG, 1, "Found Signal"); +- } +- +- return no_signal; +-} +- +-static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 timeout, int zigzag, s32 steps_max) +-{ +- int no_signal, lock = 0; +- s32 cpt_step = 0, offst_freq, car_max; +- u32 reg; +- +- car_max = state->search_range / 1000; +- car_max += (car_max / 10); +- car_max = (65536 * car_max / 2); +- car_max /= (state->internal->mclk / 1000); +- if (car_max > 0x4000) +- car_max = 0x4000; +- +- if (zigzag) +- offst_freq = 0; +- else +- offst_freq = -car_max + inc; +- +- do { +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, ((offst_freq / 256) & 0xff)) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, offst_freq & 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, PDELCTRL1); +- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x1); /* stop DVB-S2 packet delin */ +- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) +- goto err; +- +- if (zigzag) { +- if (offst_freq >= 0) +- offst_freq = -offst_freq - 2 * inc; +- else +- offst_freq = -offst_freq; +- } else { +- offst_freq += 2 * inc; +- } +- +- cpt_step++; +- +- lock = stv090x_get_dmdlock(state, timeout); +- no_signal = stv090x_chk_signal(state); +- +- } while ((!lock) && +- (!no_signal) && +- ((offst_freq - inc) < car_max) && +- ((offst_freq + inc) > -car_max) && +- (cpt_step < steps_max)); +- +- reg = STV090x_READ_DEMOD(state, PDELCTRL1); +- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) +- goto err; +- +- return lock; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_sw_algo(struct stv090x_state *state) +-{ +- int no_signal, zigzag, lock = 0; +- u32 reg; +- +- s32 dvbs2_fly_wheel; +- s32 inc, timeout_step, trials, steps_max; +- +- /* get params */ +- stv090x_get_loop_params(state, &inc, &timeout_step, &steps_max); +- +- switch (state->search_mode) { +- case STV090x_SEARCH_DVBS1: +- case STV090x_SEARCH_DSS: +- /* accelerate the frequency detector */ +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x49) < 0) +- goto err; +- zigzag = 0; +- break; +- +- case STV090x_SEARCH_DVBS2: +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) +- goto err; +- zigzag = 1; +- break; +- +- case STV090x_SEARCH_AUTO: +- default: +- /* accelerate the frequency detector */ +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0xc9) < 0) +- goto err; +- zigzag = 0; +- break; +- } +- +- trials = 0; +- do { +- lock = stv090x_search_car_loop(state, inc, timeout_step, zigzag, steps_max); +- no_signal = stv090x_chk_signal(state); +- trials++; +- +- /*run the SW search 2 times maximum*/ +- if (lock || no_signal || (trials == 2)) { +- /*Check if the demod is not losing lock in DVBS2*/ +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) +- goto err; +- } +- +- reg = STV090x_READ_DEMOD(state, DMDSTATE); +- if ((lock) && (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == STV090x_DVBS2)) { +- /*Check if the demod is not losing lock in DVBS2*/ +- msleep(timeout_step); +- reg = STV090x_READ_DEMOD(state, DMDFLYW); +- dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); +- if (dvbs2_fly_wheel < 0xd) { /*if correct frames is decrementing */ +- msleep(timeout_step); +- reg = STV090x_READ_DEMOD(state, DMDFLYW); +- dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); +- } +- if (dvbs2_fly_wheel < 0xd) { +- /*FALSE lock, The demod is losing lock */ +- lock = 0; +- if (trials < 2) { +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, 0x89) < 0) +- goto err; +- } +- } +- } +- } +- } while ((!lock) && (trials < 2) && (!no_signal)); +- +- return lock; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static enum stv090x_delsys stv090x_get_std(struct stv090x_state *state) +-{ +- u32 reg; +- enum stv090x_delsys delsys; +- +- reg = STV090x_READ_DEMOD(state, DMDSTATE); +- if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 2) +- delsys = STV090x_DVBS2; +- else if (STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD) == 3) { +- reg = STV090x_READ_DEMOD(state, FECM); +- if (STV090x_GETFIELD_Px(reg, DSS_DVB_FIELD) == 1) +- delsys = STV090x_DSS; +- else +- delsys = STV090x_DVBS1; +- } else { +- delsys = STV090x_ERROR; +- } +- +- return delsys; +-} +- +-/* in Hz */ +-static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) +-{ +- s32 derot, int_1, int_2, tmp_1, tmp_2; +- +- derot = STV090x_READ_DEMOD(state, CFR2) << 16; +- derot |= STV090x_READ_DEMOD(state, CFR1) << 8; +- derot |= STV090x_READ_DEMOD(state, CFR0); +- +- derot = comp2(derot, 24); +- int_1 = mclk >> 12; +- int_2 = derot >> 12; +- +- /* carrier_frequency = MasterClock * Reg / 2^24 */ +- tmp_1 = mclk % 0x1000; +- tmp_2 = derot % 0x1000; +- +- derot = (int_1 * int_2) + +- ((int_1 * tmp_2) >> 12) + +- ((int_2 * tmp_1) >> 12); +- +- return derot; +-} +- +-static int stv090x_get_viterbi(struct stv090x_state *state) +-{ +- u32 reg, rate; +- +- reg = STV090x_READ_DEMOD(state, VITCURPUN); +- rate = STV090x_GETFIELD_Px(reg, VIT_CURPUN_FIELD); +- +- switch (rate) { +- case 13: +- state->fec = STV090x_PR12; +- break; +- +- case 18: +- state->fec = STV090x_PR23; +- break; +- +- case 21: +- state->fec = STV090x_PR34; +- break; +- +- case 24: +- state->fec = STV090x_PR56; +- break; +- +- case 25: +- state->fec = STV090x_PR67; +- break; +- +- case 26: +- state->fec = STV090x_PR78; +- break; +- +- default: +- state->fec = STV090x_PRERR; +- break; +- } +- +- return 0; +-} +- +-static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *state) +-{ +- struct dvb_frontend *fe = &state->frontend; +- +- u8 tmg; +- u32 reg; +- s32 i = 0, offst_freq; +- +- msleep(5); +- +- if (state->algo == STV090x_BLIND_SEARCH) { +- tmg = STV090x_READ_DEMOD(state, TMGREG2); +- STV090x_WRITE_DEMOD(state, SFRSTEP, 0x5c); +- while ((i <= 50) && (tmg != 0) && (tmg != 0xff)) { +- tmg = STV090x_READ_DEMOD(state, TMGREG2); +- msleep(5); +- i += 5; +- } +- } +- state->delsys = stv090x_get_std(state); +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_get_frequency) { +- if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000; +- state->frequency += offst_freq; +- +- if (stv090x_get_viterbi(state) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, DMDMODCOD); +- state->modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); +- state->pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; +- state->frame_len = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) >> 1; +- reg = STV090x_READ_DEMOD(state, TMGOBS); +- state->rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); +- reg = STV090x_READ_DEMOD(state, FECM); +- state->inversion = STV090x_GETFIELD_Px(reg, IQINV_FIELD); +- +- if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) { +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_get_frequency) { +- if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) +- return STV090x_RANGEOK; +- else if (abs(offst_freq) <= (stv090x_car_width(state->srate, state->rolloff) / 2000)) +- return STV090x_RANGEOK; +- else +- return STV090x_OUTOFRANGE; /* Out of Range */ +- } else { +- if (abs(offst_freq) <= ((state->search_range / 2000) + 500)) +- return STV090x_RANGEOK; +- else +- return STV090x_OUTOFRANGE; +- } +- +- return STV090x_OUTOFRANGE; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static u32 stv090x_get_tmgoffst(struct stv090x_state *state, u32 srate) +-{ +- s32 offst_tmg; +- +- offst_tmg = STV090x_READ_DEMOD(state, TMGREG2) << 16; +- offst_tmg |= STV090x_READ_DEMOD(state, TMGREG1) << 8; +- offst_tmg |= STV090x_READ_DEMOD(state, TMGREG0); +- +- offst_tmg = comp2(offst_tmg, 24); /* 2's complement */ +- if (!offst_tmg) +- offst_tmg = 1; +- +- offst_tmg = ((s32) srate * 10) / ((s32) 0x1000000 / offst_tmg); +- offst_tmg /= 320; +- +- return offst_tmg; +-} +- +-static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_modcod modcod, s32 pilots) +-{ +- u8 aclc = 0x29; +- s32 i; +- struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low; +- +- if (state->internal->dev_ver == 0x20) { +- car_loop = stv090x_s2_crl_cut20; +- car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20; +- car_loop_apsk_low = stv090x_s2_apsk_crl_cut20; +- } else { +- /* >= Cut 3 */ +- car_loop = stv090x_s2_crl_cut30; +- car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut30; +- car_loop_apsk_low = stv090x_s2_apsk_crl_cut30; +- } +- +- if (modcod < STV090x_QPSK_12) { +- i = 0; +- while ((i < 3) && (modcod != car_loop_qpsk_low[i].modcod)) +- i++; +- +- if (i >= 3) +- i = 2; +- +- } else { +- i = 0; +- while ((i < 14) && (modcod != car_loop[i].modcod)) +- i++; +- +- if (i >= 14) { +- i = 0; +- while ((i < 11) && (modcod != car_loop_apsk_low[i].modcod)) +- i++; +- +- if (i >= 11) +- i = 10; +- } +- } +- +- if (modcod <= STV090x_QPSK_25) { +- if (pilots) { +- if (state->srate <= 3000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_on_2; +- else if (state->srate <= 7000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_on_5; +- else if (state->srate <= 15000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_on_10; +- else if (state->srate <= 25000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_on_20; +- else +- aclc = car_loop_qpsk_low[i].crl_pilots_on_30; +- } else { +- if (state->srate <= 3000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_off_2; +- else if (state->srate <= 7000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_off_5; +- else if (state->srate <= 15000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_off_10; +- else if (state->srate <= 25000000) +- aclc = car_loop_qpsk_low[i].crl_pilots_off_20; +- else +- aclc = car_loop_qpsk_low[i].crl_pilots_off_30; +- } +- +- } else if (modcod <= STV090x_8PSK_910) { +- if (pilots) { +- if (state->srate <= 3000000) +- aclc = car_loop[i].crl_pilots_on_2; +- else if (state->srate <= 7000000) +- aclc = car_loop[i].crl_pilots_on_5; +- else if (state->srate <= 15000000) +- aclc = car_loop[i].crl_pilots_on_10; +- else if (state->srate <= 25000000) +- aclc = car_loop[i].crl_pilots_on_20; +- else +- aclc = car_loop[i].crl_pilots_on_30; +- } else { +- if (state->srate <= 3000000) +- aclc = car_loop[i].crl_pilots_off_2; +- else if (state->srate <= 7000000) +- aclc = car_loop[i].crl_pilots_off_5; +- else if (state->srate <= 15000000) +- aclc = car_loop[i].crl_pilots_off_10; +- else if (state->srate <= 25000000) +- aclc = car_loop[i].crl_pilots_off_20; +- else +- aclc = car_loop[i].crl_pilots_off_30; +- } +- } else { /* 16APSK and 32APSK */ +- if (state->srate <= 3000000) +- aclc = car_loop_apsk_low[i].crl_pilots_on_2; +- else if (state->srate <= 7000000) +- aclc = car_loop_apsk_low[i].crl_pilots_on_5; +- else if (state->srate <= 15000000) +- aclc = car_loop_apsk_low[i].crl_pilots_on_10; +- else if (state->srate <= 25000000) +- aclc = car_loop_apsk_low[i].crl_pilots_on_20; +- else +- aclc = car_loop_apsk_low[i].crl_pilots_on_30; +- } +- +- return aclc; +-} +- +-static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) +-{ +- struct stv090x_short_frame_crloop *short_crl = NULL; +- s32 index = 0; +- u8 aclc = 0x0b; +- +- switch (state->modulation) { +- case STV090x_QPSK: +- default: +- index = 0; +- break; +- case STV090x_8PSK: +- index = 1; +- break; +- case STV090x_16APSK: +- index = 2; +- break; +- case STV090x_32APSK: +- index = 3; +- break; +- } +- +- if (state->internal->dev_ver >= 0x30) { +- /* Cut 3.0 and up */ +- short_crl = stv090x_s2_short_crl_cut30; +- } else { +- /* Cut 2.0 and up: we don't support cuts older than 2.0 */ +- short_crl = stv090x_s2_short_crl_cut20; +- } +- +- if (state->srate <= 3000000) +- aclc = short_crl[index].crl_2; +- else if (state->srate <= 7000000) +- aclc = short_crl[index].crl_5; +- else if (state->srate <= 15000000) +- aclc = short_crl[index].crl_10; +- else if (state->srate <= 25000000) +- aclc = short_crl[index].crl_20; +- else +- aclc = short_crl[index].crl_30; +- +- return aclc; +-} +- +-static int stv090x_optimize_track(struct stv090x_state *state) +-{ +- struct dvb_frontend *fe = &state->frontend; +- +- enum stv090x_rolloff rolloff; +- enum stv090x_modcod modcod; +- +- s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; +- u32 reg; +- +- srate = stv090x_get_srate(state, state->internal->mclk); +- srate += stv090x_get_tmgoffst(state, srate); +- +- switch (state->delsys) { +- case STV090x_DVBS1: +- case STV090x_DSS: +- if (state->search_mode == STV090x_SEARCH_AUTO) { +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- } +- reg = STV090x_READ_DEMOD(state, DEMOD); +- STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); +- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x01); +- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x30) { +- if (stv090x_get_viterbi(state) < 0) +- goto err; +- +- if (state->fec == STV090x_PR12) { +- if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x98) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, GAUSSR0, 0x18) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CCIR0, 0x18) < 0) +- goto err; +- } +- } +- +- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) +- goto err; +- break; +- +- case STV090x_DVBS2: +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- if (state->internal->dev_ver >= 0x30) { +- if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) +- goto err; +- } +- if (state->frame_len == STV090x_LONG_FRAME) { +- reg = STV090x_READ_DEMOD(state, DMDMODCOD); +- modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); +- pilots = STV090x_GETFIELD_Px(reg, DEMOD_TYPE_FIELD) & 0x01; +- aclc = stv090x_optimize_carloop(state, modcod, pilots); +- if (modcod <= STV090x_QPSK_910) { +- STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc); +- } else if (modcod <= STV090x_8PSK_910) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) +- goto err; +- } +- if ((state->demod_mode == STV090x_SINGLE) && (modcod > STV090x_8PSK_910)) { +- if (modcod <= STV090x_16APSK_910) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) +- goto err; +- } +- } +- } else { +- /*Carrier loop setting for short frame*/ +- aclc = stv090x_optimize_carloop_short(state); +- if (state->modulation == STV090x_QPSK) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, aclc) < 0) +- goto err; +- } else if (state->modulation == STV090x_8PSK) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S28, aclc) < 0) +- goto err; +- } else if (state->modulation == STV090x_16APSK) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S216A, aclc) < 0) +- goto err; +- } else if (state->modulation == STV090x_32APSK) { +- if (STV090x_WRITE_DEMOD(state, ACLC2S2Q, 0x2a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ACLC2S232A, aclc) < 0) +- goto err; +- } +- } +- +- STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67); /* PER */ +- break; +- +- case STV090x_ERROR: +- default: +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); +- STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- break; +- } +- +- f_1 = STV090x_READ_DEMOD(state, CFR2); +- f_0 = STV090x_READ_DEMOD(state, CFR1); +- reg = STV090x_READ_DEMOD(state, TMGOBS); +- rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); +- +- if (state->algo == STV090x_BLIND_SEARCH) { +- STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00); +- reg = STV090x_READ_DEMOD(state, DMDCFGMD); +- STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0x00); +- STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0x00); +- if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) +- goto err; +- +- if (stv090x_set_srate(state, srate) < 0) +- goto err; +- blind_tune = 1; +- +- if (stv090x_dvbs_track_crl(state) < 0) +- goto err; +- } +- +- if (state->internal->dev_ver >= 0x20) { +- if ((state->search_mode == STV090x_SEARCH_DVBS1) || +- (state->search_mode == STV090x_SEARCH_DSS) || +- (state->search_mode == STV090x_SEARCH_AUTO)) { +- +- if (STV090x_WRITE_DEMOD(state, VAVSRVIT, 0x0a) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, VITSCALE, 0x00) < 0) +- goto err; +- } +- } +- +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) +- goto err; +- +- /* AUTO tracking MODE */ +- if (STV090x_WRITE_DEMOD(state, SFRUP1, 0x80) < 0) +- goto err; +- /* AUTO tracking MODE */ +- if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0) +- goto err; +- +- if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) || +- (state->srate < 10000000)) { +- /* update initial carrier freq with the found freq offset */ +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) +- goto err; +- state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000; +- +- if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) { +- +- if (state->algo != STV090x_WARM_SEARCH) { +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_set_bandwidth) { +- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- } +- } +- if ((state->algo == STV090x_BLIND_SEARCH) || (state->srate < 10000000)) +- msleep(50); /* blind search: wait 50ms for SR stabilization */ +- else +- msleep(5); +- +- stv090x_get_lock_tmg(state); +- +- if (!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) { +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) +- goto err; +- +- i = 0; +- +- while ((!(stv090x_get_dmdlock(state, (state->DemodTimeout / 2)))) && (i <= 2)) { +- +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, CFRINIT0, f_0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x18) < 0) +- goto err; +- i++; +- } +- } +- +- } +- +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) +- goto err; +- } +- +- if ((state->delsys == STV090x_DVBS1) || (state->delsys == STV090x_DSS)) +- stv090x_set_vit_thtracq(state); +- +- return 0; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_get_feclock(struct stv090x_state *state, s32 timeout) +-{ +- s32 timer = 0, lock = 0, stat; +- u32 reg; +- +- while ((timer < timeout) && (!lock)) { +- reg = STV090x_READ_DEMOD(state, DMDSTATE); +- stat = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); +- +- switch (stat) { +- case 0: /* searching */ +- case 1: /* first PLH detected */ +- default: +- lock = 0; +- break; +- +- case 2: /* DVB-S2 mode */ +- reg = STV090x_READ_DEMOD(state, PDELSTATUS1); +- lock = STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD); +- break; +- +- case 3: /* DVB-S1/legacy mode */ +- reg = STV090x_READ_DEMOD(state, VSTATUSVIT); +- lock = STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD); +- break; +- } +- if (!lock) { +- msleep(10); +- timer += 10; +- } +- } +- return lock; +-} +- +-static int stv090x_get_lock(struct stv090x_state *state, s32 timeout_dmd, s32 timeout_fec) +-{ +- u32 reg; +- s32 timer = 0; +- int lock; +- +- lock = stv090x_get_dmdlock(state, timeout_dmd); +- if (lock) +- lock = stv090x_get_feclock(state, timeout_fec); +- +- if (lock) { +- lock = 0; +- +- while ((timer < timeout_fec) && (!lock)) { +- reg = STV090x_READ_DEMOD(state, TSSTATUS); +- lock = STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD); +- msleep(1); +- timer++; +- } +- } +- +- return lock; +-} +- +-static int stv090x_set_s2rolloff(struct stv090x_state *state) +-{ +- u32 reg; +- +- if (state->internal->dev_ver <= 0x20) { +- /* rolloff to auto mode if DVBS2 */ +- reg = STV090x_READ_DEMOD(state, DEMOD); +- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00); +- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) +- goto err; +- } else { +- /* DVB-S2 rolloff to auto mode if DVBS2 */ +- reg = STV090x_READ_DEMOD(state, DEMOD); +- STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 0x00); +- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) +- goto err; +- } +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +- +-static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) +-{ +- struct dvb_frontend *fe = &state->frontend; +- enum stv090x_signal_state signal_state = STV090x_NOCARRIER; +- u32 reg; +- s32 agc1_power, power_iq = 0, i; +- int lock = 0, low_sr = 0, no_signal = 0; +- +- reg = STV090x_READ_DEMOD(state, TSCFGH); +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* Stop path 1 stream merger */ +- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */ +- goto err; +- +- if (state->internal->dev_ver >= 0x20) { +- if (state->srate > 5000000) { +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0) +- goto err; +- } +- } +- +- stv090x_get_lock_tmg(state); +- +- if (state->algo == STV090x_BLIND_SEARCH) { +- state->tuner_bw = 2 * 36000000; /* wide bw for unknown srate */ +- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) /* wider srate scan */ +- goto err; +- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) +- goto err; +- if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */ +- goto err; +- } else { +- /* known srate */ +- if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x20) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, TMGCFG, 0xd2) < 0) +- goto err; +- +- if (state->srate < 2000000) { +- /* SR < 2MSPS */ +- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x63) < 0) +- goto err; +- } else { +- /* SR >= 2Msps */ +- if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) +- goto err; +- } +- +- if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) +- goto err; +- +- if (state->internal->dev_ver >= 0x20) { +- if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0) +- goto err; +- if (state->algo == STV090x_COLD_SEARCH) +- state->tuner_bw = (15 * (stv090x_car_width(state->srate, state->rolloff) + 10000000)) / 10; +- else if (state->algo == STV090x_WARM_SEARCH) +- state->tuner_bw = stv090x_car_width(state->srate, state->rolloff) + 10000000; +- } +- +- /* if cold start or warm (Symbolrate is known) +- * use a Narrow symbol rate scan range +- */ +- if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) /* narrow srate scan */ +- goto err; +- +- if (stv090x_set_srate(state, state->srate) < 0) +- goto err; +- +- if (stv090x_set_max_srate(state, state->internal->mclk, +- state->srate) < 0) +- goto err; +- if (stv090x_set_min_srate(state, state->internal->mclk, +- state->srate) < 0) +- goto err; +- +- if (state->srate >= 10000000) +- low_sr = 0; +- else +- low_sr = 1; +- } +- +- /* Setup tuner */ +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_set_bbgain) { +- reg = state->config->tuner_bbgain; +- if (reg == 0) +- reg = 10; /* default: 10dB */ +- if (state->config->tuner_set_bbgain(fe, reg) < 0) +- goto err_gateoff; +- } +- +- if (state->config->tuner_set_frequency) { +- if (state->config->tuner_set_frequency(fe, state->frequency) < 0) +- goto err_gateoff; +- } +- +- if (state->config->tuner_set_bandwidth) { +- if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- msleep(50); +- +- if (state->config->tuner_get_status) { +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- if (state->config->tuner_get_status(fe, ®) < 0) +- goto err_gateoff; +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- if (reg) +- dprintk(FE_DEBUG, 1, "Tuner phase locked"); +- else { +- dprintk(FE_DEBUG, 1, "Tuner unlocked"); +- return STV090x_NOCARRIER; +- } +- } +- +- msleep(10); +- agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1), +- STV090x_READ_DEMOD(state, AGCIQIN0)); +- +- if (agc1_power == 0) { +- /* If AGC1 integrator value is 0 +- * then read POWERI, POWERQ +- */ +- for (i = 0; i < 5; i++) { +- power_iq += (STV090x_READ_DEMOD(state, POWERI) + +- STV090x_READ_DEMOD(state, POWERQ)) >> 1; +- } +- power_iq /= 5; +- } +- +- if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) { +- dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq); +- lock = 0; +- signal_state = STV090x_NOAGC1; +- } else { +- reg = STV090x_READ_DEMOD(state, DEMOD); +- STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); +- +- if (state->internal->dev_ver <= 0x20) { +- /* rolloff to auto mode if DVBS2 */ +- STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1); +- } else { +- /* DVB-S2 rolloff to auto mode if DVBS2 */ +- STV090x_SETFIELD_Px(reg, MANUAL_S2ROLLOFF_FIELD, 1); +- } +- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) +- goto err; +- +- if (stv090x_delivery_search(state) < 0) +- goto err; +- +- if (state->algo != STV090x_BLIND_SEARCH) { +- if (stv090x_start_search(state) < 0) +- goto err; +- } +- } +- +- if (signal_state == STV090x_NOAGC1) +- return signal_state; +- +- if (state->algo == STV090x_BLIND_SEARCH) +- lock = stv090x_blind_search(state); +- +- else if (state->algo == STV090x_COLD_SEARCH) +- lock = stv090x_get_coldlock(state, state->DemodTimeout); +- +- else if (state->algo == STV090x_WARM_SEARCH) +- lock = stv090x_get_dmdlock(state, state->DemodTimeout); +- +- if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) { +- if (!low_sr) { +- if (stv090x_chk_tmg(state)) +- lock = stv090x_sw_algo(state); +- } +- } +- +- if (lock) +- signal_state = stv090x_get_sig_params(state); +- +- if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */ +- stv090x_optimize_track(state); +- +- if (state->internal->dev_ver >= 0x20) { +- /* >= Cut 2.0 :release TS reset after +- * demod lock and optimized Tracking +- */ +- reg = STV090x_READ_DEMOD(state, TSCFGH); +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ +- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) +- goto err; +- +- msleep(3); +- +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 1); /* merger reset */ +- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) +- goto err; +- +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0); /* release merger reset */ +- if (STV090x_WRITE_DEMOD(state, TSCFGH, reg) < 0) +- goto err; +- } +- +- lock = stv090x_get_lock(state, state->FecTimeout, +- state->FecTimeout); +- if (lock) { +- if (state->delsys == STV090x_DVBS2) { +- stv090x_set_s2rolloff(state); +- +- reg = STV090x_READ_DEMOD(state, PDELCTRL2); +- STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 1); +- if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) +- goto err; +- /* Reset DVBS2 packet delinator error counter */ +- reg = STV090x_READ_DEMOD(state, PDELCTRL2); +- STV090x_SETFIELD_Px(reg, RESET_UPKO_COUNT, 0); +- if (STV090x_WRITE_DEMOD(state, PDELCTRL2, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x67) < 0) /* PER */ +- goto err; +- } else { +- if (STV090x_WRITE_DEMOD(state, ERRCTRL1, 0x75) < 0) +- goto err; +- } +- /* Reset the Total packet counter */ +- if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0x00) < 0) +- goto err; +- /* Reset the packet Error counter2 */ +- if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) +- goto err; +- } else { +- signal_state = STV090x_NODATA; +- no_signal = stv090x_chk_signal(state); +- } +- } +- return signal_state; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static enum dvbfe_search stv090x_search(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- struct dtv_frontend_properties *props = &fe->dtv_property_cache; +- +- if (props->frequency == 0) +- return DVBFE_ALGO_SEARCH_INVALID; +- +- state->delsys = props->delivery_system; +- state->frequency = props->frequency; +- state->srate = props->symbol_rate; +- state->search_mode = STV090x_SEARCH_AUTO; +- state->algo = STV090x_COLD_SEARCH; +- state->fec = STV090x_PRERR; +- if (state->srate > 10000000) { +- dprintk(FE_DEBUG, 1, "Search range: 10 MHz"); +- state->search_range = 10000000; +- } else { +- dprintk(FE_DEBUG, 1, "Search range: 5 MHz"); +- state->search_range = 5000000; +- } +- +- if (stv090x_algo(state) == STV090x_RANGEOK) { +- dprintk(FE_DEBUG, 1, "Search success!"); +- return DVBFE_ALGO_SEARCH_SUCCESS; +- } else { +- dprintk(FE_DEBUG, 1, "Search failed!"); +- return DVBFE_ALGO_SEARCH_FAILED; +- } +- +- return DVBFE_ALGO_SEARCH_ERROR; +-} +- +-static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg, dstatus; +- u8 search_state; +- +- *status = 0; +- +- dstatus = STV090x_READ_DEMOD(state, DSTATUS); +- if (STV090x_GETFIELD_Px(dstatus, CAR_LOCK_FIELD)) +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; +- +- reg = STV090x_READ_DEMOD(state, DMDSTATE); +- search_state = STV090x_GETFIELD_Px(reg, HEADER_MODE_FIELD); +- +- switch (search_state) { +- case 0: /* searching */ +- case 1: /* first PLH detected */ +- default: +- dprintk(FE_DEBUG, 1, "Status: Unlocked (Searching ..)"); +- break; +- +- case 2: /* DVB-S2 mode */ +- dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); +- if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { +- reg = STV090x_READ_DEMOD(state, PDELSTATUS1); +- if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { +- *status |= FE_HAS_VITERBI; +- reg = STV090x_READ_DEMOD(state, TSSTATUS); +- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- } +- } +- break; +- +- case 3: /* DVB-S1/legacy mode */ +- dprintk(FE_DEBUG, 1, "Delivery system: DVB-S"); +- if (STV090x_GETFIELD_Px(dstatus, LOCK_DEFINITIF_FIELD)) { +- reg = STV090x_READ_DEMOD(state, VSTATUSVIT); +- if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) { +- *status |= FE_HAS_VITERBI; +- reg = STV090x_READ_DEMOD(state, TSSTATUS); +- if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- } +- } +- break; +- } +- +- return 0; +-} +- +-static int stv090x_read_per(struct dvb_frontend *fe, u32 *per) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- +- s32 count_4, count_3, count_2, count_1, count_0, count; +- u32 reg, h, m, l; +- enum fe_status status; +- +- stv090x_read_status(fe, &status); +- if (!(status & FE_HAS_LOCK)) { +- *per = 1 << 23; /* Max PER */ +- } else { +- /* Counter 2 */ +- reg = STV090x_READ_DEMOD(state, ERRCNT22); +- h = STV090x_GETFIELD_Px(reg, ERR_CNT2_FIELD); +- +- reg = STV090x_READ_DEMOD(state, ERRCNT21); +- m = STV090x_GETFIELD_Px(reg, ERR_CNT21_FIELD); +- +- reg = STV090x_READ_DEMOD(state, ERRCNT20); +- l = STV090x_GETFIELD_Px(reg, ERR_CNT20_FIELD); +- +- *per = ((h << 16) | (m << 8) | l); +- +- count_4 = STV090x_READ_DEMOD(state, FBERCPT4); +- count_3 = STV090x_READ_DEMOD(state, FBERCPT3); +- count_2 = STV090x_READ_DEMOD(state, FBERCPT2); +- count_1 = STV090x_READ_DEMOD(state, FBERCPT1); +- count_0 = STV090x_READ_DEMOD(state, FBERCPT0); +- +- if ((!count_4) && (!count_3)) { +- count = (count_2 & 0xff) << 16; +- count |= (count_1 & 0xff) << 8; +- count |= count_0 & 0xff; +- } else { +- count = 1 << 24; +- } +- if (count == 0) +- *per = 1; +- } +- if (STV090x_WRITE_DEMOD(state, FBERCPT4, 0) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) +-{ +- int res = 0; +- int min = 0, med; +- +- if ((val >= tab[min].read && val < tab[max].read) || +- (val >= tab[max].read && val < tab[min].read)) { +- while ((max - min) > 1) { +- med = (max + min) / 2; +- if ((val >= tab[min].read && val < tab[med].read) || +- (val >= tab[med].read && val < tab[min].read)) +- max = med; +- else +- min = med; +- } +- res = ((val - tab[min].read) * +- (tab[max].real - tab[min].real) / +- (tab[max].read - tab[min].read)) + +- tab[min].real; +- } else { +- if (tab[min].read < tab[max].read) { +- if (val < tab[min].read) +- res = tab[min].real; +- else if (val >= tab[max].read) +- res = tab[max].real; +- } else { +- if (val >= tab[min].read) +- res = tab[min].real; +- else if (val < tab[max].read) +- res = tab[max].real; +- } +- } +- +- return res; +-} +- +-static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg; +- s32 agc_0, agc_1, agc; +- s32 str; +- +- reg = STV090x_READ_DEMOD(state, AGCIQIN1); +- agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); +- reg = STV090x_READ_DEMOD(state, AGCIQIN0); +- agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); +- agc = MAKEWORD16(agc_1, agc_0); +- +- str = stv090x_table_lookup(stv090x_rf_tab, +- ARRAY_SIZE(stv090x_rf_tab) - 1, agc); +- if (agc > stv090x_rf_tab[0].read) +- str = 0; +- else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read) +- str = -100; +- *strength = (str + 100) * 0xFFFF / 100; +- +- return 0; +-} +- +-static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg_0, reg_1, reg, i; +- s32 val_0, val_1, val = 0; +- u8 lock_f; +- s32 div; +- u32 last; +- +- switch (state->delsys) { +- case STV090x_DVBS2: +- reg = STV090x_READ_DEMOD(state, DSTATUS); +- lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); +- if (lock_f) { +- msleep(5); +- for (i = 0; i < 16; i++) { +- reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1); +- val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); +- reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0); +- val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD); +- val += MAKEWORD16(val_1, val_0); +- msleep(1); +- } +- val /= 16; +- last = ARRAY_SIZE(stv090x_s2cn_tab) - 1; +- div = stv090x_s2cn_tab[0].read - +- stv090x_s2cn_tab[last].read; +- *cnr = 0xFFFF - ((val * 0xFFFF) / div); +- } +- break; +- +- case STV090x_DVBS1: +- case STV090x_DSS: +- reg = STV090x_READ_DEMOD(state, DSTATUS); +- lock_f = STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD); +- if (lock_f) { +- msleep(5); +- for (i = 0; i < 16; i++) { +- reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1); +- val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); +- reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0); +- val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD); +- val += MAKEWORD16(val_1, val_0); +- msleep(1); +- } +- val /= 16; +- last = ARRAY_SIZE(stv090x_s1cn_tab) - 1; +- div = stv090x_s1cn_tab[0].read - +- stv090x_s1cn_tab[last].read; +- *cnr = 0xFFFF - ((val * 0xFFFF) / div); +- } +- break; +- default: +- break; +- } +- +- return 0; +-} +- +-static int stv090x_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg; +- +- reg = STV090x_READ_DEMOD(state, DISTXCTL); +- switch (tone) { +- case SEC_TONE_ON: +- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- break; +- +- case SEC_TONE_OFF: +- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, 0); +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +- +-static enum dvbfe_algo stv090x_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_CUSTOM; +-} +- +-static int stv090x_send_diseqc_msg(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg, idle = 0, fifo_full = 1; +- int i; +- +- reg = STV090x_READ_DEMOD(state, DISTXCTL); +- +- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, +- (state->config->diseqc_envelope_mode) ? 4 : 2); +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- for (i = 0; i < cmd->msg_len; i++) { +- +- while (fifo_full) { +- reg = STV090x_READ_DEMOD(state, DISTXSTATUS); +- fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); +- } +- +- if (STV090x_WRITE_DEMOD(state, DISTXDATA, cmd->msg[i]) < 0) +- goto err; +- } +- reg = STV090x_READ_DEMOD(state, DISTXCTL); +- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- i = 0; +- +- while ((!idle) && (i < 10)) { +- reg = STV090x_READ_DEMOD(state, DISTXSTATUS); +- idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); +- msleep(10); +- i++; +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg, idle = 0, fifo_full = 1; +- u8 mode, value; +- int i; +- +- reg = STV090x_READ_DEMOD(state, DISTXCTL); +- +- if (burst == SEC_MINI_A) { +- mode = (state->config->diseqc_envelope_mode) ? 5 : 3; +- value = 0x00; +- } else { +- mode = (state->config->diseqc_envelope_mode) ? 4 : 2; +- value = 0xFF; +- } +- +- STV090x_SETFIELD_Px(reg, DISTX_MODE_FIELD, mode); +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, DISEQC_RESET_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 1); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- while (fifo_full) { +- reg = STV090x_READ_DEMOD(state, DISTXSTATUS); +- fifo_full = STV090x_GETFIELD_Px(reg, FIFO_FULL_FIELD); +- } +- +- if (STV090x_WRITE_DEMOD(state, DISTXDATA, value) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, DISTXCTL); +- STV090x_SETFIELD_Px(reg, DIS_PRECHARGE_FIELD, 0); +- if (STV090x_WRITE_DEMOD(state, DISTXCTL, reg) < 0) +- goto err; +- +- i = 0; +- +- while ((!idle) && (i < 10)) { +- reg = STV090x_READ_DEMOD(state, DISTXSTATUS); +- idle = STV090x_GETFIELD_Px(reg, TX_IDLE_FIELD); +- msleep(10); +- i++; +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg = 0, i = 0, rx_end = 0; +- +- while ((rx_end != 1) && (i < 10)) { +- msleep(10); +- i++; +- reg = STV090x_READ_DEMOD(state, DISRX_ST0); +- rx_end = STV090x_GETFIELD_Px(reg, RX_END_FIELD); +- } +- +- if (rx_end) { +- reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD); +- for (i = 0; i < reply->msg_len; i++) +- reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA); +- } +- +- return 0; +-} +- +-static int stv090x_sleep(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg; +- u8 full_standby = 0; +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (state->config->tuner_sleep) { +- if (state->config->tuner_sleep(fe) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep", +- state->device == STV0900 ? "STV0900" : "STV0903", +- state->demod); +- +- mutex_lock(&state->internal->demod_lock); +- +- switch (state->demod) { +- case STV090x_DEMODULATOR_0: +- /* power off ADC 1 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR1); +- STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) +- goto err; +- /* power off DiSEqC 1 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR2); +- STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) +- goto err; +- +- /* check whether path 2 is already sleeping, that is when +- ADC2 is off */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR3); +- if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0) +- full_standby = 1; +- +- /* stop clocks */ +- reg = stv090x_read_reg(state, STV090x_STOPCLK1); +- /* packet delineator 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1); +- /* ADC 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1); +- /* FEC clock is shared between the two paths, only stop it +- when full standby is possible */ +- if (full_standby) +- STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- /* sampling 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); +- /* viterbi 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1); +- /* TS clock is shared between the two paths, only stop it +- when full standby is possible */ +- if (full_standby) +- STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- break; +- +- case STV090x_DEMODULATOR_1: +- /* power off ADC 2 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR3); +- STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) +- goto err; +- /* power off DiSEqC 2 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR4); +- STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) +- goto err; +- +- /* check whether path 1 is already sleeping, that is when +- ADC1 is off */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR1); +- if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0) +- full_standby = 1; +- +- /* stop clocks */ +- reg = stv090x_read_reg(state, STV090x_STOPCLK1); +- /* packet delineator 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1); +- /* ADC 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1); +- /* FEC clock is shared between the two paths, only stop it +- when full standby is possible */ +- if (full_standby) +- STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- /* sampling 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); +- /* viterbi 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1); +- /* TS clock is shared between the two paths, only stop it +- when full standby is possible */ +- if (full_standby) +- STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- break; +- +- default: +- dprintk(FE_ERROR, 1, "Wrong demodulator!"); +- break; +- } +- +- if (full_standby) { +- /* general power off */ +- reg = stv090x_read_reg(state, STV090x_SYNTCTRL); +- STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) +- goto err; +- } +- +- mutex_unlock(&state->internal->demod_lock); +- return 0; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- mutex_unlock(&state->internal->demod_lock); +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_wakeup(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u32 reg; +- +- dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby", +- state->device == STV0900 ? "STV0900" : "STV0903", +- state->demod); +- +- mutex_lock(&state->internal->demod_lock); +- +- /* general power on */ +- reg = stv090x_read_reg(state, STV090x_SYNTCTRL); +- STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) +- goto err; +- +- switch (state->demod) { +- case STV090x_DEMODULATOR_0: +- /* power on ADC 1 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR1); +- STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) +- goto err; +- /* power on DiSEqC 1 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR2); +- STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) +- goto err; +- +- /* activate clocks */ +- reg = stv090x_read_reg(state, STV090x_STOPCLK1); +- /* packet delineator 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0); +- /* ADC 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0); +- /* FEC clock */ +- STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- /* sampling 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0); +- /* viterbi 1 clock */ +- STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0); +- /* TS clock */ +- STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- break; +- +- case STV090x_DEMODULATOR_1: +- /* power on ADC 2 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR3); +- STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) +- goto err; +- /* power on DiSEqC 2 */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR4); +- STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) +- goto err; +- +- /* activate clocks */ +- reg = stv090x_read_reg(state, STV090x_STOPCLK1); +- /* packet delineator 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0); +- /* ADC 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0); +- /* FEC clock */ +- STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_STOPCLK2); +- /* sampling 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0); +- /* viterbi 2 clock */ +- STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0); +- /* TS clock */ +- STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) +- goto err; +- break; +- +- default: +- dprintk(FE_ERROR, 1, "Wrong demodulator!"); +- break; +- } +- +- mutex_unlock(&state->internal->demod_lock); +- return 0; +-err: +- mutex_unlock(&state->internal->demod_lock); +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static void stv090x_release(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- +- state->internal->num_used--; +- if (state->internal->num_used <= 0) { +- +- dprintk(FE_ERROR, 1, "Actually removing"); +- +- remove_dev(state->internal); +- kfree(state->internal); +- } +- +- kfree(state); +-} +- +-static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc_mode) +-{ +- u32 reg = 0; +- +- reg = stv090x_read_reg(state, STV090x_GENCFG); +- +- switch (ldpc_mode) { +- case STV090x_DUAL: +- default: +- if ((state->demod_mode != STV090x_DUAL) || (STV090x_GETFIELD(reg, DDEMOD_FIELD) != 1)) { +- /* set LDPC to dual mode */ +- if (stv090x_write_reg(state, STV090x_GENCFG, 0x1d) < 0) +- goto err; +- +- state->demod_mode = STV090x_DUAL; +- +- reg = stv090x_read_reg(state, STV090x_TSTRES0); +- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); +- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) +- goto err; +- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); +- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, MODCODLST0, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST1, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST2, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST3, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST4, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST5, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST6, 0xff) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, MODCODLST7, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST8, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLST9, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTA, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTB, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTC, 0xcc) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTD, 0xcc) < 0) +- goto err; +- +- if (STV090x_WRITE_DEMOD(state, MODCODLSTE, 0xff) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, MODCODLSTF, 0xcf) < 0) +- goto err; +- } +- break; +- +- case STV090x_SINGLE: +- if (stv090x_stop_modcod(state) < 0) +- goto err; +- if (stv090x_activate_modcod_single(state) < 0) +- goto err; +- +- if (state->demod == STV090x_DEMODULATOR_1) { +- if (stv090x_write_reg(state, STV090x_GENCFG, 0x06) < 0) /* path 2 */ +- goto err; +- } else { +- if (stv090x_write_reg(state, STV090x_GENCFG, 0x04) < 0) /* path 1 */ +- goto err; +- } +- +- reg = stv090x_read_reg(state, STV090x_TSTRES0); +- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x1); +- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) +- goto err; +- STV090x_SETFIELD(reg, FRESFEC_FIELD, 0x0); +- if (stv090x_write_reg(state, STV090x_TSTRES0, reg) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, PDELCTRL1); +- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x01); +- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, ALGOSWRST_FIELD, 0x00); +- if (STV090x_WRITE_DEMOD(state, PDELCTRL1, reg) < 0) +- goto err; +- break; +- } +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-/* return (Hz), clk in Hz*/ +-static u32 stv090x_get_mclk(struct stv090x_state *state) +-{ +- const struct stv090x_config *config = state->config; +- u32 div, reg; +- u8 ratio; +- +- div = stv090x_read_reg(state, STV090x_NCOARSE); +- reg = stv090x_read_reg(state, STV090x_SYNTCTRL); +- ratio = STV090x_GETFIELD(reg, SELX1RATIO_FIELD) ? 4 : 6; +- +- return (div + 1) * config->xtal / ratio; /* kHz */ +-} +- +-static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk) +-{ +- const struct stv090x_config *config = state->config; +- u32 reg, div, clk_sel; +- +- reg = stv090x_read_reg(state, STV090x_SYNTCTRL); +- clk_sel = ((STV090x_GETFIELD(reg, SELX1RATIO_FIELD) == 1) ? 4 : 6); +- +- div = ((clk_sel * mclk) / config->xtal) - 1; +- +- reg = stv090x_read_reg(state, STV090x_NCOARSE); +- STV090x_SETFIELD(reg, M_DIV_FIELD, div); +- if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0) +- goto err; +- +- state->internal->mclk = stv090x_get_mclk(state); +- +- /*Set the DiseqC frequency to 22KHz */ +- div = state->internal->mclk / 704000; +- if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0) +- goto err; +- if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_set_tspath(struct stv090x_state *state) +-{ +- u32 reg; +- +- if (state->internal->dev_ver >= 0x20) { +- switch (state->config->ts1_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- stv090x_write_reg(state, STV090x_TSGENERAL, 0x00); +- break; +- +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x06) < 0) /* Mux'd stream mode */ +- goto err; +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) +- goto err; +- break; +- } +- break; +- +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0a) < 0) +- goto err; +- break; +- } +- break; +- } +- } else { +- switch (state->config->ts1_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x10); +- break; +- +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x16); +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) +- goto err; +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 0); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, 0x14) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, 0x28) < 0) +- goto err; +- break; +- } +- break; +- +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- default: +- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x14); +- break; +- +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- stv090x_write_reg(state, STV090x_TSGENERAL1X, 0x12); +- break; +- } +- break; +- } +- } +- +- switch (state->config->ts1_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_DVBCI: +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- break; +- +- default: +- break; +- } +- +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_DVBCI: +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- break; +- +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); +- STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); +- STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); +- STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- break; +- +- default: +- break; +- } +- +- if (state->config->ts1_clk > 0) { +- u32 speed; +- +- switch (state->config->ts1_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- default: +- speed = state->internal->mclk / +- (state->config->ts1_clk / 4); +- if (speed < 0x08) +- speed = 0x08; +- if (speed > 0xFF) +- speed = 0xFF; +- break; +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- speed = state->internal->mclk / +- (state->config->ts1_clk / 32); +- if (speed < 0x20) +- speed = 0x20; +- if (speed > 0xFF) +- speed = 0xFF; +- break; +- } +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) +- goto err; +- } +- +- if (state->config->ts2_clk > 0) { +- u32 speed; +- +- switch (state->config->ts2_mode) { +- case STV090x_TSMODE_PARALLEL_PUNCTURED: +- case STV090x_TSMODE_DVBCI: +- default: +- speed = state->internal->mclk / +- (state->config->ts2_clk / 4); +- if (speed < 0x08) +- speed = 0x08; +- if (speed > 0xFF) +- speed = 0xFF; +- break; +- case STV090x_TSMODE_SERIAL_PUNCTURED: +- case STV090x_TSMODE_SERIAL_CONTINUOUS: +- speed = state->internal->mclk / +- (state->config->ts2_clk / 32); +- if (speed < 0x20) +- speed = 0x20; +- if (speed > 0xFF) +- speed = 0xFF; +- break; +- } +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); +- STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0) +- goto err; +- } +- +- reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) +- goto err; +- +- reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x00); +- if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_init(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- const struct stv090x_config *config = state->config; +- u32 reg; +- +- if (state->internal->mclk == 0) { +- /* call tuner init to configure the tuner's clock output +- divider directly before setting up the master clock of +- the stv090x. */ +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (config->tuner_init) { +- if (config->tuner_init(fe) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ +- msleep(5); +- if (stv090x_write_reg(state, STV090x_SYNTCTRL, +- 0x20 | config->clk_mode) < 0) +- goto err; +- stv090x_get_mclk(state); +- } +- +- if (stv090x_wakeup(fe) < 0) { +- dprintk(FE_ERROR, 1, "Error waking device"); +- goto err; +- } +- +- if (stv090x_ldpc_mode(state, state->demod_mode) < 0) +- goto err; +- +- reg = STV090x_READ_DEMOD(state, TNRCFG2); +- STV090x_SETFIELD_Px(reg, TUN_IQSWAP_FIELD, state->inversion); +- if (STV090x_WRITE_DEMOD(state, TNRCFG2, reg) < 0) +- goto err; +- reg = STV090x_READ_DEMOD(state, DEMOD); +- STV090x_SETFIELD_Px(reg, ROLLOFF_CONTROL_FIELD, state->rolloff); +- if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) +- goto err; +- +- if (stv090x_i2c_gate_ctrl(state, 1) < 0) +- goto err; +- +- if (config->tuner_set_mode) { +- if (config->tuner_set_mode(fe, TUNER_WAKE) < 0) +- goto err_gateoff; +- } +- +- if (config->tuner_init) { +- if (config->tuner_init(fe) < 0) +- goto err_gateoff; +- } +- +- if (stv090x_i2c_gate_ctrl(state, 0) < 0) +- goto err; +- +- if (stv090x_set_tspath(state) < 0) +- goto err; +- +- return 0; +- +-err_gateoff: +- stv090x_i2c_gate_ctrl(state, 0); +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-static int stv090x_setup(struct dvb_frontend *fe) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- const struct stv090x_config *config = state->config; +- const struct stv090x_reg *stv090x_initval = NULL; +- const struct stv090x_reg *stv090x_cut20_val = NULL; +- unsigned long t1_size = 0, t2_size = 0; +- u32 reg = 0; +- +- int i; +- +- if (state->device == STV0900) { +- dprintk(FE_DEBUG, 1, "Initializing STV0900"); +- stv090x_initval = stv0900_initval; +- t1_size = ARRAY_SIZE(stv0900_initval); +- stv090x_cut20_val = stv0900_cut20_val; +- t2_size = ARRAY_SIZE(stv0900_cut20_val); +- } else if (state->device == STV0903) { +- dprintk(FE_DEBUG, 1, "Initializing STV0903"); +- stv090x_initval = stv0903_initval; +- t1_size = ARRAY_SIZE(stv0903_initval); +- stv090x_cut20_val = stv0903_cut20_val; +- t2_size = ARRAY_SIZE(stv0903_cut20_val); +- } +- +- /* STV090x init */ +- +- /* Stop Demod */ +- if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) +- goto err; +- +- msleep(5); +- +- /* Set No Tuner Mode */ +- if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) +- goto err; +- +- /* I2C repeater OFF */ +- STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level); +- if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) +- goto err; +- +- if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */ +- goto err; +- msleep(5); +- if (stv090x_write_reg(state, STV090x_I2CCFG, 0x08) < 0) /* 1/41 oversampling */ +- goto err; +- if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) /* enable PLL */ +- goto err; +- msleep(5); +- +- /* write initval */ +- dprintk(FE_DEBUG, 1, "Setting up initial values"); +- for (i = 0; i < t1_size; i++) { +- if (stv090x_write_reg(state, stv090x_initval[i].addr, stv090x_initval[i].data) < 0) +- goto err; +- } +- +- state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID); +- if (state->internal->dev_ver >= 0x20) { +- if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) +- goto err; +- +- /* write cut20_val*/ +- dprintk(FE_DEBUG, 1, "Setting up Cut 2.0 initial values"); +- for (i = 0; i < t2_size; i++) { +- if (stv090x_write_reg(state, stv090x_cut20_val[i].addr, stv090x_cut20_val[i].data) < 0) +- goto err; +- } +- +- } else if (state->internal->dev_ver < 0x20) { +- dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!", +- state->internal->dev_ver); +- +- goto err; +- } else if (state->internal->dev_ver > 0x30) { +- /* we shouldn't bail out from here */ +- dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!", +- state->internal->dev_ver); +- } +- +- /* ADC1 range */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR1); +- STV090x_SETFIELD(reg, ADC1_INMODE_FIELD, +- (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) +- goto err; +- +- /* ADC2 range */ +- reg = stv090x_read_reg(state, STV090x_TSTTNR3); +- STV090x_SETFIELD(reg, ADC2_INMODE_FIELD, +- (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1); +- if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) +- goto err; +- +- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) +- goto err; +- if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) +- goto err; +- +- return 0; +-err: +- dprintk(FE_ERROR, 1, "I/O error"); +- return -1; +-} +- +-int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, +- u8 xor_value) +-{ +- struct stv090x_state *state = fe->demodulator_priv; +- u8 reg = 0; +- +- STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir); +- STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value); +- STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value); +- +- return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); +-} +-EXPORT_SYMBOL(stv090x_set_gpio); +- +-static struct dvb_frontend_ops stv090x_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2, SYS_DSS }, +- .info = { +- .name = "STV090x Multistandard", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 0, +- .frequency_tolerance = 0, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | +- FE_CAN_2G_MODULATION +- }, +- +- .release = stv090x_release, +- .init = stv090x_init, +- +- .sleep = stv090x_sleep, +- .get_frontend_algo = stv090x_frontend_algo, +- +- .diseqc_send_master_cmd = stv090x_send_diseqc_msg, +- .diseqc_send_burst = stv090x_send_diseqc_burst, +- .diseqc_recv_slave_reply = stv090x_recv_slave_reply, +- .set_tone = stv090x_set_tone, +- +- .search = stv090x_search, +- .read_status = stv090x_read_status, +- .read_ber = stv090x_read_per, +- .read_signal_strength = stv090x_read_signal_strength, +- .read_snr = stv090x_read_cnr, +-}; +- +- +-struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, +- struct i2c_adapter *i2c, +- enum stv090x_demodulator demod) +-{ +- struct stv090x_state *state = NULL; +- struct stv090x_dev *temp_int; +- +- state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->verbose = &verbose; +- state->config = config; +- state->i2c = i2c; +- state->frontend.ops = stv090x_ops; +- state->frontend.demodulator_priv = state; +- state->demod = demod; +- state->demod_mode = config->demod_mode; /* Single or Dual mode */ +- state->device = config->device; +- state->rolloff = STV090x_RO_35; /* default */ +- +- temp_int = find_dev(state->i2c, +- state->config->address); +- +- if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) { +- state->internal = temp_int->internal; +- state->internal->num_used++; +- dprintk(FE_INFO, 1, "Found Internal Structure!"); +- } else { +- state->internal = kmalloc(sizeof(struct stv090x_internal), +- GFP_KERNEL); +- if (!state->internal) +- goto error; +- temp_int = append_internal(state->internal); +- if (!temp_int) { +- kfree(state->internal); +- goto error; +- } +- state->internal->num_used = 1; +- state->internal->mclk = 0; +- state->internal->dev_ver = 0; +- state->internal->i2c_adap = state->i2c; +- state->internal->i2c_addr = state->config->address; +- dprintk(FE_INFO, 1, "Create New Internal Structure!"); +- +- mutex_init(&state->internal->demod_lock); +- mutex_init(&state->internal->tuner_lock); +- +- if (stv090x_setup(&state->frontend) < 0) { +- dprintk(FE_ERROR, 1, "Error setting up device"); +- goto err_remove; +- } +- } +- +- /* workaround for stuck DiSEqC output */ +- if (config->diseqc_envelope_mode) +- stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); +- +- dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", +- state->device == STV0900 ? "STV0900" : "STV0903", +- demod, +- state->internal->dev_ver); +- +- return &state->frontend; +- +-err_remove: +- remove_dev(state->internal); +- kfree(state->internal); +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(stv090x_attach); +-MODULE_PARM_DESC(verbose, "Set Verbosity level"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_DESCRIPTION("STV090x Multi-Std Broadcast frontend"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h +deleted file mode 100644 +index 29cdc2b..0000000 +--- a/drivers/media/dvb/frontends/stv090x.h ++++ /dev/null +@@ -1,134 +0,0 @@ +-/* +- STV0900/0903 Multistandard Broadcast Frontend driver +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV090x_H +-#define __STV090x_H +- +-enum stv090x_demodulator { +- STV090x_DEMODULATOR_0 = 1, +- STV090x_DEMODULATOR_1 +-}; +- +-enum stv090x_device { +- STV0903 = 0, +- STV0900, +-}; +- +-enum stv090x_mode { +- STV090x_DUAL = 0, +- STV090x_SINGLE +-}; +- +-enum stv090x_tsmode { +- STV090x_TSMODE_SERIAL_PUNCTURED = 1, +- STV090x_TSMODE_SERIAL_CONTINUOUS, +- STV090x_TSMODE_PARALLEL_PUNCTURED, +- STV090x_TSMODE_DVBCI +-}; +- +-enum stv090x_clkmode { +- STV090x_CLK_INT = 0, /* Clk i/p = CLKI */ +- STV090x_CLK_EXT = 2 /* Clk i/p = XTALI */ +-}; +- +-enum stv090x_i2crpt { +- STV090x_RPTLEVEL_256 = 0, +- STV090x_RPTLEVEL_128 = 1, +- STV090x_RPTLEVEL_64 = 2, +- STV090x_RPTLEVEL_32 = 3, +- STV090x_RPTLEVEL_16 = 4, +- STV090x_RPTLEVEL_8 = 5, +- STV090x_RPTLEVEL_4 = 6, +- STV090x_RPTLEVEL_2 = 7, +-}; +- +-enum stv090x_adc_range { +- STV090x_ADC_2Vpp = 0, +- STV090x_ADC_1Vpp = 1 +-}; +- +-struct stv090x_config { +- enum stv090x_device device; +- enum stv090x_mode demod_mode; +- enum stv090x_clkmode clk_mode; +- +- u32 xtal; /* default: 8000000 */ +- u8 address; /* default: 0x68 */ +- +- u8 ts1_mode; +- u8 ts2_mode; +- u32 ts1_clk; +- u32 ts2_clk; +- +- u8 ts1_tei : 1; +- u8 ts2_tei : 1; +- +- enum stv090x_i2crpt repeater_level; +- +- u8 tuner_bbgain; /* default: 10db */ +- enum stv090x_adc_range adc1_range; /* default: 2Vpp */ +- enum stv090x_adc_range adc2_range; /* default: 2Vpp */ +- +- bool diseqc_envelope_mode; +- +- int (*tuner_init) (struct dvb_frontend *fe); +- int (*tuner_sleep) (struct dvb_frontend *fe); +- int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); +- int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); +- int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); +- int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth); +- int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth); +- int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain); +- int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); +- int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); +- int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); +- void (*tuner_i2c_lock) (struct dvb_frontend *fe, int lock); +-}; +- +-#if defined(CONFIG_DVB_STV090x) || (defined(CONFIG_DVB_STV090x_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, +- struct i2c_adapter *i2c, +- enum stv090x_demodulator demod); +- +-/* dir = 0 -> output, dir = 1 -> input/open-drain */ +-extern int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, +- u8 dir, u8 value, u8 xor_value); +- +-#else +- +-static inline struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, +- struct i2c_adapter *i2c, +- enum stv090x_demodulator demod) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-static inline int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, +- u8 opd, u8 value, u8 xor_value) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return -ENODEV; +-} +-#endif /* CONFIG_DVB_STV090x */ +- +-#endif /* __STV090x_H */ +diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h +deleted file mode 100644 +index 5b780c8..0000000 +--- a/drivers/media/dvb/frontends/stv090x_priv.h ++++ /dev/null +@@ -1,279 +0,0 @@ +-/* +- STV0900/0903 Multistandard Broadcast Frontend driver +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV090x_PRIV_H +-#define __STV090x_PRIV_H +- +-#include "dvb_frontend.h" +- +-#define FE_ERROR 0 +-#define FE_NOTICE 1 +-#define FE_INFO 2 +-#define FE_DEBUG 3 +-#define FE_DEBUGREG 4 +- +-#define dprintk(__y, __z, format, arg...) do { \ +- if (__z) { \ +- if ((verbose > FE_ERROR) && (verbose > __y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_NOTICE) && (verbose > __y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_INFO) && (verbose > __y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_DEBUG) && (verbose > __y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (verbose > __y) \ +- printk(format, ##arg); \ +- } \ +-} while (0) +- +-#define STV090x_READ_DEMOD(__state, __reg) (( \ +- (__state)->demod == STV090x_DEMODULATOR_1) ? \ +- stv090x_read_reg(__state, STV090x_P2_##__reg) : \ +- stv090x_read_reg(__state, STV090x_P1_##__reg)) +- +-#define STV090x_WRITE_DEMOD(__state, __reg, __data) (( \ +- (__state)->demod == STV090x_DEMODULATOR_1) ? \ +- stv090x_write_reg(__state, STV090x_P2_##__reg, __data) :\ +- stv090x_write_reg(__state, STV090x_P1_##__reg, __data)) +- +-#define STV090x_ADDR_OFFST(__state, __x) (( \ +- (__state->demod) == STV090x_DEMODULATOR_1) ? \ +- STV090x_P1_##__x : \ +- STV090x_P2_##__x) +- +- +-#define STV090x_SETFIELD(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_##bitf) - 1) <<\ +- STV090x_OFFST_##bitf))) | \ +- (val << STV090x_OFFST_##bitf)) +- +-#define STV090x_GETFIELD(val, bitf) ((val >> STV090x_OFFST_##bitf) & ((1 << STV090x_WIDTH_##bitf) - 1)) +- +- +-#define STV090x_SETFIELD_Px(mask, bitf, val) (mask = (mask & (~(((1 << STV090x_WIDTH_Px_##bitf) - 1) <<\ +- STV090x_OFFST_Px_##bitf))) | \ +- (val << STV090x_OFFST_Px_##bitf)) +- +-#define STV090x_GETFIELD_Px(val, bitf) ((val >> STV090x_OFFST_Px_##bitf) & ((1 << STV090x_WIDTH_Px_##bitf) - 1)) +- +-#define MAKEWORD16(__a, __b) (((__a) << 8) | (__b)) +- +-#define MSB(__x) ((__x >> 8) & 0xff) +-#define LSB(__x) (__x & 0xff) +- +- +-#define STV090x_IQPOWER_THRESHOLD 30 +-#define STV090x_SEARCH_AGC2_TH_CUT20 700 +-#define STV090x_SEARCH_AGC2_TH_CUT30 1400 +- +-#define STV090x_SEARCH_AGC2_TH(__ver) \ +- ((__ver <= 0x20) ? \ +- STV090x_SEARCH_AGC2_TH_CUT20 : \ +- STV090x_SEARCH_AGC2_TH_CUT30) +- +-enum stv090x_signal_state { +- STV090x_NOAGC1, +- STV090x_NOCARRIER, +- STV090x_NODATA, +- STV090x_DATAOK, +- STV090x_RANGEOK, +- STV090x_OUTOFRANGE +-}; +- +-enum stv090x_fec { +- STV090x_PR12 = 0, +- STV090x_PR23, +- STV090x_PR34, +- STV090x_PR45, +- STV090x_PR56, +- STV090x_PR67, +- STV090x_PR78, +- STV090x_PR89, +- STV090x_PR910, +- STV090x_PRERR +-}; +- +-enum stv090x_modulation { +- STV090x_QPSK, +- STV090x_8PSK, +- STV090x_16APSK, +- STV090x_32APSK, +- STV090x_UNKNOWN +-}; +- +-enum stv090x_frame { +- STV090x_LONG_FRAME, +- STV090x_SHORT_FRAME +-}; +- +-enum stv090x_pilot { +- STV090x_PILOTS_OFF, +- STV090x_PILOTS_ON +-}; +- +-enum stv090x_rolloff { +- STV090x_RO_35, +- STV090x_RO_25, +- STV090x_RO_20 +-}; +- +-enum stv090x_inversion { +- STV090x_IQ_AUTO, +- STV090x_IQ_NORMAL, +- STV090x_IQ_SWAP +-}; +- +-enum stv090x_modcod { +- STV090x_DUMMY_PLF = 0, +- STV090x_QPSK_14, +- STV090x_QPSK_13, +- STV090x_QPSK_25, +- STV090x_QPSK_12, +- STV090x_QPSK_35, +- STV090x_QPSK_23, +- STV090x_QPSK_34, +- STV090x_QPSK_45, +- STV090x_QPSK_56, +- STV090x_QPSK_89, +- STV090x_QPSK_910, +- STV090x_8PSK_35, +- STV090x_8PSK_23, +- STV090x_8PSK_34, +- STV090x_8PSK_56, +- STV090x_8PSK_89, +- STV090x_8PSK_910, +- STV090x_16APSK_23, +- STV090x_16APSK_34, +- STV090x_16APSK_45, +- STV090x_16APSK_56, +- STV090x_16APSK_89, +- STV090x_16APSK_910, +- STV090x_32APSK_34, +- STV090x_32APSK_45, +- STV090x_32APSK_56, +- STV090x_32APSK_89, +- STV090x_32APSK_910, +- STV090x_MODCODE_UNKNOWN +-}; +- +-enum stv090x_search { +- STV090x_SEARCH_DSS = 0, +- STV090x_SEARCH_DVBS1, +- STV090x_SEARCH_DVBS2, +- STV090x_SEARCH_AUTO +-}; +- +-enum stv090x_algo { +- STV090x_BLIND_SEARCH, +- STV090x_COLD_SEARCH, +- STV090x_WARM_SEARCH +-}; +- +-enum stv090x_delsys { +- STV090x_ERROR = 0, +- STV090x_DVBS1 = 1, +- STV090x_DVBS2, +- STV090x_DSS +-}; +- +-struct stv090x_long_frame_crloop { +- enum stv090x_modcod modcod; +- +- u8 crl_pilots_on_2; +- u8 crl_pilots_off_2; +- u8 crl_pilots_on_5; +- u8 crl_pilots_off_5; +- u8 crl_pilots_on_10; +- u8 crl_pilots_off_10; +- u8 crl_pilots_on_20; +- u8 crl_pilots_off_20; +- u8 crl_pilots_on_30; +- u8 crl_pilots_off_30; +-}; +- +-struct stv090x_short_frame_crloop { +- enum stv090x_modulation modulation; +- +- u8 crl_2; /* SR < 3M */ +- u8 crl_5; /* 3 < SR <= 7M */ +- u8 crl_10; /* 7 < SR <= 15M */ +- u8 crl_20; /* 10 < SR <= 25M */ +- u8 crl_30; /* 10 < SR <= 45M */ +-}; +- +-struct stv090x_reg { +- u16 addr; +- u8 data; +-}; +- +-struct stv090x_tab { +- s32 real; +- s32 read; +-}; +- +-struct stv090x_internal { +- struct i2c_adapter *i2c_adap; +- u8 i2c_addr; +- +- struct mutex demod_lock; /* Lock access to shared register */ +- struct mutex tuner_lock; /* Lock access to tuners */ +- s32 mclk; /* Masterclock Divider factor */ +- u32 dev_ver; +- +- int num_used; +-}; +- +-struct stv090x_state { +- enum stv090x_device device; +- enum stv090x_demodulator demod; +- enum stv090x_mode demod_mode; +- struct stv090x_internal *internal; +- +- struct i2c_adapter *i2c; +- const struct stv090x_config *config; +- struct dvb_frontend frontend; +- +- u32 *verbose; /* Cached module verbosity */ +- +- enum stv090x_delsys delsys; +- enum stv090x_fec fec; +- enum stv090x_modulation modulation; +- enum stv090x_modcod modcod; +- enum stv090x_search search_mode; +- enum stv090x_frame frame_len; +- enum stv090x_pilot pilots; +- enum stv090x_rolloff rolloff; +- enum stv090x_inversion inversion; +- enum stv090x_algo algo; +- +- u32 frequency; +- u32 srate; +- +- s32 tuner_bw; +- +- s32 search_range; +- +- s32 DemodTimeout; +- s32 FecTimeout; +-}; +- +-#endif /* __STV090x_PRIV_H */ +diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h +deleted file mode 100644 +index 93741ee..0000000 +--- a/drivers/media/dvb/frontends/stv090x_reg.h ++++ /dev/null +@@ -1,2371 +0,0 @@ +-/* +- STV0900/0903 Multistandard Broadcast Frontend driver +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV090x_REG_H +-#define __STV090x_REG_H +- +-#define STV090x_MID 0xf100 +-#define STV090x_OFFST_MCHIP_IDENT_FIELD 4 +-#define STV090x_WIDTH_MCHIP_IDENT_FIELD 4 +-#define STV090x_OFFST_MRELEASE_FIELD 0 +-#define STV090x_WIDTH_MRELEASE_FIELD 4 +- +-#define STV090x_DACR1 0xf113 +-#define STV090x_OFFST_DACR1_MODE_FIELD 5 +-#define STV090x_WIDTH_DACR1_MODE_FIELD 3 +-#define STV090x_OFFST_DACR1_VALUE_FIELD 0 +-#define STV090x_WIDTH_DACR1_VALUE_FIELD 4 +- +-#define STV090x_DACR2 0xf114 +-#define STV090x_OFFST_DACR2_VALUE_FIELD 0 +-#define STV090x_WIDTH_DACR2_VALUE_FIELD 8 +- +-#define STV090x_OUTCFG 0xf11c +-#define STV090x_OFFST_OUTSERRS1_HZ_FIELD 6 +-#define STV090x_WIDTH_OUTSERRS1_HZ_FIELD 1 +-#define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5 +-#define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1 +-#define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4 +-#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1 +-#define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3 +-#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 +- +-#define STV090x_MODECFG 0xf11d +- +-#define STV090x_IRQSTATUS3 0xf120 +-#define STV090x_OFFST_SPLL_LOCK_FIELD 5 +-#define STV090x_WIDTH_SPLL_LOCK_FIELD 1 +-#define STV090x_OFFST_SSTREAM_LCK_3_FIELD 4 +-#define STV090x_WIDTH_SSTREAM_LCK_3_FIELD 1 +-#define STV090x_OFFST_SSTREAM_LCK_2_FIELD 3 +-#define STV090x_WIDTH_SSTREAM_LCK_2_FIELD 1 +-#define STV090x_OFFST_SSTREAM_LCK_1_FIELD 2 +-#define STV090x_WIDTH_SSTREAM_LCK_1_FIELD 1 +-#define STV090x_OFFST_SDVBS1_PRF_2_FIELD 1 +-#define STV090x_WIDTH_SDVBS1_PRF_2_FIELD 1 +-#define STV090x_OFFST_SDVBS1_PRF_1_FIELD 0 +-#define STV090x_WIDTH_SDVBS1_PRF_1_FIELD 1 +- +-#define STV090x_IRQSTATUS2 0xf121 +-#define STV090x_OFFST_SSPY_ENDSIM_3_FIELD 7 +-#define STV090x_WIDTH_SSPY_ENDSIM_3_FIELD 1 +-#define STV090x_OFFST_SSPY_ENDSIM_2_FIELD 6 +-#define STV090x_WIDTH_SSPY_ENDSIM_2_FIELD 1 +-#define STV090x_OFFST_SSPY_ENDSIM_1_FIELD 5 +-#define STV090x_WIDTH_SSPY_ENDSIM_1_FIELD 1 +-#define STV090x_OFFST_SPKTDEL_ERROR_2_FIELD 4 +-#define STV090x_WIDTH_SPKTDEL_ERROR_2_FIELD 1 +-#define STV090x_OFFST_SPKTDEL_LOCKB_2_FIELD 3 +-#define STV090x_WIDTH_SPKTDEL_LOCKB_2_FIELD 1 +-#define STV090x_OFFST_SPKTDEL_LOCK_2_FIELD 2 +-#define STV090x_WIDTH_SPKTDEL_LOCK_2_FIELD 1 +-#define STV090x_OFFST_SPKTDEL_ERROR_1_FIELD 1 +-#define STV090x_WIDTH_SPKTDEL_ERROR_1_FIELD 1 +-#define STV090x_OFFST_SPKTDEL_LOCKB_1_FIELD 0 +-#define STV090x_WIDTH_SPKTDEL_LOCKB_1_FIELD 1 +- +-#define STV090x_IRQSTATUS1 0xf122 +-#define STV090x_OFFST_SPKTDEL_LOCK_1_FIELD 7 +-#define STV090x_WIDTH_SPKTDEL_LOCK_1_FIELD 1 +-#define STV090x_OFFST_SDEMOD_LOCKB_2_FIELD 2 +-#define STV090x_WIDTH_SDEMOD_LOCKB_2_FIELD 1 +-#define STV090x_OFFST_SDEMOD_LOCK_2_FIELD 1 +-#define STV090x_WIDTH_SDEMOD_LOCK_2_FIELD 1 +-#define STV090x_OFFST_SDEMOD_IRQ_2_FIELD 0 +-#define STV090x_WIDTH_SDEMOD_IRQ_2_FIELD 1 +- +-#define STV090x_IRQSTATUS0 0xf123 +-#define STV090x_OFFST_SDEMOD_LOCKB_1_FIELD 7 +-#define STV090x_WIDTH_SDEMOD_LOCKB_1_FIELD 1 +-#define STV090x_OFFST_SDEMOD_LOCK_1_FIELD 6 +-#define STV090x_WIDTH_SDEMOD_LOCK_1_FIELD 1 +-#define STV090x_OFFST_SDEMOD_IRQ_1_FIELD 5 +-#define STV090x_WIDTH_SDEMOD_IRQ_1_FIELD 1 +-#define STV090x_OFFST_SBCH_ERRFLAG_FIELD 4 +-#define STV090x_WIDTH_SBCH_ERRFLAG_FIELD 1 +-#define STV090x_OFFST_SDISEQC2RX_IRQ_FIELD 3 +-#define STV090x_WIDTH_SDISEQC2RX_IRQ_FIELD 1 +-#define STV090x_OFFST_SDISEQC2TX_IRQ_FIELD 2 +-#define STV090x_WIDTH_SDISEQC2TX_IRQ_FIELD 1 +-#define STV090x_OFFST_SDISEQC1RX_IRQ_FIELD 1 +-#define STV090x_WIDTH_SDISEQC1RX_IRQ_FIELD 1 +-#define STV090x_OFFST_SDISEQC1TX_IRQ_FIELD 0 +-#define STV090x_WIDTH_SDISEQC1TX_IRQ_FIELD 1 +- +-#define STV090x_IRQMASK3 0xf124 +-#define STV090x_OFFST_MPLL_LOCK_FIELD 5 +-#define STV090x_WIDTH_MPLL_LOCK_FIELD 1 +-#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4 +-#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1 +-#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3 +-#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1 +-#define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2 +-#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1 +-#define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1 +-#define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1 +-#define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0 +-#define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1 +- +-#define STV090x_IRQMASK2 0xf125 +-#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7 +-#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1 +-#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6 +-#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1 +-#define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5 +-#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1 +-#define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4 +-#define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1 +-#define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3 +-#define STV090x_WIDTH_MPKTDEL_LOCKB_2_FIELD 1 +-#define STV090x_OFFST_MPKTDEL_LOCK_2_FIELD 2 +-#define STV090x_WIDTH_MPKTDEL_LOCK_2_FIELD 1 +-#define STV090x_OFFST_MPKTDEL_ERROR_1_FIELD 1 +-#define STV090x_WIDTH_MPKTDEL_ERROR_1_FIELD 1 +-#define STV090x_OFFST_MPKTDEL_LOCKB_1_FIELD 0 +-#define STV090x_WIDTH_MPKTDEL_LOCKB_1_FIELD 1 +- +-#define STV090x_IRQMASK1 0xf126 +-#define STV090x_OFFST_MPKTDEL_LOCK_1_FIELD 7 +-#define STV090x_WIDTH_MPKTDEL_LOCK_1_FIELD 1 +-#define STV090x_OFFST_MEXTPINB2_FIELD 6 +-#define STV090x_WIDTH_MEXTPINB2_FIELD 1 +-#define STV090x_OFFST_MEXTPIN2_FIELD 5 +-#define STV090x_WIDTH_MEXTPIN2_FIELD 1 +-#define STV090x_OFFST_MEXTPINB1_FIELD 4 +-#define STV090x_WIDTH_MEXTPINB1_FIELD 1 +-#define STV090x_OFFST_MEXTPIN1_FIELD 3 +-#define STV090x_WIDTH_MEXTPIN1_FIELD 1 +-#define STV090x_OFFST_MDEMOD_LOCKB_2_FIELD 2 +-#define STV090x_WIDTH_MDEMOD_LOCKB_2_FIELD 1 +-#define STV090x_OFFST_MDEMOD_LOCK_2_FIELD 1 +-#define STV090x_WIDTH_MDEMOD_LOCK_2_FIELD 1 +-#define STV090x_OFFST_MDEMOD_IRQ_2_FIELD 0 +-#define STV090x_WIDTH_MDEMOD_IRQ_2_FIELD 1 +- +-#define STV090x_IRQMASK0 0xf127 +-#define STV090x_OFFST_MDEMOD_LOCKB_1_FIELD 7 +-#define STV090x_WIDTH_MDEMOD_LOCKB_1_FIELD 1 +-#define STV090x_OFFST_MDEMOD_LOCK_1_FIELD 6 +-#define STV090x_WIDTH_MDEMOD_LOCK_1_FIELD 1 +-#define STV090x_OFFST_MDEMOD_IRQ_1_FIELD 5 +-#define STV090x_WIDTH_MDEMOD_IRQ_1_FIELD 1 +-#define STV090x_OFFST_MBCH_ERRFLAG_FIELD 4 +-#define STV090x_WIDTH_MBCH_ERRFLAG_FIELD 1 +-#define STV090x_OFFST_MDISEQC2RX_IRQ_FIELD 3 +-#define STV090x_WIDTH_MDISEQC2RX_IRQ_FIELD 1 +-#define STV090x_OFFST_MDISEQC2TX_IRQ_FIELD 2 +-#define STV090x_WIDTH_MDISEQC2TX_IRQ_FIELD 1 +-#define STV090x_OFFST_MDISEQC1RX_IRQ_FIELD 1 +-#define STV090x_WIDTH_MDISEQC1RX_IRQ_FIELD 1 +-#define STV090x_OFFST_MDISEQC1TX_IRQ_FIELD 0 +-#define STV090x_WIDTH_MDISEQC1TX_IRQ_FIELD 1 +- +-#define STV090x_I2CCFG 0xf129 +-#define STV090x_OFFST_12C_FASTMODE_FIELD 3 +-#define STV090x_WIDTH_12C_FASTMODE_FIELD 1 +-#define STV090x_OFFST_12CADDR_INC_FIELD 0 +-#define STV090x_WIDTH_12CADDR_INC_FIELD 2 +- +-#define STV090x_Px_I2CRPT(__x) (0xf12a + (__x - 1) * 0x1) +-#define STV090x_P1_I2CRPT STV090x_Px_I2CRPT(1) +-#define STV090x_P2_I2CRPT STV090x_Px_I2CRPT(2) +-#define STV090x_OFFST_Px_I2CT_ON_FIELD 7 +-#define STV090x_WIDTH_Px_I2CT_ON_FIELD 1 +-#define STV090x_OFFST_Px_ENARPT_LEVEL_FIELD 4 +-#define STV090x_WIDTH_Px_ENARPT_LEVEL_FIELD 3 +-#define STV090x_OFFST_Px_SCLT_DELAY_FIELD 3 +-#define STV090x_WIDTH_Px_SCLT_DELAY_FIELD 1 +-#define STV090x_OFFST_Px_STOP_ENABLE_FIELD 2 +-#define STV090x_WIDTH_Px_STOP_ENABLE_FIELD 1 +-#define STV090x_OFFST_Px_STOP_SDAT2SDA_FIELD 1 +-#define STV090x_WIDTH_Px_STOP_SDAT2SDA_FIELD 1 +- +-#define STV090x_CLKI2CFG 0xf140 +-#define STV090x_OFFST_CLKI2_OPD_FIELD 7 +-#define STV090x_WIDTH_CLKI2_OPD_FIELD 1 +-#define STV090x_OFFST_CLKI2_CONFIG_FIELD 1 +-#define STV090x_WIDTH_CLKI2_CONFIG_FIELD 6 +-#define STV090x_OFFST_CLKI2_XOR_FIELD 0 +-#define STV090x_WIDTH_CLKI2_XOR_FIELD 1 +- +-#define STV090x_GPIOxCFG(__x) (0xf141 + (__x - 1)) +-#define STV090x_GPIO1CFG STV090x_GPIOxCFG(1) +-#define STV090x_GPIO2CFG STV090x_GPIOxCFG(2) +-#define STV090x_GPIO3CFG STV090x_GPIOxCFG(3) +-#define STV090x_GPIO4CFG STV090x_GPIOxCFG(4) +-#define STV090x_GPIO5CFG STV090x_GPIOxCFG(5) +-#define STV090x_GPIO6CFG STV090x_GPIOxCFG(6) +-#define STV090x_GPIO7CFG STV090x_GPIOxCFG(7) +-#define STV090x_GPIO8CFG STV090x_GPIOxCFG(8) +-#define STV090x_GPIO9CFG STV090x_GPIOxCFG(9) +-#define STV090x_GPIO10CFG STV090x_GPIOxCFG(10) +-#define STV090x_GPIO11CFG STV090x_GPIOxCFG(11) +-#define STV090x_GPIO12CFG STV090x_GPIOxCFG(12) +-#define STV090x_GPIO13CFG STV090x_GPIOxCFG(13) +-#define STV090x_OFFST_GPIOx_OPD_FIELD 7 +-#define STV090x_WIDTH_GPIOx_OPD_FIELD 1 +-#define STV090x_OFFST_GPIOx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_GPIOx_CONFIG_FIELD 6 +-#define STV090x_OFFST_GPIOx_XOR_FIELD 0 +-#define STV090x_WIDTH_GPIOx_XOR_FIELD 1 +- +-#define STV090x_CSxCFG(__x) (0xf14e + __x * 0x1) +-#define STV090x_CS0CFG STV090x_CSxCFG(0) +-#define STV090x_CS1CFG STV090x_CSxCFG(1) +-#define STV090x_OFFST_CSX_OPD_FIELD 7 +-#define STV090x_WIDTH_CSX_OPD_FIELD 1 +-#define STV090x_OFFST_CSX_CONFIG_FIELD 1 +-#define STV090x_WIDTH_CSX_CONFIG_FIELD 6 +-#define STV090x_OFFST_CSX_XOR_FIELD 0 +-#define STV090x_WIDTH_CSX_XOR_FIELD 1 +- +- +-#define STV090x_STDBYCFG 0xf150 +-#define STV090x_OFFST_STDBY_OPD_FIELD 7 +-#define STV090x_WIDTH_STDBY_OPD_FIELD 1 +-#define STV090x_OFFST_STDBY_CONFIG_FIELD 1 +-#define STV090x_WIDTH_STDBY_CONFIG_FIELD 6 +-#define STV090x_OFFST_STDBY_XOR_FIELD 0 +-#define STV090x_WIDTH_STDBY_XOR_FIELD 1 +- +-#define STV090x_DIRCLKCFG 0xf151 +-#define STV090x_OFFST_DIRCLK_OPD_FIELD 7 +-#define STV090x_WIDTH_DIRCLK_OPD_FIELD 1 +-#define STV090x_OFFST_DIRCLK_CONFIG_FIELD 1 +-#define STV090x_WIDTH_DIRCLK_CONFIG_FIELD 6 +-#define STV090x_OFFST_DIRCLK_XOR_FIELD 0 +-#define STV090x_WIDTH_DIRCLK_XOR_FIELD 1 +- +- +-#define STV090x_AGCRFxCFG(__x) (0xf152 + (__x - 1) * 0x4) +-#define STV090x_AGCRF1CFG STV090x_AGCRFxCFG(1) +-#define STV090x_AGCRF2CFG STV090x_AGCRFxCFG(2) +-#define STV090x_OFFST_AGCRFx_OPD_FIELD 7 +-#define STV090x_WIDTH_AGCRFx_OPD_FIELD 1 +-#define STV090x_OFFST_AGCRFx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_AGCRFx_CONFIG_FIELD 6 +-#define STV090x_OFFST_AGCRFx_XOR_FIELD 0 +-#define STV090x_WIDTH_AGCRFx_XOR_FIELD 1 +- +-#define STV090x_SDATxCFG(__x) (0xf153 + (__x - 1) * 0x4) +-#define STV090x_SDAT1CFG STV090x_SDATxCFG(1) +-#define STV090x_SDAT2CFG STV090x_SDATxCFG(2) +-#define STV090x_OFFST_SDATx_OPD_FIELD 7 +-#define STV090x_WIDTH_SDATx_OPD_FIELD 1 +-#define STV090x_OFFST_SDATx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_SDATx_CONFIG_FIELD 6 +-#define STV090x_OFFST_SDATx_XOR_FIELD 0 +-#define STV090x_WIDTH_SDATx_XOR_FIELD 1 +- +-#define STV090x_SCLTxCFG(__x) (0xf154 + (__x - 1) * 0x4) +-#define STV090x_SCLT1CFG STV090x_SCLTxCFG(1) +-#define STV090x_SCLT2CFG STV090x_SCLTxCFG(2) +-#define STV090x_OFFST_SCLTx_OPD_FIELD 7 +-#define STV090x_WIDTH_SCLTx_OPD_FIELD 1 +-#define STV090x_OFFST_SCLTx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_SCLTx_CONFIG_FIELD 6 +-#define STV090x_OFFST_SCLTx_XOR_FIELD 0 +-#define STV090x_WIDTH_SCLTx_XOR_FIELD 1 +- +-#define STV090x_DISEQCOxCFG(__x) (0xf155 + (__x - 1) * 0x4) +-#define STV090x_DISEQCO1CFG STV090x_DISEQCOxCFG(1) +-#define STV090x_DISEQCO2CFG STV090x_DISEQCOxCFG(2) +-#define STV090x_OFFST_DISEQCOx_OPD_FIELD 7 +-#define STV090x_WIDTH_DISEQCOx_OPD_FIELD 1 +-#define STV090x_OFFST_DISEQCOx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_DISEQCOx_CONFIG_FIELD 6 +-#define STV090x_OFFST_DISEQCOx_XOR_FIELD 0 +-#define STV090x_WIDTH_DISEQCOx_XOR_FIELD 1 +- +-#define STV090x_CLKOUT27CFG 0xf15a +-#define STV090x_OFFST_CLKOUT27_OPD_FIELD 7 +-#define STV090x_WIDTH_CLKOUT27_OPD_FIELD 1 +-#define STV090x_OFFST_CLKOUT27_CONFIG_FIELD 1 +-#define STV090x_WIDTH_CLKOUT27_CONFIG_FIELD 6 +-#define STV090x_OFFST_CLKOUT27_XOR_FIELD 0 +-#define STV090x_WIDTH_CLKOUT27_XOR_FIELD 1 +- +-#define STV090x_ERRORxCFG(__x) (0xf15b + (__x - 1) * 0x5) +-#define STV090x_ERROR1CFG STV090x_ERRORxCFG(1) +-#define STV090x_ERROR2CFG STV090x_ERRORxCFG(2) +-#define STV090x_ERROR3CFG STV090x_ERRORxCFG(3) +-#define STV090x_OFFST_ERRORx_OPD_FIELD 7 +-#define STV090x_WIDTH_ERRORx_OPD_FIELD 1 +-#define STV090x_OFFST_ERRORx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_ERRORx_CONFIG_FIELD 6 +-#define STV090x_OFFST_ERRORx_XOR_FIELD 0 +-#define STV090x_WIDTH_ERRORx_XOR_FIELD 1 +- +-#define STV090x_DPNxCFG(__x) (0xf15c + (__x - 1) * 0x5) +-#define STV090x_DPN1CFG STV090x_DPNxCFG(1) +-#define STV090x_DPN2CFG STV090x_DPNxCFG(2) +-#define STV090x_DPN3CFG STV090x_DPNxCFG(3) +-#define STV090x_OFFST_DPNx_OPD_FIELD 7 +-#define STV090x_WIDTH_DPNx_OPD_FIELD 1 +-#define STV090x_OFFST_DPNx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_DPNx_CONFIG_FIELD 6 +-#define STV090x_OFFST_DPNx_XOR_FIELD 0 +-#define STV090x_WIDTH_DPNx_XOR_FIELD 1 +- +-#define STV090x_STROUTxCFG(__x) (0xf15d + (__x - 1) * 0x5) +-#define STV090x_STROUT1CFG STV090x_STROUTxCFG(1) +-#define STV090x_STROUT2CFG STV090x_STROUTxCFG(2) +-#define STV090x_STROUT3CFG STV090x_STROUTxCFG(3) +-#define STV090x_OFFST_STROUTx_OPD_FIELD 7 +-#define STV090x_WIDTH_STROUTx_OPD_FIELD 1 +-#define STV090x_OFFST_STROUTx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_STROUTx_CONFIG_FIELD 6 +-#define STV090x_OFFST_STROUTx_XOR_FIELD 0 +-#define STV090x_WIDTH_STROUTx_XOR_FIELD 1 +- +-#define STV090x_CLKOUTxCFG(__x) (0xf15e + (__x - 1) * 0x5) +-#define STV090x_CLKOUT1CFG STV090x_CLKOUTxCFG(1) +-#define STV090x_CLKOUT2CFG STV090x_CLKOUTxCFG(2) +-#define STV090x_CLKOUT3CFG STV090x_CLKOUTxCFG(3) +-#define STV090x_OFFST_CLKOUTx_OPD_FIELD 7 +-#define STV090x_WIDTH_CLKOUTx_OPD_FIELD 1 +-#define STV090x_OFFST_CLKOUTx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_CLKOUTx_CONFIG_FIELD 6 +-#define STV090x_OFFST_CLKOUTx_XOR_FIELD 0 +-#define STV090x_WIDTH_CLKOUTx_XOR_FIELD 1 +- +-#define STV090x_DATAxCFG(__x) (0xf15f + (__x - 71) * 0x5) +-#define STV090x_DATA71CFG STV090x_DATAxCFG(71) +-#define STV090x_DATA72CFG STV090x_DATAxCFG(72) +-#define STV090x_DATA73CFG STV090x_DATAxCFG(73) +-#define STV090x_OFFST_DATAx_OPD_FIELD 7 +-#define STV090x_WIDTH_DATAx_OPD_FIELD 1 +-#define STV090x_OFFST_DATAx_CONFIG_FIELD 1 +-#define STV090x_WIDTH_DATAx_CONFIG_FIELD 6 +-#define STV090x_OFFST_DATAx_XOR_FIELD 0 +-#define STV090x_WIDTH_DATAx_XOR_FIELD 1 +- +-#define STV090x_NCOARSE 0xf1b3 +-#define STV090x_OFFST_M_DIV_FIELD 0 +-#define STV090x_WIDTH_M_DIV_FIELD 8 +- +-#define STV090x_SYNTCTRL 0xf1b6 +-#define STV090x_OFFST_STANDBY_FIELD 7 +-#define STV090x_WIDTH_STANDBY_FIELD 1 +-#define STV090x_OFFST_BYPASSPLLCORE_FIELD 6 +-#define STV090x_WIDTH_BYPASSPLLCORE_FIELD 1 +-#define STV090x_OFFST_SELX1RATIO_FIELD 5 +-#define STV090x_WIDTH_SELX1RATIO_FIELD 1 +-#define STV090x_OFFST_STOP_PLL_FIELD 3 +-#define STV090x_WIDTH_STOP_PLL_FIELD 1 +-#define STV090x_OFFST_BYPASSPLLFSK_FIELD 2 +-#define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1 +-#define STV090x_OFFST_SELOSCI_FIELD 1 +-#define STV090x_WIDTH_SELOSCI_FIELD 1 +-#define STV090x_OFFST_BYPASSPLLADC_FIELD 0 +-#define STV090x_WIDTH_BYPASSPLLADC_FIELD 1 +- +-#define STV090x_FILTCTRL 0xf1b7 +-#define STV090x_OFFST_INV_CLK135_FIELD 7 +-#define STV090x_WIDTH_INV_CLK135_FIELD 1 +-#define STV090x_OFFST_SEL_FSKCKDIV_FIELD 2 +-#define STV090x_WIDTH_SEL_FSKCKDIV_FIELD 1 +-#define STV090x_OFFST_INV_CLKFSK_FIELD 1 +-#define STV090x_WIDTH_INV_CLKFSK_FIELD 1 +-#define STV090x_OFFST_BYPASS_APPLI_FIELD 0 +-#define STV090x_WIDTH_BYPASS_APPLI_FIELD 1 +- +-#define STV090x_PLLSTAT 0xf1b8 +-#define STV090x_OFFST_PLLLOCK_FIELD 0 +-#define STV090x_WIDTH_PLLLOCK_FIELD 1 +- +-#define STV090x_STOPCLK1 0xf1c2 +-#define STV090x_OFFST_STOP_CLKPKDT2_FIELD 6 +-#define STV090x_WIDTH_STOP_CLKPKDT2_FIELD 1 +-#define STV090x_OFFST_STOP_CLKPKDT1_FIELD 5 +-#define STV090x_WIDTH_STOP_CLKPKDT1_FIELD 1 +-#define STV090x_OFFST_STOP_CLKFEC_FIELD 4 +-#define STV090x_WIDTH_STOP_CLKFEC_FIELD 1 +-#define STV090x_OFFST_STOP_CLKADCI2_FIELD 3 +-#define STV090x_WIDTH_STOP_CLKADCI2_FIELD 1 +-#define STV090x_OFFST_INV_CLKADCI2_FIELD 2 +-#define STV090x_WIDTH_INV_CLKADCI2_FIELD 1 +-#define STV090x_OFFST_STOP_CLKADCI1_FIELD 1 +-#define STV090x_WIDTH_STOP_CLKADCI1_FIELD 1 +-#define STV090x_OFFST_INV_CLKADCI1_FIELD 0 +-#define STV090x_WIDTH_INV_CLKADCI1_FIELD 1 +- +-#define STV090x_STOPCLK2 0xf1c3 +-#define STV090x_OFFST_STOP_CLKSAMP2_FIELD 4 +-#define STV090x_WIDTH_STOP_CLKSAMP2_FIELD 1 +-#define STV090x_OFFST_STOP_CLKSAMP1_FIELD 3 +-#define STV090x_WIDTH_STOP_CLKSAMP1_FIELD 1 +-#define STV090x_OFFST_STOP_CLKVIT2_FIELD 2 +-#define STV090x_WIDTH_STOP_CLKVIT2_FIELD 1 +-#define STV090x_OFFST_STOP_CLKVIT1_FIELD 1 +-#define STV090x_WIDTH_STOP_CLKVIT1_FIELD 1 +-#define STV090x_OFFST_STOP_CLKTS_FIELD 0 +-#define STV090x_WIDTH_STOP_CLKTS_FIELD 1 +- +-#define STV090x_TSTTNR0 0xf1df +-#define STV090x_OFFST_SEL_FSK_FIELD 7 +-#define STV090x_WIDTH_SEL_FSK_FIELD 1 +-#define STV090x_OFFST_FSK_PON_FIELD 2 +-#define STV090x_WIDTH_FSK_PON_FIELD 1 +- +-#define STV090x_TSTTNR1 0xf1e0 +-#define STV090x_OFFST_ADC1_PON_FIELD 1 +-#define STV090x_WIDTH_ADC1_PON_FIELD 1 +-#define STV090x_OFFST_ADC1_INMODE_FIELD 0 +-#define STV090x_WIDTH_ADC1_INMODE_FIELD 1 +- +-#define STV090x_TSTTNR2 0xf1e1 +-#define STV090x_OFFST_DISEQC1_PON_FIELD 5 +-#define STV090x_WIDTH_DISEQC1_PON_FIELD 1 +- +-#define STV090x_TSTTNR3 0xf1e2 +-#define STV090x_OFFST_ADC2_PON_FIELD 1 +-#define STV090x_WIDTH_ADC2_PON_FIELD 1 +-#define STV090x_OFFST_ADC2_INMODE_FIELD 0 +-#define STV090x_WIDTH_ADC2_INMODE_FIELD 1 +- +-#define STV090x_TSTTNR4 0xf1e3 +-#define STV090x_OFFST_DISEQC2_PON_FIELD 5 +-#define STV090x_WIDTH_DISEQC2_PON_FIELD 1 +- +-#define STV090x_FSKTFC2 0xf170 +-#define STV090x_OFFST_FSKT_KMOD_FIELD 2 +-#define STV090x_WIDTH_FSKT_KMOD_FIELD 6 +-#define STV090x_OFFST_FSKT_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKT_CAR_FIELD 2 +- +-#define STV090x_FSKTFC1 0xf171 +-#define STV090x_OFFST_FSKTC1_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKTC1_CAR_FIELD 8 +- +-#define STV090x_FSKTFC0 0xf172 +-#define STV090x_OFFST_FSKTC0_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKTC0_CAR_FIELD 8 +- +-#define STV090x_FSKTDELTAF1 0xf173 +-#define STV090x_OFFST_FSKTF1_DELTAF_FIELD 0 +-#define STV090x_WIDTH_FSKTF1_DELTAF_FIELD 4 +- +-#define STV090x_FSKTDELTAF0 0xf174 +-#define STV090x_OFFST_FSKTF0_DELTAF_FIELD 0 +-#define STV090x_WIDTH_FSKTF0_DELTAF_FIELD 8 +- +-#define STV090x_FSKTCTRL 0xf175 +-#define STV090x_OFFST_FSKT_EN_SGN_FIELD 6 +-#define STV090x_WIDTH_FSKT_EN_SGN_FIELD 1 +-#define STV090x_OFFST_FSKT_MOD_SGN_FIELD 5 +-#define STV090x_WIDTH_FSKT_MOD_SGN_FIELD 1 +-#define STV090x_OFFST_FSKT_MOD_EN_FIELD 2 +-#define STV090x_WIDTH_FSKT_MOD_EN_FIELD 3 +-#define STV090x_OFFST_FSKT_DACMODE_FIELD 0 +-#define STV090x_WIDTH_FSKT_DACMODE_FIELD 2 +- +-#define STV090x_FSKRFC2 0xf176 +-#define STV090x_OFFST_FSKRC2_DETSGN_FIELD 6 +-#define STV090x_WIDTH_FSKRC2_DETSGN_FIELD 1 +-#define STV090x_OFFST_FSKRC2_OUTSGN_FIELD 5 +-#define STV090x_WIDTH_FSKRC2_OUTSGN_FIELD 1 +-#define STV090x_OFFST_FSKRC2_KAGC_FIELD 2 +-#define STV090x_WIDTH_FSKRC2_KAGC_FIELD 3 +-#define STV090x_OFFST_FSKRC2_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKRC2_CAR_FIELD 2 +- +-#define STV090x_FSKRFC1 0xf177 +-#define STV090x_OFFST_FSKRC1_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKRC1_CAR_FIELD 8 +- +-#define STV090x_FSKRFC0 0xf178 +-#define STV090x_OFFST_FSKRC0_CAR_FIELD 0 +-#define STV090x_WIDTH_FSKRC0_CAR_FIELD 8 +- +-#define STV090x_FSKRK1 0xf179 +-#define STV090x_OFFST_FSKR_K1_EXP_FIELD 5 +-#define STV090x_WIDTH_FSKR_K1_EXP_FIELD 3 +-#define STV090x_OFFST_FSKR_K1_MANT_FIELD 0 +-#define STV090x_WIDTH_FSKR_K1_MANT_FIELD 5 +- +-#define STV090x_FSKRK2 0xf17a +-#define STV090x_OFFST_FSKR_K2_EXP_FIELD 5 +-#define STV090x_WIDTH_FSKR_K2_EXP_FIELD 3 +-#define STV090x_OFFST_FSKR_K2_MANT_FIELD 0 +-#define STV090x_WIDTH_FSKR_K2_MANT_FIELD 5 +- +-#define STV090x_FSKRAGCR 0xf17b +-#define STV090x_OFFST_FSKR_OUTCTL_FIELD 6 +-#define STV090x_WIDTH_FSKR_OUTCTL_FIELD 2 +-#define STV090x_OFFST_FSKR_AGC_REF_FIELD 0 +-#define STV090x_WIDTH_FSKR_AGC_REF_FIELD 6 +- +-#define STV090x_FSKRAGC 0xf17c +-#define STV090x_OFFST_FSKR_AGC_ACCU_FIELD 0 +-#define STV090x_WIDTH_FSKR_AGC_ACCU_FIELD 8 +- +-#define STV090x_FSKRALPHA 0xf17d +-#define STV090x_OFFST_FSKR_ALPHA_EXP_FIELD 2 +-#define STV090x_WIDTH_FSKR_ALPHA_EXP_FIELD 3 +-#define STV090x_OFFST_FSKR_ALPHA_M_FIELD 0 +-#define STV090x_WIDTH_FSKR_ALPHA_M_FIELD 2 +- +-#define STV090x_FSKRPLTH1 0xf17e +-#define STV090x_OFFST_FSKR_BETA_FIELD 4 +-#define STV090x_WIDTH_FSKR_BETA_FIELD 4 +-#define STV090x_OFFST_FSKR_PLL_TRESH1_FIELD 0 +-#define STV090x_WIDTH_FSKR_PLL_TRESH1_FIELD 4 +- +-#define STV090x_FSKRPLTH0 0xf17f +-#define STV090x_OFFST_FSKR_PLL_TRESH0_FIELD 0 +-#define STV090x_WIDTH_FSKR_PLL_TRESH0_FIELD 8 +- +-#define STV090x_FSKRDF1 0xf180 +-#define STV090x_OFFST_FSKR_DELTAF1_FIELD 0 +-#define STV090x_WIDTH_FSKR_DELTAF1_FIELD 5 +- +-#define STV090x_FSKRDF0 0xf181 +-#define STV090x_OFFST_FSKR_DELTAF0_FIELD 0 +-#define STV090x_WIDTH_FSKR_DELTAF0_FIELD 8 +- +-#define STV090x_FSKRSTEPP 0xf182 +-#define STV090x_OFFST_FSKR_STEP_PLUS_FIELD 0 +-#define STV090x_WIDTH_FSKR_STEP_PLUS_FIELD 8 +- +-#define STV090x_FSKRSTEPM 0xf183 +-#define STV090x_OFFST_FSKR_STEP_MINUS_FIELD 0 +-#define STV090x_WIDTH_FSKR_STEP_MINUS_FIELD 8 +- +-#define STV090x_FSKRDET1 0xf184 +-#define STV090x_OFFST_FSKR_CARDET1_ACCU_FIELD 0 +-#define STV090x_WIDTH_FSKR_CARDET1_ACCU_FIELD 4 +- +-#define STV090x_FSKRDET0 0xf185 +-#define STV090x_OFFST_FSKR_CARDET0_ACCU_FIELD 0 +-#define STV090x_WIDTH_FSKR_CARDET0_ACCU_FIELD 8 +- +-#define STV090x_FSKRDTH1 0xf186 +-#define STV090x_OFFST_FSKR_CARLOSS_THRESH1_FIELD 4 +-#define STV090x_WIDTH_FSKR_CARLOSS_THRESH1_FIELD 4 +-#define STV090x_OFFST_FSKR_CARDET_THRESH1_FIELD 0 +-#define STV090x_WIDTH_FSKR_CARDET_THRESH1_FIELD 4 +- +-#define STV090x_FSKRDTH0 0xf187 +-#define STV090x_OFFST_FSKR_CARDET_THRESH0_FIELD 0 +-#define STV090x_WIDTH_FSKR_CARDET_THRESH0_FIELD 8 +- +-#define STV090x_FSKRLOSS 0xf188 +-#define STV090x_OFFST_FSKR_CARLOSS_THRESH_FIELD 0 +-#define STV090x_WIDTH_FSKR_CARLOSS_THRESH_FIELD 8 +- +-#define STV090x_Px_DISTXCTL(__x) (0xF1A0 - (__x - 1) * 0x10) +-#define STV090x_P1_DISTXCTL STV090x_Px_DISTXCTL(1) +-#define STV090x_P2_DISTXCTL STV090x_Px_DISTXCTL(2) +-#define STV090x_OFFST_Px_TIM_OFF_FIELD 7 +-#define STV090x_WIDTH_Px_TIM_OFF_FIELD 1 +-#define STV090x_OFFST_Px_DISEQC_RESET_FIELD 6 +-#define STV090x_WIDTH_Px_DISEQC_RESET_FIELD 1 +-#define STV090x_OFFST_Px_TIM_CMD_FIELD 4 +-#define STV090x_WIDTH_Px_TIM_CMD_FIELD 2 +-#define STV090x_OFFST_Px_DIS_PRECHARGE_FIELD 3 +-#define STV090x_WIDTH_Px_DIS_PRECHARGE_FIELD 1 +-#define STV090x_OFFST_Px_DISTX_MODE_FIELD 0 +-#define STV090x_WIDTH_Px_DISTX_MODE_FIELD 3 +- +-#define STV090x_Px_DISRXCTL(__x) (0xf1a1 - (__x - 1) * 0x10) +-#define STV090x_P1_DISRXCTL STV090x_Px_DISRXCTL(1) +-#define STV090x_P2_DISRXCTL STV090x_Px_DISRXCTL(2) +-#define STV090x_OFFST_Px_RECEIVER_ON_FIELD 7 +-#define STV090x_WIDTH_Px_RECEIVER_ON_FIELD 1 +-#define STV090x_OFFST_Px_IGNO_SHORT22K_FIELD 6 +-#define STV090x_WIDTH_Px_IGNO_SHORT22K_FIELD 1 +-#define STV090x_OFFST_Px_ONECHIP_TRX_FIELD 5 +-#define STV090x_WIDTH_Px_ONECHIP_TRX_FIELD 1 +-#define STV090x_OFFST_Px_EXT_ENVELOP_FIELD 4 +-#define STV090x_WIDTH_Px_EXT_ENVELOP_FIELD 1 +-#define STV090x_OFFST_Px_PIN_SELECT_FIELD 2 +-#define STV090x_WIDTH_Px_PIN_SELECT_FIELD 2 +-#define STV090x_OFFST_Px_IRQ_RXEND_FIELD 1 +-#define STV090x_WIDTH_Px_IRQ_RXEND_FIELD 1 +-#define STV090x_OFFST_Px_IRQ_4NBYTES_FIELD 0 +-#define STV090x_WIDTH_Px_IRQ_4NBYTES_FIELD 1 +- +-#define STV090x_Px_DISRX_ST0(__x) (0xf1a4 - (__x - 1) * 0x10) +-#define STV090x_P1_DISRX_ST0 STV090x_Px_DISRX_ST0(1) +-#define STV090x_P2_DISRX_ST0 STV090x_Px_DISRX_ST0(2) +-#define STV090x_OFFST_Px_RX_END_FIELD 7 +-#define STV090x_WIDTH_Px_RX_END_FIELD 1 +-#define STV090x_OFFST_Px_RX_ACTIVE_FIELD 6 +-#define STV090x_WIDTH_Px_RX_ACTIVE_FIELD 1 +-#define STV090x_OFFST_Px_SHORT_22KHZ_FIELD 5 +-#define STV090x_WIDTH_Px_SHORT_22KHZ_FIELD 1 +-#define STV090x_OFFST_Px_CONT_TONE_FIELD 4 +-#define STV090x_WIDTH_Px_CONT_TONE_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3 +-#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2 +-#define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1 +-#define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0 +-#define STV090x_WIDTH_Px_ABORT_DISRX_FIELD 1 +- +-#define STV090x_Px_DISRX_ST1(__x) (0xf1a5 - (__x - 1) * 0x10) +-#define STV090x_P1_DISRX_ST1 STV090x_Px_DISRX_ST1(1) +-#define STV090x_P2_DISRX_ST1 STV090x_Px_DISRX_ST1(2) +-#define STV090x_OFFST_Px_RX_FAIL_FIELD 7 +-#define STV090x_WIDTH_Px_RX_FAIL_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_PARITYFAIL_FIELD 6 +-#define STV090x_WIDTH_Px_FIFO_PARITYFAIL_FIELD 1 +-#define STV090x_OFFST_Px_RX_NONBYTE_FIELD 5 +-#define STV090x_WIDTH_Px_RX_NONBYTE_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_OVERFLOW_FIELD 4 +-#define STV090x_WIDTH_Px_FIFO_OVERFLOW_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_BYTENBR_FIELD 0 +-#define STV090x_WIDTH_Px_FIFO_BYTENBR_FIELD 4 +- +-#define STV090x_Px_DISRXDATA(__x) (0xf1a6 - (__x - 1) * 0x10) +-#define STV090x_P1_DISRXDATA STV090x_Px_DISRXDATA(1) +-#define STV090x_P2_DISRXDATA STV090x_Px_DISRXDATA(2) +-#define STV090x_OFFST_Px_DISRX_DATA_FIELD 0 +-#define STV090x_WIDTH_Px_DISRX_DATA_FIELD 8 +- +-#define STV090x_Px_DISTXDATA(__x) (0xf1a7 - (__x - 1) * 0x10) +-#define STV090x_P1_DISTXDATA STV090x_Px_DISTXDATA(1) +-#define STV090x_P2_DISTXDATA STV090x_Px_DISTXDATA(2) +-#define STV090x_OFFST_Px_DISEQC_FIFO_FIELD 0 +-#define STV090x_WIDTH_Px_DISEQC_FIFO_FIELD 8 +- +-#define STV090x_Px_DISTXSTATUS(__x) (0xf1a8 - (__x - 1) * 0x10) +-#define STV090x_P1_DISTXSTATUS STV090x_Px_DISTXSTATUS(1) +-#define STV090x_P2_DISTXSTATUS STV090x_Px_DISTXSTATUS(2) +-#define STV090x_OFFST_Px_TX_FAIL_FIELD 7 +-#define STV090x_WIDTH_Px_TX_FAIL_FIELD 1 +-#define STV090x_OFFST_Px_FIFO_FULL_FIELD 6 +-#define STV090x_WIDTH_Px_FIFO_FULL_FIELD 1 +-#define STV090x_OFFST_Px_TX_IDLE_FIELD 5 +-#define STV090x_WIDTH_Px_TX_IDLE_FIELD 1 +-#define STV090x_OFFST_Px_GAP_BURST_FIELD 4 +-#define STV090x_WIDTH_Px_GAP_BURST_FIELD 1 +-#define STV090x_OFFST_Px_TXFIFO_BYTES_FIELD 0 +-#define STV090x_WIDTH_Px_TXFIFO_BYTES_FIELD 4 +- +-#define STV090x_Px_F22TX(__x) (0xf1a9 - (__x - 1) * 0x10) +-#define STV090x_P1_F22TX STV090x_Px_F22TX(1) +-#define STV090x_P2_F22TX STV090x_Px_F22TX(2) +-#define STV090x_OFFST_Px_F22_REG_FIELD 0 +-#define STV090x_WIDTH_Px_F22_REG_FIELD 8 +- +-#define STV090x_Px_F22RX(__x) (0xf1aa - (__x - 1) * 0x10) +-#define STV090x_P1_F22RX STV090x_Px_F22RX(1) +-#define STV090x_P2_F22RX STV090x_Px_F22RX(2) +-#define STV090x_OFFST_Px_F22RX_REG_FIELD 0 +-#define STV090x_WIDTH_Px_F22RX_REG_FIELD 8 +- +-#define STV090x_Px_ACRPRESC(__x) (0xf1ac - (__x - 1) * 0x10) +-#define STV090x_P1_ACRPRESC STV090x_Px_ACRPRESC(1) +-#define STV090x_P2_ACRPRESC STV090x_Px_ACRPRESC(2) +-#define STV090x_OFFST_Px_ACR_PRESC_FIELD 0 +-#define STV090x_WIDTH_Px_ACR_PRESC_FIELD 3 +- +-#define STV090x_Px_ACRDIV(__x) (0xf1ad - (__x - 1) * 0x10) +-#define STV090x_P1_ACRDIV STV090x_Px_ACRDIV(1) +-#define STV090x_P2_ACRDIV STV090x_Px_ACRDIV(2) +-#define STV090x_OFFST_Px_ACR_DIV_FIELD 0 +-#define STV090x_WIDTH_Px_ACR_DIV_FIELD 8 +- +-#define STV090x_Px_IQCONST(__x) (0xF400 - (__x - 1) * 0x200) +-#define STV090x_P1_IQCONST STV090x_Px_IQCONST(1) +-#define STV090x_P2_IQCONST STV090x_Px_IQCONST(2) +-#define STV090x_OFFST_Px_CONSTEL_SELECT_FIELD 5 +-#define STV090x_WIDTH_Px_CONSTEL_SELECT_FIELD 2 +- +-#define STV090x_Px_NOSCFG(__x) (0xF401 - (__x - 1) * 0x200) +-#define STV090x_P1_NOSCFG STV090x_Px_NOSCFG(1) +-#define STV090x_P2_NOSCFG STV090x_Px_NOSCFG(2) +-#define STV090x_OFFST_Px_NOSPLH_BETA_FIELD 3 +-#define STV090x_WIDTH_Px_NOSPLH_BETA_FIELD 2 +-#define STV090x_OFFST_Px_NOSDATA_BETA_FIELD 0 +-#define STV090x_WIDTH_Px_NOSDATA_BETA_FIELD 3 +- +-#define STV090x_Px_ISYMB(__x) (0xF402 - (__x - 1) * 0x200) +-#define STV090x_P1_ISYMB STV090x_Px_ISYMB(1) +-#define STV090x_P2_ISYMB STV090x_Px_ISYMB(2) +-#define STV090x_OFFST_Px_I_SYMBOL_FIELD 0 +-#define STV090x_WIDTH_Px_I_SYMBOL_FIELD 8 +- +-#define STV090x_Px_QSYMB(__x) (0xF403 - (__x - 1) * 0x200) +-#define STV090x_P1_QSYMB STV090x_Px_QSYMB(1) +-#define STV090x_P2_QSYMB STV090x_Px_QSYMB(2) +-#define STV090x_OFFST_Px_Q_SYMBOL_FIELD 0 +-#define STV090x_WIDTH_Px_Q_SYMBOL_FIELD 8 +- +-#define STV090x_Px_AGC1CFG(__x) (0xF404 - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1CFG STV090x_Px_AGC1CFG(1) +-#define STV090x_P2_AGC1CFG STV090x_Px_AGC1CFG(2) +-#define STV090x_OFFST_Px_DC_FROZEN_FIELD 7 +-#define STV090x_WIDTH_Px_DC_FROZEN_FIELD 1 +-#define STV090x_OFFST_Px_DC_CORRECT_FIELD 6 +-#define STV090x_WIDTH_Px_DC_CORRECT_FIELD 1 +-#define STV090x_OFFST_Px_AMM_FROZEN_FIELD 5 +-#define STV090x_WIDTH_Px_AMM_FROZEN_FIELD 1 +-#define STV090x_OFFST_Px_AMM_CORRECT_FIELD 4 +-#define STV090x_WIDTH_Px_AMM_CORRECT_FIELD 1 +-#define STV090x_OFFST_Px_QUAD_FROZEN_FIELD 3 +-#define STV090x_WIDTH_Px_QUAD_FROZEN_FIELD 1 +-#define STV090x_OFFST_Px_QUAD_CORRECT_FIELD 2 +-#define STV090x_WIDTH_Px_QUAD_CORRECT_FIELD 1 +- +-#define STV090x_Px_AGC1CN(__x) (0xF406 - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1CN STV090x_Px_AGC1CN(1) +-#define STV090x_P2_AGC1CN STV090x_Px_AGC1CN(2) +-#define STV090x_WIDTH_Px_AGC1_LOCKED_FIELD 7 +-#define STV090x_OFFST_Px_AGC1_LOCKED_FIELD 1 +-#define STV090x_OFFST_Px_AGC1_MINPOWER_FIELD 4 +-#define STV090x_WIDTH_Px_AGC1_MINPOWER_FIELD 1 +-#define STV090x_OFFST_Px_AGCOUT_FAST_FIELD 3 +-#define STV090x_WIDTH_Px_AGCOUT_FAST_FIELD 1 +-#define STV090x_OFFST_Px_AGCIQ_BETA_FIELD 0 +-#define STV090x_WIDTH_Px_AGCIQ_BETA_FIELD 3 +- +-#define STV090x_Px_AGC1REF(__x) (0xF407 - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1REF STV090x_Px_AGC1REF(1) +-#define STV090x_P2_AGC1REF STV090x_Px_AGC1REF(2) +-#define STV090x_OFFST_Px_AGCIQ_REF_FIELD 0 +-#define STV090x_WIDTH_Px_AGCIQ_REF_FIELD 8 +- +-#define STV090x_Px_IDCCOMP(__x) (0xF408 - (__x - 1) * 0x200) +-#define STV090x_P1_IDCCOMP STV090x_Px_IDCCOMP(1) +-#define STV090x_P2_IDCCOMP STV090x_Px_IDCCOMP(2) +-#define STV090x_OFFST_Px_IAVERAGE_ADJ_FIELD 0 +-#define STV090x_WIDTH_Px_IAVERAGE_ADJ_FIELD 8 +- +-#define STV090x_Px_QDCCOMP(__x) (0xF409 - (__x - 1) * 0x200) +-#define STV090x_P1_QDCCOMP STV090x_Px_QDCCOMP(1) +-#define STV090x_P2_QDCCOMP STV090x_Px_QDCCOMP(2) +-#define STV090x_OFFST_Px_QAVERAGE_ADJ_FIELD 0 +-#define STV090x_WIDTH_Px_QAVERAGE_ADJ_FIELD 8 +- +-#define STV090x_Px_POWERI(__x) (0xF40A - (__x - 1) * 0x200) +-#define STV090x_P1_POWERI STV090x_Px_POWERI(1) +-#define STV090x_P2_POWERI STV090x_Px_POWERI(2) +-#define STV090x_OFFST_Px_POWER_I_FIELD 0 +-#define STV090x_WIDTH_Px_POWER_I_FIELD 8 +- +-#define STV090x_Px_POWERQ(__x) (0xF40B - (__x - 1) * 0x200) +-#define STV090x_P1_POWERQ STV090x_Px_POWERQ(1) +-#define STV090x_P2_POWERQ STV090x_Px_POWERQ(2) +-#define STV090x_OFFST_Px_POWER_Q_FIELD 0 +-#define STV090x_WIDTH_Px_POWER_Q_FIELD 8 +- +-#define STV090x_Px_AGC1AMM(__x) (0xF40C - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1AMM STV090x_Px_AGC1AMM(1) +-#define STV090x_P2_AGC1AMM STV090x_Px_AGC1AMM(2) +-#define STV090x_OFFST_Px_AMM_VALUE_FIELD 0 +-#define STV090x_WIDTH_Px_AMM_VALUE_FIELD 8 +- +-#define STV090x_Px_AGC1QUAD(__x) (0xF40D - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1QUAD STV090x_Px_AGC1QUAD(1) +-#define STV090x_P2_AGC1QUAD STV090x_Px_AGC1QUAD(2) +-#define STV090x_OFFST_Px_QUAD_VALUE_FIELD 0 +-#define STV090x_WIDTH_Px_QUAD_VALUE_FIELD 8 +- +-#define STV090x_Px_AGCIQINy(__x, __y) (0xF40F - (__x-1) * 0x200 - __y * 0x1) +-#define STV090x_P1_AGCIQIN0 STV090x_Px_AGCIQINy(1, 0) +-#define STV090x_P1_AGCIQIN1 STV090x_Px_AGCIQINy(1, 1) +-#define STV090x_P2_AGCIQIN0 STV090x_Px_AGCIQINy(2, 0) +-#define STV090x_P2_AGCIQIN1 STV090x_Px_AGCIQINy(2, 1) +-#define STV090x_OFFST_Px_AGCIQ_VALUE_FIELD 0 +-#define STV090x_WIDTH_Px_AGCIQ_VALUE_FIELD 8 +- +-#define STV090x_Px_DEMOD(__x) (0xF410 - (__x - 1) * 0x200) +-#define STV090x_P1_DEMOD STV090x_Px_DEMOD(1) +-#define STV090x_P2_DEMOD STV090x_Px_DEMOD(2) +-#define STV090x_OFFST_Px_MANUAL_S2ROLLOFF_FIELD 7 +-#define STV090x_WIDTH_Px_MANUAL_S2ROLLOFF_FIELD 1 +-#define STV090x_OFFST_Px_DEMOD_STOP_FIELD 6 +-#define STV090x_WIDTH_Px_DEMOD_STOP_FIELD 1 +-#define STV090x_OFFST_Px_SPECINV_CONTROL_FIELD 4 +-#define STV090x_WIDTH_Px_SPECINV_CONTROL_FIELD 2 +-#define STV090x_OFFST_Px_FORCE_ENASAMP_FIELD 3 +-#define STV090x_WIDTH_Px_FORCE_ENASAMP_FIELD 1 +-#define STV090x_OFFST_Px_MANUAL_SXROLLOFF_FIELD 2 +-#define STV090x_WIDTH_Px_MANUAL_SXROLLOFF_FIELD 1 +-#define STV090x_OFFST_Px_ROLLOFF_CONTROL_FIELD 0 +-#define STV090x_WIDTH_Px_ROLLOFF_CONTROL_FIELD 2 +- +-#define STV090x_Px_DMDMODCOD(__x) (0xF411 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDMODCOD STV090x_Px_DMDMODCOD(1) +-#define STV090x_P2_DMDMODCOD STV090x_Px_DMDMODCOD(2) +-#define STV090x_OFFST_Px_MANUAL_MODCOD_FIELD 7 +-#define STV090x_WIDTH_Px_MANUAL_MODCOD_FIELD 1 +-#define STV090x_OFFST_Px_DEMOD_MODCOD_FIELD 2 +-#define STV090x_WIDTH_Px_DEMOD_MODCOD_FIELD 5 +-#define STV090x_OFFST_Px_DEMOD_TYPE_FIELD 0 +-#define STV090x_WIDTH_Px_DEMOD_TYPE_FIELD 2 +- +-#define STV090x_Px_DSTATUS(__x) (0xF412 - (__x - 1) * 0x200) +-#define STV090x_P1_DSTATUS STV090x_Px_DSTATUS(1) +-#define STV090x_P2_DSTATUS STV090x_Px_DSTATUS(2) +-#define STV090x_OFFST_Px_CAR_LOCK_FIELD 7 +-#define STV090x_WIDTH_Px_CAR_LOCK_FIELD 1 +-#define STV090x_OFFST_Px_TMGLOCK_QUALITY_FIELD 5 +-#define STV090x_WIDTH_Px_TMGLOCK_QUALITY_FIELD 2 +-#define STV090x_OFFST_Px_LOCK_DEFINITIF_FIELD 3 +-#define STV090x_WIDTH_Px_LOCK_DEFINITIF_FIELD 1 +- +-#define STV090x_Px_DSTATUS2(__x) (0xF413 - (__x - 1) * 0x200) +-#define STV090x_P1_DSTATUS2 STV090x_Px_DSTATUS2(1) +-#define STV090x_P2_DSTATUS2 STV090x_Px_DSTATUS2(2) +-#define STV090x_OFFST_Px_DEMOD_DELOCK_FIELD 7 +-#define STV090x_WIDTH_Px_DEMOD_DELOCK_FIELD 1 +-#define STV090x_OFFST_Px_AGC1_NOSIGNALACK_FIELD 3 +-#define STV090x_WIDTH_Px_AGC1_NOSIGNALACK_FIELD 1 +-#define STV090x_OFFST_Px_AGC2_OVERFLOW_FIELD 2 +-#define STV090x_WIDTH_Px_AGC2_OVERFLOW_FIELD 1 +-#define STV090x_OFFST_Px_CFR_OVERFLOW_FIELD 1 +-#define STV090x_WIDTH_Px_CFR_OVERFLOW_FIELD 1 +-#define STV090x_OFFST_Px_GAMMA_OVERUNDER_FIELD 0 +-#define STV090x_WIDTH_Px_GAMMA_OVERUNDER_FIELD 1 +- +-#define STV090x_Px_DMDCFGMD(__x) (0xF414 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDCFGMD STV090x_Px_DMDCFGMD(1) +-#define STV090x_P2_DMDCFGMD STV090x_Px_DMDCFGMD(2) +-#define STV090x_OFFST_Px_DVBS2_ENABLE_FIELD 7 +-#define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1 +-#define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6 +-#define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1 +-#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 +-#define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1 +-#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3 +-#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 +-#define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2 +-#define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1 +-#define STV090x_OFFST_Px_TUN_RNG_FIELD 0 +-#define STV090x_WIDTH_Px_TUN_RNG_FIELD 2 +- +-#define STV090x_Px_DMDCFG2(__x) (0xF415 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDCFG2 STV090x_Px_DMDCFG2(1) +-#define STV090x_P2_DMDCFG2 STV090x_Px_DMDCFG2(2) +-#define STV090x_OFFST_Px_S1S2_SEQUENTIAL_FIELD 6 +-#define STV090x_WIDTH_Px_S1S2_SEQUENTIAL_FIELD 1 +- +-#define STV090x_Px_DMDISTATE(__x) (0xF416 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDISTATE STV090x_Px_DMDISTATE(1) +-#define STV090x_P2_DMDISTATE STV090x_Px_DMDISTATE(2) +-#define STV090x_OFFST_Px_I2C_DEMOD_MODE_FIELD 0 +-#define STV090x_WIDTH_Px_I2C_DEMOD_MODE_FIELD 5 +- +-#define STV090x_Px_DMDTOM(__x) (0xF417 - (__x - 1) * 0x200) /* check */ +-#define STV090x_P1_DMDTOM STV090x_Px_DMDTOM(1) +-#define STV090x_P2_DMDTOM STV090x_Px_DMDTOM(2) +- +-#define STV090x_Px_DMDSTATE(__x) (0xF41B - (__x - 1) * 0x200) +-#define STV090x_P1_DMDSTATE STV090x_Px_DMDSTATE(1) +-#define STV090x_P2_DMDSTATE STV090x_Px_DMDSTATE(2) +-#define STV090x_OFFST_Px_HEADER_MODE_FIELD 5 +-#define STV090x_WIDTH_Px_HEADER_MODE_FIELD 2 +- +-#define STV090x_Px_DMDFLYW(__x) (0xF41C - (__x - 1) * 0x200) +-#define STV090x_P1_DMDFLYW STV090x_Px_DMDFLYW(1) +-#define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2) +-#define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4 +-#define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4 +-#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4 +- +-#define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200) +-#define STV090x_P1_DSTATUS3 STV090x_Px_DSTATUS3(1) +-#define STV090x_P2_DSTATUS3 STV090x_Px_DSTATUS3(2) +-#define STV090x_OFFST_Px_DEMOD_CFGMODE_FIELD 5 +-#define STV090x_WIDTH_Px_DEMOD_CFGMODE_FIELD 2 +- +-#define STV090x_Px_DMDCFG3(__x) (0xF41E - (__x - 1) * 0x200) +-#define STV090x_P1_DMDCFG3 STV090x_Px_DMDCFG3(1) +-#define STV090x_P2_DMDCFG3 STV090x_Px_DMDCFG3(2) +-#define STV090x_OFFST_Px_NOSTOP_FIFOFULL_FIELD 3 +-#define STV090x_WIDTH_Px_NOSTOP_FIFOFULL_FIELD 1 +- +-#define STV090x_Px_DMDCFG4(__x) (0xf41f - (__x - 1) * 0x200) +-#define STV090x_P1_DMDCFG4 STV090x_Px_DMDCFG4(1) +-#define STV090x_P2_DMDCFG4 STV090x_Px_DMDCFG4(2) +- +-#define STV090x_Px_CORRELMANT(__x) (0xF420 - (__x - 1) * 0x200) +-#define STV090x_P1_CORRELMANT STV090x_Px_CORRELMANT(1) +-#define STV090x_P2_CORRELMANT STV090x_Px_CORRELMANT(2) +-#define STV090x_OFFST_Px_CORREL_MANT_FIELD 0 +-#define STV090x_WIDTH_Px_CORREL_MANT_FIELD 8 +- +-#define STV090x_Px_CORRELABS(__x) (0xF421 - (__x - 1) * 0x200) +-#define STV090x_P1_CORRELABS STV090x_Px_CORRELABS(1) +-#define STV090x_P2_CORRELABS STV090x_Px_CORRELABS(2) +-#define STV090x_OFFST_Px_CORREL_ABS_FIELD 0 +-#define STV090x_WIDTH_Px_CORREL_ABS_FIELD 8 +- +-#define STV090x_Px_CORRELEXP(__x) (0xF422 - (__x - 1) * 0x200) +-#define STV090x_P1_CORRELEXP STV090x_Px_CORRELEXP(1) +-#define STV090x_P2_CORRELEXP STV090x_Px_CORRELEXP(2) +-#define STV090x_OFFST_Px_CORREL_ABSEXP_FIELD 4 +-#define STV090x_WIDTH_Px_CORREL_ABSEXP_FIELD 4 +-#define STV090x_OFFST_Px_CORREL_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_CORREL_EXP_FIELD 4 +- +-#define STV090x_Px_PLHMODCOD(__x) (0xF424 - (__x - 1) * 0x200) +-#define STV090x_P1_PLHMODCOD STV090x_Px_PLHMODCOD(1) +-#define STV090x_P2_PLHMODCOD STV090x_Px_PLHMODCOD(2) +-#define STV090x_OFFST_Px_SPECINV_DEMOD_FIELD 7 +-#define STV090x_WIDTH_Px_SPECINV_DEMOD_FIELD 1 +-#define STV090x_OFFST_Px_PLH_MODCOD_FIELD 2 +-#define STV090x_WIDTH_Px_PLH_MODCOD_FIELD 5 +-#define STV090x_OFFST_Px_PLH_TYPE_FIELD 0 +-#define STV090x_WIDTH_Px_PLH_TYPE_FIELD 2 +- +-#define STV090x_Px_AGCK32(__x) (0xf42b - (__x - 1) * 0x200) +-#define STV090x_P1_AGCK32 STV090x_Px_AGCK32(1) +-#define STV090x_P2_AGCK32 STV090x_Px_AGCK32(2) +- +-#define STV090x_Px_AGC2O(__x) (0xF42C - (__x - 1) * 0x200) +-#define STV090x_P1_AGC2O STV090x_Px_AGC2O(1) +-#define STV090x_P2_AGC2O STV090x_Px_AGC2O(2) +- +-#define STV090x_Px_AGC2REF(__x) (0xF42D - (__x - 1) * 0x200) +-#define STV090x_P1_AGC2REF STV090x_Px_AGC2REF(1) +-#define STV090x_P2_AGC2REF STV090x_Px_AGC2REF(2) +-#define STV090x_OFFST_Px_AGC2_REF_FIELD 0 +-#define STV090x_WIDTH_Px_AGC2_REF_FIELD 8 +- +-#define STV090x_Px_AGC1ADJ(__x) (0xF42E - (__x - 1) * 0x200) +-#define STV090x_P1_AGC1ADJ STV090x_Px_AGC1ADJ(1) +-#define STV090x_P2_AGC1ADJ STV090x_Px_AGC1ADJ(2) +-#define STV090x_OFFST_Px_AGC1_ADJUSTED_FIELD 0 +-#define STV090x_WIDTH_Px_AGC1_ADJUSTED_FIELD 7 +- +-#define STV090x_Px_AGC2Iy(__x, __y) (0xF437 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_AGC2I0 STV090x_Px_AGC2Iy(1, 0) +-#define STV090x_P1_AGC2I1 STV090x_Px_AGC2Iy(1, 1) +-#define STV090x_P2_AGC2I0 STV090x_Px_AGC2Iy(2, 0) +-#define STV090x_P2_AGC2I1 STV090x_Px_AGC2Iy(2, 1) +-#define STV090x_OFFST_Px_AGC2_INTEGRATOR_FIELD 0 +-#define STV090x_WIDTH_Px_AGC2_INTEGRATOR_FIELD 8 +- +-#define STV090x_Px_CARCFG(__x) (0xF438 - (__x - 1) * 0x200) +-#define STV090x_P1_CARCFG STV090x_Px_CARCFG(1) +-#define STV090x_P2_CARCFG STV090x_Px_CARCFG(2) +-#define STV090x_OFFST_Px_EN_CAR2CENTER_FIELD 5 +-#define STV090x_WIDTH_Px_EN_CAR2CENTER_FIELD 1 +-#define STV090x_OFFST_Px_ROTATON_FIELD 2 +-#define STV090x_WIDTH_Px_ROTATON_FIELD 1 +-#define STV090x_OFFST_Px_PH_DET_ALGO_FIELD 0 +-#define STV090x_WIDTH_Px_PH_DET_ALGO_FIELD 2 +- +-#define STV090x_Px_ACLC(__x) (0xF439 - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC STV090x_Px_ACLC(1) +-#define STV090x_P2_ACLC STV090x_Px_ACLC(2) +-#define STV090x_OFFST_Px_CAR_ALPHA_MANT_FIELD 4 +-#define STV090x_WIDTH_Px_CAR_ALPHA_MANT_FIELD 2 +-#define STV090x_OFFST_Px_CAR_ALPHA_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_CAR_ALPHA_EXP_FIELD 4 +- +-#define STV090x_Px_BCLC(__x) (0xF43A - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC STV090x_Px_BCLC(1) +-#define STV090x_P2_BCLC STV090x_Px_BCLC(2) +-#define STV090x_OFFST_Px_CAR_BETA_MANT_FIELD 4 +-#define STV090x_WIDTH_Px_CAR_BETA_MANT_FIELD 2 +-#define STV090x_OFFST_Px_CAR_BETA_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_CAR_BETA_EXP_FIELD 4 +- +-#define STV090x_Px_CARFREQ(__x) (0xF43D - (__x - 1) * 0x200) +-#define STV090x_P1_CARFREQ STV090x_Px_CARFREQ(1) +-#define STV090x_P2_CARFREQ STV090x_Px_CARFREQ(2) +-#define STV090x_OFFST_Px_KC_COARSE_EXP_FIELD 4 +-#define STV090x_WIDTH_Px_KC_COARSE_EXP_FIELD 4 +-#define STV090x_OFFST_Px_BETA_FREQ_FIELD 0 +-#define STV090x_WIDTH_Px_BETA_FREQ_FIELD 4 +- +-#define STV090x_Px_CARHDR(__x) (0xF43E - (__x - 1) * 0x200) +-#define STV090x_P1_CARHDR STV090x_Px_CARHDR(1) +-#define STV090x_P2_CARHDR STV090x_Px_CARHDR(2) +-#define STV090x_OFFST_Px_FREQ_HDR_FIELD 0 +-#define STV090x_WIDTH_Px_FREQ_HDR_FIELD 8 +- +-#define STV090x_Px_LDT(__x) (0xF43F - (__x - 1) * 0x200) +-#define STV090x_P1_LDT STV090x_Px_LDT(1) +-#define STV090x_P2_LDT STV090x_Px_LDT(2) +-#define STV090x_OFFST_Px_CARLOCK_THRES_FIELD 0 +-#define STV090x_WIDTH_Px_CARLOCK_THRES_FIELD 8 +- +-#define STV090x_Px_LDT2(__x) (0xF440 - (__x - 1) * 0x200) +-#define STV090x_P1_LDT2 STV090x_Px_LDT2(1) +-#define STV090x_P2_LDT2 STV090x_Px_LDT2(2) +-#define STV090x_OFFST_Px_CARLOCK_THRES2_FIELD 0 +-#define STV090x_WIDTH_Px_CARLOCK_THRES2_FIELD 8 +- +-#define STV090x_Px_CFRICFG(__x) (0xF441 - (__x - 1) * 0x200) +-#define STV090x_P1_CFRICFG STV090x_Px_CFRICFG(1) +-#define STV090x_P2_CFRICFG STV090x_Px_CFRICFG(2) +-#define STV090x_OFFST_Px_NEG_CFRSTEP_FIELD 0 +-#define STV090x_WIDTH_Px_NEG_CFRSTEP_FIELD 1 +- +-#define STV090x_Pn_CFRUPy(__x, __y) (0xF443 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_CFRUP0 STV090x_Pn_CFRUPy(1, 0) +-#define STV090x_P1_CFRUP1 STV090x_Pn_CFRUPy(1, 1) +-#define STV090x_P2_CFRUP0 STV090x_Pn_CFRUPy(2, 0) +-#define STV090x_P2_CFRUP1 STV090x_Pn_CFRUPy(2, 1) +-#define STV090x_OFFST_Px_CFR_UP_FIELD 0 +-#define STV090x_WIDTH_Px_CFR_UP_FIELD 8 +- +-#define STV090x_Pn_CFRLOWy(__x, __y) (0xF447 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_CFRLOW0 STV090x_Pn_CFRLOWy(1, 0) +-#define STV090x_P1_CFRLOW1 STV090x_Pn_CFRLOWy(1, 1) +-#define STV090x_P2_CFRLOW0 STV090x_Pn_CFRLOWy(2, 0) +-#define STV090x_P2_CFRLOW1 STV090x_Pn_CFRLOWy(2, 1) +-#define STV090x_OFFST_Px_CFR_LOW_FIELD 0 +-#define STV090x_WIDTH_Px_CFR_LOW_FIELD 8 +- +-#define STV090x_Pn_CFRINITy(__x, __y) (0xF449 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_CFRINIT0 STV090x_Pn_CFRINITy(1, 0) +-#define STV090x_P1_CFRINIT1 STV090x_Pn_CFRINITy(1, 1) +-#define STV090x_P2_CFRINIT0 STV090x_Pn_CFRINITy(2, 0) +-#define STV090x_P2_CFRINIT1 STV090x_Pn_CFRINITy(2, 1) +-#define STV090x_OFFST_Px_CFR_INIT_FIELD 0 +-#define STV090x_WIDTH_Px_CFR_INIT_FIELD 8 +- +-#define STV090x_Px_CFRINC1(__x) (0xF44A - (__x - 1) * 0x200) +-#define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1) +-#define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2) +-#define STV090x_OFFST_Px_CFR_INC1_FIELD 0 +-#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */ +- +-#define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200) +-#define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1) +-#define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2) +-#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */ +-#define STV090x_WIDTH_Px_CFR_INC0_FIELD 4 +- +-#define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_CFR0 STV090x_Pn_CFRy(1, 0) +-#define STV090x_P1_CFR1 STV090x_Pn_CFRy(1, 1) +-#define STV090x_P1_CFR2 STV090x_Pn_CFRy(1, 2) +-#define STV090x_P2_CFR0 STV090x_Pn_CFRy(2, 0) +-#define STV090x_P2_CFR1 STV090x_Pn_CFRy(2, 1) +-#define STV090x_P2_CFR2 STV090x_Pn_CFRy(2, 2) +-#define STV090x_OFFST_Px_CAR_FREQ_FIELD 0 +-#define STV090x_WIDTH_Px_CAR_FREQ_FIELD 8 +- +-#define STV090x_Px_LDI(__x) (0xF44F - (__x - 1) * 0x200) +-#define STV090x_P1_LDI STV090x_Px_LDI(1) +-#define STV090x_P2_LDI STV090x_Px_LDI(2) +-#define STV090x_OFFST_Px_LOCK_DET_INTEGR_FIELD 0 +-#define STV090x_WIDTH_Px_LOCK_DET_INTEGR_FIELD 8 +- +-#define STV090x_Px_TMGCFG(__x) (0xF450 - (__x - 1) * 0x200) +-#define STV090x_P1_TMGCFG STV090x_Px_TMGCFG(1) +-#define STV090x_P2_TMGCFG STV090x_Px_TMGCFG(2) +-#define STV090x_OFFST_Px_TMGLOCK_BETA_FIELD 6 +-#define STV090x_WIDTH_Px_TMGLOCK_BETA_FIELD 2 +-#define STV090x_OFFST_Px_DO_TIMING_FIELD 4 +-#define STV090x_WIDTH_Px_DO_TIMING_FIELD 1 +-#define STV090x_OFFST_Px_TMG_MINFREQ_FIELD 0 +-#define STV090x_WIDTH_Px_TMG_MINFREQ_FIELD 2 +- +-#define STV090x_Px_RTC(__x) (0xF451 - (__x - 1) * 0x200) +-#define STV090x_P1_RTC STV090x_Px_RTC(1) +-#define STV090x_P2_RTC STV090x_Px_RTC(2) +-#define STV090x_OFFST_Px_TMGALPHA_EXP_FIELD 4 +-#define STV090x_WIDTH_Px_TMGALPHA_EXP_FIELD 4 +-#define STV090x_OFFST_Px_TMGBETA_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_TMGBETA_EXP_FIELD 4 +- +-#define STV090x_Px_RTCS2(__x) (0xF452 - (__x - 1) * 0x200) +-#define STV090x_P1_RTCS2 STV090x_Px_RTCS2(1) +-#define STV090x_P2_RTCS2 STV090x_Px_RTCS2(2) +-#define STV090x_OFFST_Px_TMGALPHAS2_EXP_FIELD 4 +-#define STV090x_WIDTH_Px_TMGALPHAS2_EXP_FIELD 4 +-#define STV090x_OFFST_Px_TMGBETAS2_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_TMGBETAS2_EXP_FIELD 4 +- +-#define STV090x_Px_TMGTHRISE(__x) (0xF453 - (__x - 1) * 0x200) +-#define STV090x_P1_TMGTHRISE STV090x_Px_TMGTHRISE(1) +-#define STV090x_P2_TMGTHRISE STV090x_Px_TMGTHRISE(2) +-#define STV090x_OFFST_Px_TMGLOCK_THRISE_FIELD 0 +-#define STV090x_WIDTH_Px_TMGLOCK_THRISE_FIELD 8 +- +-#define STV090x_Px_TMGTHFALL(__x) (0xF454 - (__x - 1) * 0x200) +-#define STV090x_P1_TMGTHFALL STV090x_Px_TMGTHFALL(1) +-#define STV090x_P2_TMGTHFALL STV090x_Px_TMGTHFALL(2) +-#define STV090x_OFFST_Px_TMGLOCK_THFALL_FIELD 0 +-#define STV090x_WIDTH_Px_TMGLOCK_THFALL_FIELD 8 +- +-#define STV090x_Px_SFRUPRATIO(__x) (0xF455 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRUPRATIO STV090x_Px_SFRUPRATIO(1) +-#define STV090x_P2_SFRUPRATIO STV090x_Px_SFRUPRATIO(2) +-#define STV090x_OFFST_Px_SFR_UPRATIO_FIELD 0 +-#define STV090x_WIDTH_Px_SFR_UPRATIO_FIELD 8 +- +-#define STV090x_Px_SFRLOWRATIO(__x) (0xF456 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(1) +-#define STV090x_P2_SFRLOWRATIO STV090x_Px_SFRLOWRATIO(2) +-#define STV090x_OFFST_Px_SFR_LOWRATIO_FIELD 0 +-#define STV090x_WIDTH_Px_SFR_LOWRATIO_FIELD 8 +- +-#define STV090x_Px_KREFTMG(__x) (0xF458 - (__x - 1) * 0x200) +-#define STV090x_P1_KREFTMG STV090x_Px_KREFTMG(1) +-#define STV090x_P2_KREFTMG STV090x_Px_KREFTMG(2) +-#define STV090x_OFFST_Px_KREF_TMG_FIELD 0 +-#define STV090x_WIDTH_Px_KREF_TMG_FIELD 8 +- +-#define STV090x_Px_SFRSTEP(__x) (0xF459 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRSTEP STV090x_Px_SFRSTEP(1) +-#define STV090x_P2_SFRSTEP STV090x_Px_SFRSTEP(2) +-#define STV090x_OFFST_Px_SFR_SCANSTEP_FIELD 4 +-#define STV090x_WIDTH_Px_SFR_SCANSTEP_FIELD 4 +-#define STV090x_OFFST_Px_SFR_CENTERSTEP_FIELD 0 +-#define STV090x_WIDTH_Px_SFR_CENTERSTEP_FIELD 4 +- +-#define STV090x_Px_TMGCFG2(__x) (0xF45A - (__x - 1) * 0x200) +-#define STV090x_P1_TMGCFG2 STV090x_Px_TMGCFG2(1) +-#define STV090x_P2_TMGCFG2 STV090x_Px_TMGCFG2(2) +-#define STV090x_OFFST_Px_SFRRATIO_FINE_FIELD 0 +-#define STV090x_WIDTH_Px_SFRRATIO_FINE_FIELD 1 +- +-#define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200) +-#define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1) +-#define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2) +-#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0 +-#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7 +- +-#define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200) +-#define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1) +-#define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2) +-#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0 +-#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8 +- +-#define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1) +-#define STV090x_P2_SFRUP1 STV090x_Px_SFRUP1(2) +-#define STV090x_OFFST_Px_SYMB_FREQ_UP1_FIELD 0 +-#define STV090x_WIDTH_Px_SYMB_FREQ_UP1_FIELD 7 +- +-#define STV090x_Px_SFRUP0(__x) (0xF461 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRUP0 STV090x_Px_SFRUP0(1) +-#define STV090x_P2_SFRUP0 STV090x_Px_SFRUP0(2) +-#define STV090x_OFFST_Px_SYMB_FREQ_UP0_FIELD 0 +-#define STV090x_WIDTH_Px_SYMB_FREQ_UP0_FIELD 8 +- +-#define STV090x_Px_SFRLOW1(__x) (0xF462 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRLOW1 STV090x_Px_SFRLOW1(1) +-#define STV090x_P2_SFRLOW1 STV090x_Px_SFRLOW1(2) +-#define STV090x_OFFST_Px_SYMB_FREQ_LOW1_FIELD 0 +-#define STV090x_WIDTH_Px_SYMB_FREQ_LOW1_FIELD 7 +- +-#define STV090x_Px_SFRLOW0(__x) (0xF463 - (__x - 1) * 0x200) +-#define STV090x_P1_SFRLOW0 STV090x_Px_SFRLOW0(1) +-#define STV090x_P2_SFRLOW0 STV090x_Px_SFRLOW0(2) +-#define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0 +-#define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8 +- +-#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y) +-#define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0) +-#define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1) +-#define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2) +-#define STV090x_P1_SFR3 STV090x_Px_SFRy(1, 3) +-#define STV090x_P2_SFR0 STV090x_Px_SFRy(2, 0) +-#define STV090x_P2_SFR1 STV090x_Px_SFRy(2, 1) +-#define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2) +-#define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3) +-#define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0 +-#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8 +- +-#define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200) +-#define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1) +-#define STV090x_P2_TMGREG2 STV090x_Px_TMGREG2(2) +-#define STV090x_OFFST_Px_TMGREG_FIELD 0 +-#define STV090x_WIDTH_Px_TMGREG_FIELD 8 +- +-#define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200) +-#define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1) +-#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) +-#define STV090x_OFFST_Px_TMGREG_FIELD 0 +-#define STV090x_WIDTH_Px_TMGREG_FIELD 8 +- +-#define STV090x_Px_TMGREG0(__x) (0xF46A - (__x - 1) * 0x200) +-#define STV090x_P1_TMGREG0 STV090x_Px_TMGREG0(1) +-#define STV090x_P2_TMGREG0 STV090x_Px_TMGREG0(2) +-#define STV090x_OFFST_Px_TMGREG_FIELD 0 +-#define STV090x_WIDTH_Px_TMGREG_FIELD 8 +- +-#define STV090x_Px_TMGLOCKy(__x, __y) (0xF46C - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_TMGLOCK0 STV090x_Px_TMGLOCKy(1, 0) +-#define STV090x_P1_TMGLOCK1 STV090x_Px_TMGLOCKy(1, 1) +-#define STV090x_P2_TMGLOCK0 STV090x_Px_TMGLOCKy(2, 0) +-#define STV090x_P2_TMGLOCK1 STV090x_Px_TMGLOCKy(2, 1) +-#define STV090x_OFFST_Px_TMGLOCK_LEVEL_FIELD 0 +-#define STV090x_WIDTH_Px_TMGLOCK_LEVEL_FIELD 8 +- +-#define STV090x_Px_TMGOBS(__x) (0xF46D - (__x - 1) * 0x200) +-#define STV090x_P1_TMGOBS STV090x_Px_TMGOBS(1) +-#define STV090x_P2_TMGOBS STV090x_Px_TMGOBS(2) +-#define STV090x_OFFST_Px_ROLLOFF_STATUS_FIELD 6 +-#define STV090x_WIDTH_Px_ROLLOFF_STATUS_FIELD 2 +- +-#define STV090x_Px_EQUALCFG(__x) (0xF46F - (__x - 1) * 0x200) +-#define STV090x_P1_EQUALCFG STV090x_Px_EQUALCFG(1) +-#define STV090x_P2_EQUALCFG STV090x_Px_EQUALCFG(2) +-#define STV090x_OFFST_Px_EQUAL_ON_FIELD 6 +-#define STV090x_WIDTH_Px_EQUAL_ON_FIELD 1 +-#define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0 +-#define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3 +- +-#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1)) +-#define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1) +-#define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2) +-#define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3) +-#define STV090x_P1_EQUAI4 STV090x_Px_EQUAIy(1, 4) +-#define STV090x_P1_EQUAI5 STV090x_Px_EQUAIy(1, 5) +-#define STV090x_P1_EQUAI6 STV090x_Px_EQUAIy(1, 6) +-#define STV090x_P1_EQUAI7 STV090x_Px_EQUAIy(1, 7) +-#define STV090x_P1_EQUAI8 STV090x_Px_EQUAIy(1, 8) +- +-#define STV090x_P2_EQUAI1 STV090x_Px_EQUAIy(2, 1) +-#define STV090x_P2_EQUAI2 STV090x_Px_EQUAIy(2, 2) +-#define STV090x_P2_EQUAI3 STV090x_Px_EQUAIy(2, 3) +-#define STV090x_P2_EQUAI4 STV090x_Px_EQUAIy(2, 4) +-#define STV090x_P2_EQUAI5 STV090x_Px_EQUAIy(2, 5) +-#define STV090x_P2_EQUAI6 STV090x_Px_EQUAIy(2, 6) +-#define STV090x_P2_EQUAI7 STV090x_Px_EQUAIy(2, 7) +-#define STV090x_P2_EQUAI8 STV090x_Px_EQUAIy(2, 8) +-#define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0 +-#define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8 +- +-#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1)) +-#define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1) +-#define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2) +-#define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3) +-#define STV090x_P1_EQUAQ4 STV090x_Px_EQUAQy(1, 4) +-#define STV090x_P1_EQUAQ5 STV090x_Px_EQUAQy(1, 5) +-#define STV090x_P1_EQUAQ6 STV090x_Px_EQUAQy(1, 6) +-#define STV090x_P1_EQUAQ7 STV090x_Px_EQUAQy(1, 7) +-#define STV090x_P1_EQUAQ8 STV090x_Px_EQUAQy(1, 8) +- +-#define STV090x_P2_EQUAQ1 STV090x_Px_EQUAQy(2, 1) +-#define STV090x_P2_EQUAQ2 STV090x_Px_EQUAQy(2, 2) +-#define STV090x_P2_EQUAQ3 STV090x_Px_EQUAQy(2, 3) +-#define STV090x_P2_EQUAQ4 STV090x_Px_EQUAQy(2, 4) +-#define STV090x_P2_EQUAQ5 STV090x_Px_EQUAQy(2, 5) +-#define STV090x_P2_EQUAQ6 STV090x_Px_EQUAQy(2, 6) +-#define STV090x_P2_EQUAQ7 STV090x_Px_EQUAQy(2, 7) +-#define STV090x_P2_EQUAQ8 STV090x_Px_EQUAQy(2, 8) +-#define STV090x_OFFST_Px_EQUA_ACCQy_FIELD 0 +-#define STV090x_WIDTH_Px_EQUA_ACCQy_FIELD 8 +- +-#define STV090x_Px_NNOSDATATy(__x, __y) (0xf481 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NNOSDATAT0 STV090x_Px_NNOSDATATy(1, 0) +-#define STV090x_P1_NNOSDATAT1 STV090x_Px_NNOSDATATy(1, 1) +-#define STV090x_P2_NNOSDATAT0 STV090x_Px_NNOSDATATy(2, 0) +-#define STV090x_P2_NNOSDATAT1 STV090x_Px_NNOSDATATy(2, 1) +-#define STV090x_OFFST_Px_NOSDATAT_NORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSDATAT_NORMED_FIELD 8 +- +-#define STV090x_Px_NNOSDATAy(__x, __y) (0xf483 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NNOSDATA0 STV090x_Px_NNOSDATAy(1, 0) +-#define STV090x_P1_NNOSDATA1 STV090x_Px_NNOSDATAy(1, 1) +-#define STV090x_P2_NNOSDATA0 STV090x_Px_NNOSDATAy(2, 0) +-#define STV090x_P2_NNOSDATA1 STV090x_Px_NNOSDATAy(2, 1) +-#define STV090x_OFFST_Px_NOSDATA_NORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSDATA_NORMED_FIELD 8 +- +-#define STV090x_Px_NNOSPLHTy(__x, __y) (0xf485 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NNOSPLHT0 STV090x_Px_NNOSPLHTy(1, 0) +-#define STV090x_P1_NNOSPLHT1 STV090x_Px_NNOSPLHTy(1, 1) +-#define STV090x_P2_NNOSPLHT0 STV090x_Px_NNOSPLHTy(2, 0) +-#define STV090x_P2_NNOSPLHT1 STV090x_Px_NNOSPLHTy(2, 1) +-#define STV090x_OFFST_Px_NOSPLHT_NORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSPLHT_NORMED_FIELD 8 +- +-#define STV090x_Px_NNOSPLHy(__x, __y) (0xf487 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NNOSPLH0 STV090x_Px_NNOSPLHy(1, 0) +-#define STV090x_P1_NNOSPLH1 STV090x_Px_NNOSPLHy(1, 1) +-#define STV090x_P2_NNOSPLH0 STV090x_Px_NNOSPLHy(2, 0) +-#define STV090x_P2_NNOSPLH1 STV090x_Px_NNOSPLHy(2, 1) +-#define STV090x_OFFST_Px_NOSPLH_NORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSPLH_NORMED_FIELD 8 +- +-#define STV090x_Px_NOSDATATy(__x, __y) (0xf489 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NOSDATAT0 STV090x_Px_NOSDATATy(1, 0) +-#define STV090x_P1_NOSDATAT1 STV090x_Px_NOSDATATy(1, 1) +-#define STV090x_P2_NOSDATAT0 STV090x_Px_NOSDATATy(2, 0) +-#define STV090x_P2_NOSDATAT1 STV090x_Px_NOSDATATy(2, 1) +-#define STV090x_OFFST_Px_NOSDATAT_UNNORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSDATAT_UNNORMED_FIELD 8 +- +-#define STV090x_Px_NOSDATAy(__x, __y) (0xf48b - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NOSDATA0 STV090x_Px_NOSDATAy(1, 0) +-#define STV090x_P1_NOSDATA1 STV090x_Px_NOSDATAy(1, 1) +-#define STV090x_P2_NOSDATA0 STV090x_Px_NOSDATAy(2, 0) +-#define STV090x_P2_NOSDATA1 STV090x_Px_NOSDATAy(2, 1) +-#define STV090x_OFFST_Px_NOSDATA_UNNORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSDATA_UNNORMED_FIELD 8 +- +-#define STV090x_Px_NOSPLHTy(__x, __y) (0xf48d - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NOSPLHT0 STV090x_Px_NOSPLHTy(1, 0) +-#define STV090x_P1_NOSPLHT1 STV090x_Px_NOSPLHTy(1, 1) +-#define STV090x_P2_NOSPLHT0 STV090x_Px_NOSPLHTy(2, 0) +-#define STV090x_P2_NOSPLHT1 STV090x_Px_NOSPLHTy(2, 1) +-#define STV090x_OFFST_Px_NOSPLHT_UNNORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSPLHT_UNNORMED_FIELD 8 +- +-#define STV090x_Px_NOSPLHy(__x, __y) (0xf48f - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_NOSPLH0 STV090x_Px_NOSPLHy(1, 0) +-#define STV090x_P1_NOSPLH1 STV090x_Px_NOSPLHy(1, 1) +-#define STV090x_P2_NOSPLH0 STV090x_Px_NOSPLHy(2, 0) +-#define STV090x_P2_NOSPLH1 STV090x_Px_NOSPLHy(2, 1) +-#define STV090x_OFFST_Px_NOSPLH_UNNORMED_FIELD 0 +-#define STV090x_WIDTH_Px_NOSPLH_UNNORMED_FIELD 8 +- +-#define STV090x_Px_CAR2CFG(__x) (0xf490 - (__x - 1) * 0x200) +-#define STV090x_P1_CAR2CFG STV090x_Px_CAR2CFG(1) +-#define STV090x_P2_CAR2CFG STV090x_Px_CAR2CFG(2) +-#define STV090x_OFFST_Px_PN4_SELECT_FIELD 6 +-#define STV090x_WIDTH_Px_PN4_SELECT_FIELD 1 +-#define STV090x_OFFST_Px_CFR2_STOPDVBS1_FIELD 5 +-#define STV090x_WIDTH_Px_CFR2_STOPDVBS1_FIELD 1 +-#define STV090x_OFFST_Px_ROTA2ON_FIELD 2 +-#define STV090x_WIDTH_Px_ROTA2ON_FIELD 1 +-#define STV090x_OFFST_Px_PH_DET_ALGO2_FIELD 0 +-#define STV090x_WIDTH_Px_PH_DET_ALGO2_FIELD 2 +- +-#define STV090x_Px_ACLC2(__x) (0xf491 - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC2 STV090x_Px_ACLC2(1) +-#define STV090x_P2_ACLC2 STV090x_Px_ACLC2(2) +-#define STV090x_OFFST_Px_CAR2_ALPHA_MANT_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2_ALPHA_MANT_FIELD 2 +-#define STV090x_OFFST_Px_CAR2_ALPHA_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2_ALPHA_EXP_FIELD 4 +- +-#define STV090x_Px_BCLC2(__x) (0xf492 - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC2 STV090x_Px_BCLC2(1) +-#define STV090x_P2_BCLC2 STV090x_Px_BCLC2(2) +-#define STV090x_OFFST_Px_CAR2_BETA_MANT_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2_BETA_MANT_FIELD 2 +-#define STV090x_OFFST_Px_CAR2_BETA_EXP_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2_BETA_EXP_FIELD 4 +- +-#define STV090x_Px_ACLC2S2Q(__x) (0xf497 - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC2S2Q STV090x_Px_ACLC2S2Q(1) +-#define STV090x_P2_ACLC2S2Q STV090x_Px_ACLC2S2Q(2) +-#define STV090x_OFFST_Px_ENAB_SPSKSYMB_FIELD 7 +-#define STV090x_WIDTH_Px_ENAB_SPSKSYMB_FIELD 1 +-#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_Q_ALPH_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_Q_ALPH_E_FIELD 4 +- +-#define STV090x_Px_ACLC2S28(__x) (0xf498 - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC2S28 STV090x_Px_ACLC2S28(1) +-#define STV090x_P2_ACLC2S28 STV090x_Px_ACLC2S28(2) +-#define STV090x_OFFST_Px_CAR2S2_8_ALPH_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_8_ALPH_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_8_ALPH_E_FIELD 4 +- +-#define STV090x_Px_ACLC2S216A(__x) (0xf499 - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC2S216A STV090x_Px_ACLC2S216A(1) +-#define STV090x_P2_ACLC2S216A STV090x_Px_ACLC2S216A(2) +-#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4 +- +-#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200) +-#define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1) +-#define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2) +-#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_32A_ALPH_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_32A_ALPH_E_FIELD 4 +- +-#define STV090x_Px_BCLC2S2Q(__x) (0xf49c - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC2S2Q STV090x_Px_BCLC2S2Q(1) +-#define STV090x_P2_BCLC2S2Q STV090x_Px_BCLC2S2Q(2) +-#define STV090x_OFFST_Px_CAR2S2_Q_BETA_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_Q_BETA_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_Q_BETA_E_FIELD 4 +- +-#define STV090x_Px_BCLC2S28(__x) (0xf49d - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC2S28 STV090x_Px_BCLC2S28(1) +-#define STV090x_P2_BCLC2S28 STV090x_Px_BCLC2S28(2) +-#define STV090x_OFFST_Px_CAR2S2_8_BETA_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_8_BETA_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4 +- +-#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) +-#define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(2) +-#define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4 +- +-#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) +-#define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) +-#define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(2) +-#define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 +-#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_M_FIELD 2 +-#define STV090x_OFFST_Px_CAR2S2_32A_BETA_E_FIELD 0 +-#define STV090x_WIDTH_Px_CAR2S2_32A_BETA_E_FIELD 4 +- +-#define STV090x_Px_PLROOT2(__x) (0xf4ac - (__x - 1) * 0x200) +-#define STV090x_P1_PLROOT2 STV090x_Px_PLROOT2(1) +-#define STV090x_P2_PLROOT2 STV090x_Px_PLROOT2(2) +-#define STV090x_OFFST_Px_PLSCRAMB_MODE_FIELD 2 +-#define STV090x_WIDTH_Px_PLSCRAMB_MODE_FIELD 2 +-#define STV090x_OFFST_Px_PLSCRAMB_ROOT_FIELD 0 +-#define STV090x_WIDTH_Px_PLSCRAMB_ROOT_FIELD 2 +- +-#define STV090x_Px_PLROOT1(__x) (0xf4ad - (__x - 1) * 0x200) +-#define STV090x_P1_PLROOT1 STV090x_Px_PLROOT1(1) +-#define STV090x_P2_PLROOT1 STV090x_Px_PLROOT1(2) +-#define STV090x_OFFST_Px_PLSCRAMB_ROOT1_FIELD 0 +-#define STV090x_WIDTH_Px_PLSCRAMB_ROOT1_FIELD 8 +- +-#define STV090x_Px_PLROOT0(__x) (0xf4ae - (__x - 1) * 0x200) +-#define STV090x_P1_PLROOT0 STV090x_Px_PLROOT0(1) +-#define STV090x_P2_PLROOT0 STV090x_Px_PLROOT0(2) +-#define STV090x_OFFST_Px_PLSCRAMB_ROOT0_FIELD 0 +-#define STV090x_WIDTH_Px_PLSCRAMB_ROOT0_FIELD 8 +- +-#define STV090x_Px_MODCODLST0(__x) (0xf4b0 - (__x - 1) * 0x200) /* check */ +-#define STV090x_P1_MODCODLST0 STV090x_Px_MODCODLST0(1) +-#define STV090x_P2_MODCODLST0 STV090x_Px_MODCODLST0(2) +- +-#define STV090x_Px_MODCODLST1(__x) (0xf4b1 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1) +-#define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2) +-#define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4 +-#define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4 +- +-#define STV090x_Px_MODCODLST2(__x) (0xf4b2 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST2 STV090x_Px_MODCODLST2(1) +-#define STV090x_P2_MODCODLST2 STV090x_Px_MODCODLST2(2) +-#define STV090x_OFFST_Px_DIS_32PSK_8_9_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_32PSK_8_9_FIELD 4 +-#define STV090x_OFFST_Px_DIS_32PSK_5_6_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_32PSK_5_6_FIELD 4 +- +-#define STV090x_Px_MODCODLST3(__x) (0xf4b3 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST3 STV090x_Px_MODCODLST3(1) +-#define STV090x_P2_MODCODLST3 STV090x_Px_MODCODLST3(2) +-#define STV090x_OFFST_Px_DIS_32PSK_4_5_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_32PSK_4_5_FIELD 4 +-#define STV090x_OFFST_Px_DIS_32PSK_3_4_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_32PSK_3_4_FIELD 4 +- +-#define STV090x_Px_MODCODLST4(__x) (0xf4b4 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST4 STV090x_Px_MODCODLST4(1) +-#define STV090x_P2_MODCODLST4 STV090x_Px_MODCODLST4(2) +-#define STV090x_OFFST_Px_DIS_16PSK_9_10_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_16PSK_9_10_FIELD 4 +-#define STV090x_OFFST_Px_DIS_16PSK_8_9_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_16PSK_8_9_FIELD 4 +- +-#define STV090x_Px_MODCODLST5(__x) (0xf4b5 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST5 STV090x_Px_MODCODLST5(1) +-#define STV090x_P2_MODCODLST5 STV090x_Px_MODCODLST5(2) +-#define STV090x_OFFST_Px_DIS_16PSK_5_6_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_16PSK_5_6_FIELD 4 +-#define STV090x_OFFST_Px_DIS_16PSK_4_5_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_16PSK_4_5_FIELD 4 +- +-#define STV090x_Px_MODCODLST6(__x) (0xf4b6 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST6 STV090x_Px_MODCODLST6(1) +-#define STV090x_P2_MODCODLST6 STV090x_Px_MODCODLST6(2) +-#define STV090x_OFFST_Px_DIS_16PSK_3_4_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_16PSK_3_4_FIELD 4 +-#define STV090x_OFFST_Px_DIS_16PSK_2_3_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_16PSK_2_3_FIELD 4 +- +-#define STV090x_Px_MODCODLST7(__x) (0xf4b7 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST7 STV090x_Px_MODCODLST7(1) +-#define STV090x_P2_MODCODLST7 STV090x_Px_MODCODLST7(2) +-#define STV090x_OFFST_Px_DIS_8P_9_10_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_8P_9_10_FIELD 4 +-#define STV090x_OFFST_Px_DIS_8P_8_9_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_8P_8_9_FIELD 4 +- +-#define STV090x_Px_MODCODLST8(__x) (0xf4b8 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST8 STV090x_Px_MODCODLST8(1) +-#define STV090x_P2_MODCODLST8 STV090x_Px_MODCODLST8(2) +-#define STV090x_OFFST_Px_DIS_8P_5_6_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_8P_5_6_FIELD 4 +-#define STV090x_OFFST_Px_DIS_8P_3_4_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_8P_3_4_FIELD 4 +- +-#define STV090x_Px_MODCODLST9(__x) (0xf4b9 - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLST9 STV090x_Px_MODCODLST9(1) +-#define STV090x_P2_MODCODLST9 STV090x_Px_MODCODLST9(2) +-#define STV090x_OFFST_Px_DIS_8P_2_3_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_8P_2_3_FIELD 4 +-#define STV090x_OFFST_Px_DIS_8P_3_5_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_8P_3_5_FIELD 4 +- +-#define STV090x_Px_MODCODLSTA(__x) (0xf4ba - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTA STV090x_Px_MODCODLSTA(1) +-#define STV090x_P2_MODCODLSTA STV090x_Px_MODCODLSTA(2) +-#define STV090x_OFFST_Px_DIS_QP_9_10_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_9_10_FIELD 4 +-#define STV090x_OFFST_Px_DIS_QP_8_9_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_QP_8_9_FIELD 4 +- +-#define STV090x_Px_MODCODLSTB(__x) (0xf4bb - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTB STV090x_Px_MODCODLSTB(1) +-#define STV090x_P2_MODCODLSTB STV090x_Px_MODCODLSTB(2) +-#define STV090x_OFFST_Px_DIS_QP_5_6_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_5_6_FIELD 4 +-#define STV090x_OFFST_Px_DIS_QP_4_5_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_QP_4_5_FIELD 4 +- +-#define STV090x_Px_MODCODLSTC(__x) (0xf4bc - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTC STV090x_Px_MODCODLSTC(1) +-#define STV090x_P2_MODCODLSTC STV090x_Px_MODCODLSTC(2) +-#define STV090x_OFFST_Px_DIS_QP_3_4_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_3_4_FIELD 4 +-#define STV090x_OFFST_Px_DIS_QP_2_3_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_QP_2_3_FIELD 4 +- +-#define STV090x_Px_MODCODLSTD(__x) (0xf4bd - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTD STV090x_Px_MODCODLSTD(1) +-#define STV090x_P2_MODCODLSTD STV090x_Px_MODCODLSTD(2) +-#define STV090x_OFFST_Px_DIS_QP_3_5_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_3_5_FIELD 4 +-#define STV090x_OFFST_Px_DIS_QP_1_2_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_QP_1_2_FIELD 4 +- +-#define STV090x_Px_MODCODLSTE(__x) (0xf4be - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTE STV090x_Px_MODCODLSTE(1) +-#define STV090x_P2_MODCODLSTE STV090x_Px_MODCODLSTE(2) +-#define STV090x_OFFST_Px_DIS_QP_2_5_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_2_5_FIELD 4 +-#define STV090x_OFFST_Px_DIS_QP_1_3_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_QP_1_3_FIELD 4 +- +-#define STV090x_Px_MODCODLSTF(__x) (0xf4bf - (__x - 1) * 0x200) +-#define STV090x_P1_MODCODLSTF STV090x_Px_MODCODLSTF(1) +-#define STV090x_P2_MODCODLSTF STV090x_Px_MODCODLSTF(2) +-#define STV090x_OFFST_Px_DIS_QP_1_4_FIELD 4 +-#define STV090x_WIDTH_Px_DIS_QP_1_4_FIELD 4 +- +-#define STV090x_Px_GAUSSR0(__x) (0xf4c0 - (__x - 1) * 0x200) +-#define STV090x_P1_GAUSSR0 STV090x_Px_GAUSSR0(1) +-#define STV090x_P2_GAUSSR0 STV090x_Px_GAUSSR0(2) +-#define STV090x_OFFST_Px_EN_CCIMODE_FIELD 7 +-#define STV090x_WIDTH_Px_EN_CCIMODE_FIELD 1 +-#define STV090x_OFFST_Px_R0_GAUSSIEN_FIELD 0 +-#define STV090x_WIDTH_Px_R0_GAUSSIEN_FIELD 7 +- +-#define STV090x_Px_CCIR0(__x) (0xf4c1 - (__x - 1) * 0x200) +-#define STV090x_P1_CCIR0 STV090x_Px_CCIR0(1) +-#define STV090x_P2_CCIR0 STV090x_Px_CCIR0(2) +-#define STV090x_OFFST_Px_CCIDETECT_PLH_FIELD 7 +-#define STV090x_WIDTH_Px_CCIDETECT_PLH_FIELD 1 +-#define STV090x_OFFST_Px_R0_CCI_FIELD 0 +-#define STV090x_WIDTH_Px_R0_CCI_FIELD 7 +- +-#define STV090x_Px_CCIQUANT(__x) (0xf4c2 - (__x - 1) * 0x200) +-#define STV090x_P1_CCIQUANT STV090x_Px_CCIQUANT(1) +-#define STV090x_P2_CCIQUANT STV090x_Px_CCIQUANT(2) +-#define STV090x_OFFST_Px_CCI_BETA_FIELD 5 +-#define STV090x_WIDTH_Px_CCI_BETA_FIELD 3 +-#define STV090x_OFFST_Px_CCI_QUANT_FIELD 0 +-#define STV090x_WIDTH_Px_CCI_QUANT_FIELD 5 +- +-#define STV090x_Px_CCITHRESH(__x) (0xf4c3 - (__x - 1) * 0x200) +-#define STV090x_P1_CCITHRESH STV090x_Px_CCITHRESH(1) +-#define STV090x_P2_CCITHRESH STV090x_Px_CCITHRESH(2) +-#define STV090x_OFFST_Px_CCI_THRESHOLD_FIELD 0 +-#define STV090x_WIDTH_Px_CCI_THRESHOLD_FIELD 8 +- +-#define STV090x_Px_CCIACC(__x) (0xf4c4 - (__x - 1) * 0x200) +-#define STV090x_P1_CCIACC STV090x_Px_CCIACC(1) +-#define STV090x_P2_CCIACC STV090x_Px_CCIACC(2) +-#define STV090x_OFFST_Px_CCI_VALUE_FIELD 0 +-#define STV090x_WIDTH_Px_CCI_VALUE_FIELD 8 +- +-#define STV090x_Px_DMDRESCFG(__x) (0xF4C6 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDRESCFG STV090x_Px_DMDRESCFG(1) +-#define STV090x_P2_DMDRESCFG STV090x_Px_DMDRESCFG(2) +-#define STV090x_OFFST_Px_DMDRES_RESET_FIELD 7 +-#define STV090x_WIDTH_Px_DMDRES_RESET_FIELD 1 +- +-#define STV090x_Px_DMDRESADR(__x) (0xF4C7 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDRESADR STV090x_Px_DMDRESADR(1) +-#define STV090x_P2_DMDRESADR STV090x_Px_DMDRESADR(2) +-#define STV090x_OFFST_Px_DMDRES_RESNBR_FIELD 0 +-#define STV090x_WIDTH_Px_DMDRES_RESNBR_FIELD 4 +- +-#define STV090x_Px_DMDRESDATAy(__x, __y) (0xF4C8 - (__x - 1) * 0x200 + (7 - __y)) +-#define STV090x_P1_DMDRESDATA0 STV090x_Px_DMDRESDATAy(1, 0) +-#define STV090x_P1_DMDRESDATA1 STV090x_Px_DMDRESDATAy(1, 1) +-#define STV090x_P1_DMDRESDATA2 STV090x_Px_DMDRESDATAy(1, 2) +-#define STV090x_P1_DMDRESDATA3 STV090x_Px_DMDRESDATAy(1, 3) +-#define STV090x_P1_DMDRESDATA4 STV090x_Px_DMDRESDATAy(1, 4) +-#define STV090x_P1_DMDRESDATA5 STV090x_Px_DMDRESDATAy(1, 5) +-#define STV090x_P1_DMDRESDATA6 STV090x_Px_DMDRESDATAy(1, 6) +-#define STV090x_P1_DMDRESDATA7 STV090x_Px_DMDRESDATAy(1, 7) +-#define STV090x_P2_DMDRESDATA0 STV090x_Px_DMDRESDATAy(2, 0) +-#define STV090x_P2_DMDRESDATA1 STV090x_Px_DMDRESDATAy(2, 1) +-#define STV090x_P2_DMDRESDATA2 STV090x_Px_DMDRESDATAy(2, 2) +-#define STV090x_P2_DMDRESDATA3 STV090x_Px_DMDRESDATAy(2, 3) +-#define STV090x_P2_DMDRESDATA4 STV090x_Px_DMDRESDATAy(2, 4) +-#define STV090x_P2_DMDRESDATA5 STV090x_Px_DMDRESDATAy(2, 5) +-#define STV090x_P2_DMDRESDATA6 STV090x_Px_DMDRESDATAy(2, 6) +-#define STV090x_P2_DMDRESDATA7 STV090x_Px_DMDRESDATAy(2, 7) +-#define STV090x_OFFST_Px_DMDRES_DATA_FIELD 0 +-#define STV090x_WIDTH_Px_DMDRES_DATA_FIELD 8 +- +-#define STV090x_Px_FFEIy(__x, __y) (0xf4d0 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) +-#define STV090x_P1_FFEI1 STV090x_Px_FFEIy(1, 1) +-#define STV090x_P1_FFEI2 STV090x_Px_FFEIy(1, 2) +-#define STV090x_P1_FFEI3 STV090x_Px_FFEIy(1, 3) +-#define STV090x_P1_FFEI4 STV090x_Px_FFEIy(1, 4) +-#define STV090x_P2_FFEI1 STV090x_Px_FFEIy(2, 1) +-#define STV090x_P2_FFEI2 STV090x_Px_FFEIy(2, 2) +-#define STV090x_P2_FFEI3 STV090x_Px_FFEIy(2, 3) +-#define STV090x_P2_FFEI4 STV090x_Px_FFEIy(2, 4) +-#define STV090x_OFFST_Px_FFE_ACCIy_FIELD 0 +-#define STV090x_WIDTH_Px_FFE_ACCIy_FIELD 8 +- +-#define STV090x_Px_FFEQy(__x, __y) (0xf4d1 - (__x - 1) * 0x200 + 0x2 * (__y - 1)) +-#define STV090x_P1_FFEQ1 STV090x_Px_FFEQy(1, 1) +-#define STV090x_P1_FFEQ2 STV090x_Px_FFEQy(1, 2) +-#define STV090x_P1_FFEQ3 STV090x_Px_FFEQy(1, 3) +-#define STV090x_P1_FFEQ4 STV090x_Px_FFEQy(1, 4) +-#define STV090x_P2_FFEQ1 STV090x_Px_FFEQy(2, 1) +-#define STV090x_P2_FFEQ2 STV090x_Px_FFEQy(2, 2) +-#define STV090x_P2_FFEQ3 STV090x_Px_FFEQy(2, 3) +-#define STV090x_P2_FFEQ4 STV090x_Px_FFEQy(2, 4) +-#define STV090x_OFFST_Px_FFE_ACCQy_FIELD 0 +-#define STV090x_WIDTH_Px_FFE_ACCQy_FIELD 8 +- +-#define STV090x_Px_FFECFG(__x) (0xf4d8 - (__x - 1) * 0x200) +-#define STV090x_P1_FFECFG STV090x_Px_FFECFG(1) +-#define STV090x_P2_FFECFG STV090x_Px_FFECFG(2) +-#define STV090x_OFFST_Px_EQUALFFE_ON_FIELD 6 +-#define STV090x_WIDTH_Px_EQUALFFE_ON_FIELD 1 +- +-#define STV090x_Px_SMAPCOEF7(__x) (0xf500 - (__x - 1) * 0x200) +-#define STV090x_P1_SMAPCOEF7 STV090x_Px_SMAPCOEF7(1) +-#define STV090x_P2_SMAPCOEF7 STV090x_Px_SMAPCOEF7(2) +-#define STV090x_OFFST_Px_DIS_QSCALE_FIELD 7 +-#define STV090x_WIDTH_Px_DIS_QSCALE_FIELD 1 +-#define STV090x_OFFST_Px_SMAPCOEF_Q_LLR12_FIELD 0 +-#define STV090x_WIDTH_Px_SMAPCOEF_Q_LLR12_FIELD 7 +- +-#define STV090x_Px_SMAPCOEF6(__x) (0xf501 - (__x - 1) * 0x200) +-#define STV090x_P1_SMAPCOEF6 STV090x_Px_SMAPCOEF6(1) +-#define STV090x_P2_SMAPCOEF6 STV090x_Px_SMAPCOEF6(2) +-#define STV090x_OFFST_Px_ADJ_8PSKLLR1_FIELD 2 +-#define STV090x_WIDTH_Px_ADJ_8PSKLLR1_FIELD 1 +-#define STV090x_OFFST_Px_OLD_8PSKLLR1_FIELD 1 +-#define STV090x_WIDTH_Px_OLD_8PSKLLR1_FIELD 1 +-#define STV090x_OFFST_Px_DIS_AB8PSK_FIELD 0 +-#define STV090x_WIDTH_Px_DIS_AB8PSK_FIELD 1 +- +-#define STV090x_Px_SMAPCOEF5(__x) (0xf502 - (__x - 1) * 0x200) +-#define STV090x_P1_SMAPCOEF5 STV090x_Px_SMAPCOEF5(1) +-#define STV090x_P2_SMAPCOEF5 STV090x_Px_SMAPCOEF5(2) +-#define STV090x_OFFST_Px_DIS_8SCALE_FIELD 7 +-#define STV090x_WIDTH_Px_DIS_8SCALE_FIELD 1 +-#define STV090x_OFFST_Px_SMAPCOEF_8P_LLR23_FIELD 0 +-#define STV090x_WIDTH_Px_SMAPCOEF_8P_LLR23_FIELD 7 +- +-#define STV090x_Px_DMDPLHSTAT(__x) (0xF520 - (__x - 1) * 0x200) +-#define STV090x_P1_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(1) +-#define STV090x_P2_DMDPLHSTAT STV090x_Px_DMDPLHSTAT(2) +-#define STV090x_OFFST_Px_PLH_STATISTIC_FIELD 0 +-#define STV090x_WIDTH_Px_PLH_STATISTIC_FIELD 8 +- +-#define STV090x_Px_LOCKTIMEy(__x, __y) (0xF525 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_LOCKTIME0 STV090x_Px_LOCKTIMEy(1, 0) +-#define STV090x_P1_LOCKTIME1 STV090x_Px_LOCKTIMEy(1, 1) +-#define STV090x_P1_LOCKTIME2 STV090x_Px_LOCKTIMEy(1, 2) +-#define STV090x_P1_LOCKTIME3 STV090x_Px_LOCKTIMEy(1, 3) +-#define STV090x_P2_LOCKTIME0 STV090x_Px_LOCKTIMEy(2, 0) +-#define STV090x_P2_LOCKTIME1 STV090x_Px_LOCKTIMEy(2, 1) +-#define STV090x_P2_LOCKTIME2 STV090x_Px_LOCKTIMEy(2, 2) +-#define STV090x_P2_LOCKTIME3 STV090x_Px_LOCKTIMEy(2, 3) +-#define STV090x_OFFST_Px_DEMOD_LOCKTIME_FIELD 0 +-#define STV090x_WIDTH_Px_DEMOD_LOCKTIME_FIELD 8 +- +-#define STV090x_Px_TNRCFG(__x) (0xf4e0 - (__x - 1) * 0x200) /* check */ +-#define STV090x_P1_TNRCFG STV090x_Px_TNRCFG(1) +-#define STV090x_P2_TNRCFG STV090x_Px_TNRCFG(2) +- +-#define STV090x_Px_TNRCFG2(__x) (0xf4e1 - (__x - 1) * 0x200) +-#define STV090x_P1_TNRCFG2 STV090x_Px_TNRCFG2(1) +-#define STV090x_P2_TNRCFG2 STV090x_Px_TNRCFG2(2) +-#define STV090x_OFFST_Px_TUN_IQSWAP_FIELD 7 +-#define STV090x_WIDTH_Px_TUN_IQSWAP_FIELD 1 +- +-#define STV090x_Px_VITSCALE(__x) (0xf532 - (__x - 1) * 0x200) +-#define STV090x_P1_VITSCALE STV090x_Px_VITSCALE(1) +-#define STV090x_P2_VITSCALE STV090x_Px_VITSCALE(2) +-#define STV090x_OFFST_Px_NVTH_NOSRANGE_FIELD 7 +-#define STV090x_WIDTH_Px_NVTH_NOSRANGE_FIELD 1 +-#define STV090x_OFFST_Px_VERROR_MAXMODE_FIELD 6 +-#define STV090x_WIDTH_Px_VERROR_MAXMODE_FIELD 1 +-#define STV090x_OFFST_Px_NSLOWSN_LOCKED_FIELD 3 +-#define STV090x_WIDTH_Px_NSLOWSN_LOCKED_FIELD 1 +-#define STV090x_OFFST_Px_DIS_RSFLOCK_FIELD 1 +-#define STV090x_WIDTH_Px_DIS_RSFLOCK_FIELD 1 +- +-#define STV090x_Px_FECM(__x) (0xf533 - (__x - 1) * 0x200) +-#define STV090x_P1_FECM STV090x_Px_FECM(1) +-#define STV090x_P2_FECM STV090x_Px_FECM(2) +-#define STV090x_OFFST_Px_DSS_DVB_FIELD 7 +-#define STV090x_WIDTH_Px_DSS_DVB_FIELD 1 +-#define STV090x_OFFST_Px_DSS_SRCH_FIELD 4 +-#define STV090x_WIDTH_Px_DSS_SRCH_FIELD 1 +-#define STV090x_OFFST_Px_SYNCVIT_FIELD 1 +-#define STV090x_WIDTH_Px_SYNCVIT_FIELD 1 +-#define STV090x_OFFST_Px_IQINV_FIELD 0 +-#define STV090x_WIDTH_Px_IQINV_FIELD 1 +- +-#define STV090x_Px_VTH12(__x) (0xf534 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH12 STV090x_Px_VTH12(1) +-#define STV090x_P2_VTH12 STV090x_Px_VTH12(2) +-#define STV090x_OFFST_Px_VTH12_FIELD 0 +-#define STV090x_WIDTH_Px_VTH12_FIELD 8 +- +-#define STV090x_Px_VTH23(__x) (0xf535 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH23 STV090x_Px_VTH23(1) +-#define STV090x_P2_VTH23 STV090x_Px_VTH23(2) +-#define STV090x_OFFST_Px_VTH23_FIELD 0 +-#define STV090x_WIDTH_Px_VTH23_FIELD 8 +- +-#define STV090x_Px_VTH34(__x) (0xf536 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH34 STV090x_Px_VTH34(1) +-#define STV090x_P2_VTH34 STV090x_Px_VTH34(2) +-#define STV090x_OFFST_Px_VTH34_FIELD 0 +-#define STV090x_WIDTH_Px_VTH34_FIELD 8 +- +-#define STV090x_Px_VTH56(__x) (0xf537 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH56 STV090x_Px_VTH56(1) +-#define STV090x_P2_VTH56 STV090x_Px_VTH56(2) +-#define STV090x_OFFST_Px_VTH56_FIELD 0 +-#define STV090x_WIDTH_Px_VTH56_FIELD 8 +- +-#define STV090x_Px_VTH67(__x) (0xf538 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH67 STV090x_Px_VTH67(1) +-#define STV090x_P2_VTH67 STV090x_Px_VTH67(2) +-#define STV090x_OFFST_Px_VTH67_FIELD 0 +-#define STV090x_WIDTH_Px_VTH67_FIELD 8 +- +-#define STV090x_Px_VTH78(__x) (0xf539 - (__x - 1) * 0x200) +-#define STV090x_P1_VTH78 STV090x_Px_VTH78(1) +-#define STV090x_P2_VTH78 STV090x_Px_VTH78(2) +-#define STV090x_OFFST_Px_VTH78_FIELD 0 +-#define STV090x_WIDTH_Px_VTH78_FIELD 8 +- +-#define STV090x_Px_VITCURPUN(__x) (0xf53a - (__x - 1) * 0x200) +-#define STV090x_P1_VITCURPUN STV090x_Px_VITCURPUN(1) +-#define STV090x_P2_VITCURPUN STV090x_Px_VITCURPUN(2) +-#define STV090x_OFFST_Px_VIT_CURPUN_FIELD 0 +-#define STV090x_WIDTH_Px_VIT_CURPUN_FIELD 5 +- +-#define STV090x_Px_VERROR(__x) (0xf53b - (__x - 1) * 0x200) +-#define STV090x_P1_VERROR STV090x_Px_VERROR(1) +-#define STV090x_P2_VERROR STV090x_Px_VERROR(2) +-#define STV090x_OFFST_Px_REGERR_VIT_FIELD 0 +-#define STV090x_WIDTH_Px_REGERR_VIT_FIELD 8 +- +-#define STV090x_Px_PRVIT(__x) (0xf53c - (__x - 1) * 0x200) +-#define STV090x_P1_PRVIT STV090x_Px_PRVIT(1) +-#define STV090x_P2_PRVIT STV090x_Px_PRVIT(2) +-#define STV090x_OFFST_Px_DIS_VTHLOCK_FIELD 6 +-#define STV090x_WIDTH_Px_DIS_VTHLOCK_FIELD 1 +-#define STV090x_OFFST_Px_E7_8VIT_FIELD 5 +-#define STV090x_WIDTH_Px_E7_8VIT_FIELD 1 +-#define STV090x_OFFST_Px_E6_7VIT_FIELD 4 +-#define STV090x_WIDTH_Px_E6_7VIT_FIELD 1 +-#define STV090x_OFFST_Px_E5_6VIT_FIELD 3 +-#define STV090x_WIDTH_Px_E5_6VIT_FIELD 1 +-#define STV090x_OFFST_Px_E3_4VIT_FIELD 2 +-#define STV090x_WIDTH_Px_E3_4VIT_FIELD 1 +-#define STV090x_OFFST_Px_E2_3VIT_FIELD 1 +-#define STV090x_WIDTH_Px_E2_3VIT_FIELD 1 +-#define STV090x_OFFST_Px_E1_2VIT_FIELD 0 +-#define STV090x_WIDTH_Px_E1_2VIT_FIELD 1 +- +-#define STV090x_Px_VAVSRVIT(__x) (0xf53d - (__x - 1) * 0x200) +-#define STV090x_P1_VAVSRVIT STV090x_Px_VAVSRVIT(1) +-#define STV090x_P2_VAVSRVIT STV090x_Px_VAVSRVIT(2) +-#define STV090x_OFFST_Px_SNVIT_FIELD 4 +-#define STV090x_WIDTH_Px_SNVIT_FIELD 2 +-#define STV090x_OFFST_Px_TOVVIT_FIELD 2 +-#define STV090x_WIDTH_Px_TOVVIT_FIELD 2 +-#define STV090x_OFFST_Px_HYPVIT_FIELD 0 +-#define STV090x_WIDTH_Px_HYPVIT_FIELD 2 +- +-#define STV090x_Px_VSTATUSVIT(__x) (0xf53e - (__x - 1) * 0x200) +-#define STV090x_P1_VSTATUSVIT STV090x_Px_VSTATUSVIT(1) +-#define STV090x_P2_VSTATUSVIT STV090x_Px_VSTATUSVIT(2) +-#define STV090x_OFFST_Px_PRFVIT_FIELD 4 +-#define STV090x_WIDTH_Px_PRFVIT_FIELD 1 +-#define STV090x_OFFST_Px_LOCKEDVIT_FIELD 3 +-#define STV090x_WIDTH_Px_LOCKEDVIT_FIELD 1 +- +-#define STV090x_Px_VTHINUSE(__x) (0xf53f - (__x - 1) * 0x200) +-#define STV090x_P1_VTHINUSE STV090x_Px_VTHINUSE(1) +-#define STV090x_P2_VTHINUSE STV090x_Px_VTHINUSE(2) +-#define STV090x_OFFST_Px_VIT_INUSE_FIELD 0 +-#define STV090x_WIDTH_Px_VIT_INUSE_FIELD 8 +- +-#define STV090x_Px_KDIV12(__x) (0xf540 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV12 STV090x_Px_KDIV12(1) +-#define STV090x_P2_KDIV12 STV090x_Px_KDIV12(2) +-#define STV090x_OFFST_Px_K_DIVIDER_12_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_12_FIELD 7 +- +-#define STV090x_Px_KDIV23(__x) (0xf541 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV23 STV090x_Px_KDIV23(1) +-#define STV090x_P2_KDIV23 STV090x_Px_KDIV23(2) +-#define STV090x_OFFST_Px_K_DIVIDER_23_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_23_FIELD 7 +- +-#define STV090x_Px_KDIV34(__x) (0xf542 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV34 STV090x_Px_KDIV34(1) +-#define STV090x_P2_KDIV34 STV090x_Px_KDIV34(2) +-#define STV090x_OFFST_Px_K_DIVIDER_34_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_34_FIELD 7 +- +-#define STV090x_Px_KDIV56(__x) (0xf543 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV56 STV090x_Px_KDIV56(1) +-#define STV090x_P2_KDIV56 STV090x_Px_KDIV56(2) +-#define STV090x_OFFST_Px_K_DIVIDER_56_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_56_FIELD 7 +- +-#define STV090x_Px_KDIV67(__x) (0xf544 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV67 STV090x_Px_KDIV67(1) +-#define STV090x_P2_KDIV67 STV090x_Px_KDIV67(2) +-#define STV090x_OFFST_Px_K_DIVIDER_67_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_67_FIELD 7 +- +-#define STV090x_Px_KDIV78(__x) (0xf545 - (__x - 1) * 0x200) +-#define STV090x_P1_KDIV78 STV090x_Px_KDIV78(1) +-#define STV090x_P2_KDIV78 STV090x_Px_KDIV78(2) +-#define STV090x_OFFST_Px_K_DIVIDER_78_FIELD 0 +-#define STV090x_WIDTH_Px_K_DIVIDER_78_FIELD 7 +- +-#define STV090x_Px_PDELCTRL1(__x) (0xf550 - (__x - 1) * 0x200) +-#define STV090x_P1_PDELCTRL1 STV090x_Px_PDELCTRL1(1) +-#define STV090x_P2_PDELCTRL1 STV090x_Px_PDELCTRL1(2) +-#define STV090x_OFFST_Px_INV_MISMASK_FIELD 7 +-#define STV090x_WIDTH_Px_INV_MISMASK_FIELD 1 +-#define STV090x_OFFST_Px_FILTER_EN_FIELD 5 +-#define STV090x_WIDTH_Px_FILTER_EN_FIELD 1 +-#define STV090x_OFFST_Px_EN_MIS00_FIELD 1 +-#define STV090x_WIDTH_Px_EN_MIS00_FIELD 1 +-#define STV090x_OFFST_Px_ALGOSWRST_FIELD 0 +-#define STV090x_WIDTH_Px_ALGOSWRST_FIELD 1 +- +-#define STV090x_Px_PDELCTRL2(__x) (0xf551 - (__x - 1) * 0x200) +-#define STV090x_P1_PDELCTRL2 STV090x_Px_PDELCTRL2(1) +-#define STV090x_P2_PDELCTRL2 STV090x_Px_PDELCTRL2(2) +-#define STV090x_OFFST_Px_FORCE_CONTINUOUS 7 +-#define STV090x_WIDTH_Px_FORCE_CONTINUOUS 1 +-#define STV090x_OFFST_Px_RESET_UPKO_COUNT 6 +-#define STV090x_WIDTH_Px_RESET_UPKO_COUNT 1 +-#define STV090x_OFFST_Px_USER_PKTDELIN_NB 5 +-#define STV090x_WIDTH_Px_USER_PKTDELIN_NB 1 +-#define STV090x_OFFST_Px_FORCE_LOCKED 4 +-#define STV090x_WIDTH_Px_FORCE_LOCKED 1 +-#define STV090x_OFFST_Px_DATA_UNBBSCRAM 3 +-#define STV090x_WIDTH_Px_DATA_UNBBSCRAM 1 +-#define STV090x_OFFST_Px_FORCE_LONGPACKET 2 +-#define STV090x_WIDTH_Px_FORCE_LONGPACKET 1 +-#define STV090x_OFFST_Px_FRAME_MODE_FIELD 1 +-#define STV090x_WIDTH_Px_FRAME_MODE_FIELD 1 +- +-#define STV090x_Px_HYSTTHRESH(__x) (0xf554 - (__x - 1) * 0x200) +-#define STV090x_P1_HYSTTHRESH STV090x_Px_HYSTTHRESH(1) +-#define STV090x_P2_HYSTTHRESH STV090x_Px_HYSTTHRESH(2) +-#define STV090x_OFFST_Px_UNLCK_THRESH_FIELD 4 +-#define STV090x_WIDTH_Px_UNLCK_THRESH_FIELD 4 +-#define STV090x_OFFST_Px_DELIN_LCK_THRESH_FIELD 0 +-#define STV090x_WIDTH_Px_DELIN_LCK_THRESH_FIELD 4 +- +-#define STV090x_Px_ISIENTRY(__x) (0xf55e - (__x - 1) * 0x200) +-#define STV090x_P1_ISIENTRY STV090x_Px_ISIENTRY(1) +-#define STV090x_P2_ISIENTRY STV090x_Px_ISIENTRY(2) +-#define STV090x_OFFST_Px_ISI_ENTRY_FIELD 0 +-#define STV090x_WIDTH_Px_ISI_ENTRY_FIELD 8 +- +-#define STV090x_Px_ISIBITENA(__x) (0xf55f - (__x - 1) * 0x200) +-#define STV090x_P1_ISIBITENA STV090x_Px_ISIBITENA(1) +-#define STV090x_P2_ISIBITENA STV090x_Px_ISIBITENA(2) +-#define STV090x_OFFST_Px_ISI_BIT_EN_FIELD 0 +-#define STV090x_WIDTH_Px_ISI_BIT_EN_FIELD 8 +- +-#define STV090x_Px_MATSTRy(__x, __y) (0xf561 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_MATSTR0 STV090x_Px_MATSTRy(1, 0) +-#define STV090x_P1_MATSTR1 STV090x_Px_MATSTRy(1, 1) +-#define STV090x_P2_MATSTR0 STV090x_Px_MATSTRy(2, 0) +-#define STV090x_P2_MATSTR1 STV090x_Px_MATSTRy(2, 1) +-#define STV090x_OFFST_Px_MATYPE_CURRENT_FIELD 0 +-#define STV090x_WIDTH_Px_MATYPE_CURRENT_FIELD 8 +- +-#define STV090x_Px_UPLSTRy(__x, __y) (0xf563 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_UPLSTR0 STV090x_Px_UPLSTRy(1, 0) +-#define STV090x_P1_UPLSTR1 STV090x_Px_UPLSTRy(1, 1) +-#define STV090x_P2_UPLSTR0 STV090x_Px_UPLSTRy(2, 0) +-#define STV090x_P2_UPLSTR1 STV090x_Px_UPLSTRy(2, 1) +-#define STV090x_OFFST_Px_UPL_CURRENT_FIELD 0 +-#define STV090x_WIDTH_Px_UPL_CURRENT_FIELD 8 +- +-#define STV090x_Px_DFLSTRy(__x, __y) (0xf565 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_DFLSTR0 STV090x_Px_DFLSTRy(1, 0) +-#define STV090x_P1_DFLSTR1 STV090x_Px_DFLSTRy(1, 1) +-#define STV090x_P2_DFLSTR0 STV090x_Px_DFLSTRy(2, 0) +-#define STV090x_P2_DFLSTR1 STV090x_Px_DFLSTRy(2, 1) +-#define STV090x_OFFST_Px_DFL_CURRENT_FIELD 0 +-#define STV090x_WIDTH_Px_DFL_CURRENT_FIELD 8 +- +-#define STV090x_Px_SYNCSTR(__x) (0xf566 - (__x - 1) * 0x200) +-#define STV090x_P1_SYNCSTR STV090x_Px_SYNCSTR(1) +-#define STV090x_P2_SYNCSTR STV090x_Px_SYNCSTR(2) +-#define STV090x_OFFST_Px_SYNC_CURRENT_FIELD 0 +-#define STV090x_WIDTH_Px_SYNC_CURRENT_FIELD 8 +- +-#define STV090x_Px_SYNCDSTRy(__x, __y) (0xf568 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_SYNCDSTR0 STV090x_Px_SYNCDSTRy(1, 0) +-#define STV090x_P1_SYNCDSTR1 STV090x_Px_SYNCDSTRy(1, 1) +-#define STV090x_P2_SYNCDSTR0 STV090x_Px_SYNCDSTRy(2, 0) +-#define STV090x_P2_SYNCDSTR1 STV090x_Px_SYNCDSTRy(2, 1) +-#define STV090x_OFFST_Px_SYNCD_CURRENT_FIELD 0 +-#define STV090x_WIDTH_Px_SYNCD_CURRENT_FIELD 8 +- +-#define STV090x_Px_PDELSTATUS1(__x) (0xf569 - (__x - 1) * 0x200) +-#define STV090x_P1_PDELSTATUS1 STV090x_Px_PDELSTATUS1(1) +-#define STV090x_P2_PDELSTATUS1 STV090x_Px_PDELSTATUS1(2) +-#define STV090x_OFFST_Px_PKTDELIN_LOCK_FIELD 1 +-#define STV090x_WIDTH_Px_PKTDELIN_LOCK_FIELD 1 +-#define STV090x_OFFST_Px_FIRST_LOCK_FIELD 0 +-#define STV090x_WIDTH_Px_FIRST_LOCK_FIELD 1 +- +-#define STV090x_Px_PDELSTATUS2(__x) (0xf56a - (__x - 1) * 0x200) +-#define STV090x_P1_PDELSTATUS2 STV090x_Px_PDELSTATUS2(1) +-#define STV090x_P2_PDELSTATUS2 STV090x_Px_PDELSTATUS2(2) +-#define STV090x_OFFST_Px_FRAME_MODCOD_FIELD 2 +-#define STV090x_WIDTH_Px_FRAME_MODCOD_FIELD 5 +-#define STV090x_OFFST_Px_FRAME_TYPE_FIELD 0 +-#define STV090x_WIDTH_Px_FRAME_TYPE_FIELD 2 +- +-#define STV090x_Px_BBFCRCKO1(__x) (0xf56b - (__x - 1) * 0x200) +-#define STV090x_P1_BBFCRCKO1 STV090x_Px_BBFCRCKO1(1) +-#define STV090x_P2_BBFCRCKO1 STV090x_Px_BBFCRCKO1(2) +-#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 +-#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 +- +-#define STV090x_Px_BBFCRCKO0(__x) (0xf56c - (__x - 1) * 0x200) +-#define STV090x_P1_BBFCRCKO0 STV090x_Px_BBFCRCKO0(1) +-#define STV090x_P2_BBFCRCKO0 STV090x_Px_BBFCRCKO0(2) +-#define STV090x_OFFST_Px_BBHCRC_KOCNT_FIELD 0 +-#define STV090x_WIDTH_Px_BBHCRC_KOCNT_FIELD 8 +- +-#define STV090x_Px_UPCRCKO1(__x) (0xf56d - (__x - 1) * 0x200) +-#define STV090x_P1_UPCRCKO1 STV090x_Px_UPCRCKO1(1) +-#define STV090x_P2_UPCRCKO1 STV090x_Px_UPCRCKO1(2) +-#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 +-#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 +- +-#define STV090x_Px_UPCRCKO0(__x) (0xf56e - (__x - 1) * 0x200) +-#define STV090x_P1_UPCRCKO0 STV090x_Px_UPCRCKO0(1) +-#define STV090x_P2_UPCRCKO0 STV090x_Px_UPCRCKO0(2) +-#define STV090x_OFFST_Px_PKTCRC_KOCNT_FIELD 0 +-#define STV090x_WIDTH_Px_PKTCRC_KOCNT_FIELD 8 +- +-#define STV090x_NBITER_NFx(__x) (0xFA03 + (__x - 4) * 0x1) +-#define STV090x_NBITER_NF4 STV090x_NBITER_NFx(4) +-#define STV090x_NBITER_NF5 STV090x_NBITER_NFx(5) +-#define STV090x_NBITER_NF6 STV090x_NBITER_NFx(6) +-#define STV090x_NBITER_NF7 STV090x_NBITER_NFx(7) +-#define STV090x_NBITER_NF8 STV090x_NBITER_NFx(8) +-#define STV090x_NBITER_NF9 STV090x_NBITER_NFx(9) +-#define STV090x_NBITER_NF10 STV090x_NBITER_NFx(10) +-#define STV090x_NBITER_NF11 STV090x_NBITER_NFx(11) +-#define STV090x_NBITER_NF12 STV090x_NBITER_NFx(12) +-#define STV090x_NBITER_NF13 STV090x_NBITER_NFx(13) +-#define STV090x_NBITER_NF14 STV090x_NBITER_NFx(14) +-#define STV090x_NBITER_NF15 STV090x_NBITER_NFx(15) +-#define STV090x_NBITER_NF16 STV090x_NBITER_NFx(16) +-#define STV090x_NBITER_NF17 STV090x_NBITER_NFx(17) +- +-#define STV090x_NBITERNOERR 0xFA3F +-#define STV090x_OFFST_NBITER_STOP_CRIT_FIELD 0 +-#define STV090x_WIDTH_NBITER_STOP_CRIT_FIELD 4 +- +-#define STV090x_GAINLLR_NFx(__x) (0xFA43 + (__x - 4) * 0x1) +-#define STV090x_GAINLLR_NF4 STV090x_GAINLLR_NFx(4) +-#define STV090x_OFFST_GAINLLR_NF_QP_1_2_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_1_2_FIELD 7 +- +-#define STV090x_GAINLLR_NF5 STV090x_GAINLLR_NFx(5) +-#define STV090x_OFFST_GAINLLR_NF_QP_3_5_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_3_5_FIELD 7 +- +-#define STV090x_GAINLLR_NF6 STV090x_GAINLLR_NFx(6) +-#define STV090x_OFFST_GAINLLR_NF_QP_2_3_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_2_3_FIELD 7 +- +-#define STV090x_GAINLLR_NF7 STV090x_GAINLLR_NFx(7) +-#define STV090x_OFFST_GAINLLR_NF_QP_3_4_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_3_4_FIELD 7 +- +-#define STV090x_GAINLLR_NF8 STV090x_GAINLLR_NFx(8) +-#define STV090x_OFFST_GAINLLR_NF_QP_4_5_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_4_5_FIELD 7 +- +-#define STV090x_GAINLLR_NF9 STV090x_GAINLLR_NFx(9) +-#define STV090x_OFFST_GAINLLR_NF_QP_5_6_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_5_6_FIELD 7 +- +-#define STV090x_GAINLLR_NF10 STV090x_GAINLLR_NFx(10) +-#define STV090x_OFFST_GAINLLR_NF_QP_8_9_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_8_9_FIELD 7 +- +-#define STV090x_GAINLLR_NF11 STV090x_GAINLLR_NFx(11) +-#define STV090x_OFFST_GAINLLR_NF_QP_9_10_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_QP_9_10_FIELD 7 +- +-#define STV090x_GAINLLR_NF12 STV090x_GAINLLR_NFx(12) +-#define STV090x_OFFST_GAINLLR_NF_8P_3_5_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_3_5_FIELD 7 +- +-#define STV090x_GAINLLR_NF13 STV090x_GAINLLR_NFx(13) +-#define STV090x_OFFST_GAINLLR_NF_8P_2_3_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_2_3_FIELD 7 +- +-#define STV090x_GAINLLR_NF14 STV090x_GAINLLR_NFx(14) +-#define STV090x_OFFST_GAINLLR_NF_8P_3_4_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_3_4_FIELD 7 +- +-#define STV090x_GAINLLR_NF15 STV090x_GAINLLR_NFx(15) +-#define STV090x_OFFST_GAINLLR_NF_8P_5_6_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_5_6_FIELD 7 +- +-#define STV090x_GAINLLR_NF16 STV090x_GAINLLR_NFx(16) +-#define STV090x_OFFST_GAINLLR_NF_8P_8_9_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_8_9_FIELD 7 +- +-#define STV090x_GAINLLR_NF17 STV090x_GAINLLR_NFx(17) +-#define STV090x_OFFST_GAINLLR_NF_8P_9_10_FIELD 0 +-#define STV090x_WIDTH_GAINLLR_NF_8P_9_10_FIELD 7 +- +-#define STV090x_GENCFG 0xFA86 +-#define STV090x_OFFST_BROADCAST_FIELD 4 +-#define STV090x_WIDTH_BROADCAST_FIELD 1 +-#define STV090x_OFFST_PRIORITY_FIELD 1 +-#define STV090x_WIDTH_PRIORITY_FIELD 1 +-#define STV090x_OFFST_DDEMOD_FIELD 0 +-#define STV090x_WIDTH_DDEMOD_FIELD 1 +- +-#define STV090x_LDPCERRx(__x) (0xFA97 - (__x * 0x1)) +-#define STV090x_LDPCERR0 STV090x_LDPCERRx(0) +-#define STV090x_LDPCERR1 STV090x_LDPCERRx(1) +-#define STV090x_OFFST_Px_LDPC_ERRORS_COUNTER_FIELD 0 +-#define STV090x_WIDTH_Px_LDPC_ERRORS_COUNTER_FIELD 8 +- +-#define STV090x_BCHERR 0xFA98 +-#define STV090x_OFFST_Px_ERRORFLAG_FIELD 4 +-#define STV090x_WIDTH_Px_ERRORFLAG_FIELD 1 +-#define STV090x_OFFST_Px_BCH_ERRORS_COUNTER_FIELD 0 +-#define STV090x_WIDTH_Px_BCH_ERRORS_COUNTER_FIELD 4 +- +-#define STV090x_Px_TSSTATEM(__x) (0xF570 - (__x - 1) * 0x200) +-#define STV090x_P1_TSSTATEM STV090x_Px_TSSTATEM(1) +-#define STV090x_P2_TSSTATEM STV090x_Px_TSSTATEM(2) +-#define STV090x_OFFST_Px_TSDIL_ON_FIELD 7 +-#define STV090x_WIDTH_Px_TSDIL_ON_FIELD 1 +-#define STV090x_OFFST_Px_TSRS_ON_FIELD 5 +-#define STV090x_WIDTH_Px_TSRS_ON_FIELD 1 +- +-#define STV090x_Px_TSCFGH(__x) (0xF572 - (__x - 1) * 0x200) +-#define STV090x_P1_TSCFGH STV090x_Px_TSCFGH(1) +-#define STV090x_P2_TSCFGH STV090x_Px_TSCFGH(2) +-#define STV090x_OFFST_Px_TSFIFO_DVBCI_FIELD 7 +-#define STV090x_WIDTH_Px_TSFIFO_DVBCI_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_SERIAL_FIELD 6 +-#define STV090x_WIDTH_Px_TSFIFO_SERIAL_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_TEIUPDATE_FIELD 5 +-#define STV090x_WIDTH_Px_TSFIFO_TEIUPDATE_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_DUTY50_FIELD 4 +-#define STV090x_WIDTH_Px_TSFIFO_DUTY50_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_HSGNLOUT_FIELD 3 +-#define STV090x_WIDTH_Px_TSFIFO_HSGNLOUT_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_ERRORMODE_FIELD 1 +-#define STV090x_WIDTH_Px_TSFIFO_ERRORMODE_FIELD 2 +-#define STV090x_OFFST_Px_RST_HWARE_FIELD 0 +-#define STV090x_WIDTH_Px_RST_HWARE_FIELD 1 +- +-#define STV090x_Px_TSCFGM(__x) (0xF573 - (__x - 1) * 0x200) +-#define STV090x_P1_TSCFGM STV090x_Px_TSCFGM(1) +-#define STV090x_P2_TSCFGM STV090x_Px_TSCFGM(2) +-#define STV090x_OFFST_Px_TSFIFO_MANSPEED_FIELD 6 +-#define STV090x_WIDTH_Px_TSFIFO_MANSPEED_FIELD 2 +-#define STV090x_OFFST_Px_TSFIFO_PERMDATA_FIELD 5 +-#define STV090x_WIDTH_Px_TSFIFO_PERMDATA_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_INVDATA_FIELD 0 +-#define STV090x_WIDTH_Px_TSFIFO_INVDATA_FIELD 1 +- +-#define STV090x_Px_TSCFGL(__x) (0xF574 - (__x - 1) * 0x200) +-#define STV090x_P1_TSCFGL STV090x_Px_TSCFGL(1) +-#define STV090x_P2_TSCFGL STV090x_Px_TSCFGL(2) +-#define STV090x_OFFST_Px_TSFIFO_BCLKDEL1CK_FIELD 6 +-#define STV090x_WIDTH_Px_TSFIFO_BCLKDEL1CK_FIELD 2 +-#define STV090x_OFFST_Px_BCHERROR_MODE_FIELD 4 +-#define STV090x_WIDTH_Px_BCHERROR_MODE_FIELD 2 +-#define STV090x_OFFST_Px_TSFIFO_NSGNL2DATA_FIELD 3 +-#define STV090x_WIDTH_Px_TSFIFO_NSGNL2DATA_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_EMBINDVB_FIELD 2 +-#define STV090x_WIDTH_Px_TSFIFO_EMBINDVB_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_DPUNACT_FIELD 1 +-#define STV090x_WIDTH_Px_TSFIFO_DPUNACT_FIELD 1 +- +-#define STV090x_Px_TSINSDELH(__x) (0xF576 - (__x - 1) * 0x200) +-#define STV090x_P1_TSINSDELH STV090x_Px_TSINSDELH(1) +-#define STV090x_P2_TSINSDELH STV090x_Px_TSINSDELH(2) +-#define STV090x_OFFST_Px_TSDEL_SYNCBYTE_FIELD 7 +-#define STV090x_WIDTH_Px_TSDEL_SYNCBYTE_FIELD 1 +-#define STV090x_OFFST_Px_TSDEL_XXHEADER_FIELD 6 +-#define STV090x_WIDTH_Px_TSDEL_XXHEADER_FIELD 1 +- +-#define STV090x_Px_TSSPEED(__x) (0xF580 - (__x - 1) * 0x200) +-#define STV090x_P1_TSSPEED STV090x_Px_TSSPEED(1) +-#define STV090x_P2_TSSPEED STV090x_Px_TSSPEED(2) +-#define STV090x_OFFST_Px_TSFIFO_OUTSPEED_FIELD 0 +-#define STV090x_WIDTH_Px_TSFIFO_OUTSPEED_FIELD 8 +- +-#define STV090x_Px_TSSTATUS(__x) (0xF581 - (__x - 1) * 0x200) +-#define STV090x_P1_TSSTATUS STV090x_Px_TSSTATUS(1) +-#define STV090x_P2_TSSTATUS STV090x_Px_TSSTATUS(2) +-#define STV090x_OFFST_Px_TSFIFO_LINEOK_FIELD 7 +-#define STV090x_WIDTH_Px_TSFIFO_LINEOK_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_ERROR_FIELD 6 +-#define STV090x_WIDTH_Px_TSFIFO_ERROR_FIELD 1 +- +-#define STV090x_Px_TSSTATUS2(__x) (0xF582 - (__x - 1) * 0x200) +-#define STV090x_P1_TSSTATUS2 STV090x_Px_TSSTATUS2(1) +-#define STV090x_P2_TSSTATUS2 STV090x_Px_TSSTATUS2(2) +-#define STV090x_OFFST_Px_TSFIFO_DEMODSEL_FIELD 7 +-#define STV090x_WIDTH_Px_TSFIFO_DEMODSEL_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFOSPEED_STORE_FIELD 6 +-#define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1 +-#define STV090x_OFFST_Px_DILXX_RESET_FIELD 5 +-#define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1 +-#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4 +-#define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1 +-#define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1 +-#define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1 +- +-#define STV090x_Px_TSBITRATEy(__x, __y) (0xF584 - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_TSBITRATE0 STV090x_Px_TSBITRATEy(1, 0) +-#define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1) +-#define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0) +-#define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1) +-#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0 +-#define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8 +- +-#define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCTRL1 STV090x_Px_ERRCTRL1(1) +-#define STV090x_P2_ERRCTRL1 STV090x_Px_ERRCTRL1(2) +-#define STV090x_OFFST_Px_ERR_SOURCE_FIELD 4 +-#define STV090x_WIDTH_Px_ERR_SOURCE_FIELD 4 +-#define STV090x_OFFST_Px_NUM_EVENT_FIELD 0 +-#define STV090x_WIDTH_Px_NUM_EVENT_FIELD 3 +- +-#define STV090x_Px_ERRCNT12(__x) (0xF599 - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT12 STV090x_Px_ERRCNT12(1) +-#define STV090x_P2_ERRCNT12 STV090x_Px_ERRCNT12(2) +-#define STV090x_OFFST_Px_ERRCNT1_OLDVALUE_FIELD 7 +-#define STV090x_WIDTH_Px_ERRCNT1_OLDVALUE_FIELD 1 +-#define STV090x_OFFST_Px_ERR_CNT12_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT12_FIELD 7 +- +-#define STV090x_Px_ERRCNT11(__x) (0xF59A - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT11 STV090x_Px_ERRCNT11(1) +-#define STV090x_P2_ERRCNT11 STV090x_Px_ERRCNT11(2) +-#define STV090x_OFFST_Px_ERR_CNT11_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT11_FIELD 8 +- +-#define STV090x_Px_ERRCNT10(__x) (0xF59B - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT10 STV090x_Px_ERRCNT10(1) +-#define STV090x_P2_ERRCNT10 STV090x_Px_ERRCNT10(2) +-#define STV090x_OFFST_Px_ERR_CNT10_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT10_FIELD 8 +- +-#define STV090x_Px_ERRCTRL2(__x) (0xF59C - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCTRL2 STV090x_Px_ERRCTRL2(1) +-#define STV090x_P2_ERRCTRL2 STV090x_Px_ERRCTRL2(2) +-#define STV090x_OFFST_Px_ERR_SOURCE2_FIELD 4 +-#define STV090x_WIDTH_Px_ERR_SOURCE2_FIELD 4 +-#define STV090x_OFFST_Px_NUM_EVENT2_FIELD 0 +-#define STV090x_WIDTH_Px_NUM_EVENT2_FIELD 3 +- +-#define STV090x_Px_ERRCNT22(__x) (0xF59D - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT22 STV090x_Px_ERRCNT22(1) +-#define STV090x_P2_ERRCNT22 STV090x_Px_ERRCNT22(2) +-#define STV090x_OFFST_Px_ERRCNT2_OLDVALUE_FIELD 7 +-#define STV090x_WIDTH_Px_ERRCNT2_OLDVALUE_FIELD 1 +-#define STV090x_OFFST_Px_ERR_CNT2_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT2_FIELD 7 +- +-#define STV090x_Px_ERRCNT21(__x) (0xF59E - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT21 STV090x_Px_ERRCNT21(1) +-#define STV090x_P2_ERRCNT21 STV090x_Px_ERRCNT21(2) +-#define STV090x_OFFST_Px_ERR_CNT21_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT21_FIELD 8 +- +-#define STV090x_Px_ERRCNT20(__x) (0xF59F - (__x - 1) * 0x200) +-#define STV090x_P1_ERRCNT20 STV090x_Px_ERRCNT20(1) +-#define STV090x_P2_ERRCNT20 STV090x_Px_ERRCNT20(2) +-#define STV090x_OFFST_Px_ERR_CNT20_FIELD 0 +-#define STV090x_WIDTH_Px_ERR_CNT20_FIELD 8 +- +-#define STV090x_Px_FECSPY(__x) (0xF5A0 - (__x - 1) * 0x200) +-#define STV090x_P1_FECSPY STV090x_Px_FECSPY(1) +-#define STV090x_P2_FECSPY STV090x_Px_FECSPY(2) +-#define STV090x_OFFST_Px_SPY_ENABLE_FIELD 7 +-#define STV090x_WIDTH_Px_SPY_ENABLE_FIELD 1 +-#define STV090x_OFFST_Px_BERMETER_DATAMAODE_FIELD 2 +-#define STV090x_WIDTH_Px_BERMETER_DATAMAODE_FIELD 2 +- +-#define STV090x_Px_FSPYCFG(__x) (0xF5A1 - (__x - 1) * 0x200) +-#define STV090x_P1_FSPYCFG STV090x_Px_FSPYCFG(1) +-#define STV090x_P2_FSPYCFG STV090x_Px_FSPYCFG(2) +-#define STV090x_OFFST_Px_RST_ON_ERROR_FIELD 5 +-#define STV090x_WIDTH_Px_RST_ON_ERROR_FIELD 1 +-#define STV090x_OFFST_Px_ONE_SHOT_FIELD 4 +-#define STV090x_WIDTH_Px_ONE_SHOT_FIELD 1 +-#define STV090x_OFFST_Px_I2C_MODE_FIELD 2 +-#define STV090x_WIDTH_Px_I2C_MODE_FIELD 2 +- +-#define STV090x_Px_FSPYDATA(__x) (0xF5A2 - (__x - 1) * 0x200) +-#define STV090x_P1_FSPYDATA STV090x_Px_FSPYDATA(1) +-#define STV090x_P2_FSPYDATA STV090x_Px_FSPYDATA(2) +-#define STV090x_OFFST_Px_SPY_STUFFING_FIELD 7 +-#define STV090x_WIDTH_Px_SPY_STUFFING_FIELD 1 +-#define STV090x_OFFST_Px_SPY_CNULLPKT_FIELD 5 +-#define STV090x_WIDTH_Px_SPY_CNULLPKT_FIELD 1 +-#define STV090x_OFFST_Px_SPY_OUTDATA_MODE_FIELD 0 +-#define STV090x_WIDTH_Px_SPY_OUTDATA_MODE_FIELD 5 +- +-#define STV090x_Px_FSPYOUT(__x) (0xF5A3 - (__x - 1) * 0x200) +-#define STV090x_P1_FSPYOUT STV090x_Px_FSPYOUT(1) +-#define STV090x_P2_FSPYOUT STV090x_Px_FSPYOUT(2) +-#define STV090x_OFFST_Px_FSPY_DIRECT_FIELD 7 +-#define STV090x_WIDTH_Px_FSPY_DIRECT_FIELD 1 +-#define STV090x_OFFST_Px_STUFF_MODE_FIELD 0 +-#define STV090x_WIDTH_Px_STUFF_MODE_FIELD 3 +- +-#define STV090x_Px_FSTATUS(__x) (0xF5A4 - (__x - 1) * 0x200) +-#define STV090x_P1_FSTATUS STV090x_Px_FSTATUS(1) +-#define STV090x_P2_FSTATUS STV090x_Px_FSTATUS(2) +-#define STV090x_OFFST_Px_SPY_ENDSIM_FIELD 7 +-#define STV090x_WIDTH_Px_SPY_ENDSIM_FIELD 1 +-#define STV090x_OFFST_Px_VALID_SIM_FIELD 6 +-#define STV090x_WIDTH_Px_VALID_SIM_FIELD 1 +-#define STV090x_OFFST_Px_FOUND_SIGNAL_FIELD 5 +-#define STV090x_WIDTH_Px_FOUND_SIGNAL_FIELD 1 +-#define STV090x_OFFST_Px_DSS_SYNCBYTE_FIELD 4 +-#define STV090x_WIDTH_Px_DSS_SYNCBYTE_FIELD 1 +-#define STV090x_OFFST_Px_RESULT_STATE_FIELD 0 +-#define STV090x_WIDTH_Px_RESULT_STATE_FIELD 4 +- +-#define STV090x_Px_FBERCPT4(__x) (0xF5A8 - (__x - 1) * 0x200) +-#define STV090x_P1_FBERCPT4 STV090x_Px_FBERCPT4(1) +-#define STV090x_P2_FBERCPT4 STV090x_Px_FBERCPT4(2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 +- +-#define STV090x_Px_FBERCPT3(__x) (0xF5A9 - (__x - 1) * 0x200) +-#define STV090x_P1_FBERCPT3 STV090x_Px_FBERCPT3(1) +-#define STV090x_P2_FBERCPT3 STV090x_Px_FBERCPT3(2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 +- +-#define STV090x_Px_FBERCPT2(__x) (0xF5AA - (__x - 1) * 0x200) +-#define STV090x_P1_FBERCPT2 STV090x_Px_FBERCPT2(1) +-#define STV090x_P2_FBERCPT2 STV090x_Px_FBERCPT2(2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 +- +-#define STV090x_Px_FBERCPT1(__x) (0xF5AB - (__x - 1) * 0x200) +-#define STV090x_P1_FBERCPT1 STV090x_Px_FBERCPT1(1) +-#define STV090x_P2_FBERCPT1 STV090x_Px_FBERCPT1(2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 +- +-#define STV090x_Px_FBERCPT0(__x) (0xF5AC - (__x - 1) * 0x200) +-#define STV090x_P1_FBERCPT0 STV090x_Px_FBERCPT0(1) +-#define STV090x_P2_FBERCPT0 STV090x_Px_FBERCPT0(2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_FIELD 8 +- +-#define STV090x_Px_FBERERRy(__x, __y) (0xF5AF - (__x - 1) * 0x200 - __y * 0x1) +-#define STV090x_P1_FBERERR0 STV090x_Px_FBERERRy(1, 0) +-#define STV090x_P1_FBERERR1 STV090x_Px_FBERERRy(1, 1) +-#define STV090x_P1_FBERERR2 STV090x_Px_FBERERRy(1, 2) +-#define STV090x_P2_FBERERR0 STV090x_Px_FBERERRy(2, 0) +-#define STV090x_P2_FBERERR1 STV090x_Px_FBERERRy(2, 1) +-#define STV090x_P2_FBERERR2 STV090x_Px_FBERERRy(2, 2) +-#define STV090x_OFFST_Px_FBERMETER_CPT_ERR_FIELD 0 +-#define STV090x_WIDTH_Px_FBERMETER_CPT_ERR_FIELD 8 +- +-#define STV090x_Px_FSPYBER(__x) (0xF5B2 - (__x - 1) * 0x200) +-#define STV090x_P1_FSPYBER STV090x_Px_FSPYBER(1) +-#define STV090x_P2_FSPYBER STV090x_Px_FSPYBER(2) +-#define STV090x_OFFST_Px_FSPYBER_SYNCBYTE_FIELD 4 +-#define STV090x_WIDTH_Px_FSPYBER_SYNCBYTE_FIELD 1 +-#define STV090x_OFFST_Px_FSPYBER_UNSYNC_FIELD 3 +-#define STV090x_WIDTH_Px_FSPYBER_UNSYNC_FIELD 1 +-#define STV090x_OFFST_Px_FSPYBER_CTIME_FIELD 0 +-#define STV090x_WIDTH_Px_FSPYBER_CTIME_FIELD 3 +- +-#define STV090x_RCCFGH 0xf600 +- +-#define STV090x_TSGENERAL 0xF630 +-#define STV090x_OFFST_Px_MUXSTREAM_OUT_FIELD 3 +-#define STV090x_WIDTH_Px_MUXSTREAM_OUT_FIELD 1 +-#define STV090x_OFFST_Px_TSFIFO_PERMPARAL_FIELD 1 +-#define STV090x_WIDTH_Px_TSFIFO_PERMPARAL_FIELD 2 +- +-#define STV090x_TSGENERAL1X 0xf670 +-#define STV090x_CFGEXT 0xfa80 +- +-#define STV090x_TSTRES0 0xFF11 +-#define STV090x_OFFST_FRESFEC_FIELD 7 +-#define STV090x_WIDTH_FRESFEC_FIELD 1 +- +-#define STV090x_Px_TSTDISRX(__x) (0xFF67 - (__x - 1) * 0x2) +-#define STV090x_P1_TSTDISRX STV090x_Px_TSTDISRX(1) +-#define STV090x_P2_TSTDISRX STV090x_Px_TSTDISRX(2) +-#define STV090x_OFFST_Px_TSTDISRX_SELECT_FIELD 3 +-#define STV090x_WIDTH_Px_TSTDISRX_SELECT_FIELD 1 +- +-#endif /* __STV090x_REG_H */ +diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c +deleted file mode 100644 +index 20b5fa9..0000000 +--- a/drivers/media/dvb/frontends/stv6110.c ++++ /dev/null +@@ -1,451 +0,0 @@ +-/* +- * stv6110.c +- * +- * Driver for ST STV6110 satellite tuner IC. +- * +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +- +-#include +- +-#include "stv6110.h" +- +-static int debug; +- +-struct stv6110_priv { +- int i2c_address; +- struct i2c_adapter *i2c; +- +- u32 mclk; +- u8 clk_div; +- u8 gain; +- u8 regs[8]; +-}; +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG args); \ +- } while (0) +- +-static s32 abssub(s32 a, s32 b) +-{ +- if (a > b) +- return a - b; +- else +- return b - a; +-}; +- +-static int stv6110_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[], +- int start, int len) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- int rc; +- u8 cmdbuf[len + 1]; +- struct i2c_msg msg = { +- .addr = priv->i2c_address, +- .flags = 0, +- .buf = cmdbuf, +- .len = len + 1 +- }; +- +- dprintk("%s\n", __func__); +- +- if (start + len > 8) +- return -EINVAL; +- +- memcpy(&cmdbuf[1], buf, len); +- cmdbuf[0] = start; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- rc = i2c_transfer(priv->i2c, &msg, 1); +- if (rc != 1) +- dprintk("%s: i2c error\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[], +- int start, int len) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- int rc; +- u8 reg[] = { start }; +- struct i2c_msg msg[] = { +- { +- .addr = priv->i2c_address, +- .flags = 0, +- .buf = reg, +- .len = 1, +- }, { +- .addr = priv->i2c_address, +- .flags = I2C_M_RD, +- .buf = regs, +- .len = len, +- }, +- }; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- rc = i2c_transfer(priv->i2c, msg, 2); +- if (rc != 2) +- dprintk("%s: i2c error\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- memcpy(&priv->regs[start], regs, len); +- +- return 0; +-} +- +-static int stv6110_read_reg(struct dvb_frontend *fe, int start) +-{ +- u8 buf[] = { 0 }; +- stv6110_read_regs(fe, buf, start, 1); +- +- return buf[0]; +-} +- +-static int stv6110_sleep(struct dvb_frontend *fe) +-{ +- u8 reg[] = { 0 }; +- stv6110_write_regs(fe, reg, 0, 1); +- +- return 0; +-} +- +-static u32 carrier_width(u32 symbol_rate, fe_rolloff_t rolloff) +-{ +- u32 rlf; +- +- switch (rolloff) { +- case ROLLOFF_20: +- rlf = 20; +- break; +- case ROLLOFF_25: +- rlf = 25; +- break; +- default: +- rlf = 35; +- break; +- } +- +- return symbol_rate + ((symbol_rate * rlf) / 100); +-} +- +-static int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- u8 r8, ret = 0x04; +- int i; +- +- if ((bandwidth / 2) > 36000000) /*BW/2 max=31+5=36 mhz for r8=31*/ +- r8 = 31; +- else if ((bandwidth / 2) < 5000000) /* BW/2 min=5Mhz for F=0 */ +- r8 = 0; +- else /*if 5 < BW/2 < 36*/ +- r8 = (bandwidth / 2) / 1000000 - 5; +- +- /* ctrl3, RCCLKOFF = 0 Activate the calibration Clock */ +- /* ctrl3, CF = r8 Set the LPF value */ +- priv->regs[RSTV6110_CTRL3] &= ~((1 << 6) | 0x1f); +- priv->regs[RSTV6110_CTRL3] |= (r8 & 0x1f); +- stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); +- /* stat1, CALRCSTRT = 1 Start LPF auto calibration*/ +- priv->regs[RSTV6110_STAT1] |= 0x02; +- stv6110_write_regs(fe, &priv->regs[RSTV6110_STAT1], RSTV6110_STAT1, 1); +- +- i = 0; +- /* Wait for CALRCSTRT == 0 */ +- while ((i < 10) && (ret != 0)) { +- ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x02); +- mdelay(1); /* wait for LPF auto calibration */ +- i++; +- } +- +- /* RCCLKOFF = 1 calibration done, desactivate the calibration Clock */ +- priv->regs[RSTV6110_CTRL3] |= (1 << 6); +- stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1); +- return 0; +-} +- +-static int stv6110_init(struct dvb_frontend *fe) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- u8 buf0[] = { 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; +- +- memcpy(priv->regs, buf0, 8); +- /* K = (Reference / 1000000) - 16 */ +- priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); +- priv->regs[RSTV6110_CTRL1] |= +- ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); +- +- /* divisor value for the output clock */ +- priv->regs[RSTV6110_CTRL2] &= ~0xc0; +- priv->regs[RSTV6110_CTRL2] |= (priv->clk_div << 6); +- +- stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8); +- msleep(1); +- stv6110_set_bandwidth(fe, 72000000); +- +- return 0; +-} +- +-static int stv6110_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- u32 nbsteps, divider, psd2, freq; +- u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +- +- stv6110_read_regs(fe, regs, 0, 8); +- /*N*/ +- divider = (priv->regs[RSTV6110_TUNING2] & 0x0f) << 8; +- divider += priv->regs[RSTV6110_TUNING1]; +- +- /*R*/ +- nbsteps = (priv->regs[RSTV6110_TUNING2] >> 6) & 3; +- /*p*/ +- psd2 = (priv->regs[RSTV6110_TUNING2] >> 4) & 1; +- +- freq = divider * (priv->mclk / 1000); +- freq /= (1 << (nbsteps + psd2)); +- freq /= 4; +- +- *frequency = freq; +- +- return 0; +-} +- +-static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u8 ret = 0x04; +- u32 divider, ref, p, presc, i, result_freq, vco_freq; +- s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; +- s32 srate; +- +- dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, +- frequency, priv->mclk); +- +- /* K = (Reference / 1000000) - 16 */ +- priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3); +- priv->regs[RSTV6110_CTRL1] |= +- ((((priv->mclk / 1000000) - 16) & 0x1f) << 3); +- +- /* BB_GAIN = db/2 */ +- if (fe->ops.set_property && fe->ops.get_property) { +- srate = c->symbol_rate; +- dprintk("%s: Get Frontend parameters: srate=%d\n", +- __func__, srate); +- } else +- srate = 15000000; +- +- priv->regs[RSTV6110_CTRL2] &= ~0x0f; +- priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); +- +- if (frequency <= 1023000) { +- p = 1; +- presc = 0; +- } else if (frequency <= 1300000) { +- p = 1; +- presc = 1; +- } else if (frequency <= 2046000) { +- p = 0; +- presc = 0; +- } else { +- p = 0; +- presc = 1; +- } +- /* DIV4SEL = p*/ +- priv->regs[RSTV6110_TUNING2] &= ~(1 << 4); +- priv->regs[RSTV6110_TUNING2] |= (p << 4); +- +- /* PRESC32ON = presc */ +- priv->regs[RSTV6110_TUNING2] &= ~(1 << 5); +- priv->regs[RSTV6110_TUNING2] |= (presc << 5); +- +- p_val = (int)(1 << (p + 1)) * 10;/* P = 2 or P = 4 */ +- for (r_div = 0; r_div <= 3; r_div++) { +- p_calc = (priv->mclk / 100000); +- p_calc /= (1 << (r_div + 1)); +- if ((abssub(p_calc, p_val)) < (abssub(p_calc_opt, p_val))) +- r_div_opt = r_div; +- +- p_calc_opt = (priv->mclk / 100000); +- p_calc_opt /= (1 << (r_div_opt + 1)); +- } +- +- ref = priv->mclk / ((1 << (r_div_opt + 1)) * (1 << (p + 1))); +- divider = (((frequency * 1000) + (ref >> 1)) / ref); +- +- /* RDIV = r_div_opt */ +- priv->regs[RSTV6110_TUNING2] &= ~(3 << 6); +- priv->regs[RSTV6110_TUNING2] |= (((r_div_opt) & 3) << 6); +- +- /* NDIV_MSB = MSB(divider) */ +- priv->regs[RSTV6110_TUNING2] &= ~0x0f; +- priv->regs[RSTV6110_TUNING2] |= (((divider) >> 8) & 0x0f); +- +- /* NDIV_LSB, LSB(divider) */ +- priv->regs[RSTV6110_TUNING1] = (divider & 0xff); +- +- /* CALVCOSTRT = 1 VCO Auto Calibration */ +- priv->regs[RSTV6110_STAT1] |= 0x04; +- stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], +- RSTV6110_CTRL1, 8); +- +- i = 0; +- /* Wait for CALVCOSTRT == 0 */ +- while ((i < 10) && (ret != 0)) { +- ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x04); +- msleep(1); /* wait for VCO auto calibration */ +- i++; +- } +- +- ret = stv6110_read_reg(fe, RSTV6110_STAT1); +- stv6110_get_frequency(fe, &result_freq); +- +- vco_freq = divider * ((priv->mclk / 1000) / ((1 << (r_div_opt + 1)))); +- dprintk("%s, stat1=%x, lo_freq=%d kHz, vco_frec=%d kHz\n", __func__, +- ret, result_freq, vco_freq); +- +- return 0; +-} +- +-static int stv6110_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 bandwidth = carrier_width(c->symbol_rate, c->rolloff); +- +- stv6110_set_frequency(fe, c->frequency); +- stv6110_set_bandwidth(fe, bandwidth); +- +- return 0; +-} +- +-static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct stv6110_priv *priv = fe->tuner_priv; +- u8 r8 = 0; +- u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +- stv6110_read_regs(fe, regs, 0, 8); +- +- /* CF */ +- r8 = priv->regs[RSTV6110_CTRL3] & 0x1f; +- *bandwidth = (r8 + 5) * 2000000;/* x2 for ZIF tuner BW/2 = F+5 Mhz */ +- +- return 0; +-} +- +-static struct dvb_tuner_ops stv6110_tuner_ops = { +- .info = { +- .name = "ST STV6110", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 1000, +- }, +- .init = stv6110_init, +- .release = stv6110_release, +- .sleep = stv6110_sleep, +- .set_params = stv6110_set_params, +- .get_frequency = stv6110_get_frequency, +- .set_frequency = stv6110_set_frequency, +- .get_bandwidth = stv6110_get_bandwidth, +- .set_bandwidth = stv6110_set_bandwidth, +- +-}; +- +-struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, +- const struct stv6110_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv6110_priv *priv = NULL; +- u8 reg0[] = { 0x00, 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e }; +- +- struct i2c_msg msg[] = { +- { +- .addr = config->i2c_address, +- .flags = 0, +- .buf = reg0, +- .len = 9 +- } +- }; +- int ret; +- +- /* divisor value for the output clock */ +- reg0[2] &= ~0xc0; +- reg0[2] |= (config->clk_div << 6); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- ret = i2c_transfer(i2c, msg, 1); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret != 1) +- return NULL; +- +- priv = kzalloc(sizeof(struct stv6110_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->i2c_address = config->i2c_address; +- priv->i2c = i2c; +- priv->mclk = config->mclk; +- priv->clk_div = config->clk_div; +- priv->gain = config->gain; +- +- memcpy(&priv->regs, ®0[1], 8); +- +- memcpy(&fe->ops.tuner_ops, &stv6110_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- fe->tuner_priv = priv; +- printk(KERN_INFO "STV6110 attached on addr=%x!\n", priv->i2c_address); +- +- return fe; +-} +-EXPORT_SYMBOL(stv6110_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("ST STV6110 driver"); +-MODULE_AUTHOR("Igor M. Liplianin"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h +deleted file mode 100644 +index fe71bba..0000000 +--- a/drivers/media/dvb/frontends/stv6110.h ++++ /dev/null +@@ -1,63 +0,0 @@ +-/* +- * stv6110.h +- * +- * Driver for ST STV6110 satellite tuner IC. +- * +- * Copyright (C) 2009 NetUP Inc. +- * Copyright (C) 2009 Igor M. Liplianin +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __DVB_STV6110_H__ +-#define __DVB_STV6110_H__ +- +-#include +-#include "dvb_frontend.h" +- +-/* registers */ +-#define RSTV6110_CTRL1 0 +-#define RSTV6110_CTRL2 1 +-#define RSTV6110_TUNING1 2 +-#define RSTV6110_TUNING2 3 +-#define RSTV6110_CTRL3 4 +-#define RSTV6110_STAT1 5 +-#define RSTV6110_STAT2 6 +-#define RSTV6110_STAT3 7 +- +-struct stv6110_config { +- u8 i2c_address; +- u32 mclk; +- u8 gain; +- u8 clk_div; /* divisor value for the output clock */ +-}; +- +-#if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \ +- && defined(MODULE)) +-extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, +- const struct stv6110_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, +- const struct stv6110_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c +deleted file mode 100644 +index f36cab1..0000000 +--- a/drivers/media/dvb/frontends/stv6110x.c ++++ /dev/null +@@ -1,405 +0,0 @@ +-/* +- STV6110(A) Silicon tuner driver +- +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-#include "stv6110x_reg.h" +-#include "stv6110x.h" +-#include "stv6110x_priv.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "Set Verbosity level"); +- +-static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) +-{ +- int ret; +- const struct stv6110x_config *config = stv6110x->config; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { .addr = config->addr, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = config->addr, .flags = I2C_M_RD, .buf = b1, .len = 1 } +- }; +- +- ret = i2c_transfer(stv6110x->i2c, msg, 2); +- if (ret != 2) { +- dprintk(FE_ERROR, 1, "I/O Error"); +- return -EREMOTEIO; +- } +- *data = b1[0]; +- +- return 0; +-} +- +-static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len) +-{ +- int ret; +- const struct stv6110x_config *config = stv6110x->config; +- u8 buf[len + 1]; +- struct i2c_msg msg = { +- .addr = config->addr, +- .flags = 0, +- .buf = buf, +- .len = len + 1 +- }; +- +- if (start + len > 8) +- return -EINVAL; +- +- buf[0] = start; +- memcpy(&buf[1], data, len); +- +- ret = i2c_transfer(stv6110x->i2c, &msg, 1); +- if (ret != 1) { +- dprintk(FE_ERROR, 1, "I/O Error"); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) +-{ +- return stv6110x_write_regs(stv6110x, reg, &data, 1); +-} +- +-static int stv6110x_init(struct dvb_frontend *fe) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- int ret; +- +- ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, +- ARRAY_SIZE(stv6110x->regs)); +- if (ret < 0) { +- dprintk(FE_ERROR, 1, "Initialization failed"); +- return -1; +- } +- +- return 0; +-} +- +-static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- u32 rDiv, divider; +- s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; +- u8 i; +- +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); +- +- if (frequency <= 1023000) { +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); +- pVal = 40; +- } else if (frequency <= 1300000) { +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); +- pVal = 40; +- } else if (frequency <= 2046000) { +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); +- pVal = 20; +- } else { +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); +- pVal = 20; +- } +- +- for (rDiv = 0; rDiv <= 3; rDiv++) { +- pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv); +- +- if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal)))) +- rDivOpt = rDiv; +- +- pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt); +- } +- +- divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; +- divider = (divider + 5) / 10; +- +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider)); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider)); +- +- /* VCO Auto calibration */ +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1); +- +- stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); +- stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]); +- stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]); +- stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); +- +- for (i = 0; i < TRIALS; i++) { +- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); +- if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1])) +- break; +- msleep(1); +- } +- +- return 0; +-} +- +-static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]); +- stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]); +- +- *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]), +- STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz; +- +- *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) + +- STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1]))); +- +- *frequency >>= 2; +- +- return 0; +-} +- +-static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- u32 halfbw; +- u8 i; +- +- halfbw = bandwidth >> 1; +- +- if (halfbw > 36000000) +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */ +- else if (halfbw < 5000000) +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */ +- else +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */ +- +- +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */ +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */ +- +- stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); +- stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); +- +- for (i = 0; i < TRIALS; i++) { +- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); +- if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1])) +- break; +- msleep(1); +- } +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */ +- stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); +- +- return 0; +-} +- +-static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]); +- *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000; +- +- return 0; +-} +- +-static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- /* setup divider */ +- switch (refclock) { +- default: +- case 1: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); +- break; +- case 2: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); +- break; +- case 4: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); +- break; +- case 8: +- case 0: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); +- break; +- } +- stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); +- +- return 0; +-} +- +-static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]); +- *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]); +- +- return 0; +-} +- +-static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2); +- stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); +- +- return 0; +-} +- +-static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- int ret; +- +- switch (mode) { +- case TUNER_SLEEP: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0); +- break; +- +- case TUNER_WAKE: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1); +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1); +- break; +- } +- +- ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); +- if (ret < 0) { +- dprintk(FE_ERROR, 1, "I/O Error"); +- return -EIO; +- } +- +- return 0; +-} +- +-static int stv6110x_sleep(struct dvb_frontend *fe) +-{ +- if (fe->tuner_priv) +- return stv6110x_set_mode(fe, TUNER_SLEEP); +- +- return 0; +-} +- +-static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); +- +- if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1])) +- *status = TUNER_PHASELOCKED; +- else +- *status = 0; +- +- return 0; +-} +- +- +-static int stv6110x_release(struct dvb_frontend *fe) +-{ +- struct stv6110x_state *stv6110x = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(stv6110x); +- +- return 0; +-} +- +-static struct dvb_tuner_ops stv6110x_ops = { +- .info = { +- .name = "STV6110(A) Silicon Tuner", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 0, +- }, +- .release = stv6110x_release +-}; +- +-static struct stv6110x_devctl stv6110x_ctl = { +- .tuner_init = stv6110x_init, +- .tuner_sleep = stv6110x_sleep, +- .tuner_set_mode = stv6110x_set_mode, +- .tuner_set_frequency = stv6110x_set_frequency, +- .tuner_get_frequency = stv6110x_get_frequency, +- .tuner_set_bandwidth = stv6110x_set_bandwidth, +- .tuner_get_bandwidth = stv6110x_get_bandwidth, +- .tuner_set_bbgain = stv6110x_set_bbgain, +- .tuner_get_bbgain = stv6110x_get_bbgain, +- .tuner_set_refclk = stv6110x_set_refclock, +- .tuner_get_status = stv6110x_get_status, +-}; +- +-struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, +- const struct stv6110x_config *config, +- struct i2c_adapter *i2c) +-{ +- struct stv6110x_state *stv6110x; +- u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; +- +- stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); +- if (!stv6110x) +- return NULL; +- +- stv6110x->i2c = i2c; +- stv6110x->config = config; +- stv6110x->devctl = &stv6110x_ctl; +- memcpy(stv6110x->regs, default_regs, 8); +- +- /* setup divider */ +- switch (stv6110x->config->clk_div) { +- default: +- case 1: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); +- break; +- case 2: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); +- break; +- case 4: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); +- break; +- case 8: +- case 0: +- STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); +- break; +- } +- +- fe->tuner_priv = stv6110x; +- fe->ops.tuner_ops = stv6110x_ops; +- +- printk(KERN_INFO "%s: Attaching STV6110x\n", __func__); +- return stv6110x->devctl; +-} +-EXPORT_SYMBOL(stv6110x_attach); +- +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_DESCRIPTION("STV6110x Silicon tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/stv6110x.h b/drivers/media/dvb/frontends/stv6110x.h +deleted file mode 100644 +index 4751675..0000000 +--- a/drivers/media/dvb/frontends/stv6110x.h ++++ /dev/null +@@ -1,73 +0,0 @@ +-/* +- STV6110(A) Silicon tuner driver +- +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV6110x_H +-#define __STV6110x_H +- +-struct stv6110x_config { +- u8 addr; +- u32 refclk; +- u8 clk_div; /* divisor value for the output clock */ +-}; +- +-enum tuner_mode { +- TUNER_SLEEP = 1, +- TUNER_WAKE, +-}; +- +-enum tuner_status { +- TUNER_PHASELOCKED = 1, +-}; +- +-struct stv6110x_devctl { +- int (*tuner_init) (struct dvb_frontend *fe); +- int (*tuner_sleep) (struct dvb_frontend *fe); +- int (*tuner_set_mode) (struct dvb_frontend *fe, enum tuner_mode mode); +- int (*tuner_set_frequency) (struct dvb_frontend *fe, u32 frequency); +- int (*tuner_get_frequency) (struct dvb_frontend *fe, u32 *frequency); +- int (*tuner_set_bandwidth) (struct dvb_frontend *fe, u32 bandwidth); +- int (*tuner_get_bandwidth) (struct dvb_frontend *fe, u32 *bandwidth); +- int (*tuner_set_bbgain) (struct dvb_frontend *fe, u32 gain); +- int (*tuner_get_bbgain) (struct dvb_frontend *fe, u32 *gain); +- int (*tuner_set_refclk) (struct dvb_frontend *fe, u32 refclk); +- int (*tuner_get_status) (struct dvb_frontend *fe, u32 *status); +-}; +- +- +-#if defined(CONFIG_DVB_STV6110x) || (defined(CONFIG_DVB_STV6110x_MODULE) && defined(MODULE)) +- +-extern struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, +- const struct stv6110x_config *config, +- struct i2c_adapter *i2c); +- +-#else +-static inline struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, +- const struct stv6110x_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif /* CONFIG_DVB_STV6110x */ +- +-#endif /* __STV6110x_H */ +diff --git a/drivers/media/dvb/frontends/stv6110x_priv.h b/drivers/media/dvb/frontends/stv6110x_priv.h +deleted file mode 100644 +index 0ec936a..0000000 +--- a/drivers/media/dvb/frontends/stv6110x_priv.h ++++ /dev/null +@@ -1,76 +0,0 @@ +-/* +- STV6110(A) Silicon tuner driver +- +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV6110x_PRIV_H +-#define __STV6110x_PRIV_H +- +-#define FE_ERROR 0 +-#define FE_NOTICE 1 +-#define FE_INFO 2 +-#define FE_DEBUG 3 +-#define FE_DEBUGREG 4 +- +-#define dprintk(__y, __z, format, arg...) do { \ +- if (__z) { \ +- if ((verbose > FE_ERROR) && (verbose > __y)) \ +- printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_NOTICE) && (verbose > __y)) \ +- printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_INFO) && (verbose > __y)) \ +- printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ +- else if ((verbose > FE_DEBUG) && (verbose > __y)) \ +- printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ +- } else { \ +- if (verbose > __y) \ +- printk(format, ##arg); \ +- } \ +-} while (0) +- +- +-#define STV6110x_SETFIELD(mask, bitf, val) \ +- (mask = (mask & (~(((1 << STV6110x_WIDTH_##bitf) - 1) << \ +- STV6110x_OFFST_##bitf))) | \ +- (val << STV6110x_OFFST_##bitf)) +- +-#define STV6110x_GETFIELD(bitf, val) \ +- ((val >> STV6110x_OFFST_##bitf) & \ +- ((1 << STV6110x_WIDTH_##bitf) - 1)) +- +-#define MAKEWORD16(a, b) (((a) << 8) | (b)) +- +-#define LSB(x) ((x & 0xff)) +-#define MSB(y) ((y >> 8) & 0xff) +- +-#define TRIALS 10 +-#define R_DIV(__div) (1 << (__div + 1)) +-#define REFCLOCK_kHz (stv6110x->config->refclk / 1000) +-#define REFCLOCK_MHz (stv6110x->config->refclk / 1000000) +- +-struct stv6110x_state { +- struct i2c_adapter *i2c; +- const struct stv6110x_config *config; +- u8 regs[8]; +- +- struct stv6110x_devctl *devctl; +-}; +- +-#endif /* __STV6110x_PRIV_H */ +diff --git a/drivers/media/dvb/frontends/stv6110x_reg.h b/drivers/media/dvb/frontends/stv6110x_reg.h +deleted file mode 100644 +index 93e5c70..0000000 +--- a/drivers/media/dvb/frontends/stv6110x_reg.h ++++ /dev/null +@@ -1,82 +0,0 @@ +-/* +- STV6110(A) Silicon tuner driver +- +- Copyright (C) Manu Abraham +- +- Copyright (C) ST Microelectronics +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __STV6110x_REG_H +-#define __STV6110x_REG_H +- +-#define STV6110x_CTRL1 0x00 +-#define STV6110x_OFFST_CTRL1_K 3 +-#define STV6110x_WIDTH_CTRL1_K 5 +-#define STV6110x_OFFST_CTRL1_LPT 2 +-#define STV6110x_WIDTH_CTRL1_LPT 1 +-#define STV6110x_OFFST_CTRL1_RX 1 +-#define STV6110x_WIDTH_CTRL1_RX 1 +-#define STV6110x_OFFST_CTRL1_SYN 0 +-#define STV6110x_WIDTH_CTRL1_SYN 1 +- +-#define STV6110x_CTRL2 0x01 +-#define STV6110x_OFFST_CTRL2_CO_DIV 6 +-#define STV6110x_WIDTH_CTRL2_CO_DIV 2 +-#define STV6110x_OFFST_CTRL2_RSVD 5 +-#define STV6110x_WIDTH_CTRL2_RSVD 1 +-#define STV6110x_OFFST_CTRL2_REFOUT_SEL 4 +-#define STV6110x_WIDTH_CTRL2_REFOUT_SEL 1 +-#define STV6110x_OFFST_CTRL2_BBGAIN 0 +-#define STV6110x_WIDTH_CTRL2_BBGAIN 4 +- +-#define STV6110x_TNG0 0x02 +-#define STV6110x_OFFST_TNG0_N_DIV_7_0 0 +-#define STV6110x_WIDTH_TNG0_N_DIV_7_0 8 +- +-#define STV6110x_TNG1 0x03 +-#define STV6110x_OFFST_TNG1_R_DIV 6 +-#define STV6110x_WIDTH_TNG1_R_DIV 2 +-#define STV6110x_OFFST_TNG1_PRESC32_ON 5 +-#define STV6110x_WIDTH_TNG1_PRESC32_ON 1 +-#define STV6110x_OFFST_TNG1_DIV4SEL 4 +-#define STV6110x_WIDTH_TNG1_DIV4SEL 1 +-#define STV6110x_OFFST_TNG1_N_DIV_11_8 0 +-#define STV6110x_WIDTH_TNG1_N_DIV_11_8 4 +- +- +-#define STV6110x_CTRL3 0x04 +-#define STV6110x_OFFST_CTRL3_DCLOOP_OFF 7 +-#define STV6110x_WIDTH_CTRL3_DCLOOP_OFF 1 +-#define STV6110x_OFFST_CTRL3_RCCLK_OFF 6 +-#define STV6110x_WIDTH_CTRL3_RCCLK_OFF 1 +-#define STV6110x_OFFST_CTRL3_ICP 5 +-#define STV6110x_WIDTH_CTRL3_ICP 1 +-#define STV6110x_OFFST_CTRL3_CF 0 +-#define STV6110x_WIDTH_CTRL3_CF 5 +- +-#define STV6110x_STAT1 0x05 +-#define STV6110x_OFFST_STAT1_CALVCO_STRT 2 +-#define STV6110x_WIDTH_STAT1_CALVCO_STRT 1 +-#define STV6110x_OFFST_STAT1_CALRC_STRT 1 +-#define STV6110x_WIDTH_STAT1_CALRC_STRT 1 +-#define STV6110x_OFFST_STAT1_LOCK 0 +-#define STV6110x_WIDTH_STAT1_LOCK 1 +- +-#define STV6110x_STAT2 0x06 +-#define STV6110x_STAT3 0x07 +- +-#endif /* __STV6110x_REG_H */ +diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c +deleted file mode 100644 +index 1bff7f4..0000000 +--- a/drivers/media/dvb/frontends/tda10021.c ++++ /dev/null +@@ -1,528 +0,0 @@ +-/* +- TDA10021 - Single Chip Cable Channel Receiver driver module +- used on the Siemens DVB-C cards +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- Copyright (C) 2004 Markus Schulz +- Support for TDA10021 +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "tda1002x.h" +- +- +-struct tda10021_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct tda1002x_config* config; +- struct dvb_frontend frontend; +- +- u8 pwm; +- u8 reg0; +-}; +- +- +-#if 0 +-#define dprintk(x...) printk(x) +-#else +-#define dprintk(x...) +-#endif +- +-static int verbose; +- +-#define XIN 57840000UL +- +-#define FIN (XIN >> 4) +- +-static int tda10021_inittab_size = 0x40; +-static u8 tda10021_inittab[0x40]= +-{ +- 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, +- 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, +- 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, +- 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, +- 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, +- 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, +- 0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00, +- 0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00, +-}; +- +-static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- int ret; +- +- ret = i2c_transfer (state->i2c, &msg, 1); +- if (ret != 1) +- printk("DVB: TDA10021(%d): %s, writereg error " +- "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", +- state->frontend.dvb->num, __func__, reg, data, ret); +- +- msleep(10); +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) +-{ +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- int ret; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- // Don't print an error message if the id is read. +- if (ret != 2 && reg != 0x1a) +- printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", +- __func__, ret); +- return b1[0]; +-} +- +-//get access to tuner +-static int lock_tuner(struct tda10021_state* state) +-{ +- u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 }; +- struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; +- +- if(i2c_transfer(state->i2c, &msg, 1) != 1) +- { +- printk("tda10021: lock tuner fails\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-//release access from tuner +-static int unlock_tuner(struct tda10021_state* state) +-{ +- u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f }; +- struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; +- +- if(i2c_transfer(state->i2c, &msg_post, 1) != 1) +- { +- printk("tda10021: unlock tuner fails\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, +- fe_spectral_inversion_t inversion) +-{ +- reg0 |= state->reg0 & 0x63; +- +- if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) +- reg0 &= ~0x20; +- else +- reg0 |= 0x20; +- +- _tda10021_writereg (state, 0x00, reg0 & 0xfe); +- _tda10021_writereg (state, 0x00, reg0 | 0x01); +- +- state->reg0 = reg0; +- return 0; +-} +- +-static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate) +-{ +- s32 BDR; +- s32 BDRI; +- s16 SFIL=0; +- u16 NDEC = 0; +- u32 tmp, ratio; +- +- if (symbolrate > XIN/2) +- symbolrate = XIN/2; +- if (symbolrate < 500000) +- symbolrate = 500000; +- +- if (symbolrate < XIN/16) NDEC = 1; +- if (symbolrate < XIN/32) NDEC = 2; +- if (symbolrate < XIN/64) NDEC = 3; +- +- if (symbolrate < (u32)(XIN/12.3)) SFIL = 1; +- if (symbolrate < (u32)(XIN/16)) SFIL = 0; +- if (symbolrate < (u32)(XIN/24.6)) SFIL = 1; +- if (symbolrate < (u32)(XIN/32)) SFIL = 0; +- if (symbolrate < (u32)(XIN/49.2)) SFIL = 1; +- if (symbolrate < (u32)(XIN/64)) SFIL = 0; +- if (symbolrate < (u32)(XIN/98.4)) SFIL = 1; +- +- symbolrate <<= NDEC; +- ratio = (symbolrate << 4) / FIN; +- tmp = ((symbolrate << 4) % FIN) << 8; +- ratio = (ratio << 8) + tmp / FIN; +- tmp = (tmp % FIN) << 8; +- ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN); +- +- BDR = ratio; +- BDRI = (((XIN << 5) / symbolrate) + 1) / 2; +- +- if (BDRI > 0xFF) +- BDRI = 0xFF; +- +- SFIL = (SFIL << 4) | tda10021_inittab[0x0E]; +- +- NDEC = (NDEC << 6) | tda10021_inittab[0x03]; +- +- _tda10021_writereg (state, 0x03, NDEC); +- _tda10021_writereg (state, 0x0a, BDR&0xff); +- _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff); +- _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f); +- +- _tda10021_writereg (state, 0x0d, BDRI); +- _tda10021_writereg (state, 0x0e, SFIL); +- +- return 0; +-} +- +-static int tda10021_init (struct dvb_frontend *fe) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- int i; +- +- dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num); +- +- //_tda10021_writereg (fe, 0, 0); +- +- for (i=0; ipwm); +- +- //Comment by markus +- //0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0) +- //0x2A[4] == BYPPLL -> Power down mode (default 1) +- //0x2A[5] == LCK -> PLL Lock Flag +- //0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0) +- +- //Activate PLL +- _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef); +- return 0; +-} +- +-struct qam_params { +- u8 conf, agcref, lthr, mseth, aref; +-}; +- +-static int tda10021_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- unsigned qam = c->modulation; +- bool is_annex_c; +- u32 reg0x3d; +- struct tda10021_state* state = fe->demodulator_priv; +- static const struct qam_params qam_params[] = { +- /* Modulation Conf AGCref LTHR MSETH AREF */ +- [QPSK] = { 0x14, 0x78, 0x78, 0x8c, 0x96 }, +- [QAM_16] = { 0x00, 0x8c, 0x87, 0xa2, 0x91 }, +- [QAM_32] = { 0x04, 0x8c, 0x64, 0x74, 0x96 }, +- [QAM_64] = { 0x08, 0x6a, 0x46, 0x43, 0x6a }, +- [QAM_128] = { 0x0c, 0x78, 0x36, 0x34, 0x7e }, +- [QAM_256] = { 0x10, 0x5c, 0x26, 0x23, 0x6b }, +- }; +- +- switch (delsys) { +- case SYS_DVBC_ANNEX_A: +- is_annex_c = false; +- break; +- case SYS_DVBC_ANNEX_C: +- is_annex_c = true; +- break; +- default: +- return -EINVAL; +- } +- +- /* +- * gcc optimizes the code bellow the same way as it would code: +- * "if (qam > 5) return -EINVAL;" +- * Yet, the code is clearer, as it shows what QAM standards are +- * supported by the driver, and avoids the usage of magic numbers on +- * it. +- */ +- switch (qam) { +- case QPSK: +- case QAM_16: +- case QAM_32: +- case QAM_64: +- case QAM_128: +- case QAM_256: +- break; +- default: +- return -EINVAL; +- } +- +- if (c->inversion != INVERSION_ON && c->inversion != INVERSION_OFF) +- return -EINVAL; +- +- /*printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->symbol_rate);*/ +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- tda10021_set_symbolrate(state, c->symbol_rate); +- _tda10021_writereg(state, 0x34, state->pwm); +- +- _tda10021_writereg(state, 0x01, qam_params[qam].agcref); +- _tda10021_writereg(state, 0x05, qam_params[qam].lthr); +- _tda10021_writereg(state, 0x08, qam_params[qam].mseth); +- _tda10021_writereg(state, 0x09, qam_params[qam].aref); +- +- /* +- * Bit 0 == 0 means roll-off = 0.15 (Annex A) +- * == 1 means roll-off = 0.13 (Annex C) +- */ +- reg0x3d = tda10021_readreg (state, 0x3d); +- if (is_annex_c) +- _tda10021_writereg (state, 0x3d, 0x01 | reg0x3d); +- else +- _tda10021_writereg (state, 0x3d, 0xfe & reg0x3d); +- tda10021_setup_reg0(state, qam_params[qam].conf, c->inversion); +- +- return 0; +-} +- +-static int tda10021_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- int sync; +- +- *status = 0; +- //0x11[0] == EQALGO -> Equalizer algorithms state +- //0x11[1] == CARLOCK -> Carrier locked +- //0x11[2] == FSYNC -> Frame synchronisation +- //0x11[3] == FEL -> Front End locked +- //0x11[6] == NODVB -> DVB Mode Information +- sync = tda10021_readreg (state, 0x11); +- +- if (sync & 2) +- *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; +- +- if (sync & 4) +- *status |= FE_HAS_SYNC|FE_HAS_VITERBI; +- +- if (sync & 8) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int tda10021_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- u32 _ber = tda10021_readreg(state, 0x14) | +- (tda10021_readreg(state, 0x15) << 8) | +- ((tda10021_readreg(state, 0x16) & 0x0f) << 16); +- _tda10021_writereg(state, 0x10, (tda10021_readreg(state, 0x10) & ~0xc0) +- | (tda10021_inittab[0x10] & 0xc0)); +- *ber = 10 * _ber; +- +- return 0; +-} +- +-static int tda10021_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- u8 config = tda10021_readreg(state, 0x02); +- u8 gain = tda10021_readreg(state, 0x17); +- if (config & 0x02) +- /* the agc value is inverted */ +- gain = ~gain; +- *strength = (gain << 8) | gain; +- +- return 0; +-} +- +-static int tda10021_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- u8 quality = ~tda10021_readreg(state, 0x18); +- *snr = (quality << 8) | quality; +- +- return 0; +-} +- +-static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- *ucblocks = tda10021_readreg (state, 0x13) & 0x7f; +- if (*ucblocks == 0x7f) +- *ucblocks = 0xffffffff; +- +- /* reset uncorrected block counter */ +- _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf); +- _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]); +- +- return 0; +-} +- +-static int tda10021_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda10021_state* state = fe->demodulator_priv; +- int sync; +- s8 afc = 0; +- +- sync = tda10021_readreg(state, 0x11); +- afc = tda10021_readreg(state, 0x19); +- if (verbose) { +- /* AFC only valid when carrier has been recovered */ +- printk(sync & 2 ? "DVB: TDA10021(%d): AFC (%d) %dHz\n" : +- "DVB: TDA10021(%d): [AFC (%d) %dHz]\n", +- state->frontend.dvb->num, afc, +- -((s32)p->symbol_rate * afc) >> 10); +- } +- +- p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; +- p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; +- +- p->fec_inner = FEC_NONE; +- p->frequency = ((p->frequency + 31250) / 62500) * 62500; +- +- if (sync & 2) +- p->frequency -= ((s32)p->symbol_rate * afc) >> 10; +- +- return 0; +-} +- +-static int tda10021_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- if (enable) { +- lock_tuner(state); +- } else { +- unlock_tuner(state); +- } +- return 0; +-} +- +-static int tda10021_sleep(struct dvb_frontend* fe) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- +- _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */ +- _tda10021_writereg (state, 0x00, 0x80); /* standby */ +- +- return 0; +-} +- +-static void tda10021_release(struct dvb_frontend* fe) +-{ +- struct tda10021_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops tda10021_ops; +- +-struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, +- struct i2c_adapter* i2c, +- u8 pwm) +-{ +- struct tda10021_state* state = NULL; +- u8 id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->pwm = pwm; +- state->reg0 = tda10021_inittab[0]; +- +- /* check if the demod is there */ +- id = tda10021_readreg(state, 0x1a); +- if ((id & 0xf0) != 0x70) goto error; +- +- /* Don't claim TDA10023 */ +- if (id == 0x7d) +- goto error; +- +- printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", +- state->config->demod_address, id); +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops tda10021_ops = { +- .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, +- .info = { +- .name = "Philips TDA10021 DVB-C", +- .frequency_stepsize = 62500, +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ +- .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ +- #if 0 +- .frequency_tolerance = ???, +- .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ +- #endif +- .caps = 0x400 | //FE_CAN_QAM_4 +- FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | +- FE_CAN_FEC_AUTO +- }, +- +- .release = tda10021_release, +- +- .init = tda10021_init, +- .sleep = tda10021_sleep, +- .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, +- +- .set_frontend = tda10021_set_parameters, +- .get_frontend = tda10021_get_frontend, +- +- .read_status = tda10021_read_status, +- .read_ber = tda10021_read_ber, +- .read_signal_strength = tda10021_read_signal_strength, +- .read_snr = tda10021_read_snr, +- .read_ucblocks = tda10021_read_ucblocks, +-}; +- +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); +- +-MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); +-MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(tda10021_attach); +diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c +deleted file mode 100644 +index ca1e0d5..0000000 +--- a/drivers/media/dvb/frontends/tda10023.c ++++ /dev/null +@@ -1,610 +0,0 @@ +-/* +- TDA10023 - DVB-C decoder +- (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card) +- +- Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de) +- Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com) +- +- Remotely based on tda10021.c +- Copyright (C) 1999 Convergence Integrated Media GmbH +- Copyright (C) 2004 Markus Schulz +- Support for TDA10021 +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "dvb_frontend.h" +-#include "tda1002x.h" +- +-#define REG0_INIT_VAL 0x23 +- +-struct tda10023_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct tda10023_config *config; +- struct dvb_frontend frontend; +- +- u8 pwm; +- u8 reg0; +- +- /* clock settings */ +- u32 xtal; +- u8 pll_m; +- u8 pll_p; +- u8 pll_n; +- u32 sysclk; +-}; +- +-#define dprintk(x...) +- +-static int verbose; +- +-static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) +-{ +- u8 b0 [] = { reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- int ret; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- if (ret != 2) { +- int num = state->frontend.dvb ? state->frontend.dvb->num : -1; +- printk(KERN_ERR "DVB: TDA10023(%d): %s: readreg error " +- "(reg == 0x%02x, ret == %i)\n", +- num, __func__, reg, ret); +- } +- return b1[0]; +-} +- +-static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) +-{ +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- int ret; +- +- ret = i2c_transfer (state->i2c, &msg, 1); +- if (ret != 1) { +- int num = state->frontend.dvb ? state->frontend.dvb->num : -1; +- printk(KERN_ERR "DVB: TDA10023(%d): %s, writereg error " +- "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", +- num, __func__, reg, data, ret); +- } +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +- +-static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data) +-{ +- if (mask==0xff) +- return tda10023_writereg(state, reg, data); +- else { +- u8 val; +- val=tda10023_readreg(state,reg); +- val&=~mask; +- val|=(data&mask); +- return tda10023_writereg(state, reg, val); +- } +-} +- +-static void tda10023_writetab(struct tda10023_state* state, u8* tab) +-{ +- u8 r,m,v; +- while (1) { +- r=*tab++; +- m=*tab++; +- v=*tab++; +- if (r==0xff) { +- if (m==0xff) +- break; +- else +- msleep(m); +- } +- else +- tda10023_writebit(state,r,m,v); +- } +-} +- +-//get access to tuner +-static int lock_tuner(struct tda10023_state* state) +-{ +- u8 buf[2] = { 0x0f, 0xc0 }; +- struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; +- +- if(i2c_transfer(state->i2c, &msg, 1) != 1) +- { +- printk("tda10023: lock tuner fails\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-//release access from tuner +-static int unlock_tuner(struct tda10023_state* state) +-{ +- u8 buf[2] = { 0x0f, 0x40 }; +- struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; +- +- if(i2c_transfer(state->i2c, &msg_post, 1) != 1) +- { +- printk("tda10023: unlock tuner fails\n"); +- return -EREMOTEIO; +- } +- return 0; +-} +- +-static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0) +-{ +- reg0 |= state->reg0 & 0x63; +- +- tda10023_writereg (state, 0x00, reg0 & 0xfe); +- tda10023_writereg (state, 0x00, reg0 | 0x01); +- +- state->reg0 = reg0; +- return 0; +-} +- +-static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) +-{ +- s32 BDR; +- s32 BDRI; +- s16 SFIL=0; +- u16 NDEC = 0; +- +- /* avoid floating point operations multiplying syscloc and divider +- by 10 */ +- u32 sysclk_x_10 = state->sysclk * 10; +- +- if (sr < (u32)(sysclk_x_10/984)) { +- NDEC=3; +- SFIL=1; +- } else if (sr < (u32)(sysclk_x_10/640)) { +- NDEC=3; +- SFIL=0; +- } else if (sr < (u32)(sysclk_x_10/492)) { +- NDEC=2; +- SFIL=1; +- } else if (sr < (u32)(sysclk_x_10/320)) { +- NDEC=2; +- SFIL=0; +- } else if (sr < (u32)(sysclk_x_10/246)) { +- NDEC=1; +- SFIL=1; +- } else if (sr < (u32)(sysclk_x_10/160)) { +- NDEC=1; +- SFIL=0; +- } else if (sr < (u32)(sysclk_x_10/123)) { +- NDEC=0; +- SFIL=1; +- } +- +- BDRI = (state->sysclk)*16; +- BDRI>>=NDEC; +- BDRI +=sr/2; +- BDRI /=sr; +- +- if (BDRI>255) +- BDRI=255; +- +- { +- u64 BDRX; +- +- BDRX=1<<(24+NDEC); +- BDRX*=sr; +- do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */ +- +- BDR=(s32)BDRX; +- } +- dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n", +- sr, BDR, BDRI, NDEC); +- tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); +- tda10023_writereg (state, 0x0a, BDR&255); +- tda10023_writereg (state, 0x0b, (BDR>>8)&255); +- tda10023_writereg (state, 0x0c, (BDR>>16)&31); +- tda10023_writereg (state, 0x0d, BDRI); +- tda10023_writereg (state, 0x3d, (SFIL<<7)); +- return 0; +-} +- +-static int tda10023_init (struct dvb_frontend *fe) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- u8 tda10023_inittab[] = { +-/* reg mask val */ +-/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */ +-/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +-/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */ +-/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +- /* PLL1 */ +-/* 012 */ 0x28, 0xff, (state->pll_m-1), +- /* PLL2 */ +-/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1), +- /* GPR FSAMPLING=1 */ +-/* 018 */ 0x00, 0xff, REG0_INIT_VAL, +-/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */ +-/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +-/* 027 */ 0x1f, 0xff, 0x00, /* RESET */ +-/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +-/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */ +-/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */ +- +-/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */ +-/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */ +-/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 +- PPWMTUN=0 PPWMIF=0 */ +-/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */ +-/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */ +-/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */ +-/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */ +-/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */ +-/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */ +-/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */ +-/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */ +-/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */ +-/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */ +-/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */ +-/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */ +-/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */ +-/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */ +-/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */ +-/* 093 */ 0x12, 0xff, TDA10023_OUTPUT_MODE_PARALLEL_B, /* +- INTP1 POCLKP=1 FEL=1 MFS=0 */ +-/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */ +-/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */ +-/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */ +-/* 105 */ 0xc4, 0xff, 0x00, +-/* 108 */ 0xc3, 0x30, 0x00, +-/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */ +-/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */ +-/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */ +-/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */ +-/* 123 */ 0xff, 0xff, 0xff +-}; +- dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num); +- +- /* override default values if set in config */ +- if (state->config->deltaf) { +- tda10023_inittab[80] = (state->config->deltaf & 0xff); +- tda10023_inittab[83] = (state->config->deltaf >> 8); +- } +- +- if (state->config->output_mode) +- tda10023_inittab[95] = state->config->output_mode; +- +- tda10023_writetab(state, tda10023_inittab); +- +- return 0; +-} +- +-struct qam_params { +- u8 qam, lockthr, mseth, aref, agcrefnyq, eragnyq_thd; +-}; +- +-static int tda10023_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 delsys = c->delivery_system; +- unsigned qam = c->modulation; +- bool is_annex_c; +- struct tda10023_state* state = fe->demodulator_priv; +- static const struct qam_params qam_params[] = { +- /* Modulation QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD */ +- [QPSK] = { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, +- [QAM_16] = { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, +- [QAM_32] = { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, +- [QAM_64] = { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, +- [QAM_128] = { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, +- [QAM_256] = { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, +- }; +- +- switch (delsys) { +- case SYS_DVBC_ANNEX_A: +- is_annex_c = false; +- break; +- case SYS_DVBC_ANNEX_C: +- is_annex_c = true; +- break; +- default: +- return -EINVAL; +- } +- +- /* +- * gcc optimizes the code bellow the same way as it would code: +- * "if (qam > 5) return -EINVAL;" +- * Yet, the code is clearer, as it shows what QAM standards are +- * supported by the driver, and avoids the usage of magic numbers on +- * it. +- */ +- switch (qam) { +- case QPSK: +- case QAM_16: +- case QAM_32: +- case QAM_64: +- case QAM_128: +- case QAM_256: +- break; +- default: +- return -EINVAL; +- } +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- tda10023_set_symbolrate(state, c->symbol_rate); +- tda10023_writereg(state, 0x05, qam_params[qam].lockthr); +- tda10023_writereg(state, 0x08, qam_params[qam].mseth); +- tda10023_writereg(state, 0x09, qam_params[qam].aref); +- tda10023_writereg(state, 0xb4, qam_params[qam].agcrefnyq); +- tda10023_writereg(state, 0xb6, qam_params[qam].eragnyq_thd); +-#if 0 +- tda10023_writereg(state, 0x04, (c->inversion ? 0x12 : 0x32)); +- tda10023_writebit(state, 0x04, 0x60, (c->inversion ? 0 : 0x20)); +-#endif +- tda10023_writebit(state, 0x04, 0x40, 0x40); +- +- if (is_annex_c) +- tda10023_writebit(state, 0x3d, 0xfc, 0x03); +- else +- tda10023_writebit(state, 0x3d, 0xfc, 0x02); +- +- tda10023_setup_reg0(state, qam_params[qam].qam); +- +- return 0; +-} +- +-static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- int sync; +- +- *status = 0; +- +- //0x11[1] == CARLOCK -> Carrier locked +- //0x11[2] == FSYNC -> Frame synchronisation +- //0x11[3] == FEL -> Front End locked +- //0x11[6] == NODVB -> DVB Mode Information +- sync = tda10023_readreg (state, 0x11); +- +- if (sync & 2) +- *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; +- +- if (sync & 4) +- *status |= FE_HAS_SYNC|FE_HAS_VITERBI; +- +- if (sync & 8) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- u8 a,b,c; +- a=tda10023_readreg(state, 0x14); +- b=tda10023_readreg(state, 0x15); +- c=tda10023_readreg(state, 0x16)&0xf; +- tda10023_writebit (state, 0x10, 0xc0, 0x00); +- +- *ber = a | (b<<8)| (c<<16); +- return 0; +-} +- +-static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- u8 ifgain=tda10023_readreg(state, 0x2f); +- +- u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16; +- // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90 +- if (gain>0x90) +- gain=gain+2*(gain-0x90); +- if (gain>255) +- gain=255; +- +- *strength = (gain<<8)|gain; +- return 0; +-} +- +-static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- +- u8 quality = ~tda10023_readreg(state, 0x18); +- *snr = (quality << 8) | quality; +- return 0; +-} +- +-static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- u8 a,b,c,d; +- a= tda10023_readreg (state, 0x74); +- b= tda10023_readreg (state, 0x75); +- c= tda10023_readreg (state, 0x76); +- d= tda10023_readreg (state, 0x77); +- *ucblocks = a | (b<<8)|(c<<16)|(d<<24); +- +- tda10023_writebit (state, 0x10, 0x20,0x00); +- tda10023_writebit (state, 0x10, 0x20,0x20); +- tda10023_writebit (state, 0x13, 0x01, 0x00); +- +- return 0; +-} +- +-static int tda10023_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda10023_state* state = fe->demodulator_priv; +- int sync,inv; +- s8 afc = 0; +- +- sync = tda10023_readreg(state, 0x11); +- afc = tda10023_readreg(state, 0x19); +- inv = tda10023_readreg(state, 0x04); +- +- if (verbose) { +- /* AFC only valid when carrier has been recovered */ +- printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" : +- "DVB: TDA10023(%d): [AFC (%d) %dHz]\n", +- state->frontend.dvb->num, afc, +- -((s32)p->symbol_rate * afc) >> 10); +- } +- +- p->inversion = (inv&0x20?0:1); +- p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; +- +- p->fec_inner = FEC_NONE; +- p->frequency = ((p->frequency + 31250) / 62500) * 62500; +- +- if (sync & 2) +- p->frequency -= ((s32)p->symbol_rate * afc) >> 10; +- +- return 0; +-} +- +-static int tda10023_sleep(struct dvb_frontend* fe) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- +- tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */ +- tda10023_writereg (state, 0x00, 0x80); /* standby */ +- +- return 0; +-} +- +-static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- +- if (enable) { +- lock_tuner(state); +- } else { +- unlock_tuner(state); +- } +- return 0; +-} +- +-static void tda10023_release(struct dvb_frontend* fe) +-{ +- struct tda10023_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops tda10023_ops; +- +-struct dvb_frontend *tda10023_attach(const struct tda10023_config *config, +- struct i2c_adapter *i2c, +- u8 pwm) +-{ +- struct tda10023_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda10023_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* wakeup if in standby */ +- tda10023_writereg (state, 0x00, 0x33); +- /* check if the demod is there */ +- if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); +- state->pwm = pwm; +- state->reg0 = REG0_INIT_VAL; +- if (state->config->xtal) { +- state->xtal = state->config->xtal; +- state->pll_m = state->config->pll_m; +- state->pll_p = state->config->pll_p; +- state->pll_n = state->config->pll_n; +- } else { +- /* set default values if not defined in config */ +- state->xtal = 28920000; +- state->pll_m = 8; +- state->pll_p = 4; +- state->pll_n = 1; +- } +- +- /* calc sysclk */ +- state->sysclk = (state->xtal * state->pll_m / \ +- (state->pll_n * state->pll_p)); +- +- state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64; +- state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4; +- +- dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n", +- __func__, state->xtal, state->pll_m, state->pll_p, +- state->pll_n); +- +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops tda10023_ops = { +- .delsys = { SYS_DVBC_ANNEX_A, SYS_DVBC_ANNEX_C }, +- .info = { +- .name = "Philips TDA10023 DVB-C", +- .frequency_stepsize = 62500, +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .symbol_rate_min = 0, /* set in tda10023_attach */ +- .symbol_rate_max = 0, /* set in tda10023_attach */ +- .caps = 0x400 | //FE_CAN_QAM_4 +- FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | FE_CAN_QAM_256 | +- FE_CAN_FEC_AUTO +- }, +- +- .release = tda10023_release, +- +- .init = tda10023_init, +- .sleep = tda10023_sleep, +- .i2c_gate_ctrl = tda10023_i2c_gate_ctrl, +- +- .set_frontend = tda10023_set_parameters, +- .get_frontend = tda10023_get_frontend, +- .read_status = tda10023_read_status, +- .read_ber = tda10023_read_ber, +- .read_signal_strength = tda10023_read_signal_strength, +- .read_snr = tda10023_read_snr, +- .read_ucblocks = tda10023_read_ucblocks, +-}; +- +- +-MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); +-MODULE_AUTHOR("Georg Acher, Hartmut Birr"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(tda10023_attach); +diff --git a/drivers/media/dvb/frontends/tda1002x.h b/drivers/media/dvb/frontends/tda1002x.h +deleted file mode 100644 +index 04d1941..0000000 +--- a/drivers/media/dvb/frontends/tda1002x.h ++++ /dev/null +@@ -1,87 +0,0 @@ +-/* +- TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module +- used on the the Siemens DVB-C cards +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- Copyright (C) 2004 Markus Schulz +- Support for TDA10021 +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef TDA1002x_H +-#define TDA1002x_H +- +-#include +- +-struct tda1002x_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- u8 invert; +-}; +- +-enum tda10023_output_mode { +- TDA10023_OUTPUT_MODE_PARALLEL_A = 0xe0, +- TDA10023_OUTPUT_MODE_PARALLEL_B = 0xa1, +- TDA10023_OUTPUT_MODE_PARALLEL_C = 0xa0, +- TDA10023_OUTPUT_MODE_SERIAL, /* TODO: not implemented */ +-}; +- +-struct tda10023_config { +- /* the demodulator's i2c address */ +- u8 demod_address; +- u8 invert; +- +- /* clock settings */ +- u32 xtal; /* defaults: 28920000 */ +- u8 pll_m; /* defaults: 8 */ +- u8 pll_p; /* defaults: 4 */ +- u8 pll_n; /* defaults: 1 */ +- +- /* MPEG2 TS output mode */ +- u8 output_mode; +- +- /* input freq offset + baseband conversion type */ +- u16 deltaf; +-}; +- +-#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, +- struct i2c_adapter* i2c, u8 pwm); +-#else +-static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, +- struct i2c_adapter* i2c, u8 pwm) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TDA10021 +- +-#if defined(CONFIG_DVB_TDA10023) || \ +- (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda10023_attach( +- const struct tda10023_config *config, +- struct i2c_adapter *i2c, u8 pwm); +-#else +-static inline struct dvb_frontend *tda10023_attach( +- const struct tda10023_config *config, +- struct i2c_adapter *i2c, u8 pwm) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TDA10023 +- +-#endif // TDA1002x_H +diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c +deleted file mode 100644 +index 71fb632..0000000 +--- a/drivers/media/dvb/frontends/tda10048.c ++++ /dev/null +@@ -1,1191 +0,0 @@ +-/* +- NXP TDA10048HN DVB OFDM demodulator driver +- +- Copyright (C) 2009 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "dvb_math.h" +-#include "tda10048.h" +- +-#define TDA10048_DEFAULT_FIRMWARE "dvb-fe-tda10048-1.0.fw" +-#define TDA10048_DEFAULT_FIRMWARE_SIZE 24878 +- +-/* Register name definitions */ +-#define TDA10048_IDENTITY 0x00 +-#define TDA10048_VERSION 0x01 +-#define TDA10048_DSP_CODE_CPT 0x0C +-#define TDA10048_DSP_CODE_IN 0x0E +-#define TDA10048_IN_CONF1 0x10 +-#define TDA10048_IN_CONF2 0x11 +-#define TDA10048_IN_CONF3 0x12 +-#define TDA10048_OUT_CONF1 0x14 +-#define TDA10048_OUT_CONF2 0x15 +-#define TDA10048_OUT_CONF3 0x16 +-#define TDA10048_AUTO 0x18 +-#define TDA10048_SYNC_STATUS 0x1A +-#define TDA10048_CONF_C4_1 0x1E +-#define TDA10048_CONF_C4_2 0x1F +-#define TDA10048_CODE_IN_RAM 0x20 +-#define TDA10048_CHANNEL_INFO1_R 0x22 +-#define TDA10048_CHANNEL_INFO2_R 0x23 +-#define TDA10048_CHANNEL_INFO1 0x24 +-#define TDA10048_CHANNEL_INFO2 0x25 +-#define TDA10048_TIME_ERROR_R 0x26 +-#define TDA10048_TIME_ERROR 0x27 +-#define TDA10048_FREQ_ERROR_LSB_R 0x28 +-#define TDA10048_FREQ_ERROR_MSB_R 0x29 +-#define TDA10048_FREQ_ERROR_LSB 0x2A +-#define TDA10048_FREQ_ERROR_MSB 0x2B +-#define TDA10048_IT_SEL 0x30 +-#define TDA10048_IT_STAT 0x32 +-#define TDA10048_DSP_AD_LSB 0x3C +-#define TDA10048_DSP_AD_MSB 0x3D +-#define TDA10048_DSP_REG_LSB 0x3E +-#define TDA10048_DSP_REG_MSB 0x3F +-#define TDA10048_CONF_TRISTATE1 0x44 +-#define TDA10048_CONF_TRISTATE2 0x45 +-#define TDA10048_CONF_POLARITY 0x46 +-#define TDA10048_GPIO_SP_DS0 0x48 +-#define TDA10048_GPIO_SP_DS1 0x49 +-#define TDA10048_GPIO_SP_DS2 0x4A +-#define TDA10048_GPIO_SP_DS3 0x4B +-#define TDA10048_GPIO_OUT_SEL 0x4C +-#define TDA10048_GPIO_SELECT 0x4D +-#define TDA10048_IC_MODE 0x4E +-#define TDA10048_CONF_XO 0x50 +-#define TDA10048_CONF_PLL1 0x51 +-#define TDA10048_CONF_PLL2 0x52 +-#define TDA10048_CONF_PLL3 0x53 +-#define TDA10048_CONF_ADC 0x54 +-#define TDA10048_CONF_ADC_2 0x55 +-#define TDA10048_CONF_C1_1 0x60 +-#define TDA10048_CONF_C1_3 0x62 +-#define TDA10048_AGC_CONF 0x70 +-#define TDA10048_AGC_THRESHOLD_LSB 0x72 +-#define TDA10048_AGC_THRESHOLD_MSB 0x73 +-#define TDA10048_AGC_RENORM 0x74 +-#define TDA10048_AGC_GAINS 0x76 +-#define TDA10048_AGC_TUN_MIN 0x78 +-#define TDA10048_AGC_TUN_MAX 0x79 +-#define TDA10048_AGC_IF_MIN 0x7A +-#define TDA10048_AGC_IF_MAX 0x7B +-#define TDA10048_AGC_TUN_LEVEL 0x7E +-#define TDA10048_AGC_IF_LEVEL 0x7F +-#define TDA10048_DIG_AGC_LEVEL 0x81 +-#define TDA10048_FREQ_PHY2_LSB 0x86 +-#define TDA10048_FREQ_PHY2_MSB 0x87 +-#define TDA10048_TIME_INVWREF_LSB 0x88 +-#define TDA10048_TIME_INVWREF_MSB 0x89 +-#define TDA10048_TIME_WREF_LSB 0x8A +-#define TDA10048_TIME_WREF_MID1 0x8B +-#define TDA10048_TIME_WREF_MID2 0x8C +-#define TDA10048_TIME_WREF_MSB 0x8D +-#define TDA10048_NP_OUT 0xA2 +-#define TDA10048_CELL_ID_LSB 0xA4 +-#define TDA10048_CELL_ID_MSB 0xA5 +-#define TDA10048_EXTTPS_ODD 0xAA +-#define TDA10048_EXTTPS_EVEN 0xAB +-#define TDA10048_TPS_LENGTH 0xAC +-#define TDA10048_FREE_REG_1 0xB2 +-#define TDA10048_FREE_REG_2 0xB3 +-#define TDA10048_CONF_C3_1 0xC0 +-#define TDA10048_CVBER_CTRL 0xC2 +-#define TDA10048_CBER_NMAX_LSB 0xC4 +-#define TDA10048_CBER_NMAX_MSB 0xC5 +-#define TDA10048_CBER_LSB 0xC6 +-#define TDA10048_CBER_MSB 0xC7 +-#define TDA10048_VBER_LSB 0xC8 +-#define TDA10048_VBER_MID 0xC9 +-#define TDA10048_VBER_MSB 0xCA +-#define TDA10048_CVBER_LUT 0xCC +-#define TDA10048_UNCOR_CTRL 0xCD +-#define TDA10048_UNCOR_CPT_LSB 0xCE +-#define TDA10048_UNCOR_CPT_MSB 0xCF +-#define TDA10048_SOFT_IT_C3 0xD6 +-#define TDA10048_CONF_TS2 0xE0 +-#define TDA10048_CONF_TS1 0xE1 +- +-static unsigned int debug; +- +-#define dprintk(level, fmt, arg...)\ +- do { if (debug >= level)\ +- printk(KERN_DEBUG "tda10048: " fmt, ## arg);\ +- } while (0) +- +-struct tda10048_state { +- +- struct i2c_adapter *i2c; +- +- /* We'll cache and update the attach config settings */ +- struct tda10048_config config; +- struct dvb_frontend frontend; +- +- int fwloaded; +- +- u32 freq_if_hz; +- u32 xtal_hz; +- u32 pll_mfactor; +- u32 pll_nfactor; +- u32 pll_pfactor; +- u32 sample_freq; +- +- u32 bandwidth; +-}; +- +-static struct init_tab { +- u8 reg; +- u16 data; +-} init_tab[] = { +- { TDA10048_CONF_PLL1, 0x08 }, +- { TDA10048_CONF_ADC_2, 0x00 }, +- { TDA10048_CONF_C4_1, 0x00 }, +- { TDA10048_CONF_PLL1, 0x0f }, +- { TDA10048_CONF_PLL2, 0x0a }, +- { TDA10048_CONF_PLL3, 0x43 }, +- { TDA10048_FREQ_PHY2_LSB, 0x02 }, +- { TDA10048_FREQ_PHY2_MSB, 0x0a }, +- { TDA10048_TIME_WREF_LSB, 0xbd }, +- { TDA10048_TIME_WREF_MID1, 0xe4 }, +- { TDA10048_TIME_WREF_MID2, 0xa8 }, +- { TDA10048_TIME_WREF_MSB, 0x02 }, +- { TDA10048_TIME_INVWREF_LSB, 0x04 }, +- { TDA10048_TIME_INVWREF_MSB, 0x06 }, +- { TDA10048_CONF_C4_1, 0x00 }, +- { TDA10048_CONF_C1_1, 0xa8 }, +- { TDA10048_AGC_CONF, 0x16 }, +- { TDA10048_CONF_C1_3, 0x0b }, +- { TDA10048_AGC_TUN_MIN, 0x00 }, +- { TDA10048_AGC_TUN_MAX, 0xff }, +- { TDA10048_AGC_IF_MIN, 0x00 }, +- { TDA10048_AGC_IF_MAX, 0xff }, +- { TDA10048_AGC_THRESHOLD_MSB, 0x00 }, +- { TDA10048_AGC_THRESHOLD_LSB, 0x70 }, +- { TDA10048_CVBER_CTRL, 0x38 }, +- { TDA10048_AGC_GAINS, 0x12 }, +- { TDA10048_CONF_XO, 0x00 }, +- { TDA10048_CONF_TS1, 0x07 }, +- { TDA10048_IC_MODE, 0x00 }, +- { TDA10048_CONF_TS2, 0xc0 }, +- { TDA10048_CONF_TRISTATE1, 0x21 }, +- { TDA10048_CONF_TRISTATE2, 0x00 }, +- { TDA10048_CONF_POLARITY, 0x00 }, +- { TDA10048_CONF_C4_2, 0x04 }, +- { TDA10048_CONF_ADC, 0x60 }, +- { TDA10048_CONF_ADC_2, 0x10 }, +- { TDA10048_CONF_ADC, 0x60 }, +- { TDA10048_CONF_ADC_2, 0x00 }, +- { TDA10048_CONF_C1_1, 0xa8 }, +- { TDA10048_UNCOR_CTRL, 0x00 }, +- { TDA10048_CONF_C4_2, 0x04 }, +-}; +- +-static struct pll_tab { +- u32 clk_freq_khz; +- u32 if_freq_khz; +-} pll_tab[] = { +- { TDA10048_CLK_4000, TDA10048_IF_36130 }, +- { TDA10048_CLK_16000, TDA10048_IF_3300 }, +- { TDA10048_CLK_16000, TDA10048_IF_3500 }, +- { TDA10048_CLK_16000, TDA10048_IF_3800 }, +- { TDA10048_CLK_16000, TDA10048_IF_4000 }, +- { TDA10048_CLK_16000, TDA10048_IF_4300 }, +- { TDA10048_CLK_16000, TDA10048_IF_4500 }, +- { TDA10048_CLK_16000, TDA10048_IF_5000 }, +- { TDA10048_CLK_16000, TDA10048_IF_36130 }, +-}; +- +-static int tda10048_writereg(struct tda10048_state *state, u8 reg, u8 data) +-{ +- struct tda10048_config *config = &state->config; +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { +- .addr = config->demod_address, +- .flags = 0, .buf = buf, .len = 2 }; +- +- dprintk(2, "%s(reg = 0x%02x, data = 0x%02x)\n", __func__, reg, data); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk("%s: writereg error (ret == %i)\n", __func__, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static u8 tda10048_readreg(struct tda10048_state *state, u8 reg) +-{ +- struct tda10048_config *config = &state->config; +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- { .addr = config->demod_address, +- .flags = 0, .buf = b0, .len = 1 }, +- { .addr = config->demod_address, +- .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- dprintk(2, "%s(reg = 0x%02x)\n", __func__, reg); +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk(KERN_ERR "%s: readreg error (ret == %i)\n", +- __func__, ret); +- +- return b1[0]; +-} +- +-static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg, +- const u8 *data, u16 len) +-{ +- struct tda10048_config *config = &state->config; +- int ret = -EREMOTEIO; +- struct i2c_msg msg; +- u8 *buf; +- +- dprintk(2, "%s(%d, ?, len = %d)\n", __func__, reg, len); +- +- buf = kmalloc(len + 1, GFP_KERNEL); +- if (buf == NULL) { +- ret = -ENOMEM; +- goto error; +- } +- +- *buf = reg; +- memcpy(buf + 1, data, len); +- +- msg.addr = config->demod_address; +- msg.flags = 0; +- msg.buf = buf; +- msg.len = len + 1; +- +- dprintk(2, "%s(): write len = %d\n", +- __func__, msg.len); +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- if (ret != 1) { +- printk(KERN_ERR "%s(): writereg error err %i\n", +- __func__, ret); +- ret = -EREMOTEIO; +- } +- +-error: +- kfree(buf); +- +- return ret; +-} +- +-static int tda10048_set_phy2(struct dvb_frontend *fe, u32 sample_freq_hz, +- u32 if_hz) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u64 t; +- +- dprintk(1, "%s()\n", __func__); +- +- if (sample_freq_hz == 0) +- return -EINVAL; +- +- if (if_hz < (sample_freq_hz / 2)) { +- /* PHY2 = (if2/fs) * 2^15 */ +- t = if_hz; +- t *= 10; +- t *= 32768; +- do_div(t, sample_freq_hz); +- t += 5; +- do_div(t, 10); +- } else { +- /* PHY2 = ((IF1-fs)/fs) * 2^15 */ +- t = sample_freq_hz - if_hz; +- t *= 10; +- t *= 32768; +- do_div(t, sample_freq_hz); +- t += 5; +- do_div(t, 10); +- t = ~t + 1; +- } +- +- tda10048_writereg(state, TDA10048_FREQ_PHY2_LSB, (u8)t); +- tda10048_writereg(state, TDA10048_FREQ_PHY2_MSB, (u8)(t >> 8)); +- +- return 0; +-} +- +-static int tda10048_set_wref(struct dvb_frontend *fe, u32 sample_freq_hz, +- u32 bw) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u64 t, z; +- +- dprintk(1, "%s()\n", __func__); +- +- if (sample_freq_hz == 0) +- return -EINVAL; +- +- /* WREF = (B / (7 * fs)) * 2^31 */ +- t = bw * 10; +- /* avoid warning: this decimal constant is unsigned only in ISO C90 */ +- /* t *= 2147483648 on 32bit platforms */ +- t *= (2048 * 1024); +- t *= 1024; +- z = 7 * sample_freq_hz; +- do_div(t, z); +- t += 5; +- do_div(t, 10); +- +- tda10048_writereg(state, TDA10048_TIME_WREF_LSB, (u8)t); +- tda10048_writereg(state, TDA10048_TIME_WREF_MID1, (u8)(t >> 8)); +- tda10048_writereg(state, TDA10048_TIME_WREF_MID2, (u8)(t >> 16)); +- tda10048_writereg(state, TDA10048_TIME_WREF_MSB, (u8)(t >> 24)); +- +- return 0; +-} +- +-static int tda10048_set_invwref(struct dvb_frontend *fe, u32 sample_freq_hz, +- u32 bw) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u64 t; +- +- dprintk(1, "%s()\n", __func__); +- +- if (sample_freq_hz == 0) +- return -EINVAL; +- +- /* INVWREF = ((7 * fs) / B) * 2^5 */ +- t = sample_freq_hz; +- t *= 7; +- t *= 32; +- t *= 10; +- do_div(t, bw); +- t += 5; +- do_div(t, 10); +- +- tda10048_writereg(state, TDA10048_TIME_INVWREF_LSB, (u8)t); +- tda10048_writereg(state, TDA10048_TIME_INVWREF_MSB, (u8)(t >> 8)); +- +- return 0; +-} +- +-static int tda10048_set_bandwidth(struct dvb_frontend *fe, +- u32 bw) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- dprintk(1, "%s(bw=%d)\n", __func__, bw); +- +- /* Bandwidth setting may need to be adjusted */ +- switch (bw) { +- case 6000000: +- case 7000000: +- case 8000000: +- tda10048_set_wref(fe, state->sample_freq, bw); +- tda10048_set_invwref(fe, state->sample_freq, bw); +- break; +- default: +- printk(KERN_ERR "%s() invalid bandwidth\n", __func__); +- return -EINVAL; +- } +- +- state->bandwidth = bw; +- +- return 0; +-} +- +-static int tda10048_set_if(struct dvb_frontend *fe, u32 bw) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- struct tda10048_config *config = &state->config; +- int i; +- u32 if_freq_khz; +- +- dprintk(1, "%s(bw = %d)\n", __func__, bw); +- +- /* based on target bandwidth and clk we calculate pll factors */ +- switch (bw) { +- case 6000000: +- if_freq_khz = config->dtv6_if_freq_khz; +- break; +- case 7000000: +- if_freq_khz = config->dtv7_if_freq_khz; +- break; +- case 8000000: +- if_freq_khz = config->dtv8_if_freq_khz; +- break; +- default: +- printk(KERN_ERR "%s() no default\n", __func__); +- return -EINVAL; +- } +- +- for (i = 0; i < ARRAY_SIZE(pll_tab); i++) { +- if ((pll_tab[i].clk_freq_khz == config->clk_freq_khz) && +- (pll_tab[i].if_freq_khz == if_freq_khz)) { +- +- state->freq_if_hz = pll_tab[i].if_freq_khz * 1000; +- state->xtal_hz = pll_tab[i].clk_freq_khz * 1000; +- break; +- } +- } +- if (i == ARRAY_SIZE(pll_tab)) { +- printk(KERN_ERR "%s() Incorrect attach settings\n", +- __func__); +- return -EINVAL; +- } +- +- dprintk(1, "- freq_if_hz = %d\n", state->freq_if_hz); +- dprintk(1, "- xtal_hz = %d\n", state->xtal_hz); +- dprintk(1, "- pll_mfactor = %d\n", state->pll_mfactor); +- dprintk(1, "- pll_nfactor = %d\n", state->pll_nfactor); +- dprintk(1, "- pll_pfactor = %d\n", state->pll_pfactor); +- +- /* Calculate the sample frequency */ +- state->sample_freq = state->xtal_hz * (state->pll_mfactor + 45); +- state->sample_freq /= (state->pll_nfactor + 1); +- state->sample_freq /= (state->pll_pfactor + 4); +- dprintk(1, "- sample_freq = %d\n", state->sample_freq); +- +- /* Update the I/F */ +- tda10048_set_phy2(fe, state->sample_freq, state->freq_if_hz); +- +- return 0; +-} +- +-static int tda10048_firmware_upload(struct dvb_frontend *fe) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- struct tda10048_config *config = &state->config; +- const struct firmware *fw; +- int ret; +- int pos = 0; +- int cnt; +- u8 wlen = config->fwbulkwritelen; +- +- if ((wlen != TDA10048_BULKWRITE_200) && (wlen != TDA10048_BULKWRITE_50)) +- wlen = TDA10048_BULKWRITE_200; +- +- /* request the firmware, this will block and timeout */ +- printk(KERN_INFO "%s: waiting for firmware upload (%s)...\n", +- __func__, +- TDA10048_DEFAULT_FIRMWARE); +- +- ret = request_firmware(&fw, TDA10048_DEFAULT_FIRMWARE, +- state->i2c->dev.parent); +- if (ret) { +- printk(KERN_ERR "%s: Upload failed. (file not found?)\n", +- __func__); +- return -EIO; +- } else { +- printk(KERN_INFO "%s: firmware read %Zu bytes.\n", +- __func__, +- fw->size); +- ret = 0; +- } +- +- if (fw->size != TDA10048_DEFAULT_FIRMWARE_SIZE) { +- printk(KERN_ERR "%s: firmware incorrect size\n", __func__); +- ret = -EIO; +- } else { +- printk(KERN_INFO "%s: firmware uploading\n", __func__); +- +- /* Soft reset */ +- tda10048_writereg(state, TDA10048_CONF_TRISTATE1, +- tda10048_readreg(state, TDA10048_CONF_TRISTATE1) +- & 0xfe); +- tda10048_writereg(state, TDA10048_CONF_TRISTATE1, +- tda10048_readreg(state, TDA10048_CONF_TRISTATE1) +- | 0x01); +- +- /* Put the demod into host download mode */ +- tda10048_writereg(state, TDA10048_CONF_C4_1, +- tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xf9); +- +- /* Boot the DSP */ +- tda10048_writereg(state, TDA10048_CONF_C4_1, +- tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x08); +- +- /* Prepare for download */ +- tda10048_writereg(state, TDA10048_DSP_CODE_CPT, 0); +- +- /* Download the firmware payload */ +- while (pos < fw->size) { +- +- if ((fw->size - pos) > wlen) +- cnt = wlen; +- else +- cnt = fw->size - pos; +- +- tda10048_writeregbulk(state, TDA10048_DSP_CODE_IN, +- &fw->data[pos], cnt); +- +- pos += cnt; +- } +- +- ret = -EIO; +- /* Wait up to 250ms for the DSP to boot */ +- for (cnt = 0; cnt < 250 ; cnt += 10) { +- +- msleep(10); +- +- if (tda10048_readreg(state, TDA10048_SYNC_STATUS) +- & 0x40) { +- ret = 0; +- break; +- } +- } +- } +- +- release_firmware(fw); +- +- if (ret == 0) { +- printk(KERN_INFO "%s: firmware uploaded\n", __func__); +- state->fwloaded = 1; +- } else +- printk(KERN_ERR "%s: firmware upload failed\n", __func__); +- +- return ret; +-} +- +-static int tda10048_set_inversion(struct dvb_frontend *fe, int inversion) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- +- dprintk(1, "%s(%d)\n", __func__, inversion); +- +- if (inversion == TDA10048_INVERSION_ON) +- tda10048_writereg(state, TDA10048_CONF_C1_1, +- tda10048_readreg(state, TDA10048_CONF_C1_1) | 0x20); +- else +- tda10048_writereg(state, TDA10048_CONF_C1_1, +- tda10048_readreg(state, TDA10048_CONF_C1_1) & 0xdf); +- +- return 0; +-} +- +-/* Retrieve the demod settings */ +-static int tda10048_get_tps(struct tda10048_state *state, +- struct dtv_frontend_properties *p) +-{ +- u8 val; +- +- /* Make sure the TPS regs are valid */ +- if (!(tda10048_readreg(state, TDA10048_AUTO) & 0x01)) +- return -EAGAIN; +- +- val = tda10048_readreg(state, TDA10048_OUT_CONF2); +- switch ((val & 0x60) >> 5) { +- case 0: +- p->modulation = QPSK; +- break; +- case 1: +- p->modulation = QAM_16; +- break; +- case 2: +- p->modulation = QAM_64; +- break; +- } +- switch ((val & 0x18) >> 3) { +- case 0: +- p->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- p->hierarchy = HIERARCHY_1; +- break; +- case 2: +- p->hierarchy = HIERARCHY_2; +- break; +- case 3: +- p->hierarchy = HIERARCHY_4; +- break; +- } +- switch (val & 0x07) { +- case 0: +- p->code_rate_HP = FEC_1_2; +- break; +- case 1: +- p->code_rate_HP = FEC_2_3; +- break; +- case 2: +- p->code_rate_HP = FEC_3_4; +- break; +- case 3: +- p->code_rate_HP = FEC_5_6; +- break; +- case 4: +- p->code_rate_HP = FEC_7_8; +- break; +- } +- +- val = tda10048_readreg(state, TDA10048_OUT_CONF3); +- switch (val & 0x07) { +- case 0: +- p->code_rate_LP = FEC_1_2; +- break; +- case 1: +- p->code_rate_LP = FEC_2_3; +- break; +- case 2: +- p->code_rate_LP = FEC_3_4; +- break; +- case 3: +- p->code_rate_LP = FEC_5_6; +- break; +- case 4: +- p->code_rate_LP = FEC_7_8; +- break; +- } +- +- val = tda10048_readreg(state, TDA10048_OUT_CONF1); +- switch ((val & 0x0c) >> 2) { +- case 0: +- p->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- p->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- p->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- p->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- switch (val & 0x03) { +- case 0: +- p->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 1: +- p->transmission_mode = TRANSMISSION_MODE_8K; +- break; +- } +- +- return 0; +-} +- +-static int tda10048_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- struct tda10048_config *config = &state->config; +- dprintk(1, "%s(%d)\n", __func__, enable); +- +- if (config->disable_gate_access) +- return 0; +- +- if (enable) +- return tda10048_writereg(state, TDA10048_CONF_C4_1, +- tda10048_readreg(state, TDA10048_CONF_C4_1) | 0x02); +- else +- return tda10048_writereg(state, TDA10048_CONF_C4_1, +- tda10048_readreg(state, TDA10048_CONF_C4_1) & 0xfd); +-} +- +-static int tda10048_output_mode(struct dvb_frontend *fe, int serial) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- dprintk(1, "%s(%d)\n", __func__, serial); +- +- /* Ensure pins are out of tri-state */ +- tda10048_writereg(state, TDA10048_CONF_TRISTATE1, 0x21); +- tda10048_writereg(state, TDA10048_CONF_TRISTATE2, 0x00); +- +- if (serial) { +- tda10048_writereg(state, TDA10048_IC_MODE, 0x80 | 0x20); +- tda10048_writereg(state, TDA10048_CONF_TS2, 0xc0); +- } else { +- tda10048_writereg(state, TDA10048_IC_MODE, 0x00); +- tda10048_writereg(state, TDA10048_CONF_TS2, 0x01); +- } +- +- return 0; +-} +- +-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */ +-/* TODO: Support manual tuning with specific params */ +-static int tda10048_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda10048_state *state = fe->demodulator_priv; +- +- dprintk(1, "%s(frequency=%d)\n", __func__, p->frequency); +- +- /* Update the I/F pll's if the bandwidth changes */ +- if (p->bandwidth_hz != state->bandwidth) { +- tda10048_set_if(fe, p->bandwidth_hz); +- tda10048_set_bandwidth(fe, p->bandwidth_hz); +- } +- +- if (fe->ops.tuner_ops.set_params) { +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- fe->ops.tuner_ops.set_params(fe); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* Enable demod TPS auto detection and begin acquisition */ +- tda10048_writereg(state, TDA10048_AUTO, 0x57); +- /* trigger cber and vber acquisition */ +- tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x3B); +- +- return 0; +-} +- +-/* Establish sane defaults and load firmware. */ +-static int tda10048_init(struct dvb_frontend *fe) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- struct tda10048_config *config = &state->config; +- int ret = 0, i; +- +- dprintk(1, "%s()\n", __func__); +- +- /* PLL */ +- init_tab[4].data = (u8)(state->pll_mfactor); +- init_tab[5].data = (u8)(state->pll_nfactor) | 0x40; +- +- /* Apply register defaults */ +- for (i = 0; i < ARRAY_SIZE(init_tab); i++) +- tda10048_writereg(state, init_tab[i].reg, init_tab[i].data); +- +- if (state->fwloaded == 0) +- ret = tda10048_firmware_upload(fe); +- +- /* Set either serial or parallel */ +- tda10048_output_mode(fe, config->output_mode); +- +- /* Set inversion */ +- tda10048_set_inversion(fe, config->inversion); +- +- /* Establish default RF values */ +- tda10048_set_if(fe, 8000000); +- tda10048_set_bandwidth(fe, 8000000); +- +- /* Ensure we leave the gate closed */ +- tda10048_i2c_gate_ctrl(fe, 0); +- +- return ret; +-} +- +-static int tda10048_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u8 reg; +- +- *status = 0; +- +- reg = tda10048_readreg(state, TDA10048_SYNC_STATUS); +- +- dprintk(1, "%s() status =0x%02x\n", __func__, reg); +- +- if (reg & 0x02) +- *status |= FE_HAS_CARRIER; +- +- if (reg & 0x04) +- *status |= FE_HAS_SIGNAL; +- +- if (reg & 0x08) { +- *status |= FE_HAS_LOCK; +- *status |= FE_HAS_VITERBI; +- *status |= FE_HAS_SYNC; +- } +- +- return 0; +-} +- +-static int tda10048_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- static u32 cber_current; +- u32 cber_nmax; +- u64 cber_tmp; +- +- dprintk(1, "%s()\n", __func__); +- +- /* update cber on interrupt */ +- if (tda10048_readreg(state, TDA10048_SOFT_IT_C3) & 0x01) { +- cber_tmp = tda10048_readreg(state, TDA10048_CBER_MSB) << 8 | +- tda10048_readreg(state, TDA10048_CBER_LSB); +- cber_nmax = tda10048_readreg(state, TDA10048_CBER_NMAX_MSB) << 8 | +- tda10048_readreg(state, TDA10048_CBER_NMAX_LSB); +- cber_tmp *= 100000000; +- cber_tmp *= 2; +- cber_tmp = div_u64(cber_tmp, (cber_nmax * 32) + 1); +- cber_current = (u32)cber_tmp; +- /* retrigger cber acquisition */ +- tda10048_writereg(state, TDA10048_CVBER_CTRL, 0x39); +- } +- /* actual cber is (*ber)/1e8 */ +- *ber = cber_current; +- +- return 0; +-} +- +-static int tda10048_read_signal_strength(struct dvb_frontend *fe, +- u16 *signal_strength) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u8 v; +- +- dprintk(1, "%s()\n", __func__); +- +- *signal_strength = 65535; +- +- v = tda10048_readreg(state, TDA10048_NP_OUT); +- if (v > 0) +- *signal_strength -= (v << 8) | v; +- +- return 0; +-} +- +-/* SNR lookup table */ +-static struct snr_tab { +- u8 val; +- u8 data; +-} snr_tab[] = { +- { 0, 0 }, +- { 1, 246 }, +- { 2, 215 }, +- { 3, 198 }, +- { 4, 185 }, +- { 5, 176 }, +- { 6, 168 }, +- { 7, 161 }, +- { 8, 155 }, +- { 9, 150 }, +- { 10, 146 }, +- { 11, 141 }, +- { 12, 138 }, +- { 13, 134 }, +- { 14, 131 }, +- { 15, 128 }, +- { 16, 125 }, +- { 17, 122 }, +- { 18, 120 }, +- { 19, 118 }, +- { 20, 115 }, +- { 21, 113 }, +- { 22, 111 }, +- { 23, 109 }, +- { 24, 107 }, +- { 25, 106 }, +- { 26, 104 }, +- { 27, 102 }, +- { 28, 101 }, +- { 29, 99 }, +- { 30, 98 }, +- { 31, 96 }, +- { 32, 95 }, +- { 33, 94 }, +- { 34, 92 }, +- { 35, 91 }, +- { 36, 90 }, +- { 37, 89 }, +- { 38, 88 }, +- { 39, 86 }, +- { 40, 85 }, +- { 41, 84 }, +- { 42, 83 }, +- { 43, 82 }, +- { 44, 81 }, +- { 45, 80 }, +- { 46, 79 }, +- { 47, 78 }, +- { 48, 77 }, +- { 49, 76 }, +- { 50, 76 }, +- { 51, 75 }, +- { 52, 74 }, +- { 53, 73 }, +- { 54, 72 }, +- { 56, 71 }, +- { 57, 70 }, +- { 58, 69 }, +- { 60, 68 }, +- { 61, 67 }, +- { 63, 66 }, +- { 64, 65 }, +- { 66, 64 }, +- { 67, 63 }, +- { 68, 62 }, +- { 69, 62 }, +- { 70, 61 }, +- { 72, 60 }, +- { 74, 59 }, +- { 75, 58 }, +- { 77, 57 }, +- { 79, 56 }, +- { 81, 55 }, +- { 83, 54 }, +- { 85, 53 }, +- { 87, 52 }, +- { 89, 51 }, +- { 91, 50 }, +- { 93, 49 }, +- { 95, 48 }, +- { 97, 47 }, +- { 100, 46 }, +- { 102, 45 }, +- { 104, 44 }, +- { 107, 43 }, +- { 109, 42 }, +- { 112, 41 }, +- { 114, 40 }, +- { 117, 39 }, +- { 120, 38 }, +- { 123, 37 }, +- { 125, 36 }, +- { 128, 35 }, +- { 131, 34 }, +- { 134, 33 }, +- { 138, 32 }, +- { 141, 31 }, +- { 144, 30 }, +- { 147, 29 }, +- { 151, 28 }, +- { 154, 27 }, +- { 158, 26 }, +- { 162, 25 }, +- { 165, 24 }, +- { 169, 23 }, +- { 173, 22 }, +- { 177, 21 }, +- { 181, 20 }, +- { 186, 19 }, +- { 190, 18 }, +- { 194, 17 }, +- { 199, 16 }, +- { 204, 15 }, +- { 208, 14 }, +- { 213, 13 }, +- { 218, 12 }, +- { 223, 11 }, +- { 229, 10 }, +- { 234, 9 }, +- { 239, 8 }, +- { 245, 7 }, +- { 251, 6 }, +- { 255, 5 }, +-}; +- +-static int tda10048_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- u8 v; +- int i, ret = -EINVAL; +- +- dprintk(1, "%s()\n", __func__); +- +- v = tda10048_readreg(state, TDA10048_NP_OUT); +- for (i = 0; i < ARRAY_SIZE(snr_tab); i++) { +- if (v <= snr_tab[i].val) { +- *snr = snr_tab[i].data; +- ret = 0; +- break; +- } +- } +- +- return ret; +-} +- +-static int tda10048_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- +- dprintk(1, "%s()\n", __func__); +- +- *ucblocks = tda10048_readreg(state, TDA10048_UNCOR_CPT_MSB) << 8 | +- tda10048_readreg(state, TDA10048_UNCOR_CPT_LSB); +- /* clear the uncorrected TS packets counter when saturated */ +- if (*ucblocks == 0xFFFF) +- tda10048_writereg(state, TDA10048_UNCOR_CTRL, 0x80); +- +- return 0; +-} +- +-static int tda10048_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda10048_state *state = fe->demodulator_priv; +- +- dprintk(1, "%s()\n", __func__); +- +- p->inversion = tda10048_readreg(state, TDA10048_CONF_C1_1) +- & 0x20 ? INVERSION_ON : INVERSION_OFF; +- +- return tda10048_get_tps(state, p); +-} +- +-static int tda10048_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- tune->min_delay_ms = 1000; +- return 0; +-} +- +-static void tda10048_release(struct dvb_frontend *fe) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- dprintk(1, "%s()\n", __func__); +- kfree(state); +-} +- +-static void tda10048_establish_defaults(struct dvb_frontend *fe) +-{ +- struct tda10048_state *state = fe->demodulator_priv; +- struct tda10048_config *config = &state->config; +- +- /* Validate/default the config */ +- if (config->dtv6_if_freq_khz == 0) { +- config->dtv6_if_freq_khz = TDA10048_IF_4300; +- printk(KERN_WARNING "%s() tda10048_config.dtv6_if_freq_khz " +- "is not set (defaulting to %d)\n", +- __func__, +- config->dtv6_if_freq_khz); +- } +- +- if (config->dtv7_if_freq_khz == 0) { +- config->dtv7_if_freq_khz = TDA10048_IF_4300; +- printk(KERN_WARNING "%s() tda10048_config.dtv7_if_freq_khz " +- "is not set (defaulting to %d)\n", +- __func__, +- config->dtv7_if_freq_khz); +- } +- +- if (config->dtv8_if_freq_khz == 0) { +- config->dtv8_if_freq_khz = TDA10048_IF_4300; +- printk(KERN_WARNING "%s() tda10048_config.dtv8_if_freq_khz " +- "is not set (defaulting to %d)\n", +- __func__, +- config->dtv8_if_freq_khz); +- } +- +- if (config->clk_freq_khz == 0) { +- config->clk_freq_khz = TDA10048_CLK_16000; +- printk(KERN_WARNING "%s() tda10048_config.clk_freq_khz " +- "is not set (defaulting to %d)\n", +- __func__, +- config->clk_freq_khz); +- } +-} +- +-static struct dvb_frontend_ops tda10048_ops; +- +-struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, +- struct i2c_adapter *i2c) +-{ +- struct tda10048_state *state = NULL; +- +- dprintk(1, "%s()\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state and clone the config */ +- memcpy(&state->config, config, sizeof(*config)); +- state->i2c = i2c; +- state->fwloaded = config->no_firmware; +- state->bandwidth = 8000000; +- +- /* check if the demod is present */ +- if (tda10048_readreg(state, TDA10048_IDENTITY) != 0x048) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10048_ops, +- sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- /* set pll */ +- if (config->set_pll) { +- state->pll_mfactor = config->pll_m; +- state->pll_nfactor = config->pll_n; +- state->pll_pfactor = config->pll_p; +- } else { +- state->pll_mfactor = 10; +- state->pll_nfactor = 3; +- state->pll_pfactor = 0; +- } +- +- /* Establish any defaults the the user didn't pass */ +- tda10048_establish_defaults(&state->frontend); +- +- /* Set the xtal and freq defaults */ +- if (tda10048_set_if(&state->frontend, 8000000) != 0) +- goto error; +- +- /* Default bandwidth */ +- if (tda10048_set_bandwidth(&state->frontend, 8000000) != 0) +- goto error; +- +- /* Leave the gate closed */ +- tda10048_i2c_gate_ctrl(&state->frontend, 0); +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(tda10048_attach); +- +-static struct dvb_frontend_ops tda10048_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "NXP TDA10048HN DVB-T", +- .frequency_min = 177000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166666, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER +- }, +- +- .release = tda10048_release, +- .init = tda10048_init, +- .i2c_gate_ctrl = tda10048_i2c_gate_ctrl, +- .set_frontend = tda10048_set_frontend, +- .get_frontend = tda10048_get_frontend, +- .get_tune_settings = tda10048_get_tune_settings, +- .read_status = tda10048_read_status, +- .read_ber = tda10048_read_ber, +- .read_signal_strength = tda10048_read_signal_strength, +- .read_snr = tda10048_read_snr, +- .read_ucblocks = tda10048_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Enable verbose debug messages"); +- +-MODULE_DESCRIPTION("NXP TDA10048HN DVB-T Demodulator driver"); +-MODULE_AUTHOR("Steven Toth"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda10048.h b/drivers/media/dvb/frontends/tda10048.h +deleted file mode 100644 +index fb2ef5a..0000000 +--- a/drivers/media/dvb/frontends/tda10048.h ++++ /dev/null +@@ -1,90 +0,0 @@ +-/* +- NXP TDA10048HN DVB OFDM demodulator driver +- +- Copyright (C) 2009 Steven Toth +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef TDA10048_H +-#define TDA10048_H +- +-#include +-#include +- +-struct tda10048_config { +- +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* serial/parallel output */ +-#define TDA10048_PARALLEL_OUTPUT 0 +-#define TDA10048_SERIAL_OUTPUT 1 +- u8 output_mode; +- +-#define TDA10048_BULKWRITE_200 200 +-#define TDA10048_BULKWRITE_50 50 +- u8 fwbulkwritelen; +- +- /* Spectral Inversion */ +-#define TDA10048_INVERSION_OFF 0 +-#define TDA10048_INVERSION_ON 1 +- u8 inversion; +- +-#define TDA10048_IF_3300 3300 +-#define TDA10048_IF_3500 3500 +-#define TDA10048_IF_3800 3800 +-#define TDA10048_IF_4000 4000 +-#define TDA10048_IF_4300 4300 +-#define TDA10048_IF_4500 4500 +-#define TDA10048_IF_4750 4750 +-#define TDA10048_IF_5000 5000 +-#define TDA10048_IF_36130 36130 +- u16 dtv6_if_freq_khz; +- u16 dtv7_if_freq_khz; +- u16 dtv8_if_freq_khz; +- +-#define TDA10048_CLK_4000 4000 +-#define TDA10048_CLK_16000 16000 +- u16 clk_freq_khz; +- +- /* Disable I2C gate access */ +- u8 disable_gate_access; +- +- bool no_firmware; +- +- bool set_pll; +- u8 pll_m; +- u8 pll_p; +- u8 pll_n; +-}; +- +-#if defined(CONFIG_DVB_TDA10048) || \ +- (defined(CONFIG_DVB_TDA10048_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda10048_attach( +- const struct tda10048_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *tda10048_attach( +- const struct tda10048_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_TDA10048 */ +- +-#endif /* TDA10048_H */ +diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c +deleted file mode 100644 +index ae6f22a..0000000 +--- a/drivers/media/dvb/frontends/tda1004x.c ++++ /dev/null +@@ -1,1381 +0,0 @@ +- /* +- Driver for Philips tda1004xh OFDM Demodulator +- +- (c) 2003, 2004 Andrew de Quincey & Robert Schlabbach +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +-/* +- * This driver needs external firmware. Please use the commands +- * "/Documentation/dvb/get_dvb_firmware tda10045", +- * "/Documentation/dvb/get_dvb_firmware tda10046" to +- * download/extract them, and then copy them to /usr/lib/hotplug/firmware +- * or /lib/firmware (depending on configuration of firmware hotplug). +- */ +-#define TDA10045_DEFAULT_FIRMWARE "dvb-fe-tda10045.fw" +-#define TDA10046_DEFAULT_FIRMWARE "dvb-fe-tda10046.fw" +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "tda1004x.h" +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "tda1004x: " args); \ +- } while (0) +- +-#define TDA1004X_CHIPID 0x00 +-#define TDA1004X_AUTO 0x01 +-#define TDA1004X_IN_CONF1 0x02 +-#define TDA1004X_IN_CONF2 0x03 +-#define TDA1004X_OUT_CONF1 0x04 +-#define TDA1004X_OUT_CONF2 0x05 +-#define TDA1004X_STATUS_CD 0x06 +-#define TDA1004X_CONFC4 0x07 +-#define TDA1004X_DSSPARE2 0x0C +-#define TDA10045H_CODE_IN 0x0D +-#define TDA10045H_FWPAGE 0x0E +-#define TDA1004X_SCAN_CPT 0x10 +-#define TDA1004X_DSP_CMD 0x11 +-#define TDA1004X_DSP_ARG 0x12 +-#define TDA1004X_DSP_DATA1 0x13 +-#define TDA1004X_DSP_DATA2 0x14 +-#define TDA1004X_CONFADC1 0x15 +-#define TDA1004X_CONFC1 0x16 +-#define TDA10045H_S_AGC 0x1a +-#define TDA10046H_AGC_TUN_LEVEL 0x1a +-#define TDA1004X_SNR 0x1c +-#define TDA1004X_CONF_TS1 0x1e +-#define TDA1004X_CONF_TS2 0x1f +-#define TDA1004X_CBER_RESET 0x20 +-#define TDA1004X_CBER_MSB 0x21 +-#define TDA1004X_CBER_LSB 0x22 +-#define TDA1004X_CVBER_LUT 0x23 +-#define TDA1004X_VBER_MSB 0x24 +-#define TDA1004X_VBER_MID 0x25 +-#define TDA1004X_VBER_LSB 0x26 +-#define TDA1004X_UNCOR 0x27 +- +-#define TDA10045H_CONFPLL_P 0x2D +-#define TDA10045H_CONFPLL_M_MSB 0x2E +-#define TDA10045H_CONFPLL_M_LSB 0x2F +-#define TDA10045H_CONFPLL_N 0x30 +- +-#define TDA10046H_CONFPLL1 0x2D +-#define TDA10046H_CONFPLL2 0x2F +-#define TDA10046H_CONFPLL3 0x30 +-#define TDA10046H_TIME_WREF1 0x31 +-#define TDA10046H_TIME_WREF2 0x32 +-#define TDA10046H_TIME_WREF3 0x33 +-#define TDA10046H_TIME_WREF4 0x34 +-#define TDA10046H_TIME_WREF5 0x35 +- +-#define TDA10045H_UNSURW_MSB 0x31 +-#define TDA10045H_UNSURW_LSB 0x32 +-#define TDA10045H_WREF_MSB 0x33 +-#define TDA10045H_WREF_MID 0x34 +-#define TDA10045H_WREF_LSB 0x35 +-#define TDA10045H_MUXOUT 0x36 +-#define TDA1004X_CONFADC2 0x37 +- +-#define TDA10045H_IOFFSET 0x38 +- +-#define TDA10046H_CONF_TRISTATE1 0x3B +-#define TDA10046H_CONF_TRISTATE2 0x3C +-#define TDA10046H_CONF_POLARITY 0x3D +-#define TDA10046H_FREQ_OFFSET 0x3E +-#define TDA10046H_GPIO_OUT_SEL 0x41 +-#define TDA10046H_GPIO_SELECT 0x42 +-#define TDA10046H_AGC_CONF 0x43 +-#define TDA10046H_AGC_THR 0x44 +-#define TDA10046H_AGC_RENORM 0x45 +-#define TDA10046H_AGC_GAINS 0x46 +-#define TDA10046H_AGC_TUN_MIN 0x47 +-#define TDA10046H_AGC_TUN_MAX 0x48 +-#define TDA10046H_AGC_IF_MIN 0x49 +-#define TDA10046H_AGC_IF_MAX 0x4A +- +-#define TDA10046H_FREQ_PHY2_MSB 0x4D +-#define TDA10046H_FREQ_PHY2_LSB 0x4E +- +-#define TDA10046H_CVBER_CTRL 0x4F +-#define TDA10046H_AGC_IF_LEVEL 0x52 +-#define TDA10046H_CODE_CPT 0x57 +-#define TDA10046H_CODE_IN 0x58 +- +- +-static int tda1004x_write_byteI(struct tda1004x_state *state, int reg, int data) +-{ +- int ret; +- u8 buf[] = { reg, data }; +- struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; +- +- dprintk("%s: reg=0x%x, data=0x%x\n", __func__, reg, data); +- +- msg.addr = state->config->demod_address; +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", +- __func__, reg, data, ret); +- +- dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, +- reg, data, ret); +- return (ret != 1) ? -1 : 0; +-} +- +-static int tda1004x_read_byte(struct tda1004x_state *state, int reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, +- { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; +- +- dprintk("%s: reg=0x%x\n", __func__, reg); +- +- msg[0].addr = state->config->demod_address; +- msg[1].addr = state->config->demod_address; +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, +- ret); +- return -EINVAL; +- } +- +- dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__, +- reg, b1[0], ret); +- return b1[0]; +-} +- +-static int tda1004x_write_mask(struct tda1004x_state *state, int reg, int mask, int data) +-{ +- int val; +- dprintk("%s: reg=0x%x, mask=0x%x, data=0x%x\n", __func__, reg, +- mask, data); +- +- // read a byte and check +- val = tda1004x_read_byte(state, reg); +- if (val < 0) +- return val; +- +- // mask if off +- val = val & ~mask; +- val |= data & 0xff; +- +- // write it out again +- return tda1004x_write_byteI(state, reg, val); +-} +- +-static int tda1004x_write_buf(struct tda1004x_state *state, int reg, unsigned char *buf, int len) +-{ +- int i; +- int result; +- +- dprintk("%s: reg=0x%x, len=0x%x\n", __func__, reg, len); +- +- result = 0; +- for (i = 0; i < len; i++) { +- result = tda1004x_write_byteI(state, reg + i, buf[i]); +- if (result != 0) +- break; +- } +- +- return result; +-} +- +-static int tda1004x_enable_tuner_i2c(struct tda1004x_state *state) +-{ +- int result; +- dprintk("%s\n", __func__); +- +- result = tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 2); +- msleep(20); +- return result; +-} +- +-static int tda1004x_disable_tuner_i2c(struct tda1004x_state *state) +-{ +- dprintk("%s\n", __func__); +- +- return tda1004x_write_mask(state, TDA1004X_CONFC4, 2, 0); +-} +- +-static int tda10045h_set_bandwidth(struct tda1004x_state *state, +- u32 bandwidth) +-{ +- static u8 bandwidth_6mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x60, 0x1e, 0xa7, 0x45, 0x4f }; +- static u8 bandwidth_7mhz[] = { 0x02, 0x00, 0x37, 0x00, 0x4a, 0x2f, 0x6d, 0x76, 0xdb }; +- static u8 bandwidth_8mhz[] = { 0x02, 0x00, 0x3d, 0x00, 0x48, 0x17, 0x89, 0xc7, 0x14 }; +- +- switch (bandwidth) { +- case 6000000: +- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_6mhz, sizeof(bandwidth_6mhz)); +- break; +- +- case 7000000: +- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_7mhz, sizeof(bandwidth_7mhz)); +- break; +- +- case 8000000: +- tda1004x_write_buf(state, TDA10045H_CONFPLL_P, bandwidth_8mhz, sizeof(bandwidth_8mhz)); +- break; +- +- default: +- return -EINVAL; +- } +- +- tda1004x_write_byteI(state, TDA10045H_IOFFSET, 0); +- +- return 0; +-} +- +-static int tda10046h_set_bandwidth(struct tda1004x_state *state, +- u32 bandwidth) +-{ +- static u8 bandwidth_6mhz_53M[] = { 0x7b, 0x2e, 0x11, 0xf0, 0xd2 }; +- static u8 bandwidth_7mhz_53M[] = { 0x6a, 0x02, 0x6a, 0x43, 0x9f }; +- static u8 bandwidth_8mhz_53M[] = { 0x5c, 0x32, 0xc2, 0x96, 0x6d }; +- +- static u8 bandwidth_6mhz_48M[] = { 0x70, 0x02, 0x49, 0x24, 0x92 }; +- static u8 bandwidth_7mhz_48M[] = { 0x60, 0x02, 0xaa, 0xaa, 0xab }; +- static u8 bandwidth_8mhz_48M[] = { 0x54, 0x03, 0x0c, 0x30, 0xc3 }; +- int tda10046_clk53m; +- +- if ((state->config->if_freq == TDA10046_FREQ_045) || +- (state->config->if_freq == TDA10046_FREQ_052)) +- tda10046_clk53m = 0; +- else +- tda10046_clk53m = 1; +- switch (bandwidth) { +- case 6000000: +- if (tda10046_clk53m) +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_53M, +- sizeof(bandwidth_6mhz_53M)); +- else +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_6mhz_48M, +- sizeof(bandwidth_6mhz_48M)); +- if (state->config->if_freq == TDA10046_FREQ_045) { +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0a); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xab); +- } +- break; +- +- case 7000000: +- if (tda10046_clk53m) +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_53M, +- sizeof(bandwidth_7mhz_53M)); +- else +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_7mhz_48M, +- sizeof(bandwidth_7mhz_48M)); +- if (state->config->if_freq == TDA10046_FREQ_045) { +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); +- } +- break; +- +- case 8000000: +- if (tda10046_clk53m) +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_53M, +- sizeof(bandwidth_8mhz_53M)); +- else +- tda1004x_write_buf(state, TDA10046H_TIME_WREF1, bandwidth_8mhz_48M, +- sizeof(bandwidth_8mhz_48M)); +- if (state->config->if_freq == TDA10046_FREQ_045) { +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x55); +- } +- break; +- +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int tda1004x_do_upload(struct tda1004x_state *state, +- const unsigned char *mem, unsigned int len, +- u8 dspCodeCounterReg, u8 dspCodeInReg) +-{ +- u8 buf[65]; +- struct i2c_msg fw_msg = { .flags = 0, .buf = buf, .len = 0 }; +- int tx_size; +- int pos = 0; +- +- /* clear code counter */ +- tda1004x_write_byteI(state, dspCodeCounterReg, 0); +- fw_msg.addr = state->config->demod_address; +- +- buf[0] = dspCodeInReg; +- while (pos != len) { +- // work out how much to send this time +- tx_size = len - pos; +- if (tx_size > 0x10) +- tx_size = 0x10; +- +- // send the chunk +- memcpy(buf + 1, mem + pos, tx_size); +- fw_msg.len = tx_size + 1; +- if (i2c_transfer(state->i2c, &fw_msg, 1) != 1) { +- printk(KERN_ERR "tda1004x: Error during firmware upload\n"); +- return -EIO; +- } +- pos += tx_size; +- +- dprintk("%s: fw_pos=0x%x\n", __func__, pos); +- } +- // give the DSP a chance to settle 03/10/05 Hac +- msleep(100); +- +- return 0; +-} +- +-static int tda1004x_check_upload_ok(struct tda1004x_state *state) +-{ +- u8 data1, data2; +- unsigned long timeout; +- +- if (state->demod_type == TDA1004X_DEMOD_TDA10046) { +- timeout = jiffies + 2 * HZ; +- while(!(tda1004x_read_byte(state, TDA1004X_STATUS_CD) & 0x20)) { +- if (time_after(jiffies, timeout)) { +- printk(KERN_ERR "tda1004x: timeout waiting for DSP ready\n"); +- break; +- } +- msleep(1); +- } +- } else +- msleep(100); +- +- // check upload was OK +- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); // we want to read from the DSP +- tda1004x_write_byteI(state, TDA1004X_DSP_CMD, 0x67); +- +- data1 = tda1004x_read_byte(state, TDA1004X_DSP_DATA1); +- data2 = tda1004x_read_byte(state, TDA1004X_DSP_DATA2); +- if (data1 != 0x67 || data2 < 0x20 || data2 > 0x2e) { +- printk(KERN_INFO "tda1004x: found firmware revision %x -- invalid\n", data2); +- return -EIO; +- } +- printk(KERN_INFO "tda1004x: found firmware revision %x -- ok\n", data2); +- return 0; +-} +- +-static int tda10045_fwupload(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int ret; +- const struct firmware *fw; +- +- /* don't re-upload unless necessary */ +- if (tda1004x_check_upload_ok(state) == 0) +- return 0; +- +- /* request the firmware, this will block until someone uploads it */ +- printk(KERN_INFO "tda1004x: waiting for firmware upload (%s)...\n", TDA10045_DEFAULT_FIRMWARE); +- ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); +- if (ret) { +- printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); +- return ret; +- } +- +- /* reset chip */ +- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x10, 0); +- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); +- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); +- msleep(10); +- +- /* set parameters */ +- tda10045h_set_bandwidth(state, 8000000); +- +- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10045H_FWPAGE, TDA10045H_CODE_IN); +- release_firmware(fw); +- if (ret) +- return ret; +- printk(KERN_INFO "tda1004x: firmware upload complete\n"); +- +- /* wait for DSP to initialise */ +- /* DSPREADY doesn't seem to work on the TDA10045H */ +- msleep(100); +- +- return tda1004x_check_upload_ok(state); +-} +- +-static void tda10046_init_plls(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int tda10046_clk53m; +- +- if ((state->config->if_freq == TDA10046_FREQ_045) || +- (state->config->if_freq == TDA10046_FREQ_052)) +- tda10046_clk53m = 0; +- else +- tda10046_clk53m = 1; +- +- tda1004x_write_byteI(state, TDA10046H_CONFPLL1, 0xf0); +- if(tda10046_clk53m) { +- printk(KERN_INFO "tda1004x: setting up plls for 53MHz sampling clock\n"); +- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x08); // PLL M = 8 +- } else { +- printk(KERN_INFO "tda1004x: setting up plls for 48MHz sampling clock\n"); +- tda1004x_write_byteI(state, TDA10046H_CONFPLL2, 0x03); // PLL M = 3 +- } +- if (state->config->xtal_freq == TDA10046_XTAL_4M ) { +- dprintk("%s: setting up PLLs for a 4 MHz Xtal\n", __func__); +- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 0); // PLL P = N = 0 +- } else { +- dprintk("%s: setting up PLLs for a 16 MHz Xtal\n", __func__); +- tda1004x_write_byteI(state, TDA10046H_CONFPLL3, 3); // PLL P = 0, N = 3 +- } +- if(tda10046_clk53m) +- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x67); +- else +- tda1004x_write_byteI(state, TDA10046H_FREQ_OFFSET, 0x72); +- /* Note clock frequency is handled implicitly */ +- switch (state->config->if_freq) { +- case TDA10046_FREQ_045: +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0c); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x00); +- break; +- case TDA10046_FREQ_052: +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0x0d); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0xc7); +- break; +- case TDA10046_FREQ_3617: +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x59); +- break; +- case TDA10046_FREQ_3613: +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_MSB, 0xd7); +- tda1004x_write_byteI(state, TDA10046H_FREQ_PHY2_LSB, 0x3f); +- break; +- } +- tda10046h_set_bandwidth(state, 8000000); /* default bandwidth 8 MHz */ +- /* let the PLLs settle */ +- msleep(120); +-} +- +-static int tda10046_fwupload(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int ret, confc4; +- const struct firmware *fw; +- +- /* reset + wake up chip */ +- if (state->config->xtal_freq == TDA10046_XTAL_4M) { +- confc4 = 0; +- } else { +- dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__); +- confc4 = 0x80; +- } +- tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); +- +- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); +- /* set GPIO 1 and 3 */ +- if (state->config->gpio_config != TDA10046_GPTRI) { +- tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33); +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f); +- } +- /* let the clocks recover from sleep */ +- msleep(10); +- +- /* The PLLs need to be reprogrammed after sleep */ +- tda10046_init_plls(fe); +- tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0); +- +- /* don't re-upload unless necessary */ +- if (tda1004x_check_upload_ok(state) == 0) +- return 0; +- +- /* +- For i2c normal work, we need to slow down the bus speed. +- However, the slow down breaks the eeprom firmware load. +- So, use normal speed for eeprom booting and then restore the +- i2c speed after that. Tested with MSI TV @nyware A/D board, +- that comes with firmware version 29 inside their eeprom. +- +- It should also be noticed that no other I2C transfer should +- be in course while booting from eeprom, otherwise, tda10046 +- goes into an instable state. So, proper locking are needed +- at the i2c bus master. +- */ +- printk(KERN_INFO "tda1004x: trying to boot from eeprom\n"); +- tda1004x_write_byteI(state, TDA1004X_CONFC4, 4); +- msleep(300); +- tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4); +- +- /* Checks if eeprom firmware went without troubles */ +- if (tda1004x_check_upload_ok(state) == 0) +- return 0; +- +- /* eeprom firmware didn't work. Load one manually. */ +- +- if (state->config->request_firmware != NULL) { +- /* request the firmware, this will block until someone uploads it */ +- printk(KERN_INFO "tda1004x: waiting for firmware upload...\n"); +- ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); +- if (ret) { +- /* remain compatible to old bug: try to load with tda10045 image name */ +- ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); +- if (ret) { +- printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); +- return ret; +- } else { +- printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n", +- TDA10046_DEFAULT_FIRMWARE); +- } +- } +- } else { +- printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n"); +- return -EIO; +- } +- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST +- ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); +- release_firmware(fw); +- return tda1004x_check_upload_ok(state); +-} +- +-static int tda1004x_encode_fec(int fec) +-{ +- // convert known FEC values +- switch (fec) { +- case FEC_1_2: +- return 0; +- case FEC_2_3: +- return 1; +- case FEC_3_4: +- return 2; +- case FEC_5_6: +- return 3; +- case FEC_7_8: +- return 4; +- } +- +- // unsupported +- return -EINVAL; +-} +- +-static int tda1004x_decode_fec(int tdafec) +-{ +- // convert known FEC values +- switch (tdafec) { +- case 0: +- return FEC_1_2; +- case 1: +- return FEC_2_3; +- case 2: +- return FEC_3_4; +- case 3: +- return FEC_5_6; +- case 4: +- return FEC_7_8; +- } +- +- // unsupported +- return -1; +-} +- +-static int tda1004x_write(struct dvb_frontend* fe, const u8 buf[], int len) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- +- if (len != 2) +- return -EINVAL; +- +- return tda1004x_write_byteI(state, buf[0], buf[1]); +-} +- +-static int tda10045_init(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- if (tda10045_fwupload(fe)) { +- printk("tda1004x: firmware upload failed\n"); +- return -EIO; +- } +- +- tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0); // wake up the ADC +- +- // tda setup +- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer +- tda1004x_write_mask(state, TDA1004X_AUTO, 8, 0); // select HP stream +- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x40, 0); // set polarity of VAGC signal +- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x80, 0x80); // enable pulse killer +- tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); // enable auto offset +- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0x0); // no frequency offset +- tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 0); // setup MPEG2 TS interface +- tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0); // setup MPEG2 TS interface +- tda1004x_write_mask(state, TDA1004X_VBER_MSB, 0xe0, 0xa0); // 10^6 VBER measurement bits +- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x10, 0); // VAGC polarity +- tda1004x_write_byteI(state, TDA1004X_CONFADC1, 0x2e); +- +- tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); +- +- return 0; +-} +- +-static int tda10046_init(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- dprintk("%s\n", __func__); +- +- if (tda10046_fwupload(fe)) { +- printk("tda1004x: firmware upload failed\n"); +- return -EIO; +- } +- +- // tda setup +- tda1004x_write_mask(state, TDA1004X_CONFC4, 0x20, 0); // disable DSP watchdog timer +- tda1004x_write_byteI(state, TDA1004X_AUTO, 0x87); // 100 ppm crystal, select HP stream +- tda1004x_write_byteI(state, TDA1004X_CONFC1, 0x88); // enable pulse killer +- +- switch (state->config->agc_config) { +- case TDA10046_AGC_DEFAULT: +- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities +- break; +- case TDA10046_AGC_IFO_AUTO_NEG: +- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities +- break; +- case TDA10046_AGC_IFO_AUTO_POS: +- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities +- break; +- case TDA10046_AGC_TDA827X: +- tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup +- tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold +- tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities +- break; +- } +- if (state->config->ts_mode == 0) { +- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40); +- tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); +- } else { +- tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80); +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10, +- state->config->invert_oclk << 4); +- } +- tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); +- tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on +- tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } +- tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values +- tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } +- tda1004x_write_byteI(state, TDA10046H_AGC_IF_MAX, 0xff); // } +- tda1004x_write_byteI(state, TDA10046H_AGC_GAINS, 0x12); // IF gain 2, TUN gain 1 +- tda1004x_write_byteI(state, TDA10046H_CVBER_CTRL, 0x1a); // 10^6 VBER measurement bits +- tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config +- tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config +- // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes +- +- return 0; +-} +- +-static int tda1004x_set_fe(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; +- struct tda1004x_state* state = fe->demodulator_priv; +- int tmp; +- int inversion; +- +- dprintk("%s\n", __func__); +- +- if (state->demod_type == TDA1004X_DEMOD_TDA10046) { +- // setup auto offset +- tda1004x_write_mask(state, TDA1004X_AUTO, 0x10, 0x10); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x80, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0xC0, 0); +- +- // disable agc_conf[2] +- tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 0); +- } +- +- // set frequency +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- // Hardcoded to use auto as much as possible on the TDA10045 as it +- // is very unreliable if AUTO mode is _not_ used. +- if (state->demod_type == TDA1004X_DEMOD_TDA10045) { +- fe_params->code_rate_HP = FEC_AUTO; +- fe_params->guard_interval = GUARD_INTERVAL_AUTO; +- fe_params->transmission_mode = TRANSMISSION_MODE_AUTO; +- } +- +- // Set standard params.. or put them to auto +- if ((fe_params->code_rate_HP == FEC_AUTO) || +- (fe_params->code_rate_LP == FEC_AUTO) || +- (fe_params->modulation == QAM_AUTO) || +- (fe_params->hierarchy == HIERARCHY_AUTO)) { +- tda1004x_write_mask(state, TDA1004X_AUTO, 1, 1); // enable auto +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x03, 0); /* turn off modulation bits */ +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0); // turn off hierarchy bits +- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x3f, 0); // turn off FEC bits +- } else { +- tda1004x_write_mask(state, TDA1004X_AUTO, 1, 0); // disable auto +- +- // set HP FEC +- tmp = tda1004x_encode_fec(fe_params->code_rate_HP); +- if (tmp < 0) +- return tmp; +- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 7, tmp); +- +- // set LP FEC +- tmp = tda1004x_encode_fec(fe_params->code_rate_LP); +- if (tmp < 0) +- return tmp; +- tda1004x_write_mask(state, TDA1004X_IN_CONF2, 0x38, tmp << 3); +- +- /* set modulation */ +- switch (fe_params->modulation) { +- case QPSK: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 0); +- break; +- +- case QAM_16: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 1); +- break; +- +- case QAM_64: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 3, 2); +- break; +- +- default: +- return -EINVAL; +- } +- +- // set hierarchy +- switch (fe_params->hierarchy) { +- case HIERARCHY_NONE: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 0 << 5); +- break; +- +- case HIERARCHY_1: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 1 << 5); +- break; +- +- case HIERARCHY_2: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 2 << 5); +- break; +- +- case HIERARCHY_4: +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x60, 3 << 5); +- break; +- +- default: +- return -EINVAL; +- } +- } +- +- // set bandwidth +- switch (state->demod_type) { +- case TDA1004X_DEMOD_TDA10045: +- tda10045h_set_bandwidth(state, fe_params->bandwidth_hz); +- break; +- +- case TDA1004X_DEMOD_TDA10046: +- tda10046h_set_bandwidth(state, fe_params->bandwidth_hz); +- break; +- } +- +- // set inversion +- inversion = fe_params->inversion; +- if (state->config->invert) +- inversion = inversion ? INVERSION_OFF : INVERSION_ON; +- switch (inversion) { +- case INVERSION_OFF: +- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0); +- break; +- +- case INVERSION_ON: +- tda1004x_write_mask(state, TDA1004X_CONFC1, 0x20, 0x20); +- break; +- +- default: +- return -EINVAL; +- } +- +- // set guard interval +- switch (fe_params->guard_interval) { +- case GUARD_INTERVAL_1_32: +- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); +- break; +- +- case GUARD_INTERVAL_1_16: +- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 1 << 2); +- break; +- +- case GUARD_INTERVAL_1_8: +- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 2 << 2); +- break; +- +- case GUARD_INTERVAL_1_4: +- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 3 << 2); +- break; +- +- case GUARD_INTERVAL_AUTO: +- tda1004x_write_mask(state, TDA1004X_AUTO, 2, 2); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x0c, 0 << 2); +- break; +- +- default: +- return -EINVAL; +- } +- +- // set transmission mode +- switch (fe_params->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0 << 4); +- break; +- +- case TRANSMISSION_MODE_8K: +- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 0); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 1 << 4); +- break; +- +- case TRANSMISSION_MODE_AUTO: +- tda1004x_write_mask(state, TDA1004X_AUTO, 4, 4); +- tda1004x_write_mask(state, TDA1004X_IN_CONF1, 0x10, 0); +- break; +- +- default: +- return -EINVAL; +- } +- +- // start the lock +- switch (state->demod_type) { +- case TDA1004X_DEMOD_TDA10045: +- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); +- tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 0); +- break; +- +- case TDA1004X_DEMOD_TDA10046: +- tda1004x_write_mask(state, TDA1004X_AUTO, 0x40, 0x40); +- msleep(1); +- tda1004x_write_mask(state, TDA10046H_AGC_CONF, 4, 1); +- break; +- } +- +- msleep(10); +- +- return 0; +-} +- +-static int tda1004x_get_fe(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; +- struct tda1004x_state* state = fe->demodulator_priv; +- +- dprintk("%s\n", __func__); +- +- // inversion status +- fe_params->inversion = INVERSION_OFF; +- if (tda1004x_read_byte(state, TDA1004X_CONFC1) & 0x20) +- fe_params->inversion = INVERSION_ON; +- if (state->config->invert) +- fe_params->inversion = fe_params->inversion ? INVERSION_OFF : INVERSION_ON; +- +- // bandwidth +- switch (state->demod_type) { +- case TDA1004X_DEMOD_TDA10045: +- switch (tda1004x_read_byte(state, TDA10045H_WREF_LSB)) { +- case 0x14: +- fe_params->bandwidth_hz = 8000000; +- break; +- case 0xdb: +- fe_params->bandwidth_hz = 7000000; +- break; +- case 0x4f: +- fe_params->bandwidth_hz = 6000000; +- break; +- } +- break; +- case TDA1004X_DEMOD_TDA10046: +- switch (tda1004x_read_byte(state, TDA10046H_TIME_WREF1)) { +- case 0x5c: +- case 0x54: +- fe_params->bandwidth_hz = 8000000; +- break; +- case 0x6a: +- case 0x60: +- fe_params->bandwidth_hz = 7000000; +- break; +- case 0x7b: +- case 0x70: +- fe_params->bandwidth_hz = 6000000; +- break; +- } +- break; +- } +- +- // FEC +- fe_params->code_rate_HP = +- tda1004x_decode_fec(tda1004x_read_byte(state, TDA1004X_OUT_CONF2) & 7); +- fe_params->code_rate_LP = +- tda1004x_decode_fec((tda1004x_read_byte(state, TDA1004X_OUT_CONF2) >> 3) & 7); +- +- /* modulation */ +- switch (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 3) { +- case 0: +- fe_params->modulation = QPSK; +- break; +- case 1: +- fe_params->modulation = QAM_16; +- break; +- case 2: +- fe_params->modulation = QAM_64; +- break; +- } +- +- // transmission mode +- fe_params->transmission_mode = TRANSMISSION_MODE_2K; +- if (tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x10) +- fe_params->transmission_mode = TRANSMISSION_MODE_8K; +- +- // guard interval +- switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x0c) >> 2) { +- case 0: +- fe_params->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- fe_params->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- fe_params->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- fe_params->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- // hierarchy +- switch ((tda1004x_read_byte(state, TDA1004X_OUT_CONF1) & 0x60) >> 5) { +- case 0: +- fe_params->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- fe_params->hierarchy = HIERARCHY_1; +- break; +- case 2: +- fe_params->hierarchy = HIERARCHY_2; +- break; +- case 3: +- fe_params->hierarchy = HIERARCHY_4; +- break; +- } +- +- return 0; +-} +- +-static int tda1004x_read_status(struct dvb_frontend* fe, fe_status_t * fe_status) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int status; +- int cber; +- int vber; +- +- dprintk("%s\n", __func__); +- +- // read status +- status = tda1004x_read_byte(state, TDA1004X_STATUS_CD); +- if (status == -1) +- return -EIO; +- +- // decode +- *fe_status = 0; +- if (status & 4) +- *fe_status |= FE_HAS_SIGNAL; +- if (status & 2) +- *fe_status |= FE_HAS_CARRIER; +- if (status & 8) +- *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- +- // if we don't already have VITERBI (i.e. not LOCKED), see if the viterbi +- // is getting anything valid +- if (!(*fe_status & FE_HAS_VITERBI)) { +- // read the CBER +- cber = tda1004x_read_byte(state, TDA1004X_CBER_LSB); +- if (cber == -1) +- return -EIO; +- status = tda1004x_read_byte(state, TDA1004X_CBER_MSB); +- if (status == -1) +- return -EIO; +- cber |= (status << 8); +- // The address 0x20 should be read to cope with a TDA10046 bug +- tda1004x_read_byte(state, TDA1004X_CBER_RESET); +- +- if (cber != 65535) +- *fe_status |= FE_HAS_VITERBI; +- } +- +- // if we DO have some valid VITERBI output, but don't already have SYNC +- // bytes (i.e. not LOCKED), see if the RS decoder is getting anything valid. +- if ((*fe_status & FE_HAS_VITERBI) && (!(*fe_status & FE_HAS_SYNC))) { +- // read the VBER +- vber = tda1004x_read_byte(state, TDA1004X_VBER_LSB); +- if (vber == -1) +- return -EIO; +- status = tda1004x_read_byte(state, TDA1004X_VBER_MID); +- if (status == -1) +- return -EIO; +- vber |= (status << 8); +- status = tda1004x_read_byte(state, TDA1004X_VBER_MSB); +- if (status == -1) +- return -EIO; +- vber |= (status & 0x0f) << 16; +- // The CVBER_LUT should be read to cope with TDA10046 hardware bug +- tda1004x_read_byte(state, TDA1004X_CVBER_LUT); +- +- // if RS has passed some valid TS packets, then we must be +- // getting some SYNC bytes +- if (vber < 16632) +- *fe_status |= FE_HAS_SYNC; +- } +- +- // success +- dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); +- return 0; +-} +- +-static int tda1004x_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int tmp; +- int reg = 0; +- +- dprintk("%s\n", __func__); +- +- // determine the register to use +- switch (state->demod_type) { +- case TDA1004X_DEMOD_TDA10045: +- reg = TDA10045H_S_AGC; +- break; +- +- case TDA1004X_DEMOD_TDA10046: +- reg = TDA10046H_AGC_IF_LEVEL; +- break; +- } +- +- // read it +- tmp = tda1004x_read_byte(state, reg); +- if (tmp < 0) +- return -EIO; +- +- *signal = (tmp << 8) | tmp; +- dprintk("%s: signal=0x%x\n", __func__, *signal); +- return 0; +-} +- +-static int tda1004x_read_snr(struct dvb_frontend* fe, u16 * snr) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int tmp; +- +- dprintk("%s\n", __func__); +- +- // read it +- tmp = tda1004x_read_byte(state, TDA1004X_SNR); +- if (tmp < 0) +- return -EIO; +- tmp = 255 - tmp; +- +- *snr = ((tmp << 8) | tmp); +- dprintk("%s: snr=0x%x\n", __func__, *snr); +- return 0; +-} +- +-static int tda1004x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int tmp; +- int tmp2; +- int counter; +- +- dprintk("%s\n", __func__); +- +- // read the UCBLOCKS and reset +- counter = 0; +- tmp = tda1004x_read_byte(state, TDA1004X_UNCOR); +- if (tmp < 0) +- return -EIO; +- tmp &= 0x7f; +- while (counter++ < 5) { +- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); +- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); +- tda1004x_write_mask(state, TDA1004X_UNCOR, 0x80, 0); +- +- tmp2 = tda1004x_read_byte(state, TDA1004X_UNCOR); +- if (tmp2 < 0) +- return -EIO; +- tmp2 &= 0x7f; +- if ((tmp2 < tmp) || (tmp2 == 0)) +- break; +- } +- +- if (tmp != 0x7f) +- *ucblocks = tmp; +- else +- *ucblocks = 0xffffffff; +- +- dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); +- return 0; +-} +- +-static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int tmp; +- +- dprintk("%s\n", __func__); +- +- // read it in +- tmp = tda1004x_read_byte(state, TDA1004X_CBER_LSB); +- if (tmp < 0) +- return -EIO; +- *ber = tmp << 1; +- tmp = tda1004x_read_byte(state, TDA1004X_CBER_MSB); +- if (tmp < 0) +- return -EIO; +- *ber |= (tmp << 9); +- // The address 0x20 should be read to cope with a TDA10046 bug +- tda1004x_read_byte(state, TDA1004X_CBER_RESET); +- +- dprintk("%s: ber=0x%x\n", __func__, *ber); +- return 0; +-} +- +-static int tda1004x_sleep(struct dvb_frontend* fe) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- int gpio_conf; +- +- switch (state->demod_type) { +- case TDA1004X_DEMOD_TDA10045: +- tda1004x_write_mask(state, TDA1004X_CONFADC1, 0x10, 0x10); +- break; +- +- case TDA1004X_DEMOD_TDA10046: +- /* set outputs to tristate */ +- tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); +- /* invert GPIO 1 and 3 if desired*/ +- gpio_conf = state->config->gpio_config; +- if (gpio_conf >= TDA10046_GP00_I) +- tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, +- (gpio_conf & 0x0f) ^ 0x0a); +- +- tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0); +- tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); +- break; +- } +- +- return 0; +-} +- +-static int tda1004x_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct tda1004x_state* state = fe->demodulator_priv; +- +- if (enable) { +- return tda1004x_enable_tuner_i2c(state); +- } else { +- return tda1004x_disable_tuner_i2c(state); +- } +-} +- +-static int tda1004x_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 800; +- /* Drift compensation makes no sense for DVB-T */ +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void tda1004x_release(struct dvb_frontend* fe) +-{ +- struct tda1004x_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops tda10045_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Philips TDA10045H DVB-T", +- .frequency_min = 51000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166667, +- .caps = +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO +- }, +- +- .release = tda1004x_release, +- +- .init = tda10045_init, +- .sleep = tda1004x_sleep, +- .write = tda1004x_write, +- .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, +- +- .set_frontend = tda1004x_set_fe, +- .get_frontend = tda1004x_get_fe, +- .get_tune_settings = tda1004x_get_tune_settings, +- +- .read_status = tda1004x_read_status, +- .read_ber = tda1004x_read_ber, +- .read_signal_strength = tda1004x_read_signal_strength, +- .read_snr = tda1004x_read_snr, +- .read_ucblocks = tda1004x_read_ucblocks, +-}; +- +-struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c) +-{ +- struct tda1004x_state *state; +- int id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); +- if (!state) { +- printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); +- return NULL; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->demod_type = TDA1004X_DEMOD_TDA10045; +- +- /* check if the demod is there */ +- id = tda1004x_read_byte(state, TDA1004X_CHIPID); +- if (id < 0) { +- printk(KERN_ERR "tda10045: chip is not answering. Giving up.\n"); +- kfree(state); +- return NULL; +- } +- +- if (id != 0x25) { +- printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); +- kfree(state); +- return NULL; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-static struct dvb_frontend_ops tda10046_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Philips TDA10046H DVB-T", +- .frequency_min = 51000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 166667, +- .caps = +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO +- }, +- +- .release = tda1004x_release, +- +- .init = tda10046_init, +- .sleep = tda1004x_sleep, +- .write = tda1004x_write, +- .i2c_gate_ctrl = tda1004x_i2c_gate_ctrl, +- +- .set_frontend = tda1004x_set_fe, +- .get_frontend = tda1004x_get_fe, +- .get_tune_settings = tda1004x_get_tune_settings, +- +- .read_status = tda1004x_read_status, +- .read_ber = tda1004x_read_ber, +- .read_signal_strength = tda1004x_read_signal_strength, +- .read_snr = tda1004x_read_snr, +- .read_ucblocks = tda1004x_read_ucblocks, +-}; +- +-struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c) +-{ +- struct tda1004x_state *state; +- int id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); +- if (!state) { +- printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); +- return NULL; +- } +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->demod_type = TDA1004X_DEMOD_TDA10046; +- +- /* check if the demod is there */ +- id = tda1004x_read_byte(state, TDA1004X_CHIPID); +- if (id < 0) { +- printk(KERN_ERR "tda10046: chip is not answering. Giving up.\n"); +- kfree(state); +- return NULL; +- } +- if (id != 0x46) { +- printk(KERN_ERR "Invalid tda1004x ID = 0x%02x. Can't proceed\n", id); +- kfree(state); +- return NULL; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); +-MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(tda10045_attach); +-EXPORT_SYMBOL(tda10046_attach); +diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h +deleted file mode 100644 +index 4e27ffb..0000000 +--- a/drivers/media/dvb/frontends/tda1004x.h ++++ /dev/null +@@ -1,149 +0,0 @@ +- /* +- Driver for Philips tda1004xh OFDM Frontend +- +- (c) 2004 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#ifndef TDA1004X_H +-#define TDA1004X_H +- +-#include +-#include +- +-enum tda10046_xtal { +- TDA10046_XTAL_4M, +- TDA10046_XTAL_16M, +-}; +- +-enum tda10046_agc { +- TDA10046_AGC_DEFAULT, /* original configuration */ +- TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ +- TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ +- TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ +-}; +- +-/* Many (hybrid) boards use GPIO 1 and 3 +- GPIO1 analog - dvb switch +- GPIO3 firmware eeprom address switch +-*/ +-enum tda10046_gpio { +- TDA10046_GPTRI = 0x00, /* All GPIOs tristate */ +- TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */ +- TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */ +- TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */ +- TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */ +- TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/ +- TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */ +- TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */ +- TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */ +-}; +- +-enum tda10046_if { +- TDA10046_FREQ_3617, /* original config, 36,166 MHZ */ +- TDA10046_FREQ_3613, /* 36,13 MHZ */ +- TDA10046_FREQ_045, /* low IF, 4.0, 4.5, or 5.0 MHZ */ +- TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */ +-}; +- +-enum tda10046_tsout { +- TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */ +- TDA10046_TS_SERIAL = 0x01, /* serial transport stream */ +-}; +- +-struct tda1004x_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* does the "inversion" need inverted? */ +- u8 invert; +- +- /* Does the OCLK signal need inverted? */ +- u8 invert_oclk; +- +- /* parallel or serial transport stream */ +- enum tda10046_tsout ts_mode; +- +- /* Xtal frequency, 4 or 16MHz*/ +- enum tda10046_xtal xtal_freq; +- +- /* IF frequency */ +- enum tda10046_if if_freq; +- +- /* AGC configuration */ +- enum tda10046_agc agc_config; +- +- /* setting of GPIO1 and 3 */ +- enum tda10046_gpio gpio_config; +- +- /* slave address and configuration of the tuner */ +- u8 tuner_address; +- u8 antenna_switch; +- +- /* if the board uses another I2c Bridge (tda8290), its address */ +- u8 i2c_gate; +- +- /* request firmware for device */ +- int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); +-}; +- +-enum tda1004x_demod { +- TDA1004X_DEMOD_TDA10045, +- TDA1004X_DEMOD_TDA10046, +-}; +- +-struct tda1004x_state { +- struct i2c_adapter* i2c; +- const struct tda1004x_config* config; +- struct dvb_frontend frontend; +- +- /* private demod data */ +- enum tda1004x_demod demod_type; +-}; +- +-#if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c); +- +-extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TDA1004X +- +-static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { +- int r = 0; +- u8 buf[] = {reg, val}; +- if (fe->ops.write) +- r = fe->ops.write(fe, buf, 2); +- return r; +-} +- +-#endif // TDA1004X_H +diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c +deleted file mode 100644 +index c21bc92..0000000 +--- a/drivers/media/dvb/frontends/tda10071.c ++++ /dev/null +@@ -1,1267 +0,0 @@ +-/* +- * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#include "tda10071_priv.h" +- +-int tda10071_debug; +-module_param_named(debug, tda10071_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-static struct dvb_frontend_ops tda10071_ops; +- +-/* write multiple registers */ +-static int tda10071_wr_regs(struct tda10071_priv *priv, u8 reg, u8 *val, +- int len) +-{ +- int ret; +- u8 buf[len+1]; +- struct i2c_msg msg[1] = { +- { +- .addr = priv->cfg.i2c_address, +- .flags = 0, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- buf[0] = reg; +- memcpy(&buf[1], val, len); +- +- ret = i2c_transfer(priv->i2c, msg, 1); +- if (ret == 1) { +- ret = 0; +- } else { +- warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* read multiple registers */ +-static int tda10071_rd_regs(struct tda10071_priv *priv, u8 reg, u8 *val, +- int len) +-{ +- int ret; +- u8 buf[len]; +- struct i2c_msg msg[2] = { +- { +- .addr = priv->cfg.i2c_address, +- .flags = 0, +- .len = 1, +- .buf = ®, +- }, { +- .addr = priv->cfg.i2c_address, +- .flags = I2C_M_RD, +- .len = sizeof(buf), +- .buf = buf, +- } +- }; +- +- ret = i2c_transfer(priv->i2c, msg, 2); +- if (ret == 2) { +- memcpy(val, buf, len); +- ret = 0; +- } else { +- warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len); +- ret = -EREMOTEIO; +- } +- return ret; +-} +- +-/* write single register */ +-static int tda10071_wr_reg(struct tda10071_priv *priv, u8 reg, u8 val) +-{ +- return tda10071_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register */ +-static int tda10071_rd_reg(struct tda10071_priv *priv, u8 reg, u8 *val) +-{ +- return tda10071_rd_regs(priv, reg, val, 1); +-} +- +-/* write single register with mask */ +-int tda10071_wr_reg_mask(struct tda10071_priv *priv, u8 reg, u8 val, u8 mask) +-{ +- int ret; +- u8 tmp; +- +- /* no need for read if whole reg is written */ +- if (mask != 0xff) { +- ret = tda10071_rd_regs(priv, reg, &tmp, 1); +- if (ret) +- return ret; +- +- val &= mask; +- tmp &= ~mask; +- val |= tmp; +- } +- +- return tda10071_wr_regs(priv, reg, &val, 1); +-} +- +-/* read single register with mask */ +-int tda10071_rd_reg_mask(struct tda10071_priv *priv, u8 reg, u8 *val, u8 mask) +-{ +- int ret, i; +- u8 tmp; +- +- ret = tda10071_rd_regs(priv, reg, &tmp, 1); +- if (ret) +- return ret; +- +- tmp &= mask; +- +- /* find position of the first bit */ +- for (i = 0; i < 8; i++) { +- if ((mask >> i) & 0x01) +- break; +- } +- *val = tmp >> i; +- +- return 0; +-} +- +-/* execute firmware command */ +-static int tda10071_cmd_execute(struct tda10071_priv *priv, +- struct tda10071_cmd *cmd) +-{ +- int ret, i; +- u8 tmp; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- /* write cmd and args for firmware */ +- ret = tda10071_wr_regs(priv, 0x00, cmd->args, cmd->len); +- if (ret) +- goto error; +- +- /* start cmd execution */ +- ret = tda10071_wr_reg(priv, 0x1f, 1); +- if (ret) +- goto error; +- +- /* wait cmd execution terminate */ +- for (i = 1000, tmp = 1; i && tmp; i--) { +- ret = tda10071_rd_reg(priv, 0x1f, &tmp); +- if (ret) +- goto error; +- +- usleep_range(200, 5000); +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) { +- ret = -ETIMEDOUT; +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_set_tone(struct dvb_frontend *fe, +- fe_sec_tone_mode_t fe_sec_tone_mode) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret; +- u8 tone; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- dbg("%s: tone_mode=%d", __func__, fe_sec_tone_mode); +- +- switch (fe_sec_tone_mode) { +- case SEC_TONE_ON: +- tone = 1; +- break; +- case SEC_TONE_OFF: +- tone = 0; +- break; +- default: +- dbg("%s: invalid fe_sec_tone_mode", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- cmd.args[0x00] = CMD_LNB_PCB_CONFIG; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 0x00; +- cmd.args[0x03] = 0x00; +- cmd.args[0x04] = tone; +- cmd.len = 0x05; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_set_voltage(struct dvb_frontend *fe, +- fe_sec_voltage_t fe_sec_voltage) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret; +- u8 voltage; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- dbg("%s: voltage=%d", __func__, fe_sec_voltage); +- +- switch (fe_sec_voltage) { +- case SEC_VOLTAGE_13: +- voltage = 0; +- break; +- case SEC_VOLTAGE_18: +- voltage = 1; +- break; +- case SEC_VOLTAGE_OFF: +- voltage = 0; +- break; +- default: +- dbg("%s: invalid fe_sec_voltage", __func__); +- ret = -EINVAL; +- goto error; +- }; +- +- cmd.args[0x00] = CMD_LNB_SET_DC_LEVEL; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = voltage; +- cmd.len = 0x03; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_diseqc_send_master_cmd(struct dvb_frontend *fe, +- struct dvb_diseqc_master_cmd *diseqc_cmd) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i; +- u8 tmp; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- dbg("%s: msg_len=%d", __func__, diseqc_cmd->msg_len); +- +- if (diseqc_cmd->msg_len < 3 || diseqc_cmd->msg_len > 16) { +- ret = -EINVAL; +- goto error; +- } +- +- /* wait LNB TX */ +- for (i = 500, tmp = 0; i && !tmp; i--) { +- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); +- if (ret) +- goto error; +- +- usleep_range(10000, 20000); +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) { +- ret = -ETIMEDOUT; +- goto error; +- } +- +- ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_LNB_SEND_DISEQC; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 0; +- cmd.args[0x03] = 0; +- cmd.args[0x04] = 2; +- cmd.args[0x05] = 0; +- cmd.args[0x06] = diseqc_cmd->msg_len; +- memcpy(&cmd.args[0x07], diseqc_cmd->msg, diseqc_cmd->msg_len); +- cmd.len = 0x07 + diseqc_cmd->msg_len; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_diseqc_recv_slave_reply(struct dvb_frontend *fe, +- struct dvb_diseqc_slave_reply *reply) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i; +- u8 tmp; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- dbg("%s:", __func__); +- +- /* wait LNB RX */ +- for (i = 500, tmp = 0; i && !tmp; i--) { +- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x02); +- if (ret) +- goto error; +- +- usleep_range(10000, 20000); +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) { +- ret = -ETIMEDOUT; +- goto error; +- } +- +- /* reply len */ +- ret = tda10071_rd_reg(priv, 0x46, &tmp); +- if (ret) +- goto error; +- +- reply->msg_len = tmp & 0x1f; /* [4:0] */; +- if (reply->msg_len > sizeof(reply->msg)) +- reply->msg_len = sizeof(reply->msg); /* truncate API max */ +- +- /* read reply */ +- cmd.args[0x00] = CMD_LNB_UPDATE_REPLY; +- cmd.args[0x01] = 0; +- cmd.len = 0x02; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- ret = tda10071_rd_regs(priv, cmd.len, reply->msg, reply->msg_len); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_diseqc_send_burst(struct dvb_frontend *fe, +- fe_sec_mini_cmd_t fe_sec_mini_cmd) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i; +- u8 tmp, burst; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- dbg("%s: fe_sec_mini_cmd=%d", __func__, fe_sec_mini_cmd); +- +- switch (fe_sec_mini_cmd) { +- case SEC_MINI_A: +- burst = 0; +- break; +- case SEC_MINI_B: +- burst = 1; +- break; +- default: +- dbg("%s: invalid fe_sec_mini_cmd", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- /* wait LNB TX */ +- for (i = 500, tmp = 0; i && !tmp; i--) { +- ret = tda10071_rd_reg_mask(priv, 0x47, &tmp, 0x01); +- if (ret) +- goto error; +- +- usleep_range(10000, 20000); +- } +- +- dbg("%s: loop=%d", __func__, i); +- +- if (i == 0) { +- ret = -ETIMEDOUT; +- goto error; +- } +- +- ret = tda10071_wr_reg_mask(priv, 0x47, 0x00, 0x01); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_LNB_SEND_TONEBURST; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = burst; +- cmd.len = 0x03; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- int ret; +- u8 tmp; +- +- *status = 0; +- +- if (!priv->warm) { +- ret = 0; +- goto error; +- } +- +- ret = tda10071_rd_reg(priv, 0x39, &tmp); +- if (ret) +- goto error; +- +- if (tmp & 0x01) /* tuner PLL */ +- *status |= FE_HAS_SIGNAL; +- if (tmp & 0x02) /* demod PLL */ +- *status |= FE_HAS_CARRIER; +- if (tmp & 0x04) /* viterbi or LDPC*/ +- *status |= FE_HAS_VITERBI; +- if (tmp & 0x08) /* RS or BCH */ +- *status |= FE_HAS_SYNC | FE_HAS_LOCK; +- +- priv->fe_status = *status; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- int ret; +- u8 buf[2]; +- +- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +- *snr = 0; +- ret = 0; +- goto error; +- } +- +- ret = tda10071_rd_regs(priv, 0x3a, buf, 2); +- if (ret) +- goto error; +- +- /* Es/No dBx10 */ +- *snr = buf[0] << 8 | buf[1]; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret; +- u8 tmp; +- +- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +- *strength = 0; +- ret = 0; +- goto error; +- } +- +- cmd.args[0x00] = CMD_GET_AGCACC; +- cmd.args[0x01] = 0; +- cmd.len = 0x02; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- /* input power estimate dBm */ +- ret = tda10071_rd_reg(priv, 0x50, &tmp); +- if (ret) +- goto error; +- +- if (tmp < 181) +- tmp = 181; /* -75 dBm */ +- else if (tmp > 236) +- tmp = 236; /* -20 dBm */ +- +- /* scale value to 0x0000-0xffff */ +- *strength = (tmp-181) * 0xffff / (236-181); +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i, len; +- u8 tmp, reg, buf[8]; +- +- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +- *ber = priv->ber = 0; +- ret = 0; +- goto error; +- } +- +- switch (priv->delivery_system) { +- case SYS_DVBS: +- reg = 0x4c; +- len = 8; +- i = 1; +- break; +- case SYS_DVBS2: +- reg = 0x4d; +- len = 4; +- i = 0; +- break; +- default: +- *ber = priv->ber = 0; +- return 0; +- } +- +- ret = tda10071_rd_reg(priv, reg, &tmp); +- if (ret) +- goto error; +- +- if (priv->meas_count[i] == tmp) { +- dbg("%s: meas not ready=%02x", __func__, tmp); +- *ber = priv->ber; +- return 0; +- } else { +- priv->meas_count[i] = tmp; +- } +- +- cmd.args[0x00] = CMD_BER_UPDATE_COUNTERS; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = i; +- cmd.len = 0x03; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- ret = tda10071_rd_regs(priv, cmd.len, buf, len); +- if (ret) +- goto error; +- +- if (priv->delivery_system == SYS_DVBS) { +- *ber = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; +- priv->ucb += (buf[4] << 8) | buf[5]; +- } else { +- *ber = (buf[0] << 8) | buf[1]; +- } +- priv->ber = *ber; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- int ret = 0; +- +- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +- *ucblocks = 0; +- goto error; +- } +- +- /* UCB is updated when BER is read. Assume BER is read anyway. */ +- +- *ucblocks = priv->ucb; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_set_frontend(struct dvb_frontend *fe) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i; +- u8 mode, rolloff, pilot, inversion, div; +- +- dbg("%s: delivery_system=%d modulation=%d frequency=%d " \ +- "symbol_rate=%d inversion=%d pilot=%d rolloff=%d", __func__, +- c->delivery_system, c->modulation, c->frequency, +- c->symbol_rate, c->inversion, c->pilot, c->rolloff); +- +- priv->delivery_system = SYS_UNDEFINED; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- switch (c->inversion) { +- case INVERSION_OFF: +- inversion = 1; +- break; +- case INVERSION_ON: +- inversion = 0; +- break; +- case INVERSION_AUTO: +- /* 2 = auto; try first on then off +- * 3 = auto; try first off then on */ +- inversion = 3; +- break; +- default: +- dbg("%s: invalid inversion", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- switch (c->delivery_system) { +- case SYS_DVBS: +- rolloff = 0; +- pilot = 2; +- break; +- case SYS_DVBS2: +- switch (c->rolloff) { +- case ROLLOFF_20: +- rolloff = 2; +- break; +- case ROLLOFF_25: +- rolloff = 1; +- break; +- case ROLLOFF_35: +- rolloff = 0; +- break; +- case ROLLOFF_AUTO: +- default: +- dbg("%s: invalid rolloff", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- switch (c->pilot) { +- case PILOT_OFF: +- pilot = 0; +- break; +- case PILOT_ON: +- pilot = 1; +- break; +- case PILOT_AUTO: +- pilot = 2; +- break; +- default: +- dbg("%s: invalid pilot", __func__); +- ret = -EINVAL; +- goto error; +- } +- break; +- default: +- dbg("%s: invalid delivery_system", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- for (i = 0, mode = 0xff; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { +- if (c->delivery_system == TDA10071_MODCOD[i].delivery_system && +- c->modulation == TDA10071_MODCOD[i].modulation && +- c->fec_inner == TDA10071_MODCOD[i].fec) { +- mode = TDA10071_MODCOD[i].val; +- dbg("%s: mode found=%02x", __func__, mode); +- break; +- } +- } +- +- if (mode == 0xff) { +- dbg("%s: invalid parameter combination", __func__); +- ret = -EINVAL; +- goto error; +- } +- +- if (c->symbol_rate <= 5000000) +- div = 14; +- else +- div = 4; +- +- ret = tda10071_wr_reg(priv, 0x81, div); +- if (ret) +- goto error; +- +- ret = tda10071_wr_reg(priv, 0xe3, div); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_CHANGE_CHANNEL; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = mode; +- cmd.args[0x03] = (c->frequency >> 16) & 0xff; +- cmd.args[0x04] = (c->frequency >> 8) & 0xff; +- cmd.args[0x05] = (c->frequency >> 0) & 0xff; +- cmd.args[0x06] = ((c->symbol_rate / 1000) >> 8) & 0xff; +- cmd.args[0x07] = ((c->symbol_rate / 1000) >> 0) & 0xff; +- cmd.args[0x08] = (tda10071_ops.info.frequency_tolerance >> 8) & 0xff; +- cmd.args[0x09] = (tda10071_ops.info.frequency_tolerance >> 0) & 0xff; +- cmd.args[0x0a] = rolloff; +- cmd.args[0x0b] = inversion; +- cmd.args[0x0c] = pilot; +- cmd.args[0x0d] = 0x00; +- cmd.args[0x0e] = 0x00; +- cmd.len = 0x0f; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- priv->delivery_system = c->delivery_system; +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_get_frontend(struct dvb_frontend *fe) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- int ret, i; +- u8 buf[5], tmp; +- +- if (!priv->warm || !(priv->fe_status & FE_HAS_LOCK)) { +- ret = -EFAULT; +- goto error; +- } +- +- ret = tda10071_rd_regs(priv, 0x30, buf, 5); +- if (ret) +- goto error; +- +- tmp = buf[0] & 0x3f; +- for (i = 0; i < ARRAY_SIZE(TDA10071_MODCOD); i++) { +- if (tmp == TDA10071_MODCOD[i].val) { +- c->modulation = TDA10071_MODCOD[i].modulation; +- c->fec_inner = TDA10071_MODCOD[i].fec; +- c->delivery_system = TDA10071_MODCOD[i].delivery_system; +- } +- } +- +- switch ((buf[1] >> 0) & 0x01) { +- case 0: +- c->inversion = INVERSION_OFF; +- break; +- case 1: +- c->inversion = INVERSION_ON; +- break; +- } +- +- switch ((buf[1] >> 7) & 0x01) { +- case 0: +- c->pilot = PILOT_OFF; +- break; +- case 1: +- c->pilot = PILOT_ON; +- break; +- } +- +- c->frequency = (buf[2] << 16) | (buf[3] << 8) | (buf[4] << 0); +- +- ret = tda10071_rd_regs(priv, 0x52, buf, 3); +- if (ret) +- goto error; +- +- c->symbol_rate = (buf[0] << 16) | (buf[1] << 8) | (buf[2] << 0); +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_init(struct dvb_frontend *fe) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i, len, remaining, fw_size; +- const struct firmware *fw; +- u8 *fw_file = TDA10071_DEFAULT_FIRMWARE; +- u8 tmp, buf[4]; +- struct tda10071_reg_val_mask tab[] = { +- { 0xcd, 0x00, 0x07 }, +- { 0x80, 0x00, 0x02 }, +- { 0xcd, 0x00, 0xc0 }, +- { 0xce, 0x00, 0x1b }, +- { 0x9d, 0x00, 0x01 }, +- { 0x9d, 0x00, 0x02 }, +- { 0x9e, 0x00, 0x01 }, +- { 0x87, 0x00, 0x80 }, +- { 0xce, 0x00, 0x08 }, +- { 0xce, 0x00, 0x10 }, +- }; +- struct tda10071_reg_val_mask tab2[] = { +- { 0xf1, 0x70, 0xff }, +- { 0x88, priv->cfg.pll_multiplier, 0x3f }, +- { 0x89, 0x00, 0x10 }, +- { 0x89, 0x10, 0x10 }, +- { 0xc0, 0x01, 0x01 }, +- { 0xc0, 0x00, 0x01 }, +- { 0xe0, 0xff, 0xff }, +- { 0xe0, 0x00, 0xff }, +- { 0x96, 0x1e, 0x7e }, +- { 0x8b, 0x08, 0x08 }, +- { 0x8b, 0x00, 0x08 }, +- { 0x8f, 0x1a, 0x7e }, +- { 0x8c, 0x68, 0xff }, +- { 0x8d, 0x08, 0xff }, +- { 0x8e, 0x4c, 0xff }, +- { 0x8f, 0x01, 0x01 }, +- { 0x8b, 0x04, 0x04 }, +- { 0x8b, 0x00, 0x04 }, +- { 0x87, 0x05, 0x07 }, +- { 0x80, 0x00, 0x20 }, +- { 0xc8, 0x01, 0xff }, +- { 0xb4, 0x47, 0xff }, +- { 0xb5, 0x9c, 0xff }, +- { 0xb6, 0x7d, 0xff }, +- { 0xba, 0x00, 0x03 }, +- { 0xb7, 0x47, 0xff }, +- { 0xb8, 0x9c, 0xff }, +- { 0xb9, 0x7d, 0xff }, +- { 0xba, 0x00, 0x0c }, +- { 0xc8, 0x00, 0xff }, +- { 0xcd, 0x00, 0x04 }, +- { 0xcd, 0x00, 0x20 }, +- { 0xe8, 0x02, 0xff }, +- { 0xcf, 0x20, 0xff }, +- { 0x9b, 0xd7, 0xff }, +- { 0x9a, 0x01, 0x03 }, +- { 0xa8, 0x05, 0x0f }, +- { 0xa8, 0x65, 0xf0 }, +- { 0xa6, 0xa0, 0xf0 }, +- { 0x9d, 0x50, 0xfc }, +- { 0x9e, 0x20, 0xe0 }, +- { 0xa3, 0x1c, 0x7c }, +- { 0xd5, 0x03, 0x03 }, +- }; +- +- /* firmware status */ +- ret = tda10071_rd_reg(priv, 0x51, &tmp); +- if (ret) +- goto error; +- +- if (!tmp) { +- /* warm state - wake up device from sleep */ +- priv->warm = 1; +- +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = tda10071_wr_reg_mask(priv, tab[i].reg, +- tab[i].val, tab[i].mask); +- if (ret) +- goto error; +- } +- +- cmd.args[0x00] = CMD_SET_SLEEP_MODE; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 0; +- cmd.len = 0x03; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- } else { +- /* cold state - try to download firmware */ +- priv->warm = 0; +- +- /* request the firmware, this will block and timeout */ +- ret = request_firmware(&fw, fw_file, priv->i2c->dev.parent); +- if (ret) { +- err("did not find the firmware file. (%s) " +- "Please see linux/Documentation/dvb/ for more" \ +- " details on firmware-problems. (%d)", +- fw_file, ret); +- goto error; +- } +- +- /* init */ +- for (i = 0; i < ARRAY_SIZE(tab2); i++) { +- ret = tda10071_wr_reg_mask(priv, tab2[i].reg, +- tab2[i].val, tab2[i].mask); +- if (ret) +- goto error_release_firmware; +- } +- +- /* download firmware */ +- ret = tda10071_wr_reg(priv, 0xe0, 0x7f); +- if (ret) +- goto error_release_firmware; +- +- ret = tda10071_wr_reg(priv, 0xf7, 0x81); +- if (ret) +- goto error_release_firmware; +- +- ret = tda10071_wr_reg(priv, 0xf8, 0x00); +- if (ret) +- goto error_release_firmware; +- +- ret = tda10071_wr_reg(priv, 0xf9, 0x00); +- if (ret) +- goto error_release_firmware; +- +- info("found a '%s' in cold state, will try to load a firmware", +- tda10071_ops.info.name); +- +- info("downloading firmware from file '%s'", fw_file); +- +- /* do not download last byte */ +- fw_size = fw->size - 1; +- +- for (remaining = fw_size; remaining > 0; +- remaining -= (priv->cfg.i2c_wr_max - 1)) { +- len = remaining; +- if (len > (priv->cfg.i2c_wr_max - 1)) +- len = (priv->cfg.i2c_wr_max - 1); +- +- ret = tda10071_wr_regs(priv, 0xfa, +- (u8 *) &fw->data[fw_size - remaining], len); +- if (ret) { +- err("firmware download failed=%d", ret); +- if (ret) +- goto error_release_firmware; +- } +- } +- release_firmware(fw); +- +- ret = tda10071_wr_reg(priv, 0xf7, 0x0c); +- if (ret) +- goto error; +- +- ret = tda10071_wr_reg(priv, 0xe0, 0x00); +- if (ret) +- goto error; +- +- /* wait firmware start */ +- msleep(250); +- +- /* firmware status */ +- ret = tda10071_rd_reg(priv, 0x51, &tmp); +- if (ret) +- goto error; +- +- if (tmp) { +- info("firmware did not run"); +- ret = -EFAULT; +- goto error; +- } else { +- priv->warm = 1; +- } +- +- cmd.args[0x00] = CMD_GET_FW_VERSION; +- cmd.len = 0x01; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- ret = tda10071_rd_regs(priv, cmd.len, buf, 4); +- if (ret) +- goto error; +- +- info("firmware version %d.%d.%d.%d", +- buf[0], buf[1], buf[2], buf[3]); +- info("found a '%s' in warm state.", tda10071_ops.info.name); +- +- ret = tda10071_rd_regs(priv, 0x81, buf, 2); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_DEMOD_INIT; +- cmd.args[0x01] = ((priv->cfg.xtal / 1000) >> 8) & 0xff; +- cmd.args[0x02] = ((priv->cfg.xtal / 1000) >> 0) & 0xff; +- cmd.args[0x03] = buf[0]; +- cmd.args[0x04] = buf[1]; +- cmd.args[0x05] = priv->cfg.pll_multiplier; +- cmd.args[0x06] = priv->cfg.spec_inv; +- cmd.args[0x07] = 0x00; +- cmd.len = 0x08; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_TUNER_INIT; +- cmd.args[0x01] = 0x00; +- cmd.args[0x02] = 0x00; +- cmd.args[0x03] = 0x00; +- cmd.args[0x04] = 0x00; +- cmd.args[0x05] = 0x14; +- cmd.args[0x06] = 0x00; +- cmd.args[0x07] = 0x03; +- cmd.args[0x08] = 0x02; +- cmd.args[0x09] = 0x02; +- cmd.args[0x0a] = 0x00; +- cmd.args[0x0b] = 0x00; +- cmd.args[0x0c] = 0x00; +- cmd.args[0x0d] = 0x00; +- cmd.args[0x0e] = 0x00; +- cmd.len = 0x0f; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_MPEG_CONFIG; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = priv->cfg.ts_mode; +- cmd.args[0x03] = 0x00; +- cmd.args[0x04] = 0x04; +- cmd.args[0x05] = 0x00; +- cmd.len = 0x06; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- ret = tda10071_wr_reg_mask(priv, 0xf0, 0x01, 0x01); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_LNB_CONFIG; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 150; +- cmd.args[0x03] = 3; +- cmd.args[0x04] = 22; +- cmd.args[0x05] = 1; +- cmd.args[0x06] = 1; +- cmd.args[0x07] = 30; +- cmd.args[0x08] = 30; +- cmd.args[0x09] = 30; +- cmd.args[0x0a] = 30; +- cmd.len = 0x0b; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- cmd.args[0x00] = CMD_BER_CONTROL; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 14; +- cmd.args[0x03] = 14; +- cmd.len = 0x04; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- } +- +- return ret; +-error_release_firmware: +- release_firmware(fw); +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_sleep(struct dvb_frontend *fe) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- struct tda10071_cmd cmd; +- int ret, i; +- struct tda10071_reg_val_mask tab[] = { +- { 0xcd, 0x07, 0x07 }, +- { 0x80, 0x02, 0x02 }, +- { 0xcd, 0xc0, 0xc0 }, +- { 0xce, 0x1b, 0x1b }, +- { 0x9d, 0x01, 0x01 }, +- { 0x9d, 0x02, 0x02 }, +- { 0x9e, 0x01, 0x01 }, +- { 0x87, 0x80, 0x80 }, +- { 0xce, 0x08, 0x08 }, +- { 0xce, 0x10, 0x10 }, +- }; +- +- if (!priv->warm) { +- ret = -EFAULT; +- goto error; +- } +- +- cmd.args[0x00] = CMD_SET_SLEEP_MODE; +- cmd.args[0x01] = 0; +- cmd.args[0x02] = 1; +- cmd.len = 0x03; +- ret = tda10071_cmd_execute(priv, &cmd); +- if (ret) +- goto error; +- +- for (i = 0; i < ARRAY_SIZE(tab); i++) { +- ret = tda10071_wr_reg_mask(priv, tab[i].reg, tab[i].val, +- tab[i].mask); +- if (ret) +- goto error; +- } +- +- return ret; +-error: +- dbg("%s: failed=%d", __func__, ret); +- return ret; +-} +- +-static int tda10071_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *s) +-{ +- s->min_delay_ms = 8000; +- s->step_size = 0; +- s->max_drift = 0; +- +- return 0; +-} +- +-static void tda10071_release(struct dvb_frontend *fe) +-{ +- struct tda10071_priv *priv = fe->demodulator_priv; +- kfree(priv); +-} +- +-struct dvb_frontend *tda10071_attach(const struct tda10071_config *config, +- struct i2c_adapter *i2c) +-{ +- int ret; +- struct tda10071_priv *priv = NULL; +- u8 tmp; +- +- /* allocate memory for the internal priv */ +- priv = kzalloc(sizeof(struct tda10071_priv), GFP_KERNEL); +- if (priv == NULL) { +- ret = -ENOMEM; +- goto error; +- } +- +- /* setup the priv */ +- priv->i2c = i2c; +- memcpy(&priv->cfg, config, sizeof(struct tda10071_config)); +- +- /* chip ID */ +- ret = tda10071_rd_reg(priv, 0xff, &tmp); +- if (ret || tmp != 0x0f) +- goto error; +- +- /* chip type */ +- ret = tda10071_rd_reg(priv, 0xdd, &tmp); +- if (ret || tmp != 0x00) +- goto error; +- +- /* chip version */ +- ret = tda10071_rd_reg(priv, 0xfe, &tmp); +- if (ret || tmp != 0x01) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&priv->fe.ops, &tda10071_ops, sizeof(struct dvb_frontend_ops)); +- priv->fe.demodulator_priv = priv; +- +- return &priv->fe; +-error: +- dbg("%s: failed=%d", __func__, ret); +- kfree(priv); +- return NULL; +-} +-EXPORT_SYMBOL(tda10071_attach); +- +-static struct dvb_frontend_ops tda10071_ops = { +- .delsys = { SYS_DVBS, SYS_DVBS2 }, +- .info = { +- .name = "NXP TDA10071", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_tolerance = 5000, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | +- FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | +- FE_CAN_FEC_5_6 | +- FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | +- FE_CAN_FEC_8_9 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | +- FE_CAN_RECOVER | +- FE_CAN_2G_MODULATION +- }, +- +- .release = tda10071_release, +- +- .get_tune_settings = tda10071_get_tune_settings, +- +- .init = tda10071_init, +- .sleep = tda10071_sleep, +- +- .set_frontend = tda10071_set_frontend, +- .get_frontend = tda10071_get_frontend, +- +- .read_status = tda10071_read_status, +- .read_snr = tda10071_read_snr, +- .read_signal_strength = tda10071_read_signal_strength, +- .read_ber = tda10071_read_ber, +- .read_ucblocks = tda10071_read_ucblocks, +- +- .diseqc_send_master_cmd = tda10071_diseqc_send_master_cmd, +- .diseqc_recv_slave_reply = tda10071_diseqc_recv_slave_reply, +- .diseqc_send_burst = tda10071_diseqc_send_burst, +- +- .set_tone = tda10071_set_tone, +- .set_voltage = tda10071_set_voltage, +-}; +- +-MODULE_AUTHOR("Antti Palosaari "); +-MODULE_DESCRIPTION("NXP TDA10071 DVB-S/S2 demodulator driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda10071.h b/drivers/media/dvb/frontends/tda10071.h +deleted file mode 100644 +index 21163c4..0000000 +--- a/drivers/media/dvb/frontends/tda10071.h ++++ /dev/null +@@ -1,81 +0,0 @@ +-/* +- * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#ifndef TDA10071_H +-#define TDA10071_H +- +-#include +- +-struct tda10071_config { +- /* Demodulator I2C address. +- * Default: none, must set +- * Values: 0x55, +- */ +- u8 i2c_address; +- +- /* Max bytes I2C provider can write at once. +- * Note: Buffer is taken from the stack currently! +- * Default: none, must set +- * Values: +- */ +- u16 i2c_wr_max; +- +- /* TS output mode. +- * Default: TDA10071_TS_SERIAL +- * Values: +- */ +-#define TDA10071_TS_SERIAL 0 +-#define TDA10071_TS_PARALLEL 1 +- u8 ts_mode; +- +- /* Input spectrum inversion. +- * Default: 0 +- * Values: 0, 1 +- */ +- bool spec_inv; +- +- /* Xtal frequency Hz +- * Default: none, must set +- * Values: +- */ +- u32 xtal; +- +- /* PLL multiplier. +- * Default: none, must set +- * Values: +- */ +- u8 pll_multiplier; +-}; +- +- +-#if defined(CONFIG_DVB_TDA10071) || \ +- (defined(CONFIG_DVB_TDA10071_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tda10071_attach( +- const struct tda10071_config *config, struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *tda10071_attach( +- const struct tda10071_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* TDA10071_H */ +diff --git a/drivers/media/dvb/frontends/tda10071_priv.h b/drivers/media/dvb/frontends/tda10071_priv.h +deleted file mode 100644 +index 93c5e63..0000000 +--- a/drivers/media/dvb/frontends/tda10071_priv.h ++++ /dev/null +@@ -1,122 +0,0 @@ +-/* +- * NXP TDA10071 + Conexant CX24118A DVB-S/S2 demodulator + tuner driver +- * +- * Copyright (C) 2011 Antti Palosaari +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +- */ +- +-#ifndef TDA10071_PRIV +-#define TDA10071_PRIV +- +-#include "dvb_frontend.h" +-#include "tda10071.h" +-#include +- +-#define LOG_PREFIX "tda10071" +- +-#undef dbg +-#define dbg(f, arg...) \ +- if (tda10071_debug) \ +- printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef err +-#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +-#undef info +-#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +-#undef warn +-#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) +- +-struct tda10071_priv { +- struct i2c_adapter *i2c; +- struct dvb_frontend fe; +- struct tda10071_config cfg; +- +- u8 meas_count[2]; +- u32 ber; +- u32 ucb; +- fe_status_t fe_status; +- fe_delivery_system_t delivery_system; +- bool warm; /* FW running */ +-}; +- +-static struct tda10071_modcod { +- fe_delivery_system_t delivery_system; +- fe_modulation_t modulation; +- fe_code_rate_t fec; +- u8 val; +-} TDA10071_MODCOD[] = { +- /* NBC-QPSK */ +- { SYS_DVBS2, QPSK, FEC_AUTO, 0x00 }, +- { SYS_DVBS2, QPSK, FEC_1_2, 0x04 }, +- { SYS_DVBS2, QPSK, FEC_3_5, 0x05 }, +- { SYS_DVBS2, QPSK, FEC_2_3, 0x06 }, +- { SYS_DVBS2, QPSK, FEC_3_4, 0x07 }, +- { SYS_DVBS2, QPSK, FEC_4_5, 0x08 }, +- { SYS_DVBS2, QPSK, FEC_5_6, 0x09 }, +- { SYS_DVBS2, QPSK, FEC_8_9, 0x0a }, +- { SYS_DVBS2, QPSK, FEC_9_10, 0x0b }, +- /* 8PSK */ +- { SYS_DVBS2, PSK_8, FEC_3_5, 0x0c }, +- { SYS_DVBS2, PSK_8, FEC_2_3, 0x0d }, +- { SYS_DVBS2, PSK_8, FEC_3_4, 0x0e }, +- { SYS_DVBS2, PSK_8, FEC_5_6, 0x0f }, +- { SYS_DVBS2, PSK_8, FEC_8_9, 0x10 }, +- { SYS_DVBS2, PSK_8, FEC_9_10, 0x11 }, +- /* QPSK */ +- { SYS_DVBS, QPSK, FEC_AUTO, 0x2d }, +- { SYS_DVBS, QPSK, FEC_1_2, 0x2e }, +- { SYS_DVBS, QPSK, FEC_2_3, 0x2f }, +- { SYS_DVBS, QPSK, FEC_3_4, 0x30 }, +- { SYS_DVBS, QPSK, FEC_5_6, 0x31 }, +- { SYS_DVBS, QPSK, FEC_7_8, 0x32 }, +-}; +- +-struct tda10071_reg_val_mask { +- u8 reg; +- u8 val; +- u8 mask; +-}; +- +-/* firmware filename */ +-#define TDA10071_DEFAULT_FIRMWARE "dvb-fe-tda10071.fw" +- +-/* firmware commands */ +-#define CMD_DEMOD_INIT 0x10 +-#define CMD_CHANGE_CHANNEL 0x11 +-#define CMD_MPEG_CONFIG 0x13 +-#define CMD_TUNER_INIT 0x15 +-#define CMD_GET_AGCACC 0x1a +- +-#define CMD_LNB_CONFIG 0x20 +-#define CMD_LNB_SEND_DISEQC 0x21 +-#define CMD_LNB_SET_DC_LEVEL 0x22 +-#define CMD_LNB_PCB_CONFIG 0x23 +-#define CMD_LNB_SEND_TONEBURST 0x24 +-#define CMD_LNB_UPDATE_REPLY 0x25 +- +-#define CMD_GET_FW_VERSION 0x35 +-#define CMD_SET_SLEEP_MODE 0x36 +-#define CMD_BER_CONTROL 0x3e +-#define CMD_BER_UPDATE_COUNTERS 0x3f +- +-/* firmare command struct */ +-#define TDA10071_ARGLEN 0x1e +-struct tda10071_cmd { +- u8 args[TDA10071_ARGLEN]; +- u8 len; +-}; +- +- +-#endif /* TDA10071_PRIV */ +diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c +deleted file mode 100644 +index fcfe2e0..0000000 +--- a/drivers/media/dvb/frontends/tda10086.c ++++ /dev/null +@@ -1,777 +0,0 @@ +- /* +- Driver for Philips tda10086 DVBS Demodulator +- +- (c) 2006 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "tda10086.h" +- +-#define SACLK 96000000 +- +-struct tda10086_state { +- struct i2c_adapter* i2c; +- const struct tda10086_config* config; +- struct dvb_frontend frontend; +- +- /* private demod data */ +- u32 frequency; +- u32 symbol_rate; +- bool has_lock; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "tda10086: " args); \ +- } while (0) +- +-static int tda10086_write_byte(struct tda10086_state *state, int reg, int data) +-{ +- int ret; +- u8 b0[] = { reg, data }; +- struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 }; +- +- msg.addr = state->config->demod_address; +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", +- __func__, reg, data, ret); +- +- return (ret != 1) ? ret : 0; +-} +- +-static int tda10086_read_byte(struct tda10086_state *state, int reg) +-{ +- int ret; +- u8 b0[] = { reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 }, +- { .flags = I2C_M_RD, .buf = b1, .len = 1 }}; +- +- msg[0].addr = state->config->demod_address; +- msg[1].addr = state->config->demod_address; +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, +- ret); +- return ret; +- } +- +- return b1[0]; +-} +- +-static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data) +-{ +- int val; +- +- /* read a byte and check */ +- val = tda10086_read_byte(state, reg); +- if (val < 0) +- return val; +- +- /* mask if off */ +- val = val & ~mask; +- val |= data & 0xff; +- +- /* write it out again */ +- return tda10086_write_byte(state, reg, val); +-} +- +-static int tda10086_init(struct dvb_frontend* fe) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 t22k_off = 0x80; +- +- dprintk ("%s\n", __func__); +- +- if (state->config->diseqc_tone) +- t22k_off = 0; +- /* reset */ +- tda10086_write_byte(state, 0x00, 0x00); +- msleep(10); +- +- /* misc setup */ +- tda10086_write_byte(state, 0x01, 0x94); +- tda10086_write_byte(state, 0x02, 0x35); /* NOTE: TT drivers appear to disable CSWP */ +- tda10086_write_byte(state, 0x03, 0xe4); +- tda10086_write_byte(state, 0x04, 0x43); +- tda10086_write_byte(state, 0x0c, 0x0c); +- tda10086_write_byte(state, 0x1b, 0xb0); /* noise threshold */ +- tda10086_write_byte(state, 0x20, 0x89); /* misc */ +- tda10086_write_byte(state, 0x30, 0x04); /* acquisition period length */ +- tda10086_write_byte(state, 0x32, 0x00); /* irq off */ +- tda10086_write_byte(state, 0x31, 0x56); /* setup AFC */ +- +- /* setup PLL (this assumes SACLK = 96MHz) */ +- tda10086_write_byte(state, 0x55, 0x2c); /* misc PLL setup */ +- if (state->config->xtal_freq == TDA10086_XTAL_16M) { +- tda10086_write_byte(state, 0x3a, 0x0b); /* M=12 */ +- tda10086_write_byte(state, 0x3b, 0x01); /* P=2 */ +- } else { +- tda10086_write_byte(state, 0x3a, 0x17); /* M=24 */ +- tda10086_write_byte(state, 0x3b, 0x00); /* P=1 */ +- } +- tda10086_write_mask(state, 0x55, 0x20, 0x00); /* powerup PLL */ +- +- /* setup TS interface */ +- tda10086_write_byte(state, 0x11, 0x81); +- tda10086_write_byte(state, 0x12, 0x81); +- tda10086_write_byte(state, 0x19, 0x40); /* parallel mode A + MSBFIRST */ +- tda10086_write_byte(state, 0x56, 0x80); /* powerdown WPLL - unused in the mode we use */ +- tda10086_write_byte(state, 0x57, 0x08); /* bypass WPLL - unused in the mode we use */ +- tda10086_write_byte(state, 0x10, 0x2a); +- +- /* setup ADC */ +- tda10086_write_byte(state, 0x58, 0x61); /* ADC setup */ +- tda10086_write_mask(state, 0x58, 0x01, 0x00); /* powerup ADC */ +- +- /* setup AGC */ +- tda10086_write_byte(state, 0x05, 0x0B); +- tda10086_write_byte(state, 0x37, 0x63); +- tda10086_write_byte(state, 0x3f, 0x0a); /* NOTE: flydvb varies it */ +- tda10086_write_byte(state, 0x40, 0x64); +- tda10086_write_byte(state, 0x41, 0x4f); +- tda10086_write_byte(state, 0x42, 0x43); +- +- /* setup viterbi */ +- tda10086_write_byte(state, 0x1a, 0x11); /* VBER 10^6, DVB, QPSK */ +- +- /* setup carrier recovery */ +- tda10086_write_byte(state, 0x3d, 0x80); +- +- /* setup SEC */ +- tda10086_write_byte(state, 0x36, t22k_off); /* all SEC off, 22k tone */ +- tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); +- tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); +- +- return 0; +-} +- +-static void tda10086_diseqc_wait(struct tda10086_state *state) +-{ +- unsigned long timeout = jiffies + msecs_to_jiffies(200); +- while (!(tda10086_read_byte(state, 0x50) & 0x01)) { +- if(time_after(jiffies, timeout)) { +- printk("%s: diseqc queue not ready, command may be lost.\n", __func__); +- break; +- } +- msleep(10); +- } +-} +- +-static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 t22k_off = 0x80; +- +- dprintk ("%s\n", __func__); +- +- if (state->config->diseqc_tone) +- t22k_off = 0; +- +- switch (tone) { +- case SEC_TONE_OFF: +- tda10086_write_byte(state, 0x36, t22k_off); +- break; +- +- case SEC_TONE_ON: +- tda10086_write_byte(state, 0x36, 0x01 + t22k_off); +- break; +- } +- +- return 0; +-} +- +-static int tda10086_send_master_cmd (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd* cmd) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- int i; +- u8 oldval; +- u8 t22k_off = 0x80; +- +- dprintk ("%s\n", __func__); +- +- if (state->config->diseqc_tone) +- t22k_off = 0; +- +- if (cmd->msg_len > 6) +- return -EINVAL; +- oldval = tda10086_read_byte(state, 0x36); +- +- for(i=0; i< cmd->msg_len; i++) { +- tda10086_write_byte(state, 0x48+i, cmd->msg[i]); +- } +- tda10086_write_byte(state, 0x36, (0x08 + t22k_off) +- | ((cmd->msg_len - 1) << 4)); +- +- tda10086_diseqc_wait(state); +- +- tda10086_write_byte(state, 0x36, oldval); +- +- return 0; +-} +- +-static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 oldval = tda10086_read_byte(state, 0x36); +- u8 t22k_off = 0x80; +- +- dprintk ("%s\n", __func__); +- +- if (state->config->diseqc_tone) +- t22k_off = 0; +- +- switch(minicmd) { +- case SEC_MINI_A: +- tda10086_write_byte(state, 0x36, 0x04 + t22k_off); +- break; +- +- case SEC_MINI_B: +- tda10086_write_byte(state, 0x36, 0x06 + t22k_off); +- break; +- } +- +- tda10086_diseqc_wait(state); +- +- tda10086_write_byte(state, 0x36, oldval); +- +- return 0; +-} +- +-static int tda10086_set_inversion(struct tda10086_state *state, +- struct dtv_frontend_properties *fe_params) +-{ +- u8 invval = 0x80; +- +- dprintk ("%s %i %i\n", __func__, fe_params->inversion, state->config->invert); +- +- switch(fe_params->inversion) { +- case INVERSION_OFF: +- if (state->config->invert) +- invval = 0x40; +- break; +- case INVERSION_ON: +- if (!state->config->invert) +- invval = 0x40; +- break; +- case INVERSION_AUTO: +- invval = 0x00; +- break; +- } +- tda10086_write_mask(state, 0x0c, 0xc0, invval); +- +- return 0; +-} +- +-static int tda10086_set_symbol_rate(struct tda10086_state *state, +- struct dtv_frontend_properties *fe_params) +-{ +- u8 dfn = 0; +- u8 afs = 0; +- u8 byp = 0; +- u8 reg37 = 0x43; +- u8 reg42 = 0x43; +- u64 big; +- u32 tmp; +- u32 bdr; +- u32 bdri; +- u32 symbol_rate = fe_params->symbol_rate; +- +- dprintk ("%s %i\n", __func__, symbol_rate); +- +- /* setup the decimation and anti-aliasing filters.. */ +- if (symbol_rate < (u32) (SACLK * 0.0137)) { +- dfn=4; +- afs=1; +- } else if (symbol_rate < (u32) (SACLK * 0.0208)) { +- dfn=4; +- afs=0; +- } else if (symbol_rate < (u32) (SACLK * 0.0270)) { +- dfn=3; +- afs=1; +- } else if (symbol_rate < (u32) (SACLK * 0.0416)) { +- dfn=3; +- afs=0; +- } else if (symbol_rate < (u32) (SACLK * 0.0550)) { +- dfn=2; +- afs=1; +- } else if (symbol_rate < (u32) (SACLK * 0.0833)) { +- dfn=2; +- afs=0; +- } else if (symbol_rate < (u32) (SACLK * 0.1100)) { +- dfn=1; +- afs=1; +- } else if (symbol_rate < (u32) (SACLK * 0.1666)) { +- dfn=1; +- afs=0; +- } else if (symbol_rate < (u32) (SACLK * 0.2200)) { +- dfn=0; +- afs=1; +- } else if (symbol_rate < (u32) (SACLK * 0.3333)) { +- dfn=0; +- afs=0; +- } else { +- reg37 = 0x63; +- reg42 = 0x4f; +- byp=1; +- } +- +- /* calculate BDR */ +- big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<> 8); +- tda10086_write_byte(state, 0x08, bdr >> 16); +- tda10086_write_byte(state, 0x09, bdri); +- tda10086_write_byte(state, 0x37, reg37); +- tda10086_write_byte(state, 0x42, reg42); +- +- return 0; +-} +- +-static int tda10086_set_fec(struct tda10086_state *state, +- struct dtv_frontend_properties *fe_params) +-{ +- u8 fecval; +- +- dprintk("%s %i\n", __func__, fe_params->fec_inner); +- +- switch (fe_params->fec_inner) { +- case FEC_1_2: +- fecval = 0x00; +- break; +- case FEC_2_3: +- fecval = 0x01; +- break; +- case FEC_3_4: +- fecval = 0x02; +- break; +- case FEC_4_5: +- fecval = 0x03; +- break; +- case FEC_5_6: +- fecval = 0x04; +- break; +- case FEC_6_7: +- fecval = 0x05; +- break; +- case FEC_7_8: +- fecval = 0x06; +- break; +- case FEC_8_9: +- fecval = 0x07; +- break; +- case FEC_AUTO: +- fecval = 0x08; +- break; +- default: +- return -1; +- } +- tda10086_write_byte(state, 0x0d, fecval); +- +- return 0; +-} +- +-static int tda10086_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; +- struct tda10086_state *state = fe->demodulator_priv; +- int ret; +- u32 freq = 0; +- int freqoff; +- +- dprintk ("%s\n", __func__); +- +- /* modify parameters for tuning */ +- tda10086_write_byte(state, 0x02, 0x35); +- state->has_lock = false; +- +- /* set params */ +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (fe->ops.tuner_ops.get_frequency) +- fe->ops.tuner_ops.get_frequency(fe, &freq); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- /* calcluate the frequency offset (in *Hz* not kHz) */ +- freqoff = fe_params->frequency - freq; +- freqoff = ((1<<16) * freqoff) / (SACLK/1000); +- tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f)); +- tda10086_write_byte(state, 0x3e, freqoff); +- +- if ((ret = tda10086_set_inversion(state, fe_params)) < 0) +- return ret; +- if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0) +- return ret; +- if ((ret = tda10086_set_fec(state, fe_params)) < 0) +- return ret; +- +- /* soft reset + disable TS output until lock */ +- tda10086_write_mask(state, 0x10, 0x40, 0x40); +- tda10086_write_mask(state, 0x00, 0x01, 0x00); +- +- state->symbol_rate = fe_params->symbol_rate; +- state->frequency = fe_params->frequency; +- return 0; +-} +- +-static int tda10086_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fe_params = &fe->dtv_property_cache; +- struct tda10086_state* state = fe->demodulator_priv; +- u8 val; +- int tmp; +- u64 tmp64; +- +- dprintk ("%s\n", __func__); +- +- /* check for invalid symbol rate */ +- if (fe_params->symbol_rate < 500000) +- return -EINVAL; +- +- /* calculate the updated frequency (note: we convert from Hz->kHz) */ +- tmp64 = tda10086_read_byte(state, 0x52); +- tmp64 |= (tda10086_read_byte(state, 0x51) << 8); +- if (tmp64 & 0x8000) +- tmp64 |= 0xffffffffffff0000ULL; +- tmp64 = (tmp64 * (SACLK/1000ULL)); +- do_div(tmp64, (1ULL<<15) * (1ULL<<1)); +- fe_params->frequency = (int) state->frequency + (int) tmp64; +- +- /* the inversion */ +- val = tda10086_read_byte(state, 0x0c); +- if (val & 0x80) { +- switch(val & 0x40) { +- case 0x00: +- fe_params->inversion = INVERSION_OFF; +- if (state->config->invert) +- fe_params->inversion = INVERSION_ON; +- break; +- default: +- fe_params->inversion = INVERSION_ON; +- if (state->config->invert) +- fe_params->inversion = INVERSION_OFF; +- break; +- } +- } else { +- tda10086_read_byte(state, 0x0f); +- switch(val & 0x02) { +- case 0x00: +- fe_params->inversion = INVERSION_OFF; +- if (state->config->invert) +- fe_params->inversion = INVERSION_ON; +- break; +- default: +- fe_params->inversion = INVERSION_ON; +- if (state->config->invert) +- fe_params->inversion = INVERSION_OFF; +- break; +- } +- } +- +- /* calculate the updated symbol rate */ +- tmp = tda10086_read_byte(state, 0x1d); +- if (tmp & 0x80) +- tmp |= 0xffffff00; +- tmp = (tmp * 480 * (1<<1)) / 128; +- tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000); +- fe_params->symbol_rate = state->symbol_rate + tmp; +- +- /* the FEC */ +- val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4; +- switch(val) { +- case 0x00: +- fe_params->fec_inner = FEC_1_2; +- break; +- case 0x01: +- fe_params->fec_inner = FEC_2_3; +- break; +- case 0x02: +- fe_params->fec_inner = FEC_3_4; +- break; +- case 0x03: +- fe_params->fec_inner = FEC_4_5; +- break; +- case 0x04: +- fe_params->fec_inner = FEC_5_6; +- break; +- case 0x05: +- fe_params->fec_inner = FEC_6_7; +- break; +- case 0x06: +- fe_params->fec_inner = FEC_7_8; +- break; +- case 0x07: +- fe_params->fec_inner = FEC_8_9; +- break; +- } +- +- return 0; +-} +- +-static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 val; +- +- dprintk ("%s\n", __func__); +- +- val = tda10086_read_byte(state, 0x0e); +- *fe_status = 0; +- if (val & 0x01) +- *fe_status |= FE_HAS_SIGNAL; +- if (val & 0x02) +- *fe_status |= FE_HAS_CARRIER; +- if (val & 0x04) +- *fe_status |= FE_HAS_VITERBI; +- if (val & 0x08) +- *fe_status |= FE_HAS_SYNC; +- if (val & 0x10) { +- *fe_status |= FE_HAS_LOCK; +- if (!state->has_lock) { +- state->has_lock = true; +- /* modify parameters for stable reception */ +- tda10086_write_byte(state, 0x02, 0x00); +- } +- } +- +- return 0; +-} +- +-static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 _str; +- +- dprintk ("%s\n", __func__); +- +- _str = 0xff - tda10086_read_byte(state, 0x43); +- *signal = (_str << 8) | _str; +- +- return 0; +-} +- +-static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- u8 _snr; +- +- dprintk ("%s\n", __func__); +- +- _snr = 0xff - tda10086_read_byte(state, 0x1c); +- *snr = (_snr << 8) | _snr; +- +- return 0; +-} +- +-static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- +- dprintk ("%s\n", __func__); +- +- /* read it */ +- *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f; +- +- /* reset counter */ +- tda10086_write_byte(state, 0x18, 0x00); +- tda10086_write_byte(state, 0x18, 0x80); +- +- return 0; +-} +- +-static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- +- dprintk ("%s\n", __func__); +- +- /* read it */ +- *ber = 0; +- *ber |= tda10086_read_byte(state, 0x15); +- *ber |= tda10086_read_byte(state, 0x16) << 8; +- *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16; +- +- return 0; +-} +- +-static int tda10086_sleep(struct dvb_frontend* fe) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- +- dprintk ("%s\n", __func__); +- +- tda10086_write_mask(state, 0x00, 0x08, 0x08); +- +- return 0; +-} +- +-static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct tda10086_state* state = fe->demodulator_priv; +- +- dprintk ("%s\n", __func__); +- +- if (enable) { +- tda10086_write_mask(state, 0x00, 0x10, 0x10); +- } else { +- tda10086_write_mask(state, 0x00, 0x10, 0x00); +- } +- +- return 0; +-} +- +-static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- +- if (p->symbol_rate > 20000000) { +- fesettings->min_delay_ms = 50; +- fesettings->step_size = 2000; +- fesettings->max_drift = 8000; +- } else if (p->symbol_rate > 12000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 1500; +- fesettings->max_drift = 9000; +- } else if (p->symbol_rate > 8000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 1000; +- fesettings->max_drift = 8000; +- } else if (p->symbol_rate > 4000000) { +- fesettings->min_delay_ms = 100; +- fesettings->step_size = 500; +- fesettings->max_drift = 7000; +- } else if (p->symbol_rate > 2000000) { +- fesettings->min_delay_ms = 200; +- fesettings->step_size = p->symbol_rate / 8000; +- fesettings->max_drift = 14 * fesettings->step_size; +- } else { +- fesettings->min_delay_ms = 200; +- fesettings->step_size = p->symbol_rate / 8000; +- fesettings->max_drift = 18 * fesettings->step_size; +- } +- +- return 0; +-} +- +-static void tda10086_release(struct dvb_frontend* fe) +-{ +- struct tda10086_state *state = fe->demodulator_priv; +- tda10086_sleep(fe); +- kfree(state); +-} +- +-static struct dvb_frontend_ops tda10086_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Philips TDA10086 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- .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_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK +- }, +- +- .release = tda10086_release, +- +- .init = tda10086_init, +- .sleep = tda10086_sleep, +- .i2c_gate_ctrl = tda10086_i2c_gate_ctrl, +- +- .set_frontend = tda10086_set_frontend, +- .get_frontend = tda10086_get_frontend, +- .get_tune_settings = tda10086_get_tune_settings, +- +- .read_status = tda10086_read_status, +- .read_ber = tda10086_read_ber, +- .read_signal_strength = tda10086_read_signal_strength, +- .read_snr = tda10086_read_snr, +- .read_ucblocks = tda10086_read_ucblocks, +- +- .diseqc_send_master_cmd = tda10086_send_master_cmd, +- .diseqc_send_burst = tda10086_send_burst, +- .set_tone = tda10086_set_tone, +-}; +- +-struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, +- struct i2c_adapter* i2c) +-{ +- struct tda10086_state *state; +- +- dprintk ("%s\n", __func__); +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if (tda10086_read_byte(state, 0x1e) != 0xe1) { +- kfree(state); +- return NULL; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator"); +-MODULE_AUTHOR("Andrew de Quincey"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(tda10086_attach); +diff --git a/drivers/media/dvb/frontends/tda10086.h b/drivers/media/dvb/frontends/tda10086.h +deleted file mode 100644 +index 61148c5..0000000 +--- a/drivers/media/dvb/frontends/tda10086.h ++++ /dev/null +@@ -1,61 +0,0 @@ +- /* +- Driver for Philips tda10086 DVBS Frontend +- +- (c) 2006 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#ifndef TDA10086_H +-#define TDA10086_H +- +-#include +-#include +- +-enum tda10086_xtal { +- TDA10086_XTAL_16M, +- TDA10086_XTAL_4M +-}; +- +-struct tda10086_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* does the "inversion" need inverted? */ +- u8 invert; +- +- /* do we need the diseqc signal with carrier? */ +- u8 diseqc_tone; +- +- /* frequency of the reference xtal */ +- enum tda10086_xtal xtal_freq; +-}; +- +-#if defined(CONFIG_DVB_TDA10086) || (defined(CONFIG_DVB_TDA10086_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_TDA10086 */ +- +-#endif /* TDA10086_H */ +diff --git a/drivers/media/dvb/frontends/tda18271c2dd.c b/drivers/media/dvb/frontends/tda18271c2dd.c +deleted file mode 100644 +index ad7c72e..0000000 +--- a/drivers/media/dvb/frontends/tda18271c2dd.c ++++ /dev/null +@@ -1,1246 +0,0 @@ +-/* +- * tda18271c2dd: Driver for the TDA18271C2 tuner +- * +- * Copyright (C) 2010 Digital Devices GmbH +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +- +-struct SStandardParam { +- s32 m_IFFrequency; +- u32 m_BandWidth; +- u8 m_EP3_4_0; +- u8 m_EB22; +-}; +- +-struct SMap { +- u32 m_Frequency; +- u8 m_Param; +-}; +- +-struct SMapI { +- u32 m_Frequency; +- s32 m_Param; +-}; +- +-struct SMap2 { +- u32 m_Frequency; +- u8 m_Param1; +- u8 m_Param2; +-}; +- +-struct SRFBandMap { +- u32 m_RF_max; +- u32 m_RF1_Default; +- u32 m_RF2_Default; +- u32 m_RF3_Default; +-}; +- +-enum ERegister { +- ID = 0, +- TM, +- PL, +- EP1, EP2, EP3, EP4, EP5, +- CPD, CD1, CD2, CD3, +- MPD, MD1, MD2, MD3, +- EB1, EB2, EB3, EB4, EB5, EB6, EB7, EB8, EB9, EB10, +- EB11, EB12, EB13, EB14, EB15, EB16, EB17, EB18, EB19, EB20, +- EB21, EB22, EB23, +- NUM_REGS +-}; +- +-struct tda_state { +- struct i2c_adapter *i2c; +- u8 adr; +- +- u32 m_Frequency; +- u32 IF; +- +- u8 m_IFLevelAnalog; +- u8 m_IFLevelDigital; +- u8 m_IFLevelDVBC; +- u8 m_IFLevelDVBT; +- +- u8 m_EP4; +- u8 m_EP3_Standby; +- +- bool m_bMaster; +- +- s32 m_SettlingTime; +- +- u8 m_Regs[NUM_REGS]; +- +- /* Tracking filter settings for band 0..6 */ +- u32 m_RF1[7]; +- s32 m_RF_A1[7]; +- s32 m_RF_B1[7]; +- u32 m_RF2[7]; +- s32 m_RF_A2[7]; +- s32 m_RF_B2[7]; +- u32 m_RF3[7]; +- +- u8 m_TMValue_RFCal; /* Calibration temperatur */ +- +- bool m_bFMInput; /* true to use Pin 8 for FM Radio */ +- +-}; +- +-static int PowerScan(struct tda_state *state, +- u8 RFBand, u32 RF_in, +- u32 *pRF_Out, bool *pbcal); +- +-static int i2c_readn(struct i2c_adapter *adapter, u8 adr, u8 *data, int len) +-{ +- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, +- .buf = data, .len = len} }; +- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +-} +- +-static int i2c_write(struct i2c_adapter *adap, u8 adr, u8 *data, int len) +-{ +- struct i2c_msg msg = {.addr = adr, .flags = 0, +- .buf = data, .len = len}; +- +- if (i2c_transfer(adap, &msg, 1) != 1) { +- printk(KERN_ERR "tda18271c2dd: i2c write error at addr %i\n", adr); +- return -1; +- } +- return 0; +-} +- +-static int WriteRegs(struct tda_state *state, +- u8 SubAddr, u8 *Regs, u16 nRegs) +-{ +- u8 data[nRegs+1]; +- +- data[0] = SubAddr; +- memcpy(data + 1, Regs, nRegs); +- return i2c_write(state->i2c, state->adr, data, nRegs+1); +-} +- +-static int WriteReg(struct tda_state *state, u8 SubAddr, u8 Reg) +-{ +- u8 msg[2] = {SubAddr, Reg}; +- +- return i2c_write(state->i2c, state->adr, msg, 2); +-} +- +-static int Read(struct tda_state *state, u8 * Regs) +-{ +- return i2c_readn(state->i2c, state->adr, Regs, 16); +-} +- +-static int ReadExtented(struct tda_state *state, u8 * Regs) +-{ +- return i2c_readn(state->i2c, state->adr, Regs, NUM_REGS); +-} +- +-static int UpdateRegs(struct tda_state *state, u8 RegFrom, u8 RegTo) +-{ +- return WriteRegs(state, RegFrom, +- &state->m_Regs[RegFrom], RegTo-RegFrom+1); +-} +-static int UpdateReg(struct tda_state *state, u8 Reg) +-{ +- return WriteReg(state, Reg, state->m_Regs[Reg]); +-} +- +-#include "tda18271c2dd_maps.h" +- +-static void reset(struct tda_state *state) +-{ +- u32 ulIFLevelAnalog = 0; +- u32 ulIFLevelDigital = 2; +- u32 ulIFLevelDVBC = 7; +- u32 ulIFLevelDVBT = 6; +- u32 ulXTOut = 0; +- u32 ulStandbyMode = 0x06; /* Send in stdb, but leave osc on */ +- u32 ulSlave = 0; +- u32 ulFMInput = 0; +- u32 ulSettlingTime = 100; +- +- state->m_Frequency = 0; +- state->m_SettlingTime = 100; +- state->m_IFLevelAnalog = (ulIFLevelAnalog & 0x07) << 2; +- state->m_IFLevelDigital = (ulIFLevelDigital & 0x07) << 2; +- state->m_IFLevelDVBC = (ulIFLevelDVBC & 0x07) << 2; +- state->m_IFLevelDVBT = (ulIFLevelDVBT & 0x07) << 2; +- +- state->m_EP4 = 0x20; +- if (ulXTOut != 0) +- state->m_EP4 |= 0x40; +- +- state->m_EP3_Standby = ((ulStandbyMode & 0x07) << 5) | 0x0F; +- state->m_bMaster = (ulSlave == 0); +- +- state->m_SettlingTime = ulSettlingTime; +- +- state->m_bFMInput = (ulFMInput == 2); +-} +- +-static bool SearchMap1(struct SMap Map[], +- u32 Frequency, u8 *pParam) +-{ +- int i = 0; +- +- while ((Map[i].m_Frequency != 0) && (Frequency > Map[i].m_Frequency)) +- i += 1; +- if (Map[i].m_Frequency == 0) +- return false; +- *pParam = Map[i].m_Param; +- return true; +-} +- +-static bool SearchMap2(struct SMapI Map[], +- u32 Frequency, s32 *pParam) +-{ +- int i = 0; +- +- while ((Map[i].m_Frequency != 0) && +- (Frequency > Map[i].m_Frequency)) +- i += 1; +- if (Map[i].m_Frequency == 0) +- return false; +- *pParam = Map[i].m_Param; +- return true; +-} +- +-static bool SearchMap3(struct SMap2 Map[], u32 Frequency, +- u8 *pParam1, u8 *pParam2) +-{ +- int i = 0; +- +- while ((Map[i].m_Frequency != 0) && +- (Frequency > Map[i].m_Frequency)) +- i += 1; +- if (Map[i].m_Frequency == 0) +- return false; +- *pParam1 = Map[i].m_Param1; +- *pParam2 = Map[i].m_Param2; +- return true; +-} +- +-static bool SearchMap4(struct SRFBandMap Map[], +- u32 Frequency, u8 *pRFBand) +-{ +- int i = 0; +- +- while (i < 7 && (Frequency > Map[i].m_RF_max)) +- i += 1; +- if (i == 7) +- return false; +- *pRFBand = i; +- return true; +-} +- +-static int ThermometerRead(struct tda_state *state, u8 *pTM_Value) +-{ +- int status = 0; +- +- do { +- u8 Regs[16]; +- state->m_Regs[TM] |= 0x10; +- status = UpdateReg(state, TM); +- if (status < 0) +- break; +- status = Read(state, Regs); +- if (status < 0) +- break; +- if (((Regs[TM] & 0x0F) == 0 && (Regs[TM] & 0x20) == 0x20) || +- ((Regs[TM] & 0x0F) == 8 && (Regs[TM] & 0x20) == 0x00)) { +- state->m_Regs[TM] ^= 0x20; +- status = UpdateReg(state, TM); +- if (status < 0) +- break; +- msleep(10); +- status = Read(state, Regs); +- if (status < 0) +- break; +- } +- *pTM_Value = (Regs[TM] & 0x20) +- ? m_Thermometer_Map_2[Regs[TM] & 0x0F] +- : m_Thermometer_Map_1[Regs[TM] & 0x0F] ; +- state->m_Regs[TM] &= ~0x10; /* Thermometer off */ +- status = UpdateReg(state, TM); +- if (status < 0) +- break; +- state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 ????????? */ +- status = UpdateReg(state, EP4); +- if (status < 0) +- break; +- } while (0); +- +- return status; +-} +- +-static int StandBy(struct tda_state *state) +-{ +- int status = 0; +- do { +- state->m_Regs[EB12] &= ~0x20; /* PD_AGC1_Det = 0 */ +- status = UpdateReg(state, EB12); +- if (status < 0) +- break; +- state->m_Regs[EB18] &= ~0x83; /* AGC1_loop_off = 0, AGC1_Gain = 6 dB */ +- status = UpdateReg(state, EB18); +- if (status < 0) +- break; +- state->m_Regs[EB21] |= 0x03; /* AGC2_Gain = -6 dB */ +- state->m_Regs[EP3] = state->m_EP3_Standby; +- status = UpdateReg(state, EP3); +- if (status < 0) +- break; +- state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LP_Fc[2] = 0 */ +- status = UpdateRegs(state, EB21, EB23); +- if (status < 0) +- break; +- } while (0); +- return status; +-} +- +-static int CalcMainPLL(struct tda_state *state, u32 freq) +-{ +- +- u8 PostDiv; +- u8 Div; +- u64 OscFreq; +- u32 MainDiv; +- +- if (!SearchMap3(m_Main_PLL_Map, freq, &PostDiv, &Div)) +- return -EINVAL; +- +- OscFreq = (u64) freq * (u64) Div; +- OscFreq *= (u64) 16384; +- do_div(OscFreq, (u64)16000000); +- MainDiv = OscFreq; +- +- state->m_Regs[MPD] = PostDiv & 0x77; +- state->m_Regs[MD1] = ((MainDiv >> 16) & 0x7F); +- state->m_Regs[MD2] = ((MainDiv >> 8) & 0xFF); +- state->m_Regs[MD3] = (MainDiv & 0xFF); +- +- return UpdateRegs(state, MPD, MD3); +-} +- +-static int CalcCalPLL(struct tda_state *state, u32 freq) +-{ +- u8 PostDiv; +- u8 Div; +- u64 OscFreq; +- u32 CalDiv; +- +- if (!SearchMap3(m_Cal_PLL_Map, freq, &PostDiv, &Div)) +- return -EINVAL; +- +- OscFreq = (u64)freq * (u64)Div; +- /* CalDiv = u32( OscFreq * 16384 / 16000000 ); */ +- OscFreq *= (u64)16384; +- do_div(OscFreq, (u64)16000000); +- CalDiv = OscFreq; +- +- state->m_Regs[CPD] = PostDiv; +- state->m_Regs[CD1] = ((CalDiv >> 16) & 0xFF); +- state->m_Regs[CD2] = ((CalDiv >> 8) & 0xFF); +- state->m_Regs[CD3] = (CalDiv & 0xFF); +- +- return UpdateRegs(state, CPD, CD3); +-} +- +-static int CalibrateRF(struct tda_state *state, +- u8 RFBand, u32 freq, s32 *pCprog) +-{ +- int status = 0; +- u8 Regs[NUM_REGS]; +- do { +- u8 BP_Filter = 0; +- u8 GainTaper = 0; +- u8 RFC_K = 0; +- u8 RFC_M = 0; +- +- state->m_Regs[EP4] &= ~0x03; /* CAL_mode = 0 */ +- status = UpdateReg(state, EP4); +- if (status < 0) +- break; +- state->m_Regs[EB18] |= 0x03; /* AGC1_Gain = 3 */ +- status = UpdateReg(state, EB18); +- if (status < 0) +- break; +- +- /* Switching off LT (as datasheet says) causes calibration on C1 to fail */ +- /* (Readout of Cprog is allways 255) */ +- if (state->m_Regs[ID] != 0x83) /* C1: ID == 83, C2: ID == 84 */ +- state->m_Regs[EP3] |= 0x40; /* SM_LT = 1 */ +- +- if (!(SearchMap1(m_BP_Filter_Map, freq, &BP_Filter) && +- SearchMap1(m_GainTaper_Map, freq, &GainTaper) && +- SearchMap3(m_KM_Map, freq, &RFC_K, &RFC_M))) +- return -EINVAL; +- +- state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | BP_Filter; +- state->m_Regs[EP2] = (RFBand << 5) | GainTaper; +- +- state->m_Regs[EB13] = (state->m_Regs[EB13] & ~0x7C) | (RFC_K << 4) | (RFC_M << 2); +- +- status = UpdateRegs(state, EP1, EP3); +- if (status < 0) +- break; +- status = UpdateReg(state, EB13); +- if (status < 0) +- break; +- +- state->m_Regs[EB4] |= 0x20; /* LO_ForceSrce = 1 */ +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +- +- state->m_Regs[EB7] |= 0x20; /* CAL_ForceSrce = 1 */ +- status = UpdateReg(state, EB7); +- if (status < 0) +- break; +- +- state->m_Regs[EB14] = 0; /* RFC_Cprog = 0 */ +- status = UpdateReg(state, EB14); +- if (status < 0) +- break; +- +- state->m_Regs[EB20] &= ~0x20; /* ForceLock = 0; */ +- status = UpdateReg(state, EB20); +- if (status < 0) +- break; +- +- state->m_Regs[EP4] |= 0x03; /* CAL_Mode = 3 */ +- status = UpdateRegs(state, EP4, EP5); +- if (status < 0) +- break; +- +- status = CalcCalPLL(state, freq); +- if (status < 0) +- break; +- status = CalcMainPLL(state, freq + 1000000); +- if (status < 0) +- break; +- +- msleep(5); +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- +- state->m_Regs[EB4] &= ~0x20; /* LO_ForceSrce = 0 */ +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +- +- state->m_Regs[EB7] &= ~0x20; /* CAL_ForceSrce = 0 */ +- status = UpdateReg(state, EB7); +- if (status < 0) +- break; +- msleep(10); +- +- state->m_Regs[EB20] |= 0x20; /* ForceLock = 1; */ +- status = UpdateReg(state, EB20); +- if (status < 0) +- break; +- msleep(60); +- +- state->m_Regs[EP4] &= ~0x03; /* CAL_Mode = 0 */ +- state->m_Regs[EP3] &= ~0x40; /* SM_LT = 0 */ +- state->m_Regs[EB18] &= ~0x03; /* AGC1_Gain = 0 */ +- status = UpdateReg(state, EB18); +- if (status < 0) +- break; +- status = UpdateRegs(state, EP3, EP4); +- if (status < 0) +- break; +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- +- status = ReadExtented(state, Regs); +- if (status < 0) +- break; +- +- *pCprog = Regs[EB14]; +- +- } while (0); +- return status; +-} +- +-static int RFTrackingFiltersInit(struct tda_state *state, +- u8 RFBand) +-{ +- int status = 0; +- +- u32 RF1 = m_RF_Band_Map[RFBand].m_RF1_Default; +- u32 RF2 = m_RF_Band_Map[RFBand].m_RF2_Default; +- u32 RF3 = m_RF_Band_Map[RFBand].m_RF3_Default; +- bool bcal = false; +- +- s32 Cprog_cal1 = 0; +- s32 Cprog_table1 = 0; +- s32 Cprog_cal2 = 0; +- s32 Cprog_table2 = 0; +- s32 Cprog_cal3 = 0; +- s32 Cprog_table3 = 0; +- +- state->m_RF_A1[RFBand] = 0; +- state->m_RF_B1[RFBand] = 0; +- state->m_RF_A2[RFBand] = 0; +- state->m_RF_B2[RFBand] = 0; +- +- do { +- status = PowerScan(state, RFBand, RF1, &RF1, &bcal); +- if (status < 0) +- break; +- if (bcal) { +- status = CalibrateRF(state, RFBand, RF1, &Cprog_cal1); +- if (status < 0) +- break; +- } +- SearchMap2(m_RF_Cal_Map, RF1, &Cprog_table1); +- if (!bcal) +- Cprog_cal1 = Cprog_table1; +- state->m_RF_B1[RFBand] = Cprog_cal1 - Cprog_table1; +- /* state->m_RF_A1[RF_Band] = ???? */ +- +- if (RF2 == 0) +- break; +- +- status = PowerScan(state, RFBand, RF2, &RF2, &bcal); +- if (status < 0) +- break; +- if (bcal) { +- status = CalibrateRF(state, RFBand, RF2, &Cprog_cal2); +- if (status < 0) +- break; +- } +- SearchMap2(m_RF_Cal_Map, RF2, &Cprog_table2); +- if (!bcal) +- Cprog_cal2 = Cprog_table2; +- +- state->m_RF_A1[RFBand] = +- (Cprog_cal2 - Cprog_table2 - Cprog_cal1 + Cprog_table1) / +- ((s32)(RF2) - (s32)(RF1)); +- +- if (RF3 == 0) +- break; +- +- status = PowerScan(state, RFBand, RF3, &RF3, &bcal); +- if (status < 0) +- break; +- if (bcal) { +- status = CalibrateRF(state, RFBand, RF3, &Cprog_cal3); +- if (status < 0) +- break; +- } +- SearchMap2(m_RF_Cal_Map, RF3, &Cprog_table3); +- if (!bcal) +- Cprog_cal3 = Cprog_table3; +- state->m_RF_A2[RFBand] = (Cprog_cal3 - Cprog_table3 - Cprog_cal2 + Cprog_table2) / ((s32)(RF3) - (s32)(RF2)); +- state->m_RF_B2[RFBand] = Cprog_cal2 - Cprog_table2; +- +- } while (0); +- +- state->m_RF1[RFBand] = RF1; +- state->m_RF2[RFBand] = RF2; +- state->m_RF3[RFBand] = RF3; +- +-#if 0 +- printk(KERN_ERR "tda18271c2dd: %s %d RF1 = %d A1 = %d B1 = %d RF2 = %d A2 = %d B2 = %d RF3 = %d\n", __func__, +- RFBand, RF1, state->m_RF_A1[RFBand], state->m_RF_B1[RFBand], RF2, +- state->m_RF_A2[RFBand], state->m_RF_B2[RFBand], RF3); +-#endif +- +- return status; +-} +- +-static int PowerScan(struct tda_state *state, +- u8 RFBand, u32 RF_in, u32 *pRF_Out, bool *pbcal) +-{ +- int status = 0; +- do { +- u8 Gain_Taper = 0; +- s32 RFC_Cprog = 0; +- u8 CID_Target = 0; +- u8 CountLimit = 0; +- u32 freq_MainPLL; +- u8 Regs[NUM_REGS]; +- u8 CID_Gain; +- s32 Count = 0; +- int sign = 1; +- bool wait = false; +- +- if (!(SearchMap2(m_RF_Cal_Map, RF_in, &RFC_Cprog) && +- SearchMap1(m_GainTaper_Map, RF_in, &Gain_Taper) && +- SearchMap3(m_CID_Target_Map, RF_in, &CID_Target, &CountLimit))) { +- +- printk(KERN_ERR "tda18271c2dd: %s Search map failed\n", __func__); +- return -EINVAL; +- } +- +- state->m_Regs[EP2] = (RFBand << 5) | Gain_Taper; +- state->m_Regs[EB14] = (RFC_Cprog); +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- status = UpdateReg(state, EB14); +- if (status < 0) +- break; +- +- freq_MainPLL = RF_in + 1000000; +- status = CalcMainPLL(state, freq_MainPLL); +- if (status < 0) +- break; +- msleep(5); +- state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x03) | 1; /* CAL_mode = 1 */ +- status = UpdateReg(state, EP4); +- if (status < 0) +- break; +- status = UpdateReg(state, EP2); /* Launch power measurement */ +- if (status < 0) +- break; +- status = ReadExtented(state, Regs); +- if (status < 0) +- break; +- CID_Gain = Regs[EB10] & 0x3F; +- state->m_Regs[ID] = Regs[ID]; /* Chip version, (needed for C1 workarround in CalibrateRF) */ +- +- *pRF_Out = RF_in; +- +- while (CID_Gain < CID_Target) { +- freq_MainPLL = RF_in + sign * Count + 1000000; +- status = CalcMainPLL(state, freq_MainPLL); +- if (status < 0) +- break; +- msleep(wait ? 5 : 1); +- wait = false; +- status = UpdateReg(state, EP2); /* Launch power measurement */ +- if (status < 0) +- break; +- status = ReadExtented(state, Regs); +- if (status < 0) +- break; +- CID_Gain = Regs[EB10] & 0x3F; +- Count += 200000; +- +- if (Count < CountLimit * 100000) +- continue; +- if (sign < 0) +- break; +- +- sign = -sign; +- Count = 200000; +- wait = true; +- } +- status = status; +- if (status < 0) +- break; +- if (CID_Gain >= CID_Target) { +- *pbcal = true; +- *pRF_Out = freq_MainPLL - 1000000; +- } else +- *pbcal = false; +- } while (0); +- +- return status; +-} +- +-static int PowerScanInit(struct tda_state *state) +-{ +- int status = 0; +- do { +- state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | 0x12; +- state->m_Regs[EP4] = (state->m_Regs[EP4] & ~0x1F); /* If level = 0, Cal mode = 0 */ +- status = UpdateRegs(state, EP3, EP4); +- if (status < 0) +- break; +- state->m_Regs[EB18] = (state->m_Regs[EB18] & ~0x03); /* AGC 1 Gain = 0 */ +- status = UpdateReg(state, EB18); +- if (status < 0) +- break; +- state->m_Regs[EB21] = (state->m_Regs[EB21] & ~0x03); /* AGC 2 Gain = 0 (Datasheet = 3) */ +- state->m_Regs[EB23] = (state->m_Regs[EB23] | 0x06); /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ +- status = UpdateRegs(state, EB21, EB23); +- if (status < 0) +- break; +- } while (0); +- return status; +-} +- +-static int CalcRFFilterCurve(struct tda_state *state) +-{ +- int status = 0; +- do { +- msleep(200); /* Temperature stabilisation */ +- status = PowerScanInit(state); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 0); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 1); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 2); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 3); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 4); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 5); +- if (status < 0) +- break; +- status = RFTrackingFiltersInit(state, 6); +- if (status < 0) +- break; +- status = ThermometerRead(state, &state->m_TMValue_RFCal); /* also switches off Cal mode !!! */ +- if (status < 0) +- break; +- } while (0); +- +- return status; +-} +- +-static int FixedContentsI2CUpdate(struct tda_state *state) +-{ +- static u8 InitRegs[] = { +- 0x08, 0x80, 0xC6, +- 0xDF, 0x16, 0x60, 0x80, +- 0x80, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0xFC, 0x01, 0x84, 0x41, +- 0x01, 0x84, 0x40, 0x07, +- 0x00, 0x00, 0x96, 0x3F, +- 0xC1, 0x00, 0x8F, 0x00, +- 0x00, 0x8C, 0x00, 0x20, +- 0xB3, 0x48, 0xB0, +- }; +- int status = 0; +- memcpy(&state->m_Regs[TM], InitRegs, EB23 - TM + 1); +- do { +- status = UpdateRegs(state, TM, EB23); +- if (status < 0) +- break; +- +- /* AGC1 gain setup */ +- state->m_Regs[EB17] = 0x00; +- status = UpdateReg(state, EB17); +- if (status < 0) +- break; +- state->m_Regs[EB17] = 0x03; +- status = UpdateReg(state, EB17); +- if (status < 0) +- break; +- state->m_Regs[EB17] = 0x43; +- status = UpdateReg(state, EB17); +- if (status < 0) +- break; +- state->m_Regs[EB17] = 0x4C; +- status = UpdateReg(state, EB17); +- if (status < 0) +- break; +- +- /* IRC Cal Low band */ +- state->m_Regs[EP3] = 0x1F; +- state->m_Regs[EP4] = 0x66; +- state->m_Regs[EP5] = 0x81; +- state->m_Regs[CPD] = 0xCC; +- state->m_Regs[CD1] = 0x6C; +- state->m_Regs[CD2] = 0x00; +- state->m_Regs[CD3] = 0x00; +- state->m_Regs[MPD] = 0xC5; +- state->m_Regs[MD1] = 0x77; +- state->m_Regs[MD2] = 0x08; +- state->m_Regs[MD3] = 0x00; +- status = UpdateRegs(state, EP2, MD3); /* diff between sw and datasheet (ep3-md3) */ +- if (status < 0) +- break; +- +-#if 0 +- state->m_Regs[EB4] = 0x61; /* missing in sw */ +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +- msleep(1); +- state->m_Regs[EB4] = 0x41; +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +-#endif +- +- msleep(5); +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- msleep(5); +- +- state->m_Regs[EP5] = 0x85; +- state->m_Regs[CPD] = 0xCB; +- state->m_Regs[CD1] = 0x66; +- state->m_Regs[CD2] = 0x70; +- status = UpdateRegs(state, EP3, CD3); +- if (status < 0) +- break; +- msleep(5); +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- msleep(30); +- +- /* IRC Cal mid band */ +- state->m_Regs[EP5] = 0x82; +- state->m_Regs[CPD] = 0xA8; +- state->m_Regs[CD2] = 0x00; +- state->m_Regs[MPD] = 0xA1; /* Datasheet = 0xA9 */ +- state->m_Regs[MD1] = 0x73; +- state->m_Regs[MD2] = 0x1A; +- status = UpdateRegs(state, EP3, MD3); +- if (status < 0) +- break; +- +- msleep(5); +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- msleep(5); +- +- state->m_Regs[EP5] = 0x86; +- state->m_Regs[CPD] = 0xA8; +- state->m_Regs[CD1] = 0x66; +- state->m_Regs[CD2] = 0xA0; +- status = UpdateRegs(state, EP3, CD3); +- if (status < 0) +- break; +- msleep(5); +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- msleep(30); +- +- /* IRC Cal high band */ +- state->m_Regs[EP5] = 0x83; +- state->m_Regs[CPD] = 0x98; +- state->m_Regs[CD1] = 0x65; +- state->m_Regs[CD2] = 0x00; +- state->m_Regs[MPD] = 0x91; /* Datasheet = 0x91 */ +- state->m_Regs[MD1] = 0x71; +- state->m_Regs[MD2] = 0xCD; +- status = UpdateRegs(state, EP3, MD3); +- if (status < 0) +- break; +- msleep(5); +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- msleep(5); +- state->m_Regs[EP5] = 0x87; +- state->m_Regs[CD1] = 0x65; +- state->m_Regs[CD2] = 0x50; +- status = UpdateRegs(state, EP3, CD3); +- if (status < 0) +- break; +- msleep(5); +- status = UpdateReg(state, EP2); +- if (status < 0) +- break; +- msleep(30); +- +- /* Back to normal */ +- state->m_Regs[EP4] = 0x64; +- status = UpdateReg(state, EP4); +- if (status < 0) +- break; +- status = UpdateReg(state, EP1); +- if (status < 0) +- break; +- +- } while (0); +- return status; +-} +- +-static int InitCal(struct tda_state *state) +-{ +- int status = 0; +- +- do { +- status = FixedContentsI2CUpdate(state); +- if (status < 0) +- break; +- status = CalcRFFilterCurve(state); +- if (status < 0) +- break; +- status = StandBy(state); +- if (status < 0) +- break; +- /* m_bInitDone = true; */ +- } while (0); +- return status; +-}; +- +-static int RFTrackingFiltersCorrection(struct tda_state *state, +- u32 Frequency) +-{ +- int status = 0; +- s32 Cprog_table; +- u8 RFBand; +- u8 dCoverdT; +- +- if (!SearchMap2(m_RF_Cal_Map, Frequency, &Cprog_table) || +- !SearchMap4(m_RF_Band_Map, Frequency, &RFBand) || +- !SearchMap1(m_RF_Cal_DC_Over_DT_Map, Frequency, &dCoverdT)) +- +- return -EINVAL; +- +- do { +- u8 TMValue_Current; +- u32 RF1 = state->m_RF1[RFBand]; +- u32 RF2 = state->m_RF1[RFBand]; +- u32 RF3 = state->m_RF1[RFBand]; +- s32 RF_A1 = state->m_RF_A1[RFBand]; +- s32 RF_B1 = state->m_RF_B1[RFBand]; +- s32 RF_A2 = state->m_RF_A2[RFBand]; +- s32 RF_B2 = state->m_RF_B2[RFBand]; +- s32 Capprox = 0; +- int TComp; +- +- state->m_Regs[EP3] &= ~0xE0; /* Power up */ +- status = UpdateReg(state, EP3); +- if (status < 0) +- break; +- +- status = ThermometerRead(state, &TMValue_Current); +- if (status < 0) +- break; +- +- if (RF3 == 0 || Frequency < RF2) +- Capprox = RF_A1 * ((s32)(Frequency) - (s32)(RF1)) + RF_B1 + Cprog_table; +- else +- Capprox = RF_A2 * ((s32)(Frequency) - (s32)(RF2)) + RF_B2 + Cprog_table; +- +- TComp = (int)(dCoverdT) * ((int)(TMValue_Current) - (int)(state->m_TMValue_RFCal))/1000; +- +- Capprox += TComp; +- +- if (Capprox < 0) +- Capprox = 0; +- else if (Capprox > 255) +- Capprox = 255; +- +- +- /* TODO Temperature compensation. There is defenitely a scale factor */ +- /* missing in the datasheet, so leave it out for now. */ +- state->m_Regs[EB14] = Capprox; +- +- status = UpdateReg(state, EB14); +- if (status < 0) +- break; +- +- } while (0); +- return status; +-} +- +-static int ChannelConfiguration(struct tda_state *state, +- u32 Frequency, int Standard) +-{ +- +- s32 IntermediateFrequency = m_StandardTable[Standard].m_IFFrequency; +- int status = 0; +- +- u8 BP_Filter = 0; +- u8 RF_Band = 0; +- u8 GainTaper = 0; +- u8 IR_Meas = 0; +- +- state->IF = IntermediateFrequency; +- /* printk("tda18271c2dd: %s Freq = %d Standard = %d IF = %d\n", __func__, Frequency, Standard, IntermediateFrequency); */ +- /* get values from tables */ +- +- if (!(SearchMap1(m_BP_Filter_Map, Frequency, &BP_Filter) && +- SearchMap1(m_GainTaper_Map, Frequency, &GainTaper) && +- SearchMap1(m_IR_Meas_Map, Frequency, &IR_Meas) && +- SearchMap4(m_RF_Band_Map, Frequency, &RF_Band))) { +- +- printk(KERN_ERR "tda18271c2dd: %s SearchMap failed\n", __func__); +- return -EINVAL; +- } +- +- do { +- state->m_Regs[EP3] = (state->m_Regs[EP3] & ~0x1F) | m_StandardTable[Standard].m_EP3_4_0; +- state->m_Regs[EP3] &= ~0x04; /* switch RFAGC to high speed mode */ +- +- /* m_EP4 default for XToutOn, CAL_Mode (0) */ +- state->m_Regs[EP4] = state->m_EP4 | ((Standard > HF_AnalogMax) ? state->m_IFLevelDigital : state->m_IFLevelAnalog); +- /* state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; */ +- if (Standard <= HF_AnalogMax) +- state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelAnalog; +- else if (Standard <= HF_ATSC) +- state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBT; +- else if (Standard <= HF_DVBC) +- state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDVBC; +- else +- state->m_Regs[EP4] = state->m_EP4 | state->m_IFLevelDigital; +- +- if ((Standard == HF_FM_Radio) && state->m_bFMInput) +- state->m_Regs[EP4] |= 80; +- +- state->m_Regs[MPD] &= ~0x80; +- if (Standard > HF_AnalogMax) +- state->m_Regs[MPD] |= 0x80; /* Add IF_notch for digital */ +- +- state->m_Regs[EB22] = m_StandardTable[Standard].m_EB22; +- +- /* Note: This is missing from flowchart in TDA18271 specification ( 1.5 MHz cutoff for FM ) */ +- if (Standard == HF_FM_Radio) +- state->m_Regs[EB23] |= 0x06; /* ForceLP_Fc2_En = 1, LPFc[2] = 1 */ +- else +- state->m_Regs[EB23] &= ~0x06; /* ForceLP_Fc2_En = 0, LPFc[2] = 0 */ +- +- status = UpdateRegs(state, EB22, EB23); +- if (status < 0) +- break; +- +- state->m_Regs[EP1] = (state->m_Regs[EP1] & ~0x07) | 0x40 | BP_Filter; /* Dis_Power_level = 1, Filter */ +- state->m_Regs[EP5] = (state->m_Regs[EP5] & ~0x07) | IR_Meas; +- state->m_Regs[EP2] = (RF_Band << 5) | GainTaper; +- +- state->m_Regs[EB1] = (state->m_Regs[EB1] & ~0x07) | +- (state->m_bMaster ? 0x04 : 0x00); /* CALVCO_FortLOn = MS */ +- /* AGC1_always_master = 0 */ +- /* AGC_firstn = 0 */ +- status = UpdateReg(state, EB1); +- if (status < 0) +- break; +- +- if (state->m_bMaster) { +- status = CalcMainPLL(state, Frequency + IntermediateFrequency); +- if (status < 0) +- break; +- status = UpdateRegs(state, TM, EP5); +- if (status < 0) +- break; +- state->m_Regs[EB4] |= 0x20; /* LO_forceSrce = 1 */ +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +- msleep(1); +- state->m_Regs[EB4] &= ~0x20; /* LO_forceSrce = 0 */ +- status = UpdateReg(state, EB4); +- if (status < 0) +- break; +- } else { +- u8 PostDiv = 0; +- u8 Div; +- status = CalcCalPLL(state, Frequency + IntermediateFrequency); +- if (status < 0) +- break; +- +- SearchMap3(m_Cal_PLL_Map, Frequency + IntermediateFrequency, &PostDiv, &Div); +- state->m_Regs[MPD] = (state->m_Regs[MPD] & ~0x7F) | (PostDiv & 0x77); +- status = UpdateReg(state, MPD); +- if (status < 0) +- break; +- status = UpdateRegs(state, TM, EP5); +- if (status < 0) +- break; +- +- state->m_Regs[EB7] |= 0x20; /* CAL_forceSrce = 1 */ +- status = UpdateReg(state, EB7); +- if (status < 0) +- break; +- msleep(1); +- state->m_Regs[EB7] &= ~0x20; /* CAL_forceSrce = 0 */ +- status = UpdateReg(state, EB7); +- if (status < 0) +- break; +- } +- msleep(20); +- if (Standard != HF_FM_Radio) +- state->m_Regs[EP3] |= 0x04; /* RFAGC to normal mode */ +- status = UpdateReg(state, EP3); +- if (status < 0) +- break; +- +- } while (0); +- return status; +-} +- +-static int sleep(struct dvb_frontend *fe) +-{ +- struct tda_state *state = fe->tuner_priv; +- +- StandBy(state); +- return 0; +-} +- +-static int init(struct dvb_frontend *fe) +-{ +- return 0; +-} +- +-static int release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +- +-static int set_params(struct dvb_frontend *fe) +-{ +- struct tda_state *state = fe->tuner_priv; +- int status = 0; +- int Standard; +- u32 bw = fe->dtv_property_cache.bandwidth_hz; +- u32 delsys = fe->dtv_property_cache.delivery_system; +- +- state->m_Frequency = fe->dtv_property_cache.frequency; +- +- switch (delsys) { +- case SYS_DVBT: +- case SYS_DVBT2: +- switch (bw) { +- case 6000000: +- Standard = HF_DVBT_6MHZ; +- break; +- case 7000000: +- Standard = HF_DVBT_7MHZ; +- break; +- case 8000000: +- Standard = HF_DVBT_8MHZ; +- break; +- default: +- return -EINVAL; +- } +- case SYS_DVBC_ANNEX_A: +- case SYS_DVBC_ANNEX_C: +- if (bw <= 6000000) +- Standard = HF_DVBC_6MHZ; +- else if (bw <= 7000000) +- Standard = HF_DVBC_7MHZ; +- else +- Standard = HF_DVBC_8MHZ; +- break; +- default: +- return -EINVAL; +- } +- do { +- status = RFTrackingFiltersCorrection(state, state->m_Frequency); +- if (status < 0) +- break; +- status = ChannelConfiguration(state, state->m_Frequency, +- Standard); +- if (status < 0) +- break; +- +- msleep(state->m_SettlingTime); /* Allow AGC's to settle down */ +- } while (0); +- return status; +-} +- +-#if 0 +-static int GetSignalStrength(s32 *pSignalStrength, u32 RFAgc, u32 IFAgc) +-{ +- if (IFAgc < 500) { +- /* Scale this from 0 to 50000 */ +- *pSignalStrength = IFAgc * 100; +- } else { +- /* Scale range 500-1500 to 50000-80000 */ +- *pSignalStrength = 50000 + (IFAgc - 500) * 30; +- } +- +- return 0; +-} +-#endif +- +-static int get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda_state *state = fe->tuner_priv; +- +- *frequency = state->IF; +- return 0; +-} +- +-static int get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- /* struct tda_state *state = fe->tuner_priv; */ +- /* *bandwidth = priv->bandwidth; */ +- return 0; +-} +- +- +-static struct dvb_tuner_ops tuner_ops = { +- .info = { +- .name = "NXP TDA18271C2D", +- .frequency_min = 47125000, +- .frequency_max = 865000000, +- .frequency_step = 62500 +- }, +- .init = init, +- .sleep = sleep, +- .set_params = set_params, +- .release = release, +- .get_if_frequency = get_if_frequency, +- .get_bandwidth = get_bandwidth, +-}; +- +-struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 adr) +-{ +- struct tda_state *state; +- +- state = kzalloc(sizeof(struct tda_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- fe->tuner_priv = state; +- state->adr = adr; +- state->i2c = i2c; +- memcpy(&fe->ops.tuner_ops, &tuner_ops, sizeof(struct dvb_tuner_ops)); +- reset(state); +- InitCal(state); +- +- return fe; +-} +-EXPORT_SYMBOL_GPL(tda18271c2dd_attach); +- +-MODULE_DESCRIPTION("TDA18271C2 driver"); +-MODULE_AUTHOR("DD"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda18271c2dd.h b/drivers/media/dvb/frontends/tda18271c2dd.h +deleted file mode 100644 +index 1389c74..0000000 +--- a/drivers/media/dvb/frontends/tda18271c2dd.h ++++ /dev/null +@@ -1,16 +0,0 @@ +-#ifndef _TDA18271C2DD_H_ +-#define _TDA18271C2DD_H_ +-#if defined(CONFIG_DVB_TDA18271C2DD) || (defined(CONFIG_DVB_TDA18271C2DD_MODULE) \ +- && defined(MODULE)) +-struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 adr); +-#else +-static inline struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe, +- struct i2c_adapter *i2c, u8 adr) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif +diff --git a/drivers/media/dvb/frontends/tda18271c2dd_maps.h b/drivers/media/dvb/frontends/tda18271c2dd_maps.h +deleted file mode 100644 +index b87661b..0000000 +--- a/drivers/media/dvb/frontends/tda18271c2dd_maps.h ++++ /dev/null +@@ -1,814 +0,0 @@ +-enum HF_S { +- HF_None = 0, HF_B, HF_DK, HF_G, HF_I, HF_L, HF_L1, HF_MN, HF_FM_Radio, +- HF_AnalogMax, HF_DVBT_6MHZ, HF_DVBT_7MHZ, HF_DVBT_8MHZ, +- HF_DVBT, HF_ATSC, HF_DVBC_6MHZ, HF_DVBC_7MHZ, +- HF_DVBC_8MHZ, HF_DVBC +-}; +- +-struct SStandardParam m_StandardTable[] = { +- { 0, 0, 0x00, 0x00 }, /* HF_None */ +- { 6000000, 7000000, 0x1D, 0x2C }, /* HF_B, */ +- { 6900000, 8000000, 0x1E, 0x2C }, /* HF_DK, */ +- { 7100000, 8000000, 0x1E, 0x2C }, /* HF_G, */ +- { 7250000, 8000000, 0x1E, 0x2C }, /* HF_I, */ +- { 6900000, 8000000, 0x1E, 0x2C }, /* HF_L, */ +- { 1250000, 8000000, 0x1E, 0x2C }, /* HF_L1, */ +- { 5400000, 6000000, 0x1C, 0x2C }, /* HF_MN, */ +- { 1250000, 500000, 0x18, 0x2C }, /* HF_FM_Radio, */ +- { 0, 0, 0x00, 0x00 }, /* HF_AnalogMax (Unused) */ +- { 3300000, 6000000, 0x1C, 0x58 }, /* HF_DVBT_6MHZ */ +- { 3500000, 7000000, 0x1C, 0x37 }, /* HF_DVBT_7MHZ */ +- { 4000000, 8000000, 0x1D, 0x37 }, /* HF_DVBT_8MHZ */ +- { 0, 0, 0x00, 0x00 }, /* HF_DVBT (Unused) */ +- { 5000000, 6000000, 0x1C, 0x37 }, /* HF_ATSC (center = 3.25 MHz) */ +- { 4000000, 6000000, 0x1D, 0x58 }, /* HF_DVBC_6MHZ (Chicago) */ +- { 4500000, 7000000, 0x1E, 0x37 }, /* HF_DVBC_7MHZ (not documented by NXP) */ +- { 5000000, 8000000, 0x1F, 0x37 }, /* HF_DVBC_8MHZ */ +- { 0, 0, 0x00, 0x00 }, /* HF_DVBC (Unused) */ +-}; +- +-struct SMap m_BP_Filter_Map[] = { +- { 62000000, 0x00 }, +- { 84000000, 0x01 }, +- { 100000000, 0x02 }, +- { 140000000, 0x03 }, +- { 170000000, 0x04 }, +- { 180000000, 0x05 }, +- { 865000000, 0x06 }, +- { 0, 0x00 }, /* Table End */ +-}; +- +-static struct SMapI m_RF_Cal_Map[] = { +- { 41000000, 0x0F }, +- { 43000000, 0x1C }, +- { 45000000, 0x2F }, +- { 46000000, 0x39 }, +- { 47000000, 0x40 }, +- { 47900000, 0x50 }, +- { 49100000, 0x16 }, +- { 50000000, 0x18 }, +- { 51000000, 0x20 }, +- { 53000000, 0x28 }, +- { 55000000, 0x2B }, +- { 56000000, 0x32 }, +- { 57000000, 0x35 }, +- { 58000000, 0x3E }, +- { 59000000, 0x43 }, +- { 60000000, 0x4E }, +- { 61100000, 0x55 }, +- { 63000000, 0x0F }, +- { 64000000, 0x11 }, +- { 65000000, 0x12 }, +- { 66000000, 0x15 }, +- { 67000000, 0x16 }, +- { 68000000, 0x17 }, +- { 70000000, 0x19 }, +- { 71000000, 0x1C }, +- { 72000000, 0x1D }, +- { 73000000, 0x1F }, +- { 74000000, 0x20 }, +- { 75000000, 0x21 }, +- { 76000000, 0x24 }, +- { 77000000, 0x25 }, +- { 78000000, 0x27 }, +- { 80000000, 0x28 }, +- { 81000000, 0x29 }, +- { 82000000, 0x2D }, +- { 83000000, 0x2E }, +- { 84000000, 0x2F }, +- { 85000000, 0x31 }, +- { 86000000, 0x33 }, +- { 87000000, 0x34 }, +- { 88000000, 0x35 }, +- { 89000000, 0x37 }, +- { 90000000, 0x38 }, +- { 91000000, 0x39 }, +- { 93000000, 0x3C }, +- { 94000000, 0x3E }, +- { 95000000, 0x3F }, +- { 96000000, 0x40 }, +- { 97000000, 0x42 }, +- { 99000000, 0x45 }, +- { 100000000, 0x46 }, +- { 102000000, 0x48 }, +- { 103000000, 0x4A }, +- { 105000000, 0x4D }, +- { 106000000, 0x4E }, +- { 107000000, 0x50 }, +- { 108000000, 0x51 }, +- { 110000000, 0x54 }, +- { 111000000, 0x56 }, +- { 112000000, 0x57 }, +- { 113000000, 0x58 }, +- { 114000000, 0x59 }, +- { 115000000, 0x5C }, +- { 116000000, 0x5D }, +- { 117000000, 0x5F }, +- { 119000000, 0x60 }, +- { 120000000, 0x64 }, +- { 121000000, 0x65 }, +- { 122000000, 0x66 }, +- { 123000000, 0x68 }, +- { 124000000, 0x69 }, +- { 125000000, 0x6C }, +- { 126000000, 0x6D }, +- { 127000000, 0x6E }, +- { 128000000, 0x70 }, +- { 129000000, 0x71 }, +- { 130000000, 0x75 }, +- { 131000000, 0x77 }, +- { 132000000, 0x78 }, +- { 133000000, 0x7B }, +- { 134000000, 0x7E }, +- { 135000000, 0x81 }, +- { 136000000, 0x82 }, +- { 137000000, 0x87 }, +- { 138000000, 0x88 }, +- { 139000000, 0x8D }, +- { 140000000, 0x8E }, +- { 141000000, 0x91 }, +- { 142000000, 0x95 }, +- { 143000000, 0x9A }, +- { 144000000, 0x9D }, +- { 145000000, 0xA1 }, +- { 146000000, 0xA2 }, +- { 147000000, 0xA4 }, +- { 148000000, 0xA9 }, +- { 149000000, 0xAE }, +- { 150000000, 0xB0 }, +- { 151000000, 0xB1 }, +- { 152000000, 0xB7 }, +- { 152600000, 0xBD }, +- { 154000000, 0x20 }, +- { 155000000, 0x22 }, +- { 156000000, 0x24 }, +- { 157000000, 0x25 }, +- { 158000000, 0x27 }, +- { 159000000, 0x29 }, +- { 160000000, 0x2C }, +- { 161000000, 0x2D }, +- { 163000000, 0x2E }, +- { 164000000, 0x2F }, +- { 164700000, 0x30 }, +- { 166000000, 0x11 }, +- { 167000000, 0x12 }, +- { 168000000, 0x13 }, +- { 169000000, 0x14 }, +- { 170000000, 0x15 }, +- { 172000000, 0x16 }, +- { 173000000, 0x17 }, +- { 174000000, 0x18 }, +- { 175000000, 0x1A }, +- { 176000000, 0x1B }, +- { 178000000, 0x1D }, +- { 179000000, 0x1E }, +- { 180000000, 0x1F }, +- { 181000000, 0x20 }, +- { 182000000, 0x21 }, +- { 183000000, 0x22 }, +- { 184000000, 0x24 }, +- { 185000000, 0x25 }, +- { 186000000, 0x26 }, +- { 187000000, 0x27 }, +- { 188000000, 0x29 }, +- { 189000000, 0x2A }, +- { 190000000, 0x2C }, +- { 191000000, 0x2D }, +- { 192000000, 0x2E }, +- { 193000000, 0x2F }, +- { 194000000, 0x30 }, +- { 195000000, 0x33 }, +- { 196000000, 0x35 }, +- { 198000000, 0x36 }, +- { 200000000, 0x38 }, +- { 201000000, 0x3C }, +- { 202000000, 0x3D }, +- { 203500000, 0x3E }, +- { 206000000, 0x0E }, +- { 208000000, 0x0F }, +- { 212000000, 0x10 }, +- { 216000000, 0x11 }, +- { 217000000, 0x12 }, +- { 218000000, 0x13 }, +- { 220000000, 0x14 }, +- { 222000000, 0x15 }, +- { 225000000, 0x16 }, +- { 228000000, 0x17 }, +- { 231000000, 0x18 }, +- { 234000000, 0x19 }, +- { 235000000, 0x1A }, +- { 236000000, 0x1B }, +- { 237000000, 0x1C }, +- { 240000000, 0x1D }, +- { 242000000, 0x1E }, +- { 244000000, 0x1F }, +- { 247000000, 0x20 }, +- { 249000000, 0x21 }, +- { 252000000, 0x22 }, +- { 253000000, 0x23 }, +- { 254000000, 0x24 }, +- { 256000000, 0x25 }, +- { 259000000, 0x26 }, +- { 262000000, 0x27 }, +- { 264000000, 0x28 }, +- { 267000000, 0x29 }, +- { 269000000, 0x2A }, +- { 271000000, 0x2B }, +- { 273000000, 0x2C }, +- { 275000000, 0x2D }, +- { 277000000, 0x2E }, +- { 279000000, 0x2F }, +- { 282000000, 0x30 }, +- { 284000000, 0x31 }, +- { 286000000, 0x32 }, +- { 287000000, 0x33 }, +- { 290000000, 0x34 }, +- { 293000000, 0x35 }, +- { 295000000, 0x36 }, +- { 297000000, 0x37 }, +- { 300000000, 0x38 }, +- { 303000000, 0x39 }, +- { 305000000, 0x3A }, +- { 306000000, 0x3B }, +- { 307000000, 0x3C }, +- { 310000000, 0x3D }, +- { 312000000, 0x3E }, +- { 315000000, 0x3F }, +- { 318000000, 0x40 }, +- { 320000000, 0x41 }, +- { 323000000, 0x42 }, +- { 324000000, 0x43 }, +- { 325000000, 0x44 }, +- { 327000000, 0x45 }, +- { 331000000, 0x46 }, +- { 334000000, 0x47 }, +- { 337000000, 0x48 }, +- { 339000000, 0x49 }, +- { 340000000, 0x4A }, +- { 341000000, 0x4B }, +- { 343000000, 0x4C }, +- { 345000000, 0x4D }, +- { 349000000, 0x4E }, +- { 352000000, 0x4F }, +- { 353000000, 0x50 }, +- { 355000000, 0x51 }, +- { 357000000, 0x52 }, +- { 359000000, 0x53 }, +- { 361000000, 0x54 }, +- { 362000000, 0x55 }, +- { 364000000, 0x56 }, +- { 368000000, 0x57 }, +- { 370000000, 0x58 }, +- { 372000000, 0x59 }, +- { 375000000, 0x5A }, +- { 376000000, 0x5B }, +- { 377000000, 0x5C }, +- { 379000000, 0x5D }, +- { 382000000, 0x5E }, +- { 384000000, 0x5F }, +- { 385000000, 0x60 }, +- { 386000000, 0x61 }, +- { 388000000, 0x62 }, +- { 390000000, 0x63 }, +- { 393000000, 0x64 }, +- { 394000000, 0x65 }, +- { 396000000, 0x66 }, +- { 397000000, 0x67 }, +- { 398000000, 0x68 }, +- { 400000000, 0x69 }, +- { 402000000, 0x6A }, +- { 403000000, 0x6B }, +- { 407000000, 0x6C }, +- { 408000000, 0x6D }, +- { 409000000, 0x6E }, +- { 410000000, 0x6F }, +- { 411000000, 0x70 }, +- { 412000000, 0x71 }, +- { 413000000, 0x72 }, +- { 414000000, 0x73 }, +- { 417000000, 0x74 }, +- { 418000000, 0x75 }, +- { 420000000, 0x76 }, +- { 422000000, 0x77 }, +- { 423000000, 0x78 }, +- { 424000000, 0x79 }, +- { 427000000, 0x7A }, +- { 428000000, 0x7B }, +- { 429000000, 0x7D }, +- { 432000000, 0x7F }, +- { 434000000, 0x80 }, +- { 435000000, 0x81 }, +- { 436000000, 0x83 }, +- { 437000000, 0x84 }, +- { 438000000, 0x85 }, +- { 439000000, 0x86 }, +- { 440000000, 0x87 }, +- { 441000000, 0x88 }, +- { 442000000, 0x89 }, +- { 445000000, 0x8A }, +- { 446000000, 0x8B }, +- { 447000000, 0x8C }, +- { 448000000, 0x8E }, +- { 449000000, 0x8F }, +- { 450000000, 0x90 }, +- { 452000000, 0x91 }, +- { 453000000, 0x93 }, +- { 454000000, 0x94 }, +- { 456000000, 0x96 }, +- { 457800000, 0x98 }, +- { 461000000, 0x11 }, +- { 468000000, 0x12 }, +- { 472000000, 0x13 }, +- { 473000000, 0x14 }, +- { 474000000, 0x15 }, +- { 481000000, 0x16 }, +- { 486000000, 0x17 }, +- { 491000000, 0x18 }, +- { 498000000, 0x19 }, +- { 499000000, 0x1A }, +- { 501000000, 0x1B }, +- { 506000000, 0x1C }, +- { 511000000, 0x1D }, +- { 516000000, 0x1E }, +- { 520000000, 0x1F }, +- { 521000000, 0x20 }, +- { 525000000, 0x21 }, +- { 529000000, 0x22 }, +- { 533000000, 0x23 }, +- { 539000000, 0x24 }, +- { 541000000, 0x25 }, +- { 547000000, 0x26 }, +- { 549000000, 0x27 }, +- { 551000000, 0x28 }, +- { 556000000, 0x29 }, +- { 561000000, 0x2A }, +- { 563000000, 0x2B }, +- { 565000000, 0x2C }, +- { 569000000, 0x2D }, +- { 571000000, 0x2E }, +- { 577000000, 0x2F }, +- { 580000000, 0x30 }, +- { 582000000, 0x31 }, +- { 584000000, 0x32 }, +- { 588000000, 0x33 }, +- { 591000000, 0x34 }, +- { 596000000, 0x35 }, +- { 598000000, 0x36 }, +- { 603000000, 0x37 }, +- { 604000000, 0x38 }, +- { 606000000, 0x39 }, +- { 612000000, 0x3A }, +- { 615000000, 0x3B }, +- { 617000000, 0x3C }, +- { 621000000, 0x3D }, +- { 622000000, 0x3E }, +- { 625000000, 0x3F }, +- { 632000000, 0x40 }, +- { 633000000, 0x41 }, +- { 634000000, 0x42 }, +- { 642000000, 0x43 }, +- { 643000000, 0x44 }, +- { 647000000, 0x45 }, +- { 650000000, 0x46 }, +- { 652000000, 0x47 }, +- { 657000000, 0x48 }, +- { 661000000, 0x49 }, +- { 662000000, 0x4A }, +- { 665000000, 0x4B }, +- { 667000000, 0x4C }, +- { 670000000, 0x4D }, +- { 673000000, 0x4E }, +- { 676000000, 0x4F }, +- { 677000000, 0x50 }, +- { 681000000, 0x51 }, +- { 683000000, 0x52 }, +- { 686000000, 0x53 }, +- { 688000000, 0x54 }, +- { 689000000, 0x55 }, +- { 691000000, 0x56 }, +- { 695000000, 0x57 }, +- { 698000000, 0x58 }, +- { 703000000, 0x59 }, +- { 704000000, 0x5A }, +- { 705000000, 0x5B }, +- { 707000000, 0x5C }, +- { 710000000, 0x5D }, +- { 712000000, 0x5E }, +- { 717000000, 0x5F }, +- { 718000000, 0x60 }, +- { 721000000, 0x61 }, +- { 722000000, 0x62 }, +- { 723000000, 0x63 }, +- { 725000000, 0x64 }, +- { 727000000, 0x65 }, +- { 730000000, 0x66 }, +- { 732000000, 0x67 }, +- { 735000000, 0x68 }, +- { 740000000, 0x69 }, +- { 741000000, 0x6A }, +- { 742000000, 0x6B }, +- { 743000000, 0x6C }, +- { 745000000, 0x6D }, +- { 747000000, 0x6E }, +- { 748000000, 0x6F }, +- { 750000000, 0x70 }, +- { 752000000, 0x71 }, +- { 754000000, 0x72 }, +- { 757000000, 0x73 }, +- { 758000000, 0x74 }, +- { 760000000, 0x75 }, +- { 763000000, 0x76 }, +- { 764000000, 0x77 }, +- { 766000000, 0x78 }, +- { 767000000, 0x79 }, +- { 768000000, 0x7A }, +- { 773000000, 0x7B }, +- { 774000000, 0x7C }, +- { 776000000, 0x7D }, +- { 777000000, 0x7E }, +- { 778000000, 0x7F }, +- { 779000000, 0x80 }, +- { 781000000, 0x81 }, +- { 783000000, 0x82 }, +- { 784000000, 0x83 }, +- { 785000000, 0x84 }, +- { 786000000, 0x85 }, +- { 793000000, 0x86 }, +- { 794000000, 0x87 }, +- { 795000000, 0x88 }, +- { 797000000, 0x89 }, +- { 799000000, 0x8A }, +- { 801000000, 0x8B }, +- { 802000000, 0x8C }, +- { 803000000, 0x8D }, +- { 804000000, 0x8E }, +- { 810000000, 0x90 }, +- { 811000000, 0x91 }, +- { 812000000, 0x92 }, +- { 814000000, 0x93 }, +- { 816000000, 0x94 }, +- { 817000000, 0x96 }, +- { 818000000, 0x97 }, +- { 820000000, 0x98 }, +- { 821000000, 0x99 }, +- { 822000000, 0x9A }, +- { 828000000, 0x9B }, +- { 829000000, 0x9D }, +- { 830000000, 0x9F }, +- { 831000000, 0xA0 }, +- { 833000000, 0xA1 }, +- { 835000000, 0xA2 }, +- { 836000000, 0xA3 }, +- { 837000000, 0xA4 }, +- { 838000000, 0xA6 }, +- { 840000000, 0xA8 }, +- { 842000000, 0xA9 }, +- { 845000000, 0xAA }, +- { 846000000, 0xAB }, +- { 847000000, 0xAD }, +- { 848000000, 0xAE }, +- { 852000000, 0xAF }, +- { 853000000, 0xB0 }, +- { 858000000, 0xB1 }, +- { 860000000, 0xB2 }, +- { 861000000, 0xB3 }, +- { 862000000, 0xB4 }, +- { 863000000, 0xB6 }, +- { 864000000, 0xB8 }, +- { 865000000, 0xB9 }, +- { 0, 0x00 }, /* Table End */ +-}; +- +- +-static struct SMap2 m_KM_Map[] = { +- { 47900000, 3, 2 }, +- { 61100000, 3, 1 }, +- { 350000000, 3, 0 }, +- { 720000000, 2, 1 }, +- { 865000000, 3, 3 }, +- { 0, 0x00 }, /* Table End */ +-}; +- +-static struct SMap2 m_Main_PLL_Map[] = { +- { 33125000, 0x57, 0xF0 }, +- { 35500000, 0x56, 0xE0 }, +- { 38188000, 0x55, 0xD0 }, +- { 41375000, 0x54, 0xC0 }, +- { 45125000, 0x53, 0xB0 }, +- { 49688000, 0x52, 0xA0 }, +- { 55188000, 0x51, 0x90 }, +- { 62125000, 0x50, 0x80 }, +- { 66250000, 0x47, 0x78 }, +- { 71000000, 0x46, 0x70 }, +- { 76375000, 0x45, 0x68 }, +- { 82750000, 0x44, 0x60 }, +- { 90250000, 0x43, 0x58 }, +- { 99375000, 0x42, 0x50 }, +- { 110375000, 0x41, 0x48 }, +- { 124250000, 0x40, 0x40 }, +- { 132500000, 0x37, 0x3C }, +- { 142000000, 0x36, 0x38 }, +- { 152750000, 0x35, 0x34 }, +- { 165500000, 0x34, 0x30 }, +- { 180500000, 0x33, 0x2C }, +- { 198750000, 0x32, 0x28 }, +- { 220750000, 0x31, 0x24 }, +- { 248500000, 0x30, 0x20 }, +- { 265000000, 0x27, 0x1E }, +- { 284000000, 0x26, 0x1C }, +- { 305500000, 0x25, 0x1A }, +- { 331000000, 0x24, 0x18 }, +- { 361000000, 0x23, 0x16 }, +- { 397500000, 0x22, 0x14 }, +- { 441500000, 0x21, 0x12 }, +- { 497000000, 0x20, 0x10 }, +- { 530000000, 0x17, 0x0F }, +- { 568000000, 0x16, 0x0E }, +- { 611000000, 0x15, 0x0D }, +- { 662000000, 0x14, 0x0C }, +- { 722000000, 0x13, 0x0B }, +- { 795000000, 0x12, 0x0A }, +- { 883000000, 0x11, 0x09 }, +- { 994000000, 0x10, 0x08 }, +- { 0, 0x00, 0x00 }, /* Table End */ +-}; +- +-static struct SMap2 m_Cal_PLL_Map[] = { +- { 33813000, 0xDD, 0xD0 }, +- { 36625000, 0xDC, 0xC0 }, +- { 39938000, 0xDB, 0xB0 }, +- { 43938000, 0xDA, 0xA0 }, +- { 48813000, 0xD9, 0x90 }, +- { 54938000, 0xD8, 0x80 }, +- { 62813000, 0xD3, 0x70 }, +- { 67625000, 0xCD, 0x68 }, +- { 73250000, 0xCC, 0x60 }, +- { 79875000, 0xCB, 0x58 }, +- { 87875000, 0xCA, 0x50 }, +- { 97625000, 0xC9, 0x48 }, +- { 109875000, 0xC8, 0x40 }, +- { 125625000, 0xC3, 0x38 }, +- { 135250000, 0xBD, 0x34 }, +- { 146500000, 0xBC, 0x30 }, +- { 159750000, 0xBB, 0x2C }, +- { 175750000, 0xBA, 0x28 }, +- { 195250000, 0xB9, 0x24 }, +- { 219750000, 0xB8, 0x20 }, +- { 251250000, 0xB3, 0x1C }, +- { 270500000, 0xAD, 0x1A }, +- { 293000000, 0xAC, 0x18 }, +- { 319500000, 0xAB, 0x16 }, +- { 351500000, 0xAA, 0x14 }, +- { 390500000, 0xA9, 0x12 }, +- { 439500000, 0xA8, 0x10 }, +- { 502500000, 0xA3, 0x0E }, +- { 541000000, 0x9D, 0x0D }, +- { 586000000, 0x9C, 0x0C }, +- { 639000000, 0x9B, 0x0B }, +- { 703000000, 0x9A, 0x0A }, +- { 781000000, 0x99, 0x09 }, +- { 879000000, 0x98, 0x08 }, +- { 0, 0x00, 0x00 }, /* Table End */ +-}; +- +-static struct SMap m_GainTaper_Map[] = { +- { 45400000, 0x1F }, +- { 45800000, 0x1E }, +- { 46200000, 0x1D }, +- { 46700000, 0x1C }, +- { 47100000, 0x1B }, +- { 47500000, 0x1A }, +- { 47900000, 0x19 }, +- { 49600000, 0x17 }, +- { 51200000, 0x16 }, +- { 52900000, 0x15 }, +- { 54500000, 0x14 }, +- { 56200000, 0x13 }, +- { 57800000, 0x12 }, +- { 59500000, 0x11 }, +- { 61100000, 0x10 }, +- { 67600000, 0x0D }, +- { 74200000, 0x0C }, +- { 80700000, 0x0B }, +- { 87200000, 0x0A }, +- { 93800000, 0x09 }, +- { 100300000, 0x08 }, +- { 106900000, 0x07 }, +- { 113400000, 0x06 }, +- { 119900000, 0x05 }, +- { 126500000, 0x04 }, +- { 133000000, 0x03 }, +- { 139500000, 0x02 }, +- { 146100000, 0x01 }, +- { 152600000, 0x00 }, +- { 154300000, 0x1F }, +- { 156100000, 0x1E }, +- { 157800000, 0x1D }, +- { 159500000, 0x1C }, +- { 161200000, 0x1B }, +- { 163000000, 0x1A }, +- { 164700000, 0x19 }, +- { 170200000, 0x17 }, +- { 175800000, 0x16 }, +- { 181300000, 0x15 }, +- { 186900000, 0x14 }, +- { 192400000, 0x13 }, +- { 198000000, 0x12 }, +- { 203500000, 0x11 }, +- { 216200000, 0x14 }, +- { 228900000, 0x13 }, +- { 241600000, 0x12 }, +- { 254400000, 0x11 }, +- { 267100000, 0x10 }, +- { 279800000, 0x0F }, +- { 292500000, 0x0E }, +- { 305200000, 0x0D }, +- { 317900000, 0x0C }, +- { 330700000, 0x0B }, +- { 343400000, 0x0A }, +- { 356100000, 0x09 }, +- { 368800000, 0x08 }, +- { 381500000, 0x07 }, +- { 394200000, 0x06 }, +- { 406900000, 0x05 }, +- { 419700000, 0x04 }, +- { 432400000, 0x03 }, +- { 445100000, 0x02 }, +- { 457800000, 0x01 }, +- { 476300000, 0x19 }, +- { 494800000, 0x18 }, +- { 513300000, 0x17 }, +- { 531800000, 0x16 }, +- { 550300000, 0x15 }, +- { 568900000, 0x14 }, +- { 587400000, 0x13 }, +- { 605900000, 0x12 }, +- { 624400000, 0x11 }, +- { 642900000, 0x10 }, +- { 661400000, 0x0F }, +- { 679900000, 0x0E }, +- { 698400000, 0x0D }, +- { 716900000, 0x0C }, +- { 735400000, 0x0B }, +- { 753900000, 0x0A }, +- { 772500000, 0x09 }, +- { 791000000, 0x08 }, +- { 809500000, 0x07 }, +- { 828000000, 0x06 }, +- { 846500000, 0x05 }, +- { 865000000, 0x04 }, +- { 0, 0x00 }, /* Table End */ +-}; +- +-static struct SMap m_RF_Cal_DC_Over_DT_Map[] = { +- { 47900000, 0x00 }, +- { 55000000, 0x00 }, +- { 61100000, 0x0A }, +- { 64000000, 0x0A }, +- { 82000000, 0x14 }, +- { 84000000, 0x19 }, +- { 119000000, 0x1C }, +- { 124000000, 0x20 }, +- { 129000000, 0x2A }, +- { 134000000, 0x32 }, +- { 139000000, 0x39 }, +- { 144000000, 0x3E }, +- { 149000000, 0x3F }, +- { 152600000, 0x40 }, +- { 154000000, 0x40 }, +- { 164700000, 0x41 }, +- { 203500000, 0x32 }, +- { 353000000, 0x19 }, +- { 356000000, 0x1A }, +- { 359000000, 0x1B }, +- { 363000000, 0x1C }, +- { 366000000, 0x1D }, +- { 369000000, 0x1E }, +- { 373000000, 0x1F }, +- { 376000000, 0x20 }, +- { 379000000, 0x21 }, +- { 383000000, 0x22 }, +- { 386000000, 0x23 }, +- { 389000000, 0x24 }, +- { 393000000, 0x25 }, +- { 396000000, 0x26 }, +- { 399000000, 0x27 }, +- { 402000000, 0x28 }, +- { 404000000, 0x29 }, +- { 407000000, 0x2A }, +- { 409000000, 0x2B }, +- { 412000000, 0x2C }, +- { 414000000, 0x2D }, +- { 417000000, 0x2E }, +- { 419000000, 0x2F }, +- { 422000000, 0x30 }, +- { 424000000, 0x31 }, +- { 427000000, 0x32 }, +- { 429000000, 0x33 }, +- { 432000000, 0x34 }, +- { 434000000, 0x35 }, +- { 437000000, 0x36 }, +- { 439000000, 0x37 }, +- { 442000000, 0x38 }, +- { 444000000, 0x39 }, +- { 447000000, 0x3A }, +- { 449000000, 0x3B }, +- { 457800000, 0x3C }, +- { 465000000, 0x0F }, +- { 477000000, 0x12 }, +- { 483000000, 0x14 }, +- { 502000000, 0x19 }, +- { 508000000, 0x1B }, +- { 519000000, 0x1C }, +- { 522000000, 0x1D }, +- { 524000000, 0x1E }, +- { 534000000, 0x1F }, +- { 549000000, 0x20 }, +- { 554000000, 0x22 }, +- { 584000000, 0x24 }, +- { 589000000, 0x26 }, +- { 658000000, 0x27 }, +- { 664000000, 0x2C }, +- { 669000000, 0x2D }, +- { 699000000, 0x2E }, +- { 704000000, 0x30 }, +- { 709000000, 0x31 }, +- { 714000000, 0x32 }, +- { 724000000, 0x33 }, +- { 729000000, 0x36 }, +- { 739000000, 0x38 }, +- { 744000000, 0x39 }, +- { 749000000, 0x3B }, +- { 754000000, 0x3C }, +- { 759000000, 0x3D }, +- { 764000000, 0x3E }, +- { 769000000, 0x3F }, +- { 774000000, 0x40 }, +- { 779000000, 0x41 }, +- { 784000000, 0x43 }, +- { 789000000, 0x46 }, +- { 794000000, 0x48 }, +- { 799000000, 0x4B }, +- { 804000000, 0x4F }, +- { 809000000, 0x54 }, +- { 814000000, 0x59 }, +- { 819000000, 0x5D }, +- { 824000000, 0x61 }, +- { 829000000, 0x68 }, +- { 834000000, 0x6E }, +- { 839000000, 0x75 }, +- { 844000000, 0x7E }, +- { 849000000, 0x82 }, +- { 854000000, 0x84 }, +- { 859000000, 0x8F }, +- { 865000000, 0x9A }, +- { 0, 0x00 }, /* Table End */ +-}; +- +- +-static struct SMap m_IR_Meas_Map[] = { +- { 200000000, 0x05 }, +- { 400000000, 0x06 }, +- { 865000000, 0x07 }, +- { 0, 0x00 }, /* Table End */ +-}; +- +-static struct SMap2 m_CID_Target_Map[] = { +- { 46000000, 0x04, 18 }, +- { 52200000, 0x0A, 15 }, +- { 70100000, 0x01, 40 }, +- { 136800000, 0x18, 40 }, +- { 156700000, 0x18, 40 }, +- { 186250000, 0x0A, 40 }, +- { 230000000, 0x0A, 40 }, +- { 345000000, 0x18, 40 }, +- { 426000000, 0x0E, 40 }, +- { 489500000, 0x1E, 40 }, +- { 697500000, 0x32, 40 }, +- { 842000000, 0x3A, 40 }, +- { 0, 0x00, 0 }, /* Table End */ +-}; +- +-static struct SRFBandMap m_RF_Band_Map[7] = { +- { 47900000, 46000000, 0, 0}, +- { 61100000, 52200000, 0, 0}, +- { 152600000, 70100000, 136800000, 0}, +- { 164700000, 156700000, 0, 0}, +- { 203500000, 186250000, 0, 0}, +- { 457800000, 230000000, 345000000, 426000000}, +- { 865000000, 489500000, 697500000, 842000000}, +-}; +- +-u8 m_Thermometer_Map_1[16] = { +- 60, 62, 66, 64, +- 74, 72, 68, 70, +- 90, 88, 84, 86, +- 76, 78, 82, 80, +-}; +- +-u8 m_Thermometer_Map_2[16] = { +- 92, 94, 98, 96, +- 106, 104, 100, 102, +- 122, 120, 116, 118, +- 108, 110, 114, 112, +-}; +diff --git a/drivers/media/dvb/frontends/tda665x.c b/drivers/media/dvb/frontends/tda665x.c +deleted file mode 100644 +index 2c1c759..0000000 +--- a/drivers/media/dvb/frontends/tda665x.c ++++ /dev/null +@@ -1,258 +0,0 @@ +-/* +- TDA665x tuner driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "tda665x.h" +- +-struct tda665x_state { +- struct dvb_frontend *fe; +- struct i2c_adapter *i2c; +- const struct tda665x_config *config; +- +- u32 frequency; +- u32 bandwidth; +-}; +- +-static int tda665x_read(struct tda665x_state *state, u8 *buf) +-{ +- const struct tda665x_config *config = state->config; +- int err = 0; +- struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD, .buf = buf, .len = 2 }; +- +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) +- goto exit; +- +- return err; +-exit: +- printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); +- return err; +-} +- +-static int tda665x_write(struct tda665x_state *state, u8 *buf, u8 length) +-{ +- const struct tda665x_config *config = state->config; +- int err = 0; +- struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = length }; +- +- err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) +- goto exit; +- +- return err; +-exit: +- printk(KERN_ERR "%s: I/O Error err=<%d>\n", __func__, err); +- return err; +-} +- +-static int tda665x_get_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *tstate) +-{ +- struct tda665x_state *state = fe->tuner_priv; +- int err = 0; +- +- switch (param) { +- case DVBFE_TUNER_FREQUENCY: +- tstate->frequency = state->frequency; +- break; +- case DVBFE_TUNER_BANDWIDTH: +- break; +- default: +- printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param); +- err = -EINVAL; +- break; +- } +- +- return err; +-} +- +-static int tda665x_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct tda665x_state *state = fe->tuner_priv; +- u8 result = 0; +- int err = 0; +- +- *status = 0; +- +- err = tda665x_read(state, &result); +- if (err < 0) +- goto exit; +- +- if ((result >> 6) & 0x01) { +- printk(KERN_DEBUG "%s: Tuner Phase Locked\n", __func__); +- *status = 1; +- } +- +- return err; +-exit: +- printk(KERN_ERR "%s: I/O Error\n", __func__); +- return err; +-} +- +-static int tda665x_set_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *tstate) +-{ +- struct tda665x_state *state = fe->tuner_priv; +- const struct tda665x_config *config = state->config; +- u32 frequency, status = 0; +- u8 buf[4]; +- int err = 0; +- +- if (param & DVBFE_TUNER_FREQUENCY) { +- +- frequency = tstate->frequency; +- if ((frequency < config->frequency_max) || (frequency > config->frequency_min)) { +- printk(KERN_ERR "%s: Frequency beyond limits, frequency=%d\n", __func__, frequency); +- return -EINVAL; +- } +- +- frequency += config->frequency_offst; +- frequency *= config->ref_multiplier; +- frequency += config->ref_divider >> 1; +- frequency /= config->ref_divider; +- +- buf[0] = (u8) ((frequency & 0x7f00) >> 8); +- buf[1] = (u8) (frequency & 0x00ff) >> 0; +- buf[2] = 0x80 | 0x40 | 0x02; +- buf[3] = 0x00; +- +- /* restore frequency */ +- frequency = tstate->frequency; +- +- if (frequency < 153000000) { +- /* VHF-L */ +- buf[3] |= 0x01; /* fc, Low Band, 47 - 153 MHz */ +- if (frequency < 68000000) +- buf[3] |= 0x40; /* 83uA */ +- if (frequency < 1040000000) +- buf[3] |= 0x60; /* 122uA */ +- if (frequency < 1250000000) +- buf[3] |= 0x80; /* 163uA */ +- else +- buf[3] |= 0xa0; /* 254uA */ +- } else if (frequency < 438000000) { +- /* VHF-H */ +- buf[3] |= 0x02; /* fc, Mid Band, 153 - 438 MHz */ +- if (frequency < 230000000) +- buf[3] |= 0x40; +- if (frequency < 300000000) +- buf[3] |= 0x60; +- else +- buf[3] |= 0x80; +- } else { +- /* UHF */ +- buf[3] |= 0x04; /* fc, High Band, 438 - 862 MHz */ +- if (frequency < 470000000) +- buf[3] |= 0x60; +- if (frequency < 526000000) +- buf[3] |= 0x80; +- else +- buf[3] |= 0xa0; +- } +- +- /* Set params */ +- err = tda665x_write(state, buf, 5); +- if (err < 0) +- goto exit; +- +- /* sleep for some time */ +- printk(KERN_DEBUG "%s: Waiting to Phase LOCK\n", __func__); +- msleep(20); +- /* check status */ +- err = tda665x_get_status(fe, &status); +- if (err < 0) +- goto exit; +- +- if (status == 1) { +- printk(KERN_DEBUG "%s: Tuner Phase locked: status=%d\n", __func__, status); +- state->frequency = frequency; /* cache successful state */ +- } else { +- printk(KERN_ERR "%s: No Phase lock: status=%d\n", __func__, status); +- } +- } else { +- printk(KERN_ERR "%s: Unknown parameter (param=%d)\n", __func__, param); +- return -EINVAL; +- } +- +- return 0; +-exit: +- printk(KERN_ERR "%s: I/O Error\n", __func__); +- return err; +-} +- +-static int tda665x_release(struct dvb_frontend *fe) +-{ +- struct tda665x_state *state = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(state); +- return 0; +-} +- +-static struct dvb_tuner_ops tda665x_ops = { +- +- .set_state = tda665x_set_state, +- .get_state = tda665x_get_state, +- .get_status = tda665x_get_status, +- .release = tda665x_release +-}; +- +-struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, +- const struct tda665x_config *config, +- struct i2c_adapter *i2c) +-{ +- struct tda665x_state *state = NULL; +- struct dvb_tuner_info *info; +- +- state = kzalloc(sizeof(struct tda665x_state), GFP_KERNEL); +- if (state == NULL) +- goto exit; +- +- state->config = config; +- state->i2c = i2c; +- state->fe = fe; +- fe->tuner_priv = state; +- fe->ops.tuner_ops = tda665x_ops; +- info = &fe->ops.tuner_ops.info; +- +- memcpy(info->name, config->name, sizeof(config->name)); +- info->frequency_min = config->frequency_min; +- info->frequency_max = config->frequency_max; +- info->frequency_step = config->frequency_offst; +- +- printk(KERN_DEBUG "%s: Attaching TDA665x (%s) tuner\n", __func__, info->name); +- +- return fe; +- +-exit: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(tda665x_attach); +- +-MODULE_DESCRIPTION("TDA665x driver"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda665x.h b/drivers/media/dvb/frontends/tda665x.h +deleted file mode 100644 +index ec7927a..0000000 +--- a/drivers/media/dvb/frontends/tda665x.h ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* +- TDA665x tuner driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA665x_H +-#define __TDA665x_H +- +-struct tda665x_config { +- char name[128]; +- +- u8 addr; +- u32 frequency_min; +- u32 frequency_max; +- u32 frequency_offst; +- u32 ref_multiplier; +- u32 ref_divider; +-}; +- +-#if defined(CONFIG_DVB_TDA665x) || (defined(CONFIG_DVB_TDA665x_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, +- const struct tda665x_config *config, +- struct i2c_adapter *i2c); +- +-#else +- +-static inline struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, +- const struct tda665x_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif /* CONFIG_DVB_TDA665x */ +- +-#endif /* __TDA665x_H */ +diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c +deleted file mode 100644 +index 15912c9..0000000 +--- a/drivers/media/dvb/frontends/tda8083.c ++++ /dev/null +@@ -1,487 +0,0 @@ +-/* +- Driver for Philips TDA8083 based QPSK Demodulator +- +- Copyright (C) 2001 Convergence Integrated Media GmbH +- +- written by Ralph Metzler +- +- adoption to the new DVB frontend API and diagnostic ioctl's +- by Holger Waechtler +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "tda8083.h" +- +- +-struct tda8083_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct tda8083_config* config; +- struct dvb_frontend frontend; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "tda8083: " args); \ +- } while (0) +- +- +-static u8 tda8083_init_tab [] = { +- 0x04, 0x00, 0x4a, 0x79, 0x04, 0x00, 0xff, 0xea, +- 0x48, 0x42, 0x79, 0x60, 0x70, 0x52, 0x9a, 0x10, +- 0x0e, 0x10, 0xf2, 0xa7, 0x93, 0x0b, 0x05, 0xc8, +- 0x9d, 0x00, 0x42, 0x80, 0x00, 0x60, 0x40, 0x00, +- 0x00, 0x75, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00 +-}; +- +- +-static int tda8083_writereg (struct tda8083_state* state, u8 reg, u8 data) +-{ +- int ret; +- u8 buf [] = { reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- dprintk ("%s: writereg error (reg %02x, ret == %i)\n", +- __func__, reg, ret); +- +- return (ret != 1) ? -1 : 0; +-} +- +-static int tda8083_readregs (struct tda8083_state* state, u8 reg1, u8 *b, u8 len) +-{ +- int ret; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = ®1, .len = 1 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = len } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- dprintk ("%s: readreg error (reg %02x, ret == %i)\n", +- __func__, reg1, ret); +- +- return ret == 2 ? 0 : -1; +-} +- +-static inline u8 tda8083_readreg (struct tda8083_state* state, u8 reg) +-{ +- u8 val; +- +- tda8083_readregs (state, reg, &val, 1); +- +- return val; +-} +- +-static int tda8083_set_inversion (struct tda8083_state* state, fe_spectral_inversion_t inversion) +-{ +- /* XXX FIXME: implement other modes than FEC_AUTO */ +- if (inversion == INVERSION_AUTO) +- return 0; +- +- return -EINVAL; +-} +- +-static int tda8083_set_fec (struct tda8083_state* state, fe_code_rate_t fec) +-{ +- if (fec == FEC_AUTO) +- return tda8083_writereg (state, 0x07, 0xff); +- +- if (fec >= FEC_1_2 && fec <= FEC_8_9) +- return tda8083_writereg (state, 0x07, 1 << (FEC_8_9 - fec)); +- +- return -EINVAL; +-} +- +-static fe_code_rate_t tda8083_get_fec (struct tda8083_state* state) +-{ +- u8 index; +- static fe_code_rate_t fec_tab [] = { FEC_8_9, FEC_1_2, FEC_2_3, FEC_3_4, +- FEC_4_5, FEC_5_6, FEC_6_7, FEC_7_8 }; +- +- index = tda8083_readreg(state, 0x0e) & 0x07; +- +- return fec_tab [index]; +-} +- +-static int tda8083_set_symbolrate (struct tda8083_state* state, u32 srate) +-{ +- u32 ratio; +- u32 tmp; +- u8 filter; +- +- if (srate > 32000000) +- srate = 32000000; +- if (srate < 500000) +- srate = 500000; +- +- filter = 0; +- if (srate < 24000000) +- filter = 2; +- if (srate < 16000000) +- filter = 3; +- +- tmp = 31250 << 16; +- ratio = tmp / srate; +- +- tmp = (tmp % srate) << 8; +- ratio = (ratio << 8) + tmp / srate; +- +- tmp = (tmp % srate) << 8; +- ratio = (ratio << 8) + tmp / srate; +- +- dprintk("tda8083: ratio == %08x\n", (unsigned int) ratio); +- +- tda8083_writereg (state, 0x05, filter); +- tda8083_writereg (state, 0x02, (ratio >> 16) & 0xff); +- tda8083_writereg (state, 0x03, (ratio >> 8) & 0xff); +- tda8083_writereg (state, 0x04, (ratio ) & 0xff); +- +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 1; +-} +- +-static void tda8083_wait_diseqc_fifo (struct tda8083_state* state, int timeout) +-{ +- unsigned long start = jiffies; +- +- while (jiffies - start < timeout && +- !(tda8083_readreg(state, 0x02) & 0x80)) +- { +- msleep(50); +- }; +-} +- +-static int tda8083_set_tone (struct tda8083_state* state, fe_sec_tone_mode_t tone) +-{ +- tda8083_writereg (state, 0x26, 0xf1); +- +- switch (tone) { +- case SEC_TONE_OFF: +- return tda8083_writereg (state, 0x29, 0x00); +- case SEC_TONE_ON: +- return tda8083_writereg (state, 0x29, 0x80); +- default: +- return -EINVAL; +- }; +-} +- +-static int tda8083_set_voltage (struct tda8083_state* state, fe_sec_voltage_t voltage) +-{ +- switch (voltage) { +- case SEC_VOLTAGE_13: +- return tda8083_writereg (state, 0x20, 0x00); +- case SEC_VOLTAGE_18: +- return tda8083_writereg (state, 0x20, 0x11); +- default: +- return -EINVAL; +- }; +-} +- +-static int tda8083_send_diseqc_burst (struct tda8083_state* state, fe_sec_mini_cmd_t burst) +-{ +- switch (burst) { +- case SEC_MINI_A: +- tda8083_writereg (state, 0x29, (5 << 2)); /* send burst A */ +- break; +- case SEC_MINI_B: +- tda8083_writereg (state, 0x29, (7 << 2)); /* send B */ +- break; +- default: +- return -EINVAL; +- }; +- +- tda8083_wait_diseqc_fifo (state, 100); +- +- return 0; +-} +- +-static int tda8083_send_diseqc_msg (struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd *m) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- int i; +- +- tda8083_writereg (state, 0x29, (m->msg_len - 3) | (1 << 2)); /* enable */ +- +- for (i=0; imsg_len; i++) +- tda8083_writereg (state, 0x23 + i, m->msg[i]); +- +- tda8083_writereg (state, 0x29, (m->msg_len - 3) | (3 << 2)); /* send!! */ +- +- tda8083_wait_diseqc_fifo (state, 100); +- +- return 0; +-} +- +-static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- u8 signal = ~tda8083_readreg (state, 0x01); +- u8 sync = tda8083_readreg (state, 0x02); +- +- *status = 0; +- +- if (signal > 10) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 0x01) +- *status |= FE_HAS_CARRIER; +- +- if (sync & 0x02) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 0x10) +- *status |= FE_HAS_SYNC; +- +- if (sync & 0x20) /* frontend can not lock */ +- *status |= FE_TIMEDOUT; +- +- if ((sync & 0x1f) == 0x1f) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- int ret; +- u8 buf[3]; +- +- if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf)))) +- return ret; +- +- *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; +- +- return 0; +-} +- +-static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- u8 signal = ~tda8083_readreg (state, 0x01); +- *strength = (signal << 8) | signal; +- +- return 0; +-} +- +-static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- u8 _snr = tda8083_readreg (state, 0x08); +- *snr = (_snr << 8) | _snr; +- +- return 0; +-} +- +-static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- *ucblocks = tda8083_readreg(state, 0x0f); +- if (*ucblocks == 0xff) +- *ucblocks = 0xffffffff; +- +- return 0; +-} +- +-static int tda8083_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda8083_state* state = fe->demodulator_priv; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- tda8083_set_inversion (state, p->inversion); +- tda8083_set_fec(state, p->fec_inner); +- tda8083_set_symbolrate(state, p->symbol_rate); +- +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 0; +-} +- +-static int tda8083_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda8083_state* state = fe->demodulator_priv; +- +- /* FIXME: get symbolrate & frequency offset...*/ +- /*p->frequency = ???;*/ +- p->inversion = (tda8083_readreg (state, 0x0e) & 0x80) ? +- INVERSION_ON : INVERSION_OFF; +- p->fec_inner = tda8083_get_fec(state); +- /*p->symbol_rate = tda8083_get_symbolrate (state);*/ +- +- return 0; +-} +- +-static int tda8083_sleep(struct dvb_frontend* fe) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- tda8083_writereg (state, 0x00, 0x02); +- return 0; +-} +- +-static int tda8083_init(struct dvb_frontend* fe) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- int i; +- +- for (i=0; i<44; i++) +- tda8083_writereg (state, i, tda8083_init_tab[i]); +- +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 0; +-} +- +-static int tda8083_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t burst) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- tda8083_send_diseqc_burst (state, burst); +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 0; +-} +- +-static int tda8083_diseqc_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- tda8083_set_tone (state, tone); +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 0; +-} +- +-static int tda8083_diseqc_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- +- tda8083_set_voltage (state, voltage); +- tda8083_writereg (state, 0x00, 0x3c); +- tda8083_writereg (state, 0x00, 0x04); +- +- return 0; +-} +- +-static void tda8083_release(struct dvb_frontend* fe) +-{ +- struct tda8083_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops tda8083_ops; +- +-struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, +- struct i2c_adapter* i2c) +-{ +- struct tda8083_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- +- /* check if the demod is there */ +- if ((tda8083_readreg(state, 0x00)) != 0x05) goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &tda8083_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops tda8083_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "Philips TDA8083 DVB-S", +- .frequency_min = 920000, /* TDA8060 */ +- .frequency_max = 2200000, /* TDA8060 */ +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- /* .frequency_tolerance = ???,*/ +- .symbol_rate_min = 12000000, +- .symbol_rate_max = 30000000, +- /* .symbol_rate_tolerance = ???,*/ +- .caps = FE_CAN_INVERSION_AUTO | +- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | +- FE_CAN_FEC_7_8 | FE_CAN_FEC_8_9 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_MUTE_TS +- }, +- +- .release = tda8083_release, +- +- .init = tda8083_init, +- .sleep = tda8083_sleep, +- +- .set_frontend = tda8083_set_frontend, +- .get_frontend = tda8083_get_frontend, +- +- .read_status = tda8083_read_status, +- .read_signal_strength = tda8083_read_signal_strength, +- .read_snr = tda8083_read_snr, +- .read_ber = tda8083_read_ber, +- .read_ucblocks = tda8083_read_ucblocks, +- +- .diseqc_send_master_cmd = tda8083_send_diseqc_msg, +- .diseqc_send_burst = tda8083_diseqc_send_burst, +- .set_tone = tda8083_diseqc_set_tone, +- .set_voltage = tda8083_diseqc_set_voltage, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); +-MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(tda8083_attach); +diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h +deleted file mode 100644 +index 5a03c14..0000000 +--- a/drivers/media/dvb/frontends/tda8083.h ++++ /dev/null +@@ -1,50 +0,0 @@ +-/* +- Driver for Grundig 29504-491, a Philips TDA8083 based QPSK Frontend +- +- Copyright (C) 2001 Convergence Integrated Media GmbH +- +- written by Ralph Metzler +- +- adoption to the new DVB frontend API and diagnostic ioctl's +- by Holger Waechtler +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef TDA8083_H +-#define TDA8083_H +- +-#include +- +-struct tda8083_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +-}; +- +-#if defined(CONFIG_DVB_TDA8083) || (defined(CONFIG_DVB_TDA8083_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TDA8083 +- +-#endif // TDA8083_H +diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c +deleted file mode 100644 +index 53c7d8f..0000000 +--- a/drivers/media/dvb/frontends/tda8261.c ++++ /dev/null +@@ -1,230 +0,0 @@ +-/* +- TDA8261 8PSK/QPSK tuner driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +- +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "tda8261.h" +- +-struct tda8261_state { +- struct dvb_frontend *fe; +- struct i2c_adapter *i2c; +- const struct tda8261_config *config; +- +- /* state cache */ +- u32 frequency; +- u32 bandwidth; +-}; +- +-static int tda8261_read(struct tda8261_state *state, u8 *buf) +-{ +- const struct tda8261_config *config = state->config; +- int err = 0; +- struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 }; +- +- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) +- printk("%s: read error, err=%d\n", __func__, err); +- +- return err; +-} +- +-static int tda8261_write(struct tda8261_state *state, u8 *buf) +-{ +- const struct tda8261_config *config = state->config; +- int err = 0; +- struct i2c_msg msg = { .addr = config->addr, .flags = 0, .buf = buf, .len = 4 }; +- +- if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) +- printk("%s: write error, err=%d\n", __func__, err); +- +- return err; +-} +- +-static int tda8261_get_status(struct dvb_frontend *fe, u32 *status) +-{ +- struct tda8261_state *state = fe->tuner_priv; +- u8 result = 0; +- int err = 0; +- +- *status = 0; +- +- if ((err = tda8261_read(state, &result)) < 0) { +- printk("%s: I/O Error\n", __func__); +- return err; +- } +- if ((result >> 6) & 0x01) { +- printk("%s: Tuner Phase Locked\n", __func__); +- *status = 1; +- } +- +- return err; +-} +- +-static const u32 div_tab[] = { 2000, 1000, 500, 250, 125 }; /* kHz */ +-static const u8 ref_div[] = { 0x00, 0x01, 0x02, 0x05, 0x07 }; +- +-static int tda8261_get_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *tstate) +-{ +- struct tda8261_state *state = fe->tuner_priv; +- int err = 0; +- +- switch (param) { +- case DVBFE_TUNER_FREQUENCY: +- tstate->frequency = state->frequency; +- break; +- case DVBFE_TUNER_BANDWIDTH: +- tstate->bandwidth = 40000000; /* FIXME! need to calculate Bandwidth */ +- break; +- default: +- printk("%s: Unknown parameter (param=%d)\n", __func__, param); +- err = -EINVAL; +- break; +- } +- +- return err; +-} +- +-static int tda8261_set_state(struct dvb_frontend *fe, +- enum tuner_param param, +- struct tuner_state *tstate) +-{ +- struct tda8261_state *state = fe->tuner_priv; +- const struct tda8261_config *config = state->config; +- u32 frequency, N, status = 0; +- u8 buf[4]; +- int err = 0; +- +- if (param & DVBFE_TUNER_FREQUENCY) { +- /** +- * N = Max VCO Frequency / Channel Spacing +- * Max VCO Frequency = VCO frequency + (channel spacing - 1) +- * (to account for half channel spacing on either side) +- */ +- frequency = tstate->frequency; +- if ((frequency < 950000) || (frequency > 2150000)) { +- printk("%s: Frequency beyond limits, frequency=%d\n", __func__, frequency); +- return -EINVAL; +- } +- N = (frequency + (div_tab[config->step_size] - 1)) / div_tab[config->step_size]; +- printk("%s: Step size=%d, Divider=%d, PG=0x%02x (%d)\n", +- __func__, config->step_size, div_tab[config->step_size], N, N); +- +- buf[0] = (N >> 8) & 0xff; +- buf[1] = N & 0xff; +- buf[2] = (0x01 << 7) | ((ref_div[config->step_size] & 0x07) << 1); +- +- if (frequency < 1450000) +- buf[3] = 0x00; +- else if (frequency < 2000000) +- buf[3] = 0x40; +- else if (frequency < 2150000) +- buf[3] = 0x80; +- +- /* Set params */ +- if ((err = tda8261_write(state, buf)) < 0) { +- printk("%s: I/O Error\n", __func__); +- return err; +- } +- /* sleep for some time */ +- printk("%s: Waiting to Phase LOCK\n", __func__); +- msleep(20); +- /* check status */ +- if ((err = tda8261_get_status(fe, &status)) < 0) { +- printk("%s: I/O Error\n", __func__); +- return err; +- } +- if (status == 1) { +- printk("%s: Tuner Phase locked: status=%d\n", __func__, status); +- state->frequency = frequency; /* cache successful state */ +- } else { +- printk("%s: No Phase lock: status=%d\n", __func__, status); +- } +- } else { +- printk("%s: Unknown parameter (param=%d)\n", __func__, param); +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int tda8261_release(struct dvb_frontend *fe) +-{ +- struct tda8261_state *state = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(state); +- return 0; +-} +- +-static struct dvb_tuner_ops tda8261_ops = { +- +- .info = { +- .name = "TDA8261", +-// .tuner_name = NULL, +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 0 +- }, +- +- .set_state = tda8261_set_state, +- .get_state = tda8261_get_state, +- .get_status = tda8261_get_status, +- .release = tda8261_release +-}; +- +-struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, +- const struct tda8261_config *config, +- struct i2c_adapter *i2c) +-{ +- struct tda8261_state *state = NULL; +- +- if ((state = kzalloc(sizeof (struct tda8261_state), GFP_KERNEL)) == NULL) +- goto exit; +- +- state->config = config; +- state->i2c = i2c; +- state->fe = fe; +- fe->tuner_priv = state; +- fe->ops.tuner_ops = tda8261_ops; +- +- fe->ops.tuner_ops.info.frequency_step = div_tab[config->step_size]; +-// fe->ops.tuner_ops.tuner_name = &config->buf; +- +-// printk("%s: Attaching %s TDA8261 8PSK/QPSK tuner\n", +-// __func__, fe->ops.tuner_ops.tuner_name); +- printk("%s: Attaching TDA8261 8PSK/QPSK tuner\n", __func__); +- +- return fe; +- +-exit: +- kfree(state); +- return NULL; +-} +- +-EXPORT_SYMBOL(tda8261_attach); +- +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda8261.h b/drivers/media/dvb/frontends/tda8261.h +deleted file mode 100644 +index 006e453..0000000 +--- a/drivers/media/dvb/frontends/tda8261.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- TDA8261 8PSK/QPSK tuner driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __TDA8261_H +-#define __TDA8261_H +- +-enum tda8261_step { +- TDA8261_STEP_2000 = 0, /* 2000 kHz */ +- TDA8261_STEP_1000, /* 1000 kHz */ +- TDA8261_STEP_500, /* 500 kHz */ +- TDA8261_STEP_250, /* 250 kHz */ +- TDA8261_STEP_125 /* 125 kHz */ +-}; +- +-struct tda8261_config { +-// u8 buf[16]; +- u8 addr; +- enum tda8261_step step_size; +-}; +- +-#if defined(CONFIG_DVB_TDA8261) || (defined(CONFIG_DVB_TDA8261_MODULE) && defined(MODULE)) +- +-extern struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, +- const struct tda8261_config *config, +- struct i2c_adapter *i2c); +- +-#else +- +-static inline struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, +- const struct tda8261_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: Driver disabled by Kconfig\n", __func__); +- return NULL; +-} +- +-#endif //CONFIG_DVB_TDA8261 +- +-#endif// __TDA8261_H +diff --git a/drivers/media/dvb/frontends/tda8261_cfg.h b/drivers/media/dvb/frontends/tda8261_cfg.h +deleted file mode 100644 +index 1af1ee4..0000000 +--- a/drivers/media/dvb/frontends/tda8261_cfg.h ++++ /dev/null +@@ -1,84 +0,0 @@ +-/* +- TDA8261 8PSK/QPSK tuner driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- 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. +-*/ +- +-static int tda8261_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state t_state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- *frequency = t_state.frequency; +- printk("%s: Frequency=%d\n", __func__, t_state.frequency); +- } +- return 0; +-} +- +-static int tda8261_set_frequency(struct dvb_frontend *fe, u32 frequency) +-{ +- struct dvb_frontend_ops *frontend_ops = NULL; +- struct dvb_tuner_ops *tuner_ops = NULL; +- struct tuner_state t_state; +- int err = 0; +- +- t_state.frequency = frequency; +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->set_state) { +- if ((err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- } +- printk("%s: Frequency=%d\n", __func__, t_state.frequency); +- return 0; +-} +- +-static int tda8261_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +-{ +- struct dvb_frontend_ops *frontend_ops = &fe->ops; +- struct dvb_tuner_ops *tuner_ops = &frontend_ops->tuner_ops; +- struct tuner_state t_state; +- int err = 0; +- +- if (&fe->ops) +- frontend_ops = &fe->ops; +- if (&frontend_ops->tuner_ops) +- tuner_ops = &frontend_ops->tuner_ops; +- if (tuner_ops->get_state) { +- if ((err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &t_state)) < 0) { +- printk("%s: Invalid parameter\n", __func__); +- return err; +- } +- *bandwidth = t_state.bandwidth; +- } +- printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth); +- return 0; +-} +diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c +deleted file mode 100644 +index 04bbcc2..0000000 +--- a/drivers/media/dvb/frontends/tda826x.c ++++ /dev/null +@@ -1,188 +0,0 @@ +- /* +- Driver for Philips tda8262/tda8263 DVBS Silicon tuners +- +- (c) 2006 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#include +-#include +-#include +-#include +- +-#include "tda826x.h" +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "tda826x: " args); \ +- } while (0) +- +-struct tda826x_priv { +- /* i2c details */ +- int i2c_address; +- struct i2c_adapter *i2c; +- u8 has_loopthrough:1; +- u32 frequency; +-}; +- +-static int tda826x_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int tda826x_sleep(struct dvb_frontend *fe) +-{ +- struct tda826x_priv *priv = fe->tuner_priv; +- int ret; +- u8 buf [] = { 0x00, 0x8d }; +- struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 }; +- +- dprintk("%s:\n", __func__); +- +- if (!priv->has_loopthrough) +- buf[1] = 0xad; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { +- dprintk("%s: i2c error\n", __func__); +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return (ret == 1) ? 0 : ret; +-} +- +-static int tda826x_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct tda826x_priv *priv = fe->tuner_priv; +- int ret; +- u32 div; +- u32 ksyms; +- u32 bandwidth; +- u8 buf [11]; +- struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 }; +- +- dprintk("%s:\n", __func__); +- +- div = (p->frequency + (1000-1)) / 1000; +- +- /* BW = ((1 + RO) * SR/2 + 5) * 1.3 [SR in MSPS, BW in MHz] */ +- /* with R0 = 0.35 and some transformations: */ +- ksyms = p->symbol_rate / 1000; +- bandwidth = (878 * ksyms + 6500000) / 1000000 + 1; +- if (bandwidth < 5) +- bandwidth = 5; +- else if (bandwidth > 36) +- bandwidth = 36; +- +- buf[0] = 0x00; // subaddress +- buf[1] = 0x09; // powerdown RSSI + the magic value 1 +- if (!priv->has_loopthrough) +- buf[1] |= 0x20; // power down loopthrough if not needed +- buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO +- buf[3] = div >> 7; +- buf[4] = div << 1; +- buf[5] = ((bandwidth - 5) << 3) | 7; /* baseband cut-off */ +- buf[6] = 0xfe; // baseband gain 9 db + no RF attenuation +- buf[7] = 0x83; // charge pumps at high, tests off +- buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports. +- buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL +- buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { +- dprintk("%s: i2c error\n", __func__); +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- priv->frequency = div * 1000; +- +- return (ret == 1) ? 0 : ret; +-} +- +-static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tda826x_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static struct dvb_tuner_ops tda826x_tuner_ops = { +- .info = { +- .name = "Philips TDA826X", +- .frequency_min = 950000, +- .frequency_max = 2175000 +- }, +- .release = tda826x_release, +- .sleep = tda826x_sleep, +- .set_params = tda826x_set_params, +- .get_frequency = tda826x_get_frequency, +-}; +- +-struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough) +-{ +- struct tda826x_priv *priv = NULL; +- u8 b1 [] = { 0, 0 }; +- struct i2c_msg msg[2] = { +- { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, +- { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } +- }; +- int ret; +- +- dprintk("%s:\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer (i2c, msg, 2); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret != 2) +- return NULL; +- if (!(b1[1] & 0x80)) +- return NULL; +- +- priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->i2c_address = addr; +- priv->i2c = i2c; +- priv->has_loopthrough = has_loopthrough; +- +- memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops)); +- +- fe->tuner_priv = priv; +- +- return fe; +-} +-EXPORT_SYMBOL(tda826x_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-MODULE_DESCRIPTION("DVB TDA826x driver"); +-MODULE_AUTHOR("Andrew de Quincey"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tda826x.h b/drivers/media/dvb/frontends/tda826x.h +deleted file mode 100644 +index 89e9792..0000000 +--- a/drivers/media/dvb/frontends/tda826x.h ++++ /dev/null +@@ -1,53 +0,0 @@ +- /* +- Driver for Philips tda8262/tda8263 DVBS Silicon tuners +- +- (c) 2006 Andrew de Quincey +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +- */ +- +-#ifndef __DVB_TDA826X_H__ +-#define __DVB_TDA826X_H__ +- +-#include +-#include "dvb_frontend.h" +- +-/** +- * Attach a tda826x tuner to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param addr i2c address of the tuner. +- * @param i2c i2c adapter to use. +- * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector. +- * @return FE pointer on success, NULL on failure. +- */ +-#if defined(CONFIG_DVB_TDA826X) || (defined(CONFIG_DVB_TDA826X_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, int addr, +- struct i2c_adapter *i2c, +- int has_loopthrough); +-#else +-static inline struct dvb_frontend* tda826x_attach(struct dvb_frontend *fe, +- int addr, +- struct i2c_adapter *i2c, +- int has_loopthrough) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TDA826X +- +-#endif // __DVB_TDA826X_H__ +diff --git a/drivers/media/dvb/frontends/tdhd1.h b/drivers/media/dvb/frontends/tdhd1.h +deleted file mode 100644 +index 1775098..0000000 +--- a/drivers/media/dvb/frontends/tdhd1.h ++++ /dev/null +@@ -1,74 +0,0 @@ +-/* +- * tdhd1.h - ALPS TDHD1-204A tuner support +- * +- * Copyright (C) 2008 Oliver Endriss +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * The project's page is at http://www.linuxtv.org +- */ +- +-#ifndef TDHD1_H +-#define TDHD1_H +- +-#include "tda1004x.h" +- +-static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name); +- +-static struct tda1004x_config alps_tdhd1_204a_config = { +- .demod_address = 0x8, +- .invert = 1, +- .invert_oclk = 0, +- .xtal_freq = TDA10046_XTAL_4M, +- .agc_config = TDA10046_AGC_DEFAULT, +- .if_freq = TDA10046_FREQ_3617, +- .request_firmware = alps_tdhd1_204_request_firmware +-}; +- +-static int alps_tdhd1_204a_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct i2c_adapter *i2c = fe->tuner_priv; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- u32 div; +- +- div = (p->frequency + 36166666) / 166666; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x85; +- +- if (p->frequency >= 174000000 && p->frequency <= 230000000) +- data[3] = 0x02; +- else if (p->frequency >= 470000000 && p->frequency <= 823000000) +- data[3] = 0x0C; +- else if (p->frequency > 823000000 && p->frequency <= 862000000) +- data[3] = 0x8C; +- else +- return -EINVAL; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(i2c, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-#endif /* TDHD1_H */ +diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c +deleted file mode 100644 +index 029384d..0000000 +--- a/drivers/media/dvb/frontends/tua6100.c ++++ /dev/null +@@ -1,206 +0,0 @@ +-/** +- * Driver for Infineon tua6100 pll. +- * +- * (c) 2006 Andrew de Quincey +- * +- * Based on code found in budget-av.c, which has the following: +- * Compiled from various sources by Michael Hunold +- * +- * CI interface support (c) 2004 Olivier Gournet & +- * Andrew de Quincey +- * +- * Copyright (C) 2002 Ralph Metzler +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +- +-#include "tua6100.h" +- +-struct tua6100_priv { +- /* i2c details */ +- int i2c_address; +- struct i2c_adapter *i2c; +- u32 frequency; +-}; +- +-static int tua6100_release(struct dvb_frontend *fe) +-{ +- kfree(fe->tuner_priv); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static int tua6100_sleep(struct dvb_frontend *fe) +-{ +- struct tua6100_priv *priv = fe->tuner_priv; +- int ret; +- u8 reg0[] = { 0x00, 0x00 }; +- struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { +- printk("%s: i2c error\n", __func__); +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return (ret == 1) ? 0 : ret; +-} +- +-static int tua6100_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct tua6100_priv *priv = fe->tuner_priv; +- u32 div; +- u32 prediv; +- u8 reg0[] = { 0x00, 0x00 }; +- u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 }; +- u8 reg2[] = { 0x02, 0x00, 0x00 }; +- struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; +- struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 }; +- struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 }; +- +-#define _R 4 +-#define _P 32 +-#define _ri 4000000 +- +- // setup register 0 +- if (c->frequency < 2000000) +- reg0[1] = 0x03; +- else +- reg0[1] = 0x07; +- +- // setup register 1 +- if (c->frequency < 1630000) +- reg1[1] = 0x2c; +- else +- reg1[1] = 0x0c; +- +- if (_P == 64) +- reg1[1] |= 0x40; +- if (c->frequency >= 1525000) +- reg1[1] |= 0x80; +- +- // register 2 +- reg2[1] = (_R >> 8) & 0x03; +- reg2[2] = _R; +- if (c->frequency < 1455000) +- reg2[1] |= 0x1c; +- else if (c->frequency < 1630000) +- reg2[1] |= 0x0c; +- else +- reg2[1] |= 0x1c; +- +- /* +- * The N divisor ratio (note: c->frequency is in kHz, but we +- * need it in Hz) +- */ +- prediv = (c->frequency * _R) / (_ri / 1000); +- div = prediv / _P; +- reg1[1] |= (div >> 9) & 0x03; +- reg1[2] = div >> 1; +- reg1[3] = (div << 7); +- priv->frequency = ((div * _P) * (_ri / 1000)) / _R; +- +- // Finally, calculate and store the value for A +- reg1[3] |= (prediv - (div*_P)) & 0x7f; +- +-#undef _R +-#undef _P +-#undef _ri +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(priv->i2c, &msg0, 1) != 1) +- return -EIO; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(priv->i2c, &msg2, 1) != 1) +- return -EIO; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(priv->i2c, &msg1, 1) != 1) +- return -EIO; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct tua6100_priv *priv = fe->tuner_priv; +- *frequency = priv->frequency; +- return 0; +-} +- +-static struct dvb_tuner_ops tua6100_tuner_ops = { +- .info = { +- .name = "Infineon TUA6100", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_step = 1000, +- }, +- .release = tua6100_release, +- .sleep = tua6100_sleep, +- .set_params = tua6100_set_params, +- .get_frequency = tua6100_get_frequency, +-}; +- +-struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +-{ +- struct tua6100_priv *priv = NULL; +- u8 b1 [] = { 0x80 }; +- u8 b2 [] = { 0x00 }; +- struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 }, +- { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } }; +- int ret; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer (i2c, msg, 2); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- if (ret != 2) +- return NULL; +- +- priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL); +- if (priv == NULL) +- return NULL; +- +- priv->i2c_address = addr; +- priv->i2c = i2c; +- +- memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops)); +- fe->tuner_priv = priv; +- return fe; +-} +-EXPORT_SYMBOL(tua6100_attach); +- +-MODULE_DESCRIPTION("DVB tua6100 driver"); +-MODULE_AUTHOR("Andrew de Quincey"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/tua6100.h b/drivers/media/dvb/frontends/tua6100.h +deleted file mode 100644 +index f83dbd5..0000000 +--- a/drivers/media/dvb/frontends/tua6100.h ++++ /dev/null +@@ -1,47 +0,0 @@ +-/** +- * Driver for Infineon tua6100 PLL. +- * +- * (c) 2006 Andrew de Quincey +- * +- * Based on code found in budget-av.c, which has the following: +- * Compiled from various sources by Michael Hunold +- * +- * CI interface support (c) 2004 Olivier Gournet & +- * Andrew de Quincey +- * +- * Copyright (C) 2002 Ralph Metzler +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef __DVB_TUA6100_H__ +-#define __DVB_TUA6100_H__ +- +-#include +-#include "dvb_frontend.h" +- +-#if defined(CONFIG_DVB_TUA6100) || (defined(CONFIG_DVB_TUA6100_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_TUA6100 +- +-#endif +diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c +deleted file mode 100644 +index bb42b56..0000000 +--- a/drivers/media/dvb/frontends/ves1820.c ++++ /dev/null +@@ -1,447 +0,0 @@ +-/* +- VES1820 - Single Chip Cable Channel Receiver driver module +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "ves1820.h" +- +- +- +-struct ves1820_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct ves1820_config* config; +- struct dvb_frontend frontend; +- +- /* private demodulator data */ +- u8 reg0; +- u8 pwm; +-}; +- +- +-static int verbose; +- +-static u8 ves1820_inittab[] = { +- 0x69, 0x6A, 0x93, 0x1A, 0x12, 0x46, 0x26, 0x1A, +- 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20, +- 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, +- 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x40 +-}; +- +-static int ves1820_writereg(struct ves1820_state *state, u8 reg, u8 data) +-{ +- u8 buf[] = { 0x00, reg, data }; +- struct i2c_msg msg = {.addr = state->config->demod_address,.flags = 0,.buf = buf,.len = 3 }; +- int ret; +- +- ret = i2c_transfer(state->i2c, &msg, 1); +- +- if (ret != 1) +- printk("ves1820: %s(): writereg error (reg == 0x%02x, " +- "val == 0x%02x, ret == %i)\n", __func__, reg, data, ret); +- +- return (ret != 1) ? -EREMOTEIO : 0; +-} +- +-static u8 ves1820_readreg(struct ves1820_state *state, u8 reg) +-{ +- u8 b0[] = { 0x00, reg }; +- u8 b1[] = { 0 }; +- struct i2c_msg msg[] = { +- {.addr = state->config->demod_address,.flags = 0,.buf = b0,.len = 2}, +- {.addr = state->config->demod_address,.flags = I2C_M_RD,.buf = b1,.len = 1} +- }; +- int ret; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) +- printk("ves1820: %s(): readreg error (reg == 0x%02x, " +- "ret == %i)\n", __func__, reg, ret); +- +- return b1[0]; +-} +- +-static int ves1820_setup_reg0(struct ves1820_state *state, u8 reg0, fe_spectral_inversion_t inversion) +-{ +- reg0 |= state->reg0 & 0x62; +- +- if (INVERSION_ON == inversion) { +- if (!state->config->invert) reg0 |= 0x20; +- else reg0 &= ~0x20; +- } else if (INVERSION_OFF == inversion) { +- if (!state->config->invert) reg0 &= ~0x20; +- else reg0 |= 0x20; +- } +- +- ves1820_writereg(state, 0x00, reg0 & 0xfe); +- ves1820_writereg(state, 0x00, reg0 | 0x01); +- +- state->reg0 = reg0; +- +- return 0; +-} +- +-static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate) +-{ +- s32 BDR; +- s32 BDRI; +- s16 SFIL = 0; +- u16 NDEC = 0; +- u32 ratio; +- u32 fin; +- u32 tmp; +- u64 fptmp; +- u64 fpxin; +- +- if (symbolrate > state->config->xin / 2) +- symbolrate = state->config->xin / 2; +- +- if (symbolrate < 500000) +- symbolrate = 500000; +- +- if (symbolrate < state->config->xin / 16) +- NDEC = 1; +- if (symbolrate < state->config->xin / 32) +- NDEC = 2; +- if (symbolrate < state->config->xin / 64) +- NDEC = 3; +- +- /* yeuch! */ +- fpxin = state->config->xin * 10; +- fptmp = fpxin; do_div(fptmp, 123); +- if (symbolrate < fptmp) +- SFIL = 1; +- fptmp = fpxin; do_div(fptmp, 160); +- if (symbolrate < fptmp) +- SFIL = 0; +- fptmp = fpxin; do_div(fptmp, 246); +- if (symbolrate < fptmp) +- SFIL = 1; +- fptmp = fpxin; do_div(fptmp, 320); +- if (symbolrate < fptmp) +- SFIL = 0; +- fptmp = fpxin; do_div(fptmp, 492); +- if (symbolrate < fptmp) +- SFIL = 1; +- fptmp = fpxin; do_div(fptmp, 640); +- if (symbolrate < fptmp) +- SFIL = 0; +- fptmp = fpxin; do_div(fptmp, 984); +- if (symbolrate < fptmp) +- SFIL = 1; +- +- fin = state->config->xin >> 4; +- symbolrate <<= NDEC; +- ratio = (symbolrate << 4) / fin; +- tmp = ((symbolrate << 4) % fin) << 8; +- ratio = (ratio << 8) + tmp / fin; +- tmp = (tmp % fin) << 8; +- ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin); +- +- BDR = ratio; +- BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2; +- +- if (BDRI > 0xFF) +- BDRI = 0xFF; +- +- SFIL = (SFIL << 4) | ves1820_inittab[0x0E]; +- +- NDEC = (NDEC << 6) | ves1820_inittab[0x03]; +- +- ves1820_writereg(state, 0x03, NDEC); +- ves1820_writereg(state, 0x0a, BDR & 0xff); +- ves1820_writereg(state, 0x0b, (BDR >> 8) & 0xff); +- ves1820_writereg(state, 0x0c, (BDR >> 16) & 0x3f); +- +- ves1820_writereg(state, 0x0d, BDRI); +- ves1820_writereg(state, 0x0e, SFIL); +- +- return 0; +-} +- +-static int ves1820_init(struct dvb_frontend* fe) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- int i; +- +- ves1820_writereg(state, 0, 0); +- +- for (i = 0; i < sizeof(ves1820_inittab); i++) +- ves1820_writereg(state, i, ves1820_inittab[i]); +- if (state->config->selagc) +- ves1820_writereg(state, 2, ves1820_inittab[2] | 0x08); +- +- ves1820_writereg(state, 0x34, state->pwm); +- +- return 0; +-} +- +-static int ves1820_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ves1820_state* state = fe->demodulator_priv; +- static const u8 reg0x00[] = { 0x00, 0x04, 0x08, 0x0c, 0x10 }; +- static const u8 reg0x01[] = { 140, 140, 106, 100, 92 }; +- static const u8 reg0x05[] = { 135, 100, 70, 54, 38 }; +- static const u8 reg0x08[] = { 162, 116, 67, 52, 35 }; +- static const u8 reg0x09[] = { 145, 150, 106, 126, 107 }; +- int real_qam = p->modulation - QAM_16; +- +- if (real_qam < 0 || real_qam > 4) +- return -EINVAL; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- +- ves1820_set_symbolrate(state, p->symbol_rate); +- ves1820_writereg(state, 0x34, state->pwm); +- +- ves1820_writereg(state, 0x01, reg0x01[real_qam]); +- ves1820_writereg(state, 0x05, reg0x05[real_qam]); +- ves1820_writereg(state, 0x08, reg0x08[real_qam]); +- ves1820_writereg(state, 0x09, reg0x09[real_qam]); +- +- ves1820_setup_reg0(state, reg0x00[real_qam], p->inversion); +- ves1820_writereg(state, 2, ves1820_inittab[2] | (state->config->selagc ? 0x08 : 0)); +- return 0; +-} +- +-static int ves1820_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- int sync; +- +- *status = 0; +- sync = ves1820_readreg(state, 0x11); +- +- if (sync & 1) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 2) +- *status |= FE_HAS_CARRIER; +- +- if (sync & 2) /* XXX FIXME! */ +- *status |= FE_HAS_VITERBI; +- +- if (sync & 4) +- *status |= FE_HAS_SYNC; +- +- if (sync & 8) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int ves1820_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- +- u32 _ber = ves1820_readreg(state, 0x14) | +- (ves1820_readreg(state, 0x15) << 8) | +- ((ves1820_readreg(state, 0x16) & 0x0f) << 16); +- *ber = 10 * _ber; +- +- return 0; +-} +- +-static int ves1820_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- +- u8 gain = ves1820_readreg(state, 0x17); +- *strength = (gain << 8) | gain; +- +- return 0; +-} +- +-static int ves1820_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- +- u8 quality = ~ves1820_readreg(state, 0x18); +- *snr = (quality << 8) | quality; +- +- return 0; +-} +- +-static int ves1820_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- +- *ucblocks = ves1820_readreg(state, 0x13) & 0x7f; +- if (*ucblocks == 0x7f) +- *ucblocks = 0xffffffff; +- +- /* reset uncorrected block counter */ +- ves1820_writereg(state, 0x10, ves1820_inittab[0x10] & 0xdf); +- ves1820_writereg(state, 0x10, ves1820_inittab[0x10]); +- +- return 0; +-} +- +-static int ves1820_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ves1820_state* state = fe->demodulator_priv; +- int sync; +- s8 afc = 0; +- +- sync = ves1820_readreg(state, 0x11); +- afc = ves1820_readreg(state, 0x19); +- if (verbose) { +- /* AFC only valid when carrier has been recovered */ +- printk(sync & 2 ? "ves1820: AFC (%d) %dHz\n" : +- "ves1820: [AFC (%d) %dHz]\n", afc, -((s32) p->symbol_rate * afc) >> 10); +- } +- +- if (!state->config->invert) { +- p->inversion = (state->reg0 & 0x20) ? INVERSION_ON : INVERSION_OFF; +- } else { +- p->inversion = (!(state->reg0 & 0x20)) ? INVERSION_ON : INVERSION_OFF; +- } +- +- p->modulation = ((state->reg0 >> 2) & 7) + QAM_16; +- +- p->fec_inner = FEC_NONE; +- +- p->frequency = ((p->frequency + 31250) / 62500) * 62500; +- if (sync & 2) +- p->frequency -= ((s32) p->symbol_rate * afc) >> 10; +- +- return 0; +-} +- +-static int ves1820_sleep(struct dvb_frontend* fe) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- +- ves1820_writereg(state, 0x1b, 0x02); /* pdown ADC */ +- ves1820_writereg(state, 0x00, 0x80); /* standby */ +- +- return 0; +-} +- +-static int ves1820_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings) +-{ +- +- fesettings->min_delay_ms = 200; +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static void ves1820_release(struct dvb_frontend* fe) +-{ +- struct ves1820_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops ves1820_ops; +- +-struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, +- struct i2c_adapter* i2c, +- u8 pwm) +-{ +- struct ves1820_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->reg0 = ves1820_inittab[0]; +- state->config = config; +- state->i2c = i2c; +- state->pwm = pwm; +- +- /* check if the demod is there */ +- if ((ves1820_readreg(state, 0x1a) & 0xf0) != 0x70) +- goto error; +- +- if (verbose) +- printk("ves1820: pwm=0x%02x\n", state->pwm); +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &ves1820_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.ops.info.symbol_rate_min = (state->config->xin / 2) / 64; /* SACLK/64 == (XIN/2)/64 */ +- state->frontend.ops.info.symbol_rate_max = (state->config->xin / 2) / 4; /* SACLK/4 */ +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops ves1820_ops = { +- .delsys = { SYS_DVBC_ANNEX_A }, +- .info = { +- .name = "VLSI VES1820 DVB-C", +- .frequency_stepsize = 62500, +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .caps = FE_CAN_QAM_16 | +- FE_CAN_QAM_32 | +- FE_CAN_QAM_64 | +- FE_CAN_QAM_128 | +- FE_CAN_QAM_256 | +- FE_CAN_FEC_AUTO +- }, +- +- .release = ves1820_release, +- +- .init = ves1820_init, +- .sleep = ves1820_sleep, +- +- .set_frontend = ves1820_set_parameters, +- .get_frontend = ves1820_get_frontend, +- .get_tune_settings = ves1820_get_tune_settings, +- +- .read_status = ves1820_read_status, +- .read_ber = ves1820_read_ber, +- .read_signal_strength = ves1820_read_signal_strength, +- .read_snr = ves1820_read_snr, +- .read_ucblocks = ves1820_read_ucblocks, +-}; +- +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting"); +- +-MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); +-MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(ves1820_attach); +diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h +deleted file mode 100644 +index e902ed6..0000000 +--- a/drivers/media/dvb/frontends/ves1820.h ++++ /dev/null +@@ -1,56 +0,0 @@ +-/* +- VES1820 - Single Chip Cable Channel Receiver driver module +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef VES1820_H +-#define VES1820_H +- +-#include +- +-#define VES1820_SELAGC_PWM 0 +-#define VES1820_SELAGC_SIGNAMPERR 1 +- +-struct ves1820_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* value of XIN to use */ +- u32 xin; +- +- /* does inversion need inverted? */ +- u8 invert:1; +- +- /* SELAGC control */ +- u8 selagc:1; +-}; +- +-#if defined(CONFIG_DVB_VES1820) || (defined(CONFIG_DVB_VES1820_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, +- struct i2c_adapter* i2c, u8 pwm); +-#else +-static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, +- struct i2c_adapter* i2c, u8 pwm) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_VES1820 +- +-#endif // VES1820_H +diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c +deleted file mode 100644 +index 9c17eac..0000000 +--- a/drivers/media/dvb/frontends/ves1x93.c ++++ /dev/null +@@ -1,553 +0,0 @@ +-/* +- Driver for VES1893 and VES1993 QPSK Demodulators +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> +- Copyright (C) 2002 Dennis Noermann +- Copyright (C) 2002-2003 Andreas Oberritter +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "ves1x93.h" +- +- +-struct ves1x93_state { +- struct i2c_adapter* i2c; +- /* configuration settings */ +- const struct ves1x93_config* config; +- struct dvb_frontend frontend; +- +- /* previous uncorrected block counter */ +- fe_spectral_inversion_t inversion; +- u8 *init_1x93_tab; +- u8 *init_1x93_wtab; +- u8 tab_size; +- u8 demod_type; +- u32 frequency; +-}; +- +-static int debug; +-#define dprintk if (debug) printk +- +-#define DEMOD_VES1893 0 +-#define DEMOD_VES1993 1 +- +-static u8 init_1893_tab [] = { +- 0x01, 0xa4, 0x35, 0x80, 0x2a, 0x0b, 0x55, 0xc4, +- 0x09, 0x69, 0x00, 0x86, 0x4c, 0x28, 0x7f, 0x00, +- 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x80, 0x00, 0x21, 0xb0, 0x14, 0x00, 0xdc, 0x00, +- 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x55, 0x00, 0x00, 0x7f, 0x00 +-}; +- +-static u8 init_1993_tab [] = { +- 0x00, 0x9c, 0x35, 0x80, 0x6a, 0x09, 0x72, 0x8c, +- 0x09, 0x6b, 0x00, 0x00, 0x4c, 0x08, 0x00, 0x00, +- 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x80, 0x40, 0x21, 0xb0, 0x00, 0x00, 0x00, 0x10, +- 0x81, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, +- 0x00, 0x00, 0x0e, 0x80, 0x00 +-}; +- +-static u8 init_1893_wtab[] = +-{ +- 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, +- 0,1,0,0,0,0,0,0, 1,0,1,1,0,0,0,1, +- 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, +- 1,1,1,0,1,1 +-}; +- +-static u8 init_1993_wtab[] = +-{ +- 1,1,1,1,1,1,1,1, 1,1,0,0,1,1,0,0, +- 0,1,0,0,0,0,0,0, 1,1,1,1,0,0,0,1, +- 1,1,1,0,0,0,0,0, 0,0,1,1,0,0,0,0, +- 1,1,1,0,1,1,1,1, 1,1,1,1,1 +-}; +- +-static int ves1x93_writereg (struct ves1x93_state* state, u8 reg, u8 data) +-{ +- u8 buf [] = { 0x00, reg, data }; +- struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 3 }; +- int err; +- +- if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) { +- dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data); +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-static u8 ves1x93_readreg (struct ves1x93_state* state, u8 reg) +-{ +- int ret; +- u8 b0 [] = { 0x00, reg }; +- u8 b1 [] = { 0 }; +- struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 2 }, +- { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer (state->i2c, msg, 2); +- +- if (ret != 2) return ret; +- +- return b1[0]; +-} +- +-static int ves1x93_clr_bit (struct ves1x93_state* state) +-{ +- msleep(10); +- ves1x93_writereg (state, 0, state->init_1x93_tab[0] & 0xfe); +- ves1x93_writereg (state, 0, state->init_1x93_tab[0]); +- msleep(50); +- return 0; +-} +- +-static int ves1x93_set_inversion (struct ves1x93_state* state, fe_spectral_inversion_t inversion) +-{ +- u8 val; +- +- /* +- * inversion on/off are interchanged because i and q seem to +- * be swapped on the hardware +- */ +- +- switch (inversion) { +- case INVERSION_OFF: +- val = 0xc0; +- break; +- case INVERSION_ON: +- val = 0x80; +- break; +- case INVERSION_AUTO: +- val = 0x00; +- break; +- default: +- return -EINVAL; +- } +- +- return ves1x93_writereg (state, 0x0c, (state->init_1x93_tab[0x0c] & 0x3f) | val); +-} +- +-static int ves1x93_set_fec (struct ves1x93_state* state, fe_code_rate_t fec) +-{ +- if (fec == FEC_AUTO) +- return ves1x93_writereg (state, 0x0d, 0x08); +- else if (fec < FEC_1_2 || fec > FEC_8_9) +- return -EINVAL; +- else +- return ves1x93_writereg (state, 0x0d, fec - FEC_1_2); +-} +- +-static fe_code_rate_t ves1x93_get_fec (struct ves1x93_state* state) +-{ +- return FEC_1_2 + ((ves1x93_readreg (state, 0x0d) >> 4) & 0x7); +-} +- +-static int ves1x93_set_symbolrate (struct ves1x93_state* state, u32 srate) +-{ +- u32 BDR; +- u32 ratio; +- u8 ADCONF, FCONF, FNR, AGCR; +- u32 BDRI; +- u32 tmp; +- u32 FIN; +- +- dprintk("%s: srate == %d\n", __func__, (unsigned int) srate); +- +- if (srate > state->config->xin/2) +- srate = state->config->xin/2; +- +- if (srate < 500000) +- srate = 500000; +- +-#define MUL (1UL<<26) +- +- FIN = (state->config->xin + 6000) >> 4; +- +- tmp = srate << 6; +- ratio = tmp / FIN; +- +- tmp = (tmp % FIN) << 8; +- ratio = (ratio << 8) + tmp / FIN; +- +- tmp = (tmp % FIN) << 8; +- ratio = (ratio << 8) + tmp / FIN; +- +- FNR = 0xff; +- +- if (ratio < MUL/3) FNR = 0; +- if (ratio < (MUL*11)/50) FNR = 1; +- if (ratio < MUL/6) FNR = 2; +- if (ratio < MUL/9) FNR = 3; +- if (ratio < MUL/12) FNR = 4; +- if (ratio < (MUL*11)/200) FNR = 5; +- if (ratio < MUL/24) FNR = 6; +- if (ratio < (MUL*27)/1000) FNR = 7; +- if (ratio < MUL/48) FNR = 8; +- if (ratio < (MUL*137)/10000) FNR = 9; +- +- if (FNR == 0xff) { +- ADCONF = 0x89; +- FCONF = 0x80; +- FNR = 0; +- } else { +- ADCONF = 0x81; +- FCONF = 0x88 | (FNR >> 1) | ((FNR & 0x01) << 5); +- /*FCONF = 0x80 | ((FNR & 0x01) << 5) | (((FNR > 1) & 0x03) << 3) | ((FNR >> 1) & 0x07);*/ +- } +- +- BDR = (( (ratio << (FNR >> 1)) >> 4) + 1) >> 1; +- BDRI = ( ((FIN << 8) / ((srate << (FNR >> 1)) >> 2)) + 1) >> 1; +- +- dprintk("FNR= %d\n", FNR); +- dprintk("ratio= %08x\n", (unsigned int) ratio); +- dprintk("BDR= %08x\n", (unsigned int) BDR); +- dprintk("BDRI= %02x\n", (unsigned int) BDRI); +- +- if (BDRI > 0xff) +- BDRI = 0xff; +- +- ves1x93_writereg (state, 0x06, 0xff & BDR); +- ves1x93_writereg (state, 0x07, 0xff & (BDR >> 8)); +- ves1x93_writereg (state, 0x08, 0x0f & (BDR >> 16)); +- +- ves1x93_writereg (state, 0x09, BDRI); +- ves1x93_writereg (state, 0x20, ADCONF); +- ves1x93_writereg (state, 0x21, FCONF); +- +- AGCR = state->init_1x93_tab[0x05]; +- if (state->config->invert_pwm) +- AGCR |= 0x20; +- +- if (srate < 6000000) +- AGCR |= 0x80; +- else +- AGCR &= ~0x80; +- +- ves1x93_writereg (state, 0x05, AGCR); +- +- /* ves1993 hates this, will lose lock */ +- if (state->demod_type != DEMOD_VES1993) +- ves1x93_clr_bit (state); +- +- return 0; +-} +- +-static int ves1x93_init (struct dvb_frontend* fe) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- int i; +- int val; +- +- dprintk("%s: init chip\n", __func__); +- +- for (i = 0; i < state->tab_size; i++) { +- if (state->init_1x93_wtab[i]) { +- val = state->init_1x93_tab[i]; +- +- if (state->config->invert_pwm && (i == 0x05)) val |= 0x20; /* invert PWM */ +- ves1x93_writereg (state, i, val); +- } +- } +- +- return 0; +-} +- +-static int ves1x93_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- return ves1x93_writereg (state, 0x1f, 0x20); +- case SEC_VOLTAGE_18: +- return ves1x93_writereg (state, 0x1f, 0x30); +- case SEC_VOLTAGE_OFF: +- return ves1x93_writereg (state, 0x1f, 0x00); +- default: +- return -EINVAL; +- } +-} +- +-static int ves1x93_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- u8 sync = ves1x93_readreg (state, 0x0e); +- +- /* +- * The ves1893 sometimes returns sync values that make no sense, +- * because, e.g., the SIGNAL bit is 0, while some of the higher +- * bits are 1 (and how can there be a CARRIER w/o a SIGNAL?). +- * Tests showed that the VITERBI and SYNC bits are returned +- * reliably, while the SIGNAL and CARRIER bits ar sometimes wrong. +- * If such a case occurs, we read the value again, until we get a +- * valid value. +- */ +- int maxtry = 10; /* just for safety - let's not get stuck here */ +- while ((sync & 0x03) != 0x03 && (sync & 0x0c) && maxtry--) { +- msleep(10); +- sync = ves1x93_readreg (state, 0x0e); +- } +- +- *status = 0; +- +- if (sync & 1) +- *status |= FE_HAS_SIGNAL; +- +- if (sync & 2) +- *status |= FE_HAS_CARRIER; +- +- if (sync & 4) +- *status |= FE_HAS_VITERBI; +- +- if (sync & 8) +- *status |= FE_HAS_SYNC; +- +- if ((sync & 0x1f) == 0x1f) +- *status |= FE_HAS_LOCK; +- +- return 0; +-} +- +-static int ves1x93_read_ber(struct dvb_frontend* fe, u32* ber) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- *ber = ves1x93_readreg (state, 0x15); +- *ber |= (ves1x93_readreg (state, 0x16) << 8); +- *ber |= ((ves1x93_readreg (state, 0x17) & 0x0F) << 16); +- *ber *= 10; +- +- return 0; +-} +- +-static int ves1x93_read_signal_strength(struct dvb_frontend* fe, u16* strength) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- u8 signal = ~ves1x93_readreg (state, 0x0b); +- *strength = (signal << 8) | signal; +- +- return 0; +-} +- +-static int ves1x93_read_snr(struct dvb_frontend* fe, u16* snr) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- u8 _snr = ~ves1x93_readreg (state, 0x1c); +- *snr = (_snr << 8) | _snr; +- +- return 0; +-} +- +-static int ves1x93_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- *ucblocks = ves1x93_readreg (state, 0x18) & 0x7f; +- +- if (*ucblocks == 0x7f) +- *ucblocks = 0xffffffff; /* counter overflow... */ +- +- ves1x93_writereg (state, 0x18, 0x00); /* reset the counter */ +- ves1x93_writereg (state, 0x18, 0x80); /* dto. */ +- +- return 0; +-} +- +-static int ves1x93_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ves1x93_state* state = fe->demodulator_priv; +- +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); +- } +- ves1x93_set_inversion (state, p->inversion); +- ves1x93_set_fec(state, p->fec_inner); +- ves1x93_set_symbolrate(state, p->symbol_rate); +- state->inversion = p->inversion; +- state->frequency = p->frequency; +- +- return 0; +-} +- +-static int ves1x93_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ves1x93_state* state = fe->demodulator_priv; +- int afc; +- +- afc = ((int)((char)(ves1x93_readreg (state, 0x0a) << 1)))/2; +- afc = (afc * (int)(p->symbol_rate/1000/8))/16; +- +- p->frequency = state->frequency - afc; +- +- /* +- * inversion indicator is only valid +- * if auto inversion was used +- */ +- if (state->inversion == INVERSION_AUTO) +- p->inversion = (ves1x93_readreg (state, 0x0f) & 2) ? +- INVERSION_OFF : INVERSION_ON; +- p->fec_inner = ves1x93_get_fec(state); +- /* XXX FIXME: timing offset !! */ +- +- return 0; +-} +- +-static int ves1x93_sleep(struct dvb_frontend* fe) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- return ves1x93_writereg (state, 0x00, 0x08); +-} +- +-static void ves1x93_release(struct dvb_frontend* fe) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- kfree(state); +-} +- +-static int ves1x93_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct ves1x93_state* state = fe->demodulator_priv; +- +- if (enable) { +- return ves1x93_writereg(state, 0x00, 0x11); +- } else { +- return ves1x93_writereg(state, 0x00, 0x01); +- } +-} +- +-static struct dvb_frontend_ops ves1x93_ops; +- +-struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, +- struct i2c_adapter* i2c) +-{ +- struct ves1x93_state* state = NULL; +- u8 identity; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); +- if (state == NULL) goto error; +- +- /* setup the state */ +- state->config = config; +- state->i2c = i2c; +- state->inversion = INVERSION_OFF; +- +- /* check if the demod is there + identify it */ +- identity = ves1x93_readreg(state, 0x1e); +- switch (identity) { +- case 0xdc: /* VES1893A rev1 */ +- printk("ves1x93: Detected ves1893a rev1\n"); +- state->demod_type = DEMOD_VES1893; +- state->init_1x93_tab = init_1893_tab; +- state->init_1x93_wtab = init_1893_wtab; +- state->tab_size = sizeof(init_1893_tab); +- break; +- +- case 0xdd: /* VES1893A rev2 */ +- printk("ves1x93: Detected ves1893a rev2\n"); +- state->demod_type = DEMOD_VES1893; +- state->init_1x93_tab = init_1893_tab; +- state->init_1x93_wtab = init_1893_wtab; +- state->tab_size = sizeof(init_1893_tab); +- break; +- +- case 0xde: /* VES1993 */ +- printk("ves1x93: Detected ves1993\n"); +- state->demod_type = DEMOD_VES1993; +- state->init_1x93_tab = init_1993_tab; +- state->init_1x93_wtab = init_1993_wtab; +- state->tab_size = sizeof(init_1993_tab); +- break; +- +- default: +- goto error; +- } +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &ves1x93_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +- +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops ves1x93_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "VLSI VES1x93 DVB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, /* kHz for QPSK frontends */ +- .frequency_tolerance = 29500, +- .symbol_rate_min = 1000000, +- .symbol_rate_max = 45000000, +- /* .symbol_rate_tolerance = ???,*/ +- .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 +- }, +- +- .release = ves1x93_release, +- +- .init = ves1x93_init, +- .sleep = ves1x93_sleep, +- .i2c_gate_ctrl = ves1x93_i2c_gate_ctrl, +- +- .set_frontend = ves1x93_set_frontend, +- .get_frontend = ves1x93_get_frontend, +- +- .read_status = ves1x93_read_status, +- .read_ber = ves1x93_read_ber, +- .read_signal_strength = ves1x93_read_signal_strength, +- .read_snr = ves1x93_read_snr, +- .read_ucblocks = ves1x93_read_ucblocks, +- +- .set_voltage = ves1x93_set_voltage, +-}; +- +-module_param(debug, int, 0644); +- +-MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); +-MODULE_AUTHOR("Ralph Metzler"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(ves1x93_attach); +diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h +deleted file mode 100644 +index 8a5a49e..0000000 +--- a/drivers/media/dvb/frontends/ves1x93.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/* +- Driver for VES1893 and VES1993 QPSK Demodulators +- +- Copyright (C) 1999 Convergence Integrated Media GmbH +- Copyright (C) 2001 Ronny Strutz <3des@elitedvb.de> +- Copyright (C) 2002 Dennis Noermann +- Copyright (C) 2002-2003 Andreas Oberritter +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef VES1X93_H +-#define VES1X93_H +- +-#include +- +-struct ves1x93_config +-{ +- /* the demodulator's i2c address */ +- u8 demod_address; +- +- /* value of XIN to use */ +- u32 xin; +- +- /* should PWM be inverted? */ +- u8 invert_pwm:1; +-}; +- +-#if defined(CONFIG_DVB_VES1X93) || (defined(CONFIG_DVB_VES1X93_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, +- struct i2c_adapter* i2c); +-#else +-static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, +- struct i2c_adapter* i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif // CONFIG_DVB_VES1X93 +- +-#endif // VES1X93_H +diff --git a/drivers/media/dvb/frontends/z0194a.h b/drivers/media/dvb/frontends/z0194a.h +deleted file mode 100644 +index 96d86d6..0000000 +--- a/drivers/media/dvb/frontends/z0194a.h ++++ /dev/null +@@ -1,85 +0,0 @@ +-/* z0194a.h Sharp z0194a tuner support +-* +-* Copyright (C) 2008 Igor M. Liplianin (liplianin@me.by) +-* +-* This program is free software; you can redistribute it and/or modify it +-* under the terms of the GNU General Public License as published by the +-* Free Software Foundation, version 2. +-* +-* see Documentation/dvb/README.dvb-usb for more information +-*/ +- +-#ifndef Z0194A +-#define Z0194A +- +-static int sharp_z0194a_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 sharp_z0194a_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x40, /* DAC not used, set to high impendance mode */ +- 0x07, 0x00, /* DAC LSB */ +- 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ +- 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ +- 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ +- 0x10, 0x3f, /* AGC2 0x3d */ +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, /* lock detector threshold */ +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, /* out imp: normal out type: parallel FEC mode:0 */ +- 0x29, 0x1e, /* 1/2 threshold */ +- 0x2a, 0x14, /* 2/3 threshold */ +- 0x2b, 0x0f, /* 3/4 threshold */ +- 0x2c, 0x09, /* 5/6 threshold */ +- 0x2d, 0x05, /* 7/8 threshold */ +- 0x2e, 0x01, +- 0x31, 0x1f, /* test all FECs */ +- 0x32, 0x19, /* viterbi and synchro search */ +- 0x33, 0xfc, /* rs control */ +- 0x34, 0x93, /* error control */ +- 0x0f, 0x52, +- 0xff, 0xff +-}; +- +-#endif +diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c +deleted file mode 100644 +index 0903d46..0000000 +--- a/drivers/media/dvb/frontends/zl10036.c ++++ /dev/null +@@ -1,520 +0,0 @@ +-/** +- * Driver for Zarlink zl10036 DVB-S silicon tuner +- * +- * Copyright (C) 2006 Tino Reichardt +- * Copyright (C) 2007-2009 Matthias Schwarzott +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- ** +- * The data sheet for this tuner can be found at: +- * http://www.mcmilk.de/projects/dvb-card/datasheets/ZL10036.pdf +- * +- * This one is working: (at my Avermedia DVB-S Pro) +- * - zl10036 (40pin, FTA) +- * +- * A driver for zl10038 should be very similar. +- */ +- +-#include +-#include +-#include +-#include +- +-#include "zl10036.h" +- +-static int zl10036_debug; +-#define dprintk(level, args...) \ +- do { if (zl10036_debug & level) printk(KERN_DEBUG "zl10036: " args); \ +- } while (0) +- +-#define deb_info(args...) dprintk(0x01, args) +-#define deb_i2c(args...) dprintk(0x02, args) +- +-struct zl10036_state { +- struct i2c_adapter *i2c; +- const struct zl10036_config *config; +- u32 frequency; +- u8 br, bf; +-}; +- +- +-/* This driver assumes the tuner is driven by a 10.111MHz Cristal */ +-#define _XTAL 10111 +- +-/* Some of the possible dividers: +- * 64, (write 0x05 to reg), freq step size 158kHz +- * 10, (write 0x0a to reg), freq step size 1.011kHz (used here) +- * 5, (write 0x09 to reg), freq step size 2.022kHz +- */ +- +-#define _RDIV 10 +-#define _RDIV_REG 0x0a +-#define _FR (_XTAL/_RDIV) +- +-#define STATUS_POR 0x80 /* Power on Reset */ +-#define STATUS_FL 0x40 /* Frequency & Phase Lock */ +- +-/* read/write for zl10036 and zl10038 */ +- +-static int zl10036_read_status_reg(struct zl10036_state *state) +-{ +- u8 status; +- struct i2c_msg msg[1] = { +- { .addr = state->config->tuner_address, .flags = I2C_M_RD, +- .buf = &status, .len = sizeof(status) }, +- }; +- +- if (i2c_transfer(state->i2c, msg, 1) != 1) { +- printk(KERN_ERR "%s: i2c read failed at addr=%02x\n", +- __func__, state->config->tuner_address); +- return -EIO; +- } +- +- deb_i2c("R(status): %02x [FL=%d]\n", status, +- (status & STATUS_FL) ? 1 : 0); +- if (status & STATUS_POR) +- deb_info("%s: Power-On-Reset bit enabled - " +- "need to initialize the tuner\n", __func__); +- +- return status; +-} +- +-static int zl10036_write(struct zl10036_state *state, u8 buf[], u8 count) +-{ +- struct i2c_msg msg[1] = { +- { .addr = state->config->tuner_address, .flags = 0, +- .buf = buf, .len = count }, +- }; +- u8 reg = 0; +- int ret; +- +- if (zl10036_debug & 0x02) { +- /* every 8bit-value satisifes this! +- * so only check for debug log */ +- if ((buf[0] & 0x80) == 0x00) +- reg = 2; +- else if ((buf[0] & 0xc0) == 0x80) +- reg = 4; +- else if ((buf[0] & 0xf0) == 0xc0) +- reg = 6; +- else if ((buf[0] & 0xf0) == 0xd0) +- reg = 8; +- else if ((buf[0] & 0xf0) == 0xe0) +- reg = 10; +- else if ((buf[0] & 0xf0) == 0xf0) +- reg = 12; +- +- deb_i2c("W(%d):", reg); +- { +- int i; +- for (i = 0; i < count; i++) +- printk(KERN_CONT " %02x", buf[i]); +- printk(KERN_CONT "\n"); +- } +- } +- +- ret = i2c_transfer(state->i2c, msg, 1); +- if (ret != 1) { +- printk(KERN_ERR "%s: i2c error, ret=%d\n", __func__, ret); +- return -EIO; +- } +- +- return 0; +-} +- +-static int zl10036_release(struct dvb_frontend *fe) +-{ +- struct zl10036_state *state = fe->tuner_priv; +- +- fe->tuner_priv = NULL; +- kfree(state); +- +- return 0; +-} +- +-static int zl10036_sleep(struct dvb_frontend *fe) +-{ +- struct zl10036_state *state = fe->tuner_priv; +- u8 buf[] = { 0xf0, 0x80 }; /* regs 12/13 */ +- int ret; +- +- deb_info("%s\n", __func__); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = zl10036_write(state, buf, sizeof(buf)); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-/** +- * register map of the ZL10036/ZL10038 +- * +- * reg[default] content +- * 2[0x00]: 0 | N14 | N13 | N12 | N11 | N10 | N9 | N8 +- * 3[0x00]: N7 | N6 | N5 | N4 | N3 | N2 | N1 | N0 +- * 4[0x80]: 1 | 0 | RFG | BA1 | BA0 | BG1 | BG0 | LEN +- * 5[0x00]: P0 | C1 | C0 | R4 | R3 | R2 | R1 | R0 +- * 6[0xc0]: 1 | 1 | 0 | 0 | RSD | 0 | 0 | 0 +- * 7[0x20]: P1 | BF6 | BF5 | BF4 | BF3 | BF2 | BF1 | 0 +- * 8[0xdb]: 1 | 1 | 0 | 1 | 0 | CC | 1 | 1 +- * 9[0x30]: VSD | V2 | V1 | V0 | S3 | S2 | S1 | S0 +- * 10[0xe1]: 1 | 1 | 1 | 0 | 0 | LS2 | LS1 | LS0 +- * 11[0xf5]: WS | WH2 | WH1 | WH0 | WL2 | WL1 | WL0 | WRE +- * 12[0xf0]: 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 +- * 13[0x28]: PD | BR4 | BR3 | BR2 | BR1 | BR0 | CLR | TL +- */ +- +-static int zl10036_set_frequency(struct zl10036_state *state, u32 frequency) +-{ +- u8 buf[2]; +- u32 div, foffset; +- +- div = (frequency + _FR/2) / _FR; +- state->frequency = div * _FR; +- +- foffset = frequency - state->frequency; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = (div >> 0) & 0xff; +- +- deb_info("%s: ftodo=%u fpriv=%u ferr=%d div=%u\n", __func__, +- frequency, state->frequency, foffset, div); +- +- return zl10036_write(state, buf, sizeof(buf)); +-} +- +-static int zl10036_set_bandwidth(struct zl10036_state *state, u32 fbw) +-{ +- /* fbw is measured in kHz */ +- u8 br, bf; +- int ret; +- u8 buf_bf[] = { +- 0xc0, 0x00, /* 6/7: rsd=0 bf=0 */ +- }; +- u8 buf_br[] = { +- 0xf0, 0x00, /* 12/13: br=0xa clr=0 tl=0*/ +- }; +- u8 zl10036_rsd_off[] = { 0xc8 }; /* set RSD=1 */ +- +- /* ensure correct values */ +- if (fbw > 35000) +- fbw = 35000; +- if (fbw < 8000) +- fbw = 8000; +- +-#define _BR_MAXIMUM (_XTAL/575) /* _XTAL / 575kHz = 17 */ +- +- /* <= 28,82 MHz */ +- if (fbw <= 28820) { +- br = _BR_MAXIMUM; +- } else { +- /** +- * f(bw)=34,6MHz f(xtal)=10.111MHz +- * br = (10111/34600) * 63 * 1/K = 14; +- */ +- br = ((_XTAL * 21 * 1000) / (fbw * 419)); +- } +- +- /* ensure correct values */ +- if (br < 4) +- br = 4; +- if (br > _BR_MAXIMUM) +- br = _BR_MAXIMUM; +- +- /* +- * k = 1.257 +- * bf = fbw/_XTAL * br * k - 1 */ +- +- bf = (fbw * br * 1257) / (_XTAL * 1000) - 1; +- +- /* ensure correct values */ +- if (bf > 62) +- bf = 62; +- +- buf_bf[1] = (bf << 1) & 0x7e; +- buf_br[1] = (br << 2) & 0x7c; +- deb_info("%s: BW=%d br=%u bf=%u\n", __func__, fbw, br, bf); +- +- if (br != state->br) { +- ret = zl10036_write(state, buf_br, sizeof(buf_br)); +- if (ret < 0) +- return ret; +- } +- +- if (bf != state->bf) { +- ret = zl10036_write(state, buf_bf, sizeof(buf_bf)); +- if (ret < 0) +- return ret; +- +- /* time = br/(32* fxtal) */ +- /* minimal sleep time to be calculated +- * maximum br is 63 -> max time = 2 /10 MHz = 2e-7 */ +- msleep(1); +- +- ret = zl10036_write(state, zl10036_rsd_off, +- sizeof(zl10036_rsd_off)); +- if (ret < 0) +- return ret; +- } +- +- state->br = br; +- state->bf = bf; +- +- return 0; +-} +- +-static int zl10036_set_gain_params(struct zl10036_state *state, +- int c) +-{ +- u8 buf[2]; +- u8 rfg, ba, bg; +- +- /* default values */ +- rfg = 0; /* enable when using an lna */ +- ba = 1; +- bg = 1; +- +- /* reg 4 */ +- buf[0] = 0x80 | ((rfg << 5) & 0x20) +- | ((ba << 3) & 0x18) | ((bg << 1) & 0x06); +- +- if (!state->config->rf_loop_enable) +- buf[0] |= 0x01; +- +- /* P0=0 */ +- buf[1] = _RDIV_REG | ((c << 5) & 0x60); +- +- deb_info("%s: c=%u rfg=%u ba=%u bg=%u\n", __func__, c, rfg, ba, bg); +- return zl10036_write(state, buf, sizeof(buf)); +-} +- +-static int zl10036_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct zl10036_state *state = fe->tuner_priv; +- int ret = 0; +- u32 frequency = p->frequency; +- u32 fbw; +- int i; +- u8 c; +- +- /* ensure correct values +- * maybe redundant as core already checks this */ +- if ((frequency < fe->ops.info.frequency_min) +- || (frequency > fe->ops.info.frequency_max)) +- return -EINVAL; +- +- /** +- * alpha = 1.35 for dvb-s +- * fBW = (alpha*symbolrate)/(2*0.8) +- * 1.35 / (2*0.8) = 27 / 32 +- */ +- fbw = (27 * p->symbol_rate) / 32; +- +- /* scale to kHz */ +- fbw /= 1000; +- +- /* Add safe margin of 3MHz */ +- fbw += 3000; +- +- /* setting the charge pump - guessed values */ +- if (frequency < 950000) +- return -EINVAL; +- else if (frequency < 1250000) +- c = 0; +- else if (frequency < 1750000) +- c = 1; +- else if (frequency < 2175000) +- c = 2; +- else +- return -EINVAL; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = zl10036_set_gain_params(state, c); +- if (ret < 0) +- goto error; +- +- ret = zl10036_set_frequency(state, p->frequency); +- if (ret < 0) +- goto error; +- +- ret = zl10036_set_bandwidth(state, fbw); +- if (ret < 0) +- goto error; +- +- /* wait for tuner lock - no idea if this is really needed */ +- for (i = 0; i < 20; i++) { +- ret = zl10036_read_status_reg(state); +- if (ret < 0) +- goto error; +- +- /* check Frequency & Phase Lock Bit */ +- if (ret & STATUS_FL) +- break; +- +- msleep(10); +- } +- +-error: +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-static int zl10036_get_frequency(struct dvb_frontend *fe, u32 *frequency) +-{ +- struct zl10036_state *state = fe->tuner_priv; +- +- *frequency = state->frequency; +- +- return 0; +-} +- +-static int zl10036_init_regs(struct zl10036_state *state) +-{ +- int ret; +- int i; +- +- /* could also be one block from reg 2 to 13 and additional 10/11 */ +- u8 zl10036_init_tab[][2] = { +- { 0x04, 0x00 }, /* 2/3: div=0x400 - arbitrary value */ +- { 0x8b, _RDIV_REG }, /* 4/5: rfg=0 ba=1 bg=1 len=? */ +- /* p0=0 c=0 r=_RDIV_REG */ +- { 0xc0, 0x20 }, /* 6/7: rsd=0 bf=0x10 */ +- { 0xd3, 0x40 }, /* 8/9: from datasheet */ +- { 0xe3, 0x5b }, /* 10/11: lock window level */ +- { 0xf0, 0x28 }, /* 12/13: br=0xa clr=0 tl=0*/ +- { 0xe3, 0xf9 }, /* 10/11: unlock window level */ +- }; +- +- /* invalid values to trigger writing */ +- state->br = 0xff; +- state->bf = 0xff; +- +- if (!state->config->rf_loop_enable) +- zl10036_init_tab[1][0] |= 0x01; +- +- deb_info("%s\n", __func__); +- +- for (i = 0; i < ARRAY_SIZE(zl10036_init_tab); i++) { +- ret = zl10036_write(state, zl10036_init_tab[i], 2); +- if (ret < 0) +- return ret; +- } +- +- return 0; +-} +- +-static int zl10036_init(struct dvb_frontend *fe) +-{ +- struct zl10036_state *state = fe->tuner_priv; +- int ret = 0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = zl10036_read_status_reg(state); +- if (ret < 0) +- return ret; +- +- /* Only init if Power-on-Reset bit is set? */ +- ret = zl10036_init_regs(state); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- return ret; +-} +- +-static struct dvb_tuner_ops zl10036_tuner_ops = { +- .info = { +- .name = "Zarlink ZL10036", +- .frequency_min = 950000, +- .frequency_max = 2175000 +- }, +- .init = zl10036_init, +- .release = zl10036_release, +- .sleep = zl10036_sleep, +- .set_params = zl10036_set_params, +- .get_frequency = zl10036_get_frequency, +-}; +- +-struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, +- const struct zl10036_config *config, +- struct i2c_adapter *i2c) +-{ +- struct zl10036_state *state; +- int ret; +- +- if (!config) { +- printk(KERN_ERR "%s: no config specified", __func__); +- return NULL; +- } +- +- state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- state->config = config; +- state->i2c = i2c; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ +- +- ret = zl10036_read_status_reg(state); +- if (ret < 0) { +- printk(KERN_ERR "%s: No zl10036 found\n", __func__); +- goto error; +- } +- +- ret = zl10036_init_regs(state); +- if (ret < 0) { +- printk(KERN_ERR "%s: tuner initialization failed\n", +- __func__); +- goto error; +- } +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ +- +- fe->tuner_priv = state; +- +- memcpy(&fe->ops.tuner_ops, &zl10036_tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- printk(KERN_INFO "%s: tuner initialization (%s addr=0x%02x) ok\n", +- __func__, fe->ops.tuner_ops.info.name, config->tuner_address); +- +- return fe; +- +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(zl10036_attach); +- +-module_param_named(debug, zl10036_debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +-MODULE_DESCRIPTION("DVB ZL10036 driver"); +-MODULE_AUTHOR("Tino Reichardt"); +-MODULE_AUTHOR("Matthias Schwarzott"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/zl10036.h b/drivers/media/dvb/frontends/zl10036.h +deleted file mode 100644 +index d84b8f8..0000000 +--- a/drivers/media/dvb/frontends/zl10036.h ++++ /dev/null +@@ -1,53 +0,0 @@ +-/** +- * Driver for Zarlink ZL10036 DVB-S silicon tuner +- * +- * Copyright (C) 2006 Tino Reichardt +- * Copyright (C) 2007-2009 Matthias Schwarzott +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License Version 2, as +- * published by the Free Software Foundation. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef DVB_ZL10036_H +-#define DVB_ZL10036_H +- +-#include +-#include "dvb_frontend.h" +- +-/** +- * Attach a zl10036 tuner to the supplied frontend structure. +- * +- * @param fe Frontend to attach to. +- * @param config zl10036_config structure +- * @return FE pointer on success, NULL on failure. +- */ +- +-struct zl10036_config { +- u8 tuner_address; +- int rf_loop_enable; +-}; +- +-#if defined(CONFIG_DVB_ZL10036) || \ +- (defined(CONFIG_DVB_ZL10036_MODULE) && defined(MODULE)) +-extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, +- const struct zl10036_config *config, struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, +- const struct zl10036_config *config, struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif +- +-#endif /* DVB_ZL10036_H */ +diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb/frontends/zl10039.c +deleted file mode 100644 +index eff9c5f..0000000 +--- a/drivers/media/dvb/frontends/zl10039.c ++++ /dev/null +@@ -1,307 +0,0 @@ +-/* +- * Driver for Zarlink ZL10039 DVB-S tuner +- * +- * Copyright 2007 Jan D. Louw +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "zl10039.h" +- +-static int debug; +- +-#define dprintk(args...) \ +- do { \ +- if (debug) \ +- printk(KERN_DEBUG args); \ +- } while (0) +- +-enum zl10039_model_id { +- ID_ZL10039 = 1 +-}; +- +-struct zl10039_state { +- struct i2c_adapter *i2c; +- u8 i2c_addr; +- u8 id; +-}; +- +-enum zl10039_reg_addr { +- PLL0 = 0, +- PLL1, +- PLL2, +- PLL3, +- RFFE, +- BASE0, +- BASE1, +- BASE2, +- LO0, +- LO1, +- LO2, +- LO3, +- LO4, +- LO5, +- LO6, +- GENERAL +-}; +- +-static int zl10039_read(const struct zl10039_state *state, +- const enum zl10039_reg_addr reg, u8 *buf, +- const size_t count) +-{ +- u8 regbuf[] = { reg }; +- struct i2c_msg msg[] = { +- {/* Write register address */ +- .addr = state->i2c_addr, +- .flags = 0, +- .buf = regbuf, +- .len = 1, +- }, {/* Read count bytes */ +- .addr = state->i2c_addr, +- .flags = I2C_M_RD, +- .buf = buf, +- .len = count, +- }, +- }; +- +- dprintk("%s\n", __func__); +- +- if (i2c_transfer(state->i2c, msg, 2) != 2) { +- dprintk("%s: i2c read error\n", __func__); +- return -EREMOTEIO; +- } +- +- return 0; /* Success */ +-} +- +-static int zl10039_write(struct zl10039_state *state, +- const enum zl10039_reg_addr reg, const u8 *src, +- const size_t count) +-{ +- u8 buf[count + 1]; +- struct i2c_msg msg = { +- .addr = state->i2c_addr, +- .flags = 0, +- .buf = buf, +- .len = count + 1, +- }; +- +- dprintk("%s\n", __func__); +- /* Write register address and data in one go */ +- buf[0] = reg; +- memcpy(&buf[1], src, count); +- if (i2c_transfer(state->i2c, &msg, 1) != 1) { +- dprintk("%s: i2c write error\n", __func__); +- return -EREMOTEIO; +- } +- +- return 0; /* Success */ +-} +- +-static inline int zl10039_readreg(struct zl10039_state *state, +- const enum zl10039_reg_addr reg, u8 *val) +-{ +- return zl10039_read(state, reg, val, 1); +-} +- +-static inline int zl10039_writereg(struct zl10039_state *state, +- const enum zl10039_reg_addr reg, +- const u8 val) +-{ +- return zl10039_write(state, reg, &val, 1); +-} +- +-static int zl10039_init(struct dvb_frontend *fe) +-{ +- struct zl10039_state *state = fe->tuner_priv; +- int ret; +- +- dprintk("%s\n", __func__); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- /* Reset logic */ +- ret = zl10039_writereg(state, GENERAL, 0x40); +- if (ret < 0) { +- dprintk("Note: i2c write error normal when resetting the " +- "tuner\n"); +- } +- /* Wake up */ +- ret = zl10039_writereg(state, GENERAL, 0x01); +- if (ret < 0) { +- dprintk("Tuner power up failed\n"); +- return ret; +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int zl10039_sleep(struct dvb_frontend *fe) +-{ +- struct zl10039_state *state = fe->tuner_priv; +- int ret; +- +- dprintk("%s\n", __func__); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = zl10039_writereg(state, GENERAL, 0x80); +- if (ret < 0) { +- dprintk("Tuner sleep failed\n"); +- return ret; +- } +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- return 0; +-} +- +-static int zl10039_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct zl10039_state *state = fe->tuner_priv; +- u8 buf[6]; +- u8 bf; +- u32 fbw; +- u32 div; +- int ret; +- +- dprintk("%s\n", __func__); +- dprintk("Set frequency = %d, symbol rate = %d\n", +- c->frequency, c->symbol_rate); +- +- /* Assumed 10.111 MHz crystal oscillator */ +- /* Cancelled num/den 80 to prevent overflow */ +- div = (c->frequency * 1000) / 126387; +- fbw = (c->symbol_rate * 27) / 32000; +- /* Cancelled num/den 10 to prevent overflow */ +- bf = ((fbw * 5088) / 1011100) - 1; +- +- /*PLL divider*/ +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = (div >> 0) & 0xff; +- /*Reference divider*/ +- /* Select reference ratio of 80 */ +- buf[2] = 0x1D; +- /*PLL test modes*/ +- buf[3] = 0x40; +- /*RF Control register*/ +- buf[4] = 0x6E; /* Bypass enable */ +- /*Baseband filter cutoff */ +- buf[5] = bf; +- +- /* Open i2c gate */ +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- /* BR = 10, Enable filter adjustment */ +- ret = zl10039_writereg(state, BASE1, 0x0A); +- if (ret < 0) +- goto error; +- /* Write new config values */ +- ret = zl10039_write(state, PLL0, buf, sizeof(buf)); +- if (ret < 0) +- goto error; +- /* BR = 10, Disable filter adjustment */ +- ret = zl10039_writereg(state, BASE1, 0x6A); +- if (ret < 0) +- goto error; +- +- /* Close i2c gate */ +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- return 0; +-error: +- dprintk("Error setting tuner\n"); +- return ret; +-} +- +-static int zl10039_release(struct dvb_frontend *fe) +-{ +- struct zl10039_state *state = fe->tuner_priv; +- +- dprintk("%s\n", __func__); +- kfree(state); +- fe->tuner_priv = NULL; +- return 0; +-} +- +-static struct dvb_tuner_ops zl10039_ops = { +- .release = zl10039_release, +- .init = zl10039_init, +- .sleep = zl10039_sleep, +- .set_params = zl10039_set_params, +-}; +- +-struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, +- u8 i2c_addr, struct i2c_adapter *i2c) +-{ +- struct zl10039_state *state = NULL; +- +- dprintk("%s\n", __func__); +- state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- state->i2c = i2c; +- state->i2c_addr = i2c_addr; +- +- /* Open i2c gate */ +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- /* check if this is a valid tuner */ +- if (zl10039_readreg(state, GENERAL, &state->id) < 0) { +- /* Close i2c gate */ +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- goto error; +- } +- /* Close i2c gate */ +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- state->id = state->id & 0x0f; +- switch (state->id) { +- case ID_ZL10039: +- strcpy(fe->ops.tuner_ops.info.name, +- "Zarlink ZL10039 DVB-S tuner"); +- break; +- default: +- dprintk("Chip ID=%x does not match a known type\n", state->id); +- goto error; +- } +- +- memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops)); +- fe->tuner_priv = state; +- dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr); +- return fe; +-error: +- kfree(state); +- return NULL; +-} +-EXPORT_SYMBOL(zl10039_attach); +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +-MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver"); +-MODULE_AUTHOR("Jan D. Louw "); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/frontends/zl10039.h b/drivers/media/dvb/frontends/zl10039.h +deleted file mode 100644 +index 5eee7ea..0000000 +--- a/drivers/media/dvb/frontends/zl10039.h ++++ /dev/null +@@ -1,40 +0,0 @@ +-/* +- Driver for Zarlink ZL10039 DVB-S tuner +- +- Copyright (C) 2007 Jan D. Louw +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef ZL10039_H +-#define ZL10039_H +- +-#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \ +- && defined(MODULE)) +-struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, +- u8 i2c_addr, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, +- u8 i2c_addr, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_ZL10039 */ +- +-#endif /* ZL10039_H */ +diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c +deleted file mode 100644 +index ac72378..0000000 +--- a/drivers/media/dvb/frontends/zl10353.c ++++ /dev/null +@@ -1,685 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T ZL10353 demodulator +- * +- * Copyright (C) 2006, 2007 Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "zl10353_priv.h" +-#include "zl10353.h" +- +-struct zl10353_state { +- struct i2c_adapter *i2c; +- struct dvb_frontend frontend; +- +- struct zl10353_config config; +- +- u32 bandwidth; +- u32 ucblocks; +- u32 frequency; +-}; +- +-static int debug; +-#define dprintk(args...) \ +- do { \ +- if (debug) printk(KERN_DEBUG "zl10353: " args); \ +- } while (0) +- +-static int debug_regs; +- +-static int zl10353_single_write(struct dvb_frontend *fe, u8 reg, u8 val) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u8 buf[2] = { reg, val }; +- struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, +- .buf = buf, .len = 2 }; +- int err = i2c_transfer(state->i2c, &msg, 1); +- if (err != 1) { +- printk("zl10353: write to reg %x failed (err = %d)!\n", reg, err); +- return err; +- } +- return 0; +-} +- +-static int zl10353_write(struct dvb_frontend *fe, const u8 ibuf[], int ilen) +-{ +- int err, i; +- for (i = 0; i < ilen - 1; i++) +- if ((err = zl10353_single_write(fe, ibuf[0] + i, ibuf[i + 1]))) +- return err; +- +- return 0; +-} +- +-static int zl10353_read_register(struct zl10353_state *state, u8 reg) +-{ +- int ret; +- u8 b0[1] = { reg }; +- u8 b1[1] = { 0 }; +- struct i2c_msg msg[2] = { { .addr = state->config.demod_address, +- .flags = 0, +- .buf = b0, .len = 1 }, +- { .addr = state->config.demod_address, +- .flags = I2C_M_RD, +- .buf = b1, .len = 1 } }; +- +- ret = i2c_transfer(state->i2c, msg, 2); +- +- if (ret != 2) { +- printk("%s: readreg error (reg=%d, ret==%i)\n", +- __func__, reg, ret); +- return ret; +- } +- +- return b1[0]; +-} +- +-static void zl10353_dump_regs(struct dvb_frontend *fe) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- int ret; +- u8 reg; +- +- /* Dump all registers. */ +- for (reg = 0; ; reg++) { +- if (reg % 16 == 0) { +- if (reg) +- printk(KERN_CONT "\n"); +- printk(KERN_DEBUG "%02x:", reg); +- } +- ret = zl10353_read_register(state, reg); +- if (ret >= 0) +- printk(KERN_CONT " %02x", (u8)ret); +- else +- printk(KERN_CONT " --"); +- if (reg == 0xff) +- break; +- } +- printk(KERN_CONT "\n"); +-} +- +-static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, +- u32 bandwidth, +- u16 *nominal_rate) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u32 adc_clock = 450560; /* 45.056 MHz */ +- u64 value; +- u8 bw = bandwidth / 1000000; +- +- if (state->config.adc_clock) +- adc_clock = state->config.adc_clock; +- +- value = (u64)10 * (1 << 23) / 7 * 125; +- value = (bw * value) + adc_clock / 2; +- do_div(value, adc_clock); +- *nominal_rate = value; +- +- dprintk("%s: bw %d, adc_clock %d => 0x%x\n", +- __func__, bw, adc_clock, *nominal_rate); +-} +- +-static void zl10353_calc_input_freq(struct dvb_frontend *fe, +- u16 *input_freq) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u32 adc_clock = 450560; /* 45.056 MHz */ +- int if2 = 361667; /* 36.1667 MHz */ +- int ife; +- u64 value; +- +- if (state->config.adc_clock) +- adc_clock = state->config.adc_clock; +- if (state->config.if2) +- if2 = state->config.if2; +- +- if (adc_clock >= if2 * 2) +- ife = if2; +- else { +- ife = adc_clock - (if2 % adc_clock); +- if (ife > adc_clock / 2) +- ife = adc_clock - ife; +- } +- value = (u64)65536 * ife + adc_clock / 2; +- do_div(value, adc_clock); +- *input_freq = -value; +- +- dprintk("%s: if2 %d, ife %d, adc_clock %d => %d / 0x%x\n", +- __func__, if2, ife, adc_clock, -(int)value, *input_freq); +-} +- +-static int zl10353_sleep(struct dvb_frontend *fe) +-{ +- static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 }; +- +- zl10353_write(fe, zl10353_softdown, sizeof(zl10353_softdown)); +- return 0; +-} +- +-static int zl10353_set_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct zl10353_state *state = fe->demodulator_priv; +- u16 nominal_rate, input_freq; +- u8 pllbuf[6] = { 0x67 }, acq_ctl = 0; +- u16 tps = 0; +- +- state->frequency = c->frequency; +- +- zl10353_single_write(fe, RESET, 0x80); +- udelay(200); +- zl10353_single_write(fe, 0xEA, 0x01); +- udelay(200); +- zl10353_single_write(fe, 0xEA, 0x00); +- +- zl10353_single_write(fe, AGC_TARGET, 0x28); +- +- if (c->transmission_mode != TRANSMISSION_MODE_AUTO) +- acq_ctl |= (1 << 0); +- if (c->guard_interval != GUARD_INTERVAL_AUTO) +- acq_ctl |= (1 << 1); +- zl10353_single_write(fe, ACQ_CTL, acq_ctl); +- +- switch (c->bandwidth_hz) { +- case 6000000: +- /* These are extrapolated from the 7 and 8MHz values */ +- zl10353_single_write(fe, MCLK_RATIO, 0x97); +- zl10353_single_write(fe, 0x64, 0x34); +- zl10353_single_write(fe, 0xcc, 0xdd); +- break; +- case 7000000: +- zl10353_single_write(fe, MCLK_RATIO, 0x86); +- zl10353_single_write(fe, 0x64, 0x35); +- zl10353_single_write(fe, 0xcc, 0x73); +- break; +- default: +- c->bandwidth_hz = 8000000; +- /* fall though */ +- case 8000000: +- zl10353_single_write(fe, MCLK_RATIO, 0x75); +- zl10353_single_write(fe, 0x64, 0x36); +- zl10353_single_write(fe, 0xcc, 0x73); +- } +- +- zl10353_calc_nominal_rate(fe, c->bandwidth_hz, &nominal_rate); +- zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate)); +- zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate)); +- state->bandwidth = c->bandwidth_hz; +- +- zl10353_calc_input_freq(fe, &input_freq); +- zl10353_single_write(fe, INPUT_FREQ_1, msb(input_freq)); +- zl10353_single_write(fe, INPUT_FREQ_0, lsb(input_freq)); +- +- /* Hint at TPS settings */ +- switch (c->code_rate_HP) { +- case FEC_2_3: +- tps |= (1 << 7); +- break; +- case FEC_3_4: +- tps |= (2 << 7); +- break; +- case FEC_5_6: +- tps |= (3 << 7); +- break; +- case FEC_7_8: +- tps |= (4 << 7); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- break; +- default: +- return -EINVAL; +- } +- +- switch (c->code_rate_LP) { +- case FEC_2_3: +- tps |= (1 << 4); +- break; +- case FEC_3_4: +- tps |= (2 << 4); +- break; +- case FEC_5_6: +- tps |= (3 << 4); +- break; +- case FEC_7_8: +- tps |= (4 << 4); +- break; +- case FEC_1_2: +- case FEC_AUTO: +- break; +- case FEC_NONE: +- if (c->hierarchy == HIERARCHY_AUTO || +- c->hierarchy == HIERARCHY_NONE) +- break; +- default: +- return -EINVAL; +- } +- +- switch (c->modulation) { +- case QPSK: +- break; +- case QAM_AUTO: +- case QAM_16: +- tps |= (1 << 13); +- break; +- case QAM_64: +- tps |= (2 << 13); +- break; +- default: +- return -EINVAL; +- } +- +- switch (c->transmission_mode) { +- case TRANSMISSION_MODE_2K: +- case TRANSMISSION_MODE_AUTO: +- break; +- case TRANSMISSION_MODE_8K: +- tps |= (1 << 0); +- break; +- default: +- return -EINVAL; +- } +- +- switch (c->guard_interval) { +- case GUARD_INTERVAL_1_32: +- case GUARD_INTERVAL_AUTO: +- break; +- case GUARD_INTERVAL_1_16: +- tps |= (1 << 2); +- break; +- case GUARD_INTERVAL_1_8: +- tps |= (2 << 2); +- break; +- case GUARD_INTERVAL_1_4: +- tps |= (3 << 2); +- break; +- default: +- return -EINVAL; +- } +- +- switch (c->hierarchy) { +- case HIERARCHY_AUTO: +- case HIERARCHY_NONE: +- break; +- case HIERARCHY_1: +- tps |= (1 << 10); +- break; +- case HIERARCHY_2: +- tps |= (2 << 10); +- break; +- case HIERARCHY_4: +- tps |= (3 << 10); +- break; +- default: +- return -EINVAL; +- } +- +- zl10353_single_write(fe, TPS_GIVEN_1, msb(tps)); +- zl10353_single_write(fe, TPS_GIVEN_0, lsb(tps)); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- +- /* +- * If there is no tuner attached to the secondary I2C bus, we call +- * set_params to program a potential tuner attached somewhere else. +- * Otherwise, we update the PLL registers via calc_regs. +- */ +- if (state->config.no_tuner) { +- if (fe->ops.tuner_ops.set_params) { +- fe->ops.tuner_ops.set_params(fe); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 0); +- } +- } else if (fe->ops.tuner_ops.calc_regs) { +- fe->ops.tuner_ops.calc_regs(fe, pllbuf + 1, 5); +- pllbuf[1] <<= 1; +- zl10353_write(fe, pllbuf, sizeof(pllbuf)); +- } +- +- zl10353_single_write(fe, 0x5F, 0x13); +- +- /* If no attached tuner or invalid PLL registers, just start the FSM. */ +- if (state->config.no_tuner || fe->ops.tuner_ops.calc_regs == NULL) +- zl10353_single_write(fe, FSM_GO, 0x01); +- else +- zl10353_single_write(fe, TUNER_GO, 0x01); +- +- return 0; +-} +- +-static int zl10353_get_parameters(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct zl10353_state *state = fe->demodulator_priv; +- int s6, s9; +- u16 tps; +- static const u8 tps_fec_to_api[8] = { +- FEC_1_2, +- FEC_2_3, +- FEC_3_4, +- FEC_5_6, +- FEC_7_8, +- FEC_AUTO, +- FEC_AUTO, +- FEC_AUTO +- }; +- +- s6 = zl10353_read_register(state, STATUS_6); +- s9 = zl10353_read_register(state, STATUS_9); +- if (s6 < 0 || s9 < 0) +- return -EREMOTEIO; +- if ((s6 & (1 << 5)) == 0 || (s9 & (1 << 4)) == 0) +- return -EINVAL; /* no FE or TPS lock */ +- +- tps = zl10353_read_register(state, TPS_RECEIVED_1) << 8 | +- zl10353_read_register(state, TPS_RECEIVED_0); +- +- c->code_rate_HP = tps_fec_to_api[(tps >> 7) & 7]; +- c->code_rate_LP = tps_fec_to_api[(tps >> 4) & 7]; +- +- switch ((tps >> 13) & 3) { +- case 0: +- c->modulation = QPSK; +- break; +- case 1: +- c->modulation = QAM_16; +- break; +- case 2: +- c->modulation = QAM_64; +- break; +- default: +- c->modulation = QAM_AUTO; +- break; +- } +- +- c->transmission_mode = (tps & 0x01) ? TRANSMISSION_MODE_8K : +- TRANSMISSION_MODE_2K; +- +- switch ((tps >> 2) & 3) { +- case 0: +- c->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- c->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- c->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- c->guard_interval = GUARD_INTERVAL_1_4; +- break; +- default: +- c->guard_interval = GUARD_INTERVAL_AUTO; +- break; +- } +- +- switch ((tps >> 10) & 7) { +- case 0: +- c->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- c->hierarchy = HIERARCHY_1; +- break; +- case 2: +- c->hierarchy = HIERARCHY_2; +- break; +- case 3: +- c->hierarchy = HIERARCHY_4; +- break; +- default: +- c->hierarchy = HIERARCHY_AUTO; +- break; +- } +- +- c->frequency = state->frequency; +- c->bandwidth_hz = state->bandwidth; +- c->inversion = INVERSION_AUTO; +- +- return 0; +-} +- +-static int zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- int s6, s7, s8; +- +- if ((s6 = zl10353_read_register(state, STATUS_6)) < 0) +- return -EREMOTEIO; +- if ((s7 = zl10353_read_register(state, STATUS_7)) < 0) +- return -EREMOTEIO; +- if ((s8 = zl10353_read_register(state, STATUS_8)) < 0) +- return -EREMOTEIO; +- +- *status = 0; +- if (s6 & (1 << 2)) +- *status |= FE_HAS_CARRIER; +- if (s6 & (1 << 1)) +- *status |= FE_HAS_VITERBI; +- if (s6 & (1 << 5)) +- *status |= FE_HAS_LOCK; +- if (s7 & (1 << 4)) +- *status |= FE_HAS_SYNC; +- if (s8 & (1 << 6)) +- *status |= FE_HAS_SIGNAL; +- +- if ((*status & (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) != +- (FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC)) +- *status &= ~FE_HAS_LOCK; +- +- return 0; +-} +- +-static int zl10353_read_ber(struct dvb_frontend *fe, u32 *ber) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- +- *ber = zl10353_read_register(state, RS_ERR_CNT_2) << 16 | +- zl10353_read_register(state, RS_ERR_CNT_1) << 8 | +- zl10353_read_register(state, RS_ERR_CNT_0); +- +- return 0; +-} +- +-static int zl10353_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- +- u16 signal = zl10353_read_register(state, AGC_GAIN_1) << 10 | +- zl10353_read_register(state, AGC_GAIN_0) << 2 | 3; +- +- *strength = ~signal; +- +- return 0; +-} +- +-static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u8 _snr; +- +- if (debug_regs) +- zl10353_dump_regs(fe); +- +- _snr = zl10353_read_register(state, SNR); +- *snr = (_snr << 8) | _snr; +- +- return 0; +-} +- +-static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u32 ubl = 0; +- +- ubl = zl10353_read_register(state, RS_UBC_1) << 8 | +- zl10353_read_register(state, RS_UBC_0); +- +- state->ucblocks += ubl; +- *ucblocks = state->ucblocks; +- +- return 0; +-} +- +-static int zl10353_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings +- *fe_tune_settings) +-{ +- fe_tune_settings->min_delay_ms = 1000; +- fe_tune_settings->step_size = 0; +- fe_tune_settings->max_drift = 0; +- +- return 0; +-} +- +-static int zl10353_init(struct dvb_frontend *fe) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F }; +- int rc = 0; +- +- if (debug_regs) +- zl10353_dump_regs(fe); +- if (state->config.parallel_ts) +- zl10353_reset_attach[2] &= ~0x20; +- if (state->config.clock_ctl_1) +- zl10353_reset_attach[3] = state->config.clock_ctl_1; +- if (state->config.pll_0) +- zl10353_reset_attach[4] = state->config.pll_0; +- +- /* Do a "hard" reset if not already done */ +- if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || +- zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { +- rc = zl10353_write(fe, zl10353_reset_attach, +- sizeof(zl10353_reset_attach)); +- if (debug_regs) +- zl10353_dump_regs(fe); +- } +- +- return 0; +-} +- +-static int zl10353_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- u8 val = 0x0a; +- +- if (state->config.disable_i2c_gate_ctrl) { +- /* No tuner attached to the internal I2C bus */ +- /* If set enable I2C bridge, the main I2C bus stopped hardly */ +- return 0; +- } +- +- if (enable) +- val |= 0x10; +- +- return zl10353_single_write(fe, 0x62, val); +-} +- +-static void zl10353_release(struct dvb_frontend *fe) +-{ +- struct zl10353_state *state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops zl10353_ops; +- +-struct dvb_frontend *zl10353_attach(const struct zl10353_config *config, +- struct i2c_adapter *i2c) +-{ +- struct zl10353_state *state = NULL; +- int id; +- +- /* allocate memory for the internal state */ +- state = kzalloc(sizeof(struct zl10353_state), GFP_KERNEL); +- if (state == NULL) +- goto error; +- +- /* setup the state */ +- state->i2c = i2c; +- memcpy(&state->config, config, sizeof(struct zl10353_config)); +- +- /* check if the demod is there */ +- id = zl10353_read_register(state, CHIP_ID); +- if ((id != ID_ZL10353) && (id != ID_CE6230) && (id != ID_CE6231)) +- goto error; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &zl10353_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- +- return &state->frontend; +-error: +- kfree(state); +- return NULL; +-} +- +-static struct dvb_frontend_ops zl10353_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "Zarlink ZL10353 DVB-T", +- .frequency_min = 174000000, +- .frequency_max = 862000000, +- .frequency_stepsize = 166667, +- .frequency_tolerance = 0, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | +- FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | +- FE_CAN_FEC_AUTO | +- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO | FE_CAN_RECOVER | +- FE_CAN_MUTE_TS +- }, +- +- .release = zl10353_release, +- +- .init = zl10353_init, +- .sleep = zl10353_sleep, +- .i2c_gate_ctrl = zl10353_i2c_gate_ctrl, +- .write = zl10353_write, +- +- .set_frontend = zl10353_set_parameters, +- .get_frontend = zl10353_get_parameters, +- .get_tune_settings = zl10353_get_tune_settings, +- +- .read_status = zl10353_read_status, +- .read_ber = zl10353_read_ber, +- .read_signal_strength = zl10353_read_signal_strength, +- .read_snr = zl10353_read_snr, +- .read_ucblocks = zl10353_read_ucblocks, +-}; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); +- +-module_param(debug_regs, int, 0644); +-MODULE_PARM_DESC(debug_regs, "Turn on/off frontend register dumps (default:off)."); +- +-MODULE_DESCRIPTION("Zarlink ZL10353 DVB-T demodulator driver"); +-MODULE_AUTHOR("Chris Pascoe"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(zl10353_attach); +diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h +deleted file mode 100644 +index 6e3ca9e..0000000 +--- a/drivers/media/dvb/frontends/zl10353.h ++++ /dev/null +@@ -1,62 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T ZL10353 demodulator +- * +- * Copyright (C) 2006, 2007 Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= +- */ +- +-#ifndef ZL10353_H +-#define ZL10353_H +- +-#include +- +-struct zl10353_config +-{ +- /* demodulator's I2C address */ +- u8 demod_address; +- +- /* frequencies in units of 0.1kHz */ +- int adc_clock; /* default: 450560 (45.056 MHz) */ +- int if2; /* default: 361667 (36.1667 MHz) */ +- +- /* set if no pll is connected to the secondary i2c bus */ +- int no_tuner; +- +- /* set if parallel ts output is required */ +- int parallel_ts; +- +- /* set if i2c_gate_ctrl disable is required */ +- u8 disable_i2c_gate_ctrl:1; +- +- /* clock control registers (0x51-0x54) */ +- u8 clock_ctl_1; /* default: 0x46 */ +- u8 pll_0; /* default: 0x15 */ +-}; +- +-#if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE)) +-extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, +- struct i2c_adapter *i2c); +-#else +-static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config, +- struct i2c_adapter *i2c) +-{ +- printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); +- return NULL; +-} +-#endif /* CONFIG_DVB_ZL10353 */ +- +-#endif /* ZL10353_H */ +diff --git a/drivers/media/dvb/frontends/zl10353_priv.h b/drivers/media/dvb/frontends/zl10353_priv.h +deleted file mode 100644 +index e0dd1d3..0000000 +--- a/drivers/media/dvb/frontends/zl10353_priv.h ++++ /dev/null +@@ -1,79 +0,0 @@ +-/* +- * Driver for Zarlink DVB-T ZL10353 demodulator +- * +- * Copyright (C) 2006, 2007 Christopher Pascoe +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef _ZL10353_PRIV_ +-#define _ZL10353_PRIV_ +- +-#define ID_ZL10353 0x14 /* Zarlink ZL10353 */ +-#define ID_CE6230 0x18 /* Intel CE6230 */ +-#define ID_CE6231 0x19 /* Intel CE6231 */ +- +-#define msb(x) (((x) >> 8) & 0xff) +-#define lsb(x) ((x) & 0xff) +- +-enum zl10353_reg_addr { +- INTERRUPT_0 = 0x00, +- INTERRUPT_1 = 0x01, +- INTERRUPT_2 = 0x02, +- INTERRUPT_3 = 0x03, +- INTERRUPT_4 = 0x04, +- INTERRUPT_5 = 0x05, +- STATUS_6 = 0x06, +- STATUS_7 = 0x07, +- STATUS_8 = 0x08, +- STATUS_9 = 0x09, +- AGC_GAIN_1 = 0x0A, +- AGC_GAIN_0 = 0x0B, +- SNR = 0x10, +- RS_ERR_CNT_2 = 0x11, +- RS_ERR_CNT_1 = 0x12, +- RS_ERR_CNT_0 = 0x13, +- RS_UBC_1 = 0x14, +- RS_UBC_0 = 0x15, +- TPS_RECEIVED_1 = 0x1D, +- TPS_RECEIVED_0 = 0x1E, +- TPS_CURRENT_1 = 0x1F, +- TPS_CURRENT_0 = 0x20, +- CLOCK_CTL_0 = 0x51, +- CLOCK_CTL_1 = 0x52, +- PLL_0 = 0x53, +- PLL_1 = 0x54, +- RESET = 0x55, +- AGC_TARGET = 0x56, +- MCLK_RATIO = 0x5C, +- ACQ_CTL = 0x5E, +- TRL_NOMINAL_RATE_1 = 0x65, +- TRL_NOMINAL_RATE_0 = 0x66, +- INPUT_FREQ_1 = 0x6C, +- INPUT_FREQ_0 = 0x6D, +- TPS_GIVEN_1 = 0x6E, +- TPS_GIVEN_0 = 0x6F, +- TUNER_GO = 0x70, +- FSM_GO = 0x71, +- CHIP_ID = 0x7F, +- CHAN_STEP_1 = 0xE4, +- CHAN_STEP_0 = 0xE5, +- OFDM_LOCK_TIME = 0xE7, +- FEC_LOCK_TIME = 0xE8, +- ACQ_DELAY = 0xE9, +-}; +- +-#endif /* _ZL10353_PRIV_ */ +diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig +deleted file mode 100644 +index a13a505..0000000 +--- a/drivers/media/dvb/mantis/Kconfig ++++ /dev/null +@@ -1,38 +0,0 @@ +-config MANTIS_CORE +- tristate "Mantis/Hopper PCI bridge based devices" +- depends on PCI && I2C && INPUT && RC_CORE +- +- help +- Support for PCI cards based on the Mantis and Hopper PCi bridge. +- +- Say Y if you own such a device and want to use it. +- +-config DVB_MANTIS +- tristate "MANTIS based cards" +- depends on MANTIS_CORE && DVB_CORE && PCI && I2C +- select DVB_MB86A16 if !DVB_FE_CUSTOMISE +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_STB0899 if !DVB_FE_CUSTOMISE +- select DVB_STB6100 if !DVB_FE_CUSTOMISE +- select DVB_TDA665x if !DVB_FE_CUSTOMISE +- select DVB_TDA10021 if !DVB_FE_CUSTOMISE +- select DVB_TDA10023 if !DVB_FE_CUSTOMISE +- select DVB_PLL +- help +- Support for PCI cards based on the Mantis PCI bridge. +- Say Y when you have a Mantis based DVB card and want to use it. +- +- If unsure say N. +- +-config DVB_HOPPER +- tristate "HOPPER based cards" +- depends on MANTIS_CORE && DVB_CORE && PCI && I2C +- select DVB_ZL10353 if !DVB_FE_CUSTOMISE +- select DVB_PLL +- help +- Support for PCI cards based on the Hopper PCI bridge. +- Say Y when you have a Hopper based DVB card and want to use it. +- +- If unsure say N +diff --git a/drivers/media/dvb/mantis/Makefile b/drivers/media/dvb/mantis/Makefile +deleted file mode 100644 +index ec8116d..0000000 +--- a/drivers/media/dvb/mantis/Makefile ++++ /dev/null +@@ -1,28 +0,0 @@ +-mantis_core-objs := mantis_ioc.o \ +- mantis_uart.o \ +- mantis_dma.o \ +- mantis_pci.o \ +- mantis_i2c.o \ +- mantis_dvb.o \ +- mantis_evm.o \ +- mantis_hif.o \ +- mantis_ca.o \ +- mantis_pcmcia.o \ +- mantis_input.o +- +-mantis-objs := mantis_cards.o \ +- mantis_vp1033.o \ +- mantis_vp1034.o \ +- mantis_vp1041.o \ +- mantis_vp2033.o \ +- mantis_vp2040.o \ +- mantis_vp3030.o +- +-hopper-objs := hopper_cards.o \ +- hopper_vp3028.o +- +-obj-$(CONFIG_MANTIS_CORE) += mantis_core.o +-obj-$(CONFIG_DVB_MANTIS) += mantis.o +-obj-$(CONFIG_DVB_HOPPER) += hopper.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c +deleted file mode 100644 +index 71622f6..0000000 +--- a/drivers/media/dvb/mantis/hopper_cards.c ++++ /dev/null +@@ -1,278 +0,0 @@ +-/* +- Hopper PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "hopper_vp3028.h" +-#include "mantis_dma.h" +-#include "mantis_dvb.h" +-#include "mantis_uart.h" +-#include "mantis_ioc.h" +-#include "mantis_pci.h" +-#include "mantis_i2c.h" +-#include "mantis_reg.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); +- +-#define DRIVER_NAME "Hopper" +- +-static char *label[10] = { +- "DMA", +- "IRQ-0", +- "IRQ-1", +- "OCERR", +- "PABRT", +- "RIPRR", +- "PPERR", +- "FTRGT", +- "RISCI", +- "RACK" +-}; +- +-static int devs; +- +-static irqreturn_t hopper_irq_handler(int irq, void *dev_id) +-{ +- u32 stat = 0, mask = 0, lstat = 0; +- u32 rst_stat = 0, rst_mask = 0; +- +- struct mantis_pci *mantis; +- struct mantis_ca *ca; +- +- mantis = (struct mantis_pci *) dev_id; +- if (unlikely(mantis == NULL)) { +- dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); +- return IRQ_NONE; +- } +- ca = mantis->mantis_ca; +- +- stat = mmread(MANTIS_INT_STAT); +- mask = mmread(MANTIS_INT_MASK); +- lstat = stat & ~MANTIS_INT_RISCSTAT; +- if (!(stat & mask)) +- return IRQ_NONE; +- +- rst_mask = MANTIS_GPIF_WRACK | +- MANTIS_GPIF_OTHERR | +- MANTIS_SBUF_WSTO | +- MANTIS_GPIF_EXTIRQ; +- +- rst_stat = mmread(MANTIS_GPIF_STATUS); +- rst_stat &= rst_mask; +- mmwrite(rst_stat, MANTIS_GPIF_STATUS); +- +- mantis->mantis_int_stat = stat; +- mantis->mantis_int_mask = mask; +- dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); +- if (stat & MANTIS_INT_RISCEN) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); +- } +- if (stat & MANTIS_INT_IRQ0) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); +- mantis->gpif_status = rst_stat; +- wake_up(&ca->hif_write_wq); +- schedule_work(&ca->hif_evm_work); +- } +- if (stat & MANTIS_INT_IRQ1) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); +- schedule_work(&mantis->uart_work); +- } +- if (stat & MANTIS_INT_OCERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); +- } +- if (stat & MANTIS_INT_PABORT) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); +- } +- if (stat & MANTIS_INT_RIPERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); +- } +- if (stat & MANTIS_INT_PPERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); +- } +- if (stat & MANTIS_INT_FTRGT) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); +- } +- if (stat & MANTIS_INT_RISCI) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); +- mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; +- tasklet_schedule(&mantis->tasklet); +- } +- if (stat & MANTIS_INT_I2CDONE) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); +- wake_up(&mantis->i2c_wq); +- } +- mmwrite(stat, MANTIS_INT_STAT); +- stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | +- MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | +- MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | +- MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | +- MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | +- MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | +- MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | +- MANTIS_INT_PABORT | MANTIS_INT_RIPERR | +- MANTIS_INT_PPERR | MANTIS_INT_FTRGT | +- MANTIS_INT_RISCI); +- +- if (stat) +- dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); +- +- dprintk(MANTIS_DEBUG, 0, "\n"); +- return IRQ_HANDLED; +-} +- +-static int __devinit hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +-{ +- struct mantis_pci *mantis; +- struct mantis_hwconfig *config; +- int err = 0; +- +- mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); +- if (mantis == NULL) { +- printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); +- err = -ENOMEM; +- goto fail0; +- } +- +- mantis->num = devs; +- mantis->verbose = verbose; +- mantis->pdev = pdev; +- config = (struct mantis_hwconfig *) pci_id->driver_data; +- config->irq_handler = &hopper_irq_handler; +- mantis->hwconfig = config; +- +- err = mantis_pci_init(mantis); +- if (err) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); +- goto fail1; +- } +- +- err = mantis_stream_control(mantis, STREAM_TO_HIF); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); +- goto fail1; +- } +- +- err = mantis_i2c_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); +- goto fail2; +- } +- +- err = mantis_get_mac(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); +- goto fail2; +- } +- +- err = mantis_dma_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); +- goto fail3; +- } +- +- err = mantis_dvb_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); +- goto fail4; +- } +- devs++; +- +- return err; +- +-fail4: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); +- mantis_dma_exit(mantis); +- +-fail3: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); +- mantis_i2c_exit(mantis); +- +-fail2: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); +- mantis_pci_exit(mantis); +- +-fail1: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); +- kfree(mantis); +- +-fail0: +- return err; +-} +- +-static void __devexit hopper_pci_remove(struct pci_dev *pdev) +-{ +- struct mantis_pci *mantis = pci_get_drvdata(pdev); +- +- if (mantis) { +- mantis_dvb_exit(mantis); +- mantis_dma_exit(mantis); +- mantis_i2c_exit(mantis); +- mantis_pci_exit(mantis); +- kfree(mantis); +- } +- return; +- +-} +- +-static struct pci_device_id hopper_pci_table[] = { +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config), +- { } +-}; +- +-MODULE_DEVICE_TABLE(pci, hopper_pci_table); +- +-static struct pci_driver hopper_pci_driver = { +- .name = DRIVER_NAME, +- .id_table = hopper_pci_table, +- .probe = hopper_pci_probe, +- .remove = hopper_pci_remove, +-}; +- +-static int __devinit hopper_init(void) +-{ +- return pci_register_driver(&hopper_pci_driver); +-} +- +-static void __devexit hopper_exit(void) +-{ +- return pci_unregister_driver(&hopper_pci_driver); +-} +- +-module_init(hopper_init); +-module_exit(hopper_exit); +- +-MODULE_DESCRIPTION("HOPPER driver"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/mantis/hopper_vp3028.c b/drivers/media/dvb/mantis/hopper_vp3028.c +deleted file mode 100644 +index 68a29f8..0000000 +--- a/drivers/media/dvb/mantis/hopper_vp3028.c ++++ /dev/null +@@ -1,88 +0,0 @@ +-/* +- Hopper VP-3028 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "zl10353.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "hopper_vp3028.h" +- +-struct zl10353_config hopper_vp3028_config = { +- .demod_address = 0x0f, +-}; +- +-#define MANTIS_MODEL_NAME "VP-3028" +-#define MANTIS_DEV_TYPE "DVB-T" +- +-static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- struct mantis_hwconfig *config = mantis->hwconfig; +- int err = 0; +- +- mantis_gpio_set_bits(mantis, config->reset, 0); +- msleep(100); +- err = mantis_frontend_power(mantis, POWER_ON); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->reset, 1); +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- msleep(250); +- dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); +- fe = dvb_attach(zl10353_attach, &hopper_vp3028_config, adapter); +- +- if (!fe) +- return -1; +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- dprintk(MANTIS_ERROR, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp3028_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_188, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp3028_frontend_init, +- .power = GPIF_A00, +- .reset = GPIF_A03, +-}; +diff --git a/drivers/media/dvb/mantis/hopper_vp3028.h b/drivers/media/dvb/mantis/hopper_vp3028.h +deleted file mode 100644 +index 5723949..0000000 +--- a/drivers/media/dvb/mantis/hopper_vp3028.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Hopper VP-3028 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP3028_H +-#define __MANTIS_VP3028_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_3028_DVB_T 0x0028 +- +-extern struct mantis_hwconfig vp3028_config; +- +-#endif /* __MANTIS_VP3028_H */ +diff --git a/drivers/media/dvb/mantis/mantis_ca.c b/drivers/media/dvb/mantis/mantis_ca.c +deleted file mode 100644 +index 3d70469..0000000 +--- a/drivers/media/dvb/mantis/mantis_ca.c ++++ /dev/null +@@ -1,209 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_link.h" +-#include "mantis_hif.h" +-#include "mantis_reg.h" +- +-#include "mantis_ca.h" +- +-static int mantis_ca_read_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Read", slot); +- +- if (slot != 0) +- return -EINVAL; +- +- return mantis_hif_read_mem(ca, addr); +-} +- +-static int mantis_ca_write_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Write", slot); +- +- if (slot != 0) +- return -EINVAL; +- +- return mantis_hif_write_mem(ca, addr, data); +-} +- +-static int mantis_ca_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Read", slot); +- +- if (slot != 0) +- return -EINVAL; +- +- return mantis_hif_read_iom(ca, addr); +-} +- +-static int mantis_ca_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Write", slot); +- +- if (slot != 0) +- return -EINVAL; +- +- return mantis_hif_write_iom(ca, addr, data); +-} +- +-static int mantis_ca_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot RESET", slot); +- udelay(500); /* Wait.. */ +- mmwrite(0xda, MANTIS_PCMCIA_RESET); /* Leading edge assert */ +- udelay(500); +- mmwrite(0x00, MANTIS_PCMCIA_RESET); /* Trailing edge deassert */ +- msleep(1000); +- dvb_ca_en50221_camready_irq(&ca->en50221, 0); +- +- return 0; +-} +- +-static int mantis_ca_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot shutdown", slot); +- +- return 0; +-} +- +-static int mantis_ts_control(struct dvb_ca_en50221 *en50221, int slot) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): TS control", slot); +-/* mantis_set_direction(mantis, 1); */ /* Enable TS through CAM */ +- +- return 0; +-} +- +-static int mantis_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) +-{ +- struct mantis_ca *ca = en50221->data; +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Slot(%d): Poll Slot status", slot); +- +- if (ca->slot_state == MODULE_INSERTED) { +- dprintk(MANTIS_DEBUG, 1, "CA Module present and ready"); +- return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; +- } else { +- dprintk(MANTIS_DEBUG, 1, "CA Module not present or not ready"); +- } +- +- return 0; +-} +- +-int mantis_ca_init(struct mantis_pci *mantis) +-{ +- struct dvb_adapter *dvb_adapter = &mantis->dvb_adapter; +- struct mantis_ca *ca; +- int ca_flags = 0, result; +- +- dprintk(MANTIS_DEBUG, 1, "Initializing Mantis CA"); +- ca = kzalloc(sizeof(struct mantis_ca), GFP_KERNEL); +- if (!ca) { +- dprintk(MANTIS_ERROR, 1, "Out of memory!, exiting .."); +- result = -ENOMEM; +- goto err; +- } +- +- ca->ca_priv = mantis; +- mantis->mantis_ca = ca; +- ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE; +- /* register CA interface */ +- ca->en50221.owner = THIS_MODULE; +- ca->en50221.read_attribute_mem = mantis_ca_read_attr_mem; +- ca->en50221.write_attribute_mem = mantis_ca_write_attr_mem; +- ca->en50221.read_cam_control = mantis_ca_read_cam_ctl; +- ca->en50221.write_cam_control = mantis_ca_write_cam_ctl; +- ca->en50221.slot_reset = mantis_ca_slot_reset; +- ca->en50221.slot_shutdown = mantis_ca_slot_shutdown; +- ca->en50221.slot_ts_enable = mantis_ts_control; +- ca->en50221.poll_slot_status = mantis_slot_status; +- ca->en50221.data = ca; +- +- mutex_init(&ca->ca_lock); +- +- init_waitqueue_head(&ca->hif_data_wq); +- init_waitqueue_head(&ca->hif_opdone_wq); +- init_waitqueue_head(&ca->hif_write_wq); +- +- dprintk(MANTIS_ERROR, 1, "Registering EN50221 device"); +- result = dvb_ca_en50221_init(dvb_adapter, &ca->en50221, ca_flags, 1); +- if (result != 0) { +- dprintk(MANTIS_ERROR, 1, "EN50221: Initialization failed <%d>", result); +- goto err; +- } +- dprintk(MANTIS_ERROR, 1, "Registered EN50221 device"); +- mantis_evmgr_init(ca); +- return 0; +-err: +- kfree(ca); +- return result; +-} +-EXPORT_SYMBOL_GPL(mantis_ca_init); +- +-void mantis_ca_exit(struct mantis_pci *mantis) +-{ +- struct mantis_ca *ca = mantis->mantis_ca; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); +- +- mantis_evmgr_exit(ca); +- dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); +- if (ca) +- dvb_ca_en50221_release(&ca->en50221); +- +- kfree(ca); +-} +-EXPORT_SYMBOL_GPL(mantis_ca_exit); +diff --git a/drivers/media/dvb/mantis/mantis_ca.h b/drivers/media/dvb/mantis/mantis_ca.h +deleted file mode 100644 +index dc63e55..0000000 +--- a/drivers/media/dvb/mantis/mantis_ca.h ++++ /dev/null +@@ -1,27 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_CA_H +-#define __MANTIS_CA_H +- +-extern int mantis_ca_init(struct mantis_pci *mantis); +-extern void mantis_ca_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_CA_H */ +diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c +deleted file mode 100644 +index c2bb90b..0000000 +--- a/drivers/media/dvb/mantis/mantis_cards.c ++++ /dev/null +@@ -1,308 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +- +-#include "mantis_vp1033.h" +-#include "mantis_vp1034.h" +-#include "mantis_vp1041.h" +-#include "mantis_vp2033.h" +-#include "mantis_vp2040.h" +-#include "mantis_vp3030.h" +- +-#include "mantis_dma.h" +-#include "mantis_ca.h" +-#include "mantis_dvb.h" +-#include "mantis_uart.h" +-#include "mantis_ioc.h" +-#include "mantis_pci.h" +-#include "mantis_i2c.h" +-#include "mantis_reg.h" +- +-static unsigned int verbose; +-module_param(verbose, int, 0644); +-MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); +- +-static int devs; +- +-#define DRIVER_NAME "Mantis" +- +-static char *label[10] = { +- "DMA", +- "IRQ-0", +- "IRQ-1", +- "OCERR", +- "PABRT", +- "RIPRR", +- "PPERR", +- "FTRGT", +- "RISCI", +- "RACK" +-}; +- +-static irqreturn_t mantis_irq_handler(int irq, void *dev_id) +-{ +- u32 stat = 0, mask = 0, lstat = 0; +- u32 rst_stat = 0, rst_mask = 0; +- +- struct mantis_pci *mantis; +- struct mantis_ca *ca; +- +- mantis = (struct mantis_pci *) dev_id; +- if (unlikely(mantis == NULL)) { +- dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); +- return IRQ_NONE; +- } +- ca = mantis->mantis_ca; +- +- stat = mmread(MANTIS_INT_STAT); +- mask = mmread(MANTIS_INT_MASK); +- lstat = stat & ~MANTIS_INT_RISCSTAT; +- if (!(stat & mask)) +- return IRQ_NONE; +- +- rst_mask = MANTIS_GPIF_WRACK | +- MANTIS_GPIF_OTHERR | +- MANTIS_SBUF_WSTO | +- MANTIS_GPIF_EXTIRQ; +- +- rst_stat = mmread(MANTIS_GPIF_STATUS); +- rst_stat &= rst_mask; +- mmwrite(rst_stat, MANTIS_GPIF_STATUS); +- +- mantis->mantis_int_stat = stat; +- mantis->mantis_int_mask = mask; +- dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); +- if (stat & MANTIS_INT_RISCEN) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); +- } +- if (stat & MANTIS_INT_IRQ0) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); +- mantis->gpif_status = rst_stat; +- wake_up(&ca->hif_write_wq); +- schedule_work(&ca->hif_evm_work); +- } +- if (stat & MANTIS_INT_IRQ1) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); +- schedule_work(&mantis->uart_work); +- } +- if (stat & MANTIS_INT_OCERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); +- } +- if (stat & MANTIS_INT_PABORT) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); +- } +- if (stat & MANTIS_INT_RIPERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); +- } +- if (stat & MANTIS_INT_PPERR) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); +- } +- if (stat & MANTIS_INT_FTRGT) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); +- } +- if (stat & MANTIS_INT_RISCI) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); +- mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; +- tasklet_schedule(&mantis->tasklet); +- } +- if (stat & MANTIS_INT_I2CDONE) { +- dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); +- wake_up(&mantis->i2c_wq); +- } +- mmwrite(stat, MANTIS_INT_STAT); +- stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | +- MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | +- MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | +- MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | +- MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | +- MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | +- MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | +- MANTIS_INT_PABORT | MANTIS_INT_RIPERR | +- MANTIS_INT_PPERR | MANTIS_INT_FTRGT | +- MANTIS_INT_RISCI); +- +- if (stat) +- dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); +- +- dprintk(MANTIS_DEBUG, 0, "\n"); +- return IRQ_HANDLED; +-} +- +-static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) +-{ +- struct mantis_pci *mantis; +- struct mantis_hwconfig *config; +- int err = 0; +- +- mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); +- if (mantis == NULL) { +- printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); +- err = -ENOMEM; +- goto fail0; +- } +- +- mantis->num = devs; +- mantis->verbose = verbose; +- mantis->pdev = pdev; +- config = (struct mantis_hwconfig *) pci_id->driver_data; +- config->irq_handler = &mantis_irq_handler; +- mantis->hwconfig = config; +- +- err = mantis_pci_init(mantis); +- if (err) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); +- goto fail1; +- } +- +- err = mantis_stream_control(mantis, STREAM_TO_HIF); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); +- goto fail1; +- } +- +- err = mantis_i2c_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); +- goto fail2; +- } +- +- err = mantis_get_mac(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); +- goto fail2; +- } +- +- err = mantis_dma_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); +- goto fail3; +- } +- +- err = mantis_dvb_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); +- goto fail4; +- } +- err = mantis_uart_init(mantis); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err); +- goto fail6; +- } +- +- devs++; +- +- return err; +- +- +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err); +- mantis_uart_exit(mantis); +- +-fail6: +-fail4: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); +- mantis_dma_exit(mantis); +- +-fail3: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); +- mantis_i2c_exit(mantis); +- +-fail2: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); +- mantis_pci_exit(mantis); +- +-fail1: +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); +- kfree(mantis); +- +-fail0: +- return err; +-} +- +-static void __devexit mantis_pci_remove(struct pci_dev *pdev) +-{ +- struct mantis_pci *mantis = pci_get_drvdata(pdev); +- +- if (mantis) { +- +- mantis_uart_exit(mantis); +- mantis_dvb_exit(mantis); +- mantis_dma_exit(mantis); +- mantis_i2c_exit(mantis); +- mantis_pci_exit(mantis); +- kfree(mantis); +- } +- return; +-} +- +-static struct pci_device_id mantis_pci_table[] = { +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config), +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config), +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config), +- MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config), +- MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config), +- MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config), +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config), +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config), +- MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config), +- MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2033_config), +- MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config), +- { } +-}; +- +-MODULE_DEVICE_TABLE(pci, mantis_pci_table); +- +-static struct pci_driver mantis_pci_driver = { +- .name = DRIVER_NAME, +- .id_table = mantis_pci_table, +- .probe = mantis_pci_probe, +- .remove = mantis_pci_remove, +-}; +- +-static int __devinit mantis_init(void) +-{ +- return pci_register_driver(&mantis_pci_driver); +-} +- +-static void __devexit mantis_exit(void) +-{ +- return pci_unregister_driver(&mantis_pci_driver); +-} +- +-module_init(mantis_init); +-module_exit(mantis_exit); +- +-MODULE_DESCRIPTION("MANTIS driver"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/mantis/mantis_common.h b/drivers/media/dvb/mantis/mantis_common.h +deleted file mode 100644 +index f2410cf..0000000 +--- a/drivers/media/dvb/mantis/mantis_common.h ++++ /dev/null +@@ -1,179 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_COMMON_H +-#define __MANTIS_COMMON_H +- +-#include +-#include +-#include +- +-#include "mantis_uart.h" +- +-#include "mantis_link.h" +- +-#define MANTIS_ERROR 0 +-#define MANTIS_NOTICE 1 +-#define MANTIS_INFO 2 +-#define MANTIS_DEBUG 3 +-#define MANTIS_TMG 9 +- +-#define dprintk(y, z, format, arg...) do { \ +- if (z) { \ +- if ((mantis->verbose > MANTIS_ERROR) && (mantis->verbose > y)) \ +- printk(KERN_ERR "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ +- else if ((mantis->verbose > MANTIS_NOTICE) && (mantis->verbose > y)) \ +- printk(KERN_NOTICE "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ +- else if ((mantis->verbose > MANTIS_INFO) && (mantis->verbose > y)) \ +- printk(KERN_INFO "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ +- else if ((mantis->verbose > MANTIS_DEBUG) && (mantis->verbose > y)) \ +- printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ +- else if ((mantis->verbose > MANTIS_TMG) && (mantis->verbose > y)) \ +- printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ +- } else { \ +- if (mantis->verbose > y) \ +- printk(format , ##arg); \ +- } \ +-} while(0) +- +-#define mwrite(dat, addr) writel((dat), addr) +-#define mread(addr) readl(addr) +- +-#define mmwrite(dat, addr) mwrite((dat), (mantis->mmio + (addr))) +-#define mmread(addr) mread(mantis->mmio + (addr)) +- +-#define MANTIS_TS_188 0 +-#define MANTIS_TS_204 1 +- +-#define TWINHAN_TECHNOLOGIES 0x1822 +-#define MANTIS 0x4e35 +- +-#define TECHNISAT 0x1ae4 +-#define TERRATEC 0x153b +- +-#define MAKE_ENTRY(__subven, __subdev, __configptr) { \ +- .vendor = TWINHAN_TECHNOLOGIES, \ +- .device = MANTIS, \ +- .subvendor = (__subven), \ +- .subdevice = (__subdev), \ +- .driver_data = (unsigned long) (__configptr) \ +-} +- +-enum mantis_i2c_mode { +- MANTIS_PAGE_MODE = 0, +- MANTIS_BYTE_MODE, +-}; +- +-struct mantis_pci; +- +-struct mantis_hwconfig { +- char *model_name; +- char *dev_type; +- u32 ts_size; +- +- enum mantis_baud baud_rate; +- enum mantis_parity parity; +- u32 bytes; +- +- irqreturn_t (*irq_handler)(int irq, void *dev_id); +- int (*frontend_init)(struct mantis_pci *mantis, struct dvb_frontend *fe); +- +- u8 power; +- u8 reset; +- +- enum mantis_i2c_mode i2c_mode; +-}; +- +-struct mantis_pci { +- unsigned int verbose; +- +- /* PCI stuff */ +- u16 vendor_id; +- u16 device_id; +- u16 subsystem_vendor; +- u16 subsystem_device; +- +- u8 latency; +- +- struct pci_dev *pdev; +- +- unsigned long mantis_addr; +- void __iomem *mmio; +- +- u8 irq; +- u8 revision; +- +- unsigned int num; +- +- /* RISC Core */ +- u32 busy_block; +- u32 last_block; +- u8 *buf_cpu; +- dma_addr_t buf_dma; +- u32 *risc_cpu; +- dma_addr_t risc_dma; +- +- struct tasklet_struct tasklet; +- +- struct i2c_adapter adapter; +- int i2c_rc; +- wait_queue_head_t i2c_wq; +- struct mutex i2c_lock; +- +- /* DVB stuff */ +- struct dvb_adapter dvb_adapter; +- struct dvb_frontend *fe; +- struct dvb_demux demux; +- struct dmxdev dmxdev; +- struct dmx_frontend fe_hw; +- struct dmx_frontend fe_mem; +- struct dvb_net dvbnet; +- +- u8 feeds; +- +- struct mantis_hwconfig *hwconfig; +- +- u32 mantis_int_stat; +- u32 mantis_int_mask; +- +- /* board specific */ +- u8 mac_address[8]; +- u32 sub_vendor_id; +- u32 sub_device_id; +- +- /* A12 A13 A14 */ +- u32 gpio_status; +- +- u32 gpif_status; +- +- struct mantis_ca *mantis_ca; +- +- wait_queue_head_t uart_wq; +- struct work_struct uart_work; +- spinlock_t uart_lock; +- +- struct rc_dev *rc; +- char input_name[80]; +- char input_phys[80]; +-}; +- +-#define MANTIS_HIF_STATUS (mantis->gpio_status) +- +-#endif /* __MANTIS_COMMON_H */ +diff --git a/drivers/media/dvb/mantis/mantis_core.c b/drivers/media/dvb/mantis/mantis_core.c +deleted file mode 100644 +index 22524a8..0000000 +--- a/drivers/media/dvb/mantis/mantis_core.c ++++ /dev/null +@@ -1,235 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- 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 "mantis_common.h" +-#include "mantis_core.h" +-#include "mantis_vp1033.h" +-#include "mantis_vp1034.h" +-#include "mantis_vp1041.h" +-#include "mantis_vp2033.h" +-#include "mantis_vp2040.h" +-#include "mantis_vp3030.h" +- +-static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) +-{ +- int err; +- struct i2c_msg msg[] = { +- { +- .addr = 0x50, +- .flags = 0, +- .buf = data, +- .len = 1 +- }, { +- .addr = 0x50, +- .flags = I2C_M_RD, +- .buf = data, +- .len = length +- }, +- }; +- +- err = i2c_transfer(&mantis->adapter, msg, 2); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, +- "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", +- err, data[0], data[1]); +- +- return err; +- } +- +- return 0; +-} +- +-static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) +-{ +- int err; +- +- struct i2c_msg msg = { +- .addr = 0x50, +- .flags = 0, +- .buf = data, +- .len = length +- }; +- +- err = i2c_transfer(&mantis->adapter, &msg, 1); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, +- "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", +- err, length, data[0], data[1]); +- +- return err; +- } +- +- return 0; +-} +- +-static int get_mac_address(struct mantis_pci *mantis) +-{ +- int err; +- +- mantis->mac_address[0] = 0x08; +- err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error"); +- +- return err; +- } +- dprintk(verbose, MANTIS_ERROR, 0, +- " MAC Address=[%pM]\n", mantis->mac_address); +- +- return 0; +-} +- +-#define MANTIS_MODEL_UNKNOWN "UNKNOWN" +-#define MANTIS_DEV_UNKNOWN "UNKNOWN" +- +-struct mantis_hwconfig unknown_device = { +- .model_name = MANTIS_MODEL_UNKNOWN, +- .dev_type = MANTIS_DEV_UNKNOWN, +-}; +- +-static void mantis_load_config(struct mantis_pci *mantis) +-{ +- switch (mantis->subsystem_device) { +- case MANTIS_VP_1033_DVB_S: /* VP-1033 */ +- mantis->hwconfig = &vp1033_mantis_config; +- break; +- case MANTIS_VP_1034_DVB_S: /* VP-1034 */ +- mantis->hwconfig = &vp1034_mantis_config; +- break; +- case MANTIS_VP_1041_DVB_S2: /* VP-1041 */ +- case TECHNISAT_SKYSTAR_HD2: +- mantis->hwconfig = &vp1041_mantis_config; +- break; +- case MANTIS_VP_2033_DVB_C: /* VP-2033 */ +- mantis->hwconfig = &vp2033_mantis_config; +- break; +- case MANTIS_VP_2040_DVB_C: /* VP-2040 */ +- case TERRATEC_CINERGY_C_PCI: /* VP-2040 clone */ +- case TECHNISAT_CABLESTAR_HD2: +- mantis->hwconfig = &vp2040_mantis_config; +- break; +- case MANTIS_VP_3030_DVB_T: /* VP-3030 */ +- mantis->hwconfig = &vp3030_mantis_config; +- break; +- default: +- mantis->hwconfig = &unknown_device; +- break; +- } +-} +- +-int mantis_core_init(struct mantis_pci *mantis) +-{ +- int err = 0; +- +- mantis_load_config(mantis); +- dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", +- mantis->hwconfig->model_name, mantis->hwconfig->dev_type, +- mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn)); +- dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", +- mantis->revision, +- mantis->subsystem_vendor, mantis->subsystem_device); +- dprintk(verbose, MANTIS_ERROR, 0, +- "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", +- mantis->pdev->irq, mantis->latency, +- mantis->mantis_addr, mantis->mantis_mmio); +- +- err = mantis_i2c_init(mantis); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed"); +- return err; +- } +- err = get_mac_address(mantis); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed"); +- return err; +- } +- err = mantis_dma_init(mantis); +- if (err < 0) { +- dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed"); +- return err; +- } +- err = mantis_dvb_init(mantis); +- if (err < 0) { +- dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed"); +- return err; +- } +- err = mantis_uart_init(mantis); +- if (err < 0) { +- dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed"); +- return err; +- } +- +- return 0; +-} +- +-int mantis_core_exit(struct mantis_pci *mantis) +-{ +- mantis_dma_stop(mantis); +- dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping"); +- +- mantis_uart_exit(mantis); +- dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed"); +- +- if (mantis_dma_exit(mantis) < 0) +- dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed"); +- if (mantis_dvb_exit(mantis) < 0) +- dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed"); +- if (mantis_i2c_exit(mantis) < 0) +- dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed"); +- +- return 0; +-} +- +-/* Turn the given bit on or off. */ +-void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) +-{ +- u32 cur; +- +- cur = mmread(MANTIS_GPIF_ADDR); +- if (value) +- mantis->gpio_status = cur | (1 << bitpos); +- else +- mantis->gpio_status = cur & (~(1 << bitpos)); +- +- mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); +- mmwrite(0x00, MANTIS_GPIF_DOUT); +- udelay(100); +-} +- +-/* direction = 0 , no CI passthrough ; 1 , CI passthrough */ +-void mantis_set_direction(struct mantis_pci *mantis, int direction) +-{ +- u32 reg; +- +- reg = mmread(0x28); +- dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup"); +- if (direction == 0x01) { +- /* to CI */ +- reg |= 0x04; +- mmwrite(reg, 0x28); +- reg &= 0xff - 0x04; +- mmwrite(reg, 0x28); +- } else { +- reg &= 0xff - 0x04; +- mmwrite(reg, 0x28); +- reg |= 0x04; +- mmwrite(reg, 0x28); +- } +-} +diff --git a/drivers/media/dvb/mantis/mantis_core.h b/drivers/media/dvb/mantis/mantis_core.h +deleted file mode 100644 +index 833ee42..0000000 +--- a/drivers/media/dvb/mantis/mantis_core.h ++++ /dev/null +@@ -1,57 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_CORE_H +-#define __MANTIS_CORE_H +- +-#include "mantis_common.h" +- +- +-#define FE_TYPE_SAT 0 +-#define FE_TYPE_CAB 1 +-#define FE_TYPE_TER 2 +- +-#define FE_TYPE_TS204 0 +-#define FE_TYPE_TS188 1 +- +- +-struct vendorname { +- u8 *sub_vendor_name; +- u32 sub_vendor_id; +-}; +- +-struct devicetype { +- u8 *sub_device_name; +- u32 sub_device_id; +- u8 device_type; +- u32 type_flags; +-}; +- +- +-extern int mantis_dma_init(struct mantis_pci *mantis); +-extern int mantis_dma_exit(struct mantis_pci *mantis); +-extern void mantis_dma_start(struct mantis_pci *mantis); +-extern void mantis_dma_stop(struct mantis_pci *mantis); +-extern int mantis_i2c_init(struct mantis_pci *mantis); +-extern int mantis_i2c_exit(struct mantis_pci *mantis); +-extern int mantis_core_init(struct mantis_pci *mantis); +-extern int mantis_core_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_CORE_H */ +diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c +deleted file mode 100644 +index c61ca7d..0000000 +--- a/drivers/media/dvb/mantis/mantis_dma.c ++++ /dev/null +@@ -1,234 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_dma.h" +- +-#define RISC_WRITE (0x01 << 28) +-#define RISC_JUMP (0x07 << 28) +-#define RISC_IRQ (0x01 << 24) +- +-#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16)) +-#define RISC_FLUSH(risc_pos) (risc_pos = 0) +-#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode)) +- +-#define MANTIS_BUF_SIZE (64 * 1024) +-#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4) +-#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */ +-#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES) +- +-#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES) +-/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */ +-#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */ +- +-int mantis_dma_exit(struct mantis_pci *mantis) +-{ +- if (mantis->buf_cpu) { +- dprintk(MANTIS_ERROR, 1, +- "DMA=0x%lx cpu=0x%p size=%d", +- (unsigned long) mantis->buf_dma, +- mantis->buf_cpu, +- MANTIS_BUF_SIZE); +- +- pci_free_consistent(mantis->pdev, MANTIS_BUF_SIZE, +- mantis->buf_cpu, mantis->buf_dma); +- +- mantis->buf_cpu = NULL; +- } +- if (mantis->risc_cpu) { +- dprintk(MANTIS_ERROR, 1, +- "RISC=0x%lx cpu=0x%p size=%lx", +- (unsigned long) mantis->risc_dma, +- mantis->risc_cpu, +- MANTIS_RISC_SIZE); +- +- pci_free_consistent(mantis->pdev, MANTIS_RISC_SIZE, +- mantis->risc_cpu, mantis->risc_dma); +- +- mantis->risc_cpu = NULL; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_dma_exit); +- +-static inline int mantis_alloc_buffers(struct mantis_pci *mantis) +-{ +- if (!mantis->buf_cpu) { +- mantis->buf_cpu = pci_alloc_consistent(mantis->pdev, +- MANTIS_BUF_SIZE, +- &mantis->buf_dma); +- if (!mantis->buf_cpu) { +- dprintk(MANTIS_ERROR, 1, +- "DMA buffer allocation failed"); +- +- goto err; +- } +- dprintk(MANTIS_ERROR, 1, +- "DMA=0x%lx cpu=0x%p size=%d", +- (unsigned long) mantis->buf_dma, +- mantis->buf_cpu, MANTIS_BUF_SIZE); +- } +- if (!mantis->risc_cpu) { +- mantis->risc_cpu = pci_alloc_consistent(mantis->pdev, +- MANTIS_RISC_SIZE, +- &mantis->risc_dma); +- +- if (!mantis->risc_cpu) { +- dprintk(MANTIS_ERROR, 1, +- "RISC program allocation failed"); +- +- mantis_dma_exit(mantis); +- +- goto err; +- } +- dprintk(MANTIS_ERROR, 1, +- "RISC=0x%lx cpu=0x%p size=%lx", +- (unsigned long) mantis->risc_dma, +- mantis->risc_cpu, MANTIS_RISC_SIZE); +- } +- +- return 0; +-err: +- dprintk(MANTIS_ERROR, 1, "Out of memory (?) ....."); +- return -ENOMEM; +-} +- +-int mantis_dma_init(struct mantis_pci *mantis) +-{ +- int err = 0; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); +- if (mantis_alloc_buffers(mantis) < 0) { +- dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); +- +- /* Stop RISC Engine */ +- mmwrite(0, MANTIS_DMA_CTL); +- +- goto err; +- } +- +- return 0; +-err: +- return err; +-} +-EXPORT_SYMBOL_GPL(mantis_dma_init); +- +-static inline void mantis_risc_program(struct mantis_pci *mantis) +-{ +- u32 buf_pos = 0; +- u32 line, step; +- u32 risc_pos; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program"); +- RISC_FLUSH(risc_pos); +- +- dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u", +- MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES); +- +- for (line = 0; line < MANTIS_BLOCK_COUNT; line++) { +- for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) { +- dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step); +- if (step == 0) { +- RISC_INSTR(risc_pos, RISC_WRITE | +- RISC_IRQ | +- RISC_STATUS(line) | +- MANTIS_DMA_TR_BYTES); +- } else { +- RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES); +- } +- RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos); +- buf_pos += MANTIS_DMA_TR_BYTES; +- } +- } +- RISC_INSTR(risc_pos, RISC_JUMP); +- RISC_INSTR(risc_pos, mantis->risc_dma); +-} +- +-void mantis_dma_start(struct mantis_pci *mantis) +-{ +- dprintk(MANTIS_DEBUG, 1, "Mantis Start DMA engine"); +- +- mantis_risc_program(mantis); +- mmwrite(mantis->risc_dma, MANTIS_RISC_START); +- mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); +- +- mmwrite(0, MANTIS_DMA_CTL); +- mantis->last_block = mantis->busy_block = 0; +- +- mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK); +- +- mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN +- | MANTIS_RISC_EN, MANTIS_DMA_CTL); +- +-} +- +-void mantis_dma_stop(struct mantis_pci *mantis) +-{ +- u32 stat = 0, mask = 0; +- +- stat = mmread(MANTIS_INT_STAT); +- mask = mmread(MANTIS_INT_MASK); +- dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); +- +- mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); +- +- mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN | +- MANTIS_DCAP_EN | +- MANTIS_RISC_EN)), MANTIS_DMA_CTL); +- +- mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT); +- +- mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI | +- MANTIS_INT_RISCEN), MANTIS_INT_MASK); +-} +- +- +-void mantis_dma_xfer(unsigned long data) +-{ +- struct mantis_pci *mantis = (struct mantis_pci *) data; +- struct mantis_hwconfig *config = mantis->hwconfig; +- +- while (mantis->last_block != mantis->busy_block) { +- dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]", +- mantis->last_block, mantis->busy_block); +- +- (config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) +- (&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES); +- mantis->last_block = (mantis->last_block + 1) % MANTIS_BLOCK_COUNT; +- } +-} +diff --git a/drivers/media/dvb/mantis/mantis_dma.h b/drivers/media/dvb/mantis/mantis_dma.h +deleted file mode 100644 +index 6be00fa..0000000 +--- a/drivers/media/dvb/mantis/mantis_dma.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_DMA_H +-#define __MANTIS_DMA_H +- +-extern int mantis_dma_init(struct mantis_pci *mantis); +-extern int mantis_dma_exit(struct mantis_pci *mantis); +-extern void mantis_dma_start(struct mantis_pci *mantis); +-extern void mantis_dma_stop(struct mantis_pci *mantis); +-extern void mantis_dma_xfer(unsigned long data); +- +-#endif /* __MANTIS_DMA_H */ +diff --git a/drivers/media/dvb/mantis/mantis_dvb.c b/drivers/media/dvb/mantis/mantis_dvb.c +deleted file mode 100644 +index e5180e4..0000000 +--- a/drivers/media/dvb/mantis/mantis_dvb.c ++++ /dev/null +@@ -1,299 +0,0 @@ +-/* +- Mantis PCI bridge driver +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_dma.h" +-#include "mantis_ca.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power) +-{ +- struct mantis_hwconfig *config = mantis->hwconfig; +- +- switch (power) { +- case POWER_ON: +- dprintk(MANTIS_DEBUG, 1, "Power ON"); +- mantis_gpio_set_bits(mantis, config->power, POWER_ON); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->power, POWER_ON); +- msleep(100); +- break; +- +- case POWER_OFF: +- dprintk(MANTIS_DEBUG, 1, "Power OFF"); +- mantis_gpio_set_bits(mantis, config->power, POWER_OFF); +- msleep(100); +- break; +- +- default: +- dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power); +- return -1; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_frontend_power); +- +-void mantis_frontend_soft_reset(struct mantis_pci *mantis) +-{ +- struct mantis_hwconfig *config = mantis->hwconfig; +- +- dprintk(MANTIS_DEBUG, 1, "Frontend RESET"); +- mantis_gpio_set_bits(mantis, config->reset, 0); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->reset, 0); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->reset, 1); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->reset, 1); +- msleep(100); +- +- return; +-} +-EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset); +- +-static int mantis_frontend_shutdown(struct mantis_pci *mantis) +-{ +- int err; +- +- mantis_frontend_soft_reset(mantis); +- err = mantis_frontend_power(mantis, POWER_OFF); +- if (err != 0) { +- dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err); +- return 1; +- } +- +- return 0; +-} +- +-static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct mantis_pci *mantis = dvbdmx->priv; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed"); +- if (!dvbdmx->dmx.frontend) { +- dprintk(MANTIS_DEBUG, 1, "no frontend ?"); +- return -EINVAL; +- } +- +- mantis->feeds++; +- dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds); +- +- if (mantis->feeds == 1) { +- dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); +- mantis_dma_start(mantis); +- tasklet_enable(&mantis->tasklet); +- } +- +- return mantis->feeds; +-} +- +-static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct mantis_pci *mantis = dvbdmx->priv; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed"); +- if (!dvbdmx->dmx.frontend) { +- dprintk(MANTIS_DEBUG, 1, "no frontend ?"); +- return -EINVAL; +- } +- +- mantis->feeds--; +- if (mantis->feeds == 0) { +- dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); +- tasklet_disable(&mantis->tasklet); +- mantis_dma_stop(mantis); +- } +- +- return 0; +-} +- +-int __devinit mantis_dvb_init(struct mantis_pci *mantis) +-{ +- struct mantis_hwconfig *config = mantis->hwconfig; +- int result = -1; +- +- dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter"); +- +- result = dvb_register_adapter(&mantis->dvb_adapter, +- "Mantis DVB adapter", +- THIS_MODULE, +- &mantis->pdev->dev, +- adapter_nr); +- +- if (result < 0) { +- +- dprintk(MANTIS_ERROR, 1, "Error registering adapter"); +- return -ENODEV; +- } +- +- mantis->dvb_adapter.priv = mantis; +- mantis->demux.dmx.capabilities = DMX_TS_FILTERING | +- DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING; +- +- mantis->demux.priv = mantis; +- mantis->demux.filternum = 256; +- mantis->demux.feednum = 256; +- mantis->demux.start_feed = mantis_dvb_start_feed; +- mantis->demux.stop_feed = mantis_dvb_stop_feed; +- mantis->demux.write_to_decoder = NULL; +- +- dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init"); +- result = dvb_dmx_init(&mantis->demux); +- if (result < 0) { +- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); +- +- goto err0; +- } +- +- mantis->dmxdev.filternum = 256; +- mantis->dmxdev.demux = &mantis->demux.dmx; +- mantis->dmxdev.capabilities = 0; +- dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init"); +- +- result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter); +- if (result < 0) { +- +- dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result); +- goto err1; +- } +- +- mantis->fe_hw.source = DMX_FRONTEND_0; +- result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw); +- if (result < 0) { +- +- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); +- goto err2; +- } +- +- mantis->fe_mem.source = DMX_MEMORY_FE; +- result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem); +- if (result < 0) { +- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); +- goto err3; +- } +- +- result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw); +- if (result < 0) { +- dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); +- goto err4; +- } +- +- dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); +- tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis); +- tasklet_disable(&mantis->tasklet); +- if (mantis->hwconfig) { +- result = config->frontend_init(mantis, mantis->fe); +- if (result < 0) { +- dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!"); +- goto err5; +- } else { +- if (mantis->fe == NULL) { +- dprintk(MANTIS_ERROR, 1, "FE "); +- goto err5; +- } +- +- if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed"); +- +- if (mantis->fe->ops.release) +- mantis->fe->ops.release(mantis->fe); +- +- mantis->fe = NULL; +- goto err5; +- } +- } +- } +- +- return 0; +- +- /* Error conditions .. */ +-err5: +- tasklet_kill(&mantis->tasklet); +- dvb_net_release(&mantis->dvbnet); +- dvb_unregister_frontend(mantis->fe); +- dvb_frontend_detach(mantis->fe); +-err4: +- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); +- +-err3: +- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); +- +-err2: +- dvb_dmxdev_release(&mantis->dmxdev); +- +-err1: +- dvb_dmx_release(&mantis->demux); +- +-err0: +- dvb_unregister_adapter(&mantis->dvb_adapter); +- +- return result; +-} +-EXPORT_SYMBOL_GPL(mantis_dvb_init); +- +-int __devexit mantis_dvb_exit(struct mantis_pci *mantis) +-{ +- int err; +- +- if (mantis->fe) { +- /* mantis_ca_exit(mantis); */ +- err = mantis_frontend_shutdown(mantis); +- if (err != 0) +- dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err); +- dvb_unregister_frontend(mantis->fe); +- dvb_frontend_detach(mantis->fe); +- } +- +- tasklet_kill(&mantis->tasklet); +- dvb_net_release(&mantis->dvbnet); +- +- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); +- mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); +- +- dvb_dmxdev_release(&mantis->dmxdev); +- dvb_dmx_release(&mantis->demux); +- +- dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter"); +- dvb_unregister_adapter(&mantis->dvb_adapter); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_dvb_exit); +diff --git a/drivers/media/dvb/mantis/mantis_dvb.h b/drivers/media/dvb/mantis/mantis_dvb.h +deleted file mode 100644 +index 464199d..0000000 +--- a/drivers/media/dvb/mantis/mantis_dvb.h ++++ /dev/null +@@ -1,35 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_DVB_H +-#define __MANTIS_DVB_H +- +-enum mantis_power { +- POWER_OFF = 0, +- POWER_ON = 1 +-}; +- +-extern int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power); +-extern void mantis_frontend_soft_reset(struct mantis_pci *mantis); +- +-extern int mantis_dvb_init(struct mantis_pci *mantis); +-extern int mantis_dvb_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_DVB_H */ +diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c +deleted file mode 100644 +index 36f2256..0000000 +--- a/drivers/media/dvb/mantis/mantis_evm.c ++++ /dev/null +@@ -1,118 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_link.h" +-#include "mantis_hif.h" +-#include "mantis_reg.h" +- +-static void mantis_hifevm_work(struct work_struct *work) +-{ +- struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); +- struct mantis_pci *mantis = ca->ca_priv; +- +- u32 gpif_stat, gpif_mask; +- +- gpif_stat = mmread(MANTIS_GPIF_STATUS); +- gpif_mask = mmread(MANTIS_GPIF_IRQCFG); +- +- if (gpif_stat & MANTIS_GPIF_DETSTAT) { +- if (gpif_stat & MANTIS_CARD_PLUGIN) { +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num); +- mmwrite(0xdada0000, MANTIS_CARD_RESET); +- mantis_event_cam_plugin(ca); +- dvb_ca_en50221_camchange_irq(&ca->en50221, +- 0, +- DVB_CA_EN50221_CAMCHANGE_INSERTED); +- } +- } else { +- if (gpif_stat & MANTIS_CARD_PLUGOUT) { +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num); +- mmwrite(0xdada0000, MANTIS_CARD_RESET); +- mantis_event_cam_unplug(ca); +- dvb_ca_en50221_camchange_irq(&ca->en50221, +- 0, +- DVB_CA_EN50221_CAMCHANGE_REMOVED); +- } +- } +- +- if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num); +- +- if (mantis->gpif_status & MANTIS_SBUF_WSTO) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num); +- +- if (mantis->gpif_status & MANTIS_GPIF_OTHERR) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num); +- +- if (gpif_stat & MANTIS_SBUF_OVFLW) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num); +- +- if (gpif_stat & MANTIS_GPIF_BRRDY) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num); +- +- if (gpif_stat & MANTIS_GPIF_INTSTAT) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num); +- +- if (gpif_stat & MANTIS_SBUF_EMPTY) +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num); +- +- if (gpif_stat & MANTIS_SBUF_OPDONE) { +- dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num); +- ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL; +- ca->hif_event = MANTIS_SBUF_OPDONE; +- wake_up(&ca->hif_opdone_wq); +- } +-} +- +-int mantis_evmgr_init(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager"); +- INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work); +- mantis_pcmcia_init(ca); +- schedule_work(&ca->hif_evm_work); +- mantis_hif_init(ca); +- return 0; +-} +- +-void mantis_evmgr_exit(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting"); +- flush_work_sync(&ca->hif_evm_work); +- mantis_hif_exit(ca); +- mantis_pcmcia_exit(ca); +-} +diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c +deleted file mode 100644 +index 672cf4d..0000000 +--- a/drivers/media/dvb/mantis/mantis_hif.c ++++ /dev/null +@@ -1,239 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +- +-#include "mantis_hif.h" +-#include "mantis_link.h" /* temporary due to physical layer stuff */ +- +-#include "mantis_reg.h" +- +- +-static int mantis_hif_sbuf_opdone_wait(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- int rc = 0; +- +- if (wait_event_timeout(ca->hif_opdone_wq, +- ca->hif_event & MANTIS_SBUF_OPDONE, +- msecs_to_jiffies(500)) == -ERESTARTSYS) { +- +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Smart buffer operation timeout !", mantis->num); +- rc = -EREMOTEIO; +- } +- dprintk(MANTIS_DEBUG, 1, "Smart Buffer Operation complete"); +- ca->hif_event &= ~MANTIS_SBUF_OPDONE; +- return rc; +-} +- +-static int mantis_hif_write_wait(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- u32 opdone = 0, timeout = 0; +- int rc = 0; +- +- if (wait_event_timeout(ca->hif_write_wq, +- mantis->gpif_status & MANTIS_GPIF_WRACK, +- msecs_to_jiffies(500)) == -ERESTARTSYS) { +- +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write ACK timed out !", mantis->num); +- rc = -EREMOTEIO; +- } +- dprintk(MANTIS_DEBUG, 1, "Write Acknowledged"); +- mantis->gpif_status &= ~MANTIS_GPIF_WRACK; +- while (!opdone) { +- opdone = (mmread(MANTIS_GPIF_STATUS) & MANTIS_SBUF_OPDONE); +- udelay(500); +- timeout++; +- if (timeout > 100) { +- dprintk(MANTIS_ERROR, 1, "Adater(%d) Slot(0): Write operation timed out!", mantis->num); +- rc = -ETIMEDOUT; +- break; +- } +- } +- dprintk(MANTIS_DEBUG, 1, "HIF Write success"); +- return rc; +-} +- +- +-int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- u32 hif_addr = 0, data, count = 4; +- +- dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Read", mantis->num); +- mutex_lock(&ca->ca_lock); +- hif_addr &= ~MANTIS_GPIF_PCMCIAREG; +- hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; +- hif_addr |= MANTIS_HIF_STATUS; +- hif_addr |= addr; +- +- mmwrite(hif_addr, MANTIS_GPIF_BRADDR); +- mmwrite(count, MANTIS_GPIF_BRBYTES); +- udelay(20); +- mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); +- +- if (mantis_hif_sbuf_opdone_wait(ca) != 0) { +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): GPIF Smart Buffer operation failed", mantis->num); +- mutex_unlock(&ca->ca_lock); +- return -EREMOTEIO; +- } +- data = mmread(MANTIS_GPIF_DIN); +- mutex_unlock(&ca->ca_lock); +- dprintk(MANTIS_DEBUG, 1, "Mem Read: 0x%02x", data); +- return (data >> 24) & 0xff; +-} +- +-int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data) +-{ +- struct mantis_slot *slot = ca->slot; +- struct mantis_pci *mantis = ca->ca_priv; +- u32 hif_addr = 0; +- +- dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Write", mantis->num); +- mutex_lock(&ca->ca_lock); +- hif_addr &= ~MANTIS_GPIF_HIFRDWRN; +- hif_addr &= ~MANTIS_GPIF_PCMCIAREG; +- hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; +- hif_addr |= MANTIS_HIF_STATUS; +- hif_addr |= addr; +- +- mmwrite(slot->slave_cfg, MANTIS_GPIF_CFGSLA); /* Slot0 alone for now */ +- mmwrite(hif_addr, MANTIS_GPIF_ADDR); +- mmwrite(data, MANTIS_GPIF_DOUT); +- +- if (mantis_hif_write_wait(ca) != 0) { +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); +- mutex_unlock(&ca->ca_lock); +- return -EREMOTEIO; +- } +- dprintk(MANTIS_DEBUG, 1, "Mem Write: (0x%02x to 0x%02x)", data, addr); +- mutex_unlock(&ca->ca_lock); +- +- return 0; +-} +- +-int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- u32 data, hif_addr = 0; +- +- dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Read", mantis->num); +- mutex_lock(&ca->ca_lock); +- hif_addr &= ~MANTIS_GPIF_PCMCIAREG; +- hif_addr |= MANTIS_GPIF_PCMCIAIOM; +- hif_addr |= MANTIS_HIF_STATUS; +- hif_addr |= addr; +- +- mmwrite(hif_addr, MANTIS_GPIF_BRADDR); +- mmwrite(1, MANTIS_GPIF_BRBYTES); +- udelay(20); +- mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); +- +- if (mantis_hif_sbuf_opdone_wait(ca) != 0) { +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); +- mutex_unlock(&ca->ca_lock); +- return -EREMOTEIO; +- } +- data = mmread(MANTIS_GPIF_DIN); +- dprintk(MANTIS_DEBUG, 1, "I/O Read: 0x%02x", data); +- udelay(50); +- mutex_unlock(&ca->ca_lock); +- +- return (u8) data; +-} +- +-int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- u32 hif_addr = 0; +- +- dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Write", mantis->num); +- mutex_lock(&ca->ca_lock); +- hif_addr &= ~MANTIS_GPIF_PCMCIAREG; +- hif_addr &= ~MANTIS_GPIF_HIFRDWRN; +- hif_addr |= MANTIS_GPIF_PCMCIAIOM; +- hif_addr |= MANTIS_HIF_STATUS; +- hif_addr |= addr; +- +- mmwrite(hif_addr, MANTIS_GPIF_ADDR); +- mmwrite(data, MANTIS_GPIF_DOUT); +- +- if (mantis_hif_write_wait(ca) != 0) { +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); +- mutex_unlock(&ca->ca_lock); +- return -EREMOTEIO; +- } +- dprintk(MANTIS_DEBUG, 1, "I/O Write: (0x%02x to 0x%02x)", data, addr); +- mutex_unlock(&ca->ca_lock); +- udelay(50); +- +- return 0; +-} +- +-int mantis_hif_init(struct mantis_ca *ca) +-{ +- struct mantis_slot *slot = ca->slot; +- struct mantis_pci *mantis = ca->ca_priv; +- u32 irqcfg; +- +- slot[0].slave_cfg = 0x70773028; +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Initializing Mantis Host Interface", mantis->num); +- +- mutex_lock(&ca->ca_lock); +- irqcfg = mmread(MANTIS_GPIF_IRQCFG); +- irqcfg = MANTIS_MASK_BRRDY | +- MANTIS_MASK_WRACK | +- MANTIS_MASK_EXTIRQ | +- MANTIS_MASK_WSTO | +- MANTIS_MASK_OTHERR | +- MANTIS_MASK_OVFLW; +- +- mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); +- mutex_unlock(&ca->ca_lock); +- +- return 0; +-} +- +-void mantis_hif_exit(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- u32 irqcfg; +- +- dprintk(MANTIS_ERROR, 1, "Adapter(%d) Exiting Mantis Host Interface", mantis->num); +- mutex_lock(&ca->ca_lock); +- irqcfg = mmread(MANTIS_GPIF_IRQCFG); +- irqcfg &= ~MANTIS_MASK_BRRDY; +- mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); +- mutex_unlock(&ca->ca_lock); +-} +diff --git a/drivers/media/dvb/mantis/mantis_hif.h b/drivers/media/dvb/mantis/mantis_hif.h +deleted file mode 100644 +index 9094f9e..0000000 +--- a/drivers/media/dvb/mantis/mantis_hif.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_HIF_H +-#define __MANTIS_HIF_H +- +-#define MANTIS_HIF_MEMRD 1 +-#define MANTIS_HIF_MEMWR 2 +-#define MANTIS_HIF_IOMRD 3 +-#define MANTIS_HIF_IOMWR 4 +- +-#endif /* __MANTIS_HIF_H */ +diff --git a/drivers/media/dvb/mantis/mantis_i2c.c b/drivers/media/dvb/mantis/mantis_i2c.c +deleted file mode 100644 +index e779451..0000000 +--- a/drivers/media/dvb/mantis/mantis_i2c.c ++++ /dev/null +@@ -1,266 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_i2c.h" +- +-#define TRIALS 10000 +- +-static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg) +-{ +- u32 rxd, i, stat, trials; +- +- dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", +- __func__, msg->addr); +- +- for (i = 0; i < msg->len; i++) { +- rxd = (msg->addr << 25) | (1 << 24) +- | MANTIS_I2C_RATE_3 +- | MANTIS_I2C_STOP +- | MANTIS_I2C_PGMODE; +- +- if (i == (msg->len - 1)) +- rxd &= ~MANTIS_I2C_STOP; +- +- mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); +- mmwrite(rxd, MANTIS_I2CDATA_CTL); +- +- /* wait for xfer completion */ +- for (trials = 0; trials < TRIALS; trials++) { +- stat = mmread(MANTIS_INT_STAT); +- if (stat & MANTIS_INT_I2CDONE) +- break; +- } +- +- dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); +- +- /* wait for xfer completion */ +- for (trials = 0; trials < TRIALS; trials++) { +- stat = mmread(MANTIS_INT_STAT); +- if (stat & MANTIS_INT_I2CRACK) +- break; +- } +- +- dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); +- +- rxd = mmread(MANTIS_I2CDATA_CTL); +- msg->buf[i] = (u8)((rxd >> 8) & 0xFF); +- dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); +- } +- dprintk(MANTIS_INFO, 0, "]\n"); +- +- return 0; +-} +- +-static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg) +-{ +- int i; +- u32 txd = 0, stat, trials; +- +- dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", +- __func__, msg->addr); +- +- for (i = 0; i < msg->len; i++) { +- dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); +- txd = (msg->addr << 25) | (msg->buf[i] << 8) +- | MANTIS_I2C_RATE_3 +- | MANTIS_I2C_STOP +- | MANTIS_I2C_PGMODE; +- +- if (i == (msg->len - 1)) +- txd &= ~MANTIS_I2C_STOP; +- +- mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); +- mmwrite(txd, MANTIS_I2CDATA_CTL); +- +- /* wait for xfer completion */ +- for (trials = 0; trials < TRIALS; trials++) { +- stat = mmread(MANTIS_INT_STAT); +- if (stat & MANTIS_INT_I2CDONE) +- break; +- } +- +- dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); +- +- /* wait for xfer completion */ +- for (trials = 0; trials < TRIALS; trials++) { +- stat = mmread(MANTIS_INT_STAT); +- if (stat & MANTIS_INT_I2CRACK) +- break; +- } +- +- dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); +- } +- dprintk(MANTIS_INFO, 0, "]\n"); +- +- return 0; +-} +- +-static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) +-{ +- int ret = 0, i = 0, trials; +- u32 stat, data, txd; +- struct mantis_pci *mantis; +- struct mantis_hwconfig *config; +- +- mantis = i2c_get_adapdata(adapter); +- BUG_ON(!mantis); +- config = mantis->hwconfig; +- BUG_ON(!config); +- +- dprintk(MANTIS_DEBUG, 1, "Messages:%d", num); +- mutex_lock(&mantis->i2c_lock); +- +- while (i < num) { +- /* Byte MODE */ +- if ((config->i2c_mode & MANTIS_BYTE_MODE) && +- ((i + 1) < num) && +- (msgs[i].len < 2) && +- (msgs[i + 1].len < 2) && +- (msgs[i + 1].flags & I2C_M_RD)) { +- +- dprintk(MANTIS_DEBUG, 0, " Byte MODE:\n"); +- +- /* Read operation */ +- txd = msgs[i].addr << 25 | (0x1 << 24) +- | (msgs[i].buf[0] << 16) +- | MANTIS_I2C_RATE_3; +- +- mmwrite(txd, MANTIS_I2CDATA_CTL); +- /* wait for xfer completion */ +- for (trials = 0; trials < TRIALS; trials++) { +- stat = mmread(MANTIS_INT_STAT); +- if (stat & MANTIS_INT_I2CDONE) +- break; +- } +- +- /* check for xfer completion */ +- if (stat & MANTIS_INT_I2CDONE) { +- /* check xfer was acknowledged */ +- if (stat & MANTIS_INT_I2CRACK) { +- data = mmread(MANTIS_I2CDATA_CTL); +- msgs[i + 1].buf[0] = (data >> 8) & 0xff; +- dprintk(MANTIS_DEBUG, 0, " Byte <%d> RXD=0x%02x [%02x]\n", 0x0, data, msgs[i + 1].buf[0]); +- } else { +- /* I/O error */ +- dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); +- ret = -EIO; +- break; +- } +- } else { +- /* I/O error */ +- dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); +- ret = -EIO; +- break; +- } +- i += 2; /* Write/Read operation in one go */ +- } +- +- if (i < num) { +- if (msgs[i].flags & I2C_M_RD) +- ret = mantis_i2c_read(mantis, &msgs[i]); +- else +- ret = mantis_i2c_write(mantis, &msgs[i]); +- +- i++; +- if (ret < 0) +- goto bail_out; +- } +- +- } +- +- mutex_unlock(&mantis->i2c_lock); +- +- return num; +- +-bail_out: +- mutex_unlock(&mantis->i2c_lock); +- return ret; +-} +- +-static u32 mantis_i2c_func(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_SMBUS_EMUL; +-} +- +-static struct i2c_algorithm mantis_algo = { +- .master_xfer = mantis_i2c_xfer, +- .functionality = mantis_i2c_func, +-}; +- +-int __devinit mantis_i2c_init(struct mantis_pci *mantis) +-{ +- u32 intstat, intmask; +- struct i2c_adapter *i2c_adapter = &mantis->adapter; +- struct pci_dev *pdev = mantis->pdev; +- +- init_waitqueue_head(&mantis->i2c_wq); +- mutex_init(&mantis->i2c_lock); +- strncpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name)); +- i2c_set_adapdata(i2c_adapter, mantis); +- +- i2c_adapter->owner = THIS_MODULE; +- i2c_adapter->algo = &mantis_algo; +- i2c_adapter->algo_data = NULL; +- i2c_adapter->timeout = 500; +- i2c_adapter->retries = 3; +- i2c_adapter->dev.parent = &pdev->dev; +- +- mantis->i2c_rc = i2c_add_adapter(i2c_adapter); +- if (mantis->i2c_rc < 0) +- return mantis->i2c_rc; +- +- dprintk(MANTIS_DEBUG, 1, "Initializing I2C .."); +- +- intstat = mmread(MANTIS_INT_STAT); +- intmask = mmread(MANTIS_INT_MASK); +- mmwrite(intstat, MANTIS_INT_STAT); +- dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); +- intmask = mmread(MANTIS_INT_MASK); +- mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_i2c_init); +- +-int mantis_i2c_exit(struct mantis_pci *mantis) +-{ +- u32 intmask; +- +- dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); +- intmask = mmread(MANTIS_INT_MASK); +- mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); +- +- dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter"); +- return i2c_del_adapter(&mantis->adapter); +-} +-EXPORT_SYMBOL_GPL(mantis_i2c_exit); +diff --git a/drivers/media/dvb/mantis/mantis_i2c.h b/drivers/media/dvb/mantis/mantis_i2c.h +deleted file mode 100644 +index 1342df2..0000000 +--- a/drivers/media/dvb/mantis/mantis_i2c.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_I2C_H +-#define __MANTIS_I2C_H +- +-#define I2C_STOP (1 << 0) +-#define I2C_READ (1 << 1) +- +-extern int mantis_i2c_init(struct mantis_pci *mantis); +-extern int mantis_i2c_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_I2C_H */ +diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c +deleted file mode 100644 +index db6d54d..0000000 +--- a/drivers/media/dvb/mantis/mantis_input.c ++++ /dev/null +@@ -1,159 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_uart.h" +- +-#define MODULE_NAME "mantis_core" +-#define RC_MAP_MANTIS "rc-mantis" +- +-static struct rc_map_table mantis_ir_table[] = { +- { 0x29, KEY_POWER }, +- { 0x28, KEY_FAVORITES }, +- { 0x30, KEY_TEXT }, +- { 0x17, KEY_INFO }, /* Preview */ +- { 0x23, KEY_EPG }, +- { 0x3b, KEY_F22 }, /* Record List */ +- { 0x3c, KEY_1 }, +- { 0x3e, KEY_2 }, +- { 0x39, KEY_3 }, +- { 0x36, KEY_4 }, +- { 0x22, KEY_5 }, +- { 0x20, KEY_6 }, +- { 0x32, KEY_7 }, +- { 0x26, KEY_8 }, +- { 0x24, KEY_9 }, +- { 0x2a, KEY_0 }, +- +- { 0x33, KEY_CANCEL }, +- { 0x2c, KEY_BACK }, +- { 0x15, KEY_CLEAR }, +- { 0x3f, KEY_TAB }, +- { 0x10, KEY_ENTER }, +- { 0x14, KEY_UP }, +- { 0x0d, KEY_RIGHT }, +- { 0x0e, KEY_DOWN }, +- { 0x11, KEY_LEFT }, +- +- { 0x21, KEY_VOLUMEUP }, +- { 0x35, KEY_VOLUMEDOWN }, +- { 0x3d, KEY_CHANNELDOWN }, +- { 0x3a, KEY_CHANNELUP }, +- { 0x2e, KEY_RECORD }, +- { 0x2b, KEY_PLAY }, +- { 0x13, KEY_PAUSE }, +- { 0x25, KEY_STOP }, +- +- { 0x1f, KEY_REWIND }, +- { 0x2d, KEY_FASTFORWARD }, +- { 0x1e, KEY_PREVIOUS }, /* Replay |< */ +- { 0x1d, KEY_NEXT }, /* Skip >| */ +- +- { 0x0b, KEY_CAMERA }, /* Capture */ +- { 0x0f, KEY_LANGUAGE }, /* SAP */ +- { 0x18, KEY_MODE }, /* PIP */ +- { 0x12, KEY_ZOOM }, /* Full screen */ +- { 0x1c, KEY_SUBTITLE }, +- { 0x2f, KEY_MUTE }, +- { 0x16, KEY_F20 }, /* L/R */ +- { 0x38, KEY_F21 }, /* Hibernate */ +- +- { 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */ +- { 0x31, KEY_AGAIN }, /* Recall */ +- { 0x1a, KEY_KPPLUS }, /* Zoom+ */ +- { 0x19, KEY_KPMINUS }, /* Zoom- */ +- { 0x27, KEY_RED }, +- { 0x0C, KEY_GREEN }, +- { 0x01, KEY_YELLOW }, +- { 0x00, KEY_BLUE }, +-}; +- +-static struct rc_map_list ir_mantis_map = { +- .map = { +- .scan = mantis_ir_table, +- .size = ARRAY_SIZE(mantis_ir_table), +- .rc_type = RC_TYPE_UNKNOWN, +- .name = RC_MAP_MANTIS, +- } +-}; +- +-int mantis_input_init(struct mantis_pci *mantis) +-{ +- struct rc_dev *dev; +- int err; +- +- err = rc_map_register(&ir_mantis_map); +- if (err) +- goto out; +- +- dev = rc_allocate_device(); +- if (!dev) { +- dprintk(MANTIS_ERROR, 1, "Remote device allocation failed"); +- err = -ENOMEM; +- goto out_map; +- } +- +- sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name); +- sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev)); +- +- dev->input_name = mantis->input_name; +- dev->input_phys = mantis->input_phys; +- dev->input_id.bustype = BUS_PCI; +- dev->input_id.vendor = mantis->vendor_id; +- dev->input_id.product = mantis->device_id; +- dev->input_id.version = 1; +- dev->driver_name = MODULE_NAME; +- dev->map_name = RC_MAP_MANTIS; +- dev->dev.parent = &mantis->pdev->dev; +- +- err = rc_register_device(dev); +- if (err) { +- dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); +- goto out_dev; +- } +- +- mantis->rc = dev; +- return 0; +- +-out_dev: +- rc_free_device(dev); +-out_map: +- rc_map_unregister(&ir_mantis_map); +-out: +- return err; +-} +- +-int mantis_exit(struct mantis_pci *mantis) +-{ +- rc_unregister_device(mantis->rc); +- rc_map_unregister(&ir_mantis_map); +- return 0; +-} +- +diff --git a/drivers/media/dvb/mantis/mantis_ioc.c b/drivers/media/dvb/mantis/mantis_ioc.c +deleted file mode 100644 +index 24fcdc6..0000000 +--- a/drivers/media/dvb/mantis/mantis_ioc.c ++++ /dev/null +@@ -1,124 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_ioc.h" +- +-static int read_eeprom_bytes(struct mantis_pci *mantis, u8 reg, u8 *data, u8 length) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- int err; +- u8 buf = reg; +- +- struct i2c_msg msg[] = { +- { .addr = 0x50, .flags = 0, .buf = &buf, .len = 1 }, +- { .addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = length }, +- }; +- +- err = i2c_transfer(adapter, msg, 2); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", +- err, data[0], data[1]); +- +- return err; +- } +- +- return 0; +-} +-int mantis_get_mac(struct mantis_pci *mantis) +-{ +- int err; +- u8 mac_addr[6] = {0}; +- +- err = read_eeprom_bytes(mantis, 0x08, mac_addr, 6); +- if (err < 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Mantis EEPROM read error <%d>", err); +- +- return err; +- } +- +- dprintk(MANTIS_ERROR, 0, " MAC Address=[%pM]\n", mac_addr); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_get_mac); +- +-/* Turn the given bit on or off. */ +-void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) +-{ +- u32 cur; +- +- dprintk(MANTIS_DEBUG, 1, "Set Bit <%d> to <%d>", bitpos, value); +- cur = mmread(MANTIS_GPIF_ADDR); +- if (value) +- mantis->gpio_status = cur | (1 << bitpos); +- else +- mantis->gpio_status = cur & (~(1 << bitpos)); +- +- dprintk(MANTIS_DEBUG, 1, "GPIO Value <%02x>", mantis->gpio_status); +- mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); +- mmwrite(0x00, MANTIS_GPIF_DOUT); +-} +-EXPORT_SYMBOL_GPL(mantis_gpio_set_bits); +- +-int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl) +-{ +- u32 reg; +- +- reg = mmread(MANTIS_CONTROL); +- switch (stream_ctl) { +- case STREAM_TO_HIF: +- dprintk(MANTIS_DEBUG, 1, "Set stream to HIF"); +- reg &= 0xff - MANTIS_BYPASS; +- mmwrite(reg, MANTIS_CONTROL); +- reg |= MANTIS_BYPASS; +- mmwrite(reg, MANTIS_CONTROL); +- break; +- +- case STREAM_TO_CAM: +- dprintk(MANTIS_DEBUG, 1, "Set stream to CAM"); +- reg |= MANTIS_BYPASS; +- mmwrite(reg, MANTIS_CONTROL); +- reg &= 0xff - MANTIS_BYPASS; +- mmwrite(reg, MANTIS_CONTROL); +- break; +- default: +- dprintk(MANTIS_ERROR, 1, "Unknown MODE <%02x>", stream_ctl); +- return -1; +- } +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_stream_control); +diff --git a/drivers/media/dvb/mantis/mantis_ioc.h b/drivers/media/dvb/mantis/mantis_ioc.h +deleted file mode 100644 +index d56e002..0000000 +--- a/drivers/media/dvb/mantis/mantis_ioc.h ++++ /dev/null +@@ -1,51 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_IOC_H +-#define __MANTIS_IOC_H +- +-#define GPIF_A00 0x00 +-#define GPIF_A01 0x01 +-#define GPIF_A02 0x02 +-#define GPIF_A03 0x03 +-#define GPIF_A04 0x04 +-#define GPIF_A05 0x05 +-#define GPIF_A06 0x06 +-#define GPIF_A07 0x07 +-#define GPIF_A08 0x08 +-#define GPIF_A09 0x09 +-#define GPIF_A10 0x0a +-#define GPIF_A11 0x0b +- +-#define GPIF_A12 0x0c +-#define GPIF_A13 0x0d +-#define GPIF_A14 0x0e +- +-enum mantis_stream_control { +- STREAM_TO_HIF = 0, +- STREAM_TO_CAM +-}; +- +-extern int mantis_get_mac(struct mantis_pci *mantis); +-extern void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value); +- +-extern int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl); +- +-#endif /* __MANTIS_IOC_H */ +diff --git a/drivers/media/dvb/mantis/mantis_link.h b/drivers/media/dvb/mantis/mantis_link.h +deleted file mode 100644 +index 2a81477..0000000 +--- a/drivers/media/dvb/mantis/mantis_link.h ++++ /dev/null +@@ -1,83 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_LINK_H +-#define __MANTIS_LINK_H +- +-#include +-#include +-#include "dvb_ca_en50221.h" +- +-enum mantis_sbuf_status { +- MANTIS_SBUF_DATA_AVAIL = 1, +- MANTIS_SBUF_DATA_EMPTY = 2, +- MANTIS_SBUF_DATA_OVFLW = 3 +-}; +- +-struct mantis_slot { +- u32 timeout; +- u32 slave_cfg; +- u32 bar; +-}; +- +-/* Physical layer */ +-enum mantis_slot_state { +- MODULE_INSERTED = 3, +- MODULE_XTRACTED = 4 +-}; +- +-struct mantis_ca { +- struct mantis_slot slot[4]; +- +- struct work_struct hif_evm_work; +- +- u32 hif_event; +- wait_queue_head_t hif_opdone_wq; +- wait_queue_head_t hif_brrdyw_wq; +- wait_queue_head_t hif_data_wq; +- wait_queue_head_t hif_write_wq; /* HIF Write op */ +- +- enum mantis_sbuf_status sbuf_status; +- +- enum mantis_slot_state slot_state; +- +- void *ca_priv; +- +- struct dvb_ca_en50221 en50221; +- struct mutex ca_lock; +-}; +- +-/* CA */ +-extern void mantis_event_cam_plugin(struct mantis_ca *ca); +-extern void mantis_event_cam_unplug(struct mantis_ca *ca); +-extern int mantis_pcmcia_init(struct mantis_ca *ca); +-extern void mantis_pcmcia_exit(struct mantis_ca *ca); +-extern int mantis_evmgr_init(struct mantis_ca *ca); +-extern void mantis_evmgr_exit(struct mantis_ca *ca); +- +-/* HIF */ +-extern int mantis_hif_init(struct mantis_ca *ca); +-extern void mantis_hif_exit(struct mantis_ca *ca); +-extern int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr); +-extern int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data); +-extern int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr); +-extern int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data); +- +-#endif /* __MANTIS_LINK_H */ +diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/dvb/mantis/mantis_pci.c +deleted file mode 100644 +index 371558a..0000000 +--- a/drivers/media/dvb/mantis/mantis_pci.c ++++ /dev/null +@@ -1,170 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_pci.h" +- +-#define DRIVER_NAME "Mantis Core" +- +-int __devinit mantis_pci_init(struct mantis_pci *mantis) +-{ +- u8 latency; +- struct mantis_hwconfig *config = mantis->hwconfig; +- struct pci_dev *pdev = mantis->pdev; +- int err, ret = 0; +- +- dprintk(MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", +- config->model_name, +- config->dev_type, +- mantis->pdev->bus->number, +- PCI_SLOT(mantis->pdev->devfn), +- PCI_FUNC(mantis->pdev->devfn)); +- +- err = pci_enable_device(pdev); +- if (err != 0) { +- ret = -ENODEV; +- dprintk(MANTIS_ERROR, 1, "ERROR: PCI enable failed <%i>", err); +- goto fail0; +- } +- +- err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); +- if (err != 0) { +- dprintk(MANTIS_ERROR, 1, "ERROR: Unable to obtain 32 bit DMA <%i>", err); +- ret = -ENOMEM; +- goto fail1; +- } +- +- pci_set_master(pdev); +- +- if (!request_mem_region(pci_resource_start(pdev, 0), +- pci_resource_len(pdev, 0), +- DRIVER_NAME)) { +- +- dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 Request failed !"); +- ret = -ENODEV; +- goto fail1; +- } +- +- mantis->mmio = ioremap(pci_resource_start(pdev, 0), +- pci_resource_len(pdev, 0)); +- +- if (!mantis->mmio) { +- dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 remap failed !"); +- ret = -ENODEV; +- goto fail2; +- } +- +- pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency); +- mantis->latency = latency; +- mantis->revision = pdev->revision; +- +- dprintk(MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", +- mantis->revision, +- mantis->pdev->subsystem_vendor, +- mantis->pdev->subsystem_device); +- +- dprintk(MANTIS_ERROR, 0, +- "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", +- mantis->pdev->irq, +- mantis->latency, +- mantis->mantis_addr, +- mantis->mmio); +- +- err = request_irq(pdev->irq, +- config->irq_handler, +- IRQF_SHARED, +- DRIVER_NAME, +- mantis); +- +- if (err != 0) { +- +- dprintk(MANTIS_ERROR, 1, "ERROR: IRQ registration failed ! <%d>", err); +- ret = -ENODEV; +- goto fail3; +- } +- +- pci_set_drvdata(pdev, mantis); +- return ret; +- +- /* Error conditions */ +-fail3: +- dprintk(MANTIS_ERROR, 1, "ERROR: <%d> I/O unmap", ret); +- if (mantis->mmio) +- iounmap(mantis->mmio); +- +-fail2: +- dprintk(MANTIS_ERROR, 1, "ERROR: <%d> releasing regions", ret); +- release_mem_region(pci_resource_start(pdev, 0), +- pci_resource_len(pdev, 0)); +- +-fail1: +- dprintk(MANTIS_ERROR, 1, "ERROR: <%d> disabling device", ret); +- pci_disable_device(pdev); +- +-fail0: +- dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret); +- pci_set_drvdata(pdev, NULL); +- return ret; +-} +-EXPORT_SYMBOL_GPL(mantis_pci_init); +- +-void mantis_pci_exit(struct mantis_pci *mantis) +-{ +- struct pci_dev *pdev = mantis->pdev; +- +- dprintk(MANTIS_NOTICE, 1, " mem: 0x%p", mantis->mmio); +- free_irq(pdev->irq, mantis); +- if (mantis->mmio) { +- iounmap(mantis->mmio); +- release_mem_region(pci_resource_start(pdev, 0), +- pci_resource_len(pdev, 0)); +- } +- +- pci_disable_device(pdev); +- pci_set_drvdata(pdev, NULL); +-} +-EXPORT_SYMBOL_GPL(mantis_pci_exit); +- +-MODULE_DESCRIPTION("Mantis PCI DTV bridge driver"); +-MODULE_AUTHOR("Manu Abraham"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/mantis/mantis_pci.h b/drivers/media/dvb/mantis/mantis_pci.h +deleted file mode 100644 +index 65f0045..0000000 +--- a/drivers/media/dvb/mantis/mantis_pci.h ++++ /dev/null +@@ -1,27 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_PCI_H +-#define __MANTIS_PCI_H +- +-extern int mantis_pci_init(struct mantis_pci *mantis); +-extern void mantis_pci_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_PCI_H */ +diff --git a/drivers/media/dvb/mantis/mantis_pcmcia.c b/drivers/media/dvb/mantis/mantis_pcmcia.c +deleted file mode 100644 +index 2f188c0..0000000 +--- a/drivers/media/dvb/mantis/mantis_pcmcia.c ++++ /dev/null +@@ -1,121 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_link.h" /* temporary due to physical layer stuff */ +-#include "mantis_reg.h" +- +-/* +- * If Slot state is already PLUG_IN event and we are called +- * again, definitely it is jitter alone +- */ +-void mantis_event_cam_plugin(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- u32 gpif_irqcfg; +- +- if (ca->slot_state == MODULE_XTRACTED) { +- dprintk(MANTIS_DEBUG, 1, "Event: CAM Plugged IN: Adapter(%d) Slot(0)", mantis->num); +- udelay(50); +- mmwrite(0xda000000, MANTIS_CARD_RESET); +- gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); +- gpif_irqcfg |= MANTIS_MASK_PLUGOUT; +- gpif_irqcfg &= ~MANTIS_MASK_PLUGIN; +- mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); +- udelay(500); +- ca->slot_state = MODULE_INSERTED; +- } +- udelay(100); +-} +- +-/* +- * If Slot state is already UN_PLUG event and we are called +- * again, definitely it is jitter alone +- */ +-void mantis_event_cam_unplug(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- u32 gpif_irqcfg; +- +- if (ca->slot_state == MODULE_INSERTED) { +- dprintk(MANTIS_DEBUG, 1, "Event: CAM Unplugged: Adapter(%d) Slot(0)", mantis->num); +- udelay(50); +- mmwrite(0x00da0000, MANTIS_CARD_RESET); +- gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); +- gpif_irqcfg |= MANTIS_MASK_PLUGIN; +- gpif_irqcfg &= ~MANTIS_MASK_PLUGOUT; +- mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); +- udelay(500); +- ca->slot_state = MODULE_XTRACTED; +- } +- udelay(100); +-} +- +-int mantis_pcmcia_init(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- u32 gpif_stat, card_stat; +- +- mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK); +- gpif_stat = mmread(MANTIS_GPIF_STATUS); +- card_stat = mmread(MANTIS_GPIF_IRQCFG); +- +- if (gpif_stat & MANTIS_GPIF_DETSTAT) { +- dprintk(MANTIS_DEBUG, 1, "CAM found on Adapter(%d) Slot(0)", mantis->num); +- mmwrite(card_stat | MANTIS_MASK_PLUGOUT, MANTIS_GPIF_IRQCFG); +- ca->slot_state = MODULE_INSERTED; +- dvb_ca_en50221_camchange_irq(&ca->en50221, +- 0, +- DVB_CA_EN50221_CAMCHANGE_INSERTED); +- } else { +- dprintk(MANTIS_DEBUG, 1, "Empty Slot on Adapter(%d) Slot(0)", mantis->num); +- mmwrite(card_stat | MANTIS_MASK_PLUGIN, MANTIS_GPIF_IRQCFG); +- ca->slot_state = MODULE_XTRACTED; +- dvb_ca_en50221_camchange_irq(&ca->en50221, +- 0, +- DVB_CA_EN50221_CAMCHANGE_REMOVED); +- } +- +- return 0; +-} +- +-void mantis_pcmcia_exit(struct mantis_ca *ca) +-{ +- struct mantis_pci *mantis = ca->ca_priv; +- +- mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS); +- mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK); +-} +diff --git a/drivers/media/dvb/mantis/mantis_reg.h b/drivers/media/dvb/mantis/mantis_reg.h +deleted file mode 100644 +index 7761f9d..0000000 +--- a/drivers/media/dvb/mantis/mantis_reg.h ++++ /dev/null +@@ -1,197 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_REG_H +-#define __MANTIS_REG_H +- +-/* Interrupts */ +-#define MANTIS_INT_STAT 0x00 +-#define MANTIS_INT_MASK 0x04 +- +-#define MANTIS_INT_RISCSTAT (0x0f << 28) +-#define MANTIS_INT_RISCEN (0x01 << 27) +-#define MANTIS_INT_I2CRACK (0x01 << 26) +- +-/* #define MANTIS_INT_GPIF (0xff << 12) */ +- +-#define MANTIS_INT_PCMCIA7 (0x01 << 19) +-#define MANTIS_INT_PCMCIA6 (0x01 << 18) +-#define MANTIS_INT_PCMCIA5 (0x01 << 17) +-#define MANTIS_INT_PCMCIA4 (0x01 << 16) +-#define MANTIS_INT_PCMCIA3 (0x01 << 15) +-#define MANTIS_INT_PCMCIA2 (0x01 << 14) +-#define MANTIS_INT_PCMCIA1 (0x01 << 13) +-#define MANTIS_INT_PCMCIA0 (0x01 << 12) +-#define MANTIS_INT_IRQ1 (0x01 << 11) +-#define MANTIS_INT_IRQ0 (0x01 << 10) +-#define MANTIS_INT_OCERR (0x01 << 8) +-#define MANTIS_INT_PABORT (0x01 << 7) +-#define MANTIS_INT_RIPERR (0x01 << 6) +-#define MANTIS_INT_PPERR (0x01 << 5) +-#define MANTIS_INT_FTRGT (0x01 << 3) +-#define MANTIS_INT_RISCI (0x01 << 1) +-#define MANTIS_INT_I2CDONE (0x01 << 0) +- +-/* DMA */ +-#define MANTIS_DMA_CTL 0x08 +-#define MANTIS_GPIF_RD (0xff << 24) +-#define MANTIS_GPIF_WR (0xff << 16) +-#define MANTIS_CPU_DO (0x01 << 10) +-#define MANTIS_DRV_DO (0x01 << 9) +-#define MANTIS_I2C_RD (0x01 << 7) +-#define MANTIS_I2C_WR (0x01 << 6) +-#define MANTIS_DCAP_MODE (0x01 << 5) +-#define MANTIS_FIFO_TP_4 (0x00 << 3) +-#define MANTIS_FIFO_TP_8 (0x01 << 3) +-#define MANTIS_FIFO_TP_16 (0x02 << 3) +-#define MANTIS_FIFO_EN (0x01 << 2) +-#define MANTIS_DCAP_EN (0x01 << 1) +-#define MANTIS_RISC_EN (0x01 << 0) +- +-/* DEBUG */ +-#define MANTIS_DEBUGREG 0x0c +-#define MANTIS_DATINV (0x0e << 7) +-#define MANTIS_TOP_DEBUGSEL (0x07 << 4) +-#define MANTIS_PCMCIA_DEBUGSEL (0x0f << 0) +- +-#define MANTIS_RISC_START 0x10 +-#define MANTIS_RISC_PC 0x14 +- +-/* I2C */ +-#define MANTIS_I2CDATA_CTL 0x18 +-#define MANTIS_I2C_RATE_1 (0x00 << 6) +-#define MANTIS_I2C_RATE_2 (0x01 << 6) +-#define MANTIS_I2C_RATE_3 (0x02 << 6) +-#define MANTIS_I2C_RATE_4 (0x03 << 6) +-#define MANTIS_I2C_STOP (0x01 << 5) +-#define MANTIS_I2C_PGMODE (0x01 << 3) +- +-/* DATA */ +-#define MANTIS_CMD_DATA_R1 0x20 +-#define MANTIS_CMD_DATA_3 (0xff << 24) +-#define MANTIS_CMD_DATA_2 (0xff << 16) +-#define MANTIS_CMD_DATA_1 (0xff << 8) +-#define MANTIS_CMD_DATA_0 (0xff << 0) +- +-#define MANTIS_CMD_DATA_R2 0x24 +-#define MANTIS_CMD_DATA_7 (0xff << 24) +-#define MANTIS_CMD_DATA_6 (0xff << 16) +-#define MANTIS_CMD_DATA_5 (0xff << 8) +-#define MANTIS_CMD_DATA_4 (0xff << 0) +- +-#define MANTIS_CONTROL 0x28 +-#define MANTIS_DET (0x01 << 7) +-#define MANTIS_DAT_CF_EN (0x01 << 6) +-#define MANTIS_ACS (0x03 << 4) +-#define MANTIS_VCCEN (0x01 << 3) +-#define MANTIS_BYPASS (0x01 << 2) +-#define MANTIS_MRST (0x01 << 1) +-#define MANTIS_CRST_INT (0x01 << 0) +- +-#define MANTIS_GPIF_CFGSLA 0x84 +-#define MANTIS_GPIF_WAITSMPL (0x07 << 28) +-#define MANTIS_GPIF_BYTEADDRSUB (0x01 << 25) +-#define MANTIS_GPIF_WAITPOL (0x01 << 24) +-#define MANTIS_GPIF_NCDELAY (0x07 << 20) +-#define MANTIS_GPIF_RW2CSDELAY (0x07 << 16) +-#define MANTIS_GPIF_SLFTIMEDMODE (0x01 << 15) +-#define MANTIS_GPIF_SLFTIMEDDELY (0x7f << 8) +-#define MANTIS_GPIF_DEVTYPE (0x07 << 4) +-#define MANTIS_GPIF_BIGENDIAN (0x01 << 3) +-#define MANTIS_GPIF_FETCHCMD (0x03 << 1) +-#define MANTIS_GPIF_HWORDDEV (0x01 << 0) +- +-#define MANTIS_GPIF_WSTOPER 0x90 +-#define MANTIS_GPIF_WSTOPERWREN3 (0x01 << 31) +-#define MANTIS_GPIF_PARBOOTN (0x01 << 29) +-#define MANTIS_GPIF_WSTOPERSLID3 (0x1f << 24) +-#define MANTIS_GPIF_WSTOPERWREN2 (0x01 << 23) +-#define MANTIS_GPIF_WSTOPERSLID2 (0x1f << 16) +-#define MANTIS_GPIF_WSTOPERWREN1 (0x01 << 15) +-#define MANTIS_GPIF_WSTOPERSLID1 (0x1f << 8) +-#define MANTIS_GPIF_WSTOPERWREN0 (0x01 << 7) +-#define MANTIS_GPIF_WSTOPERSLID0 (0x1f << 0) +- +-#define MANTIS_GPIF_CS2RW 0x94 +-#define MANTIS_GPIF_CS2RWWREN3 (0x01 << 31) +-#define MANTIS_GPIF_CS2RWDELY3 (0x3f << 24) +-#define MANTIS_GPIF_CS2RWWREN2 (0x01 << 23) +-#define MANTIS_GPIF_CS2RWDELY2 (0x3f << 16) +-#define MANTIS_GPIF_CS2RWWREN1 (0x01 << 15) +-#define MANTIS_GPIF_CS2RWDELY1 (0x3f << 8) +-#define MANTIS_GPIF_CS2RWWREN0 (0x01 << 7) +-#define MANTIS_GPIF_CS2RWDELY0 (0x3f << 0) +- +-#define MANTIS_GPIF_IRQCFG 0x98 +-#define MANTIS_GPIF_IRQPOL (0x01 << 8) +-#define MANTIS_MASK_WRACK (0x01 << 7) +-#define MANTIS_MASK_BRRDY (0x01 << 6) +-#define MANTIS_MASK_OVFLW (0x01 << 5) +-#define MANTIS_MASK_OTHERR (0x01 << 4) +-#define MANTIS_MASK_WSTO (0x01 << 3) +-#define MANTIS_MASK_EXTIRQ (0x01 << 2) +-#define MANTIS_MASK_PLUGIN (0x01 << 1) +-#define MANTIS_MASK_PLUGOUT (0x01 << 0) +- +-#define MANTIS_GPIF_STATUS 0x9c +-#define MANTIS_SBUF_KILLOP (0x01 << 15) +-#define MANTIS_SBUF_OPDONE (0x01 << 14) +-#define MANTIS_SBUF_EMPTY (0x01 << 13) +-#define MANTIS_GPIF_DETSTAT (0x01 << 9) +-#define MANTIS_GPIF_INTSTAT (0x01 << 8) +-#define MANTIS_GPIF_WRACK (0x01 << 7) +-#define MANTIS_GPIF_BRRDY (0x01 << 6) +-#define MANTIS_SBUF_OVFLW (0x01 << 5) +-#define MANTIS_GPIF_OTHERR (0x01 << 4) +-#define MANTIS_SBUF_WSTO (0x01 << 3) +-#define MANTIS_GPIF_EXTIRQ (0x01 << 2) +-#define MANTIS_CARD_PLUGIN (0x01 << 1) +-#define MANTIS_CARD_PLUGOUT (0x01 << 0) +- +-#define MANTIS_GPIF_BRADDR 0xa0 +-#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) +-#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) +-#define MANTIS_GPIF_BR_ADDR (0xfffffff << 0) +- +-#define MANTIS_GPIF_BRBYTES 0xa4 +-#define MANTIS_GPIF_BRCNT (0xfff << 0) +- +-#define MANTIS_PCMCIA_RESET 0xa8 +-#define MANTIS_PCMCIA_RSTVAL (0xff << 0) +- +-#define MANTIS_CARD_RESET 0xac +- +-#define MANTIS_GPIF_ADDR 0xb0 +-#define MANTIS_GPIF_HIFRDWRN (0x01 << 31) +-#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) +-#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) +-#define MANTIS_GPIF_HIFADDR (0xfffffff << 0) +- +-#define MANTIS_GPIF_DOUT 0xb4 +-#define MANTIS_GPIF_HIFDOUT (0xfffffff << 0) +- +-#define MANTIS_GPIF_DIN 0xb8 +-#define MANTIS_GPIF_HIFDIN (0xfffffff << 0) +- +-#define MANTIS_GPIF_SPARE 0xbc +-#define MANTIS_GPIF_LOGICRD (0xffff << 16) +-#define MANTIS_GPIF_LOGICRW (0xffff << 0) +- +-#endif /* __MANTIS_REG_H */ +diff --git a/drivers/media/dvb/mantis/mantis_uart.c b/drivers/media/dvb/mantis/mantis_uart.c +deleted file mode 100644 +index 18340da..0000000 +--- a/drivers/media/dvb/mantis/mantis_uart.c ++++ /dev/null +@@ -1,188 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_reg.h" +-#include "mantis_uart.h" +- +-struct mantis_uart_params { +- enum mantis_baud baud_rate; +- enum mantis_parity parity; +-}; +- +-static struct { +- char string[7]; +-} rates[5] = { +- { "9600" }, +- { "19200" }, +- { "38400" }, +- { "57600" }, +- { "115200" } +-}; +- +-static struct { +- char string[5]; +-} parity[3] = { +- { "NONE" }, +- { "ODD" }, +- { "EVEN" } +-}; +- +-#define UART_MAX_BUF 16 +- +-int mantis_uart_read(struct mantis_pci *mantis, u8 *data) +-{ +- struct mantis_hwconfig *config = mantis->hwconfig; +- u32 stat = 0, i; +- +- /* get data */ +- for (i = 0; i < (config->bytes + 1); i++) { +- +- stat = mmread(MANTIS_UART_STAT); +- +- if (stat & MANTIS_UART_RXFIFO_FULL) { +- dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); +- } +- data[i] = mmread(MANTIS_UART_RXD) & 0x3f; +- +- dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f); +- +- if (data[i] & (1 << 7)) { +- dprintk(MANTIS_ERROR, 1, "UART framing error"); +- return -EINVAL; +- } +- if (data[i] & (1 << 6)) { +- dprintk(MANTIS_ERROR, 1, "UART parity error"); +- return -EINVAL; +- } +- } +- +- return 0; +-} +- +-static void mantis_uart_work(struct work_struct *work) +-{ +- struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); +- struct mantis_hwconfig *config = mantis->hwconfig; +- u8 buf[16]; +- int i; +- +- mantis_uart_read(mantis, buf); +- +- for (i = 0; i < (config->bytes + 1); i++) +- dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]); +- +- dprintk(MANTIS_DEBUG, 0, "\n"); +-} +- +-static int mantis_uart_setup(struct mantis_pci *mantis, +- struct mantis_uart_params *params) +-{ +- u32 reg; +- +- mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL); +- +- reg = mmread(MANTIS_UART_BAUD); +- +- switch (params->baud_rate) { +- case MANTIS_BAUD_9600: +- reg |= 0xd8; +- break; +- case MANTIS_BAUD_19200: +- reg |= 0x6c; +- break; +- case MANTIS_BAUD_38400: +- reg |= 0x36; +- break; +- case MANTIS_BAUD_57600: +- reg |= 0x23; +- break; +- case MANTIS_BAUD_115200: +- reg |= 0x11; +- break; +- default: +- return -EINVAL; +- } +- +- mmwrite(reg, MANTIS_UART_BAUD); +- +- return 0; +-} +- +-int mantis_uart_init(struct mantis_pci *mantis) +-{ +- struct mantis_hwconfig *config = mantis->hwconfig; +- struct mantis_uart_params params; +- +- /* default parity: */ +- params.baud_rate = config->baud_rate; +- params.parity = config->parity; +- dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s", +- rates[params.baud_rate].string, +- parity[params.parity].string); +- +- init_waitqueue_head(&mantis->uart_wq); +- spin_lock_init(&mantis->uart_lock); +- +- INIT_WORK(&mantis->uart_work, mantis_uart_work); +- +- /* disable interrupt */ +- mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); +- +- mantis_uart_setup(mantis, ¶ms); +- +- /* default 1 byte */ +- mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD); +- +- /* flush buffer */ +- mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL); +- +- /* enable interrupt */ +- mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK); +- mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL); +- +- schedule_work(&mantis->uart_work); +- dprintk(MANTIS_DEBUG, 1, "UART successfully initialized"); +- +- return 0; +-} +-EXPORT_SYMBOL_GPL(mantis_uart_init); +- +-void mantis_uart_exit(struct mantis_pci *mantis) +-{ +- /* disable interrupt */ +- mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); +- flush_work_sync(&mantis->uart_work); +-} +-EXPORT_SYMBOL_GPL(mantis_uart_exit); +diff --git a/drivers/media/dvb/mantis/mantis_uart.h b/drivers/media/dvb/mantis/mantis_uart.h +deleted file mode 100644 +index ffb62a0..0000000 +--- a/drivers/media/dvb/mantis/mantis_uart.h ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- Mantis PCI bridge driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_UART_H +-#define __MANTIS_UART_H +- +-#define MANTIS_UART_CTL 0xe0 +-#define MANTIS_UART_RXINT (1 << 4) +-#define MANTIS_UART_RXFLUSH (1 << 2) +- +-#define MANTIS_UART_RXD 0xe8 +-#define MANTIS_UART_BAUD 0xec +- +-#define MANTIS_UART_STAT 0xf0 +-#define MANTIS_UART_RXFIFO_DATA (1 << 7) +-#define MANTIS_UART_RXFIFO_EMPTY (1 << 6) +-#define MANTIS_UART_RXFIFO_FULL (1 << 3) +-#define MANTIS_UART_FRAME_ERR (1 << 2) +-#define MANTIS_UART_PARITY_ERR (1 << 1) +-#define MANTIS_UART_RXTHRESH_INT (1 << 0) +- +-enum mantis_baud { +- MANTIS_BAUD_9600 = 0, +- MANTIS_BAUD_19200, +- MANTIS_BAUD_38400, +- MANTIS_BAUD_57600, +- MANTIS_BAUD_115200 +-}; +- +-enum mantis_parity { +- MANTIS_PARITY_NONE = 0, +- MANTIS_PARITY_EVEN, +- MANTIS_PARITY_ODD, +-}; +- +-struct mantis_pci; +- +-extern int mantis_uart_init(struct mantis_pci *mantis); +-extern void mantis_uart_exit(struct mantis_pci *mantis); +- +-#endif /* __MANTIS_UART_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp1033.c b/drivers/media/dvb/mantis/mantis_vp1033.c +deleted file mode 100644 +index ad013e9..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1033.c ++++ /dev/null +@@ -1,212 +0,0 @@ +-/* +- Mantis VP-1033 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "stv0299.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp1033.h" +-#include "mantis_reg.h" +- +-u8 lgtdqcs001f_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x2a, +- 0x05, 0x85, +- 0x06, 0x02, +- 0x07, 0x00, +- 0x08, 0x00, +- 0x0c, 0x01, +- 0x0d, 0x81, +- 0x0e, 0x44, +- 0x0f, 0x94, +- 0x10, 0x3c, +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x13, 0xb5, +- 0x14, 0x4f, +- 0x15, 0xc9, +- 0x16, 0x80, +- 0x17, 0x36, +- 0x18, 0xfb, +- 0x19, 0xcf, +- 0x1a, 0xbc, +- 0x1c, 0x2b, +- 0x1d, 0x27, +- 0x1e, 0x00, +- 0x1f, 0x0b, +- 0x20, 0xa1, +- 0x21, 0x60, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, +- 0x29, 0x28, +- 0x2a, 0x14, +- 0x2b, 0x0f, +- 0x2c, 0x09, +- 0x2d, 0x05, +- 0x31, 0x1f, +- 0x32, 0x19, +- 0x33, 0xfc, +- 0x34, 0x13, +- 0xff, 0xff, +-}; +- +-#define MANTIS_MODEL_NAME "VP-1033" +-#define MANTIS_DEV_TYPE "DVB-S/DSS" +- +-int lgtdqcs001f_tuner_set(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mantis_pci *mantis = fe->dvb->priv; +- struct i2c_adapter *adapter = &mantis->adapter; +- +- u8 buf[4]; +- u32 div; +- +- +- struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf)}; +- +- div = p->frequency / 250; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x83; +- buf[3] = 0xc0; +- +- if (p->frequency < 1531000) +- buf[3] |= 0x04; +- else +- buf[3] &= ~0x04; +- if (i2c_transfer(adapter, &msg, 1) < 0) { +- dprintk(MANTIS_ERROR, 1, "Write: I2C Transfer failed"); +- return -EIO; +- } +- msleep_interruptible(100); +- +- return 0; +-} +- +-int lgtdqcs001f_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; +-} +- +-struct stv0299_config lgtdqcs001f_config = { +- .demod_address = 0x68, +- .inittab = lgtdqcs001f_inittab, +- .mclk = 88000000UL, +- .invert = 0, +- .skip_reinit = 0, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .min_delay_ms = 100, +- .set_symbol_rate = lgtdqcs001f_set_symbol_rate, +-}; +- +-static int vp1033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- int err = 0; +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- mantis_frontend_soft_reset(mantis); +- msleep(250); +- +- dprintk(MANTIS_ERROR, 1, "Probing for STV0299 (DVB-S)"); +- fe = dvb_attach(stv0299_attach, &lgtdqcs001f_config, adapter); +- +- if (fe) { +- fe->ops.tuner_ops.set_params = lgtdqcs001f_tuner_set; +- dprintk(MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x", +- lgtdqcs001f_config.demod_address); +- +- dprintk(MANTIS_ERROR, 1, "Mantis DVB-S STV0299 frontend attach success"); +- } else { +- return -1; +- } +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- mantis->fe = fe; +- dprintk(MANTIS_ERROR, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp1033_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_204, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp1033_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp1033.h b/drivers/media/dvb/mantis/mantis_vp1033.h +deleted file mode 100644 +index 7daaa1b..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1033.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Mantis VP-1033 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP1033_H +-#define __MANTIS_VP1033_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_1033_DVB_S 0x0016 +- +-extern struct mantis_hwconfig vp1033_config; +- +-#endif /* __MANTIS_VP1033_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp1034.c b/drivers/media/dvb/mantis/mantis_vp1034.c +deleted file mode 100644 +index 430ae84..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1034.c ++++ /dev/null +@@ -1,120 +0,0 @@ +-/* +- Mantis VP-1034 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mb86a16.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp1034.h" +-#include "mantis_reg.h" +- +-struct mb86a16_config vp1034_mb86a16_config = { +- .demod_address = 0x08, +- .set_voltage = vp1034_set_voltage, +-}; +- +-#define MANTIS_MODEL_NAME "VP-1034" +-#define MANTIS_DEV_TYPE "DVB-S/DSS" +- +-int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct mantis_pci *mantis = fe->dvb->priv; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- dprintk(MANTIS_ERROR, 1, "Polarization=[13V]"); +- mantis_gpio_set_bits(mantis, 13, 1); +- mantis_gpio_set_bits(mantis, 14, 0); +- break; +- case SEC_VOLTAGE_18: +- dprintk(MANTIS_ERROR, 1, "Polarization=[18V]"); +- mantis_gpio_set_bits(mantis, 13, 1); +- mantis_gpio_set_bits(mantis, 14, 1); +- break; +- case SEC_VOLTAGE_OFF: +- dprintk(MANTIS_ERROR, 1, "Frontend (dummy) POWERDOWN"); +- break; +- default: +- dprintk(MANTIS_ERROR, 1, "Invalid = (%d)", (u32) voltage); +- return -EINVAL; +- } +- mmwrite(0x00, MANTIS_GPIF_DOUT); +- +- return 0; +-} +- +-static int vp1034_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- int err = 0; +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- mantis_frontend_soft_reset(mantis); +- msleep(250); +- +- dprintk(MANTIS_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)"); +- fe = dvb_attach(mb86a16_attach, &vp1034_mb86a16_config, adapter); +- if (fe) { +- dprintk(MANTIS_ERROR, 1, +- "found MB86A16 DVB-S/DSS frontend @0x%02x", +- vp1034_mb86a16_config.demod_address); +- +- } else { +- return -1; +- } +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- mantis->fe = fe; +- dprintk(MANTIS_ERROR, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp1034_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_204, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp1034_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp1034.h b/drivers/media/dvb/mantis/mantis_vp1034.h +deleted file mode 100644 +index 323f38e..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1034.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* +- Mantis VP-1034 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP1034_H +-#define __MANTIS_VP1034_H +- +-#include "dvb_frontend.h" +-#include "mantis_common.h" +- +- +-#define MANTIS_VP_1034_DVB_S 0x0014 +- +-extern struct mantis_hwconfig vp1034_config; +-extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); +- +-#endif /* __MANTIS_VP1034_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp1041.c b/drivers/media/dvb/mantis/mantis_vp1041.c +deleted file mode 100644 +index 07aa887..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1041.c ++++ /dev/null +@@ -1,357 +0,0 @@ +-/* +- Mantis VP-1041 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp1041.h" +-#include "stb0899_reg.h" +-#include "stb0899_drv.h" +-#include "stb0899_cfg.h" +-#include "stb6100_cfg.h" +-#include "stb6100.h" +-#include "lnbp21.h" +- +-#define MANTIS_MODEL_NAME "VP-1041" +-#define MANTIS_DEV_TYPE "DSS/DVB-S/DVB-S2" +- +-static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = { +- +- /* 0x0000000b, *//* SYSREG */ +- { STB0899_DEV_ID , 0x30 }, +- { STB0899_DISCNTRL1 , 0x32 }, +- { STB0899_DISCNTRL2 , 0x80 }, +- { STB0899_DISRX_ST0 , 0x04 }, +- { STB0899_DISRX_ST1 , 0x00 }, +- { STB0899_DISPARITY , 0x00 }, +- { STB0899_DISSTATUS , 0x20 }, +- { STB0899_DISF22 , 0x99 }, +- { STB0899_DISF22RX , 0xa8 }, +- /* SYSREG ? */ +- { STB0899_ACRPRESC , 0x11 }, +- { STB0899_ACRDIV1 , 0x0a }, +- { STB0899_ACRDIV2 , 0x05 }, +- { STB0899_DACR1 , 0x00 }, +- { STB0899_DACR2 , 0x00 }, +- { STB0899_OUTCFG , 0x00 }, +- { STB0899_MODECFG , 0x00 }, +- { STB0899_IRQSTATUS_3 , 0xfe }, +- { STB0899_IRQSTATUS_2 , 0x03 }, +- { STB0899_IRQSTATUS_1 , 0x7c }, +- { STB0899_IRQSTATUS_0 , 0xf4 }, +- { STB0899_IRQMSK_3 , 0xf3 }, +- { STB0899_IRQMSK_2 , 0xfc }, +- { STB0899_IRQMSK_1 , 0xff }, +- { STB0899_IRQMSK_0 , 0xff }, +- { STB0899_IRQCFG , 0x00 }, +- { STB0899_I2CCFG , 0x88 }, +- { STB0899_I2CRPT , 0x58 }, +- { STB0899_IOPVALUE5 , 0x00 }, +- { STB0899_IOPVALUE4 , 0x33 }, +- { STB0899_IOPVALUE3 , 0x6d }, +- { STB0899_IOPVALUE2 , 0x90 }, +- { STB0899_IOPVALUE1 , 0x60 }, +- { STB0899_IOPVALUE0 , 0x00 }, +- { STB0899_GPIO00CFG , 0x82 }, +- { STB0899_GPIO01CFG , 0x82 }, +- { STB0899_GPIO02CFG , 0x82 }, +- { STB0899_GPIO03CFG , 0x82 }, +- { STB0899_GPIO04CFG , 0x82 }, +- { STB0899_GPIO05CFG , 0x82 }, +- { STB0899_GPIO06CFG , 0x82 }, +- { STB0899_GPIO07CFG , 0x82 }, +- { STB0899_GPIO08CFG , 0x82 }, +- { STB0899_GPIO09CFG , 0x82 }, +- { STB0899_GPIO10CFG , 0x82 }, +- { STB0899_GPIO11CFG , 0x82 }, +- { STB0899_GPIO12CFG , 0x82 }, +- { STB0899_GPIO13CFG , 0x82 }, +- { STB0899_GPIO14CFG , 0x82 }, +- { STB0899_GPIO15CFG , 0x82 }, +- { STB0899_GPIO16CFG , 0x82 }, +- { STB0899_GPIO17CFG , 0x82 }, +- { STB0899_GPIO18CFG , 0x82 }, +- { STB0899_GPIO19CFG , 0x82 }, +- { STB0899_GPIO20CFG , 0x82 }, +- { STB0899_SDATCFG , 0xb8 }, +- { STB0899_SCLTCFG , 0xba }, +- { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ +- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ +- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ +- { STB0899_DIRCLKCFG , 0x82 }, +- { STB0899_CLKOUT27CFG , 0x7e }, +- { STB0899_STDBYCFG , 0x82 }, +- { STB0899_CS0CFG , 0x82 }, +- { STB0899_CS1CFG , 0x82 }, +- { STB0899_DISEQCOCFG , 0x20 }, +- { STB0899_GPIO32CFG , 0x82 }, +- { STB0899_GPIO33CFG , 0x82 }, +- { STB0899_GPIO34CFG , 0x82 }, +- { STB0899_GPIO35CFG , 0x82 }, +- { STB0899_GPIO36CFG , 0x82 }, +- { STB0899_GPIO37CFG , 0x82 }, +- { STB0899_GPIO38CFG , 0x82 }, +- { STB0899_GPIO39CFG , 0x82 }, +- { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ +- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ +- { STB0899_FILTCTRL , 0x00 }, +- { STB0899_SYSCTRL , 0x01 }, +- { STB0899_STOPCLK1 , 0x20 }, +- { STB0899_STOPCLK2 , 0x00 }, +- { STB0899_INTBUFSTATUS , 0x00 }, +- { STB0899_INTBUFCTRL , 0x0a }, +- { 0xffff , 0xff }, +-}; +- +-static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { +- { STB0899_DEMOD , 0x00 }, +- { STB0899_RCOMPC , 0xc9 }, +- { STB0899_AGC1CN , 0x01 }, +- { STB0899_AGC1REF , 0x10 }, +- { STB0899_RTC , 0x23 }, +- { STB0899_TMGCFG , 0x4e }, +- { STB0899_AGC2REF , 0x34 }, +- { STB0899_TLSR , 0x84 }, +- { STB0899_CFD , 0xf7 }, +- { STB0899_ACLC , 0x87 }, +- { STB0899_BCLC , 0x94 }, +- { STB0899_EQON , 0x41 }, +- { STB0899_LDT , 0xf1 }, +- { STB0899_LDT2 , 0xe3 }, +- { STB0899_EQUALREF , 0xb4 }, +- { STB0899_TMGRAMP , 0x10 }, +- { STB0899_TMGTHD , 0x30 }, +- { STB0899_IDCCOMP , 0xfd }, +- { STB0899_QDCCOMP , 0xff }, +- { STB0899_POWERI , 0x0c }, +- { STB0899_POWERQ , 0x0f }, +- { STB0899_RCOMP , 0x6c }, +- { STB0899_AGCIQIN , 0x80 }, +- { STB0899_AGC2I1 , 0x06 }, +- { STB0899_AGC2I2 , 0x00 }, +- { STB0899_TLIR , 0x30 }, +- { STB0899_RTF , 0x7f }, +- { STB0899_DSTATUS , 0x00 }, +- { STB0899_LDI , 0xbc }, +- { STB0899_CFRM , 0xea }, +- { STB0899_CFRL , 0x31 }, +- { STB0899_NIRM , 0x2b }, +- { STB0899_NIRL , 0x80 }, +- { STB0899_ISYMB , 0x1d }, +- { STB0899_QSYMB , 0xa6 }, +- { STB0899_SFRH , 0x2f }, +- { STB0899_SFRM , 0x68 }, +- { STB0899_SFRL , 0x40 }, +- { STB0899_SFRUPH , 0x2f }, +- { STB0899_SFRUPM , 0x68 }, +- { STB0899_SFRUPL , 0x40 }, +- { STB0899_EQUAI1 , 0x02 }, +- { STB0899_EQUAQ1 , 0xff }, +- { STB0899_EQUAI2 , 0x04 }, +- { STB0899_EQUAQ2 , 0x05 }, +- { STB0899_EQUAI3 , 0x02 }, +- { STB0899_EQUAQ3 , 0xfd }, +- { STB0899_EQUAI4 , 0x03 }, +- { STB0899_EQUAQ4 , 0x07 }, +- { STB0899_EQUAI5 , 0x08 }, +- { STB0899_EQUAQ5 , 0xf5 }, +- { STB0899_DSTATUS2 , 0x00 }, +- { STB0899_VSTATUS , 0x00 }, +- { STB0899_VERROR , 0x86 }, +- { STB0899_IQSWAP , 0x2a }, +- { STB0899_ECNT1M , 0x00 }, +- { STB0899_ECNT1L , 0x00 }, +- { STB0899_ECNT2M , 0x00 }, +- { STB0899_ECNT2L , 0x00 }, +- { STB0899_ECNT3M , 0x0a }, +- { STB0899_ECNT3L , 0xad }, +- { STB0899_FECAUTO1 , 0x06 }, +- { STB0899_FECM , 0x01 }, +- { STB0899_VTH12 , 0xb0 }, +- { STB0899_VTH23 , 0x7a }, +- { STB0899_VTH34 , 0x58 }, +- { STB0899_VTH56 , 0x38 }, +- { STB0899_VTH67 , 0x34 }, +- { STB0899_VTH78 , 0x24 }, +- { STB0899_PRVIT , 0xff }, +- { STB0899_VITSYNC , 0x19 }, +- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ +- { STB0899_TSULC , 0x42 }, +- { STB0899_RSLLC , 0x41 }, +- { STB0899_TSLPL , 0x12 }, +- { STB0899_TSCFGH , 0x0c }, +- { STB0899_TSCFGM , 0x00 }, +- { STB0899_TSCFGL , 0x00 }, +- { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ +- { STB0899_RSSYNCDEL , 0x00 }, +- { STB0899_TSINHDELH , 0x02 }, +- { STB0899_TSINHDELM , 0x00 }, +- { STB0899_TSINHDELL , 0x00 }, +- { STB0899_TSLLSTKM , 0x1b }, +- { STB0899_TSLLSTKL , 0xb3 }, +- { STB0899_TSULSTKM , 0x00 }, +- { STB0899_TSULSTKL , 0x00 }, +- { STB0899_PCKLENUL , 0xbc }, +- { STB0899_PCKLENLL , 0xcc }, +- { STB0899_RSPCKLEN , 0xbd }, +- { STB0899_TSSTATUS , 0x90 }, +- { STB0899_ERRCTRL1 , 0xb6 }, +- { STB0899_ERRCTRL2 , 0x95 }, +- { STB0899_ERRCTRL3 , 0x8d }, +- { STB0899_DMONMSK1 , 0x27 }, +- { STB0899_DMONMSK0 , 0x03 }, +- { STB0899_DEMAPVIT , 0x5c }, +- { STB0899_PLPARM , 0x19 }, +- { STB0899_PDELCTRL , 0x48 }, +- { STB0899_PDELCTRL2 , 0x00 }, +- { STB0899_BBHCTRL1 , 0x00 }, +- { STB0899_BBHCTRL2 , 0x00 }, +- { STB0899_HYSTTHRESH , 0x77 }, +- { STB0899_MATCSTM , 0x00 }, +- { STB0899_MATCSTL , 0x00 }, +- { STB0899_UPLCSTM , 0x00 }, +- { STB0899_UPLCSTL , 0x00 }, +- { STB0899_DFLCSTM , 0x00 }, +- { STB0899_DFLCSTL , 0x00 }, +- { STB0899_SYNCCST , 0x00 }, +- { STB0899_SYNCDCSTM , 0x00 }, +- { STB0899_SYNCDCSTL , 0x00 }, +- { STB0899_ISI_ENTRY , 0x00 }, +- { STB0899_ISI_BIT_EN , 0x00 }, +- { STB0899_MATSTRM , 0xf0 }, +- { STB0899_MATSTRL , 0x02 }, +- { STB0899_UPLSTRM , 0x45 }, +- { STB0899_UPLSTRL , 0x60 }, +- { STB0899_DFLSTRM , 0xe3 }, +- { STB0899_DFLSTRL , 0x00 }, +- { STB0899_SYNCSTR , 0x47 }, +- { STB0899_SYNCDSTRM , 0x05 }, +- { STB0899_SYNCDSTRL , 0x18 }, +- { STB0899_CFGPDELSTATUS1 , 0x19 }, +- { STB0899_CFGPDELSTATUS2 , 0x2b }, +- { STB0899_BBFERRORM , 0x00 }, +- { STB0899_BBFERRORL , 0x01 }, +- { STB0899_UPKTERRORM , 0x00 }, +- { STB0899_UPKTERRORL , 0x00 }, +- { 0xffff , 0xff }, +-}; +- +-struct stb0899_config vp1041_stb0899_config = { +- .init_dev = vp1041_stb0899_s1_init_1, +- .init_s2_demod = stb0899_s2_init_2, +- .init_s1_demod = vp1041_stb0899_s1_init_3, +- .init_s2_fec = stb0899_s2_init_4, +- .init_tst = stb0899_s1_init_5, +- +- .demod_address = 0x68, /* 0xd0 >> 1 */ +- +- .xtal_freq = 27000000, +- .inversion = IQ_SWAP_ON, /* 1 */ +- +- .lo_clk = 76500000, +- .hi_clk = 99000000, +- +- .esno_ave = STB0899_DVBS2_ESNO_AVE, +- .esno_quant = STB0899_DVBS2_ESNO_QUANT, +- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, +- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, +- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, +- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, +- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, +- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, +- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, +- +- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, +- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, +- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, +- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, +- +- .tuner_get_frequency = stb6100_get_frequency, +- .tuner_set_frequency = stb6100_set_frequency, +- .tuner_set_bandwidth = stb6100_set_bandwidth, +- .tuner_get_bandwidth = stb6100_get_bandwidth, +- .tuner_set_rfsiggain = NULL, +-}; +- +-struct stb6100_config vp1041_stb6100_config = { +- .tuner_address = 0x60, +- .refclock = 27000000, +-}; +- +-static int vp1041_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- int err = 0; +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- mantis_frontend_soft_reset(mantis); +- msleep(250); +- mantis->fe = dvb_attach(stb0899_attach, &vp1041_stb0899_config, adapter); +- if (mantis->fe) { +- dprintk(MANTIS_ERROR, 1, +- "found STB0899 DVB-S/DVB-S2 frontend @0x%02x", +- vp1041_stb0899_config.demod_address); +- +- if (dvb_attach(stb6100_attach, mantis->fe, &vp1041_stb6100_config, adapter)) { +- if (!dvb_attach(lnbp21_attach, mantis->fe, adapter, 0, 0)) +- dprintk(MANTIS_ERROR, 1, "No LNBP21 found!"); +- } +- } else { +- return -EREMOTEIO; +- } +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- +- +- dprintk(MANTIS_ERROR, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp1041_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_188, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp1041_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp1041.h b/drivers/media/dvb/mantis/mantis_vp1041.h +deleted file mode 100644 +index 1ae5b3d..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp1041.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* +- Mantis VP-1041 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP1041_H +-#define __MANTIS_VP1041_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_1041_DVB_S2 0x0031 +-#define SKYSTAR_HD2_10 0x0001 +-#define SKYSTAR_HD2_20 0x0003 +-#define CINERGY_S2_PCI_HD 0x1179 +- +-extern struct mantis_hwconfig vp1041_config; +- +-#endif /* __MANTIS_VP1041_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp2033.c b/drivers/media/dvb/mantis/mantis_vp2033.c +deleted file mode 100644 +index 1ca6837..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp2033.c ++++ /dev/null +@@ -1,188 +0,0 @@ +-/* +- Mantis VP-2033 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "tda1002x.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp2033.h" +- +-#define MANTIS_MODEL_NAME "VP-2033" +-#define MANTIS_DEV_TYPE "DVB-C" +- +-struct tda1002x_config vp2033_tda1002x_cu1216_config = { +- .demod_address = 0x18 >> 1, +- .invert = 1, +-}; +- +-struct tda10023_config vp2033_tda10023_cu1216_config = { +- .demod_address = 0x18 >> 1, +- .invert = 1, +-}; +- +-static u8 read_pwm(struct mantis_pci *mantis) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { +- {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, +- {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} +- }; +- +- if ((i2c_transfer(adapter, msg, 2) != 2) +- || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +-static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mantis_pci *mantis = fe->dvb->priv; +- struct i2c_adapter *adapter = &mantis->adapter; +- +- u8 buf[6]; +- struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; +- int i; +- +-#define CU1216_IF 36125000 +-#define TUNER_MUL 62500 +- +- u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0xce; +- buf[3] = (p->frequency < 150000000 ? 0x01 : +- p->frequency < 445000000 ? 0x02 : 0x04); +- buf[4] = 0xde; +- buf[5] = 0x20; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) != 1) +- return -EIO; +- +- /* wait for the pll lock */ +- msg.flags = I2C_M_RD; +- msg.len = 1; +- for (i = 0; i < 20; i++) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) +- break; +- +- msleep(10); +- } +- +- /* switch the charge pump to the lower current */ +- msg.flags = 0; +- msg.len = 2; +- msg.buf = &buf[2]; +- buf[2] &= ~0x40; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-static int vp2033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- int err = 0; +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- mantis_frontend_soft_reset(mantis); +- msleep(250); +- +- dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); +- fe = dvb_attach(tda10021_attach, &vp2033_tda1002x_cu1216_config, +- adapter, +- read_pwm(mantis)); +- +- if (fe) { +- dprintk(MANTIS_ERROR, 1, +- "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", +- vp2033_tda1002x_cu1216_config.demod_address); +- } else { +- fe = dvb_attach(tda10023_attach, &vp2033_tda10023_cu1216_config, +- adapter, +- read_pwm(mantis)); +- +- if (fe) { +- dprintk(MANTIS_ERROR, 1, +- "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", +- vp2033_tda1002x_cu1216_config.demod_address); +- } +- } +- +- if (fe) { +- fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; +- dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); +- } else { +- return -1; +- } +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- +- mantis->fe = fe; +- dprintk(MANTIS_DEBUG, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp2033_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_204, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp2033_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp2033.h b/drivers/media/dvb/mantis/mantis_vp2033.h +deleted file mode 100644 +index c55242b..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp2033.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Mantis VP-2033 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP2033_H +-#define __MANTIS_VP2033_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_2033_DVB_C 0x0008 +- +-extern struct mantis_hwconfig vp2033_config; +- +-#endif /* __MANTIS_VP2033_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp2040.c b/drivers/media/dvb/mantis/mantis_vp2040.c +deleted file mode 100644 +index d480741..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp2040.c ++++ /dev/null +@@ -1,187 +0,0 @@ +-/* +- Mantis VP-2040 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "tda1002x.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp2040.h" +- +-#define MANTIS_MODEL_NAME "VP-2040" +-#define MANTIS_DEV_TYPE "DVB-C" +- +-struct tda1002x_config vp2040_tda1002x_cu1216_config = { +- .demod_address = 0x18 >> 1, +- .invert = 1, +-}; +- +-struct tda10023_config vp2040_tda10023_cu1216_config = { +- .demod_address = 0x18 >> 1, +- .invert = 1, +-}; +- +-static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct mantis_pci *mantis = fe->dvb->priv; +- struct i2c_adapter *adapter = &mantis->adapter; +- +- u8 buf[6]; +- struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; +- int i; +- +-#define CU1216_IF 36125000 +-#define TUNER_MUL 62500 +- +- u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0xce; +- buf[3] = (p->frequency < 150000000 ? 0x01 : +- p->frequency < 445000000 ? 0x02 : 0x04); +- buf[4] = 0xde; +- buf[5] = 0x20; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) != 1) +- return -EIO; +- +- /* wait for the pll lock */ +- msg.flags = I2C_M_RD; +- msg.len = 1; +- for (i = 0; i < 20; i++) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) +- break; +- +- msleep(10); +- } +- +- /* switch the charge pump to the lower current */ +- msg.flags = 0; +- msg.len = 2; +- msg.buf = &buf[2]; +- buf[2] &= ~0x40; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- +- if (i2c_transfer(adapter, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-static u8 read_pwm(struct mantis_pci *mantis) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { +- {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, +- {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} +- }; +- +- if ((i2c_transfer(adapter, msg, 2) != 2) +- || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +-static int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- +- int err = 0; +- +- err = mantis_frontend_power(mantis, POWER_ON); +- if (err == 0) { +- mantis_frontend_soft_reset(mantis); +- msleep(250); +- +- dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); +- fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config, +- adapter, +- read_pwm(mantis)); +- +- if (fe) { +- dprintk(MANTIS_ERROR, 1, +- "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", +- vp2040_tda1002x_cu1216_config.demod_address); +- } else { +- fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config, +- adapter, +- read_pwm(mantis)); +- +- if (fe) { +- dprintk(MANTIS_ERROR, 1, +- "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", +- vp2040_tda1002x_cu1216_config.demod_address); +- } +- } +- +- if (fe) { +- fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; +- dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); +- } else { +- return -1; +- } +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- } +- mantis->fe = fe; +- dprintk(MANTIS_DEBUG, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp2040_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_204, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp2040_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp2040.h b/drivers/media/dvb/mantis/mantis_vp2040.h +deleted file mode 100644 +index d125e21..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp2040.h ++++ /dev/null +@@ -1,32 +0,0 @@ +-/* +- Mantis VP-2040 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP2040_H +-#define __MANTIS_VP2040_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_2040_DVB_C 0x0043 +-#define CINERGY_C 0x1178 +-#define CABLESTAR_HD2 0x0002 +- +-extern struct mantis_hwconfig vp2040_config; +- +-#endif /* __MANTIS_VP2040_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp3028.c b/drivers/media/dvb/mantis/mantis_vp3028.c +deleted file mode 100644 +index 4155c83..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp3028.c ++++ /dev/null +@@ -1,38 +0,0 @@ +-/* +- Mantis VP-3028 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- 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 "mantis_common.h" +-#include "mantis_vp3028.h" +- +-struct zl10353_config mantis_vp3028_config = { +- .demod_address = 0x0f, +-}; +- +-#define MANTIS_MODEL_NAME "VP-3028" +-#define MANTIS_DEV_TYPE "DVB-T" +- +-struct mantis_hwconfig vp3028_mantis_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_188, +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp3028.h b/drivers/media/dvb/mantis/mantis_vp3028.h +deleted file mode 100644 +index b07be6a..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp3028.h ++++ /dev/null +@@ -1,33 +0,0 @@ +-/* +- Mantis VP-3028 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP3028_H +-#define __MANTIS_VP3028_H +- +-#include "dvb_frontend.h" +-#include "mantis_common.h" +-#include "zl10353.h" +- +-#define MANTIS_VP_3028_DVB_T 0x0028 +- +-extern struct zl10353_config mantis_vp3028_config; +-extern struct mantis_hwconfig vp3028_mantis_config; +- +-#endif /* __MANTIS_VP3028_H */ +diff --git a/drivers/media/dvb/mantis/mantis_vp3030.c b/drivers/media/dvb/mantis/mantis_vp3030.c +deleted file mode 100644 +index c09308c..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp3030.c ++++ /dev/null +@@ -1,105 +0,0 @@ +-/* +- Mantis VP-3030 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +- +-#include "zl10353.h" +-#include "tda665x.h" +-#include "mantis_common.h" +-#include "mantis_ioc.h" +-#include "mantis_dvb.h" +-#include "mantis_vp3030.h" +- +-struct zl10353_config mantis_vp3030_config = { +- .demod_address = 0x0f, +-}; +- +-struct tda665x_config env57h12d5_config = { +- .name = "ENV57H12D5 (ET-50DT)", +- .addr = 0x60, +- .frequency_min = 47000000, +- .frequency_max = 862000000, +- .frequency_offst = 3616667, +- .ref_multiplier = 6, /* 1/6 MHz */ +- .ref_divider = 100000, /* 1/6 MHz */ +-}; +- +-#define MANTIS_MODEL_NAME "VP-3030" +-#define MANTIS_DEV_TYPE "DVB-T" +- +- +-static int vp3030_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) +-{ +- struct i2c_adapter *adapter = &mantis->adapter; +- struct mantis_hwconfig *config = mantis->hwconfig; +- int err = 0; +- +- mantis_gpio_set_bits(mantis, config->reset, 0); +- msleep(100); +- err = mantis_frontend_power(mantis, POWER_ON); +- msleep(100); +- mantis_gpio_set_bits(mantis, config->reset, 1); +- +- if (err == 0) { +- msleep(250); +- dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); +- fe = dvb_attach(zl10353_attach, &mantis_vp3030_config, adapter); +- +- if (!fe) +- return -1; +- +- dvb_attach(tda665x_attach, fe, &env57h12d5_config, adapter); +- } else { +- dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", +- adapter->name, +- err); +- +- return -EIO; +- +- } +- mantis->fe = fe; +- dprintk(MANTIS_ERROR, 1, "Done!"); +- +- return 0; +-} +- +-struct mantis_hwconfig vp3030_config = { +- .model_name = MANTIS_MODEL_NAME, +- .dev_type = MANTIS_DEV_TYPE, +- .ts_size = MANTIS_TS_188, +- +- .baud_rate = MANTIS_BAUD_9600, +- .parity = MANTIS_PARITY_NONE, +- .bytes = 0, +- +- .frontend_init = vp3030_frontend_init, +- .power = GPIF_A12, +- .reset = GPIF_A13, +- +- .i2c_mode = MANTIS_BYTE_MODE +-}; +diff --git a/drivers/media/dvb/mantis/mantis_vp3030.h b/drivers/media/dvb/mantis/mantis_vp3030.h +deleted file mode 100644 +index 5f12c42..0000000 +--- a/drivers/media/dvb/mantis/mantis_vp3030.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* +- Mantis VP-3030 driver +- +- Copyright (C) Manu Abraham (abraham.manu@gmail.com) +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +-*/ +- +-#ifndef __MANTIS_VP3030_H +-#define __MANTIS_VP3030_H +- +-#include "mantis_common.h" +- +-#define MANTIS_VP_3030_DVB_T 0x0024 +- +-extern struct mantis_hwconfig vp3030_config; +- +-#endif /* __MANTIS_VP3030_H */ +diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig +deleted file mode 100644 +index 64c8470..0000000 +--- a/drivers/media/dvb/ngene/Kconfig ++++ /dev/null +@@ -1,13 +0,0 @@ +-config DVB_NGENE +- tristate "Micronas nGene support" +- depends on DVB_CORE && PCI && I2C +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_STV6110x if !DVB_FE_CUSTOMISE +- select DVB_STV090x if !DVB_FE_CUSTOMISE +- select DVB_LGDT330X if !DVB_FE_CUSTOMISE +- select DVB_DRXK if !DVB_FE_CUSTOMISE +- select DVB_TDA18271C2DD if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE +- ---help--- +- Support for Micronas PCI express cards with nGene bridge. +- +diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile +deleted file mode 100644 +index 13ebeff..0000000 +--- a/drivers/media/dvb/ngene/Makefile ++++ /dev/null +@@ -1,14 +0,0 @@ +-# +-# Makefile for the nGene device driver +-# +- +-ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o +- +-obj-$(CONFIG_DVB_NGENE) += ngene.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ +-ccflags-y += -Idrivers/media/dvb/frontends/ +-ccflags-y += -Idrivers/media/common/tuners/ +- +-# For the staging CI driver cxd2099 +-ccflags-y += -Idrivers/staging/media/cxd2099/ +diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c +deleted file mode 100644 +index 8418c02..0000000 +--- a/drivers/media/dvb/ngene/ngene-cards.c ++++ /dev/null +@@ -1,558 +0,0 @@ +-/* +- * ngene-cards.c: nGene PCIe bridge driver - card specific info +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * Copyright (C) 2008-2009 Ralph Metzler +- * Modifications for new nGene firmware, +- * support for EEPROM-copying, +- * support for new dual DVB-S2 card prototype +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +- +-#include "ngene.h" +- +-/* demods/tuners */ +-#include "stv6110x.h" +-#include "stv090x.h" +-#include "lnbh24.h" +-#include "lgdt330x.h" +-#include "mt2131.h" +-#include "tda18271c2dd.h" +-#include "drxk.h" +- +- +-/****************************************************************************/ +-/* Demod/tuner attachment ***************************************************/ +-/****************************************************************************/ +- +-static int tuner_attach_stv6110(struct ngene_channel *chan) +-{ +- struct i2c_adapter *i2c; +- struct stv090x_config *feconf = (struct stv090x_config *) +- chan->dev->card_info->fe_config[chan->number]; +- struct stv6110x_config *tunerconf = (struct stv6110x_config *) +- chan->dev->card_info->tuner_config[chan->number]; +- struct stv6110x_devctl *ctl; +- +- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ +- if (chan->number < 2) +- i2c = &chan->dev->channel[0].i2c_adapter; +- else +- i2c = &chan->dev->channel[1].i2c_adapter; +- +- ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); +- if (ctl == NULL) { +- printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); +- return -ENODEV; +- } +- +- feconf->tuner_init = ctl->tuner_init; +- feconf->tuner_sleep = ctl->tuner_sleep; +- feconf->tuner_set_mode = ctl->tuner_set_mode; +- feconf->tuner_set_frequency = ctl->tuner_set_frequency; +- feconf->tuner_get_frequency = ctl->tuner_get_frequency; +- feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; +- feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; +- feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; +- feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; +- feconf->tuner_set_refclk = ctl->tuner_set_refclk; +- feconf->tuner_get_status = ctl->tuner_get_status; +- +- return 0; +-} +- +- +-static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) +-{ +- struct ngene_channel *chan = fe->sec_priv; +- int status; +- +- if (enable) { +- down(&chan->dev->pll_mutex); +- status = chan->gate_ctrl(fe, 1); +- } else { +- status = chan->gate_ctrl(fe, 0); +- up(&chan->dev->pll_mutex); +- } +- return status; +-} +- +-static int tuner_attach_tda18271(struct ngene_channel *chan) +-{ +- struct i2c_adapter *i2c; +- struct dvb_frontend *fe; +- +- i2c = &chan->dev->channel[0].i2c_adapter; +- if (chan->fe->ops.i2c_gate_ctrl) +- chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); +- fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60); +- if (chan->fe->ops.i2c_gate_ctrl) +- chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); +- if (!fe) { +- printk(KERN_ERR "No TDA18271 found!\n"); +- return -ENODEV; +- } +- +- return 0; +-} +- +-static int tuner_attach_probe(struct ngene_channel *chan) +-{ +- if (chan->demod_type == 0) +- return tuner_attach_stv6110(chan); +- if (chan->demod_type == 1) +- return tuner_attach_tda18271(chan); +- return -EINVAL; +-} +- +-static int demod_attach_stv0900(struct ngene_channel *chan) +-{ +- struct i2c_adapter *i2c; +- struct stv090x_config *feconf = (struct stv090x_config *) +- chan->dev->card_info->fe_config[chan->number]; +- +- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ +- /* Note: Both adapters share the same i2c bus, but the demod */ +- /* driver requires that each demod has its own i2c adapter */ +- if (chan->number < 2) +- i2c = &chan->dev->channel[0].i2c_adapter; +- else +- i2c = &chan->dev->channel[1].i2c_adapter; +- +- chan->fe = dvb_attach(stv090x_attach, feconf, i2c, +- (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 +- : STV090x_DEMODULATOR_1); +- if (chan->fe == NULL) { +- printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); +- return -ENODEV; +- } +- +- /* store channel info */ +- if (feconf->tuner_i2c_lock) +- chan->fe->analog_demod_priv = chan; +- +- if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, +- 0, chan->dev->card_info->lnb[chan->number])) { +- printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); +- dvb_frontend_detach(chan->fe); +- chan->fe = NULL; +- return -ENODEV; +- } +- +- return 0; +-} +- +-static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) +-{ +- struct ngene_channel *chan = fe->analog_demod_priv; +- +- if (lock) +- down(&chan->dev->pll_mutex); +- else +- up(&chan->dev->pll_mutex); +-} +- +-static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) +-{ +- struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1 } }; +- return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; +-} +- +-static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, +- u16 reg, u8 *val) +-{ +- u8 msg[2] = {reg>>8, reg&0xff}; +- struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, +- .buf = msg, .len = 2}, +- {.addr = adr, .flags = I2C_M_RD, +- .buf = val, .len = 1} }; +- return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; +-} +- +-static int port_has_stv0900(struct i2c_adapter *i2c, int port) +-{ +- u8 val; +- if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) +- return 0; +- return 1; +-} +- +-static int port_has_drxk(struct i2c_adapter *i2c, int port) +-{ +- u8 val; +- +- if (i2c_read(i2c, 0x29+port, &val) < 0) +- return 0; +- return 1; +-} +- +-static int demod_attach_drxk(struct ngene_channel *chan, +- struct i2c_adapter *i2c) +-{ +- struct drxk_config config; +- +- memset(&config, 0, sizeof(config)); +- config.adr = 0x29 + (chan->number ^ 2); +- +- chan->fe = dvb_attach(drxk_attach, &config, i2c); +- if (!chan->fe) { +- printk(KERN_ERR "No DRXK found!\n"); +- return -ENODEV; +- } +- chan->fe->sec_priv = chan; +- chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; +- chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; +- return 0; +-} +- +-static int cineS2_probe(struct ngene_channel *chan) +-{ +- struct i2c_adapter *i2c; +- struct stv090x_config *fe_conf; +- u8 buf[3]; +- struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; +- int rc; +- +- /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ +- if (chan->number < 2) +- i2c = &chan->dev->channel[0].i2c_adapter; +- else +- i2c = &chan->dev->channel[1].i2c_adapter; +- +- if (port_has_stv0900(i2c, chan->number)) { +- chan->demod_type = 0; +- fe_conf = chan->dev->card_info->fe_config[chan->number]; +- /* demod found, attach it */ +- rc = demod_attach_stv0900(chan); +- if (rc < 0 || chan->number < 2) +- return rc; +- +- /* demod #2: reprogram outputs DPN1 & DPN2 */ +- i2c_msg.addr = fe_conf->address; +- i2c_msg.len = 3; +- buf[0] = 0xf1; +- switch (chan->number) { +- case 2: +- buf[1] = 0x5c; +- buf[2] = 0xc2; +- break; +- case 3: +- buf[1] = 0x61; +- buf[2] = 0xcc; +- break; +- default: +- return -ENODEV; +- } +- rc = i2c_transfer(i2c, &i2c_msg, 1); +- if (rc != 1) { +- printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); +- return -EIO; +- } +- } else if (port_has_drxk(i2c, chan->number^2)) { +- chan->demod_type = 1; +- demod_attach_drxk(chan, i2c); +- } else { +- printk(KERN_ERR "No demod found on chan %d\n", chan->number); +- return -ENODEV; +- } +- return 0; +-} +- +- +-static struct lgdt330x_config aver_m780 = { +- .demod_address = 0xb2 >> 1, +- .demod_chip = LGDT3303, +- .serial_mpeg = 0x00, /* PARALLEL */ +- .clock_polarity_flip = 1, +-}; +- +-static struct mt2131_config m780_tunerconfig = { +- 0xc0 >> 1 +-}; +- +-/* A single func to attach the demo and tuner, rather than +- * use two sep funcs like the current design mandates. +- */ +-static int demod_attach_lg330x(struct ngene_channel *chan) +-{ +- chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); +- if (chan->fe == NULL) { +- printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); +- return -ENODEV; +- } +- +- dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter, +- &m780_tunerconfig, 0); +- +- return (chan->fe) ? 0 : -ENODEV; +-} +- +-/****************************************************************************/ +-/* Switch control (I2C gates, etc.) *****************************************/ +-/****************************************************************************/ +- +- +-static struct stv090x_config fe_cineS2 = { +- .device = STV0900, +- .demod_mode = STV090x_DUAL, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 27000000, +- .address = 0x68, +- +- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- +- .repeater_level = STV090x_RPTLEVEL_16, +- +- .adc1_range = STV090x_ADC_1Vpp, +- .adc2_range = STV090x_ADC_1Vpp, +- +- .diseqc_envelope_mode = true, +- +- .tuner_i2c_lock = cineS2_tuner_i2c_lock, +-}; +- +-static struct stv090x_config fe_cineS2_2 = { +- .device = STV0900, +- .demod_mode = STV090x_DUAL, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 27000000, +- .address = 0x69, +- +- .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, +- +- .repeater_level = STV090x_RPTLEVEL_16, +- +- .adc1_range = STV090x_ADC_1Vpp, +- .adc2_range = STV090x_ADC_1Vpp, +- +- .diseqc_envelope_mode = true, +- +- .tuner_i2c_lock = cineS2_tuner_i2c_lock, +-}; +- +-static struct stv6110x_config tuner_cineS2_0 = { +- .addr = 0x60, +- .refclk = 27000000, +- .clk_div = 1, +-}; +- +-static struct stv6110x_config tuner_cineS2_1 = { +- .addr = 0x63, +- .refclk = 27000000, +- .clk_div = 1, +-}; +- +-static struct ngene_info ngene_info_cineS2 = { +- .type = NGENE_SIDEWINDER, +- .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", +- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, +- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, +- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, +- .fe_config = {&fe_cineS2, &fe_cineS2}, +- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, +- .lnb = {0x0b, 0x08}, +- .tsf = {3, 3}, +- .fw_version = 18, +- .msi_supported = true, +-}; +- +-static struct ngene_info ngene_info_satixS2 = { +- .type = NGENE_SIDEWINDER, +- .name = "Mystique SaTiX-S2 Dual", +- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, +- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, +- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, +- .fe_config = {&fe_cineS2, &fe_cineS2}, +- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, +- .lnb = {0x0b, 0x08}, +- .tsf = {3, 3}, +- .fw_version = 18, +- .msi_supported = true, +-}; +- +-static struct ngene_info ngene_info_satixS2v2 = { +- .type = NGENE_SIDEWINDER, +- .name = "Mystique SaTiX-S2 Dual (v2)", +- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, +- NGENE_IO_TSOUT}, +- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, +- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, +- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, +- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, +- .lnb = {0x0a, 0x08, 0x0b, 0x09}, +- .tsf = {3, 3}, +- .fw_version = 18, +- .msi_supported = true, +-}; +- +-static struct ngene_info ngene_info_cineS2v5 = { +- .type = NGENE_SIDEWINDER, +- .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", +- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, +- NGENE_IO_TSOUT}, +- .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, +- .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, +- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, +- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, +- .lnb = {0x0a, 0x08, 0x0b, 0x09}, +- .tsf = {3, 3}, +- .fw_version = 18, +- .msi_supported = true, +-}; +- +- +-static struct ngene_info ngene_info_duoFlex = { +- .type = NGENE_SIDEWINDER, +- .name = "Digital Devices DuoFlex PCIe or miniPCIe", +- .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, +- NGENE_IO_TSOUT}, +- .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, +- .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe}, +- .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, +- .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, +- .lnb = {0x0a, 0x08, 0x0b, 0x09}, +- .tsf = {3, 3}, +- .fw_version = 18, +- .msi_supported = true, +-}; +- +-static struct ngene_info ngene_info_m780 = { +- .type = NGENE_APP, +- .name = "Aver M780 ATSC/QAM-B", +- +- /* Channel 0 is analog, which is currently unsupported */ +- .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN }, +- .demod_attach = { NULL, demod_attach_lg330x }, +- +- /* Ensure these are NULL else the frame will call them (as funcs) */ +- .tuner_attach = { 0, 0, 0, 0 }, +- .fe_config = { NULL, &aver_m780 }, +- .avf = { 0 }, +- +- /* A custom electrical interface config for the demod to bridge */ +- .tsf = { 4, 4 }, +- .fw_version = 15, +-}; +- +-/****************************************************************************/ +- +- +- +-/****************************************************************************/ +-/* PCI Subsystem ID *********************************************************/ +-/****************************************************************************/ +- +-#define NGENE_ID(_subvend, _subdev, _driverdata) { \ +- .vendor = NGENE_VID, .device = NGENE_PID, \ +- .subvendor = _subvend, .subdevice = _subdev, \ +- .driver_data = (unsigned long) &_driverdata } +- +-/****************************************************************************/ +- +-static const struct pci_device_id ngene_id_tbl[] __devinitdata = { +- NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), +- NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), +- NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), +- NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2), +- NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5), +- NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), +- NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), +- NGENE_ID(0x1461, 0x062e, ngene_info_m780), +- {0} +-}; +-MODULE_DEVICE_TABLE(pci, ngene_id_tbl); +- +-/****************************************************************************/ +-/* Init/Exit ****************************************************************/ +-/****************************************************************************/ +- +-static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, +- enum pci_channel_state state) +-{ +- printk(KERN_ERR DEVICE_NAME ": PCI error\n"); +- if (state == pci_channel_io_perm_failure) +- return PCI_ERS_RESULT_DISCONNECT; +- if (state == pci_channel_io_frozen) +- return PCI_ERS_RESULT_NEED_RESET; +- return PCI_ERS_RESULT_CAN_RECOVER; +-} +- +-static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) +-{ +- printk(KERN_INFO DEVICE_NAME ": link reset\n"); +- return 0; +-} +- +-static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) +-{ +- printk(KERN_INFO DEVICE_NAME ": slot reset\n"); +- return 0; +-} +- +-static void ngene_resume(struct pci_dev *dev) +-{ +- printk(KERN_INFO DEVICE_NAME ": resume\n"); +-} +- +-static struct pci_error_handlers ngene_errors = { +- .error_detected = ngene_error_detected, +- .link_reset = ngene_link_reset, +- .slot_reset = ngene_slot_reset, +- .resume = ngene_resume, +-}; +- +-static struct pci_driver ngene_pci_driver = { +- .name = "ngene", +- .id_table = ngene_id_tbl, +- .probe = ngene_probe, +- .remove = __devexit_p(ngene_remove), +- .err_handler = &ngene_errors, +- .shutdown = ngene_shutdown, +-}; +- +-static __init int module_init_ngene(void) +-{ +- printk(KERN_INFO +- "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); +- return pci_register_driver(&ngene_pci_driver); +-} +- +-static __exit void module_exit_ngene(void) +-{ +- pci_unregister_driver(&ngene_pci_driver); +-} +- +-module_init(module_init_ngene); +-module_exit(module_exit_ngene); +- +-MODULE_DESCRIPTION("nGene"); +-MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c +deleted file mode 100644 +index f129a93..0000000 +--- a/drivers/media/dvb/ngene/ngene-core.c ++++ /dev/null +@@ -1,1715 +0,0 @@ +-/* +- * ngene.c: nGene PCIe bridge driver +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * Copyright (C) 2008-2009 Ralph Metzler +- * Modifications for new nGene firmware, +- * support for EEPROM-copying, +- * support for new dual DVB-S2 card prototype +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "ngene.h" +- +-static int one_adapter; +-module_param(one_adapter, int, 0444); +-MODULE_PARM_DESC(one_adapter, "Use only one adapter."); +- +-static int shutdown_workaround; +-module_param(shutdown_workaround, int, 0644); +-MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets."); +- +-static int debug; +-module_param(debug, int, 0444); +-MODULE_PARM_DESC(debug, "Print debugging information."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define dprintk if (debug) printk +- +-#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +-#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) +-#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +-#define ngreadl(adr) readl(dev->iomem + (adr)) +-#define ngreadb(adr) readb(dev->iomem + (adr)) +-#define ngcpyto(adr, src, count) memcpy_toio((char *) \ +- (dev->iomem + (adr)), (src), (count)) +-#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ +- (dev->iomem + (adr)), (count)) +- +-/****************************************************************************/ +-/* nGene interrupt handler **************************************************/ +-/****************************************************************************/ +- +-static void event_tasklet(unsigned long data) +-{ +- struct ngene *dev = (struct ngene *)data; +- +- while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { +- struct EVENT_BUFFER Event = +- dev->EventQueue[dev->EventQueueReadIndex]; +- dev->EventQueueReadIndex = +- (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1); +- +- if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify)) +- dev->TxEventNotify(dev, Event.TimeStamp); +- if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify)) +- dev->RxEventNotify(dev, Event.TimeStamp, +- Event.RXCharacter); +- } +-} +- +-static void demux_tasklet(unsigned long data) +-{ +- struct ngene_channel *chan = (struct ngene_channel *)data; +- struct SBufferHeader *Cur = chan->nextBuffer; +- +- spin_lock_irq(&chan->state_lock); +- +- while (Cur->ngeneBuffer.SR.Flags & 0x80) { +- if (chan->mode & NGENE_IO_TSOUT) { +- u32 Flags = chan->DataFormatFlags; +- if (Cur->ngeneBuffer.SR.Flags & 0x20) +- Flags |= BEF_OVERFLOW; +- if (chan->pBufferExchange) { +- if (!chan->pBufferExchange(chan, +- Cur->Buffer1, +- chan->Capture1Length, +- Cur->ngeneBuffer.SR. +- Clock, Flags)) { +- /* +- We didn't get data +- Clear in service flag to make sure we +- get called on next interrupt again. +- leave fill/empty (0x80) flag alone +- to avoid hardware running out of +- buffers during startup, we hold only +- in run state ( the source may be late +- delivering data ) +- */ +- +- if (chan->HWState == HWSTATE_RUN) { +- Cur->ngeneBuffer.SR.Flags &= +- ~0x40; +- break; +- /* Stop processing stream */ +- } +- } else { +- /* We got a valid buffer, +- so switch to run state */ +- chan->HWState = HWSTATE_RUN; +- } +- } else { +- printk(KERN_ERR DEVICE_NAME ": OOPS\n"); +- if (chan->HWState == HWSTATE_RUN) { +- Cur->ngeneBuffer.SR.Flags &= ~0x40; +- break; /* Stop processing stream */ +- } +- } +- if (chan->AudioDTOUpdated) { +- printk(KERN_INFO DEVICE_NAME +- ": Update AudioDTO = %d\n", +- chan->AudioDTOValue); +- Cur->ngeneBuffer.SR.DTOUpdate = +- chan->AudioDTOValue; +- chan->AudioDTOUpdated = 0; +- } +- } else { +- if (chan->HWState == HWSTATE_RUN) { +- u32 Flags = chan->DataFormatFlags; +- IBufferExchange *exch1 = chan->pBufferExchange; +- IBufferExchange *exch2 = chan->pBufferExchange2; +- if (Cur->ngeneBuffer.SR.Flags & 0x01) +- Flags |= BEF_EVEN_FIELD; +- if (Cur->ngeneBuffer.SR.Flags & 0x20) +- Flags |= BEF_OVERFLOW; +- spin_unlock_irq(&chan->state_lock); +- if (exch1) +- exch1(chan, Cur->Buffer1, +- chan->Capture1Length, +- Cur->ngeneBuffer.SR.Clock, +- Flags); +- if (exch2) +- exch2(chan, Cur->Buffer2, +- chan->Capture2Length, +- Cur->ngeneBuffer.SR.Clock, +- Flags); +- spin_lock_irq(&chan->state_lock); +- } else if (chan->HWState != HWSTATE_STOP) +- chan->HWState = HWSTATE_RUN; +- } +- Cur->ngeneBuffer.SR.Flags = 0x00; +- Cur = Cur->Next; +- } +- chan->nextBuffer = Cur; +- +- spin_unlock_irq(&chan->state_lock); +-} +- +-static irqreturn_t irq_handler(int irq, void *dev_id) +-{ +- struct ngene *dev = (struct ngene *)dev_id; +- u32 icounts = 0; +- irqreturn_t rc = IRQ_NONE; +- u32 i = MAX_STREAM; +- u8 *tmpCmdDoneByte; +- +- if (dev->BootFirmware) { +- icounts = ngreadl(NGENE_INT_COUNTS); +- if (icounts != dev->icounts) { +- ngwritel(0, FORCE_NMI); +- dev->cmd_done = 1; +- wake_up(&dev->cmd_wq); +- dev->icounts = icounts; +- rc = IRQ_HANDLED; +- } +- return rc; +- } +- +- ngwritel(0, FORCE_NMI); +- +- spin_lock(&dev->cmd_lock); +- tmpCmdDoneByte = dev->CmdDoneByte; +- if (tmpCmdDoneByte && +- (*tmpCmdDoneByte || +- (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) { +- dev->CmdDoneByte = NULL; +- dev->cmd_done = 1; +- wake_up(&dev->cmd_wq); +- rc = IRQ_HANDLED; +- } +- spin_unlock(&dev->cmd_lock); +- +- if (dev->EventBuffer->EventStatus & 0x80) { +- u8 nextWriteIndex = +- (dev->EventQueueWriteIndex + 1) & +- (EVENT_QUEUE_SIZE - 1); +- if (nextWriteIndex != dev->EventQueueReadIndex) { +- dev->EventQueue[dev->EventQueueWriteIndex] = +- *(dev->EventBuffer); +- dev->EventQueueWriteIndex = nextWriteIndex; +- } else { +- printk(KERN_ERR DEVICE_NAME ": event overflow\n"); +- dev->EventQueueOverflowCount += 1; +- dev->EventQueueOverflowFlag = 1; +- } +- dev->EventBuffer->EventStatus &= ~0x80; +- tasklet_schedule(&dev->event_tasklet); +- rc = IRQ_HANDLED; +- } +- +- while (i > 0) { +- i--; +- spin_lock(&dev->channel[i].state_lock); +- /* if (dev->channel[i].State>=KSSTATE_RUN) { */ +- if (dev->channel[i].nextBuffer) { +- if ((dev->channel[i].nextBuffer-> +- ngeneBuffer.SR.Flags & 0xC0) == 0x80) { +- dev->channel[i].nextBuffer-> +- ngeneBuffer.SR.Flags |= 0x40; +- tasklet_schedule( +- &dev->channel[i].demux_tasklet); +- rc = IRQ_HANDLED; +- } +- } +- spin_unlock(&dev->channel[i].state_lock); +- } +- +- /* Request might have been processed by a previous call. */ +- return IRQ_HANDLED; +-} +- +-/****************************************************************************/ +-/* nGene command interface **************************************************/ +-/****************************************************************************/ +- +-static void dump_command_io(struct ngene *dev) +-{ +- u8 buf[8], *b; +- +- ngcpyfrom(buf, HOST_TO_NGENE, 8); +- printk(KERN_ERR "host_to_ngene (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n", +- HOST_TO_NGENE, buf[0], buf[1], buf[2], buf[3], +- buf[4], buf[5], buf[6], buf[7]); +- +- ngcpyfrom(buf, NGENE_TO_HOST, 8); +- printk(KERN_ERR "ngene_to_host (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n", +- NGENE_TO_HOST, buf[0], buf[1], buf[2], buf[3], +- buf[4], buf[5], buf[6], buf[7]); +- +- b = dev->hosttongene; +- printk(KERN_ERR "dev->hosttongene (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n", +- b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); +- +- b = dev->ngenetohost; +- printk(KERN_ERR "dev->ngenetohost (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n", +- b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); +-} +- +-static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) +-{ +- int ret; +- u8 *tmpCmdDoneByte; +- +- dev->cmd_done = 0; +- +- if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) { +- dev->BootFirmware = 1; +- dev->icounts = ngreadl(NGENE_INT_COUNTS); +- ngwritel(0, NGENE_COMMAND); +- ngwritel(0, NGENE_COMMAND_HI); +- ngwritel(0, NGENE_STATUS); +- ngwritel(0, NGENE_STATUS_HI); +- ngwritel(0, NGENE_EVENT); +- ngwritel(0, NGENE_EVENT_HI); +- } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) { +- u64 fwio = dev->PAFWInterfaceBuffer; +- +- ngwritel(fwio & 0xffffffff, NGENE_COMMAND); +- ngwritel(fwio >> 32, NGENE_COMMAND_HI); +- ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS); +- ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI); +- ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT); +- ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI); +- } +- +- memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2); +- +- if (dev->BootFirmware) +- ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2); +- +- spin_lock_irq(&dev->cmd_lock); +- tmpCmdDoneByte = dev->ngenetohost + com->out_len; +- if (!com->out_len) +- tmpCmdDoneByte++; +- *tmpCmdDoneByte = 0; +- dev->ngenetohost[0] = 0; +- dev->ngenetohost[1] = 0; +- dev->CmdDoneByte = tmpCmdDoneByte; +- spin_unlock_irq(&dev->cmd_lock); +- +- /* Notify 8051. */ +- ngwritel(1, FORCE_INT); +- +- ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ); +- if (!ret) { +- /*ngwritel(0, FORCE_NMI);*/ +- +- printk(KERN_ERR DEVICE_NAME +- ": Command timeout cmd=%02x prev=%02x\n", +- com->cmd.hdr.Opcode, dev->prev_cmd); +- dump_command_io(dev); +- return -1; +- } +- if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) +- dev->BootFirmware = 0; +- +- dev->prev_cmd = com->cmd.hdr.Opcode; +- +- if (!com->out_len) +- return 0; +- +- memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len); +- +- return 0; +-} +- +-int ngene_command(struct ngene *dev, struct ngene_command *com) +-{ +- int result; +- +- down(&dev->cmd_mutex); +- result = ngene_command_mutex(dev, com); +- up(&dev->cmd_mutex); +- return result; +-} +- +- +-static int ngene_command_load_firmware(struct ngene *dev, +- u8 *ngene_fw, u32 size) +-{ +-#define FIRSTCHUNK (1024) +- u32 cleft; +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE; +- com.cmd.hdr.Length = 0; +- com.in_len = 0; +- com.out_len = 0; +- +- ngene_command(dev, &com); +- +- cleft = (size + 3) & ~3; +- if (cleft > FIRSTCHUNK) { +- ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK, +- cleft - FIRSTCHUNK); +- cleft = FIRSTCHUNK; +- } +- ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); +- +- memset(&com, 0, sizeof(struct ngene_command)); +- com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH; +- com.cmd.hdr.Length = 4; +- com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA; +- com.cmd.FWLoadFinish.Length = (unsigned short)cleft; +- com.in_len = 4; +- com.out_len = 0; +- +- return ngene_command(dev, &com); +-} +- +- +-static int ngene_command_config_buf(struct ngene *dev, u8 config) +-{ +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER; +- com.cmd.hdr.Length = 1; +- com.cmd.ConfigureBuffers.config = config; +- com.in_len = 1; +- com.out_len = 0; +- +- if (ngene_command(dev, &com) < 0) +- return -EIO; +- return 0; +-} +- +-static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) +-{ +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER; +- com.cmd.hdr.Length = 6; +- memcpy(&com.cmd.ConfigureBuffers.config, config, 6); +- com.in_len = 6; +- com.out_len = 0; +- +- if (ngene_command(dev, &com) < 0) +- return -EIO; +- +- return 0; +-} +- +-int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) +-{ +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN; +- com.cmd.hdr.Length = 1; +- com.cmd.SetGpioPin.select = select | (level << 7); +- com.in_len = 1; +- com.out_len = 0; +- +- return ngene_command(dev, &com); +-} +- +- +-/* +- 02000640 is sample on rising edge. +- 02000740 is sample on falling edge. +- 02000040 is ignore "valid" signal +- +- 0: FD_CTL1 Bit 7,6 must be 0,1 +- 7 disable(fw controlled) +- 6 0-AUX,1-TS +- 5 0-par,1-ser +- 4 0-lsb/1-msb +- 3,2 reserved +- 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both +- 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge +- 2: FD_STA is read-only. 0-sync +- 3: FD_INSYNC is number of 47s to trigger "in sync". +- 4: FD_OUTSYNC is number of 47s to trigger "out of sync". +- 5: FD_MAXBYTE1 is low-order of bytes per packet. +- 6: FD_MAXBYTE2 is high-order of bytes per packet. +- 7: Top byte is unused. +-*/ +- +-/****************************************************************************/ +- +-static u8 TSFeatureDecoderSetup[8 * 5] = { +- 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, +- 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ +- 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ +- 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ +- 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */ +-}; +- +-/* Set NGENE I2S Config to 16 bit packed */ +-static u8 I2SConfiguration[] = { +- 0x00, 0x10, 0x00, 0x00, +- 0x80, 0x10, 0x00, 0x00, +-}; +- +-static u8 SPDIFConfiguration[10] = { +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +-}; +- +-/* Set NGENE I2S Config to transport stream compatible mode */ +- +-static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; +- +-static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 }; +- +-static u8 ITUDecoderSetup[4][16] = { +- {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ +- 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00}, +- {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, +- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, +- {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */ +- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, +- {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */ +- 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, +-}; +- +-/* +- * 50 48 60 gleich +- * 27p50 9f 00 22 80 42 69 18 ... +- * 27p60 93 00 22 80 82 69 1c ... +- */ +- +-/* Maxbyte to 1144 (for raw data) */ +-static u8 ITUFeatureDecoderSetup[8] = { +- 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 +-}; +- +-void FillTSBuffer(void *Buffer, int Length, u32 Flags) +-{ +- u32 *ptr = Buffer; +- +- memset(Buffer, TS_FILLER, Length); +- while (Length > 0) { +- if (Flags & DF_SWAP32) +- *ptr = 0x471FFF10; +- else +- *ptr = 0x10FF1F47; +- ptr += (188 / 4); +- Length -= 188; +- } +-} +- +- +-static void flush_buffers(struct ngene_channel *chan) +-{ +- u8 val; +- +- do { +- msleep(1); +- spin_lock_irq(&chan->state_lock); +- val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80; +- spin_unlock_irq(&chan->state_lock); +- } while (val); +-} +- +-static void clear_buffers(struct ngene_channel *chan) +-{ +- struct SBufferHeader *Cur = chan->nextBuffer; +- +- do { +- memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); +- if (chan->mode & NGENE_IO_TSOUT) +- FillTSBuffer(Cur->Buffer1, +- chan->Capture1Length, +- chan->DataFormatFlags); +- Cur = Cur->Next; +- } while (Cur != chan->nextBuffer); +- +- if (chan->mode & NGENE_IO_TSOUT) { +- chan->nextBuffer->ngeneBuffer.SR.DTOUpdate = +- chan->AudioDTOValue; +- chan->AudioDTOUpdated = 0; +- +- Cur = chan->TSIdleBuffer.Head; +- +- do { +- memset(&Cur->ngeneBuffer.SR, 0, +- sizeof(Cur->ngeneBuffer.SR)); +- FillTSBuffer(Cur->Buffer1, +- chan->Capture1Length, +- chan->DataFormatFlags); +- Cur = Cur->Next; +- } while (Cur != chan->TSIdleBuffer.Head); +- } +-} +- +-static int ngene_command_stream_control(struct ngene *dev, u8 stream, +- u8 control, u8 mode, u8 flags) +-{ +- struct ngene_channel *chan = &dev->channel[stream]; +- struct ngene_command com; +- u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); +- u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500); +- u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); +- u16 BsSDO = 0x9B00; +- +- down(&dev->stream_mutex); +- memset(&com, 0, sizeof(com)); +- com.cmd.hdr.Opcode = CMD_CONTROL; +- com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; +- com.cmd.StreamControl.Stream = stream | (control ? 8 : 0); +- if (chan->mode & NGENE_IO_TSOUT) +- com.cmd.StreamControl.Stream |= 0x07; +- com.cmd.StreamControl.Control = control | +- (flags & SFLAG_ORDER_LUMA_CHROMA); +- com.cmd.StreamControl.Mode = mode; +- com.in_len = sizeof(struct FW_STREAM_CONTROL); +- com.out_len = 0; +- +- dprintk(KERN_INFO DEVICE_NAME +- ": Stream=%02x, Control=%02x, Mode=%02x\n", +- com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, +- com.cmd.StreamControl.Mode); +- +- chan->Mode = mode; +- +- if (!(control & 0x80)) { +- spin_lock_irq(&chan->state_lock); +- if (chan->State == KSSTATE_RUN) { +- chan->State = KSSTATE_ACQUIRE; +- chan->HWState = HWSTATE_STOP; +- spin_unlock_irq(&chan->state_lock); +- if (ngene_command(dev, &com) < 0) { +- up(&dev->stream_mutex); +- return -1; +- } +- /* clear_buffers(chan); */ +- flush_buffers(chan); +- up(&dev->stream_mutex); +- return 0; +- } +- spin_unlock_irq(&chan->state_lock); +- up(&dev->stream_mutex); +- return 0; +- } +- +- if (mode & SMODE_AUDIO_CAPTURE) { +- com.cmd.StreamControl.CaptureBlockCount = +- chan->Capture1Length / AUDIO_BLOCK_SIZE; +- com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; +- } else if (mode & SMODE_TRANSPORT_STREAM) { +- com.cmd.StreamControl.CaptureBlockCount = +- chan->Capture1Length / TS_BLOCK_SIZE; +- com.cmd.StreamControl.MaxLinesPerField = +- chan->Capture1Length / TS_BLOCK_SIZE; +- com.cmd.StreamControl.Buffer_Address = +- chan->TSRingBuffer.PAHead; +- if (chan->mode & NGENE_IO_TSOUT) { +- com.cmd.StreamControl.BytesPerVBILine = +- chan->Capture1Length / TS_BLOCK_SIZE; +- com.cmd.StreamControl.Stream |= 0x07; +- } +- } else { +- com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine; +- com.cmd.StreamControl.MaxLinesPerField = chan->nLines; +- com.cmd.StreamControl.MinLinesPerField = 100; +- com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; +- +- if (mode & SMODE_VBI_CAPTURE) { +- com.cmd.StreamControl.MaxVBILinesPerField = +- chan->nVBILines; +- com.cmd.StreamControl.MinVBILinesPerField = 0; +- com.cmd.StreamControl.BytesPerVBILine = +- chan->nBytesPerVBILine; +- } +- if (flags & SFLAG_COLORBAR) +- com.cmd.StreamControl.Stream |= 0x04; +- } +- +- spin_lock_irq(&chan->state_lock); +- if (mode & SMODE_AUDIO_CAPTURE) { +- chan->nextBuffer = chan->RingBuffer.Head; +- if (mode & SMODE_AUDIO_SPDIF) { +- com.cmd.StreamControl.SetupDataLen = +- sizeof(SPDIFConfiguration); +- com.cmd.StreamControl.SetupDataAddr = BsSPI; +- memcpy(com.cmd.StreamControl.SetupData, +- SPDIFConfiguration, sizeof(SPDIFConfiguration)); +- } else { +- com.cmd.StreamControl.SetupDataLen = 4; +- com.cmd.StreamControl.SetupDataAddr = BsSDI; +- memcpy(com.cmd.StreamControl.SetupData, +- I2SConfiguration + +- 4 * dev->card_info->i2s[stream], 4); +- } +- } else if (mode & SMODE_TRANSPORT_STREAM) { +- chan->nextBuffer = chan->TSRingBuffer.Head; +- if (stream >= STREAM_AUDIOIN1) { +- if (chan->mode & NGENE_IO_TSOUT) { +- com.cmd.StreamControl.SetupDataLen = +- sizeof(TS_I2SOutConfiguration); +- com.cmd.StreamControl.SetupDataAddr = BsSDO; +- memcpy(com.cmd.StreamControl.SetupData, +- TS_I2SOutConfiguration, +- sizeof(TS_I2SOutConfiguration)); +- } else { +- com.cmd.StreamControl.SetupDataLen = +- sizeof(TS_I2SConfiguration); +- com.cmd.StreamControl.SetupDataAddr = BsSDI; +- memcpy(com.cmd.StreamControl.SetupData, +- TS_I2SConfiguration, +- sizeof(TS_I2SConfiguration)); +- } +- } else { +- com.cmd.StreamControl.SetupDataLen = 8; +- com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10; +- memcpy(com.cmd.StreamControl.SetupData, +- TSFeatureDecoderSetup + +- 8 * dev->card_info->tsf[stream], 8); +- } +- } else { +- chan->nextBuffer = chan->RingBuffer.Head; +- com.cmd.StreamControl.SetupDataLen = +- 16 + sizeof(ITUFeatureDecoderSetup); +- com.cmd.StreamControl.SetupDataAddr = BsUVI; +- memcpy(com.cmd.StreamControl.SetupData, +- ITUDecoderSetup[chan->itumode], 16); +- memcpy(com.cmd.StreamControl.SetupData + 16, +- ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup)); +- } +- clear_buffers(chan); +- chan->State = KSSTATE_RUN; +- if (mode & SMODE_TRANSPORT_STREAM) +- chan->HWState = HWSTATE_RUN; +- else +- chan->HWState = HWSTATE_STARTUP; +- spin_unlock_irq(&chan->state_lock); +- +- if (ngene_command(dev, &com) < 0) { +- up(&dev->stream_mutex); +- return -1; +- } +- up(&dev->stream_mutex); +- return 0; +-} +- +-void set_transfer(struct ngene_channel *chan, int state) +-{ +- u8 control = 0, mode = 0, flags = 0; +- struct ngene *dev = chan->dev; +- int ret; +- +- /* +- printk(KERN_INFO DEVICE_NAME ": st %d\n", state); +- msleep(100); +- */ +- +- if (state) { +- if (chan->running) { +- printk(KERN_INFO DEVICE_NAME ": already running\n"); +- return; +- } +- } else { +- if (!chan->running) { +- printk(KERN_INFO DEVICE_NAME ": already stopped\n"); +- return; +- } +- } +- +- if (dev->card_info->switch_ctrl) +- dev->card_info->switch_ctrl(chan, 1, state ^ 1); +- +- if (state) { +- spin_lock_irq(&chan->state_lock); +- +- /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", +- ngreadl(0x9310)); */ +- dvb_ringbuffer_flush(&dev->tsout_rbuf); +- control = 0x80; +- if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { +- chan->Capture1Length = 512 * 188; +- mode = SMODE_TRANSPORT_STREAM; +- } +- if (chan->mode & NGENE_IO_TSOUT) { +- chan->pBufferExchange = tsout_exchange; +- /* 0x66666666 = 50MHz *2^33 /250MHz */ +- chan->AudioDTOValue = 0x80000000; +- chan->AudioDTOUpdated = 1; +- } +- if (chan->mode & NGENE_IO_TSIN) +- chan->pBufferExchange = tsin_exchange; +- spin_unlock_irq(&chan->state_lock); +- } else +- ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", +- ngreadl(0x9310)); */ +- +- ret = ngene_command_stream_control(dev, chan->number, +- control, mode, flags); +- if (!ret) +- chan->running = state; +- else +- printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", +- state); +- if (!state) { +- spin_lock_irq(&chan->state_lock); +- chan->pBufferExchange = NULL; +- dvb_ringbuffer_flush(&dev->tsout_rbuf); +- spin_unlock_irq(&chan->state_lock); +- } +-} +- +- +-/****************************************************************************/ +-/* nGene hardware init and release functions ********************************/ +-/****************************************************************************/ +- +-static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) +-{ +- struct SBufferHeader *Cur = rb->Head; +- u32 j; +- +- if (!Cur) +- return; +- +- for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) { +- if (Cur->Buffer1) +- pci_free_consistent(dev->pci_dev, +- rb->Buffer1Length, +- Cur->Buffer1, +- Cur->scList1->Address); +- +- if (Cur->Buffer2) +- pci_free_consistent(dev->pci_dev, +- rb->Buffer2Length, +- Cur->Buffer2, +- Cur->scList2->Address); +- } +- +- if (rb->SCListMem) +- pci_free_consistent(dev->pci_dev, rb->SCListMemSize, +- rb->SCListMem, rb->PASCListMem); +- +- pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); +-} +- +-static void free_idlebuffer(struct ngene *dev, +- struct SRingBufferDescriptor *rb, +- struct SRingBufferDescriptor *tb) +-{ +- int j; +- struct SBufferHeader *Cur = tb->Head; +- +- if (!rb->Head) +- return; +- free_ringbuffer(dev, rb); +- for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { +- Cur->Buffer2 = NULL; +- Cur->scList2 = NULL; +- Cur->ngeneBuffer.Address_of_first_entry_2 = 0; +- Cur->ngeneBuffer.Number_of_entries_2 = 0; +- } +-} +- +-static void free_common_buffers(struct ngene *dev) +-{ +- u32 i; +- struct ngene_channel *chan; +- +- for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { +- chan = &dev->channel[i]; +- free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer); +- free_ringbuffer(dev, &chan->RingBuffer); +- free_ringbuffer(dev, &chan->TSRingBuffer); +- } +- +- if (dev->OverflowBuffer) +- pci_free_consistent(dev->pci_dev, +- OVERFLOW_BUFFER_SIZE, +- dev->OverflowBuffer, dev->PAOverflowBuffer); +- +- if (dev->FWInterfaceBuffer) +- pci_free_consistent(dev->pci_dev, +- 4096, +- dev->FWInterfaceBuffer, +- dev->PAFWInterfaceBuffer); +-} +- +-/****************************************************************************/ +-/* Ring buffer handling *****************************************************/ +-/****************************************************************************/ +- +-static int create_ring_buffer(struct pci_dev *pci_dev, +- struct SRingBufferDescriptor *descr, u32 NumBuffers) +-{ +- dma_addr_t tmp; +- struct SBufferHeader *Head; +- u32 i; +- u32 MemSize = SIZEOF_SBufferHeader * NumBuffers; +- u64 PARingBufferHead; +- u64 PARingBufferCur; +- u64 PARingBufferNext; +- struct SBufferHeader *Cur, *Next; +- +- descr->Head = NULL; +- descr->MemSize = 0; +- descr->PAHead = 0; +- descr->NumBuffers = 0; +- +- if (MemSize < 4096) +- MemSize = 4096; +- +- Head = pci_alloc_consistent(pci_dev, MemSize, &tmp); +- PARingBufferHead = tmp; +- +- if (!Head) +- return -ENOMEM; +- +- memset(Head, 0, MemSize); +- +- PARingBufferCur = PARingBufferHead; +- Cur = Head; +- +- for (i = 0; i < NumBuffers - 1; i++) { +- Next = (struct SBufferHeader *) +- (((u8 *) Cur) + SIZEOF_SBufferHeader); +- PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader; +- Cur->Next = Next; +- Cur->ngeneBuffer.Next = PARingBufferNext; +- Cur = Next; +- PARingBufferCur = PARingBufferNext; +- } +- /* Last Buffer points back to first one */ +- Cur->Next = Head; +- Cur->ngeneBuffer.Next = PARingBufferHead; +- +- descr->Head = Head; +- descr->MemSize = MemSize; +- descr->PAHead = PARingBufferHead; +- descr->NumBuffers = NumBuffers; +- +- return 0; +-} +- +-static int AllocateRingBuffers(struct pci_dev *pci_dev, +- dma_addr_t of, +- struct SRingBufferDescriptor *pRingBuffer, +- u32 Buffer1Length, u32 Buffer2Length) +-{ +- dma_addr_t tmp; +- u32 i, j; +- int status = 0; +- u32 SCListMemSize = pRingBuffer->NumBuffers +- * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : +- NUM_SCATTER_GATHER_ENTRIES) +- * sizeof(struct HW_SCATTER_GATHER_ELEMENT); +- +- u64 PASCListMem; +- struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; +- u64 PASCListEntry; +- struct SBufferHeader *Cur; +- void *SCListMem; +- +- if (SCListMemSize < 4096) +- SCListMemSize = 4096; +- +- SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp); +- +- PASCListMem = tmp; +- if (SCListMem == NULL) +- return -ENOMEM; +- +- memset(SCListMem, 0, SCListMemSize); +- +- pRingBuffer->SCListMem = SCListMem; +- pRingBuffer->PASCListMem = PASCListMem; +- pRingBuffer->SCListMemSize = SCListMemSize; +- pRingBuffer->Buffer1Length = Buffer1Length; +- pRingBuffer->Buffer2Length = Buffer2Length; +- +- SCListEntry = SCListMem; +- PASCListEntry = PASCListMem; +- Cur = pRingBuffer->Head; +- +- for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) { +- u64 PABuffer; +- +- void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length, +- &tmp); +- PABuffer = tmp; +- +- if (Buffer == NULL) +- return -ENOMEM; +- +- Cur->Buffer1 = Buffer; +- +- SCListEntry->Address = PABuffer; +- SCListEntry->Length = Buffer1Length; +- +- Cur->scList1 = SCListEntry; +- Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry; +- Cur->ngeneBuffer.Number_of_entries_1 = +- NUM_SCATTER_GATHER_ENTRIES; +- +- SCListEntry += 1; +- PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); +- +-#if NUM_SCATTER_GATHER_ENTRIES > 1 +- for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) { +- SCListEntry->Address = of; +- SCListEntry->Length = OVERFLOW_BUFFER_SIZE; +- SCListEntry += 1; +- PASCListEntry += +- sizeof(struct HW_SCATTER_GATHER_ELEMENT); +- } +-#endif +- +- if (!Buffer2Length) +- continue; +- +- Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp); +- PABuffer = tmp; +- +- if (Buffer == NULL) +- return -ENOMEM; +- +- Cur->Buffer2 = Buffer; +- +- SCListEntry->Address = PABuffer; +- SCListEntry->Length = Buffer2Length; +- +- Cur->scList2 = SCListEntry; +- Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry; +- Cur->ngeneBuffer.Number_of_entries_2 = +- NUM_SCATTER_GATHER_ENTRIES; +- +- SCListEntry += 1; +- PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); +- +-#if NUM_SCATTER_GATHER_ENTRIES > 1 +- for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) { +- SCListEntry->Address = of; +- SCListEntry->Length = OVERFLOW_BUFFER_SIZE; +- SCListEntry += 1; +- PASCListEntry += +- sizeof(struct HW_SCATTER_GATHER_ELEMENT); +- } +-#endif +- +- } +- +- return status; +-} +- +-static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, +- struct SRingBufferDescriptor *pRingBuffer) +-{ +- int status = 0; +- +- /* Copy pointer to scatter gather list in TSRingbuffer +- structure for buffer 2 +- Load number of buffer +- */ +- u32 n = pRingBuffer->NumBuffers; +- +- /* Point to first buffer entry */ +- struct SBufferHeader *Cur = pRingBuffer->Head; +- int i; +- /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */ +- for (i = 0; i < n; i++) { +- Cur->Buffer2 = pIdleBuffer->Head->Buffer1; +- Cur->scList2 = pIdleBuffer->Head->scList1; +- Cur->ngeneBuffer.Address_of_first_entry_2 = +- pIdleBuffer->Head->ngeneBuffer. +- Address_of_first_entry_1; +- Cur->ngeneBuffer.Number_of_entries_2 = +- pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; +- Cur = Cur->Next; +- } +- return status; +-} +- +-static u32 RingBufferSizes[MAX_STREAM] = { +- RING_SIZE_VIDEO, +- RING_SIZE_VIDEO, +- RING_SIZE_AUDIO, +- RING_SIZE_AUDIO, +- RING_SIZE_AUDIO, +-}; +- +-static u32 Buffer1Sizes[MAX_STREAM] = { +- MAX_VIDEO_BUFFER_SIZE, +- MAX_VIDEO_BUFFER_SIZE, +- MAX_AUDIO_BUFFER_SIZE, +- MAX_AUDIO_BUFFER_SIZE, +- MAX_AUDIO_BUFFER_SIZE +-}; +- +-static u32 Buffer2Sizes[MAX_STREAM] = { +- MAX_VBI_BUFFER_SIZE, +- MAX_VBI_BUFFER_SIZE, +- 0, +- 0, +- 0 +-}; +- +- +-static int AllocCommonBuffers(struct ngene *dev) +-{ +- int status = 0, i; +- +- dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096, +- &dev->PAFWInterfaceBuffer); +- if (!dev->FWInterfaceBuffer) +- return -ENOMEM; +- dev->hosttongene = dev->FWInterfaceBuffer; +- dev->ngenetohost = dev->FWInterfaceBuffer + 256; +- dev->EventBuffer = dev->FWInterfaceBuffer + 512; +- +- dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev, +- OVERFLOW_BUFFER_SIZE, +- &dev->PAOverflowBuffer); +- if (!dev->OverflowBuffer) +- return -ENOMEM; +- memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE); +- +- for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { +- int type = dev->card_info->io_type[i]; +- +- dev->channel[i].State = KSSTATE_STOP; +- +- if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { +- status = create_ring_buffer(dev->pci_dev, +- &dev->channel[i].RingBuffer, +- RingBufferSizes[i]); +- if (status < 0) +- break; +- +- if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { +- status = AllocateRingBuffers(dev->pci_dev, +- dev-> +- PAOverflowBuffer, +- &dev->channel[i]. +- RingBuffer, +- Buffer1Sizes[i], +- Buffer2Sizes[i]); +- if (status < 0) +- break; +- } else if (type & NGENE_IO_HDTV) { +- status = AllocateRingBuffers(dev->pci_dev, +- dev-> +- PAOverflowBuffer, +- &dev->channel[i]. +- RingBuffer, +- MAX_HDTV_BUFFER_SIZE, +- 0); +- if (status < 0) +- break; +- } +- } +- +- if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { +- +- status = create_ring_buffer(dev->pci_dev, +- &dev->channel[i]. +- TSRingBuffer, RING_SIZE_TS); +- if (status < 0) +- break; +- +- status = AllocateRingBuffers(dev->pci_dev, +- dev->PAOverflowBuffer, +- &dev->channel[i]. +- TSRingBuffer, +- MAX_TS_BUFFER_SIZE, 0); +- if (status) +- break; +- } +- +- if (type & NGENE_IO_TSOUT) { +- status = create_ring_buffer(dev->pci_dev, +- &dev->channel[i]. +- TSIdleBuffer, 1); +- if (status < 0) +- break; +- status = AllocateRingBuffers(dev->pci_dev, +- dev->PAOverflowBuffer, +- &dev->channel[i]. +- TSIdleBuffer, +- MAX_TS_BUFFER_SIZE, 0); +- if (status) +- break; +- FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer, +- &dev->channel[i].TSRingBuffer); +- } +- } +- return status; +-} +- +-static void ngene_release_buffers(struct ngene *dev) +-{ +- if (dev->iomem) +- iounmap(dev->iomem); +- free_common_buffers(dev); +- vfree(dev->tsout_buf); +- vfree(dev->tsin_buf); +- vfree(dev->ain_buf); +- vfree(dev->vin_buf); +- vfree(dev); +-} +- +-static int ngene_get_buffers(struct ngene *dev) +-{ +- if (AllocCommonBuffers(dev)) +- return -ENOMEM; +- if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) { +- dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE); +- if (!dev->tsout_buf) +- return -ENOMEM; +- dvb_ringbuffer_init(&dev->tsout_rbuf, +- dev->tsout_buf, TSOUT_BUF_SIZE); +- } +- if (dev->card_info->io_type[2]&NGENE_IO_TSIN) { +- dev->tsin_buf = vmalloc(TSIN_BUF_SIZE); +- if (!dev->tsin_buf) +- return -ENOMEM; +- dvb_ringbuffer_init(&dev->tsin_rbuf, +- dev->tsin_buf, TSIN_BUF_SIZE); +- } +- if (dev->card_info->io_type[2] & NGENE_IO_AIN) { +- dev->ain_buf = vmalloc(AIN_BUF_SIZE); +- if (!dev->ain_buf) +- return -ENOMEM; +- dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE); +- } +- if (dev->card_info->io_type[0] & NGENE_IO_HDTV) { +- dev->vin_buf = vmalloc(VIN_BUF_SIZE); +- if (!dev->vin_buf) +- return -ENOMEM; +- dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE); +- } +- dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0), +- pci_resource_len(dev->pci_dev, 0)); +- if (!dev->iomem) +- return -ENOMEM; +- +- return 0; +-} +- +-static void ngene_init(struct ngene *dev) +-{ +- int i; +- +- tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); +- +- memset_io(dev->iomem + 0xc000, 0x00, 0x220); +- memset_io(dev->iomem + 0xc400, 0x00, 0x100); +- +- for (i = 0; i < MAX_STREAM; i++) { +- dev->channel[i].dev = dev; +- dev->channel[i].number = i; +- } +- +- dev->fw_interface_version = 0; +- +- ngwritel(0, NGENE_INT_ENABLE); +- +- dev->icounts = ngreadl(NGENE_INT_COUNTS); +- +- dev->device_version = ngreadl(DEV_VER) & 0x0f; +- printk(KERN_INFO DEVICE_NAME ": Device version %d\n", +- dev->device_version); +-} +- +-static int ngene_load_firm(struct ngene *dev) +-{ +- u32 size; +- const struct firmware *fw = NULL; +- u8 *ngene_fw; +- char *fw_name; +- int err, version; +- +- version = dev->card_info->fw_version; +- +- switch (version) { +- default: +- case 15: +- version = 15; +- size = 23466; +- fw_name = "ngene_15.fw"; +- dev->cmd_timeout_workaround = true; +- break; +- case 16: +- size = 23498; +- fw_name = "ngene_16.fw"; +- dev->cmd_timeout_workaround = true; +- break; +- case 17: +- size = 24446; +- fw_name = "ngene_17.fw"; +- dev->cmd_timeout_workaround = true; +- break; +- case 18: +- size = 0; +- fw_name = "ngene_18.fw"; +- break; +- } +- +- if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { +- printk(KERN_ERR DEVICE_NAME +- ": Could not load firmware file %s.\n", fw_name); +- printk(KERN_INFO DEVICE_NAME +- ": Copy %s to your hotplug directory!\n", fw_name); +- return -1; +- } +- if (size == 0) +- size = fw->size; +- if (size != fw->size) { +- printk(KERN_ERR DEVICE_NAME +- ": Firmware %s has invalid size!", fw_name); +- err = -1; +- } else { +- printk(KERN_INFO DEVICE_NAME +- ": Loading firmware file %s.\n", fw_name); +- ngene_fw = (u8 *) fw->data; +- err = ngene_command_load_firmware(dev, ngene_fw, size); +- } +- +- release_firmware(fw); +- +- return err; +-} +- +-static void ngene_stop(struct ngene *dev) +-{ +- down(&dev->cmd_mutex); +- i2c_del_adapter(&(dev->channel[0].i2c_adapter)); +- i2c_del_adapter(&(dev->channel[1].i2c_adapter)); +- ngwritel(0, NGENE_INT_ENABLE); +- ngwritel(0, NGENE_COMMAND); +- ngwritel(0, NGENE_COMMAND_HI); +- ngwritel(0, NGENE_STATUS); +- ngwritel(0, NGENE_STATUS_HI); +- ngwritel(0, NGENE_EVENT); +- ngwritel(0, NGENE_EVENT_HI); +- free_irq(dev->pci_dev->irq, dev); +-#ifdef CONFIG_PCI_MSI +- if (dev->msi_enabled) +- pci_disable_msi(dev->pci_dev); +-#endif +-} +- +-static int ngene_buffer_config(struct ngene *dev) +-{ +- int stat; +- +- if (dev->card_info->fw_version >= 17) { +- u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }; +- u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 }; +- u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 }; +- u8 *bconf = tsin12_config; +- +- if (dev->card_info->io_type[2]&NGENE_IO_TSIN && +- dev->card_info->io_type[3]&NGENE_IO_TSIN) { +- bconf = tsin1234_config; +- if (dev->card_info->io_type[4]&NGENE_IO_TSOUT && +- dev->ci.en) +- bconf = tsio1235_config; +- } +- stat = ngene_command_config_free_buf(dev, bconf); +- } else { +- int bconf = BUFFER_CONFIG_4422; +- +- if (dev->card_info->io_type[3] == NGENE_IO_TSIN) +- bconf = BUFFER_CONFIG_3333; +- stat = ngene_command_config_buf(dev, bconf); +- } +- return stat; +-} +- +- +-static int ngene_start(struct ngene *dev) +-{ +- int stat; +- int i; +- +- pci_set_master(dev->pci_dev); +- ngene_init(dev); +- +- stat = request_irq(dev->pci_dev->irq, irq_handler, +- IRQF_SHARED, "nGene", +- (void *)dev); +- if (stat < 0) +- return stat; +- +- init_waitqueue_head(&dev->cmd_wq); +- init_waitqueue_head(&dev->tx_wq); +- init_waitqueue_head(&dev->rx_wq); +- sema_init(&dev->cmd_mutex, 1); +- sema_init(&dev->stream_mutex, 1); +- sema_init(&dev->pll_mutex, 1); +- sema_init(&dev->i2c_switch_mutex, 1); +- spin_lock_init(&dev->cmd_lock); +- for (i = 0; i < MAX_STREAM; i++) +- spin_lock_init(&dev->channel[i].state_lock); +- ngwritel(1, TIMESTAMPS); +- +- ngwritel(1, NGENE_INT_ENABLE); +- +- stat = ngene_load_firm(dev); +- if (stat < 0) +- goto fail; +- +-#ifdef CONFIG_PCI_MSI +- /* enable MSI if kernel and card support it */ +- if (pci_msi_enabled() && dev->card_info->msi_supported) { +- unsigned long flags; +- +- ngwritel(0, NGENE_INT_ENABLE); +- free_irq(dev->pci_dev->irq, dev); +- stat = pci_enable_msi(dev->pci_dev); +- if (stat) { +- printk(KERN_INFO DEVICE_NAME +- ": MSI not available\n"); +- flags = IRQF_SHARED; +- } else { +- flags = 0; +- dev->msi_enabled = true; +- } +- stat = request_irq(dev->pci_dev->irq, irq_handler, +- flags, "nGene", dev); +- if (stat < 0) +- goto fail2; +- ngwritel(1, NGENE_INT_ENABLE); +- } +-#endif +- +- stat = ngene_i2c_init(dev, 0); +- if (stat < 0) +- goto fail; +- +- stat = ngene_i2c_init(dev, 1); +- if (stat < 0) +- goto fail; +- +- if (!stat) +- return stat; +- +- /* otherwise error: fall through */ +-fail: +- ngwritel(0, NGENE_INT_ENABLE); +- free_irq(dev->pci_dev->irq, dev); +-#ifdef CONFIG_PCI_MSI +-fail2: +- if (dev->msi_enabled) +- pci_disable_msi(dev->pci_dev); +-#endif +- return stat; +-} +- +-/****************************************************************************/ +-/****************************************************************************/ +-/****************************************************************************/ +- +-static void release_channel(struct ngene_channel *chan) +-{ +- struct dvb_demux *dvbdemux = &chan->demux; +- struct ngene *dev = chan->dev; +- +- if (chan->running) +- set_transfer(chan, 0); +- +- tasklet_kill(&chan->demux_tasklet); +- +- if (chan->ci_dev) { +- dvb_unregister_device(chan->ci_dev); +- chan->ci_dev = NULL; +- } +- +- if (chan->fe2) +- dvb_unregister_frontend(chan->fe2); +- +- if (chan->fe) { +- dvb_unregister_frontend(chan->fe); +- dvb_frontend_detach(chan->fe); +- chan->fe = NULL; +- } +- +- if (chan->has_demux) { +- dvb_net_release(&chan->dvbnet); +- dvbdemux->dmx.close(&dvbdemux->dmx); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, +- &chan->hw_frontend); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, +- &chan->mem_frontend); +- dvb_dmxdev_release(&chan->dmxdev); +- dvb_dmx_release(&chan->demux); +- chan->has_demux = false; +- } +- +- if (chan->has_adapter) { +- dvb_unregister_adapter(&dev->adapter[chan->number]); +- chan->has_adapter = false; +- } +-} +- +-static int init_channel(struct ngene_channel *chan) +-{ +- int ret = 0, nr = chan->number; +- struct dvb_adapter *adapter = NULL; +- struct dvb_demux *dvbdemux = &chan->demux; +- struct ngene *dev = chan->dev; +- struct ngene_info *ni = dev->card_info; +- int io = ni->io_type[nr]; +- +- tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); +- chan->users = 0; +- chan->type = io; +- chan->mode = chan->type; /* for now only one mode */ +- +- if (io & NGENE_IO_TSIN) { +- chan->fe = NULL; +- if (ni->demod_attach[nr]) { +- ret = ni->demod_attach[nr](chan); +- if (ret < 0) +- goto err; +- } +- if (chan->fe && ni->tuner_attach[nr]) { +- ret = ni->tuner_attach[nr](chan); +- if (ret < 0) +- goto err; +- } +- } +- +- if (!dev->ci.en && (io & NGENE_IO_TSOUT)) +- return 0; +- +- if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { +- if (nr >= STREAM_AUDIOIN1) +- chan->DataFormatFlags = DF_SWAP32; +- +- if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { +- adapter = &dev->adapter[nr]; +- ret = dvb_register_adapter(adapter, "nGene", +- THIS_MODULE, +- &chan->dev->pci_dev->dev, +- adapter_nr); +- if (ret < 0) +- goto err; +- if (dev->first_adapter == NULL) +- dev->first_adapter = adapter; +- chan->has_adapter = true; +- } else +- adapter = dev->first_adapter; +- } +- +- if (dev->ci.en && (io & NGENE_IO_TSOUT)) { +- dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); +- set_transfer(chan, 1); +- chan->dev->channel[2].DataFormatFlags = DF_SWAP32; +- set_transfer(&chan->dev->channel[2], 1); +- dvb_register_device(adapter, &chan->ci_dev, +- &ngene_dvbdev_ci, (void *) chan, +- DVB_DEVICE_SEC); +- if (!chan->ci_dev) +- goto err; +- } +- +- if (chan->fe) { +- if (dvb_register_frontend(adapter, chan->fe) < 0) +- goto err; +- chan->has_demux = true; +- } +- if (chan->fe2) { +- if (dvb_register_frontend(adapter, chan->fe2) < 0) +- goto err; +- chan->fe2->tuner_priv = chan->fe->tuner_priv; +- memcpy(&chan->fe2->ops.tuner_ops, +- &chan->fe->ops.tuner_ops, +- sizeof(struct dvb_tuner_ops)); +- } +- +- if (chan->has_demux) { +- ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", +- ngene_start_feed, +- ngene_stop_feed, chan); +- ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, +- &chan->hw_frontend, +- &chan->mem_frontend, adapter); +- ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); +- } +- +- return ret; +- +-err: +- if (chan->fe) { +- dvb_frontend_detach(chan->fe); +- chan->fe = NULL; +- } +- release_channel(chan); +- return 0; +-} +- +-static int init_channels(struct ngene *dev) +-{ +- int i, j; +- +- for (i = 0; i < MAX_STREAM; i++) { +- dev->channel[i].number = i; +- if (init_channel(&dev->channel[i]) < 0) { +- for (j = i - 1; j >= 0; j--) +- release_channel(&dev->channel[j]); +- return -1; +- } +- } +- return 0; +-} +- +-static struct cxd2099_cfg cxd_cfg = { +- .bitrate = 62000, +- .adr = 0x40, +- .polarity = 0, +- .clock_mode = 0, +-}; +- +-static void cxd_attach(struct ngene *dev) +-{ +- struct ngene_ci *ci = &dev->ci; +- +- ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter); +- ci->dev = dev; +- return; +-} +- +-static void cxd_detach(struct ngene *dev) +-{ +- struct ngene_ci *ci = &dev->ci; +- +- dvb_ca_en50221_release(ci->en); +- kfree(ci->en); +- ci->en = 0; +-} +- +-/***********************************/ +-/* workaround for shutdown failure */ +-/***********************************/ +- +-static void ngene_unlink(struct ngene *dev) +-{ +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_MEM_WRITE; +- com.cmd.hdr.Length = 3; +- com.cmd.MemoryWrite.address = 0x910c; +- com.cmd.MemoryWrite.data = 0xff; +- com.in_len = 3; +- com.out_len = 1; +- +- down(&dev->cmd_mutex); +- ngwritel(0, NGENE_INT_ENABLE); +- ngene_command_mutex(dev, &com); +- up(&dev->cmd_mutex); +-} +- +-void ngene_shutdown(struct pci_dev *pdev) +-{ +- struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); +- +- if (!dev || !shutdown_workaround) +- return; +- +- printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); +- ngene_unlink(dev); +- pci_disable_device(pdev); +-} +- +-/****************************************************************************/ +-/* device probe/remove calls ************************************************/ +-/****************************************************************************/ +- +-void __devexit ngene_remove(struct pci_dev *pdev) +-{ +- struct ngene *dev = pci_get_drvdata(pdev); +- int i; +- +- tasklet_kill(&dev->event_tasklet); +- for (i = MAX_STREAM - 1; i >= 0; i--) +- release_channel(&dev->channel[i]); +- if (dev->ci.en) +- cxd_detach(dev); +- ngene_stop(dev); +- ngene_release_buffers(dev); +- pci_set_drvdata(pdev, NULL); +- pci_disable_device(pdev); +-} +- +-int __devinit ngene_probe(struct pci_dev *pci_dev, +- const struct pci_device_id *id) +-{ +- struct ngene *dev; +- int stat = 0; +- +- if (pci_enable_device(pci_dev) < 0) +- return -ENODEV; +- +- dev = vzalloc(sizeof(struct ngene)); +- if (dev == NULL) { +- stat = -ENOMEM; +- goto fail0; +- } +- +- dev->pci_dev = pci_dev; +- dev->card_info = (struct ngene_info *)id->driver_data; +- printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); +- +- pci_set_drvdata(pci_dev, dev); +- +- /* Alloc buffers and start nGene */ +- stat = ngene_get_buffers(dev); +- if (stat < 0) +- goto fail1; +- stat = ngene_start(dev); +- if (stat < 0) +- goto fail1; +- +- cxd_attach(dev); +- +- stat = ngene_buffer_config(dev); +- if (stat < 0) +- goto fail1; +- +- +- dev->i2c_current_bus = -1; +- +- /* Register DVB adapters and devices for both channels */ +- if (init_channels(dev) < 0) +- goto fail2; +- +- return 0; +- +-fail2: +- ngene_stop(dev); +-fail1: +- ngene_release_buffers(dev); +-fail0: +- pci_disable_device(pci_dev); +- pci_set_drvdata(pci_dev, NULL); +- return stat; +-} +diff --git a/drivers/media/dvb/ngene/ngene-dvb.c b/drivers/media/dvb/ngene/ngene-dvb.c +deleted file mode 100644 +index fcb16a6..0000000 +--- a/drivers/media/dvb/ngene/ngene-dvb.c ++++ /dev/null +@@ -1,261 +0,0 @@ +-/* +- * ngene-dvb.c: nGene PCIe bridge driver - DVB functions +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * Copyright (C) 2008-2009 Ralph Metzler +- * Modifications for new nGene firmware, +- * support for EEPROM-copying, +- * support for new dual DVB-S2 card prototype +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "ngene.h" +- +- +-/****************************************************************************/ +-/* COMMAND API interface ****************************************************/ +-/****************************************************************************/ +- +-static ssize_t ts_write(struct file *file, const char *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct ngene_channel *chan = dvbdev->priv; +- struct ngene *dev = chan->dev; +- +- if (wait_event_interruptible(dev->tsout_rbuf.queue, +- dvb_ringbuffer_free +- (&dev->tsout_rbuf) >= count) < 0) +- return 0; +- +- dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); +- +- return count; +-} +- +-static ssize_t ts_read(struct file *file, char *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct ngene_channel *chan = dvbdev->priv; +- struct ngene *dev = chan->dev; +- int left, avail; +- +- left = count; +- while (left) { +- if (wait_event_interruptible( +- dev->tsin_rbuf.queue, +- dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0) +- return -EAGAIN; +- avail = dvb_ringbuffer_avail(&dev->tsin_rbuf); +- if (avail > left) +- avail = left; +- dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail); +- left -= avail; +- buf += avail; +- } +- return count; +-} +- +-static const struct file_operations ci_fops = { +- .owner = THIS_MODULE, +- .read = ts_read, +- .write = ts_write, +- .open = dvb_generic_open, +- .release = dvb_generic_release, +-}; +- +-struct dvb_device ngene_dvbdev_ci = { +- .priv = 0, +- .readers = -1, +- .writers = -1, +- .users = -1, +- .fops = &ci_fops, +-}; +- +- +-/****************************************************************************/ +-/* DVB functions and API interface ******************************************/ +-/****************************************************************************/ +- +-static void swap_buffer(u32 *p, u32 len) +-{ +- while (len) { +- *p = swab32(*p); +- p++; +- len -= 4; +- } +-} +- +-/* start of filler packet */ +-static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER }; +- +-/* #define DEBUG_CI_XFER */ +-#ifdef DEBUG_CI_XFER +-static u32 ok; +-static u32 overflow; +-static u32 stripped; +-#endif +- +-void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +-{ +- struct ngene_channel *chan = priv; +- struct ngene *dev = chan->dev; +- +- +- if (flags & DF_SWAP32) +- swap_buffer(buf, len); +- +- if (dev->ci.en && chan->number == 2) { +- while (len >= 188) { +- if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) { +- if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { +- dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); +- wake_up(&dev->tsin_rbuf.queue); +-#ifdef DEBUG_CI_XFER +- ok++; +-#endif +- } +-#ifdef DEBUG_CI_XFER +- else +- overflow++; +-#endif +- } +-#ifdef DEBUG_CI_XFER +- else +- stripped++; +- +- if (ok % 100 == 0 && overflow) +- printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped); +-#endif +- buf += 188; +- len -= 188; +- } +- return NULL; +- } +- +- if (chan->users > 0) +- dvb_dmx_swfilter(&chan->demux, buf, len); +- +- return NULL; +-} +- +-void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +-{ +- struct ngene_channel *chan = priv; +- struct ngene *dev = chan->dev; +- u32 alen; +- +- alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); +- alen -= alen % 188; +- +- if (alen < len) +- FillTSBuffer(buf + alen, len - alen, flags); +- else +- alen = len; +- dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); +- if (flags & DF_SWAP32) +- swap_buffer((u32 *)buf, alen); +- wake_up_interruptible(&dev->tsout_rbuf.queue); +- return buf; +-} +- +- +- +-int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct ngene_channel *chan = dvbdmx->priv; +- +- if (chan->users == 0) { +- if (!chan->dev->cmd_timeout_workaround || !chan->running) +- set_transfer(chan, 1); +- } +- +- return ++chan->users; +-} +- +-int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct ngene_channel *chan = dvbdmx->priv; +- +- if (--chan->users) +- return chan->users; +- +- if (!chan->dev->cmd_timeout_workaround) +- set_transfer(chan, 0); +- +- return 0; +-} +- +-int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, +- int (*start_feed)(struct dvb_demux_feed *), +- int (*stop_feed)(struct dvb_demux_feed *), +- void *priv) +-{ +- dvbdemux->priv = priv; +- +- dvbdemux->filternum = 256; +- dvbdemux->feednum = 256; +- dvbdemux->start_feed = start_feed; +- dvbdemux->stop_feed = stop_feed; +- dvbdemux->write_to_decoder = NULL; +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | +- DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING); +- return dvb_dmx_init(dvbdemux); +-} +- +-int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, +- struct dvb_demux *dvbdemux, +- struct dmx_frontend *hw_frontend, +- struct dmx_frontend *mem_frontend, +- struct dvb_adapter *dvb_adapter) +-{ +- int ret; +- +- dmxdev->filternum = 256; +- dmxdev->demux = &dvbdemux->dmx; +- dmxdev->capabilities = 0; +- ret = dvb_dmxdev_init(dmxdev, dvb_adapter); +- if (ret < 0) +- return ret; +- +- hw_frontend->source = DMX_FRONTEND_0; +- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); +- mem_frontend->source = DMX_MEMORY_FE; +- dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); +- return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +-} +diff --git a/drivers/media/dvb/ngene/ngene-i2c.c b/drivers/media/dvb/ngene/ngene-i2c.c +deleted file mode 100644 +index d28554f..0000000 +--- a/drivers/media/dvb/ngene/ngene-i2c.c ++++ /dev/null +@@ -1,176 +0,0 @@ +-/* +- * ngene-i2c.c: nGene PCIe bridge driver i2c functions +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * Copyright (C) 2008-2009 Ralph Metzler +- * Modifications for new nGene firmware, +- * support for EEPROM-copying, +- * support for new dual DVB-S2 card prototype +- * +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-/* FIXME - some of these can probably be removed */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "ngene.h" +- +-/* Firmware command for i2c operations */ +-static int ngene_command_i2c_read(struct ngene *dev, u8 adr, +- u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) +-{ +- struct ngene_command com; +- +- com.cmd.hdr.Opcode = CMD_I2C_READ; +- com.cmd.hdr.Length = outlen + 3; +- com.cmd.I2CRead.Device = adr << 1; +- memcpy(com.cmd.I2CRead.Data, out, outlen); +- com.cmd.I2CRead.Data[outlen] = inlen; +- com.cmd.I2CRead.Data[outlen + 1] = 0; +- com.in_len = outlen + 3; +- com.out_len = inlen + 1; +- +- if (ngene_command(dev, &com) < 0) +- return -EIO; +- +- if ((com.cmd.raw8[0] >> 1) != adr) +- return -EIO; +- +- if (flag) +- memcpy(in, com.cmd.raw8, inlen + 1); +- else +- memcpy(in, com.cmd.raw8 + 1, inlen); +- return 0; +-} +- +-static int ngene_command_i2c_write(struct ngene *dev, u8 adr, +- u8 *out, u8 outlen) +-{ +- struct ngene_command com; +- +- +- com.cmd.hdr.Opcode = CMD_I2C_WRITE; +- com.cmd.hdr.Length = outlen + 1; +- com.cmd.I2CRead.Device = adr << 1; +- memcpy(com.cmd.I2CRead.Data, out, outlen); +- com.in_len = outlen + 1; +- com.out_len = 1; +- +- if (ngene_command(dev, &com) < 0) +- return -EIO; +- +- if (com.cmd.raw8[0] == 1) +- return -EIO; +- +- return 0; +-} +- +-static void ngene_i2c_set_bus(struct ngene *dev, int bus) +-{ +- if (!(dev->card_info->i2c_access & 2)) +- return; +- if (dev->i2c_current_bus == bus) +- return; +- +- switch (bus) { +- case 0: +- ngene_command_gpio_set(dev, 3, 0); +- ngene_command_gpio_set(dev, 2, 1); +- break; +- +- case 1: +- ngene_command_gpio_set(dev, 2, 0); +- ngene_command_gpio_set(dev, 3, 1); +- break; +- } +- dev->i2c_current_bus = bus; +-} +- +-static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, +- struct i2c_msg msg[], int num) +-{ +- struct ngene_channel *chan = +- (struct ngene_channel *)i2c_get_adapdata(adapter); +- struct ngene *dev = chan->dev; +- +- down(&dev->i2c_switch_mutex); +- ngene_i2c_set_bus(dev, chan->number); +- +- if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) +- if (!ngene_command_i2c_read(dev, msg[0].addr, +- msg[0].buf, msg[0].len, +- msg[1].buf, msg[1].len, 0)) +- goto done; +- +- if (num == 1 && !(msg[0].flags & I2C_M_RD)) +- if (!ngene_command_i2c_write(dev, msg[0].addr, +- msg[0].buf, msg[0].len)) +- goto done; +- if (num == 1 && (msg[0].flags & I2C_M_RD)) +- if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, +- msg[0].buf, msg[0].len, 0)) +- goto done; +- +- up(&dev->i2c_switch_mutex); +- return -EIO; +- +-done: +- up(&dev->i2c_switch_mutex); +- return num; +-} +- +- +-static u32 ngene_i2c_functionality(struct i2c_adapter *adap) +-{ +- return I2C_FUNC_SMBUS_EMUL; +-} +- +-static struct i2c_algorithm ngene_i2c_algo = { +- .master_xfer = ngene_i2c_master_xfer, +- .functionality = ngene_i2c_functionality, +-}; +- +-int ngene_i2c_init(struct ngene *dev, int dev_nr) +-{ +- struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); +- +- i2c_set_adapdata(adap, &(dev->channel[dev_nr])); +- +- strcpy(adap->name, "nGene"); +- +- adap->algo = &ngene_i2c_algo; +- adap->algo_data = (void *)&(dev->channel[dev_nr]); +- adap->dev.parent = &dev->pci_dev->dev; +- +- return i2c_add_adapter(adap); +-} +- +diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h +deleted file mode 100644 +index 5443dc0..0000000 +--- a/drivers/media/dvb/ngene/ngene.h ++++ /dev/null +@@ -1,921 +0,0 @@ +-/* +- * ngene.h: nGene PCIe bridge driver +- * +- * Copyright (C) 2005-2007 Micronas +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * version 2 only, as published by the Free Software Foundation. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +- * 02110-1301, USA +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- */ +- +-#ifndef _NGENE_H_ +-#define _NGENE_H_ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_ca_en50221.h" +-#include "dvb_frontend.h" +-#include "dvb_ringbuffer.h" +-#include "dvb_net.h" +-#include "cxd2099.h" +- +-#define DEVICE_NAME "ngene" +- +-#define NGENE_VID 0x18c3 +-#define NGENE_PID 0x0720 +- +-#ifndef VIDEO_CAP_VC1 +-#define VIDEO_CAP_AVC 128 +-#define VIDEO_CAP_H264 128 +-#define VIDEO_CAP_VC1 256 +-#define VIDEO_CAP_WMV9 256 +-#define VIDEO_CAP_MPEG4 512 +-#endif +- +-enum STREAM { +- STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ +- STREAM_VIDEOIN2, +- STREAM_AUDIOIN1, /* I2S or SPI Input */ +- STREAM_AUDIOIN2, +- STREAM_AUDIOOUT, +- MAX_STREAM +-}; +- +-enum SMODE_BITS { +- SMODE_AUDIO_SPDIF = 0x20, +- SMODE_AVSYNC = 0x10, +- SMODE_TRANSPORT_STREAM = 0x08, +- SMODE_AUDIO_CAPTURE = 0x04, +- SMODE_VBI_CAPTURE = 0x02, +- SMODE_VIDEO_CAPTURE = 0x01 +-}; +- +-enum STREAM_FLAG_BITS { +- SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */ +- SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */ +- SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */ +- SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */ +- SFLAG_COLORBAR = 0x04, /* Select colorbar */ +-}; +- +-#define PROGRAM_ROM 0x0000 +-#define PROGRAM_SRAM 0x1000 +-#define PERIPHERALS0 0x8000 +-#define PERIPHERALS1 0x9000 +-#define SHARED_BUFFER 0xC000 +- +-#define HOST_TO_NGENE (SHARED_BUFFER+0x0000) +-#define NGENE_TO_HOST (SHARED_BUFFER+0x0100) +-#define NGENE_COMMAND (SHARED_BUFFER+0x0200) +-#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204) +-#define NGENE_STATUS (SHARED_BUFFER+0x0208) +-#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C) +-#define NGENE_EVENT (SHARED_BUFFER+0x0210) +-#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214) +-#define VARIABLES (SHARED_BUFFER+0x0210) +- +-#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260) +-#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264) +-#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268) +- +-#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800) +-#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900) +-#define EEPROM_AREA (SHARED_BUFFER+0x0A00) +- +-#define SG_V_IN_1 (SHARED_BUFFER+0x0A80) +-#define SG_VBI_1 (SHARED_BUFFER+0x0B00) +-#define SG_A_IN_1 (SHARED_BUFFER+0x0B80) +-#define SG_V_IN_2 (SHARED_BUFFER+0x0C00) +-#define SG_VBI_2 (SHARED_BUFFER+0x0C80) +-#define SG_A_IN_2 (SHARED_BUFFER+0x0D00) +-#define SG_V_OUT (SHARED_BUFFER+0x0D80) +-#define SG_A_OUT2 (SHARED_BUFFER+0x0E00) +- +-#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80) +-#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00) +-#define DATA_A_OUT (SHARED_BUFFER+0x0F80) +-#define DATA_V_IN_1 (SHARED_BUFFER+0x1000) +-#define DATA_V_IN_2 (SHARED_BUFFER+0x2000) +-#define DATA_V_OUT (SHARED_BUFFER+0x3000) +- +-#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000) +- +-#define TIMESTAMPS 0xA000 +-#define SCRATCHPAD 0xA080 +-#define FORCE_INT 0xA088 +-#define FORCE_NMI 0xA090 +-#define INT_STATUS 0xA0A0 +- +-#define DEV_VER 0x9004 +- +-#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF) +- +-struct SG_ADDR { +- u64 start; +- u64 curr; +- u16 curr_ptr; +- u16 elements; +- u32 pad[3]; +-} __attribute__ ((__packed__)); +- +-struct SHARED_MEMORY { +- /* C000 */ +- u32 HostToNgene[64]; +- +- /* C100 */ +- u32 NgeneToHost[64]; +- +- /* C200 */ +- u64 NgeneCommand; +- u64 NgeneStatus; +- u64 NgeneEvent; +- +- /* C210 */ +- u8 pad1[0xc260 - 0xc218]; +- +- /* C260 */ +- u32 IntCounts; +- u32 IntEnable; +- +- /* C268 */ +- u8 pad2[0xd000 - 0xc268]; +- +-} __attribute__ ((__packed__)); +- +-struct BUFFER_STREAM_RESULTS { +- u32 Clock; /* Stream time in 100ns units */ +- u16 RemainingLines; /* Remaining lines in this field. +- 0 for complete field */ +- u8 FieldCount; /* Video field number */ +- u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow, +- Bit 0 = FieldID */ +- u16 BlockCount; /* Audio block count (unused) */ +- u8 Reserved[2]; +- u32 DTOUpdate; +-} __attribute__ ((__packed__)); +- +-struct HW_SCATTER_GATHER_ELEMENT { +- u64 Address; +- u32 Length; +- u32 Reserved; +-} __attribute__ ((__packed__)); +- +-struct BUFFER_HEADER { +- u64 Next; +- struct BUFFER_STREAM_RESULTS SR; +- +- u32 Number_of_entries_1; +- u32 Reserved5; +- u64 Address_of_first_entry_1; +- +- u32 Number_of_entries_2; +- u32 Reserved7; +- u64 Address_of_first_entry_2; +-} __attribute__ ((__packed__)); +- +-struct EVENT_BUFFER { +- u32 TimeStamp; +- u8 GPIOStatus; +- u8 UARTStatus; +- u8 RXCharacter; +- u8 EventStatus; +- u32 Reserved[2]; +-} __attribute__ ((__packed__)); +- +-/* Firmware commands. */ +- +-enum OPCODES { +- CMD_NOP = 0, +- CMD_FWLOAD_PREPARE = 0x01, +- CMD_FWLOAD_FINISH = 0x02, +- CMD_I2C_READ = 0x03, +- CMD_I2C_WRITE = 0x04, +- +- CMD_I2C_WRITE_NOSTOP = 0x05, +- CMD_I2C_CONTINUE_WRITE = 0x06, +- CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07, +- +- CMD_DEBUG_OUTPUT = 0x09, +- +- CMD_CONTROL = 0x10, +- CMD_CONFIGURE_BUFFER = 0x11, +- CMD_CONFIGURE_FREE_BUFFER = 0x12, +- +- CMD_SPI_READ = 0x13, +- CMD_SPI_WRITE = 0x14, +- +- CMD_MEM_READ = 0x20, +- CMD_MEM_WRITE = 0x21, +- CMD_SFR_READ = 0x22, +- CMD_SFR_WRITE = 0x23, +- CMD_IRAM_READ = 0x24, +- CMD_IRAM_WRITE = 0x25, +- CMD_SET_GPIO_PIN = 0x26, +- CMD_SET_GPIO_INT = 0x27, +- CMD_CONFIGURE_UART = 0x28, +- CMD_WRITE_UART = 0x29, +- MAX_CMD +-}; +- +-enum RESPONSES { +- OK = 0, +- ERROR = 1 +-}; +- +-struct FW_HEADER { +- u8 Opcode; +- u8 Length; +-} __attribute__ ((__packed__)); +- +-struct FW_I2C_WRITE { +- struct FW_HEADER hdr; +- u8 Device; +- u8 Data[250]; +-} __attribute__ ((__packed__)); +- +-struct FW_I2C_CONTINUE_WRITE { +- struct FW_HEADER hdr; +- u8 Data[250]; +-} __attribute__ ((__packed__)); +- +-struct FW_I2C_READ { +- struct FW_HEADER hdr; +- u8 Device; +- u8 Data[252]; /* followed by two bytes of read data count */ +-} __attribute__ ((__packed__)); +- +-struct FW_SPI_WRITE { +- struct FW_HEADER hdr; +- u8 ModeSelect; +- u8 Data[250]; +-} __attribute__ ((__packed__)); +- +-struct FW_SPI_READ { +- struct FW_HEADER hdr; +- u8 ModeSelect; +- u8 Data[252]; /* followed by two bytes of read data count */ +-} __attribute__ ((__packed__)); +- +-struct FW_FWLOAD_PREPARE { +- struct FW_HEADER hdr; +-} __attribute__ ((__packed__)); +- +-struct FW_FWLOAD_FINISH { +- struct FW_HEADER hdr; +- u16 Address; /* address of final block */ +- u16 Length; +-} __attribute__ ((__packed__)); +- +-/* +- * Meaning of FW_STREAM_CONTROL::Mode bits: +- * Bit 7: Loopback PEXin to PEXout using TVOut channel +- * Bit 6: AVLOOP +- * Bit 5: Audio select; 0=I2S, 1=SPDIF +- * Bit 4: AVSYNC +- * Bit 3: Enable transport stream +- * Bit 2: Enable audio capture +- * Bit 1: Enable ITU-Video VBI capture +- * Bit 0: Enable ITU-Video capture +- * +- * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL) +- * Bit 7: continuous capture +- * Bit 6: capture one field +- * Bit 5: capture one frame +- * Bit 4: unused +- * Bit 3: starting field; 0=odd, 1=even +- * Bit 2: sample size; 0=8-bit, 1=10-bit +- * Bit 1: data format; 0=UYVY, 1=YUY2 +- * Bit 0: resets buffer pointers +-*/ +- +-enum FSC_MODE_BITS { +- SMODE_LOOPBACK = 0x80, +- SMODE_AVLOOP = 0x40, +- _SMODE_AUDIO_SPDIF = 0x20, +- _SMODE_AVSYNC = 0x10, +- _SMODE_TRANSPORT_STREAM = 0x08, +- _SMODE_AUDIO_CAPTURE = 0x04, +- _SMODE_VBI_CAPTURE = 0x02, +- _SMODE_VIDEO_CAPTURE = 0x01 +-}; +- +- +-/* Meaning of FW_STREAM_CONTROL::Stream bits: +- * Bit 3: Audio sample count: 0 = relative, 1 = absolute +- * Bit 2: color bar select; 1=color bars, 0=CV3 decoder +- * Bits 1-0: stream select, UVI1, UVI2, TVOUT +- */ +- +-struct FW_STREAM_CONTROL { +- struct FW_HEADER hdr; +- u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */ +- u8 Control; /* Value written to UVI1_CTL */ +- u8 Mode; /* Controls clock source */ +- u8 SetupDataLen; /* Length of setup data, MSB=1 write +- backwards */ +- u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer +- for TS and Audio */ +- u64 Buffer_Address; /* Address of first buffer header */ +- u16 BytesPerVideoLine; +- u16 MaxLinesPerField; +- u16 MinLinesPerField; +- u16 Reserved_1; +- u16 BytesPerVBILine; +- u16 MaxVBILinesPerField; +- u16 MinVBILinesPerField; +- u16 SetupDataAddr; /* ngene relative address of setup data */ +- u8 SetupData[32]; /* setup data */ +-} __attribute__((__packed__)); +- +-#define AUDIO_BLOCK_SIZE 256 +-#define TS_BLOCK_SIZE 256 +- +-struct FW_MEM_READ { +- struct FW_HEADER hdr; +- u16 address; +-} __attribute__ ((__packed__)); +- +-struct FW_MEM_WRITE { +- struct FW_HEADER hdr; +- u16 address; +- u8 data; +-} __attribute__ ((__packed__)); +- +-struct FW_SFR_IRAM_READ { +- struct FW_HEADER hdr; +- u8 address; +-} __attribute__ ((__packed__)); +- +-struct FW_SFR_IRAM_WRITE { +- struct FW_HEADER hdr; +- u8 address; +- u8 data; +-} __attribute__ ((__packed__)); +- +-struct FW_SET_GPIO_PIN { +- struct FW_HEADER hdr; +- u8 select; +-} __attribute__ ((__packed__)); +- +-struct FW_SET_GPIO_INT { +- struct FW_HEADER hdr; +- u8 select; +-} __attribute__ ((__packed__)); +- +-struct FW_SET_DEBUGMODE { +- struct FW_HEADER hdr; +- u8 debug_flags; +-} __attribute__ ((__packed__)); +- +-struct FW_CONFIGURE_BUFFERS { +- struct FW_HEADER hdr; +- u8 config; +-} __attribute__ ((__packed__)); +- +-enum _BUFFER_CONFIGS { +- /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */ +- BUFFER_CONFIG_4422 = 0, +- /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */ +- BUFFER_CONFIG_3333 = 1, +- /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */ +- BUFFER_CONFIG_8022 = 2, +- BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */ +-}; +- +-struct FW_CONFIGURE_FREE_BUFFERS { +- struct FW_HEADER hdr; +- u8 UVI1_BufferLength; +- u8 UVI2_BufferLength; +- u8 TVO_BufferLength; +- u8 AUD1_BufferLength; +- u8 AUD2_BufferLength; +- u8 TVA_BufferLength; +-} __attribute__ ((__packed__)); +- +-struct FW_CONFIGURE_UART { +- struct FW_HEADER hdr; +- u8 UartControl; +-} __attribute__ ((__packed__)); +- +-enum _UART_CONFIG { +- _UART_BAUDRATE_19200 = 0, +- _UART_BAUDRATE_9600 = 1, +- _UART_BAUDRATE_4800 = 2, +- _UART_BAUDRATE_2400 = 3, +- _UART_RX_ENABLE = 0x40, +- _UART_TX_ENABLE = 0x80, +-}; +- +-struct FW_WRITE_UART { +- struct FW_HEADER hdr; +- u8 Data[252]; +-} __attribute__ ((__packed__)); +- +- +-struct ngene_command { +- u32 in_len; +- u32 out_len; +- union { +- u32 raw[64]; +- u8 raw8[256]; +- struct FW_HEADER hdr; +- struct FW_I2C_WRITE I2CWrite; +- struct FW_I2C_CONTINUE_WRITE I2CContinueWrite; +- struct FW_I2C_READ I2CRead; +- struct FW_STREAM_CONTROL StreamControl; +- struct FW_FWLOAD_PREPARE FWLoadPrepare; +- struct FW_FWLOAD_FINISH FWLoadFinish; +- struct FW_MEM_READ MemoryRead; +- struct FW_MEM_WRITE MemoryWrite; +- struct FW_SFR_IRAM_READ SfrIramRead; +- struct FW_SFR_IRAM_WRITE SfrIramWrite; +- struct FW_SPI_WRITE SPIWrite; +- struct FW_SPI_READ SPIRead; +- struct FW_SET_GPIO_PIN SetGpioPin; +- struct FW_SET_GPIO_INT SetGpioInt; +- struct FW_SET_DEBUGMODE SetDebugMode; +- struct FW_CONFIGURE_BUFFERS ConfigureBuffers; +- struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers; +- struct FW_CONFIGURE_UART ConfigureUart; +- struct FW_WRITE_UART WriteUart; +- } cmd; +-} __attribute__ ((__packed__)); +- +-#define NGENE_INTERFACE_VERSION 0x103 +-#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */ +-#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */ +-#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */ +-#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */ +-#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page +- Max: (1920x1080i60) */ +- +-#define OVERFLOW_BUFFER_SIZE (8192) +- +-#define RING_SIZE_VIDEO 4 +-#define RING_SIZE_AUDIO 8 +-#define RING_SIZE_TS 8 +- +-#define NUM_SCATTER_GATHER_ENTRIES 8 +- +-#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \ +- RING_SIZE_VIDEO * 2) + \ +- (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \ +- (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \ +- (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \ +- (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \ +- (RING_SIZE_TS * PAGE_SIZE * 4) + \ +- 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE) +- +-#define EVENT_QUEUE_SIZE 16 +- +-/* Gathers the current state of a single channel. */ +- +-struct SBufferHeader { +- struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ +- struct SBufferHeader *Next; +- void *Buffer1; +- struct HW_SCATTER_GATHER_ELEMENT *scList1; +- void *Buffer2; +- struct HW_SCATTER_GATHER_ELEMENT *scList2; +-}; +- +-/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ +-#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63) +- +-enum HWSTATE { +- HWSTATE_STOP, +- HWSTATE_STARTUP, +- HWSTATE_RUN, +- HWSTATE_PAUSE, +-}; +- +-enum KSSTATE { +- KSSTATE_STOP, +- KSSTATE_ACQUIRE, +- KSSTATE_PAUSE, +- KSSTATE_RUN, +-}; +- +-struct SRingBufferDescriptor { +- struct SBufferHeader *Head; /* Points to first buffer in ring buffer +- structure*/ +- u64 PAHead; /* Physical address of first buffer */ +- u32 MemSize; /* Memory size of allocated ring buffers +- (needed for freeing) */ +- u32 NumBuffers; /* Number of buffers in the ring */ +- u32 Buffer1Length; /* Allocated length of Buffer 1 */ +- u32 Buffer2Length; /* Allocated length of Buffer 2 */ +- void *SCListMem; /* Memory to hold scatter gather lists for this +- ring */ +- u64 PASCListMem; /* Physical address .. */ +- u32 SCListMemSize; /* Size of this memory */ +-}; +- +-enum STREAMMODEFLAGS { +- StreamMode_NONE = 0, /* Stream not used */ +- StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */ +- StreamMode_TSIN = 2, /* Transport stream input (all) */ +- StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60 +- (only stream 0) */ +- StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */ +-}; +- +- +-enum BufferExchangeFlags { +- BEF_EVEN_FIELD = 0x00000001, +- BEF_CONTINUATION = 0x00000002, +- BEF_MORE_DATA = 0x00000004, +- BEF_OVERFLOW = 0x00000008, +- DF_SWAP32 = 0x00010000, +-}; +- +-typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); +- +-struct MICI_STREAMINFO { +- IBufferExchange *pExchange; +- IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ +- u8 Stream; +- u8 Flags; +- u8 Mode; +- u8 Reserved; +- u16 nLinesVideo; +- u16 nBytesPerLineVideo; +- u16 nLinesVBI; +- u16 nBytesPerLineVBI; +- u32 CaptureLength; /* Used for audio and transport stream */ +-}; +- +-/****************************************************************************/ +-/* STRUCTS ******************************************************************/ +-/****************************************************************************/ +- +-/* sound hardware definition */ +-#define MIXER_ADDR_TVTUNER 0 +-#define MIXER_ADDR_LAST 0 +- +-struct ngene_channel; +- +-/*struct sound chip*/ +- +-struct mychip { +- struct ngene_channel *chan; +- struct snd_card *card; +- struct pci_dev *pci; +- struct snd_pcm_substream *substream; +- struct snd_pcm *pcm; +- unsigned long port; +- int irq; +- spinlock_t mixer_lock; +- spinlock_t lock; +- int mixer_volume[MIXER_ADDR_LAST + 1][2]; +- int capture_source[MIXER_ADDR_LAST + 1][2]; +-}; +- +-#ifdef NGENE_V4L +-struct ngene_overlay { +- int tvnorm; +- struct v4l2_rect w; +- enum v4l2_field field; +- struct v4l2_clip *clips; +- int nclips; +- int setup_ok; +-}; +- +-struct ngene_tvnorm { +- int v4l2_id; +- char *name; +- u16 swidth, sheight; /* scaled standard width, height */ +- int tuner_norm; +- int soundstd; +-}; +- +-struct ngene_vopen { +- struct ngene_channel *ch; +- enum v4l2_priority prio; +- int width; +- int height; +- int depth; +- struct videobuf_queue vbuf_q; +- struct videobuf_queue vbi; +- int fourcc; +- int picxcount; +- int resources; +- enum v4l2_buf_type type; +- const struct ngene_format *fmt; +- +- const struct ngene_format *ovfmt; +- struct ngene_overlay ov; +-}; +-#endif +- +-struct ngene_channel { +- struct device device; +- struct i2c_adapter i2c_adapter; +- +- struct ngene *dev; +- int number; +- int type; +- int mode; +- bool has_adapter; +- bool has_demux; +- int demod_type; +- int (*gate_ctrl)(struct dvb_frontend *, int); +- +- struct dvb_frontend *fe; +- struct dvb_frontend *fe2; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- struct dvb_net dvbnet; +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- int users; +- struct video_device *v4l_dev; +- struct dvb_device *ci_dev; +- struct tasklet_struct demux_tasklet; +- +- struct SBufferHeader *nextBuffer; +- enum KSSTATE State; +- enum HWSTATE HWState; +- u8 Stream; +- u8 Flags; +- u8 Mode; +- IBufferExchange *pBufferExchange; +- IBufferExchange *pBufferExchange2; +- +- spinlock_t state_lock; +- u16 nLines; +- u16 nBytesPerLine; +- u16 nVBILines; +- u16 nBytesPerVBILine; +- u16 itumode; +- u32 Capture1Length; +- u32 Capture2Length; +- struct SRingBufferDescriptor RingBuffer; +- struct SRingBufferDescriptor TSRingBuffer; +- struct SRingBufferDescriptor TSIdleBuffer; +- +- u32 DataFormatFlags; +- +- int AudioDTOUpdated; +- u32 AudioDTOValue; +- +- int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); +- u8 lnbh; +- +- /* stuff from analog driver */ +- +- int minor; +- struct mychip *mychip; +- struct snd_card *soundcard; +- u8 *evenbuffer; +- u8 dma_on; +- int soundstreamon; +- int audiomute; +- int soundbuffisallocated; +- int sndbuffflag; +- int tun_rdy; +- int dec_rdy; +- int tun_dec_rdy; +- int lastbufferflag; +- +- struct ngene_tvnorm *tvnorms; +- int tvnorm_num; +- int tvnorm; +- +-#ifdef NGENE_V4L +- int videousers; +- struct v4l2_prio_state prio; +- struct ngene_vopen init; +- int resources; +- struct v4l2_framebuffer fbuf; +- struct ngene_buffer *screen; /* overlay */ +- struct list_head capture; /* video capture queue */ +- spinlock_t s_lock; +- struct semaphore reslock; +-#endif +- +- int running; +-}; +- +- +-struct ngene_ci { +- struct device device; +- struct i2c_adapter i2c_adapter; +- +- struct ngene *dev; +- struct dvb_ca_en50221 *en; +-}; +- +-struct ngene; +- +-typedef void (rx_cb_t)(struct ngene *, u32, u8); +-typedef void (tx_cb_t)(struct ngene *, u32); +- +-struct ngene { +- int nr; +- struct pci_dev *pci_dev; +- unsigned char *iomem; +- +- /*struct i2c_adapter i2c_adapter;*/ +- +- u32 device_version; +- u32 fw_interface_version; +- u32 icounts; +- bool msi_enabled; +- bool cmd_timeout_workaround; +- +- u8 *CmdDoneByte; +- int BootFirmware; +- void *OverflowBuffer; +- dma_addr_t PAOverflowBuffer; +- void *FWInterfaceBuffer; +- dma_addr_t PAFWInterfaceBuffer; +- u8 *ngenetohost; +- u8 *hosttongene; +- +- struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; +- int EventQueueOverflowCount; +- int EventQueueOverflowFlag; +- struct tasklet_struct event_tasklet; +- struct EVENT_BUFFER *EventBuffer; +- int EventQueueWriteIndex; +- int EventQueueReadIndex; +- +- wait_queue_head_t cmd_wq; +- int cmd_done; +- struct semaphore cmd_mutex; +- struct semaphore stream_mutex; +- struct semaphore pll_mutex; +- struct semaphore i2c_switch_mutex; +- int i2c_current_channel; +- int i2c_current_bus; +- spinlock_t cmd_lock; +- +- struct dvb_adapter adapter[MAX_STREAM]; +- struct dvb_adapter *first_adapter; /* "one_adapter" modprobe opt */ +- struct ngene_channel channel[MAX_STREAM]; +- +- struct ngene_info *card_info; +- +- tx_cb_t *TxEventNotify; +- rx_cb_t *RxEventNotify; +- int tx_busy; +- wait_queue_head_t tx_wq; +- wait_queue_head_t rx_wq; +-#define UART_RBUF_LEN 4096 +- u8 uart_rbuf[UART_RBUF_LEN]; +- int uart_rp, uart_wp; +- +-#define TS_FILLER 0x6f +- +- u8 *tsout_buf; +-#define TSOUT_BUF_SIZE (512*188*8) +- struct dvb_ringbuffer tsout_rbuf; +- +- u8 *tsin_buf; +-#define TSIN_BUF_SIZE (512*188*8) +- struct dvb_ringbuffer tsin_rbuf; +- +- u8 *ain_buf; +-#define AIN_BUF_SIZE (128*1024) +- struct dvb_ringbuffer ain_rbuf; +- +- +- u8 *vin_buf; +-#define VIN_BUF_SIZE (4*1920*1080) +- struct dvb_ringbuffer vin_rbuf; +- +- unsigned long exp_val; +- int prev_cmd; +- +- struct ngene_ci ci; +-}; +- +-struct ngene_info { +- int type; +-#define NGENE_APP 0 +-#define NGENE_TERRATEC 1 +-#define NGENE_SIDEWINDER 2 +-#define NGENE_RACER 3 +-#define NGENE_VIPER 4 +-#define NGENE_PYTHON 5 +-#define NGENE_VBOX_V1 6 +-#define NGENE_VBOX_V2 7 +- +- int fw_version; +- bool msi_supported; +- char *name; +- +- int io_type[MAX_STREAM]; +-#define NGENE_IO_NONE 0 +-#define NGENE_IO_TV 1 +-#define NGENE_IO_HDTV 2 +-#define NGENE_IO_TSIN 4 +-#define NGENE_IO_TSOUT 8 +-#define NGENE_IO_AIN 16 +- +- void *fe_config[4]; +- void *tuner_config[4]; +- +- int (*demod_attach[4])(struct ngene_channel *); +- int (*tuner_attach[4])(struct ngene_channel *); +- +- u8 avf[4]; +- u8 msp[4]; +- u8 demoda[4]; +- u8 lnb[4]; +- int i2c_access; +- u8 ntsc; +- u8 tsf[4]; +- u8 i2s[4]; +- +- int (*gate_ctrl)(struct dvb_frontend *, int); +- int (*switch_ctrl)(struct ngene_channel *, int, int); +-}; +- +-#ifdef NGENE_V4L +-struct ngene_format { +- char *name; +- int fourcc; /* video4linux 2 */ +- int btformat; /* BT848_COLOR_FMT_* */ +- int format; +- int btswap; /* BT848_COLOR_CTL_* */ +- int depth; /* bit/pixel */ +- int flags; +- int hshift, vshift; /* for planar modes */ +- int palette; +-}; +- +-#define RESOURCE_OVERLAY 1 +-#define RESOURCE_VIDEO 2 +-#define RESOURCE_VBI 4 +- +-struct ngene_buffer { +- /* common v4l buffer stuff -- must be first */ +- struct videobuf_buffer vb; +- +- /* ngene specific */ +- const struct ngene_format *fmt; +- int tvnorm; +- int btformat; +- int btswap; +-}; +-#endif +- +- +-/* Provided by ngene-core.c */ +-int __devinit ngene_probe(struct pci_dev *pci_dev, +- const struct pci_device_id *id); +-void __devexit ngene_remove(struct pci_dev *pdev); +-void ngene_shutdown(struct pci_dev *pdev); +-int ngene_command(struct ngene *dev, struct ngene_command *com); +-int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); +-void set_transfer(struct ngene_channel *chan, int state); +-void FillTSBuffer(void *Buffer, int Length, u32 Flags); +- +-/* Provided by ngene-i2c.c */ +-int ngene_i2c_init(struct ngene *dev, int dev_nr); +- +-/* Provided by ngene-dvb.c */ +-extern struct dvb_device ngene_dvbdev_ci; +-void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +-void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); +-int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); +-int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed); +-int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, +- int (*start_feed)(struct dvb_demux_feed *), +- int (*stop_feed)(struct dvb_demux_feed *), +- void *priv); +-int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, +- struct dvb_demux *dvbdemux, +- struct dmx_frontend *hw_frontend, +- struct dmx_frontend *mem_frontend, +- struct dvb_adapter *dvb_adapter); +- +-#endif +- +-/* LocalWords: Endif +- */ +diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig +deleted file mode 100644 +index 7d8e6e8..0000000 +--- a/drivers/media/dvb/pluto2/Kconfig ++++ /dev/null +@@ -1,15 +0,0 @@ +-config DVB_PLUTO2 +- tristate "Pluto2 cards" +- depends on DVB_CORE && PCI && I2C +- select I2C_ALGOBIT +- select DVB_TDA1004X +- help +- Support for PCI cards based on the Pluto2 FPGA like the Satelco +- Easywatch Mobile Terrestrial DVB-T Receiver. +- +- Since these cards have no MPEG decoder onboard, they transmit +- only compressed MPEG data over the PCI bus, so you need +- an external software decoder to watch TV on your computer. +- +- Say Y or M if you own such a device and want to use it. +- +diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile +deleted file mode 100644 +index 7008223..0000000 +--- a/drivers/media/dvb/pluto2/Makefile ++++ /dev/null +@@ -1,3 +0,0 @@ +-obj-$(CONFIG_DVB_PLUTO2) += pluto2.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c +deleted file mode 100644 +index e1f20c2..0000000 +--- a/drivers/media/dvb/pluto2/pluto2.c ++++ /dev/null +@@ -1,823 +0,0 @@ +-/* +- * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T] +- * +- * Copyright (C) 2005 Andreas Oberritter +- * +- * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/ +- * by Dany Salman +- * Copyright (c) 2004 TDF +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "demux.h" +-#include "dmxdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +-#include "dvbdev.h" +-#include "tda1004x.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define DRIVER_NAME "pluto2" +- +-#define REG_PIDn(n) ((n) << 2) /* PID n pattern registers */ +-#define REG_PCAR 0x0020 /* PC address register */ +-#define REG_TSCR 0x0024 /* TS ctrl & status */ +-#define REG_MISC 0x0028 /* miscellaneous */ +-#define REG_MMAC 0x002c /* MSB MAC address */ +-#define REG_IMAC 0x0030 /* ISB MAC address */ +-#define REG_LMAC 0x0034 /* LSB MAC address */ +-#define REG_SPID 0x0038 /* SPI data */ +-#define REG_SLCS 0x003c /* serial links ctrl/status */ +- +-#define PID0_NOFIL (0x0001 << 16) +-#define PIDn_ENP (0x0001 << 15) +-#define PID0_END (0x0001 << 14) +-#define PID0_AFIL (0x0001 << 13) +-#define PIDn_PID (0x1fff << 0) +- +-#define TSCR_NBPACKETS (0x00ff << 24) +-#define TSCR_DEM (0x0001 << 17) +-#define TSCR_DE (0x0001 << 16) +-#define TSCR_RSTN (0x0001 << 15) +-#define TSCR_MSKO (0x0001 << 14) +-#define TSCR_MSKA (0x0001 << 13) +-#define TSCR_MSKL (0x0001 << 12) +-#define TSCR_OVR (0x0001 << 11) +-#define TSCR_AFUL (0x0001 << 10) +-#define TSCR_LOCK (0x0001 << 9) +-#define TSCR_IACK (0x0001 << 8) +-#define TSCR_ADEF (0x007f << 0) +- +-#define MISC_DVR (0x0fff << 4) +-#define MISC_ALED (0x0001 << 3) +-#define MISC_FRST (0x0001 << 2) +-#define MISC_LED1 (0x0001 << 1) +-#define MISC_LED0 (0x0001 << 0) +- +-#define SPID_SPIDR (0x00ff << 0) +- +-#define SLCS_SCL (0x0001 << 7) +-#define SLCS_SDA (0x0001 << 6) +-#define SLCS_CSN (0x0001 << 2) +-#define SLCS_OVR (0x0001 << 1) +-#define SLCS_SWC (0x0001 << 0) +- +-#define TS_DMA_PACKETS (8) +-#define TS_DMA_BYTES (188 * TS_DMA_PACKETS) +- +-#define I2C_ADDR_TDA10046 0x10 +-#define I2C_ADDR_TUA6034 0xc2 +-#define NHWFILTERS 8 +- +-struct pluto { +- /* pci */ +- struct pci_dev *pdev; +- u8 __iomem *io_mem; +- +- /* dvb */ +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- struct dmxdev dmxdev; +- struct dvb_adapter dvb_adapter; +- struct dvb_demux demux; +- struct dvb_frontend *fe; +- struct dvb_net dvbnet; +- unsigned int full_ts_users; +- unsigned int users; +- +- /* i2c */ +- struct i2c_algo_bit_data i2c_bit; +- struct i2c_adapter i2c_adap; +- unsigned int i2cbug; +- +- /* irq */ +- unsigned int overflow; +- unsigned int dead; +- +- /* dma */ +- dma_addr_t dma_addr; +- u8 dma_buf[TS_DMA_BYTES]; +- u8 dummy[4096]; +-}; +- +-static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed) +-{ +- return container_of(feed->demux, struct pluto, demux); +-} +- +-static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe) +-{ +- return container_of(fe->dvb, struct pluto, dvb_adapter); +-} +- +-static inline u32 pluto_readreg(struct pluto *pluto, u32 reg) +-{ +- return readl(&pluto->io_mem[reg]); +-} +- +-static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val) +-{ +- writel(val, &pluto->io_mem[reg]); +-} +- +-static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) +-{ +- u32 val = readl(&pluto->io_mem[reg]); +- val &= ~mask; +- val |= bits; +- writel(val, &pluto->io_mem[reg]); +-} +- +-static void pluto_write_tscr(struct pluto *pluto, u32 val) +-{ +- /* set the number of packets */ +- val &= ~TSCR_ADEF; +- val |= TS_DMA_PACKETS / 2; +- +- pluto_writereg(pluto, REG_TSCR, val); +-} +- +-static void pluto_setsda(void *data, int state) +-{ +- struct pluto *pluto = data; +- +- if (state) +- pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA); +- else +- pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0); +-} +- +-static void pluto_setscl(void *data, int state) +-{ +- struct pluto *pluto = data; +- +- if (state) +- pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL); +- else +- pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0); +- +- /* try to detect i2c_inb() to workaround hardware bug: +- * reset SDA to high after SCL has been set to low */ +- if ((state) && (pluto->i2cbug == 0)) { +- pluto->i2cbug = 1; +- } else { +- if ((!state) && (pluto->i2cbug == 1)) +- pluto_setsda(pluto, 1); +- pluto->i2cbug = 0; +- } +-} +- +-static int pluto_getsda(void *data) +-{ +- struct pluto *pluto = data; +- +- return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA; +-} +- +-static int pluto_getscl(void *data) +-{ +- struct pluto *pluto = data; +- +- return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL; +-} +- +-static void pluto_reset_frontend(struct pluto *pluto, int reenable) +-{ +- u32 val = pluto_readreg(pluto, REG_MISC); +- +- if (val & MISC_FRST) { +- val &= ~MISC_FRST; +- pluto_writereg(pluto, REG_MISC, val); +- } +- if (reenable) { +- val |= MISC_FRST; +- pluto_writereg(pluto, REG_MISC, val); +- } +-} +- +-static void pluto_reset_ts(struct pluto *pluto, int reenable) +-{ +- u32 val = pluto_readreg(pluto, REG_TSCR); +- +- if (val & TSCR_RSTN) { +- val &= ~TSCR_RSTN; +- pluto_write_tscr(pluto, val); +- } +- if (reenable) { +- val |= TSCR_RSTN; +- pluto_write_tscr(pluto, val); +- } +-} +- +-static void pluto_set_dma_addr(struct pluto *pluto) +-{ +- pluto_writereg(pluto, REG_PCAR, pluto->dma_addr); +-} +- +-static int __devinit pluto_dma_map(struct pluto *pluto) +-{ +- pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, +- TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +- +- return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); +-} +- +-static void pluto_dma_unmap(struct pluto *pluto) +-{ +- pci_unmap_single(pluto->pdev, pluto->dma_addr, +- TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +-} +- +-static int pluto_start_feed(struct dvb_demux_feed *f) +-{ +- struct pluto *pluto = feed_to_pluto(f); +- +- /* enable PID filtering */ +- if (pluto->users++ == 0) +- pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0); +- +- if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) +- pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid); +- else if (pluto->full_ts_users++ == 0) +- pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL); +- +- return 0; +-} +- +-static int pluto_stop_feed(struct dvb_demux_feed *f) +-{ +- struct pluto *pluto = feed_to_pluto(f); +- +- /* disable PID filtering */ +- if (--pluto->users == 0) +- pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL); +- +- if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) +- pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff); +- else if (--pluto->full_ts_users == 0) +- pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0); +- +- return 0; +-} +- +-static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets) +-{ +- /* synchronize the DMA transfer with the CPU +- * first so that we see updated contents. */ +- pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr, +- TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +- +- /* Workaround for broken hardware: +- * [1] On startup NBPACKETS seems to contain an uninitialized value, +- * but no packets have been transferred. +- * [2] Sometimes (actually very often) NBPACKETS stays at zero +- * although one packet has been transferred. +- * [3] Sometimes (actually rarely), the card gets into an erroneous +- * mode where it continuously generates interrupts, claiming it +- * has received nbpackets>TS_DMA_PACKETS packets, but no packet +- * has been transferred. Only a reset seems to solve this +- */ +- if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) { +- unsigned int i = 0; +- while (pluto->dma_buf[i] == 0x47) +- i += 188; +- nbpackets = i / 188; +- if (i == 0) { +- pluto_reset_ts(pluto, 1); +- dev_printk(KERN_DEBUG, &pluto->pdev->dev, "resetting TS because of invalid packet counter\n"); +- } +- } +- +- dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets); +- +- /* clear the dma buffer. this is needed to be able to identify +- * new valid ts packets above */ +- memset(pluto->dma_buf, 0, nbpackets * 188); +- +- /* reset the dma address */ +- pluto_set_dma_addr(pluto); +- +- /* sync the buffer and give it back to the card */ +- pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr, +- TS_DMA_BYTES, PCI_DMA_FROMDEVICE); +-} +- +-static irqreturn_t pluto_irq(int irq, void *dev_id) +-{ +- struct pluto *pluto = dev_id; +- u32 tscr; +- +- /* check whether an interrupt occurred on this device */ +- tscr = pluto_readreg(pluto, REG_TSCR); +- if (!(tscr & (TSCR_DE | TSCR_OVR))) +- return IRQ_NONE; +- +- if (tscr == 0xffffffff) { +- if (pluto->dead == 0) +- dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n"); +- /* It's dead Jim */ +- pluto->dead = 1; +- return IRQ_HANDLED; +- } +- +- /* dma end interrupt */ +- if (tscr & TSCR_DE) { +- pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24); +- /* overflow interrupt */ +- if (tscr & TSCR_OVR) +- pluto->overflow++; +- if (pluto->overflow) { +- dev_err(&pluto->pdev->dev, "overflow irq (%d)\n", +- pluto->overflow); +- pluto_reset_ts(pluto, 1); +- pluto->overflow = 0; +- } +- } else if (tscr & TSCR_OVR) { +- pluto->overflow++; +- } +- +- /* ACK the interrupt */ +- pluto_write_tscr(pluto, tscr | TSCR_IACK); +- +- return IRQ_HANDLED; +-} +- +-static void __devinit pluto_enable_irqs(struct pluto *pluto) +-{ +- u32 val = pluto_readreg(pluto, REG_TSCR); +- +- /* disable AFUL and LOCK interrupts */ +- val |= (TSCR_MSKA | TSCR_MSKL); +- /* enable DMA and OVERFLOW interrupts */ +- val &= ~(TSCR_DEM | TSCR_MSKO); +- /* clear pending interrupts */ +- val |= TSCR_IACK; +- +- pluto_write_tscr(pluto, val); +-} +- +-static void pluto_disable_irqs(struct pluto *pluto) +-{ +- u32 val = pluto_readreg(pluto, REG_TSCR); +- +- /* disable all interrupts */ +- val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL); +- /* clear pending interrupts */ +- val |= TSCR_IACK; +- +- pluto_write_tscr(pluto, val); +-} +- +-static int __devinit pluto_hw_init(struct pluto *pluto) +-{ +- pluto_reset_frontend(pluto, 1); +- +- /* set automatic LED control by FPGA */ +- pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); +- +- /* set data endianess */ +-#ifdef __LITTLE_ENDIAN +- pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); +-#else +- pluto_rw(pluto, REG_PIDn(0), PID0_END, 0); +-#endif +- /* map DMA and set address */ +- pluto_dma_map(pluto); +- pluto_set_dma_addr(pluto); +- +- /* enable interrupts */ +- pluto_enable_irqs(pluto); +- +- /* reset TS logic */ +- pluto_reset_ts(pluto, 1); +- +- return 0; +-} +- +-static void pluto_hw_exit(struct pluto *pluto) +-{ +- /* disable interrupts */ +- pluto_disable_irqs(pluto); +- +- pluto_reset_ts(pluto, 0); +- +- /* LED: disable automatic control, enable yellow, disable green */ +- pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1); +- +- /* unmap DMA */ +- pluto_dma_unmap(pluto); +- +- pluto_reset_frontend(pluto, 0); +-} +- +-static inline u32 divide(u32 numerator, u32 denominator) +-{ +- if (denominator == 0) +- return ~0; +- +- return DIV_ROUND_CLOSEST(numerator, denominator); +-} +- +-/* LG Innotek TDTE-E001P (Infineon TUA6034) */ +-static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct pluto *pluto = frontend_to_pluto(fe); +- struct i2c_msg msg; +- int ret; +- u8 buf[4]; +- u32 div; +- +- // Fref = 166.667 Hz +- // Fref * 3 = 500.000 Hz +- // IF = 36166667 +- // IF / Fref = 217 +- //div = divide(p->frequency + 36166667, 166667); +- div = divide(p->frequency * 3, 500000) + 217; +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = (div >> 0) & 0xff; +- +- if (p->frequency < 611000000) +- buf[2] = 0xb4; +- else if (p->frequency < 811000000) +- buf[2] = 0xbc; +- else +- buf[2] = 0xf4; +- +- // VHF: 174-230 MHz +- // center: 350 MHz +- // UHF: 470-862 MHz +- if (p->frequency < 350000000) +- buf[3] = 0x02; +- else +- buf[3] = 0x04; +- +- if (p->bandwidth_hz == 8000000) +- buf[3] |= 0x08; +- +- if (sizeof(buf) == 6) { +- buf[4] = buf[2]; +- buf[4] &= ~0x1c; +- buf[4] |= 0x18; +- +- buf[5] = (0 << 7) | (2 << 4); +- } +- +- msg.addr = I2C_ADDR_TUA6034 >> 1; +- msg.flags = 0; +- msg.buf = buf; +- msg.len = sizeof(buf); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); +- if (ret < 0) +- return ret; +- else if (ret == 0) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int pluto2_request_firmware(struct dvb_frontend *fe, +- const struct firmware **fw, char *name) +-{ +- struct pluto *pluto = frontend_to_pluto(fe); +- +- return request_firmware(fw, name, &pluto->pdev->dev); +-} +- +-static struct tda1004x_config pluto2_fe_config __devinitdata = { +- .demod_address = I2C_ADDR_TDA10046 >> 1, +- .invert = 1, +- .invert_oclk = 0, +- .xtal_freq = TDA10046_XTAL_16M, +- .agc_config = TDA10046_AGC_DEFAULT, +- .if_freq = TDA10046_FREQ_3617, +- .request_firmware = pluto2_request_firmware, +-}; +- +-static int __devinit frontend_init(struct pluto *pluto) +-{ +- int ret; +- +- pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap); +- if (!pluto->fe) { +- dev_err(&pluto->pdev->dev, "could not attach frontend\n"); +- return -ENODEV; +- } +- pluto->fe->ops.tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; +- +- ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); +- if (ret < 0) { +- if (pluto->fe->ops.release) +- pluto->fe->ops.release(pluto->fe); +- return ret; +- } +- +- return 0; +-} +- +-static void __devinit pluto_read_rev(struct pluto *pluto) +-{ +- u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR; +- dev_info(&pluto->pdev->dev, "board revision %d.%d\n", +- (val >> 12) & 0x0f, (val >> 4) & 0xff); +-} +- +-static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac) +-{ +- u32 val = pluto_readreg(pluto, REG_MMAC); +- mac[0] = (val >> 8) & 0xff; +- mac[1] = (val >> 0) & 0xff; +- +- val = pluto_readreg(pluto, REG_IMAC); +- mac[2] = (val >> 8) & 0xff; +- mac[3] = (val >> 0) & 0xff; +- +- val = pluto_readreg(pluto, REG_LMAC); +- mac[4] = (val >> 8) & 0xff; +- mac[5] = (val >> 0) & 0xff; +- +- dev_info(&pluto->pdev->dev, "MAC %pM\n", mac); +-} +- +-static int __devinit pluto_read_serial(struct pluto *pluto) +-{ +- struct pci_dev *pdev = pluto->pdev; +- unsigned int i, j; +- u8 __iomem *cis; +- +- cis = pci_iomap(pdev, 1, 0); +- if (!cis) +- return -EIO; +- +- dev_info(&pdev->dev, "S/N "); +- +- for (i = 0xe0; i < 0x100; i += 4) { +- u32 val = readl(&cis[i]); +- for (j = 0; j < 32; j += 8) { +- if ((val & 0xff) == 0xff) +- goto out; +- printk("%c", val & 0xff); +- val >>= 8; +- } +- } +-out: +- printk("\n"); +- pci_iounmap(pdev, cis); +- +- return 0; +-} +- +-static int __devinit pluto2_probe(struct pci_dev *pdev, +- const struct pci_device_id *ent) +-{ +- struct pluto *pluto; +- struct dvb_adapter *dvb_adapter; +- struct dvb_demux *dvbdemux; +- struct dmx_demux *dmx; +- int ret = -ENOMEM; +- +- pluto = kzalloc(sizeof(struct pluto), GFP_KERNEL); +- if (!pluto) +- goto out; +- +- pluto->pdev = pdev; +- +- ret = pci_enable_device(pdev); +- if (ret < 0) +- goto err_kfree; +- +- /* enable interrupts */ +- pci_write_config_dword(pdev, 0x6c, 0x8000); +- +- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); +- if (ret < 0) +- goto err_pci_disable_device; +- +- pci_set_master(pdev); +- +- ret = pci_request_regions(pdev, DRIVER_NAME); +- if (ret < 0) +- goto err_pci_disable_device; +- +- pluto->io_mem = pci_iomap(pdev, 0, 0x40); +- if (!pluto->io_mem) { +- ret = -EIO; +- goto err_pci_release_regions; +- } +- +- pci_set_drvdata(pdev, pluto); +- +- ret = request_irq(pdev->irq, pluto_irq, IRQF_SHARED, DRIVER_NAME, pluto); +- if (ret < 0) +- goto err_pci_iounmap; +- +- ret = pluto_hw_init(pluto); +- if (ret < 0) +- goto err_free_irq; +- +- /* i2c */ +- i2c_set_adapdata(&pluto->i2c_adap, pluto); +- strcpy(pluto->i2c_adap.name, DRIVER_NAME); +- pluto->i2c_adap.owner = THIS_MODULE; +- pluto->i2c_adap.dev.parent = &pdev->dev; +- pluto->i2c_adap.algo_data = &pluto->i2c_bit; +- pluto->i2c_bit.data = pluto; +- pluto->i2c_bit.setsda = pluto_setsda; +- pluto->i2c_bit.setscl = pluto_setscl; +- pluto->i2c_bit.getsda = pluto_getsda; +- pluto->i2c_bit.getscl = pluto_getscl; +- pluto->i2c_bit.udelay = 10; +- pluto->i2c_bit.timeout = 10; +- +- /* Raise SCL and SDA */ +- pluto_setsda(pluto, 1); +- pluto_setscl(pluto, 1); +- +- ret = i2c_bit_add_bus(&pluto->i2c_adap); +- if (ret < 0) +- goto err_pluto_hw_exit; +- +- /* dvb */ +- ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, +- THIS_MODULE, &pdev->dev, adapter_nr); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- dvb_adapter = &pluto->dvb_adapter; +- +- pluto_read_rev(pluto); +- pluto_read_serial(pluto); +- pluto_read_mac(pluto, dvb_adapter->proposed_mac); +- +- dvbdemux = &pluto->demux; +- dvbdemux->filternum = 256; +- dvbdemux->feednum = 256; +- dvbdemux->start_feed = pluto_start_feed; +- dvbdemux->stop_feed = pluto_stop_feed; +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | +- DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); +- ret = dvb_dmx_init(dvbdemux); +- if (ret < 0) +- goto err_dvb_unregister_adapter; +- +- dmx = &dvbdemux->dmx; +- +- pluto->hw_frontend.source = DMX_FRONTEND_0; +- pluto->mem_frontend.source = DMX_MEMORY_FE; +- pluto->dmxdev.filternum = NHWFILTERS; +- pluto->dmxdev.demux = dmx; +- +- ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter); +- if (ret < 0) +- goto err_dvb_dmx_release; +- +- ret = dmx->add_frontend(dmx, &pluto->hw_frontend); +- if (ret < 0) +- goto err_dvb_dmxdev_release; +- +- ret = dmx->add_frontend(dmx, &pluto->mem_frontend); +- if (ret < 0) +- goto err_remove_hw_frontend; +- +- ret = dmx->connect_frontend(dmx, &pluto->hw_frontend); +- if (ret < 0) +- goto err_remove_mem_frontend; +- +- ret = frontend_init(pluto); +- if (ret < 0) +- goto err_disconnect_frontend; +- +- dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx); +-out: +- return ret; +- +-err_disconnect_frontend: +- dmx->disconnect_frontend(dmx); +-err_remove_mem_frontend: +- dmx->remove_frontend(dmx, &pluto->mem_frontend); +-err_remove_hw_frontend: +- dmx->remove_frontend(dmx, &pluto->hw_frontend); +-err_dvb_dmxdev_release: +- dvb_dmxdev_release(&pluto->dmxdev); +-err_dvb_dmx_release: +- dvb_dmx_release(dvbdemux); +-err_dvb_unregister_adapter: +- dvb_unregister_adapter(dvb_adapter); +-err_i2c_del_adapter: +- i2c_del_adapter(&pluto->i2c_adap); +-err_pluto_hw_exit: +- pluto_hw_exit(pluto); +-err_free_irq: +- free_irq(pdev->irq, pluto); +-err_pci_iounmap: +- pci_iounmap(pdev, pluto->io_mem); +-err_pci_release_regions: +- pci_release_regions(pdev); +-err_pci_disable_device: +- pci_disable_device(pdev); +-err_kfree: +- pci_set_drvdata(pdev, NULL); +- kfree(pluto); +- goto out; +-} +- +-static void __devexit pluto2_remove(struct pci_dev *pdev) +-{ +- struct pluto *pluto = pci_get_drvdata(pdev); +- struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter; +- struct dvb_demux *dvbdemux = &pluto->demux; +- struct dmx_demux *dmx = &dvbdemux->dmx; +- +- dmx->close(dmx); +- dvb_net_release(&pluto->dvbnet); +- if (pluto->fe) +- dvb_unregister_frontend(pluto->fe); +- +- dmx->disconnect_frontend(dmx); +- dmx->remove_frontend(dmx, &pluto->mem_frontend); +- dmx->remove_frontend(dmx, &pluto->hw_frontend); +- dvb_dmxdev_release(&pluto->dmxdev); +- dvb_dmx_release(dvbdemux); +- dvb_unregister_adapter(dvb_adapter); +- i2c_del_adapter(&pluto->i2c_adap); +- pluto_hw_exit(pluto); +- free_irq(pdev->irq, pluto); +- pci_iounmap(pdev, pluto->io_mem); +- pci_release_regions(pdev); +- pci_disable_device(pdev); +- pci_set_drvdata(pdev, NULL); +- kfree(pluto); +-} +- +-#ifndef PCI_VENDOR_ID_SCM +-#define PCI_VENDOR_ID_SCM 0x0432 +-#endif +-#ifndef PCI_DEVICE_ID_PLUTO2 +-#define PCI_DEVICE_ID_PLUTO2 0x0001 +-#endif +- +-static struct pci_device_id pluto2_id_table[] __devinitdata = { +- { +- .vendor = PCI_VENDOR_ID_SCM, +- .device = PCI_DEVICE_ID_PLUTO2, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- }, { +- /* empty */ +- }, +-}; +- +-MODULE_DEVICE_TABLE(pci, pluto2_id_table); +- +-static struct pci_driver pluto2_driver = { +- .name = DRIVER_NAME, +- .id_table = pluto2_id_table, +- .probe = pluto2_probe, +- .remove = __devexit_p(pluto2_remove), +-}; +- +-static int __init pluto2_init(void) +-{ +- return pci_register_driver(&pluto2_driver); +-} +- +-static void __exit pluto2_exit(void) +-{ +- pci_unregister_driver(&pluto2_driver); +-} +- +-module_init(pluto2_init); +-module_exit(pluto2_exit); +- +-MODULE_AUTHOR("Andreas Oberritter "); +-MODULE_DESCRIPTION("Pluto2 driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/pt1/Kconfig b/drivers/media/dvb/pt1/Kconfig +deleted file mode 100644 +index 24501d5..0000000 +--- a/drivers/media/dvb/pt1/Kconfig ++++ /dev/null +@@ -1,12 +0,0 @@ +-config DVB_PT1 +- tristate "PT1 cards" +- depends on DVB_CORE && PCI && I2C +- help +- Support for Earthsoft PT1 PCI cards. +- +- Since these cards have no MPEG decoder onboard, they transmit +- only compressed MPEG data over the PCI bus, so you need +- an external software decoder to watch TV on your computer. +- +- Say Y or M if you own such a device and want to use it. +- +diff --git a/drivers/media/dvb/pt1/Makefile b/drivers/media/dvb/pt1/Makefile +deleted file mode 100644 +index d80d8e8..0000000 +--- a/drivers/media/dvb/pt1/Makefile ++++ /dev/null +@@ -1,5 +0,0 @@ +-earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o +- +-obj-$(CONFIG_DVB_PT1) += earth-pt1.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core -Idrivers/media/dvb/frontends +diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c +deleted file mode 100644 +index b81df5f..0000000 +--- a/drivers/media/dvb/pt1/pt1.c ++++ /dev/null +@@ -1,1209 +0,0 @@ +-/* +- * driver for Earthsoft PT1/PT2 +- * +- * Copyright (C) 2009 HIRANO Takahito +- * +- * based on pt1dvr - http://pt1dvr.sourceforge.jp/ +- * by Tomoaki Ishikawa +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dmxdev.h" +-#include "dvb_net.h" +-#include "dvb_frontend.h" +- +-#include "va1j5jf8007t.h" +-#include "va1j5jf8007s.h" +- +-#define DRIVER_NAME "earth-pt1" +- +-#define PT1_PAGE_SHIFT 12 +-#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) +-#define PT1_NR_UPACKETS 1024 +-#define PT1_NR_BUFS 511 +- +-struct pt1_buffer_page { +- __le32 upackets[PT1_NR_UPACKETS]; +-}; +- +-struct pt1_table_page { +- __le32 next_pfn; +- __le32 buf_pfns[PT1_NR_BUFS]; +-}; +- +-struct pt1_buffer { +- struct pt1_buffer_page *page; +- dma_addr_t addr; +-}; +- +-struct pt1_table { +- struct pt1_table_page *page; +- dma_addr_t addr; +- struct pt1_buffer bufs[PT1_NR_BUFS]; +-}; +- +-#define PT1_NR_ADAPS 4 +- +-struct pt1_adapter; +- +-struct pt1 { +- struct pci_dev *pdev; +- void __iomem *regs; +- struct i2c_adapter i2c_adap; +- int i2c_running; +- struct pt1_adapter *adaps[PT1_NR_ADAPS]; +- struct pt1_table *tables; +- struct task_struct *kthread; +- +- struct mutex lock; +- int power; +- int reset; +-}; +- +-struct pt1_adapter { +- struct pt1 *pt1; +- int index; +- +- u8 *buf; +- int upacket_count; +- int packet_count; +- +- struct dvb_adapter adap; +- struct dvb_demux demux; +- int users; +- struct dmxdev dmxdev; +- struct dvb_net net; +- struct dvb_frontend *fe; +- int (*orig_set_voltage)(struct dvb_frontend *fe, +- fe_sec_voltage_t voltage); +- int (*orig_sleep)(struct dvb_frontend *fe); +- int (*orig_init)(struct dvb_frontend *fe); +- +- fe_sec_voltage_t voltage; +- int sleep; +-}; +- +-#define pt1_printk(level, pt1, format, arg...) \ +- dev_printk(level, &(pt1)->pdev->dev, format, ##arg) +- +-static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) +-{ +- writel(data, pt1->regs + reg * 4); +-} +- +-static u32 pt1_read_reg(struct pt1 *pt1, int reg) +-{ +- return readl(pt1->regs + reg * 4); +-} +- +-static int pt1_nr_tables = 64; +-module_param_named(nr_tables, pt1_nr_tables, int, 0); +- +-static void pt1_increment_table_count(struct pt1 *pt1) +-{ +- pt1_write_reg(pt1, 0, 0x00000020); +-} +- +-static void pt1_init_table_count(struct pt1 *pt1) +-{ +- pt1_write_reg(pt1, 0, 0x00000010); +-} +- +-static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) +-{ +- pt1_write_reg(pt1, 5, first_pfn); +- pt1_write_reg(pt1, 0, 0x0c000040); +-} +- +-static void pt1_unregister_tables(struct pt1 *pt1) +-{ +- pt1_write_reg(pt1, 0, 0x08080000); +-} +- +-static int pt1_sync(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < 57; i++) { +- if (pt1_read_reg(pt1, 0) & 0x20000000) +- return 0; +- pt1_write_reg(pt1, 0, 0x00000008); +- } +- pt1_printk(KERN_ERR, pt1, "could not sync\n"); +- return -EIO; +-} +- +-static u64 pt1_identify(struct pt1 *pt1) +-{ +- int i; +- u64 id; +- id = 0; +- for (i = 0; i < 57; i++) { +- id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; +- pt1_write_reg(pt1, 0, 0x00000008); +- } +- return id; +-} +- +-static int pt1_unlock(struct pt1 *pt1) +-{ +- int i; +- pt1_write_reg(pt1, 0, 0x00000008); +- for (i = 0; i < 3; i++) { +- if (pt1_read_reg(pt1, 0) & 0x80000000) +- return 0; +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- } +- pt1_printk(KERN_ERR, pt1, "could not unlock\n"); +- return -EIO; +-} +- +-static int pt1_reset_pci(struct pt1 *pt1) +-{ +- int i; +- pt1_write_reg(pt1, 0, 0x01010000); +- pt1_write_reg(pt1, 0, 0x01000000); +- for (i = 0; i < 10; i++) { +- if (pt1_read_reg(pt1, 0) & 0x00000001) +- return 0; +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- } +- pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); +- return -EIO; +-} +- +-static int pt1_reset_ram(struct pt1 *pt1) +-{ +- int i; +- pt1_write_reg(pt1, 0, 0x02020000); +- pt1_write_reg(pt1, 0, 0x02000000); +- for (i = 0; i < 10; i++) { +- if (pt1_read_reg(pt1, 0) & 0x00000002) +- return 0; +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- } +- pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); +- return -EIO; +-} +- +-static int pt1_do_enable_ram(struct pt1 *pt1) +-{ +- int i, j; +- u32 status; +- status = pt1_read_reg(pt1, 0) & 0x00000004; +- pt1_write_reg(pt1, 0, 0x00000002); +- for (i = 0; i < 10; i++) { +- for (j = 0; j < 1024; j++) { +- if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) +- return 0; +- } +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- } +- pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); +- return -EIO; +-} +- +-static int pt1_enable_ram(struct pt1 *pt1) +-{ +- int i, ret; +- int phase; +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- phase = pt1->pdev->device == 0x211a ? 128 : 166; +- for (i = 0; i < phase; i++) { +- ret = pt1_do_enable_ram(pt1); +- if (ret < 0) +- return ret; +- } +- return 0; +-} +- +-static void pt1_disable_ram(struct pt1 *pt1) +-{ +- pt1_write_reg(pt1, 0, 0x0b0b0000); +-} +- +-static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) +-{ +- pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); +-} +- +-static void pt1_init_streams(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < PT1_NR_ADAPS; i++) +- pt1_set_stream(pt1, i, 0); +-} +- +-static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) +-{ +- u32 upacket; +- int i; +- int index; +- struct pt1_adapter *adap; +- int offset; +- u8 *buf; +- +- if (!page->upackets[PT1_NR_UPACKETS - 1]) +- return 0; +- +- for (i = 0; i < PT1_NR_UPACKETS; i++) { +- upacket = le32_to_cpu(page->upackets[i]); +- index = (upacket >> 29) - 1; +- if (index < 0 || index >= PT1_NR_ADAPS) +- continue; +- +- adap = pt1->adaps[index]; +- if (upacket >> 25 & 1) +- adap->upacket_count = 0; +- else if (!adap->upacket_count) +- continue; +- +- buf = adap->buf; +- offset = adap->packet_count * 188 + adap->upacket_count * 3; +- buf[offset] = upacket >> 16; +- buf[offset + 1] = upacket >> 8; +- if (adap->upacket_count != 62) +- buf[offset + 2] = upacket; +- +- if (++adap->upacket_count >= 63) { +- adap->upacket_count = 0; +- if (++adap->packet_count >= 21) { +- dvb_dmx_swfilter_packets(&adap->demux, buf, 21); +- adap->packet_count = 0; +- } +- } +- } +- +- page->upackets[PT1_NR_UPACKETS - 1] = 0; +- return 1; +-} +- +-static int pt1_thread(void *data) +-{ +- struct pt1 *pt1; +- int table_index; +- int buf_index; +- struct pt1_buffer_page *page; +- +- pt1 = data; +- set_freezable(); +- +- table_index = 0; +- buf_index = 0; +- +- while (!kthread_should_stop()) { +- try_to_freeze(); +- +- page = pt1->tables[table_index].bufs[buf_index].page; +- if (!pt1_filter(pt1, page)) { +- schedule_timeout_interruptible((HZ + 999) / 1000); +- continue; +- } +- +- if (++buf_index >= PT1_NR_BUFS) { +- pt1_increment_table_count(pt1); +- buf_index = 0; +- if (++table_index >= pt1_nr_tables) +- table_index = 0; +- } +- } +- +- return 0; +-} +- +-static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) +-{ +- dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); +-} +- +-static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) +-{ +- void *page; +- dma_addr_t addr; +- +- page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, +- GFP_KERNEL); +- if (page == NULL) +- return NULL; +- +- BUG_ON(addr & (PT1_PAGE_SIZE - 1)); +- BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); +- +- *addrp = addr; +- *pfnp = addr >> PT1_PAGE_SHIFT; +- return page; +-} +- +-static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) +-{ +- pt1_free_page(pt1, buf->page, buf->addr); +-} +- +-static int +-pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) +-{ +- struct pt1_buffer_page *page; +- dma_addr_t addr; +- +- page = pt1_alloc_page(pt1, &addr, pfnp); +- if (page == NULL) +- return -ENOMEM; +- +- page->upackets[PT1_NR_UPACKETS - 1] = 0; +- +- buf->page = page; +- buf->addr = addr; +- return 0; +-} +- +-static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) +-{ +- int i; +- +- for (i = 0; i < PT1_NR_BUFS; i++) +- pt1_cleanup_buffer(pt1, &table->bufs[i]); +- +- pt1_free_page(pt1, table->page, table->addr); +-} +- +-static int +-pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) +-{ +- struct pt1_table_page *page; +- dma_addr_t addr; +- int i, ret; +- u32 buf_pfn; +- +- page = pt1_alloc_page(pt1, &addr, pfnp); +- if (page == NULL) +- return -ENOMEM; +- +- for (i = 0; i < PT1_NR_BUFS; i++) { +- ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); +- if (ret < 0) +- goto err; +- +- page->buf_pfns[i] = cpu_to_le32(buf_pfn); +- } +- +- pt1_increment_table_count(pt1); +- table->page = page; +- table->addr = addr; +- return 0; +- +-err: +- while (i--) +- pt1_cleanup_buffer(pt1, &table->bufs[i]); +- +- pt1_free_page(pt1, page, addr); +- return ret; +-} +- +-static void pt1_cleanup_tables(struct pt1 *pt1) +-{ +- struct pt1_table *tables; +- int i; +- +- tables = pt1->tables; +- pt1_unregister_tables(pt1); +- +- for (i = 0; i < pt1_nr_tables; i++) +- pt1_cleanup_table(pt1, &tables[i]); +- +- vfree(tables); +-} +- +-static int pt1_init_tables(struct pt1 *pt1) +-{ +- struct pt1_table *tables; +- int i, ret; +- u32 first_pfn, pfn; +- +- tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); +- if (tables == NULL) +- return -ENOMEM; +- +- pt1_init_table_count(pt1); +- +- i = 0; +- if (pt1_nr_tables) { +- ret = pt1_init_table(pt1, &tables[0], &first_pfn); +- if (ret) +- goto err; +- i++; +- } +- +- while (i < pt1_nr_tables) { +- ret = pt1_init_table(pt1, &tables[i], &pfn); +- if (ret) +- goto err; +- tables[i - 1].page->next_pfn = cpu_to_le32(pfn); +- i++; +- } +- +- tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); +- +- pt1_register_tables(pt1, first_pfn); +- pt1->tables = tables; +- return 0; +- +-err: +- while (i--) +- pt1_cleanup_table(pt1, &tables[i]); +- +- vfree(tables); +- return ret; +-} +- +-static int pt1_start_feed(struct dvb_demux_feed *feed) +-{ +- struct pt1_adapter *adap; +- adap = container_of(feed->demux, struct pt1_adapter, demux); +- if (!adap->users++) +- pt1_set_stream(adap->pt1, adap->index, 1); +- return 0; +-} +- +-static int pt1_stop_feed(struct dvb_demux_feed *feed) +-{ +- struct pt1_adapter *adap; +- adap = container_of(feed->demux, struct pt1_adapter, demux); +- if (!--adap->users) +- pt1_set_stream(adap->pt1, adap->index, 0); +- return 0; +-} +- +-static void +-pt1_update_power(struct pt1 *pt1) +-{ +- int bits; +- int i; +- struct pt1_adapter *adap; +- static const int sleep_bits[] = { +- 1 << 4, +- 1 << 6 | 1 << 7, +- 1 << 5, +- 1 << 6 | 1 << 8, +- }; +- +- bits = pt1->power | !pt1->reset << 3; +- mutex_lock(&pt1->lock); +- for (i = 0; i < PT1_NR_ADAPS; i++) { +- adap = pt1->adaps[i]; +- switch (adap->voltage) { +- case SEC_VOLTAGE_13: /* actually 11V */ +- bits |= 1 << 1; +- break; +- case SEC_VOLTAGE_18: /* actually 15V */ +- bits |= 1 << 1 | 1 << 2; +- break; +- default: +- break; +- } +- +- /* XXX: The bits should be changed depending on adap->sleep. */ +- bits |= sleep_bits[i]; +- } +- pt1_write_reg(pt1, 1, bits); +- mutex_unlock(&pt1->lock); +-} +- +-static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +-{ +- struct pt1_adapter *adap; +- +- adap = container_of(fe->dvb, struct pt1_adapter, adap); +- adap->voltage = voltage; +- pt1_update_power(adap->pt1); +- +- if (adap->orig_set_voltage) +- return adap->orig_set_voltage(fe, voltage); +- else +- return 0; +-} +- +-static int pt1_sleep(struct dvb_frontend *fe) +-{ +- struct pt1_adapter *adap; +- +- adap = container_of(fe->dvb, struct pt1_adapter, adap); +- adap->sleep = 1; +- pt1_update_power(adap->pt1); +- +- if (adap->orig_sleep) +- return adap->orig_sleep(fe); +- else +- return 0; +-} +- +-static int pt1_wakeup(struct dvb_frontend *fe) +-{ +- struct pt1_adapter *adap; +- +- adap = container_of(fe->dvb, struct pt1_adapter, adap); +- adap->sleep = 0; +- pt1_update_power(adap->pt1); +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- +- if (adap->orig_init) +- return adap->orig_init(fe); +- else +- return 0; +-} +- +-static void pt1_free_adapter(struct pt1_adapter *adap) +-{ +- dvb_net_release(&adap->net); +- adap->demux.dmx.close(&adap->demux.dmx); +- dvb_dmxdev_release(&adap->dmxdev); +- dvb_dmx_release(&adap->demux); +- dvb_unregister_adapter(&adap->adap); +- free_page((unsigned long)adap->buf); +- kfree(adap); +-} +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static struct pt1_adapter * +-pt1_alloc_adapter(struct pt1 *pt1) +-{ +- struct pt1_adapter *adap; +- void *buf; +- struct dvb_adapter *dvb_adap; +- struct dvb_demux *demux; +- struct dmxdev *dmxdev; +- int ret; +- +- adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); +- if (!adap) { +- ret = -ENOMEM; +- goto err; +- } +- +- adap->pt1 = pt1; +- +- adap->voltage = SEC_VOLTAGE_OFF; +- adap->sleep = 1; +- +- buf = (u8 *)__get_free_page(GFP_KERNEL); +- if (!buf) { +- ret = -ENOMEM; +- goto err_kfree; +- } +- +- adap->buf = buf; +- adap->upacket_count = 0; +- adap->packet_count = 0; +- +- dvb_adap = &adap->adap; +- dvb_adap->priv = adap; +- ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, +- &pt1->pdev->dev, adapter_nr); +- if (ret < 0) +- goto err_free_page; +- +- demux = &adap->demux; +- demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; +- demux->priv = adap; +- demux->feednum = 256; +- demux->filternum = 256; +- demux->start_feed = pt1_start_feed; +- demux->stop_feed = pt1_stop_feed; +- demux->write_to_decoder = NULL; +- ret = dvb_dmx_init(demux); +- if (ret < 0) +- goto err_unregister_adapter; +- +- dmxdev = &adap->dmxdev; +- dmxdev->filternum = 256; +- dmxdev->demux = &demux->dmx; +- dmxdev->capabilities = 0; +- ret = dvb_dmxdev_init(dmxdev, dvb_adap); +- if (ret < 0) +- goto err_dmx_release; +- +- dvb_net_init(dvb_adap, &adap->net, &demux->dmx); +- +- return adap; +- +-err_dmx_release: +- dvb_dmx_release(demux); +-err_unregister_adapter: +- dvb_unregister_adapter(dvb_adap); +-err_free_page: +- free_page((unsigned long)buf); +-err_kfree: +- kfree(adap); +-err: +- return ERR_PTR(ret); +-} +- +-static void pt1_cleanup_adapters(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < PT1_NR_ADAPS; i++) +- pt1_free_adapter(pt1->adaps[i]); +-} +- +-static int pt1_init_adapters(struct pt1 *pt1) +-{ +- int i; +- struct pt1_adapter *adap; +- int ret; +- +- for (i = 0; i < PT1_NR_ADAPS; i++) { +- adap = pt1_alloc_adapter(pt1); +- if (IS_ERR(adap)) { +- ret = PTR_ERR(adap); +- goto err; +- } +- +- adap->index = i; +- pt1->adaps[i] = adap; +- } +- return 0; +- +-err: +- while (i--) +- pt1_free_adapter(pt1->adaps[i]); +- +- return ret; +-} +- +-static void pt1_cleanup_frontend(struct pt1_adapter *adap) +-{ +- dvb_unregister_frontend(adap->fe); +-} +- +-static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) +-{ +- int ret; +- +- adap->orig_set_voltage = fe->ops.set_voltage; +- adap->orig_sleep = fe->ops.sleep; +- adap->orig_init = fe->ops.init; +- fe->ops.set_voltage = pt1_set_voltage; +- fe->ops.sleep = pt1_sleep; +- fe->ops.init = pt1_wakeup; +- +- ret = dvb_register_frontend(&adap->adap, fe); +- if (ret < 0) +- return ret; +- +- adap->fe = fe; +- return 0; +-} +- +-static void pt1_cleanup_frontends(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < PT1_NR_ADAPS; i++) +- pt1_cleanup_frontend(pt1->adaps[i]); +-} +- +-struct pt1_config { +- struct va1j5jf8007s_config va1j5jf8007s_config; +- struct va1j5jf8007t_config va1j5jf8007t_config; +-}; +- +-static const struct pt1_config pt1_configs[2] = { +- { +- { +- .demod_address = 0x1b, +- .frequency = VA1J5JF8007S_20MHZ, +- }, +- { +- .demod_address = 0x1a, +- .frequency = VA1J5JF8007T_20MHZ, +- }, +- }, { +- { +- .demod_address = 0x19, +- .frequency = VA1J5JF8007S_20MHZ, +- }, +- { +- .demod_address = 0x18, +- .frequency = VA1J5JF8007T_20MHZ, +- }, +- }, +-}; +- +-static const struct pt1_config pt2_configs[2] = { +- { +- { +- .demod_address = 0x1b, +- .frequency = VA1J5JF8007S_25MHZ, +- }, +- { +- .demod_address = 0x1a, +- .frequency = VA1J5JF8007T_25MHZ, +- }, +- }, { +- { +- .demod_address = 0x19, +- .frequency = VA1J5JF8007S_25MHZ, +- }, +- { +- .demod_address = 0x18, +- .frequency = VA1J5JF8007T_25MHZ, +- }, +- }, +-}; +- +-static int pt1_init_frontends(struct pt1 *pt1) +-{ +- int i, j; +- struct i2c_adapter *i2c_adap; +- const struct pt1_config *configs, *config; +- struct dvb_frontend *fe[4]; +- int ret; +- +- i = 0; +- j = 0; +- +- i2c_adap = &pt1->i2c_adap; +- configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; +- do { +- config = &configs[i / 2]; +- +- fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, +- i2c_adap); +- if (!fe[i]) { +- ret = -ENODEV; /* This does not sound nice... */ +- goto err; +- } +- i++; +- +- fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, +- i2c_adap); +- if (!fe[i]) { +- ret = -ENODEV; +- goto err; +- } +- i++; +- +- ret = va1j5jf8007s_prepare(fe[i - 2]); +- if (ret < 0) +- goto err; +- +- ret = va1j5jf8007t_prepare(fe[i - 1]); +- if (ret < 0) +- goto err; +- +- } while (i < 4); +- +- do { +- ret = pt1_init_frontend(pt1->adaps[j], fe[j]); +- if (ret < 0) +- goto err; +- } while (++j < 4); +- +- return 0; +- +-err: +- while (i-- > j) +- fe[i]->ops.release(fe[i]); +- +- while (j--) +- dvb_unregister_frontend(fe[j]); +- +- return ret; +-} +- +-static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, +- int clock, int data, int next_addr) +-{ +- pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | +- !clock << 11 | !data << 10 | next_addr); +-} +- +-static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) +-{ +- pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); +- pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); +- pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); +- *addrp = addr + 3; +-} +- +-static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) +-{ +- pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); +- pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); +- pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); +- pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); +- *addrp = addr + 4; +-} +- +-static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) +-{ +- int i; +- for (i = 0; i < 8; i++) +- pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); +- pt1_i2c_write_bit(pt1, addr, &addr, 1); +- *addrp = addr; +-} +- +-static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) +-{ +- int i; +- for (i = 0; i < 8; i++) +- pt1_i2c_read_bit(pt1, addr, &addr); +- pt1_i2c_write_bit(pt1, addr, &addr, last); +- *addrp = addr; +-} +- +-static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) +-{ +- pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); +- pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); +- pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); +- *addrp = addr + 3; +-} +- +-static void +-pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +-{ +- int i; +- pt1_i2c_prepare(pt1, addr, &addr); +- pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); +- for (i = 0; i < msg->len; i++) +- pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); +- *addrp = addr; +-} +- +-static void +-pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) +-{ +- int i; +- pt1_i2c_prepare(pt1, addr, &addr); +- pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); +- for (i = 0; i < msg->len; i++) +- pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); +- *addrp = addr; +-} +- +-static int pt1_i2c_end(struct pt1 *pt1, int addr) +-{ +- pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); +- pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); +- pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); +- +- pt1_write_reg(pt1, 0, 0x00000004); +- do { +- if (signal_pending(current)) +- return -EINTR; +- schedule_timeout_interruptible((HZ + 999) / 1000); +- } while (pt1_read_reg(pt1, 0) & 0x00000080); +- return 0; +-} +- +-static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) +-{ +- int addr; +- addr = 0; +- +- pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); +- addr = addr + 1; +- +- if (!pt1->i2c_running) { +- pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); +- pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); +- addr = addr + 2; +- pt1->i2c_running = 1; +- } +- *addrp = addr; +-} +- +-static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +-{ +- struct pt1 *pt1; +- int i; +- struct i2c_msg *msg, *next_msg; +- int addr, ret; +- u16 len; +- u32 word; +- +- pt1 = i2c_get_adapdata(adap); +- +- for (i = 0; i < num; i++) { +- msg = &msgs[i]; +- if (msg->flags & I2C_M_RD) +- return -ENOTSUPP; +- +- if (i + 1 < num) +- next_msg = &msgs[i + 1]; +- else +- next_msg = NULL; +- +- if (next_msg && next_msg->flags & I2C_M_RD) { +- i++; +- +- len = next_msg->len; +- if (len > 4) +- return -ENOTSUPP; +- +- pt1_i2c_begin(pt1, &addr); +- pt1_i2c_write_msg(pt1, addr, &addr, msg); +- pt1_i2c_read_msg(pt1, addr, &addr, next_msg); +- ret = pt1_i2c_end(pt1, addr); +- if (ret < 0) +- return ret; +- +- word = pt1_read_reg(pt1, 2); +- while (len--) { +- next_msg->buf[len] = word; +- word >>= 8; +- } +- } else { +- pt1_i2c_begin(pt1, &addr); +- pt1_i2c_write_msg(pt1, addr, &addr, msg); +- ret = pt1_i2c_end(pt1, addr); +- if (ret < 0) +- return ret; +- } +- } +- +- return num; +-} +- +-static u32 pt1_i2c_func(struct i2c_adapter *adap) +-{ +- return I2C_FUNC_I2C; +-} +- +-static const struct i2c_algorithm pt1_i2c_algo = { +- .master_xfer = pt1_i2c_xfer, +- .functionality = pt1_i2c_func, +-}; +- +-static void pt1_i2c_wait(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < 128; i++) +- pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); +-} +- +-static void pt1_i2c_init(struct pt1 *pt1) +-{ +- int i; +- for (i = 0; i < 1024; i++) +- pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); +-} +- +-static void __devexit pt1_remove(struct pci_dev *pdev) +-{ +- struct pt1 *pt1; +- void __iomem *regs; +- +- pt1 = pci_get_drvdata(pdev); +- regs = pt1->regs; +- +- kthread_stop(pt1->kthread); +- pt1_cleanup_tables(pt1); +- pt1_cleanup_frontends(pt1); +- pt1_disable_ram(pt1); +- pt1->power = 0; +- pt1->reset = 1; +- pt1_update_power(pt1); +- pt1_cleanup_adapters(pt1); +- i2c_del_adapter(&pt1->i2c_adap); +- pci_set_drvdata(pdev, NULL); +- kfree(pt1); +- pci_iounmap(pdev, regs); +- pci_release_regions(pdev); +- pci_disable_device(pdev); +-} +- +-static int __devinit +-pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +-{ +- int ret; +- void __iomem *regs; +- struct pt1 *pt1; +- struct i2c_adapter *i2c_adap; +- struct task_struct *kthread; +- +- ret = pci_enable_device(pdev); +- if (ret < 0) +- goto err; +- +- ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); +- if (ret < 0) +- goto err_pci_disable_device; +- +- pci_set_master(pdev); +- +- ret = pci_request_regions(pdev, DRIVER_NAME); +- if (ret < 0) +- goto err_pci_disable_device; +- +- regs = pci_iomap(pdev, 0, 0); +- if (!regs) { +- ret = -EIO; +- goto err_pci_release_regions; +- } +- +- pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); +- if (!pt1) { +- ret = -ENOMEM; +- goto err_pci_iounmap; +- } +- +- mutex_init(&pt1->lock); +- pt1->pdev = pdev; +- pt1->regs = regs; +- pci_set_drvdata(pdev, pt1); +- +- ret = pt1_init_adapters(pt1); +- if (ret < 0) +- goto err_kfree; +- +- mutex_init(&pt1->lock); +- +- pt1->power = 0; +- pt1->reset = 1; +- pt1_update_power(pt1); +- +- i2c_adap = &pt1->i2c_adap; +- i2c_adap->algo = &pt1_i2c_algo; +- i2c_adap->algo_data = NULL; +- i2c_adap->dev.parent = &pdev->dev; +- strcpy(i2c_adap->name, DRIVER_NAME); +- i2c_set_adapdata(i2c_adap, pt1); +- ret = i2c_add_adapter(i2c_adap); +- if (ret < 0) +- goto err_pt1_cleanup_adapters; +- +- pt1_i2c_init(pt1); +- pt1_i2c_wait(pt1); +- +- ret = pt1_sync(pt1); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- pt1_identify(pt1); +- +- ret = pt1_unlock(pt1); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- ret = pt1_reset_pci(pt1); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- ret = pt1_reset_ram(pt1); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- ret = pt1_enable_ram(pt1); +- if (ret < 0) +- goto err_i2c_del_adapter; +- +- pt1_init_streams(pt1); +- +- pt1->power = 1; +- pt1_update_power(pt1); +- schedule_timeout_uninterruptible((HZ + 49) / 50); +- +- pt1->reset = 0; +- pt1_update_power(pt1); +- schedule_timeout_uninterruptible((HZ + 999) / 1000); +- +- ret = pt1_init_frontends(pt1); +- if (ret < 0) +- goto err_pt1_disable_ram; +- +- ret = pt1_init_tables(pt1); +- if (ret < 0) +- goto err_pt1_cleanup_frontends; +- +- kthread = kthread_run(pt1_thread, pt1, "pt1"); +- if (IS_ERR(kthread)) { +- ret = PTR_ERR(kthread); +- goto err_pt1_cleanup_tables; +- } +- +- pt1->kthread = kthread; +- return 0; +- +-err_pt1_cleanup_tables: +- pt1_cleanup_tables(pt1); +-err_pt1_cleanup_frontends: +- pt1_cleanup_frontends(pt1); +-err_pt1_disable_ram: +- pt1_disable_ram(pt1); +- pt1->power = 0; +- pt1->reset = 1; +- pt1_update_power(pt1); +-err_i2c_del_adapter: +- i2c_del_adapter(i2c_adap); +-err_pt1_cleanup_adapters: +- pt1_cleanup_adapters(pt1); +-err_kfree: +- pci_set_drvdata(pdev, NULL); +- kfree(pt1); +-err_pci_iounmap: +- pci_iounmap(pdev, regs); +-err_pci_release_regions: +- pci_release_regions(pdev); +-err_pci_disable_device: +- pci_disable_device(pdev); +-err: +- return ret; +- +-} +- +-static struct pci_device_id pt1_id_table[] = { +- { PCI_DEVICE(0x10ee, 0x211a) }, +- { PCI_DEVICE(0x10ee, 0x222a) }, +- { }, +-}; +-MODULE_DEVICE_TABLE(pci, pt1_id_table); +- +-static struct pci_driver pt1_driver = { +- .name = DRIVER_NAME, +- .probe = pt1_probe, +- .remove = __devexit_p(pt1_remove), +- .id_table = pt1_id_table, +-}; +- +- +-static int __init pt1_init(void) +-{ +- return pci_register_driver(&pt1_driver); +-} +- +- +-static void __exit pt1_cleanup(void) +-{ +- pci_unregister_driver(&pt1_driver); +-} +- +-module_init(pt1_init); +-module_exit(pt1_cleanup); +- +-MODULE_AUTHOR("Takahito HIRANO "); +-MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c +deleted file mode 100644 +index d980dfb..0000000 +--- a/drivers/media/dvb/pt1/va1j5jf8007s.c ++++ /dev/null +@@ -1,735 +0,0 @@ +-/* +- * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 +- * +- * Copyright (C) 2009 HIRANO Takahito +- * +- * based on pt1dvr - http://pt1dvr.sourceforge.jp/ +- * by Tomoaki Ishikawa +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "va1j5jf8007s.h" +- +-enum va1j5jf8007s_tune_state { +- VA1J5JF8007S_IDLE, +- VA1J5JF8007S_SET_FREQUENCY_1, +- VA1J5JF8007S_SET_FREQUENCY_2, +- VA1J5JF8007S_SET_FREQUENCY_3, +- VA1J5JF8007S_CHECK_FREQUENCY, +- VA1J5JF8007S_SET_MODULATION, +- VA1J5JF8007S_CHECK_MODULATION, +- VA1J5JF8007S_SET_TS_ID, +- VA1J5JF8007S_CHECK_TS_ID, +- VA1J5JF8007S_TRACK, +-}; +- +-struct va1j5jf8007s_state { +- const struct va1j5jf8007s_config *config; +- struct i2c_adapter *adap; +- struct dvb_frontend fe; +- enum va1j5jf8007s_tune_state tune_state; +-}; +- +-static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct va1j5jf8007s_state *state; +- u8 addr; +- int i; +- u8 write_buf[1], read_buf[1]; +- struct i2c_msg msgs[2]; +- s32 word, x1, x2, x3, x4, x5, y; +- +- state = fe->demodulator_priv; +- addr = state->config->demod_address; +- +- word = 0; +- for (i = 0; i < 2; i++) { +- write_buf[0] = 0xbc + i; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- word <<= 8; +- word |= read_buf[0]; +- } +- +- word -= 3000; +- if (word < 0) +- word = 0; +- +- x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); +- x2 = (s64)x1 * x1 >> 31; +- x3 = (s64)x2 * x1 >> 31; +- x4 = (s64)x2 * x2 >> 31; +- x5 = (s64)x4 * x1 >> 31; +- +- y = (58857ll << 23) / 1000; +- y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; +- y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; +- y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; +- y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; +- y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; +- +- *snr = y < 0 ? 0 : y >> 15; +- return 0; +-} +- +-static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_HW; +-} +- +-static int +-va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct va1j5jf8007s_state *state; +- +- state = fe->demodulator_priv; +- +- switch (state->tune_state) { +- case VA1J5JF8007S_IDLE: +- case VA1J5JF8007S_SET_FREQUENCY_1: +- case VA1J5JF8007S_SET_FREQUENCY_2: +- case VA1J5JF8007S_SET_FREQUENCY_3: +- case VA1J5JF8007S_CHECK_FREQUENCY: +- *status = 0; +- return 0; +- +- +- case VA1J5JF8007S_SET_MODULATION: +- case VA1J5JF8007S_CHECK_MODULATION: +- *status |= FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007S_SET_TS_ID: +- case VA1J5JF8007S_CHECK_TS_ID: +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; +- return 0; +- +- case VA1J5JF8007S_TRACK: +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +- } +- +- BUG(); +-} +- +-struct va1j5jf8007s_cb_map { +- u32 frequency; +- u8 cb; +-}; +- +-static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { +- { 986000, 0xb2 }, +- { 1072000, 0xd2 }, +- { 1154000, 0xe2 }, +- { 1291000, 0x20 }, +- { 1447000, 0x40 }, +- { 1615000, 0x60 }, +- { 1791000, 0x80 }, +- { 1972000, 0xa0 }, +-}; +- +-static u8 va1j5jf8007s_lookup_cb(u32 frequency) +-{ +- int i; +- const struct va1j5jf8007s_cb_map *map; +- +- for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { +- map = &va1j5jf8007s_cb_maps[i]; +- if (frequency < map->frequency) +- return map->cb; +- } +- return 0xc0; +-} +- +-static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) +-{ +- u32 frequency; +- u16 word; +- u8 buf[6]; +- struct i2c_msg msg; +- +- frequency = state->fe.dtv_property_cache.frequency; +- +- word = (frequency + 500) / 1000; +- if (frequency < 1072000) +- word = (word << 1 & ~0x1f) | (word & 0x0f); +- +- buf[0] = 0xfe; +- buf[1] = 0xc0; +- buf[2] = 0x40 | word >> 8; +- buf[3] = word; +- buf[4] = 0xe0; +- buf[5] = va1j5jf8007s_lookup_cb(frequency); +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) +-{ +- u8 buf[3]; +- struct i2c_msg msg; +- +- buf[0] = 0xfe; +- buf[1] = 0xc0; +- buf[2] = 0xe4; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) +-{ +- u32 frequency; +- u8 buf[4]; +- struct i2c_msg msg; +- +- frequency = state->fe.dtv_property_cache.frequency; +- +- buf[0] = 0xfe; +- buf[1] = 0xc0; +- buf[2] = 0xf4; +- buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int +-va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) +-{ +- u8 addr; +- u8 write_buf[2], read_buf[1]; +- struct i2c_msg msgs[2]; +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0xfe; +- write_buf[1] = 0xc1; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- *lock = read_buf[0] & 0x40; +- return 0; +-} +- +-static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) +-{ +- u8 buf[2]; +- struct i2c_msg msg; +- +- buf[0] = 0x03; +- buf[1] = 0x01; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int +-va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) +-{ +- u8 addr; +- u8 write_buf[1], read_buf[1]; +- struct i2c_msg msgs[2]; +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0xc3; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- *lock = !(read_buf[0] & 0x10); +- return 0; +-} +- +-static int +-va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) +-{ +- u32 ts_id; +- u8 buf[3]; +- struct i2c_msg msg; +- +- ts_id = state->fe.dtv_property_cache.isdbs_ts_id; +- if (!ts_id) +- return 0; +- +- buf[0] = 0x8f; +- buf[1] = ts_id >> 8; +- buf[2] = ts_id; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int +-va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) +-{ +- u8 addr; +- u8 write_buf[1], read_buf[2]; +- struct i2c_msg msgs[2]; +- u32 ts_id; +- +- ts_id = state->fe.dtv_property_cache.isdbs_ts_id; +- if (!ts_id) { +- *lock = 1; +- return 0; +- } +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0xe6; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; +- return 0; +-} +- +-static int +-va1j5jf8007s_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, unsigned int *delay, +- fe_status_t *status) +-{ +- struct va1j5jf8007s_state *state; +- int ret; +- int lock = 0; +- +- state = fe->demodulator_priv; +- +- if (re_tune) +- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; +- +- switch (state->tune_state) { +- case VA1J5JF8007S_IDLE: +- *delay = 3 * HZ; +- *status = 0; +- return 0; +- +- case VA1J5JF8007S_SET_FREQUENCY_1: +- ret = va1j5jf8007s_set_frequency_1(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; +- *delay = 0; +- *status = 0; +- return 0; +- +- case VA1J5JF8007S_SET_FREQUENCY_2: +- ret = va1j5jf8007s_set_frequency_2(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; +- *delay = (HZ + 99) / 100; +- *status = 0; +- return 0; +- +- case VA1J5JF8007S_SET_FREQUENCY_3: +- ret = va1j5jf8007s_set_frequency_3(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; +- *delay = 0; +- *status = 0; +- return 0; +- +- case VA1J5JF8007S_CHECK_FREQUENCY: +- ret = va1j5jf8007s_check_frequency(state, &lock); +- if (ret < 0) +- return ret; +- +- if (!lock) { +- *delay = (HZ + 999) / 1000; +- *status = 0; +- return 0; +- } +- +- state->tune_state = VA1J5JF8007S_SET_MODULATION; +- *delay = 0; +- *status = FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007S_SET_MODULATION: +- ret = va1j5jf8007s_set_modulation(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007S_CHECK_MODULATION; +- *delay = 0; +- *status = FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007S_CHECK_MODULATION: +- ret = va1j5jf8007s_check_modulation(state, &lock); +- if (ret < 0) +- return ret; +- +- if (!lock) { +- *delay = (HZ + 49) / 50; +- *status = FE_HAS_SIGNAL; +- return 0; +- } +- +- state->tune_state = VA1J5JF8007S_SET_TS_ID; +- *delay = 0; +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; +- return 0; +- +- case VA1J5JF8007S_SET_TS_ID: +- ret = va1j5jf8007s_set_ts_id(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007S_CHECK_TS_ID; +- return 0; +- +- case VA1J5JF8007S_CHECK_TS_ID: +- ret = va1j5jf8007s_check_ts_id(state, &lock); +- if (ret < 0) +- return ret; +- +- if (!lock) { +- *delay = (HZ + 99) / 100; +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; +- return 0; +- } +- +- state->tune_state = VA1J5JF8007S_TRACK; +- /* fall through */ +- +- case VA1J5JF8007S_TRACK: +- *delay = 3 * HZ; +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +- } +- +- BUG(); +-} +- +-static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) +-{ +- u8 buf[4]; +- struct i2c_msg msg; +- +- buf[0] = 0xfe; +- buf[1] = 0xc0; +- buf[2] = 0xf0; +- buf[3] = 0x04; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) +-{ +- u8 buf[2]; +- struct i2c_msg msg; +- +- buf[0] = 0x17; +- buf[1] = sleep ? 0x01 : 0x00; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007s_sleep(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007s_state *state; +- int ret; +- +- state = fe->demodulator_priv; +- +- ret = va1j5jf8007s_init_frequency(state); +- if (ret < 0) +- return ret; +- +- return va1j5jf8007s_set_sleep(state, 1); +-} +- +-static int va1j5jf8007s_init(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007s_state *state; +- +- state = fe->demodulator_priv; +- state->tune_state = VA1J5JF8007S_IDLE; +- +- return va1j5jf8007s_set_sleep(state, 0); +-} +- +-static void va1j5jf8007s_release(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007s_state *state; +- state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops va1j5jf8007s_ops = { +- .delsys = { SYS_ISDBS }, +- .info = { +- .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 1000, +- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, +- }, +- +- .read_snr = va1j5jf8007s_read_snr, +- .get_frontend_algo = va1j5jf8007s_get_frontend_algo, +- .read_status = va1j5jf8007s_read_status, +- .tune = va1j5jf8007s_tune, +- .sleep = va1j5jf8007s_sleep, +- .init = va1j5jf8007s_init, +- .release = va1j5jf8007s_release, +-}; +- +-static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) +-{ +- u8 addr; +- u8 write_buf[1], read_buf[1]; +- struct i2c_msg msgs[2]; +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0x07; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- if (read_buf[0] != 0x41) +- return -EIO; +- +- return 0; +-} +- +-static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { +- {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, +- {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, +- {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, +- {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, +-}; +- +-static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { +- {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, +- {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, +- {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, +- {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, +-}; +- +-static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) +-{ +- const u8 (*bufs)[2]; +- int size; +- u8 addr; +- u8 buf[2]; +- struct i2c_msg msg; +- int i; +- +- switch (state->config->frequency) { +- case VA1J5JF8007S_20MHZ: +- bufs = va1j5jf8007s_20mhz_prepare_bufs; +- size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); +- break; +- case VA1J5JF8007S_25MHZ: +- bufs = va1j5jf8007s_25mhz_prepare_bufs; +- size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); +- break; +- default: +- return -EINVAL; +- } +- +- addr = state->config->demod_address; +- +- msg.addr = addr; +- msg.flags = 0; +- msg.len = 2; +- msg.buf = buf; +- for (i = 0; i < size; i++) { +- memcpy(buf, bufs[i], sizeof(buf)); +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- } +- +- return 0; +-} +- +-/* must be called after va1j5jf8007t_attach */ +-int va1j5jf8007s_prepare(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007s_state *state; +- int ret; +- +- state = fe->demodulator_priv; +- +- ret = va1j5jf8007s_prepare_1(state); +- if (ret < 0) +- return ret; +- +- ret = va1j5jf8007s_prepare_2(state); +- if (ret < 0) +- return ret; +- +- return va1j5jf8007s_init_frequency(state); +-} +- +-struct dvb_frontend * +-va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, +- struct i2c_adapter *adap) +-{ +- struct va1j5jf8007s_state *state; +- struct dvb_frontend *fe; +- u8 buf[2]; +- struct i2c_msg msg; +- +- state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- state->config = config; +- state->adap = adap; +- +- fe = &state->fe; +- memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); +- fe->demodulator_priv = state; +- +- buf[0] = 0x01; +- buf[1] = 0x80; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) { +- kfree(state); +- return NULL; +- } +- +- return fe; +-} +diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.h b/drivers/media/dvb/pt1/va1j5jf8007s.h +deleted file mode 100644 +index b7d6f05..0000000 +--- a/drivers/media/dvb/pt1/va1j5jf8007s.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 +- * +- * Copyright (C) 2009 HIRANO Takahito +- * +- * based on pt1dvr - http://pt1dvr.sourceforge.jp/ +- * by Tomoaki Ishikawa +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef VA1J5JF8007S_H +-#define VA1J5JF8007S_H +- +-enum va1j5jf8007s_frequency { +- VA1J5JF8007S_20MHZ, +- VA1J5JF8007S_25MHZ, +-}; +- +-struct va1j5jf8007s_config { +- u8 demod_address; +- enum va1j5jf8007s_frequency frequency; +-}; +- +-struct i2c_adapter; +- +-struct dvb_frontend * +-va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, +- struct i2c_adapter *adap); +- +-/* must be called after va1j5jf8007t_attach */ +-int va1j5jf8007s_prepare(struct dvb_frontend *fe); +- +-#endif +diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c +deleted file mode 100644 +index 2db1515..0000000 +--- a/drivers/media/dvb/pt1/va1j5jf8007t.c ++++ /dev/null +@@ -1,536 +0,0 @@ +-/* +- * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 +- * +- * Copyright (C) 2009 HIRANO Takahito +- * +- * based on pt1dvr - http://pt1dvr.sourceforge.jp/ +- * by Tomoaki Ishikawa +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#include +-#include +-#include +-#include +-#include "dvb_frontend.h" +-#include "dvb_math.h" +-#include "va1j5jf8007t.h" +- +-enum va1j5jf8007t_tune_state { +- VA1J5JF8007T_IDLE, +- VA1J5JF8007T_SET_FREQUENCY, +- VA1J5JF8007T_CHECK_FREQUENCY, +- VA1J5JF8007T_SET_MODULATION, +- VA1J5JF8007T_CHECK_MODULATION, +- VA1J5JF8007T_TRACK, +- VA1J5JF8007T_ABORT, +-}; +- +-struct va1j5jf8007t_state { +- const struct va1j5jf8007t_config *config; +- struct i2c_adapter *adap; +- struct dvb_frontend fe; +- enum va1j5jf8007t_tune_state tune_state; +-}; +- +-static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- struct va1j5jf8007t_state *state; +- u8 addr; +- int i; +- u8 write_buf[1], read_buf[1]; +- struct i2c_msg msgs[2]; +- s32 word, x, y; +- +- state = fe->demodulator_priv; +- addr = state->config->demod_address; +- +- word = 0; +- for (i = 0; i < 3; i++) { +- write_buf[0] = 0x8b + i; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- word <<= 8; +- word |= read_buf[0]; +- } +- +- if (!word) +- return -EIO; +- +- x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); +- y = (24ll << 46) / 1000000; +- y = ((s64)y * x >> 30) - (16ll << 40) / 10000; +- y = ((s64)y * x >> 29) + (398ll << 35) / 10000; +- y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; +- y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; +- *snr = y >> 15; +- return 0; +-} +- +-static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) +-{ +- return DVBFE_ALGO_HW; +-} +- +-static int +-va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct va1j5jf8007t_state *state; +- +- state = fe->demodulator_priv; +- +- switch (state->tune_state) { +- case VA1J5JF8007T_IDLE: +- case VA1J5JF8007T_SET_FREQUENCY: +- case VA1J5JF8007T_CHECK_FREQUENCY: +- *status = 0; +- return 0; +- +- +- case VA1J5JF8007T_SET_MODULATION: +- case VA1J5JF8007T_CHECK_MODULATION: +- case VA1J5JF8007T_ABORT: +- *status |= FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007T_TRACK: +- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +- } +- +- BUG(); +-} +- +-struct va1j5jf8007t_cb_map { +- u32 frequency; +- u8 cb; +-}; +- +-static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { +- { 90000000, 0x80 }, +- { 140000000, 0x81 }, +- { 170000000, 0xa1 }, +- { 220000000, 0x62 }, +- { 330000000, 0xa2 }, +- { 402000000, 0xe2 }, +- { 450000000, 0x64 }, +- { 550000000, 0x84 }, +- { 600000000, 0xa4 }, +- { 700000000, 0xc4 }, +-}; +- +-static u8 va1j5jf8007t_lookup_cb(u32 frequency) +-{ +- int i; +- const struct va1j5jf8007t_cb_map *map; +- +- for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { +- map = &va1j5jf8007t_cb_maps[i]; +- if (frequency < map->frequency) +- return map->cb; +- } +- return 0xe4; +-} +- +-static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) +-{ +- u32 frequency; +- u16 word; +- u8 buf[6]; +- struct i2c_msg msg; +- +- frequency = state->fe.dtv_property_cache.frequency; +- +- word = (frequency + 71428) / 142857 + 399; +- buf[0] = 0xfe; +- buf[1] = 0xc2; +- buf[2] = word >> 8; +- buf[3] = word; +- buf[4] = 0x80; +- buf[5] = va1j5jf8007t_lookup_cb(frequency); +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int +-va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) +-{ +- u8 addr; +- u8 write_buf[2], read_buf[1]; +- struct i2c_msg msgs[2]; +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0xfe; +- write_buf[1] = 0xc3; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- *lock = read_buf[0] & 0x40; +- return 0; +-} +- +-static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) +-{ +- u8 buf[2]; +- struct i2c_msg msg; +- +- buf[0] = 0x01; +- buf[1] = 0x40; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, +- int *lock, int *retry) +-{ +- u8 addr; +- u8 write_buf[1], read_buf[1]; +- struct i2c_msg msgs[2]; +- +- addr = state->config->demod_address; +- +- write_buf[0] = 0x80; +- +- msgs[0].addr = addr; +- msgs[0].flags = 0; +- msgs[0].len = sizeof(write_buf); +- msgs[0].buf = write_buf; +- +- msgs[1].addr = addr; +- msgs[1].flags = I2C_M_RD; +- msgs[1].len = sizeof(read_buf); +- msgs[1].buf = read_buf; +- +- if (i2c_transfer(state->adap, msgs, 2) != 2) +- return -EREMOTEIO; +- +- *lock = !(read_buf[0] & 0x10); +- *retry = read_buf[0] & 0x80; +- return 0; +-} +- +-static int +-va1j5jf8007t_tune(struct dvb_frontend *fe, +- bool re_tune, +- unsigned int mode_flags, unsigned int *delay, +- fe_status_t *status) +-{ +- struct va1j5jf8007t_state *state; +- int ret; +- int lock = 0, retry = 0; +- +- state = fe->demodulator_priv; +- +- if (re_tune) +- state->tune_state = VA1J5JF8007T_SET_FREQUENCY; +- +- switch (state->tune_state) { +- case VA1J5JF8007T_IDLE: +- *delay = 3 * HZ; +- *status = 0; +- return 0; +- +- case VA1J5JF8007T_SET_FREQUENCY: +- ret = va1j5jf8007t_set_frequency(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; +- *delay = 0; +- *status = 0; +- return 0; +- +- case VA1J5JF8007T_CHECK_FREQUENCY: +- ret = va1j5jf8007t_check_frequency(state, &lock); +- if (ret < 0) +- return ret; +- +- if (!lock) { +- *delay = (HZ + 999) / 1000; +- *status = 0; +- return 0; +- } +- +- state->tune_state = VA1J5JF8007T_SET_MODULATION; +- *delay = 0; +- *status = FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007T_SET_MODULATION: +- ret = va1j5jf8007t_set_modulation(state); +- if (ret < 0) +- return ret; +- +- state->tune_state = VA1J5JF8007T_CHECK_MODULATION; +- *delay = 0; +- *status = FE_HAS_SIGNAL; +- return 0; +- +- case VA1J5JF8007T_CHECK_MODULATION: +- ret = va1j5jf8007t_check_modulation(state, &lock, &retry); +- if (ret < 0) +- return ret; +- +- if (!lock) { +- if (!retry) { +- state->tune_state = VA1J5JF8007T_ABORT; +- *delay = 3 * HZ; +- *status = FE_HAS_SIGNAL; +- return 0; +- } +- *delay = (HZ + 999) / 1000; +- *status = FE_HAS_SIGNAL; +- return 0; +- } +- +- state->tune_state = VA1J5JF8007T_TRACK; +- /* fall through */ +- +- case VA1J5JF8007T_TRACK: +- *delay = 3 * HZ; +- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +- +- case VA1J5JF8007T_ABORT: +- *delay = 3 * HZ; +- *status = FE_HAS_SIGNAL; +- return 0; +- } +- +- BUG(); +-} +- +-static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) +-{ +- u8 buf[7]; +- struct i2c_msg msg; +- +- buf[0] = 0xfe; +- buf[1] = 0xc2; +- buf[2] = 0x01; +- buf[3] = 0x8f; +- buf[4] = 0xc1; +- buf[5] = 0x80; +- buf[6] = 0x80; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) +-{ +- u8 buf[2]; +- struct i2c_msg msg; +- +- buf[0] = 0x03; +- buf[1] = sleep ? 0x90 : 0x80; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- +- return 0; +-} +- +-static int va1j5jf8007t_sleep(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007t_state *state; +- int ret; +- +- state = fe->demodulator_priv; +- +- ret = va1j5jf8007t_init_frequency(state); +- if (ret < 0) +- return ret; +- +- return va1j5jf8007t_set_sleep(state, 1); +-} +- +-static int va1j5jf8007t_init(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007t_state *state; +- +- state = fe->demodulator_priv; +- state->tune_state = VA1J5JF8007T_IDLE; +- +- return va1j5jf8007t_set_sleep(state, 0); +-} +- +-static void va1j5jf8007t_release(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007t_state *state; +- state = fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops va1j5jf8007t_ops = { +- .delsys = { SYS_ISDBT }, +- .info = { +- .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", +- .frequency_min = 90000000, +- .frequency_max = 770000000, +- .frequency_stepsize = 142857, +- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | +- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, +- }, +- +- .read_snr = va1j5jf8007t_read_snr, +- .get_frontend_algo = va1j5jf8007t_get_frontend_algo, +- .read_status = va1j5jf8007t_read_status, +- .tune = va1j5jf8007t_tune, +- .sleep = va1j5jf8007t_sleep, +- .init = va1j5jf8007t_init, +- .release = va1j5jf8007t_release, +-}; +- +-static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { +- {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, +- {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, +- {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, +- {0xef, 0x01} +-}; +- +-static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { +- {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, +- {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, +- {0x77, 0x03}, {0xef, 0x01} +-}; +- +-int va1j5jf8007t_prepare(struct dvb_frontend *fe) +-{ +- struct va1j5jf8007t_state *state; +- const u8 (*bufs)[2]; +- int size; +- u8 buf[2]; +- struct i2c_msg msg; +- int i; +- +- state = fe->demodulator_priv; +- +- switch (state->config->frequency) { +- case VA1J5JF8007T_20MHZ: +- bufs = va1j5jf8007t_20mhz_prepare_bufs; +- size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); +- break; +- case VA1J5JF8007T_25MHZ: +- bufs = va1j5jf8007t_25mhz_prepare_bufs; +- size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); +- break; +- default: +- return -EINVAL; +- } +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- for (i = 0; i < size; i++) { +- memcpy(buf, bufs[i], sizeof(buf)); +- if (i2c_transfer(state->adap, &msg, 1) != 1) +- return -EREMOTEIO; +- } +- +- return va1j5jf8007t_init_frequency(state); +-} +- +-struct dvb_frontend * +-va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, +- struct i2c_adapter *adap) +-{ +- struct va1j5jf8007t_state *state; +- struct dvb_frontend *fe; +- u8 buf[2]; +- struct i2c_msg msg; +- +- state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); +- if (!state) +- return NULL; +- +- state->config = config; +- state->adap = adap; +- +- fe = &state->fe; +- memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); +- fe->demodulator_priv = state; +- +- buf[0] = 0x01; +- buf[1] = 0x80; +- +- msg.addr = state->config->demod_address; +- msg.flags = 0; +- msg.len = sizeof(buf); +- msg.buf = buf; +- +- if (i2c_transfer(state->adap, &msg, 1) != 1) { +- kfree(state); +- return NULL; +- } +- +- return fe; +-} +diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.h b/drivers/media/dvb/pt1/va1j5jf8007t.h +deleted file mode 100644 +index 2903be5..0000000 +--- a/drivers/media/dvb/pt1/va1j5jf8007t.h ++++ /dev/null +@@ -1,46 +0,0 @@ +-/* +- * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 +- * +- * Copyright (C) 2009 HIRANO Takahito +- * +- * based on pt1dvr - http://pt1dvr.sourceforge.jp/ +- * by Tomoaki Ishikawa +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- */ +- +-#ifndef VA1J5JF8007T_H +-#define VA1J5JF8007T_H +- +-enum va1j5jf8007t_frequency { +- VA1J5JF8007T_20MHZ, +- VA1J5JF8007T_25MHZ, +-}; +- +-struct va1j5jf8007t_config { +- u8 demod_address; +- enum va1j5jf8007t_frequency frequency; +-}; +- +-struct i2c_adapter; +- +-struct dvb_frontend * +-va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, +- struct i2c_adapter *adap); +- +-/* must be called after va1j5jf8007s_attach */ +-int va1j5jf8007t_prepare(struct dvb_frontend *fe); +- +-#endif +diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig +deleted file mode 100644 +index bc6456e..0000000 +--- a/drivers/media/dvb/siano/Kconfig ++++ /dev/null +@@ -1,34 +0,0 @@ +-# +-# Siano Mobile Silicon Digital TV device configuration +-# +- +-config SMS_SIANO_MDTV +- tristate "Siano SMS1xxx based MDTV receiver" +- depends on DVB_CORE && RC_CORE && HAS_DMA +- ---help--- +- Choose Y or M here if you have MDTV receiver with a Siano chipset. +- +- To compile this driver as a module, choose M here +- (The module will be called smsmdtv). +- +- Further documentation on this driver can be found on the WWW +- at http://www.siano-ms.com/ +- +-if SMS_SIANO_MDTV +-menu "Siano module components" +- +-# Hardware interfaces support +- +-config SMS_USB_DRV +- tristate "USB interface support" +- depends on DVB_CORE && USB +- ---help--- +- Choose if you would like to have Siano's support for USB interface +- +-config SMS_SDIO_DRV +- tristate "SDIO interface support" +- depends on DVB_CORE && MMC +- ---help--- +- Choose if you would like to have Siano's support for SDIO interface +-endmenu +-endif # SMS_SIANO_MDTV +diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile +deleted file mode 100644 +index f233b57..0000000 +--- a/drivers/media/dvb/siano/Makefile ++++ /dev/null +@@ -1,11 +0,0 @@ +- +-smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o +- +-obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o +-obj-$(CONFIG_SMS_USB_DRV) += smsusb.o +-obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core +- +-ccflags-y += $(extra-cflags-y) $(extra-cflags-m) +- +diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c +deleted file mode 100644 +index 680c781..0000000 +--- a/drivers/media/dvb/siano/sms-cards.c ++++ /dev/null +@@ -1,311 +0,0 @@ +-/* +- * Card-specific functions for the Siano SMS1xxx USB dongle +- * +- * Copyright (c) 2008 Michael Krufky +- * +- * 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 +- +-static int sms_dbg; +-module_param_named(cards_dbg, sms_dbg, int, 0644); +-MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))"); +- +-static struct sms_board sms_boards[] = { +- [SMS_BOARD_UNKNOWN] = { +- .name = "Unknown board", +- }, +- [SMS1XXX_BOARD_SIANO_STELLAR] = { +- .name = "Siano Stellar Digital Receiver", +- .type = SMS_STELLAR, +- }, +- [SMS1XXX_BOARD_SIANO_NOVA_A] = { +- .name = "Siano Nova A Digital Receiver", +- .type = SMS_NOVA_A0, +- }, +- [SMS1XXX_BOARD_SIANO_NOVA_B] = { +- .name = "Siano Nova B Digital Receiver", +- .type = SMS_NOVA_B0, +- }, +- [SMS1XXX_BOARD_SIANO_VEGA] = { +- .name = "Siano Vega Digital Receiver", +- .type = SMS_VEGA, +- }, +- [SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT] = { +- .name = "Hauppauge Catamount", +- .type = SMS_STELLAR, +- .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-stellar-dvbt-01.fw", +- }, +- [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A] = { +- .name = "Hauppauge Okemo-A", +- .type = SMS_NOVA_A0, +- .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-a-dvbt-01.fw", +- }, +- [SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B] = { +- .name = "Hauppauge Okemo-B", +- .type = SMS_NOVA_B0, +- .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-nova-b-dvbt-01.fw", +- }, +- [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { +- .name = "Hauppauge WinTV MiniStick", +- .type = SMS_NOVA_B0, +- .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", +- .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", +- .rc_codes = RC_MAP_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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", +- .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] = "sms1xxx-hcw-55xxx-dvbt-02.fw", +- .lna_ctrl = -1, +- }, +- [SMS1XXX_BOARD_SIANO_NICE] = { +- /* 11 */ +- .name = "Siano Nice Digital Receiver", +- .type = SMS_NOVA_B0, +- }, +- [SMS1XXX_BOARD_SIANO_VENICE] = { +- /* 12 */ +- .name = "Siano Venice Digital Receiver", +- .type = SMS_VEGA, +- }, +-}; +- +-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_gpio_config *pGpioConfig) { +- pGpioConfig->Direction = SMS_GPIO_DIRECTION_OUTPUT; +- pGpioConfig->InputCharacteristics = +- SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL; +- pGpioConfig->OutputDriving = SMS_GPIO_OUTPUT_DRIVING_4mA; +- pGpioConfig->OutputSlewRate = SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS; +- pGpioConfig->PullUpDown = SMS_GPIO_PULL_UP_DOWN_NONE; +-} +- +-int sms_board_event(struct smscore_device_t *coredev, +- enum SMS_BOARD_EVENTS gevent) { +- struct smscore_gpio_config MyGpioConfig; +- +- sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); +- +- 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: +- sms_err("Unknown SMS board event"); +- 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_OUTPUTSLEWRATE_FAST, +- .outputdriving = SMS_GPIO_OUTPUTDRIVING_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); +- +- sms_debug("%s: LNA %s", __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) +-{ +- switch (id) { +- case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT: +- case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: +- case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: +- case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: +- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: +- case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: +- request_module("smsdvb"); +- break; +- default: +- /* do nothing */ +- break; +- } +- return 0; +-} +-EXPORT_SYMBOL_GPL(sms_board_load_modules); +diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h +deleted file mode 100644 +index d8cdf75..0000000 +--- a/drivers/media/dvb/siano/sms-cards.h ++++ /dev/null +@@ -1,123 +0,0 @@ +-/* +- * Card-specific functions for the Siano SMS1xxx USB dongle +- * +- * Copyright (c) 2008 Michael Krufky +- * +- * 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 +-#include "smscoreapi.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 +- +-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; +-}; +- +-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/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c +deleted file mode 100644 +index 7331e84..0000000 +--- a/drivers/media/dvb/siano/smscoreapi.c ++++ /dev/null +@@ -1,1644 +0,0 @@ +-/* +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include "smscoreapi.h" +-#include "sms-cards.h" +-#include "smsir.h" +-#include "smsendian.h" +- +-static int sms_dbg; +-module_param_named(debug, sms_dbg, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); +- +-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; +-}; +- +-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 = 4; +- +-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 +- sms_err("failed to create smscore_registry."); +- 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 +- sms_err("No registry found."); +- +- 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 +- sms_err("No registry found."); +- +- return -1; +-} +- +-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 +- sms_err("No registry found."); +-} +- +-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 +- sms_err("No registry found."); +-} +- +- +-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(¬ifyee->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(¬ifyee->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 list_head *next, *first; +- int rc = 0; +- +- /* note: must be called under g_deviceslock */ +- +- first = &g_smscore_notifyees; +- +- for (next = first->next; next != first; next = next->next) { +- rc = ((struct smscore_device_notifyee_t *) next)-> +- 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 = +- kmalloc(sizeof(struct smscore_buffer_t), GFP_KERNEL); +- if (!cb) { +- sms_info("kmalloc(...) failed"); +- 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) +-{ +- struct smscore_device_t *dev; +- u8 *buffer; +- +- dev = kzalloc(sizeof(struct smscore_device_t), GFP_KERNEL); +- if (!dev) { +- sms_info("kzalloc(...) failed"); +- return -ENOMEM; +- } +- +- /* 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->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 = +- smscore_createbuffer(buffer, dev->common_buffer, +- dev->common_buffer_phys); +- if (!cb) { +- smscore_unregister_device(dev); +- return -ENOMEM; +- } +- +- smscore_putbuffer(dev, cb); +- } +- +- sms_info("allocated %d buffers", dev->num_buffers); +- +- dev->mode = DEVICE_MODE_NONE; +- 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; +- +- sms_info("device %p created", 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 = coredev->sendrequest_handler(coredev->context, buffer, size); +- if (rc < 0) { +- sms_info("sendrequest returned error %d", 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 */ +- sms_info("IR loading"); +- rc = sms_ir_init(coredev); +- +- if (rc != 0) +- sms_err("Error initialization DTV IR sub-module"); +- else { +- buffer = kmalloc(sizeof(struct SmsMsgData_ST2) + +- SMS_DMA_ALIGNMENT, +- GFP_KERNEL | GFP_DMA); +- if (buffer) { +- struct SmsMsgData_ST2 *msg = +- (struct SmsMsgData_ST2 *) +- SMS_ALIGN_ADDRESS(buffer); +- +- SMS_INIT_MSG(&msg->xMsgHeader, +- MSG_SMS_START_IR_REQ, +- sizeof(struct SmsMsgData_ST2)); +- msg->msgData[0] = coredev->ir.controller; +- msg->msgData[1] = coredev->ir.timeout; +- +- smsendian_handle_tx_message( +- (struct SmsMsgHdr_ST2 *)msg); +- rc = smscore_sendrequest_and_wait(coredev, msg, +- msg->xMsgHeader. msgLength, +- &coredev->ir_init_done); +- +- kfree(buffer); +- } else +- sms_err +- ("Sending IR initialization message failed"); +- } +- } else +- sms_info("IR port has not been detected"); +- +- 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 = smscore_set_device_mode( +- coredev, smscore_registry_getmode(coredev->devpath)); +- if (rc < 0) { +- sms_info("set device mode faile , rc %d", rc); +- return rc; +- } +- +- kmutex_lock(&g_smscore_deviceslock); +- +- rc = smscore_notify_callbacks(coredev, coredev->device, 1); +- smscore_init_ir(coredev); +- +- sms_info("device %p started, rc %d", 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 SmsFirmware_ST *firmware = (struct SmsFirmware_ST *) buffer; +- struct SmsMsgHdr_ST *msg; +- u32 mem_address; +- u8 *payload = firmware->Payload; +- int rc = 0; +- firmware->StartAddress = le32_to_cpu(firmware->StartAddress); +- firmware->Length = le32_to_cpu(firmware->Length); +- +- mem_address = firmware->StartAddress; +- +- sms_info("loading FW to addr 0x%x size %d", +- 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) { +- sms_debug("sending reload command."); +- SMS_INIT_MSG(msg, MSG_SW_RELOAD_START_REQ, +- sizeof(struct SmsMsgHdr_ST)); +- rc = smscore_sendrequest_and_wait(coredev, msg, +- msg->msgLength, +- &coredev->reload_start_done); +- mem_address = *(u32 *) &payload[20]; +- } +- +- while (size && rc >= 0) { +- struct SmsDataDownload_ST *DataMsg = +- (struct SmsDataDownload_ST *) msg; +- int payload_size = min((int) size, SMS_MAX_PAYLOAD_SIZE); +- +- SMS_INIT_MSG(msg, MSG_SMS_DATA_DOWNLOAD_REQ, +- (u16)(sizeof(struct SmsMsgHdr_ST) + +- sizeof(u32) + payload_size)); +- +- DataMsg->MemAddr = mem_address; +- memcpy(DataMsg->Payload, payload, payload_size); +- +- if ((coredev->device_flags & SMS_ROM_NO_RESPONSE) && +- (coredev->mode == DEVICE_MODE_NONE)) +- rc = coredev->sendrequest_handler( +- coredev->context, DataMsg, +- DataMsg->xMsgHeader.msgLength); +- else +- rc = smscore_sendrequest_and_wait( +- coredev, DataMsg, +- DataMsg->xMsgHeader.msgLength, +- &coredev->data_download_done); +- +- payload += payload_size; +- size -= payload_size; +- mem_address += payload_size; +- } +- +- if (rc >= 0) { +- if (coredev->mode == DEVICE_MODE_NONE) { +- struct SmsMsgData_ST *TriggerMsg = +- (struct SmsMsgData_ST *) msg; +- +- SMS_INIT_MSG(msg, MSG_SMS_SWDOWNLOAD_TRIGGER_REQ, +- sizeof(struct SmsMsgHdr_ST) + +- sizeof(u32) * 5); +- +- TriggerMsg->msgData[0] = firmware->StartAddress; +- /* Entry point */ +- TriggerMsg->msgData[1] = 5; /* Priority */ +- TriggerMsg->msgData[2] = 0x200; /* Stack size */ +- TriggerMsg->msgData[3] = 0; /* Parameter */ +- TriggerMsg->msgData[4] = 4; /* Task ID */ +- +- if (coredev->device_flags & SMS_ROM_NO_RESPONSE) { +- rc = coredev->sendrequest_handler( +- coredev->context, TriggerMsg, +- TriggerMsg->xMsgHeader.msgLength); +- msleep(100); +- } else +- rc = smscore_sendrequest_and_wait( +- coredev, TriggerMsg, +- TriggerMsg->xMsgHeader.msgLength, +- &coredev->trigger_done); +- } else { +- SMS_INIT_MSG(msg, MSG_SW_RELOAD_EXEC_REQ, +- sizeof(struct SmsMsgHdr_ST)); +- +- rc = coredev->sendrequest_handler(coredev->context, +- msg, msg->msgLength); +- } +- msleep(500); +- } +- +- sms_debug("rc=%d, postload=%p ", rc, +- coredev->postload_handler); +- +- kfree(msg); +- +- return ((rc >= 0) && coredev->postload_handler) ? +- coredev->postload_handler(coredev->context) : +- rc; +-} +- +-/** +- * 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, +- char *filename, +- loadfirmware_t loadfirmware_handler) +-{ +- int rc = -ENOENT; +- const struct firmware *fw; +- u8 *fw_buffer; +- +- if (loadfirmware_handler == NULL && !(coredev->device_flags & +- SMS_DEVICE_FAMILY2)) +- return -EINVAL; +- +- rc = request_firmware(&fw, filename, coredev->device); +- if (rc < 0) { +- sms_info("failed to open \"%s\"", filename); +- return rc; +- } +- sms_info("read FW %s, size=%zd", filename, fw->size); +- fw_buffer = kmalloc(ALIGN(fw->size, SMS_ALLOC_ALIGNMENT), +- GFP_KERNEL | GFP_DMA); +- if (fw_buffer) { +- memcpy(fw_buffer, fw->data, fw->size); +- +- rc = (coredev->device_flags & SMS_DEVICE_FAMILY2) ? +- smscore_load_firmware_family2(coredev, +- fw_buffer, +- fw->size) : +- loadfirmware_handler(coredev->context, +- fw_buffer, fw->size); +- +- kfree(fw_buffer); +- } else { +- sms_info("failed to allocate firmware buffer"); +- rc = -ENOMEM; +- } +- +- 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) { +- sms_info("exiting although " +- "not all buffers released."); +- break; +- } +- +- sms_info("waiting for %d buffer(s)", +- coredev->num_buffers - num_buffers); +- msleep(100); +- } +- +- sms_info("freed %d buffers", num_buffers); +- +- if (coredev->common_buffer) +- dma_free_coherent(NULL, coredev->common_buffer_size, +- coredev->common_buffer, coredev->common_buffer_phys); +- +- if (coredev->fw_buf != NULL) +- kfree(coredev->fw_buf); +- +- list_del(&coredev->entry); +- kfree(coredev); +- +- kmutex_unlock(&g_smscore_deviceslock); +- +- sms_info("device %p destroyed", coredev); +-} +-EXPORT_SYMBOL_GPL(smscore_unregister_device); +- +-static int smscore_detect_mode(struct smscore_device_t *coredev) +-{ +- void *buffer = kmalloc(sizeof(struct SmsMsgHdr_ST) + SMS_DMA_ALIGNMENT, +- GFP_KERNEL | GFP_DMA); +- struct SmsMsgHdr_ST *msg = +- (struct SmsMsgHdr_ST *) SMS_ALIGN_ADDRESS(buffer); +- int rc; +- +- if (!buffer) +- return -ENOMEM; +- +- SMS_INIT_MSG(msg, MSG_SMS_GET_VERSION_EX_REQ, +- sizeof(struct SmsMsgHdr_ST)); +- +- rc = smscore_sendrequest_and_wait(coredev, msg, msg->msgLength, +- &coredev->version_ex_done); +- if (rc == -ETIME) { +- sms_err("MSG_SMS_GET_VERSION_EX_REQ failed first try"); +- +- if (wait_for_completion_timeout(&coredev->resume_done, +- msecs_to_jiffies(5000))) { +- rc = smscore_sendrequest_and_wait( +- coredev, msg, msg->msgLength, +- &coredev->version_ex_done); +- if (rc < 0) +- sms_err("MSG_SMS_GET_VERSION_EX_REQ failed " +- "second try, rc %d", rc); +- } else +- rc = -ETIME; +- } +- +- kfree(buffer); +- +- return rc; +-} +- +-static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = { +- /*Stellar NOVA A0 Nova B0 VEGA*/ +- /*DVBT*/ +- {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, +- /*DVBH*/ +- {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, +- /*TDMB*/ +- {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"}, +- /*DABIP*/ +- {"none", "none", "none", "none"}, +- /*BDA*/ +- {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"}, +- /*ISDBT*/ +- {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, +- /*ISDBTBDA*/ +- {"none", "isdbt_nova_12mhz.inp", "isdbt_nova_12mhz_b0.inp", "none"}, +- /*CMMB*/ +- {"none", "none", "none", "cmmb_vega_12mhz.inp"} +-}; +- +-static inline char *sms_get_fw_name(struct smscore_device_t *coredev, +- int mode, enum sms_device_type_st type) +-{ +- char **fw = sms_get_board(smscore_get_board_id(coredev))->fw; +- return (fw && fw[mode]) ? fw[mode] : smscore_fw_lkup[mode][type]; +-} +- +-/** +- * 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) +-{ +- void *buffer; +- int rc = 0; +- enum sms_device_type_st type; +- +- sms_debug("set device mode to %d", mode); +- if (coredev->device_flags & SMS_DEVICE_FAMILY2) { +- if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { +- sms_err("invalid mode specified %d", 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) { +- sms_err("mode detect failed %d", rc); +- return rc; +- } +- } +- +- if (coredev->mode == mode) { +- sms_info("device mode %d already set", mode); +- return 0; +- } +- +- if (!(coredev->modes_supported & (1 << mode))) { +- char *fw_filename; +- +- type = smscore_registry_gettype(coredev->devpath); +- fw_filename = sms_get_fw_name(coredev, mode, type); +- +- rc = smscore_load_firmware_from_file(coredev, +- fw_filename, NULL); +- if (rc < 0) { +- sms_warn("error %d loading firmware: %s, " +- "trying again with default firmware", +- rc, fw_filename); +- +- /* try again with the default firmware */ +- fw_filename = smscore_fw_lkup[mode][type]; +- rc = smscore_load_firmware_from_file(coredev, +- fw_filename, NULL); +- +- if (rc < 0) { +- sms_warn("error %d loading " +- "firmware: %s", rc, +- fw_filename); +- return rc; +- } +- } +- sms_log("firmware download success: %s", fw_filename); +- } else +- sms_info("mode %d supported by running " +- "firmware", mode); +- +- buffer = kmalloc(sizeof(struct SmsMsgData_ST) + +- SMS_DMA_ALIGNMENT, GFP_KERNEL | GFP_DMA); +- if (buffer) { +- struct SmsMsgData_ST *msg = +- (struct SmsMsgData_ST *) +- SMS_ALIGN_ADDRESS(buffer); +- +- SMS_INIT_MSG(&msg->xMsgHeader, MSG_SMS_INIT_DEVICE_REQ, +- sizeof(struct SmsMsgData_ST)); +- msg->msgData[0] = mode; +- +- rc = smscore_sendrequest_and_wait( +- coredev, msg, msg->xMsgHeader.msgLength, +- &coredev->init_device_done); +- +- kfree(buffer); +- } else { +- sms_err("Could not allocate buffer for " +- "init device message."); +- rc = -ENOMEM; +- } +- } else { +- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { +- sms_err("invalid mode specified %d", 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) { +- coredev->mode = mode; +- coredev->device_flags &= ~SMS_DEVICE_NOT_READY; +- } +- +- if (rc < 0) +- sms_err("return error code %d.", rc); +- 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 smscore_client_t *client = NULL; +- struct list_head *next, *first; +- unsigned long flags; +- struct list_head *firstid, *nextid; +- +- +- spin_lock_irqsave(&coredev->clientslock, flags); +- first = &coredev->clients; +- for (next = first->next; +- (next != first) && !client; +- next = next->next) { +- firstid = &((struct smscore_client_t *)next)->idlist; +- for (nextid = firstid->next; +- nextid != firstid; +- nextid = nextid->next) { +- if ((((struct smscore_idlist_t *)nextid)->id == id) && +- (((struct smscore_idlist_t *)nextid)->data_type == data_type || +- (((struct smscore_idlist_t *)nextid)->data_type == 0))) { +- client = (struct smscore_client_t *) next; +- break; +- } +- } +- } +- 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) ((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) { +- sms_debug("\ndata rate %d bytes/secs", +- (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->msgType == MSG_SMS_HO_PER_SLICES_IND) || +- (phdr->msgType == MSG_SMS_TRANSMISSION_IND)) { +- if (coredev->mode == DEVICE_MODE_DVBT_BDA) +- phdr->msgDstId = DVBT_BDA_CONTROL_MSG_ID; +- } +- +- +- client = smscore_find_client(coredev, phdr->msgType, phdr->msgDstId); +- +- /* 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->msgType) { +- case MSG_SMS_GET_VERSION_EX_RES: +- { +- struct SmsVersionRes_ST *ver = +- (struct SmsVersionRes_ST *) phdr; +- sms_debug("MSG_SMS_GET_VERSION_EX_RES " +- "id %d prots 0x%x ver %d.%d", +- ver->FirmwareId, ver->SupportedProtocols, +- ver->RomVersionMajor, ver->RomVersionMinor); +- +- coredev->mode = ver->FirmwareId == 255 ? +- DEVICE_MODE_NONE : ver->FirmwareId; +- coredev->modes_supported = ver->SupportedProtocols; +- +- complete(&coredev->version_ex_done); +- break; +- } +- case MSG_SMS_INIT_DEVICE_RES: +- sms_debug("MSG_SMS_INIT_DEVICE_RES"); +- complete(&coredev->init_device_done); +- break; +- case MSG_SW_RELOAD_START_RES: +- sms_debug("MSG_SW_RELOAD_START_RES"); +- complete(&coredev->reload_start_done); +- break; +- case MSG_SMS_DATA_DOWNLOAD_RES: +- complete(&coredev->data_download_done); +- break; +- case MSG_SW_RELOAD_EXEC_RES: +- sms_debug("MSG_SW_RELOAD_EXEC_RES"); +- break; +- case MSG_SMS_SWDOWNLOAD_TRIGGER_RES: +- sms_debug("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: +- sms_debug("MSG_SMS_GPIO_CONFIG_EX_RES"); +- complete(&coredev->gpio_configuration_done); +- break; +- case MSG_SMS_GPIO_SET_LEVEL_RES: +- sms_debug("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]; +- sms_debug("MSG_SMS_GPIO_GET_LEVEL_RES gpio level %d", +- 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 SmsMsgHdr_ST)), +- (int)phdr->msgLength +- - sizeof(struct SmsMsgHdr_ST)); +- break; +- +- default: +- 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. +- */ +- +-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) { +- sms_err("bad parameter."); +- return -EINVAL; +- } +- registered_client = smscore_find_client(coredev, data_type, id); +- if (registered_client == client) +- return 0; +- +- if (registered_client) { +- sms_err("The msg ID already registered to another client."); +- return -EEXIST; +- } +- listentry = kzalloc(sizeof(struct smscore_idlist_t), GFP_KERNEL); +- if (!listentry) { +- sms_err("Can't allocate memory for client id."); +- 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)) { +- sms_err("Client already exist."); +- return -EEXIST; +- } +- +- newclient = kzalloc(sizeof(struct smscore_client_t), GFP_KERNEL); +- if (!newclient) { +- sms_err("Failed to allocate memory for client."); +- 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; +- sms_debug("%p %d %d", 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); +- } +- +- sms_info("%p", 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 SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) buffer; +- int rc; +- +- if (client == NULL) { +- sms_err("Got NULL client"); +- return -EINVAL; +- } +- +- coredev = client->coredev; +- +- /* check that no other channel with same id exists */ +- if (coredev == NULL) { +- sms_err("Got NULL coredev"); +- return -EINVAL; +- } +- +- rc = smscore_validate_client(client->coredev, client, 0, +- phdr->msgSrcId); +- 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 SmsMsgHdr_ST hdr; +- u32 data[6]; +- } msg; +- +- if (coredev->device_flags & SMS_DEVICE_FAMILY2) { +- msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- msg.hdr.msgDstId = HIF_TASK; +- msg.hdr.msgFlags = 0; +- msg.hdr.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; +- msg.hdr.msgLength = 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_16mA: +- msg.data[3] = 7; /* Nova - 16mA */ +- break; +- case SMS_GPIO_OUTPUTDRIVING_12mA: +- msg.data[3] = 5; /* Nova - 11mA */ +- break; +- case SMS_GPIO_OUTPUTDRIVING_8mA: +- msg.data[3] = 3; /* Nova - 7mA */ +- break; +- case SMS_GPIO_OUTPUTDRIVING_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 SmsMsgHdr_ST hdr; +- u32 data[3]; +- } msg; +- +- if (pin > MAX_GPIO_PIN_NUMBER) +- return -EINVAL; +- +- msg.hdr.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- msg.hdr.msgDstId = HIF_TASK; +- msg.hdr.msgFlags = 0; +- msg.hdr.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; +- msg.hdr.msgLength = 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 GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, +- u32 *pGroupNum, u32 *pGroupCfg) { +- +- *pGroupCfg = 1; +- +- if (PinNum <= 1) { +- *pTranslatedPinNum = 0; +- *pGroupNum = 9; +- *pGroupCfg = 2; +- } else if (PinNum >= 2 && PinNum <= 6) { +- *pTranslatedPinNum = 2; +- *pGroupNum = 0; +- *pGroupCfg = 2; +- } else if (PinNum >= 7 && PinNum <= 11) { +- *pTranslatedPinNum = 7; +- *pGroupNum = 1; +- } else if (PinNum >= 12 && PinNum <= 15) { +- *pTranslatedPinNum = 12; +- *pGroupNum = 2; +- *pGroupCfg = 3; +- } else if (PinNum == 16) { +- *pTranslatedPinNum = 16; +- *pGroupNum = 23; +- } else if (PinNum >= 17 && PinNum <= 24) { +- *pTranslatedPinNum = 17; +- *pGroupNum = 3; +- } else if (PinNum == 25) { +- *pTranslatedPinNum = 25; +- *pGroupNum = 6; +- } else if (PinNum >= 26 && PinNum <= 28) { +- *pTranslatedPinNum = 26; +- *pGroupNum = 4; +- } else if (PinNum == 29) { +- *pTranslatedPinNum = 29; +- *pGroupNum = 5; +- *pGroupCfg = 2; +- } else if (PinNum == 30) { +- *pTranslatedPinNum = 30; +- *pGroupNum = 8; +- } else if (PinNum == 31) { +- *pTranslatedPinNum = 31; +- *pGroupNum = 17; +- } else +- return -1; +- +- *pGroupCfg <<= 24; +- +- return 0; +-} +- +-int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, +- struct smscore_gpio_config *pGpioConfig) { +- +- u32 totalLen; +- u32 TranslatedPinNum = 0; +- u32 GroupNum = 0; +- u32 ElectricChar; +- u32 groupCfg; +- void *buffer; +- int rc; +- +- struct SetGpioMsg { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 msgData[6]; +- } *pMsg; +- +- +- if (PinNum > MAX_GPIO_PIN_NUMBER) +- return -EINVAL; +- +- if (pGpioConfig == NULL) +- return -EINVAL; +- +- totalLen = sizeof(struct SmsMsgHdr_ST) + (sizeof(u32) * 6); +- +- buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, +- GFP_KERNEL | GFP_DMA); +- if (!buffer) +- return -ENOMEM; +- +- pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); +- +- pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- pMsg->xMsgHeader.msgDstId = HIF_TASK; +- pMsg->xMsgHeader.msgFlags = 0; +- pMsg->xMsgHeader.msgLength = (u16) totalLen; +- pMsg->msgData[0] = PinNum; +- +- if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { +- pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; +- if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, +- &groupCfg) != 0) { +- rc = -EINVAL; +- goto free; +- } +- +- pMsg->msgData[1] = TranslatedPinNum; +- pMsg->msgData[2] = GroupNum; +- ElectricChar = (pGpioConfig->PullUpDown) +- | (pGpioConfig->InputCharacteristics << 2) +- | (pGpioConfig->OutputSlewRate << 3) +- | (pGpioConfig->OutputDriving << 4); +- pMsg->msgData[3] = ElectricChar; +- pMsg->msgData[4] = pGpioConfig->Direction; +- pMsg->msgData[5] = groupCfg; +- } else { +- pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_EX_REQ; +- pMsg->msgData[1] = pGpioConfig->PullUpDown; +- pMsg->msgData[2] = pGpioConfig->OutputSlewRate; +- pMsg->msgData[3] = pGpioConfig->OutputDriving; +- pMsg->msgData[4] = pGpioConfig->Direction; +- pMsg->msgData[5] = 0; +- } +- +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); +- rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, +- &coredev->gpio_configuration_done); +- +- if (rc != 0) { +- if (rc == -ETIME) +- sms_err("smscore_gpio_configure timeout"); +- else +- sms_err("smscore_gpio_configure error"); +- } +-free: +- kfree(buffer); +- +- return rc; +-} +- +-int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, +- u8 NewLevel) { +- +- u32 totalLen; +- int rc; +- void *buffer; +- +- struct SetGpioMsg { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 msgData[3]; /* keep it 3 ! */ +- } *pMsg; +- +- if ((NewLevel > 1) || (PinNum > MAX_GPIO_PIN_NUMBER)) +- return -EINVAL; +- +- totalLen = sizeof(struct SmsMsgHdr_ST) + +- (3 * sizeof(u32)); /* keep it 3 ! */ +- +- buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, +- GFP_KERNEL | GFP_DMA); +- if (!buffer) +- return -ENOMEM; +- +- pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); +- +- pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- pMsg->xMsgHeader.msgDstId = HIF_TASK; +- pMsg->xMsgHeader.msgFlags = 0; +- pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_SET_LEVEL_REQ; +- pMsg->xMsgHeader.msgLength = (u16) totalLen; +- pMsg->msgData[0] = PinNum; +- pMsg->msgData[1] = NewLevel; +- +- /* Send message to SMS */ +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); +- rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, +- &coredev->gpio_set_level_done); +- +- if (rc != 0) { +- if (rc == -ETIME) +- sms_err("smscore_gpio_set_level timeout"); +- else +- sms_err("smscore_gpio_set_level error"); +- } +- kfree(buffer); +- +- return rc; +-} +- +-int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, +- u8 *level) { +- +- u32 totalLen; +- int rc; +- void *buffer; +- +- struct SetGpioMsg { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 msgData[2]; +- } *pMsg; +- +- +- if (PinNum > MAX_GPIO_PIN_NUMBER) +- return -EINVAL; +- +- totalLen = sizeof(struct SmsMsgHdr_ST) + (2 * sizeof(u32)); +- +- buffer = kmalloc(totalLen + SMS_DMA_ALIGNMENT, +- GFP_KERNEL | GFP_DMA); +- if (!buffer) +- return -ENOMEM; +- +- pMsg = (struct SetGpioMsg *) SMS_ALIGN_ADDRESS(buffer); +- +- pMsg->xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- pMsg->xMsgHeader.msgDstId = HIF_TASK; +- pMsg->xMsgHeader.msgFlags = 0; +- pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_GET_LEVEL_REQ; +- pMsg->xMsgHeader.msgLength = (u16) totalLen; +- pMsg->msgData[0] = PinNum; +- pMsg->msgData[1] = 0; +- +- /* Send message to SMS */ +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)pMsg); +- rc = smscore_sendrequest_and_wait(coredev, pMsg, totalLen, +- &coredev->gpio_get_level_done); +- +- if (rc != 0) { +- if (rc == -ETIME) +- sms_err("smscore_gpio_get_level timeout"); +- else +- sms_err("smscore_gpio_get_level error"); +- } +- 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) +-{ +- int rc = 0; +- +- 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 rc; +-} +- +-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(¬ifyee->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); +- +- sms_debug(""); +-} +- +-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"); +diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h +deleted file mode 100644 +index c592ae0..0000000 +--- a/drivers/media/dvb/siano/smscoreapi.h ++++ /dev/null +@@ -1,775 +0,0 @@ +-/**************************************************************** +- +-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 . +- +-****************************************************************/ +- +-#ifndef __SMS_CORE_API_H__ +-#define __SMS_CORE_API_H__ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#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_) +- +-#ifndef min +-#define min(a, b) (((a) < (b)) ? (a) : (b)) +-#endif +- +-#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_FAMILY2 1 +-#define SMS_ROM_NO_RESPONSE 2 +-#define SMS_DEVICE_NOT_READY 0x8000000 +- +-enum sms_device_type_st { +- SMS_STELLAR = 0, +- SMS_NOVA_A0, +- SMS_NOVA_B0, +- SMS_VEGA, +- SMS_NUM_OF_DEVICE_TYPES +-}; +- +-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 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; +- +- /* Infrared (IR) */ +- struct ir_t ir; +- +- int led_state; +-}; +- +-/* GPIO definitions for antenna frequency domain control (SMS8021) */ +-#define SMS_ANTENNA_GPIO_0 1 +-#define SMS_ANTENNA_GPIO_1 0 +- +-#define BW_8_MHZ 0 +-#define BW_7_MHZ 1 +-#define BW_6_MHZ 2 +-#define BW_5_MHZ 3 +-#define BW_ISDBT_1SEG 4 +-#define BW_ISDBT_3SEG 5 +- +-#define MSG_HDR_FLAG_SPLIT_MSG 4 +- +-#define MAX_GPIO_PIN_NUMBER 31 +- +-#define HIF_TASK 11 +-#define SMS_HOST_LIB 150 +-#define DVBT_BDA_CONTROL_MSG_ID 201 +- +-#define SMS_MAX_PAYLOAD_SIZE 240 +-#define SMS_TUNE_TIMEOUT 500 +- +-#define MSG_SMS_GPIO_CONFIG_REQ 507 +-#define MSG_SMS_GPIO_CONFIG_RES 508 +-#define MSG_SMS_GPIO_SET_LEVEL_REQ 509 +-#define MSG_SMS_GPIO_SET_LEVEL_RES 510 +-#define MSG_SMS_GPIO_GET_LEVEL_REQ 511 +-#define MSG_SMS_GPIO_GET_LEVEL_RES 512 +-#define MSG_SMS_RF_TUNE_REQ 561 +-#define MSG_SMS_RF_TUNE_RES 562 +-#define MSG_SMS_INIT_DEVICE_REQ 578 +-#define MSG_SMS_INIT_DEVICE_RES 579 +-#define MSG_SMS_ADD_PID_FILTER_REQ 601 +-#define MSG_SMS_ADD_PID_FILTER_RES 602 +-#define MSG_SMS_REMOVE_PID_FILTER_REQ 603 +-#define MSG_SMS_REMOVE_PID_FILTER_RES 604 +-#define MSG_SMS_DAB_CHANNEL 607 +-#define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 +-#define MSG_SMS_GET_PID_FILTER_LIST_RES 609 +-#define MSG_SMS_GET_STATISTICS_RES 616 +-#define MSG_SMS_GET_STATISTICS_REQ 615 +-#define MSG_SMS_HO_PER_SLICES_IND 630 +-#define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 +-#define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 +-#define MSG_SMS_SLEEP_RESUME_COMP_IND 655 +-#define MSG_SMS_DATA_DOWNLOAD_REQ 660 +-#define MSG_SMS_DATA_DOWNLOAD_RES 661 +-#define MSG_SMS_SWDOWNLOAD_TRIGGER_REQ 664 +-#define MSG_SMS_SWDOWNLOAD_TRIGGER_RES 665 +-#define MSG_SMS_SWDOWNLOAD_BACKDOOR_REQ 666 +-#define MSG_SMS_SWDOWNLOAD_BACKDOOR_RES 667 +-#define MSG_SMS_GET_VERSION_EX_REQ 668 +-#define MSG_SMS_GET_VERSION_EX_RES 669 +-#define MSG_SMS_SET_CLOCK_OUTPUT_REQ 670 +-#define MSG_SMS_I2C_SET_FREQ_REQ 685 +-#define MSG_SMS_GENERIC_I2C_REQ 687 +-#define MSG_SMS_GENERIC_I2C_RES 688 +-#define MSG_SMS_DVBT_BDA_DATA 693 +-#define MSG_SW_RELOAD_REQ 697 +-#define MSG_SMS_DATA_MSG 699 +-#define MSG_SW_RELOAD_START_REQ 702 +-#define MSG_SW_RELOAD_START_RES 703 +-#define MSG_SW_RELOAD_EXEC_REQ 704 +-#define MSG_SW_RELOAD_EXEC_RES 705 +-#define MSG_SMS_SPI_INT_LINE_SET_REQ 710 +-#define MSG_SMS_GPIO_CONFIG_EX_REQ 712 +-#define MSG_SMS_GPIO_CONFIG_EX_RES 713 +-#define MSG_SMS_ISDBT_TUNE_REQ 776 +-#define MSG_SMS_ISDBT_TUNE_RES 777 +-#define MSG_SMS_TRANSMISSION_IND 782 +-#define MSG_SMS_START_IR_REQ 800 +-#define MSG_SMS_START_IR_RES 801 +-#define MSG_SMS_IR_SAMPLES_IND 802 +-#define MSG_SMS_SIGNAL_DETECTED_IND 827 +-#define MSG_SMS_NO_SIGNAL_IND 828 +- +-#define SMS_INIT_MSG_EX(ptr, type, src, dst, len) do { \ +- (ptr)->msgType = type; (ptr)->msgSrcId = src; (ptr)->msgDstId = dst; \ +- (ptr)->msgLength = len; (ptr)->msgFlags = 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_MAX, +-}; +- +-struct SmsMsgHdr_ST { +- u16 msgType; +- u8 msgSrcId; +- u8 msgDstId; +- u16 msgLength; /* Length of entire message, including header */ +- u16 msgFlags; +-}; +- +-struct SmsMsgData_ST { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 msgData[1]; +-}; +- +-struct SmsMsgData_ST2 { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 msgData[2]; +-}; +- +-struct SmsDataDownload_ST { +- struct SmsMsgHdr_ST xMsgHeader; +- u32 MemAddr; +- u8 Payload[SMS_MAX_PAYLOAD_SIZE]; +-}; +- +-struct SmsVersionRes_ST { +- struct SmsMsgHdr_ST xMsgHeader; +- +- u16 ChipModel; /* e.g. 0x1102 for SMS-1102 "Nova" */ +- u8 Step; /* 0 - Step A */ +- u8 MetalFix; /* 0 - Metal 0 */ +- +- /* FirmwareId 0xFF if ROM, otherwise the +- * value indicated by SMSHOSTLIB_DEVICE_MODES_E */ +- u8 FirmwareId; +- /* SupportedProtocols Bitwise OR combination of +- * supported protocols */ +- u8 SupportedProtocols; +- +- u8 VersionMajor; +- u8 VersionMinor; +- u8 VersionPatch; +- u8 VersionFieldPatch; +- +- u8 RomVersionMajor; +- u8 RomVersionMinor; +- u8 RomVersionPatch; +- u8 RomVersionFieldPatch; +- +- u8 TextLabel[34]; +-}; +- +-struct SmsFirmware_ST { +- u32 CheckSum; +- u32 Length; +- u32 StartAddress; +- u8 Payload[1]; +-}; +- +-/* Statistics information returned as response for +- * SmsHostApiGetStatistics_Req */ +-struct SMSHOSTLIB_STATISTICS_ST { +- u32 Reserved; /* Reserved */ +- +- /* Common parameters */ +- u32 IsRfLocked; /* 0 - not locked, 1 - locked */ +- u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ +- u32 IsExternalLNAOn; /* 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 InBandPwr; /* In band power in dBM */ +- s32 CarrierOffset; /* Carrier Offset in bin/1024 */ +- +- /* Transmission parameters */ +- u32 Frequency; /* Frequency in Hz */ +- u32 Bandwidth; /* Bandwidth in MHz, valid only for DVB-T/H */ +- u32 TransmissionMode; /* Transmission Mode, for DAB modes 1-4, +- for DVB-T/H FFT mode carriers in Kilos */ +- u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET, +- valid only for DVB-T/H */ +- u32 GuardInterval; /* Guard Interval from +- SMSHOSTLIB_GUARD_INTERVALS_ET, valid only for DVB-T/H */ +- u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, +- valid only for DVB-T/H */ +- u32 LPCodeRate; /* 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 BurstSize; /* Current burst size in bytes, +- valid only for DVB-H */ +- u32 BurstDuration; /* Current burst duration in mSec, +- valid only for DVB-H */ +- u32 BurstCycleTime; /* Current burst cycle time in mSec, +- valid only for DVB-H */ +- u32 CalculatedBurstCycleTime;/* Current burst cycle time in mSec, +- as calculated by demodulator, valid only for DVB-H */ +- u32 NumOfRows; /* Number of rows in MPE table, +- valid only for DVB-H */ +- u32 NumOfPaddCols; /* Number of padding columns in MPE table, +- valid only for DVB-H */ +- u32 NumOfPunctCols; /* Number of puncturing columns in MPE table, +- valid only for DVB-H */ +- u32 ErrorTSPackets; /* Number of erroneous +- transport-stream packets */ +- u32 TotalTSPackets; /* Total number of transport-stream packets */ +- u32 NumOfValidMpeTlbs; /* Number of MPE tables which do not include +- errors after MPE RS decoding */ +- u32 NumOfInvalidMpeTlbs;/* Number of MPE tables which include errors +- after MPE RS decoding */ +- u32 NumOfCorrectedMpeTlbs;/* Number of MPE tables which were +- corrected by MPE RS decoding */ +- /* Common params */ +- u32 BERErrorCount; /* Number of errornous SYNC bits. */ +- u32 BERBitCount; /* Total number of SYNC bits. */ +- +- /* Interface information */ +- u32 SmsToHostTxErrors; /* Total number of transmission errors. */ +- +- /* DAB/T-DMB */ +- u32 PreBER; /* DAB/T-DMB only: Pre Viterbi BER [1E-5] */ +- +- /* DVB-H TPS parameters */ +- u32 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; +- if set to 0xFFFFFFFF cell_id not yet recovered */ +- u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - +- Time Slicing indicator, bit 0 - MPE-FEC indicator */ +- u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - +- Time Slicing indicator, bit 0 - MPE-FEC indicator */ +- +- u32 NumMPEReceived; /* DVB-H, Num MPE section received */ +- +- u32 ReservedFields[10]; /* Reserved */ +-}; +- +-struct SmsMsgStatisticsInfo_ST { +- u32 RequestResult; +- +- struct SMSHOSTLIB_STATISTICS_ST Stat; +- +- /* Split the calc of the SNR in DAB */ +- u32 Signal; /* dB */ +- u32 Noise; /* dB */ +- +-}; +- +-struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { +- /* Per-layer information */ +- u32 CodeRate; /* 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 BERErrorCount; /* Post Viterbi Error Bits Count */ +- u32 BERBitCount; /* Post Viterbi Total Bits Count */ +- u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ +- u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ +- u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ +- u32 TotalTSPackets; /* Total number of transport-stream packets */ +- u32 TILdepthI; /* Time interleaver depth I parameter, +- * 255 means layer does not exist */ +- u32 NumberOfSegments; /* Number of segments in layer A, +- * 255 means layer does not exist */ +- u32 TMCCErrors; /* TMCC errors */ +-}; +- +-struct SMSHOSTLIB_STATISTICS_ISDBT_ST { +- u32 StatisticsType; /* 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 FullSize; /* Total size of the structure returned by the modem. +- * If the size requested by the host is smaller than +- * FullSize, the struct will be truncated */ +- +- /* Common parameters */ +- u32 IsRfLocked; /* 0 - not locked, 1 - locked */ +- u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ +- u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ +- +- /* Reception quality */ +- s32 SNR; /* dB */ +- s32 RSSI; /* dBm */ +- s32 InBandPwr; /* In band power in dBM */ +- s32 CarrierOffset; /* Carrier Offset in Hz */ +- +- /* Transmission parameters */ +- u32 Frequency; /* Frequency in Hz */ +- u32 Bandwidth; /* Bandwidth in MHz */ +- u32 TransmissionMode; /* ISDB-T transmission mode */ +- u32 ModemState; /* 0 - Acquisition, 1 - Locked */ +- u32 GuardInterval; /* Guard Interval, 1 divided by value */ +- u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ +- u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ +- u32 NumOfLayers; /* Number of ISDB-T layers in the network */ +- +- /* Per-layer information */ +- /* Layers A, B and C */ +- struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; +- /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ +- +- /* Interface information */ +- u32 SmsToHostTxErrors; /* Total number of transmission errors. */ +-}; +- +-struct PID_STATISTICS_DATA_S { +- 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 PID_DATA_S { +- u32 pid; +- u32 num_rows; +- struct PID_STATISTICS_DATA_S 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.TransmissionMode == 0) \ +- _stat.TransmissionMode = 2; \ +- else if (_stat.TransmissionMode == 1) \ +- _stat.TransmissionMode = 8; \ +- else \ +- _stat.TransmissionMode = 4; +- +-struct TRANSMISSION_STATISTICS_S { +- u32 Frequency; /* Frequency in Hz */ +- u32 Bandwidth; /* Bandwidth in MHz */ +- u32 TransmissionMode; /* FFT mode carriers in Kilos */ +- u32 GuardInterval; /* Guard Interval from +- SMSHOSTLIB_GUARD_INTERVALS_ET */ +- u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET */ +- u32 LPCodeRate; /* 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 CellId; /* TPS Cell ID in bits 15..0, bits 31..16 zero; +- if set to 0xFFFFFFFF cell_id not yet recovered */ +- u32 DvbhSrvIndHP; /* DVB-H service indication info, bit 1 - +- Time Slicing indicator, bit 0 - MPE-FEC indicator */ +- u32 DvbhSrvIndLP; /* DVB-H service indication info, bit 1 - +- Time Slicing indicator, bit 0 - MPE-FEC indicator */ +- u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ +-}; +- +-struct RECEPTION_STATISTICS_S { +- u32 IsRfLocked; /* 0 - not locked, 1 - locked */ +- u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ +- u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ +- +- u32 ModemState; /* from SMSHOSTLIB_DVB_MODEM_STATE_ET */ +- s32 SNR; /* dB */ +- u32 BER; /* Post Viterbi BER [1E-5] */ +- u32 BERErrorCount; /* Number of erronous SYNC bits. */ +- u32 BERBitCount; /* 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 InBandPwr; /* In band power in dBM */ +- s32 CarrierOffset; /* Carrier Offset in bin/1024 */ +- u32 ErrorTSPackets; /* Number of erroneous +- transport-stream packets */ +- u32 TotalTSPackets; /* Total number of transport-stream packets */ +- +- s32 MRC_SNR; /* dB */ +- s32 MRC_RSSI; /* dBm */ +- s32 MRC_InBandPwr; /* In band power in dBM */ +-}; +- +- +-/* Statistics information returned as response for +- * SmsHostApiGetStatisticsEx_Req for DVB applications, SMS1100 and up */ +-struct SMSHOSTLIB_STATISTICS_DVB_S { +- /* Reception */ +- struct RECEPTION_STATISTICS_S ReceptionData; +- +- /* Transmission parameters */ +- struct TRANSMISSION_STATISTICS_S TransmissionData; +- +- /* Burst parameters, valid only for DVB-H */ +-#define SRVM_MAX_PID_FILTERS 8 +- struct PID_DATA_S PidData[SRVM_MAX_PID_FILTERS]; +-}; +- +-struct SRVM_SIGNAL_STATUS_S { +- u32 result; +- u32 snr; +- u32 tsPackets; +- u32 etsPackets; +- u32 constellation; +- u32 hpCode; +- u32 tpsSrvIndLP; +- u32 tpsSrvIndHP; +- u32 cellId; +- u32 reason; +- +- s32 inBandPower; +- u32 requestId; +-}; +- +-struct SMSHOSTLIB_I2C_REQ_ST { +- u32 DeviceAddress; /* I2c device address */ +- u32 WriteCount; /* number of bytes to write */ +- u32 ReadCount; /* number of bytes to read */ +- u8 Data[1]; +-}; +- +-struct SMSHOSTLIB_I2C_RES_ST { +- u32 Status; /* non-zero value in case of failure */ +- u32 ReadCount; /* 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; +- +-#define SMS_GPIO_OUTPUTSLEWRATE_FAST 0 +-#define SMS_GPIO_OUTPUTSLEWRATE_SLOW 1 +- u8 outputslewrate; +- +-#define SMS_GPIO_OUTPUTDRIVING_4mA 0 +-#define SMS_GPIO_OUTPUTDRIVING_8mA 1 +-#define SMS_GPIO_OUTPUTDRIVING_12mA 2 +-#define SMS_GPIO_OUTPUTDRIVING_16mA 3 +- u8 outputdriving; +-}; +- +-struct smscore_gpio_config { +-#define SMS_GPIO_DIRECTION_INPUT 0 +-#define SMS_GPIO_DIRECTION_OUTPUT 1 +- u8 Direction; +- +-#define SMS_GPIO_PULL_UP_DOWN_NONE 0 +-#define SMS_GPIO_PULL_UP_DOWN_PULLDOWN 1 +-#define SMS_GPIO_PULL_UP_DOWN_PULLUP 2 +-#define SMS_GPIO_PULL_UP_DOWN_KEEPER 3 +- u8 PullUpDown; +- +-#define SMS_GPIO_INPUT_CHARACTERISTICS_NORMAL 0 +-#define SMS_GPIO_INPUT_CHARACTERISTICS_SCHMITT 1 +- u8 InputCharacteristics; +- +-#define SMS_GPIO_OUTPUT_SLEW_RATE_SLOW 1 /* 10xx */ +-#define SMS_GPIO_OUTPUT_SLEW_RATE_FAST 0 /* 10xx */ +- +- +-#define SMS_GPIO_OUTPUT_SLEW_RATE_0_45_V_NS 0 /* 11xx */ +-#define SMS_GPIO_OUTPUT_SLEW_RATE_0_9_V_NS 1 /* 11xx */ +-#define SMS_GPIO_OUTPUT_SLEW_RATE_1_7_V_NS 2 /* 11xx */ +-#define SMS_GPIO_OUTPUT_SLEW_RATE_3_3_V_NS 3 /* 11xx */ +- u8 OutputSlewRate; +- +-#define SMS_GPIO_OUTPUT_DRIVING_S_4mA 0 /* 10xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_S_8mA 1 /* 10xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_S_12mA 2 /* 10xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_S_16mA 3 /* 10xx */ +- +-#define SMS_GPIO_OUTPUT_DRIVING_1_5mA 0 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_2_8mA 1 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_4mA 2 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_7mA 3 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_10mA 4 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_11mA 5 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_14mA 6 /* 11xx */ +-#define SMS_GPIO_OUTPUT_DRIVING_16mA 7 /* 11xx */ +- u8 OutputDriving; +-}; +- +-extern void smscore_registry_setmode(char *devpath, int mode); +-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); +-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_get_fw_filename(struct smscore_device_t *coredev, +- int mode, char *filename); +-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 PinNum, +- struct smscore_gpio_config *pGpioConfig); +-extern int smscore_gpio_set_level(struct smscore_device_t *coredev, u8 PinNum, +- u8 NewLevel); +-extern int smscore_gpio_get_level(struct smscore_device_t *coredev, u8 PinNum, +- 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); +- +- +-/* ------------------------------------------------------------------------ */ +- +-#define DBG_INFO 1 +-#define DBG_ADV 2 +- +-#define sms_printk(kern, fmt, arg...) \ +- printk(kern "%s: " fmt "\n", __func__, ##arg) +- +-#define dprintk(kern, lvl, fmt, arg...) do {\ +- if (sms_dbg & lvl) \ +- sms_printk(kern, fmt, ##arg); } while (0) +- +-#define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg) +-#define sms_err(fmt, arg...) \ +- sms_printk(KERN_ERR, "line: %d: " fmt, __LINE__, ##arg) +-#define sms_warn(fmt, arg...) sms_printk(KERN_WARNING, fmt, ##arg) +-#define sms_info(fmt, arg...) \ +- dprintk(KERN_INFO, DBG_INFO, fmt, ##arg) +-#define sms_debug(fmt, arg...) \ +- dprintk(KERN_DEBUG, DBG_ADV, fmt, ##arg) +- +- +-#endif /* __SMS_CORE_API_H__ */ +diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c +deleted file mode 100644 +index aa77e54..0000000 +--- a/drivers/media/dvb/siano/smsdvb.c ++++ /dev/null +@@ -1,1078 +0,0 @@ +-/**************************************************************** +- +-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 . +- +-****************************************************************/ +- +-#include +-#include +-#include +- +-#include "dmxdev.h" +-#include "dvbdev.h" +-#include "dvb_demux.h" +-#include "dvb_frontend.h" +- +-#include "smscoreapi.h" +-#include "smsendian.h" +-#include "sms-cards.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-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 SMSHOSTLIB_STATISTICS_DVB_S sms_stat_dvb; +- int event_fe_state; +- int event_unc_state; +-}; +- +-static struct list_head g_smsdvb_clients; +-static struct mutex g_smsdvb_clientslock; +- +-static int sms_dbg; +-module_param_named(debug, sms_dbg, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); +- +-/* 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: +- sms_debug("DVB3_EVENT_INIT"); +- sms_board_event(coredev, BOARD_EVENT_BIND); +- break; +- case DVB3_EVENT_SLEEP: +- sms_debug("DVB3_EVENT_SLEEP"); +- sms_board_event(coredev, BOARD_EVENT_POWER_SUSPEND); +- break; +- case DVB3_EVENT_HOTPLUG: +- sms_debug("DVB3_EVENT_HOTPLUG"); +- 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; +- sms_debug("DVB3_EVENT_FE_LOCK"); +- 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; +- sms_debug("DVB3_EVENT_FE_UNLOCK"); +- 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; +- sms_debug("DVB3_EVENT_UNC_OK"); +- 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; +- sms_debug("DVB3_EVENT_UNC_ERR"); +- sms_board_event(coredev, BOARD_EVENT_MULTIPLEX_ERRORS); +- } +- break; +- +- default: +- sms_err("Unknown dvb3 api event"); +- break; +- } +-} +- +- +-static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, +- struct SMSHOSTLIB_STATISTICS_ST *p) +-{ +- if (sms_dbg & 2) { +- printk(KERN_DEBUG "Reserved = %d", p->Reserved); +- printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); +- printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); +- printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); +- printk(KERN_DEBUG "SNR = %d", p->SNR); +- printk(KERN_DEBUG "BER = %d", p->BER); +- printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); +- printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); +- printk(KERN_DEBUG "MFER = %d", p->MFER); +- printk(KERN_DEBUG "RSSI = %d", p->RSSI); +- printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); +- printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); +- printk(KERN_DEBUG "Frequency = %d", p->Frequency); +- printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); +- printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); +- printk(KERN_DEBUG "ModemState = %d", p->ModemState); +- printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); +- printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); +- printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); +- printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); +- printk(KERN_DEBUG "Constellation = %d", p->Constellation); +- printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); +- printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); +- printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); +- printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); +- printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); +- printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); +- printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); +- printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); +- printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); +- printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); +- printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); +- printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); +- printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); +- printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); +- printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); +- printk(KERN_DEBUG "PreBER = %d", p->PreBER); +- printk(KERN_DEBUG "CellId = %d", p->CellId); +- printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); +- printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); +- printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); +- } +- +- pReceptionData->IsDemodLocked = p->IsDemodLocked; +- +- pReceptionData->SNR = p->SNR; +- pReceptionData->BER = p->BER; +- pReceptionData->BERErrorCount = p->BERErrorCount; +- pReceptionData->InBandPwr = p->InBandPwr; +- pReceptionData->ErrorTSPackets = p->ErrorTSPackets; +-}; +- +- +-static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, +- struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) +-{ +- int i; +- +- if (sms_dbg & 2) { +- printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); +- printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); +- printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); +- printk(KERN_DEBUG "SNR = %d", p->SNR); +- printk(KERN_DEBUG "RSSI = %d", p->RSSI); +- printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); +- printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); +- printk(KERN_DEBUG "Frequency = %d", p->Frequency); +- printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); +- printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); +- printk(KERN_DEBUG "ModemState = %d", p->ModemState); +- printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); +- printk(KERN_DEBUG "SystemType = %d", p->SystemType); +- printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); +- printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); +- printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); +- +- for (i = 0; i < 3; i++) { +- printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); +- printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); +- printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); +- printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); +- printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); +- printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); +- printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); +- printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); +- printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); +- printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); +- printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); +- printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); +- } +- } +- +- pReceptionData->IsDemodLocked = p->IsDemodLocked; +- +- pReceptionData->SNR = p->SNR; +- pReceptionData->InBandPwr = p->InBandPwr; +- +- pReceptionData->ErrorTSPackets = 0; +- pReceptionData->BER = 0; +- pReceptionData->BERErrorCount = 0; +- for (i = 0; i < 3; i++) { +- pReceptionData->BER += p->LayerInfo[i].BER; +- pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; +- pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; +- } +-} +- +-static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) +-{ +- struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; +- struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *) (((u8 *) cb->p) +- + cb->offset); +- u32 *pMsgData = (u32 *) phdr + 1; +- /*u32 MsgDataLen = phdr->msgLength - sizeof(struct SmsMsgHdr_ST);*/ +- bool is_status_update = false; +- +- smsendian_handle_rx_message((struct SmsMsgData_ST *) phdr); +- +- switch (phdr->msgType) { +- case MSG_SMS_DVBT_BDA_DATA: +- dvb_dmx_swfilter(&client->demux, (u8 *)(phdr + 1), +- cb->size - sizeof(struct SmsMsgHdr_ST)); +- break; +- +- case MSG_SMS_RF_TUNE_RES: +- case MSG_SMS_ISDBT_TUNE_RES: +- complete(&client->tune_done); +- break; +- +- case MSG_SMS_SIGNAL_DETECTED_IND: +- sms_info("MSG_SMS_SIGNAL_DETECTED_IND"); +- client->sms_stat_dvb.TransmissionData.IsDemodLocked = true; +- is_status_update = true; +- break; +- +- case MSG_SMS_NO_SIGNAL_IND: +- sms_info("MSG_SMS_NO_SIGNAL_IND"); +- client->sms_stat_dvb.TransmissionData.IsDemodLocked = false; +- is_status_update = true; +- break; +- +- case MSG_SMS_TRANSMISSION_IND: { +- sms_info("MSG_SMS_TRANSMISSION_IND"); +- +- pMsgData++; +- memcpy(&client->sms_stat_dvb.TransmissionData, pMsgData, +- sizeof(struct TRANSMISSION_STATISTICS_S)); +- +- /* Mo need to correct guard interval +- * (as opposed to old statistics message). +- */ +- CORRECT_STAT_BANDWIDTH(client->sms_stat_dvb.TransmissionData); +- CORRECT_STAT_TRANSMISSON_MODE( +- client->sms_stat_dvb.TransmissionData); +- is_status_update = true; +- break; +- } +- case MSG_SMS_HO_PER_SLICES_IND: { +- struct RECEPTION_STATISTICS_S *pReceptionData = +- &client->sms_stat_dvb.ReceptionData; +- struct SRVM_SIGNAL_STATUS_S SignalStatusData; +- +- /*sms_info("MSG_SMS_HO_PER_SLICES_IND");*/ +- pMsgData++; +- SignalStatusData.result = pMsgData[0]; +- SignalStatusData.snr = pMsgData[1]; +- SignalStatusData.inBandPower = (s32) pMsgData[2]; +- SignalStatusData.tsPackets = pMsgData[3]; +- SignalStatusData.etsPackets = pMsgData[4]; +- SignalStatusData.constellation = pMsgData[5]; +- SignalStatusData.hpCode = pMsgData[6]; +- SignalStatusData.tpsSrvIndLP = pMsgData[7] & 0x03; +- SignalStatusData.tpsSrvIndHP = pMsgData[8] & 0x03; +- SignalStatusData.cellId = pMsgData[9] & 0xFFFF; +- SignalStatusData.reason = pMsgData[10]; +- SignalStatusData.requestId = pMsgData[11]; +- pReceptionData->IsRfLocked = pMsgData[16]; +- pReceptionData->IsDemodLocked = pMsgData[17]; +- pReceptionData->ModemState = pMsgData[12]; +- pReceptionData->SNR = pMsgData[1]; +- pReceptionData->BER = pMsgData[13]; +- pReceptionData->RSSI = pMsgData[14]; +- CORRECT_STAT_RSSI(client->sms_stat_dvb.ReceptionData); +- +- pReceptionData->InBandPwr = (s32) pMsgData[2]; +- pReceptionData->CarrierOffset = (s32) pMsgData[15]; +- pReceptionData->TotalTSPackets = pMsgData[3]; +- pReceptionData->ErrorTSPackets = pMsgData[4]; +- +- /* TS PER */ +- if ((SignalStatusData.tsPackets + SignalStatusData.etsPackets) +- > 0) { +- pReceptionData->TS_PER = (SignalStatusData.etsPackets +- * 100) / (SignalStatusData.tsPackets +- + SignalStatusData.etsPackets); +- } else { +- pReceptionData->TS_PER = 0; +- } +- +- pReceptionData->BERBitCount = pMsgData[18]; +- pReceptionData->BERErrorCount = pMsgData[19]; +- +- pReceptionData->MRC_SNR = pMsgData[20]; +- pReceptionData->MRC_InBandPwr = pMsgData[21]; +- pReceptionData->MRC_RSSI = pMsgData[22]; +- +- is_status_update = true; +- break; +- } +- case MSG_SMS_GET_STATISTICS_RES: { +- union { +- struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; +- struct SmsMsgStatisticsInfo_ST dvb; +- } *p = (void *) (phdr + 1); +- struct RECEPTION_STATISTICS_S *pReceptionData = +- &client->sms_stat_dvb.ReceptionData; +- +- sms_info("MSG_SMS_GET_STATISTICS_RES"); +- +- is_status_update = true; +- +- switch (smscore_get_device_mode(client->coredev)) { +- case DEVICE_MODE_ISDBT: +- case DEVICE_MODE_ISDBT_BDA: +- smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); +- break; +- default: +- smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); +- } +- if (!pReceptionData->IsDemodLocked) { +- pReceptionData->SNR = 0; +- pReceptionData->BER = 0; +- pReceptionData->BERErrorCount = 0; +- pReceptionData->InBandPwr = 0; +- pReceptionData->ErrorTSPackets = 0; +- } +- +- complete(&client->tune_done); +- break; +- } +- default: +- sms_info("Unhandled message %d", phdr->msgType); +- +- } +- smscore_putbuffer(client->coredev, cb); +- +- if (is_status_update) { +- if (client->sms_stat_dvb.ReceptionData.IsDemodLocked) { +- client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER +- | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; +- sms_board_dvb3_event(client, DVB3_EVENT_FE_LOCK); +- if (client->sms_stat_dvb.ReceptionData.ErrorTSPackets +- == 0) +- sms_board_dvb3_event(client, DVB3_EVENT_UNC_OK); +- else +- sms_board_dvb3_event(client, +- DVB3_EVENT_UNC_ERR); +- +- } else { +- if (client->sms_stat_dvb.ReceptionData.IsRfLocked) +- client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; +- else +- client->fe_status = 0; +- sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); +- } +- } +- +- return 0; +-} +- +-static void smsdvb_unregister_client(struct smsdvb_client_t *client) +-{ +- /* must be called under clientslock */ +- +- list_del(&client->entry); +- +- smscore_unregister_client(client->smsclient); +- dvb_unregister_frontend(&client->frontend); +- dvb_dmxdev_release(&client->dmxdev); +- dvb_dmx_release(&client->demux); +- 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 SmsMsgData_ST PidMsg; +- +- sms_debug("add pid %d(%x)", +- feed->pid, feed->pid); +- +- PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- PidMsg.xMsgHeader.msgDstId = HIF_TASK; +- PidMsg.xMsgHeader.msgFlags = 0; +- PidMsg.xMsgHeader.msgType = MSG_SMS_ADD_PID_FILTER_REQ; +- PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); +- PidMsg.msgData[0] = feed->pid; +- +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); +- return smsclient_sendrequest(client->smsclient, +- &PidMsg, sizeof(PidMsg)); +-} +- +-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 SmsMsgData_ST PidMsg; +- +- sms_debug("remove pid %d(%x)", +- feed->pid, feed->pid); +- +- PidMsg.xMsgHeader.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- PidMsg.xMsgHeader.msgDstId = HIF_TASK; +- PidMsg.xMsgHeader.msgFlags = 0; +- PidMsg.xMsgHeader.msgType = MSG_SMS_REMOVE_PID_FILTER_REQ; +- PidMsg.xMsgHeader.msgLength = sizeof(PidMsg); +- PidMsg.msgData[0] = feed->pid; +- +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)&PidMsg); +- return smsclient_sendrequest(client->smsclient, +- &PidMsg, sizeof(PidMsg)); +-} +- +-static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, +- void *buffer, size_t size, +- struct completion *completion) +-{ +- int rc; +- +- smsendian_handle_tx_message((struct SmsMsgHdr_ST *)buffer); +- 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 SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, +- DVBT_BDA_CONTROL_MSG_ID, +- HIF_TASK, +- sizeof(struct SmsMsgHdr_ST), 0 }; +- +- rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), +- &client->tune_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, +- (client->sms_stat_dvb.ReceptionData.BER +- == 0) ? SMS_LED_HI : SMS_LED_LO); +- else +- return sms_board_led_feedback(client->coredev, SMS_LED_OFF); +-} +- +-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->sms_stat_dvb.ReceptionData.BER; +- +- led_feedback(client); +- +- return rc; +-} +- +-static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +-{ +- int rc; +- +- struct smsdvb_client_t *client; +- client = container_of(fe, struct smsdvb_client_t, frontend); +- +- rc = smsdvb_send_statistics_request(client); +- +- if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) +- *strength = 0; +- else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) +- *strength = 100; +- else +- *strength = +- (client->sms_stat_dvb.ReceptionData.InBandPwr +- + 95) * 3 / 2; +- +- led_feedback(client); +- +- return rc; +-} +- +-static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) +-{ +- int rc; +- struct smsdvb_client_t *client; +- client = container_of(fe, struct smsdvb_client_t, frontend); +- +- rc = smsdvb_send_statistics_request(client); +- +- *snr = client->sms_stat_dvb.ReceptionData.SNR; +- +- led_feedback(client); +- +- return rc; +-} +- +-static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +-{ +- int rc; +- struct smsdvb_client_t *client; +- client = container_of(fe, struct smsdvb_client_t, frontend); +- +- rc = smsdvb_send_statistics_request(client); +- +- *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; +- +- led_feedback(client); +- +- return rc; +-} +- +-static int smsdvb_get_tune_settings(struct dvb_frontend *fe, +- struct dvb_frontend_tune_settings *tune) +-{ +- sms_debug(""); +- +- 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 SmsMsgHdr_ST Msg; +- u32 Data[3]; +- } Msg; +- +- int ret; +- +- client->fe_status = FE_HAS_SIGNAL; +- client->event_fe_state = -1; +- client->event_unc_state = -1; +- fe->dtv_property_cache.delivery_system = SYS_DVBT; +- +- Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- Msg.Msg.msgDstId = HIF_TASK; +- Msg.Msg.msgFlags = 0; +- Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; +- Msg.Msg.msgLength = sizeof(Msg); +- Msg.Data[0] = c->frequency; +- Msg.Data[2] = 12000000; +- +- sms_info("%s: freq %d band %d", __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); +- +- struct { +- struct SmsMsgHdr_ST Msg; +- u32 Data[4]; +- } Msg; +- +- fe->dtv_property_cache.delivery_system = SYS_ISDBT; +- +- Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; +- Msg.Msg.msgDstId = HIF_TASK; +- Msg.Msg.msgFlags = 0; +- Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; +- Msg.Msg.msgLength = sizeof(Msg); +- +- if (c->isdbt_sb_segment_idx == -1) +- c->isdbt_sb_segment_idx = 0; +- +- switch (c->isdbt_sb_segment_count) { +- case 3: +- Msg.Data[1] = BW_ISDBT_3SEG; +- break; +- case 1: +- Msg.Data[1] = BW_ISDBT_1SEG; +- break; +- case 0: /* AUTO */ +- switch (c->bandwidth_hz / 1000000) { +- case 8: +- case 7: +- c->isdbt_sb_segment_count = 3; +- Msg.Data[1] = BW_ISDBT_3SEG; +- break; +- case 6: +- c->isdbt_sb_segment_count = 1; +- Msg.Data[1] = BW_ISDBT_1SEG; +- break; +- default: /* Assumes 6 MHZ bw */ +- c->isdbt_sb_segment_count = 1; +- c->bandwidth_hz = 6000; +- Msg.Data[1] = BW_ISDBT_1SEG; +- break; +- } +- break; +- default: +- sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); +- return -EINVAL; +- } +- +- Msg.Data[0] = c->frequency; +- Msg.Data[2] = 12000000; +- Msg.Data[3] = c->isdbt_sb_segment_idx; +- +- sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, +- c->frequency, c->isdbt_sb_segment_count, +- c->isdbt_sb_segment_idx); +- +- return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), +- &client->tune_done); +-} +- +-static int smsdvb_set_frontend(struct dvb_frontend *fe) +-{ +- struct smsdvb_client_t *client = +- container_of(fe, struct smsdvb_client_t, frontend); +- struct smscore_device_t *coredev = client->coredev; +- +- 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; +- } +-} +- +-static int smsdvb_get_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *fep = &fe->dtv_property_cache; +- struct smsdvb_client_t *client = +- container_of(fe, struct smsdvb_client_t, frontend); +- struct smscore_device_t *coredev = client->coredev; +- struct TRANSMISSION_STATISTICS_S *td = +- &client->sms_stat_dvb.TransmissionData; +- +- switch (smscore_get_device_mode(coredev)) { +- case DEVICE_MODE_DVBT: +- case DEVICE_MODE_DVBT_BDA: +- fep->frequency = td->Frequency; +- +- switch (td->Bandwidth) { +- case 6: +- fep->bandwidth_hz = 6000000; +- break; +- case 7: +- fep->bandwidth_hz = 7000000; +- break; +- case 8: +- fep->bandwidth_hz = 8000000; +- break; +- } +- +- switch (td->TransmissionMode) { +- case 2: +- fep->transmission_mode = TRANSMISSION_MODE_2K; +- break; +- case 8: +- fep->transmission_mode = TRANSMISSION_MODE_8K; +- } +- +- switch (td->GuardInterval) { +- case 0: +- fep->guard_interval = GUARD_INTERVAL_1_32; +- break; +- case 1: +- fep->guard_interval = GUARD_INTERVAL_1_16; +- break; +- case 2: +- fep->guard_interval = GUARD_INTERVAL_1_8; +- break; +- case 3: +- fep->guard_interval = GUARD_INTERVAL_1_4; +- break; +- } +- +- switch (td->CodeRate) { +- case 0: +- fep->code_rate_HP = FEC_1_2; +- break; +- case 1: +- fep->code_rate_HP = FEC_2_3; +- break; +- case 2: +- fep->code_rate_HP = FEC_3_4; +- break; +- case 3: +- fep->code_rate_HP = FEC_5_6; +- break; +- case 4: +- fep->code_rate_HP = FEC_7_8; +- break; +- } +- +- switch (td->LPCodeRate) { +- case 0: +- fep->code_rate_LP = FEC_1_2; +- break; +- case 1: +- fep->code_rate_LP = FEC_2_3; +- break; +- case 2: +- fep->code_rate_LP = FEC_3_4; +- break; +- case 3: +- fep->code_rate_LP = FEC_5_6; +- break; +- case 4: +- fep->code_rate_LP = FEC_7_8; +- break; +- } +- +- switch (td->Constellation) { +- case 0: +- fep->modulation = QPSK; +- break; +- case 1: +- fep->modulation = QAM_16; +- break; +- case 2: +- fep->modulation = QAM_64; +- break; +- } +- +- switch (td->Hierarchy) { +- case 0: +- fep->hierarchy = HIERARCHY_NONE; +- break; +- case 1: +- fep->hierarchy = HIERARCHY_1; +- break; +- case 2: +- fep->hierarchy = HIERARCHY_2; +- break; +- case 3: +- fep->hierarchy = HIERARCHY_4; +- break; +- } +- +- fep->inversion = INVERSION_AUTO; +- break; +- case DEVICE_MODE_ISDBT: +- case DEVICE_MODE_ISDBT_BDA: +- fep->frequency = td->Frequency; +- fep->bandwidth_hz = 6000000; +- /* todo: retrive the other parameters */ +- break; +- default: +- return -EINVAL; +- } +- +- 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) { +- sms_err("kmalloc() failed"); +- 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) { +- sms_err("dvb_register_adapter() failed %d", rc); +- goto adapter_error; +- } +- +- /* 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) { +- sms_err("dvb_dmx_init failed %d", 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) { +- sms_err("dvb_dmxdev_init failed %d", 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) { +- sms_err("frontend registration failed %d", 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, ¶ms, &client->smsclient); +- if (rc < 0) { +- sms_err("smscore_register_client() failed %d", rc); +- goto client_error; +- } +- +- client->coredev = coredev; +- +- init_completion(&client->tune_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_info("success"); +- sms_board_setup(coredev); +- +- 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: +- 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); +- +- rc = smscore_register_hotplug(smsdvb_hotplug); +- +- sms_debug(""); +- +- 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); +- +- 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/dvb/siano/smsendian.c b/drivers/media/dvb/siano/smsendian.c +deleted file mode 100644 +index e2657c2..0000000 +--- a/drivers/media/dvb/siano/smsendian.c ++++ /dev/null +@@ -1,103 +0,0 @@ +-/**************************************************************** +- +- 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 . +- +- ****************************************************************/ +- +-#include +-#include +- +-#include "smsendian.h" +-#include "smscoreapi.h" +- +-void smsendian_handle_tx_message(void *buffer) +-{ +-#ifdef __BIG_ENDIAN +- struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; +- int i; +- int msgWords; +- +- switch (msg->xMsgHeader.msgType) { +- case MSG_SMS_DATA_DOWNLOAD_REQ: +- { +- msg->msgData[0] = le32_to_cpu(msg->msgData[0]); +- break; +- } +- +- default: +- msgWords = (msg->xMsgHeader.msgLength - +- sizeof(struct SmsMsgHdr_ST))/4; +- +- for (i = 0; i < msgWords; i++) +- msg->msgData[i] = le32_to_cpu(msg->msgData[i]); +- +- break; +- } +-#endif /* __BIG_ENDIAN */ +-} +-EXPORT_SYMBOL_GPL(smsendian_handle_tx_message); +- +-void smsendian_handle_rx_message(void *buffer) +-{ +-#ifdef __BIG_ENDIAN +- struct SmsMsgData_ST *msg = (struct SmsMsgData_ST *)buffer; +- int i; +- int msgWords; +- +- switch (msg->xMsgHeader.msgType) { +- case MSG_SMS_GET_VERSION_EX_RES: +- { +- struct SmsVersionRes_ST *ver = +- (struct SmsVersionRes_ST *) msg; +- ver->ChipModel = le16_to_cpu(ver->ChipModel); +- break; +- } +- +- case MSG_SMS_DVBT_BDA_DATA: +- case MSG_SMS_DAB_CHANNEL: +- case MSG_SMS_DATA_MSG: +- { +- break; +- } +- +- default: +- { +- msgWords = (msg->xMsgHeader.msgLength - +- sizeof(struct SmsMsgHdr_ST))/4; +- +- for (i = 0; i < msgWords; i++) +- msg->msgData[i] = le32_to_cpu(msg->msgData[i]); +- +- break; +- } +- } +-#endif /* __BIG_ENDIAN */ +-} +-EXPORT_SYMBOL_GPL(smsendian_handle_rx_message); +- +-void smsendian_handle_message_header(void *msg) +-{ +-#ifdef __BIG_ENDIAN +- struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)msg; +- +- phdr->msgType = le16_to_cpu(phdr->msgType); +- phdr->msgLength = le16_to_cpu(phdr->msgLength); +- phdr->msgFlags = le16_to_cpu(phdr->msgFlags); +-#endif /* __BIG_ENDIAN */ +-} +-EXPORT_SYMBOL_GPL(smsendian_handle_message_header); +diff --git a/drivers/media/dvb/siano/smsendian.h b/drivers/media/dvb/siano/smsendian.h +deleted file mode 100644 +index 1624d6f..0000000 +--- a/drivers/media/dvb/siano/smsendian.h ++++ /dev/null +@@ -1,32 +0,0 @@ +-/**************************************************************** +- +-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 . +- +-****************************************************************/ +- +-#ifndef __SMS_ENDIAN_H__ +-#define __SMS_ENDIAN_H__ +- +-#include +- +-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/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c +deleted file mode 100644 +index 37bc5c4..0000000 +--- a/drivers/media/dvb/siano/smsir.c ++++ /dev/null +@@ -1,114 +0,0 @@ +-/**************************************************************** +- +- 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 . +- +- ****************************************************************/ +- +- +-#include +-#include +- +-#include "smscoreapi.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; +- +- sms_log("Allocating rc device"); +- dev = rc_allocate_device(); +- if (!dev) { +- sms_err("Not enough memory"); +- return -ENOMEM; +- } +- +- coredev->ir.controller = 0; /* Todo: vega/nova SPI number */ +- coredev->ir.timeout = IR_DEFAULT_TIMEOUT; +- sms_log("IR port %d, timeout %d ms", +- 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_protos = RC_TYPE_ALL; +- dev->map_name = sms_get_board(board_id)->rc_codes; +- dev->driver_name = MODULE_NAME; +- +- sms_log("Input device (IR) %s is set for key events", dev->input_name); +- +- err = rc_register_device(dev); +- if (err < 0) { +- sms_err("Failed to register device"); +- rc_free_device(dev); +- return err; +- } +- +- coredev->ir.dev = dev; +- return 0; +-} +- +-void sms_ir_exit(struct smscore_device_t *coredev) +-{ +- if (coredev->ir.dev) +- rc_unregister_device(coredev->ir.dev); +- +- sms_log(""); +-} +diff --git a/drivers/media/dvb/siano/smsir.h b/drivers/media/dvb/siano/smsir.h +deleted file mode 100644 +index ae92b3a..0000000 +--- a/drivers/media/dvb/siano/smsir.h ++++ /dev/null +@@ -1,55 +0,0 @@ +-/**************************************************************** +- +-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 . +- +-****************************************************************/ +- +-#ifndef __SMS_IR_H__ +-#define __SMS_IR_H__ +- +-#include +-#include +- +-#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; +- u64 protocol; +- +- u32 timeout; +- u32 controller; +-}; +- +-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); +- +-#endif /* __SMS_IR_H__ */ +- +diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c +deleted file mode 100644 +index 91f8c82..0000000 +--- a/drivers/media/dvb/siano/smssdio.c ++++ /dev/null +@@ -1,365 +0,0 @@ +-/* +- * smssdio.c - Siano 1xxx SDIO interface driver +- * +- * Copyright 2008 Pierre Ossman +- * +- * Based on code by Siano Mobile Silicon, Inc., +- * 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 hardware is a bit odd in that all transfers should be done +- * to/from the SMSSDIO_DATA register, yet the "increase address" bit +- * always needs to be set. +- * +- * Also, buffers from the card are always aligned to 128 byte +- * boundaries. +- */ +- +-/* +- * General cleanup notes: +- * +- * - only typedefs should be name *_t +- * +- * - use ERR_PTR and friends for smscore_register_device() +- * +- * - smscore_getbuffer should zero fields +- * +- * Fix stop command +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "smscoreapi.h" +-#include "sms-cards.h" +- +-/* Registers */ +- +-#define SMSSDIO_DATA 0x00 +-#define SMSSDIO_INT 0x04 +-#define SMSSDIO_BLOCK_SIZE 128 +- +-static const struct sdio_device_id smssdio_ids[] __devinitconst = { +- {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), +- .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, +- {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), +- .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, +- {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), +- .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, +- {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), +- .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, +- {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), +- .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, +- { /* end: all zeroes */ }, +-}; +- +-MODULE_DEVICE_TABLE(sdio, smssdio_ids); +- +-struct smssdio_device { +- struct sdio_func *func; +- +- struct smscore_device_t *coredev; +- +- struct smscore_buffer_t *split_cb; +-}; +- +-/*******************************************************************/ +-/* Siano core callbacks */ +-/*******************************************************************/ +- +-static int smssdio_sendrequest(void *context, void *buffer, size_t size) +-{ +- int ret = 0; +- struct smssdio_device *smsdev; +- +- smsdev = context; +- +- sdio_claim_host(smsdev->func); +- +- while (size >= smsdev->func->cur_blksize) { +- ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, +- buffer, smsdev->func->cur_blksize); +- if (ret) +- goto out; +- +- buffer += smsdev->func->cur_blksize; +- size -= smsdev->func->cur_blksize; +- } +- +- if (size) { +- ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, +- buffer, size); +- } +- +-out: +- sdio_release_host(smsdev->func); +- +- return ret; +-} +- +-/*******************************************************************/ +-/* SDIO callbacks */ +-/*******************************************************************/ +- +-static void smssdio_interrupt(struct sdio_func *func) +-{ +- int ret, isr; +- +- struct smssdio_device *smsdev; +- struct smscore_buffer_t *cb; +- struct SmsMsgHdr_ST *hdr; +- size_t size; +- +- smsdev = sdio_get_drvdata(func); +- +- /* +- * The interrupt register has no defined meaning. It is just +- * a way of turning of the level triggered interrupt. +- */ +- isr = sdio_readb(func, SMSSDIO_INT, &ret); +- if (ret) { +- sms_err("Unable to read interrupt register!\n"); +- return; +- } +- +- if (smsdev->split_cb == NULL) { +- cb = smscore_getbuffer(smsdev->coredev); +- if (!cb) { +- sms_err("Unable to allocate data buffer!\n"); +- return; +- } +- +- ret = sdio_memcpy_fromio(smsdev->func, +- cb->p, +- SMSSDIO_DATA, +- SMSSDIO_BLOCK_SIZE); +- if (ret) { +- sms_err("Error %d reading initial block!\n", ret); +- return; +- } +- +- hdr = cb->p; +- +- if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { +- smsdev->split_cb = cb; +- return; +- } +- +- if (hdr->msgLength > smsdev->func->cur_blksize) +- size = hdr->msgLength - smsdev->func->cur_blksize; +- else +- size = 0; +- } else { +- cb = smsdev->split_cb; +- hdr = cb->p; +- +- size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); +- +- smsdev->split_cb = NULL; +- } +- +- if (size) { +- void *buffer; +- +- buffer = cb->p + (hdr->msgLength - size); +- size = ALIGN(size, SMSSDIO_BLOCK_SIZE); +- +- BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); +- +- /* +- * First attempt to transfer all of it in one go... +- */ +- ret = sdio_memcpy_fromio(smsdev->func, +- buffer, +- SMSSDIO_DATA, +- size); +- if (ret && ret != -EINVAL) { +- smscore_putbuffer(smsdev->coredev, cb); +- sms_err("Error %d reading data from card!\n", ret); +- return; +- } +- +- /* +- * ..then fall back to one block at a time if that is +- * not possible... +- * +- * (we have to do this manually because of the +- * problem with the "increase address" bit) +- */ +- if (ret == -EINVAL) { +- while (size) { +- ret = sdio_memcpy_fromio(smsdev->func, +- buffer, SMSSDIO_DATA, +- smsdev->func->cur_blksize); +- if (ret) { +- smscore_putbuffer(smsdev->coredev, cb); +- sms_err("Error %d reading " +- "data from card!\n", ret); +- return; +- } +- +- buffer += smsdev->func->cur_blksize; +- if (size > smsdev->func->cur_blksize) +- size -= smsdev->func->cur_blksize; +- else +- size = 0; +- } +- } +- } +- +- cb->size = hdr->msgLength; +- cb->offset = 0; +- +- smscore_onresponse(smsdev->coredev, cb); +-} +- +-static int __devinit smssdio_probe(struct sdio_func *func, +- const struct sdio_device_id *id) +-{ +- int ret; +- +- int board_id; +- struct smssdio_device *smsdev; +- struct smsdevice_params_t params; +- +- board_id = id->driver_data; +- +- smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); +- if (!smsdev) +- return -ENOMEM; +- +- smsdev->func = func; +- +- memset(¶ms, 0, sizeof(struct smsdevice_params_t)); +- +- params.device = &func->dev; +- params.buffer_size = 0x5000; /* ?? */ +- params.num_buffers = 22; /* ?? */ +- params.context = smsdev; +- +- snprintf(params.devpath, sizeof(params.devpath), +- "sdio\\%s", sdio_func_id(func)); +- +- params.sendrequest_handler = smssdio_sendrequest; +- +- params.device_type = sms_get_board(board_id)->type; +- +- if (params.device_type != SMS_STELLAR) +- params.flags |= SMS_DEVICE_FAMILY2; +- else { +- /* +- * FIXME: Stellar needs special handling... +- */ +- ret = -ENODEV; +- goto free; +- } +- +- ret = smscore_register_device(¶ms, &smsdev->coredev); +- if (ret < 0) +- goto free; +- +- smscore_set_board_id(smsdev->coredev, board_id); +- +- sdio_claim_host(func); +- +- ret = sdio_enable_func(func); +- if (ret) +- goto release; +- +- ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); +- if (ret) +- goto disable; +- +- ret = sdio_claim_irq(func, smssdio_interrupt); +- if (ret) +- goto disable; +- +- sdio_set_drvdata(func, smsdev); +- +- sdio_release_host(func); +- +- ret = smscore_start_device(smsdev->coredev); +- if (ret < 0) +- goto reclaim; +- +- return 0; +- +-reclaim: +- sdio_claim_host(func); +- sdio_release_irq(func); +-disable: +- sdio_disable_func(func); +-release: +- sdio_release_host(func); +- smscore_unregister_device(smsdev->coredev); +-free: +- kfree(smsdev); +- +- return ret; +-} +- +-static void smssdio_remove(struct sdio_func *func) +-{ +- struct smssdio_device *smsdev; +- +- smsdev = sdio_get_drvdata(func); +- +- /* FIXME: racy! */ +- if (smsdev->split_cb) +- smscore_putbuffer(smsdev->coredev, smsdev->split_cb); +- +- smscore_unregister_device(smsdev->coredev); +- +- sdio_claim_host(func); +- sdio_release_irq(func); +- sdio_disable_func(func); +- sdio_release_host(func); +- +- kfree(smsdev); +-} +- +-static struct sdio_driver smssdio_driver = { +- .name = "smssdio", +- .id_table = smssdio_ids, +- .probe = smssdio_probe, +- .remove = smssdio_remove, +-}; +- +-/*******************************************************************/ +-/* Module functions */ +-/*******************************************************************/ +- +-static int __init smssdio_module_init(void) +-{ +- int ret = 0; +- +- printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); +- printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); +- +- ret = sdio_register_driver(&smssdio_driver); +- +- return ret; +-} +- +-static void __exit smssdio_module_exit(void) +-{ +- sdio_unregister_driver(&smssdio_driver); +-} +- +-module_init(smssdio_module_init); +-module_exit(smssdio_module_exit); +- +-MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); +-MODULE_AUTHOR("Pierre Ossman"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c +deleted file mode 100644 +index b1fe513..0000000 +--- a/drivers/media/dvb/siano/smsusb.c ++++ /dev/null +@@ -1,564 +0,0 @@ +-/**************************************************************** +- +-Siano Mobile Silicon, Inc. +-MDTV receiver kernel modules. +-Copyright (C) 2005-2009, 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 . +- +-****************************************************************/ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "smscoreapi.h" +-#include "sms-cards.h" +-#include "smsendian.h" +- +-static int sms_dbg; +-module_param_named(debug, sms_dbg, int, 0644); +-MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))"); +- +-#define USB1_BUFFER_SIZE 0x1000 +-#define USB2_BUFFER_SIZE 0x4000 +- +-#define MAX_BUFFERS 50 +-#define MAX_URBS 10 +- +-struct smsusb_device_t; +- +-struct smsusb_urb_t { +- struct smscore_buffer_t *cb; +- struct smsusb_device_t *dev; +- +- struct urb urb; +-}; +- +-struct smsusb_device_t { +- struct usb_device *udev; +- struct smscore_device_t *coredev; +- +- struct smsusb_urb_t surbs[MAX_URBS]; +- +- int response_alignment; +- int buffer_size; +-}; +- +-static int smsusb_submit_urb(struct smsusb_device_t *dev, +- struct smsusb_urb_t *surb); +- +-static void smsusb_onresponse(struct urb *urb) +-{ +- struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context; +- struct smsusb_device_t *dev = surb->dev; +- +- if (urb->status == -ESHUTDOWN) { +- sms_err("error, urb status %d (-ESHUTDOWN), %d bytes", +- urb->status, urb->actual_length); +- return; +- } +- +- if ((urb->actual_length > 0) && (urb->status == 0)) { +- struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p; +- +- smsendian_handle_message_header(phdr); +- if (urb->actual_length >= phdr->msgLength) { +- surb->cb->size = phdr->msgLength; +- +- if (dev->response_alignment && +- (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) { +- +- surb->cb->offset = +- dev->response_alignment + +- ((phdr->msgFlags >> 8) & 3); +- +- /* sanity check */ +- if (((int) phdr->msgLength + +- surb->cb->offset) > urb->actual_length) { +- sms_err("invalid response " +- "msglen %d offset %d " +- "size %d", +- phdr->msgLength, +- surb->cb->offset, +- urb->actual_length); +- goto exit_and_resubmit; +- } +- +- /* move buffer pointer and +- * copy header to its new location */ +- memcpy((char *) phdr + surb->cb->offset, +- phdr, sizeof(struct SmsMsgHdr_ST)); +- } else +- surb->cb->offset = 0; +- +- smscore_onresponse(dev->coredev, surb->cb); +- surb->cb = NULL; +- } else { +- sms_err("invalid response " +- "msglen %d actual %d", +- phdr->msgLength, urb->actual_length); +- } +- } else +- sms_err("error, urb status %d, %d bytes", +- urb->status, urb->actual_length); +- +- +-exit_and_resubmit: +- smsusb_submit_urb(dev, surb); +-} +- +-static int smsusb_submit_urb(struct smsusb_device_t *dev, +- struct smsusb_urb_t *surb) +-{ +- if (!surb->cb) { +- surb->cb = smscore_getbuffer(dev->coredev); +- if (!surb->cb) { +- sms_err("smscore_getbuffer(...) returned NULL"); +- return -ENOMEM; +- } +- } +- +- usb_fill_bulk_urb( +- &surb->urb, +- dev->udev, +- usb_rcvbulkpipe(dev->udev, 0x81), +- surb->cb->p, +- dev->buffer_size, +- smsusb_onresponse, +- surb +- ); +- surb->urb.transfer_dma = surb->cb->phys; +- surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +- +- return usb_submit_urb(&surb->urb, GFP_ATOMIC); +-} +- +-static void smsusb_stop_streaming(struct smsusb_device_t *dev) +-{ +- int i; +- +- for (i = 0; i < MAX_URBS; i++) { +- usb_kill_urb(&dev->surbs[i].urb); +- +- if (dev->surbs[i].cb) { +- smscore_putbuffer(dev->coredev, dev->surbs[i].cb); +- dev->surbs[i].cb = NULL; +- } +- } +-} +- +-static int smsusb_start_streaming(struct smsusb_device_t *dev) +-{ +- int i, rc; +- +- for (i = 0; i < MAX_URBS; i++) { +- rc = smsusb_submit_urb(dev, &dev->surbs[i]); +- if (rc < 0) { +- sms_err("smsusb_submit_urb(...) failed"); +- smsusb_stop_streaming(dev); +- break; +- } +- } +- +- return rc; +-} +- +-static int smsusb_sendrequest(void *context, void *buffer, size_t size) +-{ +- struct smsusb_device_t *dev = (struct smsusb_device_t *) context; +- int dummy; +- +- smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer); +- return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2), +- buffer, size, &dummy, 1000); +-} +- +-static char *smsusb1_fw_lkup[] = { +- "dvbt_stellar_usb.inp", +- "dvbh_stellar_usb.inp", +- "tdmb_stellar_usb.inp", +- "none", +- "dvbt_bda_stellar_usb.inp", +-}; +- +-static inline char *sms_get_fw_name(int mode, int board_id) +-{ +- char **fw = sms_get_board(board_id)->fw; +- return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode]; +-} +- +-static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id) +-{ +- const struct firmware *fw; +- u8 *fw_buffer; +- int rc, dummy; +- char *fw_filename; +- +- if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) { +- sms_err("invalid firmware id specified %d", id); +- return -EINVAL; +- } +- +- fw_filename = sms_get_fw_name(id, board_id); +- +- rc = request_firmware(&fw, fw_filename, &udev->dev); +- if (rc < 0) { +- sms_warn("failed to open \"%s\" mode %d, " +- "trying again with default firmware", fw_filename, id); +- +- fw_filename = smsusb1_fw_lkup[id]; +- rc = request_firmware(&fw, fw_filename, &udev->dev); +- if (rc < 0) { +- sms_warn("failed to open \"%s\" mode %d", +- fw_filename, id); +- +- return rc; +- } +- } +- +- fw_buffer = kmalloc(fw->size, GFP_KERNEL); +- if (fw_buffer) { +- memcpy(fw_buffer, fw->data, fw->size); +- +- rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2), +- fw_buffer, fw->size, &dummy, 1000); +- +- sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc); +- +- kfree(fw_buffer); +- } else { +- sms_err("failed to allocate firmware buffer"); +- rc = -ENOMEM; +- } +- sms_info("read FW %s, size=%zd", fw_filename, fw->size); +- +- release_firmware(fw); +- +- return rc; +-} +- +-static void smsusb1_detectmode(void *context, int *mode) +-{ +- char *product_string = +- ((struct smsusb_device_t *) context)->udev->product; +- +- *mode = DEVICE_MODE_NONE; +- +- if (!product_string) { +- product_string = "none"; +- sms_err("product string not found"); +- } else if (strstr(product_string, "DVBH")) +- *mode = 1; +- else if (strstr(product_string, "BDA")) +- *mode = 4; +- else if (strstr(product_string, "DVBT")) +- *mode = 0; +- else if (strstr(product_string, "TDMB")) +- *mode = 2; +- +- sms_info("%d \"%s\"", *mode, product_string); +-} +- +-static int smsusb1_setmode(void *context, int mode) +-{ +- struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK, +- sizeof(struct SmsMsgHdr_ST), 0 }; +- +- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) { +- sms_err("invalid firmware id specified %d", mode); +- return -EINVAL; +- } +- +- return smsusb_sendrequest(context, &Msg, sizeof(Msg)); +-} +- +-static void smsusb_term_device(struct usb_interface *intf) +-{ +- struct smsusb_device_t *dev = usb_get_intfdata(intf); +- +- if (dev) { +- smsusb_stop_streaming(dev); +- +- /* unregister from smscore */ +- if (dev->coredev) +- smscore_unregister_device(dev->coredev); +- +- sms_info("device %p destroyed", dev); +- kfree(dev); +- } +- +- usb_set_intfdata(intf, NULL); +-} +- +-static int smsusb_init_device(struct usb_interface *intf, int board_id) +-{ +- struct smsdevice_params_t params; +- struct smsusb_device_t *dev; +- int i, rc; +- +- /* create device object */ +- dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL); +- if (!dev) { +- sms_err("kzalloc(sizeof(struct smsusb_device_t) failed"); +- return -ENOMEM; +- } +- +- memset(¶ms, 0, sizeof(params)); +- usb_set_intfdata(intf, dev); +- dev->udev = interface_to_usbdev(intf); +- +- params.device_type = sms_get_board(board_id)->type; +- +- switch (params.device_type) { +- case SMS_STELLAR: +- dev->buffer_size = USB1_BUFFER_SIZE; +- +- params.setmode_handler = smsusb1_setmode; +- params.detectmode_handler = smsusb1_detectmode; +- break; +- default: +- sms_err("Unspecified sms device type!"); +- /* fall-thru */ +- case SMS_NOVA_A0: +- case SMS_NOVA_B0: +- case SMS_VEGA: +- dev->buffer_size = USB2_BUFFER_SIZE; +- dev->response_alignment = +- le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) - +- sizeof(struct SmsMsgHdr_ST); +- +- params.flags |= SMS_DEVICE_FAMILY2; +- break; +- } +- +- params.device = &dev->udev->dev; +- params.buffer_size = dev->buffer_size; +- params.num_buffers = MAX_BUFFERS; +- params.sendrequest_handler = smsusb_sendrequest; +- params.context = dev; +- usb_make_path(dev->udev, params.devpath, sizeof(params.devpath)); +- +- /* register in smscore */ +- rc = smscore_register_device(¶ms, &dev->coredev); +- if (rc < 0) { +- sms_err("smscore_register_device(...) failed, rc %d", rc); +- smsusb_term_device(intf); +- return rc; +- } +- +- smscore_set_board_id(dev->coredev, board_id); +- +- /* initialize urbs */ +- for (i = 0; i < MAX_URBS; i++) { +- dev->surbs[i].dev = dev; +- usb_init_urb(&dev->surbs[i].urb); +- } +- +- sms_info("smsusb_start_streaming(...)."); +- rc = smsusb_start_streaming(dev); +- if (rc < 0) { +- sms_err("smsusb_start_streaming(...) failed"); +- smsusb_term_device(intf); +- return rc; +- } +- +- rc = smscore_start_device(dev->coredev); +- if (rc < 0) { +- sms_err("smscore_start_device(...) failed"); +- smsusb_term_device(intf); +- return rc; +- } +- +- sms_info("device %p created", dev); +- +- return rc; +-} +- +-static int __devinit smsusb_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct usb_device *udev = interface_to_usbdev(intf); +- char devpath[32]; +- int i, rc; +- +- rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); +- rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); +- +- if (intf->num_altsetting > 0) { +- rc = usb_set_interface( +- udev, intf->cur_altsetting->desc.bInterfaceNumber, 0); +- if (rc < 0) { +- sms_err("usb_set_interface failed, rc %d", rc); +- return rc; +- } +- } +- +- sms_info("smsusb_probe %d", +- intf->cur_altsetting->desc.bInterfaceNumber); +- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) +- sms_info("endpoint %d %02x %02x %d", i, +- intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, +- intf->cur_altsetting->endpoint[i].desc.bmAttributes, +- intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); +- +- if ((udev->actconfig->desc.bNumInterfaces == 2) && +- (intf->cur_altsetting->desc.bInterfaceNumber == 0)) { +- sms_err("rom interface 0 is not used"); +- return -ENODEV; +- } +- +- if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { +- snprintf(devpath, sizeof(devpath), "usb\\%d-%s", +- udev->bus->busnum, udev->devpath); +- sms_info("stellar device was found."); +- return smsusb1_load_firmware( +- udev, smscore_registry_getmode(devpath), +- id->driver_info); +- } +- +- rc = smsusb_init_device(intf, id->driver_info); +- sms_info("rc %d", rc); +- sms_board_load_modules(id->driver_info); +- return rc; +-} +- +-static void smsusb_disconnect(struct usb_interface *intf) +-{ +- smsusb_term_device(intf); +-} +- +-static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg) +-{ +- struct smsusb_device_t *dev = usb_get_intfdata(intf); +- printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event); +- smsusb_stop_streaming(dev); +- return 0; +-} +- +-static int smsusb_resume(struct usb_interface *intf) +-{ +- int rc, i; +- struct smsusb_device_t *dev = usb_get_intfdata(intf); +- struct usb_device *udev = interface_to_usbdev(intf); +- +- printk(KERN_INFO "%s: Entering.\n", __func__); +- usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81)); +- usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02)); +- +- for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) +- printk(KERN_INFO "endpoint %d %02x %02x %d\n", i, +- intf->cur_altsetting->endpoint[i].desc.bEndpointAddress, +- intf->cur_altsetting->endpoint[i].desc.bmAttributes, +- intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize); +- +- if (intf->num_altsetting > 0) { +- rc = usb_set_interface(udev, +- intf->cur_altsetting->desc. +- bInterfaceNumber, 0); +- if (rc < 0) { +- printk(KERN_INFO "%s usb_set_interface failed, " +- "rc %d\n", __func__, rc); +- return rc; +- } +- } +- +- smsusb_start_streaming(dev); +- return 0; +-} +- +-static const struct usb_device_id smsusb_id_table[] __devinitconst = { +- { USB_DEVICE(0x187f, 0x0010), +- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, +- { USB_DEVICE(0x187f, 0x0100), +- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR }, +- { USB_DEVICE(0x187f, 0x0200), +- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A }, +- { USB_DEVICE(0x187f, 0x0201), +- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B }, +- { USB_DEVICE(0x187f, 0x0300), +- .driver_info = SMS1XXX_BOARD_SIANO_VEGA }, +- { USB_DEVICE(0x2040, 0x1700), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT }, +- { USB_DEVICE(0x2040, 0x1800), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A }, +- { USB_DEVICE(0x2040, 0x1801), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B }, +- { USB_DEVICE(0x2040, 0x2000), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, +- { USB_DEVICE(0x2040, 0x2009), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 }, +- { USB_DEVICE(0x2040, 0x200a), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, +- { USB_DEVICE(0x2040, 0x2010), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, +- { USB_DEVICE(0x2040, 0x2011), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, +- { USB_DEVICE(0x2040, 0x2019), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD }, +- { USB_DEVICE(0x2040, 0x5500), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0x5510), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0x5520), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0x5530), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0x5580), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0x5590), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x187f, 0x0202), +- .driver_info = SMS1XXX_BOARD_SIANO_NICE }, +- { USB_DEVICE(0x187f, 0x0301), +- .driver_info = SMS1XXX_BOARD_SIANO_VENICE }, +- { USB_DEVICE(0x2040, 0xb900), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xb910), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xb980), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xb990), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xc000), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xc010), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xc080), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { USB_DEVICE(0x2040, 0xc090), +- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, +- { } /* Terminating entry */ +- }; +- +-MODULE_DEVICE_TABLE(usb, smsusb_id_table); +- +-static struct usb_driver smsusb_driver = { +- .name = "smsusb", +- .probe = smsusb_probe, +- .disconnect = smsusb_disconnect, +- .id_table = smsusb_id_table, +- +- .suspend = smsusb_suspend, +- .resume = smsusb_resume, +-}; +- +-module_usb_driver(smsusb_driver); +- +-MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle"); +-MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig +deleted file mode 100644 +index 9d83ced..0000000 +--- a/drivers/media/dvb/ttpci/Kconfig ++++ /dev/null +@@ -1,159 +0,0 @@ +-config TTPCI_EEPROM +- tristate +- depends on I2C +- default n +- +-config DVB_AV7110 +- tristate "AV7110 cards" +- depends on DVB_CORE && PCI && I2C +- select TTPCI_EEPROM +- select VIDEO_SAA7146_VV +- depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV +- select DVB_VES1820 if !DVB_FE_CUSTOMISE +- select DVB_VES1X93 if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_TDA8083 if !DVB_FE_CUSTOMISE +- select DVB_SP8870 if !DVB_FE_CUSTOMISE +- select DVB_STV0297 if !DVB_FE_CUSTOMISE +- select DVB_L64781 if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- help +- Support for SAA7146 and AV7110 based DVB cards as produced +- by Fujitsu-Siemens, Technotrend, Hauppauge and others. +- +- This driver only supports the fullfeatured cards with +- onboard MPEG2 decoder. +- +- This driver needs an external firmware. Please use the script +- "/Documentation/dvb/get_dvb_firmware av7110" to +- download/extract it, and then copy it to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +- Alternatively, you can download the file and use the kernel's +- EXTRA_FIRMWARE configuration option to build it into your +- kernel image by adding the filename to the EXTRA_FIRMWARE +- configuration option string. +- +- Say Y if you own such a card and want to use it. +- +-config DVB_AV7110_OSD +- bool "AV7110 OSD support" +- depends on DVB_AV7110 +- default y if DVB_AV7110=y || DVB_AV7110=m +- help +- The AV7110 firmware provides some code to generate an OnScreenDisplay +- on the video output. This is kind of nonstandard and not guaranteed to +- be maintained. +- +- Anyway, some popular DVB software like VDR uses this OSD to render +- its menus, so say Y if you want to use this software. +- +- All other people say N. +- +-config DVB_BUDGET_CORE +- tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)" +- depends on DVB_CORE && PCI && I2C +- select VIDEO_SAA7146 +- select TTPCI_EEPROM +- help +- Support for simple SAA7146 based DVB cards +- (so called Budget- or Nova-PCI cards) without onboard +- MPEG2 decoder. +- +-config DVB_BUDGET +- tristate "Budget cards" +- depends on DVB_BUDGET_CORE && I2C +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_VES1X93 if !DVB_FE_CUSTOMISE +- select DVB_VES1820 if !DVB_FE_CUSTOMISE +- select DVB_L64781 if !DVB_FE_CUSTOMISE +- select DVB_TDA8083 if !DVB_FE_CUSTOMISE +- select DVB_S5H1420 if !DVB_FE_CUSTOMISE +- select DVB_TDA10086 if !DVB_FE_CUSTOMISE +- select DVB_TDA826X if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_TDA1004X if !DVB_FE_CUSTOMISE +- select DVB_ISL6423 if !DVB_FE_CUSTOMISE +- select DVB_STV090x if !DVB_FE_CUSTOMISE +- select DVB_STV6110x if !DVB_FE_CUSTOMISE +- help +- Support for simple SAA7146 based DVB cards (so called Budget- +- or Nova-PCI cards) without onboard MPEG2 decoder, and without +- analog inputs or an onboard Common Interface connector. +- +- Say Y if you own such a card and want to use it. +- +- To compile this driver as a module, choose M here: the +- module will be called budget. +- +-config DVB_BUDGET_CI +- tristate "Budget cards with onboard CI connector" +- depends on DVB_BUDGET_CORE && I2C +- select DVB_STV0297 if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_TDA1004X if !DVB_FE_CUSTOMISE +- select DVB_STB0899 if !DVB_FE_CUSTOMISE +- select DVB_STB6100 if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- select DVB_STV0288 if !DVB_FE_CUSTOMISE +- select DVB_STB6000 if !DVB_FE_CUSTOMISE +- select DVB_TDA10023 if !DVB_FE_CUSTOMISE +- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE +- depends on RC_CORE +- help +- Support for simple SAA7146 based DVB cards +- (so called Budget- or Nova-PCI cards) without onboard +- MPEG2 decoder, but with onboard Common Interface connector. +- +- Note: The Common Interface is not yet supported by this driver +- due to lack of information from the vendor. +- +- Say Y if you own such a card and want to use it. +- +- To compile this driver as a module, choose M here: the +- module will be called budget-ci. +- +-config DVB_BUDGET_AV +- tristate "Budget cards with analog video inputs" +- depends on DVB_BUDGET_CORE && I2C +- select VIDEO_SAA7146_VV +- depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV +- select DVB_PLL if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_TDA1004X if !DVB_FE_CUSTOMISE +- select DVB_TDA10021 if !DVB_FE_CUSTOMISE +- select DVB_TDA10023 if !DVB_FE_CUSTOMISE +- select DVB_STB0899 if !DVB_FE_CUSTOMISE +- select DVB_TDA8261 if !DVB_FE_CUSTOMISE +- select DVB_TUA6100 if !DVB_FE_CUSTOMISE +- help +- Support for simple SAA7146 based DVB cards +- (so called Budget- or Nova-PCI cards) without onboard +- MPEG2 decoder, but with one or more analog video inputs. +- +- Say Y if you own such a card and want to use it. +- +- To compile this driver as a module, choose M here: the +- module will be called budget-av. +- +-config DVB_BUDGET_PATCH +- tristate "AV7110 cards with Budget Patch" +- depends on DVB_BUDGET_CORE && I2C +- depends on DVB_AV7110 +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_VES1X93 if !DVB_FE_CUSTOMISE +- select DVB_TDA8083 if !DVB_FE_CUSTOMISE +- help +- Support for Budget Patch (full TS) modification on +- SAA7146+AV7110 based cards (DVB-S cards). This +- driver doesn't use onboard MPEG2 decoder. The +- card is driven in Budget-only mode. Card is +- required to have loaded firmware to tune properly. +- Firmware can be loaded by insertion and removal of +- standard AV7110 driver prior to loading this +- driver. +- +- Say Y if you own such a card and want to use it. +- +- To compile this driver as a module, choose M here: the +- module will be called budget-patch. +diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile +deleted file mode 100644 +index f6e8693..0000000 +--- a/drivers/media/dvb/ttpci/Makefile ++++ /dev/null +@@ -1,21 +0,0 @@ +-# +-# Makefile for the kernel SAA7146 FULL TS DVB device driver +-# and the AV7110 DVB device driver +-# +- +-dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o +- +-ifdef CONFIG_INPUT_EVDEV +-dvb-ttpci-objs += av7110_ir.o +-endif +- +-obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o +-obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o +-obj-$(CONFIG_DVB_BUDGET) += budget.o +-obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o +-obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o +-obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o +-obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +-ccflags-y += -Idrivers/media/common/tuners +diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c +deleted file mode 100644 +index 6ecbcf6..0000000 +--- a/drivers/media/dvb/ttpci/av7110.c ++++ /dev/null +@@ -1,2940 +0,0 @@ +-/* +- * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) +- * av7110.c: initialization and demux stuff +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * originally based on code by: +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include +- +-#include "dvb_frontend.h" +- +-#include "ttpci-eeprom.h" +-#include "av7110.h" +-#include "av7110_hw.h" +-#include "av7110_av.h" +-#include "av7110_ca.h" +-#include "av7110_ipack.h" +- +-#include "bsbe1.h" +-#include "lnbp21.h" +-#include "bsru6.h" +- +-#define TS_WIDTH 376 +-#define TS_HEIGHT 512 +-#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) +-#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) +- +- +-int av7110_debug; +- +-static int vidmode = CVBS_RGB_OUT; +-static int pids_off; +-static int adac = DVB_ADAC_TI; +-static int hw_sections; +-static int rgb_on; +-static int volume = 255; +-static int budgetpatch; +-static int wss_cfg_4_3 = 0x4008; +-static int wss_cfg_16_9 = 0x0007; +-static int tv_standard; +-static int full_ts; +- +-module_param_named(debug, av7110_debug, int, 0644); +-MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); +-module_param(vidmode, int, 0444); +-MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); +-module_param(pids_off, int, 0444); +-MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); +-module_param(adac, int, 0444); +-MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); +-module_param(hw_sections, int, 0444); +-MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); +-module_param(rgb_on, int, 0444); +-MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control" +- " signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); +-module_param(volume, int, 0444); +-MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); +-module_param(budgetpatch, int, 0444); +-MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); +-module_param(full_ts, int, 0444); +-MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); +-module_param(wss_cfg_4_3, int, 0444); +-MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); +-module_param(wss_cfg_16_9, int, 0444); +-MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); +-module_param(tv_standard, int, 0444); +-MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static void restart_feeds(struct av7110 *av7110); +-static int budget_start_feed(struct dvb_demux_feed *feed); +-static int budget_stop_feed(struct dvb_demux_feed *feed); +- +-static int av7110_num; +- +-#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ +-{\ +- if (fe_func != NULL) { \ +- av7110_copy = fe_func; \ +- fe_func = av7110_func; \ +- } \ +-} +- +- +-static void init_av7110_av(struct av7110 *av7110) +-{ +- int ret; +- struct saa7146_dev *dev = av7110->dev; +- +- /* set internal volume control to maximum */ +- av7110->adac_type = DVB_ADAC_TI; +- ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); +- if (ret < 0) +- printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); +- +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, +- 1, (u16) av7110->display_ar); +- if (ret < 0) +- printk("dvb-ttpci: unable to set aspect ratio\n"); +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, +- 1, av7110->display_panscan); +- if (ret < 0) +- printk("dvb-ttpci: unable to set pan scan\n"); +- +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); +- if (ret < 0) +- printk("dvb-ttpci: unable to configure 4:3 wss\n"); +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); +- if (ret < 0) +- printk("dvb-ttpci: unable to configure 16:9 wss\n"); +- +- ret = av7710_set_video_mode(av7110, vidmode); +- if (ret < 0) +- printk("dvb-ttpci:cannot set video mode:%d\n",ret); +- +- /* handle different card types */ +- /* remaining inits according to card and frontend type */ +- av7110->analog_tuner_flags = 0; +- av7110->current_input = 0; +- if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) +- av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on +- if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { +- printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", +- av7110->dvb_adapter.num); +- av7110->adac_type = DVB_ADAC_CRYSTAL; +- i2c_writereg(av7110, 0x20, 0x01, 0xd2); +- i2c_writereg(av7110, 0x20, 0x02, 0x49); +- i2c_writereg(av7110, 0x20, 0x03, 0x00); +- i2c_writereg(av7110, 0x20, 0x04, 0x00); +- +- /** +- * some special handling for the Siemens DVB-C cards... +- */ +- } else if (0 == av7110_init_analog_module(av7110)) { +- /* done. */ +- } +- else if (dev->pci->subsystem_vendor == 0x110a) { +- printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", +- av7110->dvb_adapter.num); +- av7110->adac_type = DVB_ADAC_NONE; +- } +- else { +- av7110->adac_type = adac; +- printk("dvb-ttpci: adac type set to %d @ card %d\n", +- av7110->adac_type, av7110->dvb_adapter.num); +- } +- +- if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { +- // switch DVB SCART on +- ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); +- if (ret < 0) +- printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); +- ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); +- if (ret < 0) +- printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); +- if (rgb_on && +- ((av7110->dev->pci->subsystem_vendor == 0x110a) || +- (av7110->dev->pci->subsystem_vendor == 0x13c2)) && +- (av7110->dev->pci->subsystem_device == 0x0000)) { +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 +- //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 +- } +- } +- +- if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) +- av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on +- +- ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); +- if (ret < 0) +- printk("dvb-ttpci:cannot set volume :%d\n",ret); +-} +- +-static void recover_arm(struct av7110 *av7110) +-{ +- dprintk(4, "%p\n",av7110); +- +- av7110_bootarm(av7110); +- msleep(100); +- +- init_av7110_av(av7110); +- +- /* card-specific recovery */ +- if (av7110->recover) +- av7110->recover(av7110); +- +- restart_feeds(av7110); +- +-#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +- av7110_check_ir_config(av7110, true); +-#endif +-} +- +-static void av7110_arm_sync(struct av7110 *av7110) +-{ +- if (av7110->arm_thread) +- kthread_stop(av7110->arm_thread); +- +- av7110->arm_thread = NULL; +-} +- +-static int arm_thread(void *data) +-{ +- struct av7110 *av7110 = data; +- u16 newloops = 0; +- int timeout; +- +- dprintk(4, "%p\n",av7110); +- +- for (;;) { +- timeout = wait_event_interruptible_timeout(av7110->arm_wait, +- kthread_should_stop(), 5 * HZ); +- +- if (-ERESTARTSYS == timeout || kthread_should_stop()) { +- /* got signal or told to quit*/ +- break; +- } +- +- if (!av7110->arm_ready) +- continue; +- +-#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +- av7110_check_ir_config(av7110, false); +-#endif +- +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- break; +- newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); +- mutex_unlock(&av7110->dcomlock); +- +- if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { +- printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", +- av7110->dvb_adapter.num); +- +- recover_arm(av7110); +- +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- break; +- newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; +- mutex_unlock(&av7110->dcomlock); +- } +- av7110->arm_loops = newloops; +- av7110->arm_errors = 0; +- } +- +- return 0; +-} +- +- +-/**************************************************************************** +- * IRQ handling +- ****************************************************************************/ +- +-static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, +- u8 *buffer2, size_t buffer2_len, +- struct dvb_demux_filter *dvbdmxfilter, +- enum dmx_success success, +- struct av7110 *av7110) +-{ +- if (!dvbdmxfilter->feed->demux->dmx.frontend) +- return 0; +- if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) +- return 0; +- +- switch (dvbdmxfilter->type) { +- case DMX_TYPE_SEC: +- if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) +- return 0; +- if (dvbdmxfilter->doneq) { +- struct dmx_section_filter *filter = &dvbdmxfilter->filter; +- int i; +- u8 xor, neq = 0; +- +- for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { +- xor = filter->filter_value[i] ^ buffer1[i]; +- neq |= dvbdmxfilter->maskandnotmode[i] & xor; +- } +- if (!neq) +- return 0; +- } +- return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, +- buffer2, buffer2_len, +- &dvbdmxfilter->filter, +- DMX_OK); +- case DMX_TYPE_TS: +- if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) +- return 0; +- if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) +- return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, +- buffer2, buffer2_len, +- &dvbdmxfilter->feed->feed.ts, +- DMX_OK); +- else +- av7110_p2t_write(buffer1, buffer1_len, +- dvbdmxfilter->feed->pid, +- &av7110->p2t_filter[dvbdmxfilter->index]); +- default: +- return 0; +- } +-} +- +- +-//#define DEBUG_TIMING +-static inline void print_time(char *s) +-{ +-#ifdef DEBUG_TIMING +- struct timeval tv; +- do_gettimeofday(&tv); +- printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec); +-#endif +-} +- +-#define DEBI_READ 0 +-#define DEBI_WRITE 1 +-static inline void start_debi_dma(struct av7110 *av7110, int dir, +- unsigned long addr, unsigned int len) +-{ +- dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); +- if (saa7146_wait_for_debi_done(av7110->dev, 0)) { +- printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); +- return; +- } +- +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ +- SAA7146_IER_ENABLE(av7110->dev, MASK_19); +- if (len < 5) +- len = 5; /* we want a real DEBI DMA */ +- if (dir == DEBI_WRITE) +- iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); +- else +- irdebi(av7110, DEBISWAB, addr, 0, len); +-} +- +-static void debiirq(unsigned long cookie) +-{ +- struct av7110 *av7110 = (struct av7110 *)cookie; +- int type = av7110->debitype; +- int handle = (type >> 8) & 0x1f; +- unsigned int xfer = 0; +- +- print_time("debi"); +- dprintk(4, "type 0x%04x\n", type); +- +- if (type == -1) { +- printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", +- jiffies, saa7146_read(av7110->dev, PSR), +- saa7146_read(av7110->dev, SSR)); +- goto debi_done; +- } +- av7110->debitype = -1; +- +- switch (type & 0xff) { +- +- case DATA_TS_RECORD: +- dvb_dmx_swfilter_packets(&av7110->demux, +- (const u8 *) av7110->debi_virt, +- av7110->debilen / 188); +- xfer = RX_BUFF; +- break; +- +- case DATA_PES_RECORD: +- if (av7110->demux.recording) +- av7110_record_cb(&av7110->p2t[handle], +- (u8 *) av7110->debi_virt, +- av7110->debilen); +- xfer = RX_BUFF; +- break; +- +- case DATA_IPMPE: +- case DATA_FSECTION: +- case DATA_PIPING: +- if (av7110->handle2filter[handle]) +- DvbDmxFilterCallback((u8 *)av7110->debi_virt, +- av7110->debilen, NULL, 0, +- av7110->handle2filter[handle], +- DMX_OK, av7110); +- xfer = RX_BUFF; +- break; +- +- case DATA_CI_GET: +- { +- u8 *data = av7110->debi_virt; +- +- if ((data[0] < 2) && data[2] == 0xff) { +- int flags = 0; +- if (data[5] > 0) +- flags |= CA_CI_MODULE_PRESENT; +- if (data[5] > 5) +- flags |= CA_CI_MODULE_READY; +- av7110->ci_slot[data[0]].flags = flags; +- } else +- ci_get_data(&av7110->ci_rbuffer, +- av7110->debi_virt, +- av7110->debilen); +- xfer = RX_BUFF; +- break; +- } +- +- case DATA_COMMON_INTERFACE: +- CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); +-#if 0 +- { +- int i; +- +- printk("av7110%d: ", av7110->num); +- printk("%02x ", *(u8 *)av7110->debi_virt); +- printk("%02x ", *(1+(u8 *)av7110->debi_virt)); +- for (i = 2; i < av7110->debilen; i++) +- printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt))); +- for (i = 2; i < av7110->debilen; i++) +- printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt))); +- +- printk("\n"); +- } +-#endif +- xfer = RX_BUFF; +- break; +- +- case DATA_DEBUG_MESSAGE: +- ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; +- printk("%s\n", (s8 *) av7110->debi_virt); +- xfer = RX_BUFF; +- break; +- +- case DATA_CI_PUT: +- dprintk(4, "debi DATA_CI_PUT\n"); +- case DATA_MPEG_PLAY: +- dprintk(4, "debi DATA_MPEG_PLAY\n"); +- case DATA_BMP_LOAD: +- dprintk(4, "debi DATA_BMP_LOAD\n"); +- xfer = TX_BUFF; +- break; +- default: +- break; +- } +-debi_done: +- spin_lock(&av7110->debilock); +- if (xfer) +- iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); +- ARM_ClearMailBox(av7110); +- spin_unlock(&av7110->debilock); +-} +- +-/* irq from av7110 firmware writing the mailbox register in the DPRAM */ +-static void gpioirq(unsigned long cookie) +-{ +- struct av7110 *av7110 = (struct av7110 *)cookie; +- u32 rxbuf, txbuf; +- int len; +- +- if (av7110->debitype != -1) +- /* we shouldn't get any irq while a debi xfer is running */ +- printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", +- jiffies, saa7146_read(av7110->dev, PSR), +- saa7146_read(av7110->dev, SSR)); +- +- if (saa7146_wait_for_debi_done(av7110->dev, 0)) { +- printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); +- BUG(); /* maybe we should try resetting the debi? */ +- } +- +- spin_lock(&av7110->debilock); +- ARM_ClearIrq(av7110); +- +- /* see what the av7110 wants */ +- av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); +- av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); +- txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- len = (av7110->debilen + 3) & ~3; +- +- print_time("gpio"); +- dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); +- +- switch (av7110->debitype & 0xff) { +- +- case DATA_TS_PLAY: +- case DATA_PES_PLAY: +- break; +- +- case DATA_MPEG_VIDEO_EVENT: +- { +- u32 h_ar; +- struct video_event event; +- +- av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); +- h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); +- +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); +- +- av7110->video_size.h = h_ar & 0xfff; +- +- event.type = VIDEO_EVENT_SIZE_CHANGED; +- event.u.size.w = av7110->video_size.w; +- event.u.size.h = av7110->video_size.h; +- switch ((h_ar >> 12) & 0xf) +- { +- case 3: +- av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; +- event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; +- av7110->videostate.video_format = VIDEO_FORMAT_16_9; +- break; +- case 4: +- av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; +- event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; +- av7110->videostate.video_format = VIDEO_FORMAT_221_1; +- break; +- default: +- av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; +- event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; +- av7110->videostate.video_format = VIDEO_FORMAT_4_3; +- } +- +- dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", +- av7110->video_size.w, av7110->video_size.h, +- av7110->video_size.aspect_ratio); +- +- dvb_video_add_event(av7110, &event); +- break; +- } +- +- case DATA_CI_PUT: +- { +- int avail; +- struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; +- +- avail = dvb_ringbuffer_avail(cibuf); +- if (avail <= 2) { +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- break; +- } +- len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; +- len |= DVB_RINGBUFFER_PEEK(cibuf, 1); +- if (avail < len + 2) { +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- break; +- } +- DVB_RINGBUFFER_SKIP(cibuf, 2); +- +- dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); +- +- iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); +- dprintk(8, "DMA: CI\n"); +- start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); +- spin_unlock(&av7110->debilock); +- wake_up(&cibuf->queue); +- return; +- } +- +- case DATA_MPEG_PLAY: +- if (!av7110->playing) { +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- break; +- } +- len = 0; +- if (av7110->debitype & 0x100) { +- spin_lock(&av7110->aout.lock); +- len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); +- spin_unlock(&av7110->aout.lock); +- } +- if (len <= 0 && (av7110->debitype & 0x200) +- &&av7110->videostate.play_state != VIDEO_FREEZED) { +- spin_lock(&av7110->avout.lock); +- len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); +- spin_unlock(&av7110->avout.lock); +- } +- if (len <= 0) { +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- break; +- } +- dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); +- dprintk(8, "DMA: MPEG_PLAY\n"); +- start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); +- spin_unlock(&av7110->debilock); +- return; +- +- case DATA_BMP_LOAD: +- len = av7110->debilen; +- dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); +- if (!len) { +- av7110->bmp_state = BMP_LOADED; +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); +- iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); +- wake_up(&av7110->bmpq); +- dprintk(8, "gpio DATA_BMP_LOAD done\n"); +- break; +- } +- if (len > av7110->bmplen) +- len = av7110->bmplen; +- if (len > 2 * 1024) +- len = 2 * 1024; +- iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); +- iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); +- memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); +- av7110->bmpp += len; +- av7110->bmplen -= len; +- dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); +- start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); +- spin_unlock(&av7110->debilock); +- return; +- +- case DATA_CI_GET: +- case DATA_COMMON_INTERFACE: +- case DATA_FSECTION: +- case DATA_IPMPE: +- case DATA_PIPING: +- if (!len || len > 4 * 1024) { +- iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); +- break; +- } +- /* fall through */ +- +- case DATA_TS_RECORD: +- case DATA_PES_RECORD: +- dprintk(8, "DMA: TS_REC etc.\n"); +- start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); +- spin_unlock(&av7110->debilock); +- return; +- +- case DATA_DEBUG_MESSAGE: +- if (!len || len > 0xff) { +- iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); +- break; +- } +- start_debi_dma(av7110, DEBI_READ, Reserved, len); +- spin_unlock(&av7110->debilock); +- return; +- +- case DATA_IRCOMMAND: +- if (av7110->ir.ir_handler) +- av7110->ir.ir_handler(av7110, +- swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); +- iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); +- break; +- +- default: +- printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", +- av7110->debitype, av7110->debilen); +- break; +- } +- av7110->debitype = -1; +- ARM_ClearMailBox(av7110); +- spin_unlock(&av7110->debilock); +-} +- +- +-#ifdef CONFIG_DVB_AV7110_OSD +-static int dvb_osd_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- +- dprintk(4, "%p\n", av7110); +- +- if (cmd == OSD_SEND_CMD) +- return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); +- if (cmd == OSD_GET_CAPABILITY) +- return av7110_osd_capability(av7110, (osd_cap_t *) parg); +- +- return -EINVAL; +-} +- +- +-static const struct file_operations dvb_osd_fops = { +- .owner = THIS_MODULE, +- .unlocked_ioctl = dvb_generic_ioctl, +- .open = dvb_generic_open, +- .release = dvb_generic_release, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_osd = { +- .priv = NULL, +- .users = 1, +- .writers = 1, +- .fops = &dvb_osd_fops, +- .kernel_ioctl = dvb_osd_ioctl, +-}; +-#endif /* CONFIG_DVB_AV7110_OSD */ +- +- +-static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, +- u16 subpid, u16 pcrpid) +-{ +- u16 aflags = 0; +- +- dprintk(4, "%p\n", av7110); +- +- if (vpid == 0x1fff || apid == 0x1fff || +- ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { +- vpid = apid = ttpid = subpid = pcrpid = 0; +- av7110->pids[DMX_PES_VIDEO] = 0; +- av7110->pids[DMX_PES_AUDIO] = 0; +- av7110->pids[DMX_PES_TELETEXT] = 0; +- av7110->pids[DMX_PES_PCR] = 0; +- } +- +- if (av7110->audiostate.bypass_mode) +- aflags |= 0x8000; +- +- return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, +- pcrpid, vpid, apid, ttpid, subpid, aflags); +-} +- +-int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, +- u16 subpid, u16 pcrpid) +-{ +- int ret = 0; +- dprintk(4, "%p\n", av7110); +- +- if (mutex_lock_interruptible(&av7110->pid_mutex)) +- return -ERESTARTSYS; +- +- if (!(vpid & 0x8000)) +- av7110->pids[DMX_PES_VIDEO] = vpid; +- if (!(apid & 0x8000)) +- av7110->pids[DMX_PES_AUDIO] = apid; +- if (!(ttpid & 0x8000)) +- av7110->pids[DMX_PES_TELETEXT] = ttpid; +- if (!(pcrpid & 0x8000)) +- av7110->pids[DMX_PES_PCR] = pcrpid; +- +- av7110->pids[DMX_PES_SUBTITLE] = 0; +- +- if (av7110->fe_synced) { +- pcrpid = av7110->pids[DMX_PES_PCR]; +- ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); +- } +- +- mutex_unlock(&av7110->pid_mutex); +- return ret; +-} +- +- +-/****************************************************************************** +- * hardware filter functions +- ******************************************************************************/ +- +-static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) +-{ +- struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; +- struct av7110 *av7110 = dvbdmxfeed->demux->priv; +- u16 buf[20]; +- int ret, i; +- u16 handle; +-// u16 mode = 0x0320; +- u16 mode = 0xb96a; +- +- dprintk(4, "%p\n", av7110); +- +- if (av7110->full_ts) +- return 0; +- +- if (dvbdmxfilter->type == DMX_TYPE_SEC) { +- if (hw_sections) { +- buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | +- dvbdmxfilter->maskandmode[0]; +- for (i = 3; i < 18; i++) +- buf[i + 4 - 2] = +- (dvbdmxfilter->filter.filter_value[i] << 8) | +- dvbdmxfilter->maskandmode[i]; +- mode = 4; +- } +- } else if ((dvbdmxfeed->ts_type & TS_PACKET) && +- !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { +- av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); +- } +- +- buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; +- buf[1] = 16; +- buf[2] = dvbdmxfeed->pid; +- buf[3] = mode; +- +- ret = av7110_fw_request(av7110, buf, 20, &handle, 1); +- if (ret != 0 || handle >= 32) { +- printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " +- "ret %d handle %04x\n", +- __func__, buf[0], buf[1], buf[2], buf[3], +- ret, handle); +- dvbdmxfilter->hw_handle = 0xffff; +- if (!ret) +- ret = -1; +- return ret; +- } +- +- av7110->handle2filter[handle] = dvbdmxfilter; +- dvbdmxfilter->hw_handle = handle; +- +- return ret; +-} +- +-static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) +-{ +- struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; +- u16 buf[3]; +- u16 answ[2]; +- int ret; +- u16 handle; +- +- dprintk(4, "%p\n", av7110); +- +- if (av7110->full_ts) +- return 0; +- +- handle = dvbdmxfilter->hw_handle; +- if (handle >= 32) { +- printk("%s tried to stop invalid filter %04x, filter type = %x\n", +- __func__, handle, dvbdmxfilter->type); +- return -EINVAL; +- } +- +- av7110->handle2filter[handle] = NULL; +- +- buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; +- buf[1] = 1; +- buf[2] = handle; +- ret = av7110_fw_request(av7110, buf, 3, answ, 2); +- if (ret != 0 || answ[1] != handle) { +- printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " +- "resp %04x %04x pid %d\n", +- __func__, buf[0], buf[1], buf[2], ret, +- answ[0], answ[1], dvbdmxfilter->feed->pid); +- if (!ret) +- ret = -1; +- } +- return ret; +-} +- +- +-static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct av7110 *av7110 = dvbdmx->priv; +- u16 *pid = dvbdmx->pids, npids[5]; +- int i; +- int ret = 0; +- +- dprintk(4, "%p\n", av7110); +- +- npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; +- i = dvbdmxfeed->pes_type; +- npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; +- if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { +- npids[i] = 0; +- ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); +- if (!ret) +- ret = StartHWFilter(dvbdmxfeed->filter); +- return ret; +- } +- if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { +- ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); +- if (ret) +- return ret; +- } +- +- if (dvbdmxfeed->pes_type < 2 && npids[0]) +- if (av7110->fe_synced) +- { +- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); +- if (ret) +- return ret; +- } +- +- if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { +- if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) +- ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); +- if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) +- ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); +- } +- return ret; +-} +- +-static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct av7110 *av7110 = dvbdmx->priv; +- u16 *pid = dvbdmx->pids, npids[5]; +- int i; +- +- int ret = 0; +- +- dprintk(4, "%p\n", av7110); +- +- if (dvbdmxfeed->pes_type <= 1) { +- ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); +- if (ret) +- return ret; +- if (!av7110->rec_mode) +- dvbdmx->recording = 0; +- if (!av7110->playing) +- dvbdmx->playing = 0; +- } +- npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; +- i = dvbdmxfeed->pes_type; +- switch (i) { +- case 2: //teletext +- if (dvbdmxfeed->ts_type & TS_PACKET) +- ret = StopHWFilter(dvbdmxfeed->filter); +- npids[2] = 0; +- break; +- case 0: +- case 1: +- case 4: +- if (!pids_off) +- return 0; +- npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; +- break; +- } +- if (!ret) +- ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); +- return ret; +-} +- +-static int av7110_start_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct av7110 *av7110 = demux->priv; +- int ret = 0; +- +- dprintk(4, "%p\n", av7110); +- +- if (!demux->dmx.frontend) +- return -EINVAL; +- +- if (!av7110->full_ts && feed->pid > 0x1fff) +- return -EINVAL; +- +- if (feed->type == DMX_TYPE_TS) { +- if ((feed->ts_type & TS_DECODER) && +- (feed->pes_type <= DMX_TS_PES_PCR)) { +- switch (demux->dmx.frontend->source) { +- case DMX_MEMORY_FE: +- if (feed->ts_type & TS_DECODER) +- if (feed->pes_type < 2 && +- !(demux->pids[0] & 0x8000) && +- !(demux->pids[1] & 0x8000)) { +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); +- ret = av7110_av_start_play(av7110,RP_AV); +- if (!ret) +- demux->playing = 1; +- } +- break; +- default: +- ret = dvb_feed_start_pid(feed); +- break; +- } +- } else if ((feed->ts_type & TS_PACKET) && +- (demux->dmx.frontend->source != DMX_MEMORY_FE)) { +- ret = StartHWFilter(feed->filter); +- } +- } +- +- if (av7110->full_ts) { +- budget_start_feed(feed); +- return ret; +- } +- +- if (feed->type == DMX_TYPE_SEC) { +- int i; +- +- for (i = 0; i < demux->filternum; i++) { +- if (demux->filter[i].state != DMX_STATE_READY) +- continue; +- if (demux->filter[i].type != DMX_TYPE_SEC) +- continue; +- if (demux->filter[i].filter.parent != &feed->feed.sec) +- continue; +- demux->filter[i].state = DMX_STATE_GO; +- if (demux->dmx.frontend->source != DMX_MEMORY_FE) { +- ret = StartHWFilter(&demux->filter[i]); +- if (ret) +- break; +- } +- } +- } +- +- return ret; +-} +- +- +-static int av7110_stop_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct av7110 *av7110 = demux->priv; +- int i, rc, ret = 0; +- dprintk(4, "%p\n", av7110); +- +- if (feed->type == DMX_TYPE_TS) { +- if (feed->ts_type & TS_DECODER) { +- if (feed->pes_type >= DMX_TS_PES_OTHER || +- !demux->pesfilter[feed->pes_type]) +- return -EINVAL; +- demux->pids[feed->pes_type] |= 0x8000; +- demux->pesfilter[feed->pes_type] = NULL; +- } +- if (feed->ts_type & TS_DECODER && +- feed->pes_type < DMX_TS_PES_OTHER) { +- ret = dvb_feed_stop_pid(feed); +- } else +- if ((feed->ts_type & TS_PACKET) && +- (demux->dmx.frontend->source != DMX_MEMORY_FE)) +- ret = StopHWFilter(feed->filter); +- } +- +- if (av7110->full_ts) { +- budget_stop_feed(feed); +- return ret; +- } +- +- if (feed->type == DMX_TYPE_SEC) { +- for (i = 0; ifilternum; i++) { +- if (demux->filter[i].state == DMX_STATE_GO && +- demux->filter[i].filter.parent == &feed->feed.sec) { +- demux->filter[i].state = DMX_STATE_READY; +- if (demux->dmx.frontend->source != DMX_MEMORY_FE) { +- rc = StopHWFilter(&demux->filter[i]); +- if (!ret) +- ret = rc; +- /* keep going, stop as many filters as possible */ +- } +- } +- } +- } +- +- return ret; +-} +- +- +-static void restart_feeds(struct av7110 *av7110) +-{ +- struct dvb_demux *dvbdmx = &av7110->demux; +- struct dvb_demux_feed *feed; +- int mode; +- int feeding; +- int i, j; +- +- dprintk(4, "%p\n", av7110); +- +- mode = av7110->playing; +- av7110->playing = 0; +- av7110->rec_mode = 0; +- +- feeding = av7110->feeding1; /* full_ts mod */ +- +- for (i = 0; i < dvbdmx->feednum; i++) { +- feed = &dvbdmx->feed[i]; +- if (feed->state == DMX_STATE_GO) { +- if (feed->type == DMX_TYPE_SEC) { +- for (j = 0; j < dvbdmx->filternum; j++) { +- if (dvbdmx->filter[j].type != DMX_TYPE_SEC) +- continue; +- if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) +- continue; +- if (dvbdmx->filter[j].state == DMX_STATE_GO) +- dvbdmx->filter[j].state = DMX_STATE_READY; +- } +- } +- av7110_start_feed(feed); +- } +- } +- +- av7110->feeding1 = feeding; /* full_ts mod */ +- +- if (mode) +- av7110_av_start_play(av7110, mode); +-} +- +-static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, +- uint64_t *stc, unsigned int *base) +-{ +- int ret; +- u16 fwstc[4]; +- u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); +- struct dvb_demux *dvbdemux; +- struct av7110 *av7110; +- +- /* pointer casting paranoia... */ +- BUG_ON(!demux); +- dvbdemux = demux->priv; +- BUG_ON(!dvbdemux); +- av7110 = dvbdemux->priv; +- +- dprintk(4, "%p\n", av7110); +- +- if (num != 0) +- return -EINVAL; +- +- ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); +- if (ret) { +- printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); +- return ret; +- } +- dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", +- fwstc[0], fwstc[1], fwstc[2], fwstc[3]); +- +- *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | +- (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); +- *base = 1; +- +- dprintk(4, "stc = %lu\n", (unsigned long)*stc); +- +- return 0; +-} +- +- +-/****************************************************************************** +- * SEC device file operations +- ******************************************************************************/ +- +- +-static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- return Set22K(av7110, 1); +- +- case SEC_TONE_OFF: +- return Set22K(av7110, 0); +- +- default: +- return -EINVAL; +- } +-} +- +-static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd* cmd) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); +-} +- +-static int av7110_diseqc_send_burst(struct dvb_frontend* fe, +- fe_sec_mini_cmd_t minicmd) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- return av7110_diseqc_send(av7110, 0, NULL, minicmd); +-} +- +-/* simplified code from budget-core.c */ +-static int stop_ts_capture(struct av7110 *budget) +-{ +- dprintk(2, "budget: %p\n", budget); +- +- if (--budget->feeding1) +- return budget->feeding1; +- saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ +- SAA7146_IER_DISABLE(budget->dev, MASK_10); +- SAA7146_ISR_CLEAR(budget->dev, MASK_10); +- return 0; +-} +- +-static int start_ts_capture(struct av7110 *budget) +-{ +- dprintk(2, "budget: %p\n", budget); +- +- if (budget->feeding1) +- return ++budget->feeding1; +- memset(budget->grabbing, 0x00, TS_BUFLEN); +- budget->ttbp = 0; +- SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ +- SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ +- saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ +- return ++budget->feeding1; +-} +- +-static int budget_start_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct av7110 *budget = demux->priv; +- int status; +- +- dprintk(2, "av7110: %p\n", budget); +- +- spin_lock(&budget->feedlock1); +- feed->pusi_seen = 0; /* have a clean section start */ +- status = start_ts_capture(budget); +- spin_unlock(&budget->feedlock1); +- return status; +-} +- +-static int budget_stop_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct av7110 *budget = demux->priv; +- int status; +- +- dprintk(2, "budget: %p\n", budget); +- +- spin_lock(&budget->feedlock1); +- status = stop_ts_capture(budget); +- spin_unlock(&budget->feedlock1); +- return status; +-} +- +-static void vpeirq(unsigned long cookie) +-{ +- struct av7110 *budget = (struct av7110 *)cookie; +- u8 *mem = (u8 *) (budget->grabbing); +- u32 olddma = budget->ttbp; +- u32 newdma = saa7146_read(budget->dev, PCI_VDP3); +- struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; +- +- /* nearest lower position divisible by 188 */ +- newdma -= newdma % 188; +- +- if (newdma >= TS_BUFLEN) +- return; +- +- budget->ttbp = newdma; +- +- if (!budget->feeding1 || (newdma == olddma)) +- return; +- +- /* Ensure streamed PCI data is synced to CPU */ +- pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); +- +-#if 0 +- /* track rps1 activity */ +- printk("vpeirq: %02x Event Counter 1 0x%04x\n", +- mem[olddma], +- saa7146_read(budget->dev, EC1R) & 0x3fff); +-#endif +- +- if (newdma > olddma) +- /* no wraparound, dump olddma..newdma */ +- dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); +- else { +- /* wraparound, dump olddma..buflen and 0..newdma */ +- dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); +- dvb_dmx_swfilter_packets(demux, mem, newdma / 188); +- } +-} +- +-static int av7110_register(struct av7110 *av7110) +-{ +- int ret, i; +- struct dvb_demux *dvbdemux = &av7110->demux; +- struct dvb_demux *dvbdemux1 = &av7110->demux1; +- +- dprintk(4, "%p\n", av7110); +- +- if (av7110->registered) +- return -1; +- +- av7110->registered = 1; +- +- dvbdemux->priv = (void *) av7110; +- +- for (i = 0; i < 32; i++) +- av7110->handle2filter[i] = NULL; +- +- dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; +- dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; +- dvbdemux->start_feed = av7110_start_feed; +- dvbdemux->stop_feed = av7110_stop_feed; +- dvbdemux->write_to_decoder = av7110_write_to_decoder; +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING); +- +- dvb_dmx_init(&av7110->demux); +- av7110->demux.dmx.get_stc = dvb_get_stc; +- +- av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; +- av7110->dmxdev.demux = &dvbdemux->dmx; +- av7110->dmxdev.capabilities = 0; +- +- dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); +- +- av7110->hw_frontend.source = DMX_FRONTEND_0; +- +- ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); +- +- if (ret < 0) +- return ret; +- +- av7110->mem_frontend.source = DMX_MEMORY_FE; +- +- ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); +- +- if (ret < 0) +- return ret; +- +- ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, +- &av7110->hw_frontend); +- if (ret < 0) +- return ret; +- +- av7110_av_register(av7110); +- av7110_ca_register(av7110); +- +-#ifdef CONFIG_DVB_AV7110_OSD +- dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, +- &dvbdev_osd, av7110, DVB_DEVICE_OSD); +-#endif +- +- dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); +- +- if (budgetpatch) { +- /* initialize software demux1 without its own frontend +- * demux1 hardware is connected to frontend0 of demux0 +- */ +- dvbdemux1->priv = (void *) av7110; +- +- dvbdemux1->filternum = 256; +- dvbdemux1->feednum = 256; +- dvbdemux1->start_feed = budget_start_feed; +- dvbdemux1->stop_feed = budget_stop_feed; +- dvbdemux1->write_to_decoder = NULL; +- +- dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING); +- +- dvb_dmx_init(&av7110->demux1); +- +- av7110->dmxdev1.filternum = 256; +- av7110->dmxdev1.demux = &dvbdemux1->dmx; +- av7110->dmxdev1.capabilities = 0; +- +- dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); +- +- dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); +- printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); +- } +- return 0; +-} +- +- +-static void dvb_unregister(struct av7110 *av7110) +-{ +- struct dvb_demux *dvbdemux = &av7110->demux; +- struct dvb_demux *dvbdemux1 = &av7110->demux1; +- +- dprintk(4, "%p\n", av7110); +- +- if (!av7110->registered) +- return; +- +- if (budgetpatch) { +- dvb_net_release(&av7110->dvb_net1); +- dvbdemux->dmx.close(&dvbdemux1->dmx); +- dvb_dmxdev_release(&av7110->dmxdev1); +- dvb_dmx_release(&av7110->demux1); +- } +- +- dvb_net_release(&av7110->dvb_net); +- +- dvbdemux->dmx.close(&dvbdemux->dmx); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); +- +- dvb_dmxdev_release(&av7110->dmxdev); +- dvb_dmx_release(&av7110->demux); +- +- if (av7110->fe != NULL) { +- dvb_unregister_frontend(av7110->fe); +- dvb_frontend_detach(av7110->fe); +- } +- dvb_unregister_device(av7110->osd_dev); +- av7110_av_unregister(av7110); +- av7110_ca_unregister(av7110); +-} +- +- +-/**************************************************************************** +- * I2C client commands +- ****************************************************************************/ +- +-int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) +-{ +- u8 msg[2] = { reg, val }; +- struct i2c_msg msgs; +- +- msgs.flags = 0; +- msgs.addr = id / 2; +- msgs.len = 2; +- msgs.buf = msg; +- return i2c_transfer(&av7110->i2c_adap, &msgs, 1); +-} +- +-u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) +-{ +- u8 mm1[] = {0x00}; +- u8 mm2[] = {0x00}; +- struct i2c_msg msgs[2]; +- +- msgs[0].flags = 0; +- msgs[1].flags = I2C_M_RD; +- msgs[0].addr = msgs[1].addr = id / 2; +- mm1[0] = reg; +- msgs[0].len = 1; msgs[1].len = 1; +- msgs[0].buf = mm1; msgs[1].buf = mm2; +- i2c_transfer(&av7110->i2c_adap, msgs, 2); +- +- return mm2[0]; +-} +- +-/**************************************************************************** +- * INITIALIZATION +- ****************************************************************************/ +- +- +-static int check_firmware(struct av7110* av7110) +-{ +- u32 crc = 0, len = 0; +- unsigned char *ptr; +- +- /* check for firmware magic */ +- ptr = av7110->bin_fw; +- if (ptr[0] != 'A' || ptr[1] != 'V' || +- ptr[2] != 'F' || ptr[3] != 'W') { +- printk("dvb-ttpci: this is not an av7110 firmware\n"); +- return -EINVAL; +- } +- ptr += 4; +- +- /* check dpram file */ +- crc = get_unaligned_be32(ptr); +- ptr += 4; +- len = get_unaligned_be32(ptr); +- ptr += 4; +- if (len >= 512) { +- printk("dvb-ttpci: dpram file is way too big.\n"); +- return -EINVAL; +- } +- if (crc != crc32_le(0, ptr, len)) { +- printk("dvb-ttpci: crc32 of dpram file does not match.\n"); +- return -EINVAL; +- } +- av7110->bin_dpram = ptr; +- av7110->size_dpram = len; +- ptr += len; +- +- /* check root file */ +- crc = get_unaligned_be32(ptr); +- ptr += 4; +- len = get_unaligned_be32(ptr); +- ptr += 4; +- +- if (len <= 200000 || len >= 300000 || +- len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { +- printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); +- return -EINVAL; +- } +- if( crc != crc32_le(0, ptr, len)) { +- printk("dvb-ttpci: crc32 of root file does not match.\n"); +- return -EINVAL; +- } +- av7110->bin_root = ptr; +- av7110->size_root = len; +- return 0; +-} +- +-static void put_firmware(struct av7110* av7110) +-{ +- vfree(av7110->bin_fw); +-} +- +-static int get_firmware(struct av7110* av7110) +-{ +- int ret; +- const struct firmware *fw; +- +- /* request the av7110 firmware, this will block until someone uploads it */ +- ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); +- if (ret) { +- if (ret == -ENOENT) { +- printk(KERN_ERR "dvb-ttpci: could not load firmware," +- " file not found: dvb-ttpci-01.fw\n"); +- printk(KERN_ERR "dvb-ttpci: usually this should be in " +- "/usr/lib/hotplug/firmware or /lib/firmware\n"); +- printk(KERN_ERR "dvb-ttpci: and can be downloaded from" +- " http://www.linuxtv.org/download/dvb/firmware/\n"); +- } else +- printk(KERN_ERR "dvb-ttpci: cannot request firmware" +- " (error %i)\n", ret); +- return -EINVAL; +- } +- +- if (fw->size <= 200000) { +- printk("dvb-ttpci: this firmware is way too small.\n"); +- release_firmware(fw); +- return -EINVAL; +- } +- +- /* check if the firmware is available */ +- av7110->bin_fw = vmalloc(fw->size); +- if (NULL == av7110->bin_fw) { +- dprintk(1, "out of memory\n"); +- release_firmware(fw); +- return -ENOMEM; +- } +- +- memcpy(av7110->bin_fw, fw->data, fw->size); +- av7110->size_fw = fw->size; +- if ((ret = check_firmware(av7110))) +- vfree(av7110->bin_fw); +- +- release_firmware(fw); +- return ret; +-} +- +-static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u8 pwr = 0; +- u8 buf[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; +- u32 div = (p->frequency + 479500) / 125; +- +- if (p->frequency > 2000000) +- pwr = 3; +- else if (p->frequency > 1800000) +- pwr = 2; +- else if (p->frequency > 1600000) +- pwr = 1; +- else if (p->frequency > 1200000) +- pwr = 0; +- else if (p->frequency >= 1100000) +- pwr = 1; +- else +- pwr = 2; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = ((div & 0x18000) >> 10) | 0x95; +- buf[3] = (pwr << 6) | 0x30; +- +- // NOTE: since we're using a prescaler of 2, we set the +- // divisor frequency to 62.5kHz and divide by 125 above +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct ves1x93_config alps_bsrv2_config = { +- .demod_address = 0x08, +- .xin = 90100000UL, +- .invert_pwm = 0, +-}; +- +-static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (p->frequency + 35937500 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x85 | ((div >> 10) & 0x60); +- data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct ves1820_config alps_tdbe2_config = { +- .demod_address = 0x09, +- .xin = 57840000UL, +- .invert = 1, +- .selagc = VES1820_SELAGC_SIGNAMPERR, +-}; +- +- +- +- +-static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = p->frequency / 125; +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x8e; +- data[3] = 0x00; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct tda8083_config grundig_29504_451_config = { +- .demod_address = 0x68, +-}; +- +- +- +-static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div; +- u32 f = p->frequency; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (f + 36125000 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x8e; +- data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct ves1820_config philips_cd1516_config = { +- .demod_address = 0x09, +- .xin = 57840000UL, +- .invert = 1, +- .selagc = VES1820_SELAGC_SIGNAMPERR, +-}; +- +- +- +-static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div, pwr; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (p->frequency + 36200000) / 166666; +- +- if (p->frequency <= 782000000) +- pwr = 1; +- else +- pwr = 2; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x85; +- data[3] = pwr << 6; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +-{ +-#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) +- struct av7110* av7110 = fe->dvb->priv; +- +- return request_firmware(fw, name, &av7110->dev->pci->dev); +-#else +- return -EINVAL; +-#endif +-} +- +-static struct sp8870_config alps_tdlb7_config = { +- +- .demod_address = 0x71, +- .request_firmware = alps_tdlb7_request_firmware, +-}; +- +- +-static u8 nexusca_stv0297_inittab[] = { +- 0x80, 0x01, +- 0x80, 0x00, +- 0x81, 0x01, +- 0x81, 0x00, +- 0x00, 0x09, +- 0x01, 0x69, +- 0x03, 0x00, +- 0x04, 0x00, +- 0x07, 0x00, +- 0x08, 0x00, +- 0x20, 0x00, +- 0x21, 0x40, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x24, 0x40, +- 0x25, 0x88, +- 0x30, 0xff, +- 0x31, 0x00, +- 0x32, 0xff, +- 0x33, 0x00, +- 0x34, 0x50, +- 0x35, 0x7f, +- 0x36, 0x00, +- 0x37, 0x20, +- 0x38, 0x00, +- 0x40, 0x1c, +- 0x41, 0xff, +- 0x42, 0x29, +- 0x43, 0x00, +- 0x44, 0xff, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x49, 0x04, +- 0x4a, 0x00, +- 0x4b, 0x7b, +- 0x52, 0x30, +- 0x55, 0xae, +- 0x56, 0x47, +- 0x57, 0xe1, +- 0x58, 0x3a, +- 0x5a, 0x1e, +- 0x5b, 0x34, +- 0x60, 0x00, +- 0x63, 0x00, +- 0x64, 0x00, +- 0x65, 0x00, +- 0x66, 0x00, +- 0x67, 0x00, +- 0x68, 0x00, +- 0x69, 0x00, +- 0x6a, 0x02, +- 0x6b, 0x00, +- 0x70, 0xff, +- 0x71, 0x00, +- 0x72, 0x00, +- 0x73, 0x00, +- 0x74, 0x0c, +- 0x80, 0x00, +- 0x81, 0x00, +- 0x82, 0x00, +- 0x83, 0x00, +- 0x84, 0x04, +- 0x85, 0x80, +- 0x86, 0x24, +- 0x87, 0x78, +- 0x88, 0x10, +- 0x89, 0x00, +- 0x90, 0x01, +- 0x91, 0x01, +- 0xa0, 0x04, +- 0xa1, 0x00, +- 0xa2, 0x00, +- 0xb0, 0x91, +- 0xb1, 0x0b, +- 0xc0, 0x53, +- 0xc1, 0x70, +- 0xc2, 0x12, +- 0xd0, 0x00, +- 0xd1, 0x00, +- 0xd2, 0x00, +- 0xd3, 0x00, +- 0xd4, 0x00, +- 0xd5, 0x00, +- 0xde, 0x00, +- 0xdf, 0x00, +- 0x61, 0x49, +- 0x62, 0x0b, +- 0x53, 0x08, +- 0x59, 0x08, +- 0xff, 0xff, +-}; +- +-static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; +- struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; +- int i; +- +- div = (p->frequency + 36150000 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0xce; +- +- if (p->frequency < 45000000) +- return -EINVAL; +- else if (p->frequency < 137000000) +- data[3] = 0x01; +- else if (p->frequency < 403000000) +- data[3] = 0x02; +- else if (p->frequency < 860000000) +- data[3] = 0x04; +- else +- return -EINVAL; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { +- printk("nexusca: pll transfer failed!\n"); +- return -EIO; +- } +- +- // wait for PLL lock +- for(i = 0; i < 20; i++) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) +- if (data[0] & 0x40) break; +- msleep(10); +- } +- +- return 0; +-} +- +-static struct stv0297_config nexusca_stv0297_config = { +- +- .demod_address = 0x1C, +- .inittab = nexusca_stv0297_inittab, +- .invert = 1, +- .stop_during_read = 1, +-}; +- +- +- +-static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct av7110* av7110 = fe->dvb->priv; +- u32 div; +- u8 cfg, cpump, band_select; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (36125000 + p->frequency) / 166666; +- +- cfg = 0x88; +- +- if (p->frequency < 175000000) +- cpump = 2; +- else if (p->frequency < 390000000) +- cpump = 1; +- else if (p->frequency < 470000000) +- cpump = 2; +- else if (p->frequency < 750000000) +- cpump = 1; +- else +- cpump = 3; +- +- if (p->frequency < 175000000) +- band_select = 0x0e; +- else if (p->frequency < 470000000) +- band_select = 0x05; +- else +- band_select = 0x03; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = ((div >> 10) & 0x60) | cfg; +- data[3] = (cpump << 6) | band_select; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct l64781_config grundig_29504_401_config = { +- .demod_address = 0x55, +-}; +- +- +- +-static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) +-{ +- int ret = 0; +- int synced = (status & FE_HAS_LOCK) ? 1 : 0; +- +- av7110->fe_status = status; +- +- if (av7110->fe_synced == synced) +- return 0; +- +- if (av7110->playing) { +- av7110->fe_synced = synced; +- return 0; +- } +- +- if (mutex_lock_interruptible(&av7110->pid_mutex)) +- return -ERESTARTSYS; +- +- if (synced) { +- ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], +- av7110->pids[DMX_PES_AUDIO], +- av7110->pids[DMX_PES_TELETEXT], 0, +- av7110->pids[DMX_PES_PCR]); +- if (!ret) +- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); +- } else { +- ret = SetPIDs(av7110, 0, 0, 0, 0, 0); +- if (!ret) { +- ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); +- if (!ret) +- ret = av7110_wait_msgstate(av7110, GPMQBusy); +- } +- } +- +- if (!ret) +- av7110->fe_synced = synced; +- +- mutex_unlock(&av7110->pid_mutex); +- return ret; +-} +- +-static int av7110_fe_set_frontend(struct dvb_frontend *fe) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) +- ret = av7110->fe_set_frontend(fe); +- +- return ret; +-} +- +-static int av7110_fe_init(struct dvb_frontend* fe) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) +- ret = av7110->fe_init(fe); +- return ret; +-} +- +-static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- /* call the real implementation */ +- int ret = av7110->fe_read_status(fe, status); +- if (!ret) +- if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) +- ret = av7110_fe_lock_fix(av7110, *status); +- return ret; +-} +- +-static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) +- ret = av7110->fe_diseqc_reset_overload(fe); +- return ret; +-} +- +-static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, +- struct dvb_diseqc_master_cmd* cmd) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) { +- av7110->saved_master_cmd = *cmd; +- ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); +- } +- return ret; +-} +- +-static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) { +- av7110->saved_minicmd = minicmd; +- ret = av7110->fe_diseqc_send_burst(fe, minicmd); +- } +- return ret; +-} +- +-static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) { +- av7110->saved_tone = tone; +- ret = av7110->fe_set_tone(fe, tone); +- } +- return ret; +-} +- +-static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) { +- av7110->saved_voltage = voltage; +- ret = av7110->fe_set_voltage(fe, voltage); +- } +- return ret; +-} +- +-static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) +-{ +- struct av7110* av7110 = fe->dvb->priv; +- +- int ret = av7110_fe_lock_fix(av7110, 0); +- if (!ret) +- ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); +- return ret; +-} +- +-static void dvb_s_recover(struct av7110* av7110) +-{ +- av7110_fe_init(av7110->fe); +- +- av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); +- if (av7110->saved_master_cmd.msg_len) { +- msleep(20); +- av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); +- } +- msleep(20); +- av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); +- msleep(20); +- av7110_fe_set_tone(av7110->fe, av7110->saved_tone); +- +- av7110_fe_set_frontend(av7110->fe); +-} +- +-static u8 read_pwm(struct av7110* av7110) +-{ +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, +- { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; +- +- if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +-static int frontend_init(struct av7110 *av7110) +-{ +- int ret; +- +- if (av7110->dev->pci->subsystem_vendor == 0x110a) { +- switch(av7110->dev->pci->subsystem_device) { +- case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) +- av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, +- &av7110->i2c_adap, read_pwm(av7110)); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; +- } +- break; +- } +- +- } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { +- switch(av7110->dev->pci->subsystem_device) { +- case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X +- case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X +- case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE +- +- // try the ALPS BSRV2 first of all +- av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; +- av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; +- av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; +- av7110->fe->ops.set_tone = av7110_set_tone; +- av7110->recover = dvb_s_recover; +- break; +- } +- +- // try the ALPS BSRU6 now +- av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; +- av7110->fe->tuner_priv = &av7110->i2c_adap; +- +- av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; +- av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; +- av7110->fe->ops.set_tone = av7110_set_tone; +- av7110->recover = dvb_s_recover; +- break; +- } +- +- // Try the grundig 29504-451 +- av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; +- av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; +- av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; +- av7110->fe->ops.set_tone = av7110_set_tone; +- av7110->recover = dvb_s_recover; +- break; +- } +- +- /* Try DVB-C cards */ +- switch(av7110->dev->pci->subsystem_device) { +- case 0x0000: +- /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ +- av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, +- read_pwm(av7110)); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; +- } +- break; +- case 0x0003: +- /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ +- av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, +- read_pwm(av7110)); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; +- } +- break; +- } +- break; +- +- case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X +- // try ALPS TDLB7 first, then Grundig 29504-401 +- av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; +- break; +- } +- /* fall-thru */ +- +- case 0x0008: // Hauppauge/TT DVB-T +- // Grundig 29504-401 +- av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); +- if (av7110->fe) +- av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; +- break; +- +- case 0x0002: // Hauppauge/TT DVB-C premium rev2.X +- +- av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; +- } +- break; +- +- case 0x0004: // Galaxis DVB-S rev1.3 +- /* ALPS BSRV2 */ +- av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; +- av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; +- av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; +- av7110->fe->ops.set_tone = av7110_set_tone; +- av7110->recover = dvb_s_recover; +- } +- break; +- +- case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ +- /* Grundig 29504-451 */ +- av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; +- av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; +- av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; +- av7110->fe->ops.set_tone = av7110_set_tone; +- av7110->recover = dvb_s_recover; +- } +- break; +- +- case 0x000A: // Hauppauge/TT Nexus-CA rev1.X +- +- av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; +- +- /* set TDA9819 into DVB mode */ +- saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) +- saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) +- +- /* tuner on this needs a slower i2c bus speed */ +- av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; +- break; +- } +- break; +- +- case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ +- /* ALPS BSBE1 */ +- av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); +- if (av7110->fe) { +- av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; +- av7110->fe->tuner_priv = &av7110->i2c_adap; +- +- if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { +- printk("dvb-ttpci: LNBP21 not found!\n"); +- if (av7110->fe->ops.release) +- av7110->fe->ops.release(av7110->fe); +- av7110->fe = NULL; +- } else { +- av7110->fe->ops.dishnetwork_send_legacy_command = NULL; +- av7110->recover = dvb_s_recover; +- } +- } +- break; +- } +- } +- +- if (!av7110->fe) { +- /* FIXME: propagate the failure code from the lower layers */ +- ret = -ENOMEM; +- printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- av7110->dev->pci->vendor, +- av7110->dev->pci->device, +- av7110->dev->pci->subsystem_vendor, +- av7110->dev->pci->subsystem_device); +- } else { +- FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); +- FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); +- FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); +- FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); +- FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); +- FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); +- FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); +- FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); +- FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); +- +- ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); +- if (ret < 0) { +- printk("av7110: Frontend registration failed!\n"); +- dvb_frontend_detach(av7110->fe); +- av7110->fe = NULL; +- } +- } +- return ret; +-} +- +-/* Budgetpatch note: +- * Original hardware design by Roberto Deza: +- * There is a DVB_Wiki at +- * http://www.linuxtv.org/ +- * +- * New software triggering design by Emard that works on +- * original Roberto Deza's hardware: +- * +- * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. +- * GPIO3 is in budget-patch hardware connectd to port B VSYNC +- * HS is an internal event of 7146, accessible with RPS +- * and temporarily raised high every n lines +- * (n in defined in the RPS_THRESH1 counter threshold) +- * I think HS is raised high on the beginning of the n-th line +- * and remains high until this n-th line that triggered +- * it is completely received. When the receiption of n-th line +- * ends, HS is lowered. +- * +- * To transmit data over DMA, 7146 needs changing state at +- * port B VSYNC pin. Any changing of port B VSYNC will +- * cause some DMA data transfer, with more or less packets loss. +- * It depends on the phase and frequency of VSYNC and +- * the way of 7146 is instructed to trigger on port B (defined +- * in DD1_INIT register, 3rd nibble from the right valid +- * numbers are 0-7, see datasheet) +- * +- * The correct triggering can minimize packet loss, +- * dvbtraffic should give this stable bandwidths: +- * 22k transponder = 33814 kbit/s +- * 27.5k transponder = 38045 kbit/s +- * by experiment it is found that the best results +- * (stable bandwidths and almost no packet loss) +- * are obtained using DD1_INIT triggering number 2 +- * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) +- * and a VSYNC phase that occurs in the middle of DMA transfer +- * (about byte 188*512=96256 in the DMA window). +- * +- * Phase of HS is still not clear to me how to control, +- * It just happens to be so. It can be seen if one enables +- * RPS_IRQ and print Event Counter 1 in vpeirq(). Every +- * time RPS_INTERRUPT is called, the Event Counter 1 will +- * increment. That's how the 7146 is programmed to do event +- * counting in this budget-patch.c +- * I *think* HPS setting has something to do with the phase +- * of HS but I can't be 100% sure in that. +- * +- * hardware debug note: a working budget card (including budget patch) +- * with vpeirq() interrupt setup in mode "0x90" (every 64K) will +- * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes +- * and that means 3*25=75 Hz of interrupt freqency, as seen by +- * watch cat /proc/interrupts +- * +- * If this frequency is 3x lower (and data received in the DMA +- * buffer don't start with 0x47, but in the middle of packets, +- * whose lengths appear to be like 188 292 188 104 etc. +- * this means VSYNC line is not connected in the hardware. +- * (check soldering pcb and pins) +- * The same behaviour of missing VSYNC can be duplicated on budget +- * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble. +- */ +-static int __devinit av7110_attach(struct saa7146_dev* dev, +- struct saa7146_pci_extension_data *pci_ext) +-{ +- const int length = TS_WIDTH * TS_HEIGHT; +- struct pci_dev *pdev = dev->pci; +- struct av7110 *av7110; +- struct task_struct *thread; +- int ret, count = 0; +- +- dprintk(4, "dev: %p\n", dev); +- +- /* Set RPS_IRQ to 1 to track rps1 activity. +- * Enabling this won't send any interrupt to PC CPU. +- */ +-#define RPS_IRQ 0 +- +- if (budgetpatch == 1) { +- budgetpatch = 0; +- /* autodetect the presence of budget patch +- * this only works if saa7146 has been recently +- * reset with with MASK_31 to MC1 +- * +- * will wait for VBI_B event (vertical blank at port B) +- * and will reset GPIO3 after VBI_B is detected. +- * (GPIO3 should be raised high by CPU to +- * test if GPIO3 will generate vertical blank signal +- * in budget patch GPIO3 is connected to VSYNC_B +- */ +- +- /* RESET SAA7146 */ +- saa7146_write(dev, MC1, MASK_31); +- /* autodetection success seems to be time-dependend after reset */ +- +- /* Fix VSYNC level */ +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- /* set vsync_b triggering */ +- saa7146_write(dev, DD1_STREAM_B, 0); +- /* port B VSYNC at rising edge */ +- saa7146_write(dev, DD1_INIT, 0x00000200); +- saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI +- saa7146_write(dev, MC2, +- 1 * (MASK_08 | MASK_24) | // BRS control +- 0 * (MASK_09 | MASK_25) | // a +- 1 * (MASK_10 | MASK_26) | // b +- 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 +- 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 +- 0 * (MASK_01 | MASK_15) // DEBI +- ); +- +- /* start writing RPS1 code from beginning */ +- count = 0; +- /* Disable RPS1 */ +- saa7146_write(dev, MC1, MASK_29); +- /* RPS1 timeout disable */ +- saa7146_write(dev, RPS_TOV1, 0); +- WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +-#if RPS_IRQ +- /* issue RPS1 interrupt to increment counter */ +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- WRITE_RPS1(CMD_STOP); +- /* Jump to begin of RPS program as safety measure (p37) */ +- WRITE_RPS1(CMD_JUMP); +- WRITE_RPS1(dev->d_rps1.dma_handle); +- +-#if RPS_IRQ +- /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) +- * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled +- * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called +- */ +- saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); +- /* set event counter 1 threshold to maximum allowed value (rEC p55) */ +- saa7146_write(dev, ECT1R, 0x3fff ); +-#endif +- /* Set RPS1 Address register to point to RPS code (r108 p42) */ +- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); +- /* Enable RPS1, (rFC p33) */ +- saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); +- +- mdelay(10); +- /* now send VSYNC_B to rps1 by rising GPIO3 */ +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); +- mdelay(10); +- /* if rps1 responded by lowering the GPIO3, +- * then we have budgetpatch hardware +- */ +- if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { +- budgetpatch = 1; +- printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); +- } +- /* Disable RPS1 */ +- saa7146_write(dev, MC1, ( MASK_29 )); +-#if RPS_IRQ +- printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +-#endif +- } +- +- /* prepare the av7110 device struct */ +- av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); +- if (!av7110) { +- dprintk(1, "out of memory\n"); +- return -ENOMEM; +- } +- +- av7110->card_name = (char*) pci_ext->ext_priv; +- av7110->dev = dev; +- dev->ext_priv = av7110; +- +- ret = get_firmware(av7110); +- if (ret < 0) +- goto err_kfree_0; +- +- ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, +- THIS_MODULE, &dev->pci->dev, adapter_nr); +- if (ret < 0) +- goto err_put_firmware_1; +- +- /* the Siemens DVB needs this if you want to have the i2c chips +- get recognized before the main driver is fully loaded */ +- saa7146_write(dev, GPIO_CTRL, 0x500000); +- +- strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); +- +- saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ +- +- ret = i2c_add_adapter(&av7110->i2c_adap); +- if (ret < 0) +- goto err_dvb_unregister_adapter_2; +- +- ttpci_eeprom_parse_mac(&av7110->i2c_adap, +- av7110->dvb_adapter.proposed_mac); +- ret = -ENOMEM; +- +- /* full-ts mod? */ +- if (full_ts) +- av7110->full_ts = true; +- +- /* check for full-ts flag in eeprom */ +- if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { +- u8 flags = i2c_readreg(av7110, 0xaa, 2); +- if (flags != 0xff && (flags & 0x01)) +- av7110->full_ts = true; +- } +- +- if (av7110->full_ts) { +- printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); +- spin_lock_init(&av7110->feedlock1); +- av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, +- &av7110->pt); +- if (!av7110->grabbing) +- goto err_i2c_del_3; +- +- saa7146_write(dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(dev, MC2, (MASK_10 | MASK_26)); +- +- saa7146_write(dev, DD1_INIT, 0x00000600); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- +- saa7146_write(dev, BRS_CTRL, 0x60000000); +- saa7146_write(dev, MC2, MASK_08 | MASK_24); +- +- /* dma3 */ +- saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); +- saa7146_write(dev, BASE_ODD3, 0); +- saa7146_write(dev, BASE_EVEN3, 0); +- saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); +- saa7146_write(dev, PITCH3, TS_WIDTH); +- saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); +- saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); +- saa7146_write(dev, MC2, MASK_04 | MASK_20); +- +- tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); +- +- } else if (budgetpatch) { +- spin_lock_init(&av7110->feedlock1); +- av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, +- &av7110->pt); +- if (!av7110->grabbing) +- goto err_i2c_del_3; +- +- saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); +- saa7146_write(dev, BCS_CTRL, 0x80400040); +- /* set dd1 stream a & b */ +- saa7146_write(dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(dev, DD1_INIT, 0x03000200); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x60000000); +- saa7146_write(dev, BASE_ODD3, 0); +- saa7146_write(dev, BASE_EVEN3, 0); +- saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); +- saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); +- +- saa7146_write(dev, PITCH3, TS_WIDTH); +- saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); +- +- /* upload all */ +- saa7146_write(dev, MC2, 0x077c077c); +- saa7146_write(dev, GPIO_CTRL, 0x000000); +-#if RPS_IRQ +- /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) +- * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled +- * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called +- */ +- saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); +- /* set event counter 1 threshold to maximum allowed value (rEC p55) */ +- saa7146_write(dev, ECT1R, 0x3fff ); +-#endif +- /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ +- count = 0; +- +- /* Wait Source Line Counter Threshold (p36) */ +- WRITE_RPS1(CMD_PAUSE | EVT_HS); +- /* Set GPIO3=1 (p42) */ +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +-#if RPS_IRQ +- /* issue RPS1 interrupt */ +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- /* Wait reset Source Line Counter Threshold (p36) */ +- WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); +- /* Set GPIO3=0 (p42) */ +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +-#if RPS_IRQ +- /* issue RPS1 interrupt */ +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- /* Jump to begin of RPS program (p37) */ +- WRITE_RPS1(CMD_JUMP); +- WRITE_RPS1(dev->d_rps1.dma_handle); +- +- /* Fix VSYNC level */ +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- /* Set RPS1 Address register to point to RPS code (r108 p42) */ +- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); +- /* Set Source Line Counter Threshold, using BRS (rCC p43) +- * It generates HS event every TS_HEIGHT lines +- * this is related to TS_WIDTH set in register +- * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits +- * are set to TS_WIDTH bytes (TS_WIDTH=2*188), +- * then RPS_THRESH1 should be set to trigger +- * every TS_HEIGHT (512) lines. +- */ +- saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); +- +- /* Enable RPS1 (rFC p33) */ +- saa7146_write(dev, MC1, (MASK_13 | MASK_29)); +- +- /* end of budgetpatch register initialization */ +- tasklet_init (&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); +- } else { +- saa7146_write(dev, PCI_BT_V1, 0x1c00101f); +- saa7146_write(dev, BCS_CTRL, 0x80400040); +- +- /* set dd1 stream a & b */ +- saa7146_write(dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(dev, DD1_INIT, 0x03000000); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- +- /* upload all */ +- saa7146_write(dev, MC2, 0x077c077c); +- saa7146_write(dev, GPIO_CTRL, 0x000000); +- } +- +- tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110); +- tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110); +- +- mutex_init(&av7110->pid_mutex); +- +- /* locks for data transfers from/to AV7110 */ +- spin_lock_init(&av7110->debilock); +- mutex_init(&av7110->dcomlock); +- av7110->debitype = -1; +- +- /* default OSD window */ +- av7110->osdwin = 1; +- mutex_init(&av7110->osd_mutex); +- +- /* TV standard */ +- av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC +- : AV7110_VIDEO_MODE_PAL; +- +- /* ARM "watchdog" */ +- init_waitqueue_head(&av7110->arm_wait); +- av7110->arm_thread = NULL; +- +- /* allocate and init buffers */ +- av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus); +- if (!av7110->debi_virt) +- goto err_saa71466_vfree_4; +- +- +- av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); +- if (!av7110->iobuf) +- goto err_pci_free_5; +- +- ret = av7110_av_init(av7110); +- if (ret < 0) +- goto err_iobuf_vfree_6; +- +- /* init BMP buffer */ +- av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; +- init_waitqueue_head(&av7110->bmpq); +- +- ret = av7110_ca_init(av7110); +- if (ret < 0) +- goto err_av7110_av_exit_7; +- +- /* load firmware into AV7110 cards */ +- ret = av7110_bootarm(av7110); +- if (ret < 0) +- goto err_av7110_ca_exit_8; +- +- ret = av7110_firmversion(av7110); +- if (ret < 0) +- goto err_stop_arm_9; +- +- if (FW_VERSION(av7110->arm_app)<0x2501) +- printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. " +- "System might be unstable!\n", FW_VERSION(av7110->arm_app)); +- +- thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); +- if (IS_ERR(thread)) { +- ret = PTR_ERR(thread); +- goto err_stop_arm_9; +- } +- av7110->arm_thread = thread; +- +- /* set initial volume in mixer struct */ +- av7110->mixer.volume_left = volume; +- av7110->mixer.volume_right = volume; +- +- ret = av7110_register(av7110); +- if (ret < 0) +- goto err_arm_thread_stop_10; +- +- init_av7110_av(av7110); +- +- /* special case DVB-C: these cards have an analog tuner +- plus need some special handling, so we have separate +- saa7146_ext_vv data for these... */ +- ret = av7110_init_v4l(av7110); +- if (ret < 0) +- goto err_av7110_unregister_11; +- +- av7110->dvb_adapter.priv = av7110; +- ret = frontend_init(av7110); +- if (ret < 0) +- goto err_av7110_exit_v4l_12; +- +-#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +- av7110_ir_init(av7110); +-#endif +- printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); +- av7110_num++; +-out: +- return ret; +- +-err_av7110_exit_v4l_12: +- av7110_exit_v4l(av7110); +-err_av7110_unregister_11: +- dvb_unregister(av7110); +-err_arm_thread_stop_10: +- av7110_arm_sync(av7110); +-err_stop_arm_9: +- /* Nothing to do. Rejoice. */ +-err_av7110_ca_exit_8: +- av7110_ca_exit(av7110); +-err_av7110_av_exit_7: +- av7110_av_exit(av7110); +-err_iobuf_vfree_6: +- vfree(av7110->iobuf); +-err_pci_free_5: +- pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus); +-err_saa71466_vfree_4: +- if (av7110->grabbing) +- saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); +-err_i2c_del_3: +- i2c_del_adapter(&av7110->i2c_adap); +-err_dvb_unregister_adapter_2: +- dvb_unregister_adapter(&av7110->dvb_adapter); +-err_put_firmware_1: +- put_firmware(av7110); +-err_kfree_0: +- kfree(av7110); +- goto out; +-} +- +-static int __devexit av7110_detach(struct saa7146_dev* saa) +-{ +- struct av7110 *av7110 = saa->ext_priv; +- dprintk(4, "%p\n", av7110); +- +-#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) +- av7110_ir_exit(av7110); +-#endif +- if (budgetpatch || av7110->full_ts) { +- if (budgetpatch) { +- /* Disable RPS1 */ +- saa7146_write(saa, MC1, MASK_29); +- /* VSYNC LOW (inactive) */ +- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); +- } +- saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ +- SAA7146_IER_DISABLE(saa, MASK_10); +- SAA7146_ISR_CLEAR(saa, MASK_10); +- msleep(50); +- tasklet_kill(&av7110->vpe_tasklet); +- saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); +- } +- av7110_exit_v4l(av7110); +- +- av7110_arm_sync(av7110); +- +- tasklet_kill(&av7110->debi_tasklet); +- tasklet_kill(&av7110->gpio_tasklet); +- +- dvb_unregister(av7110); +- +- SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); +- SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); +- +- av7110_ca_exit(av7110); +- av7110_av_exit(av7110); +- +- vfree(av7110->iobuf); +- pci_free_consistent(saa->pci, 8192, av7110->debi_virt, +- av7110->debi_bus); +- +- i2c_del_adapter(&av7110->i2c_adap); +- +- dvb_unregister_adapter (&av7110->dvb_adapter); +- +- av7110_num--; +- +- put_firmware(av7110); +- +- kfree(av7110); +- +- saa->ext_priv = NULL; +- +- return 0; +-} +- +- +-static void av7110_irq(struct saa7146_dev* dev, u32 *isr) +-{ +- struct av7110 *av7110 = dev->ext_priv; +- +- //print_time("av7110_irq"); +- +- /* Note: Don't try to handle the DEBI error irq (MASK_18), in +- * intel mode the timeout is asserted all the time... +- */ +- +- if (*isr & MASK_19) { +- //printk("av7110_irq: DEBI\n"); +- /* Note 1: The DEBI irq is level triggered: We must enable it +- * only after we started a DMA xfer, and disable it here +- * immediately, or it will be signalled all the time while +- * DEBI is idle. +- * Note 2: You would think that an irq which is masked is +- * not signalled by the hardware. Not so for the SAA7146: +- * An irq is signalled as long as the corresponding bit +- * in the ISR is set, and disabling irqs just prevents the +- * hardware from setting the ISR bit. This means a) that we +- * must clear the ISR *after* disabling the irq (which is why +- * we must do it here even though saa7146_core did it already), +- * and b) that if we were to disable an edge triggered irq +- * (like the gpio irqs sadly are) temporarily we would likely +- * loose some. This sucks :-( +- */ +- SAA7146_IER_DISABLE(av7110->dev, MASK_19); +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19); +- tasklet_schedule(&av7110->debi_tasklet); +- } +- +- if (*isr & MASK_03) { +- //printk("av7110_irq: GPIO\n"); +- tasklet_schedule(&av7110->gpio_tasklet); +- } +- +- if (*isr & MASK_10) +- tasklet_schedule(&av7110->vpe_tasklet); +-} +- +- +-static struct saa7146_extension av7110_extension_driver; +- +-#define MAKE_AV7110_INFO(x_var,x_name) \ +-static struct saa7146_pci_extension_data x_var = { \ +- .ext_priv = x_name, \ +- .ext = &av7110_extension_driver } +- +-MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); +-MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); +-MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); +-MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); +-MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); +-MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); +-MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); +-MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); +-MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); +-MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); +-MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); +- +-static struct pci_device_id pci_tbl[] = { +- MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), +- MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), +- MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), +- MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), +- MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), +- MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), +- MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), +- MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), +- MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), +- MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), +- MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), +- +-/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 +-/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? +- +- { +- .vendor = 0, +- } +-}; +- +-MODULE_DEVICE_TABLE(pci, pci_tbl); +- +- +-static struct saa7146_extension av7110_extension_driver = { +- .name = "av7110", +- .flags = SAA7146_USE_I2C_IRQ, +- +- .module = THIS_MODULE, +- .pci_tbl = &pci_tbl[0], +- .attach = av7110_attach, +- .detach = __devexit_p(av7110_detach), +- +- .irq_mask = MASK_19 | MASK_03 | MASK_10, +- .irq_func = av7110_irq, +-}; +- +- +-static int __init av7110_init(void) +-{ +- int retval; +- retval = saa7146_register_extension(&av7110_extension_driver); +- return retval; +-} +- +- +-static void __exit av7110_exit(void) +-{ +- saa7146_unregister_extension(&av7110_extension_driver); +-} +- +-module_init(av7110_init); +-module_exit(av7110_exit); +- +-MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by " +- "Siemens, Technotrend, Hauppauge"); +-MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h +deleted file mode 100644 +index 88b3b2d..0000000 +--- a/drivers/media/dvb/ttpci/av7110.h ++++ /dev/null +@@ -1,314 +0,0 @@ +-#ifndef _AV7110_H_ +-#define _AV7110_H_ +- +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvbdev.h" +-#include "demux.h" +-#include "dvb_demux.h" +-#include "dmxdev.h" +-#include "dvb_filter.h" +-#include "dvb_net.h" +-#include "dvb_ringbuffer.h" +-#include "dvb_frontend.h" +-#include "ves1820.h" +-#include "ves1x93.h" +-#include "stv0299.h" +-#include "tda8083.h" +-#include "sp8870.h" +-#include "stv0297.h" +-#include "l64781.h" +- +-#include +- +- +-#define ANALOG_TUNER_VES1820 1 +-#define ANALOG_TUNER_STV0297 2 +- +-extern int av7110_debug; +- +-#define dprintk(level,args...) \ +- do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0) +- +-#define MAXFILT 32 +- +-enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; +- +-enum av7110_video_mode { +- AV7110_VIDEO_MODE_PAL = 0, +- AV7110_VIDEO_MODE_NTSC = 1 +-}; +- +-struct av7110_p2t { +- u8 pes[TS_SIZE]; +- u8 counter; +- long int pos; +- int frags; +- struct dvb_demux_feed *feed; +-}; +- +-/* video MPEG decoder events: */ +-/* (code copied from dvb_frontend.c, should maybe be factored out...) */ +-#define MAX_VIDEO_EVENT 8 +-struct dvb_video_events { +- struct video_event events[MAX_VIDEO_EVENT]; +- int eventw; +- int eventr; +- int overflow; +- wait_queue_head_t wait_queue; +- spinlock_t lock; +-}; +- +- +-struct av7110; +- +-/* infrared remote control */ +-struct infrared { +- u16 key_map[256]; +- struct input_dev *input_dev; +- char input_phys[32]; +- struct timer_list keyup_timer; +- struct tasklet_struct ir_tasklet; +- void (*ir_handler)(struct av7110 *av7110, u32 ircom); +- u32 ir_command; +- u32 ir_config; +- u32 device_mask; +- u8 protocol; +- u8 inversion; +- u16 last_key; +- u16 last_toggle; +- u8 delay_timer_finished; +-}; +- +- +-/* place to store all the necessary device information */ +-struct av7110 { +- +- /* devices */ +- +- struct dvb_device dvb_dev; +- struct dvb_net dvb_net; +- +- struct video_device *v4l_dev; +- struct video_device *vbi_dev; +- +- struct saa7146_dev *dev; +- +- struct i2c_adapter i2c_adap; +- +- char *card_name; +- +- /* support for analog module of dvb-c */ +- int analog_tuner_flags; +- int current_input; +- u32 current_freq; +- +- struct tasklet_struct debi_tasklet; +- struct tasklet_struct gpio_tasklet; +- +- int adac_type; /* audio DAC type */ +-#define DVB_ADAC_TI 0 +-#define DVB_ADAC_CRYSTAL 1 +-#define DVB_ADAC_MSP34x0 2 +-#define DVB_ADAC_MSP34x5 3 +-#define DVB_ADAC_NONE -1 +- +- +- /* buffers */ +- +- void *iobuf; /* memory for all buffers */ +- struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ +-#define AVOUTLEN (128*1024) +- struct dvb_ringbuffer aout; /* buffer for audio */ +-#define AOUTLEN (64*1024) +- void *bmpbuf; +-#define BMPLEN (8*32768+1024) +- +- /* bitmap buffers and states */ +- +- int bmpp; +- int bmplen; +- volatile int bmp_state; +-#define BMP_NONE 0 +-#define BMP_LOADING 1 +-#define BMP_LOADED 2 +- wait_queue_head_t bmpq; +- +- +- /* DEBI and polled command interface */ +- +- spinlock_t debilock; +- struct mutex dcomlock; +- volatile int debitype; +- volatile int debilen; +- +- +- /* Recording and playback flags */ +- +- int rec_mode; +- int playing; +-#define RP_NONE 0 +-#define RP_VIDEO 1 +-#define RP_AUDIO 2 +-#define RP_AV 3 +- +- +- /* OSD */ +- +- int osdwin; /* currently active window */ +- u16 osdbpp[8]; +- struct mutex osd_mutex; +- +- /* CA */ +- +- ca_slot_info_t ci_slot[2]; +- +- enum av7110_video_mode vidmode; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- +- /* for budget mode demux1 */ +- struct dmxdev dmxdev1; +- struct dvb_demux demux1; +- struct dvb_net dvb_net1; +- spinlock_t feedlock1; +- int feeding1; +- u32 ttbp; +- unsigned char *grabbing; +- struct saa7146_pgtable pt; +- struct tasklet_struct vpe_tasklet; +- bool full_ts; +- +- int fe_synced; +- struct mutex pid_mutex; +- +- int video_blank; +- struct video_status videostate; +- u16 display_panscan; +- int display_ar; +- int trickmode; +-#define TRICK_NONE 0 +-#define TRICK_FAST 1 +-#define TRICK_SLOW 2 +-#define TRICK_FREEZE 3 +- struct audio_status audiostate; +- +- struct dvb_demux_filter *handle2filter[32]; +- struct av7110_p2t p2t_filter[MAXFILT]; +- struct dvb_filter_pes2ts p2t[2]; +- struct ipack ipack[2]; +- u8 *kbuf[2]; +- +- int sinfo; +- int feeding; +- +- int arm_errors; +- int registered; +- +- +- /* AV711X */ +- +- u32 arm_fw; +- u32 arm_rtsl; +- u32 arm_vid; +- u32 arm_app; +- u32 avtype; +- int arm_ready; +- struct task_struct *arm_thread; +- wait_queue_head_t arm_wait; +- u16 arm_loops; +- +- void *debi_virt; +- dma_addr_t debi_bus; +- +- u16 pids[DMX_PES_OTHER]; +- +- struct dvb_ringbuffer ci_rbuffer; +- struct dvb_ringbuffer ci_wbuffer; +- +- struct audio_mixer mixer; +- +- struct dvb_adapter dvb_adapter; +- struct dvb_device *video_dev; +- struct dvb_device *audio_dev; +- struct dvb_device *ca_dev; +- struct dvb_device *osd_dev; +- +- struct dvb_video_events video_events; +- video_size_t video_size; +- +- u16 wssMode; +- u16 wssData; +- +- struct infrared ir; +- +- /* firmware stuff */ +- unsigned char *bin_fw; +- unsigned long size_fw; +- +- unsigned char *bin_dpram; +- unsigned long size_dpram; +- +- unsigned char *bin_root; +- unsigned long size_root; +- +- struct dvb_frontend* fe; +- fe_status_t fe_status; +- +- /* crash recovery */ +- void (*recover)(struct av7110* av7110); +- fe_sec_voltage_t saved_voltage; +- fe_sec_tone_mode_t saved_tone; +- struct dvb_diseqc_master_cmd saved_master_cmd; +- fe_sec_mini_cmd_t saved_minicmd; +- +- int (*fe_init)(struct dvb_frontend* fe); +- int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); +- int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); +- int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); +- int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); +- int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); +- int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); +- int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); +- int (*fe_set_frontend)(struct dvb_frontend *fe); +-}; +- +- +-extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, +- u16 subpid, u16 pcrpid); +- +-extern int av7110_check_ir_config(struct av7110 *av7110, int force); +-extern int av7110_ir_init(struct av7110 *av7110); +-extern void av7110_ir_exit(struct av7110 *av7110); +- +-/* msp3400 i2c subaddresses */ +-#define MSP_WR_DEM 0x10 +-#define MSP_RD_DEM 0x11 +-#define MSP_WR_DSP 0x12 +-#define MSP_RD_DSP 0x13 +- +-extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); +-extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); +-extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); +- +- +-extern int av7110_init_analog_module(struct av7110 *av7110); +-extern int av7110_init_v4l(struct av7110 *av7110); +-extern int av7110_exit_v4l(struct av7110 *av7110); +- +-#endif /* _AV7110_H_ */ +diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c +deleted file mode 100644 +index 952b33d..0000000 +--- a/drivers/media/dvb/ttpci/av7110_av.c ++++ /dev/null +@@ -1,1626 +0,0 @@ +-/* +- * av7110_av.c: audio and video MPEG decoder stuff +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * originally based on code by: +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "av7110.h" +-#include "av7110_hw.h" +-#include "av7110_av.h" +-#include "av7110_ipack.h" +- +-/* MPEG-2 (ISO 13818 / H.222.0) stream types */ +-#define PROG_STREAM_MAP 0xBC +-#define PRIVATE_STREAM1 0xBD +-#define PADDING_STREAM 0xBE +-#define PRIVATE_STREAM2 0xBF +-#define AUDIO_STREAM_S 0xC0 +-#define AUDIO_STREAM_E 0xDF +-#define VIDEO_STREAM_S 0xE0 +-#define VIDEO_STREAM_E 0xEF +-#define ECM_STREAM 0xF0 +-#define EMM_STREAM 0xF1 +-#define DSM_CC_STREAM 0xF2 +-#define ISO13522_STREAM 0xF3 +-#define PROG_STREAM_DIR 0xFF +- +-#define PTS_DTS_FLAGS 0xC0 +- +-//pts_dts flags +-#define PTS_ONLY 0x80 +-#define PTS_DTS 0xC0 +-#define TS_SIZE 188 +-#define TRANS_ERROR 0x80 +-#define PAY_START 0x40 +-#define TRANS_PRIO 0x20 +-#define PID_MASK_HI 0x1F +-//flags +-#define TRANS_SCRMBL1 0x80 +-#define TRANS_SCRMBL2 0x40 +-#define ADAPT_FIELD 0x20 +-#define PAYLOAD 0x10 +-#define COUNT_MASK 0x0F +- +-// adaptation flags +-#define DISCON_IND 0x80 +-#define RAND_ACC_IND 0x40 +-#define ES_PRI_IND 0x20 +-#define PCR_FLAG 0x10 +-#define OPCR_FLAG 0x08 +-#define SPLICE_FLAG 0x04 +-#define TRANS_PRIV 0x02 +-#define ADAP_EXT_FLAG 0x01 +- +-// adaptation extension flags +-#define LTW_FLAG 0x80 +-#define PIECE_RATE 0x40 +-#define SEAM_SPLICE 0x20 +- +- +-static void p_to_t(u8 const *buf, long int length, u16 pid, +- u8 *counter, struct dvb_demux_feed *feed); +-static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); +- +- +-int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; +- +- if (!(dvbdmxfeed->ts_type & TS_PACKET)) +- return 0; +- if (buf[3] == 0xe0) // video PES do not have a length in TS +- buf[4] = buf[5] = 0; +- if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) +- return dvbdmxfeed->cb.ts(buf, len, NULL, 0, +- &dvbdmxfeed->feed.ts, DMX_OK); +- else +- return dvb_filter_pes2ts(p2t, buf, len, 1); +-} +- +-static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) +-{ +- struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; +- +- dvbdmxfeed->cb.ts(data, 188, NULL, 0, +- &dvbdmxfeed->feed.ts, DMX_OK); +- return 0; +-} +- +-int av7110_av_start_record(struct av7110 *av7110, int av, +- struct dvb_demux_feed *dvbdmxfeed) +-{ +- int ret = 0; +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- +- dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); +- +- if (av7110->playing || (av7110->rec_mode & av)) +- return -EBUSY; +- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); +- dvbdmx->recording = 1; +- av7110->rec_mode |= av; +- +- switch (av7110->rec_mode) { +- case RP_AUDIO: +- dvb_filter_pes2ts_init(&av7110->p2t[0], +- dvbdmx->pesfilter[0]->pid, +- dvb_filter_pes2ts_cb, +- (void *) dvbdmx->pesfilter[0]); +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); +- break; +- +- case RP_VIDEO: +- dvb_filter_pes2ts_init(&av7110->p2t[1], +- dvbdmx->pesfilter[1]->pid, +- dvb_filter_pes2ts_cb, +- (void *) dvbdmx->pesfilter[1]); +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); +- break; +- +- case RP_AV: +- dvb_filter_pes2ts_init(&av7110->p2t[0], +- dvbdmx->pesfilter[0]->pid, +- dvb_filter_pes2ts_cb, +- (void *) dvbdmx->pesfilter[0]); +- dvb_filter_pes2ts_init(&av7110->p2t[1], +- dvbdmx->pesfilter[1]->pid, +- dvb_filter_pes2ts_cb, +- (void *) dvbdmx->pesfilter[1]); +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); +- break; +- } +- return ret; +-} +- +-int av7110_av_start_play(struct av7110 *av7110, int av) +-{ +- int ret = 0; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (av7110->rec_mode) +- return -EBUSY; +- if (av7110->playing & av) +- return -EBUSY; +- +- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); +- +- if (av7110->playing == RP_NONE) { +- av7110_ipack_reset(&av7110->ipack[0]); +- av7110_ipack_reset(&av7110->ipack[1]); +- } +- +- av7110->playing |= av; +- switch (av7110->playing) { +- case RP_AUDIO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); +- break; +- case RP_VIDEO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); +- av7110->sinfo = 0; +- break; +- case RP_AV: +- av7110->sinfo = 0; +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); +- break; +- } +- return ret; +-} +- +-int av7110_av_stop(struct av7110 *av7110, int av) +-{ +- int ret = 0; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (!(av7110->playing & av) && !(av7110->rec_mode & av)) +- return 0; +- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); +- if (av7110->playing) { +- av7110->playing &= ~av; +- switch (av7110->playing) { +- case RP_AUDIO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); +- break; +- case RP_VIDEO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); +- break; +- case RP_NONE: +- ret = av7110_set_vidmode(av7110, av7110->vidmode); +- break; +- } +- } else { +- av7110->rec_mode &= ~av; +- switch (av7110->rec_mode) { +- case RP_AUDIO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); +- break; +- case RP_VIDEO: +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); +- break; +- case RP_NONE: +- break; +- } +- } +- return ret; +-} +- +- +-int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) +-{ +- int len; +- u32 sync; +- u16 blen; +- +- if (!dlen) { +- wake_up(&buf->queue); +- return -1; +- } +- while (1) { +- len = dvb_ringbuffer_avail(buf); +- if (len < 6) { +- wake_up(&buf->queue); +- return -1; +- } +- sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; +- sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; +- sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; +- sync |= DVB_RINGBUFFER_PEEK(buf, 3); +- +- if (((sync &~ 0x0f) == 0x000001e0) || +- ((sync &~ 0x1f) == 0x000001c0) || +- (sync == 0x000001bd)) +- break; +- printk("resync\n"); +- DVB_RINGBUFFER_SKIP(buf, 1); +- } +- blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; +- blen |= DVB_RINGBUFFER_PEEK(buf, 5); +- blen += 6; +- if (len < blen || blen > dlen) { +- //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); +- wake_up(&buf->queue); +- return -1; +- } +- +- dvb_ringbuffer_read(buf, dest, (size_t) blen); +- +- dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", +- (unsigned long) buf->pread, (unsigned long) buf->pwrite); +- wake_up(&buf->queue); +- return blen; +-} +- +- +-int av7110_set_volume(struct av7110 *av7110, int volleft, int volright) +-{ +- int err, vol, val, balance = 0; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- av7110->mixer.volume_left = volleft; +- av7110->mixer.volume_right = volright; +- +- switch (av7110->adac_type) { +- case DVB_ADAC_TI: +- volleft = (volleft * 256) / 1036; +- volright = (volright * 256) / 1036; +- if (volleft > 0x3f) +- volleft = 0x3f; +- if (volright > 0x3f) +- volright = 0x3f; +- if ((err = SendDAC(av7110, 3, 0x80 + volleft))) +- return err; +- return SendDAC(av7110, 4, volright); +- +- case DVB_ADAC_CRYSTAL: +- volleft = 127 - volleft / 2; +- volright = 127 - volright / 2; +- i2c_writereg(av7110, 0x20, 0x03, volleft); +- i2c_writereg(av7110, 0x20, 0x04, volright); +- return 0; +- +- case DVB_ADAC_MSP34x0: +- vol = (volleft > volright) ? volleft : volright; +- val = (vol * 0x73 / 255) << 8; +- if (vol > 0) +- balance = ((volright - volleft) * 127) / vol; +- msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ +- msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ +- return 0; +- +- case DVB_ADAC_MSP34x5: +- vol = (volleft > volright) ? volleft : volright; +- val = (vol * 0x73 / 255) << 8; +- if (vol > 0) +- balance = ((volright - volleft) * 127) / vol; +- msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ +- return 0; +- } +- +- return 0; +-} +- +-int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) +-{ +- int ret; +- dprintk(2, "av7110:%p, \n", av7110); +- +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); +- +- if (!ret && !av7110->playing) { +- ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], +- av7110->pids[DMX_PES_AUDIO], +- av7110->pids[DMX_PES_TELETEXT], +- 0, av7110->pids[DMX_PES_PCR]); +- if (!ret) +- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); +- } +- return ret; +-} +- +- +-static enum av7110_video_mode sw2mode[16] = { +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, +- AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, +- AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +- AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, +-}; +- +-static int get_video_format(struct av7110 *av7110, u8 *buf, int count) +-{ +- int i; +- int hsize, vsize; +- int sw; +- u8 *p; +- int ret = 0; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (av7110->sinfo) +- return 0; +- for (i = 7; i < count - 10; i++) { +- p = buf + i; +- if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) +- continue; +- p += 4; +- hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); +- vsize = ((p[1] &0x0F) << 8) | (p[2]); +- sw = (p[3] & 0x0F); +- ret = av7110_set_vidmode(av7110, sw2mode[sw]); +- if (!ret) { +- dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); +- av7110->sinfo = 1; +- } +- break; +- } +- return ret; +-} +- +- +-/**************************************************************************** +- * I/O buffer management and control +- ****************************************************************************/ +- +-static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, +- const u8 *buf, unsigned long count) +-{ +- unsigned long todo = count; +- int free; +- +- while (todo > 0) { +- if (dvb_ringbuffer_free(rbuf) < 2048) { +- if (wait_event_interruptible(rbuf->queue, +- (dvb_ringbuffer_free(rbuf) >= 2048))) +- return count - todo; +- } +- free = dvb_ringbuffer_free(rbuf); +- if (free > todo) +- free = todo; +- dvb_ringbuffer_write(rbuf, buf, free); +- todo -= free; +- buf += free; +- } +- +- return count - todo; +-} +- +-static void play_video_cb(u8 *buf, int count, void *priv) +-{ +- struct av7110 *av7110 = (struct av7110 *) priv; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if ((buf[3] & 0xe0) == 0xe0) { +- get_video_format(av7110, buf, count); +- aux_ring_buffer_write(&av7110->avout, buf, count); +- } else +- aux_ring_buffer_write(&av7110->aout, buf, count); +-} +- +-static void play_audio_cb(u8 *buf, int count, void *priv) +-{ +- struct av7110 *av7110 = (struct av7110 *) priv; +- dprintk(2, "av7110:%p, \n", av7110); +- +- aux_ring_buffer_write(&av7110->aout, buf, count); +-} +- +- +-#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) +- +-static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, +- unsigned long count, int nonblock, int type) +-{ +- struct dvb_ringbuffer *rb; +- u8 *kb; +- unsigned long todo = count; +- +- dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); +- +- rb = (type) ? &av7110->avout : &av7110->aout; +- kb = av7110->kbuf[type]; +- +- if (!kb) +- return -ENOBUFS; +- +- if (nonblock && !FREE_COND_TS) +- return -EWOULDBLOCK; +- +- while (todo >= TS_SIZE) { +- if (!FREE_COND_TS) { +- if (nonblock) +- return count - todo; +- if (wait_event_interruptible(rb->queue, FREE_COND_TS)) +- return count - todo; +- } +- if (copy_from_user(kb, buf, TS_SIZE)) +- return -EFAULT; +- write_ts_to_decoder(av7110, type, kb, TS_SIZE); +- todo -= TS_SIZE; +- buf += TS_SIZE; +- } +- +- return count - todo; +-} +- +- +-#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ +- dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) +- +-static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, +- unsigned long count, int nonblock, int type) +-{ +- unsigned long todo = count, n; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (!av7110->kbuf[type]) +- return -ENOBUFS; +- +- if (nonblock && !FREE_COND) +- return -EWOULDBLOCK; +- +- while (todo > 0) { +- if (!FREE_COND) { +- if (nonblock) +- return count - todo; +- if (wait_event_interruptible(av7110->avout.queue, +- FREE_COND)) +- return count - todo; +- } +- n = todo; +- if (n > IPACKS * 2) +- n = IPACKS * 2; +- if (copy_from_user(av7110->kbuf[type], buf, n)) +- return -EFAULT; +- av7110_ipack_instant_repack(av7110->kbuf[type], n, +- &av7110->ipack[type]); +- todo -= n; +- buf += n; +- } +- return count - todo; +-} +- +-static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, +- unsigned long count, int nonblock, int type) +-{ +- unsigned long todo = count, n; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (!av7110->kbuf[type]) +- return -ENOBUFS; +- +- if (nonblock && !FREE_COND) +- return -EWOULDBLOCK; +- +- while (todo > 0) { +- if (!FREE_COND) { +- if (nonblock) +- return count - todo; +- if (wait_event_interruptible(av7110->avout.queue, +- FREE_COND)) +- return count - todo; +- } +- n = todo; +- if (n > IPACKS * 2) +- n = IPACKS * 2; +- av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); +- todo -= n; +- buf += n; +- } +- return count - todo; +-} +- +-static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, +- unsigned long count, int nonblock, int type) +-{ +- unsigned long todo = count, n; +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (!av7110->kbuf[type]) +- return -ENOBUFS; +- if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) +- return -EWOULDBLOCK; +- +- while (todo > 0) { +- if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { +- if (nonblock) +- return count - todo; +- if (wait_event_interruptible(av7110->aout.queue, +- (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) +- return count-todo; +- } +- n = todo; +- if (n > IPACKS * 2) +- n = IPACKS * 2; +- if (copy_from_user(av7110->kbuf[type], buf, n)) +- return -EFAULT; +- av7110_ipack_instant_repack(av7110->kbuf[type], n, +- &av7110->ipack[type]); +- todo -= n; +- buf += n; +- } +- return count - todo; +-} +- +-void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) +-{ +- memset(p->pes, 0, TS_SIZE); +- p->counter = 0; +- p->pos = 0; +- p->frags = 0; +- if (feed) +- p->feed = feed; +-} +- +-static void clear_p2t(struct av7110_p2t *p) +-{ +- memset(p->pes, 0, TS_SIZE); +-// p->counter = 0; +- p->pos = 0; +- p->frags = 0; +-} +- +- +-static int find_pes_header(u8 const *buf, long int length, int *frags) +-{ +- int c = 0; +- int found = 0; +- +- *frags = 0; +- +- while (c < length - 3 && !found) { +- if (buf[c] == 0x00 && buf[c + 1] == 0x00 && +- buf[c + 2] == 0x01) { +- switch ( buf[c + 3] ) { +- case PROG_STREAM_MAP: +- case PRIVATE_STREAM2: +- case PROG_STREAM_DIR: +- case ECM_STREAM : +- case EMM_STREAM : +- case PADDING_STREAM : +- case DSM_CC_STREAM : +- case ISO13522_STREAM: +- case PRIVATE_STREAM1: +- case AUDIO_STREAM_S ... AUDIO_STREAM_E: +- case VIDEO_STREAM_S ... VIDEO_STREAM_E: +- found = 1; +- break; +- +- default: +- c++; +- break; +- } +- } else +- c++; +- } +- if (c == length - 3 && !found) { +- if (buf[length - 1] == 0x00) +- *frags = 1; +- if (buf[length - 2] == 0x00 && +- buf[length - 1] == 0x00) +- *frags = 2; +- if (buf[length - 3] == 0x00 && +- buf[length - 2] == 0x00 && +- buf[length - 1] == 0x01) +- *frags = 3; +- return -1; +- } +- +- return c; +-} +- +-void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) +-{ +- int c, c2, l, add; +- int check, rest; +- +- c = 0; +- c2 = 0; +- if (p->frags){ +- check = 0; +- switch(p->frags) { +- case 1: +- if (buf[c] == 0x00 && buf[c + 1] == 0x01) { +- check = 1; +- c += 2; +- } +- break; +- case 2: +- if (buf[c] == 0x01) { +- check = 1; +- c++; +- } +- break; +- case 3: +- check = 1; +- } +- if (check) { +- switch (buf[c]) { +- case PROG_STREAM_MAP: +- case PRIVATE_STREAM2: +- case PROG_STREAM_DIR: +- case ECM_STREAM : +- case EMM_STREAM : +- case PADDING_STREAM : +- case DSM_CC_STREAM : +- case ISO13522_STREAM: +- case PRIVATE_STREAM1: +- case AUDIO_STREAM_S ... AUDIO_STREAM_E: +- case VIDEO_STREAM_S ... VIDEO_STREAM_E: +- p->pes[0] = 0x00; +- p->pes[1] = 0x00; +- p->pes[2] = 0x01; +- p->pes[3] = buf[c]; +- p->pos = 4; +- memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); +- c += (TS_SIZE - 4) - p->pos; +- p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); +- clear_p2t(p); +- break; +- +- default: +- c = 0; +- break; +- } +- } +- p->frags = 0; +- } +- +- if (p->pos) { +- c2 = find_pes_header(buf + c, length - c, &p->frags); +- if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) +- l = c2+c; +- else +- l = (TS_SIZE - 4) - p->pos; +- memcpy(p->pes + p->pos, buf, l); +- c += l; +- p->pos += l; +- p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); +- clear_p2t(p); +- } +- +- add = 0; +- while (c < length) { +- c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); +- if (c2 >= 0) { +- c2 += c + add; +- if (c2 > c){ +- p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); +- c = c2; +- clear_p2t(p); +- add = 0; +- } else +- add = 1; +- } else { +- l = length - c; +- rest = l % (TS_SIZE - 4); +- l -= rest; +- p_to_t(buf + c, l, pid, &p->counter, p->feed); +- memcpy(p->pes, buf + c + l, rest); +- p->pos = rest; +- c = length; +- } +- } +-} +- +- +-static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) +-{ +- int i; +- int c = 0; +- int fill; +- u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; +- +- fill = (TS_SIZE - 4) - length; +- if (pes_start) +- tshead[1] = 0x40; +- if (fill) +- tshead[3] = 0x30; +- tshead[1] |= (u8)((pid & 0x1F00) >> 8); +- tshead[2] |= (u8)(pid & 0x00FF); +- tshead[3] |= ((*counter)++ & 0x0F); +- memcpy(buf, tshead, 4); +- c += 4; +- +- if (fill) { +- buf[4] = fill - 1; +- c++; +- if (fill > 1) { +- buf[5] = 0x00; +- c++; +- } +- for (i = 6; i < fill + 4; i++) { +- buf[i] = 0xFF; +- c++; +- } +- } +- +- return c; +-} +- +- +-static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, +- struct dvb_demux_feed *feed) +-{ +- int l, pes_start; +- u8 obuf[TS_SIZE]; +- long c = 0; +- +- pes_start = 0; +- if (length > 3 && +- buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) +- switch (buf[3]) { +- case PROG_STREAM_MAP: +- case PRIVATE_STREAM2: +- case PROG_STREAM_DIR: +- case ECM_STREAM : +- case EMM_STREAM : +- case PADDING_STREAM : +- case DSM_CC_STREAM : +- case ISO13522_STREAM: +- case PRIVATE_STREAM1: +- case AUDIO_STREAM_S ... AUDIO_STREAM_E: +- case VIDEO_STREAM_S ... VIDEO_STREAM_E: +- pes_start = 1; +- break; +- +- default: +- break; +- } +- +- while (c < length) { +- memset(obuf, 0, TS_SIZE); +- if (length - c >= (TS_SIZE - 4)){ +- l = write_ts_header2(pid, counter, pes_start, +- obuf, (TS_SIZE - 4)); +- memcpy(obuf + l, buf + c, TS_SIZE - l); +- c += TS_SIZE - l; +- } else { +- l = write_ts_header2(pid, counter, pes_start, +- obuf, length - c); +- memcpy(obuf + l, buf + c, TS_SIZE - l); +- c = length; +- } +- feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK); +- pes_start = 0; +- } +-} +- +- +-static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) +-{ +- struct ipack *ipack = &av7110->ipack[type]; +- +- if (buf[1] & TRANS_ERROR) { +- av7110_ipack_reset(ipack); +- return -1; +- } +- +- if (!(buf[3] & PAYLOAD)) +- return -1; +- +- if (buf[1] & PAY_START) +- av7110_ipack_flush(ipack); +- +- if (buf[3] & ADAPT_FIELD) { +- len -= buf[4] + 1; +- buf += buf[4] + 1; +- if (!len) +- return 0; +- } +- +- av7110_ipack_instant_repack(buf + 4, len - 4, ipack); +- return 0; +-} +- +- +-int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) +-{ +- struct dvb_demux *demux = feed->demux; +- struct av7110 *av7110 = (struct av7110 *) demux->priv; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) +- return 0; +- +- switch (feed->pes_type) { +- case 0: +- if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) +- return -EINVAL; +- break; +- case 1: +- if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) +- return -EINVAL; +- break; +- default: +- return -1; +- } +- +- return write_ts_to_decoder(av7110, feed->pes_type, buf, len); +-} +- +- +- +-/****************************************************************************** +- * Video MPEG decoder events +- ******************************************************************************/ +-void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) +-{ +- struct dvb_video_events *events = &av7110->video_events; +- int wp; +- +- spin_lock_bh(&events->lock); +- +- wp = (events->eventw + 1) % MAX_VIDEO_EVENT; +- if (wp == events->eventr) { +- events->overflow = 1; +- events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; +- } +- +- //FIXME: timestamp? +- memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); +- events->eventw = wp; +- +- spin_unlock_bh(&events->lock); +- +- wake_up_interruptible(&events->wait_queue); +-} +- +- +-static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) +-{ +- struct dvb_video_events *events = &av7110->video_events; +- +- if (events->overflow) { +- events->overflow = 0; +- return -EOVERFLOW; +- } +- if (events->eventw == events->eventr) { +- int ret; +- +- if (flags & O_NONBLOCK) +- return -EWOULDBLOCK; +- +- ret = wait_event_interruptible(events->wait_queue, +- events->eventw != events->eventr); +- if (ret < 0) +- return ret; +- } +- +- spin_lock_bh(&events->lock); +- +- memcpy(event, &events->events[events->eventr], +- sizeof(struct video_event)); +- events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; +- +- spin_unlock_bh(&events->lock); +- +- return 0; +-} +- +- +-/****************************************************************************** +- * DVB device file operations +- ******************************************************************************/ +- +-static unsigned int dvb_video_poll(struct file *file, poll_table *wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned int mask = 0; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) +- poll_wait(file, &av7110->avout.queue, wait); +- +- poll_wait(file, &av7110->video_events.wait_queue, wait); +- +- if (av7110->video_events.eventw != av7110->video_events.eventr) +- mask = POLLPRI; +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) { +- if (av7110->playing) { +- if (FREE_COND) +- mask |= (POLLOUT | POLLWRNORM); +- } else /* if not playing: may play if asked for */ +- mask |= (POLLOUT | POLLWRNORM); +- } +- +- return mask; +-} +- +-static ssize_t dvb_video_write(struct file *file, const char __user *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned char c; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) +- return -EPERM; +- +- if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) +- return -EPERM; +- +- if (get_user(c, buf)) +- return -EFAULT; +- if (c == 0x47 && count % TS_SIZE == 0) +- return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); +- else +- return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); +-} +- +-static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned int mask = 0; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- poll_wait(file, &av7110->aout.queue, wait); +- +- if (av7110->playing) { +- if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) +- mask |= (POLLOUT | POLLWRNORM); +- } else /* if not playing: may play if asked for */ +- mask = (POLLOUT | POLLWRNORM); +- +- return mask; +-} +- +-static ssize_t dvb_audio_write(struct file *file, const char __user *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned char c; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { +- printk(KERN_ERR "not audio source memory\n"); +- return -EPERM; +- } +- +- if (get_user(c, buf)) +- return -EFAULT; +- if (c == 0x47 && count % TS_SIZE == 0) +- return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); +- else +- return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); +-} +- +-static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; +- +-#define MIN_IFRAME 400000 +- +-static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) +-{ +- unsigned i, n; +- int progressive = 0; +- int match = 0; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (!(av7110->playing & RP_VIDEO)) { +- if (av7110_av_start_play(av7110, RP_VIDEO) < 0) +- return -EBUSY; +- } +- +- /* search in buf for instances of 00 00 01 b5 1? */ +- for (i = 0; i < len; i++) { +- unsigned char c; +- if (get_user(c, buf + i)) +- return -EFAULT; +- if (match == 5) { +- progressive = c & 0x08; +- match = 0; +- } +- if (c == 0x00) { +- match = (match == 1 || match == 2) ? 2 : 1; +- continue; +- } +- switch (match++) { +- case 2: if (c == 0x01) +- continue; +- break; +- case 3: if (c == 0xb5) +- continue; +- break; +- case 4: if ((c & 0xf0) == 0x10) +- continue; +- break; +- } +- match = 0; +- } +- +- /* setting n always > 1, fixes problems when playing stillframes +- consisting of I- and P-Frames */ +- n = MIN_IFRAME / len + 1; +- +- /* FIXME: nonblock? */ +- dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); +- +- for (i = 0; i < n; i++) +- dvb_play(av7110, buf, len, 0, 1); +- +- av7110_ipack_flush(&av7110->ipack[1]); +- +- if (progressive) +- return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); +- else +- return 0; +-} +- +- +-static int dvb_video_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned long arg = (unsigned long) parg; +- int ret = 0; +- +- dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); +- +- if ((file->f_flags & O_ACCMODE) == O_RDONLY) { +- if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && +- cmd != VIDEO_GET_SIZE ) { +- return -EPERM; +- } +- } +- +- switch (cmd) { +- case VIDEO_STOP: +- av7110->videostate.play_state = VIDEO_STOPPED; +- if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) +- ret = av7110_av_stop(av7110, RP_VIDEO); +- else +- ret = vidcom(av7110, AV_VIDEO_CMD_STOP, +- av7110->videostate.video_blank ? 0 : 1); +- if (!ret) +- av7110->trickmode = TRICK_NONE; +- break; +- +- case VIDEO_PLAY: +- av7110->trickmode = TRICK_NONE; +- if (av7110->videostate.play_state == VIDEO_FREEZED) { +- av7110->videostate.play_state = VIDEO_PLAYING; +- ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); +- if (ret) +- break; +- } +- if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { +- if (av7110->playing == RP_AV) { +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); +- if (ret) +- break; +- av7110->playing &= ~RP_VIDEO; +- } +- ret = av7110_av_start_play(av7110, RP_VIDEO); +- } +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); +- if (!ret) +- av7110->videostate.play_state = VIDEO_PLAYING; +- break; +- +- case VIDEO_FREEZE: +- av7110->videostate.play_state = VIDEO_FREEZED; +- if (av7110->playing & RP_VIDEO) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); +- else +- ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); +- if (!ret) +- av7110->trickmode = TRICK_FREEZE; +- break; +- +- case VIDEO_CONTINUE: +- if (av7110->playing & RP_VIDEO) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); +- if (!ret) { +- av7110->videostate.play_state = VIDEO_PLAYING; +- av7110->trickmode = TRICK_NONE; +- } +- break; +- +- case VIDEO_SELECT_SOURCE: +- av7110->videostate.stream_source = (video_stream_source_t) arg; +- break; +- +- case VIDEO_SET_BLANK: +- av7110->videostate.video_blank = (int) arg; +- break; +- +- case VIDEO_GET_STATUS: +- memcpy(parg, &av7110->videostate, sizeof(struct video_status)); +- break; +- +- case VIDEO_GET_EVENT: +- ret = dvb_video_get_event(av7110, parg, file->f_flags); +- break; +- +- case VIDEO_GET_SIZE: +- memcpy(parg, &av7110->video_size, sizeof(video_size_t)); +- break; +- +- case VIDEO_SET_DISPLAY_FORMAT: +- { +- video_displayformat_t format = (video_displayformat_t) arg; +- switch (format) { +- case VIDEO_PAN_SCAN: +- av7110->display_panscan = VID_PAN_SCAN_PREF; +- break; +- case VIDEO_LETTER_BOX: +- av7110->display_panscan = VID_VC_AND_PS_PREF; +- break; +- case VIDEO_CENTER_CUT_OUT: +- av7110->display_panscan = VID_CENTRE_CUT_PREF; +- break; +- default: +- ret = -EINVAL; +- } +- if (ret < 0) +- break; +- av7110->videostate.display_format = format; +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, +- 1, av7110->display_panscan); +- break; +- } +- +- case VIDEO_SET_FORMAT: +- if (arg > 1) { +- ret = -EINVAL; +- break; +- } +- av7110->display_ar = arg; +- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, +- 1, (u16) arg); +- break; +- +- case VIDEO_STILLPICTURE: +- { +- struct video_still_picture *pic = +- (struct video_still_picture *) parg; +- av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); +- ret = play_iframe(av7110, pic->iFrame, pic->size, +- file->f_flags & O_NONBLOCK); +- break; +- } +- +- case VIDEO_FAST_FORWARD: +- //note: arg is ignored by firmware +- if (av7110->playing & RP_VIDEO) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, +- __Scan_I, 2, AV_PES, 0); +- else +- ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); +- if (!ret) { +- av7110->trickmode = TRICK_FAST; +- av7110->videostate.play_state = VIDEO_PLAYING; +- } +- break; +- +- case VIDEO_SLOWMOTION: +- if (av7110->playing&RP_VIDEO) { +- if (av7110->trickmode != TRICK_SLOW) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); +- } else { +- ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); +- } +- if (!ret) { +- av7110->trickmode = TRICK_SLOW; +- av7110->videostate.play_state = VIDEO_PLAYING; +- } +- break; +- +- case VIDEO_GET_CAPABILITIES: +- *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | +- VIDEO_CAP_SYS | VIDEO_CAP_PROG; +- break; +- +- case VIDEO_CLEAR_BUFFER: +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); +- av7110_ipack_reset(&av7110->ipack[1]); +- if (av7110->playing == RP_AV) { +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, +- __Play, 2, AV_PES, 0); +- if (ret) +- break; +- if (av7110->trickmode == TRICK_FAST) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, +- __Scan_I, 2, AV_PES, 0); +- if (av7110->trickmode == TRICK_SLOW) { +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, +- __Slow, 2, 0, 0); +- if (!ret) +- ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); +- } +- if (av7110->trickmode == TRICK_FREEZE) +- ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); +- } +- break; +- +- case VIDEO_SET_STREAMTYPE: +- break; +- +- default: +- ret = -ENOIOCTLCMD; +- break; +- } +- +- return ret; +-} +- +-static int dvb_audio_ioctl(struct file *file, +- unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned long arg = (unsigned long) parg; +- int ret = 0; +- +- dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); +- +- if (((file->f_flags & O_ACCMODE) == O_RDONLY) && +- (cmd != AUDIO_GET_STATUS)) +- return -EPERM; +- +- switch (cmd) { +- case AUDIO_STOP: +- if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) +- ret = av7110_av_stop(av7110, RP_AUDIO); +- else +- ret = audcom(av7110, AUDIO_CMD_MUTE); +- if (!ret) +- av7110->audiostate.play_state = AUDIO_STOPPED; +- break; +- +- case AUDIO_PLAY: +- if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) +- ret = av7110_av_start_play(av7110, RP_AUDIO); +- if (!ret) +- ret = audcom(av7110, AUDIO_CMD_UNMUTE); +- if (!ret) +- av7110->audiostate.play_state = AUDIO_PLAYING; +- break; +- +- case AUDIO_PAUSE: +- ret = audcom(av7110, AUDIO_CMD_MUTE); +- if (!ret) +- av7110->audiostate.play_state = AUDIO_PAUSED; +- break; +- +- case AUDIO_CONTINUE: +- if (av7110->audiostate.play_state == AUDIO_PAUSED) { +- av7110->audiostate.play_state = AUDIO_PLAYING; +- ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); +- } +- break; +- +- case AUDIO_SELECT_SOURCE: +- av7110->audiostate.stream_source = (audio_stream_source_t) arg; +- break; +- +- case AUDIO_SET_MUTE: +- { +- ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); +- if (!ret) +- av7110->audiostate.mute_state = (int) arg; +- break; +- } +- +- case AUDIO_SET_AV_SYNC: +- av7110->audiostate.AV_sync_state = (int) arg; +- ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); +- break; +- +- case AUDIO_SET_BYPASS_MODE: +- if (FW_VERSION(av7110->arm_app) < 0x2621) +- ret = -EINVAL; +- av7110->audiostate.bypass_mode = (int)arg; +- break; +- +- case AUDIO_CHANNEL_SELECT: +- av7110->audiostate.channel_select = (audio_channel_select_t) arg; +- switch(av7110->audiostate.channel_select) { +- case AUDIO_STEREO: +- ret = audcom(av7110, AUDIO_CMD_STEREO); +- if (!ret) { +- if (av7110->adac_type == DVB_ADAC_CRYSTAL) +- i2c_writereg(av7110, 0x20, 0x02, 0x49); +- else if (av7110->adac_type == DVB_ADAC_MSP34x5) +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); +- } +- break; +- case AUDIO_MONO_LEFT: +- ret = audcom(av7110, AUDIO_CMD_MONO_L); +- if (!ret) { +- if (av7110->adac_type == DVB_ADAC_CRYSTAL) +- i2c_writereg(av7110, 0x20, 0x02, 0x4a); +- else if (av7110->adac_type == DVB_ADAC_MSP34x5) +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); +- } +- break; +- case AUDIO_MONO_RIGHT: +- ret = audcom(av7110, AUDIO_CMD_MONO_R); +- if (!ret) { +- if (av7110->adac_type == DVB_ADAC_CRYSTAL) +- i2c_writereg(av7110, 0x20, 0x02, 0x45); +- else if (av7110->adac_type == DVB_ADAC_MSP34x5) +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); +- } +- break; +- default: +- ret = -EINVAL; +- break; +- } +- break; +- +- case AUDIO_GET_STATUS: +- memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); +- break; +- +- case AUDIO_GET_CAPABILITIES: +- if (FW_VERSION(av7110->arm_app) < 0x2621) +- *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; +- else +- *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | +- AUDIO_CAP_MP1 | AUDIO_CAP_MP2; +- break; +- +- case AUDIO_CLEAR_BUFFER: +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); +- av7110_ipack_reset(&av7110->ipack[0]); +- if (av7110->playing == RP_AV) +- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, +- __Play, 2, AV_PES, 0); +- break; +- +- case AUDIO_SET_ID: +- break; +- +- case AUDIO_SET_MIXER: +- { +- struct audio_mixer *amix = (struct audio_mixer *)parg; +- ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); +- break; +- } +- +- case AUDIO_SET_STREAMTYPE: +- break; +- +- default: +- ret = -ENOIOCTLCMD; +- } +- +- return ret; +-} +- +- +-static int dvb_video_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- int err; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if ((err = dvb_generic_open(inode, file)) < 0) +- return err; +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) { +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); +- av7110->video_blank = 1; +- av7110->audiostate.AV_sync_state = 1; +- av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; +- +- /* empty event queue */ +- av7110->video_events.eventr = av7110->video_events.eventw = 0; +- } +- +- return 0; +-} +- +-static int dvb_video_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if ((file->f_flags & O_ACCMODE) != O_RDONLY) { +- av7110_av_stop(av7110, RP_VIDEO); +- } +- +- return dvb_generic_release(inode, file); +-} +- +-static int dvb_audio_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- int err = dvb_generic_open(inode, file); +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- if (err < 0) +- return err; +- dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); +- av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; +- return 0; +-} +- +-static int dvb_audio_release(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- +- dprintk(2, "av7110:%p, \n", av7110); +- +- av7110_av_stop(av7110, RP_AUDIO); +- return dvb_generic_release(inode, file); +-} +- +- +- +-/****************************************************************************** +- * driver registration +- ******************************************************************************/ +- +-static const struct file_operations dvb_video_fops = { +- .owner = THIS_MODULE, +- .write = dvb_video_write, +- .unlocked_ioctl = dvb_generic_ioctl, +- .open = dvb_video_open, +- .release = dvb_video_release, +- .poll = dvb_video_poll, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_video = { +- .priv = NULL, +- .users = 6, +- .readers = 5, /* arbitrary */ +- .writers = 1, +- .fops = &dvb_video_fops, +- .kernel_ioctl = dvb_video_ioctl, +-}; +- +-static const struct file_operations dvb_audio_fops = { +- .owner = THIS_MODULE, +- .write = dvb_audio_write, +- .unlocked_ioctl = dvb_generic_ioctl, +- .open = dvb_audio_open, +- .release = dvb_audio_release, +- .poll = dvb_audio_poll, +- .llseek = noop_llseek, +-}; +- +-static struct dvb_device dvbdev_audio = { +- .priv = NULL, +- .users = 1, +- .writers = 1, +- .fops = &dvb_audio_fops, +- .kernel_ioctl = dvb_audio_ioctl, +-}; +- +- +-int av7110_av_register(struct av7110 *av7110) +-{ +- av7110->audiostate.AV_sync_state = 0; +- av7110->audiostate.mute_state = 0; +- av7110->audiostate.play_state = AUDIO_STOPPED; +- av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; +- av7110->audiostate.channel_select = AUDIO_STEREO; +- av7110->audiostate.bypass_mode = 0; +- +- av7110->videostate.video_blank = 0; +- av7110->videostate.play_state = VIDEO_STOPPED; +- av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; +- av7110->videostate.video_format = VIDEO_FORMAT_4_3; +- av7110->videostate.display_format = VIDEO_LETTER_BOX; +- av7110->display_ar = VIDEO_FORMAT_4_3; +- av7110->display_panscan = VID_VC_AND_PS_PREF; +- +- init_waitqueue_head(&av7110->video_events.wait_queue); +- spin_lock_init(&av7110->video_events.lock); +- av7110->video_events.eventw = av7110->video_events.eventr = 0; +- av7110->video_events.overflow = 0; +- memset(&av7110->video_size, 0, sizeof (video_size_t)); +- +- dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, +- &dvbdev_video, av7110, DVB_DEVICE_VIDEO); +- +- dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, +- &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); +- +- return 0; +-} +- +-void av7110_av_unregister(struct av7110 *av7110) +-{ +- dvb_unregister_device(av7110->audio_dev); +- dvb_unregister_device(av7110->video_dev); +-} +- +-int av7110_av_init(struct av7110 *av7110) +-{ +- void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; +- int i, ret; +- +- for (i = 0; i < 2; i++) { +- struct ipack *ipack = av7110->ipack + i; +- +- ret = av7110_ipack_init(ipack, IPACKS, play[i]); +- if (ret < 0) { +- if (i) +- av7110_ipack_free(--ipack); +- goto out; +- } +- ipack->data = av7110; +- } +- +- dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); +- dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); +- +- av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); +- av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; +-out: +- return ret; +-} +- +-void av7110_av_exit(struct av7110 *av7110) +-{ +- av7110_ipack_free(&av7110->ipack[0]); +- av7110_ipack_free(&av7110->ipack[1]); +-} +diff --git a/drivers/media/dvb/ttpci/av7110_av.h b/drivers/media/dvb/ttpci/av7110_av.h +deleted file mode 100644 +index 5f02ef8..0000000 +--- a/drivers/media/dvb/ttpci/av7110_av.h ++++ /dev/null +@@ -1,30 +0,0 @@ +-#ifndef _AV7110_AV_H_ +-#define _AV7110_AV_H_ +- +-struct av7110; +- +-extern int av7110_set_vidmode(struct av7110 *av7110, +- enum av7110_video_mode mode); +- +-extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); +-extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); +-extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); +- +-extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright); +-extern int av7110_av_stop(struct av7110 *av7110, int av); +-extern int av7110_av_start_record(struct av7110 *av7110, int av, +- struct dvb_demux_feed *dvbdmxfeed); +-extern int av7110_av_start_play(struct av7110 *av7110, int av); +- +-extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); +- +-extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); +-extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); +- +-extern int av7110_av_register(struct av7110 *av7110); +-extern void av7110_av_unregister(struct av7110 *av7110); +-extern int av7110_av_init(struct av7110 *av7110); +-extern void av7110_av_exit(struct av7110 *av7110); +- +- +-#endif /* _AV7110_AV_H_ */ +diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c +deleted file mode 100644 +index 9fc1dd0..0000000 +--- a/drivers/media/dvb/ttpci/av7110_ca.c ++++ /dev/null +@@ -1,387 +0,0 @@ +-/* +- * av7110_ca.c: CA and CI stuff +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * originally based on code by: +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "av7110.h" +-#include "av7110_hw.h" +-#include "av7110_ca.h" +- +- +-void CI_handle(struct av7110 *av7110, u8 *data, u16 len) +-{ +- dprintk(8, "av7110:%p\n",av7110); +- +- if (len < 3) +- return; +- switch (data[0]) { +- case CI_MSG_CI_INFO: +- if (data[2] != 1 && data[2] != 2) +- break; +- switch (data[1]) { +- case 0: +- av7110->ci_slot[data[2] - 1].flags = 0; +- break; +- case 1: +- av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; +- break; +- case 2: +- av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; +- break; +- } +- break; +- case CI_SWITCH_PRG_REPLY: +- //av7110->ci_stat=data[1]; +- break; +- default: +- break; +- } +-} +- +- +-void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) +-{ +- if (dvb_ringbuffer_free(cibuf) < len + 2) +- return; +- +- DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); +- DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); +- dvb_ringbuffer_write(cibuf, data, len); +- wake_up_interruptible(&cibuf->queue); +-} +- +- +-/****************************************************************************** +- * CI link layer file ops +- ******************************************************************************/ +- +-static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) +-{ +- struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; +- void *data; +- +- for (p = tab; *p; p++) { +- data = vmalloc(size); +- if (!data) { +- while (p-- != tab) { +- vfree(p[0]->data); +- p[0]->data = NULL; +- } +- return -ENOMEM; +- } +- dvb_ringbuffer_init(*p, data, size); +- } +- return 0; +-} +- +-static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +-{ +- dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); +- dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); +-} +- +-static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) +-{ +- vfree(cirbuf->data); +- cirbuf->data = NULL; +- vfree(ciwbuf->data); +- ciwbuf->data = NULL; +-} +- +-static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, +- int slots, ca_slot_info_t *slot) +-{ +- int i; +- int len = 0; +- u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; +- +- for (i = 0; i < 2; i++) { +- if (slots & (1 << i)) +- len += 8; +- } +- +- if (dvb_ringbuffer_free(cibuf) < len) +- return -EBUSY; +- +- for (i = 0; i < 2; i++) { +- if (slots & (1 << i)) { +- msg[2] = i; +- dvb_ringbuffer_write(cibuf, msg, 8); +- slot[i].flags = 0; +- } +- } +- +- return 0; +-} +- +-static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, +- const char __user *buf, size_t count, loff_t *ppos) +-{ +- int free; +- int non_blocking = file->f_flags & O_NONBLOCK; +- u8 *page = (u8 *)__get_free_page(GFP_USER); +- int res; +- +- if (!page) +- return -ENOMEM; +- +- res = -EINVAL; +- if (count > 2048) +- goto out; +- +- res = -EFAULT; +- if (copy_from_user(page, buf, count)) +- goto out; +- +- free = dvb_ringbuffer_free(cibuf); +- if (count + 2 > free) { +- res = -EWOULDBLOCK; +- if (non_blocking) +- goto out; +- res = -ERESTARTSYS; +- if (wait_event_interruptible(cibuf->queue, +- (dvb_ringbuffer_free(cibuf) >= count + 2))) +- goto out; +- } +- +- DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); +- DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); +- +- res = dvb_ringbuffer_write(cibuf, page, count); +-out: +- free_page((unsigned long)page); +- return res; +-} +- +-static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, +- char __user *buf, size_t count, loff_t *ppos) +-{ +- int avail; +- int non_blocking = file->f_flags & O_NONBLOCK; +- ssize_t len; +- +- if (!cibuf->data || !count) +- return 0; +- if (non_blocking && (dvb_ringbuffer_empty(cibuf))) +- return -EWOULDBLOCK; +- if (wait_event_interruptible(cibuf->queue, +- !dvb_ringbuffer_empty(cibuf))) +- return -ERESTARTSYS; +- avail = dvb_ringbuffer_avail(cibuf); +- if (avail < 4) +- return 0; +- len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; +- len |= DVB_RINGBUFFER_PEEK(cibuf, 1); +- if (avail < len + 2 || count < len) +- return -EINVAL; +- DVB_RINGBUFFER_SKIP(cibuf, 2); +- +- return dvb_ringbuffer_read_user(cibuf, buf, len); +-} +- +-static int dvb_ca_open(struct inode *inode, struct file *file) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- int err = dvb_generic_open(inode, file); +- +- dprintk(8, "av7110:%p\n",av7110); +- +- if (err < 0) +- return err; +- ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); +- return 0; +-} +- +-static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; +- struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; +- unsigned int mask = 0; +- +- dprintk(8, "av7110:%p\n",av7110); +- +- poll_wait(file, &rbuf->queue, wait); +- poll_wait(file, &wbuf->queue, wait); +- +- if (!dvb_ringbuffer_empty(rbuf)) +- mask |= (POLLIN | POLLRDNORM); +- +- if (dvb_ringbuffer_free(wbuf) > 1024) +- mask |= (POLLOUT | POLLWRNORM); +- +- return mask; +-} +- +-static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- unsigned long arg = (unsigned long) parg; +- +- dprintk(8, "av7110:%p\n",av7110); +- +- switch (cmd) { +- case CA_RESET: +- return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); +- break; +- case CA_GET_CAP: +- { +- ca_caps_t cap; +- +- cap.slot_num = 2; +- cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? +- CA_CI_LINK : CA_CI) | CA_DESCR; +- cap.descr_num = 16; +- cap.descr_type = CA_ECD; +- memcpy(parg, &cap, sizeof(cap)); +- break; +- } +- +- case CA_GET_SLOT_INFO: +- { +- ca_slot_info_t *info=(ca_slot_info_t *)parg; +- +- if (info->num < 0 || info->num > 1) +- return -EINVAL; +- av7110->ci_slot[info->num].num = info->num; +- av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? +- CA_CI_LINK : CA_CI; +- memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); +- break; +- } +- +- case CA_GET_MSG: +- break; +- +- case CA_SEND_MSG: +- break; +- +- case CA_GET_DESCR_INFO: +- { +- ca_descr_info_t info; +- +- info.num = 16; +- info.type = CA_ECD; +- memcpy(parg, &info, sizeof (info)); +- break; +- } +- +- case CA_SET_DESCR: +- { +- ca_descr_t *descr = (ca_descr_t*) parg; +- +- if (descr->index >= 16) +- return -EINVAL; +- if (descr->parity > 1) +- return -EINVAL; +- av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, +- (descr->index<<8)|descr->parity, +- (descr->cw[0]<<8)|descr->cw[1], +- (descr->cw[2]<<8)|descr->cw[3], +- (descr->cw[4]<<8)|descr->cw[5], +- (descr->cw[6]<<8)|descr->cw[7]); +- break; +- } +- +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-static ssize_t dvb_ca_write(struct file *file, const char __user *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- +- dprintk(8, "av7110:%p\n",av7110); +- return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); +-} +- +-static ssize_t dvb_ca_read(struct file *file, char __user *buf, +- size_t count, loff_t *ppos) +-{ +- struct dvb_device *dvbdev = file->private_data; +- struct av7110 *av7110 = dvbdev->priv; +- +- dprintk(8, "av7110:%p\n",av7110); +- return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); +-} +- +-static const struct file_operations dvb_ca_fops = { +- .owner = THIS_MODULE, +- .read = dvb_ca_read, +- .write = dvb_ca_write, +- .unlocked_ioctl = dvb_generic_ioctl, +- .open = dvb_ca_open, +- .release = dvb_generic_release, +- .poll = dvb_ca_poll, +- .llseek = default_llseek, +-}; +- +-static struct dvb_device dvbdev_ca = { +- .priv = NULL, +- .users = 1, +- .writers = 1, +- .fops = &dvb_ca_fops, +- .kernel_ioctl = dvb_ca_ioctl, +-}; +- +- +-int av7110_ca_register(struct av7110 *av7110) +-{ +- return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, +- &dvbdev_ca, av7110, DVB_DEVICE_CA); +-} +- +-void av7110_ca_unregister(struct av7110 *av7110) +-{ +- dvb_unregister_device(av7110->ca_dev); +-} +- +-int av7110_ca_init(struct av7110* av7110) +-{ +- return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); +-} +- +-void av7110_ca_exit(struct av7110* av7110) +-{ +- ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); +-} +diff --git a/drivers/media/dvb/ttpci/av7110_ca.h b/drivers/media/dvb/ttpci/av7110_ca.h +deleted file mode 100644 +index 70ee855..0000000 +--- a/drivers/media/dvb/ttpci/av7110_ca.h ++++ /dev/null +@@ -1,14 +0,0 @@ +-#ifndef _AV7110_CA_H_ +-#define _AV7110_CA_H_ +- +-struct av7110; +- +-extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); +-extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); +- +-extern int av7110_ca_register(struct av7110 *av7110); +-extern void av7110_ca_unregister(struct av7110 *av7110); +-extern int av7110_ca_init(struct av7110* av7110); +-extern void av7110_ca_exit(struct av7110* av7110); +- +-#endif /* _AV7110_CA_H_ */ +diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c +deleted file mode 100644 +index f1cbfe5..0000000 +--- a/drivers/media/dvb/ttpci/av7110_hw.c ++++ /dev/null +@@ -1,1208 +0,0 @@ +-/* +- * av7110_hw.c: av7110 low level hardware access and firmware interface +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * originally based on code by: +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-/* for debugging ARM communication: */ +-//#define COM_DEBUG +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "av7110.h" +-#include "av7110_hw.h" +- +-#define _NOHANDSHAKE +- +-/**************************************************************************** +- * DEBI functions +- ****************************************************************************/ +- +-/* This DEBI code is based on the Stradis driver +- by Nathan Laredo */ +- +-int av7110_debiwrite(struct av7110 *av7110, u32 config, +- int addr, u32 val, int count) +-{ +- struct saa7146_dev *dev = av7110->dev; +- +- if (count <= 0 || count > 32764) { +- printk("%s: invalid count %d\n", __func__, count); +- return -1; +- } +- if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { +- printk("%s: wait_for_debi_done failed\n", __func__); +- return -1; +- } +- saa7146_write(dev, DEBI_CONFIG, config); +- if (count <= 4) /* immediate transfer */ +- saa7146_write(dev, DEBI_AD, val); +- else /* block transfer */ +- saa7146_write(dev, DEBI_AD, av7110->debi_bus); +- saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); +- saa7146_write(dev, MC2, (2 << 16) | 2); +- return 0; +-} +- +-u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) +-{ +- struct saa7146_dev *dev = av7110->dev; +- u32 result = 0; +- +- if (count > 32764 || count <= 0) { +- printk("%s: invalid count %d\n", __func__, count); +- return 0; +- } +- if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { +- printk("%s: wait_for_debi_done #1 failed\n", __func__); +- return 0; +- } +- saa7146_write(dev, DEBI_AD, av7110->debi_bus); +- saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); +- +- saa7146_write(dev, DEBI_CONFIG, config); +- saa7146_write(dev, MC2, (2 << 16) | 2); +- if (count > 4) +- return count; +- if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { +- printk("%s: wait_for_debi_done #2 failed\n", __func__); +- return 0; +- } +- +- result = saa7146_read(dev, DEBI_AD); +- result &= (0xffffffffUL >> ((4 - count) * 8)); +- return result; +-} +- +- +- +-/* av7110 ARM core boot stuff */ +-#if 0 +-void av7110_reset_arm(struct av7110 *av7110) +-{ +- saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); +- +- /* Disable DEBI and GPIO irq */ +- SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); +- +- saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); +- msleep(30); /* the firmware needs some time to initialize */ +- +- ARM_ResetMailBox(av7110); +- +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); +- SAA7146_IER_ENABLE(av7110->dev, MASK_03); +- +- av7110->arm_ready = 1; +- dprintk(1, "reset ARM\n"); +-} +-#endif /* 0 */ +- +-static int waitdebi(struct av7110 *av7110, int adr, int state) +-{ +- int k; +- +- dprintk(4, "%p\n", av7110); +- +- for (k = 0; k < 100; k++) { +- if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) +- return 0; +- udelay(5); +- } +- return -ETIMEDOUT; +-} +- +-static int load_dram(struct av7110 *av7110, u32 *data, int len) +-{ +- int i; +- int blocks, rest; +- u32 base, bootblock = AV7110_BOOT_BLOCK; +- +- dprintk(4, "%p\n", av7110); +- +- blocks = len / AV7110_BOOT_MAX_SIZE; +- rest = len % AV7110_BOOT_MAX_SIZE; +- base = DRAM_START_CODE; +- +- for (i = 0; i < blocks; i++) { +- if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { +- printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); +- return -ETIMEDOUT; +- } +- dprintk(4, "writing DRAM block %d\n", i); +- mwdebi(av7110, DEBISWAB, bootblock, +- ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); +- bootblock ^= 0x1400; +- iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); +- base += AV7110_BOOT_MAX_SIZE; +- } +- +- if (rest > 0) { +- if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { +- printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); +- return -ETIMEDOUT; +- } +- if (rest > 4) +- mwdebi(av7110, DEBISWAB, bootblock, +- ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); +- else +- mwdebi(av7110, DEBISWAB, bootblock, +- ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); +- +- iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); +- } +- if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { +- printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); +- return -ETIMEDOUT; +- } +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); +- if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { +- printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); +- return -ETIMEDOUT; +- } +- return 0; +-} +- +- +-/* we cannot write av7110 DRAM directly, so load a bootloader into +- * the DPRAM which implements a simple boot protocol */ +-int av7110_bootarm(struct av7110 *av7110) +-{ +- const struct firmware *fw; +- const char *fw_name = "av7110/bootcode.bin"; +- struct saa7146_dev *dev = av7110->dev; +- u32 ret; +- int i; +- +- dprintk(4, "%p\n", av7110); +- +- av7110->arm_ready = 0; +- +- saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); +- +- /* Disable DEBI and GPIO irq */ +- SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); +- +- /* enable DEBI */ +- saa7146_write(av7110->dev, MC1, 0x08800880); +- saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- +- /* test DEBI */ +- iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); +- /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ +- iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); +- +- if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { +- printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: " +- "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", +- ret, 0x10325476); +- return -1; +- } +- for (i = 0; i < 8192; i += 4) +- iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); +- dprintk(2, "debi test OK\n"); +- +- /* boot */ +- dprintk(1, "load boot code\n"); +- saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); +- //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); +- //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); +- +- ret = request_firmware(&fw, fw_name, &dev->pci->dev); +- if (ret) { +- printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", +- fw_name); +- return ret; +- } +- +- mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); +- release_firmware(fw); +- iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); +- +- if (saa7146_wait_for_debi_done(av7110->dev, 1)) { +- printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " +- "saa7146_wait_for_debi_done() timed out\n"); +- return -ETIMEDOUT; +- } +- saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); +- mdelay(1); +- +- dprintk(1, "load dram code\n"); +- if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { +- printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " +- "load_dram() failed\n"); +- return -1; +- } +- +- saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); +- mdelay(1); +- +- dprintk(1, "load dpram code\n"); +- mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); +- +- if (saa7146_wait_for_debi_done(av7110->dev, 1)) { +- printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " +- "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); +- return -ETIMEDOUT; +- } +- saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); +- msleep(30); /* the firmware needs some time to initialize */ +- +- //ARM_ClearIrq(av7110); +- ARM_ResetMailBox(av7110); +- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); +- SAA7146_IER_ENABLE(av7110->dev, MASK_03); +- +- av7110->arm_errors = 0; +- av7110->arm_ready = 1; +- return 0; +-} +-MODULE_FIRMWARE("av7110/bootcode.bin"); +- +-/**************************************************************************** +- * DEBI command polling +- ****************************************************************************/ +- +-int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) +-{ +- unsigned long start; +- u32 stat; +- int err; +- +- if (FW_VERSION(av7110->arm_app) <= 0x261c) { +- /* not supported by old firmware */ +- msleep(50); +- return 0; +- } +- +- /* new firmware */ +- start = jiffies; +- for (;;) { +- err = time_after(jiffies, start + ARM_WAIT_FREE); +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- return -ERESTARTSYS; +- stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); +- mutex_unlock(&av7110->dcomlock); +- if ((stat & flags) == 0) +- break; +- if (err) { +- printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", +- __func__, stat & flags); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +- return 0; +-} +- +-static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +-{ +- int i; +- unsigned long start; +- char *type = NULL; +- u16 flags[2] = {0, 0}; +- u32 stat; +- int err; +- +-// dprintk(4, "%p\n", av7110); +- +- if (!av7110->arm_ready) { +- dprintk(1, "arm not ready.\n"); +- return -ENXIO; +- } +- +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_FREE); +- if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); +- av7110->arm_errors++; +- return -ETIMEDOUT; +- } +- msleep(1); +- } +- +- if (FW_VERSION(av7110->arm_app) <= 0x261f) +- wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); +- +-#ifndef _NOHANDSHAKE +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_SHAKE); +- if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +-#endif +- +- switch ((buf[0] >> 8) & 0xff) { +- case COMTYPE_PIDFILTER: +- case COMTYPE_ENCODER: +- case COMTYPE_REC_PLAY: +- case COMTYPE_MPEGDECODER: +- type = "MSG"; +- flags[0] = GPMQOver; +- flags[1] = GPMQFull; +- break; +- case COMTYPE_OSD: +- type = "OSD"; +- flags[0] = OSDQOver; +- flags[1] = OSDQFull; +- break; +- case COMTYPE_MISC: +- if (FW_VERSION(av7110->arm_app) >= 0x261d) { +- type = "MSG"; +- flags[0] = GPMQOver; +- flags[1] = GPMQBusy; +- } +- break; +- default: +- break; +- } +- +- if (type != NULL) { +- /* non-immediate COMMAND type */ +- start = jiffies; +- for (;;) { +- err = time_after(jiffies, start + ARM_WAIT_FREE); +- stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); +- if (stat & flags[0]) { +- printk(KERN_ERR "%s: %s QUEUE overflow\n", +- __func__, type); +- return -1; +- } +- if ((stat & flags[1]) == 0) +- break; +- if (err) { +- printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", +- __func__, type); +- av7110->arm_errors++; +- return -ETIMEDOUT; +- } +- msleep(1); +- } +- } +- +- for (i = 2; i < length; i++) +- wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); +- +- if (length) +- wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); +- else +- wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); +- +- wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); +- +- if (FW_VERSION(av7110->arm_app) <= 0x261f) +- wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); +- +-#ifdef COM_DEBUG +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_FREE); +- if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", +- __func__, (buf[0] >> 8) & 0xff); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +- +- stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); +- if (stat & GPMQOver) { +- printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); +- return -ENOSPC; +- } +- else if (stat & OSDQOver) { +- printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); +- return -ENOSPC; +- } +-#endif +- +- return 0; +-} +- +-static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) +-{ +- int ret; +- +-// dprintk(4, "%p\n", av7110); +- +- if (!av7110->arm_ready) { +- dprintk(1, "arm not ready.\n"); +- return -1; +- } +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- return -ERESTARTSYS; +- +- ret = __av7110_send_fw_cmd(av7110, buf, length); +- mutex_unlock(&av7110->dcomlock); +- if (ret && ret!=-ERESTARTSYS) +- printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", +- __func__, ret); +- return ret; +-} +- +-int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) +-{ +- va_list args; +- u16 buf[num + 2]; +- int i, ret; +- +-// dprintk(4, "%p\n", av7110); +- +- buf[0] = ((type << 8) | com); +- buf[1] = num; +- +- if (num) { +- va_start(args, num); +- for (i = 0; i < num; i++) +- buf[i + 2] = va_arg(args, u32); +- va_end(args); +- } +- +- ret = av7110_send_fw_cmd(av7110, buf, num + 2); +- if (ret && ret != -ERESTARTSYS) +- printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); +- return ret; +-} +- +-#if 0 +-int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) +-{ +- int i, ret; +- u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), +- 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +- +- dprintk(4, "%p\n", av7110); +- +- for(i = 0; i < len && i < 32; i++) +- { +- if(i % 2 == 0) +- cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; +- else +- cmd[(i / 2) + 2] |= buf[i]; +- } +- +- ret = av7110_send_fw_cmd(av7110, cmd, 18); +- if (ret && ret != -ERESTARTSYS) +- printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); +- return ret; +-} +-#endif /* 0 */ +- +-int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, +- int request_buf_len, u16 *reply_buf, int reply_buf_len) +-{ +- int err; +- s16 i; +- unsigned long start; +-#ifdef COM_DEBUG +- u32 stat; +-#endif +- +- dprintk(4, "%p\n", av7110); +- +- if (!av7110->arm_ready) { +- dprintk(1, "arm not ready.\n"); +- return -1; +- } +- +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- return -ERESTARTSYS; +- +- if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { +- mutex_unlock(&av7110->dcomlock); +- printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); +- return err; +- } +- +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_FREE); +- if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); +- mutex_unlock(&av7110->dcomlock); +- return -ETIMEDOUT; +- } +-#ifdef _NOHANDSHAKE +- msleep(1); +-#endif +- } +- +-#ifndef _NOHANDSHAKE +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_SHAKE); +- if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); +- mutex_unlock(&av7110->dcomlock); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +-#endif +- +-#ifdef COM_DEBUG +- stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); +- if (stat & GPMQOver) { +- printk(KERN_ERR "%s: GPMQOver\n", __func__); +- mutex_unlock(&av7110->dcomlock); +- return -1; +- } +- else if (stat & OSDQOver) { +- printk(KERN_ERR "%s: OSDQOver\n", __func__); +- mutex_unlock(&av7110->dcomlock); +- return -1; +- } +-#endif +- +- for (i = 0; i < reply_buf_len; i++) +- reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); +- +- mutex_unlock(&av7110->dcomlock); +- return 0; +-} +- +-static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) +-{ +- int ret; +- ret = av7110_fw_request(av7110, &tag, 0, buf, length); +- if (ret) +- printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); +- return ret; +-} +- +- +-/**************************************************************************** +- * Firmware commands +- ****************************************************************************/ +- +-/* get version of the firmware ROM, RTSL, video ucode and ARM application */ +-int av7110_firmversion(struct av7110 *av7110) +-{ +- u16 buf[20]; +- u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); +- +- dprintk(4, "%p\n", av7110); +- +- if (av7110_fw_query(av7110, tag, buf, 16)) { +- printk("dvb-ttpci: failed to boot firmware @ card %d\n", +- av7110->dvb_adapter.num); +- return -EIO; +- } +- +- av7110->arm_fw = (buf[0] << 16) + buf[1]; +- av7110->arm_rtsl = (buf[2] << 16) + buf[3]; +- av7110->arm_vid = (buf[4] << 16) + buf[5]; +- av7110->arm_app = (buf[6] << 16) + buf[7]; +- av7110->avtype = (buf[8] << 16) + buf[9]; +- +- printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", +- av7110->dvb_adapter.num, av7110->arm_fw, +- av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); +- +- /* print firmware capabilities */ +- if (FW_CI_LL_SUPPORT(av7110->arm_app)) +- printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", +- av7110->dvb_adapter.num); +- else +- printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", +- av7110->dvb_adapter.num); +- +- return 0; +-} +- +- +-int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) +-{ +- int i, ret; +- u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), +- 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +- +- dprintk(4, "%p\n", av7110); +- +- if (len > 10) +- len = 10; +- +- buf[1] = len + 2; +- buf[2] = len; +- +- if (burst != -1) +- buf[3] = burst ? 0x01 : 0x00; +- else +- buf[3] = 0xffff; +- +- for (i = 0; i < len; i++) +- buf[i + 4] = msg[i]; +- +- ret = av7110_send_fw_cmd(av7110, buf, 18); +- if (ret && ret!=-ERESTARTSYS) +- printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); +- return ret; +-} +- +- +-#ifdef CONFIG_DVB_AV7110_OSD +- +-static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); +-} +- +-static inline int SetBlend_(struct av7110 *av7110, u8 windownr, +- enum av7110_osd_palette_type colordepth, u16 index, u8 blending) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, +- windownr, colordepth, index, blending); +-} +- +-static inline int SetColor_(struct av7110 *av7110, u8 windownr, +- enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, +- windownr, colordepth, index, colorhi, colorlo); +-} +- +-static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, +- u16 colorfg, u16 colorbg) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, +- windownr, fontsize, colorfg, colorbg); +-} +- +-static int FlushText(struct av7110 *av7110) +-{ +- unsigned long start; +- int err; +- +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- return -ERESTARTSYS; +- start = jiffies; +- while (1) { +- err = time_after(jiffies, start + ARM_WAIT_OSD); +- if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) +- break; +- if (err) { +- printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", +- __func__); +- mutex_unlock(&av7110->dcomlock); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +- mutex_unlock(&av7110->dcomlock); +- return 0; +-} +- +-static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) +-{ +- int i, ret; +- unsigned long start; +- int length = strlen(buf) + 1; +- u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; +- +- if (mutex_lock_interruptible(&av7110->dcomlock)) +- return -ERESTARTSYS; +- +- start = jiffies; +- while (1) { +- ret = time_after(jiffies, start + ARM_WAIT_OSD); +- if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) +- break; +- if (ret) { +- printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", +- __func__); +- mutex_unlock(&av7110->dcomlock); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +-#ifndef _NOHANDSHAKE +- start = jiffies; +- while (1) { +- ret = time_after(jiffies, start + ARM_WAIT_SHAKE); +- if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) +- break; +- if (ret) { +- printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", +- __func__); +- mutex_unlock(&av7110->dcomlock); +- return -ETIMEDOUT; +- } +- msleep(1); +- } +-#endif +- for (i = 0; i < length / 2; i++) +- wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, +- swab16(*(u16 *)(buf + 2 * i)), 2); +- if (length & 1) +- wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); +- ret = __av7110_send_fw_cmd(av7110, cbuf, 5); +- mutex_unlock(&av7110->dcomlock); +- if (ret && ret!=-ERESTARTSYS) +- printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); +- return ret; +-} +- +-static inline int DrawLine(struct av7110 *av7110, u8 windownr, +- u16 x, u16 y, u16 dx, u16 dy, u16 color) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, +- windownr, x, y, dx, dy, color); +-} +- +-static inline int DrawBlock(struct av7110 *av7110, u8 windownr, +- u16 x, u16 y, u16 dx, u16 dy, u16 color) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, +- windownr, x, y, dx, dy, color); +-} +- +-static inline int HideWindow(struct av7110 *av7110, u8 windownr) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); +-} +- +-static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); +-} +- +-static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); +-} +- +-static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); +-} +- +-static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, +- osd_raw_window_t disptype, +- u16 width, u16 height) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, +- windownr, disptype, width, height); +-} +- +- +-static enum av7110_osd_palette_type bpp2pal[8] = { +- Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit +-}; +-static osd_raw_window_t bpp2bit[8] = { +- OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 +-}; +- +-static inline int WaitUntilBmpLoaded(struct av7110 *av7110) +-{ +- int ret = wait_event_timeout(av7110->bmpq, +- av7110->bmp_state != BMP_LOADING, 10*HZ); +- if (ret == 0) { +- printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", +- ret, av7110->bmp_state); +- av7110->bmp_state = BMP_NONE; +- return -ETIMEDOUT; +- } +- return 0; +-} +- +-static inline int LoadBitmap(struct av7110 *av7110, +- u16 dx, u16 dy, int inc, u8 __user * data) +-{ +- u16 format; +- int bpp; +- int i; +- int d, delta; +- u8 c; +- int ret; +- +- dprintk(4, "%p\n", av7110); +- +- format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; +- +- av7110->bmp_state = BMP_LOADING; +- if (format == OSD_BITMAP8) { +- bpp=8; delta = 1; +- } else if (format == OSD_BITMAP4) { +- bpp=4; delta = 2; +- } else if (format == OSD_BITMAP2) { +- bpp=2; delta = 4; +- } else if (format == OSD_BITMAP1) { +- bpp=1; delta = 8; +- } else { +- av7110->bmp_state = BMP_NONE; +- return -EINVAL; +- } +- av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; +- av7110->bmpp = 0; +- if (av7110->bmplen > 32768) { +- av7110->bmp_state = BMP_NONE; +- return -EINVAL; +- } +- for (i = 0; i < dy; i++) { +- if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { +- av7110->bmp_state = BMP_NONE; +- return -EINVAL; +- } +- } +- if (format != OSD_BITMAP8) { +- for (i = 0; i < dx * dy / delta; i++) { +- c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; +- for (d = delta - 2; d >= 0; d--) { +- c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] +- << ((delta - d - 1) * bpp)); +- ((u8 *)av7110->bmpbuf)[1024 + i] = c; +- } +- } +- } +- av7110->bmplen += 1024; +- dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); +- ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); +- if (!ret) +- ret = WaitUntilBmpLoaded(av7110); +- return ret; +-} +- +-static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) +-{ +- dprintk(4, "%p\n", av7110); +- +- return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); +-} +- +-static inline int ReleaseBitmap(struct av7110 *av7110) +-{ +- dprintk(4, "%p\n", av7110); +- +- if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) +- return -1; +- if (av7110->bmp_state == BMP_LOADING) +- dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); +- av7110->bmp_state = BMP_NONE; +- return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); +-} +- +-static u32 RGB2YUV(u16 R, u16 G, u16 B) +-{ +- u16 y, u, v; +- u16 Y, Cr, Cb; +- +- y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ +- u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ +- v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ +- +- Y = y / 256; +- Cb = u / 16; +- Cr = v / 16; +- +- return Cr | (Cb << 16) | (Y << 8); +-} +- +-static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) +-{ +- int ret; +- +- u16 ch, cl; +- u32 yuv; +- +- yuv = blend ? RGB2YUV(r,g,b) : 0; +- cl = (yuv & 0xffff); +- ch = ((yuv >> 16) & 0xffff); +- ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], +- color, ch, cl); +- if (!ret) +- ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], +- color, ((blend >> 4) & 0x0f)); +- return ret; +-} +- +-static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) +-{ +- int i; +- int length = last - first + 1; +- +- if (length * 4 > DATA_BUFF3_SIZE) +- return -EINVAL; +- +- for (i = 0; i < length; i++) { +- u32 color, blend, yuv; +- +- if (get_user(color, colors + i)) +- return -EFAULT; +- blend = (color & 0xF0000000) >> 4; +- yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, +- (color >> 16) & 0xFF) | blend : 0; +- yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); +- wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); +- } +- return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, +- av7110->osdwin, +- bpp2pal[av7110->osdbpp[av7110->osdwin]], +- first, last); +-} +- +-static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, +- int x1, int y1, int inc, u8 __user * data) +-{ +- uint w, h, bpp, bpl, size, lpb, bnum, brest; +- int i; +- int rc,release_rc; +- +- w = x1 - x0 + 1; +- h = y1 - y0 + 1; +- if (inc <= 0) +- inc = w; +- if (w <= 0 || w > 720 || h <= 0 || h > 576) +- return -EINVAL; +- bpp = av7110->osdbpp[av7110->osdwin] + 1; +- bpl = ((w * bpp + 7) & ~7) / 8; +- size = h * bpl; +- lpb = (32 * 1024) / bpl; +- bnum = size / (lpb * bpl); +- brest = size - bnum * lpb * bpl; +- +- if (av7110->bmp_state == BMP_LOADING) { +- /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ +- BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); +- rc = WaitUntilBmpLoaded(av7110); +- if (rc) +- return rc; +- /* just continue. This should work for all fw versions +- * if bnum==1 && !brest && LoadBitmap was successful +- */ +- } +- +- rc = 0; +- for (i = 0; i < bnum; i++) { +- rc = LoadBitmap(av7110, w, lpb, inc, data); +- if (rc) +- break; +- rc = BlitBitmap(av7110, x0, y0 + i * lpb); +- if (rc) +- break; +- data += lpb * inc; +- } +- if (!rc && brest) { +- rc = LoadBitmap(av7110, w, brest / bpl, inc, data); +- if (!rc) +- rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); +- } +- release_rc = ReleaseBitmap(av7110); +- if (!rc) +- rc = release_rc; +- if (rc) +- dprintk(1,"returns %d\n",rc); +- return rc; +-} +- +-int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) +-{ +- int ret; +- +- if (mutex_lock_interruptible(&av7110->osd_mutex)) +- return -ERESTARTSYS; +- +- switch (dc->cmd) { +- case OSD_Close: +- ret = DestroyOSDWindow(av7110, av7110->osdwin); +- break; +- case OSD_Open: +- av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; +- ret = CreateOSDWindow(av7110, av7110->osdwin, +- bpp2bit[av7110->osdbpp[av7110->osdwin]], +- dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); +- if (ret) +- break; +- if (!dc->data) { +- ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); +- if (ret) +- break; +- ret = SetColorBlend(av7110, av7110->osdwin); +- } +- break; +- case OSD_Show: +- ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); +- break; +- case OSD_Hide: +- ret = HideWindow(av7110, av7110->osdwin); +- break; +- case OSD_Clear: +- ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); +- break; +- case OSD_Fill: +- ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); +- break; +- case OSD_SetColor: +- ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); +- break; +- case OSD_SetPalette: +- if (FW_VERSION(av7110->arm_app) >= 0x2618) +- ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); +- else { +- int i, len = dc->x0-dc->color+1; +- u8 __user *colors = (u8 __user *)dc->data; +- u8 r, g = 0, b = 0, blend = 0; +- ret = 0; +- for (i = 0; icolor + i, r, g, b, blend); +- if (ret) +- break; +- } +- } +- break; +- case OSD_SetPixel: +- ret = DrawLine(av7110, av7110->osdwin, +- dc->x0, dc->y0, 0, 0, dc->color); +- break; +- case OSD_SetRow: +- dc->y1 = dc->y0; +- /* fall through */ +- case OSD_SetBlock: +- ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); +- break; +- case OSD_FillRow: +- ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, +- dc->x1-dc->x0+1, dc->y1, dc->color); +- break; +- case OSD_FillBlock: +- ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, +- dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); +- break; +- case OSD_Line: +- ret = DrawLine(av7110, av7110->osdwin, +- dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); +- break; +- case OSD_Text: +- { +- char textbuf[240]; +- +- if (strncpy_from_user(textbuf, dc->data, 240) < 0) { +- ret = -EFAULT; +- break; +- } +- textbuf[239] = 0; +- if (dc->x1 > 3) +- dc->x1 = 3; +- ret = SetFont(av7110, av7110->osdwin, dc->x1, +- (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); +- if (!ret) +- ret = FlushText(av7110); +- if (!ret) +- ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); +- break; +- } +- case OSD_SetWindow: +- if (dc->x0 < 1 || dc->x0 > 7) +- ret = -EINVAL; +- else { +- av7110->osdwin = dc->x0; +- ret = 0; +- } +- break; +- case OSD_MoveWindow: +- ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); +- if (!ret) +- ret = SetColorBlend(av7110, av7110->osdwin); +- break; +- case OSD_OpenRaw: +- if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { +- ret = -EINVAL; +- break; +- } +- if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) +- av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; +- else +- av7110->osdbpp[av7110->osdwin] = 0; +- ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, +- dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); +- if (ret) +- break; +- if (!dc->data) { +- ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); +- if (!ret) +- ret = SetColorBlend(av7110, av7110->osdwin); +- } +- break; +- default: +- ret = -EINVAL; +- break; +- } +- +- mutex_unlock(&av7110->osd_mutex); +- if (ret==-ERESTARTSYS) +- dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); +- else if (ret) +- dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); +- +- return ret; +-} +- +-int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) +-{ +- switch (cap->cmd) { +- case OSD_CAP_MEMSIZE: +- if (FW_4M_SDRAM(av7110->arm_app)) +- cap->val = 1000000; +- else +- cap->val = 92000; +- return 0; +- default: +- return -EINVAL; +- } +-} +-#endif /* CONFIG_DVB_AV7110_OSD */ +diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h +deleted file mode 100644 +index 1634aba..0000000 +--- a/drivers/media/dvb/ttpci/av7110_hw.h ++++ /dev/null +@@ -1,495 +0,0 @@ +-#ifndef _AV7110_HW_H_ +-#define _AV7110_HW_H_ +- +-#include "av7110.h" +- +-/* DEBI transfer mode defs */ +- +-#define DEBINOSWAP 0x000e0000 +-#define DEBISWAB 0x001e0000 +-#define DEBISWAP 0x002e0000 +- +-#define ARM_WAIT_FREE (HZ) +-#define ARM_WAIT_SHAKE (HZ/5) +-#define ARM_WAIT_OSD (HZ) +- +- +-enum av7110_bootstate +-{ +- BOOTSTATE_BUFFER_EMPTY = 0, +- BOOTSTATE_BUFFER_FULL = 1, +- BOOTSTATE_AV7110_BOOT_COMPLETE = 2 +-}; +- +-enum av7110_type_rec_play_format +-{ RP_None, +- AudioPES, +- AudioMp2, +- AudioPCM, +- VideoPES, +- AV_PES +-}; +- +-enum av7110_osd_palette_type +-{ +- NoPalet = 0, /* No palette */ +- Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ +- Pal2Bit = 4, /* 4 colors for 2 bit palette */ +- Pal4Bit = 16, /* 16 colors for 4 bit palette */ +- Pal8Bit = 256 /* 256 colors for 16 bit palette */ +-}; +- +-/* switch defines */ +-#define SB_GPIO 3 +-#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ +-#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ +-#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ +- +-#define FB_GPIO 1 +-#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ +-#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ +-#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ +- +-enum av7110_video_output_mode +-{ +- NO_OUT = 0, /* disable analog output */ +- CVBS_RGB_OUT = 1, +- CVBS_YC_OUT = 2, +- YC_OUT = 3 +-}; +- +-/* firmware internal msg q status: */ +-#define GPMQFull 0x0001 /* Main Message Queue Full */ +-#define GPMQOver 0x0002 /* Main Message Queue Overflow */ +-#define HPQFull 0x0004 /* High Priority Msg Queue Full */ +-#define HPQOver 0x0008 +-#define OSDQFull 0x0010 /* OSD Queue Full */ +-#define OSDQOver 0x0020 +-#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ +-#define HPQBusy 0x0080 +-#define OSDQBusy 0x0100 +- +-/* hw section filter flags */ +-#define SECTION_EIT 0x01 +-#define SECTION_SINGLE 0x00 +-#define SECTION_CYCLE 0x02 +-#define SECTION_CONTINUOS 0x04 +-#define SECTION_MODE 0x06 +-#define SECTION_IPMPE 0x0C /* size up to 4k */ +-#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ +-#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ +- +-#define PBUFSIZE_NONE 0x0000 +-#define PBUFSIZE_1P 0x0100 +-#define PBUFSIZE_2P 0x0200 +-#define PBUFSIZE_1K 0x0300 +-#define PBUFSIZE_2K 0x0400 +-#define PBUFSIZE_4K 0x0500 +-#define PBUFSIZE_8K 0x0600 +-#define PBUFSIZE_16K 0x0700 +-#define PBUFSIZE_32K 0x0800 +- +- +-/* firmware command codes */ +-enum av7110_osd_command { +- WCreate, +- WDestroy, +- WMoveD, +- WMoveA, +- WHide, +- WTop, +- DBox, +- DLine, +- DText, +- Set_Font, +- SetColor, +- SetBlend, +- SetWBlend, +- SetCBlend, +- SetNonBlend, +- LoadBmp, +- BlitBmp, +- ReleaseBmp, +- SetWTrans, +- SetWNoTrans, +- Set_Palette +-}; +- +-enum av7110_pid_command { +- MultiPID, +- VideoPID, +- AudioPID, +- InitFilt, +- FiltError, +- NewVersion, +- CacheError, +- AddPIDFilter, +- DelPIDFilter, +- Scan, +- SetDescr, +- SetIR, +- FlushTSQueue +-}; +- +-enum av7110_mpeg_command { +- SelAudChannels +-}; +- +-enum av7110_audio_command { +- AudioDAC, +- CabADAC, +- ON22K, +- OFF22K, +- MainSwitch, +- ADSwitch, +- SendDiSEqC, +- SetRegister, +- SpdifSwitch +-}; +- +-enum av7110_request_command { +- AudioState, +- AudioBuffState, +- VideoState1, +- VideoState2, +- VideoState3, +- CrashCounter, +- ReqVersion, +- ReqVCXO, +- ReqRegister, +- ReqSecFilterError, +- ReqSTC +-}; +- +-enum av7110_encoder_command { +- SetVidMode, +- SetTestMode, +- LoadVidCode, +- SetMonitorType, +- SetPanScanType, +- SetFreezeMode, +- SetWSSConfig +-}; +- +-enum av7110_rec_play_state { +- __Record, +- __Stop, +- __Play, +- __Pause, +- __Slow, +- __FF_IP, +- __Scan_I, +- __Continue +-}; +- +-enum av7110_fw_cmd_misc { +- AV7110_FW_VIDEO_ZOOM = 1, +- AV7110_FW_VIDEO_COMMAND, +- AV7110_FW_AUDIO_COMMAND +-}; +- +-enum av7110_command_type { +- COMTYPE_NOCOM, +- COMTYPE_PIDFILTER, +- COMTYPE_MPEGDECODER, +- COMTYPE_OSD, +- COMTYPE_BMP, +- COMTYPE_ENCODER, +- COMTYPE_AUDIODAC, +- COMTYPE_REQUEST, +- COMTYPE_SYSTEM, +- COMTYPE_REC_PLAY, +- COMTYPE_COMMON_IF, +- COMTYPE_PID_FILTER, +- COMTYPE_PES, +- COMTYPE_TS, +- COMTYPE_VIDEO, +- COMTYPE_AUDIO, +- COMTYPE_CI_LL, +- COMTYPE_MISC = 0x80 +-}; +- +-#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ +-#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ +-#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ +-#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ +-#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ +- +-/* MPEG video decoder commands */ +-#define AV_VIDEO_CMD_STOP 0x000e +-#define AV_VIDEO_CMD_PLAY 0x000d +-#define AV_VIDEO_CMD_FREEZE 0x0102 +-#define AV_VIDEO_CMD_FFWD 0x0016 +-#define AV_VIDEO_CMD_SLOW 0x0022 +- +-/* MPEG audio decoder commands */ +-#define AUDIO_CMD_MUTE 0x0001 +-#define AUDIO_CMD_UNMUTE 0x0002 +-#define AUDIO_CMD_PCM16 0x0010 +-#define AUDIO_CMD_STEREO 0x0080 +-#define AUDIO_CMD_MONO_L 0x0100 +-#define AUDIO_CMD_MONO_R 0x0200 +-#define AUDIO_CMD_SYNC_OFF 0x000e +-#define AUDIO_CMD_SYNC_ON 0x000f +- +-/* firmware data interface codes */ +-#define DATA_NONE 0x00 +-#define DATA_FSECTION 0x01 +-#define DATA_IPMPE 0x02 +-#define DATA_MPEG_RECORD 0x03 +-#define DATA_DEBUG_MESSAGE 0x04 +-#define DATA_COMMON_INTERFACE 0x05 +-#define DATA_MPEG_PLAY 0x06 +-#define DATA_BMP_LOAD 0x07 +-#define DATA_IRCOMMAND 0x08 +-#define DATA_PIPING 0x09 +-#define DATA_STREAMING 0x0a +-#define DATA_CI_GET 0x0b +-#define DATA_CI_PUT 0x0c +-#define DATA_MPEG_VIDEO_EVENT 0x0d +- +-#define DATA_PES_RECORD 0x10 +-#define DATA_PES_PLAY 0x11 +-#define DATA_TS_RECORD 0x12 +-#define DATA_TS_PLAY 0x13 +- +-/* ancient CI command codes, only two are actually still used +- * by the link level CI firmware */ +-#define CI_CMD_ERROR 0x00 +-#define CI_CMD_ACK 0x01 +-#define CI_CMD_SYSTEM_READY 0x02 +-#define CI_CMD_KEYPRESS 0x03 +-#define CI_CMD_ON_TUNED 0x04 +-#define CI_CMD_ON_SWITCH_PROGRAM 0x05 +-#define CI_CMD_SECTION_ARRIVED 0x06 +-#define CI_CMD_SECTION_TIMEOUT 0x07 +-#define CI_CMD_TIME 0x08 +-#define CI_CMD_ENTER_MENU 0x09 +-#define CI_CMD_FAST_PSI 0x0a +-#define CI_CMD_GET_SLOT_INFO 0x0b +- +-#define CI_MSG_NONE 0x00 +-#define CI_MSG_CI_INFO 0x01 +-#define CI_MSG_MENU 0x02 +-#define CI_MSG_LIST 0x03 +-#define CI_MSG_TEXT 0x04 +-#define CI_MSG_REQUEST_INPUT 0x05 +-#define CI_MSG_INPUT_COMPLETE 0x06 +-#define CI_MSG_LIST_MORE 0x07 +-#define CI_MSG_MENU_MORE 0x08 +-#define CI_MSG_CLOSE_MMI_IMM 0x09 +-#define CI_MSG_SECTION_REQUEST 0x0a +-#define CI_MSG_CLOSE_FILTER 0x0b +-#define CI_PSI_COMPLETE 0x0c +-#define CI_MODULE_READY 0x0d +-#define CI_SWITCH_PRG_REPLY 0x0e +-#define CI_MSG_TEXT_MORE 0x0f +- +-#define CI_MSG_CA_PMT 0xe0 +-#define CI_MSG_ERROR 0xf0 +- +- +-/* base address of the dual ported RAM which serves as communication +- * area between PCI bus and av7110, +- * as seen by the DEBI bus of the saa7146 */ +-#define DPRAM_BASE 0x4000 +- +-/* boot protocol area */ +-#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) +-#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) +-#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) +-#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) +-#define AV7110_BOOT_MAX_SIZE 0xc00 +- +-/* firmware command protocol area */ +-#define IRQ_STATE (DPRAM_BASE + 0x0F4) +-#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) +-#define MSGSTATE (DPRAM_BASE + 0x0F8) +-#define COMMAND (DPRAM_BASE + 0x0FC) +-#define COM_BUFF (DPRAM_BASE + 0x100) +-#define COM_BUFF_SIZE 0x20 +- +-/* various data buffers */ +-#define BUFF1_BASE (DPRAM_BASE + 0x120) +-#define BUFF1_SIZE 0xE0 +- +-#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) +-#define DATA_BUFF0_SIZE 0x0800 +- +-#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) +-#define DATA_BUFF1_SIZE 0x0800 +- +-#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) +-#define DATA_BUFF2_SIZE 0x0800 +- +-#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) +-#define DATA_BUFF3_SIZE 0x0400 +- +-#define Reserved (DPRAM_BASE + 0x1E00) +-#define Reserved_SIZE 0x1C0 +- +- +-/* firmware status area */ +-#define STATUS_BASE (DPRAM_BASE + 0x1FC0) +-#define STATUS_LOOPS (STATUS_BASE + 0x08) +- +-#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) +-/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ +-#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) +- +-/* firmware data protocol area */ +-#define RX_TYPE (DPRAM_BASE + 0x1FE8) +-#define RX_LEN (DPRAM_BASE + 0x1FEA) +-#define TX_TYPE (DPRAM_BASE + 0x1FEC) +-#define TX_LEN (DPRAM_BASE + 0x1FEE) +- +-#define RX_BUFF (DPRAM_BASE + 0x1FF4) +-#define TX_BUFF (DPRAM_BASE + 0x1FF6) +- +-#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) +-#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) +- +-#define IRQ_RX (DPRAM_BASE + 0x1FFC) +-#define IRQ_TX (DPRAM_BASE + 0x1FFE) +- +-/* used by boot protocol to load firmware into av7110 DRAM */ +-#define DRAM_START_CODE 0x2e000404 +-#define DRAM_MAX_CODE_SIZE 0x00100000 +- +-/* saa7146 gpio lines */ +-#define RESET_LINE 2 +-#define DEBI_DONE_LINE 1 +-#define ARM_IRQ_LINE 0 +- +- +- +-extern int av7110_bootarm(struct av7110 *av7110); +-extern int av7110_firmversion(struct av7110 *av7110); +-#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) +-#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) +-#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) +- +-extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); +-extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); +-extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, +- int request_buf_len, u16 *reply_buf, int reply_buf_len); +- +- +-/* DEBI (saa7146 data extension bus interface) access */ +-extern int av7110_debiwrite(struct av7110 *av7110, u32 config, +- int addr, u32 val, int count); +-extern u32 av7110_debiread(struct av7110 *av7110, u32 config, +- int addr, int count); +- +- +-/* DEBI during interrupt */ +-/* single word writes */ +-static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +-{ +- av7110_debiwrite(av7110, config, addr, val, count); +-} +- +-/* buffer writes */ +-static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, +- const u8 *val, int count) +-{ +- memcpy(av7110->debi_virt, val, count); +- av7110_debiwrite(av7110, config, addr, 0, count); +-} +- +-static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +-{ +- u32 res; +- +- res=av7110_debiread(av7110, config, addr, count); +- if (count<=4) +- memcpy(av7110->debi_virt, (char *) &res, count); +- return res; +-} +- +-/* DEBI outside interrupts, only for count <= 4! */ +-static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&av7110->debilock, flags); +- av7110_debiwrite(av7110, config, addr, val, count); +- spin_unlock_irqrestore(&av7110->debilock, flags); +-} +- +-static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) +-{ +- unsigned long flags; +- u32 res; +- +- spin_lock_irqsave(&av7110->debilock, flags); +- res=av7110_debiread(av7110, config, addr, count); +- spin_unlock_irqrestore(&av7110->debilock, flags); +- return res; +-} +- +-/* handle mailbox registers of the dual ported RAM */ +-static inline void ARM_ResetMailBox(struct av7110 *av7110) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&av7110->debilock, flags); +- av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); +- av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +- spin_unlock_irqrestore(&av7110->debilock, flags); +-} +- +-static inline void ARM_ClearMailBox(struct av7110 *av7110) +-{ +- iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +-} +- +-static inline void ARM_ClearIrq(struct av7110 *av7110) +-{ +- irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); +-} +- +-/**************************************************************************** +- * Firmware commands +- ****************************************************************************/ +- +-static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); +-} +- +-static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); +-} +- +-static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, +- (com>>16), (com&0xffff), +- (arg>>16), (arg&0xffff)); +-} +- +-static inline int audcom(struct av7110 *av7110, u32 com) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, +- (com>>16), (com&0xffff)); +-} +- +-static inline int Set22K(struct av7110 *av7110, int state) +-{ +- return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); +-} +- +- +-extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); +- +- +-#ifdef CONFIG_DVB_AV7110_OSD +-extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); +-extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); +-#endif /* CONFIG_DVB_AV7110_OSD */ +- +- +- +-#endif /* _AV7110_HW_H_ */ +diff --git a/drivers/media/dvb/ttpci/av7110_ipack.c b/drivers/media/dvb/ttpci/av7110_ipack.c +deleted file mode 100644 +index 699ef8b..0000000 +--- a/drivers/media/dvb/ttpci/av7110_ipack.c ++++ /dev/null +@@ -1,403 +0,0 @@ +-#include "dvb_filter.h" +-#include "av7110_ipack.h" +-#include /* for memcpy() */ +-#include +- +- +-void av7110_ipack_reset(struct ipack *p) +-{ +- p->found = 0; +- p->cid = 0; +- p->plength = 0; +- p->flag1 = 0; +- p->flag2 = 0; +- p->hlength = 0; +- p->mpeg = 0; +- p->check = 0; +- p->which = 0; +- p->done = 0; +- p->count = 0; +-} +- +- +-int av7110_ipack_init(struct ipack *p, int size, +- void (*func)(u8 *buf, int size, void *priv)) +-{ +- if (!(p->buf = vmalloc(size*sizeof(u8)))) { +- printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); +- return -ENOMEM; +- } +- p->size = size; +- p->func = func; +- p->repack_subids = 0; +- av7110_ipack_reset(p); +- return 0; +-} +- +- +-void av7110_ipack_free(struct ipack *p) +-{ +- vfree(p->buf); +-} +- +- +-static void send_ipack(struct ipack *p) +-{ +- int off; +- struct dvb_audio_info ai; +- int ac3_off = 0; +- int streamid = 0; +- int nframes = 0; +- int f = 0; +- +- switch (p->mpeg) { +- case 2: +- if (p->count < 10) +- return; +- p->buf[3] = p->cid; +- p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); +- p->buf[5] = (u8)((p->count - 6) & 0x00ff); +- if (p->repack_subids && p->cid == PRIVATE_STREAM1) { +- off = 9 + p->buf[8]; +- streamid = p->buf[off]; +- if ((streamid & 0xf8) == 0x80) { +- ai.off = 0; +- ac3_off = ((p->buf[off + 2] << 8)| +- p->buf[off + 3]); +- if (ac3_off < p->count) +- f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, +- p->count - ac3_off, &ai, 0); +- if (!f) { +- nframes = (p->count - off - 3 - ac3_off) / +- ai.framesize + 1; +- p->buf[off + 2] = (ac3_off >> 8) & 0xff; +- p->buf[off + 3] = (ac3_off) & 0xff; +- p->buf[off + 1] = nframes; +- ac3_off += nframes * ai.framesize - p->count; +- } +- } +- } +- p->func(p->buf, p->count, p->data); +- +- p->buf[6] = 0x80; +- p->buf[7] = 0x00; +- p->buf[8] = 0x00; +- p->count = 9; +- if (p->repack_subids && p->cid == PRIVATE_STREAM1 +- && (streamid & 0xf8) == 0x80) { +- p->count += 4; +- p->buf[9] = streamid; +- p->buf[10] = (ac3_off >> 8) & 0xff; +- p->buf[11] = (ac3_off) & 0xff; +- p->buf[12] = 0; +- } +- break; +- +- case 1: +- if (p->count < 8) +- return; +- p->buf[3] = p->cid; +- p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); +- p->buf[5] = (u8)((p->count - 6) & 0x00ff); +- p->func(p->buf, p->count, p->data); +- +- p->buf[6] = 0x0f; +- p->count = 7; +- break; +- } +-} +- +- +-void av7110_ipack_flush(struct ipack *p) +-{ +- if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) +- return; +- p->plength = p->found - 6; +- p->found = 0; +- send_ipack(p); +- av7110_ipack_reset(p); +-} +- +- +-static void write_ipack(struct ipack *p, const u8 *data, int count) +-{ +- u8 headr[3] = { 0x00, 0x00, 0x01 }; +- +- if (p->count < 6) { +- memcpy(p->buf, headr, 3); +- p->count = 6; +- } +- +- if (p->count + count < p->size){ +- memcpy(p->buf+p->count, data, count); +- p->count += count; +- } else { +- int rest = p->size - p->count; +- memcpy(p->buf+p->count, data, rest); +- p->count += rest; +- send_ipack(p); +- if (count - rest > 0) +- write_ipack(p, data + rest, count - rest); +- } +-} +- +- +-int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) +-{ +- int l; +- int c = 0; +- +- while (c < count && (p->mpeg == 0 || +- (p->mpeg == 1 && p->found < 7) || +- (p->mpeg == 2 && p->found < 9)) +- && (p->found < 5 || !p->done)) { +- switch (p->found) { +- case 0: +- case 1: +- if (buf[c] == 0x00) +- p->found++; +- else +- p->found = 0; +- c++; +- break; +- case 2: +- if (buf[c] == 0x01) +- p->found++; +- else if (buf[c] == 0) +- p->found = 2; +- else +- p->found = 0; +- c++; +- break; +- case 3: +- p->cid = 0; +- switch (buf[c]) { +- case PROG_STREAM_MAP: +- case PRIVATE_STREAM2: +- case PROG_STREAM_DIR: +- case ECM_STREAM : +- case EMM_STREAM : +- case PADDING_STREAM : +- case DSM_CC_STREAM : +- case ISO13522_STREAM: +- p->done = 1; +- /* fall through */ +- case PRIVATE_STREAM1: +- case VIDEO_STREAM_S ... VIDEO_STREAM_E: +- case AUDIO_STREAM_S ... AUDIO_STREAM_E: +- p->found++; +- p->cid = buf[c]; +- c++; +- break; +- default: +- p->found = 0; +- break; +- } +- break; +- +- case 4: +- if (count-c > 1) { +- p->plen[0] = buf[c]; +- c++; +- p->plen[1] = buf[c]; +- c++; +- p->found += 2; +- p->plength = (p->plen[0] << 8) | p->plen[1]; +- } else { +- p->plen[0] = buf[c]; +- p->found++; +- return count; +- } +- break; +- case 5: +- p->plen[1] = buf[c]; +- c++; +- p->found++; +- p->plength = (p->plen[0] << 8) | p->plen[1]; +- break; +- case 6: +- if (!p->done) { +- p->flag1 = buf[c]; +- c++; +- p->found++; +- if ((p->flag1 & 0xc0) == 0x80) +- p->mpeg = 2; +- else { +- p->hlength = 0; +- p->which = 0; +- p->mpeg = 1; +- p->flag2 = 0; +- } +- } +- break; +- +- case 7: +- if (!p->done && p->mpeg == 2) { +- p->flag2 = buf[c]; +- c++; +- p->found++; +- } +- break; +- +- case 8: +- if (!p->done && p->mpeg == 2) { +- p->hlength = buf[c]; +- c++; +- p->found++; +- } +- break; +- } +- } +- +- if (c == count) +- return count; +- +- if (!p->plength) +- p->plength = MMAX_PLENGTH - 6; +- +- if (p->done || ((p->mpeg == 2 && p->found >= 9) || +- (p->mpeg == 1 && p->found >= 7))) { +- switch (p->cid) { +- case AUDIO_STREAM_S ... AUDIO_STREAM_E: +- case VIDEO_STREAM_S ... VIDEO_STREAM_E: +- case PRIVATE_STREAM1: +- if (p->mpeg == 2 && p->found == 9) { +- write_ipack(p, &p->flag1, 1); +- write_ipack(p, &p->flag2, 1); +- write_ipack(p, &p->hlength, 1); +- } +- +- if (p->mpeg == 1 && p->found == 7) +- write_ipack(p, &p->flag1, 1); +- +- if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && +- p->found < 14) { +- while (c < count && p->found < 14) { +- p->pts[p->found - 9] = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- } +- if (c == count) +- return count; +- } +- +- if (p->mpeg == 1 && p->which < 2000) { +- +- if (p->found == 7) { +- p->check = p->flag1; +- p->hlength = 1; +- } +- +- while (!p->which && c < count && +- p->check == 0xff){ +- p->check = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->hlength++; +- } +- +- if (c == count) +- return count; +- +- if ((p->check & 0xc0) == 0x40 && !p->which) { +- p->check = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->hlength++; +- +- p->which = 1; +- if (c == count) +- return count; +- p->check = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->hlength++; +- p->which = 2; +- if (c == count) +- return count; +- } +- +- if (p->which == 1) { +- p->check = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->hlength++; +- p->which = 2; +- if (c == count) +- return count; +- } +- +- if ((p->check & 0x30) && p->check != 0xff) { +- p->flag2 = (p->check & 0xf0) << 2; +- p->pts[0] = p->check; +- p->which = 3; +- } +- +- if (c == count) +- return count; +- if (p->which > 2){ +- if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { +- while (c < count && p->which < 7) { +- p->pts[p->which - 2] = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->which++; +- p->hlength++; +- } +- if (c == count) +- return count; +- } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { +- while (c < count && p->which < 12) { +- if (p->which < 7) +- p->pts[p->which - 2] = buf[c]; +- write_ipack(p, buf + c, 1); +- c++; +- p->found++; +- p->which++; +- p->hlength++; +- } +- if (c == count) +- return count; +- } +- p->which = 2000; +- } +- +- } +- +- while (c < count && p->found < p->plength + 6) { +- l = count - c; +- if (l + p->found > p->plength + 6) +- l = p->plength + 6 - p->found; +- write_ipack(p, buf + c, l); +- p->found += l; +- c += l; +- } +- break; +- } +- +- +- if (p->done) { +- if (p->found + count - c < p->plength + 6) { +- p->found += count - c; +- c = count; +- } else { +- c += p->plength + 6 - p->found; +- p->found = p->plength + 6; +- } +- } +- +- if (p->plength && p->found == p->plength + 6) { +- send_ipack(p); +- av7110_ipack_reset(p); +- if (c < count) +- av7110_ipack_instant_repack(buf + c, count - c, p); +- } +- } +- return count; +-} +diff --git a/drivers/media/dvb/ttpci/av7110_ipack.h b/drivers/media/dvb/ttpci/av7110_ipack.h +deleted file mode 100644 +index becf94d..0000000 +--- a/drivers/media/dvb/ttpci/av7110_ipack.h ++++ /dev/null +@@ -1,12 +0,0 @@ +-#ifndef _AV7110_IPACK_H_ +-#define _AV7110_IPACK_H_ +- +-extern int av7110_ipack_init(struct ipack *p, int size, +- void (*func)(u8 *buf, int size, void *priv)); +- +-extern void av7110_ipack_reset(struct ipack *p); +-extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); +-extern void av7110_ipack_free(struct ipack * p); +-extern void av7110_ipack_flush(struct ipack *p); +- +-#endif +diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c +deleted file mode 100644 +index 908f272..0000000 +--- a/drivers/media/dvb/ttpci/av7110_ir.c ++++ /dev/null +@@ -1,415 +0,0 @@ +-/* +- * Driver for the remote control of SAA7146 based AV7110 cards +- * +- * Copyright (C) 1999-2003 Holger Waechtler +- * Copyright (C) 2003-2007 Oliver Endriss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- */ +- +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "av7110.h" +-#include "av7110_hw.h" +- +- +-#define AV_CNT 4 +- +-#define IR_RC5 0 +-#define IR_RCMM 1 +-#define IR_RC5_EXT 2 /* internal only */ +- +-#define IR_ALL 0xffffffff +- +-#define UP_TIMEOUT (HZ*7/25) +- +- +-/* Note: enable ir debugging by or'ing debug with 16 */ +- +-static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; +-module_param_array(ir_protocol, int, NULL, 0644); +-MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); +- +-static int ir_inversion[AV_CNT]; +-module_param_array(ir_inversion, int, NULL, 0644); +-MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); +- +-static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; +-module_param_array(ir_device_mask, uint, NULL, 0644); +-MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); +- +- +-static int av_cnt; +-static struct av7110 *av_list[AV_CNT]; +- +-static u16 default_key_map [256] = { +- KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, +- KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, +- KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, +- 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, +- KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, +- KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, +- KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, +- 0, 0, 0, 0, KEY_EPG, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR +-}; +- +- +-/* key-up timer */ +-static void av7110_emit_keyup(unsigned long parm) +-{ +- struct infrared *ir = (struct infrared *) parm; +- +- if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) +- return; +- +- input_report_key(ir->input_dev, ir->last_key, 0); +- input_sync(ir->input_dev); +-} +- +- +-/* tasklet */ +-static void av7110_emit_key(unsigned long parm) +-{ +- struct infrared *ir = (struct infrared *) parm; +- u32 ircom = ir->ir_command; +- u8 data; +- u8 addr; +- u16 toggle; +- u16 keycode; +- +- /* extract device address and data */ +- switch (ir->protocol) { +- case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ +- data = ircom & 0x3f; +- addr = (ircom >> 6) & 0x1f; +- toggle = ircom & 0x0800; +- break; +- +- case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ +- data = ircom & 0xff; +- addr = (ircom >> 8) & 0x1f; +- toggle = ircom & 0x8000; +- break; +- +- case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ +- data = ircom & 0x3f; +- addr = (ircom >> 6) & 0x1f; +- /* invert 7th data bit for backward compatibility with RC5 keymaps */ +- if (!(ircom & 0x1000)) +- data |= 0x40; +- toggle = ircom & 0x0800; +- break; +- +- default: +- printk("%s invalid protocol %x\n", __func__, ir->protocol); +- return; +- } +- +- input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); +- input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); +- +- keycode = ir->key_map[data]; +- +- dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", +- __func__, ircom, addr, data, keycode); +- +- /* check device address */ +- if (!(ir->device_mask & (1 << addr))) +- return; +- +- if (!keycode) { +- printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", +- __func__, ircom, addr, data); +- return; +- } +- +- if (timer_pending(&ir->keyup_timer)) { +- del_timer(&ir->keyup_timer); +- if (ir->last_key != keycode || toggle != ir->last_toggle) { +- ir->delay_timer_finished = 0; +- input_event(ir->input_dev, EV_KEY, ir->last_key, 0); +- input_event(ir->input_dev, EV_KEY, keycode, 1); +- input_sync(ir->input_dev); +- } else if (ir->delay_timer_finished) { +- input_event(ir->input_dev, EV_KEY, keycode, 2); +- input_sync(ir->input_dev); +- } +- } else { +- ir->delay_timer_finished = 0; +- input_event(ir->input_dev, EV_KEY, keycode, 1); +- input_sync(ir->input_dev); +- } +- +- ir->last_key = keycode; +- ir->last_toggle = toggle; +- +- ir->keyup_timer.expires = jiffies + UP_TIMEOUT; +- add_timer(&ir->keyup_timer); +- +-} +- +- +-/* register with input layer */ +-static void input_register_keys(struct infrared *ir) +-{ +- int i; +- +- set_bit(EV_KEY, ir->input_dev->evbit); +- set_bit(EV_REP, ir->input_dev->evbit); +- set_bit(EV_MSC, ir->input_dev->evbit); +- +- set_bit(MSC_RAW, ir->input_dev->mscbit); +- set_bit(MSC_SCAN, ir->input_dev->mscbit); +- +- memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); +- +- for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { +- if (ir->key_map[i] > KEY_MAX) +- ir->key_map[i] = 0; +- else if (ir->key_map[i] > KEY_RESERVED) +- set_bit(ir->key_map[i], ir->input_dev->keybit); +- } +- +- ir->input_dev->keycode = ir->key_map; +- ir->input_dev->keycodesize = sizeof(ir->key_map[0]); +- ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); +-} +- +- +-/* called by the input driver after rep[REP_DELAY] ms */ +-static void input_repeat_key(unsigned long parm) +-{ +- struct infrared *ir = (struct infrared *) parm; +- +- ir->delay_timer_finished = 1; +-} +- +- +-/* check for configuration changes */ +-int av7110_check_ir_config(struct av7110 *av7110, int force) +-{ +- int i; +- int modified = force; +- int ret = -ENODEV; +- +- for (i = 0; i < av_cnt; i++) +- if (av7110 == av_list[i]) +- break; +- +- if (i < av_cnt && av7110) { +- if ((av7110->ir.protocol & 1) != ir_protocol[i] || +- av7110->ir.inversion != ir_inversion[i]) +- modified = true; +- +- if (modified) { +- /* protocol */ +- if (ir_protocol[i]) { +- ir_protocol[i] = 1; +- av7110->ir.protocol = IR_RCMM; +- av7110->ir.ir_config = 0x0001; +- } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { +- av7110->ir.protocol = IR_RC5_EXT; +- av7110->ir.ir_config = 0x0002; +- } else { +- av7110->ir.protocol = IR_RC5; +- av7110->ir.ir_config = 0x0000; +- } +- /* inversion */ +- if (ir_inversion[i]) { +- ir_inversion[i] = 1; +- av7110->ir.ir_config |= 0x8000; +- } +- av7110->ir.inversion = ir_inversion[i]; +- /* update ARM */ +- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, +- av7110->ir.ir_config); +- } else +- ret = 0; +- +- /* address */ +- if (av7110->ir.device_mask != ir_device_mask[i]) +- av7110->ir.device_mask = ir_device_mask[i]; +- } +- +- return ret; +-} +- +- +-/* /proc/av7110_ir interface */ +-static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, +- size_t count, loff_t *pos) +-{ +- char *page; +- u32 ir_config; +- int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; +- int i; +- +- if (count < size) +- return -EINVAL; +- +- page = vmalloc(size); +- if (!page) +- return -ENOMEM; +- +- if (copy_from_user(page, buffer, size)) { +- vfree(page); +- return -EFAULT; +- } +- +- memcpy(&ir_config, page, sizeof ir_config); +- +- for (i = 0; i < av_cnt; i++) { +- /* keymap */ +- memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, +- sizeof(av_list[i]->ir.key_map)); +- /* protocol, inversion, address */ +- ir_protocol[i] = ir_config & 0x0001; +- ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; +- if (ir_config & 0x4000) +- ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); +- else +- ir_device_mask[i] = IR_ALL; +- /* update configuration */ +- av7110_check_ir_config(av_list[i], false); +- input_register_keys(&av_list[i]->ir); +- } +- vfree(page); +- return count; +-} +- +-static const struct file_operations av7110_ir_proc_fops = { +- .owner = THIS_MODULE, +- .write = av7110_ir_proc_write, +- .llseek = noop_llseek, +-}; +- +-/* interrupt handler */ +-static void ir_handler(struct av7110 *av7110, u32 ircom) +-{ +- dprintk(4, "ir command = %08x\n", ircom); +- av7110->ir.ir_command = ircom; +- tasklet_schedule(&av7110->ir.ir_tasklet); +-} +- +- +-int __devinit av7110_ir_init(struct av7110 *av7110) +-{ +- struct input_dev *input_dev; +- static struct proc_dir_entry *e; +- int err; +- +- if (av_cnt >= ARRAY_SIZE(av_list)) +- return -ENOSPC; +- +- av_list[av_cnt++] = av7110; +- av7110_check_ir_config(av7110, true); +- +- init_timer(&av7110->ir.keyup_timer); +- av7110->ir.keyup_timer.function = av7110_emit_keyup; +- av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; +- +- input_dev = input_allocate_device(); +- if (!input_dev) +- return -ENOMEM; +- +- av7110->ir.input_dev = input_dev; +- snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), +- "pci-%s/ir0", pci_name(av7110->dev->pci)); +- +- input_dev->name = "DVB on-card IR receiver"; +- +- input_dev->phys = av7110->ir.input_phys; +- input_dev->id.bustype = BUS_PCI; +- input_dev->id.version = 2; +- if (av7110->dev->pci->subsystem_vendor) { +- input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; +- input_dev->id.product = av7110->dev->pci->subsystem_device; +- } else { +- input_dev->id.vendor = av7110->dev->pci->vendor; +- input_dev->id.product = av7110->dev->pci->device; +- } +- input_dev->dev.parent = &av7110->dev->pci->dev; +- /* initial keymap */ +- memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); +- input_register_keys(&av7110->ir); +- err = input_register_device(input_dev); +- if (err) { +- input_free_device(input_dev); +- return err; +- } +- input_dev->timer.function = input_repeat_key; +- input_dev->timer.data = (unsigned long) &av7110->ir; +- +- if (av_cnt == 1) { +- e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); +- if (e) +- e->size = 4 + 256 * sizeof(u16); +- } +- +- tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); +- av7110->ir.ir_handler = ir_handler; +- +- return 0; +-} +- +- +-void __devexit av7110_ir_exit(struct av7110 *av7110) +-{ +- int i; +- +- if (av_cnt == 0) +- return; +- +- del_timer_sync(&av7110->ir.keyup_timer); +- av7110->ir.ir_handler = NULL; +- tasklet_kill(&av7110->ir.ir_tasklet); +- +- for (i = 0; i < av_cnt; i++) +- if (av_list[i] == av7110) { +- av_list[i] = av_list[av_cnt-1]; +- av_list[av_cnt-1] = NULL; +- break; +- } +- +- if (av_cnt == 1) +- remove_proc_entry("av7110_ir", NULL); +- +- input_unregister_device(av7110->ir.input_dev); +- +- av_cnt--; +-} +- +-//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); +-//MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c +deleted file mode 100644 +index ee8ee1d..0000000 +--- a/drivers/media/dvb/ttpci/av7110_v4l.c ++++ /dev/null +@@ -1,934 +0,0 @@ +-/* +- * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * originally based on code by: +- * Copyright (C) 1998,1999 Christian Theiss +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "av7110.h" +-#include "av7110_hw.h" +-#include "av7110_av.h" +- +-int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) +-{ +- u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; +- struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; +- +- switch (av7110->adac_type) { +- case DVB_ADAC_MSP34x0: +- msgs.addr = 0x40; +- break; +- case DVB_ADAC_MSP34x5: +- msgs.addr = 0x42; +- break; +- default: +- return 0; +- } +- +- if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { +- dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", +- av7110->dvb_adapter.num, reg, val); +- return -EIO; +- } +- return 0; +-} +- +-static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) +-{ +- u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; +- u8 msg2[2]; +- struct i2c_msg msgs[2] = { +- { .flags = 0 , .len = 3, .buf = msg1 }, +- { .flags = I2C_M_RD, .len = 2, .buf = msg2 } +- }; +- +- switch (av7110->adac_type) { +- case DVB_ADAC_MSP34x0: +- msgs[0].addr = 0x40; +- msgs[1].addr = 0x40; +- break; +- case DVB_ADAC_MSP34x5: +- msgs[0].addr = 0x42; +- msgs[1].addr = 0x42; +- break; +- default: +- return 0; +- } +- +- if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { +- dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", +- av7110->dvb_adapter.num, reg); +- return -EIO; +- } +- *val = (msg2[0] << 8) | msg2[1]; +- return 0; +-} +- +-static struct v4l2_input inputs[4] = { +- { +- .index = 0, +- .name = "DVB", +- .type = V4L2_INPUT_TYPE_CAMERA, +- .audioset = 1, +- .tuner = 0, /* ignored */ +- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, +- .status = 0, +- .capabilities = V4L2_IN_CAP_STD, +- }, { +- .index = 1, +- .name = "Television", +- .type = V4L2_INPUT_TYPE_TUNER, +- .audioset = 2, +- .tuner = 0, +- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, +- .status = 0, +- .capabilities = V4L2_IN_CAP_STD, +- }, { +- .index = 2, +- .name = "Video", +- .type = V4L2_INPUT_TYPE_CAMERA, +- .audioset = 0, +- .tuner = 0, +- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, +- .status = 0, +- .capabilities = V4L2_IN_CAP_STD, +- }, { +- .index = 3, +- .name = "Y/C", +- .type = V4L2_INPUT_TYPE_CAMERA, +- .audioset = 0, +- .tuner = 0, +- .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, +- .status = 0, +- .capabilities = V4L2_IN_CAP_STD, +- } +-}; +- +-static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) +-{ +- struct av7110 *av7110 = dev->ext_priv; +- u8 buf[] = { 0x00, reg, data }; +- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; +- +- dprintk(4, "dev: %p\n", dev); +- +- if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) +- return -1; +- return 0; +-} +- +-static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) +-{ +- struct av7110 *av7110 = dev->ext_priv; +- struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; +- +- dprintk(4, "dev: %p\n", dev); +- +- if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) +- return -1; +- return 0; +-} +- +-static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) +-{ +- u32 div; +- u8 config; +- u8 buf[4]; +- +- dprintk(4, "freq: 0x%08x\n", freq); +- +- /* magic number: 614. tuning with the frequency given by v4l2 +- is always off by 614*62.5 = 38375 kHz...*/ +- div = freq + 614; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x8e; +- +- if (freq < (u32) (16 * 168.25)) +- config = 0xa0; +- else if (freq < (u32) (16 * 447.25)) +- config = 0x90; +- else +- config = 0x30; +- config &= ~0x02; +- +- buf[3] = config; +- +- return tuner_write(dev, 0x61, buf); +-} +- +-static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) +-{ +- struct av7110 *av7110 = (struct av7110*)dev->ext_priv; +- u32 div; +- u8 data[4]; +- +- div = (freq + 38900000 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0xce; +- +- if (freq < 45000000) +- return -EINVAL; +- else if (freq < 137000000) +- data[3] = 0x01; +- else if (freq < 403000000) +- data[3] = 0x02; +- else if (freq < 860000000) +- data[3] = 0x04; +- else +- return -EINVAL; +- +- if (av7110->fe->ops.i2c_gate_ctrl) +- av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); +- return tuner_write(dev, 0x63, data); +-} +- +- +- +-static struct saa7146_standard analog_standard[]; +-static struct saa7146_standard dvb_standard[]; +-static struct saa7146_standard standard[]; +- +-static struct v4l2_audio msp3400_v4l2_audio = { +- .index = 0, +- .name = "Television", +- .capability = V4L2_AUDCAP_STEREO +-}; +- +-static int av7110_dvb_c_switch(struct saa7146_fh *fh) +-{ +- struct saa7146_dev *dev = fh->dev; +- struct saa7146_vv *vv = dev->vv_data; +- struct av7110 *av7110 = (struct av7110*)dev->ext_priv; +- u16 adswitch; +- int source, sync, err; +- +- dprintk(4, "%p\n", av7110); +- +- 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 (err != 0) { +- dprintk(2, "suspending video failed\n"); +- vv->ov_suspend = NULL; +- } +- } +- +- if (0 != av7110->current_input) { +- dprintk(1, "switching to analog TV:\n"); +- adswitch = 1; +- source = SAA7146_HPS_SOURCE_PORT_B; +- sync = SAA7146_HPS_SYNC_PORT_B; +- memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); +- +- switch (av7110->current_input) { +- case 1: +- dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source +- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source +- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source +- msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone +- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume +- +- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { +- if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) +- dprintk(1, "setting band in demodulator failed\n"); +- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) +- } +- if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) +- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); +- break; +- case 2: +- dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); +- if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) +- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); +- break; +- case 3: +- dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); +- if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) +- dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); +- break; +- default: +- dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); +- } +- } else { +- adswitch = 0; +- source = SAA7146_HPS_SOURCE_PORT_A; +- sync = SAA7146_HPS_SYNC_PORT_A; +- memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); +- dprintk(1, "switching DVB mode\n"); +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source +- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source +- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source +- msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone +- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume +- +- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { +- if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) +- dprintk(1, "setting band in demodulator failed\n"); +- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) +- } +- } +- +- /* hmm, this does not do anything!? */ +- if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) +- dprintk(1, "ADSwitch error\n"); +- +- saa7146_set_hps_source_and_sync(dev, source, sync); +- +- if (vv->ov_suspend != NULL) { +- saa7146_start_preview(vv->ov_suspend); +- vv->ov_suspend = NULL; +- } +- +- return 0; +-} +- +-static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- u16 stereo_det; +- s8 stereo; +- +- dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); +- +- if (!av7110->analog_tuner_flags || t->index != 0) +- return -EINVAL; +- +- memset(t, 0, sizeof(*t)); +- strcpy((char *)t->name, "Television"); +- +- t->type = V4L2_TUNER_ANALOG_TV; +- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | +- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; +- t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ +- t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ +- /* FIXME: add the real signal strength here */ +- t->signal = 0xffff; +- t->afc = 0; +- +- /* FIXME: standard / stereo detection is still broken */ +- msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); +- dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); +- msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); +- dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); +- stereo = (s8)(stereo_det >> 8); +- if (stereo > 0x10) { +- /* stereo */ +- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; +- t->audmode = V4L2_TUNER_MODE_STEREO; +- } else if (stereo < -0x10) { +- /* bilingual */ +- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; +- t->audmode = V4L2_TUNER_MODE_LANG1; +- } else /* mono */ +- t->rxsubchans = V4L2_TUNER_SUB_MONO; +- +- return 0; +-} +- +-static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- u16 fm_matrix, src; +- dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); +- +- if (!av7110->analog_tuner_flags || av7110->current_input != 1) +- return -EINVAL; +- +- switch (t->audmode) { +- case V4L2_TUNER_MODE_STEREO: +- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); +- fm_matrix = 0x3001; /* stereo */ +- src = 0x0020; +- break; +- case V4L2_TUNER_MODE_LANG1_LANG2: +- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); +- fm_matrix = 0x3000; /* bilingual */ +- src = 0x0020; +- break; +- case V4L2_TUNER_MODE_LANG1: +- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); +- fm_matrix = 0x3000; /* mono */ +- src = 0x0000; +- break; +- case V4L2_TUNER_MODE_LANG2: +- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); +- fm_matrix = 0x3000; /* mono */ +- src = 0x0010; +- break; +- default: /* case V4L2_TUNER_MODE_MONO: */ +- dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); +- fm_matrix = 0x3000; /* mono */ +- src = 0x0030; +- break; +- } +- msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); +- msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); +- msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); +- return 0; +-} +- +-static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); +- +- if (!av7110->analog_tuner_flags || av7110->current_input != 1) +- return -EINVAL; +- +- memset(f, 0, sizeof(*f)); +- f->type = V4L2_TUNER_ANALOG_TV; +- f->frequency = av7110->current_freq; +- return 0; +-} +- +-static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); +- +- if (!av7110->analog_tuner_flags || av7110->current_input != 1) +- return -EINVAL; +- +- if (V4L2_TUNER_ANALOG_TV != f->type) +- return -EINVAL; +- +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ +- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); +- +- /* tune in desired frequency */ +- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) +- ves1820_set_tv_freq(dev, f->frequency); +- else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) +- stv0297_set_tv_freq(dev, f->frequency); +- av7110->current_freq = f->frequency; +- +- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ +- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ +- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ +- return 0; +-} +- +-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); +- +- if (av7110->analog_tuner_flags) { +- if (i->index >= 4) +- return -EINVAL; +- } else { +- if (i->index != 0) +- return -EINVAL; +- } +- +- memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); +- +- return 0; +-} +- +-static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- *input = av7110->current_input; +- dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); +- return 0; +-} +- +-static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_S_INPUT: %d\n", input); +- +- if (!av7110->analog_tuner_flags) +- return 0; +- +- if (input >= 4) +- return -EINVAL; +- +- av7110->current_input = input; +- return av7110_dvb_c_switch(fh); +-} +- +-static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +-{ +- dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); +- if (a->index != 0) +- return -EINVAL; +- memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio)); +- return 0; +-} +- +-static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +-{ +- dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); +- return 0; +-} +- +-static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, +- struct v4l2_sliced_vbi_cap *cap) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); +- if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) +- return -EINVAL; +- if (FW_VERSION(av7110->arm_app) >= 0x2623) { +- cap->service_set = V4L2_SLICED_WSS_625; +- cap->service_lines[0][23] = V4L2_SLICED_WSS_625; +- } +- return 0; +-} +- +-static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, +- struct v4l2_format *f) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_G_FMT:\n"); +- if (FW_VERSION(av7110->arm_app) < 0x2623) +- return -EINVAL; +- memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); +- if (av7110->wssMode) { +- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; +- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; +- f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); +- } +- return 0; +-} +- +-static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, +- struct v4l2_format *f) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; +- +- dprintk(2, "VIDIOC_S_FMT\n"); +- if (FW_VERSION(av7110->arm_app) < 0x2623) +- return -EINVAL; +- if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && +- f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { +- memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); +- /* WSS controlled by firmware */ +- av7110->wssMode = 0; +- av7110->wssData = 0; +- return av7110_fw_cmd(av7110, COMTYPE_ENCODER, +- SetWSSConfig, 1, 0); +- } else { +- memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); +- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; +- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; +- f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); +- /* WSS controlled by userspace */ +- av7110->wssMode = 1; +- av7110->wssData = 0; +- } +- return 0; +-} +- +-static int av7110_vbi_reset(struct file *file) +-{ +- struct saa7146_fh *fh = file->private_data; +- struct saa7146_dev *dev = fh->dev; +- struct av7110 *av7110 = (struct av7110*) dev->ext_priv; +- +- dprintk(2, "%s\n", __func__); +- av7110->wssMode = 0; +- av7110->wssData = 0; +- if (FW_VERSION(av7110->arm_app) < 0x2623) +- return 0; +- else +- return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); +-} +- +-static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) +-{ +- struct saa7146_fh *fh = file->private_data; +- struct saa7146_dev *dev = fh->dev; +- struct av7110 *av7110 = (struct av7110*) dev->ext_priv; +- struct v4l2_sliced_vbi_data d; +- int rc; +- +- dprintk(2, "%s\n", __func__); +- if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) +- return -EINVAL; +- if (copy_from_user(&d, data, count)) +- return -EFAULT; +- if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) +- return -EINVAL; +- if (d.id) +- av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; +- else +- av7110->wssData = 0x8000; +- rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); +- return (rc < 0) ? rc : count; +-} +- +-/**************************************************************************** +- * INITIALIZATION +- ****************************************************************************/ +- +-static u8 saa7113_init_regs[] = { +- 0x02, 0xd0, +- 0x03, 0x23, +- 0x04, 0x00, +- 0x05, 0x00, +- 0x06, 0xe9, +- 0x07, 0x0d, +- 0x08, 0x98, +- 0x09, 0x02, +- 0x0a, 0x80, +- 0x0b, 0x40, +- 0x0c, 0x40, +- 0x0d, 0x00, +- 0x0e, 0x01, +- 0x0f, 0x7c, +- 0x10, 0x48, +- 0x11, 0x0c, +- 0x12, 0x8b, +- 0x13, 0x1a, +- 0x14, 0x00, +- 0x15, 0x00, +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1b, 0x00, +- 0x1c, 0x00, +- 0x1d, 0x00, +- 0x1e, 0x00, +- +- 0x41, 0x77, +- 0x42, 0x77, +- 0x43, 0x77, +- 0x44, 0x77, +- 0x45, 0x77, +- 0x46, 0x77, +- 0x47, 0x77, +- 0x48, 0x77, +- 0x49, 0x77, +- 0x4a, 0x77, +- 0x4b, 0x77, +- 0x4c, 0x77, +- 0x4d, 0x77, +- 0x4e, 0x77, +- 0x4f, 0x77, +- 0x50, 0x77, +- 0x51, 0x77, +- 0x52, 0x77, +- 0x53, 0x77, +- 0x54, 0x77, +- 0x55, 0x77, +- 0x56, 0x77, +- 0x57, 0xff, +- +- 0xff +-}; +- +- +-static struct saa7146_ext_vv av7110_vv_data_st; +-static struct saa7146_ext_vv av7110_vv_data_c; +- +-int av7110_init_analog_module(struct av7110 *av7110) +-{ +- u16 version1, version2; +- +- if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && +- i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { +- pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", +- av7110->dvb_adapter.num); +- av7110->adac_type = DVB_ADAC_MSP34x0; +- } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && +- i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { +- pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", +- av7110->dvb_adapter.num); +- av7110->adac_type = DVB_ADAC_MSP34x5; +- } else +- return -ENODEV; +- +- msleep(100); // the probing above resets the msp... +- msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); +- msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); +- dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", +- av7110->dvb_adapter.num, version1, version2); +- msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); +- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone +- msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source +- msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source +- msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume +- msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source +- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume +- msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART +- +- if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { +- pr_info("saa7113 not accessible\n"); +- } else { +- u8 *i = saa7113_init_regs; +- +- if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { +- /* Fujitsu/Siemens DVB-Cable */ +- av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; +- } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { +- /* Hauppauge/TT DVB-C premium */ +- av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; +- } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { +- /* Hauppauge/TT DVB-C premium */ +- av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; +- } +- +- /* setup for DVB by default */ +- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { +- if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) +- dprintk(1, "setting band in demodulator failed\n"); +- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { +- saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) +- saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) +- } +- +- /* init the saa7113 */ +- while (*i != 0xff) { +- if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { +- dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); +- break; +- } +- i += 2; +- } +- /* setup msp for analog sound: B/G Dual-FM */ +- msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 +- msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG +- msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz +- msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI +- msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz +- msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI +- msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 +- } +- +- memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); +- /* set dd1 stream a & b */ +- saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(av7110->dev, DD1_INIT, 0x03000700); +- saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- +- return 0; +-} +- +-int av7110_init_v4l(struct av7110 *av7110) +-{ +- struct saa7146_dev* dev = av7110->dev; +- struct saa7146_ext_vv *vv_data; +- int ret; +- +- /* special case DVB-C: these cards have an analog tuner +- plus need some special handling, so we have separate +- saa7146_ext_vv data for these... */ +- if (av7110->analog_tuner_flags) +- vv_data = &av7110_vv_data_c; +- else +- vv_data = &av7110_vv_data_st; +- ret = saa7146_vv_init(dev, vv_data); +- +- if (ret) { +- ERR("cannot init capture device. skipping\n"); +- return -ENODEV; +- } +- vv_data->ops.vidioc_enum_input = vidioc_enum_input; +- vv_data->ops.vidioc_g_input = vidioc_g_input; +- vv_data->ops.vidioc_s_input = vidioc_s_input; +- vv_data->ops.vidioc_g_tuner = vidioc_g_tuner; +- vv_data->ops.vidioc_s_tuner = vidioc_s_tuner; +- vv_data->ops.vidioc_g_frequency = vidioc_g_frequency; +- vv_data->ops.vidioc_s_frequency = vidioc_s_frequency; +- vv_data->ops.vidioc_g_audio = vidioc_g_audio; +- vv_data->ops.vidioc_s_audio = vidioc_s_audio; +- vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; +- vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; +- vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; +- +- if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { +- ERR("cannot register capture device. skipping\n"); +- saa7146_vv_release(dev); +- return -ENODEV; +- } +- if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) +- ERR("cannot register vbi v4l2 device. skipping\n"); +- return 0; +-} +- +-int av7110_exit_v4l(struct av7110 *av7110) +-{ +- struct saa7146_dev* dev = av7110->dev; +- +- saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); +- saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); +- +- saa7146_vv_release(dev); +- +- return 0; +-} +- +- +- +-/* FIXME: these values are experimental values that look better than the +- values from the latest "official" driver -- at least for me... (MiHu) */ +-static struct saa7146_standard standard[] = { +- { +- .name = "PAL", .id = V4L2_STD_PAL_BG, +- .v_offset = 0x15, .v_field = 288, +- .h_offset = 0x48, .h_pixels = 708, +- .v_max_out = 576, .h_max_out = 768, +- }, { +- .name = "NTSC", .id = V4L2_STD_NTSC, +- .v_offset = 0x10, .v_field = 244, +- .h_offset = 0x40, .h_pixels = 708, +- .v_max_out = 480, .h_max_out = 640, +- } +-}; +- +-static struct saa7146_standard analog_standard[] = { +- { +- .name = "PAL", .id = V4L2_STD_PAL_BG, +- .v_offset = 0x1b, .v_field = 288, +- .h_offset = 0x08, .h_pixels = 708, +- .v_max_out = 576, .h_max_out = 768, +- }, { +- .name = "NTSC", .id = V4L2_STD_NTSC, +- .v_offset = 0x10, .v_field = 244, +- .h_offset = 0x40, .h_pixels = 708, +- .v_max_out = 480, .h_max_out = 640, +- } +-}; +- +-static struct saa7146_standard dvb_standard[] = { +- { +- .name = "PAL", .id = V4L2_STD_PAL_BG, +- .v_offset = 0x14, .v_field = 288, +- .h_offset = 0x48, .h_pixels = 708, +- .v_max_out = 576, .h_max_out = 768, +- }, { +- .name = "NTSC", .id = V4L2_STD_NTSC, +- .v_offset = 0x10, .v_field = 244, +- .h_offset = 0x40, .h_pixels = 708, +- .v_max_out = 480, .h_max_out = 640, +- } +-}; +- +-static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) +-{ +- struct av7110 *av7110 = (struct av7110*) dev->ext_priv; +- +- if (std->id & V4L2_STD_PAL) { +- av7110->vidmode = AV7110_VIDEO_MODE_PAL; +- av7110_set_vidmode(av7110, av7110->vidmode); +- } +- else if (std->id & V4L2_STD_NTSC) { +- av7110->vidmode = AV7110_VIDEO_MODE_NTSC; +- av7110_set_vidmode(av7110, av7110->vidmode); +- } +- else +- return -1; +- +- return 0; +-} +- +- +-static struct saa7146_ext_vv av7110_vv_data_st = { +- .inputs = 1, +- .audios = 1, +- .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT, +- .flags = 0, +- +- .stds = &standard[0], +- .num_stds = ARRAY_SIZE(standard), +- .std_callback = &std_callback, +- +- .vbi_fops.open = av7110_vbi_reset, +- .vbi_fops.release = av7110_vbi_reset, +- .vbi_fops.write = av7110_vbi_write, +-}; +- +-static struct saa7146_ext_vv av7110_vv_data_c = { +- .inputs = 1, +- .audios = 1, +- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT, +- .flags = SAA7146_USE_PORT_B_FOR_VBI, +- +- .stds = &standard[0], +- .num_stds = ARRAY_SIZE(standard), +- .std_callback = &std_callback, +- +- .vbi_fops.open = av7110_vbi_reset, +- .vbi_fops.release = av7110_vbi_reset, +- .vbi_fops.write = av7110_vbi_write, +-}; +- +diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c +deleted file mode 100644 +index 8b32e28..0000000 +--- a/drivers/media/dvb/ttpci/budget-av.c ++++ /dev/null +@@ -1,1640 +0,0 @@ +-/* +- * budget-av.c: driver for the SAA7146 based Budget DVB cards +- * with analog video in +- * +- * Compiled from various sources by Michael Hunold +- * +- * CI interface support (c) 2004 Olivier Gournet & +- * Andrew de Quincey +- * +- * Copyright (C) 2002 Ralph Metzler +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +- +-#include "budget.h" +-#include "stv0299.h" +-#include "stb0899_drv.h" +-#include "stb0899_reg.h" +-#include "stb0899_cfg.h" +-#include "tda8261.h" +-#include "tda8261_cfg.h" +-#include "tda1002x.h" +-#include "tda1004x.h" +-#include "tua6100.h" +-#include "dvb-pll.h" +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_ca_en50221.h" +- +-#define DEBICICAM 0x02420000 +- +-#define SLOTSTATUS_NONE 1 +-#define SLOTSTATUS_PRESENT 2 +-#define SLOTSTATUS_RESET 4 +-#define SLOTSTATUS_READY 8 +-#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct budget_av { +- struct budget budget; +- struct video_device *vd; +- int cur_input; +- int has_saa7113; +- struct tasklet_struct ciintf_irq_tasklet; +- int slot_status; +- struct dvb_ca_en50221 ca; +- u8 reinitialise_demod:1; +-}; +- +-static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); +- +- +-/* GPIO Connections: +- * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! +- * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory +- * 2 - CI Card Enable (Active Low) +- * 3 - CI Card Detect +- */ +- +-/**************************************************************************** +- * INITIALIZATION +- ****************************************************************************/ +- +-static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) +-{ +- u8 mm1[] = { 0x00 }; +- u8 mm2[] = { 0x00 }; +- struct i2c_msg msgs[2]; +- +- msgs[0].flags = 0; +- msgs[1].flags = I2C_M_RD; +- msgs[0].addr = msgs[1].addr = id / 2; +- mm1[0] = reg; +- msgs[0].len = 1; +- msgs[1].len = 1; +- msgs[0].buf = mm1; +- msgs[1].buf = mm2; +- +- i2c_transfer(i2c, msgs, 2); +- +- return mm2[0]; +-} +- +-static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) +-{ +- u8 mm1[] = { reg }; +- struct i2c_msg msgs[2] = { +- {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, +- {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} +- }; +- +- if (i2c_transfer(i2c, msgs, 2) != 2) +- return -EIO; +- +- return 0; +-} +- +-static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) +-{ +- u8 msg[2] = { reg, val }; +- struct i2c_msg msgs; +- +- msgs.flags = 0; +- msgs.addr = id / 2; +- msgs.len = 2; +- msgs.buf = msg; +- return i2c_transfer(i2c, &msgs, 1); +-} +- +-static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- int result; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); +- udelay(1); +- +- result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); +- if (result == -ETIMEDOUT) { +- ciintf_slot_shutdown(ca, slot); +- pr_info("cam ejected 1\n"); +- } +- return result; +-} +- +-static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- int result; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); +- udelay(1); +- +- result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); +- if (result == -ETIMEDOUT) { +- ciintf_slot_shutdown(ca, slot); +- pr_info("cam ejected 2\n"); +- } +- return result; +-} +- +-static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- int result; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); +- udelay(1); +- +- result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); +- if (result == -ETIMEDOUT) { +- ciintf_slot_shutdown(ca, slot); +- pr_info("cam ejected 3\n"); +- return -ETIMEDOUT; +- } +- return result; +-} +- +-static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- int result; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); +- udelay(1); +- +- result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); +- if (result == -ETIMEDOUT) { +- ciintf_slot_shutdown(ca, slot); +- pr_info("cam ejected 5\n"); +- } +- return result; +-} +- +-static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- struct saa7146_dev *saa = budget_av->budget.dev; +- +- if (slot != 0) +- return -EINVAL; +- +- dprintk(1, "ciintf_slot_reset\n"); +- budget_av->slot_status = SLOTSTATUS_RESET; +- +- saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ +- +- saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ +- msleep(2); +- saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ +- msleep(20); /* 20 ms Vcc settling time */ +- +- saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); +- msleep(20); +- +- /* reinitialise the frontend if necessary */ +- if (budget_av->reinitialise_demod) +- dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); +- +- return 0; +-} +- +-static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- struct saa7146_dev *saa = budget_av->budget.dev; +- +- if (slot != 0) +- return -EINVAL; +- +- dprintk(1, "ciintf_slot_shutdown\n"); +- +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); +- budget_av->slot_status = SLOTSTATUS_NONE; +- +- return 0; +-} +- +-static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- struct saa7146_dev *saa = budget_av->budget.dev; +- +- if (slot != 0) +- return -EINVAL; +- +- dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); +- +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); +- +- return 0; +-} +- +-static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +-{ +- struct budget_av *budget_av = (struct budget_av *) ca->data; +- struct saa7146_dev *saa = budget_av->budget.dev; +- int result; +- +- if (slot != 0) +- return -EINVAL; +- +- /* test the card detect line - needs to be done carefully +- * since it never goes high for some CAMs on this interface (e.g. topuptv) */ +- if (budget_av->slot_status == SLOTSTATUS_NONE) { +- saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); +- udelay(1); +- if (saa7146_read(saa, PSR) & MASK_06) { +- if (budget_av->slot_status == SLOTSTATUS_NONE) { +- budget_av->slot_status = SLOTSTATUS_PRESENT; +- pr_info("cam inserted A\n"); +- } +- } +- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); +- } +- +- /* We also try and read from IO memory to work round the above detection bug. If +- * there is no CAM, we will get a timeout. Only done if there is no cam +- * present, since this test actually breaks some cams :( +- * +- * if the CI interface is not open, we also do the above test since we +- * don't care if the cam has problems - we'll be resetting it on open() anyway */ +- if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { +- saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); +- result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); +- if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { +- budget_av->slot_status = SLOTSTATUS_PRESENT; +- pr_info("cam inserted B\n"); +- } else if (result < 0) { +- if (budget_av->slot_status != SLOTSTATUS_NONE) { +- ciintf_slot_shutdown(ca, slot); +- pr_info("cam ejected 5\n"); +- return 0; +- } +- } +- } +- +- /* read from attribute memory in reset/ready state to know when the CAM is ready */ +- if (budget_av->slot_status == SLOTSTATUS_RESET) { +- result = ciintf_read_attribute_mem(ca, slot, 0); +- if (result == 0x1d) { +- budget_av->slot_status = SLOTSTATUS_READY; +- } +- } +- +- /* work out correct return code */ +- if (budget_av->slot_status != SLOTSTATUS_NONE) { +- if (budget_av->slot_status & SLOTSTATUS_READY) { +- return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; +- } +- return DVB_CA_EN50221_POLL_CAM_PRESENT; +- } +- return 0; +-} +- +-static int ciintf_init(struct budget_av *budget_av) +-{ +- struct saa7146_dev *saa = budget_av->budget.dev; +- int result; +- +- memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); +- +- saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); +- saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); +- saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); +- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); +- +- /* Enable DEBI pins */ +- saa7146_write(saa, MC1, MASK_27 | MASK_11); +- +- /* register CI interface */ +- budget_av->ca.owner = THIS_MODULE; +- budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; +- budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; +- budget_av->ca.read_cam_control = ciintf_read_cam_control; +- budget_av->ca.write_cam_control = ciintf_write_cam_control; +- budget_av->ca.slot_reset = ciintf_slot_reset; +- budget_av->ca.slot_shutdown = ciintf_slot_shutdown; +- budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; +- budget_av->ca.poll_slot_status = ciintf_poll_slot_status; +- budget_av->ca.data = budget_av; +- budget_av->budget.ci_present = 1; +- budget_av->slot_status = SLOTSTATUS_NONE; +- +- if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, +- &budget_av->ca, 0, 1)) != 0) { +- pr_err("ci initialisation failed\n"); +- goto error; +- } +- +- pr_info("ci interface initialised\n"); +- return 0; +- +-error: +- saa7146_write(saa, MC1, MASK_27); +- return result; +-} +- +-static void ciintf_deinit(struct budget_av *budget_av) +-{ +- struct saa7146_dev *saa = budget_av->budget.dev; +- +- saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); +- saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); +- saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); +- saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); +- +- /* release the CA device */ +- dvb_ca_en50221_release(&budget_av->ca); +- +- /* disable DEBI pins */ +- saa7146_write(saa, MC1, MASK_27); +-} +- +- +-static const u8 saa7113_tab[] = { +- 0x01, 0x08, +- 0x02, 0xc0, +- 0x03, 0x33, +- 0x04, 0x00, +- 0x05, 0x00, +- 0x06, 0xeb, +- 0x07, 0xe0, +- 0x08, 0x28, +- 0x09, 0x00, +- 0x0a, 0x80, +- 0x0b, 0x47, +- 0x0c, 0x40, +- 0x0d, 0x00, +- 0x0e, 0x01, +- 0x0f, 0x44, +- +- 0x10, 0x08, +- 0x11, 0x0c, +- 0x12, 0x7b, +- 0x13, 0x00, +- 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, +- +- 0x57, 0xff, +- 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, +- 0x5b, 0x83, 0x5e, 0x00, +- 0xff +-}; +- +-static int saa7113_init(struct budget_av *budget_av) +-{ +- struct budget *budget = &budget_av->budget; +- struct saa7146_dev *saa = budget->dev; +- const u8 *data = saa7113_tab; +- +- saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); +- msleep(200); +- +- if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { +- dprintk(1, "saa7113 not found on KNC card\n"); +- return -ENODEV; +- } +- +- dprintk(1, "saa7113 detected and initializing\n"); +- +- while (*data != 0xff) { +- i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); +- data += 2; +- } +- +- dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); +- +- return 0; +-} +- +-static int saa7113_setinput(struct budget_av *budget_av, int input) +-{ +- struct budget *budget = &budget_av->budget; +- +- if (1 != budget_av->has_saa7113) +- return -ENODEV; +- +- if (input == 1) { +- i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); +- i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); +- } else if (input == 0) { +- i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); +- i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); +- } else +- return -EINVAL; +- +- budget_av->cur_input = input; +- return 0; +-} +- +- +-static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +-{ +- u8 aclk = 0; +- u8 bclk = 0; +- u8 m1; +- +- aclk = 0xb5; +- if (srate < 2000000) +- bclk = 0x86; +- else if (srate < 5000000) +- bclk = 0x89; +- else if (srate < 15000000) +- bclk = 0x8f; +- else if (srate < 45000000) +- bclk = 0x95; +- +- m1 = 0x14; +- if (srate < 4000000) +- m1 = 0x10; +- +- 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); +- stv0299_writereg(fe, 0x0f, 0x80 | m1); +- +- return 0; +-} +- +-static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- u32 div; +- u8 buf[4]; +- struct budget *budget = (struct budget *) fe->dvb->priv; +- struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; +- +- if ((c->frequency < 950000) || (c->frequency > 2150000)) +- return -EINVAL; +- +- div = (c->frequency + (125 - 1)) / 125; /* round correctly */ +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; +- buf[3] = 0x20; +- +- if (c->symbol_rate < 4000000) +- buf[3] |= 1; +- +- if (c->frequency < 1250000) +- buf[3] |= 0; +- else if (c->frequency < 1550000) +- buf[3] |= 0x40; +- else if (c->frequency < 2050000) +- buf[3] |= 0x80; +- else if (c->frequency < 2150000) +- buf[3] |= 0xC0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static u8 typhoon_cinergy1200s_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x40, /* DAC not used, set to high impendance mode */ +- 0x07, 0x00, /* DAC LSB */ +- 0x08, 0x40, /* DiSEqC off */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ +- 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ +- 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ +- 0x10, 0x3f, // AGC2 0x3d +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, // lock detector threshold +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 +- 0x29, 0x1e, // 1/2 threshold +- 0x2a, 0x14, // 2/3 threshold +- 0x2b, 0x0f, // 3/4 threshold +- 0x2c, 0x09, // 5/6 threshold +- 0x2d, 0x05, // 7/8 threshold +- 0x2e, 0x01, +- 0x31, 0x1f, // test all FECs +- 0x32, 0x19, // viterbi and synchro search +- 0x33, 0xfc, // rs control +- 0x34, 0x93, // error control +- 0x0f, 0x92, +- 0xff, 0xff +-}; +- +-static struct stv0299_config typhoon_config = { +- .demod_address = 0x68, +- .inittab = typhoon_cinergy1200s_inittab, +- .mclk = 88000000UL, +- .invert = 0, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .min_delay_ms = 100, +- .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +-}; +- +- +-static struct stv0299_config cinergy_1200s_config = { +- .demod_address = 0x68, +- .inittab = typhoon_cinergy1200s_inittab, +- .mclk = 88000000UL, +- .invert = 0, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_0, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .min_delay_ms = 100, +- .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +-}; +- +-static struct stv0299_config cinergy_1200s_1894_0010_config = { +- .demod_address = 0x68, +- .inittab = typhoon_cinergy1200s_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .min_delay_ms = 100, +- .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, +-}; +- +-static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget *budget = (struct budget *) fe->dvb->priv; +- u8 buf[6]; +- struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; +- int i; +- +-#define CU1216_IF 36125000 +-#define TUNER_MUL 62500 +- +- u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0xce; +- buf[3] = (c->frequency < 150000000 ? 0x01 : +- c->frequency < 445000000 ? 0x02 : 0x04); +- buf[4] = 0xde; +- buf[5] = 0x20; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) +- return -EIO; +- +- /* wait for the pll lock */ +- msg.flags = I2C_M_RD; +- msg.len = 1; +- for (i = 0; i < 20; i++) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) +- break; +- msleep(10); +- } +- +- /* switch the charge pump to the lower current */ +- msg.flags = 0; +- msg.len = 2; +- msg.buf = &buf[2]; +- buf[2] &= ~0x40; +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-static struct tda1002x_config philips_cu1216_config = { +- .demod_address = 0x0c, +- .invert = 1, +-}; +- +-static struct tda1002x_config philips_cu1216_config_altaddress = { +- .demod_address = 0x0d, +- .invert = 0, +-}; +- +-static struct tda10023_config philips_cu1216_tda10023_config = { +- .demod_address = 0x0c, +- .invert = 1, +-}; +- +-static int philips_tu1216_tuner_init(struct dvb_frontend *fe) +-{ +- struct budget *budget = (struct budget *) fe->dvb->priv; +- static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; +- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; +- +- // setup PLL configuration +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- msleep(1); +- +- return 0; +-} +- +-static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget *budget = (struct budget *) fe->dvb->priv; +- u8 tuner_buf[4]; +- struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = +- sizeof(tuner_buf) }; +- int tuner_frequency = 0; +- u8 band, cp, filter; +- +- // determine charge pump +- tuner_frequency = c->frequency + 36166000; +- if (tuner_frequency < 87000000) +- return -EINVAL; +- else if (tuner_frequency < 130000000) +- cp = 3; +- else if (tuner_frequency < 160000000) +- cp = 5; +- else if (tuner_frequency < 200000000) +- cp = 6; +- else if (tuner_frequency < 290000000) +- cp = 3; +- else if (tuner_frequency < 420000000) +- cp = 5; +- else if (tuner_frequency < 480000000) +- cp = 6; +- else if (tuner_frequency < 620000000) +- cp = 3; +- else if (tuner_frequency < 830000000) +- cp = 5; +- else if (tuner_frequency < 895000000) +- cp = 7; +- else +- return -EINVAL; +- +- // determine band +- if (c->frequency < 49000000) +- return -EINVAL; +- else if (c->frequency < 161000000) +- band = 1; +- else if (c->frequency < 444000000) +- band = 2; +- else if (c->frequency < 861000000) +- band = 4; +- else +- return -EINVAL; +- +- // setup PLL filter +- switch (c->bandwidth_hz) { +- case 6000000: +- filter = 0; +- break; +- +- case 7000000: +- filter = 0; +- break; +- +- case 8000000: +- filter = 1; +- break; +- +- default: +- return -EINVAL; +- } +- +- // calculate divisor +- // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) +- tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; +- +- // setup tuner buffer +- tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; +- tuner_buf[1] = tuner_frequency & 0xff; +- tuner_buf[2] = 0xca; +- tuner_buf[3] = (cp << 5) | (filter << 3) | band; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- +- msleep(1); +- return 0; +-} +- +-static int philips_tu1216_request_firmware(struct dvb_frontend *fe, +- const struct firmware **fw, char *name) +-{ +- struct budget *budget = (struct budget *) fe->dvb->priv; +- +- return request_firmware(fw, name, &budget->dev->pci->dev); +-} +- +-static struct tda1004x_config philips_tu1216_config = { +- +- .demod_address = 0x8, +- .invert = 1, +- .invert_oclk = 1, +- .xtal_freq = TDA10046_XTAL_4M, +- .agc_config = TDA10046_AGC_DEFAULT, +- .if_freq = TDA10046_FREQ_3617, +- .request_firmware = philips_tu1216_request_firmware, +-}; +- +-static u8 philips_sd1878_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, +- 0x05, 0x35, +- 0x06, 0x40, +- 0x07, 0x00, +- 0x08, 0x43, +- 0x09, 0x02, +- 0x0C, 0x51, +- 0x0D, 0x82, +- 0x0E, 0x23, +- 0x10, 0x3f, +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, +- 0x16, 0x19, +- 0x17, 0x8c, +- 0x18, 0x59, +- 0x19, 0xf8, +- 0x1a, 0xfe, +- 0x1c, 0x7f, +- 0x1d, 0x00, +- 0x1e, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, +- 0x29, 0x28, +- 0x2a, 0x14, +- 0x2b, 0x0f, +- 0x2c, 0x09, +- 0x2d, 0x09, +- 0x31, 0x1f, +- 0x32, 0x19, +- 0x33, 0xfc, +- 0x34, 0x93, +- 0xff, 0xff +-}; +- +-static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, +- u32 srate, u32 ratio) +-{ +- u8 aclk = 0; +- u8 bclk = 0; +- u8 m1; +- +- aclk = 0xb5; +- if (srate < 2000000) +- bclk = 0x86; +- else if (srate < 5000000) +- bclk = 0x89; +- else if (srate < 15000000) +- bclk = 0x8f; +- else if (srate < 45000000) +- bclk = 0x95; +- +- m1 = 0x14; +- if (srate < 4000000) +- m1 = 0x10; +- +- stv0299_writereg(fe, 0x0e, 0x23); +- stv0299_writereg(fe, 0x0f, 0x94); +- stv0299_writereg(fe, 0x10, 0x39); +- stv0299_writereg(fe, 0x13, aclk); +- stv0299_writereg(fe, 0x14, bclk); +- stv0299_writereg(fe, 0x15, 0xc9); +- stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); +- stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); +- stv0299_writereg(fe, 0x21, (ratio) & 0xf0); +- stv0299_writereg(fe, 0x0f, 0x80 | m1); +- +- return 0; +-} +- +-static struct stv0299_config philips_sd1878_config = { +- .demod_address = 0x68, +- .inittab = philips_sd1878_inittab, +- .mclk = 88000000UL, +- .invert = 0, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP0, +- .min_delay_ms = 100, +- .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, +-}; +- +-/* KNC1 DVB-S (STB0899) Inittab */ +-static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { +- +- { STB0899_DEV_ID , 0x81 }, +- { STB0899_DISCNTRL1 , 0x32 }, +- { STB0899_DISCNTRL2 , 0x80 }, +- { STB0899_DISRX_ST0 , 0x04 }, +- { STB0899_DISRX_ST1 , 0x00 }, +- { STB0899_DISPARITY , 0x00 }, +- { STB0899_DISSTATUS , 0x20 }, +- { STB0899_DISF22 , 0x8c }, +- { STB0899_DISF22RX , 0x9a }, +- { STB0899_SYSREG , 0x0b }, +- { STB0899_ACRPRESC , 0x11 }, +- { STB0899_ACRDIV1 , 0x0a }, +- { STB0899_ACRDIV2 , 0x05 }, +- { STB0899_DACR1 , 0x00 }, +- { STB0899_DACR2 , 0x00 }, +- { STB0899_OUTCFG , 0x00 }, +- { STB0899_MODECFG , 0x00 }, +- { STB0899_IRQSTATUS_3 , 0x30 }, +- { STB0899_IRQSTATUS_2 , 0x00 }, +- { STB0899_IRQSTATUS_1 , 0x00 }, +- { STB0899_IRQSTATUS_0 , 0x00 }, +- { STB0899_IRQMSK_3 , 0xf3 }, +- { STB0899_IRQMSK_2 , 0xfc }, +- { STB0899_IRQMSK_1 , 0xff }, +- { STB0899_IRQMSK_0 , 0xff }, +- { STB0899_IRQCFG , 0x00 }, +- { STB0899_I2CCFG , 0x88 }, +- { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ +- { STB0899_IOPVALUE5 , 0x00 }, +- { STB0899_IOPVALUE4 , 0x20 }, +- { STB0899_IOPVALUE3 , 0xc9 }, +- { STB0899_IOPVALUE2 , 0x90 }, +- { STB0899_IOPVALUE1 , 0x40 }, +- { STB0899_IOPVALUE0 , 0x00 }, +- { STB0899_GPIO00CFG , 0x82 }, +- { STB0899_GPIO01CFG , 0x82 }, +- { STB0899_GPIO02CFG , 0x82 }, +- { STB0899_GPIO03CFG , 0x82 }, +- { STB0899_GPIO04CFG , 0x82 }, +- { STB0899_GPIO05CFG , 0x82 }, +- { STB0899_GPIO06CFG , 0x82 }, +- { STB0899_GPIO07CFG , 0x82 }, +- { STB0899_GPIO08CFG , 0x82 }, +- { STB0899_GPIO09CFG , 0x82 }, +- { STB0899_GPIO10CFG , 0x82 }, +- { STB0899_GPIO11CFG , 0x82 }, +- { STB0899_GPIO12CFG , 0x82 }, +- { STB0899_GPIO13CFG , 0x82 }, +- { STB0899_GPIO14CFG , 0x82 }, +- { STB0899_GPIO15CFG , 0x82 }, +- { STB0899_GPIO16CFG , 0x82 }, +- { STB0899_GPIO17CFG , 0x82 }, +- { STB0899_GPIO18CFG , 0x82 }, +- { STB0899_GPIO19CFG , 0x82 }, +- { STB0899_GPIO20CFG , 0x82 }, +- { STB0899_SDATCFG , 0xb8 }, +- { STB0899_SCLTCFG , 0xba }, +- { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ +- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ +- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ +- { STB0899_DIRCLKCFG , 0x82 }, +- { STB0899_CLKOUT27CFG , 0x7e }, +- { STB0899_STDBYCFG , 0x82 }, +- { STB0899_CS0CFG , 0x82 }, +- { STB0899_CS1CFG , 0x82 }, +- { STB0899_DISEQCOCFG , 0x20 }, +- { STB0899_GPIO32CFG , 0x82 }, +- { STB0899_GPIO33CFG , 0x82 }, +- { STB0899_GPIO34CFG , 0x82 }, +- { STB0899_GPIO35CFG , 0x82 }, +- { STB0899_GPIO36CFG , 0x82 }, +- { STB0899_GPIO37CFG , 0x82 }, +- { STB0899_GPIO38CFG , 0x82 }, +- { STB0899_GPIO39CFG , 0x82 }, +- { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ +- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ +- { STB0899_FILTCTRL , 0x00 }, +- { STB0899_SYSCTRL , 0x00 }, +- { STB0899_STOPCLK1 , 0x20 }, +- { STB0899_STOPCLK2 , 0x00 }, +- { STB0899_INTBUFSTATUS , 0x00 }, +- { STB0899_INTBUFCTRL , 0x0a }, +- { 0xffff , 0xff }, +-}; +- +-static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { +- { STB0899_DEMOD , 0x00 }, +- { STB0899_RCOMPC , 0xc9 }, +- { STB0899_AGC1CN , 0x41 }, +- { STB0899_AGC1REF , 0x08 }, +- { STB0899_RTC , 0x7a }, +- { STB0899_TMGCFG , 0x4e }, +- { STB0899_AGC2REF , 0x33 }, +- { STB0899_TLSR , 0x84 }, +- { STB0899_CFD , 0xee }, +- { STB0899_ACLC , 0x87 }, +- { STB0899_BCLC , 0x94 }, +- { STB0899_EQON , 0x41 }, +- { STB0899_LDT , 0xdd }, +- { STB0899_LDT2 , 0xc9 }, +- { STB0899_EQUALREF , 0xb4 }, +- { STB0899_TMGRAMP , 0x10 }, +- { STB0899_TMGTHD , 0x30 }, +- { STB0899_IDCCOMP , 0xfb }, +- { STB0899_QDCCOMP , 0x03 }, +- { STB0899_POWERI , 0x3b }, +- { STB0899_POWERQ , 0x3d }, +- { STB0899_RCOMP , 0x81 }, +- { STB0899_AGCIQIN , 0x80 }, +- { STB0899_AGC2I1 , 0x04 }, +- { STB0899_AGC2I2 , 0xf5 }, +- { STB0899_TLIR , 0x25 }, +- { STB0899_RTF , 0x80 }, +- { STB0899_DSTATUS , 0x00 }, +- { STB0899_LDI , 0xca }, +- { STB0899_CFRM , 0xf1 }, +- { STB0899_CFRL , 0xf3 }, +- { STB0899_NIRM , 0x2a }, +- { STB0899_NIRL , 0x05 }, +- { STB0899_ISYMB , 0x17 }, +- { STB0899_QSYMB , 0xfa }, +- { STB0899_SFRH , 0x2f }, +- { STB0899_SFRM , 0x68 }, +- { STB0899_SFRL , 0x40 }, +- { STB0899_SFRUPH , 0x2f }, +- { STB0899_SFRUPM , 0x68 }, +- { STB0899_SFRUPL , 0x40 }, +- { STB0899_EQUAI1 , 0xfd }, +- { STB0899_EQUAQ1 , 0x04 }, +- { STB0899_EQUAI2 , 0x0f }, +- { STB0899_EQUAQ2 , 0xff }, +- { STB0899_EQUAI3 , 0xdf }, +- { STB0899_EQUAQ3 , 0xfa }, +- { STB0899_EQUAI4 , 0x37 }, +- { STB0899_EQUAQ4 , 0x0d }, +- { STB0899_EQUAI5 , 0xbd }, +- { STB0899_EQUAQ5 , 0xf7 }, +- { STB0899_DSTATUS2 , 0x00 }, +- { STB0899_VSTATUS , 0x00 }, +- { STB0899_VERROR , 0xff }, +- { STB0899_IQSWAP , 0x2a }, +- { STB0899_ECNT1M , 0x00 }, +- { STB0899_ECNT1L , 0x00 }, +- { STB0899_ECNT2M , 0x00 }, +- { STB0899_ECNT2L , 0x00 }, +- { STB0899_ECNT3M , 0x00 }, +- { STB0899_ECNT3L , 0x00 }, +- { STB0899_FECAUTO1 , 0x06 }, +- { STB0899_FECM , 0x01 }, +- { STB0899_VTH12 , 0xf0 }, +- { STB0899_VTH23 , 0xa0 }, +- { STB0899_VTH34 , 0x78 }, +- { STB0899_VTH56 , 0x4e }, +- { STB0899_VTH67 , 0x48 }, +- { STB0899_VTH78 , 0x38 }, +- { STB0899_PRVIT , 0xff }, +- { STB0899_VITSYNC , 0x19 }, +- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ +- { STB0899_TSULC , 0x42 }, +- { STB0899_RSLLC , 0x40 }, +- { STB0899_TSLPL , 0x12 }, +- { STB0899_TSCFGH , 0x0c }, +- { STB0899_TSCFGM , 0x00 }, +- { STB0899_TSCFGL , 0x0c }, +- { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ +- { STB0899_RSSYNCDEL , 0x00 }, +- { STB0899_TSINHDELH , 0x02 }, +- { STB0899_TSINHDELM , 0x00 }, +- { STB0899_TSINHDELL , 0x00 }, +- { STB0899_TSLLSTKM , 0x00 }, +- { STB0899_TSLLSTKL , 0x00 }, +- { STB0899_TSULSTKM , 0x00 }, +- { STB0899_TSULSTKL , 0xab }, +- { STB0899_PCKLENUL , 0x00 }, +- { STB0899_PCKLENLL , 0xcc }, +- { STB0899_RSPCKLEN , 0xcc }, +- { STB0899_TSSTATUS , 0x80 }, +- { STB0899_ERRCTRL1 , 0xb6 }, +- { STB0899_ERRCTRL2 , 0x96 }, +- { STB0899_ERRCTRL3 , 0x89 }, +- { STB0899_DMONMSK1 , 0x27 }, +- { STB0899_DMONMSK0 , 0x03 }, +- { STB0899_DEMAPVIT , 0x5c }, +- { STB0899_PLPARM , 0x1f }, +- { STB0899_PDELCTRL , 0x48 }, +- { STB0899_PDELCTRL2 , 0x00 }, +- { STB0899_BBHCTRL1 , 0x00 }, +- { STB0899_BBHCTRL2 , 0x00 }, +- { STB0899_HYSTTHRESH , 0x77 }, +- { STB0899_MATCSTM , 0x00 }, +- { STB0899_MATCSTL , 0x00 }, +- { STB0899_UPLCSTM , 0x00 }, +- { STB0899_UPLCSTL , 0x00 }, +- { STB0899_DFLCSTM , 0x00 }, +- { STB0899_DFLCSTL , 0x00 }, +- { STB0899_SYNCCST , 0x00 }, +- { STB0899_SYNCDCSTM , 0x00 }, +- { STB0899_SYNCDCSTL , 0x00 }, +- { STB0899_ISI_ENTRY , 0x00 }, +- { STB0899_ISI_BIT_EN , 0x00 }, +- { STB0899_MATSTRM , 0x00 }, +- { STB0899_MATSTRL , 0x00 }, +- { STB0899_UPLSTRM , 0x00 }, +- { STB0899_UPLSTRL , 0x00 }, +- { STB0899_DFLSTRM , 0x00 }, +- { STB0899_DFLSTRL , 0x00 }, +- { STB0899_SYNCSTR , 0x00 }, +- { STB0899_SYNCDSTRM , 0x00 }, +- { STB0899_SYNCDSTRL , 0x00 }, +- { STB0899_CFGPDELSTATUS1 , 0x10 }, +- { STB0899_CFGPDELSTATUS2 , 0x00 }, +- { STB0899_BBFERRORM , 0x00 }, +- { STB0899_BBFERRORL , 0x00 }, +- { STB0899_UPKTERRORM , 0x00 }, +- { STB0899_UPKTERRORL , 0x00 }, +- { 0xffff , 0xff }, +-}; +- +-/* STB0899 demodulator config for the KNC1 and clones */ +-static struct stb0899_config knc1_dvbs2_config = { +- .init_dev = knc1_stb0899_s1_init_1, +- .init_s2_demod = stb0899_s2_init_2, +- .init_s1_demod = knc1_stb0899_s1_init_3, +- .init_s2_fec = stb0899_s2_init_4, +- .init_tst = stb0899_s1_init_5, +- +- .postproc = NULL, +- +- .demod_address = 0x68, +-// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ +- .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ +-// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ +- +- .xtal_freq = 27000000, +- .inversion = IQ_SWAP_OFF, /* 1 */ +- +- .lo_clk = 76500000, +- .hi_clk = 90000000, +- +- .esno_ave = STB0899_DVBS2_ESNO_AVE, +- .esno_quant = STB0899_DVBS2_ESNO_QUANT, +- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, +- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, +- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, +- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, +- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, +- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, +- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, +- +- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, +- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, +- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, +- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, +- +- .tuner_get_frequency = tda8261_get_frequency, +- .tuner_set_frequency = tda8261_set_frequency, +- .tuner_set_bandwidth = NULL, +- .tuner_get_bandwidth = tda8261_get_bandwidth, +- .tuner_set_rfsiggain = NULL +-}; +- +-/* +- * SD1878/SHA tuner config +- * 1F, Single I/P, Horizontal mount, High Sensitivity +- */ +-static const struct tda8261_config sd1878c_config = { +-// .name = "SD1878/SHA", +- .addr = 0x60, +- .step_size = TDA8261_STEP_1000 /* kHz */ +-}; +- +-static u8 read_pwm(struct budget_av *budget_av) +-{ +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, +- {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} +- }; +- +- if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) +- || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +-#define SUBID_DVBS_KNC1 0x0010 +-#define SUBID_DVBS_KNC1_PLUS 0x0011 +-#define SUBID_DVBS_TYPHOON 0x4f56 +-#define SUBID_DVBS_CINERGY1200 0x1154 +-#define SUBID_DVBS_CYNERGY1200N 0x1155 +-#define SUBID_DVBS_TV_STAR 0x0014 +-#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 +-#define SUBID_DVBS_TV_STAR_CI 0x0016 +-#define SUBID_DVBS2_KNC1 0x0018 +-#define SUBID_DVBS2_KNC1_OEM 0x0019 +-#define SUBID_DVBS_EASYWATCH_1 0x001a +-#define SUBID_DVBS_EASYWATCH_2 0x001b +-#define SUBID_DVBS2_EASYWATCH 0x001d +-#define SUBID_DVBS_EASYWATCH 0x001e +- +-#define SUBID_DVBC_EASYWATCH 0x002a +-#define SUBID_DVBC_EASYWATCH_MK3 0x002c +-#define SUBID_DVBC_KNC1 0x0020 +-#define SUBID_DVBC_KNC1_PLUS 0x0021 +-#define SUBID_DVBC_KNC1_MK3 0x0022 +-#define SUBID_DVBC_KNC1_TDA10024 0x0028 +-#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +-#define SUBID_DVBC_CINERGY1200 0x1156 +-#define SUBID_DVBC_CINERGY1200_MK3 0x1176 +- +-#define SUBID_DVBT_EASYWATCH 0x003a +-#define SUBID_DVBT_KNC1_PLUS 0x0031 +-#define SUBID_DVBT_KNC1 0x0030 +-#define SUBID_DVBT_CINERGY1200 0x1157 +- +-static void frontend_init(struct budget_av *budget_av) +-{ +- struct saa7146_dev * saa = budget_av->budget.dev; +- struct dvb_frontend * fe = NULL; +- +- /* Enable / PowerON Frontend */ +- saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); +- +- /* Wait for PowerON */ +- msleep(100); +- +- /* additional setup necessary for the PLUS cards */ +- switch (saa->pci->subsystem_device) { +- case SUBID_DVBS_KNC1_PLUS: +- case SUBID_DVBC_KNC1_PLUS: +- case SUBID_DVBT_KNC1_PLUS: +- case SUBID_DVBC_EASYWATCH: +- case SUBID_DVBC_KNC1_PLUS_MK3: +- case SUBID_DVBS2_KNC1: +- case SUBID_DVBS2_KNC1_OEM: +- case SUBID_DVBS2_EASYWATCH: +- saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); +- break; +- } +- +- switch (saa->pci->subsystem_device) { +- +- case SUBID_DVBS_KNC1: +- /* +- * maybe that setting is needed for other dvb-s cards as well, +- * but so far it has been only confirmed for this type +- */ +- budget_av->reinitialise_demod = 1; +- /* fall through */ +- case SUBID_DVBS_KNC1_PLUS: +- case SUBID_DVBS_EASYWATCH_1: +- if (saa->pci->subsystem_vendor == 0x1894) { +- fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); +- } +- } else { +- fe = dvb_attach(stv0299_attach, &typhoon_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; +- } +- } +- break; +- +- case SUBID_DVBS_TV_STAR: +- case SUBID_DVBS_TV_STAR_PLUS_X4: +- case SUBID_DVBS_TV_STAR_CI: +- case SUBID_DVBS_CYNERGY1200N: +- case SUBID_DVBS_EASYWATCH: +- case SUBID_DVBS_EASYWATCH_2: +- fe = dvb_attach(stv0299_attach, &philips_sd1878_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- dvb_attach(dvb_pll_attach, fe, 0x60, +- &budget_av->budget.i2c_adap, +- DVB_PLL_PHILIPS_SD1878_TDA8261); +- } +- break; +- +- case SUBID_DVBS_TYPHOON: +- fe = dvb_attach(stv0299_attach, &typhoon_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; +- } +- break; +- case SUBID_DVBS2_KNC1: +- case SUBID_DVBS2_KNC1_OEM: +- case SUBID_DVBS2_EASYWATCH: +- budget_av->reinitialise_demod = 1; +- if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) +- dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); +- +- break; +- case SUBID_DVBS_CINERGY1200: +- fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; +- } +- break; +- +- case SUBID_DVBC_KNC1: +- case SUBID_DVBC_KNC1_PLUS: +- case SUBID_DVBC_CINERGY1200: +- case SUBID_DVBC_EASYWATCH: +- budget_av->reinitialise_demod = 1; +- budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; +- fe = dvb_attach(tda10021_attach, &philips_cu1216_config, +- &budget_av->budget.i2c_adap, +- read_pwm(budget_av)); +- if (fe == NULL) +- fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, +- &budget_av->budget.i2c_adap, +- read_pwm(budget_av)); +- if (fe) { +- fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; +- } +- break; +- +- case SUBID_DVBC_EASYWATCH_MK3: +- case SUBID_DVBC_CINERGY1200_MK3: +- case SUBID_DVBC_KNC1_MK3: +- case SUBID_DVBC_KNC1_TDA10024: +- case SUBID_DVBC_KNC1_PLUS_MK3: +- budget_av->reinitialise_demod = 1; +- budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; +- fe = dvb_attach(tda10023_attach, +- &philips_cu1216_tda10023_config, +- &budget_av->budget.i2c_adap, +- read_pwm(budget_av)); +- if (fe) { +- fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; +- } +- break; +- +- case SUBID_DVBT_EASYWATCH: +- case SUBID_DVBT_KNC1: +- case SUBID_DVBT_KNC1_PLUS: +- case SUBID_DVBT_CINERGY1200: +- budget_av->reinitialise_demod = 1; +- fe = dvb_attach(tda10046_attach, &philips_tu1216_config, +- &budget_av->budget.i2c_adap); +- if (fe) { +- fe->ops.tuner_ops.init = philips_tu1216_tuner_init; +- fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; +- } +- break; +- } +- +- if (fe == NULL) { +- pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- saa->pci->vendor, +- saa->pci->device, +- saa->pci->subsystem_vendor, +- saa->pci->subsystem_device); +- return; +- } +- +- budget_av->budget.dvb_frontend = fe; +- +- if (dvb_register_frontend(&budget_av->budget.dvb_adapter, +- budget_av->budget.dvb_frontend)) { +- pr_err("Frontend registration failed!\n"); +- dvb_frontend_detach(budget_av->budget.dvb_frontend); +- budget_av->budget.dvb_frontend = NULL; +- } +-} +- +- +-static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) +-{ +- struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; +- +- dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); +- +- if (*isr & MASK_10) +- ttpci_budget_irq10_handler(dev, isr); +-} +- +-static int budget_av_detach(struct saa7146_dev *dev) +-{ +- struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; +- int err; +- +- dprintk(2, "dev: %p\n", dev); +- +- if (1 == budget_av->has_saa7113) { +- saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); +- +- msleep(200); +- +- saa7146_unregister_device(&budget_av->vd, dev); +- +- saa7146_vv_release(dev); +- } +- +- if (budget_av->budget.ci_present) +- ciintf_deinit(budget_av); +- +- if (budget_av->budget.dvb_frontend != NULL) { +- dvb_unregister_frontend(budget_av->budget.dvb_frontend); +- dvb_frontend_detach(budget_av->budget.dvb_frontend); +- } +- err = ttpci_budget_deinit(&budget_av->budget); +- +- kfree(budget_av); +- +- return err; +-} +- +-#define KNC1_INPUTS 2 +-static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { +- { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, +- V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, +- { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, +- V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, +-}; +- +-static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +-{ +- dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); +- if (i->index >= KNC1_INPUTS) +- return -EINVAL; +- memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); +- return 0; +-} +- +-static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; +- +- *i = budget_av->cur_input; +- +- dprintk(1, "VIDIOC_G_INPUT %d\n", *i); +- return 0; +-} +- +-static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +-{ +- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; +- struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; +- +- dprintk(1, "VIDIOC_S_INPUT %d\n", input); +- return saa7113_setinput(budget_av, input); +-} +- +-static struct saa7146_ext_vv vv_data; +- +-static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +-{ +- struct budget_av *budget_av; +- u8 *mac; +- int err; +- +- dprintk(2, "dev: %p\n", dev); +- +- if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) +- return -ENOMEM; +- +- budget_av->has_saa7113 = 0; +- budget_av->budget.ci_present = 0; +- +- dev->ext_priv = budget_av; +- +- err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, +- adapter_nr); +- if (err) { +- kfree(budget_av); +- return err; +- } +- +- /* knc1 initialization */ +- saa7146_write(dev, DD1_STREAM_B, 0x04000000); +- saa7146_write(dev, DD1_INIT, 0x07000600); +- saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); +- +- if (saa7113_init(budget_av) == 0) { +- budget_av->has_saa7113 = 1; +- +- if (0 != saa7146_vv_init(dev, &vv_data)) { +- /* fixme: proper cleanup here */ +- ERR("cannot init vv subsystem\n"); +- return err; +- } +- vv_data.ops.vidioc_enum_input = vidioc_enum_input; +- vv_data.ops.vidioc_g_input = vidioc_g_input; +- vv_data.ops.vidioc_s_input = vidioc_s_input; +- +- if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { +- /* fixme: proper cleanup here */ +- ERR("cannot register capture v4l2 device\n"); +- saa7146_vv_release(dev); +- return err; +- } +- +- /* beware: this modifies dev->vv ... */ +- saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, +- SAA7146_HPS_SYNC_PORT_A); +- +- saa7113_setinput(budget_av, 0); +- } +- +- /* fixme: find some sane values here... */ +- saa7146_write(dev, PCI_BT_V1, 0x1c00101f); +- +- mac = budget_av->budget.dvb_adapter.proposed_mac; +- if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { +- pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", +- budget_av->budget.dvb_adapter.num); +- memset(mac, 0, 6); +- } else { +- pr_info("KNC1-%d: MAC addr = %pM\n", +- budget_av->budget.dvb_adapter.num, mac); +- } +- +- budget_av->budget.dvb_adapter.priv = budget_av; +- frontend_init(budget_av); +- ciintf_init(budget_av); +- +- ttpci_budget_init_hooks(&budget_av->budget); +- +- return 0; +-} +- +-static struct saa7146_standard standard[] = { +- {.name = "PAL",.id = V4L2_STD_PAL, +- .v_offset = 0x17,.v_field = 288, +- .h_offset = 0x14,.h_pixels = 680, +- .v_max_out = 576,.h_max_out = 768 }, +- +- {.name = "NTSC",.id = V4L2_STD_NTSC, +- .v_offset = 0x16,.v_field = 240, +- .h_offset = 0x06,.h_pixels = 708, +- .v_max_out = 480,.h_max_out = 640, }, +-}; +- +-static struct saa7146_ext_vv vv_data = { +- .inputs = 2, +- .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 +- .flags = 0, +- .stds = &standard[0], +- .num_stds = ARRAY_SIZE(standard), +-}; +- +-static struct saa7146_extension budget_extension; +- +-MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); +-MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); +-MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); +-MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); +-MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); +-MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); +-MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); +-MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +-MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); +-MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +-MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); +-MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); +-MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); +-MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); +-MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +-MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +-MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); +-MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); +-MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); +-MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +-MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); +-MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +-MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); +-MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); +- +-static struct pci_device_id pci_tbl[] = { +- MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), +- MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), +- MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), +- MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), +- MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), +- MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), +- MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), +- MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), +- MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), +- MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), +- MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), +- MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), +- MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), +- MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), +- MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), +- MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), +- MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), +- MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), +- MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), +- MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), +- MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), +- MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), +- MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), +- MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), +- MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), +- MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), +- MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), +- MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), +- MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), +- { +- .vendor = 0, +- } +-}; +- +-MODULE_DEVICE_TABLE(pci, pci_tbl); +- +-static struct saa7146_extension budget_extension = { +- .name = "budget_av", +- .flags = SAA7146_USE_I2C_IRQ, +- +- .pci_tbl = pci_tbl, +- +- .module = THIS_MODULE, +- .attach = budget_av_attach, +- .detach = budget_av_detach, +- +- .irq_mask = MASK_10, +- .irq_func = budget_av_irq, +-}; +- +-static int __init budget_av_init(void) +-{ +- return saa7146_register_extension(&budget_extension); +-} +- +-static void __exit budget_av_exit(void) +-{ +- saa7146_unregister_extension(&budget_extension); +-} +- +-module_init(budget_av_init); +-module_exit(budget_av_exit); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +-MODULE_DESCRIPTION("driver for the SAA7146 based so-called " +- "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); +diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c +deleted file mode 100644 +index 98e5241..0000000 +--- a/drivers/media/dvb/ttpci/budget-ci.c ++++ /dev/null +@@ -1,1591 +0,0 @@ +-/* +- * budget-ci.c: driver for the SAA7146 based Budget DVB cards +- * +- * Compiled from various sources by Michael Hunold +- * +- * msp430 IR support contributed by Jack Thomasson +- * partially based on the Siemens DVB driver by Ralph+Marcus Metzler +- * +- * CI interface support (c) 2004 Andrew de Quincey +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "budget.h" +- +-#include "dvb_ca_en50221.h" +-#include "stv0299.h" +-#include "stv0297.h" +-#include "tda1004x.h" +-#include "stb0899_drv.h" +-#include "stb0899_reg.h" +-#include "stb0899_cfg.h" +-#include "stb6100.h" +-#include "stb6100_cfg.h" +-#include "lnbp21.h" +-#include "bsbe1.h" +-#include "bsru6.h" +-#include "tda1002x.h" +-#include "tda827x.h" +-#include "bsbe1-d01a.h" +- +-#define MODULE_NAME "budget_ci" +- +-/* +- * Regarding DEBIADDR_IR: +- * Some CI modules hang if random addresses are read. +- * Using address 0x4000 for the IR read means that we +- * use the same address as for CI version, which should +- * be a safe default. +- */ +-#define DEBIADDR_IR 0x4000 +-#define DEBIADDR_CICONTROL 0x0000 +-#define DEBIADDR_CIVERSION 0x4000 +-#define DEBIADDR_IO 0x1000 +-#define DEBIADDR_ATTR 0x3000 +- +-#define CICONTROL_RESET 0x01 +-#define CICONTROL_ENABLETS 0x02 +-#define CICONTROL_CAMDETECT 0x08 +- +-#define DEBICICTL 0x00420000 +-#define DEBICICAM 0x02420000 +- +-#define SLOTSTATUS_NONE 1 +-#define SLOTSTATUS_PRESENT 2 +-#define SLOTSTATUS_RESET 4 +-#define SLOTSTATUS_READY 8 +-#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +- +-/* RC5 device wildcard */ +-#define IR_DEVICE_ANY 255 +- +-static int rc5_device = -1; +-module_param(rc5_device, int, 0644); +-MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); +- +-static int ir_debug; +-module_param(ir_debug, int, 0644); +-MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-struct budget_ci_ir { +- struct rc_dev *dev; +- struct tasklet_struct msp430_irq_tasklet; +- char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ +- char phys[32]; +- int rc5_device; +- u32 ir_key; +- bool have_command; +- bool full_rc5; /* Outputs a full RC5 code */ +-}; +- +-struct budget_ci { +- struct budget budget; +- struct tasklet_struct ciintf_irq_tasklet; +- int slot_status; +- int ci_irq; +- struct dvb_ca_en50221 ca; +- struct budget_ci_ir ir; +- u8 tuner_pll_address; /* used for philips_tdm1316l configs */ +-}; +- +-static void msp430_ir_interrupt(unsigned long data) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) data; +- struct rc_dev *dev = budget_ci->ir.dev; +- u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; +- +- /* +- * The msp430 chip can generate two different bytes, command and device +- * +- * type1: X1CCCCCC, C = command bits (0 - 63) +- * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit +- * +- * Each signal from the remote control can generate one or more command +- * bytes and one or more device bytes. For the repeated bytes, the +- * highest bit (X) is set. The first command byte is always generated +- * before the first device byte. Other than that, no specific order +- * seems to apply. To make life interesting, bytes can also be lost. +- * +- * Only when we have a command and device byte, a keypress is +- * generated. +- */ +- +- if (ir_debug) +- printk("budget_ci: received byte 0x%02x\n", command); +- +- /* Remove repeat bit, we use every command */ +- command = command & 0x7f; +- +- /* Is this a RC5 command byte? */ +- if (command & 0x40) { +- budget_ci->ir.have_command = true; +- budget_ci->ir.ir_key = command & 0x3f; +- return; +- } +- +- /* It's a RC5 device byte */ +- if (!budget_ci->ir.have_command) +- return; +- budget_ci->ir.have_command = false; +- +- if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && +- budget_ci->ir.rc5_device != (command & 0x1f)) +- return; +- +- if (budget_ci->ir.full_rc5) { +- rc_keydown(dev, +- budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, +- (command & 0x20) ? 1 : 0); +- return; +- } +- +- /* FIXME: We should generate complete scancodes for all devices */ +- rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); +-} +- +-static int msp430_ir_init(struct budget_ci *budget_ci) +-{ +- struct saa7146_dev *saa = budget_ci->budget.dev; +- struct rc_dev *dev; +- int error; +- +- dev = rc_allocate_device(); +- if (!dev) { +- printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); +- return -ENOMEM; +- } +- +- snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), +- "Budget-CI dvb ir receiver %s", saa->name); +- snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), +- "pci-%s/ir0", pci_name(saa->pci)); +- +- dev->driver_name = MODULE_NAME; +- dev->input_name = budget_ci->ir.name; +- dev->input_phys = budget_ci->ir.phys; +- dev->input_id.bustype = BUS_PCI; +- dev->input_id.version = 1; +- if (saa->pci->subsystem_vendor) { +- dev->input_id.vendor = saa->pci->subsystem_vendor; +- dev->input_id.product = saa->pci->subsystem_device; +- } else { +- dev->input_id.vendor = saa->pci->vendor; +- dev->input_id.product = saa->pci->device; +- } +- dev->dev.parent = &saa->pci->dev; +- +- if (rc5_device < 0) +- budget_ci->ir.rc5_device = IR_DEVICE_ANY; +- else +- budget_ci->ir.rc5_device = rc5_device; +- +- /* Select keymap and address */ +- switch (budget_ci->budget.dev->pci->subsystem_device) { +- case 0x100c: +- case 0x100f: +- case 0x1011: +- case 0x1012: +- /* The hauppauge keymap is a superset of these remotes */ +- dev->map_name = RC_MAP_HAUPPAUGE; +- budget_ci->ir.full_rc5 = true; +- +- if (rc5_device < 0) +- budget_ci->ir.rc5_device = 0x1f; +- break; +- case 0x1010: +- case 0x1017: +- case 0x1019: +- case 0x101a: +- case 0x101b: +- /* for the Technotrend 1500 bundled remote */ +- dev->map_name = RC_MAP_TT_1500; +- break; +- default: +- /* unknown remote */ +- dev->map_name = RC_MAP_BUDGET_CI_OLD; +- break; +- } +- if (!budget_ci->ir.full_rc5) +- dev->scanmask = 0xff; +- +- error = rc_register_device(dev); +- if (error) { +- printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); +- rc_free_device(dev); +- return error; +- } +- +- budget_ci->ir.dev = dev; +- +- tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, +- (unsigned long) budget_ci); +- +- SAA7146_IER_ENABLE(saa, MASK_06); +- saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); +- +- return 0; +-} +- +-static void msp430_ir_deinit(struct budget_ci *budget_ci) +-{ +- struct saa7146_dev *saa = budget_ci->budget.dev; +- +- SAA7146_IER_DISABLE(saa, MASK_06); +- saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); +- tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); +- +- rc_unregister_device(budget_ci->ir.dev); +-} +- +-static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- +- if (slot != 0) +- return -EINVAL; +- +- return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, +- DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); +-} +- +-static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- +- if (slot != 0) +- return -EINVAL; +- +- return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, +- DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); +-} +- +-static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- +- if (slot != 0) +- return -EINVAL; +- +- return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, +- DEBIADDR_IO | (address & 3), 1, 1, 0); +-} +- +-static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- +- if (slot != 0) +- return -EINVAL; +- +- return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, +- DEBIADDR_IO | (address & 3), 1, value, 1, 0); +-} +- +-static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- struct saa7146_dev *saa = budget_ci->budget.dev; +- +- if (slot != 0) +- return -EINVAL; +- +- if (budget_ci->ci_irq) { +- // trigger on RISING edge during reset so we know when READY is re-asserted +- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); +- } +- budget_ci->slot_status = SLOTSTATUS_RESET; +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); +- msleep(1); +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, +- CICONTROL_RESET, 1, 0); +- +- saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); +- return 0; +-} +- +-static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- struct saa7146_dev *saa = budget_ci->budget.dev; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); +- return 0; +-} +- +-static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- struct saa7146_dev *saa = budget_ci->budget.dev; +- int tmp; +- +- if (slot != 0) +- return -EINVAL; +- +- saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); +- +- tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, +- tmp | CICONTROL_ENABLETS, 1, 0); +- +- ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); +- return 0; +-} +- +-static void ciintf_interrupt(unsigned long data) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) data; +- struct saa7146_dev *saa = budget_ci->budget.dev; +- unsigned int flags; +- +- // ensure we don't get spurious IRQs during initialisation +- if (!budget_ci->budget.ci_present) +- return; +- +- // read the CAM status +- flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); +- if (flags & CICONTROL_CAMDETECT) { +- +- // GPIO should be set to trigger on falling edge if a CAM is present +- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); +- +- if (budget_ci->slot_status & SLOTSTATUS_NONE) { +- // CAM insertion IRQ +- budget_ci->slot_status = SLOTSTATUS_PRESENT; +- dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, +- DVB_CA_EN50221_CAMCHANGE_INSERTED); +- +- } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { +- // CAM ready (reset completed) +- budget_ci->slot_status = SLOTSTATUS_READY; +- dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); +- +- } else if (budget_ci->slot_status & SLOTSTATUS_READY) { +- // FR/DA IRQ +- dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); +- } +- } else { +- +- // trigger on rising edge if a CAM is not present - when a CAM is inserted, we +- // only want to get the IRQ when it sets READY. If we trigger on the falling edge, +- // the CAM might not actually be ready yet. +- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); +- +- // generate a CAM removal IRQ if we haven't already +- if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { +- // CAM removal IRQ +- budget_ci->slot_status = SLOTSTATUS_NONE; +- dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, +- DVB_CA_EN50221_CAMCHANGE_REMOVED); +- } +- } +-} +- +-static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) ca->data; +- unsigned int flags; +- +- // ensure we don't get spurious IRQs during initialisation +- if (!budget_ci->budget.ci_present) +- return -EINVAL; +- +- // read the CAM status +- flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); +- if (flags & CICONTROL_CAMDETECT) { +- // mark it as present if it wasn't before +- if (budget_ci->slot_status & SLOTSTATUS_NONE) { +- budget_ci->slot_status = SLOTSTATUS_PRESENT; +- } +- +- // during a RESET, we check if we can read from IO memory to see when CAM is ready +- if (budget_ci->slot_status & SLOTSTATUS_RESET) { +- if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { +- budget_ci->slot_status = SLOTSTATUS_READY; +- } +- } +- } else { +- budget_ci->slot_status = SLOTSTATUS_NONE; +- } +- +- if (budget_ci->slot_status != SLOTSTATUS_NONE) { +- if (budget_ci->slot_status & SLOTSTATUS_READY) { +- return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; +- } +- return DVB_CA_EN50221_POLL_CAM_PRESENT; +- } +- +- return 0; +-} +- +-static int ciintf_init(struct budget_ci *budget_ci) +-{ +- struct saa7146_dev *saa = budget_ci->budget.dev; +- int flags; +- int result; +- int ci_version; +- int ca_flags; +- +- memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); +- +- // enable DEBI pins +- saa7146_write(saa, MC1, MASK_27 | MASK_11); +- +- // test if it is there +- ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); +- if ((ci_version & 0xa0) != 0xa0) { +- result = -ENODEV; +- goto error; +- } +- +- // determine whether a CAM is present or not +- flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); +- budget_ci->slot_status = SLOTSTATUS_NONE; +- if (flags & CICONTROL_CAMDETECT) +- budget_ci->slot_status = SLOTSTATUS_PRESENT; +- +- // version 0xa2 of the CI firmware doesn't generate interrupts +- if (ci_version == 0xa2) { +- ca_flags = 0; +- budget_ci->ci_irq = 0; +- } else { +- ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | +- DVB_CA_EN50221_FLAG_IRQ_FR | +- DVB_CA_EN50221_FLAG_IRQ_DA; +- budget_ci->ci_irq = 1; +- } +- +- // register CI interface +- budget_ci->ca.owner = THIS_MODULE; +- budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; +- budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; +- budget_ci->ca.read_cam_control = ciintf_read_cam_control; +- budget_ci->ca.write_cam_control = ciintf_write_cam_control; +- budget_ci->ca.slot_reset = ciintf_slot_reset; +- budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; +- budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; +- budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; +- budget_ci->ca.data = budget_ci; +- if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, +- &budget_ci->ca, +- ca_flags, 1)) != 0) { +- printk("budget_ci: CI interface detected, but initialisation failed.\n"); +- goto error; +- } +- +- // Setup CI slot IRQ +- if (budget_ci->ci_irq) { +- tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); +- if (budget_ci->slot_status != SLOTSTATUS_NONE) { +- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); +- } else { +- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); +- } +- SAA7146_IER_ENABLE(saa, MASK_03); +- } +- +- // enable interface +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, +- CICONTROL_RESET, 1, 0); +- +- // success! +- printk("budget_ci: CI interface initialised\n"); +- budget_ci->budget.ci_present = 1; +- +- // forge a fake CI IRQ so the CAM state is setup correctly +- if (budget_ci->ci_irq) { +- flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; +- if (budget_ci->slot_status != SLOTSTATUS_NONE) +- flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; +- dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); +- } +- +- return 0; +- +-error: +- saa7146_write(saa, MC1, MASK_27); +- return result; +-} +- +-static void ciintf_deinit(struct budget_ci *budget_ci) +-{ +- struct saa7146_dev *saa = budget_ci->budget.dev; +- +- // disable CI interrupts +- if (budget_ci->ci_irq) { +- SAA7146_IER_DISABLE(saa, MASK_03); +- saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); +- tasklet_kill(&budget_ci->ciintf_irq_tasklet); +- } +- +- // reset interface +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); +- msleep(1); +- ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, +- CICONTROL_RESET, 1, 0); +- +- // disable TS data stream to CI interface +- saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); +- +- // release the CA device +- dvb_ca_en50221_release(&budget_ci->ca); +- +- // disable DEBI pins +- saa7146_write(saa, MC1, MASK_27); +-} +- +-static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; +- +- dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); +- +- if (*isr & MASK_06) +- tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); +- +- if (*isr & MASK_10) +- ttpci_budget_irq10_handler(dev, isr); +- +- if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) +- tasklet_schedule(&budget_ci->ciintf_irq_tasklet); +-} +- +-static u8 philips_su1278_tt_inittab[] = { +- 0x01, 0x0f, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x5b, +- 0x05, 0x85, +- 0x06, 0x02, +- 0x07, 0x00, +- 0x08, 0x02, +- 0x09, 0x00, +- 0x0C, 0x01, +- 0x0D, 0x81, +- 0x0E, 0x44, +- 0x0f, 0x14, +- 0x10, 0x3c, +- 0x11, 0x84, +- 0x12, 0xda, +- 0x13, 0x97, +- 0x14, 0x95, +- 0x15, 0xc9, +- 0x16, 0x19, +- 0x17, 0x8c, +- 0x18, 0x59, +- 0x19, 0xf8, +- 0x1a, 0xfe, +- 0x1c, 0x7f, +- 0x1d, 0x00, +- 0x1e, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, +- 0x29, 0x28, +- 0x2a, 0x14, +- 0x2b, 0x0f, +- 0x2c, 0x09, +- 0x2d, 0x09, +- 0x31, 0x1f, +- 0x32, 0x19, +- 0x33, 0xfc, +- 0x34, 0x93, +- 0xff, 0xff +-}; +- +-static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) +-{ +- stv0299_writereg(fe, 0x0e, 0x44); +- if (srate >= 10000000) { +- stv0299_writereg(fe, 0x13, 0x97); +- stv0299_writereg(fe, 0x14, 0x95); +- stv0299_writereg(fe, 0x15, 0xc9); +- stv0299_writereg(fe, 0x17, 0x8c); +- stv0299_writereg(fe, 0x1a, 0xfe); +- stv0299_writereg(fe, 0x1c, 0x7f); +- stv0299_writereg(fe, 0x2d, 0x09); +- } else { +- stv0299_writereg(fe, 0x13, 0x99); +- stv0299_writereg(fe, 0x14, 0x8d); +- stv0299_writereg(fe, 0x15, 0xce); +- stv0299_writereg(fe, 0x17, 0x43); +- stv0299_writereg(fe, 0x1a, 0x1d); +- stv0299_writereg(fe, 0x1c, 0x12); +- stv0299_writereg(fe, 0x2d, 0x05); +- } +- stv0299_writereg(fe, 0x0e, 0x23); +- stv0299_writereg(fe, 0x0f, 0x94); +- stv0299_writereg(fe, 0x10, 0x39); +- stv0299_writereg(fe, 0x15, 0xc9); +- +- stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); +- stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); +- stv0299_writereg(fe, 0x21, (ratio) & 0xf0); +- +- return 0; +-} +- +-static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; +- u32 div; +- u8 buf[4]; +- struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; +- +- if ((p->frequency < 950000) || (p->frequency > 2150000)) +- return -EINVAL; +- +- div = (p->frequency + (500 - 1)) / 500; /* round correctly */ +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; +- buf[3] = 0x20; +- +- if (p->symbol_rate < 4000000) +- buf[3] |= 1; +- +- if (p->frequency < 1250000) +- buf[3] |= 0; +- else if (p->frequency < 1550000) +- buf[3] |= 0x40; +- else if (p->frequency < 2050000) +- buf[3] |= 0x80; +- else if (p->frequency < 2150000) +- buf[3] |= 0xC0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct stv0299_config philips_su1278_tt_config = { +- +- .demod_address = 0x68, +- .inittab = philips_su1278_tt_inittab, +- .mclk = 64000000UL, +- .invert = 0, +- .skip_reinit = 1, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 50, +- .set_symbol_rate = philips_su1278_tt_set_symbol_rate, +-}; +- +- +- +-static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; +- static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; +- static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; +- struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = +- sizeof(td1316_init) }; +- +- // setup PLL configuration +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- msleep(1); +- +- // disable the mc44BC374c (do not check for errors) +- tuner_msg.addr = 0x65; +- tuner_msg.buf = disable_mc44BC374c; +- tuner_msg.len = sizeof(disable_mc44BC374c); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); +- } +- +- return 0; +-} +- +-static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; +- u8 tuner_buf[4]; +- struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; +- int tuner_frequency = 0; +- u8 band, cp, filter; +- +- // determine charge pump +- tuner_frequency = p->frequency + 36130000; +- if (tuner_frequency < 87000000) +- return -EINVAL; +- else if (tuner_frequency < 130000000) +- cp = 3; +- else if (tuner_frequency < 160000000) +- cp = 5; +- else if (tuner_frequency < 200000000) +- cp = 6; +- else if (tuner_frequency < 290000000) +- cp = 3; +- else if (tuner_frequency < 420000000) +- cp = 5; +- else if (tuner_frequency < 480000000) +- cp = 6; +- else if (tuner_frequency < 620000000) +- cp = 3; +- else if (tuner_frequency < 830000000) +- cp = 5; +- else if (tuner_frequency < 895000000) +- cp = 7; +- else +- return -EINVAL; +- +- // determine band +- if (p->frequency < 49000000) +- return -EINVAL; +- else if (p->frequency < 159000000) +- band = 1; +- else if (p->frequency < 444000000) +- band = 2; +- else if (p->frequency < 861000000) +- band = 4; +- else +- return -EINVAL; +- +- // setup PLL filter and TDA9889 +- switch (p->bandwidth_hz) { +- case 6000000: +- tda1004x_writereg(fe, 0x0C, 0x14); +- filter = 0; +- break; +- +- case 7000000: +- tda1004x_writereg(fe, 0x0C, 0x80); +- filter = 0; +- break; +- +- case 8000000: +- tda1004x_writereg(fe, 0x0C, 0x14); +- filter = 1; +- break; +- +- default: +- return -EINVAL; +- } +- +- // calculate divisor +- // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) +- tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; +- +- // setup tuner buffer +- tuner_buf[0] = tuner_frequency >> 8; +- tuner_buf[1] = tuner_frequency & 0xff; +- tuner_buf[2] = 0xca; +- tuner_buf[3] = (cp << 5) | (filter << 3) | band; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- +- msleep(1); +- return 0; +-} +- +-static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, +- const struct firmware **fw, char *name) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; +- +- return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); +-} +- +-static struct tda1004x_config philips_tdm1316l_config = { +- +- .demod_address = 0x8, +- .invert = 0, +- .invert_oclk = 0, +- .xtal_freq = TDA10046_XTAL_4M, +- .agc_config = TDA10046_AGC_DEFAULT, +- .if_freq = TDA10046_FREQ_3617, +- .request_firmware = philips_tdm1316l_request_firmware, +-}; +- +-static struct tda1004x_config philips_tdm1316l_config_invert = { +- +- .demod_address = 0x8, +- .invert = 1, +- .invert_oclk = 0, +- .xtal_freq = TDA10046_XTAL_4M, +- .agc_config = TDA10046_AGC_DEFAULT, +- .if_freq = TDA10046_FREQ_3617, +- .request_firmware = philips_tdm1316l_request_firmware, +-}; +- +-static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; +- u8 tuner_buf[5]; +- struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, +- .flags = 0, +- .buf = tuner_buf, +- .len = sizeof(tuner_buf) }; +- int tuner_frequency = 0; +- u8 band, cp, filter; +- +- // determine charge pump +- tuner_frequency = p->frequency + 36125000; +- if (tuner_frequency < 87000000) +- return -EINVAL; +- else if (tuner_frequency < 130000000) { +- cp = 3; +- band = 1; +- } else if (tuner_frequency < 160000000) { +- cp = 5; +- band = 1; +- } else if (tuner_frequency < 200000000) { +- cp = 6; +- band = 1; +- } else if (tuner_frequency < 290000000) { +- cp = 3; +- band = 2; +- } else if (tuner_frequency < 420000000) { +- cp = 5; +- band = 2; +- } else if (tuner_frequency < 480000000) { +- cp = 6; +- band = 2; +- } else if (tuner_frequency < 620000000) { +- cp = 3; +- band = 4; +- } else if (tuner_frequency < 830000000) { +- cp = 5; +- band = 4; +- } else if (tuner_frequency < 895000000) { +- cp = 7; +- band = 4; +- } else +- return -EINVAL; +- +- // assume PLL filter should always be 8MHz for the moment. +- filter = 1; +- +- // calculate divisor +- tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; +- +- // setup tuner buffer +- tuner_buf[0] = tuner_frequency >> 8; +- tuner_buf[1] = tuner_frequency & 0xff; +- tuner_buf[2] = 0xc8; +- tuner_buf[3] = (cp << 5) | (filter << 3) | band; +- tuner_buf[4] = 0x80; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- +- msleep(50); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- +- msleep(1); +- +- return 0; +-} +- +-static u8 dvbc_philips_tdm1316l_inittab[] = { +- 0x80, 0x01, +- 0x80, 0x00, +- 0x81, 0x01, +- 0x81, 0x00, +- 0x00, 0x09, +- 0x01, 0x69, +- 0x03, 0x00, +- 0x04, 0x00, +- 0x07, 0x00, +- 0x08, 0x00, +- 0x20, 0x00, +- 0x21, 0x40, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x24, 0x40, +- 0x25, 0x88, +- 0x30, 0xff, +- 0x31, 0x00, +- 0x32, 0xff, +- 0x33, 0x00, +- 0x34, 0x50, +- 0x35, 0x7f, +- 0x36, 0x00, +- 0x37, 0x20, +- 0x38, 0x00, +- 0x40, 0x1c, +- 0x41, 0xff, +- 0x42, 0x29, +- 0x43, 0x20, +- 0x44, 0xff, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x49, 0x04, +- 0x4a, 0x00, +- 0x4b, 0x7b, +- 0x52, 0x30, +- 0x55, 0xae, +- 0x56, 0x47, +- 0x57, 0xe1, +- 0x58, 0x3a, +- 0x5a, 0x1e, +- 0x5b, 0x34, +- 0x60, 0x00, +- 0x63, 0x00, +- 0x64, 0x00, +- 0x65, 0x00, +- 0x66, 0x00, +- 0x67, 0x00, +- 0x68, 0x00, +- 0x69, 0x00, +- 0x6a, 0x02, +- 0x6b, 0x00, +- 0x70, 0xff, +- 0x71, 0x00, +- 0x72, 0x00, +- 0x73, 0x00, +- 0x74, 0x0c, +- 0x80, 0x00, +- 0x81, 0x00, +- 0x82, 0x00, +- 0x83, 0x00, +- 0x84, 0x04, +- 0x85, 0x80, +- 0x86, 0x24, +- 0x87, 0x78, +- 0x88, 0x10, +- 0x89, 0x00, +- 0x90, 0x01, +- 0x91, 0x01, +- 0xa0, 0x04, +- 0xa1, 0x00, +- 0xa2, 0x00, +- 0xb0, 0x91, +- 0xb1, 0x0b, +- 0xc0, 0x53, +- 0xc1, 0x70, +- 0xc2, 0x12, +- 0xd0, 0x00, +- 0xd1, 0x00, +- 0xd2, 0x00, +- 0xd3, 0x00, +- 0xd4, 0x00, +- 0xd5, 0x00, +- 0xde, 0x00, +- 0xdf, 0x00, +- 0x61, 0x38, +- 0x62, 0x0a, +- 0x53, 0x13, +- 0x59, 0x08, +- 0xff, 0xff, +-}; +- +-static struct stv0297_config dvbc_philips_tdm1316l_config = { +- .demod_address = 0x1c, +- .inittab = dvbc_philips_tdm1316l_inittab, +- .invert = 0, +- .stop_during_read = 1, +-}; +- +-static struct tda10023_config tda10023_config = { +- .demod_address = 0xc, +- .invert = 0, +- .xtal = 16000000, +- .pll_m = 11, +- .pll_p = 3, +- .pll_n = 1, +- .deltaf = 0xa511, +-}; +- +-static struct tda827x_config tda827x_config = { +- .config = 0, +-}; +- +-/* TT S2-3200 DVB-S (STB0899) Inittab */ +-static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { +- +- { STB0899_DEV_ID , 0x81 }, +- { STB0899_DISCNTRL1 , 0x32 }, +- { STB0899_DISCNTRL2 , 0x80 }, +- { STB0899_DISRX_ST0 , 0x04 }, +- { STB0899_DISRX_ST1 , 0x00 }, +- { STB0899_DISPARITY , 0x00 }, +- { STB0899_DISSTATUS , 0x20 }, +- { STB0899_DISF22 , 0x8c }, +- { STB0899_DISF22RX , 0x9a }, +- { STB0899_SYSREG , 0x0b }, +- { STB0899_ACRPRESC , 0x11 }, +- { STB0899_ACRDIV1 , 0x0a }, +- { STB0899_ACRDIV2 , 0x05 }, +- { STB0899_DACR1 , 0x00 }, +- { STB0899_DACR2 , 0x00 }, +- { STB0899_OUTCFG , 0x00 }, +- { STB0899_MODECFG , 0x00 }, +- { STB0899_IRQSTATUS_3 , 0x30 }, +- { STB0899_IRQSTATUS_2 , 0x00 }, +- { STB0899_IRQSTATUS_1 , 0x00 }, +- { STB0899_IRQSTATUS_0 , 0x00 }, +- { STB0899_IRQMSK_3 , 0xf3 }, +- { STB0899_IRQMSK_2 , 0xfc }, +- { STB0899_IRQMSK_1 , 0xff }, +- { STB0899_IRQMSK_0 , 0xff }, +- { STB0899_IRQCFG , 0x00 }, +- { STB0899_I2CCFG , 0x88 }, +- { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ +- { STB0899_IOPVALUE5 , 0x00 }, +- { STB0899_IOPVALUE4 , 0x20 }, +- { STB0899_IOPVALUE3 , 0xc9 }, +- { STB0899_IOPVALUE2 , 0x90 }, +- { STB0899_IOPVALUE1 , 0x40 }, +- { STB0899_IOPVALUE0 , 0x00 }, +- { STB0899_GPIO00CFG , 0x82 }, +- { STB0899_GPIO01CFG , 0x82 }, +- { STB0899_GPIO02CFG , 0x82 }, +- { STB0899_GPIO03CFG , 0x82 }, +- { STB0899_GPIO04CFG , 0x82 }, +- { STB0899_GPIO05CFG , 0x82 }, +- { STB0899_GPIO06CFG , 0x82 }, +- { STB0899_GPIO07CFG , 0x82 }, +- { STB0899_GPIO08CFG , 0x82 }, +- { STB0899_GPIO09CFG , 0x82 }, +- { STB0899_GPIO10CFG , 0x82 }, +- { STB0899_GPIO11CFG , 0x82 }, +- { STB0899_GPIO12CFG , 0x82 }, +- { STB0899_GPIO13CFG , 0x82 }, +- { STB0899_GPIO14CFG , 0x82 }, +- { STB0899_GPIO15CFG , 0x82 }, +- { STB0899_GPIO16CFG , 0x82 }, +- { STB0899_GPIO17CFG , 0x82 }, +- { STB0899_GPIO18CFG , 0x82 }, +- { STB0899_GPIO19CFG , 0x82 }, +- { STB0899_GPIO20CFG , 0x82 }, +- { STB0899_SDATCFG , 0xb8 }, +- { STB0899_SCLTCFG , 0xba }, +- { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ +- { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ +- { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ +- { STB0899_DIRCLKCFG , 0x82 }, +- { STB0899_CLKOUT27CFG , 0x7e }, +- { STB0899_STDBYCFG , 0x82 }, +- { STB0899_CS0CFG , 0x82 }, +- { STB0899_CS1CFG , 0x82 }, +- { STB0899_DISEQCOCFG , 0x20 }, +- { STB0899_GPIO32CFG , 0x82 }, +- { STB0899_GPIO33CFG , 0x82 }, +- { STB0899_GPIO34CFG , 0x82 }, +- { STB0899_GPIO35CFG , 0x82 }, +- { STB0899_GPIO36CFG , 0x82 }, +- { STB0899_GPIO37CFG , 0x82 }, +- { STB0899_GPIO38CFG , 0x82 }, +- { STB0899_GPIO39CFG , 0x82 }, +- { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ +- { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ +- { STB0899_FILTCTRL , 0x00 }, +- { STB0899_SYSCTRL , 0x00 }, +- { STB0899_STOPCLK1 , 0x20 }, +- { STB0899_STOPCLK2 , 0x00 }, +- { STB0899_INTBUFSTATUS , 0x00 }, +- { STB0899_INTBUFCTRL , 0x0a }, +- { 0xffff , 0xff }, +-}; +- +-static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { +- { STB0899_DEMOD , 0x00 }, +- { STB0899_RCOMPC , 0xc9 }, +- { STB0899_AGC1CN , 0x41 }, +- { STB0899_AGC1REF , 0x10 }, +- { STB0899_RTC , 0x7a }, +- { STB0899_TMGCFG , 0x4e }, +- { STB0899_AGC2REF , 0x34 }, +- { STB0899_TLSR , 0x84 }, +- { STB0899_CFD , 0xc7 }, +- { STB0899_ACLC , 0x87 }, +- { STB0899_BCLC , 0x94 }, +- { STB0899_EQON , 0x41 }, +- { STB0899_LDT , 0xdd }, +- { STB0899_LDT2 , 0xc9 }, +- { STB0899_EQUALREF , 0xb4 }, +- { STB0899_TMGRAMP , 0x10 }, +- { STB0899_TMGTHD , 0x30 }, +- { STB0899_IDCCOMP , 0xfb }, +- { STB0899_QDCCOMP , 0x03 }, +- { STB0899_POWERI , 0x3b }, +- { STB0899_POWERQ , 0x3d }, +- { STB0899_RCOMP , 0x81 }, +- { STB0899_AGCIQIN , 0x80 }, +- { STB0899_AGC2I1 , 0x04 }, +- { STB0899_AGC2I2 , 0xf5 }, +- { STB0899_TLIR , 0x25 }, +- { STB0899_RTF , 0x80 }, +- { STB0899_DSTATUS , 0x00 }, +- { STB0899_LDI , 0xca }, +- { STB0899_CFRM , 0xf1 }, +- { STB0899_CFRL , 0xf3 }, +- { STB0899_NIRM , 0x2a }, +- { STB0899_NIRL , 0x05 }, +- { STB0899_ISYMB , 0x17 }, +- { STB0899_QSYMB , 0xfa }, +- { STB0899_SFRH , 0x2f }, +- { STB0899_SFRM , 0x68 }, +- { STB0899_SFRL , 0x40 }, +- { STB0899_SFRUPH , 0x2f }, +- { STB0899_SFRUPM , 0x68 }, +- { STB0899_SFRUPL , 0x40 }, +- { STB0899_EQUAI1 , 0xfd }, +- { STB0899_EQUAQ1 , 0x04 }, +- { STB0899_EQUAI2 , 0x0f }, +- { STB0899_EQUAQ2 , 0xff }, +- { STB0899_EQUAI3 , 0xdf }, +- { STB0899_EQUAQ3 , 0xfa }, +- { STB0899_EQUAI4 , 0x37 }, +- { STB0899_EQUAQ4 , 0x0d }, +- { STB0899_EQUAI5 , 0xbd }, +- { STB0899_EQUAQ5 , 0xf7 }, +- { STB0899_DSTATUS2 , 0x00 }, +- { STB0899_VSTATUS , 0x00 }, +- { STB0899_VERROR , 0xff }, +- { STB0899_IQSWAP , 0x2a }, +- { STB0899_ECNT1M , 0x00 }, +- { STB0899_ECNT1L , 0x00 }, +- { STB0899_ECNT2M , 0x00 }, +- { STB0899_ECNT2L , 0x00 }, +- { STB0899_ECNT3M , 0x00 }, +- { STB0899_ECNT3L , 0x00 }, +- { STB0899_FECAUTO1 , 0x06 }, +- { STB0899_FECM , 0x01 }, +- { STB0899_VTH12 , 0xf0 }, +- { STB0899_VTH23 , 0xa0 }, +- { STB0899_VTH34 , 0x78 }, +- { STB0899_VTH56 , 0x4e }, +- { STB0899_VTH67 , 0x48 }, +- { STB0899_VTH78 , 0x38 }, +- { STB0899_PRVIT , 0xff }, +- { STB0899_VITSYNC , 0x19 }, +- { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ +- { STB0899_TSULC , 0x42 }, +- { STB0899_RSLLC , 0x40 }, +- { STB0899_TSLPL , 0x12 }, +- { STB0899_TSCFGH , 0x0c }, +- { STB0899_TSCFGM , 0x00 }, +- { STB0899_TSCFGL , 0x0c }, +- { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ +- { STB0899_RSSYNCDEL , 0x00 }, +- { STB0899_TSINHDELH , 0x02 }, +- { STB0899_TSINHDELM , 0x00 }, +- { STB0899_TSINHDELL , 0x00 }, +- { STB0899_TSLLSTKM , 0x00 }, +- { STB0899_TSLLSTKL , 0x00 }, +- { STB0899_TSULSTKM , 0x00 }, +- { STB0899_TSULSTKL , 0xab }, +- { STB0899_PCKLENUL , 0x00 }, +- { STB0899_PCKLENLL , 0xcc }, +- { STB0899_RSPCKLEN , 0xcc }, +- { STB0899_TSSTATUS , 0x80 }, +- { STB0899_ERRCTRL1 , 0xb6 }, +- { STB0899_ERRCTRL2 , 0x96 }, +- { STB0899_ERRCTRL3 , 0x89 }, +- { STB0899_DMONMSK1 , 0x27 }, +- { STB0899_DMONMSK0 , 0x03 }, +- { STB0899_DEMAPVIT , 0x5c }, +- { STB0899_PLPARM , 0x1f }, +- { STB0899_PDELCTRL , 0x48 }, +- { STB0899_PDELCTRL2 , 0x00 }, +- { STB0899_BBHCTRL1 , 0x00 }, +- { STB0899_BBHCTRL2 , 0x00 }, +- { STB0899_HYSTTHRESH , 0x77 }, +- { STB0899_MATCSTM , 0x00 }, +- { STB0899_MATCSTL , 0x00 }, +- { STB0899_UPLCSTM , 0x00 }, +- { STB0899_UPLCSTL , 0x00 }, +- { STB0899_DFLCSTM , 0x00 }, +- { STB0899_DFLCSTL , 0x00 }, +- { STB0899_SYNCCST , 0x00 }, +- { STB0899_SYNCDCSTM , 0x00 }, +- { STB0899_SYNCDCSTL , 0x00 }, +- { STB0899_ISI_ENTRY , 0x00 }, +- { STB0899_ISI_BIT_EN , 0x00 }, +- { STB0899_MATSTRM , 0x00 }, +- { STB0899_MATSTRL , 0x00 }, +- { STB0899_UPLSTRM , 0x00 }, +- { STB0899_UPLSTRL , 0x00 }, +- { STB0899_DFLSTRM , 0x00 }, +- { STB0899_DFLSTRL , 0x00 }, +- { STB0899_SYNCSTR , 0x00 }, +- { STB0899_SYNCDSTRM , 0x00 }, +- { STB0899_SYNCDSTRL , 0x00 }, +- { STB0899_CFGPDELSTATUS1 , 0x10 }, +- { STB0899_CFGPDELSTATUS2 , 0x00 }, +- { STB0899_BBFERRORM , 0x00 }, +- { STB0899_BBFERRORL , 0x00 }, +- { STB0899_UPKTERRORM , 0x00 }, +- { STB0899_UPKTERRORL , 0x00 }, +- { 0xffff , 0xff }, +-}; +- +-static struct stb0899_config tt3200_config = { +- .init_dev = tt3200_stb0899_s1_init_1, +- .init_s2_demod = stb0899_s2_init_2, +- .init_s1_demod = tt3200_stb0899_s1_init_3, +- .init_s2_fec = stb0899_s2_init_4, +- .init_tst = stb0899_s1_init_5, +- +- .postproc = NULL, +- +- .demod_address = 0x68, +- +- .xtal_freq = 27000000, +- .inversion = IQ_SWAP_ON, /* 1 */ +- +- .lo_clk = 76500000, +- .hi_clk = 99000000, +- +- .esno_ave = STB0899_DVBS2_ESNO_AVE, +- .esno_quant = STB0899_DVBS2_ESNO_QUANT, +- .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, +- .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, +- .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, +- .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, +- .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, +- .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, +- .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, +- +- .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, +- .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, +- .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, +- .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, +- +- .tuner_get_frequency = stb6100_get_frequency, +- .tuner_set_frequency = stb6100_set_frequency, +- .tuner_set_bandwidth = stb6100_set_bandwidth, +- .tuner_get_bandwidth = stb6100_get_bandwidth, +- .tuner_set_rfsiggain = NULL +-}; +- +-static struct stb6100_config tt3200_stb6100_config = { +- .tuner_address = 0x60, +- .refclock = 27000000, +-}; +- +-static void frontend_init(struct budget_ci *budget_ci) +-{ +- switch (budget_ci->budget.dev->pci->subsystem_device) { +- case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) +- budget_ci->budget.dvb_frontend = +- dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; +- budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; +- break; +- } +- break; +- +- case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) +- budget_ci->budget.dvb_frontend = +- dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; +- break; +- } +- break; +- +- case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) +- budget_ci->tuner_pll_address = 0x61; +- budget_ci->budget.dvb_frontend = +- dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; +- break; +- } +- break; +- +- case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) +- budget_ci->tuner_pll_address = 0x63; +- budget_ci->budget.dvb_frontend = +- dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; +- break; +- } +- break; +- +- case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) +- budget_ci->tuner_pll_address = 0x60; +- budget_ci->budget.dvb_frontend = +- dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; +- break; +- } +- break; +- +- case 0x1017: // TT S-1500 PCI +- budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; +- budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; +- +- budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; +- if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { +- printk("%s: No LNBP21 found!\n", __func__); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } +- break; +- +- case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ +- budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); +- if (budget_ci->budget.dvb_frontend) { +- if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { +- printk(KERN_ERR "%s: No tda827x found!\n", __func__); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } +- break; +- +- case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ +- budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { +- if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { +- printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } else { +- printk(KERN_ERR "%s: No STB6000 found!\n", __func__); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } +- break; +- +- case 0x1019: // TT S2-3200 PCI +- /* +- * NOTE! on some STB0899 versions, the internal PLL takes a longer time +- * to settle, aka LOCK. On the older revisions of the chip, we don't see +- * this, as a result on the newer chips the entire clock tree, will not +- * be stable after a freshly POWER 'ed up situation. +- * In this case, we should RESET the STB0899 (Active LOW) and wait for +- * PLL stabilization. +- * +- * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is +- * connected to the SAA7146 GPIO, GPIO2, Pin 142 +- */ +- /* Reset Demodulator */ +- saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); +- /* Wait for everything to die */ +- msleep(50); +- /* Pull it up out of Reset state */ +- saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); +- /* Wait for PLL to stabilize */ +- msleep(250); +- /* +- * PLL state should be stable now. Ideally, we should check +- * for PLL LOCK status. But well, never mind! +- */ +- budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); +- if (budget_ci->budget.dvb_frontend) { +- if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { +- if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { +- printk("%s: No LNBP21 found!\n", __func__); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } else { +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } +- break; +- +- } +- +- if (budget_ci->budget.dvb_frontend == NULL) { +- printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- budget_ci->budget.dev->pci->vendor, +- budget_ci->budget.dev->pci->device, +- budget_ci->budget.dev->pci->subsystem_vendor, +- budget_ci->budget.dev->pci->subsystem_device); +- } else { +- if (dvb_register_frontend +- (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { +- printk("budget-ci: Frontend registration failed!\n"); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- budget_ci->budget.dvb_frontend = NULL; +- } +- } +-} +- +-static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +-{ +- struct budget_ci *budget_ci; +- int err; +- +- budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); +- if (!budget_ci) { +- err = -ENOMEM; +- goto out1; +- } +- +- dprintk(2, "budget_ci: %p\n", budget_ci); +- +- dev->ext_priv = budget_ci; +- +- err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, +- adapter_nr); +- if (err) +- goto out2; +- +- err = msp430_ir_init(budget_ci); +- if (err) +- goto out3; +- +- ciintf_init(budget_ci); +- +- budget_ci->budget.dvb_adapter.priv = budget_ci; +- frontend_init(budget_ci); +- +- ttpci_budget_init_hooks(&budget_ci->budget); +- +- return 0; +- +-out3: +- ttpci_budget_deinit(&budget_ci->budget); +-out2: +- kfree(budget_ci); +-out1: +- return err; +-} +- +-static int budget_ci_detach(struct saa7146_dev *dev) +-{ +- struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; +- struct saa7146_dev *saa = budget_ci->budget.dev; +- int err; +- +- if (budget_ci->budget.ci_present) +- ciintf_deinit(budget_ci); +- msp430_ir_deinit(budget_ci); +- if (budget_ci->budget.dvb_frontend) { +- dvb_unregister_frontend(budget_ci->budget.dvb_frontend); +- dvb_frontend_detach(budget_ci->budget.dvb_frontend); +- } +- err = ttpci_budget_deinit(&budget_ci->budget); +- +- // disable frontend and CI interface +- saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); +- +- kfree(budget_ci); +- +- return err; +-} +- +-static struct saa7146_extension budget_extension; +- +-MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); +-MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); +- +-static struct pci_device_id pci_tbl[] = { +- MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), +- MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), +- MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), +- MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), +- MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), +- MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), +- MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), +- MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), +- MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), +- { +- .vendor = 0, +- } +-}; +- +-MODULE_DEVICE_TABLE(pci, pci_tbl); +- +-static struct saa7146_extension budget_extension = { +- .name = "budget_ci dvb", +- .flags = SAA7146_USE_I2C_IRQ, +- +- .module = THIS_MODULE, +- .pci_tbl = &pci_tbl[0], +- .attach = budget_ci_attach, +- .detach = budget_ci_detach, +- +- .irq_mask = MASK_03 | MASK_06 | MASK_10, +- .irq_func = budget_ci_irq, +-}; +- +-static int __init budget_ci_init(void) +-{ +- return saa7146_register_extension(&budget_extension); +-} +- +-static void __exit budget_ci_exit(void) +-{ +- saa7146_unregister_extension(&budget_extension); +-} +- +-module_init(budget_ci_init); +-module_exit(budget_ci_exit); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); +-MODULE_DESCRIPTION("driver for the SAA7146 based so-called " +- "budget PCI DVB cards w/ CI-module produced by " +- "Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c +deleted file mode 100644 +index 37d02fe..0000000 +--- a/drivers/media/dvb/ttpci/budget-core.c ++++ /dev/null +@@ -1,602 +0,0 @@ +-/* +- * budget-core.c: driver for the SAA7146 based Budget DVB cards +- * +- * Compiled from various sources by Michael Hunold +- * +- * Copyright (C) 2002 Ralph Metzler +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * 26feb2004 Support for FS Activy Card (Grundig tuner) by +- * Michael Dreher , +- * Oliver Endriss , +- * Andreas 'randy' Weinberger +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +- +-#include "budget.h" +-#include "ttpci-eeprom.h" +- +-#define TS_WIDTH (2 * TS_SIZE) +-#define TS_WIDTH_ACTIVY TS_SIZE +-#define TS_WIDTH_DVBC TS_SIZE +-#define TS_HEIGHT_MASK 0xf00 +-#define TS_HEIGHT_MASK_ACTIVY 0xc00 +-#define TS_HEIGHT_MASK_DVBC 0xe00 +-#define TS_MIN_BUFSIZE_K 188 +-#define TS_MAX_BUFSIZE_K 1410 +-#define TS_MAX_BUFSIZE_K_ACTIVY 564 +-#define TS_MAX_BUFSIZE_K_DVBC 1316 +-#define BUFFER_WARNING_WAIT (30*HZ) +- +-int budget_debug; +-static int dma_buffer_size = TS_MIN_BUFSIZE_K; +-module_param_named(debug, budget_debug, int, 0644); +-module_param_named(bufsize, dma_buffer_size, int, 0444); +-MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +-MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); +- +-/**************************************************************************** +- * TT budget / WinTV Nova +- ****************************************************************************/ +- +-static int stop_ts_capture(struct budget *budget) +-{ +- dprintk(2, "budget: %p\n", budget); +- +- saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off +- SAA7146_IER_DISABLE(budget->dev, MASK_10); +- return 0; +-} +- +-static int start_ts_capture(struct budget *budget) +-{ +- struct saa7146_dev *dev = budget->dev; +- +- dprintk(2, "budget: %p\n", budget); +- +- if (!budget->feeding || !budget->fe_synced) +- return 0; +- +- saa7146_write(dev, MC1, MASK_20); // DMA3 off +- +- memset(budget->grabbing, 0x00, budget->buffer_size); +- +- saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); +- +- budget->ttbp = 0; +- +- /* +- * Signal path on the Activy: +- * +- * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory +- * +- * Since the tuner feeds 204 bytes packets into the SAA7146, +- * DMA3 is configured to strip the trailing 16 FEC bytes: +- * Pitch: 188, NumBytes3: 188, NumLines3: 1024 +- */ +- +- switch(budget->card->type) { +- case BUDGET_FS_ACTIVY: +- saa7146_write(dev, DD1_INIT, 0x04000000); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25)); +- saa7146_write(dev, BRS_CTRL, 0x00000000); +- break; +- case BUDGET_PATCH: +- saa7146_write(dev, DD1_INIT, 0x00000200); +- saa7146_write(dev, MC2, (MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x60000000); +- break; +- case BUDGET_CIN1200C_MK3: +- case BUDGET_KNC1C_MK3: +- case BUDGET_KNC1C_TDA10024: +- case BUDGET_KNC1CP_MK3: +- if (budget->video_port == BUDGET_VIDEO_PORTA) { +- saa7146_write(dev, DD1_INIT, 0x06000200); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x00000000); +- } else { +- saa7146_write(dev, DD1_INIT, 0x00000600); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x60000000); +- } +- break; +- default: +- if (budget->video_port == BUDGET_VIDEO_PORTA) { +- saa7146_write(dev, DD1_INIT, 0x06000200); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x00000000); +- } else { +- saa7146_write(dev, DD1_INIT, 0x02000600); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- saa7146_write(dev, BRS_CTRL, 0x60000000); +- } +- } +- +- saa7146_write(dev, MC2, (MASK_08 | MASK_24)); +- mdelay(10); +- +- saa7146_write(dev, BASE_ODD3, 0); +- if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { +- // using odd/even buffers +- saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); +- } else { +- // using a single buffer +- saa7146_write(dev, BASE_EVEN3, 0); +- } +- saa7146_write(dev, PROT_ADDR3, budget->buffer_size); +- saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); +- +- saa7146_write(dev, PITCH3, budget->buffer_width); +- saa7146_write(dev, NUM_LINE_BYTE3, +- (budget->buffer_height << 16) | budget->buffer_width); +- +- saa7146_write(dev, MC2, (MASK_04 | MASK_20)); +- +- SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ +- SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ +- saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ +- +- return 0; +-} +- +-static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status) +-{ +- struct budget *budget = (struct budget *) fe->dvb->priv; +- int synced; +- int ret; +- +- if (budget->read_fe_status) +- ret = budget->read_fe_status(fe, status); +- else +- ret = -EINVAL; +- +- if (!ret) { +- synced = (*status & FE_HAS_LOCK); +- if (synced != budget->fe_synced) { +- budget->fe_synced = synced; +- spin_lock(&budget->feedlock); +- if (synced) +- start_ts_capture(budget); +- else +- stop_ts_capture(budget); +- spin_unlock(&budget->feedlock); +- } +- } +- return ret; +-} +- +-static void vpeirq(unsigned long data) +-{ +- struct budget *budget = (struct budget *) data; +- u8 *mem = (u8 *) (budget->grabbing); +- u32 olddma = budget->ttbp; +- u32 newdma = saa7146_read(budget->dev, PCI_VDP3); +- u32 count; +- +- /* Ensure streamed PCI data is synced to CPU */ +- pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); +- +- /* nearest lower position divisible by 188 */ +- newdma -= newdma % 188; +- +- if (newdma >= budget->buffer_size) +- return; +- +- budget->ttbp = newdma; +- +- if (budget->feeding == 0 || newdma == olddma) +- return; +- +- if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ +- count = newdma - olddma; +- dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); +- } else { /* wraparound, dump olddma..buflen and 0..newdma */ +- count = budget->buffer_size - olddma; +- dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); +- count += newdma; +- dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); +- } +- +- if (count > budget->buffer_warning_threshold) +- budget->buffer_warnings++; +- +- if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { +- printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", +- budget->dev->name, __func__, budget->buffer_warnings, count); +- budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; +- budget->buffer_warnings = 0; +- } +-} +- +- +-int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, +- int uselocks, int nobusyloop) +-{ +- struct saa7146_dev *saa = budget->dev; +- int result = 0; +- unsigned long flags = 0; +- +- if (count > 4 || count <= 0) +- return 0; +- +- if (uselocks) +- spin_lock_irqsave(&budget->debilock, flags); +- +- if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- return result; +- } +- +- saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); +- saa7146_write(saa, DEBI_CONFIG, config); +- saa7146_write(saa, DEBI_PAGE, 0); +- saa7146_write(saa, MC2, (2 << 16) | 2); +- +- if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- return result; +- } +- +- result = saa7146_read(saa, DEBI_AD); +- result &= (0xffffffffUL >> ((4 - count) * 8)); +- +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- +- return result; +-} +- +-int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, +- int count, u32 value, int uselocks, int nobusyloop) +-{ +- struct saa7146_dev *saa = budget->dev; +- unsigned long flags = 0; +- int result; +- +- if (count > 4 || count <= 0) +- return 0; +- +- if (uselocks) +- spin_lock_irqsave(&budget->debilock, flags); +- +- if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- return result; +- } +- +- saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); +- saa7146_write(saa, DEBI_CONFIG, config); +- saa7146_write(saa, DEBI_PAGE, 0); +- saa7146_write(saa, DEBI_AD, value); +- saa7146_write(saa, MC2, (2 << 16) | 2); +- +- if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- return result; +- } +- +- if (uselocks) +- spin_unlock_irqrestore(&budget->debilock, flags); +- return 0; +-} +- +- +-/**************************************************************************** +- * DVB API SECTION +- ****************************************************************************/ +- +-static int budget_start_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct budget *budget = (struct budget *) demux->priv; +- int status = 0; +- +- dprintk(2, "budget: %p\n", budget); +- +- if (!demux->dmx.frontend) +- return -EINVAL; +- +- spin_lock(&budget->feedlock); +- feed->pusi_seen = 0; /* have a clean section start */ +- if (budget->feeding++ == 0) +- status = start_ts_capture(budget); +- spin_unlock(&budget->feedlock); +- return status; +-} +- +-static int budget_stop_feed(struct dvb_demux_feed *feed) +-{ +- struct dvb_demux *demux = feed->demux; +- struct budget *budget = (struct budget *) demux->priv; +- int status = 0; +- +- dprintk(2, "budget: %p\n", budget); +- +- spin_lock(&budget->feedlock); +- if (--budget->feeding == 0) +- status = stop_ts_capture(budget); +- spin_unlock(&budget->feedlock); +- return status; +-} +- +-static int budget_register(struct budget *budget) +-{ +- struct dvb_demux *dvbdemux = &budget->demux; +- int ret; +- +- dprintk(2, "budget: %p\n", budget); +- +- dvbdemux->priv = (void *) budget; +- +- dvbdemux->filternum = 256; +- dvbdemux->feednum = 256; +- dvbdemux->start_feed = budget_start_feed; +- dvbdemux->stop_feed = budget_stop_feed; +- dvbdemux->write_to_decoder = NULL; +- +- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | +- DMX_MEMORY_BASED_FILTERING); +- +- dvb_dmx_init(&budget->demux); +- +- budget->dmxdev.filternum = 256; +- budget->dmxdev.demux = &dvbdemux->dmx; +- budget->dmxdev.capabilities = 0; +- +- dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); +- +- budget->hw_frontend.source = DMX_FRONTEND_0; +- +- ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); +- +- if (ret < 0) +- return ret; +- +- budget->mem_frontend.source = DMX_MEMORY_FE; +- ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); +- if (ret < 0) +- return ret; +- +- ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); +- if (ret < 0) +- return ret; +- +- dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); +- +- return 0; +-} +- +-static void budget_unregister(struct budget *budget) +-{ +- struct dvb_demux *dvbdemux = &budget->demux; +- +- dprintk(2, "budget: %p\n", budget); +- +- dvb_net_release(&budget->dvb_net); +- +- dvbdemux->dmx.close(&dvbdemux->dmx); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); +- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); +- +- dvb_dmxdev_release(&budget->dmxdev); +- dvb_dmx_release(&budget->demux); +-} +- +-int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, +- struct saa7146_pci_extension_data *info, +- struct module *owner, short *adapter_nums) +-{ +- int ret = 0; +- struct budget_info *bi = info->ext_priv; +- int max_bufsize; +- int height_mask; +- +- memset(budget, 0, sizeof(struct budget)); +- +- dprintk(2, "dev: %p, budget: %p\n", dev, budget); +- +- budget->card = bi; +- budget->dev = (struct saa7146_dev *) dev; +- +- switch(budget->card->type) { +- case BUDGET_FS_ACTIVY: +- budget->buffer_width = TS_WIDTH_ACTIVY; +- max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; +- height_mask = TS_HEIGHT_MASK_ACTIVY; +- break; +- +- case BUDGET_KNC1C: +- case BUDGET_KNC1CP: +- case BUDGET_CIN1200C: +- case BUDGET_KNC1C_MK3: +- case BUDGET_KNC1C_TDA10024: +- case BUDGET_KNC1CP_MK3: +- case BUDGET_CIN1200C_MK3: +- budget->buffer_width = TS_WIDTH_DVBC; +- max_bufsize = TS_MAX_BUFSIZE_K_DVBC; +- height_mask = TS_HEIGHT_MASK_DVBC; +- break; +- +- default: +- budget->buffer_width = TS_WIDTH; +- max_bufsize = TS_MAX_BUFSIZE_K; +- height_mask = TS_HEIGHT_MASK; +- } +- +- if (dma_buffer_size < TS_MIN_BUFSIZE_K) +- dma_buffer_size = TS_MIN_BUFSIZE_K; +- else if (dma_buffer_size > max_bufsize) +- dma_buffer_size = max_bufsize; +- +- budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; +- if (budget->buffer_height > 0xfff) { +- budget->buffer_height /= 2; +- budget->buffer_height &= height_mask; +- budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; +- } else { +- budget->buffer_height &= height_mask; +- budget->buffer_size = budget->buffer_height * budget->buffer_width; +- } +- budget->buffer_warning_threshold = budget->buffer_size * 80/100; +- budget->buffer_warnings = 0; +- budget->buffer_warning_time = jiffies; +- +- dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", +- budget->dev->name, +- budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", +- budget->buffer_width, budget->buffer_height); +- printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); +- +- ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, +- owner, &budget->dev->pci->dev, adapter_nums); +- if (ret < 0) +- return ret; +- +- /* set dd1 stream a & b */ +- saa7146_write(dev, DD1_STREAM_B, 0x00000000); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25)); +- saa7146_write(dev, MC2, (MASK_10 | MASK_26)); +- saa7146_write(dev, DD1_INIT, 0x02000000); +- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); +- +- if (bi->type != BUDGET_FS_ACTIVY) +- budget->video_port = BUDGET_VIDEO_PORTB; +- else +- budget->video_port = BUDGET_VIDEO_PORTA; +- spin_lock_init(&budget->feedlock); +- spin_lock_init(&budget->debilock); +- +- /* the Siemens DVB needs this if you want to have the i2c chips +- get recognized before the main driver is loaded */ +- if (bi->type != BUDGET_FS_ACTIVY) +- saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ +- +- strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); +- +- saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); +- strcpy(budget->i2c_adap.name, budget->card->name); +- +- if (i2c_add_adapter(&budget->i2c_adap) < 0) { +- ret = -ENOMEM; +- goto err_dvb_unregister; +- } +- +- ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); +- +- budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); +- if (NULL == budget->grabbing) { +- ret = -ENOMEM; +- goto err_del_i2c; +- } +- +- saa7146_write(dev, PCI_BT_V1, 0x001c0000); +- /* upload all */ +- saa7146_write(dev, GPIO_CTRL, 0x000000); +- +- tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget); +- +- /* frontend power on */ +- if (bi->type != BUDGET_FS_ACTIVY) +- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); +- +- if ((ret = budget_register(budget)) == 0) +- return 0; /* Everything OK */ +- +- /* An error occurred, cleanup resources */ +- saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); +- +-err_del_i2c: +- i2c_del_adapter(&budget->i2c_adap); +- +-err_dvb_unregister: +- dvb_unregister_adapter(&budget->dvb_adapter); +- +- return ret; +-} +- +-void ttpci_budget_init_hooks(struct budget *budget) +-{ +- if (budget->dvb_frontend && !budget->read_fe_status) { +- budget->read_fe_status = budget->dvb_frontend->ops.read_status; +- budget->dvb_frontend->ops.read_status = budget_read_fe_status; +- } +-} +- +-int ttpci_budget_deinit(struct budget *budget) +-{ +- struct saa7146_dev *dev = budget->dev; +- +- dprintk(2, "budget: %p\n", budget); +- +- budget_unregister(budget); +- +- tasklet_kill(&budget->vpe_tasklet); +- +- saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); +- +- i2c_del_adapter(&budget->i2c_adap); +- +- dvb_unregister_adapter(&budget->dvb_adapter); +- +- return 0; +-} +- +-void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) +-{ +- struct budget *budget = (struct budget *) dev->ext_priv; +- +- dprintk(8, "dev: %p, budget: %p\n", dev, budget); +- +- if (*isr & MASK_10) +- tasklet_schedule(&budget->vpe_tasklet); +-} +- +-void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) +-{ +- struct budget *budget = (struct budget *) dev->ext_priv; +- +- spin_lock(&budget->feedlock); +- budget->video_port = video_port; +- if (budget->feeding) { +- stop_ts_capture(budget); +- start_ts_capture(budget); +- } +- spin_unlock(&budget->feedlock); +-} +- +-EXPORT_SYMBOL_GPL(ttpci_budget_debiread); +-EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); +-EXPORT_SYMBOL_GPL(ttpci_budget_init); +-EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); +-EXPORT_SYMBOL_GPL(ttpci_budget_deinit); +-EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); +-EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); +-EXPORT_SYMBOL_GPL(budget_debug); +- +-MODULE_LICENSE("GPL"); +diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c +deleted file mode 100644 +index 2cb35c2..0000000 +--- a/drivers/media/dvb/ttpci/budget-patch.c ++++ /dev/null +@@ -1,680 +0,0 @@ +-/* +- * budget-patch.c: driver for Budget Patch, +- * hardware modification of DVB-S cards enabling full TS +- * +- * Written by Emard +- * +- * Original idea by Roberto Deza +- * +- * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic +- * and Metzlerbros +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#include "av7110.h" +-#include "av7110_hw.h" +-#include "budget.h" +-#include "stv0299.h" +-#include "ves1x93.h" +-#include "tda8083.h" +- +-#include "bsru6.h" +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define budget_patch budget +- +-static struct saa7146_extension budget_extension; +- +-MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); +-//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); +- +-static struct pci_device_id pci_tbl[] = { +- MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), +-// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), +- { +- .vendor = 0, +- } +-}; +- +-/* those lines are for budget-patch to be tried +-** on a true budget card and observe the +-** behaviour of VSYNC generated by rps1. +-** this code was shamelessly copy/pasted from budget.c +-*/ +-static void gpio_Set22K (struct budget *budget, int state) +-{ +- struct saa7146_dev *dev=budget->dev; +- dprintk(2, "budget: %p\n", budget); +- saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +-} +- +-/* Diseqc functions only for TT Budget card */ +-/* taken from the Skyvision DVB driver by +- Ralph Metzler */ +- +-static void DiseqcSendBit (struct budget *budget, int data) +-{ +- struct saa7146_dev *dev=budget->dev; +- dprintk(2, "budget: %p\n", budget); +- +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); +- udelay(data ? 500 : 1000); +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- udelay(data ? 1000 : 500); +-} +- +-static void DiseqcSendByte (struct budget *budget, int data) +-{ +- int i, par=1, d; +- +- dprintk(2, "budget: %p\n", budget); +- +- for (i=7; i>=0; i--) { +- d = (data>>i)&1; +- par ^= d; +- DiseqcSendBit(budget, d); +- } +- +- DiseqcSendBit(budget, par); +-} +- +-static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +-{ +- struct saa7146_dev *dev=budget->dev; +- int i; +- +- dprintk(2, "budget: %p\n", budget); +- +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- mdelay(16); +- +- for (i=0; idvb->priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- gpio_Set22K (budget, 1); +- break; +- +- case SEC_TONE_OFF: +- gpio_Set22K (budget, 0); +- break; +- +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); +- +- return 0; +-} +- +-static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- SendDiSEqCMsg (budget, 0, NULL, minicmd); +- +- return 0; +-} +- +-static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) +-{ +- int i; +- +- dprintk(2, "budget: %p\n", budget); +- +- for (i = 2; i < length; i++) +- { +- ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); +- msleep(5); +- } +- if (length) +- ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); +- else +- ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); +- msleep(5); +- ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); +- msleep(5); +- return 0; +-} +- +-static void av7110_set22k(struct budget_patch *budget, int state) +-{ +- u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; +- +- dprintk(2, "budget: %p\n", budget); +- budget_av7110_send_fw_cmd(budget, buf, 2); +-} +- +-static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) +-{ +- int i; +- u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), +- 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +- +- dprintk(2, "budget: %p\n", budget); +- +- if (len>10) +- len=10; +- +- buf[1] = len+2; +- buf[2] = len; +- +- if (burst != -1) +- buf[3]=burst ? 0x01 : 0x00; +- else +- buf[3]=0xffff; +- +- for (i=0; idvb->priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- av7110_set22k (budget, 1); +- break; +- +- case SEC_TONE_OFF: +- av7110_set22k (budget, 0); +- break; +- +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +-{ +- struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; +- +- av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); +- +- return 0; +-} +- +-static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; +- +- av7110_send_diseqc_msg (budget, 0, NULL, minicmd); +- +- return 0; +-} +- +-static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; +- u8 pwr = 0; +- u8 buf[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; +- u32 div = (p->frequency + 479500) / 125; +- +- if (p->frequency > 2000000) +- pwr = 3; +- else if (p->frequency > 1800000) +- pwr = 2; +- else if (p->frequency > 1600000) +- pwr = 1; +- else if (p->frequency > 1200000) +- pwr = 0; +- else if (p->frequency >= 1100000) +- pwr = 1; +- else pwr = 2; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = ((div & 0x18000) >> 10) | 0x95; +- buf[3] = (pwr << 6) | 0x30; +- +- // NOTE: since we're using a prescaler of 2, we set the +- // divisor frequency to 62.5kHz and divide by 125 above +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct ves1x93_config alps_bsrv2_config = { +- .demod_address = 0x08, +- .xin = 90100000UL, +- .invert_pwm = 0, +-}; +- +-static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = p->frequency / 125; +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x8e; +- data[3] = 0x00; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) +- return -EIO; +- return 0; +-} +- +-static struct tda8083_config grundig_29504_451_config = { +- .demod_address = 0x68, +-}; +- +-static void frontend_init(struct budget_patch* budget) +-{ +- switch(budget->dev->pci->subsystem_device) { +- case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X +- case 0x1013: // SATELCO Multimedia PCI +- +- // try the ALPS BSRV2 first of all +- budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; +- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; +- budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; +- budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; +- break; +- } +- +- // try the ALPS BSRU6 now +- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; +- budget->dvb_frontend->tuner_priv = &budget->i2c_adap; +- +- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; +- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; +- budget->dvb_frontend->ops.set_tone = budget_set_tone; +- break; +- } +- +- // Try the grundig 29504-451 +- budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; +- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; +- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; +- budget->dvb_frontend->ops.set_tone = budget_set_tone; +- break; +- } +- break; +- } +- +- if (budget->dvb_frontend == NULL) { +- printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- budget->dev->pci->vendor, +- budget->dev->pci->device, +- budget->dev->pci->subsystem_vendor, +- budget->dev->pci->subsystem_device); +- } else { +- if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { +- printk("budget-av: Frontend registration failed!\n"); +- dvb_frontend_detach(budget->dvb_frontend); +- budget->dvb_frontend = NULL; +- } +- } +-} +- +-/* written by Emard */ +-static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +-{ +- struct budget_patch *budget; +- int err; +- int count = 0; +- int detected = 0; +- +-#define PATCH_RESET 0 +-#define RPS_IRQ 0 +-#define HPS_SETUP 0 +-#if PATCH_RESET +- saa7146_write(dev, MC1, MASK_31); +- msleep(40); +-#endif +-#if HPS_SETUP +- // initialize registers. Better to have it like this +- // than leaving something unconfigured +- saa7146_write(dev, DD1_STREAM_B, 0); +- // port B VSYNC at rising edge +- saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! +- saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI +- +- // debi config +- // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); +- +- // zero all HPS registers +- saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 +- saa7146_write(dev, HPS_H_SCALE, 0); // r6c +- saa7146_write(dev, BCS_CTRL, 0); // r70 +- saa7146_write(dev, HPS_V_SCALE, 0); // r60 +- saa7146_write(dev, HPS_V_GAIN, 0); // r64 +- saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 +- saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 +- // Set HPS prescaler for port B input +- saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); +- saa7146_write(dev, MC2, +- 0 * (MASK_08 | MASK_24) | // BRS control +- 0 * (MASK_09 | MASK_25) | // a +- 0 * (MASK_10 | MASK_26) | // b +- 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 +- 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 +- 0 * (MASK_01 | MASK_15) // DEBI +- ); +-#endif +- // Disable RPS1 and RPS0 +- saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); +- // RPS1 timeout disable +- saa7146_write(dev, RPS_TOV1, 0); +- +- // code for autodetection +- // will wait for VBI_B event (vertical blank at port B) +- // and will reset GPIO3 after VBI_B is detected. +- // (GPIO3 should be raised high by CPU to +- // test if GPIO3 will generate vertical blank signal +- // in budget patch GPIO3 is connected to VSYNC_B +- count = 0; +-#if 0 +- WRITE_RPS1(CMD_UPLOAD | +- MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); +-#endif +- WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +-#if RPS_IRQ +- // issue RPS1 interrupt to increment counter +- WRITE_RPS1(CMD_INTERRUPT); +- // at least a NOP is neede between two interrupts +- WRITE_RPS1(CMD_NOP); +- // interrupt again +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- WRITE_RPS1(CMD_STOP); +- +-#if RPS_IRQ +- // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) +- // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled +- // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called +- saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); +- // set event counter 1 threshold to maximum allowed value (rEC p55) +- saa7146_write(dev, ECT1R, 0x3fff ); +-#endif +- // Fix VSYNC level +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- // Set RPS1 Address register to point to RPS code (r108 p42) +- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); +- // Enable RPS1, (rFC p33) +- saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); +- +- +- mdelay(50); +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); +- mdelay(150); +- +- +- if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) +- detected = 1; +- +-#if RPS_IRQ +- printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); +-#endif +- // Disable RPS1 +- saa7146_write(dev, MC1, ( MASK_29 )); +- +- if(detected == 0) +- printk("budget-patch not detected or saa7146 in non-default state.\n" +- "try enabling ressetting of 7146 with MASK_31 in MC1 register\n"); +- +- else +- printk("BUDGET-PATCH DETECTED.\n"); +- +- +-/* OLD (Original design by Roberto Deza): +-** This code will setup the SAA7146_RPS1 to generate a square +-** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of +-** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; +-** then, this GPIO3 output which is connected to the D1B_VSYNC +-** input, will trigger the acquisition of the alternate field +-** and so on. +-** Currently, the TT_budget / WinTV_Nova cards have two ICs +-** (74HCT4040, LVC74) for the generation of this VSYNC signal, +-** which seems that can be done perfectly without this :-)). +-*/ +- +-/* New design (By Emard) +-** this rps1 code will copy internal HS event to GPIO3 pin. +-** GPIO3 is in budget-patch hardware connected to port B VSYNC +- +-** HS is an internal event of 7146, accessible with RPS +-** and temporarily raised high every n lines +-** (n in defined in the RPS_THRESH1 counter threshold) +-** I think HS is raised high on the beginning of the n-th line +-** and remains high until this n-th line that triggered +-** it is completely received. When the reception of n-th line +-** ends, HS is lowered. +- +-** To transmit data over DMA, 7146 needs changing state at +-** port B VSYNC pin. Any changing of port B VSYNC will +-** cause some DMA data transfer, with more or less packets loss. +-** It depends on the phase and frequency of VSYNC and +-** the way of 7146 is instructed to trigger on port B (defined +-** in DD1_INIT register, 3rd nibble from the right valid +-** numbers are 0-7, see datasheet) +-** +-** The correct triggering can minimize packet loss, +-** dvbtraffic should give this stable bandwidths: +-** 22k transponder = 33814 kbit/s +-** 27.5k transponder = 38045 kbit/s +-** by experiment it is found that the best results +-** (stable bandwidths and almost no packet loss) +-** are obtained using DD1_INIT triggering number 2 +-** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) +-** and a VSYNC phase that occurs in the middle of DMA transfer +-** (about byte 188*512=96256 in the DMA window). +-** +-** Phase of HS is still not clear to me how to control, +-** It just happens to be so. It can be seen if one enables +-** RPS_IRQ and print Event Counter 1 in vpeirq(). Every +-** time RPS_INTERRUPT is called, the Event Counter 1 will +-** increment. That's how the 7146 is programmed to do event +-** counting in this budget-patch.c +-** I *think* HPS setting has something to do with the phase +-** of HS but I can't be 100% sure in that. +- +-** hardware debug note: a working budget card (including budget patch) +-** with vpeirq() interrupt setup in mode "0x90" (every 64K) will +-** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes +-** and that means 3*25=75 Hz of interrupt frequency, as seen by +-** watch cat /proc/interrupts +-** +-** If this frequency is 3x lower (and data received in the DMA +-** buffer don't start with 0x47, but in the middle of packets, +-** whose lengths appear to be like 188 292 188 104 etc. +-** this means VSYNC line is not connected in the hardware. +-** (check soldering pcb and pins) +-** The same behaviour of missing VSYNC can be duplicated on budget +-** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. +-*/ +- +- // Setup RPS1 "program" (p35) +- count = 0; +- +- +- // Wait Source Line Counter Threshold (p36) +- WRITE_RPS1(CMD_PAUSE | EVT_HS); +- // Set GPIO3=1 (p42) +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); +-#if RPS_IRQ +- // issue RPS1 interrupt +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- // Wait reset Source Line Counter Threshold (p36) +- WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); +- // Set GPIO3=0 (p42) +- WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); +- WRITE_RPS1(GPIO3_MSK); +- WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); +-#if RPS_IRQ +- // issue RPS1 interrupt +- WRITE_RPS1(CMD_INTERRUPT); +-#endif +- // Jump to begin of RPS program (p37) +- WRITE_RPS1(CMD_JUMP); +- WRITE_RPS1(dev->d_rps1.dma_handle); +- +- // Fix VSYNC level +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- // Set RPS1 Address register to point to RPS code (r108 p42) +- saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); +- +- if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) +- return -ENOMEM; +- +- dprintk(2, "budget: %p\n", budget); +- +- err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); +- if (err) { +- kfree(budget); +- return err; +- } +- +- // Set Source Line Counter Threshold, using BRS (rCC p43) +- // It generates HS event every TS_HEIGHT lines +- // this is related to TS_WIDTH set in register +- // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE +- // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 +- //,then RPS_THRESH1 +- // should be set to trigger every TS_HEIGHT (512) lines. +- // +- saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); +- +- // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); +- // Enable RPS1 (rFC p33) +- saa7146_write(dev, MC1, (MASK_13 | MASK_29)); +- +- +- dev->ext_priv = budget; +- +- budget->dvb_adapter.priv = budget; +- frontend_init(budget); +- +- ttpci_budget_init_hooks(budget); +- +- return 0; +-} +- +-static int budget_patch_detach (struct saa7146_dev* dev) +-{ +- struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; +- int err; +- +- if (budget->dvb_frontend) { +- dvb_unregister_frontend(budget->dvb_frontend); +- dvb_frontend_detach(budget->dvb_frontend); +- } +- err = ttpci_budget_deinit (budget); +- +- kfree (budget); +- +- return err; +-} +- +-static int __init budget_patch_init(void) +-{ +- return saa7146_register_extension(&budget_extension); +-} +- +-static void __exit budget_patch_exit(void) +-{ +- saa7146_unregister_extension(&budget_extension); +-} +- +-static struct saa7146_extension budget_extension = { +- .name = "budget_patch dvb", +- .flags = 0, +- +- .module = THIS_MODULE, +- .pci_tbl = pci_tbl, +- .attach = budget_patch_attach, +- .detach = budget_patch_detach, +- +- .irq_mask = MASK_10, +- .irq_func = ttpci_budget_irq10_handler, +-}; +- +-module_init(budget_patch_init); +-module_exit(budget_patch_exit); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); +-MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 " +- "based so-called Budget Patch cards"); +diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c +deleted file mode 100644 +index b21bcce..0000000 +--- a/drivers/media/dvb/ttpci/budget.c ++++ /dev/null +@@ -1,811 +0,0 @@ +-/* +- * budget.c: driver for the SAA7146 based Budget DVB cards +- * +- * Compiled from various sources by Michael Hunold +- * +- * Copyright (C) 2002 Ralph Metzler +- * +- * Copyright (C) 1999-2002 Ralph Metzler +- * & Marcus Metzler for convergence integrated media GmbH +- * +- * 26feb2004 Support for FS Activy Card (Grundig tuner) by +- * Michael Dreher , +- * Oliver Endriss and +- * Andreas 'randy' Weinberger +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License +- * as published by the Free Software Foundation; either version 2 +- * of the License, or (at your option) any later version. +- * +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html +- * +- * +- * the project's page is at http://www.linuxtv.org/ +- */ +- +-#include "budget.h" +-#include "stv0299.h" +-#include "ves1x93.h" +-#include "ves1820.h" +-#include "l64781.h" +-#include "tda8083.h" +-#include "s5h1420.h" +-#include "tda10086.h" +-#include "tda826x.h" +-#include "lnbp21.h" +-#include "bsru6.h" +-#include "bsbe1.h" +-#include "tdhd1.h" +-#include "stv6110x.h" +-#include "stv090x.h" +-#include "isl6423.h" +- +-static int diseqc_method; +-module_param(diseqc_method, int, 0444); +-MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-static void Set22K (struct budget *budget, int state) +-{ +- struct saa7146_dev *dev=budget->dev; +- dprintk(2, "budget: %p\n", budget); +- saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); +-} +- +-/* Diseqc functions only for TT Budget card */ +-/* taken from the Skyvision DVB driver by +- Ralph Metzler */ +- +-static void DiseqcSendBit (struct budget *budget, int data) +-{ +- struct saa7146_dev *dev=budget->dev; +- dprintk(2, "budget: %p\n", budget); +- +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); +- udelay(data ? 500 : 1000); +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- udelay(data ? 1000 : 500); +-} +- +-static void DiseqcSendByte (struct budget *budget, int data) +-{ +- int i, par=1, d; +- +- dprintk(2, "budget: %p\n", budget); +- +- for (i=7; i>=0; i--) { +- d = (data>>i)&1; +- par ^= d; +- DiseqcSendBit(budget, d); +- } +- +- DiseqcSendBit(budget, par); +-} +- +-static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) +-{ +- struct saa7146_dev *dev=budget->dev; +- int i; +- +- dprintk(2, "budget: %p\n", budget); +- +- saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); +- mdelay(16); +- +- for (i=0; idev; +- +- dprintk(2, "budget: %p\n", budget); +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); +- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); +- break; +- case SEC_VOLTAGE_18: +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); +- saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); +- break; +- case SEC_VOLTAGE_OFF: +- saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- return SetVoltage_Activy (budget, voltage); +-} +- +-static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- switch (tone) { +- case SEC_TONE_ON: +- Set22K (budget, 1); +- break; +- +- case SEC_TONE_OFF: +- Set22K (budget, 0); +- break; +- +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); +- +- return 0; +-} +- +-static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) +-{ +- struct budget* budget = (struct budget*) fe->dvb->priv; +- +- SendDiSEqCMsg (budget, 0, NULL, minicmd); +- +- return 0; +-} +- +-static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget* budget = (struct budget*) fe->dvb->priv; +- u8 pwr = 0; +- u8 buf[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; +- u32 div = (c->frequency + 479500) / 125; +- +- if (c->frequency > 2000000) +- pwr = 3; +- else if (c->frequency > 1800000) +- pwr = 2; +- else if (c->frequency > 1600000) +- pwr = 1; +- else if (c->frequency > 1200000) +- pwr = 0; +- else if (c->frequency >= 1100000) +- pwr = 1; +- else pwr = 2; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = ((div & 0x18000) >> 10) | 0x95; +- buf[3] = (pwr << 6) | 0x30; +- +- // NOTE: since we're using a prescaler of 2, we set the +- // divisor frequency to 62.5kHz and divide by 125 above +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct ves1x93_config alps_bsrv2_config = +-{ +- .demod_address = 0x08, +- .xin = 90100000UL, +- .invert_pwm = 0, +-}; +- +-static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget* budget = (struct budget*) fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (c->frequency + 35937500 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x85 | ((div >> 10) & 0x60); +- data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct ves1820_config alps_tdbe2_config = { +- .demod_address = 0x09, +- .xin = 57840000UL, +- .invert = 1, +- .selagc = VES1820_SELAGC_SIGNAMPERR, +-}; +- +-static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget *budget = fe->dvb->priv; +- u8 *tuner_addr = fe->tuner_priv; +- u32 div; +- u8 cfg, cpump, band_select; +- u8 data[4]; +- struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; +- +- if (tuner_addr) +- msg.addr = *tuner_addr; +- else +- msg.addr = 0x61; +- +- div = (36125000 + c->frequency) / 166666; +- +- cfg = 0x88; +- +- if (c->frequency < 175000000) +- cpump = 2; +- else if (c->frequency < 390000000) +- cpump = 1; +- else if (c->frequency < 470000000) +- cpump = 2; +- else if (c->frequency < 750000000) +- cpump = 1; +- else +- cpump = 3; +- +- if (c->frequency < 175000000) +- band_select = 0x0e; +- else if (c->frequency < 470000000) +- band_select = 0x05; +- else +- band_select = 0x03; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = ((div >> 10) & 0x60) | cfg; +- data[3] = (cpump << 6) | band_select; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct l64781_config grundig_29504_401_config = { +- .demod_address = 0x55, +-}; +- +-static struct l64781_config grundig_29504_401_config_activy = { +- .demod_address = 0x54, +-}; +- +-static u8 tuner_address_grundig_29504_401_activy = 0x60; +- +-static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget* budget = (struct budget*) fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = c->frequency / 125; +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x8e; +- data[3] = 0x00; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct tda8083_config grundig_29504_451_config = { +- .demod_address = 0x68, +-}; +- +-static int s5h1420_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *c = &fe->dtv_property_cache; +- struct budget* budget = (struct budget*) fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = c->frequency / 1000; +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0xc2; +- +- if (div < 1450) +- data[3] = 0x00; +- else if (div < 1850) +- data[3] = 0x40; +- else if (div < 2000) +- data[3] = 0x80; +- else +- data[3] = 0xc0; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; +- +- return 0; +-} +- +-static struct s5h1420_config s5h1420_config = { +- .demod_address = 0x53, +- .invert = 1, +- .cdclk_polarity = 1, +-}; +- +-static struct tda10086_config tda10086_config = { +- .demod_address = 0x0e, +- .invert = 0, +- .diseqc_tone = 1, +- .xtal_freq = TDA10086_XTAL_16M, +-}; +- +-static struct stv0299_config alps_bsru6_config_activy = { +- .demod_address = 0x68, +- .inittab = alps_bsru6_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .op0_off = 1, +- .min_delay_ms = 100, +- .set_symbol_rate = alps_bsru6_set_symbol_rate, +-}; +- +-static struct stv0299_config alps_bsbe1_config_activy = { +- .demod_address = 0x68, +- .inittab = alps_bsbe1_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .op0_off = 1, +- .min_delay_ms = 100, +- .set_symbol_rate = alps_bsbe1_set_symbol_rate, +-}; +- +-static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) +-{ +- struct budget *budget = (struct budget *)fe->dvb->priv; +- +- return request_firmware(fw, name, &budget->dev->pci->dev); +-} +- +- +-static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) +-{ +- u8 val; +- struct i2c_msg msg[] = { +- { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, +- { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } +- }; +- +- return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; +-} +- +-static u8 read_pwm(struct budget* budget) +-{ +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, +- { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; +- +- if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +-static struct stv090x_config tt1600_stv090x_config = { +- .device = STV0903, +- .demod_mode = STV090x_SINGLE, +- .clk_mode = STV090x_CLK_EXT, +- +- .xtal = 13500000, +- .address = 0x68, +- +- .ts1_mode = STV090x_TSMODE_DVBCI, +- .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, +- +- .repeater_level = STV090x_RPTLEVEL_16, +- +- .tuner_init = NULL, +- .tuner_sleep = NULL, +- .tuner_set_mode = NULL, +- .tuner_set_frequency = NULL, +- .tuner_get_frequency = NULL, +- .tuner_set_bandwidth = NULL, +- .tuner_get_bandwidth = NULL, +- .tuner_set_bbgain = NULL, +- .tuner_get_bbgain = NULL, +- .tuner_set_refclk = NULL, +- .tuner_get_status = NULL, +-}; +- +-static struct stv6110x_config tt1600_stv6110x_config = { +- .addr = 0x60, +- .refclk = 27000000, +- .clk_div = 2, +-}; +- +-static struct isl6423_config tt1600_isl6423_config = { +- .current_max = SEC_CURRENT_515m, +- .curlim = SEC_CURRENT_LIM_ON, +- .mod_extern = 1, +- .addr = 0x08, +-}; +- +-static void frontend_init(struct budget *budget) +-{ +- (void)alps_bsbe1_config; /* avoid warning */ +- +- switch(budget->dev->pci->subsystem_device) { +- case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) +- case 0x1013: +- // try the ALPS BSRV2 first of all +- budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; +- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; +- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; +- budget->dvb_frontend->ops.set_tone = budget_set_tone; +- break; +- } +- +- // try the ALPS BSRU6 now +- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; +- budget->dvb_frontend->tuner_priv = &budget->i2c_adap; +- if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { +- budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; +- budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; +- budget->dvb_frontend->ops.set_tone = budget_set_tone; +- } +- break; +- } +- break; +- +- case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) +- +- budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; +- break; +- } +- break; +- +- case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) +- +- budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; +- budget->dvb_frontend->tuner_priv = NULL; +- break; +- } +- break; +- +- case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ +- { +- int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); +- +- if (subtype < 0) +- break; +- /* fixme: find a better way to identify the card */ +- if (subtype < 0x36) { +- /* assume ALPS BSRU6 */ +- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; +- budget->dvb_frontend->tuner_priv = &budget->i2c_adap; +- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; +- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; +- break; +- } +- } else { +- /* assume ALPS BSBE1 */ +- /* reset tuner */ +- saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); +- msleep(50); +- saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); +- msleep(250); +- budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; +- budget->dvb_frontend->tuner_priv = &budget->i2c_adap; +- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; +- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; +- break; +- } +- } +- break; +- } +- +- case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) +- budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; +- budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; +- budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; +- } +- break; +- +- case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ +- budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; +- budget->dvb_frontend->tuner_priv = &budget->i2c_adap; +- } +- break; +- +- case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ +- budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; +- budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; +- } +- break; +- +- case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) +- budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; +- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { +- printk("%s: No LNBP21 found!\n", __func__); +- goto error_out; +- } +- break; +- } +- +- case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) +- // gpio2 is connected to CLB - reset it + leave it high +- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); +- msleep(1); +- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); +- msleep(1); +- +- budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); +- if (budget->dvb_frontend) { +- if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) +- printk("%s: No tda826x found!\n", __func__); +- if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { +- printk("%s: No LNBP21 found!\n", __func__); +- goto error_out; +- } +- break; +- } +- +- case 0x101c: { /* TT S2-1600 */ +- struct stv6110x_devctl *ctl; +- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); +- msleep(50); +- saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); +- msleep(250); +- +- budget->dvb_frontend = dvb_attach(stv090x_attach, +- &tt1600_stv090x_config, +- &budget->i2c_adap, +- STV090x_DEMODULATOR_0); +- +- if (budget->dvb_frontend) { +- +- ctl = dvb_attach(stv6110x_attach, +- budget->dvb_frontend, +- &tt1600_stv6110x_config, +- &budget->i2c_adap); +- +- if (ctl) { +- tt1600_stv090x_config.tuner_init = ctl->tuner_init; +- tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; +- tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; +- tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; +- tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; +- tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; +- tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; +- tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; +- tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; +- tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; +- tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; +- +- /* call the init function once to initialize +- tuner's clock output divider and demod's +- master clock */ +- if (budget->dvb_frontend->ops.init) +- budget->dvb_frontend->ops.init(budget->dvb_frontend); +- +- if (dvb_attach(isl6423_attach, +- budget->dvb_frontend, +- &budget->i2c_adap, +- &tt1600_isl6423_config) == NULL) { +- printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); +- goto error_out; +- } +- } else { +- printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); +- goto error_out; +- } +- } +- } +- break; +- } +- +- if (budget->dvb_frontend == NULL) { +- printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", +- budget->dev->pci->vendor, +- budget->dev->pci->device, +- budget->dev->pci->subsystem_vendor, +- budget->dev->pci->subsystem_device); +- } else { +- if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) +- goto error_out; +- } +- return; +- +-error_out: +- printk("budget: Frontend registration failed!\n"); +- dvb_frontend_detach(budget->dvb_frontend); +- budget->dvb_frontend = NULL; +- return; +-} +- +-static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) +-{ +- struct budget *budget = NULL; +- int err; +- +- budget = kmalloc(sizeof(struct budget), GFP_KERNEL); +- if( NULL == budget ) { +- return -ENOMEM; +- } +- +- dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); +- +- dev->ext_priv = budget; +- +- err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); +- if (err) { +- printk("==> failed\n"); +- kfree (budget); +- return err; +- } +- +- budget->dvb_adapter.priv = budget; +- frontend_init(budget); +- +- ttpci_budget_init_hooks(budget); +- +- return 0; +-} +- +-static int budget_detach (struct saa7146_dev* dev) +-{ +- struct budget *budget = (struct budget*) dev->ext_priv; +- int err; +- +- if (budget->dvb_frontend) { +- dvb_unregister_frontend(budget->dvb_frontend); +- dvb_frontend_detach(budget->dvb_frontend); +- } +- +- err = ttpci_budget_deinit (budget); +- +- kfree (budget); +- dev->ext_priv = NULL; +- +- return err; +-} +- +-static struct saa7146_extension budget_extension; +- +-MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); +-MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); +-MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); +-MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); +-MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); +-MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); +- +-static struct pci_device_id pci_tbl[] = { +- MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), +- MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), +- MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), +- MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), +- MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), +- MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), +- MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), +- MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), +- MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), +- MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), +- MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), +- { +- .vendor = 0, +- } +-}; +- +-MODULE_DEVICE_TABLE(pci, pci_tbl); +- +-static struct saa7146_extension budget_extension = { +- .name = "budget dvb", +- .flags = SAA7146_USE_I2C_IRQ, +- +- .module = THIS_MODULE, +- .pci_tbl = pci_tbl, +- .attach = budget_attach, +- .detach = budget_detach, +- +- .irq_mask = MASK_10, +- .irq_func = ttpci_budget_irq10_handler, +-}; +- +-static int __init budget_init(void) +-{ +- return saa7146_register_extension(&budget_extension); +-} +- +-static void __exit budget_exit(void) +-{ +- saa7146_unregister_extension(&budget_extension); +-} +- +-module_init(budget_init); +-module_exit(budget_exit); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); +-MODULE_DESCRIPTION("driver for the SAA7146 based so-called " +- "budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h +deleted file mode 100644 +index 3d8a806..0000000 +--- a/drivers/media/dvb/ttpci/budget.h ++++ /dev/null +@@ -1,124 +0,0 @@ +- +-#ifndef __BUDGET_DVB__ +-#define __BUDGET_DVB__ +- +-#include "dvb_frontend.h" +-#include "dvbdev.h" +-#include "demux.h" +-#include "dvb_demux.h" +-#include "dmxdev.h" +-#include "dvb_filter.h" +-#include "dvb_net.h" +- +-#include +-#include +- +-#include +- +-extern int budget_debug; +- +-#ifdef dprintk +-#undef dprintk +-#endif +- +-#define dprintk(level,args...) \ +- do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0) +- +-struct budget_info { +- char *name; +- int type; +-}; +- +-/* place to store all the necessary device information */ +-struct budget { +- +- /* devices */ +- struct dvb_device dvb_dev; +- struct dvb_net dvb_net; +- +- struct saa7146_dev *dev; +- +- struct i2c_adapter i2c_adap; +- struct budget_info *card; +- +- unsigned char *grabbing; +- struct saa7146_pgtable pt; +- +- struct tasklet_struct fidb_tasklet; +- struct tasklet_struct vpe_tasklet; +- +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- +- struct dmx_frontend hw_frontend; +- struct dmx_frontend mem_frontend; +- +- int ci_present; +- int video_port; +- +- u32 buffer_width; +- u32 buffer_height; +- u32 buffer_size; +- u32 buffer_warning_threshold; +- u32 buffer_warnings; +- unsigned long buffer_warning_time; +- +- u32 ttbp; +- int feeding; +- +- spinlock_t feedlock; +- +- spinlock_t debilock; +- +- struct dvb_adapter dvb_adapter; +- struct dvb_frontend *dvb_frontend; +- int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status); +- int fe_synced; +- +- void *priv; +-}; +- +-#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ +-static struct budget_info x_var ## _info = { \ +- .name=x_name, \ +- .type=x_type }; \ +-static struct saa7146_pci_extension_data x_var = { \ +- .ext_priv = &x_var ## _info, \ +- .ext = &budget_extension }; +- +-#define BUDGET_TT 0 +-#define BUDGET_TT_HW_DISEQC 1 +-#define BUDGET_PATCH 3 +-#define BUDGET_FS_ACTIVY 4 +-#define BUDGET_CIN1200S 5 +-#define BUDGET_CIN1200C 6 +-#define BUDGET_CIN1200T 7 +-#define BUDGET_KNC1S 8 +-#define BUDGET_KNC1C 9 +-#define BUDGET_KNC1T 10 +-#define BUDGET_KNC1SP 11 +-#define BUDGET_KNC1CP 12 +-#define BUDGET_KNC1TP 13 +-#define BUDGET_TVSTAR 14 +-#define BUDGET_CIN1200C_MK3 15 +-#define BUDGET_KNC1C_MK3 16 +-#define BUDGET_KNC1CP_MK3 17 +-#define BUDGET_KNC1S2 18 +-#define BUDGET_KNC1C_TDA10024 19 +- +-#define BUDGET_VIDEO_PORTA 0 +-#define BUDGET_VIDEO_PORTB 1 +- +-extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, +- struct saa7146_pci_extension_data *info, +- struct module *owner, short *adapter_nums); +-extern void ttpci_budget_init_hooks(struct budget *budget); +-extern int ttpci_budget_deinit(struct budget *budget); +-extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); +-extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); +-extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, +- int uselocks, int nobusyloop); +-extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, +- int uselocks, int nobusyloop); +- +-#endif +diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.c b/drivers/media/dvb/ttpci/ttpci-eeprom.c +deleted file mode 100644 +index 32d4315..0000000 +--- a/drivers/media/dvb/ttpci/ttpci-eeprom.c ++++ /dev/null +@@ -1,176 +0,0 @@ +-/* +- Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM, +- decode it and store it in the associated adapter struct for +- use by dvb_net.c +- +- This card appear to have the 24C16 write protect held to ground, +- thus permitting normal read/write operation. Theoretically it +- would be possible to write routines to burn a different (encoded) +- MAC address into the EEPROM. +- +- Robert Schlabbach GMX +- Michael Glaum KVH Industries +- Holger Waechtler Convergence +- +- Copyright (C) 2002-2003 Ralph Metzler +- Metzler Brothers Systementwicklung GbR +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#include +-#include +-#include +-#include +-#include +- +-#include "ttpci-eeprom.h" +- +-#if 1 +-#define dprintk(x...) do { printk(x); } while (0) +-#else +-#define dprintk(x...) do { } while (0) +-#endif +- +- +-static int check_mac_tt(u8 *buf) +-{ +- int i; +- u16 tmp = 0xffff; +- +- for (i = 0; i < 8; i++) { +- tmp = (tmp << 8) | ((tmp >> 8) ^ buf[i]); +- tmp ^= (tmp >> 4) & 0x0f; +- tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5); +- } +- tmp ^= 0xffff; +- return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9])); +-} +- +-static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC) +-{ +- u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, +- 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, +- 0x1d, 0x36, 0x64, 0x78}; +- u8 data[20]; +- int i; +- +- /* In case there is a sig check failure have the orig contents available */ +- memcpy(data, encodedMAC, 20); +- +- for (i = 0; i < 20; i++) +- data[i] ^= xor[i]; +- for (i = 0; i < 10; i++) +- data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) +- >> ((data[2 * i + 1] >> 6) & 3); +- +- if (check_mac_tt(data)) +- return -ENODEV; +- +- decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0]; +- decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4]; +- return 0; +-} +- +-int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC) +-{ +- u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, +- 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, +- 0x1d, 0x36, 0x64, 0x78}; +- u8 data[20]; +- int i; +- +- memcpy(data, encodedMAC, 20); +- +- for (i = 0; i < 20; i++) +- data[i] ^= xor[i]; +- for (i = 0; i < 10; i++) +- data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) +- >> ((data[2 * i + 1] >> 6) & 3); +- +- if (check_mac_tt(data)) +- return -ENODEV; +- +- decodedMAC[0] = data[2]; +- decodedMAC[1] = data[1]; +- decodedMAC[2] = data[0]; +- decodedMAC[3] = data[6]; +- decodedMAC[4] = data[5]; +- decodedMAC[5] = data[4]; +- return 0; +-} +-EXPORT_SYMBOL(ttpci_eeprom_decode_mac); +- +-static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC) +-{ +- int ret; +- u8 b0[] = { 0xcc }; +- +- struct i2c_msg msg[] = { +- { .addr = 0x50, .flags = 0, .buf = b0, .len = 1 }, +- { .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 } +- }; +- +- /* dprintk("%s\n", __func__); */ +- +- ret = i2c_transfer(adapter, msg, 2); +- +- if (ret != 2) /* Assume EEPROM isn't there */ +- return (-ENODEV); +- +- return 0; +-} +- +- +-int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) +-{ +- int ret, i; +- u8 encodedMAC[20]; +- u8 decodedMAC[6]; +- +- ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC); +- +- if (ret != 0) { /* Will only be -ENODEV */ +- dprintk("Couldn't read from EEPROM: not there?\n"); +- memset(proposed_mac, 0, 6); +- return ret; +- } +- +- ret = getmac_tt(decodedMAC, encodedMAC); +- if( ret != 0 ) { +- dprintk("adapter failed MAC signature check\n"); +- dprintk("encoded MAC from EEPROM was " ); +- for(i=0; i<19; i++) { +- dprintk( "%.2x:", encodedMAC[i]); +- } +- dprintk("%.2x\n", encodedMAC[19]); +- memset(proposed_mac, 0, 6); +- return ret; +- } +- +- memcpy(proposed_mac, decodedMAC, 6); +- dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", +- decodedMAC[0], decodedMAC[1], decodedMAC[2], +- decodedMAC[3], decodedMAC[4], decodedMAC[5]); +- return 0; +-} +- +-EXPORT_SYMBOL(ttpci_eeprom_parse_mac); +- +-MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); +-MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards " +- "made by Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/dvb/ttpci/ttpci-eeprom.h b/drivers/media/dvb/ttpci/ttpci-eeprom.h +deleted file mode 100644 +index dcc33d5..0000000 +--- a/drivers/media/dvb/ttpci/ttpci-eeprom.h ++++ /dev/null +@@ -1,34 +0,0 @@ +-/* +- Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM, +- decode it and store it in associated adapter net device +- +- Robert Schlabbach GMX +- Michael Glaum KVH Industries +- Holger Waechtler Convergence +- +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- +-*/ +- +-#ifndef __TTPCI_EEPROM_H__ +-#define __TTPCI_EEPROM_H__ +- +-#include +-#include +- +-extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC); +-extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac); +- +-#endif +diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig +deleted file mode 100644 +index 2663ae3..0000000 +--- a/drivers/media/dvb/ttusb-budget/Kconfig ++++ /dev/null +@@ -1,18 +0,0 @@ +-config DVB_TTUSB_BUDGET +- tristate "Technotrend/Hauppauge Nova-USB devices" +- depends on DVB_CORE && USB && I2C && PCI +- select DVB_CX22700 if !DVB_FE_CUSTOMISE +- select DVB_TDA1004X if !DVB_FE_CUSTOMISE +- select DVB_VES1820 if !DVB_FE_CUSTOMISE +- select DVB_TDA8083 if !DVB_FE_CUSTOMISE +- select DVB_STV0299 if !DVB_FE_CUSTOMISE +- select DVB_STV0297 if !DVB_FE_CUSTOMISE +- select DVB_LNBP21 if !DVB_FE_CUSTOMISE +- help +- Support for external USB adapters designed by Technotrend and +- produced by Hauppauge, shipped under the brand name 'Nova-USB'. +- +- These devices don't have a MPEG decoder built in, so you need +- an external software decoder to watch TV. +- +- Say Y if you own such a device and want to use it. +diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile +deleted file mode 100644 +index 8d6c4ac..0000000 +--- a/drivers/media/dvb/ttusb-budget/Makefile ++++ /dev/null +@@ -1,3 +0,0 @@ +-obj-$(CONFIG_DVB_TTUSB_BUDGET) += dvb-ttusb-budget.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends +diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c +deleted file mode 100644 +index 5b682cc..0000000 +--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c ++++ /dev/null +@@ -1,1816 +0,0 @@ +-/* +- * TTUSB DVB driver +- * +- * Copyright (c) 2002 Holger Waechtler +- * Copyright (c) 2003 Felix Domke +- * +- * This program is free software; you can redistribute it and/or +- * modify it under the terms of the GNU General Public License as +- * published by the Free Software Foundation; either version 2 of +- * the License, or (at your option) any later version. +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "dvb_frontend.h" +-#include "dmxdev.h" +-#include "dvb_demux.h" +-#include "dvb_net.h" +-#include "ves1820.h" +-#include "cx22700.h" +-#include "tda1004x.h" +-#include "stv0299.h" +-#include "tda8083.h" +-#include "stv0297.h" +-#include "lnbp21.h" +- +-#include +-#include +-#include +- +-/* +- TTUSB_HWSECTIONS: +- the DSP supports filtering in hardware, however, since the "muxstream" +- is a bit braindead (no matching channel masks or no matching filter mask), +- we won't support this - yet. it doesn't event support negative filters, +- so the best way is maybe to keep TTUSB_HWSECTIONS undef'd and just +- parse TS data. USB bandwidth will be a problem when having large +- datastreams, especially for dvb-net, but hey, that's not my problem. +- +- TTUSB_DISEQC, TTUSB_TONE: +- let the STC do the diseqc/tone stuff. this isn't supported at least with +- my TTUSB, so let it undef'd unless you want to implement another +- frontend. never tested. +- +- debug: +- define it to > 3 for really hardcore debugging. you probably don't want +- this unless the device doesn't load at all. > 2 for bandwidth statistics. +-*/ +- +-static int debug; +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define dprintk(x...) do { if (debug) printk(KERN_DEBUG x); } while (0) +- +-#define ISO_BUF_COUNT 4 +-#define FRAMES_PER_ISO_BUF 4 +-#define ISO_FRAME_SIZE 912 +-#define TTUSB_MAXCHANNEL 32 +-#ifdef TTUSB_HWSECTIONS +-#define TTUSB_MAXFILTER 16 /* ??? */ +-#endif +- +-#define TTUSB_REV_2_2 0x22 +-#define TTUSB_BUDGET_NAME "ttusb_stc_fw" +- +-/** +- * since we're casting (struct ttusb*) <-> (struct dvb_demux*) around +- * the dvb_demux field must be the first in struct!! +- */ +-struct ttusb { +- struct dvb_demux dvb_demux; +- struct dmxdev dmxdev; +- struct dvb_net dvbnet; +- +- /* and one for USB access. */ +- struct mutex semi2c; +- struct mutex semusb; +- +- struct dvb_adapter adapter; +- struct usb_device *dev; +- +- struct i2c_adapter i2c_adap; +- +- int disconnecting; +- int iso_streaming; +- +- unsigned int bulk_out_pipe; +- unsigned int bulk_in_pipe; +- unsigned int isoc_in_pipe; +- +- void *iso_buffer; +- dma_addr_t iso_dma_handle; +- +- struct urb *iso_urb[ISO_BUF_COUNT]; +- +- int running_feed_count; +- int last_channel; +- int last_filter; +- +- u8 c; /* transaction counter, wraps around... */ +- fe_sec_tone_mode_t tone; +- fe_sec_voltage_t voltage; +- +- int mux_state; // 0..2 - MuxSyncWord, 3 - nMuxPacks, 4 - muxpack +- u8 mux_npacks; +- u8 muxpack[256 + 8]; +- int muxpack_ptr, muxpack_len; +- +- int insync; +- +- int cc; /* MuxCounter - will increment on EVERY MUX PACKET */ +- /* (including stuffing. yes. really.) */ +- +- u8 last_result[32]; +- +- int revision; +- +- struct dvb_frontend* fe; +-}; +- +-/* ugly workaround ... don't know why it's necessary to read */ +-/* all result codes. */ +- +-static int ttusb_cmd(struct ttusb *ttusb, +- const u8 * data, int len, int needresult) +-{ +- int actual_len; +- int err; +- int i; +- +- if (debug >= 3) { +- printk(KERN_DEBUG ">"); +- for (i = 0; i < len; ++i) +- printk(KERN_CONT " %02x", data[i]); +- printk(KERN_CONT "\n"); +- } +- +- if (mutex_lock_interruptible(&ttusb->semusb) < 0) +- return -EAGAIN; +- +- err = usb_bulk_msg(ttusb->dev, ttusb->bulk_out_pipe, +- (u8 *) data, len, &actual_len, 1000); +- if (err != 0) { +- dprintk("%s: usb_bulk_msg(send) failed, err == %i!\n", +- __func__, err); +- mutex_unlock(&ttusb->semusb); +- return err; +- } +- if (actual_len != len) { +- dprintk("%s: only wrote %d of %d bytes\n", __func__, +- actual_len, len); +- mutex_unlock(&ttusb->semusb); +- return -1; +- } +- +- err = usb_bulk_msg(ttusb->dev, ttusb->bulk_in_pipe, +- ttusb->last_result, 32, &actual_len, 1000); +- +- if (err != 0) { +- printk("%s: failed, receive error %d\n", __func__, +- err); +- mutex_unlock(&ttusb->semusb); +- return err; +- } +- +- if (debug >= 3) { +- actual_len = ttusb->last_result[3] + 4; +- printk(KERN_DEBUG "<"); +- for (i = 0; i < actual_len; ++i) +- printk(KERN_CONT " %02x", ttusb->last_result[i]); +- printk(KERN_CONT "\n"); +- } +- +- if (!needresult) +- mutex_unlock(&ttusb->semusb); +- return 0; +-} +- +-static int ttusb_result(struct ttusb *ttusb, u8 * data, int len) +-{ +- memcpy(data, ttusb->last_result, len); +- mutex_unlock(&ttusb->semusb); +- return 0; +-} +- +-static int ttusb_i2c_msg(struct ttusb *ttusb, +- u8 addr, u8 * snd_buf, u8 snd_len, u8 * rcv_buf, +- u8 rcv_len) +-{ +- u8 b[0x28]; +- u8 id = ++ttusb->c; +- int i, err; +- +- if (snd_len > 0x28 - 7 || rcv_len > 0x20 - 7) +- return -EINVAL; +- +- b[0] = 0xaa; +- b[1] = id; +- b[2] = 0x31; +- b[3] = snd_len + 3; +- b[4] = addr << 1; +- b[5] = snd_len; +- b[6] = rcv_len; +- +- for (i = 0; i < snd_len; i++) +- b[7 + i] = snd_buf[i]; +- +- err = ttusb_cmd(ttusb, b, snd_len + 7, 1); +- +- if (err) +- return -EREMOTEIO; +- +- err = ttusb_result(ttusb, b, 0x20); +- +- /* check if the i2c transaction was successful */ +- if ((snd_len != b[5]) || (rcv_len != b[6])) return -EREMOTEIO; +- +- if (rcv_len > 0) { +- +- if (err || b[0] != 0x55 || b[1] != id) { +- dprintk +- ("%s: usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ", +- __func__, err, id); +- return -EREMOTEIO; +- } +- +- for (i = 0; i < rcv_len; i++) +- rcv_buf[i] = b[7 + i]; +- } +- +- return rcv_len; +-} +- +-static int master_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num) +-{ +- struct ttusb *ttusb = i2c_get_adapdata(adapter); +- int i = 0; +- int inc; +- +- if (mutex_lock_interruptible(&ttusb->semi2c) < 0) +- return -EAGAIN; +- +- while (i < num) { +- u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; +- int err; +- +- if (num > i + 1 && (msg[i + 1].flags & I2C_M_RD)) { +- addr = msg[i].addr; +- snd_buf = msg[i].buf; +- snd_len = msg[i].len; +- rcv_buf = msg[i + 1].buf; +- rcv_len = msg[i + 1].len; +- inc = 2; +- } else { +- addr = msg[i].addr; +- snd_buf = msg[i].buf; +- snd_len = msg[i].len; +- rcv_buf = NULL; +- rcv_len = 0; +- inc = 1; +- } +- +- err = ttusb_i2c_msg(ttusb, addr, +- snd_buf, snd_len, rcv_buf, rcv_len); +- +- if (err < rcv_len) { +- dprintk("%s: i == %i\n", __func__, i); +- break; +- } +- +- i += inc; +- } +- +- mutex_unlock(&ttusb->semi2c); +- return i; +-} +- +-static int ttusb_boot_dsp(struct ttusb *ttusb) +-{ +- const struct firmware *fw; +- int i, err; +- u8 b[40]; +- +- err = request_firmware(&fw, "ttusb-budget/dspbootcode.bin", +- &ttusb->dev->dev); +- if (err) { +- printk(KERN_ERR "ttusb-budget: failed to request firmware\n"); +- return err; +- } +- +- /* BootBlock */ +- b[0] = 0xaa; +- b[2] = 0x13; +- b[3] = 28; +- +- /* upload dsp code in 32 byte steps (36 didn't work for me ...) */ +- /* 32 is max packet size, no messages should be splitted. */ +- for (i = 0; i < fw->size; i += 28) { +- memcpy(&b[4], &fw->data[i], 28); +- +- b[1] = ++ttusb->c; +- +- err = ttusb_cmd(ttusb, b, 32, 0); +- if (err) +- goto done; +- } +- +- /* last block ... */ +- b[1] = ++ttusb->c; +- b[2] = 0x13; +- b[3] = 0; +- +- err = ttusb_cmd(ttusb, b, 4, 0); +- if (err) +- goto done; +- +- /* BootEnd */ +- b[1] = ++ttusb->c; +- b[2] = 0x14; +- b[3] = 0; +- +- err = ttusb_cmd(ttusb, b, 4, 0); +- +- done: +- release_firmware(fw); +- if (err) { +- dprintk("%s: usb_bulk_msg() failed, return value %i!\n", +- __func__, err); +- } +- +- return err; +-} +- +-static int ttusb_set_channel(struct ttusb *ttusb, int chan_id, int filter_type, +- int pid) +-{ +- int err; +- /* SetChannel */ +- u8 b[] = { 0xaa, ++ttusb->c, 0x22, 4, chan_id, filter_type, +- (pid >> 8) & 0xff, pid & 0xff +- }; +- +- err = ttusb_cmd(ttusb, b, sizeof(b), 0); +- return err; +-} +- +-static int ttusb_del_channel(struct ttusb *ttusb, int channel_id) +-{ +- int err; +- /* DelChannel */ +- u8 b[] = { 0xaa, ++ttusb->c, 0x23, 1, channel_id }; +- +- err = ttusb_cmd(ttusb, b, sizeof(b), 0); +- return err; +-} +- +-#ifdef TTUSB_HWSECTIONS +-static int ttusb_set_filter(struct ttusb *ttusb, int filter_id, +- int associated_chan, u8 filter[8], u8 mask[8]) +-{ +- int err; +- /* SetFilter */ +- u8 b[] = { 0xaa, 0, 0x24, 0x1a, filter_id, associated_chan, +- filter[0], filter[1], filter[2], filter[3], +- filter[4], filter[5], filter[6], filter[7], +- filter[8], filter[9], filter[10], filter[11], +- mask[0], mask[1], mask[2], mask[3], +- mask[4], mask[5], mask[6], mask[7], +- mask[8], mask[9], mask[10], mask[11] +- }; +- +- err = ttusb_cmd(ttusb, b, sizeof(b), 0); +- return err; +-} +- +-static int ttusb_del_filter(struct ttusb *ttusb, int filter_id) +-{ +- int err; +- /* DelFilter */ +- u8 b[] = { 0xaa, ++ttusb->c, 0x25, 1, filter_id }; +- +- err = ttusb_cmd(ttusb, b, sizeof(b), 0); +- return err; +-} +-#endif +- +-static int ttusb_init_controller(struct ttusb *ttusb) +-{ +- u8 b0[] = { 0xaa, ++ttusb->c, 0x15, 1, 0 }; +- u8 b1[] = { 0xaa, ++ttusb->c, 0x15, 1, 1 }; +- u8 b2[] = { 0xaa, ++ttusb->c, 0x32, 1, 0 }; +- /* i2c write read: 5 bytes, addr 0x10, 0x02 bytes write, 1 bytes read. */ +- u8 b3[] = +- { 0xaa, ++ttusb->c, 0x31, 5, 0x10, 0x02, 0x01, 0x00, 0x1e }; +- u8 b4[] = +- { 0x55, ttusb->c, 0x31, 4, 0x10, 0x02, 0x01, 0x00, 0x1e }; +- +- u8 get_version[] = { 0xaa, ++ttusb->c, 0x17, 5, 0, 0, 0, 0, 0 }; +- u8 get_dsp_version[0x20] = +- { 0xaa, ++ttusb->c, 0x26, 28, 0, 0, 0, 0, 0 }; +- int err; +- +- /* reset board */ +- if ((err = ttusb_cmd(ttusb, b0, sizeof(b0), 0))) +- return err; +- +- /* reset board (again?) */ +- if ((err = ttusb_cmd(ttusb, b1, sizeof(b1), 0))) +- return err; +- +- ttusb_boot_dsp(ttusb); +- +- /* set i2c bit rate */ +- if ((err = ttusb_cmd(ttusb, b2, sizeof(b2), 0))) +- return err; +- +- if ((err = ttusb_cmd(ttusb, b3, sizeof(b3), 1))) +- return err; +- +- err = ttusb_result(ttusb, b4, sizeof(b4)); +- +- if ((err = ttusb_cmd(ttusb, get_version, sizeof(get_version), 1))) +- return err; +- +- if ((err = ttusb_result(ttusb, get_version, sizeof(get_version)))) +- return err; +- +- dprintk("%s: stc-version: %c%c%c%c%c\n", __func__, +- get_version[4], get_version[5], get_version[6], +- get_version[7], get_version[8]); +- +- if (memcmp(get_version + 4, "V 0.0", 5) && +- memcmp(get_version + 4, "V 1.1", 5) && +- memcmp(get_version + 4, "V 2.1", 5) && +- memcmp(get_version + 4, "V 2.2", 5)) { +- printk +- ("%s: unknown STC version %c%c%c%c%c, please report!\n", +- __func__, get_version[4], get_version[5], +- get_version[6], get_version[7], get_version[8]); +- } +- +- ttusb->revision = ((get_version[6] - '0') << 4) | +- (get_version[8] - '0'); +- +- err = +- ttusb_cmd(ttusb, get_dsp_version, sizeof(get_dsp_version), 1); +- if (err) +- return err; +- +- err = +- ttusb_result(ttusb, get_dsp_version, sizeof(get_dsp_version)); +- if (err) +- return err; +- printk("%s: dsp-version: %c%c%c\n", __func__, +- get_dsp_version[4], get_dsp_version[5], get_dsp_version[6]); +- return 0; +-} +- +-#ifdef TTUSB_DISEQC +-static int ttusb_send_diseqc(struct dvb_frontend* fe, +- const struct dvb_diseqc_master_cmd *cmd) +-{ +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- u8 b[12] = { 0xaa, ++ttusb->c, 0x18 }; +- +- int err; +- +- b[3] = 4 + 2 + cmd->msg_len; +- b[4] = 0xFF; /* send diseqc master, not burst */ +- b[5] = cmd->msg_len; +- +- memcpy(b + 5, cmd->msg, cmd->msg_len); +- +- /* Diseqc */ +- if ((err = ttusb_cmd(ttusb, b, 4 + b[3], 0))) { +- dprintk("%s: usb_bulk_msg() failed, return value %i!\n", +- __func__, err); +- } +- +- return err; +-} +-#endif +- +-static int ttusb_update_lnb(struct ttusb *ttusb) +-{ +- u8 b[] = { 0xaa, ++ttusb->c, 0x16, 5, /*power: */ 1, +- ttusb->voltage == SEC_VOLTAGE_18 ? 0 : 1, +- ttusb->tone == SEC_TONE_ON ? 1 : 0, 1, 1 +- }; +- int err; +- +- /* SetLNB */ +- if ((err = ttusb_cmd(ttusb, b, sizeof(b), 0))) { +- dprintk("%s: usb_bulk_msg() failed, return value %i!\n", +- __func__, err); +- } +- +- return err; +-} +- +-static int ttusb_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- +- ttusb->voltage = voltage; +- return ttusb_update_lnb(ttusb); +-} +- +-#ifdef TTUSB_TONE +-static int ttusb_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- +- ttusb->tone = tone; +- return ttusb_update_lnb(ttusb); +-} +-#endif +- +- +-#if 0 +-static void ttusb_set_led_freq(struct ttusb *ttusb, u8 freq) +-{ +- u8 b[] = { 0xaa, ++ttusb->c, 0x19, 1, freq }; +- int err, actual_len; +- +- err = ttusb_cmd(ttusb, b, sizeof(b), 0); +- if (err) { +- dprintk("%s: usb_bulk_msg() failed, return value %i!\n", +- __func__, err); +- } +-} +-#endif +- +-/*****************************************************************************/ +- +-#ifdef TTUSB_HWSECTIONS +-static void ttusb_handle_ts_data(struct ttusb_channel *channel, +- const u8 * data, int len); +-static void ttusb_handle_sec_data(struct ttusb_channel *channel, +- const u8 * data, int len); +-#endif +- +-static int numpkt, numts, numstuff, numsec, numinvalid; +-static unsigned long lastj; +- +-static void ttusb_process_muxpack(struct ttusb *ttusb, const u8 * muxpack, +- int len) +-{ +- u16 csum = 0, cc; +- int i; +- for (i = 0; i < len; i += 2) +- csum ^= le16_to_cpup((__le16 *) (muxpack + i)); +- if (csum) { +- printk("%s: muxpack with incorrect checksum, ignoring\n", +- __func__); +- numinvalid++; +- return; +- } +- +- cc = (muxpack[len - 4] << 8) | muxpack[len - 3]; +- cc &= 0x7FFF; +- if ((cc != ttusb->cc) && (ttusb->cc != -1)) +- printk("%s: cc discontinuity (%d frames missing)\n", +- __func__, (cc - ttusb->cc) & 0x7FFF); +- ttusb->cc = (cc + 1) & 0x7FFF; +- if (muxpack[0] & 0x80) { +-#ifdef TTUSB_HWSECTIONS +- /* section data */ +- int pusi = muxpack[0] & 0x40; +- int channel = muxpack[0] & 0x1F; +- int payload = muxpack[1]; +- const u8 *data = muxpack + 2; +- /* check offset flag */ +- if (muxpack[0] & 0x20) +- data++; +- +- ttusb_handle_sec_data(ttusb->channel + channel, data, +- payload); +- data += payload; +- +- if ((!!(ttusb->muxpack[0] & 0x20)) ^ +- !!(ttusb->muxpack[1] & 1)) +- data++; +-#warning TODO: pusi +- printk("cc: %04x\n", (data[0] << 8) | data[1]); +-#endif +- numsec++; +- } else if (muxpack[0] == 0x47) { +-#ifdef TTUSB_HWSECTIONS +- /* we have TS data here! */ +- int pid = ((muxpack[1] & 0x0F) << 8) | muxpack[2]; +- int channel; +- for (channel = 0; channel < TTUSB_MAXCHANNEL; ++channel) +- if (ttusb->channel[channel].active +- && (pid == ttusb->channel[channel].pid)) +- ttusb_handle_ts_data(ttusb->channel + +- channel, muxpack, +- 188); +-#endif +- numts++; +- dvb_dmx_swfilter_packets(&ttusb->dvb_demux, muxpack, 1); +- } else if (muxpack[0] != 0) { +- numinvalid++; +- printk("illegal muxpack type %02x\n", muxpack[0]); +- } else +- numstuff++; +-} +- +-static void ttusb_process_frame(struct ttusb *ttusb, u8 * data, int len) +-{ +- int maxwork = 1024; +- while (len) { +- if (!(maxwork--)) { +- printk("%s: too much work\n", __func__); +- break; +- } +- +- switch (ttusb->mux_state) { +- case 0: +- case 1: +- case 2: +- len--; +- if (*data++ == 0xAA) +- ++ttusb->mux_state; +- else { +- ttusb->mux_state = 0; +- if (ttusb->insync) { +- dprintk("%s: %02x\n", +- __func__, data[-1]); +- printk(KERN_INFO "%s: lost sync.\n", +- __func__); +- ttusb->insync = 0; +- } +- } +- break; +- case 3: +- ttusb->insync = 1; +- len--; +- ttusb->mux_npacks = *data++; +- ++ttusb->mux_state; +- ttusb->muxpack_ptr = 0; +- /* maximum bytes, until we know the length */ +- ttusb->muxpack_len = 2; +- break; +- case 4: +- { +- int avail; +- avail = len; +- if (avail > +- (ttusb->muxpack_len - +- ttusb->muxpack_ptr)) +- avail = +- ttusb->muxpack_len - +- ttusb->muxpack_ptr; +- memcpy(ttusb->muxpack + ttusb->muxpack_ptr, +- data, avail); +- ttusb->muxpack_ptr += avail; +- BUG_ON(ttusb->muxpack_ptr > 264); +- data += avail; +- len -= avail; +- /* determine length */ +- if (ttusb->muxpack_ptr == 2) { +- if (ttusb->muxpack[0] & 0x80) { +- ttusb->muxpack_len = +- ttusb->muxpack[1] + 2; +- if (ttusb-> +- muxpack[0] & 0x20) +- ttusb-> +- muxpack_len++; +- if ((!! +- (ttusb-> +- muxpack[0] & 0x20)) ^ +- !!(ttusb-> +- muxpack[1] & 1)) +- ttusb-> +- muxpack_len++; +- ttusb->muxpack_len += 4; +- } else if (ttusb->muxpack[0] == +- 0x47) +- ttusb->muxpack_len = +- 188 + 4; +- else if (ttusb->muxpack[0] == 0x00) +- ttusb->muxpack_len = +- ttusb->muxpack[1] + 2 + +- 4; +- else { +- dprintk +- ("%s: invalid state: first byte is %x\n", +- __func__, +- ttusb->muxpack[0]); +- ttusb->mux_state = 0; +- } +- } +- +- /** +- * if length is valid and we reached the end: +- * goto next muxpack +- */ +- if ((ttusb->muxpack_ptr >= 2) && +- (ttusb->muxpack_ptr == +- ttusb->muxpack_len)) { +- ttusb_process_muxpack(ttusb, +- ttusb-> +- muxpack, +- ttusb-> +- muxpack_ptr); +- ttusb->muxpack_ptr = 0; +- /* maximum bytes, until we know the length */ +- ttusb->muxpack_len = 2; +- +- /** +- * no muxpacks left? +- * return to search-sync state +- */ +- if (!ttusb->mux_npacks--) { +- ttusb->mux_state = 0; +- break; +- } +- } +- break; +- } +- default: +- BUG(); +- break; +- } +- } +-} +- +-static void ttusb_iso_irq(struct urb *urb) +-{ +- struct ttusb *ttusb = urb->context; +- struct usb_iso_packet_descriptor *d; +- u8 *data; +- int len, i; +- +- if (!ttusb->iso_streaming) +- return; +- +-#if 0 +- printk("%s: status %d, errcount == %d, length == %i\n", +- __func__, +- urb->status, urb->error_count, urb->actual_length); +-#endif +- +- if (!urb->status) { +- for (i = 0; i < urb->number_of_packets; ++i) { +- numpkt++; +- if (time_after_eq(jiffies, lastj + HZ)) { +- dprintk("frames/s: %lu (ts: %d, stuff %d, " +- "sec: %d, invalid: %d, all: %d)\n", +- numpkt * HZ / (jiffies - lastj), +- numts, numstuff, numsec, numinvalid, +- numts + numstuff + numsec + numinvalid); +- numts = numstuff = numsec = numinvalid = 0; +- lastj = jiffies; +- numpkt = 0; +- } +- d = &urb->iso_frame_desc[i]; +- data = urb->transfer_buffer + d->offset; +- len = d->actual_length; +- d->actual_length = 0; +- d->status = 0; +- ttusb_process_frame(ttusb, data, len); +- } +- } +- usb_submit_urb(urb, GFP_ATOMIC); +-} +- +-static void ttusb_free_iso_urbs(struct ttusb *ttusb) +-{ +- int i; +- +- for (i = 0; i < ISO_BUF_COUNT; i++) +- if (ttusb->iso_urb[i]) +- usb_free_urb(ttusb->iso_urb[i]); +- +- pci_free_consistent(NULL, +- ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * +- ISO_BUF_COUNT, ttusb->iso_buffer, +- ttusb->iso_dma_handle); +-} +- +-static int ttusb_alloc_iso_urbs(struct ttusb *ttusb) +-{ +- int i; +- +- ttusb->iso_buffer = pci_alloc_consistent(NULL, +- ISO_FRAME_SIZE * +- FRAMES_PER_ISO_BUF * +- ISO_BUF_COUNT, +- &ttusb->iso_dma_handle); +- +- if (!ttusb->iso_buffer) { +- dprintk("%s: pci_alloc_consistent - not enough memory\n", +- __func__); +- return -ENOMEM; +- } +- +- memset(ttusb->iso_buffer, 0, +- ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT); +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- struct urb *urb; +- +- if (! +- (urb = +- usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) { +- ttusb_free_iso_urbs(ttusb); +- return -ENOMEM; +- } +- +- ttusb->iso_urb[i] = urb; +- } +- +- return 0; +-} +- +-static void ttusb_stop_iso_xfer(struct ttusb *ttusb) +-{ +- int i; +- +- for (i = 0; i < ISO_BUF_COUNT; i++) +- usb_kill_urb(ttusb->iso_urb[i]); +- +- ttusb->iso_streaming = 0; +-} +- +-static int ttusb_start_iso_xfer(struct ttusb *ttusb) +-{ +- int i, j, err, buffer_offset = 0; +- +- if (ttusb->iso_streaming) { +- printk("%s: iso xfer already running!\n", __func__); +- return 0; +- } +- +- ttusb->cc = -1; +- ttusb->insync = 0; +- ttusb->mux_state = 0; +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- int frame_offset = 0; +- struct urb *urb = ttusb->iso_urb[i]; +- +- urb->dev = ttusb->dev; +- urb->context = ttusb; +- urb->complete = ttusb_iso_irq; +- urb->pipe = ttusb->isoc_in_pipe; +- urb->transfer_flags = URB_ISO_ASAP; +- urb->interval = 1; +- urb->number_of_packets = FRAMES_PER_ISO_BUF; +- urb->transfer_buffer_length = +- ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; +- urb->transfer_buffer = ttusb->iso_buffer + buffer_offset; +- buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; +- +- for (j = 0; j < FRAMES_PER_ISO_BUF; j++) { +- urb->iso_frame_desc[j].offset = frame_offset; +- urb->iso_frame_desc[j].length = ISO_FRAME_SIZE; +- frame_offset += ISO_FRAME_SIZE; +- } +- } +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- if ((err = usb_submit_urb(ttusb->iso_urb[i], GFP_ATOMIC))) { +- ttusb_stop_iso_xfer(ttusb); +- printk +- ("%s: failed urb submission (%i: err = %i)!\n", +- __func__, i, err); +- return err; +- } +- } +- +- ttusb->iso_streaming = 1; +- +- return 0; +-} +- +-#ifdef TTUSB_HWSECTIONS +-static void ttusb_handle_ts_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data, +- int len) +-{ +- dvbdmxfeed->cb.ts(data, len, 0, 0, &dvbdmxfeed->feed.ts, 0); +-} +- +-static void ttusb_handle_sec_data(struct dvb_demux_feed *dvbdmxfeed, const u8 * data, +- int len) +-{ +-// struct dvb_demux_feed *dvbdmxfeed = channel->dvbdmxfeed; +-#error TODO: handle ugly stuff +-// dvbdmxfeed->cb.sec(data, len, 0, 0, &dvbdmxfeed->feed.sec, 0); +-} +-#endif +- +-static int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux; +- int feed_type = 1; +- +- dprintk("ttusb_start_feed\n"); +- +- switch (dvbdmxfeed->type) { +- case DMX_TYPE_TS: +- break; +- case DMX_TYPE_SEC: +- break; +- default: +- return -EINVAL; +- } +- +- if (dvbdmxfeed->type == DMX_TYPE_TS) { +- switch (dvbdmxfeed->pes_type) { +- case DMX_TS_PES_VIDEO: +- case DMX_TS_PES_AUDIO: +- case DMX_TS_PES_TELETEXT: +- case DMX_TS_PES_PCR: +- case DMX_TS_PES_OTHER: +- break; +- default: +- return -EINVAL; +- } +- } +- +-#ifdef TTUSB_HWSECTIONS +-#error TODO: allocate filters +- if (dvbdmxfeed->type == DMX_TYPE_TS) { +- feed_type = 1; +- } else if (dvbdmxfeed->type == DMX_TYPE_SEC) { +- feed_type = 2; +- } +-#endif +- +- ttusb_set_channel(ttusb, dvbdmxfeed->index, feed_type, dvbdmxfeed->pid); +- +- if (0 == ttusb->running_feed_count++) +- ttusb_start_iso_xfer(ttusb); +- +- return 0; +-} +- +-static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct ttusb *ttusb = (struct ttusb *) dvbdmxfeed->demux; +- +- ttusb_del_channel(ttusb, dvbdmxfeed->index); +- +- if (--ttusb->running_feed_count == 0) +- ttusb_stop_iso_xfer(ttusb); +- +- return 0; +-} +- +-static int ttusb_setup_interfaces(struct ttusb *ttusb) +-{ +- usb_set_interface(ttusb->dev, 1, 1); +- +- ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1); +- ttusb->bulk_in_pipe = usb_rcvbulkpipe(ttusb->dev, 1); +- ttusb->isoc_in_pipe = usb_rcvisocpipe(ttusb->dev, 2); +- +- return 0; +-} +- +-#if 0 +-static u8 stc_firmware[8192]; +- +-static int stc_open(struct inode *inode, struct file *file) +-{ +- struct ttusb *ttusb = file->private_data; +- int addr; +- +- for (addr = 0; addr < 8192; addr += 16) { +- u8 snd_buf[2] = { addr >> 8, addr & 0xFF }; +- ttusb_i2c_msg(ttusb, 0x50, snd_buf, 2, stc_firmware + addr, +- 16); +- } +- +- return 0; +-} +- +-static ssize_t stc_read(struct file *file, char *buf, size_t count, +- loff_t *offset) +-{ +- return simple_read_from_buffer(buf, count, offset, stc_firmware, 8192); +-} +- +-static int stc_release(struct inode *inode, struct file *file) +-{ +- return 0; +-} +- +-static const struct file_operations stc_fops = { +- .owner = THIS_MODULE, +- .read = stc_read, +- .open = stc_open, +- .release = stc_release, +-}; +-#endif +- +-static u32 functionality(struct i2c_adapter *adapter) +-{ +- return I2C_FUNC_I2C; +-} +- +- +- +-static int alps_tdmb7_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- u8 data[4]; +- struct i2c_msg msg = {.addr=0x61, .flags=0, .buf=data, .len=sizeof(data) }; +- u32 div; +- +- div = (p->frequency + 36166667) / 166667; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = ((div >> 10) & 0x60) | 0x85; +- data[3] = p->frequency < 592000000 ? 0x40 : 0x80; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) return -EIO; +- return 0; +-} +- +-static struct cx22700_config alps_tdmb7_config = { +- .demod_address = 0x43, +-}; +- +- +- +- +- +-static int philips_tdm1316l_tuner_init(struct dvb_frontend* fe) +-{ +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; +- static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; +- struct i2c_msg tuner_msg = { .addr=0x60, .flags=0, .buf=td1316_init, .len=sizeof(td1316_init) }; +- +- // setup PLL configuration +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) return -EIO; +- msleep(1); +- +- // disable the mc44BC374c (do not check for errors) +- tuner_msg.addr = 0x65; +- tuner_msg.buf = disable_mc44BC374c; +- tuner_msg.len = sizeof(disable_mc44BC374c); +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { +- i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1); +- } +- +- return 0; +-} +- +-static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- u8 tuner_buf[4]; +- struct i2c_msg tuner_msg = {.addr=0x60, .flags=0, .buf=tuner_buf, .len=sizeof(tuner_buf) }; +- int tuner_frequency = 0; +- u8 band, cp, filter; +- +- // determine charge pump +- tuner_frequency = p->frequency + 36130000; +- if (tuner_frequency < 87000000) return -EINVAL; +- else if (tuner_frequency < 130000000) cp = 3; +- else if (tuner_frequency < 160000000) cp = 5; +- else if (tuner_frequency < 200000000) cp = 6; +- else if (tuner_frequency < 290000000) cp = 3; +- else if (tuner_frequency < 420000000) cp = 5; +- else if (tuner_frequency < 480000000) cp = 6; +- else if (tuner_frequency < 620000000) cp = 3; +- else if (tuner_frequency < 830000000) cp = 5; +- else if (tuner_frequency < 895000000) cp = 7; +- else return -EINVAL; +- +- // determine band +- if (p->frequency < 49000000) +- return -EINVAL; +- else if (p->frequency < 159000000) +- band = 1; +- else if (p->frequency < 444000000) +- band = 2; +- else if (p->frequency < 861000000) +- band = 4; +- else return -EINVAL; +- +- // setup PLL filter +- switch (p->bandwidth_hz) { +- case 6000000: +- tda1004x_writereg(fe, 0x0C, 0); +- filter = 0; +- break; +- +- case 7000000: +- tda1004x_writereg(fe, 0x0C, 0); +- filter = 0; +- break; +- +- case 8000000: +- tda1004x_writereg(fe, 0x0C, 0xFF); +- filter = 1; +- break; +- +- default: +- return -EINVAL; +- } +- +- // calculate divisor +- // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) +- tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; +- +- // setup tuner buffer +- tuner_buf[0] = tuner_frequency >> 8; +- tuner_buf[1] = tuner_frequency & 0xff; +- tuner_buf[2] = 0xca; +- tuner_buf[3] = (cp << 5) | (filter << 3) | band; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) +- return -EIO; +- +- msleep(1); +- return 0; +-} +- +-static int philips_tdm1316l_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) +-{ +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- +- return request_firmware(fw, name, &ttusb->dev->dev); +-} +- +-static struct tda1004x_config philips_tdm1316l_config = { +- +- .demod_address = 0x8, +- .invert = 1, +- .invert_oclk = 0, +- .request_firmware = philips_tdm1316l_request_firmware, +-}; +- +-static u8 alps_bsbe1_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x40, /* DAC not used, set to high impendance mode */ +- 0x07, 0x00, /* DAC LSB */ +- 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ +- 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ +- 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ +- 0x10, 0x3f, // AGC2 0x3d +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, // lock detector threshold +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 +- 0x29, 0x1e, // 1/2 threshold +- 0x2a, 0x14, // 2/3 threshold +- 0x2b, 0x0f, // 3/4 threshold +- 0x2c, 0x09, // 5/6 threshold +- 0x2d, 0x05, // 7/8 threshold +- 0x2e, 0x01, +- 0x31, 0x1f, // test all FECs +- 0x32, 0x19, // viterbi and synchro search +- 0x33, 0xfc, // rs control +- 0x34, 0x93, // error control +- 0x0f, 0x92, +- 0xff, 0xff +-}; +- +-static u8 alps_bsru6_inittab[] = { +- 0x01, 0x15, +- 0x02, 0x30, +- 0x03, 0x00, +- 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ +- 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ +- 0x06, 0x40, /* DAC not used, set to high impendance mode */ +- 0x07, 0x00, /* DAC LSB */ +- 0x08, 0x40, /* DiSEqC off, LNB power on OP2/LOCK pin on */ +- 0x09, 0x00, /* FIFO */ +- 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ +- 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ +- 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ +- 0x10, 0x3f, // AGC2 0x3d +- 0x11, 0x84, +- 0x12, 0xb9, +- 0x15, 0xc9, // lock detector threshold +- 0x16, 0x00, +- 0x17, 0x00, +- 0x18, 0x00, +- 0x19, 0x00, +- 0x1a, 0x00, +- 0x1f, 0x50, +- 0x20, 0x00, +- 0x21, 0x00, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 +- 0x29, 0x1e, // 1/2 threshold +- 0x2a, 0x14, // 2/3 threshold +- 0x2b, 0x0f, // 3/4 threshold +- 0x2c, 0x09, // 5/6 threshold +- 0x2d, 0x05, // 7/8 threshold +- 0x2e, 0x01, +- 0x31, 0x1f, // test all FECs +- 0x32, 0x19, // viterbi and synchro search +- 0x33, 0xfc, // rs control +- 0x34, 0x93, // error control +- 0x0f, 0x52, +- 0xff, 0xff +-}; +- +-static int alps_stv0299_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 int philips_tsa5059_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- u8 buf[4]; +- u32 div; +- struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; +- +- if ((p->frequency < 950000) || (p->frequency > 2150000)) +- return -EINVAL; +- +- div = (p->frequency + (125 - 1)) / 125; /* round correctly */ +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; +- buf[3] = 0xC4; +- +- if (p->frequency > 1530000) +- buf[3] = 0xC0; +- +- /* BSBE1 wants XCE bit set */ +- if (ttusb->revision == TTUSB_REV_2_2) +- buf[3] |= 0x20; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-static struct stv0299_config alps_stv0299_config = { +- .demod_address = 0x68, +- .inittab = alps_bsru6_inittab, +- .mclk = 88000000UL, +- .invert = 1, +- .skip_reinit = 0, +- .lock_output = STV0299_LOCKOUTPUT_1, +- .volt13_op0_op1 = STV0299_VOLT13_OP1, +- .min_delay_ms = 100, +- .set_symbol_rate = alps_stv0299_set_symbol_rate, +-}; +- +-static int ttusb_novas_grundig_29504_491_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb* ttusb = (struct ttusb*) fe->dvb->priv; +- u8 buf[4]; +- u32 div; +- struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; +- +- div = p->frequency / 125; +- +- buf[0] = (div >> 8) & 0x7f; +- buf[1] = div & 0xff; +- buf[2] = 0x8e; +- buf[3] = 0x00; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +-static struct tda8083_config ttusb_novas_grundig_29504_491_config = { +- +- .demod_address = 0x68, +-}; +- +-static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb* ttusb = fe->dvb->priv; +- u32 div; +- u8 data[4]; +- struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; +- +- div = (p->frequency + 35937500 + 31250) / 62500; +- +- data[0] = (div >> 8) & 0x7f; +- data[1] = div & 0xff; +- data[2] = 0x85 | ((div >> 10) & 0x60); +- data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer (&ttusb->i2c_adap, &msg, 1) != 1) +- return -EIO; +- +- return 0; +-} +- +- +-static struct ves1820_config alps_tdbe2_config = { +- .demod_address = 0x09, +- .xin = 57840000UL, +- .invert = 1, +- .selagc = VES1820_SELAGC_SIGNAMPERR, +-}; +- +-static u8 read_pwm(struct ttusb* ttusb) +-{ +- u8 b = 0xff; +- u8 pwm; +- struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, +- { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; +- +- if ((i2c_transfer(&ttusb->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) +- pwm = 0x48; +- +- return pwm; +-} +- +- +-static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusb *ttusb = (struct ttusb *) fe->dvb->priv; +- u8 tuner_buf[5]; +- struct i2c_msg tuner_msg = {.addr = 0x60, +- .flags = 0, +- .buf = tuner_buf, +- .len = sizeof(tuner_buf) }; +- int tuner_frequency = 0; +- u8 band, cp, filter; +- +- // determine charge pump +- tuner_frequency = p->frequency; +- if (tuner_frequency < 87000000) {return -EINVAL;} +- else if (tuner_frequency < 130000000) {cp = 3; band = 1;} +- else if (tuner_frequency < 160000000) {cp = 5; band = 1;} +- else if (tuner_frequency < 200000000) {cp = 6; band = 1;} +- else if (tuner_frequency < 290000000) {cp = 3; band = 2;} +- else if (tuner_frequency < 420000000) {cp = 5; band = 2;} +- else if (tuner_frequency < 480000000) {cp = 6; band = 2;} +- else if (tuner_frequency < 620000000) {cp = 3; band = 4;} +- else if (tuner_frequency < 830000000) {cp = 5; band = 4;} +- else if (tuner_frequency < 895000000) {cp = 7; band = 4;} +- else {return -EINVAL;} +- +- // assume PLL filter should always be 8MHz for the moment. +- filter = 1; +- +- // calculate divisor +- // (Finput + Fif)/Fref; Fif = 36125000 Hz, Fref = 62500 Hz +- tuner_frequency = ((p->frequency + 36125000) / 62500); +- +- // setup tuner buffer +- tuner_buf[0] = tuner_frequency >> 8; +- tuner_buf[1] = tuner_frequency & 0xff; +- tuner_buf[2] = 0xc8; +- tuner_buf[3] = (cp << 5) | (filter << 3) | band; +- tuner_buf[4] = 0x80; +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { +- printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 1\n"); +- return -EIO; +- } +- +- msleep(50); +- +- if (fe->ops.i2c_gate_ctrl) +- fe->ops.i2c_gate_ctrl(fe, 1); +- if (i2c_transfer(&ttusb->i2c_adap, &tuner_msg, 1) != 1) { +- printk("dvb-ttusb-budget: dvbc_philips_tdm1316l_pll_set Error 2\n"); +- return -EIO; +- } +- +- msleep(1); +- +- return 0; +-} +- +-static u8 dvbc_philips_tdm1316l_inittab[] = { +- 0x80, 0x21, +- 0x80, 0x20, +- 0x81, 0x01, +- 0x81, 0x00, +- 0x00, 0x09, +- 0x01, 0x69, +- 0x03, 0x00, +- 0x04, 0x00, +- 0x07, 0x00, +- 0x08, 0x00, +- 0x20, 0x00, +- 0x21, 0x40, +- 0x22, 0x00, +- 0x23, 0x00, +- 0x24, 0x40, +- 0x25, 0x88, +- 0x30, 0xff, +- 0x31, 0x00, +- 0x32, 0xff, +- 0x33, 0x00, +- 0x34, 0x50, +- 0x35, 0x7f, +- 0x36, 0x00, +- 0x37, 0x20, +- 0x38, 0x00, +- 0x40, 0x1c, +- 0x41, 0xff, +- 0x42, 0x29, +- 0x43, 0x20, +- 0x44, 0xff, +- 0x45, 0x00, +- 0x46, 0x00, +- 0x49, 0x04, +- 0x4a, 0xff, +- 0x4b, 0x7f, +- 0x52, 0x30, +- 0x55, 0xae, +- 0x56, 0x47, +- 0x57, 0xe1, +- 0x58, 0x3a, +- 0x5a, 0x1e, +- 0x5b, 0x34, +- 0x60, 0x00, +- 0x63, 0x00, +- 0x64, 0x00, +- 0x65, 0x00, +- 0x66, 0x00, +- 0x67, 0x00, +- 0x68, 0x00, +- 0x69, 0x00, +- 0x6a, 0x02, +- 0x6b, 0x00, +- 0x70, 0xff, +- 0x71, 0x00, +- 0x72, 0x00, +- 0x73, 0x00, +- 0x74, 0x0c, +- 0x80, 0x00, +- 0x81, 0x00, +- 0x82, 0x00, +- 0x83, 0x00, +- 0x84, 0x04, +- 0x85, 0x80, +- 0x86, 0x24, +- 0x87, 0x78, +- 0x88, 0x00, +- 0x89, 0x00, +- 0x90, 0x01, +- 0x91, 0x01, +- 0xa0, 0x00, +- 0xa1, 0x00, +- 0xa2, 0x00, +- 0xb0, 0x91, +- 0xb1, 0x0b, +- 0xc0, 0x4b, +- 0xc1, 0x00, +- 0xc2, 0x00, +- 0xd0, 0x00, +- 0xd1, 0x00, +- 0xd2, 0x00, +- 0xd3, 0x00, +- 0xd4, 0x00, +- 0xd5, 0x00, +- 0xde, 0x00, +- 0xdf, 0x00, +- 0x61, 0x38, +- 0x62, 0x0a, +- 0x53, 0x13, +- 0x59, 0x08, +- 0x55, 0x00, +- 0x56, 0x40, +- 0x57, 0x08, +- 0x58, 0x3d, +- 0x88, 0x10, +- 0xa0, 0x00, +- 0xa0, 0x00, +- 0xa0, 0x00, +- 0xa0, 0x04, +- 0xff, 0xff, +-}; +- +-static struct stv0297_config dvbc_philips_tdm1316l_config = { +- .demod_address = 0x1c, +- .inittab = dvbc_philips_tdm1316l_inittab, +- .invert = 0, +-}; +- +-static void frontend_init(struct ttusb* ttusb) +-{ +- switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) { +- case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6|BSBE1(tsa5059)) +- // try the stv0299 based first +- ttusb->fe = dvb_attach(stv0299_attach, &alps_stv0299_config, &ttusb->i2c_adap); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.set_params = philips_tsa5059_tuner_set_params; +- +- if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1 +- alps_stv0299_config.inittab = alps_bsbe1_inittab; +- dvb_attach(lnbp21_attach, ttusb->fe, &ttusb->i2c_adap, 0, 0); +- } else { // ALPS BSRU6 +- ttusb->fe->ops.set_voltage = ttusb_set_voltage; +- } +- break; +- } +- +- // Grundig 29504-491 +- ttusb->fe = dvb_attach(tda8083_attach, &ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params; +- ttusb->fe->ops.set_voltage = ttusb_set_voltage; +- break; +- } +- break; +- +- case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) +- ttusb->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb)); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; +- break; +- } +- +- ttusb->fe = dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &ttusb->i2c_adap); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; +- break; +- } +- break; +- +- case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??)) +- // try the ALPS TDMB7 first +- ttusb->fe = dvb_attach(cx22700_attach, &alps_tdmb7_config, &ttusb->i2c_adap); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.set_params = alps_tdmb7_tuner_set_params; +- break; +- } +- +- // Philips td1316 +- ttusb->fe = dvb_attach(tda10046_attach, &philips_tdm1316l_config, &ttusb->i2c_adap); +- if (ttusb->fe != NULL) { +- ttusb->fe->ops.tuner_ops.init = philips_tdm1316l_tuner_init; +- ttusb->fe->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; +- break; +- } +- break; +- } +- +- if (ttusb->fe == NULL) { +- printk("dvb-ttusb-budget: A frontend driver was not found for device [%04x:%04x]\n", +- le16_to_cpu(ttusb->dev->descriptor.idVendor), +- le16_to_cpu(ttusb->dev->descriptor.idProduct)); +- } else { +- if (dvb_register_frontend(&ttusb->adapter, ttusb->fe)) { +- printk("dvb-ttusb-budget: Frontend registration failed!\n"); +- dvb_frontend_detach(ttusb->fe); +- ttusb->fe = NULL; +- } +- } +-} +- +- +- +-static struct i2c_algorithm ttusb_dec_algo = { +- .master_xfer = master_xfer, +- .functionality = functionality, +-}; +- +-static int ttusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +-{ +- struct usb_device *udev; +- struct ttusb *ttusb; +- int result; +- +- dprintk("%s: TTUSB DVB connected\n", __func__); +- +- udev = interface_to_usbdev(intf); +- +- if (intf->altsetting->desc.bInterfaceNumber != 1) return -ENODEV; +- +- if (!(ttusb = kzalloc(sizeof(struct ttusb), GFP_KERNEL))) +- return -ENOMEM; +- +- ttusb->dev = udev; +- ttusb->c = 0; +- ttusb->mux_state = 0; +- mutex_init(&ttusb->semi2c); +- +- mutex_lock(&ttusb->semi2c); +- +- mutex_init(&ttusb->semusb); +- +- ttusb_setup_interfaces(ttusb); +- +- result = ttusb_alloc_iso_urbs(ttusb); +- if (result < 0) { +- dprintk("%s: ttusb_alloc_iso_urbs - failed\n", __func__); +- mutex_unlock(&ttusb->semi2c); +- kfree(ttusb); +- return result; +- } +- +- if (ttusb_init_controller(ttusb)) +- printk("ttusb_init_controller: error\n"); +- +- mutex_unlock(&ttusb->semi2c); +- +- result = dvb_register_adapter(&ttusb->adapter, +- "Technotrend/Hauppauge Nova-USB", +- THIS_MODULE, &udev->dev, adapter_nr); +- if (result < 0) { +- ttusb_free_iso_urbs(ttusb); +- kfree(ttusb); +- return result; +- } +- ttusb->adapter.priv = ttusb; +- +- /* i2c */ +- memset(&ttusb->i2c_adap, 0, sizeof(struct i2c_adapter)); +- strcpy(ttusb->i2c_adap.name, "TTUSB DEC"); +- +- i2c_set_adapdata(&ttusb->i2c_adap, ttusb); +- +- ttusb->i2c_adap.algo = &ttusb_dec_algo; +- ttusb->i2c_adap.algo_data = NULL; +- ttusb->i2c_adap.dev.parent = &udev->dev; +- +- result = i2c_add_adapter(&ttusb->i2c_adap); +- if (result) +- goto err_unregister_adapter; +- +- memset(&ttusb->dvb_demux, 0, sizeof(ttusb->dvb_demux)); +- +- ttusb->dvb_demux.dmx.capabilities = +- DMX_TS_FILTERING | DMX_SECTION_FILTERING; +- ttusb->dvb_demux.priv = NULL; +-#ifdef TTUSB_HWSECTIONS +- ttusb->dvb_demux.filternum = TTUSB_MAXFILTER; +-#else +- ttusb->dvb_demux.filternum = 32; +-#endif +- ttusb->dvb_demux.feednum = TTUSB_MAXCHANNEL; +- ttusb->dvb_demux.start_feed = ttusb_start_feed; +- ttusb->dvb_demux.stop_feed = ttusb_stop_feed; +- ttusb->dvb_demux.write_to_decoder = NULL; +- +- result = dvb_dmx_init(&ttusb->dvb_demux); +- if (result < 0) { +- printk("ttusb_dvb: dvb_dmx_init failed (errno = %d)\n", result); +- result = -ENODEV; +- goto err_i2c_del_adapter; +- } +-//FIXME dmxdev (nur WAS?) +- ttusb->dmxdev.filternum = ttusb->dvb_demux.filternum; +- ttusb->dmxdev.demux = &ttusb->dvb_demux.dmx; +- ttusb->dmxdev.capabilities = 0; +- +- result = dvb_dmxdev_init(&ttusb->dmxdev, &ttusb->adapter); +- if (result < 0) { +- printk("ttusb_dvb: dvb_dmxdev_init failed (errno = %d)\n", +- result); +- result = -ENODEV; +- goto err_release_dmx; +- } +- +- if (dvb_net_init(&ttusb->adapter, &ttusb->dvbnet, &ttusb->dvb_demux.dmx)) { +- printk("ttusb_dvb: dvb_net_init failed!\n"); +- result = -ENODEV; +- goto err_release_dmxdev; +- } +- +- usb_set_intfdata(intf, (void *) ttusb); +- +- frontend_init(ttusb); +- +- return 0; +- +-err_release_dmxdev: +- dvb_dmxdev_release(&ttusb->dmxdev); +-err_release_dmx: +- dvb_dmx_release(&ttusb->dvb_demux); +-err_i2c_del_adapter: +- i2c_del_adapter(&ttusb->i2c_adap); +-err_unregister_adapter: +- dvb_unregister_adapter (&ttusb->adapter); +- return result; +-} +- +-static void ttusb_disconnect(struct usb_interface *intf) +-{ +- struct ttusb *ttusb = usb_get_intfdata(intf); +- +- usb_set_intfdata(intf, NULL); +- +- ttusb->disconnecting = 1; +- +- ttusb_stop_iso_xfer(ttusb); +- +- ttusb->dvb_demux.dmx.close(&ttusb->dvb_demux.dmx); +- dvb_net_release(&ttusb->dvbnet); +- dvb_dmxdev_release(&ttusb->dmxdev); +- dvb_dmx_release(&ttusb->dvb_demux); +- if (ttusb->fe != NULL) { +- dvb_unregister_frontend(ttusb->fe); +- dvb_frontend_detach(ttusb->fe); +- } +- i2c_del_adapter(&ttusb->i2c_adap); +- dvb_unregister_adapter(&ttusb->adapter); +- +- ttusb_free_iso_urbs(ttusb); +- +- kfree(ttusb); +- +- dprintk("%s: TTUSB DVB disconnected\n", __func__); +-} +- +-static struct usb_device_id ttusb_table[] = { +- {USB_DEVICE(0xb48, 0x1003)}, +- {USB_DEVICE(0xb48, 0x1004)}, +- {USB_DEVICE(0xb48, 0x1005)}, +- {} +-}; +- +-MODULE_DEVICE_TABLE(usb, ttusb_table); +- +-static struct usb_driver ttusb_driver = { +- .name = "ttusb", +- .probe = ttusb_probe, +- .disconnect = ttusb_disconnect, +- .id_table = ttusb_table, +-}; +- +-module_usb_driver(ttusb_driver); +- +-MODULE_AUTHOR("Holger Waechtler "); +-MODULE_DESCRIPTION("TTUSB DVB Driver"); +-MODULE_LICENSE("GPL"); +-MODULE_FIRMWARE("ttusb-budget/dspbootcode.bin"); +diff --git a/drivers/media/dvb/ttusb-dec/Kconfig b/drivers/media/dvb/ttusb-dec/Kconfig +deleted file mode 100644 +index 290254a..0000000 +--- a/drivers/media/dvb/ttusb-dec/Kconfig ++++ /dev/null +@@ -1,21 +0,0 @@ +-config DVB_TTUSB_DEC +- tristate "Technotrend/Hauppauge USB DEC devices" +- depends on DVB_CORE && USB && INPUT && PCI +- select CRC32 +- help +- Support for external USB adapters designed by Technotrend and +- produced by Hauppauge, shipped under the brand name 'DEC2000-t' +- and 'DEC3000-s'. +- +- Even if these devices have a MPEG decoder built in, they transmit +- only compressed MPEG data over the USB bus, so you need +- an external software decoder to watch TV on your computer. +- +- This driver needs external firmware. Please use the commands +- "/Documentation/dvb/get_dvb_firmware dec2000t", +- "/Documentation/dvb/get_dvb_firmware dec2540t", +- "/Documentation/dvb/get_dvb_firmware dec3000s", +- download/extract them, and then copy them to /usr/lib/hotplug/firmware +- or /lib/firmware (depending on configuration of firmware hotplug). +- +- Say Y if you own such a device and want to use it. +diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile +deleted file mode 100644 +index ed28b53..0000000 +--- a/drivers/media/dvb/ttusb-dec/Makefile ++++ /dev/null +@@ -1,3 +0,0 @@ +-obj-$(CONFIG_DVB_TTUSB_DEC) += ttusb_dec.o ttusbdecfe.o +- +-ccflags-y += -Idrivers/media/dvb/dvb-core/ +diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c +deleted file mode 100644 +index 504c812..0000000 +--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c ++++ /dev/null +@@ -1,1764 +0,0 @@ +-/* +- * TTUSB DEC Driver +- * +- * Copyright (C) 2003-2004 Alex Woods +- * IR support by Peter Beutner +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "dmxdev.h" +-#include "dvb_demux.h" +-#include "dvb_filter.h" +-#include "dvb_frontend.h" +-#include "dvb_net.h" +-#include "ttusbdecfe.h" +- +-static int debug; +-static int output_pva; +-static int enable_rc; +- +-module_param(debug, int, 0644); +-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); +-module_param(output_pva, int, 0444); +-MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)"); +-module_param(enable_rc, int, 0644); +-MODULE_PARM_DESC(enable_rc, "Turn on/off IR remote control(default: off)"); +- +-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +- +-#define dprintk if (debug) printk +- +-#define DRIVER_NAME "TechnoTrend/Hauppauge DEC USB" +- +-#define COMMAND_PIPE 0x03 +-#define RESULT_PIPE 0x04 +-#define IN_PIPE 0x08 +-#define OUT_PIPE 0x07 +-#define IRQ_PIPE 0x0A +- +-#define COMMAND_PACKET_SIZE 0x3c +-#define ARM_PACKET_SIZE 0x1000 +-#define IRQ_PACKET_SIZE 0x8 +- +-#define ISO_BUF_COUNT 0x04 +-#define FRAMES_PER_ISO_BUF 0x04 +-#define ISO_FRAME_SIZE 0x0380 +- +-#define MAX_PVA_LENGTH 6144 +- +-enum ttusb_dec_model { +- TTUSB_DEC2000T, +- TTUSB_DEC2540T, +- TTUSB_DEC3000S +-}; +- +-enum ttusb_dec_packet_type { +- TTUSB_DEC_PACKET_PVA, +- TTUSB_DEC_PACKET_SECTION, +- TTUSB_DEC_PACKET_EMPTY +-}; +- +-enum ttusb_dec_interface { +- TTUSB_DEC_INTERFACE_INITIAL, +- TTUSB_DEC_INTERFACE_IN, +- TTUSB_DEC_INTERFACE_OUT +-}; +- +-struct ttusb_dec { +- enum ttusb_dec_model model; +- char *model_name; +- char *firmware_name; +- int can_playback; +- +- /* DVB bits */ +- struct dvb_adapter adapter; +- struct dmxdev dmxdev; +- struct dvb_demux demux; +- struct dmx_frontend frontend; +- struct dvb_net dvb_net; +- struct dvb_frontend* fe; +- +- u16 pid[DMX_PES_OTHER]; +- +- /* USB bits */ +- struct usb_device *udev; +- u8 trans_count; +- unsigned int command_pipe; +- unsigned int result_pipe; +- unsigned int in_pipe; +- unsigned int out_pipe; +- unsigned int irq_pipe; +- enum ttusb_dec_interface interface; +- struct mutex usb_mutex; +- +- void *irq_buffer; +- struct urb *irq_urb; +- dma_addr_t irq_dma_handle; +- void *iso_buffer; +- dma_addr_t iso_dma_handle; +- struct urb *iso_urb[ISO_BUF_COUNT]; +- int iso_stream_count; +- struct mutex iso_mutex; +- +- u8 packet[MAX_PVA_LENGTH + 4]; +- enum ttusb_dec_packet_type packet_type; +- int packet_state; +- int packet_length; +- int packet_payload_length; +- u16 next_packet_id; +- +- int pva_stream_count; +- int filter_stream_count; +- +- struct dvb_filter_pes2ts a_pes2ts; +- struct dvb_filter_pes2ts v_pes2ts; +- +- u8 v_pes[16 + MAX_PVA_LENGTH]; +- int v_pes_length; +- int v_pes_postbytes; +- +- struct list_head urb_frame_list; +- struct tasklet_struct urb_tasklet; +- spinlock_t urb_frame_list_lock; +- +- struct dvb_demux_filter *audio_filter; +- struct dvb_demux_filter *video_filter; +- struct list_head filter_info_list; +- spinlock_t filter_info_list_lock; +- +- struct input_dev *rc_input_dev; +- char rc_phys[64]; +- +- int active; /* Loaded successfully */ +-}; +- +-struct urb_frame { +- u8 data[ISO_FRAME_SIZE]; +- int length; +- struct list_head urb_frame_list; +-}; +- +-struct filter_info { +- u8 stream_id; +- struct dvb_demux_filter *filter; +- struct list_head filter_info_list; +-}; +- +-static u16 rc_keys[] = { +- KEY_POWER, +- KEY_MUTE, +- KEY_1, +- KEY_2, +- KEY_3, +- KEY_4, +- KEY_5, +- KEY_6, +- KEY_7, +- KEY_8, +- KEY_9, +- KEY_0, +- KEY_CHANNELUP, +- KEY_VOLUMEDOWN, +- KEY_OK, +- KEY_VOLUMEUP, +- KEY_CHANNELDOWN, +- KEY_PREVIOUS, +- KEY_ESC, +- KEY_RED, +- KEY_GREEN, +- KEY_YELLOW, +- KEY_BLUE, +- KEY_OPTION, +- KEY_M, +- KEY_RADIO +-}; +- +-static void ttusb_dec_set_model(struct ttusb_dec *dec, +- enum ttusb_dec_model model); +- +-static void ttusb_dec_handle_irq( struct urb *urb) +-{ +- struct ttusb_dec * dec = urb->context; +- char *buffer = dec->irq_buffer; +- int retval; +- +- switch(urb->status) { +- case 0: /*success*/ +- break; +- case -ECONNRESET: +- case -ENOENT: +- case -ESHUTDOWN: +- case -ETIME: +- /* this urb is dead, cleanup */ +- dprintk("%s:urb shutting down with status: %d\n", +- __func__, urb->status); +- return; +- default: +- dprintk("%s:nonzero status received: %d\n", +- __func__,urb->status); +- goto exit; +- } +- +- if( (buffer[0] == 0x1) && (buffer[2] == 0x15) ) { +- /* IR - Event */ +- /* this is an fact a bit too simple implementation; +- * the box also reports a keyrepeat signal +- * (with buffer[3] == 0x40) in an intervall of ~100ms. +- * But to handle this correctly we had to imlemenent some +- * kind of timer which signals a 'key up' event if no +- * keyrepeat signal is received for lets say 200ms. +- * this should/could be added later ... +- * for now lets report each signal as a key down and up*/ +- dprintk("%s:rc signal:%d\n", __func__, buffer[4]); +- input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); +- input_sync(dec->rc_input_dev); +- input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); +- input_sync(dec->rc_input_dev); +- } +- +-exit: retval = usb_submit_urb(urb, GFP_ATOMIC); +- if(retval) +- printk("%s - usb_commit_urb failed with result: %d\n", +- __func__, retval); +-} +- +-static u16 crc16(u16 crc, const u8 *buf, size_t len) +-{ +- u16 tmp; +- +- while (len--) { +- crc ^= *buf++; +- crc ^= (u8)crc >> 4; +- tmp = (u8)crc; +- crc ^= (tmp ^ (tmp << 1)) << 4; +- } +- return crc; +-} +- +-static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command, +- int param_length, const u8 params[], +- int *result_length, u8 cmd_result[]) +-{ +- int result, actual_len, i; +- u8 *b; +- +- dprintk("%s\n", __func__); +- +- b = kmalloc(COMMAND_PACKET_SIZE + 4, GFP_KERNEL); +- if (!b) +- return -ENOMEM; +- +- if ((result = mutex_lock_interruptible(&dec->usb_mutex))) { +- kfree(b); +- printk("%s: Failed to lock usb mutex.\n", __func__); +- return result; +- } +- +- b[0] = 0xaa; +- b[1] = ++dec->trans_count; +- b[2] = command; +- b[3] = param_length; +- +- if (params) +- memcpy(&b[4], params, param_length); +- +- if (debug) { +- printk("%s: command: ", __func__); +- for (i = 0; i < param_length + 4; i++) +- printk("0x%02X ", b[i]); +- printk("\n"); +- } +- +- result = usb_bulk_msg(dec->udev, dec->command_pipe, b, +- COMMAND_PACKET_SIZE + 4, &actual_len, 1000); +- +- if (result) { +- printk("%s: command bulk message failed: error %d\n", +- __func__, result); +- mutex_unlock(&dec->usb_mutex); +- kfree(b); +- return result; +- } +- +- result = usb_bulk_msg(dec->udev, dec->result_pipe, b, +- COMMAND_PACKET_SIZE + 4, &actual_len, 1000); +- +- if (result) { +- printk("%s: result bulk message failed: error %d\n", +- __func__, result); +- mutex_unlock(&dec->usb_mutex); +- kfree(b); +- return result; +- } else { +- if (debug) { +- printk("%s: result: ", __func__); +- for (i = 0; i < actual_len; i++) +- printk("0x%02X ", b[i]); +- printk("\n"); +- } +- +- if (result_length) +- *result_length = b[3]; +- if (cmd_result && b[3] > 0) +- memcpy(cmd_result, &b[4], b[3]); +- +- mutex_unlock(&dec->usb_mutex); +- +- kfree(b); +- return 0; +- } +-} +- +-static int ttusb_dec_get_stb_state (struct ttusb_dec *dec, unsigned int *mode, +- unsigned int *model, unsigned int *version) +-{ +- u8 c[COMMAND_PACKET_SIZE]; +- int c_length; +- int result; +- __be32 tmp; +- +- dprintk("%s\n", __func__); +- +- result = ttusb_dec_send_command(dec, 0x08, 0, NULL, &c_length, c); +- if (result) +- return result; +- +- if (c_length >= 0x0c) { +- if (mode != NULL) { +- memcpy(&tmp, c, 4); +- *mode = ntohl(tmp); +- } +- if (model != NULL) { +- memcpy(&tmp, &c[4], 4); +- *model = ntohl(tmp); +- } +- if (version != NULL) { +- memcpy(&tmp, &c[8], 4); +- *version = ntohl(tmp); +- } +- return 0; +- } else { +- return -1; +- } +-} +- +-static int ttusb_dec_audio_pes2ts_cb(void *priv, unsigned char *data) +-{ +- struct ttusb_dec *dec = priv; +- +- dec->audio_filter->feed->cb.ts(data, 188, NULL, 0, +- &dec->audio_filter->feed->feed.ts, +- DMX_OK); +- +- return 0; +-} +- +-static int ttusb_dec_video_pes2ts_cb(void *priv, unsigned char *data) +-{ +- struct ttusb_dec *dec = priv; +- +- dec->video_filter->feed->cb.ts(data, 188, NULL, 0, +- &dec->video_filter->feed->feed.ts, +- DMX_OK); +- +- return 0; +-} +- +-static void ttusb_dec_set_pids(struct ttusb_dec *dec) +-{ +- u8 b[] = { 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0xff, 0xff, +- 0xff, 0xff, 0xff, 0xff }; +- +- __be16 pcr = htons(dec->pid[DMX_PES_PCR]); +- __be16 audio = htons(dec->pid[DMX_PES_AUDIO]); +- __be16 video = htons(dec->pid[DMX_PES_VIDEO]); +- +- dprintk("%s\n", __func__); +- +- memcpy(&b[0], &pcr, 2); +- memcpy(&b[2], &audio, 2); +- memcpy(&b[4], &video, 2); +- +- ttusb_dec_send_command(dec, 0x50, sizeof(b), b, NULL, NULL); +- +- dvb_filter_pes2ts_init(&dec->a_pes2ts, dec->pid[DMX_PES_AUDIO], +- ttusb_dec_audio_pes2ts_cb, dec); +- dvb_filter_pes2ts_init(&dec->v_pes2ts, dec->pid[DMX_PES_VIDEO], +- ttusb_dec_video_pes2ts_cb, dec); +- dec->v_pes_length = 0; +- dec->v_pes_postbytes = 0; +-} +- +-static void ttusb_dec_process_pva(struct ttusb_dec *dec, u8 *pva, int length) +-{ +- if (length < 8) { +- printk("%s: packet too short - discarding\n", __func__); +- return; +- } +- +- if (length > 8 + MAX_PVA_LENGTH) { +- printk("%s: packet too long - discarding\n", __func__); +- return; +- } +- +- switch (pva[2]) { +- +- case 0x01: { /* VideoStream */ +- int prebytes = pva[5] & 0x03; +- int postbytes = (pva[5] & 0x0c) >> 2; +- __be16 v_pes_payload_length; +- +- if (output_pva) { +- dec->video_filter->feed->cb.ts(pva, length, NULL, 0, +- &dec->video_filter->feed->feed.ts, DMX_OK); +- return; +- } +- +- if (dec->v_pes_postbytes > 0 && +- dec->v_pes_postbytes == prebytes) { +- memcpy(&dec->v_pes[dec->v_pes_length], +- &pva[12], prebytes); +- +- dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes, +- dec->v_pes_length + prebytes, 1); +- } +- +- if (pva[5] & 0x10) { +- dec->v_pes[7] = 0x80; +- dec->v_pes[8] = 0x05; +- +- dec->v_pes[9] = 0x21 | ((pva[8] & 0xc0) >> 5); +- dec->v_pes[10] = ((pva[8] & 0x3f) << 2) | +- ((pva[9] & 0xc0) >> 6); +- dec->v_pes[11] = 0x01 | +- ((pva[9] & 0x3f) << 2) | +- ((pva[10] & 0x80) >> 6); +- dec->v_pes[12] = ((pva[10] & 0x7f) << 1) | +- ((pva[11] & 0xc0) >> 7); +- dec->v_pes[13] = 0x01 | ((pva[11] & 0x7f) << 1); +- +- memcpy(&dec->v_pes[14], &pva[12 + prebytes], +- length - 12 - prebytes); +- dec->v_pes_length = 14 + length - 12 - prebytes; +- } else { +- dec->v_pes[7] = 0x00; +- dec->v_pes[8] = 0x00; +- +- memcpy(&dec->v_pes[9], &pva[8], length - 8); +- dec->v_pes_length = 9 + length - 8; +- } +- +- dec->v_pes_postbytes = postbytes; +- +- if (dec->v_pes[9 + dec->v_pes[8]] == 0x00 && +- dec->v_pes[10 + dec->v_pes[8]] == 0x00 && +- dec->v_pes[11 + dec->v_pes[8]] == 0x01) +- dec->v_pes[6] = 0x84; +- else +- dec->v_pes[6] = 0x80; +- +- v_pes_payload_length = htons(dec->v_pes_length - 6 + +- postbytes); +- memcpy(&dec->v_pes[4], &v_pes_payload_length, 2); +- +- if (postbytes == 0) +- dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes, +- dec->v_pes_length, 1); +- +- break; +- } +- +- case 0x02: /* MainAudioStream */ +- if (output_pva) { +- dec->audio_filter->feed->cb.ts(pva, length, NULL, 0, +- &dec->audio_filter->feed->feed.ts, DMX_OK); +- return; +- } +- +- dvb_filter_pes2ts(&dec->a_pes2ts, &pva[8], length - 8, +- pva[5] & 0x10); +- break; +- +- default: +- printk("%s: unknown PVA type: %02x.\n", __func__, +- pva[2]); +- break; +- } +-} +- +-static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet, +- int length) +-{ +- struct list_head *item; +- struct filter_info *finfo; +- struct dvb_demux_filter *filter = NULL; +- unsigned long flags; +- u8 sid; +- +- sid = packet[1]; +- spin_lock_irqsave(&dec->filter_info_list_lock, flags); +- for (item = dec->filter_info_list.next; item != &dec->filter_info_list; +- item = item->next) { +- finfo = list_entry(item, struct filter_info, filter_info_list); +- if (finfo->stream_id == sid) { +- filter = finfo->filter; +- break; +- } +- } +- spin_unlock_irqrestore(&dec->filter_info_list_lock, flags); +- +- if (filter) +- filter->feed->cb.sec(&packet[2], length - 2, NULL, 0, +- &filter->filter, DMX_OK); +-} +- +-static void ttusb_dec_process_packet(struct ttusb_dec *dec) +-{ +- int i; +- u16 csum = 0; +- u16 packet_id; +- +- if (dec->packet_length % 2) { +- printk("%s: odd sized packet - discarding\n", __func__); +- return; +- } +- +- for (i = 0; i < dec->packet_length; i += 2) +- csum ^= ((dec->packet[i] << 8) + dec->packet[i + 1]); +- +- if (csum) { +- printk("%s: checksum failed - discarding\n", __func__); +- return; +- } +- +- packet_id = dec->packet[dec->packet_length - 4] << 8; +- packet_id += dec->packet[dec->packet_length - 3]; +- +- if ((packet_id != dec->next_packet_id) && dec->next_packet_id) { +- printk("%s: warning: lost packets between %u and %u\n", +- __func__, dec->next_packet_id - 1, packet_id); +- } +- +- if (packet_id == 0xffff) +- dec->next_packet_id = 0x8000; +- else +- dec->next_packet_id = packet_id + 1; +- +- switch (dec->packet_type) { +- case TTUSB_DEC_PACKET_PVA: +- if (dec->pva_stream_count) +- ttusb_dec_process_pva(dec, dec->packet, +- dec->packet_payload_length); +- break; +- +- case TTUSB_DEC_PACKET_SECTION: +- if (dec->filter_stream_count) +- ttusb_dec_process_filter(dec, dec->packet, +- dec->packet_payload_length); +- break; +- +- case TTUSB_DEC_PACKET_EMPTY: +- break; +- } +-} +- +-static void swap_bytes(u8 *b, int length) +-{ +- u8 c; +- +- length -= length % 2; +- for (; length; b += 2, length -= 2) { +- c = *b; +- *b = *(b + 1); +- *(b + 1) = c; +- } +-} +- +-static void ttusb_dec_process_urb_frame(struct ttusb_dec *dec, u8 *b, +- int length) +-{ +- swap_bytes(b, length); +- +- while (length) { +- switch (dec->packet_state) { +- +- case 0: +- case 1: +- case 2: +- if (*b++ == 0xaa) +- dec->packet_state++; +- else +- dec->packet_state = 0; +- +- length--; +- break; +- +- case 3: +- if (*b == 0x00) { +- dec->packet_state++; +- dec->packet_length = 0; +- } else if (*b != 0xaa) { +- dec->packet_state = 0; +- } +- +- b++; +- length--; +- break; +- +- case 4: +- dec->packet[dec->packet_length++] = *b++; +- +- if (dec->packet_length == 2) { +- if (dec->packet[0] == 'A' && +- dec->packet[1] == 'V') { +- dec->packet_type = +- TTUSB_DEC_PACKET_PVA; +- dec->packet_state++; +- } else if (dec->packet[0] == 'S') { +- dec->packet_type = +- TTUSB_DEC_PACKET_SECTION; +- dec->packet_state++; +- } else if (dec->packet[0] == 0x00) { +- dec->packet_type = +- TTUSB_DEC_PACKET_EMPTY; +- dec->packet_payload_length = 2; +- dec->packet_state = 7; +- } else { +- printk("%s: unknown packet type: " +- "%02x%02x\n", __func__, +- dec->packet[0], dec->packet[1]); +- dec->packet_state = 0; +- } +- } +- +- length--; +- break; +- +- case 5: +- dec->packet[dec->packet_length++] = *b++; +- +- if (dec->packet_type == TTUSB_DEC_PACKET_PVA && +- dec->packet_length == 8) { +- dec->packet_state++; +- dec->packet_payload_length = 8 + +- (dec->packet[6] << 8) + +- dec->packet[7]; +- } else if (dec->packet_type == +- TTUSB_DEC_PACKET_SECTION && +- dec->packet_length == 5) { +- dec->packet_state++; +- dec->packet_payload_length = 5 + +- ((dec->packet[3] & 0x0f) << 8) + +- dec->packet[4]; +- } +- +- length--; +- break; +- +- case 6: { +- int remainder = dec->packet_payload_length - +- dec->packet_length; +- +- if (length >= remainder) { +- memcpy(dec->packet + dec->packet_length, +- b, remainder); +- dec->packet_length += remainder; +- b += remainder; +- length -= remainder; +- dec->packet_state++; +- } else { +- memcpy(&dec->packet[dec->packet_length], +- b, length); +- dec->packet_length += length; +- length = 0; +- } +- +- break; +- } +- +- case 7: { +- int tail = 4; +- +- dec->packet[dec->packet_length++] = *b++; +- +- if (dec->packet_type == TTUSB_DEC_PACKET_SECTION && +- dec->packet_payload_length % 2) +- tail++; +- +- if (dec->packet_length == +- dec->packet_payload_length + tail) { +- ttusb_dec_process_packet(dec); +- dec->packet_state = 0; +- } +- +- length--; +- break; +- } +- +- default: +- printk("%s: illegal packet state encountered.\n", +- __func__); +- dec->packet_state = 0; +- } +- } +-} +- +-static void ttusb_dec_process_urb_frame_list(unsigned long data) +-{ +- struct ttusb_dec *dec = (struct ttusb_dec *)data; +- struct list_head *item; +- struct urb_frame *frame; +- unsigned long flags; +- +- while (1) { +- spin_lock_irqsave(&dec->urb_frame_list_lock, flags); +- if ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { +- frame = list_entry(item, struct urb_frame, +- urb_frame_list); +- list_del(&frame->urb_frame_list); +- } else { +- spin_unlock_irqrestore(&dec->urb_frame_list_lock, +- flags); +- return; +- } +- spin_unlock_irqrestore(&dec->urb_frame_list_lock, flags); +- +- ttusb_dec_process_urb_frame(dec, frame->data, frame->length); +- kfree(frame); +- } +-} +- +-static void ttusb_dec_process_urb(struct urb *urb) +-{ +- struct ttusb_dec *dec = urb->context; +- +- if (!urb->status) { +- int i; +- +- for (i = 0; i < FRAMES_PER_ISO_BUF; i++) { +- struct usb_iso_packet_descriptor *d; +- u8 *b; +- int length; +- struct urb_frame *frame; +- +- d = &urb->iso_frame_desc[i]; +- b = urb->transfer_buffer + d->offset; +- length = d->actual_length; +- +- if ((frame = kmalloc(sizeof(struct urb_frame), +- GFP_ATOMIC))) { +- unsigned long flags; +- +- memcpy(frame->data, b, length); +- frame->length = length; +- +- spin_lock_irqsave(&dec->urb_frame_list_lock, +- flags); +- list_add_tail(&frame->urb_frame_list, +- &dec->urb_frame_list); +- spin_unlock_irqrestore(&dec->urb_frame_list_lock, +- flags); +- +- tasklet_schedule(&dec->urb_tasklet); +- } +- } +- } else { +- /* -ENOENT is expected when unlinking urbs */ +- if (urb->status != -ENOENT) +- dprintk("%s: urb error: %d\n", __func__, +- urb->status); +- } +- +- if (dec->iso_stream_count) +- usb_submit_urb(urb, GFP_ATOMIC); +-} +- +-static void ttusb_dec_setup_urbs(struct ttusb_dec *dec) +-{ +- int i, j, buffer_offset = 0; +- +- dprintk("%s\n", __func__); +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- int frame_offset = 0; +- struct urb *urb = dec->iso_urb[i]; +- +- urb->dev = dec->udev; +- urb->context = dec; +- urb->complete = ttusb_dec_process_urb; +- urb->pipe = dec->in_pipe; +- urb->transfer_flags = URB_ISO_ASAP; +- urb->interval = 1; +- urb->number_of_packets = FRAMES_PER_ISO_BUF; +- urb->transfer_buffer_length = ISO_FRAME_SIZE * +- FRAMES_PER_ISO_BUF; +- urb->transfer_buffer = dec->iso_buffer + buffer_offset; +- buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF; +- +- for (j = 0; j < FRAMES_PER_ISO_BUF; j++) { +- urb->iso_frame_desc[j].offset = frame_offset; +- urb->iso_frame_desc[j].length = ISO_FRAME_SIZE; +- frame_offset += ISO_FRAME_SIZE; +- } +- } +-} +- +-static void ttusb_dec_stop_iso_xfer(struct ttusb_dec *dec) +-{ +- int i; +- +- dprintk("%s\n", __func__); +- +- if (mutex_lock_interruptible(&dec->iso_mutex)) +- return; +- +- dec->iso_stream_count--; +- +- if (!dec->iso_stream_count) { +- for (i = 0; i < ISO_BUF_COUNT; i++) +- usb_kill_urb(dec->iso_urb[i]); +- } +- +- mutex_unlock(&dec->iso_mutex); +-} +- +-/* Setting the interface of the DEC tends to take down the USB communications +- * for a short period, so it's important not to call this function just before +- * trying to talk to it. +- */ +-static int ttusb_dec_set_interface(struct ttusb_dec *dec, +- enum ttusb_dec_interface interface) +-{ +- int result = 0; +- u8 b[] = { 0x05 }; +- +- if (interface != dec->interface) { +- switch (interface) { +- case TTUSB_DEC_INTERFACE_INITIAL: +- result = usb_set_interface(dec->udev, 0, 0); +- break; +- case TTUSB_DEC_INTERFACE_IN: +- result = ttusb_dec_send_command(dec, 0x80, sizeof(b), +- b, NULL, NULL); +- if (result) +- return result; +- result = usb_set_interface(dec->udev, 0, 8); +- break; +- case TTUSB_DEC_INTERFACE_OUT: +- result = usb_set_interface(dec->udev, 0, 1); +- break; +- } +- +- if (result) +- return result; +- +- dec->interface = interface; +- } +- +- return 0; +-} +- +-static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec) +-{ +- int i, result; +- +- dprintk("%s\n", __func__); +- +- if (mutex_lock_interruptible(&dec->iso_mutex)) +- return -EAGAIN; +- +- if (!dec->iso_stream_count) { +- ttusb_dec_setup_urbs(dec); +- +- dec->packet_state = 0; +- dec->v_pes_postbytes = 0; +- dec->next_packet_id = 0; +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- if ((result = usb_submit_urb(dec->iso_urb[i], +- GFP_ATOMIC))) { +- printk("%s: failed urb submission %d: " +- "error %d\n", __func__, i, result); +- +- while (i) { +- usb_kill_urb(dec->iso_urb[i - 1]); +- i--; +- } +- +- mutex_unlock(&dec->iso_mutex); +- return result; +- } +- } +- } +- +- dec->iso_stream_count++; +- +- mutex_unlock(&dec->iso_mutex); +- +- return 0; +-} +- +-static int ttusb_dec_start_ts_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- struct ttusb_dec *dec = dvbdmx->priv; +- u8 b0[] = { 0x05 }; +- int result = 0; +- +- dprintk("%s\n", __func__); +- +- dprintk(" ts_type:"); +- +- if (dvbdmxfeed->ts_type & TS_DECODER) +- dprintk(" TS_DECODER"); +- +- if (dvbdmxfeed->ts_type & TS_PACKET) +- dprintk(" TS_PACKET"); +- +- if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) +- dprintk(" TS_PAYLOAD_ONLY"); +- +- dprintk("\n"); +- +- switch (dvbdmxfeed->pes_type) { +- +- case DMX_TS_PES_VIDEO: +- dprintk(" pes_type: DMX_TS_PES_VIDEO\n"); +- dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; +- dec->pid[DMX_PES_VIDEO] = dvbdmxfeed->pid; +- dec->video_filter = dvbdmxfeed->filter; +- ttusb_dec_set_pids(dec); +- break; +- +- case DMX_TS_PES_AUDIO: +- dprintk(" pes_type: DMX_TS_PES_AUDIO\n"); +- dec->pid[DMX_PES_AUDIO] = dvbdmxfeed->pid; +- dec->audio_filter = dvbdmxfeed->filter; +- ttusb_dec_set_pids(dec); +- break; +- +- case DMX_TS_PES_TELETEXT: +- dec->pid[DMX_PES_TELETEXT] = dvbdmxfeed->pid; +- dprintk(" pes_type: DMX_TS_PES_TELETEXT(not supported)\n"); +- return -ENOSYS; +- +- case DMX_TS_PES_PCR: +- dprintk(" pes_type: DMX_TS_PES_PCR\n"); +- dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid; +- ttusb_dec_set_pids(dec); +- break; +- +- case DMX_TS_PES_OTHER: +- dprintk(" pes_type: DMX_TS_PES_OTHER(not supported)\n"); +- return -ENOSYS; +- +- default: +- dprintk(" pes_type: unknown (%d)\n", dvbdmxfeed->pes_type); +- return -EINVAL; +- +- } +- +- result = ttusb_dec_send_command(dec, 0x80, sizeof(b0), b0, NULL, NULL); +- if (result) +- return result; +- +- dec->pva_stream_count++; +- return ttusb_dec_start_iso_xfer(dec); +-} +- +-static int ttusb_dec_start_sec_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct ttusb_dec *dec = dvbdmxfeed->demux->priv; +- u8 b0[] = { 0x00, 0x00, 0x00, 0x01, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0xff, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00 }; +- __be16 pid; +- u8 c[COMMAND_PACKET_SIZE]; +- int c_length; +- int result; +- struct filter_info *finfo; +- unsigned long flags; +- u8 x = 1; +- +- dprintk("%s\n", __func__); +- +- pid = htons(dvbdmxfeed->pid); +- memcpy(&b0[0], &pid, 2); +- memcpy(&b0[4], &x, 1); +- memcpy(&b0[5], &dvbdmxfeed->filter->filter.filter_value[0], 1); +- +- result = ttusb_dec_send_command(dec, 0x60, sizeof(b0), b0, +- &c_length, c); +- +- if (!result) { +- if (c_length == 2) { +- if (!(finfo = kmalloc(sizeof(struct filter_info), +- GFP_ATOMIC))) +- return -ENOMEM; +- +- finfo->stream_id = c[1]; +- finfo->filter = dvbdmxfeed->filter; +- +- spin_lock_irqsave(&dec->filter_info_list_lock, flags); +- list_add_tail(&finfo->filter_info_list, +- &dec->filter_info_list); +- spin_unlock_irqrestore(&dec->filter_info_list_lock, +- flags); +- +- dvbdmxfeed->priv = finfo; +- +- dec->filter_stream_count++; +- return ttusb_dec_start_iso_xfer(dec); +- } +- +- return -EAGAIN; +- } else +- return result; +-} +- +-static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct dvb_demux *dvbdmx = dvbdmxfeed->demux; +- +- dprintk("%s\n", __func__); +- +- if (!dvbdmx->dmx.frontend) +- return -EINVAL; +- +- dprintk(" pid: 0x%04X\n", dvbdmxfeed->pid); +- +- switch (dvbdmxfeed->type) { +- +- case DMX_TYPE_TS: +- return ttusb_dec_start_ts_feed(dvbdmxfeed); +- break; +- +- case DMX_TYPE_SEC: +- return ttusb_dec_start_sec_feed(dvbdmxfeed); +- break; +- +- default: +- dprintk(" type: unknown (%d)\n", dvbdmxfeed->type); +- return -EINVAL; +- +- } +-} +- +-static int ttusb_dec_stop_ts_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct ttusb_dec *dec = dvbdmxfeed->demux->priv; +- u8 b0[] = { 0x00 }; +- +- ttusb_dec_send_command(dec, 0x81, sizeof(b0), b0, NULL, NULL); +- +- dec->pva_stream_count--; +- +- ttusb_dec_stop_iso_xfer(dec); +- +- return 0; +-} +- +-static int ttusb_dec_stop_sec_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- struct ttusb_dec *dec = dvbdmxfeed->demux->priv; +- u8 b0[] = { 0x00, 0x00 }; +- struct filter_info *finfo = (struct filter_info *)dvbdmxfeed->priv; +- unsigned long flags; +- +- b0[1] = finfo->stream_id; +- spin_lock_irqsave(&dec->filter_info_list_lock, flags); +- list_del(&finfo->filter_info_list); +- spin_unlock_irqrestore(&dec->filter_info_list_lock, flags); +- kfree(finfo); +- ttusb_dec_send_command(dec, 0x62, sizeof(b0), b0, NULL, NULL); +- +- dec->filter_stream_count--; +- +- ttusb_dec_stop_iso_xfer(dec); +- +- return 0; +-} +- +-static int ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +-{ +- dprintk("%s\n", __func__); +- +- switch (dvbdmxfeed->type) { +- case DMX_TYPE_TS: +- return ttusb_dec_stop_ts_feed(dvbdmxfeed); +- break; +- +- case DMX_TYPE_SEC: +- return ttusb_dec_stop_sec_feed(dvbdmxfeed); +- break; +- } +- +- return 0; +-} +- +-static void ttusb_dec_free_iso_urbs(struct ttusb_dec *dec) +-{ +- int i; +- +- dprintk("%s\n", __func__); +- +- for (i = 0; i < ISO_BUF_COUNT; i++) +- usb_free_urb(dec->iso_urb[i]); +- +- pci_free_consistent(NULL, +- ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * +- ISO_BUF_COUNT), +- dec->iso_buffer, dec->iso_dma_handle); +-} +- +-static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec) +-{ +- int i; +- +- dprintk("%s\n", __func__); +- +- dec->iso_buffer = pci_alloc_consistent(NULL, +- ISO_FRAME_SIZE * +- (FRAMES_PER_ISO_BUF * +- ISO_BUF_COUNT), +- &dec->iso_dma_handle); +- +- if (!dec->iso_buffer) { +- dprintk("%s: pci_alloc_consistent - not enough memory\n", +- __func__); +- return -ENOMEM; +- } +- +- memset(dec->iso_buffer, 0, +- ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT)); +- +- for (i = 0; i < ISO_BUF_COUNT; i++) { +- struct urb *urb; +- +- if (!(urb = usb_alloc_urb(FRAMES_PER_ISO_BUF, GFP_ATOMIC))) { +- ttusb_dec_free_iso_urbs(dec); +- return -ENOMEM; +- } +- +- dec->iso_urb[i] = urb; +- } +- +- ttusb_dec_setup_urbs(dec); +- +- return 0; +-} +- +-static void ttusb_dec_init_tasklet(struct ttusb_dec *dec) +-{ +- spin_lock_init(&dec->urb_frame_list_lock); +- INIT_LIST_HEAD(&dec->urb_frame_list); +- tasklet_init(&dec->urb_tasklet, ttusb_dec_process_urb_frame_list, +- (unsigned long)dec); +-} +- +-static int ttusb_init_rc( struct ttusb_dec *dec) +-{ +- struct input_dev *input_dev; +- u8 b[] = { 0x00, 0x01 }; +- int i; +- int err; +- +- usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); +- strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); +- +- input_dev = input_allocate_device(); +- if (!input_dev) +- return -ENOMEM; +- +- input_dev->name = "ttusb_dec remote control"; +- input_dev->phys = dec->rc_phys; +- input_dev->evbit[0] = BIT_MASK(EV_KEY); +- input_dev->keycodesize = sizeof(u16); +- input_dev->keycodemax = 0x1a; +- input_dev->keycode = rc_keys; +- +- for (i = 0; i < ARRAY_SIZE(rc_keys); i++) +- set_bit(rc_keys[i], input_dev->keybit); +- +- err = input_register_device(input_dev); +- if (err) { +- input_free_device(input_dev); +- return err; +- } +- +- dec->rc_input_dev = input_dev; +- if (usb_submit_urb(dec->irq_urb, GFP_KERNEL)) +- printk("%s: usb_submit_urb failed\n",__func__); +- /* enable irq pipe */ +- ttusb_dec_send_command(dec,0xb0,sizeof(b),b,NULL,NULL); +- +- return 0; +-} +- +-static void ttusb_dec_init_v_pes(struct ttusb_dec *dec) +-{ +- dprintk("%s\n", __func__); +- +- dec->v_pes[0] = 0x00; +- dec->v_pes[1] = 0x00; +- dec->v_pes[2] = 0x01; +- dec->v_pes[3] = 0xe0; +-} +- +-static int ttusb_dec_init_usb(struct ttusb_dec *dec) +-{ +- dprintk("%s\n", __func__); +- +- mutex_init(&dec->usb_mutex); +- mutex_init(&dec->iso_mutex); +- +- dec->command_pipe = usb_sndbulkpipe(dec->udev, COMMAND_PIPE); +- dec->result_pipe = usb_rcvbulkpipe(dec->udev, RESULT_PIPE); +- dec->in_pipe = usb_rcvisocpipe(dec->udev, IN_PIPE); +- dec->out_pipe = usb_sndisocpipe(dec->udev, OUT_PIPE); +- dec->irq_pipe = usb_rcvintpipe(dec->udev, IRQ_PIPE); +- +- if(enable_rc) { +- dec->irq_urb = usb_alloc_urb(0, GFP_KERNEL); +- if(!dec->irq_urb) { +- return -ENOMEM; +- } +- dec->irq_buffer = usb_alloc_coherent(dec->udev,IRQ_PACKET_SIZE, +- GFP_ATOMIC, &dec->irq_dma_handle); +- if(!dec->irq_buffer) { +- usb_free_urb(dec->irq_urb); +- return -ENOMEM; +- } +- usb_fill_int_urb(dec->irq_urb, dec->udev,dec->irq_pipe, +- dec->irq_buffer, IRQ_PACKET_SIZE, +- ttusb_dec_handle_irq, dec, 1); +- dec->irq_urb->transfer_dma = dec->irq_dma_handle; +- dec->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; +- } +- +- return ttusb_dec_alloc_iso_urbs(dec); +-} +- +-static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) +-{ +- int i, j, actual_len, result, size, trans_count; +- u8 b0[] = { 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x61, 0x00 }; +- u8 b1[] = { 0x61 }; +- u8 *b; +- char idstring[21]; +- const u8 *firmware = NULL; +- size_t firmware_size = 0; +- u16 firmware_csum = 0; +- __be16 firmware_csum_ns; +- __be32 firmware_size_nl; +- u32 crc32_csum, crc32_check; +- __be32 tmp; +- const struct firmware *fw_entry = NULL; +- +- dprintk("%s\n", __func__); +- +- if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) { +- printk(KERN_ERR "%s: Firmware (%s) unavailable.\n", +- __func__, dec->firmware_name); +- return 1; +- } +- +- firmware = fw_entry->data; +- firmware_size = fw_entry->size; +- +- if (firmware_size < 60) { +- printk("%s: firmware size too small for DSP code (%zu < 60).\n", +- __func__, firmware_size); +- release_firmware(fw_entry); +- return -1; +- } +- +- /* a 32 bit checksum over the first 56 bytes of the DSP Code is stored +- at offset 56 of file, so use it to check if the firmware file is +- valid. */ +- crc32_csum = crc32(~0L, firmware, 56) ^ ~0L; +- memcpy(&tmp, &firmware[56], 4); +- crc32_check = ntohl(tmp); +- if (crc32_csum != crc32_check) { +- printk("%s: crc32 check of DSP code failed (calculated " +- "0x%08x != 0x%08x in file), file invalid.\n", +- __func__, crc32_csum, crc32_check); +- release_firmware(fw_entry); +- return -1; +- } +- memcpy(idstring, &firmware[36], 20); +- idstring[20] = '\0'; +- printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring); +- +- firmware_size_nl = htonl(firmware_size); +- memcpy(b0, &firmware_size_nl, 4); +- firmware_csum = crc16(~0, firmware, firmware_size) ^ ~0; +- firmware_csum_ns = htons(firmware_csum); +- memcpy(&b0[6], &firmware_csum_ns, 2); +- +- result = ttusb_dec_send_command(dec, 0x41, sizeof(b0), b0, NULL, NULL); +- +- if (result) { +- release_firmware(fw_entry); +- return result; +- } +- +- trans_count = 0; +- j = 0; +- +- b = kmalloc(ARM_PACKET_SIZE, GFP_KERNEL); +- if (b == NULL) { +- release_firmware(fw_entry); +- return -ENOMEM; +- } +- +- for (i = 0; i < firmware_size; i += COMMAND_PACKET_SIZE) { +- size = firmware_size - i; +- if (size > COMMAND_PACKET_SIZE) +- size = COMMAND_PACKET_SIZE; +- +- b[j + 0] = 0xaa; +- b[j + 1] = trans_count++; +- b[j + 2] = 0xf0; +- b[j + 3] = size; +- memcpy(&b[j + 4], &firmware[i], size); +- +- j += COMMAND_PACKET_SIZE + 4; +- +- if (j >= ARM_PACKET_SIZE) { +- result = usb_bulk_msg(dec->udev, dec->command_pipe, b, +- ARM_PACKET_SIZE, &actual_len, +- 100); +- j = 0; +- } else if (size < COMMAND_PACKET_SIZE) { +- result = usb_bulk_msg(dec->udev, dec->command_pipe, b, +- j - COMMAND_PACKET_SIZE + size, +- &actual_len, 100); +- } +- } +- +- result = ttusb_dec_send_command(dec, 0x43, sizeof(b1), b1, NULL, NULL); +- +- release_firmware(fw_entry); +- kfree(b); +- +- return result; +-} +- +-static int ttusb_dec_init_stb(struct ttusb_dec *dec) +-{ +- int result; +- unsigned int mode = 0, model = 0, version = 0; +- +- dprintk("%s\n", __func__); +- +- result = ttusb_dec_get_stb_state(dec, &mode, &model, &version); +- +- if (!result) { +- if (!mode) { +- if (version == 0xABCDEFAB) +- printk(KERN_INFO "ttusb_dec: no version " +- "info in Firmware\n"); +- else +- printk(KERN_INFO "ttusb_dec: Firmware " +- "%x.%02x%c%c\n", +- version >> 24, (version >> 16) & 0xff, +- (version >> 8) & 0xff, version & 0xff); +- +- result = ttusb_dec_boot_dsp(dec); +- if (result) +- return result; +- else +- return 1; +- } else { +- /* We can't trust the USB IDs that some firmwares +- give the box */ +- switch (model) { +- case 0x00070001: +- case 0x00070008: +- case 0x0007000c: +- ttusb_dec_set_model(dec, TTUSB_DEC3000S); +- break; +- case 0x00070009: +- case 0x00070013: +- ttusb_dec_set_model(dec, TTUSB_DEC2000T); +- break; +- case 0x00070011: +- ttusb_dec_set_model(dec, TTUSB_DEC2540T); +- break; +- default: +- printk(KERN_ERR "%s: unknown model returned " +- "by firmware (%08x) - please report\n", +- __func__, model); +- return -1; +- break; +- } +- +- if (version >= 0x01770000) +- dec->can_playback = 1; +- +- return 0; +- } +- } +- else +- return result; +-} +- +-static int ttusb_dec_init_dvb(struct ttusb_dec *dec) +-{ +- int result; +- +- dprintk("%s\n", __func__); +- +- if ((result = dvb_register_adapter(&dec->adapter, +- dec->model_name, THIS_MODULE, +- &dec->udev->dev, +- adapter_nr)) < 0) { +- printk("%s: dvb_register_adapter failed: error %d\n", +- __func__, result); +- +- return result; +- } +- +- dec->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; +- +- dec->demux.priv = (void *)dec; +- dec->demux.filternum = 31; +- dec->demux.feednum = 31; +- dec->demux.start_feed = ttusb_dec_start_feed; +- dec->demux.stop_feed = ttusb_dec_stop_feed; +- dec->demux.write_to_decoder = NULL; +- +- if ((result = dvb_dmx_init(&dec->demux)) < 0) { +- printk("%s: dvb_dmx_init failed: error %d\n", __func__, +- result); +- +- dvb_unregister_adapter(&dec->adapter); +- +- return result; +- } +- +- dec->dmxdev.filternum = 32; +- dec->dmxdev.demux = &dec->demux.dmx; +- dec->dmxdev.capabilities = 0; +- +- if ((result = dvb_dmxdev_init(&dec->dmxdev, &dec->adapter)) < 0) { +- printk("%s: dvb_dmxdev_init failed: error %d\n", +- __func__, result); +- +- dvb_dmx_release(&dec->demux); +- dvb_unregister_adapter(&dec->adapter); +- +- return result; +- } +- +- dec->frontend.source = DMX_FRONTEND_0; +- +- if ((result = dec->demux.dmx.add_frontend(&dec->demux.dmx, +- &dec->frontend)) < 0) { +- printk("%s: dvb_dmx_init failed: error %d\n", __func__, +- result); +- +- dvb_dmxdev_release(&dec->dmxdev); +- dvb_dmx_release(&dec->demux); +- dvb_unregister_adapter(&dec->adapter); +- +- return result; +- } +- +- if ((result = dec->demux.dmx.connect_frontend(&dec->demux.dmx, +- &dec->frontend)) < 0) { +- printk("%s: dvb_dmx_init failed: error %d\n", __func__, +- result); +- +- dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); +- dvb_dmxdev_release(&dec->dmxdev); +- dvb_dmx_release(&dec->demux); +- dvb_unregister_adapter(&dec->adapter); +- +- return result; +- } +- +- dvb_net_init(&dec->adapter, &dec->dvb_net, &dec->demux.dmx); +- +- return 0; +-} +- +-static void ttusb_dec_exit_dvb(struct ttusb_dec *dec) +-{ +- dprintk("%s\n", __func__); +- +- dvb_net_release(&dec->dvb_net); +- dec->demux.dmx.close(&dec->demux.dmx); +- dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend); +- dvb_dmxdev_release(&dec->dmxdev); +- dvb_dmx_release(&dec->demux); +- if (dec->fe) { +- dvb_unregister_frontend(dec->fe); +- if (dec->fe->ops.release) +- dec->fe->ops.release(dec->fe); +- } +- dvb_unregister_adapter(&dec->adapter); +-} +- +-static void ttusb_dec_exit_rc(struct ttusb_dec *dec) +-{ +- +- dprintk("%s\n", __func__); +- /* we have to check whether the irq URB is already submitted. +- * As the irq is submitted after the interface is changed, +- * this is the best method i figured out. +- * Any others?*/ +- if (dec->interface == TTUSB_DEC_INTERFACE_IN) +- usb_kill_urb(dec->irq_urb); +- +- usb_free_urb(dec->irq_urb); +- +- usb_free_coherent(dec->udev,IRQ_PACKET_SIZE, +- dec->irq_buffer, dec->irq_dma_handle); +- +- if (dec->rc_input_dev) { +- input_unregister_device(dec->rc_input_dev); +- dec->rc_input_dev = NULL; +- } +-} +- +- +-static void ttusb_dec_exit_usb(struct ttusb_dec *dec) +-{ +- int i; +- +- dprintk("%s\n", __func__); +- +- dec->iso_stream_count = 0; +- +- for (i = 0; i < ISO_BUF_COUNT; i++) +- usb_kill_urb(dec->iso_urb[i]); +- +- ttusb_dec_free_iso_urbs(dec); +-} +- +-static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec) +-{ +- struct list_head *item; +- struct urb_frame *frame; +- +- tasklet_kill(&dec->urb_tasklet); +- +- while ((item = dec->urb_frame_list.next) != &dec->urb_frame_list) { +- frame = list_entry(item, struct urb_frame, urb_frame_list); +- list_del(&frame->urb_frame_list); +- kfree(frame); +- } +-} +- +-static void ttusb_dec_init_filters(struct ttusb_dec *dec) +-{ +- INIT_LIST_HEAD(&dec->filter_info_list); +- spin_lock_init(&dec->filter_info_list_lock); +-} +- +-static void ttusb_dec_exit_filters(struct ttusb_dec *dec) +-{ +- struct list_head *item; +- struct filter_info *finfo; +- +- while ((item = dec->filter_info_list.next) != &dec->filter_info_list) { +- finfo = list_entry(item, struct filter_info, filter_info_list); +- list_del(&finfo->filter_info_list); +- kfree(finfo); +- } +-} +- +-static int fe_send_command(struct dvb_frontend* fe, const u8 command, +- int param_length, const u8 params[], +- int *result_length, u8 cmd_result[]) +-{ +- struct ttusb_dec* dec = fe->dvb->priv; +- return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result); +-} +- +-static struct ttusbdecfe_config fe_config = { +- .send_command = fe_send_command +-}; +- +-static int ttusb_dec_probe(struct usb_interface *intf, +- const struct usb_device_id *id) +-{ +- struct usb_device *udev; +- struct ttusb_dec *dec; +- +- dprintk("%s\n", __func__); +- +- udev = interface_to_usbdev(intf); +- +- if (!(dec = kzalloc(sizeof(struct ttusb_dec), GFP_KERNEL))) { +- printk("%s: couldn't allocate memory.\n", __func__); +- return -ENOMEM; +- } +- +- usb_set_intfdata(intf, (void *)dec); +- +- switch (id->idProduct) { +- case 0x1006: +- ttusb_dec_set_model(dec, TTUSB_DEC3000S); +- break; +- +- case 0x1008: +- ttusb_dec_set_model(dec, TTUSB_DEC2000T); +- break; +- +- case 0x1009: +- ttusb_dec_set_model(dec, TTUSB_DEC2540T); +- break; +- } +- +- dec->udev = udev; +- +- if (ttusb_dec_init_usb(dec)) +- return 0; +- if (ttusb_dec_init_stb(dec)) { +- ttusb_dec_exit_usb(dec); +- return 0; +- } +- ttusb_dec_init_dvb(dec); +- +- dec->adapter.priv = dec; +- switch (id->idProduct) { +- case 0x1006: +- dec->fe = ttusbdecfe_dvbs_attach(&fe_config); +- break; +- +- case 0x1008: +- case 0x1009: +- dec->fe = ttusbdecfe_dvbt_attach(&fe_config); +- break; +- } +- +- if (dec->fe == NULL) { +- printk("dvb-ttusb-dec: A frontend driver was not found for device [%04x:%04x]\n", +- le16_to_cpu(dec->udev->descriptor.idVendor), +- le16_to_cpu(dec->udev->descriptor.idProduct)); +- } else { +- if (dvb_register_frontend(&dec->adapter, dec->fe)) { +- printk("budget-ci: Frontend registration failed!\n"); +- if (dec->fe->ops.release) +- dec->fe->ops.release(dec->fe); +- dec->fe = NULL; +- } +- } +- +- ttusb_dec_init_v_pes(dec); +- ttusb_dec_init_filters(dec); +- ttusb_dec_init_tasklet(dec); +- +- dec->active = 1; +- +- ttusb_dec_set_interface(dec, TTUSB_DEC_INTERFACE_IN); +- +- if (enable_rc) +- ttusb_init_rc(dec); +- +- return 0; +-} +- +-static void ttusb_dec_disconnect(struct usb_interface *intf) +-{ +- struct ttusb_dec *dec = usb_get_intfdata(intf); +- +- usb_set_intfdata(intf, NULL); +- +- dprintk("%s\n", __func__); +- +- if (dec->active) { +- ttusb_dec_exit_tasklet(dec); +- ttusb_dec_exit_filters(dec); +- if(enable_rc) +- ttusb_dec_exit_rc(dec); +- ttusb_dec_exit_usb(dec); +- ttusb_dec_exit_dvb(dec); +- } +- +- kfree(dec); +-} +- +-static void ttusb_dec_set_model(struct ttusb_dec *dec, +- enum ttusb_dec_model model) +-{ +- dec->model = model; +- +- switch (model) { +- case TTUSB_DEC2000T: +- dec->model_name = "DEC2000-t"; +- dec->firmware_name = "dvb-ttusb-dec-2000t.fw"; +- break; +- +- case TTUSB_DEC2540T: +- dec->model_name = "DEC2540-t"; +- dec->firmware_name = "dvb-ttusb-dec-2540t.fw"; +- break; +- +- case TTUSB_DEC3000S: +- dec->model_name = "DEC3000-s"; +- dec->firmware_name = "dvb-ttusb-dec-3000s.fw"; +- break; +- } +-} +- +-static struct usb_device_id ttusb_dec_table[] = { +- {USB_DEVICE(0x0b48, 0x1006)}, /* DEC3000-s */ +- /*{USB_DEVICE(0x0b48, 0x1007)}, Unconfirmed */ +- {USB_DEVICE(0x0b48, 0x1008)}, /* DEC2000-t */ +- {USB_DEVICE(0x0b48, 0x1009)}, /* DEC2540-t */ +- {} +-}; +- +-static struct usb_driver ttusb_dec_driver = { +- .name = "ttusb-dec", +- .probe = ttusb_dec_probe, +- .disconnect = ttusb_dec_disconnect, +- .id_table = ttusb_dec_table, +-}; +- +-module_usb_driver(ttusb_dec_driver); +- +-MODULE_AUTHOR("Alex Woods "); +-MODULE_DESCRIPTION(DRIVER_NAME); +-MODULE_LICENSE("GPL"); +-MODULE_DEVICE_TABLE(usb, ttusb_dec_table); +diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c b/drivers/media/dvb/ttusb-dec/ttusbdecfe.c +deleted file mode 100644 +index 5c45c9d..0000000 +--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.c ++++ /dev/null +@@ -1,298 +0,0 @@ +-/* +- * TTUSB DEC Frontend Driver +- * +- * Copyright (C) 2003-2004 Alex Woods +- * +- * 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 "dvb_frontend.h" +-#include "ttusbdecfe.h" +- +- +-#define LOF_HI 10600000 +-#define LOF_LO 9750000 +- +-struct ttusbdecfe_state { +- +- /* configuration settings */ +- const struct ttusbdecfe_config* config; +- +- struct dvb_frontend frontend; +- +- u8 hi_band; +- u8 voltage; +-}; +- +- +-static int ttusbdecfe_dvbs_read_status(struct dvb_frontend *fe, +- fe_status_t *status) +-{ +- *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | +- FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; +- return 0; +-} +- +- +-static int ttusbdecfe_dvbt_read_status(struct dvb_frontend *fe, +- fe_status_t *status) +-{ +- struct ttusbdecfe_state* state = fe->demodulator_priv; +- u8 b[] = { 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00 }; +- u8 result[4]; +- int len, ret; +- +- *status=0; +- +- ret=state->config->send_command(fe, 0x73, sizeof(b), b, &len, result); +- if(ret) +- return ret; +- +- if(len != 4) { +- printk(KERN_ERR "%s: unexpected reply\n", __func__); +- return -EIO; +- } +- +- switch(result[3]) { +- case 1: /* not tuned yet */ +- case 2: /* no signal/no lock*/ +- break; +- case 3: /* signal found and locked*/ +- *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | +- FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK; +- break; +- case 4: +- *status = FE_TIMEDOUT; +- break; +- default: +- pr_info("%s: returned unknown value: %d\n", +- __func__, result[3]); +- return -EIO; +- } +- +- return 0; +-} +- +-static int ttusbdecfe_dvbt_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- u8 b[] = { 0x00, 0x00, 0x00, 0x03, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x01, +- 0x00, 0x00, 0x00, 0xff, +- 0x00, 0x00, 0x00, 0xff }; +- +- __be32 freq = htonl(p->frequency / 1000); +- memcpy(&b[4], &freq, sizeof (u32)); +- state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); +- +- return 0; +-} +- +-static int ttusbdecfe_dvbt_get_tune_settings(struct dvb_frontend* fe, +- struct dvb_frontend_tune_settings* fesettings) +-{ +- fesettings->min_delay_ms = 1500; +- /* Drift compensation makes no sense for DVB-T */ +- fesettings->step_size = 0; +- fesettings->max_drift = 0; +- return 0; +-} +- +-static int ttusbdecfe_dvbs_set_frontend(struct dvb_frontend *fe) +-{ +- struct dtv_frontend_properties *p = &fe->dtv_property_cache; +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- +- u8 b[] = { 0x00, 0x00, 0x00, 0x01, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x01, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00 }; +- __be32 freq; +- __be32 sym_rate; +- __be32 band; +- __be32 lnb_voltage; +- +- freq = htonl(p->frequency + +- (state->hi_band ? LOF_HI : LOF_LO)); +- memcpy(&b[4], &freq, sizeof(u32)); +- sym_rate = htonl(p->symbol_rate); +- memcpy(&b[12], &sym_rate, sizeof(u32)); +- band = htonl(state->hi_band ? LOF_HI : LOF_LO); +- memcpy(&b[24], &band, sizeof(u32)); +- lnb_voltage = htonl(state->voltage); +- memcpy(&b[28], &lnb_voltage, sizeof(u32)); +- +- state->config->send_command(fe, 0x71, sizeof(b), b, NULL, NULL); +- +- return 0; +-} +- +-static int ttusbdecfe_dvbs_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd *cmd) +-{ +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- u8 b[] = { 0x00, 0xff, 0x00, 0x00, +- 0x00, 0x00, 0x00, 0x00, +- 0x00, 0x00 }; +- +- memcpy(&b[4], cmd->msg, cmd->msg_len); +- +- state->config->send_command(fe, 0x72, +- sizeof(b) - (6 - cmd->msg_len), b, +- NULL, NULL); +- +- return 0; +-} +- +- +-static int ttusbdecfe_dvbs_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) +-{ +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- +- state->hi_band = (SEC_TONE_ON == tone); +- +- return 0; +-} +- +- +-static int ttusbdecfe_dvbs_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) +-{ +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- +- switch (voltage) { +- case SEC_VOLTAGE_13: +- state->voltage = 13; +- break; +- case SEC_VOLTAGE_18: +- state->voltage = 18; +- break; +- default: +- return -EINVAL; +- } +- +- return 0; +-} +- +-static void ttusbdecfe_release(struct dvb_frontend* fe) +-{ +- struct ttusbdecfe_state* state = (struct ttusbdecfe_state*) fe->demodulator_priv; +- kfree(state); +-} +- +-static struct dvb_frontend_ops ttusbdecfe_dvbt_ops; +- +-struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config) +-{ +- struct ttusbdecfe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- /* setup the state */ +- state->config = config; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &ttusbdecfe_dvbt_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-static struct dvb_frontend_ops ttusbdecfe_dvbs_ops; +- +-struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config) +-{ +- struct ttusbdecfe_state* state = NULL; +- +- /* allocate memory for the internal state */ +- state = kmalloc(sizeof(struct ttusbdecfe_state), GFP_KERNEL); +- if (state == NULL) +- return NULL; +- +- /* setup the state */ +- state->config = config; +- state->voltage = 0; +- state->hi_band = 0; +- +- /* create dvb_frontend */ +- memcpy(&state->frontend.ops, &ttusbdecfe_dvbs_ops, sizeof(struct dvb_frontend_ops)); +- state->frontend.demodulator_priv = state; +- return &state->frontend; +-} +- +-static struct dvb_frontend_ops ttusbdecfe_dvbt_ops = { +- .delsys = { SYS_DVBT }, +- .info = { +- .name = "TechnoTrend/Hauppauge DEC2000-t Frontend", +- .frequency_min = 51000000, +- .frequency_max = 858000000, +- .frequency_stepsize = 62500, +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | +- FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | +- FE_CAN_HIERARCHY_AUTO, +- }, +- +- .release = ttusbdecfe_release, +- +- .set_frontend = ttusbdecfe_dvbt_set_frontend, +- +- .get_tune_settings = ttusbdecfe_dvbt_get_tune_settings, +- +- .read_status = ttusbdecfe_dvbt_read_status, +-}; +- +-static struct dvb_frontend_ops ttusbdecfe_dvbs_ops = { +- .delsys = { SYS_DVBS }, +- .info = { +- .name = "TechnoTrend/Hauppauge DEC3000-s Frontend", +- .frequency_min = 950000, +- .frequency_max = 2150000, +- .frequency_stepsize = 125, +- .symbol_rate_min = 1000000, /* guessed */ +- .symbol_rate_max = 45000000, /* guessed */ +- .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | +- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | +- FE_CAN_QPSK +- }, +- +- .release = ttusbdecfe_release, +- +- .set_frontend = ttusbdecfe_dvbs_set_frontend, +- +- .read_status = ttusbdecfe_dvbs_read_status, +- +- .diseqc_send_master_cmd = ttusbdecfe_dvbs_diseqc_send_master_cmd, +- .set_voltage = ttusbdecfe_dvbs_set_voltage, +- .set_tone = ttusbdecfe_dvbs_set_tone, +-}; +- +-MODULE_DESCRIPTION("TTUSB DEC DVB-T/S Demodulator driver"); +-MODULE_AUTHOR("Alex Woods/Andrew de Quincey"); +-MODULE_LICENSE("GPL"); +- +-EXPORT_SYMBOL(ttusbdecfe_dvbt_attach); +-EXPORT_SYMBOL(ttusbdecfe_dvbs_attach); +diff --git a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h b/drivers/media/dvb/ttusb-dec/ttusbdecfe.h +deleted file mode 100644 +index 15ccc3d..0000000 +--- a/drivers/media/dvb/ttusb-dec/ttusbdecfe.h ++++ /dev/null +@@ -1,38 +0,0 @@ +-/* +- * TTUSB DEC Driver +- * +- * Copyright (C) 2003-2004 Alex Woods +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT ANY WARRANTY; without even the implied warranty of +- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- * GNU General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License +- * along with this program; if not, write to the Free Software +- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- */ +- +-#ifndef TTUSBDECFE_H +-#define TTUSBDECFE_H +- +-#include +- +-struct ttusbdecfe_config +-{ +- int (*send_command)(struct dvb_frontend* fe, const u8 command, +- int param_length, const u8 params[], +- int *result_length, u8 cmd_result[]); +-}; +- +-extern struct dvb_frontend* ttusbdecfe_dvbs_attach(const struct ttusbdecfe_config* config); +- +-extern struct dvb_frontend* ttusbdecfe_dvbt_attach(const struct ttusbdecfe_config* config); +- +-#endif // TTUSBDECFE_H +diff --git a/drivers/media/firewire/Kconfig b/drivers/media/firewire/Kconfig +new file mode 100644 +index 0000000..f3e9448 +--- /dev/null ++++ b/drivers/media/firewire/Kconfig +@@ -0,0 +1,19 @@ ++config DVB_FIREDTV ++ tristate "FireDTV and FloppyDTV" ++ depends on DVB_CORE && FIREWIRE ++ help ++ Support for DVB receivers from Digital Everywhere ++ which are connected via IEEE 1394 (FireWire). ++ ++ These devices don't have an MPEG decoder built in, ++ so you need an external software decoder to watch TV. ++ ++ To compile this driver as a module, say M here: ++ the module will be called firedtv. ++ ++if DVB_FIREDTV ++ ++config DVB_FIREDTV_INPUT ++ def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) ++ ++endif # DVB_FIREDTV +diff --git a/drivers/media/firewire/Makefile b/drivers/media/firewire/Makefile +new file mode 100644 +index 0000000..2394813 +--- /dev/null ++++ b/drivers/media/firewire/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_DVB_FIREDTV) += firedtv.o ++ ++firedtv-y += firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o firedtv-fw.o ++firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o ++ ++ccflags-y += -Idrivers/media/dvb-core +diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c +new file mode 100644 +index 0000000..d1a1a13 +--- /dev/null ++++ b/drivers/media/firewire/firedtv-avc.c +@@ -0,0 +1,1457 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * Copyright (C) 2008 Ben Backx ++ * Copyright (C) 2008 Henrik Kurelid ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "firedtv.h" ++ ++#define FCP_COMMAND_REGISTER 0xfffff0000b00ULL ++ ++#define AVC_CTYPE_CONTROL 0x0 ++#define AVC_CTYPE_STATUS 0x1 ++#define AVC_CTYPE_NOTIFY 0x3 ++ ++#define AVC_RESPONSE_ACCEPTED 0x9 ++#define AVC_RESPONSE_STABLE 0xc ++#define AVC_RESPONSE_CHANGED 0xd ++#define AVC_RESPONSE_INTERIM 0xf ++ ++#define AVC_SUBUNIT_TYPE_TUNER (0x05 << 3) ++#define AVC_SUBUNIT_TYPE_UNIT (0x1f << 3) ++ ++#define AVC_OPCODE_VENDOR 0x00 ++#define AVC_OPCODE_READ_DESCRIPTOR 0x09 ++#define AVC_OPCODE_DSIT 0xc8 ++#define AVC_OPCODE_DSD 0xcb ++ ++#define DESCRIPTOR_TUNER_STATUS 0x80 ++#define DESCRIPTOR_SUBUNIT_IDENTIFIER 0x00 ++ ++#define SFE_VENDOR_DE_COMPANYID_0 0x00 /* OUI of Digital Everywhere */ ++#define SFE_VENDOR_DE_COMPANYID_1 0x12 ++#define SFE_VENDOR_DE_COMPANYID_2 0x87 ++ ++#define SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL 0x0a ++#define SFE_VENDOR_OPCODE_LNB_CONTROL 0x52 ++#define SFE_VENDOR_OPCODE_TUNE_QPSK 0x58 /* for DVB-S */ ++ ++#define SFE_VENDOR_OPCODE_GET_FIRMWARE_VERSION 0x00 ++#define SFE_VENDOR_OPCODE_HOST2CA 0x56 ++#define SFE_VENDOR_OPCODE_CA2HOST 0x57 ++#define SFE_VENDOR_OPCODE_CISTATUS 0x59 ++#define SFE_VENDOR_OPCODE_TUNE_QPSK2 0x60 /* for DVB-S2 */ ++ ++#define SFE_VENDOR_TAG_CA_RESET 0x00 ++#define SFE_VENDOR_TAG_CA_APPLICATION_INFO 0x01 ++#define SFE_VENDOR_TAG_CA_PMT 0x02 ++#define SFE_VENDOR_TAG_CA_DATE_TIME 0x04 ++#define SFE_VENDOR_TAG_CA_MMI 0x05 ++#define SFE_VENDOR_TAG_CA_ENTER_MENU 0x07 ++ ++#define EN50221_LIST_MANAGEMENT_ONLY 0x03 ++#define EN50221_TAG_APP_INFO 0x9f8021 ++#define EN50221_TAG_CA_INFO 0x9f8031 ++ ++struct avc_command_frame { ++ u8 ctype; ++ u8 subunit; ++ u8 opcode; ++ u8 operand[509]; ++}; ++ ++struct avc_response_frame { ++ u8 response; ++ u8 subunit; ++ u8 opcode; ++ u8 operand[509]; ++}; ++ ++#define LAST_OPERAND (509 - 1) ++ ++static inline void clear_operands(struct avc_command_frame *c, int from, int to) ++{ ++ memset(&c->operand[from], 0, to - from + 1); ++} ++ ++static void pad_operands(struct avc_command_frame *c, int from) ++{ ++ int to = ALIGN(from, 4); ++ ++ if (from <= to && to <= LAST_OPERAND) ++ clear_operands(c, from, to); ++} ++ ++#define AVC_DEBUG_READ_DESCRIPTOR 0x0001 ++#define AVC_DEBUG_DSIT 0x0002 ++#define AVC_DEBUG_DSD 0x0004 ++#define AVC_DEBUG_REGISTER_REMOTE_CONTROL 0x0008 ++#define AVC_DEBUG_LNB_CONTROL 0x0010 ++#define AVC_DEBUG_TUNE_QPSK 0x0020 ++#define AVC_DEBUG_TUNE_QPSK2 0x0040 ++#define AVC_DEBUG_HOST2CA 0x0080 ++#define AVC_DEBUG_CA2HOST 0x0100 ++#define AVC_DEBUG_APPLICATION_PMT 0x4000 ++#define AVC_DEBUG_FCP_PAYLOADS 0x8000 ++ ++static int avc_debug; ++module_param_named(debug, avc_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Verbose logging (none = 0" ++ ", FCP subactions" ++ ": READ DESCRIPTOR = " __stringify(AVC_DEBUG_READ_DESCRIPTOR) ++ ", DSIT = " __stringify(AVC_DEBUG_DSIT) ++ ", REGISTER_REMOTE_CONTROL = " __stringify(AVC_DEBUG_REGISTER_REMOTE_CONTROL) ++ ", LNB CONTROL = " __stringify(AVC_DEBUG_LNB_CONTROL) ++ ", TUNE QPSK = " __stringify(AVC_DEBUG_TUNE_QPSK) ++ ", TUNE QPSK2 = " __stringify(AVC_DEBUG_TUNE_QPSK2) ++ ", HOST2CA = " __stringify(AVC_DEBUG_HOST2CA) ++ ", CA2HOST = " __stringify(AVC_DEBUG_CA2HOST) ++ "; Application sent PMT = " __stringify(AVC_DEBUG_APPLICATION_PMT) ++ ", FCP payloads = " __stringify(AVC_DEBUG_FCP_PAYLOADS) ++ ", or a combination, or all = -1)"); ++ ++/* ++ * This is a workaround since there is no vendor specific command to retrieve ++ * ca_info using AVC. If this parameter is not used, ca_system_id will be ++ * filled with application_manufacturer from ca_app_info. ++ * Digital Everywhere have said that adding ca_info is on their TODO list. ++ */ ++static unsigned int num_fake_ca_system_ids; ++static int fake_ca_system_ids[4] = { -1, -1, -1, -1 }; ++module_param_array(fake_ca_system_ids, int, &num_fake_ca_system_ids, 0644); ++MODULE_PARM_DESC(fake_ca_system_ids, "If your CAM application manufacturer " ++ "does not have the same ca_system_id as your CAS, you can " ++ "override what ca_system_ids are presented to the " ++ "application by setting this field to an array of ids."); ++ ++static const char *debug_fcp_ctype(unsigned int ctype) ++{ ++ static const char *ctypes[] = { ++ [0x0] = "CONTROL", [0x1] = "STATUS", ++ [0x2] = "SPECIFIC INQUIRY", [0x3] = "NOTIFY", ++ [0x4] = "GENERAL INQUIRY", [0x8] = "NOT IMPLEMENTED", ++ [0x9] = "ACCEPTED", [0xa] = "REJECTED", ++ [0xb] = "IN TRANSITION", [0xc] = "IMPLEMENTED/STABLE", ++ [0xd] = "CHANGED", [0xf] = "INTERIM", ++ }; ++ const char *ret = ctype < ARRAY_SIZE(ctypes) ? ctypes[ctype] : NULL; ++ ++ return ret ? ret : "?"; ++} ++ ++static const char *debug_fcp_opcode(unsigned int opcode, ++ const u8 *data, int length) ++{ ++ switch (opcode) { ++ case AVC_OPCODE_VENDOR: ++ break; ++ case AVC_OPCODE_READ_DESCRIPTOR: ++ return avc_debug & AVC_DEBUG_READ_DESCRIPTOR ? ++ "ReadDescriptor" : NULL; ++ case AVC_OPCODE_DSIT: ++ return avc_debug & AVC_DEBUG_DSIT ? ++ "DirectSelectInfo.Type" : NULL; ++ case AVC_OPCODE_DSD: ++ return avc_debug & AVC_DEBUG_DSD ? "DirectSelectData" : NULL; ++ default: ++ return "Unknown"; ++ } ++ ++ if (length < 7 || ++ data[3] != SFE_VENDOR_DE_COMPANYID_0 || ++ data[4] != SFE_VENDOR_DE_COMPANYID_1 || ++ data[5] != SFE_VENDOR_DE_COMPANYID_2) ++ return "Vendor/Unknown"; ++ ++ switch (data[6]) { ++ case SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL: ++ return avc_debug & AVC_DEBUG_REGISTER_REMOTE_CONTROL ? ++ "RegisterRC" : NULL; ++ case SFE_VENDOR_OPCODE_LNB_CONTROL: ++ return avc_debug & AVC_DEBUG_LNB_CONTROL ? "LNBControl" : NULL; ++ case SFE_VENDOR_OPCODE_TUNE_QPSK: ++ return avc_debug & AVC_DEBUG_TUNE_QPSK ? "TuneQPSK" : NULL; ++ case SFE_VENDOR_OPCODE_TUNE_QPSK2: ++ return avc_debug & AVC_DEBUG_TUNE_QPSK2 ? "TuneQPSK2" : NULL; ++ case SFE_VENDOR_OPCODE_HOST2CA: ++ return avc_debug & AVC_DEBUG_HOST2CA ? "Host2CA" : NULL; ++ case SFE_VENDOR_OPCODE_CA2HOST: ++ return avc_debug & AVC_DEBUG_CA2HOST ? "CA2Host" : NULL; ++ } ++ return "Vendor/Unknown"; ++} ++ ++static void debug_fcp(const u8 *data, int length) ++{ ++ unsigned int subunit_type, subunit_id, opcode; ++ const char *op, *prefix; ++ ++ prefix = data[0] > 7 ? "FCP <- " : "FCP -> "; ++ subunit_type = data[1] >> 3; ++ subunit_id = data[1] & 7; ++ opcode = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2]; ++ op = debug_fcp_opcode(opcode, data, length); ++ ++ if (op) { ++ printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n", ++ prefix, subunit_type, subunit_id, length, ++ debug_fcp_ctype(data[0]), op); ++ if (avc_debug & AVC_DEBUG_FCP_PAYLOADS) ++ print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_NONE, ++ 16, 1, data, length, false); ++ } ++} ++ ++static void debug_pmt(char *msg, int length) ++{ ++ printk(KERN_INFO "APP PMT -> l=%d\n", length); ++ print_hex_dump(KERN_INFO, "APP PMT -> ", DUMP_PREFIX_NONE, ++ 16, 1, msg, length, false); ++} ++ ++static int avc_write(struct firedtv *fdtv) ++{ ++ int err, retry; ++ ++ fdtv->avc_reply_received = false; ++ ++ for (retry = 0; retry < 6; retry++) { ++ if (unlikely(avc_debug)) ++ debug_fcp(fdtv->avc_data, fdtv->avc_data_length); ++ ++ err = fdtv_write(fdtv, FCP_COMMAND_REGISTER, ++ fdtv->avc_data, fdtv->avc_data_length); ++ if (err) { ++ dev_err(fdtv->device, "FCP command write failed\n"); ++ ++ return err; ++ } ++ ++ /* ++ * AV/C specs say that answers should be sent within 150 ms. ++ * Time out after 200 ms. ++ */ ++ if (wait_event_timeout(fdtv->avc_wait, ++ fdtv->avc_reply_received, ++ msecs_to_jiffies(200)) != 0) ++ return 0; ++ } ++ dev_err(fdtv->device, "FCP response timed out\n"); ++ ++ return -ETIMEDOUT; ++} ++ ++static bool is_register_rc(struct avc_response_frame *r) ++{ ++ return r->opcode == AVC_OPCODE_VENDOR && ++ r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && ++ r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && ++ r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && ++ r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; ++} ++ ++int avc_recv(struct firedtv *fdtv, void *data, size_t length) ++{ ++ struct avc_response_frame *r = data; ++ ++ if (unlikely(avc_debug)) ++ debug_fcp(data, length); ++ ++ if (length >= 8 && is_register_rc(r)) { ++ switch (r->response) { ++ case AVC_RESPONSE_CHANGED: ++ fdtv_handle_rc(fdtv, r->operand[4] << 8 | r->operand[5]); ++ schedule_work(&fdtv->remote_ctrl_work); ++ break; ++ case AVC_RESPONSE_INTERIM: ++ if (is_register_rc((void *)fdtv->avc_data)) ++ goto wake; ++ break; ++ default: ++ dev_info(fdtv->device, ++ "remote control result = %d\n", r->response); ++ } ++ return 0; ++ } ++ ++ if (fdtv->avc_reply_received) { ++ dev_err(fdtv->device, "out-of-order AVC response, ignored\n"); ++ return -EIO; ++ } ++ ++ memcpy(fdtv->avc_data, data, length); ++ fdtv->avc_data_length = length; ++wake: ++ fdtv->avc_reply_received = true; ++ wake_up(&fdtv->avc_wait); ++ ++ return 0; ++} ++ ++static int add_pid_filter(struct firedtv *fdtv, u8 *operand) ++{ ++ int i, n, pos = 1; ++ ++ for (i = 0, n = 0; i < 16; i++) { ++ if (test_bit(i, &fdtv->channel_active)) { ++ operand[pos++] = 0x13; /* flowfunction relay */ ++ operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ ++ operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f; ++ operand[pos++] = fdtv->channel_pid[i] & 0xff; ++ operand[pos++] = 0x00; /* tableID */ ++ operand[pos++] = 0x00; /* filter_length */ ++ n++; ++ } ++ } ++ operand[0] = n; ++ ++ return pos; ++} ++ ++/* ++ * tuning command for setting the relative LNB frequency ++ * (not supported by the AVC standard) ++ */ ++static int avc_tuner_tuneqpsk(struct firedtv *fdtv, ++ struct dtv_frontend_properties *p) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ if (fdtv->type == FIREDTV_DVB_S2) ++ c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK2; ++ else ++ c->operand[3] = SFE_VENDOR_OPCODE_TUNE_QPSK; ++ ++ c->operand[4] = (p->frequency >> 24) & 0xff; ++ c->operand[5] = (p->frequency >> 16) & 0xff; ++ c->operand[6] = (p->frequency >> 8) & 0xff; ++ c->operand[7] = p->frequency & 0xff; ++ ++ c->operand[8] = ((p->symbol_rate / 1000) >> 8) & 0xff; ++ c->operand[9] = (p->symbol_rate / 1000) & 0xff; ++ ++ switch (p->fec_inner) { ++ case FEC_1_2: c->operand[10] = 0x1; break; ++ case FEC_2_3: c->operand[10] = 0x2; break; ++ case FEC_3_4: c->operand[10] = 0x3; break; ++ case FEC_5_6: c->operand[10] = 0x4; break; ++ case FEC_7_8: c->operand[10] = 0x5; break; ++ case FEC_4_5: ++ case FEC_8_9: ++ case FEC_AUTO: ++ default: c->operand[10] = 0x0; ++ } ++ ++ if (fdtv->voltage == 0xff) ++ c->operand[11] = 0xff; ++ else if (fdtv->voltage == SEC_VOLTAGE_18) /* polarisation */ ++ c->operand[11] = 0; ++ else ++ c->operand[11] = 1; ++ ++ if (fdtv->tone == 0xff) ++ c->operand[12] = 0xff; ++ else if (fdtv->tone == SEC_TONE_ON) /* band */ ++ c->operand[12] = 1; ++ else ++ c->operand[12] = 0; ++ ++ if (fdtv->type == FIREDTV_DVB_S2) { ++ if (fdtv->fe.dtv_property_cache.delivery_system == SYS_DVBS2) { ++ switch (fdtv->fe.dtv_property_cache.modulation) { ++ case QAM_16: c->operand[13] = 0x1; break; ++ case QPSK: c->operand[13] = 0x2; break; ++ case PSK_8: c->operand[13] = 0x3; break; ++ default: c->operand[13] = 0x2; break; ++ } ++ switch (fdtv->fe.dtv_property_cache.rolloff) { ++ case ROLLOFF_35: c->operand[14] = 0x2; break; ++ case ROLLOFF_20: c->operand[14] = 0x0; break; ++ case ROLLOFF_25: c->operand[14] = 0x1; break; ++ case ROLLOFF_AUTO: ++ default: c->operand[14] = 0x2; break; ++ /* case ROLLOFF_NONE: c->operand[14] = 0xff; break; */ ++ } ++ switch (fdtv->fe.dtv_property_cache.pilot) { ++ case PILOT_AUTO: c->operand[15] = 0x0; break; ++ case PILOT_OFF: c->operand[15] = 0x0; break; ++ case PILOT_ON: c->operand[15] = 0x1; break; ++ } ++ } else { ++ c->operand[13] = 0x1; /* auto modulation */ ++ c->operand[14] = 0xff; /* disable rolloff */ ++ c->operand[15] = 0xff; /* disable pilot */ ++ } ++ return 16; ++ } else { ++ return 13; ++ } ++} ++ ++static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv, ++ struct dtv_frontend_properties *p) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ ++ c->opcode = AVC_OPCODE_DSD; ++ ++ c->operand[0] = 0; /* source plug */ ++ c->operand[1] = 0xd2; /* subfunction replace */ ++ c->operand[2] = 0x20; /* system id = DVB */ ++ c->operand[3] = 0x00; /* antenna number */ ++ c->operand[4] = 0x11; /* system_specific_multiplex selection_length */ ++ ++ /* multiplex_valid_flags, high byte */ ++ c->operand[5] = 0 << 7 /* reserved */ ++ | 0 << 6 /* Polarisation */ ++ | 0 << 5 /* Orbital_Pos */ ++ | 1 << 4 /* Frequency */ ++ | 1 << 3 /* Symbol_Rate */ ++ | 0 << 2 /* FEC_outer */ ++ | (p->fec_inner != FEC_AUTO ? 1 << 1 : 0) ++ | (p->modulation != QAM_AUTO ? 1 << 0 : 0); ++ ++ /* multiplex_valid_flags, low byte */ ++ c->operand[6] = 0 << 7 /* NetworkID */ ++ | 0 << 0 /* reserved */ ; ++ ++ c->operand[7] = 0x00; ++ c->operand[8] = 0x00; ++ c->operand[9] = 0x00; ++ c->operand[10] = 0x00; ++ ++ c->operand[11] = (((p->frequency / 4000) >> 16) & 0xff) | (2 << 6); ++ c->operand[12] = ((p->frequency / 4000) >> 8) & 0xff; ++ c->operand[13] = (p->frequency / 4000) & 0xff; ++ c->operand[14] = ((p->symbol_rate / 1000) >> 12) & 0xff; ++ c->operand[15] = ((p->symbol_rate / 1000) >> 4) & 0xff; ++ c->operand[16] = ((p->symbol_rate / 1000) << 4) & 0xf0; ++ c->operand[17] = 0x00; ++ ++ switch (p->fec_inner) { ++ case FEC_1_2: c->operand[18] = 0x1; break; ++ case FEC_2_3: c->operand[18] = 0x2; break; ++ case FEC_3_4: c->operand[18] = 0x3; break; ++ case FEC_5_6: c->operand[18] = 0x4; break; ++ case FEC_7_8: c->operand[18] = 0x5; break; ++ case FEC_8_9: c->operand[18] = 0x6; break; ++ case FEC_4_5: c->operand[18] = 0x8; break; ++ case FEC_AUTO: ++ default: c->operand[18] = 0x0; ++ } ++ ++ switch (p->modulation) { ++ case QAM_16: c->operand[19] = 0x08; break; ++ case QAM_32: c->operand[19] = 0x10; break; ++ case QAM_64: c->operand[19] = 0x18; break; ++ case QAM_128: c->operand[19] = 0x20; break; ++ case QAM_256: c->operand[19] = 0x28; break; ++ case QAM_AUTO: ++ default: c->operand[19] = 0x00; ++ } ++ ++ c->operand[20] = 0x00; ++ c->operand[21] = 0x00; ++ ++ return 22 + add_pid_filter(fdtv, &c->operand[22]); ++} ++ ++static int avc_tuner_dsd_dvb_t(struct firedtv *fdtv, ++ struct dtv_frontend_properties *p) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ ++ c->opcode = AVC_OPCODE_DSD; ++ ++ c->operand[0] = 0; /* source plug */ ++ c->operand[1] = 0xd2; /* subfunction replace */ ++ c->operand[2] = 0x20; /* system id = DVB */ ++ c->operand[3] = 0x00; /* antenna number */ ++ c->operand[4] = 0x0c; /* system_specific_multiplex selection_length */ ++ ++ /* multiplex_valid_flags, high byte */ ++ c->operand[5] = ++ 0 << 7 /* reserved */ ++ | 1 << 6 /* CenterFrequency */ ++ | (p->bandwidth_hz != 0 ? 1 << 5 : 0) ++ | (p->modulation != QAM_AUTO ? 1 << 4 : 0) ++ | (p->hierarchy != HIERARCHY_AUTO ? 1 << 3 : 0) ++ | (p->code_rate_HP != FEC_AUTO ? 1 << 2 : 0) ++ | (p->code_rate_LP != FEC_AUTO ? 1 << 1 : 0) ++ | (p->guard_interval != GUARD_INTERVAL_AUTO ? 1 << 0 : 0); ++ ++ /* multiplex_valid_flags, low byte */ ++ c->operand[6] = ++ 0 << 7 /* NetworkID */ ++ | (p->transmission_mode != TRANSMISSION_MODE_AUTO ? 1 << 6 : 0) ++ | 0 << 5 /* OtherFrequencyFlag */ ++ | 0 << 0 /* reserved */ ; ++ ++ c->operand[7] = 0x0; ++ c->operand[8] = (p->frequency / 10) >> 24; ++ c->operand[9] = ((p->frequency / 10) >> 16) & 0xff; ++ c->operand[10] = ((p->frequency / 10) >> 8) & 0xff; ++ c->operand[11] = (p->frequency / 10) & 0xff; ++ ++ switch (p->bandwidth_hz) { ++ case 7000000: c->operand[12] = 0x20; break; ++ case 8000000: ++ case 6000000: /* not defined by AVC spec */ ++ case 0: ++ default: c->operand[12] = 0x00; ++ } ++ ++ switch (p->modulation) { ++ case QAM_16: c->operand[13] = 1 << 6; break; ++ case QAM_64: c->operand[13] = 2 << 6; break; ++ case QPSK: ++ default: c->operand[13] = 0x00; ++ } ++ ++ switch (p->hierarchy) { ++ case HIERARCHY_1: c->operand[13] |= 1 << 3; break; ++ case HIERARCHY_2: c->operand[13] |= 2 << 3; break; ++ case HIERARCHY_4: c->operand[13] |= 3 << 3; break; ++ case HIERARCHY_AUTO: ++ case HIERARCHY_NONE: ++ default: break; ++ } ++ ++ switch (p->code_rate_HP) { ++ case FEC_2_3: c->operand[13] |= 1; break; ++ case FEC_3_4: c->operand[13] |= 2; break; ++ case FEC_5_6: c->operand[13] |= 3; break; ++ case FEC_7_8: c->operand[13] |= 4; break; ++ case FEC_1_2: ++ default: break; ++ } ++ ++ switch (p->code_rate_LP) { ++ case FEC_2_3: c->operand[14] = 1 << 5; break; ++ case FEC_3_4: c->operand[14] = 2 << 5; break; ++ case FEC_5_6: c->operand[14] = 3 << 5; break; ++ case FEC_7_8: c->operand[14] = 4 << 5; break; ++ case FEC_1_2: ++ default: c->operand[14] = 0x00; break; ++ } ++ ++ switch (p->guard_interval) { ++ case GUARD_INTERVAL_1_16: c->operand[14] |= 1 << 3; break; ++ case GUARD_INTERVAL_1_8: c->operand[14] |= 2 << 3; break; ++ case GUARD_INTERVAL_1_4: c->operand[14] |= 3 << 3; break; ++ case GUARD_INTERVAL_1_32: ++ case GUARD_INTERVAL_AUTO: ++ default: break; ++ } ++ ++ switch (p->transmission_mode) { ++ case TRANSMISSION_MODE_8K: c->operand[14] |= 1 << 1; break; ++ case TRANSMISSION_MODE_2K: ++ case TRANSMISSION_MODE_AUTO: ++ default: break; ++ } ++ ++ c->operand[15] = 0x00; /* network_ID[0] */ ++ c->operand[16] = 0x00; /* network_ID[1] */ ++ ++ return 17 + add_pid_filter(fdtv, &c->operand[17]); ++} ++ ++int avc_tuner_dsd(struct firedtv *fdtv, ++ struct dtv_frontend_properties *p) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int pos, ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ ++ switch (fdtv->type) { ++ case FIREDTV_DVB_S: ++ case FIREDTV_DVB_S2: pos = avc_tuner_tuneqpsk(fdtv, p); break; ++ case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, p); break; ++ case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, p); break; ++ default: ++ BUG(); ++ } ++ pad_operands(c, pos); ++ ++ fdtv->avc_data_length = ALIGN(3 + pos, 4); ++ ret = avc_write(fdtv); ++#if 0 ++ /* ++ * FIXME: ++ * u8 *status was an out-parameter of avc_tuner_dsd, unused by caller. ++ * Check for AVC_RESPONSE_ACCEPTED here instead? ++ */ ++ if (status) ++ *status = r->operand[2]; ++#endif ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ if (ret == 0) ++ msleep(500); ++ ++ return ret; ++} ++ ++int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret, pos, k; ++ ++ if (pidc > 16 && pidc != 0xff) ++ return -EINVAL; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_DSD; ++ ++ c->operand[0] = 0; /* source plug */ ++ c->operand[1] = 0xd2; /* subfunction replace */ ++ c->operand[2] = 0x20; /* system id = DVB */ ++ c->operand[3] = 0x00; /* antenna number */ ++ c->operand[4] = 0x00; /* system_specific_multiplex selection_length */ ++ c->operand[5] = pidc; /* Nr_of_dsd_sel_specs */ ++ ++ pos = 6; ++ if (pidc != 0xff) ++ for (k = 0; k < pidc; k++) { ++ c->operand[pos++] = 0x13; /* flowfunction relay */ ++ c->operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */ ++ c->operand[pos++] = (pid[k] >> 8) & 0x1f; ++ c->operand[pos++] = pid[k] & 0xff; ++ c->operand[pos++] = 0x00; /* tableID */ ++ c->operand[pos++] = 0x00; /* filter_length */ ++ } ++ pad_operands(c, pos); ++ ++ fdtv->avc_data_length = ALIGN(3 + pos, 4); ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ if (ret == 0) ++ msleep(50); ++ ++ return ret; ++} ++ ++int avc_tuner_get_ts(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret, sl; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_DSIT; ++ ++ sl = fdtv->type == FIREDTV_DVB_T ? 0x0c : 0x11; ++ ++ c->operand[0] = 0; /* source plug */ ++ c->operand[1] = 0xd2; /* subfunction replace */ ++ c->operand[2] = 0xff; /* status */ ++ c->operand[3] = 0x20; /* system id = DVB */ ++ c->operand[4] = 0x00; /* antenna number */ ++ c->operand[5] = 0x0; /* system_specific_search_flags */ ++ c->operand[6] = sl; /* system_specific_multiplex selection_length */ ++ /* ++ * operand[7]: valid_flags[0] ++ * operand[8]: valid_flags[1] ++ * operand[7 + sl]: nr_of_dsit_sel_specs (always 0) ++ */ ++ clear_operands(c, 7, 24); ++ ++ fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ if (ret == 0) ++ msleep(250); ++ ++ return ret; ++} ++ ++int avc_identify_subunit(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_READ_DESCRIPTOR; ++ ++ c->operand[0] = DESCRIPTOR_SUBUNIT_IDENTIFIER; ++ c->operand[1] = 0xff; ++ c->operand[2] = 0x00; ++ c->operand[3] = 0x00; /* length highbyte */ ++ c->operand[4] = 0x08; /* length lowbyte */ ++ c->operand[5] = 0x00; /* offset highbyte */ ++ c->operand[6] = 0x0d; /* offset lowbyte */ ++ clear_operands(c, 7, 8); /* padding */ ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ if ((r->response != AVC_RESPONSE_STABLE && ++ r->response != AVC_RESPONSE_ACCEPTED) || ++ (r->operand[3] << 8) + r->operand[4] != 8) { ++ dev_err(fdtv->device, "cannot read subunit identifier\n"); ++ ret = -EINVAL; ++ } ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++#define SIZEOF_ANTENNA_INPUT_INFO 22 ++ ++int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int length, ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_READ_DESCRIPTOR; ++ ++ c->operand[0] = DESCRIPTOR_TUNER_STATUS; ++ c->operand[1] = 0xff; /* read_result_status */ ++ /* ++ * operand[2]: reserved ++ * operand[3]: SIZEOF_ANTENNA_INPUT_INFO >> 8 ++ * operand[4]: SIZEOF_ANTENNA_INPUT_INFO & 0xff ++ */ ++ clear_operands(c, 2, 31); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ if (r->response != AVC_RESPONSE_STABLE && ++ r->response != AVC_RESPONSE_ACCEPTED) { ++ dev_err(fdtv->device, "cannot read tuner status\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ length = r->operand[9]; ++ if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { ++ dev_err(fdtv->device, "got invalid tuner status\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ stat->active_system = r->operand[10]; ++ stat->searching = r->operand[11] >> 7 & 1; ++ stat->moving = r->operand[11] >> 6 & 1; ++ stat->no_rf = r->operand[11] >> 5 & 1; ++ stat->input = r->operand[12] >> 7 & 1; ++ stat->selected_antenna = r->operand[12] & 0x7f; ++ stat->ber = r->operand[13] << 24 | ++ r->operand[14] << 16 | ++ r->operand[15] << 8 | ++ r->operand[16]; ++ stat->signal_strength = r->operand[17]; ++ stat->raster_frequency = r->operand[18] >> 6 & 2; ++ stat->rf_frequency = (r->operand[18] & 0x3f) << 16 | ++ r->operand[19] << 8 | ++ r->operand[20]; ++ stat->man_dep_info_length = r->operand[21]; ++ stat->front_end_error = r->operand[22] >> 4 & 1; ++ stat->antenna_error = r->operand[22] >> 3 & 1; ++ stat->front_end_power_status = r->operand[22] >> 1 & 1; ++ stat->power_supply = r->operand[22] & 1; ++ stat->carrier_noise_ratio = r->operand[23] << 8 | ++ r->operand[24]; ++ stat->power_supply_voltage = r->operand[27]; ++ stat->antenna_voltage = r->operand[28]; ++ stat->firewire_bus_voltage = r->operand[29]; ++ stat->ca_mmi = r->operand[30] & 1; ++ stat->ca_pmt_reply = r->operand[31] >> 7 & 1; ++ stat->ca_date_time_request = r->operand[31] >> 6 & 1; ++ stat->ca_application_info = r->operand[31] >> 5 & 1; ++ stat->ca_module_present_status = r->operand[31] >> 4 & 1; ++ stat->ca_dvb_flag = r->operand[31] >> 3 & 1; ++ stat->ca_error_flag = r->operand[31] >> 2 & 1; ++ stat->ca_initialization_status = r->operand[31] >> 1 & 1; ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, ++ char conttone, char nrdiseq, ++ struct dvb_diseqc_master_cmd *diseqcmd) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int pos, j, k, ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; ++ c->operand[4] = voltage; ++ c->operand[5] = nrdiseq; ++ ++ pos = 6; ++ for (j = 0; j < nrdiseq; j++) { ++ c->operand[pos++] = diseqcmd[j].msg_len; ++ ++ for (k = 0; k < diseqcmd[j].msg_len; k++) ++ c->operand[pos++] = diseqcmd[j].msg[k]; ++ } ++ c->operand[pos++] = burst; ++ c->operand[pos++] = conttone; ++ pad_operands(c, pos); ++ ++ fdtv->avc_data_length = ALIGN(3 + pos, 4); ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ if (r->response != AVC_RESPONSE_ACCEPTED) { ++ dev_err(fdtv->device, "LNB control failed\n"); ++ ret = -EINVAL; ++ } ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_register_remote_control(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_NOTIFY; ++ c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; ++ c->operand[4] = 0; /* padding */ ++ ++ fdtv->avc_data_length = 8; ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++void avc_remote_ctrl_work(struct work_struct *work) ++{ ++ struct firedtv *fdtv = ++ container_of(work, struct firedtv, remote_ctrl_work); ++ ++ /* Should it be rescheduled in failure cases? */ ++ avc_register_remote_control(fdtv); ++} ++ ++#if 0 /* FIXME: unused */ ++int avc_tuner_host2ca(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ ++ clear_operands(c, 6, 8); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++#endif ++ ++static int get_ca_object_pos(struct avc_response_frame *r) ++{ ++ int length = 1; ++ ++ /* Check length of length field */ ++ if (r->operand[7] & 0x80) ++ length = (r->operand[7] & 0x7f) + 1; ++ return length + 7; ++} ++ ++static int get_ca_object_length(struct avc_response_frame *r) ++{ ++#if 0 /* FIXME: unused */ ++ int size = 0; ++ int i; ++ ++ if (r->operand[7] & 0x80) ++ for (i = 0; i < (r->operand[7] & 0x7f); i++) { ++ size <<= 8; ++ size += r->operand[8 + i]; ++ } ++#endif ++ return r->operand[7]; ++} ++ ++int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int pos, ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_STATUS; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ ++ clear_operands(c, 6, LAST_OPERAND); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ /* FIXME: check response code and validate response data */ ++ ++ pos = get_ca_object_pos(r); ++ app_info[0] = (EN50221_TAG_APP_INFO >> 16) & 0xff; ++ app_info[1] = (EN50221_TAG_APP_INFO >> 8) & 0xff; ++ app_info[2] = (EN50221_TAG_APP_INFO >> 0) & 0xff; ++ app_info[3] = 6 + r->operand[pos + 4]; ++ app_info[4] = 0x01; ++ memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); ++ *len = app_info[3] + 4; ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int i, pos, ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_STATUS; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ ++ clear_operands(c, 6, LAST_OPERAND); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ /* FIXME: check response code and validate response data */ ++ ++ pos = get_ca_object_pos(r); ++ app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; ++ app_info[1] = (EN50221_TAG_CA_INFO >> 8) & 0xff; ++ app_info[2] = (EN50221_TAG_CA_INFO >> 0) & 0xff; ++ if (num_fake_ca_system_ids == 0) { ++ app_info[3] = 2; ++ app_info[4] = r->operand[pos + 0]; ++ app_info[5] = r->operand[pos + 1]; ++ } else { ++ app_info[3] = num_fake_ca_system_ids * 2; ++ for (i = 0; i < num_fake_ca_system_ids; i++) { ++ app_info[4 + i * 2] = ++ (fake_ca_system_ids[i] >> 8) & 0xff; ++ app_info[5 + i * 2] = fake_ca_system_ids[i] & 0xff; ++ } ++ } ++ *len = app_info[3] + 4; ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_reset(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_RESET; /* ca tag */ ++ c->operand[6] = 0; /* more/last */ ++ c->operand[7] = 1; /* length */ ++ c->operand[8] = 0; /* force hardware reset */ ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int list_management; ++ int program_info_length; ++ int pmt_cmd_id; ++ int read_pos; ++ int write_pos; ++ int es_info_length; ++ int crc32_csum; ++ int ret; ++ ++ if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT)) ++ debug_pmt(msg, length); ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_CONTROL; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ if (msg[0] != EN50221_LIST_MANAGEMENT_ONLY) { ++ dev_info(fdtv->device, "forcing list_management to ONLY\n"); ++ msg[0] = EN50221_LIST_MANAGEMENT_ONLY; ++ } ++ /* We take the cmd_id from the programme level only! */ ++ list_management = msg[0]; ++ program_info_length = ((msg[4] & 0x0f) << 8) + msg[5]; ++ if (program_info_length > 0) ++ program_info_length--; /* Remove pmt_cmd_id */ ++ pmt_cmd_id = msg[6]; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_PMT; /* ca tag */ ++ c->operand[6] = 0; /* more/last */ ++ /* Use three bytes for length field in case length > 127 */ ++ c->operand[10] = list_management; ++ c->operand[11] = 0x01; /* pmt_cmd=OK_descramble */ ++ ++ /* TS program map table */ ++ ++ c->operand[12] = 0x02; /* Table id=2 */ ++ c->operand[13] = 0x80; /* Section syntax + length */ ++ ++ c->operand[15] = msg[1]; /* Program number */ ++ c->operand[16] = msg[2]; ++ c->operand[17] = msg[3]; /* Version number and current/next */ ++ c->operand[18] = 0x00; /* Section number=0 */ ++ c->operand[19] = 0x00; /* Last section number=0 */ ++ c->operand[20] = 0x1f; /* PCR_PID=1FFF */ ++ c->operand[21] = 0xff; ++ c->operand[22] = (program_info_length >> 8); /* Program info length */ ++ c->operand[23] = (program_info_length & 0xff); ++ ++ /* CA descriptors at programme level */ ++ read_pos = 6; ++ write_pos = 24; ++ if (program_info_length > 0) { ++ pmt_cmd_id = msg[read_pos++]; ++ if (pmt_cmd_id != 1 && pmt_cmd_id != 4) ++ dev_err(fdtv->device, ++ "invalid pmt_cmd_id %d\n", pmt_cmd_id); ++ ++ memcpy(&c->operand[write_pos], &msg[read_pos], ++ program_info_length); ++ read_pos += program_info_length; ++ write_pos += program_info_length; ++ } ++ while (read_pos < length) { ++ c->operand[write_pos++] = msg[read_pos++]; ++ c->operand[write_pos++] = msg[read_pos++]; ++ c->operand[write_pos++] = msg[read_pos++]; ++ es_info_length = ++ ((msg[read_pos] & 0x0f) << 8) + msg[read_pos + 1]; ++ read_pos += 2; ++ if (es_info_length > 0) ++ es_info_length--; /* Remove pmt_cmd_id */ ++ c->operand[write_pos++] = es_info_length >> 8; ++ c->operand[write_pos++] = es_info_length & 0xff; ++ if (es_info_length > 0) { ++ pmt_cmd_id = msg[read_pos++]; ++ if (pmt_cmd_id != 1 && pmt_cmd_id != 4) ++ dev_err(fdtv->device, "invalid pmt_cmd_id %d " ++ "at stream level\n", pmt_cmd_id); ++ ++ memcpy(&c->operand[write_pos], &msg[read_pos], ++ es_info_length); ++ read_pos += es_info_length; ++ write_pos += es_info_length; ++ } ++ } ++ write_pos += 4; /* CRC */ ++ ++ c->operand[7] = 0x82; ++ c->operand[8] = (write_pos - 10) >> 8; ++ c->operand[9] = (write_pos - 10) & 0xff; ++ c->operand[14] = write_pos - 15; ++ ++ crc32_csum = crc32_be(0, &c->operand[10], c->operand[12] - 1); ++ c->operand[write_pos - 4] = (crc32_csum >> 24) & 0xff; ++ c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; ++ c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; ++ c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; ++ pad_operands(c, write_pos); ++ ++ fdtv->avc_data_length = ALIGN(3 + write_pos, 4); ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ if (r->response != AVC_RESPONSE_ACCEPTED) { ++ dev_err(fdtv->device, ++ "CA PMT failed with response 0x%x\n", r->response); ++ ret = -EACCES; ++ } ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_STATUS; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ ++ clear_operands(c, 6, LAST_OPERAND); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ /* FIXME: check response code and validate response data */ ++ ++ *interval = r->operand[get_ca_object_pos(r)]; ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_enter_menu(struct firedtv *fdtv) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_STATUS; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; ++ clear_operands(c, 6, 8); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ ++ /* FIXME: check response code? */ ++ ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) ++{ ++ struct avc_command_frame *c = (void *)fdtv->avc_data; ++ struct avc_response_frame *r = (void *)fdtv->avc_data; ++ int ret; ++ ++ mutex_lock(&fdtv->avc_mutex); ++ ++ c->ctype = AVC_CTYPE_STATUS; ++ c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; ++ c->opcode = AVC_OPCODE_VENDOR; ++ ++ c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; ++ c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; ++ c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; ++ c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; ++ c->operand[4] = 0; /* slot */ ++ c->operand[5] = SFE_VENDOR_TAG_CA_MMI; ++ clear_operands(c, 6, LAST_OPERAND); ++ ++ fdtv->avc_data_length = 12; ++ ret = avc_write(fdtv); ++ if (ret < 0) ++ goto out; ++ ++ /* FIXME: check response code and validate response data */ ++ ++ *len = get_ca_object_length(r); ++ memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); ++out: ++ mutex_unlock(&fdtv->avc_mutex); ++ ++ return ret; ++} ++ ++#define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL ++ ++static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) ++{ ++ int ret; ++ ++ ret = fdtv_read(fdtv, addr, data); ++ if (ret < 0) ++ dev_err(fdtv->device, "CMP: read I/O error\n"); ++ ++ return ret; ++} ++ ++static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) ++{ ++ int ret; ++ ++ ret = fdtv_lock(fdtv, addr, data); ++ if (ret < 0) ++ dev_err(fdtv->device, "CMP: lock I/O error\n"); ++ ++ return ret; ++} ++ ++static inline u32 get_opcr(__be32 opcr, u32 mask, u32 shift) ++{ ++ return (be32_to_cpu(opcr) >> shift) & mask; ++} ++ ++static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) ++{ ++ *opcr &= ~cpu_to_be32(mask << shift); ++ *opcr |= cpu_to_be32((value & mask) << shift); ++} ++ ++#define get_opcr_online(v) get_opcr((v), 0x1, 31) ++#define get_opcr_p2p_connections(v) get_opcr((v), 0x3f, 24) ++#define get_opcr_channel(v) get_opcr((v), 0x3f, 16) ++ ++#define set_opcr_p2p_connections(p, v) set_opcr((p), (v), 0x3f, 24) ++#define set_opcr_channel(p, v) set_opcr((p), (v), 0x3f, 16) ++#define set_opcr_data_rate(p, v) set_opcr((p), (v), 0x3, 14) ++#define set_opcr_overhead_id(p, v) set_opcr((p), (v), 0xf, 10) ++ ++int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) ++{ ++ __be32 old_opcr, opcr[2]; ++ u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); ++ int attempts = 0; ++ int ret; ++ ++ ret = cmp_read(fdtv, opcr_address, opcr); ++ if (ret < 0) ++ return ret; ++ ++repeat: ++ if (!get_opcr_online(*opcr)) { ++ dev_err(fdtv->device, "CMP: output offline\n"); ++ return -EBUSY; ++ } ++ ++ old_opcr = *opcr; ++ ++ if (get_opcr_p2p_connections(*opcr)) { ++ if (get_opcr_channel(*opcr) != channel) { ++ dev_err(fdtv->device, "CMP: cannot change channel\n"); ++ return -EBUSY; ++ } ++ dev_info(fdtv->device, "CMP: overlaying connection\n"); ++ ++ /* We don't allocate isochronous resources. */ ++ } else { ++ set_opcr_channel(opcr, channel); ++ set_opcr_data_rate(opcr, 2); /* S400 */ ++ ++ /* FIXME: this is for the worst case - optimize */ ++ set_opcr_overhead_id(opcr, 0); ++ ++ /* FIXME: allocate isochronous channel and bandwidth at IRM */ ++ } ++ ++ set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1); ++ ++ opcr[1] = *opcr; ++ opcr[0] = old_opcr; ++ ++ ret = cmp_lock(fdtv, opcr_address, opcr); ++ if (ret < 0) ++ return ret; ++ ++ if (old_opcr != *opcr) { ++ /* ++ * FIXME: if old_opcr.P2P_Connections > 0, ++ * deallocate isochronous channel and bandwidth at IRM ++ */ ++ ++ if (++attempts < 6) /* arbitrary limit */ ++ goto repeat; ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) ++{ ++ __be32 old_opcr, opcr[2]; ++ u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); ++ int attempts = 0; ++ ++ if (cmp_read(fdtv, opcr_address, opcr) < 0) ++ return; ++ ++repeat: ++ if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) || ++ get_opcr_channel(*opcr) != channel) { ++ dev_err(fdtv->device, "CMP: no connection to break\n"); ++ return; ++ } ++ ++ old_opcr = *opcr; ++ set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1); ++ ++ opcr[1] = *opcr; ++ opcr[0] = old_opcr; ++ ++ if (cmp_lock(fdtv, opcr_address, opcr) < 0) ++ return; ++ ++ if (old_opcr != *opcr) { ++ /* ++ * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last ++ * owner, deallocate isochronous channel and bandwidth at IRM ++ * if (...) ++ * fdtv->backend->dealloc_resources(fdtv, channel, bw); ++ */ ++ ++ if (++attempts < 6) /* arbitrary limit */ ++ goto repeat; ++ } ++} +diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c +new file mode 100644 +index 0000000..e5ebdbf +--- /dev/null ++++ b/drivers/media/firewire/firedtv-ci.c +@@ -0,0 +1,258 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * Copyright (C) 2008 Henrik Kurelid ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "firedtv.h" ++ ++#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 ++#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 ++#define EN50221_TAG_CA_PMT 0x9f8032 ++#define EN50221_TAG_ENTER_MENU 0x9f8022 ++ ++static int fdtv_ca_ready(struct firedtv_tuner_status *stat) ++{ ++ return stat->ca_initialization_status == 1 && ++ stat->ca_error_flag == 0 && ++ stat->ca_dvb_flag == 1 && ++ stat->ca_module_present_status == 1; ++} ++ ++static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) ++{ ++ int flags = 0; ++ ++ if (stat->ca_module_present_status == 1) ++ flags |= CA_CI_MODULE_PRESENT; ++ if (stat->ca_initialization_status == 1 && ++ stat->ca_error_flag == 0 && ++ stat->ca_dvb_flag == 1) ++ flags |= CA_CI_MODULE_READY; ++ return flags; ++} ++ ++static int fdtv_ca_get_caps(void *arg) ++{ ++ struct ca_caps *cap = arg; ++ ++ cap->slot_num = 1; ++ cap->slot_type = CA_CI; ++ cap->descr_num = 1; ++ cap->descr_type = CA_ECD; ++ return 0; ++} ++ ++static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) ++{ ++ struct firedtv_tuner_status stat; ++ struct ca_slot_info *slot = arg; ++ int err; ++ ++ err = avc_tuner_status(fdtv, &stat); ++ if (err) ++ return err; ++ ++ if (slot->num != 0) ++ return -EACCES; ++ ++ slot->type = CA_CI; ++ slot->flags = fdtv_get_ca_flags(&stat); ++ return 0; ++} ++ ++static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) ++{ ++ struct ca_msg *reply = arg; ++ ++ return avc_ca_app_info(fdtv, reply->msg, &reply->length); ++} ++ ++static int fdtv_ca_info(struct firedtv *fdtv, void *arg) ++{ ++ struct ca_msg *reply = arg; ++ ++ return avc_ca_info(fdtv, reply->msg, &reply->length); ++} ++ ++static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) ++{ ++ struct ca_msg *reply = arg; ++ ++ return avc_ca_get_mmi(fdtv, reply->msg, &reply->length); ++} ++ ++static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) ++{ ++ struct firedtv_tuner_status stat; ++ int err; ++ ++ switch (fdtv->ca_last_command) { ++ case EN50221_TAG_APP_INFO_ENQUIRY: ++ err = fdtv_ca_app_info(fdtv, arg); ++ break; ++ case EN50221_TAG_CA_INFO_ENQUIRY: ++ err = fdtv_ca_info(fdtv, arg); ++ break; ++ default: ++ err = avc_tuner_status(fdtv, &stat); ++ if (err) ++ break; ++ if (stat.ca_mmi == 1) ++ err = fdtv_ca_get_mmi(fdtv, arg); ++ else { ++ dev_info(fdtv->device, "unhandled CA message 0x%08x\n", ++ fdtv->ca_last_command); ++ err = -EACCES; ++ } ++ } ++ fdtv->ca_last_command = 0; ++ return err; ++} ++ ++static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) ++{ ++ struct ca_msg *msg = arg; ++ int data_pos; ++ int data_length; ++ int i; ++ ++ data_pos = 4; ++ if (msg->msg[3] & 0x80) { ++ data_length = 0; ++ for (i = 0; i < (msg->msg[3] & 0x7f); i++) ++ data_length = (data_length << 8) + msg->msg[data_pos++]; ++ } else { ++ data_length = msg->msg[3]; ++ } ++ ++ return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); ++} ++ ++static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) ++{ ++ struct ca_msg *msg = arg; ++ int err; ++ ++ /* Do we need a semaphore for this? */ ++ fdtv->ca_last_command = ++ (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; ++ switch (fdtv->ca_last_command) { ++ case EN50221_TAG_CA_PMT: ++ err = fdtv_ca_pmt(fdtv, arg); ++ break; ++ case EN50221_TAG_APP_INFO_ENQUIRY: ++ /* handled in ca_get_msg */ ++ err = 0; ++ break; ++ case EN50221_TAG_CA_INFO_ENQUIRY: ++ /* handled in ca_get_msg */ ++ err = 0; ++ break; ++ case EN50221_TAG_ENTER_MENU: ++ err = avc_ca_enter_menu(fdtv); ++ break; ++ default: ++ dev_err(fdtv->device, "unhandled CA message 0x%08x\n", ++ fdtv->ca_last_command); ++ err = -EACCES; ++ } ++ return err; ++} ++ ++static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct firedtv *fdtv = dvbdev->priv; ++ struct firedtv_tuner_status stat; ++ int err; ++ ++ switch (cmd) { ++ case CA_RESET: ++ err = avc_ca_reset(fdtv); ++ break; ++ case CA_GET_CAP: ++ err = fdtv_ca_get_caps(arg); ++ break; ++ case CA_GET_SLOT_INFO: ++ err = fdtv_ca_get_slot_info(fdtv, arg); ++ break; ++ case CA_GET_MSG: ++ err = fdtv_ca_get_msg(fdtv, arg); ++ break; ++ case CA_SEND_MSG: ++ err = fdtv_ca_send_msg(fdtv, arg); ++ break; ++ default: ++ dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); ++ err = -EOPNOTSUPP; ++ } ++ ++ /* FIXME Is this necessary? */ ++ avc_tuner_status(fdtv, &stat); ++ ++ return err; ++} ++ ++static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) ++{ ++ return POLLIN; ++} ++ ++static const struct file_operations fdtv_ca_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .open = dvb_generic_open, ++ .release = dvb_generic_release, ++ .poll = fdtv_ca_io_poll, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device fdtv_ca = { ++ .users = 1, ++ .readers = 1, ++ .writers = 1, ++ .fops = &fdtv_ca_fops, ++ .kernel_ioctl = fdtv_ca_ioctl, ++}; ++ ++int fdtv_ca_register(struct firedtv *fdtv) ++{ ++ struct firedtv_tuner_status stat; ++ int err; ++ ++ if (avc_tuner_status(fdtv, &stat)) ++ return -EINVAL; ++ ++ if (!fdtv_ca_ready(&stat)) ++ return -EFAULT; ++ ++ err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, ++ &fdtv_ca, fdtv, DVB_DEVICE_CA); ++ ++ if (stat.ca_application_info == 0) ++ dev_err(fdtv->device, "CaApplicationInfo is not set\n"); ++ if (stat.ca_date_time_request == 1) ++ avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); ++ ++ return err; ++} ++ ++void fdtv_ca_release(struct firedtv *fdtv) ++{ ++ if (fdtv->cadev) ++ dvb_unregister_device(fdtv->cadev); ++} +diff --git a/drivers/media/firewire/firedtv-dvb.c b/drivers/media/firewire/firedtv-dvb.c +new file mode 100644 +index 0000000..eb7496e +--- /dev/null ++++ b/drivers/media/firewire/firedtv-dvb.c +@@ -0,0 +1,248 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * Copyright (C) 2008 Henrik Kurelid ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "firedtv.h" ++ ++static int alloc_channel(struct firedtv *fdtv) ++{ ++ int i; ++ ++ for (i = 0; i < 16; i++) ++ if (!__test_and_set_bit(i, &fdtv->channel_active)) ++ break; ++ return i; ++} ++ ++static void collect_channels(struct firedtv *fdtv, int *pidc, u16 pid[]) ++{ ++ int i, n; ++ ++ for (i = 0, n = 0; i < 16; i++) ++ if (test_bit(i, &fdtv->channel_active)) ++ pid[n++] = fdtv->channel_pid[i]; ++ *pidc = n; ++} ++ ++static inline void dealloc_channel(struct firedtv *fdtv, int i) ++{ ++ __clear_bit(i, &fdtv->channel_active); ++} ++ ++int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct firedtv *fdtv = dvbdmxfeed->demux->priv; ++ int pidc, c, ret; ++ u16 pids[16]; ++ ++ switch (dvbdmxfeed->type) { ++ case DMX_TYPE_TS: ++ case DMX_TYPE_SEC: ++ break; ++ default: ++ dev_err(fdtv->device, "can't start dmx feed: invalid type %u\n", ++ dvbdmxfeed->type); ++ return -EINVAL; ++ } ++ ++ if (mutex_lock_interruptible(&fdtv->demux_mutex)) ++ return -EINTR; ++ ++ if (dvbdmxfeed->type == DMX_TYPE_TS) { ++ switch (dvbdmxfeed->pes_type) { ++ case DMX_TS_PES_VIDEO: ++ case DMX_TS_PES_AUDIO: ++ case DMX_TS_PES_TELETEXT: ++ case DMX_TS_PES_PCR: ++ case DMX_TS_PES_OTHER: ++ c = alloc_channel(fdtv); ++ break; ++ default: ++ dev_err(fdtv->device, ++ "can't start dmx feed: invalid pes type %u\n", ++ dvbdmxfeed->pes_type); ++ ret = -EINVAL; ++ goto out; ++ } ++ } else { ++ c = alloc_channel(fdtv); ++ } ++ ++ if (c > 15) { ++ dev_err(fdtv->device, "can't start dmx feed: busy\n"); ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ dvbdmxfeed->priv = (typeof(dvbdmxfeed->priv))(unsigned long)c; ++ fdtv->channel_pid[c] = dvbdmxfeed->pid; ++ collect_channels(fdtv, &pidc, pids); ++ ++ if (dvbdmxfeed->pid == 8192) { ++ ret = avc_tuner_get_ts(fdtv); ++ if (ret) { ++ dealloc_channel(fdtv, c); ++ dev_err(fdtv->device, "can't get TS\n"); ++ goto out; ++ } ++ } else { ++ ret = avc_tuner_set_pids(fdtv, pidc, pids); ++ if (ret) { ++ dealloc_channel(fdtv, c); ++ dev_err(fdtv->device, "can't set PIDs\n"); ++ goto out; ++ } ++ } ++out: ++ mutex_unlock(&fdtv->demux_mutex); ++ ++ return ret; ++} ++ ++int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *demux = dvbdmxfeed->demux; ++ struct firedtv *fdtv = demux->priv; ++ int pidc, c, ret; ++ u16 pids[16]; ++ ++ if (dvbdmxfeed->type == DMX_TYPE_TS && ++ !((dvbdmxfeed->ts_type & TS_PACKET) && ++ (demux->dmx.frontend->source != DMX_MEMORY_FE))) { ++ ++ if (dvbdmxfeed->ts_type & TS_DECODER) { ++ if (dvbdmxfeed->pes_type >= DMX_TS_PES_OTHER || ++ !demux->pesfilter[dvbdmxfeed->pes_type]) ++ return -EINVAL; ++ ++ demux->pids[dvbdmxfeed->pes_type] |= 0x8000; ++ demux->pesfilter[dvbdmxfeed->pes_type] = NULL; ++ } ++ ++ if (!(dvbdmxfeed->ts_type & TS_DECODER && ++ dvbdmxfeed->pes_type < DMX_TS_PES_OTHER)) ++ return 0; ++ } ++ ++ if (mutex_lock_interruptible(&fdtv->demux_mutex)) ++ return -EINTR; ++ ++ c = (unsigned long)dvbdmxfeed->priv; ++ dealloc_channel(fdtv, c); ++ collect_channels(fdtv, &pidc, pids); ++ ++ ret = avc_tuner_set_pids(fdtv, pidc, pids); ++ ++ mutex_unlock(&fdtv->demux_mutex); ++ ++ return ret; ++} ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++int fdtv_dvb_register(struct firedtv *fdtv, const char *name) ++{ ++ int err; ++ ++ err = dvb_register_adapter(&fdtv->adapter, name, ++ THIS_MODULE, fdtv->device, adapter_nr); ++ if (err < 0) ++ goto fail_log; ++ ++ /*DMX_TS_FILTERING | DMX_SECTION_FILTERING*/ ++ fdtv->demux.dmx.capabilities = 0; ++ ++ fdtv->demux.priv = fdtv; ++ fdtv->demux.filternum = 16; ++ fdtv->demux.feednum = 16; ++ fdtv->demux.start_feed = fdtv_start_feed; ++ fdtv->demux.stop_feed = fdtv_stop_feed; ++ fdtv->demux.write_to_decoder = NULL; ++ ++ err = dvb_dmx_init(&fdtv->demux); ++ if (err) ++ goto fail_unreg_adapter; ++ ++ fdtv->dmxdev.filternum = 16; ++ fdtv->dmxdev.demux = &fdtv->demux.dmx; ++ fdtv->dmxdev.capabilities = 0; ++ ++ err = dvb_dmxdev_init(&fdtv->dmxdev, &fdtv->adapter); ++ if (err) ++ goto fail_dmx_release; ++ ++ fdtv->frontend.source = DMX_FRONTEND_0; ++ ++ err = fdtv->demux.dmx.add_frontend(&fdtv->demux.dmx, &fdtv->frontend); ++ if (err) ++ goto fail_dmxdev_release; ++ ++ err = fdtv->demux.dmx.connect_frontend(&fdtv->demux.dmx, ++ &fdtv->frontend); ++ if (err) ++ goto fail_rem_frontend; ++ ++ err = dvb_net_init(&fdtv->adapter, &fdtv->dvbnet, &fdtv->demux.dmx); ++ if (err) ++ goto fail_disconnect_frontend; ++ ++ fdtv_frontend_init(fdtv, name); ++ err = dvb_register_frontend(&fdtv->adapter, &fdtv->fe); ++ if (err) ++ goto fail_net_release; ++ ++ err = fdtv_ca_register(fdtv); ++ if (err) ++ dev_info(fdtv->device, ++ "Conditional Access Module not enabled\n"); ++ return 0; ++ ++fail_net_release: ++ dvb_net_release(&fdtv->dvbnet); ++fail_disconnect_frontend: ++ fdtv->demux.dmx.close(&fdtv->demux.dmx); ++fail_rem_frontend: ++ fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); ++fail_dmxdev_release: ++ dvb_dmxdev_release(&fdtv->dmxdev); ++fail_dmx_release: ++ dvb_dmx_release(&fdtv->demux); ++fail_unreg_adapter: ++ dvb_unregister_adapter(&fdtv->adapter); ++fail_log: ++ dev_err(fdtv->device, "DVB initialization failed\n"); ++ return err; ++} ++ ++void fdtv_dvb_unregister(struct firedtv *fdtv) ++{ ++ fdtv_ca_release(fdtv); ++ dvb_unregister_frontend(&fdtv->fe); ++ dvb_net_release(&fdtv->dvbnet); ++ fdtv->demux.dmx.close(&fdtv->demux.dmx); ++ fdtv->demux.dmx.remove_frontend(&fdtv->demux.dmx, &fdtv->frontend); ++ dvb_dmxdev_release(&fdtv->dmxdev); ++ dvb_dmx_release(&fdtv->demux); ++ dvb_unregister_adapter(&fdtv->adapter); ++} +diff --git a/drivers/media/firewire/firedtv-fe.c b/drivers/media/firewire/firedtv-fe.c +new file mode 100644 +index 0000000..6fe9793 +--- /dev/null ++++ b/drivers/media/firewire/firedtv-fe.c +@@ -0,0 +1,254 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * Copyright (C) 2008 Henrik Kurelid ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "firedtv.h" ++ ++static int fdtv_dvb_init(struct dvb_frontend *fe) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ int err; ++ ++ /* FIXME - allocate free channel at IRM */ ++ fdtv->isochannel = fdtv->adapter.num; ++ ++ err = cmp_establish_pp_connection(fdtv, fdtv->subunit, ++ fdtv->isochannel); ++ if (err) { ++ dev_err(fdtv->device, ++ "could not establish point to point connection\n"); ++ return err; ++ } ++ ++ return fdtv_start_iso(fdtv); ++} ++ ++static int fdtv_sleep(struct dvb_frontend *fe) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ ++ fdtv_stop_iso(fdtv); ++ cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel); ++ fdtv->isochannel = -1; ++ return 0; ++} ++ ++#define LNBCONTROL_DONTCARE 0xff ++ ++static int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe, ++ struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ ++ return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE, ++ LNBCONTROL_DONTCARE, 1, cmd); ++} ++ ++static int fdtv_diseqc_send_burst(struct dvb_frontend *fe, ++ fe_sec_mini_cmd_t minicmd) ++{ ++ return 0; ++} ++ ++static int fdtv_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ ++ fdtv->tone = tone; ++ return 0; ++} ++ ++static int fdtv_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ ++ fdtv->voltage = voltage; ++ return 0; ++} ++ ++static int fdtv_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ struct firedtv_tuner_status stat; ++ ++ if (avc_tuner_status(fdtv, &stat)) ++ return -EINVAL; ++ ++ if (stat.no_rf) ++ *status = 0; ++ else ++ *status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC | ++ FE_HAS_CARRIER | FE_HAS_LOCK; ++ return 0; ++} ++ ++static int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ struct firedtv_tuner_status stat; ++ ++ if (avc_tuner_status(fdtv, &stat)) ++ return -EINVAL; ++ ++ *ber = stat.ber; ++ return 0; ++} ++ ++static int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ struct firedtv_tuner_status stat; ++ ++ if (avc_tuner_status(fdtv, &stat)) ++ return -EINVAL; ++ ++ *strength = stat.signal_strength << 8; ++ return 0; ++} ++ ++static int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct firedtv *fdtv = fe->sec_priv; ++ struct firedtv_tuner_status stat; ++ ++ if (avc_tuner_status(fdtv, &stat)) ++ return -EINVAL; ++ ++ /* C/N[dB] = -10 * log10(snr / 65535) */ ++ *snr = stat.carrier_noise_ratio * 257; ++ return 0; ++} ++ ++static int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks) ++{ ++ return -EOPNOTSUPP; ++} ++ ++static int fdtv_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct firedtv *fdtv = fe->sec_priv; ++ ++ return avc_tuner_dsd(fdtv, p); ++} ++ ++void fdtv_frontend_init(struct firedtv *fdtv, const char *name) ++{ ++ struct dvb_frontend_ops *ops = &fdtv->fe.ops; ++ struct dvb_frontend_info *fi = &ops->info; ++ ++ ops->init = fdtv_dvb_init; ++ ops->sleep = fdtv_sleep; ++ ++ ops->set_frontend = fdtv_set_frontend; ++ ++ ops->read_status = fdtv_read_status; ++ ops->read_ber = fdtv_read_ber; ++ ops->read_signal_strength = fdtv_read_signal_strength; ++ ops->read_snr = fdtv_read_snr; ++ ops->read_ucblocks = fdtv_read_uncorrected_blocks; ++ ++ ops->diseqc_send_master_cmd = fdtv_diseqc_send_master_cmd; ++ ops->diseqc_send_burst = fdtv_diseqc_send_burst; ++ ops->set_tone = fdtv_set_tone; ++ ops->set_voltage = fdtv_set_voltage; ++ ++ switch (fdtv->type) { ++ case FIREDTV_DVB_S: ++ ops->delsys[0] = SYS_DVBS; ++ ++ fi->frequency_min = 950000; ++ fi->frequency_max = 2150000; ++ fi->frequency_stepsize = 125; ++ fi->symbol_rate_min = 1000000; ++ fi->symbol_rate_max = 40000000; ++ ++ fi->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; ++ break; ++ ++ case FIREDTV_DVB_S2: ++ ops->delsys[0] = SYS_DVBS; ++ ops->delsys[1] = SYS_DVBS2; ++ ++ fi->frequency_min = 950000; ++ fi->frequency_max = 2150000; ++ fi->frequency_stepsize = 125; ++ fi->symbol_rate_min = 1000000; ++ fi->symbol_rate_max = 40000000; ++ ++ fi->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_2G_MODULATION; ++ break; ++ ++ case FIREDTV_DVB_C: ++ ops->delsys[0] = SYS_DVBC_ANNEX_A; ++ ++ fi->frequency_min = 47000000; ++ fi->frequency_max = 866000000; ++ fi->frequency_stepsize = 62500; ++ fi->symbol_rate_min = 870000; ++ fi->symbol_rate_max = 6900000; ++ ++ fi->caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | ++ FE_CAN_QAM_AUTO; ++ break; ++ ++ case FIREDTV_DVB_T: ++ ops->delsys[0] = SYS_DVBT; ++ ++ fi->frequency_min = 49000000; ++ fi->frequency_max = 861000000; ++ fi->frequency_stepsize = 62500; ++ ++ fi->caps = FE_CAN_INVERSION_AUTO | ++ FE_CAN_FEC_2_3 | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | ++ FE_CAN_HIERARCHY_AUTO; ++ break; ++ ++ default: ++ dev_err(fdtv->device, "no frontend for model type %d\n", ++ fdtv->type); ++ } ++ strcpy(fi->name, name); ++ ++ fdtv->fe.dvb = &fdtv->adapter; ++ fdtv->fe.sec_priv = fdtv; ++} +diff --git a/drivers/media/firewire/firedtv-fw.c b/drivers/media/firewire/firedtv-fw.c +new file mode 100644 +index 0000000..e24ec53 +--- /dev/null ++++ b/drivers/media/firewire/firedtv-fw.c +@@ -0,0 +1,429 @@ ++/* ++ * FireDTV driver -- firewire I/O backend ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include "firedtv.h" ++ ++static LIST_HEAD(node_list); ++static DEFINE_SPINLOCK(node_list_lock); ++ ++static inline struct fw_device *device_of(struct firedtv *fdtv) ++{ ++ return fw_device(fdtv->device->parent); ++} ++ ++static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, ++ int tcode) ++{ ++ struct fw_device *device = device_of(fdtv); ++ int rcode, generation = device->generation; ++ ++ smp_rmb(); /* node_id vs. generation */ ++ ++ rcode = fw_run_transaction(device->card, tcode, device->node_id, ++ generation, device->max_speed, addr, data, len); ++ ++ return rcode != RCODE_COMPLETE ? -EIO : 0; ++} ++ ++int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data) ++{ ++ return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); ++} ++ ++int fdtv_read(struct firedtv *fdtv, u64 addr, void *data) ++{ ++ return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); ++} ++ ++int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) ++{ ++ return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); ++} ++ ++#define ISO_HEADER_SIZE 4 ++#define CIP_HEADER_SIZE 8 ++#define MPEG2_TS_HEADER_SIZE 4 ++#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) ++ ++#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */ ++#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE) ++#define N_PACKETS 64 /* buffer size */ ++#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) ++#define IRQ_INTERVAL 16 ++ ++struct fdtv_ir_context { ++ struct fw_iso_context *context; ++ struct fw_iso_buffer buffer; ++ int interrupt_packet; ++ int current_packet; ++ char *pages[N_PAGES]; ++}; ++ ++static int queue_iso(struct fdtv_ir_context *ctx, int index) ++{ ++ struct fw_iso_packet p; ++ ++ p.payload_length = MAX_PACKET_SIZE; ++ p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1)); ++ p.skip = 0; ++ p.header_length = ISO_HEADER_SIZE; ++ ++ return fw_iso_context_queue(ctx->context, &p, &ctx->buffer, ++ index * MAX_PACKET_SIZE); ++} ++ ++static void handle_iso(struct fw_iso_context *context, u32 cycle, ++ size_t header_length, void *header, void *data) ++{ ++ struct firedtv *fdtv = data; ++ struct fdtv_ir_context *ctx = fdtv->ir_context; ++ __be32 *h, *h_end; ++ int length, err, i = ctx->current_packet; ++ char *p, *p_end; ++ ++ for (h = header, h_end = h + header_length / 4; h < h_end; h++) { ++ length = be32_to_cpup(h) >> 16; ++ if (unlikely(length > MAX_PACKET_SIZE)) { ++ dev_err(fdtv->device, "length = %d\n", length); ++ length = MAX_PACKET_SIZE; ++ } ++ ++ p = ctx->pages[i / PACKETS_PER_PAGE] ++ + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE; ++ p_end = p + length; ++ ++ for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end; ++ p += MPEG2_TS_SOURCE_PACKET_SIZE) ++ dvb_dmx_swfilter_packets(&fdtv->demux, p, 1); ++ ++ err = queue_iso(ctx, i); ++ if (unlikely(err)) ++ dev_err(fdtv->device, "requeue failed\n"); ++ ++ i = (i + 1) & (N_PACKETS - 1); ++ } ++ fw_iso_context_queue_flush(ctx->context); ++ ctx->current_packet = i; ++} ++ ++int fdtv_start_iso(struct firedtv *fdtv) ++{ ++ struct fdtv_ir_context *ctx; ++ struct fw_device *device = device_of(fdtv); ++ int i, err; ++ ++ ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ ctx->context = fw_iso_context_create(device->card, ++ FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel, ++ device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv); ++ if (IS_ERR(ctx->context)) { ++ err = PTR_ERR(ctx->context); ++ goto fail_free; ++ } ++ ++ err = fw_iso_buffer_init(&ctx->buffer, device->card, ++ N_PAGES, DMA_FROM_DEVICE); ++ if (err) ++ goto fail_context_destroy; ++ ++ ctx->interrupt_packet = 0; ++ ctx->current_packet = 0; ++ ++ for (i = 0; i < N_PAGES; i++) ++ ctx->pages[i] = page_address(ctx->buffer.pages[i]); ++ ++ for (i = 0; i < N_PACKETS; i++) { ++ err = queue_iso(ctx, i); ++ if (err) ++ goto fail; ++ } ++ ++ err = fw_iso_context_start(ctx->context, -1, 0, ++ FW_ISO_CONTEXT_MATCH_ALL_TAGS); ++ if (err) ++ goto fail; ++ ++ fdtv->ir_context = ctx; ++ ++ return 0; ++fail: ++ fw_iso_buffer_destroy(&ctx->buffer, device->card); ++fail_context_destroy: ++ fw_iso_context_destroy(ctx->context); ++fail_free: ++ kfree(ctx); ++ ++ return err; ++} ++ ++void fdtv_stop_iso(struct firedtv *fdtv) ++{ ++ struct fdtv_ir_context *ctx = fdtv->ir_context; ++ ++ fw_iso_context_stop(ctx->context); ++ fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); ++ fw_iso_context_destroy(ctx->context); ++ kfree(ctx); ++} ++ ++static void handle_fcp(struct fw_card *card, struct fw_request *request, ++ int tcode, int destination, int source, int generation, ++ unsigned long long offset, void *payload, size_t length, ++ void *callback_data) ++{ ++ struct firedtv *f, *fdtv = NULL; ++ struct fw_device *device; ++ unsigned long flags; ++ int su; ++ ++ if (length < 2 || (((u8 *)payload)[0] & 0xf0) != 0) ++ return; ++ ++ su = ((u8 *)payload)[1] & 0x7; ++ ++ spin_lock_irqsave(&node_list_lock, flags); ++ list_for_each_entry(f, &node_list, list) { ++ device = device_of(f); ++ if (device->generation != generation) ++ continue; ++ ++ smp_rmb(); /* node_id vs. generation */ ++ ++ if (device->card == card && ++ device->node_id == source && ++ (f->subunit == su || (f->subunit == 0 && su == 0x7))) { ++ fdtv = f; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&node_list_lock, flags); ++ ++ if (fdtv) ++ avc_recv(fdtv, payload, length); ++} ++ ++static struct fw_address_handler fcp_handler = { ++ .length = CSR_FCP_END - CSR_FCP_RESPONSE, ++ .address_callback = handle_fcp, ++}; ++ ++static const struct fw_address_region fcp_region = { ++ .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, ++ .end = CSR_REGISTER_BASE + CSR_FCP_END, ++}; ++ ++static const char * const model_names[] = { ++ [FIREDTV_UNKNOWN] = "unknown type", ++ [FIREDTV_DVB_S] = "FireDTV S/CI", ++ [FIREDTV_DVB_C] = "FireDTV C/CI", ++ [FIREDTV_DVB_T] = "FireDTV T/CI", ++ [FIREDTV_DVB_S2] = "FireDTV S2 ", ++}; ++ ++/* Adjust the template string if models with longer names appear. */ ++#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????") ++ ++static int node_probe(struct device *dev) ++{ ++ struct firedtv *fdtv; ++ char name[MAX_MODEL_NAME_LEN]; ++ int name_len, i, err; ++ ++ fdtv = kzalloc(sizeof(*fdtv), GFP_KERNEL); ++ if (!fdtv) ++ return -ENOMEM; ++ ++ dev_set_drvdata(dev, fdtv); ++ fdtv->device = dev; ++ fdtv->isochannel = -1; ++ fdtv->voltage = 0xff; ++ fdtv->tone = 0xff; ++ ++ mutex_init(&fdtv->avc_mutex); ++ init_waitqueue_head(&fdtv->avc_wait); ++ mutex_init(&fdtv->demux_mutex); ++ INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); ++ ++ name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL, ++ name, sizeof(name)); ++ for (i = ARRAY_SIZE(model_names); --i; ) ++ if (strlen(model_names[i]) <= name_len && ++ strncmp(name, model_names[i], name_len) == 0) ++ break; ++ fdtv->type = i; ++ ++ err = fdtv_register_rc(fdtv, dev); ++ if (err) ++ goto fail_free; ++ ++ spin_lock_irq(&node_list_lock); ++ list_add_tail(&fdtv->list, &node_list); ++ spin_unlock_irq(&node_list_lock); ++ ++ err = avc_identify_subunit(fdtv); ++ if (err) ++ goto fail; ++ ++ err = fdtv_dvb_register(fdtv, model_names[fdtv->type]); ++ if (err) ++ goto fail; ++ ++ avc_register_remote_control(fdtv); ++ ++ return 0; ++fail: ++ spin_lock_irq(&node_list_lock); ++ list_del(&fdtv->list); ++ spin_unlock_irq(&node_list_lock); ++ fdtv_unregister_rc(fdtv); ++fail_free: ++ kfree(fdtv); ++ ++ return err; ++} ++ ++static int node_remove(struct device *dev) ++{ ++ struct firedtv *fdtv = dev_get_drvdata(dev); ++ ++ fdtv_dvb_unregister(fdtv); ++ ++ spin_lock_irq(&node_list_lock); ++ list_del(&fdtv->list); ++ spin_unlock_irq(&node_list_lock); ++ ++ fdtv_unregister_rc(fdtv); ++ ++ kfree(fdtv); ++ return 0; ++} ++ ++static void node_update(struct fw_unit *unit) ++{ ++ struct firedtv *fdtv = dev_get_drvdata(&unit->device); ++ ++ if (fdtv->isochannel >= 0) ++ cmp_establish_pp_connection(fdtv, fdtv->subunit, ++ fdtv->isochannel); ++} ++ ++#define MATCH_FLAGS (IEEE1394_MATCH_VENDOR_ID | IEEE1394_MATCH_MODEL_ID | \ ++ IEEE1394_MATCH_SPECIFIER_ID | IEEE1394_MATCH_VERSION) ++ ++#define DIGITAL_EVERYWHERE_OUI 0x001287 ++#define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d ++#define AVC_SW_VERSION_ENTRY 0x010001 ++ ++static const struct ieee1394_device_id fdtv_id_table[] = { ++ { ++ /* FloppyDTV S/CI and FloppyDTV S2 */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000024, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, { ++ /* FloppyDTV T/CI */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000025, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, { ++ /* FloppyDTV C/CI */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000026, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, { ++ /* FireDTV S/CI and FloppyDTV S2 */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000034, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, { ++ /* FireDTV T/CI */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000035, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, { ++ /* FireDTV C/CI */ ++ .match_flags = MATCH_FLAGS, ++ .vendor_id = DIGITAL_EVERYWHERE_OUI, ++ .model_id = 0x000036, ++ .specifier_id = AVC_UNIT_SPEC_ID_ENTRY, ++ .version = AVC_SW_VERSION_ENTRY, ++ }, {} ++}; ++MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); ++ ++static struct fw_driver fdtv_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "firedtv", ++ .bus = &fw_bus_type, ++ .probe = node_probe, ++ .remove = node_remove, ++ }, ++ .update = node_update, ++ .id_table = fdtv_id_table, ++}; ++ ++static int __init fdtv_init(void) ++{ ++ int ret; ++ ++ ret = fw_core_add_address_handler(&fcp_handler, &fcp_region); ++ if (ret < 0) ++ return ret; ++ ++ ret = driver_register(&fdtv_driver.driver); ++ if (ret < 0) ++ fw_core_remove_address_handler(&fcp_handler); ++ ++ return ret; ++} ++ ++static void __exit fdtv_exit(void) ++{ ++ driver_unregister(&fdtv_driver.driver); ++ fw_core_remove_address_handler(&fcp_handler); ++} ++ ++module_init(fdtv_init); ++module_exit(fdtv_exit); ++ ++MODULE_AUTHOR("Andreas Monitzer "); ++MODULE_AUTHOR("Ben Backx "); ++MODULE_DESCRIPTION("FireDTV DVB Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("FireDTV DVB"); +diff --git a/drivers/media/firewire/firedtv-rc.c b/drivers/media/firewire/firedtv-rc.c +new file mode 100644 +index 0000000..f82d4a9 +--- /dev/null ++++ b/drivers/media/firewire/firedtv-rc.c +@@ -0,0 +1,196 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "firedtv.h" ++ ++/* fixed table with older keycodes, geared towards MythTV */ ++static const u16 oldtable[] = { ++ ++ /* code from device: 0x4501...0x451f */ ++ ++ KEY_ESC, ++ KEY_F9, ++ KEY_1, ++ KEY_2, ++ KEY_3, ++ KEY_4, ++ KEY_5, ++ KEY_6, ++ KEY_7, ++ KEY_8, ++ KEY_9, ++ KEY_I, ++ KEY_0, ++ KEY_ENTER, ++ KEY_RED, ++ KEY_UP, ++ KEY_GREEN, ++ KEY_F10, ++ KEY_SPACE, ++ KEY_F11, ++ KEY_YELLOW, ++ KEY_DOWN, ++ KEY_BLUE, ++ KEY_Z, ++ KEY_P, ++ KEY_PAGEDOWN, ++ KEY_LEFT, ++ KEY_W, ++ KEY_RIGHT, ++ KEY_P, ++ KEY_M, ++ ++ /* code from device: 0x4540...0x4542 */ ++ ++ KEY_R, ++ KEY_V, ++ KEY_C, ++}; ++ ++/* user-modifiable table for a remote as sold in 2008 */ ++static const u16 keytable[] = { ++ ++ /* code from device: 0x0300...0x031f */ ++ ++ [0x00] = KEY_POWER, ++ [0x01] = KEY_SLEEP, ++ [0x02] = KEY_STOP, ++ [0x03] = KEY_OK, ++ [0x04] = KEY_RIGHT, ++ [0x05] = KEY_1, ++ [0x06] = KEY_2, ++ [0x07] = KEY_3, ++ [0x08] = KEY_LEFT, ++ [0x09] = KEY_4, ++ [0x0a] = KEY_5, ++ [0x0b] = KEY_6, ++ [0x0c] = KEY_UP, ++ [0x0d] = KEY_7, ++ [0x0e] = KEY_8, ++ [0x0f] = KEY_9, ++ [0x10] = KEY_DOWN, ++ [0x11] = KEY_TITLE, /* "OSD" - fixme */ ++ [0x12] = KEY_0, ++ [0x13] = KEY_F20, /* "16:9" - fixme */ ++ [0x14] = KEY_SCREEN, /* "FULL" - fixme */ ++ [0x15] = KEY_MUTE, ++ [0x16] = KEY_SUBTITLE, ++ [0x17] = KEY_RECORD, ++ [0x18] = KEY_TEXT, ++ [0x19] = KEY_AUDIO, ++ [0x1a] = KEY_RED, ++ [0x1b] = KEY_PREVIOUS, ++ [0x1c] = KEY_REWIND, ++ [0x1d] = KEY_PLAYPAUSE, ++ [0x1e] = KEY_NEXT, ++ [0x1f] = KEY_VOLUMEUP, ++ ++ /* code from device: 0x0340...0x0354 */ ++ ++ [0x20] = KEY_CHANNELUP, ++ [0x21] = KEY_F21, /* "4:3" - fixme */ ++ [0x22] = KEY_TV, ++ [0x23] = KEY_DVD, ++ [0x24] = KEY_VCR, ++ [0x25] = KEY_AUX, ++ [0x26] = KEY_GREEN, ++ [0x27] = KEY_YELLOW, ++ [0x28] = KEY_BLUE, ++ [0x29] = KEY_CHANNEL, /* "CH.LIST" */ ++ [0x2a] = KEY_VENDOR, /* "CI" - fixme */ ++ [0x2b] = KEY_VOLUMEDOWN, ++ [0x2c] = KEY_CHANNELDOWN, ++ [0x2d] = KEY_LAST, ++ [0x2e] = KEY_INFO, ++ [0x2f] = KEY_FORWARD, ++ [0x30] = KEY_LIST, ++ [0x31] = KEY_FAVORITES, ++ [0x32] = KEY_MENU, ++ [0x33] = KEY_EPG, ++ [0x34] = KEY_EXIT, ++}; ++ ++int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) ++{ ++ struct input_dev *idev; ++ int i, err; ++ ++ idev = input_allocate_device(); ++ if (!idev) ++ return -ENOMEM; ++ ++ fdtv->remote_ctrl_dev = idev; ++ idev->name = "FireDTV remote control"; ++ idev->dev.parent = dev; ++ idev->evbit[0] = BIT_MASK(EV_KEY); ++ idev->keycode = kmemdup(keytable, sizeof(keytable), GFP_KERNEL); ++ if (!idev->keycode) { ++ err = -ENOMEM; ++ goto fail; ++ } ++ idev->keycodesize = sizeof(keytable[0]); ++ idev->keycodemax = ARRAY_SIZE(keytable); ++ ++ for (i = 0; i < ARRAY_SIZE(keytable); i++) ++ set_bit(keytable[i], idev->keybit); ++ ++ err = input_register_device(idev); ++ if (err) ++ goto fail_free_keymap; ++ ++ return 0; ++ ++fail_free_keymap: ++ kfree(idev->keycode); ++fail: ++ input_free_device(idev); ++ return err; ++} ++ ++void fdtv_unregister_rc(struct firedtv *fdtv) ++{ ++ cancel_work_sync(&fdtv->remote_ctrl_work); ++ kfree(fdtv->remote_ctrl_dev->keycode); ++ input_unregister_device(fdtv->remote_ctrl_dev); ++} ++ ++void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) ++{ ++ struct input_dev *idev = fdtv->remote_ctrl_dev; ++ u16 *keycode = idev->keycode; ++ ++ if (code >= 0x0300 && code <= 0x031f) ++ code = keycode[code - 0x0300]; ++ else if (code >= 0x0340 && code <= 0x0354) ++ code = keycode[code - 0x0320]; ++ else if (code >= 0x4501 && code <= 0x451f) ++ code = oldtable[code - 0x4501]; ++ else if (code >= 0x4540 && code <= 0x4542) ++ code = oldtable[code - 0x4521]; ++ else { ++ printk(KERN_DEBUG "firedtv: invalid key code 0x%04x " ++ "from remote control\n", code); ++ return; ++ } ++ ++ input_report_key(idev, code, 1); ++ input_sync(idev); ++ input_report_key(idev, code, 0); ++ input_sync(idev); ++} +diff --git a/drivers/media/firewire/firedtv.h b/drivers/media/firewire/firedtv.h +new file mode 100644 +index 0000000..c2ba085 +--- /dev/null ++++ b/drivers/media/firewire/firedtv.h +@@ -0,0 +1,169 @@ ++/* ++ * FireDTV driver (formerly known as FireSAT) ++ * ++ * Copyright (C) 2004 Andreas Monitzer ++ * Copyright (C) 2008 Henrik Kurelid ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of ++ * the License, or (at your option) any later version. ++ */ ++ ++#ifndef _FIREDTV_H ++#define _FIREDTV_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct firedtv_tuner_status { ++ unsigned active_system:8; ++ unsigned searching:1; ++ unsigned moving:1; ++ unsigned no_rf:1; ++ unsigned input:1; ++ unsigned selected_antenna:7; ++ unsigned ber:32; ++ unsigned signal_strength:8; ++ unsigned raster_frequency:2; ++ unsigned rf_frequency:22; ++ unsigned man_dep_info_length:8; ++ unsigned front_end_error:1; ++ unsigned antenna_error:1; ++ unsigned front_end_power_status:1; ++ unsigned power_supply:1; ++ unsigned carrier_noise_ratio:16; ++ unsigned power_supply_voltage:8; ++ unsigned antenna_voltage:8; ++ unsigned firewire_bus_voltage:8; ++ unsigned ca_mmi:1; ++ unsigned ca_pmt_reply:1; ++ unsigned ca_date_time_request:1; ++ unsigned ca_application_info:1; ++ unsigned ca_module_present_status:1; ++ unsigned ca_dvb_flag:1; ++ unsigned ca_error_flag:1; ++ unsigned ca_initialization_status:1; ++}; ++ ++enum model_type { ++ FIREDTV_UNKNOWN = 0, ++ FIREDTV_DVB_S = 1, ++ FIREDTV_DVB_C = 2, ++ FIREDTV_DVB_T = 3, ++ FIREDTV_DVB_S2 = 4, ++}; ++ ++struct device; ++struct input_dev; ++struct fdtv_ir_context; ++ ++struct firedtv { ++ struct device *device; ++ struct list_head list; ++ ++ struct dvb_adapter adapter; ++ struct dmxdev dmxdev; ++ struct dvb_demux demux; ++ struct dmx_frontend frontend; ++ struct dvb_net dvbnet; ++ struct dvb_frontend fe; ++ ++ struct dvb_device *cadev; ++ int ca_last_command; ++ int ca_time_interval; ++ ++ struct mutex avc_mutex; ++ wait_queue_head_t avc_wait; ++ bool avc_reply_received; ++ struct work_struct remote_ctrl_work; ++ struct input_dev *remote_ctrl_dev; ++ ++ enum model_type type; ++ char subunit; ++ char isochannel; ++ struct fdtv_ir_context *ir_context; ++ ++ fe_sec_voltage_t voltage; ++ fe_sec_tone_mode_t tone; ++ ++ struct mutex demux_mutex; ++ unsigned long channel_active; ++ u16 channel_pid[16]; ++ ++ int avc_data_length; ++ u8 avc_data[512]; ++}; ++ ++/* firedtv-avc.c */ ++int avc_recv(struct firedtv *fdtv, void *data, size_t length); ++int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat); ++struct dtv_frontend_properties; ++int avc_tuner_dsd(struct firedtv *fdtv, struct dtv_frontend_properties *params); ++int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]); ++int avc_tuner_get_ts(struct firedtv *fdtv); ++int avc_identify_subunit(struct firedtv *fdtv); ++struct dvb_diseqc_master_cmd; ++int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, ++ char conttone, char nrdiseq, ++ struct dvb_diseqc_master_cmd *diseqcmd); ++void avc_remote_ctrl_work(struct work_struct *work); ++int avc_register_remote_control(struct firedtv *fdtv); ++int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len); ++int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len); ++int avc_ca_reset(struct firedtv *fdtv); ++int avc_ca_pmt(struct firedtv *fdtv, char *app_info, int length); ++int avc_ca_get_time_date(struct firedtv *fdtv, int *interval); ++int avc_ca_enter_menu(struct firedtv *fdtv); ++int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len); ++int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel); ++void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel); ++ ++/* firedtv-ci.c */ ++int fdtv_ca_register(struct firedtv *fdtv); ++void fdtv_ca_release(struct firedtv *fdtv); ++ ++/* firedtv-dvb.c */ ++int fdtv_start_feed(struct dvb_demux_feed *dvbdmxfeed); ++int fdtv_stop_feed(struct dvb_demux_feed *dvbdmxfeed); ++int fdtv_dvb_register(struct firedtv *fdtv, const char *name); ++void fdtv_dvb_unregister(struct firedtv *fdtv); ++ ++/* firedtv-fe.c */ ++void fdtv_frontend_init(struct firedtv *fdtv, const char *name); ++ ++/* firedtv-fw.c */ ++int fdtv_lock(struct firedtv *fdtv, u64 addr, void *data); ++int fdtv_read(struct firedtv *fdtv, u64 addr, void *data); ++int fdtv_write(struct firedtv *fdtv, u64 addr, void *data, size_t len); ++int fdtv_start_iso(struct firedtv *fdtv); ++void fdtv_stop_iso(struct firedtv *fdtv); ++ ++/* firedtv-rc.c */ ++#ifdef CONFIG_DVB_FIREDTV_INPUT ++int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); ++void fdtv_unregister_rc(struct firedtv *fdtv); ++void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code); ++#else ++static inline int fdtv_register_rc(struct firedtv *fdtv, ++ struct device *dev) { return 0; } ++static inline void fdtv_unregister_rc(struct firedtv *fdtv) {} ++static inline void fdtv_handle_rc(struct firedtv *fdtv, unsigned int code) {} ++#endif ++ ++#endif /* _FIREDTV_H */ +diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig +new file mode 100644 +index 0000000..1e4b2d0 +--- /dev/null ++++ b/drivers/media/i2c/Kconfig +@@ -0,0 +1,591 @@ ++# ++# Generic video config states ++# ++ ++config VIDEO_BTCX ++ depends on PCI ++ tristate ++ ++config VIDEO_TVEEPROM ++ tristate ++ depends on I2C ++ ++# ++# Multimedia Video device configuration ++# ++ ++if VIDEO_V4L2 ++ ++config VIDEO_IR_I2C ++ tristate "I2C module for IR" if !MEDIA_SUBDRV_AUTOSELECT ++ depends on I2C && RC_CORE ++ default y ++ ---help--- ++ Most boards have an IR chip directly connected via GPIO. However, ++ some video boards have the IR connected via I2C bus. ++ ++ If your board doesn't have an I2C IR chip, you may disable this ++ option. ++ ++ In doubt, say Y. ++ ++# ++# Encoder / Decoder module configuration ++# ++ ++menu "Encoders, decoders, sensors and other helper chips" ++ visible if !MEDIA_SUBDRV_AUTOSELECT ++ ++comment "Audio decoders, processors and mixers" ++ ++config VIDEO_TVAUDIO ++ tristate "Simple audio decoder chips" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for several audio decoder chips found on some bt8xx boards: ++ Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300, ++ tea6320, tea6420, tda8425, ta8874z. ++ Microchip: pic16c54 based design on ProVideo PV951 board. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvaudio. ++ ++config VIDEO_TDA7432 ++ tristate "Philips TDA7432 audio processor" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for tda7432 audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tda7432. ++ ++config VIDEO_TDA9840 ++ tristate "Philips TDA9840 audio processor" ++ depends on I2C ++ ---help--- ++ Support for tda9840 audio decoder chip found on some Zoran boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tda9840. ++ ++config VIDEO_TEA6415C ++ tristate "Philips TEA6415C audio processor" ++ depends on I2C ++ ---help--- ++ Support for tea6415c audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tea6415c. ++ ++config VIDEO_TEA6420 ++ tristate "Philips TEA6420 audio processor" ++ depends on I2C ++ ---help--- ++ Support for tea6420 audio decoder chip found on some bt8xx boards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tea6420. ++ ++config VIDEO_MSP3400 ++ tristate "Micronas MSP34xx audio decoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Micronas MSP34xx series of audio decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called msp3400. ++ ++config VIDEO_CS5345 ++ tristate "Cirrus Logic CS5345 audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Cirrus Logic CS5345 24-bit, 192 kHz ++ stereo A/D converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cs5345. ++ ++config VIDEO_CS53L32A ++ tristate "Cirrus Logic CS53L32A audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Cirrus Logic CS53L32A low voltage ++ stereo A/D converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cs53l32a. ++ ++config VIDEO_TLV320AIC23B ++ tristate "Texas Instruments TLV320AIC23B audio codec" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Texas Instruments TLV320AIC23B audio codec. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tlv320aic23b. ++ ++config VIDEO_WM8775 ++ tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Wolfson Microelectronics WM8775 high ++ performance stereo A/D Converter with a 4 channel input mixer. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm8775. ++ ++config VIDEO_WM8739 ++ tristate "Wolfson Microelectronics WM8739 stereo audio ADC" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Wolfson Microelectronics WM8739 ++ stereo A/D Converter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm8739. ++ ++config VIDEO_VP27SMPX ++ tristate "Panasonic VP27s internal MPX" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the internal MPX of the Panasonic VP27s tuner. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vp27smpx. ++ ++comment "RDS decoders" ++ ++config VIDEO_SAA6588 ++ tristate "SAA6588 Radio Chip RDS decoder support" ++ depends on VIDEO_V4L2 && I2C ++ ++ help ++ Support for this Radio Data System (RDS) decoder. This allows ++ seeing radio station identification transmitted using this ++ standard. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa6588. ++ ++comment "Video decoders" ++ ++config VIDEO_ADV7180 ++ tristate "Analog Devices ADV7180 decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Analog Devices ADV7180 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7180. ++ ++config VIDEO_ADV7183 ++ tristate "Analog Devices ADV7183 decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ V4l2 subdevice driver for the Analog Devices ++ ADV7183 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7183. ++ ++config VIDEO_ADV7604 ++ tristate "Analog Devices ADV7604 decoder" ++ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ++ ---help--- ++ Support for the Analog Devices ADV7604 video decoder. ++ ++ This is a Analog Devices Component/Graphics Digitizer ++ with 4:1 Multiplexed HDMI Receiver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7604. ++ ++config VIDEO_BT819 ++ tristate "BT819A VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for BT819A video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt819. ++ ++config VIDEO_BT856 ++ tristate "BT856 VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for BT856 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt856. ++ ++config VIDEO_BT866 ++ tristate "BT866 VideoStream decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for BT866 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bt866. ++ ++config VIDEO_KS0127 ++ tristate "KS0127 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for KS0127 video decoder. ++ ++ This chip is used on AverMedia AVS6EYES Zoran-based MJPEG ++ cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ks0127. ++ ++config VIDEO_SAA7110 ++ tristate "Philips SAA7110 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7110 video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7110. ++ ++config VIDEO_SAA711X ++ tristate "Philips SAA7111/3/4/5 video decoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7111/3/4/5 video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7115. ++ ++config VIDEO_SAA7191 ++ tristate "Philips SAA7191 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7191 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7191. ++ ++config VIDEO_TVP514X ++ tristate "Texas Instruments TVP514x video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47 ++ decoder. It is currently working with the TI OMAP3 camera ++ controller. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp514x. ++ ++config VIDEO_TVP5150 ++ tristate "Texas Instruments TVP5150 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Texas Instruments TVP5150 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp5150. ++ ++config VIDEO_TVP7002 ++ tristate "Texas Instruments TVP7002 video decoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Texas Instruments TVP7002 video decoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tvp7002. ++ ++config VIDEO_VPX3220 ++ tristate "vpx3220a, vpx3216b & vpx3214c video decoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for VPX322x video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpx3220. ++ ++comment "Video and audio decoders" ++ ++config VIDEO_SAA717X ++ tristate "Philips SAA7171/3/4 audio/video decoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7171/3/4 audio/video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa717x. ++ ++source "drivers/media/i2c/cx25840/Kconfig" ++ ++comment "MPEG video encoders" ++ ++config VIDEO_CX2341X ++ tristate "Conexant CX2341x MPEG encoders" ++ depends on VIDEO_V4L2 ++ ---help--- ++ Support for the Conexant CX23416 MPEG encoders ++ and CX23415 MPEG encoder/decoders. ++ ++ This module currently supports the encoding functions only. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx2341x. ++ ++comment "Video encoders" ++ ++config VIDEO_SAA7127 ++ tristate "Philips SAA7127/9 digital video encoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7127/9 digital video encoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7127. ++ ++config VIDEO_SAA7185 ++ tristate "Philips SAA7185 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Philips SAA7185 video encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7185. ++ ++config VIDEO_ADV7170 ++ tristate "Analog Devices ADV7170 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Analog Devices ADV7170 video encoder driver ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7170. ++ ++config VIDEO_ADV7175 ++ tristate "Analog Devices ADV7175 video encoder" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Analog Devices ADV7175 video encoder driver ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7175. ++ ++config VIDEO_ADV7343 ++ tristate "ADV7343 video encoder" ++ depends on I2C ++ help ++ Support for Analog Devices I2C bus based ADV7343 encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7343. ++ ++config VIDEO_ADV7393 ++ tristate "ADV7393 video encoder" ++ depends on I2C ++ help ++ Support for Analog Devices I2C bus based ADV7393 encoder. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called adv7393. ++ ++config VIDEO_AD9389B ++ tristate "Analog Devices AD9389B encoder" ++ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ++ ---help--- ++ Support for the Analog Devices AD9389B video encoder. ++ ++ This is a Analog Devices HDMI transmitter. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ad9389b. ++ ++config VIDEO_AK881X ++ tristate "AK8813/AK8814 video encoders" ++ depends on I2C ++ help ++ Video output driver for AKM AK8813 and AK8814 TV encoders ++ ++comment "Camera sensor devices" ++ ++config VIDEO_APTINA_PLL ++ tristate ++ ++config VIDEO_SMIAPP_PLL ++ tristate ++ ++config VIDEO_OV7670 ++ tristate "OmniVision OV7670 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the OmniVision ++ OV7670 VGA camera. It currently only works with the M88ALP01 ++ controller. ++ ++config VIDEO_VS6624 ++ tristate "ST VS6624 sensor support" ++ depends on VIDEO_V4L2 && I2C ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the ST VS6624 ++ camera. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vs6624. ++ ++config VIDEO_MT9M032 ++ tristate "MT9M032 camera sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ select VIDEO_APTINA_PLL ++ ---help--- ++ This driver supports MT9M032 camera sensors from Aptina, monochrome ++ models only. ++ ++config VIDEO_MT9P031 ++ tristate "Aptina MT9P031 support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ select VIDEO_APTINA_PLL ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Aptina ++ (Micron) mt9p031 5 Mpixel camera. ++ ++config VIDEO_MT9T001 ++ tristate "Aptina MT9T001 support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Aptina ++ (Micron) mt0t001 3 Mpixel camera. ++ ++config VIDEO_MT9V011 ++ tristate "Micron mt9v011 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Micron ++ mt0v011 1.3 Mpixel camera. It currently only works with the ++ em28xx driver. ++ ++config VIDEO_MT9V032 ++ tristate "Micron MT9V032 sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a Video4Linux2 sensor-level driver for the Micron ++ MT9V032 752x480 CMOS sensor. ++ ++config VIDEO_TCM825X ++ tristate "TCM825x camera sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_INT_DEVICE ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a driver for the Toshiba TCM825x VGA camera sensor. ++ It is used for example in Nokia N800. ++ ++config VIDEO_SR030PC30 ++ tristate "Siliconfile SR030PC30 sensor support" ++ depends on I2C && VIDEO_V4L2 ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This driver supports SR030PC30 VGA camera from Siliconfile ++ ++config VIDEO_NOON010PC30 ++ tristate "Siliconfile NOON010PC30 sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This driver supports NOON010PC30 CIF camera from Siliconfile ++ ++source "drivers/media/i2c/m5mols/Kconfig" ++ ++config VIDEO_S5K6AA ++ tristate "Samsung S5K6AAFX sensor support" ++ depends on MEDIA_CAMERA_SUPPORT ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ ---help--- ++ This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M ++ camera sensor with an embedded SoC image signal processor. ++ ++config VIDEO_S5K4ECGX ++ tristate "Samsung S5K4ECGX sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ ---help--- ++ This is a V4L2 sensor-level driver for Samsung S5K4ECGX 5M ++ camera sensor with an embedded SoC image signal processor. ++ ++source "drivers/media/i2c/smiapp/Kconfig" ++ ++comment "Flash devices" ++ ++config VIDEO_ADP1653 ++ tristate "ADP1653 flash support" ++ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a driver for the ADP1653 flash controller. It is used for ++ example in Nokia N900. ++ ++config VIDEO_AS3645A ++ tristate "AS3645A flash driver support" ++ depends on I2C && VIDEO_V4L2 && MEDIA_CONTROLLER ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This is a driver for the AS3645A and LM3555 flash controllers. It has ++ build in control for flash, torch and indicator LEDs. ++ ++comment "Video improvement chips" ++ ++config VIDEO_UPD64031A ++ tristate "NEC Electronics uPD64031A Ghost Reduction" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the NEC Electronics uPD64031A Ghost Reduction ++ video chip. It is most often found in NTSC TV cards made for ++ Japan and is used to reduce the 'ghosting' effect that can ++ be present in analog TV broadcasts. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called upd64031a. ++ ++config VIDEO_UPD64083 ++ tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the NEC Electronics uPD64083 3-Dimensional Y/C ++ separation video chip. It is used to improve the quality of ++ the colors of a composite signal. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called upd64083. ++ ++comment "Miscelaneous helper chips" ++ ++config VIDEO_THS7303 ++ tristate "THS7303 Video Amplifier" ++ depends on I2C ++ help ++ Support for TI THS7303 video amplifier ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ths7303. ++ ++config VIDEO_M52790 ++ tristate "Mitsubishi M52790 A/V switch" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Mitsubishi M52790 A/V switch. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called m52790. ++endmenu ++ ++menu "Sensors used on soc_camera driver" ++ ++if SOC_CAMERA ++ source "drivers/media/i2c/soc_camera/Kconfig" ++endif ++ ++endmenu ++ ++endif +diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile +new file mode 100644 +index 0000000..b1d62df +--- /dev/null ++++ b/drivers/media/i2c/Makefile +@@ -0,0 +1,67 @@ ++msp3400-objs := msp3400-driver.o msp3400-kthreads.o ++obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o ++ ++obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ ++obj-$(CONFIG_VIDEO_CX25840) += cx25840/ ++obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ ++obj-y += soc_camera/ ++ ++obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o ++obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o ++obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o ++obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o ++obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o ++obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o ++obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o ++obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o ++obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o ++obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o ++obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o ++obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o ++obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o ++obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o ++obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o ++obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o ++obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o ++obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o ++obj-$(CONFIG_VIDEO_ADV7393) += adv7393.o ++obj-$(CONFIG_VIDEO_ADV7604) += adv7604.o ++obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o ++obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o ++obj-$(CONFIG_VIDEO_VS6624) += vs6624.o ++obj-$(CONFIG_VIDEO_BT819) += bt819.o ++obj-$(CONFIG_VIDEO_BT856) += bt856.o ++obj-$(CONFIG_VIDEO_BT866) += bt866.o ++obj-$(CONFIG_VIDEO_KS0127) += ks0127.o ++obj-$(CONFIG_VIDEO_THS7303) += ths7303.o ++obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o ++obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o ++obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o ++obj-$(CONFIG_VIDEO_CS5345) += cs5345.o ++obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o ++obj-$(CONFIG_VIDEO_M52790) += m52790.o ++obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o ++obj-$(CONFIG_VIDEO_WM8775) += wm8775.o ++obj-$(CONFIG_VIDEO_WM8739) += wm8739.o ++obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o ++obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o ++obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o ++obj-$(CONFIG_VIDEO_OV7670) += ov7670.o ++obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o ++obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o ++obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o ++obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o ++obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o ++obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o ++obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o ++obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o ++obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o ++obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o ++obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o ++obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o ++obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o ++obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o ++obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o ++obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o ++obj-$(CONFIG_VIDEO_AK881X) += ak881x.o ++obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c +new file mode 100644 +index 0000000..c2886b6 +--- /dev/null ++++ b/drivers/media/i2c/ad9389b.c +@@ -0,0 +1,1328 @@ ++/* ++ * Analog Devices AD9389B/AD9889B video encoder driver ++ * ++ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ */ ++ ++/* ++ * References (c = chapter, p = page): ++ * REF_01 - Analog Devices, Programming Guide, AD9889B/AD9389B, ++ * HDMI Transitter, Rev. A, October 2010 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "debug level (0-2)"); ++ ++MODULE_DESCRIPTION("Analog Devices AD9389B/AD9889B video encoder driver"); ++MODULE_AUTHOR("Hans Verkuil "); ++MODULE_AUTHOR("Martin Bugge "); ++MODULE_LICENSE("GPL"); ++ ++#define MASK_AD9389B_EDID_RDY_INT 0x04 ++#define MASK_AD9389B_MSEN_INT 0x40 ++#define MASK_AD9389B_HPD_INT 0x80 ++ ++#define MASK_AD9389B_HPD_DETECT 0x40 ++#define MASK_AD9389B_MSEN_DETECT 0x20 ++#define MASK_AD9389B_EDID_RDY 0x10 ++ ++#define EDID_MAX_RETRIES (8) ++#define EDID_DELAY 250 ++#define EDID_MAX_SEGM 8 ++ ++/* ++********************************************************************** ++* ++* Arrays with configuration parameters for the AD9389B ++* ++********************************************************************** ++*/ ++ ++struct i2c_reg_value { ++ u8 reg; ++ u8 value; ++}; ++ ++struct ad9389b_state_edid { ++ /* total number of blocks */ ++ u32 blocks; ++ /* Number of segments read */ ++ u32 segments; ++ u8 data[EDID_MAX_SEGM * 256]; ++ /* Number of EDID read retries left */ ++ unsigned read_retries; ++}; ++ ++struct ad9389b_state { ++ struct ad9389b_platform_data pdata; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler hdl; ++ int chip_revision; ++ /* Is the ad9389b powered on? */ ++ bool power_on; ++ /* Did we receive hotplug and rx-sense signals? */ ++ bool have_monitor; ++ /* timings from s_dv_timings */ ++ struct v4l2_dv_timings dv_timings; ++ /* controls */ ++ struct v4l2_ctrl *hdmi_mode_ctrl; ++ struct v4l2_ctrl *hotplug_ctrl; ++ struct v4l2_ctrl *rx_sense_ctrl; ++ struct v4l2_ctrl *have_edid0_ctrl; ++ struct v4l2_ctrl *rgb_quantization_range_ctrl; ++ struct i2c_client *edid_i2c_client; ++ struct ad9389b_state_edid edid; ++ /* Running counter of the number of detected EDIDs (for debugging) */ ++ unsigned edid_detect_counter; ++ struct workqueue_struct *work_queue; ++ struct delayed_work edid_handler; /* work entry */ ++}; ++ ++static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd); ++static bool ad9389b_check_edid_status(struct v4l2_subdev *sd); ++static void ad9389b_setup(struct v4l2_subdev *sd); ++static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); ++static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq); ++ ++static inline struct ad9389b_state *get_ad9389b_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ad9389b_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct ad9389b_state, hdl)->sd; ++} ++ ++/* ------------------------ I2C ----------------------------------------------- */ ++ ++static int ad9389b_rd(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ int i; ++ ++ for (i = 0; i < 3; i++) { ++ ret = i2c_smbus_write_byte_data(client, reg, val); ++ if (ret == 0) ++ return 0; ++ } ++ v4l2_err(sd, "I2C Write Problem\n"); ++ return ret; ++} ++ ++/* To set specific bits in the register, a clear-mask is given (to be AND-ed), ++ and then the value-mask (to be OR-ed). */ ++static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg, ++ u8 clr_mask, u8 val_mask) ++{ ++ ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask); ++} ++ ++static void ad9389b_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ int i; ++ ++ v4l2_dbg(1, debug, sd, "%s:\n", __func__); ++ ++ for (i = 0; i < len; i++) ++ buf[i] = i2c_smbus_read_byte_data(state->edid_i2c_client, i); ++} ++ ++static inline bool ad9389b_have_hotplug(struct v4l2_subdev *sd) ++{ ++ return ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT; ++} ++ ++static inline bool ad9389b_have_rx_sense(struct v4l2_subdev *sd) ++{ ++ return ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT; ++} ++ ++static void ad9389b_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode) ++{ ++ ad9389b_wr_and_or(sd, 0x17, 0xe7, (mode & 0x3)<<3); ++ ad9389b_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); ++} ++ ++static void ad9389b_csc_coeff(struct v4l2_subdev *sd, ++ u16 A1, u16 A2, u16 A3, u16 A4, ++ u16 B1, u16 B2, u16 B3, u16 B4, ++ u16 C1, u16 C2, u16 C3, u16 C4) ++{ ++ /* A */ ++ ad9389b_wr_and_or(sd, 0x18, 0xe0, A1>>8); ++ ad9389b_wr(sd, 0x19, A1); ++ ad9389b_wr_and_or(sd, 0x1A, 0xe0, A2>>8); ++ ad9389b_wr(sd, 0x1B, A2); ++ ad9389b_wr_and_or(sd, 0x1c, 0xe0, A3>>8); ++ ad9389b_wr(sd, 0x1d, A3); ++ ad9389b_wr_and_or(sd, 0x1e, 0xe0, A4>>8); ++ ad9389b_wr(sd, 0x1f, A4); ++ ++ /* B */ ++ ad9389b_wr_and_or(sd, 0x20, 0xe0, B1>>8); ++ ad9389b_wr(sd, 0x21, B1); ++ ad9389b_wr_and_or(sd, 0x22, 0xe0, B2>>8); ++ ad9389b_wr(sd, 0x23, B2); ++ ad9389b_wr_and_or(sd, 0x24, 0xe0, B3>>8); ++ ad9389b_wr(sd, 0x25, B3); ++ ad9389b_wr_and_or(sd, 0x26, 0xe0, B4>>8); ++ ad9389b_wr(sd, 0x27, B4); ++ ++ /* C */ ++ ad9389b_wr_and_or(sd, 0x28, 0xe0, C1>>8); ++ ad9389b_wr(sd, 0x29, C1); ++ ad9389b_wr_and_or(sd, 0x2A, 0xe0, C2>>8); ++ ad9389b_wr(sd, 0x2B, C2); ++ ad9389b_wr_and_or(sd, 0x2C, 0xe0, C3>>8); ++ ad9389b_wr(sd, 0x2D, C3); ++ ad9389b_wr_and_or(sd, 0x2E, 0xe0, C4>>8); ++ ad9389b_wr(sd, 0x2F, C4); ++} ++ ++static void ad9389b_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) ++{ ++ if (enable) { ++ u8 csc_mode = 0; ++ ++ ad9389b_csc_conversion_mode(sd, csc_mode); ++ ad9389b_csc_coeff(sd, ++ 4096-564, 0, 0, 256, ++ 0, 4096-564, 0, 256, ++ 0, 0, 4096-564, 256); ++ /* enable CSC */ ++ ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x1); ++ /* AVI infoframe: Limited range RGB (16-235) */ ++ ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x02); ++ } else { ++ /* disable CSC */ ++ ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x0); ++ /* AVI infoframe: Full range RGB (0-255) */ ++ ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x04); ++ } ++} ++ ++static void ad9389b_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { ++ /* CEA format, not IT */ ++ ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x00); ++ } else { ++ /* IT format */ ++ ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x40); ++ } ++} ++ ++static int ad9389b_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ switch (ctrl->val) { ++ case V4L2_DV_RGB_RANGE_AUTO: ++ /* automatic */ ++ if (state->dv_timings.bt.standards & V4L2_DV_BT_STD_CEA861) { ++ /* cea format, RGB limited range (16-235) */ ++ ad9389b_csc_rgb_full2limit(sd, true); ++ } else { ++ /* not cea format, RGB full range (0-255) */ ++ ad9389b_csc_rgb_full2limit(sd, false); ++ } ++ break; ++ case V4L2_DV_RGB_RANGE_LIMITED: ++ /* RGB limited range (16-235) */ ++ ad9389b_csc_rgb_full2limit(sd, true); ++ break; ++ case V4L2_DV_RGB_RANGE_FULL: ++ /* RGB full range (0-255) */ ++ ad9389b_csc_rgb_full2limit(sd, false); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void ad9389b_set_manual_pll_gear(struct v4l2_subdev *sd, u32 pixelclock) ++{ ++ u8 gear; ++ ++ /* Workaround for TMDS PLL problem ++ * The TMDS PLL in AD9389b change gear when the chip is heated above a ++ * certain temperature. The output is disabled when the PLL change gear ++ * so the monitor has to lock on the signal again. A workaround for ++ * this is to use the manual PLL gears. This is a solution from Analog ++ * Devices that is not documented in the datasheets. ++ * 0x98 [7] = enable manual gearing. 0x98 [6:4] = gear ++ * ++ * The pixel frequency ranges are based on readout of the gear the ++ * automatic gearing selects for different pixel clocks ++ * (read from 0x9e [3:1]). ++ */ ++ ++ if (pixelclock > 140000000) ++ gear = 0xc0; /* 4th gear */ ++ else if (pixelclock > 117000000) ++ gear = 0xb0; /* 3rd gear */ ++ else if (pixelclock > 87000000) ++ gear = 0xa0; /* 2nd gear */ ++ else if (pixelclock > 60000000) ++ gear = 0x90; /* 1st gear */ ++ else ++ gear = 0x80; /* 0th gear */ ++ ++ ad9389b_wr_and_or(sd, 0x98, 0x0f, gear); ++} ++ ++/* ------------------------------ CTRL OPS ------------------------------ */ ++ ++static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ v4l2_dbg(1, debug, sd, ++ "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); ++ ++ if (state->hdmi_mode_ctrl == ctrl) { ++ /* Set HDMI or DVI-D */ ++ ad9389b_wr_and_or(sd, 0xaf, 0xfd, ++ ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); ++ return 0; ++ } ++ if (state->rgb_quantization_range_ctrl == ctrl) ++ return ad9389b_set_rgb_quantization_mode(sd, ctrl); ++ return -EINVAL; ++} ++ ++static const struct v4l2_ctrl_ops ad9389b_ctrl_ops = { ++ .s_ctrl = ad9389b_s_ctrl, ++}; ++ ++/* ---------------------------- CORE OPS ------------------------------------------- */ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = ad9389b_rd(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int ad9389b_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ad9389b_wr(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int ad9389b_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_AD9389B, 0); ++} ++ ++static int ad9389b_log_status(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ struct ad9389b_state_edid *edid = &state->edid; ++ ++ static const char * const states[] = { ++ "in reset", ++ "reading EDID", ++ "idle", ++ "initializing HDCP", ++ "HDCP enabled", ++ "initializing HDCP repeater", ++ "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ++ }; ++ static const char * const errors[] = { ++ "no error", ++ "bad receiver BKSV", ++ "Ri mismatch", ++ "Pj mismatch", ++ "i2c error", ++ "timed out", ++ "max repeater cascade exceeded", ++ "hash check failed", ++ "too many devices", ++ "9", "A", "B", "C", "D", "E", "F" ++ }; ++ ++ u8 manual_gear; ++ ++ v4l2_info(sd, "chip revision %d\n", state->chip_revision); ++ v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); ++ v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", ++ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? ++ "detected" : "no", ++ (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? ++ "detected" : "no", ++ edid->segments ? "found" : "no", edid->blocks); ++ if (state->have_monitor) { ++ v4l2_info(sd, "%s output %s\n", ++ (ad9389b_rd(sd, 0xaf) & 0x02) ? ++ "HDMI" : "DVI-D", ++ (ad9389b_rd(sd, 0xa1) & 0x3c) ? ++ "disabled" : "enabled"); ++ } ++ v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ? ++ "encrypted" : "no encryption"); ++ v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", ++ states[ad9389b_rd(sd, 0xc8) & 0xf], ++ errors[ad9389b_rd(sd, 0xc8) >> 4], ++ state->edid_detect_counter, ++ ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); ++ manual_gear = ad9389b_rd(sd, 0x98) & 0x80; ++ v4l2_info(sd, "ad9389b: RGB quantization: %s range\n", ++ ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); ++ v4l2_info(sd, "ad9389b: %s gear %d\n", ++ manual_gear ? "manual" : "automatic", ++ manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) : ++ ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); ++ if (state->have_monitor) { ++ if (ad9389b_rd(sd, 0xaf) & 0x02) { ++ /* HDMI only */ ++ u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; ++ u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | ++ ad9389b_rd(sd, 0x02) << 8 | ++ ad9389b_rd(sd, 0x03); ++ u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; ++ u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; ++ u32 CTS; ++ ++ if (manual_cts) ++ CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | ++ ad9389b_rd(sd, 0x08) << 8 | ++ ad9389b_rd(sd, 0x09); ++ else ++ CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | ++ ad9389b_rd(sd, 0x05) << 8 | ++ ad9389b_rd(sd, 0x06); ++ N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | ++ ad9389b_rd(sd, 0x02) << 8 | ++ ad9389b_rd(sd, 0x03); ++ ++ v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", ++ manual_cts ? "manual" : "automatic", N, CTS); ++ ++ v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", ++ vic_detect, vic_sent); ++ } ++ } ++ if (state->dv_timings.type == V4L2_DV_BT_656_1120) { ++ struct v4l2_bt_timings *bt = bt = &state->dv_timings.bt; ++ u32 frame_width = bt->width + bt->hfrontporch + ++ bt->hsync + bt->hbackporch; ++ u32 frame_height = bt->height + bt->vfrontporch + ++ bt->vsync + bt->vbackporch; ++ u32 frame_size = frame_width * frame_height; ++ ++ v4l2_info(sd, "timings: %ux%u%s%u (%ux%u). Pix freq. = %u Hz. Polarities = 0x%x\n", ++ bt->width, bt->height, bt->interlaced ? "i" : "p", ++ frame_size > 0 ? (unsigned)bt->pixelclock / frame_size : 0, ++ frame_width, frame_height, ++ (unsigned)bt->pixelclock, bt->polarities); ++ } else { ++ v4l2_info(sd, "no timings set\n"); ++ } ++ return 0; ++} ++ ++/* Power up/down ad9389b */ ++static int ad9389b_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ struct ad9389b_platform_data *pdata = &state->pdata; ++ const int retries = 20; ++ int i; ++ ++ v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); ++ ++ state->power_on = on; ++ ++ if (!on) { ++ /* Power down */ ++ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); ++ return true; ++ } ++ ++ /* Power up */ ++ /* The ad9389b does not always come up immediately. ++ Retry multiple times. */ ++ for (i = 0; i < retries; i++) { ++ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x0); ++ if ((ad9389b_rd(sd, 0x41) & 0x40) == 0) ++ break; ++ ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); ++ msleep(10); ++ } ++ if (i == retries) { ++ v4l2_dbg(1, debug, sd, "failed to powerup the ad9389b\n"); ++ ad9389b_s_power(sd, 0); ++ return false; ++ } ++ if (i > 1) ++ v4l2_dbg(1, debug, sd, ++ "needed %d retries to powerup the ad9389b\n", i); ++ ++ /* Select chip: AD9389B */ ++ ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10); ++ ++ /* Reserved registers that must be set according to REF_01 p. 11*/ ++ ad9389b_wr_and_or(sd, 0x98, 0xf0, 0x07); ++ ad9389b_wr(sd, 0x9c, 0x38); ++ ad9389b_wr_and_or(sd, 0x9d, 0xfc, 0x01); ++ ++ /* Differential output drive strength */ ++ if (pdata->diff_data_drive_strength > 0) ++ ad9389b_wr(sd, 0xa2, pdata->diff_data_drive_strength); ++ else ++ ad9389b_wr(sd, 0xa2, 0x87); ++ ++ if (pdata->diff_clk_drive_strength > 0) ++ ad9389b_wr(sd, 0xa3, pdata->diff_clk_drive_strength); ++ else ++ ad9389b_wr(sd, 0xa3, 0x87); ++ ++ ad9389b_wr(sd, 0x0a, 0x01); ++ ad9389b_wr(sd, 0xbb, 0xff); ++ ++ /* Set number of attempts to read the EDID */ ++ ad9389b_wr(sd, 0xc9, 0xf); ++ return true; ++} ++ ++/* Enable interrupts */ ++static void ad9389b_set_isr(struct v4l2_subdev *sd, bool enable) ++{ ++ u8 irqs = MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT; ++ u8 irqs_rd; ++ int retries = 100; ++ ++ /* The datasheet says that the EDID ready interrupt should be ++ disabled if there is no hotplug. */ ++ if (!enable) ++ irqs = 0; ++ else if (ad9389b_have_hotplug(sd)) ++ irqs |= MASK_AD9389B_EDID_RDY_INT; ++ ++ /* ++ * This i2c write can fail (approx. 1 in 1000 writes). But it ++ * is essential that this register is correct, so retry it ++ * multiple times. ++ * ++ * Note that the i2c write does not report an error, but the readback ++ * clearly shows the wrong value. ++ */ ++ do { ++ ad9389b_wr(sd, 0x94, irqs); ++ irqs_rd = ad9389b_rd(sd, 0x94); ++ } while (retries-- && irqs_rd != irqs); ++ ++ if (irqs_rd != irqs) ++ v4l2_err(sd, "Could not set interrupts: hw failure?\n"); ++} ++ ++/* Interrupt handler */ ++static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled) ++{ ++ u8 irq_status; ++ ++ /* disable interrupts to prevent a race condition */ ++ ad9389b_set_isr(sd, false); ++ irq_status = ad9389b_rd(sd, 0x96); ++ /* clear detected interrupts */ ++ ad9389b_wr(sd, 0x96, irq_status); ++ ++ if (irq_status & (MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT)) ++ ad9389b_check_monitor_present_status(sd); ++ if (irq_status & MASK_AD9389B_EDID_RDY_INT) ++ ad9389b_check_edid_status(sd); ++ ++ /* enable interrupts */ ++ ad9389b_set_isr(sd, true); ++ *handled = true; ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops ad9389b_core_ops = { ++ .log_status = ad9389b_log_status, ++ .g_chip_ident = ad9389b_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ad9389b_g_register, ++ .s_register = ad9389b_s_register, ++#endif ++ .s_power = ad9389b_s_power, ++ .interrupt_service_routine = ad9389b_isr, ++}; ++ ++/* ------------------------------ PAD OPS ------------------------------ */ ++ ++static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ if (edid->pad != 0) ++ return -EINVAL; ++ if (edid->blocks == 0 || edid->blocks > 256) ++ return -EINVAL; ++ if (!edid->edid) ++ return -EINVAL; ++ if (!state->edid.segments) { ++ v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); ++ return -ENODATA; ++ } ++ if (edid->start_block >= state->edid.segments * 2) ++ return -E2BIG; ++ if (edid->blocks + edid->start_block >= state->edid.segments * 2) ++ edid->blocks = state->edid.segments * 2 - edid->start_block; ++ memcpy(edid->edid, &state->edid.data[edid->start_block * 128], ++ 128 * edid->blocks); ++ return 0; ++} ++ ++static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { ++ .get_edid = ad9389b_get_edid, ++}; ++ ++/* ------------------------------ VIDEO OPS ------------------------------ */ ++ ++/* Enable/disable ad9389b output */ ++static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); ++ ++ ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); ++ if (enable) { ++ ad9389b_check_monitor_present_status(sd); ++ } else { ++ ad9389b_s_power(sd, 0); ++ state->have_monitor = false; ++ } ++ return 0; ++} ++ ++static const struct v4l2_dv_timings ad9389b_timings[] = { ++ V4L2_DV_BT_CEA_720X480P59_94, ++ V4L2_DV_BT_CEA_720X576P50, ++ V4L2_DV_BT_CEA_1280X720P24, ++ V4L2_DV_BT_CEA_1280X720P25, ++ V4L2_DV_BT_CEA_1280X720P30, ++ V4L2_DV_BT_CEA_1280X720P50, ++ V4L2_DV_BT_CEA_1280X720P60, ++ V4L2_DV_BT_CEA_1920X1080P24, ++ V4L2_DV_BT_CEA_1920X1080P25, ++ V4L2_DV_BT_CEA_1920X1080P30, ++ V4L2_DV_BT_CEA_1920X1080P50, ++ V4L2_DV_BT_CEA_1920X1080P60, ++ ++ V4L2_DV_BT_DMT_640X350P85, ++ V4L2_DV_BT_DMT_640X400P85, ++ V4L2_DV_BT_DMT_720X400P85, ++ V4L2_DV_BT_DMT_640X480P60, ++ V4L2_DV_BT_DMT_640X480P72, ++ V4L2_DV_BT_DMT_640X480P75, ++ V4L2_DV_BT_DMT_640X480P85, ++ V4L2_DV_BT_DMT_800X600P56, ++ V4L2_DV_BT_DMT_800X600P60, ++ V4L2_DV_BT_DMT_800X600P72, ++ V4L2_DV_BT_DMT_800X600P75, ++ V4L2_DV_BT_DMT_800X600P85, ++ V4L2_DV_BT_DMT_848X480P60, ++ V4L2_DV_BT_DMT_1024X768P60, ++ V4L2_DV_BT_DMT_1024X768P70, ++ V4L2_DV_BT_DMT_1024X768P75, ++ V4L2_DV_BT_DMT_1024X768P85, ++ V4L2_DV_BT_DMT_1152X864P75, ++ V4L2_DV_BT_DMT_1280X768P60_RB, ++ V4L2_DV_BT_DMT_1280X768P60, ++ V4L2_DV_BT_DMT_1280X768P75, ++ V4L2_DV_BT_DMT_1280X768P85, ++ V4L2_DV_BT_DMT_1280X800P60_RB, ++ V4L2_DV_BT_DMT_1280X800P60, ++ V4L2_DV_BT_DMT_1280X800P75, ++ V4L2_DV_BT_DMT_1280X800P85, ++ V4L2_DV_BT_DMT_1280X960P60, ++ V4L2_DV_BT_DMT_1280X960P85, ++ V4L2_DV_BT_DMT_1280X1024P60, ++ V4L2_DV_BT_DMT_1280X1024P75, ++ V4L2_DV_BT_DMT_1280X1024P85, ++ V4L2_DV_BT_DMT_1360X768P60, ++ V4L2_DV_BT_DMT_1400X1050P60_RB, ++ V4L2_DV_BT_DMT_1400X1050P60, ++ V4L2_DV_BT_DMT_1400X1050P75, ++ V4L2_DV_BT_DMT_1400X1050P85, ++ V4L2_DV_BT_DMT_1440X900P60_RB, ++ V4L2_DV_BT_DMT_1440X900P60, ++ V4L2_DV_BT_DMT_1600X1200P60, ++ V4L2_DV_BT_DMT_1680X1050P60_RB, ++ V4L2_DV_BT_DMT_1680X1050P60, ++ V4L2_DV_BT_DMT_1792X1344P60, ++ V4L2_DV_BT_DMT_1856X1392P60, ++ V4L2_DV_BT_DMT_1920X1200P60_RB, ++ V4L2_DV_BT_DMT_1366X768P60, ++ V4L2_DV_BT_DMT_1920X1080P60, ++ {}, ++}; ++ ++static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ int i; ++ ++ v4l2_dbg(1, debug, sd, "%s:\n", __func__); ++ ++ /* quick sanity check */ ++ if (timings->type != V4L2_DV_BT_656_1120) ++ return -EINVAL; ++ ++ if (timings->bt.interlaced) ++ return -EINVAL; ++ if (timings->bt.pixelclock < 27000000 || ++ timings->bt.pixelclock > 170000000) ++ return -EINVAL; ++ ++ /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings ++ if the format is listed in ad9389b_timings[] */ ++ for (i = 0; ad9389b_timings[i].bt.width; i++) { ++ if (v4l_match_dv_timings(timings, &ad9389b_timings[i], 0)) { ++ *timings = ad9389b_timings[i]; ++ break; ++ } ++ } ++ ++ timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; ++ ++ /* save timings */ ++ state->dv_timings = *timings; ++ ++ /* update quantization range based on new dv_timings */ ++ ad9389b_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); ++ ++ /* update PLL gear based on new dv_timings */ ++ if (state->pdata.tmds_pll_gear == AD9389B_TMDS_PLL_GEAR_SEMI_AUTOMATIC) ++ ad9389b_set_manual_pll_gear(sd, (u32)timings->bt.pixelclock); ++ ++ /* update AVI infoframe */ ++ ad9389b_set_IT_content_AVI_InfoFrame(sd); ++ ++ return 0; ++} ++ ++static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "%s:\n", __func__); ++ ++ if (!timings) ++ return -EINVAL; ++ ++ *timings = state->dv_timings; ++ ++ return 0; ++} ++ ++static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ if (timings->index >= ARRAY_SIZE(ad9389b_timings)) ++ return -EINVAL; ++ ++ memset(timings->reserved, 0, sizeof(timings->reserved)); ++ timings->timings = ad9389b_timings[timings->index]; ++ return 0; ++} ++ ++static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings_cap *cap) ++{ ++ cap->type = V4L2_DV_BT_656_1120; ++ cap->bt.max_width = 1920; ++ cap->bt.max_height = 1200; ++ cap->bt.min_pixelclock = 27000000; ++ cap->bt.max_pixelclock = 170000000; ++ cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT; ++ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM; ++ return 0; ++} ++ ++static const struct v4l2_subdev_video_ops ad9389b_video_ops = { ++ .s_stream = ad9389b_s_stream, ++ .s_dv_timings = ad9389b_s_dv_timings, ++ .g_dv_timings = ad9389b_g_dv_timings, ++ .enum_dv_timings = ad9389b_enum_dv_timings, ++ .dv_timings_cap = ad9389b_dv_timings_cap, ++}; ++ ++static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable) ++{ ++ v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); ++ ++ if (enable) ++ ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x80); ++ else ++ ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x40); ++ ++ return 0; ++} ++ ++static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ u32 N; ++ ++ switch (freq) { ++ case 32000: N = 4096; break; ++ case 44100: N = 6272; break; ++ case 48000: N = 6144; break; ++ case 88200: N = 12544; break; ++ case 96000: N = 12288; break; ++ case 176400: N = 25088; break; ++ case 192000: N = 24576; break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Set N (used with CTS to regenerate the audio clock) */ ++ ad9389b_wr(sd, 0x01, (N >> 16) & 0xf); ++ ad9389b_wr(sd, 0x02, (N >> 8) & 0xff); ++ ad9389b_wr(sd, 0x03, N & 0xff); ++ ++ return 0; ++} ++ ++static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ u32 i2s_sf; ++ ++ switch (freq) { ++ case 32000: i2s_sf = 0x30; break; ++ case 44100: i2s_sf = 0x00; break; ++ case 48000: i2s_sf = 0x20; break; ++ case 88200: i2s_sf = 0x80; break; ++ case 96000: i2s_sf = 0xa0; break; ++ case 176400: i2s_sf = 0xc0; break; ++ case 192000: i2s_sf = 0xe0; break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Set sampling frequency for I2S audio to 48 kHz */ ++ ad9389b_wr_and_or(sd, 0x15, 0xf, i2s_sf); ++ ++ return 0; ++} ++ ++static int ad9389b_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) ++{ ++ /* TODO based on input/output/config */ ++ /* TODO See datasheet "Programmers guide" p. 39-40 */ ++ ++ /* Only 2 channels in use for application */ ++ ad9389b_wr_and_or(sd, 0x50, 0x1f, 0x20); ++ /* Speaker mapping */ ++ ad9389b_wr(sd, 0x51, 0x00); ++ ++ /* TODO Where should this be placed? */ ++ /* 16 bit audio word length */ ++ ad9389b_wr_and_or(sd, 0x14, 0xf0, 0x02); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_audio_ops ad9389b_audio_ops = { ++ .s_stream = ad9389b_s_audio_stream, ++ .s_clock_freq = ad9389b_s_clock_freq, ++ .s_i2s_clock_freq = ad9389b_s_i2s_clock_freq, ++ .s_routing = ad9389b_s_routing, ++}; ++ ++/* --------------------- SUBDEV OPS --------------------------------------- */ ++ ++static const struct v4l2_subdev_ops ad9389b_ops = { ++ .core = &ad9389b_core_ops, ++ .video = &ad9389b_video_ops, ++ .audio = &ad9389b_audio_ops, ++ .pad = &ad9389b_pad_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, ++ int segment, u8 *buf) ++{ ++ int i, j; ++ ++ if (debug < lvl) ++ return; ++ ++ v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); ++ for (i = 0; i < 256; i += 16) { ++ u8 b[128]; ++ u8 *bp = b; ++ ++ if (i == 128) ++ v4l2_dbg(lvl, debug, sd, "\n"); ++ for (j = i; j < i + 16; j++) { ++ sprintf(bp, "0x%02x, ", buf[j]); ++ bp += 6; ++ } ++ bp[0] = '\0'; ++ v4l2_dbg(lvl, debug, sd, "%s\n", b); ++ } ++} ++ ++static void ad9389b_edid_handler(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct ad9389b_state *state = container_of(dwork, ++ struct ad9389b_state, edid_handler); ++ struct v4l2_subdev *sd = &state->sd; ++ struct ad9389b_edid_detect ed; ++ ++ v4l2_dbg(1, debug, sd, "%s:\n", __func__); ++ ++ if (ad9389b_check_edid_status(sd)) { ++ /* Return if we received the EDID. */ ++ return; ++ } ++ ++ if (ad9389b_have_hotplug(sd)) { ++ /* We must retry reading the EDID several times, it is possible ++ * that initially the EDID couldn't be read due to i2c errors ++ * (DVI connectors are particularly prone to this problem). */ ++ if (state->edid.read_retries) { ++ state->edid.read_retries--; ++ /* EDID read failed, trigger a retry */ ++ ad9389b_wr(sd, 0xc9, 0xf); ++ queue_delayed_work(state->work_queue, ++ &state->edid_handler, EDID_DELAY); ++ return; ++ } ++ } ++ ++ /* We failed to read the EDID, so send an event for this. */ ++ ed.present = false; ++ ed.segment = ad9389b_rd(sd, 0xc4); ++ v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); ++ v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); ++} ++ ++static void ad9389b_audio_setup(struct v4l2_subdev *sd) ++{ ++ v4l2_dbg(1, debug, sd, "%s\n", __func__); ++ ++ ad9389b_s_i2s_clock_freq(sd, 48000); ++ ad9389b_s_clock_freq(sd, 48000); ++ ad9389b_s_routing(sd, 0, 0, 0); ++} ++ ++/* Initial setup of AD9389b */ ++ ++/* Configure hdmi transmitter. */ ++static void ad9389b_setup(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "%s\n", __func__); ++ ++ /* Input format: RGB 4:4:4 */ ++ ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0); ++ /* Output format: RGB 4:4:4 */ ++ ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0); ++ /* CSC fixed point: +/-2, 1st order interpolation 4:2:2 -> 4:4:4 up ++ conversion, Aspect ratio: 16:9 */ ++ ad9389b_wr_and_or(sd, 0x17, 0xe1, 0x0e); ++ /* Disable pixel repetition and CSC */ ++ ad9389b_wr_and_or(sd, 0x3b, 0x9e, 0x0); ++ /* Output format: RGB 4:4:4, Active Format Information is valid. */ ++ ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08); ++ /* Underscanned */ ++ ad9389b_wr_and_or(sd, 0x46, 0x3f, 0x80); ++ /* Setup video format */ ++ ad9389b_wr(sd, 0x3c, 0x0); ++ /* Active format aspect ratio: same as picure. */ ++ ad9389b_wr(sd, 0x47, 0x80); ++ /* No encryption */ ++ ad9389b_wr_and_or(sd, 0xaf, 0xef, 0x0); ++ /* Positive clk edge capture for input video clock */ ++ ad9389b_wr_and_or(sd, 0xba, 0x1f, 0x60); ++ ++ ad9389b_audio_setup(sd); ++ ++ v4l2_ctrl_handler_setup(&state->hdl); ++ ++ ad9389b_set_IT_content_AVI_InfoFrame(sd); ++} ++ ++static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_monitor_detect mdt; ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ mdt.present = state->have_monitor; ++ v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt); ++} ++ ++static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ /* read hotplug and rx-sense state */ ++ u8 status = ad9389b_rd(sd, 0x42); ++ ++ v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", ++ __func__, ++ status, ++ status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", ++ status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); ++ ++ if ((status & MASK_AD9389B_HPD_DETECT) && ++ ((status & MASK_AD9389B_MSEN_DETECT) || state->edid.segments)) { ++ v4l2_dbg(1, debug, sd, ++ "%s: hotplug and (rx-sense or edid)\n", __func__); ++ if (!state->have_monitor) { ++ v4l2_dbg(1, debug, sd, "%s: monitor detected\n", __func__); ++ state->have_monitor = true; ++ ad9389b_set_isr(sd, true); ++ if (!ad9389b_s_power(sd, true)) { ++ v4l2_dbg(1, debug, sd, ++ "%s: monitor detected, powerup failed\n", __func__); ++ return; ++ } ++ ad9389b_setup(sd); ++ ad9389b_notify_monitor_detect(sd); ++ state->edid.read_retries = EDID_MAX_RETRIES; ++ queue_delayed_work(state->work_queue, ++ &state->edid_handler, EDID_DELAY); ++ } ++ } else if (status & MASK_AD9389B_HPD_DETECT) { ++ v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); ++ state->edid.read_retries = EDID_MAX_RETRIES; ++ queue_delayed_work(state->work_queue, ++ &state->edid_handler, EDID_DELAY); ++ } else if (!(status & MASK_AD9389B_HPD_DETECT)) { ++ v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); ++ if (state->have_monitor) { ++ v4l2_dbg(1, debug, sd, "%s: monitor not detected\n", __func__); ++ state->have_monitor = false; ++ ad9389b_notify_monitor_detect(sd); ++ } ++ ad9389b_s_power(sd, false); ++ memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); ++ } ++ ++ /* update read only ctrls */ ++ v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0); ++ v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0); ++ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); ++} ++ ++static bool edid_block_verify_crc(u8 *edid_block) ++{ ++ int i; ++ u8 sum = 0; ++ ++ for (i = 0; i < 127; i++) ++ sum += *(edid_block + i); ++ return ((255 - sum + 1) == edid_block[127]); ++} ++ ++static bool edid_segment_verify_crc(struct v4l2_subdev *sd, u32 segment) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ u32 blocks = state->edid.blocks; ++ u8 *data = state->edid.data; ++ ++ if (edid_block_verify_crc(&data[segment * 256])) { ++ if ((segment + 1) * 2 <= blocks) ++ return edid_block_verify_crc(&data[segment * 256 + 128]); ++ return true; ++ } ++ return false; ++} ++ ++static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ struct ad9389b_edid_detect ed; ++ int segment; ++ u8 edidRdy = ad9389b_rd(sd, 0xc5); ++ ++ v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", ++ __func__, EDID_MAX_RETRIES - state->edid.read_retries); ++ ++ if (!(edidRdy & MASK_AD9389B_EDID_RDY)) ++ return false; ++ ++ segment = ad9389b_rd(sd, 0xc4); ++ if (segment >= EDID_MAX_SEGM) { ++ v4l2_err(sd, "edid segment number too big\n"); ++ return false; ++ } ++ v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); ++ ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]); ++ ad9389b_dbg_dump_edid(2, debug, sd, segment, ++ &state->edid.data[segment * 256]); ++ if (segment == 0) { ++ state->edid.blocks = state->edid.data[0x7e] + 1; ++ v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", ++ __func__, state->edid.blocks); ++ } ++ if (!edid_segment_verify_crc(sd, segment)) { ++ /* edid crc error, force reread of edid segment */ ++ ad9389b_s_power(sd, false); ++ ad9389b_s_power(sd, true); ++ return false; ++ } ++ /* one more segment read ok */ ++ state->edid.segments = segment + 1; ++ if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { ++ /* Request next EDID segment */ ++ v4l2_dbg(1, debug, sd, "%s: request segment %d\n", ++ __func__, state->edid.segments); ++ ad9389b_wr(sd, 0xc9, 0xf); ++ ad9389b_wr(sd, 0xc4, state->edid.segments); ++ state->edid.read_retries = EDID_MAX_RETRIES; ++ queue_delayed_work(state->work_queue, ++ &state->edid_handler, EDID_DELAY); ++ return false; ++ } ++ ++ /* report when we have all segments but report only for segment 0 */ ++ ed.present = true; ++ ed.segment = 0; ++ v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); ++ state->edid_detect_counter++; ++ v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); ++ return ed.present; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void ad9389b_init_setup(struct v4l2_subdev *sd) ++{ ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ struct ad9389b_state_edid *edid = &state->edid; ++ ++ v4l2_dbg(1, debug, sd, "%s\n", __func__); ++ ++ /* clear all interrupts */ ++ ad9389b_wr(sd, 0x96, 0xff); ++ ++ memset(edid, 0, sizeof(struct ad9389b_state_edid)); ++ state->have_monitor = false; ++ ad9389b_set_isr(sd, false); ++} ++ ++static int ad9389b_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ const struct v4l2_dv_timings dv1080p60 = V4L2_DV_BT_CEA_1920X1080P60; ++ struct ad9389b_state *state; ++ struct ad9389b_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_ctrl_handler *hdl; ++ struct v4l2_subdev *sd; ++ int err = -EIO; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n", ++ client->addr << 1); ++ ++ state = kzalloc(sizeof(struct ad9389b_state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ /* Platform data */ ++ if (pdata == NULL) { ++ v4l_err(client, "No platform data!\n"); ++ err = -ENODEV; ++ goto err_free; ++ } ++ memcpy(&state->pdata, pdata, sizeof(state->pdata)); ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &ad9389b_ops); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ hdl = &state->hdl; ++ v4l2_ctrl_handler_init(hdl, 5); ++ ++ /* private controls */ ++ ++ state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, ++ V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, ++ 0, V4L2_DV_TX_MODE_DVI_D); ++ state->hdmi_mode_ctrl->is_private = true; ++ state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, ++ V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); ++ state->hotplug_ctrl->is_private = true; ++ state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, ++ V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); ++ state->rx_sense_ctrl->is_private = true; ++ state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, ++ V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); ++ state->have_edid0_ctrl->is_private = true; ++ state->rgb_quantization_range_ctrl = ++ v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, ++ V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, ++ 0, V4L2_DV_RGB_RANGE_AUTO); ++ state->rgb_quantization_range_ctrl->is_private = true; ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ err = hdl->error; ++ ++ goto err_hdl; ++ } ++ ++ state->pad.flags = MEDIA_PAD_FL_SINK; ++ err = media_entity_init(&sd->entity, 1, &state->pad, 0); ++ if (err) ++ goto err_hdl; ++ ++ state->chip_revision = ad9389b_rd(sd, 0x0); ++ if (state->chip_revision != 2) { ++ v4l2_err(sd, "chip_revision %d != 2\n", state->chip_revision); ++ err = -EIO; ++ goto err_entity; ++ } ++ v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", ++ ad9389b_rd(sd, 0x41), state->chip_revision); ++ ++ state->edid_i2c_client = i2c_new_dummy(client->adapter, (0x7e>>1)); ++ if (state->edid_i2c_client == NULL) { ++ v4l2_err(sd, "failed to register edid i2c client\n"); ++ goto err_entity; ++ } ++ ++ state->work_queue = create_singlethread_workqueue(sd->name); ++ if (state->work_queue == NULL) { ++ v4l2_err(sd, "could not create workqueue\n"); ++ goto err_unreg; ++ } ++ ++ INIT_DELAYED_WORK(&state->edid_handler, ad9389b_edid_handler); ++ state->dv_timings = dv1080p60; ++ ++ ad9389b_init_setup(sd); ++ ad9389b_set_isr(sd, true); ++ ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, ++ client->addr << 1, client->adapter->name); ++ return 0; ++ ++err_unreg: ++ i2c_unregister_device(state->edid_i2c_client); ++err_entity: ++ media_entity_cleanup(&sd->entity); ++err_hdl: ++ v4l2_ctrl_handler_free(&state->hdl); ++err_free: ++ kfree(state); ++ return err; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int ad9389b_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ad9389b_state *state = get_ad9389b_state(sd); ++ ++ state->chip_revision = -1; ++ ++ v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, ++ client->addr << 1, client->adapter->name); ++ ++ ad9389b_s_stream(sd, false); ++ ad9389b_s_audio_stream(sd, false); ++ ad9389b_init_setup(sd); ++ cancel_delayed_work(&state->edid_handler); ++ i2c_unregister_device(state->edid_i2c_client); ++ destroy_workqueue(state->work_queue); ++ v4l2_device_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ kfree(get_ad9389b_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct i2c_device_id ad9389b_id[] = { ++ { "ad9389b", V4L2_IDENT_AD9389B }, ++ { "ad9889b", V4L2_IDENT_AD9389B }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ad9389b_id); ++ ++static struct i2c_driver ad9389b_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ad9389b", ++ }, ++ .probe = ad9389b_probe, ++ .remove = ad9389b_remove, ++ .id_table = ad9389b_id, ++}; ++ ++module_i2c_driver(ad9389b_driver); +diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c +new file mode 100644 +index 0000000..df16380 +--- /dev/null ++++ b/drivers/media/i2c/adp1653.c +@@ -0,0 +1,487 @@ ++/* ++ * drivers/media/i2c/adp1653.c ++ * ++ * Copyright (C) 2008--2011 Nokia Corporation ++ * ++ * Contact: Sakari Ailus ++ * ++ * Contributors: ++ * Sakari Ailus ++ * Tuukka Toivonen ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * TODO: ++ * - fault interrupt handling ++ * - hardware strobe ++ * - power doesn't need to be ON if all lights are off ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define TIMEOUT_MAX 820000 ++#define TIMEOUT_STEP 54600 ++#define TIMEOUT_MIN (TIMEOUT_MAX - ADP1653_REG_CONFIG_TMR_SET_MAX \ ++ * TIMEOUT_STEP) ++#define TIMEOUT_US_TO_CODE(t) ((TIMEOUT_MAX + (TIMEOUT_STEP / 2) - (t)) \ ++ / TIMEOUT_STEP) ++#define TIMEOUT_CODE_TO_US(c) (TIMEOUT_MAX - (c) * TIMEOUT_STEP) ++ ++/* Write values into ADP1653 registers. */ ++static int adp1653_update_hw(struct adp1653_flash *flash) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ u8 out_sel; ++ u8 config = 0; ++ int rval; ++ ++ out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( ++ flash->indicator_intensity->val) ++ << ADP1653_REG_OUT_SEL_ILED_SHIFT; ++ ++ switch (flash->led_mode->val) { ++ case V4L2_FLASH_LED_MODE_NONE: ++ break; ++ case V4L2_FLASH_LED_MODE_FLASH: ++ /* Flash mode, light on with strobe, duration from timer */ ++ config = ADP1653_REG_CONFIG_TMR_CFG; ++ config |= TIMEOUT_US_TO_CODE(flash->flash_timeout->val) ++ << ADP1653_REG_CONFIG_TMR_SET_SHIFT; ++ break; ++ case V4L2_FLASH_LED_MODE_TORCH: ++ /* Torch mode, light immediately on, duration indefinite */ ++ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( ++ flash->torch_intensity->val) ++ << ADP1653_REG_OUT_SEL_HPLED_SHIFT; ++ break; ++ } ++ ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); ++ if (rval < 0) ++ return rval; ++ ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_CONFIG, config); ++ if (rval < 0) ++ return rval; ++ ++ return 0; ++} ++ ++static int adp1653_get_fault(struct adp1653_flash *flash) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int fault; ++ int rval; ++ ++ fault = i2c_smbus_read_byte_data(client, ADP1653_REG_FAULT); ++ if (IS_ERR_VALUE(fault)) ++ return fault; ++ ++ flash->fault |= fault; ++ ++ if (!flash->fault) ++ return 0; ++ ++ /* Clear faults. */ ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); ++ if (IS_ERR_VALUE(rval)) ++ return rval; ++ ++ flash->led_mode->val = V4L2_FLASH_LED_MODE_NONE; ++ ++ rval = adp1653_update_hw(flash); ++ if (IS_ERR_VALUE(rval)) ++ return rval; ++ ++ return flash->fault; ++} ++ ++static int adp1653_strobe(struct adp1653_flash *flash, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ u8 out_sel = ADP1653_INDICATOR_INTENSITY_uA_TO_REG( ++ flash->indicator_intensity->val) ++ << ADP1653_REG_OUT_SEL_ILED_SHIFT; ++ int rval; ++ ++ if (flash->led_mode->val != V4L2_FLASH_LED_MODE_FLASH) ++ return -EBUSY; ++ ++ if (!enable) ++ return i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, ++ out_sel); ++ ++ out_sel |= ADP1653_FLASH_INTENSITY_mA_TO_REG( ++ flash->flash_intensity->val) ++ << ADP1653_REG_OUT_SEL_HPLED_SHIFT; ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, out_sel); ++ if (rval) ++ return rval; ++ ++ /* Software strobe using i2c */ ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, ++ ADP1653_REG_SW_STROBE_SW_STROBE); ++ if (rval) ++ return rval; ++ return i2c_smbus_write_byte_data(client, ADP1653_REG_SW_STROBE, 0); ++} ++ ++/* -------------------------------------------------------------------------- ++ * V4L2 controls ++ */ ++ ++static int adp1653_get_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct adp1653_flash *flash = ++ container_of(ctrl->handler, struct adp1653_flash, ctrls); ++ int rval; ++ ++ rval = adp1653_get_fault(flash); ++ if (IS_ERR_VALUE(rval)) ++ return rval; ++ ++ ctrl->cur.val = 0; ++ ++ if (flash->fault & ADP1653_REG_FAULT_FLT_SCP) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; ++ if (flash->fault & ADP1653_REG_FAULT_FLT_OT) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; ++ if (flash->fault & ADP1653_REG_FAULT_FLT_TMR) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; ++ if (flash->fault & ADP1653_REG_FAULT_FLT_OV) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; ++ ++ flash->fault = 0; ++ ++ return 0; ++} ++ ++static int adp1653_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct adp1653_flash *flash = ++ container_of(ctrl->handler, struct adp1653_flash, ctrls); ++ int rval; ++ ++ rval = adp1653_get_fault(flash); ++ if (IS_ERR_VALUE(rval)) ++ return rval; ++ if ((rval & (ADP1653_REG_FAULT_FLT_SCP | ++ ADP1653_REG_FAULT_FLT_OT | ++ ADP1653_REG_FAULT_FLT_OV)) && ++ (ctrl->id == V4L2_CID_FLASH_STROBE || ++ ctrl->id == V4L2_CID_FLASH_TORCH_INTENSITY || ++ ctrl->id == V4L2_CID_FLASH_LED_MODE)) ++ return -EBUSY; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_FLASH_STROBE: ++ return adp1653_strobe(flash, 1); ++ case V4L2_CID_FLASH_STROBE_STOP: ++ return adp1653_strobe(flash, 0); ++ } ++ ++ return adp1653_update_hw(flash); ++} ++ ++static const struct v4l2_ctrl_ops adp1653_ctrl_ops = { ++ .g_volatile_ctrl = adp1653_get_ctrl, ++ .s_ctrl = adp1653_set_ctrl, ++}; ++ ++static int adp1653_init_controls(struct adp1653_flash *flash) ++{ ++ struct v4l2_ctrl *fault; ++ ++ v4l2_ctrl_handler_init(&flash->ctrls, 9); ++ ++ flash->led_mode = ++ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_LED_MODE, ++ V4L2_FLASH_LED_MODE_TORCH, ~0x7, 0); ++ v4l2_ctrl_new_std_menu(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_STROBE_SOURCE, ++ V4L2_FLASH_STROBE_SOURCE_SOFTWARE, ~0x1, 0); ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); ++ flash->flash_timeout = ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_TIMEOUT, TIMEOUT_MIN, ++ flash->platform_data->max_flash_timeout, ++ TIMEOUT_STEP, ++ flash->platform_data->max_flash_timeout); ++ flash->flash_intensity = ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_INTENSITY, ++ ADP1653_FLASH_INTENSITY_MIN, ++ flash->platform_data->max_flash_intensity, ++ 1, flash->platform_data->max_flash_intensity); ++ flash->torch_intensity = ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_TORCH_INTENSITY, ++ ADP1653_TORCH_INTENSITY_MIN, ++ flash->platform_data->max_torch_intensity, ++ ADP1653_FLASH_INTENSITY_STEP, ++ flash->platform_data->max_torch_intensity); ++ flash->indicator_intensity = ++ v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_INDICATOR_INTENSITY, ++ ADP1653_INDICATOR_INTENSITY_MIN, ++ flash->platform_data->max_indicator_intensity, ++ ADP1653_INDICATOR_INTENSITY_STEP, ++ ADP1653_INDICATOR_INTENSITY_MIN); ++ fault = v4l2_ctrl_new_std(&flash->ctrls, &adp1653_ctrl_ops, ++ V4L2_CID_FLASH_FAULT, 0, ++ V4L2_FLASH_FAULT_OVER_VOLTAGE ++ | V4L2_FLASH_FAULT_OVER_TEMPERATURE ++ | V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); ++ ++ if (flash->ctrls.error) ++ return flash->ctrls.error; ++ ++ fault->flags |= V4L2_CTRL_FLAG_VOLATILE; ++ ++ flash->subdev.ctrl_handler = &flash->ctrls; ++ return 0; ++} ++ ++/* -------------------------------------------------------------------------- ++ * V4L2 subdev operations ++ */ ++ ++static int ++adp1653_init_device(struct adp1653_flash *flash) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int rval; ++ ++ /* Clear FAULT register by writing zero to OUT_SEL */ ++ rval = i2c_smbus_write_byte_data(client, ADP1653_REG_OUT_SEL, 0); ++ if (rval < 0) { ++ dev_err(&client->dev, "failed writing fault register\n"); ++ return -EIO; ++ } ++ ++ mutex_lock(flash->ctrls.lock); ++ /* Reset faults before reading new ones. */ ++ flash->fault = 0; ++ rval = adp1653_get_fault(flash); ++ mutex_unlock(flash->ctrls.lock); ++ if (rval > 0) { ++ dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); ++ return -EIO; ++ } ++ ++ mutex_lock(flash->ctrls.lock); ++ rval = adp1653_update_hw(flash); ++ mutex_unlock(flash->ctrls.lock); ++ if (rval) { ++ dev_err(&client->dev, ++ "adp1653_update_hw failed at %s\n", __func__); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++static int ++__adp1653_set_power(struct adp1653_flash *flash, int on) ++{ ++ int ret; ++ ++ ret = flash->platform_data->power(&flash->subdev, on); ++ if (ret < 0) ++ return ret; ++ ++ if (!on) ++ return 0; ++ ++ ret = adp1653_init_device(flash); ++ if (ret < 0) ++ flash->platform_data->power(&flash->subdev, 0); ++ ++ return ret; ++} ++ ++static int ++adp1653_set_power(struct v4l2_subdev *subdev, int on) ++{ ++ struct adp1653_flash *flash = to_adp1653_flash(subdev); ++ int ret = 0; ++ ++ mutex_lock(&flash->power_lock); ++ ++ /* If the power count is modified from 0 to != 0 or from != 0 to 0, ++ * update the power state. ++ */ ++ if (flash->power_count == !on) { ++ ret = __adp1653_set_power(flash, !!on); ++ if (ret < 0) ++ goto done; ++ } ++ ++ /* Update the power count. */ ++ flash->power_count += on ? 1 : -1; ++ WARN_ON(flash->power_count < 0); ++ ++done: ++ mutex_unlock(&flash->power_lock); ++ return ret; ++} ++ ++static int adp1653_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ return adp1653_set_power(sd, 1); ++} ++ ++static int adp1653_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ return adp1653_set_power(sd, 0); ++} ++ ++static const struct v4l2_subdev_core_ops adp1653_core_ops = { ++ .s_power = adp1653_set_power, ++}; ++ ++static const struct v4l2_subdev_ops adp1653_ops = { ++ .core = &adp1653_core_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops adp1653_internal_ops = { ++ .open = adp1653_open, ++ .close = adp1653_close, ++}; ++ ++/* -------------------------------------------------------------------------- ++ * I2C driver ++ */ ++#ifdef CONFIG_PM ++ ++static int adp1653_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct adp1653_flash *flash = to_adp1653_flash(subdev); ++ ++ if (!flash->power_count) ++ return 0; ++ ++ return __adp1653_set_power(flash, 0); ++} ++ ++static int adp1653_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct adp1653_flash *flash = to_adp1653_flash(subdev); ++ ++ if (!flash->power_count) ++ return 0; ++ ++ return __adp1653_set_power(flash, 1); ++} ++ ++#else ++ ++#define adp1653_suspend NULL ++#define adp1653_resume NULL ++ ++#endif /* CONFIG_PM */ ++ ++static int adp1653_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct adp1653_flash *flash; ++ int ret; ++ ++ /* we couldn't work without platform data */ ++ if (client->dev.platform_data == NULL) ++ return -ENODEV; ++ ++ flash = kzalloc(sizeof(*flash), GFP_KERNEL); ++ if (flash == NULL) ++ return -ENOMEM; ++ ++ flash->platform_data = client->dev.platform_data; ++ ++ mutex_init(&flash->power_lock); ++ ++ v4l2_i2c_subdev_init(&flash->subdev, client, &adp1653_ops); ++ flash->subdev.internal_ops = &adp1653_internal_ops; ++ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ ret = adp1653_init_controls(flash); ++ if (ret) ++ goto free_and_quit; ++ ++ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); ++ if (ret < 0) ++ goto free_and_quit; ++ ++ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; ++ ++ return 0; ++ ++free_and_quit: ++ v4l2_ctrl_handler_free(&flash->ctrls); ++ kfree(flash); ++ return ret; ++} ++ ++static int __exit adp1653_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct adp1653_flash *flash = to_adp1653_flash(subdev); ++ ++ v4l2_device_unregister_subdev(&flash->subdev); ++ v4l2_ctrl_handler_free(&flash->ctrls); ++ media_entity_cleanup(&flash->subdev.entity); ++ kfree(flash); ++ return 0; ++} ++ ++static const struct i2c_device_id adp1653_id_table[] = { ++ { ADP1653_NAME, 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, adp1653_id_table); ++ ++static struct dev_pm_ops adp1653_pm_ops = { ++ .suspend = adp1653_suspend, ++ .resume = adp1653_resume, ++}; ++ ++static struct i2c_driver adp1653_i2c_driver = { ++ .driver = { ++ .name = ADP1653_NAME, ++ .pm = &adp1653_pm_ops, ++ }, ++ .probe = adp1653_probe, ++ .remove = __exit_p(adp1653_remove), ++ .id_table = adp1653_id_table, ++}; ++ ++module_i2c_driver(adp1653_i2c_driver); ++ ++MODULE_AUTHOR("Sakari Ailus "); ++MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c +new file mode 100644 +index 0000000..6bc01fb +--- /dev/null ++++ b/drivers/media/i2c/adv7170.c +@@ -0,0 +1,410 @@ ++/* ++ * adv7170 - adv7170, adv7171 video encoder driver version 0.0.1 ++ * ++ * Copyright (C) 2002 Maxim Yevtyushkin ++ * ++ * Based on adv7176 driver by: ++ * ++ * Copyright (C) 1998 Dave Perks ++ * Copyright (C) 1999 Wolfgang Scherr ++ * Copyright (C) 2000 Serguei Miridonov ++ * - some corrections for Pinnacle Systems Inc. DC10plus card. ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (1/1/2003) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); ++MODULE_AUTHOR("Maxim Yevtyushkin"); ++MODULE_LICENSE("GPL"); ++ ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct adv7170 { ++ struct v4l2_subdev sd; ++ unsigned char reg[128]; ++ ++ v4l2_std_id norm; ++ int input; ++}; ++ ++static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7170, sd); ++} ++ ++static char *inputs[] = { "pass_through", "play_back" }; ++ ++static enum v4l2_mbus_pixelcode adv7170_codes[] = { ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_UYVY8_1X16, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct adv7170 *encoder = to_adv7170(sd); ++ ++ encoder->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int adv7170_write_block(struct v4l2_subdev *sd, ++ const u8 *data, unsigned int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct adv7170 *encoder = to_adv7170(sd); ++ int ret = -1; ++ u8 reg; ++ ++ /* the adv7170 has an autoincrement function, use it if ++ * the adapter understands raw I2C */ ++ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ /* do raw I2C, not smbus compatible */ ++ u8 block_data[32]; ++ int block_len; ++ ++ while (len >= 2) { ++ block_len = 0; ++ block_data[block_len++] = reg = data[0]; ++ do { ++ block_data[block_len++] = ++ encoder->reg[reg++] = data[1]; ++ len -= 2; ++ data += 2; ++ } while (len >= 2 && data[0] == reg && block_len < 32); ++ ret = i2c_master_send(client, block_data, block_len); ++ if (ret < 0) ++ break; ++ } ++ } else { ++ /* do some slow I2C emulation kind of thing */ ++ while (len >= 2) { ++ reg = *data++; ++ ret = adv7170_write(sd, reg, *data++); ++ if (ret < 0) ++ break; ++ len -= 2; ++ } ++ } ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++#define TR0MODE 0x4c ++#define TR0RST 0x80 ++ ++#define TR1CAPT 0x00 ++#define TR1PLAY 0x00 ++ ++static const unsigned char init_NTSC[] = { ++ 0x00, 0x10, /* MR0 */ ++ 0x01, 0x20, /* MR1 */ ++ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ ++ 0x03, 0x80, /* MR3 */ ++ 0x04, 0x30, /* MR4 */ ++ 0x05, 0x00, /* Reserved */ ++ 0x06, 0x00, /* Reserved */ ++ 0x07, TR0MODE, /* TM0 */ ++ 0x08, TR1CAPT, /* TM1 */ ++ 0x09, 0x16, /* Fsc0 */ ++ 0x0a, 0x7c, /* Fsc1 */ ++ 0x0b, 0xf0, /* Fsc2 */ ++ 0x0c, 0x21, /* Fsc3 */ ++ 0x0d, 0x00, /* Subcarrier Phase */ ++ 0x0e, 0x00, /* Closed Capt. Ext 0 */ ++ 0x0f, 0x00, /* Closed Capt. Ext 1 */ ++ 0x10, 0x00, /* Closed Capt. 0 */ ++ 0x11, 0x00, /* Closed Capt. 1 */ ++ 0x12, 0x00, /* Pedestal Ctl 0 */ ++ 0x13, 0x00, /* Pedestal Ctl 1 */ ++ 0x14, 0x00, /* Pedestal Ctl 2 */ ++ 0x15, 0x00, /* Pedestal Ctl 3 */ ++ 0x16, 0x00, /* CGMS_WSS_0 */ ++ 0x17, 0x00, /* CGMS_WSS_1 */ ++ 0x18, 0x00, /* CGMS_WSS_2 */ ++ 0x19, 0x00, /* Teletext Ctl */ ++}; ++ ++static const unsigned char init_PAL[] = { ++ 0x00, 0x71, /* MR0 */ ++ 0x01, 0x20, /* MR1 */ ++ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ ++ 0x03, 0x80, /* MR3 */ ++ 0x04, 0x30, /* MR4 */ ++ 0x05, 0x00, /* Reserved */ ++ 0x06, 0x00, /* Reserved */ ++ 0x07, TR0MODE, /* TM0 */ ++ 0x08, TR1CAPT, /* TM1 */ ++ 0x09, 0xcb, /* Fsc0 */ ++ 0x0a, 0x8a, /* Fsc1 */ ++ 0x0b, 0x09, /* Fsc2 */ ++ 0x0c, 0x2a, /* Fsc3 */ ++ 0x0d, 0x00, /* Subcarrier Phase */ ++ 0x0e, 0x00, /* Closed Capt. Ext 0 */ ++ 0x0f, 0x00, /* Closed Capt. Ext 1 */ ++ 0x10, 0x00, /* Closed Capt. 0 */ ++ 0x11, 0x00, /* Closed Capt. 1 */ ++ 0x12, 0x00, /* Pedestal Ctl 0 */ ++ 0x13, 0x00, /* Pedestal Ctl 1 */ ++ 0x14, 0x00, /* Pedestal Ctl 2 */ ++ 0x15, 0x00, /* Pedestal Ctl 3 */ ++ 0x16, 0x00, /* CGMS_WSS_0 */ ++ 0x17, 0x00, /* CGMS_WSS_1 */ ++ 0x18, 0x00, /* CGMS_WSS_2 */ ++ 0x19, 0x00, /* Teletext Ctl */ ++}; ++ ++ ++static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7170 *encoder = to_adv7170(sd); ++ ++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); ++ ++ if (std & V4L2_STD_NTSC) { ++ adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); ++ if (encoder->input == 0) ++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ ++ adv7170_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7170_write(sd, 0x07, TR0MODE); ++ } else if (std & V4L2_STD_PAL) { ++ adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); ++ if (encoder->input == 0) ++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ ++ adv7170_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7170_write(sd, 0x07, TR0MODE); ++ } else { ++ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", ++ (unsigned long long)std); ++ return -EINVAL; ++ } ++ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); ++ encoder->norm = std; ++ return 0; ++} ++ ++static int adv7170_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7170 *encoder = to_adv7170(sd); ++ ++ /* RJ: input = 0: input is from decoder ++ input = 1: input is from ZR36060 ++ input = 2: color bar */ ++ ++ v4l2_dbg(1, debug, sd, "set input from %s\n", ++ input == 0 ? "decoder" : "ZR36060"); ++ ++ switch (input) { ++ case 0: ++ adv7170_write(sd, 0x01, 0x20); ++ adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ ++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ ++ adv7170_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7170_write(sd, 0x07, TR0MODE); ++ /* udelay(10); */ ++ break; ++ ++ case 1: ++ adv7170_write(sd, 0x01, 0x00); ++ adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ ++ adv7170_write(sd, 0x02, 0x08); ++ adv7170_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7170_write(sd, 0x07, TR0MODE); ++ /* udelay(10); */ ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); ++ return -EINVAL; ++ } ++ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); ++ encoder->input = input; ++ return 0; ++} ++ ++static int adv7170_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(adv7170_codes)) ++ return -EINVAL; ++ ++ *code = adv7170_codes[index]; ++ return 0; ++} ++ ++static int adv7170_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ u8 val = adv7170_read(sd, 0x7); ++ ++ if ((val & 0x40) == (1 << 6)) ++ mf->code = V4L2_MBUS_FMT_UYVY8_1X16; ++ else ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->width = 0; ++ mf->height = 0; ++ mf->field = V4L2_FIELD_ANY; ++ ++ return 0; ++} ++ ++static int adv7170_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ u8 val = adv7170_read(sd, 0x7); ++ int ret; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ val &= ~0x40; ++ break; ++ ++ case V4L2_MBUS_FMT_UYVY8_1X16: ++ val |= 0x40; ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, ++ "illegal v4l2_mbus_framefmt code: %d\n", mf->code); ++ return -EINVAL; ++ } ++ ++ ret = adv7170_write(sd, 0x7, val); ++ ++ return ret; ++} ++ ++static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops adv7170_core_ops = { ++ .g_chip_ident = adv7170_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_video_ops adv7170_video_ops = { ++ .s_std_output = adv7170_s_std_output, ++ .s_routing = adv7170_s_routing, ++ .s_mbus_fmt = adv7170_s_fmt, ++ .g_mbus_fmt = adv7170_g_fmt, ++ .enum_mbus_fmt = adv7170_enum_fmt, ++}; ++ ++static const struct v4l2_subdev_ops adv7170_ops = { ++ .core = &adv7170_core_ops, ++ .video = &adv7170_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int adv7170_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7170 *encoder; ++ struct v4l2_subdev *sd; ++ int i; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); ++ if (encoder == NULL) ++ return -ENOMEM; ++ sd = &encoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &adv7170_ops); ++ encoder->norm = V4L2_STD_NTSC; ++ encoder->input = 0; ++ ++ i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); ++ if (i >= 0) { ++ i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); ++ i = adv7170_write(sd, 0x07, TR0MODE); ++ i = adv7170_read(sd, 0x12); ++ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); ++ } ++ if (i < 0) ++ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); ++ return 0; ++} ++ ++static int adv7170_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_adv7170(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id adv7170_id[] = { ++ { "adv7170", 0 }, ++ { "adv7171", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, adv7170_id); ++ ++static struct i2c_driver adv7170_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7170", ++ }, ++ .probe = adv7170_probe, ++ .remove = adv7170_remove, ++ .id_table = adv7170_id, ++}; ++ ++module_i2c_driver(adv7170_driver); +diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c +new file mode 100644 +index 0000000..c7640fa +--- /dev/null ++++ b/drivers/media/i2c/adv7175.c +@@ -0,0 +1,460 @@ ++/* ++ * adv7175 - adv7175a video encoder driver version 0.0.3 ++ * ++ * Copyright (C) 1998 Dave Perks ++ * Copyright (C) 1999 Wolfgang Scherr ++ * Copyright (C) 2000 Serguei Miridonov ++ * - some corrections for Pinnacle Systems Inc. DC10plus card. ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (9/9/2002) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); ++MODULE_AUTHOR("Dave Perks"); ++MODULE_LICENSE("GPL"); ++ ++#define I2C_ADV7175 0xd4 ++#define I2C_ADV7176 0x54 ++ ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct adv7175 { ++ struct v4l2_subdev sd; ++ v4l2_std_id norm; ++ int input; ++}; ++ ++static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7175, sd); ++} ++ ++static char *inputs[] = { "pass_through", "play_back", "color_bar" }; ++ ++static enum v4l2_mbus_pixelcode adv7175_codes[] = { ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_UYVY8_1X16, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int adv7175_write_block(struct v4l2_subdev *sd, ++ const u8 *data, unsigned int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = -1; ++ u8 reg; ++ ++ /* the adv7175 has an autoincrement function, use it if ++ * the adapter understands raw I2C */ ++ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ /* do raw I2C, not smbus compatible */ ++ u8 block_data[32]; ++ int block_len; ++ ++ while (len >= 2) { ++ block_len = 0; ++ block_data[block_len++] = reg = data[0]; ++ do { ++ block_data[block_len++] = data[1]; ++ reg++; ++ len -= 2; ++ data += 2; ++ } while (len >= 2 && data[0] == reg && block_len < 32); ++ ret = i2c_master_send(client, block_data, block_len); ++ if (ret < 0) ++ break; ++ } ++ } else { ++ /* do some slow I2C emulation kind of thing */ ++ while (len >= 2) { ++ reg = *data++; ++ ret = adv7175_write(sd, reg, *data++); ++ if (ret < 0) ++ break; ++ len -= 2; ++ } ++ } ++ ++ return ret; ++} ++ ++static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) ++{ ++ /* for some reason pass_through NTSC needs ++ * a different sub-carrier freq to remain stable. */ ++ if (pass_through) ++ adv7175_write(sd, 0x02, 0x00); ++ else ++ adv7175_write(sd, 0x02, 0x55); ++ ++ adv7175_write(sd, 0x03, 0x55); ++ adv7175_write(sd, 0x04, 0x55); ++ adv7175_write(sd, 0x05, 0x25); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* Output filter: S-Video Composite */ ++ ++#define MR050 0x11 /* 0x09 */ ++#define MR060 0x14 /* 0x0c */ ++ ++/* ----------------------------------------------------------------------- */ ++ ++#define TR0MODE 0x46 ++#define TR0RST 0x80 ++ ++#define TR1CAPT 0x80 ++#define TR1PLAY 0x00 ++ ++static const unsigned char init_common[] = { ++ ++ 0x00, MR050, /* MR0, PAL enabled */ ++ 0x01, 0x00, /* MR1 */ ++ 0x02, 0x0c, /* subc. freq. */ ++ 0x03, 0x8c, /* subc. freq. */ ++ 0x04, 0x79, /* subc. freq. */ ++ 0x05, 0x26, /* subc. freq. */ ++ 0x06, 0x40, /* subc. phase */ ++ ++ 0x07, TR0MODE, /* TR0, 16bit */ ++ 0x08, 0x21, /* */ ++ 0x09, 0x00, /* */ ++ 0x0a, 0x00, /* */ ++ 0x0b, 0x00, /* */ ++ 0x0c, TR1CAPT, /* TR1 */ ++ 0x0d, 0x4f, /* MR2 */ ++ 0x0e, 0x00, /* */ ++ 0x0f, 0x00, /* */ ++ 0x10, 0x00, /* */ ++ 0x11, 0x00, /* */ ++}; ++ ++static const unsigned char init_pal[] = { ++ 0x00, MR050, /* MR0, PAL enabled */ ++ 0x01, 0x00, /* MR1 */ ++ 0x02, 0x0c, /* subc. freq. */ ++ 0x03, 0x8c, /* subc. freq. */ ++ 0x04, 0x79, /* subc. freq. */ ++ 0x05, 0x26, /* subc. freq. */ ++ 0x06, 0x40, /* subc. phase */ ++}; ++ ++static const unsigned char init_ntsc[] = { ++ 0x00, MR060, /* MR0, NTSC enabled */ ++ 0x01, 0x00, /* MR1 */ ++ 0x02, 0x55, /* subc. freq. */ ++ 0x03, 0x55, /* subc. freq. */ ++ 0x04, 0x55, /* subc. freq. */ ++ 0x05, 0x25, /* subc. freq. */ ++ 0x06, 0x1a, /* subc. phase */ ++}; ++ ++static int adv7175_init(struct v4l2_subdev *sd, u32 val) ++{ ++ /* This is just for testing!!! */ ++ adv7175_write_block(sd, init_common, sizeof(init_common)); ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ return 0; ++} ++ ++static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7175 *encoder = to_adv7175(sd); ++ ++ if (std & V4L2_STD_NTSC) { ++ adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); ++ if (encoder->input == 0) ++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ } else if (std & V4L2_STD_PAL) { ++ adv7175_write_block(sd, init_pal, sizeof(init_pal)); ++ if (encoder->input == 0) ++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ } else if (std & V4L2_STD_SECAM) { ++ /* This is an attempt to convert ++ * SECAM->PAL (typically it does not work ++ * due to genlock: when decoder is in SECAM ++ * and encoder in in PAL the subcarrier can ++ * not be syncronized with horizontal ++ * quency) */ ++ adv7175_write_block(sd, init_pal, sizeof(init_pal)); ++ if (encoder->input == 0) ++ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ } else { ++ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", ++ (unsigned long long)std); ++ return -EINVAL; ++ } ++ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); ++ encoder->norm = std; ++ return 0; ++} ++ ++static int adv7175_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7175 *encoder = to_adv7175(sd); ++ ++ /* RJ: input = 0: input is from decoder ++ input = 1: input is from ZR36060 ++ input = 2: color bar */ ++ ++ switch (input) { ++ case 0: ++ adv7175_write(sd, 0x01, 0x00); ++ ++ if (encoder->norm & V4L2_STD_NTSC) ++ set_subcarrier_freq(sd, 1); ++ ++ adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ ++ if (encoder->norm & V4L2_STD_SECAM) ++ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ ++ else ++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ /*udelay(10);*/ ++ break; ++ ++ case 1: ++ adv7175_write(sd, 0x01, 0x00); ++ ++ if (encoder->norm & V4L2_STD_NTSC) ++ set_subcarrier_freq(sd, 0); ++ ++ adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ ++ adv7175_write(sd, 0x0d, 0x49); ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ /* udelay(10); */ ++ break; ++ ++ case 2: ++ adv7175_write(sd, 0x01, 0x80); ++ ++ if (encoder->norm & V4L2_STD_NTSC) ++ set_subcarrier_freq(sd, 0); ++ ++ adv7175_write(sd, 0x0d, 0x49); ++ adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ adv7175_write(sd, 0x07, TR0MODE); ++ /* udelay(10); */ ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); ++ return -EINVAL; ++ } ++ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); ++ encoder->input = input; ++ return 0; ++} ++ ++static int adv7175_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(adv7175_codes)) ++ return -EINVAL; ++ ++ *code = adv7175_codes[index]; ++ return 0; ++} ++ ++static int adv7175_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ u8 val = adv7175_read(sd, 0x7); ++ ++ if ((val & 0x40) == (1 << 6)) ++ mf->code = V4L2_MBUS_FMT_UYVY8_1X16; ++ else ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ mf->width = 0; ++ mf->height = 0; ++ mf->field = V4L2_FIELD_ANY; ++ ++ return 0; ++} ++ ++static int adv7175_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ u8 val = adv7175_read(sd, 0x7); ++ int ret; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ val &= ~0x40; ++ break; ++ ++ case V4L2_MBUS_FMT_UYVY8_1X16: ++ val |= 0x40; ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, ++ "illegal v4l2_mbus_framefmt code: %d\n", mf->code); ++ return -EINVAL; ++ } ++ ++ ret = adv7175_write(sd, 0x7, val); ++ ++ return ret; ++} ++ ++static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); ++} ++ ++static int adv7175_s_power(struct v4l2_subdev *sd, int on) ++{ ++ if (on) ++ adv7175_write(sd, 0x01, 0x00); ++ else ++ adv7175_write(sd, 0x01, 0x78); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops adv7175_core_ops = { ++ .g_chip_ident = adv7175_g_chip_ident, ++ .init = adv7175_init, ++ .s_power = adv7175_s_power, ++}; ++ ++static const struct v4l2_subdev_video_ops adv7175_video_ops = { ++ .s_std_output = adv7175_s_std_output, ++ .s_routing = adv7175_s_routing, ++ .s_mbus_fmt = adv7175_s_fmt, ++ .g_mbus_fmt = adv7175_g_fmt, ++ .enum_mbus_fmt = adv7175_enum_fmt, ++}; ++ ++static const struct v4l2_subdev_ops adv7175_ops = { ++ .core = &adv7175_core_ops, ++ .video = &adv7175_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int adv7175_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int i; ++ struct adv7175 *encoder; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); ++ if (encoder == NULL) ++ return -ENOMEM; ++ sd = &encoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &adv7175_ops); ++ encoder->norm = V4L2_STD_NTSC; ++ encoder->input = 0; ++ ++ i = adv7175_write_block(sd, init_common, sizeof(init_common)); ++ if (i >= 0) { ++ i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); ++ i = adv7175_write(sd, 0x07, TR0MODE); ++ i = adv7175_read(sd, 0x12); ++ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); ++ } ++ if (i < 0) ++ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); ++ return 0; ++} ++ ++static int adv7175_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_adv7175(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id adv7175_id[] = { ++ { "adv7175", 0 }, ++ { "adv7176", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, adv7175_id); ++ ++static struct i2c_driver adv7175_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7175", ++ }, ++ .probe = adv7175_probe, ++ .remove = adv7175_remove, ++ .id_table = adv7175_id, ++}; ++ ++module_i2c_driver(adv7175_driver); +diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c +new file mode 100644 +index 0000000..45ecf8d +--- /dev/null ++++ b/drivers/media/i2c/adv7180.c +@@ -0,0 +1,667 @@ ++/* ++ * adv7180.c Analog Devices ADV7180 video decoder driver ++ * Copyright (c) 2009 Intel Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ADV7180_INPUT_CONTROL_REG 0x00 ++#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 ++#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 ++#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 ++#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 ++#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 ++#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 ++#define ADV7180_INPUT_CONTROL_PAL60 0x60 ++#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 ++#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 ++#define ADV7180_INPUT_CONTROL_PAL_N 0x90 ++#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 ++#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 ++#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 ++#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 ++#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 ++#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 ++#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f ++ ++#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 ++#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 ++ ++#define ADV7180_AUTODETECT_ENABLE_REG 0x07 ++#define ADV7180_AUTODETECT_DEFAULT 0x7f ++/* Contrast */ ++#define ADV7180_CON_REG 0x08 /*Unsigned */ ++#define ADV7180_CON_MIN 0 ++#define ADV7180_CON_DEF 128 ++#define ADV7180_CON_MAX 255 ++/* Brightness*/ ++#define ADV7180_BRI_REG 0x0a /*Signed */ ++#define ADV7180_BRI_MIN -128 ++#define ADV7180_BRI_DEF 0 ++#define ADV7180_BRI_MAX 127 ++/* Hue */ ++#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ ++#define ADV7180_HUE_MIN -127 ++#define ADV7180_HUE_DEF 0 ++#define ADV7180_HUE_MAX 128 ++ ++#define ADV7180_ADI_CTRL_REG 0x0e ++#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 ++ ++#define ADV7180_PWR_MAN_REG 0x0f ++#define ADV7180_PWR_MAN_ON 0x04 ++#define ADV7180_PWR_MAN_OFF 0x24 ++#define ADV7180_PWR_MAN_RES 0x80 ++ ++#define ADV7180_STATUS1_REG 0x10 ++#define ADV7180_STATUS1_IN_LOCK 0x01 ++#define ADV7180_STATUS1_AUTOD_MASK 0x70 ++#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 ++#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 ++#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 ++#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 ++#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 ++#define ADV7180_STATUS1_AUTOD_SECAM 0x50 ++#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 ++#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 ++ ++#define ADV7180_IDENT_REG 0x11 ++#define ADV7180_ID_7180 0x18 ++ ++#define ADV7180_ICONF1_ADI 0x40 ++#define ADV7180_ICONF1_ACTIVE_LOW 0x01 ++#define ADV7180_ICONF1_PSYNC_ONLY 0x10 ++#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 ++/* Saturation */ ++#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ ++#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ ++#define ADV7180_SAT_MIN 0 ++#define ADV7180_SAT_DEF 128 ++#define ADV7180_SAT_MAX 255 ++ ++#define ADV7180_IRQ1_LOCK 0x01 ++#define ADV7180_IRQ1_UNLOCK 0x02 ++#define ADV7180_ISR1_ADI 0x42 ++#define ADV7180_ICR1_ADI 0x43 ++#define ADV7180_IMR1_ADI 0x44 ++#define ADV7180_IMR2_ADI 0x48 ++#define ADV7180_IRQ3_AD_CHANGE 0x08 ++#define ADV7180_ISR3_ADI 0x4A ++#define ADV7180_ICR3_ADI 0x4B ++#define ADV7180_IMR3_ADI 0x4C ++#define ADV7180_IMR4_ADI 0x50 ++ ++#define ADV7180_NTSC_V_BIT_END_REG 0xE6 ++#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F ++ ++struct adv7180_state { ++ struct v4l2_ctrl_handler ctrl_hdl; ++ struct v4l2_subdev sd; ++ struct work_struct work; ++ struct mutex mutex; /* mutual excl. when accessing chip */ ++ int irq; ++ v4l2_std_id curr_norm; ++ bool autodetect; ++ u8 input; ++}; ++#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ ++ struct adv7180_state, \ ++ ctrl_hdl)->sd) ++ ++static v4l2_std_id adv7180_std_to_v4l2(u8 status1) ++{ ++ switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { ++ case ADV7180_STATUS1_AUTOD_NTSM_M_J: ++ return V4L2_STD_NTSC; ++ case ADV7180_STATUS1_AUTOD_NTSC_4_43: ++ return V4L2_STD_NTSC_443; ++ case ADV7180_STATUS1_AUTOD_PAL_M: ++ return V4L2_STD_PAL_M; ++ case ADV7180_STATUS1_AUTOD_PAL_60: ++ return V4L2_STD_PAL_60; ++ case ADV7180_STATUS1_AUTOD_PAL_B_G: ++ return V4L2_STD_PAL; ++ case ADV7180_STATUS1_AUTOD_SECAM: ++ return V4L2_STD_SECAM; ++ case ADV7180_STATUS1_AUTOD_PAL_COMB: ++ return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; ++ case ADV7180_STATUS1_AUTOD_SECAM_525: ++ return V4L2_STD_SECAM; ++ default: ++ return V4L2_STD_UNKNOWN; ++ } ++} ++ ++static int v4l2_std_to_adv7180(v4l2_std_id std) ++{ ++ if (std == V4L2_STD_PAL_60) ++ return ADV7180_INPUT_CONTROL_PAL60; ++ if (std == V4L2_STD_NTSC_443) ++ return ADV7180_INPUT_CONTROL_NTSC_443; ++ if (std == V4L2_STD_PAL_N) ++ return ADV7180_INPUT_CONTROL_PAL_N; ++ if (std == V4L2_STD_PAL_M) ++ return ADV7180_INPUT_CONTROL_PAL_M; ++ if (std == V4L2_STD_PAL_Nc) ++ return ADV7180_INPUT_CONTROL_PAL_COMB_N; ++ ++ if (std & V4L2_STD_PAL) ++ return ADV7180_INPUT_CONTROL_PAL_BG; ++ if (std & V4L2_STD_NTSC) ++ return ADV7180_INPUT_CONTROL_NTSC_M; ++ if (std & V4L2_STD_SECAM) ++ return ADV7180_INPUT_CONTROL_PAL_SECAM; ++ ++ return -EINVAL; ++} ++ ++static u32 adv7180_status_to_v4l2(u8 status1) ++{ ++ if (!(status1 & ADV7180_STATUS1_IN_LOCK)) ++ return V4L2_IN_ST_NO_SIGNAL; ++ ++ return 0; ++} ++ ++static int __adv7180_status(struct i2c_client *client, u32 *status, ++ v4l2_std_id *std) ++{ ++ int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); ++ ++ if (status1 < 0) ++ return status1; ++ ++ if (status) ++ *status = adv7180_status_to_v4l2(status1); ++ if (std) ++ *std = adv7180_std_to_v4l2(status1); ++ ++ return 0; ++} ++ ++static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7180_state, sd); ++} ++ ++static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ struct adv7180_state *state = to_state(sd); ++ int err = mutex_lock_interruptible(&state->mutex); ++ if (err) ++ return err; ++ ++ /* when we are interrupt driven we know the state */ ++ if (!state->autodetect || state->irq > 0) ++ *std = state->curr_norm; ++ else ++ err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); ++ ++ mutex_unlock(&state->mutex); ++ return err; ++} ++ ++static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, ++ u32 output, u32 config) ++{ ++ struct adv7180_state *state = to_state(sd); ++ int ret = mutex_lock_interruptible(&state->mutex); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (ret) ++ return ret; ++ ++ /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept ++ * all inputs and let the card driver take care of validation ++ */ ++ if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) ++ goto out; ++ ++ ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); ++ ++ if (ret < 0) ++ goto out; ++ ++ ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; ++ ret = i2c_smbus_write_byte_data(client, ++ ADV7180_INPUT_CONTROL_REG, ret | input); ++ state->input = input; ++out: ++ mutex_unlock(&state->mutex); ++ return ret; ++} ++ ++static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ struct adv7180_state *state = to_state(sd); ++ int ret = mutex_lock_interruptible(&state->mutex); ++ if (ret) ++ return ret; ++ ++ ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); ++ mutex_unlock(&state->mutex); ++ return ret; ++} ++ ++static int adv7180_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); ++} ++ ++static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7180_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = mutex_lock_interruptible(&state->mutex); ++ if (ret) ++ return ret; ++ ++ /* all standards -> autodetect */ ++ if (std == V4L2_STD_ALL) { ++ ret = ++ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, ++ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM ++ | state->input); ++ if (ret < 0) ++ goto out; ++ ++ __adv7180_status(client, NULL, &state->curr_norm); ++ state->autodetect = true; ++ } else { ++ ret = v4l2_std_to_adv7180(std); ++ if (ret < 0) ++ goto out; ++ ++ ret = i2c_smbus_write_byte_data(client, ++ ADV7180_INPUT_CONTROL_REG, ++ ret | state->input); ++ if (ret < 0) ++ goto out; ++ ++ state->curr_norm = std; ++ state->autodetect = false; ++ } ++ ret = 0; ++out: ++ mutex_unlock(&state->mutex); ++ return ret; ++} ++ ++static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_adv7180_sd(ctrl); ++ struct adv7180_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = mutex_lock_interruptible(&state->mutex); ++ int val; ++ ++ if (ret) ++ return ret; ++ val = ctrl->val; ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); ++ break; ++ case V4L2_CID_HUE: ++ /*Hue is inverted according to HSL chart */ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); ++ break; ++ case V4L2_CID_CONTRAST: ++ ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); ++ break; ++ case V4L2_CID_SATURATION: ++ /* ++ *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE ++ *Let's not confuse the user, everybody understands saturation ++ */ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, ++ val); ++ if (ret < 0) ++ break; ++ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, ++ val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ mutex_unlock(&state->mutex); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { ++ .s_ctrl = adv7180_s_ctrl, ++}; ++ ++static int adv7180_init_controls(struct adv7180_state *state) ++{ ++ v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); ++ ++ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, ++ ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); ++ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, ++ V4L2_CID_CONTRAST, ADV7180_CON_MIN, ++ ADV7180_CON_MAX, 1, ADV7180_CON_DEF); ++ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, ++ V4L2_CID_SATURATION, ADV7180_SAT_MIN, ++ ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); ++ v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, ++ V4L2_CID_HUE, ADV7180_HUE_MIN, ++ ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); ++ state->sd.ctrl_handler = &state->ctrl_hdl; ++ if (state->ctrl_hdl.error) { ++ int err = state->ctrl_hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->ctrl_hdl); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&state->ctrl_hdl); ++ ++ return 0; ++} ++static void adv7180_exit_controls(struct adv7180_state *state) ++{ ++ v4l2_ctrl_handler_free(&state->ctrl_hdl); ++} ++ ++static const struct v4l2_subdev_video_ops adv7180_video_ops = { ++ .querystd = adv7180_querystd, ++ .g_input_status = adv7180_g_input_status, ++ .s_routing = adv7180_s_routing, ++}; ++ ++static const struct v4l2_subdev_core_ops adv7180_core_ops = { ++ .g_chip_ident = adv7180_g_chip_ident, ++ .s_std = adv7180_s_std, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_ops adv7180_ops = { ++ .core = &adv7180_core_ops, ++ .video = &adv7180_video_ops, ++}; ++ ++static void adv7180_work(struct work_struct *work) ++{ ++ struct adv7180_state *state = container_of(work, struct adv7180_state, ++ work); ++ struct i2c_client *client = v4l2_get_subdevdata(&state->sd); ++ u8 isr3; ++ ++ mutex_lock(&state->mutex); ++ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, ++ ADV7180_ADI_CTRL_IRQ_SPACE); ++ isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); ++ /* clear */ ++ i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); ++ i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); ++ ++ if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) ++ __adv7180_status(client, NULL, &state->curr_norm); ++ mutex_unlock(&state->mutex); ++ ++ enable_irq(state->irq); ++} ++ ++static irqreturn_t adv7180_irq(int irq, void *devid) ++{ ++ struct adv7180_state *state = devid; ++ ++ schedule_work(&state->work); ++ ++ disable_irq_nosync(state->irq); ++ ++ return IRQ_HANDLED; ++} ++ ++static int init_device(struct i2c_client *client, struct adv7180_state *state) ++{ ++ int ret; ++ ++ /* Initialize adv7180 */ ++ /* Enable autodetection */ ++ if (state->autodetect) { ++ ret = ++ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, ++ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM ++ | state->input); ++ if (ret < 0) ++ return ret; ++ ++ ret = ++ i2c_smbus_write_byte_data(client, ++ ADV7180_AUTODETECT_ENABLE_REG, ++ ADV7180_AUTODETECT_DEFAULT); ++ if (ret < 0) ++ return ret; ++ } else { ++ ret = v4l2_std_to_adv7180(state->curr_norm); ++ if (ret < 0) ++ return ret; ++ ++ ret = ++ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, ++ ret | state->input); ++ if (ret < 0) ++ return ret; ++ ++ } ++ /* ITU-R BT.656-4 compatible */ ++ ret = i2c_smbus_write_byte_data(client, ++ ADV7180_EXTENDED_OUTPUT_CONTROL_REG, ++ ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); ++ if (ret < 0) ++ return ret; ++ ++ /* Manually set V bit end position in NTSC mode */ ++ ret = i2c_smbus_write_byte_data(client, ++ ADV7180_NTSC_V_BIT_END_REG, ++ ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); ++ if (ret < 0) ++ return ret; ++ ++ /* read current norm */ ++ __adv7180_status(client, NULL, &state->curr_norm); ++ ++ /* register for interrupts */ ++ if (state->irq > 0) { ++ ret = request_irq(state->irq, adv7180_irq, 0, KBUILD_MODNAME, ++ state); ++ if (ret) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, ++ ADV7180_ADI_CTRL_IRQ_SPACE); ++ if (ret < 0) ++ return ret; ++ ++ /* config the Interrupt pin to be active low */ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, ++ ADV7180_ICONF1_ACTIVE_LOW | ++ ADV7180_ICONF1_PSYNC_ONLY); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); ++ if (ret < 0) ++ return ret; ++ ++ /* enable AD change interrupts interrupts */ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, ++ ADV7180_IRQ3_AD_CHANGE); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, ++ 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static __devinit int adv7180_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7180_state *state; ++ struct v4l2_subdev *sd; ++ int ret; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); ++ if (state == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ state->irq = client->irq; ++ INIT_WORK(&state->work, adv7180_work); ++ mutex_init(&state->mutex); ++ state->autodetect = true; ++ state->input = 0; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &adv7180_ops); ++ ++ ret = adv7180_init_controls(state); ++ if (ret) ++ goto err_unreg_subdev; ++ ret = init_device(client, state); ++ if (ret) ++ goto err_free_ctrl; ++ return 0; ++ ++err_free_ctrl: ++ adv7180_exit_controls(state); ++err_unreg_subdev: ++ mutex_destroy(&state->mutex); ++ v4l2_device_unregister_subdev(sd); ++ kfree(state); ++err: ++ printk(KERN_ERR KBUILD_MODNAME ": Failed to probe: %d\n", ret); ++ return ret; ++} ++ ++static __devexit int adv7180_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7180_state *state = to_state(sd); ++ ++ if (state->irq > 0) { ++ free_irq(client->irq, state); ++ if (cancel_work_sync(&state->work)) { ++ /* ++ * Work was pending, therefore we need to enable ++ * IRQ here to balance the disable_irq() done in the ++ * interrupt handler. ++ */ ++ enable_irq(state->irq); ++ } ++ } ++ ++ mutex_destroy(&state->mutex); ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id adv7180_id[] = { ++ {KBUILD_MODNAME, 0}, ++ {}, ++}; ++ ++#ifdef CONFIG_PM ++static int adv7180_suspend(struct i2c_client *client, pm_message_t state) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, ++ ADV7180_PWR_MAN_OFF); ++ if (ret < 0) ++ return ret; ++ return 0; ++} ++ ++static int adv7180_resume(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7180_state *state = to_state(sd); ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, ++ ADV7180_PWR_MAN_ON); ++ if (ret < 0) ++ return ret; ++ ret = init_device(client, state); ++ if (ret < 0) ++ return ret; ++ return 0; ++} ++#endif ++ ++MODULE_DEVICE_TABLE(i2c, adv7180_id); ++ ++static struct i2c_driver adv7180_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = KBUILD_MODNAME, ++ }, ++ .probe = adv7180_probe, ++ .remove = __devexit_p(adv7180_remove), ++#ifdef CONFIG_PM ++ .suspend = adv7180_suspend, ++ .resume = adv7180_resume, ++#endif ++ .id_table = adv7180_id, ++}; ++ ++module_i2c_driver(adv7180_driver); ++ ++MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); ++MODULE_AUTHOR("Mocean Laboratories"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c +new file mode 100644 +index 0000000..10c3c1d +--- /dev/null ++++ b/drivers/media/i2c/adv7183.c +@@ -0,0 +1,688 @@ ++/* ++ * adv7183.c Analog Devices ADV7183 video decoder driver ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "adv7183_regs.h" ++ ++struct adv7183 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ ++ v4l2_std_id std; /* Current set standard */ ++ u32 input; ++ u32 output; ++ unsigned reset_pin; ++ unsigned oe_pin; ++ struct v4l2_mbus_framefmt fmt; ++}; ++ ++/* EXAMPLES USING 27 MHz CLOCK ++ * Mode 1 CVBS Input (Composite Video on AIN5) ++ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8. ++ */ ++static const unsigned char adv7183_init_regs[] = { ++ ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */ ++ ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */ ++ ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */ ++ ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */ ++ ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */ ++ /* ADI recommended programming sequence */ ++ ADV7183_ADI_CTRL, 0x80, ++ ADV7183_CTI_DNR_CTRL_4, 0x20, ++ 0x52, 0x18, ++ 0x58, 0xED, ++ 0x77, 0xC5, ++ 0x7C, 0x93, ++ 0x7D, 0x00, ++ 0xD0, 0x48, ++ 0xD5, 0xA0, ++ 0xD7, 0xEA, ++ ADV7183_SD_SATURATION_CR, 0x3E, ++ ADV7183_PAL_V_END, 0x3E, ++ ADV7183_PAL_F_TOGGLE, 0x0F, ++ ADV7183_ADI_CTRL, 0x00, ++}; ++ ++static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7183, sd); ++} ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct adv7183, hdl)->sd; ++} ++ ++static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static int adv7183_writeregs(struct v4l2_subdev *sd, ++ const unsigned char *regs, unsigned int num) ++{ ++ unsigned char reg, data; ++ unsigned int cnt = 0; ++ ++ if (num & 0x1) { ++ v4l2_err(sd, "invalid regs array\n"); ++ return -1; ++ } ++ ++ while (cnt < num) { ++ reg = *regs++; ++ data = *regs++; ++ cnt += 2; ++ ++ adv7183_write(sd, reg, data); ++ } ++ return 0; ++} ++ ++static int adv7183_log_status(struct v4l2_subdev *sd) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ v4l2_info(sd, "adv7183: Input control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_IN_CTRL)); ++ v4l2_info(sd, "adv7183: Video selection = 0x%02x\n", ++ adv7183_read(sd, ADV7183_VD_SEL)); ++ v4l2_info(sd, "adv7183: Output control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_OUT_CTRL)); ++ v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_EXT_OUT_CTRL)); ++ v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n", ++ adv7183_read(sd, ADV7183_AUTO_DET_EN)); ++ v4l2_info(sd, "adv7183: Contrast = 0x%02x\n", ++ adv7183_read(sd, ADV7183_CONTRAST)); ++ v4l2_info(sd, "adv7183: Brightness = 0x%02x\n", ++ adv7183_read(sd, ADV7183_BRIGHTNESS)); ++ v4l2_info(sd, "adv7183: Hue = 0x%02x\n", ++ adv7183_read(sd, ADV7183_HUE)); ++ v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n", ++ adv7183_read(sd, ADV7183_DEF_Y)); ++ v4l2_info(sd, "adv7183: Default value C = 0x%02x\n", ++ adv7183_read(sd, ADV7183_DEF_C)); ++ v4l2_info(sd, "adv7183: ADI control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_ADI_CTRL)); ++ v4l2_info(sd, "adv7183: Power Management = 0x%02x\n", ++ adv7183_read(sd, ADV7183_POW_MANAGE)); ++ v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_STATUS_1), ++ adv7183_read(sd, ADV7183_STATUS_2), ++ adv7183_read(sd, ADV7183_STATUS_3)); ++ v4l2_info(sd, "adv7183: Ident = 0x%02x\n", ++ adv7183_read(sd, ADV7183_IDENT)); ++ v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL)); ++ v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n", ++ adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1)); ++ v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL), ++ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2)); ++ v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_COMB_FILT_CTRL)); ++ v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n", ++ adv7183_read(sd, ADV7183_ADI_CTRL_2)); ++ v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_PIX_DELAY_CTRL)); ++ v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_MISC_GAIN_CTRL)); ++ v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_AGC_MODE_CTRL)); ++ v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1), ++ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2)); ++ v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1), ++ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2)); ++ v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1), ++ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2), ++ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3)); ++ v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_HS_POS_CTRL_1), ++ adv7183_read(sd, ADV7183_HS_POS_CTRL_2), ++ adv7183_read(sd, ADV7183_HS_POS_CTRL_3)); ++ v4l2_info(sd, "adv7183: Polarity = 0x%02x\n", ++ adv7183_read(sd, ADV7183_POLARITY)); ++ v4l2_info(sd, "adv7183: ADC control = 0x%02x\n", ++ adv7183_read(sd, ADV7183_ADC_CTRL)); ++ v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_SD_OFFSET_CB), ++ adv7183_read(sd, ADV7183_SD_OFFSET_CR)); ++ v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n", ++ adv7183_read(sd, ADV7183_SD_SATURATION_CB), ++ adv7183_read(sd, ADV7183_SD_SATURATION_CR)); ++ v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n", ++ adv7183_read(sd, ADV7183_DRIVE_STR)); ++ v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name); ++ return 0; ++} ++ ++static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ *std = decoder->std; ++ return 0; ++} ++ ++static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ int reg; ++ ++ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; ++ if (std == V4L2_STD_PAL_60) ++ reg |= 0x60; ++ else if (std == V4L2_STD_NTSC_443) ++ reg |= 0x70; ++ else if (std == V4L2_STD_PAL_N) ++ reg |= 0x90; ++ else if (std == V4L2_STD_PAL_M) ++ reg |= 0xA0; ++ else if (std == V4L2_STD_PAL_Nc) ++ reg |= 0xC0; ++ else if (std & V4L2_STD_PAL) ++ reg |= 0x80; ++ else if (std & V4L2_STD_NTSC) ++ reg |= 0x50; ++ else if (std & V4L2_STD_SECAM) ++ reg |= 0xE0; ++ else ++ return -EINVAL; ++ adv7183_write(sd, ADV7183_IN_CTRL, reg); ++ ++ decoder->std = std; ++ ++ return 0; ++} ++ ++static int adv7183_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ int reg; ++ ++ reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80; ++ adv7183_write(sd, ADV7183_POW_MANAGE, reg); ++ /* wait 5ms before any further i2c writes are performed */ ++ usleep_range(5000, 10000); ++ return 0; ++} ++ ++static int adv7183_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ int reg; ++ ++ if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT)) ++ return -EINVAL; ++ ++ if (input != decoder->input) { ++ decoder->input = input; ++ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0; ++ switch (input) { ++ case ADV7183_COMPOSITE1: ++ reg |= 0x1; ++ break; ++ case ADV7183_COMPOSITE2: ++ reg |= 0x2; ++ break; ++ case ADV7183_COMPOSITE3: ++ reg |= 0x3; ++ break; ++ case ADV7183_COMPOSITE4: ++ reg |= 0x4; ++ break; ++ case ADV7183_COMPOSITE5: ++ reg |= 0x5; ++ break; ++ case ADV7183_COMPOSITE6: ++ reg |= 0xB; ++ break; ++ case ADV7183_COMPOSITE7: ++ reg |= 0xC; ++ break; ++ case ADV7183_COMPOSITE8: ++ reg |= 0xD; ++ break; ++ case ADV7183_COMPOSITE9: ++ reg |= 0xE; ++ break; ++ case ADV7183_COMPOSITE10: ++ reg |= 0xF; ++ break; ++ case ADV7183_SVIDEO0: ++ reg |= 0x6; ++ break; ++ case ADV7183_SVIDEO1: ++ reg |= 0x7; ++ break; ++ case ADV7183_SVIDEO2: ++ reg |= 0x8; ++ break; ++ case ADV7183_COMPONENT0: ++ reg |= 0x9; ++ break; ++ case ADV7183_COMPONENT1: ++ reg |= 0xA; ++ break; ++ default: ++ break; ++ } ++ adv7183_write(sd, ADV7183_IN_CTRL, reg); ++ } ++ ++ if (output != decoder->output) { ++ decoder->output = output; ++ reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0; ++ switch (output) { ++ case ADV7183_16BIT_OUT: ++ reg |= 0x9; ++ break; ++ default: ++ reg |= 0xC; ++ break; ++ } ++ adv7183_write(sd, ADV7183_OUT_CTRL, reg); ++ } ++ ++ return 0; ++} ++ ++static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ int val = ctrl->val; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ if (val < 0) ++ val = 127 - val; ++ adv7183_write(sd, ADV7183_BRIGHTNESS, val); ++ break; ++ case V4L2_CID_CONTRAST: ++ adv7183_write(sd, ADV7183_CONTRAST, val); ++ break; ++ case V4L2_CID_SATURATION: ++ adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8); ++ adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF)); ++ break; ++ case V4L2_CID_HUE: ++ adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8); ++ adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ int reg; ++ ++ /* enable autodetection block */ ++ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF; ++ adv7183_write(sd, ADV7183_IN_CTRL, reg); ++ ++ /* wait autodetection switch */ ++ mdelay(10); ++ ++ /* get autodetection result */ ++ reg = adv7183_read(sd, ADV7183_STATUS_1); ++ switch ((reg >> 0x4) & 0x7) { ++ case 0: ++ *std = V4L2_STD_NTSC; ++ break; ++ case 1: ++ *std = V4L2_STD_NTSC_443; ++ break; ++ case 2: ++ *std = V4L2_STD_PAL_M; ++ break; ++ case 3: ++ *std = V4L2_STD_PAL_60; ++ break; ++ case 4: ++ *std = V4L2_STD_PAL; ++ break; ++ case 5: ++ *std = V4L2_STD_SECAM; ++ break; ++ case 6: ++ *std = V4L2_STD_PAL_Nc; ++ break; ++ case 7: ++ *std = V4L2_STD_SECAM; ++ break; ++ default: ++ *std = V4L2_STD_UNKNOWN; ++ break; ++ } ++ ++ /* after std detection, write back user set std */ ++ adv7183_s_std(sd, decoder->std); ++ return 0; ++} ++ ++static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ int reg; ++ ++ *status = V4L2_IN_ST_NO_SIGNAL; ++ reg = adv7183_read(sd, ADV7183_STATUS_1); ++ if (reg < 0) ++ return reg; ++ if (reg & 0x1) ++ *status = 0; ++ return 0; ++} ++ ++static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index > 0) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_UYVY8_2X8; ++ return 0; ++} ++ ++static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ if (decoder->std & V4L2_STD_525_60) { ++ fmt->field = V4L2_FIELD_SEQ_TB; ++ fmt->width = 720; ++ fmt->height = 480; ++ } else { ++ fmt->field = V4L2_FIELD_SEQ_BT; ++ fmt->width = 720; ++ fmt->height = 576; ++ } ++ return 0; ++} ++ ++static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ adv7183_try_mbus_fmt(sd, fmt); ++ decoder->fmt = *fmt; ++ return 0; ++} ++ ++static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ *fmt = decoder->fmt; ++ return 0; ++} ++ ++static int adv7183_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ if (enable) ++ gpio_direction_output(decoder->oe_pin, 0); ++ else ++ gpio_direction_output(decoder->oe_pin, 1); ++ udelay(1); ++ return 0; ++} ++ ++static int adv7183_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ int rev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* 0x11 for adv7183, 0x13 for adv7183b */ ++ rev = adv7183_read(sd, ADV7183_IDENT); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = adv7183_read(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static const struct v4l2_ctrl_ops adv7183_ctrl_ops = { ++ .s_ctrl = adv7183_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops adv7183_core_ops = { ++ .log_status = adv7183_log_status, ++ .g_std = adv7183_g_std, ++ .s_std = adv7183_s_std, ++ .reset = adv7183_reset, ++ .g_chip_ident = adv7183_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = adv7183_g_register, ++ .s_register = adv7183_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops adv7183_video_ops = { ++ .s_routing = adv7183_s_routing, ++ .querystd = adv7183_querystd, ++ .g_input_status = adv7183_g_input_status, ++ .enum_mbus_fmt = adv7183_enum_mbus_fmt, ++ .try_mbus_fmt = adv7183_try_mbus_fmt, ++ .s_mbus_fmt = adv7183_s_mbus_fmt, ++ .g_mbus_fmt = adv7183_g_mbus_fmt, ++ .s_stream = adv7183_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops adv7183_ops = { ++ .core = &adv7183_core_ops, ++ .video = &adv7183_video_ops, ++}; ++ ++static int adv7183_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7183 *decoder; ++ struct v4l2_subdev *sd; ++ struct v4l2_ctrl_handler *hdl; ++ int ret; ++ struct v4l2_mbus_framefmt fmt; ++ const unsigned *pin_array; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ pin_array = client->dev.platform_data; ++ if (pin_array == NULL) ++ return -EINVAL; ++ ++ decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL); ++ if (decoder == NULL) ++ return -ENOMEM; ++ ++ decoder->reset_pin = pin_array[0]; ++ decoder->oe_pin = pin_array[1]; ++ ++ if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) { ++ v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin); ++ ret = -EBUSY; ++ goto err_free_decoder; ++ } ++ ++ if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) { ++ v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin); ++ ret = -EBUSY; ++ goto err_free_reset; ++ } ++ ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &adv7183_ops); ++ ++ hdl = &decoder->hdl; ++ v4l2_ctrl_handler_init(hdl, 4); ++ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80); ++ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080); ++ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops, ++ V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080); ++ /* hook the control handler into the driver */ ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ ret = hdl->error; ++ ++ v4l2_ctrl_handler_free(hdl); ++ goto err_free_oe; ++ } ++ ++ /* v4l2 doesn't support an autodetect standard, pick PAL as default */ ++ decoder->std = V4L2_STD_PAL; ++ decoder->input = ADV7183_COMPOSITE4; ++ decoder->output = ADV7183_8BIT_OUT; ++ ++ gpio_direction_output(decoder->oe_pin, 1); ++ /* reset chip */ ++ gpio_direction_output(decoder->reset_pin, 0); ++ /* reset pulse width at least 5ms */ ++ mdelay(10); ++ gpio_direction_output(decoder->reset_pin, 1); ++ /* wait 5ms before any further i2c writes are performed */ ++ mdelay(5); ++ ++ adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs)); ++ adv7183_s_std(sd, decoder->std); ++ fmt.width = 720; ++ fmt.height = 576; ++ adv7183_s_mbus_fmt(sd, &fmt); ++ ++ /* initialize the hardware to the default control values */ ++ ret = v4l2_ctrl_handler_setup(hdl); ++ if (ret) { ++ v4l2_ctrl_handler_free(hdl); ++ goto err_free_oe; ++ } ++ ++ return 0; ++err_free_oe: ++ gpio_free(decoder->oe_pin); ++err_free_reset: ++ gpio_free(decoder->reset_pin); ++err_free_decoder: ++ kfree(decoder); ++ return ret; ++} ++ ++static int adv7183_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7183 *decoder = to_adv7183(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ gpio_free(decoder->oe_pin); ++ gpio_free(decoder->reset_pin); ++ kfree(decoder); ++ return 0; ++} ++ ++static const struct i2c_device_id adv7183_id[] = { ++ {"adv7183", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, adv7183_id); ++ ++static struct i2c_driver adv7183_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7183", ++ }, ++ .probe = adv7183_probe, ++ .remove = __devexit_p(adv7183_remove), ++ .id_table = adv7183_id, ++}; ++ ++module_i2c_driver(adv7183_driver); ++ ++MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver"); ++MODULE_AUTHOR("Scott Jiang "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/adv7183_regs.h b/drivers/media/i2c/adv7183_regs.h +new file mode 100644 +index 0000000..4a5b7d2 +--- /dev/null ++++ b/drivers/media/i2c/adv7183_regs.h +@@ -0,0 +1,107 @@ ++/* ++ * adv7183 - Analog Devices ADV7183 video decoder registers ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _ADV7183_REGS_H_ ++#define _ADV7183_REGS_H_ ++ ++#define ADV7183_IN_CTRL 0x00 /* Input control */ ++#define ADV7183_VD_SEL 0x01 /* Video selection */ ++#define ADV7183_OUT_CTRL 0x03 /* Output control */ ++#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */ ++#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */ ++#define ADV7183_CONTRAST 0x08 /* Contrast */ ++#define ADV7183_BRIGHTNESS 0x0A /* Brightness */ ++#define ADV7183_HUE 0x0B /* Hue */ ++#define ADV7183_DEF_Y 0x0C /* Default value Y */ ++#define ADV7183_DEF_C 0x0D /* Default value C */ ++#define ADV7183_ADI_CTRL 0x0E /* ADI control */ ++#define ADV7183_POW_MANAGE 0x0F /* Power Management */ ++#define ADV7183_STATUS_1 0x10 /* Status 1 */ ++#define ADV7183_IDENT 0x11 /* Ident */ ++#define ADV7183_STATUS_2 0x12 /* Status 2 */ ++#define ADV7183_STATUS_3 0x13 /* Status 3 */ ++#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */ ++#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */ ++#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */ ++#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */ ++#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */ ++#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */ ++#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */ ++#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */ ++#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */ ++#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */ ++#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */ ++#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */ ++#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */ ++#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */ ++#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */ ++#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */ ++#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */ ++#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */ ++#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */ ++#define ADV7183_POLARITY 0x37 /* Polarity */ ++#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */ ++#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */ ++#define ADV7183_ADC_CTRL 0x3A /* ADC control */ ++#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */ ++#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */ ++#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */ ++#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */ ++#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */ ++#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */ ++#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */ ++#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */ ++#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */ ++#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */ ++#define ADV7183_LOCK_CNT 0x51 /* Lock count */ ++#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */ ++#define ADV7183_VBI_INFO 0x90 /* VBI info */ ++#define ADV7183_WSS_1 0x91 /* WSS 1 */ ++#define ADV7183_WSS_2 0x92 /* WSS 2 */ ++#define ADV7183_EDTV_1 0x93 /* EDTV 1 */ ++#define ADV7183_EDTV_2 0x94 /* EDTV 2 */ ++#define ADV7183_EDTV_3 0x95 /* EDTV 3 */ ++#define ADV7183_CGMS_1 0x96 /* CGMS 1 */ ++#define ADV7183_CGMS_2 0x97 /* CGMS 2 */ ++#define ADV7183_CGMS_3 0x98 /* CGMS 3 */ ++#define ADV7183_CCAP_1 0x99 /* CCAP 1 */ ++#define ADV7183_CCAP_2 0x9A /* CCAP 2 */ ++#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */ ++#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */ ++#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */ ++#define ADV7183_CRC_EN 0xB2 /* CRC enable */ ++#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */ ++#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */ ++#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */ ++#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */ ++#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */ ++#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */ ++#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */ ++#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */ ++#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */ ++#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */ ++#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */ ++#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */ ++#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */ ++#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */ ++#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */ ++#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */ ++#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */ ++ ++#endif +diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c +new file mode 100644 +index 0000000..2b5aa67 +--- /dev/null ++++ b/drivers/media/i2c/adv7343.c +@@ -0,0 +1,476 @@ ++/* ++ * adv7343 - ADV7343 Video Encoder Driver ++ * ++ * The encoder hardware does not support SECAM. ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "adv7343_regs.h" ++ ++MODULE_DESCRIPTION("ADV7343 video encoder driver"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++ ++struct adv7343_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ u8 reg00; ++ u8 reg01; ++ u8 reg02; ++ u8 reg35; ++ u8 reg80; ++ u8 reg82; ++ u32 output; ++ v4l2_std_id std; ++}; ++ ++static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7343_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct adv7343_state, hdl)->sd; ++} ++ ++static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static const u8 adv7343_init_reg_val[] = { ++ ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, ++ ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, ++ ++ ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, ++ ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, ++ ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, ++ ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, ++ ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, ++ ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, ++ ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, ++ ++ ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, ++ ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, ++ ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, ++ ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, ++ ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, ++ ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, ++ ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, ++ ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, ++ ++ ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, ++ ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, ++ ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, ++}; ++ ++/* ++ * 2^32 ++ * FSC(reg) = FSC (HZ) * -------- ++ * 27000000 ++ */ ++static const struct adv7343_std_info stdinfo[] = { ++ { ++ /* FSC(Hz) = 3,579,545.45 Hz */ ++ SD_STD_NTSC, 569408542, V4L2_STD_NTSC, ++ }, { ++ /* FSC(Hz) = 3,575,611.00 Hz */ ++ SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, ++ }, { ++ /* FSC(Hz) = 3,582,056.00 */ ++ SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, ++ }, ++}; ++ ++static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7343_state *state = to_state(sd); ++ struct adv7343_std_info *std_info; ++ int num_std; ++ char *fsc_ptr; ++ u8 reg, val; ++ int err = 0; ++ int i = 0; ++ ++ std_info = (struct adv7343_std_info *)stdinfo; ++ num_std = ARRAY_SIZE(stdinfo); ++ ++ for (i = 0; i < num_std; i++) { ++ if (std_info[i].stdid & std) ++ break; ++ } ++ ++ if (i == num_std) { ++ v4l2_dbg(1, debug, sd, ++ "Invalid std or std is not supported: %llx\n", ++ (unsigned long long)std); ++ return -EINVAL; ++ } ++ ++ /* Set the standard */ ++ val = state->reg80 & (~(SD_STD_MASK)); ++ val |= std_info[i].standard_val3; ++ err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg80 = val; ++ ++ /* Configure the input mode register */ ++ val = state->reg01 & (~((u8) INPUT_MODE_MASK)); ++ val |= SD_INPUT_MODE; ++ err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg01 = val; ++ ++ /* Program the sub carrier frequency registers */ ++ fsc_ptr = (unsigned char *)&std_info[i].fsc_val; ++ reg = ADV7343_FSC_REG0; ++ for (i = 0; i < 4; i++, reg++, fsc_ptr++) { ++ err = adv7343_write(sd, reg, *fsc_ptr); ++ if (err < 0) ++ goto setstd_exit; ++ } ++ ++ val = state->reg80; ++ ++ /* Filter settings */ ++ if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) ++ val &= 0x03; ++ else if (std & ~V4L2_STD_SECAM) ++ val |= 0x04; ++ ++ err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg80 = val; ++ ++setstd_exit: ++ if (err != 0) ++ v4l2_err(sd, "Error setting std, write failed\n"); ++ ++ return err; ++} ++ ++static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) ++{ ++ struct adv7343_state *state = to_state(sd); ++ unsigned char val; ++ int err = 0; ++ ++ if (output_type > ADV7343_SVIDEO_ID) { ++ v4l2_dbg(1, debug, sd, ++ "Invalid output type or output type not supported:%d\n", ++ output_type); ++ return -EINVAL; ++ } ++ ++ /* Enable Appropriate DAC */ ++ val = state->reg00 & 0x03; ++ ++ if (output_type == ADV7343_COMPOSITE_ID) ++ val |= ADV7343_COMPOSITE_POWER_VALUE; ++ else if (output_type == ADV7343_COMPONENT_ID) ++ val |= ADV7343_COMPONENT_POWER_VALUE; ++ else ++ val |= ADV7343_SVIDEO_POWER_VALUE; ++ ++ err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg00 = val; ++ ++ /* Enable YUV output */ ++ val = state->reg02 | YUV_OUTPUT_SELECT; ++ err = adv7343_write(sd, ADV7343_MODE_REG0, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg02 = val; ++ ++ /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ ++ val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); ++ err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg82 = val; ++ ++ /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to ++ * zero */ ++ val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); ++ err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg35 = val; ++ ++setoutput_exit: ++ if (err != 0) ++ v4l2_err(sd, "Error setting output, write failed\n"); ++ ++ return err; ++} ++ ++static int adv7343_log_status(struct v4l2_subdev *sd) ++{ ++ struct adv7343_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); ++ v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : ++ ((state->output == 1) ? "Component" : "S-Video")); ++ return 0; ++} ++ ++static int adv7343_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, ++ ctrl->val); ++ ++ case V4L2_CID_HUE: ++ return adv7343_write(sd, ADV7343_SD_HUE_REG, ctrl->val); ++ ++ case V4L2_CID_GAIN: ++ return adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, ctrl->val); ++ } ++ return -EINVAL; ++} ++ ++static int adv7343_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); ++} ++ ++static const struct v4l2_ctrl_ops adv7343_ctrl_ops = { ++ .s_ctrl = adv7343_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops adv7343_core_ops = { ++ .log_status = adv7343_log_status, ++ .g_chip_ident = adv7343_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7343_state *state = to_state(sd); ++ int err = 0; ++ ++ if (state->std == std) ++ return 0; ++ ++ err = adv7343_setstd(sd, std); ++ if (!err) ++ state->std = std; ++ ++ return err; ++} ++ ++static int adv7343_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7343_state *state = to_state(sd); ++ int err = 0; ++ ++ if (state->output == output) ++ return 0; ++ ++ err = adv7343_setoutput(sd, output); ++ if (!err) ++ state->output = output; ++ ++ return err; ++} ++ ++static const struct v4l2_subdev_video_ops adv7343_video_ops = { ++ .s_std_output = adv7343_s_std_output, ++ .s_routing = adv7343_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops adv7343_ops = { ++ .core = &adv7343_core_ops, ++ .video = &adv7343_video_ops, ++}; ++ ++static int adv7343_initialize(struct v4l2_subdev *sd) ++{ ++ struct adv7343_state *state = to_state(sd); ++ int err = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { ++ ++ err = adv7343_write(sd, adv7343_init_reg_val[i], ++ adv7343_init_reg_val[i+1]); ++ if (err) { ++ v4l2_err(sd, "Error initializing\n"); ++ return err; ++ } ++ } ++ ++ /* Configure for default video standard */ ++ err = adv7343_setoutput(sd, state->output); ++ if (err < 0) { ++ v4l2_err(sd, "Error setting output during init\n"); ++ return -EINVAL; ++ } ++ ++ err = adv7343_setstd(sd, state->std); ++ if (err < 0) { ++ v4l2_err(sd, "Error setting std during init\n"); ++ return -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int adv7343_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7343_state *state; ++ int err; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ state->reg00 = 0x80; ++ state->reg01 = 0x00; ++ state->reg02 = 0x20; ++ state->reg35 = 0x00; ++ state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; ++ state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; ++ ++ state->output = ADV7343_COMPOSITE_ID; ++ state->std = V4L2_STD_NTSC; ++ ++ v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); ++ ++ v4l2_ctrl_handler_init(&state->hdl, 2); ++ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, ADV7343_BRIGHTNESS_MIN, ++ ADV7343_BRIGHTNESS_MAX, 1, ++ ADV7343_BRIGHTNESS_DEF); ++ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, ++ V4L2_CID_HUE, ADV7343_HUE_MIN, ++ ADV7343_HUE_MAX, 1, ++ ADV7343_HUE_DEF); ++ v4l2_ctrl_new_std(&state->hdl, &adv7343_ctrl_ops, ++ V4L2_CID_GAIN, ADV7343_GAIN_MIN, ++ ADV7343_GAIN_MAX, 1, ++ ADV7343_GAIN_DEF); ++ state->sd.ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&state->hdl); ++ ++ err = adv7343_initialize(&state->sd); ++ if (err) { ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ } ++ return err; ++} ++ ++static int adv7343_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7343_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id adv7343_id[] = { ++ {"adv7343", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, adv7343_id); ++ ++static struct i2c_driver adv7343_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7343", ++ }, ++ .probe = adv7343_probe, ++ .remove = adv7343_remove, ++ .id_table = adv7343_id, ++}; ++ ++module_i2c_driver(adv7343_driver); +diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h +new file mode 100644 +index 0000000..4466067 +--- /dev/null ++++ b/drivers/media/i2c/adv7343_regs.h +@@ -0,0 +1,181 @@ ++/* ++ * ADV7343 encoder related structure and register definitions ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef ADV7343_REG_H ++#define ADV7343_REGS_H ++ ++struct adv7343_std_info { ++ u32 standard_val3; ++ u32 fsc_val; ++ v4l2_std_id stdid; ++}; ++ ++/* Register offset macros */ ++#define ADV7343_POWER_MODE_REG (0x00) ++#define ADV7343_MODE_SELECT_REG (0x01) ++#define ADV7343_MODE_REG0 (0x02) ++ ++#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) ++ ++#define ADV7343_SOFT_RESET (0x17) ++ ++#define ADV7343_HD_MODE_REG1 (0x30) ++#define ADV7343_HD_MODE_REG2 (0x31) ++#define ADV7343_HD_MODE_REG3 (0x32) ++#define ADV7343_HD_MODE_REG4 (0x33) ++#define ADV7343_HD_MODE_REG5 (0x34) ++#define ADV7343_HD_MODE_REG6 (0x35) ++ ++#define ADV7343_HD_MODE_REG7 (0x39) ++ ++#define ADV7343_SD_MODE_REG1 (0x80) ++#define ADV7343_SD_MODE_REG2 (0x82) ++#define ADV7343_SD_MODE_REG3 (0x83) ++#define ADV7343_SD_MODE_REG4 (0x84) ++#define ADV7343_SD_MODE_REG5 (0x86) ++#define ADV7343_SD_MODE_REG6 (0x87) ++#define ADV7343_SD_MODE_REG7 (0x88) ++#define ADV7343_SD_MODE_REG8 (0x89) ++ ++#define ADV7343_FSC_REG0 (0x8C) ++#define ADV7343_FSC_REG1 (0x8D) ++#define ADV7343_FSC_REG2 (0x8E) ++#define ADV7343_FSC_REG3 (0x8F) ++ ++#define ADV7343_SD_CGMS_WSS0 (0x99) ++ ++#define ADV7343_SD_HUE_REG (0xA0) ++#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) ++ ++/* Default values for the registers */ ++#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) ++#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default ++ 720p EAVSAV code*/ ++#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data ++ valid */ ++#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ ++#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ ++#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) ++#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) ++#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) ++#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) ++#define ADV7343_SOFT_RESET_DEFAULT (0x02) ++#define ADV7343_COMPOSITE_POWER_VALUE (0x80) ++#define ADV7343_COMPONENT_POWER_VALUE (0x1C) ++#define ADV7343_SVIDEO_POWER_VALUE (0x60) ++#define ADV7343_SD_HUE_REG_DEFAULT (127) ++#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) ++ ++#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) ++ ++#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) ++#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) ++#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) ++#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) ++#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) ++#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) ++#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) ++#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) ++ ++/* Bit masks for Mode Select Register */ ++#define INPUT_MODE_MASK (0x70) ++#define SD_INPUT_MODE (0x00) ++#define HD_720P_INPUT_MODE (0x10) ++#define HD_1080I_INPUT_MODE (0x10) ++ ++/* Bit masks for Mode Register 0 */ ++#define TEST_PATTERN_BLACK_BAR_EN (0x04) ++#define YUV_OUTPUT_SELECT (0x20) ++#define RGB_OUTPUT_SELECT (0xDF) ++ ++/* Bit masks for DAC output levels */ ++#define DAC_OUTPUT_LEVEL_MASK (0xFF) ++ ++/* Bit masks for soft reset register */ ++#define SOFT_RESET (0x02) ++ ++/* Bit masks for HD Mode Register 1 */ ++#define OUTPUT_STD_MASK (0x03) ++#define OUTPUT_STD_SHIFT (0) ++#define OUTPUT_STD_EIA0_2 (0x00) ++#define OUTPUT_STD_EIA0_1 (0x01) ++#define OUTPUT_STD_FULL (0x02) ++#define EMBEDDED_SYNC (0x04) ++#define EXTERNAL_SYNC (0xFB) ++#define STD_MODE_SHIFT (3) ++#define STD_MODE_MASK (0x1F) ++#define STD_MODE_720P (0x05) ++#define STD_MODE_720P_25 (0x08) ++#define STD_MODE_720P_30 (0x07) ++#define STD_MODE_720P_50 (0x06) ++#define STD_MODE_1080I (0x0D) ++#define STD_MODE_1080I_25fps (0x0E) ++#define STD_MODE_1080P_24 (0x12) ++#define STD_MODE_1080P_25 (0x10) ++#define STD_MODE_1080P_30 (0x0F) ++#define STD_MODE_525P (0x00) ++#define STD_MODE_625P (0x03) ++ ++/* Bit masks for SD Mode Register 1 */ ++#define SD_STD_MASK (0x03) ++#define SD_STD_NTSC (0x00) ++#define SD_STD_PAL_BDGHI (0x01) ++#define SD_STD_PAL_M (0x02) ++#define SD_STD_PAL_N (0x03) ++#define SD_LUMA_FLTR_MASK (0x7) ++#define SD_LUMA_FLTR_SHIFT (0x2) ++#define SD_CHROMA_FLTR_MASK (0x7) ++#define SD_CHROMA_FLTR_SHIFT (0x5) ++ ++/* Bit masks for SD Mode Register 2 */ ++#define SD_PBPR_SSAF_EN (0x01) ++#define SD_PBPR_SSAF_DI (0xFE) ++#define SD_DAC_1_DI (0xFD) ++#define SD_DAC_2_DI (0xFB) ++#define SD_PEDESTAL_EN (0x08) ++#define SD_PEDESTAL_DI (0xF7) ++#define SD_SQUARE_PIXEL_EN (0x10) ++#define SD_SQUARE_PIXEL_DI (0xEF) ++#define SD_PIXEL_DATA_VALID (0x40) ++#define SD_ACTIVE_EDGE_EN (0x80) ++#define SD_ACTIVE_EDGE_DI (0x7F) ++ ++/* Bit masks for HD Mode Register 6 */ ++#define HD_RGB_INPUT_EN (0x02) ++#define HD_RGB_INPUT_DI (0xFD) ++#define HD_PBPR_SYNC_EN (0x04) ++#define HD_PBPR_SYNC_DI (0xFB) ++#define HD_DAC_SWAP_EN (0x08) ++#define HD_DAC_SWAP_DI (0xF7) ++#define HD_GAMMA_CURVE_A (0xEF) ++#define HD_GAMMA_CURVE_B (0x10) ++#define HD_GAMMA_EN (0x20) ++#define HD_GAMMA_DI (0xDF) ++#define HD_ADPT_FLTR_MODEB (0x40) ++#define HD_ADPT_FLTR_MODEA (0xBF) ++#define HD_ADPT_FLTR_EN (0x80) ++#define HD_ADPT_FLTR_DI (0x7F) ++ ++#define ADV7343_BRIGHTNESS_MAX (127) ++#define ADV7343_BRIGHTNESS_MIN (0) ++#define ADV7343_BRIGHTNESS_DEF (3) ++#define ADV7343_HUE_MAX (255) ++#define ADV7343_HUE_MIN (0) ++#define ADV7343_HUE_DEF (127) ++#define ADV7343_GAIN_MAX (64) ++#define ADV7343_GAIN_MIN (-64) ++#define ADV7343_GAIN_DEF (0) ++ ++#endif +diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c +new file mode 100644 +index 0000000..3dc6098 +--- /dev/null ++++ b/drivers/media/i2c/adv7393.c +@@ -0,0 +1,487 @@ ++/* ++ * adv7393 - ADV7393 Video Encoder Driver ++ * ++ * The encoder hardware does not support SECAM. ++ * ++ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ ++ * Benoît Thébaudeau ++ * ++ * Based on ADV7343 driver, ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "adv7393_regs.h" ++ ++MODULE_DESCRIPTION("ADV7393 video encoder driver"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++module_param(debug, bool, 0644); ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++ ++struct adv7393_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ u8 reg00; ++ u8 reg01; ++ u8 reg02; ++ u8 reg35; ++ u8 reg80; ++ u8 reg82; ++ u32 output; ++ v4l2_std_id std; ++}; ++ ++static inline struct adv7393_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7393_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct adv7393_state, hdl)->sd; ++} ++ ++static inline int adv7393_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static const u8 adv7393_init_reg_val[] = { ++ ADV7393_SOFT_RESET, ADV7393_SOFT_RESET_DEFAULT, ++ ADV7393_POWER_MODE_REG, ADV7393_POWER_MODE_REG_DEFAULT, ++ ++ ADV7393_HD_MODE_REG1, ADV7393_HD_MODE_REG1_DEFAULT, ++ ADV7393_HD_MODE_REG2, ADV7393_HD_MODE_REG2_DEFAULT, ++ ADV7393_HD_MODE_REG3, ADV7393_HD_MODE_REG3_DEFAULT, ++ ADV7393_HD_MODE_REG4, ADV7393_HD_MODE_REG4_DEFAULT, ++ ADV7393_HD_MODE_REG5, ADV7393_HD_MODE_REG5_DEFAULT, ++ ADV7393_HD_MODE_REG6, ADV7393_HD_MODE_REG6_DEFAULT, ++ ADV7393_HD_MODE_REG7, ADV7393_HD_MODE_REG7_DEFAULT, ++ ++ ADV7393_SD_MODE_REG1, ADV7393_SD_MODE_REG1_DEFAULT, ++ ADV7393_SD_MODE_REG2, ADV7393_SD_MODE_REG2_DEFAULT, ++ ADV7393_SD_MODE_REG3, ADV7393_SD_MODE_REG3_DEFAULT, ++ ADV7393_SD_MODE_REG4, ADV7393_SD_MODE_REG4_DEFAULT, ++ ADV7393_SD_MODE_REG5, ADV7393_SD_MODE_REG5_DEFAULT, ++ ADV7393_SD_MODE_REG6, ADV7393_SD_MODE_REG6_DEFAULT, ++ ADV7393_SD_MODE_REG7, ADV7393_SD_MODE_REG7_DEFAULT, ++ ADV7393_SD_MODE_REG8, ADV7393_SD_MODE_REG8_DEFAULT, ++ ++ ADV7393_SD_TIMING_REG0, ADV7393_SD_TIMING_REG0_DEFAULT, ++ ++ ADV7393_SD_HUE_ADJUST, ADV7393_SD_HUE_ADJUST_DEFAULT, ++ ADV7393_SD_CGMS_WSS0, ADV7393_SD_CGMS_WSS0_DEFAULT, ++ ADV7393_SD_BRIGHTNESS_WSS, ADV7393_SD_BRIGHTNESS_WSS_DEFAULT, ++}; ++ ++/* ++ * 2^32 ++ * FSC(reg) = FSC (HZ) * -------- ++ * 27000000 ++ */ ++static const struct adv7393_std_info stdinfo[] = { ++ { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, ++ }, { ++ /* FSC(Hz) = 3,579,545.45 Hz */ ++ SD_STD_NTSC, 569408542, V4L2_STD_NTSC, ++ }, { ++ /* FSC(Hz) = 3,575,611.00 Hz */ ++ SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, ++ }, { ++ /* FSC(Hz) = 3,582,056.00 Hz */ ++ SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, ++ }, { ++ /* FSC(Hz) = 4,433,618.75 Hz */ ++ SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, ++ }, ++}; ++ ++static int adv7393_setstd(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7393_state *state = to_state(sd); ++ const struct adv7393_std_info *std_info; ++ int num_std; ++ u8 reg; ++ u32 val; ++ int err = 0; ++ int i; ++ ++ num_std = ARRAY_SIZE(stdinfo); ++ ++ for (i = 0; i < num_std; i++) { ++ if (stdinfo[i].stdid & std) ++ break; ++ } ++ ++ if (i == num_std) { ++ v4l2_dbg(1, debug, sd, ++ "Invalid std or std is not supported: %llx\n", ++ (unsigned long long)std); ++ return -EINVAL; ++ } ++ ++ std_info = &stdinfo[i]; ++ ++ /* Set the standard */ ++ val = state->reg80 & ~SD_STD_MASK; ++ val |= std_info->standard_val3; ++ err = adv7393_write(sd, ADV7393_SD_MODE_REG1, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg80 = val; ++ ++ /* Configure the input mode register */ ++ val = state->reg01 & ~INPUT_MODE_MASK; ++ val |= SD_INPUT_MODE; ++ err = adv7393_write(sd, ADV7393_MODE_SELECT_REG, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg01 = val; ++ ++ /* Program the sub carrier frequency registers */ ++ val = std_info->fsc_val; ++ for (reg = ADV7393_FSC_REG0; reg <= ADV7393_FSC_REG3; reg++) { ++ err = adv7393_write(sd, reg, val); ++ if (err < 0) ++ goto setstd_exit; ++ val >>= 8; ++ } ++ ++ val = state->reg82; ++ ++ /* Pedestal settings */ ++ if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) ++ val |= SD_PEDESTAL_EN; ++ else ++ val &= SD_PEDESTAL_DI; ++ ++ err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); ++ if (err < 0) ++ goto setstd_exit; ++ ++ state->reg82 = val; ++ ++setstd_exit: ++ if (err != 0) ++ v4l2_err(sd, "Error setting std, write failed\n"); ++ ++ return err; ++} ++ ++static int adv7393_setoutput(struct v4l2_subdev *sd, u32 output_type) ++{ ++ struct adv7393_state *state = to_state(sd); ++ u8 val; ++ int err = 0; ++ ++ if (output_type > ADV7393_SVIDEO_ID) { ++ v4l2_dbg(1, debug, sd, ++ "Invalid output type or output type not supported:%d\n", ++ output_type); ++ return -EINVAL; ++ } ++ ++ /* Enable Appropriate DAC */ ++ val = state->reg00 & 0x03; ++ ++ if (output_type == ADV7393_COMPOSITE_ID) ++ val |= ADV7393_COMPOSITE_POWER_VALUE; ++ else if (output_type == ADV7393_COMPONENT_ID) ++ val |= ADV7393_COMPONENT_POWER_VALUE; ++ else ++ val |= ADV7393_SVIDEO_POWER_VALUE; ++ ++ err = adv7393_write(sd, ADV7393_POWER_MODE_REG, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg00 = val; ++ ++ /* Enable YUV output */ ++ val = state->reg02 | YUV_OUTPUT_SELECT; ++ err = adv7393_write(sd, ADV7393_MODE_REG0, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg02 = val; ++ ++ /* configure SD DAC Output 1 bit */ ++ val = state->reg82; ++ if (output_type == ADV7393_COMPONENT_ID) ++ val &= SD_DAC_OUT1_DI; ++ else ++ val |= SD_DAC_OUT1_EN; ++ err = adv7393_write(sd, ADV7393_SD_MODE_REG2, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg82 = val; ++ ++ /* configure ED/HD Color DAC Swap bit to zero */ ++ val = state->reg35 & HD_DAC_SWAP_DI; ++ err = adv7393_write(sd, ADV7393_HD_MODE_REG6, val); ++ if (err < 0) ++ goto setoutput_exit; ++ ++ state->reg35 = val; ++ ++setoutput_exit: ++ if (err != 0) ++ v4l2_err(sd, "Error setting output, write failed\n"); ++ ++ return err; ++} ++ ++static int adv7393_log_status(struct v4l2_subdev *sd) ++{ ++ struct adv7393_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); ++ v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : ++ ((state->output == 1) ? "Component" : "S-Video")); ++ return 0; ++} ++ ++static int adv7393_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return adv7393_write(sd, ADV7393_SD_BRIGHTNESS_WSS, ++ ctrl->val & SD_BRIGHTNESS_VALUE_MASK); ++ ++ case V4L2_CID_HUE: ++ return adv7393_write(sd, ADV7393_SD_HUE_ADJUST, ++ ctrl->val - ADV7393_HUE_MIN); ++ ++ case V4L2_CID_GAIN: ++ return adv7393_write(sd, ADV7393_DAC123_OUTPUT_LEVEL, ++ ctrl->val); ++ } ++ return -EINVAL; ++} ++ ++static int adv7393_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7393, 0); ++} ++ ++static const struct v4l2_ctrl_ops adv7393_ctrl_ops = { ++ .s_ctrl = adv7393_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops adv7393_core_ops = { ++ .log_status = adv7393_log_status, ++ .g_chip_ident = adv7393_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static int adv7393_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct adv7393_state *state = to_state(sd); ++ int err = 0; ++ ++ if (state->std == std) ++ return 0; ++ ++ err = adv7393_setstd(sd, std); ++ if (!err) ++ state->std = std; ++ ++ return err; ++} ++ ++static int adv7393_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7393_state *state = to_state(sd); ++ int err = 0; ++ ++ if (state->output == output) ++ return 0; ++ ++ err = adv7393_setoutput(sd, output); ++ if (!err) ++ state->output = output; ++ ++ return err; ++} ++ ++static const struct v4l2_subdev_video_ops adv7393_video_ops = { ++ .s_std_output = adv7393_s_std_output, ++ .s_routing = adv7393_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops adv7393_ops = { ++ .core = &adv7393_core_ops, ++ .video = &adv7393_video_ops, ++}; ++ ++static int adv7393_initialize(struct v4l2_subdev *sd) ++{ ++ struct adv7393_state *state = to_state(sd); ++ int err = 0; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(adv7393_init_reg_val); i += 2) { ++ ++ err = adv7393_write(sd, adv7393_init_reg_val[i], ++ adv7393_init_reg_val[i+1]); ++ if (err) { ++ v4l2_err(sd, "Error initializing\n"); ++ return err; ++ } ++ } ++ ++ /* Configure for default video standard */ ++ err = adv7393_setoutput(sd, state->output); ++ if (err < 0) { ++ v4l2_err(sd, "Error setting output during init\n"); ++ return -EINVAL; ++ } ++ ++ err = adv7393_setstd(sd, state->std); ++ if (err < 0) { ++ v4l2_err(sd, "Error setting std during init\n"); ++ return -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int adv7393_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7393_state *state; ++ int err; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct adv7393_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ state->reg00 = ADV7393_POWER_MODE_REG_DEFAULT; ++ state->reg01 = 0x00; ++ state->reg02 = 0x20; ++ state->reg35 = ADV7393_HD_MODE_REG6_DEFAULT; ++ state->reg80 = ADV7393_SD_MODE_REG1_DEFAULT; ++ state->reg82 = ADV7393_SD_MODE_REG2_DEFAULT; ++ ++ state->output = ADV7393_COMPOSITE_ID; ++ state->std = V4L2_STD_NTSC; ++ ++ v4l2_i2c_subdev_init(&state->sd, client, &adv7393_ops); ++ ++ v4l2_ctrl_handler_init(&state->hdl, 3); ++ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, ADV7393_BRIGHTNESS_MIN, ++ ADV7393_BRIGHTNESS_MAX, 1, ++ ADV7393_BRIGHTNESS_DEF); ++ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, ++ V4L2_CID_HUE, ADV7393_HUE_MIN, ++ ADV7393_HUE_MAX, 1, ++ ADV7393_HUE_DEF); ++ v4l2_ctrl_new_std(&state->hdl, &adv7393_ctrl_ops, ++ V4L2_CID_GAIN, ADV7393_GAIN_MIN, ++ ADV7393_GAIN_MAX, 1, ++ ADV7393_GAIN_DEF); ++ state->sd.ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&state->hdl); ++ ++ err = adv7393_initialize(&state->sd); ++ if (err) { ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ } ++ return err; ++} ++ ++static int adv7393_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7393_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id adv7393_id[] = { ++ {"adv7393", 0}, ++ {}, ++}; ++MODULE_DEVICE_TABLE(i2c, adv7393_id); ++ ++static struct i2c_driver adv7393_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7393", ++ }, ++ .probe = adv7393_probe, ++ .remove = adv7393_remove, ++ .id_table = adv7393_id, ++}; ++module_i2c_driver(adv7393_driver); +diff --git a/drivers/media/i2c/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h +new file mode 100644 +index 0000000..7896833 +--- /dev/null ++++ b/drivers/media/i2c/adv7393_regs.h +@@ -0,0 +1,188 @@ ++/* ++ * ADV7393 encoder related structure and register definitions ++ * ++ * Copyright (C) 2010-2012 ADVANSEE - http://www.advansee.com/ ++ * Benoît Thébaudeau ++ * ++ * Based on ADV7343 driver, ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef ADV7393_REGS_H ++#define ADV7393_REGS_H ++ ++struct adv7393_std_info { ++ u32 standard_val3; ++ u32 fsc_val; ++ v4l2_std_id stdid; ++}; ++ ++/* Register offset macros */ ++#define ADV7393_POWER_MODE_REG (0x00) ++#define ADV7393_MODE_SELECT_REG (0x01) ++#define ADV7393_MODE_REG0 (0x02) ++ ++#define ADV7393_DAC123_OUTPUT_LEVEL (0x0B) ++ ++#define ADV7393_SOFT_RESET (0x17) ++ ++#define ADV7393_HD_MODE_REG1 (0x30) ++#define ADV7393_HD_MODE_REG2 (0x31) ++#define ADV7393_HD_MODE_REG3 (0x32) ++#define ADV7393_HD_MODE_REG4 (0x33) ++#define ADV7393_HD_MODE_REG5 (0x34) ++#define ADV7393_HD_MODE_REG6 (0x35) ++ ++#define ADV7393_HD_MODE_REG7 (0x39) ++ ++#define ADV7393_SD_MODE_REG1 (0x80) ++#define ADV7393_SD_MODE_REG2 (0x82) ++#define ADV7393_SD_MODE_REG3 (0x83) ++#define ADV7393_SD_MODE_REG4 (0x84) ++#define ADV7393_SD_MODE_REG5 (0x86) ++#define ADV7393_SD_MODE_REG6 (0x87) ++#define ADV7393_SD_MODE_REG7 (0x88) ++#define ADV7393_SD_MODE_REG8 (0x89) ++ ++#define ADV7393_SD_TIMING_REG0 (0x8A) ++ ++#define ADV7393_FSC_REG0 (0x8C) ++#define ADV7393_FSC_REG1 (0x8D) ++#define ADV7393_FSC_REG2 (0x8E) ++#define ADV7393_FSC_REG3 (0x8F) ++ ++#define ADV7393_SD_CGMS_WSS0 (0x99) ++ ++#define ADV7393_SD_HUE_ADJUST (0xA0) ++#define ADV7393_SD_BRIGHTNESS_WSS (0xA1) ++ ++/* Default values for the registers */ ++#define ADV7393_POWER_MODE_REG_DEFAULT (0x10) ++#define ADV7393_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default ++ 720p EAV/SAV code*/ ++#define ADV7393_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data ++ valid */ ++#define ADV7393_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ ++#define ADV7393_HD_MODE_REG4_DEFAULT (0xEC) /* Changed */ ++#define ADV7393_HD_MODE_REG5_DEFAULT (0x08) ++#define ADV7393_HD_MODE_REG6_DEFAULT (0x00) ++#define ADV7393_HD_MODE_REG7_DEFAULT (0x00) ++#define ADV7393_SOFT_RESET_DEFAULT (0x02) ++#define ADV7393_COMPOSITE_POWER_VALUE (0x10) ++#define ADV7393_COMPONENT_POWER_VALUE (0x1C) ++#define ADV7393_SVIDEO_POWER_VALUE (0x0C) ++#define ADV7393_SD_HUE_ADJUST_DEFAULT (0x80) ++#define ADV7393_SD_BRIGHTNESS_WSS_DEFAULT (0x00) ++ ++#define ADV7393_SD_CGMS_WSS0_DEFAULT (0x10) ++ ++#define ADV7393_SD_MODE_REG1_DEFAULT (0x10) ++#define ADV7393_SD_MODE_REG2_DEFAULT (0xC9) ++#define ADV7393_SD_MODE_REG3_DEFAULT (0x00) ++#define ADV7393_SD_MODE_REG4_DEFAULT (0x00) ++#define ADV7393_SD_MODE_REG5_DEFAULT (0x02) ++#define ADV7393_SD_MODE_REG6_DEFAULT (0x8C) ++#define ADV7393_SD_MODE_REG7_DEFAULT (0x14) ++#define ADV7393_SD_MODE_REG8_DEFAULT (0x00) ++ ++#define ADV7393_SD_TIMING_REG0_DEFAULT (0x0C) ++ ++/* Bit masks for Mode Select Register */ ++#define INPUT_MODE_MASK (0x70) ++#define SD_INPUT_MODE (0x00) ++#define HD_720P_INPUT_MODE (0x10) ++#define HD_1080I_INPUT_MODE (0x10) ++ ++/* Bit masks for Mode Register 0 */ ++#define TEST_PATTERN_BLACK_BAR_EN (0x04) ++#define YUV_OUTPUT_SELECT (0x20) ++#define RGB_OUTPUT_SELECT (0xDF) ++ ++/* Bit masks for SD brightness/WSS */ ++#define SD_BRIGHTNESS_VALUE_MASK (0x7F) ++#define SD_BLANK_WSS_DATA_MASK (0x80) ++ ++/* Bit masks for soft reset register */ ++#define SOFT_RESET (0x02) ++ ++/* Bit masks for HD Mode Register 1 */ ++#define OUTPUT_STD_MASK (0x03) ++#define OUTPUT_STD_SHIFT (0) ++#define OUTPUT_STD_EIA0_2 (0x00) ++#define OUTPUT_STD_EIA0_1 (0x01) ++#define OUTPUT_STD_FULL (0x02) ++#define EMBEDDED_SYNC (0x04) ++#define EXTERNAL_SYNC (0xFB) ++#define STD_MODE_MASK (0x1F) ++#define STD_MODE_SHIFT (3) ++#define STD_MODE_720P (0x05) ++#define STD_MODE_720P_25 (0x08) ++#define STD_MODE_720P_30 (0x07) ++#define STD_MODE_720P_50 (0x06) ++#define STD_MODE_1080I (0x0D) ++#define STD_MODE_1080I_25 (0x0E) ++#define STD_MODE_1080P_24 (0x11) ++#define STD_MODE_1080P_25 (0x10) ++#define STD_MODE_1080P_30 (0x0F) ++#define STD_MODE_525P (0x00) ++#define STD_MODE_625P (0x03) ++ ++/* Bit masks for SD Mode Register 1 */ ++#define SD_STD_MASK (0x03) ++#define SD_STD_NTSC (0x00) ++#define SD_STD_PAL_BDGHI (0x01) ++#define SD_STD_PAL_M (0x02) ++#define SD_STD_PAL_N (0x03) ++#define SD_LUMA_FLTR_MASK (0x07) ++#define SD_LUMA_FLTR_SHIFT (2) ++#define SD_CHROMA_FLTR_MASK (0x07) ++#define SD_CHROMA_FLTR_SHIFT (5) ++ ++/* Bit masks for SD Mode Register 2 */ ++#define SD_PRPB_SSAF_EN (0x01) ++#define SD_PRPB_SSAF_DI (0xFE) ++#define SD_DAC_OUT1_EN (0x02) ++#define SD_DAC_OUT1_DI (0xFD) ++#define SD_PEDESTAL_EN (0x08) ++#define SD_PEDESTAL_DI (0xF7) ++#define SD_SQUARE_PIXEL_EN (0x10) ++#define SD_SQUARE_PIXEL_DI (0xEF) ++#define SD_PIXEL_DATA_VALID (0x40) ++#define SD_ACTIVE_EDGE_EN (0x80) ++#define SD_ACTIVE_EDGE_DI (0x7F) ++ ++/* Bit masks for HD Mode Register 6 */ ++#define HD_PRPB_SYNC_EN (0x04) ++#define HD_PRPB_SYNC_DI (0xFB) ++#define HD_DAC_SWAP_EN (0x08) ++#define HD_DAC_SWAP_DI (0xF7) ++#define HD_GAMMA_CURVE_A (0xEF) ++#define HD_GAMMA_CURVE_B (0x10) ++#define HD_GAMMA_EN (0x20) ++#define HD_GAMMA_DI (0xDF) ++#define HD_ADPT_FLTR_MODEA (0xBF) ++#define HD_ADPT_FLTR_MODEB (0x40) ++#define HD_ADPT_FLTR_EN (0x80) ++#define HD_ADPT_FLTR_DI (0x7F) ++ ++#define ADV7393_BRIGHTNESS_MAX (63) ++#define ADV7393_BRIGHTNESS_MIN (-64) ++#define ADV7393_BRIGHTNESS_DEF (0) ++#define ADV7393_HUE_MAX (127) ++#define ADV7393_HUE_MIN (-128) ++#define ADV7393_HUE_DEF (0) ++#define ADV7393_GAIN_MAX (64) ++#define ADV7393_GAIN_MIN (-64) ++#define ADV7393_GAIN_DEF (0) ++ ++#endif +diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c +new file mode 100644 +index 0000000..f47555b +--- /dev/null ++++ b/drivers/media/i2c/adv7604.c +@@ -0,0 +1,2136 @@ ++/* ++ * adv7604 - Analog Devices ADV7604 video decoder driver ++ * ++ * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. ++ * ++ * This program is free software; you may redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ */ ++ ++/* ++ * References (c = chapter, p = page): ++ * REF_01 - Analog devices, ADV7604, Register Settings Recommendations, ++ * Revision 2.5, June 2010 ++ * REF_02 - Analog devices, Register map documentation, Documentation of ++ * the register maps, Software manual, Rev. F, June 2010 ++ * REF_03 - Analog devices, ADV7604, Hardware Manual, Rev. F, August 2010 ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "debug level (0-2)"); ++ ++MODULE_DESCRIPTION("Analog Devices ADV7604 video decoder driver"); ++MODULE_AUTHOR("Hans Verkuil "); ++MODULE_AUTHOR("Mats Randgaard "); ++MODULE_LICENSE("GPL"); ++ ++/* ADV7604 system clock frequency */ ++#define ADV7604_fsc (28636360) ++ ++#define DIGITAL_INPUT (state->mode == ADV7604_MODE_HDMI) ++ ++/* ++ ********************************************************************** ++ * ++ * Arrays with configuration parameters for the ADV7604 ++ * ++ ********************************************************************** ++ */ ++struct adv7604_state { ++ struct adv7604_platform_data pdata; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler hdl; ++ enum adv7604_mode mode; ++ struct v4l2_dv_timings timings; ++ u8 edid[256]; ++ unsigned edid_blocks; ++ struct v4l2_fract aspect_ratio; ++ u32 rgb_quantization_range; ++ struct workqueue_struct *work_queues; ++ struct delayed_work delayed_work_enable_hotplug; ++ bool connector_hdmi; ++ bool restart_stdi_once; ++ ++ /* i2c clients */ ++ struct i2c_client *i2c_avlink; ++ struct i2c_client *i2c_cec; ++ struct i2c_client *i2c_infoframe; ++ struct i2c_client *i2c_esdp; ++ struct i2c_client *i2c_dpp; ++ struct i2c_client *i2c_afe; ++ struct i2c_client *i2c_repeater; ++ struct i2c_client *i2c_edid; ++ struct i2c_client *i2c_hdmi; ++ struct i2c_client *i2c_test; ++ struct i2c_client *i2c_cp; ++ struct i2c_client *i2c_vdp; ++ ++ /* controls */ ++ struct v4l2_ctrl *detect_tx_5v_ctrl; ++ struct v4l2_ctrl *analog_sampling_phase_ctrl; ++ struct v4l2_ctrl *free_run_color_manual_ctrl; ++ struct v4l2_ctrl *free_run_color_ctrl; ++ struct v4l2_ctrl *rgb_quantization_range_ctrl; ++}; ++ ++/* Supported CEA and DMT timings */ ++static const struct v4l2_dv_timings adv7604_timings[] = { ++ V4L2_DV_BT_CEA_720X480P59_94, ++ V4L2_DV_BT_CEA_720X576P50, ++ V4L2_DV_BT_CEA_1280X720P24, ++ V4L2_DV_BT_CEA_1280X720P25, ++ V4L2_DV_BT_CEA_1280X720P50, ++ V4L2_DV_BT_CEA_1280X720P60, ++ V4L2_DV_BT_CEA_1920X1080P24, ++ V4L2_DV_BT_CEA_1920X1080P25, ++ V4L2_DV_BT_CEA_1920X1080P30, ++ V4L2_DV_BT_CEA_1920X1080P50, ++ V4L2_DV_BT_CEA_1920X1080P60, ++ ++ /* sorted by DMT ID */ ++ V4L2_DV_BT_DMT_640X350P85, ++ V4L2_DV_BT_DMT_640X400P85, ++ V4L2_DV_BT_DMT_720X400P85, ++ V4L2_DV_BT_DMT_640X480P60, ++ V4L2_DV_BT_DMT_640X480P72, ++ V4L2_DV_BT_DMT_640X480P75, ++ V4L2_DV_BT_DMT_640X480P85, ++ V4L2_DV_BT_DMT_800X600P56, ++ V4L2_DV_BT_DMT_800X600P60, ++ V4L2_DV_BT_DMT_800X600P72, ++ V4L2_DV_BT_DMT_800X600P75, ++ V4L2_DV_BT_DMT_800X600P85, ++ V4L2_DV_BT_DMT_848X480P60, ++ V4L2_DV_BT_DMT_1024X768P60, ++ V4L2_DV_BT_DMT_1024X768P70, ++ V4L2_DV_BT_DMT_1024X768P75, ++ V4L2_DV_BT_DMT_1024X768P85, ++ V4L2_DV_BT_DMT_1152X864P75, ++ V4L2_DV_BT_DMT_1280X768P60_RB, ++ V4L2_DV_BT_DMT_1280X768P60, ++ V4L2_DV_BT_DMT_1280X768P75, ++ V4L2_DV_BT_DMT_1280X768P85, ++ V4L2_DV_BT_DMT_1280X800P60_RB, ++ V4L2_DV_BT_DMT_1280X800P60, ++ V4L2_DV_BT_DMT_1280X800P75, ++ V4L2_DV_BT_DMT_1280X800P85, ++ V4L2_DV_BT_DMT_1280X960P60, ++ V4L2_DV_BT_DMT_1280X960P85, ++ V4L2_DV_BT_DMT_1280X1024P60, ++ V4L2_DV_BT_DMT_1280X1024P75, ++ V4L2_DV_BT_DMT_1280X1024P85, ++ V4L2_DV_BT_DMT_1360X768P60, ++ V4L2_DV_BT_DMT_1400X1050P60_RB, ++ V4L2_DV_BT_DMT_1400X1050P60, ++ V4L2_DV_BT_DMT_1400X1050P75, ++ V4L2_DV_BT_DMT_1400X1050P85, ++ V4L2_DV_BT_DMT_1440X900P60_RB, ++ V4L2_DV_BT_DMT_1440X900P60, ++ V4L2_DV_BT_DMT_1600X1200P60, ++ V4L2_DV_BT_DMT_1680X1050P60_RB, ++ V4L2_DV_BT_DMT_1680X1050P60, ++ V4L2_DV_BT_DMT_1792X1344P60, ++ V4L2_DV_BT_DMT_1856X1392P60, ++ V4L2_DV_BT_DMT_1920X1200P60_RB, ++ V4L2_DV_BT_DMT_1366X768P60, ++ V4L2_DV_BT_DMT_1920X1080P60, ++ { }, ++}; ++ ++struct adv7604_video_standards { ++ struct v4l2_dv_timings timings; ++ u8 vid_std; ++ u8 v_freq; ++}; ++ ++/* sorted by number of lines */ ++static const struct adv7604_video_standards adv7604_prim_mode_comp[] = { ++ /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ ++ { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, ++ { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, ++ { V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 }, ++ { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, ++ { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, ++ { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, ++ { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, ++ { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, ++ /* TODO add 1920x1080P60_RB (CVT timing) */ ++ { }, ++}; ++ ++/* sorted by number of lines */ ++static const struct adv7604_video_standards adv7604_prim_mode_gr[] = { ++ { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, ++ { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, ++ { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, ++ { V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 }, ++ { V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 }, ++ { V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 }, ++ { V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 }, ++ { V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */ ++ /* TODO add 1600X1200P60_RB (not a DMT timing) */ ++ { V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 }, ++ { V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */ ++ { }, ++}; ++ ++/* sorted by number of lines */ ++static const struct adv7604_video_standards adv7604_prim_mode_hdmi_comp[] = { ++ { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, ++ { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, ++ { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, ++ { V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 }, ++ { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, ++ { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, ++ { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, ++ { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, ++ { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, ++ { }, ++}; ++ ++/* sorted by number of lines */ ++static const struct adv7604_video_standards adv7604_prim_mode_hdmi_gr[] = { ++ { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, ++ { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, ++ { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, ++ { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, ++ { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, ++ { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, ++ { }, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline struct adv7604_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct adv7604_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct adv7604_state, hdl)->sd; ++} ++ ++static inline unsigned hblanking(const struct v4l2_bt_timings *t) ++{ ++ return t->hfrontporch + t->hsync + t->hbackporch; ++} ++ ++static inline unsigned htotal(const struct v4l2_bt_timings *t) ++{ ++ return t->width + t->hfrontporch + t->hsync + t->hbackporch; ++} ++ ++static inline unsigned vblanking(const struct v4l2_bt_timings *t) ++{ ++ return t->vfrontporch + t->vsync + t->vbackporch; ++} ++ ++static inline unsigned vtotal(const struct v4l2_bt_timings *t) ++{ ++ return t->height + t->vfrontporch + t->vsync + t->vbackporch; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static s32 adv_smbus_read_byte_data_check(struct i2c_client *client, ++ u8 command, bool check) ++{ ++ union i2c_smbus_data data; ++ ++ if (!i2c_smbus_xfer(client->adapter, client->addr, client->flags, ++ I2C_SMBUS_READ, command, ++ I2C_SMBUS_BYTE_DATA, &data)) ++ return data.byte; ++ if (check) ++ v4l_err(client, "error reading %02x, %02x\n", ++ client->addr, command); ++ return -EIO; ++} ++ ++static s32 adv_smbus_read_byte_data(struct i2c_client *client, u8 command) ++{ ++ return adv_smbus_read_byte_data_check(client, command, true); ++} ++ ++static s32 adv_smbus_write_byte_data(struct i2c_client *client, ++ u8 command, u8 value) ++{ ++ union i2c_smbus_data data; ++ int err; ++ int i; ++ ++ data.byte = value; ++ for (i = 0; i < 3; i++) { ++ err = i2c_smbus_xfer(client->adapter, client->addr, ++ client->flags, ++ I2C_SMBUS_WRITE, command, ++ I2C_SMBUS_BYTE_DATA, &data); ++ if (!err) ++ break; ++ } ++ if (err < 0) ++ v4l_err(client, "error writing %02x, %02x, %02x\n", ++ client->addr, command, value); ++ return err; ++} ++ ++static s32 adv_smbus_write_i2c_block_data(struct i2c_client *client, ++ u8 command, unsigned length, const u8 *values) ++{ ++ union i2c_smbus_data data; ++ ++ if (length > I2C_SMBUS_BLOCK_MAX) ++ length = I2C_SMBUS_BLOCK_MAX; ++ data.block[0] = length; ++ memcpy(data.block + 1, values, length); ++ return i2c_smbus_xfer(client->adapter, client->addr, client->flags, ++ I2C_SMBUS_WRITE, command, ++ I2C_SMBUS_I2C_BLOCK_DATA, &data); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int io_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return adv_smbus_read_byte_data(client, reg); ++} ++ ++static inline int io_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return adv_smbus_write_byte_data(client, reg, val); ++} ++ ++static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) ++{ ++ return io_write(sd, reg, (io_read(sd, reg) & mask) | val); ++} ++ ++static inline int avlink_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_avlink, reg); ++} ++ ++static inline int avlink_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_avlink, reg, val); ++} ++ ++static inline int cec_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_cec, reg); ++} ++ ++static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_cec, reg, val); ++} ++ ++static inline int cec_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) ++{ ++ return cec_write(sd, reg, (cec_read(sd, reg) & mask) | val); ++} ++ ++static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_infoframe, reg); ++} ++ ++static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_infoframe, reg, val); ++} ++ ++static inline int esdp_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_esdp, reg); ++} ++ ++static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_esdp, reg, val); ++} ++ ++static inline int dpp_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_dpp, reg); ++} ++ ++static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_dpp, reg, val); ++} ++ ++static inline int afe_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_afe, reg); ++} ++ ++static inline int afe_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_afe, reg, val); ++} ++ ++static inline int rep_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_repeater, reg); ++} ++ ++static inline int rep_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_repeater, reg, val); ++} ++ ++static inline int rep_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) ++{ ++ return rep_write(sd, reg, (rep_read(sd, reg) & mask) | val); ++} ++ ++static inline int edid_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_edid, reg); ++} ++ ++static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_edid, reg, val); ++} ++ ++static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct i2c_client *client = state->i2c_edid; ++ u8 msgbuf0[1] = { 0 }; ++ u8 msgbuf1[256]; ++ struct i2c_msg msg[2] = { ++ { ++ .addr = client->addr, ++ .len = 1, ++ .buf = msgbuf0 ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = len, ++ .buf = msgbuf1 ++ }, ++ }; ++ ++ if (i2c_transfer(client->adapter, msg, 2) < 0) ++ return -EIO; ++ memcpy(val, msgbuf1, len); ++ return 0; ++} ++ ++static void adv7604_delayed_work_enable_hotplug(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct adv7604_state *state = container_of(dwork, struct adv7604_state, ++ delayed_work_enable_hotplug); ++ struct v4l2_subdev *sd = &state->sd; ++ ++ v4l2_dbg(2, debug, sd, "%s: enable hotplug\n", __func__); ++ ++ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)1); ++} ++ ++static inline int edid_write_block(struct v4l2_subdev *sd, ++ unsigned len, const u8 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct adv7604_state *state = to_state(sd); ++ int err = 0; ++ int i; ++ ++ v4l2_dbg(2, debug, sd, "%s: write EDID block (%d byte)\n", __func__, len); ++ ++ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); ++ ++ /* Disables I2C access to internal EDID ram from DDC port */ ++ rep_write_and_or(sd, 0x77, 0xf0, 0x0); ++ ++ for (i = 0; !err && i < len; i += I2C_SMBUS_BLOCK_MAX) ++ err = adv_smbus_write_i2c_block_data(state->i2c_edid, i, ++ I2C_SMBUS_BLOCK_MAX, val + i); ++ if (err) ++ return err; ++ ++ /* adv7604 calculates the checksums and enables I2C access to internal ++ EDID ram from DDC port. */ ++ rep_write_and_or(sd, 0x77, 0xf0, 0x1); ++ ++ for (i = 0; i < 1000; i++) { ++ if (rep_read(sd, 0x7d) & 1) ++ break; ++ mdelay(1); ++ } ++ if (i == 1000) { ++ v4l_err(client, "error enabling edid\n"); ++ return -EIO; ++ } ++ ++ /* enable hotplug after 100 ms */ ++ queue_delayed_work(state->work_queues, ++ &state->delayed_work_enable_hotplug, HZ / 10); ++ return 0; ++} ++ ++static inline int hdmi_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_hdmi, reg); ++} ++ ++static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); ++} ++ ++static inline int test_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_test, reg); ++} ++ ++static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_test, reg, val); ++} ++ ++static inline int cp_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_cp, reg); ++} ++ ++static inline int cp_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_cp, reg, val); ++} ++ ++static inline int cp_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) ++{ ++ return cp_write(sd, reg, (cp_read(sd, reg) & mask) | val); ++} ++ ++static inline int vdp_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_read_byte_data(state->i2c_vdp, reg); ++} ++ ++static inline int vdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ return adv_smbus_write_byte_data(state->i2c_vdp, reg, val); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static void adv7604_inv_register(struct v4l2_subdev *sd) ++{ ++ v4l2_info(sd, "0x000-0x0ff: IO Map\n"); ++ v4l2_info(sd, "0x100-0x1ff: AVLink Map\n"); ++ v4l2_info(sd, "0x200-0x2ff: CEC Map\n"); ++ v4l2_info(sd, "0x300-0x3ff: InfoFrame Map\n"); ++ v4l2_info(sd, "0x400-0x4ff: ESDP Map\n"); ++ v4l2_info(sd, "0x500-0x5ff: DPP Map\n"); ++ v4l2_info(sd, "0x600-0x6ff: AFE Map\n"); ++ v4l2_info(sd, "0x700-0x7ff: Repeater Map\n"); ++ v4l2_info(sd, "0x800-0x8ff: EDID Map\n"); ++ v4l2_info(sd, "0x900-0x9ff: HDMI Map\n"); ++ v4l2_info(sd, "0xa00-0xaff: Test Map\n"); ++ v4l2_info(sd, "0xb00-0xbff: CP Map\n"); ++ v4l2_info(sd, "0xc00-0xcff: VDP Map\n"); ++} ++ ++static int adv7604_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->size = 1; ++ switch (reg->reg >> 8) { ++ case 0: ++ reg->val = io_read(sd, reg->reg & 0xff); ++ break; ++ case 1: ++ reg->val = avlink_read(sd, reg->reg & 0xff); ++ break; ++ case 2: ++ reg->val = cec_read(sd, reg->reg & 0xff); ++ break; ++ case 3: ++ reg->val = infoframe_read(sd, reg->reg & 0xff); ++ break; ++ case 4: ++ reg->val = esdp_read(sd, reg->reg & 0xff); ++ break; ++ case 5: ++ reg->val = dpp_read(sd, reg->reg & 0xff); ++ break; ++ case 6: ++ reg->val = afe_read(sd, reg->reg & 0xff); ++ break; ++ case 7: ++ reg->val = rep_read(sd, reg->reg & 0xff); ++ break; ++ case 8: ++ reg->val = edid_read(sd, reg->reg & 0xff); ++ break; ++ case 9: ++ reg->val = hdmi_read(sd, reg->reg & 0xff); ++ break; ++ case 0xa: ++ reg->val = test_read(sd, reg->reg & 0xff); ++ break; ++ case 0xb: ++ reg->val = cp_read(sd, reg->reg & 0xff); ++ break; ++ case 0xc: ++ reg->val = vdp_read(sd, reg->reg & 0xff); ++ break; ++ default: ++ v4l2_info(sd, "Register %03llx not supported\n", reg->reg); ++ adv7604_inv_register(sd); ++ break; ++ } ++ return 0; ++} ++ ++static int adv7604_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ switch (reg->reg >> 8) { ++ case 0: ++ io_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 1: ++ avlink_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 2: ++ cec_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 3: ++ infoframe_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 4: ++ esdp_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 5: ++ dpp_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 6: ++ afe_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 7: ++ rep_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 8: ++ edid_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 9: ++ hdmi_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 0xa: ++ test_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 0xb: ++ cp_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ case 0xc: ++ vdp_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ break; ++ default: ++ v4l2_info(sd, "Register %03llx not supported\n", reg->reg); ++ adv7604_inv_register(sd); ++ break; ++ } ++ return 0; ++} ++#endif ++ ++static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ /* port A only */ ++ return v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl, ++ ((io_read(sd, 0x6f) & 0x10) >> 4)); ++} ++ ++static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, ++ u8 prim_mode, ++ const struct adv7604_video_standards *predef_vid_timings, ++ const struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ int i; ++ ++ for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { ++ if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings, ++ DIGITAL_INPUT ? 250000 : 1000000)) ++ continue; ++ io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ ++ io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + ++ prim_mode); /* v_freq and prim mode */ ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static int configure_predefined_video_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ int err; ++ ++ v4l2_dbg(1, debug, sd, "%s", __func__); ++ ++ /* reset to default values */ ++ io_write(sd, 0x16, 0x43); ++ io_write(sd, 0x17, 0x5a); ++ /* disable embedded syncs for auto graphics mode */ ++ cp_write_and_or(sd, 0x81, 0xef, 0x00); ++ cp_write(sd, 0x8f, 0x00); ++ cp_write(sd, 0x90, 0x00); ++ cp_write(sd, 0xa2, 0x00); ++ cp_write(sd, 0xa3, 0x00); ++ cp_write(sd, 0xa4, 0x00); ++ cp_write(sd, 0xa5, 0x00); ++ cp_write(sd, 0xa6, 0x00); ++ cp_write(sd, 0xa7, 0x00); ++ cp_write(sd, 0xab, 0x00); ++ cp_write(sd, 0xac, 0x00); ++ ++ switch (state->mode) { ++ case ADV7604_MODE_COMP: ++ case ADV7604_MODE_GR: ++ err = find_and_set_predefined_video_timings(sd, ++ 0x01, adv7604_prim_mode_comp, timings); ++ if (err) ++ err = find_and_set_predefined_video_timings(sd, ++ 0x02, adv7604_prim_mode_gr, timings); ++ break; ++ case ADV7604_MODE_HDMI: ++ err = find_and_set_predefined_video_timings(sd, ++ 0x05, adv7604_prim_mode_hdmi_comp, timings); ++ if (err) ++ err = find_and_set_predefined_video_timings(sd, ++ 0x06, adv7604_prim_mode_hdmi_gr, timings); ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", ++ __func__, state->mode); ++ err = -1; ++ break; ++ } ++ ++ ++ return err; ++} ++ ++static void configure_custom_video_timings(struct v4l2_subdev *sd, ++ const struct v4l2_bt_timings *bt) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u32 width = htotal(bt); ++ u32 height = vtotal(bt); ++ u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; ++ u16 cp_start_eav = width - bt->hfrontporch; ++ u16 cp_start_vbi = height - bt->vfrontporch; ++ u16 cp_end_vbi = bt->vsync + bt->vbackporch; ++ u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? ++ ((width * (ADV7604_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0; ++ const u8 pll[2] = { ++ 0xc0 | ((width >> 8) & 0x1f), ++ width & 0xff ++ }; ++ ++ v4l2_dbg(2, debug, sd, "%s\n", __func__); ++ ++ switch (state->mode) { ++ case ADV7604_MODE_COMP: ++ case ADV7604_MODE_GR: ++ /* auto graphics */ ++ io_write(sd, 0x00, 0x07); /* video std */ ++ io_write(sd, 0x01, 0x02); /* prim mode */ ++ /* enable embedded syncs for auto graphics mode */ ++ cp_write_and_or(sd, 0x81, 0xef, 0x10); ++ ++ /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ ++ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ ++ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ ++ if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { ++ v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); ++ break; ++ } ++ ++ /* active video - horizontal timing */ ++ cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff); ++ cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | ++ ((cp_start_eav >> 8) & 0x0f)); ++ cp_write(sd, 0xa4, cp_start_eav & 0xff); ++ ++ /* active video - vertical timing */ ++ cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); ++ cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | ++ ((cp_end_vbi >> 8) & 0xf)); ++ cp_write(sd, 0xa7, cp_end_vbi & 0xff); ++ break; ++ case ADV7604_MODE_HDMI: ++ /* set default prim_mode/vid_std for HDMI ++ accoring to [REF_03, c. 4.2] */ ++ io_write(sd, 0x00, 0x02); /* video std */ ++ io_write(sd, 0x01, 0x06); /* prim mode */ ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", ++ __func__, state->mode); ++ break; ++ } ++ ++ cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); ++ cp_write(sd, 0x90, ch1_fr_ll & 0xff); ++ cp_write(sd, 0xab, (height >> 4) & 0xff); ++ cp_write(sd, 0xac, (height & 0x0f) << 4); ++} ++ ++static void set_rgb_quantization_range(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ switch (state->rgb_quantization_range) { ++ case V4L2_DV_RGB_RANGE_AUTO: ++ /* automatic */ ++ if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) { ++ /* receiving DVI-D signal */ ++ ++ /* ADV7604 selects RGB limited range regardless of ++ input format (CE/IT) in automatic mode */ ++ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { ++ /* RGB limited range (16-235) */ ++ io_write_and_or(sd, 0x02, 0x0f, 0x00); ++ ++ } else { ++ /* RGB full range (0-255) */ ++ io_write_and_or(sd, 0x02, 0x0f, 0x10); ++ } ++ } else { ++ /* receiving HDMI or analog signal, set automode */ ++ io_write_and_or(sd, 0x02, 0x0f, 0xf0); ++ } ++ break; ++ case V4L2_DV_RGB_RANGE_LIMITED: ++ /* RGB limited range (16-235) */ ++ io_write_and_or(sd, 0x02, 0x0f, 0x00); ++ break; ++ case V4L2_DV_RGB_RANGE_FULL: ++ /* RGB full range (0-255) */ ++ io_write_and_or(sd, 0x02, 0x0f, 0x10); ++ break; ++ } ++} ++ ++ ++static int adv7604_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct adv7604_state *state = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ cp_write(sd, 0x3c, ctrl->val); ++ return 0; ++ case V4L2_CID_CONTRAST: ++ cp_write(sd, 0x3a, ctrl->val); ++ return 0; ++ case V4L2_CID_SATURATION: ++ cp_write(sd, 0x3b, ctrl->val); ++ return 0; ++ case V4L2_CID_HUE: ++ cp_write(sd, 0x3d, ctrl->val); ++ return 0; ++ case V4L2_CID_DV_RX_RGB_RANGE: ++ state->rgb_quantization_range = ctrl->val; ++ set_rgb_quantization_range(sd); ++ return 0; ++ case V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE: ++ /* Set the analog sampling phase. This is needed to find the ++ best sampling phase for analog video: an application or ++ driver has to try a number of phases and analyze the picture ++ quality before settling on the best performing phase. */ ++ afe_write(sd, 0xc8, ctrl->val); ++ return 0; ++ case V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL: ++ /* Use the default blue color for free running mode, ++ or supply your own. */ ++ cp_write_and_or(sd, 0xbf, ~0x04, (ctrl->val << 2)); ++ return 0; ++ case V4L2_CID_ADV_RX_FREE_RUN_COLOR: ++ cp_write(sd, 0xc0, (ctrl->val & 0xff0000) >> 16); ++ cp_write(sd, 0xc1, (ctrl->val & 0x00ff00) >> 8); ++ cp_write(sd, 0xc2, (u8)(ctrl->val & 0x0000ff)); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int adv7604_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7604, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline bool no_power(struct v4l2_subdev *sd) ++{ ++ /* Entire chip or CP powered off */ ++ return io_read(sd, 0x0c) & 0x24; ++} ++ ++static inline bool no_signal_tmds(struct v4l2_subdev *sd) ++{ ++ /* TODO port B, C and D */ ++ return !(io_read(sd, 0x6a) & 0x10); ++} ++ ++static inline bool no_lock_tmds(struct v4l2_subdev *sd) ++{ ++ return (io_read(sd, 0x6a) & 0xe0) != 0xe0; ++} ++ ++static inline bool no_lock_sspd(struct v4l2_subdev *sd) ++{ ++ /* TODO channel 2 */ ++ return ((cp_read(sd, 0xb5) & 0xd0) != 0xd0); ++} ++ ++static inline bool no_lock_stdi(struct v4l2_subdev *sd) ++{ ++ /* TODO channel 2 */ ++ return !(cp_read(sd, 0xb1) & 0x80); ++} ++ ++static inline bool no_signal(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ bool ret; ++ ++ ret = no_power(sd); ++ ++ ret |= no_lock_stdi(sd); ++ ret |= no_lock_sspd(sd); ++ ++ if (DIGITAL_INPUT) { ++ ret |= no_lock_tmds(sd); ++ ret |= no_signal_tmds(sd); ++ } ++ ++ return ret; ++} ++ ++static inline bool no_lock_cp(struct v4l2_subdev *sd) ++{ ++ /* CP has detected a non standard number of lines on the incoming ++ video compared to what it is configured to receive by s_dv_timings */ ++ return io_read(sd, 0x12) & 0x01; ++} ++ ++static int adv7604_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ *status = 0; ++ *status |= no_power(sd) ? V4L2_IN_ST_NO_POWER : 0; ++ *status |= no_signal(sd) ? V4L2_IN_ST_NO_SIGNAL : 0; ++ if (no_lock_cp(sd)) ++ *status |= DIGITAL_INPUT ? V4L2_IN_ST_NO_SYNC : V4L2_IN_ST_NO_H_LOCK; ++ ++ v4l2_dbg(1, debug, sd, "%s: status = 0x%x\n", __func__, *status); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void adv7604_print_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings, const char *txt, bool detailed) ++{ ++ struct v4l2_bt_timings *bt = &timings->bt; ++ u32 htot, vtot; ++ ++ if (timings->type != V4L2_DV_BT_656_1120) ++ return; ++ ++ htot = htotal(bt); ++ vtot = vtotal(bt); ++ ++ v4l2_info(sd, "%s %dx%d%s%d (%dx%d)", ++ txt, bt->width, bt->height, bt->interlaced ? "i" : "p", ++ (htot * vtot) > 0 ? ((u32)bt->pixelclock / ++ (htot * vtot)) : 0, ++ htot, vtot); ++ ++ if (detailed) { ++ v4l2_info(sd, " horizontal: fp = %d, %ssync = %d, bp = %d\n", ++ bt->hfrontporch, ++ (bt->polarities & V4L2_DV_HSYNC_POS_POL) ? "+" : "-", ++ bt->hsync, bt->hbackporch); ++ v4l2_info(sd, " vertical: fp = %d, %ssync = %d, bp = %d\n", ++ bt->vfrontporch, ++ (bt->polarities & V4L2_DV_VSYNC_POS_POL) ? "+" : "-", ++ bt->vsync, bt->vbackporch); ++ v4l2_info(sd, " pixelclock: %lld, flags: 0x%x, standards: 0x%x\n", ++ bt->pixelclock, bt->flags, bt->standards); ++ } ++} ++ ++struct stdi_readback { ++ u16 bl, lcf, lcvs; ++ u8 hs_pol, vs_pol; ++ bool interlaced; ++}; ++ ++static int stdi2dv_timings(struct v4l2_subdev *sd, ++ struct stdi_readback *stdi, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ u32 hfreq = (ADV7604_fsc * 8) / stdi->bl; ++ u32 pix_clk; ++ int i; ++ ++ for (i = 0; adv7604_timings[i].bt.height; i++) { ++ if (vtotal(&adv7604_timings[i].bt) != stdi->lcf + 1) ++ continue; ++ if (adv7604_timings[i].bt.vsync != stdi->lcvs) ++ continue; ++ ++ pix_clk = hfreq * htotal(&adv7604_timings[i].bt); ++ ++ if ((pix_clk < adv7604_timings[i].bt.pixelclock + 1000000) && ++ (pix_clk > adv7604_timings[i].bt.pixelclock - 1000000)) { ++ *timings = adv7604_timings[i]; ++ return 0; ++ } ++ } ++ ++ if (v4l2_detect_cvt(stdi->lcf + 1, hfreq, stdi->lcvs, ++ (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | ++ (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), ++ timings)) ++ return 0; ++ if (v4l2_detect_gtf(stdi->lcf + 1, hfreq, stdi->lcvs, ++ (stdi->hs_pol == '+' ? V4L2_DV_HSYNC_POS_POL : 0) | ++ (stdi->vs_pol == '+' ? V4L2_DV_VSYNC_POS_POL : 0), ++ state->aspect_ratio, timings)) ++ return 0; ++ ++ v4l2_dbg(2, debug, sd, ++ "%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n", ++ __func__, stdi->lcvs, stdi->lcf, stdi->bl, ++ stdi->hs_pol, stdi->vs_pol); ++ return -1; ++} ++ ++static int read_stdi(struct v4l2_subdev *sd, struct stdi_readback *stdi) ++{ ++ if (no_lock_stdi(sd) || no_lock_sspd(sd)) { ++ v4l2_dbg(2, debug, sd, "%s: STDI and/or SSPD not locked\n", __func__); ++ return -1; ++ } ++ ++ /* read STDI */ ++ stdi->bl = ((cp_read(sd, 0xb1) & 0x3f) << 8) | cp_read(sd, 0xb2); ++ stdi->lcf = ((cp_read(sd, 0xb3) & 0x7) << 8) | cp_read(sd, 0xb4); ++ stdi->lcvs = cp_read(sd, 0xb3) >> 3; ++ stdi->interlaced = io_read(sd, 0x12) & 0x10; ++ ++ /* read SSPD */ ++ if ((cp_read(sd, 0xb5) & 0x03) == 0x01) { ++ stdi->hs_pol = ((cp_read(sd, 0xb5) & 0x10) ? ++ ((cp_read(sd, 0xb5) & 0x08) ? '+' : '-') : 'x'); ++ stdi->vs_pol = ((cp_read(sd, 0xb5) & 0x40) ? ++ ((cp_read(sd, 0xb5) & 0x20) ? '+' : '-') : 'x'); ++ } else { ++ stdi->hs_pol = 'x'; ++ stdi->vs_pol = 'x'; ++ } ++ ++ if (no_lock_stdi(sd) || no_lock_sspd(sd)) { ++ v4l2_dbg(2, debug, sd, ++ "%s: signal lost during readout of STDI/SSPD\n", __func__); ++ return -1; ++ } ++ ++ if (stdi->lcf < 239 || stdi->bl < 8 || stdi->bl == 0x3fff) { ++ v4l2_dbg(2, debug, sd, "%s: invalid signal\n", __func__); ++ memset(stdi, 0, sizeof(struct stdi_readback)); ++ return -1; ++ } ++ ++ v4l2_dbg(2, debug, sd, ++ "%s: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %chsync, %cvsync, %s\n", ++ __func__, stdi->lcf, stdi->bl, stdi->lcvs, ++ stdi->hs_pol, stdi->vs_pol, ++ stdi->interlaced ? "interlaced" : "progressive"); ++ ++ return 0; ++} ++ ++static int adv7604_enum_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ if (timings->index >= ARRAY_SIZE(adv7604_timings) - 1) ++ return -EINVAL; ++ memset(timings->reserved, 0, sizeof(timings->reserved)); ++ timings->timings = adv7604_timings[timings->index]; ++ return 0; ++} ++ ++static int adv7604_dv_timings_cap(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings_cap *cap) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ cap->type = V4L2_DV_BT_656_1120; ++ cap->bt.max_width = 1920; ++ cap->bt.max_height = 1200; ++ cap->bt.min_pixelclock = 27000000; ++ if (DIGITAL_INPUT) ++ cap->bt.max_pixelclock = 225000000; ++ else ++ cap->bt.max_pixelclock = 170000000; ++ cap->bt.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | ++ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT; ++ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE | ++ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM; ++ return 0; ++} ++ ++/* Fill the optional fields .standards and .flags in struct v4l2_dv_timings ++ if the format is listed in adv7604_timings[] */ ++static void adv7604_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ int i; ++ ++ for (i = 0; adv7604_timings[i].bt.width; i++) { ++ if (v4l_match_dv_timings(timings, &adv7604_timings[i], ++ DIGITAL_INPUT ? 250000 : 1000000)) { ++ *timings = adv7604_timings[i]; ++ break; ++ } ++ } ++} ++ ++static int adv7604_query_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct v4l2_bt_timings *bt = &timings->bt; ++ struct stdi_readback stdi; ++ ++ if (!timings) ++ return -EINVAL; ++ ++ memset(timings, 0, sizeof(struct v4l2_dv_timings)); ++ ++ if (no_signal(sd)) { ++ v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); ++ return -ENOLINK; ++ } ++ ++ /* read STDI */ ++ if (read_stdi(sd, &stdi)) { ++ v4l2_dbg(1, debug, sd, "%s: STDI/SSPD not locked\n", __func__); ++ return -ENOLINK; ++ } ++ bt->interlaced = stdi.interlaced ? ++ V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; ++ ++ if (DIGITAL_INPUT) { ++ timings->type = V4L2_DV_BT_656_1120; ++ ++ bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); ++ bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); ++ bt->pixelclock = (hdmi_read(sd, 0x06) * 1000000) + ++ ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; ++ bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + ++ hdmi_read(sd, 0x21); ++ bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + ++ hdmi_read(sd, 0x23); ++ bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 + ++ hdmi_read(sd, 0x25); ++ bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + ++ hdmi_read(sd, 0x2b)) / 2; ++ bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + ++ hdmi_read(sd, 0x2f)) / 2; ++ bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + ++ hdmi_read(sd, 0x33)) / 2; ++ bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | ++ ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); ++ if (bt->interlaced == V4L2_DV_INTERLACED) { ++ bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + ++ hdmi_read(sd, 0x0c); ++ bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + ++ hdmi_read(sd, 0x2d)) / 2; ++ bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + ++ hdmi_read(sd, 0x31)) / 2; ++ bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + ++ hdmi_read(sd, 0x35)) / 2; ++ } ++ adv7604_fill_optional_dv_timings_fields(sd, timings); ++ } else { ++ /* find format ++ * Since LCVS values are inaccurate [REF_03, p. 275-276], ++ * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. ++ */ ++ if (!stdi2dv_timings(sd, &stdi, timings)) ++ goto found; ++ stdi.lcvs += 1; ++ v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs); ++ if (!stdi2dv_timings(sd, &stdi, timings)) ++ goto found; ++ stdi.lcvs -= 2; ++ v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs); ++ if (stdi2dv_timings(sd, &stdi, timings)) { ++ /* ++ * The STDI block may measure wrong values, especially ++ * for lcvs and lcf. If the driver can not find any ++ * valid timing, the STDI block is restarted to measure ++ * the video timings again. The function will return an ++ * error, but the restart of STDI will generate a new ++ * STDI interrupt and the format detection process will ++ * restart. ++ */ ++ if (state->restart_stdi_once) { ++ v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); ++ /* TODO restart STDI for Sync Channel 2 */ ++ /* enter one-shot mode */ ++ cp_write_and_or(sd, 0x86, 0xf9, 0x00); ++ /* trigger STDI restart */ ++ cp_write_and_or(sd, 0x86, 0xf9, 0x04); ++ /* reset to continuous mode */ ++ cp_write_and_or(sd, 0x86, 0xf9, 0x02); ++ state->restart_stdi_once = false; ++ return -ENOLINK; ++ } ++ v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); ++ return -ERANGE; ++ } ++ state->restart_stdi_once = true; ++ } ++found: ++ ++ if (no_signal(sd)) { ++ v4l2_dbg(1, debug, sd, "%s: signal lost during readout\n", __func__); ++ memset(timings, 0, sizeof(struct v4l2_dv_timings)); ++ return -ENOLINK; ++ } ++ ++ if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || ++ (DIGITAL_INPUT && bt->pixelclock > 225000000)) { ++ v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n", ++ __func__, (u32)bt->pixelclock); ++ return -ERANGE; ++ } ++ ++ if (debug > 1) ++ adv7604_print_timings(sd, timings, ++ "adv7604_query_dv_timings:", true); ++ ++ return 0; ++} ++ ++static int adv7604_s_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct v4l2_bt_timings *bt; ++ int err; ++ ++ if (!timings) ++ return -EINVAL; ++ ++ bt = &timings->bt; ++ ++ if ((!DIGITAL_INPUT && bt->pixelclock > 170000000) || ++ (DIGITAL_INPUT && bt->pixelclock > 225000000)) { ++ v4l2_dbg(1, debug, sd, "%s: pixelclock out of range %d\n", ++ __func__, (u32)bt->pixelclock); ++ return -ERANGE; ++ } ++ ++ adv7604_fill_optional_dv_timings_fields(sd, timings); ++ ++ state->timings = *timings; ++ ++ cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); ++ ++ /* Use prim_mode and vid_std when available */ ++ err = configure_predefined_video_timings(sd, timings); ++ if (err) { ++ /* custom settings when the video format ++ does not have prim_mode/vid_std */ ++ configure_custom_video_timings(sd, bt); ++ } ++ ++ set_rgb_quantization_range(sd); ++ ++ ++ if (debug > 1) ++ adv7604_print_timings(sd, timings, ++ "adv7604_s_dv_timings:", true); ++ return 0; ++} ++ ++static int adv7604_g_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ *timings = state->timings; ++ return 0; ++} ++ ++static void enable_input(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ switch (state->mode) { ++ case ADV7604_MODE_COMP: ++ case ADV7604_MODE_GR: ++ /* enable */ ++ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ ++ break; ++ case ADV7604_MODE_HDMI: ++ /* enable */ ++ hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ ++ hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ ++ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", ++ __func__, state->mode); ++ break; ++ } ++} ++ ++static void disable_input(struct v4l2_subdev *sd) ++{ ++ /* disable */ ++ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ ++ hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ ++ hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ ++} ++ ++static void select_input(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ switch (state->mode) { ++ case ADV7604_MODE_COMP: ++ case ADV7604_MODE_GR: ++ /* reset ADI recommended settings for HDMI: */ ++ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ ++ hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ ++ hdmi_write(sd, 0x3d, 0x00); /* DDC bus active pull-up control */ ++ hdmi_write(sd, 0x3e, 0x74); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x57, 0x74); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x58, 0x63); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x8d, 0x18); /* equaliser */ ++ hdmi_write(sd, 0x8e, 0x34); /* equaliser */ ++ hdmi_write(sd, 0x93, 0x88); /* equaliser */ ++ hdmi_write(sd, 0x94, 0x2e); /* equaliser */ ++ hdmi_write(sd, 0x96, 0x00); /* enable automatic EQ changing */ ++ ++ afe_write(sd, 0x00, 0x08); /* power up ADC */ ++ afe_write(sd, 0x01, 0x06); /* power up Analog Front End */ ++ afe_write(sd, 0xc8, 0x00); /* phase control */ ++ ++ /* set ADI recommended settings for digitizer */ ++ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ ++ afe_write(sd, 0x12, 0x7b); /* ADC noise shaping filter controls */ ++ afe_write(sd, 0x0c, 0x1f); /* CP core gain controls */ ++ cp_write(sd, 0x3e, 0x04); /* CP core pre-gain control */ ++ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ ++ cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ ++ break; ++ ++ case ADV7604_MODE_HDMI: ++ /* set ADI recommended settings for HDMI: */ ++ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ ++ hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ ++ hdmi_write(sd, 0x3d, 0x10); /* DDC bus active pull-up control */ ++ hdmi_write(sd, 0x3e, 0x39); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x4e, 0x3b); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x57, 0xb6); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x58, 0x03); /* TMDS PLL optimization */ ++ hdmi_write(sd, 0x8d, 0x18); /* equaliser */ ++ hdmi_write(sd, 0x8e, 0x34); /* equaliser */ ++ hdmi_write(sd, 0x93, 0x8b); /* equaliser */ ++ hdmi_write(sd, 0x94, 0x2d); /* equaliser */ ++ hdmi_write(sd, 0x96, 0x01); /* enable automatic EQ changing */ ++ ++ afe_write(sd, 0x00, 0xff); /* power down ADC */ ++ afe_write(sd, 0x01, 0xfe); /* power down Analog Front End */ ++ afe_write(sd, 0xc8, 0x40); /* phase control */ ++ ++ /* reset ADI recommended settings for digitizer */ ++ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 17. */ ++ afe_write(sd, 0x12, 0xfb); /* ADC noise shaping filter controls */ ++ afe_write(sd, 0x0c, 0x0d); /* CP core gain controls */ ++ cp_write(sd, 0x3e, 0x00); /* CP core pre-gain control */ ++ cp_write(sd, 0xc3, 0x39); /* CP coast control. Graphics mode */ ++ cp_write(sd, 0x40, 0x80); /* CP core pre-gain control. Graphics mode */ ++ ++ break; ++ default: ++ v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", ++ __func__, state->mode); ++ break; ++ } ++} ++ ++static int adv7604_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input); ++ ++ state->mode = input; ++ ++ disable_input(sd); ++ ++ select_input(sd); ++ ++ enable_input(sd); ++ ++ return 0; ++} ++ ++static int adv7604_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ /* Good enough for now */ ++ *code = V4L2_MBUS_FMT_FIXED; ++ return 0; ++} ++ ++static int adv7604_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ fmt->width = state->timings.bt.width; ++ fmt->height = state->timings.bt.height; ++ fmt->code = V4L2_MBUS_FMT_FIXED; ++ fmt->field = V4L2_FIELD_NONE; ++ if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { ++ fmt->colorspace = (state->timings.bt.height <= 576) ? ++ V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709; ++ } ++ return 0; ++} ++ ++static int adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) ++{ ++ struct adv7604_state *state = to_state(sd); ++ u8 fmt_change, fmt_change_digital, tx_5v; ++ ++ /* format change */ ++ fmt_change = io_read(sd, 0x43) & 0x98; ++ if (fmt_change) ++ io_write(sd, 0x44, fmt_change); ++ fmt_change_digital = DIGITAL_INPUT ? (io_read(sd, 0x6b) & 0xc0) : 0; ++ if (fmt_change_digital) ++ io_write(sd, 0x6c, fmt_change_digital); ++ if (fmt_change || fmt_change_digital) { ++ v4l2_dbg(1, debug, sd, ++ "%s: ADV7604_FMT_CHANGE, fmt_change = 0x%x, fmt_change_digital = 0x%x\n", ++ __func__, fmt_change, fmt_change_digital); ++ v4l2_subdev_notify(sd, ADV7604_FMT_CHANGE, NULL); ++ if (handled) ++ *handled = true; ++ } ++ /* tx 5v detect */ ++ tx_5v = io_read(sd, 0x70) & 0x10; ++ if (tx_5v) { ++ v4l2_dbg(1, debug, sd, "%s: tx_5v: 0x%x\n", __func__, tx_5v); ++ io_write(sd, 0x71, tx_5v); ++ adv7604_s_detect_tx_5v_ctrl(sd); ++ if (handled) ++ *handled = true; ++ } ++ return 0; ++} ++ ++static int adv7604_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) ++{ ++ struct adv7604_state *state = to_state(sd); ++ ++ if (edid->pad != 0) ++ return -EINVAL; ++ if (edid->blocks == 0) ++ return -EINVAL; ++ if (edid->start_block >= state->edid_blocks) ++ return -EINVAL; ++ if (edid->start_block + edid->blocks > state->edid_blocks) ++ edid->blocks = state->edid_blocks - edid->start_block; ++ if (!edid->edid) ++ return -EINVAL; ++ memcpy(edid->edid + edid->start_block * 128, ++ state->edid + edid->start_block * 128, ++ edid->blocks * 128); ++ return 0; ++} ++ ++static int adv7604_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) ++{ ++ struct adv7604_state *state = to_state(sd); ++ int err; ++ ++ if (edid->pad != 0) ++ return -EINVAL; ++ if (edid->start_block != 0) ++ return -EINVAL; ++ if (edid->blocks == 0) { ++ /* Pull down the hotplug pin */ ++ v4l2_subdev_notify(sd, ADV7604_HOTPLUG, (void *)0); ++ /* Disables I2C access to internal EDID ram from DDC port */ ++ rep_write_and_or(sd, 0x77, 0xf0, 0x0); ++ state->edid_blocks = 0; ++ /* Fall back to a 16:9 aspect ratio */ ++ state->aspect_ratio.numerator = 16; ++ state->aspect_ratio.denominator = 9; ++ return 0; ++ } ++ if (edid->blocks > 2) ++ return -E2BIG; ++ if (!edid->edid) ++ return -EINVAL; ++ memcpy(state->edid, edid->edid, 128 * edid->blocks); ++ state->edid_blocks = edid->blocks; ++ state->aspect_ratio = v4l2_calc_aspect_ratio(edid->edid[0x15], ++ edid->edid[0x16]); ++ err = edid_write_block(sd, 128 * edid->blocks, state->edid); ++ if (err < 0) ++ v4l2_err(sd, "error %d writing edid\n", err); ++ return err; ++} ++ ++/*********** avi info frame CEA-861-E **************/ ++ ++static void print_avi_infoframe(struct v4l2_subdev *sd) ++{ ++ int i; ++ u8 buf[14]; ++ u8 avi_len; ++ u8 avi_ver; ++ ++ if (!(hdmi_read(sd, 0x05) & 0x80)) { ++ v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); ++ return; ++ } ++ if (!(io_read(sd, 0x60) & 0x01)) { ++ v4l2_info(sd, "AVI infoframe not received\n"); ++ return; ++ } ++ ++ if (io_read(sd, 0x83) & 0x01) { ++ v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n"); ++ io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */ ++ if (io_read(sd, 0x83) & 0x01) { ++ v4l2_info(sd, "AVI infoframe checksum error still present\n"); ++ io_write(sd, 0x85, 0x01); /* clear AVI_INF_CKS_ERR_RAW */ ++ } ++ } ++ ++ avi_len = infoframe_read(sd, 0xe2); ++ avi_ver = infoframe_read(sd, 0xe1); ++ v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", ++ avi_ver, avi_len); ++ ++ if (avi_ver != 0x02) ++ return; ++ ++ for (i = 0; i < 14; i++) ++ buf[i] = infoframe_read(sd, i); ++ ++ v4l2_info(sd, ++ "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], ++ buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); ++} ++ ++static int adv7604_log_status(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct v4l2_dv_timings timings; ++ struct stdi_readback stdi; ++ u8 reg_io_0x02 = io_read(sd, 0x02); ++ ++ char *csc_coeff_sel_rb[16] = { ++ "bypassed", "YPbPr601 -> RGB", "reserved", "YPbPr709 -> RGB", ++ "reserved", "RGB -> YPbPr601", "reserved", "RGB -> YPbPr709", ++ "reserved", "YPbPr709 -> YPbPr601", "YPbPr601 -> YPbPr709", ++ "reserved", "reserved", "reserved", "reserved", "manual" ++ }; ++ char *input_color_space_txt[16] = { ++ "RGB limited range (16-235)", "RGB full range (0-255)", ++ "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", ++ "XvYCC Bt.601", "XvYCC Bt.709", ++ "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", ++ "invalid", "invalid", "invalid", "invalid", "invalid", ++ "invalid", "invalid", "automatic" ++ }; ++ char *rgb_quantization_range_txt[] = { ++ "Automatic", ++ "RGB limited range (16-235)", ++ "RGB full range (0-255)", ++ }; ++ ++ v4l2_info(sd, "-----Chip status-----\n"); ++ v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); ++ v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? ++ "HDMI" : (DIGITAL_INPUT ? "DVI-D" : "DVI-A")); ++ v4l2_info(sd, "EDID: %s\n", ((rep_read(sd, 0x7d) & 0x01) && ++ (rep_read(sd, 0x77) & 0x01)) ? "enabled" : "disabled "); ++ v4l2_info(sd, "CEC: %s\n", !!(cec_read(sd, 0x2a) & 0x01) ? ++ "enabled" : "disabled"); ++ ++ v4l2_info(sd, "-----Signal status-----\n"); ++ v4l2_info(sd, "Cable detected (+5V power): %s\n", ++ (io_read(sd, 0x6f) & 0x10) ? "true" : "false"); ++ v4l2_info(sd, "TMDS signal detected: %s\n", ++ no_signal_tmds(sd) ? "false" : "true"); ++ v4l2_info(sd, "TMDS signal locked: %s\n", ++ no_lock_tmds(sd) ? "false" : "true"); ++ v4l2_info(sd, "SSPD locked: %s\n", no_lock_sspd(sd) ? "false" : "true"); ++ v4l2_info(sd, "STDI locked: %s\n", no_lock_stdi(sd) ? "false" : "true"); ++ v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true"); ++ v4l2_info(sd, "CP free run: %s\n", ++ (!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off")); ++ v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n", ++ io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f, ++ (io_read(sd, 0x01) & 0x70) >> 4); ++ ++ v4l2_info(sd, "-----Video Timings-----\n"); ++ if (read_stdi(sd, &stdi)) ++ v4l2_info(sd, "STDI: not locked\n"); ++ else ++ v4l2_info(sd, "STDI: lcf (frame height - 1) = %d, bl = %d, lcvs (vsync) = %d, %s, %chsync, %cvsync\n", ++ stdi.lcf, stdi.bl, stdi.lcvs, ++ stdi.interlaced ? "interlaced" : "progressive", ++ stdi.hs_pol, stdi.vs_pol); ++ if (adv7604_query_dv_timings(sd, &timings)) ++ v4l2_info(sd, "No video detected\n"); ++ else ++ adv7604_print_timings(sd, &timings, "Detected format:", true); ++ adv7604_print_timings(sd, &state->timings, "Configured format:", true); ++ ++ v4l2_info(sd, "-----Color space-----\n"); ++ v4l2_info(sd, "RGB quantization range ctrl: %s\n", ++ rgb_quantization_range_txt[state->rgb_quantization_range]); ++ v4l2_info(sd, "Input color space: %s\n", ++ input_color_space_txt[reg_io_0x02 >> 4]); ++ v4l2_info(sd, "Output color space: %s %s, saturator %s\n", ++ (reg_io_0x02 & 0x02) ? "RGB" : "YCbCr", ++ (reg_io_0x02 & 0x04) ? "(16-235)" : "(0-255)", ++ ((reg_io_0x02 & 0x04) ^ (reg_io_0x02 & 0x01)) ? ++ "enabled" : "disabled"); ++ v4l2_info(sd, "Color space conversion: %s\n", ++ csc_coeff_sel_rb[cp_read(sd, 0xfc) >> 4]); ++ ++ /* Digital video */ ++ if (DIGITAL_INPUT) { ++ v4l2_info(sd, "-----HDMI status-----\n"); ++ v4l2_info(sd, "HDCP encrypted content: %s\n", ++ hdmi_read(sd, 0x05) & 0x40 ? "true" : "false"); ++ ++ print_avi_infoframe(sd); ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops adv7604_ctrl_ops = { ++ .s_ctrl = adv7604_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops adv7604_core_ops = { ++ .log_status = adv7604_log_status, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_chip_ident = adv7604_g_chip_ident, ++ .interrupt_service_routine = adv7604_isr, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = adv7604_g_register, ++ .s_register = adv7604_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops adv7604_video_ops = { ++ .s_routing = adv7604_s_routing, ++ .g_input_status = adv7604_g_input_status, ++ .s_dv_timings = adv7604_s_dv_timings, ++ .g_dv_timings = adv7604_g_dv_timings, ++ .query_dv_timings = adv7604_query_dv_timings, ++ .enum_dv_timings = adv7604_enum_dv_timings, ++ .dv_timings_cap = adv7604_dv_timings_cap, ++ .enum_mbus_fmt = adv7604_enum_mbus_fmt, ++ .g_mbus_fmt = adv7604_g_mbus_fmt, ++ .try_mbus_fmt = adv7604_g_mbus_fmt, ++ .s_mbus_fmt = adv7604_g_mbus_fmt, ++}; ++ ++static const struct v4l2_subdev_pad_ops adv7604_pad_ops = { ++ .get_edid = adv7604_get_edid, ++ .set_edid = adv7604_set_edid, ++}; ++ ++static const struct v4l2_subdev_ops adv7604_ops = { ++ .core = &adv7604_core_ops, ++ .video = &adv7604_video_ops, ++ .pad = &adv7604_pad_ops, ++}; ++ ++/* -------------------------- custom ctrls ---------------------------------- */ ++ ++static const struct v4l2_ctrl_config adv7604_ctrl_analog_sampling_phase = { ++ .ops = &adv7604_ctrl_ops, ++ .id = V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE, ++ .name = "Analog Sampling Phase", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0, ++ .max = 0x1f, ++ .step = 1, ++ .def = 0, ++}; ++ ++static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color_manual = { ++ .ops = &adv7604_ctrl_ops, ++ .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL, ++ .name = "Free Running Color, Manual", ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .min = false, ++ .max = true, ++ .step = 1, ++ .def = false, ++}; ++ ++static const struct v4l2_ctrl_config adv7604_ctrl_free_run_color = { ++ .ops = &adv7604_ctrl_ops, ++ .id = V4L2_CID_ADV_RX_FREE_RUN_COLOR, ++ .name = "Free Running Color", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .min = 0x0, ++ .max = 0xffffff, ++ .step = 0x1, ++ .def = 0x0, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int adv7604_core_init(struct v4l2_subdev *sd) ++{ ++ struct adv7604_state *state = to_state(sd); ++ struct adv7604_platform_data *pdata = &state->pdata; ++ ++ hdmi_write(sd, 0x48, ++ (pdata->disable_pwrdnb ? 0x80 : 0) | ++ (pdata->disable_cable_det_rst ? 0x40 : 0)); ++ ++ disable_input(sd); ++ ++ /* power */ ++ io_write(sd, 0x0c, 0x42); /* Power up part and power down VDP */ ++ io_write(sd, 0x0b, 0x44); /* Power down ESDP block */ ++ cp_write(sd, 0xcf, 0x01); /* Power down macrovision */ ++ ++ /* video format */ ++ io_write_and_or(sd, 0x02, 0xf0, ++ pdata->alt_gamma << 3 | ++ pdata->op_656_range << 2 | ++ pdata->rgb_out << 1 | ++ pdata->alt_data_sat << 0); ++ io_write(sd, 0x03, pdata->op_format_sel); ++ io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5); ++ io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 | ++ pdata->insert_av_codes << 2 | ++ pdata->replicate_av_codes << 1 | ++ pdata->invert_cbcr << 0); ++ ++ /* TODO from platform data */ ++ cp_write(sd, 0x69, 0x30); /* Enable CP CSC */ ++ io_write(sd, 0x06, 0xa6); /* positive VS and HS */ ++ io_write(sd, 0x14, 0x7f); /* Drive strength adjusted to max */ ++ cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */ ++ cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ ++ cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold - ++ ADI recommended setting [REF_01, c. 2.3.3] */ ++ cp_write(sd, 0x45, 0x23); /* STDI ch. 2 - LCVS change threshold - ++ ADI recommended setting [REF_01, c. 2.3.3] */ ++ cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution ++ for digital formats */ ++ ++ /* TODO from platform data */ ++ afe_write(sd, 0xb5, 0x01); /* Setting MCLK to 256Fs */ ++ ++ afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ ++ io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); ++ ++ /* interrupts */ ++ io_write(sd, 0x40, 0xc2); /* Configure INT1 */ ++ io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ ++ io_write(sd, 0x46, 0x98); /* Enable SSPD, STDI and CP unlocked interrupts */ ++ io_write(sd, 0x6e, 0xc0); /* Enable V_LOCKED and DE_REGEN_LCK interrupts */ ++ io_write(sd, 0x73, 0x10); /* Enable CABLE_DET_A_ST (+5v) interrupt */ ++ ++ return v4l2_ctrl_handler_setup(sd->ctrl_handler); ++} ++ ++static void adv7604_unregister_clients(struct adv7604_state *state) ++{ ++ if (state->i2c_avlink) ++ i2c_unregister_device(state->i2c_avlink); ++ if (state->i2c_cec) ++ i2c_unregister_device(state->i2c_cec); ++ if (state->i2c_infoframe) ++ i2c_unregister_device(state->i2c_infoframe); ++ if (state->i2c_esdp) ++ i2c_unregister_device(state->i2c_esdp); ++ if (state->i2c_dpp) ++ i2c_unregister_device(state->i2c_dpp); ++ if (state->i2c_afe) ++ i2c_unregister_device(state->i2c_afe); ++ if (state->i2c_repeater) ++ i2c_unregister_device(state->i2c_repeater); ++ if (state->i2c_edid) ++ i2c_unregister_device(state->i2c_edid); ++ if (state->i2c_hdmi) ++ i2c_unregister_device(state->i2c_hdmi); ++ if (state->i2c_test) ++ i2c_unregister_device(state->i2c_test); ++ if (state->i2c_cp) ++ i2c_unregister_device(state->i2c_cp); ++ if (state->i2c_vdp) ++ i2c_unregister_device(state->i2c_vdp); ++} ++ ++static struct i2c_client *adv7604_dummy_client(struct v4l2_subdev *sd, ++ u8 addr, u8 io_reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (addr) ++ io_write(sd, io_reg, addr << 1); ++ return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); ++} ++ ++static int adv7604_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct adv7604_state *state; ++ struct adv7604_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_ctrl_handler *hdl; ++ struct v4l2_subdev *sd; ++ int err; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ v4l_dbg(1, debug, client, "detecting adv7604 client on address 0x%x\n", ++ client->addr << 1); ++ ++ state = kzalloc(sizeof(struct adv7604_state), GFP_KERNEL); ++ if (!state) { ++ v4l_err(client, "Could not allocate adv7604_state memory!\n"); ++ return -ENOMEM; ++ } ++ ++ /* platform data */ ++ if (!pdata) { ++ v4l_err(client, "No platform data!\n"); ++ err = -ENODEV; ++ goto err_state; ++ } ++ memcpy(&state->pdata, pdata, sizeof(state->pdata)); ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &adv7604_ops); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ state->connector_hdmi = pdata->connector_hdmi; ++ ++ /* i2c access to adv7604? */ ++ if (adv_smbus_read_byte_data_check(client, 0xfb, false) != 0x68) { ++ v4l2_info(sd, "not an adv7604 on address 0x%x\n", ++ client->addr << 1); ++ err = -ENODEV; ++ goto err_state; ++ } ++ ++ /* control handlers */ ++ hdl = &state->hdl; ++ v4l2_ctrl_handler_init(hdl, 9); ++ ++ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(hdl, &adv7604_ctrl_ops, ++ V4L2_CID_HUE, 0, 128, 1, 0); ++ ++ /* private controls */ ++ state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL, ++ V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); ++ state->detect_tx_5v_ctrl->is_private = true; ++ state->rgb_quantization_range_ctrl = ++ v4l2_ctrl_new_std_menu(hdl, &adv7604_ctrl_ops, ++ V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, ++ 0, V4L2_DV_RGB_RANGE_AUTO); ++ state->rgb_quantization_range_ctrl->is_private = true; ++ ++ /* custom controls */ ++ state->analog_sampling_phase_ctrl = ++ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_analog_sampling_phase, NULL); ++ state->analog_sampling_phase_ctrl->is_private = true; ++ state->free_run_color_manual_ctrl = ++ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color_manual, NULL); ++ state->free_run_color_manual_ctrl->is_private = true; ++ state->free_run_color_ctrl = ++ v4l2_ctrl_new_custom(hdl, &adv7604_ctrl_free_run_color, NULL); ++ state->free_run_color_ctrl->is_private = true; ++ ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ err = hdl->error; ++ goto err_hdl; ++ } ++ if (adv7604_s_detect_tx_5v_ctrl(sd)) { ++ err = -ENODEV; ++ goto err_hdl; ++ } ++ ++ state->i2c_avlink = adv7604_dummy_client(sd, pdata->i2c_avlink, 0xf3); ++ state->i2c_cec = adv7604_dummy_client(sd, pdata->i2c_cec, 0xf4); ++ state->i2c_infoframe = adv7604_dummy_client(sd, pdata->i2c_infoframe, 0xf5); ++ state->i2c_esdp = adv7604_dummy_client(sd, pdata->i2c_esdp, 0xf6); ++ state->i2c_dpp = adv7604_dummy_client(sd, pdata->i2c_dpp, 0xf7); ++ state->i2c_afe = adv7604_dummy_client(sd, pdata->i2c_afe, 0xf8); ++ state->i2c_repeater = adv7604_dummy_client(sd, pdata->i2c_repeater, 0xf9); ++ state->i2c_edid = adv7604_dummy_client(sd, pdata->i2c_edid, 0xfa); ++ state->i2c_hdmi = adv7604_dummy_client(sd, pdata->i2c_hdmi, 0xfb); ++ state->i2c_test = adv7604_dummy_client(sd, pdata->i2c_test, 0xfc); ++ state->i2c_cp = adv7604_dummy_client(sd, pdata->i2c_cp, 0xfd); ++ state->i2c_vdp = adv7604_dummy_client(sd, pdata->i2c_vdp, 0xfe); ++ if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || ++ !state->i2c_esdp || !state->i2c_dpp || !state->i2c_afe || ++ !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || ++ !state->i2c_test || !state->i2c_cp || !state->i2c_vdp) { ++ err = -ENOMEM; ++ v4l2_err(sd, "failed to create all i2c clients\n"); ++ goto err_i2c; ++ } ++ state->restart_stdi_once = true; ++ ++ /* work queues */ ++ state->work_queues = create_singlethread_workqueue(client->name); ++ if (!state->work_queues) { ++ v4l2_err(sd, "Could not create work queue\n"); ++ err = -ENOMEM; ++ goto err_i2c; ++ } ++ ++ INIT_DELAYED_WORK(&state->delayed_work_enable_hotplug, ++ adv7604_delayed_work_enable_hotplug); ++ ++ state->pad.flags = MEDIA_PAD_FL_SOURCE; ++ err = media_entity_init(&sd->entity, 1, &state->pad, 0); ++ if (err) ++ goto err_work_queues; ++ ++ err = adv7604_core_init(sd); ++ if (err) ++ goto err_entity; ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, ++ client->addr << 1, client->adapter->name); ++ return 0; ++ ++err_entity: ++ media_entity_cleanup(&sd->entity); ++err_work_queues: ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++err_i2c: ++ adv7604_unregister_clients(state); ++err_hdl: ++ v4l2_ctrl_handler_free(hdl); ++err_state: ++ kfree(state); ++ return err; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int adv7604_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct adv7604_state *state = to_state(sd); ++ ++ cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ destroy_workqueue(state->work_queues); ++ v4l2_device_unregister_subdev(sd); ++ media_entity_cleanup(&sd->entity); ++ adv7604_unregister_clients(to_state(sd)); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct i2c_device_id adv7604_id[] = { ++ { "adv7604", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, adv7604_id); ++ ++static struct i2c_driver adv7604_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "adv7604", ++ }, ++ .probe = adv7604_probe, ++ .remove = adv7604_remove, ++ .id_table = adv7604_id, ++}; ++ ++module_i2c_driver(adv7604_driver); +diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c +new file mode 100644 +index 0000000..ba67465 +--- /dev/null ++++ b/drivers/media/i2c/ak881x.c +@@ -0,0 +1,359 @@ ++/* ++ * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM) ++ * ++ * Copyright (C) 2010, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define AK881X_INTERFACE_MODE 0 ++#define AK881X_VIDEO_PROCESS1 1 ++#define AK881X_VIDEO_PROCESS2 2 ++#define AK881X_VIDEO_PROCESS3 3 ++#define AK881X_DAC_MODE 5 ++#define AK881X_STATUS 0x24 ++#define AK881X_DEVICE_ID 0x25 ++#define AK881X_DEVICE_REVISION 0x26 ++ ++struct ak881x { ++ struct v4l2_subdev subdev; ++ struct ak881x_pdata *pdata; ++ unsigned int lines; ++ int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */ ++ char revision; /* DEVICE_REVISION content */ ++}; ++ ++static int reg_read(struct i2c_client *client, const u8 reg) ++{ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int reg_write(struct i2c_client *client, const u8 reg, ++ const u8 data) ++{ ++ return i2c_smbus_write_byte_data(client, reg, data); ++} ++ ++static int reg_set(struct i2c_client *client, const u8 reg, ++ const u8 data, u8 mask) ++{ ++ int ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, (ret & ~mask) | (data & mask)); ++} ++ ++static struct ak881x *to_ak881x(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ak881x, subdev); ++} ++ ++static int ak881x_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ak881x *ak881x = to_ak881x(client); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = ak881x->id; ++ id->revision = ak881x->revision; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ak881x_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ reg->val = reg_read(client, reg->reg); ++ ++ if (reg->val > 0xffff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int ak881x_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ak881x *ak881x = to_ak881x(client); ++ ++ v4l_bound_align_image(&mf->width, 0, 720, 2, ++ &mf->height, 0, ak881x->lines, 1, 0); ++ mf->field = V4L2_FIELD_INTERLACED; ++ mf->code = V4L2_MBUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ return 0; ++} ++ ++static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ if (mf->field != V4L2_FIELD_INTERLACED || ++ mf->code != V4L2_MBUS_FMT_YUYV8_2X8) ++ return -EINVAL; ++ ++ return ak881x_try_g_mbus_fmt(sd, mf); ++} ++ ++static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_YUYV8_2X8; ++ return 0; ++} ++ ++static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ak881x *ak881x = to_ak881x(client); ++ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = 720; ++ a->bounds.height = ak881x->lines; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ak881x *ak881x = to_ak881x(client); ++ u8 vp1; ++ ++ if (std == V4L2_STD_NTSC_443) { ++ vp1 = 3; ++ ak881x->lines = 480; ++ } else if (std == V4L2_STD_PAL_M) { ++ vp1 = 5; ++ ak881x->lines = 480; ++ } else if (std == V4L2_STD_PAL_60) { ++ vp1 = 7; ++ ak881x->lines = 480; ++ } else if (std && !(std & ~V4L2_STD_PAL)) { ++ vp1 = 0xf; ++ ak881x->lines = 576; ++ } else if (std && !(std & ~V4L2_STD_NTSC)) { ++ vp1 = 0; ++ ak881x->lines = 480; ++ } else { ++ /* No SECAM or PAL_N/Nc supported */ ++ return -EINVAL; ++ } ++ ++ reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf); ++ ++ return 0; ++} ++ ++static int ak881x_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ak881x *ak881x = to_ak881x(client); ++ ++ if (enable) { ++ u8 dac; ++ /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */ ++ /* Default: composite output */ ++ if (ak881x->pdata->flags & AK881X_COMPONENT) ++ dac = 3; ++ else ++ dac = 4; ++ /* Turn on the DAC(s) */ ++ reg_write(client, AK881X_DAC_MODE, dac); ++ dev_dbg(&client->dev, "chip status 0x%x\n", ++ reg_read(client, AK881X_STATUS)); ++ } else { ++ /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */ ++ reg_write(client, AK881X_DAC_MODE, 0); ++ dev_dbg(&client->dev, "chip status 0x%x\n", ++ reg_read(client, AK881X_STATUS)); ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = { ++ .g_chip_ident = ak881x_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ak881x_g_register, ++ .s_register = ak881x_s_register, ++#endif ++}; ++ ++static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = { ++ .s_mbus_fmt = ak881x_s_mbus_fmt, ++ .g_mbus_fmt = ak881x_try_g_mbus_fmt, ++ .try_mbus_fmt = ak881x_try_g_mbus_fmt, ++ .cropcap = ak881x_cropcap, ++ .enum_mbus_fmt = ak881x_enum_mbus_fmt, ++ .s_std_output = ak881x_s_std_output, ++ .s_stream = ak881x_s_stream, ++}; ++ ++static struct v4l2_subdev_ops ak881x_subdev_ops = { ++ .core = &ak881x_subdev_core_ops, ++ .video = &ak881x_subdev_video_ops, ++}; ++ ++static int ak881x_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct ak881x *ak881x; ++ u8 ifmode, data; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL); ++ if (!ak881x) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops); ++ ++ data = reg_read(client, AK881X_DEVICE_ID); ++ ++ switch (data) { ++ case 0x13: ++ ak881x->id = V4L2_IDENT_AK8813; ++ break; ++ case 0x14: ++ ak881x->id = V4L2_IDENT_AK8814; ++ break; ++ default: ++ dev_err(&client->dev, ++ "No ak881x chip detected, register read %x\n", data); ++ kfree(ak881x); ++ return -ENODEV; ++ } ++ ++ ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION); ++ ak881x->pdata = client->dev.platform_data; ++ ++ if (ak881x->pdata) { ++ if (ak881x->pdata->flags & AK881X_FIELD) ++ ifmode = 4; ++ else ++ ifmode = 0; ++ ++ switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) { ++ case AK881X_IF_MODE_BT656: ++ ifmode |= 1; ++ break; ++ case AK881X_IF_MODE_MASTER: ++ ifmode |= 2; ++ break; ++ case AK881X_IF_MODE_SLAVE: ++ default: ++ break; ++ } ++ ++ dev_dbg(&client->dev, "IF mode %x\n", ifmode); ++ ++ /* ++ * "Line Blanking No." seems to be the same as the number of ++ * "black" lines on, e.g., SuperH VOU, whose default value of 20 ++ * "incidentally" matches ak881x' default ++ */ ++ reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3)); ++ } ++ ++ /* Hardware default: NTSC-M */ ++ ak881x->lines = 480; ++ ++ dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n", ++ data, ak881x->revision); ++ ++ return 0; ++} ++ ++static int ak881x_remove(struct i2c_client *client) ++{ ++ struct ak881x *ak881x = to_ak881x(client); ++ ++ v4l2_device_unregister_subdev(&ak881x->subdev); ++ kfree(ak881x); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id ak881x_id[] = { ++ { "ak8813", 0 }, ++ { "ak8814", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ak881x_id); ++ ++static struct i2c_driver ak881x_i2c_driver = { ++ .driver = { ++ .name = "ak881x", ++ }, ++ .probe = ak881x_probe, ++ .remove = ak881x_remove, ++ .id_table = ak881x_id, ++}; ++ ++module_i2c_driver(ak881x_i2c_driver); ++ ++MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/aptina-pll.c b/drivers/media/i2c/aptina-pll.c +new file mode 100644 +index 0000000..8153a44 +--- /dev/null ++++ b/drivers/media/i2c/aptina-pll.c +@@ -0,0 +1,173 @@ ++/* ++ * Aptina Sensor PLL Configuration ++ * ++ * Copyright (C) 2012 Laurent Pinchart ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aptina-pll.h" ++ ++int aptina_pll_calculate(struct device *dev, ++ const struct aptina_pll_limits *limits, ++ struct aptina_pll *pll) ++{ ++ unsigned int mf_min; ++ unsigned int mf_max; ++ unsigned int p1_min; ++ unsigned int p1_max; ++ unsigned int p1; ++ unsigned int div; ++ ++ dev_dbg(dev, "PLL: ext clock %u pix clock %u\n", ++ pll->ext_clock, pll->pix_clock); ++ ++ if (pll->ext_clock < limits->ext_clock_min || ++ pll->ext_clock > limits->ext_clock_max) { ++ dev_err(dev, "pll: invalid external clock frequency.\n"); ++ return -EINVAL; ++ } ++ ++ if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) { ++ dev_err(dev, "pll: invalid pixel clock frequency.\n"); ++ return -EINVAL; ++ } ++ ++ /* Compute the multiplier M and combined N*P1 divisor. */ ++ div = gcd(pll->pix_clock, pll->ext_clock); ++ pll->m = pll->pix_clock / div; ++ div = pll->ext_clock / div; ++ ++ /* We now have the smallest M and N*P1 values that will result in the ++ * desired pixel clock frequency, but they might be out of the valid ++ * range. Compute the factor by which we should multiply them given the ++ * following constraints: ++ * ++ * - minimum/maximum multiplier ++ * - minimum/maximum multiplier output clock frequency assuming the ++ * minimum/maximum N value ++ * - minimum/maximum combined N*P1 divisor ++ */ ++ mf_min = DIV_ROUND_UP(limits->m_min, pll->m); ++ mf_min = max(mf_min, limits->out_clock_min / ++ (pll->ext_clock / limits->n_min * pll->m)); ++ mf_min = max(mf_min, limits->n_min * limits->p1_min / div); ++ mf_max = limits->m_max / pll->m; ++ mf_max = min(mf_max, limits->out_clock_max / ++ (pll->ext_clock / limits->n_max * pll->m)); ++ mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div)); ++ ++ dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max); ++ if (mf_min > mf_max) { ++ dev_err(dev, "pll: no valid combined N*P1 divisor.\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * We're looking for the highest acceptable P1 value for which a ++ * multiplier factor MF exists that fulfills the following conditions: ++ * ++ * 1. p1 is in the [p1_min, p1_max] range given by the limits and is ++ * even ++ * 2. mf is in the [mf_min, mf_max] range computed above ++ * 3. div * mf is a multiple of p1, in order to compute ++ * n = div * mf / p1 ++ * m = pll->m * mf ++ * 4. the internal clock frequency, given by ext_clock / n, is in the ++ * [int_clock_min, int_clock_max] range given by the limits ++ * 5. the output clock frequency, given by ext_clock / n * m, is in the ++ * [out_clock_min, out_clock_max] range given by the limits ++ * ++ * The first naive approach is to iterate over all p1 values acceptable ++ * according to (1) and all mf values acceptable according to (2), and ++ * stop at the first combination that fulfills (3), (4) and (5). This ++ * has a O(n^2) complexity. ++ * ++ * Instead of iterating over all mf values in the [mf_min, mf_max] range ++ * we can compute the mf increment between two acceptable values ++ * according to (3) with ++ * ++ * mf_inc = p1 / gcd(div, p1) (6) ++ * ++ * and round the minimum up to the nearest multiple of mf_inc. This will ++ * restrict the number of mf values to be checked. ++ * ++ * Furthermore, conditions (4) and (5) only restrict the range of ++ * acceptable p1 and mf values by modifying the minimum and maximum ++ * limits. (5) can be expressed as ++ * ++ * ext_clock / (div * mf / p1) * m * mf >= out_clock_min ++ * ext_clock / (div * mf / p1) * m * mf <= out_clock_max ++ * ++ * or ++ * ++ * p1 >= out_clock_min * div / (ext_clock * m) (7) ++ * p1 <= out_clock_max * div / (ext_clock * m) ++ * ++ * Similarly, (4) can be expressed as ++ * ++ * mf >= ext_clock * p1 / (int_clock_max * div) (8) ++ * mf <= ext_clock * p1 / (int_clock_min * div) ++ * ++ * We can thus iterate over the restricted p1 range defined by the ++ * combination of (1) and (7), and then compute the restricted mf range ++ * defined by the combination of (2), (6) and (8). If the resulting mf ++ * range is not empty, any value in the mf range is acceptable. We thus ++ * select the mf lwoer bound and the corresponding p1 value. ++ */ ++ if (limits->p1_min == 0) { ++ dev_err(dev, "pll: P1 minimum value must be >0.\n"); ++ return -EINVAL; ++ } ++ ++ p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div, ++ pll->ext_clock * pll->m)); ++ p1_max = min(limits->p1_max, limits->out_clock_max * div / ++ (pll->ext_clock * pll->m)); ++ ++ for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) { ++ unsigned int mf_inc = p1 / gcd(div, p1); ++ unsigned int mf_high; ++ unsigned int mf_low; ++ ++ mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, ++ limits->int_clock_max * div)), mf_inc); ++ mf_high = min(mf_max, pll->ext_clock * p1 / ++ (limits->int_clock_min * div)); ++ ++ if (mf_low > mf_high) ++ continue; ++ ++ pll->n = div * mf_low / p1; ++ pll->m *= mf_low; ++ pll->p1 = p1; ++ dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1); ++ return 0; ++ } ++ ++ dev_err(dev, "pll: no valid N and P1 divisors found.\n"); ++ return -EINVAL; ++} ++EXPORT_SYMBOL_GPL(aptina_pll_calculate); ++ ++MODULE_DESCRIPTION("Aptina PLL Helpers"); ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/aptina-pll.h b/drivers/media/i2c/aptina-pll.h +new file mode 100644 +index 0000000..b370e34 +--- /dev/null ++++ b/drivers/media/i2c/aptina-pll.h +@@ -0,0 +1,56 @@ ++/* ++ * Aptina Sensor PLL Configuration ++ * ++ * Copyright (C) 2012 Laurent Pinchart ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef __APTINA_PLL_H ++#define __APTINA_PLL_H ++ ++struct aptina_pll { ++ unsigned int ext_clock; ++ unsigned int pix_clock; ++ ++ unsigned int n; ++ unsigned int m; ++ unsigned int p1; ++}; ++ ++struct aptina_pll_limits { ++ unsigned int ext_clock_min; ++ unsigned int ext_clock_max; ++ unsigned int int_clock_min; ++ unsigned int int_clock_max; ++ unsigned int out_clock_min; ++ unsigned int out_clock_max; ++ unsigned int pix_clock_max; ++ ++ unsigned int n_min; ++ unsigned int n_max; ++ unsigned int m_min; ++ unsigned int m_max; ++ unsigned int p1_min; ++ unsigned int p1_max; ++}; ++ ++struct device; ++ ++int aptina_pll_calculate(struct device *dev, ++ const struct aptina_pll_limits *limits, ++ struct aptina_pll *pll); ++ ++#endif /* __APTINA_PLL_H */ +diff --git a/drivers/media/i2c/as3645a.c b/drivers/media/i2c/as3645a.c +new file mode 100644 +index 0000000..3bfdbf9 +--- /dev/null ++++ b/drivers/media/i2c/as3645a.c +@@ -0,0 +1,888 @@ ++/* ++ * drivers/media/i2c/as3645a.c - AS3645A and LM3555 flash controllers driver ++ * ++ * Copyright (C) 2008-2011 Nokia Corporation ++ * Copyright (c) 2011, Intel Corporation. ++ * ++ * Contact: Laurent Pinchart ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ * TODO: ++ * - Check hardware FSTROBE control when sensor driver add support for this ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define AS_TIMER_MS_TO_CODE(t) (((t) - 100) / 50) ++#define AS_TIMER_CODE_TO_MS(c) (50 * (c) + 100) ++ ++/* Register definitions */ ++ ++/* Read-only Design info register: Reset state: xxxx 0001 */ ++#define AS_DESIGN_INFO_REG 0x00 ++#define AS_DESIGN_INFO_FACTORY(x) (((x) >> 4)) ++#define AS_DESIGN_INFO_MODEL(x) ((x) & 0x0f) ++ ++/* Read-only Version control register: Reset state: 0000 0000 ++ * for first engineering samples ++ */ ++#define AS_VERSION_CONTROL_REG 0x01 ++#define AS_VERSION_CONTROL_RFU(x) (((x) >> 4)) ++#define AS_VERSION_CONTROL_VERSION(x) ((x) & 0x0f) ++ ++/* Read / Write (Indicator and timer register): Reset state: 0000 1111 */ ++#define AS_INDICATOR_AND_TIMER_REG 0x02 ++#define AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT 0 ++#define AS_INDICATOR_AND_TIMER_VREF_SHIFT 4 ++#define AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT 6 ++ ++/* Read / Write (Current set register): Reset state: 0110 1001 */ ++#define AS_CURRENT_SET_REG 0x03 ++#define AS_CURRENT_ASSIST_LIGHT_SHIFT 0 ++#define AS_CURRENT_LED_DET_ON (1 << 3) ++#define AS_CURRENT_FLASH_CURRENT_SHIFT 4 ++ ++/* Read / Write (Control register): Reset state: 1011 0100 */ ++#define AS_CONTROL_REG 0x04 ++#define AS_CONTROL_MODE_SETTING_SHIFT 0 ++#define AS_CONTROL_STROBE_ON (1 << 2) ++#define AS_CONTROL_OUT_ON (1 << 3) ++#define AS_CONTROL_EXT_TORCH_ON (1 << 4) ++#define AS_CONTROL_STROBE_TYPE_EDGE (0 << 5) ++#define AS_CONTROL_STROBE_TYPE_LEVEL (1 << 5) ++#define AS_CONTROL_COIL_PEAK_SHIFT 6 ++ ++/* Read only (D3 is read / write) (Fault and info): Reset state: 0000 x000 */ ++#define AS_FAULT_INFO_REG 0x05 ++#define AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT (1 << 1) ++#define AS_FAULT_INFO_INDICATOR_LED (1 << 2) ++#define AS_FAULT_INFO_LED_AMOUNT (1 << 3) ++#define AS_FAULT_INFO_TIMEOUT (1 << 4) ++#define AS_FAULT_INFO_OVER_TEMPERATURE (1 << 5) ++#define AS_FAULT_INFO_SHORT_CIRCUIT (1 << 6) ++#define AS_FAULT_INFO_OVER_VOLTAGE (1 << 7) ++ ++/* Boost register */ ++#define AS_BOOST_REG 0x0d ++#define AS_BOOST_CURRENT_DISABLE (0 << 0) ++#define AS_BOOST_CURRENT_ENABLE (1 << 0) ++ ++/* Password register is used to unlock boost register writing */ ++#define AS_PASSWORD_REG 0x0f ++#define AS_PASSWORD_UNLOCK_VALUE 0x55 ++ ++enum as_mode { ++ AS_MODE_EXT_TORCH = 0 << AS_CONTROL_MODE_SETTING_SHIFT, ++ AS_MODE_INDICATOR = 1 << AS_CONTROL_MODE_SETTING_SHIFT, ++ AS_MODE_ASSIST = 2 << AS_CONTROL_MODE_SETTING_SHIFT, ++ AS_MODE_FLASH = 3 << AS_CONTROL_MODE_SETTING_SHIFT, ++}; ++ ++/* ++ * struct as3645a ++ * ++ * @subdev: V4L2 subdev ++ * @pdata: Flash platform data ++ * @power_lock: Protects power_count ++ * @power_count: Power reference count ++ * @led_mode: V4L2 flash LED mode ++ * @timeout: Flash timeout in microseconds ++ * @flash_current: Flash current (0=200mA ... 15=500mA). Maximum ++ * values are 400mA for two LEDs and 500mA for one LED. ++ * @assist_current: Torch/Assist light current (0=20mA, 1=40mA ... 7=160mA) ++ * @indicator_current: Indicator LED current (0=0mA, 1=2.5mA ... 4=10mA) ++ * @strobe_source: Flash strobe source (software or external) ++ */ ++struct as3645a { ++ struct v4l2_subdev subdev; ++ const struct as3645a_platform_data *pdata; ++ ++ struct mutex power_lock; ++ int power_count; ++ ++ /* Controls */ ++ struct v4l2_ctrl_handler ctrls; ++ ++ enum v4l2_flash_led_mode led_mode; ++ unsigned int timeout; ++ u8 flash_current; ++ u8 assist_current; ++ u8 indicator_current; ++ enum v4l2_flash_strobe_source strobe_source; ++}; ++ ++#define to_as3645a(sd) container_of(sd, struct as3645a, subdev) ++ ++/* Return negative errno else zero on success */ ++static int as3645a_write(struct as3645a *flash, u8 addr, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int rval; ++ ++ rval = i2c_smbus_write_byte_data(client, addr, val); ++ ++ dev_dbg(&client->dev, "Write Addr:%02X Val:%02X %s\n", addr, val, ++ rval < 0 ? "fail" : "ok"); ++ ++ return rval; ++} ++ ++/* Return negative errno else a data byte received from the device. */ ++static int as3645a_read(struct as3645a *flash, u8 addr) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int rval; ++ ++ rval = i2c_smbus_read_byte_data(client, addr); ++ ++ dev_dbg(&client->dev, "Read Addr:%02X Val:%02X %s\n", addr, rval, ++ rval < 0 ? "fail" : "ok"); ++ ++ return rval; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Hardware configuration and trigger ++ */ ++ ++/* ++ * as3645a_set_config - Set flash configuration registers ++ * @flash: The flash ++ * ++ * Configure the hardware with flash, assist and indicator currents, as well as ++ * flash timeout. ++ * ++ * Return 0 on success, or a negative error code if an I2C communication error ++ * occurred. ++ */ ++static int as3645a_set_config(struct as3645a *flash) ++{ ++ int ret; ++ u8 val; ++ ++ val = (flash->flash_current << AS_CURRENT_FLASH_CURRENT_SHIFT) ++ | (flash->assist_current << AS_CURRENT_ASSIST_LIGHT_SHIFT) ++ | AS_CURRENT_LED_DET_ON; ++ ++ ret = as3645a_write(flash, AS_CURRENT_SET_REG, val); ++ if (ret < 0) ++ return ret; ++ ++ val = AS_TIMER_MS_TO_CODE(flash->timeout / 1000) ++ << AS_INDICATOR_AND_TIMER_TIMEOUT_SHIFT; ++ ++ val |= (flash->pdata->vref << AS_INDICATOR_AND_TIMER_VREF_SHIFT) ++ | ((flash->indicator_current ? flash->indicator_current - 1 : 0) ++ << AS_INDICATOR_AND_TIMER_INDICATOR_SHIFT); ++ ++ return as3645a_write(flash, AS_INDICATOR_AND_TIMER_REG, val); ++} ++ ++/* ++ * as3645a_set_control - Set flash control register ++ * @flash: The flash ++ * @mode: Desired output mode ++ * @on: Desired output state ++ * ++ * Configure the hardware with output mode and state. ++ * ++ * Return 0 on success, or a negative error code if an I2C communication error ++ * occurred. ++ */ ++static int ++as3645a_set_control(struct as3645a *flash, enum as_mode mode, bool on) ++{ ++ u8 reg; ++ ++ /* Configure output parameters and operation mode. */ ++ reg = (flash->pdata->peak << AS_CONTROL_COIL_PEAK_SHIFT) ++ | (on ? AS_CONTROL_OUT_ON : 0) ++ | mode; ++ ++ if (flash->led_mode == V4L2_FLASH_LED_MODE_FLASH && ++ flash->strobe_source == V4L2_FLASH_STROBE_SOURCE_EXTERNAL) { ++ reg |= AS_CONTROL_STROBE_TYPE_LEVEL ++ | AS_CONTROL_STROBE_ON; ++ } ++ ++ return as3645a_write(flash, AS_CONTROL_REG, reg); ++} ++ ++/* ++ * as3645a_set_output - Configure output and operation mode ++ * @flash: Flash controller ++ * @strobe: Strobe the flash (only valid in flash mode) ++ * ++ * Turn the LEDs output on/off and set the operation mode based on the current ++ * parameters. ++ * ++ * The AS3645A can't control the indicator LED independently of the flash/torch ++ * LED. If the flash controller is in V4L2_FLASH_LED_MODE_NONE mode, set the ++ * chip to indicator mode. Otherwise set it to assist light (torch) or flash ++ * mode. ++ * ++ * In indicator and assist modes, turn the output on/off based on the indicator ++ * and torch currents. In software strobe flash mode, turn the output on/off ++ * based on the strobe parameter. ++ */ ++static int as3645a_set_output(struct as3645a *flash, bool strobe) ++{ ++ enum as_mode mode; ++ bool on; ++ ++ switch (flash->led_mode) { ++ case V4L2_FLASH_LED_MODE_NONE: ++ on = flash->indicator_current != 0; ++ mode = AS_MODE_INDICATOR; ++ break; ++ case V4L2_FLASH_LED_MODE_TORCH: ++ on = true; ++ mode = AS_MODE_ASSIST; ++ break; ++ case V4L2_FLASH_LED_MODE_FLASH: ++ on = strobe; ++ mode = AS_MODE_FLASH; ++ break; ++ default: ++ BUG(); ++ } ++ ++ /* Configure output parameters and operation mode. */ ++ return as3645a_set_control(flash, mode, on); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 controls ++ */ ++ ++static int as3645a_is_active(struct as3645a *flash) ++{ ++ int ret; ++ ++ ret = as3645a_read(flash, AS_CONTROL_REG); ++ return ret < 0 ? ret : !!(ret & AS_CONTROL_OUT_ON); ++} ++ ++static int as3645a_read_fault(struct as3645a *flash) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int rval; ++ ++ /* NOTE: reading register clear fault status */ ++ rval = as3645a_read(flash, AS_FAULT_INFO_REG); ++ if (rval < 0) ++ return rval; ++ ++ if (rval & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) ++ dev_dbg(&client->dev, "Inductor Peak limit fault\n"); ++ ++ if (rval & AS_FAULT_INFO_INDICATOR_LED) ++ dev_dbg(&client->dev, "Indicator LED fault: " ++ "Short circuit or open loop\n"); ++ ++ dev_dbg(&client->dev, "%u connected LEDs\n", ++ rval & AS_FAULT_INFO_LED_AMOUNT ? 2 : 1); ++ ++ if (rval & AS_FAULT_INFO_TIMEOUT) ++ dev_dbg(&client->dev, "Timeout fault\n"); ++ ++ if (rval & AS_FAULT_INFO_OVER_TEMPERATURE) ++ dev_dbg(&client->dev, "Over temperature fault\n"); ++ ++ if (rval & AS_FAULT_INFO_SHORT_CIRCUIT) ++ dev_dbg(&client->dev, "Short circuit fault\n"); ++ ++ if (rval & AS_FAULT_INFO_OVER_VOLTAGE) ++ dev_dbg(&client->dev, "Over voltage fault: " ++ "Indicates missing capacitor or open connection\n"); ++ ++ return rval; ++} ++ ++static int as3645a_get_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct as3645a *flash = ++ container_of(ctrl->handler, struct as3645a, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int value; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_FLASH_FAULT: ++ value = as3645a_read_fault(flash); ++ if (value < 0) ++ return value; ++ ++ ctrl->cur.val = 0; ++ if (value & AS_FAULT_INFO_SHORT_CIRCUIT) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_SHORT_CIRCUIT; ++ if (value & AS_FAULT_INFO_OVER_TEMPERATURE) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_TEMPERATURE; ++ if (value & AS_FAULT_INFO_TIMEOUT) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_TIMEOUT; ++ if (value & AS_FAULT_INFO_OVER_VOLTAGE) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_VOLTAGE; ++ if (value & AS_FAULT_INFO_INDUCTOR_PEAK_LIMIT) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_OVER_CURRENT; ++ if (value & AS_FAULT_INFO_INDICATOR_LED) ++ ctrl->cur.val |= V4L2_FLASH_FAULT_INDICATOR; ++ break; ++ ++ case V4L2_CID_FLASH_STROBE_STATUS: ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) { ++ ctrl->cur.val = 0; ++ break; ++ } ++ ++ value = as3645a_is_active(flash); ++ if (value < 0) ++ return value; ++ ++ ctrl->cur.val = value; ++ break; ++ } ++ ++ dev_dbg(&client->dev, "G_CTRL %08x:%d\n", ctrl->id, ctrl->cur.val); ++ ++ return 0; ++} ++ ++static int as3645a_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct as3645a *flash = ++ container_of(ctrl->handler, struct as3645a, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int ret; ++ ++ dev_dbg(&client->dev, "S_CTRL %08x:%d\n", ctrl->id, ctrl->val); ++ ++ /* If a control that doesn't apply to the current mode is modified, ++ * we store the value and return immediately. The setting will be ++ * applied when the LED mode is changed. Otherwise we apply the setting ++ * immediately. ++ */ ++ ++ switch (ctrl->id) { ++ case V4L2_CID_FLASH_LED_MODE: ++ if (flash->indicator_current) ++ return -EBUSY; ++ ++ ret = as3645a_set_config(flash); ++ if (ret < 0) ++ return ret; ++ ++ flash->led_mode = ctrl->val; ++ return as3645a_set_output(flash, false); ++ ++ case V4L2_CID_FLASH_STROBE_SOURCE: ++ flash->strobe_source = ctrl->val; ++ ++ /* Applies to flash mode only. */ ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) ++ break; ++ ++ return as3645a_set_output(flash, false); ++ ++ case V4L2_CID_FLASH_STROBE: ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) ++ return -EBUSY; ++ ++ return as3645a_set_output(flash, true); ++ ++ case V4L2_CID_FLASH_STROBE_STOP: ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) ++ return -EBUSY; ++ ++ return as3645a_set_output(flash, false); ++ ++ case V4L2_CID_FLASH_TIMEOUT: ++ flash->timeout = ctrl->val; ++ ++ /* Applies to flash mode only. */ ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) ++ break; ++ ++ return as3645a_set_config(flash); ++ ++ case V4L2_CID_FLASH_INTENSITY: ++ flash->flash_current = (ctrl->val - AS3645A_FLASH_INTENSITY_MIN) ++ / AS3645A_FLASH_INTENSITY_STEP; ++ ++ /* Applies to flash mode only. */ ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) ++ break; ++ ++ return as3645a_set_config(flash); ++ ++ case V4L2_CID_FLASH_TORCH_INTENSITY: ++ flash->assist_current = ++ (ctrl->val - AS3645A_TORCH_INTENSITY_MIN) ++ / AS3645A_TORCH_INTENSITY_STEP; ++ ++ /* Applies to torch mode only. */ ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_TORCH) ++ break; ++ ++ return as3645a_set_config(flash); ++ ++ case V4L2_CID_FLASH_INDICATOR_INTENSITY: ++ if (flash->led_mode != V4L2_FLASH_LED_MODE_NONE) ++ return -EBUSY; ++ ++ flash->indicator_current = ++ (ctrl->val - AS3645A_INDICATOR_INTENSITY_MIN) ++ / AS3645A_INDICATOR_INTENSITY_STEP; ++ ++ ret = as3645a_set_config(flash); ++ if (ret < 0) ++ return ret; ++ ++ if ((ctrl->val == 0) == (ctrl->cur.val == 0)) ++ break; ++ ++ return as3645a_set_output(flash, false); ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops as3645a_ctrl_ops = { ++ .g_volatile_ctrl = as3645a_get_ctrl, ++ .s_ctrl = as3645a_set_ctrl, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev core operations ++ */ ++ ++/* Put device into know state. */ ++static int as3645a_setup(struct as3645a *flash) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&flash->subdev); ++ int ret; ++ ++ /* clear errors */ ++ ret = as3645a_read(flash, AS_FAULT_INFO_REG); ++ if (ret < 0) ++ return ret; ++ ++ dev_dbg(&client->dev, "Fault info: %02x\n", ret); ++ ++ ret = as3645a_set_config(flash); ++ if (ret < 0) ++ return ret; ++ ++ ret = as3645a_set_output(flash, false); ++ if (ret < 0) ++ return ret; ++ ++ /* read status */ ++ ret = as3645a_read_fault(flash); ++ if (ret < 0) ++ return ret; ++ ++ dev_dbg(&client->dev, "AS_INDICATOR_AND_TIMER_REG: %02x\n", ++ as3645a_read(flash, AS_INDICATOR_AND_TIMER_REG)); ++ dev_dbg(&client->dev, "AS_CURRENT_SET_REG: %02x\n", ++ as3645a_read(flash, AS_CURRENT_SET_REG)); ++ dev_dbg(&client->dev, "AS_CONTROL_REG: %02x\n", ++ as3645a_read(flash, AS_CONTROL_REG)); ++ ++ return ret & ~AS_FAULT_INFO_LED_AMOUNT ? -EIO : 0; ++} ++ ++static int __as3645a_set_power(struct as3645a *flash, int on) ++{ ++ int ret; ++ ++ if (!on) ++ as3645a_set_control(flash, AS_MODE_EXT_TORCH, false); ++ ++ if (flash->pdata->set_power) { ++ ret = flash->pdata->set_power(&flash->subdev, on); ++ if (ret < 0) ++ return ret; ++ } ++ ++ if (!on) ++ return 0; ++ ++ ret = as3645a_setup(flash); ++ if (ret < 0) { ++ if (flash->pdata->set_power) ++ flash->pdata->set_power(&flash->subdev, 0); ++ } ++ ++ return ret; ++} ++ ++static int as3645a_set_power(struct v4l2_subdev *sd, int on) ++{ ++ struct as3645a *flash = to_as3645a(sd); ++ int ret = 0; ++ ++ mutex_lock(&flash->power_lock); ++ ++ if (flash->power_count == !on) { ++ ret = __as3645a_set_power(flash, !!on); ++ if (ret < 0) ++ goto done; ++ } ++ ++ flash->power_count += on ? 1 : -1; ++ WARN_ON(flash->power_count < 0); ++ ++done: ++ mutex_unlock(&flash->power_lock); ++ return ret; ++} ++ ++static int as3645a_registered(struct v4l2_subdev *sd) ++{ ++ struct as3645a *flash = to_as3645a(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int rval, man, model, rfu, version; ++ const char *vendor; ++ ++ /* Power up the flash driver and read manufacturer ID, model ID, RFU ++ * and version. ++ */ ++ rval = as3645a_set_power(&flash->subdev, 1); ++ if (rval < 0) ++ return rval; ++ ++ rval = as3645a_read(flash, AS_DESIGN_INFO_REG); ++ if (rval < 0) ++ goto power_off; ++ ++ man = AS_DESIGN_INFO_FACTORY(rval); ++ model = AS_DESIGN_INFO_MODEL(rval); ++ ++ rval = as3645a_read(flash, AS_VERSION_CONTROL_REG); ++ if (rval < 0) ++ goto power_off; ++ ++ rfu = AS_VERSION_CONTROL_RFU(rval); ++ version = AS_VERSION_CONTROL_VERSION(rval); ++ ++ /* Verify the chip model and version. */ ++ if (model != 0x01 || rfu != 0x00) { ++ dev_err(&client->dev, "AS3645A not detected " ++ "(model %d rfu %d)\n", model, rfu); ++ rval = -ENODEV; ++ goto power_off; ++ } ++ ++ switch (man) { ++ case 1: ++ vendor = "AMS, Austria Micro Systems"; ++ break; ++ case 2: ++ vendor = "ADI, Analog Devices Inc."; ++ break; ++ case 3: ++ vendor = "NSC, National Semiconductor"; ++ break; ++ case 4: ++ vendor = "NXP"; ++ break; ++ case 5: ++ vendor = "TI, Texas Instrument"; ++ break; ++ default: ++ vendor = "Unknown"; ++ } ++ ++ dev_info(&client->dev, "Chip vendor: %s (%d) Version: %d\n", vendor, ++ man, version); ++ ++ rval = as3645a_write(flash, AS_PASSWORD_REG, AS_PASSWORD_UNLOCK_VALUE); ++ if (rval < 0) ++ goto power_off; ++ ++ rval = as3645a_write(flash, AS_BOOST_REG, AS_BOOST_CURRENT_DISABLE); ++ if (rval < 0) ++ goto power_off; ++ ++ /* Setup default values. This makes sure that the chip is in a known ++ * state, in case the power rail can't be controlled. ++ */ ++ rval = as3645a_setup(flash); ++ ++power_off: ++ as3645a_set_power(&flash->subdev, 0); ++ ++ return rval; ++} ++ ++static int as3645a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ return as3645a_set_power(sd, 1); ++} ++ ++static int as3645a_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ return as3645a_set_power(sd, 0); ++} ++ ++static const struct v4l2_subdev_core_ops as3645a_core_ops = { ++ .s_power = as3645a_set_power, ++}; ++ ++static const struct v4l2_subdev_ops as3645a_ops = { ++ .core = &as3645a_core_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops as3645a_internal_ops = { ++ .registered = as3645a_registered, ++ .open = as3645a_open, ++ .close = as3645a_close, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * I2C driver ++ */ ++#ifdef CONFIG_PM ++ ++static int as3645a_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct as3645a *flash = to_as3645a(subdev); ++ int rval; ++ ++ if (flash->power_count == 0) ++ return 0; ++ ++ rval = __as3645a_set_power(flash, 0); ++ ++ dev_dbg(&client->dev, "Suspend %s\n", rval < 0 ? "failed" : "ok"); ++ ++ return rval; ++} ++ ++static int as3645a_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct as3645a *flash = to_as3645a(subdev); ++ int rval; ++ ++ if (flash->power_count == 0) ++ return 0; ++ ++ rval = __as3645a_set_power(flash, 1); ++ ++ dev_dbg(&client->dev, "Resume %s\n", rval < 0 ? "fail" : "ok"); ++ ++ return rval; ++} ++ ++#else ++ ++#define as3645a_suspend NULL ++#define as3645a_resume NULL ++ ++#endif /* CONFIG_PM */ ++ ++/* ++ * as3645a_init_controls - Create controls ++ * @flash: The flash ++ * ++ * The number of LEDs reported in platform data is used to compute default ++ * limits. Parameters passed through platform data can override those limits. ++ */ ++static int __devinit as3645a_init_controls(struct as3645a *flash) ++{ ++ const struct as3645a_platform_data *pdata = flash->pdata; ++ struct v4l2_ctrl *ctrl; ++ int maximum; ++ ++ v4l2_ctrl_handler_init(&flash->ctrls, 10); ++ ++ /* V4L2_CID_FLASH_LED_MODE */ ++ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_LED_MODE, 2, ~7, ++ V4L2_FLASH_LED_MODE_NONE); ++ ++ /* V4L2_CID_FLASH_STROBE_SOURCE */ ++ v4l2_ctrl_new_std_menu(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_STROBE_SOURCE, ++ pdata->ext_strobe ? 1 : 0, ++ pdata->ext_strobe ? ~3 : ~1, ++ V4L2_FLASH_STROBE_SOURCE_SOFTWARE); ++ ++ flash->strobe_source = V4L2_FLASH_STROBE_SOURCE_SOFTWARE; ++ ++ /* V4L2_CID_FLASH_STROBE */ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_STROBE, 0, 0, 0, 0); ++ ++ /* V4L2_CID_FLASH_STROBE_STOP */ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_STROBE_STOP, 0, 0, 0, 0); ++ ++ /* V4L2_CID_FLASH_STROBE_STATUS */ ++ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_STROBE_STATUS, 0, 1, 1, 1); ++ if (ctrl != NULL) ++ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; ++ ++ /* V4L2_CID_FLASH_TIMEOUT */ ++ maximum = pdata->timeout_max; ++ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_TIMEOUT, AS3645A_FLASH_TIMEOUT_MIN, ++ maximum, AS3645A_FLASH_TIMEOUT_STEP, maximum); ++ ++ flash->timeout = maximum; ++ ++ /* V4L2_CID_FLASH_INTENSITY */ ++ maximum = pdata->flash_max_current; ++ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_INTENSITY, AS3645A_FLASH_INTENSITY_MIN, ++ maximum, AS3645A_FLASH_INTENSITY_STEP, maximum); ++ ++ flash->flash_current = (maximum - AS3645A_FLASH_INTENSITY_MIN) ++ / AS3645A_FLASH_INTENSITY_STEP; ++ ++ /* V4L2_CID_FLASH_TORCH_INTENSITY */ ++ maximum = pdata->torch_max_current; ++ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_TORCH_INTENSITY, ++ AS3645A_TORCH_INTENSITY_MIN, maximum, ++ AS3645A_TORCH_INTENSITY_STEP, ++ AS3645A_TORCH_INTENSITY_MIN); ++ ++ flash->assist_current = 0; ++ ++ /* V4L2_CID_FLASH_INDICATOR_INTENSITY */ ++ v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_INDICATOR_INTENSITY, ++ AS3645A_INDICATOR_INTENSITY_MIN, ++ AS3645A_INDICATOR_INTENSITY_MAX, ++ AS3645A_INDICATOR_INTENSITY_STEP, ++ AS3645A_INDICATOR_INTENSITY_MIN); ++ ++ flash->indicator_current = 0; ++ ++ /* V4L2_CID_FLASH_FAULT */ ++ ctrl = v4l2_ctrl_new_std(&flash->ctrls, &as3645a_ctrl_ops, ++ V4L2_CID_FLASH_FAULT, 0, ++ V4L2_FLASH_FAULT_OVER_VOLTAGE | ++ V4L2_FLASH_FAULT_TIMEOUT | ++ V4L2_FLASH_FAULT_OVER_TEMPERATURE | ++ V4L2_FLASH_FAULT_SHORT_CIRCUIT, 0, 0); ++ if (ctrl != NULL) ++ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; ++ ++ flash->subdev.ctrl_handler = &flash->ctrls; ++ ++ return flash->ctrls.error; ++} ++ ++static int __devinit as3645a_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct as3645a *flash; ++ int ret; ++ ++ if (client->dev.platform_data == NULL) ++ return -ENODEV; ++ ++ flash = kzalloc(sizeof(*flash), GFP_KERNEL); ++ if (flash == NULL) ++ return -ENOMEM; ++ ++ flash->pdata = client->dev.platform_data; ++ ++ v4l2_i2c_subdev_init(&flash->subdev, client, &as3645a_ops); ++ flash->subdev.internal_ops = &as3645a_internal_ops; ++ flash->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ ret = as3645a_init_controls(flash); ++ if (ret < 0) ++ goto done; ++ ++ ret = media_entity_init(&flash->subdev.entity, 0, NULL, 0); ++ if (ret < 0) ++ goto done; ++ ++ flash->subdev.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_FLASH; ++ ++ mutex_init(&flash->power_lock); ++ ++ flash->led_mode = V4L2_FLASH_LED_MODE_NONE; ++ ++done: ++ if (ret < 0) { ++ v4l2_ctrl_handler_free(&flash->ctrls); ++ kfree(flash); ++ } ++ ++ return ret; ++} ++ ++static int __devexit as3645a_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct as3645a *flash = to_as3645a(subdev); ++ ++ v4l2_device_unregister_subdev(subdev); ++ v4l2_ctrl_handler_free(&flash->ctrls); ++ media_entity_cleanup(&flash->subdev.entity); ++ mutex_destroy(&flash->power_lock); ++ kfree(flash); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id as3645a_id_table[] = { ++ { AS3645A_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, as3645a_id_table); ++ ++static const struct dev_pm_ops as3645a_pm_ops = { ++ .suspend = as3645a_suspend, ++ .resume = as3645a_resume, ++}; ++ ++static struct i2c_driver as3645a_i2c_driver = { ++ .driver = { ++ .name = AS3645A_NAME, ++ .pm = &as3645a_pm_ops, ++ }, ++ .probe = as3645a_probe, ++ .remove = __devexit_p(as3645a_remove), ++ .id_table = as3645a_id_table, ++}; ++ ++module_i2c_driver(as3645a_i2c_driver); ++ ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c +new file mode 100644 +index 0000000..377bf05 +--- /dev/null ++++ b/drivers/media/i2c/bt819.c +@@ -0,0 +1,517 @@ ++/* ++ * bt819 - BT819A VideoStream Decoder (Rockwell Part) ++ * ++ * Copyright (C) 1999 Mike Bernson ++ * Copyright (C) 1998 Dave Perks ++ * ++ * Modifications for LML33/DC10plus unified driver ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (9/9/2002) ++ * ++ * This code was modify/ported from the saa7111 driver written ++ * by Dave Perks. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); ++MODULE_AUTHOR("Mike Bernson & Dave Perks"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct bt819 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ unsigned char reg[32]; ++ ++ v4l2_std_id norm; ++ int ident; ++ int input; ++ int enable; ++}; ++ ++static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct bt819, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct bt819, hdl)->sd; ++} ++ ++struct timing { ++ int hactive; ++ int hdelay; ++ int vactive; ++ int vdelay; ++ int hscale; ++ int vscale; ++}; ++ ++/* for values, see the bt819 datasheet */ ++static struct timing timing_data[] = { ++ {864 - 24, 20, 625 - 2, 1, 0x0504, 0x0000}, ++ {858 - 24, 20, 525 - 2, 1, 0x00f8, 0x0000}, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); ++ ++ decoder->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) ++{ ++ return bt819_write(decoder, reg, ++ (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); ++} ++ ++static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); ++ int ret = -1; ++ u8 reg; ++ ++ /* the bt819 has an autoincrement function, use it if ++ * the adapter understands raw I2C */ ++ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ /* do raw I2C, not smbus compatible */ ++ u8 block_data[32]; ++ int block_len; ++ ++ while (len >= 2) { ++ block_len = 0; ++ block_data[block_len++] = reg = data[0]; ++ do { ++ block_data[block_len++] = ++ decoder->reg[reg++] = data[1]; ++ len -= 2; ++ data += 2; ++ } while (len >= 2 && data[0] == reg && block_len < 32); ++ ret = i2c_master_send(client, block_data, block_len); ++ if (ret < 0) ++ break; ++ } ++ } else { ++ /* do some slow I2C emulation kind of thing */ ++ while (len >= 2) { ++ reg = *data++; ++ ret = bt819_write(decoder, reg, *data++); ++ if (ret < 0) ++ break; ++ len -= 2; ++ } ++ } ++ ++ return ret; ++} ++ ++static inline int bt819_read(struct bt819 *decoder, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int bt819_init(struct v4l2_subdev *sd) ++{ ++ static unsigned char init[] = { ++ /*0x1f, 0x00,*/ /* Reset */ ++ 0x01, 0x59, /* 0x01 input format */ ++ 0x02, 0x00, /* 0x02 temporal decimation */ ++ 0x03, 0x12, /* 0x03 Cropping msb */ ++ 0x04, 0x16, /* 0x04 Vertical Delay, lsb */ ++ 0x05, 0xe0, /* 0x05 Vertical Active lsb */ ++ 0x06, 0x80, /* 0x06 Horizontal Delay lsb */ ++ 0x07, 0xd0, /* 0x07 Horizontal Active lsb */ ++ 0x08, 0x00, /* 0x08 Horizontal Scaling msb */ ++ 0x09, 0xf8, /* 0x09 Horizontal Scaling lsb */ ++ 0x0a, 0x00, /* 0x0a Brightness control */ ++ 0x0b, 0x30, /* 0x0b Miscellaneous control */ ++ 0x0c, 0xd8, /* 0x0c Luma Gain lsb */ ++ 0x0d, 0xfe, /* 0x0d Chroma Gain (U) lsb */ ++ 0x0e, 0xb4, /* 0x0e Chroma Gain (V) msb */ ++ 0x0f, 0x00, /* 0x0f Hue control */ ++ 0x12, 0x04, /* 0x12 Output Format */ ++ 0x13, 0x20, /* 0x13 Vertial Scaling msb 0x00 ++ chroma comb OFF, line drop scaling, interlace scaling ++ BUG? Why does turning the chroma comb on fuck up color? ++ Bug in the bt819 stepping on my board? ++ */ ++ 0x14, 0x00, /* 0x14 Vertial Scaling lsb */ ++ 0x16, 0x07, /* 0x16 Video Timing Polarity ++ ACTIVE=active low ++ FIELD: high=odd, ++ vreset=active high, ++ hreset=active high */ ++ 0x18, 0x68, /* 0x18 AGC Delay */ ++ 0x19, 0x5d, /* 0x19 Burst Gate Delay */ ++ 0x1a, 0x80, /* 0x1a ADC Interface */ ++ }; ++ ++ struct bt819 *decoder = to_bt819(sd); ++ struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; ++ ++ init[0x03 * 2 - 1] = ++ (((timing->vdelay >> 8) & 0x03) << 6) | ++ (((timing->vactive >> 8) & 0x03) << 4) | ++ (((timing->hdelay >> 8) & 0x03) << 2) | ++ ((timing->hactive >> 8) & 0x03); ++ init[0x04 * 2 - 1] = timing->vdelay & 0xff; ++ init[0x05 * 2 - 1] = timing->vactive & 0xff; ++ init[0x06 * 2 - 1] = timing->hdelay & 0xff; ++ init[0x07 * 2 - 1] = timing->hactive & 0xff; ++ init[0x08 * 2 - 1] = timing->hscale >> 8; ++ init[0x09 * 2 - 1] = timing->hscale & 0xff; ++ /* 0x15 in array is address 0x19 */ ++ init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ ++ /* reset */ ++ bt819_write(decoder, 0x1f, 0x00); ++ mdelay(1); ++ ++ /* init */ ++ return bt819_write_block(decoder, init, sizeof(init)); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) ++{ ++ struct bt819 *decoder = to_bt819(sd); ++ int status = bt819_read(decoder, 0x00); ++ int res = V4L2_IN_ST_NO_SIGNAL; ++ v4l2_std_id std; ++ ++ if ((status & 0x80)) ++ res = 0; ++ ++ if ((status & 0x10)) ++ std = V4L2_STD_PAL; ++ else ++ std = V4L2_STD_NTSC; ++ if (pstd) ++ *pstd = std; ++ if (pstatus) ++ *pstatus = res; ++ ++ v4l2_dbg(1, debug, sd, "get status %x\n", status); ++ return 0; ++} ++ ++static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ return bt819_status(sd, NULL, std); ++} ++ ++static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ return bt819_status(sd, status, NULL); ++} ++ ++static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct bt819 *decoder = to_bt819(sd); ++ struct timing *timing = NULL; ++ ++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); ++ ++ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) ++ v4l2_err(sd, "no notify found!\n"); ++ ++ if (std & V4L2_STD_NTSC) { ++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); ++ bt819_setbit(decoder, 0x01, 0, 1); ++ bt819_setbit(decoder, 0x01, 1, 0); ++ bt819_setbit(decoder, 0x01, 5, 0); ++ bt819_write(decoder, 0x18, 0x68); ++ bt819_write(decoder, 0x19, 0x5d); ++ /* bt819_setbit(decoder, 0x1a, 5, 1); */ ++ timing = &timing_data[1]; ++ } else if (std & V4L2_STD_PAL) { ++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); ++ bt819_setbit(decoder, 0x01, 0, 1); ++ bt819_setbit(decoder, 0x01, 1, 1); ++ bt819_setbit(decoder, 0x01, 5, 1); ++ bt819_write(decoder, 0x18, 0x7f); ++ bt819_write(decoder, 0x19, 0x72); ++ /* bt819_setbit(decoder, 0x1a, 5, 0); */ ++ timing = &timing_data[0]; ++ } else { ++ v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", ++ (unsigned long long)std); ++ return -EINVAL; ++ } ++ bt819_write(decoder, 0x03, ++ (((timing->vdelay >> 8) & 0x03) << 6) | ++ (((timing->vactive >> 8) & 0x03) << 4) | ++ (((timing->hdelay >> 8) & 0x03) << 2) | ++ ((timing->hactive >> 8) & 0x03)); ++ bt819_write(decoder, 0x04, timing->vdelay & 0xff); ++ bt819_write(decoder, 0x05, timing->vactive & 0xff); ++ bt819_write(decoder, 0x06, timing->hdelay & 0xff); ++ bt819_write(decoder, 0x07, timing->hactive & 0xff); ++ bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); ++ bt819_write(decoder, 0x09, timing->hscale & 0xff); ++ decoder->norm = std; ++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); ++ return 0; ++} ++ ++static int bt819_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct bt819 *decoder = to_bt819(sd); ++ ++ v4l2_dbg(1, debug, sd, "set input %x\n", input); ++ ++ if (input > 7) ++ return -EINVAL; ++ ++ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) ++ v4l2_err(sd, "no notify found!\n"); ++ ++ if (decoder->input != input) { ++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); ++ decoder->input = input; ++ /* select mode */ ++ if (decoder->input == 0) { ++ bt819_setbit(decoder, 0x0b, 6, 0); ++ bt819_setbit(decoder, 0x1a, 1, 1); ++ } else { ++ bt819_setbit(decoder, 0x0b, 6, 1); ++ bt819_setbit(decoder, 0x1a, 1, 0); ++ } ++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); ++ } ++ return 0; ++} ++ ++static int bt819_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct bt819 *decoder = to_bt819(sd); ++ ++ v4l2_dbg(1, debug, sd, "enable output %x\n", enable); ++ ++ if (decoder->enable != enable) { ++ decoder->enable = enable; ++ bt819_setbit(decoder, 0x16, 7, !enable); ++ } ++ return 0; ++} ++ ++static int bt819_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct bt819 *decoder = to_bt819(sd); ++ int temp; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ bt819_write(decoder, 0x0a, ctrl->val); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ bt819_write(decoder, 0x0c, ctrl->val & 0xff); ++ bt819_setbit(decoder, 0x0b, 2, ((ctrl->val >> 8) & 0x01)); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ bt819_write(decoder, 0x0d, (ctrl->val >> 7) & 0xff); ++ bt819_setbit(decoder, 0x0b, 1, ((ctrl->val >> 15) & 0x01)); ++ ++ /* Ratio between U gain and V gain must stay the same as ++ the ratio between the default U and V gain values. */ ++ temp = (ctrl->val * 180) / 254; ++ bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); ++ bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); ++ break; ++ ++ case V4L2_CID_HUE: ++ bt819_write(decoder, 0x0f, ctrl->val); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct bt819 *decoder = to_bt819(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops bt819_ctrl_ops = { ++ .s_ctrl = bt819_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops bt819_core_ops = { ++ .g_chip_ident = bt819_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = bt819_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops bt819_video_ops = { ++ .s_routing = bt819_s_routing, ++ .s_stream = bt819_s_stream, ++ .querystd = bt819_querystd, ++ .g_input_status = bt819_g_input_status, ++}; ++ ++static const struct v4l2_subdev_ops bt819_ops = { ++ .core = &bt819_core_ops, ++ .video = &bt819_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int bt819_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int i, ver; ++ struct bt819 *decoder; ++ struct v4l2_subdev *sd; ++ const char *name; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); ++ if (decoder == NULL) ++ return -ENOMEM; ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &bt819_ops); ++ ++ ver = bt819_read(decoder, 0x17); ++ switch (ver & 0xf0) { ++ case 0x70: ++ name = "bt819a"; ++ decoder->ident = V4L2_IDENT_BT819A; ++ break; ++ case 0x60: ++ name = "bt817a"; ++ decoder->ident = V4L2_IDENT_BT817A; ++ break; ++ case 0x20: ++ name = "bt815a"; ++ decoder->ident = V4L2_IDENT_BT815A; ++ break; ++ default: ++ v4l2_dbg(1, debug, sd, ++ "unknown chip version 0x%02x\n", ver); ++ return -ENODEV; ++ } ++ ++ v4l_info(client, "%s found @ 0x%x (%s)\n", name, ++ client->addr << 1, client->adapter->name); ++ ++ decoder->norm = V4L2_STD_NTSC; ++ decoder->input = 0; ++ decoder->enable = 1; ++ ++ i = bt819_init(sd); ++ if (i < 0) ++ v4l2_dbg(1, debug, sd, "init status %d\n", i); ++ ++ v4l2_ctrl_handler_init(&decoder->hdl, 4); ++ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); ++ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 511, 1, 0xd8); ++ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 511, 1, 0xfe); ++ v4l2_ctrl_new_std(&decoder->hdl, &bt819_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ sd->ctrl_handler = &decoder->hdl; ++ if (decoder->hdl.error) { ++ int err = decoder->hdl.error; ++ ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&decoder->hdl); ++ return 0; ++} ++ ++static int bt819_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct bt819 *decoder = to_bt819(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id bt819_id[] = { ++ { "bt819a", 0 }, ++ { "bt817a", 0 }, ++ { "bt815a", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, bt819_id); ++ ++static struct i2c_driver bt819_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "bt819", ++ }, ++ .probe = bt819_probe, ++ .remove = bt819_remove, ++ .id_table = bt819_id, ++}; ++ ++module_i2c_driver(bt819_driver); +diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c +new file mode 100644 +index 0000000..7e5bd36 +--- /dev/null ++++ b/drivers/media/i2c/bt856.c +@@ -0,0 +1,273 @@ ++/* ++ * bt856 - BT856A Digital Video Encoder (Rockwell Part) ++ * ++ * Copyright (C) 1999 Mike Bernson ++ * Copyright (C) 1998 Dave Perks ++ * ++ * Modifications for LML33/DC10plus unified driver ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * This code was modify/ported from the saa7111 driver written ++ * by Dave Perks. ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (9/9/2002) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); ++MODULE_AUTHOR("Mike Bernson & Dave Perks"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++#define BT856_REG_OFFSET 0xDA ++#define BT856_NR_REG 6 ++ ++struct bt856 { ++ struct v4l2_subdev sd; ++ unsigned char reg[BT856_NR_REG]; ++ ++ v4l2_std_id norm; ++}; ++ ++static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct bt856, sd); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); ++ ++ encoder->reg[reg - BT856_REG_OFFSET] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) ++{ ++ return bt856_write(encoder, reg, ++ (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | ++ (value ? (1 << bit) : 0)); ++} ++ ++static void bt856_dump(struct bt856 *encoder) ++{ ++ int i; ++ ++ v4l2_info(&encoder->sd, "register dump:\n"); ++ for (i = 0; i < BT856_NR_REG; i += 2) ++ printk(KERN_CONT " %02x", encoder->reg[i]); ++ printk(KERN_CONT "\n"); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int bt856_init(struct v4l2_subdev *sd, u32 arg) ++{ ++ struct bt856 *encoder = to_bt856(sd); ++ ++ /* This is just for testing!!! */ ++ v4l2_dbg(1, debug, sd, "init\n"); ++ bt856_write(encoder, 0xdc, 0x18); ++ bt856_write(encoder, 0xda, 0); ++ bt856_write(encoder, 0xde, 0); ++ ++ bt856_setbit(encoder, 0xdc, 3, 1); ++ /*bt856_setbit(encoder, 0xdc, 6, 0);*/ ++ bt856_setbit(encoder, 0xdc, 4, 1); ++ ++ if (encoder->norm & V4L2_STD_NTSC) ++ bt856_setbit(encoder, 0xdc, 2, 0); ++ else ++ bt856_setbit(encoder, 0xdc, 2, 1); ++ ++ bt856_setbit(encoder, 0xdc, 1, 1); ++ bt856_setbit(encoder, 0xde, 4, 0); ++ bt856_setbit(encoder, 0xde, 3, 1); ++ if (debug != 0) ++ bt856_dump(encoder); ++ return 0; ++} ++ ++static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct bt856 *encoder = to_bt856(sd); ++ ++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); ++ ++ if (std & V4L2_STD_NTSC) { ++ bt856_setbit(encoder, 0xdc, 2, 0); ++ } else if (std & V4L2_STD_PAL) { ++ bt856_setbit(encoder, 0xdc, 2, 1); ++ bt856_setbit(encoder, 0xda, 0, 0); ++ /*bt856_setbit(encoder, 0xda, 0, 1);*/ ++ } else { ++ return -EINVAL; ++ } ++ encoder->norm = std; ++ if (debug != 0) ++ bt856_dump(encoder); ++ return 0; ++} ++ ++static int bt856_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct bt856 *encoder = to_bt856(sd); ++ ++ v4l2_dbg(1, debug, sd, "set input %d\n", input); ++ ++ /* We only have video bus. ++ * input= 0: input is from bt819 ++ * input= 1: input is from ZR36060 */ ++ switch (input) { ++ case 0: ++ bt856_setbit(encoder, 0xde, 4, 0); ++ bt856_setbit(encoder, 0xde, 3, 1); ++ bt856_setbit(encoder, 0xdc, 3, 1); ++ bt856_setbit(encoder, 0xdc, 6, 0); ++ break; ++ case 1: ++ bt856_setbit(encoder, 0xde, 4, 0); ++ bt856_setbit(encoder, 0xde, 3, 1); ++ bt856_setbit(encoder, 0xdc, 3, 1); ++ bt856_setbit(encoder, 0xdc, 6, 1); ++ break; ++ case 2: /* Color bar */ ++ bt856_setbit(encoder, 0xdc, 3, 0); ++ bt856_setbit(encoder, 0xde, 4, 1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (debug != 0) ++ bt856_dump(encoder); ++ return 0; ++} ++ ++static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops bt856_core_ops = { ++ .g_chip_ident = bt856_g_chip_ident, ++ .init = bt856_init, ++}; ++ ++static const struct v4l2_subdev_video_ops bt856_video_ops = { ++ .s_std_output = bt856_s_std_output, ++ .s_routing = bt856_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops bt856_ops = { ++ .core = &bt856_core_ops, ++ .video = &bt856_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int bt856_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct bt856 *encoder; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); ++ if (encoder == NULL) ++ return -ENOMEM; ++ sd = &encoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &bt856_ops); ++ encoder->norm = V4L2_STD_NTSC; ++ ++ bt856_write(encoder, 0xdc, 0x18); ++ bt856_write(encoder, 0xda, 0); ++ bt856_write(encoder, 0xde, 0); ++ ++ bt856_setbit(encoder, 0xdc, 3, 1); ++ /*bt856_setbit(encoder, 0xdc, 6, 0);*/ ++ bt856_setbit(encoder, 0xdc, 4, 1); ++ ++ if (encoder->norm & V4L2_STD_NTSC) ++ bt856_setbit(encoder, 0xdc, 2, 0); ++ else ++ bt856_setbit(encoder, 0xdc, 2, 1); ++ ++ bt856_setbit(encoder, 0xdc, 1, 1); ++ bt856_setbit(encoder, 0xde, 4, 0); ++ bt856_setbit(encoder, 0xde, 3, 1); ++ ++ if (debug != 0) ++ bt856_dump(encoder); ++ return 0; ++} ++ ++static int bt856_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_bt856(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id bt856_id[] = { ++ { "bt856", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, bt856_id); ++ ++static struct i2c_driver bt856_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "bt856", ++ }, ++ .probe = bt856_probe, ++ .remove = bt856_remove, ++ .id_table = bt856_id, ++}; ++ ++module_i2c_driver(bt856_driver); +diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c +new file mode 100644 +index 0000000..905320b +--- /dev/null ++++ b/drivers/media/i2c/bt866.c +@@ -0,0 +1,243 @@ ++/* ++ bt866 - BT866 Digital Video Encoder (Rockwell Part) ++ ++ Copyright (C) 1999 Mike Bernson ++ Copyright (C) 1998 Dave Perks ++ ++ Modifications for LML33/DC10plus unified driver ++ Copyright (C) 2000 Serguei Miridonov ++ ++ This code was modify/ported from the saa7111 driver written ++ by Dave Perks. ++ ++ This code was adapted for the bt866 by Christer Weinigel and ported ++ to 2.6 by Martin Samuelsson. ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); ++MODULE_AUTHOR("Mike Bernson & Dave Perks"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct bt866 { ++ struct v4l2_subdev sd; ++ u8 reg[256]; ++}; ++ ++static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct bt866, sd); ++} ++ ++static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); ++ u8 buffer[2]; ++ int err; ++ ++ buffer[0] = subaddr; ++ buffer[1] = data; ++ ++ encoder->reg[subaddr] = data; ++ ++ v4l_dbg(1, debug, client, "write 0x%02x = 0x%02x\n", subaddr, data); ++ ++ for (err = 0; err < 3;) { ++ if (i2c_master_send(client, buffer, 2) == 2) ++ break; ++ err++; ++ v4l_warn(client, "error #%d writing to 0x%02x\n", ++ err, subaddr); ++ schedule_timeout_interruptible(msecs_to_jiffies(100)); ++ } ++ if (err == 3) { ++ v4l_warn(client, "giving up\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); ++ ++ /* Only PAL supported by this driver at the moment! */ ++ if (!(std & V4L2_STD_NTSC)) ++ return -EINVAL; ++ return 0; ++} ++ ++static int bt866_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ static const __u8 init[] = { ++ 0xc8, 0xcc, /* CRSCALE */ ++ 0xca, 0x91, /* CBSCALE */ ++ 0xcc, 0x24, /* YC16 | OSDNUM */ ++ 0xda, 0x00, /* */ ++ 0xdc, 0x24, /* SETMODE | PAL */ ++ 0xde, 0x02, /* EACTIVE */ ++ ++ /* overlay colors */ ++ 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ ++ 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ ++ 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ ++ 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ ++ 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ ++ 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ ++ 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ ++ 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ ++ ++ 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ ++ 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ ++ 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ ++ 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ ++ 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ ++ 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ ++ 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ ++ 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ ++ }; ++ struct bt866 *encoder = to_bt866(sd); ++ u8 val; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) ++ bt866_write(encoder, init[i], init[i+1]); ++ ++ val = encoder->reg[0xdc]; ++ ++ if (input == 0) ++ val |= 0x40; /* CBSWAP */ ++ else ++ val &= ~0x40; /* !CBSWAP */ ++ ++ bt866_write(encoder, 0xdc, val); ++ ++ val = encoder->reg[0xcc]; ++ if (input == 2) ++ val |= 0x01; /* OSDBAR */ ++ else ++ val &= ~0x01; /* !OSDBAR */ ++ bt866_write(encoder, 0xcc, val); ++ ++ v4l2_dbg(1, debug, sd, "set input %d\n", input); ++ ++ switch (input) { ++ case 0: ++ case 1: ++ case 2: ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++#if 0 ++/* Code to setup square pixels, might be of some use in the future, ++ but is currently unused. */ ++ val = encoder->reg[0xdc]; ++ if (*iarg) ++ val |= 1; /* SQUARE */ ++ else ++ val &= ~1; /* !SQUARE */ ++ bt866_write(client, 0xdc, val); ++#endif ++ ++static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops bt866_core_ops = { ++ .g_chip_ident = bt866_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_video_ops bt866_video_ops = { ++ .s_std_output = bt866_s_std_output, ++ .s_routing = bt866_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops bt866_ops = { ++ .core = &bt866_core_ops, ++ .video = &bt866_video_ops, ++}; ++ ++static int bt866_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct bt866 *encoder; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); ++ if (encoder == NULL) ++ return -ENOMEM; ++ sd = &encoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &bt866_ops); ++ return 0; ++} ++ ++static int bt866_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_bt866(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id bt866_id[] = { ++ { "bt866", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, bt866_id); ++ ++static struct i2c_driver bt866_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "bt866", ++ }, ++ .probe = bt866_probe, ++ .remove = bt866_remove, ++ .id_table = bt866_id, ++}; ++ ++module_i2c_driver(bt866_driver); +diff --git a/drivers/media/i2c/btcx-risc.c b/drivers/media/i2c/btcx-risc.c +new file mode 100644 +index 0000000..ac1b268 +--- /dev/null ++++ b/drivers/media/i2c/btcx-risc.c +@@ -0,0 +1,260 @@ ++/* ++ ++ btcx-risc.c ++ ++ bt848/bt878/cx2388x risc code generator. ++ ++ (c) 2000-03 Gerd Knorr [SuSE Labs] ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "btcx-risc.h" ++ ++MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); ++MODULE_AUTHOR("Gerd Knorr"); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); ++ ++/* ---------------------------------------------------------- */ ++/* allocate/free risc memory */ ++ ++static int memcnt; ++ ++void btcx_riscmem_free(struct pci_dev *pci, ++ struct btcx_riscmem *risc) ++{ ++ if (NULL == risc->cpu) ++ return; ++ if (debug) { ++ memcnt--; ++ printk("btcx: riscmem free [%d] dma=%lx\n", ++ memcnt, (unsigned long)risc->dma); ++ } ++ pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); ++ memset(risc,0,sizeof(*risc)); ++} ++ ++int btcx_riscmem_alloc(struct pci_dev *pci, ++ struct btcx_riscmem *risc, ++ unsigned int size) ++{ ++ __le32 *cpu; ++ dma_addr_t dma = 0; ++ ++ if (NULL != risc->cpu && risc->size < size) ++ btcx_riscmem_free(pci,risc); ++ if (NULL == risc->cpu) { ++ cpu = pci_alloc_consistent(pci, size, &dma); ++ if (NULL == cpu) ++ return -ENOMEM; ++ risc->cpu = cpu; ++ risc->dma = dma; ++ risc->size = size; ++ if (debug) { ++ memcnt++; ++ printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", ++ memcnt, (unsigned long)dma, cpu, size); ++ } ++ } ++ memset(risc->cpu,0,risc->size); ++ return 0; ++} ++ ++/* ---------------------------------------------------------- */ ++/* screen overlay helpers */ ++ ++int ++btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, ++ struct v4l2_clip *clips, unsigned int n) ++{ ++ if (win->left < 0) { ++ /* left */ ++ clips[n].c.left = 0; ++ clips[n].c.top = 0; ++ clips[n].c.width = -win->left; ++ clips[n].c.height = win->height; ++ n++; ++ } ++ if (win->left + win->width > swidth) { ++ /* right */ ++ clips[n].c.left = swidth - win->left; ++ clips[n].c.top = 0; ++ clips[n].c.width = win->width - clips[n].c.left; ++ clips[n].c.height = win->height; ++ n++; ++ } ++ if (win->top < 0) { ++ /* top */ ++ clips[n].c.left = 0; ++ clips[n].c.top = 0; ++ clips[n].c.width = win->width; ++ clips[n].c.height = -win->top; ++ n++; ++ } ++ if (win->top + win->height > sheight) { ++ /* bottom */ ++ clips[n].c.left = 0; ++ clips[n].c.top = sheight - win->top; ++ clips[n].c.width = win->width; ++ clips[n].c.height = win->height - clips[n].c.top; ++ n++; ++ } ++ return n; ++} ++ ++int ++btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) ++{ ++ s32 nx,nw,dx; ++ unsigned int i; ++ ++ /* fixup window */ ++ nx = (win->left + mask) & ~mask; ++ nw = (win->width) & ~mask; ++ if (nx + nw > win->left + win->width) ++ nw -= mask+1; ++ dx = nx - win->left; ++ win->left = nx; ++ win->width = nw; ++ if (debug) ++ printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", ++ win->width, win->height, win->left, win->top, dx); ++ ++ /* fixup clips */ ++ for (i = 0; i < n; i++) { ++ nx = (clips[i].c.left-dx) & ~mask; ++ nw = (clips[i].c.width) & ~mask; ++ if (nx + nw < clips[i].c.left-dx + clips[i].c.width) ++ nw += mask+1; ++ clips[i].c.left = nx; ++ clips[i].c.width = nw; ++ if (debug) ++ printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", ++ clips[i].c.width, clips[i].c.height, ++ clips[i].c.left, clips[i].c.top); ++ } ++ return 0; ++} ++ ++void ++btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) ++{ ++ struct v4l2_clip swap; ++ int i,j,n; ++ ++ if (nclips < 2) ++ return; ++ for (i = nclips-2; i >= 0; i--) { ++ for (n = 0, j = 0; j <= i; j++) { ++ if (clips[j].c.left > clips[j+1].c.left) { ++ swap = clips[j]; ++ clips[j] = clips[j+1]; ++ clips[j+1] = swap; ++ n++; ++ } ++ } ++ if (0 == n) ++ break; ++ } ++} ++ ++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) ++{ ++ unsigned int clip,skip; ++ int end, maxline; ++ ++ skip=0; ++ maxline = 9999; ++ for (clip = 0; clip < nclips; clip++) { ++ ++ /* sanity checks */ ++ if (clips[clip].c.left + clips[clip].c.width <= 0) ++ continue; ++ if (clips[clip].c.left > (signed)width) ++ break; ++ ++ /* vertical range */ ++ if (line > clips[clip].c.top+clips[clip].c.height-1) ++ continue; ++ if (line < clips[clip].c.top) { ++ if (maxline > clips[clip].c.top-1) ++ maxline = clips[clip].c.top-1; ++ continue; ++ } ++ if (maxline > clips[clip].c.top+clips[clip].c.height-1) ++ maxline = clips[clip].c.top+clips[clip].c.height-1; ++ ++ /* horizontal range */ ++ if (0 == skip || clips[clip].c.left > skips[skip-1].end) { ++ /* new one */ ++ skips[skip].start = clips[clip].c.left; ++ if (skips[skip].start < 0) ++ skips[skip].start = 0; ++ skips[skip].end = clips[clip].c.left + clips[clip].c.width; ++ if (skips[skip].end > width) ++ skips[skip].end = width; ++ skip++; ++ } else { ++ /* overlaps -- expand last one */ ++ end = clips[clip].c.left + clips[clip].c.width; ++ if (skips[skip-1].end < end) ++ skips[skip-1].end = end; ++ if (skips[skip-1].end > width) ++ skips[skip-1].end = width; ++ } ++ } ++ *nskips = skip; ++ *maxy = maxline; ++ ++ if (debug) { ++ printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); ++ for (skip = 0; skip < *nskips; skip++) { ++ printk(" %d-%d",skips[skip].start,skips[skip].end); ++ } ++ printk("\n"); ++ } ++} ++ ++/* ---------------------------------------------------------- */ ++ ++EXPORT_SYMBOL(btcx_riscmem_alloc); ++EXPORT_SYMBOL(btcx_riscmem_free); ++ ++EXPORT_SYMBOL(btcx_screen_clips); ++EXPORT_SYMBOL(btcx_align); ++EXPORT_SYMBOL(btcx_sort_clips); ++EXPORT_SYMBOL(btcx_calc_skips); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/i2c/btcx-risc.h b/drivers/media/i2c/btcx-risc.h +new file mode 100644 +index 0000000..f8bc6e8 +--- /dev/null ++++ b/drivers/media/i2c/btcx-risc.h +@@ -0,0 +1,34 @@ ++/* ++ */ ++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); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c +new file mode 100644 +index 0000000..c8581e2 +--- /dev/null ++++ b/drivers/media/i2c/cs5345.c +@@ -0,0 +1,252 @@ ++/* ++ * cs5345 Cirrus Logic 24-bit, 192 kHz Stereo Audio ADC ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("i2c device driver for cs5345 Audio ADC"); ++MODULE_AUTHOR("Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++ ++module_param(debug, bool, 0644); ++ ++MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); ++ ++struct cs5345_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++}; ++ ++static inline struct cs5345_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct cs5345_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct cs5345_state, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int cs5345_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int cs5345_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ if ((input & 0xf) > 6) { ++ v4l2_err(sd, "Invalid input %d.\n", input); ++ return -EINVAL; ++ } ++ cs5345_write(sd, 0x09, input & 0xf); ++ cs5345_write(sd, 0x05, input & 0xf0); ++ return 0; ++} ++ ++static int cs5345_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ cs5345_write(sd, 0x04, ctrl->val ? 0x80 : 0); ++ return 0; ++ case V4L2_CID_AUDIO_VOLUME: ++ cs5345_write(sd, 0x07, ((u8)ctrl->val) & 0x3f); ++ cs5345_write(sd, 0x08, ((u8)ctrl->val) & 0x3f); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cs5345_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->size = 1; ++ reg->val = cs5345_read(sd, reg->reg & 0x1f); ++ return 0; ++} ++ ++static int cs5345_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ cs5345_write(sd, reg->reg & 0x1f, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int cs5345_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_CS5345, 0); ++} ++ ++static int cs5345_log_status(struct v4l2_subdev *sd) ++{ ++ u8 v = cs5345_read(sd, 0x09) & 7; ++ u8 m = cs5345_read(sd, 0x04); ++ int vol = cs5345_read(sd, 0x08) & 0x3f; ++ ++ v4l2_info(sd, "Input: %d%s\n", v, ++ (m & 0x80) ? " (muted)" : ""); ++ if (vol >= 32) ++ vol = vol - 64; ++ v4l2_info(sd, "Volume: %d dB\n", vol); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops cs5345_ctrl_ops = { ++ .s_ctrl = cs5345_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops cs5345_core_ops = { ++ .log_status = cs5345_log_status, ++ .g_chip_ident = cs5345_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = cs5345_g_register, ++ .s_register = cs5345_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_audio_ops cs5345_audio_ops = { ++ .s_routing = cs5345_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops cs5345_ops = { ++ .core = &cs5345_core_ops, ++ .audio = &cs5345_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cs5345_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct cs5345_state *state; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct cs5345_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &cs5345_ops); ++ ++ v4l2_ctrl_handler_init(&state->hdl, 2); ++ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&state->hdl, &cs5345_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, -24, 24, 1, 0); ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ /* set volume/mute */ ++ v4l2_ctrl_handler_setup(&state->hdl); ++ ++ cs5345_write(sd, 0x02, 0x00); ++ cs5345_write(sd, 0x04, 0x01); ++ cs5345_write(sd, 0x09, 0x01); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cs5345_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct cs5345_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id cs5345_id[] = { ++ { "cs5345", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, cs5345_id); ++ ++static struct i2c_driver cs5345_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "cs5345", ++ }, ++ .probe = cs5345_probe, ++ .remove = cs5345_remove, ++ .id_table = cs5345_id, ++}; ++ ++module_i2c_driver(cs5345_driver); +diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c +new file mode 100644 +index 0000000..b293912 +--- /dev/null ++++ b/drivers/media/i2c/cs53l32a.c +@@ -0,0 +1,251 @@ ++/* ++ * cs53l32a (Adaptec AVC-2010 and AVC-2410) i2c ivtv driver. ++ * Copyright (C) 2005 Martin Vaughan ++ * ++ * Audio source switching for Adaptec AVC-2410 added by Trev Jackson ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); ++MODULE_AUTHOR("Martin Vaughan"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++ ++module_param(debug, bool, 0644); ++ ++MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); ++ ++ ++struct cs53l32a_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++}; ++ ++static inline struct cs53l32a_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct cs53l32a_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct cs53l32a_state, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int cs53l32a_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ /* There are 2 physical inputs, but the second input can be ++ placed in two modes, the first mode bypasses the PGA (gain), ++ the second goes through the PGA. Hence there are three ++ possible inputs to choose from. */ ++ if (input > 2) { ++ v4l2_err(sd, "Invalid input %d.\n", input); ++ return -EINVAL; ++ } ++ cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); ++ return 0; ++} ++ ++static int cs53l32a_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ cs53l32a_write(sd, 0x03, ctrl->val ? 0xf0 : 0x30); ++ return 0; ++ case V4L2_CID_AUDIO_VOLUME: ++ cs53l32a_write(sd, 0x04, (u8)ctrl->val); ++ cs53l32a_write(sd, 0x05, (u8)ctrl->val); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, ++ chip, V4L2_IDENT_CS53l32A, 0); ++} ++ ++static int cs53l32a_log_status(struct v4l2_subdev *sd) ++{ ++ struct cs53l32a_state *state = to_state(sd); ++ u8 v = cs53l32a_read(sd, 0x01); ++ ++ v4l2_info(sd, "Input: %d\n", (v >> 4) & 3); ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops cs53l32a_ctrl_ops = { ++ .s_ctrl = cs53l32a_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { ++ .log_status = cs53l32a_log_status, ++ .g_chip_ident = cs53l32a_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { ++ .s_routing = cs53l32a_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops cs53l32a_ops = { ++ .core = &cs53l32a_core_ops, ++ .audio = &cs53l32a_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* i2c implementation */ ++ ++/* ++ * Generic i2c probe ++ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' ++ */ ++ ++static int cs53l32a_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct cs53l32a_state *state; ++ struct v4l2_subdev *sd; ++ int i; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ if (!id) ++ strlcpy(client->name, "cs53l32a", sizeof(client->name)); ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct cs53l32a_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); ++ ++ for (i = 1; i <= 7; i++) { ++ u8 v = cs53l32a_read(sd, i); ++ ++ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); ++ } ++ ++ v4l2_ctrl_handler_init(&state->hdl, 2); ++ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, -96, 12, 1, 0); ++ v4l2_ctrl_new_std(&state->hdl, &cs53l32a_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ ++ /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ ++ ++ cs53l32a_write(sd, 0x01, 0x21); ++ cs53l32a_write(sd, 0x02, 0x29); ++ cs53l32a_write(sd, 0x03, 0x30); ++ cs53l32a_write(sd, 0x04, 0x00); ++ cs53l32a_write(sd, 0x05, 0x00); ++ cs53l32a_write(sd, 0x06, 0x00); ++ cs53l32a_write(sd, 0x07, 0x00); ++ ++ /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ ++ ++ for (i = 1; i <= 7; i++) { ++ u8 v = cs53l32a_read(sd, i); ++ ++ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); ++ } ++ return 0; ++} ++ ++static int cs53l32a_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct cs53l32a_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++static const struct i2c_device_id cs53l32a_id[] = { ++ { "cs53l32a", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, cs53l32a_id); ++ ++static struct i2c_driver cs53l32a_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "cs53l32a", ++ }, ++ .probe = cs53l32a_probe, ++ .remove = cs53l32a_remove, ++ .id_table = cs53l32a_id, ++}; ++ ++module_i2c_driver(cs53l32a_driver); +diff --git a/drivers/media/i2c/cx2341x.c b/drivers/media/i2c/cx2341x.c +new file mode 100644 +index 0000000..103ef6b +--- /dev/null ++++ b/drivers/media/i2c/cx2341x.c +@@ -0,0 +1,1726 @@ ++/* ++ * cx2341x - generic code for cx23415/6/8 based devices ++ * ++ * Copyright (C) 2006 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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); ++ } ++} ++ ++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 ""; ++} ++ ++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; ++ ++ 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/i2c/cx25840/Kconfig b/drivers/media/i2c/cx25840/Kconfig +new file mode 100644 +index 0000000..451133a +--- /dev/null ++++ b/drivers/media/i2c/cx25840/Kconfig +@@ -0,0 +1,8 @@ ++config VIDEO_CX25840 ++ tristate "Conexant CX2584x audio/video decoders" ++ depends on VIDEO_V4L2 && I2C ++ ---help--- ++ Support for the Conexant CX2584x audio/video decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx25840 +diff --git a/drivers/media/i2c/cx25840/Makefile b/drivers/media/i2c/cx25840/Makefile +new file mode 100644 +index 0000000..898eb13 +--- /dev/null ++++ b/drivers/media/i2c/cx25840/Makefile +@@ -0,0 +1,6 @@ ++cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ ++ cx25840-vbi.o cx25840-ir.o ++ ++obj-$(CONFIG_VIDEO_CX25840) += cx25840.o ++ ++ccflags-y += -Idrivers/media/i2c +diff --git a/drivers/media/i2c/cx25840/cx25840-audio.c b/drivers/media/i2c/cx25840/cx25840-audio.c +new file mode 100644 +index 0000000..34b96c7 +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-audio.c +@@ -0,0 +1,571 @@ ++/* cx25840 audio functions ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "cx25840-core.h" ++ ++/* ++ * Note: The PLL and SRC parameters are based on a reference frequency that ++ * would ideally be: ++ * ++ * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz ++ * ++ * However, it's not the exact reference frequency that matters, only that the ++ * firmware and modules that comprise the driver for a particular board all ++ * use the same value (close to the ideal value). ++ * ++ * Comments below will note which reference frequency is assumed for various ++ * parameters. They will usually be one of ++ * ++ * ref_freq = 28.636360 MHz ++ * or ++ * ref_freq = 28.636363 MHz ++ */ ++ ++static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->aud_input != CX25840_AUDIO_SERIAL) { ++ switch (freq) { ++ case 32000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 ++ */ ++ cx25840_write4(client, 0x108, 0x1006040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x1bb39ee ++ * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 ++ * 196.6 MHz pre-postdivide ++ * FIXME < 200 MHz is out of specified valid range ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x01bb39ee); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x50); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ ++ cx25840_write4(client, 0x900, 0x0801f77f); ++ cx25840_write4(client, 0x904, 0x0801f77f); ++ cx25840_write4(client, 0x90c, 0x0801f77f); ++ break; ++ ++ case 44100: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 ++ */ ++ cx25840_write4(client, 0x108, 0x1009040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x0ec6bd6 ++ * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 ++ * 271 MHz pre-postdivide ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x00ec6bd6); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x50); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ ++ cx25840_write4(client, 0x900, 0x08016d59); ++ cx25840_write4(client, 0x904, 0x08016d59); ++ cx25840_write4(client, 0x90c, 0x08016d59); ++ break; ++ ++ case 48000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 ++ */ ++ cx25840_write4(client, 0x108, 0x100a040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x098d6e5 ++ * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 ++ * 295 MHz pre-postdivide ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x0098d6e5); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x50); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ ++ cx25840_write4(client, 0x900, 0x08014faa); ++ cx25840_write4(client, 0x904, 0x08014faa); ++ cx25840_write4(client, 0x90c, 0x08014faa); ++ break; ++ } ++ } else { ++ switch (freq) { ++ case 32000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e ++ */ ++ cx25840_write4(client, 0x108, 0x1e08040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x12a0869 ++ * 28636363 * 0x8.9504348/0x1e = 32000 * 256 ++ * 246 MHz pre-postdivide ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x012a0869); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x54); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src1_ctl */ ++ /* 0x1.0000 = 32000/32000 */ ++ cx25840_write4(client, 0x8f8, 0x08010000); ++ ++ /* src3/4/6_ctl */ ++ /* 0x2.0000 = 2 * (32000/32000) */ ++ cx25840_write4(client, 0x900, 0x08020000); ++ cx25840_write4(client, 0x904, 0x08020000); ++ cx25840_write4(client, 0x90c, 0x08020000); ++ break; ++ ++ case 44100: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 ++ */ ++ cx25840_write4(client, 0x108, 0x1809040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x0ec6bd6 ++ * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 ++ * 271 MHz pre-postdivide ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x00ec6bd6); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x50); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src1_ctl */ ++ /* 0x1.60cd = 44100/32000 */ ++ cx25840_write4(client, 0x8f8, 0x080160cd); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.7385 = 2 * (32000/44100) */ ++ cx25840_write4(client, 0x900, 0x08017385); ++ cx25840_write4(client, 0x904, 0x08017385); ++ cx25840_write4(client, 0x90c, 0x08017385); ++ break; ++ ++ case 48000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 ++ */ ++ cx25840_write4(client, 0x108, 0x180a040f); ++ ++ /* ++ * VID_PLL Fraction (register 0x10c) = 0x2be2fe ++ * 28636360 * 0xf.15f17f0/4 = 108 MHz ++ * 432 MHz pre-postdivide ++ */ ++ ++ /* ++ * AUX_PLL Fraction = 0x098d6e5 ++ * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 ++ * 295 MHz pre-postdivide ++ * FIXME 28636363 ref_freq doesn't match VID PLL ref ++ */ ++ cx25840_write4(client, 0x110, 0x0098d6e5); ++ ++ /* ++ * SA_MCLK_SEL = 1 ++ * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider ++ */ ++ cx25840_write(client, 0x127, 0x50); ++ ++ if (is_cx2583x(state)) ++ break; ++ ++ /* src1_ctl */ ++ /* 0x1.8000 = 48000/32000 */ ++ cx25840_write4(client, 0x8f8, 0x08018000); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.5555 = 2 * (32000/48000) */ ++ cx25840_write4(client, 0x900, 0x08015555); ++ cx25840_write4(client, 0x904, 0x08015555); ++ cx25840_write4(client, 0x90c, 0x08015555); ++ break; ++ } ++ } ++ ++ state->audclk_freq = freq; ++ ++ return 0; ++} ++ ++static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) ++{ ++ return cx25840_set_audclk_freq(client, freq); ++} ++ ++static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->aud_input != CX25840_AUDIO_SERIAL) { ++ switch (freq) { ++ case 32000: ++ case 44100: ++ case 48000: ++ /* We don't have register values ++ * so avoid destroying registers. */ ++ /* FIXME return -EINVAL; */ ++ break; ++ } ++ } else { ++ switch (freq) { ++ case 32000: ++ case 44100: ++ /* We don't have register values ++ * so avoid destroying registers. */ ++ /* FIXME return -EINVAL; */ ++ break; ++ ++ case 48000: ++ /* src1_ctl */ ++ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ ++ cx25840_write4(client, 0x8f8, 0x0801867c); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ ++ cx25840_write4(client, 0x900, 0x08014faa); ++ cx25840_write4(client, 0x904, 0x08014faa); ++ cx25840_write4(client, 0x90c, 0x08014faa); ++ break; ++ } ++ } ++ ++ state->audclk_freq = freq; ++ ++ return 0; ++} ++ ++static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->aud_input != CX25840_AUDIO_SERIAL) { ++ switch (freq) { ++ case 32000: ++ /* src3/4/6_ctl */ ++ /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ ++ cx25840_write4(client, 0x900, 0x0801f77f); ++ cx25840_write4(client, 0x904, 0x0801f77f); ++ cx25840_write4(client, 0x90c, 0x0801f77f); ++ break; ++ ++ case 44100: ++ /* src3/4/6_ctl */ ++ /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ ++ cx25840_write4(client, 0x900, 0x08016d59); ++ cx25840_write4(client, 0x904, 0x08016d59); ++ cx25840_write4(client, 0x90c, 0x08016d59); ++ break; ++ ++ case 48000: ++ /* src3/4/6_ctl */ ++ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ ++ cx25840_write4(client, 0x900, 0x08014faa); ++ cx25840_write4(client, 0x904, 0x08014faa); ++ cx25840_write4(client, 0x90c, 0x08014faa); ++ break; ++ } ++ } else { ++ switch (freq) { ++ /* FIXME These cases make different assumptions about audclk */ ++ case 32000: ++ /* src1_ctl */ ++ /* 0x1.0000 = 32000/32000 */ ++ cx25840_write4(client, 0x8f8, 0x08010000); ++ ++ /* src3/4/6_ctl */ ++ /* 0x2.0000 = 2 * (32000/32000) */ ++ cx25840_write4(client, 0x900, 0x08020000); ++ cx25840_write4(client, 0x904, 0x08020000); ++ cx25840_write4(client, 0x90c, 0x08020000); ++ break; ++ ++ case 44100: ++ /* src1_ctl */ ++ /* 0x1.60cd = 44100/32000 */ ++ cx25840_write4(client, 0x8f8, 0x080160cd); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.7385 = 2 * (32000/44100) */ ++ cx25840_write4(client, 0x900, 0x08017385); ++ cx25840_write4(client, 0x904, 0x08017385); ++ cx25840_write4(client, 0x90c, 0x08017385); ++ break; ++ ++ case 48000: ++ /* src1_ctl */ ++ /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ ++ cx25840_write4(client, 0x8f8, 0x0801867c); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ ++ cx25840_write4(client, 0x900, 0x08014faa); ++ cx25840_write4(client, 0x904, 0x08014faa); ++ cx25840_write4(client, 0x90c, 0x08014faa); ++ break; ++ } ++ } ++ ++ state->audclk_freq = freq; ++ ++ return 0; ++} ++ ++static int set_audclk_freq(struct i2c_client *client, u32 freq) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (freq != 32000 && freq != 44100 && freq != 48000) ++ return -EINVAL; ++ ++ if (is_cx231xx(state)) ++ return cx231xx_set_audclk_freq(client, freq); ++ ++ if (is_cx2388x(state)) ++ return cx23885_set_audclk_freq(client, freq); ++ ++ if (is_cx2583x(state)) ++ return cx25836_set_audclk_freq(client, freq); ++ ++ return cx25840_set_audclk_freq(client, freq); ++} ++ ++void cx25840_audio_set_path(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (!is_cx2583x(state)) { ++ /* assert soft reset */ ++ cx25840_and_or(client, 0x810, ~0x1, 0x01); ++ ++ /* stop microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0); ++ ++ /* Mute everything to prevent the PFFT! */ ++ cx25840_write(client, 0x8d3, 0x1f); ++ ++ if (state->aud_input == CX25840_AUDIO_SERIAL) { ++ /* Set Path1 to Serial Audio Input */ ++ cx25840_write4(client, 0x8d0, 0x01011012); ++ ++ /* The microcontroller should not be started for the ++ * non-tuner inputs: autodetection is specific for ++ * TV audio. */ ++ } else { ++ /* Set Path1 to Analog Demod Main Channel */ ++ cx25840_write4(client, 0x8d0, 0x1f063870); ++ } ++ } ++ ++ set_audclk_freq(client, state->audclk_freq); ++ ++ if (!is_cx2583x(state)) { ++ if (state->aud_input != CX25840_AUDIO_SERIAL) { ++ /* When the microcontroller detects the ++ * audio format, it will unmute the lines */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++ } ++ ++ /* deassert soft reset */ ++ cx25840_and_or(client, 0x810, ~0x1, 0x00); ++ ++ /* Ensure the controller is running when we exit */ ++ if (is_cx2388x(state) || is_cx231xx(state)) ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++ } ++} ++ ++static void set_volume(struct i2c_client *client, int volume) ++{ ++ int vol; ++ ++ /* Convert the volume to msp3400 values (0-127) */ ++ vol = volume >> 9; ++ ++ /* now scale it up to cx25840 values ++ * -114dB to -96dB maps to 0 ++ * this should be 19, but in my testing that was 4dB too loud */ ++ if (vol <= 23) { ++ vol = 0; ++ } else { ++ vol -= 23; ++ } ++ ++ /* PATH1_VOLUME */ ++ cx25840_write(client, 0x8d4, 228 - (vol * 2)); ++} ++ ++static void set_balance(struct i2c_client *client, int balance) ++{ ++ int bal = balance >> 8; ++ if (bal > 0x80) { ++ /* PATH1_BAL_LEFT */ ++ cx25840_and_or(client, 0x8d5, 0x7f, 0x80); ++ /* PATH1_BAL_LEVEL */ ++ cx25840_and_or(client, 0x8d5, ~0x7f, bal & 0x7f); ++ } else { ++ /* PATH1_BAL_LEFT */ ++ cx25840_and_or(client, 0x8d5, 0x7f, 0x00); ++ /* PATH1_BAL_LEVEL */ ++ cx25840_and_or(client, 0x8d5, ~0x7f, 0x80 - bal); ++ } ++} ++ ++int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct cx25840_state *state = to_state(sd); ++ int retval; ++ ++ if (!is_cx2583x(state)) ++ cx25840_and_or(client, 0x810, ~0x1, 1); ++ if (state->aud_input != CX25840_AUDIO_SERIAL) { ++ cx25840_and_or(client, 0x803, ~0x10, 0); ++ cx25840_write(client, 0x8d3, 0x1f); ++ } ++ retval = set_audclk_freq(client, freq); ++ if (state->aud_input != CX25840_AUDIO_SERIAL) ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++ if (!is_cx2583x(state)) ++ cx25840_and_or(client, 0x810, ~0x1, 0); ++ return retval; ++} ++ ++static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_VOLUME: ++ if (state->mute->val) ++ set_volume(client, 0); ++ else ++ set_volume(client, state->volume->val); ++ break; ++ case V4L2_CID_AUDIO_BASS: ++ /* PATH1_EQ_BASS_VOL */ ++ cx25840_and_or(client, 0x8d9, ~0x3f, ++ 48 - (ctrl->val * 48 / 0xffff)); ++ break; ++ case V4L2_CID_AUDIO_TREBLE: ++ /* PATH1_EQ_TREBLE_VOL */ ++ cx25840_and_or(client, 0x8db, ~0x3f, ++ 48 - (ctrl->val * 48 / 0xffff)); ++ break; ++ case V4L2_CID_AUDIO_BALANCE: ++ set_balance(client, ctrl->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { ++ .s_ctrl = cx25840_audio_s_ctrl, ++}; +diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c +new file mode 100644 +index 0000000..f4149eb +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-core.c +@@ -0,0 +1,5340 @@ ++/* cx25840 - Conexant CX25840 audio/video decoder driver ++ * ++ * Copyright (C) 2004 Ulf Eklund ++ * ++ * Based on the saa7115 driver and on the first version of Chris Kennedy's ++ * cx25840 driver. ++ * ++ * Changes by Tyler Trafford ++ * - cleanup/rewrite for V4L2 API (2005) ++ * ++ * VBI support by Hans Verkuil . ++ * ++ * NTSC sliced VBI support by Christopher Neufeld ++ * with additional fixes by Hans Verkuil . ++ * ++ * CX23885 support by Steven Toth . ++ * ++ * CX2388[578] IRQ handling, IO Pin mux configuration and other small fixes are ++ * Copyright (C) 2010 Andy Walls ++ * ++ * CX23888 DIF support for the HVR1850 ++ * Copyright (C) 2011 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25840-core.h" ++ ++MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); ++MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); ++MODULE_LICENSE("GPL"); ++ ++#define CX25840_VID_INT_STAT_REG 0x410 ++#define CX25840_VID_INT_STAT_BITS 0x0000ffff ++#define CX25840_VID_INT_MASK_BITS 0xffff0000 ++#define CX25840_VID_INT_MASK_SHFT 16 ++#define CX25840_VID_INT_MASK_REG 0x412 ++ ++#define CX23885_AUD_MC_INT_MASK_REG 0x80c ++#define CX23885_AUD_MC_INT_STAT_BITS 0xffff0000 ++#define CX23885_AUD_MC_INT_CTRL_BITS 0x0000ffff ++#define CX23885_AUD_MC_INT_STAT_SHFT 16 ++ ++#define CX25840_AUD_INT_CTRL_REG 0x812 ++#define CX25840_AUD_INT_STAT_REG 0x813 ++ ++#define CX23885_PIN_CTRL_IRQ_REG 0x123 ++#define CX23885_PIN_CTRL_IRQ_IR_STAT 0x40 ++#define CX23885_PIN_CTRL_IRQ_AUD_STAT 0x20 ++#define CX23885_PIN_CTRL_IRQ_VID_STAT 0x10 ++ ++#define CX25840_IR_STATS_REG 0x210 ++#define CX25840_IR_IRQEN_REG 0x214 ++ ++static int cx25840_debug; ++ ++module_param_named(debug,cx25840_debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++static void cx23888_std_setup(struct i2c_client *client); ++ ++int cx25840_write(struct i2c_client *client, u16 addr, u8 value) ++{ ++ u8 buffer[3]; ++ buffer[0] = addr >> 8; ++ buffer[1] = addr & 0xff; ++ buffer[2] = value; ++ return i2c_master_send(client, buffer, 3); ++} ++ ++int cx25840_write4(struct i2c_client *client, u16 addr, u32 value) ++{ ++ u8 buffer[6]; ++ buffer[0] = addr >> 8; ++ buffer[1] = addr & 0xff; ++ buffer[2] = value & 0xff; ++ buffer[3] = (value >> 8) & 0xff; ++ buffer[4] = (value >> 16) & 0xff; ++ buffer[5] = value >> 24; ++ return i2c_master_send(client, buffer, 6); ++} ++ ++u8 cx25840_read(struct i2c_client * client, u16 addr) ++{ ++ struct i2c_msg msgs[2]; ++ u8 tx_buf[2], rx_buf[1]; ++ ++ /* Write register address */ ++ tx_buf[0] = addr >> 8; ++ tx_buf[1] = addr & 0xff; ++ msgs[0].addr = client->addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 2; ++ msgs[0].buf = (char *) tx_buf; ++ ++ /* Read data from register */ ++ msgs[1].addr = client->addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = 1; ++ msgs[1].buf = (char *) rx_buf; ++ ++ if (i2c_transfer(client->adapter, msgs, 2) < 2) ++ return 0; ++ ++ return rx_buf[0]; ++} ++ ++u32 cx25840_read4(struct i2c_client * client, u16 addr) ++{ ++ struct i2c_msg msgs[2]; ++ u8 tx_buf[2], rx_buf[4]; ++ ++ /* Write register address */ ++ tx_buf[0] = addr >> 8; ++ tx_buf[1] = addr & 0xff; ++ msgs[0].addr = client->addr; ++ msgs[0].flags = 0; ++ msgs[0].len = 2; ++ msgs[0].buf = (char *) tx_buf; ++ ++ /* Read data from registers */ ++ msgs[1].addr = client->addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = 4; ++ msgs[1].buf = (char *) rx_buf; ++ ++ if (i2c_transfer(client->adapter, msgs, 2) < 2) ++ return 0; ++ ++ return (rx_buf[3] << 24) | (rx_buf[2] << 16) | (rx_buf[1] << 8) | ++ rx_buf[0]; ++} ++ ++int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned and_mask, ++ u8 or_value) ++{ ++ return cx25840_write(client, addr, ++ (cx25840_read(client, addr) & and_mask) | ++ or_value); ++} ++ ++int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, ++ u32 or_value) ++{ ++ return cx25840_write4(client, addr, ++ (cx25840_read4(client, addr) & and_mask) | ++ or_value); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, ++ enum cx25840_audio_input aud_input); ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cx23885_s_io_pin_config(struct v4l2_subdev *sd, size_t n, ++ struct v4l2_subdev_io_pin_config *p) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int i; ++ u32 pin_ctrl; ++ u8 gpio_oe, gpio_data, strength; ++ ++ pin_ctrl = cx25840_read4(client, 0x120); ++ gpio_oe = cx25840_read(client, 0x160); ++ gpio_data = cx25840_read(client, 0x164); ++ ++ for (i = 0; i < n; i++) { ++ strength = p[i].strength; ++ if (strength > CX25840_PIN_DRIVE_FAST) ++ strength = CX25840_PIN_DRIVE_FAST; ++ ++ switch (p[i].pin) { ++ case CX23885_PIN_IRQ_N_GPIO16: ++ if (p[i].function != CX23885_PAD_IRQ_N) { ++ /* GPIO16 */ ++ pin_ctrl &= ~(0x1 << 25); ++ } else { ++ /* IRQ_N */ ++ if (p[i].flags & ++ (V4L2_SUBDEV_IO_PIN_DISABLE | ++ V4L2_SUBDEV_IO_PIN_INPUT)) { ++ pin_ctrl &= ~(0x1 << 25); ++ } else { ++ pin_ctrl |= (0x1 << 25); ++ } ++ if (p[i].flags & ++ V4L2_SUBDEV_IO_PIN_ACTIVE_LOW) { ++ pin_ctrl &= ~(0x1 << 24); ++ } else { ++ pin_ctrl |= (0x1 << 24); ++ } ++ } ++ break; ++ case CX23885_PIN_IR_RX_GPIO19: ++ if (p[i].function != CX23885_PAD_GPIO19) { ++ /* IR_RX */ ++ gpio_oe |= (0x1 << 0); ++ pin_ctrl &= ~(0x3 << 18); ++ pin_ctrl |= (strength << 18); ++ } else { ++ /* GPIO19 */ ++ gpio_oe &= ~(0x1 << 0); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { ++ gpio_data &= ~(0x1 << 0); ++ gpio_data |= ((p[i].value & 0x1) << 0); ++ } ++ pin_ctrl &= ~(0x3 << 12); ++ pin_ctrl |= (strength << 12); ++ } ++ break; ++ case CX23885_PIN_IR_TX_GPIO20: ++ if (p[i].function != CX23885_PAD_GPIO20) { ++ /* IR_TX */ ++ gpio_oe |= (0x1 << 1); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_DISABLE) ++ pin_ctrl &= ~(0x1 << 10); ++ else ++ pin_ctrl |= (0x1 << 10); ++ pin_ctrl &= ~(0x3 << 18); ++ pin_ctrl |= (strength << 18); ++ } else { ++ /* GPIO20 */ ++ gpio_oe &= ~(0x1 << 1); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { ++ gpio_data &= ~(0x1 << 1); ++ gpio_data |= ((p[i].value & 0x1) << 1); ++ } ++ pin_ctrl &= ~(0x3 << 12); ++ pin_ctrl |= (strength << 12); ++ } ++ break; ++ case CX23885_PIN_I2S_SDAT_GPIO21: ++ if (p[i].function != CX23885_PAD_GPIO21) { ++ /* I2S_SDAT */ ++ /* TODO: Input or Output config */ ++ gpio_oe |= (0x1 << 2); ++ pin_ctrl &= ~(0x3 << 22); ++ pin_ctrl |= (strength << 22); ++ } else { ++ /* GPIO21 */ ++ gpio_oe &= ~(0x1 << 2); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { ++ gpio_data &= ~(0x1 << 2); ++ gpio_data |= ((p[i].value & 0x1) << 2); ++ } ++ pin_ctrl &= ~(0x3 << 12); ++ pin_ctrl |= (strength << 12); ++ } ++ break; ++ case CX23885_PIN_I2S_WCLK_GPIO22: ++ if (p[i].function != CX23885_PAD_GPIO22) { ++ /* I2S_WCLK */ ++ /* TODO: Input or Output config */ ++ gpio_oe |= (0x1 << 3); ++ pin_ctrl &= ~(0x3 << 22); ++ pin_ctrl |= (strength << 22); ++ } else { ++ /* GPIO22 */ ++ gpio_oe &= ~(0x1 << 3); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { ++ gpio_data &= ~(0x1 << 3); ++ gpio_data |= ((p[i].value & 0x1) << 3); ++ } ++ pin_ctrl &= ~(0x3 << 12); ++ pin_ctrl |= (strength << 12); ++ } ++ break; ++ case CX23885_PIN_I2S_BCLK_GPIO23: ++ if (p[i].function != CX23885_PAD_GPIO23) { ++ /* I2S_BCLK */ ++ /* TODO: Input or Output config */ ++ gpio_oe |= (0x1 << 4); ++ pin_ctrl &= ~(0x3 << 22); ++ pin_ctrl |= (strength << 22); ++ } else { ++ /* GPIO23 */ ++ gpio_oe &= ~(0x1 << 4); ++ if (p[i].flags & V4L2_SUBDEV_IO_PIN_SET_VALUE) { ++ gpio_data &= ~(0x1 << 4); ++ gpio_data |= ((p[i].value & 0x1) << 4); ++ } ++ pin_ctrl &= ~(0x3 << 12); ++ pin_ctrl |= (strength << 12); ++ } ++ break; ++ } ++ } ++ ++ cx25840_write(client, 0x164, gpio_data); ++ cx25840_write(client, 0x160, gpio_oe); ++ cx25840_write4(client, 0x120, pin_ctrl); ++ return 0; ++} ++ ++static int common_s_io_pin_config(struct v4l2_subdev *sd, size_t n, ++ struct v4l2_subdev_io_pin_config *pincfg) ++{ ++ struct cx25840_state *state = to_state(sd); ++ ++ if (is_cx2388x(state)) ++ return cx23885_s_io_pin_config(sd, n, pincfg); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void init_dll1(struct i2c_client *client) ++{ ++ /* This is the Hauppauge sequence used to ++ * initialize the Delay Lock Loop 1 (ADC DLL). */ ++ cx25840_write(client, 0x159, 0x23); ++ cx25840_write(client, 0x15a, 0x87); ++ cx25840_write(client, 0x15b, 0x06); ++ udelay(10); ++ cx25840_write(client, 0x159, 0xe1); ++ udelay(10); ++ cx25840_write(client, 0x15a, 0x86); ++ cx25840_write(client, 0x159, 0xe0); ++ cx25840_write(client, 0x159, 0xe1); ++ cx25840_write(client, 0x15b, 0x10); ++} ++ ++static void init_dll2(struct i2c_client *client) ++{ ++ /* This is the Hauppauge sequence used to ++ * initialize the Delay Lock Loop 2 (ADC DLL). */ ++ cx25840_write(client, 0x15d, 0xe3); ++ cx25840_write(client, 0x15e, 0x86); ++ cx25840_write(client, 0x15f, 0x06); ++ udelay(10); ++ cx25840_write(client, 0x15d, 0xe1); ++ cx25840_write(client, 0x15d, 0xe0); ++ cx25840_write(client, 0x15d, 0xe1); ++} ++ ++static void cx25836_initialize(struct i2c_client *client) ++{ ++ /* reset configuration is described on page 3-77 of the CX25836 datasheet */ ++ /* 2. */ ++ cx25840_and_or(client, 0x000, ~0x01, 0x01); ++ cx25840_and_or(client, 0x000, ~0x01, 0x00); ++ /* 3a. */ ++ cx25840_and_or(client, 0x15a, ~0x70, 0x00); ++ /* 3b. */ ++ cx25840_and_or(client, 0x15b, ~0x1e, 0x06); ++ /* 3c. */ ++ cx25840_and_or(client, 0x159, ~0x02, 0x02); ++ /* 3d. */ ++ udelay(10); ++ /* 3e. */ ++ cx25840_and_or(client, 0x159, ~0x02, 0x00); ++ /* 3f. */ ++ cx25840_and_or(client, 0x159, ~0xc0, 0xc0); ++ /* 3g. */ ++ cx25840_and_or(client, 0x159, ~0x01, 0x00); ++ cx25840_and_or(client, 0x159, ~0x01, 0x01); ++ /* 3h. */ ++ cx25840_and_or(client, 0x15b, ~0x1e, 0x10); ++} ++ ++static void cx25840_work_handler(struct work_struct *work) ++{ ++ struct cx25840_state *state = container_of(work, struct cx25840_state, fw_work); ++ cx25840_loadfw(state->c); ++ wake_up(&state->fw_wait); ++} ++ ++static void cx25840_initialize(struct i2c_client *client) ++{ ++ DEFINE_WAIT(wait); ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ struct workqueue_struct *q; ++ ++ /* datasheet startup in numbered steps, refer to page 3-77 */ ++ /* 2. */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x00); ++ /* The default of this register should be 4, but I get 0 instead. ++ * Set this register to 4 manually. */ ++ cx25840_write(client, 0x000, 0x04); ++ /* 3. */ ++ init_dll1(client); ++ init_dll2(client); ++ cx25840_write(client, 0x136, 0x0a); ++ /* 4. */ ++ cx25840_write(client, 0x13c, 0x01); ++ cx25840_write(client, 0x13c, 0x00); ++ /* 5. */ ++ /* Do the firmware load in a work handler to prevent. ++ Otherwise the kernel is blocked waiting for the ++ bit-banging i2c interface to finish uploading the ++ firmware. */ ++ INIT_WORK(&state->fw_work, cx25840_work_handler); ++ init_waitqueue_head(&state->fw_wait); ++ q = create_singlethread_workqueue("cx25840_fw"); ++ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); ++ queue_work(q, &state->fw_work); ++ schedule(); ++ finish_wait(&state->fw_wait, &wait); ++ destroy_workqueue(q); ++ ++ /* 6. */ ++ cx25840_write(client, 0x115, 0x8c); ++ cx25840_write(client, 0x116, 0x07); ++ cx25840_write(client, 0x118, 0x02); ++ /* 7. */ ++ cx25840_write(client, 0x4a5, 0x80); ++ cx25840_write(client, 0x4a5, 0x00); ++ cx25840_write(client, 0x402, 0x00); ++ /* 8. */ ++ cx25840_and_or(client, 0x401, ~0x18, 0); ++ cx25840_and_or(client, 0x4a2, ~0x10, 0x10); ++ /* steps 8c and 8d are done in change_input() */ ++ /* 10. */ ++ cx25840_write(client, 0x8d3, 0x1f); ++ cx25840_write(client, 0x8e3, 0x03); ++ ++ cx25840_std_setup(client); ++ ++ /* trial and error says these are needed to get audio */ ++ cx25840_write(client, 0x914, 0xa0); ++ cx25840_write(client, 0x918, 0xa0); ++ cx25840_write(client, 0x919, 0x01); ++ ++ /* stereo preferred */ ++ cx25840_write(client, 0x809, 0x04); ++ /* AC97 shift */ ++ cx25840_write(client, 0x8cf, 0x0f); ++ ++ /* (re)set input */ ++ set_input(client, state->vid_input, state->aud_input); ++ ++ /* start microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++} ++ ++static void cx23885_initialize(struct i2c_client *client) ++{ ++ DEFINE_WAIT(wait); ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ struct workqueue_struct *q; ++ ++ /* ++ * Come out of digital power down ++ * The CX23888, at least, needs this, otherwise registers aside from ++ * 0x0-0x2 can't be read or written. ++ */ ++ cx25840_write(client, 0x000, 0); ++ ++ /* Internal Reset */ ++ cx25840_and_or(client, 0x102, ~0x01, 0x01); ++ cx25840_and_or(client, 0x102, ~0x01, 0x00); ++ ++ /* Stop microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x00); ++ ++ /* DIF in reset? */ ++ cx25840_write(client, 0x398, 0); ++ ++ /* ++ * Trust the default xtal, no division ++ * '885: 28.636363... MHz ++ * '887: 25.000000 MHz ++ * '888: 50.000000 MHz ++ */ ++ cx25840_write(client, 0x2, 0x76); ++ ++ /* Power up all the PLL's and DLL */ ++ cx25840_write(client, 0x1, 0x40); ++ ++ /* Sys PLL */ ++ switch (state->id) { ++ case V4L2_IDENT_CX23888_AV: ++ /* ++ * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz ++ * 572.73 MHz before post divide ++ */ ++ /* HVR1850 or 50MHz xtal */ ++ cx25840_write(client, 0x2, 0x71); ++ cx25840_write4(client, 0x11c, 0x01d1744c); ++ cx25840_write4(client, 0x118, 0x00000416); ++ cx25840_write4(client, 0x404, 0x0010253e); ++ cx25840_write4(client, 0x42c, 0x42600000); ++ cx25840_write4(client, 0x44c, 0x161f1000); ++ break; ++ case V4L2_IDENT_CX23887_AV: ++ /* ++ * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz ++ * 572.73 MHz before post divide ++ */ ++ cx25840_write4(client, 0x11c, 0x01d1744c); ++ cx25840_write4(client, 0x118, 0x00000416); ++ break; ++ case V4L2_IDENT_CX23885_AV: ++ default: ++ /* ++ * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz ++ * 572.73 MHz before post divide ++ */ ++ cx25840_write4(client, 0x11c, 0x00000000); ++ cx25840_write4(client, 0x118, 0x00000414); ++ break; ++ } ++ ++ /* Disable DIF bypass */ ++ cx25840_write4(client, 0x33c, 0x00000001); ++ ++ /* DIF Src phase inc */ ++ cx25840_write4(client, 0x340, 0x0df7df83); ++ ++ /* ++ * Vid PLL ++ * Setup for a BT.656 pixel clock of 13.5 Mpixels/second ++ * ++ * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz ++ * 432.0 MHz before post divide ++ */ ++ ++ /* HVR1850 */ ++ switch (state->id) { ++ case V4L2_IDENT_CX23888_AV: ++ /* 888/HVR1250 specific */ ++ cx25840_write4(client, 0x10c, 0x13333333); ++ cx25840_write4(client, 0x108, 0x00000515); ++ break; ++ default: ++ cx25840_write4(client, 0x10c, 0x002be2c9); ++ cx25840_write4(client, 0x108, 0x0000040f); ++ } ++ ++ /* Luma */ ++ cx25840_write4(client, 0x414, 0x00107d12); ++ ++ /* Chroma */ ++ cx25840_write4(client, 0x420, 0x3d008282); ++ ++ /* ++ * Aux PLL ++ * Initial setup for audio sample clock: ++ * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz ++ * Initial I2S output/master clock(?): ++ * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz ++ */ ++ switch (state->id) { ++ case V4L2_IDENT_CX23888_AV: ++ /* ++ * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz ++ * 368.64 MHz before post divide ++ * 122.88 MHz / 0xa = 12.288 MHz ++ */ ++ /* HVR1850 or 50MHz xtal */ ++ cx25840_write4(client, 0x114, 0x017dbf48); ++ cx25840_write4(client, 0x110, 0x000a030e); ++ break; ++ case V4L2_IDENT_CX23887_AV: ++ /* ++ * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz ++ * 368.64 MHz before post divide ++ * 122.88 MHz / 0xa = 12.288 MHz ++ */ ++ cx25840_write4(client, 0x114, 0x017dbf48); ++ cx25840_write4(client, 0x110, 0x000a030e); ++ break; ++ case V4L2_IDENT_CX23885_AV: ++ default: ++ /* ++ * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz ++ * 368.64 MHz before post divide ++ * 122.88 MHz / 0xa = 12.288 MHz ++ */ ++ cx25840_write4(client, 0x114, 0x01bf0c9e); ++ cx25840_write4(client, 0x110, 0x000a030c); ++ break; ++ } ++ ++ /* ADC2 input select */ ++ cx25840_write(client, 0x102, 0x10); ++ ++ /* VIN1 & VIN5 */ ++ cx25840_write(client, 0x103, 0x11); ++ ++ /* Enable format auto detect */ ++ cx25840_write(client, 0x400, 0); ++ /* Fast subchroma lock */ ++ /* White crush, Chroma AGC & Chroma Killer enabled */ ++ cx25840_write(client, 0x401, 0xe8); ++ ++ /* Select AFE clock pad output source */ ++ cx25840_write(client, 0x144, 0x05); ++ ++ /* Drive GPIO2 direction and values for HVR1700 ++ * where an onboard mux selects the output of demodulator ++ * vs the 417. Failure to set this results in no DTV. ++ * It's safe to set this across all Hauppauge boards ++ * currently, regardless of the board type. ++ */ ++ cx25840_write(client, 0x160, 0x1d); ++ cx25840_write(client, 0x164, 0x00); ++ ++ /* Do the firmware load in a work handler to prevent. ++ Otherwise the kernel is blocked waiting for the ++ bit-banging i2c interface to finish uploading the ++ firmware. */ ++ INIT_WORK(&state->fw_work, cx25840_work_handler); ++ init_waitqueue_head(&state->fw_wait); ++ q = create_singlethread_workqueue("cx25840_fw"); ++ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); ++ queue_work(q, &state->fw_work); ++ schedule(); ++ finish_wait(&state->fw_wait, &wait); ++ destroy_workqueue(q); ++ ++ /* Call the cx23888 specific std setup func, we no longer rely on ++ * the generic cx24840 func. ++ */ ++ if (is_cx23888(state)) ++ cx23888_std_setup(client); ++ else ++ cx25840_std_setup(client); ++ ++ /* (re)set input */ ++ set_input(client, state->vid_input, state->aud_input); ++ ++ /* start microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++ ++ /* Disable and clear video interrupts - we don't use them */ ++ cx25840_write4(client, CX25840_VID_INT_STAT_REG, 0xffffffff); ++ ++ /* Disable and clear audio interrupts - we don't use them */ ++ cx25840_write(client, CX25840_AUD_INT_CTRL_REG, 0xff); ++ cx25840_write(client, CX25840_AUD_INT_STAT_REG, 0xff); ++ ++ /* CC raw enable */ ++ /* - VIP 1.1 control codes - 10bit, blue field enable. ++ * - enable raw data during vertical blanking. ++ * - enable ancillary Data insertion for 656 or VIP. ++ */ ++ cx25840_write4(client, 0x404, 0x0010253e); ++ ++ /* CC on - Undocumented Register */ ++ cx25840_write(client, 0x42f, 0x66); ++ ++ /* HVR-1250 / HVR1850 DIF related */ ++ /* Power everything up */ ++ cx25840_write4(client, 0x130, 0x0); ++ ++ /* Undocumented */ ++ cx25840_write4(client, 0x478, 0x6628021F); ++ ++ /* AFE_CLK_OUT_CTRL - Select the clock output source as output */ ++ cx25840_write4(client, 0x144, 0x5); ++ ++ /* I2C_OUT_CTL - I2S output configuration as ++ * Master, Sony, Left justified, left sample on WS=1 ++ */ ++ cx25840_write4(client, 0x918, 0x1a0); ++ ++ /* AFE_DIAG_CTRL1 */ ++ cx25840_write4(client, 0x134, 0x000a1800); ++ ++ /* AFE_DIAG_CTRL3 - Inverted Polarity for Audio and Video */ ++ cx25840_write4(client, 0x13c, 0x00310000); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void cx231xx_initialize(struct i2c_client *client) ++{ ++ DEFINE_WAIT(wait); ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ struct workqueue_struct *q; ++ ++ /* Internal Reset */ ++ cx25840_and_or(client, 0x102, ~0x01, 0x01); ++ cx25840_and_or(client, 0x102, ~0x01, 0x00); ++ ++ /* Stop microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x00); ++ ++ /* DIF in reset? */ ++ cx25840_write(client, 0x398, 0); ++ ++ /* Trust the default xtal, no division */ ++ /* This changes for the cx23888 products */ ++ cx25840_write(client, 0x2, 0x76); ++ ++ /* Bring down the regulator for AUX clk */ ++ cx25840_write(client, 0x1, 0x40); ++ ++ /* Disable DIF bypass */ ++ cx25840_write4(client, 0x33c, 0x00000001); ++ ++ /* DIF Src phase inc */ ++ cx25840_write4(client, 0x340, 0x0df7df83); ++ ++ /* Luma */ ++ cx25840_write4(client, 0x414, 0x00107d12); ++ ++ /* Chroma */ ++ cx25840_write4(client, 0x420, 0x3d008282); ++ ++ /* ADC2 input select */ ++ cx25840_write(client, 0x102, 0x10); ++ ++ /* VIN1 & VIN5 */ ++ cx25840_write(client, 0x103, 0x11); ++ ++ /* Enable format auto detect */ ++ cx25840_write(client, 0x400, 0); ++ /* Fast subchroma lock */ ++ /* White crush, Chroma AGC & Chroma Killer enabled */ ++ cx25840_write(client, 0x401, 0xe8); ++ ++ /* Do the firmware load in a work handler to prevent. ++ Otherwise the kernel is blocked waiting for the ++ bit-banging i2c interface to finish uploading the ++ firmware. */ ++ INIT_WORK(&state->fw_work, cx25840_work_handler); ++ init_waitqueue_head(&state->fw_wait); ++ q = create_singlethread_workqueue("cx25840_fw"); ++ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); ++ queue_work(q, &state->fw_work); ++ schedule(); ++ finish_wait(&state->fw_wait, &wait); ++ destroy_workqueue(q); ++ ++ cx25840_std_setup(client); ++ ++ /* (re)set input */ ++ set_input(client, state->vid_input, state->aud_input); ++ ++ /* start microcontroller */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x10); ++ ++ /* CC raw enable */ ++ cx25840_write(client, 0x404, 0x0b); ++ ++ /* CC on */ ++ cx25840_write(client, 0x42f, 0x66); ++ cx25840_write4(client, 0x474, 0x1e1e601a); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++void cx25840_std_setup(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ v4l2_std_id std = state->std; ++ int hblank, hactive, burst, vblank, vactive, sc; ++ int vblank656, src_decimation; ++ int luma_lpf, uv_lpf, comb; ++ u32 pll_int, pll_frac, pll_post; ++ ++ /* datasheet startup, step 8d */ ++ if (std & ~V4L2_STD_NTSC) ++ cx25840_write(client, 0x49f, 0x11); ++ else ++ cx25840_write(client, 0x49f, 0x14); ++ ++ if (std & V4L2_STD_625_50) { ++ hblank = 132; ++ hactive = 720; ++ burst = 93; ++ vblank = 36; ++ vactive = 580; ++ vblank656 = 40; ++ src_decimation = 0x21f; ++ luma_lpf = 2; ++ ++ if (std & V4L2_STD_SECAM) { ++ uv_lpf = 0; ++ comb = 0; ++ sc = 0x0a425f; ++ } else if (std == V4L2_STD_PAL_Nc) { ++ uv_lpf = 1; ++ comb = 0x20; ++ sc = 556453; ++ } else { ++ uv_lpf = 1; ++ comb = 0x20; ++ sc = 688739; ++ } ++ } else { ++ hactive = 720; ++ hblank = 122; ++ vactive = 487; ++ luma_lpf = 1; ++ uv_lpf = 1; ++ ++ src_decimation = 0x21f; ++ if (std == V4L2_STD_PAL_60) { ++ vblank = 26; ++ vblank656 = 26; ++ burst = 0x5b; ++ luma_lpf = 2; ++ comb = 0x20; ++ sc = 688739; ++ } else if (std == V4L2_STD_PAL_M) { ++ vblank = 20; ++ vblank656 = 24; ++ burst = 0x61; ++ comb = 0x20; ++ sc = 555452; ++ } else { ++ vblank = 26; ++ vblank656 = 26; ++ burst = 0x5b; ++ comb = 0x66; ++ sc = 556063; ++ } ++ } ++ ++ /* DEBUG: Displays configured PLL frequency */ ++ if (!is_cx231xx(state)) { ++ pll_int = cx25840_read(client, 0x108); ++ pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; ++ pll_post = cx25840_read(client, 0x109); ++ v4l_dbg(1, cx25840_debug, client, ++ "PLL regs = int: %u, frac: %u, post: %u\n", ++ pll_int, pll_frac, pll_post); ++ ++ if (pll_post) { ++ int fin, fsc; ++ int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; ++ ++ pll /= pll_post; ++ v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", ++ pll / 1000000, pll % 1000000); ++ v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", ++ pll / 8000000, (pll / 8) % 1000000); ++ ++ fin = ((u64)src_decimation * pll) >> 12; ++ v4l_dbg(1, cx25840_debug, client, ++ "ADC Sampling freq = %d.%06d MHz\n", ++ fin / 1000000, fin % 1000000); ++ ++ fsc = (((u64)sc) * pll) >> 24L; ++ v4l_dbg(1, cx25840_debug, client, ++ "Chroma sub-carrier freq = %d.%06d MHz\n", ++ fsc / 1000000, fsc % 1000000); ++ ++ v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " ++ "vblank %i, vactive %i, vblank656 %i, src_dec %i, " ++ "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " ++ "sc 0x%06x\n", ++ hblank, hactive, vblank, vactive, vblank656, ++ src_decimation, burst, luma_lpf, uv_lpf, comb, sc); ++ } ++ } ++ ++ /* Sets horizontal blanking delay and active lines */ ++ cx25840_write(client, 0x470, hblank); ++ cx25840_write(client, 0x471, ++ 0xff & (((hblank >> 8) & 0x3) | (hactive << 4))); ++ cx25840_write(client, 0x472, hactive >> 4); ++ ++ /* Sets burst gate delay */ ++ cx25840_write(client, 0x473, burst); ++ ++ /* Sets vertical blanking delay and active duration */ ++ cx25840_write(client, 0x474, vblank); ++ cx25840_write(client, 0x475, ++ 0xff & (((vblank >> 8) & 0x3) | (vactive << 4))); ++ cx25840_write(client, 0x476, vactive >> 4); ++ cx25840_write(client, 0x477, vblank656); ++ ++ /* Sets src decimation rate */ ++ cx25840_write(client, 0x478, 0xff & src_decimation); ++ cx25840_write(client, 0x479, 0xff & (src_decimation >> 8)); ++ ++ /* Sets Luma and UV Low pass filters */ ++ cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); ++ ++ /* Enables comb filters */ ++ cx25840_write(client, 0x47b, comb); ++ ++ /* Sets SC Step*/ ++ cx25840_write(client, 0x47c, sc); ++ cx25840_write(client, 0x47d, 0xff & sc >> 8); ++ cx25840_write(client, 0x47e, 0xff & sc >> 16); ++ ++ /* Sets VBI parameters */ ++ if (std & V4L2_STD_625_50) { ++ cx25840_write(client, 0x47f, 0x01); ++ state->vbi_line_offset = 5; ++ } else { ++ cx25840_write(client, 0x47f, 0x00); ++ state->vbi_line_offset = 8; ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void input_change(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ v4l2_std_id std = state->std; ++ ++ /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ ++ if (std & V4L2_STD_SECAM) { ++ cx25840_write(client, 0x402, 0); ++ } ++ else { ++ cx25840_write(client, 0x402, 0x04); ++ cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); ++ } ++ cx25840_and_or(client, 0x401, ~0x60, 0); ++ cx25840_and_or(client, 0x401, ~0x60, 0x60); ++ ++ /* Don't write into audio registers on cx2583x chips */ ++ if (is_cx2583x(state)) ++ return; ++ ++ cx25840_and_or(client, 0x810, ~0x01, 1); ++ ++ if (state->radio) { ++ cx25840_write(client, 0x808, 0xf9); ++ cx25840_write(client, 0x80b, 0x00); ++ } ++ else if (std & V4L2_STD_525_60) { ++ /* Certain Hauppauge PVR150 models have a hardware bug ++ that causes audio to drop out. For these models the ++ audio standard must be set explicitly. ++ To be precise: it affects cards with tuner models ++ 85, 99 and 112 (model numbers from tveeprom). */ ++ int hw_fix = state->pvr150_workaround; ++ ++ if (std == V4L2_STD_NTSC_M_JP) { ++ /* Japan uses EIAJ audio standard */ ++ cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); ++ } else if (std == V4L2_STD_NTSC_M_KR) { ++ /* South Korea uses A2 audio standard */ ++ cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); ++ } else { ++ /* Others use the BTSC audio standard */ ++ cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); ++ } ++ cx25840_write(client, 0x80b, 0x00); ++ } else if (std & V4L2_STD_PAL) { ++ /* Autodetect audio standard and audio system */ ++ cx25840_write(client, 0x808, 0xff); ++ /* Since system PAL-L is pretty much non-existent and ++ not used by any public broadcast network, force ++ 6.5 MHz carrier to be interpreted as System DK, ++ this avoids DK audio detection instability */ ++ cx25840_write(client, 0x80b, 0x00); ++ } else if (std & V4L2_STD_SECAM) { ++ /* Autodetect audio standard and audio system */ ++ cx25840_write(client, 0x808, 0xff); ++ /* If only one of SECAM-DK / SECAM-L is required, then force ++ 6.5MHz carrier, else autodetect it */ ++ if ((std & V4L2_STD_SECAM_DK) && ++ !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { ++ /* 6.5 MHz carrier to be interpreted as System DK */ ++ cx25840_write(client, 0x80b, 0x00); ++ } else if (!(std & V4L2_STD_SECAM_DK) && ++ (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { ++ /* 6.5 MHz carrier to be interpreted as System L */ ++ cx25840_write(client, 0x80b, 0x08); ++ } else { ++ /* 6.5 MHz carrier to be autodetected */ ++ cx25840_write(client, 0x80b, 0x10); ++ } ++ } ++ ++ cx25840_and_or(client, 0x810, ~0x01, 0); ++} ++ ++static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, ++ enum cx25840_audio_input aud_input) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && ++ vid_input <= CX25840_COMPOSITE8); ++ u8 is_component = (vid_input & CX25840_COMPONENT_ON) == ++ CX25840_COMPONENT_ON; ++ u8 is_dif = (vid_input & CX25840_DIF_ON) == ++ CX25840_DIF_ON; ++ u8 is_svideo = (vid_input & CX25840_SVIDEO_ON) == ++ CX25840_SVIDEO_ON; ++ int luma = vid_input & 0xf0; ++ int chroma = vid_input & 0xf00; ++ u8 reg; ++ u32 val; ++ ++ v4l_dbg(1, cx25840_debug, client, ++ "decoder set video input %d, audio input %d\n", ++ vid_input, aud_input); ++ ++ if (vid_input >= CX25840_VIN1_CH1) { ++ v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", ++ vid_input); ++ reg = vid_input & 0xff; ++ is_composite = !is_component && ++ ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); ++ ++ v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", ++ reg, is_composite); ++ } else if (is_composite) { ++ reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); ++ } else { ++ if ((vid_input & ~0xff0) || ++ luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || ++ chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { ++ v4l_err(client, "0x%04x is not a valid video input!\n", ++ vid_input); ++ return -EINVAL; ++ } ++ reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); ++ if (chroma >= CX25840_SVIDEO_CHROMA7) { ++ reg &= 0x3f; ++ reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; ++ } else { ++ reg &= 0xcf; ++ reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; ++ } ++ } ++ ++ /* The caller has previously prepared the correct routing ++ * configuration in reg (for the cx23885) so we have no ++ * need to attempt to flip bits for earlier av decoders. ++ */ ++ if (!is_cx2388x(state) && !is_cx231xx(state)) { ++ switch (aud_input) { ++ case CX25840_AUDIO_SERIAL: ++ /* do nothing, use serial audio input */ ++ break; ++ case CX25840_AUDIO4: reg &= ~0x30; break; ++ case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; ++ case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; ++ case CX25840_AUDIO7: reg &= ~0xc0; break; ++ case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; ++ ++ default: ++ v4l_err(client, "0x%04x is not a valid audio input!\n", ++ aud_input); ++ return -EINVAL; ++ } ++ } ++ ++ cx25840_write(client, 0x103, reg); ++ ++ /* Set INPUT_MODE to Composite, S-Video or Component */ ++ if (is_component) ++ cx25840_and_or(client, 0x401, ~0x6, 0x6); ++ else ++ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); ++ ++ if (is_cx2388x(state)) { ++ ++ /* Enable or disable the DIF for tuner use */ ++ if (is_dif) { ++ cx25840_and_or(client, 0x102, ~0x80, 0x80); ++ ++ /* Set of defaults for NTSC and PAL */ ++ cx25840_write4(client, 0x31c, 0xc2262600); ++ cx25840_write4(client, 0x320, 0xc2262600); ++ ++ /* 18271 IF - Nobody else yet uses a different ++ * tuner with the DIF, so these are reasonable ++ * assumptions (HVR1250 and HVR1850 specific). ++ */ ++ cx25840_write4(client, 0x318, 0xda262600); ++ cx25840_write4(client, 0x33c, 0x2a24c800); ++ cx25840_write4(client, 0x104, 0x0704dd00); ++ } else { ++ cx25840_write4(client, 0x300, 0x015c28f5); ++ ++ cx25840_and_or(client, 0x102, ~0x80, 0); ++ cx25840_write4(client, 0x340, 0xdf7df83); ++ cx25840_write4(client, 0x104, 0x0704dd80); ++ cx25840_write4(client, 0x314, 0x22400600); ++ cx25840_write4(client, 0x318, 0x40002600); ++ cx25840_write4(client, 0x324, 0x40002600); ++ cx25840_write4(client, 0x32c, 0x0250e620); ++ cx25840_write4(client, 0x39c, 0x01FF0B00); ++ ++ cx25840_write4(client, 0x410, 0xffff0dbf); ++ cx25840_write4(client, 0x414, 0x00137d03); ++ ++ /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is ++ CHROMA_CTRL */ ++ if (is_cx23888(state)) ++ cx25840_write4(client, 0x418, 0x01008080); ++ else ++ cx25840_write4(client, 0x418, 0x01000000); ++ ++ cx25840_write4(client, 0x41c, 0x00000000); ++ ++ /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is ++ CRUSH_CTRL */ ++ if (is_cx23888(state)) ++ cx25840_write4(client, 0x420, 0x001c3e0f); ++ else ++ cx25840_write4(client, 0x420, 0x001c8282); ++ ++ cx25840_write4(client, 0x42c, 0x42600000); ++ cx25840_write4(client, 0x430, 0x0000039b); ++ cx25840_write4(client, 0x438, 0x00000000); ++ ++ cx25840_write4(client, 0x440, 0xF8E3E824); ++ cx25840_write4(client, 0x444, 0x401040dc); ++ cx25840_write4(client, 0x448, 0xcd3f02a0); ++ cx25840_write4(client, 0x44c, 0x161f1000); ++ cx25840_write4(client, 0x450, 0x00000802); ++ ++ cx25840_write4(client, 0x91c, 0x01000000); ++ cx25840_write4(client, 0x8e0, 0x03063870); ++ cx25840_write4(client, 0x8d4, 0x7FFF0024); ++ cx25840_write4(client, 0x8d0, 0x00063073); ++ ++ cx25840_write4(client, 0x8c8, 0x00010000); ++ cx25840_write4(client, 0x8cc, 0x00080023); ++ ++ /* DIF BYPASS */ ++ cx25840_write4(client, 0x33c, 0x2a04c800); ++ } ++ ++ /* Reset the DIF */ ++ cx25840_write4(client, 0x398, 0); ++ } ++ ++ if (!is_cx2388x(state) && !is_cx231xx(state)) { ++ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ ++ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); ++ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ ++ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) ++ cx25840_and_or(client, 0x102, ~0x4, 4); ++ else ++ cx25840_and_or(client, 0x102, ~0x4, 0); ++ } else { ++ /* Set DUAL_MODE_ADC2 to 1 if component*/ ++ cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); ++ if (is_composite) { ++ /* ADC2 input select channel 2 */ ++ cx25840_and_or(client, 0x102, ~0x2, 0); ++ } else if (!is_component) { ++ /* S-Video */ ++ if (chroma >= CX25840_SVIDEO_CHROMA7) { ++ /* ADC2 input select channel 3 */ ++ cx25840_and_or(client, 0x102, ~0x2, 2); ++ } else { ++ /* ADC2 input select channel 2 */ ++ cx25840_and_or(client, 0x102, ~0x2, 0); ++ } ++ } ++ ++ /* cx23885 / SVIDEO */ ++ if (is_cx2388x(state) && is_svideo) { ++#define AFE_CTRL (0x104) ++#define MODE_CTRL (0x400) ++ cx25840_and_or(client, 0x102, ~0x2, 0x2); ++ ++ val = cx25840_read4(client, MODE_CTRL); ++ val &= 0xFFFFF9FF; ++ ++ /* YC */ ++ val |= 0x00000200; ++ val &= ~0x2000; ++ cx25840_write4(client, MODE_CTRL, val); ++ ++ val = cx25840_read4(client, AFE_CTRL); ++ ++ /* Chroma in select */ ++ val |= 0x00001000; ++ val &= 0xfffffe7f; ++ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8). ++ * This sets them to use video rather than audio. ++ * Only one of the two will be in use. ++ */ ++ cx25840_write4(client, AFE_CTRL, val); ++ } else ++ cx25840_and_or(client, 0x102, ~0x2, 0); ++ } ++ ++ state->vid_input = vid_input; ++ state->aud_input = aud_input; ++ cx25840_audio_set_path(client); ++ input_change(client); ++ ++ if (is_cx2388x(state)) { ++ /* Audio channel 1 src : Parallel 1 */ ++ cx25840_write(client, 0x124, 0x03); ++ ++ /* Select AFE clock pad output source */ ++ cx25840_write(client, 0x144, 0x05); ++ ++ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ ++ cx25840_write(client, 0x914, 0xa0); ++ ++ /* I2S_OUT_CTL: ++ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 ++ * I2S_OUT_MASTER_MODE = Master ++ */ ++ cx25840_write(client, 0x918, 0xa0); ++ cx25840_write(client, 0x919, 0x01); ++ } else if (is_cx231xx(state)) { ++ /* Audio channel 1 src : Parallel 1 */ ++ cx25840_write(client, 0x124, 0x03); ++ ++ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ ++ cx25840_write(client, 0x914, 0xa0); ++ ++ /* I2S_OUT_CTL: ++ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 ++ * I2S_OUT_MASTER_MODE = Master ++ */ ++ cx25840_write(client, 0x918, 0xa0); ++ cx25840_write(client, 0x919, 0x01); ++ } ++ ++ if (is_cx2388x(state) && ((aud_input == CX25840_AUDIO7) || ++ (aud_input == CX25840_AUDIO6))) { ++ /* Configure audio from LR1 or LR2 input */ ++ cx25840_write4(client, 0x910, 0); ++ cx25840_write4(client, 0x8d0, 0x63073); ++ } else ++ if (is_cx2388x(state) && (aud_input == CX25840_AUDIO8)) { ++ /* Configure audio from tuner/sif input */ ++ cx25840_write4(client, 0x910, 0x12b000c9); ++ cx25840_write4(client, 0x8d0, 0x1f063870); ++ } ++ ++ if (is_cx23888(state)) { ++ /* HVR1850 */ ++ /* AUD_IO_CTRL - I2S Input, Parallel1*/ ++ /* - Channel 1 src - Parallel1 (Merlin out) */ ++ /* - Channel 2 src - Parallel2 (Merlin out) */ ++ /* - Channel 3 src - Parallel3 (Merlin AC97 out) */ ++ /* - I2S source and dir - Merlin, output */ ++ cx25840_write4(client, 0x124, 0x100); ++ ++ if (!is_dif) { ++ /* Stop microcontroller if we don't need it ++ * to avoid audio popping on svideo/composite use. ++ */ ++ cx25840_and_or(client, 0x803, ~0x10, 0x00); ++ } ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int set_v4lstd(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ u8 fmt = 0; /* zero is autodetect */ ++ u8 pal_m = 0; ++ ++ /* First tests should be against specific std */ ++ if (state->std == V4L2_STD_NTSC_M_JP) { ++ fmt = 0x2; ++ } else if (state->std == V4L2_STD_NTSC_443) { ++ fmt = 0x3; ++ } else if (state->std == V4L2_STD_PAL_M) { ++ pal_m = 1; ++ fmt = 0x5; ++ } else if (state->std == V4L2_STD_PAL_N) { ++ fmt = 0x6; ++ } else if (state->std == V4L2_STD_PAL_Nc) { ++ fmt = 0x7; ++ } else if (state->std == V4L2_STD_PAL_60) { ++ fmt = 0x8; ++ } else { ++ /* Then, test against generic ones */ ++ if (state->std & V4L2_STD_NTSC) ++ fmt = 0x1; ++ else if (state->std & V4L2_STD_PAL) ++ fmt = 0x4; ++ else if (state->std & V4L2_STD_SECAM) ++ fmt = 0xc; ++ } ++ ++ v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); ++ ++ /* Follow step 9 of section 3.16 in the cx25840 datasheet. ++ Without this PAL may display a vertical ghosting effect. ++ This happens for example with the Yuan MPC622. */ ++ if (fmt >= 4 && fmt < 8) { ++ /* Set format to NTSC-M */ ++ cx25840_and_or(client, 0x400, ~0xf, 1); ++ /* Turn off LCOMB */ ++ cx25840_and_or(client, 0x47b, ~6, 0); ++ } ++ cx25840_and_or(client, 0x400, ~0xf, fmt); ++ cx25840_and_or(client, 0x403, ~0x3, pal_m); ++ if (is_cx23888(state)) ++ cx23888_std_setup(client); ++ else ++ cx25840_std_setup(client); ++ if (!is_cx2583x(state)) ++ input_change(client); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ cx25840_write(client, 0x414, ctrl->val - 128); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ cx25840_write(client, 0x415, ctrl->val << 1); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ if (is_cx23888(state)) { ++ cx25840_write(client, 0x418, ctrl->val << 1); ++ cx25840_write(client, 0x419, ctrl->val << 1); ++ } else { ++ cx25840_write(client, 0x420, ctrl->val << 1); ++ cx25840_write(client, 0x421, ctrl->val << 1); ++ } ++ break; ++ ++ case V4L2_CID_HUE: ++ if (is_cx23888(state)) ++ cx25840_write(client, 0x41a, ctrl->val); ++ else ++ cx25840_write(client, 0x422, ctrl->val); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int HSC, VSC, Vsrc, Hsrc, filter, Vlines; ++ int is_50Hz = !(state->std & V4L2_STD_525_60); ++ ++ if (fmt->code != V4L2_MBUS_FMT_FIXED) ++ return -EINVAL; ++ ++ fmt->field = V4L2_FIELD_INTERLACED; ++ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ if (is_cx23888(state)) { ++ Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; ++ Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; ++ } else { ++ Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; ++ Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; ++ } ++ ++ if (is_cx23888(state)) { ++ Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; ++ Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; ++ } else { ++ Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; ++ Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; ++ } ++ ++ Vlines = fmt->height + (is_50Hz ? 4 : 7); ++ ++ if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || ++ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { ++ v4l_err(client, "%dx%d is not a valid size!\n", ++ fmt->width, fmt->height); ++ return -ERANGE; ++ } ++ ++ HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); ++ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); ++ VSC &= 0x1fff; ++ ++ if (fmt->width >= 385) ++ filter = 0; ++ else if (fmt->width > 192) ++ filter = 1; ++ else if (fmt->width > 96) ++ filter = 2; ++ else ++ filter = 3; ++ ++ v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", ++ fmt->width, fmt->height, HSC, VSC); ++ ++ /* HSCALE=HSC */ ++ cx25840_write(client, 0x418, HSC & 0xff); ++ cx25840_write(client, 0x419, (HSC >> 8) & 0xff); ++ cx25840_write(client, 0x41a, HSC >> 16); ++ /* VSCALE=VSC */ ++ cx25840_write(client, 0x41c, VSC & 0xff); ++ cx25840_write(client, 0x41d, VSC >> 8); ++ /* VS_INTRLACE=1 VFILT=filter */ ++ cx25840_write(client, 0x41e, 0x8 | filter); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void log_video_status(struct i2c_client *client) ++{ ++ static const char *const fmt_strs[] = { ++ "0x0", ++ "NTSC-M", "NTSC-J", "NTSC-4.43", ++ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", ++ "0x9", "0xA", "0xB", ++ "SECAM", ++ "0xD", "0xE", "0xF" ++ }; ++ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; ++ u8 gen_stat1 = cx25840_read(client, 0x40d); ++ u8 gen_stat2 = cx25840_read(client, 0x40e); ++ int vid_input = state->vid_input; ++ ++ v4l_info(client, "Video signal: %spresent\n", ++ (gen_stat2 & 0x20) ? "" : "not "); ++ v4l_info(client, "Detected format: %s\n", ++ fmt_strs[gen_stat1 & 0xf]); ++ ++ v4l_info(client, "Specified standard: %s\n", ++ vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); ++ ++ if (vid_input >= CX25840_COMPOSITE1 && ++ vid_input <= CX25840_COMPOSITE8) { ++ v4l_info(client, "Specified video input: Composite %d\n", ++ vid_input - CX25840_COMPOSITE1 + 1); ++ } else { ++ v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", ++ (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); ++ } ++ ++ v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void log_audio_status(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ u8 download_ctl = cx25840_read(client, 0x803); ++ u8 mod_det_stat0 = cx25840_read(client, 0x804); ++ u8 mod_det_stat1 = cx25840_read(client, 0x805); ++ u8 audio_config = cx25840_read(client, 0x808); ++ u8 pref_mode = cx25840_read(client, 0x809); ++ u8 afc0 = cx25840_read(client, 0x80b); ++ u8 mute_ctl = cx25840_read(client, 0x8d3); ++ int aud_input = state->aud_input; ++ char *p; ++ ++ switch (mod_det_stat0) { ++ case 0x00: p = "mono"; break; ++ case 0x01: p = "stereo"; break; ++ case 0x02: p = "dual"; break; ++ case 0x04: p = "tri"; break; ++ case 0x10: p = "mono with SAP"; break; ++ case 0x11: p = "stereo with SAP"; break; ++ case 0x12: p = "dual with SAP"; break; ++ case 0x14: p = "tri with SAP"; break; ++ case 0xfe: p = "forced mode"; break; ++ default: p = "not defined"; ++ } ++ v4l_info(client, "Detected audio mode: %s\n", p); ++ ++ switch (mod_det_stat1) { ++ case 0x00: p = "not defined"; break; ++ case 0x01: p = "EIAJ"; break; ++ case 0x02: p = "A2-M"; break; ++ case 0x03: p = "A2-BG"; break; ++ case 0x04: p = "A2-DK1"; break; ++ case 0x05: p = "A2-DK2"; break; ++ case 0x06: p = "A2-DK3"; break; ++ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; ++ case 0x08: p = "AM-L"; break; ++ case 0x09: p = "NICAM-BG"; break; ++ case 0x0a: p = "NICAM-DK"; break; ++ case 0x0b: p = "NICAM-I"; break; ++ case 0x0c: p = "NICAM-L"; break; ++ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; ++ case 0x0e: p = "IF FM Radio"; break; ++ case 0x0f: p = "BTSC"; break; ++ case 0x10: p = "high-deviation FM"; break; ++ case 0x11: p = "very high-deviation FM"; break; ++ case 0xfd: p = "unknown audio standard"; break; ++ case 0xfe: p = "forced audio standard"; break; ++ case 0xff: p = "no detected audio standard"; break; ++ default: p = "not defined"; ++ } ++ v4l_info(client, "Detected audio standard: %s\n", p); ++ v4l_info(client, "Audio microcontroller: %s\n", ++ (download_ctl & 0x10) ? ++ ((mute_ctl & 0x2) ? "detecting" : "running") : "stopped"); ++ ++ switch (audio_config >> 4) { ++ case 0x00: p = "undefined"; break; ++ case 0x01: p = "BTSC"; break; ++ case 0x02: p = "EIAJ"; break; ++ case 0x03: p = "A2-M"; break; ++ case 0x04: p = "A2-BG"; break; ++ case 0x05: p = "A2-DK1"; break; ++ case 0x06: p = "A2-DK2"; break; ++ case 0x07: p = "A2-DK3"; break; ++ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; ++ case 0x09: p = "AM-L"; break; ++ case 0x0a: p = "NICAM-BG"; break; ++ case 0x0b: p = "NICAM-DK"; break; ++ case 0x0c: p = "NICAM-I"; break; ++ case 0x0d: p = "NICAM-L"; break; ++ case 0x0e: p = "FM radio"; break; ++ case 0x0f: p = "automatic detection"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Configured audio standard: %s\n", p); ++ ++ if ((audio_config >> 4) < 0xF) { ++ switch (audio_config & 0xF) { ++ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; ++ case 0x01: p = "MONO2 (LANGUAGE B)"; break; ++ case 0x02: p = "MONO3 (STEREO forced MONO)"; break; ++ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; ++ case 0x04: p = "STEREO"; break; ++ case 0x05: p = "DUAL1 (AB)"; break; ++ case 0x06: p = "DUAL2 (AC) (FM)"; break; ++ case 0x07: p = "DUAL3 (BC) (FM)"; break; ++ case 0x08: p = "DUAL4 (AC) (AM)"; break; ++ case 0x09: p = "DUAL5 (BC) (AM)"; break; ++ case 0x0a: p = "SAP"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Configured audio mode: %s\n", p); ++ } else { ++ switch (audio_config & 0xF) { ++ case 0x00: p = "BG"; break; ++ case 0x01: p = "DK1"; break; ++ case 0x02: p = "DK2"; break; ++ case 0x03: p = "DK3"; break; ++ case 0x04: p = "I"; break; ++ case 0x05: p = "L"; break; ++ case 0x06: p = "BTSC"; break; ++ case 0x07: p = "EIAJ"; break; ++ case 0x08: p = "A2-M"; break; ++ case 0x09: p = "FM Radio"; break; ++ case 0x0f: p = "automatic standard and mode detection"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Configured audio system: %s\n", p); ++ } ++ ++ if (aud_input) { ++ v4l_info(client, "Specified audio input: Tuner (In%d)\n", aud_input); ++ } else { ++ v4l_info(client, "Specified audio input: External\n"); ++ } ++ ++ switch (pref_mode & 0xf) { ++ case 0: p = "mono/language A"; break; ++ case 1: p = "language B"; break; ++ case 2: p = "language C"; break; ++ case 3: p = "analog fallback"; break; ++ case 4: p = "stereo"; break; ++ case 5: p = "language AC"; break; ++ case 6: p = "language BC"; break; ++ case 7: p = "language AB"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Preferred audio mode: %s\n", p); ++ ++ if ((audio_config & 0xf) == 0xf) { ++ switch ((afc0 >> 3) & 0x3) { ++ case 0: p = "system DK"; break; ++ case 1: p = "system L"; break; ++ case 2: p = "autodetect"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Selected 65 MHz format: %s\n", p); ++ ++ switch (afc0 & 0x7) { ++ case 0: p = "chroma"; break; ++ case 1: p = "BTSC"; break; ++ case 2: p = "EIAJ"; break; ++ case 3: p = "A2-M"; break; ++ case 4: p = "autodetect"; break; ++ default: p = "undefined"; ++ } ++ v4l_info(client, "Selected 45 MHz format: %s\n", p); ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* This load_fw operation must be called to load the driver's firmware. ++ Without this the audio standard detection will fail and you will ++ only get mono. ++ ++ Since loading the firmware is often problematic when the driver is ++ compiled into the kernel I recommend postponing calling this function ++ until the first open of the video device. Another reason for ++ postponing it is that loading this firmware takes a long time (seconds) ++ due to the slow i2c bus speed. So it will speed up the boot process if ++ you can avoid loading the fw as long as the video device isn't used. */ ++static int cx25840_load_fw(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!state->is_initialized) { ++ /* initialize and load firmware */ ++ state->is_initialized = 1; ++ if (is_cx2583x(state)) ++ cx25836_initialize(client); ++ else if (is_cx2388x(state)) ++ cx23885_initialize(client); ++ else if (is_cx231xx(state)) ++ cx231xx_initialize(client); ++ else ++ cx25840_initialize(client); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->size = 1; ++ reg->val = cx25840_read(client, reg->reg & 0x0fff); ++ return 0; ++} ++ ++static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 v; ++ ++ if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) ++ return 0; ++ ++ v4l_dbg(1, cx25840_debug, client, "%s audio output\n", ++ enable ? "enable" : "disable"); ++ ++ if (enable) { ++ v = cx25840_read(client, 0x115) | 0x80; ++ cx25840_write(client, 0x115, v); ++ v = cx25840_read(client, 0x116) | 0x03; ++ cx25840_write(client, 0x116, v); ++ } else { ++ v = cx25840_read(client, 0x115) & ~(0x80); ++ cx25840_write(client, 0x115, v); ++ v = cx25840_read(client, 0x116) & ~(0x03); ++ cx25840_write(client, 0x116, v); ++ } ++ return 0; ++} ++ ++static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 v; ++ ++ v4l_dbg(1, cx25840_debug, client, "%s video output\n", ++ enable ? "enable" : "disable"); ++ if (enable) { ++ if (is_cx2388x(state) || is_cx231xx(state)) { ++ v = cx25840_read(client, 0x421) | 0x0b; ++ cx25840_write(client, 0x421, v); ++ } else { ++ v = cx25840_read(client, 0x115) | 0x0c; ++ cx25840_write(client, 0x115, v); ++ v = cx25840_read(client, 0x116) | 0x04; ++ cx25840_write(client, 0x116, v); ++ } ++ } else { ++ if (is_cx2388x(state) || is_cx231xx(state)) { ++ v = cx25840_read(client, 0x421) & ~(0x0b); ++ cx25840_write(client, 0x421, v); ++ } else { ++ v = cx25840_read(client, 0x115) & ~(0x0c); ++ cx25840_write(client, 0x115, v); ++ v = cx25840_read(client, 0x116) & ~(0x04); ++ cx25840_write(client, 0x116, v); ++ } ++ } ++ return 0; ++} ++ ++/* Query the current detected video format */ ++static int cx25840_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ v4l2_std_id stds[] = { ++ /* 0000 */ V4L2_STD_UNKNOWN, ++ ++ /* 0001 */ V4L2_STD_NTSC_M, ++ /* 0010 */ V4L2_STD_NTSC_M_JP, ++ /* 0011 */ V4L2_STD_NTSC_443, ++ /* 0100 */ V4L2_STD_PAL, ++ /* 0101 */ V4L2_STD_PAL_M, ++ /* 0110 */ V4L2_STD_PAL_N, ++ /* 0111 */ V4L2_STD_PAL_Nc, ++ /* 1000 */ V4L2_STD_PAL_60, ++ ++ /* 1001 */ V4L2_STD_UNKNOWN, ++ /* 1010 */ V4L2_STD_UNKNOWN, ++ /* 1001 */ V4L2_STD_UNKNOWN, ++ /* 1010 */ V4L2_STD_UNKNOWN, ++ /* 1011 */ V4L2_STD_UNKNOWN, ++ /* 1110 */ V4L2_STD_UNKNOWN, ++ /* 1111 */ V4L2_STD_UNKNOWN ++ }; ++ ++ u32 fmt = (cx25840_read4(client, 0x40c) >> 8) & 0xf; ++ *std = stds[ fmt ]; ++ ++ v4l_dbg(1, cx25840_debug, client, "g_std fmt = %x, v4l2_std_id = 0x%x\n", ++ fmt, (unsigned int)stds[ fmt ]); ++ ++ return 0; ++} ++ ++static int cx25840_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* A limited function that checks for signal status and returns ++ * the state. ++ */ ++ ++ /* Check for status of Horizontal lock (SRC lock isn't reliable) */ ++ if ((cx25840_read4(client, 0x40c) & 0x00010000) == 0) ++ *status |= V4L2_IN_ST_NO_SIGNAL; ++ ++ return 0; ++} ++ ++static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (state->radio == 0 && state->std == std) ++ return 0; ++ state->radio = 0; ++ state->std = std; ++ return set_v4lstd(client); ++} ++ ++static int cx25840_s_radio(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ ++ state->radio = 1; ++ return 0; ++} ++ ++static int cx25840_s_video_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (is_cx23888(state)) ++ cx23888_std_setup(client); ++ ++ return set_input(client, input, state->aud_input); ++} ++ ++static int cx25840_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (is_cx23888(state)) ++ cx23888_std_setup(client); ++ return set_input(client, state->vid_input, input); ++} ++ ++static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ input_change(client); ++ return 0; ++} ++ ++static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 vpres = cx25840_read(client, 0x40e) & 0x20; ++ u8 mode; ++ int val = 0; ++ ++ if (state->radio) ++ return 0; ++ ++ vt->signal = vpres ? 0xffff : 0x0; ++ if (is_cx2583x(state)) ++ return 0; ++ ++ vt->capability |= ++ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | ++ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; ++ ++ mode = cx25840_read(client, 0x804); ++ ++ /* get rxsubchans and audmode */ ++ if ((mode & 0xf) == 1) ++ val |= V4L2_TUNER_SUB_STEREO; ++ else ++ val |= V4L2_TUNER_SUB_MONO; ++ ++ if (mode == 2 || mode == 4) ++ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ ++ if (mode & 0x10) ++ val |= V4L2_TUNER_SUB_SAP; ++ ++ vt->rxsubchans = val; ++ vt->audmode = state->audmode; ++ return 0; ++} ++ ++static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (state->radio || is_cx2583x(state)) ++ return 0; ++ ++ switch (vt->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ /* mono -> mono ++ stereo -> mono ++ bilingual -> lang1 */ ++ cx25840_and_or(client, 0x809, ~0xf, 0x00); ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang1 */ ++ cx25840_and_or(client, 0x809, ~0xf, 0x04); ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang1/lang2 */ ++ cx25840_and_or(client, 0x809, ~0xf, 0x07); ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang2 */ ++ cx25840_and_or(client, 0x809, ~0xf, 0x01); ++ break; ++ default: ++ return -EINVAL; ++ } ++ state->audmode = vt->audmode; ++ return 0; ++} ++ ++static int cx25840_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (is_cx2583x(state)) ++ cx25836_initialize(client); ++ else if (is_cx2388x(state)) ++ cx23885_initialize(client); ++ else if (is_cx231xx(state)) ++ cx231xx_initialize(client); ++ else ++ cx25840_initialize(client); ++ return 0; ++} ++ ++static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); ++} ++ ++static int cx25840_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ log_video_status(client); ++ if (!is_cx2583x(state)) ++ log_audio_status(client); ++ cx25840_ir_log_status(sd); ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++static int cx23885_irq_handler(struct v4l2_subdev *sd, u32 status, ++ bool *handled) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ u8 irq_stat, aud_stat, aud_en, ir_stat, ir_en; ++ u32 vid_stat, aud_mc_stat; ++ bool block_handled; ++ int ret = 0; ++ ++ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); ++ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (entry): %s %s %s\n", ++ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", ++ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", ++ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); ++ ++ if ((is_cx23885(state) || is_cx23887(state))) { ++ ir_stat = cx25840_read(c, CX25840_IR_STATS_REG); ++ ir_en = cx25840_read(c, CX25840_IR_IRQEN_REG); ++ v4l_dbg(2, cx25840_debug, c, ++ "AV Core ir IRQ status: %#04x disables: %#04x\n", ++ ir_stat, ir_en); ++ if (irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT) { ++ block_handled = false; ++ ret = cx25840_ir_irq_handler(sd, ++ status, &block_handled); ++ if (block_handled) ++ *handled = true; ++ } ++ } ++ ++ aud_stat = cx25840_read(c, CX25840_AUD_INT_STAT_REG); ++ aud_en = cx25840_read(c, CX25840_AUD_INT_CTRL_REG); ++ v4l_dbg(2, cx25840_debug, c, ++ "AV Core audio IRQ status: %#04x disables: %#04x\n", ++ aud_stat, aud_en); ++ aud_mc_stat = cx25840_read4(c, CX23885_AUD_MC_INT_MASK_REG); ++ v4l_dbg(2, cx25840_debug, c, ++ "AV Core audio MC IRQ status: %#06x enables: %#06x\n", ++ aud_mc_stat >> CX23885_AUD_MC_INT_STAT_SHFT, ++ aud_mc_stat & CX23885_AUD_MC_INT_CTRL_BITS); ++ if (irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT) { ++ if (aud_stat) { ++ cx25840_write(c, CX25840_AUD_INT_STAT_REG, aud_stat); ++ *handled = true; ++ } ++ } ++ ++ vid_stat = cx25840_read4(c, CX25840_VID_INT_STAT_REG); ++ v4l_dbg(2, cx25840_debug, c, ++ "AV Core video IRQ status: %#06x disables: %#06x\n", ++ vid_stat & CX25840_VID_INT_STAT_BITS, ++ vid_stat >> CX25840_VID_INT_MASK_SHFT); ++ if (irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT) { ++ if (vid_stat & CX25840_VID_INT_STAT_BITS) { ++ cx25840_write4(c, CX25840_VID_INT_STAT_REG, vid_stat); ++ *handled = true; ++ } ++ } ++ ++ irq_stat = cx25840_read(c, CX23885_PIN_CTRL_IRQ_REG); ++ v4l_dbg(2, cx25840_debug, c, "AV Core IRQ status (exit): %s %s %s\n", ++ irq_stat & CX23885_PIN_CTRL_IRQ_IR_STAT ? "ir" : " ", ++ irq_stat & CX23885_PIN_CTRL_IRQ_AUD_STAT ? "aud" : " ", ++ irq_stat & CX23885_PIN_CTRL_IRQ_VID_STAT ? "vid" : " "); ++ ++ return ret; ++} ++ ++static int cx25840_irq_handler(struct v4l2_subdev *sd, u32 status, ++ bool *handled) ++{ ++ struct cx25840_state *state = to_state(sd); ++ ++ *handled = false; ++ ++ /* Only support the CX2388[578] AV Core for now */ ++ if (is_cx2388x(state)) ++ return cx23885_irq_handler(sd, status, handled); ++ ++ return -ENODEV; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++#define DIF_PLL_FREQ_WORD (0x300) ++#define DIF_BPF_COEFF01 (0x348) ++#define DIF_BPF_COEFF23 (0x34c) ++#define DIF_BPF_COEFF45 (0x350) ++#define DIF_BPF_COEFF67 (0x354) ++#define DIF_BPF_COEFF89 (0x358) ++#define DIF_BPF_COEFF1011 (0x35c) ++#define DIF_BPF_COEFF1213 (0x360) ++#define DIF_BPF_COEFF1415 (0x364) ++#define DIF_BPF_COEFF1617 (0x368) ++#define DIF_BPF_COEFF1819 (0x36c) ++#define DIF_BPF_COEFF2021 (0x370) ++#define DIF_BPF_COEFF2223 (0x374) ++#define DIF_BPF_COEFF2425 (0x378) ++#define DIF_BPF_COEFF2627 (0x37c) ++#define DIF_BPF_COEFF2829 (0x380) ++#define DIF_BPF_COEFF3031 (0x384) ++#define DIF_BPF_COEFF3233 (0x388) ++#define DIF_BPF_COEFF3435 (0x38c) ++#define DIF_BPF_COEFF36 (0x390) ++ ++static void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) ++{ ++ u64 pll_freq; ++ u32 pll_freq_word; ++ ++ v4l_dbg(1, cx25840_debug, client, "%s(%d)\n", __func__, ifHz); ++ ++ /* Assuming TV */ ++ /* Calculate the PLL frequency word based on the adjusted ifHz */ ++ pll_freq = div_u64((u64)ifHz * 268435456, 50000000); ++ pll_freq_word = (u32)pll_freq; ++ ++ cx25840_write4(client, DIF_PLL_FREQ_WORD, pll_freq_word); ++ ++ /* Round down to the nearest 100KHz */ ++ ifHz = (ifHz / 100000) * 100000; ++ ++ if (ifHz < 3000000) ++ ifHz = 3000000; ++ ++ if (ifHz > 16000000) ++ ifHz = 16000000; ++ ++ v4l_dbg(1, cx25840_debug, client, "%s(%d) again\n", __func__, ifHz); ++ ++ switch (ifHz) { ++ case 3000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0024); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bfff8); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff50); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed8fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe34); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfebaffc7); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d031f); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04f0065d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07010688); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04c901d6); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f9d3); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f342); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f337); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf64efb22); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105070f); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0c460fce); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220032); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00370026); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff91); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0efe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fdcc); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe0afedb); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440224); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0434060c); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738074e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06090361); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99fb39); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef3b6); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21af2a5); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf573fa33); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034067d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bfb0fb9); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000e); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c004f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x002fffdf); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff5cfeb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dfd92); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7ffe03); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36010a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03410575); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x072607d2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x071804d5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134fcb7); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff451); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223f22e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4a7f94b); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff6405e8); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0bae0fa4); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000008); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0036); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056006d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00670030); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffbdff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46fd8d); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd25fd4f); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35ffe0); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0224049f); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9080e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07ef0627); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9fe45); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f513); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250f1d2); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3ecf869); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930552); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b5f0f8f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0001); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000f002c); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054007d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0093007c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0024ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6fdbb); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd03fcca); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51feb9); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00eb0392); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270802); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08880750); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044dffdb); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf5f8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0f193); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf342f78f); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc404b9); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0b0e0f78); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff9); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0002001b); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046007d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad00ba); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00870000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe1a); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1bfc7e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fda4); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffa5025c); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x054507ad); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08dd0847); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80172); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef6ff); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313f170); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2abf6bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6041f); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0abc0f61); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff3); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff50006); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f006c); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b200e3); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00dc007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9fea0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd6bfc71); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fcb1); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe65010b); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x042d0713); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ec0906); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020302); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff823); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7f16a); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf228f5f5); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2a0384); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a670f4a); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7ffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9fff1); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010004d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a100f2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011a00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053ff44); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdedfca2); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fbef); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd39ffae); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x02ea0638); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08b50987); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230483); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f960); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45bf180); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1b8f537); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb6102e7); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x0a110f32); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffdd); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00024); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x007c00e5); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013a014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e6fff8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe98fd0f); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fb67); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc32fe54); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01880525); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x083909c7); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x091505ee); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7fab3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52df1b4); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf15df484); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9b0249); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09ba0f19); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 3900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff0); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffcf); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1fff6); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x004800be); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01390184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x016300ac); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff5efdb1); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb23); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb5cfd0d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x001703e4); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x077b09c4); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d2073c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251fc18); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61cf203); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf118f3dc); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d801aa); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09600eff); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffefff4); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffca); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x000b0082); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01170198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10152); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0030fe7b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb24); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfac3fbe9); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5027f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0683097f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a560867); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2fd89); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723f26f); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0e8f341); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919010a); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x09060ee5); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fffb); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8ffca); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffa4); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffcd0036); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d70184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f601dc); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ffff60); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb6d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6efaf5); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd410103); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x055708f9); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e0969); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543ff02); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842f2f5); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cef2b2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85e006b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x08aa0ecb); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050003); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff3ffd3); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaff8b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ffe5); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0080014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe023f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ba0050); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fbf8); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa62fa3b); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9ff7e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04010836); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa90a3d); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f007f); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf975f395); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0cbf231); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ffcb); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x084c0eaf); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000a); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0000ffe4); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff81); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff96); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x001c00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70271); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0254013b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fcbd); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa9ff9c5); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbfdfe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x028c073b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a750adf); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e101fa); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab8f44e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0ddf1be); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ff2b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x07ed0e94); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000efff8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff87); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff54); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffb5007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01860270); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c00210); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fdb2); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb22f997); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2fc90); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0102060f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a050b4c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902036e); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0af51e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf106f15a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64efe8b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x078d0e77); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0019000e); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff9e); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff25); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff560000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112023b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f702c0); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfec8); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbe5f9b3); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947fb41); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xff7004b9); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a0b81); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a0004d8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd65f603); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf144f104); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aafdec); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x072b0e5a); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200022); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ffc1); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff09ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x008601d7); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f50340); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fff0); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcddfa19); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa1e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfde30343); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08790b7f); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50631); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec7f6fc); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf198f0bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50dfd4e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06c90e3d); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220030); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffed); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff87ff15); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed6ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffed014c); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b90386); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110119); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfdfefac4); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6f92f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc6701b7); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07670b44); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0776); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x002df807); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf200f086); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477fcb1); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06650e1e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0009); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f001b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffbcff36); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec2feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff5600a5); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0248038d); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00232); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff39fbab); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4f87f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb060020); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x062a0ad2); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf908a3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0192f922); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf27df05e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8fc14); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x06000e00); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 4900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0002); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00160037); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510046); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff9ff6d); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed0fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefff0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01aa0356); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413032b); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x007ffcc5); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cf812); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9cefe87); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c90a2c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4309b4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f3fa4a); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf30ef046); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361fb7a); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x059b0de0); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000a002d); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00570067); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0037ffb5); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfefffe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62ff3d); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00ec02e3); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x043503f6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x01befe05); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa27f7ee); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8c6fcf8); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x034c0954); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0aa4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x044cfb7e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3b1f03f); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2fae1); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x05340dc0); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff4); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffd001e); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051007b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x006e0006); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff48fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfe9a); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x001d023e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130488); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02e6ff5b); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1ef812); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7f7fb7f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bc084e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430b72); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x059afcba); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf467f046); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cfa4a); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x04cd0da0); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff00009); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f007f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00980056); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffa5feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe15); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff4b0170); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b004d7); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03e800b9); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc48f87f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf768fa23); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022071f); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90c1b); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x06dafdfd); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf52df05e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef9b5); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x04640d7f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6fff3); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00250072); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00af009c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x000cff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13fdb8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe870089); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104e1); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04b8020f); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd98f92f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf71df8f0); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe8805ce); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0c9c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0808ff44); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf603f086); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af922); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03fb0d5e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe0); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00050056); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b000d1); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0071ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53fd8c); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfddfff99); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104a3); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x054a034d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff01fa1e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf717f7ed); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf50461); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50cf4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0921008d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf6e7f0bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff891); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03920d3b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffffff3); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffd1); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5002f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x009c00ed); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00cb0000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfebafd94); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd61feb0); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d0422); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05970464); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0074fb41); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf759f721); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb7502de); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000d21); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a2201d4); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7d9f104); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf804); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x03280d19); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fffa); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3ffc9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc90002); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x007500ef); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x010e007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3dfdcf); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd16fddd); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440365); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059b0548); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3fc90); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7dff691); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0f014d); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020d23); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0318); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8d7f15a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f779); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x02bd0cf6); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060001); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffecffc9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffd4); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x004000d5); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013600f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd3fe39); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd04fd31); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff360277); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x055605ef); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x033efdfe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8a5f642); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbffb6); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10cfb); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50456); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9dff1be); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f6f2); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x02520cd2); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080009); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff8ffd2); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffac); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x000200a3); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013c014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x006dfec9); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2bfcb7); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe350165); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04cb0651); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477ff7e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9a5f635); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1fe20); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0ca8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c81058b); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfaf0f231); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f66d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x01e60cae); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 5900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009000e); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0005ffe1); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacff90); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5005f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01210184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fcff72); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd8afc77); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51003f); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04020669); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830103); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfad7f66b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8fc93); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430c2b); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d06b5); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfc08f2b2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af5ec); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x017b0c89); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0012fff5); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaff82); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff8e000f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e80198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750028); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe18fc75); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99ff15); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03050636); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0656027f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc32f6e2); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614fb17); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20b87); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d7707d2); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfd26f341); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf56f); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x010f0c64); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001c000b); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff84); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ffbe); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00960184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd00da); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeccfcb2); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fdf9); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x01e005bc); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e703e4); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfdabf798); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f9b3); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510abd); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf08df); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfe48f3dc); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f4f6); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x00a20c3e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021001f); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff97); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff74); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0034014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa0179); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff97fd2a); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fcfa); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00a304fe); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310525); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xff37f886); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf86e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c709d0); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de209db); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff6df484); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf481); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0x00360c18); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe000a); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021002f); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ffb8); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff50ff3b); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffcc00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fa01fa); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0069fdd4); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fc26); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff5d0407); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07310638); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x00c9f9a8); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55cf74e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff3908c3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de20ac3); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0093f537); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefcbf410); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xffca0bf2); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb0003); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001c0037); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x002fffe2); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff66ff17); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff6a007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01cd0251); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0134fea5); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fb8b); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe2002e0); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06e70713); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0255faf5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf599f658); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0799); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dbf0b96); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x01b8f5f5); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefd5f3a3); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xff5e0bca); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00120037); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00460010); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff8eff0f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff180000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01750276); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01e8ff8d); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fb31); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcfb0198); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x065607ad); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x03cefc64); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf614f592); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0656); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d770c52); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x02daf6bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xefeaf33b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfef10ba3); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff5); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0005002f); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054003c); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffc5ff22); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedfff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00fc0267); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0276007e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb1c); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbfe003e); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05830802); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0529fdec); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6c8f4fe); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd04ff); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d0d0cf6); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x03f8f78f); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf00af2d7); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe850b7b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff80020); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00560060); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0002ff4e); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec4ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x006d0225); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02d50166); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb4e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb35fee1); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0477080e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x065bff82); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7b1f4a0); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610397); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c810d80); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0510f869); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf033f278); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfe1a0b52); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffaffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffec000c); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0078); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0040ff8e); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecafeb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffd301b6); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fc0235); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fbc5); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaaafd90); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x033e07d2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x075b011b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf8cbf47a); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f0224); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0bd50def); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0621f94b); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf067f21e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfdae0b29); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 6900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe3fff6); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037007f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0075ffdc); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef2fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff3d0122); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02ea02dd); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fc79); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa65fc5d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x01e3074e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x082102ad); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa0ff48c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe00a9); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b0a0e43); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0729fa33); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0a5f1c9); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfd430b00); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0001fff3); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe2); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x001b0076); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x009c002d); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff35fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfeba0076); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x029f0352); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfd60); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa69fb53); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00740688); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08a7042d); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfb75f4d6); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600ff2d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a220e7a); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0827fb22); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf0edf17a); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfcd80ad6); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff9); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffd2); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb005e); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b0007a); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8ffe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe53ffc1); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0221038c); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fe6e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfab6fa80); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff010587); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e90590); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfcf5f556); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfdb3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09210e95); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0919fc15); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf13ff12f); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc6e0aab); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070000); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe6ffc9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0039); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00af00b8); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfff4feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe13ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01790388); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311ff92); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb48f9ed); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd980453); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08e306cd); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe88f60a); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fc40); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x08080e93); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09fdfd0c); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf19af0ea); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfc050a81); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080008); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff0ffc9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1000d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x009800e2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x005bff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe00fe74); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00b50345); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000bc); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc18f9a1); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc4802f9); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x089807dc); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0022f6f0); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fada); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x06da0e74); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ad3fe06); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf1fef0ab); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb9c0a55); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000e); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffdffd0); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffdf); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x006e00f2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00b8ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe1bfdf8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffe302c8); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x041301dc); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd1af99e); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb1e0183); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x080908b5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01bcf801); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf985); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x059a0e38); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b99ff03); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf26cf071); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfb330a2a); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00070011); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000affdf); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffa9ffb5); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x003700e6); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01010000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe62fda8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff140219); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x043502e1); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe42f9e6); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa270000); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x073a0953); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x034cf939); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a4f845); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x044c0de1); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c4f0000); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf2e2f03c); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfacc09fe); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00040012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0016fff3); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafff95); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff900c0); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0130007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfecefd89); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe560146); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x041303bc); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff81fa76); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf96cfe7d); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x063209b1); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x04c9fa93); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdf71e); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02f30d6e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cf200fd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf361f00e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa6509d1); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00010010); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e0008); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ff84); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffbc0084); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013e00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff56fd9f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdb8005c); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00460); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00c7fb45); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8f4fd07); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04fa09ce); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x062afc07); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407f614); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x01920ce0); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d8301fa); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf3e8efe5); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xfa0009a4); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd000b); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001d); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff82); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff870039); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x012a014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffedfde7); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd47ff6b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031104c6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0202fc4c); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8c6fbad); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x039909a7); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0767fd8e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482f52b); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x002d0c39); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e0002f4); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf477efc2); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf99b0977); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 7900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa0004); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0020002d); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff91); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff61ffe8); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f70184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0086fe5c); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0bfe85); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104e5); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0323fd7d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8e2fa79); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x021d093f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0879ff22); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bf465); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfec70b79); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e6803eb); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf50defa5); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf937094a); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffd); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00190036); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bffaf); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4fff99); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00aa0198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0112fef3); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd09fdb9); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04be); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x041bfecc); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf947f978); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x00900897); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x095a00b9); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f3c5); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfd650aa3); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ebc04de); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf5aaef8e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf8d5091c); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff7fff6); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000e0038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ffd7); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff52ff56); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x004b0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0186ffa1); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd40fd16); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440452); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04de0029); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9f2f8b2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfefe07b5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a05024d); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef34d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfc0a09b8); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0efa05cd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf64eef7d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf87308ed); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff0); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00000031); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0005); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff6aff27); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffe4014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01d70057); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdacfca6); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3603a7); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05610184); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfadbf82e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd74069f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a7503d6); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff2ff); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfab808b9); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f2306b5); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf6f9ef72); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf81308bf); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff30022); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00560032); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff95ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff8000f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01fe0106); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe46fc71); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3502c7); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059e02ce); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbf9f7f2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfbff055b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aa9054c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f2db); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf97507aa); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f350797); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf7a9ef6d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7b40890); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe8000f); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00540058); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffcdff14); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff29007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f6019e); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff01fc7c); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5101bf); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x059203f6); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfd41f7fe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfaa903f3); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a9e06a9); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf2e2); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf842068b); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f320871); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf85eef6e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf7560860); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002fff2); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1fff9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00460073); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x000bff34); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee90000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01c10215); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffd0fcc5); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99009d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x053d04f1); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfea5f853); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf97d0270); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a5607e4); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef314); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf723055f); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0f180943); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf919ef75); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6fa0830); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff8); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffe4); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f007f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0048ff6b); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec7ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0163025f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00a2fd47); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff73); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04a405b2); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0017f8ed); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf88500dc); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x09d208f9); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff370); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf61c0429); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ee80a0b); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xf9d8ef82); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf6a00800); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007ffff); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe1ffd4); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010007a); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x007cffb2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec6ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00e60277); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0168fdf9); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fe50); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x03ce0631); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0188f9c8); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7c7ff43); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x091509e3); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f3f6); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf52d02ea); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0ea30ac9); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfa9bef95); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf64607d0); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00090007); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe9ffca); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff00065); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a10003); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfee6feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0053025b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0213fed0); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3fd46); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02c70668); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x02eafadb); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf74bfdae); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08230a9c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f4a3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf45b01a6); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0e480b7c); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfb61efae); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf5ef079f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 8900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000d); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff5ffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10043); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b20053); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff24fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffb9020c); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0295ffbb); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fc64); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x019b0654); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x042dfc1c); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf714fc2a); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07020b21); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f575); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3a7005e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0dd80c24); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfc2aefcd); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf599076e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060011); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0002ffcf); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffba0018); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00ad009a); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff79fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff260192); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e500ab); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fbb6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x005b05f7); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545fd81); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf723fabf); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b80b70); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f669); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf313ff15); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d550cbf); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6eff2); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544073d); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00030012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000fffdd); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffea); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x009300cf); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdcfe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea600f7); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fd0190); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fb46); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff150554); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0627fefd); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf778f978); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044d0b87); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f77d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a0fdcf); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbe0d4e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc4f01d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2070b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000010); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001afff0); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaaffbf); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x006700ed); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe460047); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02db0258); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb1b); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddc0473); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c90082); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf811f85e); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c90b66); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff8ad); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf250fc8d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c140dcf); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe93f04d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a106d9); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc000c); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00200006); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ff9c); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x002f00ef); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a4ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0dff92); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x028102f7); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb37); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbf035e); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07260202); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e8f778); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x01340b0d); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f9f4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf223fb51); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b590e42); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff64f083); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf45206a7); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90005); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022001a); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ff86); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff000d7); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01fee5); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f60362); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb99); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbcc0222); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07380370); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f7f6cc); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff990a7e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902fb50); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21afa1f); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8d0ea6); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0034f0bf); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4050675); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fffe); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001e002b); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff81); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb400a5); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe24fe50); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01460390); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfc3a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb1000ce); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x070104bf); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb37f65f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe0009bc); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00fcbb); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf235f8f8); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b20efc); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0105f101); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3ba0642); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff7); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00150036); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff8c); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff810061); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013d007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe71fddf); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x007c0380); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fd13); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa94ff70); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x068005e2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9bf633); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc7308ca); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5fe30); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf274f7e0); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c90f43); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d4f147); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371060f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fff1); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00090038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ffa7); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5e0012); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013200f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee3fd9b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaa0331); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fe15); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa60fe18); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bd06d1); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1bf64a); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafa07ae); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7effab); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d5f6d7); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d30f7a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a3f194); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf32905dc); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffb0032); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003fffcd); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effc1); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6efd8a); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfedd02aa); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0ff34); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa74fcd7); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bf0781); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaaf6a3); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99e066b); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf90128); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf359f5e1); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d20fa2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0370f1e5); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e405a8); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 9900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffef0024); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051fffa); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff54ff77); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00be0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0006fdad); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe2701f3); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413005e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad1fbba); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x039007ee); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x013bf73d); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868050a); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c4302a1); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fdf4fe); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c70fba); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x043bf23c); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a10575); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003fff1); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe50011); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00570027); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff70ff3c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00620198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x009efe01); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95011a); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350183); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb71fad0); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x023c0812); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c3f811); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75e0390); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5c0411); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c1f432); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b30fc1); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0503f297); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2610541); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0006fff7); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdffffc); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510050); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff9dff18); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffc0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0128fe80); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32002e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130292); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4dfa21); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d107ee); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0435f91c); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6850205); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c430573); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a1f37d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x03990fba); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c7f2f8); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222050d); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffe); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdfffe7); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f006e); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffd6ff0f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0197ff1f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd05ff3e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0037c); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd59f9b7); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5d0781); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0585fa56); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4006f); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf906c4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69df2e0); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x02790fa2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0688f35d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e604d8); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00090005); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4ffd6); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025007e); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0014ff20); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3c00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e1ffd0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd12fe5c); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110433); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88f996); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf106d1); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aafbb7); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57efed8); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e07ff); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b0f25e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x01560f7a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0745f3c7); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1ac04a4); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008000c); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffedffcb); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005007d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0050ff4c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ff0086); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd58fd97); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024104ad); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffcaf9c0); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc9905e2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x079afd35); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf555fd46); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad50920); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d9f1f6); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x00310f43); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fdf435); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174046f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xfffffffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050011); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffaffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5006b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0082ff8c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f00130); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd2fcfc); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d04e3); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x010efa32); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb6404bf); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x084efec5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569fbc2); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000a23); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa15f1ab); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0b0efc); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b0f4a7); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13f043a); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00020012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0007ffcd); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9004c); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a4ffd9); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b401c1); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe76fc97); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404d2); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0245fae8); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5f0370); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1005f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bcfa52); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x09020b04); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb60f17b); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde70ea6); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x095df51e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10c0405); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0011); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0014ffdb); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40023); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2002a); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedbff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150022d); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff38fc6f); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36047b); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x035efbda); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9940202); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08ee01f5); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf649f8fe); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e10bc2); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb6f169); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc60e42); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a04f599); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0db03d0); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffb000d); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001dffed); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaafff5); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00aa0077); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00ce026b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x000afc85); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3503e3); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x044cfcfb); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90c0082); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5037f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf710f7cc); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0c59); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe16f173); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaa0dcf); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa5f617); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0ad039b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 10900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff90006); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00210003); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffacffc8); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x008e00b6); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff63fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x003a0275); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00dafcda); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd510313); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0501fe40); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cbfefd); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x087604f0); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80af6c2); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430cc8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7af19a); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa940d4e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3ff699); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0810365); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8ffff); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00210018); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffa3); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x006000e1); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc4fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0024b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x019afd66); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc990216); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0575ff99); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4fd81); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d40640); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf932f5e6); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20d0d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x00dff1de); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf9860cbf); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd1f71e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058032f); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff8); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001b0029); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd1ff8a); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x002600f2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x002cfe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0f01f0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x023bfe20); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc1700fa); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a200f7); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf927fc1c); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f40765); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa82f53b); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x02510d27); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0243f23d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf8810c24); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5cf7a7); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf03102fa); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00110035); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0ff81); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffe700e7); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x008ffeb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94016d); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b0fefb); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3ffd1); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05850249); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c1fadb); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x05de0858); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf2f4c4); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c70d17); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a0f2b8); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf7870b7c); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdff833); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00d02c4); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffdffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00040038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0010ff88); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffac00c2); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e2ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe3900cb); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f1ffe9); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3feaa); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05210381); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9cf9c8); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x04990912); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7af484); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff390cdb); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f4f34d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69a0ac9); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5af8c1); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefec028e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0000ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff60033); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x002fff9f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7b0087); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011eff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe080018); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f900d8); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17fd96); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x04790490); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbadf8ed); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x032f098e); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff10f47d); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaf0c75); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x063cf3fc); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5ba0a0b); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dccf952); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcd0258); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004fff1); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffea0026); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046ffc3); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5a003c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04ff63); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c801b8); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fca6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397056a); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcecf853); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x01ad09c9); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x00acf4ad); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2e0be7); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0773f4c2); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e90943); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35f9e6); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb10221); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff6); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe20014); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0054ffee); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4effeb); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2efebb); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260027a); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fbe6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02870605); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4af7fe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x001d09c1); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0243f515); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabd0b32); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0897f59e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4280871); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e95fa7c); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef9701eb); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffd); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdeffff); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056001d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff57ff9c); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011300f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe82fe2e); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01ca0310); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fb62); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155065a); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xffbaf7f2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8c0977); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x03cef5b2); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf9610a58); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a5f68f); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf3790797); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eebfb14); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef8001b5); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080004); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe0ffe9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x004c0047); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff75ff58); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef9fdc8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111036f); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb21); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x00120665); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x012df82e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd0708ec); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542f682); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81f095c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9af792); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2db06b5); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f38fbad); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6c017e); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 11900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000b); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe7ffd8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00370068); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffa4ff28); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00790184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87fd91); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00430392); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb26); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfece0626); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294f8b2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb990825); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0698f77f); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fe0842); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b73f8a7); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf25105cd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7bfc48); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5a0148); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00050010); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff2ffcc); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x001b007b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffdfff10); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00140198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0020fd8e); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff710375); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb73); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9a059f); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e0f978); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4e0726); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c8f8a7); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600070c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2ff9c9); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1db04de); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4fce5); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4b0111); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00010012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffffffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffb007e); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x001dff14); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffad0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b7fdbe); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9031b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fc01); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc8504d6); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0504fa79); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf93005f6); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08caf9f2); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52b05c0); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccbfaf9); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf17903eb); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3fd83); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3f00db); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffe0011); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000cffcc); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdb0071); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0058ff32); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4f014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x013cfe1f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfb028a); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fcc9); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9d03d6); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f4fbad); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848049d); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0999fb5b); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf4820461); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d46fc32); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12d02f4); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1007fe21); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3600a4); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0017ffd9); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc10055); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0088ff68); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff0400f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a6fea7); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd7501cc); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fdc0); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaef02a8); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a7fd07); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79d0326); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a31fcda); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf40702f3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9ffd72); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f601fa); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1021fec0); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2f006d); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80007); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001fffeb); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaf002d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a8ffb0); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e9ff4c); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd2000ee); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fed8); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82015c); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0715fe7d); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7340198); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8dfe69); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bd017c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd5feb8); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d500fd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x1031ff60); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2b0037); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff70000); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00220000); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffa90000); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b30000); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec20000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x02000000); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd030000); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04350000); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa5e0000); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x073b0000); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7110000); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0aac0000); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3a40000); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0de70000); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0c90000); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x10360000); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef290000); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff8fff9); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001f0015); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffafffd3); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a80050); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfed3ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e900b4); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd20ff12); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x04130128); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa82fea4); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x07150183); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf734fe68); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a8d0197); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf3bdfe84); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0dd50148); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0d5ff03); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x103100a0); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2bffc9); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffafff2); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00170027); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc1ffab); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00880098); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff04ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01a60159); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd75fe34); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b00240); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfaeffd58); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06a702f9); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf79dfcda); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0a310326); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf407fd0d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d9f028e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf0f6fe06); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x10210140); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef2fff93); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffeffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000c0034); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffdbff8f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x005800ce); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff4ffeb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x013c01e1); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdfbfd76); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03110337); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb9dfc2a); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05f40453); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf848fb63); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x099904a5); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf482fb9f); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0d4603ce); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf12dfd0c); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x100701df); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef36ff5c); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 12900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0001ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffff0038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfffbff82); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x001d00ec); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffadfe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00b70242); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfea9fce5); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024103ff); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc85fb2a); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05040587); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf930fa0a); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x08ca060e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf52bfa40); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0ccb0507); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf179fc15); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fe3027d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef3fff25); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005fff0); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff20034); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x001bff85); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffdf00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0014fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00200272); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff71fc8b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d048d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd9afa61); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x03e00688); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfa4ef8da); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x07c80759); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf600f8f4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0c2f0637); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf1dbfb22); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0fb4031b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef4bfeef); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007fff5); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe70028); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0037ff98); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffa400d8); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0079fe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff87026f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0043fc6e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x004404da); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfecef9da); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0294074e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb99f7db); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x06980881); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf6fef7be); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0b730759); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf251fa33); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f7b03b8); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef5afeb8); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fffc); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe00017); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x004cffb9); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7500a8); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00d1feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfef90238); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0111fc91); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604df); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0012f99b); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x012d07d2); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfd07f714); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0542097e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf81ff6a4); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x0a9a086e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf2dbf94b); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0f380453); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef6cfe82); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080003); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffde0001); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0056ffe3); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff570064); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0113ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe8201d2); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01cafcf0); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35049e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0155f9a6); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xffba080e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe8cf689); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x03ce0a4e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xf961f5a8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x09a50971); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf379f869); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0eeb04ec); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef80fe4b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0007000a); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe2ffec); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00540012); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e0015); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0137ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2e0145); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0260fd86); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51041a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0287f9fb); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfe4a0802); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x001df63f); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02430aeb); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfabdf4ce); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x08970a62); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf428f78f); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e950584); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xef97fe15); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0004000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffeaffda); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0046003d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5affc4); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013b0000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe04009d); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02c8fe48); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99035a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0397fa96); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfcec07ad); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x01adf637); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x00ac0b53); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfc2ef419); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x07730b3e); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf4e9f6bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0e35061a); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefb1fddf); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00000012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfff6ffcd); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x002f0061); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7bff79); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x011e007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe08ffe8); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f9ff28); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17026a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0479fb70); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfbad0713); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x032ff672); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff100b83); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xfdaff38b); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x063c0c04); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf5baf5f5); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0dcc06ae); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefcdfda8); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffd0012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0004ffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00100078); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffacff3e); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00e200f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe39ff35); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02f10017); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd30156); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0521fc7f); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa9c0638); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0499f6ee); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfd7a0b7c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0xff39f325); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x04f40cb3); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf69af537); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0d5a073f); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xefecfd72); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffa000e); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0011ffcb); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xfff0007f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffe7ff19); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x008f014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe94fe93); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02b00105); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfbd3002f); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x0585fdb7); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf9c10525); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x05def7a8); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfbf20b3c); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x00c7f2e9); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x03a00d48); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf787f484); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0cdf07cd); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf00dfd3c); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 13900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010000); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80008); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001bffd7); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffd10076); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0026ff0e); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x002c0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff0ffe10); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x023b01e0); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc17ff06); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05a2ff09); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf92703e4); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06f4f89b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfa820ac5); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0251f2d9); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x02430dc3); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf881f3dc); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0c5c0859); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf031fd06); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80001); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021ffe8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffba005d); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0060ff1f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffc40198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xffa0fdb5); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x019a029a); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99fdea); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x05750067); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8d4027f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x07d4f9c0); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf9320a1a); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d2f2f3); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0x00df0e22); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xf986f341); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0bd108e2); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf058fcd1); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffa); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0021fffd); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0038); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x008eff4a); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff630184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x003afd8b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x00da0326); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd51fced); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x050101c0); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf8cb0103); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x0876fb10); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf80a093e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0543f338); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xff7a0e66); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfa94f2b2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0b3f0967); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf081fc9b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffbfff3); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001d0013); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa000b); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00aaff89); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff13014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00cefd95); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x000a037b); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe35fc1d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x044c0305); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf90cff7e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08d5fc81); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf7100834); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069ff3a7); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfe160e8d); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfbaaf231); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0aa509e9); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0adfc65); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xffffffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00140025); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb4ffdd); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00b2ffd6); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfedb00f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x0150fdd3); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xff380391); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff36fb85); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x035e0426); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xf994fdfe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08eefe0b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf6490702); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1f43e); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfcb60e97); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfcc6f1be); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x0a040a67); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf0dbfc30); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0002ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00070033); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9ffb4); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00a40027); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfec3007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01b4fe3f); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe760369); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0044fb2e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x02450518); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfa5ffc90); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x08c1ffa1); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5bc05ae); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0902f4fc); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfb600e85); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xfde7f15a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x095d0ae2); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf10cfbfb); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0005ffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffa0038); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5ff95); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00820074); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfecc0000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01f0fed0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfdd20304); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014dfb1d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x010e05ce); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfb64fb41); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x084e013b); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf569043e); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a00f5dd); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xfa150e55); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0xff0bf104); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x08b00b59); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf13ffbc6); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0008fff4); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffed0035); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0005ff83); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x005000b4); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfef6ff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01ffff7a); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd580269); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0241fb53); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xffca0640); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfc99fa1e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x079a02cb); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf55502ba); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad5f6e0); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf8d90e0a); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0031f0bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07fd0bcb); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf174fb91); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffffffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0009fffb); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe4002a); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0025ff82); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x001400e0); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff3cff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01e10030); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd1201a4); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0311fbcd); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfe88066a); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xfdf1f92f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x06aa0449); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf57e0128); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7ef801); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf7b00da2); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0156f086); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x07450c39); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1acfb5c); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00080002); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0019); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003fff92); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffd600f1); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff96feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x019700e1); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd0500c2); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b0fc84); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfd590649); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0xff5df87f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x058505aa); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf5e4ff91); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9f93c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf69d0d20); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0279f05e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x06880ca3); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf1e6fb28); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 14900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x00060009); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffdf0004); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0051ffb0); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff9d00e8); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xfffcfe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x01280180); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd32ffd2); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413fd6e); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfc4d05df); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x00d1f812); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x043506e4); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf685fdfb); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fa8d); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf5a10c83); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0399f046); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05c70d08); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf222faf3); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffe5ffef); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x0057ffd9); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff7000c4); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0062fe68); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x009e01ff); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfd95fee6); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0435fe7d); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb710530); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x023cf7ee); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x02c307ef); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf75efc70); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c5cfbef); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf4c10bce); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x04b3f03f); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x05030d69); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf261fabf); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15100000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0000fffd); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xffff0012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xffefffdc); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00510006); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff540089); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00befe7c); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0x00060253); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfe27fe0d); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x0413ffa2); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfad10446); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0390f812); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0x013b08c3); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf868faf6); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0c43fd5f); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3fd0b02); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x05c7f046); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x043b0dc4); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2a1fa8b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15200000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001fffe); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffc0012); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0xfffbffce); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x003f0033); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff4e003f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0106feb6); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff6e0276); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xfeddfd56); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x03b000cc); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa740329); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x04bff87f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xffaa095d); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xf99ef995); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0bf9fed8); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf3590a1f); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x06d2f05e); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x03700e1b); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf2e4fa58); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15300000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x0001ffff); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9000f); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0009ffc8); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00250059); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff5effee); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0132ff10); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfee30265); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0xffaafccf); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x031101eb); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa6001e8); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x05bdf92f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfe1b09b6); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfafaf852); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0b7e0055); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2d50929); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x07d3f086); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x02a30e6c); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf329fa24); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15400000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00010001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80009); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0015ffca); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0x00050074); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xff81ff9f); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x013dff82); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe710221); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x007cfc80); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x024102ed); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfa940090); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0680fa1e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfc9b09cd); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfc73f736); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0ad501d0); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2740820); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x08c9f0bd); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01d40eb9); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf371f9f1); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15500000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff80002); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001effd5); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffe5007f); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xffb4ff5b); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x01280000); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe2401b0); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0146fc70); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x014d03c6); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfb10ff32); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0701fb41); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xfb3709a1); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xfe00f644); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x0a000345); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2350708); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x09b2f104); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x01050eff); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf3baf9be); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15600000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfff9fffb); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0022ffe6); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffc9007a); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0xfff0ff29); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00f2007e); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe01011b); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x01f6fc9e); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0x00440467); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfbccfdde); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0738fc90); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf9f70934); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0xff99f582); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x090204b0); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf21a05e1); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0a8df15a); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0x00340f41); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf405f98b); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15700000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0xfffcfff4); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x0020fffa); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffb40064); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x002fff11); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x00a400f0); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe0d006e); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x0281fd09); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xff3604c9); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfcbffca2); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0726fdfe); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf8e80888); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x0134f4f3); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x07e1060c); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf22304af); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0b59f1be); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xff640f7d); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf452f959); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15800000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0x00000003); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0000fff0); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x001a0010); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffaa0041); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0067ff13); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0x0043014a); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfe46ffb9); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02dbfda8); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfe3504e5); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xfddcfb8d); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06c9ff7e); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf81107a2); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x02c9f49a); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x069f0753); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2500373); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0c14f231); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfe930fb3); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4a1f927); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 15900000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0002); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0003ffee); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x000f0023); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffac0016); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x0093ff31); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xffdc0184); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xfea6ff09); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02fdfe70); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfd5104ba); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0xff15faac); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x06270103); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7780688); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x044df479); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x05430883); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf2a00231); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0cbef2b2); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfdc40fe3); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf4f2f8f5); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ ++ case 16000000: ++ cx25840_write4(client, DIF_BPF_COEFF01, 0xffff0001); ++ cx25840_write4(client, DIF_BPF_COEFF23, 0x0006ffef); ++ cx25840_write4(client, DIF_BPF_COEFF45, 0x00020031); ++ cx25840_write4(client, DIF_BPF_COEFF67, 0xffbaffe8); ++ cx25840_write4(client, DIF_BPF_COEFF89, 0x00adff66); ++ cx25840_write4(client, DIF_BPF_COEFF1011, 0xff790198); ++ cx25840_write4(client, DIF_BPF_COEFF1213, 0xff26fe6e); ++ cx25840_write4(client, DIF_BPF_COEFF1415, 0x02e5ff55); ++ cx25840_write4(client, DIF_BPF_COEFF1617, 0xfc99044a); ++ cx25840_write4(client, DIF_BPF_COEFF1819, 0x005bfa09); ++ cx25840_write4(client, DIF_BPF_COEFF2021, 0x0545027f); ++ cx25840_write4(client, DIF_BPF_COEFF2223, 0xf7230541); ++ cx25840_write4(client, DIF_BPF_COEFF2425, 0x05b8f490); ++ cx25840_write4(client, DIF_BPF_COEFF2627, 0x03d20997); ++ cx25840_write4(client, DIF_BPF_COEFF2829, 0xf31300eb); ++ cx25840_write4(client, DIF_BPF_COEFF3031, 0x0d55f341); ++ cx25840_write4(client, DIF_BPF_COEFF3233, 0xfcf6100e); ++ cx25840_write4(client, DIF_BPF_COEFF3435, 0xf544f8c3); ++ cx25840_write4(client, DIF_BPF_COEFF36, 0x110d0000); ++ break; ++ } ++} ++ ++static void cx23888_std_setup(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ v4l2_std_id std = state->std; ++ u32 ifHz; ++ ++ cx25840_write4(client, 0x478, 0x6628021F); ++ cx25840_write4(client, 0x400, 0x0); ++ cx25840_write4(client, 0x4b4, 0x20524030); ++ cx25840_write4(client, 0x47c, 0x010a8263); ++ ++ if (std & V4L2_STD_NTSC) { ++ v4l_dbg(1, cx25840_debug, client, "%s() Selecting NTSC", ++ __func__); ++ ++ /* Horiz / vert timing */ ++ cx25840_write4(client, 0x428, 0x1e1e601a); ++ cx25840_write4(client, 0x424, 0x5b2d007a); ++ ++ /* DIF NTSC */ ++ cx25840_write4(client, 0x304, 0x6503bc0c); ++ cx25840_write4(client, 0x308, 0xbd038c85); ++ cx25840_write4(client, 0x30c, 0x1db4640a); ++ cx25840_write4(client, 0x310, 0x00008800); ++ cx25840_write4(client, 0x314, 0x44400400); ++ cx25840_write4(client, 0x32c, 0x0c800800); ++ cx25840_write4(client, 0x330, 0x27000100); ++ cx25840_write4(client, 0x334, 0x1f296e1f); ++ cx25840_write4(client, 0x338, 0x009f50c1); ++ cx25840_write4(client, 0x340, 0x1befbf06); ++ cx25840_write4(client, 0x344, 0x000035e8); ++ ++ /* DIF I/F */ ++ ifHz = 5400000; ++ ++ } else { ++ v4l_dbg(1, cx25840_debug, client, "%s() Selecting PAL-BG", ++ __func__); ++ ++ /* Horiz / vert timing */ ++ cx25840_write4(client, 0x428, 0x28244024); ++ cx25840_write4(client, 0x424, 0x5d2d0084); ++ ++ /* DIF */ ++ cx25840_write4(client, 0x304, 0x6503bc0c); ++ cx25840_write4(client, 0x308, 0xbd038c85); ++ cx25840_write4(client, 0x30c, 0x1db4640a); ++ cx25840_write4(client, 0x310, 0x00008800); ++ cx25840_write4(client, 0x314, 0x44400600); ++ cx25840_write4(client, 0x32c, 0x0c800800); ++ cx25840_write4(client, 0x330, 0x27000100); ++ cx25840_write4(client, 0x334, 0x213530ec); ++ cx25840_write4(client, 0x338, 0x00a65ba8); ++ cx25840_write4(client, 0x340, 0x1befbf06); ++ cx25840_write4(client, 0x344, 0x000035e8); ++ ++ /* DIF I/F */ ++ ifHz = 6000000; ++ } ++ ++ cx23885_dif_setup(client, ifHz); ++ ++ /* Explicitly ensure the inputs are reconfigured after ++ * a standard change. ++ */ ++ set_input(client, state->vid_input, state->aud_input); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops cx25840_ctrl_ops = { ++ .s_ctrl = cx25840_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops cx25840_core_ops = { ++ .log_status = cx25840_log_status, ++ .g_chip_ident = cx25840_g_chip_ident, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = cx25840_s_std, ++ .g_std = cx25840_g_std, ++ .reset = cx25840_reset, ++ .load_fw = cx25840_load_fw, ++ .s_io_pin_config = common_s_io_pin_config, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = cx25840_g_register, ++ .s_register = cx25840_s_register, ++#endif ++ .interrupt_service_routine = cx25840_irq_handler, ++}; ++ ++static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { ++ .s_frequency = cx25840_s_frequency, ++ .s_radio = cx25840_s_radio, ++ .g_tuner = cx25840_g_tuner, ++ .s_tuner = cx25840_s_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { ++ .s_clock_freq = cx25840_s_clock_freq, ++ .s_routing = cx25840_s_audio_routing, ++ .s_stream = cx25840_s_audio_stream, ++}; ++ ++static const struct v4l2_subdev_video_ops cx25840_video_ops = { ++ .s_routing = cx25840_s_video_routing, ++ .s_mbus_fmt = cx25840_s_mbus_fmt, ++ .s_stream = cx25840_s_stream, ++ .g_input_status = cx25840_g_input_status, ++}; ++ ++static const struct v4l2_subdev_vbi_ops cx25840_vbi_ops = { ++ .decode_vbi_line = cx25840_decode_vbi_line, ++ .s_raw_fmt = cx25840_s_raw_fmt, ++ .s_sliced_fmt = cx25840_s_sliced_fmt, ++ .g_sliced_fmt = cx25840_g_sliced_fmt, ++}; ++ ++static const struct v4l2_subdev_ops cx25840_ops = { ++ .core = &cx25840_core_ops, ++ .tuner = &cx25840_tuner_ops, ++ .audio = &cx25840_audio_ops, ++ .video = &cx25840_video_ops, ++ .vbi = &cx25840_vbi_ops, ++ .ir = &cx25840_ir_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static u32 get_cx2388x_ident(struct i2c_client *client) ++{ ++ u32 ret; ++ ++ /* Come out of digital power down */ ++ cx25840_write(client, 0x000, 0); ++ ++ /* Detecting whether the part is cx23885/7/8 is more ++ * difficult than it needs to be. No ID register. Instead we ++ * probe certain registers indicated in the datasheets to look ++ * for specific defaults that differ between the silicon designs. */ ++ ++ /* It's either 885/7 if the IR Tx Clk Divider register exists */ ++ if (cx25840_read4(client, 0x204) & 0xffff) { ++ /* CX23885 returns bogus repetitive byte values for the DIF, ++ * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ ++ ret = cx25840_read4(client, 0x300); ++ if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { ++ /* No DIF */ ++ ret = V4L2_IDENT_CX23885_AV; ++ } else { ++ /* CX23887 has a broken DIF, but the registers ++ * appear valid (but unused), good enough to detect. */ ++ ret = V4L2_IDENT_CX23887_AV; ++ } ++ } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { ++ /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ ++ ret = V4L2_IDENT_CX23888_AV; ++ } else { ++ v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); ++ ret = V4L2_IDENT_CX23887_AV; ++ } ++ ++ /* Back into digital power down */ ++ cx25840_write(client, 0x000, 2); ++ return ret; ++} ++ ++static int cx25840_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct cx25840_state *state; ++ struct v4l2_subdev *sd; ++ int default_volume; ++ u32 id = V4L2_IDENT_NONE; ++ u16 device_id; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_dbg(1, cx25840_debug, client, "detecting cx25840 client on address 0x%x\n", client->addr << 1); ++ ++ device_id = cx25840_read(client, 0x101) << 8; ++ device_id |= cx25840_read(client, 0x100); ++ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id); ++ ++ /* The high byte of the device ID should be ++ * 0x83 for the cx2583x and 0x84 for the cx2584x */ ++ if ((device_id & 0xff00) == 0x8300) { ++ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; ++ } else if ((device_id & 0xff00) == 0x8400) { ++ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); ++ } else if (device_id == 0x0000) { ++ id = get_cx2388x_ident(client); ++ } else if ((device_id & 0xfff0) == 0x5A30) { ++ /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ ++ id = V4L2_IDENT_CX2310X_AV; ++ } else if ((device_id & 0xff) == (device_id >> 8)) { ++ v4l_err(client, ++ "likely a confused/unresponsive cx2388[578] A/V decoder" ++ " found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ v4l_err(client, "A method to reset it from the cx25840 driver" ++ " software is not known at this time\n"); ++ return -ENODEV; ++ } else { ++ v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); ++ return -ENODEV; ++ } ++ ++ state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &cx25840_ops); ++ ++ switch (id) { ++ case V4L2_IDENT_CX23885_AV: ++ v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ break; ++ case V4L2_IDENT_CX23887_AV: ++ v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ break; ++ case V4L2_IDENT_CX23888_AV: ++ v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ break; ++ case V4L2_IDENT_CX2310X_AV: ++ v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", ++ device_id, client->addr << 1, client->adapter->name); ++ break; ++ case V4L2_IDENT_CX25840: ++ case V4L2_IDENT_CX25841: ++ case V4L2_IDENT_CX25842: ++ case V4L2_IDENT_CX25843: ++ /* Note: revision '(device_id & 0x0f) == 2' was never built. The ++ marking skips from 0x1 == 22 to 0x3 == 23. */ ++ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", ++ (device_id & 0xfff0) >> 4, ++ (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 ++ : (device_id & 0x0f), ++ client->addr << 1, client->adapter->name); ++ break; ++ case V4L2_IDENT_CX25836: ++ case V4L2_IDENT_CX25837: ++ default: ++ v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", ++ (device_id & 0xfff0) >> 4, device_id & 0x0f, ++ client->addr << 1, client->adapter->name); ++ break; ++ } ++ ++ state->c = client; ++ state->vid_input = CX25840_COMPOSITE7; ++ state->aud_input = CX25840_AUDIO8; ++ state->audclk_freq = 48000; ++ state->audmode = V4L2_TUNER_MODE_LANG1; ++ state->vbi_line_offset = 8; ++ state->id = id; ++ state->rev = device_id; ++ v4l2_ctrl_handler_init(&state->hdl, 9); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ if (!is_cx2583x(state)) { ++ default_volume = cx25840_read(client, 0x8d4); ++ /* ++ * Enforce the legacy PVR-350/MSP3400 to PVR-150/CX25843 volume ++ * scale mapping limits to avoid -ERANGE errors when ++ * initializing the volume control ++ */ ++ if (default_volume > 228) { ++ /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ ++ default_volume = 228; ++ cx25840_write(client, 0x8d4, 228); ++ } ++ else if (default_volume < 20) { ++ /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ ++ default_volume = 20; ++ cx25840_write(client, 0x8d4, 20); ++ } ++ default_volume = (((228 - default_volume) >> 1) + 23) << 9; ++ ++ state->volume = v4l2_ctrl_new_std(&state->hdl, ++ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, ++ 0, 65535, 65535 / 100, default_volume); ++ state->mute = v4l2_ctrl_new_std(&state->hdl, ++ &cx25840_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, ++ 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, ++ 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, ++ V4L2_CID_AUDIO_BASS, ++ 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(&state->hdl, &cx25840_audio_ctrl_ops, ++ V4L2_CID_AUDIO_TREBLE, ++ 0, 65535, 65535 / 100, 32768); ++ } ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ if (!is_cx2583x(state)) ++ v4l2_ctrl_cluster(2, &state->volume); ++ v4l2_ctrl_handler_setup(&state->hdl); ++ ++ if (client->dev.platform_data) { ++ struct cx25840_platform_data *pdata = client->dev.platform_data; ++ ++ state->pvr150_workaround = pdata->pvr150_workaround; ++ } ++ ++ cx25840_ir_probe(sd); ++ return 0; ++} ++ ++static int cx25840_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct cx25840_state *state = to_state(sd); ++ ++ cx25840_ir_remove(sd); ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++static const struct i2c_device_id cx25840_id[] = { ++ { "cx25840", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, cx25840_id); ++ ++static struct i2c_driver cx25840_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "cx25840", ++ }, ++ .probe = cx25840_probe, ++ .remove = cx25840_remove, ++ .id_table = cx25840_id, ++}; ++ ++module_i2c_driver(cx25840_driver); +diff --git a/drivers/media/i2c/cx25840/cx25840-core.h b/drivers/media/i2c/cx25840/cx25840-core.h +new file mode 100644 +index 0000000..bd4ada2 +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-core.h +@@ -0,0 +1,137 @@ ++/* cx25840 internal API header ++ * ++ * Copyright (C) 2003-2004 Chris Kennedy ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#ifndef _CX25840_CORE_H_ ++#define _CX25840_CORE_H_ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++struct cx25840_ir_state; ++ ++struct cx25840_state { ++ struct i2c_client *c; ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* volume cluster */ ++ struct v4l2_ctrl *volume; ++ struct v4l2_ctrl *mute; ++ }; ++ int pvr150_workaround; ++ int radio; ++ v4l2_std_id std; ++ enum cx25840_video_input vid_input; ++ enum cx25840_audio_input aud_input; ++ u32 audclk_freq; ++ int audmode; ++ int vbi_line_offset; ++ u32 id; ++ u32 rev; ++ int is_initialized; ++ wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ ++ struct work_struct fw_work; /* work entry for fw load */ ++ struct cx25840_ir_state *ir_state; ++}; ++ ++static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct cx25840_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct cx25840_state, hdl)->sd; ++} ++ ++static inline bool is_cx2583x(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX25836 || ++ state->id == V4L2_IDENT_CX25837; ++} ++ ++static inline bool is_cx231xx(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX2310X_AV; ++} ++ ++static inline bool is_cx2388x(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX23885_AV || ++ state->id == V4L2_IDENT_CX23887_AV || ++ state->id == V4L2_IDENT_CX23888_AV; ++} ++ ++static inline bool is_cx23885(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX23885_AV; ++} ++ ++static inline bool is_cx23887(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX23887_AV; ++} ++ ++static inline bool is_cx23888(struct cx25840_state *state) ++{ ++ return state->id == V4L2_IDENT_CX23888_AV; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* cx25850-core.c */ ++int cx25840_write(struct i2c_client *client, u16 addr, u8 value); ++int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); ++u8 cx25840_read(struct i2c_client *client, u16 addr); ++u32 cx25840_read4(struct i2c_client *client, u16 addr); ++int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); ++int cx25840_and_or4(struct i2c_client *client, u16 addr, u32 and_mask, ++ u32 or_value); ++void cx25840_std_setup(struct i2c_client *client); ++ ++/* ----------------------------------------------------------------------- */ ++/* cx25850-firmware.c */ ++int cx25840_loadfw(struct i2c_client *client); ++ ++/* ----------------------------------------------------------------------- */ ++/* cx25850-audio.c */ ++void cx25840_audio_set_path(struct i2c_client *client); ++int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); ++ ++extern const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops; ++ ++/* ----------------------------------------------------------------------- */ ++/* cx25850-vbi.c */ ++int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); ++int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); ++int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); ++int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); ++ ++/* ----------------------------------------------------------------------- */ ++/* cx25850-ir.c */ ++extern const struct v4l2_subdev_ir_ops cx25840_ir_ops; ++int cx25840_ir_log_status(struct v4l2_subdev *sd); ++int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled); ++int cx25840_ir_probe(struct v4l2_subdev *sd); ++int cx25840_ir_remove(struct v4l2_subdev *sd); ++ ++#endif +diff --git a/drivers/media/i2c/cx25840/cx25840-firmware.c b/drivers/media/i2c/cx25840/cx25840-firmware.c +new file mode 100644 +index 0000000..b3169f9 +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-firmware.c +@@ -0,0 +1,175 @@ ++/* cx25840 firmware functions ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25840-core.h" ++ ++/* ++ * Mike Isely - The FWSEND parameter controls the ++ * size of the firmware chunks sent down the I2C bus to the chip. ++ * Previously this had been set to 1024 but unfortunately some I2C ++ * implementations can't transfer data in such big gulps. ++ * Specifically, the pvrusb2 driver has a hard limit of around 60 ++ * bytes, due to the encapsulation there of I2C traffic into USB ++ * messages. So we have to significantly reduce this parameter. ++ */ ++#define FWSEND 48 ++ ++#define FWDEV(x) &((x)->dev) ++ ++static char *firmware = ""; ++ ++module_param(firmware, charp, 0444); ++ ++MODULE_PARM_DESC(firmware, "Firmware image to load"); ++ ++static void start_fw_load(struct i2c_client *client) ++{ ++ /* DL_ADDR_LB=0 DL_ADDR_HB=0 */ ++ cx25840_write(client, 0x800, 0x00); ++ cx25840_write(client, 0x801, 0x00); ++ // DL_MAP=3 DL_AUTO_INC=0 DL_ENABLE=1 ++ cx25840_write(client, 0x803, 0x0b); ++ /* AUTO_INC_DIS=1 */ ++ cx25840_write(client, 0x000, 0x20); ++} ++ ++static void end_fw_load(struct i2c_client *client) ++{ ++ /* AUTO_INC_DIS=0 */ ++ cx25840_write(client, 0x000, 0x00); ++ /* DL_ENABLE=0 */ ++ cx25840_write(client, 0x803, 0x03); ++} ++ ++#define CX2388x_FIRMWARE "v4l-cx23885-avcore-01.fw" ++#define CX231xx_FIRMWARE "v4l-cx231xx-avcore-01.fw" ++#define CX25840_FIRMWARE "v4l-cx25840.fw" ++ ++static const char *get_fw_name(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (firmware[0]) ++ return firmware; ++ if (is_cx2388x(state)) ++ return CX2388x_FIRMWARE; ++ if (is_cx231xx(state)) ++ return CX231xx_FIRMWARE; ++ return CX25840_FIRMWARE; ++} ++ ++static int check_fw_load(struct i2c_client *client, int size) ++{ ++ /* DL_ADDR_HB DL_ADDR_LB */ ++ int s = cx25840_read(client, 0x801) << 8; ++ s |= cx25840_read(client, 0x800); ++ ++ if (size != s) { ++ v4l_err(client, "firmware %s load failed\n", ++ get_fw_name(client)); ++ return -EINVAL; ++ } ++ ++ v4l_info(client, "loaded %s firmware (%d bytes)\n", ++ get_fw_name(client), size); ++ return 0; ++} ++ ++static int fw_write(struct i2c_client *client, const u8 *data, int size) ++{ ++ if (i2c_master_send(client, data, size) < size) { ++ v4l_err(client, "firmware load i2c failure\n"); ++ return -ENOSYS; ++ } ++ ++ return 0; ++} ++ ++int cx25840_loadfw(struct i2c_client *client) ++{ ++ struct cx25840_state *state = to_state(i2c_get_clientdata(client)); ++ const struct firmware *fw = NULL; ++ u8 buffer[FWSEND]; ++ const u8 *ptr; ++ const char *fwname = get_fw_name(client); ++ int size, retval; ++ int MAX_BUF_SIZE = FWSEND; ++ u32 gpio_oe = 0, gpio_da = 0; ++ ++ if (is_cx2388x(state)) { ++ /* Preserve the GPIO OE and output bits */ ++ gpio_oe = cx25840_read(client, 0x160); ++ gpio_da = cx25840_read(client, 0x164); ++ } ++ ++ if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { ++ v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); ++ MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ ++ } ++ ++ if (request_firmware(&fw, fwname, FWDEV(client)) != 0) { ++ v4l_err(client, "unable to open firmware %s\n", fwname); ++ return -EINVAL; ++ } ++ ++ start_fw_load(client); ++ ++ buffer[0] = 0x08; ++ buffer[1] = 0x02; ++ ++ size = fw->size; ++ ptr = fw->data; ++ while (size > 0) { ++ int len = min(MAX_BUF_SIZE - 2, size); ++ ++ memcpy(buffer + 2, ptr, len); ++ ++ retval = fw_write(client, buffer, len + 2); ++ ++ if (retval < 0) { ++ release_firmware(fw); ++ return retval; ++ } ++ ++ size -= len; ++ ptr += len; ++ } ++ ++ end_fw_load(client); ++ ++ size = fw->size; ++ release_firmware(fw); ++ ++ if (is_cx2388x(state)) { ++ /* Restore GPIO configuration after f/w load */ ++ cx25840_write(client, 0x160, gpio_oe); ++ cx25840_write(client, 0x164, gpio_da); ++ } ++ ++ return check_fw_load(client, size); ++} ++ ++MODULE_FIRMWARE(CX2388x_FIRMWARE); ++MODULE_FIRMWARE(CX231xx_FIRMWARE); ++MODULE_FIRMWARE(CX25840_FIRMWARE); ++ +diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c +new file mode 100644 +index 0000000..9ae977b +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-ir.c +@@ -0,0 +1,1279 @@ ++/* ++ * Driver for the Conexant CX2584x Audio/Video decoder chip and related cores ++ * ++ * Integrated Consumer Infrared Controller ++ * ++ * Copyright (C) 2010 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25840-core.h" ++ ++static unsigned int ir_debug; ++module_param(ir_debug, int, 0644); ++MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages"); ++ ++#define CX25840_IR_REG_BASE 0x200 ++ ++#define CX25840_IR_CNTRL_REG 0x200 ++#define CNTRL_WIN_3_3 0x00000000 ++#define CNTRL_WIN_4_3 0x00000001 ++#define CNTRL_WIN_3_4 0x00000002 ++#define CNTRL_WIN_4_4 0x00000003 ++#define CNTRL_WIN 0x00000003 ++#define CNTRL_EDG_NONE 0x00000000 ++#define CNTRL_EDG_FALL 0x00000004 ++#define CNTRL_EDG_RISE 0x00000008 ++#define CNTRL_EDG_BOTH 0x0000000C ++#define CNTRL_EDG 0x0000000C ++#define CNTRL_DMD 0x00000010 ++#define CNTRL_MOD 0x00000020 ++#define CNTRL_RFE 0x00000040 ++#define CNTRL_TFE 0x00000080 ++#define CNTRL_RXE 0x00000100 ++#define CNTRL_TXE 0x00000200 ++#define CNTRL_RIC 0x00000400 ++#define CNTRL_TIC 0x00000800 ++#define CNTRL_CPL 0x00001000 ++#define CNTRL_LBM 0x00002000 ++#define CNTRL_R 0x00004000 ++ ++#define CX25840_IR_TXCLK_REG 0x204 ++#define TXCLK_TCD 0x0000FFFF ++ ++#define CX25840_IR_RXCLK_REG 0x208 ++#define RXCLK_RCD 0x0000FFFF ++ ++#define CX25840_IR_CDUTY_REG 0x20C ++#define CDUTY_CDC 0x0000000F ++ ++#define CX25840_IR_STATS_REG 0x210 ++#define STATS_RTO 0x00000001 ++#define STATS_ROR 0x00000002 ++#define STATS_RBY 0x00000004 ++#define STATS_TBY 0x00000008 ++#define STATS_RSR 0x00000010 ++#define STATS_TSR 0x00000020 ++ ++#define CX25840_IR_IRQEN_REG 0x214 ++#define IRQEN_RTE 0x00000001 ++#define IRQEN_ROE 0x00000002 ++#define IRQEN_RSE 0x00000010 ++#define IRQEN_TSE 0x00000020 ++#define IRQEN_MSK 0x00000033 ++ ++#define CX25840_IR_FILTR_REG 0x218 ++#define FILTR_LPF 0x0000FFFF ++ ++#define CX25840_IR_FIFO_REG 0x23C ++#define FIFO_RXTX 0x0000FFFF ++#define FIFO_RXTX_LVL 0x00010000 ++#define FIFO_RXTX_RTO 0x0001FFFF ++#define FIFO_RX_NDV 0x00020000 ++#define FIFO_RX_DEPTH 8 ++#define FIFO_TX_DEPTH 8 ++ ++#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ ++#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2) ++ ++/* ++ * We use this union internally for convenience, but callers to tx_write ++ * and rx_read will be expecting records of type struct ir_raw_event. ++ * Always ensure the size of this union is dictated by struct ir_raw_event. ++ */ ++union cx25840_ir_fifo_rec { ++ u32 hw_fifo_data; ++ struct ir_raw_event ir_core_data; ++}; ++ ++#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) ++#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec)) ++ ++struct cx25840_ir_state { ++ struct i2c_client *c; ++ ++ struct v4l2_subdev_ir_parameters rx_params; ++ struct mutex rx_params_lock; /* protects Rx parameter settings cache */ ++ atomic_t rxclk_divider; ++ atomic_t rx_invert; ++ ++ struct kfifo rx_kfifo; ++ spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */ ++ ++ struct v4l2_subdev_ir_parameters tx_params; ++ struct mutex tx_params_lock; /* protects Tx parameter settings cache */ ++ atomic_t txclk_divider; ++}; ++ ++static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ return state ? state->ir_state : NULL; ++} ++ ++ ++/* ++ * Rx and Tx Clock Divider register computations ++ * ++ * Note the largest clock divider value of 0xffff corresponds to: ++ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns ++ * which fits in 21 bits, so we'll use unsigned int for time arguments. ++ */ ++static inline u16 count_to_clock_divider(unsigned int d) ++{ ++ if (d > RXCLK_RCD + 1) ++ d = RXCLK_RCD; ++ else if (d < 2) ++ d = 1; ++ else ++ d--; ++ return (u16) d; ++} ++ ++static inline u16 ns_to_clock_divider(unsigned int ns) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); ++} ++ ++static inline unsigned int clock_divider_to_ns(unsigned int divider) ++{ ++ /* Period of the Rx or Tx clock in ns */ ++ return DIV_ROUND_CLOSEST((divider + 1) * 1000, ++ CX25840_IR_REFCLK_FREQ / 1000000); ++} ++ ++static inline u16 carrier_freq_to_clock_divider(unsigned int freq) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16)); ++} ++ ++static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) ++{ ++ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16); ++} ++ ++static inline u16 freq_to_clock_divider(unsigned int freq, ++ unsigned int rollovers) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers)); ++} ++ ++static inline unsigned int clock_divider_to_freq(unsigned int divider, ++ unsigned int rollovers) ++{ ++ return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, ++ (divider + 1) * rollovers); ++} ++ ++/* ++ * Low Pass Filter register calculations ++ * ++ * Note the largest count value of 0xffff corresponds to: ++ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns ++ * which fits in 21 bits, so we'll use unsigned int for time arguments. ++ */ ++static inline u16 count_to_lpf_count(unsigned int d) ++{ ++ if (d > FILTR_LPF) ++ d = FILTR_LPF; ++ else if (d < 4) ++ d = 0; ++ return (u16) d; ++} ++ ++static inline u16 ns_to_lpf_count(unsigned int ns) ++{ ++ return count_to_lpf_count( ++ DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000)); ++} ++ ++static inline unsigned int lpf_count_to_ns(unsigned int count) ++{ ++ /* Duration of the Low Pass Filter rejection window in ns */ ++ return DIV_ROUND_CLOSEST(count * 1000, ++ CX25840_IR_REFCLK_FREQ / 1000000); ++} ++ ++static inline unsigned int lpf_count_to_us(unsigned int count) ++{ ++ /* Duration of the Low Pass Filter rejection window in us */ ++ return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000); ++} ++ ++/* ++ * FIFO register pulse width count compuations ++ */ ++static u32 clock_divider_to_resolution(u16 divider) ++{ ++ /* ++ * Resolution is the duration of 1 tick of the readable portion of ++ * of the pulse width counter as read from the FIFO. The two lsb's are ++ * not readable, hence the << 2. This function returns ns. ++ */ ++ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, ++ CX25840_IR_REFCLK_FREQ / 1000000); ++} ++ ++static u64 pulse_width_count_to_ns(u16 count, u16 divider) ++{ ++ u64 n; ++ u32 rem; ++ ++ /* ++ * The 2 lsb's of the pulse width timer count are not readable, hence ++ * the (count << 2) | 0x3 ++ */ ++ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ ++ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ ++ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) ++ n++; ++ return n; ++} ++ ++#if 0 ++/* Keep as we will need this for Transmit functionality */ ++static u16 ns_to_pulse_width_count(u32 ns, u16 divider) ++{ ++ u64 n; ++ u32 d; ++ u32 rem; ++ ++ /* ++ * The 2 lsb's of the pulse width timer count are not accessible, hence ++ * the (1 << 2) ++ */ ++ n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */ ++ d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */ ++ rem = do_div(n, d); ++ if (rem >= d / 2) ++ n++; ++ ++ if (n > FIFO_RXTX) ++ n = FIFO_RXTX; ++ else if (n == 0) ++ n = 1; ++ return (u16) n; ++} ++ ++#endif ++static unsigned int pulse_width_count_to_us(u16 count, u16 divider) ++{ ++ u64 n; ++ u32 rem; ++ ++ /* ++ * The 2 lsb's of the pulse width timer count are not readable, hence ++ * the (count << 2) | 0x3 ++ */ ++ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ ++ rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ ++ if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2) ++ n++; ++ return (unsigned int) n; ++} ++ ++/* ++ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts ++ * ++ * The total pulse clock count is an 18 bit pulse width timer count as the most ++ * significant part and (up to) 16 bit clock divider count as a modulus. ++ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse ++ * width timer count's least significant bit. ++ */ ++static u64 ns_to_pulse_clocks(u32 ns) ++{ ++ u64 clocks; ++ u32 rem; ++ clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ ++ rem = do_div(clocks, 1000); /* /1000 = cycles */ ++ if (rem >= 1000 / 2) ++ clocks++; ++ return clocks; ++} ++ ++static u16 pulse_clocks_to_clock_divider(u64 count) ++{ ++ do_div(count, (FIFO_RXTX << 2) | 0x3); ++ ++ /* net result needs to be rounded down and decremented by 1 */ ++ if (count > RXCLK_RCD + 1) ++ count = RXCLK_RCD; ++ else if (count < 2) ++ count = 1; ++ else ++ count--; ++ return (u16) count; ++} ++ ++/* ++ * IR Control Register helpers ++ */ ++enum tx_fifo_watermark { ++ TX_FIFO_HALF_EMPTY = 0, ++ TX_FIFO_EMPTY = CNTRL_TIC, ++}; ++ ++enum rx_fifo_watermark { ++ RX_FIFO_HALF_FULL = 0, ++ RX_FIFO_NOT_EMPTY = CNTRL_RIC, ++}; ++ ++static inline void control_tx_irq_watermark(struct i2c_client *c, ++ enum tx_fifo_watermark level) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level); ++} ++ ++static inline void control_rx_irq_watermark(struct i2c_client *c, ++ enum rx_fifo_watermark level) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level); ++} ++ ++static inline void control_tx_enable(struct i2c_client *c, bool enable) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), ++ enable ? (CNTRL_TXE | CNTRL_TFE) : 0); ++} ++ ++static inline void control_rx_enable(struct i2c_client *c, bool enable) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), ++ enable ? (CNTRL_RXE | CNTRL_RFE) : 0); ++} ++ ++static inline void control_tx_modulation_enable(struct i2c_client *c, ++ bool enable) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD, ++ enable ? CNTRL_MOD : 0); ++} ++ ++static inline void control_rx_demodulation_enable(struct i2c_client *c, ++ bool enable) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD, ++ enable ? CNTRL_DMD : 0); ++} ++ ++static inline void control_rx_s_edge_detection(struct i2c_client *c, ++ u32 edge_types) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, ++ edge_types & CNTRL_EDG_BOTH); ++} ++ ++static void control_rx_s_carrier_window(struct i2c_client *c, ++ unsigned int carrier, ++ unsigned int *carrier_range_low, ++ unsigned int *carrier_range_high) ++{ ++ u32 v; ++ unsigned int c16 = carrier * 16; ++ ++ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { ++ v = CNTRL_WIN_3_4; ++ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); ++ } else { ++ v = CNTRL_WIN_3_3; ++ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); ++ } ++ ++ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { ++ v |= CNTRL_WIN_4_3; ++ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); ++ } else { ++ v |= CNTRL_WIN_3_3; ++ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); ++ } ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v); ++} ++ ++static inline void control_tx_polarity_invert(struct i2c_client *c, ++ bool invert) ++{ ++ cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL, ++ invert ? CNTRL_CPL : 0); ++} ++ ++/* ++ * IR Rx & Tx Clock Register helpers ++ */ ++static unsigned int txclk_tx_s_carrier(struct i2c_client *c, ++ unsigned int freq, ++ u16 *divider) ++{ ++ *divider = carrier_freq_to_clock_divider(freq); ++ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); ++ return clock_divider_to_carrier_freq(*divider); ++} ++ ++static unsigned int rxclk_rx_s_carrier(struct i2c_client *c, ++ unsigned int freq, ++ u16 *divider) ++{ ++ *divider = carrier_freq_to_clock_divider(freq); ++ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); ++ return clock_divider_to_carrier_freq(*divider); ++} ++ ++static u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns, ++ u16 *divider) ++{ ++ u64 pulse_clocks; ++ ++ if (ns > IR_MAX_DURATION) ++ ns = IR_MAX_DURATION; ++ pulse_clocks = ns_to_pulse_clocks(ns); ++ *divider = pulse_clocks_to_clock_divider(pulse_clocks); ++ cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider); ++ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); ++} ++ ++static u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns, ++ u16 *divider) ++{ ++ u64 pulse_clocks; ++ ++ if (ns > IR_MAX_DURATION) ++ ns = IR_MAX_DURATION; ++ pulse_clocks = ns_to_pulse_clocks(ns); ++ *divider = pulse_clocks_to_clock_divider(pulse_clocks); ++ cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider); ++ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); ++} ++ ++/* ++ * IR Tx Carrier Duty Cycle register helpers ++ */ ++static unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c, ++ unsigned int duty_cycle) ++{ ++ u32 n; ++ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ ++ if (n != 0) ++ n--; ++ if (n > 15) ++ n = 15; ++ cx25840_write4(c, CX25840_IR_CDUTY_REG, n); ++ return DIV_ROUND_CLOSEST((n + 1) * 100, 16); ++} ++ ++/* ++ * IR Filter Register helpers ++ */ ++static u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns) ++{ ++ u32 count = ns_to_lpf_count(min_width_ns); ++ cx25840_write4(c, CX25840_IR_FILTR_REG, count); ++ return lpf_count_to_ns(count); ++} ++ ++/* ++ * IR IRQ Enable Register helpers ++ */ ++static inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask) ++{ ++ struct cx25840_state *state = to_state(sd); ++ ++ if (is_cx23885(state) || is_cx23887(state)) ++ mask ^= IRQEN_MSK; ++ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); ++ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ++ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); ++} ++ ++static inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask) ++{ ++ struct cx25840_state *state = to_state(sd); ++ ++ if (is_cx23885(state) || is_cx23887(state)) ++ mask ^= IRQEN_MSK; ++ mask &= IRQEN_TSE; ++ cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask); ++} ++ ++/* ++ * V4L2 Subdevice IR Ops ++ */ ++int cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ struct i2c_client *c = NULL; ++ unsigned long flags; ++ ++ union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; ++ unsigned int i, j, k; ++ u32 events, v; ++ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; ++ u32 cntrl, irqen, stats; ++ ++ *handled = false; ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ c = ir_state->c; ++ ++ /* Only support the IR controller for the CX2388[57] AV Core for now */ ++ if (!(is_cx23885(state) || is_cx23887(state))) ++ return -ENODEV; ++ ++ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); ++ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); ++ if (is_cx23885(state) || is_cx23887(state)) ++ irqen ^= IRQEN_MSK; ++ stats = cx25840_read4(c, CX25840_IR_STATS_REG); ++ ++ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ ++ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ ++ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ ++ ror = stats & STATS_ROR; /* Rx FIFO Over Run */ ++ ++ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ ++ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ ++ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ ++ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ ++ ++ v4l2_dbg(2, ir_debug, sd, "IR IRQ Status: %s %s %s %s %s %s\n", ++ tsr ? "tsr" : " ", rsr ? "rsr" : " ", ++ rto ? "rto" : " ", ror ? "ror" : " ", ++ stats & STATS_TBY ? "tby" : " ", ++ stats & STATS_RBY ? "rby" : " "); ++ ++ v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n", ++ tse ? "tse" : " ", rse ? "rse" : " ", ++ rte ? "rte" : " ", roe ? "roe" : " "); ++ ++ /* ++ * Transmitter interrupt service ++ */ ++ if (tse && tsr) { ++ /* ++ * TODO: ++ * Check the watermark threshold setting ++ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo ++ * Push the data to the hardware FIFO. ++ * If there was nothing more to send in the tx_kfifo, disable ++ * the TSR IRQ and notify the v4l2_device. ++ * If there was something in the tx_kfifo, check the tx_kfifo ++ * level and notify the v4l2_device, if it is low. ++ */ ++ /* For now, inhibit TSR interrupt until Tx is implemented */ ++ irqenable_tx(sd, 0); ++ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; ++ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); ++ *handled = true; ++ } ++ ++ /* ++ * Receiver interrupt service ++ */ ++ kror = 0; ++ if ((rse && rsr) || (rte && rto)) { ++ /* ++ * Receive data on RSR to clear the STATS_RSR. ++ * Receive data on RTO, since we may not have yet hit the RSR ++ * watermark when we receive the RTO. ++ */ ++ for (i = 0, v = FIFO_RX_NDV; ++ (v & FIFO_RX_NDV) && !kror; i = 0) { ++ for (j = 0; ++ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { ++ v = cx25840_read4(c, CX25840_IR_FIFO_REG); ++ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; ++ i++; ++ } ++ if (i == 0) ++ break; ++ j = i * sizeof(union cx25840_ir_fifo_rec); ++ k = kfifo_in_locked(&ir_state->rx_kfifo, ++ (unsigned char *) rx_data, j, ++ &ir_state->rx_kfifo_lock); ++ if (k != j) ++ kror++; /* rx_kfifo over run */ ++ } ++ *handled = true; ++ } ++ ++ events = 0; ++ v = 0; ++ if (kror) { ++ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; ++ v4l2_err(sd, "IR receiver software FIFO overrun\n"); ++ } ++ if (roe && ror) { ++ /* ++ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear ++ * the Rx FIFO Over Run status (STATS_ROR) ++ */ ++ v |= CNTRL_RFE; ++ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; ++ v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); ++ } ++ if (rte && rto) { ++ /* ++ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear ++ * the Rx Pulse Width Timer Time Out (STATS_RTO) ++ */ ++ v |= CNTRL_RXE; ++ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; ++ } ++ if (v) { ++ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ ++ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v); ++ cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl); ++ *handled = true; ++ } ++ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); ++ if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2) ++ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; ++ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); ++ ++ if (events) ++ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); ++ return 0; ++} ++ ++/* Receiver */ ++static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, ++ ssize_t *num) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ bool invert; ++ u16 divider; ++ unsigned int i, n; ++ union cx25840_ir_fifo_rec *p; ++ unsigned u, v, w; ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ invert = (bool) atomic_read(&ir_state->rx_invert); ++ divider = (u16) atomic_read(&ir_state->rxclk_divider); ++ ++ n = count / sizeof(union cx25840_ir_fifo_rec) ++ * sizeof(union cx25840_ir_fifo_rec); ++ if (n == 0) { ++ *num = 0; ++ return 0; ++ } ++ ++ n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n, ++ &ir_state->rx_kfifo_lock); ++ ++ n /= sizeof(union cx25840_ir_fifo_rec); ++ *num = n * sizeof(union cx25840_ir_fifo_rec); ++ ++ for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { ++ ++ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { ++ /* Assume RTO was because of no IR light input */ ++ u = 0; ++ w = 1; ++ } else { ++ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; ++ if (invert) ++ u = u ? 0 : 1; ++ w = 0; ++ } ++ ++ v = (unsigned) pulse_width_count_to_ns( ++ (u16) (p->hw_fifo_data & FIFO_RXTX), divider); ++ if (v > IR_MAX_DURATION) ++ v = IR_MAX_DURATION; ++ ++ init_ir_raw_event(&p->ir_core_data); ++ p->ir_core_data.pulse = u; ++ p->ir_core_data.duration = v; ++ p->ir_core_data.timeout = w; ++ ++ v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", ++ v, u ? "mark" : "space", w ? "(timed out)" : ""); ++ if (w) ++ v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n"); ++ } ++ return 0; ++} ++ ++static int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ mutex_lock(&ir_state->rx_params_lock); ++ memcpy(p, &ir_state->rx_params, ++ sizeof(struct v4l2_subdev_ir_parameters)); ++ mutex_unlock(&ir_state->rx_params_lock); ++ return 0; ++} ++ ++static int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ struct i2c_client *c; ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ c = ir_state->c; ++ mutex_lock(&ir_state->rx_params_lock); ++ ++ /* Disable or slow down all IR Rx circuits and counters */ ++ irqenable_rx(sd, 0); ++ control_rx_enable(c, false); ++ control_rx_demodulation_enable(c, false); ++ control_rx_s_edge_detection(c, CNTRL_EDG_NONE); ++ filter_rx_s_min_width(c, 0); ++ cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD); ++ ++ ir_state->rx_params.shutdown = true; ++ ++ mutex_unlock(&ir_state->rx_params_lock); ++ return 0; ++} ++ ++static int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ struct i2c_client *c; ++ struct v4l2_subdev_ir_parameters *o; ++ u16 rxclk_divider; ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ if (p->shutdown) ++ return cx25840_ir_rx_shutdown(sd); ++ ++ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) ++ return -ENOSYS; ++ ++ c = ir_state->c; ++ o = &ir_state->rx_params; ++ ++ mutex_lock(&ir_state->rx_params_lock); ++ ++ o->shutdown = p->shutdown; ++ ++ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ o->mode = p->mode; ++ ++ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); ++ o->bytes_per_data_element = p->bytes_per_data_element; ++ ++ /* Before we tweak the hardware, we have to disable the receiver */ ++ irqenable_rx(sd, 0); ++ control_rx_enable(c, false); ++ ++ control_rx_demodulation_enable(c, p->modulation); ++ o->modulation = p->modulation; ++ ++ if (p->modulation) { ++ p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq, ++ &rxclk_divider); ++ ++ o->carrier_freq = p->carrier_freq; ++ ++ p->duty_cycle = 50; ++ o->duty_cycle = p->duty_cycle; ++ ++ control_rx_s_carrier_window(c, p->carrier_freq, ++ &p->carrier_range_lower, ++ &p->carrier_range_upper); ++ o->carrier_range_lower = p->carrier_range_lower; ++ o->carrier_range_upper = p->carrier_range_upper; ++ ++ p->max_pulse_width = ++ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); ++ } else { ++ p->max_pulse_width = ++ rxclk_rx_s_max_pulse_width(c, p->max_pulse_width, ++ &rxclk_divider); ++ } ++ o->max_pulse_width = p->max_pulse_width; ++ atomic_set(&ir_state->rxclk_divider, rxclk_divider); ++ ++ p->noise_filter_min_width = ++ filter_rx_s_min_width(c, p->noise_filter_min_width); ++ o->noise_filter_min_width = p->noise_filter_min_width; ++ ++ p->resolution = clock_divider_to_resolution(rxclk_divider); ++ o->resolution = p->resolution; ++ ++ /* FIXME - make this dependent on resolution for better performance */ ++ control_rx_irq_watermark(c, RX_FIFO_HALF_FULL); ++ ++ control_rx_s_edge_detection(c, CNTRL_EDG_BOTH); ++ ++ o->invert_level = p->invert_level; ++ atomic_set(&ir_state->rx_invert, p->invert_level); ++ ++ o->interrupt_enable = p->interrupt_enable; ++ o->enable = p->enable; ++ if (p->enable) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags); ++ kfifo_reset(&ir_state->rx_kfifo); ++ spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags); ++ if (p->interrupt_enable) ++ irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); ++ control_rx_enable(c, p->enable); ++ } ++ ++ mutex_unlock(&ir_state->rx_params_lock); ++ return 0; ++} ++ ++/* Transmitter */ ++static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, ++ ssize_t *num) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++#if 0 ++ /* ++ * FIXME - the code below is an incomplete and untested sketch of what ++ * may need to be done. The critical part is to get 4 (or 8) pulses ++ * from the tx_kfifo, or converted from ns to the proper units from the ++ * input, and push them off to the hardware Tx FIFO right away, if the ++ * HW TX fifo needs service. The rest can be pushed to the tx_kfifo in ++ * a less critical timeframe. Also watch out for overruning the ++ * tx_kfifo - don't let it happen and let the caller know not all his ++ * pulses were written. ++ */ ++ u32 *ns_pulse = (u32 *) buf; ++ unsigned int n; ++ u32 fifo_pulse[FIFO_TX_DEPTH]; ++ u32 mark; ++ ++ /* Compute how much we can fit in the tx kfifo */ ++ n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo); ++ n = min(n, (unsigned int) count); ++ n /= sizeof(u32); ++ ++ /* FIXME - turn on Tx Fifo service interrupt ++ * check hardware fifo level, and other stuff ++ */ ++ for (i = 0; i < n; ) { ++ for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) { ++ mark = ns_pulse[i] & LEVEL_MASK; ++ fifo_pulse[j] = ns_to_pulse_width_count( ++ ns_pulse[i] & ++ ~LEVEL_MASK, ++ ir_state->txclk_divider); ++ if (mark) ++ fifo_pulse[j] &= FIFO_RXTX_LVL; ++ i++; ++ } ++ kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse, ++ j * sizeof(u32)); ++ } ++ *num = n * sizeof(u32); ++#else ++ /* For now enable the Tx FIFO Service interrupt & pretend we did work */ ++ irqenable_tx(sd, IRQEN_TSE); ++ *num = count; ++#endif ++ return 0; ++} ++ ++static int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ mutex_lock(&ir_state->tx_params_lock); ++ memcpy(p, &ir_state->tx_params, ++ sizeof(struct v4l2_subdev_ir_parameters)); ++ mutex_unlock(&ir_state->tx_params_lock); ++ return 0; ++} ++ ++static int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ struct i2c_client *c; ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ c = ir_state->c; ++ mutex_lock(&ir_state->tx_params_lock); ++ ++ /* Disable or slow down all IR Tx circuits and counters */ ++ irqenable_tx(sd, 0); ++ control_tx_enable(c, false); ++ control_tx_modulation_enable(c, false); ++ cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD); ++ ++ ir_state->tx_params.shutdown = true; ++ ++ mutex_unlock(&ir_state->tx_params_lock); ++ return 0; ++} ++ ++static int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ struct i2c_client *c; ++ struct v4l2_subdev_ir_parameters *o; ++ u16 txclk_divider; ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ if (p->shutdown) ++ return cx25840_ir_tx_shutdown(sd); ++ ++ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) ++ return -ENOSYS; ++ ++ c = ir_state->c; ++ o = &ir_state->tx_params; ++ mutex_lock(&ir_state->tx_params_lock); ++ ++ o->shutdown = p->shutdown; ++ ++ p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ o->mode = p->mode; ++ ++ p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec); ++ o->bytes_per_data_element = p->bytes_per_data_element; ++ ++ /* Before we tweak the hardware, we have to disable the transmitter */ ++ irqenable_tx(sd, 0); ++ control_tx_enable(c, false); ++ ++ control_tx_modulation_enable(c, p->modulation); ++ o->modulation = p->modulation; ++ ++ if (p->modulation) { ++ p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq, ++ &txclk_divider); ++ o->carrier_freq = p->carrier_freq; ++ ++ p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle); ++ o->duty_cycle = p->duty_cycle; ++ ++ p->max_pulse_width = ++ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); ++ } else { ++ p->max_pulse_width = ++ txclk_tx_s_max_pulse_width(c, p->max_pulse_width, ++ &txclk_divider); ++ } ++ o->max_pulse_width = p->max_pulse_width; ++ atomic_set(&ir_state->txclk_divider, txclk_divider); ++ ++ p->resolution = clock_divider_to_resolution(txclk_divider); ++ o->resolution = p->resolution; ++ ++ /* FIXME - make this dependent on resolution for better performance */ ++ control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY); ++ ++ control_tx_polarity_invert(c, p->invert_carrier_sense); ++ o->invert_carrier_sense = p->invert_carrier_sense; ++ ++ /* ++ * FIXME: we don't have hardware help for IO pin level inversion ++ * here like we have on the CX23888. ++ * Act on this with some mix of logical inversion of data levels, ++ * carrier polarity, and carrier duty cycle. ++ */ ++ o->invert_level = p->invert_level; ++ ++ o->interrupt_enable = p->interrupt_enable; ++ o->enable = p->enable; ++ if (p->enable) { ++ /* reset tx_fifo here */ ++ if (p->interrupt_enable) ++ irqenable_tx(sd, IRQEN_TSE); ++ control_tx_enable(c, p->enable); ++ } ++ ++ mutex_unlock(&ir_state->tx_params_lock); ++ return 0; ++} ++ ++ ++/* ++ * V4L2 Subdevice Core Ops support ++ */ ++int cx25840_ir_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct i2c_client *c = state->c; ++ char *s; ++ int i, j; ++ u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr; ++ ++ /* The CX23888 chip doesn't have an IR controller on the A/V core */ ++ if (is_cx23888(state)) ++ return 0; ++ ++ cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG); ++ txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD; ++ rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD; ++ cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC; ++ stats = cx25840_read4(c, CX25840_IR_STATS_REG); ++ irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG); ++ if (is_cx23885(state) || is_cx23887(state)) ++ irqen ^= IRQEN_MSK; ++ filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF; ++ ++ v4l2_info(sd, "IR Receiver:\n"); ++ v4l2_info(sd, "\tEnabled: %s\n", ++ cntrl & CNTRL_RXE ? "yes" : "no"); ++ v4l2_info(sd, "\tDemodulation from a carrier: %s\n", ++ cntrl & CNTRL_DMD ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO: %s\n", ++ cntrl & CNTRL_RFE ? "enabled" : "disabled"); ++ switch (cntrl & CNTRL_EDG) { ++ case CNTRL_EDG_NONE: ++ s = "disabled"; ++ break; ++ case CNTRL_EDG_FALL: ++ s = "falling edge"; ++ break; ++ case CNTRL_EDG_RISE: ++ s = "rising edge"; ++ break; ++ case CNTRL_EDG_BOTH: ++ s = "rising & falling edges"; ++ break; ++ default: ++ s = "??? edge"; ++ break; ++ } ++ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); ++ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", ++ cntrl & CNTRL_R ? "not loaded" : "overflow marker"); ++ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", ++ cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); ++ v4l2_info(sd, "\tLoopback mode: %s\n", ++ cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); ++ if (cntrl & CNTRL_DMD) { ++ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", ++ clock_divider_to_carrier_freq(rxclk)); ++ switch (cntrl & CNTRL_WIN) { ++ case CNTRL_WIN_3_3: ++ i = 3; ++ j = 3; ++ break; ++ case CNTRL_WIN_4_3: ++ i = 4; ++ j = 3; ++ break; ++ case CNTRL_WIN_3_4: ++ i = 3; ++ j = 4; ++ break; ++ case CNTRL_WIN_4_4: ++ i = 4; ++ j = 4; ++ break; ++ default: ++ i = 0; ++ j = 0; ++ break; ++ } ++ v4l2_info(sd, "\tNext carrier edge window: 16 clocks " ++ "-%1d/+%1d, %u to %u Hz\n", i, j, ++ clock_divider_to_freq(rxclk, 16 + j), ++ clock_divider_to_freq(rxclk, 16 - i)); ++ } ++ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", ++ pulse_width_count_to_us(FIFO_RXTX, rxclk), ++ pulse_width_count_to_ns(FIFO_RXTX, rxclk)); ++ v4l2_info(sd, "\tLow pass filter: %s\n", ++ filtr ? "enabled" : "disabled"); ++ if (filtr) ++ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " ++ "%u ns\n", ++ lpf_count_to_us(filtr), ++ lpf_count_to_ns(filtr)); ++ v4l2_info(sd, "\tPulse width timer timed-out: %s\n", ++ stats & STATS_RTO ? "yes" : "no"); ++ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", ++ irqen & IRQEN_RTE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO overrun: %s\n", ++ stats & STATS_ROR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", ++ irqen & IRQEN_ROE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tBusy: %s\n", ++ stats & STATS_RBY ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service requested: %s\n", ++ stats & STATS_RSR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service request interrupt: %s\n", ++ irqen & IRQEN_RSE ? "enabled" : "disabled"); ++ ++ v4l2_info(sd, "IR Transmitter:\n"); ++ v4l2_info(sd, "\tEnabled: %s\n", ++ cntrl & CNTRL_TXE ? "yes" : "no"); ++ v4l2_info(sd, "\tModulation onto a carrier: %s\n", ++ cntrl & CNTRL_MOD ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO: %s\n", ++ cntrl & CNTRL_TFE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", ++ cntrl & CNTRL_TIC ? "not empty" : "half full or less"); ++ v4l2_info(sd, "\tCarrier polarity: %s\n", ++ cntrl & CNTRL_CPL ? "space:burst mark:noburst" ++ : "space:noburst mark:burst"); ++ if (cntrl & CNTRL_MOD) { ++ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", ++ clock_divider_to_carrier_freq(txclk)); ++ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", ++ cduty + 1); ++ } ++ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", ++ pulse_width_count_to_us(FIFO_RXTX, txclk), ++ pulse_width_count_to_ns(FIFO_RXTX, txclk)); ++ v4l2_info(sd, "\tBusy: %s\n", ++ stats & STATS_TBY ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service requested: %s\n", ++ stats & STATS_TSR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service request interrupt: %s\n", ++ irqen & IRQEN_TSE ? "enabled" : "disabled"); ++ ++ return 0; ++} ++ ++ ++const struct v4l2_subdev_ir_ops cx25840_ir_ops = { ++ .rx_read = cx25840_ir_rx_read, ++ .rx_g_parameters = cx25840_ir_rx_g_parameters, ++ .rx_s_parameters = cx25840_ir_rx_s_parameters, ++ ++ .tx_write = cx25840_ir_tx_write, ++ .tx_g_parameters = cx25840_ir_tx_g_parameters, ++ .tx_s_parameters = cx25840_ir_tx_s_parameters, ++}; ++ ++ ++static const struct v4l2_subdev_ir_parameters default_rx_params = { ++ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), ++ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, ++ ++ .enable = false, ++ .interrupt_enable = false, ++ .shutdown = true, ++ ++ .modulation = true, ++ .carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */ ++ ++ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ ++ /* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ ++ .noise_filter_min_width = 333333, /* ns */ ++ .carrier_range_lower = 35000, ++ .carrier_range_upper = 37000, ++ .invert_level = false, ++}; ++ ++static const struct v4l2_subdev_ir_parameters default_tx_params = { ++ .bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec), ++ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, ++ ++ .enable = false, ++ .interrupt_enable = false, ++ .shutdown = true, ++ ++ .modulation = true, ++ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ ++ .duty_cycle = 25, /* 25 % - RC-5 carrier */ ++ .invert_level = false, ++ .invert_carrier_sense = false, ++}; ++ ++int cx25840_ir_probe(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct cx25840_ir_state *ir_state; ++ struct v4l2_subdev_ir_parameters default_params; ++ ++ /* Only init the IR controller for the CX2388[57] AV Core for now */ ++ if (!(is_cx23885(state) || is_cx23887(state))) ++ return 0; ++ ++ ir_state = kzalloc(sizeof(struct cx25840_ir_state), GFP_KERNEL); ++ if (ir_state == NULL) ++ return -ENOMEM; ++ ++ spin_lock_init(&ir_state->rx_kfifo_lock); ++ if (kfifo_alloc(&ir_state->rx_kfifo, ++ CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL)) { ++ kfree(ir_state); ++ return -ENOMEM; ++ } ++ ++ ir_state->c = state->c; ++ state->ir_state = ir_state; ++ ++ /* Ensure no interrupts arrive yet */ ++ if (is_cx23885(state) || is_cx23887(state)) ++ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK); ++ else ++ cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0); ++ ++ mutex_init(&ir_state->rx_params_lock); ++ default_params = default_rx_params; ++ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); ++ ++ mutex_init(&ir_state->tx_params_lock); ++ default_params = default_tx_params; ++ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); ++ ++ return 0; ++} ++ ++int cx25840_ir_remove(struct v4l2_subdev *sd) ++{ ++ struct cx25840_state *state = to_state(sd); ++ struct cx25840_ir_state *ir_state = to_ir_state(sd); ++ ++ if (ir_state == NULL) ++ return -ENODEV; ++ ++ cx25840_ir_rx_shutdown(sd); ++ cx25840_ir_tx_shutdown(sd); ++ ++ kfifo_free(&ir_state->rx_kfifo); ++ kfree(ir_state); ++ state->ir_state = NULL; ++ return 0; ++} +diff --git a/drivers/media/i2c/cx25840/cx25840-vbi.c b/drivers/media/i2c/cx25840/cx25840-vbi.c +new file mode 100644 +index 0000000..c39e91d +--- /dev/null ++++ b/drivers/media/i2c/cx25840/cx25840-vbi.c +@@ -0,0 +1,257 @@ ++/* cx25840 VBI functions ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "cx25840-core.h" ++ ++static int odd_parity(u8 c) ++{ ++ c ^= (c >> 4); ++ c ^= (c >> 2); ++ c ^= (c >> 1); ++ ++ return c & 1; ++} ++ ++static int decode_vps(u8 * dst, u8 * p) ++{ ++ static const u8 biphase_tbl[] = { ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, ++ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, ++ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, ++ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, ++ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, ++ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, ++ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ }; ++ ++ u8 c, err = 0; ++ int i; ++ ++ for (i = 0; i < 2 * 13; i += 2) { ++ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; ++ c = (biphase_tbl[p[i + 1]] & 0xf) | ++ ((biphase_tbl[p[i]] & 0xf) << 4); ++ dst[i / 2] = c; ++ } ++ ++ return err & 0xf0; ++} ++ ++int cx25840_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct cx25840_state *state = to_state(sd); ++ static const u16 lcr2vbi[] = { ++ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ ++ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ ++ V4L2_SLICED_CAPTION_525, /* 6 */ ++ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ ++ 0, 0, 0, 0 ++ }; ++ int is_pal = !(state->std & V4L2_STD_525_60); ++ int i; ++ ++ memset(svbi->service_lines, 0, sizeof(svbi->service_lines)); ++ svbi->service_set = 0; ++ /* we're done if raw VBI is active */ ++ if ((cx25840_read(client, 0x404) & 0x10) == 0) ++ return 0; ++ ++ if (is_pal) { ++ for (i = 7; i <= 23; i++) { ++ u8 v = cx25840_read(client, 0x424 + i - 7); ++ ++ svbi->service_lines[0][i] = lcr2vbi[v >> 4]; ++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; ++ svbi->service_set |= svbi->service_lines[0][i] | ++ svbi->service_lines[1][i]; ++ } ++ } else { ++ for (i = 10; i <= 21; i++) { ++ u8 v = cx25840_read(client, 0x424 + i - 10); ++ ++ svbi->service_lines[0][i] = lcr2vbi[v >> 4]; ++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; ++ svbi->service_set |= svbi->service_lines[0][i] | ++ svbi->service_lines[1][i]; ++ } ++ } ++ return 0; ++} ++ ++int cx25840_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct cx25840_state *state = to_state(sd); ++ int is_pal = !(state->std & V4L2_STD_525_60); ++ int vbi_offset = is_pal ? 1 : 0; ++ ++ /* Setup standard */ ++ cx25840_std_setup(client); ++ ++ /* VBI Offset */ ++ cx25840_write(client, 0x47f, vbi_offset); ++ cx25840_write(client, 0x404, 0x2e); ++ return 0; ++} ++ ++int cx25840_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct cx25840_state *state = to_state(sd); ++ int is_pal = !(state->std & V4L2_STD_525_60); ++ int vbi_offset = is_pal ? 1 : 0; ++ int i, x; ++ u8 lcr[24]; ++ ++ for (x = 0; x <= 23; x++) ++ lcr[x] = 0x00; ++ ++ /* Setup standard */ ++ cx25840_std_setup(client); ++ ++ /* Sliced VBI */ ++ cx25840_write(client, 0x404, 0x32); /* Ancillary data */ ++ cx25840_write(client, 0x406, 0x13); ++ cx25840_write(client, 0x47f, vbi_offset); ++ ++ if (is_pal) { ++ for (i = 0; i <= 6; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ } else { ++ for (i = 0; i <= 9; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ ++ for (i = 22; i <= 23; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ } ++ ++ for (i = 7; i <= 23; i++) { ++ for (x = 0; x <= 1; x++) { ++ switch (svbi->service_lines[1-x][i]) { ++ case V4L2_SLICED_TELETEXT_B: ++ lcr[i] |= 1 << (4 * x); ++ break; ++ case V4L2_SLICED_WSS_625: ++ lcr[i] |= 4 << (4 * x); ++ break; ++ case V4L2_SLICED_CAPTION_525: ++ lcr[i] |= 6 << (4 * x); ++ break; ++ case V4L2_SLICED_VPS: ++ lcr[i] |= 9 << (4 * x); ++ break; ++ } ++ } ++ } ++ ++ if (is_pal) { ++ for (x = 1, i = 0x424; i <= 0x434; i++, x++) ++ cx25840_write(client, i, lcr[6 + x]); ++ } else { ++ for (x = 1, i = 0x424; i <= 0x430; i++, x++) ++ cx25840_write(client, i, lcr[9 + x]); ++ for (i = 0x431; i <= 0x434; i++) ++ cx25840_write(client, i, 0); ++ } ++ ++ cx25840_write(client, 0x43c, 0x16); ++ cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); ++ return 0; ++} ++ ++int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) ++{ ++ struct cx25840_state *state = to_state(sd); ++ u8 *p = vbi->p; ++ int id1, id2, l, err = 0; ++ ++ if (p[0] || p[1] != 0xff || p[2] != 0xff || ++ (p[3] != 0x55 && p[3] != 0x91)) { ++ vbi->line = vbi->type = 0; ++ return 0; ++ } ++ ++ p += 4; ++ id1 = p[-1]; ++ id2 = p[0] & 0xf; ++ l = p[2] & 0x3f; ++ l += state->vbi_line_offset; ++ p += 4; ++ ++ switch (id2) { ++ case 1: ++ id2 = V4L2_SLICED_TELETEXT_B; ++ break; ++ case 4: ++ id2 = V4L2_SLICED_WSS_625; ++ break; ++ case 6: ++ id2 = V4L2_SLICED_CAPTION_525; ++ err = !odd_parity(p[0]) || !odd_parity(p[1]); ++ break; ++ case 9: ++ id2 = V4L2_SLICED_VPS; ++ if (decode_vps(p, p) != 0) ++ err = 1; ++ break; ++ default: ++ id2 = 0; ++ err = 1; ++ break; ++ } ++ ++ vbi->type = err ? 0 : id2; ++ vbi->line = err ? 0 : l; ++ vbi->is_second_field = err ? 0 : (id1 == 0x55); ++ vbi->p = p; ++ return 0; ++} +diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c +new file mode 100644 +index 0000000..08ae067 +--- /dev/null ++++ b/drivers/media/i2c/ir-kbd-i2c.c +@@ -0,0 +1,489 @@ ++/* ++ * ++ * keyboard input driver for i2c IR remote controls ++ * ++ * Copyright (c) 2000-2003 Gerd Knorr ++ * modified for PixelView (BT878P+W/FM) by ++ * Michal Kochanowicz ++ * Christoph Bartelmus ++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by ++ * Ulrich Mueller ++ * modified for em2820 based USB TV tuners by ++ * Markus Rechberger ++ * modified for DViCO Fusion HDTV 5 RT GOLD by ++ * Chaogui Zhang ++ * modified for MSI TV@nywhere Plus by ++ * Henry Wong ++ * Mark Schultz ++ * Brian Rogers ++ * modified for AVerMedia Cardbus by ++ * Oldrich Jedlicka ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ----------------------------------------------------------------------- */ ++/* insmod parameters */ ++ ++static int debug; ++module_param(debug, int, 0644); /* debug level (0,1,2) */ ++ ++ ++#define MODULE_NAME "ir-kbd-i2c" ++#define dprintk(level, fmt, arg...) if (debug >= level) \ ++ printk(KERN_DEBUG MODULE_NAME ": " fmt , ## arg) ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, ++ int size, int offset) ++{ ++ unsigned char buf[6]; ++ int start, range, toggle, dev, code, ircode; ++ ++ /* poll IR chip */ ++ if (size != i2c_master_recv(ir->c, buf, size)) ++ return -EIO; ++ ++ /* split rc5 data block ... */ ++ start = (buf[offset] >> 7) & 1; ++ range = (buf[offset] >> 6) & 1; ++ toggle = (buf[offset] >> 5) & 1; ++ dev = buf[offset] & 0x1f; ++ code = (buf[offset+1] >> 2) & 0x3f; ++ ++ /* rc5 has two start bits ++ * the first bit must be one ++ * the second bit defines the command range (1 = 0-63, 0 = 64 - 127) ++ */ ++ if (!start) ++ /* no key pressed */ ++ return 0; ++ /* ++ * Hauppauge remotes (black/silver) always use ++ * specific device ids. If we do not filter the ++ * device ids then messages destined for devices ++ * such as TVs (id=0) will get through causing ++ * mis-fired events. ++ * ++ * We also filter out invalid key presses which ++ * produce annoying debug log entries. ++ */ ++ ircode= (start << 12) | (toggle << 11) | (dev << 6) | code; ++ if ((ircode & 0x1fff)==0x1fff) ++ /* invalid key press */ ++ return 0; ++ ++ if (!range) ++ code += 64; ++ ++ dprintk(1,"ir hauppauge (rc5): s%d r%d t%d dev=%d code=%d\n", ++ start, range, toggle, dev, code); ++ ++ /* return key */ ++ *ir_key = (dev << 8) | code; ++ *ir_raw = ircode; ++ return 1; ++} ++ ++static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); ++} ++ ++static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ int ret; ++ unsigned char buf[1] = { 0 }; ++ ++ /* ++ * This is the same apparent "are you ready?" poll command observed ++ * watching Windows driver traffic and implemented in lirc_zilog. With ++ * this added, we get far saner remote behavior with z8 chips on usb ++ * connected devices, even with the default polling interval of 100ms. ++ */ ++ ret = i2c_master_send(ir->c, buf, 1); ++ if (ret != 1) ++ return (ret < 0) ? ret : -EINVAL; ++ ++ return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); ++} ++ ++static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char b; ++ ++ /* poll IR chip */ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ dprintk(1,"read error\n"); ++ return -EIO; ++ } ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char buf[4]; ++ ++ /* poll IR chip */ ++ if (4 != i2c_master_recv(ir->c, buf, 4)) { ++ dprintk(1,"read error\n"); ++ return -EIO; ++ } ++ ++ if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) ++ dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, ++ buf[0], buf[1], buf[2], buf[3]); ++ ++ /* no key pressed or signal from other ir remote */ ++ if(buf[0] != 0x1 || buf[1] != 0xfe) ++ return 0; ++ ++ *ir_key = buf[2]; ++ *ir_raw = (buf[2] << 8) | buf[3]; ++ ++ return 1; ++} ++ ++static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char b; ++ ++ /* poll IR chip */ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ dprintk(1,"read error\n"); ++ return -EIO; ++ } ++ ++ /* it seems that 0xFE indicates that a button is still hold ++ down, while 0xff indicates that no button is hold ++ down. 0xfe sequences are sometimes interrupted by 0xFF */ ++ ++ dprintk(2,"key %02x\n", b); ++ ++ if (b == 0xff) ++ return 0; ++ ++ if (b == 0xfe) ++ /* keep old data */ ++ return 1; ++ ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++static int get_key_avermedia_cardbus(struct IR_i2c *ir, ++ u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char subaddr, key, keygroup; ++ struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, ++ .buf = &subaddr, .len = 1}, ++ { .addr = ir->c->addr, .flags = I2C_M_RD, ++ .buf = &key, .len = 1} }; ++ subaddr = 0x0d; ++ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { ++ dprintk(1, "read error\n"); ++ return -EIO; ++ } ++ ++ if (key == 0xff) ++ return 0; ++ ++ subaddr = 0x0b; ++ msg[1].buf = &keygroup; ++ if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { ++ dprintk(1, "read error\n"); ++ return -EIO; ++ } ++ ++ if (keygroup == 0xff) ++ return 0; ++ ++ dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); ++ if (keygroup < 2 || keygroup > 3) { ++ /* Only a warning */ ++ dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", ++ keygroup, key); ++ } ++ key |= (keygroup & 1) << 6; ++ ++ *ir_key = key; ++ *ir_raw = key; ++ return 1; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int ir_key_poll(struct IR_i2c *ir) ++{ ++ static u32 ir_key, ir_raw; ++ int rc; ++ ++ dprintk(3, "%s\n", __func__); ++ rc = ir->get_key(ir, &ir_key, &ir_raw); ++ if (rc < 0) { ++ dprintk(2,"error\n"); ++ return rc; ++ } ++ ++ if (rc) { ++ dprintk(1, "%s: keycode = 0x%04x\n", __func__, ir_key); ++ rc_keydown(ir->rc, ir_key, 0); ++ } ++ return 0; ++} ++ ++static void ir_work(struct work_struct *work) ++{ ++ int rc; ++ struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); ++ ++ rc = ir_key_poll(ir); ++ if (rc == -ENODEV) { ++ rc_unregister_device(ir->rc); ++ ir->rc = NULL; ++ return; ++ } ++ ++ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling_interval)); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ char *ir_codes = NULL; ++ const char *name = NULL; ++ u64 rc_type = RC_BIT_UNKNOWN; ++ struct IR_i2c *ir; ++ struct rc_dev *rc = NULL; ++ struct i2c_adapter *adap = client->adapter; ++ unsigned short addr = client->addr; ++ int err; ++ ++ ir = kzalloc(sizeof(struct IR_i2c), GFP_KERNEL); ++ if (!ir) ++ return -ENOMEM; ++ ++ ir->c = client; ++ ir->polling_interval = DEFAULT_POLLING_INTERVAL; ++ i2c_set_clientdata(client, ir); ++ ++ switch(addr) { ++ case 0x64: ++ name = "Pixelview"; ++ ir->get_key = get_key_pixelview; ++ rc_type = RC_BIT_OTHER; ++ ir_codes = RC_MAP_EMPTY; ++ break; ++ case 0x18: ++ case 0x1f: ++ case 0x1a: ++ name = "Hauppauge"; ++ ir->get_key = get_key_haup; ++ rc_type = RC_BIT_RC5; ++ ir_codes = RC_MAP_HAUPPAUGE; ++ break; ++ case 0x30: ++ name = "KNC One"; ++ ir->get_key = get_key_knc1; ++ rc_type = RC_BIT_OTHER; ++ ir_codes = RC_MAP_EMPTY; ++ break; ++ case 0x6b: ++ name = "FusionHDTV"; ++ ir->get_key = get_key_fusionhdtv; ++ rc_type = RC_BIT_RC5; ++ ir_codes = RC_MAP_FUSIONHDTV_MCE; ++ break; ++ case 0x40: ++ name = "AVerMedia Cardbus remote"; ++ ir->get_key = get_key_avermedia_cardbus; ++ rc_type = RC_BIT_OTHER; ++ ir_codes = RC_MAP_AVERMEDIA_CARDBUS; ++ break; ++ case 0x71: ++ name = "Hauppauge/Zilog Z8"; ++ ir->get_key = get_key_haup_xvr; ++ rc_type = RC_BIT_RC5; ++ ir_codes = RC_MAP_HAUPPAUGE; ++ break; ++ } ++ ++ /* Let the caller override settings */ ++ if (client->dev.platform_data) { ++ const struct IR_i2c_init_data *init_data = ++ client->dev.platform_data; ++ ++ ir_codes = init_data->ir_codes; ++ rc = init_data->rc_dev; ++ ++ name = init_data->name; ++ if (init_data->type) ++ rc_type = init_data->type; ++ ++ if (init_data->polling_interval) ++ ir->polling_interval = init_data->polling_interval; ++ ++ switch (init_data->internal_get_key_func) { ++ case IR_KBD_GET_KEY_CUSTOM: ++ /* The bridge driver provided us its own function */ ++ ir->get_key = init_data->get_key; ++ break; ++ case IR_KBD_GET_KEY_PIXELVIEW: ++ ir->get_key = get_key_pixelview; ++ break; ++ case IR_KBD_GET_KEY_HAUP: ++ ir->get_key = get_key_haup; ++ break; ++ case IR_KBD_GET_KEY_KNC1: ++ ir->get_key = get_key_knc1; ++ break; ++ case IR_KBD_GET_KEY_FUSIONHDTV: ++ ir->get_key = get_key_fusionhdtv; ++ break; ++ case IR_KBD_GET_KEY_HAUP_XVR: ++ ir->get_key = get_key_haup_xvr; ++ break; ++ case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: ++ ir->get_key = get_key_avermedia_cardbus; ++ break; ++ } ++ } ++ ++ if (!rc) { ++ /* ++ * If platform_data doesn't specify rc_dev, initilize it ++ * internally ++ */ ++ rc = rc_allocate_device(); ++ if (!rc) { ++ err = -ENOMEM; ++ goto err_out_free; ++ } ++ } ++ ir->rc = rc; ++ ++ /* Make sure we are all setup before going on */ ++ if (!name || !ir->get_key || !rc_type || !ir_codes) { ++ dprintk(1, ": Unsupported device at address 0x%02x\n", ++ addr); ++ err = -ENODEV; ++ goto err_out_free; ++ } ++ ++ /* Sets name */ ++ snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); ++ ir->ir_codes = ir_codes; ++ ++ snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", ++ dev_name(&adap->dev), ++ dev_name(&client->dev)); ++ ++ /* ++ * Initialize input_dev fields ++ * It doesn't make sense to allow overriding them via platform_data ++ */ ++ rc->input_id.bustype = BUS_I2C; ++ rc->input_phys = ir->phys; ++ rc->input_name = ir->name; ++ ++ /* ++ * Initialize the other fields of rc_dev ++ */ ++ rc->map_name = ir->ir_codes; ++ rc->allowed_protos = rc_type; ++ if (!rc->driver_name) ++ rc->driver_name = MODULE_NAME; ++ ++ err = rc_register_device(rc); ++ if (err) ++ goto err_out_free; ++ ++ printk(MODULE_NAME ": %s detected at %s [%s]\n", ++ ir->name, ir->phys, adap->name); ++ ++ /* start polling via eventd */ ++ INIT_DELAYED_WORK(&ir->work, ir_work); ++ schedule_delayed_work(&ir->work, 0); ++ ++ return 0; ++ ++ err_out_free: ++ /* Only frees rc if it were allocated internally */ ++ rc_free_device(rc); ++ kfree(ir); ++ return err; ++} ++ ++static int ir_remove(struct i2c_client *client) ++{ ++ struct IR_i2c *ir = i2c_get_clientdata(client); ++ ++ /* kill outstanding polls */ ++ cancel_delayed_work_sync(&ir->work); ++ ++ /* unregister device */ ++ if (ir->rc) ++ rc_unregister_device(ir->rc); ++ ++ /* free memory */ ++ kfree(ir); ++ return 0; ++} ++ ++static const struct i2c_device_id ir_kbd_id[] = { ++ /* Generic entry for any IR receiver */ ++ { "ir_video", 0 }, ++ /* IR device specific entries should be added here */ ++ { "ir_rx_z8f0811_haup", 0 }, ++ { "ir_rx_z8f0811_hdpvr", 0 }, ++ { } ++}; ++ ++static struct i2c_driver ir_kbd_driver = { ++ .driver = { ++ .name = "ir-kbd-i2c", ++ }, ++ .probe = ir_probe, ++ .remove = ir_remove, ++ .id_table = ir_kbd_id, ++}; ++ ++module_i2c_driver(ir_kbd_driver); ++ ++/* ----------------------------------------------------------------------- */ ++ ++MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller"); ++MODULE_DESCRIPTION("input driver for i2c IR remote controls"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c +new file mode 100644 +index 0000000..04a6efa +--- /dev/null ++++ b/drivers/media/i2c/ks0127.c +@@ -0,0 +1,733 @@ ++/* ++ * Video Capture Driver (Video for Linux 1/2) ++ * for the Matrox Marvel G200,G400 and Rainbow Runner-G series ++ * ++ * This module is an interface to the KS0127 video decoder chip. ++ * ++ * Copyright (C) 1999 Ryan Drake ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ ***************************************************************************** ++ * ++ * Modified and extended by ++ * Mike Bernson ++ * Gerard v.d. Horst ++ * Leon van Stuivenberg ++ * Gernot Ziegler ++ * ++ * Version History: ++ * V1.0 Ryan Drake Initial version by Ryan Drake ++ * V1.1 Gerard v.d. Horst Added some debugoutput, reset the video-standard ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ks0127.h" ++ ++MODULE_DESCRIPTION("KS0127 video decoder driver"); ++MODULE_AUTHOR("Ryan Drake"); ++MODULE_LICENSE("GPL"); ++ ++/* Addresses */ ++#define I2C_KS0127_ADDON 0xD8 ++#define I2C_KS0127_ONBOARD 0xDA ++ ++ ++/* ks0127 control registers */ ++#define KS_STAT 0x00 ++#define KS_CMDA 0x01 ++#define KS_CMDB 0x02 ++#define KS_CMDC 0x03 ++#define KS_CMDD 0x04 ++#define KS_HAVB 0x05 ++#define KS_HAVE 0x06 ++#define KS_HS1B 0x07 ++#define KS_HS1E 0x08 ++#define KS_HS2B 0x09 ++#define KS_HS2E 0x0a ++#define KS_AGC 0x0b ++#define KS_HXTRA 0x0c ++#define KS_CDEM 0x0d ++#define KS_PORTAB 0x0e ++#define KS_LUMA 0x0f ++#define KS_CON 0x10 ++#define KS_BRT 0x11 ++#define KS_CHROMA 0x12 ++#define KS_CHROMB 0x13 ++#define KS_DEMOD 0x14 ++#define KS_SAT 0x15 ++#define KS_HUE 0x16 ++#define KS_VERTIA 0x17 ++#define KS_VERTIB 0x18 ++#define KS_VERTIC 0x19 ++#define KS_HSCLL 0x1a ++#define KS_HSCLH 0x1b ++#define KS_VSCLL 0x1c ++#define KS_VSCLH 0x1d ++#define KS_OFMTA 0x1e ++#define KS_OFMTB 0x1f ++#define KS_VBICTL 0x20 ++#define KS_CCDAT2 0x21 ++#define KS_CCDAT1 0x22 ++#define KS_VBIL30 0x23 ++#define KS_VBIL74 0x24 ++#define KS_VBIL118 0x25 ++#define KS_VBIL1512 0x26 ++#define KS_TTFRAM 0x27 ++#define KS_TESTA 0x28 ++#define KS_UVOFFH 0x29 ++#define KS_UVOFFL 0x2a ++#define KS_UGAIN 0x2b ++#define KS_VGAIN 0x2c ++#define KS_VAVB 0x2d ++#define KS_VAVE 0x2e ++#define KS_CTRACK 0x2f ++#define KS_POLCTL 0x30 ++#define KS_REFCOD 0x31 ++#define KS_INVALY 0x32 ++#define KS_INVALU 0x33 ++#define KS_INVALV 0x34 ++#define KS_UNUSEY 0x35 ++#define KS_UNUSEU 0x36 ++#define KS_UNUSEV 0x37 ++#define KS_USRSAV 0x38 ++#define KS_USREAV 0x39 ++#define KS_SHS1A 0x3a ++#define KS_SHS1B 0x3b ++#define KS_SHS1C 0x3c ++#define KS_CMDE 0x3d ++#define KS_VSDEL 0x3e ++#define KS_CMDF 0x3f ++#define KS_GAMMA0 0x40 ++#define KS_GAMMA1 0x41 ++#define KS_GAMMA2 0x42 ++#define KS_GAMMA3 0x43 ++#define KS_GAMMA4 0x44 ++#define KS_GAMMA5 0x45 ++#define KS_GAMMA6 0x46 ++#define KS_GAMMA7 0x47 ++#define KS_GAMMA8 0x48 ++#define KS_GAMMA9 0x49 ++#define KS_GAMMA10 0x4a ++#define KS_GAMMA11 0x4b ++#define KS_GAMMA12 0x4c ++#define KS_GAMMA13 0x4d ++#define KS_GAMMA14 0x4e ++#define KS_GAMMA15 0x4f ++#define KS_GAMMA16 0x50 ++#define KS_GAMMA17 0x51 ++#define KS_GAMMA18 0x52 ++#define KS_GAMMA19 0x53 ++#define KS_GAMMA20 0x54 ++#define KS_GAMMA21 0x55 ++#define KS_GAMMA22 0x56 ++#define KS_GAMMA23 0x57 ++#define KS_GAMMA24 0x58 ++#define KS_GAMMA25 0x59 ++#define KS_GAMMA26 0x5a ++#define KS_GAMMA27 0x5b ++#define KS_GAMMA28 0x5c ++#define KS_GAMMA29 0x5d ++#define KS_GAMMA30 0x5e ++#define KS_GAMMA31 0x5f ++#define KS_GAMMAD0 0x60 ++#define KS_GAMMAD1 0x61 ++#define KS_GAMMAD2 0x62 ++#define KS_GAMMAD3 0x63 ++#define KS_GAMMAD4 0x64 ++#define KS_GAMMAD5 0x65 ++#define KS_GAMMAD6 0x66 ++#define KS_GAMMAD7 0x67 ++#define KS_GAMMAD8 0x68 ++#define KS_GAMMAD9 0x69 ++#define KS_GAMMAD10 0x6a ++#define KS_GAMMAD11 0x6b ++#define KS_GAMMAD12 0x6c ++#define KS_GAMMAD13 0x6d ++#define KS_GAMMAD14 0x6e ++#define KS_GAMMAD15 0x6f ++#define KS_GAMMAD16 0x70 ++#define KS_GAMMAD17 0x71 ++#define KS_GAMMAD18 0x72 ++#define KS_GAMMAD19 0x73 ++#define KS_GAMMAD20 0x74 ++#define KS_GAMMAD21 0x75 ++#define KS_GAMMAD22 0x76 ++#define KS_GAMMAD23 0x77 ++#define KS_GAMMAD24 0x78 ++#define KS_GAMMAD25 0x79 ++#define KS_GAMMAD26 0x7a ++#define KS_GAMMAD27 0x7b ++#define KS_GAMMAD28 0x7c ++#define KS_GAMMAD29 0x7d ++#define KS_GAMMAD30 0x7e ++#define KS_GAMMAD31 0x7f ++ ++ ++/**************************************************************************** ++* mga_dev : represents one ks0127 chip. ++****************************************************************************/ ++ ++struct adjust { ++ int contrast; ++ int bright; ++ int hue; ++ int ugain; ++ int vgain; ++}; ++ ++struct ks0127 { ++ struct v4l2_subdev sd; ++ v4l2_std_id norm; ++ int ident; ++ u8 regs[256]; ++}; ++ ++static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ks0127, sd); ++} ++ ++ ++static int debug; /* insmod parameter */ ++ ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug output"); ++ ++static u8 reg_defaults[64]; ++ ++static void init_reg_defaults(void) ++{ ++ static int initialized; ++ u8 *table = reg_defaults; ++ ++ if (initialized) ++ return; ++ initialized = 1; ++ ++ table[KS_CMDA] = 0x2c; /* VSE=0, CCIR 601, autodetect standard */ ++ table[KS_CMDB] = 0x12; /* VALIGN=0, AGC control and input */ ++ table[KS_CMDC] = 0x00; /* Test options */ ++ /* clock & input select, write 1 to PORTA */ ++ table[KS_CMDD] = 0x01; ++ table[KS_HAVB] = 0x00; /* HAV Start Control */ ++ table[KS_HAVE] = 0x00; /* HAV End Control */ ++ table[KS_HS1B] = 0x10; /* HS1 Start Control */ ++ table[KS_HS1E] = 0x00; /* HS1 End Control */ ++ table[KS_HS2B] = 0x00; /* HS2 Start Control */ ++ table[KS_HS2E] = 0x00; /* HS2 End Control */ ++ table[KS_AGC] = 0x53; /* Manual setting for AGC */ ++ table[KS_HXTRA] = 0x00; /* Extra Bits for HAV and HS1/2 */ ++ table[KS_CDEM] = 0x00; /* Chroma Demodulation Control */ ++ table[KS_PORTAB] = 0x0f; /* port B is input, port A output GPPORT */ ++ table[KS_LUMA] = 0x01; /* Luma control */ ++ table[KS_CON] = 0x00; /* Contrast Control */ ++ table[KS_BRT] = 0x00; /* Brightness Control */ ++ table[KS_CHROMA] = 0x2a; /* Chroma control A */ ++ table[KS_CHROMB] = 0x90; /* Chroma control B */ ++ table[KS_DEMOD] = 0x00; /* Chroma Demodulation Control & Status */ ++ table[KS_SAT] = 0x00; /* Color Saturation Control*/ ++ table[KS_HUE] = 0x00; /* Hue Control */ ++ table[KS_VERTIA] = 0x00; /* Vertical Processing Control A */ ++ /* Vertical Processing Control B, luma 1 line delayed */ ++ table[KS_VERTIB] = 0x12; ++ table[KS_VERTIC] = 0x0b; /* Vertical Processing Control C */ ++ table[KS_HSCLL] = 0x00; /* Horizontal Scaling Ratio Low */ ++ table[KS_HSCLH] = 0x00; /* Horizontal Scaling Ratio High */ ++ table[KS_VSCLL] = 0x00; /* Vertical Scaling Ratio Low */ ++ table[KS_VSCLH] = 0x00; /* Vertical Scaling Ratio High */ ++ /* 16 bit YCbCr 4:2:2 output; I can't make the bt866 like 8 bit /Sam */ ++ table[KS_OFMTA] = 0x30; ++ table[KS_OFMTB] = 0x00; /* Output Control B */ ++ /* VBI Decoder Control; 4bit fmt: avoid Y overflow */ ++ table[KS_VBICTL] = 0x5d; ++ table[KS_CCDAT2] = 0x00; /* Read Only register */ ++ table[KS_CCDAT1] = 0x00; /* Read Only register */ ++ table[KS_VBIL30] = 0xa8; /* VBI data decoding options */ ++ table[KS_VBIL74] = 0xaa; /* VBI data decoding options */ ++ table[KS_VBIL118] = 0x2a; /* VBI data decoding options */ ++ table[KS_VBIL1512] = 0x00; /* VBI data decoding options */ ++ table[KS_TTFRAM] = 0x00; /* Teletext frame alignment pattern */ ++ table[KS_TESTA] = 0x00; /* test register, shouldn't be written */ ++ table[KS_UVOFFH] = 0x00; /* UV Offset Adjustment High */ ++ table[KS_UVOFFL] = 0x00; /* UV Offset Adjustment Low */ ++ table[KS_UGAIN] = 0x00; /* U Component Gain Adjustment */ ++ table[KS_VGAIN] = 0x00; /* V Component Gain Adjustment */ ++ table[KS_VAVB] = 0x07; /* VAV Begin */ ++ table[KS_VAVE] = 0x00; /* VAV End */ ++ table[KS_CTRACK] = 0x00; /* Chroma Tracking Control */ ++ table[KS_POLCTL] = 0x41; /* Timing Signal Polarity Control */ ++ table[KS_REFCOD] = 0x80; /* Reference Code Insertion Control */ ++ table[KS_INVALY] = 0x10; /* Invalid Y Code */ ++ table[KS_INVALU] = 0x80; /* Invalid U Code */ ++ table[KS_INVALV] = 0x80; /* Invalid V Code */ ++ table[KS_UNUSEY] = 0x10; /* Unused Y Code */ ++ table[KS_UNUSEU] = 0x80; /* Unused U Code */ ++ table[KS_UNUSEV] = 0x80; /* Unused V Code */ ++ table[KS_USRSAV] = 0x00; /* reserved */ ++ table[KS_USREAV] = 0x00; /* reserved */ ++ table[KS_SHS1A] = 0x00; /* User Defined SHS1 A */ ++ /* User Defined SHS1 B, ALT656=1 on 0127B */ ++ table[KS_SHS1B] = 0x80; ++ table[KS_SHS1C] = 0x00; /* User Defined SHS1 C */ ++ table[KS_CMDE] = 0x00; /* Command Register E */ ++ table[KS_VSDEL] = 0x00; /* VS Delay Control */ ++ /* Command Register F, update -immediately- */ ++ /* (there might come no vsync)*/ ++ table[KS_CMDF] = 0x02; ++} ++ ++ ++/* We need to manually read because of a bug in the KS0127 chip. ++ * ++ * An explanation from kayork@mail.utexas.edu: ++ * ++ * During I2C reads, the KS0127 only samples for a stop condition ++ * during the place where the acknowledge bit should be. Any standard ++ * I2C implementation (correctly) throws in another clock transition ++ * at the 9th bit, and the KS0127 will not recognize the stop condition ++ * and will continue to clock out data. ++ * ++ * So we have to do the read ourself. Big deal. ++ * workaround in i2c-algo-bit ++ */ ++ ++ ++static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ char val = 0; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .len = sizeof(reg), ++ .buf = ® ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD | I2C_M_NO_RD_ACK, ++ .len = sizeof(val), ++ .buf = &val ++ } ++ }; ++ int ret; ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (ret != ARRAY_SIZE(msgs)) ++ v4l2_dbg(1, debug, sd, "read error\n"); ++ ++ return val; ++} ++ ++ ++static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ks0127 *ks = to_ks0127(sd); ++ char msg[] = { reg, val }; ++ ++ if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) ++ v4l2_dbg(1, debug, sd, "write error\n"); ++ ++ ks->regs[reg] = val; ++} ++ ++ ++/* generic bit-twiddling */ ++static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) ++{ ++ struct ks0127 *ks = to_ks0127(sd); ++ ++ u8 val = ks->regs[reg]; ++ val = (val & and_v) | or_v; ++ ks0127_write(sd, reg, val); ++} ++ ++ ++ ++/**************************************************************************** ++* ks0127 private api ++****************************************************************************/ ++static void ks0127_init(struct v4l2_subdev *sd) ++{ ++ struct ks0127 *ks = to_ks0127(sd); ++ u8 *table = reg_defaults; ++ int i; ++ ++ ks->ident = V4L2_IDENT_KS0127; ++ ++ v4l2_dbg(1, debug, sd, "reset\n"); ++ msleep(1); ++ ++ /* initialize all registers to known values */ ++ /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ ++ ++ for (i = 1; i < 33; i++) ++ ks0127_write(sd, i, table[i]); ++ ++ for (i = 35; i < 40; i++) ++ ks0127_write(sd, i, table[i]); ++ ++ for (i = 41; i < 56; i++) ++ ks0127_write(sd, i, table[i]); ++ ++ for (i = 58; i < 64; i++) ++ ks0127_write(sd, i, table[i]); ++ ++ ++ if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { ++ ks->ident = V4L2_IDENT_KS0122S; ++ v4l2_dbg(1, debug, sd, "ks0122s found\n"); ++ return; ++ } ++ ++ switch (ks0127_read(sd, KS_CMDE) & 0x0f) { ++ case 0: ++ v4l2_dbg(1, debug, sd, "ks0127 found\n"); ++ break; ++ ++ case 9: ++ ks->ident = V4L2_IDENT_KS0127B; ++ v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, "unknown revision\n"); ++ break; ++ } ++} ++ ++static int ks0127_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct ks0127 *ks = to_ks0127(sd); ++ ++ switch (input) { ++ case KS_INPUT_COMPOSITE_1: ++ case KS_INPUT_COMPOSITE_2: ++ case KS_INPUT_COMPOSITE_3: ++ case KS_INPUT_COMPOSITE_4: ++ case KS_INPUT_COMPOSITE_5: ++ case KS_INPUT_COMPOSITE_6: ++ v4l2_dbg(1, debug, sd, ++ "s_routing %d: Composite\n", input); ++ /* autodetect 50/60 Hz */ ++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); ++ /* VSE=0 */ ++ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); ++ /* set input line */ ++ ks0127_and_or(sd, KS_CMDB, 0xb0, input); ++ /* non-freerunning mode */ ++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); ++ /* analog input */ ++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); ++ /* enable chroma demodulation */ ++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); ++ /* chroma trap, HYBWR=1 */ ++ ks0127_and_or(sd, KS_LUMA, 0x00, ++ (reg_defaults[KS_LUMA])|0x0c); ++ /* scaler fullbw, luma comb off */ ++ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); ++ /* manual chroma comb .25 .5 .25 */ ++ ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); ++ ++ /* chroma path delay */ ++ ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); ++ ++ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); ++ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); ++ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); ++ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); ++ break; ++ ++ case KS_INPUT_SVIDEO_1: ++ case KS_INPUT_SVIDEO_2: ++ case KS_INPUT_SVIDEO_3: ++ v4l2_dbg(1, debug, sd, ++ "s_routing %d: S-Video\n", input); ++ /* autodetect 50/60 Hz */ ++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); ++ /* VSE=0 */ ++ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); ++ /* set input line */ ++ ks0127_and_or(sd, KS_CMDB, 0xb0, input); ++ /* non-freerunning mode */ ++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); ++ /* analog input */ ++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); ++ /* enable chroma demodulation */ ++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); ++ ks0127_and_or(sd, KS_LUMA, 0x00, ++ reg_defaults[KS_LUMA]); ++ /* disable luma comb */ ++ ks0127_and_or(sd, KS_VERTIA, 0x08, ++ (reg_defaults[KS_VERTIA]&0xf0)|0x01); ++ ks0127_and_or(sd, KS_VERTIC, 0x0f, ++ reg_defaults[KS_VERTIC]&0xf0); ++ ++ ks0127_and_or(sd, KS_CHROMB, 0x0f, ++ reg_defaults[KS_CHROMB]&0xf0); ++ ++ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); ++ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); ++ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); ++ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); ++ break; ++ ++ case KS_INPUT_YUV656: ++ v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); ++ if (ks->norm & V4L2_STD_525_60) ++ /* force 60 Hz */ ++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); ++ else ++ /* force 50 Hz */ ++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); ++ ++ ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ ++ /* set input line and VALIGN */ ++ ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); ++ /* freerunning mode, */ ++ /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ ++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); ++ /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ ++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); ++ /* disable chroma demodulation */ ++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); ++ /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ ++ ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); ++ ks0127_and_or(sd, KS_VERTIC, 0x0f, ++ reg_defaults[KS_VERTIC]&0xf0); ++ ++ /* scaler fullbw, luma comb off */ ++ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); ++ ++ ks0127_and_or(sd, KS_CHROMB, 0x0f, ++ reg_defaults[KS_CHROMB]&0xf0); ++ ++ ks0127_and_or(sd, KS_CON, 0x00, 0x00); ++ ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ ++ /* spec: 229 (e5) */ ++ ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); ++ ks0127_and_or(sd, KS_HUE, 0x00, 0); ++ ++ ks0127_and_or(sd, KS_UGAIN, 0x00, 238); ++ ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); ++ ++ /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ ++ ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); ++ ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); ++ break; ++ ++ default: ++ v4l2_dbg(1, debug, sd, ++ "s_routing: Unknown input %d\n", input); ++ break; ++ } ++ ++ /* hack: CDMLPF sometimes spontaneously switches on; */ ++ /* force back off */ ++ ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); ++ return 0; ++} ++ ++static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct ks0127 *ks = to_ks0127(sd); ++ ++ /* Set to automatic SECAM/Fsc mode */ ++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); ++ ++ ks->norm = std; ++ if (std & V4L2_STD_NTSC) { ++ v4l2_dbg(1, debug, sd, ++ "s_std: NTSC_M\n"); ++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); ++ } else if (std & V4L2_STD_PAL_N) { ++ v4l2_dbg(1, debug, sd, ++ "s_std: NTSC_N (fixme)\n"); ++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); ++ } else if (std & V4L2_STD_PAL) { ++ v4l2_dbg(1, debug, sd, ++ "s_std: PAL_N\n"); ++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); ++ } else if (std & V4L2_STD_PAL_M) { ++ v4l2_dbg(1, debug, sd, ++ "s_std: PAL_M (fixme)\n"); ++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); ++ } else if (std & V4L2_STD_SECAM) { ++ v4l2_dbg(1, debug, sd, ++ "s_std: SECAM\n"); ++ ++ /* set to secam autodetection */ ++ ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); ++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); ++ schedule_timeout_interruptible(HZ/10+1); ++ ++ /* did it autodetect? */ ++ if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) ++ /* force to secam mode */ ++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); ++ } else { ++ v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", ++ (unsigned long long)std); ++ } ++ return 0; ++} ++ ++static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); ++ if (enable) { ++ /* All output pins on */ ++ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); ++ /* Obey the OEN pin */ ++ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); ++ } else { ++ /* Video output pins off */ ++ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); ++ /* Ignore the OEN pin */ ++ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); ++ } ++ return 0; ++} ++ ++static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) ++{ ++ int stat = V4L2_IN_ST_NO_SIGNAL; ++ u8 status; ++ v4l2_std_id std = V4L2_STD_ALL; ++ ++ status = ks0127_read(sd, KS_STAT); ++ if (!(status & 0x20)) /* NOVID not set */ ++ stat = 0; ++ if (!(status & 0x01)) /* CLOCK set */ ++ stat |= V4L2_IN_ST_NO_COLOR; ++ if ((status & 0x08)) /* PALDET set */ ++ std = V4L2_STD_PAL; ++ else ++ std = V4L2_STD_NTSC; ++ if (pstd) ++ *pstd = std; ++ if (pstatus) ++ *pstatus = stat; ++ return 0; ++} ++ ++static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ v4l2_dbg(1, debug, sd, "querystd\n"); ++ return ks0127_status(sd, NULL, std); ++} ++ ++static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ v4l2_dbg(1, debug, sd, "g_input_status\n"); ++ return ks0127_status(sd, status, NULL); ++} ++ ++static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ks0127 *ks = to_ks0127(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops ks0127_core_ops = { ++ .g_chip_ident = ks0127_g_chip_ident, ++ .s_std = ks0127_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops ks0127_video_ops = { ++ .s_routing = ks0127_s_routing, ++ .s_stream = ks0127_s_stream, ++ .querystd = ks0127_querystd, ++ .g_input_status = ks0127_g_input_status, ++}; ++ ++static const struct v4l2_subdev_ops ks0127_ops = { ++ .core = &ks0127_core_ops, ++ .video = &ks0127_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++ ++static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct ks0127 *ks; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "%s chip found @ 0x%x (%s)\n", ++ client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", ++ client->addr << 1, client->adapter->name); ++ ++ ks = kzalloc(sizeof(*ks), GFP_KERNEL); ++ if (ks == NULL) ++ return -ENOMEM; ++ sd = &ks->sd; ++ v4l2_i2c_subdev_init(sd, client, &ks0127_ops); ++ ++ /* power up */ ++ init_reg_defaults(); ++ ks0127_write(sd, KS_CMDA, 0x2c); ++ mdelay(10); ++ ++ /* reset the device */ ++ ks0127_init(sd); ++ return 0; ++} ++ ++static int ks0127_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ ++ ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ ++ kfree(to_ks0127(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id ks0127_id[] = { ++ { "ks0127", 0 }, ++ { "ks0127b", 0 }, ++ { "ks0122s", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ks0127_id); ++ ++static struct i2c_driver ks0127_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ks0127", ++ }, ++ .probe = ks0127_probe, ++ .remove = ks0127_remove, ++ .id_table = ks0127_id, ++}; ++ ++module_i2c_driver(ks0127_driver); +diff --git a/drivers/media/i2c/ks0127.h b/drivers/media/i2c/ks0127.h +new file mode 100644 +index 0000000..cb8abd5 +--- /dev/null ++++ b/drivers/media/i2c/ks0127.h +@@ -0,0 +1,51 @@ ++/* ++ * Video Capture Driver ( Video for Linux 1/2 ) ++ * for the Matrox Marvel G200,G400 and Rainbow Runner-G series ++ * ++ * This module is an interface to the KS0127 video decoder chip. ++ * ++ * Copyright (C) 1999 Ryan Drake ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#ifndef KS0127_H ++#define KS0127_H ++ ++/* input channels */ ++#define KS_INPUT_COMPOSITE_1 0 ++#define KS_INPUT_COMPOSITE_2 1 ++#define KS_INPUT_COMPOSITE_3 2 ++#define KS_INPUT_COMPOSITE_4 4 ++#define KS_INPUT_COMPOSITE_5 5 ++#define KS_INPUT_COMPOSITE_6 6 ++ ++#define KS_INPUT_SVIDEO_1 8 ++#define KS_INPUT_SVIDEO_2 9 ++#define KS_INPUT_SVIDEO_3 10 ++ ++#define KS_INPUT_YUV656 15 ++#define KS_INPUT_COUNT 10 ++ ++/* output channels */ ++#define KS_OUTPUT_YUV656E 0 ++#define KS_OUTPUT_EXV 1 ++ ++/* video standards */ ++#define KS_STD_NTSC_N 112 /* 50 Hz NTSC */ ++#define KS_STD_PAL_M 113 /* 60 Hz PAL */ ++ ++#endif /* KS0127_H */ ++ +diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c +new file mode 100644 +index 0000000..0991576 +--- /dev/null ++++ b/drivers/media/i2c/m52790.c +@@ -0,0 +1,216 @@ ++/* ++ * m52790 i2c ivtv driver. ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * A/V source switching Mitsubishi M52790SP/FP ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); ++MODULE_AUTHOR("Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++ ++struct m52790_state { ++ struct v4l2_subdev sd; ++ u16 input; ++ u16 output; ++}; ++ ++static inline struct m52790_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct m52790_state, sd); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int m52790_write(struct v4l2_subdev *sd) ++{ ++ struct m52790_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ u8 sw1 = (state->input | state->output) & 0xff; ++ u8 sw2 = (state->input | state->output) >> 8; ++ ++ return i2c_smbus_write_byte_data(client, sw1, sw2); ++} ++ ++/* Note: audio and video are linked and cannot be switched separately. ++ So audio and video routing commands are identical for this chip. ++ In theory the video amplifier and audio modes could be handled ++ separately for the output, but that seems to be overkill right now. ++ The same holds for implementing an audio mute control, this is now ++ part of the audio output routing. The normal case is that another ++ chip takes care of the actual muting so making it part of the ++ output routing seems to be the right thing to do for now. */ ++static int m52790_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct m52790_state *state = to_state(sd); ++ ++ state->input = input; ++ state->output = output; ++ m52790_write(sd); ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct m52790_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (reg->reg != 0) ++ return -EINVAL; ++ reg->size = 1; ++ reg->val = state->input | state->output; ++ return 0; ++} ++ ++static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct m52790_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (reg->reg != 0) ++ return -EINVAL; ++ state->input = reg->val & 0x0303; ++ state->output = reg->val & ~0x0303; ++ m52790_write(sd); ++ return 0; ++} ++#endif ++ ++static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); ++} ++ ++static int m52790_log_status(struct v4l2_subdev *sd) ++{ ++ struct m52790_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Switch 1: %02x\n", ++ (state->input | state->output) & 0xff); ++ v4l2_info(sd, "Switch 2: %02x\n", ++ (state->input | state->output) >> 8); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops m52790_core_ops = { ++ .log_status = m52790_log_status, ++ .g_chip_ident = m52790_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = m52790_g_register, ++ .s_register = m52790_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_audio_ops m52790_audio_ops = { ++ .s_routing = m52790_s_routing, ++}; ++ ++static const struct v4l2_subdev_video_ops m52790_video_ops = { ++ .s_routing = m52790_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops m52790_ops = { ++ .core = &m52790_core_ops, ++ .audio = &m52790_audio_ops, ++ .video = &m52790_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* i2c implementation */ ++ ++static int m52790_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct m52790_state *state; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct m52790_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &m52790_ops); ++ state->input = M52790_IN_TUNER; ++ state->output = M52790_OUT_STEREO; ++ m52790_write(sd); ++ return 0; ++} ++ ++static int m52790_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id m52790_id[] = { ++ { "m52790", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, m52790_id); ++ ++static struct i2c_driver m52790_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "m52790", ++ }, ++ .probe = m52790_probe, ++ .remove = m52790_remove, ++ .id_table = m52790_id, ++}; ++ ++module_i2c_driver(m52790_driver); +diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig +new file mode 100644 +index 0000000..dc8c250 +--- /dev/null ++++ b/drivers/media/i2c/m5mols/Kconfig +@@ -0,0 +1,6 @@ ++config VIDEO_M5MOLS ++ tristate "Fujitsu M-5MOLS 8MP sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API ++ depends on MEDIA_CAMERA_SUPPORT ++ ---help--- ++ This driver supports Fujitsu M-5MOLS camera sensor with ISP +diff --git a/drivers/media/i2c/m5mols/Makefile b/drivers/media/i2c/m5mols/Makefile +new file mode 100644 +index 0000000..0a44e02 +--- /dev/null ++++ b/drivers/media/i2c/m5mols/Makefile +@@ -0,0 +1,3 @@ ++m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o ++ ++obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o +diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h +new file mode 100644 +index 0000000..90a6c52 +--- /dev/null ++++ b/drivers/media/i2c/m5mols/m5mols.h +@@ -0,0 +1,348 @@ ++/* ++ * Header for M-5MOLS 8M Pixel camera sensor with ISP ++ * ++ * Copyright (C) 2011 Samsung Electronics Co., Ltd. ++ * Author: HeungJun Kim ++ * ++ * Copyright (C) 2009 Samsung Electronics Co., Ltd. ++ * Author: Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef M5MOLS_H ++#define M5MOLS_H ++ ++#include ++#include ++#include "m5mols_reg.h" ++ ++ ++/* An amount of data transmitted in addition to the value ++ * determined by CAPP_JPEG_SIZE_MAX register. ++ */ ++#define M5MOLS_JPEG_TAGS_SIZE 0x20000 ++#define M5MOLS_MAIN_JPEG_SIZE_MAX (5 * SZ_1M) ++ ++extern int m5mols_debug; ++ ++enum m5mols_restype { ++ M5MOLS_RESTYPE_MONITOR, ++ M5MOLS_RESTYPE_CAPTURE, ++ M5MOLS_RESTYPE_MAX, ++}; ++ ++/** ++ * struct m5mols_resolution - structure for the resolution ++ * @type: resolution type according to the pixel code ++ * @width: width of the resolution ++ * @height: height of the resolution ++ * @reg: resolution preset register value ++ */ ++struct m5mols_resolution { ++ u8 reg; ++ enum m5mols_restype type; ++ u16 width; ++ u16 height; ++}; ++ ++/** ++ * struct m5mols_exif - structure for the EXIF information of M-5MOLS ++ * @exposure_time: exposure time register value ++ * @shutter_speed: speed of the shutter register value ++ * @aperture: aperture register value ++ * @exposure_bias: it calls also EV bias ++ * @iso_speed: ISO register value ++ * @flash: status register value of the flash ++ * @sdr: status register value of the Subject Distance Range ++ * @qval: not written exact meaning in document ++ */ ++struct m5mols_exif { ++ u32 exposure_time; ++ u32 shutter_speed; ++ u32 aperture; ++ u32 brightness; ++ u32 exposure_bias; ++ u16 iso_speed; ++ u16 flash; ++ u16 sdr; ++ u16 qval; ++}; ++ ++/** ++ * struct m5mols_capture - Structure for the capture capability ++ * @exif: EXIF information ++ * @buf_size: internal JPEG frame buffer size, in bytes ++ * @main: size in bytes of the main image ++ * @thumb: size in bytes of the thumb image, if it was accompanied ++ * @total: total size in bytes of the produced image ++ */ ++struct m5mols_capture { ++ struct m5mols_exif exif; ++ unsigned int buf_size; ++ u32 main; ++ u32 thumb; ++ u32 total; ++}; ++ ++/** ++ * struct m5mols_scenemode - structure for the scenemode capability ++ * @metering: metering light register value ++ * @ev_bias: EV bias register value ++ * @wb_mode: mode which means the WhiteBalance is Auto or Manual ++ * @wb_preset: whitebalance preset register value in the Manual mode ++ * @chroma_en: register value whether the Chroma capability is enabled or not ++ * @chroma_lvl: chroma's level register value ++ * @edge_en: register value Whether the Edge capability is enabled or not ++ * @edge_lvl: edge's level register value ++ * @af_range: Auto Focus's range ++ * @fd_mode: Face Detection mode ++ * @mcc: Multi-axis Color Conversion which means emotion color ++ * @light: status of the Light ++ * @flash: status of the Flash ++ * @tone: Tone color which means Contrast ++ * @iso: ISO register value ++ * @capt_mode: Mode of the Image Stabilization while the camera capturing ++ * @wdr: Wide Dynamic Range register value ++ * ++ * The each value according to each scenemode is recommended in the documents. ++ */ ++struct m5mols_scenemode { ++ u8 metering; ++ u8 ev_bias; ++ u8 wb_mode; ++ u8 wb_preset; ++ u8 chroma_en; ++ u8 chroma_lvl; ++ u8 edge_en; ++ u8 edge_lvl; ++ u8 af_range; ++ u8 fd_mode; ++ u8 mcc; ++ u8 light; ++ u8 flash; ++ u8 tone; ++ u8 iso; ++ u8 capt_mode; ++ u8 wdr; ++}; ++ ++/** ++ * struct m5mols_version - firmware version information ++ * @customer: customer information ++ * @project: version of project information according to customer ++ * @fw: firmware revision ++ * @hw: hardware revision ++ * @param: version of the parameter ++ * @awb: Auto WhiteBalance algorithm version ++ * @str: information about manufacturer and packaging vendor ++ * @af: Auto Focus version ++ * ++ * The register offset starts the customer version at 0x0, and it ends ++ * the awb version at 0x09. The customer, project information occupies 1 bytes ++ * each. And also the fw, hw, param, awb each requires 2 bytes. The str is ++ * unique string associated with firmware's version. It includes information ++ * about manufacturer and the vendor of the sensor's packaging. The least ++ * significant 2 bytes of the string indicate packaging manufacturer. ++ */ ++#define VERSION_STRING_SIZE 22 ++struct m5mols_version { ++ u8 customer; ++ u8 project; ++ u16 fw; ++ u16 hw; ++ u16 param; ++ u16 awb; ++ u8 str[VERSION_STRING_SIZE]; ++ u8 af; ++}; ++ ++/** ++ * struct m5mols_info - M-5MOLS driver data structure ++ * @pdata: platform data ++ * @sd: v4l-subdev instance ++ * @pad: media pad ++ * @irq_waitq: waitqueue for the capture ++ * @irq_done: set to 1 in the interrupt handler ++ * @handle: control handler ++ * @auto_exposure: auto/manual exposure control ++ * @exposure_bias: exposure compensation control ++ * @exposure: manual exposure control ++ * @metering: exposure metering control ++ * @auto_iso: auto/manual ISO sensitivity control ++ * @iso: manual ISO sensitivity control ++ * @auto_wb: auto white balance control ++ * @lock_3a: 3A lock control ++ * @colorfx: color effect control ++ * @saturation: saturation control ++ * @zoom: zoom control ++ * @wdr: wide dynamic range control ++ * @stabilization: image stabilization control ++ * @jpeg_quality: JPEG compression quality control ++ * @set_power: optional power callback to the board code ++ * @lock: mutex protecting the structure fields below ++ * @ffmt: current fmt according to resolution type ++ * @res_type: current resolution type ++ * @ver: information of the version ++ * @cap: the capture mode attributes ++ * @isp_ready: 1 when the ISP controller has completed booting ++ * @power: current sensor's power status ++ * @ctrl_sync: 1 when the control handler state is restored in H/W ++ * @resolution: register value for current resolution ++ * @mode: register value for current operation mode ++ */ ++struct m5mols_info { ++ const struct m5mols_platform_data *pdata; ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ ++ wait_queue_head_t irq_waitq; ++ atomic_t irq_done; ++ ++ struct v4l2_ctrl_handler handle; ++ struct { ++ /* exposure/exposure bias/auto exposure cluster */ ++ struct v4l2_ctrl *auto_exposure; ++ struct v4l2_ctrl *exposure_bias; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *metering; ++ }; ++ struct { ++ /* iso/auto iso cluster */ ++ struct v4l2_ctrl *auto_iso; ++ struct v4l2_ctrl *iso; ++ }; ++ struct v4l2_ctrl *auto_wb; ++ ++ struct v4l2_ctrl *lock_3a; ++ struct v4l2_ctrl *colorfx; ++ struct v4l2_ctrl *saturation; ++ struct v4l2_ctrl *zoom; ++ struct v4l2_ctrl *wdr; ++ struct v4l2_ctrl *stabilization; ++ struct v4l2_ctrl *jpeg_quality; ++ ++ int (*set_power)(struct device *dev, int on); ++ ++ struct mutex lock; ++ ++ struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; ++ int res_type; ++ ++ struct m5mols_version ver; ++ struct m5mols_capture cap; ++ ++ unsigned int isp_ready:1; ++ unsigned int power:1; ++ unsigned int ctrl_sync:1; ++ ++ u8 resolution; ++ u8 mode; ++}; ++ ++#define is_available_af(__info) (__info->ver.af) ++#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) ++#define is_manufacturer(__info, __manufacturer) \ ++ (__info->ver.str[0] == __manufacturer[0] && \ ++ __info->ver.str[1] == __manufacturer[1]) ++/* ++ * I2C operation of the M-5MOLS ++ * ++ * The I2C read operation of the M-5MOLS requires 2 messages. The first ++ * message sends the information about the command, command category, and total ++ * message size. The second message is used to retrieve the data specifed in ++ * the first message ++ * ++ * 1st message 2nd message ++ * +-------+---+----------+-----+-------+ +------+------+------+------+ ++ * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | ++ * +-------+---+----------+-----+-------+ +------+------+------+------+ ++ * - size1: message data size(5 in this case) ++ * - size2: desired buffer size of the 2nd message ++ * - d[0..3]: according to size2 ++ * ++ * The I2C write operation needs just one message. The message includes ++ * category, command, total size, and desired data. ++ * ++ * 1st message ++ * +-------+---+----------+-----+------+------+------+------+ ++ * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | ++ * +-------+---+----------+-----+------+------+------+------+ ++ * - d[0..3]: according to size1 ++ */ ++int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); ++int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); ++int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); ++int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); ++ ++int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, ++ int timeout); ++ ++/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ ++#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) ++/* ISP state transition timeout, in ms */ ++#define M5MOLS_MODE_CHANGE_TIMEOUT 200 ++#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 ++ ++/* ++ * Mode operation of the M-5MOLS ++ * ++ * Changing the mode of the M-5MOLS is needed right executing order. ++ * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed ++ * by user. There are various categories associated with each mode. ++ * ++ * +============================================================+ ++ * | mode | category | ++ * +============================================================+ ++ * | FLASH | FLASH(only after Stand-by or Power-on) | ++ * | SYSTEM | SYSTEM(only after sensor arm-booting) | ++ * | PARAMETER | PARAMETER | ++ * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | ++ * | CAPTURE | Single CAPTURE, Preview(recording) | ++ * +============================================================+ ++ * ++ * The available executing order between each modes are as follows: ++ * PARAMETER <---> MONITOR <---> CAPTURE ++ */ ++int m5mols_set_mode(struct m5mols_info *info, u8 mode); ++ ++int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); ++int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); ++int m5mols_restore_controls(struct m5mols_info *info); ++int m5mols_start_capture(struct m5mols_info *info); ++int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); ++int m5mols_lock_3a(struct m5mols_info *info, bool lock); ++int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); ++int m5mols_init_controls(struct v4l2_subdev *sd); ++ ++/* The firmware function */ ++int m5mols_update_fw(struct v4l2_subdev *sd, ++ int (*set_power)(struct m5mols_info *, bool)); ++ ++static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) ++{ ++ return container_of(subdev, struct m5mols_info, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ struct m5mols_info *info = container_of(ctrl->handler, ++ struct m5mols_info, handle); ++ return &info->sd; ++} ++ ++static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, ++ unsigned int mode) ++{ ++ ctrl->priv = (void *)(uintptr_t)mode; ++} ++ ++static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) ++{ ++ return (unsigned int)(uintptr_t)ctrl->priv; ++} ++ ++#endif /* M5MOLS_H */ +diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c +new file mode 100644 +index 0000000..ab34cce +--- /dev/null ++++ b/drivers/media/i2c/m5mols/m5mols_capture.c +@@ -0,0 +1,158 @@ ++ ++/* ++ * The Capture code for Fujitsu M-5MOLS ISP ++ * ++ * Copyright (C) 2011 Samsung Electronics Co., Ltd. ++ * Author: HeungJun Kim ++ * ++ * Copyright (C) 2009 Samsung Electronics Co., Ltd. ++ * Author: Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "m5mols.h" ++#include "m5mols_reg.h" ++ ++/** ++ * m5mols_read_rational - I2C read of a rational number ++ * ++ * Read numerator and denominator from registers @addr_num and @addr_den ++ * respectively and return the division result in @val. ++ */ ++static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, ++ u32 addr_den, u32 *val) ++{ ++ u32 num, den; ++ ++ int ret = m5mols_read_u32(sd, addr_num, &num); ++ if (!ret) ++ ret = m5mols_read_u32(sd, addr_den, &den); ++ if (ret) ++ return ret; ++ *val = den == 0 ? 0 : num / den; ++ return ret; ++} ++ ++/** ++ * m5mols_capture_info - Gather captured image information ++ * ++ * For now it gathers only EXIF information and file size. ++ */ ++static int m5mols_capture_info(struct m5mols_info *info) ++{ ++ struct m5mols_exif *exif = &info->cap.exif; ++ struct v4l2_subdev *sd = &info->sd; ++ int ret; ++ ++ ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, ++ EXIF_INFO_EXPTIME_DE, &exif->exposure_time); ++ if (ret) ++ return ret; ++ ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, ++ &exif->shutter_speed); ++ if (ret) ++ return ret; ++ ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, ++ &exif->aperture); ++ if (ret) ++ return ret; ++ ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, ++ &exif->brightness); ++ if (ret) ++ return ret; ++ ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, ++ &exif->exposure_bias); ++ if (ret) ++ return ret; ++ ++ ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); ++ if (!ret) ++ ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); ++ if (!ret) ++ ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); ++ if (!ret) ++ ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); ++ if (ret) ++ return ret; ++ ++ if (!ret) ++ ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); ++ if (!ret) ++ ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); ++ if (!ret) ++ info->cap.total = info->cap.main + info->cap.thumb; ++ ++ return ret; ++} ++ ++int m5mols_start_capture(struct m5mols_info *info) ++{ ++ unsigned int framesize = info->cap.buf_size - M5MOLS_JPEG_TAGS_SIZE; ++ struct v4l2_subdev *sd = &info->sd; ++ int ret; ++ ++ /* ++ * Synchronize the controls, set the capture frame resolution and color ++ * format. The frame capture is initiated during switching from Monitor ++ * to Capture mode. ++ */ ++ ret = m5mols_set_mode(info, REG_MONITOR); ++ if (!ret) ++ ret = m5mols_restore_controls(info); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_JPEG_SIZE_MAX, framesize); ++ if (!ret) ++ ret = m5mols_set_mode(info, REG_CAPTURE); ++ if (!ret) ++ /* Wait until a frame is captured to ISP internal memory */ ++ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); ++ if (ret) ++ return ret; ++ ++ /* ++ * Initiate the captured data transfer to a MIPI-CSI receiver. ++ */ ++ ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); ++ if (!ret) ++ ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); ++ if (!ret) { ++ bool captured = false; ++ unsigned int size; ++ ++ /* Wait for the capture completion interrupt */ ++ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); ++ if (!ret) { ++ captured = true; ++ ret = m5mols_capture_info(info); ++ } ++ size = captured ? info->cap.main : 0; ++ v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", ++ __func__, size, info->cap.thumb); ++ ++ v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); ++ } ++ ++ return ret; ++} +diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c +new file mode 100644 +index 0000000..f34429e +--- /dev/null ++++ b/drivers/media/i2c/m5mols/m5mols_controls.c +@@ -0,0 +1,628 @@ ++/* ++ * Controls for M-5MOLS 8M Pixel camera sensor with ISP ++ * ++ * Copyright (C) 2011 Samsung Electronics Co., Ltd. ++ * Author: HeungJun Kim ++ * ++ * Copyright (C) 2009 Samsung Electronics Co., Ltd. ++ * Author: Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "m5mols.h" ++#include "m5mols_reg.h" ++ ++static struct m5mols_scenemode m5mols_default_scenemode[] = { ++ [REG_SCENE_NORMAL] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_PORTRAIT] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 4, ++ REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_LANDSCAPE] = { ++ REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 4, REG_EDGE_ON, 6, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_SPORTS] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_PARTY_INDOOR] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 4, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_BEACH_SNOW] = { ++ REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 4, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_SUNSET] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, ++ REG_AWB_DAYLIGHT, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_DAWN_DUSK] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, ++ REG_AWB_FLUORESCENT_1, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_FALL] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 5, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_NIGHT] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_AGAINST_LIGHT] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_FIRE] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++ [REG_SCENE_TEXT] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 7, ++ REG_AF_MACRO, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, ++ }, ++ [REG_SCENE_CANDLE] = { ++ REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, ++ REG_CHROMA_ON, 3, REG_EDGE_ON, 5, ++ REG_AF_NORMAL, REG_FD_OFF, ++ REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, ++ 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, ++ }, ++}; ++ ++/** ++ * m5mols_do_scenemode() - Change current scenemode ++ * @mode: Desired mode of the scenemode ++ * ++ * WARNING: The execution order is important. Do not change the order. ++ */ ++int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; ++ int ret; ++ ++ if (mode > REG_SCENE_CANDLE) ++ return -EINVAL; ++ ++ ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); ++ if (!ret) ++ ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); ++ if (!ret) ++ ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); ++ if (!ret) ++ ret = m5mols_write(sd, AE_MODE, scenemode.metering); ++ if (!ret) ++ ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); ++ if (!ret) ++ ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); ++ if (!ret) ++ ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); ++ if (!ret) ++ ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); ++ if (!ret) ++ ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); ++ if (!ret) ++ ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); ++ if (!ret) ++ ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); ++ if (!ret && is_available_af(info)) ++ ret = m5mols_write(sd, AF_MODE, scenemode.af_range); ++ if (!ret && is_available_af(info)) ++ ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); ++ if (!ret) ++ ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); ++ if (!ret) ++ ret = m5mols_write(sd, AE_ISO, scenemode.iso); ++ if (!ret) ++ ret = m5mols_set_mode(info, REG_CAPTURE); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); ++ if (!ret) ++ ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); ++ if (!ret) ++ ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); ++ if (!ret) ++ ret = m5mols_set_mode(info, REG_MONITOR); ++ ++ return ret; ++} ++ ++static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) ++{ ++ bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; ++ int ret = 0; ++ ++ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { ++ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; ++ ++ ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? ++ REG_AE_LOCK : REG_AE_UNLOCK); ++ if (ret) ++ return ret; ++ } ++ ++ if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) ++ && info->auto_wb->val) { ++ bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; ++ ++ ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? ++ REG_AWB_LOCK : REG_AWB_UNLOCK); ++ if (ret) ++ return ret; ++ } ++ ++ if (!info->ver.af || !af_lock) ++ return ret; ++ ++ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) ++ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); ++ ++ return ret; ++} ++ ++static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) ++{ ++ unsigned int metering; ++ ++ switch (mode) { ++ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: ++ metering = REG_AE_CENTER; ++ break; ++ case V4L2_EXPOSURE_METERING_SPOT: ++ metering = REG_AE_SPOT; ++ break; ++ default: ++ metering = REG_AE_ALL; ++ break; ++ } ++ ++ return m5mols_write(&info->sd, AE_MODE, metering); ++} ++ ++static int m5mols_set_exposure(struct m5mols_info *info, int exposure) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ int ret = 0; ++ ++ if (exposure == V4L2_EXPOSURE_AUTO) { ++ /* Unlock auto exposure */ ++ info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; ++ m5mols_3a_lock(info, info->lock_3a); ++ ++ ret = m5mols_set_metering_mode(info, info->metering->val); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_dbg(1, m5mols_debug, sd, ++ "%s: exposure bias: %#x, metering: %#x\n", ++ __func__, info->exposure_bias->val, ++ info->metering->val); ++ ++ return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); ++ } ++ ++ if (exposure == V4L2_EXPOSURE_MANUAL) { ++ ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); ++ if (ret == 0) ++ ret = m5mols_write(sd, AE_MAN_GAIN_MON, ++ info->exposure->val); ++ if (ret == 0) ++ ret = m5mols_write(sd, AE_MAN_GAIN_CAP, ++ info->exposure->val); ++ ++ v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", ++ __func__, info->exposure->val); ++ } ++ ++ return ret; ++} ++ ++static int m5mols_set_white_balance(struct m5mols_info *info, int val) ++{ ++ static const unsigned short wb[][2] = { ++ { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, ++ { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, ++ { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, ++ { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, ++ { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, ++ { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, ++ { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, ++ { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, ++ { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, ++ }; ++ int i; ++ struct v4l2_subdev *sd = &info->sd; ++ int ret = -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(wb); i++) { ++ int awb; ++ if (wb[i][0] != val) ++ continue; ++ ++ v4l2_dbg(1, m5mols_debug, sd, ++ "Setting white balance to: %#x\n", wb[i][0]); ++ ++ awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; ++ ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : ++ REG_AWB_PRESET); ++ if (ret < 0) ++ return ret; ++ ++ if (!awb) ++ ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); ++ } ++ ++ return ret; ++} ++ ++static int m5mols_set_saturation(struct m5mols_info *info, int val) ++{ ++ int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); ++ if (ret < 0) ++ return ret; ++ ++ return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); ++} ++ ++static int m5mols_set_color_effect(struct m5mols_info *info, int val) ++{ ++ unsigned int m_effect = REG_COLOR_EFFECT_OFF; ++ unsigned int p_effect = REG_EFFECT_OFF; ++ unsigned int cfix_r = 0, cfix_b = 0; ++ struct v4l2_subdev *sd = &info->sd; ++ int ret = 0; ++ ++ switch (val) { ++ case V4L2_COLORFX_BW: ++ m_effect = REG_COLOR_EFFECT_ON; ++ break; ++ case V4L2_COLORFX_NEGATIVE: ++ p_effect = REG_EFFECT_NEGA; ++ break; ++ case V4L2_COLORFX_EMBOSS: ++ p_effect = REG_EFFECT_EMBOSS; ++ break; ++ case V4L2_COLORFX_SEPIA: ++ m_effect = REG_COLOR_EFFECT_ON; ++ cfix_r = REG_CFIXR_SEPIA; ++ cfix_b = REG_CFIXB_SEPIA; ++ break; ++ } ++ ++ ret = m5mols_write(sd, PARM_EFFECT, p_effect); ++ if (!ret) ++ ret = m5mols_write(sd, MON_EFFECT, m_effect); ++ ++ if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { ++ ret = m5mols_write(sd, MON_CFIXR, cfix_r); ++ if (!ret) ++ ret = m5mols_write(sd, MON_CFIXB, cfix_b); ++ } ++ ++ v4l2_dbg(1, m5mols_debug, sd, ++ "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", ++ p_effect, m_effect, cfix_r, cfix_b, ret); ++ ++ return ret; ++} ++ ++static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) ++{ ++ u32 iso = auto_iso ? 0 : info->iso->val + 1; ++ ++ return m5mols_write(&info->sd, AE_ISO, iso); ++} ++ ++static int m5mols_set_wdr(struct m5mols_info *info, int wdr) ++{ ++ int ret; ++ ++ ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); ++ if (ret < 0) ++ return ret; ++ ++ ret = m5mols_set_mode(info, REG_CAPTURE); ++ if (ret < 0) ++ return ret; ++ ++ return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); ++} ++ ++static int m5mols_set_stabilization(struct m5mols_info *info, int val) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ unsigned int evp = val ? 0xe : 0x0; ++ int ret; ++ ++ ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); ++ if (ret < 0) ++ return ret; ++ ++ return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); ++} ++ ++static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct m5mols_info *info = to_m5mols(sd); ++ int ret = 0; ++ u8 status; ++ ++ v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", ++ __func__, ctrl->name, info->isp_ready); ++ ++ if (!info->isp_ready) ++ return -EBUSY; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ISO_SENSITIVITY_AUTO: ++ ret = m5mols_read_u8(sd, AE_ISO, &status); ++ if (ret == 0) ++ ctrl->val = !status; ++ if (status != REG_ISO_AUTO) ++ info->iso->val = status - 1; ++ break; ++ ++ case V4L2_CID_3A_LOCK: ++ ctrl->val &= ~0x7; ++ ++ ret = m5mols_read_u8(sd, AE_LOCK, &status); ++ if (ret) ++ return ret; ++ if (status) ++ info->lock_3a->val |= V4L2_LOCK_EXPOSURE; ++ ++ ret = m5mols_read_u8(sd, AWB_LOCK, &status); ++ if (ret) ++ return ret; ++ if (status) ++ info->lock_3a->val |= V4L2_LOCK_EXPOSURE; ++ ++ ret = m5mols_read_u8(sd, AF_EXECUTE, &status); ++ if (!status) ++ info->lock_3a->val |= V4L2_LOCK_EXPOSURE; ++ break; ++ } ++ ++ return ret; ++} ++ ++static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct m5mols_info *info = to_m5mols(sd); ++ int last_mode = info->mode; ++ int ret = 0; ++ ++ /* ++ * If needed, defer restoring the controls until ++ * the device is fully initialized. ++ */ ++ if (!info->isp_ready) { ++ info->ctrl_sync = 0; ++ return 0; ++ } ++ ++ v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %p\n", ++ __func__, ctrl->name, ctrl->val, ctrl->priv); ++ ++ if (ctrl_mode && ctrl_mode != info->mode) { ++ ret = m5mols_set_mode(info, ctrl_mode); ++ if (ret < 0) ++ return ret; ++ } ++ ++ switch (ctrl->id) { ++ case V4L2_CID_3A_LOCK: ++ ret = m5mols_3a_lock(info, ctrl); ++ break; ++ ++ case V4L2_CID_ZOOM_ABSOLUTE: ++ ret = m5mols_write(sd, MON_ZOOM, ctrl->val); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ ret = m5mols_set_exposure(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_ISO_SENSITIVITY: ++ ret = m5mols_set_iso(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: ++ ret = m5mols_set_white_balance(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ ret = m5mols_set_saturation(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_COLORFX: ++ ret = m5mols_set_color_effect(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_WIDE_DYNAMIC_RANGE: ++ ret = m5mols_set_wdr(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_IMAGE_STABILIZATION: ++ ret = m5mols_set_stabilization(info, ctrl->val); ++ break; ++ ++ case V4L2_CID_JPEG_COMPRESSION_QUALITY: ++ ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); ++ break; ++ } ++ ++ if (ret == 0 && info->mode != last_mode) ++ ret = m5mols_set_mode(info, last_mode); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { ++ .g_volatile_ctrl = m5mols_g_volatile_ctrl, ++ .s_ctrl = m5mols_s_ctrl, ++}; ++ ++/* Supported manual ISO values */ ++static const s64 iso_qmenu[] = { ++ /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ ++ 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 ++}; ++ ++/* Supported Exposure Bias values, -2.0EV...+2.0EV */ ++static const s64 ev_bias_qmenu[] = { ++ /* AE_INDEX: 0x00...0x08 */ ++ -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 ++}; ++ ++int m5mols_init_controls(struct v4l2_subdev *sd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ u16 exposure_max; ++ u16 zoom_step; ++ int ret; ++ ++ /* Determine the firmware dependant control range and step values */ ++ ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); ++ if (ret < 0) ++ return ret; ++ ++ zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; ++ v4l2_ctrl_handler_init(&info->handle, 20); ++ ++ info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, ++ &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); ++ ++ /* Exposure control cluster */ ++ info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, ++ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, ++ 1, ~0x03, V4L2_EXPOSURE_AUTO); ++ ++ info->exposure = v4l2_ctrl_new_std(&info->handle, ++ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, ++ 0, exposure_max, 1, exposure_max / 2); ++ ++ info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, ++ &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, ++ ARRAY_SIZE(ev_bias_qmenu) - 1, ++ ARRAY_SIZE(ev_bias_qmenu)/2 - 1, ++ ev_bias_qmenu); ++ ++ info->metering = v4l2_ctrl_new_std_menu(&info->handle, ++ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, ++ 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); ++ ++ /* ISO control cluster */ ++ info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); ++ ++ info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, ++ ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); ++ ++ info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_SATURATION, 1, 5, 1, 3); ++ ++ info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); ++ ++ info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); ++ ++ info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); ++ ++ info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); ++ ++ info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); ++ ++ info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, ++ V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); ++ ++ if (info->handle.error) { ++ int ret = info->handle.error; ++ v4l2_err(sd, "Failed to initialize controls: %d\n", ret); ++ v4l2_ctrl_handler_free(&info->handle); ++ return ret; ++ } ++ ++ v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); ++ info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | ++ V4L2_CTRL_FLAG_UPDATE; ++ v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); ++ ++ info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; ++ ++ m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); ++ m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); ++ m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); ++ ++ sd->ctrl_handler = &info->handle; ++ ++ return 0; ++} +diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c +new file mode 100644 +index 0000000..8131d65 +--- /dev/null ++++ b/drivers/media/i2c/m5mols/m5mols_core.c +@@ -0,0 +1,1057 @@ ++/* ++ * Driver for M-5MOLS 8M Pixel camera sensor with ISP ++ * ++ * Copyright (C) 2011 Samsung Electronics Co., Ltd. ++ * Author: HeungJun Kim ++ * ++ * Copyright (C) 2009 Samsung Electronics Co., Ltd. ++ * Author: Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "m5mols.h" ++#include "m5mols_reg.h" ++ ++int m5mols_debug; ++module_param(m5mols_debug, int, 0644); ++ ++#define MODULE_NAME "M5MOLS" ++#define M5MOLS_I2C_CHECK_RETRY 500 ++ ++/* The regulator consumer names for external voltage regulators */ ++static struct regulator_bulk_data supplies[] = { ++ { ++ .supply = "core", /* ARM core power, 1.2V */ ++ }, { ++ .supply = "dig_18", /* digital power 1, 1.8V */ ++ }, { ++ .supply = "d_sensor", /* sensor power 1, 1.8V */ ++ }, { ++ .supply = "dig_28", /* digital power 2, 2.8V */ ++ }, { ++ .supply = "a_sensor", /* analog power */ ++ }, { ++ .supply = "dig_12", /* digital power 3, 1.2V */ ++ }, ++}; ++ ++static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { ++ [M5MOLS_RESTYPE_MONITOR] = { ++ .width = 1920, ++ .height = 1080, ++ .code = V4L2_MBUS_FMT_VYUY8_2X8, ++ .field = V4L2_FIELD_NONE, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ [M5MOLS_RESTYPE_CAPTURE] = { ++ .width = 1920, ++ .height = 1080, ++ .code = V4L2_MBUS_FMT_JPEG_1X8, ++ .field = V4L2_FIELD_NONE, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++}; ++#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) ++ ++static const struct m5mols_resolution m5mols_reg_res[] = { ++ { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ ++ { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ ++ { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ ++ { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, ++ { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ ++ { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ ++ { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ ++ { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ ++ { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ ++ { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, ++ { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ ++ { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ ++ { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, ++ { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ ++ { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ ++ { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ ++ { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ ++ { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ ++ { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ ++ ++ { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ ++ { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ ++ { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, ++ { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ ++ { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ ++ { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ ++ { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ ++ { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ ++ { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ ++ { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ ++ { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ ++ { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, ++ { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ ++ { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, ++ { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ ++ { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ ++ { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, ++ { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ ++}; ++ ++/** ++ * m5mols_swap_byte - an byte array to integer conversion function ++ * @size: size in bytes of I2C packet defined in the M-5MOLS datasheet ++ * ++ * Convert I2C data byte array with performing any required byte ++ * reordering to assure proper values for each data type, regardless ++ * of the architecture endianness. ++ */ ++static u32 m5mols_swap_byte(u8 *data, u8 length) ++{ ++ if (length == 1) ++ return *data; ++ else if (length == 2) ++ return be16_to_cpu(*((u16 *)data)); ++ else ++ return be32_to_cpu(*((u32 *)data)); ++} ++ ++/** ++ * m5mols_read - I2C read function ++ * @reg: combination of size, category and command for the I2C packet ++ * @size: desired size of I2C packet ++ * @val: read value ++ * ++ * Returns 0 on success, or else negative errno. ++ */ ++static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct m5mols_info *info = to_m5mols(sd); ++ u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; ++ u8 category = I2C_CATEGORY(reg); ++ u8 cmd = I2C_COMMAND(reg); ++ struct i2c_msg msg[2]; ++ u8 wbuf[5]; ++ int ret; ++ ++ if (!client->adapter) ++ return -ENODEV; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 5; ++ msg[0].buf = wbuf; ++ wbuf[0] = 5; ++ wbuf[1] = M5MOLS_BYTE_READ; ++ wbuf[2] = category; ++ wbuf[3] = cmd; ++ wbuf[4] = size; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = size + 1; ++ msg[1].buf = rbuf; ++ ++ /* minimum stabilization time */ ++ usleep_range(200, 200); ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ ++ if (ret == 2) { ++ *val = m5mols_swap_byte(&rbuf[1], size); ++ return 0; ++ } ++ ++ if (info->isp_ready) ++ v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", ++ size, category, cmd, ret); ++ ++ return ret < 0 ? ret : -EIO; ++} ++ ++int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) ++{ ++ u32 val_32; ++ int ret; ++ ++ if (I2C_SIZE(reg) != 1) { ++ v4l2_err(sd, "Wrong data size\n"); ++ return -EINVAL; ++ } ++ ++ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); ++ if (ret) ++ return ret; ++ ++ *val = (u8)val_32; ++ return ret; ++} ++ ++int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) ++{ ++ u32 val_32; ++ int ret; ++ ++ if (I2C_SIZE(reg) != 2) { ++ v4l2_err(sd, "Wrong data size\n"); ++ return -EINVAL; ++ } ++ ++ ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); ++ if (ret) ++ return ret; ++ ++ *val = (u16)val_32; ++ return ret; ++} ++ ++int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) ++{ ++ if (I2C_SIZE(reg) != 4) { ++ v4l2_err(sd, "Wrong data size\n"); ++ return -EINVAL; ++ } ++ ++ return m5mols_read(sd, I2C_SIZE(reg), reg, val); ++} ++ ++/** ++ * m5mols_write - I2C command write function ++ * @reg: combination of size, category and command for the I2C packet ++ * @val: value to write ++ * ++ * Returns 0 on success, or else negative errno. ++ */ ++int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct m5mols_info *info = to_m5mols(sd); ++ u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; ++ u8 category = I2C_CATEGORY(reg); ++ u8 cmd = I2C_COMMAND(reg); ++ u8 size = I2C_SIZE(reg); ++ u32 *buf = (u32 *)&wbuf[4]; ++ struct i2c_msg msg[1]; ++ int ret; ++ ++ if (!client->adapter) ++ return -ENODEV; ++ ++ if (size != 1 && size != 2 && size != 4) { ++ v4l2_err(sd, "Wrong data size\n"); ++ return -EINVAL; ++ } ++ ++ msg->addr = client->addr; ++ msg->flags = 0; ++ msg->len = (u16)size + 4; ++ msg->buf = wbuf; ++ wbuf[0] = size + 4; ++ wbuf[1] = M5MOLS_BYTE_WRITE; ++ wbuf[2] = category; ++ wbuf[3] = cmd; ++ ++ *buf = m5mols_swap_byte((u8 *)&val, size); ++ ++ usleep_range(200, 200); ++ ++ ret = i2c_transfer(client->adapter, msg, 1); ++ if (ret == 1) ++ return 0; ++ ++ if (info->isp_ready) ++ v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", ++ category, cmd, ret); ++ ++ return ret < 0 ? ret : -EIO; ++} ++ ++/** ++ * m5mols_busy_wait - Busy waiting with I2C register polling ++ * @reg: the I2C_REG() address of an 8-bit status register to check ++ * @value: expected status register value ++ * @mask: bit mask for the read status register value ++ * @timeout: timeout in miliseconds, or -1 for default timeout ++ * ++ * The @reg register value is ORed with @mask before comparing with @value. ++ * ++ * Return: 0 if the requested condition became true within less than ++ * @timeout ms, or else negative errno. ++ */ ++int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, ++ int timeout) ++{ ++ int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; ++ unsigned long end = jiffies + msecs_to_jiffies(ms); ++ u8 status; ++ ++ do { ++ int ret = m5mols_read_u8(sd, reg, &status); ++ ++ if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) ++ return ret; ++ if (!ret && (status & mask & 0xff) == (value & 0xff)) ++ return 0; ++ usleep_range(100, 250); ++ } while (ms > 0 && time_is_after_jiffies(end)); ++ ++ return -EBUSY; ++} ++ ++/** ++ * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts ++ * ++ * Before writing desired interrupt value the INT_FACTOR register should ++ * be read to clear pending interrupts. ++ */ ++int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ u8 mask = is_available_af(info) ? REG_INT_AF : 0; ++ u8 dummy; ++ int ret; ++ ++ ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); ++ if (!ret) ++ ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); ++ return ret; ++} ++ ++int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ ++ int ret = wait_event_interruptible_timeout(info->irq_waitq, ++ atomic_add_unless(&info->irq_done, -1, 0), ++ msecs_to_jiffies(timeout)); ++ if (ret <= 0) ++ return ret ? ret : -ETIMEDOUT; ++ ++ return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, ++ M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); ++} ++ ++/** ++ * m5mols_reg_mode - Write the mode and check busy status ++ * ++ * It always accompanies a little delay changing the M-5MOLS mode, so it is ++ * needed checking current busy status to guarantee right mode. ++ */ ++static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) ++{ ++ int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); ++ if (ret < 0) ++ return ret; ++ return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, ++ M5MOLS_MODE_CHANGE_TIMEOUT); ++} ++ ++/** ++ * m5mols_set_mode - set the M-5MOLS controller mode ++ * @mode: the required operation mode ++ * ++ * The commands of M-5MOLS are grouped into specific modes. Each functionality ++ * can be guaranteed only when the sensor is operating in mode which a command ++ * belongs to. ++ */ ++int m5mols_set_mode(struct m5mols_info *info, u8 mode) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ int ret = -EINVAL; ++ u8 reg; ++ ++ if (mode < REG_PARAMETER || mode > REG_CAPTURE) ++ return ret; ++ ++ ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); ++ if (ret || reg == mode) ++ return ret; ++ ++ switch (reg) { ++ case REG_PARAMETER: ++ ret = m5mols_reg_mode(sd, REG_MONITOR); ++ if (mode == REG_MONITOR) ++ break; ++ if (!ret) ++ ret = m5mols_reg_mode(sd, REG_CAPTURE); ++ break; ++ ++ case REG_MONITOR: ++ if (mode == REG_PARAMETER) { ++ ret = m5mols_reg_mode(sd, REG_PARAMETER); ++ break; ++ } ++ ++ ret = m5mols_reg_mode(sd, REG_CAPTURE); ++ break; ++ ++ case REG_CAPTURE: ++ ret = m5mols_reg_mode(sd, REG_MONITOR); ++ if (mode == REG_MONITOR) ++ break; ++ if (!ret) ++ ret = m5mols_reg_mode(sd, REG_PARAMETER); ++ break; ++ ++ default: ++ v4l2_warn(sd, "Wrong mode: %d\n", mode); ++ } ++ ++ if (!ret) ++ info->mode = mode; ++ ++ return ret; ++} ++ ++/** ++ * m5mols_get_version - retrieve full revisions information of M-5MOLS ++ * ++ * The version information includes revisions of hardware and firmware, ++ * AutoFocus alghorithm version and the version string. ++ */ ++static int m5mols_get_version(struct v4l2_subdev *sd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ struct m5mols_version *ver = &info->ver; ++ u8 *str = ver->str; ++ int i; ++ int ret; ++ ++ ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); ++ if (!ret) ++ ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); ++ if (!ret) ++ ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); ++ if (!ret) ++ ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); ++ if (!ret) ++ ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); ++ if (!ret) ++ ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); ++ if (!ret) ++ ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < VERSION_STRING_SIZE; i++) { ++ ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); ++ if (ret) ++ return ret; ++ } ++ ++ ver->fw = be16_to_cpu(ver->fw); ++ ver->hw = be16_to_cpu(ver->hw); ++ ver->param = be16_to_cpu(ver->param); ++ ver->awb = be16_to_cpu(ver->awb); ++ ++ v4l2_info(sd, "Manufacturer\t[%s]\n", ++ is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? ++ "Samsung Electro-Machanics" : ++ is_manufacturer(info, REG_SAMSUNG_OPTICS) ? ++ "Samsung Fiber-Optics" : ++ is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? ++ "Samsung Techwin" : "None"); ++ v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", ++ info->ver.customer, info->ver.project); ++ ++ if (!is_available_af(info)) ++ v4l2_info(sd, "No support Auto Focus on this firmware\n"); ++ ++ return ret; ++} ++ ++/** ++ * __find_restype - Lookup M-5MOLS resolution type according to pixel code ++ * @code: pixel code ++ */ ++static enum m5mols_restype __find_restype(enum v4l2_mbus_pixelcode code) ++{ ++ enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; ++ ++ do { ++ if (code == m5mols_default_ffmt[type].code) ++ return type; ++ } while (type++ != SIZE_DEFAULT_FFMT); ++ ++ return 0; ++} ++ ++/** ++ * __find_resolution - Lookup preset and type of M-5MOLS's resolution ++ * @mf: pixel format to find/negotiate the resolution preset for ++ * @type: M-5MOLS resolution type ++ * @resolution: M-5MOLS resolution preset register value ++ * ++ * Find nearest resolution matching resolution preset and adjust mf ++ * to supported values. ++ */ ++static int __find_resolution(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf, ++ enum m5mols_restype *type, ++ u32 *resolution) ++{ ++ const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; ++ const struct m5mols_resolution *match = NULL; ++ enum m5mols_restype stype = __find_restype(mf->code); ++ int i = ARRAY_SIZE(m5mols_reg_res); ++ unsigned int min_err = ~0; ++ ++ while (i--) { ++ int err; ++ if (stype == fsize->type) { ++ err = abs(fsize->width - mf->width) ++ + abs(fsize->height - mf->height); ++ ++ if (err < min_err) { ++ min_err = err; ++ match = fsize; ++ } ++ } ++ fsize++; ++ } ++ if (match) { ++ mf->width = match->width; ++ mf->height = match->height; ++ *resolution = match->reg; ++ *type = stype; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, ++ struct v4l2_subdev_fh *fh, ++ enum v4l2_subdev_format_whence which, ++ enum m5mols_restype type) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_TRY) ++ return fh ? v4l2_subdev_get_try_format(fh, 0) : NULL; ++ ++ return &info->ffmt[type]; ++} ++ ++static int m5mols_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ struct v4l2_mbus_framefmt *format; ++ int ret = 0; ++ ++ mutex_lock(&info->lock); ++ ++ format = __find_format(info, fh, fmt->which, info->res_type); ++ if (!format) ++ fmt->format = *format; ++ else ++ ret = -EINVAL; ++ ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static int m5mols_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ struct v4l2_mbus_framefmt *format = &fmt->format; ++ struct v4l2_mbus_framefmt *sfmt; ++ enum m5mols_restype type; ++ u32 resolution = 0; ++ int ret; ++ ++ ret = __find_resolution(sd, format, &type, &resolution); ++ if (ret < 0) ++ return ret; ++ ++ sfmt = __find_format(info, fh, fmt->which, type); ++ if (!sfmt) ++ return 0; ++ ++ mutex_lock(&info->lock); ++ ++ format->code = m5mols_default_ffmt[type].code; ++ format->colorspace = V4L2_COLORSPACE_JPEG; ++ format->field = V4L2_FIELD_NONE; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ *sfmt = *format; ++ info->resolution = resolution; ++ info->res_type = type; ++ } ++ ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static int m5mols_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, ++ struct v4l2_mbus_frame_desc *fd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ ++ if (pad != 0 || fd == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&info->lock); ++ /* ++ * .get_frame_desc is only used for compressed formats, ++ * thus we always return the capture frame parameters here. ++ */ ++ fd->entry[0].length = info->cap.buf_size; ++ fd->entry[0].pixelcode = info->ffmt[M5MOLS_RESTYPE_CAPTURE].code; ++ mutex_unlock(&info->lock); ++ ++ fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; ++ fd->num_entries = 1; ++ ++ return 0; ++} ++ ++static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, ++ struct v4l2_mbus_frame_desc *fd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ struct v4l2_mbus_framefmt *mf = &info->ffmt[M5MOLS_RESTYPE_CAPTURE]; ++ ++ if (pad != 0 || fd == NULL) ++ return -EINVAL; ++ ++ fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; ++ fd->num_entries = 1; ++ fd->entry[0].length = clamp_t(u32, fd->entry[0].length, ++ mf->width * mf->height, ++ M5MOLS_MAIN_JPEG_SIZE_MAX); ++ mutex_lock(&info->lock); ++ info->cap.buf_size = fd->entry[0].length; ++ mutex_unlock(&info->lock); ++ ++ return 0; ++} ++ ++ ++static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (!code || code->index >= SIZE_DEFAULT_FFMT) ++ return -EINVAL; ++ ++ code->code = m5mols_default_ffmt[code->index].code; ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_pad_ops m5mols_pad_ops = { ++ .enum_mbus_code = m5mols_enum_mbus_code, ++ .get_fmt = m5mols_get_fmt, ++ .set_fmt = m5mols_set_fmt, ++ .get_frame_desc = m5mols_get_frame_desc, ++ .set_frame_desc = m5mols_set_frame_desc, ++}; ++ ++/** ++ * m5mols_restore_controls - Apply current control values to the registers ++ * ++ * m5mols_do_scenemode() handles all parameters for which there is yet no ++ * individual control. It should be replaced at some point by setting each ++ * control individually, in required register set up order. ++ */ ++int m5mols_restore_controls(struct m5mols_info *info) ++{ ++ int ret; ++ ++ if (info->ctrl_sync) ++ return 0; ++ ++ ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); ++ if (ret) ++ return ret; ++ ++ ret = v4l2_ctrl_handler_setup(&info->handle); ++ info->ctrl_sync = !ret; ++ ++ return ret; ++} ++ ++/** ++ * m5mols_start_monitor - Start the monitor mode ++ * ++ * Before applying the controls setup the resolution and frame rate ++ * in PARAMETER mode, and then switch over to MONITOR mode. ++ */ ++static int m5mols_start_monitor(struct m5mols_info *info) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ int ret; ++ ++ ret = m5mols_set_mode(info, REG_PARAMETER); ++ if (!ret) ++ ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); ++ if (!ret) ++ ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); ++ if (!ret) ++ ret = m5mols_set_mode(info, REG_MONITOR); ++ if (!ret) ++ ret = m5mols_restore_controls(info); ++ ++ return ret; ++} ++ ++static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ u32 code; ++ int ret; ++ ++ mutex_lock(&info->lock); ++ code = info->ffmt[info->res_type].code; ++ ++ if (enable) { ++ if (is_code(code, M5MOLS_RESTYPE_MONITOR)) ++ ret = m5mols_start_monitor(info); ++ if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) ++ ret = m5mols_start_capture(info); ++ else ++ ret = -EINVAL; ++ } else { ++ ret = m5mols_set_mode(info, REG_PARAMETER); ++ } ++ ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static const struct v4l2_subdev_video_ops m5mols_video_ops = { ++ .s_stream = m5mols_s_stream, ++}; ++ ++static int m5mols_sensor_power(struct m5mols_info *info, bool enable) ++{ ++ struct v4l2_subdev *sd = &info->sd; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ const struct m5mols_platform_data *pdata = info->pdata; ++ int ret; ++ ++ if (info->power == enable) ++ return 0; ++ ++ if (enable) { ++ if (info->set_power) { ++ ret = info->set_power(&client->dev, 1); ++ if (ret) ++ return ret; ++ } ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); ++ if (ret) { ++ info->set_power(&client->dev, 0); ++ return ret; ++ } ++ ++ gpio_set_value(pdata->gpio_reset, !pdata->reset_polarity); ++ info->power = 1; ++ ++ return ret; ++ } ++ ++ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); ++ if (ret) ++ return ret; ++ ++ if (info->set_power) ++ info->set_power(&client->dev, 0); ++ ++ gpio_set_value(pdata->gpio_reset, pdata->reset_polarity); ++ ++ info->isp_ready = 0; ++ info->power = 0; ++ ++ return ret; ++} ++ ++/* m5mols_update_fw - optional firmware update routine */ ++int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, ++ int (*set_power)(struct m5mols_info *, bool)) ++{ ++ return 0; ++} ++ ++/** ++ * m5mols_fw_start - M-5MOLS internal ARM controller initialization ++ * ++ * Execute the M-5MOLS internal ARM controller initialization sequence. ++ * This function should be called after the supply voltage has been ++ * applied and before any requests to the device are made. ++ */ ++static int m5mols_fw_start(struct v4l2_subdev *sd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ int ret; ++ ++ atomic_set(&info->irq_done, 0); ++ /* Wait until I2C slave is initialized in Flash Writer mode */ ++ ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, ++ M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); ++ if (!ret) ++ ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); ++ if (!ret) ++ ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); ++ if (ret < 0) ++ return ret; ++ ++ info->isp_ready = 1; ++ ++ ret = m5mols_get_version(sd); ++ if (!ret) ++ ret = m5mols_update_fw(sd, m5mols_sensor_power); ++ if (ret) ++ return ret; ++ ++ v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); ++ ++ ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); ++ if (!ret) ++ ret = m5mols_enable_interrupt(sd, ++ REG_INT_AF | REG_INT_CAPTURE); ++ ++ return ret; ++} ++ ++/* Execute the lens soft-landing algorithm */ ++static int m5mols_auto_focus_stop(struct m5mols_info *info) ++{ ++ int ret; ++ ++ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); ++ if (!ret) ++ ret = m5mols_write(&info->sd, AF_MODE, REG_AF_POWEROFF); ++ if (!ret) ++ ret = m5mols_busy_wait(&info->sd, SYSTEM_STATUS, REG_AF_IDLE, ++ 0xff, -1); ++ return ret; ++} ++ ++/** ++ * m5mols_s_power - Main sensor power control function ++ * ++ * To prevent breaking the lens when the sensor is powered off the Soft-Landing ++ * algorithm is called where available. The Soft-Landing algorithm availability ++ * dependends on the firmware provider. ++ */ ++static int m5mols_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ int ret; ++ ++ mutex_lock(&info->lock); ++ ++ if (on) { ++ ret = m5mols_sensor_power(info, true); ++ if (!ret) ++ ret = m5mols_fw_start(sd); ++ } else { ++ if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { ++ ret = m5mols_set_mode(info, REG_MONITOR); ++ if (!ret) ++ ret = m5mols_auto_focus_stop(info); ++ if (ret < 0) ++ v4l2_warn(sd, "Soft landing lens failed\n"); ++ } ++ ret = m5mols_sensor_power(info, false); ++ ++ info->ctrl_sync = 0; ++ } ++ ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static int m5mols_log_status(struct v4l2_subdev *sd) ++{ ++ struct m5mols_info *info = to_m5mols(sd); ++ ++ v4l2_ctrl_handler_log_status(&info->handle, sd->name); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops m5mols_core_ops = { ++ .s_power = m5mols_s_power, ++ .log_status = m5mols_log_status, ++}; ++ ++/* ++ * V4L2 subdev internal operations ++ */ ++static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); ++ ++ *format = m5mols_default_ffmt[0]; ++ return 0; ++} ++ ++static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { ++ .open = m5mols_open, ++}; ++ ++static const struct v4l2_subdev_ops m5mols_ops = { ++ .core = &m5mols_core_ops, ++ .pad = &m5mols_pad_ops, ++ .video = &m5mols_video_ops, ++}; ++ ++static irqreturn_t m5mols_irq_handler(int irq, void *data) ++{ ++ struct m5mols_info *info = to_m5mols(data); ++ ++ atomic_set(&info->irq_done, 1); ++ wake_up_interruptible(&info->irq_waitq); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __devinit m5mols_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ const struct m5mols_platform_data *pdata = client->dev.platform_data; ++ struct m5mols_info *info; ++ struct v4l2_subdev *sd; ++ int ret; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "No platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!gpio_is_valid(pdata->gpio_reset)) { ++ dev_err(&client->dev, "No valid RESET GPIO specified\n"); ++ return -EINVAL; ++ } ++ ++ if (!client->irq) { ++ dev_err(&client->dev, "Interrupt not assigned\n"); ++ return -EINVAL; ++ } ++ ++ info = kzalloc(sizeof(struct m5mols_info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ info->pdata = pdata; ++ info->set_power = pdata->set_power; ++ ++ ret = gpio_request(pdata->gpio_reset, "M5MOLS_NRST"); ++ if (ret) { ++ dev_err(&client->dev, "Failed to request gpio: %d\n", ret); ++ goto out_free; ++ } ++ gpio_direction_output(pdata->gpio_reset, pdata->reset_polarity); ++ ++ ret = regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), supplies); ++ if (ret) { ++ dev_err(&client->dev, "Failed to get regulators: %d\n", ret); ++ goto out_gpio; ++ } ++ ++ sd = &info->sd; ++ v4l2_i2c_subdev_init(sd, client, &m5mols_ops); ++ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ sd->internal_ops = &m5mols_subdev_internal_ops; ++ info->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_init(&sd->entity, 1, &info->pad, 0); ++ if (ret < 0) ++ goto out_reg; ++ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ ++ init_waitqueue_head(&info->irq_waitq); ++ mutex_init(&info->lock); ++ ++ ret = request_irq(client->irq, m5mols_irq_handler, ++ IRQF_TRIGGER_RISING, MODULE_NAME, sd); ++ if (ret) { ++ dev_err(&client->dev, "Interrupt request failed: %d\n", ret); ++ goto out_me; ++ } ++ info->res_type = M5MOLS_RESTYPE_MONITOR; ++ info->ffmt[0] = m5mols_default_ffmt[0]; ++ info->ffmt[1] = m5mols_default_ffmt[1]; ++ ++ ret = m5mols_sensor_power(info, true); ++ if (ret) ++ goto out_irq; ++ ++ ret = m5mols_fw_start(sd); ++ if (!ret) ++ ret = m5mols_init_controls(sd); ++ ++ ret = m5mols_sensor_power(info, false); ++ if (!ret) ++ return 0; ++out_irq: ++ free_irq(client->irq, sd); ++out_me: ++ media_entity_cleanup(&sd->entity); ++out_reg: ++ regulator_bulk_free(ARRAY_SIZE(supplies), supplies); ++out_gpio: ++ gpio_free(pdata->gpio_reset); ++out_free: ++ kfree(info); ++ return ret; ++} ++ ++static int __devexit m5mols_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct m5mols_info *info = to_m5mols(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ free_irq(client->irq, sd); ++ ++ regulator_bulk_free(ARRAY_SIZE(supplies), supplies); ++ gpio_free(info->pdata->gpio_reset); ++ media_entity_cleanup(&sd->entity); ++ kfree(info); ++ return 0; ++} ++ ++static const struct i2c_device_id m5mols_id[] = { ++ { MODULE_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, m5mols_id); ++ ++static struct i2c_driver m5mols_i2c_driver = { ++ .driver = { ++ .name = MODULE_NAME, ++ }, ++ .probe = m5mols_probe, ++ .remove = __devexit_p(m5mols_remove), ++ .id_table = m5mols_id, ++}; ++ ++module_i2c_driver(m5mols_i2c_driver); ++ ++MODULE_AUTHOR("HeungJun Kim "); ++MODULE_AUTHOR("Dongsoo Kim "); ++MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/m5mols/m5mols_reg.h b/drivers/media/i2c/m5mols/m5mols_reg.h +new file mode 100644 +index 0000000..58d8027 +--- /dev/null ++++ b/drivers/media/i2c/m5mols/m5mols_reg.h +@@ -0,0 +1,363 @@ ++/* ++ * Register map for M-5MOLS 8M Pixel camera sensor with ISP ++ * ++ * Copyright (C) 2011 Samsung Electronics Co., Ltd. ++ * Author: HeungJun Kim ++ * ++ * Copyright (C) 2009 Samsung Electronics Co., Ltd. ++ * Author: Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef M5MOLS_REG_H ++#define M5MOLS_REG_H ++ ++#define M5MOLS_I2C_MAX_SIZE 4 ++#define M5MOLS_BYTE_READ 0x01 ++#define M5MOLS_BYTE_WRITE 0x02 ++ ++#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) ++#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) ++#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) ++#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) ++ ++/* ++ * Category section register ++ * ++ * The category means set including relevant command of M-5MOLS. ++ */ ++#define CAT_SYSTEM 0x00 ++#define CAT_PARAM 0x01 ++#define CAT_MONITOR 0x02 ++#define CAT_AE 0x03 ++#define CAT_WB 0x06 ++#define CAT_EXIF 0x07 ++#define CAT_FD 0x09 ++#define CAT_LENS 0x0a ++#define CAT_CAPT_PARM 0x0b ++#define CAT_CAPT_CTRL 0x0c ++#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ ++ ++/* ++ * Category 0 - SYSTEM mode ++ * ++ * The SYSTEM mode in the M-5MOLS means area available to handle with the whole ++ * & all-round system of sensor. It deals with version/interrupt/setting mode & ++ * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by ++ * packaging & manufacturer, even the customer and project code. And the ++ * function details may vary among them. The version information helps to ++ * determine what methods shall be used in the driver. ++ * ++ * There is many registers between customer version address and awb one. For ++ * more specific contents, see definition if file m5mols.h. ++ */ ++#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) ++#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) ++#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) ++#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) ++#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) ++#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) ++ ++#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) ++#define REG_SYSINIT 0x00 /* SYSTEM mode */ ++#define REG_PARAMETER 0x01 /* PARAMETER mode */ ++#define REG_MONITOR 0x02 /* MONITOR mode */ ++#define REG_CAPTURE 0x03 /* CAPTURE mode */ ++ ++#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) ++#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) ++#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ ++#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ ++#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ ++/* SYSTEM mode status */ ++#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) ++ ++/* Interrupt pending register */ ++#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) ++/* interrupt enable register */ ++#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) ++#define REG_INT_MODE (1 << 0) ++#define REG_INT_AF (1 << 1) ++#define REG_INT_ZOOM (1 << 2) ++#define REG_INT_CAPTURE (1 << 3) ++#define REG_INT_FRAMESYNC (1 << 4) ++#define REG_INT_FD (1 << 5) ++#define REG_INT_LENS_INIT (1 << 6) ++#define REG_INT_SOUND (1 << 7) ++#define REG_INT_MASK 0x0f ++ ++/* ++ * category 1 - PARAMETER mode ++ * ++ * This category supports function of camera features of M-5MOLS. It means we ++ * can handle with preview(MONITOR) resolution size/frame per second/interface ++ * between the sensor and the Application Processor/even the image effect. ++ */ ++ ++/* Resolution in the MONITOR mode */ ++#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) ++ ++/* Frame rate */ ++#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) ++#define REG_FPS_30 0x02 ++ ++/* Video bus between the sensor and a host processor */ ++#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) ++#define REG_INTERFACE_MIPI 0x02 ++ ++/* Image effects */ ++#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) ++#define REG_EFFECT_OFF 0x00 ++#define REG_EFFECT_NEGA 0x01 ++#define REG_EFFECT_EMBOSS 0x06 ++#define REG_EFFECT_OUTLINE 0x07 ++#define REG_EFFECT_WATERCOLOR 0x08 ++ ++/* ++ * Category 2 - MONITOR mode ++ * ++ * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another ++ * mode named "Preview", but this preview mode is used at the case specific ++ * vider-recording mode. This mmode supports only YUYV format. On the other ++ * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are ++ * another options like zoom/color effect(different with effect in PARAMETER ++ * mode)/anti hand shaking algorithm. ++ */ ++ ++/* Target digital zoom position */ ++#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) ++ ++/* CR value for color effect */ ++#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) ++/* CB value for color effect */ ++#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) ++#define REG_CFIXB_SEPIA 0xd8 ++#define REG_CFIXR_SEPIA 0x18 ++ ++#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) ++#define REG_COLOR_EFFECT_OFF 0x00 ++#define REG_COLOR_EFFECT_ON 0x01 ++ ++/* Chroma enable */ ++#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) ++/* Chroma level */ ++#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) ++#define REG_CHROMA_OFF 0x00 ++#define REG_CHROMA_ON 0x01 ++ ++/* Sharpness on/off */ ++#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) ++/* Sharpness level */ ++#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) ++#define REG_EDGE_OFF 0x00 ++#define REG_EDGE_ON 0x01 ++ ++/* Set color tone (contrast) */ ++#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) ++ ++/* ++ * Category 3 - Auto Exposure ++ * ++ * The M-5MOLS exposure capbility is detailed as which is similar to digital ++ * camera. This category supports AE locking/various AE mode(range of exposure) ++ * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the ++ * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be ++ * different. So, this category also provide getting the max/min values. And, ++ * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. ++ */ ++ ++/* Auto Exposure locking */ ++#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) ++#define REG_AE_UNLOCK 0x00 ++#define REG_AE_LOCK 0x01 ++ ++/* Auto Exposure algorithm mode */ ++#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) ++#define REG_AE_OFF 0x00 /* AE off */ ++#define REG_AE_ALL 0x01 /* calc AE in all block integral */ ++#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ ++#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ ++ ++#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) ++#define REG_ISO_AUTO 0x00 ++#define REG_ISO_50 0x01 ++#define REG_ISO_100 0x02 ++#define REG_ISO_200 0x03 ++#define REG_ISO_400 0x04 ++#define REG_ISO_800 0x05 ++ ++/* EV (scenemode) preset for MONITOR */ ++#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) ++/* EV (scenemode) preset for CAPTURE */ ++#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) ++#define REG_SCENE_NORMAL 0x00 ++#define REG_SCENE_PORTRAIT 0x01 ++#define REG_SCENE_LANDSCAPE 0x02 ++#define REG_SCENE_SPORTS 0x03 ++#define REG_SCENE_PARTY_INDOOR 0x04 ++#define REG_SCENE_BEACH_SNOW 0x05 ++#define REG_SCENE_SUNSET 0x06 ++#define REG_SCENE_DAWN_DUSK 0x07 ++#define REG_SCENE_FALL 0x08 ++#define REG_SCENE_NIGHT 0x09 ++#define REG_SCENE_AGAINST_LIGHT 0x0a ++#define REG_SCENE_FIRE 0x0b ++#define REG_SCENE_TEXT 0x0c ++#define REG_SCENE_CANDLE 0x0d ++ ++/* Manual gain in MONITOR mode */ ++#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) ++/* Maximum gain in MONITOR mode */ ++#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) ++/* Manual gain in CAPTURE mode */ ++#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) ++ ++#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) ++#define REG_AE_INDEX_20_NEG 0x00 ++#define REG_AE_INDEX_15_NEG 0x01 ++#define REG_AE_INDEX_10_NEG 0x02 ++#define REG_AE_INDEX_05_NEG 0x03 ++#define REG_AE_INDEX_00 0x04 ++#define REG_AE_INDEX_05_POS 0x05 ++#define REG_AE_INDEX_10_POS 0x06 ++#define REG_AE_INDEX_15_POS 0x07 ++#define REG_AE_INDEX_20_POS 0x08 ++ ++/* ++ * Category 6 - White Balance ++ */ ++ ++/* Auto Whitebalance locking */ ++#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) ++#define REG_AWB_UNLOCK 0x00 ++#define REG_AWB_LOCK 0x01 ++ ++#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) ++#define REG_AWB_AUTO 0x01 /* AWB off */ ++#define REG_AWB_PRESET 0x02 /* AWB preset */ ++ ++/* Manual WB (preset) */ ++#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) ++#define REG_AWB_INCANDESCENT 0x01 ++#define REG_AWB_FLUORESCENT_1 0x02 ++#define REG_AWB_FLUORESCENT_2 0x03 ++#define REG_AWB_DAYLIGHT 0x04 ++#define REG_AWB_CLOUDY 0x05 ++#define REG_AWB_SHADE 0x06 ++#define REG_AWB_HORIZON 0x07 ++#define REG_AWB_LEDLIGHT 0x09 ++ ++/* ++ * Category 7 - EXIF information ++ */ ++#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) ++#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) ++#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) ++#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) ++#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) ++#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) ++#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) ++#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) ++#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) ++#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) ++#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) ++#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) ++#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) ++#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) ++ ++/* ++ * Category 9 - Face Detection ++ */ ++#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) ++#define BIT_FD_EN 0 ++#define BIT_FD_DRAW_FACE_FRAME 4 ++#define BIT_FD_DRAW_SMILE_LVL 6 ++#define REG_FD(shift) (1 << shift) ++#define REG_FD_OFF 0x0 ++ ++/* ++ * Category A - Lens Parameter ++ */ ++#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) ++#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ ++#define REG_AF_MACRO 0x01 /* Macro AF, one time */ ++#define REG_AF_POWEROFF 0x07 ++ ++#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) ++#define REG_AF_STOP 0x00 ++#define REG_AF_EXE_AUTO 0x01 ++#define REG_AF_EXE_CAF 0x02 ++ ++#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) ++#define REG_AF_FAIL 0x00 ++#define REG_AF_SUCCESS 0x02 ++#define REG_AF_IDLE 0x04 ++#define REG_AF_BUSY 0x05 ++ ++#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) ++ ++/* ++ * Category B - CAPTURE Parameter ++ */ ++#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) ++#define REG_YUV422 0x00 ++#define REG_BAYER10 0x05 ++#define REG_BAYER8 0x06 ++#define REG_JPEG 0x10 ++ ++#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) ++#define CAPP_JPEG_SIZE_MAX I2C_REG(CAT_CAPT_PARM, 0x0f, 4) ++#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) ++ ++#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) ++#define REG_MCC_OFF 0x00 ++#define REG_MCC_NORMAL 0x01 ++ ++#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) ++#define REG_WDR_OFF 0x00 ++#define REG_WDR_ON 0x01 ++#define REG_WDR_AUTO 0x02 ++ ++#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) ++#define REG_LIGHT_OFF 0x00 ++#define REG_LIGHT_ON 0x01 ++#define REG_LIGHT_AUTO 0x02 ++ ++#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) ++#define REG_FLASH_OFF 0x00 ++#define REG_FLASH_ON 0x01 ++#define REG_FLASH_AUTO 0x02 ++ ++/* ++ * Category C - CAPTURE Control ++ */ ++#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) ++#define REG_CAP_NONE 0x00 ++#define REG_CAP_ANTI_SHAKE 0x02 ++ ++/* Select single- or multi-shot capture */ ++#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) ++ ++#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) ++#define REG_CAP_START_MAIN 0x01 ++#define REG_CAP_START_THUMB 0x03 ++ ++#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) ++#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) ++ ++/* ++ * Category F - Flash ++ * ++ * This mode provides functions about internal flash stuff and system startup. ++ */ ++ ++/* Starts internal ARM core booting after power-up */ ++#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) ++#define REG_START_ARM_BOOT 0x01 /* write value */ ++#define REG_IN_FLASH_MODE 0x00 /* read value */ ++ ++#endif /* M5MOLS_REG_H */ +diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c +new file mode 100644 +index 0000000..766305f +--- /dev/null ++++ b/drivers/media/i2c/msp3400-driver.c +@@ -0,0 +1,927 @@ ++/* ++ * Programming the mspx4xx sound processor family ++ * ++ * (c) 1997-2001 Gerd Knorr ++ * ++ * what works and what doesn't: ++ * ++ * AM-Mono ++ * Support for Hauppauge cards added (decoding handled by tuner) added by ++ * Frederic Crozat ++ * ++ * FM-Mono ++ * should work. The stereo modes are backward compatible to FM-mono, ++ * therefore FM-Mono should be allways available. ++ * ++ * FM-Stereo (B/G, used in germany) ++ * should work, with autodetect ++ * ++ * FM-Stereo (satellite) ++ * should work, no autodetect (i.e. default is mono, but you can ++ * switch to stereo -- untested) ++ * ++ * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) ++ * should work, with autodetect. Support for NICAM was added by ++ * Pekka Pietikainen ++ * ++ * TODO: ++ * - better SAT support ++ * ++ * 980623 Thomas Sailer (sailer@ife.ee.ethz.ch) ++ * using soundcore instead of OSS ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msp3400-driver.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++MODULE_DESCRIPTION("device driver for msp34xx TV sound processor"); ++MODULE_AUTHOR("Gerd Knorr"); ++MODULE_LICENSE("GPL"); ++ ++/* module parameters */ ++static int opmode = OPMODE_AUTO; ++int msp_debug; /* msp_debug output */ ++bool msp_once; /* no continuous stereo monitoring */ ++bool msp_amsound; /* hard-wire AM sound at 6.5 Hz (france), ++ the autoscan seems work well only with FM... */ ++int msp_standard = 1; /* Override auto detect of audio msp_standard, ++ if needed. */ ++bool msp_dolby; ++ ++int msp_stereo_thresh = 0x190; /* a2 threshold for stereo/bilingual ++ (msp34xxg only) 0x00a0-0x03c0 */ ++ ++/* read-only */ ++module_param(opmode, int, 0444); ++ ++/* read-write */ ++module_param_named(once, msp_once, bool, 0644); ++module_param_named(debug, msp_debug, int, 0644); ++module_param_named(stereo_threshold, msp_stereo_thresh, int, 0644); ++module_param_named(standard, msp_standard, int, 0644); ++module_param_named(amsound, msp_amsound, bool, 0644); ++module_param_named(dolby, msp_dolby, bool, 0644); ++ ++MODULE_PARM_DESC(opmode, "Forces a MSP3400 opmode. 0=Manual, 1=Autodetect, 2=Autodetect and autoselect"); ++MODULE_PARM_DESC(once, "No continuous stereo monitoring"); ++MODULE_PARM_DESC(debug, "Enable debug messages [0-3]"); ++MODULE_PARM_DESC(stereo_threshold, "Sets signal threshold to activate stereo"); ++MODULE_PARM_DESC(standard, "Specify audio standard: 32 = NTSC, 64 = radio, Default: Autodetect"); ++MODULE_PARM_DESC(amsound, "Hardwire AM sound at 6.5Hz (France), FM can autoscan"); ++MODULE_PARM_DESC(dolby, "Activates Dolby processing"); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* control subaddress */ ++#define I2C_MSP_CONTROL 0x00 ++/* demodulator unit subaddress */ ++#define I2C_MSP_DEM 0x10 ++/* DSP unit subaddress */ ++#define I2C_MSP_DSP 0x12 ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* functions for talking to the MSP3400C Sound processor */ ++ ++int msp_reset(struct i2c_client *client) ++{ ++ /* reset and read revision code */ ++ static u8 reset_off[3] = { I2C_MSP_CONTROL, 0x80, 0x00 }; ++ static u8 reset_on[3] = { I2C_MSP_CONTROL, 0x00, 0x00 }; ++ static u8 write[3] = { I2C_MSP_DSP + 1, 0x00, 0x1e }; ++ u8 read[2]; ++ struct i2c_msg reset[2] = { ++ { ++ .addr = client->addr, ++ .flags = I2C_M_IGNORE_NAK, ++ .len = 3, ++ .buf = reset_off ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_IGNORE_NAK, ++ .len = 3, ++ .buf = reset_on ++ }, ++ }; ++ struct i2c_msg test[2] = { ++ { ++ .addr = client->addr, ++ .len = 3, ++ .buf = write ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = 2, ++ .buf = read ++ }, ++ }; ++ ++ v4l_dbg(3, msp_debug, client, "msp_reset\n"); ++ if (i2c_transfer(client->adapter, &reset[0], 1) != 1 || ++ i2c_transfer(client->adapter, &reset[1], 1) != 1 || ++ i2c_transfer(client->adapter, test, 2) != 2) { ++ v4l_err(client, "chip reset failed\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static int msp_read(struct i2c_client *client, int dev, int addr) ++{ ++ int err, retval; ++ u8 write[3]; ++ u8 read[2]; ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = client->addr, ++ .len = 3, ++ .buf = write ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = 2, ++ .buf = read ++ } ++ }; ++ ++ write[0] = dev + 1; ++ write[1] = addr >> 8; ++ write[2] = addr & 0xff; ++ ++ for (err = 0; err < 3; err++) { ++ if (i2c_transfer(client->adapter, msgs, 2) == 2) ++ break; ++ v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, ++ dev, addr); ++ schedule_timeout_interruptible(msecs_to_jiffies(10)); ++ } ++ if (err == 3) { ++ v4l_warn(client, "resetting chip, sound will go off.\n"); ++ msp_reset(client); ++ return -1; ++ } ++ retval = read[0] << 8 | read[1]; ++ v4l_dbg(3, msp_debug, client, "msp_read(0x%x, 0x%x): 0x%x\n", ++ dev, addr, retval); ++ return retval; ++} ++ ++int msp_read_dem(struct i2c_client *client, int addr) ++{ ++ return msp_read(client, I2C_MSP_DEM, addr); ++} ++ ++int msp_read_dsp(struct i2c_client *client, int addr) ++{ ++ return msp_read(client, I2C_MSP_DSP, addr); ++} ++ ++static int msp_write(struct i2c_client *client, int dev, int addr, int val) ++{ ++ int err; ++ u8 buffer[5]; ++ ++ buffer[0] = dev; ++ buffer[1] = addr >> 8; ++ buffer[2] = addr & 0xff; ++ buffer[3] = val >> 8; ++ buffer[4] = val & 0xff; ++ ++ v4l_dbg(3, msp_debug, client, "msp_write(0x%x, 0x%x, 0x%x)\n", ++ dev, addr, val); ++ for (err = 0; err < 3; err++) { ++ if (i2c_master_send(client, buffer, 5) == 5) ++ break; ++ v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, ++ dev, addr); ++ schedule_timeout_interruptible(msecs_to_jiffies(10)); ++ } ++ if (err == 3) { ++ v4l_warn(client, "resetting chip, sound will go off.\n"); ++ msp_reset(client); ++ return -1; ++ } ++ return 0; ++} ++ ++int msp_write_dem(struct i2c_client *client, int addr, int val) ++{ ++ return msp_write(client, I2C_MSP_DEM, addr, val); ++} ++ ++int msp_write_dsp(struct i2c_client *client, int addr, int val) ++{ ++ return msp_write(client, I2C_MSP_DSP, addr, val); ++} ++ ++/* ----------------------------------------------------------------------- * ++ * bits 9 8 5 - SCART DSP input Select: ++ * 0 0 0 - SCART 1 to DSP input (reset position) ++ * 0 1 0 - MONO to DSP input ++ * 1 0 0 - SCART 2 to DSP input ++ * 1 1 1 - Mute DSP input ++ * ++ * bits 11 10 6 - SCART 1 Output Select: ++ * 0 0 0 - undefined (reset position) ++ * 0 1 0 - SCART 2 Input to SCART 1 Output (for devices with 2 SCARTS) ++ * 1 0 0 - MONO input to SCART 1 Output ++ * 1 1 0 - SCART 1 DA to SCART 1 Output ++ * 0 0 1 - SCART 2 DA to SCART 1 Output ++ * 0 1 1 - SCART 1 Input to SCART 1 Output ++ * 1 1 1 - Mute SCART 1 Output ++ * ++ * bits 13 12 7 - SCART 2 Output Select (for devices with 2 Output SCART): ++ * 0 0 0 - SCART 1 DA to SCART 2 Output (reset position) ++ * 0 1 0 - SCART 1 Input to SCART 2 Output ++ * 1 0 0 - MONO input to SCART 2 Output ++ * 0 0 1 - SCART 2 DA to SCART 2 Output ++ * 0 1 1 - SCART 2 Input to SCART 2 Output ++ * 1 1 0 - Mute SCART 2 Output ++ * ++ * Bits 4 to 0 should be zero. ++ * ----------------------------------------------------------------------- */ ++ ++static int scarts[3][9] = { ++ /* MASK IN1 IN2 IN3 IN4 IN1_DA IN2_DA MONO MUTE */ ++ /* SCART DSP Input select */ ++ { 0x0320, 0x0000, 0x0200, 0x0300, 0x0020, -1, -1, 0x0100, 0x0320 }, ++ /* SCART1 Output select */ ++ { 0x0c40, 0x0440, 0x0400, 0x0000, 0x0840, 0x0c00, 0x0040, 0x0800, 0x0c40 }, ++ /* SCART2 Output select */ ++ { 0x3080, 0x1000, 0x1080, 0x2080, 0x3080, 0x0000, 0x0080, 0x2000, 0x3000 }, ++}; ++ ++static char *scart_names[] = { ++ "in1", "in2", "in3", "in4", "in1 da", "in2 da", "mono", "mute" ++}; ++ ++void msp_set_scart(struct i2c_client *client, int in, int out) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ state->in_scart = in; ++ ++ if (in >= 0 && in <= 7 && out >= 0 && out <= 2) { ++ if (-1 == scarts[out][in + 1]) ++ return; ++ ++ state->acb &= ~scarts[out][0]; ++ state->acb |= scarts[out][in + 1]; ++ } else ++ state->acb = 0xf60; /* Mute Input and SCART 1 Output */ ++ ++ v4l_dbg(1, msp_debug, client, "scart switch: %s => %d (ACB=0x%04x)\n", ++ scart_names[in], out, state->acb); ++ msp_write_dsp(client, 0x13, state->acb); ++ ++ /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ ++ if (state->has_i2s_conf) ++ msp_write_dem(client, 0x40, state->i2s_mode); ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static void msp_wake_thread(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (NULL == state->kthread) ++ return; ++ state->watch_stereo = 0; ++ state->restart = 1; ++ wake_up_interruptible(&state->wq); ++} ++ ++int msp_sleep(struct msp_state *state, int timeout) ++{ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ add_wait_queue(&state->wq, &wait); ++ if (!kthread_should_stop()) { ++ if (timeout < 0) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ } else { ++ schedule_timeout_interruptible ++ (msecs_to_jiffies(timeout)); ++ } ++ } ++ ++ remove_wait_queue(&state->wq, &wait); ++ try_to_freeze(); ++ return state->restart; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int msp_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct msp_state *state = ctrl_to_state(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(&state->sd); ++ int val = ctrl->val; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_VOLUME: { ++ /* audio volume cluster */ ++ int reallymuted = state->muted->val | state->scan_in_progress; ++ ++ if (!reallymuted) ++ val = (val * 0x7f / 65535) << 8; ++ ++ v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", ++ state->muted->val ? "on" : "off", ++ state->scan_in_progress ? "yes" : "no", ++ state->volume->val); ++ ++ msp_write_dsp(client, 0x0000, val); ++ msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); ++ if (state->has_scart2_out_volume) ++ msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); ++ if (state->has_headphones) ++ msp_write_dsp(client, 0x0006, val); ++ break; ++ } ++ ++ case V4L2_CID_AUDIO_BASS: ++ val = ((val - 32768) * 0x60 / 65535) << 8; ++ msp_write_dsp(client, 0x0002, val); ++ if (state->has_headphones) ++ msp_write_dsp(client, 0x0031, val); ++ break; ++ ++ case V4L2_CID_AUDIO_TREBLE: ++ val = ((val - 32768) * 0x60 / 65535) << 8; ++ msp_write_dsp(client, 0x0003, val); ++ if (state->has_headphones) ++ msp_write_dsp(client, 0x0032, val); ++ break; ++ ++ case V4L2_CID_AUDIO_LOUDNESS: ++ val = val ? ((5 * 4) << 8) : 0; ++ msp_write_dsp(client, 0x0004, val); ++ if (state->has_headphones) ++ msp_write_dsp(client, 0x0033, val); ++ break; ++ ++ case V4L2_CID_AUDIO_BALANCE: ++ val = (u8)((val / 256) - 128); ++ msp_write_dsp(client, 0x0001, val << 8); ++ if (state->has_headphones) ++ msp_write_dsp(client, 0x0030, val << 8); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++void msp_update_volume(struct msp_state *state) ++{ ++ /* Force an update of the volume/mute cluster */ ++ v4l2_ctrl_lock(state->volume); ++ state->volume->val = state->volume->cur.val; ++ state->muted->val = state->muted->cur.val; ++ msp_s_ctrl(state->volume); ++ v4l2_ctrl_unlock(state->volume); ++} ++ ++/* --- v4l2 ioctls --- */ ++static int msp_s_radio(struct v4l2_subdev *sd) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (state->radio) ++ return 0; ++ state->radio = 1; ++ v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); ++ state->watch_stereo = 0; ++ switch (state->opmode) { ++ case OPMODE_MANUAL: ++ /* set msp3400 to FM radio mode */ ++ msp3400c_set_mode(client, MSP_MODE_FM_RADIO); ++ msp3400c_set_carrier(client, MSP_CARRIER(10.7), ++ MSP_CARRIER(10.7)); ++ msp_update_volume(state); ++ break; ++ case OPMODE_AUTODETECT: ++ case OPMODE_AUTOSELECT: ++ /* the thread will do for us */ ++ msp_wake_thread(client); ++ break; ++ } ++ return 0; ++} ++ ++static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* new channel -- kick audio carrier scan */ ++ msp_wake_thread(client); ++ return 0; ++} ++ ++static int msp_querystd(struct v4l2_subdev *sd, v4l2_std_id *id) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ *id &= state->detected_std; ++ ++ v4l_dbg(2, msp_debug, client, ++ "detected standard: %s(0x%08Lx)\n", ++ msp_standard_std_name(state->std), state->detected_std); ++ ++ return 0; ++} ++ ++static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int update = state->radio || state->v4l2_std != id; ++ ++ state->v4l2_std = id; ++ state->radio = 0; ++ if (update) ++ msp_wake_thread(client); ++ return 0; ++} ++ ++static int msp_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int tuner = (input >> 3) & 1; ++ int sc_in = input & 0x7; ++ int sc1_out = output & 0xf; ++ int sc2_out = (output >> 4) & 0xf; ++ u16 val, reg; ++ int i; ++ int extern_input = 1; ++ ++ if (state->route_in == input && state->route_out == output) ++ return 0; ++ state->route_in = input; ++ state->route_out = output; ++ /* check if the tuner input is used */ ++ for (i = 0; i < 5; i++) { ++ if (((input >> (4 + i * 4)) & 0xf) == 0) ++ extern_input = 0; ++ } ++ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; ++ state->rxsubchans = V4L2_TUNER_SUB_STEREO; ++ msp_set_scart(client, sc_in, 0); ++ msp_set_scart(client, sc1_out, 1); ++ msp_set_scart(client, sc2_out, 2); ++ msp_set_audmode(client); ++ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; ++ val = msp_read_dem(client, reg); ++ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); ++ /* wake thread when a new input is chosen */ ++ msp_wake_thread(client); ++ return 0; ++} ++ ++static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (vt->type != V4L2_TUNER_ANALOG_TV) ++ return 0; ++ if (!state->radio) { ++ if (state->opmode == OPMODE_AUTOSELECT) ++ msp_detect_stereo(client); ++ vt->rxsubchans = state->rxsubchans; ++ } ++ vt->audmode = state->audmode; ++ vt->capability |= V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; ++ return 0; ++} ++ ++static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (state->radio) /* TODO: add mono/stereo support for radio */ ++ return 0; ++ if (state->audmode == vt->audmode) ++ return 0; ++ state->audmode = vt->audmode; ++ /* only set audmode */ ++ msp_set_audmode(client); ++ return 0; ++} ++ ++static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); ++ ++ switch (freq) { ++ case 1024000: ++ state->i2s_mode = 0; ++ break; ++ case 2048000: ++ state->i2s_mode = 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, state->ident, ++ (state->rev1 << 16) | state->rev2); ++} ++ ++static int msp_log_status(struct v4l2_subdev *sd) ++{ ++ struct msp_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ const char *p; ++ char prefix[V4L2_SUBDEV_NAME_SIZE + 20]; ++ ++ if (state->opmode == OPMODE_AUTOSELECT) ++ msp_detect_stereo(client); ++ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", ++ client->name, state->rev1, state->rev2); ++ snprintf(prefix, sizeof(prefix), "%s: Audio: ", sd->name); ++ v4l2_ctrl_handler_log_status(&state->hdl, prefix); ++ switch (state->mode) { ++ case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; ++ case MSP_MODE_FM_RADIO: p = "FM Radio"; break; ++ case MSP_MODE_FM_TERRA: p = "Terrestrial FM-mono/stereo"; break; ++ case MSP_MODE_FM_SAT: p = "Satellite FM-mono"; break; ++ case MSP_MODE_FM_NICAM1: p = "NICAM/FM (B/G, D/K)"; break; ++ case MSP_MODE_FM_NICAM2: p = "NICAM/FM (I)"; break; ++ case MSP_MODE_AM_NICAM: p = "NICAM/AM (L)"; break; ++ case MSP_MODE_BTSC: p = "BTSC"; break; ++ case MSP_MODE_EXTERN: p = "External input"; break; ++ default: p = "unknown"; break; ++ } ++ if (state->mode == MSP_MODE_EXTERN) { ++ v4l_info(client, "Mode: %s\n", p); ++ } else if (state->opmode == OPMODE_MANUAL) { ++ v4l_info(client, "Mode: %s (%s%s)\n", p, ++ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", ++ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); ++ } else { ++ if (state->opmode == OPMODE_AUTODETECT) ++ v4l_info(client, "Mode: %s\n", p); ++ v4l_info(client, "Standard: %s (%s%s)\n", ++ msp_standard_std_name(state->std), ++ (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", ++ (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); ++ } ++ v4l_info(client, "Audmode: 0x%04x\n", state->audmode); ++ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", ++ state->route_in, state->route_out); ++ v4l_info(client, "ACB: 0x%04x\n", state->acb); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int msp_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ v4l_dbg(1, msp_debug, client, "suspend\n"); ++ msp_reset(client); ++ return 0; ++} ++ ++static int msp_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ v4l_dbg(1, msp_debug, client, "resume\n"); ++ msp_wake_thread(client); ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops msp_ctrl_ops = { ++ .s_ctrl = msp_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops msp_core_ops = { ++ .log_status = msp_log_status, ++ .g_chip_ident = msp_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = msp_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops msp_video_ops = { ++ .querystd = msp_querystd, ++}; ++ ++static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { ++ .s_frequency = msp_s_frequency, ++ .g_tuner = msp_g_tuner, ++ .s_tuner = msp_s_tuner, ++ .s_radio = msp_s_radio, ++}; ++ ++static const struct v4l2_subdev_audio_ops msp_audio_ops = { ++ .s_routing = msp_s_routing, ++ .s_i2s_clock_freq = msp_s_i2s_clock_freq, ++}; ++ ++static const struct v4l2_subdev_ops msp_ops = { ++ .core = &msp_core_ops, ++ .video = &msp_video_ops, ++ .tuner = &msp_tuner_ops, ++ .audio = &msp_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct msp_state *state; ++ struct v4l2_subdev *sd; ++ struct v4l2_ctrl_handler *hdl; ++ int (*thread_func)(void *data) = NULL; ++ int msp_hard; ++ int msp_family; ++ int msp_revision; ++ int msp_product, msp_prod_hi, msp_prod_lo; ++ int msp_rom; ++ ++ if (!id) ++ strlcpy(client->name, "msp3400", sizeof(client->name)); ++ ++ if (msp_reset(client) == -1) { ++ v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); ++ return -ENODEV; ++ } ++ ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &msp_ops); ++ ++ state->v4l2_std = V4L2_STD_NTSC; ++ state->detected_std = V4L2_STD_ALL; ++ state->audmode = V4L2_TUNER_MODE_STEREO; ++ state->input = -1; ++ state->i2s_mode = 0; ++ init_waitqueue_head(&state->wq); ++ /* These are the reset input/output positions */ ++ state->route_in = MSP_INPUT_DEFAULT; ++ state->route_out = MSP_OUTPUT_DEFAULT; ++ ++ state->rev1 = msp_read_dsp(client, 0x1e); ++ if (state->rev1 != -1) ++ state->rev2 = msp_read_dsp(client, 0x1f); ++ v4l_dbg(1, msp_debug, client, "rev1=0x%04x, rev2=0x%04x\n", ++ state->rev1, state->rev2); ++ if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { ++ v4l_dbg(1, msp_debug, client, ++ "not an msp3400 (cannot read chip version)\n"); ++ kfree(state); ++ return -ENODEV; ++ } ++ ++ msp_family = ((state->rev1 >> 4) & 0x0f) + 3; ++ msp_product = (state->rev2 >> 8) & 0xff; ++ msp_prod_hi = msp_product / 10; ++ msp_prod_lo = msp_product % 10; ++ msp_revision = (state->rev1 & 0x0f) + '@'; ++ msp_hard = ((state->rev1 >> 8) & 0xff) + '@'; ++ msp_rom = state->rev2 & 0x1f; ++ /* Rev B=2, C=3, D=4, G=7 */ ++ state->ident = msp_family * 10000 + 4000 + msp_product * 10 + ++ msp_revision - '@'; ++ ++ /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ ++ state->has_nicam = ++ msp_prod_hi == 1 || msp_prod_hi == 5; ++ /* Has radio support: was added with revision G */ ++ state->has_radio = ++ msp_revision >= 'G'; ++ /* Has headphones output: not for stripped down products */ ++ state->has_headphones = ++ msp_prod_lo < 5; ++ /* Has scart2 input: not in stripped down products of the '3' family */ ++ state->has_scart2 = ++ msp_family >= 4 || msp_prod_lo < 7; ++ /* Has scart3 input: not in stripped down products of the '3' family */ ++ state->has_scart3 = ++ msp_family >= 4 || msp_prod_lo < 5; ++ /* Has scart4 input: not in pre D revisions, not in stripped D revs */ ++ state->has_scart4 = ++ msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); ++ /* Has scart2 output: not in stripped down products of ++ * the '3' family */ ++ state->has_scart2_out = ++ msp_family >= 4 || msp_prod_lo < 5; ++ /* Has scart2 a volume control? Not in pre-D revisions. */ ++ state->has_scart2_out_volume = ++ msp_revision > 'C' && state->has_scart2_out; ++ /* Has a configurable i2s out? */ ++ state->has_i2s_conf = ++ msp_revision >= 'G' && msp_prod_lo < 7; ++ /* Has subwoofer output: not in pre-D revs and not in stripped down ++ * products */ ++ state->has_subwoofer = ++ msp_revision >= 'D' && msp_prod_lo < 5; ++ /* Has soundprocessing (bass/treble/balance/loudness/equalizer): ++ * not in stripped down products */ ++ state->has_sound_processing = ++ msp_prod_lo < 7; ++ /* Has Virtual Dolby Surround: only in msp34x1 */ ++ state->has_virtual_dolby_surround = ++ msp_revision == 'G' && msp_prod_lo == 1; ++ /* Has Virtual Dolby Surround & Dolby Pro Logic: only in msp34x2 */ ++ state->has_dolby_pro_logic = ++ msp_revision == 'G' && msp_prod_lo == 2; ++ /* The msp343xG supports BTSC only and cannot do Automatic Standard ++ * Detection. */ ++ state->force_btsc = ++ msp_family == 3 && msp_revision == 'G' && msp_prod_hi == 3; ++ ++ state->opmode = opmode; ++ if (state->opmode == OPMODE_AUTO) { ++ /* MSP revision G and up have both autodetect and autoselect */ ++ if (msp_revision >= 'G') ++ state->opmode = OPMODE_AUTOSELECT; ++ /* MSP revision D and up have autodetect */ ++ else if (msp_revision >= 'D') ++ state->opmode = OPMODE_AUTODETECT; ++ else ++ state->opmode = OPMODE_MANUAL; ++ } ++ ++ hdl = &state->hdl; ++ v4l2_ctrl_handler_init(hdl, 6); ++ if (state->has_sound_processing) { ++ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_BASS, 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_TREBLE, 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 0); ++ } ++ state->volume = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 58880); ++ v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); ++ state->muted = v4l2_ctrl_new_std(hdl, &msp_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ int err = hdl->error; ++ ++ v4l2_ctrl_handler_free(hdl); ++ kfree(state); ++ return err; ++ } ++ ++ v4l2_ctrl_cluster(2, &state->volume); ++ v4l2_ctrl_handler_setup(hdl); ++ ++ /* hello world :-) */ ++ v4l_info(client, "MSP%d4%02d%c-%c%d found @ 0x%x (%s)\n", ++ msp_family, msp_product, ++ msp_revision, msp_hard, msp_rom, ++ client->addr << 1, client->adapter->name); ++ v4l_info(client, "%s ", client->name); ++ if (state->has_nicam && state->has_radio) ++ printk(KERN_CONT "supports nicam and radio, "); ++ else if (state->has_nicam) ++ printk(KERN_CONT "supports nicam, "); ++ else if (state->has_radio) ++ printk(KERN_CONT "supports radio, "); ++ printk(KERN_CONT "mode is "); ++ ++ /* version-specific initialization */ ++ switch (state->opmode) { ++ case OPMODE_MANUAL: ++ printk(KERN_CONT "manual"); ++ thread_func = msp3400c_thread; ++ break; ++ case OPMODE_AUTODETECT: ++ printk(KERN_CONT "autodetect"); ++ thread_func = msp3410d_thread; ++ break; ++ case OPMODE_AUTOSELECT: ++ printk(KERN_CONT "autodetect and autoselect"); ++ thread_func = msp34xxg_thread; ++ break; ++ } ++ printk(KERN_CONT "\n"); ++ ++ /* startup control thread if needed */ ++ if (thread_func) { ++ state->kthread = kthread_run(thread_func, client, "msp34xx"); ++ ++ if (IS_ERR(state->kthread)) ++ v4l_warn(client, "kernel_thread() failed\n"); ++ msp_wake_thread(client); ++ } ++ return 0; ++} ++ ++static int msp_remove(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ v4l2_device_unregister_subdev(&state->sd); ++ /* shutdown control thread */ ++ if (state->kthread) { ++ state->restart = 1; ++ kthread_stop(state->kthread); ++ } ++ msp_reset(client); ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct dev_pm_ops msp3400_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) ++}; ++ ++static const struct i2c_device_id msp_id[] = { ++ { "msp3400", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, msp_id); ++ ++static struct i2c_driver msp_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "msp3400", ++ .pm = &msp3400_pm_ops, ++ }, ++ .probe = msp_probe, ++ .remove = msp_remove, ++ .id_table = msp_id, ++}; ++ ++module_i2c_driver(msp_driver); ++ ++/* ++ * Overrides for Emacs so that we follow Linus's tabbing style. ++ * --------------------------------------------------------------------------- ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/i2c/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h +new file mode 100644 +index 0000000..fbe5e07 +--- /dev/null ++++ b/drivers/media/i2c/msp3400-driver.h +@@ -0,0 +1,137 @@ ++/* ++ */ ++ ++#ifndef MSP3400_DRIVER_H ++#define MSP3400_DRIVER_H ++ ++#include ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* This macro is allowed for *constants* only, gcc must calculate it ++ at compile time. Remember -- no floats in kernel mode */ ++#define MSP_CARRIER(freq) ((int)((float)(freq / 18.432) * (1 << 24))) ++ ++#define MSP_MODE_AM_DETECT 0 ++#define MSP_MODE_FM_RADIO 2 ++#define MSP_MODE_FM_TERRA 3 ++#define MSP_MODE_FM_SAT 4 ++#define MSP_MODE_FM_NICAM1 5 ++#define MSP_MODE_FM_NICAM2 6 ++#define MSP_MODE_AM_NICAM 7 ++#define MSP_MODE_BTSC 8 ++#define MSP_MODE_EXTERN 9 ++ ++#define SCART_IN1 0 ++#define SCART_IN2 1 ++#define SCART_IN3 2 ++#define SCART_IN4 3 ++#define SCART_IN1_DA 4 ++#define SCART_IN2_DA 5 ++#define SCART_MONO 6 ++#define SCART_MUTE 7 ++ ++#define SCART_DSP_IN 0 ++#define SCART1_OUT 1 ++#define SCART2_OUT 2 ++ ++#define OPMODE_AUTO -1 ++#define OPMODE_MANUAL 0 ++#define OPMODE_AUTODETECT 1 /* use autodetect (>= msp3410 only) */ ++#define OPMODE_AUTOSELECT 2 /* use autodetect & autoselect (>= msp34xxG) */ ++ ++/* module parameters */ ++extern int msp_debug; ++extern bool msp_once; ++extern bool msp_amsound; ++extern int msp_standard; ++extern bool msp_dolby; ++extern int msp_stereo_thresh; ++ ++struct msp_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ int rev1, rev2; ++ int ident; ++ u8 has_nicam; ++ u8 has_radio; ++ u8 has_headphones; ++ u8 has_ntsc_jp_d_k3; ++ u8 has_scart2; ++ u8 has_scart3; ++ u8 has_scart4; ++ u8 has_scart2_out; ++ u8 has_scart2_out_volume; ++ u8 has_i2s_conf; ++ u8 has_subwoofer; ++ u8 has_sound_processing; ++ u8 has_virtual_dolby_surround; ++ u8 has_dolby_pro_logic; ++ u8 force_btsc; ++ ++ int radio; ++ int opmode; ++ int std; ++ int mode; ++ v4l2_std_id v4l2_std, detected_std; ++ int nicam_on; ++ int acb; ++ int in_scart; ++ int i2s_mode; ++ int main, second; /* sound carrier */ ++ int input; ++ u32 route_in; ++ u32 route_out; ++ ++ /* v4l2 */ ++ int audmode; ++ int rxsubchans; ++ ++ struct { ++ /* volume cluster */ ++ struct v4l2_ctrl *volume; ++ struct v4l2_ctrl *muted; ++ }; ++ ++ int scan_in_progress; ++ ++ /* thread */ ++ struct task_struct *kthread; ++ wait_queue_head_t wq; ++ unsigned int restart:1; ++ unsigned int watch_stereo:1; ++}; ++ ++static inline struct msp_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct msp_state, sd); ++} ++ ++static inline struct msp_state *ctrl_to_state(struct v4l2_ctrl *ctrl) ++{ ++ return container_of(ctrl->handler, struct msp_state, hdl); ++} ++ ++/* msp3400-driver.c */ ++int msp_write_dem(struct i2c_client *client, int addr, int val); ++int msp_write_dsp(struct i2c_client *client, int addr, int val); ++int msp_read_dem(struct i2c_client *client, int addr); ++int msp_read_dsp(struct i2c_client *client, int addr); ++int msp_reset(struct i2c_client *client); ++void msp_set_scart(struct i2c_client *client, int in, int out); ++void msp_update_volume(struct msp_state *state); ++int msp_sleep(struct msp_state *state, int timeout); ++ ++/* msp3400-kthreads.c */ ++const char *msp_standard_std_name(int std); ++void msp_set_audmode(struct i2c_client *client); ++int msp_detect_stereo(struct i2c_client *client); ++int msp3400c_thread(void *data); ++int msp3410d_thread(void *data); ++int msp34xxg_thread(void *data); ++void msp3400c_set_mode(struct i2c_client *client, int mode); ++void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2); ++ ++#endif /* MSP3400_DRIVER_H */ +diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c +new file mode 100644 +index 0000000..f8b5171 +--- /dev/null ++++ b/drivers/media/i2c/msp3400-kthreads.c +@@ -0,0 +1,1165 @@ ++/* ++ * Programming the mspx4xx sound processor family ++ * ++ * (c) 1997-2001 Gerd Knorr ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "msp3400-driver.h" ++ ++/* this one uses the automatic sound standard detection of newer msp34xx ++ chip versions */ ++static struct { ++ int retval; ++ int main, second; ++ char *name; ++ v4l2_std_id std; ++} msp_stdlist[] = { ++ { 0x0000, 0, 0, "could not detect sound standard", V4L2_STD_ALL }, ++ { 0x0001, 0, 0, "autodetect start", V4L2_STD_ALL }, ++ { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), ++ "4.5/4.72 M Dual FM-Stereo", V4L2_STD_MN }, ++ { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), ++ "5.5/5.74 B/G Dual FM-Stereo", V4L2_STD_BG }, ++ { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), ++ "6.5/6.25 D/K1 Dual FM-Stereo", V4L2_STD_DK }, ++ { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), ++ "6.5/6.74 D/K2 Dual FM-Stereo", V4L2_STD_DK }, ++ { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), ++ "6.5 D/K FM-Mono (HDEV3)", V4L2_STD_DK }, ++ { 0x0007, MSP_CARRIER(6.5), MSP_CARRIER(5.7421875), ++ "6.5/5.74 D/K3 Dual FM-Stereo", V4L2_STD_DK }, ++ { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), ++ "5.5/5.85 B/G NICAM FM", V4L2_STD_BG }, ++ { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), ++ "6.5/5.85 L NICAM AM", V4L2_STD_L }, ++ { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), ++ "6.0/6.55 I NICAM FM", V4L2_STD_PAL_I }, ++ { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), ++ "6.5/5.85 D/K NICAM FM", V4L2_STD_DK }, ++ { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), ++ "6.5/5.85 D/K NICAM FM (HDEV2)", V4L2_STD_DK }, ++ { 0x000d, MSP_CARRIER(6.5), MSP_CARRIER(5.85), ++ "6.5/5.85 D/K NICAM FM (HDEV3)", V4L2_STD_DK }, ++ { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), ++ "4.5 M BTSC-Stereo", V4L2_STD_MTS }, ++ { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), ++ "4.5 M BTSC-Mono + SAP", V4L2_STD_MTS }, ++ { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), ++ "4.5 M EIA-J Japan Stereo", V4L2_STD_NTSC_M_JP }, ++ { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), ++ "10.7 FM-Stereo Radio", V4L2_STD_ALL }, ++ { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), ++ "6.5 SAT-Mono", V4L2_STD_ALL }, ++ { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), ++ "7.02/7.20 SAT-Stereo", V4L2_STD_ALL }, ++ { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), ++ "7.2 SAT ADR", V4L2_STD_ALL }, ++ { -1, 0, 0, NULL, 0 }, /* EOF */ ++}; ++ ++static struct msp3400c_init_data_dem { ++ int fir1[6]; ++ int fir2[6]; ++ int cdo1; ++ int cdo2; ++ int ad_cv; ++ int mode_reg; ++ int dsp_src; ++ int dsp_matrix; ++} msp3400c_init_data[] = { ++ { /* AM (for carrier detect / msp3400) */ ++ {75, 19, 36, 35, 39, 40}, ++ {75, 19, 36, 35, 39, 40}, ++ MSP_CARRIER(5.5), MSP_CARRIER(5.5), ++ 0x00d0, 0x0500, 0x0020, 0x3000 ++ }, { /* AM (for carrier detect / msp3410) */ ++ {-1, -1, -8, 2, 59, 126}, ++ {-1, -1, -8, 2, 59, 126}, ++ MSP_CARRIER(5.5), MSP_CARRIER(5.5), ++ 0x00d0, 0x0100, 0x0020, 0x3000 ++ }, { /* FM Radio */ ++ {-8, -8, 4, 6, 78, 107}, ++ {-8, -8, 4, 6, 78, 107}, ++ MSP_CARRIER(10.7), MSP_CARRIER(10.7), ++ 0x00d0, 0x0480, 0x0020, 0x3000 ++ }, { /* Terrestrial FM-mono + FM-stereo */ ++ {3, 18, 27, 48, 66, 72}, ++ {3, 18, 27, 48, 66, 72}, ++ MSP_CARRIER(5.5), MSP_CARRIER(5.5), ++ 0x00d0, 0x0480, 0x0030, 0x3000 ++ }, { /* Sat FM-mono */ ++ { 1, 9, 14, 24, 33, 37}, ++ { 3, 18, 27, 48, 66, 72}, ++ MSP_CARRIER(6.5), MSP_CARRIER(6.5), ++ 0x00c6, 0x0480, 0x0000, 0x3000 ++ }, { /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ ++ {-2, -8, -10, 10, 50, 86}, ++ {3, 18, 27, 48, 66, 72}, ++ MSP_CARRIER(5.5), MSP_CARRIER(5.5), ++ 0x00d0, 0x0040, 0x0120, 0x3000 ++ }, { /* NICAM/FM -- I (6.0/6.552) */ ++ {2, 4, -6, -4, 40, 94}, ++ {3, 18, 27, 48, 66, 72}, ++ MSP_CARRIER(6.0), MSP_CARRIER(6.0), ++ 0x00d0, 0x0040, 0x0120, 0x3000 ++ }, { /* NICAM/AM -- L (6.5/5.85) */ ++ {-2, -8, -10, 10, 50, 86}, ++ {-4, -12, -9, 23, 79, 126}, ++ MSP_CARRIER(6.5), MSP_CARRIER(6.5), ++ 0x00c6, 0x0140, 0x0120, 0x7c00 ++ }, ++}; ++ ++struct msp3400c_carrier_detect { ++ int cdo; ++ char *name; ++}; ++ ++static struct msp3400c_carrier_detect msp3400c_carrier_detect_main[] = { ++ /* main carrier */ ++ { MSP_CARRIER(4.5), "4.5 NTSC" }, ++ { MSP_CARRIER(5.5), "5.5 PAL B/G" }, ++ { MSP_CARRIER(6.0), "6.0 PAL I" }, ++ { MSP_CARRIER(6.5), "6.5 PAL D/K + SAT + SECAM" } ++}; ++ ++static struct msp3400c_carrier_detect msp3400c_carrier_detect_55[] = { ++ /* PAL B/G */ ++ { MSP_CARRIER(5.7421875), "5.742 PAL B/G FM-stereo" }, ++ { MSP_CARRIER(5.85), "5.85 PAL B/G NICAM" } ++}; ++ ++static struct msp3400c_carrier_detect msp3400c_carrier_detect_65[] = { ++ /* PAL SAT / SECAM */ ++ { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, ++ { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, ++ { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, ++ { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, ++ { MSP_CARRIER(7.20), "7.20 PAL SAT FM-stereo s" }, ++ { MSP_CARRIER(7.38), "7.38 PAL SAT FM-stereo b" }, ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++const char *msp_standard_std_name(int std) ++{ ++ int i; ++ ++ for (i = 0; msp_stdlist[i].name != NULL; i++) ++ if (msp_stdlist[i].retval == std) ++ return msp_stdlist[i].name; ++ return "unknown"; ++} ++ ++static v4l2_std_id msp_standard_std(int std) ++{ ++ int i; ++ ++ for (i = 0; msp_stdlist[i].name != NULL; i++) ++ if (msp_stdlist[i].retval == std) ++ return msp_stdlist[i].std; ++ return V4L2_STD_ALL; ++} ++ ++static void msp_set_source(struct i2c_client *client, u16 src) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (msp_dolby) { ++ msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ ++ msp_write_dsp(client, 0x0009, 0x0620); /* I2S2 */ ++ } else { ++ msp_write_dsp(client, 0x0008, src); ++ msp_write_dsp(client, 0x0009, src); ++ } ++ msp_write_dsp(client, 0x000a, src); ++ msp_write_dsp(client, 0x000b, src); ++ msp_write_dsp(client, 0x000c, src); ++ if (state->has_scart2_out) ++ msp_write_dsp(client, 0x0041, src); ++} ++ ++void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) ++{ ++ msp_write_dem(client, 0x0093, cdo1 & 0xfff); ++ msp_write_dem(client, 0x009b, cdo1 >> 12); ++ msp_write_dem(client, 0x00a3, cdo2 & 0xfff); ++ msp_write_dem(client, 0x00ab, cdo2 >> 12); ++ msp_write_dem(client, 0x0056, 0); /* LOAD_REG_1/2 */ ++} ++ ++void msp3400c_set_mode(struct i2c_client *client, int mode) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; ++ int tuner = (state->route_in >> 3) & 1; ++ int i; ++ ++ v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); ++ state->mode = mode; ++ state->rxsubchans = V4L2_TUNER_SUB_MONO; ++ ++ msp_write_dem(client, 0x00bb, data->ad_cv | (tuner ? 0x100 : 0)); ++ ++ for (i = 5; i >= 0; i--) /* fir 1 */ ++ msp_write_dem(client, 0x0001, data->fir1[i]); ++ ++ msp_write_dem(client, 0x0005, 0x0004); /* fir 2 */ ++ msp_write_dem(client, 0x0005, 0x0040); ++ msp_write_dem(client, 0x0005, 0x0000); ++ for (i = 5; i >= 0; i--) ++ msp_write_dem(client, 0x0005, data->fir2[i]); ++ ++ msp_write_dem(client, 0x0083, data->mode_reg); ++ ++ msp3400c_set_carrier(client, data->cdo1, data->cdo2); ++ ++ msp_set_source(client, data->dsp_src); ++ /* set prescales */ ++ ++ /* volume prescale for SCART (AM mono input) */ ++ msp_write_dsp(client, 0x000d, 0x1900); ++ msp_write_dsp(client, 0x000e, data->dsp_matrix); ++ if (state->has_nicam) /* nicam prescale */ ++ msp_write_dsp(client, 0x0010, 0x5a00); ++} ++ ++/* Set audio mode. Note that the pre-'G' models do not support BTSC+SAP, ++ nor do they support stereo BTSC. */ ++static void msp3400c_set_audmode(struct i2c_client *client) ++{ ++ static char *strmode[] = { ++ "mono", "stereo", "lang2", "lang1", "lang1+lang2" ++ }; ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ char *modestr = (state->audmode >= 0 && state->audmode < 5) ? ++ strmode[state->audmode] : "unknown"; ++ int src = 0; /* channel source: FM/AM, nicam or SCART */ ++ int audmode = state->audmode; ++ ++ if (state->opmode == OPMODE_AUTOSELECT) { ++ /* this method would break everything, let's make sure ++ * it's never called ++ */ ++ v4l_dbg(1, msp_debug, client, ++ "set_audmode called with mode=%d instead of set_source (ignored)\n", ++ state->audmode); ++ return; ++ } ++ ++ /* Note: for the C and D revs no NTSC stereo + SAP is possible as ++ the hardware does not support SAP. So the rxsubchans combination ++ of STEREO | LANG2 does not occur. */ ++ ++ if (state->mode != MSP_MODE_EXTERN) { ++ /* switch to mono if only mono is available */ ++ if (state->rxsubchans == V4L2_TUNER_SUB_MONO) ++ audmode = V4L2_TUNER_MODE_MONO; ++ /* if bilingual */ ++ else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { ++ /* and mono or stereo, then fallback to lang1 */ ++ if (audmode == V4L2_TUNER_MODE_MONO || ++ audmode == V4L2_TUNER_MODE_STEREO) ++ audmode = V4L2_TUNER_MODE_LANG1; ++ } ++ /* if stereo, and audmode is not mono, then switch to stereo */ ++ else if (audmode != V4L2_TUNER_MODE_MONO) ++ audmode = V4L2_TUNER_MODE_STEREO; ++ } ++ ++ /* switch demodulator */ ++ switch (state->mode) { ++ case MSP_MODE_FM_TERRA: ++ v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); ++ switch (audmode) { ++ case V4L2_TUNER_MODE_STEREO: ++ msp_write_dsp(client, 0x000e, 0x3001); ++ break; ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_LANG1: ++ case V4L2_TUNER_MODE_LANG2: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ msp_write_dsp(client, 0x000e, 0x3000); ++ break; ++ } ++ break; ++ case MSP_MODE_FM_SAT: ++ v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); ++ switch (audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ msp3400c_set_carrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ msp3400c_set_carrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); ++ break; ++ } ++ break; ++ case MSP_MODE_FM_NICAM1: ++ case MSP_MODE_FM_NICAM2: ++ case MSP_MODE_AM_NICAM: ++ v4l_dbg(1, msp_debug, client, ++ "NICAM set_audmode: %s\n", modestr); ++ if (state->nicam_on) ++ src = 0x0100; /* NICAM */ ++ break; ++ case MSP_MODE_BTSC: ++ v4l_dbg(1, msp_debug, client, ++ "BTSC set_audmode: %s\n", modestr); ++ break; ++ case MSP_MODE_EXTERN: ++ v4l_dbg(1, msp_debug, client, ++ "extern set_audmode: %s\n", modestr); ++ src = 0x0200; /* SCART */ ++ break; ++ case MSP_MODE_FM_RADIO: ++ v4l_dbg(1, msp_debug, client, ++ "FM-Radio set_audmode: %s\n", modestr); ++ break; ++ default: ++ v4l_dbg(1, msp_debug, client, "mono set_audmode\n"); ++ return; ++ } ++ ++ /* switch audio */ ++ v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); ++ switch (audmode) { ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ src |= 0x0020; ++ break; ++ case V4L2_TUNER_MODE_MONO: ++ if (state->mode == MSP_MODE_AM_NICAM) { ++ v4l_dbg(1, msp_debug, client, "switching to AM mono\n"); ++ /* AM mono decoding is handled by tuner, not MSP chip */ ++ /* SCART switching control register */ ++ msp_set_scart(client, SCART_MONO, 0); ++ src = 0x0200; ++ break; ++ } ++ if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ++ src = 0x0030; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ src |= 0x0010; ++ break; ++ } ++ v4l_dbg(1, msp_debug, client, ++ "set_audmode final source/matrix = 0x%x\n", src); ++ ++ msp_set_source(client, src); ++} ++ ++static void msp3400c_print_mode(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->main == state->second) ++ v4l_dbg(1, msp_debug, client, ++ "mono sound carrier: %d.%03d MHz\n", ++ state->main / 910000, (state->main / 910) % 1000); ++ else ++ v4l_dbg(1, msp_debug, client, ++ "main sound carrier: %d.%03d MHz\n", ++ state->main / 910000, (state->main / 910) % 1000); ++ if (state->mode == MSP_MODE_FM_NICAM1 || state->mode == MSP_MODE_FM_NICAM2) ++ v4l_dbg(1, msp_debug, client, ++ "NICAM/FM carrier : %d.%03d MHz\n", ++ state->second / 910000, (state->second/910) % 1000); ++ if (state->mode == MSP_MODE_AM_NICAM) ++ v4l_dbg(1, msp_debug, client, ++ "NICAM/AM carrier : %d.%03d MHz\n", ++ state->second / 910000, (state->second / 910) % 1000); ++ if (state->mode == MSP_MODE_FM_TERRA && state->main != state->second) { ++ v4l_dbg(1, msp_debug, client, ++ "FM-stereo carrier : %d.%03d MHz\n", ++ state->second / 910000, (state->second / 910) % 1000); ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int msp3400c_detect_stereo(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int val; ++ int rxsubchans = state->rxsubchans; ++ int newnicam = state->nicam_on; ++ int update = 0; ++ ++ switch (state->mode) { ++ case MSP_MODE_FM_TERRA: ++ val = msp_read_dsp(client, 0x18); ++ if (val > 32767) ++ val -= 65536; ++ v4l_dbg(2, msp_debug, client, ++ "stereo detect register: %d\n", val); ++ if (val > 8192) { ++ rxsubchans = V4L2_TUNER_SUB_STEREO; ++ } else if (val < -4096) { ++ rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ } else { ++ rxsubchans = V4L2_TUNER_SUB_MONO; ++ } ++ newnicam = 0; ++ break; ++ case MSP_MODE_FM_NICAM1: ++ case MSP_MODE_FM_NICAM2: ++ case MSP_MODE_AM_NICAM: ++ val = msp_read_dem(client, 0x23); ++ v4l_dbg(2, msp_debug, client, "nicam sync=%d, mode=%d\n", ++ val & 1, (val & 0x1e) >> 1); ++ ++ if (val & 1) { ++ /* nicam synced */ ++ switch ((val & 0x1e) >> 1) { ++ case 0: ++ case 8: ++ rxsubchans = V4L2_TUNER_SUB_STEREO; ++ break; ++ case 1: ++ case 9: ++ rxsubchans = V4L2_TUNER_SUB_MONO; ++ break; ++ case 2: ++ case 10: ++ rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ break; ++ default: ++ rxsubchans = V4L2_TUNER_SUB_MONO; ++ break; ++ } ++ newnicam = 1; ++ } else { ++ newnicam = 0; ++ rxsubchans = V4L2_TUNER_SUB_MONO; ++ } ++ break; ++ } ++ if (rxsubchans != state->rxsubchans) { ++ update = 1; ++ v4l_dbg(1, msp_debug, client, ++ "watch: rxsubchans %02x => %02x\n", ++ state->rxsubchans, rxsubchans); ++ state->rxsubchans = rxsubchans; ++ } ++ if (newnicam != state->nicam_on) { ++ update = 1; ++ v4l_dbg(1, msp_debug, client, "watch: nicam %d => %d\n", ++ state->nicam_on, newnicam); ++ state->nicam_on = newnicam; ++ } ++ return update; ++} ++ ++/* ++ * A kernel thread for msp3400 control -- we don't want to block the ++ * in the ioctl while doing the sound carrier & stereo detect ++ */ ++/* stereo/multilang monitoring */ ++static void watch_stereo(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (msp_detect_stereo(client)) ++ msp_set_audmode(client); ++ ++ if (msp_once) ++ state->watch_stereo = 0; ++} ++ ++int msp3400c_thread(void *data) ++{ ++ struct i2c_client *client = data; ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ struct msp3400c_carrier_detect *cd; ++ int count, max1, max2, val1, val2, val, i; ++ ++ v4l_dbg(1, msp_debug, client, "msp3400 daemon started\n"); ++ state->detected_std = V4L2_STD_ALL; ++ set_freezable(); ++ for (;;) { ++ v4l_dbg(2, msp_debug, client, "msp3400 thread: sleep\n"); ++ msp_sleep(state, -1); ++ v4l_dbg(2, msp_debug, client, "msp3400 thread: wakeup\n"); ++ ++restart: ++ v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); ++ state->restart = 0; ++ if (kthread_should_stop()) ++ break; ++ ++ if (state->radio || MSP_MODE_EXTERN == state->mode) { ++ /* no carrier scan, just unmute */ ++ v4l_dbg(1, msp_debug, client, ++ "thread: no carrier scan\n"); ++ state->scan_in_progress = 0; ++ msp_update_volume(state); ++ continue; ++ } ++ ++ /* mute audio */ ++ state->scan_in_progress = 1; ++ msp_update_volume(state); ++ ++ msp3400c_set_mode(client, MSP_MODE_AM_DETECT); ++ val1 = val2 = 0; ++ max1 = max2 = -1; ++ state->watch_stereo = 0; ++ state->nicam_on = 0; ++ ++ /* wait for tuner to settle down after a channel change */ ++ if (msp_sleep(state, 200)) ++ goto restart; ++ ++ /* carrier detect pass #1 -- main carrier */ ++ cd = msp3400c_carrier_detect_main; ++ count = ARRAY_SIZE(msp3400c_carrier_detect_main); ++ ++ if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { ++ /* autodetect doesn't work well with AM ... */ ++ max1 = 3; ++ count = 0; ++ v4l_dbg(1, msp_debug, client, "AM sound override\n"); ++ } ++ ++ for (i = 0; i < count; i++) { ++ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); ++ if (msp_sleep(state, 100)) ++ goto restart; ++ val = msp_read_dsp(client, 0x1b); ++ if (val > 32767) ++ val -= 65536; ++ if (val1 < val) ++ val1 = val, max1 = i; ++ v4l_dbg(1, msp_debug, client, ++ "carrier1 val: %5d / %s\n", val, cd[i].name); ++ } ++ ++ /* carrier detect pass #2 -- second (stereo) carrier */ ++ switch (max1) { ++ case 1: /* 5.5 */ ++ cd = msp3400c_carrier_detect_55; ++ count = ARRAY_SIZE(msp3400c_carrier_detect_55); ++ break; ++ case 3: /* 6.5 */ ++ cd = msp3400c_carrier_detect_65; ++ count = ARRAY_SIZE(msp3400c_carrier_detect_65); ++ break; ++ case 0: /* 4.5 */ ++ case 2: /* 6.0 */ ++ default: ++ cd = NULL; ++ count = 0; ++ break; ++ } ++ ++ if (msp_amsound && (state->v4l2_std & V4L2_STD_SECAM)) { ++ /* autodetect doesn't work well with AM ... */ ++ cd = NULL; ++ count = 0; ++ max2 = 0; ++ } ++ for (i = 0; i < count; i++) { ++ msp3400c_set_carrier(client, cd[i].cdo, cd[i].cdo); ++ if (msp_sleep(state, 100)) ++ goto restart; ++ val = msp_read_dsp(client, 0x1b); ++ if (val > 32767) ++ val -= 65536; ++ if (val2 < val) ++ val2 = val, max2 = i; ++ v4l_dbg(1, msp_debug, client, ++ "carrier2 val: %5d / %s\n", val, cd[i].name); ++ } ++ ++ /* program the msp3400 according to the results */ ++ state->main = msp3400c_carrier_detect_main[max1].cdo; ++ switch (max1) { ++ case 1: /* 5.5 */ ++ state->detected_std = V4L2_STD_BG | V4L2_STD_PAL_H; ++ if (max2 == 0) { ++ /* B/G FM-stereo */ ++ state->second = msp3400c_carrier_detect_55[max2].cdo; ++ msp3400c_set_mode(client, MSP_MODE_FM_TERRA); ++ state->watch_stereo = 1; ++ } else if (max2 == 1 && state->has_nicam) { ++ /* B/G NICAM */ ++ state->second = msp3400c_carrier_detect_55[max2].cdo; ++ msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); ++ state->nicam_on = 1; ++ state->watch_stereo = 1; ++ } else { ++ goto no_second; ++ } ++ break; ++ case 2: /* 6.0 */ ++ /* PAL I NICAM */ ++ state->detected_std = V4L2_STD_PAL_I; ++ state->second = MSP_CARRIER(6.552); ++ msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); ++ state->nicam_on = 1; ++ state->watch_stereo = 1; ++ break; ++ case 3: /* 6.5 */ ++ if (max2 == 1 || max2 == 2) { ++ /* D/K FM-stereo */ ++ state->second = msp3400c_carrier_detect_65[max2].cdo; ++ msp3400c_set_mode(client, MSP_MODE_FM_TERRA); ++ state->watch_stereo = 1; ++ state->detected_std = V4L2_STD_DK; ++ } else if (max2 == 0 && (state->v4l2_std & V4L2_STD_SECAM)) { ++ /* L NICAM or AM-mono */ ++ state->second = msp3400c_carrier_detect_65[max2].cdo; ++ msp3400c_set_mode(client, MSP_MODE_AM_NICAM); ++ state->watch_stereo = 1; ++ state->detected_std = V4L2_STD_L; ++ } else if (max2 == 0 && state->has_nicam) { ++ /* D/K NICAM */ ++ state->second = msp3400c_carrier_detect_65[max2].cdo; ++ msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); ++ state->nicam_on = 1; ++ state->watch_stereo = 1; ++ state->detected_std = V4L2_STD_DK; ++ } else { ++ goto no_second; ++ } ++ break; ++ case 0: /* 4.5 */ ++ state->detected_std = V4L2_STD_MN; ++ default: ++no_second: ++ state->second = msp3400c_carrier_detect_main[max1].cdo; ++ msp3400c_set_mode(client, MSP_MODE_FM_TERRA); ++ break; ++ } ++ msp3400c_set_carrier(client, state->second, state->main); ++ ++ /* unmute */ ++ state->scan_in_progress = 0; ++ msp3400c_set_audmode(client); ++ msp_update_volume(state); ++ ++ if (msp_debug) ++ msp3400c_print_mode(client); ++ ++ /* monitor tv audio mode, the first time don't wait ++ so long to get a quick stereo/bilingual result */ ++ count = 3; ++ while (state->watch_stereo) { ++ if (msp_sleep(state, count ? 1000 : 5000)) ++ goto restart; ++ if (count) ++ count--; ++ watch_stereo(client); ++ } ++ } ++ v4l_dbg(1, msp_debug, client, "thread: exit\n"); ++ return 0; ++} ++ ++ ++int msp3410d_thread(void *data) ++{ ++ struct i2c_client *client = data; ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int val, i, std, count; ++ ++ v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); ++ state->detected_std = V4L2_STD_ALL; ++ set_freezable(); ++ for (;;) { ++ v4l_dbg(2, msp_debug, client, "msp3410 thread: sleep\n"); ++ msp_sleep(state, -1); ++ v4l_dbg(2, msp_debug, client, "msp3410 thread: wakeup\n"); ++ ++restart: ++ v4l_dbg(2, msp_debug, client, "thread: restart scan\n"); ++ state->restart = 0; ++ if (kthread_should_stop()) ++ break; ++ ++ if (state->mode == MSP_MODE_EXTERN) { ++ /* no carrier scan needed, just unmute */ ++ v4l_dbg(1, msp_debug, client, ++ "thread: no carrier scan\n"); ++ state->scan_in_progress = 0; ++ msp_update_volume(state); ++ continue; ++ } ++ ++ /* mute audio */ ++ state->scan_in_progress = 1; ++ msp_update_volume(state); ++ ++ /* start autodetect. Note: autodetect is not supported for ++ NTSC-M and radio, hence we force the standard in those ++ cases. */ ++ if (state->radio) ++ std = 0x40; ++ else ++ std = (state->v4l2_std & V4L2_STD_NTSC) ? 0x20 : 1; ++ state->watch_stereo = 0; ++ state->nicam_on = 0; ++ ++ /* wait for tuner to settle down after a channel change */ ++ if (msp_sleep(state, 200)) ++ goto restart; ++ ++ if (msp_debug) ++ v4l_dbg(2, msp_debug, client, ++ "setting standard: %s (0x%04x)\n", ++ msp_standard_std_name(std), std); ++ ++ if (std != 1) { ++ /* programmed some specific mode */ ++ val = std; ++ } else { ++ /* triggered autodetect */ ++ msp_write_dem(client, 0x20, std); ++ for (;;) { ++ if (msp_sleep(state, 100)) ++ goto restart; ++ ++ /* check results */ ++ val = msp_read_dem(client, 0x7e); ++ if (val < 0x07ff) ++ break; ++ v4l_dbg(2, msp_debug, client, ++ "detection still in progress\n"); ++ } ++ } ++ for (i = 0; msp_stdlist[i].name != NULL; i++) ++ if (msp_stdlist[i].retval == val) ++ break; ++ v4l_dbg(1, msp_debug, client, "current standard: %s (0x%04x)\n", ++ msp_standard_std_name(val), val); ++ state->main = msp_stdlist[i].main; ++ state->second = msp_stdlist[i].second; ++ state->std = val; ++ state->rxsubchans = V4L2_TUNER_SUB_MONO; ++ ++ if (msp_amsound && !state->radio && ++ (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { ++ /* autodetection has failed, let backup */ ++ v4l_dbg(1, msp_debug, client, "autodetection failed," ++ " switching to backup standard: %s (0x%04x)\n", ++ msp_stdlist[8].name ? ++ msp_stdlist[8].name : "unknown", val); ++ state->std = val = 0x0009; ++ msp_write_dem(client, 0x20, val); ++ } else { ++ state->detected_std = msp_standard_std(state->std); ++ } ++ ++ /* set stereo */ ++ switch (val) { ++ case 0x0008: /* B/G NICAM */ ++ case 0x000a: /* I NICAM */ ++ case 0x000b: /* D/K NICAM */ ++ if (val == 0x000a) ++ state->mode = MSP_MODE_FM_NICAM2; ++ else ++ state->mode = MSP_MODE_FM_NICAM1; ++ /* just turn on stereo */ ++ state->nicam_on = 1; ++ state->watch_stereo = 1; ++ break; ++ case 0x0009: ++ state->mode = MSP_MODE_AM_NICAM; ++ state->nicam_on = 1; ++ state->watch_stereo = 1; ++ break; ++ case 0x0020: /* BTSC */ ++ /* The pre-'G' models only have BTSC-mono */ ++ state->mode = MSP_MODE_BTSC; ++ break; ++ case 0x0040: /* FM radio */ ++ state->mode = MSP_MODE_FM_RADIO; ++ state->rxsubchans = V4L2_TUNER_SUB_STEREO; ++ /* not needed in theory if we have radio, but ++ short programming enables carrier mute */ ++ msp3400c_set_mode(client, MSP_MODE_FM_RADIO); ++ msp3400c_set_carrier(client, MSP_CARRIER(10.7), ++ MSP_CARRIER(10.7)); ++ break; ++ case 0x0002: ++ case 0x0003: ++ case 0x0004: ++ case 0x0005: ++ state->mode = MSP_MODE_FM_TERRA; ++ state->watch_stereo = 1; ++ break; ++ } ++ ++ /* set various prescales */ ++ msp_write_dsp(client, 0x0d, 0x1900); /* scart */ ++ msp_write_dsp(client, 0x0e, 0x3000); /* FM */ ++ if (state->has_nicam) ++ msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ ++ ++ if (state->has_i2s_conf) ++ msp_write_dem(client, 0x40, state->i2s_mode); ++ ++ /* unmute */ ++ msp3400c_set_audmode(client); ++ state->scan_in_progress = 0; ++ msp_update_volume(state); ++ ++ /* monitor tv audio mode, the first time don't wait ++ so long to get a quick stereo/bilingual result */ ++ count = 3; ++ while (state->watch_stereo) { ++ if (msp_sleep(state, count ? 1000 : 5000)) ++ goto restart; ++ if (count) ++ count--; ++ watch_stereo(client); ++ } ++ } ++ v4l_dbg(1, msp_debug, client, "thread: exit\n"); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* msp34xxG + (autoselect no-thread) ++ * this one uses both automatic standard detection and automatic sound ++ * select which are available in the newer G versions ++ * struct msp: only norm, acb and source are really used in this mode ++ */ ++ ++static int msp34xxg_modus(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->radio) { ++ v4l_dbg(1, msp_debug, client, "selected radio modus\n"); ++ return 0x0001; ++ } ++ if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { ++ v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); ++ return 0x4001; ++ } ++ if (state->v4l2_std == V4L2_STD_NTSC_M_KR) { ++ v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); ++ return 0x0001; ++ } ++ if (state->v4l2_std == V4L2_STD_SECAM_L) { ++ v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); ++ return 0x6001; ++ } ++ if (state->v4l2_std & V4L2_STD_MN) { ++ v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); ++ return 0x2001; ++ } ++ return 0x7001; ++} ++ ++static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) ++ { ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int source, matrix; ++ ++ switch (state->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ source = 0; /* mono only */ ++ matrix = 0x30; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ source = 4; /* stereo or B */ ++ matrix = 0x10; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ source = 1; /* stereo or A|B */ ++ matrix = 0x20; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ source = 3; /* stereo or A */ ++ matrix = 0x00; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ default: ++ source = 3; /* stereo or A */ ++ matrix = 0x20; ++ break; ++ } ++ ++ if (in == MSP_DSP_IN_TUNER) ++ source = (source << 8) | 0x20; ++ /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 ++ instead of 11, 12, 13. So we add one for that msp version. */ ++ else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) ++ source = ((in + 1) << 8) | matrix; ++ else ++ source = (in << 8) | matrix; ++ ++ v4l_dbg(1, msp_debug, client, ++ "set source to %d (0x%x) for output %02x\n", in, source, reg); ++ msp_write_dsp(client, reg, source); ++} ++ ++static void msp34xxg_set_sources(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ u32 in = state->route_in; ++ ++ msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); ++ /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ ++ msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); ++ msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); ++ msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); ++ if (state->has_scart2_out) ++ msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); ++ msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); ++} ++ ++/* (re-)initialize the msp34xxg */ ++static void msp34xxg_reset(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int tuner = (state->route_in >> 3) & 1; ++ int modus; ++ ++ /* initialize std to 1 (autodetect) to signal that no standard is ++ selected yet. */ ++ state->std = 1; ++ ++ msp_reset(client); ++ ++ if (state->has_i2s_conf) ++ msp_write_dem(client, 0x40, state->i2s_mode); ++ ++ /* step-by-step initialisation, as described in the manual */ ++ modus = msp34xxg_modus(client); ++ modus |= tuner ? 0x100 : 0; ++ msp_write_dem(client, 0x30, modus); ++ ++ /* write the dsps that may have an influence on ++ standard/audio autodetection right now */ ++ msp34xxg_set_sources(client); ++ ++ msp_write_dsp(client, 0x0d, 0x1900); /* scart */ ++ msp_write_dsp(client, 0x0e, 0x3000); /* FM */ ++ if (state->has_nicam) ++ msp_write_dsp(client, 0x10, 0x5a00); /* nicam */ ++ ++ /* set identification threshold. Personally, I ++ * I set it to a higher value than the default ++ * of 0x190 to ignore noisy stereo signals. ++ * this needs tuning. (recommended range 0x00a0-0x03c0) ++ * 0x7f0 = forced mono mode ++ * ++ * a2 threshold for stereo/bilingual. ++ * Note: this register is part of the Manual/Compatibility mode. ++ * It is supported by all 'G'-family chips. ++ */ ++ msp_write_dem(client, 0x22, msp_stereo_thresh); ++} ++ ++int msp34xxg_thread(void *data) ++{ ++ struct i2c_client *client = data; ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int val, i; ++ ++ v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); ++ state->detected_std = V4L2_STD_ALL; ++ set_freezable(); ++ for (;;) { ++ v4l_dbg(2, msp_debug, client, "msp34xxg thread: sleep\n"); ++ msp_sleep(state, -1); ++ v4l_dbg(2, msp_debug, client, "msp34xxg thread: wakeup\n"); ++ ++restart: ++ v4l_dbg(1, msp_debug, client, "thread: restart scan\n"); ++ state->restart = 0; ++ if (kthread_should_stop()) ++ break; ++ ++ if (state->mode == MSP_MODE_EXTERN) { ++ /* no carrier scan needed, just unmute */ ++ v4l_dbg(1, msp_debug, client, ++ "thread: no carrier scan\n"); ++ state->scan_in_progress = 0; ++ msp_update_volume(state); ++ continue; ++ } ++ ++ /* setup the chip*/ ++ msp34xxg_reset(client); ++ state->std = state->radio ? 0x40 : ++ (state->force_btsc && msp_standard == 1) ? 32 : msp_standard; ++ msp_write_dem(client, 0x20, state->std); ++ /* start autodetect */ ++ if (state->std != 1) ++ goto unmute; ++ ++ /* watch autodetect */ ++ v4l_dbg(1, msp_debug, client, ++ "started autodetect, waiting for result\n"); ++ for (i = 0; i < 10; i++) { ++ if (msp_sleep(state, 100)) ++ goto restart; ++ ++ /* check results */ ++ val = msp_read_dem(client, 0x7e); ++ if (val < 0x07ff) { ++ state->std = val; ++ break; ++ } ++ v4l_dbg(2, msp_debug, client, ++ "detection still in progress\n"); ++ } ++ if (state->std == 1) { ++ v4l_dbg(1, msp_debug, client, ++ "detection still in progress after 10 tries. giving up.\n"); ++ continue; ++ } ++ ++unmute: ++ v4l_dbg(1, msp_debug, client, ++ "detected standard: %s (0x%04x)\n", ++ msp_standard_std_name(state->std), state->std); ++ state->detected_std = msp_standard_std(state->std); ++ ++ if (state->std == 9) { ++ /* AM NICAM mode */ ++ msp_write_dsp(client, 0x0e, 0x7c00); ++ } ++ ++ /* unmute: dispatch sound to scart output, set scart volume */ ++ msp_update_volume(state); ++ ++ /* restore ACB */ ++ if (msp_write_dsp(client, 0x13, state->acb)) ++ return -1; ++ ++ /* the periodic stereo/SAP check is only relevant for ++ the 0x20 standard (BTSC) */ ++ if (state->std != 0x20) ++ continue; ++ ++ state->watch_stereo = 1; ++ ++ /* monitor tv audio mode, the first time don't wait ++ in order to get a quick stereo/SAP update */ ++ watch_stereo(client); ++ while (state->watch_stereo) { ++ watch_stereo(client); ++ if (msp_sleep(state, 5000)) ++ goto restart; ++ } ++ } ++ v4l_dbg(1, msp_debug, client, "thread: exit\n"); ++ return 0; ++} ++ ++static int msp34xxg_detect_stereo(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ int status = msp_read_dem(client, 0x0200); ++ int is_bilingual = status & 0x100; ++ int is_stereo = status & 0x40; ++ int oldrx = state->rxsubchans; ++ ++ if (state->mode == MSP_MODE_EXTERN) ++ return 0; ++ ++ state->rxsubchans = 0; ++ if (is_stereo) ++ state->rxsubchans = V4L2_TUNER_SUB_STEREO; ++ else ++ state->rxsubchans = V4L2_TUNER_SUB_MONO; ++ if (is_bilingual) { ++ if (state->std == 0x20) ++ state->rxsubchans |= V4L2_TUNER_SUB_SAP; ++ else ++ state->rxsubchans = ++ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ } ++ v4l_dbg(1, msp_debug, client, ++ "status=0x%x, stereo=%d, bilingual=%d -> rxsubchans=%d\n", ++ status, is_stereo, is_bilingual, state->rxsubchans); ++ return (oldrx != state->rxsubchans); ++} ++ ++static void msp34xxg_set_audmode(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ if (state->std == 0x20) { ++ if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && ++ (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || ++ state->audmode == V4L2_TUNER_MODE_LANG2)) { ++ msp_write_dem(client, 0x20, 0x21); ++ } else { ++ msp_write_dem(client, 0x20, 0x20); ++ } ++ } ++ ++ msp34xxg_set_sources(client); ++} ++ ++void msp_set_audmode(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ switch (state->opmode) { ++ case OPMODE_MANUAL: ++ case OPMODE_AUTODETECT: ++ msp3400c_set_audmode(client); ++ break; ++ case OPMODE_AUTOSELECT: ++ msp34xxg_set_audmode(client); ++ break; ++ } ++} ++ ++int msp_detect_stereo(struct i2c_client *client) ++{ ++ struct msp_state *state = to_state(i2c_get_clientdata(client)); ++ ++ switch (state->opmode) { ++ case OPMODE_MANUAL: ++ case OPMODE_AUTODETECT: ++ return msp3400c_detect_stereo(client); ++ case OPMODE_AUTOSELECT: ++ return msp34xxg_detect_stereo(client); ++ } ++ return 0; ++} ++ +diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c +new file mode 100644 +index 0000000..f80c1d7 +--- /dev/null ++++ b/drivers/media/i2c/mt9m032.c +@@ -0,0 +1,878 @@ ++/* ++ * Driver for MT9M032 CMOS Image Sensor from Micron ++ * ++ * Copyright (C) 2010-2011 Lund Engineering ++ * Contact: Gil Lund ++ * Author: Martin Hostettler ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aptina-pll.h" ++ ++/* ++ * width and height include active boundary and black parts ++ * ++ * column 0- 15 active boundary ++ * column 16-1455 image ++ * column 1456-1471 active boundary ++ * column 1472-1599 black ++ * ++ * row 0- 51 black ++ * row 53- 59 active boundary ++ * row 60-1139 image ++ * row 1140-1147 active boundary ++ * row 1148-1151 black ++ */ ++ ++#define MT9M032_PIXEL_ARRAY_WIDTH 1600 ++#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 ++ ++#define MT9M032_CHIP_VERSION 0x00 ++#define MT9M032_CHIP_VERSION_VALUE 0x1402 ++#define MT9M032_ROW_START 0x01 ++#define MT9M032_ROW_START_MIN 0 ++#define MT9M032_ROW_START_MAX 1152 ++#define MT9M032_ROW_START_DEF 60 ++#define MT9M032_COLUMN_START 0x02 ++#define MT9M032_COLUMN_START_MIN 0 ++#define MT9M032_COLUMN_START_MAX 1600 ++#define MT9M032_COLUMN_START_DEF 16 ++#define MT9M032_ROW_SIZE 0x03 ++#define MT9M032_ROW_SIZE_MIN 32 ++#define MT9M032_ROW_SIZE_MAX 1152 ++#define MT9M032_ROW_SIZE_DEF 1080 ++#define MT9M032_COLUMN_SIZE 0x04 ++#define MT9M032_COLUMN_SIZE_MIN 32 ++#define MT9M032_COLUMN_SIZE_MAX 1600 ++#define MT9M032_COLUMN_SIZE_DEF 1440 ++#define MT9M032_HBLANK 0x05 ++#define MT9M032_VBLANK 0x06 ++#define MT9M032_VBLANK_MAX 0x7ff ++#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 ++#define MT9M032_SHUTTER_WIDTH_LOW 0x09 ++#define MT9M032_SHUTTER_WIDTH_MIN 1 ++#define MT9M032_SHUTTER_WIDTH_MAX 1048575 ++#define MT9M032_SHUTTER_WIDTH_DEF 1943 ++#define MT9M032_PIX_CLK_CTRL 0x0a ++#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 ++#define MT9M032_RESTART 0x0b ++#define MT9M032_RESET 0x0d ++#define MT9M032_PLL_CONFIG1 0x11 ++#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f ++#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 ++#define MT9M032_READ_MODE1 0x1e ++#define MT9M032_READ_MODE2 0x20 ++#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 ++#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 ++#define MT9M032_READ_MODE2_ROW_BLC 0x40 ++#define MT9M032_GAIN_GREEN1 0x2b ++#define MT9M032_GAIN_BLUE 0x2c ++#define MT9M032_GAIN_RED 0x2d ++#define MT9M032_GAIN_GREEN2 0x2e ++ ++/* write only */ ++#define MT9M032_GAIN_ALL 0x35 ++#define MT9M032_GAIN_DIGITAL_MASK 0x7f ++#define MT9M032_GAIN_DIGITAL_SHIFT 8 ++#define MT9M032_GAIN_AMUL_SHIFT 6 ++#define MT9M032_GAIN_ANALOG_MASK 0x3f ++#define MT9M032_FORMATTER1 0x9e ++#define MT9M032_FORMATTER2 0x9f ++#define MT9M032_FORMATTER2_DOUT_EN 0x1000 ++#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 ++ ++/* ++ * The available MT9M032 datasheet is missing documentation for register 0x10 ++ * MT9P031 seems to be close enough, so use constants from that datasheet for ++ * now. ++ * But keep the name MT9P031 to remind us, that this isn't really confirmed ++ * for this sensor. ++ */ ++#define MT9P031_PLL_CONTROL 0x10 ++#define MT9P031_PLL_CONTROL_PWROFF 0x0050 ++#define MT9P031_PLL_CONTROL_PWRON 0x0051 ++#define MT9P031_PLL_CONTROL_USEPLL 0x0052 ++#define MT9P031_PLL_CONFIG2 0x11 ++#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f ++ ++struct mt9m032 { ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ struct mt9m032_platform_data *pdata; ++ ++ unsigned int pix_clock; ++ ++ struct v4l2_ctrl_handler ctrls; ++ struct { ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ }; ++ ++ struct mutex lock; /* Protects streaming, format, interval and crop */ ++ ++ bool streaming; ++ ++ struct v4l2_mbus_framefmt format; ++ struct v4l2_rect crop; ++ struct v4l2_fract frame_interval; ++}; ++ ++#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) ++#define to_dev(sensor) \ ++ (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) ++ ++static int mt9m032_read(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) ++{ ++ unsigned int effective_width; ++ u32 ns; ++ ++ effective_width = width + 716; /* empirical value */ ++ ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); ++ dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); ++ return ns; ++} ++ ++static int mt9m032_update_timing(struct mt9m032 *sensor, ++ struct v4l2_fract *interval) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ struct v4l2_rect *crop = &sensor->crop; ++ unsigned int min_vblank; ++ unsigned int vblank; ++ u32 row_time; ++ ++ if (!interval) ++ interval = &sensor->frame_interval; ++ ++ row_time = mt9m032_row_time(sensor, crop->width); ++ ++ vblank = div_u64(1000000000ULL * interval->numerator, ++ (u64)row_time * interval->denominator) ++ - crop->height; ++ ++ if (vblank > MT9M032_VBLANK_MAX) { ++ /* hardware limits to 11 bit values */ ++ interval->denominator = 1000; ++ interval->numerator = ++ div_u64((crop->height + MT9M032_VBLANK_MAX) * ++ (u64)row_time * interval->denominator, ++ 1000000000ULL); ++ vblank = div_u64(1000000000ULL * interval->numerator, ++ (u64)row_time * interval->denominator) ++ - crop->height; ++ } ++ /* enforce minimal 1.6ms blanking time. */ ++ min_vblank = 1600000 / row_time; ++ vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); ++ ++ return mt9m032_write(client, MT9M032_VBLANK, vblank); ++} ++ ++static int mt9m032_update_geom_timing(struct mt9m032 *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ int ret; ++ ++ ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, ++ sensor->crop.width - 1); ++ if (!ret) ++ ret = mt9m032_write(client, MT9M032_ROW_SIZE, ++ sensor->crop.height - 1); ++ if (!ret) ++ ret = mt9m032_write(client, MT9M032_COLUMN_START, ++ sensor->crop.left); ++ if (!ret) ++ ret = mt9m032_write(client, MT9M032_ROW_START, ++ sensor->crop.top); ++ if (!ret) ++ ret = mt9m032_update_timing(sensor, NULL); ++ return ret; ++} ++ ++static int update_formatter2(struct mt9m032 *sensor, bool streaming) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ u16 reg_val = MT9M032_FORMATTER2_DOUT_EN ++ | 0x0070; /* parts reserved! */ ++ /* possibly for changing to 14-bit mode */ ++ ++ if (streaming) ++ reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ ++ ++ return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); ++} ++ ++static int mt9m032_setup_pll(struct mt9m032 *sensor) ++{ ++ static const struct aptina_pll_limits limits = { ++ .ext_clock_min = 8000000, ++ .ext_clock_max = 16500000, ++ .int_clock_min = 2000000, ++ .int_clock_max = 24000000, ++ .out_clock_min = 322000000, ++ .out_clock_max = 693000000, ++ .pix_clock_max = 99000000, ++ .n_min = 1, ++ .n_max = 64, ++ .m_min = 16, ++ .m_max = 255, ++ .p1_min = 1, ++ .p1_max = 128, ++ }; ++ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ struct mt9m032_platform_data *pdata = sensor->pdata; ++ struct aptina_pll pll; ++ int ret; ++ ++ pll.ext_clock = pdata->ext_clock; ++ pll.pix_clock = pdata->pix_clock; ++ ++ ret = aptina_pll_calculate(&client->dev, &limits, &pll); ++ if (ret < 0) ++ return ret; ++ ++ sensor->pix_clock = pdata->pix_clock; ++ ++ ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, ++ (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) ++ | (pll.p1 - 1)); ++ if (!ret) ++ ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1); ++ if (!ret) ++ ret = mt9m032_write(client, MT9P031_PLL_CONTROL, ++ MT9P031_PLL_CONTROL_PWRON | ++ MT9P031_PLL_CONTROL_USEPLL); ++ if (!ret) /* more reserved, Continuous, Master Mode */ ++ ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006); ++ if (!ret) /* Set 14-bit mode, select 7 divider */ ++ ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e); ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Subdev pad operations ++ */ ++ ++static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index != 0) ++ return -EINVAL; ++ ++ code->code = V4L2_MBUS_FMT_Y8_1X8; ++ return 0; ++} ++ ++static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8) ++ return -EINVAL; ++ ++ fse->min_width = MT9M032_COLUMN_SIZE_DEF; ++ fse->max_width = MT9M032_COLUMN_SIZE_DEF; ++ fse->min_height = MT9M032_ROW_SIZE_DEF; ++ fse->max_height = MT9M032_ROW_SIZE_DEF; ++ ++ return 0; ++} ++ ++/** ++ * __mt9m032_get_pad_crop() - get crop rect ++ * @sensor: pointer to the sensor struct ++ * @fh: file handle for getting the try crop rect from ++ * @which: select try or active crop rect ++ * ++ * Returns a pointer the current active or fh relative try crop rect ++ */ ++static struct v4l2_rect * ++__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, ++ enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(fh, 0); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &sensor->crop; ++ default: ++ return NULL; ++ } ++} ++ ++/** ++ * __mt9m032_get_pad_format() - get format ++ * @sensor: pointer to the sensor struct ++ * @fh: file handle for getting the try format from ++ * @which: select try or active format ++ * ++ * Returns a pointer the current active or fh relative try format ++ */ ++static struct v4l2_mbus_framefmt * ++__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh, ++ enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_format(fh, 0); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &sensor->format; ++ default: ++ return NULL; ++ } ++} ++ ++static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ ++ mutex_lock(&sensor->lock); ++ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ int ret; ++ ++ mutex_lock(&sensor->lock); ++ ++ if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ /* Scaling is not supported, the format is thus fixed. */ ++ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which); ++ ret = 0; ++ ++done: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ ++ mutex_lock(&sensor->lock); ++ crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ struct v4l2_mbus_framefmt *format; ++ struct v4l2_rect *__crop; ++ struct v4l2_rect rect; ++ int ret = 0; ++ ++ mutex_lock(&sensor->lock); ++ ++ if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ /* Clamp the crop rectangle boundaries and align them to a multiple of 2 ++ * pixels to ensure a GRBG Bayer pattern. ++ */ ++ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, ++ MT9M032_COLUMN_START_MAX); ++ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, ++ MT9M032_ROW_START_MAX); ++ rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN, ++ MT9M032_COLUMN_SIZE_MAX); ++ rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN, ++ MT9M032_ROW_SIZE_MAX); ++ ++ rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left); ++ rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); ++ ++ __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); ++ ++ if (rect.width != __crop->width || rect.height != __crop->height) { ++ /* Reset the output image size if the crop rectangle size has ++ * been modified. ++ */ ++ format = __mt9m032_get_pad_format(sensor, fh, crop->which); ++ format->width = rect.width; ++ format->height = rect.height; ++ } ++ ++ *__crop = rect; ++ crop->rect = rect; ++ ++ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ ret = mt9m032_update_geom_timing(sensor); ++ ++done: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ ++ mutex_lock(&sensor->lock); ++ memset(fi, 0, sizeof(*fi)); ++ fi->interval = sensor->frame_interval; ++ mutex_unlock(&sensor->lock); ++ ++ return 0; ++} ++ ++static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ int ret; ++ ++ mutex_lock(&sensor->lock); ++ ++ if (sensor->streaming) { ++ ret = -EBUSY; ++ goto done; ++ } ++ ++ /* Avoid divisions by 0. */ ++ if (fi->interval.denominator == 0) ++ fi->interval.denominator = 1; ++ ++ ret = mt9m032_update_timing(sensor, &fi->interval); ++ if (!ret) ++ sensor->frame_interval = fi->interval; ++ ++done: ++ mutex_unlock(&sensor->lock); ++ return ret; ++} ++ ++static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) ++{ ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ int ret; ++ ++ mutex_lock(&sensor->lock); ++ ret = update_formatter2(sensor, streaming); ++ if (!ret) ++ sensor->streaming = streaming; ++ mutex_unlock(&sensor->lock); ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev core operations ++ */ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9m032_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct mt9m032 *sensor = to_mt9m032(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ int val; ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ val = mt9m032_read(client, reg->reg); ++ if (val < 0) ++ return -EIO; ++ ++ reg->size = 2; ++ reg->val = val; ++ ++ return 0; ++} ++ ++static int mt9m032_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct mt9m032 *sensor = to_mt9m032(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ return mt9m032_write(client, reg->reg, reg->val); ++} ++#endif ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev control operations ++ */ ++ ++static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) ++ | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) ++ | MT9M032_READ_MODE2_ROW_BLC ++ | 0x0007; ++ ++ return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); ++} ++ ++static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ int digital_gain_val; /* in 1/8th (0..127) */ ++ int analog_mul; /* 0 or 1 */ ++ int analog_gain_val; /* in 1/16th. (0..63) */ ++ u16 reg_val; ++ ++ digital_gain_val = 51; /* from setup example */ ++ ++ if (val < 63) { ++ analog_mul = 0; ++ analog_gain_val = val; ++ } else { ++ analog_mul = 1; ++ analog_gain_val = val / 2; ++ } ++ ++ /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ ++ /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ ++ ++ reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) ++ << MT9M032_GAIN_DIGITAL_SHIFT) ++ | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) ++ | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); ++ ++ return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); ++} ++ ++static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { ++ /* round because of multiplier used for values >= 63 */ ++ ctrl->val &= ~1; ++ } ++ ++ return 0; ++} ++ ++static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9m032 *sensor = ++ container_of(ctrl->handler, struct mt9m032, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); ++ int ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN: ++ return mt9m032_set_gain(sensor, ctrl->val); ++ ++ case V4L2_CID_HFLIP: ++ /* case V4L2_CID_VFLIP: -- In the same cluster */ ++ return update_read_mode2(sensor, sensor->vflip->val, ++ sensor->hflip->val); ++ ++ case V4L2_CID_EXPOSURE: ++ ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, ++ (ctrl->val >> 16) & 0xffff); ++ if (ret < 0) ++ return ret; ++ ++ return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, ++ ctrl->val & 0xffff); ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_ctrl_ops mt9m032_ctrl_ops = { ++ .s_ctrl = mt9m032_set_ctrl, ++ .try_ctrl = mt9m032_try_ctrl, ++}; ++ ++/* -------------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops mt9m032_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9m032_g_register, ++ .s_register = mt9m032_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops mt9m032_video_ops = { ++ .s_stream = mt9m032_s_stream, ++ .g_frame_interval = mt9m032_get_frame_interval, ++ .s_frame_interval = mt9m032_set_frame_interval, ++}; ++ ++static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { ++ .enum_mbus_code = mt9m032_enum_mbus_code, ++ .enum_frame_size = mt9m032_enum_frame_size, ++ .get_fmt = mt9m032_get_pad_format, ++ .set_fmt = mt9m032_set_pad_format, ++ .set_crop = mt9m032_set_pad_crop, ++ .get_crop = mt9m032_get_pad_crop, ++}; ++ ++static const struct v4l2_subdev_ops mt9m032_ops = { ++ .core = &mt9m032_core_ops, ++ .video = &mt9m032_video_ops, ++ .pad = &mt9m032_pad_ops, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Driver initialization and probing ++ */ ++ ++static int mt9m032_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct mt9m032_platform_data *pdata = client->dev.platform_data; ++ struct i2c_adapter *adapter = client->adapter; ++ struct mt9m032 *sensor; ++ int chip_version; ++ int ret; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "No platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&client->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ if (!client->dev.platform_data) ++ return -ENODEV; ++ ++ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); ++ if (sensor == NULL) ++ return -ENOMEM; ++ ++ mutex_init(&sensor->lock); ++ ++ sensor->pdata = pdata; ++ ++ v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); ++ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); ++ if (chip_version != MT9M032_CHIP_VERSION_VALUE) { ++ dev_err(&client->dev, "MT9M032 not detected, wrong version " ++ "0x%04x\n", chip_version); ++ ret = -ENODEV; ++ goto error_sensor; ++ } ++ ++ dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", ++ client->addr); ++ ++ sensor->frame_interval.numerator = 1; ++ sensor->frame_interval.denominator = 30; ++ ++ sensor->crop.left = MT9M032_COLUMN_START_DEF; ++ sensor->crop.top = MT9M032_ROW_START_DEF; ++ sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; ++ sensor->crop.height = MT9M032_ROW_SIZE_DEF; ++ ++ sensor->format.width = sensor->crop.width; ++ sensor->format.height = sensor->crop.height; ++ sensor->format.code = V4L2_MBUS_FMT_Y8_1X8; ++ sensor->format.field = V4L2_FIELD_NONE; ++ sensor->format.colorspace = V4L2_COLORSPACE_SRGB; ++ ++ v4l2_ctrl_handler_init(&sensor->ctrls, 5); ++ ++ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, ++ V4L2_CID_GAIN, 0, 127, 1, 64); ++ ++ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, ++ &mt9m032_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, ++ &mt9m032_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, ++ V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, ++ MT9M032_SHUTTER_WIDTH_MAX, 1, ++ MT9M032_SHUTTER_WIDTH_DEF); ++ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, pdata->pix_clock, ++ pdata->pix_clock, 1, pdata->pix_clock); ++ ++ if (sensor->ctrls.error) { ++ ret = sensor->ctrls.error; ++ dev_err(&client->dev, "control initialization error %d\n", ret); ++ goto error_ctrl; ++ } ++ ++ v4l2_ctrl_cluster(2, &sensor->hflip); ++ ++ sensor->subdev.ctrl_handler = &sensor->ctrls; ++ sensor->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0); ++ if (ret < 0) ++ goto error_ctrl; ++ ++ ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ ++ if (ret < 0) ++ goto error_entity; ++ ret = mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ ++ if (ret < 0) ++ goto error_entity; ++ ++ ret = mt9m032_setup_pll(sensor); ++ if (ret < 0) ++ goto error_entity; ++ usleep_range(10000, 11000); ++ ++ ret = v4l2_ctrl_handler_setup(&sensor->ctrls); ++ if (ret < 0) ++ goto error_entity; ++ ++ /* SIZE */ ++ ret = mt9m032_update_geom_timing(sensor); ++ if (ret < 0) ++ goto error_entity; ++ ++ ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ ++ if (ret < 0) ++ goto error_entity; ++ ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ ++ if (ret < 0) ++ goto error_entity; ++ ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ ++ if (ret < 0) ++ goto error_entity; ++ ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ ++ if (ret < 0) ++ goto error_entity; ++ if (sensor->pdata->invert_pixclock) { ++ ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, ++ MT9M032_PIX_CLK_CTRL_INV_PIXCLK); ++ if (ret < 0) ++ goto error_entity; ++ } ++ ++ ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ ++ if (ret < 0) ++ goto error_entity; ++ msleep(100); ++ ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ ++ if (ret < 0) ++ goto error_entity; ++ msleep(100); ++ ret = update_formatter2(sensor, false); ++ if (ret < 0) ++ goto error_entity; ++ ++ return ret; ++ ++error_entity: ++ media_entity_cleanup(&sensor->subdev.entity); ++error_ctrl: ++ v4l2_ctrl_handler_free(&sensor->ctrls); ++error_sensor: ++ mutex_destroy(&sensor->lock); ++ kfree(sensor); ++ return ret; ++} ++ ++static int mt9m032_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct mt9m032 *sensor = to_mt9m032(subdev); ++ ++ v4l2_device_unregister_subdev(subdev); ++ v4l2_ctrl_handler_free(&sensor->ctrls); ++ media_entity_cleanup(&subdev->entity); ++ mutex_destroy(&sensor->lock); ++ kfree(sensor); ++ return 0; ++} ++ ++static const struct i2c_device_id mt9m032_id_table[] = { ++ { MT9M032_NAME, 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); ++ ++static struct i2c_driver mt9m032_i2c_driver = { ++ .driver = { ++ .name = MT9M032_NAME, ++ }, ++ .probe = mt9m032_probe, ++ .remove = mt9m032_remove, ++ .id_table = mt9m032_id_table, ++}; ++ ++module_i2c_driver(mt9m032_i2c_driver); ++ ++MODULE_AUTHOR("Martin Hostettler "); ++MODULE_DESCRIPTION("MT9M032 camera sensor driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c +new file mode 100644 +index 0000000..e328332 +--- /dev/null ++++ b/drivers/media/i2c/mt9p031.c +@@ -0,0 +1,1062 @@ ++/* ++ * Driver for MT9P031 CMOS Image Sensor from Aptina ++ * ++ * Copyright (C) 2011, Laurent Pinchart ++ * Copyright (C) 2011, Javier Martin ++ * Copyright (C) 2011, Guennadi Liakhovetski ++ * ++ * Based on the MT9V032 driver and Bastian Hecht's code. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "aptina-pll.h" ++ ++#define MT9P031_PIXEL_ARRAY_WIDTH 2752 ++#define MT9P031_PIXEL_ARRAY_HEIGHT 2004 ++ ++#define MT9P031_CHIP_VERSION 0x00 ++#define MT9P031_CHIP_VERSION_VALUE 0x1801 ++#define MT9P031_ROW_START 0x01 ++#define MT9P031_ROW_START_MIN 0 ++#define MT9P031_ROW_START_MAX 2004 ++#define MT9P031_ROW_START_DEF 54 ++#define MT9P031_COLUMN_START 0x02 ++#define MT9P031_COLUMN_START_MIN 0 ++#define MT9P031_COLUMN_START_MAX 2750 ++#define MT9P031_COLUMN_START_DEF 16 ++#define MT9P031_WINDOW_HEIGHT 0x03 ++#define MT9P031_WINDOW_HEIGHT_MIN 2 ++#define MT9P031_WINDOW_HEIGHT_MAX 2006 ++#define MT9P031_WINDOW_HEIGHT_DEF 1944 ++#define MT9P031_WINDOW_WIDTH 0x04 ++#define MT9P031_WINDOW_WIDTH_MIN 2 ++#define MT9P031_WINDOW_WIDTH_MAX 2752 ++#define MT9P031_WINDOW_WIDTH_DEF 2592 ++#define MT9P031_HORIZONTAL_BLANK 0x05 ++#define MT9P031_HORIZONTAL_BLANK_MIN 0 ++#define MT9P031_HORIZONTAL_BLANK_MAX 4095 ++#define MT9P031_VERTICAL_BLANK 0x06 ++#define MT9P031_VERTICAL_BLANK_MIN 1 ++#define MT9P031_VERTICAL_BLANK_MAX 4096 ++#define MT9P031_VERTICAL_BLANK_DEF 26 ++#define MT9P031_OUTPUT_CONTROL 0x07 ++#define MT9P031_OUTPUT_CONTROL_CEN 2 ++#define MT9P031_OUTPUT_CONTROL_SYN 1 ++#define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 ++#define MT9P031_SHUTTER_WIDTH_UPPER 0x08 ++#define MT9P031_SHUTTER_WIDTH_LOWER 0x09 ++#define MT9P031_SHUTTER_WIDTH_MIN 1 ++#define MT9P031_SHUTTER_WIDTH_MAX 1048575 ++#define MT9P031_SHUTTER_WIDTH_DEF 1943 ++#define MT9P031_PLL_CONTROL 0x10 ++#define MT9P031_PLL_CONTROL_PWROFF 0x0050 ++#define MT9P031_PLL_CONTROL_PWRON 0x0051 ++#define MT9P031_PLL_CONTROL_USEPLL 0x0052 ++#define MT9P031_PLL_CONFIG_1 0x11 ++#define MT9P031_PLL_CONFIG_2 0x12 ++#define MT9P031_PIXEL_CLOCK_CONTROL 0x0a ++#define MT9P031_FRAME_RESTART 0x0b ++#define MT9P031_SHUTTER_DELAY 0x0c ++#define MT9P031_RST 0x0d ++#define MT9P031_RST_ENABLE 1 ++#define MT9P031_RST_DISABLE 0 ++#define MT9P031_READ_MODE_1 0x1e ++#define MT9P031_READ_MODE_2 0x20 ++#define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) ++#define MT9P031_READ_MODE_2_COL_MIR (1 << 14) ++#define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) ++#define MT9P031_ROW_ADDRESS_MODE 0x22 ++#define MT9P031_COLUMN_ADDRESS_MODE 0x23 ++#define MT9P031_GLOBAL_GAIN 0x35 ++#define MT9P031_GLOBAL_GAIN_MIN 8 ++#define MT9P031_GLOBAL_GAIN_MAX 1024 ++#define MT9P031_GLOBAL_GAIN_DEF 8 ++#define MT9P031_GLOBAL_GAIN_MULT (1 << 6) ++#define MT9P031_ROW_BLACK_TARGET 0x49 ++#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b ++#define MT9P031_GREEN1_OFFSET 0x60 ++#define MT9P031_GREEN2_OFFSET 0x61 ++#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 ++#define MT9P031_BLC_MANUAL_BLC (1 << 0) ++#define MT9P031_RED_OFFSET 0x63 ++#define MT9P031_BLUE_OFFSET 0x64 ++#define MT9P031_TEST_PATTERN 0xa0 ++#define MT9P031_TEST_PATTERN_SHIFT 3 ++#define MT9P031_TEST_PATTERN_ENABLE (1 << 0) ++#define MT9P031_TEST_PATTERN_DISABLE (0 << 0) ++#define MT9P031_TEST_PATTERN_GREEN 0xa1 ++#define MT9P031_TEST_PATTERN_RED 0xa2 ++#define MT9P031_TEST_PATTERN_BLUE 0xa3 ++ ++enum mt9p031_model { ++ MT9P031_MODEL_COLOR, ++ MT9P031_MODEL_MONOCHROME, ++}; ++ ++struct mt9p031 { ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ struct v4l2_rect crop; /* Sensor window */ ++ struct v4l2_mbus_framefmt format; ++ struct mt9p031_platform_data *pdata; ++ struct mutex power_lock; /* lock to protect power_count */ ++ int power_count; ++ ++ enum mt9p031_model model; ++ struct aptina_pll pll; ++ int reset; ++ ++ struct v4l2_ctrl_handler ctrls; ++ struct v4l2_ctrl *blc_auto; ++ struct v4l2_ctrl *blc_offset; ++ ++ /* Registers cache */ ++ u16 output_control; ++ u16 mode2; ++}; ++ ++static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct mt9p031, subdev); ++} ++ ++static int mt9p031_read(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, ++ u16 set) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ u16 value = (mt9p031->output_control & ~clear) | set; ++ int ret; ++ ++ ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); ++ if (ret < 0) ++ return ret; ++ ++ mt9p031->output_control = value; ++ return 0; ++} ++ ++static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ u16 value = (mt9p031->mode2 & ~clear) | set; ++ int ret; ++ ++ ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); ++ if (ret < 0) ++ return ret; ++ ++ mt9p031->mode2 = value; ++ return 0; ++} ++ ++static int mt9p031_reset(struct mt9p031 *mt9p031) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ int ret; ++ ++ /* Disable chip output, synchronous option update */ ++ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, ++ 0); ++} ++ ++static int mt9p031_pll_setup(struct mt9p031 *mt9p031) ++{ ++ static const struct aptina_pll_limits limits = { ++ .ext_clock_min = 6000000, ++ .ext_clock_max = 27000000, ++ .int_clock_min = 2000000, ++ .int_clock_max = 13500000, ++ .out_clock_min = 180000000, ++ .out_clock_max = 360000000, ++ .pix_clock_max = 96000000, ++ .n_min = 1, ++ .n_max = 64, ++ .m_min = 16, ++ .m_max = 255, ++ .p1_min = 1, ++ .p1_max = 128, ++ }; ++ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ struct mt9p031_platform_data *pdata = mt9p031->pdata; ++ ++ mt9p031->pll.ext_clock = pdata->ext_freq; ++ mt9p031->pll.pix_clock = pdata->target_freq; ++ ++ return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); ++} ++ ++static int mt9p031_pll_enable(struct mt9p031 *mt9p031) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ int ret; ++ ++ ret = mt9p031_write(client, MT9P031_PLL_CONTROL, ++ MT9P031_PLL_CONTROL_PWRON); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, ++ (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); ++ if (ret < 0) ++ return ret; ++ ++ usleep_range(1000, 2000); ++ ret = mt9p031_write(client, MT9P031_PLL_CONTROL, ++ MT9P031_PLL_CONTROL_PWRON | ++ MT9P031_PLL_CONTROL_USEPLL); ++ return ret; ++} ++ ++static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ ++ return mt9p031_write(client, MT9P031_PLL_CONTROL, ++ MT9P031_PLL_CONTROL_PWROFF); ++} ++ ++static int mt9p031_power_on(struct mt9p031 *mt9p031) ++{ ++ /* Ensure RESET_BAR is low */ ++ if (mt9p031->reset != -1) { ++ gpio_set_value(mt9p031->reset, 0); ++ usleep_range(1000, 2000); ++ } ++ ++ /* Emable clock */ ++ if (mt9p031->pdata->set_xclk) ++ mt9p031->pdata->set_xclk(&mt9p031->subdev, ++ mt9p031->pdata->ext_freq); ++ ++ /* Now RESET_BAR must be high */ ++ if (mt9p031->reset != -1) { ++ gpio_set_value(mt9p031->reset, 1); ++ usleep_range(1000, 2000); ++ } ++ ++ return 0; ++} ++ ++static void mt9p031_power_off(struct mt9p031 *mt9p031) ++{ ++ if (mt9p031->reset != -1) { ++ gpio_set_value(mt9p031->reset, 0); ++ usleep_range(1000, 2000); ++ } ++ ++ if (mt9p031->pdata->set_xclk) ++ mt9p031->pdata->set_xclk(&mt9p031->subdev, 0); ++} ++ ++static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ int ret; ++ ++ if (!on) { ++ mt9p031_power_off(mt9p031); ++ return 0; ++ } ++ ++ ret = mt9p031_power_on(mt9p031); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9p031_reset(mt9p031); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed to reset the camera\n"); ++ return ret; ++ } ++ ++ return v4l2_ctrl_handler_setup(&mt9p031->ctrls); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev video operations ++ */ ++ ++static int mt9p031_set_params(struct mt9p031 *mt9p031) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ struct v4l2_mbus_framefmt *format = &mt9p031->format; ++ const struct v4l2_rect *crop = &mt9p031->crop; ++ unsigned int hblank; ++ unsigned int vblank; ++ unsigned int xskip; ++ unsigned int yskip; ++ unsigned int xbin; ++ unsigned int ybin; ++ int ret; ++ ++ /* Windows position and size. ++ * ++ * TODO: Make sure the start coordinates and window size match the ++ * skipping, binning and mirroring (see description of registers 2 and 4 ++ * in table 13, and Binning section on page 41). ++ */ ++ ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Row and column binning and skipping. Use the maximum binning value ++ * compatible with the skipping settings. ++ */ ++ xskip = DIV_ROUND_CLOSEST(crop->width, format->width); ++ yskip = DIV_ROUND_CLOSEST(crop->height, format->height); ++ xbin = 1 << (ffs(xskip) - 1); ++ ybin = 1 << (ffs(yskip) - 1); ++ ++ ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, ++ ((xbin - 1) << 4) | (xskip - 1)); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, ++ ((ybin - 1) << 4) | (yskip - 1)); ++ if (ret < 0) ++ return ret; ++ ++ /* Blanking - use minimum value for horizontal blanking and default ++ * value for vertical blanking. ++ */ ++ hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3)); ++ vblank = MT9P031_VERTICAL_BLANK_DEF; ++ ++ ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1); ++ if (ret < 0) ++ return ret; ++ ++ return ret; ++} ++ ++static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ int ret; ++ ++ if (!enable) { ++ /* Stop sensor readout */ ++ ret = mt9p031_set_output_control(mt9p031, ++ MT9P031_OUTPUT_CONTROL_CEN, 0); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_pll_disable(mt9p031); ++ } ++ ++ ret = mt9p031_set_params(mt9p031); ++ if (ret < 0) ++ return ret; ++ ++ /* Switch to master "normal" mode */ ++ ret = mt9p031_set_output_control(mt9p031, 0, ++ MT9P031_OUTPUT_CONTROL_CEN); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_pll_enable(mt9p031); ++} ++ ++static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ ++ if (code->pad || code->index) ++ return -EINVAL; ++ ++ code->code = mt9p031->format.code; ++ return 0; ++} ++ ++static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ ++ if (fse->index >= 8 || fse->code != mt9p031->format.code) ++ return -EINVAL; ++ ++ fse->min_width = MT9P031_WINDOW_WIDTH_DEF ++ / min_t(unsigned int, 7, fse->index + 1); ++ fse->max_width = fse->min_width; ++ fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); ++ fse->max_height = fse->min_height; ++ ++ return 0; ++} ++ ++static struct v4l2_mbus_framefmt * ++__mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, ++ unsigned int pad, u32 which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_format(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9p031->format; ++ default: ++ return NULL; ++ } ++} ++ ++static struct v4l2_rect * ++__mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, ++ unsigned int pad, u32 which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9p031->crop; ++ default: ++ return NULL; ++ } ++} ++ ++static int mt9p031_get_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ ++ fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, ++ fmt->which); ++ return 0; ++} ++ ++static int mt9p031_set_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ unsigned int width; ++ unsigned int height; ++ unsigned int hratio; ++ unsigned int vratio; ++ ++ __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, ++ format->which); ++ ++ /* Clamp the width and height to avoid dividing by zero. */ ++ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), ++ max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), ++ __crop->width); ++ height = clamp_t(unsigned int, ALIGN(format->format.height, 2), ++ max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), ++ __crop->height); ++ ++ hratio = DIV_ROUND_CLOSEST(__crop->width, width); ++ vratio = DIV_ROUND_CLOSEST(__crop->height, height); ++ ++ __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, ++ format->which); ++ __format->width = __crop->width / hratio; ++ __format->height = __crop->height / vratio; ++ ++ format->format = *__format; ++ ++ return 0; ++} ++ ++static int mt9p031_get_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ ++ crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, ++ crop->which); ++ return 0; ++} ++ ++static int mt9p031_set_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ struct v4l2_rect rect; ++ ++ /* Clamp the crop rectangle boundaries and align them to a multiple of 2 ++ * pixels to ensure a GRBG Bayer pattern. ++ */ ++ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, ++ MT9P031_COLUMN_START_MAX); ++ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, ++ MT9P031_ROW_START_MAX); ++ rect.width = clamp(ALIGN(crop->rect.width, 2), ++ MT9P031_WINDOW_WIDTH_MIN, ++ MT9P031_WINDOW_WIDTH_MAX); ++ rect.height = clamp(ALIGN(crop->rect.height, 2), ++ MT9P031_WINDOW_HEIGHT_MIN, ++ MT9P031_WINDOW_HEIGHT_MAX); ++ ++ rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); ++ rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); ++ ++ __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); ++ ++ if (rect.width != __crop->width || rect.height != __crop->height) { ++ /* Reset the output image size if the crop rectangle size has ++ * been modified. ++ */ ++ __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, ++ crop->which); ++ __format->width = rect.width; ++ __format->height = rect.height; ++ } ++ ++ *__crop = rect; ++ crop->rect = rect; ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev control operations ++ */ ++ ++#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) ++#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) ++#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) ++#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) ++ ++static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9p031 *mt9p031 = ++ container_of(ctrl->handler, struct mt9p031, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); ++ u16 data; ++ int ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE: ++ ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, ++ (ctrl->val >> 16) & 0xffff); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, ++ ctrl->val & 0xffff); ++ ++ case V4L2_CID_GAIN: ++ /* Gain is controlled by 2 analog stages and a digital stage. ++ * Valid values for the 3 stages are ++ * ++ * Stage Min Max Step ++ * ------------------------------------------ ++ * First analog stage x1 x2 1 ++ * Second analog stage x1 x4 0.125 ++ * Digital stage x1 x16 0.125 ++ * ++ * To minimize noise, the gain stages should be used in the ++ * second analog stage, first analog stage, digital stage order. ++ * Gain from a previous stage should be pushed to its maximum ++ * value before the next stage is used. ++ */ ++ if (ctrl->val <= 32) { ++ data = ctrl->val; ++ } else if (ctrl->val <= 64) { ++ ctrl->val &= ~1; ++ data = (1 << 6) | (ctrl->val >> 1); ++ } else { ++ ctrl->val &= ~7; ++ data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; ++ } ++ ++ return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); ++ ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ return mt9p031_set_mode2(mt9p031, ++ 0, MT9P031_READ_MODE_2_COL_MIR); ++ else ++ return mt9p031_set_mode2(mt9p031, ++ MT9P031_READ_MODE_2_COL_MIR, 0); ++ ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ return mt9p031_set_mode2(mt9p031, ++ 0, MT9P031_READ_MODE_2_ROW_MIR); ++ else ++ return mt9p031_set_mode2(mt9p031, ++ MT9P031_READ_MODE_2_ROW_MIR, 0); ++ ++ case V4L2_CID_TEST_PATTERN: ++ if (!ctrl->val) { ++ /* Restore the black level compensation settings. */ ++ if (mt9p031->blc_auto->cur.val != 0) { ++ ret = mt9p031_s_ctrl(mt9p031->blc_auto); ++ if (ret < 0) ++ return ret; ++ } ++ if (mt9p031->blc_offset->cur.val != 0) { ++ ret = mt9p031_s_ctrl(mt9p031->blc_offset); ++ if (ret < 0) ++ return ret; ++ } ++ return mt9p031_write(client, MT9P031_TEST_PATTERN, ++ MT9P031_TEST_PATTERN_DISABLE); ++ } ++ ++ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); ++ if (ret < 0) ++ return ret; ++ ++ /* Disable digital black level compensation when using a test ++ * pattern. ++ */ ++ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, ++ 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_write(client, MT9P031_TEST_PATTERN, ++ ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) ++ | MT9P031_TEST_PATTERN_ENABLE); ++ ++ case V4L2_CID_BLC_AUTO: ++ ret = mt9p031_set_mode2(mt9p031, ++ ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, ++ ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); ++ if (ret < 0) ++ return ret; ++ ++ return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, ++ ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); ++ ++ case V4L2_CID_BLC_TARGET_LEVEL: ++ return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, ++ ctrl->val); ++ ++ case V4L2_CID_BLC_ANALOG_OFFSET: ++ data = ctrl->val & ((1 << 9) - 1); ++ ++ ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); ++ if (ret < 0) ++ return ret; ++ ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); ++ if (ret < 0) ++ return ret; ++ return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); ++ ++ case V4L2_CID_BLC_DIGITAL_OFFSET: ++ return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, ++ ctrl->val & ((1 << 12) - 1)); ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { ++ .s_ctrl = mt9p031_s_ctrl, ++}; ++ ++static const char * const mt9p031_test_pattern_menu[] = { ++ "Disabled", ++ "Color Field", ++ "Horizontal Gradient", ++ "Vertical Gradient", ++ "Diagonal Gradient", ++ "Classic Test Pattern", ++ "Walking 1s", ++ "Monochrome Horizontal Bars", ++ "Monochrome Vertical Bars", ++ "Vertical Color Bars", ++}; ++ ++static const struct v4l2_ctrl_config mt9p031_ctrls[] = { ++ { ++ .ops = &mt9p031_ctrl_ops, ++ .id = V4L2_CID_BLC_AUTO, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "BLC, Auto", ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ .def = 1, ++ .flags = 0, ++ }, { ++ .ops = &mt9p031_ctrl_ops, ++ .id = V4L2_CID_BLC_TARGET_LEVEL, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "BLC Target Level", ++ .min = 0, ++ .max = 4095, ++ .step = 1, ++ .def = 168, ++ .flags = 0, ++ }, { ++ .ops = &mt9p031_ctrl_ops, ++ .id = V4L2_CID_BLC_ANALOG_OFFSET, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "BLC Analog Offset", ++ .min = -255, ++ .max = 255, ++ .step = 1, ++ .def = 32, ++ .flags = 0, ++ }, { ++ .ops = &mt9p031_ctrl_ops, ++ .id = V4L2_CID_BLC_DIGITAL_OFFSET, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "BLC Digital Offset", ++ .min = -2048, ++ .max = 2047, ++ .step = 1, ++ .def = 40, ++ .flags = 0, ++ } ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev core operations ++ */ ++ ++static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ int ret = 0; ++ ++ mutex_lock(&mt9p031->power_lock); ++ ++ /* If the power count is modified from 0 to != 0 or from != 0 to 0, ++ * update the power state. ++ */ ++ if (mt9p031->power_count == !on) { ++ ret = __mt9p031_set_power(mt9p031, !!on); ++ if (ret < 0) ++ goto out; ++ } ++ ++ /* Update the power count. */ ++ mt9p031->power_count += on ? 1 : -1; ++ WARN_ON(mt9p031->power_count < 0); ++ ++out: ++ mutex_unlock(&mt9p031->power_lock); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev internal operations ++ */ ++ ++static int mt9p031_registered(struct v4l2_subdev *subdev) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ s32 data; ++ int ret; ++ ++ ret = mt9p031_power_on(mt9p031); ++ if (ret < 0) { ++ dev_err(&client->dev, "MT9P031 power up failed\n"); ++ return ret; ++ } ++ ++ /* Read out the chip version register */ ++ data = mt9p031_read(client, MT9P031_CHIP_VERSION); ++ if (data != MT9P031_CHIP_VERSION_VALUE) { ++ dev_err(&client->dev, "MT9P031 not detected, wrong version " ++ "0x%04x\n", data); ++ return -ENODEV; ++ } ++ ++ mt9p031_power_off(mt9p031); ++ ++ dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", ++ client->addr); ++ ++ return ret; ++} ++ ++static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) ++{ ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ struct v4l2_mbus_framefmt *format; ++ struct v4l2_rect *crop; ++ ++ crop = v4l2_subdev_get_try_crop(fh, 0); ++ crop->left = MT9P031_COLUMN_START_DEF; ++ crop->top = MT9P031_ROW_START_DEF; ++ crop->width = MT9P031_WINDOW_WIDTH_DEF; ++ crop->height = MT9P031_WINDOW_HEIGHT_DEF; ++ ++ format = v4l2_subdev_get_try_format(fh, 0); ++ ++ if (mt9p031->model == MT9P031_MODEL_MONOCHROME) ++ format->code = V4L2_MBUS_FMT_Y12_1X12; ++ else ++ format->code = V4L2_MBUS_FMT_SGRBG12_1X12; ++ ++ format->width = MT9P031_WINDOW_WIDTH_DEF; ++ format->height = MT9P031_WINDOW_HEIGHT_DEF; ++ format->field = V4L2_FIELD_NONE; ++ format->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return mt9p031_set_power(subdev, 1); ++} ++ ++static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) ++{ ++ return mt9p031_set_power(subdev, 0); ++} ++ ++static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { ++ .s_power = mt9p031_set_power, ++}; ++ ++static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { ++ .s_stream = mt9p031_s_stream, ++}; ++ ++static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { ++ .enum_mbus_code = mt9p031_enum_mbus_code, ++ .enum_frame_size = mt9p031_enum_frame_size, ++ .get_fmt = mt9p031_get_format, ++ .set_fmt = mt9p031_set_format, ++ .get_crop = mt9p031_get_crop, ++ .set_crop = mt9p031_set_crop, ++}; ++ ++static struct v4l2_subdev_ops mt9p031_subdev_ops = { ++ .core = &mt9p031_subdev_core_ops, ++ .video = &mt9p031_subdev_video_ops, ++ .pad = &mt9p031_subdev_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { ++ .registered = mt9p031_registered, ++ .open = mt9p031_open, ++ .close = mt9p031_close, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Driver initialization and probing ++ */ ++ ++static int mt9p031_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9p031_platform_data *pdata = client->dev.platform_data; ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct mt9p031 *mt9p031; ++ unsigned int i; ++ int ret; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "No platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&client->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9p031 = kzalloc(sizeof(*mt9p031), GFP_KERNEL); ++ if (mt9p031 == NULL) ++ return -ENOMEM; ++ ++ mt9p031->pdata = pdata; ++ mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; ++ mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; ++ mt9p031->model = did->driver_data; ++ mt9p031->reset = -1; ++ ++ v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6); ++ ++ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, ++ MT9P031_SHUTTER_WIDTH_MAX, 1, ++ MT9P031_SHUTTER_WIDTH_DEF); ++ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, ++ MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); ++ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, pdata->target_freq, ++ pdata->target_freq, 1, pdata->target_freq); ++ v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0, ++ 0, mt9p031_test_pattern_menu); ++ ++ for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) ++ v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); ++ ++ mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; ++ ++ if (mt9p031->ctrls.error) { ++ printk(KERN_INFO "%s: control initialization error %d\n", ++ __func__, mt9p031->ctrls.error); ++ ret = mt9p031->ctrls.error; ++ goto done; ++ } ++ ++ mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); ++ mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, ++ V4L2_CID_BLC_DIGITAL_OFFSET); ++ ++ mutex_init(&mt9p031->power_lock); ++ v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); ++ mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; ++ ++ mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); ++ if (ret < 0) ++ goto done; ++ ++ mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; ++ mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; ++ mt9p031->crop.left = MT9P031_COLUMN_START_DEF; ++ mt9p031->crop.top = MT9P031_ROW_START_DEF; ++ ++ if (mt9p031->model == MT9P031_MODEL_MONOCHROME) ++ mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; ++ else ++ mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; ++ ++ mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; ++ mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; ++ mt9p031->format.field = V4L2_FIELD_NONE; ++ mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; ++ ++ if (pdata->reset != -1) { ++ ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, ++ "mt9p031_rst"); ++ if (ret < 0) ++ goto done; ++ ++ mt9p031->reset = pdata->reset; ++ } ++ ++ ret = mt9p031_pll_setup(mt9p031); ++ ++done: ++ if (ret < 0) { ++ if (mt9p031->reset != -1) ++ gpio_free(mt9p031->reset); ++ ++ v4l2_ctrl_handler_free(&mt9p031->ctrls); ++ media_entity_cleanup(&mt9p031->subdev.entity); ++ kfree(mt9p031); ++ } ++ ++ return ret; ++} ++ ++static int mt9p031_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct mt9p031 *mt9p031 = to_mt9p031(subdev); ++ ++ v4l2_ctrl_handler_free(&mt9p031->ctrls); ++ v4l2_device_unregister_subdev(subdev); ++ media_entity_cleanup(&subdev->entity); ++ if (mt9p031->reset != -1) ++ gpio_free(mt9p031->reset); ++ kfree(mt9p031); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id mt9p031_id[] = { ++ { "mt9p031", MT9P031_MODEL_COLOR }, ++ { "mt9p031m", MT9P031_MODEL_MONOCHROME }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9p031_id); ++ ++static struct i2c_driver mt9p031_i2c_driver = { ++ .driver = { ++ .name = "mt9p031", ++ }, ++ .probe = mt9p031_probe, ++ .remove = mt9p031_remove, ++ .id_table = mt9p031_id, ++}; ++ ++module_i2c_driver(mt9p031_i2c_driver); ++ ++MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); ++MODULE_AUTHOR("Bastian Hecht "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c +new file mode 100644 +index 0000000..2e189d8 +--- /dev/null ++++ b/drivers/media/i2c/mt9t001.c +@@ -0,0 +1,841 @@ ++/* ++ * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) ++ * ++ * Copyright (C) 2010-2011, Laurent Pinchart ++ * ++ * Based on the MT9M001 driver, ++ * ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 ++#define MT9T001_PIXEL_ARRAY_WIDTH 2112 ++ ++#define MT9T001_CHIP_VERSION 0x00 ++#define MT9T001_CHIP_ID 0x1621 ++#define MT9T001_ROW_START 0x01 ++#define MT9T001_ROW_START_MIN 0 ++#define MT9T001_ROW_START_DEF 20 ++#define MT9T001_ROW_START_MAX 1534 ++#define MT9T001_COLUMN_START 0x02 ++#define MT9T001_COLUMN_START_MIN 0 ++#define MT9T001_COLUMN_START_DEF 32 ++#define MT9T001_COLUMN_START_MAX 2046 ++#define MT9T001_WINDOW_HEIGHT 0x03 ++#define MT9T001_WINDOW_HEIGHT_MIN 1 ++#define MT9T001_WINDOW_HEIGHT_DEF 1535 ++#define MT9T001_WINDOW_HEIGHT_MAX 1567 ++#define MT9T001_WINDOW_WIDTH 0x04 ++#define MT9T001_WINDOW_WIDTH_MIN 1 ++#define MT9T001_WINDOW_WIDTH_DEF 2047 ++#define MT9T001_WINDOW_WIDTH_MAX 2111 ++#define MT9T001_HORIZONTAL_BLANKING 0x05 ++#define MT9T001_HORIZONTAL_BLANKING_MIN 21 ++#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 ++#define MT9T001_VERTICAL_BLANKING 0x06 ++#define MT9T001_VERTICAL_BLANKING_MIN 3 ++#define MT9T001_VERTICAL_BLANKING_MAX 1023 ++#define MT9T001_OUTPUT_CONTROL 0x07 ++#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) ++#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) ++#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) ++#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 ++#define MT9T001_SHUTTER_WIDTH_LOW 0x09 ++#define MT9T001_SHUTTER_WIDTH_MIN 1 ++#define MT9T001_SHUTTER_WIDTH_DEF 1561 ++#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) ++#define MT9T001_PIXEL_CLOCK 0x0a ++#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) ++#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) ++#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 ++#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) ++#define MT9T001_FRAME_RESTART 0x0b ++#define MT9T001_SHUTTER_DELAY 0x0c ++#define MT9T001_SHUTTER_DELAY_MAX 2047 ++#define MT9T001_RESET 0x0d ++#define MT9T001_READ_MODE1 0x1e ++#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) ++#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) ++#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) ++#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) ++#define MT9T001_READ_MODE2 0x20 ++#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) ++#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) ++#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) ++#define MT9T001_READ_MODE3 0x21 ++#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) ++#define MT9T001_READ_MODE_GHST_CTL (1 << 1) ++#define MT9T001_ROW_ADDRESS_MODE 0x22 ++#define MT9T001_ROW_SKIP_MASK (7 << 0) ++#define MT9T001_ROW_BIN_MASK (3 << 3) ++#define MT9T001_ROW_BIN_SHIFT 3 ++#define MT9T001_COLUMN_ADDRESS_MODE 0x23 ++#define MT9T001_COLUMN_SKIP_MASK (7 << 0) ++#define MT9T001_COLUMN_BIN_MASK (3 << 3) ++#define MT9T001_COLUMN_BIN_SHIFT 3 ++#define MT9T001_GREEN1_GAIN 0x2b ++#define MT9T001_BLUE_GAIN 0x2c ++#define MT9T001_RED_GAIN 0x2d ++#define MT9T001_GREEN2_GAIN 0x2e ++#define MT9T001_TEST_DATA 0x32 ++#define MT9T001_GLOBAL_GAIN 0x35 ++#define MT9T001_GLOBAL_GAIN_MIN 8 ++#define MT9T001_GLOBAL_GAIN_MAX 1024 ++#define MT9T001_BLACK_LEVEL 0x49 ++#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b ++#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d ++#define MT9T001_CAL_THRESHOLDS 0x5f ++#define MT9T001_GREEN1_OFFSET 0x60 ++#define MT9T001_GREEN2_OFFSET 0x61 ++#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 ++#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) ++#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) ++#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) ++#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) ++#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) ++#define MT9T001_RED_OFFSET 0x63 ++#define MT9T001_BLUE_OFFSET 0x64 ++ ++struct mt9t001 { ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ ++ struct v4l2_mbus_framefmt format; ++ struct v4l2_rect crop; ++ ++ struct v4l2_ctrl_handler ctrls; ++ struct v4l2_ctrl *gains[4]; ++ ++ u16 output_control; ++ u16 black_level; ++}; ++ ++static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct mt9t001, subdev); ++} ++ ++static int mt9t001_read(struct i2c_client *client, u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, ++ u16 set) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); ++ u16 value = (mt9t001->output_control & ~clear) | set; ++ int ret; ++ ++ if (value == mt9t001->output_control) ++ return 0; ++ ++ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); ++ if (ret < 0) ++ return ret; ++ ++ mt9t001->output_control = value; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev video operations ++ */ ++ ++static struct v4l2_mbus_framefmt * ++__mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_format(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9t001->format; ++ default: ++ return NULL; ++ } ++} ++ ++static struct v4l2_rect * ++__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9t001->crop; ++ default: ++ return NULL; ++ } ++} ++ ++static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) ++{ ++ const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ struct v4l2_mbus_framefmt *format = &mt9t001->format; ++ struct v4l2_rect *crop = &mt9t001->crop; ++ unsigned int hratio; ++ unsigned int vratio; ++ int ret; ++ ++ if (!enable) ++ return mt9t001_set_output_control(mt9t001, mode, 0); ++ ++ /* Configure the window size and row/column bin */ ++ hratio = DIV_ROUND_CLOSEST(crop->width, format->width); ++ vratio = DIV_ROUND_CLOSEST(crop->height, format->height); ++ ++ ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Switch to master "normal" mode */ ++ return mt9t001_set_output_control(mt9t001, 0, mode); ++} ++ ++static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index > 0) ++ return -EINVAL; ++ ++ code->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ return 0; ++} ++ ++static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) ++ return -EINVAL; ++ ++ fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; ++ fse->max_width = fse->min_width; ++ fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; ++ fse->max_height = fse->min_height; ++ ++ return 0; ++} ++ ++static int mt9t001_get_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format) ++{ ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ ++ format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad, ++ format->which); ++ return 0; ++} ++ ++static int mt9t001_set_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format) ++{ ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ unsigned int width; ++ unsigned int height; ++ unsigned int hratio; ++ unsigned int vratio; ++ ++ __crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad, ++ format->which); ++ ++ /* Clamp the width and height to avoid dividing by zero. */ ++ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), ++ max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), ++ __crop->width); ++ height = clamp_t(unsigned int, ALIGN(format->format.height, 2), ++ max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1), ++ __crop->height); ++ ++ hratio = DIV_ROUND_CLOSEST(__crop->width, width); ++ vratio = DIV_ROUND_CLOSEST(__crop->height, height); ++ ++ __format = __mt9t001_get_pad_format(mt9t001, fh, format->pad, ++ format->which); ++ __format->width = __crop->width / hratio; ++ __format->height = __crop->height / vratio; ++ ++ format->format = *__format; ++ ++ return 0; ++} ++ ++static int mt9t001_get_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ ++ crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, ++ crop->which); ++ return 0; ++} ++ ++static int mt9t001_set_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ struct v4l2_rect rect; ++ ++ /* Clamp the crop rectangle boundaries and align them to a multiple of 2 ++ * pixels. ++ */ ++ rect.left = clamp(ALIGN(crop->rect.left, 2), ++ MT9T001_COLUMN_START_MIN, ++ MT9T001_COLUMN_START_MAX); ++ rect.top = clamp(ALIGN(crop->rect.top, 2), ++ MT9T001_ROW_START_MIN, ++ MT9T001_ROW_START_MAX); ++ rect.width = clamp(ALIGN(crop->rect.width, 2), ++ MT9T001_WINDOW_WIDTH_MIN + 1, ++ MT9T001_WINDOW_WIDTH_MAX + 1); ++ rect.height = clamp(ALIGN(crop->rect.height, 2), ++ MT9T001_WINDOW_HEIGHT_MIN + 1, ++ MT9T001_WINDOW_HEIGHT_MAX + 1); ++ ++ rect.width = min(rect.width, MT9T001_PIXEL_ARRAY_WIDTH - rect.left); ++ rect.height = min(rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); ++ ++ __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); ++ ++ if (rect.width != __crop->width || rect.height != __crop->height) { ++ /* Reset the output image size if the crop rectangle size has ++ * been modified. ++ */ ++ __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, ++ crop->which); ++ __format->width = rect.width; ++ __format->height = rect.height; ++ } ++ ++ *__crop = rect; ++ crop->rect = rect; ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev control operations ++ */ ++ ++#define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) ++#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) ++#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) ++#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) ++ ++#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) ++#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) ++#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) ++#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) ++ ++static u16 mt9t001_gain_value(s32 *gain) ++{ ++ /* Gain is controlled by 2 analog stages and a digital stage. Valid ++ * values for the 3 stages are ++ * ++ * Stage Min Max Step ++ * ------------------------------------------ ++ * First analog stage x1 x2 1 ++ * Second analog stage x1 x4 0.125 ++ * Digital stage x1 x16 0.125 ++ * ++ * To minimize noise, the gain stages should be used in the second ++ * analog stage, first analog stage, digital stage order. Gain from a ++ * previous stage should be pushed to its maximum value before the next ++ * stage is used. ++ */ ++ if (*gain <= 32) ++ return *gain; ++ ++ if (*gain <= 64) { ++ *gain &= ~1; ++ return (1 << 6) | (*gain >> 1); ++ } ++ ++ *gain &= ~7; ++ return ((*gain - 64) << 5) | (1 << 6) | 32; ++} ++ ++static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) ++{ ++ return mt9t001_set_output_control(mt9t001, ++ freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, ++ freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); ++} ++ ++static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ static const u8 gains[4] = { ++ MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, ++ MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN ++ }; ++ ++ struct mt9t001 *mt9t001 = ++ container_of(ctrl->handler, struct mt9t001, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); ++ unsigned int count; ++ unsigned int i; ++ u16 value; ++ int ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN_RED: ++ case V4L2_CID_GAIN_GREEN_RED: ++ case V4L2_CID_GAIN_GREEN_BLUE: ++ case V4L2_CID_GAIN_BLUE: ++ ++ /* Disable control updates if more than one control has changed ++ * in the cluster. ++ */ ++ for (i = 0, count = 0; i < 4; ++i) { ++ struct v4l2_ctrl *gain = mt9t001->gains[i]; ++ ++ if (gain->val != gain->cur.val) ++ count++; ++ } ++ ++ if (count > 1) { ++ ret = mt9t001_ctrl_freeze(mt9t001, true); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Update the gain controls. */ ++ for (i = 0; i < 4; ++i) { ++ struct v4l2_ctrl *gain = mt9t001->gains[i]; ++ ++ if (gain->val == gain->cur.val) ++ continue; ++ ++ value = mt9t001_gain_value(&gain->val); ++ ret = mt9t001_write(client, gains[i], value); ++ if (ret < 0) { ++ mt9t001_ctrl_freeze(mt9t001, false); ++ return ret; ++ } ++ } ++ ++ /* Enable control updates. */ ++ if (count > 1) { ++ ret = mt9t001_ctrl_freeze(mt9t001, false); ++ if (ret < 0) ++ return ret; ++ } ++ ++ break; ++ ++ case V4L2_CID_EXPOSURE: ++ ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, ++ ctrl->val & 0xffff); ++ if (ret < 0) ++ return ret; ++ ++ return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, ++ ctrl->val >> 16); ++ ++ case V4L2_CID_TEST_PATTERN: ++ return mt9t001_set_output_control(mt9t001, ++ ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, ++ ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); ++ ++ case V4L2_CID_TEST_PATTERN_COLOR: ++ return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); ++ ++ case V4L2_CID_BLACK_LEVEL_AUTO: ++ value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; ++ ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, ++ value); ++ if (ret < 0) ++ return ret; ++ ++ mt9t001->black_level = value; ++ break; ++ ++ case V4L2_CID_BLACK_LEVEL_OFFSET: ++ ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); ++ if (ret < 0) ++ return ret; ++ ++ return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); ++ ++ case V4L2_CID_BLACK_LEVEL_CALIBRATE: ++ return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, ++ MT9T001_BLACK_LEVEL_RECALCULATE | ++ mt9t001->black_level); ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_ctrl_ops mt9t001_ctrl_ops = { ++ .s_ctrl = mt9t001_s_ctrl, ++}; ++ ++static const char * const mt9t001_test_pattern_menu[] = { ++ "Disabled", ++ "Enabled", ++}; ++ ++static const struct v4l2_ctrl_config mt9t001_ctrls[] = { ++ { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_TEST_PATTERN_COLOR, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Test Pattern Color", ++ .min = 0, ++ .max = 1023, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_BLACK_LEVEL_AUTO, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Black Level, Auto", ++ .min = 0, ++ .max = 1, ++ .step = 1, ++ .def = 1, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_BLACK_LEVEL_OFFSET, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Black Level, Offset", ++ .min = -256, ++ .max = 255, ++ .step = 1, ++ .def = 32, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, ++ .type = V4L2_CTRL_TYPE_BUTTON, ++ .name = "Black Level, Calibrate", ++ .min = 0, ++ .max = 0, ++ .step = 0, ++ .def = 0, ++ .flags = V4L2_CTRL_FLAG_WRITE_ONLY, ++ }, ++}; ++ ++static const struct v4l2_ctrl_config mt9t001_gains[] = { ++ { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_GAIN_RED, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Red", ++ .min = MT9T001_GLOBAL_GAIN_MIN, ++ .max = MT9T001_GLOBAL_GAIN_MAX, ++ .step = 1, ++ .def = MT9T001_GLOBAL_GAIN_MIN, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_GAIN_GREEN_RED, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Green (R)", ++ .min = MT9T001_GLOBAL_GAIN_MIN, ++ .max = MT9T001_GLOBAL_GAIN_MAX, ++ .step = 1, ++ .def = MT9T001_GLOBAL_GAIN_MIN, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_GAIN_GREEN_BLUE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Green (B)", ++ .min = MT9T001_GLOBAL_GAIN_MIN, ++ .max = MT9T001_GLOBAL_GAIN_MAX, ++ .step = 1, ++ .def = MT9T001_GLOBAL_GAIN_MIN, ++ .flags = 0, ++ }, { ++ .ops = &mt9t001_ctrl_ops, ++ .id = V4L2_CID_GAIN_BLUE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Blue", ++ .min = MT9T001_GLOBAL_GAIN_MIN, ++ .max = MT9T001_GLOBAL_GAIN_MAX, ++ .step = 1, ++ .def = MT9T001_GLOBAL_GAIN_MIN, ++ .flags = 0, ++ }, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev internal operations ++ */ ++ ++static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *format; ++ struct v4l2_rect *crop; ++ ++ crop = v4l2_subdev_get_try_crop(fh, 0); ++ crop->left = MT9T001_COLUMN_START_DEF; ++ crop->top = MT9T001_ROW_START_DEF; ++ crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; ++ crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; ++ ++ format = v4l2_subdev_get_try_format(fh, 0); ++ format->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format->width = MT9T001_WINDOW_WIDTH_DEF + 1; ++ format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; ++ format->field = V4L2_FIELD_NONE; ++ format->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { ++ .s_stream = mt9t001_s_stream, ++}; ++ ++static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { ++ .enum_mbus_code = mt9t001_enum_mbus_code, ++ .enum_frame_size = mt9t001_enum_frame_size, ++ .get_fmt = mt9t001_get_format, ++ .set_fmt = mt9t001_set_format, ++ .get_crop = mt9t001_get_crop, ++ .set_crop = mt9t001_set_crop, ++}; ++ ++static struct v4l2_subdev_ops mt9t001_subdev_ops = { ++ .video = &mt9t001_subdev_video_ops, ++ .pad = &mt9t001_subdev_pad_ops, ++}; ++ ++static struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { ++ .open = mt9t001_open, ++}; ++ ++static int mt9t001_video_probe(struct i2c_client *client) ++{ ++ struct mt9t001_platform_data *pdata = client->dev.platform_data; ++ s32 data; ++ int ret; ++ ++ dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n", ++ client->addr); ++ ++ /* Reset the chip and stop data read out */ ++ ret = mt9t001_write(client, MT9T001_RESET, 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_RESET, 0); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0); ++ if (ret < 0) ++ return ret; ++ ++ /* Configure the pixel clock polarity */ ++ if (pdata->clk_pol) { ++ ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, ++ MT9T001_PIXEL_CLOCK_INVERT); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Read and check the sensor version */ ++ data = mt9t001_read(client, MT9T001_CHIP_VERSION); ++ if (data != MT9T001_CHIP_ID) { ++ dev_err(&client->dev, "MT9T001 not detected, wrong version " ++ "0x%04x\n", data); ++ return -ENODEV; ++ } ++ ++ dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", ++ client->addr); ++ ++ return ret; ++} ++ ++static int mt9t001_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9t001_platform_data *pdata = client->dev.platform_data; ++ struct mt9t001 *mt9t001; ++ unsigned int i; ++ int ret; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "No platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&client->adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ ret = mt9t001_video_probe(client); ++ if (ret < 0) ++ return ret; ++ ++ mt9t001 = kzalloc(sizeof(*mt9t001), GFP_KERNEL); ++ if (!mt9t001) ++ return -ENOMEM; ++ ++ v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + ++ ARRAY_SIZE(mt9t001_gains) + 4); ++ ++ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, ++ V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, ++ MT9T001_SHUTTER_WIDTH_MAX, 1, ++ MT9T001_SHUTTER_WIDTH_DEF); ++ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, ++ V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); ++ v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, ++ 1, pdata->ext_clk); ++ v4l2_ctrl_new_std_menu_items(&mt9t001->ctrls, &mt9t001_ctrl_ops, ++ V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(mt9t001_test_pattern_menu) - 1, 0, ++ 0, mt9t001_test_pattern_menu); ++ ++ for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) ++ v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); ++ ++ for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) ++ mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, ++ &mt9t001_gains[i], NULL); ++ ++ v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); ++ ++ mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; ++ ++ if (mt9t001->ctrls.error) { ++ printk(KERN_INFO "%s: control initialization error %d\n", ++ __func__, mt9t001->ctrls.error); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ mt9t001->crop.left = MT9T001_COLUMN_START_DEF; ++ mt9t001->crop.top = MT9T001_ROW_START_DEF; ++ mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; ++ mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; ++ ++ mt9t001->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; ++ mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; ++ mt9t001->format.field = V4L2_FIELD_NONE; ++ mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; ++ ++ v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); ++ mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; ++ mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_init(&mt9t001->subdev.entity, 1, &mt9t001->pad, 0); ++ ++done: ++ if (ret < 0) { ++ v4l2_ctrl_handler_free(&mt9t001->ctrls); ++ media_entity_cleanup(&mt9t001->subdev.entity); ++ kfree(mt9t001); ++ } ++ ++ return ret; ++} ++ ++static int mt9t001_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct mt9t001 *mt9t001 = to_mt9t001(subdev); ++ ++ v4l2_ctrl_handler_free(&mt9t001->ctrls); ++ v4l2_device_unregister_subdev(subdev); ++ media_entity_cleanup(&subdev->entity); ++ kfree(mt9t001); ++ return 0; ++} ++ ++static const struct i2c_device_id mt9t001_id[] = { ++ { "mt9t001", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9t001_id); ++ ++static struct i2c_driver mt9t001_driver = { ++ .driver = { ++ .name = "mt9t001", ++ }, ++ .probe = mt9t001_probe, ++ .remove = mt9t001_remove, ++ .id_table = mt9t001_id, ++}; ++ ++module_i2c_driver(mt9t001_driver); ++ ++MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c +new file mode 100644 +index 0000000..6bf01ad +--- /dev/null ++++ b/drivers/media/i2c/mt9v011.c +@@ -0,0 +1,712 @@ ++/* ++ * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor ++ * ++ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) ++ * This code is placed under the terms of the GNU General Public License v2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Micron mt9v011 sensor driver"); ++MODULE_AUTHOR("Mauro Carvalho Chehab "); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-2)"); ++ ++#define R00_MT9V011_CHIP_VERSION 0x00 ++#define R01_MT9V011_ROWSTART 0x01 ++#define R02_MT9V011_COLSTART 0x02 ++#define R03_MT9V011_HEIGHT 0x03 ++#define R04_MT9V011_WIDTH 0x04 ++#define R05_MT9V011_HBLANK 0x05 ++#define R06_MT9V011_VBLANK 0x06 ++#define R07_MT9V011_OUT_CTRL 0x07 ++#define R09_MT9V011_SHUTTER_WIDTH 0x09 ++#define R0A_MT9V011_CLK_SPEED 0x0a ++#define R0B_MT9V011_RESTART 0x0b ++#define R0C_MT9V011_SHUTTER_DELAY 0x0c ++#define R0D_MT9V011_RESET 0x0d ++#define R1E_MT9V011_DIGITAL_ZOOM 0x1e ++#define R20_MT9V011_READ_MODE 0x20 ++#define R2B_MT9V011_GREEN_1_GAIN 0x2b ++#define R2C_MT9V011_BLUE_GAIN 0x2c ++#define R2D_MT9V011_RED_GAIN 0x2d ++#define R2E_MT9V011_GREEN_2_GAIN 0x2e ++#define R35_MT9V011_GLOBAL_GAIN 0x35 ++#define RF1_MT9V011_CHIP_ENABLE 0xf1 ++ ++#define MT9V011_VERSION 0x8232 ++#define MT9V011_REV_B_VERSION 0x8243 ++ ++/* supported controls */ ++static struct v4l2_queryctrl mt9v011_qctrl[] = { ++ { ++ .id = V4L2_CID_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain", ++ .minimum = 0, ++ .maximum = (1 << 12) - 1 - 0x0020, ++ .step = 1, ++ .default_value = 0x0020, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Exposure", ++ .minimum = 0, ++ .maximum = 2047, ++ .step = 1, ++ .default_value = 0x01fc, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_RED_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Red Balance", ++ .minimum = -1 << 9, ++ .maximum = (1 << 9) - 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_BLUE_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Blue Balance", ++ .minimum = -1 << 9, ++ .maximum = (1 << 9) - 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Mirror", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Vflip", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 0, ++ .flags = 0, ++ }, { ++ } ++}; ++ ++struct mt9v011 { ++ struct v4l2_subdev sd; ++ unsigned width, height; ++ unsigned xtal; ++ unsigned hflip:1; ++ unsigned vflip:1; ++ ++ u16 global_gain, exposure; ++ s16 red_bal, blue_bal; ++}; ++ ++static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct mt9v011, sd); ++} ++ ++static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ __be16 buffer; ++ int rc, val; ++ ++ rc = i2c_master_send(c, &addr, 1); ++ if (rc != 1) ++ v4l2_dbg(0, debug, sd, ++ "i2c i/o error: rc == %d (should be 1)\n", rc); ++ ++ msleep(10); ++ ++ rc = i2c_master_recv(c, (char *)&buffer, 2); ++ if (rc != 2) ++ v4l2_dbg(0, debug, sd, ++ "i2c i/o error: rc == %d (should be 2)\n", rc); ++ ++ val = be16_to_cpu(buffer); ++ ++ v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val); ++ ++ return val; ++} ++ ++static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr, ++ u16 value) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char buffer[3]; ++ int rc; ++ ++ buffer[0] = addr; ++ buffer[1] = value >> 8; ++ buffer[2] = value & 0xff; ++ ++ v4l2_dbg(2, debug, sd, ++ "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value); ++ rc = i2c_master_send(c, buffer, 3); ++ if (rc != 3) ++ v4l2_dbg(0, debug, sd, ++ "i2c i/o error: rc == %d (should be 3)\n", rc); ++} ++ ++ ++struct i2c_reg_value { ++ unsigned char reg; ++ u16 value; ++}; ++ ++/* ++ * Values used at the original driver ++ * Some values are marked as Reserved at the datasheet ++ */ ++static const struct i2c_reg_value mt9v011_init_default[] = { ++ { R0D_MT9V011_RESET, 0x0001 }, ++ { R0D_MT9V011_RESET, 0x0000 }, ++ ++ { R0C_MT9V011_SHUTTER_DELAY, 0x0000 }, ++ { R09_MT9V011_SHUTTER_WIDTH, 0x1fc }, ++ ++ { R0A_MT9V011_CLK_SPEED, 0x0000 }, ++ { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, ++ ++ { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ ++}; ++ ++ ++static u16 calc_mt9v011_gain(s16 lineargain) ++{ ++ ++ u16 digitalgain = 0; ++ u16 analogmult = 0; ++ u16 analoginit = 0; ++ ++ if (lineargain < 0) ++ lineargain = 0; ++ ++ /* recommended minimum */ ++ lineargain += 0x0020; ++ ++ if (lineargain > 2047) ++ lineargain = 2047; ++ ++ if (lineargain > 1023) { ++ digitalgain = 3; ++ analogmult = 3; ++ analoginit = lineargain / 16; ++ } else if (lineargain > 511) { ++ digitalgain = 1; ++ analogmult = 3; ++ analoginit = lineargain / 8; ++ } else if (lineargain > 255) { ++ analogmult = 3; ++ analoginit = lineargain / 4; ++ } else if (lineargain > 127) { ++ analogmult = 1; ++ analoginit = lineargain / 2; ++ } else ++ analoginit = lineargain; ++ ++ return analoginit + (analogmult << 7) + (digitalgain << 9); ++ ++} ++ ++static void set_balance(struct v4l2_subdev *sd) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ u16 green_gain, blue_gain, red_gain; ++ u16 exposure; ++ s16 bal; ++ ++ exposure = core->exposure; ++ ++ green_gain = calc_mt9v011_gain(core->global_gain); ++ ++ bal = core->global_gain; ++ bal += (core->blue_bal * core->global_gain / (1 << 7)); ++ blue_gain = calc_mt9v011_gain(bal); ++ ++ bal = core->global_gain; ++ bal += (core->red_bal * core->global_gain / (1 << 7)); ++ red_gain = calc_mt9v011_gain(bal); ++ ++ mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain); ++ mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain); ++ mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain); ++ mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); ++ mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure); ++} ++ ++static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ unsigned height, width, hblank, vblank, speed; ++ unsigned row_time, t_time; ++ u64 frames_per_ms; ++ unsigned tmp; ++ ++ height = mt9v011_read(sd, R03_MT9V011_HEIGHT); ++ width = mt9v011_read(sd, R04_MT9V011_WIDTH); ++ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); ++ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); ++ speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED); ++ ++ row_time = (width + 113 + hblank) * (speed + 2); ++ t_time = row_time * (height + vblank + 1); ++ ++ frames_per_ms = core->xtal * 1000l; ++ do_div(frames_per_ms, t_time); ++ tmp = frames_per_ms; ++ ++ v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", ++ tmp / 1000, tmp % 1000, t_time); ++ ++ if (numerator && denominator) { ++ *numerator = 1000; ++ *denominator = (u32)frames_per_ms; ++ } ++} ++ ++static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ unsigned height, width, hblank, vblank; ++ unsigned row_time, line_time; ++ u64 t_time, speed; ++ ++ /* Avoid bogus calculus */ ++ if (!numerator || !denominator) ++ return 0; ++ ++ height = mt9v011_read(sd, R03_MT9V011_HEIGHT); ++ width = mt9v011_read(sd, R04_MT9V011_WIDTH); ++ hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); ++ vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); ++ ++ row_time = width + 113 + hblank; ++ line_time = height + vblank + 1; ++ ++ t_time = core->xtal * ((u64)numerator); ++ /* round to the closest value */ ++ t_time += denominator / 2; ++ do_div(t_time, denominator); ++ ++ speed = t_time; ++ do_div(speed, row_time * line_time); ++ ++ /* Avoid having a negative value for speed */ ++ if (speed < 2) ++ speed = 0; ++ else ++ speed -= 2; ++ ++ /* Avoid speed overflow */ ++ if (speed > 15) ++ return 15; ++ ++ return (u16)speed; ++} ++ ++static void set_res(struct v4l2_subdev *sd) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ unsigned vstart, hstart; ++ ++ /* ++ * The mt9v011 doesn't have scaling. So, in order to select the desired ++ * resolution, we're cropping at the middle of the sensor. ++ * hblank and vblank should be adjusted, in order to warrant that ++ * we'll preserve the line timings for 30 fps, no matter what resolution ++ * is selected. ++ * NOTE: datasheet says that width (and height) should be filled with ++ * width-1. However, this doesn't work, since one pixel per line will ++ * be missing. ++ */ ++ ++ hstart = 20 + (640 - core->width) / 2; ++ mt9v011_write(sd, R02_MT9V011_COLSTART, hstart); ++ mt9v011_write(sd, R04_MT9V011_WIDTH, core->width); ++ mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width); ++ ++ vstart = 8 + (480 - core->height) / 2; ++ mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart); ++ mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); ++ mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); ++ ++ calc_fps(sd, NULL, NULL); ++}; ++ ++static void set_read_mode(struct v4l2_subdev *sd) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ unsigned mode = 0x1000; ++ ++ if (core->hflip) ++ mode |= 0x4000; ++ ++ if (core->vflip) ++ mode |= 0x8000; ++ ++ mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); ++} ++ ++static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++) ++ mt9v011_write(sd, mt9v011_init_default[i].reg, ++ mt9v011_init_default[i].value); ++ ++ set_balance(sd); ++ set_res(sd); ++ set_read_mode(sd); ++ ++ return 0; ++}; ++ ++static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ ++ v4l2_dbg(1, debug, sd, "g_ctrl called\n"); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN: ++ ctrl->value = core->global_gain; ++ return 0; ++ case V4L2_CID_EXPOSURE: ++ ctrl->value = core->exposure; ++ return 0; ++ case V4L2_CID_RED_BALANCE: ++ ctrl->value = core->red_bal; ++ return 0; ++ case V4L2_CID_BLUE_BALANCE: ++ ctrl->value = core->blue_bal; ++ return 0; ++ case V4L2_CID_HFLIP: ++ ctrl->value = core->hflip ? 1 : 0; ++ return 0; ++ case V4L2_CID_VFLIP: ++ ctrl->value = core->vflip ? 1 : 0; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int mt9v011_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) ++{ ++ int i; ++ ++ v4l2_dbg(1, debug, sd, "queryctrl called\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(mt9v011_qctrl); i++) ++ if (qc->id && qc->id == mt9v011_qctrl[i].id) { ++ memcpy(qc, &(mt9v011_qctrl[i]), ++ sizeof(*qc)); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++ ++static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ u8 i, n; ++ n = ARRAY_SIZE(mt9v011_qctrl); ++ ++ for (i = 0; i < n; i++) { ++ if (ctrl->id != mt9v011_qctrl[i].id) ++ continue; ++ if (ctrl->value < mt9v011_qctrl[i].minimum || ++ ctrl->value > mt9v011_qctrl[i].maximum) ++ return -ERANGE; ++ v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", ++ ctrl->id, ctrl->value); ++ break; ++ } ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN: ++ core->global_gain = ctrl->value; ++ break; ++ case V4L2_CID_EXPOSURE: ++ core->exposure = ctrl->value; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ core->red_bal = ctrl->value; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ core->blue_bal = ctrl->value; ++ break; ++ case V4L2_CID_HFLIP: ++ core->hflip = ctrl->value; ++ set_read_mode(sd); ++ return 0; ++ case V4L2_CID_VFLIP: ++ core->vflip = ctrl->value; ++ set_read_mode(sd); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++ ++ set_balance(sd); ++ ++ return 0; ++} ++ ++static int mt9v011_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index > 0) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_SGRBG8_1X8; ++ return 0; ++} ++ ++static int mt9v011_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ if (fmt->code != V4L2_MBUS_FMT_SGRBG8_1X8) ++ return -EINVAL; ++ ++ v4l_bound_align_image(&fmt->width, 48, 639, 1, ++ &fmt->height, 32, 480, 1, 0); ++ fmt->field = V4L2_FIELD_NONE; ++ fmt->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return 0; ++} ++ ++static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(cp, 0, sizeof(struct v4l2_captureparm)); ++ cp->capability = V4L2_CAP_TIMEPERFRAME; ++ calc_fps(sd, ++ &cp->timeperframe.numerator, ++ &cp->timeperframe.denominator); ++ ++ return 0; ++} ++ ++static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ struct v4l2_fract *tpf = &cp->timeperframe; ++ u16 speed; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (cp->extendedmode != 0) ++ return -EINVAL; ++ ++ speed = calc_speed(sd, tpf->numerator, tpf->denominator); ++ ++ mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); ++ v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); ++ ++ /* Recalculate and update fps info */ ++ calc_fps(sd, &tpf->numerator, &tpf->denominator); ++ ++ return 0; ++} ++ ++static int mt9v011_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ struct mt9v011 *core = to_mt9v011(sd); ++ int rc; ++ ++ rc = mt9v011_try_mbus_fmt(sd, fmt); ++ if (rc < 0) ++ return -EINVAL; ++ ++ core->width = fmt->width; ++ core->height = fmt->height; ++ ++ set_res(sd); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9v011_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ reg->val = mt9v011_read(sd, reg->reg & 0xff); ++ reg->size = 2; ++ ++ return 0; ++} ++ ++static int mt9v011_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff); ++ ++ return 0; ++} ++#endif ++ ++static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ u16 version; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, ++ version); ++} ++ ++static const struct v4l2_subdev_core_ops mt9v011_core_ops = { ++ .queryctrl = mt9v011_queryctrl, ++ .g_ctrl = mt9v011_g_ctrl, ++ .s_ctrl = mt9v011_s_ctrl, ++ .reset = mt9v011_reset, ++ .g_chip_ident = mt9v011_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9v011_g_register, ++ .s_register = mt9v011_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops mt9v011_video_ops = { ++ .enum_mbus_fmt = mt9v011_enum_mbus_fmt, ++ .try_mbus_fmt = mt9v011_try_mbus_fmt, ++ .s_mbus_fmt = mt9v011_s_mbus_fmt, ++ .g_parm = mt9v011_g_parm, ++ .s_parm = mt9v011_s_parm, ++}; ++ ++static const struct v4l2_subdev_ops mt9v011_ops = { ++ .core = &mt9v011_core_ops, ++ .video = &mt9v011_video_ops, ++}; ++ ++ ++/**************************************************************************** ++ I2C Client & Driver ++ ****************************************************************************/ ++ ++static int mt9v011_probe(struct i2c_client *c, ++ const struct i2c_device_id *id) ++{ ++ u16 version; ++ struct mt9v011 *core; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(c->adapter, ++ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -EIO; ++ ++ core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL); ++ if (!core) ++ return -ENOMEM; ++ ++ sd = &core->sd; ++ v4l2_i2c_subdev_init(sd, c, &mt9v011_ops); ++ ++ /* Check if the sensor is really a MT9V011 */ ++ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); ++ if ((version != MT9V011_VERSION) && ++ (version != MT9V011_REV_B_VERSION)) { ++ v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", ++ version); ++ kfree(core); ++ return -EINVAL; ++ } ++ ++ core->global_gain = 0x0024; ++ core->exposure = 0x01fc; ++ core->width = 640; ++ core->height = 480; ++ core->xtal = 27000000; /* Hz */ ++ ++ if (c->dev.platform_data) { ++ struct mt9v011_platform_data *pdata = c->dev.platform_data; ++ ++ core->xtal = pdata->xtal; ++ v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n", ++ core->xtal / 1000000, (core->xtal / 1000) % 1000); ++ } ++ ++ v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", ++ c->addr << 1, c->adapter->name, version); ++ ++ return 0; ++} ++ ++static int mt9v011_remove(struct i2c_client *c) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(c); ++ ++ v4l2_dbg(1, debug, sd, ++ "mt9v011.c: removing mt9v011 adapter on address 0x%x\n", ++ c->addr << 1); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_mt9v011(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id mt9v011_id[] = { ++ { "mt9v011", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9v011_id); ++ ++static struct i2c_driver mt9v011_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "mt9v011", ++ }, ++ .probe = mt9v011_probe, ++ .remove = mt9v011_remove, ++ .id_table = mt9v011_id, ++}; ++ ++module_i2c_driver(mt9v011_driver); +diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c +new file mode 100644 +index 0000000..3f356cb +--- /dev/null ++++ b/drivers/media/i2c/mt9v032.c +@@ -0,0 +1,869 @@ ++/* ++ * Driver for MT9V032 CMOS Image Sensor from Micron ++ * ++ * Copyright (C) 2010, Laurent Pinchart ++ * ++ * Based on the MT9M001 driver, ++ * ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MT9V032_PIXEL_ARRAY_HEIGHT 492 ++#define MT9V032_PIXEL_ARRAY_WIDTH 782 ++ ++#define MT9V032_SYSCLK_FREQ_DEF 26600000 ++ ++#define MT9V032_CHIP_VERSION 0x00 ++#define MT9V032_CHIP_ID_REV1 0x1311 ++#define MT9V032_CHIP_ID_REV3 0x1313 ++#define MT9V032_COLUMN_START 0x01 ++#define MT9V032_COLUMN_START_MIN 1 ++#define MT9V032_COLUMN_START_DEF 1 ++#define MT9V032_COLUMN_START_MAX 752 ++#define MT9V032_ROW_START 0x02 ++#define MT9V032_ROW_START_MIN 4 ++#define MT9V032_ROW_START_DEF 5 ++#define MT9V032_ROW_START_MAX 482 ++#define MT9V032_WINDOW_HEIGHT 0x03 ++#define MT9V032_WINDOW_HEIGHT_MIN 1 ++#define MT9V032_WINDOW_HEIGHT_DEF 480 ++#define MT9V032_WINDOW_HEIGHT_MAX 480 ++#define MT9V032_WINDOW_WIDTH 0x04 ++#define MT9V032_WINDOW_WIDTH_MIN 1 ++#define MT9V032_WINDOW_WIDTH_DEF 752 ++#define MT9V032_WINDOW_WIDTH_MAX 752 ++#define MT9V032_HORIZONTAL_BLANKING 0x05 ++#define MT9V032_HORIZONTAL_BLANKING_MIN 43 ++#define MT9V032_HORIZONTAL_BLANKING_DEF 94 ++#define MT9V032_HORIZONTAL_BLANKING_MAX 1023 ++#define MT9V032_VERTICAL_BLANKING 0x06 ++#define MT9V032_VERTICAL_BLANKING_MIN 4 ++#define MT9V032_VERTICAL_BLANKING_DEF 45 ++#define MT9V032_VERTICAL_BLANKING_MAX 3000 ++#define MT9V032_CHIP_CONTROL 0x07 ++#define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) ++#define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) ++#define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) ++#define MT9V032_SHUTTER_WIDTH1 0x08 ++#define MT9V032_SHUTTER_WIDTH2 0x09 ++#define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a ++#define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b ++#define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 ++#define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 ++#define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 ++#define MT9V032_RESET 0x0c ++#define MT9V032_READ_MODE 0x0d ++#define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) ++#define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 ++#define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) ++#define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 ++#define MT9V032_READ_MODE_ROW_FLIP (1 << 4) ++#define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) ++#define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) ++#define MT9V032_READ_MODE_DARK_ROWS (1 << 7) ++#define MT9V032_PIXEL_OPERATION_MODE 0x0f ++#define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) ++#define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) ++#define MT9V032_ANALOG_GAIN 0x35 ++#define MT9V032_ANALOG_GAIN_MIN 16 ++#define MT9V032_ANALOG_GAIN_DEF 16 ++#define MT9V032_ANALOG_GAIN_MAX 64 ++#define MT9V032_MAX_ANALOG_GAIN 0x36 ++#define MT9V032_MAX_ANALOG_GAIN_MAX 127 ++#define MT9V032_FRAME_DARK_AVERAGE 0x42 ++#define MT9V032_DARK_AVG_THRESH 0x46 ++#define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) ++#define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 ++#define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) ++#define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 ++#define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 ++#define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) ++#define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) ++#define MT9V032_PIXEL_CLOCK 0x74 ++#define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) ++#define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) ++#define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) ++#define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) ++#define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) ++#define MT9V032_TEST_PATTERN 0x7f ++#define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) ++#define MT9V032_TEST_PATTERN_DATA_SHIFT 0 ++#define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) ++#define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) ++#define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) ++#define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) ++#define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) ++#define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) ++#define MT9V032_TEST_PATTERN_ENABLE (1 << 13) ++#define MT9V032_TEST_PATTERN_FLIP (1 << 14) ++#define MT9V032_AEC_AGC_ENABLE 0xaf ++#define MT9V032_AEC_ENABLE (1 << 0) ++#define MT9V032_AGC_ENABLE (1 << 1) ++#define MT9V032_THERMAL_INFO 0xc1 ++ ++struct mt9v032 { ++ struct v4l2_subdev subdev; ++ struct media_pad pad; ++ ++ struct v4l2_mbus_framefmt format; ++ struct v4l2_rect crop; ++ ++ struct v4l2_ctrl_handler ctrls; ++ struct { ++ struct v4l2_ctrl *link_freq; ++ struct v4l2_ctrl *pixel_rate; ++ }; ++ ++ struct mutex power_lock; ++ int power_count; ++ ++ struct mt9v032_platform_data *pdata; ++ ++ u32 sysclk; ++ u16 chip_control; ++ u16 aec_agc; ++ u16 hblank; ++ struct { ++ struct v4l2_ctrl *test_pattern; ++ struct v4l2_ctrl *test_pattern_color; ++ }; ++}; ++ ++static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct mt9v032, subdev); ++} ++ ++static int mt9v032_read(struct i2c_client *client, const u8 reg) ++{ ++ s32 data = i2c_smbus_read_word_swapped(client, reg); ++ dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, ++ data, reg); ++ return data; ++} ++ ++static int mt9v032_write(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, ++ data, reg); ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ u16 value = (mt9v032->chip_control & ~clear) | set; ++ int ret; ++ ++ ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); ++ if (ret < 0) ++ return ret; ++ ++ mt9v032->chip_control = value; ++ return 0; ++} ++ ++static int ++mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ u16 value = mt9v032->aec_agc; ++ int ret; ++ ++ if (enable) ++ value |= which; ++ else ++ value &= ~which; ++ ++ ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); ++ if (ret < 0) ++ return ret; ++ ++ mt9v032->aec_agc = value; ++ return 0; ++} ++ ++static int ++mt9v032_update_hblank(struct mt9v032 *mt9v032) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ struct v4l2_rect *crop = &mt9v032->crop; ++ ++ return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, ++ max_t(s32, mt9v032->hblank, 660 - crop->width)); ++} ++ ++#define EXT_CLK 25000000 ++ ++static int mt9v032_power_on(struct mt9v032 *mt9v032) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ int ret; ++ ++ if (mt9v032->pdata->set_clock) { ++ mt9v032->pdata->set_clock(&mt9v032->subdev, mt9v032->sysclk); ++ udelay(1); ++ } ++ ++ /* Reset the chip and stop data read out */ ++ ret = mt9v032_write(client, MT9V032_RESET, 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_write(client, MT9V032_RESET, 0); ++ if (ret < 0) ++ return ret; ++ ++ return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); ++} ++ ++static void mt9v032_power_off(struct mt9v032 *mt9v032) ++{ ++ if (mt9v032->pdata->set_clock) ++ mt9v032->pdata->set_clock(&mt9v032->subdev, 0); ++} ++ ++static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ int ret; ++ ++ if (!on) { ++ mt9v032_power_off(mt9v032); ++ return 0; ++ } ++ ++ ret = mt9v032_power_on(mt9v032); ++ if (ret < 0) ++ return ret; ++ ++ /* Configure the pixel clock polarity */ ++ if (mt9v032->pdata && mt9v032->pdata->clk_pol) { ++ ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, ++ MT9V032_PIXEL_CLOCK_INV_PXL_CLK); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Disable the noise correction algorithm and restore the controls. */ ++ ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); ++ if (ret < 0) ++ return ret; ++ ++ return v4l2_ctrl_handler_setup(&mt9v032->ctrls); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev video operations ++ */ ++ ++static struct v4l2_mbus_framefmt * ++__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_format(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9v032->format; ++ default: ++ return NULL; ++ } ++} ++ ++static struct v4l2_rect * ++__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, ++ unsigned int pad, enum v4l2_subdev_format_whence which) ++{ ++ switch (which) { ++ case V4L2_SUBDEV_FORMAT_TRY: ++ return v4l2_subdev_get_try_crop(fh, pad); ++ case V4L2_SUBDEV_FORMAT_ACTIVE: ++ return &mt9v032->crop; ++ default: ++ return NULL; ++ } ++} ++ ++static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) ++{ ++ const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE ++ | MT9V032_CHIP_CONTROL_DOUT_ENABLE ++ | MT9V032_CHIP_CONTROL_SEQUENTIAL; ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ struct v4l2_mbus_framefmt *format = &mt9v032->format; ++ struct v4l2_rect *crop = &mt9v032->crop; ++ unsigned int hratio; ++ unsigned int vratio; ++ int ret; ++ ++ if (!enable) ++ return mt9v032_set_chip_control(mt9v032, mode, 0); ++ ++ /* Configure the window size and row/column bin */ ++ hratio = DIV_ROUND_CLOSEST(crop->width, format->width); ++ vratio = DIV_ROUND_CLOSEST(crop->height, format->height); ++ ++ ret = mt9v032_write(client, MT9V032_READ_MODE, ++ (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | ++ (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9v032_update_hblank(mt9v032); ++ if (ret < 0) ++ return ret; ++ ++ /* Switch to master "normal" mode */ ++ return mt9v032_set_chip_control(mt9v032, 0, mode); ++} ++ ++static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index > 0) ++ return -EINVAL; ++ ++ code->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ return 0; ++} ++ ++static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) ++ return -EINVAL; ++ ++ fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; ++ fse->max_width = fse->min_width; ++ fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; ++ fse->max_height = fse->min_height; ++ ++ return 0; ++} ++ ++static int mt9v032_get_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format) ++{ ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ ++ format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, ++ format->which); ++ return 0; ++} ++ ++static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032, ++ unsigned int hratio) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ int ret; ++ ++ ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, ++ mt9v032->sysclk / hratio); ++ if (ret < 0) ++ dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); ++} ++ ++static int mt9v032_set_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *format) ++{ ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ unsigned int width; ++ unsigned int height; ++ unsigned int hratio; ++ unsigned int vratio; ++ ++ __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, ++ format->which); ++ ++ /* Clamp the width and height to avoid dividing by zero. */ ++ width = clamp_t(unsigned int, ALIGN(format->format.width, 2), ++ max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), ++ __crop->width); ++ height = clamp_t(unsigned int, ALIGN(format->format.height, 2), ++ max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), ++ __crop->height); ++ ++ hratio = DIV_ROUND_CLOSEST(__crop->width, width); ++ vratio = DIV_ROUND_CLOSEST(__crop->height, height); ++ ++ __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, ++ format->which); ++ __format->width = __crop->width / hratio; ++ __format->height = __crop->height / vratio; ++ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ mt9v032_configure_pixel_rate(mt9v032, hratio); ++ ++ format->format = *__format; ++ ++ return 0; ++} ++ ++static int mt9v032_get_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ ++ crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, ++ crop->which); ++ return 0; ++} ++ ++static int mt9v032_set_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ struct v4l2_mbus_framefmt *__format; ++ struct v4l2_rect *__crop; ++ struct v4l2_rect rect; ++ ++ /* Clamp the crop rectangle boundaries and align them to a non multiple ++ * of 2 pixels to ensure a GRBG Bayer pattern. ++ */ ++ rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, ++ MT9V032_COLUMN_START_MIN, ++ MT9V032_COLUMN_START_MAX); ++ rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, ++ MT9V032_ROW_START_MIN, ++ MT9V032_ROW_START_MAX); ++ rect.width = clamp(ALIGN(crop->rect.width, 2), ++ MT9V032_WINDOW_WIDTH_MIN, ++ MT9V032_WINDOW_WIDTH_MAX); ++ rect.height = clamp(ALIGN(crop->rect.height, 2), ++ MT9V032_WINDOW_HEIGHT_MIN, ++ MT9V032_WINDOW_HEIGHT_MAX); ++ ++ rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); ++ rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); ++ ++ __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); ++ ++ if (rect.width != __crop->width || rect.height != __crop->height) { ++ /* Reset the output image size if the crop rectangle size has ++ * been modified. ++ */ ++ __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, ++ crop->which); ++ __format->width = rect.width; ++ __format->height = rect.height; ++ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ mt9v032_configure_pixel_rate(mt9v032, 1); ++ } ++ ++ *__crop = rect; ++ crop->rect = rect; ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev control operations ++ */ ++ ++#define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) ++ ++static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9v032 *mt9v032 = ++ container_of(ctrl->handler, struct mt9v032, ctrls); ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); ++ u32 freq; ++ u16 data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, ++ ctrl->val); ++ ++ case V4L2_CID_GAIN: ++ return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, ++ !ctrl->val); ++ ++ case V4L2_CID_EXPOSURE: ++ return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, ++ ctrl->val); ++ ++ case V4L2_CID_HBLANK: ++ mt9v032->hblank = ctrl->val; ++ return mt9v032_update_hblank(mt9v032); ++ ++ case V4L2_CID_VBLANK: ++ return mt9v032_write(client, MT9V032_VERTICAL_BLANKING, ++ ctrl->val); ++ ++ case V4L2_CID_PIXEL_RATE: ++ case V4L2_CID_LINK_FREQ: ++ if (mt9v032->link_freq == NULL) ++ break; ++ ++ freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; ++ mt9v032->pixel_rate->val64 = freq; ++ mt9v032->sysclk = freq; ++ break; ++ ++ case V4L2_CID_TEST_PATTERN: ++ switch (mt9v032->test_pattern->val) { ++ case 0: ++ data = 0; ++ break; ++ case 1: ++ data = MT9V032_TEST_PATTERN_GRAY_VERTICAL ++ | MT9V032_TEST_PATTERN_ENABLE; ++ break; ++ case 2: ++ data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL ++ | MT9V032_TEST_PATTERN_ENABLE; ++ break; ++ case 3: ++ data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL ++ | MT9V032_TEST_PATTERN_ENABLE; ++ break; ++ default: ++ data = (mt9v032->test_pattern_color->val << ++ MT9V032_TEST_PATTERN_DATA_SHIFT) ++ | MT9V032_TEST_PATTERN_USE_DATA ++ | MT9V032_TEST_PATTERN_ENABLE ++ | MT9V032_TEST_PATTERN_FLIP; ++ break; ++ } ++ return mt9v032_write(client, MT9V032_TEST_PATTERN, data); ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { ++ .s_ctrl = mt9v032_s_ctrl, ++}; ++ ++static const char * const mt9v032_test_pattern_menu[] = { ++ "Disabled", ++ "Gray Vertical Shade", ++ "Gray Horizontal Shade", ++ "Gray Diagonal Shade", ++ "Plain", ++}; ++ ++static const struct v4l2_ctrl_config mt9v032_test_pattern_color = { ++ .ops = &mt9v032_ctrl_ops, ++ .id = V4L2_CID_TEST_PATTERN_COLOR, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Test Pattern Color", ++ .min = 0, ++ .max = 1023, ++ .step = 1, ++ .def = 0, ++ .flags = 0, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev core operations ++ */ ++ ++static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) ++{ ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ int ret = 0; ++ ++ mutex_lock(&mt9v032->power_lock); ++ ++ /* If the power count is modified from 0 to != 0 or from != 0 to 0, ++ * update the power state. ++ */ ++ if (mt9v032->power_count == !on) { ++ ret = __mt9v032_set_power(mt9v032, !!on); ++ if (ret < 0) ++ goto done; ++ } ++ ++ /* Update the power count. */ ++ mt9v032->power_count += on ? 1 : -1; ++ WARN_ON(mt9v032->power_count < 0); ++ ++done: ++ mutex_unlock(&mt9v032->power_lock); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev internal operations ++ */ ++ ++static int mt9v032_registered(struct v4l2_subdev *subdev) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ s32 data; ++ int ret; ++ ++ dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", ++ client->addr); ++ ++ ret = mt9v032_power_on(mt9v032); ++ if (ret < 0) { ++ dev_err(&client->dev, "MT9V032 power up failed\n"); ++ return ret; ++ } ++ ++ /* Read and check the sensor version */ ++ data = mt9v032_read(client, MT9V032_CHIP_VERSION); ++ if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { ++ dev_err(&client->dev, "MT9V032 not detected, wrong version " ++ "0x%04x\n", data); ++ return -ENODEV; ++ } ++ ++ mt9v032_power_off(mt9v032); ++ ++ dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", ++ client->addr); ++ ++ mt9v032_configure_pixel_rate(mt9v032, 1); ++ ++ return ret; ++} ++ ++static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *format; ++ struct v4l2_rect *crop; ++ ++ crop = v4l2_subdev_get_try_crop(fh, 0); ++ crop->left = MT9V032_COLUMN_START_DEF; ++ crop->top = MT9V032_ROW_START_DEF; ++ crop->width = MT9V032_WINDOW_WIDTH_DEF; ++ crop->height = MT9V032_WINDOW_HEIGHT_DEF; ++ ++ format = v4l2_subdev_get_try_format(fh, 0); ++ format->code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ format->width = MT9V032_WINDOW_WIDTH_DEF; ++ format->height = MT9V032_WINDOW_HEIGHT_DEF; ++ format->field = V4L2_FIELD_NONE; ++ format->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return mt9v032_set_power(subdev, 1); ++} ++ ++static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) ++{ ++ return mt9v032_set_power(subdev, 0); ++} ++ ++static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { ++ .s_power = mt9v032_set_power, ++}; ++ ++static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { ++ .s_stream = mt9v032_s_stream, ++}; ++ ++static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { ++ .enum_mbus_code = mt9v032_enum_mbus_code, ++ .enum_frame_size = mt9v032_enum_frame_size, ++ .get_fmt = mt9v032_get_format, ++ .set_fmt = mt9v032_set_format, ++ .get_crop = mt9v032_get_crop, ++ .set_crop = mt9v032_set_crop, ++}; ++ ++static struct v4l2_subdev_ops mt9v032_subdev_ops = { ++ .core = &mt9v032_subdev_core_ops, ++ .video = &mt9v032_subdev_video_ops, ++ .pad = &mt9v032_subdev_pad_ops, ++}; ++ ++static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { ++ .registered = mt9v032_registered, ++ .open = mt9v032_open, ++ .close = mt9v032_close, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * Driver initialization and probing ++ */ ++ ++static int mt9v032_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9v032_platform_data *pdata = client->dev.platform_data; ++ struct mt9v032 *mt9v032; ++ unsigned int i; ++ int ret; ++ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&client->adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); ++ if (!mt9v032) ++ return -ENOMEM; ++ ++ mutex_init(&mt9v032->power_lock); ++ mt9v032->pdata = pdata; ++ ++ v4l2_ctrl_handler_init(&mt9v032->ctrls, 10); ++ ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, ++ MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); ++ v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, ++ V4L2_EXPOSURE_AUTO); ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, ++ MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, ++ MT9V032_TOTAL_SHUTTER_WIDTH_DEF); ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_HBLANK, MT9V032_HORIZONTAL_BLANKING_MIN, ++ MT9V032_HORIZONTAL_BLANKING_MAX, 1, ++ MT9V032_HORIZONTAL_BLANKING_DEF); ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_VBLANK, MT9V032_VERTICAL_BLANKING_MIN, ++ MT9V032_VERTICAL_BLANKING_MAX, 1, ++ MT9V032_VERTICAL_BLANKING_DEF); ++ mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls, ++ &mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, ++ ARRAY_SIZE(mt9v032_test_pattern_menu) - 1, 0, 0, ++ mt9v032_test_pattern_menu); ++ mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls, ++ &mt9v032_test_pattern_color, NULL); ++ ++ v4l2_ctrl_cluster(2, &mt9v032->test_pattern); ++ ++ mt9v032->pixel_rate = ++ v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); ++ ++ if (pdata && pdata->link_freqs) { ++ unsigned int def = 0; ++ ++ for (i = 0; pdata->link_freqs[i]; ++i) { ++ if (pdata->link_freqs[i] == pdata->link_def_freq) ++ def = i; ++ } ++ ++ mt9v032->link_freq = ++ v4l2_ctrl_new_int_menu(&mt9v032->ctrls, ++ &mt9v032_ctrl_ops, ++ V4L2_CID_LINK_FREQ, i - 1, def, ++ pdata->link_freqs); ++ v4l2_ctrl_cluster(2, &mt9v032->link_freq); ++ } ++ ++ ++ mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; ++ ++ if (mt9v032->ctrls.error) ++ printk(KERN_INFO "%s: control initialization error %d\n", ++ __func__, mt9v032->ctrls.error); ++ ++ mt9v032->crop.left = MT9V032_COLUMN_START_DEF; ++ mt9v032->crop.top = MT9V032_ROW_START_DEF; ++ mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; ++ mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; ++ ++ mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; ++ mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; ++ mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; ++ mt9v032->format.field = V4L2_FIELD_NONE; ++ mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; ++ ++ mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; ++ mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF; ++ mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; ++ ++ v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); ++ mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; ++ mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; ++ ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); ++ if (ret < 0) ++ kfree(mt9v032); ++ ++ return ret; ++} ++ ++static int mt9v032_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct mt9v032 *mt9v032 = to_mt9v032(subdev); ++ ++ v4l2_device_unregister_subdev(subdev); ++ media_entity_cleanup(&subdev->entity); ++ kfree(mt9v032); ++ return 0; ++} ++ ++static const struct i2c_device_id mt9v032_id[] = { ++ { "mt9v032", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9v032_id); ++ ++static struct i2c_driver mt9v032_driver = { ++ .driver = { ++ .name = "mt9v032", ++ }, ++ .probe = mt9v032_probe, ++ .remove = mt9v032_remove, ++ .id_table = mt9v032_id, ++}; ++ ++module_i2c_driver(mt9v032_driver); ++ ++MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c +new file mode 100644 +index 0000000..440c129 +--- /dev/null ++++ b/drivers/media/i2c/noon010pc30.c +@@ -0,0 +1,851 @@ ++/* ++ * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP ++ * ++ * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. ++ * Contact: Sylwester Nawrocki, ++ * ++ * Initial register configuration based on a driver authored by ++ * HeungJun Kim . ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); ++ ++#define MODULE_NAME "NOON010PC30" ++ ++/* ++ * Register offsets within a page ++ * b15..b8 - page id, b7..b0 - register address ++ */ ++#define POWER_CTRL_REG 0x0001 ++#define PAGEMODE_REG 0x03 ++#define DEVICE_ID_REG 0x0004 ++#define NOON010PC30_ID 0x86 ++#define VDO_CTL_REG(n) (0x0010 + (n)) ++#define SYNC_CTL_REG 0x0012 ++/* Window size and position */ ++#define WIN_ROWH_REG 0x0013 ++#define WIN_ROWL_REG 0x0014 ++#define WIN_COLH_REG 0x0015 ++#define WIN_COLL_REG 0x0016 ++#define WIN_HEIGHTH_REG 0x0017 ++#define WIN_HEIGHTL_REG 0x0018 ++#define WIN_WIDTHH_REG 0x0019 ++#define WIN_WIDTHL_REG 0x001A ++#define HBLANKH_REG 0x001B ++#define HBLANKL_REG 0x001C ++#define VSYNCH_REG 0x001D ++#define VSYNCL_REG 0x001E ++/* VSYNC control */ ++#define VS_CTL_REG(n) (0x00A1 + (n)) ++/* page 1 */ ++#define ISP_CTL_REG(n) (0x0110 + (n)) ++#define YOFS_REG 0x0119 ++#define DARK_YOFS_REG 0x011A ++#define SAT_CTL_REG 0x0120 ++#define BSAT_REG 0x0121 ++#define RSAT_REG 0x0122 ++/* Color correction */ ++#define CMC_CTL_REG 0x0130 ++#define CMC_OFSGH_REG 0x0133 ++#define CMC_OFSGL_REG 0x0135 ++#define CMC_SIGN_REG 0x0136 ++#define CMC_GOFS_REG 0x0137 ++#define CMC_COEF_REG(n) (0x0138 + (n)) ++#define CMC_OFS_REG(n) (0x0141 + (n)) ++/* Gamma correction */ ++#define GMA_CTL_REG 0x0160 ++#define GMA_COEF_REG(n) (0x0161 + (n)) ++/* Lens Shading */ ++#define LENS_CTRL_REG 0x01D0 ++#define LENS_XCEN_REG 0x01D1 ++#define LENS_YCEN_REG 0x01D2 ++#define LENS_RC_REG 0x01D3 ++#define LENS_GC_REG 0x01D4 ++#define LENS_BC_REG 0x01D5 ++#define L_AGON_REG 0x01D6 ++#define L_AGOFF_REG 0x01D7 ++/* Page 3 - Auto Exposure */ ++#define AE_CTL_REG(n) (0x0310 + (n)) ++#define AE_CTL9_REG 0x032C ++#define AE_CTL10_REG 0x032D ++#define AE_YLVL_REG 0x031C ++#define AE_YTH_REG(n) (0x031D + (n)) ++#define AE_WGT_REG 0x0326 ++#define EXP_TIMEH_REG 0x0333 ++#define EXP_TIMEM_REG 0x0334 ++#define EXP_TIMEL_REG 0x0335 ++#define EXP_MMINH_REG 0x0336 ++#define EXP_MMINL_REG 0x0337 ++#define EXP_MMAXH_REG 0x0338 ++#define EXP_MMAXM_REG 0x0339 ++#define EXP_MMAXL_REG 0x033A ++/* Page 4 - Auto White Balance */ ++#define AWB_CTL_REG(n) (0x0410 + (n)) ++#define AWB_ENABE 0x80 ++#define AWB_WGHT_REG 0x0419 ++#define BGAIN_PAR_REG(n) (0x044F + (n)) ++/* Manual white balance, when AWB_CTL2[0]=1 */ ++#define MWB_RGAIN_REG 0x0466 ++#define MWB_BGAIN_REG 0x0467 ++ ++/* The token to mark an array end */ ++#define REG_TERM 0xFFFF ++ ++struct noon010_format { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++ u16 ispctl1_reg; ++}; ++ ++struct noon010_frmsize { ++ u16 width; ++ u16 height; ++ int vid_ctl1; ++}; ++ ++static const char * const noon010_supply_name[] = { ++ "vdd_core", "vddio", "vdda" ++}; ++ ++#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) ++ ++struct noon010_info { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler hdl; ++ struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; ++ u32 gpio_nreset; ++ u32 gpio_nstby; ++ ++ /* Protects the struct members below */ ++ struct mutex lock; ++ ++ const struct noon010_format *curr_fmt; ++ const struct noon010_frmsize *curr_win; ++ unsigned int apply_new_cfg:1; ++ unsigned int streaming:1; ++ unsigned int hflip:1; ++ unsigned int vflip:1; ++ unsigned int power:1; ++ u8 i2c_reg_page; ++}; ++ ++struct i2c_regval { ++ u16 addr; ++ u16 val; ++}; ++ ++/* Supported resolutions. */ ++static const struct noon010_frmsize noon010_sizes[] = { ++ { ++ .width = 352, ++ .height = 288, ++ .vid_ctl1 = 0, ++ }, { ++ .width = 176, ++ .height = 144, ++ .vid_ctl1 = 0x10, ++ }, { ++ .width = 88, ++ .height = 72, ++ .vid_ctl1 = 0x20, ++ }, ++}; ++ ++/* Supported pixel formats. */ ++static const struct noon010_format noon010_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x03, ++ }, { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x02, ++ }, { ++ .code = V4L2_MBUS_FMT_VYUY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0, ++ }, { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x01, ++ }, { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x40, ++ }, ++}; ++ ++static const struct i2c_regval noon010_base_regs[] = { ++ { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, ++ /* Color corection and saturation */ ++ { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, ++ { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, ++ { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, ++ { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, ++ { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, ++ { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, ++ { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, ++ { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, ++ { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, ++ { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, ++ { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, ++ { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, ++ { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, ++ { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, ++ /* Automatic white balance */ ++ { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, ++ { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, ++ /* Auto exposure */ ++ { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, ++ { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, ++ { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, ++ { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, ++ { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, ++ { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, ++ { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, ++ /* Lens shading compensation */ ++ { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, ++ { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, ++ { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, ++ { REG_TERM, 0 }, ++}; ++ ++static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct noon010_info, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; ++} ++ ++static inline int set_i2c_page(struct noon010_info *info, ++ struct i2c_client *client, unsigned int reg) ++{ ++ u32 page = reg >> 8 & 0xFF; ++ int ret = 0; ++ ++ if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { ++ ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); ++ if (!ret) ++ info->i2c_reg_page = page; ++ } ++ return ret; ++} ++ ++static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct noon010_info *info = to_noon010(sd); ++ int ret = set_i2c_page(info, client, reg_addr); ++ ++ if (ret) ++ return ret; ++ return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); ++} ++ ++static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct noon010_info *info = to_noon010(sd); ++ int ret = set_i2c_page(info, client, reg_addr); ++ ++ if (ret) ++ return ret; ++ return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); ++} ++ ++static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, ++ const struct i2c_regval *msg) ++{ ++ while (msg->addr != REG_TERM) { ++ int ret = cam_i2c_write(sd, msg->addr, msg->val); ++ ++ if (ret) ++ return ret; ++ msg++; ++ } ++ return 0; ++} ++ ++/* Device reset and sleep mode control */ ++static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ u8 reg = sleep ? 0xF1 : 0xF0; ++ int ret = 0; ++ ++ if (reset) { ++ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); ++ udelay(20); ++ } ++ if (!ret) { ++ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); ++ if (reset && !ret) ++ info->i2c_reg_page = -1; ++ } ++ return ret; ++} ++ ++/* Automatic white balance control */ ++static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) ++{ ++ int ret; ++ ++ ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); ++ if (!ret) ++ ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); ++ return ret; ++} ++ ++/* Called with struct noon010_info.lock mutex held */ ++static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ int reg, ret; ++ ++ reg = cam_i2c_read(sd, VDO_CTL_REG(1)); ++ if (reg < 0) ++ return reg; ++ ++ reg &= 0x7C; ++ if (hflip) ++ reg |= 0x01; ++ if (vflip) ++ reg |= 0x02; ++ ++ ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); ++ if (!ret) { ++ info->hflip = hflip; ++ info->vflip = vflip; ++ } ++ return ret; ++} ++ ++/* Configure resolution and color format */ ++static int noon010_set_params(struct v4l2_subdev *sd) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ ++ int ret = cam_i2c_write(sd, VDO_CTL_REG(0), ++ info->curr_win->vid_ctl1); ++ if (ret) ++ return ret; ++ return cam_i2c_write(sd, ISP_CTL_REG(0), ++ info->curr_fmt->ispctl1_reg); ++} ++ ++/* Find nearest matching image pixel size. */ ++static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, ++ const struct noon010_frmsize **size) ++{ ++ unsigned int min_err = ~0; ++ int i = ARRAY_SIZE(noon010_sizes); ++ const struct noon010_frmsize *fsize = &noon010_sizes[0], ++ *match = NULL; ++ ++ while (i--) { ++ int err = abs(fsize->width - mf->width) ++ + abs(fsize->height - mf->height); ++ ++ if (err < min_err) { ++ min_err = err; ++ match = fsize; ++ } ++ fsize++; ++ } ++ if (match) { ++ mf->width = match->width; ++ mf->height = match->height; ++ if (size) ++ *size = match; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* Called with info.lock mutex held */ ++static int power_enable(struct noon010_info *info) ++{ ++ int ret; ++ ++ if (info->power) { ++ v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); ++ return 0; ++ } ++ ++ if (gpio_is_valid(info->gpio_nstby)) ++ gpio_set_value(info->gpio_nstby, 0); ++ ++ if (gpio_is_valid(info->gpio_nreset)) ++ gpio_set_value(info->gpio_nreset, 0); ++ ++ ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); ++ if (ret) ++ return ret; ++ ++ if (gpio_is_valid(info->gpio_nreset)) { ++ msleep(50); ++ gpio_set_value(info->gpio_nreset, 1); ++ } ++ if (gpio_is_valid(info->gpio_nstby)) { ++ udelay(1000); ++ gpio_set_value(info->gpio_nstby, 1); ++ } ++ if (gpio_is_valid(info->gpio_nreset)) { ++ udelay(1000); ++ gpio_set_value(info->gpio_nreset, 0); ++ msleep(100); ++ gpio_set_value(info->gpio_nreset, 1); ++ msleep(20); ++ } ++ info->power = 1; ++ ++ v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); ++ return 0; ++} ++ ++/* Called with info.lock mutex held */ ++static int power_disable(struct noon010_info *info) ++{ ++ int ret; ++ ++ if (!info->power) { ++ v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); ++ return 0; ++ } ++ ++ ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); ++ if (ret) ++ return ret; ++ ++ if (gpio_is_valid(info->gpio_nstby)) ++ gpio_set_value(info->gpio_nstby, 0); ++ ++ if (gpio_is_valid(info->gpio_nreset)) ++ gpio_set_value(info->gpio_nreset, 0); ++ ++ info->power = 0; ++ ++ v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); ++ ++ return 0; ++} ++ ++static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct noon010_info *info = to_noon010(sd); ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", ++ __func__, ctrl->id, ctrl->val); ++ ++ mutex_lock(&info->lock); ++ /* ++ * If the device is not powered up by the host driver do ++ * not apply any controls to H/W at this time. Instead ++ * the controls will be restored right after power-up. ++ */ ++ if (!info->power) ++ goto unlock; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = noon010_enable_autowhitebalance(sd, ctrl->val); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++unlock: ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static int noon010_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index >= ARRAY_SIZE(noon010_formats)) ++ return -EINVAL; ++ ++ code->code = noon010_formats[code->index].code; ++ return 0; ++} ++ ++static int noon010_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ struct v4l2_mbus_framefmt *mf; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ if (fh) { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ fmt->format = *mf; ++ } ++ return 0; ++ } ++ mf = &fmt->format; ++ ++ mutex_lock(&info->lock); ++ mf->width = info->curr_win->width; ++ mf->height = info->curr_win->height; ++ mf->code = info->curr_fmt->code; ++ mf->colorspace = info->curr_fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ mutex_unlock(&info->lock); ++ return 0; ++} ++ ++/* Return nearest media bus frame format. */ ++static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ int i = ARRAY_SIZE(noon010_formats); ++ ++ while (--i) ++ if (mf->code == noon010_formats[i].code) ++ break; ++ mf->code = noon010_formats[i].code; ++ ++ return &noon010_formats[i]; ++} ++ ++static int noon010_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ const struct noon010_frmsize *size = NULL; ++ const struct noon010_format *nf; ++ struct v4l2_mbus_framefmt *mf; ++ int ret = 0; ++ ++ nf = noon010_try_fmt(sd, &fmt->format); ++ noon010_try_frame_size(&fmt->format, &size); ++ fmt->format.colorspace = V4L2_COLORSPACE_JPEG; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ if (fh) { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ *mf = fmt->format; ++ } ++ return 0; ++ } ++ mutex_lock(&info->lock); ++ if (!info->streaming) { ++ info->apply_new_cfg = 1; ++ info->curr_fmt = nf; ++ info->curr_win = size; ++ } else { ++ ret = -EBUSY; ++ } ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++/* Called with struct noon010_info.lock mutex held */ ++static int noon010_base_config(struct v4l2_subdev *sd) ++{ ++ int ret = noon010_bulk_write_reg(sd, noon010_base_regs); ++ if (!ret) ++ ret = noon010_set_params(sd); ++ if (!ret) ++ ret = noon010_set_flip(sd, 1, 0); ++ ++ return ret; ++} ++ ++static int noon010_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ int ret; ++ ++ mutex_lock(&info->lock); ++ if (on) { ++ ret = power_enable(info); ++ if (!ret) ++ ret = noon010_base_config(sd); ++ } else { ++ noon010_power_ctrl(sd, false, true); ++ ret = power_disable(info); ++ } ++ mutex_unlock(&info->lock); ++ ++ /* Restore the controls state */ ++ if (!ret && on) ++ ret = v4l2_ctrl_handler_setup(&info->hdl); ++ ++ return ret; ++} ++ ++static int noon010_s_stream(struct v4l2_subdev *sd, int on) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ int ret = 0; ++ ++ mutex_lock(&info->lock); ++ if (!info->streaming != !on) { ++ ret = noon010_power_ctrl(sd, false, !on); ++ if (!ret) ++ info->streaming = on; ++ } ++ if (!ret && on && info->apply_new_cfg) { ++ ret = noon010_set_params(sd); ++ if (!ret) ++ info->apply_new_cfg = 0; ++ } ++ mutex_unlock(&info->lock); ++ return ret; ++} ++ ++static int noon010_log_status(struct v4l2_subdev *sd) ++{ ++ struct noon010_info *info = to_noon010(sd); ++ ++ v4l2_ctrl_handler_log_status(&info->hdl, sd->name); ++ return 0; ++} ++ ++static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); ++ ++ mf->width = noon010_sizes[0].width; ++ mf->height = noon010_sizes[0].height; ++ mf->code = noon010_formats[0].code; ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ mf->field = V4L2_FIELD_NONE; ++ return 0; ++} ++ ++static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { ++ .open = noon010_open, ++}; ++ ++static const struct v4l2_ctrl_ops noon010_ctrl_ops = { ++ .s_ctrl = noon010_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops noon010_core_ops = { ++ .s_power = noon010_s_power, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .log_status = noon010_log_status, ++}; ++ ++static struct v4l2_subdev_pad_ops noon010_pad_ops = { ++ .enum_mbus_code = noon010_enum_mbus_code, ++ .get_fmt = noon010_get_fmt, ++ .set_fmt = noon010_set_fmt, ++}; ++ ++static struct v4l2_subdev_video_ops noon010_video_ops = { ++ .s_stream = noon010_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops noon010_ops = { ++ .core = &noon010_core_ops, ++ .pad = &noon010_pad_ops, ++ .video = &noon010_video_ops, ++}; ++ ++/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ ++static int noon010_detect(struct i2c_client *client, struct noon010_info *info) ++{ ++ int ret; ++ ++ ret = power_enable(info); ++ if (ret) ++ return ret; ++ ++ ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); ++ if (ret < 0) ++ dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); ++ ++ power_disable(info); ++ ++ return ret == NOON010PC30_ID ? 0 : -ENODEV; ++} ++ ++static int noon010_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct noon010_info *info; ++ struct v4l2_subdev *sd; ++ const struct noon010pc30_platform_data *pdata ++ = client->dev.platform_data; ++ int ret; ++ int i; ++ ++ if (!pdata) { ++ dev_err(&client->dev, "No platform data!\n"); ++ return -EIO; ++ } ++ ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ mutex_init(&info->lock); ++ sd = &info->sd; ++ v4l2_i2c_subdev_init(sd, client, &noon010_ops); ++ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); ++ ++ sd->internal_ops = &noon010_subdev_internal_ops; ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ v4l2_ctrl_handler_init(&info->hdl, 3); ++ ++ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ++ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, ++ V4L2_CID_RED_BALANCE, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, ++ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); ++ ++ sd->ctrl_handler = &info->hdl; ++ ++ ret = info->hdl.error; ++ if (ret) ++ goto np_err; ++ ++ info->i2c_reg_page = -1; ++ info->gpio_nreset = -EINVAL; ++ info->gpio_nstby = -EINVAL; ++ info->curr_fmt = &noon010_formats[0]; ++ info->curr_win = &noon010_sizes[0]; ++ ++ if (gpio_is_valid(pdata->gpio_nreset)) { ++ ret = gpio_request(pdata->gpio_nreset, "NOON010PC30 NRST"); ++ if (ret) { ++ dev_err(&client->dev, "GPIO request error: %d\n", ret); ++ goto np_err; ++ } ++ info->gpio_nreset = pdata->gpio_nreset; ++ gpio_direction_output(info->gpio_nreset, 0); ++ gpio_export(info->gpio_nreset, 0); ++ } ++ ++ if (gpio_is_valid(pdata->gpio_nstby)) { ++ ret = gpio_request(pdata->gpio_nstby, "NOON010PC30 NSTBY"); ++ if (ret) { ++ dev_err(&client->dev, "GPIO request error: %d\n", ret); ++ goto np_gpio_err; ++ } ++ info->gpio_nstby = pdata->gpio_nstby; ++ gpio_direction_output(info->gpio_nstby, 0); ++ gpio_export(info->gpio_nstby, 0); ++ } ++ ++ for (i = 0; i < NOON010_NUM_SUPPLIES; i++) ++ info->supply[i].supply = noon010_supply_name[i]; ++ ++ ret = regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, ++ info->supply); ++ if (ret) ++ goto np_reg_err; ++ ++ info->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ ret = media_entity_init(&sd->entity, 1, &info->pad, 0); ++ if (ret < 0) ++ goto np_me_err; ++ ++ ret = noon010_detect(client, info); ++ if (!ret) ++ return 0; ++ ++np_me_err: ++ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); ++np_reg_err: ++ if (gpio_is_valid(info->gpio_nstby)) ++ gpio_free(info->gpio_nstby); ++np_gpio_err: ++ if (gpio_is_valid(info->gpio_nreset)) ++ gpio_free(info->gpio_nreset); ++np_err: ++ v4l2_ctrl_handler_free(&info->hdl); ++ v4l2_device_unregister_subdev(sd); ++ kfree(info); ++ return ret; ++} ++ ++static int noon010_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct noon010_info *info = to_noon010(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&info->hdl); ++ ++ regulator_bulk_free(NOON010_NUM_SUPPLIES, info->supply); ++ ++ if (gpio_is_valid(info->gpio_nreset)) ++ gpio_free(info->gpio_nreset); ++ ++ if (gpio_is_valid(info->gpio_nstby)) ++ gpio_free(info->gpio_nstby); ++ ++ media_entity_cleanup(&sd->entity); ++ kfree(info); ++ return 0; ++} ++ ++static const struct i2c_device_id noon010_id[] = { ++ { MODULE_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, noon010_id); ++ ++ ++static struct i2c_driver noon010_i2c_driver = { ++ .driver = { ++ .name = MODULE_NAME ++ }, ++ .probe = noon010_probe, ++ .remove = noon010_remove, ++ .id_table = noon010_id, ++}; ++ ++module_i2c_driver(noon010_i2c_driver); ++ ++MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); ++MODULE_AUTHOR("Sylwester Nawrocki "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c +new file mode 100644 +index 0000000..882ddf6 +--- /dev/null ++++ b/drivers/media/i2c/ov7670.c +@@ -0,0 +1,1586 @@ ++/* ++ * A V4L2 driver for OmniVision OV7670 cameras. ++ * ++ * Copyright 2006 One Laptop Per Child Association, Inc. Written ++ * by Jonathan Corbet with substantial inspiration from Mark ++ * McClelland's ovcamchip code. ++ * ++ * Copyright 2006-7 Jonathan Corbet ++ * ++ * This file may be distributed under the terms of the GNU General ++ * Public License, version 2. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Jonathan Corbet "); ++MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++module_param(debug, bool, 0644); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++/* ++ * Basic window sizes. These probably belong somewhere more globally ++ * useful. ++ */ ++#define VGA_WIDTH 640 ++#define VGA_HEIGHT 480 ++#define QVGA_WIDTH 320 ++#define QVGA_HEIGHT 240 ++#define CIF_WIDTH 352 ++#define CIF_HEIGHT 288 ++#define QCIF_WIDTH 176 ++#define QCIF_HEIGHT 144 ++ ++/* ++ * The 7670 sits on i2c with ID 0x42 ++ */ ++#define OV7670_I2C_ADDR 0x42 ++ ++/* Registers */ ++#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ ++#define REG_BLUE 0x01 /* blue gain */ ++#define REG_RED 0x02 /* red gain */ ++#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ ++#define REG_COM1 0x04 /* Control 1 */ ++#define COM1_CCIR656 0x40 /* CCIR656 enable */ ++#define REG_BAVE 0x05 /* U/B Average level */ ++#define REG_GbAVE 0x06 /* Y/Gb Average level */ ++#define REG_AECHH 0x07 /* AEC MS 5 bits */ ++#define REG_RAVE 0x08 /* V/R Average level */ ++#define REG_COM2 0x09 /* Control 2 */ ++#define COM2_SSLEEP 0x10 /* Soft sleep mode */ ++#define REG_PID 0x0a /* Product ID MSB */ ++#define REG_VER 0x0b /* Product ID LSB */ ++#define REG_COM3 0x0c /* Control 3 */ ++#define COM3_SWAP 0x40 /* Byte swap */ ++#define COM3_SCALEEN 0x08 /* Enable scaling */ ++#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ ++#define REG_COM4 0x0d /* Control 4 */ ++#define REG_COM5 0x0e /* All "reserved" */ ++#define REG_COM6 0x0f /* Control 6 */ ++#define REG_AECH 0x10 /* More bits of AEC value */ ++#define REG_CLKRC 0x11 /* Clocl control */ ++#define CLK_EXT 0x40 /* Use external clock directly */ ++#define CLK_SCALE 0x3f /* Mask for internal clock scale */ ++#define REG_COM7 0x12 /* Control 7 */ ++#define COM7_RESET 0x80 /* Register reset */ ++#define COM7_FMT_MASK 0x38 ++#define COM7_FMT_VGA 0x00 ++#define COM7_FMT_CIF 0x20 /* CIF format */ ++#define COM7_FMT_QVGA 0x10 /* QVGA format */ ++#define COM7_FMT_QCIF 0x08 /* QCIF format */ ++#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ ++#define COM7_YUV 0x00 /* YUV */ ++#define COM7_BAYER 0x01 /* Bayer format */ ++#define COM7_PBAYER 0x05 /* "Processed bayer" */ ++#define REG_COM8 0x13 /* Control 8 */ ++#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ ++#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ ++#define COM8_BFILT 0x20 /* Band filter enable */ ++#define COM8_AGC 0x04 /* Auto gain enable */ ++#define COM8_AWB 0x02 /* White balance enable */ ++#define COM8_AEC 0x01 /* Auto exposure enable */ ++#define REG_COM9 0x14 /* Control 9 - gain ceiling */ ++#define REG_COM10 0x15 /* Control 10 */ ++#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ ++#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ ++#define COM10_HREF_REV 0x08 /* Reverse HREF */ ++#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ ++#define COM10_VS_NEG 0x02 /* VSYNC negative */ ++#define COM10_HS_NEG 0x01 /* HSYNC negative */ ++#define REG_HSTART 0x17 /* Horiz start high bits */ ++#define REG_HSTOP 0x18 /* Horiz stop high bits */ ++#define REG_VSTART 0x19 /* Vert start high bits */ ++#define REG_VSTOP 0x1a /* Vert stop high bits */ ++#define REG_PSHFT 0x1b /* Pixel delay after HREF */ ++#define REG_MIDH 0x1c /* Manuf. ID high */ ++#define REG_MIDL 0x1d /* Manuf. ID low */ ++#define REG_MVFP 0x1e /* Mirror / vflip */ ++#define MVFP_MIRROR 0x20 /* Mirror image */ ++#define MVFP_FLIP 0x10 /* Vertical flip */ ++ ++#define REG_AEW 0x24 /* AGC upper limit */ ++#define REG_AEB 0x25 /* AGC lower limit */ ++#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ ++#define REG_HSYST 0x30 /* HSYNC rising edge delay */ ++#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ ++#define REG_HREF 0x32 /* HREF pieces */ ++#define REG_TSLB 0x3a /* lots of stuff */ ++#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ ++#define REG_COM11 0x3b /* Control 11 */ ++#define COM11_NIGHT 0x80 /* NIght mode enable */ ++#define COM11_NMFR 0x60 /* Two bit NM frame rate */ ++#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ ++#define COM11_50HZ 0x08 /* Manual 50Hz select */ ++#define COM11_EXP 0x02 ++#define REG_COM12 0x3c /* Control 12 */ ++#define COM12_HREF 0x80 /* HREF always */ ++#define REG_COM13 0x3d /* Control 13 */ ++#define COM13_GAMMA 0x80 /* Gamma enable */ ++#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ ++#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ ++#define REG_COM14 0x3e /* Control 14 */ ++#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ ++#define REG_EDGE 0x3f /* Edge enhancement factor */ ++#define REG_COM15 0x40 /* Control 15 */ ++#define COM15_R10F0 0x00 /* Data range 10 to F0 */ ++#define COM15_R01FE 0x80 /* 01 to FE */ ++#define COM15_R00FF 0xc0 /* 00 to FF */ ++#define COM15_RGB565 0x10 /* RGB565 output */ ++#define COM15_RGB555 0x30 /* RGB555 output */ ++#define REG_COM16 0x41 /* Control 16 */ ++#define COM16_AWBGAIN 0x08 /* AWB gain enable */ ++#define REG_COM17 0x42 /* Control 17 */ ++#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ ++#define COM17_CBAR 0x08 /* DSP Color bar */ ++ ++/* ++ * This matrix defines how the colors are generated, must be ++ * tweaked to adjust hue and saturation. ++ * ++ * Order: v-red, v-green, v-blue, u-red, u-green, u-blue ++ * ++ * They are nine-bit signed quantities, with the sign bit ++ * stored in 0x58. Sign for v-red is bit 0, and up from there. ++ */ ++#define REG_CMATRIX_BASE 0x4f ++#define CMATRIX_LEN 6 ++#define REG_CMATRIX_SIGN 0x58 ++ ++ ++#define REG_BRIGHT 0x55 /* Brightness */ ++#define REG_CONTRAS 0x56 /* Contrast control */ ++ ++#define REG_GFIX 0x69 /* Fix gain control */ ++ ++#define REG_REG76 0x76 /* OV's name */ ++#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ ++#define R76_WHTPCOR 0x40 /* White pixel correction enable */ ++ ++#define REG_RGB444 0x8c /* RGB 444 control */ ++#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ ++#define R444_RGBX 0x01 /* Empty nibble at end */ ++ ++#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ ++#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ ++ ++#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ ++#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ ++#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ ++#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ ++#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ ++#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ ++#define REG_BD60MAX 0xab /* 60hz banding step limit */ ++ ++ ++/* ++ * Information we maintain about a known sensor. ++ */ ++struct ov7670_format_struct; /* coming later */ ++struct ov7670_info { ++ struct v4l2_subdev sd; ++ struct ov7670_format_struct *fmt; /* Current format */ ++ unsigned char sat; /* Saturation value */ ++ int hue; /* Hue value */ ++ int min_width; /* Filter out smaller sizes */ ++ int min_height; /* Filter out smaller sizes */ ++ int clock_speed; /* External clock speed (MHz) */ ++ u8 clkrc; /* Clock divider value */ ++ bool use_smbus; /* Use smbus I/O instead of I2C */ ++}; ++ ++static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ov7670_info, sd); ++} ++ ++ ++ ++/* ++ * The default register settings, as obtained from OmniVision. There ++ * is really no making sense of most of these - lots of "reserved" values ++ * and such. ++ * ++ * These settings give VGA YUYV. ++ */ ++ ++struct regval_list { ++ unsigned char reg_num; ++ unsigned char value; ++}; ++ ++static struct regval_list ov7670_default_regs[] = { ++ { REG_COM7, COM7_RESET }, ++/* ++ * Clock scale: 3 = 15fps ++ * 2 = 20fps ++ * 1 = 30fps ++ */ ++ { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ ++ { REG_TSLB, 0x04 }, /* OV */ ++ { REG_COM7, 0 }, /* VGA */ ++ /* ++ * Set the hardware window. These values from OV don't entirely ++ * make sense - hstop is less than hstart. But they work... ++ */ ++ { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, ++ { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, ++ { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, ++ ++ { REG_COM3, 0 }, { REG_COM14, 0 }, ++ /* Mystery scaling numbers */ ++ { 0x70, 0x3a }, { 0x71, 0x35 }, ++ { 0x72, 0x11 }, { 0x73, 0xf0 }, ++ { 0xa2, 0x02 }, { REG_COM10, 0x0 }, ++ ++ /* Gamma curve values */ ++ { 0x7a, 0x20 }, { 0x7b, 0x10 }, ++ { 0x7c, 0x1e }, { 0x7d, 0x35 }, ++ { 0x7e, 0x5a }, { 0x7f, 0x69 }, ++ { 0x80, 0x76 }, { 0x81, 0x80 }, ++ { 0x82, 0x88 }, { 0x83, 0x8f }, ++ { 0x84, 0x96 }, { 0x85, 0xa3 }, ++ { 0x86, 0xaf }, { 0x87, 0xc4 }, ++ { 0x88, 0xd7 }, { 0x89, 0xe8 }, ++ ++ /* AGC and AEC parameters. Note we start by disabling those features, ++ then turn them only after tweaking the values. */ ++ { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, ++ { REG_GAIN, 0 }, { REG_AECH, 0 }, ++ { REG_COM4, 0x40 }, /* magic reserved bit */ ++ { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ ++ { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, ++ { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, ++ { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, ++ { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ ++ { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, ++ { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, ++ { REG_HAECC7, 0x94 }, ++ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, ++ ++ /* Almost all of these are magic "reserved" values. */ ++ { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, ++ { 0x16, 0x02 }, { REG_MVFP, 0x07 }, ++ { 0x21, 0x02 }, { 0x22, 0x91 }, ++ { 0x29, 0x07 }, { 0x33, 0x0b }, ++ { 0x35, 0x0b }, { 0x37, 0x1d }, ++ { 0x38, 0x71 }, { 0x39, 0x2a }, ++ { REG_COM12, 0x78 }, { 0x4d, 0x40 }, ++ { 0x4e, 0x20 }, { REG_GFIX, 0 }, ++ { 0x6b, 0x4a }, { 0x74, 0x10 }, ++ { 0x8d, 0x4f }, { 0x8e, 0 }, ++ { 0x8f, 0 }, { 0x90, 0 }, ++ { 0x91, 0 }, { 0x96, 0 }, ++ { 0x9a, 0 }, { 0xb0, 0x84 }, ++ { 0xb1, 0x0c }, { 0xb2, 0x0e }, ++ { 0xb3, 0x82 }, { 0xb8, 0x0a }, ++ ++ /* More reserved magic, some of which tweaks white balance */ ++ { 0x43, 0x0a }, { 0x44, 0xf0 }, ++ { 0x45, 0x34 }, { 0x46, 0x58 }, ++ { 0x47, 0x28 }, { 0x48, 0x3a }, ++ { 0x59, 0x88 }, { 0x5a, 0x88 }, ++ { 0x5b, 0x44 }, { 0x5c, 0x67 }, ++ { 0x5d, 0x49 }, { 0x5e, 0x0e }, ++ { 0x6c, 0x0a }, { 0x6d, 0x55 }, ++ { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ ++ { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, ++ { REG_RED, 0x60 }, ++ { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, ++ ++ /* Matrix coefficients */ ++ { 0x4f, 0x80 }, { 0x50, 0x80 }, ++ { 0x51, 0 }, { 0x52, 0x22 }, ++ { 0x53, 0x5e }, { 0x54, 0x80 }, ++ { 0x58, 0x9e }, ++ ++ { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, ++ { 0x75, 0x05 }, { 0x76, 0xe1 }, ++ { 0x4c, 0 }, { 0x77, 0x01 }, ++ { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, ++ { 0xc9, 0x60 }, { REG_COM16, 0x38 }, ++ { 0x56, 0x40 }, ++ ++ { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, ++ { 0xa4, 0x88 }, { 0x96, 0 }, ++ { 0x97, 0x30 }, { 0x98, 0x20 }, ++ { 0x99, 0x30 }, { 0x9a, 0x84 }, ++ { 0x9b, 0x29 }, { 0x9c, 0x03 }, ++ { 0x9d, 0x4c }, { 0x9e, 0x3f }, ++ { 0x78, 0x04 }, ++ ++ /* Extra-weird stuff. Some sort of multiplexor register */ ++ { 0x79, 0x01 }, { 0xc8, 0xf0 }, ++ { 0x79, 0x0f }, { 0xc8, 0x00 }, ++ { 0x79, 0x10 }, { 0xc8, 0x7e }, ++ { 0x79, 0x0a }, { 0xc8, 0x80 }, ++ { 0x79, 0x0b }, { 0xc8, 0x01 }, ++ { 0x79, 0x0c }, { 0xc8, 0x0f }, ++ { 0x79, 0x0d }, { 0xc8, 0x20 }, ++ { 0x79, 0x09 }, { 0xc8, 0x80 }, ++ { 0x79, 0x02 }, { 0xc8, 0xc0 }, ++ { 0x79, 0x03 }, { 0xc8, 0x40 }, ++ { 0x79, 0x05 }, { 0xc8, 0x30 }, ++ { 0x79, 0x26 }, ++ ++ { 0xff, 0xff }, /* END MARKER */ ++}; ++ ++ ++/* ++ * Here we'll try to encapsulate the changes for just the output ++ * video format. ++ * ++ * RGB656 and YUV422 come from OV; RGB444 is homebrewed. ++ * ++ * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. ++ */ ++ ++ ++static struct regval_list ov7670_fmt_yuv422[] = { ++ { REG_COM7, 0x0 }, /* Selects YUV mode */ ++ { REG_RGB444, 0 }, /* No RGB444 please */ ++ { REG_COM1, 0 }, /* CCIR601 */ ++ { REG_COM15, COM15_R00FF }, ++ { REG_COM9, 0x48 }, /* 32x gain ceiling; 0x8 is reserved bit */ ++ { 0x4f, 0x80 }, /* "matrix coefficient 1" */ ++ { 0x50, 0x80 }, /* "matrix coefficient 2" */ ++ { 0x51, 0 }, /* vb */ ++ { 0x52, 0x22 }, /* "matrix coefficient 4" */ ++ { 0x53, 0x5e }, /* "matrix coefficient 5" */ ++ { 0x54, 0x80 }, /* "matrix coefficient 6" */ ++ { REG_COM13, COM13_GAMMA|COM13_UVSAT }, ++ { 0xff, 0xff }, ++}; ++ ++static struct regval_list ov7670_fmt_rgb565[] = { ++ { REG_COM7, COM7_RGB }, /* Selects RGB mode */ ++ { REG_RGB444, 0 }, /* No RGB444 please */ ++ { REG_COM1, 0x0 }, /* CCIR601 */ ++ { REG_COM15, COM15_RGB565 }, ++ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ ++ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ ++ { 0x50, 0xb3 }, /* "matrix coefficient 2" */ ++ { 0x51, 0 }, /* vb */ ++ { 0x52, 0x3d }, /* "matrix coefficient 4" */ ++ { 0x53, 0xa7 }, /* "matrix coefficient 5" */ ++ { 0x54, 0xe4 }, /* "matrix coefficient 6" */ ++ { REG_COM13, COM13_GAMMA|COM13_UVSAT }, ++ { 0xff, 0xff }, ++}; ++ ++static struct regval_list ov7670_fmt_rgb444[] = { ++ { REG_COM7, COM7_RGB }, /* Selects RGB mode */ ++ { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ ++ { REG_COM1, 0x0 }, /* CCIR601 */ ++ { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ ++ { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ ++ { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ ++ { 0x50, 0xb3 }, /* "matrix coefficient 2" */ ++ { 0x51, 0 }, /* vb */ ++ { 0x52, 0x3d }, /* "matrix coefficient 4" */ ++ { 0x53, 0xa7 }, /* "matrix coefficient 5" */ ++ { 0x54, 0xe4 }, /* "matrix coefficient 6" */ ++ { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ ++ { 0xff, 0xff }, ++}; ++ ++static struct regval_list ov7670_fmt_raw[] = { ++ { REG_COM7, COM7_BAYER }, ++ { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ ++ { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ ++ { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ ++ { 0xff, 0xff }, ++}; ++ ++ ++ ++/* ++ * Low-level register I/O. ++ * ++ * Note that there are two versions of these. On the XO 1, the ++ * i2c controller only does SMBUS, so that's what we use. The ++ * ov7670 is not really an SMBUS device, though, so the communication ++ * is not always entirely reliable. ++ */ ++static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ ret = i2c_smbus_read_byte_data(client, reg); ++ if (ret >= 0) { ++ *value = (unsigned char)ret; ++ ret = 0; ++ } ++ return ret; ++} ++ ++ ++static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = i2c_smbus_write_byte_data(client, reg, value); ++ ++ if (reg == REG_COM7 && (value & COM7_RESET)) ++ msleep(5); /* Wait for reset to run */ ++ return ret; ++} ++ ++/* ++ * On most platforms, we'd rather do straight i2c I/O. ++ */ ++static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 data = reg; ++ struct i2c_msg msg; ++ int ret; ++ ++ /* ++ * Send out the register address... ++ */ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 1; ++ msg.buf = &data; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ printk(KERN_ERR "Error %d on register write\n", ret); ++ return ret; ++ } ++ /* ++ * ...then read back the result. ++ */ ++ msg.flags = I2C_M_RD; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret >= 0) { ++ *value = data; ++ ret = 0; ++ } ++ return ret; ++} ++ ++ ++static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct i2c_msg msg; ++ unsigned char data[2] = { reg, value }; ++ int ret; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = data; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret > 0) ++ ret = 0; ++ if (reg == REG_COM7 && (value & COM7_RESET)) ++ msleep(5); /* Wait for reset to run */ ++ return ret; ++} ++ ++static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char *value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ if (info->use_smbus) ++ return ov7670_read_smbus(sd, reg, value); ++ else ++ return ov7670_read_i2c(sd, reg, value); ++} ++ ++static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, ++ unsigned char value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ if (info->use_smbus) ++ return ov7670_write_smbus(sd, reg, value); ++ else ++ return ov7670_write_i2c(sd, reg, value); ++} ++ ++/* ++ * Write a list of register settings; ff/ff stops the process. ++ */ ++static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) ++{ ++ while (vals->reg_num != 0xff || vals->value != 0xff) { ++ int ret = ov7670_write(sd, vals->reg_num, vals->value); ++ if (ret < 0) ++ return ret; ++ vals++; ++ } ++ return 0; ++} ++ ++ ++/* ++ * Stuff that knows about the sensor. ++ */ ++static int ov7670_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ ov7670_write(sd, REG_COM7, COM7_RESET); ++ msleep(1); ++ return 0; ++} ++ ++ ++static int ov7670_init(struct v4l2_subdev *sd, u32 val) ++{ ++ return ov7670_write_array(sd, ov7670_default_regs); ++} ++ ++ ++ ++static int ov7670_detect(struct v4l2_subdev *sd) ++{ ++ unsigned char v; ++ int ret; ++ ++ ret = ov7670_init(sd, 0); ++ if (ret < 0) ++ return ret; ++ ret = ov7670_read(sd, REG_MIDH, &v); ++ if (ret < 0) ++ return ret; ++ if (v != 0x7f) /* OV manuf. id. */ ++ return -ENODEV; ++ ret = ov7670_read(sd, REG_MIDL, &v); ++ if (ret < 0) ++ return ret; ++ if (v != 0xa2) ++ return -ENODEV; ++ /* ++ * OK, we know we have an OmniVision chip...but which one? ++ */ ++ ret = ov7670_read(sd, REG_PID, &v); ++ if (ret < 0) ++ return ret; ++ if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ ++ return -ENODEV; ++ ret = ov7670_read(sd, REG_VER, &v); ++ if (ret < 0) ++ return ret; ++ if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ ++ return -ENODEV; ++ return 0; ++} ++ ++ ++/* ++ * Store information about the video data format. The color matrix ++ * is deeply tied into the format, so keep the relevant values here. ++ * The magic matrix numbers come from OmniVision. ++ */ ++static struct ov7670_format_struct { ++ enum v4l2_mbus_pixelcode mbus_code; ++ enum v4l2_colorspace colorspace; ++ struct regval_list *regs; ++ int cmatrix[CMATRIX_LEN]; ++} ov7670_formats[] = { ++ { ++ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .regs = ov7670_fmt_yuv422, ++ .cmatrix = { 128, -128, 0, -34, -94, 128 }, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .regs = ov7670_fmt_rgb444, ++ .cmatrix = { 179, -179, 0, -61, -176, 228 }, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .regs = ov7670_fmt_rgb565, ++ .cmatrix = { 179, -179, 0, -61, -176, 228 }, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .regs = ov7670_fmt_raw, ++ .cmatrix = { 0, 0, 0, 0, 0, 0 }, ++ }, ++}; ++#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) ++ ++ ++/* ++ * Then there is the issue of window sizes. Try to capture the info here. ++ */ ++ ++/* ++ * QCIF mode is done (by OV) in a very strange way - it actually looks like ++ * VGA with weird scaling options - they do *not* use the canned QCIF mode ++ * which is allegedly provided by the sensor. So here's the weird register ++ * settings. ++ */ ++static struct regval_list ov7670_qcif_regs[] = { ++ { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, ++ { REG_COM3, COM3_DCWEN }, ++ { REG_COM14, COM14_DCWEN | 0x01}, ++ { 0x73, 0xf1 }, ++ { 0xa2, 0x52 }, ++ { 0x7b, 0x1c }, ++ { 0x7c, 0x28 }, ++ { 0x7d, 0x3c }, ++ { 0x7f, 0x69 }, ++ { REG_COM9, 0x38 }, ++ { 0xa1, 0x0b }, ++ { 0x74, 0x19 }, ++ { 0x9a, 0x80 }, ++ { 0x43, 0x14 }, ++ { REG_COM13, 0xc0 }, ++ { 0xff, 0xff }, ++}; ++ ++static struct ov7670_win_size { ++ int width; ++ int height; ++ unsigned char com7_bit; ++ int hstart; /* Start/stop values for the camera. Note */ ++ int hstop; /* that they do not always make complete */ ++ int vstart; /* sense to humans, but evidently the sensor */ ++ int vstop; /* will do the right thing... */ ++ struct regval_list *regs; /* Regs to tweak */ ++/* h/vref stuff */ ++} ov7670_win_sizes[] = { ++ /* VGA */ ++ { ++ .width = VGA_WIDTH, ++ .height = VGA_HEIGHT, ++ .com7_bit = COM7_FMT_VGA, ++ .hstart = 158, /* These values from */ ++ .hstop = 14, /* Omnivision */ ++ .vstart = 10, ++ .vstop = 490, ++ .regs = NULL, ++ }, ++ /* CIF */ ++ { ++ .width = CIF_WIDTH, ++ .height = CIF_HEIGHT, ++ .com7_bit = COM7_FMT_CIF, ++ .hstart = 170, /* Empirically determined */ ++ .hstop = 90, ++ .vstart = 14, ++ .vstop = 494, ++ .regs = NULL, ++ }, ++ /* QVGA */ ++ { ++ .width = QVGA_WIDTH, ++ .height = QVGA_HEIGHT, ++ .com7_bit = COM7_FMT_QVGA, ++ .hstart = 168, /* Empirically determined */ ++ .hstop = 24, ++ .vstart = 12, ++ .vstop = 492, ++ .regs = NULL, ++ }, ++ /* QCIF */ ++ { ++ .width = QCIF_WIDTH, ++ .height = QCIF_HEIGHT, ++ .com7_bit = COM7_FMT_VGA, /* see comment above */ ++ .hstart = 456, /* Empirically determined */ ++ .hstop = 24, ++ .vstart = 14, ++ .vstop = 494, ++ .regs = ov7670_qcif_regs, ++ }, ++}; ++ ++#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) ++ ++ ++/* ++ * Store a set of start/stop values into the camera. ++ */ ++static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, ++ int vstart, int vstop) ++{ ++ int ret; ++ unsigned char v; ++/* ++ * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of ++ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is ++ * a mystery "edge offset" value in the top two bits of href. ++ */ ++ ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); ++ ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); ++ ret += ov7670_read(sd, REG_HREF, &v); ++ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); ++ msleep(10); ++ ret += ov7670_write(sd, REG_HREF, v); ++/* ++ * Vertical: similar arrangement, but only 10 bits. ++ */ ++ ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); ++ ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); ++ ret += ov7670_read(sd, REG_VREF, &v); ++ v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); ++ msleep(10); ++ ret += ov7670_write(sd, REG_VREF, v); ++ return ret; ++} ++ ++ ++static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= N_OV7670_FMTS) ++ return -EINVAL; ++ ++ *code = ov7670_formats[index].mbus_code; ++ return 0; ++} ++ ++static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt, ++ struct ov7670_format_struct **ret_fmt, ++ struct ov7670_win_size **ret_wsize) ++{ ++ int index; ++ struct ov7670_win_size *wsize; ++ ++ for (index = 0; index < N_OV7670_FMTS; index++) ++ if (ov7670_formats[index].mbus_code == fmt->code) ++ break; ++ if (index >= N_OV7670_FMTS) { ++ /* default to first format */ ++ index = 0; ++ fmt->code = ov7670_formats[0].mbus_code; ++ } ++ if (ret_fmt != NULL) ++ *ret_fmt = ov7670_formats + index; ++ /* ++ * Fields: the OV devices claim to be progressive. ++ */ ++ fmt->field = V4L2_FIELD_NONE; ++ /* ++ * Round requested image size down to the nearest ++ * we support, but not below the smallest. ++ */ ++ for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; ++ wsize++) ++ if (fmt->width >= wsize->width && fmt->height >= wsize->height) ++ break; ++ if (wsize >= ov7670_win_sizes + N_WIN_SIZES) ++ wsize--; /* Take the smallest one */ ++ if (ret_wsize != NULL) ++ *ret_wsize = wsize; ++ /* ++ * Note the size we'll actually handle. ++ */ ++ fmt->width = wsize->width; ++ fmt->height = wsize->height; ++ fmt->colorspace = ov7670_formats[index].colorspace; ++ return 0; ++} ++ ++static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); ++} ++ ++/* ++ * Set a format. ++ */ ++static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct ov7670_format_struct *ovfmt; ++ struct ov7670_win_size *wsize; ++ struct ov7670_info *info = to_state(sd); ++ unsigned char com7; ++ int ret; ++ ++ ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); ++ ++ if (ret) ++ return ret; ++ /* ++ * COM7 is a pain in the ass, it doesn't like to be read then ++ * quickly written afterward. But we have everything we need ++ * to set it absolutely here, as long as the format-specific ++ * register sets list it first. ++ */ ++ com7 = ovfmt->regs[0].value; ++ com7 |= wsize->com7_bit; ++ ov7670_write(sd, REG_COM7, com7); ++ /* ++ * Now write the rest of the array. Also store start/stops ++ */ ++ ov7670_write_array(sd, ovfmt->regs + 1); ++ ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, ++ wsize->vstop); ++ ret = 0; ++ if (wsize->regs) ++ ret = ov7670_write_array(sd, wsize->regs); ++ info->fmt = ovfmt; ++ ++ /* ++ * If we're running RGB565, we must rewrite clkrc after setting ++ * the other parameters or the image looks poor. If we're *not* ++ * doing RGB565, we must not rewrite clkrc or the image looks ++ * *really* poor. ++ * ++ * (Update) Now that we retain clkrc state, we should be able ++ * to write it unconditionally, and that will make the frame ++ * rate persistent too. ++ */ ++ if (ret == 0) ++ ret = ov7670_write(sd, REG_CLKRC, info->clkrc); ++ return 0; ++} ++ ++/* ++ * Implement G/S_PARM. There is a "high quality" mode we could try ++ * to do someday; for now, we just do the frame rate tweak. ++ */ ++static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ struct ov7670_info *info = to_state(sd); ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(cp, 0, sizeof(struct v4l2_captureparm)); ++ cp->capability = V4L2_CAP_TIMEPERFRAME; ++ cp->timeperframe.numerator = 1; ++ cp->timeperframe.denominator = info->clock_speed; ++ if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) ++ cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); ++ return 0; ++} ++ ++static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ struct v4l2_fract *tpf = &cp->timeperframe; ++ struct ov7670_info *info = to_state(sd); ++ int div; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (cp->extendedmode != 0) ++ return -EINVAL; ++ ++ if (tpf->numerator == 0 || tpf->denominator == 0) ++ div = 1; /* Reset to full rate */ ++ else ++ div = (tpf->numerator * info->clock_speed) / tpf->denominator; ++ if (div == 0) ++ div = 1; ++ else if (div > CLK_SCALE) ++ div = CLK_SCALE; ++ info->clkrc = (info->clkrc & 0x80) | div; ++ tpf->numerator = 1; ++ tpf->denominator = info->clock_speed / div; ++ return ov7670_write(sd, REG_CLKRC, info->clkrc); ++} ++ ++ ++/* ++ * Frame intervals. Since frame rates are controlled with the clock ++ * divider, we can only do 30/n for integer n values. So no continuous ++ * or stepwise options. Here we just pick a handful of logical values. ++ */ ++ ++static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; ++ ++static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, ++ struct v4l2_frmivalenum *interval) ++{ ++ if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) ++ return -EINVAL; ++ interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; ++ interval->discrete.numerator = 1; ++ interval->discrete.denominator = ov7670_frame_rates[interval->index]; ++ return 0; ++} ++ ++/* ++ * Frame size enumeration ++ */ ++static int ov7670_enum_framesizes(struct v4l2_subdev *sd, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ struct ov7670_info *info = to_state(sd); ++ int i; ++ int num_valid = -1; ++ __u32 index = fsize->index; ++ ++ /* ++ * If a minimum width/height was requested, filter out the capture ++ * windows that fall outside that. ++ */ ++ for (i = 0; i < N_WIN_SIZES; i++) { ++ struct ov7670_win_size *win = &ov7670_win_sizes[index]; ++ if (info->min_width && win->width < info->min_width) ++ continue; ++ if (info->min_height && win->height < info->min_height) ++ continue; ++ if (index == ++num_valid) { ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete.width = win->width; ++ fsize->discrete.height = win->height; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/* ++ * Code for dealing with controls. ++ */ ++ ++static int ov7670_store_cmatrix(struct v4l2_subdev *sd, ++ int matrix[CMATRIX_LEN]) ++{ ++ int i, ret; ++ unsigned char signbits = 0; ++ ++ /* ++ * Weird crap seems to exist in the upper part of ++ * the sign bits register, so let's preserve it. ++ */ ++ ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); ++ signbits &= 0xc0; ++ ++ for (i = 0; i < CMATRIX_LEN; i++) { ++ unsigned char raw; ++ ++ if (matrix[i] < 0) { ++ signbits |= (1 << i); ++ if (matrix[i] < -255) ++ raw = 0xff; ++ else ++ raw = (-1 * matrix[i]) & 0xff; ++ } ++ else { ++ if (matrix[i] > 255) ++ raw = 0xff; ++ else ++ raw = matrix[i] & 0xff; ++ } ++ ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); ++ } ++ ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); ++ return ret; ++} ++ ++ ++/* ++ * Hue also requires messing with the color matrix. It also requires ++ * trig functions, which tend not to be well supported in the kernel. ++ * So here is a simple table of sine values, 0-90 degrees, in steps ++ * of five degrees. Values are multiplied by 1000. ++ * ++ * The following naive approximate trig functions require an argument ++ * carefully limited to -180 <= theta <= 180. ++ */ ++#define SIN_STEP 5 ++static const int ov7670_sin_table[] = { ++ 0, 87, 173, 258, 342, 422, ++ 499, 573, 642, 707, 766, 819, ++ 866, 906, 939, 965, 984, 996, ++ 1000 ++}; ++ ++static int ov7670_sine(int theta) ++{ ++ int chs = 1; ++ int sine; ++ ++ if (theta < 0) { ++ theta = -theta; ++ chs = -1; ++ } ++ if (theta <= 90) ++ sine = ov7670_sin_table[theta/SIN_STEP]; ++ else { ++ theta -= 90; ++ sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; ++ } ++ return sine*chs; ++} ++ ++static int ov7670_cosine(int theta) ++{ ++ theta = 90 - theta; ++ if (theta > 180) ++ theta -= 360; ++ else if (theta < -180) ++ theta += 360; ++ return ov7670_sine(theta); ++} ++ ++ ++ ++ ++static void ov7670_calc_cmatrix(struct ov7670_info *info, ++ int matrix[CMATRIX_LEN]) ++{ ++ int i; ++ /* ++ * Apply the current saturation setting first. ++ */ ++ for (i = 0; i < CMATRIX_LEN; i++) ++ matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; ++ /* ++ * Then, if need be, rotate the hue value. ++ */ ++ if (info->hue != 0) { ++ int sinth, costh, tmpmatrix[CMATRIX_LEN]; ++ ++ memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); ++ sinth = ov7670_sine(info->hue); ++ costh = ov7670_cosine(info->hue); ++ ++ matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; ++ matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; ++ matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; ++ matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; ++ matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; ++ matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; ++ } ++} ++ ++ ++ ++static int ov7670_s_sat(struct v4l2_subdev *sd, int value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ int matrix[CMATRIX_LEN]; ++ int ret; ++ ++ info->sat = value; ++ ov7670_calc_cmatrix(info, matrix); ++ ret = ov7670_store_cmatrix(sd, matrix); ++ return ret; ++} ++ ++static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ ++ *value = info->sat; ++ return 0; ++} ++ ++static int ov7670_s_hue(struct v4l2_subdev *sd, int value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ int matrix[CMATRIX_LEN]; ++ int ret; ++ ++ if (value < -180 || value > 180) ++ return -EINVAL; ++ info->hue = value; ++ ov7670_calc_cmatrix(info, matrix); ++ ret = ov7670_store_cmatrix(sd, matrix); ++ return ret; ++} ++ ++ ++static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) ++{ ++ struct ov7670_info *info = to_state(sd); ++ ++ *value = info->hue; ++ return 0; ++} ++ ++ ++/* ++ * Some weird registers seem to store values in a sign/magnitude format! ++ */ ++static unsigned char ov7670_sm_to_abs(unsigned char v) ++{ ++ if ((v & 0x80) == 0) ++ return v + 128; ++ return 128 - (v & 0x7f); ++} ++ ++ ++static unsigned char ov7670_abs_to_sm(unsigned char v) ++{ ++ if (v > 127) ++ return v & 0x7f; ++ return (128 - v) | 0x80; ++} ++ ++static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) ++{ ++ unsigned char com8 = 0, v; ++ int ret; ++ ++ ov7670_read(sd, REG_COM8, &com8); ++ com8 &= ~COM8_AEC; ++ ov7670_write(sd, REG_COM8, com8); ++ v = ov7670_abs_to_sm(value); ++ ret = ov7670_write(sd, REG_BRIGHT, v); ++ return ret; ++} ++ ++static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) ++{ ++ unsigned char v = 0; ++ int ret = ov7670_read(sd, REG_BRIGHT, &v); ++ ++ *value = ov7670_sm_to_abs(v); ++ return ret; ++} ++ ++static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) ++{ ++ return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); ++} ++ ++static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) ++{ ++ unsigned char v = 0; ++ int ret = ov7670_read(sd, REG_CONTRAS, &v); ++ ++ *value = v; ++ return ret; ++} ++ ++static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char v = 0; ++ ++ ret = ov7670_read(sd, REG_MVFP, &v); ++ *value = (v & MVFP_MIRROR) == MVFP_MIRROR; ++ return ret; ++} ++ ++ ++static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) ++{ ++ unsigned char v = 0; ++ int ret; ++ ++ ret = ov7670_read(sd, REG_MVFP, &v); ++ if (value) ++ v |= MVFP_MIRROR; ++ else ++ v &= ~MVFP_MIRROR; ++ msleep(10); /* FIXME */ ++ ret += ov7670_write(sd, REG_MVFP, v); ++ return ret; ++} ++ ++ ++ ++static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char v = 0; ++ ++ ret = ov7670_read(sd, REG_MVFP, &v); ++ *value = (v & MVFP_FLIP) == MVFP_FLIP; ++ return ret; ++} ++ ++ ++static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) ++{ ++ unsigned char v = 0; ++ int ret; ++ ++ ret = ov7670_read(sd, REG_MVFP, &v); ++ if (value) ++ v |= MVFP_FLIP; ++ else ++ v &= ~MVFP_FLIP; ++ msleep(10); /* FIXME */ ++ ret += ov7670_write(sd, REG_MVFP, v); ++ return ret; ++} ++ ++/* ++ * GAIN is split between REG_GAIN and REG_VREF[7:6]. If one believes ++ * the data sheet, the VREF parts should be the most significant, but ++ * experience shows otherwise. There seems to be little value in ++ * messing with the VREF bits, so we leave them alone. ++ */ ++static int ov7670_g_gain(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char gain; ++ ++ ret = ov7670_read(sd, REG_GAIN, &gain); ++ *value = gain; ++ return ret; ++} ++ ++static int ov7670_s_gain(struct v4l2_subdev *sd, int value) ++{ ++ int ret; ++ unsigned char com8; ++ ++ ret = ov7670_write(sd, REG_GAIN, value & 0xff); ++ /* Have to turn off AGC as well */ ++ if (ret == 0) { ++ ret = ov7670_read(sd, REG_COM8, &com8); ++ ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AGC); ++ } ++ return ret; ++} ++ ++/* ++ * Tweak autogain. ++ */ ++static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char com8; ++ ++ ret = ov7670_read(sd, REG_COM8, &com8); ++ *value = (com8 & COM8_AGC) != 0; ++ return ret; ++} ++ ++static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) ++{ ++ int ret; ++ unsigned char com8; ++ ++ ret = ov7670_read(sd, REG_COM8, &com8); ++ if (ret == 0) { ++ if (value) ++ com8 |= COM8_AGC; ++ else ++ com8 &= ~COM8_AGC; ++ ret = ov7670_write(sd, REG_COM8, com8); ++ } ++ return ret; ++} ++ ++/* ++ * Exposure is spread all over the place: top 6 bits in AECHH, middle ++ * 8 in AECH, and two stashed in COM1 just for the hell of it. ++ */ ++static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char com1, aech, aechh; ++ ++ ret = ov7670_read(sd, REG_COM1, &com1) + ++ ov7670_read(sd, REG_AECH, &aech) + ++ ov7670_read(sd, REG_AECHH, &aechh); ++ *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); ++ return ret; ++} ++ ++static int ov7670_s_exp(struct v4l2_subdev *sd, int value) ++{ ++ int ret; ++ unsigned char com1, com8, aech, aechh; ++ ++ ret = ov7670_read(sd, REG_COM1, &com1) + ++ ov7670_read(sd, REG_COM8, &com8); ++ ov7670_read(sd, REG_AECHH, &aechh); ++ if (ret) ++ return ret; ++ ++ com1 = (com1 & 0xfc) | (value & 0x03); ++ aech = (value >> 2) & 0xff; ++ aechh = (aechh & 0xc0) | ((value >> 10) & 0x3f); ++ ret = ov7670_write(sd, REG_COM1, com1) + ++ ov7670_write(sd, REG_AECH, aech) + ++ ov7670_write(sd, REG_AECHH, aechh); ++ /* Have to turn off AEC as well */ ++ if (ret == 0) ++ ret = ov7670_write(sd, REG_COM8, com8 & ~COM8_AEC); ++ return ret; ++} ++ ++/* ++ * Tweak autoexposure. ++ */ ++static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) ++{ ++ int ret; ++ unsigned char com8; ++ enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; ++ ++ ret = ov7670_read(sd, REG_COM8, &com8); ++ if (com8 & COM8_AEC) ++ *atype = V4L2_EXPOSURE_AUTO; ++ else ++ *atype = V4L2_EXPOSURE_MANUAL; ++ return ret; ++} ++ ++static int ov7670_s_autoexp(struct v4l2_subdev *sd, ++ enum v4l2_exposure_auto_type value) ++{ ++ int ret; ++ unsigned char com8; ++ ++ ret = ov7670_read(sd, REG_COM8, &com8); ++ if (ret == 0) { ++ if (value == V4L2_EXPOSURE_AUTO) ++ com8 |= COM8_AEC; ++ else ++ com8 &= ~COM8_AEC; ++ ret = ov7670_write(sd, REG_COM8, com8); ++ } ++ return ret; ++} ++ ++ ++ ++static int ov7670_queryctrl(struct v4l2_subdev *sd, ++ struct v4l2_queryctrl *qc) ++{ ++ /* Fill in min, max, step and default value for these controls. */ ++ switch (qc->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); ++ case V4L2_CID_CONTRAST: ++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); ++ case V4L2_CID_VFLIP: ++ case V4L2_CID_HFLIP: ++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); ++ case V4L2_CID_SATURATION: ++ return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); ++ case V4L2_CID_HUE: ++ return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); ++ case V4L2_CID_GAIN: ++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); ++ case V4L2_CID_AUTOGAIN: ++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); ++ case V4L2_CID_EXPOSURE: ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); ++ case V4L2_CID_EXPOSURE_AUTO: ++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); ++ } ++ return -EINVAL; ++} ++ ++static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return ov7670_g_brightness(sd, &ctrl->value); ++ case V4L2_CID_CONTRAST: ++ return ov7670_g_contrast(sd, &ctrl->value); ++ case V4L2_CID_SATURATION: ++ return ov7670_g_sat(sd, &ctrl->value); ++ case V4L2_CID_HUE: ++ return ov7670_g_hue(sd, &ctrl->value); ++ case V4L2_CID_VFLIP: ++ return ov7670_g_vflip(sd, &ctrl->value); ++ case V4L2_CID_HFLIP: ++ return ov7670_g_hflip(sd, &ctrl->value); ++ case V4L2_CID_GAIN: ++ return ov7670_g_gain(sd, &ctrl->value); ++ case V4L2_CID_AUTOGAIN: ++ return ov7670_g_autogain(sd, &ctrl->value); ++ case V4L2_CID_EXPOSURE: ++ return ov7670_g_exp(sd, &ctrl->value); ++ case V4L2_CID_EXPOSURE_AUTO: ++ return ov7670_g_autoexp(sd, &ctrl->value); ++ } ++ return -EINVAL; ++} ++ ++static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return ov7670_s_brightness(sd, ctrl->value); ++ case V4L2_CID_CONTRAST: ++ return ov7670_s_contrast(sd, ctrl->value); ++ case V4L2_CID_SATURATION: ++ return ov7670_s_sat(sd, ctrl->value); ++ case V4L2_CID_HUE: ++ return ov7670_s_hue(sd, ctrl->value); ++ case V4L2_CID_VFLIP: ++ return ov7670_s_vflip(sd, ctrl->value); ++ case V4L2_CID_HFLIP: ++ return ov7670_s_hflip(sd, ctrl->value); ++ case V4L2_CID_GAIN: ++ return ov7670_s_gain(sd, ctrl->value); ++ case V4L2_CID_AUTOGAIN: ++ return ov7670_s_autogain(sd, ctrl->value); ++ case V4L2_CID_EXPOSURE: ++ return ov7670_s_exp(sd, ctrl->value); ++ case V4L2_CID_EXPOSURE_AUTO: ++ return ov7670_s_autoexp(sd, ++ (enum v4l2_exposure_auto_type) ctrl->value); ++ } ++ return -EINVAL; ++} ++ ++static int ov7670_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ unsigned char val = 0; ++ int ret; ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ret = ov7670_read(sd, reg->reg & 0xff, &val); ++ reg->val = val; ++ reg->size = 1; ++ return ret; ++} ++ ++static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops ov7670_core_ops = { ++ .g_chip_ident = ov7670_g_chip_ident, ++ .g_ctrl = ov7670_g_ctrl, ++ .s_ctrl = ov7670_s_ctrl, ++ .queryctrl = ov7670_queryctrl, ++ .reset = ov7670_reset, ++ .init = ov7670_init, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov7670_g_register, ++ .s_register = ov7670_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops ov7670_video_ops = { ++ .enum_mbus_fmt = ov7670_enum_mbus_fmt, ++ .try_mbus_fmt = ov7670_try_mbus_fmt, ++ .s_mbus_fmt = ov7670_s_mbus_fmt, ++ .s_parm = ov7670_s_parm, ++ .g_parm = ov7670_g_parm, ++ .enum_frameintervals = ov7670_enum_frameintervals, ++ .enum_framesizes = ov7670_enum_framesizes, ++}; ++ ++static const struct v4l2_subdev_ops ov7670_ops = { ++ .core = &ov7670_core_ops, ++ .video = &ov7670_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int ov7670_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ struct ov7670_info *info; ++ int ret; ++ ++ info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); ++ if (info == NULL) ++ return -ENOMEM; ++ sd = &info->sd; ++ v4l2_i2c_subdev_init(sd, client, &ov7670_ops); ++ ++ info->clock_speed = 30; /* default: a guess */ ++ if (client->dev.platform_data) { ++ struct ov7670_config *config = client->dev.platform_data; ++ ++ /* ++ * Must apply configuration before initializing device, because it ++ * selects I/O method. ++ */ ++ info->min_width = config->min_width; ++ info->min_height = config->min_height; ++ info->use_smbus = config->use_smbus; ++ ++ if (config->clock_speed) ++ info->clock_speed = config->clock_speed; ++ } ++ ++ /* Make sure it's an ov7670 */ ++ ret = ov7670_detect(sd); ++ if (ret) { ++ v4l_dbg(1, debug, client, ++ "chip found @ 0x%x (%s) is not an ov7670 chip.\n", ++ client->addr << 1, client->adapter->name); ++ kfree(info); ++ return ret; ++ } ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ info->fmt = &ov7670_formats[0]; ++ info->sat = 128; /* Review this */ ++ info->clkrc = info->clock_speed / 30; ++ return 0; ++} ++ ++ ++static int ov7670_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id ov7670_id[] = { ++ { "ov7670", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov7670_id); ++ ++static struct i2c_driver ov7670_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ov7670", ++ }, ++ .probe = ov7670_probe, ++ .remove = ov7670_remove, ++ .id_table = ov7670_id, ++}; ++ ++module_i2c_driver(ov7670_driver); +diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c +new file mode 100644 +index 0000000..2750de6 +--- /dev/null ++++ b/drivers/media/i2c/s5k4ecgx.c +@@ -0,0 +1,1036 @@ ++/* ++ * Driver for Samsung S5K4ECGX 1/4" 5Mp CMOS Image Sensor SoC ++ * with an Embedded Image Signal Processor. ++ * ++ * Copyright (C) 2012, Linaro, Sangwook Lee ++ * Copyright (C) 2012, Insignal Co,. Ltd, Homin Lee ++ * ++ * Based on s5k6aa and noon010pc30 driver ++ * Copyright (C) 2011, Samsung Electronics Co., Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++#define S5K4ECGX_DRIVER_NAME "s5k4ecgx" ++#define S5K4ECGX_FIRMWARE "s5k4ecgx.bin" ++ ++/* Firmware revision information */ ++#define REG_FW_REVISION 0x700001a6 ++#define REG_FW_VERSION 0x700001a4 ++#define S5K4ECGX_REVISION_1_1 0x11 ++#define S5K4ECGX_FW_VERSION 0x4ec0 ++ ++/* General purpose parameters */ ++#define REG_USER_BRIGHTNESS 0x7000022c ++#define REG_USER_CONTRAST 0x7000022e ++#define REG_USER_SATURATION 0x70000230 ++ ++#define REG_G_ENABLE_PREV 0x7000023e ++#define REG_G_ENABLE_PREV_CHG 0x70000240 ++#define REG_G_NEW_CFG_SYNC 0x7000024a ++#define REG_G_PREV_IN_WIDTH 0x70000250 ++#define REG_G_PREV_IN_HEIGHT 0x70000252 ++#define REG_G_PREV_IN_XOFFS 0x70000254 ++#define REG_G_PREV_IN_YOFFS 0x70000256 ++#define REG_G_CAP_IN_WIDTH 0x70000258 ++#define REG_G_CAP_IN_HEIGHT 0x7000025a ++#define REG_G_CAP_IN_XOFFS 0x7000025c ++#define REG_G_CAP_IN_YOFFS 0x7000025e ++#define REG_G_INPUTS_CHANGE_REQ 0x70000262 ++#define REG_G_ACTIVE_PREV_CFG 0x70000266 ++#define REG_G_PREV_CFG_CHG 0x70000268 ++#define REG_G_PREV_OPEN_AFTER_CH 0x7000026a ++ ++/* Preview context register sets. n = 0...4. */ ++#define PREG(n, x) ((n) * 0x30 + (x)) ++#define REG_P_OUT_WIDTH(n) PREG(n, 0x700002a6) ++#define REG_P_OUT_HEIGHT(n) PREG(n, 0x700002a8) ++#define REG_P_FMT(n) PREG(n, 0x700002aa) ++#define REG_P_PVI_MASK(n) PREG(n, 0x700002b4) ++#define REG_P_FR_TIME_TYPE(n) PREG(n, 0x700002be) ++#define FR_TIME_DYNAMIC 0 ++#define FR_TIME_FIXED 1 ++#define FR_TIME_FIXED_ACCURATE 2 ++#define REG_P_FR_TIME_Q_TYPE(n) PREG(n, 0x700002c0) ++#define FR_TIME_Q_DYNAMIC 0 ++#define FR_TIME_Q_BEST_FRRATE 1 ++#define FR_TIME_Q_BEST_QUALITY 2 ++ ++/* Frame period in 0.1 ms units */ ++#define REG_P_MAX_FR_TIME(n) PREG(n, 0x700002c2) ++#define REG_P_MIN_FR_TIME(n) PREG(n, 0x700002c4) ++#define US_TO_FR_TIME(__t) ((__t) / 100) ++#define REG_P_PREV_MIRROR(n) PREG(n, 0x700002d0) ++#define REG_P_CAP_MIRROR(n) PREG(n, 0x700002d2) ++ ++#define REG_G_PREVZOOM_IN_WIDTH 0x70000494 ++#define REG_G_PREVZOOM_IN_HEIGHT 0x70000496 ++#define REG_G_PREVZOOM_IN_XOFFS 0x70000498 ++#define REG_G_PREVZOOM_IN_YOFFS 0x7000049a ++#define REG_G_CAPZOOM_IN_WIDTH 0x7000049c ++#define REG_G_CAPZOOM_IN_HEIGHT 0x7000049e ++#define REG_G_CAPZOOM_IN_XOFFS 0x700004a0 ++#define REG_G_CAPZOOM_IN_YOFFS 0x700004a2 ++ ++/* n = 0...4 */ ++#define REG_USER_SHARPNESS(n) (0x70000a28 + (n) * 0xb6) ++ ++/* Reduce sharpness range for user space API */ ++#define SHARPNESS_DIV 8208 ++#define TOK_TERM 0xffffffff ++ ++/* ++ * FIXME: This is copied from s5k6aa, because of no information ++ * in the S5K4ECGX datasheet. ++ * H/W register Interface (0xd0000000 - 0xd0000fff) ++ */ ++#define AHB_MSB_ADDR_PTR 0xfcfc ++#define GEN_REG_OFFSH 0xd000 ++#define REG_CMDWR_ADDRH 0x0028 ++#define REG_CMDWR_ADDRL 0x002a ++#define REG_CMDRD_ADDRH 0x002c ++#define REG_CMDRD_ADDRL 0x002e ++#define REG_CMDBUF0_ADDR 0x0f12 ++ ++struct s5k4ecgx_frmsize { ++ struct v4l2_frmsize_discrete size; ++ /* Fixed sensor matrix crop rectangle */ ++ struct v4l2_rect input_window; ++}; ++ ++struct regval_list { ++ u32 addr; ++ u16 val; ++}; ++ ++/* ++ * TODO: currently only preview is supported and snapshot (capture) ++ * is not implemented yet ++ */ ++static const struct s5k4ecgx_frmsize s5k4ecgx_prev_sizes[] = { ++ { ++ .size = { 176, 144 }, ++ .input_window = { 0x00, 0x00, 0x928, 0x780 }, ++ }, { ++ .size = { 352, 288 }, ++ .input_window = { 0x00, 0x00, 0x928, 0x780 }, ++ }, { ++ .size = { 640, 480 }, ++ .input_window = { 0x00, 0x00, 0xa00, 0x780 }, ++ }, { ++ .size = { 720, 480 }, ++ .input_window = { 0x00, 0x00, 0xa00, 0x6a8 }, ++ } ++}; ++ ++#define S5K4ECGX_NUM_PREV ARRAY_SIZE(s5k4ecgx_prev_sizes) ++ ++struct s5k4ecgx_pixfmt { ++ enum v4l2_mbus_pixelcode code; ++ u32 colorspace; ++ /* REG_TC_PCFG_Format register value */ ++ u16 reg_p_format; ++}; ++ ++/* By default value, output from sensor will be YUV422 0-255 */ ++static const struct s5k4ecgx_pixfmt s5k4ecgx_formats[] = { ++ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, ++}; ++ ++static const char * const s5k4ecgx_supply_names[] = { ++ /* ++ * Usually 2.8V is used for analog power (vdda) ++ * and digital IO (vddio, vdddcore) ++ */ ++ "vdda", ++ "vddio", ++ "vddcore", ++ "vddreg", /* The internal s5k4ecgx regulator's supply (1.8V) */ ++}; ++ ++#define S5K4ECGX_NUM_SUPPLIES ARRAY_SIZE(s5k4ecgx_supply_names) ++ ++enum s5k4ecgx_gpio_id { ++ STBY, ++ RST, ++ GPIO_NUM, ++}; ++ ++struct s5k4ecgx { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ struct v4l2_ctrl_handler handler; ++ ++ struct s5k4ecgx_platform_data *pdata; ++ const struct s5k4ecgx_pixfmt *curr_pixfmt; ++ const struct s5k4ecgx_frmsize *curr_frmsize; ++ struct mutex lock; ++ u8 streaming; ++ u8 set_params; ++ ++ struct regulator_bulk_data supplies[S5K4ECGX_NUM_SUPPLIES]; ++ struct s5k4ecgx_gpio gpio[GPIO_NUM]; ++}; ++ ++static inline struct s5k4ecgx *to_s5k4ecgx(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct s5k4ecgx, sd); ++} ++ ++static int s5k4ecgx_i2c_read(struct i2c_client *client, u16 addr, u16 *val) ++{ ++ u8 wbuf[2] = { addr >> 8, addr & 0xff }; ++ struct i2c_msg msg[2]; ++ u8 rbuf[2]; ++ int ret; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 2; ++ msg[0].buf = wbuf; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 2; ++ msg[1].buf = rbuf; ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ *val = be16_to_cpu(*((u16 *)rbuf)); ++ ++ v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); ++ ++ return ret == 2 ? 0 : ret; ++} ++ ++static int s5k4ecgx_i2c_write(struct i2c_client *client, u16 addr, u16 val) ++{ ++ u8 buf[4] = { addr >> 8, addr & 0xff, val >> 8, val & 0xff }; ++ ++ int ret = i2c_master_send(client, buf, 4); ++ v4l2_dbg(4, debug, client, "i2c_write: 0x%04x : 0x%04x\n", addr, val); ++ ++ return ret == 4 ? 0 : ret; ++} ++ ++static int s5k4ecgx_write(struct i2c_client *client, u32 addr, u16 val) ++{ ++ u16 high = addr >> 16, low = addr & 0xffff; ++ int ret; ++ ++ v4l2_dbg(3, debug, client, "write: 0x%08x : 0x%04x\n", addr, val); ++ ++ ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRH, high); ++ if (!ret) ++ ret = s5k4ecgx_i2c_write(client, REG_CMDWR_ADDRL, low); ++ if (!ret) ++ ret = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val); ++ ++ return ret; ++} ++ ++static int s5k4ecgx_read(struct i2c_client *client, u32 addr, u16 *val) ++{ ++ u16 high = addr >> 16, low = addr & 0xffff; ++ int ret; ++ ++ ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRH, high); ++ if (!ret) ++ ret = s5k4ecgx_i2c_write(client, REG_CMDRD_ADDRL, low); ++ if (!ret) ++ ret = s5k4ecgx_i2c_read(client, REG_CMDBUF0_ADDR, val); ++ if (!ret) ++ dev_err(&client->dev, "Failed to execute read command\n"); ++ ++ return ret; ++} ++ ++static int s5k4ecgx_read_fw_ver(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 hw_rev, fw_ver = 0; ++ int ret; ++ ++ ret = s5k4ecgx_read(client, REG_FW_VERSION, &fw_ver); ++ if (ret < 0 || fw_ver != S5K4ECGX_FW_VERSION) { ++ v4l2_err(sd, "FW version check failed!\n"); ++ return -ENODEV; ++ } ++ ++ ret = s5k4ecgx_read(client, REG_FW_REVISION, &hw_rev); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_info(sd, "chip found FW ver: 0x%x, HW rev: 0x%x\n", ++ fw_ver, hw_rev); ++ return 0; ++} ++ ++static int s5k4ecgx_set_ahb_address(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ /* Set APB peripherals start address */ ++ ret = s5k4ecgx_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); ++ if (ret < 0) ++ return ret; ++ /* ++ * FIXME: This is copied from s5k6aa, because of no information ++ * in s5k4ecgx's datasheet. ++ * sw_reset is activated to put device into idle status ++ */ ++ ret = s5k4ecgx_i2c_write(client, 0x0010, 0x0001); ++ if (ret < 0) ++ return ret; ++ ++ ret = s5k4ecgx_i2c_write(client, 0x1030, 0x0000); ++ if (ret < 0) ++ return ret; ++ /* Halt ARM CPU */ ++ return s5k4ecgx_i2c_write(client, 0x0014, 0x0001); ++} ++ ++#define FW_CRC_SIZE 4 ++/* Register address, value are 4, 2 bytes */ ++#define FW_RECORD_SIZE 6 ++/* ++ * The firmware has following format: ++ * < total number of records (4 bytes + 2 bytes padding) N >, ++ * < record 0 >, ..., < record N - 1 >, < CRC32-CCITT (4-bytes) >, ++ * where "record" is a 4-byte register address followed by 2-byte ++ * register value (little endian). ++ * The firmware generator can be found in following git repository: ++ * git://git.linaro.org/people/sangwook/fimc-v4l2-app.git ++ */ ++static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ const struct firmware *fw; ++ const u8 *ptr; ++ int err, i, regs_num; ++ u32 addr, crc, crc_file, addr_inc = 0; ++ u16 val; ++ ++ err = request_firmware(&fw, S5K4ECGX_FIRMWARE, sd->v4l2_dev->dev); ++ if (err) { ++ v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE); ++ return err; ++ } ++ regs_num = le32_to_cpu(get_unaligned_le32(fw->data)); ++ ++ v4l2_dbg(3, debug, sd, "FW: %s size %zu register sets %d\n", ++ S5K4ECGX_FIRMWARE, fw->size, regs_num); ++ ++ regs_num++; /* Add header */ ++ if (fw->size != regs_num * FW_RECORD_SIZE + FW_CRC_SIZE) { ++ err = -EINVAL; ++ goto fw_out; ++ } ++ crc_file = le32_to_cpu(get_unaligned_le32(fw->data + ++ regs_num * FW_RECORD_SIZE)); ++ crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE); ++ if (crc != crc_file) { ++ v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file); ++ err = -EINVAL; ++ goto fw_out; ++ } ++ ptr = fw->data + FW_RECORD_SIZE; ++ for (i = 1; i < regs_num; i++) { ++ addr = le32_to_cpu(get_unaligned_le32(ptr)); ++ ptr += sizeof(u32); ++ val = le16_to_cpu(get_unaligned_le16(ptr)); ++ ptr += sizeof(u16); ++ if (addr - addr_inc != 2) ++ err = s5k4ecgx_write(client, addr, val); ++ else ++ err = s5k4ecgx_i2c_write(client, REG_CMDBUF0_ADDR, val); ++ if (err) ++ break; ++ addr_inc = addr; ++ } ++fw_out: ++ release_firmware(fw); ++ return err; ++} ++ ++/* Set preview and capture input window */ ++static int s5k4ecgx_set_input_window(struct i2c_client *c, ++ const struct v4l2_rect *r) ++{ ++ int ret; ++ ++ ret = s5k4ecgx_write(c, REG_G_PREV_IN_WIDTH, r->width); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREV_IN_HEIGHT, r->height); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREV_IN_XOFFS, r->left); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREV_IN_YOFFS, r->top); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAP_IN_WIDTH, r->width); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAP_IN_HEIGHT, r->height); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAP_IN_XOFFS, r->left); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAP_IN_YOFFS, r->top); ++ ++ return ret; ++} ++ ++/* Set preview and capture zoom input window */ ++static int s5k4ecgx_set_zoom_window(struct i2c_client *c, ++ const struct v4l2_rect *r) ++{ ++ int ret; ++ ++ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_WIDTH, r->width); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_HEIGHT, r->height); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_XOFFS, r->left); ++ if (!ret) ++ ret = s5k4ecgx_write(c, REG_G_CAPZOOM_IN_YOFFS, r->top); ++ ++ return ret; ++} ++ ++static int s5k4ecgx_set_output_framefmt(struct s5k4ecgx *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ int ret; ++ ++ ret = s5k4ecgx_write(client, REG_P_OUT_WIDTH(0), ++ priv->curr_frmsize->size.width); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_OUT_HEIGHT(0), ++ priv->curr_frmsize->size.height); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_FMT(0), ++ priv->curr_pixfmt->reg_p_format); ++ return ret; ++} ++ ++static int s5k4ecgx_init_sensor(struct v4l2_subdev *sd) ++{ ++ int ret; ++ ++ ret = s5k4ecgx_set_ahb_address(sd); ++ ++ /* The delay is from manufacturer's settings */ ++ msleep(100); ++ ++ if (!ret) ++ ret = s5k4ecgx_load_firmware(sd); ++ if (ret) ++ v4l2_err(sd, "Failed to write initial settings\n"); ++ ++ return ret; ++} ++ ++static int s5k4ecgx_gpio_set_value(struct s5k4ecgx *priv, int id, u32 val) ++{ ++ if (!gpio_is_valid(priv->gpio[id].gpio)) ++ return 0; ++ gpio_set_value(priv->gpio[id].gpio, val); ++ ++ return 1; ++} ++ ++static int __s5k4ecgx_power_on(struct s5k4ecgx *priv) ++{ ++ int ret; ++ ++ ret = regulator_bulk_enable(S5K4ECGX_NUM_SUPPLIES, priv->supplies); ++ if (ret) ++ return ret; ++ usleep_range(30, 50); ++ ++ /* The polarity of STBY is controlled by TSP */ ++ if (s5k4ecgx_gpio_set_value(priv, STBY, priv->gpio[STBY].level)) ++ usleep_range(30, 50); ++ ++ if (s5k4ecgx_gpio_set_value(priv, RST, priv->gpio[RST].level)) ++ usleep_range(30, 50); ++ ++ return 0; ++} ++ ++static int __s5k4ecgx_power_off(struct s5k4ecgx *priv) ++{ ++ if (s5k4ecgx_gpio_set_value(priv, RST, !priv->gpio[RST].level)) ++ usleep_range(30, 50); ++ ++ if (s5k4ecgx_gpio_set_value(priv, STBY, !priv->gpio[STBY].level)) ++ usleep_range(30, 50); ++ ++ priv->streaming = 0; ++ ++ return regulator_bulk_disable(S5K4ECGX_NUM_SUPPLIES, priv->supplies); ++} ++ ++/* Find nearest matching image pixel size. */ ++static int s5k4ecgx_try_frame_size(struct v4l2_mbus_framefmt *mf, ++ const struct s5k4ecgx_frmsize **size) ++{ ++ unsigned int min_err = ~0; ++ int i = ARRAY_SIZE(s5k4ecgx_prev_sizes); ++ const struct s5k4ecgx_frmsize *fsize = &s5k4ecgx_prev_sizes[0], ++ *match = NULL; ++ ++ while (i--) { ++ int err = abs(fsize->size.width - mf->width) ++ + abs(fsize->size.height - mf->height); ++ if (err < min_err) { ++ min_err = err; ++ match = fsize; ++ } ++ fsize++; ++ } ++ if (match) { ++ mf->width = match->size.width; ++ mf->height = match->size.height; ++ if (size) ++ *size = match; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int s5k4ecgx_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index >= ARRAY_SIZE(s5k4ecgx_formats)) ++ return -EINVAL; ++ code->code = s5k4ecgx_formats[code->index].code; ++ ++ return 0; ++} ++ ++static int s5k4ecgx_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ struct v4l2_mbus_framefmt *mf; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ if (fh) { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ fmt->format = *mf; ++ } ++ return 0; ++ } ++ ++ mf = &fmt->format; ++ ++ mutex_lock(&priv->lock); ++ mf->width = priv->curr_frmsize->size.width; ++ mf->height = priv->curr_frmsize->size.height; ++ mf->code = priv->curr_pixfmt->code; ++ mf->colorspace = priv->curr_pixfmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ mutex_unlock(&priv->lock); ++ ++ return 0; ++} ++ ++static const struct s5k4ecgx_pixfmt *s5k4ecgx_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ int i = ARRAY_SIZE(s5k4ecgx_formats); ++ ++ while (--i) ++ if (mf->code == s5k4ecgx_formats[i].code) ++ break; ++ mf->code = s5k4ecgx_formats[i].code; ++ ++ return &s5k4ecgx_formats[i]; ++} ++ ++static int s5k4ecgx_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ const struct s5k4ecgx_frmsize *fsize = NULL; ++ const struct s5k4ecgx_pixfmt *pf; ++ struct v4l2_mbus_framefmt *mf; ++ int ret = 0; ++ ++ pf = s5k4ecgx_try_fmt(sd, &fmt->format); ++ s5k4ecgx_try_frame_size(&fmt->format, &fsize); ++ fmt->format.colorspace = V4L2_COLORSPACE_JPEG; ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ if (fh) { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ *mf = fmt->format; ++ } ++ return 0; ++ } ++ ++ mutex_lock(&priv->lock); ++ if (!priv->streaming) { ++ priv->curr_frmsize = fsize; ++ priv->curr_pixfmt = pf; ++ priv->set_params = 1; ++ } else { ++ ret = -EBUSY; ++ } ++ mutex_unlock(&priv->lock); ++ ++ return ret; ++} ++ ++static const struct v4l2_subdev_pad_ops s5k4ecgx_pad_ops = { ++ .enum_mbus_code = s5k4ecgx_enum_mbus_code, ++ .get_fmt = s5k4ecgx_get_fmt, ++ .set_fmt = s5k4ecgx_set_fmt, ++}; ++ ++/* ++ * V4L2 subdev controls ++ */ ++static int s5k4ecgx_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = &container_of(ctrl->handler, struct s5k4ecgx, ++ handler)->sd; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ unsigned int i; ++ int err = 0; ++ ++ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); ++ ++ mutex_lock(&priv->lock); ++ switch (ctrl->id) { ++ case V4L2_CID_CONTRAST: ++ err = s5k4ecgx_write(client, REG_USER_CONTRAST, ctrl->val); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ err = s5k4ecgx_write(client, REG_USER_SATURATION, ctrl->val); ++ break; ++ ++ case V4L2_CID_SHARPNESS: ++ /* TODO: Revisit, is this setting for all presets ? */ ++ for (i = 0; i < 4 && !err; i++) ++ err = s5k4ecgx_write(client, REG_USER_SHARPNESS(i), ++ ctrl->val * SHARPNESS_DIV); ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ err = s5k4ecgx_write(client, REG_USER_BRIGHTNESS, ctrl->val); ++ break; ++ } ++ mutex_unlock(&priv->lock); ++ if (err < 0) ++ v4l2_err(sd, "Failed to write s_ctrl err %d\n", err); ++ ++ return err; ++} ++ ++static const struct v4l2_ctrl_ops s5k4ecgx_ctrl_ops = { ++ .s_ctrl = s5k4ecgx_s_ctrl, ++}; ++ ++/* ++ * Reading s5k4ecgx version information ++ */ ++static int s5k4ecgx_registered(struct v4l2_subdev *sd) ++{ ++ int ret; ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ ++ mutex_lock(&priv->lock); ++ ret = __s5k4ecgx_power_on(priv); ++ if (!ret) { ++ ret = s5k4ecgx_read_fw_ver(sd); ++ __s5k4ecgx_power_off(priv); ++ } ++ mutex_unlock(&priv->lock); ++ ++ return ret; ++} ++ ++/* ++ * V4L2 subdev internal operations ++ */ ++static int s5k4ecgx_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(fh, 0); ++ ++ mf->width = s5k4ecgx_prev_sizes[0].size.width; ++ mf->height = s5k4ecgx_prev_sizes[0].size.height; ++ mf->code = s5k4ecgx_formats[0].code; ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_internal_ops s5k4ecgx_subdev_internal_ops = { ++ .registered = s5k4ecgx_registered, ++ .open = s5k4ecgx_open, ++}; ++ ++static int s5k4ecgx_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ int ret; ++ ++ v4l2_dbg(1, debug, sd, "Switching %s\n", on ? "on" : "off"); ++ ++ if (on) { ++ ret = __s5k4ecgx_power_on(priv); ++ if (ret < 0) ++ return ret; ++ /* Time to stabilize sensor */ ++ msleep(100); ++ ret = s5k4ecgx_init_sensor(sd); ++ if (ret < 0) ++ __s5k4ecgx_power_off(priv); ++ else ++ priv->set_params = 1; ++ } else { ++ ret = __s5k4ecgx_power_off(priv); ++ } ++ ++ return ret; ++} ++ ++static int s5k4ecgx_log_status(struct v4l2_subdev *sd) ++{ ++ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops s5k4ecgx_core_ops = { ++ .s_power = s5k4ecgx_s_power, ++ .log_status = s5k4ecgx_log_status, ++}; ++ ++static int __s5k4ecgx_s_params(struct s5k4ecgx *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ const struct v4l2_rect *crop_rect = &priv->curr_frmsize->input_window; ++ int ret; ++ ++ ret = s5k4ecgx_set_input_window(client, crop_rect); ++ if (!ret) ++ ret = s5k4ecgx_set_zoom_window(client, crop_rect); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_G_INPUTS_CHANGE_REQ, 1); ++ if (!ret) ++ ret = s5k4ecgx_write(client, 0x70000a1e, 0x28); ++ if (!ret) ++ ret = s5k4ecgx_write(client, 0x70000ad4, 0x3c); ++ if (!ret) ++ ret = s5k4ecgx_set_output_framefmt(priv); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_PVI_MASK(0), 0x52); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_FR_TIME_TYPE(0), ++ FR_TIME_DYNAMIC); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_FR_TIME_Q_TYPE(0), ++ FR_TIME_Q_BEST_FRRATE); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_MIN_FR_TIME(0), ++ US_TO_FR_TIME(33300)); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_MAX_FR_TIME(0), ++ US_TO_FR_TIME(66600)); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_PREV_MIRROR(0), 0); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_P_CAP_MIRROR(0), 0); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_G_ACTIVE_PREV_CFG, 0); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_G_PREV_OPEN_AFTER_CH, 1); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_G_NEW_CFG_SYNC, 1); ++ if (!ret) ++ ret = s5k4ecgx_write(client, REG_G_PREV_CFG_CHG, 1); ++ ++ return ret; ++} ++ ++static int __s5k4ecgx_s_stream(struct s5k4ecgx *priv, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->sd); ++ int ret; ++ ++ if (on && priv->set_params) { ++ ret = __s5k4ecgx_s_params(priv); ++ if (ret < 0) ++ return ret; ++ priv->set_params = 0; ++ } ++ /* ++ * This enables/disables preview stream only. Capture requests ++ * are not supported yet. ++ */ ++ ret = s5k4ecgx_write(client, REG_G_ENABLE_PREV, on); ++ if (ret < 0) ++ return ret; ++ return s5k4ecgx_write(client, REG_G_ENABLE_PREV_CHG, 1); ++} ++ ++static int s5k4ecgx_s_stream(struct v4l2_subdev *sd, int on) ++{ ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, sd, "Turn streaming %s\n", on ? "on" : "off"); ++ ++ mutex_lock(&priv->lock); ++ ++ if (priv->streaming == !on) { ++ ret = __s5k4ecgx_s_stream(priv, on); ++ if (!ret) ++ priv->streaming = on & 1; ++ } ++ ++ mutex_unlock(&priv->lock); ++ return ret; ++} ++ ++static const struct v4l2_subdev_video_ops s5k4ecgx_video_ops = { ++ .s_stream = s5k4ecgx_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops s5k4ecgx_ops = { ++ .core = &s5k4ecgx_core_ops, ++ .pad = &s5k4ecgx_pad_ops, ++ .video = &s5k4ecgx_video_ops, ++}; ++ ++/* ++ * GPIO setup ++ */ ++static int s5k4ecgx_config_gpio(int nr, int val, const char *name) ++{ ++ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ int ret; ++ ++ if (!gpio_is_valid(nr)) ++ return 0; ++ ret = gpio_request_one(nr, flags, name); ++ if (!ret) ++ gpio_export(nr, 0); ++ ++ return ret; ++} ++ ++static void s5k4ecgx_free_gpios(struct s5k4ecgx *priv) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(priv->gpio); i++) { ++ if (!gpio_is_valid(priv->gpio[i].gpio)) ++ continue; ++ gpio_free(priv->gpio[i].gpio); ++ priv->gpio[i].gpio = -EINVAL; ++ } ++} ++ ++static int s5k4ecgx_config_gpios(struct s5k4ecgx *priv, ++ const struct s5k4ecgx_platform_data *pdata) ++{ ++ const struct s5k4ecgx_gpio *gpio = &pdata->gpio_stby; ++ int ret; ++ ++ priv->gpio[STBY].gpio = -EINVAL; ++ priv->gpio[RST].gpio = -EINVAL; ++ ++ ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_STBY"); ++ ++ if (ret) { ++ s5k4ecgx_free_gpios(priv); ++ return ret; ++ } ++ priv->gpio[STBY] = *gpio; ++ if (gpio_is_valid(gpio->gpio)) ++ gpio_set_value(gpio->gpio, 0); ++ ++ gpio = &pdata->gpio_reset; ++ ++ ret = s5k4ecgx_config_gpio(gpio->gpio, gpio->level, "S5K4ECGX_RST"); ++ if (ret) { ++ s5k4ecgx_free_gpios(priv); ++ return ret; ++ } ++ priv->gpio[RST] = *gpio; ++ if (gpio_is_valid(gpio->gpio)) ++ gpio_set_value(gpio->gpio, 0); ++ ++ return 0; ++} ++ ++static int s5k4ecgx_init_v4l2_ctrls(struct s5k4ecgx *priv) ++{ ++ const struct v4l2_ctrl_ops *ops = &s5k4ecgx_ctrl_ops; ++ struct v4l2_ctrl_handler *hdl = &priv->handler; ++ int ret; ++ ++ ret = v4l2_ctrl_handler_init(hdl, 4); ++ if (ret) ++ return ret; ++ ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -208, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); ++ ++ /* Sharpness default is 24612, and then (24612/SHARPNESS_DIV) = 2 */ ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -32704/SHARPNESS_DIV, ++ 24612/SHARPNESS_DIV, 1, 2); ++ if (hdl->error) { ++ ret = hdl->error; ++ v4l2_ctrl_handler_free(hdl); ++ return ret; ++ } ++ priv->sd.ctrl_handler = hdl; ++ ++ return 0; ++}; ++ ++static int s5k4ecgx_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct s5k4ecgx_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_subdev *sd; ++ struct s5k4ecgx *priv; ++ int ret, i; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "platform data is missing!\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct s5k4ecgx), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ mutex_init(&priv->lock); ++ priv->streaming = 0; ++ ++ sd = &priv->sd; ++ /* Registering subdev */ ++ v4l2_i2c_subdev_init(sd, client, &s5k4ecgx_ops); ++ strlcpy(sd->name, S5K4ECGX_DRIVER_NAME, sizeof(sd->name)); ++ ++ sd->internal_ops = &s5k4ecgx_subdev_internal_ops; ++ /* Support v4l2 sub-device user space API */ ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ priv->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ ret = media_entity_init(&sd->entity, 1, &priv->pad, 0); ++ if (ret) ++ return ret; ++ ++ ret = s5k4ecgx_config_gpios(priv, pdata); ++ if (ret) { ++ dev_err(&client->dev, "Failed to set gpios\n"); ++ goto out_err1; ++ } ++ for (i = 0; i < S5K4ECGX_NUM_SUPPLIES; i++) ++ priv->supplies[i].supply = s5k4ecgx_supply_names[i]; ++ ++ ret = devm_regulator_bulk_get(&client->dev, S5K4ECGX_NUM_SUPPLIES, ++ priv->supplies); ++ if (ret) { ++ dev_err(&client->dev, "Failed to get regulators\n"); ++ goto out_err2; ++ } ++ ret = s5k4ecgx_init_v4l2_ctrls(priv); ++ if (ret) ++ goto out_err2; ++ ++ priv->curr_pixfmt = &s5k4ecgx_formats[0]; ++ priv->curr_frmsize = &s5k4ecgx_prev_sizes[0]; ++ ++ return 0; ++ ++out_err2: ++ s5k4ecgx_free_gpios(priv); ++out_err1: ++ media_entity_cleanup(&priv->sd.entity); ++ ++ return ret; ++} ++ ++static int s5k4ecgx_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct s5k4ecgx *priv = to_s5k4ecgx(sd); ++ ++ mutex_destroy(&priv->lock); ++ s5k4ecgx_free_gpios(priv); ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&priv->handler); ++ media_entity_cleanup(&sd->entity); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id s5k4ecgx_id[] = { ++ { S5K4ECGX_DRIVER_NAME, 0 }, ++ {} ++}; ++MODULE_DEVICE_TABLE(i2c, s5k4ecgx_id); ++ ++static struct i2c_driver v4l2_i2c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = S5K4ECGX_DRIVER_NAME, ++ }, ++ .probe = s5k4ecgx_probe, ++ .remove = s5k4ecgx_remove, ++ .id_table = s5k4ecgx_id, ++}; ++ ++module_i2c_driver(v4l2_i2c_driver); ++ ++MODULE_DESCRIPTION("Samsung S5K4ECGX 5MP SOC camera"); ++MODULE_AUTHOR("Sangwook Lee "); ++MODULE_AUTHOR("Seok-Young Jang "); ++MODULE_LICENSE("GPL"); ++MODULE_FIRMWARE(S5K4ECGX_FIRMWARE); +diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c +new file mode 100644 +index 0000000..57cd4fa +--- /dev/null ++++ b/drivers/media/i2c/s5k6aa.c +@@ -0,0 +1,1664 @@ ++/* ++ * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor ++ * with embedded SoC ISP. ++ * ++ * Copyright (C) 2011, Samsung Electronics Co., Ltd. ++ * Sylwester Nawrocki ++ * ++ * Based on a driver authored by Dongsoo Nathaniel Kim. ++ * Copyright (C) 2009, Dongsoo Nathaniel Kim ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++#define DRIVER_NAME "S5K6AA" ++ ++/* The token to indicate array termination */ ++#define S5K6AA_TERM 0xffff ++#define S5K6AA_OUT_WIDTH_DEF 640 ++#define S5K6AA_OUT_HEIGHT_DEF 480 ++#define S5K6AA_WIN_WIDTH_MAX 1280 ++#define S5K6AA_WIN_HEIGHT_MAX 1024 ++#define S5K6AA_WIN_WIDTH_MIN 8 ++#define S5K6AA_WIN_HEIGHT_MIN 8 ++ ++/* ++ * H/W register Interface (0xD0000000 - 0xD0000FFF) ++ */ ++#define AHB_MSB_ADDR_PTR 0xfcfc ++#define GEN_REG_OFFSH 0xd000 ++#define REG_CMDWR_ADDRH 0x0028 ++#define REG_CMDWR_ADDRL 0x002a ++#define REG_CMDRD_ADDRH 0x002c ++#define REG_CMDRD_ADDRL 0x002e ++#define REG_CMDBUF0_ADDR 0x0f12 ++#define REG_CMDBUF1_ADDR 0x0f10 ++ ++/* ++ * Host S/W Register interface (0x70000000 - 0x70002000) ++ * The value of the two most significant address bytes is 0x7000, ++ * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. ++ */ ++#define HOST_SWIF_OFFSH 0x7000 ++ ++/* Initialization parameters */ ++/* Master clock frequency in KHz */ ++#define REG_I_INCLK_FREQ_L 0x01b8 ++#define REG_I_INCLK_FREQ_H 0x01ba ++#define MIN_MCLK_FREQ_KHZ 6000U ++#define MAX_MCLK_FREQ_KHZ 27000U ++#define REG_I_USE_NPVI_CLOCKS 0x01c6 ++#define REG_I_USE_NMIPI_CLOCKS 0x01c8 ++ ++/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ ++#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) ++#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) ++#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) ++#define SYS_PLL_OUT_FREQ (48000000 / 4000) ++#define PCLK_FREQ_MIN (24000000 / 4000) ++#define PCLK_FREQ_MAX (48000000 / 4000) ++#define REG_I_INIT_PARAMS_UPDATED 0x01e0 ++#define REG_I_ERROR_INFO 0x01e2 ++ ++/* General purpose parameters */ ++#define REG_USER_BRIGHTNESS 0x01e4 ++#define REG_USER_CONTRAST 0x01e6 ++#define REG_USER_SATURATION 0x01e8 ++#define REG_USER_SHARPBLUR 0x01ea ++ ++#define REG_G_SPEC_EFFECTS 0x01ee ++#define REG_G_ENABLE_PREV 0x01f0 ++#define REG_G_ENABLE_PREV_CHG 0x01f2 ++#define REG_G_NEW_CFG_SYNC 0x01f8 ++#define REG_G_PREVZOOM_IN_WIDTH 0x020a ++#define REG_G_PREVZOOM_IN_HEIGHT 0x020c ++#define REG_G_PREVZOOM_IN_XOFFS 0x020e ++#define REG_G_PREVZOOM_IN_YOFFS 0x0210 ++#define REG_G_INPUTS_CHANGE_REQ 0x021a ++#define REG_G_ACTIVE_PREV_CFG 0x021c ++#define REG_G_PREV_CFG_CHG 0x021e ++#define REG_G_PREV_OPEN_AFTER_CH 0x0220 ++#define REG_G_PREV_CFG_ERROR 0x0222 ++ ++/* Preview control section. n = 0...4. */ ++#define PREG(n, x) ((n) * 0x26 + x) ++#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) ++#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) ++#define REG_P_FMT(n) PREG(n, 0x0246) ++#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) ++#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) ++#define REG_P_PVI_MASK(n) PREG(n, 0x024c) ++#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) ++#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) ++#define FR_RATE_DYNAMIC 0 ++#define FR_RATE_FIXED 1 ++#define FR_RATE_FIXED_ACCURATE 2 ++#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) ++#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ ++#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ ++/* Frame period in 0.1 ms units */ ++#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) ++#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) ++/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ ++#define US_TO_FR_TIME(__t) ((__t) / 100) ++#define S5K6AA_MIN_FR_TIME 33300 /* us */ ++#define S5K6AA_MAX_FR_TIME 650000 /* us */ ++#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ ++/* The below 5 registers are for "device correction" values */ ++#define REG_P_COLORTEMP(n) PREG(n, 0x025e) ++#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) ++ ++/* Extended image property controls */ ++/* Exposure time in 10 us units */ ++#define REG_SF_USR_EXPOSURE_L 0x03c6 ++#define REG_SF_USR_EXPOSURE_H 0x03c8 ++#define REG_SF_USR_EXPOSURE_CHG 0x03ca ++#define REG_SF_USR_TOT_GAIN 0x03cc ++#define REG_SF_USR_TOT_GAIN_CHG 0x03ce ++#define REG_SF_RGAIN 0x03d0 ++#define REG_SF_RGAIN_CHG 0x03d2 ++#define REG_SF_GGAIN 0x03d4 ++#define REG_SF_GGAIN_CHG 0x03d6 ++#define REG_SF_BGAIN 0x03d8 ++#define REG_SF_BGAIN_CHG 0x03da ++#define REG_SF_FLICKER_QUANT 0x03dc ++#define REG_SF_FLICKER_QUANT_CHG 0x03de ++ ++/* Output interface (parallel/MIPI) setup */ ++#define REG_OIF_EN_MIPI_LANES 0x03fa ++#define REG_OIF_EN_PACKETS 0x03fc ++#define REG_OIF_CFG_CHG 0x03fe ++ ++/* Auto-algorithms enable mask */ ++#define REG_DBG_AUTOALG_EN 0x0400 ++#define AALG_ALL_EN_MASK (1 << 0) ++#define AALG_AE_EN_MASK (1 << 1) ++#define AALG_DIVLEI_EN_MASK (1 << 2) ++#define AALG_WB_EN_MASK (1 << 3) ++#define AALG_FLICKER_EN_MASK (1 << 5) ++#define AALG_FIT_EN_MASK (1 << 6) ++#define AALG_WRHW_EN_MASK (1 << 7) ++ ++/* Firmware revision information */ ++#define REG_FW_APIVER 0x012e ++#define S5K6AAFX_FW_APIVER 0x0001 ++#define REG_FW_REVISION 0x0130 ++ ++/* For now we use only one user configuration register set */ ++#define S5K6AA_MAX_PRESETS 1 ++ ++static const char * const s5k6aa_supply_names[] = { ++ "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ ++ "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ ++ "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) ++ or 2.8V (2.6V to 3.0) */ ++ "vddio", /* I/O supply 1.8V (1.65V to 1.95V) ++ or 2.8V (2.5V to 3.1V) */ ++}; ++#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) ++ ++enum s5k6aa_gpio_id { ++ STBY, ++ RST, ++ GPIO_NUM, ++}; ++ ++struct s5k6aa_regval { ++ u16 addr; ++ u16 val; ++}; ++ ++struct s5k6aa_pixfmt { ++ enum v4l2_mbus_pixelcode code; ++ u32 colorspace; ++ /* REG_P_FMT(x) register value */ ++ u16 reg_p_fmt; ++}; ++ ++struct s5k6aa_preset { ++ /* output pixel format and resolution */ ++ struct v4l2_mbus_framefmt mbus_fmt; ++ u8 clk_id; ++ u8 index; ++}; ++ ++struct s5k6aa_ctrls { ++ struct v4l2_ctrl_handler handler; ++ /* Auto / manual white balance cluster */ ++ struct v4l2_ctrl *awb; ++ struct v4l2_ctrl *gain_red; ++ struct v4l2_ctrl *gain_blue; ++ struct v4l2_ctrl *gain_green; ++ /* Mirror cluster */ ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ /* Auto exposure / manual exposure and gain cluster */ ++ struct v4l2_ctrl *auto_exp; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *gain; ++}; ++ ++struct s5k6aa_interval { ++ u16 reg_fr_time; ++ struct v4l2_fract interval; ++ /* Maximum rectangle for the interval */ ++ struct v4l2_frmsize_discrete size; ++}; ++ ++struct s5k6aa { ++ struct v4l2_subdev sd; ++ struct media_pad pad; ++ ++ enum v4l2_mbus_type bus_type; ++ u8 mipi_lanes; ++ ++ int (*s_power)(int enable); ++ struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; ++ struct s5k6aa_gpio gpio[GPIO_NUM]; ++ ++ /* external master clock frequency */ ++ unsigned long mclk_frequency; ++ /* ISP internal master clock frequency */ ++ u16 clk_fop; ++ /* output pixel clock frequency range */ ++ u16 pclk_fmin; ++ u16 pclk_fmax; ++ ++ unsigned int inv_hflip:1; ++ unsigned int inv_vflip:1; ++ ++ /* protects the struct members below */ ++ struct mutex lock; ++ ++ /* sensor matrix scan window */ ++ struct v4l2_rect ccd_rect; ++ ++ struct s5k6aa_ctrls ctrls; ++ struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; ++ struct s5k6aa_preset *preset; ++ const struct s5k6aa_interval *fiv; ++ ++ unsigned int streaming:1; ++ unsigned int apply_cfg:1; ++ unsigned int apply_crop:1; ++ unsigned int power; ++}; ++ ++static struct s5k6aa_regval s5k6aa_analog_config[] = { ++ /* Analog settings */ ++ { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, ++ { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, ++ { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, ++ { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, ++ { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, ++ { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, ++ { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, ++ { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, ++ { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, ++ { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, ++ { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, ++ { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, ++ { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, ++}; ++ ++/* TODO: Add RGB888 and Bayer format */ ++static const struct s5k6aa_pixfmt s5k6aa_formats[] = { ++ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, ++ /* range 16-240 */ ++ { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, ++ { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, ++}; ++ ++static const struct s5k6aa_interval s5k6aa_intervals[] = { ++ { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ ++ { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ ++ { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ ++ { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ ++ { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ ++}; ++ ++#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ ++ ++static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; ++} ++ ++static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct s5k6aa, sd); ++} ++ ++/* Set initial values for all preview presets */ ++static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) ++{ ++ struct s5k6aa_preset *preset = &s5k6aa->presets[0]; ++ int i; ++ ++ for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { ++ preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; ++ preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; ++ preset->mbus_fmt.code = s5k6aa_formats[0].code; ++ preset->index = i; ++ preset->clk_id = 0; ++ preset++; ++ } ++ ++ s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; ++ s5k6aa->preset = &s5k6aa->presets[0]; ++} ++ ++static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) ++{ ++ u8 wbuf[2] = {addr >> 8, addr & 0xFF}; ++ struct i2c_msg msg[2]; ++ u8 rbuf[2]; ++ int ret; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 2; ++ msg[0].buf = wbuf; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 2; ++ msg[1].buf = rbuf; ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ *val = be16_to_cpu(*((u16 *)rbuf)); ++ ++ v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); ++ ++ return ret == 2 ? 0 : ret; ++} ++ ++static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) ++{ ++ u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; ++ ++ int ret = i2c_master_send(client, buf, 4); ++ v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); ++ ++ return ret == 4 ? 0 : ret; ++} ++ ++/* The command register write, assumes Command_Wr_addH = 0x7000. */ ++static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) ++{ ++ int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); ++ if (ret) ++ return ret; ++ return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); ++} ++ ++/* The command register read, assumes Command_Rd_addH = 0x7000. */ ++static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) ++{ ++ int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); ++ if (ret) ++ return ret; ++ return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); ++} ++ ++static int s5k6aa_write_array(struct v4l2_subdev *sd, ++ const struct s5k6aa_regval *msg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 addr_incr = 0; ++ int ret = 0; ++ ++ while (msg->addr != S5K6AA_TERM) { ++ if (addr_incr != 2) ++ ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, ++ msg->addr); ++ if (ret) ++ break; ++ ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); ++ if (ret) ++ break; ++ /* Assume that msg->addr is always less than 0xfffc */ ++ addr_incr = (msg + 1)->addr - msg->addr; ++ msg++; ++ } ++ ++ return ret; ++} ++ ++/* Configure the AHB high address bytes for GTG registers access */ ++static int s5k6aa_set_ahb_address(struct i2c_client *client) ++{ ++ int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); ++ if (ret) ++ return ret; ++ ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); ++ if (ret) ++ return ret; ++ return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); ++} ++ ++/** ++ * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration ++ * ++ * Configure the internal ISP PLL for the required output frequency. ++ * Locking: called with s5k6aa.lock mutex held. ++ */ ++static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); ++ unsigned long fmclk = s5k6aa->mclk_frequency / 1000; ++ u16 status; ++ int ret; ++ ++ if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, ++ "Invalid clock frequency: %ld\n", fmclk)) ++ return -EINVAL; ++ ++ s5k6aa->pclk_fmin = PCLK_FREQ_MIN; ++ s5k6aa->pclk_fmax = PCLK_FREQ_MAX; ++ s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; ++ ++ /* External input clock frequency in kHz */ ++ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); ++ /* Internal PLL frequency */ ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), ++ s5k6aa->pclk_fmin); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), ++ s5k6aa->pclk_fmax); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); ++ if (!ret) ++ ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); ++ ++ return ret ? ret : (status ? -EINVAL : 0); ++} ++ ++/* Set horizontal and vertical image flipping */ ++static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ int index = s5k6aa->preset->index; ++ ++ unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; ++ unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); ++ ++ return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); ++} ++ ++/* Configure auto/manual white balance and R/G/B gains */ ++static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); ++ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; ++ u16 reg; ++ ++ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); ++ ++ if (!ret && !awb) { ++ ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); ++ if (ret) ++ return ret; ++ ++ ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); ++ if (ret) ++ return ret; ++ ++ ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); ++ } ++ if (!ret) { ++ reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; ++ ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); ++ } ++ ++ return ret; ++} ++ ++/* Program FW with exposure time, 'exposure' in us units */ ++static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) ++{ ++ unsigned int time = exposure / 10; ++ ++ int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); ++ if (ret) ++ return ret; ++ return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); ++} ++ ++static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) ++{ ++ int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); ++ if (ret) ++ return ret; ++ return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); ++} ++ ++/* Set auto/manual exposure and total gain */ ++static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); ++ unsigned int exp_time = s5k6aa->ctrls.exposure->val; ++ u16 auto_alg; ++ ++ int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); ++ if (ret) ++ return ret; ++ ++ v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", ++ exp_time, value, auto_alg); ++ ++ if (value == V4L2_EXPOSURE_AUTO) { ++ auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; ++ } else { ++ ret = s5k6aa_set_user_exposure(c, exp_time); ++ if (ret) ++ return ret; ++ ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); ++ if (ret) ++ return ret; ++ auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); ++ } ++ ++ return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); ++} ++ ++static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ u16 auto_alg; ++ int ret; ++ ++ ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); ++ if (ret) ++ return ret; ++ ++ if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { ++ auto_alg |= AALG_FLICKER_EN_MASK; ++ } else { ++ auto_alg &= ~AALG_FLICKER_EN_MASK; ++ /* The V4L2_CID_LINE_FREQUENCY control values match ++ * the register values */ ++ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); ++ if (ret) ++ return ret; ++ ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); ++ if (ret) ++ return ret; ++ } ++ ++ return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); ++} ++ ++static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ static const struct v4l2_control colorfx[] = { ++ { V4L2_COLORFX_NONE, 0 }, ++ { V4L2_COLORFX_BW, 1 }, ++ { V4L2_COLORFX_NEGATIVE, 2 }, ++ { V4L2_COLORFX_SEPIA, 3 }, ++ { V4L2_COLORFX_SKY_BLUE, 4 }, ++ { V4L2_COLORFX_SKETCH, 5 }, ++ }; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(colorfx); i++) { ++ if (colorfx[i].id == val) ++ return s5k6aa_write(client, REG_G_SPEC_EFFECTS, ++ colorfx[i].value); ++ } ++ return -EINVAL; ++} ++ ++static int s5k6aa_preview_config_status(struct i2c_client *client) ++{ ++ u16 error = 0; ++ int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); ++ ++ v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); ++ return ret ? ret : (error ? -EINVAL : 0); ++} ++ ++static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) ++ if (mf->colorspace == s5k6aa_formats[i].colorspace && ++ mf->code == s5k6aa_formats[i].code) ++ return i; ++ return 0; ++} ++ ++static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, ++ struct s5k6aa_preset *preset) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); ++ int ret; ++ ++ ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), ++ preset->mbus_fmt.width); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), ++ preset->mbus_fmt.height); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_FMT(preset->index), ++ s5k6aa_formats[fmt_index].reg_p_fmt); ++ return ret; ++} ++ ++static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); ++ struct v4l2_rect *r = &s5k6aa->ccd_rect; ++ int ret; ++ ++ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); ++ if (!ret) ++ ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); ++ if (!ret) ++ s5k6aa->apply_crop = 0; ++ ++ return ret; ++} ++ ++/** ++ * s5k6aa_configure_video_bus - configure the video output interface ++ * @bus_type: video bus type: parallel or MIPI-CSI ++ * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) ++ * ++ * Note: Only parallel bus operation has been tested. ++ */ ++static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, ++ enum v4l2_mbus_type bus_type, int nlanes) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ u16 cfg = 0; ++ int ret; ++ ++ /* ++ * TODO: The sensor is supposed to support BT.601 and BT.656 ++ * but there is nothing indicating how to switch between both ++ * in the datasheet. For now default BT.601 interface is assumed. ++ */ ++ if (bus_type == V4L2_MBUS_CSI2) ++ cfg = nlanes; ++ else if (bus_type != V4L2_MBUS_PARALLEL) ++ return -EINVAL; ++ ++ ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); ++ if (ret) ++ return ret; ++ return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); ++} ++ ++/* This function should be called when switching to new user configuration set*/ ++static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, ++ int cid) ++{ ++ unsigned long end = jiffies + msecs_to_jiffies(timeout); ++ u16 reg = 1; ++ int ret; ++ ++ ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); ++ if (timeout == 0) ++ return ret; ++ ++ while (ret >= 0 && time_is_after_jiffies(end)) { ++ ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); ++ if (!reg) ++ return 0; ++ usleep_range(1000, 5000); ++ } ++ return ret ? ret : -ETIMEDOUT; ++} ++ ++/** ++ * s5k6aa_set_prev_config - write user preview register set ++ * ++ * Configure output resolution and color fromat, pixel clock ++ * frequency range, device frame rate type and frame period range. ++ */ ++static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, ++ struct s5k6aa_preset *preset) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ int idx = preset->index; ++ u16 frame_rate_q; ++ int ret; ++ ++ if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) ++ frame_rate_q = FR_RATE_Q_BEST_FRRATE; ++ else ++ frame_rate_q = FR_RATE_Q_BEST_QUALITY; ++ ++ ret = s5k6aa_set_output_framefmt(s5k6aa, preset); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), ++ s5k6aa->pclk_fmax); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), ++ s5k6aa->pclk_fmin); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), ++ preset->clk_id); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), ++ FR_RATE_DYNAMIC); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), ++ frame_rate_q); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), ++ s5k6aa->fiv->reg_fr_time + 33); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), ++ s5k6aa->fiv->reg_fr_time - 33); ++ if (!ret) ++ ret = s5k6aa_new_config_sync(client, 250, idx); ++ if (!ret) ++ ret = s5k6aa_preview_config_status(client); ++ if (!ret) ++ s5k6aa->apply_cfg = 0; ++ ++ v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", ++ s5k6aa->fiv->reg_fr_time, ret); ++ return ret; ++} ++ ++/** ++ * s5k6aa_initialize_isp - basic ISP MCU initialization ++ * ++ * Configure AHB addresses for registers read/write; configure PLLs for ++ * required output pixel clock. The ISP power supply needs to be already ++ * enabled, with an optional H/W reset. ++ * Locking: called with s5k6aa.lock mutex held. ++ */ ++static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int ret; ++ ++ s5k6aa->apply_crop = 1; ++ s5k6aa->apply_cfg = 1; ++ msleep(100); ++ ++ ret = s5k6aa_set_ahb_address(client); ++ if (ret) ++ return ret; ++ ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, ++ s5k6aa->mipi_lanes); ++ if (ret) ++ return ret; ++ ret = s5k6aa_write_array(sd, s5k6aa_analog_config); ++ if (ret) ++ return ret; ++ msleep(20); ++ ++ return s5k6aa_configure_pixel_clocks(s5k6aa); ++} ++ ++static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) ++{ ++ if (!gpio_is_valid(priv->gpio[id].gpio)) ++ return 0; ++ gpio_set_value(priv->gpio[id].gpio, !!val); ++ return 1; ++} ++ ++static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) ++{ ++ return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); ++} ++ ++static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) ++{ ++ return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); ++} ++ ++static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) ++{ ++ int ret; ++ ++ ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); ++ if (ret) ++ return ret; ++ if (s5k6aa_gpio_deassert(s5k6aa, STBY)) ++ usleep_range(150, 200); ++ ++ if (s5k6aa->s_power) ++ ret = s5k6aa->s_power(1); ++ usleep_range(4000, 4000); ++ ++ if (s5k6aa_gpio_deassert(s5k6aa, RST)) ++ msleep(20); ++ ++ return ret; ++} ++ ++static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) ++{ ++ int ret; ++ ++ if (s5k6aa_gpio_assert(s5k6aa, RST)) ++ usleep_range(100, 150); ++ ++ if (s5k6aa->s_power) { ++ ret = s5k6aa->s_power(0); ++ if (ret) ++ return ret; ++ } ++ if (s5k6aa_gpio_assert(s5k6aa, STBY)) ++ usleep_range(50, 100); ++ s5k6aa->streaming = 0; ++ ++ return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); ++} ++ ++/* ++ * V4L2 subdev core and video operations ++ */ ++static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int ret = 0; ++ ++ mutex_lock(&s5k6aa->lock); ++ ++ if (!on == s5k6aa->power) { ++ if (on) { ++ ret = __s5k6aa_power_on(s5k6aa); ++ if (!ret) ++ ret = s5k6aa_initialize_isp(sd); ++ } else { ++ ret = __s5k6aa_power_off(s5k6aa); ++ } ++ ++ if (!ret) ++ s5k6aa->power += on ? 1 : -1; ++ } ++ ++ mutex_unlock(&s5k6aa->lock); ++ ++ if (!on || ret || s5k6aa->power != 1) ++ return ret; ++ ++ return v4l2_ctrl_handler_setup(sd->ctrl_handler); ++} ++ ++static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ int ret = 0; ++ ++ ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); ++ if (!ret) ++ ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); ++ if (!ret) ++ s5k6aa->streaming = enable; ++ ++ return ret; ++} ++ ++static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int ret = 0; ++ ++ mutex_lock(&s5k6aa->lock); ++ ++ if (s5k6aa->streaming == !on) { ++ if (!ret && s5k6aa->apply_cfg) ++ ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); ++ if (s5k6aa->apply_crop) ++ ret = s5k6aa_set_input_params(s5k6aa); ++ if (!ret) ++ ret = __s5k6aa_stream(s5k6aa, !!on); ++ } ++ mutex_unlock(&s5k6aa->lock); ++ ++ return ret; ++} ++ ++static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ ++ mutex_lock(&s5k6aa->lock); ++ fi->interval = s5k6aa->fiv->interval; ++ mutex_unlock(&s5k6aa->lock); ++ ++ return 0; ++} ++ ++static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; ++ const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; ++ unsigned int err, min_err = UINT_MAX; ++ unsigned int i, fr_time; ++ ++ if (fi->interval.denominator == 0) ++ return -EINVAL; ++ ++ fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; ++ ++ for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { ++ const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; ++ ++ if (mbus_fmt->width > iv->size.width || ++ mbus_fmt->height > iv->size.height) ++ continue; ++ ++ err = abs(iv->reg_fr_time - fr_time); ++ if (err < min_err) { ++ fiv = iv; ++ min_err = err; ++ } ++ } ++ s5k6aa->fiv = fiv; ++ ++ v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", ++ fiv->reg_fr_time * 100); ++ return 0; ++} ++ ++static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_frame_interval *fi) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int ret; ++ ++ v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", ++ fi->interval.numerator, fi->interval.denominator); ++ ++ mutex_lock(&s5k6aa->lock); ++ ret = __s5k6aa_set_frame_interval(s5k6aa, fi); ++ s5k6aa->apply_cfg = 1; ++ ++ mutex_unlock(&s5k6aa->lock); ++ return ret; ++} ++ ++/* ++ * V4L2 subdev pad level and video operations ++ */ ++static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_interval_enum *fie) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ const struct s5k6aa_interval *fi; ++ int ret = 0; ++ ++ if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) ++ return -EINVAL; ++ ++ v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, ++ S5K6AA_WIN_WIDTH_MAX, 1, ++ &fie->height, S5K6AA_WIN_HEIGHT_MIN, ++ S5K6AA_WIN_HEIGHT_MAX, 1, 0); ++ ++ mutex_lock(&s5k6aa->lock); ++ fi = &s5k6aa_intervals[fie->index]; ++ if (fie->width > fi->size.width || fie->height > fi->size.height) ++ ret = -EINVAL; ++ else ++ fie->interval = fi->interval; ++ mutex_unlock(&s5k6aa->lock); ++ ++ return ret; ++} ++ ++static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ if (code->index >= ARRAY_SIZE(s5k6aa_formats)) ++ return -EINVAL; ++ ++ code->code = s5k6aa_formats[code->index].code; ++ return 0; ++} ++ ++static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_frame_size_enum *fse) ++{ ++ int i = ARRAY_SIZE(s5k6aa_formats); ++ ++ if (fse->index > 0) ++ return -EINVAL; ++ ++ while (--i) ++ if (fse->code == s5k6aa_formats[i].code) ++ break; ++ ++ fse->code = s5k6aa_formats[i].code; ++ fse->min_width = S5K6AA_WIN_WIDTH_MIN; ++ fse->max_width = S5K6AA_WIN_WIDTH_MAX; ++ fse->max_height = S5K6AA_WIN_HEIGHT_MIN; ++ fse->min_height = S5K6AA_WIN_HEIGHT_MAX; ++ ++ return 0; ++} ++ ++static struct v4l2_rect * ++__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, ++ enum v4l2_subdev_format_whence which) ++{ ++ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ return &s5k6aa->ccd_rect; ++ ++ WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY); ++ return v4l2_subdev_get_try_crop(fh, 0); ++} ++ ++static void s5k6aa_try_format(struct s5k6aa *s5k6aa, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ unsigned int index; ++ ++ v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, ++ S5K6AA_WIN_WIDTH_MAX, 1, ++ &mf->height, S5K6AA_WIN_HEIGHT_MIN, ++ S5K6AA_WIN_HEIGHT_MAX, 1, 0); ++ ++ if (mf->colorspace != V4L2_COLORSPACE_JPEG && ++ mf->colorspace != V4L2_COLORSPACE_REC709) ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ ++ index = s5k6aa_get_pixfmt_index(s5k6aa, mf); ++ ++ mf->colorspace = s5k6aa_formats[index].colorspace; ++ mf->code = s5k6aa_formats[index].code; ++ mf->field = V4L2_FIELD_NONE; ++} ++ ++static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ struct v4l2_mbus_framefmt *mf; ++ ++ memset(fmt->reserved, 0, sizeof(fmt->reserved)); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ fmt->format = *mf; ++ return 0; ++ } ++ ++ mutex_lock(&s5k6aa->lock); ++ fmt->format = s5k6aa->preset->mbus_fmt; ++ mutex_unlock(&s5k6aa->lock); ++ ++ return 0; ++} ++ ++static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ struct s5k6aa_preset *preset = s5k6aa->preset; ++ struct v4l2_mbus_framefmt *mf; ++ struct v4l2_rect *crop; ++ int ret = 0; ++ ++ mutex_lock(&s5k6aa->lock); ++ s5k6aa_try_format(s5k6aa, &fmt->format); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ mf = v4l2_subdev_get_try_format(fh, fmt->pad); ++ crop = v4l2_subdev_get_try_crop(fh, 0); ++ } else { ++ if (s5k6aa->streaming) { ++ ret = -EBUSY; ++ } else { ++ mf = &preset->mbus_fmt; ++ crop = &s5k6aa->ccd_rect; ++ s5k6aa->apply_cfg = 1; ++ } ++ } ++ ++ if (ret == 0) { ++ struct v4l2_subdev_frame_interval fiv = { ++ .interval = {0, 1} ++ }; ++ ++ *mf = fmt->format; ++ /* ++ * Make sure the crop window is valid, i.e. its size is ++ * greater than the output window, as the ISP supports ++ * only down-scaling. ++ */ ++ crop->width = clamp_t(unsigned int, crop->width, mf->width, ++ S5K6AA_WIN_WIDTH_MAX); ++ crop->height = clamp_t(unsigned int, crop->height, mf->height, ++ S5K6AA_WIN_HEIGHT_MAX); ++ crop->left = clamp_t(unsigned int, crop->left, 0, ++ S5K6AA_WIN_WIDTH_MAX - crop->width); ++ crop->top = clamp_t(unsigned int, crop->top, 0, ++ S5K6AA_WIN_HEIGHT_MAX - crop->height); ++ ++ /* Reset to minimum possible frame interval */ ++ ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); ++ } ++ mutex_unlock(&s5k6aa->lock); ++ ++ return ret; ++} ++ ++static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ struct v4l2_rect *rect; ++ ++ memset(crop->reserved, 0, sizeof(crop->reserved)); ++ ++ mutex_lock(&s5k6aa->lock); ++ rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); ++ crop->rect = *rect; ++ mutex_unlock(&s5k6aa->lock); ++ ++ v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", ++ rect->left, rect->top, rect->width, rect->height); ++ ++ return 0; ++} ++ ++static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_crop *crop) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ struct v4l2_mbus_framefmt *mf; ++ unsigned int max_x, max_y; ++ struct v4l2_rect *crop_r; ++ ++ mutex_lock(&s5k6aa->lock); ++ crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); ++ ++ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ mf = &s5k6aa->preset->mbus_fmt; ++ s5k6aa->apply_crop = 1; ++ } else { ++ mf = v4l2_subdev_get_try_format(fh, 0); ++ } ++ v4l_bound_align_image(&crop->rect.width, mf->width, ++ S5K6AA_WIN_WIDTH_MAX, 1, ++ &crop->rect.height, mf->height, ++ S5K6AA_WIN_HEIGHT_MAX, 1, 0); ++ ++ max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; ++ max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; ++ ++ crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); ++ crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); ++ ++ *crop_r = crop->rect; ++ ++ mutex_unlock(&s5k6aa->lock); ++ ++ v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", ++ crop_r->left, crop_r->top, crop_r->width, crop_r->height); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { ++ .enum_mbus_code = s5k6aa_enum_mbus_code, ++ .enum_frame_size = s5k6aa_enum_frame_size, ++ .enum_frame_interval = s5k6aa_enum_frame_interval, ++ .get_fmt = s5k6aa_get_fmt, ++ .set_fmt = s5k6aa_set_fmt, ++ .get_crop = s5k6aa_get_crop, ++ .set_crop = s5k6aa_set_crop, ++}; ++ ++static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { ++ .g_frame_interval = s5k6aa_g_frame_interval, ++ .s_frame_interval = s5k6aa_s_frame_interval, ++ .s_stream = s5k6aa_s_stream, ++}; ++ ++/* ++ * V4L2 subdev controls ++ */ ++ ++static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ctrl_to_sd(ctrl); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int idx, err = 0; ++ ++ v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); ++ ++ mutex_lock(&s5k6aa->lock); ++ /* ++ * If the device is not powered up by the host driver do ++ * not apply any controls to H/W at this time. Instead ++ * the controls will be restored right after power-up. ++ */ ++ if (s5k6aa->power == 0) ++ goto unlock; ++ idx = s5k6aa->preset->index; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ err = s5k6aa_set_awb(s5k6aa, ctrl->val); ++ break; ++ ++ case V4L2_CID_BRIGHTNESS: ++ err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); ++ break; ++ ++ case V4L2_CID_COLORFX: ++ err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); ++ break; ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); ++ break; ++ ++ case V4L2_CID_HFLIP: ++ err = s5k6aa_set_mirror(s5k6aa, ctrl->val); ++ if (err) ++ break; ++ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); ++ break; ++ ++ case V4L2_CID_POWER_LINE_FREQUENCY: ++ err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); ++ break; ++ ++ case V4L2_CID_SHARPNESS: ++ err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); ++ break; ++ ++ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: ++ err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); ++ if (err) ++ break; ++ err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); ++ break; ++ } ++unlock: ++ mutex_unlock(&s5k6aa->lock); ++ return err; ++} ++ ++static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { ++ .s_ctrl = s5k6aa_s_ctrl, ++}; ++ ++static int s5k6aa_log_status(struct v4l2_subdev *sd) ++{ ++ v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); ++ return 0; ++} ++ ++#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) ++#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) ++#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) ++ ++static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { ++ { ++ .ops = &s5k6aa_ctrl_ops, ++ .id = V4L2_CID_RED_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Red", ++ .min = 0, ++ .max = 256, ++ .def = 127, ++ .step = 1, ++ }, { ++ .ops = &s5k6aa_ctrl_ops, ++ .id = V4L2_CID_GREEN_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Green", ++ .min = 0, ++ .max = 256, ++ .def = 127, ++ .step = 1, ++ }, { ++ .ops = &s5k6aa_ctrl_ops, ++ .id = V4L2_CID_BLUE_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain, Blue", ++ .min = 0, ++ .max = 256, ++ .def = 127, ++ .step = 1, ++ }, ++}; ++ ++static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) ++{ ++ const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; ++ struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; ++ struct v4l2_ctrl_handler *hdl = &ctrls->handler; ++ ++ int ret = v4l2_ctrl_handler_init(hdl, 16); ++ if (ret) ++ return ret; ++ /* Auto white balance cluster */ ++ ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, ++ 0, 1, 1, 1); ++ ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); ++ ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); ++ ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); ++ v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); ++ ++ ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_cluster(2, &ctrls->hflip); ++ ++ ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, ++ V4L2_CID_EXPOSURE_AUTO, ++ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); ++ /* Exposure time: x 1 us */ ++ ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, ++ 0, 6000000U, 1, 100000U); ++ /* Total gain: 256 <=> 1x */ ++ ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, ++ 0, 256, 1, 256); ++ v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); ++ ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO); ++ ++ v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, ++ V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); ++ ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, ++ 0, 256, 1, 0); ++ ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); ++ ++ if (hdl->error) { ++ ret = hdl->error; ++ v4l2_ctrl_handler_free(hdl); ++ return ret; ++ } ++ ++ s5k6aa->sd.ctrl_handler = hdl; ++ return 0; ++} ++ ++/* ++ * V4L2 subdev internal operations ++ */ ++static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); ++ struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); ++ ++ format->colorspace = s5k6aa_formats[0].colorspace; ++ format->code = s5k6aa_formats[0].code; ++ format->width = S5K6AA_OUT_WIDTH_DEF; ++ format->height = S5K6AA_OUT_HEIGHT_DEF; ++ format->field = V4L2_FIELD_NONE; ++ ++ crop->width = S5K6AA_WIN_WIDTH_MAX; ++ crop->height = S5K6AA_WIN_HEIGHT_MAX; ++ crop->left = 0; ++ crop->top = 0; ++ ++ return 0; ++} ++ ++static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); ++ u16 api_ver = 0, fw_rev = 0; ++ ++ int ret = s5k6aa_set_ahb_address(client); ++ ++ if (!ret) ++ ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); ++ if (!ret) ++ ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); ++ if (ret) { ++ v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); ++ return ret; ++ } ++ ++ v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", ++ api_ver, fw_rev); ++ ++ return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; ++} ++ ++static int s5k6aa_registered(struct v4l2_subdev *sd) ++{ ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ int ret; ++ ++ mutex_lock(&s5k6aa->lock); ++ ret = __s5k6aa_power_on(s5k6aa); ++ if (!ret) { ++ msleep(100); ++ ret = s5k6aa_check_fw_revision(s5k6aa); ++ __s5k6aa_power_off(s5k6aa); ++ } ++ mutex_unlock(&s5k6aa->lock); ++ ++ return ret; ++} ++ ++static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { ++ .registered = s5k6aa_registered, ++ .open = s5k6aa_open, ++}; ++ ++static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { ++ .s_power = s5k6aa_set_power, ++ .log_status = s5k6aa_log_status, ++}; ++ ++static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { ++ .core = &s5k6aa_core_ops, ++ .pad = &s5k6aa_pad_ops, ++ .video = &s5k6aa_video_ops, ++}; ++ ++/* ++ * GPIO setup ++ */ ++static int s5k6aa_configure_gpio(int nr, int val, const char *name) ++{ ++ unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ int ret; ++ ++ if (!gpio_is_valid(nr)) ++ return 0; ++ ret = gpio_request_one(nr, flags, name); ++ if (!ret) ++ gpio_export(nr, 0); ++ return ret; ++} ++ ++static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { ++ if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) ++ continue; ++ gpio_free(s5k6aa->gpio[i].gpio); ++ s5k6aa->gpio[i].gpio = -EINVAL; ++ } ++} ++ ++static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, ++ const struct s5k6aa_platform_data *pdata) ++{ ++ const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; ++ int ret; ++ ++ s5k6aa->gpio[STBY].gpio = -EINVAL; ++ s5k6aa->gpio[RST].gpio = -EINVAL; ++ ++ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); ++ if (ret) { ++ s5k6aa_free_gpios(s5k6aa); ++ return ret; ++ } ++ s5k6aa->gpio[STBY] = *gpio; ++ if (gpio_is_valid(gpio->gpio)) ++ gpio_set_value(gpio->gpio, 0); ++ ++ gpio = &pdata->gpio_reset; ++ ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); ++ if (ret) { ++ s5k6aa_free_gpios(s5k6aa); ++ return ret; ++ } ++ s5k6aa->gpio[RST] = *gpio; ++ if (gpio_is_valid(gpio->gpio)) ++ gpio_set_value(gpio->gpio, 0); ++ ++ return 0; ++} ++ ++static int s5k6aa_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ const struct s5k6aa_platform_data *pdata = client->dev.platform_data; ++ struct v4l2_subdev *sd; ++ struct s5k6aa *s5k6aa; ++ int i, ret; ++ ++ if (pdata == NULL) { ++ dev_err(&client->dev, "Platform data not specified\n"); ++ return -EINVAL; ++ } ++ ++ if (pdata->mclk_frequency == 0) { ++ dev_err(&client->dev, "MCLK frequency not specified\n"); ++ return -EINVAL; ++ } ++ ++ s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); ++ if (!s5k6aa) ++ return -ENOMEM; ++ ++ mutex_init(&s5k6aa->lock); ++ ++ s5k6aa->mclk_frequency = pdata->mclk_frequency; ++ s5k6aa->bus_type = pdata->bus_type; ++ s5k6aa->mipi_lanes = pdata->nlanes; ++ s5k6aa->s_power = pdata->set_power; ++ s5k6aa->inv_hflip = pdata->horiz_flip; ++ s5k6aa->inv_vflip = pdata->vert_flip; ++ ++ sd = &s5k6aa->sd; ++ v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); ++ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); ++ ++ sd->internal_ops = &s5k6aa_subdev_internal_ops; ++ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ ++ s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; ++ sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); ++ if (ret) ++ return ret; ++ ++ ret = s5k6aa_configure_gpios(s5k6aa, pdata); ++ if (ret) ++ goto out_err2; ++ ++ for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) ++ s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; ++ ++ ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, ++ s5k6aa->supplies); ++ if (ret) { ++ dev_err(&client->dev, "Failed to get regulators\n"); ++ goto out_err3; ++ } ++ ++ ret = s5k6aa_initialize_ctrls(s5k6aa); ++ if (ret) ++ goto out_err4; ++ ++ s5k6aa_presets_data_init(s5k6aa); ++ ++ s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; ++ s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; ++ s5k6aa->ccd_rect.left = 0; ++ s5k6aa->ccd_rect.top = 0; ++ ++ return 0; ++ ++out_err4: ++ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); ++out_err3: ++ s5k6aa_free_gpios(s5k6aa); ++out_err2: ++ media_entity_cleanup(&s5k6aa->sd.entity); ++ return ret; ++} ++ ++static int s5k6aa_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct s5k6aa *s5k6aa = to_s5k6aa(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ media_entity_cleanup(&sd->entity); ++ regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); ++ s5k6aa_free_gpios(s5k6aa); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id s5k6aa_id[] = { ++ { DRIVER_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, s5k6aa_id); ++ ++ ++static struct i2c_driver s5k6aa_i2c_driver = { ++ .driver = { ++ .name = DRIVER_NAME ++ }, ++ .probe = s5k6aa_probe, ++ .remove = s5k6aa_remove, ++ .id_table = s5k6aa_id, ++}; ++ ++module_i2c_driver(s5k6aa_i2c_driver); ++ ++MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); ++MODULE_AUTHOR("Sylwester Nawrocki "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c +new file mode 100644 +index 0000000..0caac50 +--- /dev/null ++++ b/drivers/media/i2c/saa6588.c +@@ -0,0 +1,542 @@ ++/* ++ Driver for SAA6588 RDS decoder ++ ++ (c) 2005 Hans J. Koch ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++ ++/* insmod options */ ++static unsigned int debug; ++static unsigned int xtal; ++static unsigned int mmbs; ++static unsigned int plvl; ++static unsigned int bufblocks = 100; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++module_param(xtal, int, 0); ++MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); ++module_param(mmbs, int, 0); ++MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); ++module_param(plvl, int, 0); ++MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); ++module_param(bufblocks, int, 0); ++MODULE_PARM_DESC(bufblocks, "number of buffered blocks, default 100"); ++ ++MODULE_DESCRIPTION("v4l2 driver module for SAA6588 RDS decoder"); ++MODULE_AUTHOR("Hans J. Koch "); ++ ++MODULE_LICENSE("GPL"); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define UNSET (-1U) ++#define PREFIX "saa6588: " ++#define dprintk if (debug) printk ++ ++struct saa6588 { ++ struct v4l2_subdev sd; ++ struct delayed_work work; ++ spinlock_t lock; ++ unsigned char *buffer; ++ unsigned int buf_size; ++ unsigned int rd_index; ++ unsigned int wr_index; ++ unsigned int block_count; ++ unsigned char last_blocknum; ++ wait_queue_head_t read_queue; ++ int data_available_for_read; ++ u8 sync; ++}; ++ ++static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa6588, sd); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * SAA6588 defines ++ */ ++ ++/* Initialization and mode control byte (0w) */ ++ ++/* bit 0+1 (DAC0/DAC1) */ ++#define cModeStandard 0x00 ++#define cModeFastPI 0x01 ++#define cModeReducedRequest 0x02 ++#define cModeInvalid 0x03 ++ ++/* bit 2 (RBDS) */ ++#define cProcessingModeRDS 0x00 ++#define cProcessingModeRBDS 0x04 ++ ++/* bit 3+4 (SYM0/SYM1) */ ++#define cErrCorrectionNone 0x00 ++#define cErrCorrection2Bits 0x08 ++#define cErrCorrection5Bits 0x10 ++#define cErrCorrectionNoneRBDS 0x18 ++ ++/* bit 5 (NWSY) */ ++#define cSyncNormal 0x00 ++#define cSyncRestart 0x20 ++ ++/* bit 6 (TSQD) */ ++#define cSigQualityDetectOFF 0x00 ++#define cSigQualityDetectON 0x40 ++ ++/* bit 7 (SQCM) */ ++#define cSigQualityTriggered 0x00 ++#define cSigQualityContinous 0x80 ++ ++/* Pause level and flywheel control byte (1w) */ ++ ++/* bits 0..5 (FEB0..FEB5) */ ++#define cFlywheelMaxBlocksMask 0x3F ++#define cFlywheelDefault 0x20 ++ ++/* bits 6+7 (PL0/PL1) */ ++#define cPauseLevel_11mV 0x00 ++#define cPauseLevel_17mV 0x40 ++#define cPauseLevel_27mV 0x80 ++#define cPauseLevel_43mV 0xC0 ++ ++/* Pause time/oscillator frequency/quality detector control byte (1w) */ ++ ++/* bits 0..4 (SQS0..SQS4) */ ++#define cQualityDetectSensMask 0x1F ++#define cQualityDetectDefault 0x0F ++ ++/* bit 5 (SOSC) */ ++#define cSelectOscFreqOFF 0x00 ++#define cSelectOscFreqON 0x20 ++ ++/* bit 6+7 (PTF0/PTF1) */ ++#define cOscFreq_4332kHz 0x00 ++#define cOscFreq_8664kHz 0x40 ++#define cOscFreq_12996kHz 0x80 ++#define cOscFreq_17328kHz 0xC0 ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int block_to_user_buf(struct saa6588 *s, unsigned char __user *user_buf) ++{ ++ int i; ++ ++ if (s->rd_index == s->wr_index) { ++ if (debug > 2) ++ dprintk(PREFIX "Read: buffer empty.\n"); ++ return 0; ++ } ++ ++ if (debug > 2) { ++ dprintk(PREFIX "Read: "); ++ for (i = s->rd_index; i < s->rd_index + 3; i++) ++ dprintk("0x%02x ", s->buffer[i]); ++ } ++ ++ if (copy_to_user(user_buf, &s->buffer[s->rd_index], 3)) ++ return -EFAULT; ++ ++ s->rd_index += 3; ++ if (s->rd_index >= s->buf_size) ++ s->rd_index = 0; ++ s->block_count--; ++ ++ if (debug > 2) ++ dprintk("%d blocks total.\n", s->block_count); ++ ++ return 1; ++} ++ ++static void read_from_buf(struct saa6588 *s, struct saa6588_command *a) ++{ ++ unsigned long flags; ++ ++ unsigned char __user *buf_ptr = a->buffer; ++ unsigned int i; ++ unsigned int rd_blocks; ++ ++ a->result = 0; ++ if (!a->buffer) ++ return; ++ ++ while (!s->data_available_for_read) { ++ int ret = wait_event_interruptible(s->read_queue, ++ s->data_available_for_read); ++ if (ret == -ERESTARTSYS) { ++ a->result = -EINTR; ++ return; ++ } ++ } ++ ++ spin_lock_irqsave(&s->lock, flags); ++ rd_blocks = a->block_count; ++ if (rd_blocks > s->block_count) ++ rd_blocks = s->block_count; ++ ++ if (!rd_blocks) { ++ spin_unlock_irqrestore(&s->lock, flags); ++ return; ++ } ++ ++ for (i = 0; i < rd_blocks; i++) { ++ if (block_to_user_buf(s, buf_ptr)) { ++ buf_ptr += 3; ++ a->result++; ++ } else ++ break; ++ } ++ a->result *= 3; ++ s->data_available_for_read = (s->block_count > 0); ++ spin_unlock_irqrestore(&s->lock, flags); ++} ++ ++static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) ++{ ++ unsigned int i; ++ ++ if (debug > 3) ++ dprintk(PREFIX "New block: "); ++ ++ for (i = 0; i < 3; ++i) { ++ if (debug > 3) ++ dprintk("0x%02x ", blockbuf[i]); ++ s->buffer[s->wr_index] = blockbuf[i]; ++ s->wr_index++; ++ } ++ ++ if (s->wr_index >= s->buf_size) ++ s->wr_index = 0; ++ ++ if (s->wr_index == s->rd_index) { ++ s->rd_index += 3; ++ if (s->rd_index >= s->buf_size) ++ s->rd_index = 0; ++ } else ++ s->block_count++; ++ ++ if (debug > 3) ++ dprintk("%d blocks total.\n", s->block_count); ++} ++ ++static void saa6588_i2c_poll(struct saa6588 *s) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s->sd); ++ unsigned long flags; ++ unsigned char tmpbuf[6]; ++ unsigned char blocknum; ++ unsigned char tmp; ++ ++ /* Although we only need 3 bytes, we have to read at least 6. ++ SAA6588 returns garbage otherwise. */ ++ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { ++ if (debug > 1) ++ dprintk(PREFIX "read error!\n"); ++ return; ++ } ++ ++ s->sync = tmpbuf[0] & 0x10; ++ if (!s->sync) ++ return; ++ blocknum = tmpbuf[0] >> 5; ++ if (blocknum == s->last_blocknum) { ++ if (debug > 3) ++ dprintk("Saw block %d again.\n", blocknum); ++ return; ++ } ++ ++ s->last_blocknum = blocknum; ++ ++ /* ++ Byte order according to v4l2 specification: ++ ++ Byte 0: Least Significant Byte of RDS Block ++ Byte 1: Most Significant Byte of RDS Block ++ Byte 2 Bit 7: Error bit. Indicates that an uncorrectable error ++ occurred during reception of this block. ++ Bit 6: Corrected bit. Indicates that an error was ++ corrected for this data block. ++ Bits 5-3: Same as bits 0-2. ++ Bits 2-0: Block number. ++ ++ SAA6588 byte order is Status-MSB-LSB, so we have to swap the ++ first and the last of the 3 bytes block. ++ */ ++ ++ tmp = tmpbuf[2]; ++ tmpbuf[2] = tmpbuf[0]; ++ tmpbuf[0] = tmp; ++ ++ /* Map 'Invalid block E' to 'Invalid Block' */ ++ if (blocknum == 6) ++ blocknum = V4L2_RDS_BLOCK_INVALID; ++ /* And if are not in mmbs mode, then 'Block E' is also mapped ++ to 'Invalid Block'. As far as I can tell MMBS is discontinued, ++ and if there is ever a need to support E blocks, then please ++ contact the linux-media mailinglist. */ ++ else if (!mmbs && blocknum == 5) ++ blocknum = V4L2_RDS_BLOCK_INVALID; ++ tmp = blocknum; ++ tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ ++ if ((tmpbuf[2] & 0x03) == 0x03) ++ tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ ++ else if ((tmpbuf[2] & 0x03) != 0x00) ++ tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ ++ tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ ++ ++ spin_lock_irqsave(&s->lock, flags); ++ block_to_buf(s, tmpbuf); ++ spin_unlock_irqrestore(&s->lock, flags); ++ s->data_available_for_read = 1; ++ wake_up_interruptible(&s->read_queue); ++} ++ ++static void saa6588_work(struct work_struct *work) ++{ ++ struct saa6588 *s = container_of(work, struct saa6588, work.work); ++ ++ saa6588_i2c_poll(s); ++ schedule_delayed_work(&s->work, msecs_to_jiffies(20)); ++} ++ ++static void saa6588_configure(struct saa6588 *s) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&s->sd); ++ unsigned char buf[3]; ++ int rc; ++ ++ buf[0] = cSyncRestart; ++ if (mmbs) ++ buf[0] |= cProcessingModeRBDS; ++ ++ buf[1] = cFlywheelDefault; ++ switch (plvl) { ++ case 0: ++ buf[1] |= cPauseLevel_11mV; ++ break; ++ case 1: ++ buf[1] |= cPauseLevel_17mV; ++ break; ++ case 2: ++ buf[1] |= cPauseLevel_27mV; ++ break; ++ case 3: ++ buf[1] |= cPauseLevel_43mV; ++ break; ++ default: /* nothing */ ++ break; ++ } ++ ++ buf[2] = cQualityDetectDefault | cSelectOscFreqON; ++ ++ switch (xtal) { ++ case 0: ++ buf[2] |= cOscFreq_4332kHz; ++ break; ++ case 1: ++ buf[2] |= cOscFreq_8664kHz; ++ break; ++ case 2: ++ buf[2] |= cOscFreq_12996kHz; ++ break; ++ case 3: ++ buf[2] |= cOscFreq_17328kHz; ++ break; ++ default: /* nothing */ ++ break; ++ } ++ ++ dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", ++ buf[0], buf[1], buf[2]); ++ ++ rc = i2c_master_send(client, buf, 3); ++ if (rc != 3) ++ printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct saa6588 *s = to_saa6588(sd); ++ struct saa6588_command *a = arg; ++ ++ switch (cmd) { ++ /* --- open() for /dev/radio --- */ ++ case SAA6588_CMD_OPEN: ++ a->result = 0; /* return error if chip doesn't work ??? */ ++ break; ++ /* --- close() for /dev/radio --- */ ++ case SAA6588_CMD_CLOSE: ++ s->data_available_for_read = 1; ++ wake_up_interruptible(&s->read_queue); ++ a->result = 0; ++ break; ++ /* --- read() for /dev/radio --- */ ++ case SAA6588_CMD_READ: ++ read_from_buf(s, a); ++ break; ++ /* --- poll() for /dev/radio --- */ ++ case SAA6588_CMD_POLL: ++ a->result = 0; ++ if (s->data_available_for_read) { ++ a->result |= POLLIN | POLLRDNORM; ++ } ++ poll_wait(a->instance, &s->read_queue, a->event_list); ++ break; ++ ++ default: ++ /* nothing */ ++ return -ENOIOCTLCMD; ++ } ++ return 0; ++} ++ ++static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct saa6588 *s = to_saa6588(sd); ++ ++ vt->capability |= V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO; ++ if (s->sync) ++ vt->rxsubchans |= V4L2_TUNER_SUB_RDS; ++ return 0; ++} ++ ++static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct saa6588 *s = to_saa6588(sd); ++ ++ saa6588_configure(s); ++ return 0; ++} ++ ++static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops saa6588_core_ops = { ++ .g_chip_ident = saa6588_g_chip_ident, ++ .ioctl = saa6588_ioctl, ++}; ++ ++static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { ++ .g_tuner = saa6588_g_tuner, ++ .s_tuner = saa6588_s_tuner, ++}; ++ ++static const struct v4l2_subdev_ops saa6588_ops = { ++ .core = &saa6588_core_ops, ++ .tuner = &saa6588_tuner_ops, ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int saa6588_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct saa6588 *s; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "saa6588 found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ s = kzalloc(sizeof(*s), GFP_KERNEL); ++ if (s == NULL) ++ return -ENOMEM; ++ ++ s->buf_size = bufblocks * 3; ++ ++ s->buffer = kmalloc(s->buf_size, GFP_KERNEL); ++ if (s->buffer == NULL) { ++ kfree(s); ++ return -ENOMEM; ++ } ++ sd = &s->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa6588_ops); ++ spin_lock_init(&s->lock); ++ s->block_count = 0; ++ s->wr_index = 0; ++ s->rd_index = 0; ++ s->last_blocknum = 0xff; ++ init_waitqueue_head(&s->read_queue); ++ s->data_available_for_read = 0; ++ ++ saa6588_configure(s); ++ ++ /* start polling via eventd */ ++ INIT_DELAYED_WORK(&s->work, saa6588_work); ++ schedule_delayed_work(&s->work, 0); ++ return 0; ++} ++ ++static int saa6588_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct saa6588 *s = to_saa6588(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ ++ cancel_delayed_work_sync(&s->work); ++ ++ kfree(s->buffer); ++ kfree(s); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id saa6588_id[] = { ++ { "saa6588", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa6588_id); ++ ++static struct i2c_driver saa6588_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa6588", ++ }, ++ .probe = saa6588_probe, ++ .remove = saa6588_remove, ++ .id_table = saa6588_id, ++}; ++ ++module_i2c_driver(saa6588_driver); +diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c +new file mode 100644 +index 0000000..51cd4c8 +--- /dev/null ++++ b/drivers/media/i2c/saa7110.c +@@ -0,0 +1,494 @@ ++/* ++ * saa7110 - Philips SAA7110(A) video decoder driver ++ * ++ * Copyright (C) 1998 Pauline Middelink ++ * ++ * Copyright (C) 1999 Wolfgang Scherr ++ * Copyright (C) 2000 Serguei Miridonov ++ * - some corrections for Pinnacle Systems Inc. DC10plus card. ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (1/1/2003) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); ++MODULE_AUTHOR("Pauline Middelink"); ++MODULE_LICENSE("GPL"); ++ ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++#define SAA7110_MAX_INPUT 9 /* 6 CVBS, 3 SVHS */ ++#define SAA7110_MAX_OUTPUT 1 /* 1 YUV */ ++ ++#define SAA7110_NR_REG 0x35 ++ ++struct saa7110 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ u8 reg[SAA7110_NR_REG]; ++ ++ v4l2_std_id norm; ++ int input; ++ int enable; ++ ++ wait_queue_head_t wq; ++}; ++ ++static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa7110, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct saa7110, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* I2C support functions */ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa7110 *decoder = to_saa7110(sd); ++ ++ decoder->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa7110 *decoder = to_saa7110(sd); ++ int ret = -1; ++ u8 reg = *data; /* first register to write to */ ++ ++ /* Sanity check */ ++ if (reg + (len - 1) > SAA7110_NR_REG) ++ return ret; ++ ++ /* the saa7110 has an autoincrement function, use it if ++ * the adapter understands raw I2C */ ++ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ ret = i2c_master_send(client, data, len); ++ ++ /* Cache the written data */ ++ memcpy(decoder->reg + reg, data + 1, len - 1); ++ } else { ++ for (++data, --len; len; len--) { ++ ret = saa7110_write(sd, reg++, *data++); ++ if (ret < 0) ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static inline int saa7110_read(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte(client); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* SAA7110 functions */ ++/* ----------------------------------------------------------------------- */ ++ ++#define FRESP_06H_COMPST 0x03 /*0x13*/ ++#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ ++ ++ ++static int saa7110_selmux(struct v4l2_subdev *sd, int chan) ++{ ++ static const unsigned char modes[9][8] = { ++ /* mode 0 */ ++ {FRESP_06H_COMPST, 0xD9, 0x17, 0x40, 0x03, ++ 0x44, 0x75, 0x16}, ++ /* mode 1 */ ++ {FRESP_06H_COMPST, 0xD8, 0x17, 0x40, 0x03, ++ 0x44, 0x75, 0x16}, ++ /* mode 2 */ ++ {FRESP_06H_COMPST, 0xBA, 0x07, 0x91, 0x03, ++ 0x60, 0xB5, 0x05}, ++ /* mode 3 */ ++ {FRESP_06H_COMPST, 0xB8, 0x07, 0x91, 0x03, ++ 0x60, 0xB5, 0x05}, ++ /* mode 4 */ ++ {FRESP_06H_COMPST, 0x7C, 0x07, 0xD2, 0x83, ++ 0x60, 0xB5, 0x03}, ++ /* mode 5 */ ++ {FRESP_06H_COMPST, 0x78, 0x07, 0xD2, 0x83, ++ 0x60, 0xB5, 0x03}, ++ /* mode 6 */ ++ {FRESP_06H_SVIDEO, 0x59, 0x17, 0x42, 0xA3, ++ 0x44, 0x75, 0x12}, ++ /* mode 7 */ ++ {FRESP_06H_SVIDEO, 0x9A, 0x17, 0xB1, 0x13, ++ 0x60, 0xB5, 0x14}, ++ /* mode 8 */ ++ {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, ++ 0x44, 0x75, 0x21} ++ }; ++ struct saa7110 *decoder = to_saa7110(sd); ++ const unsigned char *ptr = modes[chan]; ++ ++ saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ ++ saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ ++ saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ ++ saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ ++ saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ ++ saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ ++ saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ ++ saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ ++ decoder->input = chan; ++ ++ return 0; ++} ++ ++static const unsigned char initseq[1 + SAA7110_NR_REG] = { ++ 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF2, 0x03, 0x00, ++ /* 0x08 */ 0xF8, 0xF8, 0x60, 0x60, 0x00, 0x86, 0x18, 0x90, ++ /* 0x10 */ 0x00, 0x59, 0x40, 0x46, 0x42, 0x1A, 0xFF, 0xDA, ++ /* 0x18 */ 0xF2, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ /* 0x20 */ 0xD9, 0x16, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, ++ /* 0x28 */ 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x03, 0x0C, ++ /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 ++}; ++ ++static v4l2_std_id determine_norm(struct v4l2_subdev *sd) ++{ ++ DEFINE_WAIT(wait); ++ struct saa7110 *decoder = to_saa7110(sd); ++ int status; ++ ++ /* mode changed, start automatic detection */ ++ saa7110_write_block(sd, initseq, sizeof(initseq)); ++ saa7110_selmux(sd, decoder->input); ++ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); ++ schedule_timeout(msecs_to_jiffies(250)); ++ finish_wait(&decoder->wq, &wait); ++ status = saa7110_read(sd); ++ if (status & 0x40) { ++ v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); ++ return decoder->norm; /* no change*/ ++ } ++ if ((status & 3) == 0) { ++ saa7110_write(sd, 0x06, 0x83); ++ if (status & 0x20) { ++ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); ++ /*saa7110_write(sd,0x2E,0x81);*/ ++ return V4L2_STD_NTSC; ++ } ++ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); ++ /*saa7110_write(sd,0x2E,0x9A);*/ ++ return V4L2_STD_PAL; ++ } ++ /*saa7110_write(sd,0x06,0x03);*/ ++ if (status & 0x20) { /* 60Hz */ ++ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); ++ saa7110_write(sd, 0x0D, 0x86); ++ saa7110_write(sd, 0x0F, 0x50); ++ saa7110_write(sd, 0x11, 0x2C); ++ /*saa7110_write(sd,0x2E,0x81);*/ ++ return V4L2_STD_NTSC; ++ } ++ ++ /* 50Hz -> PAL/SECAM */ ++ saa7110_write(sd, 0x0D, 0x86); ++ saa7110_write(sd, 0x0F, 0x10); ++ saa7110_write(sd, 0x11, 0x59); ++ /*saa7110_write(sd,0x2E,0x9A);*/ ++ ++ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); ++ schedule_timeout(msecs_to_jiffies(250)); ++ finish_wait(&decoder->wq, &wait); ++ ++ status = saa7110_read(sd); ++ if ((status & 0x03) == 0x01) { ++ v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); ++ saa7110_write(sd, 0x0D, 0x87); ++ return V4L2_STD_SECAM; ++ } ++ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); ++ return V4L2_STD_PAL; ++} ++ ++static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) ++{ ++ struct saa7110 *decoder = to_saa7110(sd); ++ int res = V4L2_IN_ST_NO_SIGNAL; ++ int status = saa7110_read(sd); ++ ++ v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", ++ status, (unsigned long long)decoder->norm); ++ if (!(status & 0x40)) ++ res = 0; ++ if (!(status & 0x03)) ++ res |= V4L2_IN_ST_NO_COLOR; ++ ++ *pstatus = res; ++ return 0; ++} ++ ++static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ *(v4l2_std_id *)std = determine_norm(sd); ++ return 0; ++} ++ ++static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa7110 *decoder = to_saa7110(sd); ++ ++ if (decoder->norm != std) { ++ decoder->norm = std; ++ /*saa7110_write(sd, 0x06, 0x03);*/ ++ if (std & V4L2_STD_NTSC) { ++ saa7110_write(sd, 0x0D, 0x86); ++ saa7110_write(sd, 0x0F, 0x50); ++ saa7110_write(sd, 0x11, 0x2C); ++ /*saa7110_write(sd, 0x2E, 0x81);*/ ++ v4l2_dbg(1, debug, sd, "switched to NTSC\n"); ++ } else if (std & V4L2_STD_PAL) { ++ saa7110_write(sd, 0x0D, 0x86); ++ saa7110_write(sd, 0x0F, 0x10); ++ saa7110_write(sd, 0x11, 0x59); ++ /*saa7110_write(sd, 0x2E, 0x9A);*/ ++ v4l2_dbg(1, debug, sd, "switched to PAL\n"); ++ } else if (std & V4L2_STD_SECAM) { ++ saa7110_write(sd, 0x0D, 0x87); ++ saa7110_write(sd, 0x0F, 0x10); ++ saa7110_write(sd, 0x11, 0x59); ++ /*saa7110_write(sd, 0x2E, 0x9A);*/ ++ v4l2_dbg(1, debug, sd, "switched to SECAM\n"); ++ } else { ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++ ++static int saa7110_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa7110 *decoder = to_saa7110(sd); ++ ++ if (input >= SAA7110_MAX_INPUT) { ++ v4l2_dbg(1, debug, sd, "input=%d not available\n", input); ++ return -EINVAL; ++ } ++ if (decoder->input != input) { ++ saa7110_selmux(sd, input); ++ v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); ++ } ++ return 0; ++} ++ ++static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct saa7110 *decoder = to_saa7110(sd); ++ ++ if (decoder->enable != enable) { ++ decoder->enable = enable; ++ saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); ++ v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); ++ } ++ return 0; ++} ++ ++static int saa7110_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ saa7110_write(sd, 0x19, ctrl->val); ++ break; ++ case V4L2_CID_CONTRAST: ++ saa7110_write(sd, 0x13, ctrl->val); ++ break; ++ case V4L2_CID_SATURATION: ++ saa7110_write(sd, 0x12, ctrl->val); ++ break; ++ case V4L2_CID_HUE: ++ saa7110_write(sd, 0x07, ctrl->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops saa7110_ctrl_ops = { ++ .s_ctrl = saa7110_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops saa7110_core_ops = { ++ .g_chip_ident = saa7110_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = saa7110_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops saa7110_video_ops = { ++ .s_routing = saa7110_s_routing, ++ .s_stream = saa7110_s_stream, ++ .querystd = saa7110_querystd, ++ .g_input_status = saa7110_g_input_status, ++}; ++ ++static const struct v4l2_subdev_ops saa7110_ops = { ++ .core = &saa7110_core_ops, ++ .video = &saa7110_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7110_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct saa7110 *decoder; ++ struct v4l2_subdev *sd; ++ int rv; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); ++ if (!decoder) ++ return -ENOMEM; ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa7110_ops); ++ decoder->norm = V4L2_STD_PAL; ++ decoder->input = 0; ++ decoder->enable = 1; ++ v4l2_ctrl_handler_init(&decoder->hdl, 2); ++ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&decoder->hdl, &saa7110_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ sd->ctrl_handler = &decoder->hdl; ++ if (decoder->hdl.error) { ++ int err = decoder->hdl.error; ++ ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&decoder->hdl); ++ ++ init_waitqueue_head(&decoder->wq); ++ ++ rv = saa7110_write_block(sd, initseq, sizeof(initseq)); ++ if (rv < 0) { ++ v4l2_dbg(1, debug, sd, "init status %d\n", rv); ++ } else { ++ int ver, status; ++ saa7110_write(sd, 0x21, 0x10); ++ saa7110_write(sd, 0x0e, 0x18); ++ saa7110_write(sd, 0x0D, 0x04); ++ ver = saa7110_read(sd); ++ saa7110_write(sd, 0x0D, 0x06); ++ /*mdelay(150);*/ ++ status = saa7110_read(sd); ++ v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", ++ ver, status); ++ saa7110_write(sd, 0x0D, 0x86); ++ saa7110_write(sd, 0x0F, 0x10); ++ saa7110_write(sd, 0x11, 0x59); ++ /*saa7110_write(sd, 0x2E, 0x9A);*/ ++ } ++ ++ /*saa7110_selmux(sd,0);*/ ++ /*determine_norm(sd);*/ ++ /* setup and implicit mode 0 select has been performed */ ++ ++ return 0; ++} ++ ++static int saa7110_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct saa7110 *decoder = to_saa7110(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id saa7110_id[] = { ++ { "saa7110", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa7110_id); ++ ++static struct i2c_driver saa7110_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa7110", ++ }, ++ .probe = saa7110_probe, ++ .remove = saa7110_remove, ++ .id_table = saa7110_id, ++}; ++ ++module_i2c_driver(saa7110_driver); +diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c +new file mode 100644 +index 0000000..6b6788c +--- /dev/null ++++ b/drivers/media/i2c/saa7115.c +@@ -0,0 +1,1728 @@ ++/* saa711x - Philips SAA711x video decoder driver ++ * This driver can work with saa7111, saa7111a, saa7113, saa7114, ++ * saa7115 and saa7118. ++ * ++ * Based on saa7114 driver by Maxim Yevtyushkin, which is based on ++ * the saa7111 driver by Dave Perks. ++ * ++ * Copyright (C) 1998 Dave Perks ++ * Copyright (C) 2002 Maxim Yevtyushkin ++ * ++ * Slight changes for video timing and attachment output by ++ * Wolfgang Scherr ++ * ++ * Moved over to the linux >= 2.4.x i2c protocol (1/1/2003) ++ * by Ronald Bultje ++ * ++ * Added saa7115 support by Kevin Thayer ++ * (2/17/2003) ++ * ++ * VBI support (2004) and cleanups (2005) by Hans Verkuil ++ * ++ * Copyright (c) 2005-2006 Mauro Carvalho Chehab ++ * SAA7111, SAA7113 and SAA7118 support ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++ */ ++ ++#include "saa711x_regs.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define VRES_60HZ (480+16) ++ ++MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver"); ++MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " ++ "Hans Verkuil, Mauro Carvalho Chehab"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++module_param(debug, bool, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++struct saa711x_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ ++ struct { ++ /* chroma gain control cluster */ ++ struct v4l2_ctrl *agc; ++ struct v4l2_ctrl *gain; ++ }; ++ ++ v4l2_std_id std; ++ int input; ++ int output; ++ int enable; ++ int radio; ++ int width; ++ int height; ++ u32 ident; ++ u32 audclk_freq; ++ u32 crystal_freq; ++ u8 ucgc; ++ u8 cgcdiv; ++ u8 apll; ++}; ++ ++static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa711x_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct saa711x_state, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* Sanity routine to check if a register is present */ ++static int saa711x_has_reg(const int id, const u8 reg) ++{ ++ if (id == V4L2_IDENT_SAA7111) ++ return reg < 0x20 && reg != 0x01 && reg != 0x0f && ++ (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; ++ if (id == V4L2_IDENT_SAA7111A) ++ return reg < 0x20 && reg != 0x01 && reg != 0x0f && ++ reg != 0x14 && reg != 0x18 && reg != 0x19 && ++ reg != 0x1d && reg != 0x1e; ++ ++ /* common for saa7113/4/5/8 */ ++ if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || ++ reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) || ++ reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) || ++ reg == 0x82 || (reg >= 0x89 && reg <= 0x8e))) ++ return 0; ++ ++ switch (id) { ++ case V4L2_IDENT_SAA7113: ++ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) && ++ reg != 0x5d && reg < 0x63; ++ case V4L2_IDENT_SAA7114: ++ return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) && ++ (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 && ++ reg != 0x81 && reg < 0xf0; ++ case V4L2_IDENT_SAA7115: ++ return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe); ++ case V4L2_IDENT_SAA7118: ++ return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) && ++ (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 && ++ (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0; ++ } ++ return 1; ++} ++ ++static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) ++{ ++ struct saa711x_state *state = to_state(sd); ++ unsigned char reg, data; ++ ++ while (*regs != 0x00) { ++ reg = *(regs++); ++ data = *(regs++); ++ ++ /* According with datasheets, reserved regs should be ++ filled with 0 - seems better not to touch on they */ ++ if (saa711x_has_reg(state->ident, reg)) { ++ if (saa711x_write(sd, reg, data) < 0) ++ return -1; ++ } else { ++ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); ++ } ++ } ++ return 0; ++} ++ ++static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* SAA7111 initialization table */ ++static const unsigned char saa7111_init[] = { ++ R_01_INC_DELAY, 0x00, /* reserved */ ++ ++ /*front end */ ++ R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */ ++ R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, ++ * GAFIX=0, GAI1=256, GAI2=256 */ ++ R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */ ++ R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */ ++ ++ /* decoder */ ++ R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz) ++ * pixels after end of last line */ ++ R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to ++ * work with NTSC, too */ ++ R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0, ++ * VTRC=1, HPLL=0, VNOI=0 */ ++ R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0, ++ * VBLB=0, UPTCV=0, APER=1 */ ++ R_0A_LUMA_BRIGHT_CNTL, 0x80, ++ R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */ ++ R_0C_CHROMA_SAT_CNTL, 0x40, ++ R_0D_CHROMA_HUE_CNTL, 0x00, ++ R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, ++ * FCTC=0, CHBW=1 */ ++ R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */ ++ R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ ++ R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, ++ * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ ++ R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */ ++ R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */ ++ R_14_ANAL_ADC_COMPAT_CNTL, 0x00, ++ R_15_VGATE_START_FID_CHG, 0x00, ++ R_16_VGATE_STOP, 0x00, ++ R_17_MISC_VGATE_CONF_AND_MSB, 0x00, ++ ++ 0x00, 0x00 ++}; ++ ++/* SAA7113 init codes */ ++static const unsigned char saa7113_init[] = { ++ R_01_INC_DELAY, 0x08, ++ R_02_INPUT_CNTL_1, 0xc2, ++ R_03_INPUT_CNTL_2, 0x30, ++ R_04_INPUT_CNTL_3, 0x00, ++ R_05_INPUT_CNTL_4, 0x00, ++ R_06_H_SYNC_START, 0x89, ++ R_07_H_SYNC_STOP, 0x0d, ++ R_08_SYNC_CNTL, 0x88, ++ R_09_LUMA_CNTL, 0x01, ++ R_0A_LUMA_BRIGHT_CNTL, 0x80, ++ R_0B_LUMA_CONTRAST_CNTL, 0x47, ++ R_0C_CHROMA_SAT_CNTL, 0x40, ++ R_0D_CHROMA_HUE_CNTL, 0x00, ++ R_0E_CHROMA_CNTL_1, 0x01, ++ R_0F_CHROMA_GAIN_CNTL, 0x2a, ++ R_10_CHROMA_CNTL_2, 0x08, ++ R_11_MODE_DELAY_CNTL, 0x0c, ++ R_12_RT_SIGNAL_CNTL, 0x07, ++ R_13_RT_X_PORT_OUT_CNTL, 0x00, ++ R_14_ANAL_ADC_COMPAT_CNTL, 0x00, ++ R_15_VGATE_START_FID_CHG, 0x00, ++ R_16_VGATE_STOP, 0x00, ++ R_17_MISC_VGATE_CONF_AND_MSB, 0x00, ++ ++ 0x00, 0x00 ++}; ++ ++/* If a value differs from the Hauppauge driver values, then the comment starts with ++ 'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the ++ Hauppauge driver sets. */ ++ ++/* SAA7114 and SAA7115 initialization table */ ++static const unsigned char saa7115_init_auto_input[] = { ++ /* Front-End Part */ ++ R_01_INC_DELAY, 0x48, /* white peak control disabled */ ++ R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */ ++ R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */ ++ R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */ ++ /* Decoder Part */ ++ R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */ ++ R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */ ++ R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ ++ R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */ ++ R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */ ++ R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */ ++ R_0D_CHROMA_HUE_CNTL, 0x00, ++ R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */ ++ R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */ ++ R_11_MODE_DELAY_CNTL, 0x00, ++ R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */ ++ R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */ ++ R_14_ANAL_ADC_COMPAT_CNTL, 0x00, ++ R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */ ++ R_19_RAW_DATA_OFF_CNTL, 0x80, ++ R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */ ++ R_1B_MISC_TVVCRDET, 0x42, /* recommended value */ ++ R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */ ++ R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */ ++ ++ ++ R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */ ++ ++ /* Power Device Control */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */ ++ 0x00, 0x00 ++}; ++ ++/* Used to reset saa7113, saa7114 and saa7115 */ ++static const unsigned char saa7115_cfg_reset_scaler[] = { ++ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ ++ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */ ++ 0x00, 0x00 ++}; ++ ++/* ============== SAA7715 VIDEO templates ============= */ ++ ++static const unsigned char saa7115_cfg_60hz_video[] = { ++ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ ++ ++ R_15_VGATE_START_FID_CHG, 0x03, ++ R_16_VGATE_STOP, 0x11, ++ R_17_MISC_VGATE_CONF_AND_MSB, 0x9c, ++ ++ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */ ++ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */ ++ ++ R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */ ++ ++ /* Task A */ ++ R_90_A_TASK_HANDLING_CNTL, 0x80, ++ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, ++ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, ++ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, ++ ++ /* hoffset low (input), 0x0002 is minimum */ ++ R_94_A_HORIZ_INPUT_WINDOW_START, 0x01, ++ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* hsize low (input), 0x02d0 = 720 */ ++ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, ++ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ R_98_A_VERT_INPUT_WINDOW_START, 0x05, ++ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, ++ ++ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c, ++ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, ++ ++ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, ++ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, ++ ++ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c, ++ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, ++ ++ /* Task B */ ++ R_C0_B_TASK_HANDLING_CNTL, 0x00, ++ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, ++ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, ++ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, ++ ++ /* 0x0002 is minimum */ ++ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02, ++ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* 0x02d0 = 720 */ ++ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, ++ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ /* vwindow start 0x12 = 18 */ ++ R_C8_B_VERT_INPUT_WINDOW_START, 0x12, ++ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* vwindow length 0xf8 = 248 */ ++ R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1, ++ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9, ++ ++ /* hwindow 0x02d0 = 720 */ ++ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, ++ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */ ++ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */ ++ R_F5_PULSGEN_LINE_LENGTH, 0xad, ++ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, ++ ++ 0x00, 0x00 ++}; ++ ++static const unsigned char saa7115_cfg_50hz_video[] = { ++ R_80_GLOBAL_CNTL_1, 0x00, ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ ++ ++ R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */ ++ R_16_VGATE_STOP, 0x16, ++ R_17_MISC_VGATE_CONF_AND_MSB, 0x99, ++ ++ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */ ++ R_0E_CHROMA_CNTL_1, 0x07, ++ ++ R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */ ++ ++ /* Task A */ ++ R_90_A_TASK_HANDLING_CNTL, 0x81, ++ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48, ++ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40, ++ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84, ++ ++ /* This is weird: the datasheet says that you should use 2 as the minimum value, */ ++ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ ++ /* hoffset low (input), 0x0002 is minimum */ ++ R_94_A_HORIZ_INPUT_WINDOW_START, 0x00, ++ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* hsize low (input), 0x02d0 = 720 */ ++ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, ++ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ R_98_A_VERT_INPUT_WINDOW_START, 0x03, ++ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* vsize 0x12 = 18 */ ++ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12, ++ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00, ++ ++ /* hsize 0x05a0 = 1440 */ ++ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0, ++ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */ ++ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */ ++ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */ ++ ++ /* Task B */ ++ R_C0_B_TASK_HANDLING_CNTL, 0x00, ++ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08, ++ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00, ++ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80, ++ ++ /* This is weird: the datasheet says that you should use 2 as the minimum value, */ ++ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */ ++ /* hoffset low (input), 0x0002 is minimum. See comment above. */ ++ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00, ++ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* hsize 0x02d0 = 720 */ ++ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0, ++ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ /* voffset 0x16 = 22 */ ++ R_C8_B_VERT_INPUT_WINDOW_START, 0x16, ++ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00, ++ ++ /* vsize 0x0120 = 288 */ ++ R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20, ++ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01, ++ ++ /* hsize 0x02d0 = 720 */ ++ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0, ++ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02, ++ ++ R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */ ++ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */ ++ R_F5_PULSGEN_LINE_LENGTH, 0xb0, ++ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01, ++ ++ 0x00, 0x00 ++}; ++ ++/* ============== SAA7715 VIDEO templates (end) ======= */ ++ ++static const unsigned char saa7115_cfg_vbi_on[] = { ++ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ ++ R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ ++ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ ++ ++ 0x00, 0x00 ++}; ++ ++static const unsigned char saa7115_cfg_vbi_off[] = { ++ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */ ++ R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */ ++ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */ ++ ++ 0x00, 0x00 ++}; ++ ++ ++static const unsigned char saa7115_init_misc[] = { ++ R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01, ++ R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01, ++ R_84_I_PORT_SIGNAL_DEF, 0x20, ++ R_85_I_PORT_SIGNAL_POLAR, 0x21, ++ R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5, ++ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, ++ ++ /* Task A */ ++ R_A0_A_HORIZ_PRESCALING, 0x01, ++ R_A1_A_ACCUMULATION_LENGTH, 0x00, ++ R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, ++ ++ /* Configure controls at nominal value*/ ++ R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80, ++ R_A5_A_LUMA_CONTRAST_CNTL, 0x40, ++ R_A6_A_CHROMA_SATURATION_CNTL, 0x40, ++ ++ /* note: 2 x zoom ensures that VBI lines have same length as video lines. */ ++ R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00, ++ R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02, ++ ++ R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00, ++ ++ /* must be horiz lum scaling / 2 */ ++ R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00, ++ R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01, ++ ++ /* must be offset luma / 2 */ ++ R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00, ++ ++ R_B0_A_VERT_LUMA_SCALING_INC, 0x00, ++ R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04, ++ ++ R_B2_A_VERT_CHROMA_SCALING_INC, 0x00, ++ R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04, ++ ++ R_B4_A_VERT_SCALING_MODE_CNTL, 0x01, ++ ++ R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00, ++ R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00, ++ R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00, ++ R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00, ++ ++ R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00, ++ R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00, ++ R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00, ++ R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00, ++ ++ /* Task B */ ++ R_D0_B_HORIZ_PRESCALING, 0x01, ++ R_D1_B_ACCUMULATION_LENGTH, 0x00, ++ R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00, ++ ++ /* Configure controls at nominal value*/ ++ R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80, ++ R_D5_B_LUMA_CONTRAST_CNTL, 0x40, ++ R_D6_B_CHROMA_SATURATION_CNTL, 0x40, ++ ++ /* hor lum scaling 0x0400 = 1 */ ++ R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00, ++ R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04, ++ ++ R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00, ++ ++ /* must be hor lum scaling / 2 */ ++ R_DC_B_HORIZ_CHROMA_SCALING, 0x00, ++ R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02, ++ ++ /* must be offset luma / 2 */ ++ R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00, ++ ++ R_E0_B_VERT_LUMA_SCALING_INC, 0x00, ++ R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04, ++ ++ R_E2_B_VERT_CHROMA_SCALING_INC, 0x00, ++ R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04, ++ ++ R_E4_B_VERT_SCALING_MODE_CNTL, 0x01, ++ ++ R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00, ++ R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00, ++ R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00, ++ R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00, ++ ++ R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00, ++ R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00, ++ R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00, ++ R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00, ++ ++ R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */ ++ R_F3_PLL_INCREMENT, 0x46, ++ R_F4_PLL2_STATUS, 0x00, ++ R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */ ++ R_F8_PULSE_B_POS, 0x00, ++ R_F9_PULSE_B_POS_MSB, 0x4b, ++ R_FA_PULSE_C_POS, 0x00, ++ R_FB_PULSE_C_POS_MSB, 0x4b, ++ ++ /* PLL2 lock detection settings: 71 lines 50% phase error */ ++ R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88, ++ ++ /* Turn off VBI */ ++ R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */ ++ R_41_LCR_BASE, 0xff, ++ R_41_LCR_BASE+1, 0xff, ++ R_41_LCR_BASE+2, 0xff, ++ R_41_LCR_BASE+3, 0xff, ++ R_41_LCR_BASE+4, 0xff, ++ R_41_LCR_BASE+5, 0xff, ++ R_41_LCR_BASE+6, 0xff, ++ R_41_LCR_BASE+7, 0xff, ++ R_41_LCR_BASE+8, 0xff, ++ R_41_LCR_BASE+9, 0xff, ++ R_41_LCR_BASE+10, 0xff, ++ R_41_LCR_BASE+11, 0xff, ++ R_41_LCR_BASE+12, 0xff, ++ R_41_LCR_BASE+13, 0xff, ++ R_41_LCR_BASE+14, 0xff, ++ R_41_LCR_BASE+15, 0xff, ++ R_41_LCR_BASE+16, 0xff, ++ R_41_LCR_BASE+17, 0xff, ++ R_41_LCR_BASE+18, 0xff, ++ R_41_LCR_BASE+19, 0xff, ++ R_41_LCR_BASE+20, 0xff, ++ R_41_LCR_BASE+21, 0xff, ++ R_41_LCR_BASE+22, 0xff, ++ R_58_PROGRAM_FRAMING_CODE, 0x40, ++ R_59_H_OFF_FOR_SLICER, 0x47, ++ R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83, ++ R_5D_DID, 0xbd, ++ R_5E_SDID, 0x35, ++ ++ R_02_INPUT_CNTL_1, 0xc4, /* input tuner -> input 4, amplifier active */ ++ ++ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */ ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, ++ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, ++ 0x00, 0x00 ++}; ++ ++static int saa711x_odd_parity(u8 c) ++{ ++ c ^= (c >> 4); ++ c ^= (c >> 2); ++ c ^= (c >> 1); ++ ++ return c & 1; ++} ++ ++static int saa711x_decode_vps(u8 *dst, u8 *p) ++{ ++ static const u8 biphase_tbl[] = { ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, ++ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, ++ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, ++ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, ++ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, ++ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, ++ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ }; ++ int i; ++ u8 c, err = 0; ++ ++ for (i = 0; i < 2 * 13; i += 2) { ++ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; ++ c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); ++ dst[i / 2] = c; ++ } ++ return err & 0xf0; ++} ++ ++static int saa711x_decode_wss(u8 *p) ++{ ++ static const int wss_bits[8] = { ++ 0, 0, 0, 1, 0, 1, 1, 1 ++ }; ++ unsigned char parity; ++ int wss = 0; ++ int i; ++ ++ for (i = 0; i < 16; i++) { ++ int b1 = wss_bits[p[i] & 7]; ++ int b2 = wss_bits[(p[i] >> 3) & 7]; ++ ++ if (b1 == b2) ++ return -1; ++ wss |= b2 << i; ++ } ++ parity = wss & 15; ++ parity ^= parity >> 2; ++ parity ^= parity >> 1; ++ ++ if (!(parity & 1)) ++ return -1; ++ ++ return wss; ++} ++ ++static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ struct saa711x_state *state = to_state(sd); ++ u32 acpf; ++ u32 acni; ++ u32 hz; ++ u64 f; ++ u8 acc = 0; /* reg 0x3a, audio clock control */ ++ ++ /* Checks for chips that don't have audio clock (saa7111, saa7113) */ ++ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) ++ return 0; ++ ++ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); ++ ++ /* sanity check */ ++ if (freq < 32000 || freq > 48000) ++ return -EINVAL; ++ ++ /* hz is the refresh rate times 100 */ ++ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; ++ /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ ++ acpf = (25600 * freq) / hz; ++ /* acni = (256 * freq * 2^23) / crystal_frequency = ++ (freq * 2^(8+23)) / crystal_frequency = ++ (freq << 31) / crystal_frequency */ ++ f = freq; ++ f = f << 31; ++ do_div(f, state->crystal_freq); ++ acni = f; ++ if (state->ucgc) { ++ acpf = acpf * state->cgcdiv / 16; ++ acni = acni * state->cgcdiv / 16; ++ acc = 0x80; ++ if (state->cgcdiv == 3) ++ acc |= 0x40; ++ } ++ if (state->apll) ++ acc |= 0x08; ++ ++ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); ++ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); ++ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); ++ ++ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); ++ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, ++ (acpf >> 8) & 0xff); ++ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, ++ (acpf >> 16) & 0x03); ++ ++ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); ++ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); ++ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); ++ state->audclk_freq = freq; ++ return 0; ++} ++ ++static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct saa711x_state *state = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_CHROMA_AGC: ++ /* chroma gain cluster */ ++ if (state->agc->val) ++ state->gain->val = ++ saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; ++ break; ++ } ++ return 0; ++} ++ ++static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct saa711x_state *state = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, ctrl->val); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, ctrl->val); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, ctrl->val); ++ break; ++ ++ case V4L2_CID_HUE: ++ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, ctrl->val); ++ break; ++ ++ case V4L2_CID_CHROMA_AGC: ++ /* chroma gain cluster */ ++ if (state->agc->val) ++ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val); ++ else ++ saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int HPSC, HFSC; ++ int VSCY; ++ int res; ++ int is_50hz = state->std & V4L2_STD_625_50; ++ int Vsrc = is_50hz ? 576 : 480; ++ ++ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); ++ ++ /* FIXME need better bounds checking here */ ++ if ((width < 1) || (width > 1440)) ++ return -EINVAL; ++ if ((height < 1) || (height > Vsrc)) ++ return -EINVAL; ++ ++ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { ++ /* Decoder only supports 720 columns and 480 or 576 lines */ ++ if (width != 720) ++ return -EINVAL; ++ if (height != Vsrc) ++ return -EINVAL; ++ } ++ ++ state->width = width; ++ state->height = height; ++ ++ if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH)) ++ return 0; ++ ++ /* probably have a valid size, let's set it */ ++ /* Set output width/height */ ++ /* width */ ++ ++ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, ++ (u8) (width & 0xff)); ++ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, ++ (u8) ((width >> 8) & 0xff)); ++ ++ /* Vertical Scaling uses height/2 */ ++ res = height / 2; ++ ++ /* On 60Hz, it is using a higher Vertical Output Size */ ++ if (!is_50hz) ++ res += (VRES_60HZ - 480) >> 1; ++ ++ /* height */ ++ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, ++ (u8) (res & 0xff)); ++ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, ++ (u8) ((res >> 8) & 0xff)); ++ ++ /* Scaling settings */ ++ /* Hprescaler is floor(inres/outres) */ ++ HPSC = (int)(720 / width); ++ /* 0 is not allowed (div. by zero) */ ++ HPSC = HPSC ? HPSC : 1; ++ HFSC = (int)((1024 * 720) / (HPSC * width)); ++ /* FIXME hardcodes to "Task B" ++ * write H prescaler integer */ ++ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, ++ (u8) (HPSC & 0x3f)); ++ ++ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); ++ /* write H fine-scaling (luminance) */ ++ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, ++ (u8) (HFSC & 0xff)); ++ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, ++ (u8) ((HFSC >> 8) & 0xff)); ++ /* write H fine-scaling (chrominance) ++ * must be lum/2, so i'll just bitshift :) */ ++ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, ++ (u8) ((HFSC >> 1) & 0xff)); ++ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, ++ (u8) ((HFSC >> 9) & 0xff)); ++ ++ VSCY = (int)((1024 * Vsrc) / height); ++ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); ++ ++ /* Correct Contrast and Luminance */ ++ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, ++ (u8) (64 * 1024 / VSCY)); ++ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, ++ (u8) (64 * 1024 / VSCY)); ++ ++ /* write V fine-scaling (luminance) */ ++ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, ++ (u8) (VSCY & 0xff)); ++ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, ++ (u8) ((VSCY >> 8) & 0xff)); ++ /* write V fine-scaling (chrominance) */ ++ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, ++ (u8) (VSCY & 0xff)); ++ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, ++ (u8) ((VSCY >> 8) & 0xff)); ++ ++ saa711x_writeregs(sd, saa7115_cfg_reset_scaler); ++ ++ /* Activates task "B" */ ++ saa711x_write(sd, R_80_GLOBAL_CNTL_1, ++ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); ++ ++ return 0; ++} ++ ++static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ /* Prevent unnecessary standard changes. During a standard ++ change the I-Port is temporarily disabled. Any devices ++ reading from that port can get confused. ++ Note that s_std is also used to switch from ++ radio to TV mode, so if a s_std is broadcast to ++ all I2C devices then you do not want to have an unwanted ++ side-effect here. */ ++ if (std == state->std) ++ return; ++ ++ state->std = std; ++ ++ // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. ++ if (std & V4L2_STD_525_60) { ++ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); ++ saa711x_writeregs(sd, saa7115_cfg_60hz_video); ++ saa711x_set_size(sd, 720, 480); ++ } else { ++ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); ++ saa711x_writeregs(sd, saa7115_cfg_50hz_video); ++ saa711x_set_size(sd, 720, 576); ++ } ++ ++ /* Register 0E - Bits D6-D4 on NO-AUTO mode ++ (SAA7111 and SAA7113 doesn't have auto mode) ++ 50 Hz / 625 lines 60 Hz / 525 lines ++ 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) ++ 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) ++ 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) ++ 011 NTSC N (3.58MHz) PAL M (3.58MHz) ++ 100 reserved NTSC-Japan (3.58MHz) ++ */ ++ if (state->ident <= V4L2_IDENT_SAA7113) { ++ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; ++ ++ if (std == V4L2_STD_PAL_M) { ++ reg |= 0x30; ++ } else if (std == V4L2_STD_PAL_Nc) { ++ reg |= 0x20; ++ } else if (std == V4L2_STD_PAL_60) { ++ reg |= 0x10; ++ } else if (std == V4L2_STD_NTSC_M_JP) { ++ reg |= 0x40; ++ } else if (std & V4L2_STD_SECAM) { ++ reg |= 0x50; ++ } ++ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); ++ } else { ++ /* restart task B if needed */ ++ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; ++ ++ if (taskb && state->ident == V4L2_IDENT_SAA7114) { ++ saa711x_writeregs(sd, saa7115_cfg_vbi_on); ++ } ++ ++ /* switch audio mode too! */ ++ saa711x_s_clock_freq(sd, state->audclk_freq); ++ } ++} ++ ++/* setup the sliced VBI lcr registers according to the sliced VBI format */ ++static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int is_50hz = (state->std & V4L2_STD_625_50); ++ u8 lcr[24]; ++ int i, x; ++ ++#if 1 ++ /* saa7113/7114/7118 VBI support are experimental */ ++ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) ++ return; ++ ++#else ++ /* SAA7113 and SAA7118 also should support VBI - Need testing */ ++ if (state->ident != V4L2_IDENT_SAA7115) ++ return; ++#endif ++ ++ for (i = 0; i <= 23; i++) ++ lcr[i] = 0xff; ++ ++ if (fmt == NULL) { ++ /* raw VBI */ ++ if (is_50hz) ++ for (i = 6; i <= 23; i++) ++ lcr[i] = 0xdd; ++ else ++ for (i = 10; i <= 21; i++) ++ lcr[i] = 0xdd; ++ } else { ++ /* sliced VBI */ ++ /* first clear lines that cannot be captured */ ++ if (is_50hz) { ++ for (i = 0; i <= 5; i++) ++ fmt->service_lines[0][i] = ++ fmt->service_lines[1][i] = 0; ++ } ++ else { ++ for (i = 0; i <= 9; i++) ++ fmt->service_lines[0][i] = ++ fmt->service_lines[1][i] = 0; ++ for (i = 22; i <= 23; i++) ++ fmt->service_lines[0][i] = ++ fmt->service_lines[1][i] = 0; ++ } ++ ++ /* Now set the lcr values according to the specified service */ ++ for (i = 6; i <= 23; i++) { ++ lcr[i] = 0; ++ for (x = 0; x <= 1; x++) { ++ switch (fmt->service_lines[1-x][i]) { ++ case 0: ++ lcr[i] |= 0xf << (4 * x); ++ break; ++ case V4L2_SLICED_TELETEXT_B: ++ lcr[i] |= 1 << (4 * x); ++ break; ++ case V4L2_SLICED_CAPTION_525: ++ lcr[i] |= 4 << (4 * x); ++ break; ++ case V4L2_SLICED_WSS_625: ++ lcr[i] |= 5 << (4 * x); ++ break; ++ case V4L2_SLICED_VPS: ++ lcr[i] |= 7 << (4 * x); ++ break; ++ } ++ } ++ } ++ } ++ ++ /* write the lcr registers */ ++ for (i = 2; i <= 23; i++) { ++ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); ++ } ++ ++ /* enable/disable raw VBI capturing */ ++ saa711x_writeregs(sd, fmt == NULL ? ++ saa7115_cfg_vbi_on : ++ saa7115_cfg_vbi_off); ++} ++ ++static int saa711x_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *sliced) ++{ ++ static u16 lcr2vbi[] = { ++ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ ++ 0, V4L2_SLICED_CAPTION_525, /* 4 */ ++ V4L2_SLICED_WSS_625, 0, /* 5 */ ++ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ ++ 0, 0, 0, 0 ++ }; ++ int i; ++ ++ memset(sliced->service_lines, 0, sizeof(sliced->service_lines)); ++ sliced->service_set = 0; ++ /* done if using raw VBI */ ++ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) ++ return 0; ++ for (i = 2; i <= 23; i++) { ++ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); ++ ++ sliced->service_lines[0][i] = lcr2vbi[v >> 4]; ++ sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; ++ sliced->service_set |= ++ sliced->service_lines[0][i] | sliced->service_lines[1][i]; ++ } ++ return 0; ++} ++ ++static int saa711x_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) ++{ ++ saa711x_set_lcr(sd, NULL); ++ return 0; ++} ++ ++static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) ++{ ++ saa711x_set_lcr(sd, fmt); ++ return 0; ++} ++ ++static int saa711x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ if (fmt->code != V4L2_MBUS_FMT_FIXED) ++ return -EINVAL; ++ fmt->field = V4L2_FIELD_INTERLACED; ++ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return saa711x_set_size(sd, fmt->width, fmt->height); ++} ++ ++/* Decode the sliced VBI data stream as created by the saa7115. ++ The format is described in the saa7115 datasheet in Tables 25 and 26 ++ and in Figure 33. ++ The current implementation uses SAV/EAV codes and not the ancillary data ++ headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV ++ code. */ ++static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) ++{ ++ struct saa711x_state *state = to_state(sd); ++ static const char vbi_no_data_pattern[] = { ++ 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 ++ }; ++ u8 *p = vbi->p; ++ u32 wss; ++ int id1, id2; /* the ID1 and ID2 bytes from the internal header */ ++ ++ vbi->type = 0; /* mark result as a failure */ ++ id1 = p[2]; ++ id2 = p[3]; ++ /* Note: the field bit is inverted for 60 Hz video */ ++ if (state->std & V4L2_STD_525_60) ++ id1 ^= 0x40; ++ ++ /* Skip internal header, p now points to the start of the payload */ ++ p += 4; ++ vbi->p = p; ++ ++ /* calculate field and line number of the VBI packet (1-23) */ ++ vbi->is_second_field = ((id1 & 0x40) != 0); ++ vbi->line = (id1 & 0x3f) << 3; ++ vbi->line |= (id2 & 0x70) >> 4; ++ ++ /* Obtain data type */ ++ id2 &= 0xf; ++ ++ /* If the VBI slicer does not detect any signal it will fill up ++ the payload buffer with 0xa0 bytes. */ ++ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) ++ return 0; ++ ++ /* decode payloads */ ++ switch (id2) { ++ case 1: ++ vbi->type = V4L2_SLICED_TELETEXT_B; ++ break; ++ case 4: ++ if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) ++ return 0; ++ vbi->type = V4L2_SLICED_CAPTION_525; ++ break; ++ case 5: ++ wss = saa711x_decode_wss(p); ++ if (wss == -1) ++ return 0; ++ p[0] = wss & 0xff; ++ p[1] = wss >> 8; ++ vbi->type = V4L2_SLICED_WSS_625; ++ break; ++ case 7: ++ if (saa711x_decode_vps(p, p) != 0) ++ return 0; ++ vbi->type = V4L2_SLICED_VPS; ++ break; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++/* ============ SAA7115 AUDIO settings (end) ============= */ ++ ++static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int status; ++ ++ if (state->radio) ++ return 0; ++ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); ++ ++ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); ++ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; ++ return 0; ++} ++ ++static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ state->radio = 0; ++ saa711x_set_v4lstd(sd, std); ++ return 0; ++} ++ ++static int saa711x_s_radio(struct v4l2_subdev *sd) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ state->radio = 1; ++ return 0; ++} ++ ++static int saa711x_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa711x_state *state = to_state(sd); ++ u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; ++ ++ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", ++ input, output); ++ ++ /* saa7111/3 does not have these inputs */ ++ if (state->ident <= V4L2_IDENT_SAA7113 && ++ (input == SAA7115_COMPOSITE4 || ++ input == SAA7115_COMPOSITE5)) { ++ return -EINVAL; ++ } ++ if (input > SAA7115_SVIDEO3) ++ return -EINVAL; ++ if (state->input == input && state->output == output) ++ return 0; ++ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", ++ (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", ++ (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); ++ state->input = input; ++ ++ /* saa7111 has slightly different input numbering */ ++ if (state->ident <= V4L2_IDENT_SAA7111A) { ++ if (input >= SAA7115_COMPOSITE4) ++ input -= 2; ++ /* saa7111 specific */ ++ saa711x_write(sd, R_10_CHROMA_CNTL_2, ++ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | ++ ((output & 0xc0) ^ 0x40)); ++ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, ++ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | ++ ((output & 2) ? 0x0a : 0)); ++ } ++ ++ /* select mode */ ++ saa711x_write(sd, R_02_INPUT_CNTL_1, ++ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | ++ input); ++ ++ /* bypass chrominance trap for S-Video modes */ ++ saa711x_write(sd, R_09_LUMA_CNTL, ++ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | ++ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); ++ ++ state->output = output; ++ if (state->ident == V4L2_IDENT_SAA7114 || ++ state->ident == V4L2_IDENT_SAA7115) { ++ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, ++ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | ++ (state->output & 0x01)); ++ } ++ return 0; ++} ++ ++static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ if (state->ident > V4L2_IDENT_SAA7111A) ++ return -EINVAL; ++ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | ++ (val ? 0x80 : 0)); ++ return 0; ++} ++ ++static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "%s output\n", ++ enable ? "enable" : "disable"); ++ ++ if (state->enable == enable) ++ return 0; ++ state->enable = enable; ++ if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) ++ return 0; ++ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); ++ return 0; ++} ++ ++static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) ++{ ++ struct saa711x_state *state = to_state(sd); ++ ++ if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) ++ return -EINVAL; ++ state->crystal_freq = freq; ++ state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; ++ state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; ++ state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; ++ saa711x_s_clock_freq(sd, state->audclk_freq); ++ return 0; ++} ++ ++static int saa711x_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ v4l2_dbg(1, debug, sd, "decoder RESET\n"); ++ saa711x_writeregs(sd, saa7115_cfg_reset_scaler); ++ return 0; ++} ++ ++static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) ++{ ++ /* Note: the internal field ID is inverted for NTSC, ++ so data->field 0 maps to the saa7115 even field, ++ whereas for PAL it maps to the saa7115 odd field. */ ++ switch (data->id) { ++ case V4L2_SLICED_WSS_625: ++ if (saa711x_read(sd, 0x6b) & 0xc0) ++ return -EIO; ++ data->data[0] = saa711x_read(sd, 0x6c); ++ data->data[1] = saa711x_read(sd, 0x6d); ++ return 0; ++ case V4L2_SLICED_CAPTION_525: ++ if (data->field == 0) { ++ /* CC */ ++ if (saa711x_read(sd, 0x66) & 0x30) ++ return -EIO; ++ data->data[0] = saa711x_read(sd, 0x69); ++ data->data[1] = saa711x_read(sd, 0x6a); ++ return 0; ++ } ++ /* XDS */ ++ if (saa711x_read(sd, 0x66) & 0xc0) ++ return -EIO; ++ data->data[0] = saa711x_read(sd, 0x67); ++ data->data[1] = saa711x_read(sd, 0x68); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int reg1f, reg1e; ++ ++ /* ++ * The V4L2 core already initializes std with all supported ++ * Standards. All driver needs to do is to mask it, to remove ++ * standards that don't apply from the mask ++ */ ++ ++ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); ++ v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f); ++ ++ /* horizontal/vertical not locked */ ++ if (reg1f & 0x40) ++ goto ret; ++ ++ if (reg1f & 0x20) ++ *std &= V4L2_STD_525_60; ++ else ++ *std &= V4L2_STD_625_50; ++ ++ if (state->ident != V4L2_IDENT_SAA7115) ++ goto ret; ++ ++ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); ++ ++ switch (reg1e & 0x03) { ++ case 1: ++ *std &= V4L2_STD_NTSC; ++ break; ++ case 2: ++ /* ++ * V4L2_STD_PAL just cover the european PAL standards. ++ * This is wrong, as the device could also be using an ++ * other PAL standard. ++ */ ++ *std &= V4L2_STD_PAL | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | ++ V4L2_STD_PAL_M | V4L2_STD_PAL_60; ++ break; ++ case 3: ++ *std &= V4L2_STD_SECAM; ++ break; ++ default: ++ /* Can't detect anything */ ++ break; ++ } ++ ++ v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e); ++ ++ret: ++ v4l2_dbg(1, debug, sd, "detected std mask = %08Lx\n", *std); ++ ++ return 0; ++} ++ ++static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int reg1e = 0x80; ++ int reg1f; ++ ++ *status = V4L2_IN_ST_NO_SIGNAL; ++ if (state->ident == V4L2_IDENT_SAA7115) ++ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); ++ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); ++ if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) ++ *status = 0; ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = saa711x_read(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct saa711x_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); ++} ++ ++static int saa711x_log_status(struct v4l2_subdev *sd) ++{ ++ struct saa711x_state *state = to_state(sd); ++ int reg1e, reg1f; ++ int signalOk; ++ int vcr; ++ ++ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); ++ if (state->ident != V4L2_IDENT_SAA7115) { ++ /* status for the saa7114 */ ++ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); ++ signalOk = (reg1f & 0xc1) == 0x81; ++ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); ++ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); ++ return 0; ++ } ++ ++ /* status for the saa7115 */ ++ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); ++ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); ++ ++ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; ++ vcr = !(reg1f & 0x10); ++ ++ if (state->input >= 6) ++ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); ++ else ++ v4l2_info(sd, "Input: Composite %d\n", state->input); ++ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); ++ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); ++ ++ switch (reg1e & 0x03) { ++ case 1: ++ v4l2_info(sd, "Detected format: NTSC\n"); ++ break; ++ case 2: ++ v4l2_info(sd, "Detected format: PAL\n"); ++ break; ++ case 3: ++ v4l2_info(sd, "Detected format: SECAM\n"); ++ break; ++ default: ++ v4l2_info(sd, "Detected format: BW/No color\n"); ++ break; ++ } ++ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops saa711x_ctrl_ops = { ++ .s_ctrl = saa711x_s_ctrl, ++ .g_volatile_ctrl = saa711x_g_volatile_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops saa711x_core_ops = { ++ .log_status = saa711x_log_status, ++ .g_chip_ident = saa711x_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = saa711x_s_std, ++ .reset = saa711x_reset, ++ .s_gpio = saa711x_s_gpio, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = saa711x_g_register, ++ .s_register = saa711x_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { ++ .s_radio = saa711x_s_radio, ++ .g_tuner = saa711x_g_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { ++ .s_clock_freq = saa711x_s_clock_freq, ++}; ++ ++static const struct v4l2_subdev_video_ops saa711x_video_ops = { ++ .s_routing = saa711x_s_routing, ++ .s_crystal_freq = saa711x_s_crystal_freq, ++ .s_mbus_fmt = saa711x_s_mbus_fmt, ++ .s_stream = saa711x_s_stream, ++ .querystd = saa711x_querystd, ++ .g_input_status = saa711x_g_input_status, ++}; ++ ++static const struct v4l2_subdev_vbi_ops saa711x_vbi_ops = { ++ .g_vbi_data = saa711x_g_vbi_data, ++ .decode_vbi_line = saa711x_decode_vbi_line, ++ .g_sliced_fmt = saa711x_g_sliced_fmt, ++ .s_sliced_fmt = saa711x_s_sliced_fmt, ++ .s_raw_fmt = saa711x_s_raw_fmt, ++}; ++ ++static const struct v4l2_subdev_ops saa711x_ops = { ++ .core = &saa711x_core_ops, ++ .tuner = &saa711x_tuner_ops, ++ .audio = &saa711x_audio_ops, ++ .video = &saa711x_video_ops, ++ .vbi = &saa711x_vbi_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa711x_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct saa711x_state *state; ++ struct v4l2_subdev *sd; ++ struct v4l2_ctrl_handler *hdl; ++ int i; ++ char name[17]; ++ char chip_id; ++ int autodetect = !id || id->driver_data == 1; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ for (i = 0; i < 0x0f; i++) { ++ i2c_smbus_write_byte_data(client, 0, i); ++ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; ++ if (name[i] > '9') ++ name[i] += 'a' - '9' - 1; ++ } ++ name[i] = '\0'; ++ ++ chip_id = name[5]; ++ ++ /* Check whether this chip is part of the saa711x series */ ++ if (memcmp(name + 1, "f711", 4)) { ++ v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", ++ client->addr << 1, name); ++ return -ENODEV; ++ } ++ ++ /* Safety check */ ++ if (!autodetect && id->name[6] != chip_id) { ++ v4l_warn(client, "found saa711%c while %s was expected\n", ++ chip_id, id->name); ++ } ++ snprintf(client->name, sizeof(client->name), "saa711%c", chip_id); ++ v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name, ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa711x_ops); ++ ++ hdl = &state->hdl; ++ v4l2_ctrl_handler_init(hdl, 6); ++ /* add in ascending ID order */ ++ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ state->agc = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_CHROMA_AGC, 0, 1, 1, 1); ++ state->gain = v4l2_ctrl_new_std(hdl, &saa711x_ctrl_ops, ++ V4L2_CID_CHROMA_GAIN, 0, 127, 1, 40); ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ int err = hdl->error; ++ ++ v4l2_ctrl_handler_free(hdl); ++ kfree(state); ++ return err; ++ } ++ v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); ++ ++ state->input = -1; ++ state->output = SAA7115_IPORT_ON; ++ state->enable = 1; ++ state->radio = 0; ++ switch (chip_id) { ++ case '1': ++ state->ident = V4L2_IDENT_SAA7111; ++ if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { ++ v4l_info(client, "saa7111a variant found\n"); ++ state->ident = V4L2_IDENT_SAA7111A; ++ } ++ break; ++ case '3': ++ state->ident = V4L2_IDENT_SAA7113; ++ break; ++ case '4': ++ state->ident = V4L2_IDENT_SAA7114; ++ break; ++ case '5': ++ state->ident = V4L2_IDENT_SAA7115; ++ break; ++ case '8': ++ state->ident = V4L2_IDENT_SAA7118; ++ break; ++ default: ++ state->ident = V4L2_IDENT_SAA7111; ++ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); ++ break; ++ } ++ ++ state->audclk_freq = 48000; ++ ++ v4l2_dbg(1, debug, sd, "writing init values\n"); ++ ++ /* init to 60hz/48khz */ ++ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; ++ switch (state->ident) { ++ case V4L2_IDENT_SAA7111: ++ case V4L2_IDENT_SAA7111A: ++ saa711x_writeregs(sd, saa7111_init); ++ break; ++ case V4L2_IDENT_SAA7113: ++ saa711x_writeregs(sd, saa7113_init); ++ break; ++ default: ++ state->crystal_freq = SAA7115_FREQ_32_11_MHZ; ++ saa711x_writeregs(sd, saa7115_init_auto_input); ++ } ++ if (state->ident > V4L2_IDENT_SAA7111A) ++ saa711x_writeregs(sd, saa7115_init_misc); ++ saa711x_set_v4lstd(sd, V4L2_STD_NTSC); ++ v4l2_ctrl_handler_setup(hdl); ++ ++ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", ++ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), ++ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa711x_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id saa711x_id[] = { ++ { "saa7115_auto", 1 }, /* autodetect */ ++ { "saa7111", 0 }, ++ { "saa7113", 0 }, ++ { "saa7114", 0 }, ++ { "saa7115", 0 }, ++ { "saa7118", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa711x_id); ++ ++static struct i2c_driver saa711x_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa7115", ++ }, ++ .probe = saa711x_probe, ++ .remove = saa711x_remove, ++ .id_table = saa711x_id, ++}; ++ ++module_i2c_driver(saa711x_driver); +diff --git a/drivers/media/i2c/saa711x_regs.h b/drivers/media/i2c/saa711x_regs.h +new file mode 100644 +index 0000000..4e5f2eb +--- /dev/null ++++ b/drivers/media/i2c/saa711x_regs.h +@@ -0,0 +1,549 @@ ++/* saa711x - Philips SAA711x video decoder register specifications ++ * ++ * Copyright (c) 2006 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. ++ */ ++ ++#define R_00_CHIP_VERSION 0x00 ++/* Video Decoder */ ++ /* Video Decoder - Frontend part */ ++#define R_01_INC_DELAY 0x01 ++#define R_02_INPUT_CNTL_1 0x02 ++#define R_03_INPUT_CNTL_2 0x03 ++#define R_04_INPUT_CNTL_3 0x04 ++#define R_05_INPUT_CNTL_4 0x05 ++ /* Video Decoder - Decoder part */ ++#define R_06_H_SYNC_START 0x06 ++#define R_07_H_SYNC_STOP 0x07 ++#define R_08_SYNC_CNTL 0x08 ++#define R_09_LUMA_CNTL 0x09 ++#define R_0A_LUMA_BRIGHT_CNTL 0x0a ++#define R_0B_LUMA_CONTRAST_CNTL 0x0b ++#define R_0C_CHROMA_SAT_CNTL 0x0c ++#define R_0D_CHROMA_HUE_CNTL 0x0d ++#define R_0E_CHROMA_CNTL_1 0x0e ++#define R_0F_CHROMA_GAIN_CNTL 0x0f ++#define R_10_CHROMA_CNTL_2 0x10 ++#define R_11_MODE_DELAY_CNTL 0x11 ++#define R_12_RT_SIGNAL_CNTL 0x12 ++#define R_13_RT_X_PORT_OUT_CNTL 0x13 ++#define R_14_ANAL_ADC_COMPAT_CNTL 0x14 ++#define R_15_VGATE_START_FID_CHG 0x15 ++#define R_16_VGATE_STOP 0x16 ++#define R_17_MISC_VGATE_CONF_AND_MSB 0x17 ++#define R_18_RAW_DATA_GAIN_CNTL 0x18 ++#define R_19_RAW_DATA_OFF_CNTL 0x19 ++#define R_1A_COLOR_KILL_LVL_CNTL 0x1a ++#define R_1B_MISC_TVVCRDET 0x1b ++#define R_1C_ENHAN_COMB_CTRL1 0x1c ++#define R_1D_ENHAN_COMB_CTRL2 0x1d ++#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e ++#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f ++ ++/* Component processing and interrupt masking part */ ++#define R_23_INPUT_CNTL_5 0x23 ++#define R_24_INPUT_CNTL_6 0x24 ++#define R_25_INPUT_CNTL_7 0x25 ++#define R_29_COMP_DELAY 0x29 ++#define R_2A_COMP_BRIGHT_CNTL 0x2a ++#define R_2B_COMP_CONTRAST_CNTL 0x2b ++#define R_2C_COMP_SAT_CNTL 0x2c ++#define R_2D_INTERRUPT_MASK_1 0x2d ++#define R_2E_INTERRUPT_MASK_2 0x2e ++#define R_2F_INTERRUPT_MASK_3 0x2f ++ ++/* Audio clock generator part */ ++#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30 ++#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34 ++#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38 ++#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39 ++#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a ++ ++/* General purpose VBI data slicer part */ ++#define R_40_SLICER_CNTL_1 0x40 ++#define R_41_LCR_BASE 0x41 ++#define R_58_PROGRAM_FRAMING_CODE 0x58 ++#define R_59_H_OFF_FOR_SLICER 0x59 ++#define R_5A_V_OFF_FOR_SLICER 0x5a ++#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b ++#define R_5D_DID 0x5d ++#define R_5E_SDID 0x5e ++#define R_60_SLICER_STATUS_BYTE_0 0x60 ++#define R_61_SLICER_STATUS_BYTE_1 0x61 ++#define R_62_SLICER_STATUS_BYTE_2 0x62 ++ ++/* X port, I port and the scaler part */ ++ /* Task independent global settings */ ++#define R_80_GLOBAL_CNTL_1 0x80 ++#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81 ++#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83 ++#define R_84_I_PORT_SIGNAL_DEF 0x84 ++#define R_85_I_PORT_SIGNAL_POLAR 0x85 ++#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86 ++#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87 ++#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88 ++#define R_8F_STATUS_INFO_SCALER 0x8f ++ /* Task A definition */ ++ /* Basic settings and acquisition window definition */ ++#define R_90_A_TASK_HANDLING_CNTL 0x90 ++#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91 ++#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92 ++#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93 ++#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94 ++#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95 ++#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96 ++#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97 ++#define R_98_A_VERT_INPUT_WINDOW_START 0x98 ++#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99 ++#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a ++#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b ++#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c ++#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d ++#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e ++#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f ++ /* FIR filtering and prescaling */ ++#define R_A0_A_HORIZ_PRESCALING 0xa0 ++#define R_A1_A_ACCUMULATION_LENGTH 0xa1 ++#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2 ++#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4 ++#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5 ++#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6 ++ /* Horizontal phase scaling */ ++#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8 ++#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9 ++#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa ++#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac ++#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad ++#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae ++#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf ++ /* Vertical scaling */ ++#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0 ++#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1 ++#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2 ++#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3 ++#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4 ++#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8 ++#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9 ++#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba ++#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb ++#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc ++#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd ++#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe ++#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf ++ /* Task B definition */ ++ /* Basic settings and acquisition window definition */ ++#define R_C0_B_TASK_HANDLING_CNTL 0xc0 ++#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1 ++#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2 ++#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3 ++#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4 ++#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5 ++#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6 ++#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7 ++#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8 ++#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9 ++#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca ++#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb ++#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc ++#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd ++#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce ++#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf ++ /* FIR filtering and prescaling */ ++#define R_D0_B_HORIZ_PRESCALING 0xd0 ++#define R_D1_B_ACCUMULATION_LENGTH 0xd1 ++#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2 ++#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4 ++#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5 ++#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6 ++ /* Horizontal phase scaling */ ++#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8 ++#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9 ++#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda ++#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc ++#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd ++#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde ++ /* Vertical scaling */ ++#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0 ++#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1 ++#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2 ++#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3 ++#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4 ++#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8 ++#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9 ++#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea ++#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb ++#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec ++#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed ++#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee ++#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef ++ ++/* second PLL (PLL2) and Pulsegenerator Programming */ ++#define R_F0_LFCO_PER_LINE 0xf0 ++#define R_F1_P_I_PARAM_SELECT 0xf1 ++#define R_F2_NOMINAL_PLL2_DTO 0xf2 ++#define R_F3_PLL_INCREMENT 0xf3 ++#define R_F4_PLL2_STATUS 0xf4 ++#define R_F5_PULSGEN_LINE_LENGTH 0xf5 ++#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6 ++#define R_F7_PULSE_A_POS_MSB 0xf7 ++#define R_F8_PULSE_B_POS 0xf8 ++#define R_F9_PULSE_B_POS_MSB 0xf9 ++#define R_FA_PULSE_C_POS 0xfa ++#define R_FB_PULSE_C_POS_MSB 0xfb ++#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff ++ ++#if 0 ++/* Those structs will be used in the future for debug purposes */ ++struct saa711x_reg_descr { ++ u8 reg; ++ int count; ++ char *name; ++}; ++ ++struct saa711x_reg_descr saa711x_regs[] = { ++ /* REG COUNT NAME */ ++ {R_00_CHIP_VERSION,1, ++ "Chip version"}, ++ ++ /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */ ++ ++ /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */ ++ {R_01_INC_DELAY,1, ++ "Increment delay"}, ++ {R_02_INPUT_CNTL_1,1, ++ "Analog input control 1"}, ++ {R_03_INPUT_CNTL_2,1, ++ "Analog input control 2"}, ++ {R_04_INPUT_CNTL_3,1, ++ "Analog input control 3"}, ++ {R_05_INPUT_CNTL_4,1, ++ "Analog input control 4"}, ++ ++ /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */ ++ {R_06_H_SYNC_START,1, ++ "Horizontal sync start"}, ++ {R_07_H_SYNC_STOP,1, ++ "Horizontal sync stop"}, ++ {R_08_SYNC_CNTL,1, ++ "Sync control"}, ++ {R_09_LUMA_CNTL,1, ++ "Luminance control"}, ++ {R_0A_LUMA_BRIGHT_CNTL,1, ++ "Luminance brightness control"}, ++ {R_0B_LUMA_CONTRAST_CNTL,1, ++ "Luminance contrast control"}, ++ {R_0C_CHROMA_SAT_CNTL,1, ++ "Chrominance saturation control"}, ++ {R_0D_CHROMA_HUE_CNTL,1, ++ "Chrominance hue control"}, ++ {R_0E_CHROMA_CNTL_1,1, ++ "Chrominance control 1"}, ++ {R_0F_CHROMA_GAIN_CNTL,1, ++ "Chrominance gain control"}, ++ {R_10_CHROMA_CNTL_2,1, ++ "Chrominance control 2"}, ++ {R_11_MODE_DELAY_CNTL,1, ++ "Mode/delay control"}, ++ {R_12_RT_SIGNAL_CNTL,1, ++ "RT signal control"}, ++ {R_13_RT_X_PORT_OUT_CNTL,1, ++ "RT/X port output control"}, ++ {R_14_ANAL_ADC_COMPAT_CNTL,1, ++ "Analog/ADC/compatibility control"}, ++ {R_15_VGATE_START_FID_CHG, 1, ++ "VGATE start FID change"}, ++ {R_16_VGATE_STOP,1, ++ "VGATE stop"}, ++ {R_17_MISC_VGATE_CONF_AND_MSB, 1, ++ "Miscellaneous VGATE configuration and MSBs"}, ++ {R_18_RAW_DATA_GAIN_CNTL,1, ++ "Raw data gain control",}, ++ {R_19_RAW_DATA_OFF_CNTL,1, ++ "Raw data offset control",}, ++ {R_1A_COLOR_KILL_LVL_CNTL,1, ++ "Color Killer Level Control"}, ++ { R_1B_MISC_TVVCRDET, 1, ++ "MISC /TVVCRDET"}, ++ { R_1C_ENHAN_COMB_CTRL1, 1, ++ "Enhanced comb ctrl1"}, ++ { R_1D_ENHAN_COMB_CTRL2, 1, ++ "Enhanced comb ctrl1"}, ++ {R_1E_STATUS_BYTE_1_VD_DEC,1, ++ "Status byte 1 video decoder"}, ++ {R_1F_STATUS_BYTE_2_VD_DEC,1, ++ "Status byte 2 video decoder"}, ++ ++ /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */ ++ /* 0x20 to 0x22 - Reserved */ ++ {R_23_INPUT_CNTL_5,1, ++ "Analog input control 5"}, ++ {R_24_INPUT_CNTL_6,1, ++ "Analog input control 6"}, ++ {R_25_INPUT_CNTL_7,1, ++ "Analog input control 7"}, ++ /* 0x26 to 0x28 - Reserved */ ++ {R_29_COMP_DELAY,1, ++ "Component delay"}, ++ {R_2A_COMP_BRIGHT_CNTL,1, ++ "Component brightness control"}, ++ {R_2B_COMP_CONTRAST_CNTL,1, ++ "Component contrast control"}, ++ {R_2C_COMP_SAT_CNTL,1, ++ "Component saturation control"}, ++ {R_2D_INTERRUPT_MASK_1,1, ++ "Interrupt mask 1"}, ++ {R_2E_INTERRUPT_MASK_2,1, ++ "Interrupt mask 2"}, ++ {R_2F_INTERRUPT_MASK_3,1, ++ "Interrupt mask 3"}, ++ ++ /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */ ++ {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3, ++ "Audio master clock cycles per field"}, ++ /* 0x33 - Reserved */ ++ {R_34_AUD_MAST_CLK_NOMINAL_INC,3, ++ "Audio master clock nominal increment"}, ++ /* 0x37 - Reserved */ ++ {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1, ++ "Clock ratio AMXCLK to ASCLK"}, ++ {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1, ++ "Clock ratio ASCLK to ALRCLK"}, ++ {R_3A_AUD_CLK_GEN_BASIC_SETUP,1, ++ "Audio clock generator basic setup"}, ++ /* 0x3b-0x3f - Reserved */ ++ ++ /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */ ++ {R_40_SLICER_CNTL_1,1, ++ "Slicer control 1"}, ++ {R_41_LCR,23, ++ "R_41_LCR"}, ++ {R_58_PROGRAM_FRAMING_CODE,1, ++ "Programmable framing code"}, ++ {R_59_H_OFF_FOR_SLICER,1, ++ "Horizontal offset for slicer"}, ++ {R_5A_V_OFF_FOR_SLICER,1, ++ "Vertical offset for slicer"}, ++ {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1, ++ "Field offset and MSBs for horizontal and vertical offset"}, ++ {R_5D_DID,1, ++ "Header and data identification (R_5D_DID)"}, ++ {R_5E_SDID,1, ++ "Sliced data identification (R_5E_SDID) code"}, ++ {R_60_SLICER_STATUS_BYTE_0,1, ++ "Slicer status byte 0"}, ++ {R_61_SLICER_STATUS_BYTE_1,1, ++ "Slicer status byte 1"}, ++ {R_62_SLICER_STATUS_BYTE_2,1, ++ "Slicer status byte 2"}, ++ /* 0x63-0x7f - Reserved */ ++ ++ /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ ++ /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */ ++ {R_80_GLOBAL_CNTL_1,1, ++ "Global control 1"}, ++ {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1, ++ "Vertical sync and Field ID source selection, retimed V and F signals"}, ++ /* 0x82 - Reserved */ ++ {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1, ++ "X port I/O enable and output clock"}, ++ {R_84_I_PORT_SIGNAL_DEF,1, ++ "I port signal definitions"}, ++ {R_85_I_PORT_SIGNAL_POLAR,1, ++ "I port signal polarities"}, ++ {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1, ++ "I port FIFO flag control and arbitration"}, ++ {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1, ++ "I port I/O enable output clock and gated"}, ++ {R_88_POWER_SAVE_ADC_PORT_CNTL,1, ++ "Power save/ADC port control"}, ++ /* 089-0x8e - Reserved */ ++ {R_8F_STATUS_INFO_SCALER,1, ++ "Status information scaler part"}, ++ ++ /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */ ++ /* Task A: Basic settings and acquisition window definition */ ++ {R_90_A_TASK_HANDLING_CNTL,1, ++ "Task A: Task handling control"}, ++ {R_91_A_X_PORT_FORMATS_AND_CONF,1, ++ "Task A: X port formats and configuration"}, ++ {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1, ++ "Task A: X port input reference signal definition"}, ++ {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1, ++ "Task A: I port output formats and configuration"}, ++ {R_94_A_HORIZ_INPUT_WINDOW_START,2, ++ "Task A: Horizontal input window start"}, ++ {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2, ++ "Task A: Horizontal input window length"}, ++ {R_98_A_VERT_INPUT_WINDOW_START,2, ++ "Task A: Vertical input window start"}, ++ {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2, ++ "Task A: Vertical input window length"}, ++ {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2, ++ "Task A: Horizontal output window length"}, ++ {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2, ++ "Task A: Vertical output window length"}, ++ ++ /* Task A: FIR filtering and prescaling */ ++ {R_A0_A_HORIZ_PRESCALING,1, ++ "Task A: Horizontal prescaling"}, ++ {R_A1_A_ACCUMULATION_LENGTH,1, ++ "Task A: Accumulation length"}, ++ {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, ++ "Task A: Prescaler DC gain and FIR prefilter"}, ++ /* 0xa3 - Reserved */ ++ {R_A4_A_LUMA_BRIGHTNESS_CNTL,1, ++ "Task A: Luminance brightness control"}, ++ {R_A5_A_LUMA_CONTRAST_CNTL,1, ++ "Task A: Luminance contrast control"}, ++ {R_A6_A_CHROMA_SATURATION_CNTL,1, ++ "Task A: Chrominance saturation control"}, ++ /* 0xa7 - Reserved */ ++ ++ /* Task A: Horizontal phase scaling */ ++ {R_A8_A_HORIZ_LUMA_SCALING_INC,2, ++ "Task A: Horizontal luminance scaling increment"}, ++ {R_AA_A_HORIZ_LUMA_PHASE_OFF,1, ++ "Task A: Horizontal luminance phase offset"}, ++ /* 0xab - Reserved */ ++ {R_AC_A_HORIZ_CHROMA_SCALING_INC,2, ++ "Task A: Horizontal chrominance scaling increment"}, ++ {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1, ++ "Task A: Horizontal chrominance phase offset"}, ++ /* 0xaf - Reserved */ ++ ++ /* Task A: Vertical scaling */ ++ {R_B0_A_VERT_LUMA_SCALING_INC,2, ++ "Task A: Vertical luminance scaling increment"}, ++ {R_B2_A_VERT_CHROMA_SCALING_INC,2, ++ "Task A: Vertical chrominance scaling increment"}, ++ {R_B4_A_VERT_SCALING_MODE_CNTL,1, ++ "Task A: Vertical scaling mode control"}, ++ /* 0xb5-0xb7 - Reserved */ ++ {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1, ++ "Task A: Vertical chrominance phase offset '00'"}, ++ {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1, ++ "Task A: Vertical chrominance phase offset '01'"}, ++ {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1, ++ "Task A: Vertical chrominance phase offset '10'"}, ++ {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1, ++ "Task A: Vertical chrominance phase offset '11'"}, ++ {R_BC_A_VERT_LUMA_PHASE_OFF_00,1, ++ "Task A: Vertical luminance phase offset '00'"}, ++ {R_BD_A_VERT_LUMA_PHASE_OFF_01,1, ++ "Task A: Vertical luminance phase offset '01'"}, ++ {R_BE_A_VERT_LUMA_PHASE_OFF_10,1, ++ "Task A: Vertical luminance phase offset '10'"}, ++ {R_BF_A_VERT_LUMA_PHASE_OFF_11,1, ++ "Task A: Vertical luminance phase offset '11'"}, ++ ++ /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */ ++ /* Task B: Basic settings and acquisition window definition */ ++ {R_C0_B_TASK_HANDLING_CNTL,1, ++ "Task B: Task handling control"}, ++ {R_C1_B_X_PORT_FORMATS_AND_CONF,1, ++ "Task B: X port formats and configuration"}, ++ {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1, ++ "Task B: Input reference signal definition"}, ++ {R_C3_B_I_PORT_FORMATS_AND_CONF,1, ++ "Task B: I port formats and configuration"}, ++ {R_C4_B_HORIZ_INPUT_WINDOW_START,2, ++ "Task B: Horizontal input window start"}, ++ {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2, ++ "Task B: Horizontal input window length"}, ++ {R_C8_B_VERT_INPUT_WINDOW_START,2, ++ "Task B: Vertical input window start"}, ++ {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2, ++ "Task B: Vertical input window length"}, ++ {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2, ++ "Task B: Horizontal output window length"}, ++ {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2, ++ "Task B: Vertical output window length"}, ++ ++ /* Task B: FIR filtering and prescaling */ ++ {R_D0_B_HORIZ_PRESCALING,1, ++ "Task B: Horizontal prescaling"}, ++ {R_D1_B_ACCUMULATION_LENGTH,1, ++ "Task B: Accumulation length"}, ++ {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1, ++ "Task B: Prescaler DC gain and FIR prefilter"}, ++ /* 0xd3 - Reserved */ ++ {R_D4_B_LUMA_BRIGHTNESS_CNTL,1, ++ "Task B: Luminance brightness control"}, ++ {R_D5_B_LUMA_CONTRAST_CNTL,1, ++ "Task B: Luminance contrast control"}, ++ {R_D6_B_CHROMA_SATURATION_CNTL,1, ++ "Task B: Chrominance saturation control"}, ++ /* 0xd7 - Reserved */ ++ ++ /* Task B: Horizontal phase scaling */ ++ {R_D8_B_HORIZ_LUMA_SCALING_INC,2, ++ "Task B: Horizontal luminance scaling increment"}, ++ {R_DA_B_HORIZ_LUMA_PHASE_OFF,1, ++ "Task B: Horizontal luminance phase offset"}, ++ /* 0xdb - Reserved */ ++ {R_DC_B_HORIZ_CHROMA_SCALING,2, ++ "Task B: Horizontal chrominance scaling"}, ++ {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1, ++ "Task B: Horizontal Phase Offset Chroma"}, ++ /* 0xdf - Reserved */ ++ ++ /* Task B: Vertical scaling */ ++ {R_E0_B_VERT_LUMA_SCALING_INC,2, ++ "Task B: Vertical luminance scaling increment"}, ++ {R_E2_B_VERT_CHROMA_SCALING_INC,2, ++ "Task B: Vertical chrominance scaling increment"}, ++ {R_E4_B_VERT_SCALING_MODE_CNTL,1, ++ "Task B: Vertical scaling mode control"}, ++ /* 0xe5-0xe7 - Reserved */ ++ {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1, ++ "Task B: Vertical chrominance phase offset '00'"}, ++ {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1, ++ "Task B: Vertical chrominance phase offset '01'"}, ++ {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1, ++ "Task B: Vertical chrominance phase offset '10'"}, ++ {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1, ++ "Task B: Vertical chrominance phase offset '11'"}, ++ {R_EC_B_VERT_LUMA_PHASE_OFF_00,1, ++ "Task B: Vertical luminance phase offset '00'"}, ++ {R_ED_B_VERT_LUMA_PHASE_OFF_01,1, ++ "Task B: Vertical luminance phase offset '01'"}, ++ {R_EE_B_VERT_LUMA_PHASE_OFF_10,1, ++ "Task B: Vertical luminance phase offset '10'"}, ++ {R_EF_B_VERT_LUMA_PHASE_OFF_11,1, ++ "Task B: Vertical luminance phase offset '11'"}, ++ ++ /* second PLL (PLL2) and Pulsegenerator Programming */ ++ { R_F0_LFCO_PER_LINE, 1, ++ "LFCO's per line"}, ++ { R_F1_P_I_PARAM_SELECT,1, ++ "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"}, ++ { R_F2_NOMINAL_PLL2_DTO,1, ++ "Nominal PLL2 DTO"}, ++ {R_F3_PLL_INCREMENT,1, ++ "PLL2 Increment"}, ++ {R_F4_PLL2_STATUS,1, ++ "PLL2 Status"}, ++ {R_F5_PULSGEN_LINE_LENGTH,1, ++ "Pulsgen. line length"}, ++ {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1, ++ "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"}, ++ {R_F7_PULSE_A_POS_MSB,1, ++ "Pulse A Position"}, ++ {R_F8_PULSE_B_POS,2, ++ "Pulse B Position"}, ++ {R_FA_PULSE_C_POS,2, ++ "Pulse C Position"}, ++ /* 0xfc to 0xfe - Reserved */ ++ {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1, ++ "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"}, ++}; ++#endif +diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c +new file mode 100644 +index 0000000..b745f68 +--- /dev/null ++++ b/drivers/media/i2c/saa7127.c +@@ -0,0 +1,852 @@ ++/* ++ * saa7127 - Philips SAA7127/SAA7129 video encoder driver ++ * ++ * Copyright (C) 2003 Roy Bulter ++ * ++ * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter ++ * ++ * Copyright (C) 2000-2001 Gillem ++ * Copyright (C) 2002 Andreas Oberritter ++ * ++ * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo ++ * ++ * Copyright (C) 1999 Nathan Laredo ++ * ++ * This driver is designed for the Hauppauge 250/350 Linux driver ++ * from the ivtv Project ++ * ++ * Copyright (C) 2003 Kevin Thayer ++ * ++ * Dual output support: ++ * Copyright (C) 2004 Eric Varsanyi ++ * ++ * NTSC Tuning and 7.5 IRE Setup ++ * Copyright (C) 2004 Chris Kennedy ++ * ++ * VBI additions & cleanup: ++ * Copyright (C) 2004, 2005 Hans Verkuil ++ * ++ * Note: the saa7126 is identical to the saa7127, and the saa7128 is ++ * identical to the saa7129, except that the saa7126 and saa7128 have ++ * macrovision anti-taping support. This driver will almost certainly ++ * work fine for those chips, except of course for the missing anti-taping ++ * support. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++static int test_image; ++ ++MODULE_DESCRIPTION("Philips SAA7127/9 video encoder driver"); ++MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++module_param(debug, int, 0644); ++module_param(test_image, int, 0644); ++MODULE_PARM_DESC(debug, "debug level (0-2)"); ++MODULE_PARM_DESC(test_image, "test_image (0-1)"); ++ ++ ++/* ++ * SAA7127 registers ++ */ ++ ++#define SAA7127_REG_STATUS 0x00 ++#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 ++#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 ++#define SAA7127_REG_BURST_START 0x28 ++#define SAA7127_REG_BURST_END 0x29 ++#define SAA7127_REG_COPYGEN_0 0x2a ++#define SAA7127_REG_COPYGEN_1 0x2b ++#define SAA7127_REG_COPYGEN_2 0x2c ++#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d ++#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 ++#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 ++#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3a ++#define SAA7129_REG_FADE_KEY_COL2 0x4f ++#define SAA7127_REG_CHROMA_PHASE 0x5a ++#define SAA7127_REG_GAINU 0x5b ++#define SAA7127_REG_GAINV 0x5c ++#define SAA7127_REG_BLACK_LEVEL 0x5d ++#define SAA7127_REG_BLANKING_LEVEL 0x5e ++#define SAA7127_REG_VBI_BLANKING 0x5f ++#define SAA7127_REG_DAC_CONTROL 0x61 ++#define SAA7127_REG_BURST_AMP 0x62 ++#define SAA7127_REG_SUBC3 0x63 ++#define SAA7127_REG_SUBC2 0x64 ++#define SAA7127_REG_SUBC1 0x65 ++#define SAA7127_REG_SUBC0 0x66 ++#define SAA7127_REG_LINE_21_ODD_0 0x67 ++#define SAA7127_REG_LINE_21_ODD_1 0x68 ++#define SAA7127_REG_LINE_21_EVEN_0 0x69 ++#define SAA7127_REG_LINE_21_EVEN_1 0x6a ++#define SAA7127_REG_RCV_PORT_CONTROL 0x6b ++#define SAA7127_REG_VTRIG 0x6c ++#define SAA7127_REG_HTRIG_HI 0x6d ++#define SAA7127_REG_MULTI 0x6e ++#define SAA7127_REG_CLOSED_CAPTION 0x6f ++#define SAA7127_REG_RCV2_OUTPUT_START 0x70 ++#define SAA7127_REG_RCV2_OUTPUT_END 0x71 ++#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 ++#define SAA7127_REG_TTX_REQUEST_H_START 0x73 ++#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 ++#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 ++#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 ++#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 ++#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 ++#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 ++#define SAA7127_REG_FIRST_ACTIVE 0x7a ++#define SAA7127_REG_LAST_ACTIVE 0x7b ++#define SAA7127_REG_MSB_VERTICAL 0x7c ++#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7e ++#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7f ++ ++/* ++ ********************************************************************** ++ * ++ * Arrays with configuration parameters for the SAA7127 ++ * ++ ********************************************************************** ++ */ ++ ++struct i2c_reg_value { ++ unsigned char reg; ++ unsigned char value; ++}; ++ ++static const struct i2c_reg_value saa7129_init_config_extra[] = { ++ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0x38 }, ++ { SAA7127_REG_VTRIG, 0xfa }, ++ { 0, 0 } ++}; ++ ++static const struct i2c_reg_value saa7127_init_config_common[] = { ++ { SAA7127_REG_WIDESCREEN_CONFIG, 0x0d }, ++ { SAA7127_REG_WIDESCREEN_ENABLE, 0x00 }, ++ { SAA7127_REG_COPYGEN_0, 0x77 }, ++ { SAA7127_REG_COPYGEN_1, 0x41 }, ++ { SAA7127_REG_COPYGEN_2, 0x00 }, /* Macrovision enable/disable */ ++ { SAA7127_REG_OUTPUT_PORT_CONTROL, 0xbf }, ++ { SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00 }, ++ { SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00 }, ++ { SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80 }, /* for color bars */ ++ { SAA7127_REG_LINE_21_ODD_0, 0x77 }, ++ { SAA7127_REG_LINE_21_ODD_1, 0x41 }, ++ { SAA7127_REG_LINE_21_EVEN_0, 0x88 }, ++ { SAA7127_REG_LINE_21_EVEN_1, 0x41 }, ++ { SAA7127_REG_RCV_PORT_CONTROL, 0x12 }, ++ { SAA7127_REG_VTRIG, 0xf9 }, ++ { SAA7127_REG_HTRIG_HI, 0x00 }, ++ { SAA7127_REG_RCV2_OUTPUT_START, 0x41 }, ++ { SAA7127_REG_RCV2_OUTPUT_END, 0xc3 }, ++ { SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00 }, ++ { SAA7127_REG_TTX_REQUEST_H_START, 0x3e }, ++ { SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8 }, ++ { SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03 }, ++ { SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15 }, ++ { SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16 }, ++ { SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15 }, ++ { SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16 }, ++ { SAA7127_REG_FIRST_ACTIVE, 0x1a }, ++ { SAA7127_REG_LAST_ACTIVE, 0x01 }, ++ { SAA7127_REG_MSB_VERTICAL, 0xc0 }, ++ { SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00 }, ++ { SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00 }, ++ { 0, 0 } ++}; ++ ++#define SAA7127_60HZ_DAC_CONTROL 0x15 ++static const struct i2c_reg_value saa7127_init_config_60hz[] = { ++ { SAA7127_REG_BURST_START, 0x19 }, ++ /* BURST_END is also used as a chip ID in saa7127_probe */ ++ { SAA7127_REG_BURST_END, 0x1d }, ++ { SAA7127_REG_CHROMA_PHASE, 0xa3 }, ++ { SAA7127_REG_GAINU, 0x98 }, ++ { SAA7127_REG_GAINV, 0xd3 }, ++ { SAA7127_REG_BLACK_LEVEL, 0x39 }, ++ { SAA7127_REG_BLANKING_LEVEL, 0x2e }, ++ { SAA7127_REG_VBI_BLANKING, 0x2e }, ++ { SAA7127_REG_DAC_CONTROL, 0x15 }, ++ { SAA7127_REG_BURST_AMP, 0x4d }, ++ { SAA7127_REG_SUBC3, 0x1f }, ++ { SAA7127_REG_SUBC2, 0x7c }, ++ { SAA7127_REG_SUBC1, 0xf0 }, ++ { SAA7127_REG_SUBC0, 0x21 }, ++ { SAA7127_REG_MULTI, 0x90 }, ++ { SAA7127_REG_CLOSED_CAPTION, 0x11 }, ++ { 0, 0 } ++}; ++ ++#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 ++static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { ++ { SAA7127_REG_BURST_START, 0x21 }, ++ /* BURST_END is also used as a chip ID in saa7127_probe */ ++ { SAA7127_REG_BURST_END, 0x1d }, ++ { SAA7127_REG_CHROMA_PHASE, 0x3f }, ++ { SAA7127_REG_GAINU, 0x7d }, ++ { SAA7127_REG_GAINV, 0xaf }, ++ { SAA7127_REG_BLACK_LEVEL, 0x33 }, ++ { SAA7127_REG_BLANKING_LEVEL, 0x35 }, ++ { SAA7127_REG_VBI_BLANKING, 0x35 }, ++ { SAA7127_REG_DAC_CONTROL, 0x02 }, ++ { SAA7127_REG_BURST_AMP, 0x2f }, ++ { SAA7127_REG_SUBC3, 0xcb }, ++ { SAA7127_REG_SUBC2, 0x8a }, ++ { SAA7127_REG_SUBC1, 0x09 }, ++ { SAA7127_REG_SUBC0, 0x2a }, ++ { SAA7127_REG_MULTI, 0xa0 }, ++ { SAA7127_REG_CLOSED_CAPTION, 0x00 }, ++ { 0, 0 } ++}; ++ ++#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 ++static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { ++ { SAA7127_REG_BURST_START, 0x21 }, ++ /* BURST_END is also used as a chip ID in saa7127_probe */ ++ { SAA7127_REG_BURST_END, 0x1d }, ++ { SAA7127_REG_CHROMA_PHASE, 0x3f }, ++ { SAA7127_REG_GAINU, 0x6a }, ++ { SAA7127_REG_GAINV, 0x81 }, ++ { SAA7127_REG_BLACK_LEVEL, 0x33 }, ++ { SAA7127_REG_BLANKING_LEVEL, 0x35 }, ++ { SAA7127_REG_VBI_BLANKING, 0x35 }, ++ { SAA7127_REG_DAC_CONTROL, 0x08 }, ++ { SAA7127_REG_BURST_AMP, 0x2f }, ++ { SAA7127_REG_SUBC3, 0xb2 }, ++ { SAA7127_REG_SUBC2, 0x3b }, ++ { SAA7127_REG_SUBC1, 0xa3 }, ++ { SAA7127_REG_SUBC0, 0x28 }, ++ { SAA7127_REG_MULTI, 0x90 }, ++ { SAA7127_REG_CLOSED_CAPTION, 0x00 }, ++ { 0, 0 } ++}; ++ ++/* ++ ********************************************************************** ++ * ++ * Encoder Struct, holds the configuration state of the encoder ++ * ++ ********************************************************************** ++ */ ++ ++struct saa7127_state { ++ struct v4l2_subdev sd; ++ v4l2_std_id std; ++ u32 ident; ++ enum saa7127_input_type input_type; ++ enum saa7127_output_type output_type; ++ int video_enable; ++ int wss_enable; ++ u16 wss_mode; ++ int cc_enable; ++ u16 cc_data; ++ int xds_enable; ++ u16 xds_data; ++ int vps_enable; ++ u8 vps_data[5]; ++ u8 reg_2d; ++ u8 reg_3a; ++ u8 reg_3a_cb; /* colorbar bit */ ++ u8 reg_61; ++}; ++ ++static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa7127_state, sd); ++} ++ ++static const char * const output_strs[] = ++{ ++ "S-Video + Composite", ++ "Composite", ++ "S-Video", ++ "RGB", ++ "YUV C", ++ "YUV V" ++}; ++ ++static const char * const wss_strs[] = { ++ "invalid", ++ "letterbox 14:9 center", ++ "letterbox 14:9 top", ++ "invalid", ++ "letterbox 16:9 top", ++ "invalid", ++ "invalid", ++ "16:9 full format anamorphic", ++ "4:3 full format", ++ "invalid", ++ "invalid", ++ "letterbox 16:9 center", ++ "invalid", ++ "letterbox >16:9 center", ++ "14:9 full format center", ++ "invalid", ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int i; ++ ++ for (i = 0; i < 3; i++) { ++ if (i2c_smbus_write_byte_data(client, reg, val) == 0) ++ return 0; ++ } ++ v4l2_err(sd, "I2C Write Problem\n"); ++ return -1; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_write_inittab(struct v4l2_subdev *sd, ++ const struct i2c_reg_value *regs) ++{ ++ while (regs->reg != 0) { ++ saa7127_write(sd, regs->reg, regs->value); ++ regs++; ++ } ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) ++{ ++ struct saa7127_state *state = to_state(sd); ++ int enable = (data->line != 0); ++ ++ if (enable && (data->field != 0 || data->line != 16)) ++ return -EINVAL; ++ if (state->vps_enable != enable) { ++ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); ++ saa7127_write(sd, 0x54, enable << 7); ++ state->vps_enable = enable; ++ } ++ if (!enable) ++ return 0; ++ ++ state->vps_data[0] = data->data[2]; ++ state->vps_data[1] = data->data[8]; ++ state->vps_data[2] = data->data[9]; ++ state->vps_data[3] = data->data[10]; ++ state->vps_data[4] = data->data[11]; ++ v4l2_dbg(1, debug, sd, "Set VPS data %*ph\n", 5, state->vps_data); ++ saa7127_write(sd, 0x55, state->vps_data[0]); ++ saa7127_write(sd, 0x56, state->vps_data[1]); ++ saa7127_write(sd, 0x57, state->vps_data[2]); ++ saa7127_write(sd, 0x58, state->vps_data[3]); ++ saa7127_write(sd, 0x59, state->vps_data[4]); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) ++{ ++ struct saa7127_state *state = to_state(sd); ++ u16 cc = data->data[1] << 8 | data->data[0]; ++ int enable = (data->line != 0); ++ ++ if (enable && (data->field != 0 || data->line != 21)) ++ return -EINVAL; ++ if (state->cc_enable != enable) { ++ v4l2_dbg(1, debug, sd, ++ "Turn CC %s\n", enable ? "on" : "off"); ++ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, ++ (state->xds_enable << 7) | (enable << 6) | 0x11); ++ state->cc_enable = enable; ++ } ++ if (!enable) ++ return 0; ++ ++ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); ++ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); ++ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); ++ state->cc_data = cc; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) ++{ ++ struct saa7127_state *state = to_state(sd); ++ u16 xds = data->data[1] << 8 | data->data[0]; ++ int enable = (data->line != 0); ++ ++ if (enable && (data->field != 1 || data->line != 21)) ++ return -EINVAL; ++ if (state->xds_enable != enable) { ++ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); ++ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, ++ (enable << 7) | (state->cc_enable << 6) | 0x11); ++ state->xds_enable = enable; ++ } ++ if (!enable) ++ return 0; ++ ++ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); ++ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); ++ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); ++ state->xds_data = xds; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) ++{ ++ struct saa7127_state *state = to_state(sd); ++ int enable = (data->line != 0); ++ ++ if (enable && (data->field != 0 || data->line != 23)) ++ return -EINVAL; ++ if (state->wss_enable != enable) { ++ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); ++ saa7127_write(sd, 0x27, enable << 7); ++ state->wss_enable = enable; ++ } ++ if (!enable) ++ return 0; ++ ++ saa7127_write(sd, 0x26, data->data[0]); ++ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); ++ v4l2_dbg(1, debug, sd, ++ "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); ++ state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ if (enable) { ++ v4l2_dbg(1, debug, sd, "Enable Video Output\n"); ++ saa7127_write(sd, 0x2d, state->reg_2d); ++ saa7127_write(sd, 0x61, state->reg_61); ++ } else { ++ v4l2_dbg(1, debug, sd, "Disable Video Output\n"); ++ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); ++ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); ++ } ++ state->video_enable = enable; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa7127_state *state = to_state(sd); ++ const struct i2c_reg_value *inittab; ++ ++ if (std & V4L2_STD_525_60) { ++ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); ++ inittab = saa7127_init_config_60hz; ++ state->reg_61 = SAA7127_60HZ_DAC_CONTROL; ++ ++ } else if (state->ident == V4L2_IDENT_SAA7129 && ++ (std & V4L2_STD_SECAM) && ++ !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { ++ ++ /* If and only if SECAM, with a SAA712[89] */ ++ v4l2_dbg(1, debug, sd, ++ "Selecting 50 Hz SECAM video Standard\n"); ++ inittab = saa7127_init_config_50hz_secam; ++ state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; ++ ++ } else { ++ v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); ++ inittab = saa7127_init_config_50hz_pal; ++ state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; ++ } ++ ++ /* Write Table */ ++ saa7127_write_inittab(sd, inittab); ++ state->std = std; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ switch (output) { ++ case SAA7127_OUTPUT_TYPE_RGB: ++ state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ ++ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ ++ break; ++ ++ case SAA7127_OUTPUT_TYPE_COMPOSITE: ++ if (state->ident == V4L2_IDENT_SAA7129) ++ state->reg_2d = 0x20; /* CVBS only */ ++ else ++ state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ ++ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ ++ break; ++ ++ case SAA7127_OUTPUT_TYPE_SVIDEO: ++ if (state->ident == V4L2_IDENT_SAA7129) ++ state->reg_2d = 0x18; /* Y + C */ ++ else ++ state->reg_2d = 0xff; /*11111111 croma -> R, luma -> CVBS + G + B */ ++ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ ++ break; ++ ++ case SAA7127_OUTPUT_TYPE_YUV_V: ++ state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ ++ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ ++ break; ++ ++ case SAA7127_OUTPUT_TYPE_YUV_C: ++ state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ ++ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ ++ break; ++ ++ case SAA7127_OUTPUT_TYPE_BOTH: ++ if (state->ident == V4L2_IDENT_SAA7129) ++ state->reg_2d = 0x38; ++ else ++ state->reg_2d = 0xbf; ++ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ v4l2_dbg(1, debug, sd, ++ "Selecting %s output type\n", output_strs[output]); ++ ++ /* Configure Encoder */ ++ saa7127_write(sd, 0x2d, state->reg_2d); ++ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); ++ state->output_type = output; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ switch (input) { ++ case SAA7127_INPUT_TYPE_NORMAL: /* avia */ ++ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); ++ state->reg_3a_cb = 0; ++ break; ++ ++ case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ ++ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); ++ state->reg_3a_cb = 0x80; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); ++ state->input_type = input; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ if (state->std == std) ++ return 0; ++ return saa7127_set_std(sd, std); ++} ++ ++static int saa7127_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa7127_state *state = to_state(sd); ++ int rc = 0; ++ ++ if (state->input_type != input) ++ rc = saa7127_set_input_type(sd, input); ++ if (rc == 0 && state->output_type != output) ++ rc = saa7127_set_output_type(sd, output); ++ return rc; ++} ++ ++static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ if (state->video_enable == enable) ++ return 0; ++ return saa7127_set_video_enable(sd, enable); ++} ++ ++static int saa7127_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ memset(fmt->service_lines, 0, sizeof(fmt->service_lines)); ++ if (state->vps_enable) ++ fmt->service_lines[0][16] = V4L2_SLICED_VPS; ++ if (state->wss_enable) ++ fmt->service_lines[0][23] = V4L2_SLICED_WSS_625; ++ if (state->cc_enable) { ++ fmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; ++ fmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; ++ } ++ fmt->service_set = ++ (state->vps_enable ? V4L2_SLICED_VPS : 0) | ++ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | ++ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); ++ return 0; ++} ++ ++static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) ++{ ++ switch (data->id) { ++ case V4L2_SLICED_WSS_625: ++ return saa7127_set_wss(sd, data); ++ case V4L2_SLICED_VPS: ++ return saa7127_set_vps(sd, data); ++ case V4L2_SLICED_CAPTION_525: ++ if (data->field == 0) ++ return saa7127_set_cc(sd, data); ++ return saa7127_set_xds(sd, data); ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = saa7127_read(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct saa7127_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); ++} ++ ++static int saa7127_log_status(struct v4l2_subdev *sd) ++{ ++ struct saa7127_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); ++ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); ++ v4l2_info(sd, "Output: %s\n", state->video_enable ? ++ output_strs[state->output_type] : "disabled"); ++ v4l2_info(sd, "WSS: %s\n", state->wss_enable ? ++ wss_strs[state->wss_mode] : "disabled"); ++ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); ++ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops saa7127_core_ops = { ++ .log_status = saa7127_log_status, ++ .g_chip_ident = saa7127_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = saa7127_g_register, ++ .s_register = saa7127_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops saa7127_video_ops = { ++ .s_std_output = saa7127_s_std_output, ++ .s_routing = saa7127_s_routing, ++ .s_stream = saa7127_s_stream, ++}; ++ ++static const struct v4l2_subdev_vbi_ops saa7127_vbi_ops = { ++ .s_vbi_data = saa7127_s_vbi_data, ++ .g_sliced_fmt = saa7127_g_sliced_fmt, ++}; ++ ++static const struct v4l2_subdev_ops saa7127_ops = { ++ .core = &saa7127_core_ops, ++ .video = &saa7127_video_ops, ++ .vbi = &saa7127_vbi_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct saa7127_state *state; ++ struct v4l2_subdev *sd; ++ struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", ++ client->addr << 1); ++ ++ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa7127_ops); ++ ++ /* First test register 0: Bits 5-7 are a version ID (should be 0), ++ and bit 2 should also be 0. ++ This is rather general, so the second test is more specific and ++ looks at the 'ending point of burst in clock cycles' which is ++ 0x1d after a reset and not expected to ever change. */ ++ if ((saa7127_read(sd, 0) & 0xe4) != 0 || ++ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { ++ v4l2_dbg(1, debug, sd, "saa7127 not found\n"); ++ kfree(state); ++ return -ENODEV; ++ } ++ ++ if (id->driver_data) { /* Chip type is already known */ ++ state->ident = id->driver_data; ++ } else { /* Needs detection */ ++ int read_result; ++ ++ /* Detect if it's an saa7129 */ ++ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); ++ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); ++ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { ++ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, ++ read_result); ++ state->ident = V4L2_IDENT_SAA7129; ++ strlcpy(client->name, "saa7129", I2C_NAME_SIZE); ++ } else { ++ state->ident = V4L2_IDENT_SAA7127; ++ strlcpy(client->name, "saa7127", I2C_NAME_SIZE); ++ } ++ } ++ ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, ++ client->addr << 1, client->adapter->name); ++ ++ v4l2_dbg(1, debug, sd, "Configuring encoder\n"); ++ saa7127_write_inittab(sd, saa7127_init_config_common); ++ saa7127_set_std(sd, V4L2_STD_NTSC); ++ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); ++ saa7127_set_vps(sd, &vbi); ++ saa7127_set_wss(sd, &vbi); ++ saa7127_set_cc(sd, &vbi); ++ saa7127_set_xds(sd, &vbi); ++ if (test_image == 1) ++ /* The Encoder has an internal Colorbar generator */ ++ /* This can be used for debugging */ ++ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); ++ else ++ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); ++ saa7127_set_video_enable(sd, 1); ++ ++ if (state->ident == V4L2_IDENT_SAA7129) ++ saa7127_write_inittab(sd, saa7129_init_config_extra); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7127_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ /* Turn off TV output */ ++ saa7127_set_video_enable(sd, 0); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct i2c_device_id saa7127_id[] = { ++ { "saa7127_auto", 0 }, /* auto-detection */ ++ { "saa7126", V4L2_IDENT_SAA7127 }, ++ { "saa7127", V4L2_IDENT_SAA7127 }, ++ { "saa7128", V4L2_IDENT_SAA7129 }, ++ { "saa7129", V4L2_IDENT_SAA7129 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa7127_id); ++ ++static struct i2c_driver saa7127_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa7127", ++ }, ++ .probe = saa7127_probe, ++ .remove = saa7127_remove, ++ .id_table = saa7127_id, ++}; ++ ++module_i2c_driver(saa7127_driver); +diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c +new file mode 100644 +index 0000000..1e84466 +--- /dev/null ++++ b/drivers/media/i2c/saa717x.c +@@ -0,0 +1,1378 @@ ++/* ++ * saa717x - Philips SAA717xHL video decoder driver ++ * ++ * Based on the saa7115 driver ++ * ++ * Changes by Ohta Kyuma ++ * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) ++ * ++ * Changes by T.Adachi (tadachi@tadachi-net.com) ++ * - support audio, video scaler etc, and checked the initialize sequence. ++ * ++ * Cleaned up by Hans Verkuil ++ * ++ * Note: this is a reversed engineered driver based on captures from ++ * the I2C bus under Windows. This chip is very similar to the saa7134, ++ * though. Unfortunately, this driver is currently only working for NTSC. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); ++MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++/* ++ * Generic i2c probe ++ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' ++ */ ++ ++struct saa717x_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ v4l2_std_id std; ++ int input; ++ int enable; ++ int radio; ++ int playback; ++ int audio; ++ int tuner_audio_mode; ++ int audio_main_mute; ++ int audio_main_vol_r; ++ int audio_main_vol_l; ++ u16 audio_main_bass; ++ u16 audio_main_treble; ++ u16 audio_main_volume; ++ u16 audio_main_balance; ++ int audio_input; ++}; ++ ++static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa717x_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct saa717x_state, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* for audio mode */ ++#define TUNER_AUDIO_MONO 0 /* LL */ ++#define TUNER_AUDIO_STEREO 1 /* LR */ ++#define TUNER_AUDIO_LANG1 2 /* LL */ ++#define TUNER_AUDIO_LANG2 3 /* RR */ ++ ++#define SAA717X_NTSC_WIDTH (704) ++#define SAA717X_NTSC_HEIGHT (480) ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct i2c_adapter *adap = client->adapter; ++ int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; ++ unsigned char mm1[6]; ++ struct i2c_msg msg; ++ ++ msg.flags = 0; ++ msg.addr = client->addr; ++ mm1[0] = (reg >> 8) & 0xff; ++ mm1[1] = reg & 0xff; ++ ++ if (fw_addr) { ++ mm1[4] = (value >> 16) & 0xff; ++ mm1[3] = (value >> 8) & 0xff; ++ mm1[2] = value & 0xff; ++ } else { ++ mm1[2] = value & 0xff; ++ } ++ msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ ++ msg.buf = mm1; ++ v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); ++ return i2c_transfer(adap, &msg, 1) == 1; ++} ++ ++static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) ++{ ++ while (data[0] || data[1]) { ++ saa717x_write(sd, data[0], data[1]); ++ data += 2; ++ } ++} ++ ++static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct i2c_adapter *adap = client->adapter; ++ int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; ++ unsigned char mm1[2]; ++ unsigned char mm2[4] = { 0, 0, 0, 0 }; ++ struct i2c_msg msgs[2]; ++ u32 value; ++ ++ msgs[0].flags = 0; ++ msgs[1].flags = I2C_M_RD; ++ msgs[0].addr = msgs[1].addr = client->addr; ++ mm1[0] = (reg >> 8) & 0xff; ++ mm1[1] = reg & 0xff; ++ msgs[0].len = 2; ++ msgs[0].buf = mm1; ++ msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ ++ msgs[1].buf = mm2; ++ i2c_transfer(adap, msgs, 2); ++ ++ if (fw_addr) ++ value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); ++ else ++ value = mm2[0] & 0xff; ++ ++ v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); ++ return value; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static u32 reg_init_initialize[] = ++{ ++ /* from linux driver */ ++ 0x101, 0x008, /* Increment delay */ ++ ++ 0x103, 0x000, /* Analog input control 2 */ ++ 0x104, 0x090, /* Analog input control 3 */ ++ 0x105, 0x090, /* Analog input control 4 */ ++ 0x106, 0x0eb, /* Horizontal sync start */ ++ 0x107, 0x0e0, /* Horizontal sync stop */ ++ 0x109, 0x055, /* Luminance control */ ++ ++ 0x10f, 0x02a, /* Chroma gain control */ ++ 0x110, 0x000, /* Chroma control 2 */ ++ ++ 0x114, 0x045, /* analog/ADC */ ++ ++ 0x118, 0x040, /* RAW data gain */ ++ 0x119, 0x080, /* RAW data offset */ ++ ++ 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ ++ 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ ++ 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ ++ 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ ++ ++ 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ ++ ++ 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ ++ 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ ++ ++ 0x064, 0x080, /* Lumina brightness TASK A */ ++ 0x065, 0x040, /* Luminance contrast TASK A */ ++ 0x066, 0x040, /* Chroma saturation TASK A */ ++ /* 067H: Reserved */ ++ 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ ++ 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ ++ 0x06a, 0x000, /* VBI phase offset TASK A */ ++ ++ 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ ++ 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ ++ ++ 0x072, 0x000, /* Vertical filter mode TASK A */ ++ ++ 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ ++ 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ ++ 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ ++ 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ ++ ++ 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ ++ ++ 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ ++ 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ ++ ++ 0x0a4, 0x080, /* Lumina brightness TASK B */ ++ 0x0a5, 0x040, /* Luminance contrast TASK B */ ++ 0x0a6, 0x040, /* Chroma saturation TASK B */ ++ /* 0A7H reserved */ ++ 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ ++ 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ ++ 0x0aa, 0x000, /* VBI phase offset TASK B */ ++ ++ 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ ++ 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ ++ ++ 0x0b2, 0x000, /* Vertical filter mode TASK B */ ++ ++ 0x00c, 0x000, /* Start point GREEN path */ ++ 0x00d, 0x000, /* Start point BLUE path */ ++ 0x00e, 0x000, /* Start point RED path */ ++ ++ 0x010, 0x010, /* GREEN path gamma curve --- */ ++ 0x011, 0x020, ++ 0x012, 0x030, ++ 0x013, 0x040, ++ 0x014, 0x050, ++ 0x015, 0x060, ++ 0x016, 0x070, ++ 0x017, 0x080, ++ 0x018, 0x090, ++ 0x019, 0x0a0, ++ 0x01a, 0x0b0, ++ 0x01b, 0x0c0, ++ 0x01c, 0x0d0, ++ 0x01d, 0x0e0, ++ 0x01e, 0x0f0, ++ 0x01f, 0x0ff, /* --- GREEN path gamma curve */ ++ ++ 0x020, 0x010, /* BLUE path gamma curve --- */ ++ 0x021, 0x020, ++ 0x022, 0x030, ++ 0x023, 0x040, ++ 0x024, 0x050, ++ 0x025, 0x060, ++ 0x026, 0x070, ++ 0x027, 0x080, ++ 0x028, 0x090, ++ 0x029, 0x0a0, ++ 0x02a, 0x0b0, ++ 0x02b, 0x0c0, ++ 0x02c, 0x0d0, ++ 0x02d, 0x0e0, ++ 0x02e, 0x0f0, ++ 0x02f, 0x0ff, /* --- BLUE path gamma curve */ ++ ++ 0x030, 0x010, /* RED path gamma curve --- */ ++ 0x031, 0x020, ++ 0x032, 0x030, ++ 0x033, 0x040, ++ 0x034, 0x050, ++ 0x035, 0x060, ++ 0x036, 0x070, ++ 0x037, 0x080, ++ 0x038, 0x090, ++ 0x039, 0x0a0, ++ 0x03a, 0x0b0, ++ 0x03b, 0x0c0, ++ 0x03c, 0x0d0, ++ 0x03d, 0x0e0, ++ 0x03e, 0x0f0, ++ 0x03f, 0x0ff, /* --- RED path gamma curve */ ++ ++ 0x109, 0x085, /* Luminance control */ ++ ++ /**** from app start ****/ ++ 0x584, 0x000, /* AGC gain control */ ++ 0x585, 0x000, /* Program count */ ++ 0x586, 0x003, /* Status reset */ ++ 0x588, 0x0ff, /* Number of audio samples (L) */ ++ 0x589, 0x00f, /* Number of audio samples (M) */ ++ 0x58a, 0x000, /* Number of audio samples (H) */ ++ 0x58b, 0x000, /* Audio select */ ++ 0x58c, 0x010, /* Audio channel assign1 */ ++ 0x58d, 0x032, /* Audio channel assign2 */ ++ 0x58e, 0x054, /* Audio channel assign3 */ ++ 0x58f, 0x023, /* Audio format */ ++ 0x590, 0x000, /* SIF control */ ++ ++ 0x595, 0x000, /* ?? */ ++ 0x596, 0x000, /* ?? */ ++ 0x597, 0x000, /* ?? */ ++ ++ 0x464, 0x00, /* Digital input crossbar1 */ ++ ++ 0x46c, 0xbbbb10, /* Digital output selection1-3 */ ++ 0x470, 0x101010, /* Digital output selection4-6 */ ++ ++ 0x478, 0x00, /* Sound feature control */ ++ ++ 0x474, 0x18, /* Softmute control */ ++ ++ 0x454, 0x0425b9, /* Sound Easy programming(reset) */ ++ 0x454, 0x042539, /* Sound Easy programming(reset) */ ++ ++ ++ /**** common setting( of DVD play, including scaler commands) ****/ ++ 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ ++ ++ 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ ++ ++ 0x108, 0x0f8, /* Sync control */ ++ 0x2a9, 0x0fd, /* ??? */ ++ 0x102, 0x089, /* select video input "mode 9" */ ++ 0x111, 0x000, /* Mode/delay control */ ++ ++ 0x10e, 0x00a, /* Chroma control 1 */ ++ ++ 0x594, 0x002, /* SIF, analog I/O select */ ++ ++ 0x454, 0x0425b9, /* Sound */ ++ 0x454, 0x042539, ++ ++ 0x111, 0x000, ++ 0x10e, 0x00a, ++ 0x464, 0x000, ++ 0x300, 0x000, ++ 0x301, 0x006, ++ 0x302, 0x000, ++ 0x303, 0x006, ++ 0x308, 0x040, ++ 0x309, 0x000, ++ 0x30a, 0x000, ++ 0x30b, 0x000, ++ 0x000, 0x002, ++ 0x001, 0x000, ++ 0x002, 0x000, ++ 0x003, 0x000, ++ 0x004, 0x033, ++ 0x040, 0x01d, ++ 0x041, 0x001, ++ 0x042, 0x004, ++ 0x043, 0x000, ++ 0x080, 0x01e, ++ 0x081, 0x001, ++ 0x082, 0x004, ++ 0x083, 0x000, ++ 0x190, 0x018, ++ 0x115, 0x000, ++ 0x116, 0x012, ++ 0x117, 0x018, ++ 0x04a, 0x011, ++ 0x08a, 0x011, ++ 0x04b, 0x000, ++ 0x08b, 0x000, ++ 0x048, 0x000, ++ 0x088, 0x000, ++ 0x04e, 0x012, ++ 0x08e, 0x012, ++ 0x058, 0x012, ++ 0x098, 0x012, ++ 0x059, 0x000, ++ 0x099, 0x000, ++ 0x05a, 0x003, ++ 0x09a, 0x003, ++ 0x05b, 0x001, ++ 0x09b, 0x001, ++ 0x054, 0x008, ++ 0x094, 0x008, ++ 0x055, 0x000, ++ 0x095, 0x000, ++ 0x056, 0x0c7, ++ 0x096, 0x0c7, ++ 0x057, 0x002, ++ 0x097, 0x002, ++ 0x0ff, 0x0ff, ++ 0x060, 0x001, ++ 0x0a0, 0x001, ++ 0x061, 0x000, ++ 0x0a1, 0x000, ++ 0x062, 0x000, ++ 0x0a2, 0x000, ++ 0x063, 0x000, ++ 0x0a3, 0x000, ++ 0x070, 0x000, ++ 0x0b0, 0x000, ++ 0x071, 0x004, ++ 0x0b1, 0x004, ++ 0x06c, 0x0e9, ++ 0x0ac, 0x0e9, ++ 0x06d, 0x003, ++ 0x0ad, 0x003, ++ 0x05c, 0x0d0, ++ 0x09c, 0x0d0, ++ 0x05d, 0x002, ++ 0x09d, 0x002, ++ 0x05e, 0x0f2, ++ 0x09e, 0x0f2, ++ 0x05f, 0x000, ++ 0x09f, 0x000, ++ 0x074, 0x000, ++ 0x0b4, 0x000, ++ 0x075, 0x000, ++ 0x0b5, 0x000, ++ 0x076, 0x000, ++ 0x0b6, 0x000, ++ 0x077, 0x000, ++ 0x0b7, 0x000, ++ 0x195, 0x008, ++ 0x0ff, 0x0ff, ++ 0x108, 0x0f8, ++ 0x111, 0x000, ++ 0x10e, 0x00a, ++ 0x2a9, 0x0fd, ++ 0x464, 0x001, ++ 0x454, 0x042135, ++ 0x598, 0x0e7, ++ 0x599, 0x07d, ++ 0x59a, 0x018, ++ 0x59c, 0x066, ++ 0x59d, 0x090, ++ 0x59e, 0x001, ++ 0x584, 0x000, ++ 0x585, 0x000, ++ 0x586, 0x003, ++ 0x588, 0x0ff, ++ 0x589, 0x00f, ++ 0x58a, 0x000, ++ 0x58b, 0x000, ++ 0x58c, 0x010, ++ 0x58d, 0x032, ++ 0x58e, 0x054, ++ 0x58f, 0x023, ++ 0x590, 0x000, ++ 0x595, 0x000, ++ 0x596, 0x000, ++ 0x597, 0x000, ++ 0x464, 0x000, ++ 0x46c, 0xbbbb10, ++ 0x470, 0x101010, ++ ++ ++ 0x478, 0x000, ++ 0x474, 0x018, ++ 0x454, 0x042135, ++ 0x598, 0x0e7, ++ 0x599, 0x07d, ++ 0x59a, 0x018, ++ 0x59c, 0x066, ++ 0x59d, 0x090, ++ 0x59e, 0x001, ++ 0x584, 0x000, ++ 0x585, 0x000, ++ 0x586, 0x003, ++ 0x588, 0x0ff, ++ 0x589, 0x00f, ++ 0x58a, 0x000, ++ 0x58b, 0x000, ++ 0x58c, 0x010, ++ 0x58d, 0x032, ++ 0x58e, 0x054, ++ 0x58f, 0x023, ++ 0x590, 0x000, ++ 0x595, 0x000, ++ 0x596, 0x000, ++ 0x597, 0x000, ++ 0x464, 0x000, ++ 0x46c, 0xbbbb10, ++ 0x470, 0x101010, ++ ++ 0x478, 0x000, ++ 0x474, 0x018, ++ 0x454, 0x042135, ++ 0x598, 0x0e7, ++ 0x599, 0x07d, ++ 0x59a, 0x018, ++ 0x59c, 0x066, ++ 0x59d, 0x090, ++ 0x59e, 0x001, ++ 0x584, 0x000, ++ 0x585, 0x000, ++ 0x586, 0x003, ++ 0x588, 0x0ff, ++ 0x589, 0x00f, ++ 0x58a, 0x000, ++ 0x58b, 0x000, ++ 0x58c, 0x010, ++ 0x58d, 0x032, ++ 0x58e, 0x054, ++ 0x58f, 0x023, ++ 0x590, 0x000, ++ 0x595, 0x000, ++ 0x596, 0x000, ++ 0x597, 0x000, ++ 0x464, 0x000, ++ 0x46c, 0xbbbb10, ++ 0x470, 0x101010, ++ 0x478, 0x000, ++ 0x474, 0x018, ++ 0x454, 0x042135, ++ 0x193, 0x000, ++ 0x300, 0x000, ++ 0x301, 0x006, ++ 0x302, 0x000, ++ 0x303, 0x006, ++ 0x308, 0x040, ++ 0x309, 0x000, ++ 0x30a, 0x000, ++ 0x30b, 0x000, ++ 0x000, 0x002, ++ 0x001, 0x000, ++ 0x002, 0x000, ++ 0x003, 0x000, ++ 0x004, 0x033, ++ 0x040, 0x01d, ++ 0x041, 0x001, ++ 0x042, 0x004, ++ 0x043, 0x000, ++ 0x080, 0x01e, ++ 0x081, 0x001, ++ 0x082, 0x004, ++ 0x083, 0x000, ++ 0x190, 0x018, ++ 0x115, 0x000, ++ 0x116, 0x012, ++ 0x117, 0x018, ++ 0x04a, 0x011, ++ 0x08a, 0x011, ++ 0x04b, 0x000, ++ 0x08b, 0x000, ++ 0x048, 0x000, ++ 0x088, 0x000, ++ 0x04e, 0x012, ++ 0x08e, 0x012, ++ 0x058, 0x012, ++ 0x098, 0x012, ++ 0x059, 0x000, ++ 0x099, 0x000, ++ 0x05a, 0x003, ++ 0x09a, 0x003, ++ 0x05b, 0x001, ++ 0x09b, 0x001, ++ 0x054, 0x008, ++ 0x094, 0x008, ++ 0x055, 0x000, ++ 0x095, 0x000, ++ 0x056, 0x0c7, ++ 0x096, 0x0c7, ++ 0x057, 0x002, ++ 0x097, 0x002, ++ 0x060, 0x001, ++ 0x0a0, 0x001, ++ 0x061, 0x000, ++ 0x0a1, 0x000, ++ 0x062, 0x000, ++ 0x0a2, 0x000, ++ 0x063, 0x000, ++ 0x0a3, 0x000, ++ 0x070, 0x000, ++ 0x0b0, 0x000, ++ 0x071, 0x004, ++ 0x0b1, 0x004, ++ 0x06c, 0x0e9, ++ 0x0ac, 0x0e9, ++ 0x06d, 0x003, ++ 0x0ad, 0x003, ++ 0x05c, 0x0d0, ++ 0x09c, 0x0d0, ++ 0x05d, 0x002, ++ 0x09d, 0x002, ++ 0x05e, 0x0f2, ++ 0x09e, 0x0f2, ++ 0x05f, 0x000, ++ 0x09f, 0x000, ++ 0x074, 0x000, ++ 0x0b4, 0x000, ++ 0x075, 0x000, ++ 0x0b5, 0x000, ++ 0x076, 0x000, ++ 0x0b6, 0x000, ++ 0x077, 0x000, ++ 0x0b7, 0x000, ++ 0x195, 0x008, ++ 0x598, 0x0e7, ++ 0x599, 0x07d, ++ 0x59a, 0x018, ++ 0x59c, 0x066, ++ 0x59d, 0x090, ++ 0x59e, 0x001, ++ 0x584, 0x000, ++ 0x585, 0x000, ++ 0x586, 0x003, ++ 0x588, 0x0ff, ++ 0x589, 0x00f, ++ 0x58a, 0x000, ++ 0x58b, 0x000, ++ 0x58c, 0x010, ++ 0x58d, 0x032, ++ 0x58e, 0x054, ++ 0x58f, 0x023, ++ 0x590, 0x000, ++ 0x595, 0x000, ++ 0x596, 0x000, ++ 0x597, 0x000, ++ 0x464, 0x000, ++ 0x46c, 0xbbbb10, ++ 0x470, 0x101010, ++ 0x478, 0x000, ++ 0x474, 0x018, ++ 0x454, 0x042135, ++ 0x193, 0x0a6, ++ 0x108, 0x0f8, ++ 0x042, 0x003, ++ 0x082, 0x003, ++ 0x454, 0x0425b9, ++ 0x454, 0x042539, ++ 0x193, 0x000, ++ 0x193, 0x0a6, ++ 0x464, 0x000, ++ ++ 0, 0 ++}; ++ ++/* Tuner */ ++static u32 reg_init_tuner_input[] = { ++ 0x108, 0x0f8, /* Sync control */ ++ 0x111, 0x000, /* Mode/delay control */ ++ 0x10e, 0x00a, /* Chroma control 1 */ ++ 0, 0 ++}; ++ ++/* Composite */ ++static u32 reg_init_composite_input[] = { ++ 0x108, 0x0e8, /* Sync control */ ++ 0x111, 0x000, /* Mode/delay control */ ++ 0x10e, 0x04a, /* Chroma control 1 */ ++ 0, 0 ++}; ++ ++/* S-Video */ ++static u32 reg_init_svideo_input[] = { ++ 0x108, 0x0e8, /* Sync control */ ++ 0x111, 0x000, /* Mode/delay control */ ++ 0x10e, 0x04a, /* Chroma control 1 */ ++ 0, 0 ++}; ++ ++static u32 reg_set_audio_template[4][2] = ++{ ++ { /* for MONO ++ tadachi 6/29 DMA audio output select? ++ Register 0x46c ++ 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 ++ 0: MAIN left, 1: MAIN right ++ 2: AUX1 left, 3: AUX1 right ++ 4: AUX2 left, 5: AUX2 right ++ 6: DPL left, 7: DPL right ++ 8: DPL center, 9: DPL surround ++ A: monitor output, B: digital sense */ ++ 0xbbbb00, ++ ++ /* tadachi 6/29 DAC and I2S output select? ++ Register 0x470 ++ 7-4:DAC right ch. 3-0:DAC left ch. ++ I2S1 right,left I2S2 right,left */ ++ 0x00, ++ }, ++ { /* for STEREO */ ++ 0xbbbb10, 0x101010, ++ }, ++ { /* for LANG1 */ ++ 0xbbbb00, 0x00, ++ }, ++ { /* for LANG2/SAP */ ++ 0xbbbb11, 0x111111, ++ } ++}; ++ ++ ++/* Get detected audio flags (from saa7134 driver) */ ++static void get_inf_dev_status(struct v4l2_subdev *sd, ++ int *dual_flag, int *stereo_flag) ++{ ++ u32 reg_data3; ++ ++ static char *stdres[0x20] = { ++ [0x00] = "no standard detected", ++ [0x01] = "B/G (in progress)", ++ [0x02] = "D/K (in progress)", ++ [0x03] = "M (in progress)", ++ ++ [0x04] = "B/G A2", ++ [0x05] = "B/G NICAM", ++ [0x06] = "D/K A2 (1)", ++ [0x07] = "D/K A2 (2)", ++ [0x08] = "D/K A2 (3)", ++ [0x09] = "D/K NICAM", ++ [0x0a] = "L NICAM", ++ [0x0b] = "I NICAM", ++ ++ [0x0c] = "M Korea", ++ [0x0d] = "M BTSC ", ++ [0x0e] = "M EIAJ", ++ ++ [0x0f] = "FM radio / IF 10.7 / 50 deemp", ++ [0x10] = "FM radio / IF 10.7 / 75 deemp", ++ [0x11] = "FM radio / IF sel / 50 deemp", ++ [0x12] = "FM radio / IF sel / 75 deemp", ++ ++ [0x13 ... 0x1e] = "unknown", ++ [0x1f] = "??? [in progress]", ++ }; ++ ++ ++ *dual_flag = *stereo_flag = 0; ++ ++ /* (demdec status: 0x528) */ ++ ++ /* read current status */ ++ reg_data3 = saa717x_read(sd, 0x0528); ++ ++ v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", ++ reg_data3, stdres[reg_data3 & 0x1f], ++ (reg_data3 & 0x000020) ? ",stereo" : "", ++ (reg_data3 & 0x000040) ? ",dual" : ""); ++ v4l2_dbg(1, debug, sd, "detailed status: " ++ "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", ++ (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", ++ (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", ++ (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", ++ (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", ++ ++ (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", ++ (reg_data3 & 0x001000) ? " SAP carrier " : "", ++ (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", ++ (reg_data3 & 0x004000) ? " SAP noise mute " : "", ++ (reg_data3 & 0x008000) ? " VDSP " : "", ++ ++ (reg_data3 & 0x010000) ? " NICST " : "", ++ (reg_data3 & 0x020000) ? " NICDU " : "", ++ (reg_data3 & 0x040000) ? " NICAM muted " : "", ++ (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", ++ ++ (reg_data3 & 0x100000) ? " init done " : ""); ++ ++ if (reg_data3 & 0x000220) { ++ v4l2_dbg(1, debug, sd, "ST!!!\n"); ++ *stereo_flag = 1; ++ } ++ ++ if (reg_data3 & 0x000140) { ++ v4l2_dbg(1, debug, sd, "DUAL!!!\n"); ++ *dual_flag = 1; ++ } ++} ++ ++/* regs write to set audio mode */ ++static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) ++{ ++ v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", ++ audio_mode); ++ ++ saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); ++ saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); ++} ++ ++/* write regs to set audio volume, bass and treble */ ++static int set_audio_regs(struct v4l2_subdev *sd, ++ struct saa717x_state *decoder) ++{ ++ u8 mute = 0xac; /* -84 dB */ ++ u32 val; ++ unsigned int work_l, work_r; ++ ++ /* set SIF analog I/O select */ ++ saa717x_write(sd, 0x0594, decoder->audio_input); ++ v4l2_dbg(1, debug, sd, "set audio input %d\n", ++ decoder->audio_input); ++ ++ /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ ++ work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; ++ work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; ++ decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; ++ decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; ++ ++ /* set main volume */ ++ /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ ++ /* def:0dB->6dB(MPG600GR) */ ++ /* if mute is on, set mute */ ++ if (decoder->audio_main_mute) { ++ val = mute | (mute << 8); ++ } else { ++ val = (u8)decoder->audio_main_vol_l | ++ ((u8)decoder->audio_main_vol_r << 8); ++ } ++ ++ saa717x_write(sd, 0x480, val); ++ ++ /* set bass and treble */ ++ val = decoder->audio_main_bass & 0x1f; ++ val |= (decoder->audio_main_treble & 0x1f) << 5; ++ saa717x_write(sd, 0x488, val); ++ return 0; ++} ++ ++/********** scaling staff ***********/ ++static void set_h_prescale(struct v4l2_subdev *sd, ++ int task, int prescale) ++{ ++ static const struct { ++ int xpsc; ++ int xacl; ++ int xc2_1; ++ int xdcg; ++ int vpfy; ++ } vals[] = { ++ /* XPSC XACL XC2_1 XDCG VPFY */ ++ { 1, 0, 0, 0, 0 }, ++ { 2, 2, 1, 2, 2 }, ++ { 3, 4, 1, 3, 2 }, ++ { 4, 8, 1, 4, 2 }, ++ { 5, 8, 1, 4, 2 }, ++ { 6, 8, 1, 4, 3 }, ++ { 7, 8, 1, 4, 3 }, ++ { 8, 15, 0, 4, 3 }, ++ { 9, 15, 0, 4, 3 }, ++ { 10, 16, 1, 5, 3 }, ++ }; ++ static const int count = ARRAY_SIZE(vals); ++ int i, task_shift; ++ ++ task_shift = task * 0x40; ++ for (i = 0; i < count; i++) ++ if (vals[i].xpsc == prescale) ++ break; ++ if (i == count) ++ return; ++ ++ /* horizonal prescaling */ ++ saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); ++ /* accumulation length */ ++ saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); ++ /* level control */ ++ saa717x_write(sd, 0x62 + task_shift, ++ (vals[i].xc2_1 << 3) | vals[i].xdcg); ++ /*FIR prefilter control */ ++ saa717x_write(sd, 0x63 + task_shift, ++ (vals[i].vpfy << 2) | vals[i].vpfy); ++} ++ ++/********** scaling staff ***********/ ++static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) ++{ ++ int task_shift; ++ ++ task_shift = task * 0x40; ++ /* Vertical scaling ratio (LOW) */ ++ saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); ++ /* Vertical scaling ratio (HI) */ ++ saa717x_write(sd, 0x71 + task_shift, yscale >> 8); ++} ++ ++static int saa717x_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct saa717x_state *state = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ saa717x_write(sd, 0x10a, ctrl->val); ++ return 0; ++ ++ case V4L2_CID_CONTRAST: ++ saa717x_write(sd, 0x10b, ctrl->val); ++ return 0; ++ ++ case V4L2_CID_SATURATION: ++ saa717x_write(sd, 0x10c, ctrl->val); ++ return 0; ++ ++ case V4L2_CID_HUE: ++ saa717x_write(sd, 0x10d, ctrl->val); ++ return 0; ++ ++ case V4L2_CID_AUDIO_MUTE: ++ state->audio_main_mute = ctrl->val; ++ break; ++ ++ case V4L2_CID_AUDIO_VOLUME: ++ state->audio_main_volume = ctrl->val; ++ break; ++ ++ case V4L2_CID_AUDIO_BALANCE: ++ state->audio_main_balance = ctrl->val; ++ break; ++ ++ case V4L2_CID_AUDIO_TREBLE: ++ state->audio_main_treble = ctrl->val; ++ break; ++ ++ case V4L2_CID_AUDIO_BASS: ++ state->audio_main_bass = ctrl->val; ++ break; ++ ++ default: ++ return 0; ++ } ++ set_audio_regs(sd, state); ++ return 0; ++} ++ ++static int saa717x_s_video_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ int is_tuner = input & 0x80; /* tuner input flag */ ++ ++ input &= 0x7f; ++ ++ v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); ++ /* inputs from 0-9 are available*/ ++ /* saa717x have mode0-mode9 but mode5 is reserved. */ ++ if (input > 9 || input == 5) ++ return -EINVAL; ++ ++ if (decoder->input != input) { ++ int input_line = input; ++ ++ decoder->input = input_line; ++ v4l2_dbg(1, debug, sd, "now setting %s input %d\n", ++ input_line >= 6 ? "S-Video" : "Composite", ++ input_line); ++ ++ /* select mode */ ++ saa717x_write(sd, 0x102, ++ (saa717x_read(sd, 0x102) & 0xf0) | ++ input_line); ++ ++ /* bypass chrominance trap for modes 6..9 */ ++ saa717x_write(sd, 0x109, ++ (saa717x_read(sd, 0x109) & 0x7f) | ++ (input_line < 6 ? 0x0 : 0x80)); ++ ++ /* change audio_mode */ ++ if (is_tuner) { ++ /* tuner */ ++ set_audio_mode(sd, decoder->tuner_audio_mode); ++ } else { ++ /* Force to STEREO mode if Composite or ++ * S-Video were chosen */ ++ set_audio_mode(sd, TUNER_AUDIO_STEREO); ++ } ++ /* change initialize procedure (Composite/S-Video) */ ++ if (is_tuner) ++ saa717x_write_regs(sd, reg_init_tuner_input); ++ else if (input_line >= 6) ++ saa717x_write_regs(sd, reg_init_svideo_input); ++ else ++ saa717x_write_regs(sd, reg_init_composite_input); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = saa717x_read(sd, reg->reg); ++ reg->size = 1; ++ return 0; ++} ++ ++static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u16 addr = reg->reg & 0xffff; ++ u8 val = reg->val & 0xff; ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ saa717x_write(sd, addr, val); ++ return 0; ++} ++#endif ++ ++static int saa717x_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ int prescale, h_scale, v_scale; ++ ++ v4l2_dbg(1, debug, sd, "decoder set size\n"); ++ ++ if (fmt->code != V4L2_MBUS_FMT_FIXED) ++ return -EINVAL; ++ ++ /* FIXME need better bounds checking here */ ++ if (fmt->width < 1 || fmt->width > 1440) ++ return -EINVAL; ++ if (fmt->height < 1 || fmt->height > 960) ++ return -EINVAL; ++ ++ fmt->field = V4L2_FIELD_INTERLACED; ++ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ /* scaling setting */ ++ /* NTSC and interlace only */ ++ prescale = SAA717X_NTSC_WIDTH / fmt->width; ++ if (prescale == 0) ++ prescale = 1; ++ h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / fmt->width; ++ /* interlace */ ++ v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / fmt->height; ++ ++ /* Horizontal prescaling etc */ ++ set_h_prescale(sd, 0, prescale); ++ set_h_prescale(sd, 1, prescale); ++ ++ /* Horizontal scaling increment */ ++ /* TASK A */ ++ saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); ++ saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); ++ /* TASK B */ ++ saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); ++ saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); ++ ++ /* Vertical prescaling etc */ ++ set_v_scale(sd, 0, v_scale); ++ set_v_scale(sd, 1, v_scale); ++ ++ /* set video output size */ ++ /* video number of pixels at output */ ++ /* TASK A */ ++ saa717x_write(sd, 0x5C, (u8)(fmt->width & 0xFF)); ++ saa717x_write(sd, 0x5D, (u8)((fmt->width >> 8) & 0xFF)); ++ /* TASK B */ ++ saa717x_write(sd, 0x9C, (u8)(fmt->width & 0xFF)); ++ saa717x_write(sd, 0x9D, (u8)((fmt->width >> 8) & 0xFF)); ++ ++ /* video number of lines at output */ ++ /* TASK A */ ++ saa717x_write(sd, 0x5E, (u8)(fmt->height & 0xFF)); ++ saa717x_write(sd, 0x5F, (u8)((fmt->height >> 8) & 0xFF)); ++ /* TASK B */ ++ saa717x_write(sd, 0x9E, (u8)(fmt->height & 0xFF)); ++ saa717x_write(sd, 0x9F, (u8)((fmt->height >> 8) & 0xFF)); ++ return 0; ++} ++ ++static int saa717x_s_radio(struct v4l2_subdev *sd) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ ++ decoder->radio = 1; ++ return 0; ++} ++ ++static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "decoder set norm "); ++ v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); ++ ++ decoder->radio = 0; ++ decoder->std = std; ++ return 0; ++} ++ ++static int saa717x_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ ++ if (input < 3) { /* FIXME! --tadachi */ ++ decoder->audio_input = input; ++ v4l2_dbg(1, debug, sd, ++ "set decoder audio input to %d\n", ++ decoder->audio_input); ++ set_audio_regs(sd, decoder); ++ return 0; ++ } ++ return -ERANGE; ++} ++ ++static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ ++ v4l2_dbg(1, debug, sd, "decoder %s output\n", ++ enable ? "enable" : "disable"); ++ decoder->enable = enable; ++ saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); ++ return 0; ++} ++ ++/* change audio mode */ ++static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ int audio_mode; ++ char *mes[4] = { ++ "MONO", "STEREO", "LANG1", "LANG2/SAP" ++ }; ++ ++ audio_mode = TUNER_AUDIO_STEREO; ++ ++ switch (vt->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ audio_mode = TUNER_AUDIO_MONO; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ audio_mode = TUNER_AUDIO_STEREO; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ audio_mode = TUNER_AUDIO_LANG2; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ audio_mode = TUNER_AUDIO_LANG1; ++ break; ++ } ++ ++ v4l2_dbg(1, debug, sd, "change audio mode to %s\n", ++ mes[audio_mode]); ++ decoder->tuner_audio_mode = audio_mode; ++ /* The registers are not changed here. */ ++ /* See DECODER_ENABLE_OUTPUT section. */ ++ set_audio_mode(sd, decoder->tuner_audio_mode); ++ return 0; ++} ++ ++static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct saa717x_state *decoder = to_state(sd); ++ int dual_f, stereo_f; ++ ++ if (decoder->radio) ++ return 0; ++ get_inf_dev_status(sd, &dual_f, &stereo_f); ++ ++ v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", ++ stereo_f, dual_f); ++ ++ /* mono */ ++ if ((dual_f == 0) && (stereo_f == 0)) { ++ vt->rxsubchans = V4L2_TUNER_SUB_MONO; ++ v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); ++ } ++ ++ /* stereo */ ++ if (stereo_f == 1) { ++ if (vt->audmode == V4L2_TUNER_MODE_STEREO || ++ vt->audmode == V4L2_TUNER_MODE_LANG1) { ++ vt->rxsubchans = V4L2_TUNER_SUB_STEREO; ++ v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); ++ } else { ++ vt->rxsubchans = V4L2_TUNER_SUB_MONO; ++ v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); ++ } ++ } ++ ++ /* dual */ ++ if (dual_f == 1) { ++ if (vt->audmode == V4L2_TUNER_MODE_LANG2) { ++ vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; ++ v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); ++ } else { ++ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; ++ v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); ++ } ++ } ++ return 0; ++} ++ ++static int saa717x_log_status(struct v4l2_subdev *sd) ++{ ++ struct saa717x_state *state = to_state(sd); ++ ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops saa717x_ctrl_ops = { ++ .s_ctrl = saa717x_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops saa717x_core_ops = { ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = saa717x_g_register, ++ .s_register = saa717x_s_register, ++#endif ++ .s_std = saa717x_s_std, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .log_status = saa717x_log_status, ++}; ++ ++static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { ++ .g_tuner = saa717x_g_tuner, ++ .s_tuner = saa717x_s_tuner, ++ .s_radio = saa717x_s_radio, ++}; ++ ++static const struct v4l2_subdev_video_ops saa717x_video_ops = { ++ .s_routing = saa717x_s_video_routing, ++ .s_mbus_fmt = saa717x_s_mbus_fmt, ++ .s_stream = saa717x_s_stream, ++}; ++ ++static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { ++ .s_routing = saa717x_s_audio_routing, ++}; ++ ++static const struct v4l2_subdev_ops saa717x_ops = { ++ .core = &saa717x_core_ops, ++ .tuner = &saa717x_tuner_ops, ++ .audio = &saa717x_audio_ops, ++ .video = &saa717x_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++ ++/* i2c implementation */ ++ ++/* ----------------------------------------------------------------------- */ ++static int saa717x_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct saa717x_state *decoder; ++ struct v4l2_ctrl_handler *hdl; ++ struct v4l2_subdev *sd; ++ u8 id = 0; ++ char *p = ""; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); ++ if (decoder == NULL) ++ return -ENOMEM; ++ ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa717x_ops); ++ ++ if (saa717x_write(sd, 0x5a4, 0xfe) && ++ saa717x_write(sd, 0x5a5, 0x0f) && ++ saa717x_write(sd, 0x5a6, 0x00) && ++ saa717x_write(sd, 0x5a7, 0x01)) ++ id = saa717x_read(sd, 0x5a0); ++ if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { ++ v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); ++ kfree(decoder); ++ return -ENODEV; ++ } ++ if (id == 0xc2) ++ p = "saa7173"; ++ else if (id == 0x32) ++ p = "saa7174A"; ++ else if (id == 0x6c) ++ p = "saa7174HL"; ++ else ++ p = "saa7171"; ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, ++ client->addr << 1, client->adapter->name); ++ ++ hdl = &decoder->hdl; ++ v4l2_ctrl_handler_init(hdl, 9); ++ /* add in ascending ID order */ ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 68); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 255, 1, 64); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 42000); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_AUDIO_BASS, -16, 15, 1, 0); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_AUDIO_TREBLE, -16, 15, 1, 0); ++ v4l2_ctrl_new_std(hdl, &saa717x_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ int err = hdl->error; ++ ++ v4l2_ctrl_handler_free(hdl); ++ kfree(decoder); ++ return err; ++ } ++ ++ decoder->std = V4L2_STD_NTSC; ++ decoder->input = -1; ++ decoder->enable = 1; ++ ++ /* FIXME!! */ ++ decoder->playback = 0; /* initially capture mode used */ ++ decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ ++ ++ decoder->audio_input = 2; /* FIXME!! */ ++ ++ decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; ++ /* set volume, bass and treble */ ++ decoder->audio_main_vol_l = 6; ++ decoder->audio_main_vol_r = 6; ++ ++ v4l2_dbg(1, debug, sd, "writing init values\n"); ++ ++ /* FIXME!! */ ++ saa717x_write_regs(sd, reg_init_initialize); ++ ++ v4l2_ctrl_handler_setup(hdl); ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(2*HZ); ++ return 0; ++} ++ ++static int saa717x_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id saa717x_id[] = { ++ { "saa717x", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa717x_id); ++ ++static struct i2c_driver saa717x_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa717x", ++ }, ++ .probe = saa717x_probe, ++ .remove = saa717x_remove, ++ .id_table = saa717x_id, ++}; ++ ++module_i2c_driver(saa717x_driver); +diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c +new file mode 100644 +index 0000000..2c6b65c +--- /dev/null ++++ b/drivers/media/i2c/saa7185.c +@@ -0,0 +1,377 @@ ++/* ++ * saa7185 - Philips SAA7185B video encoder driver version 0.0.3 ++ * ++ * Copyright (C) 1998 Dave Perks ++ * ++ * Slight changes for video timing and attachment output by ++ * Wolfgang Scherr ++ * ++ * Changes by Ronald Bultje ++ * - moved over to linux>=2.4.x i2c protocol (1/1/2003) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); ++MODULE_AUTHOR("Dave Perks"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct saa7185 { ++ struct v4l2_subdev sd; ++ unsigned char reg[128]; ++ ++ v4l2_std_id norm; ++}; ++ ++static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa7185, sd); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int saa7185_read(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte(client); ++} ++ ++static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa7185 *encoder = to_saa7185(sd); ++ ++ v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); ++ encoder->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static int saa7185_write_block(struct v4l2_subdev *sd, ++ const u8 *data, unsigned int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa7185 *encoder = to_saa7185(sd); ++ int ret = -1; ++ u8 reg; ++ ++ /* the adv7175 has an autoincrement function, use it if ++ * the adapter understands raw I2C */ ++ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { ++ /* do raw I2C, not smbus compatible */ ++ u8 block_data[32]; ++ int block_len; ++ ++ while (len >= 2) { ++ block_len = 0; ++ block_data[block_len++] = reg = data[0]; ++ do { ++ block_data[block_len++] = ++ encoder->reg[reg++] = data[1]; ++ len -= 2; ++ data += 2; ++ } while (len >= 2 && data[0] == reg && block_len < 32); ++ ret = i2c_master_send(client, block_data, block_len); ++ if (ret < 0) ++ break; ++ } ++ } else { ++ /* do some slow I2C emulation kind of thing */ ++ while (len >= 2) { ++ reg = *data++; ++ ret = saa7185_write(sd, reg, *data++); ++ if (ret < 0) ++ break; ++ len -= 2; ++ } ++ } ++ ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const unsigned char init_common[] = { ++ 0x3a, 0x0f, /* CBENB=0, V656=0, VY2C=1, ++ * YUV2C=1, MY2C=1, MUV2C=1 */ ++ ++ 0x42, 0x6b, /* OVLY0=107 */ ++ 0x43, 0x00, /* OVLU0=0 white */ ++ 0x44, 0x00, /* OVLV0=0 */ ++ 0x45, 0x22, /* OVLY1=34 */ ++ 0x46, 0xac, /* OVLU1=172 yellow */ ++ 0x47, 0x0e, /* OVLV1=14 */ ++ 0x48, 0x03, /* OVLY2=3 */ ++ 0x49, 0x1d, /* OVLU2=29 cyan */ ++ 0x4a, 0xac, /* OVLV2=172 */ ++ 0x4b, 0xf0, /* OVLY3=240 */ ++ 0x4c, 0xc8, /* OVLU3=200 green */ ++ 0x4d, 0xb9, /* OVLV3=185 */ ++ 0x4e, 0xd4, /* OVLY4=212 */ ++ 0x4f, 0x38, /* OVLU4=56 magenta */ ++ 0x50, 0x47, /* OVLV4=71 */ ++ 0x51, 0xc1, /* OVLY5=193 */ ++ 0x52, 0xe3, /* OVLU5=227 red */ ++ 0x53, 0x54, /* OVLV5=84 */ ++ 0x54, 0xa3, /* OVLY6=163 */ ++ 0x55, 0x54, /* OVLU6=84 blue */ ++ 0x56, 0xf2, /* OVLV6=242 */ ++ 0x57, 0x90, /* OVLY7=144 */ ++ 0x58, 0x00, /* OVLU7=0 black */ ++ 0x59, 0x00, /* OVLV7=0 */ ++ ++ 0x5a, 0x00, /* CHPS=0 */ ++ 0x5b, 0x76, /* GAINU=118 */ ++ 0x5c, 0xa5, /* GAINV=165 */ ++ 0x5d, 0x3c, /* BLCKL=60 */ ++ 0x5e, 0x3a, /* BLNNL=58 */ ++ 0x5f, 0x3a, /* CCRS=0, BLNVB=58 */ ++ 0x60, 0x00, /* NULL */ ++ ++ /* 0x61 - 0x66 set according to norm */ ++ ++ 0x67, 0x00, /* 0 : caption 1st byte odd field */ ++ 0x68, 0x00, /* 0 : caption 2nd byte odd field */ ++ 0x69, 0x00, /* 0 : caption 1st byte even field */ ++ 0x6a, 0x00, /* 0 : caption 2nd byte even field */ ++ ++ 0x6b, 0x91, /* MODIN=2, PCREF=0, SCCLN=17 */ ++ 0x6c, 0x20, /* SRCV1=0, TRCV2=1, ORCV1=0, PRCV1=0, ++ * CBLF=0, ORCV2=0, PRCV2=0 */ ++ 0x6d, 0x00, /* SRCM1=0, CCEN=0 */ ++ ++ 0x6e, 0x0e, /* HTRIG=0x005, approx. centered, at ++ * least for PAL */ ++ 0x6f, 0x00, /* HTRIG upper bits */ ++ 0x70, 0x20, /* PHRES=0, SBLN=1, VTRIG=0 */ ++ ++ /* The following should not be needed */ ++ ++ 0x71, 0x15, /* BMRQ=0x115 */ ++ 0x72, 0x90, /* EMRQ=0x690 */ ++ 0x73, 0x61, /* EMRQ=0x690, BMRQ=0x115 */ ++ 0x74, 0x00, /* NULL */ ++ 0x75, 0x00, /* NULL */ ++ 0x76, 0x00, /* NULL */ ++ 0x77, 0x15, /* BRCV=0x115 */ ++ 0x78, 0x90, /* ERCV=0x690 */ ++ 0x79, 0x61, /* ERCV=0x690, BRCV=0x115 */ ++ ++ /* Field length controls */ ++ ++ 0x7a, 0x70, /* FLC=0 */ ++ ++ /* The following should not be needed if SBLN = 1 */ ++ ++ 0x7b, 0x16, /* FAL=22 */ ++ 0x7c, 0x35, /* LAL=244 */ ++ 0x7d, 0x20, /* LAL=244, FAL=22 */ ++}; ++ ++static const unsigned char init_pal[] = { ++ 0x61, 0x1e, /* FISE=0, PAL=1, SCBW=1, RTCE=1, ++ * YGS=1, INPI=0, DOWN=0 */ ++ 0x62, 0xc8, /* DECTYP=1, BSTA=72 */ ++ 0x63, 0xcb, /* FSC0 */ ++ 0x64, 0x8a, /* FSC1 */ ++ 0x65, 0x09, /* FSC2 */ ++ 0x66, 0x2a, /* FSC3 */ ++}; ++ ++static const unsigned char init_ntsc[] = { ++ 0x61, 0x1d, /* FISE=1, PAL=0, SCBW=1, RTCE=1, ++ * YGS=1, INPI=0, DOWN=0 */ ++ 0x62, 0xe6, /* DECTYP=1, BSTA=102 */ ++ 0x63, 0x1f, /* FSC0 */ ++ 0x64, 0x7c, /* FSC1 */ ++ 0x65, 0xf0, /* FSC2 */ ++ 0x66, 0x21, /* FSC3 */ ++}; ++ ++ ++static int saa7185_init(struct v4l2_subdev *sd, u32 val) ++{ ++ struct saa7185 *encoder = to_saa7185(sd); ++ ++ saa7185_write_block(sd, init_common, sizeof(init_common)); ++ if (encoder->norm & V4L2_STD_NTSC) ++ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); ++ else ++ saa7185_write_block(sd, init_pal, sizeof(init_pal)); ++ return 0; ++} ++ ++static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa7185 *encoder = to_saa7185(sd); ++ ++ if (std & V4L2_STD_NTSC) ++ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); ++ else if (std & V4L2_STD_PAL) ++ saa7185_write_block(sd, init_pal, sizeof(init_pal)); ++ else ++ return -EINVAL; ++ encoder->norm = std; ++ return 0; ++} ++ ++static int saa7185_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa7185 *encoder = to_saa7185(sd); ++ ++ /* RJ: input = 0: input is from SA7111 ++ input = 1: input is from ZR36060 */ ++ ++ switch (input) { ++ case 0: ++ /* turn off colorbar */ ++ saa7185_write(sd, 0x3a, 0x0f); ++ /* Switch RTCE to 1 */ ++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); ++ saa7185_write(sd, 0x6e, 0x01); ++ break; ++ ++ case 1: ++ /* turn off colorbar */ ++ saa7185_write(sd, 0x3a, 0x0f); ++ /* Switch RTCE to 0 */ ++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); ++ /* SW: a slight sync problem... */ ++ saa7185_write(sd, 0x6e, 0x00); ++ break; ++ ++ case 2: ++ /* turn on colorbar */ ++ saa7185_write(sd, 0x3a, 0x8f); ++ /* Switch RTCE to 0 */ ++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); ++ /* SW: a slight sync problem... */ ++ saa7185_write(sd, 0x6e, 0x01); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops saa7185_core_ops = { ++ .g_chip_ident = saa7185_g_chip_ident, ++ .init = saa7185_init, ++}; ++ ++static const struct v4l2_subdev_video_ops saa7185_video_ops = { ++ .s_std_output = saa7185_s_std_output, ++ .s_routing = saa7185_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops saa7185_ops = { ++ .core = &saa7185_core_ops, ++ .video = &saa7185_video_ops, ++}; ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int saa7185_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int i; ++ struct saa7185 *encoder; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); ++ if (encoder == NULL) ++ return -ENOMEM; ++ encoder->norm = V4L2_STD_NTSC; ++ sd = &encoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa7185_ops); ++ ++ i = saa7185_write_block(sd, init_common, sizeof(init_common)); ++ if (i >= 0) ++ i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); ++ if (i < 0) ++ v4l2_dbg(1, debug, sd, "init error %d\n", i); ++ else ++ v4l2_dbg(1, debug, sd, "revision 0x%x\n", ++ saa7185_read(sd) >> 5); ++ return 0; ++} ++ ++static int saa7185_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct saa7185 *encoder = to_saa7185(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ /* SW: output off is active */ ++ saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); ++ kfree(encoder); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id saa7185_id[] = { ++ { "saa7185", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa7185_id); ++ ++static struct i2c_driver saa7185_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa7185", ++ }, ++ .probe = saa7185_probe, ++ .remove = saa7185_remove, ++ .id_table = saa7185_id, ++}; ++ ++module_i2c_driver(saa7185_driver); +diff --git a/drivers/media/i2c/saa7191.c b/drivers/media/i2c/saa7191.c +new file mode 100644 +index 0000000..d7d1670 +--- /dev/null ++++ b/drivers/media/i2c/saa7191.c +@@ -0,0 +1,659 @@ ++/* ++ * saa7191.c - Philips SAA7191 video decoder driver ++ * ++ * Copyright (C) 2003 Ladislav Michl ++ * Copyright (C) 2004,2005 Mikael Nousiainen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "saa7191.h" ++ ++#define SAA7191_MODULE_VERSION "0.0.5" ++ ++MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); ++MODULE_VERSION(SAA7191_MODULE_VERSION); ++MODULE_AUTHOR("Mikael Nousiainen "); ++MODULE_LICENSE("GPL"); ++ ++ ++// #define SAA7191_DEBUG ++ ++#ifdef SAA7191_DEBUG ++#define dprintk(x...) printk("SAA7191: " x); ++#else ++#define dprintk(x...) ++#endif ++ ++#define SAA7191_SYNC_COUNT 30 ++#define SAA7191_SYNC_DELAY 100 /* milliseconds */ ++ ++struct saa7191 { ++ struct v4l2_subdev sd; ++ ++ /* the register values are stored here as the actual ++ * I2C-registers are write-only */ ++ u8 reg[25]; ++ ++ int input; ++ v4l2_std_id norm; ++}; ++ ++static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa7191, sd); ++} ++ ++static const u8 initseq[] = { ++ 0, /* Subaddress */ ++ ++ 0x50, /* (0x50) SAA7191_REG_IDEL */ ++ ++ /* 50 Hz signal timing */ ++ 0x30, /* (0x30) SAA7191_REG_HSYB */ ++ 0x00, /* (0x00) SAA7191_REG_HSYS */ ++ 0xe8, /* (0xe8) SAA7191_REG_HCLB */ ++ 0xb6, /* (0xb6) SAA7191_REG_HCLS */ ++ 0xf4, /* (0xf4) SAA7191_REG_HPHI */ ++ ++ /* control */ ++ SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ ++ 0x00, /* (0x00) SAA7191_REG_HUEC */ ++ 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ ++ 0xf8, /* (0xf8) SAA7191_REG_CKTS */ ++ 0x90, /* (0x90) SAA7191_REG_PLSE */ ++ 0x90, /* (0x90) SAA7191_REG_SESE */ ++ 0x00, /* (0x00) SAA7191_REG_GAIN */ ++ SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC ++ * - not SECAM, ++ * slow time constant */ ++ SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS ++ | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK ++ * - chroma from CVBS, GPSW1 & 2 off */ ++ SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS ++ | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 ++ * - automatic field detection */ ++ 0x00, /* (0x00) SAA7191_REG_CTL4 */ ++ 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ ++ 0x00, /* unused */ ++ 0x00, /* unused */ ++ ++ /* 60 Hz signal timing */ ++ 0x34, /* (0x34) SAA7191_REG_HS6B */ ++ 0x0a, /* (0x0a) SAA7191_REG_HS6S */ ++ 0xf4, /* (0xf4) SAA7191_REG_HC6B */ ++ 0xce, /* (0xce) SAA7191_REG_HC6S */ ++ 0xf4, /* (0xf4) SAA7191_REG_HP6I */ ++}; ++ ++/* SAA7191 register handling */ ++ ++static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) ++{ ++ return to_saa7191(sd)->reg[reg]; ++} ++ ++static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ ret = i2c_master_recv(client, value, 1); ++ if (ret < 0) { ++ printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ to_saa7191(sd)->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++/* the first byte of data must be the first subaddress number (register) */ ++static int saa7191_write_block(struct v4l2_subdev *sd, ++ u8 length, const u8 *data) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa7191 *decoder = to_saa7191(sd); ++ int i; ++ int ret; ++ ++ for (i = 0; i < (length - 1); i++) { ++ decoder->reg[data[0] + i] = data[i + 1]; ++ } ++ ++ ret = i2c_master_send(client, data, length); ++ if (ret < 0) { ++ printk(KERN_ERR "SAA7191: saa7191_write_block(): " ++ "write failed\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* Helper functions */ ++ ++static int saa7191_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct saa7191 *decoder = to_saa7191(sd); ++ u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); ++ u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); ++ int err; ++ ++ switch (input) { ++ case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ ++ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 ++ | SAA7191_IOCK_GPSW2); ++ /* Chrominance trap active */ ++ luma &= ~SAA7191_LUMA_BYPS; ++ break; ++ case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ ++ iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; ++ /* Chrominance trap bypassed */ ++ luma |= SAA7191_LUMA_BYPS; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); ++ if (err) ++ return -EIO; ++ err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); ++ if (err) ++ return -EIO; ++ ++ decoder->input = input; ++ ++ return 0; ++} ++ ++static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ struct saa7191 *decoder = to_saa7191(sd); ++ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); ++ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); ++ u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); ++ int err; ++ ++ if (norm & V4L2_STD_PAL) { ++ stdc &= ~SAA7191_STDC_SECS; ++ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); ++ chcv = SAA7191_CHCV_PAL; ++ } else if (norm & V4L2_STD_NTSC) { ++ stdc &= ~SAA7191_STDC_SECS; ++ ctl3 &= ~SAA7191_CTL3_AUFD; ++ ctl3 |= SAA7191_CTL3_FSEL; ++ chcv = SAA7191_CHCV_NTSC; ++ } else if (norm & V4L2_STD_SECAM) { ++ stdc |= SAA7191_STDC_SECS; ++ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); ++ chcv = SAA7191_CHCV_PAL; ++ } else { ++ return -EINVAL; ++ } ++ ++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); ++ if (err) ++ return -EIO; ++ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); ++ if (err) ++ return -EIO; ++ err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); ++ if (err) ++ return -EIO; ++ ++ decoder->norm = norm; ++ ++ dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, ++ stdc, chcv); ++ dprintk("norm: %llx\n", norm); ++ ++ return 0; ++} ++ ++static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) ++{ ++ int i = 0; ++ ++ dprintk("Checking for signal...\n"); ++ ++ for (i = 0; i < SAA7191_SYNC_COUNT; i++) { ++ if (saa7191_read_status(sd, status)) ++ return -EIO; ++ ++ if (((*status) & SAA7191_STATUS_HLCK) == 0) { ++ dprintk("Signal found\n"); ++ return 0; ++ } ++ ++ msleep(SAA7191_SYNC_DELAY); ++ } ++ ++ dprintk("No signal\n"); ++ ++ return -EBUSY; ++} ++ ++static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) ++{ ++ struct saa7191 *decoder = to_saa7191(sd); ++ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); ++ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); ++ u8 status; ++ v4l2_std_id old_norm = decoder->norm; ++ int err = 0; ++ ++ dprintk("SAA7191 extended signal auto-detection...\n"); ++ ++ *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; ++ stdc &= ~SAA7191_STDC_SECS; ++ ctl3 &= ~(SAA7191_CTL3_FSEL); ++ ++ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); ++ if (err) { ++ err = -EIO; ++ goto out; ++ } ++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); ++ if (err) { ++ err = -EIO; ++ goto out; ++ } ++ ++ ctl3 |= SAA7191_CTL3_AUFD; ++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); ++ if (err) { ++ err = -EIO; ++ goto out; ++ } ++ ++ msleep(SAA7191_SYNC_DELAY); ++ ++ err = saa7191_wait_for_signal(sd, &status); ++ if (err) ++ goto out; ++ ++ if (status & SAA7191_STATUS_FIDT) { ++ /* 60Hz signal -> NTSC */ ++ dprintk("60Hz signal: NTSC\n"); ++ *norm = V4L2_STD_NTSC; ++ return 0; ++ } ++ ++ /* 50Hz signal */ ++ dprintk("50Hz signal: Trying PAL...\n"); ++ ++ /* try PAL first */ ++ err = saa7191_s_std(sd, V4L2_STD_PAL); ++ if (err) ++ goto out; ++ ++ msleep(SAA7191_SYNC_DELAY); ++ ++ err = saa7191_wait_for_signal(sd, &status); ++ if (err) ++ goto out; ++ ++ /* not 50Hz ? */ ++ if (status & SAA7191_STATUS_FIDT) { ++ dprintk("No 50Hz signal\n"); ++ saa7191_s_std(sd, old_norm); ++ return -EAGAIN; ++ } ++ ++ if (status & SAA7191_STATUS_CODE) { ++ dprintk("PAL\n"); ++ *norm = V4L2_STD_PAL; ++ return saa7191_s_std(sd, old_norm); ++ } ++ ++ dprintk("No color detected with PAL - Trying SECAM...\n"); ++ ++ /* no color detected ? -> try SECAM */ ++ err = saa7191_s_std(sd, V4L2_STD_SECAM); ++ if (err) ++ goto out; ++ ++ msleep(SAA7191_SYNC_DELAY); ++ ++ err = saa7191_wait_for_signal(sd, &status); ++ if (err) ++ goto out; ++ ++ /* not 50Hz ? */ ++ if (status & SAA7191_STATUS_FIDT) { ++ dprintk("No 50Hz signal\n"); ++ err = -EAGAIN; ++ goto out; ++ } ++ ++ if (status & SAA7191_STATUS_CODE) { ++ /* Color detected -> SECAM */ ++ dprintk("SECAM\n"); ++ *norm = V4L2_STD_SECAM; ++ return saa7191_s_std(sd, old_norm); ++ } ++ ++ dprintk("No color detected with SECAM - Going back to PAL.\n"); ++ ++out: ++ return saa7191_s_std(sd, old_norm); ++} ++ ++static int saa7191_autodetect_norm(struct v4l2_subdev *sd) ++{ ++ u8 status; ++ ++ dprintk("SAA7191 signal auto-detection...\n"); ++ ++ dprintk("Reading status...\n"); ++ ++ if (saa7191_read_status(sd, &status)) ++ return -EIO; ++ ++ dprintk("Checking for signal...\n"); ++ ++ /* no signal ? */ ++ if (status & SAA7191_STATUS_HLCK) { ++ dprintk("No signal\n"); ++ return -EBUSY; ++ } ++ ++ dprintk("Signal found\n"); ++ ++ if (status & SAA7191_STATUS_FIDT) { ++ /* 60hz signal -> NTSC */ ++ dprintk("NTSC\n"); ++ return saa7191_s_std(sd, V4L2_STD_NTSC); ++ } else { ++ /* 50hz signal -> PAL */ ++ dprintk("PAL\n"); ++ return saa7191_s_std(sd, V4L2_STD_PAL); ++ } ++} ++ ++static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ u8 reg; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case SAA7191_CONTROL_BANDPASS: ++ case SAA7191_CONTROL_BANDPASS_WEIGHT: ++ case SAA7191_CONTROL_CORING: ++ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); ++ switch (ctrl->id) { ++ case SAA7191_CONTROL_BANDPASS: ++ ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) ++ >> SAA7191_LUMA_BPSS_SHIFT; ++ break; ++ case SAA7191_CONTROL_BANDPASS_WEIGHT: ++ ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) ++ >> SAA7191_LUMA_APER_SHIFT; ++ break; ++ case SAA7191_CONTROL_CORING: ++ ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) ++ >> SAA7191_LUMA_CORI_SHIFT; ++ break; ++ } ++ break; ++ case SAA7191_CONTROL_FORCE_COLOUR: ++ case SAA7191_CONTROL_CHROMA_GAIN: ++ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); ++ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) ++ ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; ++ else ++ ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) ++ >> SAA7191_GAIN_LFIS_SHIFT; ++ break; ++ case V4L2_CID_HUE: ++ reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); ++ if (reg < 0x80) ++ reg += 0x80; ++ else ++ reg -= 0x80; ++ ctrl->value = (s32)reg; ++ break; ++ case SAA7191_CONTROL_VTRC: ++ reg = saa7191_read_reg(sd, SAA7191_REG_STDC); ++ ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; ++ break; ++ case SAA7191_CONTROL_LUMA_DELAY: ++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); ++ ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) ++ >> SAA7191_CTL3_YDEL_SHIFT; ++ if (ctrl->value >= 4) ++ ctrl->value -= 8; ++ break; ++ case SAA7191_CONTROL_VNR: ++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); ++ ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) ++ >> SAA7191_CTL4_VNOI_SHIFT; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ u8 reg; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case SAA7191_CONTROL_BANDPASS: ++ case SAA7191_CONTROL_BANDPASS_WEIGHT: ++ case SAA7191_CONTROL_CORING: ++ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); ++ switch (ctrl->id) { ++ case SAA7191_CONTROL_BANDPASS: ++ reg &= ~SAA7191_LUMA_BPSS_MASK; ++ reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) ++ & SAA7191_LUMA_BPSS_MASK; ++ break; ++ case SAA7191_CONTROL_BANDPASS_WEIGHT: ++ reg &= ~SAA7191_LUMA_APER_MASK; ++ reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) ++ & SAA7191_LUMA_APER_MASK; ++ break; ++ case SAA7191_CONTROL_CORING: ++ reg &= ~SAA7191_LUMA_CORI_MASK; ++ reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) ++ & SAA7191_LUMA_CORI_MASK; ++ break; ++ } ++ ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); ++ break; ++ case SAA7191_CONTROL_FORCE_COLOUR: ++ case SAA7191_CONTROL_CHROMA_GAIN: ++ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); ++ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { ++ if (ctrl->value) ++ reg |= SAA7191_GAIN_COLO; ++ else ++ reg &= ~SAA7191_GAIN_COLO; ++ } else { ++ reg &= ~SAA7191_GAIN_LFIS_MASK; ++ reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) ++ & SAA7191_GAIN_LFIS_MASK; ++ } ++ ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); ++ break; ++ case V4L2_CID_HUE: ++ reg = ctrl->value & 0xff; ++ if (reg < 0x80) ++ reg += 0x80; ++ else ++ reg -= 0x80; ++ ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); ++ break; ++ case SAA7191_CONTROL_VTRC: ++ reg = saa7191_read_reg(sd, SAA7191_REG_STDC); ++ if (ctrl->value) ++ reg |= SAA7191_STDC_VTRC; ++ else ++ reg &= ~SAA7191_STDC_VTRC; ++ ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); ++ break; ++ case SAA7191_CONTROL_LUMA_DELAY: { ++ s32 value = ctrl->value; ++ if (value < 0) ++ value += 8; ++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); ++ reg &= ~SAA7191_CTL3_YDEL_MASK; ++ reg |= (value << SAA7191_CTL3_YDEL_SHIFT) ++ & SAA7191_CTL3_YDEL_MASK; ++ ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); ++ break; ++ } ++ case SAA7191_CONTROL_VNR: ++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); ++ reg &= ~SAA7191_CTL4_VNOI_MASK; ++ reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) ++ & SAA7191_CTL4_VNOI_MASK; ++ ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/* I2C-interface */ ++ ++static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ u8 status_reg; ++ int res = V4L2_IN_ST_NO_SIGNAL; ++ ++ if (saa7191_read_status(sd, &status_reg)) ++ return -EIO; ++ if ((status_reg & SAA7191_STATUS_HLCK) == 0) ++ res = 0; ++ if (!(status_reg & SAA7191_STATUS_CODE)) ++ res |= V4L2_IN_ST_NO_COLOR; ++ *status = res; ++ return 0; ++} ++ ++ ++static int saa7191_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops saa7191_core_ops = { ++ .g_chip_ident = saa7191_g_chip_ident, ++ .g_ctrl = saa7191_g_ctrl, ++ .s_ctrl = saa7191_s_ctrl, ++ .s_std = saa7191_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops saa7191_video_ops = { ++ .s_routing = saa7191_s_routing, ++ .querystd = saa7191_querystd, ++ .g_input_status = saa7191_g_input_status, ++}; ++ ++static const struct v4l2_subdev_ops saa7191_ops = { ++ .core = &saa7191_core_ops, ++ .video = &saa7191_video_ops, ++}; ++ ++static int saa7191_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int err = 0; ++ struct saa7191 *decoder; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); ++ if (!decoder) ++ return -ENOMEM; ++ ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa7191_ops); ++ ++ err = saa7191_write_block(sd, sizeof(initseq), initseq); ++ if (err) { ++ printk(KERN_ERR "SAA7191 initialization failed\n"); ++ kfree(decoder); ++ return err; ++ } ++ ++ printk(KERN_INFO "SAA7191 initialized\n"); ++ ++ decoder->input = SAA7191_INPUT_COMPOSITE; ++ decoder->norm = V4L2_STD_PAL; ++ ++ err = saa7191_autodetect_norm(sd); ++ if (err && (err != -EBUSY)) ++ printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); ++ ++ return 0; ++} ++ ++static int saa7191_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_saa7191(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id saa7191_id[] = { ++ { "saa7191", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa7191_id); ++ ++static struct i2c_driver saa7191_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa7191", ++ }, ++ .probe = saa7191_probe, ++ .remove = saa7191_remove, ++ .id_table = saa7191_id, ++}; ++ ++module_i2c_driver(saa7191_driver); +diff --git a/drivers/media/i2c/saa7191.h b/drivers/media/i2c/saa7191.h +new file mode 100644 +index 0000000..803c74d +--- /dev/null ++++ b/drivers/media/i2c/saa7191.h +@@ -0,0 +1,245 @@ ++/* ++ * saa7191.h - Philips SAA7191 video decoder driver ++ * ++ * Copyright (C) 2003 Ladislav Michl ++ * Copyright (C) 2004,2005 Mikael Nousiainen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _SAA7191_H_ ++#define _SAA7191_H_ ++ ++/* Philips SAA7191 DMSD I2C bus address */ ++#define SAA7191_ADDR 0x8a ++ ++/* Register subaddresses. */ ++#define SAA7191_REG_IDEL 0x00 ++#define SAA7191_REG_HSYB 0x01 ++#define SAA7191_REG_HSYS 0x02 ++#define SAA7191_REG_HCLB 0x03 ++#define SAA7191_REG_HCLS 0x04 ++#define SAA7191_REG_HPHI 0x05 ++#define SAA7191_REG_LUMA 0x06 ++#define SAA7191_REG_HUEC 0x07 ++#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ ++#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ ++#define SAA7191_REG_PLSE 0x0a ++#define SAA7191_REG_SESE 0x0b ++#define SAA7191_REG_GAIN 0x0c ++#define SAA7191_REG_STDC 0x0d ++#define SAA7191_REG_IOCK 0x0e ++#define SAA7191_REG_CTL3 0x0f ++#define SAA7191_REG_CTL4 0x10 ++#define SAA7191_REG_CHCV 0x11 ++#define SAA7191_REG_HS6B 0x14 ++#define SAA7191_REG_HS6S 0x15 ++#define SAA7191_REG_HC6B 0x16 ++#define SAA7191_REG_HC6S 0x17 ++#define SAA7191_REG_HP6I 0x18 ++#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ ++ ++/* Status Register definitions */ ++#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ ++#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ ++#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ ++#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ ++ ++/* Luminance Control Register definitions */ ++/* input mode select bit: ++ * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ ++#define SAA7191_LUMA_BYPS 0x80 ++/* pre-filter (only when chrominance trap is active) */ ++#define SAA7191_LUMA_PREF 0x40 ++/* aperture bandpass to select different characteristics with maximums ++ * (bits 4-5) */ ++#define SAA7191_LUMA_BPSS_MASK 0x30 ++#define SAA7191_LUMA_BPSS_SHIFT 4 ++#define SAA7191_LUMA_BPSS_3 0x30 ++#define SAA7191_LUMA_BPSS_2 0x20 ++#define SAA7191_LUMA_BPSS_1 0x10 ++#define SAA7191_LUMA_BPSS_0 0x00 ++/* coring range for high frequency components according to 8-bit luminance ++ * (bits 2-3) ++ * 0=coring off, n= (+-)n LSB */ ++#define SAA7191_LUMA_CORI_MASK 0x0c ++#define SAA7191_LUMA_CORI_SHIFT 2 ++#define SAA7191_LUMA_CORI_3 0x0c ++#define SAA7191_LUMA_CORI_2 0x08 ++#define SAA7191_LUMA_CORI_1 0x04 ++#define SAA7191_LUMA_CORI_0 0x00 ++/* aperture bandpass filter weights high frequency components of luminance ++ * signal (bits 0-1) ++ * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ ++#define SAA7191_LUMA_APER_MASK 0x03 ++#define SAA7191_LUMA_APER_SHIFT 0 ++#define SAA7191_LUMA_APER_3 0x03 ++#define SAA7191_LUMA_APER_2 0x02 ++#define SAA7191_LUMA_APER_1 0x01 ++#define SAA7191_LUMA_APER_0 0x00 ++ ++/* Chrominance Gain Control Settings Register definitions */ ++/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ ++#define SAA7191_GAIN_COLO 0x80 ++/* chrominance gain control (AGC filter) ++ * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ ++#define SAA7191_GAIN_LFIS_MASK 0x60 ++#define SAA7191_GAIN_LFIS_SHIFT 5 ++#define SAA7191_GAIN_LFIS_3 0x60 ++#define SAA7191_GAIN_LFIS_2 0x40 ++#define SAA7191_GAIN_LFIS_1 0x20 ++#define SAA7191_GAIN_LFIS_0 0x00 ++ ++/* Standard/Mode Control Register definitions */ ++/* tv/vtr mode bit: 0=TV mode (slow time constant), ++ * 1=VTR mode (fast time constant) */ ++#define SAA7191_STDC_VTRC 0x80 ++/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) ++ * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ ++#define SAA7191_STDC_NFEN 0x08 ++/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ ++#define SAA7191_STDC_HRMV 0x04 ++/* general purpose switch 0 ++ * (not used with VINO afaik) */ ++#define SAA7191_STDC_GPSW0 0x02 ++/* SECAM mode bit: 0=other standards, 1=SECAM */ ++#define SAA7191_STDC_SECS 0x01 ++ ++/* I/O and Clock Control Register definitions */ ++/* horizontal clock PLL: 0=PLL closed, ++ * 1=PLL circuit open and horizontal freq fixed */ ++#define SAA7191_IOCK_HPLL 0x80 ++/* colour-difference output enable (outputs UV0-UV7) */ ++#define SAA7191_IOCK_OEDC 0x40 ++/* H-sync output enable */ ++#define SAA7191_IOCK_OEHS 0x20 ++/* V-sync output enable */ ++#define SAA7191_IOCK_OEVS 0x10 ++/* luminance output enable (outputs Y0-Y7) */ ++#define SAA7191_IOCK_OEDY 0x08 ++/* S-VHS bit (chrominance from CVBS or from chrominance input): ++ * 0=controlled by BYPS-bit, 1=from chrominance input */ ++#define SAA7191_IOCK_CHRS 0x04 ++/* general purpose switch 2 ++ * VINO-specific: 0=used with CVBS, 1=used with S-Video */ ++#define SAA7191_IOCK_GPSW2 0x02 ++/* general purpose switch 1 */ ++/* VINO-specific: 0=always, 1=not used!*/ ++#define SAA7191_IOCK_GPSW1 0x01 ++ ++/* Miscellaneous Control #1 Register definitions */ ++/* automatic field detection (50/60Hz standard) */ ++#define SAA7191_CTL3_AUFD 0x80 ++/* field select: (if AUFD=0) ++ * 0=50Hz (625 lines), 1=60Hz (525 lines) */ ++#define SAA7191_CTL3_FSEL 0x40 ++/* SECAM cross-colour reduction enable */ ++#define SAA7191_CTL3_SXCR 0x20 ++/* sync and clamping pulse enable (HCL and HSY outputs) */ ++#define SAA7191_CTL3_SCEN 0x10 ++/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ ++#define SAA7191_CTL3_OFTS 0x08 ++/* luminance delay compensation ++ * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, ++ * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC ++ * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ ++#define SAA7191_CTL3_YDEL_MASK 0x07 ++#define SAA7191_CTL3_YDEL_SHIFT 0 ++#define SAA7191_CTL3_YDEL2 0x04 ++#define SAA7191_CTL3_YDEL1 0x02 ++#define SAA7191_CTL3_YDEL0 0x01 ++ ++/* Miscellaneous Control #2 Register definitions */ ++/* select HREF position ++ * 0=normal, HREF is matched to YUV output port, ++ * 1=HREF is matched to CVBS input port */ ++#define SAA7191_CTL4_HRFS 0x04 ++/* vertical noise reduction ++ * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ ++#define SAA7191_CTL4_VNOI_MASK 0x03 ++#define SAA7191_CTL4_VNOI_SHIFT 0 ++#define SAA7191_CTL4_VNOI_3 0x03 ++#define SAA7191_CTL4_VNOI_2 0x02 ++#define SAA7191_CTL4_VNOI_1 0x01 ++#define SAA7191_CTL4_VNOI_0 0x00 ++ ++/* Chrominance Gain Control Register definitions ++ * - for QAM-modulated input signals, effects output amplitude ++ * (SECAM gain fixed) ++ * (nominal values for UV CCIR level) */ ++#define SAA7191_CHCV_NTSC 0x2c ++#define SAA7191_CHCV_PAL 0x59 ++ ++/* Driver interface definitions */ ++#define SAA7191_INPUT_COMPOSITE 0 ++#define SAA7191_INPUT_SVIDEO 1 ++ ++#define SAA7191_NORM_PAL 1 ++#define SAA7191_NORM_NTSC 2 ++#define SAA7191_NORM_SECAM 3 ++ ++struct saa7191_status { ++ /* 0=no signal, 1=signal detected */ ++ int signal; ++ /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ ++ int signal_60hz; ++ /* 0=no color detected, 1=color detected */ ++ int color; ++ ++ /* current SAA7191_INPUT_ */ ++ int input; ++ /* current SAA7191_NORM_ */ ++ int norm; ++}; ++ ++#define SAA7191_BANDPASS_MIN 0x00 ++#define SAA7191_BANDPASS_MAX 0x03 ++#define SAA7191_BANDPASS_DEFAULT 0x00 ++ ++#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 ++#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 ++#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 ++ ++#define SAA7191_CORING_MIN 0x00 ++#define SAA7191_CORING_MAX 0x03 ++#define SAA7191_CORING_DEFAULT 0x00 ++ ++#define SAA7191_HUE_MIN 0x00 ++#define SAA7191_HUE_MAX 0xff ++#define SAA7191_HUE_DEFAULT 0x80 ++ ++#define SAA7191_VTRC_MIN 0x00 ++#define SAA7191_VTRC_MAX 0x01 ++#define SAA7191_VTRC_DEFAULT 0x00 ++ ++#define SAA7191_FORCE_COLOUR_MIN 0x00 ++#define SAA7191_FORCE_COLOUR_MAX 0x01 ++#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 ++ ++#define SAA7191_CHROMA_GAIN_MIN 0x00 ++#define SAA7191_CHROMA_GAIN_MAX 0x03 ++#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 ++ ++#define SAA7191_LUMA_DELAY_MIN -0x04 ++#define SAA7191_LUMA_DELAY_MAX 0x03 ++#define SAA7191_LUMA_DELAY_DEFAULT 0x01 ++ ++#define SAA7191_VNR_MIN 0x00 ++#define SAA7191_VNR_MAX 0x03 ++#define SAA7191_VNR_DEFAULT 0x00 ++ ++#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) ++#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) ++#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) ++#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) ++#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) ++#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) ++#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) ++#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) ++ ++#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) ++#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) ++ ++#endif +diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c +new file mode 100644 +index 0000000..d8d5da7 +--- /dev/null ++++ b/drivers/media/i2c/smiapp-pll.c +@@ -0,0 +1,443 @@ ++/* ++ * drivers/media/i2c/smiapp-pll.c ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++ ++#include "smiapp-pll.h" ++ ++/* Return an even number or one. */ ++static inline uint32_t clk_div_even(uint32_t a) ++{ ++ return max_t(uint32_t, 1, a & ~1); ++} ++ ++/* Return an even number or one. */ ++static inline uint32_t clk_div_even_up(uint32_t a) ++{ ++ if (a == 1) ++ return 1; ++ return (a + 1) & ~1; ++} ++ ++static inline uint32_t is_one_or_even(uint32_t a) ++{ ++ if (a == 1) ++ return 1; ++ if (a & 1) ++ return 0; ++ ++ return 1; ++} ++ ++static int bounds_check(struct device *dev, uint32_t val, ++ uint32_t min, uint32_t max, char *str) ++{ ++ if (val >= min && val <= max) ++ return 0; ++ ++ dev_dbg(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); ++ ++ return -EINVAL; ++} ++ ++static void print_pll(struct device *dev, struct smiapp_pll *pll) ++{ ++ dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); ++ dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); ++ if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { ++ dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); ++ dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); ++ } ++ dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); ++ dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); ++ ++ dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); ++ dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); ++ dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); ++ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { ++ dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", ++ pll->op_sys_clk_freq_hz); ++ dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", ++ pll->op_pix_clk_freq_hz); ++ } ++ dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); ++ dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); ++} ++ ++static int __smiapp_pll_calculate(struct device *dev, ++ const struct smiapp_pll_limits *limits, ++ struct smiapp_pll *pll, uint32_t mul, ++ uint32_t div, uint32_t lane_op_clock_ratio) ++{ ++ uint32_t sys_div; ++ uint32_t best_pix_div = INT_MAX >> 1; ++ uint32_t vt_op_binning_div; ++ uint32_t more_mul_min, more_mul_max; ++ uint32_t more_mul_factor; ++ uint32_t min_vt_div, max_vt_div, vt_div; ++ uint32_t min_sys_div, max_sys_div; ++ unsigned int i; ++ int rval; ++ ++ /* ++ * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be ++ * too high. ++ */ ++ dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); ++ ++ /* Don't go above max pll multiplier. */ ++ more_mul_max = limits->max_pll_multiplier / mul; ++ dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", ++ more_mul_max); ++ /* Don't go above max pll op frequency. */ ++ more_mul_max = ++ min_t(uint32_t, ++ more_mul_max, ++ limits->max_pll_op_freq_hz ++ / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); ++ dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", ++ more_mul_max); ++ /* Don't go above the division capability of op sys clock divider. */ ++ more_mul_max = min(more_mul_max, ++ limits->op.max_sys_clk_div * pll->pre_pll_clk_div ++ / div); ++ dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", ++ more_mul_max); ++ /* Ensure we won't go above min_pll_multiplier. */ ++ more_mul_max = min(more_mul_max, ++ DIV_ROUND_UP(limits->max_pll_multiplier, mul)); ++ dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", ++ more_mul_max); ++ ++ /* Ensure we won't go below min_pll_op_freq_hz. */ ++ more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, ++ pll->ext_clk_freq_hz / pll->pre_pll_clk_div ++ * mul); ++ dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", ++ more_mul_min); ++ /* Ensure we won't go below min_pll_multiplier. */ ++ more_mul_min = max(more_mul_min, ++ DIV_ROUND_UP(limits->min_pll_multiplier, mul)); ++ dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", ++ more_mul_min); ++ ++ if (more_mul_min > more_mul_max) { ++ dev_dbg(dev, ++ "unable to compute more_mul_min and more_mul_max\n"); ++ return -EINVAL; ++ } ++ ++ more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; ++ dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); ++ more_mul_factor = lcm(more_mul_factor, limits->op.min_sys_clk_div); ++ dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", ++ more_mul_factor); ++ i = roundup(more_mul_min, more_mul_factor); ++ if (!is_one_or_even(i)) ++ i <<= 1; ++ ++ dev_dbg(dev, "final more_mul: %d\n", i); ++ if (i > more_mul_max) { ++ dev_dbg(dev, "final more_mul is bad, max %d\n", more_mul_max); ++ return -EINVAL; ++ } ++ ++ pll->pll_multiplier = mul * i; ++ pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; ++ dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); ++ ++ pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz ++ / pll->pre_pll_clk_div; ++ ++ pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz ++ * pll->pll_multiplier; ++ ++ /* Derive pll_op_clk_freq_hz. */ ++ pll->op_sys_clk_freq_hz = ++ pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; ++ ++ pll->op_pix_clk_div = pll->bits_per_pixel; ++ dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); ++ ++ pll->op_pix_clk_freq_hz = ++ pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; ++ ++ /* ++ * Some sensors perform analogue binning and some do this ++ * digitally. The ones doing this digitally can be roughly be ++ * found out using this formula. The ones doing this digitally ++ * should run at higher clock rate, so smaller divisor is used ++ * on video timing side. ++ */ ++ if (limits->min_line_length_pck_bin > limits->min_line_length_pck ++ / pll->binning_horizontal) ++ vt_op_binning_div = pll->binning_horizontal; ++ else ++ vt_op_binning_div = 1; ++ dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); ++ ++ /* ++ * Profile 2 supports vt_pix_clk_div E [4, 10] ++ * ++ * Horizontal binning can be used as a base for difference in ++ * divisors. One must make sure that horizontal blanking is ++ * enough to accommodate the CSI-2 sync codes. ++ * ++ * Take scaling factor into account as well. ++ * ++ * Find absolute limits for the factor of vt divider. ++ */ ++ dev_dbg(dev, "scale_m: %d\n", pll->scale_m); ++ min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div ++ * pll->scale_n, ++ lane_op_clock_ratio * vt_op_binning_div ++ * pll->scale_m); ++ ++ /* Find smallest and biggest allowed vt divisor. */ ++ dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); ++ min_vt_div = max(min_vt_div, ++ DIV_ROUND_UP(pll->pll_op_clk_freq_hz, ++ limits->vt.max_pix_clk_freq_hz)); ++ dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", ++ min_vt_div); ++ min_vt_div = max_t(uint32_t, min_vt_div, ++ limits->vt.min_pix_clk_div ++ * limits->vt.min_sys_clk_div); ++ dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); ++ ++ max_vt_div = limits->vt.max_sys_clk_div * limits->vt.max_pix_clk_div; ++ dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); ++ max_vt_div = min(max_vt_div, ++ DIV_ROUND_UP(pll->pll_op_clk_freq_hz, ++ limits->vt.min_pix_clk_freq_hz)); ++ dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", ++ max_vt_div); ++ ++ /* ++ * Find limitsits for sys_clk_div. Not all values are possible ++ * with all values of pix_clk_div. ++ */ ++ min_sys_div = limits->vt.min_sys_clk_div; ++ dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); ++ min_sys_div = max(min_sys_div, ++ DIV_ROUND_UP(min_vt_div, ++ limits->vt.max_pix_clk_div)); ++ dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); ++ min_sys_div = max(min_sys_div, ++ pll->pll_op_clk_freq_hz ++ / limits->vt.max_sys_clk_freq_hz); ++ dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); ++ min_sys_div = clk_div_even_up(min_sys_div); ++ dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); ++ ++ max_sys_div = limits->vt.max_sys_clk_div; ++ dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); ++ max_sys_div = min(max_sys_div, ++ DIV_ROUND_UP(max_vt_div, ++ limits->vt.min_pix_clk_div)); ++ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); ++ max_sys_div = min(max_sys_div, ++ DIV_ROUND_UP(pll->pll_op_clk_freq_hz, ++ limits->vt.min_pix_clk_freq_hz)); ++ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); ++ ++ /* ++ * Find pix_div such that a legal pix_div * sys_div results ++ * into a value which is not smaller than div, the desired ++ * divisor. ++ */ ++ for (vt_div = min_vt_div; vt_div <= max_vt_div; ++ vt_div += 2 - (vt_div & 1)) { ++ for (sys_div = min_sys_div; ++ sys_div <= max_sys_div; ++ sys_div += 2 - (sys_div & 1)) { ++ uint16_t pix_div = DIV_ROUND_UP(vt_div, sys_div); ++ ++ if (pix_div < limits->vt.min_pix_clk_div ++ || pix_div > limits->vt.max_pix_clk_div) { ++ dev_dbg(dev, ++ "pix_div %d too small or too big (%d--%d)\n", ++ pix_div, ++ limits->vt.min_pix_clk_div, ++ limits->vt.max_pix_clk_div); ++ continue; ++ } ++ ++ /* Check if this one is better. */ ++ if (pix_div * sys_div ++ <= roundup(min_vt_div, best_pix_div)) ++ best_pix_div = pix_div; ++ } ++ if (best_pix_div < INT_MAX >> 1) ++ break; ++ } ++ ++ pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); ++ pll->vt_pix_clk_div = best_pix_div; ++ ++ pll->vt_sys_clk_freq_hz = ++ pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; ++ pll->vt_pix_clk_freq_hz = ++ pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; ++ ++ pll->pixel_rate_csi = ++ pll->op_pix_clk_freq_hz * lane_op_clock_ratio; ++ ++ rval = bounds_check(dev, pll->pll_ip_clk_freq_hz, ++ limits->min_pll_ip_freq_hz, ++ limits->max_pll_ip_freq_hz, ++ "pll_ip_clk_freq_hz"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->pll_multiplier, ++ limits->min_pll_multiplier, limits->max_pll_multiplier, ++ "pll_multiplier"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->pll_op_clk_freq_hz, ++ limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, ++ "pll_op_clk_freq_hz"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->op_sys_clk_div, ++ limits->op.min_sys_clk_div, limits->op.max_sys_clk_div, ++ "op_sys_clk_div"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->op_pix_clk_div, ++ limits->op.min_pix_clk_div, limits->op.max_pix_clk_div, ++ "op_pix_clk_div"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->op_sys_clk_freq_hz, ++ limits->op.min_sys_clk_freq_hz, ++ limits->op.max_sys_clk_freq_hz, ++ "op_sys_clk_freq_hz"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->op_pix_clk_freq_hz, ++ limits->op.min_pix_clk_freq_hz, ++ limits->op.max_pix_clk_freq_hz, ++ "op_pix_clk_freq_hz"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->vt_sys_clk_freq_hz, ++ limits->vt.min_sys_clk_freq_hz, ++ limits->vt.max_sys_clk_freq_hz, ++ "vt_sys_clk_freq_hz"); ++ if (!rval) ++ rval = bounds_check( ++ dev, pll->vt_pix_clk_freq_hz, ++ limits->vt.min_pix_clk_freq_hz, ++ limits->vt.max_pix_clk_freq_hz, ++ "vt_pix_clk_freq_hz"); ++ ++ return rval; ++} ++ ++int smiapp_pll_calculate(struct device *dev, ++ const struct smiapp_pll_limits *limits, ++ struct smiapp_pll *pll) ++{ ++ uint16_t min_pre_pll_clk_div; ++ uint16_t max_pre_pll_clk_div; ++ uint32_t lane_op_clock_ratio; ++ uint32_t mul, div; ++ unsigned int i; ++ int rval = -EINVAL; ++ ++ if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) ++ lane_op_clock_ratio = pll->csi2.lanes; ++ else ++ lane_op_clock_ratio = 1; ++ dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); ++ ++ dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, ++ pll->binning_vertical); ++ ++ switch (pll->bus_type) { ++ case SMIAPP_PLL_BUS_TYPE_CSI2: ++ /* CSI transfers 2 bits per clock per lane; thus times 2 */ ++ pll->pll_op_clk_freq_hz = pll->link_freq * 2 ++ * (pll->csi2.lanes / lane_op_clock_ratio); ++ break; ++ case SMIAPP_PLL_BUS_TYPE_PARALLEL: ++ pll->pll_op_clk_freq_hz = pll->link_freq * pll->bits_per_pixel ++ / DIV_ROUND_UP(pll->bits_per_pixel, ++ pll->parallel.bus_width); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Figure out limits for pre-pll divider based on extclk */ ++ dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", ++ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); ++ max_pre_pll_clk_div = ++ min_t(uint16_t, limits->max_pre_pll_clk_div, ++ clk_div_even(pll->ext_clk_freq_hz / ++ limits->min_pll_ip_freq_hz)); ++ min_pre_pll_clk_div = ++ max_t(uint16_t, limits->min_pre_pll_clk_div, ++ clk_div_even_up( ++ DIV_ROUND_UP(pll->ext_clk_freq_hz, ++ limits->max_pll_ip_freq_hz))); ++ dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", ++ min_pre_pll_clk_div, max_pre_pll_clk_div); ++ ++ i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); ++ mul = div_u64(pll->pll_op_clk_freq_hz, i); ++ div = pll->ext_clk_freq_hz / i; ++ dev_dbg(dev, "mul %d / div %d\n", mul, div); ++ ++ min_pre_pll_clk_div = ++ max_t(uint16_t, min_pre_pll_clk_div, ++ clk_div_even_up( ++ DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, ++ limits->max_pll_op_freq_hz))); ++ dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", ++ min_pre_pll_clk_div, max_pre_pll_clk_div); ++ ++ for (pll->pre_pll_clk_div = min_pre_pll_clk_div; ++ pll->pre_pll_clk_div <= max_pre_pll_clk_div; ++ pll->pre_pll_clk_div += 2 - (pll->pre_pll_clk_div & 1)) { ++ rval = __smiapp_pll_calculate(dev, limits, pll, mul, div, ++ lane_op_clock_ratio); ++ if (rval) ++ continue; ++ ++ print_pll(dev, pll); ++ return 0; ++ } ++ ++ dev_info(dev, "unable to compute pre_pll divisor\n"); ++ return rval; ++} ++EXPORT_SYMBOL_GPL(smiapp_pll_calculate); ++ ++MODULE_AUTHOR("Sakari Ailus "); ++MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h +new file mode 100644 +index 0000000..a4a6498 +--- /dev/null ++++ b/drivers/media/i2c/smiapp-pll.h +@@ -0,0 +1,114 @@ ++/* ++ * drivers/media/i2c/smiapp-pll.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef SMIAPP_PLL_H ++#define SMIAPP_PLL_H ++ ++#include ++ ++/* CSI-2 or CCP-2 */ ++#define SMIAPP_PLL_BUS_TYPE_CSI2 0x00 ++#define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01 ++ ++/* op pix clock is for all lanes in total normally */ ++#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) ++#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) ++ ++struct smiapp_pll { ++ /* input values */ ++ uint8_t bus_type; ++ union { ++ struct { ++ uint8_t lanes; ++ } csi2; ++ struct { ++ uint8_t bus_width; ++ } parallel; ++ }; ++ uint8_t flags; ++ uint8_t binning_horizontal; ++ uint8_t binning_vertical; ++ uint8_t scale_m; ++ uint8_t scale_n; ++ uint8_t bits_per_pixel; ++ uint32_t link_freq; ++ ++ /* output values */ ++ uint16_t pre_pll_clk_div; ++ uint16_t pll_multiplier; ++ uint16_t op_sys_clk_div; ++ uint16_t op_pix_clk_div; ++ uint16_t vt_sys_clk_div; ++ uint16_t vt_pix_clk_div; ++ ++ uint32_t ext_clk_freq_hz; ++ uint32_t pll_ip_clk_freq_hz; ++ uint32_t pll_op_clk_freq_hz; ++ uint32_t op_sys_clk_freq_hz; ++ uint32_t op_pix_clk_freq_hz; ++ uint32_t vt_sys_clk_freq_hz; ++ uint32_t vt_pix_clk_freq_hz; ++ ++ uint32_t pixel_rate_csi; ++}; ++ ++struct smiapp_pll_branch_limits { ++ uint16_t min_sys_clk_div; ++ uint16_t max_sys_clk_div; ++ uint32_t min_sys_clk_freq_hz; ++ uint32_t max_sys_clk_freq_hz; ++ uint16_t min_pix_clk_div; ++ uint16_t max_pix_clk_div; ++ uint32_t min_pix_clk_freq_hz; ++ uint32_t max_pix_clk_freq_hz; ++}; ++ ++struct smiapp_pll_limits { ++ /* Strict PLL limits */ ++ uint32_t min_ext_clk_freq_hz; ++ uint32_t max_ext_clk_freq_hz; ++ uint16_t min_pre_pll_clk_div; ++ uint16_t max_pre_pll_clk_div; ++ uint32_t min_pll_ip_freq_hz; ++ uint32_t max_pll_ip_freq_hz; ++ uint16_t min_pll_multiplier; ++ uint16_t max_pll_multiplier; ++ uint32_t min_pll_op_freq_hz; ++ uint32_t max_pll_op_freq_hz; ++ ++ struct smiapp_pll_branch_limits vt; ++ struct smiapp_pll_branch_limits op; ++ ++ /* Other relevant limits */ ++ uint32_t min_line_length_pck_bin; ++ uint32_t min_line_length_pck; ++}; ++ ++struct device; ++ ++int smiapp_pll_calculate(struct device *dev, ++ const struct smiapp_pll_limits *limits, ++ struct smiapp_pll *pll); ++ ++#endif /* SMIAPP_PLL_H */ +diff --git a/drivers/media/i2c/smiapp/Kconfig b/drivers/media/i2c/smiapp/Kconfig +new file mode 100644 +index 0000000..3149cda +--- /dev/null ++++ b/drivers/media/i2c/smiapp/Kconfig +@@ -0,0 +1,7 @@ ++config VIDEO_SMIAPP ++ tristate "SMIA++/SMIA sensor support" ++ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAVE_CLK ++ depends on MEDIA_CAMERA_SUPPORT ++ select VIDEO_SMIAPP_PLL ++ ---help--- ++ This is a generic driver for SMIA++/SMIA camera modules. +diff --git a/drivers/media/i2c/smiapp/Makefile b/drivers/media/i2c/smiapp/Makefile +new file mode 100644 +index 0000000..f45a003 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/Makefile +@@ -0,0 +1,5 @@ ++smiapp-objs += smiapp-core.o smiapp-regs.o \ ++ smiapp-quirk.o smiapp-limits.o ++obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o ++ ++ccflags-y += -Idrivers/media/i2c +diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c +new file mode 100644 +index 0000000..83c7ed7 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-core.c +@@ -0,0 +1,2892 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-core.c ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2010--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * Based on smiapp driver by Vimarsh Zutshi ++ * Based on jt8ev1.c by Vimarsh Zutshi ++ * Based on smia-sensor.c by Tuukka Toivonen ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "smiapp.h" ++ ++#define SMIAPP_ALIGN_DIM(dim, flags) \ ++ ((flags) & V4L2_SEL_FLAG_GE \ ++ ? ALIGN((dim), 2) \ ++ : (dim) & ~1) ++ ++/* ++ * smiapp_module_idents - supported camera modules ++ */ ++static const struct smiapp_module_ident smiapp_module_idents[] = { ++ SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), ++ SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), ++ SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), ++ SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), ++ SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), ++ SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), ++ SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), ++ SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), ++ SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), ++ SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), ++ SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), ++}; ++ ++/* ++ * ++ * Dynamic Capability Identification ++ * ++ */ ++ ++static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; ++ unsigned int i; ++ int rval; ++ int line_count = 0; ++ int embedded_start = -1, embedded_end = -1; ++ int image_start = 0; ++ ++ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, ++ &fmt_model_type); ++ if (rval) ++ return rval; ++ ++ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, ++ &fmt_model_subtype); ++ if (rval) ++ return rval; ++ ++ ncol_desc = (fmt_model_subtype ++ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) ++ >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; ++ nrow_desc = fmt_model_subtype ++ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; ++ ++ dev_dbg(&client->dev, "format_model_type %s\n", ++ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE ++ ? "2 byte" : ++ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE ++ ? "4 byte" : "is simply bad"); ++ ++ for (i = 0; i < ncol_desc + nrow_desc; i++) { ++ u32 desc; ++ u32 pixelcode; ++ u32 pixels; ++ char *which; ++ char *what; ++ ++ if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { ++ rval = smiapp_read( ++ sensor, ++ SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), ++ &desc); ++ if (rval) ++ return rval; ++ ++ pixelcode = ++ (desc ++ & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) ++ >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; ++ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; ++ } else if (fmt_model_type ++ == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { ++ rval = smiapp_read( ++ sensor, ++ SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), ++ &desc); ++ if (rval) ++ return rval; ++ ++ pixelcode = ++ (desc ++ & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) ++ >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; ++ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; ++ } else { ++ dev_dbg(&client->dev, ++ "invalid frame format model type %d\n", ++ fmt_model_type); ++ return -EINVAL; ++ } ++ ++ if (i < ncol_desc) ++ which = "columns"; ++ else ++ which = "rows"; ++ ++ switch (pixelcode) { ++ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: ++ what = "embedded"; ++ break; ++ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: ++ what = "dummy"; ++ break; ++ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: ++ what = "black"; ++ break; ++ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: ++ what = "dark"; ++ break; ++ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: ++ what = "visible"; ++ break; ++ default: ++ what = "invalid"; ++ dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); ++ break; ++ } ++ ++ dev_dbg(&client->dev, "%s pixels: %d %s\n", ++ what, pixels, which); ++ ++ if (i < ncol_desc) ++ continue; ++ ++ /* Handle row descriptors */ ++ if (pixelcode ++ == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { ++ embedded_start = line_count; ++ } else { ++ if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE ++ || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) ++ image_start = line_count; ++ if (embedded_start != -1 && embedded_end == -1) ++ embedded_end = line_count; ++ } ++ line_count += pixels; ++ } ++ ++ if (embedded_start == -1 || embedded_end == -1) { ++ embedded_start = 0; ++ embedded_end = 0; ++ } ++ ++ dev_dbg(&client->dev, "embedded data from lines %d to %d\n", ++ embedded_start, embedded_end); ++ dev_dbg(&client->dev, "image data starts at line %d\n", image_start); ++ ++ return 0; ++} ++ ++static int smiapp_pll_configure(struct smiapp_sensor *sensor) ++{ ++ struct smiapp_pll *pll = &sensor->pll; ++ int rval; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); ++ if (rval < 0) ++ return rval; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); ++ if (rval < 0) ++ return rval; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); ++ if (rval < 0) ++ return rval; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); ++ if (rval < 0) ++ return rval; ++ ++ /* Lane op clock ratio does not apply here. */ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, ++ DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); ++ if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) ++ return rval; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); ++ if (rval < 0) ++ return rval; ++ ++ return smiapp_write( ++ sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); ++} ++ ++static int smiapp_pll_update(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ struct smiapp_pll_limits lim = { ++ .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], ++ .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], ++ .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], ++ .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], ++ .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], ++ .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], ++ .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], ++ .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], ++ ++ .op.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], ++ .op.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], ++ .op.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], ++ .op.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], ++ .op.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], ++ .op.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], ++ .op.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], ++ .op.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], ++ ++ .vt.min_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], ++ .vt.max_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], ++ .vt.min_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], ++ .vt.max_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], ++ .vt.min_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], ++ .vt.max_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], ++ .vt.min_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], ++ .vt.max_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], ++ ++ .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], ++ .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], ++ }; ++ struct smiapp_pll *pll = &sensor->pll; ++ int rval; ++ ++ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { ++ /* ++ * Fill in operational clock divisors limits from the ++ * video timing ones. On profile 0 sensors the ++ * requirements regarding them are essentially the ++ * same as on VT ones. ++ */ ++ lim.op = lim.vt; ++ } ++ ++ pll->binning_horizontal = sensor->binning_horizontal; ++ pll->binning_vertical = sensor->binning_vertical; ++ pll->link_freq = ++ sensor->link_freq->qmenu_int[sensor->link_freq->val]; ++ pll->scale_m = sensor->scale_m; ++ pll->bits_per_pixel = sensor->csi_format->compressed; ++ ++ rval = smiapp_pll_calculate(&client->dev, &lim, pll); ++ if (rval < 0) ++ return rval; ++ ++ sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; ++ sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; ++ ++ return 0; ++} ++ ++ ++/* ++ * ++ * V4L2 Controls handling ++ * ++ */ ++ ++static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) ++{ ++ struct v4l2_ctrl *ctrl = sensor->exposure; ++ int max; ++ ++ max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height ++ + sensor->vblank->val ++ - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; ++ ++ ctrl->maximum = max; ++ if (ctrl->default_value > max) ++ ctrl->default_value = max; ++ if (ctrl->val > max) ++ ctrl->val = max; ++ if (ctrl->cur.val > max) ++ ctrl->cur.val = max; ++} ++ ++/* ++ * Order matters. ++ * ++ * 1. Bits-per-pixel, descending. ++ * 2. Bits-per-pixel compressed, descending. ++ * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel ++ * orders must be defined. ++ */ ++static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { ++ { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, ++ { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, ++ { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, ++ { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, ++ { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, ++ { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, ++ { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, ++ { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, ++ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, ++ { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, ++ { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, ++ { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, ++ { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, ++ { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, ++ { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, ++ { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, ++}; ++ ++const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; ++ ++#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ ++ - (unsigned long)smiapp_csi_data_formats) \ ++ / sizeof(*smiapp_csi_data_formats)) ++ ++static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int flip = 0; ++ ++ if (sensor->hflip) { ++ if (sensor->hflip->val) ++ flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; ++ ++ if (sensor->vflip->val) ++ flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; ++ } ++ ++ flip ^= sensor->hvflip_inv_mask; ++ ++ dev_dbg(&client->dev, "flip %d\n", flip); ++ return sensor->default_pixel_order ^ flip; ++} ++ ++static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int csi_format_idx = ++ to_csi_format_idx(sensor->csi_format) & ~3; ++ unsigned int internal_csi_format_idx = ++ to_csi_format_idx(sensor->internal_csi_format) & ~3; ++ unsigned int pixel_order = smiapp_pixel_order(sensor); ++ ++ sensor->mbus_frame_fmts = ++ sensor->default_mbus_frame_fmts << pixel_order; ++ sensor->csi_format = ++ &smiapp_csi_data_formats[csi_format_idx + pixel_order]; ++ sensor->internal_csi_format = ++ &smiapp_csi_data_formats[internal_csi_format_idx ++ + pixel_order]; ++ ++ BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order ++ >= ARRAY_SIZE(smiapp_csi_data_formats)); ++ BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); ++ ++ dev_dbg(&client->dev, "new pixel order %s\n", ++ pixel_order_str[pixel_order]); ++} ++ ++static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct smiapp_sensor *sensor = ++ container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) ++ ->sensor; ++ u32 orient = 0; ++ int exposure; ++ int rval; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_ANALOGUE_GAIN: ++ return smiapp_write( ++ sensor, ++ SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); ++ ++ case V4L2_CID_EXPOSURE: ++ return smiapp_write( ++ sensor, ++ SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); ++ ++ case V4L2_CID_HFLIP: ++ case V4L2_CID_VFLIP: ++ if (sensor->streaming) ++ return -EBUSY; ++ ++ if (sensor->hflip->val) ++ orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; ++ ++ if (sensor->vflip->val) ++ orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; ++ ++ orient ^= sensor->hvflip_inv_mask; ++ rval = smiapp_write(sensor, ++ SMIAPP_REG_U8_IMAGE_ORIENTATION, ++ orient); ++ if (rval < 0) ++ return rval; ++ ++ smiapp_update_mbus_formats(sensor); ++ ++ return 0; ++ ++ case V4L2_CID_VBLANK: ++ exposure = sensor->exposure->val; ++ ++ __smiapp_update_exposure_limits(sensor); ++ ++ if (exposure > sensor->exposure->maximum) { ++ sensor->exposure->val = ++ sensor->exposure->maximum; ++ rval = smiapp_set_ctrl( ++ sensor->exposure); ++ if (rval < 0) ++ return rval; ++ } ++ ++ return smiapp_write( ++ sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height ++ + ctrl->val); ++ ++ case V4L2_CID_HBLANK: ++ return smiapp_write( ++ sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width ++ + ctrl->val); ++ ++ case V4L2_CID_LINK_FREQ: ++ if (sensor->streaming) ++ return -EBUSY; ++ ++ return smiapp_pll_update(sensor); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { ++ .s_ctrl = smiapp_set_ctrl, ++}; ++ ++static int smiapp_init_controls(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int max; ++ int rval; ++ ++ rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); ++ if (rval) ++ return rval; ++ sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; ++ ++ sensor->analog_gain = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_ANALOGUE_GAIN, ++ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], ++ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], ++ max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), ++ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); ++ ++ /* Exposure limits will be updated soon, use just something here. */ ++ sensor->exposure = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0, 1, 0); ++ ++ sensor->hflip = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ sensor->vflip = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ++ sensor->vblank = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_VBLANK, 0, 1, 1, 0); ++ ++ if (sensor->vblank) ++ sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; ++ ++ sensor->hblank = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_HBLANK, 0, 1, 1, 0); ++ ++ if (sensor->hblank) ++ sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; ++ ++ sensor->pixel_rate_parray = v4l2_ctrl_new_std( ++ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); ++ ++ if (sensor->pixel_array->ctrl_handler.error) { ++ dev_err(&client->dev, ++ "pixel array controls initialization failed (%d)\n", ++ sensor->pixel_array->ctrl_handler.error); ++ rval = sensor->pixel_array->ctrl_handler.error; ++ goto error; ++ } ++ ++ sensor->pixel_array->sd.ctrl_handler = ++ &sensor->pixel_array->ctrl_handler; ++ ++ v4l2_ctrl_cluster(2, &sensor->hflip); ++ ++ rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); ++ if (rval) ++ goto error; ++ sensor->src->ctrl_handler.lock = &sensor->mutex; ++ ++ for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); ++ ++ sensor->link_freq = v4l2_ctrl_new_int_menu( ++ &sensor->src->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_LINK_FREQ, max, 0, ++ sensor->platform_data->op_sys_clock); ++ ++ sensor->pixel_rate_csi = v4l2_ctrl_new_std( ++ &sensor->src->ctrl_handler, &smiapp_ctrl_ops, ++ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); ++ ++ if (sensor->src->ctrl_handler.error) { ++ dev_err(&client->dev, ++ "src controls initialization failed (%d)\n", ++ sensor->src->ctrl_handler.error); ++ rval = sensor->src->ctrl_handler.error; ++ goto error; ++ } ++ ++ sensor->src->sd.ctrl_handler = ++ &sensor->src->ctrl_handler; ++ ++ return 0; ++ ++error: ++ v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); ++ v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); ++ ++ return rval; ++} ++ ++static void smiapp_free_controls(struct smiapp_sensor *sensor) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < sensor->ssds_used; i++) ++ v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); ++} ++ ++static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, ++ unsigned int n) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int i; ++ u32 val; ++ int rval; ++ ++ for (i = 0; i < n; i++) { ++ rval = smiapp_read( ++ sensor, smiapp_reg_limits[limit[i]].addr, &val); ++ if (rval) ++ return rval; ++ sensor->limits[limit[i]] = val; ++ dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", ++ smiapp_reg_limits[limit[i]].addr, ++ smiapp_reg_limits[limit[i]].what, val, val); ++ } ++ ++ return 0; ++} ++ ++static int smiapp_get_all_limits(struct smiapp_sensor *sensor) ++{ ++ unsigned int i; ++ int rval; ++ ++ for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { ++ rval = smiapp_get_limits(sensor, &i, 1); ++ if (rval < 0) ++ return rval; ++ } ++ ++ if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) ++ smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); ++ ++ return 0; ++} ++ ++static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ static u32 const limits[] = { ++ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, ++ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, ++ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, ++ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, ++ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, ++ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, ++ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, ++ }; ++ static u32 const limits_replace[] = { ++ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, ++ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, ++ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, ++ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, ++ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, ++ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, ++ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, ++ }; ++ unsigned int i; ++ int rval; ++ ++ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == ++ SMIAPP_BINNING_CAPABILITY_NO) { ++ for (i = 0; i < ARRAY_SIZE(limits); i++) ++ sensor->limits[limits[i]] = ++ sensor->limits[limits_replace[i]]; ++ ++ return 0; ++ } ++ ++ rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); ++ if (rval < 0) ++ return rval; ++ ++ /* ++ * Sanity check whether the binning limits are valid. If not, ++ * use the non-binning ones. ++ */ ++ if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] ++ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] ++ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) ++ return 0; ++ ++ for (i = 0; i < ARRAY_SIZE(limits); i++) { ++ dev_dbg(&client->dev, ++ "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", ++ smiapp_reg_limits[limits[i]].addr, ++ smiapp_reg_limits[limits[i]].what, ++ sensor->limits[limits_replace[i]], ++ sensor->limits[limits_replace[i]]); ++ sensor->limits[limits[i]] = ++ sensor->limits[limits_replace[i]]; ++ } ++ ++ return 0; ++} ++ ++static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int type, n; ++ unsigned int i, pixel_order; ++ int rval; ++ ++ rval = smiapp_read( ++ sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); ++ if (rval) ++ return rval; ++ ++ dev_dbg(&client->dev, "data_format_model_type %d\n", type); ++ ++ rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, ++ &pixel_order); ++ if (rval) ++ return rval; ++ ++ if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { ++ dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); ++ return -EINVAL; ++ } ++ ++ dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, ++ pixel_order_str[pixel_order]); ++ ++ switch (type) { ++ case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: ++ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; ++ break; ++ case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: ++ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ sensor->default_pixel_order = pixel_order; ++ sensor->mbus_frame_fmts = 0; ++ ++ for (i = 0; i < n; i++) { ++ unsigned int fmt, j; ++ ++ rval = smiapp_read( ++ sensor, ++ SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); ++ if (rval) ++ return rval; ++ ++ dev_dbg(&client->dev, "bpp %d, compressed %d\n", ++ fmt >> 8, (u8)fmt); ++ ++ for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { ++ const struct smiapp_csi_data_format *f = ++ &smiapp_csi_data_formats[j]; ++ ++ if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) ++ continue; ++ ++ if (f->width != fmt >> 8 || f->compressed != (u8)fmt) ++ continue; ++ ++ dev_dbg(&client->dev, "jolly good! %d\n", j); ++ ++ sensor->default_mbus_frame_fmts |= 1 << j; ++ if (!sensor->csi_format ++ || f->width > sensor->csi_format->width ++ || (f->width == sensor->csi_format->width ++ && f->compressed ++ > sensor->csi_format->compressed)) { ++ sensor->csi_format = f; ++ sensor->internal_csi_format = f; ++ } ++ } ++ } ++ ++ if (!sensor->csi_format) { ++ dev_err(&client->dev, "no supported mbus code found\n"); ++ return -EINVAL; ++ } ++ ++ smiapp_update_mbus_formats(sensor); ++ ++ return 0; ++} ++ ++static void smiapp_update_blanking(struct smiapp_sensor *sensor) ++{ ++ struct v4l2_ctrl *vblank = sensor->vblank; ++ struct v4l2_ctrl *hblank = sensor->hblank; ++ ++ vblank->minimum = ++ max_t(int, ++ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], ++ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); ++ vblank->maximum = ++ sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; ++ ++ vblank->val = clamp_t(int, vblank->val, ++ vblank->minimum, vblank->maximum); ++ vblank->default_value = vblank->minimum; ++ vblank->val = vblank->val; ++ vblank->cur.val = vblank->val; ++ ++ hblank->minimum = ++ max_t(int, ++ sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, ++ sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); ++ hblank->maximum = ++ sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; ++ ++ hblank->val = clamp_t(int, hblank->val, ++ hblank->minimum, hblank->maximum); ++ hblank->default_value = hblank->minimum; ++ hblank->val = hblank->val; ++ hblank->cur.val = hblank->val; ++ ++ __smiapp_update_exposure_limits(sensor); ++} ++ ++static int smiapp_update_mode(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int binning_mode; ++ int rval; ++ ++ dev_dbg(&client->dev, "frame size: %dx%d\n", ++ sensor->src->crop[SMIAPP_PAD_SRC].width, ++ sensor->src->crop[SMIAPP_PAD_SRC].height); ++ dev_dbg(&client->dev, "csi format width: %d\n", ++ sensor->csi_format->width); ++ ++ /* Binning has to be set up here; it affects limits */ ++ if (sensor->binning_horizontal == 1 && ++ sensor->binning_vertical == 1) { ++ binning_mode = 0; ++ } else { ++ u8 binning_type = ++ (sensor->binning_horizontal << 4) ++ | sensor->binning_vertical; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); ++ if (rval < 0) ++ return rval; ++ ++ binning_mode = 1; ++ } ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); ++ if (rval < 0) ++ return rval; ++ ++ /* Get updated limits due to binning */ ++ rval = smiapp_get_limits_binning(sensor); ++ if (rval < 0) ++ return rval; ++ ++ rval = smiapp_pll_update(sensor); ++ if (rval < 0) ++ return rval; ++ ++ /* Output from pixel array, including blanking */ ++ smiapp_update_blanking(sensor); ++ ++ dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); ++ dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); ++ ++ dev_dbg(&client->dev, "real timeperframe\t100/%d\n", ++ sensor->pll.vt_pix_clk_freq_hz / ++ ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width ++ + sensor->hblank->val) * ++ (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height ++ + sensor->vblank->val) / 100)); ++ ++ return 0; ++} ++ ++/* ++ * ++ * SMIA++ NVM handling ++ * ++ */ ++static int smiapp_read_nvm(struct smiapp_sensor *sensor, ++ unsigned char *nvm) ++{ ++ u32 i, s, p, np, v; ++ int rval = 0, rval2; ++ ++ np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; ++ for (p = 0; p < np; p++) { ++ rval = smiapp_write( ++ sensor, ++ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); ++ if (rval) ++ goto out; ++ ++ rval = smiapp_write(sensor, ++ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, ++ SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | ++ SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); ++ if (rval) ++ goto out; ++ ++ for (i = 0; i < 1000; i++) { ++ rval = smiapp_read( ++ sensor, ++ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); ++ ++ if (rval) ++ goto out; ++ ++ if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) ++ break; ++ ++ if (--i == 0) { ++ rval = -ETIMEDOUT; ++ goto out; ++ } ++ ++ } ++ ++ for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { ++ rval = smiapp_read( ++ sensor, ++ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, ++ &v); ++ if (rval) ++ goto out; ++ ++ *nvm++ = v; ++ } ++ } ++ ++out: ++ rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); ++ if (rval < 0) ++ return rval; ++ else ++ return rval2; ++} ++ ++/* ++ * ++ * SMIA++ CCI address control ++ * ++ */ ++static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int rval; ++ u32 val; ++ ++ client->addr = sensor->platform_data->i2c_addr_dfl; ++ ++ rval = smiapp_write(sensor, ++ SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, ++ sensor->platform_data->i2c_addr_alt << 1); ++ if (rval) ++ return rval; ++ ++ client->addr = sensor->platform_data->i2c_addr_alt; ++ ++ /* verify addr change went ok */ ++ rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); ++ if (rval) ++ return rval; ++ ++ if (val != sensor->platform_data->i2c_addr_alt << 1) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++/* ++ * ++ * SMIA++ Mode Control ++ * ++ */ ++static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) ++{ ++ struct smiapp_flash_strobe_parms *strobe_setup; ++ unsigned int ext_freq = sensor->platform_data->ext_clk; ++ u32 tmp; ++ u32 strobe_adjustment; ++ u32 strobe_width_high_rs; ++ int rval; ++ ++ strobe_setup = sensor->platform_data->strobe_setup; ++ ++ /* ++ * How to calculate registers related to strobe length. Please ++ * do not change, or if you do at least know what you're ++ * doing. :-) ++ * ++ * Sakari Ailus 2010-10-25 ++ * ++ * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl ++ * / EXTCLK freq [Hz]) * flash_strobe_adjustment ++ * ++ * tFlash_strobe_width_ctrl E N, [1 - 0xffff] ++ * flash_strobe_adjustment E N, [1 - 0xff] ++ * ++ * The formula above is written as below to keep it on one ++ * line: ++ * ++ * l / 10^6 = w / e * a ++ * ++ * Let's mark w * a by x: ++ * ++ * x = w * a ++ * ++ * Thus, we get: ++ * ++ * x = l * e / 10^6 ++ * ++ * The strobe width must be at least as long as requested, ++ * thus rounding upwards is needed. ++ * ++ * x = (l * e + 10^6 - 1) / 10^6 ++ * ----------------------------- ++ * ++ * Maximum possible accuracy is wanted at all times. Thus keep ++ * a as small as possible. ++ * ++ * Calculate a, assuming maximum w, with rounding upwards: ++ * ++ * a = (x + (2^16 - 1) - 1) / (2^16 - 1) ++ * ------------------------------------- ++ * ++ * Thus, we also get w, with that a, with rounding upwards: ++ * ++ * w = (x + a - 1) / a ++ * ------------------- ++ * ++ * To get limits: ++ * ++ * x E [1, (2^16 - 1) * (2^8 - 1)] ++ * ++ * Substituting maximum x to the original formula (with rounding), ++ * the maximum l is thus ++ * ++ * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 ++ * ++ * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e ++ * -------------------------------------------------- ++ * ++ * flash_strobe_length must be clamped between 1 and ++ * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. ++ * ++ * Then, ++ * ++ * flash_strobe_adjustment = ((flash_strobe_length * ++ * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) ++ * ++ * tFlash_strobe_width_ctrl = ((flash_strobe_length * ++ * EXTCLK freq + 10^6 - 1) / 10^6 + ++ * flash_strobe_adjustment - 1) / flash_strobe_adjustment ++ */ ++ tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - ++ 1000000 + 1, ext_freq); ++ strobe_setup->strobe_width_high_us = ++ clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); ++ ++ tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + ++ 1000000 - 1), 1000000ULL); ++ strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); ++ strobe_width_high_rs = (tmp + strobe_adjustment - 1) / ++ strobe_adjustment; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, ++ strobe_setup->mode); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, ++ strobe_adjustment); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, ++ strobe_width_high_rs); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, ++ strobe_setup->strobe_delay); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, ++ strobe_setup->stobe_start_point); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, ++ strobe_setup->trigger); ++ ++out: ++ sensor->platform_data->strobe_setup->trigger = 0; ++ ++ return rval; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Power management ++ */ ++ ++static int smiapp_power_on(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int sleep; ++ int rval; ++ ++ rval = regulator_enable(sensor->vana); ++ if (rval) { ++ dev_err(&client->dev, "failed to enable vana regulator\n"); ++ return rval; ++ } ++ usleep_range(1000, 1000); ++ ++ if (sensor->platform_data->set_xclk) ++ rval = sensor->platform_data->set_xclk( ++ &sensor->src->sd, sensor->platform_data->ext_clk); ++ else ++ rval = clk_enable(sensor->ext_clk); ++ if (rval < 0) { ++ dev_dbg(&client->dev, "failed to set xclk\n"); ++ goto out_xclk_fail; ++ } ++ usleep_range(1000, 1000); ++ ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_set_value(sensor->platform_data->xshutdown, 1); ++ ++ sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); ++ usleep_range(sleep, sleep); ++ ++ /* ++ * Failures to respond to the address change command have been noticed. ++ * Those failures seem to be caused by the sensor requiring a longer ++ * boot time than advertised. An additional 10ms delay seems to work ++ * around the issue, but the SMIA++ I2C write retry hack makes the delay ++ * unnecessary. The failures need to be investigated to find a proper ++ * fix, and a delay will likely need to be added here if the I2C write ++ * retry hack is reverted before the root cause of the boot time issue ++ * is found. ++ */ ++ ++ if (sensor->platform_data->i2c_addr_alt) { ++ rval = smiapp_change_cci_addr(sensor); ++ if (rval) { ++ dev_err(&client->dev, "cci address change error\n"); ++ goto out_cci_addr_fail; ++ } ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, ++ SMIAPP_SOFTWARE_RESET); ++ if (rval < 0) { ++ dev_err(&client->dev, "software reset failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ if (sensor->platform_data->i2c_addr_alt) { ++ rval = smiapp_change_cci_addr(sensor); ++ if (rval) { ++ dev_err(&client->dev, "cci address change error\n"); ++ goto out_cci_addr_fail; ++ } ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, ++ SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); ++ if (rval) { ++ dev_err(&client->dev, "compression mode set failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, ++ sensor->platform_data->ext_clk / (1000000 / (1 << 8))); ++ if (rval) { ++ dev_err(&client->dev, "extclk frequency set failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, ++ sensor->platform_data->lanes - 1); ++ if (rval) { ++ dev_err(&client->dev, "csi lane mode set failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, ++ SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); ++ if (rval) { ++ dev_err(&client->dev, "fast standby set failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, ++ sensor->platform_data->csi_signalling_mode); ++ if (rval) { ++ dev_err(&client->dev, "csi signalling mode set failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ /* DPHY control done by sensor based on requested link rate */ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, ++ SMIAPP_DPHY_CTRL_UI); ++ if (rval < 0) ++ return rval; ++ ++ rval = smiapp_call_quirk(sensor, post_poweron); ++ if (rval) { ++ dev_err(&client->dev, "post_poweron quirks failed\n"); ++ goto out_cci_addr_fail; ++ } ++ ++ /* Are we still initialising...? If yes, return here. */ ++ if (!sensor->pixel_array) ++ return 0; ++ ++ rval = v4l2_ctrl_handler_setup( ++ &sensor->pixel_array->ctrl_handler); ++ if (rval) ++ goto out_cci_addr_fail; ++ ++ rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); ++ if (rval) ++ goto out_cci_addr_fail; ++ ++ mutex_lock(&sensor->mutex); ++ rval = smiapp_update_mode(sensor); ++ mutex_unlock(&sensor->mutex); ++ if (rval < 0) ++ goto out_cci_addr_fail; ++ ++ return 0; ++ ++out_cci_addr_fail: ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_set_value(sensor->platform_data->xshutdown, 0); ++ if (sensor->platform_data->set_xclk) ++ sensor->platform_data->set_xclk(&sensor->src->sd, 0); ++ else ++ clk_disable(sensor->ext_clk); ++ ++out_xclk_fail: ++ regulator_disable(sensor->vana); ++ return rval; ++} ++ ++static void smiapp_power_off(struct smiapp_sensor *sensor) ++{ ++ /* ++ * Currently power/clock to lens are enable/disabled separately ++ * but they are essentially the same signals. So if the sensor is ++ * powered off while the lens is powered on the sensor does not ++ * really see a power off and next time the cci address change ++ * will fail. So do a soft reset explicitly here. ++ */ ++ if (sensor->platform_data->i2c_addr_alt) ++ smiapp_write(sensor, ++ SMIAPP_REG_U8_SOFTWARE_RESET, ++ SMIAPP_SOFTWARE_RESET); ++ ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_set_value(sensor->platform_data->xshutdown, 0); ++ if (sensor->platform_data->set_xclk) ++ sensor->platform_data->set_xclk(&sensor->src->sd, 0); ++ else ++ clk_disable(sensor->ext_clk); ++ usleep_range(5000, 5000); ++ regulator_disable(sensor->vana); ++ sensor->streaming = 0; ++} ++ ++static int smiapp_set_power(struct v4l2_subdev *subdev, int on) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int ret = 0; ++ ++ mutex_lock(&sensor->power_mutex); ++ ++ /* ++ * If the power count is modified from 0 to != 0 or from != 0 ++ * to 0, update the power state. ++ */ ++ if (!sensor->power_count == !on) ++ goto out; ++ ++ if (on) { ++ /* Power on and perform initialisation. */ ++ ret = smiapp_power_on(sensor); ++ if (ret < 0) ++ goto out; ++ } else { ++ smiapp_power_off(sensor); ++ } ++ ++ /* Update the power count. */ ++ sensor->power_count += on ? 1 : -1; ++ WARN_ON(sensor->power_count < 0); ++ ++out: ++ mutex_unlock(&sensor->power_mutex); ++ return ret; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Video stream management ++ */ ++ ++static int smiapp_start_streaming(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int rval; ++ ++ mutex_lock(&sensor->mutex); ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, ++ (sensor->csi_format->width << 8) | ++ sensor->csi_format->compressed); ++ if (rval) ++ goto out; ++ ++ rval = smiapp_pll_configure(sensor); ++ if (rval) ++ goto out; ++ ++ /* Analog crop start coordinates */ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); ++ if (rval < 0) ++ goto out; ++ ++ /* Analog crop end coordinates */ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_X_ADDR_END, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left ++ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_Y_ADDR_END, ++ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top ++ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); ++ if (rval < 0) ++ goto out; ++ ++ /* ++ * Output from pixel array, including blanking, is set using ++ * controls below. No need to set here. ++ */ ++ ++ /* Digital crop */ ++ if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] ++ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, ++ sensor->scaler->crop[SMIAPP_PAD_SINK].left); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, ++ sensor->scaler->crop[SMIAPP_PAD_SINK].top); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, ++ sensor->scaler->crop[SMIAPP_PAD_SINK].width); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write( ++ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, ++ sensor->scaler->crop[SMIAPP_PAD_SINK].height); ++ if (rval < 0) ++ goto out; ++ } ++ ++ /* Scaling */ ++ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] ++ != SMIAPP_SCALING_CAPABILITY_NONE) { ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, ++ sensor->scaling_mode); ++ if (rval < 0) ++ goto out; ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, ++ sensor->scale_m); ++ if (rval < 0) ++ goto out; ++ } ++ ++ /* Output size from sensor */ ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, ++ sensor->src->crop[SMIAPP_PAD_SRC].width); ++ if (rval < 0) ++ goto out; ++ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, ++ sensor->src->crop[SMIAPP_PAD_SRC].height); ++ if (rval < 0) ++ goto out; ++ ++ if ((sensor->flash_capability & ++ (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | ++ SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && ++ sensor->platform_data->strobe_setup != NULL && ++ sensor->platform_data->strobe_setup->trigger != 0) { ++ rval = smiapp_setup_flash_strobe(sensor); ++ if (rval) ++ goto out; ++ } ++ ++ rval = smiapp_call_quirk(sensor, pre_streamon); ++ if (rval) { ++ dev_err(&client->dev, "pre_streamon quirks failed\n"); ++ goto out; ++ } ++ ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, ++ SMIAPP_MODE_SELECT_STREAMING); ++ ++out: ++ mutex_unlock(&sensor->mutex); ++ ++ return rval; ++} ++ ++static int smiapp_stop_streaming(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int rval; ++ ++ mutex_lock(&sensor->mutex); ++ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, ++ SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); ++ if (rval) ++ goto out; ++ ++ rval = smiapp_call_quirk(sensor, post_streamoff); ++ if (rval) ++ dev_err(&client->dev, "post_streamoff quirks failed\n"); ++ ++out: ++ mutex_unlock(&sensor->mutex); ++ return rval; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev video operations ++ */ ++ ++static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int rval; ++ ++ if (sensor->streaming == enable) ++ return 0; ++ ++ if (enable) { ++ sensor->streaming = 1; ++ rval = smiapp_start_streaming(sensor); ++ if (rval < 0) ++ sensor->streaming = 0; ++ } else { ++ rval = smiapp_stop_streaming(sensor); ++ sensor->streaming = 0; ++ } ++ ++ return rval; ++} ++ ++static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_mbus_code_enum *code) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ unsigned int i; ++ int idx = -1; ++ int rval = -EINVAL; ++ ++ mutex_lock(&sensor->mutex); ++ ++ dev_err(&client->dev, "subdev %s, pad %d, index %d\n", ++ subdev->name, code->pad, code->index); ++ ++ if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { ++ if (code->index) ++ goto out; ++ ++ code->code = sensor->internal_csi_format->code; ++ rval = 0; ++ goto out; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { ++ if (sensor->mbus_frame_fmts & (1 << i)) ++ idx++; ++ ++ if (idx == code->index) { ++ code->code = smiapp_csi_data_formats[i].code; ++ dev_err(&client->dev, "found index %d, i %d, code %x\n", ++ code->index, i, code->code); ++ rval = 0; ++ break; ++ } ++ } ++ ++out: ++ mutex_unlock(&sensor->mutex); ++ ++ return rval; ++} ++ ++static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, ++ unsigned int pad) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ ++ if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) ++ return sensor->csi_format->code; ++ else ++ return sensor->internal_csi_format->code; ++} ++ ++static int __smiapp_get_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ ++ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { ++ fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); ++ } else { ++ struct v4l2_rect *r; ++ ++ if (fmt->pad == ssd->source_pad) ++ r = &ssd->crop[ssd->source_pad]; ++ else ++ r = &ssd->sink_fmt; ++ ++ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); ++ fmt->format.width = r->width; ++ fmt->format.height = r->height; ++ } ++ ++ return 0; ++} ++ ++static int smiapp_get_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int rval; ++ ++ mutex_lock(&sensor->mutex); ++ rval = __smiapp_get_format(subdev, fh, fmt); ++ mutex_unlock(&sensor->mutex); ++ ++ return rval; ++} ++ ++static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_rect **crops, ++ struct v4l2_rect **comps, int which) ++{ ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ unsigned int i; ++ ++ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ if (crops) ++ for (i = 0; i < subdev->entity.num_pads; i++) ++ crops[i] = &ssd->crop[i]; ++ if (comps) ++ *comps = &ssd->compose; ++ } else { ++ if (crops) { ++ for (i = 0; i < subdev->entity.num_pads; i++) { ++ crops[i] = v4l2_subdev_get_try_crop(fh, i); ++ BUG_ON(!crops[i]); ++ } ++ } ++ if (comps) { ++ *comps = v4l2_subdev_get_try_compose(fh, ++ SMIAPP_PAD_SINK); ++ BUG_ON(!*comps); ++ } ++ } ++} ++ ++/* Changes require propagation only on sink pad. */ ++static void smiapp_propagate(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, int which, ++ int target) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ struct v4l2_rect *comp, *crops[SMIAPP_PADS]; ++ ++ smiapp_get_crop_compose(subdev, fh, crops, &comp, which); ++ ++ switch (target) { ++ case V4L2_SEL_TGT_CROP: ++ comp->width = crops[SMIAPP_PAD_SINK]->width; ++ comp->height = crops[SMIAPP_PAD_SINK]->height; ++ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ if (ssd == sensor->scaler) { ++ sensor->scale_m = ++ sensor->limits[ ++ SMIAPP_LIMIT_SCALER_N_MIN]; ++ sensor->scaling_mode = ++ SMIAPP_SCALING_MODE_NONE; ++ } else if (ssd == sensor->binner) { ++ sensor->binning_horizontal = 1; ++ sensor->binning_vertical = 1; ++ } ++ } ++ /* Fall through */ ++ case V4L2_SEL_TGT_COMPOSE: ++ *crops[SMIAPP_PAD_SRC] = *comp; ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static const struct smiapp_csi_data_format ++*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) ++{ ++ const struct smiapp_csi_data_format *csi_format = sensor->csi_format; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { ++ if (sensor->mbus_frame_fmts & (1 << i) ++ && smiapp_csi_data_formats[i].code == code) ++ return &smiapp_csi_data_formats[i]; ++ } ++ ++ return csi_format; ++} ++ ++static int smiapp_set_format(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_format *fmt) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ struct v4l2_rect *crops[SMIAPP_PADS]; ++ ++ mutex_lock(&sensor->mutex); ++ ++ /* ++ * Media bus code is changeable on src subdev's source pad. On ++ * other source pads we just get format here. ++ */ ++ if (fmt->pad == ssd->source_pad) { ++ u32 code = fmt->format.code; ++ int rval = __smiapp_get_format(subdev, fh, fmt); ++ ++ if (!rval && subdev == &sensor->src->sd) { ++ const struct smiapp_csi_data_format *csi_format = ++ smiapp_validate_csi_data_format(sensor, code); ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ sensor->csi_format = csi_format; ++ fmt->format.code = csi_format->code; ++ } ++ ++ mutex_unlock(&sensor->mutex); ++ return rval; ++ } ++ ++ /* Sink pad. Width and height are changeable here. */ ++ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); ++ fmt->format.width &= ~1; ++ fmt->format.height &= ~1; ++ ++ fmt->format.width = ++ clamp(fmt->format.width, ++ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], ++ sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); ++ fmt->format.height = ++ clamp(fmt->format.height, ++ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], ++ sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); ++ ++ smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); ++ ++ crops[ssd->sink_pad]->left = 0; ++ crops[ssd->sink_pad]->top = 0; ++ crops[ssd->sink_pad]->width = fmt->format.width; ++ crops[ssd->sink_pad]->height = fmt->format.height; ++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ ssd->sink_fmt = *crops[ssd->sink_pad]; ++ smiapp_propagate(subdev, fh, fmt->which, ++ V4L2_SEL_TGT_CROP); ++ ++ mutex_unlock(&sensor->mutex); ++ ++ return 0; ++} ++ ++/* ++ * Calculate goodness of scaled image size compared to expected image ++ * size and flags provided. ++ */ ++#define SCALING_GOODNESS 100000 ++#define SCALING_GOODNESS_EXTREME 100000000 ++static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, ++ int h, int ask_h, u32 flags) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ int val = 0; ++ ++ w &= ~1; ++ ask_w &= ~1; ++ h &= ~1; ++ ask_h &= ~1; ++ ++ if (flags & V4L2_SEL_FLAG_GE) { ++ if (w < ask_w) ++ val -= SCALING_GOODNESS; ++ if (h < ask_h) ++ val -= SCALING_GOODNESS; ++ } ++ ++ if (flags & V4L2_SEL_FLAG_LE) { ++ if (w > ask_w) ++ val -= SCALING_GOODNESS; ++ if (h > ask_h) ++ val -= SCALING_GOODNESS; ++ } ++ ++ val -= abs(w - ask_w); ++ val -= abs(h - ask_h); ++ ++ if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) ++ val -= SCALING_GOODNESS_EXTREME; ++ ++ dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", ++ w, ask_h, h, ask_h, val); ++ ++ return val; ++} ++ ++static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel, ++ struct v4l2_rect **crops, ++ struct v4l2_rect *comp) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ unsigned int i; ++ unsigned int binh = 1, binv = 1; ++ unsigned int best = scaling_goodness( ++ subdev, ++ crops[SMIAPP_PAD_SINK]->width, sel->r.width, ++ crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); ++ ++ for (i = 0; i < sensor->nbinning_subtypes; i++) { ++ int this = scaling_goodness( ++ subdev, ++ crops[SMIAPP_PAD_SINK]->width ++ / sensor->binning_subtypes[i].horizontal, ++ sel->r.width, ++ crops[SMIAPP_PAD_SINK]->height ++ / sensor->binning_subtypes[i].vertical, ++ sel->r.height, sel->flags); ++ ++ if (this > best) { ++ binh = sensor->binning_subtypes[i].horizontal; ++ binv = sensor->binning_subtypes[i].vertical; ++ best = this; ++ } ++ } ++ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ sensor->binning_vertical = binv; ++ sensor->binning_horizontal = binh; ++ } ++ ++ sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; ++ sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; ++} ++ ++/* ++ * Calculate best scaling ratio and mode for given output resolution. ++ * ++ * Try all of these: horizontal ratio, vertical ratio and smallest ++ * size possible (horizontally). ++ * ++ * Also try whether horizontal scaler or full scaler gives a better ++ * result. ++ */ ++static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel, ++ struct v4l2_rect **crops, ++ struct v4l2_rect *comp) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ u32 min, max, a, b, max_m; ++ u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; ++ int mode = SMIAPP_SCALING_MODE_HORIZONTAL; ++ u32 try[4]; ++ u32 ntry = 0; ++ unsigned int i; ++ int best = INT_MIN; ++ ++ sel->r.width = min_t(unsigned int, sel->r.width, ++ crops[SMIAPP_PAD_SINK]->width); ++ sel->r.height = min_t(unsigned int, sel->r.height, ++ crops[SMIAPP_PAD_SINK]->height); ++ ++ a = crops[SMIAPP_PAD_SINK]->width ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; ++ b = crops[SMIAPP_PAD_SINK]->height ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; ++ max_m = crops[SMIAPP_PAD_SINK]->width ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] ++ / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; ++ ++ a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], ++ max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); ++ b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], ++ max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); ++ max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], ++ max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); ++ ++ dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); ++ ++ min = min(max_m, min(a, b)); ++ max = min(max_m, max(a, b)); ++ ++ try[ntry] = min; ++ ntry++; ++ if (min != max) { ++ try[ntry] = max; ++ ntry++; ++ } ++ if (max != max_m) { ++ try[ntry] = min + 1; ++ ntry++; ++ if (min != max) { ++ try[ntry] = max + 1; ++ ntry++; ++ } ++ } ++ ++ for (i = 0; i < ntry; i++) { ++ int this = scaling_goodness( ++ subdev, ++ crops[SMIAPP_PAD_SINK]->width ++ / try[i] ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], ++ sel->r.width, ++ crops[SMIAPP_PAD_SINK]->height, ++ sel->r.height, ++ sel->flags); ++ ++ dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); ++ ++ if (this > best) { ++ scale_m = try[i]; ++ mode = SMIAPP_SCALING_MODE_HORIZONTAL; ++ best = this; ++ } ++ ++ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] ++ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) ++ continue; ++ ++ this = scaling_goodness( ++ subdev, crops[SMIAPP_PAD_SINK]->width ++ / try[i] ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], ++ sel->r.width, ++ crops[SMIAPP_PAD_SINK]->height ++ / try[i] ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], ++ sel->r.height, ++ sel->flags); ++ ++ if (this > best) { ++ scale_m = try[i]; ++ mode = SMIAPP_SCALING_MODE_BOTH; ++ best = this; ++ } ++ } ++ ++ sel->r.width = ++ (crops[SMIAPP_PAD_SINK]->width ++ / scale_m ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; ++ if (mode == SMIAPP_SCALING_MODE_BOTH) ++ sel->r.height = ++ (crops[SMIAPP_PAD_SINK]->height ++ / scale_m ++ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) ++ & ~1; ++ else ++ sel->r.height = crops[SMIAPP_PAD_SINK]->height; ++ ++ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ sensor->scale_m = scale_m; ++ sensor->scaling_mode = mode; ++ } ++} ++/* We're only called on source pads. This function sets scaling. */ ++static int smiapp_set_compose(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ struct v4l2_rect *comp, *crops[SMIAPP_PADS]; ++ ++ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); ++ ++ sel->r.top = 0; ++ sel->r.left = 0; ++ ++ if (ssd == sensor->binner) ++ smiapp_set_compose_binner(subdev, fh, sel, crops, comp); ++ else ++ smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); ++ ++ *comp = sel->r; ++ smiapp_propagate(subdev, fh, sel->which, ++ V4L2_SEL_TGT_COMPOSE); ++ ++ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) ++ return smiapp_update_mode(sensor); ++ ++ return 0; ++} ++ ++static int __smiapp_sel_supported(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ ++ /* We only implement crop in three places. */ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ if (ssd == sensor->pixel_array ++ && sel->pad == SMIAPP_PA_PAD_SRC) ++ return 0; ++ if (ssd == sensor->src ++ && sel->pad == SMIAPP_PAD_SRC) ++ return 0; ++ if (ssd == sensor->scaler ++ && sel->pad == SMIAPP_PAD_SINK ++ && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] ++ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) ++ return 0; ++ return -EINVAL; ++ case V4L2_SEL_TGT_COMPOSE: ++ case V4L2_SEL_TGT_COMPOSE_BOUNDS: ++ if (sel->pad == ssd->source_pad) ++ return -EINVAL; ++ if (ssd == sensor->binner) ++ return 0; ++ if (ssd == sensor->scaler ++ && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] ++ != SMIAPP_SCALING_CAPABILITY_NONE) ++ return 0; ++ /* Fall through */ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int smiapp_set_crop(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; ++ struct v4l2_rect _r; ++ ++ smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); ++ ++ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ if (sel->pad == ssd->sink_pad) ++ src_size = &ssd->sink_fmt; ++ else ++ src_size = &ssd->compose; ++ } else { ++ if (sel->pad == ssd->sink_pad) { ++ _r.left = 0; ++ _r.top = 0; ++ _r.width = v4l2_subdev_get_try_format(fh, sel->pad) ++ ->width; ++ _r.height = v4l2_subdev_get_try_format(fh, sel->pad) ++ ->height; ++ src_size = &_r; ++ } else { ++ src_size = ++ v4l2_subdev_get_try_compose( ++ fh, ssd->sink_pad); ++ } ++ } ++ ++ if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { ++ sel->r.left = 0; ++ sel->r.top = 0; ++ } ++ ++ sel->r.width = min(sel->r.width, src_size->width); ++ sel->r.height = min(sel->r.height, src_size->height); ++ ++ sel->r.left = min(sel->r.left, src_size->width - sel->r.width); ++ sel->r.top = min(sel->r.top, src_size->height - sel->r.height); ++ ++ *crops[sel->pad] = sel->r; ++ ++ if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) ++ smiapp_propagate(subdev, fh, sel->which, ++ V4L2_SEL_TGT_CROP); ++ ++ return 0; ++} ++ ++static int __smiapp_get_selection(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); ++ struct v4l2_rect *comp, *crops[SMIAPP_PADS]; ++ struct v4l2_rect sink_fmt; ++ int ret; ++ ++ ret = __smiapp_sel_supported(subdev, sel); ++ if (ret) ++ return ret; ++ ++ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); ++ ++ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ++ sink_fmt = ssd->sink_fmt; ++ } else { ++ struct v4l2_mbus_framefmt *fmt = ++ v4l2_subdev_get_try_format(fh, ssd->sink_pad); ++ ++ sink_fmt.left = 0; ++ sink_fmt.top = 0; ++ sink_fmt.width = fmt->width; ++ sink_fmt.height = fmt->height; ++ } ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ if (ssd == sensor->pixel_array) { ++ sel->r.width = ++ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; ++ sel->r.height = ++ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; ++ } else if (sel->pad == ssd->sink_pad) { ++ sel->r = sink_fmt; ++ } else { ++ sel->r = *comp; ++ } ++ break; ++ case V4L2_SEL_TGT_CROP: ++ case V4L2_SEL_TGT_COMPOSE_BOUNDS: ++ sel->r = *crops[sel->pad]; ++ break; ++ case V4L2_SEL_TGT_COMPOSE: ++ sel->r = *comp; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int smiapp_get_selection(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int rval; ++ ++ mutex_lock(&sensor->mutex); ++ rval = __smiapp_get_selection(subdev, fh, sel); ++ mutex_unlock(&sensor->mutex); ++ ++ return rval; ++} ++static int smiapp_set_selection(struct v4l2_subdev *subdev, ++ struct v4l2_subdev_fh *fh, ++ struct v4l2_subdev_selection *sel) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int ret; ++ ++ ret = __smiapp_sel_supported(subdev, sel); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&sensor->mutex); ++ ++ sel->r.left = max(0, sel->r.left & ~1); ++ sel->r.top = max(0, sel->r.top & ~1); ++ sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); ++ sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); ++ ++ sel->r.width = max_t(unsigned int, ++ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], ++ sel->r.width); ++ sel->r.height = max_t(unsigned int, ++ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], ++ sel->r.height); ++ ++ switch (sel->target) { ++ case V4L2_SEL_TGT_CROP: ++ ret = smiapp_set_crop(subdev, fh, sel); ++ break; ++ case V4L2_SEL_TGT_COMPOSE: ++ ret = smiapp_set_compose(subdev, fh, sel); ++ break; ++ default: ++ BUG(); ++ } ++ ++ mutex_unlock(&sensor->mutex); ++ return ret; ++} ++ ++static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ ++ *frames = sensor->frame_skip; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * sysfs attributes ++ */ ++ ++static ssize_t ++smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ unsigned int nbytes; ++ ++ if (!sensor->dev_init_done) ++ return -EBUSY; ++ ++ if (!sensor->nvm_size) { ++ /* NVM not read yet - read it now */ ++ sensor->nvm_size = sensor->platform_data->nvm_size; ++ if (smiapp_set_power(subdev, 1) < 0) ++ return -ENODEV; ++ if (smiapp_read_nvm(sensor, sensor->nvm)) { ++ dev_err(&client->dev, "nvm read failed\n"); ++ return -ENODEV; ++ } ++ smiapp_set_power(subdev, 0); ++ } ++ /* ++ * NVM is still way below a PAGE_SIZE, so we can safely ++ * assume this for now. ++ */ ++ nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); ++ memcpy(buf, sensor->nvm, nbytes); ++ ++ return nbytes; ++} ++static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); ++ ++static ssize_t ++smiapp_sysfs_ident_read(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct smiapp_module_info *minfo = &sensor->minfo; ++ ++ return snprintf(buf, PAGE_SIZE, "%2.2x%4.4x%2.2x\n", ++ minfo->manufacturer_id, minfo->model_id, ++ minfo->revision_number_major) + 1; ++} ++ ++static DEVICE_ATTR(ident, S_IRUGO, smiapp_sysfs_ident_read, NULL); ++ ++/* ----------------------------------------------------------------------------- ++ * V4L2 subdev core operations ++ */ ++ ++static int smiapp_identify_module(struct v4l2_subdev *subdev) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct smiapp_module_info *minfo = &sensor->minfo; ++ unsigned int i; ++ int rval = 0; ++ ++ minfo->name = SMIAPP_NAME; ++ ++ /* Module info */ ++ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, ++ &minfo->manufacturer_id); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, ++ &minfo->model_id); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, ++ &minfo->revision_number_major); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_REVISION_NUMBER_MINOR, ++ &minfo->revision_number_minor); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_MODULE_DATE_YEAR, ++ &minfo->module_year); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_MODULE_DATE_MONTH, ++ &minfo->module_month); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, ++ &minfo->module_day); ++ ++ /* Sensor info */ ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, ++ &minfo->sensor_manufacturer_id); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U16_SENSOR_MODEL_ID, ++ &minfo->sensor_model_id); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, ++ &minfo->sensor_revision_number); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, ++ SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, ++ &minfo->sensor_firmware_version); ++ ++ /* SMIA */ ++ if (!rval) ++ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, ++ &minfo->smia_version); ++ if (!rval) ++ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, ++ &minfo->smiapp_version); ++ ++ if (rval) { ++ dev_err(&client->dev, "sensor detection failed\n"); ++ return -ENODEV; ++ } ++ ++ dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", ++ minfo->manufacturer_id, minfo->model_id); ++ ++ dev_dbg(&client->dev, ++ "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", ++ minfo->revision_number_major, minfo->revision_number_minor, ++ minfo->module_year, minfo->module_month, minfo->module_day); ++ ++ dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", ++ minfo->sensor_manufacturer_id, minfo->sensor_model_id); ++ ++ dev_dbg(&client->dev, ++ "sensor revision 0x%2.2x firmware version 0x%2.2x\n", ++ minfo->sensor_revision_number, minfo->sensor_firmware_version); ++ ++ dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", ++ minfo->smia_version, minfo->smiapp_version); ++ ++ /* ++ * Some modules have bad data in the lvalues below. Hope the ++ * rvalues have better stuff. The lvalues are module ++ * parameters whereas the rvalues are sensor parameters. ++ */ ++ if (!minfo->manufacturer_id && !minfo->model_id) { ++ minfo->manufacturer_id = minfo->sensor_manufacturer_id; ++ minfo->model_id = minfo->sensor_model_id; ++ minfo->revision_number_major = minfo->sensor_revision_number; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { ++ if (smiapp_module_idents[i].manufacturer_id ++ != minfo->manufacturer_id) ++ continue; ++ if (smiapp_module_idents[i].model_id != minfo->model_id) ++ continue; ++ if (smiapp_module_idents[i].flags ++ & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { ++ if (smiapp_module_idents[i].revision_number_major ++ < minfo->revision_number_major) ++ continue; ++ } else { ++ if (smiapp_module_idents[i].revision_number_major ++ != minfo->revision_number_major) ++ continue; ++ } ++ ++ minfo->name = smiapp_module_idents[i].name; ++ minfo->quirk = smiapp_module_idents[i].quirk; ++ break; ++ } ++ ++ if (i >= ARRAY_SIZE(smiapp_module_idents)) ++ dev_warn(&client->dev, ++ "no quirks for this module; let's hope it's fully compliant\n"); ++ ++ dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", ++ minfo->name, minfo->manufacturer_id, minfo->model_id, ++ minfo->revision_number_major); ++ ++ strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_ops smiapp_ops; ++static const struct v4l2_subdev_internal_ops smiapp_internal_ops; ++static const struct media_entity_operations smiapp_entity_ops; ++ ++static int smiapp_registered(struct v4l2_subdev *subdev) ++{ ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ struct i2c_client *client = v4l2_get_subdevdata(subdev); ++ struct smiapp_pll *pll = &sensor->pll; ++ struct smiapp_subdev *last = NULL; ++ u32 tmp; ++ unsigned int i; ++ int rval; ++ ++ sensor->vana = devm_regulator_get(&client->dev, "VANA"); ++ if (IS_ERR(sensor->vana)) { ++ dev_err(&client->dev, "could not get regulator for vana\n"); ++ return -ENODEV; ++ } ++ ++ if (!sensor->platform_data->set_xclk) { ++ sensor->ext_clk = devm_clk_get(&client->dev, ++ sensor->platform_data->ext_clk_name); ++ if (IS_ERR(sensor->ext_clk)) { ++ dev_err(&client->dev, "could not get clock %s\n", ++ sensor->platform_data->ext_clk_name); ++ return -ENODEV; ++ } ++ ++ rval = clk_set_rate(sensor->ext_clk, ++ sensor->platform_data->ext_clk); ++ if (rval < 0) { ++ dev_err(&client->dev, ++ "unable to set clock %s freq to %u\n", ++ sensor->platform_data->ext_clk_name, ++ sensor->platform_data->ext_clk); ++ return -ENODEV; ++ } ++ } ++ ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { ++ if (gpio_request_one(sensor->platform_data->xshutdown, 0, ++ "SMIA++ xshutdown") != 0) { ++ dev_err(&client->dev, ++ "unable to acquire reset gpio %d\n", ++ sensor->platform_data->xshutdown); ++ return -ENODEV; ++ } ++ } ++ ++ rval = smiapp_power_on(sensor); ++ if (rval) { ++ rval = -ENODEV; ++ goto out_smiapp_power_on; ++ } ++ ++ rval = smiapp_identify_module(subdev); ++ if (rval) { ++ rval = -ENODEV; ++ goto out_power_off; ++ } ++ ++ rval = smiapp_get_all_limits(sensor); ++ if (rval) { ++ rval = -ENODEV; ++ goto out_power_off; ++ } ++ ++ /* ++ * Handle Sensor Module orientation on the board. ++ * ++ * The application of H-FLIP and V-FLIP on the sensor is modified by ++ * the sensor orientation on the board. ++ * ++ * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set ++ * both H-FLIP and V-FLIP for normal operation which also implies ++ * that a set/unset operation for user space HFLIP and VFLIP v4l2 ++ * controls will need to be internally inverted. ++ * ++ * Rotation also changes the bayer pattern. ++ */ ++ if (sensor->platform_data->module_board_orient == ++ SMIAPP_MODULE_BOARD_ORIENT_180) ++ sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | ++ SMIAPP_IMAGE_ORIENTATION_VFLIP; ++ ++ rval = smiapp_get_mbus_formats(sensor); ++ if (rval) { ++ rval = -ENODEV; ++ goto out_power_off; ++ } ++ ++ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { ++ u32 val; ++ ++ rval = smiapp_read(sensor, ++ SMIAPP_REG_U8_BINNING_SUBTYPES, &val); ++ if (rval < 0) { ++ rval = -ENODEV; ++ goto out_power_off; ++ } ++ sensor->nbinning_subtypes = min_t(u8, val, ++ SMIAPP_BINNING_SUBTYPES); ++ ++ for (i = 0; i < sensor->nbinning_subtypes; i++) { ++ rval = smiapp_read( ++ sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); ++ if (rval < 0) { ++ rval = -ENODEV; ++ goto out_power_off; ++ } ++ sensor->binning_subtypes[i] = ++ *(struct smiapp_binning_subtype *)&val; ++ ++ dev_dbg(&client->dev, "binning %xx%x\n", ++ sensor->binning_subtypes[i].horizontal, ++ sensor->binning_subtypes[i].vertical); ++ } ++ } ++ sensor->binning_horizontal = 1; ++ sensor->binning_vertical = 1; ++ ++ if (device_create_file(&client->dev, &dev_attr_ident) != 0) { ++ dev_err(&client->dev, "sysfs ident entry creation failed\n"); ++ rval = -ENOENT; ++ goto out_power_off; ++ } ++ /* SMIA++ NVM initialization - it will be read from the sensor ++ * when it is first requested by userspace. ++ */ ++ if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { ++ sensor->nvm = devm_kzalloc(&client->dev, ++ sensor->platform_data->nvm_size, GFP_KERNEL); ++ if (sensor->nvm == NULL) { ++ dev_err(&client->dev, "nvm buf allocation failed\n"); ++ rval = -ENOMEM; ++ goto out_ident_release; ++ } ++ ++ if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { ++ dev_err(&client->dev, "sysfs nvm entry failed\n"); ++ rval = -EBUSY; ++ goto out_ident_release; ++ } ++ } ++ ++ rval = smiapp_call_quirk(sensor, limits); ++ if (rval) { ++ dev_err(&client->dev, "limits quirks failed\n"); ++ goto out_nvm_release; ++ } ++ ++ /* We consider this as profile 0 sensor if any of these are zero. */ ++ if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || ++ !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || ++ !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || ++ !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { ++ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; ++ } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] ++ != SMIAPP_SCALING_CAPABILITY_NONE) { ++ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] ++ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) ++ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; ++ else ++ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; ++ sensor->scaler = &sensor->ssds[sensor->ssds_used]; ++ sensor->ssds_used++; ++ } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] ++ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { ++ sensor->scaler = &sensor->ssds[sensor->ssds_used]; ++ sensor->ssds_used++; ++ } ++ sensor->binner = &sensor->ssds[sensor->ssds_used]; ++ sensor->ssds_used++; ++ sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; ++ sensor->ssds_used++; ++ ++ sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; ++ ++ for (i = 0; i < SMIAPP_SUBDEVS; i++) { ++ struct { ++ struct smiapp_subdev *ssd; ++ char *name; ++ } const __this[] = { ++ { sensor->scaler, "scaler", }, ++ { sensor->binner, "binner", }, ++ { sensor->pixel_array, "pixel array", }, ++ }, *_this = &__this[i]; ++ struct smiapp_subdev *this = _this->ssd; ++ ++ if (!this) ++ continue; ++ ++ if (this != sensor->src) ++ v4l2_subdev_init(&this->sd, &smiapp_ops); ++ ++ this->sensor = sensor; ++ ++ if (this == sensor->pixel_array) { ++ this->npads = 1; ++ } else { ++ this->npads = 2; ++ this->source_pad = 1; ++ } ++ ++ snprintf(this->sd.name, ++ sizeof(this->sd.name), "%s %s", ++ sensor->minfo.name, _this->name); ++ ++ this->sink_fmt.width = ++ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; ++ this->sink_fmt.height = ++ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; ++ this->compose.width = this->sink_fmt.width; ++ this->compose.height = this->sink_fmt.height; ++ this->crop[this->source_pad] = this->compose; ++ this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; ++ if (this != sensor->pixel_array) { ++ this->crop[this->sink_pad] = this->compose; ++ this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; ++ } ++ ++ this->sd.entity.ops = &smiapp_entity_ops; ++ ++ if (last == NULL) { ++ last = this; ++ continue; ++ } ++ ++ this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ this->sd.internal_ops = &smiapp_internal_ops; ++ this->sd.owner = NULL; ++ v4l2_set_subdevdata(&this->sd, client); ++ ++ rval = media_entity_init(&this->sd.entity, ++ this->npads, this->pads, 0); ++ if (rval) { ++ dev_err(&client->dev, ++ "media_entity_init failed\n"); ++ goto out_nvm_release; ++ } ++ ++ rval = media_entity_create_link(&this->sd.entity, ++ this->source_pad, ++ &last->sd.entity, ++ last->sink_pad, ++ MEDIA_LNK_FL_ENABLED | ++ MEDIA_LNK_FL_IMMUTABLE); ++ if (rval) { ++ dev_err(&client->dev, ++ "media_entity_create_link failed\n"); ++ goto out_nvm_release; ++ } ++ ++ rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, ++ &this->sd); ++ if (rval) { ++ dev_err(&client->dev, ++ "v4l2_device_register_subdev failed\n"); ++ goto out_nvm_release; ++ } ++ ++ last = this; ++ } ++ ++ dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); ++ ++ sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ++ ++ /* final steps */ ++ smiapp_read_frame_fmt(sensor); ++ rval = smiapp_init_controls(sensor); ++ if (rval < 0) ++ goto out_nvm_release; ++ ++ /* prepare PLL configuration input values */ ++ pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; ++ pll->csi2.lanes = sensor->platform_data->lanes; ++ pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; ++ /* Profile 0 sensors have no separate OP clock branch. */ ++ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) ++ pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; ++ if (smiapp_needs_quirk(sensor, ++ SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) ++ pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; ++ pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; ++ ++ rval = smiapp_update_mode(sensor); ++ if (rval) { ++ dev_err(&client->dev, "update mode failed\n"); ++ goto out_nvm_release; ++ } ++ ++ sensor->streaming = false; ++ sensor->dev_init_done = true; ++ ++ /* check flash capability */ ++ rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); ++ sensor->flash_capability = tmp; ++ if (rval) ++ goto out_nvm_release; ++ ++ smiapp_power_off(sensor); ++ ++ return 0; ++ ++out_nvm_release: ++ device_remove_file(&client->dev, &dev_attr_nvm); ++ ++out_ident_release: ++ device_remove_file(&client->dev, &dev_attr_ident); ++ ++out_power_off: ++ smiapp_power_off(sensor); ++ ++out_smiapp_power_on: ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_free(sensor->platform_data->xshutdown); ++ ++ return rval; ++} ++ ++static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ struct smiapp_subdev *ssd = to_smiapp_subdev(sd); ++ struct smiapp_sensor *sensor = ssd->sensor; ++ u32 mbus_code = ++ smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; ++ unsigned int i; ++ ++ mutex_lock(&sensor->mutex); ++ ++ for (i = 0; i < ssd->npads; i++) { ++ struct v4l2_mbus_framefmt *try_fmt = ++ v4l2_subdev_get_try_format(fh, i); ++ struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); ++ struct v4l2_rect *try_comp; ++ ++ try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; ++ try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; ++ try_fmt->code = mbus_code; ++ ++ try_crop->top = 0; ++ try_crop->left = 0; ++ try_crop->width = try_fmt->width; ++ try_crop->height = try_fmt->height; ++ ++ if (ssd != sensor->pixel_array) ++ continue; ++ ++ try_comp = v4l2_subdev_get_try_compose(fh, i); ++ *try_comp = *try_crop; ++ } ++ ++ mutex_unlock(&sensor->mutex); ++ ++ return smiapp_set_power(sd, 1); ++} ++ ++static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ++{ ++ return smiapp_set_power(sd, 0); ++} ++ ++static const struct v4l2_subdev_video_ops smiapp_video_ops = { ++ .s_stream = smiapp_set_stream, ++}; ++ ++static const struct v4l2_subdev_core_ops smiapp_core_ops = { ++ .s_power = smiapp_set_power, ++}; ++ ++static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { ++ .enum_mbus_code = smiapp_enum_mbus_code, ++ .get_fmt = smiapp_get_format, ++ .set_fmt = smiapp_set_format, ++ .get_selection = smiapp_get_selection, ++ .set_selection = smiapp_set_selection, ++}; ++ ++static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { ++ .g_skip_frames = smiapp_get_skip_frames, ++}; ++ ++static const struct v4l2_subdev_ops smiapp_ops = { ++ .core = &smiapp_core_ops, ++ .video = &smiapp_video_ops, ++ .pad = &smiapp_pad_ops, ++ .sensor = &smiapp_sensor_ops, ++}; ++ ++static const struct media_entity_operations smiapp_entity_ops = { ++ .link_validate = v4l2_subdev_link_validate, ++}; ++ ++static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { ++ .registered = smiapp_registered, ++ .open = smiapp_open, ++ .close = smiapp_close, ++}; ++ ++static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { ++ .open = smiapp_open, ++ .close = smiapp_close, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * I2C Driver ++ */ ++ ++#ifdef CONFIG_PM ++ ++static int smiapp_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ bool streaming; ++ ++ BUG_ON(mutex_is_locked(&sensor->mutex)); ++ ++ if (sensor->power_count == 0) ++ return 0; ++ ++ if (sensor->streaming) ++ smiapp_stop_streaming(sensor); ++ ++ streaming = sensor->streaming; ++ ++ smiapp_power_off(sensor); ++ ++ /* save state for resume */ ++ sensor->streaming = streaming; ++ ++ return 0; ++} ++ ++static int smiapp_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ int rval; ++ ++ if (sensor->power_count == 0) ++ return 0; ++ ++ rval = smiapp_power_on(sensor); ++ if (rval) ++ return rval; ++ ++ if (sensor->streaming) ++ rval = smiapp_start_streaming(sensor); ++ ++ return rval; ++} ++ ++#else ++ ++#define smiapp_suspend NULL ++#define smiapp_resume NULL ++ ++#endif /* CONFIG_PM */ ++ ++static int smiapp_probe(struct i2c_client *client, ++ const struct i2c_device_id *devid) ++{ ++ struct smiapp_sensor *sensor; ++ ++ if (client->dev.platform_data == NULL) ++ return -ENODEV; ++ ++ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); ++ if (sensor == NULL) ++ return -ENOMEM; ++ ++ sensor->platform_data = client->dev.platform_data; ++ mutex_init(&sensor->mutex); ++ mutex_init(&sensor->power_mutex); ++ sensor->src = &sensor->ssds[sensor->ssds_used]; ++ ++ v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); ++ sensor->src->sd.internal_ops = &smiapp_internal_src_ops; ++ sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; ++ sensor->src->sensor = sensor; ++ ++ sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; ++ return media_entity_init(&sensor->src->sd.entity, 2, ++ sensor->src->pads, 0); ++} ++ ++static int __exit smiapp_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); ++ unsigned int i; ++ ++ if (sensor->power_count) { ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_set_value(sensor->platform_data->xshutdown, 0); ++ if (sensor->platform_data->set_xclk) ++ sensor->platform_data->set_xclk(&sensor->src->sd, 0); ++ else ++ clk_disable(sensor->ext_clk); ++ sensor->power_count = 0; ++ } ++ ++ device_remove_file(&client->dev, &dev_attr_ident); ++ if (sensor->nvm) ++ device_remove_file(&client->dev, &dev_attr_nvm); ++ ++ for (i = 0; i < sensor->ssds_used; i++) { ++ media_entity_cleanup(&sensor->ssds[i].sd.entity); ++ v4l2_device_unregister_subdev(&sensor->ssds[i].sd); ++ } ++ smiapp_free_controls(sensor); ++ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) ++ gpio_free(sensor->platform_data->xshutdown); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id smiapp_id_table[] = { ++ { SMIAPP_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, smiapp_id_table); ++ ++static const struct dev_pm_ops smiapp_pm_ops = { ++ .suspend = smiapp_suspend, ++ .resume = smiapp_resume, ++}; ++ ++static struct i2c_driver smiapp_i2c_driver = { ++ .driver = { ++ .name = SMIAPP_NAME, ++ .pm = &smiapp_pm_ops, ++ }, ++ .probe = smiapp_probe, ++ .remove = __exit_p(smiapp_remove), ++ .id_table = smiapp_id_table, ++}; ++ ++module_i2c_driver(smiapp_i2c_driver); ++ ++MODULE_AUTHOR("Sakari Ailus "); ++MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c +new file mode 100644 +index 0000000..847cb23 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-limits.c +@@ -0,0 +1,132 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-limits.c ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include "smiapp.h" ++ ++struct smiapp_reg_limits smiapp_reg_limits[] = { ++ { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ ++ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, ++ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, ++ { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, ++ { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, ++ { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ ++ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, ++ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, ++ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, ++ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, ++ { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ ++ { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, ++ { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, ++ { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, ++ { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, ++ { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ ++ { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, ++ { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, ++ { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, ++ { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, ++ { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ ++ { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, ++ { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, ++ { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, ++ { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, ++ { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ ++ { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, ++ { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, ++ { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, ++ { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, ++ { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ ++ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, ++ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, ++ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, ++ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, ++ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ ++ { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, ++ { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, ++ { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, ++ { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, ++ { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ ++ { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, ++ { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, ++ { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, ++ { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, ++ { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ ++ { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, ++ { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, ++ { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, ++ { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, ++ { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ ++ { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, ++ { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, ++ { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, ++ { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, ++ { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ ++ { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, ++ { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, ++ { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, ++ { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, ++ { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ ++ { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, ++ { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, ++ { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, ++ { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, ++ { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ ++ { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, ++ { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, ++ { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, ++ { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, ++ { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ ++ { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, ++ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, ++ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, ++ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, ++ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ ++ { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, ++ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, ++ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, ++ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, ++ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ ++ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, ++ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, ++ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, ++ { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, ++ { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ ++ { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, ++ { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, ++ { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, ++ { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, ++ { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ ++ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, ++ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, ++ { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, ++ { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, ++ { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ ++ { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, ++ { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, ++ { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, ++ { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, ++ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ ++ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, ++ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, ++ { 0, NULL }, ++}; +diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h +new file mode 100644 +index 0000000..343e9c3 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-limits.h +@@ -0,0 +1,128 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-limits.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 ++#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 ++#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 ++#define SMIAPP_LIMIT_THS_ZERO_MIN 3 ++#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 ++#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 ++#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 ++#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 ++#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 ++#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 ++#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 ++#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 ++#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 ++#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 ++#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 ++#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 ++#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 ++#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 ++#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 ++#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 ++#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 ++#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 ++#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 ++#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 ++#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 ++#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 ++#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 ++#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 ++#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 ++#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 ++#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 ++#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 ++#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 ++#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 ++#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 ++#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 ++#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 ++#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 ++#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 ++#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 ++#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 ++#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 ++#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 ++#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 ++#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 ++#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 ++#define SMIAPP_LIMIT_X_ADDR_MIN 46 ++#define SMIAPP_LIMIT_Y_ADDR_MIN 47 ++#define SMIAPP_LIMIT_X_ADDR_MAX 48 ++#define SMIAPP_LIMIT_Y_ADDR_MAX 49 ++#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 ++#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 ++#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 ++#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 ++#define SMIAPP_LIMIT_MIN_EVEN_INC 54 ++#define SMIAPP_LIMIT_MAX_EVEN_INC 55 ++#define SMIAPP_LIMIT_MIN_ODD_INC 56 ++#define SMIAPP_LIMIT_MAX_ODD_INC 57 ++#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 ++#define SMIAPP_LIMIT_SCALER_M_MIN 59 ++#define SMIAPP_LIMIT_SCALER_M_MAX 60 ++#define SMIAPP_LIMIT_SCALER_N_MIN 61 ++#define SMIAPP_LIMIT_SCALER_N_MAX 62 ++#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 ++#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 ++#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 ++#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 ++#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 ++#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 ++#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 ++#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 ++#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 ++#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 ++#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 ++#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 ++#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 ++#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 ++#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 ++#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 ++#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 ++#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 ++#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 ++#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 ++#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 ++#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 ++#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 ++#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 ++#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 ++#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 ++#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 ++#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 ++#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 ++#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 ++#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 ++#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 ++#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 ++#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 ++#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 ++#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 ++#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 ++#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 ++#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 ++#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 ++#define SMIAPP_LIMIT_LAST 103 +diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c +new file mode 100644 +index 0000000..bb8c506 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-quirk.c +@@ -0,0 +1,286 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-quirk.c ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++ ++#include "smiapp.h" ++ ++static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) ++{ ++ return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); ++} ++ ++static int smiapp_write_8s(struct smiapp_sensor *sensor, ++ struct smiapp_reg_8 *regs, int len) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int rval; ++ ++ for (; len > 0; len--, regs++) { ++ rval = smiapp_write_8(sensor, regs->reg, regs->val); ++ if (rval < 0) { ++ dev_err(&client->dev, ++ "error %d writing reg 0x%4.4x, val 0x%2.2x", ++ rval, regs->reg, regs->val); ++ return rval; ++ } ++ } ++ ++ return 0; ++} ++ ++void smiapp_replace_limit(struct smiapp_sensor *sensor, ++ u32 limit, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ ++ dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", ++ smiapp_reg_limits[limit].addr, ++ smiapp_reg_limits[limit].what, val, val); ++ sensor->limits[limit] = val; ++} ++ ++bool smiapp_quirk_reg(struct smiapp_sensor *sensor, ++ u32 reg, u32 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ const struct smia_reg *sreg; ++ ++ if (!sensor->minfo.quirk) ++ return false; ++ ++ sreg = sensor->minfo.quirk->regs; ++ ++ if (!sreg) ++ return false; ++ ++ while (sreg->type) { ++ u16 type = reg >> 16; ++ u16 reg16 = reg; ++ ++ if (sreg->type != type || sreg->reg != reg16) { ++ sreg++; ++ continue; ++ } ++ ++ switch ((u8)type) { ++ case SMIA_REG_8BIT: ++ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", ++ reg, sreg->val); ++ break; ++ case SMIA_REG_16BIT: ++ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", ++ reg, sreg->val); ++ break; ++ case SMIA_REG_32BIT: ++ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", ++ reg, sreg->val); ++ break; ++ } ++ ++ *val = sreg->val; ++ ++ return true; ++ } ++ ++ return false; ++} ++ ++static int jt8ew9_limits(struct smiapp_sensor *sensor) ++{ ++ if (sensor->minfo.revision_number_major < 0x03) ++ sensor->frame_skip = 1; ++ ++ /* Below 24 gain doesn't have effect at all, */ ++ /* but ~59 is needed for full dynamic range */ ++ smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); ++ smiapp_replace_limit( ++ sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); ++ ++ return 0; ++} ++ ++static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) ++{ ++ struct smiapp_reg_8 regs[] = { ++ { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ ++ { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ ++ { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ ++ { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ ++ { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ ++ { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ ++ { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ ++ { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ ++ { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ ++ { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ ++ { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ ++ { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ ++ { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ ++ { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ ++ { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ ++ { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ ++ { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ ++ { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ ++ /* Taken from v03. No idea what the rest are. */ ++ { 0x32e0, 0x05 }, ++ { 0x32e1, 0x05 }, ++ { 0x32e2, 0x04 }, ++ { 0x32e5, 0x04 }, ++ { 0x32e6, 0x04 }, ++ ++ }; ++ ++ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); ++} ++ ++const struct smiapp_quirk smiapp_jt8ew9_quirk = { ++ .limits = jt8ew9_limits, ++ .post_poweron = jt8ew9_post_poweron, ++}; ++ ++static int imx125es_post_poweron(struct smiapp_sensor *sensor) ++{ ++ /* Taken from v02. No idea what the other two are. */ ++ struct smiapp_reg_8 regs[] = { ++ /* ++ * 0x3302: clk during frame blanking: ++ * 0x00 - HS mode, 0x01 - LP11 ++ */ ++ { 0x3302, 0x01 }, ++ { 0x302d, 0x00 }, ++ { 0x3b08, 0x8c }, ++ }; ++ ++ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); ++} ++ ++const struct smiapp_quirk smiapp_imx125es_quirk = { ++ .post_poweron = imx125es_post_poweron, ++}; ++ ++static int jt8ev1_limits(struct smiapp_sensor *sensor) ++{ ++ smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); ++ smiapp_replace_limit(sensor, ++ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); ++ ++ return 0; ++} ++ ++static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ int rval; ++ ++ struct smiapp_reg_8 regs[] = { ++ { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ ++ { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ ++ { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ ++ { 0x3238, 0x43 }, ++ { 0x3301, 0x06 }, /* For analog bias for sensor */ ++ { 0x3302, 0x06 }, ++ { 0x3304, 0x00 }, ++ { 0x3305, 0x88 }, ++ { 0x332a, 0x14 }, ++ { 0x332c, 0x6b }, ++ { 0x3336, 0x01 }, ++ { 0x333f, 0x1f }, ++ { 0x3355, 0x00 }, ++ { 0x3356, 0x20 }, ++ { 0x33bf, 0x20 }, /* Adjust the FBC speed */ ++ { 0x33c9, 0x20 }, ++ { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ ++ { 0x33cf, 0xec }, /* For Black sun */ ++ { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ ++ }; ++ ++ struct smiapp_reg_8 regs_96[] = { ++ { 0x30ae, 0x00 }, /* For control of ADC clock */ ++ { 0x30af, 0xd0 }, ++ { 0x30b0, 0x01 }, ++ }; ++ ++ rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); ++ if (rval < 0) ++ return rval; ++ ++ switch (sensor->platform_data->ext_clk) { ++ case 9600000: ++ return smiapp_write_8s(sensor, regs_96, ++ ARRAY_SIZE(regs_96)); ++ default: ++ dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", ++ sensor->platform_data->ext_clk); ++ return 0; ++ } ++} ++ ++static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) ++{ ++ return smiapp_write_8(sensor, 0x3328, 0x00); ++} ++ ++static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) ++{ ++ int rval; ++ ++ /* Workaround: allows fast standby to work properly */ ++ rval = smiapp_write_8(sensor, 0x3205, 0x04); ++ if (rval < 0) ++ return rval; ++ ++ /* Wait for 1 ms + one line => 2 ms is likely enough */ ++ usleep_range(2000, 2000); ++ ++ /* Restore it */ ++ rval = smiapp_write_8(sensor, 0x3205, 0x00); ++ if (rval < 0) ++ return rval; ++ ++ return smiapp_write_8(sensor, 0x3328, 0x80); ++} ++ ++const struct smiapp_quirk smiapp_jt8ev1_quirk = { ++ .limits = jt8ev1_limits, ++ .post_poweron = jt8ev1_post_poweron, ++ .pre_streamon = jt8ev1_pre_streamon, ++ .post_streamoff = jt8ev1_post_streamoff, ++ .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, ++}; ++ ++static int tcm8500md_limits(struct smiapp_sensor *sensor) ++{ ++ smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); ++ ++ return 0; ++} ++ ++const struct smiapp_quirk smiapp_tcm8500md_quirk = { ++ .limits = tcm8500md_limits, ++}; +diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h +new file mode 100644 +index 0000000..504a6d8 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-quirk.h +@@ -0,0 +1,83 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-quirk.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef __SMIAPP_QUIRK__ ++#define __SMIAPP_QUIRK__ ++ ++struct smiapp_sensor; ++ ++/** ++ * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard ++ * ++ * @limits: Replace sensor->limits with values which can't be read from ++ * sensor registers. Called the first time the sensor is powered up. ++ * @post_poweron: Called always after the sensor has been fully powered on. ++ * @pre_streamon: Called just before streaming is enabled. ++ * @post_streamon: Called right after stopping streaming. ++ */ ++struct smiapp_quirk { ++ int (*limits)(struct smiapp_sensor *sensor); ++ int (*post_poweron)(struct smiapp_sensor *sensor); ++ int (*pre_streamon)(struct smiapp_sensor *sensor); ++ int (*post_streamoff)(struct smiapp_sensor *sensor); ++ const struct smia_reg *regs; ++ unsigned long flags; ++}; ++ ++/* op pix clock is for all lanes in total normally */ ++#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) ++#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) ++ ++struct smiapp_reg_8 { ++ u16 reg; ++ u8 val; ++}; ++ ++void smiapp_replace_limit(struct smiapp_sensor *sensor, ++ u32 limit, u32 val); ++bool smiapp_quirk_reg(struct smiapp_sensor *sensor, ++ u32 reg, u32 *val); ++ ++#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ ++ { \ ++ .type = (_reg >> 16), \ ++ .reg = (u16)_reg, \ ++ .val = _val, \ ++ } ++ ++#define smiapp_call_quirk(_sensor, _quirk, ...) \ ++ (_sensor->minfo.quirk && \ ++ _sensor->minfo.quirk->_quirk ? \ ++ _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) ++ ++#define smiapp_needs_quirk(_sensor, _quirk) \ ++ (_sensor->minfo.quirk ? \ ++ _sensor->minfo.quirk->flags & _quirk : 0) ++ ++extern const struct smiapp_quirk smiapp_jt8ev1_quirk; ++extern const struct smiapp_quirk smiapp_imx125es_quirk; ++extern const struct smiapp_quirk smiapp_jt8ew9_quirk; ++extern const struct smiapp_quirk smiapp_tcm8500md_quirk; ++ ++#endif /* __SMIAPP_QUIRK__ */ +diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h +new file mode 100644 +index 0000000..3aa0ca9 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h +@@ -0,0 +1,503 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-reg-defs.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) ++#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) ++#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) ++ ++#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) ++ ++#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) ++#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) ++#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) ++#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) ++#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) ++#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) ++#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) ++#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) ++#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) ++#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) ++#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) ++#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) ++#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) ++#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) ++#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) ++#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) ++#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) ++#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) ++#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) ++#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) ++#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) ++#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ ++#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) ++#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) ++#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) ++#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) ++#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) ++#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) ++#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) ++#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) ++#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) ++#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) ++#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) ++#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) ++#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) ++#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) ++#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) ++#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) ++#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) ++#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) ++#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) ++#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) ++#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) ++#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) ++#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) ++#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) ++#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) ++#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) ++#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) ++#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) ++#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) ++#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) ++#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) ++#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) ++#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) ++#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) ++#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) ++#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) ++#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) ++#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) ++#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) ++#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) ++#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) ++#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) ++#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) ++#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) ++#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) ++#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) ++#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) ++#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) ++#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) ++#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) ++#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) ++#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) ++#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) ++#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) ++#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) ++#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) ++#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) ++#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) ++#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) ++#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) ++#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) ++#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) ++#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) ++#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) ++#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) ++#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) ++#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) ++#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) ++#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) ++#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) ++#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) ++#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) ++#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) ++#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) ++#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) ++#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) ++#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) ++#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) ++#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) ++#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) ++#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) ++#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) ++#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) ++#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) ++#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) ++#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) ++#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) ++#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) ++#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) ++#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) ++#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) ++#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) ++#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) ++#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) ++#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) ++#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) ++#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) ++#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) ++#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) ++#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) ++#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) ++#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) ++#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) ++#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) ++#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) ++#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) ++#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) ++#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) ++#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) ++#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) ++#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) ++#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) ++#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) ++#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) ++#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) ++#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) ++#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) ++#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) ++#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) ++#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) ++#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) ++#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) ++#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) ++#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) ++#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) ++#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) ++#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) ++#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) ++#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) ++#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) ++#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) ++#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) ++#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) ++#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) ++#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) ++#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) ++#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) ++#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) ++#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) ++#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) ++#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) ++#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) ++#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) ++#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) ++#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) ++#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) ++#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) ++#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) ++#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) ++#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) ++#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) ++#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) ++#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) ++#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) ++#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) ++#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) ++#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) ++#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) ++#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) ++#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) ++#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) ++#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) ++#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) ++#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) ++#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) ++#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) ++#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) ++#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) ++#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) ++#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) ++#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) ++#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) ++#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) ++#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) ++#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) ++#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) ++#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) ++#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) ++#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) ++#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) ++#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) ++#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) ++#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) ++#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) ++#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) ++#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) ++#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) ++#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) ++#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) ++#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) ++#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) ++#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) ++#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) ++#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) ++#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) ++#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) ++#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) ++#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) ++#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) ++#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) ++#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) ++#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) ++#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) ++#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) ++#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) ++#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) ++#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) ++#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) ++#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) ++#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) ++#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) ++#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) ++#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) ++#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) ++#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) ++#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) ++#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) ++#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) ++#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) ++#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) ++#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) ++#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) ++#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) ++#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) ++#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) ++#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) ++#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) ++#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) ++#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) ++#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) ++#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) ++#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) ++#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) ++#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) ++#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) ++#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) ++#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) ++#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) ++#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) ++#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) ++#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) ++#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) ++#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) ++#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) ++#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) ++#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) ++#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) ++#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) ++#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) ++#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) ++#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) ++#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) ++#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) ++#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) ++#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) ++#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) ++#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) ++#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) ++#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) ++#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) ++#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ ++#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) ++#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) ++#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) ++#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) ++#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) ++#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) ++#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) ++#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) ++#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) ++#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) ++#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) ++#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) ++#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) ++#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) ++#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) ++#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) ++#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) ++#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) ++#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) ++#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) ++#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) ++#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) ++#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) ++#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) ++#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) ++#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) ++#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) ++#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) ++#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) ++#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) +diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h +new file mode 100644 +index 0000000..b0dcbb8 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-reg.h +@@ -0,0 +1,122 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-reg.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef __SMIAPP_REG_H_ ++#define __SMIAPP_REG_H_ ++ ++#include "smiapp-reg-defs.h" ++ ++/* Bits for above register */ ++#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) ++#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) ++ ++#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) ++#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) ++#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) ++#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) ++#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) ++#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) ++#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) ++#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) ++ ++#define SMIAPP_SOFTWARE_RESET (1 << 0) ++ ++#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) ++#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) ++ ++#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 ++/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ ++#define SMIAPP_DPHY_CTRL_UI 1 ++#define SMIAPP_DPHY_CTRL_REGISTER 2 ++ ++#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 ++#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 ++ ++#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 ++#define SMIAPP_MODE_SELECT_STREAMING 1 ++ ++#define SMIAPP_SCALING_MODE_NONE 0 ++#define SMIAPP_SCALING_MODE_HORIZONTAL 1 ++#define SMIAPP_SCALING_MODE_BOTH 2 ++ ++#define SMIAPP_SCALING_CAPABILITY_NONE 0 ++#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 ++#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ ++ ++/* digital crop right before scaler */ ++#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 ++#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 ++ ++#define SMIAPP_BINNING_CAPABILITY_NO 0 ++#define SMIAPP_BINNING_CAPABILITY_YES 1 ++ ++/* Maximum number of binning subtypes */ ++#define SMIAPP_BINNING_SUBTYPES 253 ++ ++#define SMIAPP_PIXEL_ORDER_GRBG 0 ++#define SMIAPP_PIXEL_ORDER_RGGB 1 ++#define SMIAPP_PIXEL_ORDER_BGGR 2 ++#define SMIAPP_PIXEL_ORDER_GBRG 3 ++ ++#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 ++#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 ++#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 ++#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 ++ ++#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 ++#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 ++#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f ++#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 ++#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 ++ ++#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 ++#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 ++#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff ++ ++#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 ++#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 ++#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff ++ ++#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 ++#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 ++#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 ++#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 ++#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 ++ ++#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 ++#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 ++ ++/* Scaling N factor */ ++#define SMIAPP_SCALE_N 16 ++ ++/* Image statistics registers */ ++/* Registers 0x2000 to 0x2fff are reserved for future ++ * use for statistics features. ++ */ ++ ++/* Manufacturer Specific Registers: 0x3000 to 0x3fff ++ * The manufacturer specifies these as a black box. ++ */ ++ ++#endif /* __SMIAPP_REG_H_ */ +diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c +new file mode 100644 +index 0000000..4fac32c +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-regs.c +@@ -0,0 +1,273 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp-regs.c ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#include ++#include ++ ++#include "smiapp.h" ++#include "smiapp-regs.h" ++ ++static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, ++ uint32_t phloat) ++{ ++ int32_t exp; ++ uint64_t man; ++ ++ if (phloat >= 0x80000000) { ++ dev_err(&client->dev, "this is a negative number\n"); ++ return 0; ++ } ++ ++ if (phloat == 0x7f800000) ++ return ~0; /* Inf. */ ++ ++ if ((phloat & 0x7f800000) == 0x7f800000) { ++ dev_err(&client->dev, "NaN or other special number\n"); ++ return 0; ++ } ++ ++ /* Valid cases begin here */ ++ if (phloat == 0) ++ return 0; /* Valid zero */ ++ ++ if (phloat > 0x4f800000) ++ return ~0; /* larger than 4294967295 */ ++ ++ /* ++ * Unbias exponent (note how phloat is now guaranteed to ++ * have 0 in the high bit) ++ */ ++ exp = ((int32_t)phloat >> 23) - 127; ++ ++ /* Extract mantissa, add missing '1' bit and it's in MHz */ ++ man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; ++ ++ if (exp < 0) ++ man >>= -exp; ++ else ++ man <<= exp; ++ ++ man >>= 23; /* Remove mantissa bias */ ++ ++ return man & 0xffffffff; ++} ++ ++ ++/* ++ * Read a 8/16/32-bit i2c register. The value is returned in 'val'. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, ++ u16 len, u32 *val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ struct i2c_msg msg; ++ unsigned char data[4]; ++ u16 offset = reg; ++ int r; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = data; ++ ++ /* high byte goes out first */ ++ data[0] = (u8) (offset >> 8); ++ data[1] = (u8) offset; ++ r = i2c_transfer(client->adapter, &msg, 1); ++ if (r != 1) { ++ if (r >= 0) ++ r = -EBUSY; ++ goto err; ++ } ++ ++ msg.len = len; ++ msg.flags = I2C_M_RD; ++ r = i2c_transfer(client->adapter, &msg, 1); ++ if (r != 1) { ++ if (r >= 0) ++ r = -EBUSY; ++ goto err; ++ } ++ ++ *val = 0; ++ /* high byte comes first */ ++ switch (len) { ++ case SMIA_REG_32BIT: ++ *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + ++ data[3]; ++ break; ++ case SMIA_REG_16BIT: ++ *val = (data[0] << 8) + data[1]; ++ break; ++ case SMIA_REG_8BIT: ++ *val = data[0]; ++ break; ++ default: ++ BUG(); ++ } ++ ++ return 0; ++ ++err: ++ dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); ++ ++ return r; ++} ++ ++/* Read a register using 8-bit access only. */ ++static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, ++ u16 len, u32 *val) ++{ ++ unsigned int i; ++ int rval; ++ ++ *val = 0; ++ ++ for (i = 0; i < len; i++) { ++ u32 val8; ++ ++ rval = ____smiapp_read(sensor, reg + i, 1, &val8); ++ if (rval < 0) ++ return rval; ++ *val |= val8 << ((len - i - 1) << 3); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Read a 8/16/32-bit i2c register. The value is returned in 'val'. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, ++ bool only8) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ unsigned int len = (u8)(reg >> 16); ++ int rval; ++ ++ if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT ++ && len != SMIA_REG_32BIT) ++ return -EINVAL; ++ ++ if (smiapp_quirk_reg(sensor, reg, val)) ++ goto found_quirk; ++ ++ if (len == SMIA_REG_8BIT && !only8) ++ rval = ____smiapp_read(sensor, (u16)reg, len, val); ++ else ++ rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); ++ if (rval < 0) ++ return rval; ++ ++found_quirk: ++ if (reg & SMIA_REG_FLAG_FLOAT) ++ *val = float_to_u32_mul_1000000(client, *val); ++ ++ return 0; ++} ++ ++int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) ++{ ++ return __smiapp_read( ++ sensor, reg, val, ++ smiapp_needs_quirk(sensor, ++ SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); ++} ++ ++int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) ++{ ++ return __smiapp_read(sensor, reg, val, true); ++} ++ ++/* ++ * Write to a 8/16-bit register. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); ++ struct i2c_msg msg; ++ unsigned char data[6]; ++ unsigned int retries; ++ unsigned int flags = reg >> 24; ++ unsigned int len = (u8)(reg >> 16); ++ u16 offset = reg; ++ int r; ++ ++ if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && ++ len != SMIA_REG_32BIT) || flags) ++ return -EINVAL; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; /* Write */ ++ msg.len = 2 + len; ++ msg.buf = data; ++ ++ /* high byte goes out first */ ++ data[0] = (u8) (reg >> 8); ++ data[1] = (u8) (reg & 0xff); ++ ++ switch (len) { ++ case SMIA_REG_8BIT: ++ data[2] = val; ++ break; ++ case SMIA_REG_16BIT: ++ data[2] = val >> 8; ++ data[3] = val; ++ break; ++ case SMIA_REG_32BIT: ++ data[2] = val >> 24; ++ data[3] = val >> 16; ++ data[4] = val >> 8; ++ data[5] = val; ++ break; ++ default: ++ BUG(); ++ } ++ ++ for (retries = 0; retries < 5; retries++) { ++ /* ++ * Due to unknown reason sensor stops responding. This ++ * loop is a temporaty solution until the root cause ++ * is found. ++ */ ++ r = i2c_transfer(client->adapter, &msg, 1); ++ if (r == 1) { ++ if (retries) ++ dev_err(&client->dev, ++ "sensor i2c stall encountered. " ++ "retries: %d\n", retries); ++ return 0; ++ } ++ ++ usleep_range(2000, 2000); ++ } ++ ++ dev_err(&client->dev, ++ "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); ++ ++ return r; ++} +diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h +new file mode 100644 +index 0000000..eefc6c8 +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp-regs.h +@@ -0,0 +1,49 @@ ++/* ++ * include/media/smiapp/smiapp-regs.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2011--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef SMIAPP_REGS_H ++#define SMIAPP_REGS_H ++ ++#include ++#include ++ ++/* Use upper 8 bits of the type field for flags */ ++#define SMIA_REG_FLAG_FLOAT (1 << 24) ++ ++#define SMIA_REG_8BIT 1 ++#define SMIA_REG_16BIT 2 ++#define SMIA_REG_32BIT 4 ++struct smia_reg { ++ u16 type; ++ u16 reg; /* 16-bit offset */ ++ u32 val; /* 8/16/32-bit value */ ++}; ++ ++struct smiapp_sensor; ++ ++int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); ++int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); ++int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); ++ ++#endif +diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h +new file mode 100644 +index 0000000..7cc5aae +--- /dev/null ++++ b/drivers/media/i2c/smiapp/smiapp.h +@@ -0,0 +1,252 @@ ++/* ++ * drivers/media/i2c/smiapp/smiapp.h ++ * ++ * Generic driver for SMIA/SMIA++ compliant camera modules ++ * ++ * Copyright (C) 2010--2012 Nokia Corporation ++ * Contact: Sakari Ailus ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ * ++ */ ++ ++#ifndef __SMIAPP_PRIV_H_ ++#define __SMIAPP_PRIV_H_ ++ ++#include ++#include ++#include ++#include ++ ++#include "smiapp-pll.h" ++#include "smiapp-reg.h" ++#include "smiapp-regs.h" ++#include "smiapp-quirk.h" ++ ++/* ++ * Standard SMIA++ constants ++ */ ++#define SMIA_VERSION_1 10 ++#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ ++#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ ++#define SMIAPP_VERSION_1 10 ++ ++#define SMIAPP_PROFILE_0 0 ++#define SMIAPP_PROFILE_1 1 ++#define SMIAPP_PROFILE_2 2 ++ ++#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ ++ ++#define SMIAPP_RESET_DELAY_CLOCKS 2400 ++#define SMIAPP_RESET_DELAY(clk) \ ++ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ ++ + (clk) / 1000 - 1) / ((clk) / 1000)) ++ ++#include "smiapp-limits.h" ++ ++struct smiapp_quirk; ++ ++#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) ++ ++struct smiapp_module_ident { ++ u8 manufacturer_id; ++ u16 model_id; ++ u8 revision_number_major; ++ ++ u8 flags; ++ ++ char *name; ++ const struct smiapp_quirk *quirk; ++}; ++ ++struct smiapp_module_info { ++ u32 manufacturer_id; ++ u32 model_id; ++ u32 revision_number_major; ++ u32 revision_number_minor; ++ ++ u32 module_year; ++ u32 module_month; ++ u32 module_day; ++ ++ u32 sensor_manufacturer_id; ++ u32 sensor_model_id; ++ u32 sensor_revision_number; ++ u32 sensor_firmware_version; ++ ++ u32 smia_version; ++ u32 smiapp_version; ++ ++ u32 smiapp_profile; ++ ++ char *name; ++ const struct smiapp_quirk *quirk; ++}; ++ ++#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ ++ { .manufacturer_id = manufacturer, \ ++ .model_id = model, \ ++ .revision_number_major = rev, \ ++ .flags = fl, \ ++ .name = _name, \ ++ .quirk = _quirk, } ++ ++#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ ++ { .manufacturer_id = manufacturer, \ ++ .model_id = model, \ ++ .revision_number_major = rev, \ ++ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ ++ .name = _name, \ ++ .quirk = _quirk, } ++ ++#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ ++ { .manufacturer_id = manufacturer, \ ++ .model_id = model, \ ++ .revision_number_major = rev, \ ++ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ ++ .name = _name, } ++ ++#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ ++ { .manufacturer_id = manufacturer, \ ++ .model_id = model, \ ++ .revision_number_major = rev, \ ++ .flags = 0, \ ++ .name = _name, \ ++ .quirk = _quirk, } ++ ++#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ ++ { .manufacturer_id = manufacturer, \ ++ .model_id = model, \ ++ .revision_number_major = rev, \ ++ .flags = 0, \ ++ .name = _name, } ++ ++struct smiapp_reg_limits { ++ u32 addr; ++ char *what; ++}; ++ ++extern struct smiapp_reg_limits smiapp_reg_limits[]; ++ ++struct smiapp_csi_data_format { ++ u32 code; ++ u8 width; ++ u8 compressed; ++ u8 pixel_order; ++}; ++ ++#define SMIAPP_SUBDEVS 3 ++ ++#define SMIAPP_PA_PAD_SRC 0 ++#define SMIAPP_PAD_SINK 0 ++#define SMIAPP_PAD_SRC 1 ++#define SMIAPP_PADS 2 ++ ++struct smiapp_binning_subtype { ++ u8 horizontal:4; ++ u8 vertical:4; ++} __packed; ++ ++struct smiapp_subdev { ++ struct v4l2_subdev sd; ++ struct media_pad pads[2]; ++ struct v4l2_rect sink_fmt; ++ struct v4l2_rect crop[2]; ++ struct v4l2_rect compose; /* compose on sink */ ++ unsigned short sink_pad; ++ unsigned short source_pad; ++ int npads; ++ struct smiapp_sensor *sensor; ++ struct v4l2_ctrl_handler ctrl_handler; ++}; ++ ++/* ++ * struct smiapp_sensor - Main device structure ++ */ ++struct smiapp_sensor { ++ /* ++ * "mutex" is used to serialise access to all fields here ++ * except v4l2_ctrls at the end of the struct. "mutex" is also ++ * used to serialise access to file handle specific ++ * information. The exception to this rule is the power_mutex ++ * below. ++ */ ++ struct mutex mutex; ++ /* ++ * power_mutex is used to serialise power management related ++ * activities. Acquiring "mutex" at that time isn't necessary ++ * since there are no other users anyway. ++ */ ++ struct mutex power_mutex; ++ struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; ++ u32 ssds_used; ++ struct smiapp_subdev *src; ++ struct smiapp_subdev *binner; ++ struct smiapp_subdev *scaler; ++ struct smiapp_subdev *pixel_array; ++ struct smiapp_platform_data *platform_data; ++ struct regulator *vana; ++ struct clk *ext_clk; ++ u32 limits[SMIAPP_LIMIT_LAST]; ++ u8 nbinning_subtypes; ++ struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; ++ u32 mbus_frame_fmts; ++ const struct smiapp_csi_data_format *csi_format; ++ const struct smiapp_csi_data_format *internal_csi_format; ++ u32 default_mbus_frame_fmts; ++ int default_pixel_order; ++ ++ u8 binning_horizontal; ++ u8 binning_vertical; ++ ++ u8 scale_m; ++ u8 scaling_mode; ++ ++ u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ ++ u8 flash_capability; ++ u8 frame_skip; ++ ++ int power_count; ++ ++ bool streaming; ++ bool dev_init_done; ++ ++ u8 *nvm; /* nvm memory buffer */ ++ unsigned int nvm_size; /* bytes */ ++ ++ struct smiapp_module_info minfo; ++ ++ struct smiapp_pll pll; ++ ++ /* Pixel array controls */ ++ struct v4l2_ctrl *analog_gain; ++ struct v4l2_ctrl *exposure; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *pixel_rate_parray; ++ /* src controls */ ++ struct v4l2_ctrl *link_freq; ++ struct v4l2_ctrl *pixel_rate_csi; ++}; ++ ++#define to_smiapp_subdev(_sd) \ ++ container_of(_sd, struct smiapp_subdev, sd) ++ ++#define to_smiapp_sensor(_sd) \ ++ (to_smiapp_subdev(_sd)->sensor) ++ ++#endif /* __SMIAPP_PRIV_H_ */ +diff --git a/drivers/media/i2c/soc_camera/Kconfig b/drivers/media/i2c/soc_camera/Kconfig +new file mode 100644 +index 0000000..6dff2b7 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/Kconfig +@@ -0,0 +1,89 @@ ++comment "soc_camera sensor drivers" ++ ++config SOC_CAMERA_IMX074 ++ tristate "imx074 support" ++ depends on SOC_CAMERA && I2C ++ help ++ This driver supports IMX074 cameras from Sony ++ ++config SOC_CAMERA_MT9M001 ++ tristate "mt9m001 support" ++ depends on SOC_CAMERA && I2C ++ select GPIO_PCA953X if MT9M001_PCA9536_SWITCH ++ help ++ This driver supports MT9M001 cameras from Micron, monochrome ++ and colour models. ++ ++config SOC_CAMERA_MT9M111 ++ tristate "mt9m111, mt9m112 and mt9m131 support" ++ depends on SOC_CAMERA && I2C ++ help ++ This driver supports MT9M111, MT9M112 and MT9M131 cameras from ++ Micron/Aptina ++ ++config SOC_CAMERA_MT9T031 ++ tristate "mt9t031 support" ++ depends on SOC_CAMERA && I2C ++ help ++ This driver supports MT9T031 cameras from Micron. ++ ++config SOC_CAMERA_MT9T112 ++ tristate "mt9t112 support" ++ depends on SOC_CAMERA && I2C ++ help ++ This driver supports MT9T112 cameras from Aptina. ++ ++config SOC_CAMERA_MT9V022 ++ tristate "mt9v022 and mt9v024 support" ++ depends on SOC_CAMERA && I2C ++ select GPIO_PCA953X if MT9V022_PCA9536_SWITCH ++ help ++ This driver supports MT9V022 cameras from Micron ++ ++config SOC_CAMERA_OV2640 ++ tristate "ov2640 camera support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a ov2640 camera driver ++ ++config SOC_CAMERA_OV5642 ++ tristate "ov5642 camera support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a V4L2 camera driver for the OmniVision OV5642 sensor ++ ++config SOC_CAMERA_OV6650 ++ tristate "ov6650 sensor support" ++ depends on SOC_CAMERA && I2C ++ ---help--- ++ This is a V4L2 SoC camera driver for the OmniVision OV6650 sensor ++ ++config SOC_CAMERA_OV772X ++ tristate "ov772x camera support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a ov772x camera driver ++ ++config SOC_CAMERA_OV9640 ++ tristate "ov9640 camera support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a ov9640 camera driver ++ ++config SOC_CAMERA_OV9740 ++ tristate "ov9740 camera support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a ov9740 camera driver ++ ++config SOC_CAMERA_RJ54N1 ++ tristate "rj54n1cb0c support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a rj54n1cb0c video driver ++ ++config SOC_CAMERA_TW9910 ++ tristate "tw9910 support" ++ depends on SOC_CAMERA && I2C ++ help ++ This is a tw9910 video driver +diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile +new file mode 100644 +index 0000000..d0421fe +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/Makefile +@@ -0,0 +1,14 @@ ++obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o ++obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o ++obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o ++obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o ++obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o ++obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o ++obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o ++obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o ++obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o ++obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o ++obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o ++obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o ++obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o ++obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +diff --git a/drivers/media/i2c/soc_camera/imx074.c b/drivers/media/i2c/soc_camera/imx074.c +new file mode 100644 +index 0000000..a2a5cbb +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/imx074.c +@@ -0,0 +1,486 @@ ++/* ++ * Driver for IMX074 CMOS Image Sensor from Sony ++ * ++ * Copyright (C) 2010, Guennadi Liakhovetski ++ * ++ * Partially inspired by the IMX074 driver from the Android / MSM tree ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* IMX074 registers */ ++ ++#define MODE_SELECT 0x0100 ++#define IMAGE_ORIENTATION 0x0101 ++#define GROUPED_PARAMETER_HOLD 0x0104 ++ ++/* Integration Time */ ++#define COARSE_INTEGRATION_TIME_HI 0x0202 ++#define COARSE_INTEGRATION_TIME_LO 0x0203 ++/* Gain */ ++#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204 ++#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205 ++ ++/* PLL registers */ ++#define PRE_PLL_CLK_DIV 0x0305 ++#define PLL_MULTIPLIER 0x0307 ++#define PLSTATIM 0x302b ++#define VNDMY_ABLMGSHLMT 0x300a ++#define Y_OPBADDR_START_DI 0x3014 ++/* mode setting */ ++#define FRAME_LENGTH_LINES_HI 0x0340 ++#define FRAME_LENGTH_LINES_LO 0x0341 ++#define LINE_LENGTH_PCK_HI 0x0342 ++#define LINE_LENGTH_PCK_LO 0x0343 ++#define YADDR_START 0x0347 ++#define YADDR_END 0x034b ++#define X_OUTPUT_SIZE_MSB 0x034c ++#define X_OUTPUT_SIZE_LSB 0x034d ++#define Y_OUTPUT_SIZE_MSB 0x034e ++#define Y_OUTPUT_SIZE_LSB 0x034f ++#define X_EVEN_INC 0x0381 ++#define X_ODD_INC 0x0383 ++#define Y_EVEN_INC 0x0385 ++#define Y_ODD_INC 0x0387 ++ ++#define HMODEADD 0x3001 ++#define VMODEADD 0x3016 ++#define VAPPLINE_START 0x3069 ++#define VAPPLINE_END 0x306b ++#define SHUTTER 0x3086 ++#define HADDAVE 0x30e8 ++#define LANESEL 0x3301 ++ ++/* IMX074 supported geometry */ ++#define IMX074_WIDTH 1052 ++#define IMX074_HEIGHT 780 ++ ++/* IMX074 has only one fixed colorspace per pixelcode */ ++struct imx074_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++struct imx074 { ++ struct v4l2_subdev subdev; ++ const struct imx074_datafmt *fmt; ++}; ++ ++static const struct imx074_datafmt imx074_colour_fmts[] = { ++ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, ++}; ++ ++static struct imx074 *to_imx074(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct imx074, subdev); ++} ++ ++/* Find a data format by a pixel code in an array */ ++static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) ++ if (imx074_colour_fmts[i].code == code) ++ return imx074_colour_fmts + i; ++ ++ return NULL; ++} ++ ++static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg; ++ unsigned char tx[3]; ++ int ret; ++ ++ msg.addr = client->addr; ++ msg.buf = tx; ++ msg.len = 3; ++ msg.flags = 0; ++ ++ tx[0] = addr >> 8; ++ tx[1] = addr & 0xff; ++ tx[2] = data; ++ ++ ret = i2c_transfer(adap, &msg, 1); ++ ++ mdelay(2); ++ ++ return ret == 1 ? 0 : -EIO; ++} ++ ++static int reg_read(struct i2c_client *client, const u16 addr) ++{ ++ u8 buf[2] = {addr >> 8, addr & 0xff}; ++ int ret; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = buf, ++ }, { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = 2, ++ .buf = buf, ++ }, ++ }; ++ ++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); ++ if (ret < 0) { ++ dev_warn(&client->dev, "Reading register %x from %x failed\n", ++ addr, client->addr); ++ return ret; ++ } ++ ++ return buf[0] & 0xff; /* no sign-extension */ ++} ++ ++static int imx074_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); ++ ++ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); ++ ++ if (!fmt) { ++ mf->code = imx074_colour_fmts[0].code; ++ mf->colorspace = imx074_colour_fmts[0].colorspace; ++ } ++ ++ mf->width = IMX074_WIDTH; ++ mf->height = IMX074_HEIGHT; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int imx074_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx074 *priv = to_imx074(client); ++ ++ dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); ++ ++ /* MIPI CSI could have changed the format, double-check */ ++ if (!imx074_find_datafmt(mf->code)) ++ return -EINVAL; ++ ++ imx074_try_fmt(sd, mf); ++ ++ priv->fmt = imx074_find_datafmt(mf->code); ++ ++ return 0; ++} ++ ++static int imx074_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct imx074 *priv = to_imx074(client); ++ ++ const struct imx074_datafmt *fmt = priv->fmt; ++ ++ mf->code = fmt->code; ++ mf->colorspace = fmt->colorspace; ++ mf->width = IMX074_WIDTH; ++ mf->height = IMX074_HEIGHT; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct v4l2_rect *rect = &a->c; ++ ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ rect->top = 0; ++ rect->left = 0; ++ rect->width = IMX074_WIDTH; ++ rect->height = IMX074_HEIGHT; ++ ++ return 0; ++} ++ ++static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = IMX074_WIDTH; ++ a->bounds.height = IMX074_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts)) ++ return -EINVAL; ++ ++ *code = imx074_colour_fmts[index].code; ++ return 0; ++} ++ ++static int imx074_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* MODE_SELECT: stream or standby */ ++ return reg_write(client, MODE_SELECT, !!enable); ++} ++ ++static int imx074_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = V4L2_IDENT_IMX074; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++static int imx074_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int imx074_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ cfg->type = V4L2_MBUS_CSI2; ++ cfg->flags = V4L2_MBUS_CSI2_2_LANE | ++ V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { ++ .s_stream = imx074_s_stream, ++ .s_mbus_fmt = imx074_s_fmt, ++ .g_mbus_fmt = imx074_g_fmt, ++ .try_mbus_fmt = imx074_try_fmt, ++ .enum_mbus_fmt = imx074_enum_fmt, ++ .g_crop = imx074_g_crop, ++ .cropcap = imx074_cropcap, ++ .g_mbus_config = imx074_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { ++ .g_chip_ident = imx074_g_chip_ident, ++ .s_power = imx074_s_power, ++}; ++ ++static struct v4l2_subdev_ops imx074_subdev_ops = { ++ .core = &imx074_subdev_core_ops, ++ .video = &imx074_subdev_video_ops, ++}; ++ ++static int imx074_video_probe(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ int ret; ++ u16 id; ++ ++ ret = imx074_s_power(subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Read sensor Model ID */ ++ ret = reg_read(client, 0); ++ if (ret < 0) ++ goto done; ++ ++ id = ret << 8; ++ ++ ret = reg_read(client, 1); ++ if (ret < 0) ++ goto done; ++ ++ id |= ret; ++ ++ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); ++ ++ if (id != 0x74) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ /* PLL Setting EXTCLK=24MHz, 22.5times */ ++ reg_write(client, PLL_MULTIPLIER, 0x2D); ++ reg_write(client, PRE_PLL_CLK_DIV, 0x02); ++ reg_write(client, PLSTATIM, 0x4B); ++ ++ /* 2-lane mode */ ++ reg_write(client, 0x3024, 0x00); ++ ++ reg_write(client, IMAGE_ORIENTATION, 0x00); ++ ++ /* select RAW mode: ++ * 0x08+0x08 = top 8 bits ++ * 0x0a+0x08 = compressed 8-bits ++ * 0x0a+0x0a = 10 bits ++ */ ++ reg_write(client, 0x0112, 0x08); ++ reg_write(client, 0x0113, 0x08); ++ ++ /* Base setting for High frame mode */ ++ reg_write(client, VNDMY_ABLMGSHLMT, 0x80); ++ reg_write(client, Y_OPBADDR_START_DI, 0x08); ++ reg_write(client, 0x3015, 0x37); ++ reg_write(client, 0x301C, 0x01); ++ reg_write(client, 0x302C, 0x05); ++ reg_write(client, 0x3031, 0x26); ++ reg_write(client, 0x3041, 0x60); ++ reg_write(client, 0x3051, 0x24); ++ reg_write(client, 0x3053, 0x34); ++ reg_write(client, 0x3057, 0xC0); ++ reg_write(client, 0x305C, 0x09); ++ reg_write(client, 0x305D, 0x07); ++ reg_write(client, 0x3060, 0x30); ++ reg_write(client, 0x3065, 0x00); ++ reg_write(client, 0x30AA, 0x08); ++ reg_write(client, 0x30AB, 0x1C); ++ reg_write(client, 0x30B0, 0x32); ++ reg_write(client, 0x30B2, 0x83); ++ reg_write(client, 0x30D3, 0x04); ++ reg_write(client, 0x3106, 0x78); ++ reg_write(client, 0x310C, 0x82); ++ reg_write(client, 0x3304, 0x05); ++ reg_write(client, 0x3305, 0x04); ++ reg_write(client, 0x3306, 0x11); ++ reg_write(client, 0x3307, 0x02); ++ reg_write(client, 0x3308, 0x0C); ++ reg_write(client, 0x3309, 0x06); ++ reg_write(client, 0x330A, 0x08); ++ reg_write(client, 0x330B, 0x04); ++ reg_write(client, 0x330C, 0x08); ++ reg_write(client, 0x330D, 0x06); ++ reg_write(client, 0x330E, 0x01); ++ reg_write(client, 0x3381, 0x00); ++ ++ /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ ++ /* 1608 = 1560 + 48 (black lines) */ ++ reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); ++ reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); ++ reg_write(client, YADDR_START, 0x00); ++ reg_write(client, YADDR_END, 0x2F); ++ /* 0x838 == 2104 */ ++ reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); ++ reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); ++ /* 0x618 == 1560 */ ++ reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); ++ reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); ++ reg_write(client, X_EVEN_INC, 0x01); ++ reg_write(client, X_ODD_INC, 0x03); ++ reg_write(client, Y_EVEN_INC, 0x01); ++ reg_write(client, Y_ODD_INC, 0x03); ++ reg_write(client, HMODEADD, 0x00); ++ reg_write(client, VMODEADD, 0x16); ++ reg_write(client, VAPPLINE_START, 0x24); ++ reg_write(client, VAPPLINE_END, 0x53); ++ reg_write(client, SHUTTER, 0x00); ++ reg_write(client, HADDAVE, 0x80); ++ ++ reg_write(client, LANESEL, 0x00); ++ ++ reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ ++ ++ ret = 0; ++ ++done: ++ imx074_s_power(subdev, 0); ++ return ret; ++} ++ ++static int imx074_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct imx074 *priv; ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "IMX074: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); ++ return -EIO; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); ++ ++ priv->fmt = &imx074_colour_fmts[0]; ++ ++ return imx074_video_probe(client); ++} ++ ++static int imx074_remove(struct i2c_client *client) ++{ ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (ssdd->free_bus) ++ ssdd->free_bus(ssdd); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id imx074_id[] = { ++ { "imx074", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, imx074_id); ++ ++static struct i2c_driver imx074_i2c_driver = { ++ .driver = { ++ .name = "imx074", ++ }, ++ .probe = imx074_probe, ++ .remove = imx074_remove, ++ .id_table = imx074_id, ++}; ++ ++module_i2c_driver(imx074_i2c_driver); ++ ++MODULE_DESCRIPTION("Sony IMX074 Camera driver"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/mt9m001.c +new file mode 100644 +index 0000000..bcdc861 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/mt9m001.c +@@ -0,0 +1,751 @@ ++/* ++ * Driver for MT9M001 CMOS Image Sensor from Micron ++ * ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * mt9m001 i2c address 0x5d ++ * The platform has to define struct i2c_board_info objects and link to them ++ * from struct soc_camera_host_desc ++ */ ++ ++/* mt9m001 selected register addresses */ ++#define MT9M001_CHIP_VERSION 0x00 ++#define MT9M001_ROW_START 0x01 ++#define MT9M001_COLUMN_START 0x02 ++#define MT9M001_WINDOW_HEIGHT 0x03 ++#define MT9M001_WINDOW_WIDTH 0x04 ++#define MT9M001_HORIZONTAL_BLANKING 0x05 ++#define MT9M001_VERTICAL_BLANKING 0x06 ++#define MT9M001_OUTPUT_CONTROL 0x07 ++#define MT9M001_SHUTTER_WIDTH 0x09 ++#define MT9M001_FRAME_RESTART 0x0b ++#define MT9M001_SHUTTER_DELAY 0x0c ++#define MT9M001_RESET 0x0d ++#define MT9M001_READ_OPTIONS1 0x1e ++#define MT9M001_READ_OPTIONS2 0x20 ++#define MT9M001_GLOBAL_GAIN 0x35 ++#define MT9M001_CHIP_ENABLE 0xF1 ++ ++#define MT9M001_MAX_WIDTH 1280 ++#define MT9M001_MAX_HEIGHT 1024 ++#define MT9M001_MIN_WIDTH 48 ++#define MT9M001_MIN_HEIGHT 32 ++#define MT9M001_COLUMN_SKIP 20 ++#define MT9M001_ROW_SKIP 12 ++ ++/* MT9M001 has only one fixed colorspace per pixelcode */ ++struct mt9m001_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++/* Find a data format by a pixel code in an array */ ++static const struct mt9m001_datafmt *mt9m001_find_datafmt( ++ enum v4l2_mbus_pixelcode code, const struct mt9m001_datafmt *fmt, ++ int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) ++ if (fmt[i].code == code) ++ return fmt + i; ++ ++ return NULL; ++} ++ ++static const struct mt9m001_datafmt mt9m001_colour_fmts[] = { ++ /* ++ * Order important: first natively supported, ++ * second supported with a GPIO extender ++ */ ++ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, ++}; ++ ++static const struct mt9m001_datafmt mt9m001_monochrome_fmts[] = { ++ /* Order important - see above */ ++ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, ++}; ++ ++struct mt9m001 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* exposure/auto-exposure cluster */ ++ struct v4l2_ctrl *autoexposure; ++ struct v4l2_ctrl *exposure; ++ }; ++ struct v4l2_rect rect; /* Sensor window */ ++ const struct mt9m001_datafmt *fmt; ++ const struct mt9m001_datafmt *fmts; ++ int num_fmts; ++ int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ ++ unsigned int total_h; ++ unsigned short y_skip_top; /* Lines to skip at the top */ ++}; ++ ++static struct mt9m001 *to_mt9m001(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); ++} ++ ++static int reg_read(struct i2c_client *client, const u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int reg_write(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int reg_set(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret | data); ++} ++ ++static int reg_clear(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret & ~data); ++} ++ ++static int mt9m001_init(struct i2c_client *client) ++{ ++ int ret; ++ ++ dev_dbg(&client->dev, "%s\n", __func__); ++ ++ /* ++ * We don't know, whether platform provides reset, issue a soft reset ++ * too. This returns all registers to their default values. ++ */ ++ ret = reg_write(client, MT9M001_RESET, 1); ++ if (!ret) ++ ret = reg_write(client, MT9M001_RESET, 0); ++ ++ /* Disable chip, synchronous option update */ ++ if (!ret) ++ ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); ++ ++ return ret; ++} ++ ++static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* Switch to master "normal" mode or stop sensor readout */ ++ if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) ++ return -EIO; ++ return 0; ++} ++ ++static int mt9m001_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ struct v4l2_rect rect = a->c; ++ int ret; ++ const u16 hblank = 9, vblank = 25; ++ ++ if (mt9m001->fmts == mt9m001_colour_fmts) ++ /* ++ * Bayer format - even number of rows for simplicity, ++ * but let the user play with the top row. ++ */ ++ rect.height = ALIGN(rect.height, 2); ++ ++ /* Datasheet requirement: see register description */ ++ rect.width = ALIGN(rect.width, 2); ++ rect.left = ALIGN(rect.left, 2); ++ ++ soc_camera_limit_side(&rect.left, &rect.width, ++ MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); ++ ++ soc_camera_limit_side(&rect.top, &rect.height, ++ MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); ++ ++ mt9m001->total_h = rect.height + mt9m001->y_skip_top + vblank; ++ ++ /* Blanking and start values - default... */ ++ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); ++ if (!ret) ++ ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); ++ ++ /* ++ * The caller provides a supported format, as verified per ++ * call to .try_mbus_fmt() ++ */ ++ if (!ret) ++ ret = reg_write(client, MT9M001_COLUMN_START, rect.left); ++ if (!ret) ++ ret = reg_write(client, MT9M001_ROW_START, rect.top); ++ if (!ret) ++ ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); ++ if (!ret) ++ ret = reg_write(client, MT9M001_WINDOW_HEIGHT, ++ rect.height + mt9m001->y_skip_top - 1); ++ if (!ret && v4l2_ctrl_g_ctrl(mt9m001->autoexposure) == V4L2_EXPOSURE_AUTO) ++ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h); ++ ++ if (!ret) ++ mt9m001->rect = rect; ++ ++ return ret; ++} ++ ++static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ ++ a->c = mt9m001->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = MT9M001_COLUMN_SKIP; ++ a->bounds.top = MT9M001_ROW_SKIP; ++ a->bounds.width = MT9M001_MAX_WIDTH; ++ a->bounds.height = MT9M001_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int mt9m001_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ ++ mf->width = mt9m001->rect.width; ++ mf->height = mt9m001->rect.height; ++ mf->code = mt9m001->fmt->code; ++ mf->colorspace = mt9m001->fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9m001_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ struct v4l2_crop a = { ++ .c = { ++ .left = mt9m001->rect.left, ++ .top = mt9m001->rect.top, ++ .width = mf->width, ++ .height = mf->height, ++ }, ++ }; ++ int ret; ++ ++ /* No support for scaling so far, just crop. TODO: use skipping */ ++ ret = mt9m001_s_crop(sd, &a); ++ if (!ret) { ++ mf->width = mt9m001->rect.width; ++ mf->height = mt9m001->rect.height; ++ mt9m001->fmt = mt9m001_find_datafmt(mf->code, ++ mt9m001->fmts, mt9m001->num_fmts); ++ mf->colorspace = mt9m001->fmt->colorspace; ++ } ++ ++ return ret; ++} ++ ++static int mt9m001_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ const struct mt9m001_datafmt *fmt; ++ ++ v4l_bound_align_image(&mf->width, MT9M001_MIN_WIDTH, ++ MT9M001_MAX_WIDTH, 1, ++ &mf->height, MT9M001_MIN_HEIGHT + mt9m001->y_skip_top, ++ MT9M001_MAX_HEIGHT + mt9m001->y_skip_top, 0, 0); ++ ++ if (mt9m001->fmts == mt9m001_colour_fmts) ++ mf->height = ALIGN(mf->height - 1, 2); ++ ++ fmt = mt9m001_find_datafmt(mf->code, mt9m001->fmts, ++ mt9m001->num_fmts); ++ if (!fmt) { ++ fmt = mt9m001->fmt; ++ mf->code = fmt->code; ++ } ++ ++ mf->colorspace = fmt->colorspace; ++ ++ return 0; ++} ++ ++static int mt9m001_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = mt9m001->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9m001_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ reg->size = 2; ++ reg->val = reg_read(client, reg->reg); ++ ++ if (reg->val > 0xffff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int mt9m001_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int mt9m001_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9m001 *mt9m001 = container_of(ctrl->handler, ++ struct mt9m001, hdl); ++ s32 min, max; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_AUTO: ++ min = mt9m001->exposure->minimum; ++ max = mt9m001->exposure->maximum; ++ mt9m001->exposure->val = ++ (524 + (mt9m001->total_h - 1) * (max - min)) / 1048 + min; ++ break; ++ } ++ return 0; ++} ++ ++static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9m001 *mt9m001 = container_of(ctrl->handler, ++ struct mt9m001, hdl); ++ struct v4l2_subdev *sd = &mt9m001->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct v4l2_ctrl *exp = mt9m001->exposure; ++ int data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); ++ else ++ data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ ++ case V4L2_CID_GAIN: ++ /* See Datasheet Table 7, Gain settings. */ ++ if (ctrl->val <= ctrl->default_value) { ++ /* Pack it into 0..1 step 0.125, register values 0..8 */ ++ unsigned long range = ctrl->default_value - ctrl->minimum; ++ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; ++ ++ dev_dbg(&client->dev, "Setting gain %d\n", data); ++ data = reg_write(client, MT9M001_GLOBAL_GAIN, data); ++ if (data < 0) ++ return -EIO; ++ } else { ++ /* Pack it into 1.125..15 variable step, register values 9..67 */ ++ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ ++ unsigned long range = ctrl->maximum - ctrl->default_value - 1; ++ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * ++ 111 + range / 2) / range + 9; ++ ++ if (gain <= 32) ++ data = gain; ++ else if (gain <= 64) ++ data = ((gain - 32) * 16 + 16) / 32 + 80; ++ else ++ data = ((gain - 64) * 7 + 28) / 56 + 96; ++ ++ dev_dbg(&client->dev, "Setting gain from %d to %d\n", ++ reg_read(client, MT9M001_GLOBAL_GAIN), data); ++ data = reg_write(client, MT9M001_GLOBAL_GAIN, data); ++ if (data < 0) ++ return -EIO; ++ } ++ return 0; ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ if (ctrl->val == V4L2_EXPOSURE_MANUAL) { ++ unsigned long range = exp->maximum - exp->minimum; ++ unsigned long shutter = ((exp->val - exp->minimum) * 1048 + ++ range / 2) / range + 1; ++ ++ dev_dbg(&client->dev, ++ "Setting shutter width from %d to %lu\n", ++ reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); ++ if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) ++ return -EIO; ++ } else { ++ const u16 vblank = 25; ++ ++ mt9m001->total_h = mt9m001->rect.height + ++ mt9m001->y_skip_top + vblank; ++ if (reg_write(client, MT9M001_SHUTTER_WIDTH, mt9m001->total_h) < 0) ++ return -EIO; ++ } ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * Interface active, can use i2c. If it fails, it can indeed mean, that ++ * this wasn't our capture interface, so, we wait for the right one ++ */ ++static int mt9m001_video_probe(struct soc_camera_subdev_desc *ssdd, ++ struct i2c_client *client) ++{ ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ s32 data; ++ unsigned long flags; ++ int ret; ++ ++ ret = mt9m001_s_power(&mt9m001->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable the chip */ ++ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); ++ dev_dbg(&client->dev, "write: %d\n", data); ++ ++ /* Read out the chip version register */ ++ data = reg_read(client, MT9M001_CHIP_VERSION); ++ ++ /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ ++ switch (data) { ++ case 0x8411: ++ case 0x8421: ++ mt9m001->model = V4L2_IDENT_MT9M001C12ST; ++ mt9m001->fmts = mt9m001_colour_fmts; ++ break; ++ case 0x8431: ++ mt9m001->model = V4L2_IDENT_MT9M001C12STM; ++ mt9m001->fmts = mt9m001_monochrome_fmts; ++ break; ++ default: ++ dev_err(&client->dev, ++ "No MT9M001 chip detected, register read %x\n", data); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ mt9m001->num_fmts = 0; ++ ++ /* ++ * This is a 10bit sensor, so by default we only allow 10bit. ++ * The platform may support different bus widths due to ++ * different routing of the data lines. ++ */ ++ if (ssdd->query_bus_param) ++ flags = ssdd->query_bus_param(ssdd); ++ else ++ flags = SOCAM_DATAWIDTH_10; ++ ++ if (flags & SOCAM_DATAWIDTH_10) ++ mt9m001->num_fmts++; ++ else ++ mt9m001->fmts++; ++ ++ if (flags & SOCAM_DATAWIDTH_8) ++ mt9m001->num_fmts++; ++ ++ mt9m001->fmt = &mt9m001->fmts[0]; ++ ++ dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, ++ data == 0x8431 ? "C12STM" : "C12ST"); ++ ++ ret = mt9m001_init(client); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed to initialise the camera\n"); ++ goto done; ++ } ++ ++ /* mt9m001_init() has reset the chip, returning registers to defaults */ ++ ret = v4l2_ctrl_handler_setup(&mt9m001->hdl); ++ ++done: ++ mt9m001_s_power(&mt9m001->subdev, 0); ++ return ret; ++} ++ ++static void mt9m001_video_remove(struct soc_camera_subdev_desc *ssdd) ++{ ++ if (ssdd->free_bus) ++ ssdd->free_bus(ssdd); ++} ++ ++static int mt9m001_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ ++ *lines = mt9m001->y_skip_top; ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops mt9m001_ctrl_ops = { ++ .g_volatile_ctrl = mt9m001_g_volatile_ctrl, ++ .s_ctrl = mt9m001_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { ++ .g_chip_ident = mt9m001_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9m001_g_register, ++ .s_register = mt9m001_s_register, ++#endif ++ .s_power = mt9m001_s_power, ++}; ++ ++static int mt9m001_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ ++ if (index >= mt9m001->num_fmts) ++ return -EINVAL; ++ ++ *code = mt9m001->fmts[index].code; ++ return 0; ++} ++ ++static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ /* MT9M001 has all capture_format parameters fixed */ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH | V4L2_MBUS_MASTER; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ const struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; ++ ++ if (ssdd->set_bus_param) ++ return ssdd->set_bus_param(ssdd, 1 << (bps - 1)); ++ ++ /* ++ * Without board specific bus width settings we only support the ++ * sensors native bus width ++ */ ++ return bps == 10 ? 0 : -EINVAL; ++} ++ ++static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { ++ .s_stream = mt9m001_s_stream, ++ .s_mbus_fmt = mt9m001_s_fmt, ++ .g_mbus_fmt = mt9m001_g_fmt, ++ .try_mbus_fmt = mt9m001_try_fmt, ++ .s_crop = mt9m001_s_crop, ++ .g_crop = mt9m001_g_crop, ++ .cropcap = mt9m001_cropcap, ++ .enum_mbus_fmt = mt9m001_enum_fmt, ++ .g_mbus_config = mt9m001_g_mbus_config, ++ .s_mbus_config = mt9m001_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_sensor_ops mt9m001_subdev_sensor_ops = { ++ .g_skip_top_lines = mt9m001_g_skip_top_lines, ++}; ++ ++static struct v4l2_subdev_ops mt9m001_subdev_ops = { ++ .core = &mt9m001_subdev_core_ops, ++ .video = &mt9m001_subdev_video_ops, ++ .sensor = &mt9m001_subdev_sensor_ops, ++}; ++ ++static int mt9m001_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9m001 *mt9m001; ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "MT9M001 driver needs platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9m001 = devm_kzalloc(&client->dev, sizeof(struct mt9m001), GFP_KERNEL); ++ if (!mt9m001) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); ++ v4l2_ctrl_handler_init(&mt9m001->hdl, 4); ++ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, ++ V4L2_CID_GAIN, 0, 127, 1, 64); ++ mt9m001->exposure = v4l2_ctrl_new_std(&mt9m001->hdl, &mt9m001_ctrl_ops, ++ V4L2_CID_EXPOSURE, 1, 255, 1, 255); ++ /* ++ * Simulated autoexposure. If enabled, we calculate shutter width ++ * ourselves in the driver based on vertical blanking and frame width ++ */ ++ mt9m001->autoexposure = v4l2_ctrl_new_std_menu(&mt9m001->hdl, ++ &mt9m001_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, ++ V4L2_EXPOSURE_AUTO); ++ mt9m001->subdev.ctrl_handler = &mt9m001->hdl; ++ if (mt9m001->hdl.error) ++ return mt9m001->hdl.error; ++ ++ v4l2_ctrl_auto_cluster(2, &mt9m001->autoexposure, ++ V4L2_EXPOSURE_MANUAL, true); ++ ++ /* Second stage probe - when a capture adapter is there */ ++ mt9m001->y_skip_top = 0; ++ mt9m001->rect.left = MT9M001_COLUMN_SKIP; ++ mt9m001->rect.top = MT9M001_ROW_SKIP; ++ mt9m001->rect.width = MT9M001_MAX_WIDTH; ++ mt9m001->rect.height = MT9M001_MAX_HEIGHT; ++ ++ ret = mt9m001_video_probe(ssdd, client); ++ if (ret) ++ v4l2_ctrl_handler_free(&mt9m001->hdl); ++ ++ return ret; ++} ++ ++static int mt9m001_remove(struct i2c_client *client) ++{ ++ struct mt9m001 *mt9m001 = to_mt9m001(client); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ v4l2_device_unregister_subdev(&mt9m001->subdev); ++ v4l2_ctrl_handler_free(&mt9m001->hdl); ++ mt9m001_video_remove(ssdd); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id mt9m001_id[] = { ++ { "mt9m001", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9m001_id); ++ ++static struct i2c_driver mt9m001_i2c_driver = { ++ .driver = { ++ .name = "mt9m001", ++ }, ++ .probe = mt9m001_probe, ++ .remove = mt9m001_remove, ++ .id_table = mt9m001_id, ++}; ++ ++module_i2c_driver(mt9m001_i2c_driver); ++ ++MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/mt9m111.c b/drivers/media/i2c/soc_camera/mt9m111.c +new file mode 100644 +index 0000000..bbc4ff9 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/mt9m111.c +@@ -0,0 +1,1040 @@ ++/* ++ * Driver for MT9M111/MT9M112/MT9M131 CMOS Image Sensor from Micron/Aptina ++ * ++ * Copyright (C) 2008, Robert Jarzmik ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * MT9M111, MT9M112 and MT9M131: ++ * i2c address is 0x48 or 0x5d (depending on SADDR pin) ++ * The platform has to define struct i2c_board_info objects and link to them ++ * from struct soc_camera_host_desc ++ */ ++ ++/* ++ * Sensor core register addresses (0x000..0x0ff) ++ */ ++#define MT9M111_CHIP_VERSION 0x000 ++#define MT9M111_ROW_START 0x001 ++#define MT9M111_COLUMN_START 0x002 ++#define MT9M111_WINDOW_HEIGHT 0x003 ++#define MT9M111_WINDOW_WIDTH 0x004 ++#define MT9M111_HORIZONTAL_BLANKING_B 0x005 ++#define MT9M111_VERTICAL_BLANKING_B 0x006 ++#define MT9M111_HORIZONTAL_BLANKING_A 0x007 ++#define MT9M111_VERTICAL_BLANKING_A 0x008 ++#define MT9M111_SHUTTER_WIDTH 0x009 ++#define MT9M111_ROW_SPEED 0x00a ++#define MT9M111_EXTRA_DELAY 0x00b ++#define MT9M111_SHUTTER_DELAY 0x00c ++#define MT9M111_RESET 0x00d ++#define MT9M111_READ_MODE_B 0x020 ++#define MT9M111_READ_MODE_A 0x021 ++#define MT9M111_FLASH_CONTROL 0x023 ++#define MT9M111_GREEN1_GAIN 0x02b ++#define MT9M111_BLUE_GAIN 0x02c ++#define MT9M111_RED_GAIN 0x02d ++#define MT9M111_GREEN2_GAIN 0x02e ++#define MT9M111_GLOBAL_GAIN 0x02f ++#define MT9M111_CONTEXT_CONTROL 0x0c8 ++#define MT9M111_PAGE_MAP 0x0f0 ++#define MT9M111_BYTE_WISE_ADDR 0x0f1 ++ ++#define MT9M111_RESET_SYNC_CHANGES (1 << 15) ++#define MT9M111_RESET_RESTART_BAD_FRAME (1 << 9) ++#define MT9M111_RESET_SHOW_BAD_FRAMES (1 << 8) ++#define MT9M111_RESET_RESET_SOC (1 << 5) ++#define MT9M111_RESET_OUTPUT_DISABLE (1 << 4) ++#define MT9M111_RESET_CHIP_ENABLE (1 << 3) ++#define MT9M111_RESET_ANALOG_STANDBY (1 << 2) ++#define MT9M111_RESET_RESTART_FRAME (1 << 1) ++#define MT9M111_RESET_RESET_MODE (1 << 0) ++ ++#define MT9M111_RM_FULL_POWER_RD (0 << 10) ++#define MT9M111_RM_LOW_POWER_RD (1 << 10) ++#define MT9M111_RM_COL_SKIP_4X (1 << 5) ++#define MT9M111_RM_ROW_SKIP_4X (1 << 4) ++#define MT9M111_RM_COL_SKIP_2X (1 << 3) ++#define MT9M111_RM_ROW_SKIP_2X (1 << 2) ++#define MT9M111_RMB_MIRROR_COLS (1 << 1) ++#define MT9M111_RMB_MIRROR_ROWS (1 << 0) ++#define MT9M111_CTXT_CTRL_RESTART (1 << 15) ++#define MT9M111_CTXT_CTRL_DEFECTCOR_B (1 << 12) ++#define MT9M111_CTXT_CTRL_RESIZE_B (1 << 10) ++#define MT9M111_CTXT_CTRL_CTRL2_B (1 << 9) ++#define MT9M111_CTXT_CTRL_GAMMA_B (1 << 8) ++#define MT9M111_CTXT_CTRL_XENON_EN (1 << 7) ++#define MT9M111_CTXT_CTRL_READ_MODE_B (1 << 3) ++#define MT9M111_CTXT_CTRL_LED_FLASH_EN (1 << 2) ++#define MT9M111_CTXT_CTRL_VBLANK_SEL_B (1 << 1) ++#define MT9M111_CTXT_CTRL_HBLANK_SEL_B (1 << 0) ++ ++/* ++ * Colorpipe register addresses (0x100..0x1ff) ++ */ ++#define MT9M111_OPER_MODE_CTRL 0x106 ++#define MT9M111_OUTPUT_FORMAT_CTRL 0x108 ++#define MT9M111_REDUCER_XZOOM_B 0x1a0 ++#define MT9M111_REDUCER_XSIZE_B 0x1a1 ++#define MT9M111_REDUCER_YZOOM_B 0x1a3 ++#define MT9M111_REDUCER_YSIZE_B 0x1a4 ++#define MT9M111_REDUCER_XZOOM_A 0x1a6 ++#define MT9M111_REDUCER_XSIZE_A 0x1a7 ++#define MT9M111_REDUCER_YZOOM_A 0x1a9 ++#define MT9M111_REDUCER_YSIZE_A 0x1aa ++ ++#define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a ++#define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b ++ ++#define MT9M111_OPMODE_AUTOEXPO_EN (1 << 14) ++#define MT9M111_OPMODE_AUTOWHITEBAL_EN (1 << 1) ++#define MT9M111_OUTFMT_FLIP_BAYER_COL (1 << 9) ++#define MT9M111_OUTFMT_FLIP_BAYER_ROW (1 << 8) ++#define MT9M111_OUTFMT_PROCESSED_BAYER (1 << 14) ++#define MT9M111_OUTFMT_BYPASS_IFP (1 << 10) ++#define MT9M111_OUTFMT_INV_PIX_CLOCK (1 << 9) ++#define MT9M111_OUTFMT_RGB (1 << 8) ++#define MT9M111_OUTFMT_RGB565 (0 << 6) ++#define MT9M111_OUTFMT_RGB555 (1 << 6) ++#define MT9M111_OUTFMT_RGB444x (2 << 6) ++#define MT9M111_OUTFMT_RGBx444 (3 << 6) ++#define MT9M111_OUTFMT_TST_RAMP_OFF (0 << 4) ++#define MT9M111_OUTFMT_TST_RAMP_COL (1 << 4) ++#define MT9M111_OUTFMT_TST_RAMP_ROW (2 << 4) ++#define MT9M111_OUTFMT_TST_RAMP_FRAME (3 << 4) ++#define MT9M111_OUTFMT_SHIFT_3_UP (1 << 3) ++#define MT9M111_OUTFMT_AVG_CHROMA (1 << 2) ++#define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1) ++#define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0) ++ ++/* ++ * Camera control register addresses (0x200..0x2ff not implemented) ++ */ ++ ++#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg) ++#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val)) ++#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val)) ++#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val)) ++#define reg_mask(reg, val, mask) mt9m111_reg_mask(client, MT9M111_##reg, \ ++ (val), (mask)) ++ ++#define MT9M111_MIN_DARK_ROWS 8 ++#define MT9M111_MIN_DARK_COLS 26 ++#define MT9M111_MAX_HEIGHT 1024 ++#define MT9M111_MAX_WIDTH 1280 ++ ++struct mt9m111_context { ++ u16 read_mode; ++ u16 blanking_h; ++ u16 blanking_v; ++ u16 reducer_xzoom; ++ u16 reducer_yzoom; ++ u16 reducer_xsize; ++ u16 reducer_ysize; ++ u16 output_fmt_ctrl2; ++ u16 control; ++}; ++ ++static struct mt9m111_context context_a = { ++ .read_mode = MT9M111_READ_MODE_A, ++ .blanking_h = MT9M111_HORIZONTAL_BLANKING_A, ++ .blanking_v = MT9M111_VERTICAL_BLANKING_A, ++ .reducer_xzoom = MT9M111_REDUCER_XZOOM_A, ++ .reducer_yzoom = MT9M111_REDUCER_YZOOM_A, ++ .reducer_xsize = MT9M111_REDUCER_XSIZE_A, ++ .reducer_ysize = MT9M111_REDUCER_YSIZE_A, ++ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_A, ++ .control = MT9M111_CTXT_CTRL_RESTART, ++}; ++ ++static struct mt9m111_context context_b = { ++ .read_mode = MT9M111_READ_MODE_B, ++ .blanking_h = MT9M111_HORIZONTAL_BLANKING_B, ++ .blanking_v = MT9M111_VERTICAL_BLANKING_B, ++ .reducer_xzoom = MT9M111_REDUCER_XZOOM_B, ++ .reducer_yzoom = MT9M111_REDUCER_YZOOM_B, ++ .reducer_xsize = MT9M111_REDUCER_XSIZE_B, ++ .reducer_ysize = MT9M111_REDUCER_YSIZE_B, ++ .output_fmt_ctrl2 = MT9M111_OUTPUT_FORMAT_CTRL2_B, ++ .control = MT9M111_CTXT_CTRL_RESTART | ++ MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B | ++ MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B | ++ MT9M111_CTXT_CTRL_READ_MODE_B | MT9M111_CTXT_CTRL_VBLANK_SEL_B | ++ MT9M111_CTXT_CTRL_HBLANK_SEL_B, ++}; ++ ++/* MT9M111 has only one fixed colorspace per pixelcode */ ++struct mt9m111_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++static const struct mt9m111_datafmt mt9m111_colour_fmts[] = { ++ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_VYUY8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_BGR565_2X8_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_BGR565_2X8_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, ++}; ++ ++struct mt9m111 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct v4l2_ctrl *gain; ++ int model; /* V4L2_IDENT_MT9M111 or V4L2_IDENT_MT9M112 code ++ * from v4l2-chip-ident.h */ ++ struct mt9m111_context *ctx; ++ struct v4l2_rect rect; /* cropping rectangle */ ++ int width; /* output */ ++ int height; /* sizes */ ++ struct mutex power_lock; /* lock to protect power_count */ ++ int power_count; ++ const struct mt9m111_datafmt *fmt; ++ int lastpage; /* PageMap cache value */ ++}; ++ ++/* Find a data format by a pixel code */ ++static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111, ++ enum v4l2_mbus_pixelcode code) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(mt9m111_colour_fmts); i++) ++ if (mt9m111_colour_fmts[i].code == code) ++ return mt9m111_colour_fmts + i; ++ ++ return mt9m111->fmt; ++} ++ ++static struct mt9m111 *to_mt9m111(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct mt9m111, subdev); ++} ++ ++static int reg_page_map_set(struct i2c_client *client, const u16 reg) ++{ ++ int ret; ++ u16 page; ++ struct mt9m111 *mt9m111 = to_mt9m111(client); ++ ++ page = (reg >> 8); ++ if (page == mt9m111->lastpage) ++ return 0; ++ if (page > 2) ++ return -EINVAL; ++ ++ ret = i2c_smbus_write_word_swapped(client, MT9M111_PAGE_MAP, page); ++ if (!ret) ++ mt9m111->lastpage = page; ++ return ret; ++} ++ ++static int mt9m111_reg_read(struct i2c_client *client, const u16 reg) ++{ ++ int ret; ++ ++ ret = reg_page_map_set(client, reg); ++ if (!ret) ++ ret = i2c_smbus_read_word_swapped(client, reg & 0xff); ++ ++ dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); ++ return ret; ++} ++ ++static int mt9m111_reg_write(struct i2c_client *client, const u16 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_page_map_set(client, reg); ++ if (!ret) ++ ret = i2c_smbus_write_word_swapped(client, reg & 0xff, data); ++ dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); ++ return ret; ++} ++ ++static int mt9m111_reg_set(struct i2c_client *client, const u16 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = mt9m111_reg_read(client, reg); ++ if (ret >= 0) ++ ret = mt9m111_reg_write(client, reg, ret | data); ++ return ret; ++} ++ ++static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = mt9m111_reg_read(client, reg); ++ if (ret >= 0) ++ ret = mt9m111_reg_write(client, reg, ret & ~data); ++ return ret; ++} ++ ++static int mt9m111_reg_mask(struct i2c_client *client, const u16 reg, ++ const u16 data, const u16 mask) ++{ ++ int ret; ++ ++ ret = mt9m111_reg_read(client, reg); ++ if (ret >= 0) ++ ret = mt9m111_reg_write(client, reg, (ret & ~mask) | data); ++ return ret; ++} ++ ++static int mt9m111_set_context(struct mt9m111 *mt9m111, ++ struct mt9m111_context *ctx) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ return reg_write(CONTEXT_CONTROL, ctx->control); ++} ++ ++static int mt9m111_setup_rect_ctx(struct mt9m111 *mt9m111, ++ struct mt9m111_context *ctx, struct v4l2_rect *rect, ++ unsigned int width, unsigned int height) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret = mt9m111_reg_write(client, ctx->reducer_xzoom, rect->width); ++ if (!ret) ++ ret = mt9m111_reg_write(client, ctx->reducer_yzoom, rect->height); ++ if (!ret) ++ ret = mt9m111_reg_write(client, ctx->reducer_xsize, width); ++ if (!ret) ++ ret = mt9m111_reg_write(client, ctx->reducer_ysize, height); ++ return ret; ++} ++ ++static int mt9m111_setup_geometry(struct mt9m111 *mt9m111, struct v4l2_rect *rect, ++ int width, int height, enum v4l2_mbus_pixelcode code) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret; ++ ++ ret = reg_write(COLUMN_START, rect->left); ++ if (!ret) ++ ret = reg_write(ROW_START, rect->top); ++ ++ if (!ret) ++ ret = reg_write(WINDOW_WIDTH, rect->width); ++ if (!ret) ++ ret = reg_write(WINDOW_HEIGHT, rect->height); ++ ++ if (code != V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) { ++ /* IFP in use, down-scaling possible */ ++ if (!ret) ++ ret = mt9m111_setup_rect_ctx(mt9m111, &context_b, ++ rect, width, height); ++ if (!ret) ++ ret = mt9m111_setup_rect_ctx(mt9m111, &context_a, ++ rect, width, height); ++ } ++ ++ dev_dbg(&client->dev, "%s(%x): %ux%u@%u:%u -> %ux%u = %d\n", ++ __func__, code, rect->width, rect->height, rect->left, rect->top, ++ width, height, ret); ++ ++ return ret; ++} ++ ++static int mt9m111_enable(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ return reg_write(RESET, MT9M111_RESET_CHIP_ENABLE); ++} ++ ++static int mt9m111_reset(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret; ++ ++ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); ++ if (!ret) ++ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC); ++ if (!ret) ++ ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE ++ | MT9M111_RESET_RESET_SOC); ++ ++ return ret; ++} ++ ++static int mt9m111_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct v4l2_rect rect = a->c; ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ int width, height; ++ int ret; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if (mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 || ++ mt9m111->fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) { ++ /* Bayer format - even size lengths */ ++ rect.width = ALIGN(rect.width, 2); ++ rect.height = ALIGN(rect.height, 2); ++ /* Let the user play with the starting pixel */ ++ } ++ ++ /* FIXME: the datasheet doesn't specify minimum sizes */ ++ soc_camera_limit_side(&rect.left, &rect.width, ++ MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH); ++ ++ soc_camera_limit_side(&rect.top, &rect.height, ++ MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT); ++ ++ width = min(mt9m111->width, rect.width); ++ height = min(mt9m111->height, rect.height); ++ ++ ret = mt9m111_setup_geometry(mt9m111, &rect, width, height, mt9m111->fmt->code); ++ if (!ret) { ++ mt9m111->rect = rect; ++ mt9m111->width = width; ++ mt9m111->height = height; ++ } ++ ++ return ret; ++} ++ ++static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ ++ a->c = mt9m111->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ a->bounds.left = MT9M111_MIN_DARK_COLS; ++ a->bounds.top = MT9M111_MIN_DARK_ROWS; ++ a->bounds.width = MT9M111_MAX_WIDTH; ++ a->bounds.height = MT9M111_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int mt9m111_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ ++ mf->width = mt9m111->width; ++ mf->height = mt9m111->height; ++ mf->code = mt9m111->fmt->code; ++ mf->colorspace = mt9m111->fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111, ++ enum v4l2_mbus_pixelcode code) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ u16 data_outfmt2, mask_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER | ++ MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB | ++ MT9M111_OUTFMT_RGB565 | MT9M111_OUTFMT_RGB555 | ++ MT9M111_OUTFMT_RGB444x | MT9M111_OUTFMT_RGBx444 | ++ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | ++ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; ++ int ret; ++ ++ switch (code) { ++ case V4L2_MBUS_FMT_SBGGR8_1X8: ++ data_outfmt2 = MT9M111_OUTFMT_PROCESSED_BAYER | ++ MT9M111_OUTFMT_RGB; ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE: ++ data_outfmt2 = MT9M111_OUTFMT_BYPASS_IFP | MT9M111_OUTFMT_RGB; ++ break; ++ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555 | ++ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; ++ break; ++ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | ++ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; ++ break; ++ case V4L2_MBUS_FMT_BGR565_2X8_BE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | ++ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; ++ break; ++ case V4L2_MBUS_FMT_BGR565_2X8_LE: ++ data_outfmt2 = MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565 | ++ MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | ++ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; ++ break; ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ data_outfmt2 = 0; ++ break; ++ case V4L2_MBUS_FMT_VYUY8_2X8: ++ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; ++ break; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN; ++ break; ++ case V4L2_MBUS_FMT_YVYU8_2X8: ++ data_outfmt2 = MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN | ++ MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B; ++ break; ++ default: ++ dev_err(&client->dev, "Pixel format not handled: %x\n", code); ++ return -EINVAL; ++ } ++ ++ ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2, ++ data_outfmt2, mask_outfmt2); ++ if (!ret) ++ ret = mt9m111_reg_mask(client, context_b.output_fmt_ctrl2, ++ data_outfmt2, mask_outfmt2); ++ ++ return ret; ++} ++ ++static int mt9m111_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ const struct mt9m111_datafmt *fmt; ++ struct v4l2_rect *rect = &mt9m111->rect; ++ bool bayer; ++ ++ fmt = mt9m111_find_datafmt(mt9m111, mf->code); ++ ++ bayer = fmt->code == V4L2_MBUS_FMT_SBGGR8_1X8 || ++ fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE; ++ ++ /* ++ * With Bayer format enforce even side lengths, but let the user play ++ * with the starting pixel ++ */ ++ if (bayer) { ++ rect->width = ALIGN(rect->width, 2); ++ rect->height = ALIGN(rect->height, 2); ++ } ++ ++ if (fmt->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE) { ++ /* IFP bypass mode, no scaling */ ++ mf->width = rect->width; ++ mf->height = rect->height; ++ } else { ++ /* No upscaling */ ++ if (mf->width > rect->width) ++ mf->width = rect->width; ++ if (mf->height > rect->height) ++ mf->height = rect->height; ++ } ++ ++ dev_dbg(&client->dev, "%s(): %ux%u, code=%x\n", __func__, ++ mf->width, mf->height, fmt->code); ++ ++ mf->code = fmt->code; ++ mf->colorspace = fmt->colorspace; ++ ++ return 0; ++} ++ ++static int mt9m111_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ const struct mt9m111_datafmt *fmt; ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ struct v4l2_rect *rect = &mt9m111->rect; ++ int ret; ++ ++ mt9m111_try_fmt(sd, mf); ++ fmt = mt9m111_find_datafmt(mt9m111, mf->code); ++ /* try_fmt() guarantees fmt != NULL && fmt->code == mf->code */ ++ ++ ret = mt9m111_setup_geometry(mt9m111, rect, mf->width, mf->height, mf->code); ++ if (!ret) ++ ret = mt9m111_set_pixfmt(mt9m111, mf->code); ++ if (!ret) { ++ mt9m111->width = mf->width; ++ mt9m111->height = mf->height; ++ mt9m111->fmt = fmt; ++ } ++ ++ return ret; ++} ++ ++static int mt9m111_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = mt9m111->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9m111_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int val; ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) ++ return -EINVAL; ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ val = mt9m111_reg_read(client, reg->reg); ++ reg->size = 2; ++ reg->val = (u64)val; ++ ++ if (reg->val > 0xffff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int mt9m111_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (mt9m111_reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int mt9m111_set_flip(struct mt9m111 *mt9m111, int flip, int mask) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret; ++ ++ if (flip) ++ ret = mt9m111_reg_set(client, mt9m111->ctx->read_mode, mask); ++ else ++ ret = mt9m111_reg_clear(client, mt9m111->ctx->read_mode, mask); ++ ++ return ret; ++} ++ ++static int mt9m111_get_global_gain(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int data; ++ ++ data = reg_read(GLOBAL_GAIN); ++ if (data >= 0) ++ return (data & 0x2f) * (1 << ((data >> 10) & 1)) * ++ (1 << ((data >> 9) & 1)); ++ return data; ++} ++ ++static int mt9m111_set_global_gain(struct mt9m111 *mt9m111, int gain) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ u16 val; ++ ++ if (gain > 63 * 2 * 2) ++ return -EINVAL; ++ ++ if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) ++ val = (1 << 10) | (1 << 9) | (gain / 4); ++ else if ((gain >= 64) && (gain < 64 * 2)) ++ val = (1 << 9) | (gain / 2); ++ else ++ val = gain; ++ ++ return reg_write(GLOBAL_GAIN, val); ++} ++ ++static int mt9m111_set_autoexposure(struct mt9m111 *mt9m111, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ ++ if (on) ++ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); ++ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOEXPO_EN); ++} ++ ++static int mt9m111_set_autowhitebalance(struct mt9m111 *mt9m111, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ ++ if (on) ++ return reg_set(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); ++ return reg_clear(OPER_MODE_CTRL, MT9M111_OPMODE_AUTOWHITEBAL_EN); ++} ++ ++static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9m111 *mt9m111 = container_of(ctrl->handler, ++ struct mt9m111, hdl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ return mt9m111_set_flip(mt9m111, ctrl->val, ++ MT9M111_RMB_MIRROR_ROWS); ++ case V4L2_CID_HFLIP: ++ return mt9m111_set_flip(mt9m111, ctrl->val, ++ MT9M111_RMB_MIRROR_COLS); ++ case V4L2_CID_GAIN: ++ return mt9m111_set_global_gain(mt9m111, ctrl->val); ++ case V4L2_CID_EXPOSURE_AUTO: ++ return mt9m111_set_autoexposure(mt9m111, ctrl->val); ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ return mt9m111_set_autowhitebalance(mt9m111, ctrl->val); ++ } ++ ++ return -EINVAL; ++} ++ ++static int mt9m111_suspend(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret; ++ ++ v4l2_ctrl_s_ctrl(mt9m111->gain, mt9m111_get_global_gain(mt9m111)); ++ ++ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); ++ if (!ret) ++ ret = reg_set(RESET, MT9M111_RESET_RESET_SOC | ++ MT9M111_RESET_OUTPUT_DISABLE | ++ MT9M111_RESET_ANALOG_STANDBY); ++ if (!ret) ++ ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); ++ ++ return ret; ++} ++ ++static void mt9m111_restore_state(struct mt9m111 *mt9m111) ++{ ++ mt9m111_set_context(mt9m111, mt9m111->ctx); ++ mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code); ++ mt9m111_setup_geometry(mt9m111, &mt9m111->rect, ++ mt9m111->width, mt9m111->height, mt9m111->fmt->code); ++ v4l2_ctrl_handler_setup(&mt9m111->hdl); ++} ++ ++static int mt9m111_resume(struct mt9m111 *mt9m111) ++{ ++ int ret = mt9m111_enable(mt9m111); ++ if (!ret) ++ ret = mt9m111_reset(mt9m111); ++ if (!ret) ++ mt9m111_restore_state(mt9m111); ++ ++ return ret; ++} ++ ++static int mt9m111_init(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ int ret; ++ ++ /* Default HIGHPOWER context */ ++ mt9m111->ctx = &context_b; ++ ret = mt9m111_enable(mt9m111); ++ if (!ret) ++ ret = mt9m111_reset(mt9m111); ++ if (!ret) ++ ret = mt9m111_set_context(mt9m111, mt9m111->ctx); ++ if (ret) ++ dev_err(&client->dev, "mt9m111 init failed: %d\n", ret); ++ return ret; ++} ++ ++static int mt9m111_power_on(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ ret = soc_camera_power_on(&client->dev, ssdd); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9m111_resume(mt9m111); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); ++ soc_camera_power_off(&client->dev, ssdd); ++ } ++ ++ return ret; ++} ++ ++static void mt9m111_power_off(struct mt9m111 *mt9m111) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ mt9m111_suspend(mt9m111); ++ soc_camera_power_off(&client->dev, ssdd); ++} ++ ++static int mt9m111_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); ++ int ret = 0; ++ ++ mutex_lock(&mt9m111->power_lock); ++ ++ /* ++ * If the power count is modified from 0 to != 0 or from != 0 to 0, ++ * update the power state. ++ */ ++ if (mt9m111->power_count == !on) { ++ if (on) ++ ret = mt9m111_power_on(mt9m111); ++ else ++ mt9m111_power_off(mt9m111); ++ } ++ ++ if (!ret) { ++ /* Update the power count. */ ++ mt9m111->power_count += on ? 1 : -1; ++ WARN_ON(mt9m111->power_count < 0); ++ } ++ ++ mutex_unlock(&mt9m111->power_lock); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = { ++ .s_ctrl = mt9m111_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { ++ .g_chip_ident = mt9m111_g_chip_ident, ++ .s_power = mt9m111_s_power, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9m111_g_register, ++ .s_register = mt9m111_s_register, ++#endif ++}; ++ ++static int mt9m111_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(mt9m111_colour_fmts)) ++ return -EINVAL; ++ ++ *code = mt9m111_colour_fmts[index].code; ++ return 0; ++} ++ ++static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { ++ .s_mbus_fmt = mt9m111_s_fmt, ++ .g_mbus_fmt = mt9m111_g_fmt, ++ .try_mbus_fmt = mt9m111_try_fmt, ++ .s_crop = mt9m111_s_crop, ++ .g_crop = mt9m111_g_crop, ++ .cropcap = mt9m111_cropcap, ++ .enum_mbus_fmt = mt9m111_enum_fmt, ++ .g_mbus_config = mt9m111_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops mt9m111_subdev_ops = { ++ .core = &mt9m111_subdev_core_ops, ++ .video = &mt9m111_subdev_video_ops, ++}; ++ ++/* ++ * Interface active, can use i2c. If it fails, it can indeed mean, that ++ * this wasn't our capture interface, so, we wait for the right one ++ */ ++static int mt9m111_video_probe(struct i2c_client *client) ++{ ++ struct mt9m111 *mt9m111 = to_mt9m111(client); ++ s32 data; ++ int ret; ++ ++ ret = mt9m111_s_power(&mt9m111->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ data = reg_read(CHIP_VERSION); ++ ++ switch (data) { ++ case 0x143a: /* MT9M111 or MT9M131 */ ++ mt9m111->model = V4L2_IDENT_MT9M111; ++ dev_info(&client->dev, ++ "Detected a MT9M111/MT9M131 chip ID %x\n", data); ++ break; ++ case 0x148c: /* MT9M112 */ ++ mt9m111->model = V4L2_IDENT_MT9M112; ++ dev_info(&client->dev, "Detected a MT9M112 chip ID %x\n", data); ++ break; ++ default: ++ dev_err(&client->dev, ++ "No MT9M111/MT9M112/MT9M131 chip detected register read %x\n", ++ data); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ ret = mt9m111_init(mt9m111); ++ if (ret) ++ goto done; ++ ++ ret = v4l2_ctrl_handler_setup(&mt9m111->hdl); ++ ++done: ++ mt9m111_s_power(&mt9m111->subdev, 0); ++ return ret; ++} ++ ++static int mt9m111_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9m111 *mt9m111; ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "mt9m111: driver needs platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9m111 = devm_kzalloc(&client->dev, sizeof(struct mt9m111), GFP_KERNEL); ++ if (!mt9m111) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); ++ v4l2_ctrl_handler_init(&mt9m111->hdl, 5); ++ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ++ mt9m111->gain = v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, ++ V4L2_CID_GAIN, 0, 63 * 2 * 2, 1, 32); ++ v4l2_ctrl_new_std_menu(&mt9m111->hdl, ++ &mt9m111_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, ++ V4L2_EXPOSURE_AUTO); ++ mt9m111->subdev.ctrl_handler = &mt9m111->hdl; ++ if (mt9m111->hdl.error) ++ return mt9m111->hdl.error; ++ ++ /* Second stage probe - when a capture adapter is there */ ++ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; ++ mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; ++ mt9m111->rect.width = MT9M111_MAX_WIDTH; ++ mt9m111->rect.height = MT9M111_MAX_HEIGHT; ++ mt9m111->fmt = &mt9m111_colour_fmts[0]; ++ mt9m111->lastpage = -1; ++ mutex_init(&mt9m111->power_lock); ++ ++ ret = mt9m111_video_probe(client); ++ if (ret) ++ v4l2_ctrl_handler_free(&mt9m111->hdl); ++ ++ return ret; ++} ++ ++static int mt9m111_remove(struct i2c_client *client) ++{ ++ struct mt9m111 *mt9m111 = to_mt9m111(client); ++ ++ v4l2_device_unregister_subdev(&mt9m111->subdev); ++ v4l2_ctrl_handler_free(&mt9m111->hdl); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id mt9m111_id[] = { ++ { "mt9m111", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9m111_id); ++ ++static struct i2c_driver mt9m111_i2c_driver = { ++ .driver = { ++ .name = "mt9m111", ++ }, ++ .probe = mt9m111_probe, ++ .remove = mt9m111_remove, ++ .id_table = mt9m111_id, ++}; ++ ++module_i2c_driver(mt9m111_i2c_driver); ++ ++MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver"); ++MODULE_AUTHOR("Robert Jarzmik"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/mt9t031.c b/drivers/media/i2c/soc_camera/mt9t031.c +new file mode 100644 +index 0000000..d80d044 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/mt9t031.c +@@ -0,0 +1,851 @@ ++/* ++ * Driver for MT9T031 CMOS Image Sensor from Micron ++ * ++ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * ATTENTION: this driver still cannot be used outside of the soc-camera ++ * framework because of its PM implementation, using the video_device node. ++ * If hardware becomes available for testing, alternative PM approaches shall ++ * be considered and tested. ++ */ ++ ++/* ++ * mt9t031 i2c address 0x5d ++ * The platform has to define struct i2c_board_info objects and link to them ++ * from struct soc_camera_host_desc ++ */ ++ ++/* mt9t031 selected register addresses */ ++#define MT9T031_CHIP_VERSION 0x00 ++#define MT9T031_ROW_START 0x01 ++#define MT9T031_COLUMN_START 0x02 ++#define MT9T031_WINDOW_HEIGHT 0x03 ++#define MT9T031_WINDOW_WIDTH 0x04 ++#define MT9T031_HORIZONTAL_BLANKING 0x05 ++#define MT9T031_VERTICAL_BLANKING 0x06 ++#define MT9T031_OUTPUT_CONTROL 0x07 ++#define MT9T031_SHUTTER_WIDTH_UPPER 0x08 ++#define MT9T031_SHUTTER_WIDTH 0x09 ++#define MT9T031_PIXEL_CLOCK_CONTROL 0x0a ++#define MT9T031_FRAME_RESTART 0x0b ++#define MT9T031_SHUTTER_DELAY 0x0c ++#define MT9T031_RESET 0x0d ++#define MT9T031_READ_MODE_1 0x1e ++#define MT9T031_READ_MODE_2 0x20 ++#define MT9T031_READ_MODE_3 0x21 ++#define MT9T031_ROW_ADDRESS_MODE 0x22 ++#define MT9T031_COLUMN_ADDRESS_MODE 0x23 ++#define MT9T031_GLOBAL_GAIN 0x35 ++#define MT9T031_CHIP_ENABLE 0xF8 ++ ++#define MT9T031_MAX_HEIGHT 1536 ++#define MT9T031_MAX_WIDTH 2048 ++#define MT9T031_MIN_HEIGHT 2 ++#define MT9T031_MIN_WIDTH 18 ++#define MT9T031_HORIZONTAL_BLANK 142 ++#define MT9T031_VERTICAL_BLANK 25 ++#define MT9T031_COLUMN_SKIP 32 ++#define MT9T031_ROW_SKIP 20 ++ ++struct mt9t031 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* exposure/auto-exposure cluster */ ++ struct v4l2_ctrl *autoexposure; ++ struct v4l2_ctrl *exposure; ++ }; ++ struct v4l2_rect rect; /* Sensor window */ ++ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ ++ u16 xskip; ++ u16 yskip; ++ unsigned int total_h; ++ unsigned short y_skip_top; /* Lines to skip at the top */ ++}; ++ ++static struct mt9t031 *to_mt9t031(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); ++} ++ ++static int reg_read(struct i2c_client *client, const u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int reg_write(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int reg_set(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret | data); ++} ++ ++static int reg_clear(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret & ~data); ++} ++ ++static int set_shutter(struct i2c_client *client, const u32 data) ++{ ++ int ret; ++ ++ ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); ++ ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); ++ ++ return ret; ++} ++ ++static int get_shutter(struct i2c_client *client, u32 *data) ++{ ++ int ret; ++ ++ ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); ++ *data = ret << 16; ++ ++ if (ret >= 0) ++ ret = reg_read(client, MT9T031_SHUTTER_WIDTH); ++ *data |= ret & 0xffff; ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static int mt9t031_idle(struct i2c_client *client) ++{ ++ int ret; ++ ++ /* Disable chip output, synchronous option update */ ++ ret = reg_write(client, MT9T031_RESET, 1); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_RESET, 0); ++ if (ret >= 0) ++ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); ++ ++ return ret >= 0 ? 0 : -EIO; ++} ++ ++static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ if (enable) ++ /* Switch to master "normal" mode */ ++ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); ++ else ++ /* Stop sensor readout */ ++ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); ++ ++ if (ret < 0) ++ return -EIO; ++ ++ return 0; ++} ++ ++/* target must be _even_ */ ++static u16 mt9t031_skip(s32 *source, s32 target, s32 max) ++{ ++ unsigned int skip; ++ ++ if (*source < target + target / 2) { ++ *source = target; ++ return 1; ++ } ++ ++ skip = min(max, *source + target / 2) / target; ++ if (skip > 8) ++ skip = 8; ++ *source = target * skip; ++ ++ return skip; ++} ++ ++/* rect is the sensor rectangle, the caller guarantees parameter validity */ ++static int mt9t031_set_params(struct i2c_client *client, ++ struct v4l2_rect *rect, u16 xskip, u16 yskip) ++{ ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ int ret; ++ u16 xbin, ybin; ++ const u16 hblank = MT9T031_HORIZONTAL_BLANK, ++ vblank = MT9T031_VERTICAL_BLANK; ++ ++ xbin = min(xskip, (u16)3); ++ ybin = min(yskip, (u16)3); ++ ++ /* ++ * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. ++ * There is always a valid suitably aligned value. The worst case is ++ * xbin = 3, width = 2048. Then we will start at 36, the last read out ++ * pixel will be 2083, which is < 2085 - first black pixel. ++ * ++ * MT9T031 datasheet imposes window left border alignment, depending on ++ * the selected xskip. Failing to conform to this requirement produces ++ * dark horizontal stripes in the image. However, even obeying to this ++ * requirement doesn't eliminate the stripes in all configurations. They ++ * appear "locally reproducibly," but can differ between tests under ++ * different lighting conditions. ++ */ ++ switch (xbin) { ++ case 1: ++ rect->left &= ~1; ++ break; ++ case 2: ++ rect->left &= ~3; ++ break; ++ case 3: ++ rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? ++ (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); ++ } ++ ++ rect->top &= ~1; ++ ++ dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", ++ xskip, yskip, rect->width, rect->height, rect->left, rect->top); ++ ++ /* Disable register update, reconfigure atomically */ ++ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Blanking and start values - default... */ ++ ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); ++ ++ if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { ++ /* Binning, skipping */ ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, ++ ((xbin - 1) << 4) | (xskip - 1)); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ++ ((ybin - 1) << 4) | (yskip - 1)); ++ } ++ dev_dbg(&client->dev, "new physical left %u, top %u\n", ++ rect->left, rect->top); ++ ++ /* ++ * The caller provides a supported format, as guaranteed by ++ * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap() ++ */ ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_COLUMN_START, rect->left); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_ROW_START, rect->top); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); ++ if (ret >= 0) ++ ret = reg_write(client, MT9T031_WINDOW_HEIGHT, ++ rect->height + mt9t031->y_skip_top - 1); ++ if (ret >= 0 && v4l2_ctrl_g_ctrl(mt9t031->autoexposure) == V4L2_EXPOSURE_AUTO) { ++ mt9t031->total_h = rect->height + mt9t031->y_skip_top + vblank; ++ ++ ret = set_shutter(client, mt9t031->total_h); ++ } ++ ++ /* Re-enable register update, commit all changes */ ++ if (ret >= 0) ++ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); ++ ++ if (ret >= 0) { ++ mt9t031->rect = *rect; ++ mt9t031->xskip = xskip; ++ mt9t031->yskip = yskip; ++ } ++ ++ return ret < 0 ? ret : 0; ++} ++ ++static int mt9t031_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct v4l2_rect rect = a->c; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ rect.width = ALIGN(rect.width, 2); ++ rect.height = ALIGN(rect.height, 2); ++ ++ soc_camera_limit_side(&rect.left, &rect.width, ++ MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); ++ ++ soc_camera_limit_side(&rect.top, &rect.height, ++ MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); ++ ++ return mt9t031_set_params(client, &rect, mt9t031->xskip, mt9t031->yskip); ++} ++ ++static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ a->c = mt9t031->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = MT9T031_COLUMN_SKIP; ++ a->bounds.top = MT9T031_ROW_SKIP; ++ a->bounds.width = MT9T031_MAX_WIDTH; ++ a->bounds.height = MT9T031_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int mt9t031_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ mf->width = mt9t031->rect.width / mt9t031->xskip; ++ mf->height = mt9t031->rect.height / mt9t031->yskip; ++ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9t031_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ u16 xskip, yskip; ++ struct v4l2_rect rect = mt9t031->rect; ++ ++ /* ++ * try_fmt has put width and height within limits. ++ * S_FMT: use binning and skipping for scaling ++ */ ++ xskip = mt9t031_skip(&rect.width, mf->width, MT9T031_MAX_WIDTH); ++ yskip = mt9t031_skip(&rect.height, mf->height, MT9T031_MAX_HEIGHT); ++ ++ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ /* mt9t031_set_params() doesn't change width and height */ ++ return mt9t031_set_params(client, &rect, xskip, yskip); ++} ++ ++/* ++ * If a user window larger than sensor window is requested, we'll increase the ++ * sensor window. ++ */ ++static int mt9t031_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ v4l_bound_align_image( ++ &mf->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, ++ &mf->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); ++ ++ mf->code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return 0; ++} ++ ++static int mt9t031_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = mt9t031->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9t031_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ reg->val = reg_read(client, reg->reg); ++ ++ if (reg->val > 0xffff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int mt9t031_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int mt9t031_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9t031 *mt9t031 = container_of(ctrl->handler, ++ struct mt9t031, hdl); ++ const u32 shutter_max = MT9T031_MAX_HEIGHT + MT9T031_VERTICAL_BLANK; ++ s32 min, max; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_EXPOSURE_AUTO: ++ min = mt9t031->exposure->minimum; ++ max = mt9t031->exposure->maximum; ++ mt9t031->exposure->val = ++ (shutter_max / 2 + (mt9t031->total_h - 1) * (max - min)) ++ / shutter_max + min; ++ break; ++ } ++ return 0; ++} ++ ++static int mt9t031_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9t031 *mt9t031 = container_of(ctrl->handler, ++ struct mt9t031, hdl); ++ struct v4l2_subdev *sd = &mt9t031->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct v4l2_ctrl *exp = mt9t031->exposure; ++ int data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); ++ else ++ data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); ++ else ++ data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_GAIN: ++ /* See Datasheet Table 7, Gain settings. */ ++ if (ctrl->val <= ctrl->default_value) { ++ /* Pack it into 0..1 step 0.125, register values 0..8 */ ++ unsigned long range = ctrl->default_value - ctrl->minimum; ++ data = ((ctrl->val - ctrl->minimum) * 8 + range / 2) / range; ++ ++ dev_dbg(&client->dev, "Setting gain %d\n", data); ++ data = reg_write(client, MT9T031_GLOBAL_GAIN, data); ++ if (data < 0) ++ return -EIO; ++ } else { ++ /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ ++ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ ++ unsigned long range = ctrl->maximum - ctrl->default_value - 1; ++ /* calculated gain: map 65..127 to 9..1024 step 0.125 */ ++ unsigned long gain = ((ctrl->val - ctrl->default_value - 1) * ++ 1015 + range / 2) / range + 9; ++ ++ if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ ++ data = gain; ++ else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ ++ data = ((gain - 32) * 16 + 16) / 32 + 80; ++ else ++ /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ ++ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; ++ ++ dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", ++ reg_read(client, MT9T031_GLOBAL_GAIN), data); ++ data = reg_write(client, MT9T031_GLOBAL_GAIN, data); ++ if (data < 0) ++ return -EIO; ++ } ++ return 0; ++ ++ case V4L2_CID_EXPOSURE_AUTO: ++ if (ctrl->val == V4L2_EXPOSURE_MANUAL) { ++ unsigned int range = exp->maximum - exp->minimum; ++ unsigned int shutter = ((exp->val - exp->minimum) * 1048 + ++ range / 2) / range + 1; ++ u32 old; ++ ++ get_shutter(client, &old); ++ dev_dbg(&client->dev, "Set shutter from %u to %u\n", ++ old, shutter); ++ if (set_shutter(client, shutter) < 0) ++ return -EIO; ++ } else { ++ const u16 vblank = MT9T031_VERTICAL_BLANK; ++ mt9t031->total_h = mt9t031->rect.height + ++ mt9t031->y_skip_top + vblank; ++ ++ if (set_shutter(client, mt9t031->total_h) < 0) ++ return -EIO; ++ } ++ return 0; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * Power Management: ++ * This function does nothing for now but must be present for pm to work ++ */ ++static int mt9t031_runtime_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++/* ++ * Power Management: ++ * COLUMN_ADDRESS_MODE and ROW_ADDRESS_MODE are not rewritten if unchanged ++ * they are however changed at reset if the platform hook is present ++ * thus we rewrite them with the values stored by the driver ++ */ ++static int mt9t031_runtime_resume(struct device *dev) ++{ ++ struct video_device *vdev = to_video_device(dev); ++ struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ int ret; ++ u16 xbin, ybin; ++ ++ xbin = min(mt9t031->xskip, (u16)3); ++ ybin = min(mt9t031->yskip, (u16)3); ++ ++ ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, ++ ((xbin - 1) << 4) | (mt9t031->xskip - 1)); ++ if (ret < 0) ++ return ret; ++ ++ ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ++ ((ybin - 1) << 4) | (mt9t031->yskip - 1)); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static struct dev_pm_ops mt9t031_dev_pm_ops = { ++ .runtime_suspend = mt9t031_runtime_suspend, ++ .runtime_resume = mt9t031_runtime_resume, ++}; ++ ++static struct device_type mt9t031_dev_type = { ++ .name = "MT9T031", ++ .pm = &mt9t031_dev_pm_ops, ++}; ++ ++static int mt9t031_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct video_device *vdev = soc_camera_i2c_to_vdev(client); ++ int ret; ++ ++ if (on) { ++ ret = soc_camera_power_on(&client->dev, ssdd); ++ if (ret < 0) ++ return ret; ++ vdev->dev.type = &mt9t031_dev_type; ++ } else { ++ vdev->dev.type = NULL; ++ soc_camera_power_off(&client->dev, ssdd); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Interface active, can use i2c. If it fails, it can indeed mean, that ++ * this wasn't our capture interface, so, we wait for the right one ++ */ ++static int mt9t031_video_probe(struct i2c_client *client) ++{ ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ s32 data; ++ int ret; ++ ++ ret = mt9t031_s_power(&mt9t031->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ ret = mt9t031_idle(client); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed to initialise the camera\n"); ++ goto done; ++ } ++ ++ /* Read out the chip version register */ ++ data = reg_read(client, MT9T031_CHIP_VERSION); ++ ++ switch (data) { ++ case 0x1621: ++ mt9t031->model = V4L2_IDENT_MT9T031; ++ break; ++ default: ++ dev_err(&client->dev, ++ "No MT9T031 chip detected, register read %x\n", data); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); ++ ++ ret = v4l2_ctrl_handler_setup(&mt9t031->hdl); ++ ++done: ++ mt9t031_s_power(&mt9t031->subdev, 0); ++ ++ return ret; ++} ++ ++static int mt9t031_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ *lines = mt9t031->y_skip_top; ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { ++ .g_volatile_ctrl = mt9t031_g_volatile_ctrl, ++ .s_ctrl = mt9t031_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { ++ .g_chip_ident = mt9t031_g_chip_ident, ++ .s_power = mt9t031_s_power, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9t031_g_register, ++ .s_register = mt9t031_s_register, ++#endif ++}; ++ ++static int mt9t031_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_SBGGR10_1X10; ++ return 0; ++} ++ ++static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | ++ V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (soc_camera_apply_board_flags(ssdd, cfg) & ++ V4L2_MBUS_PCLK_SAMPLE_FALLING) ++ return reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); ++ else ++ return reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); ++} ++ ++static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { ++ .s_stream = mt9t031_s_stream, ++ .s_mbus_fmt = mt9t031_s_fmt, ++ .g_mbus_fmt = mt9t031_g_fmt, ++ .try_mbus_fmt = mt9t031_try_fmt, ++ .s_crop = mt9t031_s_crop, ++ .g_crop = mt9t031_g_crop, ++ .cropcap = mt9t031_cropcap, ++ .enum_mbus_fmt = mt9t031_enum_fmt, ++ .g_mbus_config = mt9t031_g_mbus_config, ++ .s_mbus_config = mt9t031_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_sensor_ops mt9t031_subdev_sensor_ops = { ++ .g_skip_top_lines = mt9t031_g_skip_top_lines, ++}; ++ ++static struct v4l2_subdev_ops mt9t031_subdev_ops = { ++ .core = &mt9t031_subdev_core_ops, ++ .video = &mt9t031_subdev_video_ops, ++ .sensor = &mt9t031_subdev_sensor_ops, ++}; ++ ++static int mt9t031_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9t031 *mt9t031; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "MT9T031 driver needs platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9t031 = devm_kzalloc(&client->dev, sizeof(struct mt9t031), GFP_KERNEL); ++ if (!mt9t031) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); ++ v4l2_ctrl_handler_init(&mt9t031->hdl, 5); ++ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, ++ V4L2_CID_GAIN, 0, 127, 1, 64); ++ ++ /* ++ * Simulated autoexposure. If enabled, we calculate shutter width ++ * ourselves in the driver based on vertical blanking and frame width ++ */ ++ mt9t031->autoexposure = v4l2_ctrl_new_std_menu(&mt9t031->hdl, ++ &mt9t031_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, ++ V4L2_EXPOSURE_AUTO); ++ mt9t031->exposure = v4l2_ctrl_new_std(&mt9t031->hdl, &mt9t031_ctrl_ops, ++ V4L2_CID_EXPOSURE, 1, 255, 1, 255); ++ ++ mt9t031->subdev.ctrl_handler = &mt9t031->hdl; ++ if (mt9t031->hdl.error) ++ return mt9t031->hdl.error; ++ ++ v4l2_ctrl_auto_cluster(2, &mt9t031->autoexposure, ++ V4L2_EXPOSURE_MANUAL, true); ++ ++ mt9t031->y_skip_top = 0; ++ mt9t031->rect.left = MT9T031_COLUMN_SKIP; ++ mt9t031->rect.top = MT9T031_ROW_SKIP; ++ mt9t031->rect.width = MT9T031_MAX_WIDTH; ++ mt9t031->rect.height = MT9T031_MAX_HEIGHT; ++ ++ mt9t031->xskip = 1; ++ mt9t031->yskip = 1; ++ ++ ret = mt9t031_video_probe(client); ++ if (ret) ++ v4l2_ctrl_handler_free(&mt9t031->hdl); ++ ++ return ret; ++} ++ ++static int mt9t031_remove(struct i2c_client *client) ++{ ++ struct mt9t031 *mt9t031 = to_mt9t031(client); ++ ++ v4l2_device_unregister_subdev(&mt9t031->subdev); ++ v4l2_ctrl_handler_free(&mt9t031->hdl); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id mt9t031_id[] = { ++ { "mt9t031", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9t031_id); ++ ++static struct i2c_driver mt9t031_i2c_driver = { ++ .driver = { ++ .name = "mt9t031", ++ }, ++ .probe = mt9t031_probe, ++ .remove = mt9t031_remove, ++ .id_table = mt9t031_id, ++}; ++ ++module_i2c_driver(mt9t031_i2c_driver); ++ ++MODULE_DESCRIPTION("Micron MT9T031 Camera driver"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/mt9t112.c +new file mode 100644 +index 0000000..c75d831 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/mt9t112.c +@@ -0,0 +1,1137 @@ ++/* ++ * mt9t112 Camera Driver ++ * ++ * Copyright (C) 2009 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * Based on ov772x driver, mt9m111 driver, ++ * ++ * Copyright (C) 2008 Kuninori Morimoto ++ * Copyright (C) 2008, Robert Jarzmik ++ * Copyright 2006-7 Jonathan Corbet ++ * Copyright (C) 2008 Magnus Damm ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* you can check PLL/clock info */ ++/* #define EXT_CLOCK 24000000 */ ++ ++/************************************************************************ ++ macro ++************************************************************************/ ++/* ++ * frame size ++ */ ++#define MAX_WIDTH 2048 ++#define MAX_HEIGHT 1536 ++ ++#define VGA_WIDTH 640 ++#define VGA_HEIGHT 480 ++ ++/* ++ * macro of read/write ++ */ ++#define ECHECKER(ret, x) \ ++ do { \ ++ (ret) = (x); \ ++ if ((ret) < 0) \ ++ return (ret); \ ++ } while (0) ++ ++#define mt9t112_reg_write(ret, client, a, b) \ ++ ECHECKER(ret, __mt9t112_reg_write(client, a, b)) ++#define mt9t112_mcu_write(ret, client, a, b) \ ++ ECHECKER(ret, __mt9t112_mcu_write(client, a, b)) ++ ++#define mt9t112_reg_mask_set(ret, client, a, b, c) \ ++ ECHECKER(ret, __mt9t112_reg_mask_set(client, a, b, c)) ++#define mt9t112_mcu_mask_set(ret, client, a, b, c) \ ++ ECHECKER(ret, __mt9t112_mcu_mask_set(client, a, b, c)) ++ ++#define mt9t112_reg_read(ret, client, a) \ ++ ECHECKER(ret, __mt9t112_reg_read(client, a)) ++ ++/* ++ * Logical address ++ */ ++#define _VAR(id, offset, base) (base | (id & 0x1f) << 10 | (offset & 0x3ff)) ++#define VAR(id, offset) _VAR(id, offset, 0x0000) ++#define VAR8(id, offset) _VAR(id, offset, 0x8000) ++ ++/************************************************************************ ++ struct ++************************************************************************/ ++struct mt9t112_format { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++ u16 fmt; ++ u16 order; ++}; ++ ++struct mt9t112_priv { ++ struct v4l2_subdev subdev; ++ struct mt9t112_camera_info *info; ++ struct i2c_client *client; ++ struct v4l2_rect frame; ++ const struct mt9t112_format *format; ++ int model; ++ u32 flags; ++/* for flags */ ++#define INIT_DONE (1 << 0) ++#define PCLK_RISING (1 << 1) ++}; ++ ++/************************************************************************ ++ supported format ++************************************************************************/ ++ ++static const struct mt9t112_format mt9t112_cfmts[] = { ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .fmt = 1, ++ .order = 0, ++ }, { ++ .code = V4L2_MBUS_FMT_VYUY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .fmt = 1, ++ .order = 1, ++ }, { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .fmt = 1, ++ .order = 2, ++ }, { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .fmt = 1, ++ .order = 3, ++ }, { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt = 8, ++ .order = 2, ++ }, { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .fmt = 4, ++ .order = 2, ++ }, ++}; ++ ++/************************************************************************ ++ general function ++************************************************************************/ ++static struct mt9t112_priv *to_mt9t112(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), ++ struct mt9t112_priv, ++ subdev); ++} ++ ++static int __mt9t112_reg_read(const struct i2c_client *client, u16 command) ++{ ++ struct i2c_msg msg[2]; ++ u8 buf[2]; ++ int ret; ++ ++ command = swab16(command); ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 2; ++ msg[0].buf = (u8 *)&command; ++ ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 2; ++ msg[1].buf = buf; ++ ++ /* ++ * if return value of this function is < 0, ++ * it mean error. ++ * else, under 16bit is valid data. ++ */ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ if (ret < 0) ++ return ret; ++ ++ memcpy(&ret, buf, 2); ++ return swab16(ret); ++} ++ ++static int __mt9t112_reg_write(const struct i2c_client *client, ++ u16 command, u16 data) ++{ ++ struct i2c_msg msg; ++ u8 buf[4]; ++ int ret; ++ ++ command = swab16(command); ++ data = swab16(data); ++ ++ memcpy(buf + 0, &command, 2); ++ memcpy(buf + 2, &data, 2); ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 4; ++ msg.buf = buf; ++ ++ /* ++ * i2c_transfer return message length, ++ * but this function should return 0 if correct case ++ */ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret >= 0) ++ ret = 0; ++ ++ return ret; ++} ++ ++static int __mt9t112_reg_mask_set(const struct i2c_client *client, ++ u16 command, ++ u16 mask, ++ u16 set) ++{ ++ int val = __mt9t112_reg_read(client, command); ++ if (val < 0) ++ return val; ++ ++ val &= ~mask; ++ val |= set & mask; ++ ++ return __mt9t112_reg_write(client, command, val); ++} ++ ++/* mcu access */ ++static int __mt9t112_mcu_read(const struct i2c_client *client, u16 command) ++{ ++ int ret; ++ ++ ret = __mt9t112_reg_write(client, 0x098E, command); ++ if (ret < 0) ++ return ret; ++ ++ return __mt9t112_reg_read(client, 0x0990); ++} ++ ++static int __mt9t112_mcu_write(const struct i2c_client *client, ++ u16 command, u16 data) ++{ ++ int ret; ++ ++ ret = __mt9t112_reg_write(client, 0x098E, command); ++ if (ret < 0) ++ return ret; ++ ++ return __mt9t112_reg_write(client, 0x0990, data); ++} ++ ++static int __mt9t112_mcu_mask_set(const struct i2c_client *client, ++ u16 command, ++ u16 mask, ++ u16 set) ++{ ++ int val = __mt9t112_mcu_read(client, command); ++ if (val < 0) ++ return val; ++ ++ val &= ~mask; ++ val |= set & mask; ++ ++ return __mt9t112_mcu_write(client, command, val); ++} ++ ++static int mt9t112_reset(const struct i2c_client *client) ++{ ++ int ret; ++ ++ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0001); ++ msleep(1); ++ mt9t112_reg_mask_set(ret, client, 0x001a, 0x0001, 0x0000); ++ ++ return ret; ++} ++ ++#ifndef EXT_CLOCK ++#define CLOCK_INFO(a, b) ++#else ++#define CLOCK_INFO(a, b) mt9t112_clock_info(a, b) ++static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) ++{ ++ int m, n, p1, p2, p3, p4, p5, p6, p7; ++ u32 vco, clk; ++ char *enable; ++ ++ ext /= 1000; /* kbyte order */ ++ ++ mt9t112_reg_read(n, client, 0x0012); ++ p1 = n & 0x000f; ++ n = n >> 4; ++ p2 = n & 0x000f; ++ n = n >> 4; ++ p3 = n & 0x000f; ++ ++ mt9t112_reg_read(n, client, 0x002a); ++ p4 = n & 0x000f; ++ n = n >> 4; ++ p5 = n & 0x000f; ++ n = n >> 4; ++ p6 = n & 0x000f; ++ ++ mt9t112_reg_read(n, client, 0x002c); ++ p7 = n & 0x000f; ++ ++ mt9t112_reg_read(n, client, 0x0010); ++ m = n & 0x00ff; ++ n = (n >> 8) & 0x003f; ++ ++ enable = ((6000 > ext) || (54000 < ext)) ? "X" : ""; ++ dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); ++ ++ vco = 2 * m * ext / (n+1); ++ enable = ((384000 > vco) || (768000 < vco)) ? "X" : ""; ++ dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); ++ ++ clk = vco / (p1+1) / (p2+1); ++ enable = (96000 < clk) ? "X" : ""; ++ dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); ++ ++ clk = vco / (p3+1); ++ enable = (768000 < clk) ? "X" : ""; ++ dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); ++ ++ clk = vco / (p6+1); ++ enable = (96000 < clk) ? "X" : ""; ++ dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); ++ ++ clk = vco / (p5+1); ++ enable = (54000 < clk) ? "X" : ""; ++ dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); ++ ++ clk = vco / (p4+1); ++ enable = (70000 < clk) ? "X" : ""; ++ dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); ++ ++ clk = vco / (p7+1); ++ dev_dbg(&client->dev, "External sensor : %10u K\n", clk); ++ ++ clk = ext / (n+1); ++ enable = ((2000 > clk) || (24000 < clk)) ? "X" : ""; ++ dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); ++ ++ return 0; ++} ++#endif ++ ++static void mt9t112_frame_check(u32 *width, u32 *height, u32 *left, u32 *top) ++{ ++ soc_camera_limit_side(left, width, 0, 0, MAX_WIDTH); ++ soc_camera_limit_side(top, height, 0, 0, MAX_HEIGHT); ++} ++ ++static int mt9t112_set_a_frame_size(const struct i2c_client *client, ++ u16 width, ++ u16 height) ++{ ++ int ret; ++ u16 wstart = (MAX_WIDTH - width) / 2; ++ u16 hstart = (MAX_HEIGHT - height) / 2; ++ ++ /* (Context A) Image Width/Height */ ++ mt9t112_mcu_write(ret, client, VAR(26, 0), width); ++ mt9t112_mcu_write(ret, client, VAR(26, 2), height); ++ ++ /* (Context A) Output Width/Height */ ++ mt9t112_mcu_write(ret, client, VAR(18, 43), 8 + width); ++ mt9t112_mcu_write(ret, client, VAR(18, 45), 8 + height); ++ ++ /* (Context A) Start Row/Column */ ++ mt9t112_mcu_write(ret, client, VAR(18, 2), 4 + hstart); ++ mt9t112_mcu_write(ret, client, VAR(18, 4), 4 + wstart); ++ ++ /* (Context A) End Row/Column */ ++ mt9t112_mcu_write(ret, client, VAR(18, 6), 11 + height + hstart); ++ mt9t112_mcu_write(ret, client, VAR(18, 8), 11 + width + wstart); ++ ++ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); ++ ++ return ret; ++} ++ ++static int mt9t112_set_pll_dividers(const struct i2c_client *client, ++ u8 m, u8 n, ++ u8 p1, u8 p2, u8 p3, ++ u8 p4, u8 p5, u8 p6, ++ u8 p7) ++{ ++ int ret; ++ u16 val; ++ ++ /* N/M */ ++ val = (n << 8) | ++ (m << 0); ++ mt9t112_reg_mask_set(ret, client, 0x0010, 0x3fff, val); ++ ++ /* P1/P2/P3 */ ++ val = ((p3 & 0x0F) << 8) | ++ ((p2 & 0x0F) << 4) | ++ ((p1 & 0x0F) << 0); ++ mt9t112_reg_mask_set(ret, client, 0x0012, 0x0fff, val); ++ ++ /* P4/P5/P6 */ ++ val = (0x7 << 12) | ++ ((p6 & 0x0F) << 8) | ++ ((p5 & 0x0F) << 4) | ++ ((p4 & 0x0F) << 0); ++ mt9t112_reg_mask_set(ret, client, 0x002A, 0x7fff, val); ++ ++ /* P7 */ ++ val = (0x1 << 12) | ++ ((p7 & 0x0F) << 0); ++ mt9t112_reg_mask_set(ret, client, 0x002C, 0x100f, val); ++ ++ return ret; ++} ++ ++static int mt9t112_init_pll(const struct i2c_client *client) ++{ ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ int data, i, ret; ++ ++ mt9t112_reg_mask_set(ret, client, 0x0014, 0x003, 0x0001); ++ ++ /* PLL control: BYPASS PLL = 8517 */ ++ mt9t112_reg_write(ret, client, 0x0014, 0x2145); ++ ++ /* Replace these registers when new timing parameters are generated */ ++ mt9t112_set_pll_dividers(client, ++ priv->info->divider.m, ++ priv->info->divider.n, ++ priv->info->divider.p1, ++ priv->info->divider.p2, ++ priv->info->divider.p3, ++ priv->info->divider.p4, ++ priv->info->divider.p5, ++ priv->info->divider.p6, ++ priv->info->divider.p7); ++ ++ /* ++ * TEST_BYPASS on ++ * PLL_ENABLE on ++ * SEL_LOCK_DET on ++ * TEST_BYPASS off ++ */ ++ mt9t112_reg_write(ret, client, 0x0014, 0x2525); ++ mt9t112_reg_write(ret, client, 0x0014, 0x2527); ++ mt9t112_reg_write(ret, client, 0x0014, 0x3427); ++ mt9t112_reg_write(ret, client, 0x0014, 0x3027); ++ ++ mdelay(10); ++ ++ /* ++ * PLL_BYPASS off ++ * Reference clock count ++ * I2C Master Clock Divider ++ */ ++ mt9t112_reg_write(ret, client, 0x0014, 0x3046); ++ mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */ ++ mt9t112_reg_write(ret, client, 0x0022, 0x0190); ++ mt9t112_reg_write(ret, client, 0x3B84, 0x0212); ++ ++ /* External sensor clock is PLL bypass */ ++ mt9t112_reg_write(ret, client, 0x002E, 0x0500); ++ ++ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0002, 0x0002); ++ mt9t112_reg_mask_set(ret, client, 0x3B82, 0x0004, 0x0004); ++ ++ /* MCU disabled */ ++ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0x0004); ++ ++ /* out of standby */ ++ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0001, 0); ++ ++ mdelay(50); ++ ++ /* ++ * Standby Workaround ++ * Disable Secondary I2C Pads ++ */ ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ mt9t112_reg_write(ret, client, 0x0614, 0x0001); ++ mdelay(1); ++ ++ /* poll to verify out of standby. Must Poll this bit */ ++ for (i = 0; i < 100; i++) { ++ mt9t112_reg_read(data, client, 0x0018); ++ if (!(0x4000 & data)) ++ break; ++ ++ mdelay(10); ++ } ++ ++ return ret; ++} ++ ++static int mt9t112_init_setting(const struct i2c_client *client) ++{ ++ ++ int ret; ++ ++ /* Adaptive Output Clock (A) */ ++ mt9t112_mcu_mask_set(ret, client, VAR(26, 160), 0x0040, 0x0000); ++ ++ /* Read Mode (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 12), 0x0024); ++ ++ /* Fine Correction (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 15), 0x00CC); ++ ++ /* Fine IT Min (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 17), 0x01f1); ++ ++ /* Fine IT Max Margin (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 19), 0x00fF); ++ ++ /* Base Frame Lines (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 29), 0x032D); ++ ++ /* Min Line Length (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 31), 0x073a); ++ ++ /* Line Length (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 37), 0x07d0); ++ ++ /* Adaptive Output Clock (B) */ ++ mt9t112_mcu_mask_set(ret, client, VAR(27, 160), 0x0040, 0x0000); ++ ++ /* Row Start (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 74), 0x004); ++ ++ /* Column Start (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 76), 0x004); ++ ++ /* Row End (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 78), 0x60B); ++ ++ /* Column End (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 80), 0x80B); ++ ++ /* Fine Correction (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 87), 0x008C); ++ ++ /* Fine IT Min (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 89), 0x01F1); ++ ++ /* Fine IT Max Margin (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 91), 0x00FF); ++ ++ /* Base Frame Lines (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 101), 0x0668); ++ ++ /* Min Line Length (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 103), 0x0AF0); ++ ++ /* Line Length (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 109), 0x0AF0); ++ ++ /* ++ * Flicker Dectection registers ++ * This section should be replaced whenever new Timing file is generated ++ * All the following registers need to be replaced ++ * Following registers are generated from Register Wizard but user can ++ * modify them. For detail see auto flicker detection tuning ++ */ ++ ++ /* FD_FDPERIOD_SELECT */ ++ mt9t112_mcu_write(ret, client, VAR8(8, 5), 0x01); ++ ++ /* PRI_B_CONFIG_FD_ALGO_RUN */ ++ mt9t112_mcu_write(ret, client, VAR(27, 17), 0x0003); ++ ++ /* PRI_A_CONFIG_FD_ALGO_RUN */ ++ mt9t112_mcu_write(ret, client, VAR(26, 17), 0x0003); ++ ++ /* ++ * AFD range detection tuning registers ++ */ ++ ++ /* search_f1_50 */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 165), 0x25); ++ ++ /* search_f2_50 */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 166), 0x28); ++ ++ /* search_f1_60 */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 167), 0x2C); ++ ++ /* search_f2_60 */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 168), 0x2F); ++ ++ /* period_50Hz (A) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 68), 0xBA); ++ ++ /* secret register by aptina */ ++ /* period_50Hz (A MSB) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 303), 0x00); ++ ++ /* period_60Hz (A) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 69), 0x9B); ++ ++ /* secret register by aptina */ ++ /* period_60Hz (A MSB) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 301), 0x00); ++ ++ /* period_50Hz (B) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 140), 0x82); ++ ++ /* secret register by aptina */ ++ /* period_50Hz (B) MSB */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 304), 0x00); ++ ++ /* period_60Hz (B) */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 141), 0x6D); ++ ++ /* secret register by aptina */ ++ /* period_60Hz (B) MSB */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 302), 0x00); ++ ++ /* FD Mode */ ++ mt9t112_mcu_write(ret, client, VAR8(8, 2), 0x10); ++ ++ /* Stat_min */ ++ mt9t112_mcu_write(ret, client, VAR8(8, 9), 0x02); ++ ++ /* Stat_max */ ++ mt9t112_mcu_write(ret, client, VAR8(8, 10), 0x03); ++ ++ /* Min_amplitude */ ++ mt9t112_mcu_write(ret, client, VAR8(8, 12), 0x0A); ++ ++ /* RX FIFO Watermark (A) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 70), 0x0014); ++ ++ /* RX FIFO Watermark (B) */ ++ mt9t112_mcu_write(ret, client, VAR(18, 142), 0x0014); ++ ++ /* MCLK: 16MHz ++ * PCLK: 73MHz ++ * CorePixCLK: 36.5 MHz ++ */ ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x0044), 133); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x0045), 110); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x008c), 130); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x008d), 108); ++ ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x00A5), 27); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a6), 30); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a7), 32); ++ mt9t112_mcu_write(ret, client, VAR8(18, 0x00a8), 35); ++ ++ return ret; ++} ++ ++static int mt9t112_auto_focus_setting(const struct i2c_client *client) ++{ ++ int ret; ++ ++ mt9t112_mcu_write(ret, client, VAR(12, 13), 0x000F); ++ mt9t112_mcu_write(ret, client, VAR(12, 23), 0x0F0F); ++ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); ++ ++ mt9t112_reg_write(ret, client, 0x0614, 0x0000); ++ ++ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); ++ mt9t112_mcu_write(ret, client, VAR8(12, 2), 0x02); ++ mt9t112_mcu_write(ret, client, VAR(12, 3), 0x0002); ++ mt9t112_mcu_write(ret, client, VAR(17, 3), 0x8001); ++ mt9t112_mcu_write(ret, client, VAR(17, 11), 0x0025); ++ mt9t112_mcu_write(ret, client, VAR(17, 13), 0x0193); ++ mt9t112_mcu_write(ret, client, VAR8(17, 33), 0x18); ++ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x05); ++ ++ return ret; ++} ++ ++static int mt9t112_auto_focus_trigger(const struct i2c_client *client) ++{ ++ int ret; ++ ++ mt9t112_mcu_write(ret, client, VAR8(12, 25), 0x01); ++ ++ return ret; ++} ++ ++static int mt9t112_init_camera(const struct i2c_client *client) ++{ ++ int ret; ++ ++ ECHECKER(ret, mt9t112_reset(client)); ++ ++ ECHECKER(ret, mt9t112_init_pll(client)); ++ ++ ECHECKER(ret, mt9t112_init_setting(client)); ++ ++ ECHECKER(ret, mt9t112_auto_focus_setting(client)); ++ ++ mt9t112_reg_mask_set(ret, client, 0x0018, 0x0004, 0); ++ ++ /* Analog setting B */ ++ mt9t112_reg_write(ret, client, 0x3084, 0x2409); ++ mt9t112_reg_write(ret, client, 0x3092, 0x0A49); ++ mt9t112_reg_write(ret, client, 0x3094, 0x4949); ++ mt9t112_reg_write(ret, client, 0x3096, 0x4950); ++ ++ /* ++ * Disable adaptive clock ++ * PRI_A_CONFIG_JPEG_OB_TX_CONTROL_VAR ++ * PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR ++ */ ++ mt9t112_mcu_write(ret, client, VAR(26, 160), 0x0A2E); ++ mt9t112_mcu_write(ret, client, VAR(27, 160), 0x0A2E); ++ ++ /* Configure STatus in Status_before_length Format and enable header */ ++ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ ++ mt9t112_mcu_write(ret, client, VAR(27, 144), 0x0CB4); ++ ++ /* Enable JPEG in context B */ ++ /* PRI_B_CONFIG_JPEG_OB_TX_CONTROL_VAR */ ++ mt9t112_mcu_write(ret, client, VAR8(27, 142), 0x01); ++ ++ /* Disable Dac_TXLO */ ++ mt9t112_reg_write(ret, client, 0x316C, 0x350F); ++ ++ /* Set max slew rates */ ++ mt9t112_reg_write(ret, client, 0x1E, 0x777); ++ ++ return ret; ++} ++ ++/************************************************************************ ++ v4l2_subdev_core_ops ++************************************************************************/ ++static int mt9t112_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ ++ id->ident = priv->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9t112_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ reg->size = 2; ++ mt9t112_reg_read(ret, client, reg->reg); ++ ++ reg->val = (__u64)ret; ++ ++ return 0; ++} ++ ++static int mt9t112_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ mt9t112_reg_write(ret, client, reg->reg, reg->val); ++ ++ return ret; ++} ++#endif ++ ++static int mt9t112_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { ++ .g_chip_ident = mt9t112_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9t112_g_register, ++ .s_register = mt9t112_s_register, ++#endif ++ .s_power = mt9t112_s_power, ++}; ++ ++ ++/************************************************************************ ++ v4l2_subdev_video_ops ++************************************************************************/ ++static int mt9t112_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ int ret = 0; ++ ++ if (!enable) { ++ /* FIXME ++ * ++ * If user selected large output size, ++ * and used it long time, ++ * mt9t112 camera will be very warm. ++ * ++ * But current driver can not stop mt9t112 camera. ++ * So, set small size here to solve this problem. ++ */ ++ mt9t112_set_a_frame_size(client, VGA_WIDTH, VGA_HEIGHT); ++ return ret; ++ } ++ ++ if (!(priv->flags & INIT_DONE)) { ++ u16 param = PCLK_RISING & priv->flags ? 0x0001 : 0x0000; ++ ++ ECHECKER(ret, mt9t112_init_camera(client)); ++ ++ /* Invert PCLK (Data sampled on falling edge of pixclk) */ ++ mt9t112_reg_write(ret, client, 0x3C20, param); ++ ++ mdelay(5); ++ ++ priv->flags |= INIT_DONE; ++ } ++ ++ mt9t112_mcu_write(ret, client, VAR(26, 7), priv->format->fmt); ++ mt9t112_mcu_write(ret, client, VAR(26, 9), priv->format->order); ++ mt9t112_mcu_write(ret, client, VAR8(1, 0), 0x06); ++ ++ mt9t112_set_a_frame_size(client, ++ priv->frame.width, ++ priv->frame.height); ++ ++ ECHECKER(ret, mt9t112_auto_focus_trigger(client)); ++ ++ dev_dbg(&client->dev, "format : %d\n", priv->format->code); ++ dev_dbg(&client->dev, "size : %d x %d\n", ++ priv->frame.width, ++ priv->frame.height); ++ ++ CLOCK_INFO(client, EXT_CLOCK); ++ ++ return ret; ++} ++ ++static int mt9t112_set_params(struct mt9t112_priv *priv, ++ const struct v4l2_rect *rect, ++ enum v4l2_mbus_pixelcode code) ++{ ++ int i; ++ ++ /* ++ * get color format ++ */ ++ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) ++ if (mt9t112_cfmts[i].code == code) ++ break; ++ ++ if (i == ARRAY_SIZE(mt9t112_cfmts)) ++ return -EINVAL; ++ ++ priv->frame = *rect; ++ ++ /* ++ * frame size check ++ */ ++ mt9t112_frame_check(&priv->frame.width, &priv->frame.height, ++ &priv->frame.left, &priv->frame.top); ++ ++ priv->format = mt9t112_cfmts + i; ++ ++ return 0; ++} ++ ++static int mt9t112_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = MAX_WIDTH; ++ a->bounds.height = MAX_HEIGHT; ++ a->defrect.left = 0; ++ a->defrect.top = 0; ++ a->defrect.width = VGA_WIDTH; ++ a->defrect.height = VGA_HEIGHT; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int mt9t112_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ ++ a->c = priv->frame; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int mt9t112_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ const struct v4l2_rect *rect = &a->c; ++ ++ return mt9t112_set_params(priv, rect, priv->format->code); ++} ++ ++static int mt9t112_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ ++ mf->width = priv->frame.width; ++ mf->height = priv->frame.height; ++ mf->colorspace = priv->format->colorspace; ++ mf->code = priv->format->code; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9t112_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ struct v4l2_rect rect = { ++ .width = mf->width, ++ .height = mf->height, ++ .left = priv->frame.left, ++ .top = priv->frame.top, ++ }; ++ int ret; ++ ++ ret = mt9t112_set_params(priv, &rect, mf->code); ++ ++ if (!ret) ++ mf->colorspace = priv->format->colorspace; ++ ++ return ret; ++} ++ ++static int mt9t112_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ unsigned int top, left; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(mt9t112_cfmts); i++) ++ if (mt9t112_cfmts[i].code == mf->code) ++ break; ++ ++ if (i == ARRAY_SIZE(mt9t112_cfmts)) { ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ } else { ++ mf->colorspace = mt9t112_cfmts[i].colorspace; ++ } ++ ++ mt9t112_frame_check(&mf->width, &mf->height, &left, &top); ++ ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9t112_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(mt9t112_cfmts)) ++ return -EINVAL; ++ ++ *code = mt9t112_cfmts[index].code; ++ ++ return 0; ++} ++ ++static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | ++ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ ++ if (soc_camera_apply_board_flags(ssdd, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) ++ priv->flags |= PCLK_RISING; ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops mt9t112_subdev_video_ops = { ++ .s_stream = mt9t112_s_stream, ++ .g_mbus_fmt = mt9t112_g_fmt, ++ .s_mbus_fmt = mt9t112_s_fmt, ++ .try_mbus_fmt = mt9t112_try_fmt, ++ .cropcap = mt9t112_cropcap, ++ .g_crop = mt9t112_g_crop, ++ .s_crop = mt9t112_s_crop, ++ .enum_mbus_fmt = mt9t112_enum_fmt, ++ .g_mbus_config = mt9t112_g_mbus_config, ++ .s_mbus_config = mt9t112_s_mbus_config, ++}; ++ ++/************************************************************************ ++ i2c driver ++************************************************************************/ ++static struct v4l2_subdev_ops mt9t112_subdev_ops = { ++ .core = &mt9t112_subdev_core_ops, ++ .video = &mt9t112_subdev_video_ops, ++}; ++ ++static int mt9t112_camera_probe(struct i2c_client *client) ++{ ++ struct mt9t112_priv *priv = to_mt9t112(client); ++ const char *devname; ++ int chipid; ++ int ret; ++ ++ ret = mt9t112_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show chip ID ++ */ ++ mt9t112_reg_read(chipid, client, 0x0000); ++ ++ switch (chipid) { ++ case 0x2680: ++ devname = "mt9t111"; ++ priv->model = V4L2_IDENT_MT9T111; ++ break; ++ case 0x2682: ++ devname = "mt9t112"; ++ priv->model = V4L2_IDENT_MT9T112; ++ break; ++ default: ++ dev_err(&client->dev, "Product ID error %04x\n", chipid); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, "%s chip ID %04x\n", devname, chipid); ++ ++done: ++ mt9t112_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static int mt9t112_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9t112_priv *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct v4l2_rect rect = { ++ .width = VGA_WIDTH, ++ .height = VGA_HEIGHT, ++ .left = (MAX_WIDTH - VGA_WIDTH) / 2, ++ .top = (MAX_HEIGHT - VGA_HEIGHT) / 2, ++ }; ++ int ret; ++ ++ if (!ssdd || !ssdd->drv_priv) { ++ dev_err(&client->dev, "mt9t112: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->info = ssdd->drv_priv; ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); ++ ++ ret = mt9t112_camera_probe(client); ++ if (ret) ++ return ret; ++ ++ /* Cannot fail: using the default supported pixel code */ ++ mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); ++ ++ return ret; ++} ++ ++static int mt9t112_remove(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static const struct i2c_device_id mt9t112_id[] = { ++ { "mt9t112", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9t112_id); ++ ++static struct i2c_driver mt9t112_i2c_driver = { ++ .driver = { ++ .name = "mt9t112", ++ }, ++ .probe = mt9t112_probe, ++ .remove = mt9t112_remove, ++ .id_table = mt9t112_id, ++}; ++ ++module_i2c_driver(mt9t112_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for mt9t112"); ++MODULE_AUTHOR("Kuninori Morimoto"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c +new file mode 100644 +index 0000000..a5e65d6 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/mt9v022.c +@@ -0,0 +1,989 @@ ++/* ++ * Driver for MT9V022 CMOS Image Sensor from Micron ++ * ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c ++ * The platform has to define struct i2c_board_info objects and link to them ++ * from struct soc_camera_host_desc ++ */ ++ ++static char *sensor_type; ++module_param(sensor_type, charp, S_IRUGO); ++MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); ++ ++/* mt9v022 selected register addresses */ ++#define MT9V022_CHIP_VERSION 0x00 ++#define MT9V022_COLUMN_START 0x01 ++#define MT9V022_ROW_START 0x02 ++#define MT9V022_WINDOW_HEIGHT 0x03 ++#define MT9V022_WINDOW_WIDTH 0x04 ++#define MT9V022_HORIZONTAL_BLANKING 0x05 ++#define MT9V022_VERTICAL_BLANKING 0x06 ++#define MT9V022_CHIP_CONTROL 0x07 ++#define MT9V022_SHUTTER_WIDTH1 0x08 ++#define MT9V022_SHUTTER_WIDTH2 0x09 ++#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a ++#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b ++#define MT9V022_RESET 0x0c ++#define MT9V022_READ_MODE 0x0d ++#define MT9V022_MONITOR_MODE 0x0e ++#define MT9V022_PIXEL_OPERATION_MODE 0x0f ++#define MT9V022_LED_OUT_CONTROL 0x1b ++#define MT9V022_ADC_MODE_CONTROL 0x1c ++#define MT9V022_REG32 0x20 ++#define MT9V022_ANALOG_GAIN 0x35 ++#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 ++#define MT9V022_PIXCLK_FV_LV 0x74 ++#define MT9V022_DIGITAL_TEST_PATTERN 0x7f ++#define MT9V022_AEC_AGC_ENABLE 0xAF ++#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD ++ ++/* mt9v024 partial list register addresses changes with respect to mt9v022 */ ++#define MT9V024_PIXCLK_FV_LV 0x72 ++#define MT9V024_MAX_TOTAL_SHUTTER_WIDTH 0xAD ++ ++/* Progressive scan, master, defaults */ ++#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 ++ ++#define MT9V022_MAX_WIDTH 752 ++#define MT9V022_MAX_HEIGHT 480 ++#define MT9V022_MIN_WIDTH 48 ++#define MT9V022_MIN_HEIGHT 32 ++#define MT9V022_COLUMN_SKIP 1 ++#define MT9V022_ROW_SKIP 4 ++ ++#define MT9V022_HORIZONTAL_BLANKING_MIN 43 ++#define MT9V022_HORIZONTAL_BLANKING_MAX 1023 ++#define MT9V022_HORIZONTAL_BLANKING_DEF 94 ++#define MT9V022_VERTICAL_BLANKING_MIN 2 ++#define MT9V022_VERTICAL_BLANKING_MAX 3000 ++#define MT9V022_VERTICAL_BLANKING_DEF 45 ++ ++#define is_mt9v022_rev3(id) (id == 0x1313) ++#define is_mt9v024(id) (id == 0x1324) ++ ++/* MT9V022 has only one fixed colorspace per pixelcode */ ++struct mt9v022_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++/* Find a data format by a pixel code in an array */ ++static const struct mt9v022_datafmt *mt9v022_find_datafmt( ++ enum v4l2_mbus_pixelcode code, const struct mt9v022_datafmt *fmt, ++ int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) ++ if (fmt[i].code == code) ++ return fmt + i; ++ ++ return NULL; ++} ++ ++static const struct mt9v022_datafmt mt9v022_colour_fmts[] = { ++ /* ++ * Order important: first natively supported, ++ * second supported with a GPIO extender ++ */ ++ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB}, ++}; ++ ++static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { ++ /* Order important - see above */ ++ {V4L2_MBUS_FMT_Y10_1X10, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_Y8_1X8, V4L2_COLORSPACE_JPEG}, ++}; ++ ++/* only registers with different addresses on different mt9v02x sensors */ ++struct mt9v02x_register { ++ u8 max_total_shutter_width; ++ u8 pixclk_fv_lv; ++}; ++ ++static const struct mt9v02x_register mt9v022_register = { ++ .max_total_shutter_width = MT9V022_MAX_TOTAL_SHUTTER_WIDTH, ++ .pixclk_fv_lv = MT9V022_PIXCLK_FV_LV, ++}; ++ ++static const struct mt9v02x_register mt9v024_register = { ++ .max_total_shutter_width = MT9V024_MAX_TOTAL_SHUTTER_WIDTH, ++ .pixclk_fv_lv = MT9V024_PIXCLK_FV_LV, ++}; ++ ++struct mt9v022 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* exposure/auto-exposure cluster */ ++ struct v4l2_ctrl *autoexposure; ++ struct v4l2_ctrl *exposure; ++ }; ++ struct { ++ /* gain/auto-gain cluster */ ++ struct v4l2_ctrl *autogain; ++ struct v4l2_ctrl *gain; ++ }; ++ struct v4l2_ctrl *hblank; ++ struct v4l2_ctrl *vblank; ++ struct v4l2_rect rect; /* Sensor window */ ++ const struct mt9v022_datafmt *fmt; ++ const struct mt9v022_datafmt *fmts; ++ const struct mt9v02x_register *reg; ++ int num_fmts; ++ int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ ++ u16 chip_control; ++ u16 chip_version; ++ unsigned short y_skip_top; /* Lines to skip at the top */ ++}; ++ ++static struct mt9v022 *to_mt9v022(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); ++} ++ ++static int reg_read(struct i2c_client *client, const u8 reg) ++{ ++ return i2c_smbus_read_word_swapped(client, reg); ++} ++ ++static int reg_write(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ return i2c_smbus_write_word_swapped(client, reg, data); ++} ++ ++static int reg_set(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret | data); ++} ++ ++static int reg_clear(struct i2c_client *client, const u8 reg, ++ const u16 data) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, ret & ~data); ++} ++ ++static int mt9v022_init(struct i2c_client *client) ++{ ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ int ret; ++ ++ /* ++ * Almost the default mode: master, parallel, simultaneous, and an ++ * undocumented bit 0x200, which is present in table 7, but not in 8, ++ * plus snapshot mode to disable scan for now ++ */ ++ mt9v022->chip_control |= 0x10; ++ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); ++ if (!ret) ++ ret = reg_write(client, MT9V022_READ_MODE, 0x300); ++ ++ /* All defaults */ ++ if (!ret) ++ /* AEC, AGC on */ ++ ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); ++ if (!ret) ++ ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); ++ if (!ret) ++ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); ++ if (!ret) ++ ret = reg_write(client, mt9v022->reg->max_total_shutter_width, 480); ++ if (!ret) ++ /* default - auto */ ++ ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); ++ if (!ret) ++ ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); ++ if (!ret) ++ return v4l2_ctrl_handler_setup(&mt9v022->hdl); ++ ++ return ret; ++} ++ ++static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ if (enable) { ++ /* Switch to master "normal" mode */ ++ mt9v022->chip_control &= ~0x10; ++ if (is_mt9v022_rev3(mt9v022->chip_version) || ++ is_mt9v024(mt9v022->chip_version)) { ++ /* ++ * Unset snapshot mode specific settings: clear bit 9 ++ * and bit 2 in reg. 0x20 when in normal mode. ++ */ ++ if (reg_clear(client, MT9V022_REG32, 0x204)) ++ return -EIO; ++ } ++ } else { ++ /* Switch to snapshot mode */ ++ mt9v022->chip_control |= 0x10; ++ if (is_mt9v022_rev3(mt9v022->chip_version) || ++ is_mt9v024(mt9v022->chip_version)) { ++ /* ++ * Required settings for snapshot mode: set bit 9 ++ * (RST enable) and bit 2 (CR enable) in reg. 0x20 ++ * See TechNote TN0960 or TN-09-225. ++ */ ++ if (reg_set(client, MT9V022_REG32, 0x204)) ++ return -EIO; ++ } ++ } ++ ++ if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) ++ return -EIO; ++ return 0; ++} ++ ++static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ struct v4l2_rect rect = a->c; ++ int ret; ++ ++ /* Bayer format - even size lengths */ ++ if (mt9v022->fmts == mt9v022_colour_fmts) { ++ rect.width = ALIGN(rect.width, 2); ++ rect.height = ALIGN(rect.height, 2); ++ /* Let the user play with the starting pixel */ ++ } ++ ++ soc_camera_limit_side(&rect.left, &rect.width, ++ MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); ++ ++ soc_camera_limit_side(&rect.top, &rect.height, ++ MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); ++ ++ /* Like in example app. Contradicts the datasheet though */ ++ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); ++ if (ret >= 0) { ++ if (ret & 1) /* Autoexposure */ ++ ret = reg_write(client, mt9v022->reg->max_total_shutter_width, ++ rect.height + mt9v022->y_skip_top + 43); ++ /* ++ * If autoexposure is off, there is no need to set ++ * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off ++ * only if the user has set exposure manually, using the ++ * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. ++ * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH ++ * already contains the correct value. ++ */ ++ } ++ /* Setup frame format: defaults apart from width and height */ ++ if (!ret) ++ ret = reg_write(client, MT9V022_COLUMN_START, rect.left); ++ if (!ret) ++ ret = reg_write(client, MT9V022_ROW_START, rect.top); ++ if (!ret) ++ /* ++ * Default 94, Phytec driver says: ++ * "width + horizontal blank >= 660" ++ */ ++ ret = v4l2_ctrl_s_ctrl(mt9v022->hblank, ++ rect.width > 660 - 43 ? 43 : 660 - rect.width); ++ if (!ret) ++ ret = v4l2_ctrl_s_ctrl(mt9v022->vblank, 45); ++ if (!ret) ++ ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); ++ if (!ret) ++ ret = reg_write(client, MT9V022_WINDOW_HEIGHT, ++ rect.height + mt9v022->y_skip_top); ++ ++ if (ret < 0) ++ return ret; ++ ++ dev_dbg(&client->dev, "Frame %dx%d pixel\n", rect.width, rect.height); ++ ++ mt9v022->rect = rect; ++ ++ return 0; ++} ++ ++static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ a->c = mt9v022->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = MT9V022_COLUMN_SKIP; ++ a->bounds.top = MT9V022_ROW_SKIP; ++ a->bounds.width = MT9V022_MAX_WIDTH; ++ a->bounds.height = MT9V022_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int mt9v022_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ mf->width = mt9v022->rect.width; ++ mf->height = mt9v022->rect.height; ++ mf->code = mt9v022->fmt->code; ++ mf->colorspace = mt9v022->fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int mt9v022_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ struct v4l2_crop a = { ++ .c = { ++ .left = mt9v022->rect.left, ++ .top = mt9v022->rect.top, ++ .width = mf->width, ++ .height = mf->height, ++ }, ++ }; ++ int ret; ++ ++ /* ++ * The caller provides a supported format, as verified per call to ++ * .try_mbus_fmt(), datawidth is from our supported format list ++ */ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_Y8_1X8: ++ case V4L2_MBUS_FMT_Y10_1X10: ++ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) ++ return -EINVAL; ++ break; ++ case V4L2_MBUS_FMT_SBGGR8_1X8: ++ case V4L2_MBUS_FMT_SBGGR10_1X10: ++ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* No support for scaling on this camera, just crop. */ ++ ret = mt9v022_s_crop(sd, &a); ++ if (!ret) { ++ mf->width = mt9v022->rect.width; ++ mf->height = mt9v022->rect.height; ++ mt9v022->fmt = mt9v022_find_datafmt(mf->code, ++ mt9v022->fmts, mt9v022->num_fmts); ++ mf->colorspace = mt9v022->fmt->colorspace; ++ } ++ ++ return ret; ++} ++ ++static int mt9v022_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ const struct mt9v022_datafmt *fmt; ++ int align = mf->code == V4L2_MBUS_FMT_SBGGR8_1X8 || ++ mf->code == V4L2_MBUS_FMT_SBGGR10_1X10; ++ ++ v4l_bound_align_image(&mf->width, MT9V022_MIN_WIDTH, ++ MT9V022_MAX_WIDTH, align, ++ &mf->height, MT9V022_MIN_HEIGHT + mt9v022->y_skip_top, ++ MT9V022_MAX_HEIGHT + mt9v022->y_skip_top, align, 0); ++ ++ fmt = mt9v022_find_datafmt(mf->code, mt9v022->fmts, ++ mt9v022->num_fmts); ++ if (!fmt) { ++ fmt = mt9v022->fmt; ++ mf->code = fmt->code; ++ } ++ ++ mf->colorspace = fmt->colorspace; ++ ++ return 0; ++} ++ ++static int mt9v022_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = mt9v022->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mt9v022_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ reg->size = 2; ++ reg->val = reg_read(client, reg->reg); ++ ++ if (reg->val > 0xffff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int mt9v022_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int mt9v022_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9v022 *mt9v022 = container_of(ctrl->handler, ++ struct mt9v022, hdl); ++ struct v4l2_subdev *sd = &mt9v022->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct v4l2_ctrl *gain = mt9v022->gain; ++ struct v4l2_ctrl *exp = mt9v022->exposure; ++ unsigned long range; ++ int data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ data = reg_read(client, MT9V022_ANALOG_GAIN); ++ if (data < 0) ++ return -EIO; ++ ++ range = gain->maximum - gain->minimum; ++ gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; ++ return 0; ++ case V4L2_CID_EXPOSURE_AUTO: ++ data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); ++ if (data < 0) ++ return -EIO; ++ ++ range = exp->maximum - exp->minimum; ++ exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; ++ return 0; ++ case V4L2_CID_HBLANK: ++ data = reg_read(client, MT9V022_HORIZONTAL_BLANKING); ++ if (data < 0) ++ return -EIO; ++ ctrl->val = data; ++ return 0; ++ case V4L2_CID_VBLANK: ++ data = reg_read(client, MT9V022_VERTICAL_BLANKING); ++ if (data < 0) ++ return -EIO; ++ ctrl->val = data; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct mt9v022 *mt9v022 = container_of(ctrl->handler, ++ struct mt9v022, hdl); ++ struct v4l2_subdev *sd = &mt9v022->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ data = reg_set(client, MT9V022_READ_MODE, 0x10); ++ else ++ data = reg_clear(client, MT9V022_READ_MODE, 0x10); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ data = reg_set(client, MT9V022_READ_MODE, 0x20); ++ else ++ data = reg_clear(client, MT9V022_READ_MODE, 0x20); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_AUTOGAIN: ++ if (ctrl->val) { ++ if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) ++ return -EIO; ++ } else { ++ struct v4l2_ctrl *gain = mt9v022->gain; ++ /* mt9v022 has minimum == default */ ++ unsigned long range = gain->maximum - gain->minimum; ++ /* Valid values 16 to 64, 32 to 64 must be even. */ ++ unsigned long gain_val = ((gain->val - gain->minimum) * ++ 48 + range / 2) / range + 16; ++ ++ if (gain_val >= 32) ++ gain_val &= ~1; ++ ++ /* ++ * The user wants to set gain manually, hope, she ++ * knows, what she's doing... Switch AGC off. ++ */ ++ if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) ++ return -EIO; ++ ++ dev_dbg(&client->dev, "Setting gain from %d to %lu\n", ++ reg_read(client, MT9V022_ANALOG_GAIN), gain_val); ++ if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) ++ return -EIO; ++ } ++ return 0; ++ case V4L2_CID_EXPOSURE_AUTO: ++ if (ctrl->val == V4L2_EXPOSURE_AUTO) { ++ data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); ++ } else { ++ struct v4l2_ctrl *exp = mt9v022->exposure; ++ unsigned long range = exp->maximum - exp->minimum; ++ unsigned long shutter = ((exp->val - exp->minimum) * ++ 479 + range / 2) / range + 1; ++ ++ /* ++ * The user wants to set shutter width manually, hope, ++ * she knows, what she's doing... Switch AEC off. ++ */ ++ data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); ++ if (data < 0) ++ return -EIO; ++ dev_dbg(&client->dev, "Shutter width from %d to %lu\n", ++ reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), ++ shutter); ++ if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, ++ shutter) < 0) ++ return -EIO; ++ } ++ return 0; ++ case V4L2_CID_HBLANK: ++ if (reg_write(client, MT9V022_HORIZONTAL_BLANKING, ++ ctrl->val) < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_VBLANK: ++ if (reg_write(client, MT9V022_VERTICAL_BLANKING, ++ ctrl->val) < 0) ++ return -EIO; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * Interface active, can use i2c. If it fails, it can indeed mean, that ++ * this wasn't our capture interface, so, we wait for the right one ++ */ ++static int mt9v022_video_probe(struct i2c_client *client) ++{ ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ s32 data; ++ int ret; ++ unsigned long flags; ++ ++ ret = mt9v022_s_power(&mt9v022->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Read out the chip version register */ ++ data = reg_read(client, MT9V022_CHIP_VERSION); ++ ++ /* must be 0x1311, 0x1313 or 0x1324 */ ++ if (data != 0x1311 && data != 0x1313 && data != 0x1324) { ++ ret = -ENODEV; ++ dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", ++ data); ++ goto ei2c; ++ } ++ ++ mt9v022->chip_version = data; ++ ++ mt9v022->reg = is_mt9v024(data) ? &mt9v024_register : ++ &mt9v022_register; ++ ++ /* Soft reset */ ++ ret = reg_write(client, MT9V022_RESET, 1); ++ if (ret < 0) ++ goto ei2c; ++ /* 15 clock cycles */ ++ udelay(200); ++ if (reg_read(client, MT9V022_RESET)) { ++ dev_err(&client->dev, "Resetting MT9V022 failed!\n"); ++ if (ret > 0) ++ ret = -EIO; ++ goto ei2c; ++ } ++ ++ /* Set monochrome or colour sensor type */ ++ if (sensor_type && (!strcmp("colour", sensor_type) || ++ !strcmp("color", sensor_type))) { ++ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); ++ mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; ++ mt9v022->fmts = mt9v022_colour_fmts; ++ } else { ++ ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); ++ mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; ++ mt9v022->fmts = mt9v022_monochrome_fmts; ++ } ++ ++ if (ret < 0) ++ goto ei2c; ++ ++ mt9v022->num_fmts = 0; ++ ++ /* ++ * This is a 10bit sensor, so by default we only allow 10bit. ++ * The platform may support different bus widths due to ++ * different routing of the data lines. ++ */ ++ if (ssdd->query_bus_param) ++ flags = ssdd->query_bus_param(ssdd); ++ else ++ flags = SOCAM_DATAWIDTH_10; ++ ++ if (flags & SOCAM_DATAWIDTH_10) ++ mt9v022->num_fmts++; ++ else ++ mt9v022->fmts++; ++ ++ if (flags & SOCAM_DATAWIDTH_8) ++ mt9v022->num_fmts++; ++ ++ mt9v022->fmt = &mt9v022->fmts[0]; ++ ++ dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", ++ data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? ++ "monochrome" : "colour"); ++ ++ ret = mt9v022_init(client); ++ if (ret < 0) ++ dev_err(&client->dev, "Failed to initialise the camera\n"); ++ ++ei2c: ++ mt9v022_s_power(&mt9v022->subdev, 0); ++ return ret; ++} ++ ++static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ *lines = mt9v022->y_skip_top; ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { ++ .g_volatile_ctrl = mt9v022_g_volatile_ctrl, ++ .s_ctrl = mt9v022_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { ++ .g_chip_ident = mt9v022_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = mt9v022_g_register, ++ .s_register = mt9v022_s_register, ++#endif ++ .s_power = mt9v022_s_power, ++}; ++ ++static int mt9v022_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ ++ if (index >= mt9v022->num_fmts) ++ return -EINVAL; ++ ++ *code = mt9v022->fmts[index].code; ++ return 0; ++} ++ ++static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | ++ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); ++ unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; ++ int ret; ++ u16 pixclk = 0; ++ ++ if (ssdd->set_bus_param) { ++ ret = ssdd->set_bus_param(ssdd, 1 << (bps - 1)); ++ if (ret) ++ return ret; ++ } else if (bps != 10) { ++ /* ++ * Without board specific bus width settings we only support the ++ * sensors native bus width ++ */ ++ return -EINVAL; ++ } ++ ++ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) ++ pixclk |= 0x10; ++ ++ if (!(flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)) ++ pixclk |= 0x1; ++ ++ if (!(flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)) ++ pixclk |= 0x2; ++ ++ ret = reg_write(client, mt9v022->reg->pixclk_fv_lv, pixclk); ++ if (ret < 0) ++ return ret; ++ ++ if (!(flags & V4L2_MBUS_MASTER)) ++ mt9v022->chip_control &= ~0x8; ++ ++ ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); ++ if (ret < 0) ++ return ret; ++ ++ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", ++ pixclk, mt9v022->chip_control); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { ++ .s_stream = mt9v022_s_stream, ++ .s_mbus_fmt = mt9v022_s_fmt, ++ .g_mbus_fmt = mt9v022_g_fmt, ++ .try_mbus_fmt = mt9v022_try_fmt, ++ .s_crop = mt9v022_s_crop, ++ .g_crop = mt9v022_g_crop, ++ .cropcap = mt9v022_cropcap, ++ .enum_mbus_fmt = mt9v022_enum_fmt, ++ .g_mbus_config = mt9v022_g_mbus_config, ++ .s_mbus_config = mt9v022_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_sensor_ops mt9v022_subdev_sensor_ops = { ++ .g_skip_top_lines = mt9v022_g_skip_top_lines, ++}; ++ ++static struct v4l2_subdev_ops mt9v022_subdev_ops = { ++ .core = &mt9v022_subdev_core_ops, ++ .video = &mt9v022_subdev_video_ops, ++ .sensor = &mt9v022_subdev_sensor_ops, ++}; ++ ++static int mt9v022_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct mt9v022 *mt9v022; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct mt9v022_platform_data *pdata; ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "MT9V022 driver needs platform data\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); ++ return -EIO; ++ } ++ ++ mt9v022 = devm_kzalloc(&client->dev, sizeof(struct mt9v022), GFP_KERNEL); ++ if (!mt9v022) ++ return -ENOMEM; ++ ++ pdata = ssdd->drv_priv; ++ v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); ++ v4l2_ctrl_handler_init(&mt9v022->hdl, 6); ++ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ++ mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_GAIN, 0, 127, 1, 64); ++ ++ /* ++ * Simulated autoexposure. If enabled, we calculate shutter width ++ * ourselves in the driver based on vertical blanking and frame width ++ */ ++ mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, ++ &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, ++ V4L2_EXPOSURE_AUTO); ++ mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_EXPOSURE, 1, 255, 1, 255); ++ ++ mt9v022->hblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_HBLANK, MT9V022_HORIZONTAL_BLANKING_MIN, ++ MT9V022_HORIZONTAL_BLANKING_MAX, 1, ++ MT9V022_HORIZONTAL_BLANKING_DEF); ++ ++ mt9v022->vblank = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, ++ V4L2_CID_VBLANK, MT9V022_VERTICAL_BLANKING_MIN, ++ MT9V022_VERTICAL_BLANKING_MAX, 1, ++ MT9V022_VERTICAL_BLANKING_DEF); ++ ++ mt9v022->subdev.ctrl_handler = &mt9v022->hdl; ++ if (mt9v022->hdl.error) { ++ int err = mt9v022->hdl.error; ++ ++ dev_err(&client->dev, "control initialisation err %d\n", err); ++ return err; ++ } ++ v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, ++ V4L2_EXPOSURE_MANUAL, true); ++ v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); ++ ++ mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; ++ ++ /* ++ * On some platforms the first read out line is corrupted. ++ * Workaround it by skipping if indicated by platform data. ++ */ ++ mt9v022->y_skip_top = pdata ? pdata->y_skip_top : 0; ++ mt9v022->rect.left = MT9V022_COLUMN_SKIP; ++ mt9v022->rect.top = MT9V022_ROW_SKIP; ++ mt9v022->rect.width = MT9V022_MAX_WIDTH; ++ mt9v022->rect.height = MT9V022_MAX_HEIGHT; ++ ++ ret = mt9v022_video_probe(client); ++ if (ret) ++ v4l2_ctrl_handler_free(&mt9v022->hdl); ++ ++ return ret; ++} ++ ++static int mt9v022_remove(struct i2c_client *client) ++{ ++ struct mt9v022 *mt9v022 = to_mt9v022(client); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ v4l2_device_unregister_subdev(&mt9v022->subdev); ++ if (ssdd->free_bus) ++ ssdd->free_bus(ssdd); ++ v4l2_ctrl_handler_free(&mt9v022->hdl); ++ ++ return 0; ++} ++static const struct i2c_device_id mt9v022_id[] = { ++ { "mt9v022", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, mt9v022_id); ++ ++static struct i2c_driver mt9v022_i2c_driver = { ++ .driver = { ++ .name = "mt9v022", ++ }, ++ .probe = mt9v022_probe, ++ .remove = mt9v022_remove, ++ .id_table = mt9v022_id, ++}; ++ ++module_i2c_driver(mt9v022_i2c_driver); ++ ++MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c +new file mode 100644 +index 0000000..0f520f6 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov2640.c +@@ -0,0 +1,1153 @@ ++/* ++ * ov2640 Camera Driver ++ * ++ * Copyright (C) 2010 Alberto Panizzo ++ * ++ * Based on ov772x, ov9640 drivers and previous non merged implementations. ++ * ++ * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. ++ * Copyright (C) 2006, OmniVision ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define VAL_SET(x, mask, rshift, lshift) \ ++ ((((x) >> rshift) & mask) << lshift) ++/* ++ * DSP registers ++ * register offset for BANK_SEL == BANK_SEL_DSP ++ */ ++#define R_BYPASS 0x05 /* Bypass DSP */ ++#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ ++#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ ++#define QS 0x44 /* Quantization Scale Factor */ ++#define CTRLI 0x50 ++#define CTRLI_LP_DP 0x80 ++#define CTRLI_ROUND 0x40 ++#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) ++#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) ++#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ ++#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) ++#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ ++#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) ++#define XOFFL 0x53 /* OFFSET_X[7:0] */ ++#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) ++#define YOFFL 0x54 /* OFFSET_Y[7:0] */ ++#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) ++#define VHYX 0x55 /* Offset and size completion */ ++#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) ++#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) ++#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) ++#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) ++#define DPRP 0x56 ++#define TEST 0x57 /* Horizontal size completion */ ++#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) ++#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ ++#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) ++#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ ++#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) ++#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ ++#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) ++#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) ++#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) ++#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ ++#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ ++#define CTRL2 0x86 /* DSP Module enable 2 */ ++#define CTRL2_DCW_EN 0x20 ++#define CTRL2_SDE_EN 0x10 ++#define CTRL2_UV_ADJ_EN 0x08 ++#define CTRL2_UV_AVG_EN 0x04 ++#define CTRL2_CMX_EN 0x01 ++#define CTRL3 0x87 /* DSP Module enable 3 */ ++#define CTRL3_BPC_EN 0x80 ++#define CTRL3_WPC_EN 0x40 ++#define SIZEL 0x8C /* Image Size Completion */ ++#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) ++#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) ++#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) ++#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ ++#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) ++#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ ++#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) ++#define CTRL0 0xC2 /* DSP Module enable 0 */ ++#define CTRL0_AEC_EN 0x80 ++#define CTRL0_AEC_SEL 0x40 ++#define CTRL0_STAT_SEL 0x20 ++#define CTRL0_VFIRST 0x10 ++#define CTRL0_YUV422 0x08 ++#define CTRL0_YUV_EN 0x04 ++#define CTRL0_RGB_EN 0x02 ++#define CTRL0_RAW_EN 0x01 ++#define CTRL1 0xC3 /* DSP Module enable 1 */ ++#define CTRL1_CIP 0x80 ++#define CTRL1_DMY 0x40 ++#define CTRL1_RAW_GMA 0x20 ++#define CTRL1_DG 0x10 ++#define CTRL1_AWB 0x08 ++#define CTRL1_AWB_GAIN 0x04 ++#define CTRL1_LENC 0x02 ++#define CTRL1_PRE 0x01 ++#define R_DVP_SP 0xD3 /* DVP output speed control */ ++#define R_DVP_SP_AUTO_MODE 0x80 ++#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); ++ * = sysclk (48)/(2*[6:0]) (RAW);*/ ++#define IMAGE_MODE 0xDA /* Image Output Format Select */ ++#define IMAGE_MODE_Y8_DVP_EN 0x40 ++#define IMAGE_MODE_JPEG_EN 0x10 ++#define IMAGE_MODE_YUV422 0x00 ++#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ ++#define IMAGE_MODE_RGB565 0x08 ++#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output ++ * mode (0 for HREF is same as sensor) */ ++#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP ++ * 1: Low byte first UYVY (C2[4] =0) ++ * VYUY (C2[4] =1) ++ * 0: High byte first YUYV (C2[4]=0) ++ * YVYU (C2[4] = 1) */ ++#define RESET 0xE0 /* Reset */ ++#define RESET_MICROC 0x40 ++#define RESET_SCCB 0x20 ++#define RESET_JPEG 0x10 ++#define RESET_DVP 0x04 ++#define RESET_IPU 0x02 ++#define RESET_CIF 0x01 ++#define REGED 0xED /* Register ED */ ++#define REGED_CLK_OUT_DIS 0x10 ++#define MS_SP 0xF0 /* SCCB Master Speed */ ++#define SS_ID 0xF7 /* SCCB Slave ID */ ++#define SS_CTRL 0xF8 /* SCCB Slave Control */ ++#define SS_CTRL_ADD_AUTO_INC 0x20 ++#define SS_CTRL_EN 0x08 ++#define SS_CTRL_DELAY_CLK 0x04 ++#define SS_CTRL_ACC_EN 0x02 ++#define SS_CTRL_SEN_PASS_THR 0x01 ++#define MC_BIST 0xF9 /* Microcontroller misc register */ ++#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ ++#define MC_BIST_BOOT_ROM_SEL 0x40 ++#define MC_BIST_12KB_SEL 0x20 ++#define MC_BIST_12KB_MASK 0x30 ++#define MC_BIST_512KB_SEL 0x08 ++#define MC_BIST_512KB_MASK 0x0C ++#define MC_BIST_BUSY_BIT_R 0x02 ++#define MC_BIST_MC_RES_ONE_SH_W 0x02 ++#define MC_BIST_LAUNCH 0x01 ++#define BANK_SEL 0xFF /* Register Bank Select */ ++#define BANK_SEL_DSP 0x00 ++#define BANK_SEL_SENS 0x01 ++ ++/* ++ * Sensor registers ++ * register offset for BANK_SEL == BANK_SEL_SENS ++ */ ++#define GAIN 0x00 /* AGC - Gain control gain setting */ ++#define COM1 0x03 /* Common control 1 */ ++#define COM1_1_DUMMY_FR 0x40 ++#define COM1_3_DUMMY_FR 0x80 ++#define COM1_7_DUMMY_FR 0xC0 ++#define COM1_VWIN_LSB_UXGA 0x0F ++#define COM1_VWIN_LSB_SVGA 0x0A ++#define COM1_VWIN_LSB_CIF 0x06 ++#define REG04 0x04 /* Register 04 */ ++#define REG04_DEF 0x20 /* Always set */ ++#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ ++#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ ++#define REG04_VREF_EN 0x10 ++#define REG04_HREF_EN 0x08 ++#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) ++#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ ++#define COM2 0x09 /* Common control 2 */ ++#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ ++ /* Output drive capability */ ++#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ ++#define PID 0x0A /* Product ID Number MSB */ ++#define VER 0x0B /* Product ID Number LSB */ ++#define COM3 0x0C /* Common control 3 */ ++#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ ++#define COM3_BAND_AUTO 0x02 /* Auto Banding */ ++#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the ++ * snapshot sequence*/ ++#define AEC 0x10 /* AEC[9:2] Exposure Value */ ++#define CLKRC 0x11 /* Internal clock */ ++#define CLKRC_EN 0x80 ++#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ ++#define COM7 0x12 /* Common control 7 */ ++#define COM7_SRST 0x80 /* Initiates system reset. All registers are ++ * set to factory default values after which ++ * the chip resumes normal operation */ ++#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ ++#define COM7_RES_SVGA 0x40 /* SVGA */ ++#define COM7_RES_CIF 0x20 /* CIF */ ++#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ ++#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ ++#define COM8 0x13 /* Common control 8 */ ++#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ ++#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ ++#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ ++#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ ++#define COM9 0x14 /* Common control 9 ++ * Automatic gain ceiling - maximum AGC value [7:5]*/ ++#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ ++#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ ++#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ ++#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ ++#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ ++#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ ++#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ ++#define COM10 0x15 /* Common control 10 */ ++#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ ++#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of ++ * PCLK (user can latch data at the next ++ * falling edge of PCLK). ++ * 0 otherwise. */ ++#define COM10_HREF_INV 0x08 /* Invert HREF polarity: ++ * HREF negative for valid data*/ ++#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ ++#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ ++#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ ++#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ ++#define VEND 0x1A /* Vertical Window end MSB 8 bit */ ++#define MIDH 0x1C /* Manufacturer ID byte - high */ ++#define MIDL 0x1D /* Manufacturer ID byte - low */ ++#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ ++#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ ++#define VV 0x26 /* AGC/AEC Fast mode operating region */ ++#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) ++#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) ++#define REG2A 0x2A /* Dummy pixel insert MSB */ ++#define FRARL 0x2B /* Dummy pixel insert LSB */ ++#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ ++#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ ++#define YAVG 0x2F /* Y/G Channel Average value */ ++#define REG32 0x32 /* Common Control 32 */ ++#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ ++#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ ++#define ARCOM2 0x34 /* Zoom: Horizontal start point */ ++#define REG45 0x45 /* Register 45 */ ++#define FLL 0x46 /* Frame Length Adjustment LSBs */ ++#define FLH 0x47 /* Frame Length Adjustment MSBs */ ++#define COM19 0x48 /* Zoom: Vertical start point */ ++#define ZOOMS 0x49 /* Zoom: Vertical start point */ ++#define COM22 0x4B /* Flash light control */ ++#define COM25 0x4E /* For Banding operations */ ++#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ ++#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ ++#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ ++#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ ++#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ ++#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ ++#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ ++#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ ++ ++/* ++ * ID ++ */ ++#define MANUFACTURER_ID 0x7FA2 ++#define PID_OV2640 0x2642 ++#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) ++ ++/* ++ * Struct ++ */ ++struct regval_list { ++ u8 reg_num; ++ u8 value; ++}; ++ ++/* Supported resolutions */ ++enum ov2640_width { ++ W_QCIF = 176, ++ W_QVGA = 320, ++ W_CIF = 352, ++ W_VGA = 640, ++ W_SVGA = 800, ++ W_XGA = 1024, ++ W_SXGA = 1280, ++ W_UXGA = 1600, ++}; ++ ++enum ov2640_height { ++ H_QCIF = 144, ++ H_QVGA = 240, ++ H_CIF = 288, ++ H_VGA = 480, ++ H_SVGA = 600, ++ H_XGA = 768, ++ H_SXGA = 1024, ++ H_UXGA = 1200, ++}; ++ ++struct ov2640_win_size { ++ char *name; ++ enum ov2640_width width; ++ enum ov2640_height height; ++ const struct regval_list *regs; ++}; ++ ++ ++struct ov2640_priv { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ enum v4l2_mbus_pixelcode cfmt_code; ++ const struct ov2640_win_size *win; ++ int model; ++}; ++ ++/* ++ * Registers settings ++ */ ++ ++#define ENDMARKER { 0xff, 0xff } ++ ++static const struct regval_list ov2640_init_regs[] = { ++ { BANK_SEL, BANK_SEL_DSP }, ++ { 0x2c, 0xff }, ++ { 0x2e, 0xdf }, ++ { BANK_SEL, BANK_SEL_SENS }, ++ { 0x3c, 0x32 }, ++ { CLKRC, CLKRC_DIV_SET(1) }, ++ { COM2, COM2_OCAP_Nx_SET(3) }, ++ { REG04, REG04_DEF | REG04_HREF_EN }, ++ { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN }, ++ { COM9, COM9_AGC_GAIN_8x | 0x08}, ++ { 0x2c, 0x0c }, ++ { 0x33, 0x78 }, ++ { 0x3a, 0x33 }, ++ { 0x3b, 0xfb }, ++ { 0x3e, 0x00 }, ++ { 0x43, 0x11 }, ++ { 0x16, 0x10 }, ++ { 0x39, 0x02 }, ++ { 0x35, 0x88 }, ++ { 0x22, 0x0a }, ++ { 0x37, 0x40 }, ++ { 0x23, 0x00 }, ++ { ARCOM2, 0xa0 }, ++ { 0x06, 0x02 }, ++ { 0x06, 0x88 }, ++ { 0x07, 0xc0 }, ++ { 0x0d, 0xb7 }, ++ { 0x0e, 0x01 }, ++ { 0x4c, 0x00 }, ++ { 0x4a, 0x81 }, ++ { 0x21, 0x99 }, ++ { AEW, 0x40 }, ++ { AEB, 0x38 }, ++ { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, ++ { 0x5c, 0x00 }, ++ { 0x63, 0x00 }, ++ { FLL, 0x22 }, ++ { COM3, 0x38 | COM3_BAND_AUTO }, ++ { REG5D, 0x55 }, ++ { REG5E, 0x7d }, ++ { REG5F, 0x7d }, ++ { REG60, 0x55 }, ++ { HISTO_LOW, 0x70 }, ++ { HISTO_HIGH, 0x80 }, ++ { 0x7c, 0x05 }, ++ { 0x20, 0x80 }, ++ { 0x28, 0x30 }, ++ { 0x6c, 0x00 }, ++ { 0x6d, 0x80 }, ++ { 0x6e, 0x00 }, ++ { 0x70, 0x02 }, ++ { 0x71, 0x94 }, ++ { 0x73, 0xc1 }, ++ { 0x3d, 0x34 }, ++ { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, ++ { 0x5a, 0x57 }, ++ { BD50, 0xbb }, ++ { BD60, 0x9c }, ++ { BANK_SEL, BANK_SEL_DSP }, ++ { 0xe5, 0x7f }, ++ { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, ++ { 0x41, 0x24 }, ++ { RESET, RESET_JPEG | RESET_DVP }, ++ { 0x76, 0xff }, ++ { 0x33, 0xa0 }, ++ { 0x42, 0x20 }, ++ { 0x43, 0x18 }, ++ { 0x4c, 0x00 }, ++ { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, ++ { 0x88, 0x3f }, ++ { 0xd7, 0x03 }, ++ { 0xd9, 0x10 }, ++ { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, ++ { 0xc8, 0x08 }, ++ { 0xc9, 0x80 }, ++ { BPADDR, 0x00 }, ++ { BPDATA, 0x00 }, ++ { BPADDR, 0x03 }, ++ { BPDATA, 0x48 }, ++ { BPDATA, 0x48 }, ++ { BPADDR, 0x08 }, ++ { BPDATA, 0x20 }, ++ { BPDATA, 0x10 }, ++ { BPDATA, 0x0e }, ++ { 0x90, 0x00 }, ++ { 0x91, 0x0e }, ++ { 0x91, 0x1a }, ++ { 0x91, 0x31 }, ++ { 0x91, 0x5a }, ++ { 0x91, 0x69 }, ++ { 0x91, 0x75 }, ++ { 0x91, 0x7e }, ++ { 0x91, 0x88 }, ++ { 0x91, 0x8f }, ++ { 0x91, 0x96 }, ++ { 0x91, 0xa3 }, ++ { 0x91, 0xaf }, ++ { 0x91, 0xc4 }, ++ { 0x91, 0xd7 }, ++ { 0x91, 0xe8 }, ++ { 0x91, 0x20 }, ++ { 0x92, 0x00 }, ++ { 0x93, 0x06 }, ++ { 0x93, 0xe3 }, ++ { 0x93, 0x03 }, ++ { 0x93, 0x03 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x02 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x93, 0x00 }, ++ { 0x96, 0x00 }, ++ { 0x97, 0x08 }, ++ { 0x97, 0x19 }, ++ { 0x97, 0x02 }, ++ { 0x97, 0x0c }, ++ { 0x97, 0x24 }, ++ { 0x97, 0x30 }, ++ { 0x97, 0x28 }, ++ { 0x97, 0x26 }, ++ { 0x97, 0x02 }, ++ { 0x97, 0x98 }, ++ { 0x97, 0x80 }, ++ { 0x97, 0x00 }, ++ { 0x97, 0x00 }, ++ { 0xa4, 0x00 }, ++ { 0xa8, 0x00 }, ++ { 0xc5, 0x11 }, ++ { 0xc6, 0x51 }, ++ { 0xbf, 0x80 }, ++ { 0xc7, 0x10 }, ++ { 0xb6, 0x66 }, ++ { 0xb8, 0xA5 }, ++ { 0xb7, 0x64 }, ++ { 0xb9, 0x7C }, ++ { 0xb3, 0xaf }, ++ { 0xb4, 0x97 }, ++ { 0xb5, 0xFF }, ++ { 0xb0, 0xC5 }, ++ { 0xb1, 0x94 }, ++ { 0xb2, 0x0f }, ++ { 0xc4, 0x5c }, ++ { 0xa6, 0x00 }, ++ { 0xa7, 0x20 }, ++ { 0xa7, 0xd8 }, ++ { 0xa7, 0x1b }, ++ { 0xa7, 0x31 }, ++ { 0xa7, 0x00 }, ++ { 0xa7, 0x18 }, ++ { 0xa7, 0x20 }, ++ { 0xa7, 0xd8 }, ++ { 0xa7, 0x19 }, ++ { 0xa7, 0x31 }, ++ { 0xa7, 0x00 }, ++ { 0xa7, 0x18 }, ++ { 0xa7, 0x20 }, ++ { 0xa7, 0xd8 }, ++ { 0xa7, 0x19 }, ++ { 0xa7, 0x31 }, ++ { 0xa7, 0x00 }, ++ { 0xa7, 0x18 }, ++ { 0x7f, 0x00 }, ++ { 0xe5, 0x1f }, ++ { 0xe1, 0x77 }, ++ { 0xdd, 0x7f }, ++ { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN }, ++ ENDMARKER, ++}; ++ ++/* ++ * Register settings for window size ++ * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. ++ * Then the different zooming configurations will setup the output image size. ++ */ ++static const struct regval_list ov2640_size_change_preamble_regs[] = { ++ { BANK_SEL, BANK_SEL_DSP }, ++ { RESET, RESET_DVP }, ++ { HSIZE8, HSIZE8_SET(W_UXGA) }, ++ { VSIZE8, VSIZE8_SET(H_UXGA) }, ++ { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | ++ CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, ++ { HSIZE, HSIZE_SET(W_UXGA) }, ++ { VSIZE, VSIZE_SET(H_UXGA) }, ++ { XOFFL, XOFFL_SET(0) }, ++ { YOFFL, YOFFL_SET(0) }, ++ { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | ++ VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, ++ { TEST, TEST_HSIZE_SET(W_UXGA) }, ++ ENDMARKER, ++}; ++ ++#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ ++ { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ ++ CTRLI_H_DIV_SET(h_div)}, \ ++ { ZMOW, ZMOW_OUTW_SET(x) }, \ ++ { ZMOH, ZMOH_OUTH_SET(y) }, \ ++ { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ ++ { R_DVP_SP, pclk_div }, \ ++ { RESET, 0x00} ++ ++static const struct regval_list ov2640_qcif_regs[] = { ++ PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_qvga_regs[] = { ++ PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_cif_regs[] = { ++ PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_vga_regs[] = { ++ PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_svga_regs[] = { ++ PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_xga_regs[] = { ++ PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), ++ { CTRLI, 0x00}, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_sxga_regs[] = { ++ PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), ++ { CTRLI, 0x00}, ++ { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_uxga_regs[] = { ++ PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), ++ { CTRLI, 0x00}, ++ { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, ++ ENDMARKER, ++}; ++ ++#define OV2640_SIZE(n, w, h, r) \ ++ {.name = n, .width = w , .height = h, .regs = r } ++ ++static const struct ov2640_win_size ov2640_supported_win_sizes[] = { ++ OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), ++ OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), ++ OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), ++ OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), ++ OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), ++ OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), ++ OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), ++ OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), ++}; ++ ++/* ++ * Register settings for pixel formats ++ */ ++static const struct regval_list ov2640_format_change_preamble_regs[] = { ++ { BANK_SEL, BANK_SEL_DSP }, ++ { R_BYPASS, R_BYPASS_USE_DSP }, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_yuyv_regs[] = { ++ { IMAGE_MODE, IMAGE_MODE_YUV422 }, ++ { 0xd7, 0x03 }, ++ { 0x33, 0xa0 }, ++ { 0xe5, 0x1f }, ++ { 0xe1, 0x67 }, ++ { RESET, 0x00 }, ++ { R_BYPASS, R_BYPASS_USE_DSP }, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_uyvy_regs[] = { ++ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, ++ { 0xd7, 0x01 }, ++ { 0x33, 0xa0 }, ++ { 0xe1, 0x67 }, ++ { RESET, 0x00 }, ++ { R_BYPASS, R_BYPASS_USE_DSP }, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_rgb565_be_regs[] = { ++ { IMAGE_MODE, IMAGE_MODE_RGB565 }, ++ { 0xd7, 0x03 }, ++ { RESET, 0x00 }, ++ { R_BYPASS, R_BYPASS_USE_DSP }, ++ ENDMARKER, ++}; ++ ++static const struct regval_list ov2640_rgb565_le_regs[] = { ++ { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, ++ { 0xd7, 0x03 }, ++ { RESET, 0x00 }, ++ { R_BYPASS, R_BYPASS_USE_DSP }, ++ ENDMARKER, ++}; ++ ++static enum v4l2_mbus_pixelcode ov2640_codes[] = { ++ V4L2_MBUS_FMT_YUYV8_2X8, ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_RGB565_2X8_BE, ++ V4L2_MBUS_FMT_RGB565_2X8_LE, ++}; ++ ++/* ++ * General functions ++ */ ++static struct ov2640_priv *to_ov2640(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov2640_priv, ++ subdev); ++} ++ ++static int ov2640_write_array(struct i2c_client *client, ++ const struct regval_list *vals) ++{ ++ int ret; ++ ++ while ((vals->reg_num != 0xff) || (vals->value != 0xff)) { ++ ret = i2c_smbus_write_byte_data(client, ++ vals->reg_num, vals->value); ++ dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x", ++ vals->reg_num, vals->value); ++ ++ if (ret < 0) ++ return ret; ++ vals++; ++ } ++ return 0; ++} ++ ++static int ov2640_mask_set(struct i2c_client *client, ++ u8 reg, u8 mask, u8 set) ++{ ++ s32 val = i2c_smbus_read_byte_data(client, reg); ++ if (val < 0) ++ return val; ++ ++ val &= ~mask; ++ val |= set & mask; ++ ++ dev_vdbg(&client->dev, "masks: 0x%02x, 0x%02x", reg, val); ++ ++ return i2c_smbus_write_byte_data(client, reg, val); ++} ++ ++static int ov2640_reset(struct i2c_client *client) ++{ ++ int ret; ++ const struct regval_list reset_seq[] = { ++ {BANK_SEL, BANK_SEL_SENS}, ++ {COM7, COM7_SRST}, ++ ENDMARKER, ++ }; ++ ++ ret = ov2640_write_array(client, reset_seq); ++ if (ret) ++ goto err; ++ ++ msleep(5); ++err: ++ dev_dbg(&client->dev, "%s: (ret %d)", __func__, ret); ++ return ret; ++} ++ ++/* ++ * soc_camera_ops functions ++ */ ++static int ov2640_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = ++ &container_of(ctrl->handler, struct ov2640_priv, hdl)->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 val; ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS); ++ if (ret < 0) ++ return ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ val = ctrl->val ? REG04_VFLIP_IMG : 0x00; ++ return ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); ++ case V4L2_CID_HFLIP: ++ val = ctrl->val ? REG04_HFLIP_IMG : 0x00; ++ return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); ++ } ++ ++ return -EINVAL; ++} ++ ++static int ov2640_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov2640_priv *priv = to_ov2640(client); ++ ++ id->ident = priv->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov2640_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ reg->size = 1; ++ if (reg->reg > 0xff) ++ return -EINVAL; ++ ++ ret = i2c_smbus_read_byte_data(client, reg->reg); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = ret; ++ ++ return 0; ++} ++ ++static int ov2640_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg > 0xff || ++ reg->val > 0xff) ++ return -EINVAL; ++ ++ return i2c_smbus_write_byte_data(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov2640_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++/* Select the nearest higher resolution for capture */ ++static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height) ++{ ++ int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1; ++ ++ for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) { ++ if (ov2640_supported_win_sizes[i].width >= *width && ++ ov2640_supported_win_sizes[i].height >= *height) { ++ *width = ov2640_supported_win_sizes[i].width; ++ *height = ov2640_supported_win_sizes[i].height; ++ return &ov2640_supported_win_sizes[i]; ++ } ++ } ++ ++ *width = ov2640_supported_win_sizes[default_size].width; ++ *height = ov2640_supported_win_sizes[default_size].height; ++ return &ov2640_supported_win_sizes[default_size]; ++} ++ ++static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height, ++ enum v4l2_mbus_pixelcode code) ++{ ++ struct ov2640_priv *priv = to_ov2640(client); ++ const struct regval_list *selected_cfmt_regs; ++ int ret; ++ ++ /* select win */ ++ priv->win = ov2640_select_win(width, height); ++ ++ /* select format */ ++ priv->cfmt_code = 0; ++ switch (code) { ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); ++ selected_cfmt_regs = ov2640_rgb565_be_regs; ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ dev_dbg(&client->dev, "%s: Selected cfmt RGB565 LE", __func__); ++ selected_cfmt_regs = ov2640_rgb565_le_regs; ++ break; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ dev_dbg(&client->dev, "%s: Selected cfmt YUYV (YUV422)", __func__); ++ selected_cfmt_regs = ov2640_yuyv_regs; ++ break; ++ default: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ dev_dbg(&client->dev, "%s: Selected cfmt UYVY", __func__); ++ selected_cfmt_regs = ov2640_uyvy_regs; ++ } ++ ++ /* reset hardware */ ++ ov2640_reset(client); ++ ++ /* initialize the sensor with default data */ ++ dev_dbg(&client->dev, "%s: Init default", __func__); ++ ret = ov2640_write_array(client, ov2640_init_regs); ++ if (ret < 0) ++ goto err; ++ ++ /* select preamble */ ++ dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name); ++ ret = ov2640_write_array(client, ov2640_size_change_preamble_regs); ++ if (ret < 0) ++ goto err; ++ ++ /* set size win */ ++ ret = ov2640_write_array(client, priv->win->regs); ++ if (ret < 0) ++ goto err; ++ ++ /* cfmt preamble */ ++ dev_dbg(&client->dev, "%s: Set cfmt", __func__); ++ ret = ov2640_write_array(client, ov2640_format_change_preamble_regs); ++ if (ret < 0) ++ goto err; ++ ++ /* set cfmt */ ++ ret = ov2640_write_array(client, selected_cfmt_regs); ++ if (ret < 0) ++ goto err; ++ ++ priv->cfmt_code = code; ++ *width = priv->win->width; ++ *height = priv->win->height; ++ ++ return 0; ++ ++err: ++ dev_err(&client->dev, "%s: Error %d", __func__, ret); ++ ov2640_reset(client); ++ priv->win = NULL; ++ ++ return ret; ++} ++ ++static int ov2640_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov2640_priv *priv = to_ov2640(client); ++ ++ if (!priv->win) { ++ u32 width = W_SVGA, height = H_SVGA; ++ priv->win = ov2640_select_win(&width, &height); ++ priv->cfmt_code = V4L2_MBUS_FMT_UYVY8_2X8; ++ } ++ ++ mf->width = priv->win->width; ++ mf->height = priv->win->height; ++ mf->code = priv->cfmt_code; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ } ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov2640_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ } ++ ++ ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code); ++ ++ return ret; ++} ++ ++static int ov2640_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ /* ++ * select suitable win, but don't store it ++ */ ++ ov2640_select_win(&mf->width, &mf->height); ++ ++ mf->field = V4L2_FIELD_NONE; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ } ++ ++ return 0; ++} ++ ++static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov2640_codes)) ++ return -EINVAL; ++ ++ *code = ov2640_codes[index]; ++ return 0; ++} ++ ++static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ a->c.left = 0; ++ a->c.top = 0; ++ a->c.width = W_UXGA; ++ a->c.height = H_UXGA; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = W_UXGA; ++ a->bounds.height = H_UXGA; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov2640_video_probe(struct i2c_client *client) ++{ ++ struct ov2640_priv *priv = to_ov2640(client); ++ u8 pid, ver, midh, midl; ++ const char *devname; ++ int ret; ++ ++ ret = ov2640_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS); ++ pid = i2c_smbus_read_byte_data(client, PID); ++ ver = i2c_smbus_read_byte_data(client, VER); ++ midh = i2c_smbus_read_byte_data(client, MIDH); ++ midl = i2c_smbus_read_byte_data(client, MIDL); ++ ++ switch (VERSION(pid, ver)) { ++ case PID_OV2640: ++ devname = "ov2640"; ++ priv->model = V4L2_IDENT_OV2640; ++ break; ++ default: ++ dev_err(&client->dev, ++ "Product ID error %x:%x\n", pid, ver); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, ++ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", ++ devname, pid, ver, midh, midl); ++ ++ ret = v4l2_ctrl_handler_setup(&priv->hdl); ++ ++done: ++ ov2640_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov2640_ctrl_ops = { ++ .s_ctrl = ov2640_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { ++ .g_chip_ident = ov2640_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov2640_g_register, ++ .s_register = ov2640_s_register, ++#endif ++ .s_power = ov2640_s_power, ++}; ++ ++static int ov2640_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { ++ .s_stream = ov2640_s_stream, ++ .g_mbus_fmt = ov2640_g_fmt, ++ .s_mbus_fmt = ov2640_s_fmt, ++ .try_mbus_fmt = ov2640_try_fmt, ++ .cropcap = ov2640_cropcap, ++ .g_crop = ov2640_g_crop, ++ .enum_mbus_fmt = ov2640_enum_fmt, ++ .g_mbus_config = ov2640_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops ov2640_subdev_ops = { ++ .core = &ov2640_subdev_core_ops, ++ .video = &ov2640_subdev_video_ops, ++}; ++ ++/* ++ * i2c_driver functions ++ */ ++static int ov2640_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov2640_priv *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&adapter->dev, ++ "OV2640: Missing platform_data for driver\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_err(&adapter->dev, ++ "OV2640: I2C-Adapter doesn't support SMBUS\n"); ++ return -EIO; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct ov2640_priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&adapter->dev, ++ "Failed to allocate memory for private data!\n"); ++ return -ENOMEM; ++ } ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->hdl, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) ++ return priv->hdl.error; ++ ++ ret = ov2640_video_probe(client); ++ if (ret) ++ v4l2_ctrl_handler_free(&priv->hdl); ++ else ++ dev_info(&adapter->dev, "OV2640 Probed\n"); ++ ++ return ret; ++} ++ ++static int ov2640_remove(struct i2c_client *client) ++{ ++ struct ov2640_priv *priv = to_ov2640(client); ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ return 0; ++} ++ ++static const struct i2c_device_id ov2640_id[] = { ++ { "ov2640", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov2640_id); ++ ++static struct i2c_driver ov2640_i2c_driver = { ++ .driver = { ++ .name = "ov2640", ++ }, ++ .probe = ov2640_probe, ++ .remove = ov2640_remove, ++ .id_table = ov2640_id, ++}; ++ ++module_i2c_driver(ov2640_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor"); ++MODULE_AUTHOR("Alberto Panizzo"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/ov5642.c +new file mode 100644 +index 0000000..9d53309 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov5642.c +@@ -0,0 +1,1077 @@ ++/* ++ * Driver for OV5642 CMOS Image Sensor from Omnivision ++ * ++ * Copyright (C) 2011, Bastian Hecht ++ * ++ * Based on Sony IMX074 Camera Driver ++ * Copyright (C) 2010, Guennadi Liakhovetski ++ * ++ * Based on Omnivision OV7670 Camera Driver ++ * Copyright (C) 2006-7 Jonathan Corbet ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* OV5642 registers */ ++#define REG_CHIP_ID_HIGH 0x300a ++#define REG_CHIP_ID_LOW 0x300b ++ ++#define REG_WINDOW_START_X_HIGH 0x3800 ++#define REG_WINDOW_START_X_LOW 0x3801 ++#define REG_WINDOW_START_Y_HIGH 0x3802 ++#define REG_WINDOW_START_Y_LOW 0x3803 ++#define REG_WINDOW_WIDTH_HIGH 0x3804 ++#define REG_WINDOW_WIDTH_LOW 0x3805 ++#define REG_WINDOW_HEIGHT_HIGH 0x3806 ++#define REG_WINDOW_HEIGHT_LOW 0x3807 ++#define REG_OUT_WIDTH_HIGH 0x3808 ++#define REG_OUT_WIDTH_LOW 0x3809 ++#define REG_OUT_HEIGHT_HIGH 0x380a ++#define REG_OUT_HEIGHT_LOW 0x380b ++#define REG_OUT_TOTAL_WIDTH_HIGH 0x380c ++#define REG_OUT_TOTAL_WIDTH_LOW 0x380d ++#define REG_OUT_TOTAL_HEIGHT_HIGH 0x380e ++#define REG_OUT_TOTAL_HEIGHT_LOW 0x380f ++#define REG_OUTPUT_FORMAT 0x4300 ++#define REG_ISP_CTRL_01 0x5001 ++#define REG_AVG_WINDOW_END_X_HIGH 0x5682 ++#define REG_AVG_WINDOW_END_X_LOW 0x5683 ++#define REG_AVG_WINDOW_END_Y_HIGH 0x5686 ++#define REG_AVG_WINDOW_END_Y_LOW 0x5687 ++ ++/* active pixel array size */ ++#define OV5642_SENSOR_SIZE_X 2592 ++#define OV5642_SENSOR_SIZE_Y 1944 ++ ++/* ++ * About OV5642 resolution, cropping and binning: ++ * This sensor supports it all, at least in the feature description. ++ * Unfortunately, no combination of appropriate registers settings could make ++ * the chip work the intended way. As it works with predefined register lists, ++ * some undocumented registers are presumably changed there to achieve their ++ * goals. ++ * This driver currently only works for resolutions up to 720 lines with a ++ * 1:1 scale. Hopefully these restrictions will be removed in the future. ++ */ ++#define OV5642_MAX_WIDTH OV5642_SENSOR_SIZE_X ++#define OV5642_MAX_HEIGHT 720 ++ ++/* default sizes */ ++#define OV5642_DEFAULT_WIDTH 1280 ++#define OV5642_DEFAULT_HEIGHT OV5642_MAX_HEIGHT ++ ++/* minimum extra blanking */ ++#define BLANKING_EXTRA_WIDTH 500 ++#define BLANKING_EXTRA_HEIGHT 20 ++ ++/* ++ * the sensor's autoexposure is buggy when setting total_height low. ++ * It tries to expose longer than 1 frame period without taking care of it ++ * and this leads to weird output. So we set 1000 lines as minimum. ++ */ ++#define BLANKING_MIN_HEIGHT 1000 ++ ++struct regval_list { ++ u16 reg_num; ++ u8 value; ++}; ++ ++static struct regval_list ov5642_default_regs_init[] = { ++ { 0x3103, 0x93 }, ++ { 0x3008, 0x82 }, ++ { 0x3017, 0x7f }, ++ { 0x3018, 0xfc }, ++ { 0x3810, 0xc2 }, ++ { 0x3615, 0xf0 }, ++ { 0x3000, 0x0 }, ++ { 0x3001, 0x0 }, ++ { 0x3002, 0x0 }, ++ { 0x3003, 0x0 }, ++ { 0x3004, 0xff }, ++ { 0x3030, 0x2b }, ++ { 0x3011, 0x8 }, ++ { 0x3010, 0x10 }, ++ { 0x3604, 0x60 }, ++ { 0x3622, 0x60 }, ++ { 0x3621, 0x9 }, ++ { 0x3709, 0x0 }, ++ { 0x4000, 0x21 }, ++ { 0x401d, 0x22 }, ++ { 0x3600, 0x54 }, ++ { 0x3605, 0x4 }, ++ { 0x3606, 0x3f }, ++ { 0x3c01, 0x80 }, ++ { 0x300d, 0x22 }, ++ { 0x3623, 0x22 }, ++ { 0x5000, 0x4f }, ++ { 0x5020, 0x4 }, ++ { 0x5181, 0x79 }, ++ { 0x5182, 0x0 }, ++ { 0x5185, 0x22 }, ++ { 0x5197, 0x1 }, ++ { 0x5500, 0xa }, ++ { 0x5504, 0x0 }, ++ { 0x5505, 0x7f }, ++ { 0x5080, 0x8 }, ++ { 0x300e, 0x18 }, ++ { 0x4610, 0x0 }, ++ { 0x471d, 0x5 }, ++ { 0x4708, 0x6 }, ++ { 0x370c, 0xa0 }, ++ { 0x5687, 0x94 }, ++ { 0x501f, 0x0 }, ++ { 0x5000, 0x4f }, ++ { 0x5001, 0xcf }, ++ { 0x4300, 0x30 }, ++ { 0x4300, 0x30 }, ++ { 0x460b, 0x35 }, ++ { 0x471d, 0x0 }, ++ { 0x3002, 0xc }, ++ { 0x3002, 0x0 }, ++ { 0x4713, 0x3 }, ++ { 0x471c, 0x50 }, ++ { 0x4721, 0x2 }, ++ { 0x4402, 0x90 }, ++ { 0x460c, 0x22 }, ++ { 0x3815, 0x44 }, ++ { 0x3503, 0x7 }, ++ { 0x3501, 0x73 }, ++ { 0x3502, 0x80 }, ++ { 0x350b, 0x0 }, ++ { 0x3818, 0xc8 }, ++ { 0x3824, 0x11 }, ++ { 0x3a00, 0x78 }, ++ { 0x3a1a, 0x4 }, ++ { 0x3a13, 0x30 }, ++ { 0x3a18, 0x0 }, ++ { 0x3a19, 0x7c }, ++ { 0x3a08, 0x12 }, ++ { 0x3a09, 0xc0 }, ++ { 0x3a0a, 0xf }, ++ { 0x3a0b, 0xa0 }, ++ { 0x350c, 0x7 }, ++ { 0x350d, 0xd0 }, ++ { 0x3a0d, 0x8 }, ++ { 0x3a0e, 0x6 }, ++ { 0x3500, 0x0 }, ++ { 0x3501, 0x0 }, ++ { 0x3502, 0x0 }, ++ { 0x350a, 0x0 }, ++ { 0x350b, 0x0 }, ++ { 0x3503, 0x0 }, ++ { 0x3a0f, 0x3c }, ++ { 0x3a10, 0x32 }, ++ { 0x3a1b, 0x3c }, ++ { 0x3a1e, 0x32 }, ++ { 0x3a11, 0x80 }, ++ { 0x3a1f, 0x20 }, ++ { 0x3030, 0x2b }, ++ { 0x3a02, 0x0 }, ++ { 0x3a03, 0x7d }, ++ { 0x3a04, 0x0 }, ++ { 0x3a14, 0x0 }, ++ { 0x3a15, 0x7d }, ++ { 0x3a16, 0x0 }, ++ { 0x3a00, 0x78 }, ++ { 0x3a08, 0x9 }, ++ { 0x3a09, 0x60 }, ++ { 0x3a0a, 0x7 }, ++ { 0x3a0b, 0xd0 }, ++ { 0x3a0d, 0x10 }, ++ { 0x3a0e, 0xd }, ++ { 0x4407, 0x4 }, ++ { 0x5193, 0x70 }, ++ { 0x589b, 0x0 }, ++ { 0x589a, 0xc0 }, ++ { 0x401e, 0x20 }, ++ { 0x4001, 0x42 }, ++ { 0x401c, 0x6 }, ++ { 0x3825, 0xac }, ++ { 0x3827, 0xc }, ++ { 0x528a, 0x1 }, ++ { 0x528b, 0x4 }, ++ { 0x528c, 0x8 }, ++ { 0x528d, 0x10 }, ++ { 0x528e, 0x20 }, ++ { 0x528f, 0x28 }, ++ { 0x5290, 0x30 }, ++ { 0x5292, 0x0 }, ++ { 0x5293, 0x1 }, ++ { 0x5294, 0x0 }, ++ { 0x5295, 0x4 }, ++ { 0x5296, 0x0 }, ++ { 0x5297, 0x8 }, ++ { 0x5298, 0x0 }, ++ { 0x5299, 0x10 }, ++ { 0x529a, 0x0 }, ++ { 0x529b, 0x20 }, ++ { 0x529c, 0x0 }, ++ { 0x529d, 0x28 }, ++ { 0x529e, 0x0 }, ++ { 0x529f, 0x30 }, ++ { 0x5282, 0x0 }, ++ { 0x5300, 0x0 }, ++ { 0x5301, 0x20 }, ++ { 0x5302, 0x0 }, ++ { 0x5303, 0x7c }, ++ { 0x530c, 0x0 }, ++ { 0x530d, 0xc }, ++ { 0x530e, 0x20 }, ++ { 0x530f, 0x80 }, ++ { 0x5310, 0x20 }, ++ { 0x5311, 0x80 }, ++ { 0x5308, 0x20 }, ++ { 0x5309, 0x40 }, ++ { 0x5304, 0x0 }, ++ { 0x5305, 0x30 }, ++ { 0x5306, 0x0 }, ++ { 0x5307, 0x80 }, ++ { 0x5314, 0x8 }, ++ { 0x5315, 0x20 }, ++ { 0x5319, 0x30 }, ++ { 0x5316, 0x10 }, ++ { 0x5317, 0x0 }, ++ { 0x5318, 0x2 }, ++ { 0x5380, 0x1 }, ++ { 0x5381, 0x0 }, ++ { 0x5382, 0x0 }, ++ { 0x5383, 0x4e }, ++ { 0x5384, 0x0 }, ++ { 0x5385, 0xf }, ++ { 0x5386, 0x0 }, ++ { 0x5387, 0x0 }, ++ { 0x5388, 0x1 }, ++ { 0x5389, 0x15 }, ++ { 0x538a, 0x0 }, ++ { 0x538b, 0x31 }, ++ { 0x538c, 0x0 }, ++ { 0x538d, 0x0 }, ++ { 0x538e, 0x0 }, ++ { 0x538f, 0xf }, ++ { 0x5390, 0x0 }, ++ { 0x5391, 0xab }, ++ { 0x5392, 0x0 }, ++ { 0x5393, 0xa2 }, ++ { 0x5394, 0x8 }, ++ { 0x5480, 0x14 }, ++ { 0x5481, 0x21 }, ++ { 0x5482, 0x36 }, ++ { 0x5483, 0x57 }, ++ { 0x5484, 0x65 }, ++ { 0x5485, 0x71 }, ++ { 0x5486, 0x7d }, ++ { 0x5487, 0x87 }, ++ { 0x5488, 0x91 }, ++ { 0x5489, 0x9a }, ++ { 0x548a, 0xaa }, ++ { 0x548b, 0xb8 }, ++ { 0x548c, 0xcd }, ++ { 0x548d, 0xdd }, ++ { 0x548e, 0xea }, ++ { 0x548f, 0x1d }, ++ { 0x5490, 0x5 }, ++ { 0x5491, 0x0 }, ++ { 0x5492, 0x4 }, ++ { 0x5493, 0x20 }, ++ { 0x5494, 0x3 }, ++ { 0x5495, 0x60 }, ++ { 0x5496, 0x2 }, ++ { 0x5497, 0xb8 }, ++ { 0x5498, 0x2 }, ++ { 0x5499, 0x86 }, ++ { 0x549a, 0x2 }, ++ { 0x549b, 0x5b }, ++ { 0x549c, 0x2 }, ++ { 0x549d, 0x3b }, ++ { 0x549e, 0x2 }, ++ { 0x549f, 0x1c }, ++ { 0x54a0, 0x2 }, ++ { 0x54a1, 0x4 }, ++ { 0x54a2, 0x1 }, ++ { 0x54a3, 0xed }, ++ { 0x54a4, 0x1 }, ++ { 0x54a5, 0xc5 }, ++ { 0x54a6, 0x1 }, ++ { 0x54a7, 0xa5 }, ++ { 0x54a8, 0x1 }, ++ { 0x54a9, 0x6c }, ++ { 0x54aa, 0x1 }, ++ { 0x54ab, 0x41 }, ++ { 0x54ac, 0x1 }, ++ { 0x54ad, 0x20 }, ++ { 0x54ae, 0x0 }, ++ { 0x54af, 0x16 }, ++ { 0x54b0, 0x1 }, ++ { 0x54b1, 0x20 }, ++ { 0x54b2, 0x0 }, ++ { 0x54b3, 0x10 }, ++ { 0x54b4, 0x0 }, ++ { 0x54b5, 0xf0 }, ++ { 0x54b6, 0x0 }, ++ { 0x54b7, 0xdf }, ++ { 0x5402, 0x3f }, ++ { 0x5403, 0x0 }, ++ { 0x3406, 0x0 }, ++ { 0x5180, 0xff }, ++ { 0x5181, 0x52 }, ++ { 0x5182, 0x11 }, ++ { 0x5183, 0x14 }, ++ { 0x5184, 0x25 }, ++ { 0x5185, 0x24 }, ++ { 0x5186, 0x6 }, ++ { 0x5187, 0x8 }, ++ { 0x5188, 0x8 }, ++ { 0x5189, 0x7c }, ++ { 0x518a, 0x60 }, ++ { 0x518b, 0xb2 }, ++ { 0x518c, 0xb2 }, ++ { 0x518d, 0x44 }, ++ { 0x518e, 0x3d }, ++ { 0x518f, 0x58 }, ++ { 0x5190, 0x46 }, ++ { 0x5191, 0xf8 }, ++ { 0x5192, 0x4 }, ++ { 0x5193, 0x70 }, ++ { 0x5194, 0xf0 }, ++ { 0x5195, 0xf0 }, ++ { 0x5196, 0x3 }, ++ { 0x5197, 0x1 }, ++ { 0x5198, 0x4 }, ++ { 0x5199, 0x12 }, ++ { 0x519a, 0x4 }, ++ { 0x519b, 0x0 }, ++ { 0x519c, 0x6 }, ++ { 0x519d, 0x82 }, ++ { 0x519e, 0x0 }, ++ { 0x5025, 0x80 }, ++ { 0x3a0f, 0x38 }, ++ { 0x3a10, 0x30 }, ++ { 0x3a1b, 0x3a }, ++ { 0x3a1e, 0x2e }, ++ { 0x3a11, 0x60 }, ++ { 0x3a1f, 0x10 }, ++ { 0x5688, 0xa6 }, ++ { 0x5689, 0x6a }, ++ { 0x568a, 0xea }, ++ { 0x568b, 0xae }, ++ { 0x568c, 0xa6 }, ++ { 0x568d, 0x6a }, ++ { 0x568e, 0x62 }, ++ { 0x568f, 0x26 }, ++ { 0x5583, 0x40 }, ++ { 0x5584, 0x40 }, ++ { 0x5580, 0x2 }, ++ { 0x5000, 0xcf }, ++ { 0x5800, 0x27 }, ++ { 0x5801, 0x19 }, ++ { 0x5802, 0x12 }, ++ { 0x5803, 0xf }, ++ { 0x5804, 0x10 }, ++ { 0x5805, 0x15 }, ++ { 0x5806, 0x1e }, ++ { 0x5807, 0x2f }, ++ { 0x5808, 0x15 }, ++ { 0x5809, 0xd }, ++ { 0x580a, 0xa }, ++ { 0x580b, 0x9 }, ++ { 0x580c, 0xa }, ++ { 0x580d, 0xc }, ++ { 0x580e, 0x12 }, ++ { 0x580f, 0x19 }, ++ { 0x5810, 0xb }, ++ { 0x5811, 0x7 }, ++ { 0x5812, 0x4 }, ++ { 0x5813, 0x3 }, ++ { 0x5814, 0x3 }, ++ { 0x5815, 0x6 }, ++ { 0x5816, 0xa }, ++ { 0x5817, 0xf }, ++ { 0x5818, 0xa }, ++ { 0x5819, 0x5 }, ++ { 0x581a, 0x1 }, ++ { 0x581b, 0x0 }, ++ { 0x581c, 0x0 }, ++ { 0x581d, 0x3 }, ++ { 0x581e, 0x8 }, ++ { 0x581f, 0xc }, ++ { 0x5820, 0xa }, ++ { 0x5821, 0x5 }, ++ { 0x5822, 0x1 }, ++ { 0x5823, 0x0 }, ++ { 0x5824, 0x0 }, ++ { 0x5825, 0x3 }, ++ { 0x5826, 0x8 }, ++ { 0x5827, 0xc }, ++ { 0x5828, 0xe }, ++ { 0x5829, 0x8 }, ++ { 0x582a, 0x6 }, ++ { 0x582b, 0x4 }, ++ { 0x582c, 0x5 }, ++ { 0x582d, 0x7 }, ++ { 0x582e, 0xb }, ++ { 0x582f, 0x12 }, ++ { 0x5830, 0x18 }, ++ { 0x5831, 0x10 }, ++ { 0x5832, 0xc }, ++ { 0x5833, 0xa }, ++ { 0x5834, 0xb }, ++ { 0x5835, 0xe }, ++ { 0x5836, 0x15 }, ++ { 0x5837, 0x19 }, ++ { 0x5838, 0x32 }, ++ { 0x5839, 0x1f }, ++ { 0x583a, 0x18 }, ++ { 0x583b, 0x16 }, ++ { 0x583c, 0x17 }, ++ { 0x583d, 0x1e }, ++ { 0x583e, 0x26 }, ++ { 0x583f, 0x53 }, ++ { 0x5840, 0x10 }, ++ { 0x5841, 0xf }, ++ { 0x5842, 0xd }, ++ { 0x5843, 0xc }, ++ { 0x5844, 0xe }, ++ { 0x5845, 0x9 }, ++ { 0x5846, 0x11 }, ++ { 0x5847, 0x10 }, ++ { 0x5848, 0x10 }, ++ { 0x5849, 0x10 }, ++ { 0x584a, 0x10 }, ++ { 0x584b, 0xe }, ++ { 0x584c, 0x10 }, ++ { 0x584d, 0x10 }, ++ { 0x584e, 0x11 }, ++ { 0x584f, 0x10 }, ++ { 0x5850, 0xf }, ++ { 0x5851, 0xc }, ++ { 0x5852, 0xf }, ++ { 0x5853, 0x10 }, ++ { 0x5854, 0x10 }, ++ { 0x5855, 0xf }, ++ { 0x5856, 0xe }, ++ { 0x5857, 0xb }, ++ { 0x5858, 0x10 }, ++ { 0x5859, 0xd }, ++ { 0x585a, 0xd }, ++ { 0x585b, 0xc }, ++ { 0x585c, 0xc }, ++ { 0x585d, 0xc }, ++ { 0x585e, 0xb }, ++ { 0x585f, 0xc }, ++ { 0x5860, 0xc }, ++ { 0x5861, 0xc }, ++ { 0x5862, 0xd }, ++ { 0x5863, 0x8 }, ++ { 0x5864, 0x11 }, ++ { 0x5865, 0x18 }, ++ { 0x5866, 0x18 }, ++ { 0x5867, 0x19 }, ++ { 0x5868, 0x17 }, ++ { 0x5869, 0x19 }, ++ { 0x586a, 0x16 }, ++ { 0x586b, 0x13 }, ++ { 0x586c, 0x13 }, ++ { 0x586d, 0x12 }, ++ { 0x586e, 0x13 }, ++ { 0x586f, 0x16 }, ++ { 0x5870, 0x14 }, ++ { 0x5871, 0x12 }, ++ { 0x5872, 0x10 }, ++ { 0x5873, 0x11 }, ++ { 0x5874, 0x11 }, ++ { 0x5875, 0x16 }, ++ { 0x5876, 0x14 }, ++ { 0x5877, 0x11 }, ++ { 0x5878, 0x10 }, ++ { 0x5879, 0xf }, ++ { 0x587a, 0x10 }, ++ { 0x587b, 0x14 }, ++ { 0x587c, 0x13 }, ++ { 0x587d, 0x12 }, ++ { 0x587e, 0x11 }, ++ { 0x587f, 0x11 }, ++ { 0x5880, 0x12 }, ++ { 0x5881, 0x15 }, ++ { 0x5882, 0x14 }, ++ { 0x5883, 0x15 }, ++ { 0x5884, 0x15 }, ++ { 0x5885, 0x15 }, ++ { 0x5886, 0x13 }, ++ { 0x5887, 0x17 }, ++ { 0x3710, 0x10 }, ++ { 0x3632, 0x51 }, ++ { 0x3702, 0x10 }, ++ { 0x3703, 0xb2 }, ++ { 0x3704, 0x18 }, ++ { 0x370b, 0x40 }, ++ { 0x370d, 0x3 }, ++ { 0x3631, 0x1 }, ++ { 0x3632, 0x52 }, ++ { 0x3606, 0x24 }, ++ { 0x3620, 0x96 }, ++ { 0x5785, 0x7 }, ++ { 0x3a13, 0x30 }, ++ { 0x3600, 0x52 }, ++ { 0x3604, 0x48 }, ++ { 0x3606, 0x1b }, ++ { 0x370d, 0xb }, ++ { 0x370f, 0xc0 }, ++ { 0x3709, 0x1 }, ++ { 0x3823, 0x0 }, ++ { 0x5007, 0x0 }, ++ { 0x5009, 0x0 }, ++ { 0x5011, 0x0 }, ++ { 0x5013, 0x0 }, ++ { 0x519e, 0x0 }, ++ { 0x5086, 0x0 }, ++ { 0x5087, 0x0 }, ++ { 0x5088, 0x0 }, ++ { 0x5089, 0x0 }, ++ { 0x302b, 0x0 }, ++ { 0x3503, 0x7 }, ++ { 0x3011, 0x8 }, ++ { 0x350c, 0x2 }, ++ { 0x350d, 0xe4 }, ++ { 0x3621, 0xc9 }, ++ { 0x370a, 0x81 }, ++ { 0xffff, 0xff }, ++}; ++ ++static struct regval_list ov5642_default_regs_finalise[] = { ++ { 0x3810, 0xc2 }, ++ { 0x3818, 0xc9 }, ++ { 0x381c, 0x10 }, ++ { 0x381d, 0xa0 }, ++ { 0x381e, 0x5 }, ++ { 0x381f, 0xb0 }, ++ { 0x3820, 0x0 }, ++ { 0x3821, 0x0 }, ++ { 0x3824, 0x11 }, ++ { 0x3a08, 0x1b }, ++ { 0x3a09, 0xc0 }, ++ { 0x3a0a, 0x17 }, ++ { 0x3a0b, 0x20 }, ++ { 0x3a0d, 0x2 }, ++ { 0x3a0e, 0x1 }, ++ { 0x401c, 0x4 }, ++ { 0x5682, 0x5 }, ++ { 0x5683, 0x0 }, ++ { 0x5686, 0x2 }, ++ { 0x5687, 0xcc }, ++ { 0x5001, 0x4f }, ++ { 0x589b, 0x6 }, ++ { 0x589a, 0xc5 }, ++ { 0x3503, 0x0 }, ++ { 0x460c, 0x20 }, ++ { 0x460b, 0x37 }, ++ { 0x471c, 0xd0 }, ++ { 0x471d, 0x5 }, ++ { 0x3815, 0x1 }, ++ { 0x3818, 0xc1 }, ++ { 0x501f, 0x0 }, ++ { 0x5002, 0xe0 }, ++ { 0x4300, 0x32 }, /* UYVY */ ++ { 0x3002, 0x1c }, ++ { 0x4800, 0x14 }, ++ { 0x4801, 0xf }, ++ { 0x3007, 0x3b }, ++ { 0x300e, 0x4 }, ++ { 0x4803, 0x50 }, ++ { 0x3815, 0x1 }, ++ { 0x4713, 0x2 }, ++ { 0x4842, 0x1 }, ++ { 0x300f, 0xe }, ++ { 0x3003, 0x3 }, ++ { 0x3003, 0x1 }, ++ { 0xffff, 0xff }, ++}; ++ ++struct ov5642_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++struct ov5642 { ++ struct v4l2_subdev subdev; ++ const struct ov5642_datafmt *fmt; ++ struct v4l2_rect crop_rect; ++ ++ /* blanking information */ ++ int total_width; ++ int total_height; ++}; ++ ++static const struct ov5642_datafmt ov5642_colour_fmts[] = { ++ {V4L2_MBUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_JPEG}, ++}; ++ ++static struct ov5642 *to_ov5642(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov5642, subdev); ++} ++ ++/* Find a data format by a pixel code in an array */ ++static const struct ov5642_datafmt ++ *ov5642_find_datafmt(enum v4l2_mbus_pixelcode code) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ov5642_colour_fmts); i++) ++ if (ov5642_colour_fmts[i].code == code) ++ return ov5642_colour_fmts + i; ++ ++ return NULL; ++} ++ ++static int reg_read(struct i2c_client *client, u16 reg, u8 *val) ++{ ++ int ret; ++ /* We have 16-bit i2c addresses - care for endianess */ ++ unsigned char data[2] = { reg >> 8, reg & 0xff }; ++ ++ ret = i2c_master_send(client, data, 2); ++ if (ret < 2) { ++ dev_err(&client->dev, "%s: i2c read error, reg: %x\n", ++ __func__, reg); ++ return ret < 0 ? ret : -EIO; ++ } ++ ++ ret = i2c_master_recv(client, val, 1); ++ if (ret < 1) { ++ dev_err(&client->dev, "%s: i2c read error, reg: %x\n", ++ __func__, reg); ++ return ret < 0 ? ret : -EIO; ++ } ++ return 0; ++} ++ ++static int reg_write(struct i2c_client *client, u16 reg, u8 val) ++{ ++ int ret; ++ unsigned char data[3] = { reg >> 8, reg & 0xff, val }; ++ ++ ret = i2c_master_send(client, data, 3); ++ if (ret < 3) { ++ dev_err(&client->dev, "%s: i2c write error, reg: %x\n", ++ __func__, reg); ++ return ret < 0 ? ret : -EIO; ++ } ++ ++ return 0; ++} ++ ++/* ++ * convenience function to write 16 bit register values that are split up ++ * into two consecutive high and low parts ++ */ ++static int reg_write16(struct i2c_client *client, u16 reg, u16 val16) ++{ ++ int ret; ++ ++ ret = reg_write(client, reg, val16 >> 8); ++ if (ret) ++ return ret; ++ return reg_write(client, reg + 1, val16 & 0x00ff); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov5642_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val; ++ ++ if (reg->reg & ~0xffff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ ret = reg_read(client, reg->reg, &val); ++ if (!ret) ++ reg->val = (__u64)val; ++ ++ return ret; ++} ++ ++static int ov5642_set_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg & ~0xffff || reg->val & ~0xff) ++ return -EINVAL; ++ ++ return reg_write(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov5642_write_array(struct i2c_client *client, ++ struct regval_list *vals) ++{ ++ while (vals->reg_num != 0xffff || vals->value != 0xff) { ++ int ret = reg_write(client, vals->reg_num, vals->value); ++ if (ret < 0) ++ return ret; ++ vals++; ++ } ++ dev_dbg(&client->dev, "Register list loaded\n"); ++ return 0; ++} ++ ++static int ov5642_set_resolution(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ int width = priv->crop_rect.width; ++ int height = priv->crop_rect.height; ++ int total_width = priv->total_width; ++ int total_height = priv->total_height; ++ int start_x = (OV5642_SENSOR_SIZE_X - width) / 2; ++ int start_y = (OV5642_SENSOR_SIZE_Y - height) / 2; ++ int ret; ++ ++ /* ++ * This should set the starting point for cropping. ++ * Doesn't work so far. ++ */ ++ ret = reg_write16(client, REG_WINDOW_START_X_HIGH, start_x); ++ if (!ret) ++ ret = reg_write16(client, REG_WINDOW_START_Y_HIGH, start_y); ++ if (!ret) { ++ priv->crop_rect.left = start_x; ++ priv->crop_rect.top = start_y; ++ } ++ ++ if (!ret) ++ ret = reg_write16(client, REG_WINDOW_WIDTH_HIGH, width); ++ if (!ret) ++ ret = reg_write16(client, REG_WINDOW_HEIGHT_HIGH, height); ++ if (ret) ++ return ret; ++ priv->crop_rect.width = width; ++ priv->crop_rect.height = height; ++ ++ /* Set the output window size. Only 1:1 scale is supported so far. */ ++ ret = reg_write16(client, REG_OUT_WIDTH_HIGH, width); ++ if (!ret) ++ ret = reg_write16(client, REG_OUT_HEIGHT_HIGH, height); ++ ++ /* Total width = output size + blanking */ ++ if (!ret) ++ ret = reg_write16(client, REG_OUT_TOTAL_WIDTH_HIGH, total_width); ++ if (!ret) ++ ret = reg_write16(client, REG_OUT_TOTAL_HEIGHT_HIGH, total_height); ++ ++ /* Sets the window for AWB calculations */ ++ if (!ret) ++ ret = reg_write16(client, REG_AVG_WINDOW_END_X_HIGH, width); ++ if (!ret) ++ ret = reg_write16(client, REG_AVG_WINDOW_END_Y_HIGH, height); ++ ++ return ret; ++} ++ ++static int ov5642_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ const struct ov5642_datafmt *fmt = ov5642_find_datafmt(mf->code); ++ ++ mf->width = priv->crop_rect.width; ++ mf->height = priv->crop_rect.height; ++ ++ if (!fmt) { ++ mf->code = ov5642_colour_fmts[0].code; ++ mf->colorspace = ov5642_colour_fmts[0].colorspace; ++ } ++ ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov5642_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ ++ /* MIPI CSI could have changed the format, double-check */ ++ if (!ov5642_find_datafmt(mf->code)) ++ return -EINVAL; ++ ++ ov5642_try_fmt(sd, mf); ++ priv->fmt = ov5642_find_datafmt(mf->code); ++ ++ return 0; ++} ++ ++static int ov5642_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ ++ const struct ov5642_datafmt *fmt = priv->fmt; ++ ++ mf->code = fmt->code; ++ mf->colorspace = fmt->colorspace; ++ mf->width = priv->crop_rect.width; ++ mf->height = priv->crop_rect.height; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov5642_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov5642_colour_fmts)) ++ return -EINVAL; ++ ++ *code = ov5642_colour_fmts[index].code; ++ return 0; ++} ++ ++static int ov5642_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = V4L2_IDENT_OV5642; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++static int ov5642_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ struct v4l2_rect rect = a->c; ++ int ret; ++ ++ v4l_bound_align_image(&rect.width, 48, OV5642_MAX_WIDTH, 1, ++ &rect.height, 32, OV5642_MAX_HEIGHT, 1, 0); ++ ++ priv->crop_rect.width = rect.width; ++ priv->crop_rect.height = rect.height; ++ priv->total_width = rect.width + BLANKING_EXTRA_WIDTH; ++ priv->total_height = max_t(int, rect.height + ++ BLANKING_EXTRA_HEIGHT, ++ BLANKING_MIN_HEIGHT); ++ priv->crop_rect.width = rect.width; ++ priv->crop_rect.height = rect.height; ++ ++ ret = ov5642_write_array(client, ov5642_default_regs_init); ++ if (!ret) ++ ret = ov5642_set_resolution(sd); ++ if (!ret) ++ ret = ov5642_write_array(client, ov5642_default_regs_finalise); ++ ++ return ret; ++} ++ ++static int ov5642_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov5642 *priv = to_ov5642(client); ++ struct v4l2_rect *rect = &a->c; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ *rect = priv->crop_rect; ++ ++ return 0; ++} ++ ++static int ov5642_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = OV5642_MAX_WIDTH; ++ a->bounds.height = OV5642_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov5642_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ cfg->type = V4L2_MBUS_CSI2; ++ cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | ++ V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; ++ ++ return 0; ++} ++ ++static int ov5642_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!on) ++ return soc_camera_power_off(&client->dev, ssdd); ++ ++ ret = soc_camera_power_on(&client->dev, ssdd); ++ if (ret < 0) ++ return ret; ++ ++ ret = ov5642_write_array(client, ov5642_default_regs_init); ++ if (!ret) ++ ret = ov5642_set_resolution(sd); ++ if (!ret) ++ ret = ov5642_write_array(client, ov5642_default_regs_finalise); ++ ++ return ret; ++} ++ ++static struct v4l2_subdev_video_ops ov5642_subdev_video_ops = { ++ .s_mbus_fmt = ov5642_s_fmt, ++ .g_mbus_fmt = ov5642_g_fmt, ++ .try_mbus_fmt = ov5642_try_fmt, ++ .enum_mbus_fmt = ov5642_enum_fmt, ++ .s_crop = ov5642_s_crop, ++ .g_crop = ov5642_g_crop, ++ .cropcap = ov5642_cropcap, ++ .g_mbus_config = ov5642_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_core_ops ov5642_subdev_core_ops = { ++ .s_power = ov5642_s_power, ++ .g_chip_ident = ov5642_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov5642_get_register, ++ .s_register = ov5642_set_register, ++#endif ++}; ++ ++static struct v4l2_subdev_ops ov5642_subdev_ops = { ++ .core = &ov5642_subdev_core_ops, ++ .video = &ov5642_subdev_video_ops, ++}; ++ ++static int ov5642_video_probe(struct i2c_client *client) ++{ ++ struct v4l2_subdev *subdev = i2c_get_clientdata(client); ++ int ret; ++ u8 id_high, id_low; ++ u16 id; ++ ++ ret = ov5642_s_power(subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Read sensor Model ID */ ++ ret = reg_read(client, REG_CHIP_ID_HIGH, &id_high); ++ if (ret < 0) ++ goto done; ++ ++ id = id_high << 8; ++ ++ ret = reg_read(client, REG_CHIP_ID_LOW, &id_low); ++ if (ret < 0) ++ goto done; ++ ++ id |= id_low; ++ ++ dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); ++ ++ if (id != 0x5642) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ ret = 0; ++ ++done: ++ ov5642_s_power(subdev, 0); ++ return ret; ++} ++ ++static int ov5642_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov5642 *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "OV5642: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct ov5642), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); ++ ++ priv->fmt = &ov5642_colour_fmts[0]; ++ ++ priv->crop_rect.width = OV5642_DEFAULT_WIDTH; ++ priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; ++ priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; ++ priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; ++ priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; ++ priv->total_height = BLANKING_MIN_HEIGHT; ++ ++ return ov5642_video_probe(client); ++} ++ ++static int ov5642_remove(struct i2c_client *client) ++{ ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (ssdd->free_bus) ++ ssdd->free_bus(ssdd); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id ov5642_id[] = { ++ { "ov5642", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov5642_id); ++ ++static struct i2c_driver ov5642_i2c_driver = { ++ .driver = { ++ .name = "ov5642", ++ }, ++ .probe = ov5642_probe, ++ .remove = ov5642_remove, ++ .id_table = ov5642_id, ++}; ++ ++module_i2c_driver(ov5642_i2c_driver); ++ ++MODULE_DESCRIPTION("Omnivision OV5642 Camera driver"); ++MODULE_AUTHOR("Bastian Hecht "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/ov6650.c b/drivers/media/i2c/soc_camera/ov6650.c +new file mode 100644 +index 0000000..dbe4f56 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov6650.c +@@ -0,0 +1,1063 @@ ++/* ++ * V4L2 SoC Camera driver for OmniVision OV6650 Camera Sensor ++ * ++ * Copyright (C) 2010 Janusz Krzysztofik ++ * ++ * Based on OmniVision OV96xx Camera Driver ++ * Copyright (C) 2009 Marek Vasut ++ * ++ * Based on ov772x camera driver: ++ * Copyright (C) 2008 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * Based on ov7670 and soc_camera_platform driver, ++ * Copyright 2006-7 Jonathan Corbet ++ * Copyright (C) 2008 Magnus Damm ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * Hardware specific bits initialy based on former work by Matt Callow ++ * drivers/media/video/omap/sensor_ov6650.c ++ * Copyright (C) 2006 Matt Callow ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* Register definitions */ ++#define REG_GAIN 0x00 /* range 00 - 3F */ ++#define REG_BLUE 0x01 ++#define REG_RED 0x02 ++#define REG_SAT 0x03 /* [7:4] saturation [0:3] reserved */ ++#define REG_HUE 0x04 /* [7:6] rsrvd [5] hue en [4:0] hue */ ++ ++#define REG_BRT 0x06 ++ ++#define REG_PIDH 0x0a ++#define REG_PIDL 0x0b ++ ++#define REG_AECH 0x10 ++#define REG_CLKRC 0x11 /* Data Format and Internal Clock */ ++ /* [7:6] Input system clock (MHz)*/ ++ /* 00=8, 01=12, 10=16, 11=24 */ ++ /* [5:0]: Internal Clock Pre-Scaler */ ++#define REG_COMA 0x12 /* [7] Reset */ ++#define REG_COMB 0x13 ++#define REG_COMC 0x14 ++#define REG_COMD 0x15 ++#define REG_COML 0x16 ++#define REG_HSTRT 0x17 ++#define REG_HSTOP 0x18 ++#define REG_VSTRT 0x19 ++#define REG_VSTOP 0x1a ++#define REG_PSHFT 0x1b ++#define REG_MIDH 0x1c ++#define REG_MIDL 0x1d ++#define REG_HSYNS 0x1e ++#define REG_HSYNE 0x1f ++#define REG_COME 0x20 ++#define REG_YOFF 0x21 ++#define REG_UOFF 0x22 ++#define REG_VOFF 0x23 ++#define REG_AEW 0x24 ++#define REG_AEB 0x25 ++#define REG_COMF 0x26 ++#define REG_COMG 0x27 ++#define REG_COMH 0x28 ++#define REG_COMI 0x29 ++ ++#define REG_FRARL 0x2b ++#define REG_COMJ 0x2c ++#define REG_COMK 0x2d ++#define REG_AVGY 0x2e ++#define REG_REF0 0x2f ++#define REG_REF1 0x30 ++#define REG_REF2 0x31 ++#define REG_FRAJH 0x32 ++#define REG_FRAJL 0x33 ++#define REG_FACT 0x34 ++#define REG_L1AEC 0x35 ++#define REG_AVGU 0x36 ++#define REG_AVGV 0x37 ++ ++#define REG_SPCB 0x60 ++#define REG_SPCC 0x61 ++#define REG_GAM1 0x62 ++#define REG_GAM2 0x63 ++#define REG_GAM3 0x64 ++#define REG_SPCD 0x65 ++ ++#define REG_SPCE 0x68 ++#define REG_ADCL 0x69 ++ ++#define REG_RMCO 0x6c ++#define REG_GMCO 0x6d ++#define REG_BMCO 0x6e ++ ++ ++/* Register bits, values, etc. */ ++#define OV6650_PIDH 0x66 /* high byte of product ID number */ ++#define OV6650_PIDL 0x50 /* low byte of product ID number */ ++#define OV6650_MIDH 0x7F /* high byte of mfg ID */ ++#define OV6650_MIDL 0xA2 /* low byte of mfg ID */ ++ ++#define DEF_GAIN 0x00 ++#define DEF_BLUE 0x80 ++#define DEF_RED 0x80 ++ ++#define SAT_SHIFT 4 ++#define SAT_MASK (0xf << SAT_SHIFT) ++#define SET_SAT(x) (((x) << SAT_SHIFT) & SAT_MASK) ++ ++#define HUE_EN BIT(5) ++#define HUE_MASK 0x1f ++#define DEF_HUE 0x10 ++#define SET_HUE(x) (HUE_EN | ((x) & HUE_MASK)) ++ ++#define DEF_AECH 0x4D ++ ++#define CLKRC_6MHz 0x00 ++#define CLKRC_12MHz 0x40 ++#define CLKRC_16MHz 0x80 ++#define CLKRC_24MHz 0xc0 ++#define CLKRC_DIV_MASK 0x3f ++#define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1) ++ ++#define COMA_RESET BIT(7) ++#define COMA_QCIF BIT(5) ++#define COMA_RAW_RGB BIT(4) ++#define COMA_RGB BIT(3) ++#define COMA_BW BIT(2) ++#define COMA_WORD_SWAP BIT(1) ++#define COMA_BYTE_SWAP BIT(0) ++#define DEF_COMA 0x00 ++ ++#define COMB_FLIP_V BIT(7) ++#define COMB_FLIP_H BIT(5) ++#define COMB_BAND_FILTER BIT(4) ++#define COMB_AWB BIT(2) ++#define COMB_AGC BIT(1) ++#define COMB_AEC BIT(0) ++#define DEF_COMB 0x5f ++ ++#define COML_ONE_CHANNEL BIT(7) ++ ++#define DEF_HSTRT 0x24 ++#define DEF_HSTOP 0xd4 ++#define DEF_VSTRT 0x04 ++#define DEF_VSTOP 0x94 ++ ++#define COMF_HREF_LOW BIT(4) ++ ++#define COMJ_PCLK_RISING BIT(4) ++#define COMJ_VSYNC_HIGH BIT(0) ++ ++/* supported resolutions */ ++#define W_QCIF (DEF_HSTOP - DEF_HSTRT) ++#define W_CIF (W_QCIF << 1) ++#define H_QCIF (DEF_VSTOP - DEF_VSTRT) ++#define H_CIF (H_QCIF << 1) ++ ++#define FRAME_RATE_MAX 30 ++ ++ ++struct ov6650_reg { ++ u8 reg; ++ u8 val; ++}; ++ ++struct ov6650 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* exposure/autoexposure cluster */ ++ struct v4l2_ctrl *autoexposure; ++ struct v4l2_ctrl *exposure; ++ }; ++ struct { ++ /* gain/autogain cluster */ ++ struct v4l2_ctrl *autogain; ++ struct v4l2_ctrl *gain; ++ }; ++ struct { ++ /* blue/red/autowhitebalance cluster */ ++ struct v4l2_ctrl *autowb; ++ struct v4l2_ctrl *blue; ++ struct v4l2_ctrl *red; ++ }; ++ bool half_scale; /* scale down output by 2 */ ++ struct v4l2_rect rect; /* sensor cropping window */ ++ unsigned long pclk_limit; /* from host */ ++ unsigned long pclk_max; /* from resolution and format */ ++ struct v4l2_fract tpf; /* as requested with s_parm */ ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++ ++static enum v4l2_mbus_pixelcode ov6650_codes[] = { ++ V4L2_MBUS_FMT_YUYV8_2X8, ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_YVYU8_2X8, ++ V4L2_MBUS_FMT_VYUY8_2X8, ++ V4L2_MBUS_FMT_SBGGR8_1X8, ++ V4L2_MBUS_FMT_Y8_1X8, ++}; ++ ++/* read a register */ ++static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ int ret; ++ u8 data = reg; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 1, ++ .buf = &data, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ goto err; ++ ++ msg.flags = I2C_M_RD; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ goto err; ++ ++ *val = data; ++ return 0; ++ ++err: ++ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); ++ return ret; ++} ++ ++/* write a register */ ++static int ov6650_reg_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ int ret; ++ unsigned char data[2] = { reg, val }; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = data, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ udelay(100); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); ++ return ret; ++ } ++ return 0; ++} ++ ++ ++/* Read a register, alter its bits, write it back */ ++static int ov6650_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 mask) ++{ ++ u8 val; ++ int ret; ++ ++ ret = ov6650_reg_read(client, reg, &val); ++ if (ret) { ++ dev_err(&client->dev, ++ "[Read]-Modify-Write of register 0x%02x failed!\n", ++ reg); ++ return ret; ++ } ++ ++ val &= ~mask; ++ val |= set; ++ ++ ret = ov6650_reg_write(client, reg, val); ++ if (ret) ++ dev_err(&client->dev, ++ "Read-Modify-[Write] of register 0x%02x failed!\n", ++ reg); ++ ++ return ret; ++} ++ ++static struct ov6650 *to_ov6650(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct ov6650, subdev); ++} ++ ++/* Start/Stop streaming from the device */ ++static int ov6650_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++/* Get status of additional camera capabilities */ ++static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); ++ struct v4l2_subdev *sd = &priv->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ uint8_t reg, reg2; ++ int ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ ret = ov6650_reg_read(client, REG_GAIN, ®); ++ if (!ret) ++ priv->gain->val = reg; ++ return ret; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = ov6650_reg_read(client, REG_BLUE, ®); ++ if (!ret) ++ ret = ov6650_reg_read(client, REG_RED, ®2); ++ if (!ret) { ++ priv->blue->val = reg; ++ priv->red->val = reg2; ++ } ++ return ret; ++ case V4L2_CID_EXPOSURE_AUTO: ++ ret = ov6650_reg_read(client, REG_AECH, ®); ++ if (!ret) ++ priv->exposure->val = reg; ++ return ret; ++ } ++ return -EINVAL; ++} ++ ++/* Set status of additional camera capabilities */ ++static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl); ++ struct v4l2_subdev *sd = &priv->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ ret = ov6650_reg_rmw(client, REG_COMB, ++ ctrl->val ? COMB_AGC : 0, COMB_AGC); ++ if (!ret && !ctrl->val) ++ ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val); ++ return ret; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = ov6650_reg_rmw(client, REG_COMB, ++ ctrl->val ? COMB_AWB : 0, COMB_AWB); ++ if (!ret && !ctrl->val) { ++ ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val); ++ if (!ret) ++ ret = ov6650_reg_write(client, REG_RED, ++ priv->red->val); ++ } ++ return ret; ++ case V4L2_CID_SATURATION: ++ return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val), ++ SAT_MASK); ++ case V4L2_CID_HUE: ++ return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val), ++ HUE_MASK); ++ case V4L2_CID_BRIGHTNESS: ++ return ov6650_reg_write(client, REG_BRT, ctrl->val); ++ case V4L2_CID_EXPOSURE_AUTO: ++ ret = ov6650_reg_rmw(client, REG_COMB, ctrl->val == ++ V4L2_EXPOSURE_AUTO ? COMB_AEC : 0, COMB_AEC); ++ if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) ++ ret = ov6650_reg_write(client, REG_AECH, ++ priv->exposure->val); ++ return ret; ++ case V4L2_CID_GAMMA: ++ return ov6650_reg_write(client, REG_GAM1, ctrl->val); ++ case V4L2_CID_VFLIP: ++ return ov6650_reg_rmw(client, REG_COMB, ++ ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V); ++ case V4L2_CID_HFLIP: ++ return ov6650_reg_rmw(client, REG_COMB, ++ ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H); ++ } ++ ++ return -EINVAL; ++} ++ ++/* Get chip identification */ ++static int ov6650_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ id->ident = V4L2_IDENT_OV6650; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov6650_get_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val; ++ ++ if (reg->reg & ~0xff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ ret = ov6650_reg_read(client, reg->reg, &val); ++ if (!ret) ++ reg->val = (__u64)val; ++ ++ return ret; ++} ++ ++static int ov6650_set_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg & ~0xff || reg->val & ~0xff) ++ return -EINVAL; ++ ++ return ov6650_reg_write(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov6650_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->c = priv->rect; ++ ++ return 0; ++} ++ ++static int ov6650_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ struct v4l2_rect rect = a->c; ++ int ret; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ rect.left = ALIGN(rect.left, 2); ++ rect.width = ALIGN(rect.width, 2); ++ rect.top = ALIGN(rect.top, 2); ++ rect.height = ALIGN(rect.height, 2); ++ soc_camera_limit_side(&rect.left, &rect.width, ++ DEF_HSTRT << 1, 2, W_CIF); ++ soc_camera_limit_side(&rect.top, &rect.height, ++ DEF_VSTRT << 1, 2, H_CIF); ++ ++ ret = ov6650_reg_write(client, REG_HSTRT, rect.left >> 1); ++ if (!ret) { ++ priv->rect.left = rect.left; ++ ret = ov6650_reg_write(client, REG_HSTOP, ++ (rect.left + rect.width) >> 1); ++ } ++ if (!ret) { ++ priv->rect.width = rect.width; ++ ret = ov6650_reg_write(client, REG_VSTRT, rect.top >> 1); ++ } ++ if (!ret) { ++ priv->rect.top = rect.top; ++ ret = ov6650_reg_write(client, REG_VSTOP, ++ (rect.top + rect.height) >> 1); ++ } ++ if (!ret) ++ priv->rect.height = rect.height; ++ ++ return ret; ++} ++ ++static int ov6650_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ a->bounds.left = DEF_HSTRT << 1; ++ a->bounds.top = DEF_VSTRT << 1; ++ a->bounds.width = W_CIF; ++ a->bounds.height = H_CIF; ++ a->defrect = a->bounds; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov6650_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ ++ mf->width = priv->rect.width >> priv->half_scale; ++ mf->height = priv->rect.height >> priv->half_scale; ++ mf->code = priv->code; ++ mf->colorspace = priv->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static bool is_unscaled_ok(int width, int height, struct v4l2_rect *rect) ++{ ++ return width > rect->width >> 1 || height > rect->height >> 1; ++} ++ ++static u8 to_clkrc(struct v4l2_fract *timeperframe, ++ unsigned long pclk_limit, unsigned long pclk_max) ++{ ++ unsigned long pclk; ++ ++ if (timeperframe->numerator && timeperframe->denominator) ++ pclk = pclk_max * timeperframe->denominator / ++ (FRAME_RATE_MAX * timeperframe->numerator); ++ else ++ pclk = pclk_max; ++ ++ if (pclk_limit && pclk_limit < pclk) ++ pclk = pclk_limit; ++ ++ return (pclk_max - 1) / pclk; ++} ++ ++/* set the format we will capture in */ ++static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); ++ struct soc_camera_sense *sense = icd->sense; ++ struct ov6650 *priv = to_ov6650(client); ++ bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect); ++ struct v4l2_crop a = { ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .c = { ++ .left = priv->rect.left + (priv->rect.width >> 1) - ++ (mf->width >> (1 - half_scale)), ++ .top = priv->rect.top + (priv->rect.height >> 1) - ++ (mf->height >> (1 - half_scale)), ++ .width = mf->width << half_scale, ++ .height = mf->height << half_scale, ++ }, ++ }; ++ enum v4l2_mbus_pixelcode code = mf->code; ++ unsigned long mclk, pclk; ++ u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc; ++ int ret; ++ ++ /* select color matrix configuration for given color encoding */ ++ switch (code) { ++ case V4L2_MBUS_FMT_Y8_1X8: ++ dev_dbg(&client->dev, "pixel format GREY8_1X8\n"); ++ coma_mask |= COMA_RGB | COMA_WORD_SWAP | COMA_BYTE_SWAP; ++ coma_set |= COMA_BW; ++ break; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ dev_dbg(&client->dev, "pixel format YUYV8_2X8_LE\n"); ++ coma_mask |= COMA_RGB | COMA_BW | COMA_BYTE_SWAP; ++ coma_set |= COMA_WORD_SWAP; ++ break; ++ case V4L2_MBUS_FMT_YVYU8_2X8: ++ dev_dbg(&client->dev, "pixel format YVYU8_2X8_LE (untested)\n"); ++ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP | ++ COMA_BYTE_SWAP; ++ break; ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ dev_dbg(&client->dev, "pixel format YUYV8_2X8_BE\n"); ++ if (half_scale) { ++ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP; ++ coma_set |= COMA_BYTE_SWAP; ++ } else { ++ coma_mask |= COMA_RGB | COMA_BW; ++ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP; ++ } ++ break; ++ case V4L2_MBUS_FMT_VYUY8_2X8: ++ dev_dbg(&client->dev, "pixel format YVYU8_2X8_BE (untested)\n"); ++ if (half_scale) { ++ coma_mask |= COMA_RGB | COMA_BW; ++ coma_set |= COMA_BYTE_SWAP | COMA_WORD_SWAP; ++ } else { ++ coma_mask |= COMA_RGB | COMA_BW | COMA_WORD_SWAP; ++ coma_set |= COMA_BYTE_SWAP; ++ } ++ break; ++ case V4L2_MBUS_FMT_SBGGR8_1X8: ++ dev_dbg(&client->dev, "pixel format SBGGR8_1X8 (untested)\n"); ++ coma_mask |= COMA_BW | COMA_BYTE_SWAP | COMA_WORD_SWAP; ++ coma_set |= COMA_RAW_RGB | COMA_RGB; ++ break; ++ default: ++ dev_err(&client->dev, "Pixel format not handled: 0x%x\n", code); ++ return -EINVAL; ++ } ++ priv->code = code; ++ ++ if (code == V4L2_MBUS_FMT_Y8_1X8 || ++ code == V4L2_MBUS_FMT_SBGGR8_1X8) { ++ coml_mask = COML_ONE_CHANNEL; ++ coml_set = 0; ++ priv->pclk_max = 4000000; ++ } else { ++ coml_mask = 0; ++ coml_set = COML_ONE_CHANNEL; ++ priv->pclk_max = 8000000; ++ } ++ ++ if (code == V4L2_MBUS_FMT_SBGGR8_1X8) ++ priv->colorspace = V4L2_COLORSPACE_SRGB; ++ else if (code != 0) ++ priv->colorspace = V4L2_COLORSPACE_JPEG; ++ ++ if (half_scale) { ++ dev_dbg(&client->dev, "max resolution: QCIF\n"); ++ coma_set |= COMA_QCIF; ++ priv->pclk_max /= 2; ++ } else { ++ dev_dbg(&client->dev, "max resolution: CIF\n"); ++ coma_mask |= COMA_QCIF; ++ } ++ priv->half_scale = half_scale; ++ ++ if (sense) { ++ if (sense->master_clock == 8000000) { ++ dev_dbg(&client->dev, "8MHz input clock\n"); ++ clkrc = CLKRC_6MHz; ++ } else if (sense->master_clock == 12000000) { ++ dev_dbg(&client->dev, "12MHz input clock\n"); ++ clkrc = CLKRC_12MHz; ++ } else if (sense->master_clock == 16000000) { ++ dev_dbg(&client->dev, "16MHz input clock\n"); ++ clkrc = CLKRC_16MHz; ++ } else if (sense->master_clock == 24000000) { ++ dev_dbg(&client->dev, "24MHz input clock\n"); ++ clkrc = CLKRC_24MHz; ++ } else { ++ dev_err(&client->dev, ++ "unsupported input clock, check platform data\n"); ++ return -EINVAL; ++ } ++ mclk = sense->master_clock; ++ priv->pclk_limit = sense->pixel_clock_max; ++ } else { ++ clkrc = CLKRC_24MHz; ++ mclk = 24000000; ++ priv->pclk_limit = 0; ++ dev_dbg(&client->dev, "using default 24MHz input clock\n"); ++ } ++ ++ clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); ++ ++ pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); ++ dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", ++ mclk / pclk, 10 * mclk % pclk / pclk); ++ ++ ret = ov6650_s_crop(sd, &a); ++ if (!ret) ++ ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); ++ if (!ret) ++ ret = ov6650_reg_write(client, REG_CLKRC, clkrc); ++ if (!ret) ++ ret = ov6650_reg_rmw(client, REG_COML, coml_set, coml_mask); ++ ++ if (!ret) { ++ mf->colorspace = priv->colorspace; ++ mf->width = priv->rect.width >> half_scale; ++ mf->height = priv->rect.height >> half_scale; ++ } ++ ++ return ret; ++} ++ ++static int ov6650_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ ++ if (is_unscaled_ok(mf->width, mf->height, &priv->rect)) ++ v4l_bound_align_image(&mf->width, 2, W_CIF, 1, ++ &mf->height, 2, H_CIF, 1, 0); ++ ++ mf->field = V4L2_FIELD_NONE; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_Y10_1X10: ++ mf->code = V4L2_MBUS_FMT_Y8_1X8; ++ case V4L2_MBUS_FMT_Y8_1X8: ++ case V4L2_MBUS_FMT_YVYU8_2X8: ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ case V4L2_MBUS_FMT_VYUY8_2X8: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ break; ++ default: ++ mf->code = V4L2_MBUS_FMT_SBGGR8_1X8; ++ case V4L2_MBUS_FMT_SBGGR8_1X8: ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int ov6650_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov6650_codes)) ++ return -EINVAL; ++ ++ *code = ov6650_codes[index]; ++ return 0; ++} ++ ++static int ov6650_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(cp, 0, sizeof(*cp)); ++ cp->capability = V4L2_CAP_TIMEPERFRAME; ++ cp->timeperframe.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, ++ priv->pclk_limit, priv->pclk_max)); ++ cp->timeperframe.denominator = FRAME_RATE_MAX; ++ ++ dev_dbg(&client->dev, "Frame interval: %u/%u s\n", ++ cp->timeperframe.numerator, cp->timeperframe.denominator); ++ ++ return 0; ++} ++ ++static int ov6650_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov6650 *priv = to_ov6650(client); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ struct v4l2_fract *tpf = &cp->timeperframe; ++ int div, ret; ++ u8 clkrc; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if (cp->extendedmode != 0) ++ return -EINVAL; ++ ++ if (tpf->numerator == 0 || tpf->denominator == 0) ++ div = 1; /* Reset to full rate */ ++ else ++ div = (tpf->numerator * FRAME_RATE_MAX) / tpf->denominator; ++ ++ if (div == 0) ++ div = 1; ++ else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) ++ div = GET_CLKRC_DIV(CLKRC_DIV_MASK); ++ ++ /* ++ * Keep result to be used as tpf limit ++ * for subseqent clock divider calculations ++ */ ++ priv->tpf.numerator = div; ++ priv->tpf.denominator = FRAME_RATE_MAX; ++ ++ clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); ++ ++ ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); ++ if (!ret) { ++ tpf->numerator = GET_CLKRC_DIV(clkrc); ++ tpf->denominator = FRAME_RATE_MAX; ++ } ++ ++ return ret; ++} ++ ++/* Soft reset the camera. This has nothing to do with the RESET pin! */ ++static int ov6650_reset(struct i2c_client *client) ++{ ++ int ret; ++ ++ dev_dbg(&client->dev, "reset\n"); ++ ++ ret = ov6650_reg_rmw(client, REG_COMA, COMA_RESET, 0); ++ if (ret) ++ dev_err(&client->dev, ++ "An error occurred while entering soft reset!\n"); ++ ++ return ret; ++} ++ ++/* program default register values */ ++static int ov6650_prog_dflt(struct i2c_client *client) ++{ ++ int ret; ++ ++ dev_dbg(&client->dev, "initializing\n"); ++ ++ ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ ++ if (!ret) ++ ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); ++ ++ return ret; ++} ++ ++static int ov6650_video_probe(struct i2c_client *client) ++{ ++ struct ov6650 *priv = to_ov6650(client); ++ u8 pidh, pidl, midh, midl; ++ int ret; ++ ++ ret = ov6650_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ ret = ov6650_reg_read(client, REG_PIDH, &pidh); ++ if (!ret) ++ ret = ov6650_reg_read(client, REG_PIDL, &pidl); ++ if (!ret) ++ ret = ov6650_reg_read(client, REG_MIDH, &midh); ++ if (!ret) ++ ret = ov6650_reg_read(client, REG_MIDL, &midl); ++ ++ if (ret) ++ goto done; ++ ++ if ((pidh != OV6650_PIDH) || (pidl != OV6650_PIDL)) { ++ dev_err(&client->dev, "Product ID error 0x%02x:0x%02x\n", ++ pidh, pidl); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, ++ "ov6650 Product ID 0x%02x:0x%02x Manufacturer ID 0x%02x:0x%02x\n", ++ pidh, pidl, midh, midl); ++ ++ ret = ov6650_reset(client); ++ if (!ret) ++ ret = ov6650_prog_dflt(client); ++ if (!ret) ++ ret = v4l2_ctrl_handler_setup(&priv->hdl); ++ ++done: ++ ov6650_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov6550_ctrl_ops = { ++ .g_volatile_ctrl = ov6550_g_volatile_ctrl, ++ .s_ctrl = ov6550_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops ov6650_core_ops = { ++ .g_chip_ident = ov6650_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov6650_get_register, ++ .s_register = ov6650_set_register, ++#endif ++ .s_power = ov6650_s_power, ++}; ++ ++/* Request bus settings on camera side */ ++static int ov6650_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_MASTER | ++ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++/* Alter bus settings on camera side */ ++static int ov6650_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); ++ int ret; ++ ++ if (flags & V4L2_MBUS_PCLK_SAMPLE_RISING) ++ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); ++ else ++ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); ++ if (ret) ++ return ret; ++ ++ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) ++ ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); ++ else ++ ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); ++ if (ret) ++ return ret; ++ ++ if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ++ ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); ++ else ++ ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); ++ ++ return ret; ++} ++ ++static struct v4l2_subdev_video_ops ov6650_video_ops = { ++ .s_stream = ov6650_s_stream, ++ .g_mbus_fmt = ov6650_g_fmt, ++ .s_mbus_fmt = ov6650_s_fmt, ++ .try_mbus_fmt = ov6650_try_fmt, ++ .enum_mbus_fmt = ov6650_enum_fmt, ++ .cropcap = ov6650_cropcap, ++ .g_crop = ov6650_g_crop, ++ .s_crop = ov6650_s_crop, ++ .g_parm = ov6650_g_parm, ++ .s_parm = ov6650_s_parm, ++ .g_mbus_config = ov6650_g_mbus_config, ++ .s_mbus_config = ov6650_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops ov6650_subdev_ops = { ++ .core = &ov6650_core_ops, ++ .video = &ov6650_video_ops, ++}; ++ ++/* ++ * i2c_driver function ++ */ ++static int ov6650_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov6650 *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "Missing platform_data for driver\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&client->dev, ++ "Failed to allocate memory for private data!\n"); ++ return -ENOMEM; ++ } ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->hdl, 13); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ++ priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN); ++ priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ++ priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE); ++ priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 0xf, 1, 0x8); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80); ++ priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl, ++ &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, ++ V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); ++ priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH); ++ v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops, ++ V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); ++ ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) ++ return priv->hdl.error; ++ ++ v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); ++ v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); ++ v4l2_ctrl_auto_cluster(2, &priv->autoexposure, ++ V4L2_EXPOSURE_MANUAL, true); ++ ++ priv->rect.left = DEF_HSTRT << 1; ++ priv->rect.top = DEF_VSTRT << 1; ++ priv->rect.width = W_CIF; ++ priv->rect.height = H_CIF; ++ priv->half_scale = false; ++ priv->code = V4L2_MBUS_FMT_YUYV8_2X8; ++ priv->colorspace = V4L2_COLORSPACE_JPEG; ++ ++ ret = ov6650_video_probe(client); ++ if (ret) ++ v4l2_ctrl_handler_free(&priv->hdl); ++ ++ return ret; ++} ++ ++static int ov6650_remove(struct i2c_client *client) ++{ ++ struct ov6650 *priv = to_ov6650(client); ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ return 0; ++} ++ ++static const struct i2c_device_id ov6650_id[] = { ++ { "ov6650", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov6650_id); ++ ++static struct i2c_driver ov6650_i2c_driver = { ++ .driver = { ++ .name = "ov6650", ++ }, ++ .probe = ov6650_probe, ++ .remove = ov6650_remove, ++ .id_table = ov6650_id, ++}; ++ ++module_i2c_driver(ov6650_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); ++MODULE_AUTHOR("Janusz Krzysztofik "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/ov772x.c +new file mode 100644 +index 0000000..fbeb5b2 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov772x.c +@@ -0,0 +1,1129 @@ ++/* ++ * ov772x Camera Driver ++ * ++ * Copyright (C) 2008 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * Based on ov7670 and soc_camera_platform driver, ++ * ++ * Copyright 2006-7 Jonathan Corbet ++ * Copyright (C) 2008 Magnus Damm ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * register offset ++ */ ++#define GAIN 0x00 /* AGC - Gain control gain setting */ ++#define BLUE 0x01 /* AWB - Blue channel gain setting */ ++#define RED 0x02 /* AWB - Red channel gain setting */ ++#define GREEN 0x03 /* AWB - Green channel gain setting */ ++#define COM1 0x04 /* Common control 1 */ ++#define BAVG 0x05 /* U/B Average Level */ ++#define GAVG 0x06 /* Y/Gb Average Level */ ++#define RAVG 0x07 /* V/R Average Level */ ++#define AECH 0x08 /* Exposure Value - AEC MSBs */ ++#define COM2 0x09 /* Common control 2 */ ++#define PID 0x0A /* Product ID Number MSB */ ++#define VER 0x0B /* Product ID Number LSB */ ++#define COM3 0x0C /* Common control 3 */ ++#define COM4 0x0D /* Common control 4 */ ++#define COM5 0x0E /* Common control 5 */ ++#define COM6 0x0F /* Common control 6 */ ++#define AEC 0x10 /* Exposure Value */ ++#define CLKRC 0x11 /* Internal clock */ ++#define COM7 0x12 /* Common control 7 */ ++#define COM8 0x13 /* Common control 8 */ ++#define COM9 0x14 /* Common control 9 */ ++#define COM10 0x15 /* Common control 10 */ ++#define REG16 0x16 /* Register 16 */ ++#define HSTART 0x17 /* Horizontal sensor size */ ++#define HSIZE 0x18 /* Horizontal frame (HREF column) end high 8-bit */ ++#define VSTART 0x19 /* Vertical frame (row) start high 8-bit */ ++#define VSIZE 0x1A /* Vertical sensor size */ ++#define PSHFT 0x1B /* Data format - pixel delay select */ ++#define MIDH 0x1C /* Manufacturer ID byte - high */ ++#define MIDL 0x1D /* Manufacturer ID byte - low */ ++#define LAEC 0x1F /* Fine AEC value */ ++#define COM11 0x20 /* Common control 11 */ ++#define BDBASE 0x22 /* Banding filter Minimum AEC value */ ++#define DBSTEP 0x23 /* Banding filter Maximum Setp */ ++#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ ++#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ ++#define VPT 0x26 /* AGC/AEC Fast mode operating region */ ++#define REG28 0x28 /* Register 28 */ ++#define HOUTSIZE 0x29 /* Horizontal data output size MSBs */ ++#define EXHCH 0x2A /* Dummy pixel insert MSB */ ++#define EXHCL 0x2B /* Dummy pixel insert LSB */ ++#define VOUTSIZE 0x2C /* Vertical data output size MSBs */ ++#define ADVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ ++#define ADVFH 0x2E /* MSG of insert dummy lines in Vertical direction */ ++#define YAVE 0x2F /* Y/G Channel Average value */ ++#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance high level threshold */ ++#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance low level threshold */ ++#define HREF 0x32 /* Image start and size control */ ++#define DM_LNL 0x33 /* Dummy line low 8 bits */ ++#define DM_LNH 0x34 /* Dummy line high 8 bits */ ++#define ADOFF_B 0x35 /* AD offset compensation value for B channel */ ++#define ADOFF_R 0x36 /* AD offset compensation value for R channel */ ++#define ADOFF_GB 0x37 /* AD offset compensation value for Gb channel */ ++#define ADOFF_GR 0x38 /* AD offset compensation value for Gr channel */ ++#define OFF_B 0x39 /* Analog process B channel offset value */ ++#define OFF_R 0x3A /* Analog process R channel offset value */ ++#define OFF_GB 0x3B /* Analog process Gb channel offset value */ ++#define OFF_GR 0x3C /* Analog process Gr channel offset value */ ++#define COM12 0x3D /* Common control 12 */ ++#define COM13 0x3E /* Common control 13 */ ++#define COM14 0x3F /* Common control 14 */ ++#define COM15 0x40 /* Common control 15*/ ++#define COM16 0x41 /* Common control 16 */ ++#define TGT_B 0x42 /* BLC blue channel target value */ ++#define TGT_R 0x43 /* BLC red channel target value */ ++#define TGT_GB 0x44 /* BLC Gb channel target value */ ++#define TGT_GR 0x45 /* BLC Gr channel target value */ ++/* for ov7720 */ ++#define LCC0 0x46 /* Lens correction control 0 */ ++#define LCC1 0x47 /* Lens correction option 1 - X coordinate */ ++#define LCC2 0x48 /* Lens correction option 2 - Y coordinate */ ++#define LCC3 0x49 /* Lens correction option 3 */ ++#define LCC4 0x4A /* Lens correction option 4 - radius of the circular */ ++#define LCC5 0x4B /* Lens correction option 5 */ ++#define LCC6 0x4C /* Lens correction option 6 */ ++/* for ov7725 */ ++#define LC_CTR 0x46 /* Lens correction control */ ++#define LC_XC 0x47 /* X coordinate of lens correction center relative */ ++#define LC_YC 0x48 /* Y coordinate of lens correction center relative */ ++#define LC_COEF 0x49 /* Lens correction coefficient */ ++#define LC_RADI 0x4A /* Lens correction radius */ ++#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */ ++#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */ ++ ++#define FIXGAIN 0x4D /* Analog fix gain amplifer */ ++#define AREF0 0x4E /* Sensor reference control */ ++#define AREF1 0x4F /* Sensor reference current control */ ++#define AREF2 0x50 /* Analog reference control */ ++#define AREF3 0x51 /* ADC reference control */ ++#define AREF4 0x52 /* ADC reference control */ ++#define AREF5 0x53 /* ADC reference control */ ++#define AREF6 0x54 /* Analog reference control */ ++#define AREF7 0x55 /* Analog reference control */ ++#define UFIX 0x60 /* U channel fixed value output */ ++#define VFIX 0x61 /* V channel fixed value output */ ++#define AWBB_BLK 0x62 /* AWB option for advanced AWB */ ++#define AWB_CTRL0 0x63 /* AWB control byte 0 */ ++#define DSP_CTRL1 0x64 /* DSP control byte 1 */ ++#define DSP_CTRL2 0x65 /* DSP control byte 2 */ ++#define DSP_CTRL3 0x66 /* DSP control byte 3 */ ++#define DSP_CTRL4 0x67 /* DSP control byte 4 */ ++#define AWB_BIAS 0x68 /* AWB BLC level clip */ ++#define AWB_CTRL1 0x69 /* AWB control 1 */ ++#define AWB_CTRL2 0x6A /* AWB control 2 */ ++#define AWB_CTRL3 0x6B /* AWB control 3 */ ++#define AWB_CTRL4 0x6C /* AWB control 4 */ ++#define AWB_CTRL5 0x6D /* AWB control 5 */ ++#define AWB_CTRL6 0x6E /* AWB control 6 */ ++#define AWB_CTRL7 0x6F /* AWB control 7 */ ++#define AWB_CTRL8 0x70 /* AWB control 8 */ ++#define AWB_CTRL9 0x71 /* AWB control 9 */ ++#define AWB_CTRL10 0x72 /* AWB control 10 */ ++#define AWB_CTRL11 0x73 /* AWB control 11 */ ++#define AWB_CTRL12 0x74 /* AWB control 12 */ ++#define AWB_CTRL13 0x75 /* AWB control 13 */ ++#define AWB_CTRL14 0x76 /* AWB control 14 */ ++#define AWB_CTRL15 0x77 /* AWB control 15 */ ++#define AWB_CTRL16 0x78 /* AWB control 16 */ ++#define AWB_CTRL17 0x79 /* AWB control 17 */ ++#define AWB_CTRL18 0x7A /* AWB control 18 */ ++#define AWB_CTRL19 0x7B /* AWB control 19 */ ++#define AWB_CTRL20 0x7C /* AWB control 20 */ ++#define AWB_CTRL21 0x7D /* AWB control 21 */ ++#define GAM1 0x7E /* Gamma Curve 1st segment input end point */ ++#define GAM2 0x7F /* Gamma Curve 2nd segment input end point */ ++#define GAM3 0x80 /* Gamma Curve 3rd segment input end point */ ++#define GAM4 0x81 /* Gamma Curve 4th segment input end point */ ++#define GAM5 0x82 /* Gamma Curve 5th segment input end point */ ++#define GAM6 0x83 /* Gamma Curve 6th segment input end point */ ++#define GAM7 0x84 /* Gamma Curve 7th segment input end point */ ++#define GAM8 0x85 /* Gamma Curve 8th segment input end point */ ++#define GAM9 0x86 /* Gamma Curve 9th segment input end point */ ++#define GAM10 0x87 /* Gamma Curve 10th segment input end point */ ++#define GAM11 0x88 /* Gamma Curve 11th segment input end point */ ++#define GAM12 0x89 /* Gamma Curve 12th segment input end point */ ++#define GAM13 0x8A /* Gamma Curve 13th segment input end point */ ++#define GAM14 0x8B /* Gamma Curve 14th segment input end point */ ++#define GAM15 0x8C /* Gamma Curve 15th segment input end point */ ++#define SLOP 0x8D /* Gamma curve highest segment slope */ ++#define DNSTH 0x8E /* De-noise threshold */ ++#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ ++#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ ++#define DNSOFF 0x91 /* Auto De-noise threshold control */ ++#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ ++#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ ++#define MTX1 0x94 /* Matrix coefficient 1 */ ++#define MTX2 0x95 /* Matrix coefficient 2 */ ++#define MTX3 0x96 /* Matrix coefficient 3 */ ++#define MTX4 0x97 /* Matrix coefficient 4 */ ++#define MTX5 0x98 /* Matrix coefficient 5 */ ++#define MTX6 0x99 /* Matrix coefficient 6 */ ++#define MTX_CTRL 0x9A /* Matrix control */ ++#define BRIGHT 0x9B /* Brightness control */ ++#define CNTRST 0x9C /* Contrast contrast */ ++#define CNTRST_CTRL 0x9D /* Contrast contrast center */ ++#define UVAD_J0 0x9E /* Auto UV adjust contrast 0 */ ++#define UVAD_J1 0x9F /* Auto UV adjust contrast 1 */ ++#define SCAL0 0xA0 /* Scaling control 0 */ ++#define SCAL1 0xA1 /* Scaling control 1 */ ++#define SCAL2 0xA2 /* Scaling control 2 */ ++#define FIFODLYM 0xA3 /* FIFO manual mode delay control */ ++#define FIFODLYA 0xA4 /* FIFO auto mode delay control */ ++#define SDE 0xA6 /* Special digital effect control */ ++#define USAT 0xA7 /* U component saturation control */ ++#define VSAT 0xA8 /* V component saturation control */ ++/* for ov7720 */ ++#define HUE0 0xA9 /* Hue control 0 */ ++#define HUE1 0xAA /* Hue control 1 */ ++/* for ov7725 */ ++#define HUECOS 0xA9 /* Cosine value */ ++#define HUESIN 0xAA /* Sine value */ ++ ++#define SIGN 0xAB /* Sign bit for Hue and contrast */ ++#define DSPAUTO 0xAC /* DSP auto function ON/OFF control */ ++ ++/* ++ * register detail ++ */ ++ ++/* COM2 */ ++#define SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ ++ /* Output drive capability */ ++#define OCAP_1x 0x00 /* 1x */ ++#define OCAP_2x 0x01 /* 2x */ ++#define OCAP_3x 0x02 /* 3x */ ++#define OCAP_4x 0x03 /* 4x */ ++ ++/* COM3 */ ++#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) ++#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) ++ ++#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ ++#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ ++#define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ ++#define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ ++#define SWAP_ML 0x08 /* Swap output MSB/LSB */ ++ /* Tri-state option for output clock */ ++#define NOTRI_CLOCK 0x04 /* 0: Tri-state at this period */ ++ /* 1: No tri-state at this period */ ++ /* Tri-state option for output data */ ++#define NOTRI_DATA 0x02 /* 0: Tri-state at this period */ ++ /* 1: No tri-state at this period */ ++#define SCOLOR_TEST 0x01 /* Sensor color bar test pattern */ ++ ++/* COM4 */ ++ /* PLL frequency control */ ++#define PLL_BYPASS 0x00 /* 00: Bypass PLL */ ++#define PLL_4x 0x40 /* 01: PLL 4x */ ++#define PLL_6x 0x80 /* 10: PLL 6x */ ++#define PLL_8x 0xc0 /* 11: PLL 8x */ ++ /* AEC evaluate window */ ++#define AEC_FULL 0x00 /* 00: Full window */ ++#define AEC_1p2 0x10 /* 01: 1/2 window */ ++#define AEC_1p4 0x20 /* 10: 1/4 window */ ++#define AEC_2p3 0x30 /* 11: Low 2/3 window */ ++ ++/* COM5 */ ++#define AFR_ON_OFF 0x80 /* Auto frame rate control ON/OFF selection */ ++#define AFR_SPPED 0x40 /* Auto frame rate control speed selection */ ++ /* Auto frame rate max rate control */ ++#define AFR_NO_RATE 0x00 /* No reduction of frame rate */ ++#define AFR_1p2 0x10 /* Max reduction to 1/2 frame rate */ ++#define AFR_1p4 0x20 /* Max reduction to 1/4 frame rate */ ++#define AFR_1p8 0x30 /* Max reduction to 1/8 frame rate */ ++ /* Auto frame rate active point control */ ++#define AF_2x 0x00 /* Add frame when AGC reaches 2x gain */ ++#define AF_4x 0x04 /* Add frame when AGC reaches 4x gain */ ++#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */ ++#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */ ++ /* AEC max step control */ ++#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */ ++ /* 1 : No limit to AEC increase step */ ++ ++/* COM7 */ ++ /* SCCB Register Reset */ ++#define SCCB_RESET 0x80 /* 0 : No change */ ++ /* 1 : Resets all registers to default */ ++ /* Resolution selection */ ++#define SLCT_MASK 0x40 /* Mask of VGA or QVGA */ ++#define SLCT_VGA 0x00 /* 0 : VGA */ ++#define SLCT_QVGA 0x40 /* 1 : QVGA */ ++#define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ ++#define SENSOR_RAW 0x10 /* Sensor RAW */ ++ /* RGB output format control */ ++#define FMT_MASK 0x0c /* Mask of color format */ ++#define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ ++#define FMT_RGB565 0x04 /* 01 : RGB 565 */ ++#define FMT_RGB555 0x08 /* 10 : RGB 555 */ ++#define FMT_RGB444 0x0c /* 11 : RGB 444 */ ++ /* Output format control */ ++#define OFMT_MASK 0x03 /* Mask of output format */ ++#define OFMT_YUV 0x00 /* 00 : YUV */ ++#define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ ++#define OFMT_RGB 0x02 /* 10 : RGB */ ++#define OFMT_BRAW 0x03 /* 11 : Bayer RAW */ ++ ++/* COM8 */ ++#define FAST_ALGO 0x80 /* Enable fast AGC/AEC algorithm */ ++ /* AEC Setp size limit */ ++#define UNLMT_STEP 0x40 /* 0 : Step size is limited */ ++ /* 1 : Unlimited step size */ ++#define BNDF_ON_OFF 0x20 /* Banding filter ON/OFF */ ++#define AEC_BND 0x10 /* Enable AEC below banding value */ ++#define AEC_ON_OFF 0x08 /* Fine AEC ON/OFF control */ ++#define AGC_ON 0x04 /* AGC Enable */ ++#define AWB_ON 0x02 /* AWB Enable */ ++#define AEC_ON 0x01 /* AEC Enable */ ++ ++/* COM9 */ ++#define BASE_AECAGC 0x80 /* Histogram or average based AEC/AGC */ ++ /* Automatic gain ceiling - maximum AGC value */ ++#define GAIN_2x 0x00 /* 000 : 2x */ ++#define GAIN_4x 0x10 /* 001 : 4x */ ++#define GAIN_8x 0x20 /* 010 : 8x */ ++#define GAIN_16x 0x30 /* 011 : 16x */ ++#define GAIN_32x 0x40 /* 100 : 32x */ ++#define GAIN_64x 0x50 /* 101 : 64x */ ++#define GAIN_128x 0x60 /* 110 : 128x */ ++#define DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */ ++#define DROP_HREF 0x02 /* Drop HREF output of corrupt frame */ ++ ++/* COM11 */ ++#define SGLF_ON_OFF 0x02 /* Single frame ON/OFF selection */ ++#define SGLF_TRIG 0x01 /* Single frame transfer trigger */ ++ ++/* HREF */ ++#define HREF_VSTART_SHIFT 6 /* VSTART LSB */ ++#define HREF_HSTART_SHIFT 4 /* HSTART 2 LSBs */ ++#define HREF_VSIZE_SHIFT 2 /* VSIZE LSB */ ++#define HREF_HSIZE_SHIFT 0 /* HSIZE 2 LSBs */ ++ ++/* EXHCH */ ++#define EXHCH_VSIZE_SHIFT 2 /* VOUTSIZE LSB */ ++#define EXHCH_HSIZE_SHIFT 0 /* HOUTSIZE 2 LSBs */ ++ ++/* DSP_CTRL1 */ ++#define FIFO_ON 0x80 /* FIFO enable/disable selection */ ++#define UV_ON_OFF 0x40 /* UV adjust function ON/OFF selection */ ++#define YUV444_2_422 0x20 /* YUV444 to 422 UV channel option selection */ ++#define CLR_MTRX_ON_OFF 0x10 /* Color matrix ON/OFF selection */ ++#define INTPLT_ON_OFF 0x08 /* Interpolation ON/OFF selection */ ++#define GMM_ON_OFF 0x04 /* Gamma function ON/OFF selection */ ++#define AUTO_BLK_ON_OFF 0x02 /* Black defect auto correction ON/OFF */ ++#define AUTO_WHT_ON_OFF 0x01 /* White define auto correction ON/OFF */ ++ ++/* DSP_CTRL3 */ ++#define UV_MASK 0x80 /* UV output sequence option */ ++#define UV_ON 0x80 /* ON */ ++#define UV_OFF 0x00 /* OFF */ ++#define CBAR_MASK 0x20 /* DSP Color bar mask */ ++#define CBAR_ON 0x20 /* ON */ ++#define CBAR_OFF 0x00 /* OFF */ ++ ++/* DSP_CTRL4 */ ++#define DSP_OFMT_YUV 0x00 ++#define DSP_OFMT_RGB 0x00 ++#define DSP_OFMT_RAW8 0x02 ++#define DSP_OFMT_RAW10 0x03 ++ ++/* DSPAUTO (DSP Auto Function ON/OFF Control) */ ++#define AWB_ACTRL 0x80 /* AWB auto threshold control */ ++#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ ++#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ ++#define UV_ACTRL 0x10 /* UV adjust auto slope control */ ++#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ ++#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ ++ ++#define VGA_WIDTH 640 ++#define VGA_HEIGHT 480 ++#define QVGA_WIDTH 320 ++#define QVGA_HEIGHT 240 ++#define OV772X_MAX_WIDTH VGA_WIDTH ++#define OV772X_MAX_HEIGHT VGA_HEIGHT ++ ++/* ++ * ID ++ */ ++#define OV7720 0x7720 ++#define OV7725 0x7721 ++#define VERSION(pid, ver) ((pid<<8)|(ver&0xFF)) ++ ++/* ++ * struct ++ */ ++ ++struct ov772x_color_format { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++ u8 dsp3; ++ u8 dsp4; ++ u8 com3; ++ u8 com7; ++}; ++ ++struct ov772x_win_size { ++ char *name; ++ unsigned char com7_bit; ++ struct v4l2_rect rect; ++}; ++ ++struct ov772x_priv { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct ov772x_camera_info *info; ++ const struct ov772x_color_format *cfmt; ++ const struct ov772x_win_size *win; ++ int model; ++ unsigned short flag_vflip:1; ++ unsigned short flag_hflip:1; ++ /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ ++ unsigned short band_filter; ++}; ++ ++/* ++ * supported color format list ++ */ ++static const struct ov772x_color_format ov772x_cfmts[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = SWAP_YUV, ++ .com7 = OFMT_YUV, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .dsp3 = UV_ON, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = SWAP_YUV, ++ .com7 = OFMT_YUV, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = 0x0, ++ .com7 = OFMT_YUV, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = SWAP_RGB, ++ .com7 = FMT_RGB555 | OFMT_RGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = 0x0, ++ .com7 = FMT_RGB555 | OFMT_RGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = SWAP_RGB, ++ .com7 = FMT_RGB565 | OFMT_RGB, ++ }, ++ { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_YUV, ++ .com3 = 0x0, ++ .com7 = FMT_RGB565 | OFMT_RGB, ++ }, ++ { ++ /* Setting DSP4 to DSP_OFMT_RAW8 still gives 10-bit output, ++ * regardless of the COM7 value. We can thus only support 10-bit ++ * Bayer until someone figures it out. ++ */ ++ .code = V4L2_MBUS_FMT_SBGGR10_1X10, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .dsp3 = 0x0, ++ .dsp4 = DSP_OFMT_RAW10, ++ .com3 = 0x0, ++ .com7 = SENSOR_RAW | OFMT_BRAW, ++ }, ++}; ++ ++ ++/* ++ * window size list ++ */ ++ ++static const struct ov772x_win_size ov772x_win_sizes[] = { ++ { ++ .name = "VGA", ++ .com7_bit = SLCT_VGA, ++ .rect = { ++ .left = 140, ++ .top = 14, ++ .width = VGA_WIDTH, ++ .height = VGA_HEIGHT, ++ }, ++ }, { ++ .name = "QVGA", ++ .com7_bit = SLCT_QVGA, ++ .rect = { ++ .left = 252, ++ .top = 6, ++ .width = QVGA_WIDTH, ++ .height = QVGA_HEIGHT, ++ }, ++ }, ++}; ++ ++/* ++ * general function ++ */ ++ ++static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ov772x_priv, subdev); ++} ++ ++static inline int ov772x_read(struct i2c_client *client, u8 addr) ++{ ++ return i2c_smbus_read_byte_data(client, addr); ++} ++ ++static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) ++{ ++ return i2c_smbus_write_byte_data(client, addr, value); ++} ++ ++static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, ++ u8 set) ++{ ++ s32 val = ov772x_read(client, command); ++ if (val < 0) ++ return val; ++ ++ val &= ~mask; ++ val |= set & mask; ++ ++ return ov772x_write(client, command, val); ++} ++ ++static int ov772x_reset(struct i2c_client *client) ++{ ++ int ret; ++ ++ ret = ov772x_write(client, COM7, SCCB_RESET); ++ if (ret < 0) ++ return ret; ++ ++ msleep(1); ++ ++ return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); ++} ++ ++/* ++ * soc_camera_ops function ++ */ ++ ++static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov772x_priv *priv = to_ov772x(sd); ++ ++ if (!enable) { ++ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); ++ return 0; ++ } ++ ++ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); ++ ++ dev_dbg(&client->dev, "format %d, win %s\n", ++ priv->cfmt->code, priv->win->name); ++ ++ return 0; ++} ++ ++static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov772x_priv *priv = container_of(ctrl->handler, ++ struct ov772x_priv, hdl); ++ struct v4l2_subdev *sd = &priv->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret = 0; ++ u8 val; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ val = ctrl->val ? VFLIP_IMG : 0x00; ++ priv->flag_vflip = ctrl->val; ++ if (priv->info->flags & OV772X_FLAG_VFLIP) ++ val ^= VFLIP_IMG; ++ return ov772x_mask_set(client, COM3, VFLIP_IMG, val); ++ case V4L2_CID_HFLIP: ++ val = ctrl->val ? HFLIP_IMG : 0x00; ++ priv->flag_hflip = ctrl->val; ++ if (priv->info->flags & OV772X_FLAG_HFLIP) ++ val ^= HFLIP_IMG; ++ return ov772x_mask_set(client, COM3, HFLIP_IMG, val); ++ case V4L2_CID_BAND_STOP_FILTER: ++ if (!ctrl->val) { ++ /* Switch the filter off, it is on now */ ++ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); ++ if (!ret) ++ ret = ov772x_mask_set(client, COM8, ++ BNDF_ON_OFF, 0); ++ } else { ++ /* Switch the filter on, set AEC low limit */ ++ val = 256 - ctrl->val; ++ ret = ov772x_mask_set(client, COM8, ++ BNDF_ON_OFF, BNDF_ON_OFF); ++ if (!ret) ++ ret = ov772x_mask_set(client, BDBASE, ++ 0xff, val); ++ } ++ if (!ret) ++ priv->band_filter = ctrl->val; ++ return ret; ++ } ++ ++ return -EINVAL; ++} ++ ++static int ov772x_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct ov772x_priv *priv = to_ov772x(sd); ++ ++ id->ident = priv->model; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov772x_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ reg->size = 1; ++ if (reg->reg > 0xff) ++ return -EINVAL; ++ ++ ret = ov772x_read(client, reg->reg); ++ if (ret < 0) ++ return ret; ++ ++ reg->val = (__u64)ret; ++ ++ return 0; ++} ++ ++static int ov772x_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg > 0xff || ++ reg->val > 0xff) ++ return -EINVAL; ++ ++ return ov772x_write(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov772x_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) ++{ ++ const struct ov772x_win_size *win = &ov772x_win_sizes[0]; ++ u32 best_diff = UINT_MAX; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ov772x_win_sizes); ++i) { ++ u32 diff = abs(width - ov772x_win_sizes[i].rect.width) ++ + abs(height - ov772x_win_sizes[i].rect.height); ++ if (diff < best_diff) { ++ best_diff = diff; ++ win = &ov772x_win_sizes[i]; ++ } ++ } ++ ++ return win; ++} ++ ++static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, ++ const struct ov772x_color_format **cfmt, ++ const struct ov772x_win_size **win) ++{ ++ unsigned int i; ++ ++ /* Select a format. */ ++ *cfmt = &ov772x_cfmts[0]; ++ ++ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { ++ if (mf->code == ov772x_cfmts[i].code) { ++ *cfmt = &ov772x_cfmts[i]; ++ break; ++ } ++ } ++ ++ /* Select a window size. */ ++ *win = ov772x_select_win(mf->width, mf->height); ++} ++ ++static int ov772x_set_params(struct ov772x_priv *priv, ++ const struct ov772x_color_format *cfmt, ++ const struct ov772x_win_size *win) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); ++ int ret; ++ u8 val; ++ ++ /* ++ * reset hardware ++ */ ++ ov772x_reset(client); ++ ++ /* ++ * Edge Ctrl ++ */ ++ if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { ++ ++ /* ++ * Manual Edge Control Mode ++ * ++ * Edge auto strength bit is set by default. ++ * Remove it when manual mode. ++ */ ++ ++ ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ ret = ov772x_mask_set(client, ++ EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, ++ priv->info->edgectrl.threshold); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ ret = ov772x_mask_set(client, ++ EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, ++ priv->info->edgectrl.strength); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { ++ /* ++ * Auto Edge Control Mode ++ * ++ * set upper and lower limit ++ */ ++ ret = ov772x_mask_set(client, ++ EDGE_UPPER, OV772X_EDGE_UPPER_MASK, ++ priv->info->edgectrl.upper); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ ret = ov772x_mask_set(client, ++ EDGE_LOWER, OV772X_EDGE_LOWER_MASK, ++ priv->info->edgectrl.lower); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ } ++ ++ /* Format and window size */ ++ ret = ov772x_write(client, HSTART, win->rect.left >> 2); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, HSIZE, win->rect.width >> 2); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, VSTART, win->rect.top >> 1); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, VSIZE, win->rect.height >> 1); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, HREF, ++ ((win->rect.top & 1) << HREF_VSTART_SHIFT) | ++ ((win->rect.left & 3) << HREF_HSTART_SHIFT) | ++ ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | ++ ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ret = ov772x_write(client, EXHCH, ++ ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | ++ ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ /* ++ * set DSP_CTRL3 ++ */ ++ val = cfmt->dsp3; ++ if (val) { ++ ret = ov772x_mask_set(client, ++ DSP_CTRL3, UV_MASK, val); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ } ++ ++ /* DSP_CTRL4: AEC reference point and DSP output format. */ ++ if (cfmt->dsp4) { ++ ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ } ++ ++ /* ++ * set COM3 ++ */ ++ val = cfmt->com3; ++ if (priv->info->flags & OV772X_FLAG_VFLIP) ++ val |= VFLIP_IMG; ++ if (priv->info->flags & OV772X_FLAG_HFLIP) ++ val |= HFLIP_IMG; ++ if (priv->flag_vflip) ++ val ^= VFLIP_IMG; ++ if (priv->flag_hflip) ++ val ^= HFLIP_IMG; ++ ++ ret = ov772x_mask_set(client, ++ COM3, SWAP_MASK | IMG_MASK, val); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ /* COM7: Sensor resolution and output format control. */ ++ ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ ++ /* ++ * set COM8 ++ */ ++ if (priv->band_filter) { ++ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); ++ if (!ret) ++ ret = ov772x_mask_set(client, BDBASE, ++ 0xff, 256 - priv->band_filter); ++ if (ret < 0) ++ goto ov772x_set_fmt_error; ++ } ++ ++ return ret; ++ ++ov772x_set_fmt_error: ++ ++ ov772x_reset(client); ++ ++ return ret; ++} ++ ++static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ a->c.left = 0; ++ a->c.top = 0; ++ a->c.width = VGA_WIDTH; ++ a->c.height = VGA_HEIGHT; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = OV772X_MAX_WIDTH; ++ a->bounds.height = OV772X_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov772x_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct ov772x_priv *priv = to_ov772x(sd); ++ ++ mf->width = priv->win->rect.width; ++ mf->height = priv->win->rect.height; ++ mf->code = priv->cfmt->code; ++ mf->colorspace = priv->cfmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) ++{ ++ struct ov772x_priv *priv = to_ov772x(sd); ++ const struct ov772x_color_format *cfmt; ++ const struct ov772x_win_size *win; ++ int ret; ++ ++ ov772x_select_params(mf, &cfmt, &win); ++ ++ ret = ov772x_set_params(priv, cfmt, win); ++ if (ret < 0) ++ return ret; ++ ++ priv->win = win; ++ priv->cfmt = cfmt; ++ ++ mf->code = cfmt->code; ++ mf->width = win->rect.width; ++ mf->height = win->rect.height; ++ mf->field = V4L2_FIELD_NONE; ++ mf->colorspace = cfmt->colorspace; ++ ++ return 0; ++} ++ ++static int ov772x_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ const struct ov772x_color_format *cfmt; ++ const struct ov772x_win_size *win; ++ ++ ov772x_select_params(mf, &cfmt, &win); ++ ++ mf->code = cfmt->code; ++ mf->width = win->rect.width; ++ mf->height = win->rect.height; ++ mf->field = V4L2_FIELD_NONE; ++ mf->colorspace = cfmt->colorspace; ++ ++ return 0; ++} ++ ++static int ov772x_video_probe(struct ov772x_priv *priv) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); ++ u8 pid, ver; ++ const char *devname; ++ int ret; ++ ++ ret = ov772x_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ pid = ov772x_read(client, PID); ++ ver = ov772x_read(client, VER); ++ ++ switch (VERSION(pid, ver)) { ++ case OV7720: ++ devname = "ov7720"; ++ priv->model = V4L2_IDENT_OV7720; ++ break; ++ case OV7725: ++ devname = "ov7725"; ++ priv->model = V4L2_IDENT_OV7725; ++ break; ++ default: ++ dev_err(&client->dev, ++ "Product ID error %x:%x\n", pid, ver); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, ++ "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", ++ devname, ++ pid, ++ ver, ++ ov772x_read(client, MIDH), ++ ov772x_read(client, MIDL)); ++ ret = v4l2_ctrl_handler_setup(&priv->hdl); ++ ++done: ++ ov772x_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { ++ .s_ctrl = ov772x_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { ++ .g_chip_ident = ov772x_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov772x_g_register, ++ .s_register = ov772x_s_register, ++#endif ++ .s_power = ov772x_s_power, ++}; ++ ++static int ov772x_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov772x_cfmts)) ++ return -EINVAL; ++ ++ *code = ov772x_cfmts[index].code; ++ return 0; ++} ++ ++static int ov772x_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { ++ .s_stream = ov772x_s_stream, ++ .g_mbus_fmt = ov772x_g_fmt, ++ .s_mbus_fmt = ov772x_s_fmt, ++ .try_mbus_fmt = ov772x_try_fmt, ++ .cropcap = ov772x_cropcap, ++ .g_crop = ov772x_g_crop, ++ .enum_mbus_fmt = ov772x_enum_fmt, ++ .g_mbus_config = ov772x_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops ov772x_subdev_ops = { ++ .core = &ov772x_subdev_core_ops, ++ .video = &ov772x_subdev_video_ops, ++}; ++ ++/* ++ * i2c_driver function ++ */ ++ ++static int ov772x_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov772x_priv *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ int ret; ++ ++ if (!ssdd || !ssdd->drv_priv) { ++ dev_err(&client->dev, "OV772X: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_err(&adapter->dev, ++ "I2C-Adapter doesn't support " ++ "I2C_FUNC_SMBUS_BYTE_DATA\n"); ++ return -EIO; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->info = ssdd->drv_priv; ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->hdl, 3); ++ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops, ++ V4L2_CID_BAND_STOP_FILTER, 0, 256, 1, 0); ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) ++ return priv->hdl.error; ++ ++ ret = ov772x_video_probe(priv); ++ if (ret < 0) { ++ v4l2_ctrl_handler_free(&priv->hdl); ++ } else { ++ priv->cfmt = &ov772x_cfmts[0]; ++ priv->win = &ov772x_win_sizes[0]; ++ } ++ return ret; ++} ++ ++static int ov772x_remove(struct i2c_client *client) ++{ ++ struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ return 0; ++} ++ ++static const struct i2c_device_id ov772x_id[] = { ++ { "ov772x", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov772x_id); ++ ++static struct i2c_driver ov772x_i2c_driver = { ++ .driver = { ++ .name = "ov772x", ++ }, ++ .probe = ov772x_probe, ++ .remove = ov772x_remove, ++ .id_table = ov772x_id, ++}; ++ ++module_i2c_driver(ov772x_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for ov772x"); ++MODULE_AUTHOR("Kuninori Morimoto"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/ov9640.c +new file mode 100644 +index 0000000..0599304 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov9640.c +@@ -0,0 +1,756 @@ ++/* ++ * OmniVision OV96xx Camera Driver ++ * ++ * Copyright (C) 2009 Marek Vasut ++ * ++ * Based on ov772x camera driver: ++ * ++ * Copyright (C) 2008 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * Based on ov7670 and soc_camera_platform driver, ++ * ++ * Copyright 2006-7 Jonathan Corbet ++ * Copyright (C) 2008 Magnus Damm ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "ov9640.h" ++ ++#define to_ov9640_sensor(sd) container_of(sd, struct ov9640_priv, subdev) ++ ++/* default register setup */ ++static const struct ov9640_reg ov9640_regs_dflt[] = { ++ { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, ++ { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | ++ OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, ++ { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, ++ { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, ++ { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, ++ { OV9640_COM16, OV9640_COM16_RB_AVG }, ++ ++ /* Gamma curve P */ ++ { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, ++ { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, ++ { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, ++ { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, ++ ++ /* Gamma curve T */ ++ { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, ++ { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, ++ { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, ++ { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, ++}; ++ ++/* Configurations ++ * NOTE: for YUV, alter the following registers: ++ * COM12 |= OV9640_COM12_YUV_AVG ++ * ++ * for RGB, alter the following registers: ++ * COM7 |= OV9640_COM7_RGB ++ * COM13 |= OV9640_COM13_RGB_AVG ++ * COM15 |= proper RGB color encoding mode ++ */ ++static const struct ov9640_reg ov9640_regs_qqcif[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, ++ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, ++ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, ++ { OV9640_COM7, OV9640_COM7_QCIF }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_qqvga[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, ++ { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, ++ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, ++ { OV9640_COM7, OV9640_COM7_QVGA }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_qcif[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, ++ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, ++ { OV9640_COM7, OV9640_COM7_QCIF }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_qvga[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, ++ { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, ++ { OV9640_COM7, OV9640_COM7_QVGA }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_cif[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, ++ { OV9640_COM3, OV9640_COM3_VP }, ++ { OV9640_COM7, OV9640_COM7_CIF }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_vga[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, ++ { OV9640_COM3, OV9640_COM3_VP }, ++ { OV9640_COM7, OV9640_COM7_VGA }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_sxga[] = { ++ { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, ++ { OV9640_COM3, OV9640_COM3_VP }, ++ { OV9640_COM7, 0 }, ++ { OV9640_COM12, OV9640_COM12_RSVD }, ++ { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, ++ { OV9640_COM15, OV9640_COM15_OR_10F0 }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_yuv[] = { ++ { OV9640_MTX1, 0x58 }, ++ { OV9640_MTX2, 0x48 }, ++ { OV9640_MTX3, 0x10 }, ++ { OV9640_MTX4, 0x28 }, ++ { OV9640_MTX5, 0x48 }, ++ { OV9640_MTX6, 0x70 }, ++ { OV9640_MTX7, 0x40 }, ++ { OV9640_MTX8, 0x40 }, ++ { OV9640_MTX9, 0x40 }, ++ { OV9640_MTXS, 0x0f }, ++}; ++ ++static const struct ov9640_reg ov9640_regs_rgb[] = { ++ { OV9640_MTX1, 0x71 }, ++ { OV9640_MTX2, 0x3e }, ++ { OV9640_MTX3, 0x0c }, ++ { OV9640_MTX4, 0x33 }, ++ { OV9640_MTX5, 0x72 }, ++ { OV9640_MTX6, 0x00 }, ++ { OV9640_MTX7, 0x2b }, ++ { OV9640_MTX8, 0x66 }, ++ { OV9640_MTX9, 0xd2 }, ++ { OV9640_MTXS, 0x65 }, ++}; ++ ++static enum v4l2_mbus_pixelcode ov9640_codes[] = { ++ V4L2_MBUS_FMT_UYVY8_2X8, ++ V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, ++ V4L2_MBUS_FMT_RGB565_2X8_LE, ++}; ++ ++/* read a register */ ++static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) ++{ ++ int ret; ++ u8 data = reg; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 1, ++ .buf = &data, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ goto err; ++ ++ msg.flags = I2C_M_RD; ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ goto err; ++ ++ *val = data; ++ return 0; ++ ++err: ++ dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); ++ return ret; ++} ++ ++/* write a register */ ++static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) ++{ ++ int ret; ++ u8 _val; ++ unsigned char data[2] = { reg, val }; ++ struct i2c_msg msg = { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = data, ++ }; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); ++ return ret; ++ } ++ ++ /* we have to read the register back ... no idea why, maybe HW bug */ ++ ret = ov9640_reg_read(client, reg, &_val); ++ if (ret) ++ dev_err(&client->dev, ++ "Failed reading back register 0x%02x!\n", reg); ++ ++ return 0; ++} ++ ++ ++/* Read a register, alter its bits, write it back */ ++static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) ++{ ++ u8 val; ++ int ret; ++ ++ ret = ov9640_reg_read(client, reg, &val); ++ if (ret) { ++ dev_err(&client->dev, ++ "[Read]-Modify-Write of register %02x failed!\n", reg); ++ return val; ++ } ++ ++ val |= set; ++ val &= ~unset; ++ ++ ret = ov9640_reg_write(client, reg, val); ++ if (ret) ++ dev_err(&client->dev, ++ "Read-Modify-[Write] of register %02x failed!\n", reg); ++ ++ return ret; ++} ++ ++/* Soft reset the camera. This has nothing to do with the RESET pin! */ ++static int ov9640_reset(struct i2c_client *client) ++{ ++ int ret; ++ ++ ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); ++ if (ret) ++ dev_err(&client->dev, ++ "An error occurred while entering soft reset!\n"); ++ ++ return ret; ++} ++ ++/* Start/Stop streaming from the device */ ++static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ return 0; ++} ++ ++/* Set status of additional camera capabilities */ ++static int ov9640_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov9640_priv *priv = container_of(ctrl->handler, struct ov9640_priv, hdl); ++ struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ return ov9640_reg_rmw(client, OV9640_MVFP, ++ OV9640_MVFP_V, 0); ++ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_V); ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ return ov9640_reg_rmw(client, OV9640_MVFP, ++ OV9640_MVFP_H, 0); ++ return ov9640_reg_rmw(client, OV9640_MVFP, 0, OV9640_MVFP_H); ++ } ++ return -EINVAL; ++} ++ ++/* Get chip identification */ ++static int ov9640_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct ov9640_priv *priv = to_ov9640_sensor(sd); ++ ++ id->ident = priv->model; ++ id->revision = priv->revision; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov9640_get_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val; ++ ++ if (reg->reg & ~0xff) ++ return -EINVAL; ++ ++ reg->size = 1; ++ ++ ret = ov9640_reg_read(client, reg->reg, &val); ++ if (ret) ++ return ret; ++ ++ reg->val = (__u64)val; ++ ++ return 0; ++} ++ ++static int ov9640_set_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg & ~0xff || reg->val & ~0xff) ++ return -EINVAL; ++ ++ return ov9640_reg_write(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov9640_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++/* select nearest higher resolution for capture */ ++static void ov9640_res_roundup(u32 *width, u32 *height) ++{ ++ int i; ++ enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; ++ int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; ++ int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; ++ ++ for (i = 0; i < ARRAY_SIZE(res_x); i++) { ++ if (res_x[i] >= *width && res_y[i] >= *height) { ++ *width = res_x[i]; ++ *height = res_y[i]; ++ return; ++ } ++ } ++ ++ *width = res_x[SXGA]; ++ *height = res_y[SXGA]; ++} ++ ++/* Prepare necessary register changes depending on color encoding */ ++static void ov9640_alter_regs(enum v4l2_mbus_pixelcode code, ++ struct ov9640_reg_alt *alt) ++{ ++ switch (code) { ++ default: ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ alt->com12 = OV9640_COM12_YUV_AVG; ++ alt->com13 = OV9640_COM13_Y_DELAY_EN | ++ OV9640_COM13_YUV_DLY(0x01); ++ break; ++ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: ++ alt->com7 = OV9640_COM7_RGB; ++ alt->com13 = OV9640_COM13_RGB_AVG; ++ alt->com15 = OV9640_COM15_RGB_555; ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ alt->com7 = OV9640_COM7_RGB; ++ alt->com13 = OV9640_COM13_RGB_AVG; ++ alt->com15 = OV9640_COM15_RGB_565; ++ break; ++ }; ++} ++ ++/* Setup registers according to resolution and color encoding */ ++static int ov9640_write_regs(struct i2c_client *client, u32 width, ++ enum v4l2_mbus_pixelcode code, struct ov9640_reg_alt *alts) ++{ ++ const struct ov9640_reg *ov9640_regs, *matrix_regs; ++ int ov9640_regs_len, matrix_regs_len; ++ int i, ret; ++ u8 val; ++ ++ /* select register configuration for given resolution */ ++ switch (width) { ++ case W_QQCIF: ++ ov9640_regs = ov9640_regs_qqcif; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); ++ break; ++ case W_QQVGA: ++ ov9640_regs = ov9640_regs_qqvga; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); ++ break; ++ case W_QCIF: ++ ov9640_regs = ov9640_regs_qcif; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); ++ break; ++ case W_QVGA: ++ ov9640_regs = ov9640_regs_qvga; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); ++ break; ++ case W_CIF: ++ ov9640_regs = ov9640_regs_cif; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); ++ break; ++ case W_VGA: ++ ov9640_regs = ov9640_regs_vga; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); ++ break; ++ case W_SXGA: ++ ov9640_regs = ov9640_regs_sxga; ++ ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); ++ break; ++ default: ++ dev_err(&client->dev, "Failed to select resolution!\n"); ++ return -EINVAL; ++ } ++ ++ /* select color matrix configuration for given color encoding */ ++ if (code == V4L2_MBUS_FMT_UYVY8_2X8) { ++ matrix_regs = ov9640_regs_yuv; ++ matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); ++ } else { ++ matrix_regs = ov9640_regs_rgb; ++ matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); ++ } ++ ++ /* write register settings into the module */ ++ for (i = 0; i < ov9640_regs_len; i++) { ++ val = ov9640_regs[i].val; ++ ++ switch (ov9640_regs[i].reg) { ++ case OV9640_COM7: ++ val |= alts->com7; ++ break; ++ case OV9640_COM12: ++ val |= alts->com12; ++ break; ++ case OV9640_COM13: ++ val |= alts->com13; ++ break; ++ case OV9640_COM15: ++ val |= alts->com15; ++ break; ++ } ++ ++ ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); ++ if (ret) ++ return ret; ++ } ++ ++ /* write color matrix configuration into the module */ ++ for (i = 0; i < matrix_regs_len; i++) { ++ ret = ov9640_reg_write(client, matrix_regs[i].reg, ++ matrix_regs[i].val); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* program default register values */ ++static int ov9640_prog_dflt(struct i2c_client *client) ++{ ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { ++ ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, ++ ov9640_regs_dflt[i].val); ++ if (ret) ++ return ret; ++ } ++ ++ /* wait for the changes to actually happen, 140ms are not enough yet */ ++ mdelay(150); ++ ++ return 0; ++} ++ ++/* set the format we will capture in */ ++static int ov9640_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov9640_reg_alt alts = {0}; ++ enum v4l2_colorspace cspace; ++ enum v4l2_mbus_pixelcode code = mf->code; ++ int ret; ++ ++ ov9640_res_roundup(&mf->width, &mf->height); ++ ov9640_alter_regs(mf->code, &alts); ++ ++ ov9640_reset(client); ++ ++ ret = ov9640_prog_dflt(client); ++ if (ret) ++ return ret; ++ ++ switch (code) { ++ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ cspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ code = V4L2_MBUS_FMT_UYVY8_2X8; ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ cspace = V4L2_COLORSPACE_JPEG; ++ } ++ ++ ret = ov9640_write_regs(client, mf->width, code, &alts); ++ if (!ret) { ++ mf->code = code; ++ mf->colorspace = cspace; ++ } ++ ++ return ret; ++} ++ ++static int ov9640_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ ov9640_res_roundup(&mf->width, &mf->height); ++ ++ mf->field = V4L2_FIELD_NONE; ++ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE: ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ } ++ ++ return 0; ++} ++ ++static int ov9640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov9640_codes)) ++ return -EINVAL; ++ ++ *code = ov9640_codes[index]; ++ return 0; ++} ++ ++static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ a->c.left = 0; ++ a->c.top = 0; ++ a->c.width = W_SXGA; ++ a->c.height = H_SXGA; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = W_SXGA; ++ a->bounds.height = H_SXGA; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov9640_video_probe(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ov9640_priv *priv = to_ov9640_sensor(sd); ++ u8 pid, ver, midh, midl; ++ const char *devname; ++ int ret; ++ ++ ret = ov9640_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ ++ ret = ov9640_reg_read(client, OV9640_PID, &pid); ++ if (!ret) ++ ret = ov9640_reg_read(client, OV9640_VER, &ver); ++ if (!ret) ++ ret = ov9640_reg_read(client, OV9640_MIDH, &midh); ++ if (!ret) ++ ret = ov9640_reg_read(client, OV9640_MIDL, &midl); ++ if (ret) ++ goto done; ++ ++ switch (VERSION(pid, ver)) { ++ case OV9640_V2: ++ devname = "ov9640"; ++ priv->model = V4L2_IDENT_OV9640; ++ priv->revision = 2; ++ break; ++ case OV9640_V3: ++ devname = "ov9640"; ++ priv->model = V4L2_IDENT_OV9640; ++ priv->revision = 3; ++ break; ++ default: ++ dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", ++ devname, pid, ver, midh, midl); ++ ++ ret = v4l2_ctrl_handler_setup(&priv->hdl); ++ ++done: ++ ov9640_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops ov9640_ctrl_ops = { ++ .s_ctrl = ov9640_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops ov9640_core_ops = { ++ .g_chip_ident = ov9640_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov9640_get_register, ++ .s_register = ov9640_set_register, ++#endif ++ .s_power = ov9640_s_power, ++}; ++ ++/* Request bus settings on camera side */ ++static int ov9640_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops ov9640_video_ops = { ++ .s_stream = ov9640_s_stream, ++ .s_mbus_fmt = ov9640_s_fmt, ++ .try_mbus_fmt = ov9640_try_fmt, ++ .enum_mbus_fmt = ov9640_enum_fmt, ++ .cropcap = ov9640_cropcap, ++ .g_crop = ov9640_g_crop, ++ .g_mbus_config = ov9640_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops ov9640_subdev_ops = { ++ .core = &ov9640_core_ops, ++ .video = &ov9640_video_ops, ++}; ++ ++/* ++ * i2c_driver function ++ */ ++static int ov9640_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov9640_priv *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "Missing platform_data for driver\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct ov9640_priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&client->dev, ++ "Failed to allocate memory for private data!\n"); ++ return -ENOMEM; ++ } ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); ++ ++ v4l2_ctrl_handler_init(&priv->hdl, 2); ++ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov9640_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) ++ return priv->hdl.error; ++ ++ ret = ov9640_video_probe(client); ++ ++ if (ret) ++ v4l2_ctrl_handler_free(&priv->hdl); ++ ++ return ret; ++} ++ ++static int ov9640_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ov9640_priv *priv = to_ov9640_sensor(sd); ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ return 0; ++} ++ ++static const struct i2c_device_id ov9640_id[] = { ++ { "ov9640", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov9640_id); ++ ++static struct i2c_driver ov9640_i2c_driver = { ++ .driver = { ++ .name = "ov9640", ++ }, ++ .probe = ov9640_probe, ++ .remove = ov9640_remove, ++ .id_table = ov9640_id, ++}; ++ ++module_i2c_driver(ov9640_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); ++MODULE_AUTHOR("Marek Vasut "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/ov9640.h b/drivers/media/i2c/soc_camera/ov9640.h +new file mode 100644 +index 0000000..6b33a97 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov9640.h +@@ -0,0 +1,207 @@ ++/* ++ * OmniVision OV96xx Camera Header File ++ * ++ * Copyright (C) 2009 Marek Vasut ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ ++#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ ++ ++/* Register definitions */ ++#define OV9640_GAIN 0x00 ++#define OV9640_BLUE 0x01 ++#define OV9640_RED 0x02 ++#define OV9640_VFER 0x03 ++#define OV9640_COM1 0x04 ++#define OV9640_BAVE 0x05 ++#define OV9640_GEAVE 0x06 ++#define OV9640_RSID 0x07 ++#define OV9640_RAVE 0x08 ++#define OV9640_COM2 0x09 ++#define OV9640_PID 0x0a ++#define OV9640_VER 0x0b ++#define OV9640_COM3 0x0c ++#define OV9640_COM4 0x0d ++#define OV9640_COM5 0x0e ++#define OV9640_COM6 0x0f ++#define OV9640_AECH 0x10 ++#define OV9640_CLKRC 0x11 ++#define OV9640_COM7 0x12 ++#define OV9640_COM8 0x13 ++#define OV9640_COM9 0x14 ++#define OV9640_COM10 0x15 ++/* 0x16 - RESERVED */ ++#define OV9640_HSTART 0x17 ++#define OV9640_HSTOP 0x18 ++#define OV9640_VSTART 0x19 ++#define OV9640_VSTOP 0x1a ++#define OV9640_PSHFT 0x1b ++#define OV9640_MIDH 0x1c ++#define OV9640_MIDL 0x1d ++#define OV9640_MVFP 0x1e ++#define OV9640_LAEC 0x1f ++#define OV9640_BOS 0x20 ++#define OV9640_GBOS 0x21 ++#define OV9640_GROS 0x22 ++#define OV9640_ROS 0x23 ++#define OV9640_AEW 0x24 ++#define OV9640_AEB 0x25 ++#define OV9640_VPT 0x26 ++#define OV9640_BBIAS 0x27 ++#define OV9640_GBBIAS 0x28 ++/* 0x29 - RESERVED */ ++#define OV9640_EXHCH 0x2a ++#define OV9640_EXHCL 0x2b ++#define OV9640_RBIAS 0x2c ++#define OV9640_ADVFL 0x2d ++#define OV9640_ADVFH 0x2e ++#define OV9640_YAVE 0x2f ++#define OV9640_HSYST 0x30 ++#define OV9640_HSYEN 0x31 ++#define OV9640_HREF 0x32 ++#define OV9640_CHLF 0x33 ++#define OV9640_ARBLM 0x34 ++/* 0x35..0x36 - RESERVED */ ++#define OV9640_ADC 0x37 ++#define OV9640_ACOM 0x38 ++#define OV9640_OFON 0x39 ++#define OV9640_TSLB 0x3a ++#define OV9640_COM11 0x3b ++#define OV9640_COM12 0x3c ++#define OV9640_COM13 0x3d ++#define OV9640_COM14 0x3e ++#define OV9640_EDGE 0x3f ++#define OV9640_COM15 0x40 ++#define OV9640_COM16 0x41 ++#define OV9640_COM17 0x42 ++/* 0x43..0x4e - RESERVED */ ++#define OV9640_MTX1 0x4f ++#define OV9640_MTX2 0x50 ++#define OV9640_MTX3 0x51 ++#define OV9640_MTX4 0x52 ++#define OV9640_MTX5 0x53 ++#define OV9640_MTX6 0x54 ++#define OV9640_MTX7 0x55 ++#define OV9640_MTX8 0x56 ++#define OV9640_MTX9 0x57 ++#define OV9640_MTXS 0x58 ++/* 0x59..0x61 - RESERVED */ ++#define OV9640_LCC1 0x62 ++#define OV9640_LCC2 0x63 ++#define OV9640_LCC3 0x64 ++#define OV9640_LCC4 0x65 ++#define OV9640_LCC5 0x66 ++#define OV9640_MANU 0x67 ++#define OV9640_MANV 0x68 ++#define OV9640_HV 0x69 ++#define OV9640_MBD 0x6a ++#define OV9640_DBLV 0x6b ++#define OV9640_GSP 0x6c /* ... till 0x7b */ ++#define OV9640_GST 0x7c /* ... till 0x8a */ ++ ++#define OV9640_CLKRC_DPLL_EN 0x80 ++#define OV9640_CLKRC_DIRECT 0x40 ++#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) ++ ++#define OV9640_PSHFT_VAL(x) ((x) & 0xff) ++ ++#define OV9640_ACOM_2X_ANALOG 0x80 ++#define OV9640_ACOM_RSVD 0x12 ++ ++#define OV9640_MVFP_V 0x10 ++#define OV9640_MVFP_H 0x20 ++ ++#define OV9640_COM1_HREF_NOSKIP 0x00 ++#define OV9640_COM1_HREF_2SKIP 0x04 ++#define OV9640_COM1_HREF_3SKIP 0x08 ++#define OV9640_COM1_QQFMT 0x20 ++ ++#define OV9640_COM2_SSM 0x10 ++ ++#define OV9640_COM3_VP 0x04 ++ ++#define OV9640_COM4_QQ_VP 0x80 ++#define OV9640_COM4_RSVD 0x40 ++ ++#define OV9640_COM5_SYSCLK 0x80 ++#define OV9640_COM5_LONGEXP 0x01 ++ ++#define OV9640_COM6_OPT_BLC 0x40 ++#define OV9640_COM6_ADBLC_BIAS 0x08 ++#define OV9640_COM6_FMT_RST 0x82 ++#define OV9640_COM6_ADBLC_OPTEN 0x01 ++ ++#define OV9640_COM7_RAW_RGB 0x01 ++#define OV9640_COM7_RGB 0x04 ++#define OV9640_COM7_QCIF 0x08 ++#define OV9640_COM7_QVGA 0x10 ++#define OV9640_COM7_CIF 0x20 ++#define OV9640_COM7_VGA 0x40 ++#define OV9640_COM7_SCCB_RESET 0x80 ++ ++#define OV9640_TSLB_YVYU_YUYV 0x04 ++#define OV9640_TSLB_YUYV_UYVY 0x08 ++ ++#define OV9640_COM12_YUV_AVG 0x04 ++#define OV9640_COM12_RSVD 0x40 ++ ++#define OV9640_COM13_GAMMA_NONE 0x00 ++#define OV9640_COM13_GAMMA_Y 0x40 ++#define OV9640_COM13_GAMMA_RAW 0x80 ++#define OV9640_COM13_RGB_AVG 0x20 ++#define OV9640_COM13_MATRIX_EN 0x10 ++#define OV9640_COM13_Y_DELAY_EN 0x08 ++#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) ++ ++#define OV9640_COM15_OR_00FF 0x00 ++#define OV9640_COM15_OR_01FE 0x40 ++#define OV9640_COM15_OR_10F0 0xc0 ++#define OV9640_COM15_RGB_NORM 0x00 ++#define OV9640_COM15_RGB_565 0x10 ++#define OV9640_COM15_RGB_555 0x30 ++ ++#define OV9640_COM16_RB_AVG 0x01 ++ ++/* IDs */ ++#define OV9640_V2 0x9648 ++#define OV9640_V3 0x9649 ++#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) ++ ++/* supported resolutions */ ++enum { ++ W_QQCIF = 88, ++ W_QQVGA = 160, ++ W_QCIF = 176, ++ W_QVGA = 320, ++ W_CIF = 352, ++ W_VGA = 640, ++ W_SXGA = 1280 ++}; ++#define H_SXGA 960 ++ ++/* Misc. structures */ ++struct ov9640_reg_alt { ++ u8 com7; ++ u8 com12; ++ u8 com13; ++ u8 com15; ++}; ++ ++struct ov9640_reg { ++ u8 reg; ++ u8 val; ++}; ++ ++struct ov9640_priv { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ ++ int model; ++ int revision; ++}; ++ ++#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ +diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/ov9740.c +new file mode 100644 +index 0000000..2f236da +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/ov9740.c +@@ -0,0 +1,1013 @@ ++/* ++ * OmniVision OV9740 Camera Driver ++ * ++ * Copyright (C) 2011 NVIDIA Corporation ++ * ++ * Based on ov9640 camera driver. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define to_ov9740(sd) container_of(sd, struct ov9740_priv, subdev) ++ ++/* General Status Registers */ ++#define OV9740_MODEL_ID_HI 0x0000 ++#define OV9740_MODEL_ID_LO 0x0001 ++#define OV9740_REVISION_NUMBER 0x0002 ++#define OV9740_MANUFACTURER_ID 0x0003 ++#define OV9740_SMIA_VERSION 0x0004 ++ ++/* General Setup Registers */ ++#define OV9740_MODE_SELECT 0x0100 ++#define OV9740_IMAGE_ORT 0x0101 ++#define OV9740_SOFTWARE_RESET 0x0103 ++#define OV9740_GRP_PARAM_HOLD 0x0104 ++#define OV9740_MSK_CORRUP_FM 0x0105 ++ ++/* Timing Setting */ ++#define OV9740_FRM_LENGTH_LN_HI 0x0340 /* VTS */ ++#define OV9740_FRM_LENGTH_LN_LO 0x0341 /* VTS */ ++#define OV9740_LN_LENGTH_PCK_HI 0x0342 /* HTS */ ++#define OV9740_LN_LENGTH_PCK_LO 0x0343 /* HTS */ ++#define OV9740_X_ADDR_START_HI 0x0344 ++#define OV9740_X_ADDR_START_LO 0x0345 ++#define OV9740_Y_ADDR_START_HI 0x0346 ++#define OV9740_Y_ADDR_START_LO 0x0347 ++#define OV9740_X_ADDR_END_HI 0x0348 ++#define OV9740_X_ADDR_END_LO 0x0349 ++#define OV9740_Y_ADDR_END_HI 0x034a ++#define OV9740_Y_ADDR_END_LO 0x034b ++#define OV9740_X_OUTPUT_SIZE_HI 0x034c ++#define OV9740_X_OUTPUT_SIZE_LO 0x034d ++#define OV9740_Y_OUTPUT_SIZE_HI 0x034e ++#define OV9740_Y_OUTPUT_SIZE_LO 0x034f ++ ++/* IO Control Registers */ ++#define OV9740_IO_CREL00 0x3002 ++#define OV9740_IO_CREL01 0x3004 ++#define OV9740_IO_CREL02 0x3005 ++#define OV9740_IO_OUTPUT_SEL01 0x3026 ++#define OV9740_IO_OUTPUT_SEL02 0x3027 ++ ++/* AWB Registers */ ++#define OV9740_AWB_MANUAL_CTRL 0x3406 ++ ++/* Analog Control Registers */ ++#define OV9740_ANALOG_CTRL01 0x3601 ++#define OV9740_ANALOG_CTRL02 0x3602 ++#define OV9740_ANALOG_CTRL03 0x3603 ++#define OV9740_ANALOG_CTRL04 0x3604 ++#define OV9740_ANALOG_CTRL10 0x3610 ++#define OV9740_ANALOG_CTRL12 0x3612 ++#define OV9740_ANALOG_CTRL15 0x3615 ++#define OV9740_ANALOG_CTRL20 0x3620 ++#define OV9740_ANALOG_CTRL21 0x3621 ++#define OV9740_ANALOG_CTRL22 0x3622 ++#define OV9740_ANALOG_CTRL30 0x3630 ++#define OV9740_ANALOG_CTRL31 0x3631 ++#define OV9740_ANALOG_CTRL32 0x3632 ++#define OV9740_ANALOG_CTRL33 0x3633 ++ ++/* Sensor Control */ ++#define OV9740_SENSOR_CTRL03 0x3703 ++#define OV9740_SENSOR_CTRL04 0x3704 ++#define OV9740_SENSOR_CTRL05 0x3705 ++#define OV9740_SENSOR_CTRL07 0x3707 ++ ++/* Timing Control */ ++#define OV9740_TIMING_CTRL17 0x3817 ++#define OV9740_TIMING_CTRL19 0x3819 ++#define OV9740_TIMING_CTRL33 0x3833 ++#define OV9740_TIMING_CTRL35 0x3835 ++ ++/* Banding Filter */ ++#define OV9740_AEC_MAXEXPO_60_H 0x3a02 ++#define OV9740_AEC_MAXEXPO_60_L 0x3a03 ++#define OV9740_AEC_B50_STEP_HI 0x3a08 ++#define OV9740_AEC_B50_STEP_LO 0x3a09 ++#define OV9740_AEC_B60_STEP_HI 0x3a0a ++#define OV9740_AEC_B60_STEP_LO 0x3a0b ++#define OV9740_AEC_CTRL0D 0x3a0d ++#define OV9740_AEC_CTRL0E 0x3a0e ++#define OV9740_AEC_MAXEXPO_50_H 0x3a14 ++#define OV9740_AEC_MAXEXPO_50_L 0x3a15 ++ ++/* AEC/AGC Control */ ++#define OV9740_AEC_ENABLE 0x3503 ++#define OV9740_GAIN_CEILING_01 0x3a18 ++#define OV9740_GAIN_CEILING_02 0x3a19 ++#define OV9740_AEC_HI_THRESHOLD 0x3a11 ++#define OV9740_AEC_3A1A 0x3a1a ++#define OV9740_AEC_CTRL1B_WPT2 0x3a1b ++#define OV9740_AEC_CTRL0F_WPT 0x3a0f ++#define OV9740_AEC_CTRL10_BPT 0x3a10 ++#define OV9740_AEC_CTRL1E_BPT2 0x3a1e ++#define OV9740_AEC_LO_THRESHOLD 0x3a1f ++ ++/* BLC Control */ ++#define OV9740_BLC_AUTO_ENABLE 0x4002 ++#define OV9740_BLC_MODE 0x4005 ++ ++/* VFIFO */ ++#define OV9740_VFIFO_READ_START_HI 0x4608 ++#define OV9740_VFIFO_READ_START_LO 0x4609 ++ ++/* DVP Control */ ++#define OV9740_DVP_VSYNC_CTRL02 0x4702 ++#define OV9740_DVP_VSYNC_MODE 0x4704 ++#define OV9740_DVP_VSYNC_CTRL06 0x4706 ++ ++/* PLL Setting */ ++#define OV9740_PLL_MODE_CTRL01 0x3104 ++#define OV9740_PRE_PLL_CLK_DIV 0x0305 ++#define OV9740_PLL_MULTIPLIER 0x0307 ++#define OV9740_VT_SYS_CLK_DIV 0x0303 ++#define OV9740_VT_PIX_CLK_DIV 0x0301 ++#define OV9740_PLL_CTRL3010 0x3010 ++#define OV9740_VFIFO_CTRL00 0x460e ++ ++/* ISP Control */ ++#define OV9740_ISP_CTRL00 0x5000 ++#define OV9740_ISP_CTRL01 0x5001 ++#define OV9740_ISP_CTRL03 0x5003 ++#define OV9740_ISP_CTRL05 0x5005 ++#define OV9740_ISP_CTRL12 0x5012 ++#define OV9740_ISP_CTRL19 0x5019 ++#define OV9740_ISP_CTRL1A 0x501a ++#define OV9740_ISP_CTRL1E 0x501e ++#define OV9740_ISP_CTRL1F 0x501f ++#define OV9740_ISP_CTRL20 0x5020 ++#define OV9740_ISP_CTRL21 0x5021 ++ ++/* AWB */ ++#define OV9740_AWB_CTRL00 0x5180 ++#define OV9740_AWB_CTRL01 0x5181 ++#define OV9740_AWB_CTRL02 0x5182 ++#define OV9740_AWB_CTRL03 0x5183 ++#define OV9740_AWB_ADV_CTRL01 0x5184 ++#define OV9740_AWB_ADV_CTRL02 0x5185 ++#define OV9740_AWB_ADV_CTRL03 0x5186 ++#define OV9740_AWB_ADV_CTRL04 0x5187 ++#define OV9740_AWB_ADV_CTRL05 0x5188 ++#define OV9740_AWB_ADV_CTRL06 0x5189 ++#define OV9740_AWB_ADV_CTRL07 0x518a ++#define OV9740_AWB_ADV_CTRL08 0x518b ++#define OV9740_AWB_ADV_CTRL09 0x518c ++#define OV9740_AWB_ADV_CTRL10 0x518d ++#define OV9740_AWB_ADV_CTRL11 0x518e ++#define OV9740_AWB_CTRL0F 0x518f ++#define OV9740_AWB_CTRL10 0x5190 ++#define OV9740_AWB_CTRL11 0x5191 ++#define OV9740_AWB_CTRL12 0x5192 ++#define OV9740_AWB_CTRL13 0x5193 ++#define OV9740_AWB_CTRL14 0x5194 ++ ++/* MIPI Control */ ++#define OV9740_MIPI_CTRL00 0x4800 ++#define OV9740_MIPI_3837 0x3837 ++#define OV9740_MIPI_CTRL01 0x4801 ++#define OV9740_MIPI_CTRL03 0x4803 ++#define OV9740_MIPI_CTRL05 0x4805 ++#define OV9740_VFIFO_RD_CTRL 0x4601 ++#define OV9740_MIPI_CTRL_3012 0x3012 ++#define OV9740_SC_CMMM_MIPI_CTR 0x3014 ++ ++#define OV9740_MAX_WIDTH 1280 ++#define OV9740_MAX_HEIGHT 720 ++ ++/* Misc. structures */ ++struct ov9740_reg { ++ u16 reg; ++ u8 val; ++}; ++ ++struct ov9740_priv { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ ++ int ident; ++ u16 model; ++ u8 revision; ++ u8 manid; ++ u8 smiaver; ++ ++ bool flag_vflip; ++ bool flag_hflip; ++ ++ /* For suspend/resume. */ ++ struct v4l2_mbus_framefmt current_mf; ++ bool current_enable; ++}; ++ ++static const struct ov9740_reg ov9740_defaults[] = { ++ /* Software Reset */ ++ { OV9740_SOFTWARE_RESET, 0x01 }, ++ ++ /* Banding Filter */ ++ { OV9740_AEC_B50_STEP_HI, 0x00 }, ++ { OV9740_AEC_B50_STEP_LO, 0xe8 }, ++ { OV9740_AEC_CTRL0E, 0x03 }, ++ { OV9740_AEC_MAXEXPO_50_H, 0x15 }, ++ { OV9740_AEC_MAXEXPO_50_L, 0xc6 }, ++ { OV9740_AEC_B60_STEP_HI, 0x00 }, ++ { OV9740_AEC_B60_STEP_LO, 0xc0 }, ++ { OV9740_AEC_CTRL0D, 0x04 }, ++ { OV9740_AEC_MAXEXPO_60_H, 0x18 }, ++ { OV9740_AEC_MAXEXPO_60_L, 0x20 }, ++ ++ /* LC */ ++ { 0x5842, 0x02 }, { 0x5843, 0x5e }, { 0x5844, 0x04 }, { 0x5845, 0x32 }, ++ { 0x5846, 0x03 }, { 0x5847, 0x29 }, { 0x5848, 0x02 }, { 0x5849, 0xcc }, ++ ++ /* Un-documented OV9740 registers */ ++ { 0x5800, 0x29 }, { 0x5801, 0x25 }, { 0x5802, 0x20 }, { 0x5803, 0x21 }, ++ { 0x5804, 0x26 }, { 0x5805, 0x2e }, { 0x5806, 0x11 }, { 0x5807, 0x0c }, ++ { 0x5808, 0x09 }, { 0x5809, 0x0a }, { 0x580a, 0x0e }, { 0x580b, 0x16 }, ++ { 0x580c, 0x06 }, { 0x580d, 0x02 }, { 0x580e, 0x00 }, { 0x580f, 0x00 }, ++ { 0x5810, 0x04 }, { 0x5811, 0x0a }, { 0x5812, 0x05 }, { 0x5813, 0x02 }, ++ { 0x5814, 0x00 }, { 0x5815, 0x00 }, { 0x5816, 0x03 }, { 0x5817, 0x09 }, ++ { 0x5818, 0x0f }, { 0x5819, 0x0a }, { 0x581a, 0x07 }, { 0x581b, 0x08 }, ++ { 0x581c, 0x0b }, { 0x581d, 0x14 }, { 0x581e, 0x28 }, { 0x581f, 0x23 }, ++ { 0x5820, 0x1d }, { 0x5821, 0x1e }, { 0x5822, 0x24 }, { 0x5823, 0x2a }, ++ { 0x5824, 0x4f }, { 0x5825, 0x6f }, { 0x5826, 0x5f }, { 0x5827, 0x7f }, ++ { 0x5828, 0x9f }, { 0x5829, 0x5f }, { 0x582a, 0x8f }, { 0x582b, 0x9e }, ++ { 0x582c, 0x8f }, { 0x582d, 0x9f }, { 0x582e, 0x4f }, { 0x582f, 0x87 }, ++ { 0x5830, 0x86 }, { 0x5831, 0x97 }, { 0x5832, 0xae }, { 0x5833, 0x3f }, ++ { 0x5834, 0x8e }, { 0x5835, 0x7c }, { 0x5836, 0x7e }, { 0x5837, 0xaf }, ++ { 0x5838, 0x8f }, { 0x5839, 0x8f }, { 0x583a, 0x9f }, { 0x583b, 0x7f }, ++ { 0x583c, 0x5f }, ++ ++ /* Y Gamma */ ++ { 0x5480, 0x07 }, { 0x5481, 0x18 }, { 0x5482, 0x2c }, { 0x5483, 0x4e }, ++ { 0x5484, 0x5e }, { 0x5485, 0x6b }, { 0x5486, 0x77 }, { 0x5487, 0x82 }, ++ { 0x5488, 0x8c }, { 0x5489, 0x95 }, { 0x548a, 0xa4 }, { 0x548b, 0xb1 }, ++ { 0x548c, 0xc6 }, { 0x548d, 0xd8 }, { 0x548e, 0xe9 }, ++ ++ /* UV Gamma */ ++ { 0x5490, 0x0f }, { 0x5491, 0xff }, { 0x5492, 0x0d }, { 0x5493, 0x05 }, ++ { 0x5494, 0x07 }, { 0x5495, 0x1a }, { 0x5496, 0x04 }, { 0x5497, 0x01 }, ++ { 0x5498, 0x03 }, { 0x5499, 0x53 }, { 0x549a, 0x02 }, { 0x549b, 0xeb }, ++ { 0x549c, 0x02 }, { 0x549d, 0xa0 }, { 0x549e, 0x02 }, { 0x549f, 0x67 }, ++ { 0x54a0, 0x02 }, { 0x54a1, 0x3b }, { 0x54a2, 0x02 }, { 0x54a3, 0x18 }, ++ { 0x54a4, 0x01 }, { 0x54a5, 0xe7 }, { 0x54a6, 0x01 }, { 0x54a7, 0xc3 }, ++ { 0x54a8, 0x01 }, { 0x54a9, 0x94 }, { 0x54aa, 0x01 }, { 0x54ab, 0x72 }, ++ { 0x54ac, 0x01 }, { 0x54ad, 0x57 }, ++ ++ /* AWB */ ++ { OV9740_AWB_CTRL00, 0xf0 }, ++ { OV9740_AWB_CTRL01, 0x00 }, ++ { OV9740_AWB_CTRL02, 0x41 }, ++ { OV9740_AWB_CTRL03, 0x42 }, ++ { OV9740_AWB_ADV_CTRL01, 0x8a }, ++ { OV9740_AWB_ADV_CTRL02, 0x61 }, ++ { OV9740_AWB_ADV_CTRL03, 0xce }, ++ { OV9740_AWB_ADV_CTRL04, 0xa8 }, ++ { OV9740_AWB_ADV_CTRL05, 0x17 }, ++ { OV9740_AWB_ADV_CTRL06, 0x1f }, ++ { OV9740_AWB_ADV_CTRL07, 0x27 }, ++ { OV9740_AWB_ADV_CTRL08, 0x41 }, ++ { OV9740_AWB_ADV_CTRL09, 0x34 }, ++ { OV9740_AWB_ADV_CTRL10, 0xf0 }, ++ { OV9740_AWB_ADV_CTRL11, 0x10 }, ++ { OV9740_AWB_CTRL0F, 0xff }, ++ { OV9740_AWB_CTRL10, 0x00 }, ++ { OV9740_AWB_CTRL11, 0xff }, ++ { OV9740_AWB_CTRL12, 0x00 }, ++ { OV9740_AWB_CTRL13, 0xff }, ++ { OV9740_AWB_CTRL14, 0x00 }, ++ ++ /* CIP */ ++ { 0x530d, 0x12 }, ++ ++ /* CMX */ ++ { 0x5380, 0x01 }, { 0x5381, 0x00 }, { 0x5382, 0x00 }, { 0x5383, 0x17 }, ++ { 0x5384, 0x00 }, { 0x5385, 0x01 }, { 0x5386, 0x00 }, { 0x5387, 0x00 }, ++ { 0x5388, 0x00 }, { 0x5389, 0xe0 }, { 0x538a, 0x00 }, { 0x538b, 0x20 }, ++ { 0x538c, 0x00 }, { 0x538d, 0x00 }, { 0x538e, 0x00 }, { 0x538f, 0x16 }, ++ { 0x5390, 0x00 }, { 0x5391, 0x9c }, { 0x5392, 0x00 }, { 0x5393, 0xa0 }, ++ { 0x5394, 0x18 }, ++ ++ /* 50/60 Detection */ ++ { 0x3c0a, 0x9c }, { 0x3c0b, 0x3f }, ++ ++ /* Output Select */ ++ { OV9740_IO_OUTPUT_SEL01, 0x00 }, ++ { OV9740_IO_OUTPUT_SEL02, 0x00 }, ++ { OV9740_IO_CREL00, 0x00 }, ++ { OV9740_IO_CREL01, 0x00 }, ++ { OV9740_IO_CREL02, 0x00 }, ++ ++ /* AWB Control */ ++ { OV9740_AWB_MANUAL_CTRL, 0x00 }, ++ ++ /* Analog Control */ ++ { OV9740_ANALOG_CTRL03, 0xaa }, ++ { OV9740_ANALOG_CTRL32, 0x2f }, ++ { OV9740_ANALOG_CTRL20, 0x66 }, ++ { OV9740_ANALOG_CTRL21, 0xc0 }, ++ { OV9740_ANALOG_CTRL31, 0x52 }, ++ { OV9740_ANALOG_CTRL33, 0x50 }, ++ { OV9740_ANALOG_CTRL30, 0xca }, ++ { OV9740_ANALOG_CTRL04, 0x0c }, ++ { OV9740_ANALOG_CTRL01, 0x40 }, ++ { OV9740_ANALOG_CTRL02, 0x16 }, ++ { OV9740_ANALOG_CTRL10, 0xa1 }, ++ { OV9740_ANALOG_CTRL12, 0x24 }, ++ { OV9740_ANALOG_CTRL22, 0x9f }, ++ { OV9740_ANALOG_CTRL15, 0xf0 }, ++ ++ /* Sensor Control */ ++ { OV9740_SENSOR_CTRL03, 0x42 }, ++ { OV9740_SENSOR_CTRL04, 0x10 }, ++ { OV9740_SENSOR_CTRL05, 0x45 }, ++ { OV9740_SENSOR_CTRL07, 0x14 }, ++ ++ /* Timing Control */ ++ { OV9740_TIMING_CTRL33, 0x04 }, ++ { OV9740_TIMING_CTRL35, 0x02 }, ++ { OV9740_TIMING_CTRL19, 0x6e }, ++ { OV9740_TIMING_CTRL17, 0x94 }, ++ ++ /* AEC/AGC Control */ ++ { OV9740_AEC_ENABLE, 0x10 }, ++ { OV9740_GAIN_CEILING_01, 0x00 }, ++ { OV9740_GAIN_CEILING_02, 0x7f }, ++ { OV9740_AEC_HI_THRESHOLD, 0xa0 }, ++ { OV9740_AEC_3A1A, 0x05 }, ++ { OV9740_AEC_CTRL1B_WPT2, 0x50 }, ++ { OV9740_AEC_CTRL0F_WPT, 0x50 }, ++ { OV9740_AEC_CTRL10_BPT, 0x4c }, ++ { OV9740_AEC_CTRL1E_BPT2, 0x4c }, ++ { OV9740_AEC_LO_THRESHOLD, 0x26 }, ++ ++ /* BLC Control */ ++ { OV9740_BLC_AUTO_ENABLE, 0x45 }, ++ { OV9740_BLC_MODE, 0x18 }, ++ ++ /* DVP Control */ ++ { OV9740_DVP_VSYNC_CTRL02, 0x04 }, ++ { OV9740_DVP_VSYNC_MODE, 0x00 }, ++ { OV9740_DVP_VSYNC_CTRL06, 0x08 }, ++ ++ /* PLL Setting */ ++ { OV9740_PLL_MODE_CTRL01, 0x20 }, ++ { OV9740_PRE_PLL_CLK_DIV, 0x03 }, ++ { OV9740_PLL_MULTIPLIER, 0x4c }, ++ { OV9740_VT_SYS_CLK_DIV, 0x01 }, ++ { OV9740_VT_PIX_CLK_DIV, 0x08 }, ++ { OV9740_PLL_CTRL3010, 0x01 }, ++ { OV9740_VFIFO_CTRL00, 0x82 }, ++ ++ /* Timing Setting */ ++ /* VTS */ ++ { OV9740_FRM_LENGTH_LN_HI, 0x03 }, ++ { OV9740_FRM_LENGTH_LN_LO, 0x07 }, ++ /* HTS */ ++ { OV9740_LN_LENGTH_PCK_HI, 0x06 }, ++ { OV9740_LN_LENGTH_PCK_LO, 0x62 }, ++ ++ /* MIPI Control */ ++ { OV9740_MIPI_CTRL00, 0x44 }, /* 0x64 for discontinuous clk */ ++ { OV9740_MIPI_3837, 0x01 }, ++ { OV9740_MIPI_CTRL01, 0x0f }, ++ { OV9740_MIPI_CTRL03, 0x05 }, ++ { OV9740_MIPI_CTRL05, 0x10 }, ++ { OV9740_VFIFO_RD_CTRL, 0x16 }, ++ { OV9740_MIPI_CTRL_3012, 0x70 }, ++ { OV9740_SC_CMMM_MIPI_CTR, 0x01 }, ++ ++ /* YUYV order */ ++ { OV9740_ISP_CTRL19, 0x02 }, ++}; ++ ++static enum v4l2_mbus_pixelcode ov9740_codes[] = { ++ V4L2_MBUS_FMT_YUYV8_2X8, ++}; ++ ++/* read a register */ ++static int ov9740_reg_read(struct i2c_client *client, u16 reg, u8 *val) ++{ ++ int ret; ++ struct i2c_msg msg[] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = (u8 *)®, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = val, ++ }, ++ }; ++ ++ reg = swab16(reg); ++ ++ ret = i2c_transfer(client->adapter, msg, 2); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed reading register 0x%04x!\n", reg); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* write a register */ ++static int ov9740_reg_write(struct i2c_client *client, u16 reg, u8 val) ++{ ++ struct i2c_msg msg; ++ struct { ++ u16 reg; ++ u8 val; ++ } __packed buf; ++ int ret; ++ ++ reg = swab16(reg); ++ ++ buf.reg = reg; ++ buf.val = val; ++ ++ msg.addr = client->addr; ++ msg.flags = 0; ++ msg.len = 3; ++ msg.buf = (u8 *)&buf; ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) { ++ dev_err(&client->dev, "Failed writing register 0x%04x!\n", reg); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++/* Read a register, alter its bits, write it back */ ++static int ov9740_reg_rmw(struct i2c_client *client, u16 reg, u8 set, u8 unset) ++{ ++ u8 val; ++ int ret; ++ ++ ret = ov9740_reg_read(client, reg, &val); ++ if (ret < 0) { ++ dev_err(&client->dev, ++ "[Read]-Modify-Write of register 0x%04x failed!\n", ++ reg); ++ return ret; ++ } ++ ++ val |= set; ++ val &= ~unset; ++ ++ ret = ov9740_reg_write(client, reg, val); ++ if (ret < 0) { ++ dev_err(&client->dev, ++ "Read-Modify-[Write] of register 0x%04x failed!\n", ++ reg); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ov9740_reg_write_array(struct i2c_client *client, ++ const struct ov9740_reg *regarray, ++ int regarraylen) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; i < regarraylen; i++) { ++ ret = ov9740_reg_write(client, ++ regarray[i].reg, regarray[i].val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* Start/Stop streaming from the device */ ++static int ov9740_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov9740_priv *priv = to_ov9740(sd); ++ int ret; ++ ++ /* Program orientation register. */ ++ if (priv->flag_vflip) ++ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x2, 0); ++ else ++ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x2); ++ if (ret < 0) ++ return ret; ++ ++ if (priv->flag_hflip) ++ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0x1, 0); ++ else ++ ret = ov9740_reg_rmw(client, OV9740_IMAGE_ORT, 0, 0x1); ++ if (ret < 0) ++ return ret; ++ ++ if (enable) { ++ dev_dbg(&client->dev, "Enabling Streaming\n"); ++ /* Start Streaming */ ++ ret = ov9740_reg_write(client, OV9740_MODE_SELECT, 0x01); ++ ++ } else { ++ dev_dbg(&client->dev, "Disabling Streaming\n"); ++ /* Software Reset */ ++ ret = ov9740_reg_write(client, OV9740_SOFTWARE_RESET, 0x01); ++ if (!ret) ++ /* Setting Streaming to Standby */ ++ ret = ov9740_reg_write(client, OV9740_MODE_SELECT, ++ 0x00); ++ } ++ ++ priv->current_enable = enable; ++ ++ return ret; ++} ++ ++/* select nearest higher resolution for capture */ ++static void ov9740_res_roundup(u32 *width, u32 *height) ++{ ++ /* Width must be a multiple of 4 pixels. */ ++ *width = ALIGN(*width, 4); ++ ++ /* Max resolution is 1280x720 (720p). */ ++ if (*width > OV9740_MAX_WIDTH) ++ *width = OV9740_MAX_WIDTH; ++ ++ if (*height > OV9740_MAX_HEIGHT) ++ *height = OV9740_MAX_HEIGHT; ++} ++ ++/* Setup registers according to resolution and color encoding */ ++static int ov9740_set_res(struct i2c_client *client, u32 width, u32 height) ++{ ++ u32 x_start; ++ u32 y_start; ++ u32 x_end; ++ u32 y_end; ++ bool scaling = 0; ++ u32 scale_input_x; ++ u32 scale_input_y; ++ int ret; ++ ++ if ((width != OV9740_MAX_WIDTH) || (height != OV9740_MAX_HEIGHT)) ++ scaling = 1; ++ ++ /* ++ * Try to use as much of the sensor area as possible when supporting ++ * smaller resolutions. Depending on the aspect ratio of the ++ * chosen resolution, we can either use the full width of the sensor, ++ * or the full height of the sensor (or both if the aspect ratio is ++ * the same as 1280x720. ++ */ ++ if ((OV9740_MAX_WIDTH * height) > (OV9740_MAX_HEIGHT * width)) { ++ scale_input_x = (OV9740_MAX_HEIGHT * width) / height; ++ scale_input_y = OV9740_MAX_HEIGHT; ++ } else { ++ scale_input_x = OV9740_MAX_WIDTH; ++ scale_input_y = (OV9740_MAX_WIDTH * height) / width; ++ } ++ ++ /* These describe the area of the sensor to use. */ ++ x_start = (OV9740_MAX_WIDTH - scale_input_x) / 2; ++ y_start = (OV9740_MAX_HEIGHT - scale_input_y) / 2; ++ x_end = x_start + scale_input_x - 1; ++ y_end = y_start + scale_input_y - 1; ++ ++ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_HI, x_start >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_X_ADDR_START_LO, x_start & 0xff); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_HI, y_start >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_ADDR_START_LO, y_start & 0xff); ++ if (ret) ++ goto done; ++ ++ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_HI, x_end >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_X_ADDR_END_LO, x_end & 0xff); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_HI, y_end >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_ADDR_END_LO, y_end & 0xff); ++ if (ret) ++ goto done; ++ ++ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_HI, width >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_X_OUTPUT_SIZE_LO, width & 0xff); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_HI, height >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_Y_OUTPUT_SIZE_LO, height & 0xff); ++ if (ret) ++ goto done; ++ ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1E, scale_input_x >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL1F, scale_input_x & 0xff); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL20, scale_input_y >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL21, scale_input_y & 0xff); ++ if (ret) ++ goto done; ++ ++ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_HI, ++ (scale_input_x - width) >> 8); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_VFIFO_READ_START_LO, ++ (scale_input_x - width) & 0xff); ++ if (ret) ++ goto done; ++ ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL00, 0xff); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL01, 0xef | ++ (scaling << 4)); ++ if (ret) ++ goto done; ++ ret = ov9740_reg_write(client, OV9740_ISP_CTRL03, 0xff); ++ ++done: ++ return ret; ++} ++ ++/* set the format we will capture in */ ++static int ov9740_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct ov9740_priv *priv = to_ov9740(sd); ++ enum v4l2_colorspace cspace; ++ enum v4l2_mbus_pixelcode code = mf->code; ++ int ret; ++ ++ ov9740_res_roundup(&mf->width, &mf->height); ++ ++ switch (code) { ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ cspace = V4L2_COLORSPACE_SRGB; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ret = ov9740_reg_write_array(client, ov9740_defaults, ++ ARRAY_SIZE(ov9740_defaults)); ++ if (ret < 0) ++ return ret; ++ ++ ret = ov9740_set_res(client, mf->width, mf->height); ++ if (ret < 0) ++ return ret; ++ ++ mf->code = code; ++ mf->colorspace = cspace; ++ ++ memcpy(&priv->current_mf, mf, sizeof(struct v4l2_mbus_framefmt)); ++ ++ return ret; ++} ++ ++static int ov9740_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ ov9740_res_roundup(&mf->width, &mf->height); ++ ++ mf->field = V4L2_FIELD_NONE; ++ mf->code = V4L2_MBUS_FMT_YUYV8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_SRGB; ++ ++ return 0; ++} ++ ++static int ov9740_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(ov9740_codes)) ++ return -EINVAL; ++ ++ *code = ov9740_codes[index]; ++ ++ return 0; ++} ++ ++static int ov9740_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = OV9740_MAX_WIDTH; ++ a->bounds.height = OV9740_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int ov9740_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ a->c.left = 0; ++ a->c.top = 0; ++ a->c.width = OV9740_MAX_WIDTH; ++ a->c.height = OV9740_MAX_HEIGHT; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++/* Set status of additional camera capabilities */ ++static int ov9740_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ov9740_priv *priv = ++ container_of(ctrl->handler, struct ov9740_priv, hdl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ priv->flag_vflip = ctrl->val; ++ break; ++ case V4L2_CID_HFLIP: ++ priv->flag_hflip = ctrl->val; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* Get chip identification */ ++static int ov9740_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct ov9740_priv *priv = to_ov9740(sd); ++ ++ id->ident = priv->ident; ++ id->revision = priv->revision; ++ ++ return 0; ++} ++ ++static int ov9740_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct ov9740_priv *priv = to_ov9740(sd); ++ int ret; ++ ++ if (on) { ++ ret = soc_camera_power_on(&client->dev, ssdd); ++ if (ret < 0) ++ return ret; ++ ++ if (priv->current_enable) { ++ ov9740_s_fmt(sd, &priv->current_mf); ++ ov9740_s_stream(sd, 1); ++ } ++ } else { ++ if (priv->current_enable) { ++ ov9740_s_stream(sd, 0); ++ priv->current_enable = true; ++ } ++ ++ soc_camera_power_off(&client->dev, ssdd); ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ov9740_get_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ u8 val; ++ ++ if (reg->reg & ~0xffff) ++ return -EINVAL; ++ ++ reg->size = 2; ++ ++ ret = ov9740_reg_read(client, reg->reg, &val); ++ if (ret) ++ return ret; ++ ++ reg->val = (__u64)val; ++ ++ return ret; ++} ++ ++static int ov9740_set_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg & ~0xffff || reg->val & ~0xff) ++ return -EINVAL; ++ ++ return ov9740_reg_write(client, reg->reg, reg->val); ++} ++#endif ++ ++static int ov9740_video_probe(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct ov9740_priv *priv = to_ov9740(sd); ++ u8 modelhi, modello; ++ int ret; ++ ++ ret = ov9740_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show product ID and manufacturer ID ++ */ ++ ret = ov9740_reg_read(client, OV9740_MODEL_ID_HI, &modelhi); ++ if (ret < 0) ++ goto done; ++ ++ ret = ov9740_reg_read(client, OV9740_MODEL_ID_LO, &modello); ++ if (ret < 0) ++ goto done; ++ ++ priv->model = (modelhi << 8) | modello; ++ ++ ret = ov9740_reg_read(client, OV9740_REVISION_NUMBER, &priv->revision); ++ if (ret < 0) ++ goto done; ++ ++ ret = ov9740_reg_read(client, OV9740_MANUFACTURER_ID, &priv->manid); ++ if (ret < 0) ++ goto done; ++ ++ ret = ov9740_reg_read(client, OV9740_SMIA_VERSION, &priv->smiaver); ++ if (ret < 0) ++ goto done; ++ ++ if (priv->model != 0x9740) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ priv->ident = V4L2_IDENT_OV9740; ++ ++ dev_info(&client->dev, "ov9740 Model ID 0x%04x, Revision 0x%02x, " ++ "Manufacturer 0x%02x, SMIA Version 0x%02x\n", ++ priv->model, priv->revision, priv->manid, priv->smiaver); ++ ++ ret = v4l2_ctrl_handler_setup(&priv->hdl); ++ ++done: ++ ov9740_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++/* Request bus settings on camera side */ ++static int ov9740_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static struct v4l2_subdev_video_ops ov9740_video_ops = { ++ .s_stream = ov9740_s_stream, ++ .s_mbus_fmt = ov9740_s_fmt, ++ .try_mbus_fmt = ov9740_try_fmt, ++ .enum_mbus_fmt = ov9740_enum_fmt, ++ .cropcap = ov9740_cropcap, ++ .g_crop = ov9740_g_crop, ++ .g_mbus_config = ov9740_g_mbus_config, ++}; ++ ++static struct v4l2_subdev_core_ops ov9740_core_ops = { ++ .g_chip_ident = ov9740_g_chip_ident, ++ .s_power = ov9740_s_power, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = ov9740_get_register, ++ .s_register = ov9740_set_register, ++#endif ++}; ++ ++static struct v4l2_subdev_ops ov9740_subdev_ops = { ++ .core = &ov9740_core_ops, ++ .video = &ov9740_video_ops, ++}; ++ ++static const struct v4l2_ctrl_ops ov9740_ctrl_ops = { ++ .s_ctrl = ov9740_s_ctrl, ++}; ++ ++/* ++ * i2c_driver function ++ */ ++static int ov9740_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct ov9740_priv *priv; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ int ret; ++ ++ if (!ssdd) { ++ dev_err(&client->dev, "Missing platform_data for driver\n"); ++ return -EINVAL; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(struct ov9740_priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&client->dev, "Failed to allocate private data!\n"); ++ return -ENOMEM; ++ } ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &ov9740_subdev_ops); ++ v4l2_ctrl_handler_init(&priv->hdl, 13); ++ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&priv->hdl, &ov9740_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ priv->subdev.ctrl_handler = &priv->hdl; ++ if (priv->hdl.error) ++ return priv->hdl.error; ++ ++ ret = ov9740_video_probe(client); ++ if (ret < 0) ++ v4l2_ctrl_handler_free(&priv->hdl); ++ ++ return ret; ++} ++ ++static int ov9740_remove(struct i2c_client *client) ++{ ++ struct ov9740_priv *priv = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(&priv->subdev); ++ v4l2_ctrl_handler_free(&priv->hdl); ++ return 0; ++} ++ ++static const struct i2c_device_id ov9740_id[] = { ++ { "ov9740", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, ov9740_id); ++ ++static struct i2c_driver ov9740_i2c_driver = { ++ .driver = { ++ .name = "ov9740", ++ }, ++ .probe = ov9740_probe, ++ .remove = ov9740_remove, ++ .id_table = ov9740_id, ++}; ++ ++module_i2c_driver(ov9740_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740"); ++MODULE_AUTHOR("Andrew Chew "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/rj54n1cb0c.c +new file mode 100644 +index 0000000..5c92679 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/rj54n1cb0c.c +@@ -0,0 +1,1424 @@ ++/* ++ * Driver for RJ54N1CB0C CMOS Image Sensor from Sharp ++ * ++ * Copyright (C) 2009, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define RJ54N1_DEV_CODE 0x0400 ++#define RJ54N1_DEV_CODE2 0x0401 ++#define RJ54N1_OUT_SEL 0x0403 ++#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 ++#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 ++#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 ++#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 ++#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 ++#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 ++#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a ++#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b ++#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c ++#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d ++#define RJ54N1_RESIZE_N 0x040e ++#define RJ54N1_RESIZE_N_STEP 0x040f ++#define RJ54N1_RESIZE_STEP 0x0410 ++#define RJ54N1_RESIZE_HOLD_H 0x0411 ++#define RJ54N1_RESIZE_HOLD_L 0x0412 ++#define RJ54N1_H_OBEN_OFS 0x0413 ++#define RJ54N1_V_OBEN_OFS 0x0414 ++#define RJ54N1_RESIZE_CONTROL 0x0415 ++#define RJ54N1_STILL_CONTROL 0x0417 ++#define RJ54N1_INC_USE_SEL_H 0x0425 ++#define RJ54N1_INC_USE_SEL_L 0x0426 ++#define RJ54N1_MIRROR_STILL_MODE 0x0427 ++#define RJ54N1_INIT_START 0x0428 ++#define RJ54N1_SCALE_1_2_LEV 0x0429 ++#define RJ54N1_SCALE_4_LEV 0x042a ++#define RJ54N1_Y_GAIN 0x04d8 ++#define RJ54N1_APT_GAIN_UP 0x04fa ++#define RJ54N1_RA_SEL_UL 0x0530 ++#define RJ54N1_BYTE_SWAP 0x0531 ++#define RJ54N1_OUT_SIGPO 0x053b ++#define RJ54N1_WB_SEL_WEIGHT_I 0x054e ++#define RJ54N1_BIT8_WB 0x0569 ++#define RJ54N1_HCAPS_WB 0x056a ++#define RJ54N1_VCAPS_WB 0x056b ++#define RJ54N1_HCAPE_WB 0x056c ++#define RJ54N1_VCAPE_WB 0x056d ++#define RJ54N1_EXPOSURE_CONTROL 0x058c ++#define RJ54N1_FRAME_LENGTH_S_H 0x0595 ++#define RJ54N1_FRAME_LENGTH_S_L 0x0596 ++#define RJ54N1_FRAME_LENGTH_P_H 0x0597 ++#define RJ54N1_FRAME_LENGTH_P_L 0x0598 ++#define RJ54N1_PEAK_H 0x05b7 ++#define RJ54N1_PEAK_50 0x05b8 ++#define RJ54N1_PEAK_60 0x05b9 ++#define RJ54N1_PEAK_DIFF 0x05ba ++#define RJ54N1_IOC 0x05ef ++#define RJ54N1_TG_BYPASS 0x0700 ++#define RJ54N1_PLL_L 0x0701 ++#define RJ54N1_PLL_N 0x0702 ++#define RJ54N1_PLL_EN 0x0704 ++#define RJ54N1_RATIO_TG 0x0706 ++#define RJ54N1_RATIO_T 0x0707 ++#define RJ54N1_RATIO_R 0x0708 ++#define RJ54N1_RAMP_TGCLK_EN 0x0709 ++#define RJ54N1_OCLK_DSP 0x0710 ++#define RJ54N1_RATIO_OP 0x0711 ++#define RJ54N1_RATIO_O 0x0712 ++#define RJ54N1_OCLK_SEL_EN 0x0713 ++#define RJ54N1_CLK_RST 0x0717 ++#define RJ54N1_RESET_STANDBY 0x0718 ++#define RJ54N1_FWFLG 0x07fe ++ ++#define E_EXCLK (1 << 7) ++#define SOFT_STDBY (1 << 4) ++#define SEN_RSTX (1 << 2) ++#define TG_RSTX (1 << 1) ++#define DSP_RSTX (1 << 0) ++ ++#define RESIZE_HOLD_SEL (1 << 2) ++#define RESIZE_GO (1 << 1) ++ ++/* ++ * When cropping, the camera automatically centers the cropped region, there ++ * doesn't seem to be a way to specify an explicit location of the rectangle. ++ */ ++#define RJ54N1_COLUMN_SKIP 0 ++#define RJ54N1_ROW_SKIP 0 ++#define RJ54N1_MAX_WIDTH 1600 ++#define RJ54N1_MAX_HEIGHT 1200 ++ ++#define PLL_L 2 ++#define PLL_N 0x31 ++ ++/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ ++ ++/* RJ54N1CB0C has only one fixed colorspace per pixelcode */ ++struct rj54n1_datafmt { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++}; ++ ++/* Find a data format by a pixel code in an array */ ++static const struct rj54n1_datafmt *rj54n1_find_datafmt( ++ enum v4l2_mbus_pixelcode code, const struct rj54n1_datafmt *fmt, ++ int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) ++ if (fmt[i].code == code) ++ return fmt + i; ++ ++ return NULL; ++} ++ ++static const struct rj54n1_datafmt rj54n1_colour_fmts[] = { ++ {V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_YVYU8_2X8, V4L2_COLORSPACE_JPEG}, ++ {V4L2_MBUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, V4L2_COLORSPACE_SRGB}, ++ {V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_COLORSPACE_SRGB}, ++}; ++ ++struct rj54n1_clock_div { ++ u8 ratio_tg; /* can be 0 or an odd number */ ++ u8 ratio_t; ++ u8 ratio_r; ++ u8 ratio_op; ++ u8 ratio_o; ++}; ++ ++struct rj54n1 { ++ struct v4l2_subdev subdev; ++ struct v4l2_ctrl_handler hdl; ++ struct rj54n1_clock_div clk_div; ++ const struct rj54n1_datafmt *fmt; ++ struct v4l2_rect rect; /* Sensor window */ ++ unsigned int tgclk_mhz; ++ bool auto_wb; ++ unsigned short width; /* Output window */ ++ unsigned short height; ++ unsigned short resize; /* Sensor * 1024 / resize = Output */ ++ unsigned short scale; ++ u8 bank; ++}; ++ ++struct rj54n1_reg_val { ++ u16 reg; ++ u8 val; ++}; ++ ++static const struct rj54n1_reg_val bank_4[] = { ++ {0x417, 0}, ++ {0x42c, 0}, ++ {0x42d, 0xf0}, ++ {0x42e, 0}, ++ {0x42f, 0x50}, ++ {0x430, 0xf5}, ++ {0x431, 0x16}, ++ {0x432, 0x20}, ++ {0x433, 0}, ++ {0x434, 0xc8}, ++ {0x43c, 8}, ++ {0x43e, 0x90}, ++ {0x445, 0x83}, ++ {0x4ba, 0x58}, ++ {0x4bb, 4}, ++ {0x4bc, 0x20}, ++ {0x4db, 4}, ++ {0x4fe, 2}, ++}; ++ ++static const struct rj54n1_reg_val bank_5[] = { ++ {0x514, 0}, ++ {0x516, 0}, ++ {0x518, 0}, ++ {0x51a, 0}, ++ {0x51d, 0xff}, ++ {0x56f, 0x28}, ++ {0x575, 0x40}, ++ {0x5bc, 0x48}, ++ {0x5c1, 6}, ++ {0x5e5, 0x11}, ++ {0x5e6, 0x43}, ++ {0x5e7, 0x33}, ++ {0x5e8, 0x21}, ++ {0x5e9, 0x30}, ++ {0x5ea, 0x0}, ++ {0x5eb, 0xa5}, ++ {0x5ec, 0xff}, ++ {0x5fe, 2}, ++}; ++ ++static const struct rj54n1_reg_val bank_7[] = { ++ {0x70a, 0}, ++ {0x714, 0xff}, ++ {0x715, 0xff}, ++ {0x716, 0x1f}, ++ {0x7FE, 2}, ++}; ++ ++static const struct rj54n1_reg_val bank_8[] = { ++ {0x800, 0x00}, ++ {0x801, 0x01}, ++ {0x802, 0x61}, ++ {0x805, 0x00}, ++ {0x806, 0x00}, ++ {0x807, 0x00}, ++ {0x808, 0x00}, ++ {0x809, 0x01}, ++ {0x80A, 0x61}, ++ {0x80B, 0x00}, ++ {0x80C, 0x01}, ++ {0x80D, 0x00}, ++ {0x80E, 0x00}, ++ {0x80F, 0x00}, ++ {0x810, 0x00}, ++ {0x811, 0x01}, ++ {0x812, 0x61}, ++ {0x813, 0x00}, ++ {0x814, 0x11}, ++ {0x815, 0x00}, ++ {0x816, 0x41}, ++ {0x817, 0x00}, ++ {0x818, 0x51}, ++ {0x819, 0x01}, ++ {0x81A, 0x1F}, ++ {0x81B, 0x00}, ++ {0x81C, 0x01}, ++ {0x81D, 0x00}, ++ {0x81E, 0x11}, ++ {0x81F, 0x00}, ++ {0x820, 0x41}, ++ {0x821, 0x00}, ++ {0x822, 0x51}, ++ {0x823, 0x00}, ++ {0x824, 0x00}, ++ {0x825, 0x00}, ++ {0x826, 0x47}, ++ {0x827, 0x01}, ++ {0x828, 0x4F}, ++ {0x829, 0x00}, ++ {0x82A, 0x00}, ++ {0x82B, 0x00}, ++ {0x82C, 0x30}, ++ {0x82D, 0x00}, ++ {0x82E, 0x40}, ++ {0x82F, 0x00}, ++ {0x830, 0xB3}, ++ {0x831, 0x00}, ++ {0x832, 0xE3}, ++ {0x833, 0x00}, ++ {0x834, 0x00}, ++ {0x835, 0x00}, ++ {0x836, 0x00}, ++ {0x837, 0x00}, ++ {0x838, 0x00}, ++ {0x839, 0x01}, ++ {0x83A, 0x61}, ++ {0x83B, 0x00}, ++ {0x83C, 0x01}, ++ {0x83D, 0x00}, ++ {0x83E, 0x00}, ++ {0x83F, 0x00}, ++ {0x840, 0x00}, ++ {0x841, 0x01}, ++ {0x842, 0x61}, ++ {0x843, 0x00}, ++ {0x844, 0x1D}, ++ {0x845, 0x00}, ++ {0x846, 0x00}, ++ {0x847, 0x00}, ++ {0x848, 0x00}, ++ {0x849, 0x01}, ++ {0x84A, 0x1F}, ++ {0x84B, 0x00}, ++ {0x84C, 0x05}, ++ {0x84D, 0x00}, ++ {0x84E, 0x19}, ++ {0x84F, 0x01}, ++ {0x850, 0x21}, ++ {0x851, 0x01}, ++ {0x852, 0x5D}, ++ {0x853, 0x00}, ++ {0x854, 0x00}, ++ {0x855, 0x00}, ++ {0x856, 0x19}, ++ {0x857, 0x01}, ++ {0x858, 0x21}, ++ {0x859, 0x00}, ++ {0x85A, 0x00}, ++ {0x85B, 0x00}, ++ {0x85C, 0x00}, ++ {0x85D, 0x00}, ++ {0x85E, 0x00}, ++ {0x85F, 0x00}, ++ {0x860, 0xB3}, ++ {0x861, 0x00}, ++ {0x862, 0xE3}, ++ {0x863, 0x00}, ++ {0x864, 0x00}, ++ {0x865, 0x00}, ++ {0x866, 0x00}, ++ {0x867, 0x00}, ++ {0x868, 0x00}, ++ {0x869, 0xE2}, ++ {0x86A, 0x00}, ++ {0x86B, 0x01}, ++ {0x86C, 0x06}, ++ {0x86D, 0x00}, ++ {0x86E, 0x00}, ++ {0x86F, 0x00}, ++ {0x870, 0x60}, ++ {0x871, 0x8C}, ++ {0x872, 0x10}, ++ {0x873, 0x00}, ++ {0x874, 0xE0}, ++ {0x875, 0x00}, ++ {0x876, 0x27}, ++ {0x877, 0x01}, ++ {0x878, 0x00}, ++ {0x879, 0x00}, ++ {0x87A, 0x00}, ++ {0x87B, 0x03}, ++ {0x87C, 0x00}, ++ {0x87D, 0x00}, ++ {0x87E, 0x00}, ++ {0x87F, 0x00}, ++ {0x880, 0x00}, ++ {0x881, 0x00}, ++ {0x882, 0x00}, ++ {0x883, 0x00}, ++ {0x884, 0x00}, ++ {0x885, 0x00}, ++ {0x886, 0xF8}, ++ {0x887, 0x00}, ++ {0x888, 0x03}, ++ {0x889, 0x00}, ++ {0x88A, 0x64}, ++ {0x88B, 0x00}, ++ {0x88C, 0x03}, ++ {0x88D, 0x00}, ++ {0x88E, 0xB1}, ++ {0x88F, 0x00}, ++ {0x890, 0x03}, ++ {0x891, 0x01}, ++ {0x892, 0x1D}, ++ {0x893, 0x00}, ++ {0x894, 0x03}, ++ {0x895, 0x01}, ++ {0x896, 0x4B}, ++ {0x897, 0x00}, ++ {0x898, 0xE5}, ++ {0x899, 0x00}, ++ {0x89A, 0x01}, ++ {0x89B, 0x00}, ++ {0x89C, 0x01}, ++ {0x89D, 0x04}, ++ {0x89E, 0xC8}, ++ {0x89F, 0x00}, ++ {0x8A0, 0x01}, ++ {0x8A1, 0x01}, ++ {0x8A2, 0x61}, ++ {0x8A3, 0x00}, ++ {0x8A4, 0x01}, ++ {0x8A5, 0x00}, ++ {0x8A6, 0x00}, ++ {0x8A7, 0x00}, ++ {0x8A8, 0x00}, ++ {0x8A9, 0x00}, ++ {0x8AA, 0x7F}, ++ {0x8AB, 0x03}, ++ {0x8AC, 0x00}, ++ {0x8AD, 0x00}, ++ {0x8AE, 0x00}, ++ {0x8AF, 0x00}, ++ {0x8B0, 0x00}, ++ {0x8B1, 0x00}, ++ {0x8B6, 0x00}, ++ {0x8B7, 0x01}, ++ {0x8B8, 0x00}, ++ {0x8B9, 0x00}, ++ {0x8BA, 0x02}, ++ {0x8BB, 0x00}, ++ {0x8BC, 0xFF}, ++ {0x8BD, 0x00}, ++ {0x8FE, 2}, ++}; ++ ++static const struct rj54n1_reg_val bank_10[] = { ++ {0x10bf, 0x69} ++}; ++ ++/* Clock dividers - these are default register values, divider = register + 1 */ ++static const struct rj54n1_clock_div clk_div = { ++ .ratio_tg = 3 /* default: 5 */, ++ .ratio_t = 4 /* default: 1 */, ++ .ratio_r = 4 /* default: 0 */, ++ .ratio_op = 1 /* default: 5 */, ++ .ratio_o = 9 /* default: 0 */, ++}; ++ ++static struct rj54n1 *to_rj54n1(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); ++} ++ ++static int reg_read(struct i2c_client *client, const u16 reg) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ int ret; ++ ++ /* set bank */ ++ if (rj54n1->bank != reg >> 8) { ++ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); ++ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); ++ if (ret < 0) ++ return ret; ++ rj54n1->bank = reg >> 8; ++ } ++ return i2c_smbus_read_byte_data(client, reg & 0xff); ++} ++ ++static int reg_write(struct i2c_client *client, const u16 reg, ++ const u8 data) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ int ret; ++ ++ /* set bank */ ++ if (rj54n1->bank != reg >> 8) { ++ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); ++ ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); ++ if (ret < 0) ++ return ret; ++ rj54n1->bank = reg >> 8; ++ } ++ dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); ++ return i2c_smbus_write_byte_data(client, reg & 0xff, data); ++} ++ ++static int reg_set(struct i2c_client *client, const u16 reg, ++ const u8 data, const u8 mask) ++{ ++ int ret; ++ ++ ret = reg_read(client, reg); ++ if (ret < 0) ++ return ret; ++ return reg_write(client, reg, (ret & ~mask) | (data & mask)); ++} ++ ++static int reg_write_multiple(struct i2c_client *client, ++ const struct rj54n1_reg_val *rv, const int n) ++{ ++ int i, ret; ++ ++ for (i = 0; i < n; i++) { ++ ret = reg_write(client, rv->reg, rv->val); ++ if (ret < 0) ++ return ret; ++ rv++; ++ } ++ ++ return 0; ++} ++ ++static int rj54n1_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(rj54n1_colour_fmts)) ++ return -EINVAL; ++ ++ *code = rj54n1_colour_fmts[index].code; ++ return 0; ++} ++ ++static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* Switch between preview and still shot modes */ ++ return reg_set(client, RJ54N1_STILL_CONTROL, (!enable) << 7, 0x80); ++} ++ ++static int rj54n1_set_rect(struct i2c_client *client, ++ u16 reg_x, u16 reg_y, u16 reg_xy, ++ u32 width, u32 height) ++{ ++ int ret; ++ ++ ret = reg_write(client, reg_xy, ++ ((width >> 4) & 0x70) | ++ ((height >> 8) & 7)); ++ ++ if (!ret) ++ ret = reg_write(client, reg_x, width & 0xff); ++ if (!ret) ++ ret = reg_write(client, reg_y, height & 0xff); ++ ++ return ret; ++} ++ ++/* ++ * Some commands, specifically certain initialisation sequences, require ++ * a commit operation. ++ */ ++static int rj54n1_commit(struct i2c_client *client) ++{ ++ int ret = reg_write(client, RJ54N1_INIT_START, 1); ++ msleep(10); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_INIT_START, 0); ++ return ret; ++} ++ ++static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, ++ s32 *out_w, s32 *out_h); ++ ++static int rj54n1_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ const struct v4l2_rect *rect = &a->c; ++ int dummy = 0, output_w, output_h, ++ input_w = rect->width, input_h = rect->height; ++ int ret; ++ ++ /* arbitrary minimum width and height, edges unimportant */ ++ soc_camera_limit_side(&dummy, &input_w, ++ RJ54N1_COLUMN_SKIP, 8, RJ54N1_MAX_WIDTH); ++ ++ soc_camera_limit_side(&dummy, &input_h, ++ RJ54N1_ROW_SKIP, 8, RJ54N1_MAX_HEIGHT); ++ ++ output_w = (input_w * 1024 + rj54n1->resize / 2) / rj54n1->resize; ++ output_h = (input_h * 1024 + rj54n1->resize / 2) / rj54n1->resize; ++ ++ dev_dbg(&client->dev, "Scaling for %dx%d : %u = %dx%d\n", ++ input_w, input_h, rj54n1->resize, output_w, output_h); ++ ++ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); ++ if (ret < 0) ++ return ret; ++ ++ rj54n1->width = output_w; ++ rj54n1->height = output_h; ++ rj54n1->resize = ret; ++ rj54n1->rect.width = input_w; ++ rj54n1->rect.height = input_h; ++ ++ return 0; ++} ++ ++static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ ++ a->c = rj54n1->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ a->bounds.left = RJ54N1_COLUMN_SKIP; ++ a->bounds.top = RJ54N1_ROW_SKIP; ++ a->bounds.width = RJ54N1_MAX_WIDTH; ++ a->bounds.height = RJ54N1_MAX_HEIGHT; ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int rj54n1_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ ++ mf->code = rj54n1->fmt->code; ++ mf->colorspace = rj54n1->fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ mf->width = rj54n1->width; ++ mf->height = rj54n1->height; ++ ++ return 0; ++} ++ ++/* ++ * The actual geometry configuration routine. It scales the input window into ++ * the output one, updates the window sizes and returns an error or the resize ++ * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. ++ */ ++static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h, ++ s32 *out_w, s32 *out_h) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ unsigned int skip, resize, input_w = *in_w, input_h = *in_h, ++ output_w = *out_w, output_h = *out_h; ++ u16 inc_sel, wb_bit8, wb_left, wb_right, wb_top, wb_bottom; ++ unsigned int peak, peak_50, peak_60; ++ int ret; ++ ++ /* ++ * We have a problem with crops, where the window is larger than 512x384 ++ * and output window is larger than a half of the input one. In this ++ * case we have to either reduce the input window to equal or below ++ * 512x384 or the output window to equal or below 1/2 of the input. ++ */ ++ if (output_w > max(512U, input_w / 2)) { ++ if (2 * output_w > RJ54N1_MAX_WIDTH) { ++ input_w = RJ54N1_MAX_WIDTH; ++ output_w = RJ54N1_MAX_WIDTH / 2; ++ } else { ++ input_w = output_w * 2; ++ } ++ ++ dev_dbg(&client->dev, "Adjusted output width: in %u, out %u\n", ++ input_w, output_w); ++ } ++ ++ if (output_h > max(384U, input_h / 2)) { ++ if (2 * output_h > RJ54N1_MAX_HEIGHT) { ++ input_h = RJ54N1_MAX_HEIGHT; ++ output_h = RJ54N1_MAX_HEIGHT / 2; ++ } else { ++ input_h = output_h * 2; ++ } ++ ++ dev_dbg(&client->dev, "Adjusted output height: in %u, out %u\n", ++ input_h, output_h); ++ } ++ ++ /* Idea: use the read mode for snapshots, handle separate geometries */ ++ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, ++ RJ54N1_Y_OUTPUT_SIZE_S_L, ++ RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); ++ if (!ret) ++ ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, ++ RJ54N1_Y_OUTPUT_SIZE_P_L, ++ RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (output_w > input_w && output_h > input_h) { ++ input_w = output_w; ++ input_h = output_h; ++ ++ resize = 1024; ++ } else { ++ unsigned int resize_x, resize_y; ++ resize_x = (input_w * 1024 + output_w / 2) / output_w; ++ resize_y = (input_h * 1024 + output_h / 2) / output_h; ++ ++ /* We want max(resize_x, resize_y), check if it still fits */ ++ if (resize_x > resize_y && ++ (output_h * resize_x + 512) / 1024 > RJ54N1_MAX_HEIGHT) ++ resize = (RJ54N1_MAX_HEIGHT * 1024 + output_h / 2) / ++ output_h; ++ else if (resize_y > resize_x && ++ (output_w * resize_y + 512) / 1024 > RJ54N1_MAX_WIDTH) ++ resize = (RJ54N1_MAX_WIDTH * 1024 + output_w / 2) / ++ output_w; ++ else ++ resize = max(resize_x, resize_y); ++ ++ /* Prohibited value ranges */ ++ switch (resize) { ++ case 2040 ... 2047: ++ resize = 2039; ++ break; ++ case 4080 ... 4095: ++ resize = 4079; ++ break; ++ case 8160 ... 8191: ++ resize = 8159; ++ break; ++ case 16320 ... 16384: ++ resize = 16319; ++ } ++ } ++ ++ /* Set scaling */ ++ ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); ++ ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Configure a skipping bitmask. The sensor will select a skipping value ++ * among set bits automatically. This is very unclear in the datasheet ++ * too. I was told, in this register one enables all skipping values, ++ * that are required for a specific resize, and the camera selects ++ * automatically, which ones to use. But it is unclear how to identify, ++ * which cropping values are needed. Secondly, why don't we just set all ++ * bits and let the camera choose? Would it increase processing time and ++ * reduce the framerate? Using 0xfffc for INC_USE_SEL doesn't seem to ++ * improve the image quality or stability for larger frames (see comment ++ * above), but I didn't check the framerate. ++ */ ++ skip = min(resize / 1024, 15U); ++ ++ inc_sel = 1 << skip; ++ ++ if (inc_sel <= 2) ++ inc_sel = 0xc; ++ else if (resize & 1023 && skip < 15) ++ inc_sel |= 1 << (skip + 1); ++ ++ ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); ++ ++ if (!rj54n1->auto_wb) { ++ /* Auto white balance window */ ++ wb_left = output_w / 16; ++ wb_right = (3 * output_w / 4 - 3) / 4; ++ wb_top = output_h / 16; ++ wb_bottom = (3 * output_h / 4 - 3) / 4; ++ wb_bit8 = ((wb_left >> 2) & 0x40) | ((wb_top >> 4) & 0x10) | ++ ((wb_right >> 6) & 4) | ((wb_bottom >> 8) & 1); ++ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_BIT8_WB, wb_bit8); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_HCAPS_WB, wb_left); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_VCAPS_WB, wb_top); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_HCAPE_WB, wb_right); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_VCAPE_WB, wb_bottom); ++ } ++ ++ /* Antiflicker */ ++ peak = 12 * RJ54N1_MAX_WIDTH * (1 << 14) * resize / rj54n1->tgclk_mhz / ++ 10000; ++ peak_50 = peak / 6; ++ peak_60 = peak / 5; ++ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PEAK_H, ++ ((peak_50 >> 4) & 0xf0) | (peak_60 >> 8)); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PEAK_50, peak_50); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PEAK_60, peak_60); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PEAK_DIFF, peak / 150); ++ ++ /* Start resizing */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, ++ RESIZE_HOLD_SEL | RESIZE_GO | 1); ++ ++ if (ret < 0) ++ return ret; ++ ++ /* Constant taken from manufacturer's example */ ++ msleep(230); ++ ++ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); ++ if (ret < 0) ++ return ret; ++ ++ *in_w = (output_w * resize + 512) / 1024; ++ *in_h = (output_h * resize + 512) / 1024; ++ *out_w = output_w; ++ *out_h = output_h; ++ ++ dev_dbg(&client->dev, "Scaled for %dx%d : %u = %ux%u, skip %u\n", ++ *in_w, *in_h, resize, output_w, output_h, skip); ++ ++ return resize; ++} ++ ++static int rj54n1_set_clock(struct i2c_client *client) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ int ret; ++ ++ /* Enable external clock */ ++ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); ++ /* Leave stand-by. Note: use this when implementing suspend / resume */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); ++ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PLL_L, PLL_L); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PLL_N, PLL_N); ++ ++ /* TGCLK dividers */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RATIO_TG, ++ rj54n1->clk_div.ratio_tg); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RATIO_T, ++ rj54n1->clk_div.ratio_t); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RATIO_R, ++ rj54n1->clk_div.ratio_r); ++ ++ /* Enable TGCLK & RAMP */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); ++ ++ /* Disable clock output */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_OCLK_DSP, 0); ++ ++ /* Set divisors */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RATIO_OP, ++ rj54n1->clk_div.ratio_op); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RATIO_O, ++ rj54n1->clk_div.ratio_o); ++ ++ /* Enable OCLK */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); ++ ++ /* Use PLL for Timing Generator, write 2 to reserved bits */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_TG_BYPASS, 2); ++ ++ /* Take sensor out of reset */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESET_STANDBY, ++ E_EXCLK | SEN_RSTX); ++ /* Enable PLL */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_PLL_EN, 1); ++ ++ /* Wait for PLL to stabilise */ ++ msleep(10); ++ ++ /* Enable clock to frequency divider */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_CLK_RST, 1); ++ ++ if (!ret) ++ ret = reg_read(client, RJ54N1_CLK_RST); ++ if (ret != 1) { ++ dev_err(&client->dev, ++ "Resetting RJ54N1CB0C clock failed: %d!\n", ret); ++ return -EIO; ++ } ++ ++ /* Start the PLL */ ++ ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); ++ ++ /* Enable OCLK */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); ++ ++ return ret; ++} ++ ++static int rj54n1_reg_init(struct i2c_client *client) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ int ret = rj54n1_set_clock(client); ++ ++ if (!ret) ++ ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); ++ if (!ret) ++ ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); ++ ++ /* Set binning divisors */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); ++ ++ /* Switch to fixed resize mode */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESIZE_CONTROL, ++ RESIZE_HOLD_SEL | 1); ++ ++ /* Set gain */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); ++ ++ /* ++ * Mirror the image back: default is upside down and left-to-right... ++ * Set manual preview / still shot switching ++ */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_MIRROR_STILL_MODE, 0x27); ++ ++ if (!ret) ++ ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); ++ ++ /* Auto exposure area */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_EXPOSURE_CONTROL, 0x80); ++ /* Check current auto WB config */ ++ if (!ret) ++ ret = reg_read(client, RJ54N1_WB_SEL_WEIGHT_I); ++ if (ret >= 0) { ++ rj54n1->auto_wb = ret & 0x80; ++ ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); ++ } ++ if (!ret) ++ ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); ++ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESET_STANDBY, ++ E_EXCLK | DSP_RSTX | SEN_RSTX); ++ ++ /* Commit init */ ++ if (!ret) ++ ret = rj54n1_commit(client); ++ ++ /* Take DSP, TG, sensor out of reset */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RESET_STANDBY, ++ E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); ++ ++ /* Start register update? Same register as 0x?FE in many bank_* sets */ ++ if (!ret) ++ ret = reg_write(client, RJ54N1_FWFLG, 2); ++ ++ /* Constant taken from manufacturer's example */ ++ msleep(700); ++ ++ return ret; ++} ++ ++static int rj54n1_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ const struct rj54n1_datafmt *fmt; ++ int align = mf->code == V4L2_MBUS_FMT_SBGGR10_1X10 || ++ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE || ++ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE || ++ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE || ++ mf->code == V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE; ++ ++ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", ++ __func__, mf->code, mf->width, mf->height); ++ ++ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, ++ ARRAY_SIZE(rj54n1_colour_fmts)); ++ if (!fmt) { ++ fmt = rj54n1->fmt; ++ mf->code = fmt->code; ++ } ++ ++ mf->field = V4L2_FIELD_NONE; ++ mf->colorspace = fmt->colorspace; ++ ++ v4l_bound_align_image(&mf->width, 112, RJ54N1_MAX_WIDTH, align, ++ &mf->height, 84, RJ54N1_MAX_HEIGHT, align, 0); ++ ++ return 0; ++} ++ ++static int rj54n1_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ const struct rj54n1_datafmt *fmt; ++ int output_w, output_h, max_w, max_h, ++ input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; ++ int ret; ++ ++ /* ++ * The host driver can call us without .try_fmt(), so, we have to take ++ * care ourseleves ++ */ ++ rj54n1_try_fmt(sd, mf); ++ ++ /* ++ * Verify if the sensor has just been powered on. TODO: replace this ++ * with proper PM, when a suitable API is available. ++ */ ++ ret = reg_read(client, RJ54N1_RESET_STANDBY); ++ if (ret < 0) ++ return ret; ++ ++ if (!(ret & E_EXCLK)) { ++ ret = rj54n1_reg_init(client); ++ if (ret < 0) ++ return ret; ++ } ++ ++ dev_dbg(&client->dev, "%s: code = %d, width = %u, height = %u\n", ++ __func__, mf->code, mf->width, mf->height); ++ ++ /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ ++ switch (mf->code) { ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 0); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); ++ break; ++ case V4L2_MBUS_FMT_YVYU8_2X8: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 0); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_BE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 4); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 4); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 4); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RA_SEL_UL, 0); ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 4); ++ if (!ret) ++ ret = reg_set(client, RJ54N1_BYTE_SWAP, 0, 8); ++ if (!ret) ++ ret = reg_write(client, RJ54N1_RA_SEL_UL, 8); ++ break; ++ case V4L2_MBUS_FMT_SBGGR10_1X10: ++ ret = reg_write(client, RJ54N1_OUT_SEL, 5); ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ /* Special case: a raw mode with 10 bits of data per clock tick */ ++ if (!ret) ++ ret = reg_set(client, RJ54N1_OCLK_SEL_EN, ++ (mf->code == V4L2_MBUS_FMT_SBGGR10_1X10) << 1, 2); ++ ++ if (ret < 0) ++ return ret; ++ ++ /* Supported scales 1:1 >= scale > 1:16 */ ++ max_w = mf->width * (16 * 1024 - 1) / 1024; ++ if (input_w > max_w) ++ input_w = max_w; ++ max_h = mf->height * (16 * 1024 - 1) / 1024; ++ if (input_h > max_h) ++ input_h = max_h; ++ ++ output_w = mf->width; ++ output_h = mf->height; ++ ++ ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); ++ if (ret < 0) ++ return ret; ++ ++ fmt = rj54n1_find_datafmt(mf->code, rj54n1_colour_fmts, ++ ARRAY_SIZE(rj54n1_colour_fmts)); ++ ++ rj54n1->fmt = fmt; ++ rj54n1->resize = ret; ++ rj54n1->rect.width = input_w; ++ rj54n1->rect.height = input_h; ++ rj54n1->width = output_w; ++ rj54n1->height = output_h; ++ ++ mf->width = output_w; ++ mf->height = output_h; ++ mf->field = V4L2_FIELD_NONE; ++ mf->colorspace = fmt->colorspace; ++ ++ return 0; ++} ++ ++static int rj54n1_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ if (id->match.addr != client->addr) ++ return -ENODEV; ++ ++ id->ident = V4L2_IDENT_RJ54N1CB0C; ++ id->revision = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int rj54n1_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || ++ reg->reg < 0x400 || reg->reg > 0x1fff) ++ /* Registers > 0x0800 are only available from Sharp support */ ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ reg->size = 1; ++ reg->val = reg_read(client, reg->reg); ++ ++ if (reg->val > 0xff) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int rj54n1_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || ++ reg->reg < 0x400 || reg->reg > 0x1fff) ++ /* Registers >= 0x0800 are only available from Sharp support */ ++ return -EINVAL; ++ ++ if (reg->match.addr != client->addr) ++ return -ENODEV; ++ ++ if (reg_write(client, reg->reg, reg->val) < 0) ++ return -EIO; ++ ++ return 0; ++} ++#endif ++ ++static int rj54n1_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct rj54n1 *rj54n1 = container_of(ctrl->handler, struct rj54n1, hdl); ++ struct v4l2_subdev *sd = &rj54n1->subdev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); ++ else ++ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); ++ else ++ data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); ++ if (data < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_GAIN: ++ if (reg_write(client, RJ54N1_Y_GAIN, ctrl->val * 2) < 0) ++ return -EIO; ++ return 0; ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ /* Auto WB area - whole image */ ++ if (reg_set(client, RJ54N1_WB_SEL_WEIGHT_I, ctrl->val << 7, ++ 0x80) < 0) ++ return -EIO; ++ rj54n1->auto_wb = ctrl->val; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static const struct v4l2_ctrl_ops rj54n1_ctrl_ops = { ++ .s_ctrl = rj54n1_s_ctrl, ++}; ++ ++static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { ++ .g_chip_ident = rj54n1_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = rj54n1_g_register, ++ .s_register = rj54n1_s_register, ++#endif ++ .s_power = rj54n1_s_power, ++}; ++ ++static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = ++ V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | ++ V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ ++ if (soc_camera_apply_board_flags(ssdd, cfg) & ++ V4L2_MBUS_PCLK_SAMPLE_RISING) ++ return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); ++ else ++ return reg_write(client, RJ54N1_OUT_SIGPO, 0); ++} ++ ++static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { ++ .s_stream = rj54n1_s_stream, ++ .s_mbus_fmt = rj54n1_s_fmt, ++ .g_mbus_fmt = rj54n1_g_fmt, ++ .try_mbus_fmt = rj54n1_try_fmt, ++ .enum_mbus_fmt = rj54n1_enum_fmt, ++ .g_crop = rj54n1_g_crop, ++ .s_crop = rj54n1_s_crop, ++ .cropcap = rj54n1_cropcap, ++ .g_mbus_config = rj54n1_g_mbus_config, ++ .s_mbus_config = rj54n1_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops rj54n1_subdev_ops = { ++ .core = &rj54n1_subdev_core_ops, ++ .video = &rj54n1_subdev_video_ops, ++}; ++ ++/* ++ * Interface active, can use i2c. If it fails, it can indeed mean, that ++ * this wasn't our capture interface, so, we wait for the right one ++ */ ++static int rj54n1_video_probe(struct i2c_client *client, ++ struct rj54n1_pdata *priv) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ int data1, data2; ++ int ret; ++ ++ ret = rj54n1_s_power(&rj54n1->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* Read out the chip version register */ ++ data1 = reg_read(client, RJ54N1_DEV_CODE); ++ data2 = reg_read(client, RJ54N1_DEV_CODE2); ++ ++ if (data1 != 0x51 || data2 != 0x10) { ++ ret = -ENODEV; ++ dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", ++ data1, data2); ++ goto done; ++ } ++ ++ /* Configure IOCTL polarity from the platform data: 0 or 1 << 7. */ ++ ret = reg_write(client, RJ54N1_IOC, priv->ioctl_high << 7); ++ if (ret < 0) ++ goto done; ++ ++ dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", ++ data1, data2); ++ ++ ret = v4l2_ctrl_handler_setup(&rj54n1->hdl); ++ ++done: ++ rj54n1_s_power(&rj54n1->subdev, 0); ++ return ret; ++} ++ ++static int rj54n1_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct rj54n1 *rj54n1; ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); ++ struct rj54n1_pdata *rj54n1_priv; ++ int ret; ++ ++ if (!ssdd || !ssdd->drv_priv) { ++ dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ rj54n1_priv = ssdd->drv_priv; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_warn(&adapter->dev, ++ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); ++ return -EIO; ++ } ++ ++ rj54n1 = devm_kzalloc(&client->dev, sizeof(struct rj54n1), GFP_KERNEL); ++ if (!rj54n1) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); ++ v4l2_ctrl_handler_init(&rj54n1->hdl, 4); ++ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, ++ V4L2_CID_GAIN, 0, 127, 1, 66); ++ v4l2_ctrl_new_std(&rj54n1->hdl, &rj54n1_ctrl_ops, ++ V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); ++ rj54n1->subdev.ctrl_handler = &rj54n1->hdl; ++ if (rj54n1->hdl.error) ++ return rj54n1->hdl.error; ++ ++ rj54n1->clk_div = clk_div; ++ rj54n1->rect.left = RJ54N1_COLUMN_SKIP; ++ rj54n1->rect.top = RJ54N1_ROW_SKIP; ++ rj54n1->rect.width = RJ54N1_MAX_WIDTH; ++ rj54n1->rect.height = RJ54N1_MAX_HEIGHT; ++ rj54n1->width = RJ54N1_MAX_WIDTH; ++ rj54n1->height = RJ54N1_MAX_HEIGHT; ++ rj54n1->fmt = &rj54n1_colour_fmts[0]; ++ rj54n1->resize = 1024; ++ rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / ++ (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); ++ ++ ret = rj54n1_video_probe(client, rj54n1_priv); ++ if (ret < 0) ++ v4l2_ctrl_handler_free(&rj54n1->hdl); ++ ++ return ret; ++} ++ ++static int rj54n1_remove(struct i2c_client *client) ++{ ++ struct rj54n1 *rj54n1 = to_rj54n1(client); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ v4l2_device_unregister_subdev(&rj54n1->subdev); ++ if (ssdd->free_bus) ++ ssdd->free_bus(ssdd); ++ v4l2_ctrl_handler_free(&rj54n1->hdl); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id rj54n1_id[] = { ++ { "rj54n1cb0c", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, rj54n1_id); ++ ++static struct i2c_driver rj54n1_i2c_driver = { ++ .driver = { ++ .name = "rj54n1cb0c", ++ }, ++ .probe = rj54n1_probe, ++ .remove = rj54n1_remove, ++ .id_table = rj54n1_id, ++}; ++ ++module_i2c_driver(rj54n1_i2c_driver); ++ ++MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); ++MODULE_AUTHOR("Guennadi Liakhovetski "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/tw9910.c +new file mode 100644 +index 0000000..7d20746 +--- /dev/null ++++ b/drivers/media/i2c/soc_camera/tw9910.c +@@ -0,0 +1,965 @@ ++/* ++ * tw9910 Video Driver ++ * ++ * Copyright (C) 2008 Renesas Solutions Corp. ++ * Kuninori Morimoto ++ * ++ * Based on ov772x driver, ++ * ++ * Copyright (C) 2008 Kuninori Morimoto ++ * Copyright 2006-7 Jonathan Corbet ++ * Copyright (C) 2008 Magnus Damm ++ * Copyright (C) 2008, Guennadi Liakhovetski ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define GET_ID(val) ((val & 0xF8) >> 3) ++#define GET_REV(val) (val & 0x07) ++ ++/* ++ * register offset ++ */ ++#define ID 0x00 /* Product ID Code Register */ ++#define STATUS1 0x01 /* Chip Status Register I */ ++#define INFORM 0x02 /* Input Format */ ++#define OPFORM 0x03 /* Output Format Control Register */ ++#define DLYCTR 0x04 /* Hysteresis and HSYNC Delay Control */ ++#define OUTCTR1 0x05 /* Output Control I */ ++#define ACNTL1 0x06 /* Analog Control Register 1 */ ++#define CROP_HI 0x07 /* Cropping Register, High */ ++#define VDELAY_LO 0x08 /* Vertical Delay Register, Low */ ++#define VACTIVE_LO 0x09 /* Vertical Active Register, Low */ ++#define HDELAY_LO 0x0A /* Horizontal Delay Register, Low */ ++#define HACTIVE_LO 0x0B /* Horizontal Active Register, Low */ ++#define CNTRL1 0x0C /* Control Register I */ ++#define VSCALE_LO 0x0D /* Vertical Scaling Register, Low */ ++#define SCALE_HI 0x0E /* Scaling Register, High */ ++#define HSCALE_LO 0x0F /* Horizontal Scaling Register, Low */ ++#define BRIGHT 0x10 /* BRIGHTNESS Control Register */ ++#define CONTRAST 0x11 /* CONTRAST Control Register */ ++#define SHARPNESS 0x12 /* SHARPNESS Control Register I */ ++#define SAT_U 0x13 /* Chroma (U) Gain Register */ ++#define SAT_V 0x14 /* Chroma (V) Gain Register */ ++#define HUE 0x15 /* Hue Control Register */ ++#define CORING1 0x17 ++#define CORING2 0x18 /* Coring and IF compensation */ ++#define VBICNTL 0x19 /* VBI Control Register */ ++#define ACNTL2 0x1A /* Analog Control 2 */ ++#define OUTCTR2 0x1B /* Output Control 2 */ ++#define SDT 0x1C /* Standard Selection */ ++#define SDTR 0x1D /* Standard Recognition */ ++#define TEST 0x1F /* Test Control Register */ ++#define CLMPG 0x20 /* Clamping Gain */ ++#define IAGC 0x21 /* Individual AGC Gain */ ++#define AGCGAIN 0x22 /* AGC Gain */ ++#define PEAKWT 0x23 /* White Peak Threshold */ ++#define CLMPL 0x24 /* Clamp level */ ++#define SYNCT 0x25 /* Sync Amplitude */ ++#define MISSCNT 0x26 /* Sync Miss Count Register */ ++#define PCLAMP 0x27 /* Clamp Position Register */ ++#define VCNTL1 0x28 /* Vertical Control I */ ++#define VCNTL2 0x29 /* Vertical Control II */ ++#define CKILL 0x2A /* Color Killer Level Control */ ++#define COMB 0x2B /* Comb Filter Control */ ++#define LDLY 0x2C /* Luma Delay and H Filter Control */ ++#define MISC1 0x2D /* Miscellaneous Control I */ ++#define LOOP 0x2E /* LOOP Control Register */ ++#define MISC2 0x2F /* Miscellaneous Control II */ ++#define MVSN 0x30 /* Macrovision Detection */ ++#define STATUS2 0x31 /* Chip STATUS II */ ++#define HFREF 0x32 /* H monitor */ ++#define CLMD 0x33 /* CLAMP MODE */ ++#define IDCNTL 0x34 /* ID Detection Control */ ++#define CLCNTL1 0x35 /* Clamp Control I */ ++#define ANAPLLCTL 0x4C ++#define VBIMIN 0x4D ++#define HSLOWCTL 0x4E ++#define WSS3 0x4F ++#define FILLDATA 0x50 ++#define SDID 0x51 ++#define DID 0x52 ++#define WSS1 0x53 ++#define WSS2 0x54 ++#define VVBI 0x55 ++#define LCTL6 0x56 ++#define LCTL7 0x57 ++#define LCTL8 0x58 ++#define LCTL9 0x59 ++#define LCTL10 0x5A ++#define LCTL11 0x5B ++#define LCTL12 0x5C ++#define LCTL13 0x5D ++#define LCTL14 0x5E ++#define LCTL15 0x5F ++#define LCTL16 0x60 ++#define LCTL17 0x61 ++#define LCTL18 0x62 ++#define LCTL19 0x63 ++#define LCTL20 0x64 ++#define LCTL21 0x65 ++#define LCTL22 0x66 ++#define LCTL23 0x67 ++#define LCTL24 0x68 ++#define LCTL25 0x69 ++#define LCTL26 0x6A ++#define HSBEGIN 0x6B ++#define HSEND 0x6C ++#define OVSDLY 0x6D ++#define OVSEND 0x6E ++#define VBIDELAY 0x6F ++ ++/* ++ * register detail ++ */ ++ ++/* INFORM */ ++#define FC27_ON 0x40 /* 1 : Input crystal clock frequency is 27MHz */ ++#define FC27_FF 0x00 /* 0 : Square pixel mode. */ ++ /* Must use 24.54MHz for 60Hz field rate */ ++ /* source or 29.5MHz for 50Hz field rate */ ++#define IFSEL_S 0x10 /* 01 : S-video decoding */ ++#define IFSEL_C 0x00 /* 00 : Composite video decoding */ ++ /* Y input video selection */ ++#define YSEL_M0 0x00 /* 00 : Mux0 selected */ ++#define YSEL_M1 0x04 /* 01 : Mux1 selected */ ++#define YSEL_M2 0x08 /* 10 : Mux2 selected */ ++#define YSEL_M3 0x10 /* 11 : Mux3 selected */ ++ ++/* OPFORM */ ++#define MODE 0x80 /* 0 : CCIR601 compatible YCrCb 4:2:2 format */ ++ /* 1 : ITU-R-656 compatible data sequence format */ ++#define LEN 0x40 /* 0 : 8-bit YCrCb 4:2:2 output format */ ++ /* 1 : 16-bit YCrCb 4:2:2 output format.*/ ++#define LLCMODE 0x20 /* 1 : LLC output mode. */ ++ /* 0 : free-run output mode */ ++#define AINC 0x10 /* Serial interface auto-indexing control */ ++ /* 0 : auto-increment */ ++ /* 1 : non-auto */ ++#define VSCTL 0x08 /* 1 : Vertical out ctrl by DVALID */ ++ /* 0 : Vertical out ctrl by HACTIVE and DVALID */ ++#define OEN_TRI_SEL_MASK 0x07 ++#define OEN_TRI_SEL_ALL_ON 0x00 /* Enable output for Rev0/Rev1 */ ++#define OEN_TRI_SEL_ALL_OFF_r0 0x06 /* All tri-stated for Rev0 */ ++#define OEN_TRI_SEL_ALL_OFF_r1 0x07 /* All tri-stated for Rev1 */ ++ ++/* OUTCTR1 */ ++#define VSP_LO 0x00 /* 0 : VS pin output polarity is active low */ ++#define VSP_HI 0x80 /* 1 : VS pin output polarity is active high. */ ++ /* VS pin output control */ ++#define VSSL_VSYNC 0x00 /* 0 : VSYNC */ ++#define VSSL_VACT 0x10 /* 1 : VACT */ ++#define VSSL_FIELD 0x20 /* 2 : FIELD */ ++#define VSSL_VVALID 0x30 /* 3 : VVALID */ ++#define VSSL_ZERO 0x70 /* 7 : 0 */ ++#define HSP_LOW 0x00 /* 0 : HS pin output polarity is active low */ ++#define HSP_HI 0x08 /* 1 : HS pin output polarity is active high.*/ ++ /* HS pin output control */ ++#define HSSL_HACT 0x00 /* 0 : HACT */ ++#define HSSL_HSYNC 0x01 /* 1 : HSYNC */ ++#define HSSL_DVALID 0x02 /* 2 : DVALID */ ++#define HSSL_HLOCK 0x03 /* 3 : HLOCK */ ++#define HSSL_ASYNCW 0x04 /* 4 : ASYNCW */ ++#define HSSL_ZERO 0x07 /* 7 : 0 */ ++ ++/* ACNTL1 */ ++#define SRESET 0x80 /* resets the device to its default state ++ * but all register content remain unchanged. ++ * This bit is self-resetting. ++ */ ++#define ACNTL1_PDN_MASK 0x0e ++#define CLK_PDN 0x08 /* system clock power down */ ++#define Y_PDN 0x04 /* Luma ADC power down */ ++#define C_PDN 0x02 /* Chroma ADC power down */ ++ ++/* ACNTL2 */ ++#define ACNTL2_PDN_MASK 0x40 ++#define PLL_PDN 0x40 /* PLL power down */ ++ ++/* VBICNTL */ ++ ++/* RTSEL : control the real time signal output from the MPOUT pin */ ++#define RTSEL_MASK 0x07 ++#define RTSEL_VLOSS 0x00 /* 0000 = Video loss */ ++#define RTSEL_HLOCK 0x01 /* 0001 = H-lock */ ++#define RTSEL_SLOCK 0x02 /* 0010 = S-lock */ ++#define RTSEL_VLOCK 0x03 /* 0011 = V-lock */ ++#define RTSEL_MONO 0x04 /* 0100 = MONO */ ++#define RTSEL_DET50 0x05 /* 0101 = DET50 */ ++#define RTSEL_FIELD 0x06 /* 0110 = FIELD */ ++#define RTSEL_RTCO 0x07 /* 0111 = RTCO ( Real Time Control ) */ ++ ++/* HSYNC start and end are constant for now */ ++#define HSYNC_START 0x0260 ++#define HSYNC_END 0x0300 ++ ++/* ++ * structure ++ */ ++ ++struct regval_list { ++ unsigned char reg_num; ++ unsigned char value; ++}; ++ ++struct tw9910_scale_ctrl { ++ char *name; ++ unsigned short width; ++ unsigned short height; ++ u16 hscale; ++ u16 vscale; ++}; ++ ++struct tw9910_priv { ++ struct v4l2_subdev subdev; ++ struct tw9910_video_info *info; ++ const struct tw9910_scale_ctrl *scale; ++ v4l2_std_id norm; ++ u32 revision; ++}; ++ ++static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { ++ { ++ .name = "NTSC SQ", ++ .width = 640, ++ .height = 480, ++ .hscale = 0x0100, ++ .vscale = 0x0100, ++ }, ++ { ++ .name = "NTSC CCIR601", ++ .width = 720, ++ .height = 480, ++ .hscale = 0x0100, ++ .vscale = 0x0100, ++ }, ++ { ++ .name = "NTSC SQ (CIF)", ++ .width = 320, ++ .height = 240, ++ .hscale = 0x0200, ++ .vscale = 0x0200, ++ }, ++ { ++ .name = "NTSC CCIR601 (CIF)", ++ .width = 360, ++ .height = 240, ++ .hscale = 0x0200, ++ .vscale = 0x0200, ++ }, ++ { ++ .name = "NTSC SQ (QCIF)", ++ .width = 160, ++ .height = 120, ++ .hscale = 0x0400, ++ .vscale = 0x0400, ++ }, ++ { ++ .name = "NTSC CCIR601 (QCIF)", ++ .width = 180, ++ .height = 120, ++ .hscale = 0x0400, ++ .vscale = 0x0400, ++ }, ++}; ++ ++static const struct tw9910_scale_ctrl tw9910_pal_scales[] = { ++ { ++ .name = "PAL SQ", ++ .width = 768, ++ .height = 576, ++ .hscale = 0x0100, ++ .vscale = 0x0100, ++ }, ++ { ++ .name = "PAL CCIR601", ++ .width = 720, ++ .height = 576, ++ .hscale = 0x0100, ++ .vscale = 0x0100, ++ }, ++ { ++ .name = "PAL SQ (CIF)", ++ .width = 384, ++ .height = 288, ++ .hscale = 0x0200, ++ .vscale = 0x0200, ++ }, ++ { ++ .name = "PAL CCIR601 (CIF)", ++ .width = 360, ++ .height = 288, ++ .hscale = 0x0200, ++ .vscale = 0x0200, ++ }, ++ { ++ .name = "PAL SQ (QCIF)", ++ .width = 192, ++ .height = 144, ++ .hscale = 0x0400, ++ .vscale = 0x0400, ++ }, ++ { ++ .name = "PAL CCIR601 (QCIF)", ++ .width = 180, ++ .height = 144, ++ .hscale = 0x0400, ++ .vscale = 0x0400, ++ }, ++}; ++ ++/* ++ * general function ++ */ ++static struct tw9910_priv *to_tw9910(const struct i2c_client *client) ++{ ++ return container_of(i2c_get_clientdata(client), struct tw9910_priv, ++ subdev); ++} ++ ++static int tw9910_mask_set(struct i2c_client *client, u8 command, ++ u8 mask, u8 set) ++{ ++ s32 val = i2c_smbus_read_byte_data(client, command); ++ if (val < 0) ++ return val; ++ ++ val &= ~mask; ++ val |= set & mask; ++ ++ return i2c_smbus_write_byte_data(client, command, val); ++} ++ ++static int tw9910_set_scale(struct i2c_client *client, ++ const struct tw9910_scale_ctrl *scale) ++{ ++ int ret; ++ ++ ret = i2c_smbus_write_byte_data(client, SCALE_HI, ++ (scale->vscale & 0x0F00) >> 4 | ++ (scale->hscale & 0x0F00) >> 8); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, HSCALE_LO, ++ scale->hscale & 0x00FF); ++ if (ret < 0) ++ return ret; ++ ++ ret = i2c_smbus_write_byte_data(client, VSCALE_LO, ++ scale->vscale & 0x00FF); ++ ++ return ret; ++} ++ ++static int tw9910_set_hsync(struct i2c_client *client) ++{ ++ struct tw9910_priv *priv = to_tw9910(client); ++ int ret; ++ ++ /* bit 10 - 3 */ ++ ret = i2c_smbus_write_byte_data(client, HSBEGIN, ++ (HSYNC_START & 0x07F8) >> 3); ++ if (ret < 0) ++ return ret; ++ ++ /* bit 10 - 3 */ ++ ret = i2c_smbus_write_byte_data(client, HSEND, ++ (HSYNC_END & 0x07F8) >> 3); ++ if (ret < 0) ++ return ret; ++ ++ /* So far only revisions 0 and 1 have been seen */ ++ /* bit 2 - 0 */ ++ if (1 == priv->revision) ++ ret = tw9910_mask_set(client, HSLOWCTL, 0x77, ++ (HSYNC_START & 0x0007) << 4 | ++ (HSYNC_END & 0x0007)); ++ ++ return ret; ++} ++ ++static void tw9910_reset(struct i2c_client *client) ++{ ++ tw9910_mask_set(client, ACNTL1, SRESET, SRESET); ++ msleep(1); ++} ++ ++static int tw9910_power(struct i2c_client *client, int enable) ++{ ++ int ret; ++ u8 acntl1; ++ u8 acntl2; ++ ++ if (enable) { ++ acntl1 = 0; ++ acntl2 = 0; ++ } else { ++ acntl1 = CLK_PDN | Y_PDN | C_PDN; ++ acntl2 = PLL_PDN; ++ } ++ ++ ret = tw9910_mask_set(client, ACNTL1, ACNTL1_PDN_MASK, acntl1); ++ if (ret < 0) ++ return ret; ++ ++ return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); ++} ++ ++static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, ++ u32 width, u32 height) ++{ ++ const struct tw9910_scale_ctrl *scale; ++ const struct tw9910_scale_ctrl *ret = NULL; ++ __u32 diff = 0xffffffff, tmp; ++ int size, i; ++ ++ if (norm & V4L2_STD_NTSC) { ++ scale = tw9910_ntsc_scales; ++ size = ARRAY_SIZE(tw9910_ntsc_scales); ++ } else if (norm & V4L2_STD_PAL) { ++ scale = tw9910_pal_scales; ++ size = ARRAY_SIZE(tw9910_pal_scales); ++ } else { ++ return NULL; ++ } ++ ++ for (i = 0; i < size; i++) { ++ tmp = abs(width - scale[i].width) + ++ abs(height - scale[i].height); ++ if (tmp < diff) { ++ diff = tmp; ++ ret = scale + i; ++ } ++ } ++ ++ return ret; ++} ++ ++/* ++ * subdevice operations ++ */ ++static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ u8 val; ++ int ret; ++ ++ if (!enable) { ++ switch (priv->revision) { ++ case 0: ++ val = OEN_TRI_SEL_ALL_OFF_r0; ++ break; ++ case 1: ++ val = OEN_TRI_SEL_ALL_OFF_r1; ++ break; ++ default: ++ dev_err(&client->dev, "un-supported revision\n"); ++ return -EINVAL; ++ } ++ } else { ++ val = OEN_TRI_SEL_ALL_ON; ++ ++ if (!priv->scale) { ++ dev_err(&client->dev, "norm select error\n"); ++ return -EPERM; ++ } ++ ++ dev_dbg(&client->dev, "%s %dx%d\n", ++ priv->scale->name, ++ priv->scale->width, ++ priv->scale->height); ++ } ++ ++ ret = tw9910_mask_set(client, OPFORM, OEN_TRI_SEL_MASK, val); ++ if (ret < 0) ++ return ret; ++ ++ return tw9910_power(client, enable); ++} ++ ++static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ *norm = priv->norm; ++ ++ return 0; ++} ++ ++static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) ++ return -EINVAL; ++ ++ priv->norm = norm; ++ ++ return 0; ++} ++ ++static int tw9910_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *id) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ id->ident = V4L2_IDENT_TW9910; ++ id->revision = priv->revision; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int tw9910_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ if (reg->reg > 0xff) ++ return -EINVAL; ++ ++ ret = i2c_smbus_read_byte_data(client, reg->reg); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * ret = int ++ * reg->val = __u64 ++ */ ++ reg->val = (__u64)ret; ++ ++ return 0; ++} ++ ++static int tw9910_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (reg->reg > 0xff || ++ reg->val > 0xff) ++ return -EINVAL; ++ ++ return i2c_smbus_write_byte_data(client, reg->reg, reg->val); ++} ++#endif ++ ++static int tw9910_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ return soc_camera_set_power(&client->dev, ssdd, on); ++} ++ ++static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ int ret = -EINVAL; ++ u8 val; ++ ++ /* ++ * select suitable norm ++ */ ++ priv->scale = tw9910_select_norm(priv->norm, *width, *height); ++ if (!priv->scale) ++ goto tw9910_set_fmt_error; ++ ++ /* ++ * reset hardware ++ */ ++ tw9910_reset(client); ++ ++ /* ++ * set bus width ++ */ ++ val = 0x00; ++ if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) ++ val = LEN; ++ ++ ret = tw9910_mask_set(client, OPFORM, LEN, val); ++ if (ret < 0) ++ goto tw9910_set_fmt_error; ++ ++ /* ++ * select MPOUT behavior ++ */ ++ switch (priv->info->mpout) { ++ case TW9910_MPO_VLOSS: ++ val = RTSEL_VLOSS; break; ++ case TW9910_MPO_HLOCK: ++ val = RTSEL_HLOCK; break; ++ case TW9910_MPO_SLOCK: ++ val = RTSEL_SLOCK; break; ++ case TW9910_MPO_VLOCK: ++ val = RTSEL_VLOCK; break; ++ case TW9910_MPO_MONO: ++ val = RTSEL_MONO; break; ++ case TW9910_MPO_DET50: ++ val = RTSEL_DET50; break; ++ case TW9910_MPO_FIELD: ++ val = RTSEL_FIELD; break; ++ case TW9910_MPO_RTCO: ++ val = RTSEL_RTCO; break; ++ default: ++ val = 0; ++ } ++ ++ ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); ++ if (ret < 0) ++ goto tw9910_set_fmt_error; ++ ++ /* ++ * set scale ++ */ ++ ret = tw9910_set_scale(client, priv->scale); ++ if (ret < 0) ++ goto tw9910_set_fmt_error; ++ ++ /* ++ * set hsync ++ */ ++ ret = tw9910_set_hsync(client); ++ if (ret < 0) ++ goto tw9910_set_fmt_error; ++ ++ *width = priv->scale->width; ++ *height = priv->scale->height; ++ ++ return ret; ++ ++tw9910_set_fmt_error: ++ ++ tw9910_reset(client); ++ priv->scale = NULL; ++ ++ return ret; ++} ++ ++static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ a->c.left = 0; ++ a->c.top = 0; ++ if (priv->norm & V4L2_STD_NTSC) { ++ a->c.width = 640; ++ a->c.height = 480; ++ } else { ++ a->c.width = 768; ++ a->c.height = 576; ++ } ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ if (priv->norm & V4L2_STD_NTSC) { ++ a->bounds.width = 640; ++ a->bounds.height = 480; ++ } else { ++ a->bounds.width = 768; ++ a->bounds.height = 576; ++ } ++ a->defrect = a->bounds; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++static int tw9910_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ ++ if (!priv->scale) { ++ priv->scale = tw9910_select_norm(priv->norm, 640, 480); ++ if (!priv->scale) ++ return -EINVAL; ++ } ++ ++ mf->width = priv->scale->width; ++ mf->height = priv->scale->height; ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ mf->field = V4L2_FIELD_INTERLACED_BT; ++ ++ return 0; ++} ++ ++static int tw9910_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ u32 width = mf->width, height = mf->height; ++ int ret; ++ ++ WARN_ON(mf->field != V4L2_FIELD_ANY && ++ mf->field != V4L2_FIELD_INTERLACED_BT); ++ ++ /* ++ * check color format ++ */ ++ if (mf->code != V4L2_MBUS_FMT_UYVY8_2X8) ++ return -EINVAL; ++ ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ ++ ret = tw9910_set_frame(sd, &width, &height); ++ if (!ret) { ++ mf->width = width; ++ mf->height = height; ++ } ++ return ret; ++} ++ ++static int tw9910_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tw9910_priv *priv = to_tw9910(client); ++ const struct tw9910_scale_ctrl *scale; ++ ++ if (V4L2_FIELD_ANY == mf->field) { ++ mf->field = V4L2_FIELD_INTERLACED_BT; ++ } else if (V4L2_FIELD_INTERLACED_BT != mf->field) { ++ dev_err(&client->dev, "Field type %d invalid.\n", mf->field); ++ return -EINVAL; ++ } ++ ++ mf->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ mf->colorspace = V4L2_COLORSPACE_JPEG; ++ ++ /* ++ * select suitable norm ++ */ ++ scale = tw9910_select_norm(priv->norm, mf->width, mf->height); ++ if (!scale) ++ return -EINVAL; ++ ++ mf->width = scale->width; ++ mf->height = scale->height; ++ ++ return 0; ++} ++ ++static int tw9910_video_probe(struct i2c_client *client) ++{ ++ struct tw9910_priv *priv = to_tw9910(client); ++ s32 id; ++ int ret; ++ ++ /* ++ * tw9910 only use 8 or 16 bit bus width ++ */ ++ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && ++ SOCAM_DATAWIDTH_8 != priv->info->buswidth) { ++ dev_err(&client->dev, "bus width error\n"); ++ return -ENODEV; ++ } ++ ++ ret = tw9910_s_power(&priv->subdev, 1); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * check and show Product ID ++ * So far only revisions 0 and 1 have been seen ++ */ ++ id = i2c_smbus_read_byte_data(client, ID); ++ priv->revision = GET_REV(id); ++ id = GET_ID(id); ++ ++ if (0x0B != id || ++ 0x01 < priv->revision) { ++ dev_err(&client->dev, ++ "Product ID error %x:%x\n", ++ id, priv->revision); ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ dev_info(&client->dev, ++ "tw9910 Product ID %0x:%0x\n", id, priv->revision); ++ ++ priv->norm = V4L2_STD_NTSC; ++ ++done: ++ tw9910_s_power(&priv->subdev, 0); ++ return ret; ++} ++ ++static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { ++ .g_chip_ident = tw9910_g_chip_ident, ++ .s_std = tw9910_s_std, ++ .g_std = tw9910_g_std, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = tw9910_g_register, ++ .s_register = tw9910_s_register, ++#endif ++ .s_power = tw9910_s_power, ++}; ++ ++static int tw9910_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_UYVY8_2X8; ++ return 0; ++} ++ ++static int tw9910_g_mbus_config(struct v4l2_subdev *sd, ++ struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | ++ V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | ++ V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | ++ V4L2_MBUS_DATA_ACTIVE_HIGH; ++ cfg->type = V4L2_MBUS_PARALLEL; ++ cfg->flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ return 0; ++} ++ ++static int tw9910_s_mbus_config(struct v4l2_subdev *sd, ++ const struct v4l2_mbus_config *cfg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ u8 val = VSSL_VVALID | HSSL_DVALID; ++ unsigned long flags = soc_camera_apply_board_flags(ssdd, cfg); ++ ++ /* ++ * set OUTCTR1 ++ * ++ * We use VVALID and DVALID signals to control VSYNC and HSYNC ++ * outputs, in this mode their polarity is inverted. ++ */ ++ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) ++ val |= HSP_HI; ++ ++ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) ++ val |= VSP_HI; ++ ++ return i2c_smbus_write_byte_data(client, OUTCTR1, val); ++} ++ ++static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { ++ .s_stream = tw9910_s_stream, ++ .g_mbus_fmt = tw9910_g_fmt, ++ .s_mbus_fmt = tw9910_s_fmt, ++ .try_mbus_fmt = tw9910_try_fmt, ++ .cropcap = tw9910_cropcap, ++ .g_crop = tw9910_g_crop, ++ .enum_mbus_fmt = tw9910_enum_fmt, ++ .g_mbus_config = tw9910_g_mbus_config, ++ .s_mbus_config = tw9910_s_mbus_config, ++}; ++ ++static struct v4l2_subdev_ops tw9910_subdev_ops = { ++ .core = &tw9910_subdev_core_ops, ++ .video = &tw9910_subdev_video_ops, ++}; ++ ++/* ++ * i2c_driver function ++ */ ++ ++static int tw9910_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++ ++{ ++ struct tw9910_priv *priv; ++ struct tw9910_video_info *info; ++ struct i2c_adapter *adapter = ++ to_i2c_adapter(client->dev.parent); ++ struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); ++ ++ if (!ssdd || !ssdd->drv_priv) { ++ dev_err(&client->dev, "TW9910: missing platform data!\n"); ++ return -EINVAL; ++ } ++ ++ info = ssdd->drv_priv; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { ++ dev_err(&client->dev, ++ "I2C-Adapter doesn't support " ++ "I2C_FUNC_SMBUS_BYTE_DATA\n"); ++ return -EIO; ++ } ++ ++ priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ priv->info = info; ++ ++ v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); ++ ++ return tw9910_video_probe(client); ++} ++ ++static int tw9910_remove(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static const struct i2c_device_id tw9910_id[] = { ++ { "tw9910", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tw9910_id); ++ ++static struct i2c_driver tw9910_i2c_driver = { ++ .driver = { ++ .name = "tw9910", ++ }, ++ .probe = tw9910_probe, ++ .remove = tw9910_remove, ++ .id_table = tw9910_id, ++}; ++ ++module_i2c_driver(tw9910_i2c_driver); ++ ++MODULE_DESCRIPTION("SoC Camera driver for tw9910"); ++MODULE_AUTHOR("Kuninori Morimoto"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c +new file mode 100644 +index 0000000..e9d95bd +--- /dev/null ++++ b/drivers/media/i2c/sr030pc30.c +@@ -0,0 +1,871 @@ ++/* ++ * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP ++ * ++ * Copyright (C) 2010 Samsung Electronics Co., Ltd ++ * Author: Sylwester Nawrocki, s.nawrocki@samsung.com ++ * ++ * Based on original driver authored by Dongsoo Nathaniel Kim ++ * and HeungJun Kim . ++ * ++ * Based on mt9v011 Micron Digital Image Sensor driver ++ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++#define MODULE_NAME "SR030PC30" ++ ++/* ++ * Register offsets within a page ++ * b15..b8 - page id, b7..b0 - register address ++ */ ++#define POWER_CTRL_REG 0x0001 ++#define PAGEMODE_REG 0x03 ++#define DEVICE_ID_REG 0x0004 ++#define NOON010PC30_ID 0x86 ++#define SR030PC30_ID 0x8C ++#define VDO_CTL1_REG 0x0010 ++#define SUBSAMPL_NONE_VGA 0 ++#define SUBSAMPL_QVGA 0x10 ++#define SUBSAMPL_QQVGA 0x20 ++#define VDO_CTL2_REG 0x0011 ++#define SYNC_CTL_REG 0x0012 ++#define WIN_ROWH_REG 0x0020 ++#define WIN_ROWL_REG 0x0021 ++#define WIN_COLH_REG 0x0022 ++#define WIN_COLL_REG 0x0023 ++#define WIN_HEIGHTH_REG 0x0024 ++#define WIN_HEIGHTL_REG 0x0025 ++#define WIN_WIDTHH_REG 0x0026 ++#define WIN_WIDTHL_REG 0x0027 ++#define HBLANKH_REG 0x0040 ++#define HBLANKL_REG 0x0041 ++#define VSYNCH_REG 0x0042 ++#define VSYNCL_REG 0x0043 ++/* page 10 */ ++#define ISP_CTL_REG(n) (0x1010 + (n)) ++#define YOFS_REG 0x1040 ++#define DARK_YOFS_REG 0x1041 ++#define AG_ABRTH_REG 0x1050 ++#define SAT_CTL_REG 0x1060 ++#define BSAT_REG 0x1061 ++#define RSAT_REG 0x1062 ++#define AG_SAT_TH_REG 0x1063 ++/* page 11 */ ++#define ZLPF_CTRL_REG 0x1110 ++#define ZLPF_CTRL2_REG 0x1112 ++#define ZLPF_AGH_THR_REG 0x1121 ++#define ZLPF_THR_REG 0x1160 ++#define ZLPF_DYN_THR_REG 0x1160 ++/* page 12 */ ++#define YCLPF_CTL1_REG 0x1240 ++#define YCLPF_CTL2_REG 0x1241 ++#define YCLPF_THR_REG 0x1250 ++#define BLPF_CTL_REG 0x1270 ++#define BLPF_THR1_REG 0x1274 ++#define BLPF_THR2_REG 0x1275 ++/* page 14 - Lens Shading Compensation */ ++#define LENS_CTRL_REG 0x1410 ++#define LENS_XCEN_REG 0x1420 ++#define LENS_YCEN_REG 0x1421 ++#define LENS_R_COMP_REG 0x1422 ++#define LENS_G_COMP_REG 0x1423 ++#define LENS_B_COMP_REG 0x1424 ++/* page 15 - Color correction */ ++#define CMC_CTL_REG 0x1510 ++#define CMC_OFSGH_REG 0x1514 ++#define CMC_OFSGL_REG 0x1516 ++#define CMC_SIGN_REG 0x1517 ++/* Color correction coefficients */ ++#define CMC_COEF_REG(n) (0x1530 + (n)) ++/* Color correction offset coefficients */ ++#define CMC_OFS_REG(n) (0x1540 + (n)) ++/* page 16 - Gamma correction */ ++#define GMA_CTL_REG 0x1610 ++/* Gamma correction coefficients 0.14 */ ++#define GMA_COEF_REG(n) (0x1630 + (n)) ++/* page 20 - Auto Exposure */ ++#define AE_CTL1_REG 0x2010 ++#define AE_CTL2_REG 0x2011 ++#define AE_FRM_CTL_REG 0x2020 ++#define AE_FINE_CTL_REG(n) (0x2028 + (n)) ++#define EXP_TIMEH_REG 0x2083 ++#define EXP_TIMEM_REG 0x2084 ++#define EXP_TIMEL_REG 0x2085 ++#define EXP_MMINH_REG 0x2086 ++#define EXP_MMINL_REG 0x2087 ++#define EXP_MMAXH_REG 0x2088 ++#define EXP_MMAXM_REG 0x2089 ++#define EXP_MMAXL_REG 0x208A ++/* page 22 - Auto White Balance */ ++#define AWB_CTL1_REG 0x2210 ++#define AWB_ENABLE 0x80 ++#define AWB_CTL2_REG 0x2211 ++#define MWB_ENABLE 0x01 ++/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ ++#define AWB_RGAIN_REG 0x2280 ++#define AWB_GGAIN_REG 0x2281 ++#define AWB_BGAIN_REG 0x2282 ++#define AWB_RMAX_REG 0x2283 ++#define AWB_RMIN_REG 0x2284 ++#define AWB_BMAX_REG 0x2285 ++#define AWB_BMIN_REG 0x2286 ++/* R, B gain range in bright light conditions */ ++#define AWB_RMAXB_REG 0x2287 ++#define AWB_RMINB_REG 0x2288 ++#define AWB_BMAXB_REG 0x2289 ++#define AWB_BMINB_REG 0x228A ++/* manual white balance, when AWB_CTL2[0]=1 */ ++#define MWB_RGAIN_REG 0x22B2 ++#define MWB_BGAIN_REG 0x22B3 ++/* the token to mark an array end */ ++#define REG_TERM 0xFFFF ++ ++/* Minimum and maximum exposure time in ms */ ++#define EXPOS_MIN_MS 1 ++#define EXPOS_MAX_MS 125 ++ ++struct sr030pc30_info { ++ struct v4l2_subdev sd; ++ const struct sr030pc30_platform_data *pdata; ++ const struct sr030pc30_format *curr_fmt; ++ const struct sr030pc30_frmsize *curr_win; ++ unsigned int auto_wb:1; ++ unsigned int auto_exp:1; ++ unsigned int hflip:1; ++ unsigned int vflip:1; ++ unsigned int sleep:1; ++ unsigned int exposure; ++ u8 blue_balance; ++ u8 red_balance; ++ u8 i2c_reg_page; ++}; ++ ++struct sr030pc30_format { ++ enum v4l2_mbus_pixelcode code; ++ enum v4l2_colorspace colorspace; ++ u16 ispctl1_reg; ++}; ++ ++struct sr030pc30_frmsize { ++ u16 width; ++ u16 height; ++ int vid_ctl1; ++}; ++ ++struct i2c_regval { ++ u16 addr; ++ u16 val; ++}; ++ ++static const struct v4l2_queryctrl sr030pc30_ctrl[] = { ++ { ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Auto White Balance", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 1, ++ }, { ++ .id = V4L2_CID_RED_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Red Balance", ++ .minimum = 0, ++ .maximum = 127, ++ .step = 1, ++ .default_value = 64, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_BLUE_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Blue Balance", ++ .minimum = 0, ++ .maximum = 127, ++ .step = 1, ++ .default_value = 64, ++ }, { ++ .id = V4L2_CID_EXPOSURE_AUTO, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Auto Exposure", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 1, ++ }, { ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Exposure", ++ .minimum = EXPOS_MIN_MS, ++ .maximum = EXPOS_MAX_MS, ++ .step = 1, ++ .default_value = 1, ++ }, { ++ } ++}; ++ ++/* supported resolutions */ ++static const struct sr030pc30_frmsize sr030pc30_sizes[] = { ++ { ++ .width = 640, ++ .height = 480, ++ .vid_ctl1 = SUBSAMPL_NONE_VGA, ++ }, { ++ .width = 320, ++ .height = 240, ++ .vid_ctl1 = SUBSAMPL_QVGA, ++ }, { ++ .width = 160, ++ .height = 120, ++ .vid_ctl1 = SUBSAMPL_QQVGA, ++ }, ++}; ++ ++/* supported pixel formats */ ++static const struct sr030pc30_format sr030pc30_formats[] = { ++ { ++ .code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x03, ++ }, { ++ .code = V4L2_MBUS_FMT_YVYU8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x02, ++ }, { ++ .code = V4L2_MBUS_FMT_VYUY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0, ++ }, { ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x01, ++ }, { ++ .code = V4L2_MBUS_FMT_RGB565_2X8_BE, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ .ispctl1_reg = 0x40, ++ }, ++}; ++ ++static const struct i2c_regval sr030pc30_base_regs[] = { ++ /* Window size and position within pixel matrix */ ++ { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, ++ { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, ++ { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, ++ { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, ++ { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, ++ { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, ++ { SYNC_CTL_REG, 0 }, ++ /* Color corection and saturation */ ++ { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, ++ { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, ++ { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, ++ { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, ++ { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, ++ { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, ++ { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, ++ { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, ++ { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, ++ { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, ++ { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, ++ { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, ++ { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, ++ { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, ++ { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, ++ /* Color corection coefficients */ ++ { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, ++ { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, ++ { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, ++ { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, ++ { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, ++ { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, ++ { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, ++ { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, ++ /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ ++ { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, ++ { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, ++ { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, ++ { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, ++ { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, ++ { BLPF_THR2_REG, 0x04 }, ++ /* Automatic white balance */ ++ { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, ++ { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, ++ { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, ++ { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, ++ { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, ++ /* Auto exposure */ ++ { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, ++ { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, ++ { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, ++ /* Lens shading compensation */ ++ { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, ++ { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, ++ { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, ++ { REG_TERM, 0 }, ++}; ++ ++static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct sr030pc30_info, sd); ++} ++ ++static inline int set_i2c_page(struct sr030pc30_info *info, ++ struct i2c_client *client, unsigned int reg) ++{ ++ int ret = 0; ++ u32 page = reg >> 8 & 0xFF; ++ ++ if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { ++ ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); ++ if (!ret) ++ info->i2c_reg_page = page; ++ } ++ return ret; ++} ++ ++static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ int ret = set_i2c_page(info, client, reg_addr); ++ if (!ret) ++ ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); ++ return ret; ++} ++ ++static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ int ret = set_i2c_page(info, client, reg_addr); ++ if (!ret) ++ ret = i2c_smbus_write_byte_data( ++ client, reg_addr & 0xFF, val); ++ return ret; ++} ++ ++static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, ++ const struct i2c_regval *msg) ++{ ++ while (msg->addr != REG_TERM) { ++ int ret = cam_i2c_write(sd, msg->addr, msg->val); ++ if (ret) ++ return ret; ++ msg++; ++ } ++ return 0; ++} ++ ++/* Device reset and sleep mode control */ ++static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, ++ bool reset, bool sleep) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ u8 reg = sleep ? 0xF1 : 0xF0; ++ int ret = 0; ++ ++ if (reset) ++ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); ++ if (!ret) { ++ ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); ++ if (!ret) { ++ info->sleep = sleep; ++ if (reset) ++ info->i2c_reg_page = -1; ++ } ++ } ++ return ret; ++} ++ ++static inline int sr030pc30_enable_autoexposure(struct v4l2_subdev *sd, int on) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ /* auto anti-flicker is also enabled here */ ++ int ret = cam_i2c_write(sd, AE_CTL1_REG, on ? 0xDC : 0x0C); ++ if (!ret) ++ info->auto_exp = on; ++ return ret; ++} ++ ++static int sr030pc30_set_exposure(struct v4l2_subdev *sd, int value) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ unsigned long expos = value * info->pdata->clk_rate / (8 * 1000); ++ ++ int ret = cam_i2c_write(sd, EXP_TIMEH_REG, expos >> 16 & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_TIMEM_REG, expos >> 8 & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_TIMEL_REG, expos & 0xFF); ++ if (!ret) { /* Turn off AE */ ++ info->exposure = value; ++ ret = sr030pc30_enable_autoexposure(sd, 0); ++ } ++ return ret; ++} ++ ++/* Automatic white balance control */ ++static int sr030pc30_enable_autowhitebalance(struct v4l2_subdev *sd, int on) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ int ret = cam_i2c_write(sd, AWB_CTL2_REG, on ? 0x2E : 0x2F); ++ if (!ret) ++ ret = cam_i2c_write(sd, AWB_CTL1_REG, on ? 0xFB : 0x7B); ++ if (!ret) ++ info->auto_wb = on; ++ ++ return ret; ++} ++ ++static int sr030pc30_set_flip(struct v4l2_subdev *sd) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); ++ if (reg < 0) ++ return reg; ++ ++ reg &= 0x7C; ++ if (info->hflip) ++ reg |= 0x01; ++ if (info->vflip) ++ reg |= 0x02; ++ return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); ++} ++ ++/* Configure resolution, color format and image flip */ ++static int sr030pc30_set_params(struct v4l2_subdev *sd) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ int ret; ++ ++ if (!info->curr_win) ++ return -EINVAL; ++ ++ /* Configure the resolution through subsampling */ ++ ret = cam_i2c_write(sd, VDO_CTL1_REG, ++ info->curr_win->vid_ctl1); ++ ++ if (!ret && info->curr_fmt) ++ ret = cam_i2c_write(sd, ISP_CTL_REG(0), ++ info->curr_fmt->ispctl1_reg); ++ if (!ret) ++ ret = sr030pc30_set_flip(sd); ++ ++ return ret; ++} ++ ++/* Find nearest matching image pixel size. */ ++static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) ++{ ++ unsigned int min_err = ~0; ++ int i = ARRAY_SIZE(sr030pc30_sizes); ++ const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], ++ *match = NULL; ++ while (i--) { ++ int err = abs(fsize->width - mf->width) ++ + abs(fsize->height - mf->height); ++ if (err < min_err) { ++ min_err = err; ++ match = fsize; ++ } ++ fsize++; ++ } ++ if (match) { ++ mf->width = match->width; ++ mf->height = match->height; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int sr030pc30_queryctrl(struct v4l2_subdev *sd, ++ struct v4l2_queryctrl *qc) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) ++ if (qc->id == sr030pc30_ctrl[i].id) { ++ *qc = sr030pc30_ctrl[i]; ++ v4l2_dbg(1, debug, sd, "%s id: %d\n", ++ __func__, qc->id); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static inline int sr030pc30_set_bluebalance(struct v4l2_subdev *sd, int value) ++{ ++ int ret = cam_i2c_write(sd, MWB_BGAIN_REG, value); ++ if (!ret) ++ to_sr030pc30(sd)->blue_balance = value; ++ return ret; ++} ++ ++static inline int sr030pc30_set_redbalance(struct v4l2_subdev *sd, int value) ++{ ++ int ret = cam_i2c_write(sd, MWB_RGAIN_REG, value); ++ if (!ret) ++ to_sr030pc30(sd)->red_balance = value; ++ return ret; ++} ++ ++static int sr030pc30_s_ctrl(struct v4l2_subdev *sd, ++ struct v4l2_control *ctrl) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(sr030pc30_ctrl); i++) ++ if (ctrl->id == sr030pc30_ctrl[i].id) ++ break; ++ ++ if (i == ARRAY_SIZE(sr030pc30_ctrl)) ++ return -EINVAL; ++ ++ if (ctrl->value < sr030pc30_ctrl[i].minimum || ++ ctrl->value > sr030pc30_ctrl[i].maximum) ++ return -ERANGE; ++ ++ v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", ++ __func__, ctrl->id, ctrl->value); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ sr030pc30_enable_autowhitebalance(sd, ctrl->value); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ ret = sr030pc30_set_bluebalance(sd, ctrl->value); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ ret = sr030pc30_set_redbalance(sd, ctrl->value); ++ break; ++ case V4L2_CID_EXPOSURE_AUTO: ++ sr030pc30_enable_autoexposure(sd, ++ ctrl->value == V4L2_EXPOSURE_AUTO); ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = sr030pc30_set_exposure(sd, ctrl->value); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int sr030pc30_g_ctrl(struct v4l2_subdev *sd, ++ struct v4l2_control *ctrl) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ v4l2_dbg(1, debug, sd, "%s: id: %d\n", __func__, ctrl->id); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ctrl->value = info->auto_wb; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ ctrl->value = info->blue_balance; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ ctrl->value = info->red_balance; ++ break; ++ case V4L2_CID_EXPOSURE_AUTO: ++ ctrl->value = info->auto_exp; ++ break; ++ case V4L2_CID_EXPOSURE: ++ ctrl->value = info->exposure; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int sr030pc30_enum_fmt(struct v4l2_subdev *sd, unsigned int index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (!code || index >= ARRAY_SIZE(sr030pc30_formats)) ++ return -EINVAL; ++ ++ *code = sr030pc30_formats[index].code; ++ return 0; ++} ++ ++static int sr030pc30_g_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ int ret; ++ ++ if (!mf) ++ return -EINVAL; ++ ++ if (!info->curr_win || !info->curr_fmt) { ++ ret = sr030pc30_set_params(sd); ++ if (ret) ++ return ret; ++ } ++ ++ mf->width = info->curr_win->width; ++ mf->height = info->curr_win->height; ++ mf->code = info->curr_fmt->code; ++ mf->colorspace = info->curr_fmt->colorspace; ++ mf->field = V4L2_FIELD_NONE; ++ ++ return 0; ++} ++ ++/* Return nearest media bus frame format. */ ++static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ int i = ARRAY_SIZE(sr030pc30_formats); ++ ++ sr030pc30_try_frame_size(mf); ++ ++ while (i--) ++ if (mf->code == sr030pc30_formats[i].code) ++ break; ++ ++ mf->code = sr030pc30_formats[i].code; ++ ++ return &sr030pc30_formats[i]; ++} ++ ++/* Return nearest media bus frame format. */ ++static int sr030pc30_try_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ if (!sd || !mf) ++ return -EINVAL; ++ ++ try_fmt(sd, mf); ++ return 0; ++} ++ ++static int sr030pc30_s_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *mf) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ if (!sd || !mf) ++ return -EINVAL; ++ ++ info->curr_fmt = try_fmt(sd, mf); ++ ++ return sr030pc30_set_params(sd); ++} ++ ++static int sr030pc30_base_config(struct v4l2_subdev *sd) ++{ ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ int ret; ++ unsigned long expmin, expmax; ++ ++ ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); ++ if (!ret) { ++ info->curr_fmt = &sr030pc30_formats[0]; ++ info->curr_win = &sr030pc30_sizes[0]; ++ ret = sr030pc30_set_params(sd); ++ } ++ if (!ret) ++ ret = sr030pc30_pwr_ctrl(sd, false, false); ++ ++ if (!ret && !info->pdata) ++ return ret; ++ ++ expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); ++ expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); ++ ++ v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, ++ expmin, expmax); ++ ++ /* Setting up manual exposure time range */ ++ ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); ++ if (!ret) ++ ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); ++ ++ return ret; ++} ++ ++static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ const struct sr030pc30_platform_data *pdata = info->pdata; ++ int ret; ++ ++ if (pdata == NULL) { ++ WARN(1, "No platform data!\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * Put sensor into power sleep mode before switching off ++ * power and disabling MCLK. ++ */ ++ if (!on) ++ sr030pc30_pwr_ctrl(sd, false, true); ++ ++ /* set_power controls sensor's power and clock */ ++ if (pdata->set_power) { ++ ret = pdata->set_power(&client->dev, on); ++ if (ret) ++ return ret; ++ } ++ ++ if (on) { ++ ret = sr030pc30_base_config(sd); ++ } else { ++ ret = 0; ++ info->curr_win = NULL; ++ info->curr_fmt = NULL; ++ } ++ ++ return ret; ++} ++ ++static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { ++ .s_power = sr030pc30_s_power, ++ .queryctrl = sr030pc30_queryctrl, ++ .s_ctrl = sr030pc30_s_ctrl, ++ .g_ctrl = sr030pc30_g_ctrl, ++}; ++ ++static const struct v4l2_subdev_video_ops sr030pc30_video_ops = { ++ .g_mbus_fmt = sr030pc30_g_fmt, ++ .s_mbus_fmt = sr030pc30_s_fmt, ++ .try_mbus_fmt = sr030pc30_try_fmt, ++ .enum_mbus_fmt = sr030pc30_enum_fmt, ++}; ++ ++static const struct v4l2_subdev_ops sr030pc30_ops = { ++ .core = &sr030pc30_core_ops, ++ .video = &sr030pc30_video_ops, ++}; ++ ++/* ++ * Detect sensor type. Return 0 if SR030PC30 was detected ++ * or -ENODEV otherwise. ++ */ ++static int sr030pc30_detect(struct i2c_client *client) ++{ ++ const struct sr030pc30_platform_data *pdata ++ = client->dev.platform_data; ++ int ret; ++ ++ /* Enable sensor's power and clock */ ++ if (pdata->set_power) { ++ ret = pdata->set_power(&client->dev, 1); ++ if (ret) ++ return ret; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); ++ ++ if (pdata->set_power) ++ pdata->set_power(&client->dev, 0); ++ ++ if (ret < 0) { ++ dev_err(&client->dev, "%s: I2C read failed\n", __func__); ++ return ret; ++ } ++ ++ return ret == SR030PC30_ID ? 0 : -ENODEV; ++} ++ ++ ++static int sr030pc30_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct sr030pc30_info *info; ++ struct v4l2_subdev *sd; ++ const struct sr030pc30_platform_data *pdata ++ = client->dev.platform_data; ++ int ret; ++ ++ if (!pdata) { ++ dev_err(&client->dev, "No platform data!"); ++ return -EIO; ++ } ++ ++ ret = sr030pc30_detect(client); ++ if (ret) ++ return ret; ++ ++ info = kzalloc(sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ sd = &info->sd; ++ strcpy(sd->name, MODULE_NAME); ++ info->pdata = client->dev.platform_data; ++ ++ v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); ++ ++ info->i2c_reg_page = -1; ++ info->hflip = 1; ++ info->auto_exp = 1; ++ info->exposure = 30; ++ ++ return 0; ++} ++ ++static int sr030pc30_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct sr030pc30_info *info = to_sr030pc30(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(info); ++ return 0; ++} ++ ++static const struct i2c_device_id sr030pc30_id[] = { ++ { MODULE_NAME, 0 }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(i2c, sr030pc30_id); ++ ++ ++static struct i2c_driver sr030pc30_i2c_driver = { ++ .driver = { ++ .name = MODULE_NAME ++ }, ++ .probe = sr030pc30_probe, ++ .remove = sr030pc30_remove, ++ .id_table = sr030pc30_id, ++}; ++ ++module_i2c_driver(sr030pc30_i2c_driver); ++ ++MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); ++MODULE_AUTHOR("Sylwester Nawrocki "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/tcm825x.c b/drivers/media/i2c/tcm825x.c +new file mode 100644 +index 0000000..9252529 +--- /dev/null ++++ b/drivers/media/i2c/tcm825x.c +@@ -0,0 +1,937 @@ ++/* ++ * drivers/media/i2c/tcm825x.c ++ * ++ * TCM825X camera sensor driver. ++ * ++ * Copyright (C) 2007 Nokia Corporation. ++ * ++ * Contact: Sakari Ailus ++ * ++ * Based on code from David Cohen ++ * ++ * This driver was based on ov9640 sensor driver from MontaVista ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include "tcm825x.h" ++ ++/* ++ * The sensor has two fps modes: the lower one just gives half the fps ++ * at the same xclk than the high one. ++ */ ++#define MAX_FPS 30 ++#define MIN_FPS 8 ++#define MAX_HALF_FPS (MAX_FPS / 2) ++#define HIGH_FPS_MODE_LOWER_LIMIT 14 ++#define DEFAULT_FPS MAX_HALF_FPS ++ ++struct tcm825x_sensor { ++ const struct tcm825x_platform_data *platform_data; ++ struct v4l2_int_device *v4l2_int_device; ++ struct i2c_client *i2c_client; ++ struct v4l2_pix_format pix; ++ struct v4l2_fract timeperframe; ++}; ++ ++/* list of image formats supported by TCM825X sensor */ ++static const struct v4l2_fmtdesc tcm825x_formats[] = { ++ { ++ .description = "YUYV (YUV 4:2:2), packed", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ }, { ++ /* Note: V4L2 defines RGB565 as: ++ * ++ * Byte 0 Byte 1 ++ * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 ++ * ++ * We interpret RGB565 as: ++ * ++ * Byte 0 Byte 1 ++ * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 ++ */ ++ .description = "RGB565, le", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ }, ++}; ++ ++#define TCM825X_NUM_CAPTURE_FORMATS ARRAY_SIZE(tcm825x_formats) ++ ++/* ++ * TCM825X register configuration for all combinations of pixel format and ++ * image size ++ */ ++static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; ++static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; ++static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; ++static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; ++static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; ++static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; ++ ++static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; ++static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; ++ ++/* Our own specific controls */ ++#define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE ++#define V4L2_CID_H_EDGE_EN V4L2_CID_PRIVATE_BASE + 1 ++#define V4L2_CID_V_EDGE_EN V4L2_CID_PRIVATE_BASE + 2 ++#define V4L2_CID_LENS V4L2_CID_PRIVATE_BASE + 3 ++#define V4L2_CID_MAX_EXPOSURE_TIME V4L2_CID_PRIVATE_BASE + 4 ++#define V4L2_CID_LAST_PRIV V4L2_CID_MAX_EXPOSURE_TIME ++ ++/* Video controls */ ++static struct vcontrol { ++ struct v4l2_queryctrl qc; ++ u16 reg; ++ u16 start_bit; ++} video_control[] = { ++ { ++ { ++ .id = V4L2_CID_GAIN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Gain", ++ .minimum = 0, ++ .maximum = 63, ++ .step = 1, ++ }, ++ .reg = TCM825X_AG, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_RED_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Red Balance", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ }, ++ .reg = TCM825X_MRG, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_BLUE_BALANCE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Blue Balance", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ }, ++ .reg = TCM825X_MBG, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_AUTO_WHITE_BALANCE, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Auto White Balance", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 0, ++ }, ++ .reg = TCM825X_AWBSW, ++ .start_bit = 7, ++ }, ++ { ++ { ++ .id = V4L2_CID_EXPOSURE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Exposure Time", ++ .minimum = 0, ++ .maximum = 0x1fff, ++ .step = 1, ++ }, ++ .reg = TCM825X_ESRSPD_U, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_HFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Mirror Image", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 0, ++ }, ++ .reg = TCM825X_H_INV, ++ .start_bit = 6, ++ }, ++ { ++ { ++ .id = V4L2_CID_VFLIP, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Vertical Flip", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 0, ++ }, ++ .reg = TCM825X_V_INV, ++ .start_bit = 7, ++ }, ++ /* Private controls */ ++ { ++ { ++ .id = V4L2_CID_ALC, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ .name = "Auto Luminance Control", ++ .minimum = 0, ++ .maximum = 1, ++ .step = 0, ++ }, ++ .reg = TCM825X_ALCSW, ++ .start_bit = 7, ++ }, ++ { ++ { ++ .id = V4L2_CID_H_EDGE_EN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Horizontal Edge Enhancement", ++ .minimum = 0, ++ .maximum = 0xff, ++ .step = 1, ++ }, ++ .reg = TCM825X_HDTG, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_V_EDGE_EN, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Vertical Edge Enhancement", ++ .minimum = 0, ++ .maximum = 0xff, ++ .step = 1, ++ }, ++ .reg = TCM825X_VDTG, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_LENS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Lens Shading Compensation", ++ .minimum = 0, ++ .maximum = 0x3f, ++ .step = 1, ++ }, ++ .reg = TCM825X_LENS, ++ .start_bit = 0, ++ }, ++ { ++ { ++ .id = V4L2_CID_MAX_EXPOSURE_TIME, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Maximum Exposure Time", ++ .minimum = 0, ++ .maximum = 0x3, ++ .step = 1, ++ }, ++ .reg = TCM825X_ESRLIM, ++ .start_bit = 5, ++ }, ++}; ++ ++ ++static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = ++{ &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; ++ ++static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = ++{ &yuv422, &rgb565 }; ++ ++/* ++ * Read a value from a register in an TCM825X sensor device. The value is ++ * returned in 'val'. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tcm825x_read_reg(struct i2c_client *client, int reg) ++{ ++ int err; ++ struct i2c_msg msg[2]; ++ u8 reg_buf, data_buf = 0; ++ ++ if (!client->adapter) ++ return -ENODEV; ++ ++ msg[0].addr = client->addr; ++ msg[0].flags = 0; ++ msg[0].len = 1; ++ msg[0].buf = ®_buf; ++ msg[1].addr = client->addr; ++ msg[1].flags = I2C_M_RD; ++ msg[1].len = 1; ++ msg[1].buf = &data_buf; ++ ++ reg_buf = reg; ++ ++ err = i2c_transfer(client->adapter, msg, 2); ++ if (err < 0) ++ return err; ++ return data_buf; ++} ++ ++/* ++ * Write a value to a register in an TCM825X sensor device. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tcm825x_write_reg(struct i2c_client *client, u8 reg, u8 val) ++{ ++ int err; ++ struct i2c_msg msg[1]; ++ unsigned char data[2]; ++ ++ if (!client->adapter) ++ return -ENODEV; ++ ++ msg->addr = client->addr; ++ msg->flags = 0; ++ msg->len = 2; ++ msg->buf = data; ++ data[0] = reg; ++ data[1] = val; ++ err = i2c_transfer(client->adapter, msg, 1); ++ if (err >= 0) ++ return 0; ++ return err; ++} ++ ++static int __tcm825x_write_reg_mask(struct i2c_client *client, ++ u8 reg, u8 val, u8 mask) ++{ ++ int rc; ++ ++ /* need to do read - modify - write */ ++ rc = tcm825x_read_reg(client, reg); ++ if (rc < 0) ++ return rc; ++ ++ rc &= (~mask); /* Clear the masked bits */ ++ val &= mask; /* Enforce mask on value */ ++ val |= rc; ++ ++ /* write the new value to the register */ ++ rc = tcm825x_write_reg(client, reg, val); ++ if (rc) ++ return rc; ++ ++ return 0; ++} ++ ++#define tcm825x_write_reg_mask(client, regmask, val) \ ++ __tcm825x_write_reg_mask(client, TCM825X_ADDR((regmask)), val, \ ++ TCM825X_MASK((regmask))) ++ ++ ++/* ++ * Initialize a list of TCM825X registers. ++ * The list of registers is terminated by the pair of values ++ * { TCM825X_REG_TERM, TCM825X_VAL_TERM }. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tcm825x_write_default_regs(struct i2c_client *client, ++ const struct tcm825x_reg *reglist) ++{ ++ int err; ++ const struct tcm825x_reg *next = reglist; ++ ++ while (!((next->reg == TCM825X_REG_TERM) ++ && (next->val == TCM825X_VAL_TERM))) { ++ err = tcm825x_write_reg(client, next->reg, next->val); ++ if (err) { ++ dev_err(&client->dev, "register writing failed\n"); ++ return err; ++ } ++ next++; ++ } ++ ++ return 0; ++} ++ ++static struct vcontrol *find_vctrl(int id) ++{ ++ int i; ++ ++ if (id < V4L2_CID_BASE) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(video_control); i++) ++ if (video_control[i].qc.id == id) ++ return &video_control[i]; ++ ++ return NULL; ++} ++ ++/* ++ * Find the best match for a requested image capture size. The best match ++ * is chosen as the nearest match that has the same number or fewer pixels ++ * as the requested size, or the smallest image size if the requested size ++ * has fewer pixels than the smallest image. ++ */ ++static enum image_size tcm825x_find_size(struct v4l2_int_device *s, ++ unsigned int width, ++ unsigned int height) ++{ ++ enum image_size isize; ++ unsigned long pixels = width * height; ++ struct tcm825x_sensor *sensor = s->priv; ++ ++ for (isize = subQCIF; isize < VGA; isize++) { ++ if (tcm825x_sizes[isize + 1].height ++ * tcm825x_sizes[isize + 1].width > pixels) { ++ dev_dbg(&sensor->i2c_client->dev, "size %d\n", isize); ++ ++ return isize; ++ } ++ } ++ ++ dev_dbg(&sensor->i2c_client->dev, "format default VGA\n"); ++ ++ return VGA; ++} ++ ++/* ++ * Configure the TCM825X for current image size, pixel format, and ++ * frame period. fper is the frame period (in seconds) expressed as a ++ * fraction. Returns zero if successful, or non-zero otherwise. The ++ * actual frame period is returned in fper. ++ */ ++static int tcm825x_configure(struct v4l2_int_device *s) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct v4l2_pix_format *pix = &sensor->pix; ++ enum image_size isize = tcm825x_find_size(s, pix->width, pix->height); ++ struct v4l2_fract *fper = &sensor->timeperframe; ++ enum pixel_format pfmt; ++ int err; ++ u32 tgt_fps; ++ u8 val; ++ ++ /* common register initialization */ ++ err = tcm825x_write_default_regs( ++ sensor->i2c_client, sensor->platform_data->default_regs()); ++ if (err) ++ return err; ++ ++ /* configure image size */ ++ val = tcm825x_siz_reg[isize]->val; ++ dev_dbg(&sensor->i2c_client->dev, ++ "configuring image size %d\n", isize); ++ err = tcm825x_write_reg_mask(sensor->i2c_client, ++ tcm825x_siz_reg[isize]->reg, val); ++ if (err) ++ return err; ++ ++ /* configure pixel format */ ++ switch (pix->pixelformat) { ++ default: ++ case V4L2_PIX_FMT_RGB565: ++ pfmt = RGB565; ++ break; ++ case V4L2_PIX_FMT_UYVY: ++ pfmt = YUV422; ++ break; ++ } ++ ++ dev_dbg(&sensor->i2c_client->dev, ++ "configuring pixel format %d\n", pfmt); ++ val = tcm825x_fmt_reg[pfmt]->val; ++ ++ err = tcm825x_write_reg_mask(sensor->i2c_client, ++ tcm825x_fmt_reg[pfmt]->reg, val); ++ if (err) ++ return err; ++ ++ /* ++ * For frame rate < 15, the FPS reg (addr 0x02, bit 7) must be ++ * set. Frame rate will be halved from the normal. ++ */ ++ tgt_fps = fper->denominator / fper->numerator; ++ if (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) { ++ val = tcm825x_read_reg(sensor->i2c_client, 0x02); ++ val |= 0x80; ++ tcm825x_write_reg(sensor->i2c_client, 0x02, val); ++ } ++ ++ return 0; ++} ++ ++static int ioctl_queryctrl(struct v4l2_int_device *s, ++ struct v4l2_queryctrl *qc) ++{ ++ struct vcontrol *control; ++ ++ control = find_vctrl(qc->id); ++ ++ if (control == NULL) ++ return -EINVAL; ++ ++ *qc = control->qc; ++ ++ return 0; ++} ++ ++static int ioctl_g_ctrl(struct v4l2_int_device *s, ++ struct v4l2_control *vc) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct i2c_client *client = sensor->i2c_client; ++ int val, r; ++ struct vcontrol *lvc; ++ ++ /* exposure time is special, spread across 2 registers */ ++ if (vc->id == V4L2_CID_EXPOSURE) { ++ int val_lower, val_upper; ++ ++ val_upper = tcm825x_read_reg(client, ++ TCM825X_ADDR(TCM825X_ESRSPD_U)); ++ if (val_upper < 0) ++ return val_upper; ++ val_lower = tcm825x_read_reg(client, ++ TCM825X_ADDR(TCM825X_ESRSPD_L)); ++ if (val_lower < 0) ++ return val_lower; ++ ++ vc->value = ((val_upper & 0x1f) << 8) | (val_lower); ++ return 0; ++ } ++ ++ lvc = find_vctrl(vc->id); ++ if (lvc == NULL) ++ return -EINVAL; ++ ++ r = tcm825x_read_reg(client, TCM825X_ADDR(lvc->reg)); ++ if (r < 0) ++ return r; ++ val = r & TCM825X_MASK(lvc->reg); ++ val >>= lvc->start_bit; ++ ++ if (val < 0) ++ return val; ++ ++ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) ++ val ^= sensor->platform_data->is_upside_down(); ++ ++ vc->value = val; ++ return 0; ++} ++ ++static int ioctl_s_ctrl(struct v4l2_int_device *s, ++ struct v4l2_control *vc) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct i2c_client *client = sensor->i2c_client; ++ struct vcontrol *lvc; ++ int val = vc->value; ++ ++ /* exposure time is special, spread across 2 registers */ ++ if (vc->id == V4L2_CID_EXPOSURE) { ++ int val_lower, val_upper; ++ val_lower = val & TCM825X_MASK(TCM825X_ESRSPD_L); ++ val_upper = (val >> 8) & TCM825X_MASK(TCM825X_ESRSPD_U); ++ ++ if (tcm825x_write_reg_mask(client, ++ TCM825X_ESRSPD_U, val_upper)) ++ return -EIO; ++ ++ if (tcm825x_write_reg_mask(client, ++ TCM825X_ESRSPD_L, val_lower)) ++ return -EIO; ++ ++ return 0; ++ } ++ ++ lvc = find_vctrl(vc->id); ++ if (lvc == NULL) ++ return -EINVAL; ++ ++ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP) ++ val ^= sensor->platform_data->is_upside_down(); ++ ++ val = val << lvc->start_bit; ++ if (tcm825x_write_reg_mask(client, lvc->reg, val)) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int ioctl_enum_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_fmtdesc *fmt) ++{ ++ int index = fmt->index; ++ ++ switch (fmt->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (index >= TCM825X_NUM_CAPTURE_FORMATS) ++ return -EINVAL; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ fmt->flags = tcm825x_formats[index].flags; ++ strlcpy(fmt->description, tcm825x_formats[index].description, ++ sizeof(fmt->description)); ++ fmt->pixelformat = tcm825x_formats[index].pixelformat; ++ ++ return 0; ++} ++ ++static int ioctl_try_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_format *f) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ enum image_size isize; ++ int ifmt; ++ struct v4l2_pix_format *pix = &f->fmt.pix; ++ ++ isize = tcm825x_find_size(s, pix->width, pix->height); ++ dev_dbg(&sensor->i2c_client->dev, "isize = %d num_capture = %lu\n", ++ isize, (unsigned long)TCM825X_NUM_CAPTURE_FORMATS); ++ ++ pix->width = tcm825x_sizes[isize].width; ++ pix->height = tcm825x_sizes[isize].height; ++ ++ for (ifmt = 0; ifmt < TCM825X_NUM_CAPTURE_FORMATS; ifmt++) ++ if (pix->pixelformat == tcm825x_formats[ifmt].pixelformat) ++ break; ++ ++ if (ifmt == TCM825X_NUM_CAPTURE_FORMATS) ++ ifmt = 0; /* Default = YUV 4:2:2 */ ++ ++ pix->pixelformat = tcm825x_formats[ifmt].pixelformat; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = pix->width * TCM825X_BYTES_PER_PIXEL; ++ pix->sizeimage = pix->bytesperline * pix->height; ++ pix->priv = 0; ++ dev_dbg(&sensor->i2c_client->dev, "format = 0x%08x\n", ++ pix->pixelformat); ++ ++ switch (pix->pixelformat) { ++ case V4L2_PIX_FMT_UYVY: ++ default: ++ pix->colorspace = V4L2_COLORSPACE_JPEG; ++ break; ++ case V4L2_PIX_FMT_RGB565: ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int ioctl_s_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_format *f) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct v4l2_pix_format *pix = &f->fmt.pix; ++ int rval; ++ ++ rval = ioctl_try_fmt_cap(s, f); ++ if (rval) ++ return rval; ++ ++ rval = tcm825x_configure(s); ++ ++ sensor->pix = *pix; ++ ++ return rval; ++} ++ ++static int ioctl_g_fmt_cap(struct v4l2_int_device *s, ++ struct v4l2_format *f) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ ++ f->fmt.pix = sensor->pix; ++ ++ return 0; ++} ++ ++static int ioctl_g_parm(struct v4l2_int_device *s, ++ struct v4l2_streamparm *a) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct v4l2_captureparm *cparm = &a->parm.capture; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(a, 0, sizeof(*a)); ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ cparm->capability = V4L2_CAP_TIMEPERFRAME; ++ cparm->timeperframe = sensor->timeperframe; ++ ++ return 0; ++} ++ ++static int ioctl_s_parm(struct v4l2_int_device *s, ++ struct v4l2_streamparm *a) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; ++ u32 tgt_fps; /* target frames per secound */ ++ int rval; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ if ((timeperframe->numerator == 0) ++ || (timeperframe->denominator == 0)) { ++ timeperframe->denominator = DEFAULT_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ tgt_fps = timeperframe->denominator / timeperframe->numerator; ++ ++ if (tgt_fps > MAX_FPS) { ++ timeperframe->denominator = MAX_FPS; ++ timeperframe->numerator = 1; ++ } else if (tgt_fps < MIN_FPS) { ++ timeperframe->denominator = MIN_FPS; ++ timeperframe->numerator = 1; ++ } ++ ++ sensor->timeperframe = *timeperframe; ++ ++ rval = tcm825x_configure(s); ++ ++ return rval; ++} ++ ++static int ioctl_s_power(struct v4l2_int_device *s, int on) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ ++ return sensor->platform_data->power_set(on); ++} ++ ++/* ++ * Given the image capture format in pix, the nominal frame period in ++ * timeperframe, calculate the required xclk frequency. ++ * ++ * TCM825X input frequency characteristics are: ++ * Minimum 11.9 MHz, Typical 24.57 MHz and maximum 25/27 MHz ++ */ ++ ++static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ struct v4l2_fract *timeperframe = &sensor->timeperframe; ++ u32 tgt_xclk; /* target xclk */ ++ u32 tgt_fps; /* target frames per secound */ ++ int rval; ++ ++ rval = sensor->platform_data->ifparm(p); ++ if (rval) ++ return rval; ++ ++ tgt_fps = timeperframe->denominator / timeperframe->numerator; ++ ++ tgt_xclk = (tgt_fps <= HIGH_FPS_MODE_LOWER_LIMIT) ? ++ (2457 * tgt_fps) / MAX_HALF_FPS : ++ (2457 * tgt_fps) / MAX_FPS; ++ tgt_xclk *= 10000; ++ ++ tgt_xclk = min(tgt_xclk, (u32)TCM825X_XCLK_MAX); ++ tgt_xclk = max(tgt_xclk, (u32)TCM825X_XCLK_MIN); ++ ++ p->u.bt656.clock_curr = tgt_xclk; ++ ++ return 0; ++} ++ ++static int ioctl_g_needs_reset(struct v4l2_int_device *s, void *buf) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ ++ return sensor->platform_data->needs_reset(s, buf, &sensor->pix); ++} ++ ++static int ioctl_reset(struct v4l2_int_device *s) ++{ ++ return -EBUSY; ++} ++ ++static int ioctl_init(struct v4l2_int_device *s) ++{ ++ return tcm825x_configure(s); ++} ++ ++static int ioctl_dev_exit(struct v4l2_int_device *s) ++{ ++ return 0; ++} ++ ++static int ioctl_dev_init(struct v4l2_int_device *s) ++{ ++ struct tcm825x_sensor *sensor = s->priv; ++ int r; ++ ++ r = tcm825x_read_reg(sensor->i2c_client, 0x01); ++ if (r < 0) ++ return r; ++ if (r == 0) { ++ dev_err(&sensor->i2c_client->dev, "device not detected\n"); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static struct v4l2_int_ioctl_desc tcm825x_ioctl_desc[] = { ++ { vidioc_int_dev_init_num, ++ (v4l2_int_ioctl_func *)ioctl_dev_init }, ++ { vidioc_int_dev_exit_num, ++ (v4l2_int_ioctl_func *)ioctl_dev_exit }, ++ { vidioc_int_s_power_num, ++ (v4l2_int_ioctl_func *)ioctl_s_power }, ++ { vidioc_int_g_ifparm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ifparm }, ++ { vidioc_int_g_needs_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_g_needs_reset }, ++ { vidioc_int_reset_num, ++ (v4l2_int_ioctl_func *)ioctl_reset }, ++ { vidioc_int_init_num, ++ (v4l2_int_ioctl_func *)ioctl_init }, ++ { vidioc_int_enum_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_enum_fmt_cap }, ++ { vidioc_int_try_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_try_fmt_cap }, ++ { vidioc_int_g_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_g_fmt_cap }, ++ { vidioc_int_s_fmt_cap_num, ++ (v4l2_int_ioctl_func *)ioctl_s_fmt_cap }, ++ { vidioc_int_g_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_g_parm }, ++ { vidioc_int_s_parm_num, ++ (v4l2_int_ioctl_func *)ioctl_s_parm }, ++ { vidioc_int_queryctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_queryctrl }, ++ { vidioc_int_g_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_g_ctrl }, ++ { vidioc_int_s_ctrl_num, ++ (v4l2_int_ioctl_func *)ioctl_s_ctrl }, ++}; ++ ++static struct v4l2_int_slave tcm825x_slave = { ++ .ioctls = tcm825x_ioctl_desc, ++ .num_ioctls = ARRAY_SIZE(tcm825x_ioctl_desc), ++}; ++ ++static struct tcm825x_sensor tcm825x; ++ ++static struct v4l2_int_device tcm825x_int_device = { ++ .module = THIS_MODULE, ++ .name = TCM825X_NAME, ++ .priv = &tcm825x, ++ .type = v4l2_int_type_slave, ++ .u = { ++ .slave = &tcm825x_slave, ++ }, ++}; ++ ++static int tcm825x_probe(struct i2c_client *client, ++ const struct i2c_device_id *did) ++{ ++ struct tcm825x_sensor *sensor = &tcm825x; ++ ++ if (i2c_get_clientdata(client)) ++ return -EBUSY; ++ ++ sensor->platform_data = client->dev.platform_data; ++ ++ if (sensor->platform_data == NULL ++ || !sensor->platform_data->is_okay()) ++ return -ENODEV; ++ ++ sensor->v4l2_int_device = &tcm825x_int_device; ++ ++ sensor->i2c_client = client; ++ i2c_set_clientdata(client, sensor); ++ ++ /* Make the default capture format QVGA RGB565 */ ++ sensor->pix.width = tcm825x_sizes[QVGA].width; ++ sensor->pix.height = tcm825x_sizes[QVGA].height; ++ sensor->pix.pixelformat = V4L2_PIX_FMT_RGB565; ++ ++ return v4l2_int_device_register(sensor->v4l2_int_device); ++} ++ ++static int tcm825x_remove(struct i2c_client *client) ++{ ++ struct tcm825x_sensor *sensor = i2c_get_clientdata(client); ++ ++ if (!client->adapter) ++ return -ENODEV; /* our client isn't attached */ ++ ++ v4l2_int_device_unregister(sensor->v4l2_int_device); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id tcm825x_id[] = { ++ { "tcm825x", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tcm825x_id); ++ ++static struct i2c_driver tcm825x_i2c_driver = { ++ .driver = { ++ .name = TCM825X_NAME, ++ }, ++ .probe = tcm825x_probe, ++ .remove = tcm825x_remove, ++ .id_table = tcm825x_id, ++}; ++ ++static struct tcm825x_sensor tcm825x = { ++ .timeperframe = { ++ .numerator = 1, ++ .denominator = DEFAULT_FPS, ++ }, ++}; ++ ++static int __init tcm825x_init(void) ++{ ++ int rval; ++ ++ rval = i2c_add_driver(&tcm825x_i2c_driver); ++ if (rval) ++ printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", ++ __func__); ++ ++ return rval; ++} ++ ++static void __exit tcm825x_exit(void) ++{ ++ i2c_del_driver(&tcm825x_i2c_driver); ++} ++ ++/* ++ * FIXME: Menelaus isn't ready (?) at module_init stage, so use ++ * late_initcall for now. ++ */ ++late_initcall(tcm825x_init); ++module_exit(tcm825x_exit); ++ ++MODULE_AUTHOR("Sakari Ailus "); ++MODULE_DESCRIPTION("TCM825x camera sensor driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/i2c/tcm825x.h b/drivers/media/i2c/tcm825x.h +new file mode 100644 +index 0000000..8ebab95 +--- /dev/null ++++ b/drivers/media/i2c/tcm825x.h +@@ -0,0 +1,200 @@ ++/* ++ * drivers/media/i2c/tcm825x.h ++ * ++ * Register definitions for the TCM825X CameraChip. ++ * ++ * Author: David Cohen (david.cohen@indt.org.br) ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ * ++ * This file was based on ov9640.h from MontaVista ++ */ ++ ++#ifndef TCM825X_H ++#define TCM825X_H ++ ++#include ++ ++#include ++ ++#define TCM825X_NAME "tcm825x" ++ ++#define TCM825X_MASK(x) x & 0x00ff ++#define TCM825X_ADDR(x) (x & 0xff00) >> 8 ++ ++/* The TCM825X I2C sensor chip has a fixed slave address of 0x3d. */ ++#define TCM825X_I2C_ADDR 0x3d ++ ++/* ++ * define register offsets for the TCM825X sensor chip ++ * OFFSET(8 bits) + MASK(8 bits) ++ * MASK bit 4 and 3 are used when the register uses more than one address ++ */ ++#define TCM825X_FPS 0x0280 ++#define TCM825X_ACF 0x0240 ++#define TCM825X_DOUTBUF 0x020C ++#define TCM825X_DCLKP 0x0202 ++#define TCM825X_ACFDET 0x0201 ++#define TCM825X_DOUTSW 0x0380 ++#define TCM825X_DATAHZ 0x0340 ++#define TCM825X_PICSIZ 0x033c ++#define TCM825X_PICFMT 0x0302 ++#define TCM825X_V_INV 0x0480 ++#define TCM825X_H_INV 0x0440 ++#define TCM825X_ESRLSW 0x0430 ++#define TCM825X_V_LENGTH 0x040F ++#define TCM825X_ALCSW 0x0580 ++#define TCM825X_ESRLIM 0x0560 ++#define TCM825X_ESRSPD_U 0x051F ++#define TCM825X_ESRSPD_L 0x06FF ++#define TCM825X_AG 0x07FF ++#define TCM825X_ESRSPD2 0x06FF ++#define TCM825X_ALCMODE 0x0830 ++#define TCM825X_ALCH 0x080F ++#define TCM825X_ALCL 0x09FF ++#define TCM825X_AWBSW 0x0A80 ++#define TCM825X_MRG 0x0BFF ++#define TCM825X_MBG 0x0CFF ++#define TCM825X_GAMSW 0x0D80 ++#define TCM825X_HDTG 0x0EFF ++#define TCM825X_VDTG 0x0FFF ++#define TCM825X_HDTCORE 0x10F0 ++#define TCM825X_VDTCORE 0x100F ++#define TCM825X_CONT 0x11FF ++#define TCM825X_BRIGHT 0x12FF ++#define TCM825X_VHUE 0x137F ++#define TCM825X_UHUE 0x147F ++#define TCM825X_VGAIN 0x153F ++#define TCM825X_UGAIN 0x163F ++#define TCM825X_UVCORE 0x170F ++#define TCM825X_SATU 0x187F ++#define TCM825X_MHMODE 0x1980 ++#define TCM825X_MHLPFSEL 0x1940 ++#define TCM825X_YMODE 0x1930 ++#define TCM825X_MIXHG 0x1907 ++#define TCM825X_LENS 0x1A3F ++#define TCM825X_AGLIM 0x1BE0 ++#define TCM825X_LENSRPOL 0x1B10 ++#define TCM825X_LENSRGAIN 0x1B0F ++#define TCM825X_ES100S 0x1CFF ++#define TCM825X_ES120S 0x1DFF ++#define TCM825X_DMASK 0x1EC0 ++#define TCM825X_CODESW 0x1E20 ++#define TCM825X_CODESEL 0x1E10 ++#define TCM825X_TESPIC 0x1E04 ++#define TCM825X_PICSEL 0x1E03 ++#define TCM825X_HNUM 0x20FF ++#define TCM825X_VOUTPH 0x287F ++#define TCM825X_ESROUT 0x327F ++#define TCM825X_ESROUT2 0x33FF ++#define TCM825X_AGOUT 0x34FF ++#define TCM825X_DGOUT 0x353F ++#define TCM825X_AGSLOW1 0x39C0 ++#define TCM825X_FLLSMODE 0x3930 ++#define TCM825X_FLLSLIM 0x390F ++#define TCM825X_DETSEL 0x3AF0 ++#define TCM825X_ACDETNC 0x3A0F ++#define TCM825X_AGSLOW2 0x3BC0 ++#define TCM825X_DG 0x3B3F ++#define TCM825X_REJHLEV 0x3CFF ++#define TCM825X_ALCLOCK 0x3D80 ++#define TCM825X_FPSLNKSW 0x3D40 ++#define TCM825X_ALCSPD 0x3D30 ++#define TCM825X_REJH 0x3D03 ++#define TCM825X_SHESRSW 0x3E80 ++#define TCM825X_ESLIMSEL 0x3E40 ++#define TCM825X_SHESRSPD 0x3E30 ++#define TCM825X_ELSTEP 0x3E0C ++#define TCM825X_ELSTART 0x3E03 ++#define TCM825X_AGMIN 0x3FFF ++#define TCM825X_PREGRG 0x423F ++#define TCM825X_PREGBG 0x433F ++#define TCM825X_PRERG 0x443F ++#define TCM825X_PREBG 0x453F ++#define TCM825X_MSKBR 0x477F ++#define TCM825X_MSKGR 0x487F ++#define TCM825X_MSKRB 0x497F ++#define TCM825X_MSKGB 0x4A7F ++#define TCM825X_MSKRG 0x4B7F ++#define TCM825X_MSKBG 0x4C7F ++#define TCM825X_HDTCSW 0x4D80 ++#define TCM825X_VDTCSW 0x4D40 ++#define TCM825X_DTCYL 0x4D3F ++#define TCM825X_HDTPSW 0x4E80 ++#define TCM825X_VDTPSW 0x4E40 ++#define TCM825X_DTCGAIN 0x4E3F ++#define TCM825X_DTLLIMSW 0x4F10 ++#define TCM825X_DTLYLIM 0x4F0F ++#define TCM825X_YLCUTLMSK 0x5080 ++#define TCM825X_YLCUTL 0x503F ++#define TCM825X_YLCUTHMSK 0x5180 ++#define TCM825X_YLCUTH 0x513F ++#define TCM825X_UVSKNC 0x527F ++#define TCM825X_UVLJ 0x537F ++#define TCM825X_WBGMIN 0x54FF ++#define TCM825X_WBGMAX 0x55FF ++#define TCM825X_WBSPDUP 0x5603 ++#define TCM825X_ALLAREA 0x5820 ++#define TCM825X_WBLOCK 0x5810 ++#define TCM825X_WB2SP 0x580F ++#define TCM825X_KIZUSW 0x5920 ++#define TCM825X_PBRSW 0x5910 ++#define TCM825X_ABCSW 0x5903 ++#define TCM825X_PBDLV 0x5AFF ++#define TCM825X_PBC1LV 0x5BFF ++ ++#define TCM825X_NUM_REGS (TCM825X_ADDR(TCM825X_PBC1LV) + 1) ++ ++#define TCM825X_BYTES_PER_PIXEL 2 ++ ++#define TCM825X_REG_TERM 0xff /* terminating list entry for reg */ ++#define TCM825X_VAL_TERM 0xff /* terminating list entry for val */ ++ ++/* define a structure for tcm825x register initialization values */ ++struct tcm825x_reg { ++ u8 val; ++ u16 reg; ++}; ++ ++enum image_size { subQCIF = 0, QQVGA, QCIF, QVGA, CIF, VGA }; ++enum pixel_format { YUV422 = 0, RGB565 }; ++#define NUM_IMAGE_SIZES 6 ++#define NUM_PIXEL_FORMATS 2 ++ ++#define TCM825X_XCLK_MIN 11900000 ++#define TCM825X_XCLK_MAX 25000000 ++ ++struct capture_size { ++ unsigned long width; ++ unsigned long height; ++}; ++ ++struct tcm825x_platform_data { ++ /* Is the sensor usable? Doesn't yet mean it's there, but you ++ * can try! */ ++ int (*is_okay)(void); ++ /* Set power state, zero is off, non-zero is on. */ ++ int (*power_set)(int power); ++ /* Default registers written after power-on or reset. */ ++ const struct tcm825x_reg *(*default_regs)(void); ++ int (*needs_reset)(struct v4l2_int_device *s, void *buf, ++ struct v4l2_pix_format *fmt); ++ int (*ifparm)(struct v4l2_ifparm *p); ++ int (*is_upside_down)(void); ++}; ++ ++/* Array of image sizes supported by TCM825X. These must be ordered from ++ * smallest image size to largest. ++ */ ++static const struct capture_size tcm825x_sizes[] = { ++ { 128, 96 }, /* subQCIF */ ++ { 160, 120 }, /* QQVGA */ ++ { 176, 144 }, /* QCIF */ ++ { 320, 240 }, /* QVGA */ ++ { 352, 288 }, /* CIF */ ++ { 640, 480 }, /* VGA */ ++}; ++ ++#endif /* ifndef TCM825X_H */ +diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c +new file mode 100644 +index 0000000..f7707e6 +--- /dev/null ++++ b/drivers/media/i2c/tda7432.c +@@ -0,0 +1,485 @@ ++/* ++ * For the STS-Thompson TDA7432 audio processor chip ++ * ++ * Handles audio functions: volume, balance, tone, loudness ++ * This driver will not complain if used with any ++ * other i2c device with the same address. ++ * ++ * Muting and tone control by Jonathan Isom ++ * ++ * Copyright (c) 2000 Eric Sandeen ++ * Copyright (c) 2006 Mauro Carvalho Chehab ++ * This code is placed under the terms of the GNU General Public License ++ * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) ++ * Which was based on tda8425.c by Greg Alexander (c) 1998 ++ * ++ * OPTIONS: ++ * debug - set to 1 if you'd like to see debug messages ++ * set to 2 if you'd like to be inundated with debug messages ++ * ++ * loudness - set between 0 and 15 for varying degrees of loudness effect ++ * ++ * maxvol - set maximium volume to +20db (1), default is 0db(0) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#ifndef VIDEO_AUDIO_BALANCE ++# define VIDEO_AUDIO_BALANCE 32 ++#endif ++ ++MODULE_AUTHOR("Eric Sandeen "); ++MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); ++MODULE_LICENSE("GPL"); ++ ++static int maxvol; ++static int loudness; /* disable loudness by default */ ++static int debug; /* insmod parameter */ ++module_param(debug, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); ++module_param(loudness, int, S_IRUGO); ++MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); ++module_param(maxvol, int, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); ++ ++ ++/* Structure of address and subaddresses for the tda7432 */ ++ ++struct tda7432 { ++ struct v4l2_subdev sd; ++ int addr; ++ int input; ++ int volume; ++ int muted; ++ int bass, treble; ++ int lf, lr, rf, rr; ++ int loud; ++}; ++ ++static inline struct tda7432 *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct tda7432, sd); ++} ++ ++/* The TDA7432 is made by STS-Thompson ++ * http://www.st.com ++ * http://us.st.com/stonline/books/pdf/docs/4056.pdf ++ * ++ * TDA7432: I2C-bus controlled basic audio processor ++ * ++ * The TDA7432 controls basic audio functions like volume, balance, ++ * and tone control (including loudness). It also has four channel ++ * output (for front and rear). Since most vidcap cards probably ++ * don't have 4 channel output, this driver will set front & rear ++ * together (no independent control). ++ */ ++ ++ /* Subaddresses for TDA7432 */ ++ ++#define TDA7432_IN 0x00 /* Input select */ ++#define TDA7432_VL 0x01 /* Volume */ ++#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ ++#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ ++#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ ++#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ ++#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ ++#define TDA7432_LD 0x07 /* Loudness */ ++ ++ ++ /* Masks for bits in TDA7432 subaddresses */ ++ ++/* Many of these not used - just for documentation */ ++ ++/* Subaddress 0x00 - Input selection and bass control */ ++ ++/* Bits 0,1,2 control input: ++ * 0x00 - Stereo input ++ * 0x02 - Mono input ++ * 0x03 - Mute (Using Attenuators Plays better with modules) ++ * Mono probably isn't used - I'm guessing only the stereo ++ * input is connected on most cards, so we'll set it to stereo. ++ * ++ * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut ++ * Bit 4 controls bass range: 0/1 is extended/standard bass range ++ * ++ * Highest 3 bits not used ++ */ ++ ++#define TDA7432_STEREO_IN 0 ++#define TDA7432_MONO_IN 2 /* Probably won't be used */ ++#define TDA7432_BASS_SYM 1 << 3 ++#define TDA7432_BASS_NORM 1 << 4 ++ ++/* Subaddress 0x01 - Volume */ ++ ++/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps ++ * Recommended maximum is +20 dB ++ * ++ * +32dB: 0x00 ++ * +20dB: 0x0c ++ * 0dB: 0x20 ++ * -79dB: 0x6f ++ * ++ * MSB (bit 7) controls loudness: 1/0 is loudness on/off ++ */ ++ ++#define TDA7432_VOL_0DB 0x20 ++#define TDA7432_LD_ON 1 << 7 ++ ++ ++/* Subaddress 0x02 - Tone control */ ++ ++/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB ++ * 0x0 is 14dB, 0x7 is 0dB ++ * ++ * Bit 3 controls treble attenuation/gain (sign) ++ * 1 = gain (+) ++ * 0 = attenuation (-) ++ * ++ * Bits 4,5,6 control absolute bass gain from 0dB to 14dB ++ * (This is only true for normal base range, set in 0x00) ++ * 0x0 << 4 is 14dB, 0x7 is 0dB ++ * ++ * Bit 7 controls bass attenuation/gain (sign) ++ * 1 << 7 = gain (+) ++ * 0 << 7 = attenuation (-) ++ * ++ * Example: ++ * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble ++ */ ++ ++#define TDA7432_TREBLE_0DB 0xf ++#define TDA7432_TREBLE 7 ++#define TDA7432_TREBLE_GAIN 1 << 3 ++#define TDA7432_BASS_0DB 0xf ++#define TDA7432_BASS 7 << 4 ++#define TDA7432_BASS_GAIN 1 << 7 ++ ++ ++/* Subaddress 0x03 - Left Front attenuation */ ++/* Subaddress 0x04 - Left Rear attenuation */ ++/* Subaddress 0x05 - Right Front attenuation */ ++/* Subaddress 0x06 - Right Rear attenuation */ ++ ++/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB ++ * in 1.5dB steps. ++ * ++ * 0x00 is 0dB ++ * 0x1f is -37.5dB ++ * ++ * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) ++ * We'll use the mute on the input, though (above) ++ * Bits 6,7 unused ++ */ ++ ++#define TDA7432_ATTEN_0DB 0x00 ++#define TDA7432_MUTE 0x1 << 5 ++ ++ ++/* Subaddress 0x07 - Loudness Control */ ++ ++/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps ++ * when bit 4 is NOT set ++ * ++ * 0x0 is 0dB ++ * 0xf is -15dB ++ * ++ * If bit 4 is set, then there is a flat attenuation according to ++ * the lower 4 bits, as above. ++ * ++ * Bits 5,6,7 unused ++ */ ++ ++ ++ ++/* Begin code */ ++ ++static int tda7432_write(struct v4l2_subdev *sd, int subaddr, int val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ unsigned char buffer[2]; ++ ++ v4l2_dbg(2, debug, sd, "In tda7432_write\n"); ++ v4l2_dbg(1, debug, sd, "Writing %d 0x%x\n", subaddr, val); ++ buffer[0] = subaddr; ++ buffer[1] = val; ++ if (2 != i2c_master_send(client, buffer, 2)) { ++ v4l2_err(sd, "I/O error, trying (write %d 0x%x)\n", ++ subaddr, val); ++ return -1; ++ } ++ return 0; ++} ++ ++static int tda7432_set(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tda7432 *t = to_state(sd); ++ unsigned char buf[16]; ++ ++ v4l2_dbg(1, debug, sd, ++ "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", ++ t->input, t->volume, t->bass, t->treble, t->lf, t->lr, ++ t->rf, t->rr, t->loud); ++ buf[0] = TDA7432_IN; ++ buf[1] = t->input; ++ buf[2] = t->volume; ++ buf[3] = t->bass; ++ buf[4] = t->treble; ++ buf[5] = t->lf; ++ buf[6] = t->lr; ++ buf[7] = t->rf; ++ buf[8] = t->rr; ++ buf[9] = t->loud; ++ if (10 != i2c_master_send(client, buf, 10)) { ++ v4l2_err(sd, "I/O error, trying tda7432_set\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void do_tda7432_init(struct v4l2_subdev *sd) ++{ ++ struct tda7432 *t = to_state(sd); ++ ++ v4l2_dbg(2, debug, sd, "In tda7432_init\n"); ++ ++ t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ ++ TDA7432_BASS_SYM | /* Symmetric bass cut */ ++ TDA7432_BASS_NORM; /* Normal bass range */ ++ t->volume = 0x3b ; /* -27dB Volume */ ++ if (loudness) /* Turn loudness on? */ ++ t->volume |= TDA7432_LD_ON; ++ t->muted = 1; ++ t->treble = TDA7432_TREBLE_0DB; /* 0dB Treble */ ++ t->bass = TDA7432_BASS_0DB; /* 0dB Bass */ ++ t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ ++ t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ ++ t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ ++ t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ ++ t->loud = loudness; /* insmod parameter */ ++ ++ tda7432_set(sd); ++} ++ ++static int tda7432_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct tda7432 *t = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ ctrl->value=t->muted; ++ return 0; ++ case V4L2_CID_AUDIO_VOLUME: ++ if (!maxvol){ /* max +20db */ ++ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 630; ++ } else { /* max 0db */ ++ ctrl->value = ( 0x6f - (t->volume & 0x7F) ) * 829; ++ } ++ return 0; ++ case V4L2_CID_AUDIO_BALANCE: ++ { ++ if ( (t->lf) < (t->rf) ) ++ /* right is attenuated, balance shifted left */ ++ ctrl->value = (32768 - 1057*(t->rf)); ++ else ++ /* left is attenuated, balance shifted right */ ++ ctrl->value = (32768 + 1057*(t->lf)); ++ return 0; ++ } ++ case V4L2_CID_AUDIO_BASS: ++ { ++ /* Bass/treble 4 bits each */ ++ int bass=t->bass; ++ if(bass >= 0x8) ++ bass = ~(bass - 0x8) & 0xf; ++ ctrl->value = (bass << 12)+(bass << 8)+(bass << 4)+(bass); ++ return 0; ++ } ++ case V4L2_CID_AUDIO_TREBLE: ++ { ++ int treble=t->treble; ++ if(treble >= 0x8) ++ treble = ~(treble - 0x8) & 0xf; ++ ctrl->value = (treble << 12)+(treble << 8)+(treble << 4)+(treble); ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct tda7432 *t = to_state(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ t->muted=ctrl->value; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ if(!maxvol){ /* max +20db */ ++ t->volume = 0x6f - ((ctrl->value)/630); ++ } else { /* max 0db */ ++ t->volume = 0x6f - ((ctrl->value)/829); ++ } ++ if (loudness) /* Turn on the loudness bit */ ++ t->volume |= TDA7432_LD_ON; ++ ++ tda7432_write(sd, TDA7432_VL, t->volume); ++ return 0; ++ case V4L2_CID_AUDIO_BALANCE: ++ if (ctrl->value < 32768) { ++ /* shifted to left, attenuate right */ ++ t->rr = (32768 - ctrl->value)/1057; ++ t->rf = t->rr; ++ t->lr = TDA7432_ATTEN_0DB; ++ t->lf = TDA7432_ATTEN_0DB; ++ } else if(ctrl->value > 32769) { ++ /* shifted to right, attenuate left */ ++ t->lf = (ctrl->value - 32768)/1057; ++ t->lr = t->lf; ++ t->rr = TDA7432_ATTEN_0DB; ++ t->rf = TDA7432_ATTEN_0DB; ++ } else { ++ /* centered */ ++ t->rr = TDA7432_ATTEN_0DB; ++ t->rf = TDA7432_ATTEN_0DB; ++ t->lf = TDA7432_ATTEN_0DB; ++ t->lr = TDA7432_ATTEN_0DB; ++ } ++ break; ++ case V4L2_CID_AUDIO_BASS: ++ t->bass = ctrl->value >> 12; ++ if(t->bass>= 0x8) ++ t->bass = (~t->bass & 0xf) + 0x8 ; ++ ++ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); ++ return 0; ++ case V4L2_CID_AUDIO_TREBLE: ++ t->treble= ctrl->value >> 12; ++ if(t->treble>= 0x8) ++ t->treble = (~t->treble & 0xf) + 0x8 ; ++ ++ tda7432_write(sd, TDA7432_TN, 0x10 | (t->bass << 4) | t->treble); ++ return 0; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Used for both mute and balance changes */ ++ if (t->muted) ++ { ++ /* Mute & update balance*/ ++ tda7432_write(sd, TDA7432_LF, t->lf | TDA7432_MUTE); ++ tda7432_write(sd, TDA7432_LR, t->lr | TDA7432_MUTE); ++ tda7432_write(sd, TDA7432_RF, t->rf | TDA7432_MUTE); ++ tda7432_write(sd, TDA7432_RR, t->rr | TDA7432_MUTE); ++ } else { ++ tda7432_write(sd, TDA7432_LF, t->lf); ++ tda7432_write(sd, TDA7432_LR, t->lr); ++ tda7432_write(sd, TDA7432_RF, t->rf); ++ tda7432_write(sd, TDA7432_RR, t->rr); ++ } ++ return 0; ++} ++ ++static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) ++{ ++ switch (qc->id) { ++ case V4L2_CID_AUDIO_VOLUME: ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); ++ case V4L2_CID_AUDIO_MUTE: ++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); ++ case V4L2_CID_AUDIO_BALANCE: ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); ++ } ++ return -EINVAL; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops tda7432_core_ops = { ++ .queryctrl = tda7432_queryctrl, ++ .g_ctrl = tda7432_g_ctrl, ++ .s_ctrl = tda7432_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_ops tda7432_ops = { ++ .core = &tda7432_core_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* *********************** * ++ * i2c interface functions * ++ * *********************** */ ++ ++static int tda7432_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct tda7432 *t; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ t = kzalloc(sizeof(*t), GFP_KERNEL); ++ if (!t) ++ return -ENOMEM; ++ sd = &t->sd; ++ v4l2_i2c_subdev_init(sd, client, &tda7432_ops); ++ if (loudness < 0 || loudness > 15) { ++ v4l2_warn(sd, "loudness parameter must be between 0 and 15\n"); ++ if (loudness < 0) ++ loudness = 0; ++ if (loudness > 15) ++ loudness = 15; ++ } ++ ++ do_tda7432_init(sd); ++ return 0; ++} ++ ++static int tda7432_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ do_tda7432_init(sd); ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id tda7432_id[] = { ++ { "tda7432", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tda7432_id); ++ ++static struct i2c_driver tda7432_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tda7432", ++ }, ++ .probe = tda7432_probe, ++ .remove = tda7432_remove, ++ .id_table = tda7432_id, ++}; ++ ++module_i2c_driver(tda7432_driver); +diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c +new file mode 100644 +index 0000000..3d7ddd9 +--- /dev/null ++++ b/drivers/media/i2c/tda9840.c +@@ -0,0 +1,224 @@ ++ /* ++ tda9840 - i2c-driver for the tda9840 by SGS Thomson ++ ++ Copyright (C) 1998-2003 Michael Hunold ++ Copyright (C) 2008 Hans Verkuil ++ ++ The tda9840 is a stereo/dual sound processor with digital ++ identification. It can be found at address 0x84 on the i2c-bus. ++ ++ For detailed informations download the specifications directly ++ from SGS Thomson at http://www.st.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 ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_DESCRIPTION("tda9840 driver"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++#define SWITCH 0x00 ++#define LEVEL_ADJUST 0x02 ++#define STEREO_ADJUST 0x03 ++#define TEST 0x04 ++ ++#define TDA9840_SET_MUTE 0x00 ++#define TDA9840_SET_MONO 0x10 ++#define TDA9840_SET_STEREO 0x2a ++#define TDA9840_SET_LANG1 0x12 ++#define TDA9840_SET_LANG2 0x1e ++#define TDA9840_SET_BOTH 0x1a ++#define TDA9840_SET_BOTH_R 0x16 ++#define TDA9840_SET_EXTERNAL 0x7a ++ ++ ++static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (i2c_smbus_write_byte_data(client, reg, val)) ++ v4l2_dbg(1, debug, sd, "error writing %02x to %02x\n", ++ val, reg); ++} ++ ++static int tda9840_status(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 byte; ++ ++ if (1 != i2c_master_recv(client, &byte, 1)) { ++ v4l2_dbg(1, debug, sd, ++ "i2c_master_recv() failed\n"); ++ return -EIO; ++ } ++ ++ if (byte & 0x80) { ++ v4l2_dbg(1, debug, sd, ++ "TDA9840_DETECT: register contents invalid\n"); ++ return -EINVAL; ++ } ++ ++ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); ++ return byte & 0x60; ++} ++ ++static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) ++{ ++ int stat = tda9840_status(sd); ++ int byte; ++ ++ if (t->index) ++ return -EINVAL; ++ ++ stat = stat < 0 ? 0 : stat; ++ if (stat == 0 || stat == 0x60) /* mono input */ ++ byte = TDA9840_SET_MONO; ++ else if (stat == 0x40) /* stereo input */ ++ byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? ++ TDA9840_SET_MONO : TDA9840_SET_STEREO; ++ else { /* bilingual */ ++ switch (t->audmode) { ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ byte = TDA9840_SET_BOTH; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ byte = TDA9840_SET_LANG2; ++ break; ++ default: ++ byte = TDA9840_SET_LANG1; ++ break; ++ } ++ } ++ v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); ++ tda9840_write(sd, SWITCH, byte); ++ return 0; ++} ++ ++static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) ++{ ++ int stat = tda9840_status(sd); ++ ++ if (stat < 0) ++ return stat; ++ ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ ++ switch (stat & 0x60) { ++ case 0x00: ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ break; ++ case 0x20: ++ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ break; ++ case 0x40: ++ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; ++ break; ++ default: /* Incorrect detect */ ++ t->rxsubchans = V4L2_TUNER_MODE_MONO; ++ break; ++ } ++ return 0; ++} ++ ++static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops tda9840_core_ops = { ++ .g_chip_ident = tda9840_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { ++ .s_tuner = tda9840_s_tuner, ++ .g_tuner = tda9840_g_tuner, ++}; ++ ++static const struct v4l2_subdev_ops tda9840_ops = { ++ .core = &tda9840_core_ops, ++ .tuner = &tda9840_tuner_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int tda9840_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ ++ /* let's see whether this adapter can support what we need */ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_READ_BYTE_DATA | ++ I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); ++ if (sd == NULL) ++ return -ENOMEM; ++ v4l2_i2c_subdev_init(sd, client, &tda9840_ops); ++ ++ /* set initial values for level & stereo - adjustment, mode */ ++ tda9840_write(sd, LEVEL_ADJUST, 0); ++ tda9840_write(sd, STEREO_ADJUST, 0); ++ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); ++ return 0; ++} ++ ++static int tda9840_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(sd); ++ return 0; ++} ++ ++static const struct i2c_device_id tda9840_id[] = { ++ { "tda9840", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tda9840_id); ++ ++static struct i2c_driver tda9840_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tda9840", ++ }, ++ .probe = tda9840_probe, ++ .remove = tda9840_remove, ++ .id_table = tda9840_id, ++}; ++ ++module_i2c_driver(tda9840_driver); +diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c +new file mode 100644 +index 0000000..3d5b06a +--- /dev/null ++++ b/drivers/media/i2c/tea6415c.c +@@ -0,0 +1,187 @@ ++ /* ++ tea6415c - i2c-driver for the tea6415c by SGS Thomson ++ ++ Copyright (C) 1998-2003 Michael Hunold ++ Copyright (C) 2008 Hans Verkuil ++ ++ The tea6415c is a bus controlled video-matrix-switch ++ with 8 inputs and 6 outputs. ++ It is cascadable, i.e. it can be found at the addresses ++ 0x86 and 0x06 on the i2c-bus. ++ ++ For detailed informations download the specifications directly ++ from SGS Thomson at http://www.st.com ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tea6415c.h" ++ ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_DESCRIPTION("tea6415c driver"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* makes a connection between the input-pin 'i' and the output-pin 'o' */ ++static int tea6415c_s_routing(struct v4l2_subdev *sd, ++ u32 i, u32 o, u32 config) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 byte = 0; ++ int ret; ++ ++ v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); ++ ++ /* check if the pins are valid */ ++ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) ++ && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) ++ return -EINVAL; ++ ++ /* to understand this, have a look at the tea6415c-specs (p.5) */ ++ switch (o) { ++ case 18: ++ byte = 0x00; ++ break; ++ case 14: ++ byte = 0x20; ++ break; ++ case 16: ++ byte = 0x10; ++ break; ++ case 17: ++ byte = 0x08; ++ break; ++ case 15: ++ byte = 0x18; ++ break; ++ case 13: ++ byte = 0x28; ++ break; ++ } ++ ++ switch (i) { ++ case 5: ++ byte |= 0x00; ++ break; ++ case 8: ++ byte |= 0x04; ++ break; ++ case 3: ++ byte |= 0x02; ++ break; ++ case 20: ++ byte |= 0x06; ++ break; ++ case 6: ++ byte |= 0x01; ++ break; ++ case 10: ++ byte |= 0x05; ++ break; ++ case 1: ++ byte |= 0x03; ++ break; ++ case 11: ++ byte |= 0x07; ++ break; ++ } ++ ++ ret = i2c_smbus_write_byte(client, byte); ++ if (ret) { ++ v4l2_dbg(1, debug, sd, ++ "i2c_smbus_write_byte() failed, ret:%d\n", ret); ++ return -EIO; ++ } ++ return ret; ++} ++ ++static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops tea6415c_core_ops = { ++ .g_chip_ident = tea6415c_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_video_ops tea6415c_video_ops = { ++ .s_routing = tea6415c_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops tea6415c_ops = { ++ .core = &tea6415c_core_ops, ++ .video = &tea6415c_video_ops, ++}; ++ ++static int tea6415c_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ ++ /* let's see whether this adapter can support what we need */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); ++ if (sd == NULL) ++ return -ENOMEM; ++ v4l2_i2c_subdev_init(sd, client, &tea6415c_ops); ++ return 0; ++} ++ ++static int tea6415c_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(sd); ++ return 0; ++} ++ ++static const struct i2c_device_id tea6415c_id[] = { ++ { "tea6415c", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tea6415c_id); ++ ++static struct i2c_driver tea6415c_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tea6415c", ++ }, ++ .probe = tea6415c_probe, ++ .remove = tea6415c_remove, ++ .id_table = tea6415c_id, ++}; ++ ++module_i2c_driver(tea6415c_driver); +diff --git a/drivers/media/i2c/tea6415c.h b/drivers/media/i2c/tea6415c.h +new file mode 100644 +index 0000000..3a47d69 +--- /dev/null ++++ b/drivers/media/i2c/tea6415c.h +@@ -0,0 +1,27 @@ ++#ifndef __INCLUDED_TEA6415C__ ++#define __INCLUDED_TEA6415C__ ++ ++/* the tea6415c's design is quite brain-dead. although there are ++ 8 inputs and 6 outputs, these aren't enumerated in any way. because ++ I don't want to say "connect input pin 20 to output pin 17", I define ++ a "virtual" pin-order. */ ++ ++/* input pins */ ++#define TEA6415C_OUTPUT1 18 ++#define TEA6415C_OUTPUT2 14 ++#define TEA6415C_OUTPUT3 16 ++#define TEA6415C_OUTPUT4 17 ++#define TEA6415C_OUTPUT5 13 ++#define TEA6415C_OUTPUT6 15 ++ ++/* output pins */ ++#define TEA6415C_INPUT1 5 ++#define TEA6415C_INPUT2 8 ++#define TEA6415C_INPUT3 3 ++#define TEA6415C_INPUT4 20 ++#define TEA6415C_INPUT5 6 ++#define TEA6415C_INPUT6 10 ++#define TEA6415C_INPUT7 1 ++#define TEA6415C_INPUT8 11 ++ ++#endif +diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c +new file mode 100644 +index 0000000..3875721 +--- /dev/null ++++ b/drivers/media/i2c/tea6420.c +@@ -0,0 +1,169 @@ ++ /* ++ tea6420 - i2c-driver for the tea6420 by SGS Thomson ++ ++ Copyright (C) 1998-2003 Michael Hunold ++ Copyright (C) 2008 Hans Verkuil ++ ++ The tea6420 is a bus controlled audio-matrix with 5 stereo inputs, ++ 4 stereo outputs and gain control for each output. ++ It is cascadable, i.e. it can be found at the addresses 0x98 ++ and 0x9a on the i2c-bus. ++ ++ For detailed informations download the specifications directly ++ from SGS Thomson at http://www.st.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 ++#include ++#include ++#include ++#include ++#include ++#include "tea6420.h" ++ ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_DESCRIPTION("tea6420 driver"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* make a connection between the input 'i' and the output 'o' ++ with gain 'g' (note: i = 6 means 'mute') */ ++static int tea6420_s_routing(struct v4l2_subdev *sd, ++ u32 i, u32 o, u32 config) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int g = (o >> 4) & 0xf; ++ u8 byte; ++ int ret; ++ ++ o &= 0xf; ++ v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); ++ ++ /* check if the parameters are valid */ ++ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) ++ return -EINVAL; ++ ++ byte = ((o - 1) << 5); ++ byte |= (i - 1); ++ ++ /* to understand this, have a look at the tea6420-specs (p.5) */ ++ switch (g) { ++ case 0: ++ byte |= (3 << 3); ++ break; ++ case 2: ++ byte |= (2 << 3); ++ break; ++ case 4: ++ byte |= (1 << 3); ++ break; ++ case 6: ++ break; ++ } ++ ++ ret = i2c_smbus_write_byte(client, byte); ++ if (ret) { ++ v4l2_dbg(1, debug, sd, ++ "i2c_smbus_write_byte() failed, ret:%d\n", ret); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops tea6420_core_ops = { ++ .g_chip_ident = tea6420_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { ++ .s_routing = tea6420_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops tea6420_ops = { ++ .core = &tea6420_core_ops, ++ .audio = &tea6420_audio_ops, ++}; ++ ++static int tea6420_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ int err, i; ++ ++ /* let's see whether this adapter can support what we need */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); ++ if (sd == NULL) ++ return -ENOMEM; ++ v4l2_i2c_subdev_init(sd, client, &tea6420_ops); ++ ++ /* set initial values: set "mute"-input to all outputs at gain 0 */ ++ err = 0; ++ for (i = 1; i < 5; i++) ++ err += tea6420_s_routing(sd, 6, i, 0); ++ if (err) { ++ v4l_dbg(1, debug, client, "could not initialize tea6420\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int tea6420_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(sd); ++ return 0; ++} ++ ++static const struct i2c_device_id tea6420_id[] = { ++ { "tea6420", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tea6420_id); ++ ++static struct i2c_driver tea6420_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tea6420", ++ }, ++ .probe = tea6420_probe, ++ .remove = tea6420_remove, ++ .id_table = tea6420_id, ++}; ++ ++module_i2c_driver(tea6420_driver); +diff --git a/drivers/media/i2c/tea6420.h b/drivers/media/i2c/tea6420.h +new file mode 100644 +index 0000000..4aa3edb +--- /dev/null ++++ b/drivers/media/i2c/tea6420.h +@@ -0,0 +1,24 @@ ++#ifndef __INCLUDED_TEA6420__ ++#define __INCLUDED_TEA6420__ ++ ++/* input pins */ ++#define TEA6420_OUTPUT1 1 ++#define TEA6420_OUTPUT2 2 ++#define TEA6420_OUTPUT3 3 ++#define TEA6420_OUTPUT4 4 ++ ++/* output pins */ ++#define TEA6420_INPUT1 1 ++#define TEA6420_INPUT2 2 ++#define TEA6420_INPUT3 3 ++#define TEA6420_INPUT4 4 ++#define TEA6420_INPUT5 5 ++#define TEA6420_INPUT6 6 ++ ++/* gain on the output pins, ORed with the output pin */ ++#define TEA6420_GAIN0 0x00 ++#define TEA6420_GAIN2 0x20 ++#define TEA6420_GAIN4 0x40 ++#define TEA6420_GAIN6 0x60 ++ ++#endif +diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c +new file mode 100644 +index 0000000..c31cc04 +--- /dev/null ++++ b/drivers/media/i2c/ths7303.c +@@ -0,0 +1,214 @@ ++/* ++ * ths7303- THS7303 Video Amplifier driver ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define THS7303_CHANNEL_1 1 ++#define THS7303_CHANNEL_2 2 ++#define THS7303_CHANNEL_3 3 ++ ++enum ths7303_filter_mode { ++ THS7303_FILTER_MODE_480I_576I, ++ THS7303_FILTER_MODE_480P_576P, ++ THS7303_FILTER_MODE_720P_1080I, ++ THS7303_FILTER_MODE_1080P, ++ THS7303_FILTER_MODE_DISABLE ++}; ++ ++MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); ++MODULE_AUTHOR("Chaithrika U S"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++ ++/* following function is used to set ths7303 */ ++int ths7303_setval(struct v4l2_subdev *sd, enum ths7303_filter_mode mode) ++{ ++ u8 input_bias_chroma = 3; ++ u8 input_bias_luma = 3; ++ int disable = 0; ++ int err = 0; ++ u8 val = 0; ++ u8 temp; ++ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!client) ++ return -EINVAL; ++ ++ switch (mode) { ++ case THS7303_FILTER_MODE_1080P: ++ val = (3 << 6); ++ val |= (3 << 3); ++ break; ++ case THS7303_FILTER_MODE_720P_1080I: ++ val = (2 << 6); ++ val |= (2 << 3); ++ break; ++ case THS7303_FILTER_MODE_480P_576P: ++ val = (1 << 6); ++ val |= (1 << 3); ++ break; ++ case THS7303_FILTER_MODE_480I_576I: ++ break; ++ case THS7303_FILTER_MODE_DISABLE: ++ pr_info("mode disabled\n"); ++ /* disable all channels */ ++ disable = 1; ++ default: ++ /* disable all channels */ ++ disable = 1; ++ } ++ /* Setup channel 2 - Luma - Green */ ++ temp = val; ++ if (!disable) ++ val |= input_bias_luma; ++ err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_2, val); ++ if (err) ++ goto out; ++ ++ /* setup two chroma channels */ ++ if (!disable) ++ temp |= input_bias_chroma; ++ ++ err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_1, temp); ++ if (err) ++ goto out; ++ ++ err = i2c_smbus_write_byte_data(client, THS7303_CHANNEL_3, temp); ++ if (err) ++ goto out; ++ return err; ++out: ++ pr_info("write byte data failed\n"); ++ return err; ++} ++ ++static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ if (norm & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) ++ return ths7303_setval(sd, THS7303_FILTER_MODE_480I_576I); ++ else ++ return ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); ++} ++ ++/* for setting filter for HD output */ ++static int ths7303_s_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ u32 height = dv_timings->bt.height; ++ int interlaced = dv_timings->bt.interlaced; ++ int res = 0; ++ ++ if (height == 1080 && !interlaced) ++ res = ths7303_setval(sd, THS7303_FILTER_MODE_1080P); ++ else if ((height == 720 && !interlaced) || ++ (height == 1080 && interlaced)) ++ res = ths7303_setval(sd, THS7303_FILTER_MODE_720P_1080I); ++ else if ((height == 480 || height == 576) && !interlaced) ++ res = ths7303_setval(sd, THS7303_FILTER_MODE_480P_576P); ++ else ++ /* disable all channels */ ++ res = ths7303_setval(sd, THS7303_FILTER_MODE_DISABLE); ++ ++ return res; ++} ++ ++static int ths7303_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); ++} ++ ++static const struct v4l2_subdev_video_ops ths7303_video_ops = { ++ .s_std_output = ths7303_s_std_output, ++ .s_dv_timings = ths7303_s_dv_timings, ++}; ++ ++static const struct v4l2_subdev_core_ops ths7303_core_ops = { ++ .g_chip_ident = ths7303_g_chip_ident, ++}; ++ ++static const struct v4l2_subdev_ops ths7303_ops = { ++ .core = &ths7303_core_ops, ++ .video = &ths7303_video_ops, ++}; ++ ++static int ths7303_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ v4l2_std_id std_id = V4L2_STD_NTSC; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -ENODEV; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); ++ if (sd == NULL) ++ return -ENOMEM; ++ ++ v4l2_i2c_subdev_init(sd, client, &ths7303_ops); ++ ++ return ths7303_s_std_output(sd, std_id); ++} ++ ++static int ths7303_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(sd); ++ ++ return 0; ++} ++ ++static const struct i2c_device_id ths7303_id[] = { ++ {"ths7303", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, ths7303_id); ++ ++static struct i2c_driver ths7303_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "ths7303", ++ }, ++ .probe = ths7303_probe, ++ .remove = ths7303_remove, ++ .id_table = ths7303_id, ++}; ++ ++module_i2c_driver(ths7303_driver); +diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c +new file mode 100644 +index 0000000..809a75a +--- /dev/null ++++ b/drivers/media/i2c/tlv320aic23b.c +@@ -0,0 +1,230 @@ ++/* ++ * tlv320aic23b - driver version 0.0.1 ++ * ++ * Copyright (C) 2006 Scott Alfter ++ * ++ * Based on wm8775 driver ++ * ++ * Copyright (C) 2004 Ulf Eklund ++ * Copyright (C) 2005 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("tlv320aic23b driver"); ++MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct tlv320aic23b_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++}; ++ ++static inline struct tlv320aic23b_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct tlv320aic23b_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct tlv320aic23b_state, hdl)->sd; ++} ++ ++static int tlv320aic23b_write(struct v4l2_subdev *sd, int reg, u16 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int i; ++ ++ if ((reg < 0 || reg > 9) && (reg != 15)) { ++ v4l2_err(sd, "Invalid register R%d\n", reg); ++ return -1; ++ } ++ ++ for (i = 0; i < 3; i++) ++ if (i2c_smbus_write_byte_data(client, ++ (reg << 1) | (val >> 8), val & 0xff) == 0) ++ return 0; ++ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); ++ return -1; ++} ++ ++static int tlv320aic23b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ switch (freq) { ++ case 32000: /* set sample rate to 32 kHz */ ++ tlv320aic23b_write(sd, 8, 0x018); ++ break; ++ case 44100: /* set sample rate to 44.1 kHz */ ++ tlv320aic23b_write(sd, 8, 0x022); ++ break; ++ case 48000: /* set sample rate to 48 kHz */ ++ tlv320aic23b_write(sd, 8, 0x000); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int tlv320aic23b_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ tlv320aic23b_write(sd, 0, 0x180); /* mute both channels */ ++ /* set gain on both channels to +3.0 dB */ ++ if (!ctrl->val) ++ tlv320aic23b_write(sd, 0, 0x119); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int tlv320aic23b_log_status(struct v4l2_subdev *sd) ++{ ++ struct tlv320aic23b_state *state = to_state(sd); ++ ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops tlv320aic23b_ctrl_ops = { ++ .s_ctrl = tlv320aic23b_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { ++ .log_status = tlv320aic23b_log_status, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static const struct v4l2_subdev_audio_ops tlv320aic23b_audio_ops = { ++ .s_clock_freq = tlv320aic23b_s_clock_freq, ++}; ++ ++static const struct v4l2_subdev_ops tlv320aic23b_ops = { ++ .core = &tlv320aic23b_core_ops, ++ .audio = &tlv320aic23b_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* i2c implementation */ ++ ++/* ++ * Generic i2c probe ++ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' ++ */ ++ ++static int tlv320aic23b_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct tlv320aic23b_state *state; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct tlv320aic23b_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &tlv320aic23b_ops); ++ ++ /* Initialize tlv320aic23b */ ++ ++ /* RESET */ ++ tlv320aic23b_write(sd, 15, 0x000); ++ /* turn off DAC & mic input */ ++ tlv320aic23b_write(sd, 6, 0x00A); ++ /* left-justified, 24-bit, master mode */ ++ tlv320aic23b_write(sd, 7, 0x049); ++ /* set gain on both channels to +3.0 dB */ ++ tlv320aic23b_write(sd, 0, 0x119); ++ /* set sample rate to 48 kHz */ ++ tlv320aic23b_write(sd, 8, 0x000); ++ /* activate digital interface */ ++ tlv320aic23b_write(sd, 9, 0x001); ++ ++ v4l2_ctrl_handler_init(&state->hdl, 1); ++ v4l2_ctrl_new_std(&state->hdl, &tlv320aic23b_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&state->hdl); ++ return 0; ++} ++ ++static int tlv320aic23b_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct tlv320aic23b_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id tlv320aic23b_id[] = { ++ { "tlv320aic23b", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); ++ ++static struct i2c_driver tlv320aic23b_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tlv320aic23b", ++ }, ++ .probe = tlv320aic23b_probe, ++ .remove = tlv320aic23b_remove, ++ .id_table = tlv320aic23b_id, ++}; ++ ++module_i2c_driver(tlv320aic23b_driver); +diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c +new file mode 100644 +index 0000000..3b24d3f +--- /dev/null ++++ b/drivers/media/i2c/tvaudio.c +@@ -0,0 +1,2131 @@ ++/* ++ * Driver for simple i2c audio chips. ++ * ++ * Copyright (c) 2000 Gerd Knorr ++ * based on code by: ++ * Eric Sandeen (eric_sandeen@bigfoot.com) ++ * Steve VanDeBogart (vandebo@uclink.berkeley.edu) ++ * Greg Alexander (galexand@acm.org) ++ * ++ * For the TDA9875 part: ++ * Copyright (c) 2000 Guillaume Delvit based on Gerd Knorr source ++ * and Eric Sandeen ++ * ++ * Copyright(c) 2005-2008 Mauro Carvalho Chehab ++ * - Some cleanups, code fixes, etc ++ * - Convert it to V4L2 API ++ * ++ * This code is placed under the terms of the GNU General Public License ++ * ++ * OPTIONS: ++ * debug - set to 1 if you'd like to see debug messages ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++/* ---------------------------------------------------------------------- */ ++/* insmod args */ ++ ++static int debug; /* insmod parameter */ ++module_param(debug, int, 0644); ++ ++MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); ++MODULE_AUTHOR("Eric Sandeen, Steve VanDeBogart, Greg Alexander, Gerd Knorr"); ++MODULE_LICENSE("GPL"); ++ ++#define UNSET (-1U) ++ ++/* ---------------------------------------------------------------------- */ ++/* our structs */ ++ ++#define MAXREGS 256 ++ ++struct CHIPSTATE; ++typedef int (*getvalue)(int); ++typedef int (*checkit)(struct CHIPSTATE*); ++typedef int (*initialize)(struct CHIPSTATE*); ++typedef int (*getrxsubchans)(struct CHIPSTATE *); ++typedef void (*setaudmode)(struct CHIPSTATE*, int mode); ++ ++/* i2c command */ ++typedef struct AUDIOCMD { ++ int count; /* # of bytes to send */ ++ unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ ++} audiocmd; ++ ++/* chip description */ ++struct CHIPDESC { ++ char *name; /* chip name */ ++ int addr_lo, addr_hi; /* i2c address range */ ++ int registers; /* # of registers */ ++ ++ int *insmodopt; ++ checkit checkit; ++ initialize initialize; ++ int flags; ++#define CHIP_HAS_VOLUME 1 ++#define CHIP_HAS_BASSTREBLE 2 ++#define CHIP_HAS_INPUTSEL 4 ++#define CHIP_NEED_CHECKMODE 8 ++ ++ /* various i2c command sequences */ ++ audiocmd init; ++ ++ /* which register has which value */ ++ int leftreg,rightreg,treblereg,bassreg; ++ ++ /* initialize with (defaults to 65535/65535/32768/32768 */ ++ int leftinit,rightinit,trebleinit,bassinit; ++ ++ /* functions to convert the values (v4l -> chip) */ ++ getvalue volfunc,treblefunc,bassfunc; ++ ++ /* get/set mode */ ++ getrxsubchans getrxsubchans; ++ setaudmode setaudmode; ++ ++ /* input switch register + values for v4l inputs */ ++ int inputreg; ++ int inputmap[4]; ++ int inputmute; ++ int inputmask; ++}; ++ ++/* current state of the chip */ ++struct CHIPSTATE { ++ struct v4l2_subdev sd; ++ ++ /* chip-specific description - should point to ++ an entry at CHIPDESC table */ ++ struct CHIPDESC *desc; ++ ++ /* shadow register set */ ++ audiocmd shadow; ++ ++ /* current settings */ ++ __u16 left, right, treble, bass, muted; ++ int prevmode; ++ int radio; ++ int input; ++ ++ /* thread */ ++ struct task_struct *thread; ++ struct timer_list wt; ++ int audmode; ++}; ++ ++static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct CHIPSTATE, sd); ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* i2c I/O functions */ ++ ++static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char buffer[2]; ++ ++ if (subaddr < 0) { ++ v4l2_dbg(1, debug, sd, "chip_write: 0x%x\n", val); ++ chip->shadow.bytes[1] = val; ++ buffer[0] = val; ++ if (1 != i2c_master_send(c, buffer, 1)) { ++ v4l2_warn(sd, "I/O error (write 0x%x)\n", val); ++ return -1; ++ } ++ } else { ++ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { ++ v4l2_info(sd, ++ "Tried to access a non-existent register: %d\n", ++ subaddr); ++ return -EINVAL; ++ } ++ ++ v4l2_dbg(1, debug, sd, "chip_write: reg%d=0x%x\n", ++ subaddr, val); ++ chip->shadow.bytes[subaddr+1] = val; ++ buffer[0] = subaddr; ++ buffer[1] = val; ++ if (2 != i2c_master_send(c, buffer, 2)) { ++ v4l2_warn(sd, "I/O error (write reg%d=0x%x)\n", ++ subaddr, val); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int chip_write_masked(struct CHIPSTATE *chip, ++ int subaddr, int val, int mask) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ ++ if (mask != 0) { ++ if (subaddr < 0) { ++ val = (chip->shadow.bytes[1] & ~mask) | (val & mask); ++ } else { ++ if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { ++ v4l2_info(sd, ++ "Tried to access a non-existent register: %d\n", ++ subaddr); ++ return -EINVAL; ++ } ++ ++ val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); ++ } ++ } ++ return chip_write(chip, subaddr, val); ++} ++ ++static int chip_read(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char buffer; ++ ++ if (1 != i2c_master_recv(c, &buffer, 1)) { ++ v4l2_warn(sd, "I/O error (read)\n"); ++ return -1; ++ } ++ v4l2_dbg(1, debug, sd, "chip_read: 0x%x\n", buffer); ++ return buffer; ++} ++ ++static int chip_read2(struct CHIPSTATE *chip, int subaddr) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char write[1]; ++ unsigned char read[1]; ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = c->addr, ++ .len = 1, ++ .buf = write ++ }, ++ { ++ .addr = c->addr, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = read ++ } ++ }; ++ ++ write[0] = subaddr; ++ ++ if (2 != i2c_transfer(c->adapter, msgs, 2)) { ++ v4l2_warn(sd, "I/O error (read2)\n"); ++ return -1; ++ } ++ v4l2_dbg(1, debug, sd, "chip_read2: reg%d=0x%x\n", ++ subaddr, read[0]); ++ return read[0]; ++} ++ ++static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ int i; ++ ++ if (0 == cmd->count) ++ return 0; ++ ++ if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { ++ v4l2_info(sd, ++ "Tried to access a non-existent register range: %d to %d\n", ++ cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); ++ return -EINVAL; ++ } ++ ++ /* FIXME: it seems that the shadow bytes are wrong bellow !*/ ++ ++ /* update our shadow register set; print bytes if (debug > 0) */ ++ v4l2_dbg(1, debug, sd, "chip_cmd(%s): reg=%d, data:", ++ name, cmd->bytes[0]); ++ for (i = 1; i < cmd->count; i++) { ++ if (debug) ++ printk(KERN_CONT " 0x%x", cmd->bytes[i]); ++ chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; ++ } ++ if (debug) ++ printk(KERN_CONT "\n"); ++ ++ /* send data to the chip */ ++ if (cmd->count != i2c_master_send(c, cmd->bytes, cmd->count)) { ++ v4l2_warn(sd, "I/O error (%s)\n", name); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* kernel thread for doing i2c stuff asyncronly ++ * right now it is used only to check the audio mode (mono/stereo/whatever) ++ * some time after switching to another TV channel, then turn on stereo ++ * if available, ... ++ */ ++ ++static void chip_thread_wake(unsigned long data) ++{ ++ struct CHIPSTATE *chip = (struct CHIPSTATE*)data; ++ wake_up_process(chip->thread); ++} ++ ++static int chip_thread(void *data) ++{ ++ struct CHIPSTATE *chip = data; ++ struct CHIPDESC *desc = chip->desc; ++ struct v4l2_subdev *sd = &chip->sd; ++ int mode, selected; ++ ++ v4l2_dbg(1, debug, sd, "thread started\n"); ++ set_freezable(); ++ for (;;) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (!kthread_should_stop()) ++ schedule(); ++ set_current_state(TASK_RUNNING); ++ try_to_freeze(); ++ if (kthread_should_stop()) ++ break; ++ v4l2_dbg(1, debug, sd, "thread wakeup\n"); ++ ++ /* don't do anything for radio */ ++ if (chip->radio) ++ continue; ++ ++ /* have a look what's going on */ ++ mode = desc->getrxsubchans(chip); ++ if (mode == chip->prevmode) ++ continue; ++ ++ /* chip detected a new audio mode - set it */ ++ v4l2_dbg(1, debug, sd, "thread checkmode\n"); ++ ++ chip->prevmode = mode; ++ ++ selected = V4L2_TUNER_MODE_MONO; ++ switch (chip->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ if (mode & V4L2_TUNER_SUB_LANG1) ++ selected = V4L2_TUNER_MODE_LANG1; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1: ++ if (mode & V4L2_TUNER_SUB_LANG1) ++ selected = V4L2_TUNER_MODE_LANG1; ++ else if (mode & V4L2_TUNER_SUB_STEREO) ++ selected = V4L2_TUNER_MODE_STEREO; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ if (mode & V4L2_TUNER_SUB_LANG2) ++ selected = V4L2_TUNER_MODE_LANG2; ++ else if (mode & V4L2_TUNER_SUB_STEREO) ++ selected = V4L2_TUNER_MODE_STEREO; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ if (mode & V4L2_TUNER_SUB_LANG2) ++ selected = V4L2_TUNER_MODE_LANG1_LANG2; ++ else if (mode & V4L2_TUNER_SUB_STEREO) ++ selected = V4L2_TUNER_MODE_STEREO; ++ } ++ desc->setaudmode(chip, selected); ++ ++ /* schedule next check */ ++ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); ++ } ++ ++ v4l2_dbg(1, debug, sd, "thread exiting\n"); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for tda9840 */ ++ ++#define TDA9840_SW 0x00 ++#define TDA9840_LVADJ 0x02 ++#define TDA9840_STADJ 0x03 ++#define TDA9840_TEST 0x04 ++ ++#define TDA9840_MONO 0x10 ++#define TDA9840_STEREO 0x2a ++#define TDA9840_DUALA 0x12 ++#define TDA9840_DUALB 0x1e ++#define TDA9840_DUALAB 0x1a ++#define TDA9840_DUALBA 0x16 ++#define TDA9840_EXTERNAL 0x7a ++ ++#define TDA9840_DS_DUAL 0x20 /* Dual sound identified */ ++#define TDA9840_ST_STEREO 0x40 /* Stereo sound identified */ ++#define TDA9840_PONRES 0x80 /* Power-on reset detected if = 1 */ ++ ++#define TDA9840_TEST_INT1SN 0x1 /* Integration time 0.5s when set */ ++#define TDA9840_TEST_INTFU 0x02 /* Disables integrator function */ ++ ++static int tda9840_getrxsubchans(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int val, mode; ++ ++ val = chip_read(chip); ++ mode = V4L2_TUNER_SUB_MONO; ++ if (val & TDA9840_DS_DUAL) ++ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ if (val & TDA9840_ST_STEREO) ++ mode = V4L2_TUNER_SUB_STEREO; ++ ++ v4l2_dbg(1, debug, sd, ++ "tda9840_getrxsubchans(): raw chip read: %d, return: %d\n", ++ val, mode); ++ return mode; ++} ++ ++static void tda9840_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ int update = 1; ++ int t = chip->shadow.bytes[TDA9840_SW + 1] & ~0x7e; ++ ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ t |= TDA9840_MONO; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ t |= TDA9840_STEREO; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ t |= TDA9840_DUALA; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ t |= TDA9840_DUALB; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ t |= TDA9840_DUALAB; ++ break; ++ default: ++ update = 0; ++ } ++ ++ if (update) ++ chip_write(chip, TDA9840_SW, t); ++} ++ ++static int tda9840_checkit(struct CHIPSTATE *chip) ++{ ++ int rc; ++ rc = chip_read(chip); ++ /* lower 5 bits should be 0 */ ++ return ((rc & 0x1f) == 0) ? 1 : 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for tda985x */ ++ ++/* subaddresses for TDA9855 */ ++#define TDA9855_VR 0x00 /* Volume, right */ ++#define TDA9855_VL 0x01 /* Volume, left */ ++#define TDA9855_BA 0x02 /* Bass */ ++#define TDA9855_TR 0x03 /* Treble */ ++#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ ++ ++/* subaddresses for TDA9850 */ ++#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ ++ ++/* subaddesses for both chips */ ++#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ ++#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ ++#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ ++#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ ++#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ ++#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ ++ ++/* Masks for bits in TDA9855 subaddresses */ ++/* 0x00 - VR in TDA9855 */ ++/* 0x01 - VL in TDA9855 */ ++/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) ++ * in 1dB steps - mute is 0x27 */ ++ ++ ++/* 0x02 - BA in TDA9855 */ ++/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) ++ * in .5dB steps - 0 is 0x0E */ ++ ++ ++/* 0x03 - TR in TDA9855 */ ++/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) ++ * in 3dB steps - 0 is 0x7 */ ++ ++/* Masks for bits in both chips' subaddresses */ ++/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ ++/* Unique to TDA9855: */ ++/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) ++ * in 3dB steps - mute is 0x0 */ ++ ++/* Unique to TDA9850: */ ++/* lower 4 bits control stereo noise threshold, over which stereo turns off ++ * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ ++ ++ ++/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ ++/* Unique to TDA9855: */ ++#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ ++#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ ++#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ ++#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ ++ /* Bits 0 to 3 select various combinations ++ * of line in and line out, only the ++ * interesting ones are defined */ ++#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ ++#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ ++ ++/* Unique to TDA9850: */ ++/* lower 4 bits contol SAP noise threshold, over which SAP turns off ++ * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ ++ ++ ++/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ ++/* Common to TDA9855 and TDA9850: */ ++#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ ++#define TDA985x_MONOSAP 2<<6 /* Selects Mono on left, SAP on right */ ++#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ ++#define TDA985x_MONO 0 /* Forces Mono output */ ++#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ ++ ++/* Unique to TDA9855: */ ++#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ ++#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ ++#define TDA9855_LINEAR 0 /* Linear Stereo */ ++#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ ++#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ ++#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ ++#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ ++ ++/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ ++/* Common to both TDA9855 and TDA9850: */ ++/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) ++ * in .5dB steps - 0dB is 0x7 */ ++ ++/* 0x08, 0x09 - A1 and A2 (read/write) */ ++/* Common to both TDA9855 and TDA9850: */ ++/* lower 5 bites are wideband and spectral expander alignment ++ * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ ++#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ ++#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ ++#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ ++ ++/* 0x0a - A3 */ ++/* Common to both TDA9855 and TDA9850: */ ++/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), ++ * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ ++#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ ++ ++static int tda9855_volume(int val) { return val/0x2e8+0x27; } ++static int tda9855_bass(int val) { return val/0xccc+0x06; } ++static int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } ++ ++static int tda985x_getrxsubchans(struct CHIPSTATE *chip) ++{ ++ int mode, val; ++ ++ /* Add mono mode regardless of SAP and stereo */ ++ /* Allows forced mono */ ++ mode = V4L2_TUNER_SUB_MONO; ++ val = chip_read(chip); ++ if (val & TDA985x_STP) ++ mode = V4L2_TUNER_SUB_STEREO; ++ if (val & TDA985x_SAPP) ++ mode |= V4L2_TUNER_SUB_SAP; ++ return mode; ++} ++ ++static void tda985x_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ int update = 1; ++ int c6 = chip->shadow.bytes[TDA985x_C6+1] & 0x3f; ++ ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ c6 |= TDA985x_MONO; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1: ++ c6 |= TDA985x_STEREO; ++ break; ++ case V4L2_TUNER_MODE_SAP: ++ c6 |= TDA985x_SAP; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ c6 |= TDA985x_MONOSAP; ++ break; ++ default: ++ update = 0; ++ } ++ if (update) ++ chip_write(chip,TDA985x_C6,c6); ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for tda9873h */ ++ ++/* Subaddresses for TDA9873H */ ++ ++#define TDA9873_SW 0x00 /* Switching */ ++#define TDA9873_AD 0x01 /* Adjust */ ++#define TDA9873_PT 0x02 /* Port */ ++ ++/* Subaddress 0x00: Switching Data ++ * B7..B0: ++ * ++ * B1, B0: Input source selection ++ * 0, 0 internal ++ * 1, 0 external stereo ++ * 0, 1 external mono ++ */ ++#define TDA9873_INP_MASK 3 ++#define TDA9873_INTERNAL 0 ++#define TDA9873_EXT_STEREO 2 ++#define TDA9873_EXT_MONO 1 ++ ++/* B3, B2: output signal select ++ * B4 : transmission mode ++ * 0, 0, 1 Mono ++ * 1, 0, 0 Stereo ++ * 1, 1, 1 Stereo (reversed channel) ++ * 0, 0, 0 Dual AB ++ * 0, 0, 1 Dual AA ++ * 0, 1, 0 Dual BB ++ * 0, 1, 1 Dual BA ++ */ ++ ++#define TDA9873_TR_MASK (7 << 2) ++#define TDA9873_TR_MONO 4 ++#define TDA9873_TR_STEREO 1 << 4 ++#define TDA9873_TR_REVERSE ((1 << 3) | (1 << 2)) ++#define TDA9873_TR_DUALA 1 << 2 ++#define TDA9873_TR_DUALB 1 << 3 ++#define TDA9873_TR_DUALAB 0 ++ ++/* output level controls ++ * B5: output level switch (0 = reduced gain, 1 = normal gain) ++ * B6: mute (1 = muted) ++ * B7: auto-mute (1 = auto-mute enabled) ++ */ ++ ++#define TDA9873_GAIN_NORMAL 1 << 5 ++#define TDA9873_MUTE 1 << 6 ++#define TDA9873_AUTOMUTE 1 << 7 ++ ++/* Subaddress 0x01: Adjust/standard */ ++ ++/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) ++ * Recommended value is +0 dB ++ */ ++ ++#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ ++ ++/* Bits C6..C4 control FM stantard ++ * C6, C5, C4 ++ * 0, 0, 0 B/G (PAL FM) ++ * 0, 0, 1 M ++ * 0, 1, 0 D/K(1) ++ * 0, 1, 1 D/K(2) ++ * 1, 0, 0 D/K(3) ++ * 1, 0, 1 I ++ */ ++#define TDA9873_BG 0 ++#define TDA9873_M 1 ++#define TDA9873_DK1 2 ++#define TDA9873_DK2 3 ++#define TDA9873_DK3 4 ++#define TDA9873_I 5 ++ ++/* C7 controls identification response time (1=fast/0=normal) ++ */ ++#define TDA9873_IDR_NORM 0 ++#define TDA9873_IDR_FAST 1 << 7 ++ ++ ++/* Subaddress 0x02: Port data */ ++ ++/* E1, E0 free programmable ports P1/P2 ++ 0, 0 both ports low ++ 0, 1 P1 high ++ 1, 0 P2 high ++ 1, 1 both ports high ++*/ ++ ++#define TDA9873_PORTS 3 ++ ++/* E2: test port */ ++#define TDA9873_TST_PORT 1 << 2 ++ ++/* E5..E3 control mono output channel (together with transmission mode bit B4) ++ * ++ * E5 E4 E3 B4 OUTM ++ * 0 0 0 0 mono ++ * 0 0 1 0 DUAL B ++ * 0 1 0 1 mono (from stereo decoder) ++ */ ++#define TDA9873_MOUT_MONO 0 ++#define TDA9873_MOUT_FMONO 0 ++#define TDA9873_MOUT_DUALA 0 ++#define TDA9873_MOUT_DUALB 1 << 3 ++#define TDA9873_MOUT_ST 1 << 4 ++#define TDA9873_MOUT_EXTM ((1 << 4) | (1 << 3)) ++#define TDA9873_MOUT_EXTL 1 << 5 ++#define TDA9873_MOUT_EXTR ((1 << 5) | (1 << 3)) ++#define TDA9873_MOUT_EXTLR ((1 << 5) | (1 << 4)) ++#define TDA9873_MOUT_MUTE ((1 << 5) | (1 << 4) | (1 << 3)) ++ ++/* Status bits: (chip read) */ ++#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ ++#define TDA9873_STEREO 2 /* Stereo sound is identified */ ++#define TDA9873_DUAL 4 /* Dual sound is identified */ ++ ++static int tda9873_getrxsubchans(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int val,mode; ++ ++ val = chip_read(chip); ++ mode = V4L2_TUNER_SUB_MONO; ++ if (val & TDA9873_STEREO) ++ mode = V4L2_TUNER_SUB_STEREO; ++ if (val & TDA9873_DUAL) ++ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ v4l2_dbg(1, debug, sd, ++ "tda9873_getrxsubchans(): raw chip read: %d, return: %d\n", ++ val, mode); ++ return mode; ++} ++ ++static void tda9873_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int sw_data = chip->shadow.bytes[TDA9873_SW+1] & ~ TDA9873_TR_MASK; ++ /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ ++ ++ if ((sw_data & TDA9873_INP_MASK) != TDA9873_INTERNAL) { ++ v4l2_dbg(1, debug, sd, ++ "tda9873_setaudmode(): external input\n"); ++ return; ++ } ++ ++ v4l2_dbg(1, debug, sd, ++ "tda9873_setaudmode(): chip->shadow.bytes[%d] = %d\n", ++ TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); ++ v4l2_dbg(1, debug, sd, "tda9873_setaudmode(): sw_data = %d\n", ++ sw_data); ++ ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ sw_data |= TDA9873_TR_MONO; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ sw_data |= TDA9873_TR_STEREO; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ sw_data |= TDA9873_TR_DUALA; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ sw_data |= TDA9873_TR_DUALB; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ sw_data |= TDA9873_TR_DUALAB; ++ break; ++ default: ++ return; ++ } ++ ++ chip_write(chip, TDA9873_SW, sw_data); ++ v4l2_dbg(1, debug, sd, ++ "tda9873_setaudmode(): req. mode %d; chip_write: %d\n", ++ mode, sw_data); ++} ++ ++static int tda9873_checkit(struct CHIPSTATE *chip) ++{ ++ int rc; ++ ++ if (-1 == (rc = chip_read2(chip,254))) ++ return 0; ++ return (rc & ~0x1f) == 0x80; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip description - defines+functions for tda9874h and tda9874a */ ++/* Dariusz Kowalewski */ ++ ++/* Subaddresses for TDA9874H and TDA9874A (slave rx) */ ++#define TDA9874A_AGCGR 0x00 /* AGC gain */ ++#define TDA9874A_GCONR 0x01 /* general config */ ++#define TDA9874A_MSR 0x02 /* monitor select */ ++#define TDA9874A_C1FRA 0x03 /* carrier 1 freq. */ ++#define TDA9874A_C1FRB 0x04 /* carrier 1 freq. */ ++#define TDA9874A_C1FRC 0x05 /* carrier 1 freq. */ ++#define TDA9874A_C2FRA 0x06 /* carrier 2 freq. */ ++#define TDA9874A_C2FRB 0x07 /* carrier 2 freq. */ ++#define TDA9874A_C2FRC 0x08 /* carrier 2 freq. */ ++#define TDA9874A_DCR 0x09 /* demodulator config */ ++#define TDA9874A_FMER 0x0a /* FM de-emphasis */ ++#define TDA9874A_FMMR 0x0b /* FM dematrix */ ++#define TDA9874A_C1OLAR 0x0c /* ch.1 output level adj. */ ++#define TDA9874A_C2OLAR 0x0d /* ch.2 output level adj. */ ++#define TDA9874A_NCONR 0x0e /* NICAM config */ ++#define TDA9874A_NOLAR 0x0f /* NICAM output level adj. */ ++#define TDA9874A_NLELR 0x10 /* NICAM lower error limit */ ++#define TDA9874A_NUELR 0x11 /* NICAM upper error limit */ ++#define TDA9874A_AMCONR 0x12 /* audio mute control */ ++#define TDA9874A_SDACOSR 0x13 /* stereo DAC output select */ ++#define TDA9874A_AOSR 0x14 /* analog output select */ ++#define TDA9874A_DAICONR 0x15 /* digital audio interface config */ ++#define TDA9874A_I2SOSR 0x16 /* I2S-bus output select */ ++#define TDA9874A_I2SOLAR 0x17 /* I2S-bus output level adj. */ ++#define TDA9874A_MDACOSR 0x18 /* mono DAC output select (tda9874a) */ ++#define TDA9874A_ESP 0xFF /* easy standard progr. (tda9874a) */ ++ ++/* Subaddresses for TDA9874H and TDA9874A (slave tx) */ ++#define TDA9874A_DSR 0x00 /* device status */ ++#define TDA9874A_NSR 0x01 /* NICAM status */ ++#define TDA9874A_NECR 0x02 /* NICAM error count */ ++#define TDA9874A_DR1 0x03 /* add. data LSB */ ++#define TDA9874A_DR2 0x04 /* add. data MSB */ ++#define TDA9874A_LLRA 0x05 /* monitor level read-out LSB */ ++#define TDA9874A_LLRB 0x06 /* monitor level read-out MSB */ ++#define TDA9874A_SIFLR 0x07 /* SIF level */ ++#define TDA9874A_TR2 252 /* test reg. 2 */ ++#define TDA9874A_TR1 253 /* test reg. 1 */ ++#define TDA9874A_DIC 254 /* device id. code */ ++#define TDA9874A_SIC 255 /* software id. code */ ++ ++ ++static int tda9874a_mode = 1; /* 0: A2, 1: NICAM */ ++static int tda9874a_GCONR = 0xc0; /* default config. input pin: SIFSEL=0 */ ++static int tda9874a_NCONR = 0x01; /* default NICAM config.: AMSEL=0,AMUTE=1 */ ++static int tda9874a_ESP = 0x07; /* default standard: NICAM D/K */ ++static int tda9874a_dic = -1; /* device id. code */ ++ ++/* insmod options for tda9874a */ ++static unsigned int tda9874a_SIF = UNSET; ++static unsigned int tda9874a_AMSEL = UNSET; ++static unsigned int tda9874a_STD = UNSET; ++module_param(tda9874a_SIF, int, 0444); ++module_param(tda9874a_AMSEL, int, 0444); ++module_param(tda9874a_STD, int, 0444); ++ ++/* ++ * initialization table for tda9874 decoder: ++ * - carrier 1 freq. registers (3 bytes) ++ * - carrier 2 freq. registers (3 bytes) ++ * - demudulator config register ++ * - FM de-emphasis register (slow identification mode) ++ * Note: frequency registers must be written in single i2c transfer. ++ */ ++static struct tda9874a_MODES { ++ char *name; ++ audiocmd cmd; ++} tda9874a_modelist[9] = { ++ { "A2, B/G", /* default */ ++ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, ++ { "A2, M (Korea)", ++ { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, ++ { "A2, D/K (1)", ++ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x82,0x60,0x00, 0x00,0x00 }} }, ++ { "A2, D/K (2)", ++ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x8C,0x75,0x55, 0x00,0x00 }} }, ++ { "A2, D/K (3)", ++ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x77,0xA0,0x00, 0x00,0x00 }} }, ++ { "NICAM, I", ++ { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, ++ { "NICAM, B/G", ++ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, ++ { "NICAM, D/K", ++ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, ++ { "NICAM, L", ++ { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } ++}; ++ ++static int tda9874a_setup(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ ++ chip_write(chip, TDA9874A_AGCGR, 0x00); /* 0 dB */ ++ chip_write(chip, TDA9874A_GCONR, tda9874a_GCONR); ++ chip_write(chip, TDA9874A_MSR, (tda9874a_mode) ? 0x03:0x02); ++ if(tda9874a_dic == 0x11) { ++ chip_write(chip, TDA9874A_FMMR, 0x80); ++ } else { /* dic == 0x07 */ ++ chip_cmd(chip,"tda9874_modelist",&tda9874a_modelist[tda9874a_STD].cmd); ++ chip_write(chip, TDA9874A_FMMR, 0x00); ++ } ++ chip_write(chip, TDA9874A_C1OLAR, 0x00); /* 0 dB */ ++ chip_write(chip, TDA9874A_C2OLAR, 0x00); /* 0 dB */ ++ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); ++ chip_write(chip, TDA9874A_NOLAR, 0x00); /* 0 dB */ ++ /* Note: If signal quality is poor you may want to change NICAM */ ++ /* error limit registers (NLELR and NUELR) to some greater values. */ ++ /* Then the sound would remain stereo, but won't be so clear. */ ++ chip_write(chip, TDA9874A_NLELR, 0x14); /* default */ ++ chip_write(chip, TDA9874A_NUELR, 0x50); /* default */ ++ ++ if(tda9874a_dic == 0x11) { ++ chip_write(chip, TDA9874A_AMCONR, 0xf9); ++ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); ++ chip_write(chip, TDA9874A_AOSR, 0x80); ++ chip_write(chip, TDA9874A_MDACOSR, (tda9874a_mode) ? 0x82:0x80); ++ chip_write(chip, TDA9874A_ESP, tda9874a_ESP); ++ } else { /* dic == 0x07 */ ++ chip_write(chip, TDA9874A_AMCONR, 0xfb); ++ chip_write(chip, TDA9874A_SDACOSR, (tda9874a_mode) ? 0x81:0x80); ++ chip_write(chip, TDA9874A_AOSR, 0x00); /* or 0x10 */ ++ } ++ v4l2_dbg(1, debug, sd, "tda9874a_setup(): %s [0x%02X].\n", ++ tda9874a_modelist[tda9874a_STD].name,tda9874a_STD); ++ return 1; ++} ++ ++static int tda9874a_getrxsubchans(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int dsr,nsr,mode; ++ int necr; /* just for debugging */ ++ ++ mode = V4L2_TUNER_SUB_MONO; ++ ++ if(-1 == (dsr = chip_read2(chip,TDA9874A_DSR))) ++ return mode; ++ if(-1 == (nsr = chip_read2(chip,TDA9874A_NSR))) ++ return mode; ++ if(-1 == (necr = chip_read2(chip,TDA9874A_NECR))) ++ return mode; ++ ++ /* need to store dsr/nsr somewhere */ ++ chip->shadow.bytes[MAXREGS-2] = dsr; ++ chip->shadow.bytes[MAXREGS-1] = nsr; ++ ++ if(tda9874a_mode) { ++ /* Note: DSR.RSSF and DSR.AMSTAT bits are also checked. ++ * If NICAM auto-muting is enabled, DSR.AMSTAT=1 indicates ++ * that sound has (temporarily) switched from NICAM to ++ * mono FM (or AM) on 1st sound carrier due to high NICAM bit ++ * error count. So in fact there is no stereo in this case :-( ++ * But changing the mode to V4L2_TUNER_MODE_MONO would switch ++ * external 4052 multiplexer in audio_hook(). ++ */ ++ if(nsr & 0x02) /* NSR.S/MB=1 */ ++ mode = V4L2_TUNER_SUB_STEREO; ++ if(nsr & 0x01) /* NSR.D/SB=1 */ ++ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ } else { ++ if(dsr & 0x02) /* DSR.IDSTE=1 */ ++ mode = V4L2_TUNER_SUB_STEREO; ++ if(dsr & 0x04) /* DSR.IDDUA=1 */ ++ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ } ++ ++ v4l2_dbg(1, debug, sd, ++ "tda9874a_getrxsubchans(): DSR=0x%X, NSR=0x%X, NECR=0x%X, return: %d.\n", ++ dsr, nsr, necr, mode); ++ return mode; ++} ++ ++static void tda9874a_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ ++ /* Disable/enable NICAM auto-muting (based on DSR.RSSF status bit). */ ++ /* If auto-muting is disabled, we can hear a signal of degrading quality. */ ++ if (tda9874a_mode) { ++ if(chip->shadow.bytes[MAXREGS-2] & 0x20) /* DSR.RSSF=1 */ ++ tda9874a_NCONR &= 0xfe; /* enable */ ++ else ++ tda9874a_NCONR |= 0x01; /* disable */ ++ chip_write(chip, TDA9874A_NCONR, tda9874a_NCONR); ++ } ++ ++ /* Note: TDA9874A supports automatic FM dematrixing (FMMR register) ++ * and has auto-select function for audio output (AOSR register). ++ * Old TDA9874H doesn't support these features. ++ * TDA9874A also has additional mono output pin (OUTM), which ++ * on same (all?) tv-cards is not used, anyway (as well as MONOIN). ++ */ ++ if(tda9874a_dic == 0x11) { ++ int aosr = 0x80; ++ int mdacosr = (tda9874a_mode) ? 0x82:0x80; ++ ++ switch(mode) { ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_STEREO: ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ aosr = 0x80; /* auto-select, dual A/A */ ++ mdacosr = (tda9874a_mode) ? 0x82:0x80; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ aosr = 0xa0; /* auto-select, dual B/B */ ++ mdacosr = (tda9874a_mode) ? 0x83:0x81; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ aosr = 0x00; /* always route L to L and R to R */ ++ mdacosr = (tda9874a_mode) ? 0x82:0x80; ++ break; ++ default: ++ return; ++ } ++ chip_write(chip, TDA9874A_AOSR, aosr); ++ chip_write(chip, TDA9874A_MDACOSR, mdacosr); ++ ++ v4l2_dbg(1, debug, sd, ++ "tda9874a_setaudmode(): req. mode %d; AOSR=0x%X, MDACOSR=0x%X.\n", ++ mode, aosr, mdacosr); ++ ++ } else { /* dic == 0x07 */ ++ int fmmr,aosr; ++ ++ switch(mode) { ++ case V4L2_TUNER_MODE_MONO: ++ fmmr = 0x00; /* mono */ ++ aosr = 0x10; /* A/A */ ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ if(tda9874a_mode) { ++ fmmr = 0x00; ++ aosr = 0x00; /* handled by NICAM auto-mute */ ++ } else { ++ fmmr = (tda9874a_ESP == 1) ? 0x05 : 0x04; /* stereo */ ++ aosr = 0x00; ++ } ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ fmmr = 0x02; /* dual */ ++ aosr = 0x10; /* dual A/A */ ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ fmmr = 0x02; /* dual */ ++ aosr = 0x20; /* dual B/B */ ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ fmmr = 0x02; /* dual */ ++ aosr = 0x00; /* dual A/B */ ++ break; ++ default: ++ return; ++ } ++ chip_write(chip, TDA9874A_FMMR, fmmr); ++ chip_write(chip, TDA9874A_AOSR, aosr); ++ ++ v4l2_dbg(1, debug, sd, ++ "tda9874a_setaudmode(): req. mode %d; FMMR=0x%X, AOSR=0x%X.\n", ++ mode, fmmr, aosr); ++ } ++} ++ ++static int tda9874a_checkit(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int dic,sic; /* device id. and software id. codes */ ++ ++ if(-1 == (dic = chip_read2(chip,TDA9874A_DIC))) ++ return 0; ++ if(-1 == (sic = chip_read2(chip,TDA9874A_SIC))) ++ return 0; ++ ++ v4l2_dbg(1, debug, sd, "tda9874a_checkit(): DIC=0x%X, SIC=0x%X.\n", dic, sic); ++ ++ if((dic == 0x11)||(dic == 0x07)) { ++ v4l2_info(sd, "found tda9874%s.\n", (dic == 0x11) ? "a" : "h"); ++ tda9874a_dic = dic; /* remember device id. */ ++ return 1; ++ } ++ return 0; /* not found */ ++} ++ ++static int tda9874a_initialize(struct CHIPSTATE *chip) ++{ ++ if (tda9874a_SIF > 2) ++ tda9874a_SIF = 1; ++ if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) ++ tda9874a_STD = 0; ++ if(tda9874a_AMSEL > 1) ++ tda9874a_AMSEL = 0; ++ ++ if(tda9874a_SIF == 1) ++ tda9874a_GCONR = 0xc0; /* sound IF input 1 */ ++ else ++ tda9874a_GCONR = 0xc1; /* sound IF input 2 */ ++ ++ tda9874a_ESP = tda9874a_STD; ++ tda9874a_mode = (tda9874a_STD < 5) ? 0 : 1; ++ ++ if(tda9874a_AMSEL == 0) ++ tda9874a_NCONR = 0x01; /* auto-mute: analog mono input */ ++ else ++ tda9874a_NCONR = 0x05; /* auto-mute: 1st carrier FM or AM */ ++ ++ tda9874a_setup(chip); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip description - defines+functions for tda9875 */ ++/* The TDA9875 is made by Philips Semiconductor ++ * http://www.semiconductors.philips.com ++ * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator ++ * ++ */ ++ ++/* subaddresses for TDA9875 */ ++#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ ++#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ ++#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ ++#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ ++ ++#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ ++#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ ++#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ ++#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ ++ ++#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ ++#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ ++#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ ++#define TDA9875_MVL 0x1a /* Main volume gauche */ ++#define TDA9875_MVR 0x1b /* Main volume droite */ ++#define TDA9875_MBA 0x1d /* Main Basse */ ++#define TDA9875_MTR 0x1e /* Main treble */ ++#define TDA9875_ACS 0x1f /* Auxiliary channel select (FM) 0b0000000*/ ++#define TDA9875_AVL 0x20 /* Auxiliary volume gauche */ ++#define TDA9875_AVR 0x21 /* Auxiliary volume droite */ ++#define TDA9875_ABA 0x22 /* Auxiliary Basse */ ++#define TDA9875_ATR 0x23 /* Auxiliary treble */ ++ ++#define TDA9875_MSR 0x02 /* Monitor select register */ ++#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ ++#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ ++#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ ++#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ ++#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ ++#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ ++#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ ++#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ ++#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ ++ ++/* values */ ++#define TDA9875_MUTE_ON 0xff /* general mute */ ++#define TDA9875_MUTE_OFF 0xcc /* general no mute */ ++ ++static int tda9875_initialize(struct CHIPSTATE *chip) ++{ ++ chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ ++ chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ ++ chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ ++ chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ ++ chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ ++ chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ ++ chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ ++ chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ ++ chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ ++ chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ ++ chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ ++ chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ ++ chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ ++ ++ chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ ++ chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ ++ chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ ++ chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ ++ chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ ++ chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ ++ chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ ++ chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ ++ chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ ++ chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ ++ chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ ++ chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ ++ chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ ++ chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ ++ chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ ++ chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ ++ ++ chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ ++ return 0; ++} ++ ++static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } ++static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } ++static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } ++ ++/* ----------------------------------------------------------------------- */ ++ ++ ++/* *********************** * ++ * i2c interface functions * ++ * *********************** */ ++ ++static int tda9875_checkit(struct CHIPSTATE *chip) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int dic, rev; ++ ++ dic = chip_read2(chip, 254); ++ rev = chip_read2(chip, 255); ++ ++ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ ++ v4l2_info(sd, "found tda9875%s rev. %d.\n", ++ dic == 0 ? "" : "A", rev); ++ return 1; ++ } ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for tea6420 */ ++ ++#define TEA6300_VL 0x00 /* volume left */ ++#define TEA6300_VR 0x01 /* volume right */ ++#define TEA6300_BA 0x02 /* bass */ ++#define TEA6300_TR 0x03 /* treble */ ++#define TEA6300_FA 0x04 /* fader control */ ++#define TEA6300_S 0x05 /* switch register */ ++ /* values for those registers: */ ++#define TEA6300_S_SA 0x01 /* stereo A input */ ++#define TEA6300_S_SB 0x02 /* stereo B */ ++#define TEA6300_S_SC 0x04 /* stereo C */ ++#define TEA6300_S_GMU 0x80 /* general mute */ ++ ++#define TEA6320_V 0x00 /* volume (0-5)/loudness off (6)/zero crossing mute(7) */ ++#define TEA6320_FFR 0x01 /* fader front right (0-5) */ ++#define TEA6320_FFL 0x02 /* fader front left (0-5) */ ++#define TEA6320_FRR 0x03 /* fader rear right (0-5) */ ++#define TEA6320_FRL 0x04 /* fader rear left (0-5) */ ++#define TEA6320_BA 0x05 /* bass (0-4) */ ++#define TEA6320_TR 0x06 /* treble (0-4) */ ++#define TEA6320_S 0x07 /* switch register */ ++ /* values for those registers: */ ++#define TEA6320_S_SA 0x07 /* stereo A input */ ++#define TEA6320_S_SB 0x06 /* stereo B */ ++#define TEA6320_S_SC 0x05 /* stereo C */ ++#define TEA6320_S_SD 0x04 /* stereo D */ ++#define TEA6320_S_GMU 0x80 /* general mute */ ++ ++#define TEA6420_S_SA 0x00 /* stereo A input */ ++#define TEA6420_S_SB 0x01 /* stereo B */ ++#define TEA6420_S_SC 0x02 /* stereo C */ ++#define TEA6420_S_SD 0x03 /* stereo D */ ++#define TEA6420_S_SE 0x04 /* stereo E */ ++#define TEA6420_S_GMU 0x05 /* general mute */ ++ ++static int tea6300_shift10(int val) { return val >> 10; } ++static int tea6300_shift12(int val) { return val >> 12; } ++ ++/* Assumes 16bit input (values 0x3f to 0x0c are unique, values less than */ ++/* 0x0c mirror those immediately higher) */ ++static int tea6320_volume(int val) { return (val / (65535/(63-12)) + 12) & 0x3f; } ++static int tea6320_shift11(int val) { return val >> 11; } ++static int tea6320_initialize(struct CHIPSTATE * chip) ++{ ++ chip_write(chip, TEA6320_FFR, 0x3f); ++ chip_write(chip, TEA6320_FFL, 0x3f); ++ chip_write(chip, TEA6320_FRR, 0x3f); ++ chip_write(chip, TEA6320_FRL, 0x3f); ++ ++ return 0; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for tda8425 */ ++ ++#define TDA8425_VL 0x00 /* volume left */ ++#define TDA8425_VR 0x01 /* volume right */ ++#define TDA8425_BA 0x02 /* bass */ ++#define TDA8425_TR 0x03 /* treble */ ++#define TDA8425_S1 0x08 /* switch functions */ ++ /* values for those registers: */ ++#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ ++#define TDA8425_S1_CH1 0xCE /* audio channel 1 (mute off) - "linear stereo" mode */ ++#define TDA8425_S1_CH2 0xCF /* audio channel 2 (mute off) - "linear stereo" mode */ ++#define TDA8425_S1_MU 0x20 /* mute bit */ ++#define TDA8425_S1_STEREO 0x18 /* stereo bits */ ++#define TDA8425_S1_STEREO_SPATIAL 0x18 /* spatial stereo */ ++#define TDA8425_S1_STEREO_LINEAR 0x08 /* linear stereo */ ++#define TDA8425_S1_STEREO_PSEUDO 0x10 /* pseudo stereo */ ++#define TDA8425_S1_STEREO_MONO 0x00 /* forced mono */ ++#define TDA8425_S1_ML 0x06 /* language selector */ ++#define TDA8425_S1_ML_SOUND_A 0x02 /* sound a */ ++#define TDA8425_S1_ML_SOUND_B 0x04 /* sound b */ ++#define TDA8425_S1_ML_STEREO 0x06 /* stereo */ ++#define TDA8425_S1_IS 0x01 /* channel selector */ ++ ++ ++static int tda8425_shift10(int val) { return (val >> 10) | 0xc0; } ++static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } ++ ++static void tda8425_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ int s1 = chip->shadow.bytes[TDA8425_S1+1] & 0xe1; ++ ++ switch (mode) { ++ case V4L2_TUNER_MODE_LANG1: ++ s1 |= TDA8425_S1_ML_SOUND_A; ++ s1 |= TDA8425_S1_STEREO_PSEUDO; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ s1 |= TDA8425_S1_ML_SOUND_B; ++ s1 |= TDA8425_S1_STEREO_PSEUDO; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ s1 |= TDA8425_S1_ML_STEREO; ++ s1 |= TDA8425_S1_STEREO_LINEAR; ++ break; ++ case V4L2_TUNER_MODE_MONO: ++ s1 |= TDA8425_S1_ML_STEREO; ++ s1 |= TDA8425_S1_STEREO_MONO; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ s1 |= TDA8425_S1_ML_STEREO; ++ s1 |= TDA8425_S1_STEREO_SPATIAL; ++ break; ++ default: ++ return; ++ } ++ chip_write(chip,TDA8425_S1,s1); ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ ++ ++/* the registers of 16C54, I2C sub address. */ ++#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ ++#define PIC16C54_REG_MISC 0x02 ++ ++/* bit definition of the RESET register, I2C data. */ ++#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ ++ /* code of remote controller */ ++#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ ++#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ ++#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ ++#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ ++#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ ++#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ ++#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - defines+functions for TA8874Z */ ++ ++/* write 1st byte */ ++#define TA8874Z_LED_STE 0x80 ++#define TA8874Z_LED_BIL 0x40 ++#define TA8874Z_LED_EXT 0x20 ++#define TA8874Z_MONO_SET 0x10 ++#define TA8874Z_MUTE 0x08 ++#define TA8874Z_F_MONO 0x04 ++#define TA8874Z_MODE_SUB 0x02 ++#define TA8874Z_MODE_MAIN 0x01 ++ ++/* write 2nd byte */ ++/*#define TA8874Z_TI 0x80 */ /* test mode */ ++#define TA8874Z_SEPARATION 0x3f ++#define TA8874Z_SEPARATION_DEFAULT 0x10 ++ ++/* read */ ++#define TA8874Z_B1 0x80 ++#define TA8874Z_B0 0x40 ++#define TA8874Z_CHAG_FLAG 0x20 ++ ++/* ++ * B1 B0 ++ * mono L H ++ * stereo L L ++ * BIL H L ++ */ ++static int ta8874z_getrxsubchans(struct CHIPSTATE *chip) ++{ ++ int val, mode; ++ ++ val = chip_read(chip); ++ mode = V4L2_TUNER_SUB_MONO; ++ if (val & TA8874Z_B1){ ++ mode |= V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ }else if (!(val & TA8874Z_B0)){ ++ mode = V4L2_TUNER_SUB_STEREO; ++ } ++ /* v4l2_dbg(1, debug, &chip->sd, ++ "ta8874z_getrxsubchans(): raw chip read: 0x%02x, return: 0x%02x\n", ++ val, mode); */ ++ return mode; ++} ++ ++static audiocmd ta8874z_stereo = { 2, {0, TA8874Z_SEPARATION_DEFAULT}}; ++static audiocmd ta8874z_mono = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}; ++static audiocmd ta8874z_main = {2, { 0, TA8874Z_SEPARATION_DEFAULT}}; ++static audiocmd ta8874z_sub = {2, { TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; ++static audiocmd ta8874z_both = {2, { TA8874Z_MODE_MAIN | TA8874Z_MODE_SUB, TA8874Z_SEPARATION_DEFAULT}}; ++ ++static void ta8874z_setaudmode(struct CHIPSTATE *chip, int mode) ++{ ++ struct v4l2_subdev *sd = &chip->sd; ++ int update = 1; ++ audiocmd *t = NULL; ++ ++ v4l2_dbg(1, debug, sd, "ta8874z_setaudmode(): mode: 0x%02x\n", mode); ++ ++ switch(mode){ ++ case V4L2_TUNER_MODE_MONO: ++ t = &ta8874z_mono; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ t = &ta8874z_stereo; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ t = &ta8874z_main; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ t = &ta8874z_sub; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ t = &ta8874z_both; ++ break; ++ default: ++ update = 0; ++ } ++ ++ if(update) ++ chip_cmd(chip, "TA8874Z", t); ++} ++ ++static int ta8874z_checkit(struct CHIPSTATE *chip) ++{ ++ int rc; ++ rc = chip_read(chip); ++ return ((rc & 0x1f) == 0x1f) ? 1 : 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* audio chip descriptions - struct CHIPDESC */ ++ ++/* insmod options to enable/disable individual audio chips */ ++static int tda8425 = 1; ++static int tda9840 = 1; ++static int tda9850 = 1; ++static int tda9855 = 1; ++static int tda9873 = 1; ++static int tda9874a = 1; ++static int tda9875 = 1; ++static int tea6300; /* default 0 - address clash with msp34xx */ ++static int tea6320; /* default 0 - address clash with msp34xx */ ++static int tea6420 = 1; ++static int pic16c54 = 1; ++static int ta8874z; /* default 0 - address clash with tda9840 */ ++ ++module_param(tda8425, int, 0444); ++module_param(tda9840, int, 0444); ++module_param(tda9850, int, 0444); ++module_param(tda9855, int, 0444); ++module_param(tda9873, int, 0444); ++module_param(tda9874a, int, 0444); ++module_param(tda9875, int, 0444); ++module_param(tea6300, int, 0444); ++module_param(tea6320, int, 0444); ++module_param(tea6420, int, 0444); ++module_param(pic16c54, int, 0444); ++module_param(ta8874z, int, 0444); ++ ++static struct CHIPDESC chiplist[] = { ++ { ++ .name = "tda9840", ++ .insmodopt = &tda9840, ++ .addr_lo = I2C_ADDR_TDA9840 >> 1, ++ .addr_hi = I2C_ADDR_TDA9840 >> 1, ++ .registers = 5, ++ .flags = CHIP_NEED_CHECKMODE, ++ ++ /* callbacks */ ++ .checkit = tda9840_checkit, ++ .getrxsubchans = tda9840_getrxsubchans, ++ .setaudmode = tda9840_setaudmode, ++ ++ .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN ++ /* ,TDA9840_SW, TDA9840_MONO */} } ++ }, ++ { ++ .name = "tda9873h", ++ .insmodopt = &tda9873, ++ .addr_lo = I2C_ADDR_TDA985x_L >> 1, ++ .addr_hi = I2C_ADDR_TDA985x_H >> 1, ++ .registers = 3, ++ .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, ++ ++ /* callbacks */ ++ .checkit = tda9873_checkit, ++ .getrxsubchans = tda9873_getrxsubchans, ++ .setaudmode = tda9873_setaudmode, ++ ++ .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, ++ .inputreg = TDA9873_SW, ++ .inputmute = TDA9873_MUTE | TDA9873_AUTOMUTE, ++ .inputmap = {0xa0, 0xa2, 0xa0, 0xa0}, ++ .inputmask = TDA9873_INP_MASK|TDA9873_MUTE|TDA9873_AUTOMUTE, ++ ++ }, ++ { ++ .name = "tda9874h/a", ++ .insmodopt = &tda9874a, ++ .addr_lo = I2C_ADDR_TDA9874 >> 1, ++ .addr_hi = I2C_ADDR_TDA9874 >> 1, ++ .flags = CHIP_NEED_CHECKMODE, ++ ++ /* callbacks */ ++ .initialize = tda9874a_initialize, ++ .checkit = tda9874a_checkit, ++ .getrxsubchans = tda9874a_getrxsubchans, ++ .setaudmode = tda9874a_setaudmode, ++ }, ++ { ++ .name = "tda9875", ++ .insmodopt = &tda9875, ++ .addr_lo = I2C_ADDR_TDA9875 >> 1, ++ .addr_hi = I2C_ADDR_TDA9875 >> 1, ++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, ++ ++ /* callbacks */ ++ .initialize = tda9875_initialize, ++ .checkit = tda9875_checkit, ++ .volfunc = tda9875_volume, ++ .bassfunc = tda9875_bass, ++ .treblefunc = tda9875_treble, ++ .leftreg = TDA9875_MVL, ++ .rightreg = TDA9875_MVR, ++ .bassreg = TDA9875_MBA, ++ .treblereg = TDA9875_MTR, ++ .leftinit = 58880, ++ .rightinit = 58880, ++ }, ++ { ++ .name = "tda9850", ++ .insmodopt = &tda9850, ++ .addr_lo = I2C_ADDR_TDA985x_L >> 1, ++ .addr_hi = I2C_ADDR_TDA985x_H >> 1, ++ .registers = 11, ++ ++ .getrxsubchans = tda985x_getrxsubchans, ++ .setaudmode = tda985x_setaudmode, ++ ++ .init = { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } ++ }, ++ { ++ .name = "tda9855", ++ .insmodopt = &tda9855, ++ .addr_lo = I2C_ADDR_TDA985x_L >> 1, ++ .addr_hi = I2C_ADDR_TDA985x_H >> 1, ++ .registers = 11, ++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, ++ ++ .leftreg = TDA9855_VL, ++ .rightreg = TDA9855_VR, ++ .bassreg = TDA9855_BA, ++ .treblereg = TDA9855_TR, ++ ++ /* callbacks */ ++ .volfunc = tda9855_volume, ++ .bassfunc = tda9855_bass, ++ .treblefunc = tda9855_treble, ++ .getrxsubchans = tda985x_getrxsubchans, ++ .setaudmode = tda985x_setaudmode, ++ ++ .init = { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, ++ TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, ++ TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, ++ 0x07, 0x10, 0x10, 0x03 }} ++ }, ++ { ++ .name = "tea6300", ++ .insmodopt = &tea6300, ++ .addr_lo = I2C_ADDR_TEA6300 >> 1, ++ .addr_hi = I2C_ADDR_TEA6300 >> 1, ++ .registers = 6, ++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, ++ ++ .leftreg = TEA6300_VR, ++ .rightreg = TEA6300_VL, ++ .bassreg = TEA6300_BA, ++ .treblereg = TEA6300_TR, ++ ++ /* callbacks */ ++ .volfunc = tea6300_shift10, ++ .bassfunc = tea6300_shift12, ++ .treblefunc = tea6300_shift12, ++ ++ .inputreg = TEA6300_S, ++ .inputmap = { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, ++ .inputmute = TEA6300_S_GMU, ++ }, ++ { ++ .name = "tea6320", ++ .insmodopt = &tea6320, ++ .addr_lo = I2C_ADDR_TEA6300 >> 1, ++ .addr_hi = I2C_ADDR_TEA6300 >> 1, ++ .registers = 8, ++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, ++ ++ .leftreg = TEA6320_V, ++ .rightreg = TEA6320_V, ++ .bassreg = TEA6320_BA, ++ .treblereg = TEA6320_TR, ++ ++ /* callbacks */ ++ .initialize = tea6320_initialize, ++ .volfunc = tea6320_volume, ++ .bassfunc = tea6320_shift11, ++ .treblefunc = tea6320_shift11, ++ ++ .inputreg = TEA6320_S, ++ .inputmap = { TEA6320_S_SA, TEA6420_S_SB, TEA6300_S_SC, TEA6320_S_SD }, ++ .inputmute = TEA6300_S_GMU, ++ }, ++ { ++ .name = "tea6420", ++ .insmodopt = &tea6420, ++ .addr_lo = I2C_ADDR_TEA6420 >> 1, ++ .addr_hi = I2C_ADDR_TEA6420 >> 1, ++ .registers = 1, ++ .flags = CHIP_HAS_INPUTSEL, ++ ++ .inputreg = -1, ++ .inputmap = { TEA6420_S_SA, TEA6420_S_SB, TEA6420_S_SC }, ++ .inputmute = TEA6300_S_GMU, ++ }, ++ { ++ .name = "tda8425", ++ .insmodopt = &tda8425, ++ .addr_lo = I2C_ADDR_TDA8425 >> 1, ++ .addr_hi = I2C_ADDR_TDA8425 >> 1, ++ .registers = 9, ++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, ++ ++ .leftreg = TDA8425_VL, ++ .rightreg = TDA8425_VR, ++ .bassreg = TDA8425_BA, ++ .treblereg = TDA8425_TR, ++ ++ /* callbacks */ ++ .volfunc = tda8425_shift10, ++ .bassfunc = tda8425_shift12, ++ .treblefunc = tda8425_shift12, ++ .setaudmode = tda8425_setaudmode, ++ ++ .inputreg = TDA8425_S1, ++ .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, ++ .inputmute = TDA8425_S1_OFF, ++ ++ }, ++ { ++ .name = "pic16c54 (PV951)", ++ .insmodopt = &pic16c54, ++ .addr_lo = I2C_ADDR_PIC16C54 >> 1, ++ .addr_hi = I2C_ADDR_PIC16C54>> 1, ++ .registers = 2, ++ .flags = CHIP_HAS_INPUTSEL, ++ ++ .inputreg = PIC16C54_REG_MISC, ++ .inputmap = {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, ++ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, ++ PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE, ++ PIC16C54_MISC_SND_MUTE}, ++ .inputmute = PIC16C54_MISC_SND_MUTE, ++ }, ++ { ++ .name = "ta8874z", ++ .checkit = ta8874z_checkit, ++ .insmodopt = &ta8874z, ++ .addr_lo = I2C_ADDR_TDA9840 >> 1, ++ .addr_hi = I2C_ADDR_TDA9840 >> 1, ++ .registers = 2, ++ ++ /* callbacks */ ++ .getrxsubchans = ta8874z_getrxsubchans, ++ .setaudmode = ta8874z_setaudmode, ++ ++ .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, ++ }, ++ { .name = NULL } /* EOF */ ++}; ++ ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int tvaudio_g_ctrl(struct v4l2_subdev *sd, ++ struct v4l2_control *ctrl) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ if (!(desc->flags & CHIP_HAS_INPUTSEL)) ++ break; ++ ctrl->value=chip->muted; ++ return 0; ++ case V4L2_CID_AUDIO_VOLUME: ++ if (!(desc->flags & CHIP_HAS_VOLUME)) ++ break; ++ ctrl->value = max(chip->left,chip->right); ++ return 0; ++ case V4L2_CID_AUDIO_BALANCE: ++ { ++ int volume; ++ if (!(desc->flags & CHIP_HAS_VOLUME)) ++ break; ++ volume = max(chip->left,chip->right); ++ if (volume) ++ ctrl->value=(32768*min(chip->left,chip->right))/volume; ++ else ++ ctrl->value=32768; ++ return 0; ++ } ++ case V4L2_CID_AUDIO_BASS: ++ if (!(desc->flags & CHIP_HAS_BASSTREBLE)) ++ break; ++ ctrl->value = chip->bass; ++ return 0; ++ case V4L2_CID_AUDIO_TREBLE: ++ if (!(desc->flags & CHIP_HAS_BASSTREBLE)) ++ break; ++ ctrl->value = chip->treble; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int tvaudio_s_ctrl(struct v4l2_subdev *sd, ++ struct v4l2_control *ctrl) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ if (!(desc->flags & CHIP_HAS_INPUTSEL)) ++ break; ++ ++ if (ctrl->value < 0 || ctrl->value >= 2) ++ return -ERANGE; ++ chip->muted = ctrl->value; ++ if (chip->muted) ++ chip_write_masked(chip,desc->inputreg,desc->inputmute,desc->inputmask); ++ else ++ chip_write_masked(chip,desc->inputreg, ++ desc->inputmap[chip->input],desc->inputmask); ++ return 0; ++ case V4L2_CID_AUDIO_VOLUME: ++ { ++ int volume,balance; ++ ++ if (!(desc->flags & CHIP_HAS_VOLUME)) ++ break; ++ ++ volume = max(chip->left,chip->right); ++ if (volume) ++ balance=(32768*min(chip->left,chip->right))/volume; ++ else ++ balance=32768; ++ ++ volume=ctrl->value; ++ chip->left = (min(65536 - balance,32768) * volume) / 32768; ++ chip->right = (min(balance,volume *(__u16)32768)) / 32768; ++ ++ chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); ++ chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); ++ ++ return 0; ++ } ++ case V4L2_CID_AUDIO_BALANCE: ++ { ++ int volume, balance; ++ ++ if (!(desc->flags & CHIP_HAS_VOLUME)) ++ break; ++ ++ volume = max(chip->left, chip->right); ++ balance = ctrl->value; ++ chip->left = (min(65536 - balance, 32768) * volume) / 32768; ++ chip->right = (min(balance, volume * (__u16)32768)) / 32768; ++ ++ chip_write(chip, desc->leftreg, desc->volfunc(chip->left)); ++ chip_write(chip, desc->rightreg, desc->volfunc(chip->right)); ++ ++ return 0; ++ } ++ case V4L2_CID_AUDIO_BASS: ++ if (!(desc->flags & CHIP_HAS_BASSTREBLE)) ++ break; ++ chip->bass = ctrl->value; ++ chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); ++ ++ return 0; ++ case V4L2_CID_AUDIO_TREBLE: ++ if (!(desc->flags & CHIP_HAS_BASSTREBLE)) ++ break; ++ chip->treble = ctrl->value; ++ chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); ++ ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* video4linux interface */ ++ ++static int tvaudio_s_radio(struct v4l2_subdev *sd) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ ++ chip->radio = 1; ++ /* del_timer(&chip->wt); */ ++ return 0; ++} ++ ++static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ switch (qc->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ if (desc->flags & CHIP_HAS_INPUTSEL) ++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ if (desc->flags & CHIP_HAS_VOLUME) ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); ++ break; ++ case V4L2_CID_AUDIO_BALANCE: ++ if (desc->flags & CHIP_HAS_VOLUME) ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); ++ break; ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ if (desc->flags & CHIP_HAS_BASSTREBLE) ++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); ++ break; ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++static int tvaudio_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ if (!(desc->flags & CHIP_HAS_INPUTSEL)) ++ return 0; ++ if (input >= 4) ++ return -EINVAL; ++ /* There are four inputs: tuner, radio, extern and intern. */ ++ chip->input = input; ++ if (chip->muted) ++ return 0; ++ chip_write_masked(chip, desc->inputreg, ++ desc->inputmap[chip->input], desc->inputmask); ++ return 0; ++} ++ ++static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ if (!desc->setaudmode) ++ return 0; ++ if (chip->radio) ++ return 0; ++ ++ switch (vt->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1: ++ case V4L2_TUNER_MODE_LANG2: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ break; ++ default: ++ return -EINVAL; ++ } ++ chip->audmode = vt->audmode; ++ ++ if (chip->thread) ++ wake_up_process(chip->thread); ++ else ++ desc->setaudmode(chip, vt->audmode); ++ ++ return 0; ++} ++ ++static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ if (!desc->getrxsubchans) ++ return 0; ++ if (chip->radio) ++ return 0; ++ ++ vt->audmode = chip->audmode; ++ vt->rxsubchans = desc->getrxsubchans(chip); ++ vt->capability = V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; ++ ++ return 0; ++} ++ ++static int tvaudio_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ ++ chip->radio = 0; ++ return 0; ++} ++ ++static int tvaudio_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) ++{ ++ struct CHIPSTATE *chip = to_state(sd); ++ struct CHIPDESC *desc = chip->desc; ++ ++ /* For chips that provide getrxsubchans and setaudmode, and doesn't ++ automatically follows the stereo carrier, a kthread is ++ created to set the audio standard. In this case, when then ++ the video channel is changed, tvaudio starts on MONO mode. ++ After waiting for 2 seconds, the kernel thread is called, ++ to follow whatever audio standard is pointed by the ++ audio carrier. ++ */ ++ if (chip->thread) { ++ desc->setaudmode(chip, V4L2_TUNER_MODE_MONO); ++ chip->prevmode = -1; /* reset previous mode */ ++ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); ++ } ++ return 0; ++} ++ ++static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops tvaudio_core_ops = { ++ .g_chip_ident = tvaudio_g_chip_ident, ++ .queryctrl = tvaudio_queryctrl, ++ .g_ctrl = tvaudio_g_ctrl, ++ .s_ctrl = tvaudio_s_ctrl, ++ .s_std = tvaudio_s_std, ++}; ++ ++static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { ++ .s_radio = tvaudio_s_radio, ++ .s_frequency = tvaudio_s_frequency, ++ .s_tuner = tvaudio_s_tuner, ++ .g_tuner = tvaudio_g_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { ++ .s_routing = tvaudio_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops tvaudio_ops = { ++ .core = &tvaudio_core_ops, ++ .tuner = &tvaudio_tuner_ops, ++ .audio = &tvaudio_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++ ++/* i2c registration */ ++ ++static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct CHIPSTATE *chip; ++ struct CHIPDESC *desc; ++ struct v4l2_subdev *sd; ++ ++ if (debug) { ++ printk(KERN_INFO "tvaudio: TV audio decoder + audio/video mux driver\n"); ++ printk(KERN_INFO "tvaudio: known chips: "); ++ for (desc = chiplist; desc->name != NULL; desc++) ++ printk("%s%s", (desc == chiplist) ? "" : ", ", desc->name); ++ printk("\n"); ++ } ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ sd = &chip->sd; ++ v4l2_i2c_subdev_init(sd, client, &tvaudio_ops); ++ ++ /* find description for the chip */ ++ v4l2_dbg(1, debug, sd, "chip found @ 0x%x\n", client->addr<<1); ++ for (desc = chiplist; desc->name != NULL; desc++) { ++ if (0 == *(desc->insmodopt)) ++ continue; ++ if (client->addr < desc->addr_lo || ++ client->addr > desc->addr_hi) ++ continue; ++ if (desc->checkit && !desc->checkit(chip)) ++ continue; ++ break; ++ } ++ if (desc->name == NULL) { ++ v4l2_dbg(1, debug, sd, "no matching chip description found\n"); ++ kfree(chip); ++ return -EIO; ++ } ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); ++ if (desc->flags) { ++ v4l2_dbg(1, debug, sd, "matches:%s%s%s.\n", ++ (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", ++ (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", ++ (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); ++ } ++ ++ /* fill required data structures */ ++ if (!id) ++ strlcpy(client->name, desc->name, I2C_NAME_SIZE); ++ chip->desc = desc; ++ chip->shadow.count = desc->registers+1; ++ chip->prevmode = -1; ++ chip->audmode = V4L2_TUNER_MODE_LANG1; ++ ++ /* initialization */ ++ if (desc->initialize != NULL) ++ desc->initialize(chip); ++ else ++ chip_cmd(chip, "init", &desc->init); ++ ++ if (desc->flags & CHIP_HAS_VOLUME) { ++ if (!desc->volfunc) { ++ /* This shouldn't be happen. Warn user, but keep working ++ without volume controls ++ */ ++ v4l2_info(sd, "volume callback undefined!\n"); ++ desc->flags &= ~CHIP_HAS_VOLUME; ++ } else { ++ chip->left = desc->leftinit ? desc->leftinit : 65535; ++ chip->right = desc->rightinit ? desc->rightinit : 65535; ++ chip_write(chip, desc->leftreg, ++ desc->volfunc(chip->left)); ++ chip_write(chip, desc->rightreg, ++ desc->volfunc(chip->right)); ++ } ++ } ++ if (desc->flags & CHIP_HAS_BASSTREBLE) { ++ if (!desc->bassfunc || !desc->treblefunc) { ++ /* This shouldn't be happen. Warn user, but keep working ++ without bass/treble controls ++ */ ++ v4l2_info(sd, "bass/treble callbacks undefined!\n"); ++ desc->flags &= ~CHIP_HAS_BASSTREBLE; ++ } else { ++ chip->treble = desc->trebleinit ? ++ desc->trebleinit : 32768; ++ chip->bass = desc->bassinit ? ++ desc->bassinit : 32768; ++ chip_write(chip, desc->bassreg, ++ desc->bassfunc(chip->bass)); ++ chip_write(chip, desc->treblereg, ++ desc->treblefunc(chip->treble)); ++ } ++ } ++ ++ chip->thread = NULL; ++ init_timer(&chip->wt); ++ if (desc->flags & CHIP_NEED_CHECKMODE) { ++ if (!desc->getrxsubchans || !desc->setaudmode) { ++ /* This shouldn't be happen. Warn user, but keep working ++ without kthread ++ */ ++ v4l2_info(sd, "set/get mode callbacks undefined!\n"); ++ return 0; ++ } ++ /* start async thread */ ++ chip->wt.function = chip_thread_wake; ++ chip->wt.data = (unsigned long)chip; ++ chip->thread = kthread_run(chip_thread, chip, client->name); ++ if (IS_ERR(chip->thread)) { ++ v4l2_warn(sd, "failed to create kthread\n"); ++ chip->thread = NULL; ++ } ++ } ++ return 0; ++} ++ ++static int tvaudio_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct CHIPSTATE *chip = to_state(sd); ++ ++ del_timer_sync(&chip->wt); ++ if (chip->thread) { ++ /* shutdown async thread */ ++ kthread_stop(chip->thread); ++ chip->thread = NULL; ++ } ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(chip); ++ return 0; ++} ++ ++/* This driver supports many devices and the idea is to let the driver ++ detect which device is present. So rather than listing all supported ++ devices here, we pretend to support a single, fake device type. */ ++static const struct i2c_device_id tvaudio_id[] = { ++ { "tvaudio", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tvaudio_id); ++ ++static struct i2c_driver tvaudio_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tvaudio", ++ }, ++ .probe = tvaudio_probe, ++ .remove = tvaudio_remove, ++ .id_table = tvaudio_id, ++}; ++ ++module_i2c_driver(tvaudio_driver); +diff --git a/drivers/media/i2c/tveeprom.c b/drivers/media/i2c/tveeprom.c +new file mode 100644 +index 0000000..3b6cf03 +--- /dev/null ++++ b/drivers/media/i2c/tveeprom.c +@@ -0,0 +1,792 @@ ++/* ++ * 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 ++ ++ * Adjustments to fit a more general model and all bugs: ++ ++ Copyright (C) 2003 John Klar ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 struct HAUPPAUGE_TUNER_FMT ++{ ++ int id; ++ char *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 struct HAUPPAUGE_TUNER ++{ ++ int id; ++ char *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-189 */ ++ { TUNER_ABSENT, "NXP 18272M"}, ++ { TUNER_ABSENT, "NXP 18272S"}, ++}; ++ ++/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are ++ * internal to a video chip, i.e. not a separate audio chip. */ ++static struct HAUPPAUGE_AUDIOIC ++{ ++ u32 id; ++ char *name; ++} ++audioIC[] = ++{ ++ /* 0-4 */ ++ { V4L2_IDENT_NONE, "None" }, ++ { V4L2_IDENT_UNKNOWN, "TEA6300" }, ++ { V4L2_IDENT_UNKNOWN, "TEA6320" }, ++ { V4L2_IDENT_UNKNOWN, "TDA9850" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3400C" }, ++ /* 5-9 */ ++ { V4L2_IDENT_MSPX4XX, "MSP3410D" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3415" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3430" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3438" }, ++ { V4L2_IDENT_UNKNOWN, "CS5331" }, ++ /* 10-14 */ ++ { V4L2_IDENT_MSPX4XX, "MSP3435" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3440" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3445" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3411" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3416" }, ++ /* 15-19 */ ++ { V4L2_IDENT_MSPX4XX, "MSP3425" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3451" }, ++ { V4L2_IDENT_MSPX4XX, "MSP3418" }, ++ { V4L2_IDENT_UNKNOWN, "Type 0x12" }, ++ { V4L2_IDENT_UNKNOWN, "OKI7716" }, ++ /* 20-24 */ ++ { V4L2_IDENT_MSPX4XX, "MSP4410" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4420" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4440" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4450" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4408" }, ++ /* 25-29 */ ++ { V4L2_IDENT_MSPX4XX, "MSP4418" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4428" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4448" }, ++ { V4L2_IDENT_MSPX4XX, "MSP4458" }, ++ { V4L2_IDENT_MSPX4XX, "Type 0x1d" }, ++ /* 30-34 */ ++ { V4L2_IDENT_AMBIGUOUS, "CX880" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX881" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX883" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX882" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX25840" }, ++ /* 35-39 */ ++ { V4L2_IDENT_AMBIGUOUS, "CX25841" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX25842" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX25843" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX23418" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX23885" }, ++ /* 40-44 */ ++ { V4L2_IDENT_AMBIGUOUS, "CX23888" }, ++ { V4L2_IDENT_AMBIGUOUS, "SAA7131" }, ++ { V4L2_IDENT_AMBIGUOUS, "CX23887" }, ++ { V4L2_IDENT_AMBIGUOUS, "SAA7164" }, ++ { V4L2_IDENT_AMBIGUOUS, "AU8522" }, ++}; ++ ++/* 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-42 */ ++ "SAA7164", "CX23885B", "AU8522" ++}; ++ ++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; ++ char *t_name1 = NULL; ++ const char *t_fmt_name1[8] = { " none", "", "", "", "", "", "", "" }; ++ ++ int tuner2 = 0, t_format2 = 0; ++ 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(audioIC)) ++ tvee->audio_processor = audioIC[audioic].id; ++ else ++ tvee->audio_processor = V4L2_IDENT_UNKNOWN; ++ 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); ++ ++ if ((eeprom_data[i + 8] & 0xf0) && ++ (tvee->serial_number < 0xffffff)) { ++ 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(audioIC)) ++ tvee->audio_processor = audioIC[audioic].id; ++ else ++ tvee->audio_processor = V4L2_IDENT_UNKNOWN; ++ ++ 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# %d\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 = V4L2_IDENT_UNKNOWN; ++ } else { ++ if (audioic < ARRAY_SIZE(audioIC)) ++ tveeprom_info("audio processor is %s (idx %d)\n", ++ audioIC[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); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c +new file mode 100644 +index 0000000..d5e1021 +--- /dev/null ++++ b/drivers/media/i2c/tvp514x.c +@@ -0,0 +1,1101 @@ ++/* ++ * drivers/media/i2c/tvp514x.c ++ * ++ * TI TVP5146/47 decoder driver ++ * ++ * Copyright (C) 2008 Texas Instruments Inc ++ * Author: Vaibhav Hiremath ++ * ++ * Contributors: ++ * Sivaraj R ++ * Brijesh R Jadav ++ * Hardik Shah ++ * Manjunath Hadli ++ * Karicheri Muralidharan ++ * ++ * This package is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tvp514x_regs.h" ++ ++/* Module Name */ ++#define TVP514X_MODULE_NAME "tvp514x" ++ ++/* Private macros for TVP */ ++#define I2C_RETRY_COUNT (5) ++#define LOCK_RETRY_COUNT (5) ++#define LOCK_RETRY_DELAY (200) ++ ++/* Debug functions */ ++static bool debug; ++module_param(debug, bool, 0644); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++MODULE_AUTHOR("Texas Instruments"); ++MODULE_DESCRIPTION("TVP514X linux decoder driver"); ++MODULE_LICENSE("GPL"); ++ ++/* enum tvp514x_std - enum for supported standards */ ++enum tvp514x_std { ++ STD_NTSC_MJ = 0, ++ STD_PAL_BDGHIN, ++ STD_INVALID ++}; ++ ++/** ++ * struct tvp514x_std_info - Structure to store standard informations ++ * @width: Line width in pixels ++ * @height:Number of active lines ++ * @video_std: Value to write in REG_VIDEO_STD register ++ * @standard: v4l2 standard structure information ++ */ ++struct tvp514x_std_info { ++ unsigned long width; ++ unsigned long height; ++ u8 video_std; ++ struct v4l2_standard standard; ++}; ++ ++static struct tvp514x_reg tvp514x_reg_list_default[0x40]; ++ ++static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable); ++/** ++ * struct tvp514x_decoder - TVP5146/47 decoder object ++ * @sd: Subdevice Slave handle ++ * @tvp514x_regs: copy of hw's regs with preset values. ++ * @pdata: Board specific ++ * @ver: Chip version ++ * @streaming: TVP5146/47 decoder streaming - enabled or disabled. ++ * @current_std: Current standard ++ * @num_stds: Number of standards ++ * @std_list: Standards list ++ * @input: Input routing at chip level ++ * @output: Output routing at chip level ++ */ ++struct tvp514x_decoder { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; ++ const struct tvp514x_platform_data *pdata; ++ ++ int ver; ++ int streaming; ++ ++ enum tvp514x_std current_std; ++ int num_stds; ++ const struct tvp514x_std_info *std_list; ++ /* Input and Output Routing parameters */ ++ u32 input; ++ u32 output; ++}; ++ ++/* TVP514x default register values */ ++static struct tvp514x_reg tvp514x_reg_list_default[] = { ++ /* Composite selected */ ++ {TOK_WRITE, REG_INPUT_SEL, 0x05}, ++ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, ++ /* Auto mode */ ++ {TOK_WRITE, REG_VIDEO_STD, 0x00}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x00}, ++ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, ++ {TOK_WRITE, REG_COLOR_KILLER, 0x10}, ++ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00}, ++ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00}, ++ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02}, ++ {TOK_WRITE, REG_BRIGHTNESS, 0x80}, ++ {TOK_WRITE, REG_CONTRAST, 0x80}, ++ {TOK_WRITE, REG_SATURATION, 0x80}, ++ {TOK_WRITE, REG_HUE, 0x00}, ++ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, ++ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, ++ /* Reserved */ ++ {TOK_SKIP, 0x0F, 0x00}, ++ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, ++ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, ++ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, ++ /* Reserved */ ++ {TOK_SKIP, 0x13, 0x00}, ++ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, ++ /* Reserved */ ++ {TOK_SKIP, 0x15, 0x00}, ++ /* NTSC timing */ ++ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, ++ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, ++ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, ++ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, ++ /* NTSC timing */ ++ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, ++ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, ++ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, ++ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, ++ /* NTSC timing */ ++ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, ++ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, ++ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, ++ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, ++ /* NTSC timing */ ++ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, ++ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, ++ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, ++ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x26, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x27, 0x00}, ++ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, ++ /* Reserved */ ++ {TOK_SKIP, 0x29, 0x00}, ++ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x2B, 0x00}, ++ {TOK_SKIP, REG_SCART_DELAY, 0x00}, ++ {TOK_SKIP, REG_CTI_DELAY, 0x00}, ++ {TOK_SKIP, REG_CTI_CONTROL, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x2F, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x30, 0x00}, ++ /* Reserved */ ++ {TOK_SKIP, 0x31, 0x00}, ++ /* HS, VS active high */ ++ {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, ++ /* 10-bit BT.656 */ ++ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, ++ /* Enable clk & data */ ++ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, ++ /* Enable AVID & FLD */ ++ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, ++ /* Enable VS & HS */ ++ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, ++ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, ++ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, ++ /* Clear status */ ++ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, ++ {TOK_TERM, 0, 0}, ++}; ++ ++/** ++ * Supported standards - ++ * ++ * Currently supports two standards only, need to add support for rest of the ++ * modes, like SECAM, etc... ++ */ ++static const struct tvp514x_std_info tvp514x_std_list[] = { ++ /* Standard: STD_NTSC_MJ */ ++ [STD_NTSC_MJ] = { ++ .width = NTSC_NUM_ACTIVE_PIXELS, ++ .height = NTSC_NUM_ACTIVE_LINES, ++ .video_std = VIDEO_STD_NTSC_MJ_BIT, ++ .standard = { ++ .index = 0, ++ .id = V4L2_STD_NTSC, ++ .name = "NTSC", ++ .frameperiod = {1001, 30000}, ++ .framelines = 525 ++ }, ++ /* Standard: STD_PAL_BDGHIN */ ++ }, ++ [STD_PAL_BDGHIN] = { ++ .width = PAL_NUM_ACTIVE_PIXELS, ++ .height = PAL_NUM_ACTIVE_LINES, ++ .video_std = VIDEO_STD_PAL_BDGHIN_BIT, ++ .standard = { ++ .index = 1, ++ .id = V4L2_STD_PAL, ++ .name = "PAL", ++ .frameperiod = {1, 25}, ++ .framelines = 625 ++ }, ++ }, ++ /* Standard: need to add for additional standard */ ++}; ++ ++ ++static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct tvp514x_decoder, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct tvp514x_decoder, hdl)->sd; ++} ++ ++ ++/** ++ * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. ++ * @sd: ptr to v4l2_subdev struct ++ * @reg: TVP5146/47 register address ++ * ++ * Returns value read if successful, or non-zero (-1) otherwise. ++ */ ++static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) ++{ ++ int err, retry = 0; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++read_again: ++ ++ err = i2c_smbus_read_byte_data(client, reg); ++ if (err < 0) { ++ if (retry <= I2C_RETRY_COUNT) { ++ v4l2_warn(sd, "Read: retry ... %d\n", retry); ++ retry++; ++ msleep_interruptible(10); ++ goto read_again; ++ } ++ } ++ ++ return err; ++} ++ ++/** ++ * dump_reg() - dump the register content of TVP5146/47. ++ * @sd: ptr to v4l2_subdev struct ++ * @reg: TVP5146/47 register address ++ */ ++static void dump_reg(struct v4l2_subdev *sd, u8 reg) ++{ ++ u32 val; ++ ++ val = tvp514x_read_reg(sd, reg); ++ v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); ++} ++ ++/** ++ * tvp514x_write_reg() - Write a value to a register in TVP5146/47 ++ * @sd: ptr to v4l2_subdev struct ++ * @reg: TVP5146/47 register address ++ * @val: value to be written to the register ++ * ++ * Write a value to a register in an TVP5146/47 decoder device. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ int err, retry = 0; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++write_again: ++ ++ err = i2c_smbus_write_byte_data(client, reg, val); ++ if (err) { ++ if (retry <= I2C_RETRY_COUNT) { ++ v4l2_warn(sd, "Write: retry ... %d\n", retry); ++ retry++; ++ msleep_interruptible(10); ++ goto write_again; ++ } ++ } ++ ++ return err; ++} ++ ++/** ++ * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers ++ * @sd: ptr to v4l2_subdev struct ++ * @reglist: list of TVP5146/47 registers and values ++ * ++ * Initializes a list of TVP5146/47 registers:- ++ * if token is TOK_TERM, then entire write operation terminates ++ * if token is TOK_DELAY, then a delay of 'val' msec is introduced ++ * if token is TOK_SKIP, then the register write is skipped ++ * if token is TOK_WRITE, then the register write is performed ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tvp514x_write_regs(struct v4l2_subdev *sd, ++ const struct tvp514x_reg reglist[]) ++{ ++ int err; ++ const struct tvp514x_reg *next = reglist; ++ ++ for (; next->token != TOK_TERM; next++) { ++ if (next->token == TOK_DELAY) { ++ msleep(next->val); ++ continue; ++ } ++ ++ if (next->token == TOK_SKIP) ++ continue; ++ ++ err = tvp514x_write_reg(sd, next->reg, (u8) next->val); ++ if (err) { ++ v4l2_err(sd, "Write failed. Err[%d]\n", err); ++ return err; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * tvp514x_query_current_std() : Query the current standard detected by TVP5146/47 ++ * @sd: ptr to v4l2_subdev struct ++ * ++ * Returns the current standard detected by TVP5146/47, STD_INVALID if there is no ++ * standard detected. ++ */ ++static enum tvp514x_std tvp514x_query_current_std(struct v4l2_subdev *sd) ++{ ++ u8 std, std_status; ++ ++ std = tvp514x_read_reg(sd, REG_VIDEO_STD); ++ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) ++ /* use the standard status register */ ++ std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); ++ else ++ /* use the standard register itself */ ++ std_status = std; ++ ++ switch (std_status & VIDEO_STD_MASK) { ++ case VIDEO_STD_NTSC_MJ_BIT: ++ return STD_NTSC_MJ; ++ ++ case VIDEO_STD_PAL_BDGHIN_BIT: ++ return STD_PAL_BDGHIN; ++ ++ default: ++ return STD_INVALID; ++ } ++ ++ return STD_INVALID; ++} ++ ++/* TVP5146/47 register dump function */ ++static void tvp514x_reg_dump(struct v4l2_subdev *sd) ++{ ++ dump_reg(sd, REG_INPUT_SEL); ++ dump_reg(sd, REG_AFE_GAIN_CTRL); ++ dump_reg(sd, REG_VIDEO_STD); ++ dump_reg(sd, REG_OPERATION_MODE); ++ dump_reg(sd, REG_COLOR_KILLER); ++ dump_reg(sd, REG_LUMA_CONTROL1); ++ dump_reg(sd, REG_LUMA_CONTROL2); ++ dump_reg(sd, REG_LUMA_CONTROL3); ++ dump_reg(sd, REG_BRIGHTNESS); ++ dump_reg(sd, REG_CONTRAST); ++ dump_reg(sd, REG_SATURATION); ++ dump_reg(sd, REG_HUE); ++ dump_reg(sd, REG_CHROMA_CONTROL1); ++ dump_reg(sd, REG_CHROMA_CONTROL2); ++ dump_reg(sd, REG_COMP_PR_SATURATION); ++ dump_reg(sd, REG_COMP_Y_CONTRAST); ++ dump_reg(sd, REG_COMP_PB_SATURATION); ++ dump_reg(sd, REG_COMP_Y_BRIGHTNESS); ++ dump_reg(sd, REG_AVID_START_PIXEL_LSB); ++ dump_reg(sd, REG_AVID_START_PIXEL_MSB); ++ dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); ++ dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); ++ dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); ++ dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); ++ dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); ++ dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); ++ dump_reg(sd, REG_VSYNC_START_LINE_LSB); ++ dump_reg(sd, REG_VSYNC_START_LINE_MSB); ++ dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); ++ dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); ++ dump_reg(sd, REG_VBLK_START_LINE_LSB); ++ dump_reg(sd, REG_VBLK_START_LINE_MSB); ++ dump_reg(sd, REG_VBLK_STOP_LINE_LSB); ++ dump_reg(sd, REG_VBLK_STOP_LINE_MSB); ++ dump_reg(sd, REG_SYNC_CONTROL); ++ dump_reg(sd, REG_OUTPUT_FORMATTER1); ++ dump_reg(sd, REG_OUTPUT_FORMATTER2); ++ dump_reg(sd, REG_OUTPUT_FORMATTER3); ++ dump_reg(sd, REG_OUTPUT_FORMATTER4); ++ dump_reg(sd, REG_OUTPUT_FORMATTER5); ++ dump_reg(sd, REG_OUTPUT_FORMATTER6); ++ dump_reg(sd, REG_CLEAR_LOST_LOCK); ++} ++ ++/** ++ * tvp514x_configure() - Configure the TVP5146/47 registers ++ * @sd: ptr to v4l2_subdev struct ++ * @decoder: ptr to tvp514x_decoder structure ++ * ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tvp514x_configure(struct v4l2_subdev *sd, ++ struct tvp514x_decoder *decoder) ++{ ++ int err; ++ ++ /* common register initialization */ ++ err = ++ tvp514x_write_regs(sd, decoder->tvp514x_regs); ++ if (err) ++ return err; ++ ++ if (debug) ++ tvp514x_reg_dump(sd); ++ ++ return 0; ++} ++ ++/** ++ * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @decoder: pointer to tvp514x_decoder structure ++ * ++ * A device is considered to be detected if the chip ID (LSB and MSB) ++ * registers match the expected values. ++ * Any value of the rom version register is accepted. ++ * Returns ENODEV error number if no device is detected, or zero ++ * if a device is detected. ++ */ ++static int tvp514x_detect(struct v4l2_subdev *sd, ++ struct tvp514x_decoder *decoder) ++{ ++ u8 chip_id_msb, chip_id_lsb, rom_ver; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); ++ chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); ++ rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); ++ ++ v4l2_dbg(1, debug, sd, ++ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", ++ chip_id_msb, chip_id_lsb, rom_ver); ++ if ((chip_id_msb != TVP514X_CHIP_ID_MSB) ++ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB) ++ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) { ++ /* We didn't read the values we expected, so this must not be ++ * an TVP5146/47. ++ */ ++ v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", ++ chip_id_msb, chip_id_lsb); ++ return -ENODEV; ++ } ++ ++ decoder->ver = rom_ver; ++ ++ v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", ++ client->name, decoder->ver, ++ client->addr << 1, client->adapter->name); ++ return 0; ++} ++ ++/** ++ * tvp514x_querystd() - V4L2 decoder interface handler for querystd ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @std_id: standard V4L2 std_id ioctl enum ++ * ++ * Returns the current standard detected by TVP5146/47. If no active input is ++ * detected then *std_id is set to 0 and the function returns 0. ++ */ ++static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ enum tvp514x_std current_std; ++ enum tvp514x_input input_sel; ++ u8 sync_lock_status, lock_mask; ++ ++ if (std_id == NULL) ++ return -EINVAL; ++ ++ *std_id = V4L2_STD_UNKNOWN; ++ ++ /* To query the standard the TVP514x must power on the ADCs. */ ++ if (!decoder->streaming) { ++ tvp514x_s_stream(sd, 1); ++ msleep(LOCK_RETRY_DELAY); ++ } ++ ++ /* query the current standard */ ++ current_std = tvp514x_query_current_std(sd); ++ if (current_std == STD_INVALID) ++ return 0; ++ ++ input_sel = decoder->input; ++ ++ switch (input_sel) { ++ case INPUT_CVBS_VI1A: ++ case INPUT_CVBS_VI1B: ++ case INPUT_CVBS_VI1C: ++ case INPUT_CVBS_VI2A: ++ case INPUT_CVBS_VI2B: ++ case INPUT_CVBS_VI2C: ++ case INPUT_CVBS_VI3A: ++ case INPUT_CVBS_VI3B: ++ case INPUT_CVBS_VI3C: ++ case INPUT_CVBS_VI4A: ++ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT | ++ STATUS_HORZ_SYNC_LOCK_BIT | ++ STATUS_VIRT_SYNC_LOCK_BIT; ++ break; ++ ++ case INPUT_SVIDEO_VI2A_VI1A: ++ case INPUT_SVIDEO_VI2B_VI1B: ++ case INPUT_SVIDEO_VI2C_VI1C: ++ case INPUT_SVIDEO_VI2A_VI3A: ++ case INPUT_SVIDEO_VI2B_VI3B: ++ case INPUT_SVIDEO_VI2C_VI3C: ++ case INPUT_SVIDEO_VI4A_VI1A: ++ case INPUT_SVIDEO_VI4A_VI1B: ++ case INPUT_SVIDEO_VI4A_VI1C: ++ case INPUT_SVIDEO_VI4A_VI3A: ++ case INPUT_SVIDEO_VI4A_VI3B: ++ case INPUT_SVIDEO_VI4A_VI3C: ++ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | ++ STATUS_VIRT_SYNC_LOCK_BIT; ++ break; ++ /*Need to add other interfaces*/ ++ default: ++ return -EINVAL; ++ } ++ /* check whether signal is locked */ ++ sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); ++ if (lock_mask != (sync_lock_status & lock_mask)) ++ return 0; /* No input detected */ ++ ++ *std_id = decoder->std_list[current_std].standard.id; ++ ++ v4l2_dbg(1, debug, sd, "Current STD: %s\n", ++ decoder->std_list[current_std].standard.name); ++ return 0; ++} ++ ++/** ++ * tvp514x_s_std() - V4L2 decoder interface handler for s_std ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @std_id: standard V4L2 v4l2_std_id ioctl enum ++ * ++ * If std_id is supported, sets the requested standard. Otherwise, returns ++ * -EINVAL ++ */ ++static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ int err, i; ++ ++ for (i = 0; i < decoder->num_stds; i++) ++ if (std_id & decoder->std_list[i].standard.id) ++ break; ++ ++ if ((i == decoder->num_stds) || (i == STD_INVALID)) ++ return -EINVAL; ++ ++ err = tvp514x_write_reg(sd, REG_VIDEO_STD, ++ decoder->std_list[i].video_std); ++ if (err) ++ return err; ++ ++ decoder->current_std = i; ++ decoder->tvp514x_regs[REG_VIDEO_STD].val = ++ decoder->std_list[i].video_std; ++ ++ v4l2_dbg(1, debug, sd, "Standard set to: %s\n", ++ decoder->std_list[i].standard.name); ++ return 0; ++} ++ ++/** ++ * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @input: input selector for routing the signal ++ * @output: output selector for routing the signal ++ * @config: config value. Not used ++ * ++ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if ++ * the input is not supported or there is no active signal present in the ++ * selected input. ++ */ ++static int tvp514x_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ int err; ++ enum tvp514x_input input_sel; ++ enum tvp514x_output output_sel; ++ ++ if ((input >= INPUT_INVALID) || ++ (output >= OUTPUT_INVALID)) ++ /* Index out of bound */ ++ return -EINVAL; ++ ++ input_sel = input; ++ output_sel = output; ++ ++ err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); ++ if (err) ++ return err; ++ ++ output_sel |= tvp514x_read_reg(sd, ++ REG_OUTPUT_FORMATTER1) & 0x7; ++ err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, ++ output_sel); ++ if (err) ++ return err; ++ ++ decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; ++ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; ++ decoder->input = input; ++ decoder->output = output; ++ ++ v4l2_dbg(1, debug, sd, "Input set to: %d\n", input_sel); ++ ++ return 0; ++} ++ ++/** ++ * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl ++ * @ctrl: pointer to v4l2_ctrl structure ++ * ++ * If the requested control is supported, sets the control's current ++ * value in HW. Otherwise, returns -EINVAL if the control is not supported. ++ */ ++static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ int err = -EINVAL, value; ++ ++ value = ctrl->val; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); ++ if (!err) ++ decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; ++ break; ++ case V4L2_CID_CONTRAST: ++ err = tvp514x_write_reg(sd, REG_CONTRAST, value); ++ if (!err) ++ decoder->tvp514x_regs[REG_CONTRAST].val = value; ++ break; ++ case V4L2_CID_SATURATION: ++ err = tvp514x_write_reg(sd, REG_SATURATION, value); ++ if (!err) ++ decoder->tvp514x_regs[REG_SATURATION].val = value; ++ break; ++ case V4L2_CID_HUE: ++ if (value == 180) ++ value = 0x7F; ++ else if (value == -180) ++ value = 0x80; ++ err = tvp514x_write_reg(sd, REG_HUE, value); ++ if (!err) ++ decoder->tvp514x_regs[REG_HUE].val = value; ++ break; ++ case V4L2_CID_AUTOGAIN: ++ err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value ? 0x0f : 0x0c); ++ if (!err) ++ decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; ++ break; ++ } ++ ++ v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d\n", ++ ctrl->id, ctrl->val); ++ return err; ++} ++ ++/** ++ * tvp514x_enum_mbus_fmt() - V4L2 decoder interface handler for enum_mbus_fmt ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @index: index of pixelcode to retrieve ++ * @code: receives the pixelcode ++ * ++ * Enumerates supported mediabus formats ++ */ ++static int ++tvp514x_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_YUYV10_2X10; ++ return 0; ++} ++ ++/** ++ * tvp514x_mbus_fmt_cap() - V4L2 decoder interface handler for try/s/g_mbus_fmt ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @f: pointer to the mediabus format structure ++ * ++ * Negotiates the image capture size and mediabus format. ++ */ ++static int ++tvp514x_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ enum tvp514x_std current_std; ++ ++ if (f == NULL) ++ return -EINVAL; ++ ++ /* Calculate height and width based on current standard */ ++ current_std = decoder->current_std; ++ ++ f->code = V4L2_MBUS_FMT_YUYV10_2X10; ++ f->width = decoder->std_list[current_std].width; ++ f->height = decoder->std_list[current_std].height; ++ f->field = V4L2_FIELD_INTERLACED; ++ f->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d\n", ++ f->width, f->height); ++ return 0; ++} ++ ++/** ++ * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure ++ * ++ * Returns the decoder's video CAPTURE parameters. ++ */ ++static int ++tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ struct v4l2_captureparm *cparm; ++ enum tvp514x_std current_std; ++ ++ if (a == NULL) ++ return -EINVAL; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ /* only capture is supported */ ++ return -EINVAL; ++ ++ /* get the current standard */ ++ current_std = decoder->current_std; ++ ++ cparm = &a->parm.capture; ++ cparm->capability = V4L2_CAP_TIMEPERFRAME; ++ cparm->timeperframe = ++ decoder->std_list[current_std].standard.frameperiod; ++ ++ return 0; ++} ++ ++/** ++ * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure ++ * ++ * Configures the decoder to use the input parameters, if possible. If ++ * not possible, returns the appropriate error code. ++ */ ++static int ++tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) ++{ ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ struct v4l2_fract *timeperframe; ++ enum tvp514x_std current_std; ++ ++ if (a == NULL) ++ return -EINVAL; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ /* only capture is supported */ ++ return -EINVAL; ++ ++ timeperframe = &a->parm.capture.timeperframe; ++ ++ /* get the current standard */ ++ current_std = decoder->current_std; ++ ++ *timeperframe = ++ decoder->std_list[current_std].standard.frameperiod; ++ ++ return 0; ++} ++ ++/** ++ * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @enable: streaming enable or disable ++ * ++ * Sets streaming to enable or disable, if possible. ++ */ ++static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ int err = 0; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ ++ if (decoder->streaming == enable) ++ return 0; ++ ++ switch (enable) { ++ case 0: ++ { ++ /* Power Down Sequence */ ++ err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); ++ if (err) { ++ v4l2_err(sd, "Unable to turn off decoder\n"); ++ return err; ++ } ++ decoder->streaming = enable; ++ break; ++ } ++ case 1: ++ { ++ struct tvp514x_reg *int_seq = (struct tvp514x_reg *) ++ client->driver->id_table->driver_data; ++ ++ /* Power Up Sequence */ ++ err = tvp514x_write_regs(sd, int_seq); ++ if (err) { ++ v4l2_err(sd, "Unable to turn on decoder\n"); ++ return err; ++ } ++ /* Detect if not already detected */ ++ err = tvp514x_detect(sd, decoder); ++ if (err) { ++ v4l2_err(sd, "Unable to detect decoder\n"); ++ return err; ++ } ++ err = tvp514x_configure(sd, decoder); ++ if (err) { ++ v4l2_err(sd, "Unable to configure decoder\n"); ++ return err; ++ } ++ decoder->streaming = enable; ++ break; ++ } ++ default: ++ err = -ENODEV; ++ break; ++ } ++ ++ return err; ++} ++ ++static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = { ++ .s_ctrl = tvp514x_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops tvp514x_core_ops = { ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = tvp514x_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops tvp514x_video_ops = { ++ .s_routing = tvp514x_s_routing, ++ .querystd = tvp514x_querystd, ++ .enum_mbus_fmt = tvp514x_enum_mbus_fmt, ++ .g_mbus_fmt = tvp514x_mbus_fmt, ++ .try_mbus_fmt = tvp514x_mbus_fmt, ++ .s_mbus_fmt = tvp514x_mbus_fmt, ++ .g_parm = tvp514x_g_parm, ++ .s_parm = tvp514x_s_parm, ++ .s_stream = tvp514x_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops tvp514x_ops = { ++ .core = &tvp514x_core_ops, ++ .video = &tvp514x_video_ops, ++}; ++ ++static struct tvp514x_decoder tvp514x_dev = { ++ .streaming = 0, ++ .current_std = STD_NTSC_MJ, ++ .std_list = tvp514x_std_list, ++ .num_stds = ARRAY_SIZE(tvp514x_std_list), ++ ++}; ++ ++/** ++ * tvp514x_probe() - decoder driver i2c probe handler ++ * @client: i2c driver client device structure ++ * @id: i2c driver id table ++ * ++ * Register decoder as an i2c client device and V4L2 ++ * device. ++ */ ++static int ++tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ struct tvp514x_decoder *decoder; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ if (!client->dev.platform_data) { ++ v4l2_err(client, "No platform data!!\n"); ++ return -ENODEV; ++ } ++ ++ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); ++ if (!decoder) ++ return -ENOMEM; ++ ++ /* Initialize the tvp514x_decoder with default configuration */ ++ *decoder = tvp514x_dev; ++ /* Copy default register configuration */ ++ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, ++ sizeof(tvp514x_reg_list_default)); ++ ++ /* Copy board specific information here */ ++ decoder->pdata = client->dev.platform_data; ++ ++ /** ++ * Fetch platform specific data, and configure the ++ * tvp514x_reg_list[] accordingly. Since this is one ++ * time configuration, no need to preserve. ++ */ ++ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= ++ (decoder->pdata->clk_polarity << 1); ++ decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= ++ ((decoder->pdata->hs_polarity << 2) | ++ (decoder->pdata->vs_polarity << 3)); ++ /* Set default standard to auto */ ++ decoder->tvp514x_regs[REG_VIDEO_STD].val = ++ VIDEO_STD_AUTO_SWITCH_BIT; ++ ++ /* Register with V4L2 layer as slave device */ ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); ++ ++ v4l2_ctrl_handler_init(&decoder->hdl, 5); ++ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, ++ V4L2_CID_HUE, -180, 180, 180, 0); ++ v4l2_ctrl_new_std(&decoder->hdl, &tvp514x_ctrl_ops, ++ V4L2_CID_AUTOGAIN, 0, 1, 1, 1); ++ sd->ctrl_handler = &decoder->hdl; ++ if (decoder->hdl.error) { ++ int err = decoder->hdl.error; ++ ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&decoder->hdl); ++ ++ v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); ++ ++ return 0; ++ ++} ++ ++/** ++ * tvp514x_remove() - decoder driver i2c remove handler ++ * @client: i2c driver client device structure ++ * ++ * Unregister decoder as an i2c client device and V4L2 ++ * device. Complement of tvp514x_probe(). ++ */ ++static int tvp514x_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct tvp514x_decoder *decoder = to_decoder(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return 0; ++} ++/* TVP5146 Init/Power on Sequence */ ++static const struct tvp514x_reg tvp5146_init_reg_seq[] = { ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x01}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x00}, ++ {TOK_TERM, 0, 0}, ++}; ++ ++/* TVP5147 Init/Power on Sequence */ ++static const struct tvp514x_reg tvp5147_init_reg_seq[] = { ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, ++ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0}, ++ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x01}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x00}, ++ {TOK_TERM, 0, 0}, ++}; ++ ++/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ ++static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { ++ {TOK_WRITE, REG_OPERATION_MODE, 0x01}, ++ {TOK_WRITE, REG_OPERATION_MODE, 0x00}, ++ {TOK_TERM, 0, 0}, ++}; ++ ++/** ++ * I2C Device Table - ++ * ++ * name - Name of the actual device/chip. ++ * driver_data - Driver data ++ */ ++static const struct i2c_device_id tvp514x_id[] = { ++ {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, ++ {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, ++ {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, ++ {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, tvp514x_id); ++ ++static struct i2c_driver tvp514x_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = TVP514X_MODULE_NAME, ++ }, ++ .probe = tvp514x_probe, ++ .remove = tvp514x_remove, ++ .id_table = tvp514x_id, ++}; ++ ++module_i2c_driver(tvp514x_driver); +diff --git a/drivers/media/i2c/tvp514x_regs.h b/drivers/media/i2c/tvp514x_regs.h +new file mode 100644 +index 0000000..d23aa2f +--- /dev/null ++++ b/drivers/media/i2c/tvp514x_regs.h +@@ -0,0 +1,287 @@ ++/* ++ * drivers/media/i2c/tvp514x_regs.h ++ * ++ * Copyright (C) 2008 Texas Instruments Inc ++ * Author: Vaibhav Hiremath ++ * ++ * Contributors: ++ * Sivaraj R ++ * Brijesh R Jadav ++ * Hardik Shah ++ * Manjunath Hadli ++ * Karicheri Muralidharan ++ * ++ * This package is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef _TVP514X_REGS_H ++#define _TVP514X_REGS_H ++ ++/* ++ * TVP5146/47 registers ++ */ ++#define REG_INPUT_SEL (0x00) ++#define REG_AFE_GAIN_CTRL (0x01) ++#define REG_VIDEO_STD (0x02) ++#define REG_OPERATION_MODE (0x03) ++#define REG_AUTOSWITCH_MASK (0x04) ++ ++#define REG_COLOR_KILLER (0x05) ++#define REG_LUMA_CONTROL1 (0x06) ++#define REG_LUMA_CONTROL2 (0x07) ++#define REG_LUMA_CONTROL3 (0x08) ++ ++#define REG_BRIGHTNESS (0x09) ++#define REG_CONTRAST (0x0A) ++#define REG_SATURATION (0x0B) ++#define REG_HUE (0x0C) ++ ++#define REG_CHROMA_CONTROL1 (0x0D) ++#define REG_CHROMA_CONTROL2 (0x0E) ++ ++/* 0x0F Reserved */ ++ ++#define REG_COMP_PR_SATURATION (0x10) ++#define REG_COMP_Y_CONTRAST (0x11) ++#define REG_COMP_PB_SATURATION (0x12) ++ ++/* 0x13 Reserved */ ++ ++#define REG_COMP_Y_BRIGHTNESS (0x14) ++ ++/* 0x15 Reserved */ ++ ++#define REG_AVID_START_PIXEL_LSB (0x16) ++#define REG_AVID_START_PIXEL_MSB (0x17) ++#define REG_AVID_STOP_PIXEL_LSB (0x18) ++#define REG_AVID_STOP_PIXEL_MSB (0x19) ++ ++#define REG_HSYNC_START_PIXEL_LSB (0x1A) ++#define REG_HSYNC_START_PIXEL_MSB (0x1B) ++#define REG_HSYNC_STOP_PIXEL_LSB (0x1C) ++#define REG_HSYNC_STOP_PIXEL_MSB (0x1D) ++ ++#define REG_VSYNC_START_LINE_LSB (0x1E) ++#define REG_VSYNC_START_LINE_MSB (0x1F) ++#define REG_VSYNC_STOP_LINE_LSB (0x20) ++#define REG_VSYNC_STOP_LINE_MSB (0x21) ++ ++#define REG_VBLK_START_LINE_LSB (0x22) ++#define REG_VBLK_START_LINE_MSB (0x23) ++#define REG_VBLK_STOP_LINE_LSB (0x24) ++#define REG_VBLK_STOP_LINE_MSB (0x25) ++ ++/* 0x26 - 0x27 Reserved */ ++ ++#define REG_FAST_SWTICH_CONTROL (0x28) ++ ++/* 0x29 Reserved */ ++ ++#define REG_FAST_SWTICH_SCART_DELAY (0x2A) ++ ++/* 0x2B Reserved */ ++ ++#define REG_SCART_DELAY (0x2C) ++#define REG_CTI_DELAY (0x2D) ++#define REG_CTI_CONTROL (0x2E) ++ ++/* 0x2F - 0x31 Reserved */ ++ ++#define REG_SYNC_CONTROL (0x32) ++#define REG_OUTPUT_FORMATTER1 (0x33) ++#define REG_OUTPUT_FORMATTER2 (0x34) ++#define REG_OUTPUT_FORMATTER3 (0x35) ++#define REG_OUTPUT_FORMATTER4 (0x36) ++#define REG_OUTPUT_FORMATTER5 (0x37) ++#define REG_OUTPUT_FORMATTER6 (0x38) ++#define REG_CLEAR_LOST_LOCK (0x39) ++ ++#define REG_STATUS1 (0x3A) ++#define REG_STATUS2 (0x3B) ++ ++#define REG_AGC_GAIN_STATUS_LSB (0x3C) ++#define REG_AGC_GAIN_STATUS_MSB (0x3D) ++ ++/* 0x3E Reserved */ ++ ++#define REG_VIDEO_STD_STATUS (0x3F) ++#define REG_GPIO_INPUT1 (0x40) ++#define REG_GPIO_INPUT2 (0x41) ++ ++/* 0x42 - 0x45 Reserved */ ++ ++#define REG_AFE_COARSE_GAIN_CH1 (0x46) ++#define REG_AFE_COARSE_GAIN_CH2 (0x47) ++#define REG_AFE_COARSE_GAIN_CH3 (0x48) ++#define REG_AFE_COARSE_GAIN_CH4 (0x49) ++ ++#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A) ++#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B) ++#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C) ++#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D) ++#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E) ++#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F) ++#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50) ++#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51) ++ ++/* 0x52 - 0x68 Reserved */ ++ ++#define REG_FBIT_VBIT_CONTROL1 (0x69) ++ ++/* 0x6A - 0x6B Reserved */ ++ ++#define REG_BACKEND_AGC_CONTROL (0x6C) ++ ++/* 0x6D - 0x6E Reserved */ ++ ++#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F) ++#define REG_ROM_VERSION (0x70) ++ ++/* 0x71 - 0x73 Reserved */ ++ ++#define REG_AGC_WHITE_PEAK_PROCESSING (0x74) ++#define REG_FBIT_VBIT_CONTROL2 (0x75) ++#define REG_VCR_TRICK_MODE_CONTROL (0x76) ++#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77) ++#define REG_AGC_INCREMENT_SPEED (0x78) ++#define REG_AGC_INCREMENT_DELAY (0x79) ++ ++/* 0x7A - 0x7F Reserved */ ++ ++#define REG_CHIP_ID_MSB (0x80) ++#define REG_CHIP_ID_LSB (0x81) ++ ++/* 0x82 Reserved */ ++ ++#define REG_CPLL_SPEED_CONTROL (0x83) ++ ++/* 0x84 - 0x96 Reserved */ ++ ++#define REG_STATUS_REQUEST (0x97) ++ ++/* 0x98 - 0x99 Reserved */ ++ ++#define REG_VERTICAL_LINE_COUNT_LSB (0x9A) ++#define REG_VERTICAL_LINE_COUNT_MSB (0x9B) ++ ++/* 0x9C - 0x9D Reserved */ ++ ++#define REG_AGC_DECREMENT_DELAY (0x9E) ++ ++/* 0x9F - 0xB0 Reserved */ ++ ++#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1) ++#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2) ++#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3) ++#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4) ++#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5) ++#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6) ++#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7) ++#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8) ++#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9) ++#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA) ++#define REG_VDP_TTX_FILTER_CONTROL (0xBB) ++#define REG_VDP_FIFO_WORD_COUNT (0xBC) ++#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD) ++ ++/* 0xBE Reserved */ ++ ++#define REG_VDP_FIFO_RESET (0xBF) ++#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0) ++#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1) ++#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2) ++#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3) ++ ++/* 0xC4 - 0xD5 Reserved */ ++ ++#define REG_VDP_LINE_START (0xD6) ++#define REG_VDP_LINE_STOP (0xD7) ++#define REG_VDP_GLOBAL_LINE_MODE (0xD8) ++#define REG_VDP_FULL_FIELD_ENABLE (0xD9) ++#define REG_VDP_FULL_FIELD_MODE (0xDA) ++ ++/* 0xDB - 0xDF Reserved */ ++ ++#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0) ++#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1) ++#define REG_FIFO_READ_DATA (0xE2) ++ ++/* 0xE3 - 0xE7 Reserved */ ++ ++#define REG_VBUS_ADDRESS_ACCESS1 (0xE8) ++#define REG_VBUS_ADDRESS_ACCESS2 (0xE9) ++#define REG_VBUS_ADDRESS_ACCESS3 (0xEA) ++ ++/* 0xEB - 0xEF Reserved */ ++ ++#define REG_INTERRUPT_RAW_STATUS0 (0xF0) ++#define REG_INTERRUPT_RAW_STATUS1 (0xF1) ++#define REG_INTERRUPT_STATUS0 (0xF2) ++#define REG_INTERRUPT_STATUS1 (0xF3) ++#define REG_INTERRUPT_MASK0 (0xF4) ++#define REG_INTERRUPT_MASK1 (0xF5) ++#define REG_INTERRUPT_CLEAR0 (0xF6) ++#define REG_INTERRUPT_CLEAR1 (0xF7) ++ ++/* 0xF8 - 0xFF Reserved */ ++ ++/* ++ * Mask and bit definitions of TVP5146/47 registers ++ */ ++/* The ID values we are looking for */ ++#define TVP514X_CHIP_ID_MSB (0x51) ++#define TVP5146_CHIP_ID_LSB (0x46) ++#define TVP5147_CHIP_ID_LSB (0x47) ++ ++#define VIDEO_STD_MASK (0x07) ++#define VIDEO_STD_AUTO_SWITCH_BIT (0x00) ++#define VIDEO_STD_NTSC_MJ_BIT (0x01) ++#define VIDEO_STD_PAL_BDGHIN_BIT (0x02) ++#define VIDEO_STD_PAL_M_BIT (0x03) ++#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04) ++#define VIDEO_STD_NTSC_4_43_BIT (0x05) ++#define VIDEO_STD_SECAM_BIT (0x06) ++#define VIDEO_STD_PAL_60_BIT (0x07) ++ ++/* ++ * Status bit ++ */ ++#define STATUS_TV_VCR_BIT (1<<0) ++#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1) ++#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2) ++#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3) ++#define STATUS_LOST_LOCK_DETECT_BIT (1<<4) ++#define STATUS_FEILD_RATE_BIT (1<<5) ++#define STATUS_LINE_ALTERNATING_BIT (1<<6) ++#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7) ++ ++/* Tokens for register write */ ++#define TOK_WRITE (0) /* token for write operation */ ++#define TOK_TERM (1) /* terminating token */ ++#define TOK_DELAY (2) /* delay token for reg list */ ++#define TOK_SKIP (3) /* token to skip a register */ ++/** ++ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values ++ * @token - Token: TOK_WRITE, TOK_TERM etc.. ++ * @reg - Register offset ++ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY ++ */ ++struct tvp514x_reg { ++ u8 token; ++ u8 reg; ++ u32 val; ++}; ++ ++#endif /* ifndef _TVP514X_REGS_H */ +diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c +new file mode 100644 +index 0000000..5967e1a +--- /dev/null ++++ b/drivers/media/i2c/tvp5150.c +@@ -0,0 +1,1267 @@ ++/* ++ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder driver ++ * ++ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) ++ * This code is placed under the terms of the GNU General Public License v2 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tvp5150_reg.h" ++ ++#define TVP5150_H_MAX 720 ++#define TVP5150_V_MAX_525_60 480 ++#define TVP5150_V_MAX_OTHERS 576 ++#define TVP5150_MAX_CROP_LEFT 511 ++#define TVP5150_MAX_CROP_TOP 127 ++#define TVP5150_CROP_SHIFT 2 ++ ++MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); ++MODULE_AUTHOR("Mauro Carvalho Chehab"); ++MODULE_LICENSE("GPL"); ++ ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-2)"); ++ ++struct tvp5150 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct v4l2_rect rect; ++ ++ v4l2_std_id norm; /* Current set standard */ ++ u32 input; ++ u32 output; ++ int enable; ++}; ++ ++static inline struct tvp5150 *to_tvp5150(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct tvp5150, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct tvp5150, hdl)->sd; ++} ++ ++static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char buffer[1]; ++ int rc; ++ ++ buffer[0] = addr; ++ ++ rc = i2c_master_send(c, buffer, 1); ++ if (rc < 0) { ++ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); ++ return rc; ++ } ++ ++ msleep(10); ++ ++ rc = i2c_master_recv(c, buffer, 1); ++ if (rc < 0) { ++ v4l2_err(sd, "i2c i/o error: rc == %d (should be 1)\n", rc); ++ return rc; ++ } ++ ++ v4l2_dbg(2, debug, sd, "tvp5150: read 0x%02x = 0x%02x\n", addr, buffer[0]); ++ ++ return (buffer[0]); ++} ++ ++static inline void tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, ++ unsigned char value) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ unsigned char buffer[2]; ++ int rc; ++ ++ buffer[0] = addr; ++ buffer[1] = value; ++ v4l2_dbg(2, debug, sd, "tvp5150: writing 0x%02x 0x%02x\n", buffer[0], buffer[1]); ++ if (2 != (rc = i2c_master_send(c, buffer, 2))) ++ v4l2_dbg(0, debug, sd, "i2c i/o error: rc == %d (should be 2)\n", rc); ++} ++ ++static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, ++ const u8 end, int max_line) ++{ ++ int i = 0; ++ ++ while (init != (u8)(end + 1)) { ++ if ((i % max_line) == 0) { ++ if (i > 0) ++ printk("\n"); ++ printk("tvp5150: %s reg 0x%02x = ", s, init); ++ } ++ printk("%02x ", tvp5150_read(sd, init)); ++ ++ init++; ++ i++; ++ } ++ printk("\n"); ++} ++ ++static int tvp5150_log_status(struct v4l2_subdev *sd) ++{ ++ printk("tvp5150: Video input source selection #1 = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_VD_IN_SRC_SEL_1)); ++ printk("tvp5150: Analog channel controls = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_ANAL_CHL_CTL)); ++ printk("tvp5150: Operation mode controls = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_OP_MODE_CTL)); ++ printk("tvp5150: Miscellaneous controls = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_MISC_CTL)); ++ printk("tvp5150: Autoswitch mask= 0x%02x\n", ++ tvp5150_read(sd, TVP5150_AUTOSW_MSK)); ++ printk("tvp5150: Color killer threshold control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_COLOR_KIL_THSH_CTL)); ++ printk("tvp5150: Luminance processing controls #1 #2 and #3 = %02x %02x %02x\n", ++ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_1), ++ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_2), ++ tvp5150_read(sd, TVP5150_LUMA_PROC_CTL_3)); ++ printk("tvp5150: Brightness control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_BRIGHT_CTL)); ++ printk("tvp5150: Color saturation control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_SATURATION_CTL)); ++ printk("tvp5150: Hue control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_HUE_CTL)); ++ printk("tvp5150: Contrast control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_CONTRAST_CTL)); ++ printk("tvp5150: Outputs and data rates select = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_DATA_RATE_SEL)); ++ printk("tvp5150: Configuration shared pins = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_CONF_SHARED_PIN)); ++ printk("tvp5150: Active video cropping start = 0x%02x%02x\n", ++ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_MSB), ++ tvp5150_read(sd, TVP5150_ACT_VD_CROP_ST_LSB)); ++ printk("tvp5150: Active video cropping stop = 0x%02x%02x\n", ++ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_MSB), ++ tvp5150_read(sd, TVP5150_ACT_VD_CROP_STP_LSB)); ++ printk("tvp5150: Genlock/RTC = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_GENLOCK)); ++ printk("tvp5150: Horizontal sync start = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_HORIZ_SYNC_START)); ++ printk("tvp5150: Vertical blanking start = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_VERT_BLANKING_START)); ++ printk("tvp5150: Vertical blanking stop = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_VERT_BLANKING_STOP)); ++ printk("tvp5150: Chrominance processing control #1 and #2 = %02x %02x\n", ++ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_1), ++ tvp5150_read(sd, TVP5150_CHROMA_PROC_CTL_2)); ++ printk("tvp5150: Interrupt reset register B = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_RESET_REG_B)); ++ printk("tvp5150: Interrupt enable register B = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_B)); ++ printk("tvp5150: Interrupt configuration register B = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INTT_CONFIG_REG_B)); ++ printk("tvp5150: Video standard = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_VIDEO_STD)); ++ printk("tvp5150: Chroma gain factor: Cb=0x%02x Cr=0x%02x\n", ++ tvp5150_read(sd, TVP5150_CB_GAIN_FACT), ++ tvp5150_read(sd, TVP5150_CR_GAIN_FACTOR)); ++ printk("tvp5150: Macrovision on counter = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_MACROVISION_ON_CTR)); ++ printk("tvp5150: Macrovision off counter = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_MACROVISION_OFF_CTR)); ++ printk("tvp5150: ITU-R BT.656.%d timing(TVP5150AM1 only)\n", ++ (tvp5150_read(sd, TVP5150_REV_SELECT) & 1) ? 3 : 4); ++ printk("tvp5150: Device ID = %02x%02x\n", ++ tvp5150_read(sd, TVP5150_MSB_DEV_ID), ++ tvp5150_read(sd, TVP5150_LSB_DEV_ID)); ++ printk("tvp5150: ROM version = (hex) %02x.%02x\n", ++ tvp5150_read(sd, TVP5150_ROM_MAJOR_VER), ++ tvp5150_read(sd, TVP5150_ROM_MINOR_VER)); ++ printk("tvp5150: Vertical line count = 0x%02x%02x\n", ++ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_MSB), ++ tvp5150_read(sd, TVP5150_VERT_LN_COUNT_LSB)); ++ printk("tvp5150: Interrupt status register B = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_STATUS_REG_B)); ++ printk("tvp5150: Interrupt active register B = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_ACTIVE_REG_B)); ++ printk("tvp5150: Status regs #1 to #5 = %02x %02x %02x %02x %02x\n", ++ tvp5150_read(sd, TVP5150_STATUS_REG_1), ++ tvp5150_read(sd, TVP5150_STATUS_REG_2), ++ tvp5150_read(sd, TVP5150_STATUS_REG_3), ++ tvp5150_read(sd, TVP5150_STATUS_REG_4), ++ tvp5150_read(sd, TVP5150_STATUS_REG_5)); ++ ++ dump_reg_range(sd, "Teletext filter 1", TVP5150_TELETEXT_FIL1_INI, ++ TVP5150_TELETEXT_FIL1_END, 8); ++ dump_reg_range(sd, "Teletext filter 2", TVP5150_TELETEXT_FIL2_INI, ++ TVP5150_TELETEXT_FIL2_END, 8); ++ ++ printk("tvp5150: Teletext filter enable = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_TELETEXT_FIL_ENA)); ++ printk("tvp5150: Interrupt status register A = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_STATUS_REG_A)); ++ printk("tvp5150: Interrupt enable register A = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_ENABLE_REG_A)); ++ printk("tvp5150: Interrupt configuration = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_INT_CONF)); ++ printk("tvp5150: VDP status register = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_VDP_STATUS_REG)); ++ printk("tvp5150: FIFO word count = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FIFO_WORD_COUNT)); ++ printk("tvp5150: FIFO interrupt threshold = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FIFO_INT_THRESHOLD)); ++ printk("tvp5150: FIFO reset = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FIFO_RESET)); ++ printk("tvp5150: Line number interrupt = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_LINE_NUMBER_INT)); ++ printk("tvp5150: Pixel alignment register = 0x%02x%02x\n", ++ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_HIGH), ++ tvp5150_read(sd, TVP5150_PIX_ALIGN_REG_LOW)); ++ printk("tvp5150: FIFO output control = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FIFO_OUT_CTRL)); ++ printk("tvp5150: Full field enable = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FULL_FIELD_ENA)); ++ printk("tvp5150: Full field mode register = 0x%02x\n", ++ tvp5150_read(sd, TVP5150_FULL_FIELD_MODE_REG)); ++ ++ dump_reg_range(sd, "CC data", TVP5150_CC_DATA_INI, ++ TVP5150_CC_DATA_END, 8); ++ ++ dump_reg_range(sd, "WSS data", TVP5150_WSS_DATA_INI, ++ TVP5150_WSS_DATA_END, 8); ++ ++ dump_reg_range(sd, "VPS data", TVP5150_VPS_DATA_INI, ++ TVP5150_VPS_DATA_END, 8); ++ ++ dump_reg_range(sd, "VITC data", TVP5150_VITC_DATA_INI, ++ TVP5150_VITC_DATA_END, 10); ++ ++ dump_reg_range(sd, "Line mode", TVP5150_LINE_MODE_INI, ++ TVP5150_LINE_MODE_END, 8); ++ return 0; ++} ++ ++/**************************************************************************** ++ Basic functions ++ ****************************************************************************/ ++ ++static inline void tvp5150_selmux(struct v4l2_subdev *sd) ++{ ++ int opmode = 0; ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ int input = 0; ++ int val; ++ ++ if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) ++ input = 8; ++ ++ switch (decoder->input) { ++ case TVP5150_COMPOSITE1: ++ input |= 2; ++ /* fall through */ ++ case TVP5150_COMPOSITE0: ++ break; ++ case TVP5150_SVIDEO: ++ default: ++ input |= 1; ++ break; ++ } ++ ++ v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " ++ "=> tvp5150 input=%i, opmode=%i\n", ++ decoder->input, decoder->output, ++ input, opmode); ++ ++ tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); ++ tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); ++ ++ /* Svideo should enable YCrCb output and disable GPCL output ++ * For Composite and TV, it should be the reverse ++ */ ++ val = tvp5150_read(sd, TVP5150_MISC_CTL); ++ if (val < 0) { ++ v4l2_err(sd, "%s: failed with error = %d\n", __func__, val); ++ return; ++ } ++ ++ if (decoder->input == TVP5150_SVIDEO) ++ val = (val & ~0x40) | 0x10; ++ else ++ val = (val & ~0x10) | 0x40; ++ tvp5150_write(sd, TVP5150_MISC_CTL, val); ++}; ++ ++struct i2c_reg_value { ++ unsigned char reg; ++ unsigned char value; ++}; ++ ++/* Default values as sugested at TVP5150AM1 datasheet */ ++static const struct i2c_reg_value tvp5150_init_default[] = { ++ { /* 0x00 */ ++ TVP5150_VD_IN_SRC_SEL_1,0x00 ++ }, ++ { /* 0x01 */ ++ TVP5150_ANAL_CHL_CTL,0x15 ++ }, ++ { /* 0x02 */ ++ TVP5150_OP_MODE_CTL,0x00 ++ }, ++ { /* 0x03 */ ++ TVP5150_MISC_CTL,0x01 ++ }, ++ { /* 0x06 */ ++ TVP5150_COLOR_KIL_THSH_CTL,0x10 ++ }, ++ { /* 0x07 */ ++ TVP5150_LUMA_PROC_CTL_1,0x60 ++ }, ++ { /* 0x08 */ ++ TVP5150_LUMA_PROC_CTL_2,0x00 ++ }, ++ { /* 0x09 */ ++ TVP5150_BRIGHT_CTL,0x80 ++ }, ++ { /* 0x0a */ ++ TVP5150_SATURATION_CTL,0x80 ++ }, ++ { /* 0x0b */ ++ TVP5150_HUE_CTL,0x00 ++ }, ++ { /* 0x0c */ ++ TVP5150_CONTRAST_CTL,0x80 ++ }, ++ { /* 0x0d */ ++ TVP5150_DATA_RATE_SEL,0x47 ++ }, ++ { /* 0x0e */ ++ TVP5150_LUMA_PROC_CTL_3,0x00 ++ }, ++ { /* 0x0f */ ++ TVP5150_CONF_SHARED_PIN,0x08 ++ }, ++ { /* 0x11 */ ++ TVP5150_ACT_VD_CROP_ST_MSB,0x00 ++ }, ++ { /* 0x12 */ ++ TVP5150_ACT_VD_CROP_ST_LSB,0x00 ++ }, ++ { /* 0x13 */ ++ TVP5150_ACT_VD_CROP_STP_MSB,0x00 ++ }, ++ { /* 0x14 */ ++ TVP5150_ACT_VD_CROP_STP_LSB,0x00 ++ }, ++ { /* 0x15 */ ++ TVP5150_GENLOCK,0x01 ++ }, ++ { /* 0x16 */ ++ TVP5150_HORIZ_SYNC_START,0x80 ++ }, ++ { /* 0x18 */ ++ TVP5150_VERT_BLANKING_START,0x00 ++ }, ++ { /* 0x19 */ ++ TVP5150_VERT_BLANKING_STOP,0x00 ++ }, ++ { /* 0x1a */ ++ TVP5150_CHROMA_PROC_CTL_1,0x0c ++ }, ++ { /* 0x1b */ ++ TVP5150_CHROMA_PROC_CTL_2,0x14 ++ }, ++ { /* 0x1c */ ++ TVP5150_INT_RESET_REG_B,0x00 ++ }, ++ { /* 0x1d */ ++ TVP5150_INT_ENABLE_REG_B,0x00 ++ }, ++ { /* 0x1e */ ++ TVP5150_INTT_CONFIG_REG_B,0x00 ++ }, ++ { /* 0x28 */ ++ TVP5150_VIDEO_STD,0x00 ++ }, ++ { /* 0x2e */ ++ TVP5150_MACROVISION_ON_CTR,0x0f ++ }, ++ { /* 0x2f */ ++ TVP5150_MACROVISION_OFF_CTR,0x01 ++ }, ++ { /* 0xbb */ ++ TVP5150_TELETEXT_FIL_ENA,0x00 ++ }, ++ { /* 0xc0 */ ++ TVP5150_INT_STATUS_REG_A,0x00 ++ }, ++ { /* 0xc1 */ ++ TVP5150_INT_ENABLE_REG_A,0x00 ++ }, ++ { /* 0xc2 */ ++ TVP5150_INT_CONF,0x04 ++ }, ++ { /* 0xc8 */ ++ TVP5150_FIFO_INT_THRESHOLD,0x80 ++ }, ++ { /* 0xc9 */ ++ TVP5150_FIFO_RESET,0x00 ++ }, ++ { /* 0xca */ ++ TVP5150_LINE_NUMBER_INT,0x00 ++ }, ++ { /* 0xcb */ ++ TVP5150_PIX_ALIGN_REG_LOW,0x4e ++ }, ++ { /* 0xcc */ ++ TVP5150_PIX_ALIGN_REG_HIGH,0x00 ++ }, ++ { /* 0xcd */ ++ TVP5150_FIFO_OUT_CTRL,0x01 ++ }, ++ { /* 0xcf */ ++ TVP5150_FULL_FIELD_ENA,0x00 ++ }, ++ { /* 0xd0 */ ++ TVP5150_LINE_MODE_INI,0x00 ++ }, ++ { /* 0xfc */ ++ TVP5150_FULL_FIELD_MODE_REG,0x7f ++ }, ++ { /* end of data */ ++ 0xff,0xff ++ } ++}; ++ ++/* Default values as sugested at TVP5150AM1 datasheet */ ++static const struct i2c_reg_value tvp5150_init_enable[] = { ++ { ++ TVP5150_CONF_SHARED_PIN, 2 ++ },{ /* Automatic offset and AGC enabled */ ++ TVP5150_ANAL_CHL_CTL, 0x15 ++ },{ /* Activate YCrCb output 0x9 or 0xd ? */ ++ TVP5150_MISC_CTL, 0x6f ++ },{ /* Activates video std autodetection for all standards */ ++ TVP5150_AUTOSW_MSK, 0x0 ++ },{ /* Default format: 0x47. For 4:2:2: 0x40 */ ++ TVP5150_DATA_RATE_SEL, 0x47 ++ },{ ++ TVP5150_CHROMA_PROC_CTL_1, 0x0c ++ },{ ++ TVP5150_CHROMA_PROC_CTL_2, 0x54 ++ },{ /* Non documented, but initialized on WinTV USB2 */ ++ 0x27, 0x20 ++ },{ ++ 0xff,0xff ++ } ++}; ++ ++struct tvp5150_vbi_type { ++ unsigned int vbi_type; ++ unsigned int ini_line; ++ unsigned int end_line; ++ unsigned int by_field :1; ++}; ++ ++struct i2c_vbi_ram_value { ++ u16 reg; ++ struct tvp5150_vbi_type type; ++ unsigned char values[16]; ++}; ++ ++/* This struct have the values for each supported VBI Standard ++ * by ++ tvp5150_vbi_types should follow the same order as vbi_ram_default ++ * value 0 means rom position 0x10, value 1 means rom position 0x30 ++ * and so on. There are 16 possible locations from 0 to 15. ++ */ ++ ++static struct i2c_vbi_ram_value vbi_ram_default[] = ++{ ++ /* FIXME: Current api doesn't handle all VBI types, those not ++ yet supported are placed under #if 0 */ ++#if 0 ++ {0x010, /* Teletext, SECAM, WST System A */ ++ {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, ++ 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } ++ }, ++#endif ++ {0x030, /* Teletext, PAL, WST System B */ ++ {V4L2_SLICED_TELETEXT_B,6,22,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, ++ 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } ++ }, ++#if 0 ++ {0x050, /* Teletext, PAL, WST System C */ ++ {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, ++ 0xa6, 0x98, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } ++ }, ++ {0x070, /* Teletext, NTSC, WST System B */ ++ {V4L2_SLICED_TELETEXT_NTSC_B,10,21,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x23, ++ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } ++ }, ++ {0x090, /* Tetetext, NTSC NABTS System C */ ++ {V4L2_SLICED_TELETEXT_NTSC_C,10,21,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, ++ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x15, 0x00 } ++ }, ++ {0x0b0, /* Teletext, NTSC-J, NABTS System D */ ++ {V4L2_SLICED_TELETEXT_NTSC_D,10,21,1}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0xa7, 0x2e, 0x20, 0x23, ++ 0x69, 0x93, 0x0d, 0x00, 0x00, 0x00, 0x10, 0x00 } ++ }, ++ {0x0d0, /* Closed Caption, PAL/SECAM */ ++ {V4L2_SLICED_CAPTION_625,22,22,1}, ++ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, ++ 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } ++ }, ++#endif ++ {0x0f0, /* Closed Caption, NTSC */ ++ {V4L2_SLICED_CAPTION_525,21,21,1}, ++ { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, ++ 0x69, 0x8c, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } ++ }, ++ {0x110, /* Wide Screen Signal, PAL/SECAM */ ++ {V4L2_SLICED_WSS_625,23,23,1}, ++ { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, ++ 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } ++ }, ++#if 0 ++ {0x130, /* Wide Screen Signal, NTSC C */ ++ {V4L2_SLICED_WSS_525,20,20,1}, ++ { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, ++ 0x69, 0x7c, 0x08, 0x00, 0x00, 0x00, 0x39, 0x00 } ++ }, ++ {0x150, /* Vertical Interval Timecode (VITC), PAL/SECAM */ ++ {V4l2_SLICED_VITC_625,6,22,0}, ++ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, ++ 0xa6, 0x85, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } ++ }, ++ {0x170, /* Vertical Interval Timecode (VITC), NTSC */ ++ {V4l2_SLICED_VITC_525,10,20,0}, ++ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, ++ 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } ++ }, ++#endif ++ {0x190, /* Video Program System (VPS), PAL */ ++ {V4L2_SLICED_VPS,16,16,0}, ++ { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, ++ 0xa6, 0xda, 0x0b, 0x00, 0x00, 0x00, 0x60, 0x00 } ++ }, ++ /* 0x1d0 User programmable */ ++ ++ /* End of struct */ ++ { (u16)-1 } ++}; ++ ++static int tvp5150_write_inittab(struct v4l2_subdev *sd, ++ const struct i2c_reg_value *regs) ++{ ++ while (regs->reg != 0xff) { ++ tvp5150_write(sd, regs->reg, regs->value); ++ regs++; ++ } ++ return 0; ++} ++ ++static int tvp5150_vdp_init(struct v4l2_subdev *sd, ++ const struct i2c_vbi_ram_value *regs) ++{ ++ unsigned int i; ++ ++ /* Disable Full Field */ ++ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); ++ ++ /* Before programming, Line mode should be at 0xff */ ++ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) ++ tvp5150_write(sd, i, 0xff); ++ ++ /* Load Ram Table */ ++ while (regs->reg != (u16)-1) { ++ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); ++ tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); ++ ++ for (i = 0; i < 16; i++) ++ tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); ++ ++ regs++; ++ } ++ return 0; ++} ++ ++/* Fills VBI capabilities based on i2c_vbi_ram_value struct */ ++static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, ++ struct v4l2_sliced_vbi_cap *cap) ++{ ++ const struct i2c_vbi_ram_value *regs = vbi_ram_default; ++ int line; ++ ++ v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); ++ memset(cap, 0, sizeof *cap); ++ ++ while (regs->reg != (u16)-1 ) { ++ for (line=regs->type.ini_line;line<=regs->type.end_line;line++) { ++ cap->service_lines[0][line] |= regs->type.vbi_type; ++ } ++ cap->service_set |= regs->type.vbi_type; ++ ++ regs++; ++ } ++ return 0; ++} ++ ++/* Set vbi processing ++ * type - one of tvp5150_vbi_types ++ * line - line to gather data ++ * fields: bit 0 field1, bit 1, field2 ++ * flags (default=0xf0) is a bitmask, were set means: ++ * bit 7: enable filtering null bytes on CC ++ * bit 6: send data also to FIFO ++ * bit 5: don't allow data with errors on FIFO ++ * bit 4: enable ECC when possible ++ * pix_align = pix alignment: ++ * LSB = field1 ++ * MSB = field2 ++ */ ++static int tvp5150_set_vbi(struct v4l2_subdev *sd, ++ const struct i2c_vbi_ram_value *regs, ++ unsigned int type,u8 flags, int line, ++ const int fields) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ v4l2_std_id std = decoder->norm; ++ u8 reg; ++ int pos=0; ++ ++ if (std == V4L2_STD_ALL) { ++ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); ++ return 0; ++ } else if (std & V4L2_STD_625_50) { ++ /* Don't follow NTSC Line number convension */ ++ line += 3; ++ } ++ ++ if (line<6||line>27) ++ return 0; ++ ++ while (regs->reg != (u16)-1 ) { ++ if ((type & regs->type.vbi_type) && ++ (line>=regs->type.ini_line) && ++ (line<=regs->type.end_line)) { ++ type=regs->type.vbi_type; ++ break; ++ } ++ ++ regs++; ++ pos++; ++ } ++ if (regs->reg == (u16)-1) ++ return 0; ++ ++ type=pos | (flags & 0xf0); ++ reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; ++ ++ if (fields&1) { ++ tvp5150_write(sd, reg, type); ++ } ++ ++ if (fields&2) { ++ tvp5150_write(sd, reg+1, type); ++ } ++ ++ return type; ++} ++ ++static int tvp5150_get_vbi(struct v4l2_subdev *sd, ++ const struct i2c_vbi_ram_value *regs, int line) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ v4l2_std_id std = decoder->norm; ++ u8 reg; ++ int pos, type = 0; ++ int i, ret = 0; ++ ++ if (std == V4L2_STD_ALL) { ++ v4l2_err(sd, "VBI can't be configured without knowing number of lines\n"); ++ return 0; ++ } else if (std & V4L2_STD_625_50) { ++ /* Don't follow NTSC Line number convension */ ++ line += 3; ++ } ++ ++ if (line < 6 || line > 27) ++ return 0; ++ ++ reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; ++ ++ for (i = 0; i <= 1; i++) { ++ ret = tvp5150_read(sd, reg + i); ++ if (ret < 0) { ++ v4l2_err(sd, "%s: failed with error = %d\n", ++ __func__, ret); ++ return 0; ++ } ++ pos = ret & 0x0f; ++ if (pos < 0x0f) ++ type |= regs[pos].type.vbi_type; ++ } ++ ++ return type; ++} ++ ++static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ int fmt = 0; ++ ++ decoder->norm = std; ++ ++ /* First tests should be against specific std */ ++ ++ if (std == V4L2_STD_ALL) { ++ fmt = VIDEO_STD_AUTO_SWITCH_BIT; /* Autodetect mode */ ++ } else if (std & V4L2_STD_NTSC_443) { ++ fmt = VIDEO_STD_NTSC_4_43_BIT; ++ } else if (std & V4L2_STD_PAL_M) { ++ fmt = VIDEO_STD_PAL_M_BIT; ++ } else if (std & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { ++ fmt = VIDEO_STD_PAL_COMBINATION_N_BIT; ++ } else { ++ /* Then, test against generic ones */ ++ if (std & V4L2_STD_NTSC) ++ fmt = VIDEO_STD_NTSC_MJ_BIT; ++ else if (std & V4L2_STD_PAL) ++ fmt = VIDEO_STD_PAL_BDGHIN_BIT; ++ else if (std & V4L2_STD_SECAM) ++ fmt = VIDEO_STD_SECAM_BIT; ++ } ++ ++ v4l2_dbg(1, debug, sd, "Set video std register to %d.\n", fmt); ++ tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); ++ return 0; ++} ++ ++static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ ++ if (decoder->norm == std) ++ return 0; ++ ++ /* Change cropping height limits */ ++ if (std & V4L2_STD_525_60) ++ decoder->rect.height = TVP5150_V_MAX_525_60; ++ else ++ decoder->rect.height = TVP5150_V_MAX_OTHERS; ++ ++ ++ return tvp5150_set_std(sd, std); ++} ++ ++static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ ++ /* Initializes TVP5150 to its default values */ ++ tvp5150_write_inittab(sd, tvp5150_init_default); ++ ++ /* Initializes VDP registers */ ++ tvp5150_vdp_init(sd, vbi_ram_default); ++ ++ /* Selects decoder input */ ++ tvp5150_selmux(sd); ++ ++ /* Initializes TVP5150 to stream enabled values */ ++ tvp5150_write_inittab(sd, tvp5150_init_enable); ++ ++ /* Initialize image preferences */ ++ v4l2_ctrl_handler_setup(&decoder->hdl); ++ ++ tvp5150_set_std(sd, decoder->norm); ++ return 0; ++}; ++ ++static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); ++ return 0; ++ case V4L2_CID_CONTRAST: ++ tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); ++ return 0; ++ case V4L2_CID_SATURATION: ++ tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); ++ return 0; ++ case V4L2_CID_HUE: ++ tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) ++{ ++ int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); ++ ++ switch (val & 0x0F) { ++ case 0x01: ++ return V4L2_STD_NTSC; ++ case 0x03: ++ return V4L2_STD_PAL; ++ case 0x05: ++ return V4L2_STD_PAL_M; ++ case 0x07: ++ return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; ++ case 0x09: ++ return V4L2_STD_NTSC_443; ++ case 0xb: ++ return V4L2_STD_SECAM; ++ default: ++ return V4L2_STD_UNKNOWN; ++ } ++} ++ ++static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index) ++ return -EINVAL; ++ ++ *code = V4L2_MBUS_FMT_UYVY8_2X8; ++ return 0; ++} ++ ++static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *f) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ ++ if (f == NULL) ++ return -EINVAL; ++ ++ tvp5150_reset(sd, 0); ++ ++ f->width = decoder->rect.width; ++ f->height = decoder->rect.height; ++ ++ f->code = V4L2_MBUS_FMT_UYVY8_2X8; ++ f->field = V4L2_FIELD_SEQ_TB; ++ f->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ v4l2_dbg(1, debug, sd, "width = %d, height = %d\n", f->width, ++ f->height); ++ return 0; ++} ++ ++static int tvp5150_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) ++{ ++ struct v4l2_rect rect = a->c; ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ v4l2_std_id std; ++ int hmax; ++ ++ v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n", ++ __func__, rect.left, rect.top, rect.width, rect.height); ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ /* tvp5150 has some special limits */ ++ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); ++ rect.width = clamp(rect.width, ++ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, ++ TVP5150_H_MAX - rect.left); ++ rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); ++ ++ /* Calculate height based on current standard */ ++ if (decoder->norm == V4L2_STD_ALL) ++ std = tvp5150_read_std(sd); ++ else ++ std = decoder->norm; ++ ++ if (std & V4L2_STD_525_60) ++ hmax = TVP5150_V_MAX_525_60; ++ else ++ hmax = TVP5150_V_MAX_OTHERS; ++ ++ rect.height = clamp(rect.height, ++ hmax - TVP5150_MAX_CROP_TOP - rect.top, ++ hmax - rect.top); ++ ++ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); ++ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, ++ rect.top + rect.height - hmax); ++ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, ++ rect.left >> TVP5150_CROP_SHIFT); ++ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, ++ rect.left | (1 << TVP5150_CROP_SHIFT)); ++ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, ++ (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> ++ TVP5150_CROP_SHIFT); ++ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, ++ rect.left + rect.width - TVP5150_MAX_CROP_LEFT); ++ ++ decoder->rect = rect; ++ ++ return 0; ++} ++ ++static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) ++{ ++ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); ++ ++ a->c = decoder->rect; ++ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ return 0; ++} ++ ++static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) ++{ ++ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd); ++ v4l2_std_id std; ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ a->bounds.left = 0; ++ a->bounds.top = 0; ++ a->bounds.width = TVP5150_H_MAX; ++ ++ /* Calculate height based on current standard */ ++ if (decoder->norm == V4L2_STD_ALL) ++ std = tvp5150_read_std(sd); ++ else ++ std = decoder->norm; ++ ++ if (std & V4L2_STD_525_60) ++ a->bounds.height = TVP5150_V_MAX_525_60; ++ else ++ a->bounds.height = TVP5150_V_MAX_OTHERS; ++ ++ a->defrect = a->bounds; ++ a->pixelaspect.numerator = 1; ++ a->pixelaspect.denominator = 1; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ I2C Command ++ ****************************************************************************/ ++ ++static int tvp5150_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ ++ decoder->input = input; ++ decoder->output = output; ++ tvp5150_selmux(sd); ++ return 0; ++} ++ ++static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) ++{ ++ /* this is for capturing 36 raw vbi lines ++ if there's a way to cut off the beginning 2 vbi lines ++ with the tvp5150 then the vbi line count could be lowered ++ to 17 lines/field again, although I couldn't find a register ++ which could do that cropping */ ++ if (fmt->sample_format == V4L2_PIX_FMT_GREY) ++ tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); ++ if (fmt->count[0] == 18 && fmt->count[1] == 18) { ++ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); ++ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); ++ } ++ return 0; ++} ++ ++static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ int i; ++ ++ if (svbi->service_set != 0) { ++ for (i = 0; i <= 23; i++) { ++ svbi->service_lines[1][i] = 0; ++ svbi->service_lines[0][i] = ++ tvp5150_set_vbi(sd, vbi_ram_default, ++ svbi->service_lines[0][i], 0xf0, i, 3); ++ } ++ /* Enables FIFO */ ++ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); ++ } else { ++ /* Disables FIFO*/ ++ tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); ++ ++ /* Disable Full Field */ ++ tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); ++ ++ /* Disable Line modes */ ++ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) ++ tvp5150_write(sd, i, 0xff); ++ } ++ return 0; ++} ++ ++static int tvp5150_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ int i, mask = 0; ++ ++ memset(svbi->service_lines, 0, sizeof(svbi->service_lines)); ++ ++ for (i = 0; i <= 23; i++) { ++ svbi->service_lines[0][i] = ++ tvp5150_get_vbi(sd, vbi_ram_default, i); ++ mask |= svbi->service_lines[0][i]; ++ } ++ svbi->service_set = mask; ++ return 0; ++} ++ ++static int tvp5150_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ int rev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ rev = tvp5150_read(sd, TVP5150_ROM_MAJOR_VER) << 8 | ++ tvp5150_read(sd, TVP5150_ROM_MINOR_VER); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP5150, ++ rev); ++} ++ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ int res; ++ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ res = tvp5150_read(sd, reg->reg & 0xff); ++ if (res < 0) { ++ v4l2_err(sd, "%s: failed with error = %d\n", __func__, res); ++ return res; ++ } ++ ++ reg->val = res; ++ reg->size = 1; ++ return 0; ++} ++ ++static int tvp5150_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ int status = tvp5150_read(sd, 0x88); ++ ++ vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { ++ .s_ctrl = tvp5150_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops tvp5150_core_ops = { ++ .log_status = tvp5150_log_status, ++ .s_std = tvp5150_s_std, ++ .reset = tvp5150_reset, ++ .g_chip_ident = tvp5150_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = tvp5150_g_register, ++ .s_register = tvp5150_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { ++ .g_tuner = tvp5150_g_tuner, ++}; ++ ++static const struct v4l2_subdev_video_ops tvp5150_video_ops = { ++ .s_routing = tvp5150_s_routing, ++ .enum_mbus_fmt = tvp5150_enum_mbus_fmt, ++ .s_mbus_fmt = tvp5150_mbus_fmt, ++ .try_mbus_fmt = tvp5150_mbus_fmt, ++ .g_mbus_fmt = tvp5150_mbus_fmt, ++ .s_crop = tvp5150_s_crop, ++ .g_crop = tvp5150_g_crop, ++ .cropcap = tvp5150_cropcap, ++}; ++ ++static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { ++ .g_sliced_vbi_cap = tvp5150_g_sliced_vbi_cap, ++ .g_sliced_fmt = tvp5150_g_sliced_fmt, ++ .s_sliced_fmt = tvp5150_s_sliced_fmt, ++ .s_raw_fmt = tvp5150_s_raw_fmt, ++}; ++ ++static const struct v4l2_subdev_ops tvp5150_ops = { ++ .core = &tvp5150_core_ops, ++ .tuner = &tvp5150_tuner_ops, ++ .video = &tvp5150_video_ops, ++ .vbi = &tvp5150_vbi_ops, ++}; ++ ++ ++/**************************************************************************** ++ I2C Client & Driver ++ ****************************************************************************/ ++ ++static int tvp5150_probe(struct i2c_client *c, ++ const struct i2c_device_id *id) ++{ ++ struct tvp5150 *core; ++ struct v4l2_subdev *sd; ++ int tvp5150_id[4]; ++ int i, res; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(c->adapter, ++ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -EIO; ++ ++ core = kzalloc(sizeof(struct tvp5150), GFP_KERNEL); ++ if (!core) { ++ return -ENOMEM; ++ } ++ sd = &core->sd; ++ v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); ++ ++ /* ++ * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, ++ * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER ++ */ ++ for (i = 0; i < 4; i++) { ++ res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); ++ if (res < 0) ++ goto free_core; ++ tvp5150_id[i] = res; ++ } ++ ++ v4l_info(c, "chip found @ 0x%02x (%s)\n", ++ c->addr << 1, c->adapter->name); ++ ++ if (tvp5150_id[2] == 4 && tvp5150_id[3] == 0) { /* Is TVP5150AM1 */ ++ v4l2_info(sd, "tvp%02x%02xam1 detected.\n", ++ tvp5150_id[0], tvp5150_id[1]); ++ ++ /* ITU-T BT.656.4 timing */ ++ tvp5150_write(sd, TVP5150_REV_SELECT, 0); ++ } else { ++ /* Is TVP5150A */ ++ if (tvp5150_id[2] == 3 || tvp5150_id[3] == 0x21) { ++ v4l2_info(sd, "tvp%02x%02xa detected.\n", ++ tvp5150_id[2], tvp5150_id[3]); ++ } else { ++ v4l2_info(sd, "*** unknown tvp%02x%02x chip detected.\n", ++ tvp5150_id[2], tvp5150_id[3]); ++ v4l2_info(sd, "*** Rom ver is %d.%d\n", ++ tvp5150_id[2], tvp5150_id[3]); ++ } ++ } ++ ++ core->norm = V4L2_STD_ALL; /* Default is autodetect */ ++ core->input = TVP5150_COMPOSITE1; ++ core->enable = 1; ++ ++ v4l2_ctrl_handler_init(&core->hdl, 4); ++ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&core->hdl, &tvp5150_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ sd->ctrl_handler = &core->hdl; ++ if (core->hdl.error) { ++ res = core->hdl.error; ++ v4l2_ctrl_handler_free(&core->hdl); ++ goto free_core; ++ } ++ v4l2_ctrl_handler_setup(&core->hdl); ++ ++ /* Default is no cropping */ ++ core->rect.top = 0; ++ if (tvp5150_read_std(sd) & V4L2_STD_525_60) ++ core->rect.height = TVP5150_V_MAX_525_60; ++ else ++ core->rect.height = TVP5150_V_MAX_OTHERS; ++ core->rect.left = 0; ++ core->rect.width = TVP5150_H_MAX; ++ ++ if (debug > 1) ++ tvp5150_log_status(sd); ++ return 0; ++ ++free_core: ++ kfree(core); ++ return res; ++} ++ ++static int tvp5150_remove(struct i2c_client *c) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(c); ++ struct tvp5150 *decoder = to_tvp5150(sd); ++ ++ v4l2_dbg(1, debug, sd, ++ "tvp5150.c: removing tvp5150 adapter on address 0x%x\n", ++ c->addr << 1); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(to_tvp5150(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id tvp5150_id[] = { ++ { "tvp5150", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tvp5150_id); ++ ++static struct i2c_driver tvp5150_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "tvp5150", ++ }, ++ .probe = tvp5150_probe, ++ .remove = tvp5150_remove, ++ .id_table = tvp5150_id, ++}; ++ ++module_i2c_driver(tvp5150_driver); +diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h +new file mode 100644 +index 0000000..25a9949 +--- /dev/null ++++ b/drivers/media/i2c/tvp5150_reg.h +@@ -0,0 +1,139 @@ ++/* ++ * tvp5150 - Texas Instruments TVP5150A/AM1 video decoder registers ++ * ++ * Copyright (c) 2005,2006 Mauro Carvalho Chehab (mchehab@infradead.org) ++ * This code is placed under the terms of the GNU General Public License v2 ++ */ ++ ++#define TVP5150_VD_IN_SRC_SEL_1 0x00 /* Video input source selection #1 */ ++#define TVP5150_ANAL_CHL_CTL 0x01 /* Analog channel controls */ ++#define TVP5150_OP_MODE_CTL 0x02 /* Operation mode controls */ ++#define TVP5150_MISC_CTL 0x03 /* Miscellaneous controls */ ++#define TVP5150_AUTOSW_MSK 0x04 /* Autoswitch mask: TVP5150A / TVP5150AM */ ++ ++/* Reserved 05h */ ++ ++#define TVP5150_COLOR_KIL_THSH_CTL 0x06 /* Color killer threshold control */ ++#define TVP5150_LUMA_PROC_CTL_1 0x07 /* Luminance processing control #1 */ ++#define TVP5150_LUMA_PROC_CTL_2 0x08 /* Luminance processing control #2 */ ++#define TVP5150_BRIGHT_CTL 0x09 /* Brightness control */ ++#define TVP5150_SATURATION_CTL 0x0a /* Color saturation control */ ++#define TVP5150_HUE_CTL 0x0b /* Hue control */ ++#define TVP5150_CONTRAST_CTL 0x0c /* Contrast control */ ++#define TVP5150_DATA_RATE_SEL 0x0d /* Outputs and data rates select */ ++#define TVP5150_LUMA_PROC_CTL_3 0x0e /* Luminance processing control #3 */ ++#define TVP5150_CONF_SHARED_PIN 0x0f /* Configuration shared pins */ ++ ++/* Reserved 10h */ ++ ++#define TVP5150_ACT_VD_CROP_ST_MSB 0x11 /* Active video cropping start MSB */ ++#define TVP5150_ACT_VD_CROP_ST_LSB 0x12 /* Active video cropping start LSB */ ++#define TVP5150_ACT_VD_CROP_STP_MSB 0x13 /* Active video cropping stop MSB */ ++#define TVP5150_ACT_VD_CROP_STP_LSB 0x14 /* Active video cropping stop LSB */ ++#define TVP5150_GENLOCK 0x15 /* Genlock/RTC */ ++#define TVP5150_HORIZ_SYNC_START 0x16 /* Horizontal sync start */ ++ ++/* Reserved 17h */ ++ ++#define TVP5150_VERT_BLANKING_START 0x18 /* Vertical blanking start */ ++#define TVP5150_VERT_BLANKING_STOP 0x19 /* Vertical blanking stop */ ++#define TVP5150_CHROMA_PROC_CTL_1 0x1a /* Chrominance processing control #1 */ ++#define TVP5150_CHROMA_PROC_CTL_2 0x1b /* Chrominance processing control #2 */ ++#define TVP5150_INT_RESET_REG_B 0x1c /* Interrupt reset register B */ ++#define TVP5150_INT_ENABLE_REG_B 0x1d /* Interrupt enable register B */ ++#define TVP5150_INTT_CONFIG_REG_B 0x1e /* Interrupt configuration register B */ ++ ++/* Reserved 1Fh-27h */ ++ ++#define VIDEO_STD_MASK (0x07 >> 1) ++#define TVP5150_VIDEO_STD 0x28 /* Video standard */ ++#define VIDEO_STD_AUTO_SWITCH_BIT 0x00 ++#define VIDEO_STD_NTSC_MJ_BIT 0x02 ++#define VIDEO_STD_PAL_BDGHIN_BIT 0x04 ++#define VIDEO_STD_PAL_M_BIT 0x06 ++#define VIDEO_STD_PAL_COMBINATION_N_BIT 0x08 ++#define VIDEO_STD_NTSC_4_43_BIT 0x0a ++#define VIDEO_STD_SECAM_BIT 0x0c ++ ++#define VIDEO_STD_NTSC_MJ_BIT_AS 0x01 ++#define VIDEO_STD_PAL_BDGHIN_BIT_AS 0x03 ++#define VIDEO_STD_PAL_M_BIT_AS 0x05 ++#define VIDEO_STD_PAL_COMBINATION_N_BIT_AS 0x07 ++#define VIDEO_STD_NTSC_4_43_BIT_AS 0x09 ++#define VIDEO_STD_SECAM_BIT_AS 0x0b ++ ++/* Reserved 29h-2bh */ ++ ++#define TVP5150_CB_GAIN_FACT 0x2c /* Cb gain factor */ ++#define TVP5150_CR_GAIN_FACTOR 0x2d /* Cr gain factor */ ++#define TVP5150_MACROVISION_ON_CTR 0x2e /* Macrovision on counter */ ++#define TVP5150_MACROVISION_OFF_CTR 0x2f /* Macrovision off counter */ ++#define TVP5150_REV_SELECT 0x30 /* revision select (TVP5150AM1 only) */ ++ ++/* Reserved 31h-7Fh */ ++ ++#define TVP5150_MSB_DEV_ID 0x80 /* MSB of device ID */ ++#define TVP5150_LSB_DEV_ID 0x81 /* LSB of device ID */ ++#define TVP5150_ROM_MAJOR_VER 0x82 /* ROM major version */ ++#define TVP5150_ROM_MINOR_VER 0x83 /* ROM minor version */ ++#define TVP5150_VERT_LN_COUNT_MSB 0x84 /* Vertical line count MSB */ ++#define TVP5150_VERT_LN_COUNT_LSB 0x85 /* Vertical line count LSB */ ++#define TVP5150_INT_STATUS_REG_B 0x86 /* Interrupt status register B */ ++#define TVP5150_INT_ACTIVE_REG_B 0x87 /* Interrupt active register B */ ++#define TVP5150_STATUS_REG_1 0x88 /* Status register #1 */ ++#define TVP5150_STATUS_REG_2 0x89 /* Status register #2 */ ++#define TVP5150_STATUS_REG_3 0x8a /* Status register #3 */ ++#define TVP5150_STATUS_REG_4 0x8b /* Status register #4 */ ++#define TVP5150_STATUS_REG_5 0x8c /* Status register #5 */ ++/* Reserved 8Dh-8Fh */ ++ /* Closed caption data registers */ ++#define TVP5150_CC_DATA_INI 0x90 ++#define TVP5150_CC_DATA_END 0x93 ++ ++ /* WSS data registers */ ++#define TVP5150_WSS_DATA_INI 0x94 ++#define TVP5150_WSS_DATA_END 0x99 ++ ++/* VPS data registers */ ++#define TVP5150_VPS_DATA_INI 0x9a ++#define TVP5150_VPS_DATA_END 0xa6 ++ ++/* VITC data registers */ ++#define TVP5150_VITC_DATA_INI 0xa7 ++#define TVP5150_VITC_DATA_END 0xaf ++ ++#define TVP5150_VBI_FIFO_READ_DATA 0xb0 /* VBI FIFO read data */ ++ ++/* Teletext filter 1 */ ++#define TVP5150_TELETEXT_FIL1_INI 0xb1 ++#define TVP5150_TELETEXT_FIL1_END 0xb5 ++ ++/* Teletext filter 2 */ ++#define TVP5150_TELETEXT_FIL2_INI 0xb6 ++#define TVP5150_TELETEXT_FIL2_END 0xba ++ ++#define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ ++/* Reserved BCh-BFh */ ++#define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ ++#define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ ++#define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ ++#define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ ++#define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ ++#define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ ++#define TVP5150_VDP_STATUS_REG 0xc6 /* VDP status register */ ++#define TVP5150_FIFO_WORD_COUNT 0xc7 /* FIFO word count */ ++#define TVP5150_FIFO_INT_THRESHOLD 0xc8 /* FIFO interrupt threshold */ ++#define TVP5150_FIFO_RESET 0xc9 /* FIFO reset */ ++#define TVP5150_LINE_NUMBER_INT 0xca /* Line number interrupt */ ++#define TVP5150_PIX_ALIGN_REG_LOW 0xcb /* Pixel alignment register low byte */ ++#define TVP5150_PIX_ALIGN_REG_HIGH 0xcc /* Pixel alignment register high byte */ ++#define TVP5150_FIFO_OUT_CTRL 0xcd /* FIFO output control */ ++/* Reserved CEh */ ++#define TVP5150_FULL_FIELD_ENA 0xcf /* Full field enable 1 */ ++ ++/* Line mode registers */ ++#define TVP5150_LINE_MODE_INI 0xd0 ++#define TVP5150_LINE_MODE_END 0xfb ++ ++#define TVP5150_FULL_FIELD_MODE_REG 0xfc /* Full field mode register */ ++/* Reserved FDh-FFh */ +diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c +new file mode 100644 +index 0000000..fb6a5b5 +--- /dev/null ++++ b/drivers/media/i2c/tvp7002.c +@@ -0,0 +1,1145 @@ ++/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics ++ * Digitizer with Horizontal PLL registers ++ * ++ * Copyright (C) 2009 Texas Instruments Inc ++ * Author: Santiago Nunez-Corrales ++ * ++ * This code is partially based upon the TVP5150 driver ++ * written by Mauro Carvalho Chehab (mchehab@infradead.org), ++ * the TVP514x driver written by Vaibhav Hiremath ++ * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by ++ * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "tvp7002_reg.h" ++ ++MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); ++MODULE_AUTHOR("Santiago Nunez-Corrales "); ++MODULE_LICENSE("GPL"); ++ ++/* Module Name */ ++#define TVP7002_MODULE_NAME "tvp7002" ++ ++/* I2C retry attempts */ ++#define I2C_RETRY_COUNT (5) ++ ++/* End of registers */ ++#define TVP7002_EOR 0x5c ++ ++/* Read write definition for registers */ ++#define TVP7002_READ 0 ++#define TVP7002_WRITE 1 ++#define TVP7002_RESERVED 2 ++ ++/* Interlaced vs progressive mask and shift */ ++#define TVP7002_IP_SHIFT 5 ++#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) ++ ++/* Shift for CPL and LPF registers */ ++#define TVP7002_CL_SHIFT 8 ++#define TVP7002_CL_MASK 0x0f ++ ++/* Debug functions */ ++static bool debug; ++module_param(debug, bool, 0644); ++MODULE_PARM_DESC(debug, "Debug level (0-2)"); ++ ++/* Structure for register values */ ++struct i2c_reg_value { ++ u8 reg; ++ u8 value; ++ u8 type; ++}; ++ ++/* ++ * Register default values (according to tvp7002 datasheet) ++ * In the case of read-only registers, the value (0xff) is ++ * never written. R/W functionality is controlled by the ++ * writable bit in the register struct definition. ++ */ ++static const struct i2c_reg_value tvp7002_init_default[] = { ++ { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, ++ { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, ++ { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, ++ { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, ++ { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, ++ { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, ++ { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, ++ { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, ++ { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, ++ { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, ++ { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, ++ { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, ++ { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, ++ { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, ++ { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, ++ { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, ++ { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, ++ { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, ++ { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, ++ { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, ++ { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, ++ { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, ++ { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, ++ { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, ++ { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, ++ { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, ++ { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, ++ { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, ++ { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, ++ { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, ++ { 0x29, 0x08, TVP7002_RESERVED }, ++ { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, ++ /* PWR_CTL is controlled only by the probe and reset functions */ ++ { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, ++ { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, ++ { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, ++ { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, ++ { TVP7002_RGB_COARSE_CLAMP_CTL, 0x8c, TVP7002_WRITE }, ++ { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { 0x32, 0x18, TVP7002_RESERVED }, ++ { 0x33, 0x60, TVP7002_RESERVED }, ++ { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, ++ { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, ++ { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, ++ { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, ++ { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, ++ { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, ++ { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, ++ { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, ++ { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, ++ { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, ++ { 0x3e, 0x60, TVP7002_RESERVED }, ++ { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, ++ { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, ++ { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, ++ { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, ++ { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, ++ { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, ++ { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, ++ { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, ++ { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, ++ { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, ++ { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, ++ { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, ++ /* This signals end of register values */ ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 480P */ ++static const struct i2c_reg_value tvp7002_parms_480P[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xa0, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 576P */ ++static const struct i2c_reg_value tvp7002_parms_576P[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 1080I60 */ ++static const struct i2c_reg_value tvp7002_parms_1080I60[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 1080P60 */ ++static const struct i2c_reg_value tvp7002_parms_1080P60[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x80, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 1080I50 */ ++static const struct i2c_reg_value tvp7002_parms_1080I50[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 720P60 */ ++static const struct i2c_reg_value tvp7002_parms_720P60[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Register parameters for 720P50 */ ++static const struct i2c_reg_value tvp7002_parms_720P50[] = { ++ { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, ++ { TVP7002_HPLL_FDBK_DIV_LSBS, 0xc0, TVP7002_WRITE }, ++ { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, ++ { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, ++ { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, ++ { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, ++ { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, ++ { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, ++ { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, ++ { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, ++ { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, ++ { TVP7002_EOR, 0xff, TVP7002_RESERVED } ++}; ++ ++/* Preset definition for handling device operation */ ++struct tvp7002_preset_definition { ++ u32 preset; ++ struct v4l2_dv_timings timings; ++ const struct i2c_reg_value *p_settings; ++ enum v4l2_colorspace color_space; ++ enum v4l2_field scanmode; ++ u16 progressive; ++ u16 lines_per_frame; ++ u16 cpl_min; ++ u16 cpl_max; ++}; ++ ++/* Struct list for digital video presets */ ++static const struct tvp7002_preset_definition tvp7002_presets[] = { ++ { ++ V4L2_DV_720P60, ++ V4L2_DV_BT_CEA_1280X720P60, ++ tvp7002_parms_720P60, ++ V4L2_COLORSPACE_REC709, ++ V4L2_FIELD_NONE, ++ 1, ++ 0x2EE, ++ 135, ++ 153 ++ }, ++ { ++ V4L2_DV_1080I60, ++ V4L2_DV_BT_CEA_1920X1080I60, ++ tvp7002_parms_1080I60, ++ V4L2_COLORSPACE_REC709, ++ V4L2_FIELD_INTERLACED, ++ 0, ++ 0x465, ++ 181, ++ 205 ++ }, ++ { ++ V4L2_DV_1080I50, ++ V4L2_DV_BT_CEA_1920X1080I50, ++ tvp7002_parms_1080I50, ++ V4L2_COLORSPACE_REC709, ++ V4L2_FIELD_INTERLACED, ++ 0, ++ 0x465, ++ 217, ++ 245 ++ }, ++ { ++ V4L2_DV_720P50, ++ V4L2_DV_BT_CEA_1280X720P50, ++ tvp7002_parms_720P50, ++ V4L2_COLORSPACE_REC709, ++ V4L2_FIELD_NONE, ++ 1, ++ 0x2EE, ++ 163, ++ 183 ++ }, ++ { ++ V4L2_DV_1080P60, ++ V4L2_DV_BT_CEA_1920X1080P60, ++ tvp7002_parms_1080P60, ++ V4L2_COLORSPACE_REC709, ++ V4L2_FIELD_NONE, ++ 1, ++ 0x465, ++ 90, ++ 102 ++ }, ++ { ++ V4L2_DV_480P59_94, ++ V4L2_DV_BT_CEA_720X480P59_94, ++ tvp7002_parms_480P, ++ V4L2_COLORSPACE_SMPTE170M, ++ V4L2_FIELD_NONE, ++ 1, ++ 0x20D, ++ 0xffff, ++ 0xffff ++ }, ++ { ++ V4L2_DV_576P50, ++ V4L2_DV_BT_CEA_720X576P50, ++ tvp7002_parms_576P, ++ V4L2_COLORSPACE_SMPTE170M, ++ V4L2_FIELD_NONE, ++ 1, ++ 0x271, ++ 0xffff, ++ 0xffff ++ } ++}; ++ ++#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) ++ ++/* Device definition */ ++struct tvp7002 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ const struct tvp7002_config *pdata; ++ ++ int ver; ++ int streaming; ++ ++ const struct tvp7002_preset_definition *current_preset; ++}; ++ ++/* ++ * to_tvp7002 - Obtain device handler TVP7002 ++ * @sd: ptr to v4l2_subdev struct ++ * ++ * Returns device handler tvp7002. ++ */ ++static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct tvp7002, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct tvp7002, hdl)->sd; ++} ++ ++/* ++ * tvp7002_read - Read a value from a register in an TVP7002 ++ * @sd: ptr to v4l2_subdev struct ++ * @addr: TVP7002 register address ++ * @dst: pointer to 8-bit destination ++ * ++ * Returns value read if successful, or non-zero (-1) otherwise. ++ */ ++static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) ++{ ++ struct i2c_client *c = v4l2_get_subdevdata(sd); ++ int retry; ++ int error; ++ ++ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { ++ error = i2c_smbus_read_byte_data(c, addr); ++ ++ if (error >= 0) { ++ *dst = (u8)error; ++ return 0; ++ } ++ ++ msleep_interruptible(10); ++ } ++ v4l2_err(sd, "TVP7002 read error %d\n", error); ++ return error; ++} ++ ++/* ++ * tvp7002_read_err() - Read a register value with error code ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @reg: destination register ++ * @val: value to be read ++ * @err: pointer to error value ++ * ++ * Read a value in a register and save error value in pointer. ++ * Also update the register table if successful ++ */ ++static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, ++ u8 *dst, int *err) ++{ ++ if (!*err) ++ *err = tvp7002_read(sd, reg, dst); ++} ++ ++/* ++ * tvp7002_write() - Write a value to a register in TVP7002 ++ * @sd: ptr to v4l2_subdev struct ++ * @addr: TVP7002 register address ++ * @value: value to be written to the register ++ * ++ * Write a value to a register in an TVP7002 decoder device. ++ * Returns zero if successful, or non-zero otherwise. ++ */ ++static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) ++{ ++ struct i2c_client *c; ++ int retry; ++ int error; ++ ++ c = v4l2_get_subdevdata(sd); ++ ++ for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { ++ error = i2c_smbus_write_byte_data(c, addr, value); ++ ++ if (error >= 0) ++ return 0; ++ ++ v4l2_warn(sd, "Write: retry ... %d\n", retry); ++ msleep_interruptible(10); ++ } ++ v4l2_err(sd, "TVP7002 write error %d\n", error); ++ return error; ++} ++ ++/* ++ * tvp7002_write_err() - Write a register value with error code ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @reg: destination register ++ * @val: value to be written ++ * @err: pointer to error value ++ * ++ * Write a value in a register and save error value in pointer. ++ * Also update the register table if successful ++ */ ++static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, ++ u8 val, int *err) ++{ ++ if (!*err) ++ *err = tvp7002_write(sd, reg, val); ++} ++ ++/* ++ * tvp7002_g_chip_ident() - Get chip identification number ++ * @sd: ptr to v4l2_subdev struct ++ * @chip: ptr to v4l2_dbg_chip_ident struct ++ * ++ * Obtains the chip's identification number. ++ * Returns zero or -EINVAL if read operation fails. ++ */ ++static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ u8 rev; ++ int error; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); ++ ++ if (error < 0) ++ return error; ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); ++} ++ ++/* ++ * tvp7002_write_inittab() - Write initialization values ++ * @sd: ptr to v4l2_subdev struct ++ * @regs: ptr to i2c_reg_value struct ++ * ++ * Write initialization values. ++ * Returns zero or -EINVAL if read operation fails. ++ */ ++static int tvp7002_write_inittab(struct v4l2_subdev *sd, ++ const struct i2c_reg_value *regs) ++{ ++ int error = 0; ++ ++ /* Initialize the first (defined) registers */ ++ while (TVP7002_EOR != regs->reg) { ++ if (TVP7002_WRITE == regs->type) ++ tvp7002_write_err(sd, regs->reg, regs->value, &error); ++ regs++; ++ } ++ ++ return error; ++} ++ ++/* ++ * tvp7002_s_dv_preset() - Set digital video preset ++ * @sd: ptr to v4l2_subdev struct ++ * @dv_preset: ptr to v4l2_dv_preset struct ++ * ++ * Set the digital video preset for a TVP7002 decoder device. ++ * Returns zero when successful or -EINVAL if register access fails. ++ */ ++static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, ++ struct v4l2_dv_preset *dv_preset) ++{ ++ struct tvp7002 *device = to_tvp7002(sd); ++ u32 preset; ++ int i; ++ ++ for (i = 0; i < NUM_PRESETS; i++) { ++ preset = tvp7002_presets[i].preset; ++ if (preset == dv_preset->preset) { ++ device->current_preset = &tvp7002_presets[i]; ++ return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ struct tvp7002 *device = to_tvp7002(sd); ++ const struct v4l2_bt_timings *bt = &dv_timings->bt; ++ int i; ++ ++ if (dv_timings->type != V4L2_DV_BT_656_1120) ++ return -EINVAL; ++ for (i = 0; i < NUM_PRESETS; i++) { ++ const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; ++ ++ if (!memcmp(bt, t, &bt->standards - &bt->width)) { ++ device->current_preset = &tvp7002_presets[i]; ++ return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); ++ } ++ } ++ return -EINVAL; ++} ++ ++static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ struct tvp7002 *device = to_tvp7002(sd); ++ ++ *dv_timings = device->current_preset->timings; ++ return 0; ++} ++ ++/* ++ * tvp7002_s_ctrl() - Set a control ++ * @ctrl: ptr to v4l2_ctrl struct ++ * ++ * Set a control in TVP7002 decoder device. ++ * Returns zero when successful or -EINVAL if register access fails. ++ */ ++static int tvp7002_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ int error = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_GAIN: ++ tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, ctrl->val, &error); ++ tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, ctrl->val, &error); ++ tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, ctrl->val, &error); ++ return error; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * tvp7002_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @f: pointer to mediabus format structure ++ * ++ * Negotiate the image capture size and mediabus format. ++ * There is only one possible format, so this single function works for ++ * get, set and try. ++ */ ++static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) ++{ ++ struct tvp7002 *device = to_tvp7002(sd); ++ struct v4l2_dv_enum_preset e_preset; ++ int error; ++ ++ /* Calculate height and width based on current standard */ ++ error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); ++ if (error) ++ return error; ++ ++ f->width = e_preset.width; ++ f->height = e_preset.height; ++ f->code = V4L2_MBUS_FMT_YUYV10_1X20; ++ f->field = device->current_preset->scanmode; ++ f->colorspace = device->current_preset->color_space; ++ ++ v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d", ++ f->width, f->height); ++ return 0; ++} ++ ++/* ++ * tvp7002_query_dv_preset() - query DV preset ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @qpreset: standard V4L2 v4l2_dv_preset structure ++ * ++ * Returns the current DV preset by TVP7002. If no active input is ++ * detected, returns -EINVAL ++ */ ++static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) ++{ ++ const struct tvp7002_preset_definition *presets = tvp7002_presets; ++ u8 progressive; ++ u32 lpfr; ++ u32 cpln; ++ int error = 0; ++ u8 lpf_lsb; ++ u8 lpf_msb; ++ u8 cpl_lsb; ++ u8 cpl_msb; ++ ++ /* Return invalid index if no active input is detected */ ++ *index = NUM_PRESETS; ++ ++ /* Read standards from device registers */ ++ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); ++ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); ++ ++ if (error < 0) ++ return error; ++ ++ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); ++ tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); ++ ++ if (error < 0) ++ return error; ++ ++ /* Get lines per frame, clocks per line and interlaced/progresive */ ++ lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); ++ cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); ++ progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; ++ ++ /* Do checking of video modes */ ++ for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) ++ if (lpfr == presets->lines_per_frame && ++ progressive == presets->progressive) { ++ if (presets->cpl_min == 0xffff) ++ break; ++ if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) ++ break; ++ } ++ ++ if (*index == NUM_PRESETS) { ++ v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", ++ lpfr, cpln); ++ return -ENOLINK; ++ } ++ ++ /* Update lines per frame and clocks per line info */ ++ v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); ++ return 0; ++} ++ ++static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, ++ struct v4l2_dv_preset *qpreset) ++{ ++ int index; ++ int err = tvp7002_query_dv(sd, &index); ++ ++ if (err || index == NUM_PRESETS) { ++ qpreset->preset = V4L2_DV_INVALID; ++ if (err == -ENOLINK) ++ err = 0; ++ return err; ++ } ++ qpreset->preset = tvp7002_presets[index].preset; ++ return 0; ++} ++ ++static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *timings) ++{ ++ int index; ++ int err = tvp7002_query_dv(sd, &index); ++ ++ if (err) ++ return err; ++ *timings = tvp7002_presets[index].timings; ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++/* ++ * tvp7002_g_register() - Get the value of a register ++ * @sd: ptr to v4l2_subdev struct ++ * @reg: ptr to v4l2_dbg_register struct ++ * ++ * Get the value of a TVP7002 decoder device register. ++ * Returns zero when successful, -EINVAL if register read fails or ++ * access to I2C client fails, -EPERM if the call is not allowed ++ * by disabled CAP_SYS_ADMIN. ++ */ ++static int tvp7002_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 val; ++ int ret; ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ ret = tvp7002_read(sd, reg->reg & 0xff, &val); ++ reg->val = val; ++ return ret; ++} ++ ++/* ++ * tvp7002_s_register() - set a control ++ * @sd: ptr to v4l2_subdev struct ++ * @reg: ptr to v4l2_dbg_register struct ++ * ++ * Get the value of a TVP7002 decoder device register. ++ * Returns zero when successful, -EINVAL if register read fails or ++ * -EPERM if call not allowed. ++ */ ++static int tvp7002_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); ++} ++#endif ++ ++/* ++ * tvp7002_enum_mbus_fmt() - Enum supported mediabus formats ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @index: format index ++ * @code: pointer to mediabus format ++ * ++ * Enumerate supported mediabus formats. ++ */ ++ ++static int tvp7002_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ /* Check requested format index is within range */ ++ if (index) ++ return -EINVAL; ++ *code = V4L2_MBUS_FMT_YUYV10_1X20; ++ return 0; ++} ++ ++/* ++ * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @enable: streaming enable or disable ++ * ++ * Sets streaming to enable or disable, if possible. ++ */ ++static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct tvp7002 *device = to_tvp7002(sd); ++ int error = 0; ++ ++ if (device->streaming == enable) ++ return 0; ++ ++ if (enable) { ++ /* Set output state on (low impedance means stream on) */ ++ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); ++ device->streaming = enable; ++ } else { ++ /* Set output state off (high impedance means stream off) */ ++ error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); ++ if (error) ++ v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); ++ ++ device->streaming = enable; ++ } ++ ++ return error; ++} ++ ++/* ++ * tvp7002_log_status() - Print information about register settings ++ * @sd: ptr to v4l2_subdev struct ++ * ++ * Log register values of a TVP7002 decoder device. ++ * Returns zero or -EINVAL if read operation fails. ++ */ ++static int tvp7002_log_status(struct v4l2_subdev *sd) ++{ ++ const struct tvp7002_preset_definition *presets = tvp7002_presets; ++ struct tvp7002 *device = to_tvp7002(sd); ++ struct v4l2_dv_enum_preset e_preset; ++ struct v4l2_dv_preset detected; ++ int i; ++ ++ detected.preset = V4L2_DV_INVALID; ++ /* Find my current standard*/ ++ tvp7002_query_dv_preset(sd, &detected); ++ ++ /* Print standard related code values */ ++ for (i = 0; i < NUM_PRESETS; i++, presets++) ++ if (presets->preset == detected.preset) ++ break; ++ ++ if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) ++ return -EINVAL; ++ ++ v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); ++ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); ++ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); ++ if (i == NUM_PRESETS) { ++ v4l2_info(sd, "Detected DV Preset: None\n"); ++ } else { ++ if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) ++ return -EINVAL; ++ v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); ++ v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); ++ v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); ++ } ++ v4l2_info(sd, "Streaming enabled: %s\n", ++ device->streaming ? "yes" : "no"); ++ ++ /* Print the current value of the gain control */ ++ v4l2_ctrl_handler_log_status(&device->hdl, sd->name); ++ ++ return 0; ++} ++ ++/* ++ * tvp7002_enum_dv_presets() - Enum supported digital video formats ++ * @sd: pointer to standard V4L2 sub-device structure ++ * @preset: pointer to format struct ++ * ++ * Enumerate supported digital video formats. ++ */ ++static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, ++ struct v4l2_dv_enum_preset *preset) ++{ ++ /* Check requested format index is within range */ ++ if (preset->index >= NUM_PRESETS) ++ return -EINVAL; ++ ++ return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); ++} ++ ++static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ /* Check requested format index is within range */ ++ if (timings->index >= NUM_PRESETS) ++ return -EINVAL; ++ ++ timings->timings = tvp7002_presets[timings->index].timings; ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { ++ .s_ctrl = tvp7002_s_ctrl, ++}; ++ ++/* V4L2 core operation handlers */ ++static const struct v4l2_subdev_core_ops tvp7002_core_ops = { ++ .g_chip_ident = tvp7002_g_chip_ident, ++ .log_status = tvp7002_log_status, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = tvp7002_g_register, ++ .s_register = tvp7002_s_register, ++#endif ++}; ++ ++/* Specific video subsystem operation handlers */ ++static const struct v4l2_subdev_video_ops tvp7002_video_ops = { ++ .enum_dv_presets = tvp7002_enum_dv_presets, ++ .s_dv_preset = tvp7002_s_dv_preset, ++ .query_dv_preset = tvp7002_query_dv_preset, ++ .g_dv_timings = tvp7002_g_dv_timings, ++ .s_dv_timings = tvp7002_s_dv_timings, ++ .enum_dv_timings = tvp7002_enum_dv_timings, ++ .query_dv_timings = tvp7002_query_dv_timings, ++ .s_stream = tvp7002_s_stream, ++ .g_mbus_fmt = tvp7002_mbus_fmt, ++ .try_mbus_fmt = tvp7002_mbus_fmt, ++ .s_mbus_fmt = tvp7002_mbus_fmt, ++ .enum_mbus_fmt = tvp7002_enum_mbus_fmt, ++}; ++ ++/* V4L2 top level operation handlers */ ++static const struct v4l2_subdev_ops tvp7002_ops = { ++ .core = &tvp7002_core_ops, ++ .video = &tvp7002_video_ops, ++}; ++ ++/* ++ * tvp7002_probe - Probe a TVP7002 device ++ * @c: ptr to i2c_client struct ++ * @id: ptr to i2c_device_id struct ++ * ++ * Initialize the TVP7002 device ++ * Returns zero when successful, -EINVAL if register read fails or ++ * -EIO if i2c access is not available. ++ */ ++static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) ++{ ++ struct v4l2_subdev *sd; ++ struct tvp7002 *device; ++ struct v4l2_dv_preset preset; ++ int polarity_a; ++ int polarity_b; ++ u8 revision; ++ ++ int error; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(c->adapter, ++ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) ++ return -EIO; ++ ++ if (!c->dev.platform_data) { ++ v4l_err(c, "No platform data!!\n"); ++ return -ENODEV; ++ } ++ ++ device = kzalloc(sizeof(struct tvp7002), GFP_KERNEL); ++ ++ if (!device) ++ return -ENOMEM; ++ ++ sd = &device->sd; ++ device->pdata = c->dev.platform_data; ++ device->current_preset = tvp7002_presets; ++ ++ /* Tell v4l2 the device is ready */ ++ v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); ++ v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", ++ c->addr, c->adapter->name); ++ ++ error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); ++ if (error < 0) ++ goto found_error; ++ ++ /* Get revision number */ ++ v4l2_info(sd, "Rev. %02x detected.\n", revision); ++ if (revision != 0x02) ++ v4l2_info(sd, "Unknown revision detected.\n"); ++ ++ /* Initializes TVP7002 to its default values */ ++ error = tvp7002_write_inittab(sd, tvp7002_init_default); ++ ++ if (error < 0) ++ goto found_error; ++ ++ /* Set polarity information after registers have been set */ ++ polarity_a = 0x20 | device->pdata->hs_polarity << 5 ++ | device->pdata->vs_polarity << 2; ++ error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); ++ if (error < 0) ++ goto found_error; ++ ++ polarity_b = 0x01 | device->pdata->fid_polarity << 2 ++ | device->pdata->sog_polarity << 1 ++ | device->pdata->clk_polarity; ++ error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); ++ if (error < 0) ++ goto found_error; ++ ++ /* Set registers according to default video mode */ ++ preset.preset = device->current_preset->preset; ++ error = tvp7002_s_dv_preset(sd, &preset); ++ ++ v4l2_ctrl_handler_init(&device->hdl, 1); ++ v4l2_ctrl_new_std(&device->hdl, &tvp7002_ctrl_ops, ++ V4L2_CID_GAIN, 0, 255, 1, 0); ++ sd->ctrl_handler = &device->hdl; ++ if (device->hdl.error) { ++ int err = device->hdl.error; ++ ++ v4l2_ctrl_handler_free(&device->hdl); ++ kfree(device); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&device->hdl); ++ ++found_error: ++ if (error < 0) ++ kfree(device); ++ ++ return error; ++} ++ ++/* ++ * tvp7002_remove - Remove TVP7002 device support ++ * @c: ptr to i2c_client struct ++ * ++ * Reset the TVP7002 device ++ * Returns zero. ++ */ ++static int tvp7002_remove(struct i2c_client *c) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(c); ++ struct tvp7002 *device = to_tvp7002(sd); ++ ++ v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" ++ "on address 0x%x\n", c->addr); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&device->hdl); ++ kfree(device); ++ return 0; ++} ++ ++/* I2C Device ID table */ ++static const struct i2c_device_id tvp7002_id[] = { ++ { "tvp7002", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, tvp7002_id); ++ ++/* I2C driver data */ ++static struct i2c_driver tvp7002_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = TVP7002_MODULE_NAME, ++ }, ++ .probe = tvp7002_probe, ++ .remove = tvp7002_remove, ++ .id_table = tvp7002_id, ++}; ++ ++module_i2c_driver(tvp7002_driver); +diff --git a/drivers/media/i2c/tvp7002_reg.h b/drivers/media/i2c/tvp7002_reg.h +new file mode 100644 +index 0000000..0e34ca9 +--- /dev/null ++++ b/drivers/media/i2c/tvp7002_reg.h +@@ -0,0 +1,150 @@ ++/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics ++ * Digitizer with Horizontal PLL registers ++ * ++ * Copyright (C) 2009 Texas Instruments Inc ++ * Author: Santiago Nunez-Corrales ++ * ++ * This code is partially based upon the TVP5150 driver ++ * written by Mauro Carvalho Chehab (mchehab@infradead.org), ++ * the TVP514x driver written by Vaibhav Hiremath ++ * and the TVP7002 driver in the TI LSP 2.10.00.14 ++ * ++ * 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. ++ */ ++ ++/* Naming conventions ++ * ------------------ ++ * ++ * FDBK: Feedback ++ * DIV: Divider ++ * CTL: Control ++ * SEL: Select ++ * IN: Input ++ * OUT: Output ++ * R: Red ++ * G: Green ++ * B: Blue ++ * OFF: Offset ++ * THRS: Threshold ++ * DGTL: Digital ++ * LVL: Level ++ * PWR: Power ++ * MVIS: Macrovision ++ * W: Width ++ * H: Height ++ * ALGN: Alignment ++ * CLK: Clocks ++ * TOL: Tolerance ++ * BWTH: Bandwidth ++ * COEF: Coefficient ++ * STAT: Status ++ * AUTO: Automatic ++ * FLD: Field ++ * L: Line ++ */ ++ ++#define TVP7002_CHIP_REV 0x00 ++#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 ++#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 ++#define TVP7002_HPLL_CRTL 0x03 ++#define TVP7002_HPLL_PHASE_SEL 0x04 ++#define TVP7002_CLAMP_START 0x05 ++#define TVP7002_CLAMP_W 0x06 ++#define TVP7002_HSYNC_OUT_W 0x07 ++#define TVP7002_B_FINE_GAIN 0x08 ++#define TVP7002_G_FINE_GAIN 0x09 ++#define TVP7002_R_FINE_GAIN 0x0a ++#define TVP7002_B_FINE_OFF_MSBS 0x0b ++#define TVP7002_G_FINE_OFF_MSBS 0x0c ++#define TVP7002_R_FINE_OFF_MSBS 0x0d ++#define TVP7002_SYNC_CTL_1 0x0e ++#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f ++#define TVP7002_SYNC_ON_G_THRS 0x10 ++#define TVP7002_SYNC_SEPARATOR_THRS 0x11 ++#define TVP7002_HPLL_PRE_COAST 0x12 ++#define TVP7002_HPLL_POST_COAST 0x13 ++#define TVP7002_SYNC_DETECT_STAT 0x14 ++#define TVP7002_OUT_FORMATTER 0x15 ++#define TVP7002_MISC_CTL_1 0x16 ++#define TVP7002_MISC_CTL_2 0x17 ++#define TVP7002_MISC_CTL_3 0x18 ++#define TVP7002_IN_MUX_SEL_1 0x19 ++#define TVP7002_IN_MUX_SEL_2 0x1a ++#define TVP7002_B_AND_G_COARSE_GAIN 0x1b ++#define TVP7002_R_COARSE_GAIN 0x1c ++#define TVP7002_FINE_OFF_LSBS 0x1d ++#define TVP7002_B_COARSE_OFF 0x1e ++#define TVP7002_G_COARSE_OFF 0x1f ++#define TVP7002_R_COARSE_OFF 0x20 ++#define TVP7002_HSOUT_OUT_START 0x21 ++#define TVP7002_MISC_CTL_4 0x22 ++#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 ++#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 ++#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 ++#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 ++#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 ++#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 ++/* Reserved 0x29*/ ++#define TVP7002_FINE_CLAMP_CTL 0x2a ++#define TVP7002_PWR_CTL 0x2b ++#define TVP7002_ADC_SETUP 0x2c ++#define TVP7002_COARSE_CLAMP_CTL 0x2d ++#define TVP7002_SOG_CLAMP 0x2e ++#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f ++#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 ++#define TVP7002_ALC_PLACEMENT 0x31 ++/* Reserved 0x32 */ ++/* Reserved 0x33 */ ++#define TVP7002_MVIS_STRIPPER_W 0x34 ++#define TVP7002_VSYNC_ALGN 0x35 ++#define TVP7002_SYNC_BYPASS 0x36 ++#define TVP7002_L_FRAME_STAT_LSBS 0x37 ++#define TVP7002_L_FRAME_STAT_MSBS 0x38 ++#define TVP7002_CLK_L_STAT_LSBS 0x39 ++#define TVP7002_CLK_L_STAT_MSBS 0x3a ++#define TVP7002_HSYNC_W 0x3b ++#define TVP7002_VSYNC_W 0x3c ++#define TVP7002_L_LENGTH_TOL 0x3d ++/* Reserved 0x3e */ ++#define TVP7002_VIDEO_BWTH_CTL 0x3f ++#define TVP7002_AVID_START_PIXEL_LSBS 0x40 ++#define TVP7002_AVID_START_PIXEL_MSBS 0x41 ++#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 ++#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 ++#define TVP7002_VBLK_F_0_START_L_OFF 0x44 ++#define TVP7002_VBLK_F_1_START_L_OFF 0x45 ++#define TVP7002_VBLK_F_0_DURATION 0x46 ++#define TVP7002_VBLK_F_1_DURATION 0x47 ++#define TVP7002_FBIT_F_0_START_L_OFF 0x48 ++#define TVP7002_FBIT_F_1_START_L_OFF 0x49 ++#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a ++#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b ++#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c ++#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d ++#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e ++#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f ++#define TVP7002_YUV_U_G_COEF_LSBS 0x50 ++#define TVP7002_YUV_U_G_COEF_MSBS 0x51 ++#define TVP7002_YUV_U_B_COEF_LSBS 0x52 ++#define TVP7002_YUV_U_B_COEF_MSBS 0x53 ++#define TVP7002_YUV_U_R_COEF_LSBS 0x54 ++#define TVP7002_YUV_U_R_COEF_MSBS 0x55 ++#define TVP7002_YUV_V_G_COEF_LSBS 0x56 ++#define TVP7002_YUV_V_G_COEF_MSBS 0x57 ++#define TVP7002_YUV_V_B_COEF_LSBS 0x58 ++#define TVP7002_YUV_V_B_COEF_MSBS 0x59 ++#define TVP7002_YUV_V_R_COEF_LSBS 0x5a ++#define TVP7002_YUV_V_R_COEF_MSBS 0x5b ++ +diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c +new file mode 100644 +index 0000000..1e74465 +--- /dev/null ++++ b/drivers/media/i2c/upd64031a.c +@@ -0,0 +1,274 @@ ++/* ++ * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan ++ * ++ * 2003 by T.Adachi ++ * 2003 by Takeru KOMORIYA ++ * 2006 by Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* --------------------- read registers functions define -------------------- */ ++ ++/* bit masks */ ++#define GR_MODE_MASK 0xc0 ++#define DIRECT_3DYCS_CONNECT_MASK 0xc0 ++#define SYNC_CIRCUIT_MASK 0xa0 ++ ++/* -------------------------------------------------------------------------- */ ++ ++MODULE_DESCRIPTION("uPD64031A driver"); ++MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++enum { ++ R00 = 0, R01, R02, R03, R04, ++ R05, R06, R07, R08, R09, ++ R0A, R0B, R0C, R0D, R0E, R0F, ++ /* unused registers ++ R10, R11, R12, R13, R14, ++ R15, R16, R17, ++ */ ++ TOT_REGS ++}; ++ ++struct upd64031a_state { ++ struct v4l2_subdev sd; ++ u8 regs[TOT_REGS]; ++ u8 gr_mode; ++ u8 direct_3dycs_connect; ++ u8 ext_comp_sync; ++ u8 ext_vert_sync; ++}; ++ ++static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct upd64031a_state, sd); ++} ++ ++static u8 upd64031a_init[] = { ++ 0x00, 0xb8, 0x48, 0xd2, 0xe6, ++ 0x03, 0x10, 0x0b, 0xaf, 0x7f, ++ 0x00, 0x00, 0x1d, 0x5e, 0x00, ++ 0xd0 ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[2]; ++ ++ if (reg >= sizeof(buf)) ++ return 0xff; ++ i2c_master_recv(client, buf, 2); ++ return buf[reg]; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[2]; ++ ++ buf[0] = reg; ++ buf[1] = val; ++ v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); ++ if (i2c_master_send(client, buf, 2) != 2) ++ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* The input changed due to new input or channel changed */ ++static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) ++{ ++ struct upd64031a_state *state = to_state(sd); ++ u8 reg = state->regs[R00]; ++ ++ v4l2_dbg(1, debug, sd, "changed input or channel\n"); ++ upd64031a_write(sd, R00, reg | 0x10); ++ upd64031a_write(sd, R00, reg & ~0x10); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int upd64031a_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct upd64031a_state *state = to_state(sd); ++ u8 r00, r05, r08; ++ ++ state->gr_mode = (input & 3) << 6; ++ state->direct_3dycs_connect = (input & 0xc) << 4; ++ state->ext_comp_sync = ++ (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; ++ state->ext_vert_sync = ++ (input & UPD64031A_VERTICAL_EXTERNAL) << 2; ++ r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; ++ r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | ++ state->ext_comp_sync | state->ext_vert_sync; ++ r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | ++ state->direct_3dycs_connect; ++ upd64031a_write(sd, R00, r00); ++ upd64031a_write(sd, R05, r05); ++ upd64031a_write(sd, R08, r08); ++ return upd64031a_s_frequency(sd, NULL); ++} ++ ++static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); ++} ++ ++static int upd64031a_log_status(struct v4l2_subdev *sd) ++{ ++ v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", ++ upd64031a_read(sd, 0), upd64031a_read(sd, 1)); ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = upd64031a_read(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops upd64031a_core_ops = { ++ .log_status = upd64031a_log_status, ++ .g_chip_ident = upd64031a_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = upd64031a_g_register, ++ .s_register = upd64031a_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { ++ .s_frequency = upd64031a_s_frequency, ++}; ++ ++static const struct v4l2_subdev_video_ops upd64031a_video_ops = { ++ .s_routing = upd64031a_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops upd64031a_ops = { ++ .core = &upd64031a_core_ops, ++ .tuner = &upd64031a_tuner_ops, ++ .video = &upd64031a_video_ops, ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* i2c implementation */ ++ ++static int upd64031a_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct upd64031a_state *state; ++ struct v4l2_subdev *sd; ++ int i; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct upd64031a_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); ++ memcpy(state->regs, upd64031a_init, sizeof(state->regs)); ++ state->gr_mode = UPD64031A_GR_ON << 6; ++ state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; ++ state->ext_comp_sync = state->ext_vert_sync = 0; ++ for (i = 0; i < TOT_REGS; i++) ++ upd64031a_write(sd, i, state->regs[i]); ++ return 0; ++} ++ ++static int upd64031a_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id upd64031a_id[] = { ++ { "upd64031a", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, upd64031a_id); ++ ++static struct i2c_driver upd64031a_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "upd64031a", ++ }, ++ .probe = upd64031a_probe, ++ .remove = upd64031a_remove, ++ .id_table = upd64031a_id, ++}; ++ ++module_i2c_driver(upd64031a_driver); +diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c +new file mode 100644 +index 0000000..75d6acc +--- /dev/null ++++ b/drivers/media/i2c/upd64083.c +@@ -0,0 +1,246 @@ ++/* ++ * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver ++ * ++ * 2003 by T.Adachi (tadachi@tadachi-net.com) ++ * 2003 by Takeru KOMORIYA ++ * 2006 by Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("uPD64083 driver"); ++MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++static bool debug; ++module_param(debug, bool, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++enum { ++ R00 = 0, R01, R02, R03, R04, ++ R05, R06, R07, R08, R09, ++ R0A, R0B, R0C, R0D, R0E, R0F, ++ R10, R11, R12, R13, R14, ++ R15, R16, ++ TOT_REGS ++}; ++ ++struct upd64083_state { ++ struct v4l2_subdev sd; ++ u8 mode; ++ u8 ext_y_adc; ++ u8 regs[TOT_REGS]; ++}; ++ ++static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct upd64083_state, sd); ++} ++ ++/* Initial values when used in combination with the ++ NEC upd64031a ghost reduction chip. */ ++static u8 upd64083_init[] = { ++ 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ ++ 0x36, 0xdd, 0x05, 0x56, 0x48, ++ 0x00, 0x3a, 0xa0, 0x05, 0x08, ++ 0x44, 0x60, 0x08, 0x52, 0xf8, ++ 0x53, 0x60, 0x10 ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[2]; ++ ++ buf[0] = reg; ++ buf[1] = val; ++ v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); ++ if (i2c_master_send(client, buf, 2) != 2) ++ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[7]; ++ ++ if (reg >= sizeof(buf)) ++ return 0xff; ++ i2c_master_recv(client, buf, sizeof(buf)); ++ return buf[reg]; ++} ++#endif ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int upd64083_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct upd64083_state *state = to_state(sd); ++ u8 r00, r02; ++ ++ if (input > 7 || (input & 6) == 6) ++ return -EINVAL; ++ state->mode = (input & 3) << 6; ++ state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; ++ r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; ++ r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; ++ upd64083_write(sd, R00, r00); ++ upd64083_write(sd, R02, r02); ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = upd64083_read(sd, reg->reg & 0xff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); ++} ++ ++static int upd64083_log_status(struct v4l2_subdev *sd) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[7]; ++ ++ i2c_master_recv(client, buf, 7); ++ v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " ++ "SA04=%02x SA05=%02x SA06=%02x\n", ++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops upd64083_core_ops = { ++ .log_status = upd64083_log_status, ++ .g_chip_ident = upd64083_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = upd64083_g_register, ++ .s_register = upd64083_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops upd64083_video_ops = { ++ .s_routing = upd64083_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops upd64083_ops = { ++ .core = &upd64083_core_ops, ++ .video = &upd64083_video_ops, ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* i2c implementation */ ++ ++static int upd64083_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct upd64083_state *state; ++ struct v4l2_subdev *sd; ++ int i; ++ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct upd64083_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &upd64083_ops); ++ /* Initially assume that a ghost reduction chip is present */ ++ state->mode = 0; /* YCS mode */ ++ state->ext_y_adc = (1 << 5); ++ memcpy(state->regs, upd64083_init, TOT_REGS); ++ for (i = 0; i < TOT_REGS; i++) ++ upd64083_write(sd, i, state->regs[i]); ++ return 0; ++} ++ ++static int upd64083_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id upd64083_id[] = { ++ { "upd64083", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, upd64083_id); ++ ++static struct i2c_driver upd64083_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "upd64083", ++ }, ++ .probe = upd64083_probe, ++ .remove = upd64083_remove, ++ .id_table = upd64083_id, ++}; ++ ++module_i2c_driver(upd64083_driver); +diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c +new file mode 100644 +index 0000000..7cfbc9d +--- /dev/null ++++ b/drivers/media/i2c/vp27smpx.c +@@ -0,0 +1,211 @@ ++/* ++ * vp27smpx - driver version 0.0.1 ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * Based on a tvaudio patch from Takahiro Adachi ++ * and Kazuhiko Kawakami ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("vp27smpx driver"); ++MODULE_AUTHOR("Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct vp27smpx_state { ++ struct v4l2_subdev sd; ++ int radio; ++ u32 audmode; ++}; ++ ++static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct vp27smpx_state, sd); ++} ++ ++static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 data[3] = { 0x00, 0x00, 0x04 }; ++ ++ switch (audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_LANG1: ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ data[1] = 0x01; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ data[1] = 0x02; ++ break; ++ } ++ ++ if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) ++ v4l2_err(sd, "I/O error setting audmode\n"); ++ else ++ state->audmode = audmode; ++} ++ ++static int vp27smpx_s_radio(struct v4l2_subdev *sd) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ ++ state->radio = 1; ++ return 0; ++} ++ ++static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ ++ state->radio = 0; ++ return 0; ++} ++ ++static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ ++ if (!state->radio) ++ vp27smpx_set_audmode(sd, vt->audmode); ++ return 0; ++} ++ ++static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ ++ if (state->radio) ++ return 0; ++ vt->audmode = state->audmode; ++ vt->capability = V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; ++ vt->rxsubchans = V4L2_TUNER_SUB_MONO; ++ return 0; ++} ++ ++static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); ++} ++ ++static int vp27smpx_log_status(struct v4l2_subdev *sd) ++{ ++ struct vp27smpx_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, ++ state->radio ? " (Radio)" : ""); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { ++ .log_status = vp27smpx_log_status, ++ .g_chip_ident = vp27smpx_g_chip_ident, ++ .s_std = vp27smpx_s_std, ++}; ++ ++static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { ++ .s_radio = vp27smpx_s_radio, ++ .s_tuner = vp27smpx_s_tuner, ++ .g_tuner = vp27smpx_g_tuner, ++}; ++ ++static const struct v4l2_subdev_ops vp27smpx_ops = { ++ .core = &vp27smpx_core_ops, ++ .tuner = &vp27smpx_tuner_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* i2c implementation */ ++ ++/* ++ * Generic i2c probe ++ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' ++ */ ++ ++static int vp27smpx_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct vp27smpx_state *state; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); ++ state->audmode = V4L2_TUNER_MODE_STEREO; ++ ++ /* initialize vp27smpx */ ++ vp27smpx_set_audmode(sd, state->audmode); ++ return 0; ++} ++ ++static int vp27smpx_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_device_id vp27smpx_id[] = { ++ { "vp27smpx", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, vp27smpx_id); ++ ++static struct i2c_driver vp27smpx_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "vp27smpx", ++ }, ++ .probe = vp27smpx_probe, ++ .remove = vp27smpx_remove, ++ .id_table = vp27smpx_id, ++}; ++ ++module_i2c_driver(vp27smpx_driver); +diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c +new file mode 100644 +index 0000000..2f67b4c +--- /dev/null ++++ b/drivers/media/i2c/vpx3220.c +@@ -0,0 +1,591 @@ ++/* ++ * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 ++ * ++ * Copyright (C) 2001 Laurent Pinchart ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); ++MODULE_AUTHOR("Laurent Pinchart"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++#define VPX_TIMEOUT_COUNT 10 ++ ++/* ----------------------------------------------------------------------- */ ++ ++struct vpx3220 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ unsigned char reg[255]; ++ ++ v4l2_std_id norm; ++ int ident; ++ int input; ++ int enable; ++}; ++ ++static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct vpx3220, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; ++} ++ ++static char *inputs[] = { "internal", "composite", "svideo" }; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct vpx3220 *decoder = i2c_get_clientdata(client); ++ ++ decoder->reg[reg] = value; ++ return i2c_smbus_write_byte_data(client, reg, value); ++} ++ ++static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return i2c_smbus_read_byte_data(client, reg); ++} ++ ++static int vpx3220_fp_status(struct v4l2_subdev *sd) ++{ ++ unsigned char status; ++ unsigned int i; ++ ++ for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { ++ status = vpx3220_read(sd, 0x29); ++ ++ if (!(status & 4)) ++ return 0; ++ ++ udelay(10); ++ ++ if (need_resched()) ++ cond_resched(); ++ } ++ ++ return -1; ++} ++ ++static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ /* Write the 16-bit address to the FPWR register */ ++ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { ++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); ++ return -1; ++ } ++ ++ if (vpx3220_fp_status(sd) < 0) ++ return -1; ++ ++ /* Write the 16-bit data to the FPDAT register */ ++ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { ++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ s16 data; ++ ++ /* Write the 16-bit address to the FPRD register */ ++ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { ++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); ++ return -1; ++ } ++ ++ if (vpx3220_fp_status(sd) < 0) ++ return -1; ++ ++ /* Read the 16-bit data from the FPDAT register */ ++ data = i2c_smbus_read_word_data(client, 0x28); ++ if (data == -1) { ++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); ++ return -1; ++ } ++ ++ return swab16(data); ++} ++ ++static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) ++{ ++ u8 reg; ++ int ret = -1; ++ ++ while (len >= 2) { ++ reg = *data++; ++ ret = vpx3220_write(sd, reg, *data++); ++ if (ret < 0) ++ break; ++ len -= 2; ++ } ++ ++ return ret; ++} ++ ++static int vpx3220_write_fp_block(struct v4l2_subdev *sd, ++ const u16 *data, unsigned int len) ++{ ++ u8 reg; ++ int ret = 0; ++ ++ while (len > 1) { ++ reg = *data++; ++ ret |= vpx3220_fp_write(sd, reg, *data++); ++ len -= 2; ++ } ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const unsigned short init_ntsc[] = { ++ 0x1c, 0x00, /* NTSC tint angle */ ++ 0x88, 17, /* Window 1 vertical */ ++ 0x89, 240, /* Vertical lines in */ ++ 0x8a, 240, /* Vertical lines out */ ++ 0x8b, 000, /* Horizontal begin */ ++ 0x8c, 640, /* Horizontal length */ ++ 0x8d, 640, /* Number of pixels */ ++ 0x8f, 0xc00, /* Disable window 2 */ ++ 0xf0, 0x73, /* 13.5 MHz transport, Forced ++ * mode, latch windows */ ++ 0xf2, 0x13, /* NTSC M, composite input */ ++ 0xe7, 0x1e1, /* Enable vertical standard ++ * locking @ 240 lines */ ++}; ++ ++static const unsigned short init_pal[] = { ++ 0x88, 23, /* Window 1 vertical begin */ ++ 0x89, 288, /* Vertical lines in (16 lines ++ * skipped by the VFE) */ ++ 0x8a, 288, /* Vertical lines out (16 lines ++ * skipped by the VFE) */ ++ 0x8b, 16, /* Horizontal begin */ ++ 0x8c, 768, /* Horizontal length */ ++ 0x8d, 784, /* Number of pixels ++ * Must be >= Horizontal begin + Horizontal length */ ++ 0x8f, 0xc00, /* Disable window 2 */ ++ 0xf0, 0x77, /* 13.5 MHz transport, Forced ++ * mode, latch windows */ ++ 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ ++ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ ++}; ++ ++static const unsigned short init_secam[] = { ++ 0x88, 23, /* Window 1 vertical begin */ ++ 0x89, 288, /* Vertical lines in (16 lines ++ * skipped by the VFE) */ ++ 0x8a, 288, /* Vertical lines out (16 lines ++ * skipped by the VFE) */ ++ 0x8b, 16, /* Horizontal begin */ ++ 0x8c, 768, /* Horizontal length */ ++ 0x8d, 784, /* Number of pixels ++ * Must be >= Horizontal begin + Horizontal length */ ++ 0x8f, 0xc00, /* Disable window 2 */ ++ 0xf0, 0x77, /* 13.5 MHz transport, Forced ++ * mode, latch windows */ ++ 0xf2, 0x3d5, /* SECAM, composite input */ ++ 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ ++}; ++ ++static const unsigned char init_common[] = { ++ 0xf2, 0x00, /* Disable all outputs */ ++ 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN ++ * (clamp off) */ ++ 0xd8, 0xa8, /* HREF/VREF active high, VREF ++ * pulse = 2, Odd/Even flag */ ++ 0x20, 0x03, /* IF compensation 0dB/oct */ ++ 0xe0, 0xff, /* Open up all comparators */ ++ 0xe1, 0x00, ++ 0xe2, 0x7f, ++ 0xe3, 0x80, ++ 0xe4, 0x7f, ++ 0xe5, 0x80, ++ 0xe6, 0x00, /* Brightness set to 0 */ ++ 0xe7, 0xe0, /* Contrast to 1.0, noise shaping ++ * 10 to 8 2-bit error diffusion */ ++ 0xe8, 0xf8, /* YUV422, CbCr binary offset, ++ * ... (p.32) */ ++ 0xea, 0x18, /* LLC2 connected, output FIFO ++ * reset with VACTintern */ ++ 0xf0, 0x8a, /* Half full level to 10, bus ++ * shuffler [7:0, 23:16, 15:8] */ ++ 0xf1, 0x18, /* Single clock, sync mode, no ++ * FE delay, no HLEN counter */ ++ 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# ++ * strength to 2 */ ++ 0xf9, 0x24, /* Port B, HREF, VREF, PREF & ++ * ALPHA strength to 4 */ ++}; ++ ++static const unsigned short init_fp[] = { ++ 0x59, 0, ++ 0xa0, 2070, /* ACC reference */ ++ 0xa3, 0, ++ 0xa4, 0, ++ 0xa8, 30, ++ 0xb2, 768, ++ 0xbe, 27, ++ 0x58, 0, ++ 0x26, 0, ++ 0x4b, 0x298, /* PLL gain */ ++}; ++ ++ ++static int vpx3220_init(struct v4l2_subdev *sd, u32 val) ++{ ++ struct vpx3220 *decoder = to_vpx3220(sd); ++ ++ vpx3220_write_block(sd, init_common, sizeof(init_common)); ++ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); ++ if (decoder->norm & V4L2_STD_NTSC) ++ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); ++ else if (decoder->norm & V4L2_STD_PAL) ++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); ++ else if (decoder->norm & V4L2_STD_SECAM) ++ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); ++ else ++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); ++ return 0; ++} ++ ++static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) ++{ ++ int res = V4L2_IN_ST_NO_SIGNAL, status; ++ v4l2_std_id std = 0; ++ ++ status = vpx3220_fp_read(sd, 0x0f3); ++ ++ v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); ++ ++ if (status < 0) ++ return status; ++ ++ if ((status & 0x20) == 0) { ++ res = 0; ++ ++ switch (status & 0x18) { ++ case 0x00: ++ case 0x10: ++ case 0x14: ++ case 0x18: ++ std = V4L2_STD_PAL; ++ break; ++ ++ case 0x08: ++ std = V4L2_STD_SECAM; ++ break; ++ ++ case 0x04: ++ case 0x0c: ++ case 0x1c: ++ std = V4L2_STD_NTSC; ++ break; ++ } ++ } ++ if (pstd) ++ *pstd = std; ++ if (pstatus) ++ *pstatus = res; ++ return 0; ++} ++ ++static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) ++{ ++ v4l2_dbg(1, debug, sd, "querystd\n"); ++ return vpx3220_status(sd, NULL, std); ++} ++ ++static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) ++{ ++ v4l2_dbg(1, debug, sd, "g_input_status\n"); ++ return vpx3220_status(sd, status, NULL); ++} ++ ++static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct vpx3220 *decoder = to_vpx3220(sd); ++ int temp_input; ++ ++ /* Here we back up the input selection because it gets ++ overwritten when we fill the registers with the ++ chosen video norm */ ++ temp_input = vpx3220_fp_read(sd, 0xf2); ++ ++ v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); ++ if (std & V4L2_STD_NTSC) { ++ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); ++ v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); ++ } else if (std & V4L2_STD_PAL) { ++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); ++ v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); ++ } else if (std & V4L2_STD_SECAM) { ++ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); ++ v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); ++ } else { ++ return -EINVAL; ++ } ++ ++ decoder->norm = std; ++ ++ /* And here we set the backed up video input again */ ++ vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); ++ udelay(10); ++ return 0; ++} ++ ++static int vpx3220_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ int data; ++ ++ /* RJ: input = 0: ST8 (PCTV) input ++ input = 1: COMPOSITE input ++ input = 2: SVHS input */ ++ ++ const int input_vals[3][2] = { ++ {0x0c, 0}, ++ {0x0d, 0}, ++ {0x0e, 1} ++ }; ++ ++ if (input > 2) ++ return -EINVAL; ++ ++ v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); ++ ++ vpx3220_write(sd, 0x33, input_vals[input][0]); ++ ++ data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); ++ if (data < 0) ++ return data; ++ /* 0x0010 is required to latch the setting */ ++ vpx3220_fp_write(sd, 0xf2, ++ data | (input_vals[input][1] << 5) | 0x0010); ++ ++ udelay(10); ++ return 0; ++} ++ ++static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); ++ ++ vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); ++ return 0; ++} ++ ++static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ vpx3220_write(sd, 0xe6, ctrl->val); ++ return 0; ++ case V4L2_CID_CONTRAST: ++ /* Bit 7 and 8 is for noise shaping */ ++ vpx3220_write(sd, 0xe7, ctrl->val + 192); ++ return 0; ++ case V4L2_CID_SATURATION: ++ vpx3220_fp_write(sd, 0xa0, ctrl->val); ++ return 0; ++ case V4L2_CID_HUE: ++ vpx3220_fp_write(sd, 0x1c, ctrl->val); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct vpx3220 *decoder = to_vpx3220(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { ++ .s_ctrl = vpx3220_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops vpx3220_core_ops = { ++ .g_chip_ident = vpx3220_g_chip_ident, ++ .init = vpx3220_init, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = vpx3220_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops vpx3220_video_ops = { ++ .s_routing = vpx3220_s_routing, ++ .s_stream = vpx3220_s_stream, ++ .querystd = vpx3220_querystd, ++ .g_input_status = vpx3220_g_input_status, ++}; ++ ++static const struct v4l2_subdev_ops vpx3220_ops = { ++ .core = &vpx3220_core_ops, ++ .video = &vpx3220_video_ops, ++}; ++ ++/* ----------------------------------------------------------------------- ++ * Client management code ++ */ ++ ++static int vpx3220_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct vpx3220 *decoder; ++ struct v4l2_subdev *sd; ++ const char *name = NULL; ++ u8 ver; ++ u16 pn; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, ++ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) ++ return -ENODEV; ++ ++ decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); ++ if (decoder == NULL) ++ return -ENOMEM; ++ sd = &decoder->sd; ++ v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); ++ decoder->norm = V4L2_STD_PAL; ++ decoder->input = 0; ++ decoder->enable = 1; ++ v4l2_ctrl_handler_init(&decoder->hdl, 4); ++ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); ++ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 63, 1, 32); ++ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 4095, 1, 2048); ++ v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, ++ V4L2_CID_HUE, -512, 511, 1, 0); ++ sd->ctrl_handler = &decoder->hdl; ++ if (decoder->hdl.error) { ++ int err = decoder->hdl.error; ++ ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return err; ++ } ++ v4l2_ctrl_handler_setup(&decoder->hdl); ++ ++ ver = i2c_smbus_read_byte_data(client, 0x00); ++ pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + ++ i2c_smbus_read_byte_data(client, 0x01); ++ decoder->ident = V4L2_IDENT_VPX3220A; ++ if (ver == 0xec) { ++ switch (pn) { ++ case 0x4680: ++ name = "vpx3220a"; ++ break; ++ case 0x4260: ++ name = "vpx3216b"; ++ decoder->ident = V4L2_IDENT_VPX3216B; ++ break; ++ case 0x4280: ++ name = "vpx3214c"; ++ decoder->ident = V4L2_IDENT_VPX3214C; ++ break; ++ } ++ } ++ if (name) ++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, ++ client->addr << 1, client->adapter->name); ++ else ++ v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", ++ ver, pn, client->addr << 1, client->adapter->name); ++ ++ vpx3220_write_block(sd, init_common, sizeof(init_common)); ++ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); ++ /* Default to PAL */ ++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); ++ return 0; ++} ++ ++static int vpx3220_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct vpx3220 *decoder = to_vpx3220(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&decoder->hdl); ++ kfree(decoder); ++ return 0; ++} ++ ++static const struct i2c_device_id vpx3220_id[] = { ++ { "vpx3220a", 0 }, ++ { "vpx3216b", 0 }, ++ { "vpx3214c", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, vpx3220_id); ++ ++static struct i2c_driver vpx3220_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "vpx3220", ++ }, ++ .probe = vpx3220_probe, ++ .remove = vpx3220_remove, ++ .id_table = vpx3220_id, ++}; ++ ++module_i2c_driver(vpx3220_driver); +diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c +new file mode 100644 +index 0000000..f434a19 +--- /dev/null ++++ b/drivers/media/i2c/vs6624.c +@@ -0,0 +1,917 @@ ++/* ++ * vs6624.c ST VS6624 CMOS image sensor driver ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "vs6624_regs.h" ++ ++#define VGA_WIDTH 640 ++#define VGA_HEIGHT 480 ++#define QVGA_WIDTH 320 ++#define QVGA_HEIGHT 240 ++#define QQVGA_WIDTH 160 ++#define QQVGA_HEIGHT 120 ++#define CIF_WIDTH 352 ++#define CIF_HEIGHT 288 ++#define QCIF_WIDTH 176 ++#define QCIF_HEIGHT 144 ++#define QQCIF_WIDTH 88 ++#define QQCIF_HEIGHT 72 ++ ++#define MAX_FRAME_RATE 30 ++ ++struct vs6624 { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct v4l2_fract frame_rate; ++ struct v4l2_mbus_framefmt fmt; ++ unsigned ce_pin; ++}; ++ ++static const struct vs6624_format { ++ enum v4l2_mbus_pixelcode mbus_code; ++ enum v4l2_colorspace colorspace; ++} vs6624_formats[] = { ++ { ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ }, ++}; ++ ++static struct v4l2_mbus_framefmt vs6624_default_fmt = { ++ .width = VGA_WIDTH, ++ .height = VGA_HEIGHT, ++ .code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .field = V4L2_FIELD_NONE, ++ .colorspace = V4L2_COLORSPACE_JPEG, ++}; ++ ++static const u16 vs6624_p1[] = { ++ 0x8104, 0x03, ++ 0x8105, 0x01, ++ 0xc900, 0x03, ++ 0xc904, 0x47, ++ 0xc905, 0x10, ++ 0xc906, 0x80, ++ 0xc907, 0x3a, ++ 0x903a, 0x02, ++ 0x903b, 0x47, ++ 0x903c, 0x15, ++ 0xc908, 0x31, ++ 0xc909, 0xdc, ++ 0xc90a, 0x80, ++ 0xc90b, 0x44, ++ 0x9044, 0x02, ++ 0x9045, 0x31, ++ 0x9046, 0xe2, ++ 0xc90c, 0x07, ++ 0xc90d, 0xe0, ++ 0xc90e, 0x80, ++ 0xc90f, 0x47, ++ 0x9047, 0x90, ++ 0x9048, 0x83, ++ 0x9049, 0x81, ++ 0x904a, 0xe0, ++ 0x904b, 0x60, ++ 0x904c, 0x08, ++ 0x904d, 0x90, ++ 0x904e, 0xc0, ++ 0x904f, 0x43, ++ 0x9050, 0x74, ++ 0x9051, 0x01, ++ 0x9052, 0xf0, ++ 0x9053, 0x80, ++ 0x9054, 0x05, ++ 0x9055, 0xE4, ++ 0x9056, 0x90, ++ 0x9057, 0xc0, ++ 0x9058, 0x43, ++ 0x9059, 0xf0, ++ 0x905a, 0x02, ++ 0x905b, 0x07, ++ 0x905c, 0xec, ++ 0xc910, 0x5d, ++ 0xc911, 0xca, ++ 0xc912, 0x80, ++ 0xc913, 0x5d, ++ 0x905d, 0xa3, ++ 0x905e, 0x04, ++ 0x905f, 0xf0, ++ 0x9060, 0xa3, ++ 0x9061, 0x04, ++ 0x9062, 0xf0, ++ 0x9063, 0x22, ++ 0xc914, 0x72, ++ 0xc915, 0x92, ++ 0xc916, 0x80, ++ 0xc917, 0x64, ++ 0x9064, 0x74, ++ 0x9065, 0x01, ++ 0x9066, 0x02, ++ 0x9067, 0x72, ++ 0x9068, 0x95, ++ 0xc918, 0x47, ++ 0xc919, 0xf2, ++ 0xc91a, 0x81, ++ 0xc91b, 0x69, ++ 0x9169, 0x74, ++ 0x916a, 0x02, ++ 0x916b, 0xf0, ++ 0x916c, 0xec, ++ 0x916d, 0xb4, ++ 0x916e, 0x10, ++ 0x916f, 0x0a, ++ 0x9170, 0x90, ++ 0x9171, 0x80, ++ 0x9172, 0x16, ++ 0x9173, 0xe0, ++ 0x9174, 0x70, ++ 0x9175, 0x04, ++ 0x9176, 0x90, ++ 0x9177, 0xd3, ++ 0x9178, 0xc4, ++ 0x9179, 0xf0, ++ 0x917a, 0x22, ++ 0xc91c, 0x0a, ++ 0xc91d, 0xbe, ++ 0xc91e, 0x80, ++ 0xc91f, 0x73, ++ 0x9073, 0xfc, ++ 0x9074, 0xa3, ++ 0x9075, 0xe0, ++ 0x9076, 0xf5, ++ 0x9077, 0x82, ++ 0x9078, 0x8c, ++ 0x9079, 0x83, ++ 0x907a, 0xa3, ++ 0x907b, 0xa3, ++ 0x907c, 0xe0, ++ 0x907d, 0xfc, ++ 0x907e, 0xa3, ++ 0x907f, 0xe0, ++ 0x9080, 0xc3, ++ 0x9081, 0x9f, ++ 0x9082, 0xff, ++ 0x9083, 0xec, ++ 0x9084, 0x9e, ++ 0x9085, 0xfe, ++ 0x9086, 0x02, ++ 0x9087, 0x0a, ++ 0x9088, 0xea, ++ 0xc920, 0x47, ++ 0xc921, 0x38, ++ 0xc922, 0x80, ++ 0xc923, 0x89, ++ 0x9089, 0xec, ++ 0x908a, 0xd3, ++ 0x908b, 0x94, ++ 0x908c, 0x20, ++ 0x908d, 0x40, ++ 0x908e, 0x01, ++ 0x908f, 0x1c, ++ 0x9090, 0x90, ++ 0x9091, 0xd3, ++ 0x9092, 0xd4, ++ 0x9093, 0xec, ++ 0x9094, 0xf0, ++ 0x9095, 0x02, ++ 0x9096, 0x47, ++ 0x9097, 0x3d, ++ 0xc924, 0x45, ++ 0xc925, 0xca, ++ 0xc926, 0x80, ++ 0xc927, 0x98, ++ 0x9098, 0x12, ++ 0x9099, 0x77, ++ 0x909a, 0xd6, ++ 0x909b, 0x02, ++ 0x909c, 0x45, ++ 0x909d, 0xcd, ++ 0xc928, 0x20, ++ 0xc929, 0xd5, ++ 0xc92a, 0x80, ++ 0xc92b, 0x9e, ++ 0x909e, 0x90, ++ 0x909f, 0x82, ++ 0x90a0, 0x18, ++ 0x90a1, 0xe0, ++ 0x90a2, 0xb4, ++ 0x90a3, 0x03, ++ 0x90a4, 0x0e, ++ 0x90a5, 0x90, ++ 0x90a6, 0x83, ++ 0x90a7, 0xbf, ++ 0x90a8, 0xe0, ++ 0x90a9, 0x60, ++ 0x90aa, 0x08, ++ 0x90ab, 0x90, ++ 0x90ac, 0x81, ++ 0x90ad, 0xfc, ++ 0x90ae, 0xe0, ++ 0x90af, 0xff, ++ 0x90b0, 0xc3, ++ 0x90b1, 0x13, ++ 0x90b2, 0xf0, ++ 0x90b3, 0x90, ++ 0x90b4, 0x81, ++ 0x90b5, 0xfc, ++ 0x90b6, 0xe0, ++ 0x90b7, 0xff, ++ 0x90b8, 0x02, ++ 0x90b9, 0x20, ++ 0x90ba, 0xda, ++ 0xc92c, 0x70, ++ 0xc92d, 0xbc, ++ 0xc92e, 0x80, ++ 0xc92f, 0xbb, ++ 0x90bb, 0x90, ++ 0x90bc, 0x82, ++ 0x90bd, 0x18, ++ 0x90be, 0xe0, ++ 0x90bf, 0xb4, ++ 0x90c0, 0x03, ++ 0x90c1, 0x06, ++ 0x90c2, 0x90, ++ 0x90c3, 0xc1, ++ 0x90c4, 0x06, ++ 0x90c5, 0x74, ++ 0x90c6, 0x05, ++ 0x90c7, 0xf0, ++ 0x90c8, 0x90, ++ 0x90c9, 0xd3, ++ 0x90ca, 0xa0, ++ 0x90cb, 0x02, ++ 0x90cc, 0x70, ++ 0x90cd, 0xbf, ++ 0xc930, 0x72, ++ 0xc931, 0x21, ++ 0xc932, 0x81, ++ 0xc933, 0x3b, ++ 0x913b, 0x7d, ++ 0x913c, 0x02, ++ 0x913d, 0x7f, ++ 0x913e, 0x7b, ++ 0x913f, 0x02, ++ 0x9140, 0x72, ++ 0x9141, 0x25, ++ 0xc934, 0x28, ++ 0xc935, 0xae, ++ 0xc936, 0x80, ++ 0xc937, 0xd2, ++ 0x90d2, 0xf0, ++ 0x90d3, 0x90, ++ 0x90d4, 0xd2, ++ 0x90d5, 0x0a, ++ 0x90d6, 0x02, ++ 0x90d7, 0x28, ++ 0x90d8, 0xb4, ++ 0xc938, 0x28, ++ 0xc939, 0xb1, ++ 0xc93a, 0x80, ++ 0xc93b, 0xd9, ++ 0x90d9, 0x90, ++ 0x90da, 0x83, ++ 0x90db, 0xba, ++ 0x90dc, 0xe0, ++ 0x90dd, 0xff, ++ 0x90de, 0x90, ++ 0x90df, 0xd2, ++ 0x90e0, 0x08, ++ 0x90e1, 0xe0, ++ 0x90e2, 0xe4, ++ 0x90e3, 0xef, ++ 0x90e4, 0xf0, ++ 0x90e5, 0xa3, ++ 0x90e6, 0xe0, ++ 0x90e7, 0x74, ++ 0x90e8, 0xff, ++ 0x90e9, 0xf0, ++ 0x90ea, 0x90, ++ 0x90eb, 0xd2, ++ 0x90ec, 0x0a, ++ 0x90ed, 0x02, ++ 0x90ee, 0x28, ++ 0x90ef, 0xb4, ++ 0xc93c, 0x29, ++ 0xc93d, 0x79, ++ 0xc93e, 0x80, ++ 0xc93f, 0xf0, ++ 0x90f0, 0xf0, ++ 0x90f1, 0x90, ++ 0x90f2, 0xd2, ++ 0x90f3, 0x0e, ++ 0x90f4, 0x02, ++ 0x90f5, 0x29, ++ 0x90f6, 0x7f, ++ 0xc940, 0x29, ++ 0xc941, 0x7c, ++ 0xc942, 0x80, ++ 0xc943, 0xf7, ++ 0x90f7, 0x90, ++ 0x90f8, 0x83, ++ 0x90f9, 0xba, ++ 0x90fa, 0xe0, ++ 0x90fb, 0xff, ++ 0x90fc, 0x90, ++ 0x90fd, 0xd2, ++ 0x90fe, 0x0c, ++ 0x90ff, 0xe0, ++ 0x9100, 0xe4, ++ 0x9101, 0xef, ++ 0x9102, 0xf0, ++ 0x9103, 0xa3, ++ 0x9104, 0xe0, ++ 0x9105, 0x74, ++ 0x9106, 0xff, ++ 0x9107, 0xf0, ++ 0x9108, 0x90, ++ 0x9109, 0xd2, ++ 0x910a, 0x0e, ++ 0x910b, 0x02, ++ 0x910c, 0x29, ++ 0x910d, 0x7f, ++ 0xc944, 0x2a, ++ 0xc945, 0x42, ++ 0xc946, 0x81, ++ 0xc947, 0x0e, ++ 0x910e, 0xf0, ++ 0x910f, 0x90, ++ 0x9110, 0xd2, ++ 0x9111, 0x12, ++ 0x9112, 0x02, ++ 0x9113, 0x2a, ++ 0x9114, 0x48, ++ 0xc948, 0x2a, ++ 0xc949, 0x45, ++ 0xc94a, 0x81, ++ 0xc94b, 0x15, ++ 0x9115, 0x90, ++ 0x9116, 0x83, ++ 0x9117, 0xba, ++ 0x9118, 0xe0, ++ 0x9119, 0xff, ++ 0x911a, 0x90, ++ 0x911b, 0xd2, ++ 0x911c, 0x10, ++ 0x911d, 0xe0, ++ 0x911e, 0xe4, ++ 0x911f, 0xef, ++ 0x9120, 0xf0, ++ 0x9121, 0xa3, ++ 0x9122, 0xe0, ++ 0x9123, 0x74, ++ 0x9124, 0xff, ++ 0x9125, 0xf0, ++ 0x9126, 0x90, ++ 0x9127, 0xd2, ++ 0x9128, 0x12, ++ 0x9129, 0x02, ++ 0x912a, 0x2a, ++ 0x912b, 0x48, ++ 0xc900, 0x01, ++ 0x0000, 0x00, ++}; ++ ++static const u16 vs6624_p2[] = { ++ 0x806f, 0x01, ++ 0x058c, 0x01, ++ 0x0000, 0x00, ++}; ++ ++static const u16 vs6624_run_setup[] = { ++ 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ ++ VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ ++ VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ ++ VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ ++ VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ ++ VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ ++ VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ ++ VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ ++ VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ ++ VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ ++ VS6624_NORA_USAGE, 0x04, /* Nora usage */ ++ VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ ++ VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ ++ VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ ++ VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ ++ VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ ++ VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ ++ VS6624_F2B_DISABLE, 0x00, /* Disable */ ++ 0x1d8a, 0x30, /* MAXWeightHigh */ ++ 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ ++ 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ ++ 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ ++ 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ ++ 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ ++ 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ ++ 0x1e08, 0x06, /* MAXWeightLow */ ++ 0x1e0a, 0x0a, /* MAXWeightHigh */ ++ 0x1601, 0x3a, /* Red A MSB */ ++ 0x1602, 0x14, /* Red A LSB */ ++ 0x1605, 0x3b, /* Blue A MSB */ ++ 0x1606, 0x85, /* BLue A LSB */ ++ 0x1609, 0x3b, /* RED B MSB */ ++ 0x160a, 0x85, /* RED B LSB */ ++ 0x160d, 0x3a, /* Blue B MSB */ ++ 0x160e, 0x14, /* Blue B LSB */ ++ 0x1611, 0x30, /* Max Distance from Locus MSB */ ++ 0x1612, 0x8f, /* Max Distance from Locus MSB */ ++ 0x1614, 0x01, /* Enable constrainer */ ++ 0x0000, 0x00, ++}; ++ ++static const u16 vs6624_default[] = { ++ VS6624_CONTRAST0, 0x84, ++ VS6624_SATURATION0, 0x75, ++ VS6624_GAMMA0, 0x11, ++ VS6624_CONTRAST1, 0x84, ++ VS6624_SATURATION1, 0x75, ++ VS6624_GAMMA1, 0x11, ++ VS6624_MAN_RG, 0x80, ++ VS6624_MAN_GG, 0x80, ++ VS6624_MAN_BG, 0x80, ++ VS6624_WB_MODE, 0x1, ++ VS6624_EXPO_COMPENSATION, 0xfe, ++ VS6624_EXPO_METER, 0x0, ++ VS6624_LIGHT_FREQ, 0x64, ++ VS6624_PEAK_GAIN, 0xe, ++ VS6624_PEAK_LOW_THR, 0x28, ++ VS6624_HMIRROR0, 0x0, ++ VS6624_VFLIP0, 0x0, ++ VS6624_ZOOM_HSTEP0_MSB, 0x0, ++ VS6624_ZOOM_HSTEP0_LSB, 0x1, ++ VS6624_ZOOM_VSTEP0_MSB, 0x0, ++ VS6624_ZOOM_VSTEP0_LSB, 0x1, ++ VS6624_PAN_HSTEP0_MSB, 0x0, ++ VS6624_PAN_HSTEP0_LSB, 0xf, ++ VS6624_PAN_VSTEP0_MSB, 0x0, ++ VS6624_PAN_VSTEP0_LSB, 0xf, ++ VS6624_SENSOR_MODE, 0x1, ++ VS6624_SYNC_CODE_SETUP, 0x21, ++ VS6624_DISABLE_FR_DAMPER, 0x0, ++ VS6624_FR_DEN, 0x1, ++ VS6624_FR_NUM_LSB, 0xf, ++ VS6624_INIT_PIPE_SETUP, 0x0, ++ VS6624_IMG_FMT0, 0x0, ++ VS6624_YUV_SETUP, 0x1, ++ VS6624_IMAGE_SIZE0, 0x2, ++ 0x0000, 0x00, ++}; ++ ++static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct vs6624, sd); ++} ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct vs6624, hdl)->sd; ++} ++ ++static int vs6624_read(struct v4l2_subdev *sd, u16 index) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[2]; ++ ++ buf[0] = index >> 8; ++ buf[1] = index; ++ i2c_master_send(client, buf, 2); ++ i2c_master_recv(client, buf, 1); ++ ++ return buf[0]; ++} ++ ++static int vs6624_write(struct v4l2_subdev *sd, u16 index, ++ u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ u8 buf[3]; ++ ++ buf[0] = index >> 8; ++ buf[1] = index; ++ buf[2] = value; ++ ++ return i2c_master_send(client, buf, 3); ++} ++ ++static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) ++{ ++ u16 reg; ++ u8 data; ++ ++ while (*regs != 0x00) { ++ reg = *regs++; ++ data = *regs++; ++ ++ vs6624_write(sd, reg, data); ++ } ++ return 0; ++} ++ ++static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_CONTRAST: ++ vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); ++ break; ++ case V4L2_CID_SATURATION: ++ vs6624_write(sd, VS6624_SATURATION0, ctrl->val); ++ break; ++ case V4L2_CID_HFLIP: ++ vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); ++ break; ++ case V4L2_CID_VFLIP: ++ vs6624_write(sd, VS6624_VFLIP0, ctrl->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, ++ enum v4l2_mbus_pixelcode *code) ++{ ++ if (index >= ARRAY_SIZE(vs6624_formats)) ++ return -EINVAL; ++ ++ *code = vs6624_formats[index].mbus_code; ++ return 0; ++} ++ ++static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ int index; ++ ++ for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) ++ if (vs6624_formats[index].mbus_code == fmt->code) ++ break; ++ if (index >= ARRAY_SIZE(vs6624_formats)) { ++ /* default to first format */ ++ index = 0; ++ fmt->code = vs6624_formats[0].mbus_code; ++ } ++ ++ /* sensor mode is VGA */ ++ if (fmt->width > VGA_WIDTH) ++ fmt->width = VGA_WIDTH; ++ if (fmt->height > VGA_HEIGHT) ++ fmt->height = VGA_HEIGHT; ++ fmt->width = fmt->width & (~3); ++ fmt->height = fmt->height & (~3); ++ fmt->field = V4L2_FIELD_NONE; ++ fmt->colorspace = vs6624_formats[index].colorspace; ++ return 0; ++} ++ ++static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct vs6624 *sensor = to_vs6624(sd); ++ int ret; ++ ++ ret = vs6624_try_mbus_fmt(sd, fmt); ++ if (ret) ++ return ret; ++ ++ /* set image format */ ++ switch (fmt->code) { ++ case V4L2_MBUS_FMT_UYVY8_2X8: ++ vs6624_write(sd, VS6624_IMG_FMT0, 0x0); ++ vs6624_write(sd, VS6624_YUV_SETUP, 0x1); ++ break; ++ case V4L2_MBUS_FMT_YUYV8_2X8: ++ vs6624_write(sd, VS6624_IMG_FMT0, 0x0); ++ vs6624_write(sd, VS6624_YUV_SETUP, 0x3); ++ break; ++ case V4L2_MBUS_FMT_RGB565_2X8_LE: ++ vs6624_write(sd, VS6624_IMG_FMT0, 0x4); ++ vs6624_write(sd, VS6624_RGB_SETUP, 0x0); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* set image size */ ++ if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); ++ else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); ++ else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); ++ else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); ++ else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); ++ else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); ++ else { ++ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); ++ vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); ++ vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); ++ vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); ++ vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); ++ vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); ++ } ++ ++ sensor->fmt = *fmt; ++ ++ return 0; ++} ++ ++static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd, ++ struct v4l2_mbus_framefmt *fmt) ++{ ++ struct vs6624 *sensor = to_vs6624(sd); ++ ++ *fmt = sensor->fmt; ++ return 0; ++} ++ ++static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct vs6624 *sensor = to_vs6624(sd); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ memset(cp, 0, sizeof(*cp)); ++ cp->capability = V4L2_CAP_TIMEPERFRAME; ++ cp->timeperframe.numerator = sensor->frame_rate.denominator; ++ cp->timeperframe.denominator = sensor->frame_rate.numerator; ++ return 0; ++} ++ ++static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) ++{ ++ struct vs6624 *sensor = to_vs6624(sd); ++ struct v4l2_captureparm *cp = &parms->parm.capture; ++ struct v4l2_fract *tpf = &cp->timeperframe; ++ ++ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (cp->extendedmode != 0) ++ return -EINVAL; ++ ++ if (tpf->numerator == 0 || tpf->denominator == 0 ++ || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { ++ /* reset to max frame rate */ ++ tpf->numerator = 1; ++ tpf->denominator = MAX_FRAME_RATE; ++ } ++ sensor->frame_rate.numerator = tpf->denominator; ++ sensor->frame_rate.denominator = tpf->numerator; ++ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); ++ vs6624_write(sd, VS6624_FR_NUM_MSB, ++ sensor->frame_rate.numerator >> 8); ++ vs6624_write(sd, VS6624_FR_NUM_LSB, ++ sensor->frame_rate.numerator & 0xFF); ++ vs6624_write(sd, VS6624_FR_DEN, ++ sensor->frame_rate.denominator & 0xFF); ++ return 0; ++} ++ ++static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ if (enable) ++ vs6624_write(sd, VS6624_USER_CMD, 0x2); ++ else ++ vs6624_write(sd, VS6624_USER_CMD, 0x4); ++ udelay(100); ++ return 0; ++} ++ ++static int vs6624_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ int rev; ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8) ++ | vs6624_read(sd, VS6624_FW_VSN_MINOR); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->val = vs6624_read(sd, reg->reg & 0xffff); ++ reg->size = 1; ++ return 0; ++} ++ ++static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ if (!v4l2_chip_match_i2c_client(client, ®->match)) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); ++ return 0; ++} ++#endif ++ ++static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { ++ .s_ctrl = vs6624_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops vs6624_core_ops = { ++ .g_chip_ident = vs6624_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = vs6624_g_register, ++ .s_register = vs6624_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_video_ops vs6624_video_ops = { ++ .enum_mbus_fmt = vs6624_enum_mbus_fmt, ++ .try_mbus_fmt = vs6624_try_mbus_fmt, ++ .s_mbus_fmt = vs6624_s_mbus_fmt, ++ .g_mbus_fmt = vs6624_g_mbus_fmt, ++ .s_parm = vs6624_s_parm, ++ .g_parm = vs6624_g_parm, ++ .s_stream = vs6624_s_stream, ++}; ++ ++static const struct v4l2_subdev_ops vs6624_ops = { ++ .core = &vs6624_core_ops, ++ .video = &vs6624_video_ops, ++}; ++ ++static int __devinit vs6624_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct vs6624 *sensor; ++ struct v4l2_subdev *sd; ++ struct v4l2_ctrl_handler *hdl; ++ const unsigned *ce; ++ int ret; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) ++ return -EIO; ++ ++ ce = client->dev.platform_data; ++ if (ce == NULL) ++ return -EINVAL; ++ ++ ret = gpio_request(*ce, "VS6624 Chip Enable"); ++ if (ret) { ++ v4l_err(client, "failed to request GPIO %d\n", *ce); ++ return ret; ++ } ++ gpio_direction_output(*ce, 1); ++ /* wait 100ms before any further i2c writes are performed */ ++ mdelay(100); ++ ++ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); ++ if (sensor == NULL) { ++ gpio_free(*ce); ++ return -ENOMEM; ++ } ++ ++ sd = &sensor->sd; ++ v4l2_i2c_subdev_init(sd, client, &vs6624_ops); ++ ++ vs6624_writeregs(sd, vs6624_p1); ++ vs6624_write(sd, VS6624_MICRO_EN, 0x2); ++ vs6624_write(sd, VS6624_DIO_EN, 0x1); ++ mdelay(10); ++ vs6624_writeregs(sd, vs6624_p2); ++ ++ vs6624_writeregs(sd, vs6624_default); ++ vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); ++ vs6624_writeregs(sd, vs6624_run_setup); ++ ++ /* set frame rate */ ++ sensor->frame_rate.numerator = MAX_FRAME_RATE; ++ sensor->frame_rate.denominator = 1; ++ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); ++ vs6624_write(sd, VS6624_FR_NUM_MSB, ++ sensor->frame_rate.numerator >> 8); ++ vs6624_write(sd, VS6624_FR_NUM_LSB, ++ sensor->frame_rate.numerator & 0xFF); ++ vs6624_write(sd, VS6624_FR_DEN, ++ sensor->frame_rate.denominator & 0xFF); ++ ++ sensor->fmt = vs6624_default_fmt; ++ sensor->ce_pin = *ce; ++ ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ hdl = &sensor->hdl; ++ v4l2_ctrl_handler_init(hdl, 4); ++ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); ++ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); ++ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ /* hook the control handler into the driver */ ++ sd->ctrl_handler = hdl; ++ if (hdl->error) { ++ int err = hdl->error; ++ ++ v4l2_ctrl_handler_free(hdl); ++ kfree(sensor); ++ gpio_free(*ce); ++ return err; ++ } ++ ++ /* initialize the hardware to the default control values */ ++ ret = v4l2_ctrl_handler_setup(hdl); ++ if (ret) { ++ v4l2_ctrl_handler_free(hdl); ++ kfree(sensor); ++ gpio_free(*ce); ++ } ++ return ret; ++} ++ ++static int __devexit vs6624_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct vs6624 *sensor = to_vs6624(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(sd->ctrl_handler); ++ gpio_free(sensor->ce_pin); ++ kfree(sensor); ++ return 0; ++} ++ ++static const struct i2c_device_id vs6624_id[] = { ++ {"vs6624", 0}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(i2c, vs6624_id); ++ ++static struct i2c_driver vs6624_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "vs6624", ++ }, ++ .probe = vs6624_probe, ++ .remove = __devexit_p(vs6624_remove), ++ .id_table = vs6624_id, ++}; ++ ++module_i2c_driver(vs6624_driver); ++ ++MODULE_DESCRIPTION("VS6624 sensor driver"); ++MODULE_AUTHOR("Scott Jiang "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/i2c/vs6624_regs.h b/drivers/media/i2c/vs6624_regs.h +new file mode 100644 +index 0000000..6ba2ee2 +--- /dev/null ++++ b/drivers/media/i2c/vs6624_regs.h +@@ -0,0 +1,337 @@ ++/* ++ * vs6624 - ST VS6624 CMOS image sensor registers ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _VS6624_REGS_H_ ++#define _VS6624_REGS_H_ ++ ++/* low level control registers */ ++#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ ++#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ ++/* device parameters */ ++#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ ++#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ ++#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ ++#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ ++#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ ++#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ ++/* host interface manager control */ ++#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ ++/* host interface manager status */ ++#define VS6624_STATE 0x0202 /* current state of the mode manager */ ++/* run mode control */ ++#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ ++/* mode setup */ ++#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ ++#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ ++/* pipe setup bank0 */ ++#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ ++#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ ++#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ ++#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ ++#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ ++#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ ++#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ ++#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ ++#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ ++#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ ++#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ ++#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ ++#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ ++#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ ++#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ ++#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ ++#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ ++#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ ++#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ ++#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ ++#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ ++#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ ++#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ ++#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ ++#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ ++#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ ++#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ ++#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ ++#define VS6624_GAMMA0 0x03B8 /* gamma settings */ ++#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ ++#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ ++#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ ++/* pipe setup bank1 */ ++#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ ++#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ ++#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ ++#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ ++#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ ++#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ ++#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ ++#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ ++#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ ++#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ ++#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ ++#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ ++#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ ++#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ ++#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ ++#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ ++#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ ++#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ ++#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ ++#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ ++#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ ++#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ ++#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ ++#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ ++#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ ++#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ ++#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ ++#define VS6624_SATURATION1 0x0436 /* saturation control for output */ ++#define VS6624_GAMMA1 0x0438 /* gamma settings */ ++#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ ++#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ ++#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ ++/* view live control */ ++#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ ++#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ ++/* view live status */ ++#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ ++/* power management */ ++#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ ++/* video timing parameter host inputs */ ++#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ ++#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ ++#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ ++/* video timing control */ ++#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ ++/* frame dimension parameter host inputs */ ++#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ ++#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ ++/* static frame rate control */ ++#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ ++#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ ++#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ ++/* automatic frame rate control */ ++#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ ++#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ ++#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ ++/* exposure controls */ ++#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ ++#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ ++#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ ++#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ ++#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ ++#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ ++#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ ++#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ ++#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ ++#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ ++#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ ++#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ ++#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ ++#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ ++#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ ++#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ ++#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ ++#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ ++#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ ++#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ ++#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ ++#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ ++#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ ++#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ ++#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ ++#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ ++#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ ++#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ ++#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ ++/* white balance control */ ++#define VS6624_WB_MODE 0x1480 /* set white balance mode */ ++#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ ++#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ ++#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ ++#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ ++#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ ++#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ ++#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ ++#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ ++#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ ++/* sensor setup */ ++#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ ++/* image stability */ ++#define VS6624_STABLE_WB 0x1900 /* white balance stable */ ++#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ ++#define VS6624_STABLE 0x1906 /* system stable */ ++/* flash control */ ++#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ ++#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ ++#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ ++/* flash status */ ++#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ ++#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ ++/* scythe filter controls */ ++#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ ++/* jack filter controls */ ++#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ ++/* demosaic control */ ++#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ ++/* color matrix dampers */ ++#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ ++#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ ++#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ ++#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ ++#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ ++#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ ++#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ ++/* peaking control */ ++#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ ++#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ ++#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ ++#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ ++#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ ++#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ ++#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ ++#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ ++#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ ++#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ ++#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ ++#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ ++#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ ++#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ ++#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ ++#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ ++#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ ++/* pipe 0 RGB to YUV matrix manual control */ ++#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ ++#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ ++#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ ++#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ ++#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ ++#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ ++#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ ++#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ ++#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ ++#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ ++#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ ++#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ ++#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ ++#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ ++#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ ++#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ ++#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ ++#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ ++#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ ++#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ ++#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ ++#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ ++#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ ++#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ ++#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ ++/* pipe 1 RGB to YUV matrix manual control */ ++#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ ++#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ ++#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ ++#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ ++#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ ++#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ ++#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ ++#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ ++#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ ++#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ ++#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ ++#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ ++#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ ++#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ ++#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ ++#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ ++#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ ++#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ ++#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ ++#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ ++#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ ++#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ ++#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ ++#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ ++#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ ++/* pipe 0 gamma manual control */ ++#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ ++#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ ++#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ ++#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ ++/* pipe 1 gamma manual control */ ++#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ ++#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ ++#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ ++#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ ++#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ ++/* fade to black */ ++#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ ++#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ ++#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ ++#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ ++#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ ++#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ ++#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ ++#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ ++#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ ++/* output formatter control */ ++#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ ++#define VS6624_BLANK_FMT 0x2582 /* blank format */ ++#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ ++#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ ++#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ ++#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ ++#define VS6624_PCLK_EN 0x258C /* PCLK enable */ ++#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ ++#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ ++#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ ++#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ ++#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ ++#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ ++#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ ++#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ ++#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ ++#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ ++#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ ++#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ ++#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ ++#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ ++#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ ++#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ ++#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ ++#define VS6624_OUT_IF 0x25B0 /* output interface */ ++#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ ++/* NoRA controls */ ++#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ ++#define VS6624_NORA_USAGE 0x2602 /* usage */ ++#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ ++#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ ++#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ ++#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ ++#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ ++#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ ++#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ ++#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ ++#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ ++#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ ++ ++#endif +diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c +new file mode 100644 +index 0000000..3bb99e9 +--- /dev/null ++++ b/drivers/media/i2c/wm8739.c +@@ -0,0 +1,294 @@ ++/* ++ * wm8739 ++ * ++ * Copyright (C) 2005 T. Adachi ++ * ++ * Copyright (C) 2005 Hans Verkuil ++ * - Cleanup ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("wm8739 driver"); ++MODULE_AUTHOR("T. Adachi, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++static int debug; ++ ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(debug, "Debug level (0-1)"); ++ ++ ++/* ------------------------------------------------------------------------ */ ++ ++enum { ++ R0 = 0, R1, ++ R5 = 5, R6, R7, R8, R9, R15 = 15, ++ TOT_REGS ++}; ++ ++struct wm8739_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct { ++ /* audio cluster */ ++ struct v4l2_ctrl *volume; ++ struct v4l2_ctrl *mute; ++ struct v4l2_ctrl *balance; ++ }; ++ u32 clock_freq; ++}; ++ ++static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct wm8739_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct wm8739_state, hdl)->sd; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int i; ++ ++ if (reg < 0 || reg >= TOT_REGS) { ++ v4l2_err(sd, "Invalid register R%d\n", reg); ++ return -1; ++ } ++ ++ v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); ++ ++ for (i = 0; i < 3; i++) ++ if (i2c_smbus_write_byte_data(client, ++ (reg << 1) | (val >> 8), val & 0xff) == 0) ++ return 0; ++ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); ++ return -1; ++} ++ ++static int wm8739_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct wm8739_state *state = to_state(sd); ++ unsigned int work_l, work_r; ++ u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ ++ u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ ++ u16 mute; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_VOLUME: ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ ++ work_l = (min(65536 - state->balance->val, 32768) * state->volume->val) / 32768; ++ work_r = (min(state->balance->val, 32768) * state->volume->val) / 32768; ++ ++ vol_l = (long)work_l * 31 / 65535; ++ vol_r = (long)work_r * 31 / 65535; ++ ++ /* set audio volume etc. */ ++ mute = state->mute->val ? 0x80 : 0; ++ ++ /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB ++ * Default setting: 0x17 = 0 dB ++ */ ++ wm8739_write(sd, R0, (vol_l & 0x1f) | mute); ++ wm8739_write(sd, R1, (vol_r & 0x1f) | mute); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------------ */ ++ ++static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) ++{ ++ struct wm8739_state *state = to_state(sd); ++ ++ state->clock_freq = audiofreq; ++ /* de-activate */ ++ wm8739_write(sd, R9, 0x000); ++ switch (audiofreq) { ++ case 44100: ++ /* 256fps, fs=44.1k */ ++ wm8739_write(sd, R8, 0x020); ++ break; ++ case 48000: ++ /* 256fps, fs=48k */ ++ wm8739_write(sd, R8, 0x000); ++ break; ++ case 32000: ++ /* 256fps, fs=32k */ ++ wm8739_write(sd, R8, 0x018); ++ break; ++ default: ++ break; ++ } ++ /* activate */ ++ wm8739_write(sd, R9, 0x001); ++ return 0; ++} ++ ++static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); ++} ++ ++static int wm8739_log_status(struct v4l2_subdev *sd) ++{ ++ struct wm8739_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops wm8739_ctrl_ops = { ++ .s_ctrl = wm8739_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops wm8739_core_ops = { ++ .log_status = wm8739_log_status, ++ .g_chip_ident = wm8739_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { ++ .s_clock_freq = wm8739_s_clock_freq, ++}; ++ ++static const struct v4l2_subdev_ops wm8739_ops = { ++ .core = &wm8739_core_ops, ++ .audio = &wm8739_audio_ops, ++}; ++ ++/* ------------------------------------------------------------------------ */ ++ ++/* i2c implementation */ ++ ++static int wm8739_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct wm8739_state *state; ++ struct v4l2_subdev *sd; ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct wm8739_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &wm8739_ops); ++ v4l2_ctrl_handler_init(&state->hdl, 2); ++ state->volume = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, 0, 65535, 65535 / 100, 50736); ++ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ state->balance = v4l2_ctrl_new_std(&state->hdl, &wm8739_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, 0, 65535, 65535 / 100, 32768); ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ v4l2_ctrl_cluster(3, &state->volume); ++ ++ state->clock_freq = 48000; ++ ++ /* Initialize wm8739 */ ++ ++ /* reset */ ++ wm8739_write(sd, R15, 0x00); ++ /* filter setting, high path, offet clear */ ++ wm8739_write(sd, R5, 0x000); ++ /* ADC, OSC, Power Off mode Disable */ ++ wm8739_write(sd, R6, 0x000); ++ /* Digital Audio interface format: ++ Enable Master mode, 24 bit, MSB first/left justified */ ++ wm8739_write(sd, R7, 0x049); ++ /* sampling control: normal, 256fs, 48KHz sampling rate */ ++ wm8739_write(sd, R8, 0x000); ++ /* activate */ ++ wm8739_write(sd, R9, 0x001); ++ /* set volume/mute */ ++ v4l2_ctrl_handler_setup(&state->hdl); ++ return 0; ++} ++ ++static int wm8739_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct wm8739_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id wm8739_id[] = { ++ { "wm8739", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, wm8739_id); ++ ++static struct i2c_driver wm8739_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "wm8739", ++ }, ++ .probe = wm8739_probe, ++ .remove = wm8739_remove, ++ .id_table = wm8739_id, ++}; ++ ++module_i2c_driver(wm8739_driver); +diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c +new file mode 100644 +index 0000000..bee77ea +--- /dev/null ++++ b/drivers/media/i2c/wm8775.c +@@ -0,0 +1,342 @@ ++/* ++ * wm8775 - driver version 0.0.1 ++ * ++ * Copyright (C) 2004 Ulf Eklund ++ * ++ * Based on saa7115 driver ++ * ++ * Copyright (C) 2005 Hans Verkuil ++ * - Cleanup ++ * - V4L2 API update ++ * - sound fixes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("wm8775 driver"); ++MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); ++MODULE_LICENSE("GPL"); ++ ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++enum { ++ R7 = 7, R11 = 11, ++ R12, R13, R14, R15, R16, R17, R18, R19, R20, R21, R23 = 23, ++ TOT_REGS ++}; ++ ++#define ALC_HOLD 0x85 /* R17: use zero cross detection, ALC hold time 42.6 ms */ ++#define ALC_EN 0x100 /* R17: ALC enable */ ++ ++struct wm8775_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct v4l2_ctrl *mute; ++ struct v4l2_ctrl *vol; ++ struct v4l2_ctrl *bal; ++ struct v4l2_ctrl *loud; ++ u8 input; /* Last selected input (0-0xf) */ ++}; ++ ++static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct wm8775_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct wm8775_state, hdl)->sd; ++} ++ ++static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int i; ++ ++ if (reg < 0 || reg >= TOT_REGS) { ++ v4l2_err(sd, "Invalid register R%d\n", reg); ++ return -1; ++ } ++ ++ for (i = 0; i < 3; i++) ++ if (i2c_smbus_write_byte_data(client, ++ (reg << 1) | (val >> 8), val & 0xff) == 0) ++ return 0; ++ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); ++ return -1; ++} ++ ++static void wm8775_set_audio(struct v4l2_subdev *sd, int quietly) ++{ ++ struct wm8775_state *state = to_state(sd); ++ u8 vol_l, vol_r; ++ int muted = 0 != state->mute->val; ++ u16 volume = (u16)state->vol->val; ++ u16 balance = (u16)state->bal->val; ++ ++ /* normalize ( 65535 to 0 -> 255 to 0 (+24dB to -103dB) ) */ ++ vol_l = (min(65536 - balance, 32768) * volume) >> 23; ++ vol_r = (min(balance, (u16)32768) * volume) >> 23; ++ ++ /* Mute */ ++ if (muted || quietly) ++ wm8775_write(sd, R21, 0x0c0 | state->input); ++ ++ wm8775_write(sd, R14, vol_l | 0x100); /* 0x100= Left channel ADC zero cross enable */ ++ wm8775_write(sd, R15, vol_r | 0x100); /* 0x100= Right channel ADC zero cross enable */ ++ ++ /* Un-mute */ ++ if (!muted) ++ wm8775_write(sd, R21, state->input); ++} ++ ++static int wm8775_s_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct wm8775_state *state = to_state(sd); ++ ++ /* There are 4 inputs and one output. Zero or more inputs ++ are multiplexed together to the output. Hence there are ++ 16 combinations. ++ If only one input is active (the normal case) then the ++ input values 1, 2, 4 or 8 should be used. */ ++ if (input > 15) { ++ v4l2_err(sd, "Invalid input %d.\n", input); ++ return -EINVAL; ++ } ++ state->input = input; ++ if (!v4l2_ctrl_g_ctrl(state->mute)) ++ return 0; ++ if (!v4l2_ctrl_g_ctrl(state->vol)) ++ return 0; ++ if (!v4l2_ctrl_g_ctrl(state->bal)) ++ return 0; ++ wm8775_set_audio(sd, 1); ++ return 0; ++} ++ ++static int wm8775_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ case V4L2_CID_AUDIO_VOLUME: ++ case V4L2_CID_AUDIO_BALANCE: ++ wm8775_set_audio(sd, 0); ++ return 0; ++ case V4L2_CID_AUDIO_LOUDNESS: ++ wm8775_write(sd, R17, (ctrl->val ? ALC_EN : 0) | ALC_HOLD); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); ++} ++ ++static int wm8775_log_status(struct v4l2_subdev *sd) ++{ ++ struct wm8775_state *state = to_state(sd); ++ ++ v4l2_info(sd, "Input: %d\n", state->input); ++ v4l2_ctrl_handler_log_status(&state->hdl, sd->name); ++ return 0; ++} ++ ++static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) ++{ ++ wm8775_set_audio(sd, 0); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_ctrl_ops wm8775_ctrl_ops = { ++ .s_ctrl = wm8775_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops wm8775_core_ops = { ++ .log_status = wm8775_log_status, ++ .g_chip_ident = wm8775_g_chip_ident, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { ++ .s_frequency = wm8775_s_frequency, ++}; ++ ++static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { ++ .s_routing = wm8775_s_routing, ++}; ++ ++static const struct v4l2_subdev_ops wm8775_ops = { ++ .core = &wm8775_core_ops, ++ .tuner = &wm8775_tuner_ops, ++ .audio = &wm8775_audio_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* i2c implementation */ ++ ++/* ++ * Generic i2c probe ++ * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' ++ */ ++ ++static int wm8775_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct wm8775_state *state; ++ struct v4l2_subdev *sd; ++ int err; ++ bool is_nova_s = false; ++ ++ if (client->dev.platform_data) { ++ struct wm8775_platform_data *data = client->dev.platform_data; ++ is_nova_s = data->is_nova_s; ++ } ++ ++ /* Check if the adapter supports the needed features */ ++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) ++ return -EIO; ++ ++ v4l_info(client, "chip found @ 0x%02x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ state = kzalloc(sizeof(struct wm8775_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ sd = &state->sd; ++ v4l2_i2c_subdev_init(sd, client, &wm8775_ops); ++ state->input = 2; ++ ++ v4l2_ctrl_handler_init(&state->hdl, 4); ++ state->mute = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ state->vol = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, ++ V4L2_CID_AUDIO_VOLUME, 0, 65535, (65535+99)/100, 0xCF00); /* 0dB*/ ++ state->bal = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, 0, 65535, (65535+99)/100, 32768); ++ state->loud = v4l2_ctrl_new_std(&state->hdl, &wm8775_ctrl_ops, ++ V4L2_CID_AUDIO_LOUDNESS, 0, 1, 1, 1); ++ sd->ctrl_handler = &state->hdl; ++ err = state->hdl.error; ++ if (err) { ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return err; ++ } ++ ++ /* Initialize wm8775 */ ++ ++ /* RESET */ ++ wm8775_write(sd, R23, 0x000); ++ /* Disable zero cross detect timeout */ ++ wm8775_write(sd, R7, 0x000); ++ /* HPF enable, left justified, 24-bit (Philips) mode */ ++ wm8775_write(sd, R11, 0x021); ++ /* Master mode, clock ratio 256fs */ ++ wm8775_write(sd, R12, 0x102); ++ /* Powered up */ ++ wm8775_write(sd, R13, 0x000); ++ ++ if (!is_nova_s) { ++ /* ADC gain +2.5dB, enable zero cross */ ++ wm8775_write(sd, R14, 0x1d4); ++ /* ADC gain +2.5dB, enable zero cross */ ++ wm8775_write(sd, R15, 0x1d4); ++ /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ ++ wm8775_write(sd, R16, 0x1bf); ++ /* Enable gain control, use zero cross detection, ++ ALC hold time 42.6 ms */ ++ wm8775_write(sd, R17, 0x185); ++ } else { ++ /* ALC stereo, ALC target level -5dB FS, ALC max gain +8dB */ ++ wm8775_write(sd, R16, 0x1bb); ++ /* Set ALC mode and hold time */ ++ wm8775_write(sd, R17, (state->loud->val ? ALC_EN : 0) | ALC_HOLD); ++ } ++ /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ ++ wm8775_write(sd, R18, 0x0a2); ++ /* Enable noise gate, threshold -72dBfs */ ++ wm8775_write(sd, R19, 0x005); ++ if (!is_nova_s) { ++ /* Transient window 4ms, lower PGA gain limit -1dB */ ++ wm8775_write(sd, R20, 0x07a); ++ /* LRBOTH = 1, use input 2. */ ++ wm8775_write(sd, R21, 0x102); ++ } else { ++ /* Transient window 4ms, ALC min gain -5dB */ ++ wm8775_write(sd, R20, 0x0fb); ++ ++ wm8775_set_audio(sd, 1); /* set volume/mute/mux */ ++ } ++ return 0; ++} ++ ++static int wm8775_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ struct wm8775_state *state = to_state(sd); ++ ++ v4l2_device_unregister_subdev(sd); ++ v4l2_ctrl_handler_free(&state->hdl); ++ kfree(state); ++ return 0; ++} ++ ++static const struct i2c_device_id wm8775_id[] = { ++ { "wm8775", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, wm8775_id); ++ ++static struct i2c_driver wm8775_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "wm8775", ++ }, ++ .probe = wm8775_probe, ++ .remove = wm8775_remove, ++ .id_table = wm8775_id, ++}; ++ ++module_i2c_driver(wm8775_driver); +diff --git a/drivers/media/mmc/Kconfig b/drivers/media/mmc/Kconfig +new file mode 100644 +index 0000000..8c30ada +--- /dev/null ++++ b/drivers/media/mmc/Kconfig +@@ -0,0 +1,2 @@ ++comment "Supported MMC/SDIO adapters" ++source "drivers/media/mmc/siano/Kconfig" +diff --git a/drivers/media/mmc/Makefile b/drivers/media/mmc/Makefile +new file mode 100644 +index 0000000..31e297a +--- /dev/null ++++ b/drivers/media/mmc/Makefile +@@ -0,0 +1 @@ ++obj-y += siano/ +diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig +new file mode 100644 +index 0000000..aa05ad3 +--- /dev/null ++++ b/drivers/media/mmc/siano/Kconfig +@@ -0,0 +1,11 @@ ++# ++# Siano Mobile Silicon Digital TV device configuration ++# ++ ++config SMS_SDIO_DRV ++ tristate "Siano SMS1xxx based MDTV via SDIO interface" ++ depends on DVB_CORE && HAS_DMA ++ depends on MMC ++ select MEDIA_COMMON_OPTIONS ++ ---help--- ++ Choose if you would like to have Siano's support for SDIO interface +diff --git a/drivers/media/mmc/siano/Makefile b/drivers/media/mmc/siano/Makefile +new file mode 100644 +index 0000000..0e01f97 +--- /dev/null ++++ b/drivers/media/mmc/siano/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o ++ ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/common/siano ++ccflags-y += $(extra-cflags-y) $(extra-cflags-m) ++ +diff --git a/drivers/media/mmc/siano/smssdio.c b/drivers/media/mmc/siano/smssdio.c +new file mode 100644 +index 0000000..d6f3f10 +--- /dev/null ++++ b/drivers/media/mmc/siano/smssdio.c +@@ -0,0 +1,365 @@ ++/* ++ * smssdio.c - Siano 1xxx SDIO interface driver ++ * ++ * Copyright 2008 Pierre Ossman ++ * ++ * Based on code by Siano Mobile Silicon, Inc., ++ * 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 hardware is a bit odd in that all transfers should be done ++ * to/from the SMSSDIO_DATA register, yet the "increase address" bit ++ * always needs to be set. ++ * ++ * Also, buffers from the card are always aligned to 128 byte ++ * boundaries. ++ */ ++ ++/* ++ * General cleanup notes: ++ * ++ * - only typedefs should be name *_t ++ * ++ * - use ERR_PTR and friends for smscore_register_device() ++ * ++ * - smscore_getbuffer should zero fields ++ * ++ * Fix stop command ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "smscoreapi.h" ++#include "sms-cards.h" ++ ++/* Registers */ ++ ++#define SMSSDIO_DATA 0x00 ++#define SMSSDIO_INT 0x04 ++#define SMSSDIO_BLOCK_SIZE 128 ++ ++static const struct sdio_device_id smssdio_ids[] __devinitconst = { ++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR), ++ .driver_data = SMS1XXX_BOARD_SIANO_STELLAR}, ++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0), ++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A}, ++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0), ++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B}, ++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0), ++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, ++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE), ++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA}, ++ { /* end: all zeroes */ }, ++}; ++ ++MODULE_DEVICE_TABLE(sdio, smssdio_ids); ++ ++struct smssdio_device { ++ struct sdio_func *func; ++ ++ struct smscore_device_t *coredev; ++ ++ struct smscore_buffer_t *split_cb; ++}; ++ ++/*******************************************************************/ ++/* Siano core callbacks */ ++/*******************************************************************/ ++ ++static int smssdio_sendrequest(void *context, void *buffer, size_t size) ++{ ++ int ret = 0; ++ struct smssdio_device *smsdev; ++ ++ smsdev = context; ++ ++ sdio_claim_host(smsdev->func); ++ ++ while (size >= smsdev->func->cur_blksize) { ++ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, ++ buffer, smsdev->func->cur_blksize); ++ if (ret) ++ goto out; ++ ++ buffer += smsdev->func->cur_blksize; ++ size -= smsdev->func->cur_blksize; ++ } ++ ++ if (size) { ++ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA, ++ buffer, size); ++ } ++ ++out: ++ sdio_release_host(smsdev->func); ++ ++ return ret; ++} ++ ++/*******************************************************************/ ++/* SDIO callbacks */ ++/*******************************************************************/ ++ ++static void smssdio_interrupt(struct sdio_func *func) ++{ ++ int ret; ++ ++ struct smssdio_device *smsdev; ++ struct smscore_buffer_t *cb; ++ struct SmsMsgHdr_ST *hdr; ++ size_t size; ++ ++ smsdev = sdio_get_drvdata(func); ++ ++ /* ++ * The interrupt register has no defined meaning. It is just ++ * a way of turning of the level triggered interrupt. ++ */ ++ (void)sdio_readb(func, SMSSDIO_INT, &ret); ++ if (ret) { ++ sms_err("Unable to read interrupt register!\n"); ++ return; ++ } ++ ++ if (smsdev->split_cb == NULL) { ++ cb = smscore_getbuffer(smsdev->coredev); ++ if (!cb) { ++ sms_err("Unable to allocate data buffer!\n"); ++ return; ++ } ++ ++ ret = sdio_memcpy_fromio(smsdev->func, ++ cb->p, ++ SMSSDIO_DATA, ++ SMSSDIO_BLOCK_SIZE); ++ if (ret) { ++ sms_err("Error %d reading initial block!\n", ret); ++ return; ++ } ++ ++ hdr = cb->p; ++ ++ if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { ++ smsdev->split_cb = cb; ++ return; ++ } ++ ++ if (hdr->msgLength > smsdev->func->cur_blksize) ++ size = hdr->msgLength - smsdev->func->cur_blksize; ++ else ++ size = 0; ++ } else { ++ cb = smsdev->split_cb; ++ hdr = cb->p; ++ ++ size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST); ++ ++ smsdev->split_cb = NULL; ++ } ++ ++ if (size) { ++ void *buffer; ++ ++ buffer = cb->p + (hdr->msgLength - size); ++ size = ALIGN(size, SMSSDIO_BLOCK_SIZE); ++ ++ BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); ++ ++ /* ++ * First attempt to transfer all of it in one go... ++ */ ++ ret = sdio_memcpy_fromio(smsdev->func, ++ buffer, ++ SMSSDIO_DATA, ++ size); ++ if (ret && ret != -EINVAL) { ++ smscore_putbuffer(smsdev->coredev, cb); ++ sms_err("Error %d reading data from card!\n", ret); ++ return; ++ } ++ ++ /* ++ * ..then fall back to one block at a time if that is ++ * not possible... ++ * ++ * (we have to do this manually because of the ++ * problem with the "increase address" bit) ++ */ ++ if (ret == -EINVAL) { ++ while (size) { ++ ret = sdio_memcpy_fromio(smsdev->func, ++ buffer, SMSSDIO_DATA, ++ smsdev->func->cur_blksize); ++ if (ret) { ++ smscore_putbuffer(smsdev->coredev, cb); ++ sms_err("Error %d reading " ++ "data from card!\n", ret); ++ return; ++ } ++ ++ buffer += smsdev->func->cur_blksize; ++ if (size > smsdev->func->cur_blksize) ++ size -= smsdev->func->cur_blksize; ++ else ++ size = 0; ++ } ++ } ++ } ++ ++ cb->size = hdr->msgLength; ++ cb->offset = 0; ++ ++ smscore_onresponse(smsdev->coredev, cb); ++} ++ ++static int __devinit smssdio_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ int ret; ++ ++ int board_id; ++ struct smssdio_device *smsdev; ++ struct smsdevice_params_t params; ++ ++ board_id = id->driver_data; ++ ++ smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL); ++ if (!smsdev) ++ return -ENOMEM; ++ ++ smsdev->func = func; ++ ++ memset(¶ms, 0, sizeof(struct smsdevice_params_t)); ++ ++ params.device = &func->dev; ++ params.buffer_size = 0x5000; /* ?? */ ++ params.num_buffers = 22; /* ?? */ ++ params.context = smsdev; ++ ++ snprintf(params.devpath, sizeof(params.devpath), ++ "sdio\\%s", sdio_func_id(func)); ++ ++ params.sendrequest_handler = smssdio_sendrequest; ++ ++ params.device_type = sms_get_board(board_id)->type; ++ ++ if (params.device_type != SMS_STELLAR) ++ params.flags |= SMS_DEVICE_FAMILY2; ++ else { ++ /* ++ * FIXME: Stellar needs special handling... ++ */ ++ ret = -ENODEV; ++ goto free; ++ } ++ ++ ret = smscore_register_device(¶ms, &smsdev->coredev); ++ if (ret < 0) ++ goto free; ++ ++ smscore_set_board_id(smsdev->coredev, board_id); ++ ++ sdio_claim_host(func); ++ ++ ret = sdio_enable_func(func); ++ if (ret) ++ goto release; ++ ++ ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE); ++ if (ret) ++ goto disable; ++ ++ ret = sdio_claim_irq(func, smssdio_interrupt); ++ if (ret) ++ goto disable; ++ ++ sdio_set_drvdata(func, smsdev); ++ ++ sdio_release_host(func); ++ ++ ret = smscore_start_device(smsdev->coredev); ++ if (ret < 0) ++ goto reclaim; ++ ++ return 0; ++ ++reclaim: ++ sdio_claim_host(func); ++ sdio_release_irq(func); ++disable: ++ sdio_disable_func(func); ++release: ++ sdio_release_host(func); ++ smscore_unregister_device(smsdev->coredev); ++free: ++ kfree(smsdev); ++ ++ return ret; ++} ++ ++static void smssdio_remove(struct sdio_func *func) ++{ ++ struct smssdio_device *smsdev; ++ ++ smsdev = sdio_get_drvdata(func); ++ ++ /* FIXME: racy! */ ++ if (smsdev->split_cb) ++ smscore_putbuffer(smsdev->coredev, smsdev->split_cb); ++ ++ smscore_unregister_device(smsdev->coredev); ++ ++ sdio_claim_host(func); ++ sdio_release_irq(func); ++ sdio_disable_func(func); ++ sdio_release_host(func); ++ ++ kfree(smsdev); ++} ++ ++static struct sdio_driver smssdio_driver = { ++ .name = "smssdio", ++ .id_table = smssdio_ids, ++ .probe = smssdio_probe, ++ .remove = smssdio_remove, ++}; ++ ++/*******************************************************************/ ++/* Module functions */ ++/*******************************************************************/ ++ ++static int __init smssdio_module_init(void) ++{ ++ int ret = 0; ++ ++ printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n"); ++ printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n"); ++ ++ ret = sdio_register_driver(&smssdio_driver); ++ ++ return ret; ++} ++ ++static void __exit smssdio_module_exit(void) ++{ ++ sdio_unregister_driver(&smssdio_driver); ++} ++ ++module_init(smssdio_module_init); ++module_exit(smssdio_module_exit); ++ ++MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver"); ++MODULE_AUTHOR("Pierre Ossman"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/parport/Kconfig b/drivers/media/parport/Kconfig +new file mode 100644 +index 0000000..ece13dc +--- /dev/null ++++ b/drivers/media/parport/Kconfig +@@ -0,0 +1,52 @@ ++menuconfig MEDIA_PARPORT_SUPPORT ++ bool "ISA and parallel port devices" ++ depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT ++ help ++ Enables drivers for ISA and parallel port bus. If you ++ need media drivers using those legacy buses, say Y. ++ ++if MEDIA_PARPORT_SUPPORT ++config VIDEO_BWQCAM ++ tristate "Quickcam BW Video For Linux" ++ depends on PARPORT && VIDEO_V4L2 ++ help ++ Say Y have if you the black and white version of the QuickCam ++ camera. See the next option for the color version. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bw-qcam. ++ ++config VIDEO_CQCAM ++ tristate "QuickCam Colour Video For Linux" ++ depends on PARPORT && VIDEO_V4L2 ++ help ++ This is the video4linux driver for the colour version of the ++ Connectix QuickCam. If you have one of these cameras, say Y here, ++ otherwise say N. This driver does not work with the original ++ monochrome QuickCam, QuickCam VC or QuickClip. It is also available ++ as a module (c-qcam). ++ Read for more information. ++ ++config VIDEO_PMS ++ tristate "Mediavision Pro Movie Studio Video For Linux" ++ depends on ISA && VIDEO_V4L2 ++ help ++ Say Y if you have the ISA Mediavision Pro Movie Studio ++ capture card. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called pms. ++ ++config VIDEO_W9966 ++ tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux" ++ depends on PARPORT_1284 && PARPORT && VIDEO_V4L2 ++ help ++ Video4linux driver for Winbond's w9966 based Webcams. ++ Currently tested with the LifeView FlyCam Supra. ++ If you have one of these cameras, say Y here ++ otherwise say N. ++ This driver is also available as a module (w9966). ++ ++ Check out for more ++ information. ++endif +diff --git a/drivers/media/parport/Makefile b/drivers/media/parport/Makefile +new file mode 100644 +index 0000000..4eea06d +--- /dev/null ++++ b/drivers/media/parport/Makefile +@@ -0,0 +1,4 @@ ++obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o ++obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o ++obj-$(CONFIG_VIDEO_W9966) += w9966.o ++obj-$(CONFIG_VIDEO_PMS) += pms.o +diff --git a/drivers/media/parport/bw-qcam.c b/drivers/media/parport/bw-qcam.c +new file mode 100644 +index 0000000..5b75a64 +--- /dev/null ++++ b/drivers/media/parport/bw-qcam.c +@@ -0,0 +1,1113 @@ ++/* ++ * QuickCam Driver For Video4Linux. ++ * ++ * Video4Linux conversion work by Alan Cox. ++ * Parport compatibility by Phil Blundell. ++ * Busy loop avoidance by Mark Cooke. ++ * ++ * Module parameters: ++ * ++ * maxpoll=<1 - 5000> ++ * ++ * When polling the QuickCam for a response, busy-wait for a ++ * maximum of this many loops. The default of 250 gives little ++ * impact on interactive response. ++ * ++ * NOTE: If this parameter is set too high, the processor ++ * will busy wait until this loop times out, and then ++ * slowly poll for a further 5 seconds before failing ++ * the transaction. You have been warned. ++ * ++ * yieldlines=<1 - 250> ++ * ++ * When acquiring a frame from the camera, the data gathering ++ * loop will yield back to the scheduler after completing ++ * this many lines. The default of 4 provides a trade-off ++ * between increased frame acquisition time and impact on ++ * interactive response. ++ */ ++ ++/* qcam-lib.c -- Library for programming with the Connectix QuickCam. ++ * See the included documentation for usage instructions and details ++ * of the protocol involved. */ ++ ++ ++/* Version 0.5, August 4, 1996 */ ++/* Version 0.7, August 27, 1996 */ ++/* Version 0.9, November 17, 1996 */ ++ ++ ++/****************************************************************** ++ ++Copyright (C) 1996 by Scott Laird ++ ++Permission is hereby granted, free of charge, to any person obtaining ++a copy of this software and associated documentation files (the ++"Software"), to deal in the Software without restriction, including ++without limitation the rights to use, copy, modify, merge, publish, ++distribute, sublicense, and/or sell copies of the Software, and to ++permit persons to whom the Software is furnished to do so, subject to ++the following conditions: ++ ++The above copyright notice and this permission notice shall be ++included in all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR ++OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++OTHER DEALINGS IN THE SOFTWARE. ++ ++******************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* One from column A... */ ++#define QC_NOTSET 0 ++#define QC_UNIDIR 1 ++#define QC_BIDIR 2 ++#define QC_SERIAL 3 ++ ++/* ... and one from column B */ ++#define QC_ANY 0x00 ++#define QC_FORCE_UNIDIR 0x10 ++#define QC_FORCE_BIDIR 0x20 ++#define QC_FORCE_SERIAL 0x30 ++/* in the port_mode member */ ++ ++#define QC_MODE_MASK 0x07 ++#define QC_FORCE_MASK 0x70 ++ ++#define MAX_HEIGHT 243 ++#define MAX_WIDTH 336 ++ ++/* Bit fields for status flags */ ++#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */ ++ ++struct qcam { ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct v4l2_ctrl_handler hdl; ++ struct pardevice *pdev; ++ struct parport *pport; ++ struct mutex lock; ++ int width, height; ++ int bpp; ++ int mode; ++ int contrast, brightness, whitebal; ++ int port_mode; ++ int transfer_scale; ++ int top, left; ++ int status; ++ unsigned int saved_bits; ++ unsigned long in_use; ++}; ++ ++static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */ ++static unsigned int yieldlines = 4; /* Yield after this many during capture */ ++static int video_nr = -1; ++static unsigned int force_init; /* Whether to probe aggressively */ ++ ++module_param(maxpoll, int, 0); ++module_param(yieldlines, int, 0); ++module_param(video_nr, int, 0); ++ ++/* Set force_init=1 to avoid detection by polling status register and ++ * immediately attempt to initialize qcam */ ++module_param(force_init, int, 0); ++ ++#define MAX_CAMS 4 ++static struct qcam *qcams[MAX_CAMS]; ++static unsigned int num_cams; ++ ++static inline int read_lpstatus(struct qcam *q) ++{ ++ return parport_read_status(q->pport); ++} ++ ++static inline int read_lpdata(struct qcam *q) ++{ ++ return parport_read_data(q->pport); ++} ++ ++static inline void write_lpdata(struct qcam *q, int d) ++{ ++ parport_write_data(q->pport, d); ++} ++ ++static void write_lpcontrol(struct qcam *q, int d) ++{ ++ if (d & 0x20) { ++ /* Set bidirectional mode to reverse (data in) */ ++ parport_data_reverse(q->pport); ++ } else { ++ /* Set bidirectional mode to forward (data out) */ ++ parport_data_forward(q->pport); ++ } ++ ++ /* Now issue the regular port command, but strip out the ++ * direction flag */ ++ d &= ~0x20; ++ parport_write_control(q->pport, d); ++} ++ ++ ++/* qc_waithand busy-waits for a handshake signal from the QuickCam. ++ * Almost all communication with the camera requires handshaking. */ ++ ++static int qc_waithand(struct qcam *q, int val) ++{ ++ int status; ++ int runs = 0; ++ ++ if (val) { ++ while (!((status = read_lpstatus(q)) & 8)) { ++ /* 1000 is enough spins on the I/O for all normal ++ cases, at that point we start to poll slowly ++ until the camera wakes up. However, we are ++ busy blocked until the camera responds, so ++ setting it lower is much better for interactive ++ response. */ ++ ++ if (runs++ > maxpoll) ++ msleep_interruptible(5); ++ if (runs > (maxpoll + 1000)) /* 5 seconds */ ++ return -1; ++ } ++ } else { ++ while (((status = read_lpstatus(q)) & 8)) { ++ /* 1000 is enough spins on the I/O for all normal ++ cases, at that point we start to poll slowly ++ until the camera wakes up. However, we are ++ busy blocked until the camera responds, so ++ setting it lower is much better for interactive ++ response. */ ++ ++ if (runs++ > maxpoll) ++ msleep_interruptible(5); ++ if (runs++ > (maxpoll + 1000)) /* 5 seconds */ ++ return -1; ++ } ++ } ++ ++ return status; ++} ++ ++/* Waithand2 is used when the qcam is in bidirectional mode, and the ++ * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1 ++ * (bit 3 of status register). It also returns the last value read, ++ * since this data is useful. */ ++ ++static unsigned int qc_waithand2(struct qcam *q, int val) ++{ ++ unsigned int status; ++ int runs = 0; ++ ++ do { ++ status = read_lpdata(q); ++ /* 1000 is enough spins on the I/O for all normal ++ cases, at that point we start to poll slowly ++ until the camera wakes up. However, we are ++ busy blocked until the camera responds, so ++ setting it lower is much better for interactive ++ response. */ ++ ++ if (runs++ > maxpoll) ++ msleep_interruptible(5); ++ if (runs++ > (maxpoll + 1000)) /* 5 seconds */ ++ return 0; ++ } while ((status & 1) != val); ++ ++ return status; ++} ++ ++/* qc_command is probably a bit of a misnomer -- it's used to send ++ * bytes *to* the camera. Generally, these bytes are either commands ++ * or arguments to commands, so the name fits, but it still bugs me a ++ * bit. See the documentation for a list of commands. */ ++ ++static int qc_command(struct qcam *q, int command) ++{ ++ int n1, n2; ++ int cmd; ++ ++ write_lpdata(q, command); ++ write_lpcontrol(q, 6); ++ ++ n1 = qc_waithand(q, 1); ++ ++ write_lpcontrol(q, 0xe); ++ n2 = qc_waithand(q, 0); ++ ++ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); ++ return cmd; ++} ++ ++static int qc_readparam(struct qcam *q) ++{ ++ int n1, n2; ++ int cmd; ++ ++ write_lpcontrol(q, 6); ++ n1 = qc_waithand(q, 1); ++ ++ write_lpcontrol(q, 0xe); ++ n2 = qc_waithand(q, 0); ++ ++ cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); ++ return cmd; ++} ++ ++ ++/* Try to detect a QuickCam. It appears to flash the upper 4 bits of ++ the status register at 5-10 Hz. This is only used in the autoprobe ++ code. Be aware that this isn't the way Connectix detects the ++ camera (they send a reset and try to handshake), but this should be ++ almost completely safe, while their method screws up my printer if ++ I plug it in before the camera. */ ++ ++static int qc_detect(struct qcam *q) ++{ ++ int reg, lastreg; ++ int count = 0; ++ int i; ++ ++ if (force_init) ++ return 1; ++ ++ lastreg = reg = read_lpstatus(q) & 0xf0; ++ ++ for (i = 0; i < 500; i++) { ++ reg = read_lpstatus(q) & 0xf0; ++ if (reg != lastreg) ++ count++; ++ lastreg = reg; ++ mdelay(2); ++ } ++ ++ ++#if 0 ++ /* Force camera detection during testing. Sometimes the camera ++ won't be flashing these bits. Possibly unloading the module ++ in the middle of a grab? Or some timeout condition? ++ I've seen this parameter as low as 19 on my 450Mhz box - mpc */ ++ printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); ++ return 1; ++#endif ++ ++ /* Be (even more) liberal in what you accept... */ ++ ++ if (count > 20 && count < 400) { ++ return 1; /* found */ ++ } else { ++ printk(KERN_ERR "No Quickcam found on port %s\n", ++ q->pport->name); ++ printk(KERN_DEBUG "Quickcam detection counter: %u\n", count); ++ return 0; /* not found */ ++ } ++} ++ ++/* Decide which scan mode to use. There's no real requirement that ++ * the scanmode match the resolution in q->height and q-> width -- the ++ * camera takes the picture at the resolution specified in the ++ * "scanmode" and then returns the image at the resolution specified ++ * with the resolution commands. If the scan is bigger than the ++ * requested resolution, the upper-left hand corner of the scan is ++ * returned. If the scan is smaller, then the rest of the image ++ * returned contains garbage. */ ++ ++static int qc_setscanmode(struct qcam *q) ++{ ++ int old_mode = q->mode; ++ ++ switch (q->transfer_scale) { ++ case 1: ++ q->mode = 0; ++ break; ++ case 2: ++ q->mode = 4; ++ break; ++ case 4: ++ q->mode = 8; ++ break; ++ } ++ ++ switch (q->bpp) { ++ case 4: ++ break; ++ case 6: ++ q->mode += 2; ++ break; ++ } ++ ++ switch (q->port_mode & QC_MODE_MASK) { ++ case QC_BIDIR: ++ q->mode += 1; ++ break; ++ case QC_NOTSET: ++ case QC_UNIDIR: ++ break; ++ } ++ ++ if (q->mode != old_mode) ++ q->status |= QC_PARAM_CHANGE; ++ ++ return 0; ++} ++ ++ ++/* Reset the QuickCam. This uses the same sequence the Windows ++ * QuickPic program uses. Someone with a bi-directional port should ++ * check that bi-directional mode is detected right, and then ++ * implement bi-directional mode in qc_readbyte(). */ ++ ++static void qc_reset(struct qcam *q) ++{ ++ switch (q->port_mode & QC_FORCE_MASK) { ++ case QC_FORCE_UNIDIR: ++ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; ++ break; ++ ++ case QC_FORCE_BIDIR: ++ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; ++ break; ++ ++ case QC_ANY: ++ write_lpcontrol(q, 0x20); ++ write_lpdata(q, 0x75); ++ ++ if (read_lpdata(q) != 0x75) ++ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; ++ else ++ q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; ++ break; ++ } ++ ++ write_lpcontrol(q, 0xb); ++ udelay(250); ++ write_lpcontrol(q, 0xe); ++ qc_setscanmode(q); /* in case port_mode changed */ ++} ++ ++ ++ ++/* Reset the QuickCam and program for brightness, contrast, ++ * white-balance, and resolution. */ ++ ++static void qc_set(struct qcam *q) ++{ ++ int val; ++ int val2; ++ ++ qc_reset(q); ++ ++ /* Set the brightness. Yes, this is repetitive, but it works. ++ * Shorter versions seem to fail subtly. Feel free to try :-). */ ++ /* I think the problem was in qc_command, not here -- bls */ ++ ++ qc_command(q, 0xb); ++ qc_command(q, q->brightness); ++ ++ val = q->height / q->transfer_scale; ++ qc_command(q, 0x11); ++ qc_command(q, val); ++ if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) { ++ /* The normal "transfers per line" calculation doesn't seem to work ++ as expected here (and yet it works fine in qc_scan). No idea ++ why this case is the odd man out. Fortunately, Laird's original ++ working version gives me a good way to guess at working values. ++ -- bls */ ++ val = q->width; ++ val2 = q->transfer_scale * 4; ++ } else { ++ val = q->width * q->bpp; ++ val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * ++ q->transfer_scale; ++ } ++ val = DIV_ROUND_UP(val, val2); ++ qc_command(q, 0x13); ++ qc_command(q, val); ++ ++ /* Setting top and left -- bls */ ++ qc_command(q, 0xd); ++ qc_command(q, q->top); ++ qc_command(q, 0xf); ++ qc_command(q, q->left / 2); ++ ++ qc_command(q, 0x19); ++ qc_command(q, q->contrast); ++ qc_command(q, 0x1f); ++ qc_command(q, q->whitebal); ++ ++ /* Clear flag that we must update the grabbing parameters on the camera ++ before we grab the next frame */ ++ q->status &= (~QC_PARAM_CHANGE); ++} ++ ++/* Qc_readbytes reads some bytes from the QC and puts them in ++ the supplied buffer. It returns the number of bytes read, ++ or -1 on error. */ ++ ++static inline int qc_readbytes(struct qcam *q, char buffer[]) ++{ ++ int ret = 1; ++ unsigned int hi, lo; ++ unsigned int hi2, lo2; ++ static int state; ++ ++ if (buffer == NULL) { ++ state = 0; ++ return 0; ++ } ++ ++ switch (q->port_mode & QC_MODE_MASK) { ++ case QC_BIDIR: /* Bi-directional Port */ ++ write_lpcontrol(q, 0x26); ++ lo = (qc_waithand2(q, 1) >> 1); ++ hi = (read_lpstatus(q) >> 3) & 0x1f; ++ write_lpcontrol(q, 0x2e); ++ lo2 = (qc_waithand2(q, 0) >> 1); ++ hi2 = (read_lpstatus(q) >> 3) & 0x1f; ++ switch (q->bpp) { ++ case 4: ++ buffer[0] = lo & 0xf; ++ buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); ++ buffer[2] = (hi & 0x1e) >> 1; ++ buffer[3] = lo2 & 0xf; ++ buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); ++ buffer[5] = (hi2 & 0x1e) >> 1; ++ ret = 6; ++ break; ++ case 6: ++ buffer[0] = lo & 0x3f; ++ buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); ++ buffer[2] = lo2 & 0x3f; ++ buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); ++ ret = 4; ++ break; ++ } ++ break; ++ ++ case QC_UNIDIR: /* Unidirectional Port */ ++ write_lpcontrol(q, 6); ++ lo = (qc_waithand(q, 1) & 0xf0) >> 4; ++ write_lpcontrol(q, 0xe); ++ hi = (qc_waithand(q, 0) & 0xf0) >> 4; ++ ++ switch (q->bpp) { ++ case 4: ++ buffer[0] = lo; ++ buffer[1] = hi; ++ ret = 2; ++ break; ++ case 6: ++ switch (state) { ++ case 0: ++ buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); ++ q->saved_bits = (hi & 3) << 4; ++ state = 1; ++ ret = 1; ++ break; ++ case 1: ++ buffer[0] = lo | q->saved_bits; ++ q->saved_bits = hi << 2; ++ state = 2; ++ ret = 1; ++ break; ++ case 2: ++ buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; ++ buffer[1] = ((lo & 3) << 4) | hi; ++ state = 0; ++ ret = 2; ++ break; ++ } ++ break; ++ } ++ break; ++ } ++ return ret; ++} ++ ++/* requests a scan from the camera. It sends the correct instructions ++ * to the camera and then reads back the correct number of bytes. In ++ * previous versions of this routine the return structure contained ++ * the raw output from the camera, and there was a 'qc_convertscan' ++ * function that converted that to a useful format. In version 0.3 I ++ * rolled qc_convertscan into qc_scan and now I only return the ++ * converted scan. The format is just an one-dimensional array of ++ * characters, one for each pixel, with 0=black up to n=white, where ++ * n=2^(bit depth)-1. Ask me for more details if you don't understand ++ * this. */ ++ ++static long qc_capture(struct qcam *q, char __user *buf, unsigned long len) ++{ ++ int i, j, k, yield; ++ int bytes; ++ int linestotrans, transperline; ++ int divisor; ++ int pixels_per_line; ++ int pixels_read = 0; ++ int got = 0; ++ char buffer[6]; ++ int shift = 8 - q->bpp; ++ char invert; ++ ++ if (q->mode == -1) ++ return -ENXIO; ++ ++ qc_command(q, 0x7); ++ qc_command(q, q->mode); ++ ++ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { ++ write_lpcontrol(q, 0x2e); /* turn port around */ ++ write_lpcontrol(q, 0x26); ++ qc_waithand(q, 1); ++ write_lpcontrol(q, 0x2e); ++ qc_waithand(q, 0); ++ } ++ ++ /* strange -- should be 15:63 below, but 4bpp is odd */ ++ invert = (q->bpp == 4) ? 16 : 63; ++ ++ linestotrans = q->height / q->transfer_scale; ++ pixels_per_line = q->width / q->transfer_scale; ++ transperline = q->width * q->bpp; ++ divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * ++ q->transfer_scale; ++ transperline = DIV_ROUND_UP(transperline, divisor); ++ ++ for (i = 0, yield = yieldlines; i < linestotrans; i++) { ++ for (pixels_read = j = 0; j < transperline; j++) { ++ bytes = qc_readbytes(q, buffer); ++ for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) { ++ int o; ++ if (buffer[k] == 0 && invert == 16) { ++ /* 4bpp is odd (again) -- inverter is 16, not 15, but output ++ must be 0-15 -- bls */ ++ buffer[k] = 16; ++ } ++ o = i * pixels_per_line + pixels_read + k; ++ if (o < len) { ++ u8 ch = invert - buffer[k]; ++ got++; ++ put_user(ch << shift, buf + o); ++ } ++ } ++ pixels_read += bytes; ++ } ++ qc_readbytes(q, NULL); /* reset state machine */ ++ ++ /* Grabbing an entire frame from the quickcam is a lengthy ++ process. We don't (usually) want to busy-block the ++ processor for the entire frame. yieldlines is a module ++ parameter. If we yield every line, the minimum frame ++ time will be 240 / 200 = 1.2 seconds. The compile-time ++ default is to yield every 4 lines. */ ++ if (i >= yield) { ++ msleep_interruptible(5); ++ yield = i + yieldlines; ++ } ++ } ++ ++ if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { ++ write_lpcontrol(q, 2); ++ write_lpcontrol(q, 6); ++ udelay(3); ++ write_lpcontrol(q, 0xe); ++ } ++ if (got < len) ++ return got; ++ return len; ++} ++ ++/* ++ * Video4linux interfacing ++ */ ++ ++static int qcam_querycap(struct file *file, void *priv, ++ struct v4l2_capability *vcap) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ ++ strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); ++ strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card)); ++ strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info)); ++ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; ++ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ if (vin->index > 0) ++ return -EINVAL; ++ strlcpy(vin->name, "Camera", sizeof(vin->name)); ++ vin->type = V4L2_INPUT_TYPE_CAMERA; ++ vin->audioset = 0; ++ vin->tuner = 0; ++ vin->std = 0; ++ vin->status = 0; ++ return 0; ++} ++ ++static int qcam_g_input(struct file *file, void *fh, unsigned int *inp) ++{ ++ *inp = 0; ++ return 0; ++} ++ ++static int qcam_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ return (inp > 0) ? -EINVAL : 0; ++} ++ ++static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ pix->width = qcam->width / qcam->transfer_scale; ++ pix->height = qcam->height / qcam->transfer_scale; ++ pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = pix->width; ++ pix->sizeimage = pix->width * pix->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ if (pix->height <= 60 || pix->width <= 80) { ++ pix->height = 60; ++ pix->width = 80; ++ } else if (pix->height <= 120 || pix->width <= 160) { ++ pix->height = 120; ++ pix->width = 160; ++ } else { ++ pix->height = 240; ++ pix->width = 320; ++ } ++ if (pix->pixelformat != V4L2_PIX_FMT_Y4 && ++ pix->pixelformat != V4L2_PIX_FMT_Y6) ++ pix->pixelformat = V4L2_PIX_FMT_Y4; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = pix->width; ++ pix->sizeimage = pix->width * pix->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ int ret = qcam_try_fmt_vid_cap(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ qcam->width = 320; ++ qcam->height = 240; ++ if (pix->height == 60) ++ qcam->transfer_scale = 4; ++ else if (pix->height == 120) ++ qcam->transfer_scale = 2; ++ else ++ qcam->transfer_scale = 1; ++ if (pix->pixelformat == V4L2_PIX_FMT_Y6) ++ qcam->bpp = 6; ++ else ++ qcam->bpp = 4; ++ ++ mutex_lock(&qcam->lock); ++ qc_setscanmode(qcam); ++ /* We must update the camera before we grab. We could ++ just have changed the grab size */ ++ qcam->status |= QC_PARAM_CHANGE; ++ mutex_unlock(&qcam->lock); ++ return 0; ++} ++ ++static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static struct v4l2_fmtdesc formats[] = { ++ { 0, 0, 0, ++ "4-Bit Monochrome", V4L2_PIX_FMT_Y4, ++ { 0, 0, 0, 0 } ++ }, ++ { 1, 0, 0, ++ "6-Bit Monochrome", V4L2_PIX_FMT_Y6, ++ { 0, 0, 0, 0 } ++ }, ++ }; ++ enum v4l2_buf_type type = fmt->type; ++ ++ if (fmt->index > 1) ++ return -EINVAL; ++ ++ *fmt = formats[fmt->index]; ++ fmt->type = type; ++ return 0; ++} ++ ++static int qcam_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ static const struct v4l2_frmsize_discrete sizes[] = { ++ { 80, 60 }, ++ { 160, 120 }, ++ { 320, 240 }, ++ }; ++ ++ if (fsize->index > 2) ++ return -EINVAL; ++ if (fsize->pixel_format != V4L2_PIX_FMT_Y4 && ++ fsize->pixel_format != V4L2_PIX_FMT_Y6) ++ return -EINVAL; ++ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; ++ fsize->discrete = sizes[fsize->index]; ++ return 0; ++} ++ ++static ssize_t qcam_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ int len; ++ parport_claim_or_block(qcam->pdev); ++ ++ mutex_lock(&qcam->lock); ++ ++ qc_reset(qcam); ++ ++ /* Update the camera parameters if we need to */ ++ if (qcam->status & QC_PARAM_CHANGE) ++ qc_set(qcam); ++ ++ len = qc_capture(qcam, buf, count); ++ ++ mutex_unlock(&qcam->lock); ++ ++ parport_release(qcam->pdev); ++ return len; ++} ++ ++static unsigned int qcam_poll(struct file *filp, poll_table *wait) ++{ ++ return v4l2_ctrl_poll(filp, wait) | POLLIN | POLLRDNORM; ++} ++ ++static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct qcam *qcam = ++ container_of(ctrl->handler, struct qcam, hdl); ++ int ret = 0; ++ ++ mutex_lock(&qcam->lock); ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ qcam->brightness = ctrl->val; ++ break; ++ case V4L2_CID_CONTRAST: ++ qcam->contrast = ctrl->val; ++ break; ++ case V4L2_CID_GAMMA: ++ qcam->whitebal = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ if (ret == 0) { ++ qc_setscanmode(qcam); ++ qcam->status |= QC_PARAM_CHANGE; ++ } ++ mutex_unlock(&qcam->lock); ++ return ret; ++} ++ ++static const struct v4l2_file_operations qcam_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = v4l2_fh_release, ++ .poll = qcam_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .read = qcam_read, ++}; ++ ++static const struct v4l2_ioctl_ops qcam_ioctl_ops = { ++ .vidioc_querycap = qcam_querycap, ++ .vidioc_g_input = qcam_g_input, ++ .vidioc_s_input = qcam_s_input, ++ .vidioc_enum_input = qcam_enum_input, ++ .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, ++ .vidioc_enum_framesizes = qcam_enum_framesizes, ++ .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static const struct v4l2_ctrl_ops qcam_ctrl_ops = { ++ .s_ctrl = qcam_s_ctrl, ++}; ++ ++/* Initialize the QuickCam driver control structure. This is where ++ * defaults are set for people who don't have a config file.*/ ++ ++static struct qcam *qcam_init(struct parport *port) ++{ ++ struct qcam *qcam; ++ struct v4l2_device *v4l2_dev; ++ ++ qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL); ++ if (qcam == NULL) ++ return NULL; ++ ++ v4l2_dev = &qcam->v4l2_dev; ++ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%d", num_cams); ++ ++ if (v4l2_device_register(port->dev, v4l2_dev) < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ kfree(qcam); ++ return NULL; ++ } ++ ++ v4l2_ctrl_handler_init(&qcam->hdl, 3); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 180); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 192); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_GAMMA, 0, 255, 1, 105); ++ if (qcam->hdl.error) { ++ v4l2_err(v4l2_dev, "couldn't register controls\n"); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ kfree(qcam); ++ return NULL; ++ } ++ qcam->pport = port; ++ qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL, ++ NULL, 0, NULL); ++ if (qcam->pdev == NULL) { ++ v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ kfree(qcam); ++ return NULL; ++ } ++ ++ strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); ++ qcam->vdev.v4l2_dev = v4l2_dev; ++ qcam->vdev.ctrl_handler = &qcam->hdl; ++ qcam->vdev.fops = &qcam_fops; ++ qcam->vdev.ioctl_ops = &qcam_ioctl_ops; ++ set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); ++ qcam->vdev.release = video_device_release_empty; ++ video_set_drvdata(&qcam->vdev, qcam); ++ ++ mutex_init(&qcam->lock); ++ ++ qcam->port_mode = (QC_ANY | QC_NOTSET); ++ qcam->width = 320; ++ qcam->height = 240; ++ qcam->bpp = 4; ++ qcam->transfer_scale = 2; ++ qcam->contrast = 192; ++ qcam->brightness = 180; ++ qcam->whitebal = 105; ++ qcam->top = 1; ++ qcam->left = 14; ++ qcam->mode = -1; ++ qcam->status = QC_PARAM_CHANGE; ++ return qcam; ++} ++ ++static int qc_calibrate(struct qcam *q) ++{ ++ /* ++ * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96 ++ * The white balance is an individual value for each ++ * quickcam. ++ */ ++ ++ int value; ++ int count = 0; ++ ++ qc_command(q, 27); /* AutoAdjustOffset */ ++ qc_command(q, 0); /* Dummy Parameter, ignored by the camera */ ++ ++ /* GetOffset (33) will read 255 until autocalibration */ ++ /* is finished. After that, a value of 1-254 will be */ ++ /* returned. */ ++ ++ do { ++ qc_command(q, 33); ++ value = qc_readparam(q); ++ mdelay(1); ++ schedule(); ++ count++; ++ } while (value == 0xff && count < 2048); ++ ++ q->whitebal = value; ++ return value; ++} ++ ++static int init_bwqcam(struct parport *port) ++{ ++ struct qcam *qcam; ++ ++ if (num_cams == MAX_CAMS) { ++ printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); ++ return -ENOSPC; ++ } ++ ++ qcam = qcam_init(port); ++ if (qcam == NULL) ++ return -ENODEV; ++ ++ parport_claim_or_block(qcam->pdev); ++ ++ qc_reset(qcam); ++ ++ if (qc_detect(qcam) == 0) { ++ parport_release(qcam->pdev); ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++ return -ENODEV; ++ } ++ qc_calibrate(qcam); ++ v4l2_ctrl_handler_setup(&qcam->hdl); ++ ++ parport_release(qcam->pdev); ++ ++ v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name); ++ ++ if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++ return -ENODEV; ++ } ++ ++ qcams[num_cams++] = qcam; ++ ++ return 0; ++} ++ ++static void close_bwqcam(struct qcam *qcam) ++{ ++ video_unregister_device(&qcam->vdev); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++} ++ ++/* The parport parameter controls which parports will be scanned. ++ * Scanning all parports causes some printers to print a garbage page. ++ * -- March 14, 1999 Billy Donahue */ ++#ifdef MODULE ++static char *parport[MAX_CAMS] = { NULL, }; ++module_param_array(parport, charp, NULL, 0); ++#endif ++ ++static int accept_bwqcam(struct parport *port) ++{ ++#ifdef MODULE ++ int n; ++ ++ if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { ++ /* user gave parport parameters */ ++ for (n = 0; n < MAX_CAMS && parport[n]; n++) { ++ char *ep; ++ unsigned long r; ++ r = simple_strtoul(parport[n], &ep, 0); ++ if (ep == parport[n]) { ++ printk(KERN_ERR ++ "bw-qcam: bad port specifier \"%s\"\n", ++ parport[n]); ++ continue; ++ } ++ if (r == port->number) ++ return 1; ++ } ++ return 0; ++ } ++#endif ++ return 1; ++} ++ ++static void bwqcam_attach(struct parport *port) ++{ ++ if (accept_bwqcam(port)) ++ init_bwqcam(port); ++} ++ ++static void bwqcam_detach(struct parport *port) ++{ ++ int i; ++ for (i = 0; i < num_cams; i++) { ++ struct qcam *qcam = qcams[i]; ++ if (qcam && qcam->pdev->port == port) { ++ qcams[i] = NULL; ++ close_bwqcam(qcam); ++ } ++ } ++} ++ ++static struct parport_driver bwqcam_driver = { ++ .name = "bw-qcam", ++ .attach = bwqcam_attach, ++ .detach = bwqcam_detach, ++}; ++ ++static void __exit exit_bw_qcams(void) ++{ ++ parport_unregister_driver(&bwqcam_driver); ++} ++ ++static int __init init_bw_qcams(void) ++{ ++#ifdef MODULE ++ /* Do some sanity checks on the module parameters. */ ++ if (maxpoll > 5000) { ++ printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n"); ++ maxpoll = 5000; ++ } ++ ++ if (yieldlines < 1) { ++ printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n"); ++ yieldlines = 1; ++ } ++#endif ++ return parport_register_driver(&bwqcam_driver); ++} ++ ++module_init(init_bw_qcams); ++module_exit(exit_bw_qcams); ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.0.3"); +diff --git a/drivers/media/parport/c-qcam.c b/drivers/media/parport/c-qcam.c +new file mode 100644 +index 0000000..ec51e1f +--- /dev/null ++++ b/drivers/media/parport/c-qcam.c +@@ -0,0 +1,883 @@ ++/* ++ * Video4Linux Colour QuickCam driver ++ * Copyright 1997-2000 Philip Blundell ++ * ++ * Module parameters: ++ * ++ * parport=auto -- probe all parports (default) ++ * parport=0 -- parport0 becomes qcam1 ++ * parport=2,0,1 -- parports 2,0,1 are tried in that order ++ * ++ * probe=0 -- do no probing, assume camera is present ++ * probe=1 -- use IEEE-1284 autoprobe data only (default) ++ * probe=2 -- probe aggressively for cameras ++ * ++ * force_rgb=1 -- force data format to RGB (default is BGR) ++ * ++ * The parport parameter controls which parports will be scanned. ++ * Scanning all parports causes some printers to print a garbage page. ++ * -- March 14, 1999 Billy Donahue ++ * ++ * Fixed data format to BGR, added force_rgb parameter. Added missing ++ * parport_unregister_driver() on module removal. ++ * -- May 28, 2000 Claudio Matsuoka ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct qcam { ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct v4l2_ctrl_handler hdl; ++ struct pardevice *pdev; ++ struct parport *pport; ++ int width, height; ++ int ccd_width, ccd_height; ++ int mode; ++ int contrast, brightness, whitebal; ++ int top, left; ++ unsigned int bidirectional; ++ struct mutex lock; ++}; ++ ++/* cameras maximum */ ++#define MAX_CAMS 4 ++ ++/* The three possible QuickCam modes */ ++#define QC_MILLIONS 0x18 ++#define QC_BILLIONS 0x10 ++#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */ ++ ++/* The three possible decimations */ ++#define QC_DECIMATION_1 0 ++#define QC_DECIMATION_2 2 ++#define QC_DECIMATION_4 4 ++ ++#define BANNER "Colour QuickCam for Video4Linux v0.06" ++ ++static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; ++static int probe = 2; ++static bool force_rgb; ++static int video_nr = -1; ++ ++/* FIXME: parport=auto would never have worked, surely? --RR */ ++MODULE_PARM_DESC(parport, "parport= for port detection method\n" ++ "probe=<0|1|2> for camera detection method\n" ++ "force_rgb=<0|1> for RGB data format (default BGR)"); ++module_param_array(parport, int, NULL, 0); ++module_param(probe, int, 0); ++module_param(force_rgb, bool, 0); ++module_param(video_nr, int, 0); ++ ++static struct qcam *qcams[MAX_CAMS]; ++static unsigned int num_cams; ++ ++static inline void qcam_set_ack(struct qcam *qcam, unsigned int i) ++{ ++ /* note: the QC specs refer to the PCAck pin by voltage, not ++ software level. PC ports have builtin inverters. */ ++ parport_frob_control(qcam->pport, 8, i ? 8 : 0); ++} ++ ++static inline unsigned int qcam_ready1(struct qcam *qcam) ++{ ++ return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; ++} ++ ++static inline unsigned int qcam_ready2(struct qcam *qcam) ++{ ++ return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; ++} ++ ++static unsigned int qcam_await_ready1(struct qcam *qcam, int value) ++{ ++ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; ++ unsigned long oldjiffies = jiffies; ++ unsigned int i; ++ ++ for (oldjiffies = jiffies; ++ time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) ++ if (qcam_ready1(qcam) == value) ++ return 0; ++ ++ /* If the camera didn't respond within 1/25 second, poll slowly ++ for a while. */ ++ for (i = 0; i < 50; i++) { ++ if (qcam_ready1(qcam) == value) ++ return 0; ++ msleep_interruptible(100); ++ } ++ ++ /* Probably somebody pulled the plug out. Not much we can do. */ ++ v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value, ++ parport_read_status(qcam->pport), ++ parport_read_control(qcam->pport)); ++ return 1; ++} ++ ++static unsigned int qcam_await_ready2(struct qcam *qcam, int value) ++{ ++ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; ++ unsigned long oldjiffies = jiffies; ++ unsigned int i; ++ ++ for (oldjiffies = jiffies; ++ time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) ++ if (qcam_ready2(qcam) == value) ++ return 0; ++ ++ /* If the camera didn't respond within 1/25 second, poll slowly ++ for a while. */ ++ for (i = 0; i < 50; i++) { ++ if (qcam_ready2(qcam) == value) ++ return 0; ++ msleep_interruptible(100); ++ } ++ ++ /* Probably somebody pulled the plug out. Not much we can do. */ ++ v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value, ++ parport_read_status(qcam->pport), ++ parport_read_control(qcam->pport), ++ parport_read_data(qcam->pport)); ++ return 1; ++} ++ ++static int qcam_read_data(struct qcam *qcam) ++{ ++ unsigned int idata; ++ ++ qcam_set_ack(qcam, 0); ++ if (qcam_await_ready1(qcam, 1)) ++ return -1; ++ idata = parport_read_status(qcam->pport) & 0xf0; ++ qcam_set_ack(qcam, 1); ++ if (qcam_await_ready1(qcam, 0)) ++ return -1; ++ idata |= parport_read_status(qcam->pport) >> 4; ++ return idata; ++} ++ ++static int qcam_write_data(struct qcam *qcam, unsigned int data) ++{ ++ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; ++ unsigned int idata; ++ ++ parport_write_data(qcam->pport, data); ++ idata = qcam_read_data(qcam); ++ if (data != idata) { ++ v4l2_warn(v4l2_dev, "sent %x but received %x\n", data, ++ idata); ++ return 1; ++ } ++ return 0; ++} ++ ++static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data) ++{ ++ if (qcam_write_data(qcam, cmd)) ++ return -1; ++ if (qcam_write_data(qcam, data)) ++ return -1; ++ return 0; ++} ++ ++static inline int qcam_get(struct qcam *qcam, unsigned int cmd) ++{ ++ if (qcam_write_data(qcam, cmd)) ++ return -1; ++ return qcam_read_data(qcam); ++} ++ ++static int qc_detect(struct qcam *qcam) ++{ ++ unsigned int stat, ostat, i, count = 0; ++ ++ /* The probe routine below is not very reliable. The IEEE-1284 ++ probe takes precedence. */ ++ /* XXX Currently parport provides no way to distinguish between ++ "the IEEE probe was not done" and "the probe was done, but ++ no device was found". Fix this one day. */ ++ if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA ++ && qcam->pport->probe_info[0].model ++ && !strcmp(qcam->pdev->port->probe_info[0].model, ++ "Color QuickCam 2.0")) { ++ printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n"); ++ return 1; ++ } ++ ++ if (probe < 2) ++ return 0; ++ ++ parport_write_control(qcam->pport, 0xc); ++ ++ /* look for a heartbeat */ ++ ostat = stat = parport_read_status(qcam->pport); ++ for (i = 0; i < 250; i++) { ++ mdelay(1); ++ stat = parport_read_status(qcam->pport); ++ if (ostat != stat) { ++ if (++count >= 3) ++ return 1; ++ ostat = stat; ++ } ++ } ++ ++ /* Reset the camera and try again */ ++ parport_write_control(qcam->pport, 0xc); ++ parport_write_control(qcam->pport, 0x8); ++ mdelay(1); ++ parport_write_control(qcam->pport, 0xc); ++ mdelay(1); ++ count = 0; ++ ++ ostat = stat = parport_read_status(qcam->pport); ++ for (i = 0; i < 250; i++) { ++ mdelay(1); ++ stat = parport_read_status(qcam->pport); ++ if (ostat != stat) { ++ if (++count >= 3) ++ return 1; ++ ostat = stat; ++ } ++ } ++ ++ /* no (or flatline) camera, give up */ ++ return 0; ++} ++ ++static void qc_reset(struct qcam *qcam) ++{ ++ parport_write_control(qcam->pport, 0xc); ++ parport_write_control(qcam->pport, 0x8); ++ mdelay(1); ++ parport_write_control(qcam->pport, 0xc); ++ mdelay(1); ++} ++ ++/* Reset the QuickCam and program for brightness, contrast, ++ * white-balance, and resolution. */ ++ ++static void qc_setup(struct qcam *qcam) ++{ ++ qc_reset(qcam); ++ ++ /* Set the brightness. */ ++ qcam_set(qcam, 11, qcam->brightness); ++ ++ /* Set the height and width. These refer to the actual ++ CCD area *before* applying the selected decimation. */ ++ qcam_set(qcam, 17, qcam->ccd_height); ++ qcam_set(qcam, 19, qcam->ccd_width / 2); ++ ++ /* Set top and left. */ ++ qcam_set(qcam, 0xd, qcam->top); ++ qcam_set(qcam, 0xf, qcam->left); ++ ++ /* Set contrast and white balance. */ ++ qcam_set(qcam, 0x19, qcam->contrast); ++ qcam_set(qcam, 0x1f, qcam->whitebal); ++ ++ /* Set the speed. */ ++ qcam_set(qcam, 45, 2); ++} ++ ++/* Read some bytes from the camera and put them in the buffer. ++ nbytes should be a multiple of 3, because bidirectional mode gives ++ us three bytes at a time. */ ++ ++static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes) ++{ ++ unsigned int bytes = 0; ++ ++ qcam_set_ack(qcam, 0); ++ if (qcam->bidirectional) { ++ /* It's a bidirectional port */ ++ while (bytes < nbytes) { ++ unsigned int lo1, hi1, lo2, hi2; ++ unsigned char r, g, b; ++ ++ if (qcam_await_ready2(qcam, 1)) ++ return bytes; ++ lo1 = parport_read_data(qcam->pport) >> 1; ++ hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; ++ qcam_set_ack(qcam, 1); ++ if (qcam_await_ready2(qcam, 0)) ++ return bytes; ++ lo2 = parport_read_data(qcam->pport) >> 1; ++ hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; ++ qcam_set_ack(qcam, 0); ++ r = lo1 | ((hi1 & 1) << 7); ++ g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); ++ b = lo2 | ((hi2 & 1) << 7); ++ if (force_rgb) { ++ buf[bytes++] = r; ++ buf[bytes++] = g; ++ buf[bytes++] = b; ++ } else { ++ buf[bytes++] = b; ++ buf[bytes++] = g; ++ buf[bytes++] = r; ++ } ++ } ++ } else { ++ /* It's a unidirectional port */ ++ int i = 0, n = bytes; ++ unsigned char rgb[3]; ++ ++ while (bytes < nbytes) { ++ unsigned int hi, lo; ++ ++ if (qcam_await_ready1(qcam, 1)) ++ return bytes; ++ hi = (parport_read_status(qcam->pport) & 0xf0); ++ qcam_set_ack(qcam, 1); ++ if (qcam_await_ready1(qcam, 0)) ++ return bytes; ++ lo = (parport_read_status(qcam->pport) & 0xf0); ++ qcam_set_ack(qcam, 0); ++ /* flip some bits */ ++ rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88; ++ if (i >= 2) { ++get_fragment: ++ if (force_rgb) { ++ buf[n++] = rgb[0]; ++ buf[n++] = rgb[1]; ++ buf[n++] = rgb[2]; ++ } else { ++ buf[n++] = rgb[2]; ++ buf[n++] = rgb[1]; ++ buf[n++] = rgb[0]; ++ } ++ } ++ } ++ if (i) { ++ i = 0; ++ goto get_fragment; ++ } ++ } ++ return bytes; ++} ++ ++#define BUFSZ 150 ++ ++static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) ++{ ++ struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; ++ unsigned lines, pixelsperline; ++ unsigned int is_bi_dir = qcam->bidirectional; ++ size_t wantlen, outptr = 0; ++ char tmpbuf[BUFSZ]; ++ ++ if (!access_ok(VERIFY_WRITE, buf, len)) ++ return -EFAULT; ++ ++ /* Wait for camera to become ready */ ++ for (;;) { ++ int i = qcam_get(qcam, 41); ++ ++ if (i == -1) { ++ qc_setup(qcam); ++ return -EIO; ++ } ++ if ((i & 0x80) == 0) ++ break; ++ schedule(); ++ } ++ ++ if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1)) ++ return -EIO; ++ ++ lines = qcam->height; ++ pixelsperline = qcam->width; ++ ++ if (is_bi_dir) { ++ /* Turn the port around */ ++ parport_data_reverse(qcam->pport); ++ mdelay(3); ++ qcam_set_ack(qcam, 0); ++ if (qcam_await_ready1(qcam, 1)) { ++ qc_setup(qcam); ++ return -EIO; ++ } ++ qcam_set_ack(qcam, 1); ++ if (qcam_await_ready1(qcam, 0)) { ++ qc_setup(qcam); ++ return -EIO; ++ } ++ } ++ ++ wantlen = lines * pixelsperline * 24 / 8; ++ ++ while (wantlen) { ++ size_t t, s; ++ ++ s = (wantlen > BUFSZ) ? BUFSZ : wantlen; ++ t = qcam_read_bytes(qcam, tmpbuf, s); ++ if (outptr < len) { ++ size_t sz = len - outptr; ++ ++ if (sz > t) ++ sz = t; ++ if (__copy_to_user(buf + outptr, tmpbuf, sz)) ++ break; ++ outptr += sz; ++ } ++ wantlen -= t; ++ if (t < s) ++ break; ++ cond_resched(); ++ } ++ ++ len = outptr; ++ ++ if (wantlen) { ++ v4l2_err(v4l2_dev, "short read.\n"); ++ if (is_bi_dir) ++ parport_data_forward(qcam->pport); ++ qc_setup(qcam); ++ return len; ++ } ++ ++ if (is_bi_dir) { ++ int l; ++ ++ do { ++ l = qcam_read_bytes(qcam, tmpbuf, 3); ++ cond_resched(); ++ } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); ++ if (force_rgb) { ++ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) ++ v4l2_err(v4l2_dev, "bad EOF\n"); ++ } else { ++ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) ++ v4l2_err(v4l2_dev, "bad EOF\n"); ++ } ++ qcam_set_ack(qcam, 0); ++ if (qcam_await_ready1(qcam, 1)) { ++ v4l2_err(v4l2_dev, "no ack after EOF\n"); ++ parport_data_forward(qcam->pport); ++ qc_setup(qcam); ++ return len; ++ } ++ parport_data_forward(qcam->pport); ++ mdelay(3); ++ qcam_set_ack(qcam, 1); ++ if (qcam_await_ready1(qcam, 0)) { ++ v4l2_err(v4l2_dev, "no ack to port turnaround\n"); ++ qc_setup(qcam); ++ return len; ++ } ++ } else { ++ int l; ++ ++ do { ++ l = qcam_read_bytes(qcam, tmpbuf, 1); ++ cond_resched(); ++ } while (l && tmpbuf[0] == 0x7e); ++ l = qcam_read_bytes(qcam, tmpbuf + 1, 2); ++ if (force_rgb) { ++ if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) ++ v4l2_err(v4l2_dev, "bad EOF\n"); ++ } else { ++ if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) ++ v4l2_err(v4l2_dev, "bad EOF\n"); ++ } ++ } ++ ++ qcam_write_data(qcam, 0); ++ return len; ++} ++ ++/* ++ * Video4linux interfacing ++ */ ++ ++static int qcam_querycap(struct file *file, void *priv, ++ struct v4l2_capability *vcap) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ ++ strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); ++ strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card)); ++ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); ++ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; ++ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ if (vin->index > 0) ++ return -EINVAL; ++ strlcpy(vin->name, "Camera", sizeof(vin->name)); ++ vin->type = V4L2_INPUT_TYPE_CAMERA; ++ vin->audioset = 0; ++ vin->tuner = 0; ++ vin->std = 0; ++ vin->status = 0; ++ return 0; ++} ++ ++static int qcam_g_input(struct file *file, void *fh, unsigned int *inp) ++{ ++ *inp = 0; ++ return 0; ++} ++ ++static int qcam_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ return (inp > 0) ? -EINVAL : 0; ++} ++ ++static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ pix->width = qcam->width; ++ pix->height = qcam->height; ++ pix->pixelformat = V4L2_PIX_FMT_RGB24; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 3 * qcam->width; ++ pix->sizeimage = 3 * qcam->width * qcam->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ if (pix->height < 60 || pix->width < 80) { ++ pix->height = 60; ++ pix->width = 80; ++ } else if (pix->height < 120 || pix->width < 160) { ++ pix->height = 120; ++ pix->width = 160; ++ } else { ++ pix->height = 240; ++ pix->width = 320; ++ } ++ pix->pixelformat = V4L2_PIX_FMT_RGB24; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 3 * pix->width; ++ pix->sizeimage = 3 * pix->width * pix->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ int ret = qcam_try_fmt_vid_cap(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ switch (pix->height) { ++ case 60: ++ qcam->mode = QC_DECIMATION_4; ++ break; ++ case 120: ++ qcam->mode = QC_DECIMATION_2; ++ break; ++ default: ++ qcam->mode = QC_DECIMATION_1; ++ break; ++ } ++ ++ mutex_lock(&qcam->lock); ++ qcam->mode |= QC_MILLIONS; ++ qcam->height = pix->height; ++ qcam->width = pix->width; ++ parport_claim_or_block(qcam->pdev); ++ qc_setup(qcam); ++ parport_release(qcam->pdev); ++ mutex_unlock(&qcam->lock); ++ return 0; ++} ++ ++static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static struct v4l2_fmtdesc formats[] = { ++ { 0, 0, 0, ++ "RGB 8:8:8", V4L2_PIX_FMT_RGB24, ++ { 0, 0, 0, 0 } ++ }, ++ }; ++ enum v4l2_buf_type type = fmt->type; ++ ++ if (fmt->index > 0) ++ return -EINVAL; ++ ++ *fmt = formats[fmt->index]; ++ fmt->type = type; ++ return 0; ++} ++ ++static ssize_t qcam_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct qcam *qcam = video_drvdata(file); ++ int len; ++ ++ mutex_lock(&qcam->lock); ++ parport_claim_or_block(qcam->pdev); ++ /* Probably should have a semaphore against multiple users */ ++ len = qc_capture(qcam, buf, count); ++ parport_release(qcam->pdev); ++ mutex_unlock(&qcam->lock); ++ return len; ++} ++ ++static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct qcam *qcam = ++ container_of(ctrl->handler, struct qcam, hdl); ++ int ret = 0; ++ ++ mutex_lock(&qcam->lock); ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ qcam->brightness = ctrl->val; ++ break; ++ case V4L2_CID_CONTRAST: ++ qcam->contrast = ctrl->val; ++ break; ++ case V4L2_CID_GAMMA: ++ qcam->whitebal = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ if (ret == 0) { ++ parport_claim_or_block(qcam->pdev); ++ qc_setup(qcam); ++ parport_release(qcam->pdev); ++ } ++ mutex_unlock(&qcam->lock); ++ return ret; ++} ++ ++static const struct v4l2_file_operations qcam_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = v4l2_fh_release, ++ .poll = v4l2_ctrl_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .read = qcam_read, ++}; ++ ++static const struct v4l2_ioctl_ops qcam_ioctl_ops = { ++ .vidioc_querycap = qcam_querycap, ++ .vidioc_g_input = qcam_g_input, ++ .vidioc_s_input = qcam_s_input, ++ .vidioc_enum_input = qcam_enum_input, ++ .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static const struct v4l2_ctrl_ops qcam_ctrl_ops = { ++ .s_ctrl = qcam_s_ctrl, ++}; ++ ++/* Initialize the QuickCam driver control structure. */ ++ ++static struct qcam *qcam_init(struct parport *port) ++{ ++ struct qcam *qcam; ++ struct v4l2_device *v4l2_dev; ++ ++ qcam = kzalloc(sizeof(*qcam), GFP_KERNEL); ++ if (qcam == NULL) ++ return NULL; ++ ++ v4l2_dev = &qcam->v4l2_dev; ++ strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name)); ++ ++ if (v4l2_device_register(NULL, v4l2_dev) < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ kfree(qcam); ++ return NULL; ++ } ++ ++ v4l2_ctrl_handler_init(&qcam->hdl, 3); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 240); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 192); ++ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, ++ V4L2_CID_GAMMA, 0, 255, 1, 128); ++ if (qcam->hdl.error) { ++ v4l2_err(v4l2_dev, "couldn't register controls\n"); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ kfree(qcam); ++ return NULL; ++ } ++ ++ qcam->pport = port; ++ qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL, ++ NULL, 0, NULL); ++ ++ qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; ++ ++ if (qcam->pdev == NULL) { ++ v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ kfree(qcam); ++ return NULL; ++ } ++ ++ strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name)); ++ qcam->vdev.v4l2_dev = v4l2_dev; ++ qcam->vdev.fops = &qcam_fops; ++ qcam->vdev.ioctl_ops = &qcam_ioctl_ops; ++ qcam->vdev.release = video_device_release_empty; ++ qcam->vdev.ctrl_handler = &qcam->hdl; ++ set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); ++ video_set_drvdata(&qcam->vdev, qcam); ++ ++ mutex_init(&qcam->lock); ++ qcam->width = qcam->ccd_width = 320; ++ qcam->height = qcam->ccd_height = 240; ++ qcam->mode = QC_MILLIONS | QC_DECIMATION_1; ++ qcam->contrast = 192; ++ qcam->brightness = 240; ++ qcam->whitebal = 128; ++ qcam->top = 1; ++ qcam->left = 14; ++ return qcam; ++} ++ ++static int init_cqcam(struct parport *port) ++{ ++ struct qcam *qcam; ++ struct v4l2_device *v4l2_dev; ++ ++ if (parport[0] != -1) { ++ /* The user gave specific instructions */ ++ int i, found = 0; ++ ++ for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) { ++ if (parport[0] == port->number) ++ found = 1; ++ } ++ if (!found) ++ return -ENODEV; ++ } ++ ++ if (num_cams == MAX_CAMS) ++ return -ENOSPC; ++ ++ qcam = qcam_init(port); ++ if (qcam == NULL) ++ return -ENODEV; ++ ++ v4l2_dev = &qcam->v4l2_dev; ++ ++ parport_claim_or_block(qcam->pdev); ++ ++ qc_reset(qcam); ++ ++ if (probe && qc_detect(qcam) == 0) { ++ parport_release(qcam->pdev); ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++ return -ENODEV; ++ } ++ ++ qc_setup(qcam); ++ ++ parport_release(qcam->pdev); ++ ++ if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ++ v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n", ++ qcam->pport->name); ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++ return -ENODEV; ++ } ++ ++ v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n", ++ video_device_node_name(&qcam->vdev), qcam->pport->name); ++ ++ qcams[num_cams++] = qcam; ++ ++ return 0; ++} ++ ++static void close_cqcam(struct qcam *qcam) ++{ ++ video_unregister_device(&qcam->vdev); ++ v4l2_ctrl_handler_free(&qcam->hdl); ++ parport_unregister_device(qcam->pdev); ++ kfree(qcam); ++} ++ ++static void cq_attach(struct parport *port) ++{ ++ init_cqcam(port); ++} ++ ++static void cq_detach(struct parport *port) ++{ ++ /* Write this some day. */ ++} ++ ++static struct parport_driver cqcam_driver = { ++ .name = "cqcam", ++ .attach = cq_attach, ++ .detach = cq_detach, ++}; ++ ++static int __init cqcam_init(void) ++{ ++ printk(KERN_INFO BANNER "\n"); ++ ++ return parport_register_driver(&cqcam_driver); ++} ++ ++static void __exit cqcam_cleanup(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < num_cams; i++) ++ close_cqcam(qcams[i]); ++ ++ parport_unregister_driver(&cqcam_driver); ++} ++ ++MODULE_AUTHOR("Philip Blundell "); ++MODULE_DESCRIPTION(BANNER); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.0.4"); ++ ++module_init(cqcam_init); ++module_exit(cqcam_cleanup); +diff --git a/drivers/media/parport/pms.c b/drivers/media/parport/pms.c +new file mode 100644 +index 0000000..77f9c92 +--- /dev/null ++++ b/drivers/media/parport/pms.c +@@ -0,0 +1,1152 @@ ++/* ++ * Media Vision Pro Movie Studio ++ * or ++ * "all you need is an I2C bus some RAM and a prayer" ++ * ++ * This draws heavily on code ++ * ++ * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994 ++ * Kiefernring 15 ++ * 14478 Potsdam, Germany ++ * ++ * Most of this code is directly derived from his userspace driver. ++ * His driver works so send any reports to alan@lxorguk.ukuu.org.uk ++ * unless the userspace driver also doesn't work for you... ++ * ++ * Changes: ++ * 25-11-2009 Hans Verkuil ++ * - converted to version 2 of the V4L API. ++ * 08/07/2003 Daniele Bellucci ++ * - pms_capture: report back -EFAULT ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.0.5"); ++ ++#define MOTOROLA 1 ++#define PHILIPS2 2 /* SAA7191 */ ++#define PHILIPS1 3 ++#define MVVMEMORYWIDTH 0x40 /* 512 bytes */ ++ ++struct i2c_info { ++ u8 slave; ++ u8 sub; ++ u8 data; ++ u8 hits; ++}; ++ ++struct pms { ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct v4l2_ctrl_handler hdl; ++ int height; ++ int width; ++ int depth; ++ int input; ++ struct mutex lock; ++ int i2c_count; ++ struct i2c_info i2cinfo[64]; ++ ++ int decoder; ++ int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ ++ v4l2_std_id std; ++ int io; ++ int data; ++ void __iomem *mem; ++}; ++ ++/* ++ * I/O ports and Shared Memory ++ */ ++ ++static int io_port = 0x250; ++module_param(io_port, int, 0); ++ ++static int mem_base = 0xc8000; ++module_param(mem_base, int, 0); ++ ++static int video_nr = -1; ++module_param(video_nr, int, 0); ++ ++ ++static inline void mvv_write(struct pms *dev, u8 index, u8 value) ++{ ++ outw(index | (value << 8), dev->io); ++} ++ ++static inline u8 mvv_read(struct pms *dev, u8 index) ++{ ++ outb(index, dev->io); ++ return inb(dev->data); ++} ++ ++static int pms_i2c_stat(struct pms *dev, u8 slave) ++{ ++ int counter = 0; ++ int i; ++ ++ outb(0x28, dev->io); ++ ++ while ((inb(dev->data) & 0x01) == 0) ++ if (counter++ == 256) ++ break; ++ ++ while ((inb(dev->data) & 0x01) != 0) ++ if (counter++ == 256) ++ break; ++ ++ outb(slave, dev->io); ++ ++ counter = 0; ++ while ((inb(dev->data) & 0x01) == 0) ++ if (counter++ == 256) ++ break; ++ ++ while ((inb(dev->data) & 0x01) != 0) ++ if (counter++ == 256) ++ break; ++ ++ for (i = 0; i < 12; i++) { ++ char st = inb(dev->data); ++ ++ if ((st & 2) != 0) ++ return -1; ++ if ((st & 1) == 0) ++ break; ++ } ++ outb(0x29, dev->io); ++ return inb(dev->data); ++} ++ ++static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data) ++{ ++ int skip = 0; ++ int count; ++ int i; ++ ++ for (i = 0; i < dev->i2c_count; i++) { ++ if ((dev->i2cinfo[i].slave == slave) && ++ (dev->i2cinfo[i].sub == sub)) { ++ if (dev->i2cinfo[i].data == data) ++ skip = 1; ++ dev->i2cinfo[i].data = data; ++ i = dev->i2c_count + 1; ++ } ++ } ++ ++ if (i == dev->i2c_count && dev->i2c_count < 64) { ++ dev->i2cinfo[dev->i2c_count].slave = slave; ++ dev->i2cinfo[dev->i2c_count].sub = sub; ++ dev->i2cinfo[dev->i2c_count].data = data; ++ dev->i2c_count++; ++ } ++ ++ if (skip) ++ return 0; ++ ++ mvv_write(dev, 0x29, sub); ++ mvv_write(dev, 0x2A, data); ++ mvv_write(dev, 0x28, slave); ++ ++ outb(0x28, dev->io); ++ ++ count = 0; ++ while ((inb(dev->data) & 1) == 0) ++ if (count > 255) ++ break; ++ while ((inb(dev->data) & 1) != 0) ++ if (count > 255) ++ break; ++ ++ count = inb(dev->data); ++ ++ if (count & 2) ++ return -1; ++ return count; ++} ++ ++static int pms_i2c_read(struct pms *dev, int slave, int sub) ++{ ++ int i; ++ ++ for (i = 0; i < dev->i2c_count; i++) { ++ if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub) ++ return dev->i2cinfo[i].data; ++ } ++ return 0; ++} ++ ++ ++static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or) ++{ ++ u8 tmp; ++ ++ tmp = pms_i2c_read(dev, slave, sub); ++ tmp = (tmp & and) | or; ++ pms_i2c_write(dev, slave, sub, tmp); ++} ++ ++/* ++ * Control functions ++ */ ++ ++ ++static void pms_videosource(struct pms *dev, short source) ++{ ++ switch (dev->decoder) { ++ case MOTOROLA: ++ break; ++ case PHILIPS2: ++ pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0); ++ break; ++ case PHILIPS1: ++ break; ++ } ++ mvv_write(dev, 0x2E, 0x31); ++ /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30); ++ But could not make this work correctly. Only Composite input ++ worked for me. */ ++} ++ ++static void pms_hue(struct pms *dev, short hue) ++{ ++ switch (dev->decoder) { ++ case MOTOROLA: ++ pms_i2c_write(dev, 0x8a, 0x00, hue); ++ break; ++ case PHILIPS2: ++ pms_i2c_write(dev, 0x8a, 0x07, hue); ++ break; ++ case PHILIPS1: ++ pms_i2c_write(dev, 0x42, 0x07, hue); ++ break; ++ } ++} ++ ++static void pms_saturation(struct pms *dev, short sat) ++{ ++ switch (dev->decoder) { ++ case MOTOROLA: ++ pms_i2c_write(dev, 0x8a, 0x00, sat); ++ break; ++ case PHILIPS1: ++ pms_i2c_write(dev, 0x42, 0x12, sat); ++ break; ++ } ++} ++ ++ ++static void pms_contrast(struct pms *dev, short contrast) ++{ ++ switch (dev->decoder) { ++ case MOTOROLA: ++ pms_i2c_write(dev, 0x8a, 0x00, contrast); ++ break; ++ case PHILIPS1: ++ pms_i2c_write(dev, 0x42, 0x13, contrast); ++ break; ++ } ++} ++ ++static void pms_brightness(struct pms *dev, short brightness) ++{ ++ switch (dev->decoder) { ++ case MOTOROLA: ++ pms_i2c_write(dev, 0x8a, 0x00, brightness); ++ pms_i2c_write(dev, 0x8a, 0x00, brightness); ++ pms_i2c_write(dev, 0x8a, 0x00, brightness); ++ break; ++ case PHILIPS1: ++ pms_i2c_write(dev, 0x42, 0x19, brightness); ++ break; ++ } ++} ++ ++ ++static void pms_format(struct pms *dev, short format) ++{ ++ int target; ++ ++ dev->standard = format; ++ ++ if (dev->decoder == PHILIPS1) ++ target = 0x42; ++ else if (dev->decoder == PHILIPS2) ++ target = 0x8a; ++ else ++ return; ++ ++ switch (format) { ++ case 0: /* Auto */ ++ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); ++ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80); ++ break; ++ case 1: /* NTSC */ ++ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); ++ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40); ++ break; ++ case 2: /* PAL */ ++ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); ++ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); ++ break; ++ case 3: /* SECAM */ ++ pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01); ++ pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); ++ break; ++ } ++} ++ ++#ifdef FOR_FUTURE_EXPANSION ++ ++/* ++ * These features of the PMS card are not currently exposes. They ++ * could become a private v4l ioctl for PMSCONFIG or somesuch if ++ * people need it. We also don't yet use the PMS interrupt. ++ */ ++ ++static void pms_hstart(struct pms *dev, short start) ++{ ++ switch (dev->decoder) { ++ case PHILIPS1: ++ pms_i2c_write(dev, 0x8a, 0x05, start); ++ pms_i2c_write(dev, 0x8a, 0x18, start); ++ break; ++ case PHILIPS2: ++ pms_i2c_write(dev, 0x42, 0x05, start); ++ pms_i2c_write(dev, 0x42, 0x18, start); ++ break; ++ } ++} ++ ++/* ++ * Bandpass filters ++ */ ++ ++static void pms_bandpass(struct pms *dev, short pass) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4); ++} ++ ++static void pms_antisnow(struct pms *dev, short snow) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2); ++} ++ ++static void pms_sharpness(struct pms *dev, short sharp) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03); ++} ++ ++static void pms_chromaagc(struct pms *dev, short agc) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5); ++} ++ ++static void pms_vertnoise(struct pms *dev, short noise) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3); ++} ++ ++static void pms_forcecolour(struct pms *dev, short colour) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7); ++} ++ ++static void pms_antigamma(struct pms *dev, short gamma) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7); ++} ++ ++static void pms_prefilter(struct pms *dev, short filter) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6); ++} ++ ++static void pms_hfilter(struct pms *dev, short filter) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5); ++} ++ ++static void pms_vfilter(struct pms *dev, short filter) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5); ++} ++ ++static void pms_killcolour(struct pms *dev, short colour) ++{ ++ if (dev->decoder == PHILIPS2) { ++ pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3); ++ pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3); ++ } else if (dev->decoder == PHILIPS1) { ++ pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3); ++ pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3); ++ } ++} ++ ++static void pms_chromagain(struct pms *dev, short chroma) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_write(dev, 0x8a, 0x11, chroma); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_write(dev, 0x42, 0x11, chroma); ++} ++ ++ ++static void pms_spacialcompl(struct pms *dev, short data) ++{ ++ mvv_write(dev, 0x3b, data); ++} ++ ++static void pms_spacialcomph(struct pms *dev, short data) ++{ ++ mvv_write(dev, 0x3a, data); ++} ++ ++static void pms_vstart(struct pms *dev, short start) ++{ ++ mvv_write(dev, 0x16, start); ++ mvv_write(dev, 0x17, (start >> 8) & 0x01); ++} ++ ++#endif ++ ++static void pms_secamcross(struct pms *dev, short cross) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5); ++} ++ ++ ++static void pms_swsense(struct pms *dev, short sense) ++{ ++ if (dev->decoder == PHILIPS2) { ++ pms_i2c_write(dev, 0x8a, 0x0a, sense); ++ pms_i2c_write(dev, 0x8a, 0x0b, sense); ++ } else if (dev->decoder == PHILIPS1) { ++ pms_i2c_write(dev, 0x42, 0x0a, sense); ++ pms_i2c_write(dev, 0x42, 0x0b, sense); ++ } ++} ++ ++ ++static void pms_framerate(struct pms *dev, short frr) ++{ ++ int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25; ++ ++ if (frr == 0) ++ return; ++ fps = fps/frr; ++ mvv_write(dev, 0x14, 0x80 | fps); ++ mvv_write(dev, 0x15, 1); ++} ++ ++static void pms_vert(struct pms *dev, u8 deciden, u8 decinum) ++{ ++ mvv_write(dev, 0x1c, deciden); /* Denominator */ ++ mvv_write(dev, 0x1d, decinum); /* Numerator */ ++} ++ ++/* ++ * Turn 16bit ratios into best small ratio the chipset can grok ++ */ ++ ++static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden) ++{ ++ /* Knock it down by / 5 once */ ++ if (decinum % 5 == 0) { ++ deciden /= 5; ++ decinum /= 5; ++ } ++ /* ++ * 3's ++ */ ++ while (decinum % 3 == 0 && deciden % 3 == 0) { ++ deciden /= 3; ++ decinum /= 3; ++ } ++ /* ++ * 2's ++ */ ++ while (decinum % 2 == 0 && deciden % 2 == 0) { ++ decinum /= 2; ++ deciden /= 2; ++ } ++ /* ++ * Fudgyify ++ */ ++ while (deciden > 32) { ++ deciden /= 2; ++ decinum = (decinum + 1) / 2; ++ } ++ if (deciden == 32) ++ deciden--; ++ pms_vert(dev, deciden, decinum); ++} ++ ++static void pms_horzdeci(struct pms *dev, short decinum, short deciden) ++{ ++ if (decinum <= 512) { ++ if (decinum % 5 == 0) { ++ decinum /= 5; ++ deciden /= 5; ++ } ++ } else { ++ decinum = 512; ++ deciden = 640; /* 768 would be ideal */ ++ } ++ ++ while (((decinum | deciden) & 1) == 0) { ++ decinum >>= 1; ++ deciden >>= 1; ++ } ++ while (deciden > 32) { ++ deciden >>= 1; ++ decinum = (decinum + 1) >> 1; ++ } ++ if (deciden == 32) ++ deciden--; ++ ++ mvv_write(dev, 0x24, 0x80 | deciden); ++ mvv_write(dev, 0x25, decinum); ++} ++ ++static void pms_resolution(struct pms *dev, short width, short height) ++{ ++ int fg_height; ++ ++ fg_height = height; ++ if (fg_height > 280) ++ fg_height = 280; ++ ++ mvv_write(dev, 0x18, fg_height); ++ mvv_write(dev, 0x19, fg_height >> 8); ++ ++ if (dev->std & V4L2_STD_525_60) { ++ mvv_write(dev, 0x1a, 0xfc); ++ mvv_write(dev, 0x1b, 0x00); ++ if (height > fg_height) ++ pms_vertdeci(dev, 240, 240); ++ else ++ pms_vertdeci(dev, fg_height, 240); ++ } else { ++ mvv_write(dev, 0x1a, 0x1a); ++ mvv_write(dev, 0x1b, 0x01); ++ if (fg_height > 256) ++ pms_vertdeci(dev, 270, 270); ++ else ++ pms_vertdeci(dev, fg_height, 270); ++ } ++ mvv_write(dev, 0x12, 0); ++ mvv_write(dev, 0x13, MVVMEMORYWIDTH); ++ mvv_write(dev, 0x42, 0x00); ++ mvv_write(dev, 0x43, 0x00); ++ mvv_write(dev, 0x44, MVVMEMORYWIDTH); ++ ++ mvv_write(dev, 0x22, width + 8); ++ mvv_write(dev, 0x23, (width + 8) >> 8); ++ ++ if (dev->std & V4L2_STD_525_60) ++ pms_horzdeci(dev, width, 640); ++ else ++ pms_horzdeci(dev, width + 8, 768); ++ ++ mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe); ++ mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01); ++ mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd); ++ mvv_write(dev, 0x32, 0x00); ++ mvv_write(dev, 0x33, MVVMEMORYWIDTH); ++} ++ ++ ++/* ++ * Set Input ++ */ ++ ++static void pms_vcrinput(struct pms *dev, short input) ++{ ++ if (dev->decoder == PHILIPS2) ++ pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7); ++ else if (dev->decoder == PHILIPS1) ++ pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7); ++} ++ ++ ++static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) ++{ ++ int y; ++ int dw = 2 * dev->width; ++ char tmp[dw + 32]; /* using a temp buffer is faster than direct */ ++ int cnt = 0; ++ int len = 0; ++ unsigned char r8 = 0x5; /* value for reg8 */ ++ ++ if (rgb555) ++ r8 |= 0x20; /* else use untranslated rgb = 565 */ ++ mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ ++ ++/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ ++ ++ for (y = 0; y < dev->height; y++) { ++ writeb(0, dev->mem); /* synchronisiert neue Zeile */ ++ ++ /* ++ * This is in truth a fifo, be very careful as if you ++ * forgot this odd things will occur 8) ++ */ ++ ++ memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */ ++ cnt -= dev->height; ++ while (cnt <= 0) { ++ /* ++ * Don't copy too far ++ */ ++ int dt = dw; ++ if (dt + len > count) ++ dt = count - len; ++ cnt += dev->height; ++ if (copy_to_user(buf, tmp + 32, dt)) ++ return len ? len : -EFAULT; ++ buf += dt; ++ len += dt; ++ } ++ } ++ return len; ++} ++ ++ ++/* ++ * Video4linux interfacing ++ */ ++ ++static int pms_querycap(struct file *file, void *priv, ++ struct v4l2_capability *vcap) ++{ ++ struct pms *dev = video_drvdata(file); ++ ++ strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); ++ strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); ++ snprintf(vcap->bus_info, sizeof(vcap->bus_info), ++ "ISA:%s", dev->v4l2_dev.name); ++ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; ++ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ static const char *inputs[4] = { ++ "Composite", ++ "S-Video", ++ "Composite (VCR)", ++ "S-Video (VCR)" ++ }; ++ ++ if (vin->index > 3) ++ return -EINVAL; ++ strlcpy(vin->name, inputs[vin->index], sizeof(vin->name)); ++ vin->type = V4L2_INPUT_TYPE_CAMERA; ++ vin->audioset = 0; ++ vin->tuner = 0; ++ vin->std = V4L2_STD_ALL; ++ vin->status = 0; ++ return 0; ++} ++ ++static int pms_g_input(struct file *file, void *fh, unsigned int *inp) ++{ ++ struct pms *dev = video_drvdata(file); ++ ++ *inp = dev->input; ++ return 0; ++} ++ ++static int pms_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ struct pms *dev = video_drvdata(file); ++ ++ if (inp > 3) ++ return -EINVAL; ++ ++ dev->input = inp; ++ pms_videosource(dev, inp & 1); ++ pms_vcrinput(dev, inp >> 1); ++ return 0; ++} ++ ++static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct pms *dev = video_drvdata(file); ++ ++ *std = dev->std; ++ return 0; ++} ++ ++static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct pms *dev = video_drvdata(file); ++ int ret = 0; ++ ++ dev->std = *std; ++ if (dev->std & V4L2_STD_NTSC) { ++ pms_framerate(dev, 30); ++ pms_secamcross(dev, 0); ++ pms_format(dev, 1); ++ } else if (dev->std & V4L2_STD_PAL) { ++ pms_framerate(dev, 25); ++ pms_secamcross(dev, 0); ++ pms_format(dev, 2); ++ } else if (dev->std & V4L2_STD_SECAM) { ++ pms_framerate(dev, 25); ++ pms_secamcross(dev, 1); ++ pms_format(dev, 2); ++ } else { ++ ret = -EINVAL; ++ } ++ /* ++ switch (v->mode) { ++ case VIDEO_MODE_AUTO: ++ pms_framerate(dev, 25); ++ pms_secamcross(dev, 0); ++ pms_format(dev, 0); ++ break; ++ }*/ ++ return ret; ++} ++ ++static int pms_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct pms *dev = container_of(ctrl->handler, struct pms, hdl); ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ pms_brightness(dev, ctrl->val); ++ break; ++ case V4L2_CID_CONTRAST: ++ pms_contrast(dev, ctrl->val); ++ break; ++ case V4L2_CID_SATURATION: ++ pms_saturation(dev, ctrl->val); ++ break; ++ case V4L2_CID_HUE: ++ pms_hue(dev, ctrl->val); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct pms *dev = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ pix->width = dev->width; ++ pix->height = dev->height; ++ pix->pixelformat = dev->width == 15 ? ++ V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 2 * dev->width; ++ pix->sizeimage = 2 * dev->width * dev->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ if (pix->height < 16 || pix->height > 480) ++ return -EINVAL; ++ if (pix->width < 16 || pix->width > 640) ++ return -EINVAL; ++ if (pix->pixelformat != V4L2_PIX_FMT_RGB555 && ++ pix->pixelformat != V4L2_PIX_FMT_RGB565) ++ return -EINVAL; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 2 * pix->width; ++ pix->sizeimage = 2 * pix->width * pix->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SRGB; ++ return 0; ++} ++ ++static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct pms *dev = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ int ret = pms_try_fmt_vid_cap(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ dev->width = pix->width; ++ dev->height = pix->height; ++ dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; ++ pms_resolution(dev, dev->width, dev->height); ++ /* Ok we figured out what to use from our wide choice */ ++ return 0; ++} ++ ++static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static struct v4l2_fmtdesc formats[] = { ++ { 0, 0, 0, ++ "RGB 5:5:5", V4L2_PIX_FMT_RGB555, ++ { 0, 0, 0, 0 } ++ }, ++ { 1, 0, 0, ++ "RGB 5:6:5", V4L2_PIX_FMT_RGB565, ++ { 0, 0, 0, 0 } ++ }, ++ }; ++ enum v4l2_buf_type type = fmt->type; ++ ++ if (fmt->index > 1) ++ return -EINVAL; ++ ++ *fmt = formats[fmt->index]; ++ fmt->type = type; ++ return 0; ++} ++ ++static ssize_t pms_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct pms *dev = video_drvdata(file); ++ int len; ++ ++ len = pms_capture(dev, buf, (dev->depth == 15), count); ++ return len; ++} ++ ++static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct v4l2_fh *fh = file->private_data; ++ unsigned int res = POLLIN | POLLRDNORM; ++ ++ if (v4l2_event_pending(fh)) ++ res |= POLLPRI; ++ poll_wait(file, &fh->wait, wait); ++ return res; ++} ++ ++static const struct v4l2_file_operations pms_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = v4l2_fh_release, ++ .poll = pms_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .read = pms_read, ++}; ++ ++static const struct v4l2_ioctl_ops pms_ioctl_ops = { ++ .vidioc_querycap = pms_querycap, ++ .vidioc_g_input = pms_g_input, ++ .vidioc_s_input = pms_s_input, ++ .vidioc_enum_input = pms_enum_input, ++ .vidioc_g_std = pms_g_std, ++ .vidioc_s_std = pms_s_std, ++ .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ++ * Probe for and initialise the Mediavision PMS ++ */ ++ ++static int init_mediavision(struct pms *dev) ++{ ++ int idec, decst; ++ int i; ++ static const unsigned char i2c_defs[] = { ++ 0x4c, 0x30, 0x00, 0xe8, ++ 0xb6, 0xe2, 0x00, 0x00, ++ 0xff, 0xff, 0x00, 0x00, ++ 0x00, 0x00, 0x78, 0x98, ++ 0x00, 0x00, 0x00, 0x00, ++ 0x34, 0x0a, 0xf4, 0xce, ++ 0xe4 ++ }; ++ ++ dev->mem = ioremap(mem_base, 0x800); ++ if (!dev->mem) ++ return -ENOMEM; ++ ++ if (!request_region(0x9a01, 1, "Mediavision PMS config")) { ++ printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n"); ++ iounmap(dev->mem); ++ return -EBUSY; ++ } ++ if (!request_region(dev->io, 3, "Mediavision PMS")) { ++ printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io); ++ release_region(0x9a01, 1); ++ iounmap(dev->mem); ++ return -EBUSY; ++ } ++ outb(0xb8, 0x9a01); /* Unlock */ ++ outb(dev->io >> 4, 0x9a01); /* Set IO port */ ++ ++ ++ decst = pms_i2c_stat(dev, 0x43); ++ ++ if (decst != -1) ++ idec = 2; ++ else if (pms_i2c_stat(dev, 0xb9) != -1) ++ idec = 3; ++ else if (pms_i2c_stat(dev, 0x8b) != -1) ++ idec = 1; ++ else ++ idec = 0; ++ ++ printk(KERN_INFO "PMS type is %d\n", idec); ++ if (idec == 0) { ++ release_region(dev->io, 3); ++ release_region(0x9a01, 1); ++ iounmap(dev->mem); ++ return -ENODEV; ++ } ++ ++ /* ++ * Ok we have a PMS of some sort ++ */ ++ ++ mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */ ++ ++ /* Ok now load the defaults */ ++ ++ for (i = 0; i < 0x19; i++) { ++ if (i2c_defs[i] == 0xff) ++ pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00); ++ else ++ pms_i2c_write(dev, 0x8a, i, i2c_defs[i]); ++ } ++ ++ pms_i2c_write(dev, 0xb8, 0x00, 0x12); ++ pms_i2c_write(dev, 0xb8, 0x04, 0x00); ++ pms_i2c_write(dev, 0xb8, 0x07, 0x00); ++ pms_i2c_write(dev, 0xb8, 0x08, 0x00); ++ pms_i2c_write(dev, 0xb8, 0x09, 0xff); ++ pms_i2c_write(dev, 0xb8, 0x0a, 0x00); ++ pms_i2c_write(dev, 0xb8, 0x0b, 0x10); ++ pms_i2c_write(dev, 0xb8, 0x10, 0x03); ++ ++ mvv_write(dev, 0x01, 0x00); ++ mvv_write(dev, 0x05, 0xa0); ++ mvv_write(dev, 0x08, 0x25); ++ mvv_write(dev, 0x09, 0x00); ++ mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH); ++ ++ mvv_write(dev, 0x10, 0x02); ++ mvv_write(dev, 0x1e, 0x0c); ++ mvv_write(dev, 0x1f, 0x03); ++ mvv_write(dev, 0x26, 0x06); ++ ++ mvv_write(dev, 0x2b, 0x00); ++ mvv_write(dev, 0x2c, 0x20); ++ mvv_write(dev, 0x2d, 0x00); ++ mvv_write(dev, 0x2f, 0x70); ++ mvv_write(dev, 0x32, 0x00); ++ mvv_write(dev, 0x33, MVVMEMORYWIDTH); ++ mvv_write(dev, 0x34, 0x00); ++ mvv_write(dev, 0x35, 0x00); ++ mvv_write(dev, 0x3a, 0x80); ++ mvv_write(dev, 0x3b, 0x10); ++ mvv_write(dev, 0x20, 0x00); ++ mvv_write(dev, 0x21, 0x00); ++ mvv_write(dev, 0x30, 0x22); ++ return 0; ++} ++ ++/* ++ * Initialization and module stuff ++ */ ++ ++#ifndef MODULE ++static int enable; ++module_param(enable, int, 0); ++#endif ++ ++static const struct v4l2_ctrl_ops pms_ctrl_ops = { ++ .s_ctrl = pms_s_ctrl, ++}; ++ ++static int pms_probe(struct device *pdev, unsigned int card) ++{ ++ struct pms *dev; ++ struct v4l2_device *v4l2_dev; ++ struct v4l2_ctrl_handler *hdl; ++ int res; ++ ++#ifndef MODULE ++ if (!enable) { ++ pr_err("PMS: not enabled, use pms.enable=1 to probe\n"); ++ return -ENODEV; ++ } ++#endif ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (dev == NULL) ++ return -ENOMEM; ++ ++ dev->decoder = PHILIPS2; ++ dev->io = io_port; ++ dev->data = io_port + 1; ++ v4l2_dev = &dev->v4l2_dev; ++ hdl = &dev->hdl; ++ ++ res = v4l2_device_register(pdev, v4l2_dev); ++ if (res < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ goto free_dev; ++ } ++ v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n"); ++ ++ res = init_mediavision(dev); ++ if (res) { ++ v4l2_err(v4l2_dev, "Board not found.\n"); ++ goto free_io; ++ } ++ ++ v4l2_ctrl_handler_init(hdl, 4); ++ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 139); ++ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 255, 1, 70); ++ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 255, 1, 64); ++ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, ++ V4L2_CID_HUE, 0, 255, 1, 0); ++ if (hdl->error) { ++ res = hdl->error; ++ goto free_hdl; ++ } ++ ++ mutex_init(&dev->lock); ++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); ++ dev->vdev.v4l2_dev = v4l2_dev; ++ dev->vdev.ctrl_handler = hdl; ++ dev->vdev.fops = &pms_fops; ++ dev->vdev.ioctl_ops = &pms_ioctl_ops; ++ dev->vdev.release = video_device_release_empty; ++ dev->vdev.lock = &dev->lock; ++ dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; ++ set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); ++ video_set_drvdata(&dev->vdev, dev); ++ dev->std = V4L2_STD_NTSC_M; ++ dev->height = 240; ++ dev->width = 320; ++ dev->depth = 16; ++ pms_swsense(dev, 75); ++ pms_resolution(dev, 320, 240); ++ pms_videosource(dev, 0); ++ pms_vcrinput(dev, 0); ++ v4l2_ctrl_handler_setup(hdl); ++ res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); ++ if (res >= 0) ++ return 0; ++ ++free_hdl: ++ v4l2_ctrl_handler_free(hdl); ++ v4l2_device_unregister(&dev->v4l2_dev); ++free_io: ++ release_region(dev->io, 3); ++ release_region(0x9a01, 1); ++ iounmap(dev->mem); ++free_dev: ++ kfree(dev); ++ return res; ++} ++ ++static int pms_remove(struct device *pdev, unsigned int card) ++{ ++ struct pms *dev = dev_get_drvdata(pdev); ++ ++ video_unregister_device(&dev->vdev); ++ v4l2_ctrl_handler_free(&dev->hdl); ++ release_region(dev->io, 3); ++ release_region(0x9a01, 1); ++ iounmap(dev->mem); ++ return 0; ++} ++ ++static struct isa_driver pms_driver = { ++ .probe = pms_probe, ++ .remove = pms_remove, ++ .driver = { ++ .name = "pms", ++ }, ++}; ++ ++static int __init pms_init(void) ++{ ++ return isa_register_driver(&pms_driver, 1); ++} ++ ++static void __exit pms_exit(void) ++{ ++ isa_unregister_driver(&pms_driver); ++} ++ ++module_init(pms_init); ++module_exit(pms_exit); +diff --git a/drivers/media/parport/w9966.c b/drivers/media/parport/w9966.c +new file mode 100644 +index 0000000..db2a600 +--- /dev/null ++++ b/drivers/media/parport/w9966.c +@@ -0,0 +1,981 @@ ++/* ++ Winbond w9966cf Webcam parport driver. ++ ++ Version 0.33 ++ ++ Copyright (C) 2001 Jakob Kemi ++ ++ 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. ++*/ ++/* ++ Supported devices: ++ *Lifeview FlyCam Supra (using the Philips saa7111a chip) ++ ++ Does any other model using the w9966 interface chip exist ? ++ ++ Todo: ++ ++ *Add a working EPP mode, since DMA ECP read isn't implemented ++ in the parport drivers. (That's why it's so sloow) ++ ++ *Add support for other ccd-control chips than the saa7111 ++ please send me feedback on what kind of chips you have. ++ ++ *Add proper probing. I don't know what's wrong with the IEEE1284 ++ parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID) ++ and nibble read seems to be broken for some peripherals. ++ ++ *Add probing for onboard SRAM, port directions etc. (if possible) ++ ++ *Add support for the hardware compressed modes (maybe using v4l2) ++ ++ *Fix better support for the capture window (no skewed images, v4l ++ interface to capt. window) ++ ++ *Probably some bugs that I don't know of ++ ++ Please support me by sending feedback! ++ ++ Changes: ++ ++ Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE ++ and owner support for newer module locks ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/*#define DEBUG*/ /* Undef me for production */ ++ ++#ifdef DEBUG ++#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a) ++#else ++#define DPRINTF(x...) ++#endif ++ ++/* ++ * Defines, simple typedefs etc. ++ */ ++ ++#define W9966_DRIVERNAME "W9966CF Webcam" ++#define W9966_MAXCAMS 4 /* Maximum number of cameras */ ++#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */ ++#define W9966_SRAMSIZE 131072 /* 128kb */ ++#define W9966_SRAMID 0x02 /* check w9966cf.pdf */ ++ ++/* Empirically determined window limits */ ++#define W9966_WND_MIN_X 16 ++#define W9966_WND_MIN_Y 14 ++#define W9966_WND_MAX_X 705 ++#define W9966_WND_MAX_Y 253 ++#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) ++#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) ++ ++/* Keep track of our current state */ ++#define W9966_STATE_PDEV 0x01 ++#define W9966_STATE_CLAIMED 0x02 ++#define W9966_STATE_VDEV 0x04 ++ ++#define W9966_I2C_W_ID 0x48 ++#define W9966_I2C_R_ID 0x49 ++#define W9966_I2C_R_DATA 0x08 ++#define W9966_I2C_R_CLOCK 0x04 ++#define W9966_I2C_W_DATA 0x02 ++#define W9966_I2C_W_CLOCK 0x01 ++ ++struct w9966 { ++ struct v4l2_device v4l2_dev; ++ struct v4l2_ctrl_handler hdl; ++ unsigned char dev_state; ++ unsigned char i2c_state; ++ unsigned short ppmode; ++ struct parport *pport; ++ struct pardevice *pdev; ++ struct video_device vdev; ++ unsigned short width; ++ unsigned short height; ++ unsigned char brightness; ++ signed char contrast; ++ signed char color; ++ signed char hue; ++ struct mutex lock; ++}; ++ ++/* ++ * Module specific properties ++ */ ++ ++MODULE_AUTHOR("Jakob Kemi "); ++MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.33.1"); ++ ++#ifdef MODULE ++static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; ++#else ++static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; ++#endif ++module_param_array(pardev, charp, NULL, 0); ++MODULE_PARM_DESC(pardev, "pardev: where to search for\n" ++ "\teach camera. 'aggressive' means brute-force search.\n" ++ "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n" ++ "\tcam 1 to parport3 and search every parport for cam 2 etc..."); ++ ++static int parmode; ++module_param(parmode, int, 0); ++MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); ++ ++static int video_nr = -1; ++module_param(video_nr, int, 0); ++ ++static struct w9966 w9966_cams[W9966_MAXCAMS]; ++ ++/* ++ * Private function defines ++ */ ++ ++ ++/* Set camera phase flags, so we know what to uninit when terminating */ ++static inline void w9966_set_state(struct w9966 *cam, int mask, int val) ++{ ++ cam->dev_state = (cam->dev_state & ~mask) ^ val; ++} ++ ++/* Get camera phase flags */ ++static inline int w9966_get_state(struct w9966 *cam, int mask, int val) ++{ ++ return ((cam->dev_state & mask) == val); ++} ++ ++/* Claim parport for ourself */ ++static void w9966_pdev_claim(struct w9966 *cam) ++{ ++ if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) ++ return; ++ parport_claim_or_block(cam->pdev); ++ w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); ++} ++ ++/* Release parport for others to use */ ++static void w9966_pdev_release(struct w9966 *cam) ++{ ++ if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0)) ++ return; ++ parport_release(cam->pdev); ++ w9966_set_state(cam, W9966_STATE_CLAIMED, 0); ++} ++ ++/* Read register from W9966 interface-chip ++ Expects a claimed pdev ++ -1 on error, else register data (byte) */ ++static int w9966_read_reg(struct w9966 *cam, int reg) ++{ ++ /* ECP, read, regtransfer, REG, REG, REG, REG, REG */ ++ const unsigned char addr = 0x80 | (reg & 0x1f); ++ unsigned char val; ++ ++ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) ++ return -1; ++ if (parport_write(cam->pport, &addr, 1) != 1) ++ return -1; ++ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) ++ return -1; ++ if (parport_read(cam->pport, &val, 1) != 1) ++ return -1; ++ ++ return val; ++} ++ ++/* Write register to W9966 interface-chip ++ Expects a claimed pdev ++ -1 on error */ ++static int w9966_write_reg(struct w9966 *cam, int reg, int data) ++{ ++ /* ECP, write, regtransfer, REG, REG, REG, REG, REG */ ++ const unsigned char addr = 0xc0 | (reg & 0x1f); ++ const unsigned char val = data; ++ ++ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) ++ return -1; ++ if (parport_write(cam->pport, &addr, 1) != 1) ++ return -1; ++ if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) ++ return -1; ++ if (parport_write(cam->pport, &val, 1) != 1) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * Ugly and primitive i2c protocol functions ++ */ ++ ++/* Sets the data line on the i2c bus. ++ Expects a claimed pdev. */ ++static void w9966_i2c_setsda(struct w9966 *cam, int state) ++{ ++ if (state) ++ cam->i2c_state |= W9966_I2C_W_DATA; ++ else ++ cam->i2c_state &= ~W9966_I2C_W_DATA; ++ ++ w9966_write_reg(cam, 0x18, cam->i2c_state); ++ udelay(5); ++} ++ ++/* Get peripheral clock line ++ Expects a claimed pdev. */ ++static int w9966_i2c_getscl(struct w9966 *cam) ++{ ++ const unsigned char state = w9966_read_reg(cam, 0x18); ++ return ((state & W9966_I2C_R_CLOCK) > 0); ++} ++ ++/* Sets the clock line on the i2c bus. ++ Expects a claimed pdev. -1 on error */ ++static int w9966_i2c_setscl(struct w9966 *cam, int state) ++{ ++ unsigned long timeout; ++ ++ if (state) ++ cam->i2c_state |= W9966_I2C_W_CLOCK; ++ else ++ cam->i2c_state &= ~W9966_I2C_W_CLOCK; ++ ++ w9966_write_reg(cam, 0x18, cam->i2c_state); ++ udelay(5); ++ ++ /* we go to high, we also expect the peripheral to ack. */ ++ if (state) { ++ timeout = jiffies + 100; ++ while (!w9966_i2c_getscl(cam)) { ++ if (time_after(jiffies, timeout)) ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++#if 0 ++/* Get peripheral data line ++ Expects a claimed pdev. */ ++static int w9966_i2c_getsda(struct w9966 *cam) ++{ ++ const unsigned char state = w9966_read_reg(cam, 0x18); ++ return ((state & W9966_I2C_R_DATA) > 0); ++} ++#endif ++ ++/* Write a byte with ack to the i2c bus. ++ Expects a claimed pdev. -1 on error */ ++static int w9966_i2c_wbyte(struct w9966 *cam, int data) ++{ ++ int i; ++ ++ for (i = 7; i >= 0; i--) { ++ w9966_i2c_setsda(cam, (data >> i) & 0x01); ++ ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ w9966_i2c_setscl(cam, 0); ++ } ++ ++ w9966_i2c_setsda(cam, 1); ++ ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ w9966_i2c_setscl(cam, 0); ++ ++ return 0; ++} ++ ++/* Read a data byte with ack from the i2c-bus ++ Expects a claimed pdev. -1 on error */ ++#if 0 ++static int w9966_i2c_rbyte(struct w9966 *cam) ++{ ++ unsigned char data = 0x00; ++ int i; ++ ++ w9966_i2c_setsda(cam, 1); ++ ++ for (i = 0; i < 8; i++) { ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ data = data << 1; ++ if (w9966_i2c_getsda(cam)) ++ data |= 0x01; ++ ++ w9966_i2c_setscl(cam, 0); ++ } ++ return data; ++} ++#endif ++ ++/* Read a register from the i2c device. ++ Expects claimed pdev. -1 on error */ ++#if 0 ++static int w9966_read_reg_i2c(struct w9966 *cam, int reg) ++{ ++ int data; ++ ++ w9966_i2c_setsda(cam, 0); ++ w9966_i2c_setscl(cam, 0); ++ ++ if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || ++ w9966_i2c_wbyte(cam, reg) == -1) ++ return -1; ++ ++ w9966_i2c_setsda(cam, 1); ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ w9966_i2c_setsda(cam, 0); ++ w9966_i2c_setscl(cam, 0); ++ ++ if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1) ++ return -1; ++ data = w9966_i2c_rbyte(cam); ++ if (data == -1) ++ return -1; ++ ++ w9966_i2c_setsda(cam, 0); ++ ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ w9966_i2c_setsda(cam, 1); ++ ++ return data; ++} ++#endif ++ ++/* Write a register to the i2c device. ++ Expects claimed pdev. -1 on error */ ++static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data) ++{ ++ w9966_i2c_setsda(cam, 0); ++ w9966_i2c_setscl(cam, 0); ++ ++ if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || ++ w9966_i2c_wbyte(cam, reg) == -1 || ++ w9966_i2c_wbyte(cam, data) == -1) ++ return -1; ++ ++ w9966_i2c_setsda(cam, 0); ++ if (w9966_i2c_setscl(cam, 1) == -1) ++ return -1; ++ ++ w9966_i2c_setsda(cam, 1); ++ ++ return 0; ++} ++ ++/* Find a good length for capture window (used both for W and H) ++ A bit ugly but pretty functional. The capture length ++ have to match the downscale */ ++static int w9966_findlen(int near, int size, int maxlen) ++{ ++ int bestlen = size; ++ int besterr = abs(near - bestlen); ++ int len; ++ ++ for (len = size + 1; len < maxlen; len++) { ++ int err; ++ if (((64 * size) % len) != 0) ++ continue; ++ ++ err = abs(near - len); ++ ++ /* Only continue as long as we keep getting better values */ ++ if (err > besterr) ++ break; ++ ++ besterr = err; ++ bestlen = len; ++ } ++ ++ return bestlen; ++} ++ ++/* Modify capture window (if necessary) ++ and calculate downscaling ++ Return -1 on error */ ++static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor) ++{ ++ int maxlen = max - min; ++ int len = *end - *beg + 1; ++ int newlen = w9966_findlen(len, size, maxlen); ++ int err = newlen - len; ++ ++ /* Check for bad format */ ++ if (newlen > maxlen || newlen < size) ++ return -1; ++ ++ /* Set factor (6 bit fixed) */ ++ *factor = (64 * size) / newlen; ++ if (*factor == 64) ++ *factor = 0x00; /* downscale is disabled */ ++ else ++ *factor |= 0x80; /* set downscale-enable bit */ ++ ++ /* Modify old beginning and end */ ++ *beg -= err / 2; ++ *end += err - (err / 2); ++ ++ /* Move window if outside borders */ ++ if (*beg < min) { ++ *end += min - *beg; ++ *beg += min - *beg; ++ } ++ if (*end > max) { ++ *beg -= *end - max; ++ *end -= *end - max; ++ } ++ ++ return 0; ++} ++ ++/* Setup the cameras capture window etc. ++ Expects a claimed pdev ++ return -1 on error */ ++static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h) ++{ ++ unsigned int i; ++ unsigned int enh_s, enh_e; ++ unsigned char scale_x, scale_y; ++ unsigned char regs[0x1c]; ++ unsigned char saa7111_regs[] = { ++ 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, ++ 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, ++ 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 ++ }; ++ ++ ++ if (w * h * 2 > W9966_SRAMSIZE) { ++ DPRINTF("capture window exceeds SRAM size!.\n"); ++ w = 200; h = 160; /* Pick default values */ ++ } ++ ++ w &= ~0x1; ++ if (w < 2) ++ w = 2; ++ if (h < 1) ++ h = 1; ++ if (w > W9966_WND_MAX_W) ++ w = W9966_WND_MAX_W; ++ if (h > W9966_WND_MAX_H) ++ h = W9966_WND_MAX_H; ++ ++ cam->width = w; ++ cam->height = h; ++ ++ enh_s = 0; ++ enh_e = w * h * 2; ++ ++ /* Modify capture window if necessary and calculate downscaling */ ++ if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || ++ w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0) ++ return -1; ++ ++ DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", ++ w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80); ++ ++ /* Setup registers */ ++ regs[0x00] = 0x00; /* Set normal operation */ ++ regs[0x01] = 0x18; /* Capture mode */ ++ regs[0x02] = scale_y; /* V-scaling */ ++ regs[0x03] = scale_x; /* H-scaling */ ++ ++ /* Capture window */ ++ regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */ ++ regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */ ++ regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */ ++ regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */ ++ regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */ ++ regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */ ++ regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */ ++ ++ regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */ ++ ++ /* Enhancement layer */ ++ regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */ ++ regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */ ++ regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */ ++ regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */ ++ regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */ ++ regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */ ++ ++ /* Misc */ ++ regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */ ++ regs[0x17] = 0x00; /* ??? */ ++ regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */ ++ regs[0x19] = 0xff; /* I/O port direction control */ ++ regs[0x1a] = 0xff; /* I/O port data register */ ++ regs[0x1b] = 0x10; /* ??? */ ++ ++ /* SAA7111 chip settings */ ++ saa7111_regs[0x0a] = cam->brightness; ++ saa7111_regs[0x0b] = cam->contrast; ++ saa7111_regs[0x0c] = cam->color; ++ saa7111_regs[0x0d] = cam->hue; ++ ++ /* Reset (ECP-fifo & serial-bus) */ ++ if (w9966_write_reg(cam, 0x00, 0x03) == -1) ++ return -1; ++ ++ /* Write regs to w9966cf chip */ ++ for (i = 0; i < 0x1c; i++) ++ if (w9966_write_reg(cam, i, regs[i]) == -1) ++ return -1; ++ ++ /* Write regs to saa7111 chip */ ++ for (i = 0; i < 0x20; i++) ++ if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * Video4linux interfacing ++ */ ++ ++static int cam_querycap(struct file *file, void *priv, ++ struct v4l2_capability *vcap) ++{ ++ struct w9966 *cam = video_drvdata(file); ++ ++ strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); ++ strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); ++ strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); ++ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; ++ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ if (vin->index > 0) ++ return -EINVAL; ++ strlcpy(vin->name, "Camera", sizeof(vin->name)); ++ vin->type = V4L2_INPUT_TYPE_CAMERA; ++ vin->audioset = 0; ++ vin->tuner = 0; ++ vin->std = 0; ++ vin->status = 0; ++ return 0; ++} ++ ++static int cam_g_input(struct file *file, void *fh, unsigned int *inp) ++{ ++ *inp = 0; ++ return 0; ++} ++ ++static int cam_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ return (inp > 0) ? -EINVAL : 0; ++} ++ ++static int cam_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct w9966 *cam = ++ container_of(ctrl->handler, struct w9966, hdl); ++ int ret = 0; ++ ++ mutex_lock(&cam->lock); ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ cam->brightness = ctrl->val; ++ break; ++ case V4L2_CID_CONTRAST: ++ cam->contrast = ctrl->val; ++ break; ++ case V4L2_CID_SATURATION: ++ cam->color = ctrl->val; ++ break; ++ case V4L2_CID_HUE: ++ cam->hue = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret == 0) { ++ w9966_pdev_claim(cam); ++ ++ if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 || ++ w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 || ++ w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 || ++ w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) { ++ ret = -EIO; ++ } ++ ++ w9966_pdev_release(cam); ++ } ++ mutex_unlock(&cam->lock); ++ return ret; ++} ++ ++static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct w9966 *cam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ pix->width = cam->width; ++ pix->height = cam->height; ++ pix->pixelformat = V4L2_PIX_FMT_YUYV; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 2 * cam->width; ++ pix->sizeimage = 2 * cam->width * cam->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ if (pix->width < 2) ++ pix->width = 2; ++ if (pix->height < 1) ++ pix->height = 1; ++ if (pix->width > W9966_WND_MAX_W) ++ pix->width = W9966_WND_MAX_W; ++ if (pix->height > W9966_WND_MAX_H) ++ pix->height = W9966_WND_MAX_H; ++ pix->pixelformat = V4L2_PIX_FMT_YUYV; ++ pix->field = V4L2_FIELD_NONE; ++ pix->bytesperline = 2 * pix->width; ++ pix->sizeimage = 2 * pix->width * pix->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct w9966 *cam = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ int ret = cam_try_fmt_vid_cap(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ ++ mutex_lock(&cam->lock); ++ /* Update camera regs */ ++ w9966_pdev_claim(cam); ++ ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height); ++ w9966_pdev_release(cam); ++ mutex_unlock(&cam->lock); ++ return ret; ++} ++ ++static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static struct v4l2_fmtdesc formats[] = { ++ { 0, 0, 0, ++ "YUV 4:2:2", V4L2_PIX_FMT_YUYV, ++ { 0, 0, 0, 0 } ++ }, ++ }; ++ enum v4l2_buf_type type = fmt->type; ++ ++ if (fmt->index > 0) ++ return -EINVAL; ++ ++ *fmt = formats[fmt->index]; ++ fmt->type = type; ++ return 0; ++} ++ ++/* Capture data */ ++static ssize_t w9966_v4l_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct w9966 *cam = video_drvdata(file); ++ unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */ ++ unsigned char __user *dest = (unsigned char __user *)buf; ++ unsigned long dleft = count; ++ unsigned char *tbuf; ++ ++ /* Why would anyone want more than this?? */ ++ if (count > cam->width * cam->height * 2) ++ return -EINVAL; ++ ++ mutex_lock(&cam->lock); ++ w9966_pdev_claim(cam); ++ w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */ ++ w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */ ++ w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */ ++ ++ /* write special capture-addr and negotiate into data transfer */ ++ if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) || ++ (parport_write(cam->pport, &addr, 1) != 1) || ++ (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) { ++ w9966_pdev_release(cam); ++ mutex_unlock(&cam->lock); ++ return -EFAULT; ++ } ++ ++ tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL); ++ if (tbuf == NULL) { ++ count = -ENOMEM; ++ goto out; ++ } ++ ++ while (dleft > 0) { ++ unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; ++ ++ if (parport_read(cam->pport, tbuf, tsize) < tsize) { ++ count = -EFAULT; ++ goto out; ++ } ++ if (copy_to_user(dest, tbuf, tsize) != 0) { ++ count = -EFAULT; ++ goto out; ++ } ++ dest += tsize; ++ dleft -= tsize; ++ } ++ ++ w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */ ++ ++out: ++ kfree(tbuf); ++ w9966_pdev_release(cam); ++ mutex_unlock(&cam->lock); ++ ++ return count; ++} ++ ++static const struct v4l2_file_operations w9966_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = v4l2_fh_release, ++ .poll = v4l2_ctrl_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .read = w9966_v4l_read, ++}; ++ ++static const struct v4l2_ioctl_ops w9966_ioctl_ops = { ++ .vidioc_querycap = cam_querycap, ++ .vidioc_g_input = cam_g_input, ++ .vidioc_s_input = cam_s_input, ++ .vidioc_enum_input = cam_enum_input, ++ .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static const struct v4l2_ctrl_ops cam_ctrl_ops = { ++ .s_ctrl = cam_s_ctrl, ++}; ++ ++ ++/* Initialize camera device. Setup all internal flags, set a ++ default video mode, setup ccd-chip, register v4l device etc.. ++ Also used for 'probing' of hardware. ++ -1 on error */ ++static int w9966_init(struct w9966 *cam, struct parport *port) ++{ ++ struct v4l2_device *v4l2_dev = &cam->v4l2_dev; ++ ++ if (cam->dev_state != 0) ++ return -1; ++ ++ strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name)); ++ ++ if (v4l2_device_register(NULL, v4l2_dev) < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ return -1; ++ } ++ ++ v4l2_ctrl_handler_init(&cam->hdl, 4); ++ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, ++ V4L2_CID_CONTRAST, -64, 64, 1, 64); ++ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, ++ V4L2_CID_SATURATION, -64, 64, 1, 64); ++ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ if (cam->hdl.error) { ++ v4l2_err(v4l2_dev, "couldn't register controls\n"); ++ return -1; ++ } ++ cam->pport = port; ++ cam->brightness = 128; ++ cam->contrast = 64; ++ cam->color = 64; ++ cam->hue = 0; ++ ++ /* Select requested transfer mode */ ++ switch (parmode) { ++ default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */ ++ case 0: ++ if (port->modes & PARPORT_MODE_ECP) ++ cam->ppmode = IEEE1284_MODE_ECP; ++ else if (port->modes & PARPORT_MODE_EPP) ++ cam->ppmode = IEEE1284_MODE_EPP; ++ else ++ cam->ppmode = IEEE1284_MODE_ECP; ++ break; ++ case 1: /* hw- or sw-ecp */ ++ cam->ppmode = IEEE1284_MODE_ECP; ++ break; ++ case 2: /* hw- or sw-epp */ ++ cam->ppmode = IEEE1284_MODE_EPP; ++ break; ++ } ++ ++ /* Tell the parport driver that we exists */ ++ cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); ++ if (cam->pdev == NULL) { ++ DPRINTF("parport_register_device() failed\n"); ++ return -1; ++ } ++ w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); ++ ++ w9966_pdev_claim(cam); ++ ++ /* Setup a default capture mode */ ++ if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { ++ DPRINTF("w9966_setup() failed.\n"); ++ return -1; ++ } ++ ++ w9966_pdev_release(cam); ++ ++ /* Fill in the video_device struct and register us to v4l */ ++ strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name)); ++ cam->vdev.v4l2_dev = v4l2_dev; ++ cam->vdev.fops = &w9966_fops; ++ cam->vdev.ioctl_ops = &w9966_ioctl_ops; ++ cam->vdev.release = video_device_release_empty; ++ cam->vdev.ctrl_handler = &cam->hdl; ++ set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); ++ video_set_drvdata(&cam->vdev, cam); ++ ++ mutex_init(&cam->lock); ++ ++ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) ++ return -1; ++ ++ w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); ++ ++ /* All ok */ ++ v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n", ++ cam->pport->name); ++ return 0; ++} ++ ++ ++/* Terminate everything gracefully */ ++static void w9966_term(struct w9966 *cam) ++{ ++ /* Unregister from v4l */ ++ if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { ++ video_unregister_device(&cam->vdev); ++ w9966_set_state(cam, W9966_STATE_VDEV, 0); ++ } ++ ++ v4l2_ctrl_handler_free(&cam->hdl); ++ ++ /* Terminate from IEEE1284 mode and release pdev block */ ++ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { ++ w9966_pdev_claim(cam); ++ parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); ++ w9966_pdev_release(cam); ++ } ++ ++ /* Unregister from parport */ ++ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { ++ parport_unregister_device(cam->pdev); ++ w9966_set_state(cam, W9966_STATE_PDEV, 0); ++ } ++ memset(cam, 0, sizeof(*cam)); ++} ++ ++ ++/* Called once for every parport on init */ ++static void w9966_attach(struct parport *port) ++{ ++ int i; ++ ++ for (i = 0; i < W9966_MAXCAMS; i++) { ++ if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */ ++ continue; ++ if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) { ++ if (w9966_init(&w9966_cams[i], port) != 0) ++ w9966_term(&w9966_cams[i]); ++ break; /* return */ ++ } ++ } ++} ++ ++/* Called once for every parport on termination */ ++static void w9966_detach(struct parport *port) ++{ ++ int i; ++ ++ for (i = 0; i < W9966_MAXCAMS; i++) ++ if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) ++ w9966_term(&w9966_cams[i]); ++} ++ ++ ++static struct parport_driver w9966_ppd = { ++ .name = W9966_DRIVERNAME, ++ .attach = w9966_attach, ++ .detach = w9966_detach, ++}; ++ ++/* Module entry point */ ++static int __init w9966_mod_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < W9966_MAXCAMS; i++) ++ w9966_cams[i].dev_state = 0; ++ ++ return parport_register_driver(&w9966_ppd); ++} ++ ++/* Module cleanup */ ++static void __exit w9966_mod_term(void) ++{ ++ parport_unregister_driver(&w9966_ppd); ++} ++ ++module_init(w9966_mod_init); ++module_exit(w9966_mod_term); +diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig +new file mode 100644 +index 0000000..d4e2ed3 +--- /dev/null ++++ b/drivers/media/pci/Kconfig +@@ -0,0 +1,47 @@ ++menuconfig MEDIA_PCI_SUPPORT ++ bool "Media PCI Adapters" ++ depends on PCI && MEDIA_SUPPORT ++ help ++ Enable media drivers for PCI/PCIe bus. ++ If you have such devices, say Y. ++ ++if MEDIA_PCI_SUPPORT ++ ++if MEDIA_CAMERA_SUPPORT ++ comment "Media capture support" ++source "drivers/media/pci/meye/Kconfig" ++source "drivers/media/pci/sta2x11/Kconfig" ++endif ++ ++if MEDIA_ANALOG_TV_SUPPORT ++ comment "Media capture/analog TV support" ++source "drivers/media/pci/ivtv/Kconfig" ++source "drivers/media/pci/zoran/Kconfig" ++source "drivers/media/pci/saa7146/Kconfig" ++endif ++ ++if MEDIA_ANALOG_TV_SUPPORT || MEDIA_DIGITAL_TV_SUPPORT ++ comment "Media capture/analog/hybrid TV support" ++source "drivers/media/pci/cx18/Kconfig" ++source "drivers/media/pci/cx23885/Kconfig" ++source "drivers/media/pci/cx25821/Kconfig" ++source "drivers/media/pci/cx88/Kconfig" ++source "drivers/media/pci/bt8xx/Kconfig" ++source "drivers/media/pci/saa7134/Kconfig" ++source "drivers/media/pci/saa7164/Kconfig" ++ ++endif ++ ++if MEDIA_DIGITAL_TV_SUPPORT ++ comment "Media digital TV PCI Adapters" ++source "drivers/media/pci/ttpci/Kconfig" ++source "drivers/media/pci/b2c2/Kconfig" ++source "drivers/media/pci/pluto2/Kconfig" ++source "drivers/media/pci/dm1105/Kconfig" ++source "drivers/media/pci/pt1/Kconfig" ++source "drivers/media/pci/mantis/Kconfig" ++source "drivers/media/pci/ngene/Kconfig" ++source "drivers/media/pci/ddbridge/Kconfig" ++endif ++ ++endif #MEDIA_PCI_SUPPORT +diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile +new file mode 100644 +index 0000000..35cc578 +--- /dev/null ++++ b/drivers/media/pci/Makefile +@@ -0,0 +1,26 @@ ++# ++# Makefile for the kernel multimedia device drivers. ++# ++ ++obj-y += ttpci/ \ ++ b2c2/ \ ++ pluto2/ \ ++ dm1105/ \ ++ pt1/ \ ++ mantis/ \ ++ ngene/ \ ++ ddbridge/ \ ++ b2c2/ \ ++ saa7146/ ++ ++obj-$(CONFIG_VIDEO_IVTV) += ivtv/ ++obj-$(CONFIG_VIDEO_ZORAN) += zoran/ ++obj-$(CONFIG_VIDEO_CX18) += cx18/ ++obj-$(CONFIG_VIDEO_CX23885) += cx23885/ ++obj-$(CONFIG_VIDEO_CX25821) += cx25821/ ++obj-$(CONFIG_VIDEO_CX88) += cx88/ ++obj-$(CONFIG_VIDEO_BT848) += bt8xx/ ++obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ ++obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ ++obj-$(CONFIG_VIDEO_MEYE) += meye/ ++obj-$(CONFIG_STA2X11_VIP) += sta2x11/ +diff --git a/drivers/media/pci/b2c2/Kconfig b/drivers/media/pci/b2c2/Kconfig +new file mode 100644 +index 0000000..58761a2 +--- /dev/null ++++ b/drivers/media/pci/b2c2/Kconfig +@@ -0,0 +1,15 @@ ++config DVB_B2C2_FLEXCOP_PCI ++ tristate "Technisat/B2C2 Air/Sky/Cable2PC PCI" ++ depends on DVB_CORE && I2C ++ help ++ Support for the Air/Sky/CableStar2 PCI card (DVB/ATSC) by Technisat/B2C2. ++ ++ Say Y if you own such a device and want to use it. ++ ++config DVB_B2C2_FLEXCOP_PCI_DEBUG ++ bool "Enable debug for the B2C2 FlexCop drivers" ++ depends on DVB_B2C2_FLEXCOP_PCI ++ select DVB_B2C2_FLEXCOP_DEBUG ++ help ++ Say Y if you want to enable the module option to control debug messages ++ of all B2C2 FlexCop drivers. +diff --git a/drivers/media/pci/b2c2/Makefile b/drivers/media/pci/b2c2/Makefile +new file mode 100644 +index 0000000..b894320 +--- /dev/null ++++ b/drivers/media/pci/b2c2/Makefile +@@ -0,0 +1,9 @@ ++ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),) ++b2c2-flexcop-pci-objs += flexcop-dma.o ++endif ++ ++b2c2-flexcop-pci-objs += flexcop-pci.o ++obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ ++ccflags-y += -Idrivers/media/common/b2c2/ +diff --git a/drivers/media/pci/b2c2/flexcop-dma.c b/drivers/media/pci/b2c2/flexcop-dma.c +new file mode 100644 +index 0000000..2881e0d +--- /dev/null ++++ b/drivers/media/pci/b2c2/flexcop-dma.c +@@ -0,0 +1,172 @@ ++/* ++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III ++ * flexcop-dma.c - configuring and controlling the DMA of the FlexCop ++ * see flexcop.c for copyright information ++ */ ++#include "flexcop.h" ++ ++int flexcop_dma_allocate(struct pci_dev *pdev, ++ struct flexcop_dma *dma, u32 size) ++{ ++ u8 *tcpu; ++ dma_addr_t tdma = 0; ++ ++ if (size % 2) { ++ err("dma buffersize has to be even."); ++ return -EINVAL; ++ } ++ ++ if ((tcpu = pci_alloc_consistent(pdev, size, &tdma)) != NULL) { ++ dma->pdev = pdev; ++ dma->cpu_addr0 = tcpu; ++ dma->dma_addr0 = tdma; ++ dma->cpu_addr1 = tcpu + size/2; ++ dma->dma_addr1 = tdma + size/2; ++ dma->size = size/2; ++ return 0; ++ } ++ return -ENOMEM; ++} ++EXPORT_SYMBOL(flexcop_dma_allocate); ++ ++void flexcop_dma_free(struct flexcop_dma *dma) ++{ ++ pci_free_consistent(dma->pdev, dma->size*2, ++ dma->cpu_addr0, dma->dma_addr0); ++ memset(dma,0,sizeof(struct flexcop_dma)); ++} ++EXPORT_SYMBOL(flexcop_dma_free); ++ ++int flexcop_dma_config(struct flexcop_device *fc, ++ struct flexcop_dma *dma, ++ flexcop_dma_index_t dma_idx) ++{ ++ flexcop_ibi_value v0x0,v0x4,v0xc; ++ v0x0.raw = v0x4.raw = v0xc.raw = 0; ++ ++ v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2; ++ v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2; ++ v0x4.dma_0x4_write.dma_addr_size = dma->size / 4; ++ ++ if ((dma_idx & FC_DMA_1) == dma_idx) { ++ fc->write_ibi_reg(fc,dma1_000,v0x0); ++ fc->write_ibi_reg(fc,dma1_004,v0x4); ++ fc->write_ibi_reg(fc,dma1_00c,v0xc); ++ } else if ((dma_idx & FC_DMA_2) == dma_idx) { ++ fc->write_ibi_reg(fc,dma2_010,v0x0); ++ fc->write_ibi_reg(fc,dma2_014,v0x4); ++ fc->write_ibi_reg(fc,dma2_01c,v0xc); ++ } else { ++ err("either DMA1 or DMA2 can be configured within one " ++ "flexcop_dma_config call."); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(flexcop_dma_config); ++ ++/* start the DMA transfers, but not the DMA IRQs */ ++int flexcop_dma_xfer_control(struct flexcop_device *fc, ++ flexcop_dma_index_t dma_idx, ++ flexcop_dma_addr_index_t index, ++ int onoff) ++{ ++ flexcop_ibi_value v0x0,v0xc; ++ flexcop_ibi_register r0x0,r0xc; ++ ++ if ((dma_idx & FC_DMA_1) == dma_idx) { ++ r0x0 = dma1_000; ++ r0xc = dma1_00c; ++ } else if ((dma_idx & FC_DMA_2) == dma_idx) { ++ r0x0 = dma2_010; ++ r0xc = dma2_01c; ++ } else { ++ err("either transfer DMA1 or DMA2 can be started within one " ++ "flexcop_dma_xfer_control call."); ++ return -EINVAL; ++ } ++ ++ v0x0 = fc->read_ibi_reg(fc,r0x0); ++ v0xc = fc->read_ibi_reg(fc,r0xc); ++ ++ deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); ++ deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); ++ ++ if (index & FC_DMA_SUBADDR_0) ++ v0x0.dma_0x0.dma_0start = onoff; ++ ++ if (index & FC_DMA_SUBADDR_1) ++ v0xc.dma_0xc.dma_1start = onoff; ++ ++ fc->write_ibi_reg(fc,r0x0,v0x0); ++ fc->write_ibi_reg(fc,r0xc,v0xc); ++ ++ deb_rdump("reg: %03x: %x\n",r0x0,v0x0.raw); ++ deb_rdump("reg: %03x: %x\n",r0xc,v0xc.raw); ++ return 0; ++} ++EXPORT_SYMBOL(flexcop_dma_xfer_control); ++ ++static int flexcop_dma_remap(struct flexcop_device *fc, ++ flexcop_dma_index_t dma_idx, ++ int onoff) ++{ ++ flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c; ++ flexcop_ibi_value v = fc->read_ibi_reg(fc,r); ++ deb_info("%s\n",__func__); ++ v.dma_0xc.remap_enable = onoff; ++ fc->write_ibi_reg(fc,r,v); ++ return 0; ++} ++ ++int flexcop_dma_control_size_irq(struct flexcop_device *fc, ++ flexcop_dma_index_t no, ++ int onoff) ++{ ++ flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); ++ ++ if (no & FC_DMA_1) ++ v.ctrl_208.DMA1_IRQ_Enable_sig = onoff; ++ ++ if (no & FC_DMA_2) ++ v.ctrl_208.DMA2_IRQ_Enable_sig = onoff; ++ ++ fc->write_ibi_reg(fc,ctrl_208,v); ++ return 0; ++} ++EXPORT_SYMBOL(flexcop_dma_control_size_irq); ++ ++int flexcop_dma_control_timer_irq(struct flexcop_device *fc, ++ flexcop_dma_index_t no, ++ int onoff) ++{ ++ flexcop_ibi_value v = fc->read_ibi_reg(fc,ctrl_208); ++ ++ if (no & FC_DMA_1) ++ v.ctrl_208.DMA1_Timer_Enable_sig = onoff; ++ ++ if (no & FC_DMA_2) ++ v.ctrl_208.DMA2_Timer_Enable_sig = onoff; ++ ++ fc->write_ibi_reg(fc,ctrl_208,v); ++ return 0; ++} ++EXPORT_SYMBOL(flexcop_dma_control_timer_irq); ++ ++/* 1 cycles = 1.97 msec */ ++int flexcop_dma_config_timer(struct flexcop_device *fc, ++ flexcop_dma_index_t dma_idx, u8 cycles) ++{ ++ flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014; ++ flexcop_ibi_value v = fc->read_ibi_reg(fc,r); ++ ++ flexcop_dma_remap(fc,dma_idx,0); ++ ++ deb_info("%s\n",__func__); ++ v.dma_0x4_write.dmatimer = cycles; ++ fc->write_ibi_reg(fc,r,v); ++ return 0; ++} ++EXPORT_SYMBOL(flexcop_dma_config_timer); ++ +diff --git a/drivers/media/pci/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c +new file mode 100644 +index 0000000..44f8fb5 +--- /dev/null ++++ b/drivers/media/pci/b2c2/flexcop-pci.c +@@ -0,0 +1,450 @@ ++/* ++ * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III ++ * flexcop-pci.c - covers the PCI part including DMA transfers ++ * see flexcop.c for copyright information ++ */ ++ ++#define FC_LOG_PREFIX "flexcop-pci" ++#include "flexcop-common.h" ++ ++static int enable_pid_filtering = 1; ++module_param(enable_pid_filtering, int, 0444); ++MODULE_PARM_DESC(enable_pid_filtering, ++ "enable hardware pid filtering: supported values: 0 (fullts), 1"); ++ ++static int irq_chk_intv = 100; ++module_param(irq_chk_intv, int, 0644); ++MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog."); ++ ++#ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG ++#define dprintk(level,args...) \ ++ do { if ((debug & level)) printk(args); } while (0) ++#define DEBSTATUS "" ++#else ++#define dprintk(level,args...) ++#define DEBSTATUS " (debugging is not enabled)" ++#endif ++ ++#define deb_info(args...) dprintk(0x01, args) ++#define deb_reg(args...) dprintk(0x02, args) ++#define deb_ts(args...) dprintk(0x04, args) ++#define deb_irq(args...) dprintk(0x08, args) ++#define deb_chk(args...) dprintk(0x10, args) ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, ++ "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))." ++ DEBSTATUS); ++ ++#define DRIVER_VERSION "0.1" ++#define DRIVER_NAME "flexcop-pci" ++#define DRIVER_AUTHOR "Patrick Boettcher " ++ ++struct flexcop_pci { ++ struct pci_dev *pdev; ++ ++#define FC_PCI_INIT 0x01 ++#define FC_PCI_DMA_INIT 0x02 ++ int init_state; ++ ++ void __iomem *io_mem; ++ u32 irq; ++ /* buffersize (at least for DMA1, need to be % 188 == 0, ++ * this logic is required */ ++#define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188) ++#define FC_DEFAULT_DMA2_BUFSIZE (10 * 188) ++ struct flexcop_dma dma[2]; ++ ++ int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */ ++ u32 last_dma1_cur_pos; ++ /* position of the pointer last time the timer/packet irq occurred */ ++ int count; ++ int count_prev; ++ int stream_problem; ++ ++ spinlock_t irq_lock; ++ unsigned long last_irq; ++ ++ struct delayed_work irq_check_work; ++ struct flexcop_device *fc_dev; ++}; ++ ++static int lastwreg, lastwval, lastrreg, lastrval; ++ ++static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc, ++ flexcop_ibi_register r) ++{ ++ struct flexcop_pci *fc_pci = fc->bus_specific; ++ flexcop_ibi_value v; ++ v.raw = readl(fc_pci->io_mem + r); ++ ++ if (lastrreg != r || lastrval != v.raw) { ++ lastrreg = r; lastrval = v.raw; ++ deb_reg("new rd: %3x: %08x\n", r, v.raw); ++ } ++ ++ return v; ++} ++ ++static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, ++ flexcop_ibi_register r, flexcop_ibi_value v) ++{ ++ struct flexcop_pci *fc_pci = fc->bus_specific; ++ ++ if (lastwreg != r || lastwval != v.raw) { ++ lastwreg = r; lastwval = v.raw; ++ deb_reg("new wr: %3x: %08x\n", r, v.raw); ++ } ++ ++ writel(v.raw, fc_pci->io_mem + r); ++ return 0; ++} ++ ++static void flexcop_pci_irq_check_work(struct work_struct *work) ++{ ++ struct flexcop_pci *fc_pci = ++ container_of(work, struct flexcop_pci, irq_check_work.work); ++ struct flexcop_device *fc = fc_pci->fc_dev; ++ ++ if (fc->feedcount) { ++ ++ if (fc_pci->count == fc_pci->count_prev) { ++ deb_chk("no IRQ since the last check\n"); ++ if (fc_pci->stream_problem++ == 3) { ++ struct dvb_demux_feed *feed; ++ deb_info("flexcop-pci: stream problem, resetting pid filter\n"); ++ ++ spin_lock_irq(&fc->demux.lock); ++ list_for_each_entry(feed, &fc->demux.feed_list, ++ list_head) { ++ flexcop_pid_feed_control(fc, feed, 0); ++ } ++ ++ list_for_each_entry(feed, &fc->demux.feed_list, ++ list_head) { ++ flexcop_pid_feed_control(fc, feed, 1); ++ } ++ spin_unlock_irq(&fc->demux.lock); ++ ++ fc_pci->stream_problem = 0; ++ } ++ } else { ++ fc_pci->stream_problem = 0; ++ fc_pci->count_prev = fc_pci->count; ++ } ++ } ++ ++ schedule_delayed_work(&fc_pci->irq_check_work, ++ msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv)); ++} ++ ++/* When PID filtering is turned on, we use the timer IRQ, because small amounts ++ * of data need to be passed to the user space instantly as well. When PID ++ * filtering is turned off, we use the page-change-IRQ */ ++static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) ++{ ++ struct flexcop_pci *fc_pci = dev_id; ++ struct flexcop_device *fc = fc_pci->fc_dev; ++ unsigned long flags; ++ flexcop_ibi_value v; ++ irqreturn_t ret = IRQ_HANDLED; ++ ++ spin_lock_irqsave(&fc_pci->irq_lock, flags); ++ v = fc->read_ibi_reg(fc, irq_20c); ++ ++ /* errors */ ++ if (v.irq_20c.Data_receiver_error) ++ deb_chk("data receiver error\n"); ++ if (v.irq_20c.Continuity_error_flag) ++ deb_chk("Contunuity error flag is set\n"); ++ if (v.irq_20c.LLC_SNAP_FLAG_set) ++ deb_chk("LLC_SNAP_FLAG_set is set\n"); ++ if (v.irq_20c.Transport_Error) ++ deb_chk("Transport error\n"); ++ ++ if ((fc_pci->count % 1000) == 0) ++ deb_chk("%d valid irq took place so far\n", fc_pci->count); ++ ++ if (v.irq_20c.DMA1_IRQ_Status == 1) { ++ if (fc_pci->active_dma1_addr == 0) ++ flexcop_pass_dmx_packets(fc_pci->fc_dev, ++ fc_pci->dma[0].cpu_addr0, ++ fc_pci->dma[0].size / 188); ++ else ++ flexcop_pass_dmx_packets(fc_pci->fc_dev, ++ fc_pci->dma[0].cpu_addr1, ++ fc_pci->dma[0].size / 188); ++ ++ deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr); ++ fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr; ++ /* for the timer IRQ we only can use buffer dmx feeding, because we don't have ++ * complete TS packets when reading from the DMA memory */ ++ } else if (v.irq_20c.DMA1_Timer_Status == 1) { ++ dma_addr_t cur_addr = ++ fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2; ++ u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0; ++ ++ deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, " ++ "last_cur_pos: %08x ", ++ jiffies_to_usecs(jiffies - fc_pci->last_irq), ++ v.raw, (unsigned long long)cur_addr, cur_pos, ++ fc_pci->last_dma1_cur_pos); ++ fc_pci->last_irq = jiffies; ++ ++ /* buffer end was reached, restarted from the beginning ++ * pass the data from last_cur_pos to the buffer end to the demux ++ */ ++ if (cur_pos < fc_pci->last_dma1_cur_pos) { ++ deb_irq(" end was reached: passing %d bytes ", ++ (fc_pci->dma[0].size*2 - 1) - ++ fc_pci->last_dma1_cur_pos); ++ flexcop_pass_dmx_data(fc_pci->fc_dev, ++ fc_pci->dma[0].cpu_addr0 + ++ fc_pci->last_dma1_cur_pos, ++ (fc_pci->dma[0].size*2) - ++ fc_pci->last_dma1_cur_pos); ++ fc_pci->last_dma1_cur_pos = 0; ++ } ++ ++ if (cur_pos > fc_pci->last_dma1_cur_pos) { ++ deb_irq(" passing %d bytes ", ++ cur_pos - fc_pci->last_dma1_cur_pos); ++ flexcop_pass_dmx_data(fc_pci->fc_dev, ++ fc_pci->dma[0].cpu_addr0 + ++ fc_pci->last_dma1_cur_pos, ++ cur_pos - fc_pci->last_dma1_cur_pos); ++ } ++ deb_irq("\n"); ++ ++ fc_pci->last_dma1_cur_pos = cur_pos; ++ fc_pci->count++; ++ } else { ++ deb_irq("isr for flexcop called, " ++ "apparently without reason (%08x)\n", v.raw); ++ ret = IRQ_NONE; ++ } ++ ++ spin_unlock_irqrestore(&fc_pci->irq_lock, flags); ++ return ret; ++} ++ ++static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff) ++{ ++ struct flexcop_pci *fc_pci = fc->bus_specific; ++ if (onoff) { ++ flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1); ++ flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2); ++ flexcop_dma_config_timer(fc, FC_DMA_1, 0); ++ flexcop_dma_xfer_control(fc, FC_DMA_1, ++ FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1); ++ deb_irq("DMA xfer enabled\n"); ++ ++ fc_pci->last_dma1_cur_pos = 0; ++ flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1); ++ deb_irq("IRQ enabled\n"); ++ fc_pci->count_prev = fc_pci->count; ++ } else { ++ flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0); ++ deb_irq("IRQ disabled\n"); ++ ++ flexcop_dma_xfer_control(fc, FC_DMA_1, ++ FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0); ++ deb_irq("DMA xfer disabled\n"); ++ } ++ return 0; ++} ++ ++static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci) ++{ ++ int ret; ++ ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0], ++ FC_DEFAULT_DMA1_BUFSIZE); ++ if (ret != 0) ++ return ret; ++ ++ ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1], ++ FC_DEFAULT_DMA2_BUFSIZE); ++ if (ret != 0) { ++ flexcop_dma_free(&fc_pci->dma[0]); ++ return ret; ++ } ++ ++ flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA | ++ FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1); ++ flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO | ++ FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2); ++ fc_pci->init_state |= FC_PCI_DMA_INIT; ++ return ret; ++} ++ ++static void flexcop_pci_dma_exit(struct flexcop_pci *fc_pci) ++{ ++ if (fc_pci->init_state & FC_PCI_DMA_INIT) { ++ flexcop_dma_free(&fc_pci->dma[0]); ++ flexcop_dma_free(&fc_pci->dma[1]); ++ } ++ fc_pci->init_state &= ~FC_PCI_DMA_INIT; ++} ++ ++static int flexcop_pci_init(struct flexcop_pci *fc_pci) ++{ ++ int ret; ++ ++ info("card revision %x", fc_pci->pdev->revision); ++ ++ if ((ret = pci_enable_device(fc_pci->pdev)) != 0) ++ return ret; ++ pci_set_master(fc_pci->pdev); ++ ++ if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0) ++ goto err_pci_disable_device; ++ ++ fc_pci->io_mem = pci_iomap(fc_pci->pdev, 0, 0x800); ++ ++ if (!fc_pci->io_mem) { ++ err("cannot map io memory\n"); ++ ret = -EIO; ++ goto err_pci_release_regions; ++ } ++ ++ pci_set_drvdata(fc_pci->pdev, fc_pci); ++ spin_lock_init(&fc_pci->irq_lock); ++ if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, ++ IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) ++ goto err_pci_iounmap; ++ ++ fc_pci->init_state |= FC_PCI_INIT; ++ return ret; ++ ++err_pci_iounmap: ++ pci_iounmap(fc_pci->pdev, fc_pci->io_mem); ++ pci_set_drvdata(fc_pci->pdev, NULL); ++err_pci_release_regions: ++ pci_release_regions(fc_pci->pdev); ++err_pci_disable_device: ++ pci_disable_device(fc_pci->pdev); ++ return ret; ++} ++ ++static void flexcop_pci_exit(struct flexcop_pci *fc_pci) ++{ ++ if (fc_pci->init_state & FC_PCI_INIT) { ++ free_irq(fc_pci->pdev->irq, fc_pci); ++ pci_iounmap(fc_pci->pdev, fc_pci->io_mem); ++ pci_set_drvdata(fc_pci->pdev, NULL); ++ pci_release_regions(fc_pci->pdev); ++ pci_disable_device(fc_pci->pdev); ++ } ++ fc_pci->init_state &= ~FC_PCI_INIT; ++} ++ ++static int flexcop_pci_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct flexcop_device *fc; ++ struct flexcop_pci *fc_pci; ++ int ret = -ENOMEM; ++ ++ if ((fc = flexcop_device_kmalloc(sizeof(struct flexcop_pci))) == NULL) { ++ err("out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ /* general flexcop init */ ++ fc_pci = fc->bus_specific; ++ fc_pci->fc_dev = fc; ++ ++ fc->read_ibi_reg = flexcop_pci_read_ibi_reg; ++ fc->write_ibi_reg = flexcop_pci_write_ibi_reg; ++ fc->i2c_request = flexcop_i2c_request; ++ fc->get_mac_addr = flexcop_eeprom_check_mac_addr; ++ fc->stream_control = flexcop_pci_stream_control; ++ ++ if (enable_pid_filtering) ++ info("will use the HW PID filter."); ++ else ++ info("will pass the complete TS to the demuxer."); ++ ++ fc->pid_filtering = enable_pid_filtering; ++ fc->bus_type = FC_PCI; ++ fc->dev = &pdev->dev; ++ fc->owner = THIS_MODULE; ++ ++ /* bus specific part */ ++ fc_pci->pdev = pdev; ++ if ((ret = flexcop_pci_init(fc_pci)) != 0) ++ goto err_kfree; ++ ++ /* init flexcop */ ++ if ((ret = flexcop_device_initialize(fc)) != 0) ++ goto err_pci_exit; ++ ++ /* init dma */ ++ if ((ret = flexcop_pci_dma_init(fc_pci)) != 0) ++ goto err_fc_exit; ++ ++ INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work); ++ ++ if (irq_chk_intv > 0) ++ schedule_delayed_work(&fc_pci->irq_check_work, ++ msecs_to_jiffies(irq_chk_intv < 100 ? ++ 100 : ++ irq_chk_intv)); ++ return ret; ++ ++err_fc_exit: ++ flexcop_device_exit(fc); ++err_pci_exit: ++ flexcop_pci_exit(fc_pci); ++err_kfree: ++ flexcop_device_kfree(fc); ++ return ret; ++} ++ ++/* in theory every _exit function should be called exactly two times, ++ * here and in the bail-out-part of the _init-function ++ */ ++static void flexcop_pci_remove(struct pci_dev *pdev) ++{ ++ struct flexcop_pci *fc_pci = pci_get_drvdata(pdev); ++ ++ if (irq_chk_intv > 0) ++ cancel_delayed_work(&fc_pci->irq_check_work); ++ ++ flexcop_pci_dma_exit(fc_pci); ++ flexcop_device_exit(fc_pci->fc_dev); ++ flexcop_pci_exit(fc_pci); ++ flexcop_device_kfree(fc_pci->fc_dev); ++} ++ ++static struct pci_device_id flexcop_pci_tbl[] = { ++ { PCI_DEVICE(0x13d0, 0x2103) }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, flexcop_pci_tbl); ++ ++static struct pci_driver flexcop_pci_driver = { ++ .name = "b2c2_flexcop_pci", ++ .id_table = flexcop_pci_tbl, ++ .probe = flexcop_pci_probe, ++ .remove = flexcop_pci_remove, ++}; ++ ++static int __init flexcop_pci_module_init(void) ++{ ++ return pci_register_driver(&flexcop_pci_driver); ++} ++ ++static void __exit flexcop_pci_module_exit(void) ++{ ++ pci_unregister_driver(&flexcop_pci_driver); ++} ++ ++module_init(flexcop_pci_module_init); ++module_exit(flexcop_pci_module_exit); ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_NAME); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig +new file mode 100644 +index 0000000..61d09e0 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/Kconfig +@@ -0,0 +1,43 @@ ++config VIDEO_BT848 ++ tristate "BT848 Video For Linux" ++ depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 ++ select I2C_ALGOBIT ++ select VIDEO_BTCX ++ select VIDEOBUF_DMA_SG ++ depends on RC_CORE ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ Support for BT848 based frame grabber/overlay boards. This includes ++ the Miro, Hauppauge and STB boards. Please read the material in ++ for more information. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bttv. ++ ++config DVB_BT8XX ++ tristate "DVB/ATSC Support for bt878 based TV cards" ++ depends on DVB_CORE && PCI && I2C && VIDEO_BT848 ++ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SP887X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_NXT6000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX24110 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_OR51211 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for PCI cards based on the Bt8xx PCI bridge. Examples are ++ the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, ++ the pcHDTV HD2000 cards, the DViCO FusionHDTV Lite cards, and ++ some AVerMedia cards. ++ ++ Since these cards have no MPEG decoder onboard, they transmit ++ only compressed MPEG data over the PCI bus, so you need ++ an external software decoder to watch TV on your computer. ++ ++ Say Y if you own such a device and want to use it. +diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile +new file mode 100644 +index 0000000..5f06597 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/Makefile +@@ -0,0 +1,11 @@ ++bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ ++ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \ ++ bttv-input.o bttv-audio-hook.o ++ ++obj-$(CONFIG_VIDEO_BT848) += bttv.o ++obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o ++ ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/dvb-frontends ++ccflags-y += -Idrivers/media/i2c ++ccflags-y += -Idrivers/media/tuners +diff --git a/drivers/media/pci/bt8xx/bt848.h b/drivers/media/pci/bt8xx/bt848.h +new file mode 100644 +index 0000000..c37e6ac +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bt848.h +@@ -0,0 +1,369 @@ ++/* ++ bt848.h - Bt848 register offsets ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef _BT848_H_ ++#define _BT848_H_ ++ ++#ifndef PCI_VENDOR_ID_BROOKTREE ++#define PCI_VENDOR_ID_BROOKTREE 0x109e ++#endif ++#ifndef PCI_DEVICE_ID_BT848 ++#define PCI_DEVICE_ID_BT848 0x350 ++#endif ++#ifndef PCI_DEVICE_ID_BT849 ++#define PCI_DEVICE_ID_BT849 0x351 ++#endif ++#ifndef PCI_DEVICE_ID_FUSION879 ++#define PCI_DEVICE_ID_FUSION879 0x36c ++#endif ++ ++#ifndef PCI_DEVICE_ID_BT878 ++#define PCI_DEVICE_ID_BT878 0x36e ++#endif ++#ifndef PCI_DEVICE_ID_BT879 ++#define PCI_DEVICE_ID_BT879 0x36f ++#endif ++ ++/* Brooktree 848 registers */ ++ ++#define BT848_DSTATUS 0x000 ++#define BT848_DSTATUS_PRES (1<<7) ++#define BT848_DSTATUS_HLOC (1<<6) ++#define BT848_DSTATUS_FIELD (1<<5) ++#define BT848_DSTATUS_NUML (1<<4) ++#define BT848_DSTATUS_CSEL (1<<3) ++#define BT848_DSTATUS_PLOCK (1<<2) ++#define BT848_DSTATUS_LOF (1<<1) ++#define BT848_DSTATUS_COF (1<<0) ++ ++#define BT848_IFORM 0x004 ++#define BT848_IFORM_HACTIVE (1<<7) ++#define BT848_IFORM_MUXSEL (3<<5) ++#define BT848_IFORM_MUX0 (2<<5) ++#define BT848_IFORM_MUX1 (3<<5) ++#define BT848_IFORM_MUX2 (1<<5) ++#define BT848_IFORM_XTSEL (3<<3) ++#define BT848_IFORM_XT0 (1<<3) ++#define BT848_IFORM_XT1 (2<<3) ++#define BT848_IFORM_XTAUTO (3<<3) ++#define BT848_IFORM_XTBOTH (3<<3) ++#define BT848_IFORM_NTSC 1 ++#define BT848_IFORM_NTSC_J 2 ++#define BT848_IFORM_PAL_BDGHI 3 ++#define BT848_IFORM_PAL_M 4 ++#define BT848_IFORM_PAL_N 5 ++#define BT848_IFORM_SECAM 6 ++#define BT848_IFORM_PAL_NC 7 ++#define BT848_IFORM_AUTO 0 ++#define BT848_IFORM_NORM 7 ++ ++#define BT848_TDEC 0x008 ++#define BT848_TDEC_DEC_FIELD (1<<7) ++#define BT848_TDEC_FLDALIGN (1<<6) ++#define BT848_TDEC_DEC_RAT (0x1f) ++ ++#define BT848_E_CROP 0x00C ++#define BT848_O_CROP 0x08C ++ ++#define BT848_E_VDELAY_LO 0x010 ++#define BT848_O_VDELAY_LO 0x090 ++ ++#define BT848_E_VACTIVE_LO 0x014 ++#define BT848_O_VACTIVE_LO 0x094 ++ ++#define BT848_E_HDELAY_LO 0x018 ++#define BT848_O_HDELAY_LO 0x098 ++ ++#define BT848_E_HACTIVE_LO 0x01C ++#define BT848_O_HACTIVE_LO 0x09C ++ ++#define BT848_E_HSCALE_HI 0x020 ++#define BT848_O_HSCALE_HI 0x0A0 ++ ++#define BT848_E_HSCALE_LO 0x024 ++#define BT848_O_HSCALE_LO 0x0A4 ++ ++#define BT848_BRIGHT 0x028 ++ ++#define BT848_E_CONTROL 0x02C ++#define BT848_O_CONTROL 0x0AC ++#define BT848_CONTROL_LNOTCH (1<<7) ++#define BT848_CONTROL_COMP (1<<6) ++#define BT848_CONTROL_LDEC (1<<5) ++#define BT848_CONTROL_CBSENSE (1<<4) ++#define BT848_CONTROL_CON_MSB (1<<2) ++#define BT848_CONTROL_SAT_U_MSB (1<<1) ++#define BT848_CONTROL_SAT_V_MSB (1<<0) ++ ++#define BT848_CONTRAST_LO 0x030 ++#define BT848_SAT_U_LO 0x034 ++#define BT848_SAT_V_LO 0x038 ++#define BT848_HUE 0x03C ++ ++#define BT848_E_SCLOOP 0x040 ++#define BT848_O_SCLOOP 0x0C0 ++#define BT848_SCLOOP_CAGC (1<<6) ++#define BT848_SCLOOP_CKILL (1<<5) ++#define BT848_SCLOOP_HFILT_AUTO (0<<3) ++#define BT848_SCLOOP_HFILT_CIF (1<<3) ++#define BT848_SCLOOP_HFILT_QCIF (2<<3) ++#define BT848_SCLOOP_HFILT_ICON (3<<3) ++ ++#define BT848_SCLOOP_PEAK (1<<7) ++#define BT848_SCLOOP_HFILT_MINP (1<<3) ++#define BT848_SCLOOP_HFILT_MEDP (2<<3) ++#define BT848_SCLOOP_HFILT_MAXP (3<<3) ++ ++ ++#define BT848_OFORM 0x048 ++#define BT848_OFORM_RANGE (1<<7) ++#define BT848_OFORM_CORE0 (0<<5) ++#define BT848_OFORM_CORE8 (1<<5) ++#define BT848_OFORM_CORE16 (2<<5) ++#define BT848_OFORM_CORE32 (3<<5) ++ ++#define BT848_E_VSCALE_HI 0x04C ++#define BT848_O_VSCALE_HI 0x0CC ++#define BT848_VSCALE_YCOMB (1<<7) ++#define BT848_VSCALE_COMB (1<<6) ++#define BT848_VSCALE_INT (1<<5) ++#define BT848_VSCALE_HI 15 ++ ++#define BT848_E_VSCALE_LO 0x050 ++#define BT848_O_VSCALE_LO 0x0D0 ++#define BT848_TEST 0x054 ++#define BT848_ADELAY 0x060 ++#define BT848_BDELAY 0x064 ++ ++#define BT848_ADC 0x068 ++#define BT848_ADC_RESERVED (2<<6) ++#define BT848_ADC_SYNC_T (1<<5) ++#define BT848_ADC_AGC_EN (1<<4) ++#define BT848_ADC_CLK_SLEEP (1<<3) ++#define BT848_ADC_Y_SLEEP (1<<2) ++#define BT848_ADC_C_SLEEP (1<<1) ++#define BT848_ADC_CRUSH (1<<0) ++ ++#define BT848_WC_UP 0x044 ++#define BT848_WC_DOWN 0x078 ++ ++#define BT848_E_VTC 0x06C ++#define BT848_O_VTC 0x0EC ++#define BT848_VTC_HSFMT (1<<7) ++#define BT848_VTC_VFILT_2TAP 0 ++#define BT848_VTC_VFILT_3TAP 1 ++#define BT848_VTC_VFILT_4TAP 2 ++#define BT848_VTC_VFILT_5TAP 3 ++ ++#define BT848_SRESET 0x07C ++ ++#define BT848_COLOR_FMT 0x0D4 ++#define BT848_COLOR_FMT_O_RGB32 (0<<4) ++#define BT848_COLOR_FMT_O_RGB24 (1<<4) ++#define BT848_COLOR_FMT_O_RGB16 (2<<4) ++#define BT848_COLOR_FMT_O_RGB15 (3<<4) ++#define BT848_COLOR_FMT_O_YUY2 (4<<4) ++#define BT848_COLOR_FMT_O_BtYUV (5<<4) ++#define BT848_COLOR_FMT_O_Y8 (6<<4) ++#define BT848_COLOR_FMT_O_RGB8 (7<<4) ++#define BT848_COLOR_FMT_O_YCrCb422 (8<<4) ++#define BT848_COLOR_FMT_O_YCrCb411 (9<<4) ++#define BT848_COLOR_FMT_O_RAW (14<<4) ++#define BT848_COLOR_FMT_E_RGB32 0 ++#define BT848_COLOR_FMT_E_RGB24 1 ++#define BT848_COLOR_FMT_E_RGB16 2 ++#define BT848_COLOR_FMT_E_RGB15 3 ++#define BT848_COLOR_FMT_E_YUY2 4 ++#define BT848_COLOR_FMT_E_BtYUV 5 ++#define BT848_COLOR_FMT_E_Y8 6 ++#define BT848_COLOR_FMT_E_RGB8 7 ++#define BT848_COLOR_FMT_E_YCrCb422 8 ++#define BT848_COLOR_FMT_E_YCrCb411 9 ++#define BT848_COLOR_FMT_E_RAW 14 ++ ++#define BT848_COLOR_FMT_RGB32 0x00 ++#define BT848_COLOR_FMT_RGB24 0x11 ++#define BT848_COLOR_FMT_RGB16 0x22 ++#define BT848_COLOR_FMT_RGB15 0x33 ++#define BT848_COLOR_FMT_YUY2 0x44 ++#define BT848_COLOR_FMT_BtYUV 0x55 ++#define BT848_COLOR_FMT_Y8 0x66 ++#define BT848_COLOR_FMT_RGB8 0x77 ++#define BT848_COLOR_FMT_YCrCb422 0x88 ++#define BT848_COLOR_FMT_YCrCb411 0x99 ++#define BT848_COLOR_FMT_RAW 0xee ++ ++#define BT848_VTOTAL_LO 0xB0 ++#define BT848_VTOTAL_HI 0xB4 ++ ++#define BT848_COLOR_CTL 0x0D8 ++#define BT848_COLOR_CTL_EXT_FRMRATE (1<<7) ++#define BT848_COLOR_CTL_COLOR_BARS (1<<6) ++#define BT848_COLOR_CTL_RGB_DED (1<<5) ++#define BT848_COLOR_CTL_GAMMA (1<<4) ++#define BT848_COLOR_CTL_WSWAP_ODD (1<<3) ++#define BT848_COLOR_CTL_WSWAP_EVEN (1<<2) ++#define BT848_COLOR_CTL_BSWAP_ODD (1<<1) ++#define BT848_COLOR_CTL_BSWAP_EVEN (1<<0) ++ ++#define BT848_CAP_CTL 0x0DC ++#define BT848_CAP_CTL_DITH_FRAME (1<<4) ++#define BT848_CAP_CTL_CAPTURE_VBI_ODD (1<<3) ++#define BT848_CAP_CTL_CAPTURE_VBI_EVEN (1<<2) ++#define BT848_CAP_CTL_CAPTURE_ODD (1<<1) ++#define BT848_CAP_CTL_CAPTURE_EVEN (1<<0) ++ ++#define BT848_VBI_PACK_SIZE 0x0E0 ++ ++#define BT848_VBI_PACK_DEL 0x0E4 ++#define BT848_VBI_PACK_DEL_VBI_HDELAY 0xfc ++#define BT848_VBI_PACK_DEL_EXT_FRAME 2 ++#define BT848_VBI_PACK_DEL_VBI_PKT_HI 1 ++ ++ ++#define BT848_INT_STAT 0x100 ++#define BT848_INT_MASK 0x104 ++ ++#define BT848_INT_ETBF (1<<23) ++ ++#define BT848_INT_RISCS (0xf<<28) ++#define BT848_INT_RISC_EN (1<<27) ++#define BT848_INT_RACK (1<<25) ++#define BT848_INT_FIELD (1<<24) ++#define BT848_INT_SCERR (1<<19) ++#define BT848_INT_OCERR (1<<18) ++#define BT848_INT_PABORT (1<<17) ++#define BT848_INT_RIPERR (1<<16) ++#define BT848_INT_PPERR (1<<15) ++#define BT848_INT_FDSR (1<<14) ++#define BT848_INT_FTRGT (1<<13) ++#define BT848_INT_FBUS (1<<12) ++#define BT848_INT_RISCI (1<<11) ++#define BT848_INT_GPINT (1<<9) ++#define BT848_INT_I2CDONE (1<<8) ++#define BT848_INT_VPRES (1<<5) ++#define BT848_INT_HLOCK (1<<4) ++#define BT848_INT_OFLOW (1<<3) ++#define BT848_INT_HSYNC (1<<2) ++#define BT848_INT_VSYNC (1<<1) ++#define BT848_INT_FMTCHG (1<<0) ++ ++ ++#define BT848_GPIO_DMA_CTL 0x10C ++#define BT848_GPIO_DMA_CTL_GPINTC (1<<15) ++#define BT848_GPIO_DMA_CTL_GPINTI (1<<14) ++#define BT848_GPIO_DMA_CTL_GPWEC (1<<13) ++#define BT848_GPIO_DMA_CTL_GPIOMODE (3<<11) ++#define BT848_GPIO_DMA_CTL_GPCLKMODE (1<<10) ++#define BT848_GPIO_DMA_CTL_PLTP23_4 (0<<6) ++#define BT848_GPIO_DMA_CTL_PLTP23_8 (1<<6) ++#define BT848_GPIO_DMA_CTL_PLTP23_16 (2<<6) ++#define BT848_GPIO_DMA_CTL_PLTP23_32 (3<<6) ++#define BT848_GPIO_DMA_CTL_PLTP1_4 (0<<4) ++#define BT848_GPIO_DMA_CTL_PLTP1_8 (1<<4) ++#define BT848_GPIO_DMA_CTL_PLTP1_16 (2<<4) ++#define BT848_GPIO_DMA_CTL_PLTP1_32 (3<<4) ++#define BT848_GPIO_DMA_CTL_PKTP_4 (0<<2) ++#define BT848_GPIO_DMA_CTL_PKTP_8 (1<<2) ++#define BT848_GPIO_DMA_CTL_PKTP_16 (2<<2) ++#define BT848_GPIO_DMA_CTL_PKTP_32 (3<<2) ++#define BT848_GPIO_DMA_CTL_RISC_ENABLE (1<<1) ++#define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0) ++ ++#define BT848_I2C 0x110 ++#define BT878_I2C_MODE (1<<7) ++#define BT878_I2C_RATE (1<<6) ++#define BT878_I2C_NOSTOP (1<<5) ++#define BT878_I2C_NOSTART (1<<4) ++#define BT848_I2C_DIV (0xf<<4) ++#define BT848_I2C_SYNC (1<<3) ++#define BT848_I2C_W3B (1<<2) ++#define BT848_I2C_SCL (1<<1) ++#define BT848_I2C_SDA (1<<0) ++ ++#define BT848_RISC_STRT_ADD 0x114 ++#define BT848_GPIO_OUT_EN 0x118 ++#define BT848_GPIO_REG_INP 0x11C ++#define BT848_RISC_COUNT 0x120 ++#define BT848_GPIO_DATA 0x200 ++ ++ ++/* Bt848 RISC commands */ ++ ++/* only for the SYNC RISC command */ ++#define BT848_FIFO_STATUS_FM1 0x06 ++#define BT848_FIFO_STATUS_FM3 0x0e ++#define BT848_FIFO_STATUS_SOL 0x02 ++#define BT848_FIFO_STATUS_EOL4 0x01 ++#define BT848_FIFO_STATUS_EOL3 0x0d ++#define BT848_FIFO_STATUS_EOL2 0x09 ++#define BT848_FIFO_STATUS_EOL1 0x05 ++#define BT848_FIFO_STATUS_VRE 0x04 ++#define BT848_FIFO_STATUS_VRO 0x0c ++#define BT848_FIFO_STATUS_PXV 0x00 ++ ++#define BT848_RISC_RESYNC (1<<15) ++ ++/* WRITE and SKIP */ ++/* disable which bytes of each DWORD */ ++#define BT848_RISC_BYTE0 (1U<<12) ++#define BT848_RISC_BYTE1 (1U<<13) ++#define BT848_RISC_BYTE2 (1U<<14) ++#define BT848_RISC_BYTE3 (1U<<15) ++#define BT848_RISC_BYTE_ALL (0x0fU<<12) ++#define BT848_RISC_BYTE_NONE 0 ++/* cause RISCI */ ++#define BT848_RISC_IRQ (1U<<24) ++/* RISC command is last one in this line */ ++#define BT848_RISC_EOL (1U<<26) ++/* RISC command is first one in this line */ ++#define BT848_RISC_SOL (1U<<27) ++ ++#define BT848_RISC_WRITE (0x01U<<28) ++#define BT848_RISC_SKIP (0x02U<<28) ++#define BT848_RISC_WRITEC (0x05U<<28) ++#define BT848_RISC_JUMP (0x07U<<28) ++#define BT848_RISC_SYNC (0x08U<<28) ++ ++#define BT848_RISC_WRITE123 (0x09U<<28) ++#define BT848_RISC_SKIP123 (0x0aU<<28) ++#define BT848_RISC_WRITE1S23 (0x0bU<<28) ++ ++ ++/* Bt848A and higher only !! */ ++#define BT848_TGLB 0x080 ++#define BT848_TGCTRL 0x084 ++#define BT848_FCAP 0x0E8 ++#define BT848_PLL_F_LO 0x0F0 ++#define BT848_PLL_F_HI 0x0F4 ++ ++#define BT848_PLL_XCI 0x0F8 ++#define BT848_PLL_X (1<<7) ++#define BT848_PLL_C (1<<6) ++ ++#define BT848_DVSIF 0x0FC ++ ++/* Bt878 register */ ++ ++#define BT878_DEVCTRL 0x40 ++#define BT878_EN_TBFX 0x02 ++#define BT878_EN_VSFX 0x04 ++ ++#endif +diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c +new file mode 100644 +index 0000000..b34fa95 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bt878.c +@@ -0,0 +1,609 @@ ++/* ++ * bt878.c: part of the driver for the Pinnacle PCTV Sat DVB PCI card ++ * ++ * Copyright (C) 2002 Peter Hettkamp ++ * ++ * large parts based on the bttv driver ++ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@metzlerbros.de) ++ * & Marcus Metzler (mocm@metzlerbros.de) ++ * (c) 1999,2000 Gerd Knorr ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "bt878.h" ++#include "dst_priv.h" ++ ++ ++/**************************************/ ++/* Miscellaneous utility definitions */ ++/**************************************/ ++ ++static unsigned int bt878_verbose = 1; ++static unsigned int bt878_debug; ++ ++module_param_named(verbose, bt878_verbose, int, 0444); ++MODULE_PARM_DESC(verbose, ++ "verbose startup messages, default is 1 (yes)"); ++module_param_named(debug, bt878_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off debugging, default is 0 (off)."); ++ ++int bt878_num; ++struct bt878 bt878[BT878_MAX]; ++ ++EXPORT_SYMBOL(bt878_num); ++EXPORT_SYMBOL(bt878); ++ ++#define btwrite(dat,adr) bmtwrite((dat), (bt->bt878_mem+(adr))) ++#define btread(adr) bmtread(bt->bt878_mem+(adr)) ++ ++#define btand(dat,adr) btwrite((dat) & btread(adr), adr) ++#define btor(dat,adr) btwrite((dat) | btread(adr), adr) ++#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) ++ ++#if defined(dprintk) ++#undef dprintk ++#endif ++#define dprintk(fmt, arg...) \ ++ do { \ ++ if (bt878_debug) \ ++ printk(KERN_DEBUG fmt, ##arg); \ ++ } while (0) ++ ++static void bt878_mem_free(struct bt878 *bt) ++{ ++ if (bt->buf_cpu) { ++ pci_free_consistent(bt->dev, bt->buf_size, bt->buf_cpu, ++ bt->buf_dma); ++ bt->buf_cpu = NULL; ++ } ++ ++ if (bt->risc_cpu) { ++ pci_free_consistent(bt->dev, bt->risc_size, bt->risc_cpu, ++ bt->risc_dma); ++ bt->risc_cpu = NULL; ++ } ++} ++ ++static int bt878_mem_alloc(struct bt878 *bt) ++{ ++ if (!bt->buf_cpu) { ++ bt->buf_size = 128 * 1024; ++ ++ bt->buf_cpu = ++ pci_alloc_consistent(bt->dev, bt->buf_size, ++ &bt->buf_dma); ++ ++ if (!bt->buf_cpu) ++ return -ENOMEM; ++ ++ memset(bt->buf_cpu, 0, bt->buf_size); ++ } ++ ++ if (!bt->risc_cpu) { ++ bt->risc_size = PAGE_SIZE; ++ bt->risc_cpu = ++ pci_alloc_consistent(bt->dev, bt->risc_size, ++ &bt->risc_dma); ++ ++ if (!bt->risc_cpu) { ++ bt878_mem_free(bt); ++ return -ENOMEM; ++ } ++ ++ memset(bt->risc_cpu, 0, bt->risc_size); ++ } ++ ++ return 0; ++} ++ ++/* RISC instructions */ ++#define RISC_WRITE (0x01 << 28) ++#define RISC_JUMP (0x07 << 28) ++#define RISC_SYNC (0x08 << 28) ++ ++/* RISC bits */ ++#define RISC_WR_SOL (1 << 27) ++#define RISC_WR_EOL (1 << 26) ++#define RISC_IRQ (1 << 24) ++#define RISC_STATUS(status) ((((~status) & 0x0F) << 20) | ((status & 0x0F) << 16)) ++#define RISC_SYNC_RESYNC (1 << 15) ++#define RISC_SYNC_FM1 0x06 ++#define RISC_SYNC_VRO 0x0C ++ ++#define RISC_FLUSH() bt->risc_pos = 0 ++#define RISC_INSTR(instr) bt->risc_cpu[bt->risc_pos++] = cpu_to_le32(instr) ++ ++static int bt878_make_risc(struct bt878 *bt) ++{ ++ bt->block_bytes = bt->buf_size >> 4; ++ bt->block_count = 1 << 4; ++ bt->line_bytes = bt->block_bytes; ++ bt->line_count = bt->block_count; ++ ++ while (bt->line_bytes > 4095) { ++ bt->line_bytes >>= 1; ++ bt->line_count <<= 1; ++ } ++ ++ if (bt->line_count > 255) { ++ printk(KERN_ERR "bt878: buffer size error!\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++ ++static void bt878_risc_program(struct bt878 *bt, u32 op_sync_orin) ++{ ++ u32 buf_pos = 0; ++ u32 line; ++ ++ RISC_FLUSH(); ++ RISC_INSTR(RISC_SYNC | RISC_SYNC_FM1 | op_sync_orin); ++ RISC_INSTR(0); ++ ++ dprintk("bt878: risc len lines %u, bytes per line %u\n", ++ bt->line_count, bt->line_bytes); ++ for (line = 0; line < bt->line_count; line++) { ++ // At the beginning of every block we issue an IRQ with previous (finished) block number set ++ if (!(buf_pos % bt->block_bytes)) ++ RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | ++ RISC_IRQ | ++ RISC_STATUS(((buf_pos / ++ bt->block_bytes) + ++ (bt->block_count - ++ 1)) % ++ bt->block_count) | bt-> ++ line_bytes); ++ else ++ RISC_INSTR(RISC_WRITE | RISC_WR_SOL | RISC_WR_EOL | ++ bt->line_bytes); ++ RISC_INSTR(bt->buf_dma + buf_pos); ++ buf_pos += bt->line_bytes; ++ } ++ ++ RISC_INSTR(RISC_SYNC | op_sync_orin | RISC_SYNC_VRO); ++ RISC_INSTR(0); ++ ++ RISC_INSTR(RISC_JUMP); ++ RISC_INSTR(bt->risc_dma); ++ ++ btwrite((bt->line_count << 16) | bt->line_bytes, BT878_APACK_LEN); ++} ++ ++/*****************************/ ++/* Start/Stop grabbing funcs */ ++/*****************************/ ++ ++void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, ++ u32 irq_err_ignore) ++{ ++ u32 int_mask; ++ ++ dprintk("bt878 debug: bt878_start (ctl=%8.8x)\n", controlreg); ++ /* complete the writing of the risc dma program now we have ++ * the card specifics ++ */ ++ bt878_risc_program(bt, op_sync_orin); ++ controlreg &= ~0x1f; ++ controlreg |= 0x1b; ++ ++ btwrite(bt->risc_dma, BT878_ARISC_START); ++ ++ /* original int mask had : ++ * 6 2 8 4 0 ++ * 1111 1111 1000 0000 0000 ++ * SCERR|OCERR|PABORT|RIPERR|FDSR|FTRGT|FBUS|RISCI ++ * Hacked for DST to: ++ * SCERR | OCERR | FDSR | FTRGT | FBUS | RISCI ++ */ ++ int_mask = BT878_ASCERR | BT878_AOCERR | BT878_APABORT | ++ BT878_ARIPERR | BT878_APPERR | BT878_AFDSR | BT878_AFTRGT | ++ BT878_AFBUS | BT878_ARISCI; ++ ++ ++ /* ignore pesky bits */ ++ int_mask &= ~irq_err_ignore; ++ ++ btwrite(int_mask, BT878_AINT_MASK); ++ btwrite(controlreg, BT878_AGPIO_DMA_CTL); ++} ++ ++void bt878_stop(struct bt878 *bt) ++{ ++ u32 stat; ++ int i = 0; ++ ++ dprintk("bt878 debug: bt878_stop\n"); ++ ++ btwrite(0, BT878_AINT_MASK); ++ btand(~0x13, BT878_AGPIO_DMA_CTL); ++ ++ do { ++ stat = btread(BT878_AINT_STAT); ++ if (!(stat & BT878_ARISC_EN)) ++ break; ++ i++; ++ } while (i < 500); ++ ++ dprintk("bt878(%d) debug: bt878_stop, i=%d, stat=0x%8.8x\n", ++ bt->nr, i, stat); ++} ++ ++EXPORT_SYMBOL(bt878_start); ++EXPORT_SYMBOL(bt878_stop); ++ ++/*****************************/ ++/* Interrupt service routine */ ++/*****************************/ ++ ++static irqreturn_t bt878_irq(int irq, void *dev_id) ++{ ++ u32 stat, astat, mask; ++ int count; ++ struct bt878 *bt; ++ ++ bt = (struct bt878 *) dev_id; ++ ++ count = 0; ++ while (1) { ++ stat = btread(BT878_AINT_STAT); ++ mask = btread(BT878_AINT_MASK); ++ if (!(astat = (stat & mask))) ++ return IRQ_NONE; /* this interrupt is not for me */ ++/* dprintk("bt878(%d) debug: irq count %d, stat 0x%8.8x, mask 0x%8.8x\n",bt->nr,count,stat,mask); */ ++ btwrite(astat, BT878_AINT_STAT); /* try to clear interrupt condition */ ++ ++ ++ if (astat & (BT878_ASCERR | BT878_AOCERR)) { ++ if (bt878_verbose) { ++ printk(KERN_INFO ++ "bt878(%d): irq%s%s risc_pc=%08x\n", ++ bt->nr, ++ (astat & BT878_ASCERR) ? " SCERR" : ++ "", ++ (astat & BT878_AOCERR) ? " OCERR" : ++ "", btread(BT878_ARISC_PC)); ++ } ++ } ++ if (astat & (BT878_APABORT | BT878_ARIPERR | BT878_APPERR)) { ++ if (bt878_verbose) { ++ printk(KERN_INFO ++ "bt878(%d): irq%s%s%s risc_pc=%08x\n", ++ bt->nr, ++ (astat & BT878_APABORT) ? " PABORT" : ++ "", ++ (astat & BT878_ARIPERR) ? " RIPERR" : ++ "", ++ (astat & BT878_APPERR) ? " PPERR" : ++ "", btread(BT878_ARISC_PC)); ++ } ++ } ++ if (astat & (BT878_AFDSR | BT878_AFTRGT | BT878_AFBUS)) { ++ if (bt878_verbose) { ++ printk(KERN_INFO ++ "bt878(%d): irq%s%s%s risc_pc=%08x\n", ++ bt->nr, ++ (astat & BT878_AFDSR) ? " FDSR" : "", ++ (astat & BT878_AFTRGT) ? " FTRGT" : ++ "", ++ (astat & BT878_AFBUS) ? " FBUS" : "", ++ btread(BT878_ARISC_PC)); ++ } ++ } ++ if (astat & BT878_ARISCI) { ++ bt->finished_block = (stat & BT878_ARISCS) >> 28; ++ tasklet_schedule(&bt->tasklet); ++ break; ++ } ++ count++; ++ if (count > 20) { ++ btwrite(0, BT878_AINT_MASK); ++ printk(KERN_ERR ++ "bt878(%d): IRQ lockup, cleared int mask\n", ++ bt->nr); ++ break; ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++int ++bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp) ++{ ++ int retval; ++ ++ retval = 0; ++ if (mutex_lock_interruptible(&bt->gpio_lock)) ++ return -ERESTARTSYS; ++ /* special gpio signal */ ++ switch (cmd) { ++ case DST_IG_ENABLE: ++ // dprintk("dvb_bt8xx: dst enable mask 0x%02x enb 0x%02x \n", mp->dstg.enb.mask, mp->dstg.enb.enable); ++ retval = bttv_gpio_enable(bt->bttv_nr, ++ mp->enb.mask, ++ mp->enb.enable); ++ break; ++ case DST_IG_WRITE: ++ // dprintk("dvb_bt8xx: dst write gpio mask 0x%02x out 0x%02x\n", mp->dstg.outp.mask, mp->dstg.outp.highvals); ++ retval = bttv_write_gpio(bt->bttv_nr, ++ mp->outp.mask, ++ mp->outp.highvals); ++ ++ break; ++ case DST_IG_READ: ++ /* read */ ++ retval = bttv_read_gpio(bt->bttv_nr, &mp->rd.value); ++ // dprintk("dvb_bt8xx: dst read gpio 0x%02x\n", (unsigned)mp->dstg.rd.value); ++ break; ++ case DST_IG_TS: ++ /* Set packet size */ ++ bt->TS_Size = mp->psize; ++ break; ++ ++ default: ++ retval = -EINVAL; ++ break; ++ } ++ mutex_unlock(&bt->gpio_lock); ++ return retval; ++} ++ ++EXPORT_SYMBOL(bt878_device_control); ++ ++#define BROOKTREE_878_DEVICE(vend, dev, name) \ ++ { \ ++ .vendor = PCI_VENDOR_ID_BROOKTREE, \ ++ .device = PCI_DEVICE_ID_BROOKTREE_878, \ ++ .subvendor = (vend), .subdevice = (dev), \ ++ .driver_data = (unsigned long) name \ ++ } ++ ++static struct pci_device_id bt878_pci_tbl[] __devinitdata = { ++ BROOKTREE_878_DEVICE(0x0071, 0x0101, "Nebula Electronics DigiTV"), ++ BROOKTREE_878_DEVICE(0x1461, 0x0761, "AverMedia AverTV DVB-T 761"), ++ BROOKTREE_878_DEVICE(0x11bd, 0x001c, "Pinnacle PCTV Sat"), ++ BROOKTREE_878_DEVICE(0x11bd, 0x0026, "Pinnacle PCTV SAT CI"), ++ BROOKTREE_878_DEVICE(0x1822, 0x0001, "Twinhan VisionPlus DVB"), ++ BROOKTREE_878_DEVICE(0x270f, 0xfc00, ++ "ChainTech digitop DST-1000 DVB-S"), ++ BROOKTREE_878_DEVICE(0x1461, 0x0771, "AVermedia AverTV DVB-T 771"), ++ BROOKTREE_878_DEVICE(0x18ac, 0xdb10, "DViCO FusionHDTV DVB-T Lite"), ++ BROOKTREE_878_DEVICE(0x18ac, 0xdb11, "Ultraview DVB-T Lite"), ++ BROOKTREE_878_DEVICE(0x18ac, 0xd500, "DViCO FusionHDTV 5 Lite"), ++ BROOKTREE_878_DEVICE(0x7063, 0x2000, "pcHDTV HD-2000 TV"), ++ BROOKTREE_878_DEVICE(0x1822, 0x0026, "DNTV Live! Mini"), ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(pci, bt878_pci_tbl); ++ ++static const char * __devinit card_name(const struct pci_device_id *id) ++{ ++ return id->driver_data ? (const char *)id->driver_data : "Unknown"; ++} ++ ++/***********************/ ++/* PCI device handling */ ++/***********************/ ++ ++static int __devinit bt878_probe(struct pci_dev *dev, ++ const struct pci_device_id *pci_id) ++{ ++ int result = 0; ++ unsigned char lat; ++ struct bt878 *bt; ++#if defined(__powerpc__) ++ unsigned int cmd; ++#endif ++ unsigned int cardid; ++ ++ printk(KERN_INFO "bt878: Bt878 AUDIO function found (%d).\n", ++ bt878_num); ++ if (bt878_num >= BT878_MAX) { ++ printk(KERN_ERR "bt878: Too many devices inserted\n"); ++ result = -ENOMEM; ++ goto fail0; ++ } ++ if (pci_enable_device(dev)) ++ return -EIO; ++ ++ cardid = dev->subsystem_device << 16; ++ cardid |= dev->subsystem_vendor; ++ ++ printk(KERN_INFO "%s: card id=[0x%x],[ %s ] has DVB functions.\n", ++ __func__, cardid, card_name(pci_id)); ++ ++ bt = &bt878[bt878_num]; ++ bt->dev = dev; ++ bt->nr = bt878_num; ++ bt->shutdown = 0; ++ ++ bt->id = dev->device; ++ bt->irq = dev->irq; ++ bt->bt878_adr = pci_resource_start(dev, 0); ++ if (!request_mem_region(pci_resource_start(dev, 0), ++ pci_resource_len(dev, 0), "bt878")) { ++ result = -EBUSY; ++ goto fail0; ++ } ++ ++ bt->revision = dev->revision; ++ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); ++ ++ ++ printk(KERN_INFO "bt878(%d): Bt%x (rev %d) at %02x:%02x.%x, ", ++ bt878_num, bt->id, bt->revision, dev->bus->number, ++ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); ++ printk("irq: %d, latency: %d, memory: 0x%lx\n", ++ bt->irq, lat, bt->bt878_adr); ++ ++ ++#if defined(__powerpc__) ++ /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ ++ /* response on cards with no firmware is not enabled by OF */ ++ pci_read_config_dword(dev, PCI_COMMAND, &cmd); ++ cmd = (cmd | PCI_COMMAND_MEMORY); ++ pci_write_config_dword(dev, PCI_COMMAND, cmd); ++#endif ++ ++#ifdef __sparc__ ++ bt->bt878_mem = (unsigned char *) bt->bt878_adr; ++#else ++ bt->bt878_mem = ioremap(bt->bt878_adr, 0x1000); ++#endif ++ ++ /* clear interrupt mask */ ++ btwrite(0, BT848_INT_MASK); ++ ++ result = request_irq(bt->irq, bt878_irq, ++ IRQF_SHARED | IRQF_DISABLED, "bt878", ++ (void *) bt); ++ if (result == -EINVAL) { ++ printk(KERN_ERR "bt878(%d): Bad irq number or handler\n", ++ bt878_num); ++ goto fail1; ++ } ++ if (result == -EBUSY) { ++ printk(KERN_ERR ++ "bt878(%d): IRQ %d busy, change your PnP config in BIOS\n", ++ bt878_num, bt->irq); ++ goto fail1; ++ } ++ if (result < 0) ++ goto fail1; ++ ++ pci_set_master(dev); ++ pci_set_drvdata(dev, bt); ++ ++ if ((result = bt878_mem_alloc(bt))) { ++ printk(KERN_ERR "bt878: failed to allocate memory!\n"); ++ goto fail2; ++ } ++ ++ bt878_make_risc(bt); ++ btwrite(0, BT878_AINT_MASK); ++ bt878_num++; ++ ++ return 0; ++ ++ fail2: ++ free_irq(bt->irq, bt); ++ fail1: ++ release_mem_region(pci_resource_start(bt->dev, 0), ++ pci_resource_len(bt->dev, 0)); ++ fail0: ++ pci_disable_device(dev); ++ return result; ++} ++ ++static void __devexit bt878_remove(struct pci_dev *pci_dev) ++{ ++ u8 command; ++ struct bt878 *bt = pci_get_drvdata(pci_dev); ++ ++ if (bt878_verbose) ++ printk(KERN_INFO "bt878(%d): unloading\n", bt->nr); ++ ++ /* turn off all capturing, DMA and IRQs */ ++ btand(~0x13, BT878_AGPIO_DMA_CTL); ++ ++ /* first disable interrupts before unmapping the memory! */ ++ btwrite(0, BT878_AINT_MASK); ++ btwrite(~0U, BT878_AINT_STAT); ++ ++ /* disable PCI bus-mastering */ ++ pci_read_config_byte(bt->dev, PCI_COMMAND, &command); ++ /* Should this be &=~ ?? */ ++ command &= ~PCI_COMMAND_MASTER; ++ pci_write_config_byte(bt->dev, PCI_COMMAND, command); ++ ++ free_irq(bt->irq, bt); ++ printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt->bt878_mem); ++ if (bt->bt878_mem) ++ iounmap(bt->bt878_mem); ++ ++ release_mem_region(pci_resource_start(bt->dev, 0), ++ pci_resource_len(bt->dev, 0)); ++ /* wake up any waiting processes ++ because shutdown flag is set, no new processes (in this queue) ++ are expected ++ */ ++ bt->shutdown = 1; ++ bt878_mem_free(bt); ++ ++ pci_set_drvdata(pci_dev, NULL); ++ pci_disable_device(pci_dev); ++ return; ++} ++ ++static struct pci_driver bt878_pci_driver = { ++ .name = "bt878", ++ .id_table = bt878_pci_tbl, ++ .probe = bt878_probe, ++ .remove = __devexit_p(bt878_remove), ++}; ++ ++/*******************************/ ++/* Module management functions */ ++/*******************************/ ++ ++static int __init bt878_init_module(void) ++{ ++ bt878_num = 0; ++ ++ printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n", ++ (BT878_VERSION_CODE >> 16) & 0xff, ++ (BT878_VERSION_CODE >> 8) & 0xff, ++ BT878_VERSION_CODE & 0xff); ++ ++ return pci_register_driver(&bt878_pci_driver); ++} ++ ++static void __exit bt878_cleanup_module(void) ++{ ++ pci_unregister_driver(&bt878_pci_driver); ++} ++ ++module_init(bt878_init_module); ++module_exit(bt878_cleanup_module); ++ ++MODULE_LICENSE("GPL"); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bt878.h b/drivers/media/pci/bt8xx/bt878.h +new file mode 100644 +index 0000000..d19b592 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bt878.h +@@ -0,0 +1,159 @@ ++/* ++ bt878.h - Bt878 audio module (register offsets) ++ ++ Copyright (C) 2002 Peter Hettkamp ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef _BT878_H_ ++#define _BT878_H_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "bt848.h" ++#include "bttv.h" ++ ++#define BT878_VERSION_CODE 0x000000 ++ ++#define BT878_AINT_STAT 0x100 ++#define BT878_ARISCS (0xf<<28) ++#define BT878_ARISC_EN (1<<27) ++#define BT878_ASCERR (1<<19) ++#define BT878_AOCERR (1<<18) ++#define BT878_APABORT (1<<17) ++#define BT878_ARIPERR (1<<16) ++#define BT878_APPERR (1<<15) ++#define BT878_AFDSR (1<<14) ++#define BT878_AFTRGT (1<<13) ++#define BT878_AFBUS (1<<12) ++#define BT878_ARISCI (1<<11) ++#define BT878_AOFLOW (1<<3) ++ ++#define BT878_AINT_MASK 0x104 ++ ++#define BT878_AGPIO_DMA_CTL 0x10c ++#define BT878_A_GAIN (0xf<<28) ++#define BT878_A_G2X (1<<27) ++#define BT878_A_PWRDN (1<<26) ++#define BT878_A_SEL (3<<24) ++#define BT878_DA_SCE (1<<23) ++#define BT878_DA_LRI (1<<22) ++#define BT878_DA_MLB (1<<21) ++#define BT878_DA_LRD (0x1f<<16) ++#define BT878_DA_DPM (1<<15) ++#define BT878_DA_SBR (1<<14) ++#define BT878_DA_ES2 (1<<13) ++#define BT878_DA_LMT (1<<12) ++#define BT878_DA_SDR (0xf<<8) ++#define BT878_DA_IOM (3<<6) ++#define BT878_DA_APP (1<<5) ++#define BT878_ACAP_EN (1<<4) ++#define BT878_PKTP (3<<2) ++#define BT878_RISC_EN (1<<1) ++#define BT878_FIFO_EN 1 ++ ++#define BT878_APACK_LEN 0x110 ++#define BT878_AFP_LEN (0xff<<16) ++#define BT878_ALP_LEN 0xfff ++ ++#define BT878_ARISC_START 0x114 ++ ++#define BT878_ARISC_PC 0x120 ++ ++/* BT878 FUNCTION 0 REGISTERS */ ++#define BT878_GPIO_DMA_CTL 0x10c ++ ++/* Interrupt register */ ++#define BT878_INT_STAT 0x100 ++#define BT878_INT_MASK 0x104 ++#define BT878_I2CRACK (1<<25) ++#define BT878_I2CDONE (1<<8) ++ ++#define BT878_MAX 4 ++ ++#define BT878_RISC_SYNC_MASK (1 << 15) ++ ++ ++#define BTTV_BOARD_UNKNOWN 0x00 ++#define BTTV_BOARD_PINNACLESAT 0x5e ++#define BTTV_BOARD_NEBULA_DIGITV 0x68 ++#define BTTV_BOARD_PC_HDTV 0x70 ++#define BTTV_BOARD_TWINHAN_DST 0x71 ++#define BTTV_BOARD_AVDVBT_771 0x7b ++#define BTTV_BOARD_AVDVBT_761 0x7c ++#define BTTV_BOARD_DVICO_DVBT_LITE 0x80 ++#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87 ++ ++extern int bt878_num; ++ ++struct bt878 { ++ struct mutex gpio_lock; ++ unsigned int nr; ++ unsigned int bttv_nr; ++ struct i2c_adapter *adapter; ++ struct pci_dev *dev; ++ unsigned int id; ++ unsigned int TS_Size; ++ unsigned char revision; ++ unsigned int irq; ++ unsigned long bt878_adr; ++ volatile void __iomem *bt878_mem; /* function 1 */ ++ ++ volatile u32 finished_block; ++ volatile u32 last_block; ++ u32 block_count; ++ u32 block_bytes; ++ u32 line_bytes; ++ u32 line_count; ++ ++ u32 buf_size; ++ u8 *buf_cpu; ++ dma_addr_t buf_dma; ++ ++ u32 risc_size; ++ __le32 *risc_cpu; ++ dma_addr_t risc_dma; ++ u32 risc_pos; ++ ++ struct tasklet_struct tasklet; ++ int shutdown; ++}; ++ ++extern struct bt878 bt878[BT878_MAX]; ++ ++void bt878_start(struct bt878 *bt, u32 controlreg, u32 op_sync_orin, ++ u32 irq_err_ignore); ++void bt878_stop(struct bt878 *bt); ++ ++#if defined(__powerpc__) /* big-endian */ ++static inline void io_st_le32(volatile unsigned __iomem *addr, unsigned val) ++{ ++ st_le32(addr, val); ++ eieio(); ++} ++ ++#define bmtwrite(dat,adr) io_st_le32((adr),(dat)) ++#define bmtread(adr) ld_le32((adr)) ++#else ++#define bmtwrite(dat,adr) writel((dat), (adr)) ++#define bmtread(adr) readl(adr) ++#endif ++ ++#endif +diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.c b/drivers/media/pci/bt8xx/bttv-audio-hook.c +new file mode 100644 +index 0000000..2364d16 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-audio-hook.c +@@ -0,0 +1,382 @@ ++/* ++ * Handlers for board audio hooks, splitted from bttv-cards ++ * ++ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org) ++ * This code is placed under the terms of the GNU General Public License ++ */ ++ ++#include "bttv-audio-hook.h" ++ ++#include ++ ++/* ----------------------------------------------------------------------- */ ++/* winview */ ++ ++void winview_volume(struct bttv *btv, __u16 volume) ++{ ++ /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ ++ int bits_out, loops, vol, data; ++ ++ /* 32 levels logarithmic */ ++ vol = 32 - ((volume>>11)); ++ /* units */ ++ bits_out = (PT2254_DBS_IN_2>>(vol%5)); ++ /* tens */ ++ bits_out |= (PT2254_DBS_IN_10>>(vol/5)); ++ bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL; ++ data = gpio_read(); ++ data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| ++ WINVIEW_PT2254_STROBE); ++ for (loops = 17; loops >= 0 ; loops--) { ++ if (bits_out & (1<audmode & V4L2_TUNER_MODE_LANG1) ++ con = 0x000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) ++ con = 0x300; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) ++ con = 0x200; ++/* if (t->audmode & V4L2_TUNER_MODE_MONO) ++ * con = 0x100; */ ++ gpio_bits(0x300, con); ++ } else { ++ t->audmode = V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned int val, con; ++ ++ if (btv->radio_user) ++ return; ++ ++ val = gpio_read(); ++ if (set) { ++ con = 0x000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) { ++ if (t->audmode & V4L2_TUNER_MODE_LANG1) { ++ /* LANG1 + LANG2 */ ++ con = 0x100; ++ } ++ else { ++ /* LANG2 */ ++ con = 0x300; ++ } ++ } ++ if (con != (val & 0x300)) { ++ gpio_bits(0x300, con); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"gvbctv5pci"); ++ } ++ } else { ++ switch (val & 0x70) { ++ case 0x10: ++ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ break; ++ case 0x30: ++ t->rxsubchans = V4L2_TUNER_SUB_LANG2; ++ break; ++ case 0x50: ++ t->rxsubchans = V4L2_TUNER_SUB_LANG1; ++ break; ++ case 0x60: ++ t->rxsubchans = V4L2_TUNER_SUB_STEREO; ++ break; ++ case 0x70: ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ break; ++ default: ++ t->rxsubchans = V4L2_TUNER_SUB_MONO | ++ V4L2_TUNER_SUB_STEREO | ++ V4L2_TUNER_SUB_LANG1 | ++ V4L2_TUNER_SUB_LANG2; ++ } ++ t->audmode = V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++/* ++ * Mario Medina Nussbaum ++ * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo, ++ * 0xdde enables mono and 0xccd enables sap ++ * ++ * Petr Vandrovec ++ * P.S.: At least mask in line above is wrong - GPIO pins 3,2 select ++ * input/output sound connection, so both must be set for output mode. ++ * ++ * Looks like it's needed only for the "tvphone", the "tvphone 98" ++ * handles this with a tda9840 ++ * ++ */ ++ ++void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ int val = 0; ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ ++ val = 0x02; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) ++ val = 0x01; ++ if (val) { ++ gpio_bits(0x03,val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"avermedia"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1; ++ return; ++ } ++} ++ ++ ++void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ int val = 0; ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ ++ val = 0x01; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* STEREO */ ++ val = 0x02; ++ btaor(val, ~0x03, BT848_GPIO_DATA); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"avermedia"); ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ return; ++ } ++} ++ ++/* Lifetec 9415 handling */ ++ ++void lt9415_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ int val = 0; ++ ++ if (gpio_read() & 0x4000) { ++ t->audmode = V4L2_TUNER_MODE_MONO; ++ return; ++ } ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* A2 SAP */ ++ val = 0x0080; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* A2 stereo */ ++ val = 0x0880; ++ if ((t->audmode & V4L2_TUNER_MODE_LANG1) || ++ (t->audmode & V4L2_TUNER_MODE_MONO)) ++ val = 0; ++ gpio_bits(0x0880, val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"lt9415"); ++ } else { ++ /* autodetect doesn't work with this card :-( */ ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ return; ++ } ++} ++ ++/* TDA9821 on TerraTV+ Bt848, Bt878 */ ++void terratv_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned int con = 0; ++ ++ if (set) { ++ gpio_inout(0x180000,0x180000); ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) ++ con = 0x080000; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) ++ con = 0x180000; ++ gpio_bits(0x180000, con); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"terratv"); ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++ ++void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned long val = 0; ++ ++ if (set) { ++ /*btor (0xc32000, BT848_GPIO_OUT_EN);*/ ++ if (t->audmode & V4L2_TUNER_MODE_MONO) /* Mono */ ++ val = 0x420000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG1) /* Mono */ ++ val = 0x420000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) /* SAP */ ++ val = 0x410000; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) /* Stereo */ ++ val = 0x020000; ++ if (val) { ++ gpio_bits(0x430000, val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"winfast2000"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++/* ++ * Dariusz Kowalewski ++ * sound control for Prolink PV-BT878P+9B (PixelView PlayTV Pro FM+NICAM ++ * revision 9B has on-board TDA9874A sound decoder). ++ * ++ * Note: There are card variants without tda9874a. Forcing the "stereo sound route" ++ * will mute this cards. ++ */ ++void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned int val = 0; ++ ++ if (btv->radio_user) ++ return; ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_MONO) { ++ val = 0x01; ++ } ++ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2)) ++ || (t->audmode & V4L2_TUNER_MODE_STEREO)) { ++ val = 0x02; ++ } ++ if (val) { ++ gpio_bits(0x03,val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"pvbt878p9b"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++/* ++ * Dariusz Kowalewski ++ * sound control for FlyVideo 2000S (with tda9874 decoder) ++ * based on pvbt878p9b_audio() - this is not tested, please fix!!! ++ */ ++void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned int val = 0xffff; ++ ++ if (btv->radio_user) ++ return; ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_MONO) { ++ val = 0x0000; ++ } ++ if ((t->audmode & (V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2)) ++ || (t->audmode & V4L2_TUNER_MODE_STEREO)) { ++ val = 0x1080; /*-dk-???: 0x0880, 0x0080, 0x1800 ... */ ++ } ++ if (val != 0xffff) { ++ gpio_bits(0x1800, val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"fv2000s"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++/* ++ * sound control for Canopus WinDVR PCI ++ * Masaki Suzuki ++ */ ++void windvr_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned long val = 0; ++ ++ if (set) { ++ if (t->audmode & V4L2_TUNER_MODE_MONO) ++ val = 0x040000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG1) ++ val = 0; ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) ++ val = 0x100000; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) ++ val = 0; ++ if (val) { ++ gpio_bits(0x140000, val); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"windvr"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} ++ ++/* ++ * sound control for AD-TVK503 ++ * Hiroshi Takekawa ++ */ ++void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *t, int set) ++{ ++ unsigned int con = 0xffffff; ++ ++ /* btaor(0x1e0000, ~0x1e0000, BT848_GPIO_OUT_EN); */ ++ ++ if (set) { ++ /* btor(***, BT848_GPIO_OUT_EN); */ ++ if (t->audmode & V4L2_TUNER_MODE_LANG1) ++ con = 0x00000000; ++ if (t->audmode & V4L2_TUNER_MODE_LANG2) ++ con = 0x00180000; ++ if (t->audmode & V4L2_TUNER_MODE_STEREO) ++ con = 0x00000000; ++ if (t->audmode & V4L2_TUNER_MODE_MONO) ++ con = 0x00060000; ++ if (con != 0xffffff) { ++ gpio_bits(0x1e0000,con); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv, "adtvk503"); ++ } ++ } else { ++ t->audmode = V4L2_TUNER_MODE_MONO | V4L2_TUNER_MODE_STEREO | ++ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; ++ } ++} +diff --git a/drivers/media/pci/bt8xx/bttv-audio-hook.h b/drivers/media/pci/bt8xx/bttv-audio-hook.h +new file mode 100644 +index 0000000..159d07a +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-audio-hook.h +@@ -0,0 +1,23 @@ ++/* ++ * Handlers for board audio hooks, splitted from bttv-cards ++ * ++ * Copyright (c) 2006 Mauro Carvalho Chehab (mchehab@infradead.org) ++ * This code is placed under the terms of the GNU General Public License ++ */ ++ ++#include "bttvp.h" ++ ++void winview_volume (struct bttv *btv, __u16 volume); ++ ++void lt9415_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void avermedia_tvphone_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void avermedia_tv_stereo_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void terratv_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void gvbctv3pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void gvbctv5pci_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void winfast2000_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void pvbt878p9b_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void fv2000s_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void windvr_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++void adtvk503_audio(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++ +diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c +new file mode 100644 +index 0000000..b3d0a4d +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-cards.c +@@ -0,0 +1,4896 @@ ++/* ++ ++ bttv-cards.c ++ ++ this file has configuration informations - card-specific stuff ++ like the big tvcards array for the most part ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ & Marcus Metzler (mocm@thp.uni-koeln.de) ++ (c) 1999-2001 Gerd Knorr ++ ++ 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. ++ ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "bttvp.h" ++#include ++#include ++#include "bttv-audio-hook.h" ++ ++/* fwd decl */ ++static void boot_msp34xx(struct bttv *btv, int pin); ++static void hauppauge_eeprom(struct bttv *btv); ++static void avermedia_eeprom(struct bttv *btv); ++static void osprey_eeprom(struct bttv *btv, const u8 ee[256]); ++static void modtec_eeprom(struct bttv *btv); ++static void init_PXC200(struct bttv *btv); ++static void init_RTV24(struct bttv *btv); ++ ++static void rv605_muxsel(struct bttv *btv, unsigned int input); ++static void eagle_muxsel(struct bttv *btv, unsigned int input); ++static void xguard_muxsel(struct bttv *btv, unsigned int input); ++static void ivc120_muxsel(struct bttv *btv, unsigned int input); ++static void gvc1100_muxsel(struct bttv *btv, unsigned int input); ++ ++static void PXC200_muxsel(struct bttv *btv, unsigned int input); ++ ++static void picolo_tetra_muxsel(struct bttv *btv, unsigned int input); ++static void picolo_tetra_init(struct bttv *btv); ++ ++static void tibetCS16_muxsel(struct bttv *btv, unsigned int input); ++static void tibetCS16_init(struct bttv *btv); ++ ++static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input); ++static void kodicom4400r_init(struct bttv *btv); ++ ++static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input); ++static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input); ++ ++static void geovision_muxsel(struct bttv *btv, unsigned int input); ++ ++static void phytec_muxsel(struct bttv *btv, unsigned int input); ++ ++static void gv800s_muxsel(struct bttv *btv, unsigned int input); ++static void gv800s_init(struct bttv *btv); ++ ++static void td3116_muxsel(struct bttv *btv, unsigned int input); ++ ++static int terratec_active_radio_upgrade(struct bttv *btv); ++static int tea5757_read(struct bttv *btv); ++static int tea5757_write(struct bttv *btv, int value); ++static void identify_by_eeprom(struct bttv *btv, ++ unsigned char eeprom_data[256]); ++static int __devinit pvr_boot(struct bttv *btv); ++ ++/* config variables */ ++static unsigned int triton1; ++static unsigned int vsfx; ++static unsigned int latency = UNSET; ++int no_overlay=-1; ++ ++static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; ++static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; ++static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; ++static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; ++static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; ++static unsigned int audiodev[BTTV_MAX]; ++static unsigned int saa6588[BTTV_MAX]; ++static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL }; ++static unsigned int autoload = UNSET; ++static unsigned int gpiomask = UNSET; ++static unsigned int audioall = UNSET; ++static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET }; ++ ++/* insmod options */ ++module_param(triton1, int, 0444); ++module_param(vsfx, int, 0444); ++module_param(no_overlay, int, 0444); ++module_param(latency, int, 0444); ++module_param(gpiomask, int, 0444); ++module_param(audioall, int, 0444); ++module_param(autoload, int, 0444); ++ ++module_param_array(card, int, NULL, 0444); ++module_param_array(pll, int, NULL, 0444); ++module_param_array(tuner, int, NULL, 0444); ++module_param_array(svhs, int, NULL, 0444); ++module_param_array(remote, int, NULL, 0444); ++module_param_array(audiodev, int, NULL, 0444); ++module_param_array(audiomux, int, NULL, 0444); ++ ++MODULE_PARM_DESC(triton1,"set ETBF pci config bit " ++ "[enable bug compatibility for triton1 + others]"); ++MODULE_PARM_DESC(vsfx,"set VSFX pci config bit " ++ "[yet another chipset flaw workaround]"); ++MODULE_PARM_DESC(latency,"pci latency timer"); ++MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); ++MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); ++MODULE_PARM_DESC(tuner,"specify installed tuner type"); ++MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore"); ++MODULE_PARM_DESC(audiodev, "specify audio device:\n" ++ "\t\t-1 = no audio\n" ++ "\t\t 0 = autodetect (default)\n" ++ "\t\t 1 = msp3400\n" ++ "\t\t 2 = tda7432\n" ++ "\t\t 3 = tvaudio"); ++MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition."); ++MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" ++ " [some VIA/SIS chipsets are known to have problem with overlay]"); ++ ++/* ----------------------------------------------------------------------- */ ++/* list of card IDs for bt878+ cards */ ++ ++static struct CARD { ++ unsigned id; ++ int cardnr; ++ char *name; ++} cards[] __devinitdata = { ++ { 0x13eb0070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV" }, ++ { 0x39000070, BTTV_BOARD_HAUPPAUGE878, "Hauppauge WinTV-D" }, ++ { 0x45000070, BTTV_BOARD_HAUPPAUGEPVR, "Hauppauge WinTV/PVR" }, ++ { 0xff000070, BTTV_BOARD_OSPREY1x0, "Osprey-100" }, ++ { 0xff010070, BTTV_BOARD_OSPREY2x0_SVID,"Osprey-200" }, ++ { 0xff020070, BTTV_BOARD_OSPREY500, "Osprey-500" }, ++ { 0xff030070, BTTV_BOARD_OSPREY2000, "Osprey-2000" }, ++ { 0xff040070, BTTV_BOARD_OSPREY540, "Osprey-540" }, ++ { 0xff070070, BTTV_BOARD_OSPREY440, "Osprey-440" }, ++ ++ { 0x00011002, BTTV_BOARD_ATI_TVWONDER, "ATI TV Wonder" }, ++ { 0x00031002, BTTV_BOARD_ATI_TVWONDERVE,"ATI TV Wonder/VE" }, ++ ++ { 0x6606107d, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" }, ++ { 0x6607107d, BTTV_BOARD_WINFASTVC100, "Leadtek WinFast VC 100" }, ++ { 0x6609107d, BTTV_BOARD_WINFAST2000, "Leadtek TV 2000 XP" }, ++ { 0x263610b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, ++ { 0x264510b4, BTTV_BOARD_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, ++ { 0x402010fc, BTTV_BOARD_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" }, ++ { 0x405010fc, BTTV_BOARD_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" }, ++ { 0x407010fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" }, ++ { 0xd01810fc, BTTV_BOARD_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" }, ++ ++ { 0x001211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" }, ++ /* some cards ship with byteswapped IDs ... */ ++ { 0x1200bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" }, ++ { 0xff00bd11, BTTV_BOARD_PINNACLE, "Pinnacle PCTV [bswap]" }, ++ /* this seems to happen as well ... */ ++ { 0xff1211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" }, ++ ++ { 0x3000121a, BTTV_BOARD_VOODOOTV_200, "3Dfx VoodooTV 200" }, ++ { 0x263710b4, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM" }, ++ { 0x3060121a, BTTV_BOARD_STB2, "3Dfx VoodooTV 100/ STB OEM" }, ++ ++ { 0x3000144f, BTTV_BOARD_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" }, ++ { 0xa005144f, BTTV_BOARD_MAGICTVIEW063, "CPH06X TView99-Card" }, ++ { 0x3002144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" }, ++ { 0x3005144f, BTTV_BOARD_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" }, ++ { 0x5000144f, BTTV_BOARD_MAGICTVIEW061, "Askey CPH050" }, ++ { 0x300014ff, BTTV_BOARD_MAGICTVIEW061, "TView 99 (CPH061)" }, ++ { 0x300214ff, BTTV_BOARD_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" }, ++ ++ { 0x00011461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" }, ++ { 0x00021461, BTTV_BOARD_AVERMEDIA98, "AVermedia TVCapture 98" }, ++ { 0x00031461, BTTV_BOARD_AVPHONE98, "AVerMedia TVPhone98" }, ++ { 0x00041461, BTTV_BOARD_AVERMEDIA98, "AVerMedia TVCapture 98" }, ++ { 0x03001461, BTTV_BOARD_AVERMEDIA98, "VDOMATE TV TUNER CARD" }, ++ ++ { 0x1117153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL B/G)" }, ++ { 0x1118153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL B/G)" }, ++ { 0x1119153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Philips PAL I)" }, ++ { 0x111a153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (Temic PAL I)" }, ++ ++ { 0x1123153b, BTTV_BOARD_TERRATVRADIO, "Terratec TV Radio+" }, ++ { 0x1127153b, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.05)" }, ++ /* clashes with FlyVideo ++ *{ 0x18521852, BTTV_BOARD_TERRATV, "Terratec TV+ (V1.10)" }, */ ++ { 0x1134153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue (LR102)" }, ++ { 0x1135153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* LR102 */ ++ { 0x5018153b, BTTV_BOARD_TERRATVALUE, "Terratec TValue" }, /* ?? */ ++ { 0xff3b153b, BTTV_BOARD_TERRATVALUER, "Terratec TValue Radio" }, /* ?? */ ++ ++ { 0x400015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, ++ { 0x400a15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, ++ { 0x400d15b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, ++ { 0x401015b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, ++ { 0x401615b0, BTTV_BOARD_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" }, ++ ++ { 0x1430aa00, BTTV_BOARD_PV143, "Provideo PV143A" }, ++ { 0x1431aa00, BTTV_BOARD_PV143, "Provideo PV143B" }, ++ { 0x1432aa00, BTTV_BOARD_PV143, "Provideo PV143C" }, ++ { 0x1433aa00, BTTV_BOARD_PV143, "Provideo PV143D" }, ++ { 0x1433aa03, BTTV_BOARD_PV143, "Security Eyes" }, ++ ++ { 0x1460aa00, BTTV_BOARD_PV150, "Provideo PV150A-1" }, ++ { 0x1461aa01, BTTV_BOARD_PV150, "Provideo PV150A-2" }, ++ { 0x1462aa02, BTTV_BOARD_PV150, "Provideo PV150A-3" }, ++ { 0x1463aa03, BTTV_BOARD_PV150, "Provideo PV150A-4" }, ++ ++ { 0x1464aa04, BTTV_BOARD_PV150, "Provideo PV150B-1" }, ++ { 0x1465aa05, BTTV_BOARD_PV150, "Provideo PV150B-2" }, ++ { 0x1466aa06, BTTV_BOARD_PV150, "Provideo PV150B-3" }, ++ { 0x1467aa07, BTTV_BOARD_PV150, "Provideo PV150B-4" }, ++ ++ { 0xa132ff00, BTTV_BOARD_IVC100, "IVC-100" }, ++ { 0xa1550000, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550001, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550002, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550003, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550100, BTTV_BOARD_IVC200, "IVC-200G" }, ++ { 0xa1550101, BTTV_BOARD_IVC200, "IVC-200G" }, ++ { 0xa1550102, BTTV_BOARD_IVC200, "IVC-200G" }, ++ { 0xa1550103, BTTV_BOARD_IVC200, "IVC-200G" }, ++ { 0xa1550800, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550801, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550802, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa1550803, BTTV_BOARD_IVC200, "IVC-200" }, ++ { 0xa182ff00, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff01, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff02, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff03, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff04, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff05, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff06, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff07, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff08, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff09, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0a, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0b, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0c, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" }, ++ { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" }, ++ { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" }, ++ { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" }, ++ { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" }, ++ ++ { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" }, ++ { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" }, ++ ++ { 0x18501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" }, ++ { 0xa0501851, BTTV_BOARD_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" }, ++ { 0x18511851, BTTV_BOARD_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" }, ++ { 0x18521852, BTTV_BOARD_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" }, ++ { 0x41a0a051, BTTV_BOARD_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" }, ++ { 0x18501f7f, BTTV_BOARD_FLYVIDEO_98, "Lifeview Flyvideo 98" }, ++ ++ { 0x010115cb, BTTV_BOARD_GMV1, "AG GMV1" }, ++ { 0x010114c7, BTTV_BOARD_MODTEC_205, "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV" }, ++ ++ { 0x10b42636, BTTV_BOARD_HAUPPAUGE878, "STB ???" }, ++ { 0x217d6606, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" }, ++ { 0xfff6f6ff, BTTV_BOARD_WINFAST2000, "Leadtek WinFast TV 2000" }, ++ { 0x03116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 311" }, ++ { 0x06116000, BTTV_BOARD_SENSORAY311_611, "Sensoray 611" }, ++ { 0x00790e11, BTTV_BOARD_WINDVR, "Canopus WinDVR PCI" }, ++ { 0xa0fca1a0, BTTV_BOARD_ZOLTRIX, "Face to Face Tvmax" }, ++ { 0x82b2aa6a, BTTV_BOARD_SIMUS_GVC1100, "SIMUS GVC1100" }, ++ { 0x146caa0c, BTTV_BOARD_PV951, "ituner spectra8" }, ++ { 0x200a1295, BTTV_BOARD_PXC200, "ImageNation PXC200A" }, ++ ++ { 0x40111554, BTTV_BOARD_PV_BT878P_9B, "Prolink Pixelview PV-BT" }, ++ { 0x17de0a01, BTTV_BOARD_KWORLD, "Mecer TV/FM/Video Tuner" }, ++ ++ { 0x01051805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #1" }, ++ { 0x01061805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #2" }, ++ { 0x01071805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #3" }, ++ { 0x01081805, BTTV_BOARD_PICOLO_TETRA_CHIP, "Picolo Tetra Chip #4" }, ++ ++ { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" }, ++ ++ { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" }, ++ { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" }, ++ ++ /* likely broken, vendor id doesn't match the other magic views ... ++ * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */ ++ ++ /* Duplicate PCI ID, reconfigure for this board during the eeprom read. ++ * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ ++ ++ { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"}, ++ ++ /* DVB cards (using pci function .1 for mpeg data xfer) */ ++ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" }, ++ { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, ++ { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV"}, ++ { 0x002611bd, BTTV_BOARD_TWINHAN_DST, "Pinnacle PCTV SAT CI" }, ++ { 0x00011822, BTTV_BOARD_TWINHAN_DST, "Twinhan VisionPlus DVB" }, ++ { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" }, ++ { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" }, ++ { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" }, ++ { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" }, ++ { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, ++ { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, ++ { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "}, ++ { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" }, ++ { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" }, ++ { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" }, ++ { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" }, ++ { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, ++ { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, ++ { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, ++ ++ { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" }, ++ { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" }, ++ { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" }, ++ { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" }, ++ { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" }, ++ { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" }, ++ { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" }, ++ { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" }, ++ { 0x3116f200, BTTV_BOARD_TVT_TD3116, "Tongwei Video Technology TD-3116" }, ++ { 0x02280279, BTTV_BOARD_APOSONIC_WDVR, "Aposonic W-DVR" }, ++ { 0, -1, NULL } ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* array with description for bt848 / bt878 tv/grabber cards */ ++ ++struct tvcard bttv_tvcards[] = { ++ /* ---- card 0x00 ---------------------------------- */ ++ [BTTV_BOARD_UNKNOWN] = { ++ .name = " *** UNKNOWN/GENERIC *** ", ++ .video_inputs = 4, ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MIRO] = { ++ .name = "MIRO PCTV", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 0, 0, 0 }, ++ .gpiomute = 10, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_HAUPPAUGE] = { ++ .name = "Hauppauge (bt848)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_STB] = { ++ .name = "STB, Gateway P/N 6000699 (bt848)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 4, 0, 2, 3 }, ++ .gpiomute = 1, ++ .no_msp34xx = 1, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ }, ++ ++ /* ---- card 0x04 ---------------------------------- */ ++ [BTTV_BOARD_INTEL] = { ++ .name = "Intel Create and Share PCI/ Smart Video Recorder III", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 2, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0 }, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_DIAMOND] = { ++ .name = "Diamond DTV2000", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 3, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0, 1, 0, 1 }, ++ .gpiomute = 3, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_AVERMEDIA] = { ++ .name = "AVerMedia TVPhone", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomask = 0x0f, ++ .gpiomux = { 0x0c, 0x04, 0x08, 0x04 }, ++ /* 0x04 for some cards ?? */ ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= avermedia_tvphone_audio, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_MATRIX_VISION] = { ++ .name = "MATRIX-Vision MV-Delta", ++ .video_inputs = 5, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0, 0), ++ .gpiomux = { 0 }, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x08 ---------------------------------- */ ++ [BTTV_BOARD_FLYVIDEO] = { ++ .name = "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xc00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0xc00, 0x800, 0x400 }, ++ .gpiomute = 0xc00, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TURBOTV] = { ++ .name = "IMS/IXmicro TurboTV", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 3, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 1, 2, 3 }, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_HAUPPAUGE878] = { ++ .name = "Hauppauge (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x0f, /* old: 7 */ ++ .muxsel = MUXSEL(2, 0, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MIROPRO] = { ++ .name = "MIRO PCTV pro", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x3014f, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x20001,0x10001, 0, 0 }, ++ .gpiomute = 10, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x0c ---------------------------------- */ ++ [BTTV_BOARD_ADSTECH_TV] = { ++ .name = "ADS Technologies Channel Surfer TV (bt848)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 13, 14, 11, 7 }, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_AVERMEDIA98] = { ++ .name = "AVerMedia TVCapture 98", ++ .video_inputs = 3, ++ /* .audio_inputs= 4, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 13, 14, 11, 7 }, ++ .msp34xx_alt = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= avermedia_tv_stereo_audio, ++ .no_gpioirq = 1, ++ }, ++ [BTTV_BOARD_VHX] = { ++ .name = "Aimslab Video Highway Xtreme (VHX)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */ ++ .gpiomute = 4, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ZOLTRIX] = { ++ .name = "Zoltrix TV-Max", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 1, 0 }, ++ .gpiomute = 10, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x10 ---------------------------------- */ ++ [BTTV_BOARD_PIXVIEWPLAYTV] = { ++ .name = "Prolink Pixelview PlayTV (bt878)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x01fe00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ /* 2003-10-20 by "Anton A. Arapov" */ ++ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, ++ .gpiomute = 0x002000, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_WINVIEW_601] = { ++ .name = "Leadtek WinView 601", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x8300f8, ++ .muxsel = MUXSEL(2, 3, 1, 1, 0), ++ .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 }, ++ .gpiomute = 0xcfa007, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .volume_gpio = winview_volume, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_AVEC_INTERCAP] = { ++ .name = "AVEC Intercapture", ++ .video_inputs = 3, ++ /* .audio_inputs= 2, */ ++ .svhs = 2, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 0, 0, 0 }, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_LIFE_FLYKIT] = { ++ .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x8dff00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x14 ---------------------------------- */ ++ [BTTV_BOARD_CEI_RAFFLES] = { ++ .name = "CEI Raffles Card", ++ .video_inputs = 3, ++ /* .audio_inputs= 3, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_CONFERENCETV] = { ++ .name = "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50", ++ .video_inputs = 4, ++ /* .audio_inputs= 2, tuner, line in */ ++ .svhs = 2, ++ .gpiomask = 0x1800, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, ++ .gpiomute = 0x1800, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_PHOEBE_TVMAS] = { ++ .name = "Askey CPH050/ Phoebe Tv Master + FM", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xc00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 0x800, 0x400 }, ++ .gpiomute = 0xc00, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MODTEC_205] = { ++ .name = "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .has_dig_in = 1, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 0), /* input 2 is digital */ ++ /* .digital_mode= DIGITAL_MODE_CAMERA, */ ++ .gpiomux = { 0, 0, 0, 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ALPS_TSBB5_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x18 ---------------------------------- */ ++ [BTTV_BOARD_MAGICTVIEW061] = { ++ .name = "Askey CPH05X/06X (bt878) [many vendors]", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xe00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = {0x400, 0x400, 0x400, 0x400 }, ++ .gpiomute = 0xc00, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ .has_radio = 1, /* not every card has radio */ ++ }, ++ [BTTV_BOARD_VOBIS_BOOSTAR] = { ++ .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x1f0fff, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, ++ .gpiomute = 0x40000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= terratv_audio, ++ }, ++ [BTTV_BOARD_HAUPPAUG_WCAM] = { ++ .name = "Hauppauge WinCam newer (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 0, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MAXI] = { ++ .name = "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50", ++ .video_inputs = 4, ++ /* .audio_inputs= 2, */ ++ .svhs = 2, ++ .gpiomask = 0x1800, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, ++ .gpiomute = 0x1800, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_SECAM, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x1c ---------------------------------- */ ++ [BTTV_BOARD_TERRATV] = { ++ .name = "Terratec TerraTV+ Version 1.1 (bt878)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x1f0fff, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 }, ++ .gpiomute = 0x40000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= terratv_audio, ++ /* GPIO wiring: ++ External 20 pin connector (for Active Radio Upgrade board) ++ gpio00: i2c-sda ++ gpio01: i2c-scl ++ gpio02: om5610-data ++ gpio03: om5610-clk ++ gpio04: om5610-wre ++ gpio05: om5610-stereo ++ gpio06: rds6588-davn ++ gpio07: Pin 7 n.c. ++ gpio08: nIOW ++ gpio09+10: nIOR, nSEL ?? (bt878) ++ gpio09: nIOR (bt848) ++ gpio10: nSEL (bt848) ++ Sound Routing: ++ gpio16: u2-A0 (1st 4052bt) ++ gpio17: u2-A1 ++ gpio18: u2-nEN ++ gpio19: u4-A0 (2nd 4052) ++ gpio20: u4-A1 ++ u4-nEN - GND ++ Btspy: ++ 00000 : Cdrom (internal audio input) ++ 10000 : ext. Video audio input ++ 20000 : TV Mono ++ a0000 : TV Mono/2 ++ 1a0000 : TV Stereo ++ 30000 : Radio ++ 40000 : Mute ++ */ ++ ++ }, ++ [BTTV_BOARD_PXC200] = { ++ /* Jannik Fritsch */ ++ .name = "Imagenation PXC200", ++ .video_inputs = 5, ++ /* .audio_inputs= 1, */ ++ .svhs = 1, /* was: 4 */ ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0, 0), ++ .gpiomux = { 0 }, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .muxsel_hook = PXC200_muxsel, ++ ++ }, ++ [BTTV_BOARD_FLYVIDEO_98] = { ++ .name = "Lifeview FlyVideo 98 LR50", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x1800, /* 0x8dfe00 */ ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0x0800, 0x1000, 0x1000 }, ++ .gpiomute = 0x1800, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_IPROTV] = { ++ .name = "Formac iProTV, Formac ProTV I (bt848)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 1, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 0, 0, 0 }, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x20 ---------------------------------- */ ++ [BTTV_BOARD_INTEL_C_S_PCI] = { ++ .name = "Intel Create and Share PCI/ Smart Video Recorder III", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 2, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0 }, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TERRATVALUE] = { ++ .name = "Terratec TerraTValue Version Bt878", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xffff00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x500, 0, 0x300, 0x900 }, ++ .gpiomute = 0x900, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_WINFAST2000] = { ++ .name = "Leadtek WinFast 2000/ WinFast 2000 XP", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ /* TV, CVid, SVid, CVid over SVid connector */ ++ .muxsel = MUXSEL(2, 3, 1, 1, 0), ++ /* Alexander Varakin [stereo version] */ ++ .gpiomask = 0xb33000, ++ .gpiomux = { 0x122000,0x1000,0x0000,0x620000 }, ++ .gpiomute = 0x800000, ++ /* Audio Routing for "WinFast 2000 XP" (no tv stereo !) ++ gpio23 -- hef4052:nEnable (0x800000) ++ gpio12 -- hef4052:A1 ++ gpio13 -- hef4052:A0 ++ 0x0000: external audio ++ 0x1000: FM ++ 0x2000: TV ++ 0x3000: n.c. ++ Note: There exists another variant "Winfast 2000" with tv stereo !? ++ Note: eeprom only contains FF and pci subsystem id 107d:6606 ++ */ ++ .pll = PLL_28, ++ .has_radio = 1, ++ .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */ ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= winfast2000_audio, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_CHRONOS_VS2] = { ++ .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II", ++ .video_inputs = 4, ++ /* .audio_inputs= 3, */ ++ .svhs = 2, ++ .gpiomask = 0x1800, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, ++ .gpiomute = 0x1800, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x24 ---------------------------------- */ ++ [BTTV_BOARD_TYPHOON_TVIEW] = { ++ .name = "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner", ++ .video_inputs = 4, ++ /* .audio_inputs= 3, */ ++ .svhs = 2, ++ .gpiomask = 0x1800, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, ++ .gpiomute = 0x1800, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_PXELVWPLTVPRO] = { ++ .name = "Prolink PixelView PlayTV pro", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xff, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, ++ .gpiomute = 0x29, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MAGICTVIEW063] = { ++ .name = "Askey CPH06X TView99", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x551e00, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0x551400, 0x551200, 0, 0 }, ++ .gpiomute = 0x551c00, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_PINNACLE] = { ++ .name = "Pinnacle PCTV Studio/Rave", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x03000F, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 0xd0001, 0, 0 }, ++ .gpiomute = 1, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x28 ---------------------------------- */ ++ [BTTV_BOARD_STB2] = { ++ .name = "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 4, 0, 2, 3 }, ++ .gpiomute = 1, ++ .no_msp34xx = 1, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_AVPHONE98] = { ++ .name = "AVerMedia TVPhone 98", ++ .video_inputs = 3, ++ /* .audio_inputs= 4, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 13, 4, 11, 7 }, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ .audio_mode_gpio= avermedia_tvphone_audio, ++ }, ++ [BTTV_BOARD_PV951] = { ++ .name = "ProVideo PV951", /* pic16c54 */ ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 0, 0}, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ONAIR_TV] = { ++ .name = "Little OnAir TV", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xe00b, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 }, ++ .gpiomute = 0xff3ffc, ++ .no_msp34xx = 1, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x2c ---------------------------------- */ ++ [BTTV_BOARD_SIGMA_TVII_FM] = { ++ .name = "Sigma TVII-FM", ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 3, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 1, 0, 2 }, ++ .gpiomute = 3, ++ .no_msp34xx = 1, ++ .pll = PLL_NONE, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MATRIX_VISION2] = { ++ .name = "MATRIX-Vision MV-Delta 2", ++ .video_inputs = 5, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0, 0), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ZOLTRIX_GENIE] = { ++ .name = "Zoltrix Genie TV/FM", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xbcf03f, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 }, ++ .gpiomute = 0xbcb03f, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_4039FR5_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TERRATVRADIO] = { ++ .name = "Terratec TV/Radio+", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x70000, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, ++ .gpiomute = 0x40000, ++ .no_msp34xx = 1, ++ .pll = PLL_35, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ }, ++ ++ /* ---- card 0x30 ---------------------------------- */ ++ [BTTV_BOARD_DYNALINK] = { ++ .name = "Askey CPH03x/ Dynalink Magic TView", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = {2,0,0,0 }, ++ .gpiomute = 1, ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_GVBCTV3PCI] = { ++ .name = "IODATA GV-BCTV3/PCI", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x010f00, ++ .muxsel = MUXSEL(2, 3, 0, 0), ++ .gpiomux = {0x10000, 0, 0x10000, 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ALPS_TSHC6_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= gvbctv3pci_audio, ++ }, ++ [BTTV_BOARD_PXELVWPLTVPAK] = { ++ .name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP", ++ .video_inputs = 5, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .has_dig_in = 1, ++ .gpiomask = 0xAA0000, ++ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */ ++ /* .digital_mode= DIGITAL_MODE_CAMERA, */ ++ .gpiomux = { 0x20000, 0, 0x80000, 0x80000 }, ++ .gpiomute = 0xa8000, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ /* GPIO wiring: (different from Rev.4C !) ++ GPIO17: U4.A0 (first hef4052bt) ++ GPIO19: U4.A1 ++ GPIO20: U5.A1 (second hef4052bt) ++ GPIO21: U4.nEN ++ GPIO22: BT832 Reset Line ++ GPIO23: A5,A0, U5,nEN ++ Note: At i2c=0x8a is a Bt832 chip, which changes to 0x88 after being reset via GPIO22 ++ */ ++ }, ++ [BTTV_BOARD_EAGLE] = { ++ .name = "Eagle Wireless Capricorn2 (bt878A)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 0, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .pll = PLL_28, ++ .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x34 ---------------------------------- */ ++ [BTTV_BOARD_PINNACLEPRO] = { ++ /* David Härdeman */ ++ .name = "Pinnacle PCTV Studio Pro", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 0x03000F, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 0xd0001, 0, 0 }, ++ .gpiomute = 10, ++ /* sound path (5 sources): ++ MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable) ++ 0= ext. Audio IN ++ 1= from MUX2 ++ 2= Mono TV sound from Tuner ++ 3= not connected ++ MUX2 (mask 0x30000): ++ 0,2,3= from MSP34xx ++ 1= FM stereo Radio from Tuner */ ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TVIEW_RDS_FM] = { ++ /* Claas Langbehn , ++ Sven Grothklags */ ++ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS", ++ .video_inputs = 4, ++ /* .audio_inputs= 3, */ ++ .svhs = 2, ++ .gpiomask = 0x1c, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 0x10, 8 }, ++ .gpiomute = 4, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_LIFETEC_9415] = { ++ /* Tim Röstermundt ++ in de.comp.os.unix.linux.hardware: ++ options bttv card=0 pll=1 radio=1 gpiomask=0x18e0 ++ gpiomux =0x44c71f,0x44d71f,0,0x44d71f,0x44dfff ++ options tuner type=5 */ ++ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x18e0, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 }, ++ .gpiomute = 0x18e0, ++ /* For cards with tda9820/tda9821: ++ 0x0000: Tuner normal stereo ++ 0x0080: Tuner A2 SAP (second audio program = Zweikanalton) ++ 0x0880: Tuner A2 stereo */ ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_BESTBUY_EASYTV] = { ++ /* Miguel Angel Alvarez ++ old Easy TV BT848 version (model CPH031) */ ++ .name = "Askey CPH031/ BESTBUY Easy TV", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xF, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 2, 0, 0, 0 }, ++ .gpiomute = 10, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x38 ---------------------------------- */ ++ [BTTV_BOARD_FLYVIDEO_98FM] = { ++ /* Gordon Heydon */ ++ [BTTV_BOARD_GRANDTEC] = { ++ .name = "GrandTec 'Grand Video Capture' (Bt848)", ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(3, 1), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_35, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ASKEY_CPH060] = { ++ /* Daniel Herrington */ ++ .name = "Askey CPH060/ Phoebe TV Master Only (No FM)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xe00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x400, 0x400, 0x400, 0x400 }, ++ .gpiomute = 0x800, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_4036FY5_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ASKEY_CPH03X] = { ++ /* Matti Mottus */ ++ .name = "Askey CPH03x TV Capturer", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x03000F, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 2, 0, 0, 0 }, ++ .gpiomute = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ }, ++ ++ /* ---- card 0x3c ---------------------------------- */ ++ [BTTV_BOARD_MM100PCTV] = { ++ /* Philip Blundell */ ++ .name = "Modular Technology MM100PCTV", ++ .video_inputs = 2, ++ /* .audio_inputs= 2, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 11, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 0, 0, 1 }, ++ .gpiomute = 8, ++ .pll = PLL_35, ++ .tuner_type = TUNER_TEMIC_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_GMV1] = { ++ /* Adrian Cox ++ new Easy TV BT878 version (model CPH061) ++ special thanks to Informatica Mieres for providing the card */ ++ .name = "Askey CPH061/ BESTBUY Easy TV (bt878)", ++ .video_inputs = 3, ++ /* .audio_inputs= 2, */ ++ .svhs = 2, ++ .gpiomask = 0xFF, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 1, 0, 4, 4 }, ++ .gpiomute = 9, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_ATI_TVWONDER] = { ++ /* Lukas Gebauer */ ++ .name = "ATI TV-Wonder", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0xf03f, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0xbffe, 0, 0xbfff, 0 }, ++ .gpiomute = 0xbffe, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x40 ---------------------------------- */ ++ [BTTV_BOARD_ATI_TVWONDERVE] = { ++ /* Lukas Gebauer */ ++ .name = "ATI TV-Wonder VE", ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 1, ++ .muxsel = MUXSEL(2, 3, 0, 1), ++ .gpiomux = { 0, 0, 1, 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_FLYVIDEO2000] = { ++ /* DeeJay */ ++ .name = "IODATA GV-BCTV4/PCI", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x010f00, ++ .muxsel = MUXSEL(2, 3, 0, 0), ++ .gpiomux = {0x10000, 0, 0x10000, 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_SHARP_2U5JF5540_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= gvbctv3pci_audio, ++ }, ++ ++ /* ---- card 0x44 ---------------------------------- */ ++ [BTTV_BOARD_VOODOOTV_FM] = { ++ .name = "3Dfx VoodooTV FM (Euro)", ++ /* try "insmod msp3400 simple=0" if you have ++ * sound problems with this card. */ ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x4f8a00, ++ /* 0x100000: 1=MSP enabled (0=disable again) ++ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ ++ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff }, ++ .gpiomute = 0x947fff, ++ /* tvtuner, radio, external,internal, mute, stereo ++ * tuner, Composit, SVid, Composit-on-Svid-adapter */ ++ .muxsel = MUXSEL(2, 3, 0, 1), ++ .tuner_type = TUNER_MT2032, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_VOODOOTV_200] = { ++ .name = "VoodooTV 200 (USA)", ++ /* try "insmod msp3400 simple=0" if you have ++ * sound problems with this card. */ ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x4f8a00, ++ /* 0x100000: 1=MSP enabled (0=disable again) ++ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ ++ .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff }, ++ .gpiomute = 0x947fff, ++ /* tvtuner, radio, external,internal, mute, stereo ++ * tuner, Composit, SVid, Composit-on-Svid-adapter */ ++ .muxsel = MUXSEL(2, 3, 0, 1), ++ .tuner_type = TUNER_MT2032, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_AIMMS] = { ++ /* Philip Blundell */ ++ .name = "Active Imaging AIMMS", ++ .video_inputs = 1, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .muxsel = MUXSEL(2), ++ .gpiomask = 0 ++ }, ++ [BTTV_BOARD_PV_BT878P_PLUS] = { ++ /* Tomasz Pyra */ ++ .name = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)", ++ .video_inputs = 3, ++ /* .audio_inputs= 4, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */ ++ .gpiomute = 13, ++ .pll = PLL_28, ++ .tuner_type = TUNER_LG_PAL_I_FM, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ /* GPIO wiring: ++ GPIO0: U4.A0 (hef4052bt) ++ GPIO1: U4.A1 ++ GPIO2: U4.A1 (second hef4052bt) ++ GPIO3: U4.nEN, U5.A0, A5.nEN ++ GPIO8-15: vrd866b ? ++ */ ++ }, ++ [BTTV_BOARD_FLYVIDEO98EZ] = { ++ .name = "Lifeview FlyVideo 98EZ (capture only) LR51", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 2, ++ /* AV1, AV2, SVHS, CVid adapter on SVHS */ ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x48 ---------------------------------- */ ++ [BTTV_BOARD_PV_BT878P_9B] = { ++ /* Dariusz Kowalewski */ ++ .name = "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x3f, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x01, 0x00, 0x03, 0x03 }, ++ .gpiomute = 0x09, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */ ++ .has_radio = 1, /* Note: not all cards have radio */ ++ .has_remote = 1, ++ /* GPIO wiring: ++ GPIO0: A0 hef4052 ++ GPIO1: A1 hef4052 ++ GPIO3: nEN hef4052 ++ GPIO8-15: vrd866b ++ GPIO20,22,23: R30,R29,R28 ++ */ ++ }, ++ [BTTV_BOARD_SENSORAY311_611] = { ++ /* Clay Kunz */ ++ /* you must jumper JP5 for the 311 card (PC/104+) to work */ ++ .name = "Sensoray 311/611", ++ .video_inputs = 5, ++ /* .audio_inputs= 0, */ ++ .svhs = 4, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0, 0), ++ .gpiomux = { 0 }, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_RV605] = { ++ /* Miguel Freitas */ ++ .name = "RemoteVision MX (RV605)", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x00, ++ .gpiomask2 = 0x07ff, ++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), ++ .no_msp34xx = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .muxsel_hook = rv605_muxsel, ++ }, ++ [BTTV_BOARD_POWERCLR_MTV878] = { ++ .name = "Powercolor MTV878/ MTV878R/ MTV878F", ++ .video_inputs = 3, ++ /* .audio_inputs= 2, */ ++ .svhs = 2, ++ .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */ ++ .muxsel = MUXSEL(2, 1, 1), ++ .gpiomux = { 0, 1, 2, 2 }, ++ .gpiomute = 4, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ }, ++ ++ /* ---- card 0x4c ---------------------------------- */ ++ [BTTV_BOARD_WINDVR] = { ++ /* Masaki Suzuki */ ++ .name = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x140007, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= windvr_audio, ++ }, ++ [BTTV_BOARD_GRANDTEC_MULTI] = { ++ .name = "GrandTec Multi Capture Card (Bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_KWORLD] = { ++ .name = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF", ++ .video_inputs = 4, ++ /* .audio_inputs= 3, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ /* Tuner, SVid, SVHS, SVid to SVHS connector */ ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio! ++ * This card lacks external Audio In, so we mute it on Ext. & Int. ++ * The PCB can take a sbx1637/sbx1673, wiring unknown. ++ * This card lacks PCI subsystem ID, sigh. ++ * gpiomux =1: lower volume, 2+3: mute ++ * btwincap uses 0x80000/0x80003 ++ */ ++ .gpiomute = 4, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and ++ radio signal strength indicators work fine. */ ++ .has_radio = 1, ++ /* GPIO Info: ++ GPIO0,1: HEF4052 A0,A1 ++ GPIO2: HEF4052 nENABLE ++ GPIO3-7: n.c. ++ GPIO8-13: IRDC357 data0-5 (data6 n.c. ?) [chip not present on my card] ++ GPIO14,15: ?? ++ GPIO16-21: n.c. ++ GPIO22,23: ?? ++ ?? : mtu8b56ep microcontroller for IR (GPIO wiring unknown)*/ ++ }, ++ [BTTV_BOARD_DSP_TCVIDEO] = { ++ /* Arthur Tetzlaff-Deas, DSP Design Ltd */ ++ .name = "DSP Design TCVIDEO", ++ .video_inputs = 4, ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x50 ---------------------------------- */ ++ [BTTV_BOARD_HAUPPAUGEPVR] = { ++ .name = "Hauppauge WinTV PVR", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 0, 1, 1), ++ .pll = PLL_28, ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ ++ .gpiomask = 7, ++ .gpiomux = {7}, ++ }, ++ [BTTV_BOARD_GVBCTV5PCI] = { ++ .name = "IODATA GV-BCTV5/PCI", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x0f0f80, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = {0x030000, 0x010000, 0, 0 }, ++ .gpiomute = 0x020000, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= gvbctv5pci_audio, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_OSPREY1x0] = { ++ .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */ ++ .video_inputs = 4, /* id-inputs-clock */ ++ /* .audio_inputs= 0, */ ++ .svhs = 3, ++ .muxsel = MUXSEL(3, 2, 0, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY1x0_848] = { ++ .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */ ++ .video_inputs = 3, ++ /* .audio_inputs= 0, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ ++ /* ---- card 0x54 ---------------------------------- */ ++ [BTTV_BOARD_OSPREY101_848] = { ++ .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(3, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY1x1] = { ++ .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */ ++ .video_inputs = 1, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(0), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY1x1_SVID] = { ++ .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(0, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY2xx] = { ++ .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */ ++ .video_inputs = 1, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(0), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ ++ /* ---- card 0x58 ---------------------------------- */ ++ [BTTV_BOARD_OSPREY2x0_SVID] = { ++ .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(0, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY2x0] = { ++ .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(2, 3), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY500] = { ++ .name = "Osprey 500", /* 500 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(2, 3), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ [BTTV_BOARD_OSPREY540] = { ++ .name = "Osprey 540", /* 540 */ ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ ++ /* ---- card 0x5C ---------------------------------- */ ++ [BTTV_BOARD_OSPREY2000] = { ++ .name = "Osprey 2000", /* 2000 */ ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(2, 3), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */ ++ }, ++ [BTTV_BOARD_IDS_EAGLE] = { ++ /* M G Berberich */ ++ .name = "IDS Eagle", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .muxsel_hook = eagle_muxsel, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_PINNACLESAT] = { ++ .name = "Pinnacle PCTV Sat", ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel = MUXSEL(3, 1), ++ .pll = PLL_28, ++ .no_gpioirq = 1, ++ .has_dvb = 1, ++ }, ++ [BTTV_BOARD_FORMAC_PROTV] = { ++ .name = "Formac ProTV II (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .gpiomask = 2, ++ /* TV, Comp1, Composite over SVID con, SVID */ ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 2, 0, 0 }, ++ .pll = PLL_28, ++ .has_radio = 1, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ /* sound routing: ++ GPIO=0x00,0x01,0x03: mute (?) ++ 0x02: both TV and radio (tuner: FM1216/I) ++ The card has onboard audio connectors labeled "cdrom" and "board", ++ not soldered here, though unknown wiring. ++ Card lacks: external audio in, pci subsystem id. ++ */ ++ }, ++ ++ /* ---- card 0x60 ---------------------------------- */ ++ [BTTV_BOARD_MACHTV] = { ++ .name = "MachTV", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 3}, ++ .gpiomute = 4, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_EURESYS_PICOLO] = { ++ .name = "Euresys Picolo", ++ .video_inputs = 3, ++ /* .audio_inputs= 0, */ ++ .svhs = 2, ++ .gpiomask = 0, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel = MUXSEL(2, 0, 1), ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_PV150] = { ++ /* Luc Van Hoeylandt */ ++ .name = "ProVideo PV150", /* 0x4f */ ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_AD_TVK503] = { ++ /* Hiroshi Takekawa */ ++ /* This card lacks subsystem ID */ ++ .name = "AD-TVK503", /* 0x63 */ ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x001e8007, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ /* Tuner, Radio, external, internal, off, on */ ++ .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 }, ++ .gpiomute = 0x0f, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .audio_mode_gpio= adtvk503_audio, ++ }, ++ ++ /* ---- card 0x64 ---------------------------------- */ ++ [BTTV_BOARD_HERCULES_SM_TV] = { ++ .name = "Hercules Smart TV Stereo", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ /* Notes: ++ - card lacks subsystem ID ++ - stereo variant w/ daughter board with tda9874a @0xb0 ++ - Audio Routing: ++ always from tda9874 independent of GPIO (?) ++ external line in: unknown ++ - Other chips: em78p156elp @ 0x96 (probably IR remote control) ++ hef4053 (instead 4052) for unknown function ++ */ ++ }, ++ [BTTV_BOARD_PACETV] = { ++ .name = "Pace TV & Radio Card", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ /* Tuner, CVid, SVid, CVid over SVid connector */ ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomask = 0, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ .pll = PLL_28, ++ /* Bt878, Bt832, FI1246 tuner; no pci subsystem id ++ only internal line out: (4pin header) RGGL ++ Radio must be decoded by msp3410d (not routed through)*/ ++ /* ++ .digital_mode = DIGITAL_MODE_CAMERA, todo! ++ */ ++ }, ++ [BTTV_BOARD_IVC200] = { ++ /* Chris Willing */ ++ .name = "IVC-200", ++ .video_inputs = 1, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0xdf, ++ .muxsel = MUXSEL(2), ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_IVCE8784] = { ++ .name = "IVCE-8784", ++ .video_inputs = 1, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0xdf, ++ .muxsel = MUXSEL(2), ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_XGUARD] = { ++ .name = "Grand X-Guard / Trust 814PCI", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .gpiomask2 = 0xff, ++ .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0), ++ .muxsel_hook = xguard_muxsel, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ }, ++ ++ /* ---- card 0x68 ---------------------------------- */ ++ [BTTV_BOARD_NEBULA_DIGITV] = { ++ .name = "Nebula Electronics DigiTV", ++ .video_inputs = 1, ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .has_dvb = 1, ++ .has_remote = 1, ++ .gpiomask = 0x1b, ++ .no_gpioirq = 1, ++ }, ++ [BTTV_BOARD_PV143] = { ++ /* Jorge Boncompte - DTI2 */ ++ .name = "ProVideo PV143", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = { ++ /* M.Klahr@phytec.de */ ++ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 3, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VD009X1_VD011_COMBI] = { ++ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 3, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ ++ /* ---- card 0x6c ---------------------------------- */ ++ [BTTV_BOARD_VD009_MINIDIN] = { ++ .name = "PHYTEC VD-009 MiniDIN (bt878)", ++ .video_inputs = 10, ++ /* .audio_inputs= 0, */ ++ .svhs = 9, ++ .gpiomask = 0x00, ++ .gpiomask2 = 0x03, /* used for external vodeo mux */ ++ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0), ++ .muxsel_hook = phytec_muxsel, ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VD009_COMBI] = { ++ .name = "PHYTEC VD-009 Combi (bt878)", ++ .video_inputs = 10, ++ /* .audio_inputs= 0, */ ++ .svhs = 9, ++ .gpiomask = 0x00, ++ .gpiomask2 = 0x03, /* used for external vodeo mux */ ++ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1), ++ .muxsel_hook = phytec_muxsel, ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_IVC100] = { ++ .name = "IVC-100", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0xdf, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_IVC120] = { ++ /* IVC-120G - Alan Garfield */ ++ .name = "IVC-120G", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, /* card has no svhs */ ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), ++ .muxsel_hook = ivc120_muxsel, ++ .pll = PLL_28, ++ }, ++ ++ /* ---- card 0x70 ---------------------------------- */ ++ [BTTV_BOARD_PC_HDTV] = { ++ .name = "pcHDTV HD-2000 TV", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .tuner_type = TUNER_PHILIPS_FCV1236D, ++ .tuner_addr = ADDR_UNSET, ++ .has_dvb = 1, ++ }, ++ [BTTV_BOARD_TWINHAN_DST] = { ++ .name = "Twinhan DST + clones", ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_video = 1, ++ .has_dvb = 1, ++ }, ++ [BTTV_BOARD_WINFASTVC100] = { ++ .name = "Winfast VC100", ++ .video_inputs = 3, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ /* Vid In, SVid In, Vid over SVid in connector */ ++ .muxsel = MUXSEL(3, 1, 1, 3), ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ }, ++ [BTTV_BOARD_TEV560] = { ++ .name = "Teppro TEV-560/InterVision IV-560", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 3, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 1, 1, 1, 1 }, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_35, ++ }, ++ ++ /* ---- card 0x74 ---------------------------------- */ ++ [BTTV_BOARD_SIMUS_GVC1100] = { ++ .name = "SIMUS GVC1100", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .gpiomask = 0x3F, ++ .muxsel_hook = gvc1100_muxsel, ++ }, ++ [BTTV_BOARD_NGSTV_PLUS] = { ++ /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */ ++ .name = "NGS NGSTV+", ++ .video_inputs = 3, ++ .svhs = 2, ++ .gpiomask = 0x008007, ++ .muxsel = MUXSEL(2, 3, 0, 0), ++ .gpiomux = { 0, 0, 0, 0 }, ++ .gpiomute = 0x000003, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_LMLBT4] = { ++ /* http://linuxmedialabs.com */ ++ .name = "LMLBT4", ++ .video_inputs = 4, /* IN1,IN2,IN3,IN4 */ ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TEKRAM_M205] = { ++ /* Helmroos Harri */ ++ .name = "Tekram M205 PRO", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = 2, ++ .gpiomask = 0x68, ++ .muxsel = MUXSEL(2, 3, 1), ++ .gpiomux = { 0x68, 0x68, 0x61, 0x61 }, ++ .pll = PLL_28, ++ }, ++ ++ /* ---- card 0x78 ---------------------------------- */ ++ [BTTV_BOARD_CONTVFMI] = { ++ /* Javier Cendan Ares */ ++ /* bt878 TV + FM without subsystem ID */ ++ .name = "Conceptronic CONTVFMi", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x008007, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 2 }, ++ .gpiomute = 3, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_PICOLO_TETRA_CHIP] = { ++ /*Eric DEBIEF */ ++ /*EURESYS Picolo Tetra : 4 Conexant Fusion 878A, no audio, video input set with analog multiplexers GPIO controlled*/ ++ /* adds picolo_tetra_muxsel(), picolo_tetra_init(), the following declaration strucure, and #define BTTV_BOARD_PICOLO_TETRA_CHIP*/ ++ /*0x79 in bttv.h*/ ++ .name = "Euresys Picolo Tetra", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/ ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ /*878A input is always MUX0, see above.*/ ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_SPIRIT_TV] = { ++ /* Spirit TV Tuner from http://spiritmodems.com.au */ ++ /* Stafford Goodsell */ ++ .name = "Spirit TV Tuner", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x0000000f, ++ .muxsel = MUXSEL(2, 1, 1), ++ .gpiomux = { 0x02, 0x00, 0x00, 0x00 }, ++ .tuner_type = TUNER_TEMIC_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ }, ++ [BTTV_BOARD_AVDVBT_771] = { ++ /* Wolfram Joost */ ++ .name = "AVerMedia AVerTV DVB-T 771", ++ .video_inputs = 2, ++ .svhs = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .muxsel = MUXSEL(3, 3), ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ .has_dvb = 1, ++ .no_gpioirq = 1, ++ .has_remote = 1, ++ }, ++ /* ---- card 0x7c ---------------------------------- */ ++ [BTTV_BOARD_AVDVBT_761] = { ++ /* Matt Jesson */ ++ /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */ ++ .name = "AverMedia AverTV DVB-T 761", ++ .video_inputs = 2, ++ .svhs = 1, ++ .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */ ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .has_dvb = 1, ++ .no_gpioirq = 1, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_MATRIX_VISIONSQ] = { ++ /* andre.schwarz@matrix-vision.de */ ++ .name = "MATRIX Vision Sigma-SQ", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x0, ++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3), ++ .muxsel_hook = sigmaSQ_muxsel, ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MATRIX_VISIONSLC] = { ++ /* andre.schwarz@matrix-vision.de */ ++ .name = "MATRIX Vision Sigma-SLC", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x0, ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .muxsel_hook = sigmaSLC_muxsel, ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ /* BTTV_BOARD_APAC_VIEWCOMP */ ++ [BTTV_BOARD_APAC_VIEWCOMP] = { ++ /* Attila Kondoros */ ++ /* bt878 TV + FM 0x00000000 subsystem ID */ ++ .name = "APAC Viewcomp 878(AMAX)", ++ .video_inputs = 2, ++ /* .audio_inputs= 1, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0xFF, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 0, 0, 0 }, ++ .gpiomute = 10, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */ ++ .has_radio = 1, /* not every card has radio */ ++ }, ++ ++ /* ---- card 0x80 ---------------------------------- */ ++ [BTTV_BOARD_DVICO_DVBT_LITE] = { ++ /* Chris Pascoe */ ++ .name = "DViCO FusionHDTV DVB-T Lite", ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ .no_video = 1, ++ .has_dvb = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VGEAR_MYVCD] = { ++ /* Steven */ ++ .name = "V-Gear MyVCD", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x3f, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .gpiomux = {0x31, 0x31, 0x31, 0x31 }, ++ .gpiomute = 0x31, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 0, ++ }, ++ [BTTV_BOARD_SUPER_TV] = { ++ /* Rick C */ ++ .name = "Super TV Tuner", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ .gpiomask = 0x008007, ++ .gpiomux = { 0, 0x000001,0,0 }, ++ .has_radio = 1, ++ }, ++ [BTTV_BOARD_TIBET_CS16] = { ++ /* Chris Fanning */ ++ .name = "Tibet Systems 'Progress DVR' CS16", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .muxsel_hook = tibetCS16_muxsel, ++ }, ++ [BTTV_BOARD_KODICOM_4400R] = { ++ /* Bill Brack */ ++ /* ++ * Note that, because of the card's wiring, the "master" ++ * BT878A chip (i.e. the one which controls the analog switch ++ * and must use this card type) is the 2nd one detected. The ++ * other 3 chips should use card type 0x85, whose description ++ * follows this one. There is a EEPROM on the card (which is ++ * connected to the I2C of one of those other chips), but is ++ * not currently handled. There is also a facility for a ++ * "monitor", which is also not currently implemented. ++ */ ++ .name = "Kodicom 4400R (master)", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ /* GPIO bits 0-9 used for analog switch: ++ * 00 - 03: camera selector ++ * 04 - 06: channel (controller) selector ++ * 07: data (1->on, 0->off) ++ * 08: strobe ++ * 09: reset ++ * bit 16 is input from sync separator for the channel ++ */ ++ .gpiomask = 0x0003ff, ++ .no_gpioirq = 1, ++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel_hook = kodicom4400r_muxsel, ++ }, ++ [BTTV_BOARD_KODICOM_4400R_SL] = { ++ /* Bill Brack */ ++ /* Note that, for reasons unknown, the "master" BT878A chip (i.e. the ++ * one which controls the analog switch, and must use the card type) ++ * is the 2nd one detected. The other 3 chips should use this card ++ * type ++ */ ++ .name = "Kodicom 4400R (slave)", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0x010000, ++ .no_gpioirq = 1, ++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel_hook = kodicom4400r_muxsel, ++ }, ++ /* ---- card 0x86---------------------------------- */ ++ [BTTV_BOARD_ADLINK_RTV24] = { ++ /* Michael Henson */ ++ /* Adlink RTV24 with special unlock codes */ ++ .name = "Adlink RTV24", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .tuner_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ }, ++ /* ---- card 0x87---------------------------------- */ ++ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = { ++ /* Michael Krufky */ ++ .name = "DViCO FusionHDTV 5 Lite", ++ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ ++ .tuner_addr = ADDR_UNSET, ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1), ++ .gpiomask = 0x00e00007, ++ .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, ++ .gpiomute = 0x00c00007, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .has_dvb = 1, ++ }, ++ /* ---- card 0x88---------------------------------- */ ++ [BTTV_BOARD_ACORP_Y878F] = { ++ /* Mauro Carvalho Chehab */ ++ .name = "Acorp Y878F", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x01fe00, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, ++ .gpiomute = 0x002000, ++ .pll = PLL_28, ++ .tuner_type = TUNER_YMEC_TVF66T5_B_DFF, ++ .tuner_addr = 0xc1 >>1, ++ .has_radio = 1, ++ }, ++ /* ---- card 0x89 ---------------------------------- */ ++ [BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = { ++ .name = "Conceptronic CTVFMi v2", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x001c0007, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 2 }, ++ .gpiomute = 3, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TENA_9533_DI, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ .has_radio = 1, ++ }, ++ /* ---- card 0x8a ---------------------------------- */ ++ [BTTV_BOARD_PV_BT878P_2E] = { ++ .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)", ++ .video_inputs = 5, ++ /* .audio_inputs= 1, */ ++ .svhs = 3, ++ .has_dig_in = 1, ++ .gpiomask = 0x01fe00, ++ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */ ++ /* .digital_mode= DIGITAL_MODE_CAMERA, */ ++ .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 }, ++ .gpiomute = 0x12400, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_LG_PAL_FM, ++ .tuner_addr = ADDR_UNSET, ++ .has_remote = 1, ++ }, ++ /* ---- card 0x8b ---------------------------------- */ ++ [BTTV_BOARD_PV_M4900] = { ++ /* Sérgio Fortier */ ++ .name = "Prolink PixelView PlayTV MPEG2 PV-M4900", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x3f, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, ++ .gpiomute = 0x29, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_YMEC_TVF_5533MF, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ .has_remote = 1, ++ }, ++ /* ---- card 0x8c ---------------------------------- */ ++ /* Has four Bt878 chips behind a PCI bridge, each chip has: ++ one external BNC composite input (mux 2) ++ three internal composite inputs (unknown muxes) ++ an 18-bit stereo A/D (CS5331A), which has: ++ one external stereo unblanced (RCA) audio connection ++ one (or 3?) internal stereo balanced (XLR) audio connection ++ input is selected via gpio to a 14052B mux ++ (mask=0x300, unbal=0x000, bal=0x100, ??=0x200,0x300) ++ gain is controlled via an X9221A chip on the I2C bus @0x28 ++ sample rate is controlled via gpio to an MK1413S ++ (mask=0x3, 32kHz=0x0, 44.1kHz=0x1, 48kHz=0x2, ??=0x3) ++ There is neither a tuner nor an svideo input. */ ++ [BTTV_BOARD_OSPREY440] = { ++ .name = "Osprey 440", ++ .video_inputs = 4, ++ /* .audio_inputs= 2, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */ ++ .gpiomask = 0x303, ++ .gpiomute = 0x000, /* int + 32kHz */ ++ .gpiomux = { 0, 0, 0x000, 0x100}, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ /* ---- card 0x8d ---------------------------------- */ ++ [BTTV_BOARD_ASOUND_SKYEYE] = { ++ .name = "Asound Skyeye PCTV", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 15, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 2, 0, 0, 0 }, ++ .gpiomute = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ /* ---- card 0x8e ---------------------------------- */ ++ [BTTV_BOARD_SABRENT_TVFM] = { ++ .name = "Sabrent TV-FM (bttv version)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x108007, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 100000, 100002, 100002, 100000 }, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_TNF_5335MF, ++ .tuner_addr = ADDR_UNSET, ++ .has_radio = 1, ++ }, ++ /* ---- card 0x8f ---------------------------------- */ ++ [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = { ++ .name = "Hauppauge ImpactVCB (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x0f, /* old: 7 */ ++ .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */ ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_MACHTV_MAGICTV] = { ++ /* Julian Calaby ++ * Slightly different from original MachTV definition (0x60) ++ ++ * FIXME: RegSpy says gpiomask should be "0x001c800f", but it ++ * stuffs up remote chip. Bug is a pin on the jaecs is not set ++ * properly (methinks) causing no keyup bits being set */ ++ ++ .name = "MagicTV", /* rebranded MachTV */ ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 7, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 3 }, ++ .gpiomute = 4, ++ .tuner_type = TUNER_TEMIC_4009FR5_PAL, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_SSAI_SECURITY] = { ++ .name = "SSAI Security Video Interface", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(0, 1, 2, 3), ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_SSAI_ULTRASOUND] = { ++ .name = "SSAI Ultrasound Video Interface", ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = 1, ++ .muxsel = MUXSEL(2, 0, 1, 3), ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ /* ---- card 0x94---------------------------------- */ ++ [BTTV_BOARD_DVICO_FUSIONHDTV_2] = { ++ .name = "DViCO FusionHDTV 2", ++ .tuner_type = TUNER_PHILIPS_FCV1236D, ++ .tuner_addr = ADDR_UNSET, ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .muxsel = MUXSEL(2, 3, 1), ++ .gpiomask = 0x00e00007, ++ .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, ++ .gpiomute = 0x00c00007, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ }, ++ /* ---- card 0x95---------------------------------- */ ++ [BTTV_BOARD_TYPHOON_TVTUNERPCI] = { ++ .name = "Typhoon TV-Tuner PCI (50684)", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x3014f, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0x20001,0x10001, 0, 0 }, ++ .gpiomute = 10, ++ .pll = PLL_28, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_GEOVISION_GV600] = { ++ /* emhn@usb.ve */ ++ .name = "Geovision GV-600", ++ .video_inputs = 16, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x0, ++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), ++ .muxsel_hook = geovision_muxsel, ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_KOZUMI_KTV_01C] = { ++ /* Mauro Lacy ++ * Based on MagicTV and Conceptronic CONTVFMi */ ++ ++ .name = "Kozumi KTV-01C", ++ .video_inputs = 3, ++ /* .audio_inputs= 1, */ ++ .svhs = 2, ++ .gpiomask = 0x008007, ++ .muxsel = MUXSEL(2, 3, 1, 1), ++ .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */ ++ .gpiomute = 3, /* CONTVFMi */ ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */ ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_ENLTV_FM_2] = { ++ /* Encore TV Tuner Pro ENL TV-FM-2 ++ Mauro Carvalho Chehab IR disabled ++ bit 18/17 = 00 -> mute ++ 01 -> enable external audio input ++ 10 -> internal audio input (mono?) ++ 11 -> internal audio input ++ */ ++ .gpiomask = 0x060040, ++ .muxsel = MUXSEL(2, 3, 3), ++ .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 }, ++ .gpiomute = 0, ++ .tuner_type = TUNER_TCL_MF02GIP_5N, ++ .tuner_addr = ADDR_UNSET, ++ .pll = PLL_28, ++ .has_radio = 1, ++ .has_remote = 1, ++ }, ++ [BTTV_BOARD_VD012] = { ++ /* D.Heer@Phytec.de */ ++ .name = "PHYTEC VD-012 (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(0, 2, 3, 1), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VD012_X1] = { ++ /* D.Heer@Phytec.de */ ++ .name = "PHYTEC VD-012-X1 (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 3, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(2, 3, 1), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_VD012_X2] = { ++ /* D.Heer@Phytec.de */ ++ .name = "PHYTEC VD-012-X2 (bt878)", ++ .video_inputs = 4, ++ /* .audio_inputs= 0, */ ++ .svhs = 3, ++ .gpiomask = 0x00, ++ .muxsel = MUXSEL(3, 2, 1), ++ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_GEOVISION_GV800S] = { ++ /* Bruno Christo ++ * ++ * GeoVision GV-800(S) has 4 Conexant Fusion 878A: ++ * 1 audio input per BT878A = 4 audio inputs ++ * 4 video inputs per BT878A = 16 video inputs ++ * This is the first BT878A chip of the GV-800(S). It's the ++ * "master" chip and it controls the video inputs through an ++ * analog multiplexer (a CD22M3494) via some GPIO pins. The ++ * slaves should use card type 0x9e (following this one). ++ * There is a EEPROM on the card which is currently not handled. ++ * The audio input is not working yet. ++ */ ++ .name = "Geovision GV-800(S) (master)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0xf107f, ++ .no_gpioirq = 1, ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel_hook = gv800s_muxsel, ++ }, ++ [BTTV_BOARD_GEOVISION_GV800S_SL] = { ++ /* Bruno Christo ++ * ++ * GeoVision GV-800(S) has 4 Conexant Fusion 878A: ++ * 1 audio input per BT878A = 4 audio inputs ++ * 4 video inputs per BT878A = 16 video inputs ++ * The 3 other BT878A chips are "slave" chips of the GV-800(S) ++ * and should use this card type. ++ * The audio input is not working yet. ++ */ ++ .name = "Geovision GV-800(S) (slave)", ++ .video_inputs = 4, ++ /* .audio_inputs= 1, */ ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ .svhs = NO_SVHS, ++ .gpiomask = 0x00, ++ .no_gpioirq = 1, ++ .muxsel = MUXSEL(2, 2, 2, 2), ++ .pll = PLL_28, ++ .no_msp34xx = 1, ++ .no_tda7432 = 1, ++ .muxsel_hook = gv800s_muxsel, ++ }, ++ [BTTV_BOARD_PV183] = { ++ .name = "ProVideo PV183", /* 0x9f */ ++ .video_inputs = 2, ++ /* .audio_inputs= 0, */ ++ .svhs = NO_SVHS, ++ .gpiomask = 0, ++ .muxsel = MUXSEL(2, 3), ++ .gpiomux = { 0 }, ++ .no_msp34xx = 1, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = ADDR_UNSET, ++ }, ++ [BTTV_BOARD_TVT_TD3116] = { ++ .name = "Tongwei Video Technology TD-3116", ++ .video_inputs = 16, ++ .gpiomask = 0xc00ff, ++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), ++ .muxsel_hook = td3116_muxsel, ++ .svhs = NO_SVHS, ++ .pll = PLL_28, ++ .tuner_type = TUNER_ABSENT, ++ }, ++ [BTTV_BOARD_APOSONIC_WDVR] = { ++ .name = "Aposonic W-DVR", ++ .video_inputs = 4, ++ .svhs = NO_SVHS, ++ .muxsel = MUXSEL(2, 3, 1, 0), ++ .tuner_type = TUNER_ABSENT, ++ }, ++ ++}; ++ ++static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); ++ ++/* ----------------------------------------------------------------------- */ ++ ++static unsigned char eeprom_data[256]; ++ ++/* ++ * identify card ++ */ ++void __devinit bttv_idcard(struct bttv *btv) ++{ ++ unsigned int gpiobits; ++ int i,type; ++ ++ /* read PCI subsystem ID */ ++ btv->cardid = btv->c.pci->subsystem_device << 16; ++ btv->cardid |= btv->c.pci->subsystem_vendor; ++ ++ if (0 != btv->cardid && 0xffffffff != btv->cardid) { ++ /* look for the card */ ++ for (type = -1, i = 0; cards[i].id != 0; i++) ++ if (cards[i].id == btv->cardid) ++ type = i; ++ ++ if (type != -1) { ++ /* found it */ ++ pr_info("%d: detected: %s [card=%d], PCI subsystem ID is %04x:%04x\n", ++ btv->c.nr, cards[type].name, cards[type].cardnr, ++ btv->cardid & 0xffff, ++ (btv->cardid >> 16) & 0xffff); ++ btv->c.type = cards[type].cardnr; ++ } else { ++ /* 404 */ ++ pr_info("%d: subsystem: %04x:%04x (UNKNOWN)\n", ++ btv->c.nr, btv->cardid & 0xffff, ++ (btv->cardid >> 16) & 0xffff); ++ pr_debug("please mail id, board name and the correct card= insmod option to linux-media@vger.kernel.org\n"); ++ } ++ } ++ ++ /* let the user override the autodetected type */ ++ if (card[btv->c.nr] < bttv_num_tvcards) ++ btv->c.type=card[btv->c.nr]; ++ ++ /* print which card config we are using */ ++ pr_info("%d: using: %s [card=%d,%s]\n", ++ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type, ++ card[btv->c.nr] < bttv_num_tvcards ++ ? "insmod option" : "autodetected"); ++ ++ /* overwrite gpio stuff ?? */ ++ if (UNSET == audioall && UNSET == audiomux[0]) ++ return; ++ ++ if (UNSET != audiomux[0]) { ++ gpiobits = 0; ++ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { ++ bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i]; ++ gpiobits |= audiomux[i]; ++ } ++ } else { ++ gpiobits = audioall; ++ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { ++ bttv_tvcards[btv->c.type].gpiomux[i] = audioall; ++ } ++ } ++ bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; ++ pr_info("%d: gpio config override: mask=0x%x, mux=", ++ btv->c.nr, bttv_tvcards[btv->c.type].gpiomask); ++ for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { ++ pr_cont("%s0x%x", ++ i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); ++ } ++ pr_cont("\n"); ++} ++ ++/* ++ * (most) board specific initialisations goes here ++ */ ++ ++/* Some Modular Technology cards have an eeprom, but no subsystem ID */ ++static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) ++{ ++ int type = -1; ++ ++ if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13)) ++ type = BTTV_BOARD_MODTEC_205; ++ else if (0 == strncmp(eeprom_data+20,"Picolo",7)) ++ type = BTTV_BOARD_EURESYS_PICOLO; ++ else if (eeprom_data[0] == 0x84 && eeprom_data[2]== 0) ++ type = BTTV_BOARD_HAUPPAUGE; /* old bt848 */ ++ ++ if (-1 != type) { ++ btv->c.type = type; ++ pr_info("%d: detected by eeprom: %s [card=%d]\n", ++ btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type); ++ } ++} ++ ++static void flyvideo_gpio(struct bttv *btv) ++{ ++ int gpio, has_remote, has_radio, is_capture_only; ++ int is_lr90, has_tda9820_tda9821; ++ int tuner_type = UNSET, ttype; ++ ++ gpio_inout(0xffffff, 0); ++ udelay(8); /* without this we would see the 0x1800 mask */ ++ gpio = gpio_read(); ++ /* FIXME: must restore OUR_EN ??? */ ++ ++ /* all cards provide GPIO info, some have an additional eeprom ++ * LR50: GPIO coding can be found lower right CP1 .. CP9 ++ * CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1. ++ * GPIO14-12: n.c. ++ * LR90: GP9=GPIO23 .. GP1=GPIO15 (right above the bt878) ++ ++ * lowest 3 bytes are remote control codes (no handshake needed) ++ * xxxFFF: No remote control chip soldered ++ * xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered ++ * Note: Some bits are Audio_Mask ! ++ */ ++ ttype = (gpio & 0x0f0000) >> 16; ++ switch (ttype) { ++ case 0x0: ++ tuner_type = 2; /* NTSC, e.g. TPI8NSR11P */ ++ break; ++ case 0x2: ++ tuner_type = 39; /* LG NTSC (newer TAPC series) TAPC-H701P */ ++ break; ++ case 0x4: ++ tuner_type = 5; /* Philips PAL TPI8PSB02P, TPI8PSB12P, TPI8PSB12D or FI1216, FM1216 */ ++ break; ++ case 0x6: ++ tuner_type = 37; /* LG PAL (newer TAPC series) TAPC-G702P */ ++ break; ++ case 0xC: ++ tuner_type = 3; /* Philips SECAM(+PAL) FQ1216ME or FI1216MF */ ++ break; ++ default: ++ pr_info("%d: FlyVideo_gpio: unknown tuner type\n", btv->c.nr); ++ break; ++ } ++ ++ has_remote = gpio & 0x800000; ++ has_radio = gpio & 0x400000; ++ /* unknown 0x200000; ++ * unknown2 0x100000; */ ++ is_capture_only = !(gpio & 0x008000); /* GPIO15 */ ++ has_tda9820_tda9821 = !(gpio & 0x004000); ++ is_lr90 = !(gpio & 0x002000); /* else LR26/LR50 (LR38/LR51 f. capture only) */ ++ /* ++ * gpio & 0x001000 output bit for audio routing */ ++ ++ if (is_capture_only) ++ tuner_type = TUNER_ABSENT; /* No tuner present */ ++ ++ pr_info("%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", ++ btv->c.nr, has_radio ? "yes" : "no", ++ has_remote ? "yes" : "no", tuner_type, gpio); ++ pr_info("%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", ++ btv->c.nr, is_lr90 ? "yes" : "no", ++ has_tda9820_tda9821 ? "yes" : "no", ++ is_capture_only ? "yes" : "no"); ++ ++ if (tuner_type != UNSET) /* only set if known tuner autodetected, else let insmod option through */ ++ btv->tuner_type = tuner_type; ++ btv->has_radio = has_radio; ++ ++ /* LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80 ++ * LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00 ++ * Audio options: from tuner, from tda9821/tda9821(mono,stereo,sap), from tda9874, ext., mute */ ++ if (has_tda9820_tda9821) ++ btv->audio_mode_gpio = lt9415_audio; ++ /* todo: if(has_tda9874) btv->audio_mode_gpio = fv2000s_audio; */ ++} ++ ++static int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1, ++ 14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 }; ++static int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, ++ 1,1,1,1, 1,1,1,0, 0,0,0,0, 0,1,0,0 }; ++ ++static void miro_pinnacle_gpio(struct bttv *btv) ++{ ++ int id,msp,gpio; ++ char *info; ++ ++ gpio_inout(0xffffff, 0); ++ gpio = gpio_read(); ++ id = ((gpio>>10) & 63) -1; ++ msp = bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx"); ++ if (id < 32) { ++ btv->tuner_type = miro_tunermap[id]; ++ if (0 == (gpio & 0x20)) { ++ btv->has_radio = 1; ++ if (!miro_fmtuner[id]) { ++ btv->has_matchbox = 1; ++ btv->mbox_we = (1<<6); ++ btv->mbox_most = (1<<7); ++ btv->mbox_clk = (1<<8); ++ btv->mbox_data = (1<<9); ++ btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9); ++ } ++ } else { ++ btv->has_radio = 0; ++ } ++ if (-1 != msp) { ++ if (btv->c.type == BTTV_BOARD_MIRO) ++ btv->c.type = BTTV_BOARD_MIROPRO; ++ if (btv->c.type == BTTV_BOARD_PINNACLE) ++ btv->c.type = BTTV_BOARD_PINNACLEPRO; ++ } ++ pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", ++ btv->c.nr, id+1, btv->tuner_type, ++ !btv->has_radio ? "no" : ++ (btv->has_matchbox ? "matchbox" : "fmtuner"), ++ (-1 == msp) ? "no" : "yes"); ++ } else { ++ /* new cards with microtune tuner */ ++ id = 63 - id; ++ btv->has_radio = 0; ++ switch (id) { ++ case 1: ++ info = "PAL / mono"; ++ btv->tda9887_conf = TDA9887_INTERCARRIER; ++ break; ++ case 2: ++ info = "PAL+SECAM / stereo"; ++ btv->has_radio = 1; ++ btv->tda9887_conf = TDA9887_QSS; ++ break; ++ case 3: ++ info = "NTSC / stereo"; ++ btv->has_radio = 1; ++ btv->tda9887_conf = TDA9887_QSS; ++ break; ++ case 4: ++ info = "PAL+SECAM / mono"; ++ btv->tda9887_conf = TDA9887_QSS; ++ break; ++ case 5: ++ info = "NTSC / mono"; ++ btv->tda9887_conf = TDA9887_INTERCARRIER; ++ break; ++ case 6: ++ info = "NTSC / stereo"; ++ btv->tda9887_conf = TDA9887_INTERCARRIER; ++ break; ++ case 7: ++ info = "PAL / stereo"; ++ btv->tda9887_conf = TDA9887_INTERCARRIER; ++ break; ++ default: ++ info = "oops: unknown card"; ++ break; ++ } ++ if (-1 != msp) ++ btv->c.type = BTTV_BOARD_PINNACLEPRO; ++ pr_info("%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", ++ btv->c.nr, id, info, btv->has_radio ? "yes" : "no"); ++ btv->tuner_type = TUNER_MT2032; ++ } ++} ++ ++/* GPIO21 L: Buffer aktiv, H: Buffer inaktiv */ ++#define LM1882_SYNC_DRIVE 0x200000L ++ ++static void init_ids_eagle(struct bttv *btv) ++{ ++ gpio_inout(0xffffff,0xFFFF37); ++ gpio_write(0x200020); ++ ++ /* flash strobe inverter ?! */ ++ gpio_write(0x200024); ++ ++ /* switch sync drive off */ ++ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE); ++ ++ /* set BT848 muxel to 2 */ ++ btaor((2)<<5, ~(2<<5), BT848_IFORM); ++} ++ ++/* Muxsel helper for the IDS Eagle. ++ * the eagles does not use the standard muxsel-bits but ++ * has its own multiplexer */ ++static void eagle_muxsel(struct bttv *btv, unsigned int input) ++{ ++ gpio_bits(3, input & 3); ++ ++ /* composite */ ++ /* set chroma ADC to sleep */ ++ btor(BT848_ADC_C_SLEEP, BT848_ADC); ++ /* set to composite video */ ++ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); ++ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); ++ ++ /* switch sync drive off */ ++ gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE); ++} ++ ++static void gvc1100_muxsel(struct bttv *btv, unsigned int input) ++{ ++ static const int masks[] = {0x30, 0x01, 0x12, 0x23}; ++ gpio_write(masks[input%4]); ++} ++ ++/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and ++ alarms output ++ ++ GPIObit | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | ++ assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1| | | ++ ++ IN - sensor inputs, INx - sensor inputs and TI XORed together ++ O1,O2,O3 - alarm outputs (relays) ++ ++ OUT ENABLE 1 1 0 . 1 1 0 0 . 0 0 0 0 = 0x6C0 ++ ++*/ ++ ++static void init_lmlbt4x(struct bttv *btv) ++{ ++ pr_debug("LMLBT4x init\n"); ++ btwrite(0x000000, BT848_GPIO_REG_INP); ++ gpio_inout(0xffffff, 0x0006C0); ++ gpio_write(0x000000); ++} ++ ++static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input) ++{ ++ unsigned int inmux = input % 8; ++ gpio_inout( 0xf, 0xf ); ++ gpio_bits( 0xf, inmux ); ++} ++ ++static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input) ++{ ++ unsigned int inmux = input % 4; ++ gpio_inout( 3<<9, 3<<9 ); ++ gpio_bits( 3<<9, inmux<<9 ); ++} ++ ++static void geovision_muxsel(struct bttv *btv, unsigned int input) ++{ ++ unsigned int inmux = input % 16; ++ gpio_inout(0xf, 0xf); ++ gpio_bits(0xf, inmux); ++} ++ ++/* ++ * The TD3116 has 2 74HC4051 muxes wired to the MUX0 input of a bt878. ++ * The first 74HC4051 has the lower 8 inputs, the second one the higher 8. ++ * The muxes are controlled via a 74HC373 latch which is connected to ++ * GPIOs 0-7. GPIO 18 is connected to the LE signal of the latch. ++ * Q0 of the latch is connected to the Enable (~E) input of the first ++ * 74HC4051. Q1 - Q3 are connected to S0 - S2 of the same 74HC4051. ++ * Q4 - Q7 are connected to the second 74HC4051 in the same way. ++ */ ++ ++static void td3116_latch_value(struct bttv *btv, u32 value) ++{ ++ gpio_bits((1<<18) | 0xff, value); ++ gpio_bits((1<<18) | 0xff, (1<<18) | value); ++ udelay(1); ++ gpio_bits((1<<18) | 0xff, value); ++} ++ ++static void td3116_muxsel(struct bttv *btv, unsigned int input) ++{ ++ u32 value; ++ u32 highbit; ++ ++ highbit = (input & 0x8) >> 3 ; ++ ++ /* Disable outputs and set value in the mux */ ++ value = 0x11; /* Disable outputs */ ++ value |= ((input & 0x7) << 1) << (4 * highbit); ++ td3116_latch_value(btv, value); ++ ++ /* Enable the correct output */ ++ value &= ~0x11; ++ value |= ((highbit ^ 0x1) << 4) | highbit; ++ td3116_latch_value(btv, value); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void bttv_reset_audio(struct bttv *btv) ++{ ++ /* ++ * BT878A has a audio-reset register. ++ * 1. This register is an audio reset function but it is in ++ * function-0 (video capture) address space. ++ * 2. It is enough to do this once per power-up of the card. ++ * 3. There is a typo in the Conexant doc -- it is not at ++ * 0x5B, but at 0x058. (B is an odd-number, obviously a typo!). ++ * --//Shrikumar 030609 ++ */ ++ if (btv->id != 878) ++ return; ++ ++ if (bttv_debug) ++ pr_debug("%d: BT878A ARESET\n", btv->c.nr); ++ btwrite((1<<7), 0x058); ++ udelay(10); ++ btwrite( 0, 0x058); ++} ++ ++/* initialization part one -- before registering i2c bus */ ++void __devinit bttv_init_card1(struct bttv *btv) ++{ ++ switch (btv->c.type) { ++ case BTTV_BOARD_HAUPPAUGE: ++ case BTTV_BOARD_HAUPPAUGE878: ++ boot_msp34xx(btv,5); ++ break; ++ case BTTV_BOARD_VOODOOTV_200: ++ case BTTV_BOARD_VOODOOTV_FM: ++ boot_msp34xx(btv,20); ++ break; ++ case BTTV_BOARD_AVERMEDIA98: ++ boot_msp34xx(btv,11); ++ break; ++ case BTTV_BOARD_HAUPPAUGEPVR: ++ pvr_boot(btv); ++ break; ++ case BTTV_BOARD_TWINHAN_DST: ++ case BTTV_BOARD_AVDVBT_771: ++ case BTTV_BOARD_PINNACLESAT: ++ btv->use_i2c_hw = 1; ++ break; ++ case BTTV_BOARD_ADLINK_RTV24: ++ init_RTV24( btv ); ++ break; ++ ++ } ++ if (!bttv_tvcards[btv->c.type].has_dvb) ++ bttv_reset_audio(btv); ++} ++ ++/* initialization part two -- after registering i2c bus */ ++void __devinit bttv_init_card2(struct bttv *btv) ++{ ++ btv->tuner_type = UNSET; ++ ++ if (BTTV_BOARD_UNKNOWN == btv->c.type) { ++ bttv_readee(btv,eeprom_data,0xa0); ++ identify_by_eeprom(btv,eeprom_data); ++ } ++ ++ switch (btv->c.type) { ++ case BTTV_BOARD_MIRO: ++ case BTTV_BOARD_MIROPRO: ++ case BTTV_BOARD_PINNACLE: ++ case BTTV_BOARD_PINNACLEPRO: ++ /* miro/pinnacle */ ++ miro_pinnacle_gpio(btv); ++ break; ++ case BTTV_BOARD_FLYVIDEO_98: ++ case BTTV_BOARD_MAXI: ++ case BTTV_BOARD_LIFE_FLYKIT: ++ case BTTV_BOARD_FLYVIDEO: ++ case BTTV_BOARD_TYPHOON_TVIEW: ++ case BTTV_BOARD_CHRONOS_VS2: ++ case BTTV_BOARD_FLYVIDEO_98FM: ++ case BTTV_BOARD_FLYVIDEO2000: ++ case BTTV_BOARD_FLYVIDEO98EZ: ++ case BTTV_BOARD_CONFERENCETV: ++ case BTTV_BOARD_LIFETEC_9415: ++ flyvideo_gpio(btv); ++ break; ++ case BTTV_BOARD_HAUPPAUGE: ++ case BTTV_BOARD_HAUPPAUGE878: ++ case BTTV_BOARD_HAUPPAUGEPVR: ++ /* pick up some config infos from the eeprom */ ++ bttv_readee(btv,eeprom_data,0xa0); ++ hauppauge_eeprom(btv); ++ break; ++ case BTTV_BOARD_AVERMEDIA98: ++ case BTTV_BOARD_AVPHONE98: ++ bttv_readee(btv,eeprom_data,0xa0); ++ avermedia_eeprom(btv); ++ break; ++ case BTTV_BOARD_PXC200: ++ init_PXC200(btv); ++ break; ++ case BTTV_BOARD_PICOLO_TETRA_CHIP: ++ picolo_tetra_init(btv); ++ break; ++ case BTTV_BOARD_VHX: ++ btv->has_radio = 1; ++ btv->has_matchbox = 1; ++ btv->mbox_we = 0x20; ++ btv->mbox_most = 0; ++ btv->mbox_clk = 0x08; ++ btv->mbox_data = 0x10; ++ btv->mbox_mask = 0x38; ++ break; ++ case BTTV_BOARD_VOBIS_BOOSTAR: ++ case BTTV_BOARD_TERRATV: ++ terratec_active_radio_upgrade(btv); ++ break; ++ case BTTV_BOARD_MAGICTVIEW061: ++ if (btv->cardid == 0x3002144f) { ++ btv->has_radio=1; ++ pr_info("%d: radio detected by subsystem id (CPH05x)\n", ++ btv->c.nr); ++ } ++ break; ++ case BTTV_BOARD_STB2: ++ if (btv->cardid == 0x3060121a) { ++ /* Fix up entry for 3DFX VoodooTV 100, ++ which is an OEM STB card variant. */ ++ btv->has_radio=0; ++ btv->tuner_type=TUNER_TEMIC_NTSC; ++ } ++ break; ++ case BTTV_BOARD_OSPREY1x0: ++ case BTTV_BOARD_OSPREY1x0_848: ++ case BTTV_BOARD_OSPREY101_848: ++ case BTTV_BOARD_OSPREY1x1: ++ case BTTV_BOARD_OSPREY1x1_SVID: ++ case BTTV_BOARD_OSPREY2xx: ++ case BTTV_BOARD_OSPREY2x0_SVID: ++ case BTTV_BOARD_OSPREY2x0: ++ case BTTV_BOARD_OSPREY440: ++ case BTTV_BOARD_OSPREY500: ++ case BTTV_BOARD_OSPREY540: ++ case BTTV_BOARD_OSPREY2000: ++ bttv_readee(btv,eeprom_data,0xa0); ++ osprey_eeprom(btv, eeprom_data); ++ break; ++ case BTTV_BOARD_IDS_EAGLE: ++ init_ids_eagle(btv); ++ break; ++ case BTTV_BOARD_MODTEC_205: ++ bttv_readee(btv,eeprom_data,0xa0); ++ modtec_eeprom(btv); ++ break; ++ case BTTV_BOARD_LMLBT4: ++ init_lmlbt4x(btv); ++ break; ++ case BTTV_BOARD_TIBET_CS16: ++ tibetCS16_init(btv); ++ break; ++ case BTTV_BOARD_KODICOM_4400R: ++ kodicom4400r_init(btv); ++ break; ++ case BTTV_BOARD_GEOVISION_GV800S: ++ gv800s_init(btv); ++ break; ++ } ++ ++ /* pll configuration */ ++ if (!(btv->id==848 && btv->revision==0x11)) { ++ /* defaults from card list */ ++ if (PLL_28 == bttv_tvcards[btv->c.type].pll) { ++ btv->pll.pll_ifreq=28636363; ++ btv->pll.pll_crystal=BT848_IFORM_XT0; ++ } ++ if (PLL_35 == bttv_tvcards[btv->c.type].pll) { ++ btv->pll.pll_ifreq=35468950; ++ btv->pll.pll_crystal=BT848_IFORM_XT1; ++ } ++ /* insmod options can override */ ++ switch (pll[btv->c.nr]) { ++ case 0: /* none */ ++ btv->pll.pll_crystal = 0; ++ btv->pll.pll_ifreq = 0; ++ btv->pll.pll_ofreq = 0; ++ break; ++ case 1: /* 28 MHz */ ++ case 28: ++ btv->pll.pll_ifreq = 28636363; ++ btv->pll.pll_ofreq = 0; ++ btv->pll.pll_crystal = BT848_IFORM_XT0; ++ break; ++ case 2: /* 35 MHz */ ++ case 35: ++ btv->pll.pll_ifreq = 35468950; ++ btv->pll.pll_ofreq = 0; ++ btv->pll.pll_crystal = BT848_IFORM_XT1; ++ break; ++ } ++ } ++ btv->pll.pll_current = -1; ++ ++ /* tuner configuration (from card list / autodetect / insmod option) */ ++ if (UNSET != bttv_tvcards[btv->c.type].tuner_type) ++ if (UNSET == btv->tuner_type) ++ btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; ++ if (UNSET != tuner[btv->c.nr]) ++ btv->tuner_type = tuner[btv->c.nr]; ++ ++ if (btv->tuner_type == TUNER_ABSENT) ++ pr_info("%d: tuner absent\n", btv->c.nr); ++ else if (btv->tuner_type == UNSET) ++ pr_warn("%d: tuner type unset\n", btv->c.nr); ++ else ++ pr_info("%d: tuner type=%d\n", btv->c.nr, btv->tuner_type); ++ ++ if (autoload != UNSET) { ++ pr_warn("%d: the autoload option is obsolete\n", btv->c.nr); ++ pr_warn("%d: use option msp3400, tda7432 or tvaudio to override which audio module should be used\n", ++ btv->c.nr); ++ } ++ ++ if (UNSET == btv->tuner_type) ++ btv->tuner_type = TUNER_ABSENT; ++ ++ btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? ++ bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET; ++ btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ? ++ UNSET : bttv_tvcards[btv->c.type].svhs; ++ if (svhs[btv->c.nr] != UNSET) ++ btv->svhs = svhs[btv->c.nr]; ++ if (remote[btv->c.nr] != UNSET) ++ btv->has_remote = remote[btv->c.nr]; ++ ++ if (bttv_tvcards[btv->c.type].has_radio) ++ btv->has_radio = 1; ++ if (bttv_tvcards[btv->c.type].has_remote) ++ btv->has_remote = 1; ++ if (!bttv_tvcards[btv->c.type].no_gpioirq) ++ btv->gpioirq = 1; ++ if (bttv_tvcards[btv->c.type].volume_gpio) ++ btv->volume_gpio = bttv_tvcards[btv->c.type].volume_gpio; ++ if (bttv_tvcards[btv->c.type].audio_mode_gpio) ++ btv->audio_mode_gpio = bttv_tvcards[btv->c.type].audio_mode_gpio; ++ ++ if (btv->tuner_type == TUNER_ABSENT) ++ return; /* no tuner or related drivers to load */ ++ ++ if (btv->has_saa6588 || saa6588[btv->c.nr]) { ++ /* Probe for RDS receiver chip */ ++ static const unsigned short addrs[] = { ++ 0x20 >> 1, ++ 0x22 >> 1, ++ I2C_CLIENT_END ++ }; ++ struct v4l2_subdev *sd; ++ ++ sd = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "saa6588", 0, addrs); ++ btv->has_saa6588 = (sd != NULL); ++ } ++ ++ /* try to detect audio/fader chips */ ++ ++ /* First check if the user specified the audio chip via a module ++ option. */ ++ ++ switch (audiodev[btv->c.nr]) { ++ case -1: ++ return; /* do not load any audio module */ ++ ++ case 0: /* autodetect */ ++ break; ++ ++ case 1: { ++ /* The user specified that we should probe for msp3400 */ ++ static const unsigned short addrs[] = { ++ I2C_ADDR_MSP3400 >> 1, ++ I2C_ADDR_MSP3400_ALT >> 1, ++ I2C_CLIENT_END ++ }; ++ ++ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "msp3400", 0, addrs); ++ if (btv->sd_msp34xx) ++ return; ++ goto no_audio; ++ } ++ ++ case 2: { ++ /* The user specified that we should probe for tda7432 */ ++ static const unsigned short addrs[] = { ++ I2C_ADDR_TDA7432 >> 1, ++ I2C_CLIENT_END ++ }; ++ ++ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tda7432", 0, addrs)) ++ return; ++ goto no_audio; ++ } ++ ++ case 3: { ++ /* The user specified that we should probe for tvaudio */ ++ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs()); ++ if (btv->sd_tvaudio) ++ return; ++ goto no_audio; ++ } ++ ++ default: ++ pr_warn("%d: unknown audiodev value!\n", btv->c.nr); ++ return; ++ } ++ ++ /* There were no overrides, so now we try to discover this through the ++ card definition */ ++ ++ /* probe for msp3400 first: this driver can detect whether or not ++ it really is a msp3400, so it will return NULL when the device ++ found is really something else (e.g. a tea6300). */ ++ if (!bttv_tvcards[btv->c.type].no_msp34xx) { ++ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "msp3400", ++ 0, I2C_ADDRS(I2C_ADDR_MSP3400 >> 1)); ++ } else if (bttv_tvcards[btv->c.type].msp34xx_alt) { ++ btv->sd_msp34xx = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "msp3400", ++ 0, I2C_ADDRS(I2C_ADDR_MSP3400_ALT >> 1)); ++ } ++ ++ /* If we found a msp34xx, then we're done. */ ++ if (btv->sd_msp34xx) ++ return; ++ ++ /* it might also be a tda7432. */ ++ if (!bttv_tvcards[btv->c.type].no_tda7432) { ++ static const unsigned short addrs[] = { ++ I2C_ADDR_TDA7432 >> 1, ++ I2C_CLIENT_END ++ }; ++ ++ if (v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tda7432", 0, addrs)) ++ return; ++ } ++ ++ /* Now see if we can find one of the tvaudio devices. */ ++ btv->sd_tvaudio = v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tvaudio", 0, tvaudio_addrs()); ++ if (btv->sd_tvaudio) ++ return; ++ ++no_audio: ++ pr_warn("%d: audio absent, no audio device found!\n", btv->c.nr); ++} ++ ++ ++/* initialize the tuner */ ++void __devinit bttv_init_tuner(struct bttv *btv) ++{ ++ int addr = ADDR_UNSET; ++ ++ if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) ++ addr = bttv_tvcards[btv->c.type].tuner_addr; ++ ++ if (btv->tuner_type != TUNER_ABSENT) { ++ struct tuner_setup tun_setup; ++ ++ /* Load tuner module before issuing tuner config call! */ ++ if (btv->has_radio) ++ v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO)); ++ v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); ++ v4l2_i2c_new_subdev(&btv->c.v4l2_dev, ++ &btv->c.i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); ++ ++ tun_setup.mode_mask = T_ANALOG_TV; ++ tun_setup.type = btv->tuner_type; ++ tun_setup.addr = addr; ++ ++ if (btv->has_radio) ++ tun_setup.mode_mask |= T_RADIO; ++ ++ bttv_call_all(btv, tuner, s_type_addr, &tun_setup); ++ } ++ ++ if (btv->tda9887_conf) { ++ struct v4l2_priv_tun_config tda9887_cfg; ++ ++ tda9887_cfg.tuner = TUNER_TDA9887; ++ tda9887_cfg.priv = &btv->tda9887_conf; ++ ++ bttv_call_all(btv, tuner, s_config, &tda9887_cfg); ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void modtec_eeprom(struct bttv *btv) ++{ ++ if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) { ++ btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I; ++ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", ++ btv->c.nr, &eeprom_data[0x1e]); ++ } else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) { ++ btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I; ++ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", ++ btv->c.nr, &eeprom_data[0x1e]); ++ } else if (strncmp(&(eeprom_data[0x1e]),"Philips FM1246",14) ==0) { ++ btv->tuner_type=TUNER_PHILIPS_NTSC; ++ pr_info("%d: Modtec: Tuner autodetected by eeprom: %s\n", ++ btv->c.nr, &eeprom_data[0x1e]); ++ } else { ++ pr_info("%d: Modtec: Unknown TunerString: %s\n", ++ btv->c.nr, &eeprom_data[0x1e]); ++ } ++} ++ ++static void __devinit hauppauge_eeprom(struct bttv *btv) ++{ ++ struct tveeprom tv; ++ ++ tveeprom_hauppauge_analog(&btv->i2c_client, &tv, eeprom_data); ++ btv->tuner_type = tv.tuner_type; ++ btv->has_radio = tv.has_radio; ++ ++ pr_info("%d: Hauppauge eeprom indicates model#%d\n", ++ btv->c.nr, tv.model); ++ ++ /* ++ * Some of the 878 boards have duplicate PCI IDs. Switch the board ++ * type based on model #. ++ */ ++ if(tv.model == 64900) { ++ pr_info("%d: Switching board type from %s to %s\n", ++ btv->c.nr, ++ bttv_tvcards[btv->c.type].name, ++ bttv_tvcards[BTTV_BOARD_HAUPPAUGE_IMPACTVCB].name); ++ btv->c.type = BTTV_BOARD_HAUPPAUGE_IMPACTVCB; ++ } ++ ++ /* The 61334 needs the msp3410 to do the radio demod to get sound */ ++ if (tv.model == 61334) ++ btv->radio_uses_msp_demodulator = 1; ++} ++ ++static int terratec_active_radio_upgrade(struct bttv *btv) ++{ ++ int freq; ++ ++ btv->has_radio = 1; ++ btv->has_matchbox = 1; ++ btv->mbox_we = 0x10; ++ btv->mbox_most = 0x20; ++ btv->mbox_clk = 0x08; ++ btv->mbox_data = 0x04; ++ btv->mbox_mask = 0x3c; ++ ++ btv->mbox_iow = 1 << 8; ++ btv->mbox_ior = 1 << 9; ++ btv->mbox_csel = 1 << 10; ++ ++ freq=88000/62.5; ++ tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */ ++ if (0x1ed8 == tea5757_read(btv)) { ++ pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr); ++ btv->has_radio = 1; ++ btv->has_saa6588 = 1; ++ btv->has_matchbox = 1; ++ } else { ++ btv->has_radio = 0; ++ btv->has_matchbox = 0; ++ } ++ return 0; ++} ++ ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* ++ * minimal bootstrap for the WinTV/PVR -- upload altera firmware. ++ * ++ * The hcwamc.rbf firmware file is on the Hauppauge driver CD. Have ++ * a look at Pvr/pvr45xxx.EXE (self-extracting zip archive, can be ++ * unpacked with unzip). ++ */ ++#define PVR_GPIO_DELAY 10 ++ ++#define BTTV_ALT_DATA 0x000001 ++#define BTTV_ALT_DCLK 0x100000 ++#define BTTV_ALT_NCONFIG 0x800000 ++ ++static int __devinit pvr_altera_load(struct bttv *btv, const u8 *micro, ++ u32 microlen) ++{ ++ u32 n; ++ u8 bits; ++ int i; ++ ++ gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG); ++ gpio_write(0); ++ udelay(PVR_GPIO_DELAY); ++ ++ gpio_write(BTTV_ALT_NCONFIG); ++ udelay(PVR_GPIO_DELAY); ++ ++ for (n = 0; n < microlen; n++) { ++ bits = micro[n]; ++ for (i = 0 ; i < 8 ; i++) { ++ gpio_bits(BTTV_ALT_DCLK,0); ++ if (bits & 0x01) ++ gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA); ++ else ++ gpio_bits(BTTV_ALT_DATA,0); ++ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK); ++ bits >>= 1; ++ } ++ } ++ gpio_bits(BTTV_ALT_DCLK,0); ++ udelay(PVR_GPIO_DELAY); ++ ++ /* begin Altera init loop (Not necessary,but doesn't hurt) */ ++ for (i = 0 ; i < 30 ; i++) { ++ gpio_bits(BTTV_ALT_DCLK,0); ++ gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK); ++ } ++ gpio_bits(BTTV_ALT_DCLK,0); ++ return 0; ++} ++ ++static int __devinit pvr_boot(struct bttv *btv) ++{ ++ const struct firmware *fw_entry; ++ int rc; ++ ++ rc = request_firmware(&fw_entry, "hcwamc.rbf", &btv->c.pci->dev); ++ if (rc != 0) { ++ pr_warn("%d: no altera firmware [via hotplug]\n", btv->c.nr); ++ return rc; ++ } ++ rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size); ++ pr_info("%d: altera firmware upload %s\n", ++ btv->c.nr, (rc < 0) ? "failed" : "ok"); ++ release_firmware(fw_entry); ++ return rc; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* some osprey specific stuff */ ++ ++static void __devinit osprey_eeprom(struct bttv *btv, const u8 ee[256]) ++{ ++ int i; ++ u32 serial = 0; ++ int cardid = -1; ++ ++ /* This code will nevery actually get called in this case.... */ ++ if (btv->c.type == BTTV_BOARD_UNKNOWN) { ++ /* this might be an antique... check for MMAC label in eeprom */ ++ if (!strncmp(ee, "MMAC", 4)) { ++ u8 checksum = 0; ++ for (i = 0; i < 21; i++) ++ checksum += ee[i]; ++ if (checksum != ee[21]) ++ return; ++ cardid = BTTV_BOARD_OSPREY1x0_848; ++ for (i = 12; i < 21; i++) ++ serial *= 10, serial += ee[i] - '0'; ++ } ++ } else { ++ unsigned short type; ++ ++ for (i = 4*16; i < 8*16; i += 16) { ++ u16 checksum = ip_compute_csum(ee + i, 16); ++ ++ if ((checksum&0xff) + (checksum>>8) == 0xff) ++ break; ++ } ++ if (i >= 8*16) ++ return; ++ ee += i; ++ ++ /* found a valid descriptor */ ++ type = get_unaligned_be16((__be16 *)(ee+4)); ++ ++ switch(type) { ++ /* 848 based */ ++ case 0x0004: ++ cardid = BTTV_BOARD_OSPREY1x0_848; ++ break; ++ case 0x0005: ++ cardid = BTTV_BOARD_OSPREY101_848; ++ break; ++ ++ /* 878 based */ ++ case 0x0012: ++ case 0x0013: ++ cardid = BTTV_BOARD_OSPREY1x0; ++ break; ++ case 0x0014: ++ case 0x0015: ++ cardid = BTTV_BOARD_OSPREY1x1; ++ break; ++ case 0x0016: ++ case 0x0017: ++ case 0x0020: ++ cardid = BTTV_BOARD_OSPREY1x1_SVID; ++ break; ++ case 0x0018: ++ case 0x0019: ++ case 0x001E: ++ case 0x001F: ++ cardid = BTTV_BOARD_OSPREY2xx; ++ break; ++ case 0x001A: ++ case 0x001B: ++ cardid = BTTV_BOARD_OSPREY2x0_SVID; ++ break; ++ case 0x0040: ++ cardid = BTTV_BOARD_OSPREY500; ++ break; ++ case 0x0050: ++ case 0x0056: ++ cardid = BTTV_BOARD_OSPREY540; ++ /* bttv_osprey_540_init(btv); */ ++ break; ++ case 0x0060: ++ case 0x0070: ++ case 0x00A0: ++ cardid = BTTV_BOARD_OSPREY2x0; ++ /* enable output on select control lines */ ++ gpio_inout(0xffffff,0x000303); ++ break; ++ case 0x00D8: ++ cardid = BTTV_BOARD_OSPREY440; ++ break; ++ default: ++ /* unknown...leave generic, but get serial # */ ++ pr_info("%d: osprey eeprom: unknown card type 0x%04x\n", ++ btv->c.nr, type); ++ break; ++ } ++ serial = get_unaligned_be32((__be32 *)(ee+6)); ++ } ++ ++ pr_info("%d: osprey eeprom: card=%d '%s' serial=%u\n", ++ btv->c.nr, cardid, ++ cardid > 0 ? bttv_tvcards[cardid].name : "Unknown", serial); ++ ++ if (cardid<0 || btv->c.type == cardid) ++ return; ++ ++ /* card type isn't set correctly */ ++ if (card[btv->c.nr] < bttv_num_tvcards) { ++ pr_warn("%d: osprey eeprom: Not overriding user specified card type\n", ++ btv->c.nr); ++ } else { ++ pr_info("%d: osprey eeprom: Changing card type from %d to %d\n", ++ btv->c.nr, btv->c.type, cardid); ++ btv->c.type = cardid; ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* AVermedia specific stuff, from bktr_card.c */ ++ ++static int tuner_0_table[] = { ++ TUNER_PHILIPS_NTSC, TUNER_PHILIPS_PAL /* PAL-BG*/, ++ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL /* PAL-I*/, ++ TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL, ++ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM, ++ TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL, ++ TUNER_PHILIPS_FM1216ME_MK3 }; ++ ++static int tuner_1_table[] = { ++ TUNER_TEMIC_NTSC, TUNER_TEMIC_PAL, ++ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL, ++ TUNER_TEMIC_PAL, TUNER_TEMIC_PAL, ++ TUNER_TEMIC_4012FY5, TUNER_TEMIC_4012FY5, /* TUNER_TEMIC_SECAM */ ++ TUNER_TEMIC_4012FY5, TUNER_TEMIC_PAL}; ++ ++static void __devinit avermedia_eeprom(struct bttv *btv) ++{ ++ int tuner_make, tuner_tv_fm, tuner_format, tuner_type = 0; ++ ++ tuner_make = (eeprom_data[0x41] & 0x7); ++ tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3; ++ tuner_format = (eeprom_data[0x42] & 0xf0) >> 4; ++ btv->has_remote = (eeprom_data[0x42] & 0x01); ++ ++ if (tuner_make == 0 || tuner_make == 2) ++ if (tuner_format <= 0x0a) ++ tuner_type = tuner_0_table[tuner_format]; ++ if (tuner_make == 1) ++ if (tuner_format <= 9) ++ tuner_type = tuner_1_table[tuner_format]; ++ ++ if (tuner_make == 4) ++ if (tuner_format == 0x09) ++ tuner_type = TUNER_LG_NTSC_NEW_TAPC; /* TAPC-G702P */ ++ ++ pr_info("%d: Avermedia eeprom[0x%02x%02x]: tuner=", ++ btv->c.nr, eeprom_data[0x41], eeprom_data[0x42]); ++ if (tuner_type) { ++ btv->tuner_type = tuner_type; ++ pr_cont("%d", tuner_type); ++ } else ++ pr_cont("Unknown type"); ++ pr_cont(" radio:%s remote control:%s\n", ++ tuner_tv_fm ? "yes" : "no", ++ btv->has_remote ? "yes" : "no"); ++} ++ ++/* ++ * For Voodoo TV/FM and Voodoo 200. These cards' tuners use a TDA9880 ++ * analog demod, which is not I2C controlled like the newer and more common ++ * TDA9887 series. Instead is has two tri-state input pins, S0 and S1, ++ * that control the IF for the video and audio. Apparently, bttv GPIO ++ * 0x10000 is connected to S0. S0 low selects a 38.9 MHz VIF for B/G/D/K/I ++ * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC). ++ */ ++u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits) ++{ ++ ++ if (btv->audio == TVAUDIO_INPUT_TUNER) { ++ if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN) ++ gpiobits |= 0x10000; ++ else ++ gpiobits &= ~0x10000; ++ } ++ ++ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits); ++ return gpiobits; ++} ++ ++ ++/* ++ * reset/enable the MSP on some Hauppauge cards ++ * Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! ++ * ++ * Hauppauge: pin 5 ++ * Voodoo: pin 20 ++ */ ++static void __devinit boot_msp34xx(struct bttv *btv, int pin) ++{ ++ int mask = (1 << pin); ++ ++ gpio_inout(mask,mask); ++ gpio_bits(mask,0); ++ mdelay(2); ++ udelay(500); ++ gpio_bits(mask,mask); ++ ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"msp34xx"); ++ if (bttv_verbose) ++ pr_info("%d: Hauppauge/Voodoo msp34xx: reset line init [%d]\n", ++ btv->c.nr, pin); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* Imagenation L-Model PXC200 Framegrabber */ ++/* This is basically the same procedure as ++ * used by Alessandro Rubini in his pxc200 ++ * driver, but using BTTV functions */ ++ ++static void __devinit init_PXC200(struct bttv *btv) ++{ ++ static int vals[] __devinitdata = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d, ++ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, ++ 0x00 }; ++ unsigned int i; ++ int tmp; ++ u32 val; ++ ++ /* Initialise GPIO-connevted stuff */ ++ gpio_inout(0xffffff, (1<<13)); ++ gpio_write(0); ++ udelay(3); ++ gpio_write(1<<13); ++ /* GPIO inputs are pulled up, so no need to drive ++ * reset pin any longer */ ++ gpio_bits(0xffffff, 0); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"pxc200"); ++ ++ /* we could/should try and reset/control the AD pots? but ++ right now we simply turned off the crushing. Without ++ this the AGC drifts drifts ++ remember the EN is reverse logic --> ++ setting BT848_ADC_AGC_EN disable the AGC ++ tboult@eecs.lehigh.edu ++ */ ++ ++ btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC); ++ ++ /* Initialise MAX517 DAC */ ++ pr_info("Setting DAC reference voltage level ...\n"); ++ bttv_I2CWrite(btv,0x5E,0,0x80,1); ++ ++ /* Initialise 12C508 PIC */ ++ /* The I2CWrite and I2CRead commmands are actually to the ++ * same chips - but the R/W bit is included in the address ++ * argument so the numbers are different */ ++ ++ ++ pr_info("Initialising 12C508 PIC chip ...\n"); ++ ++ /* First of all, enable the clock line. This is used in the PXC200-F */ ++ val = btread(BT848_GPIO_DMA_CTL); ++ val |= BT848_GPIO_DMA_CTL_GPCLKMODE; ++ btwrite(val, BT848_GPIO_DMA_CTL); ++ ++ /* Then, push to 0 the reset pin long enough to reset the * ++ * device same as above for the reset line, but not the same ++ * value sent to the GPIO-connected stuff ++ * which one is the good one? */ ++ gpio_inout(0xffffff,(1<<2)); ++ gpio_write(0); ++ udelay(10); ++ gpio_write(1<<2); ++ ++ for (i = 0; i < ARRAY_SIZE(vals); i++) { ++ tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1); ++ if (tmp != -1) { ++ pr_info("I2C Write(%2.2x) = %i\nI2C Read () = %2.2x\n\n", ++ vals[i],tmp,bttv_I2CRead(btv,0x1F,NULL)); ++ } ++ } ++ ++ pr_info("PXC200 Initialised\n"); ++} ++ ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* ++ * The Adlink RTV-24 (aka Angelo) has some special initialisation to unlock ++ * it. This apparently involves the following procedure for each 878 chip: ++ * ++ * 1) write 0x00C3FEFF to the GPIO_OUT_EN register ++ * ++ * 2) write to GPIO_DATA ++ * - 0x0E ++ * - sleep 1ms ++ * - 0x10 + 0x0E ++ * - sleep 10ms ++ * - 0x0E ++ * read from GPIO_DATA into buf (uint_32) ++ * - if ( data>>18 & 0x01 != 0) || ( buf>>19 & 0x01 != 1 ) ++ * error. ERROR_CPLD_Check_Failed stop. ++ * ++ * 3) write to GPIO_DATA ++ * - write 0x4400 + 0x0E ++ * - sleep 10ms ++ * - write 0x4410 + 0x0E ++ * - sleep 1ms ++ * - write 0x0E ++ * read from GPIO_DATA into buf (uint_32) ++ * - if ( buf>>18 & 0x01 ) || ( buf>>19 & 0x01 != 0 ) ++ * error. ERROR_CPLD_Check_Failed. ++ */ ++/* ----------------------------------------------------------------------- */ ++static void ++init_RTV24 (struct bttv *btv) ++{ ++ uint32_t dataRead = 0; ++ long watchdog_value = 0x0E; ++ ++ pr_info("%d: Adlink RTV-24 initialisation in progress ...\n", ++ btv->c.nr); ++ ++ btwrite (0x00c3feff, BT848_GPIO_OUT_EN); ++ ++ btwrite (0 + watchdog_value, BT848_GPIO_DATA); ++ msleep (1); ++ btwrite (0x10 + watchdog_value, BT848_GPIO_DATA); ++ msleep (10); ++ btwrite (0 + watchdog_value, BT848_GPIO_DATA); ++ ++ dataRead = btread (BT848_GPIO_DATA); ++ ++ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 1)) { ++ pr_info("%d: Adlink RTV-24 initialisation(1) ERROR_CPLD_Check_Failed (read %d)\n", ++ btv->c.nr, dataRead); ++ } ++ ++ btwrite (0x4400 + watchdog_value, BT848_GPIO_DATA); ++ msleep (10); ++ btwrite (0x4410 + watchdog_value, BT848_GPIO_DATA); ++ msleep (1); ++ btwrite (watchdog_value, BT848_GPIO_DATA); ++ msleep (1); ++ dataRead = btread (BT848_GPIO_DATA); ++ ++ if ((((dataRead >> 18) & 0x01) != 0) || (((dataRead >> 19) & 0x01) != 0)) { ++ pr_info("%d: Adlink RTV-24 initialisation(2) ERROR_CPLD_Check_Failed (read %d)\n", ++ btv->c.nr, dataRead); ++ ++ return; ++ } ++ ++ pr_info("%d: Adlink RTV-24 initialisation complete\n", btv->c.nr); ++} ++ ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */ ++/* ++ * Copyright (c) 1999 Csaba Halasz ++ * This code is placed under the terms of the GNU General Public License ++ * ++ * Brutally hacked by Dan Sheridan djs52 8/3/00 ++ */ ++ ++static void bus_low(struct bttv *btv, int bit) ++{ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, ++ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); ++ udelay(5); ++ } ++ ++ gpio_bits(bit,0); ++ udelay(5); ++ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); ++ udelay(5); ++ } ++} ++ ++static void bus_high(struct bttv *btv, int bit) ++{ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, ++ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); ++ udelay(5); ++ } ++ ++ gpio_bits(bit,bit); ++ udelay(5); ++ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); ++ udelay(5); ++ } ++} ++ ++static int bus_in(struct bttv *btv, int bit) ++{ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, ++ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); ++ udelay(5); ++ ++ gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); ++ udelay(5); ++ } ++ return gpio_read() & (bit); ++} ++ ++/* TEA5757 register bits */ ++#define TEA_FREQ 0:14 ++#define TEA_BUFFER 15:15 ++ ++#define TEA_SIGNAL_STRENGTH 16:17 ++ ++#define TEA_PORT1 18:18 ++#define TEA_PORT0 19:19 ++ ++#define TEA_BAND 20:21 ++#define TEA_BAND_FM 0 ++#define TEA_BAND_MW 1 ++#define TEA_BAND_LW 2 ++#define TEA_BAND_SW 3 ++ ++#define TEA_MONO 22:22 ++#define TEA_ALLOW_STEREO 0 ++#define TEA_FORCE_MONO 1 ++ ++#define TEA_SEARCH_DIRECTION 23:23 ++#define TEA_SEARCH_DOWN 0 ++#define TEA_SEARCH_UP 1 ++ ++#define TEA_STATUS 24:24 ++#define TEA_STATUS_TUNED 0 ++#define TEA_STATUS_SEARCHING 1 ++ ++/* Low-level stuff */ ++static int tea5757_read(struct bttv *btv) ++{ ++ unsigned long timeout; ++ int value = 0; ++ int i; ++ ++ /* better safe than sorry */ ++ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we); ++ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, ++ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); ++ udelay(5); ++ } ++ ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"tea5757 read"); ++ ++ bus_low(btv,btv->mbox_we); ++ bus_low(btv,btv->mbox_clk); ++ ++ udelay(10); ++ timeout= jiffies + msecs_to_jiffies(1000); ++ ++ /* wait for DATA line to go low; error if it doesn't */ ++ while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout)) ++ schedule(); ++ if (bus_in(btv,btv->mbox_data)) { ++ pr_warn("%d: tea5757: read timeout\n", btv->c.nr); ++ return -1; ++ } ++ ++ dprintk("%d: tea5757:", btv->c.nr); ++ for (i = 0; i < 24; i++) { ++ udelay(5); ++ bus_high(btv,btv->mbox_clk); ++ udelay(5); ++ dprintk_cont("%c", ++ bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-'); ++ bus_low(btv,btv->mbox_clk); ++ value <<= 1; ++ value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */ ++ dprintk_cont("%c", ++ bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M'); ++ } ++ dprintk_cont("\n"); ++ dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value); ++ return value; ++} ++ ++static int tea5757_write(struct bttv *btv, int value) ++{ ++ int i; ++ int reg = value; ++ ++ gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data); ++ ++ if (btv->mbox_ior) { ++ gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, ++ btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); ++ udelay(5); ++ } ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"tea5757 write"); ++ ++ dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value); ++ bus_low(btv,btv->mbox_clk); ++ bus_high(btv,btv->mbox_we); ++ for (i = 0; i < 25; i++) { ++ if (reg & 0x1000000) ++ bus_high(btv,btv->mbox_data); ++ else ++ bus_low(btv,btv->mbox_data); ++ reg <<= 1; ++ bus_high(btv,btv->mbox_clk); ++ udelay(10); ++ bus_low(btv,btv->mbox_clk); ++ udelay(10); ++ } ++ bus_low(btv,btv->mbox_we); /* unmute !!! */ ++ return 0; ++} ++ ++void tea5757_set_freq(struct bttv *btv, unsigned short freq) ++{ ++ dprintk("tea5757_set_freq %d\n",freq); ++ tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */ ++} ++ ++/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas] ++ * ++ * This is needed because rv605 don't use a normal multiplex, but a crosspoint ++ * switch instead (CD22M3494E). This IC can have multiple active connections ++ * between Xn (input) and Yn (output) pins. We need to clear any existing ++ * connection prior to establish a new one, pulsing the STROBE pin. ++ * ++ * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin. ++ * GPIO pins are wired as: ++ * GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroller) ++ * GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroller) ++ * GPIO[7] - DATA (xpoint) - P1[7] (microcontroller) ++ * GPIO[8] - - P3[5] (microcontroller) ++ * GPIO[9] - RESET (xpoint) - P3[6] (microcontroller) ++ * GPIO[10] - STROBE (xpoint) - P3[7] (microcontroller) ++ * GPINTR - - P3[4] (microcontroller) ++ * ++ * The microcontroller is a 80C32 like. It should be possible to change xpoint ++ * configuration either directly (as we are doing) or using the microcontroller ++ * which is also wired to I2C interface. I have no further info on the ++ * microcontroller features, one would need to disassembly the firmware. ++ * note: the vendor refused to give any information on this product, all ++ * that stuff was found using a multimeter! :) ++ */ ++static void rv605_muxsel(struct bttv *btv, unsigned int input) ++{ ++ static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0, ++ 0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa }; ++ ++ gpio_bits(0x07f, muxgpio[input]); ++ ++ /* reset all conections */ ++ gpio_bits(0x200,0x200); ++ mdelay(1); ++ gpio_bits(0x200,0x000); ++ mdelay(1); ++ ++ /* create a new connection */ ++ gpio_bits(0x480,0x480); ++ mdelay(1); ++ gpio_bits(0x480,0x080); ++ mdelay(1); ++} ++ ++/* Tibet Systems 'Progress DVR' CS16 muxsel helper [Chris Fanning] ++ * ++ * The CS16 (available on eBay cheap) is a PCI board with four Fusion ++ * 878A chips, a PCI bridge, an Atmel microcontroller, four sync separator ++ * chips, ten eight input analog multiplexors, a not chip and a few ++ * other components. ++ * ++ * 16 inputs on a secondary bracket are provided and can be selected ++ * from each of the four capture chips. Two of the eight input ++ * multiplexors are used to select from any of the 16 input signals. ++ * ++ * Unsupported hardware capabilities: ++ * . A video output monitor on the secondary bracket can be selected from ++ * one of the 878A chips. ++ * . Another passthrough but I haven't spent any time investigating it. ++ * . Digital I/O (logic level connected to GPIO) is available from an ++ * onboard header. ++ * ++ * The on chip input mux should always be set to 2. ++ * GPIO[16:19] - Video input selection ++ * GPIO[0:3] - Video output monitor select (only available from one 878A) ++ * GPIO[?:?] - Digital I/O. ++ * ++ * There is an ATMEL microcontroller with an 8031 core on board. I have not ++ * determined what function (if any) it provides. With the microcontroller ++ * and sync separator chips a guess is that it might have to do with video ++ * switching and maybe some digital I/O. ++ */ ++static void tibetCS16_muxsel(struct bttv *btv, unsigned int input) ++{ ++ /* video mux */ ++ gpio_bits(0x0f0000, input << 16); ++} ++ ++static void tibetCS16_init(struct bttv *btv) ++{ ++ /* enable gpio bits, mask obtained via btSpy */ ++ gpio_inout(0xffffff, 0x0f7fff); ++ gpio_write(0x0f7fff); ++} ++ ++/* ++ * The following routines for the Kodicom-4400r get a little mind-twisting. ++ * There is a "master" controller and three "slave" controllers, together ++ * an analog switch which connects any of 16 cameras to any of the BT87A's. ++ * The analog switch is controlled by the "master", but the detection order ++ * of the four BT878A chips is in an order which I just don't understand. ++ * The "master" is actually the second controller to be detected. The ++ * logic on the board uses logical numbers for the 4 controllers, but ++ * those numbers are different from the detection sequence. When working ++ * with the analog switch, we need to "map" from the detection sequence ++ * over to the board's logical controller number. This mapping sequence ++ * is {3, 0, 2, 1}, i.e. the first controller to be detected is logical ++ * unit 3, the second (which is the master) is logical unit 0, etc. ++ * We need to maintain the status of the analog switch (which of the 16 ++ * cameras is connected to which of the 4 controllers). Rather than ++ * add to the bttv structure for this, we use the data reserved for ++ * the mbox (unused for this card type). ++ */ ++ ++/* ++ * First a routine to set the analog switch, which controls which camera ++ * is routed to which controller. The switch comprises an X-address ++ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a ++ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3). ++ * A data value (gpio bit 7) of '1' enables the switch, and '0' disables ++ * the switch. A STROBE bit (gpio bit 8) latches the data value into the ++ * specified address. The idea is to set the address and data, then bring ++ * STROBE high, and finally bring STROBE back to low. ++ */ ++static void kodicom4400r_write(struct bttv *btv, ++ unsigned char xaddr, ++ unsigned char yaddr, ++ unsigned char data) { ++ unsigned int udata; ++ ++ udata = (data << 7) | ((yaddr&3) << 4) | (xaddr&0xf); ++ gpio_bits(0x1ff, udata); /* write ADDR and DAT */ ++ gpio_bits(0x1ff, udata | (1 << 8)); /* strobe high */ ++ gpio_bits(0x1ff, udata); /* strobe low */ ++} ++ ++/* ++ * Next the mux select. Both the "master" and "slave" 'cards' (controllers) ++ * use this routine. The routine finds the "master" for the card, maps ++ * the controller number from the detected position over to the logical ++ * number, writes the appropriate data to the analog switch, and housekeeps ++ * the local copy of the switch information. The parameter 'input' is the ++ * requested camera number (0 - 15). ++ */ ++static void kodicom4400r_muxsel(struct bttv *btv, unsigned int input) ++{ ++ char *sw_status; ++ int xaddr, yaddr; ++ struct bttv *mctlr; ++ static unsigned char map[4] = {3, 0, 2, 1}; ++ ++ mctlr = master[btv->c.nr]; ++ if (mctlr == NULL) { /* ignore if master not yet detected */ ++ return; ++ } ++ yaddr = (btv->c.nr - mctlr->c.nr + 1) & 3; /* the '&' is for safety */ ++ yaddr = map[yaddr]; ++ sw_status = (char *)(&mctlr->mbox_we); ++ xaddr = input & 0xf; ++ /* Check if the controller/camera pair has changed, else ignore */ ++ if (sw_status[yaddr] != xaddr) ++ { ++ /* "open" the old switch, "close" the new one, save the new */ ++ kodicom4400r_write(mctlr, sw_status[yaddr], yaddr, 0); ++ sw_status[yaddr] = xaddr; ++ kodicom4400r_write(mctlr, xaddr, yaddr, 1); ++ } ++} ++ ++/* ++ * During initialisation, we need to reset the analog switch. We ++ * also preset the switch to map the 4 connectors on the card to the ++ * *user's* (see above description of kodicom4400r_muxsel) channels ++ * 0 through 3 ++ */ ++static void kodicom4400r_init(struct bttv *btv) ++{ ++ char *sw_status = (char *)(&btv->mbox_we); ++ int ix; ++ ++ gpio_inout(0x0003ff, 0x0003ff); ++ gpio_write(1 << 9); /* reset MUX */ ++ gpio_write(0); ++ /* Preset camera 0 to the 4 controllers */ ++ for (ix = 0; ix < 4; ix++) { ++ sw_status[ix] = ix; ++ kodicom4400r_write(btv, ix, ix, 1); ++ } ++ /* ++ * Since this is the "master", we need to set up the ++ * other three controller chips' pointers to this structure ++ * for later use in the muxsel routine. ++ */ ++ if ((btv->c.nr<1) || (btv->c.nr>BTTV_MAX-3)) ++ return; ++ master[btv->c.nr-1] = btv; ++ master[btv->c.nr] = btv; ++ master[btv->c.nr+1] = btv; ++ master[btv->c.nr+2] = btv; ++} ++ ++/* The Grandtec X-Guard framegrabber card uses two Dual 4-channel ++ * video multiplexers to provide up to 16 video inputs. These ++ * multiplexers are controlled by the lower 8 GPIO pins of the ++ * bt878. The multiplexers probably Pericom PI5V331Q or similar. ++ ++ * xxx0 is pin xxx of multiplexer U5, ++ * yyy1 is pin yyy of multiplexer U2 ++ */ ++#define ENA0 0x01 ++#define ENB0 0x02 ++#define ENA1 0x04 ++#define ENB1 0x08 ++ ++#define IN10 0x10 ++#define IN00 0x20 ++#define IN11 0x40 ++#define IN01 0x80 ++ ++static void xguard_muxsel(struct bttv *btv, unsigned int input) ++{ ++ static const int masks[] = { ++ ENB0, ENB0|IN00, ENB0|IN10, ENB0|IN00|IN10, ++ ENA0, ENA0|IN00, ENA0|IN10, ENA0|IN00|IN10, ++ ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11, ++ ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11, ++ }; ++ gpio_write(masks[input%16]); ++} ++static void picolo_tetra_init(struct bttv *btv) ++{ ++ /*This is the video input redirection fonctionality : I DID NOT USED IT. */ ++ btwrite (0x08<<16,BT848_GPIO_DATA);/*GPIO[19] [==> 4053 B+C] set to 1 */ ++ btwrite (0x04<<16,BT848_GPIO_DATA);/*GPIO[18] [==> 4053 A] set to 1*/ ++} ++static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input) ++{ ++ ++ dprintk("%d : picolo_tetra_muxsel => input = %d\n", btv->c.nr, input); ++ /*Just set the right path in the analog multiplexers : channel 1 -> 4 ==> Analog Mux ==> MUX0*/ ++ /*GPIO[20]&GPIO[21] used to choose the right input*/ ++ btwrite (input<<20,BT848_GPIO_DATA); ++ ++} ++ ++/* ++ * ivc120_muxsel [Added by Alan Garfield ] ++ * ++ * The IVC120G security card has 4 i2c controlled TDA8540 matrix ++ * swichers to provide 16 channels to MUX0. The TDA8540's have ++ * 4 independent outputs and as such the IVC120G also has the ++ * optional "Monitor Out" bus. This allows the card to be looking ++ * at one input while the monitor is looking at another. ++ * ++ * Since I've couldn't be bothered figuring out how to add an ++ * independent muxsel for the monitor bus, I've just set it to ++ * whatever the card is looking at. ++ * ++ * OUT0 of the TDA8540's is connected to MUX0 (0x03) ++ * OUT1 of the TDA8540's is connected to "Monitor Out" (0x0C) ++ * ++ * TDA8540_ALT3 IN0-3 = Channel 13 - 16 (0x03) ++ * TDA8540_ALT4 IN0-3 = Channel 1 - 4 (0x03) ++ * TDA8540_ALT5 IN0-3 = Channel 5 - 8 (0x03) ++ * TDA8540_ALT6 IN0-3 = Channel 9 - 12 (0x03) ++ * ++ */ ++ ++/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */ ++#define I2C_TDA8540 0x90 ++#define I2C_TDA8540_ALT1 0x92 ++#define I2C_TDA8540_ALT2 0x94 ++#define I2C_TDA8540_ALT3 0x96 ++#define I2C_TDA8540_ALT4 0x98 ++#define I2C_TDA8540_ALT5 0x9a ++#define I2C_TDA8540_ALT6 0x9c ++ ++static void ivc120_muxsel(struct bttv *btv, unsigned int input) ++{ ++ /* Simple maths */ ++ int key = input % 4; ++ int matrix = input / 4; ++ ++ dprintk("%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n", ++ btv->c.nr, input, matrix, key); ++ ++ /* Handles the input selection on the TDA8540's */ ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00, ++ ((matrix == 3) ? (key | key << 2) : 0x00), 1); ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00, ++ ((matrix == 0) ? (key | key << 2) : 0x00), 1); ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00, ++ ((matrix == 1) ? (key | key << 2) : 0x00), 1); ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00, ++ ((matrix == 2) ? (key | key << 2) : 0x00), 1); ++ ++ /* Handles the output enables on the TDA8540's */ ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02, ++ ((matrix == 3) ? 0x03 : 0x00), 1); /* 13 - 16 */ ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02, ++ ((matrix == 0) ? 0x03 : 0x00), 1); /* 1-4 */ ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02, ++ ((matrix == 1) ? 0x03 : 0x00), 1); /* 5-8 */ ++ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02, ++ ((matrix == 2) ? 0x03 : 0x00), 1); /* 9-12 */ ++ ++ /* 878's MUX0 is already selected for input via muxsel values */ ++} ++ ++ ++/* PXC200 muxsel helper ++ * luke@syseng.anu.edu.au ++ * another transplant ++ * from Alessandro Rubini (rubini@linux.it) ++ * ++ * There are 4 kinds of cards: ++ * PXC200L which is bt848 ++ * PXC200F which is bt848 with PIC controlling mux ++ * PXC200AL which is bt878 ++ * PXC200AF which is bt878 with PIC controlling mux ++ */ ++#define PX_CFG_PXC200F 0x01 ++#define PX_FLAG_PXC200A 0x00001000 /* a pxc200A is bt-878 based */ ++#define PX_I2C_PIC 0x0f ++#define PX_PXC200A_CARDID 0x200a1295 ++#define PX_I2C_CMD_CFG 0x00 ++ ++static void PXC200_muxsel(struct bttv *btv, unsigned int input) ++{ ++ int rc; ++ long mux; ++ int bitmask; ++ unsigned char buf[2]; ++ ++ /* Read PIC config to determine if this is a PXC200F */ ++ /* PX_I2C_CMD_CFG*/ ++ buf[0]=0; ++ buf[1]=0; ++ rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1); ++ if (rc) { ++ pr_debug("%d: PXC200_muxsel: pic cfg write failed:%d\n", ++ btv->c.nr, rc); ++ /* not PXC ? do nothing */ ++ return; ++ } ++ ++ rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),NULL); ++ if (!(rc & PX_CFG_PXC200F)) { ++ pr_debug("%d: PXC200_muxsel: not PXC200F rc:%d\n", ++ btv->c.nr, rc); ++ return; ++ } ++ ++ ++ /* The multiplexer in the 200F is handled by the GPIO port */ ++ /* get correct mapping between inputs */ ++ /* mux = bttv_tvcards[btv->type].muxsel[input] & 3; */ ++ /* ** not needed!? */ ++ mux = input; ++ ++ /* make sure output pins are enabled */ ++ /* bitmask=0x30f; */ ++ bitmask=0x302; ++ /* check whether we have a PXC200A */ ++ if (btv->cardid == PX_PXC200A_CARDID) { ++ bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */ ++ bitmask |= 7<<4; /* the DAC */ ++ } ++ btwrite(bitmask, BT848_GPIO_OUT_EN); ++ ++ bitmask = btread(BT848_GPIO_DATA); ++ if (btv->cardid == PX_PXC200A_CARDID) ++ bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7); ++ else /* older device */ ++ bitmask = (bitmask & ~0x300) | ((mux & 3) << 8); ++ btwrite(bitmask,BT848_GPIO_DATA); ++ ++ /* ++ * Was "to be safe, set the bt848 to input 0" ++ * Actually, since it's ok at load time, better not messing ++ * with these bits (on PXC200AF you need to set mux 2 here) ++ * ++ * needed because bttv-driver sets mux before calling this function ++ */ ++ if (btv->cardid == PX_PXC200A_CARDID) ++ btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM); ++ else /* older device */ ++ btand(~BT848_IFORM_MUXSEL,BT848_IFORM); ++ ++ pr_debug("%d: setting input channel to:%d\n", btv->c.nr, (int)mux); ++} ++ ++static void phytec_muxsel(struct bttv *btv, unsigned int input) ++{ ++ unsigned int mux = input % 4; ++ ++ if (input == btv->svhs) ++ mux = 0; ++ ++ gpio_bits(0x3, mux); ++} ++ ++/* ++ * GeoVision GV-800(S) functions ++ * Bruno Christo ++*/ ++ ++/* This is a function to control the analog switch, which determines which ++ * camera is routed to which controller. The switch comprises an X-address ++ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a ++ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3). ++ * A data value (gpio bit 18) of '1' enables the switch, and '0' disables ++ * the switch. A STROBE bit (gpio bit 17) latches the data value into the ++ * specified address. There is also a chip select (gpio bit 16). ++ * The idea is to set the address and chip select together, bring ++ * STROBE high, write the data, and finally bring STROBE back to low. ++ */ ++static void gv800s_write(struct bttv *btv, ++ unsigned char xaddr, ++ unsigned char yaddr, ++ unsigned char data) { ++ /* On the "master" 878A: ++ * GPIO bits 0-9 are used for the analog switch: ++ * 00 - 03: camera selector ++ * 04 - 06: 878A (controller) selector ++ * 16: cselect ++ * 17: strobe ++ * 18: data (1->on, 0->off) ++ * 19: reset ++ */ ++ const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4); ++ const u32 CSELECT = 1<<16; ++ const u32 STROBE = 1<<17; ++ const u32 DATA = data<<18; ++ ++ gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */ ++ gpio_bits(0x20000, STROBE); /* STROBE high */ ++ gpio_bits(0x40000, DATA); /* write DATA */ ++ gpio_bits(0x20000, ~STROBE); /* STROBE low */ ++} ++ ++/* ++ * GeoVision GV-800(S) muxsel ++ * ++ * Each of the 4 cards (controllers) use this function. ++ * The controller using this function selects the input through the GPIO pins ++ * of the "master" card. A pointer to this card is stored in master[btv->c.nr]. ++ * ++ * The parameter 'input' is the requested camera number (0-4) on the controller. ++ * The map array has the address of each input. Note that the addresses in the ++ * array are in the sequence the original GeoVision driver uses, that is, set ++ * every controller to input 0, then to input 1, 2, 3, repeat. This means that ++ * the physical "camera 1" connector corresponds to controller 0 input 0, ++ * "camera 2" corresponds to controller 1 input 0, and so on. ++ * ++ * After getting the input address, the function then writes the appropriate ++ * data to the analog switch, and housekeeps the local copy of the switch ++ * information. ++ */ ++static void gv800s_muxsel(struct bttv *btv, unsigned int input) ++{ ++ struct bttv *mctlr; ++ char *sw_status; ++ int xaddr, yaddr; ++ static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 }, ++ { 0x1, 0x5, 0xb, 0x7 }, ++ { 0x2, 0x8, 0xc, 0xe }, ++ { 0x3, 0x9, 0xd, 0xf } }; ++ input = input%4; ++ mctlr = master[btv->c.nr]; ++ if (mctlr == NULL) { ++ /* do nothing until the "master" is detected */ ++ return; ++ } ++ yaddr = (btv->c.nr - mctlr->c.nr) & 3; ++ sw_status = (char *)(&mctlr->mbox_we); ++ xaddr = map[yaddr][input] & 0xf; ++ ++ /* Check if the controller/camera pair has changed, ignore otherwise */ ++ if (sw_status[yaddr] != xaddr) { ++ /* disable the old switch, enable the new one and save status */ ++ gv800s_write(mctlr, sw_status[yaddr], yaddr, 0); ++ sw_status[yaddr] = xaddr; ++ gv800s_write(mctlr, xaddr, yaddr, 1); ++ } ++} ++ ++/* GeoVision GV-800(S) "master" chip init */ ++static void gv800s_init(struct bttv *btv) ++{ ++ char *sw_status = (char *)(&btv->mbox_we); ++ int ix; ++ ++ gpio_inout(0xf107f, 0xf107f); ++ gpio_write(1<<19); /* reset the analog MUX */ ++ gpio_write(0); ++ ++ /* Preset camera 0 to the 4 controllers */ ++ for (ix = 0; ix < 4; ix++) { ++ sw_status[ix] = ix; ++ gv800s_write(btv, ix, ix, 1); ++ } ++ ++ /* Inputs on the "master" controller need this brightness fix */ ++ bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1); ++ ++ if (btv->c.nr > BTTV_MAX-4) ++ return; ++ /* ++ * Store the "master" controller pointer in the master ++ * array for later use in the muxsel function. ++ */ ++ master[btv->c.nr] = btv; ++ master[btv->c.nr+1] = btv; ++ master[btv->c.nr+2] = btv; ++ master[btv->c.nr+3] = btv; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* motherboard chipset specific stuff */ ++ ++void __init bttv_check_chipset(void) ++{ ++ int pcipci_fail = 0; ++ struct pci_dev *dev = NULL; ++ ++ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */ ++ pcipci_fail = 1; ++ if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF)) ++ triton1 = 1; ++ if (pci_pci_problems & PCIPCI_VSFX) ++ vsfx = 1; ++#ifdef PCIPCI_ALIMAGIK ++ if (pci_pci_problems & PCIPCI_ALIMAGIK) ++ latency = 0x0A; ++#endif ++ ++ ++ /* print warnings about any quirks found */ ++ if (triton1) ++ pr_info("Host bridge needs ETBF enabled\n"); ++ if (vsfx) ++ pr_info("Host bridge needs VSFX enabled\n"); ++ if (pcipci_fail) { ++ pr_info("bttv and your chipset may not work together\n"); ++ if (!no_overlay) { ++ pr_info("overlay will be disabled\n"); ++ no_overlay = 1; ++ } else { ++ pr_info("overlay forced. Use this option at your own risk.\n"); ++ } ++ } ++ if (UNSET != latency) ++ pr_info("pci latency fixup [%d]\n", latency); ++ while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, ++ PCI_DEVICE_ID_INTEL_82441, dev))) { ++ unsigned char b; ++ pci_read_config_byte(dev, 0x53, &b); ++ if (bttv_debug) ++ pr_info("Host bridge: 82441FX Natoma, bufcon=0x%02x\n", ++ b); ++ } ++} ++ ++int __devinit bttv_handle_chipset(struct bttv *btv) ++{ ++ unsigned char command; ++ ++ if (!triton1 && !vsfx && UNSET == latency) ++ return 0; ++ ++ if (bttv_verbose) { ++ if (triton1) ++ pr_info("%d: enabling ETBF (430FX/VP3 compatibility)\n", ++ btv->c.nr); ++ if (vsfx && btv->id >= 878) ++ pr_info("%d: enabling VSFX\n", btv->c.nr); ++ if (UNSET != latency) ++ pr_info("%d: setting pci timer to %d\n", ++ btv->c.nr, latency); ++ } ++ ++ if (btv->id < 878) { ++ /* bt848 (mis)uses a bit in the irq mask for etbf */ ++ if (triton1) ++ btv->triton1 = BT848_INT_ETBF; ++ } else { ++ /* bt878 has a bit in the pci config space for it */ ++ pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command); ++ if (triton1) ++ command |= BT878_EN_TBFX; ++ if (vsfx) ++ command |= BT878_EN_VSFX; ++ pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command); ++ } ++ if (UNSET != latency) ++ pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency); ++ return 0; ++} ++ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c +new file mode 100644 +index 0000000..2dc9ce3 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-driver.c +@@ -0,0 +1,4652 @@ ++/* ++ ++ bttv - Bt848 frame grabber driver ++ ++ Copyright (C) 1996,97,98 Ralph Metzler ++ & Marcus Metzler ++ (c) 1999-2002 Gerd Knorr ++ ++ some v4l2 code lines are taken from Justin's bttv2 driver which is ++ (c) 2000 Justin Schoeman ++ ++ V4L1 removal from: ++ (c) 2005-2006 Nickolay V. Shmyrev ++ ++ Fixes to be fully V4L2 compliant by ++ (c) 2006 Mauro Carvalho Chehab ++ ++ Cropping and overscan support ++ Copyright (C) 2005, 2006 Michael H. Schimek ++ Sponsored by OPQ Systems AB ++ ++ 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. ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "bttvp.h" ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#define BTTV_VERSION "0.9.19" ++ ++unsigned int bttv_num; /* number of Bt848s in use */ ++struct bttv *bttvs[BTTV_MAX]; ++ ++unsigned int bttv_debug; ++unsigned int bttv_verbose = 1; ++unsigned int bttv_gpio; ++ ++/* config variables */ ++#ifdef __BIG_ENDIAN ++static unsigned int bigendian=1; ++#else ++static unsigned int bigendian; ++#endif ++static unsigned int radio[BTTV_MAX]; ++static unsigned int irq_debug; ++static unsigned int gbuffers = 8; ++static unsigned int gbufsize = 0x208000; ++static unsigned int reset_crop = 1; ++ ++static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; ++static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; ++static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; ++static int debug_latency; ++static int disable_ir; ++ ++static unsigned int fdsr; ++ ++/* options */ ++static unsigned int combfilter; ++static unsigned int lumafilter; ++static unsigned int automute = 1; ++static unsigned int chroma_agc; ++static unsigned int adc_crush = 1; ++static unsigned int whitecrush_upper = 0xCF; ++static unsigned int whitecrush_lower = 0x7F; ++static unsigned int vcr_hack; ++static unsigned int irq_iswitch; ++static unsigned int uv_ratio = 50; ++static unsigned int full_luma_range; ++static unsigned int coring; ++ ++/* API features (turn on/off stuff for testing) */ ++static unsigned int v4l2 = 1; ++ ++/* insmod args */ ++module_param(bttv_verbose, int, 0644); ++module_param(bttv_gpio, int, 0644); ++module_param(bttv_debug, int, 0644); ++module_param(irq_debug, int, 0644); ++module_param(debug_latency, int, 0644); ++module_param(disable_ir, int, 0444); ++ ++module_param(fdsr, int, 0444); ++module_param(gbuffers, int, 0444); ++module_param(gbufsize, int, 0444); ++module_param(reset_crop, int, 0444); ++ ++module_param(v4l2, int, 0644); ++module_param(bigendian, int, 0644); ++module_param(irq_iswitch, int, 0644); ++module_param(combfilter, int, 0444); ++module_param(lumafilter, int, 0444); ++module_param(automute, int, 0444); ++module_param(chroma_agc, int, 0444); ++module_param(adc_crush, int, 0444); ++module_param(whitecrush_upper, int, 0444); ++module_param(whitecrush_lower, int, 0444); ++module_param(vcr_hack, int, 0444); ++module_param(uv_ratio, int, 0444); ++module_param(full_luma_range, int, 0444); ++module_param(coring, int, 0444); ++ ++module_param_array(radio, int, NULL, 0444); ++module_param_array(video_nr, int, NULL, 0444); ++module_param_array(radio_nr, int, NULL, 0444); ++module_param_array(vbi_nr, int, NULL, 0444); ++ ++MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); ++MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); ++MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)"); ++MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)"); ++MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); ++MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); ++MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); ++MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); ++MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); ++MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " ++ "is 1 (yes) for compatibility with older applications"); ++MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); ++MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); ++MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); ++MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207"); ++MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127"); ++MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); ++MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); ++MODULE_PARM_DESC(uv_ratio,"ratio between u and v gains, default is 50"); ++MODULE_PARM_DESC(full_luma_range,"use the full luma range, default is 0 (no)"); ++MODULE_PARM_DESC(coring,"set the luma coring level, default is 0 (no)"); ++MODULE_PARM_DESC(video_nr, "video device numbers"); ++MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); ++MODULE_PARM_DESC(radio_nr, "radio device numbers"); ++ ++MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); ++MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(BTTV_VERSION); ++ ++/* ----------------------------------------------------------------------- */ ++/* sysfs */ ++ ++static ssize_t show_card(struct device *cd, ++ struct device_attribute *attr, char *buf) ++{ ++ struct video_device *vfd = container_of(cd, struct video_device, dev); ++ struct bttv *btv = video_get_drvdata(vfd); ++ return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET); ++} ++static DEVICE_ATTR(card, S_IRUGO, show_card, NULL); ++ ++/* ----------------------------------------------------------------------- */ ++/* dvb auto-load setup */ ++#if defined(CONFIG_MODULES) && defined(MODULE) ++static void request_module_async(struct work_struct *work) ++{ ++ request_module("dvb-bt8xx"); ++} ++ ++static void request_modules(struct bttv *dev) ++{ ++ INIT_WORK(&dev->request_module_wk, request_module_async); ++ schedule_work(&dev->request_module_wk); ++} ++ ++static void flush_request_modules(struct bttv *dev) ++{ ++ flush_work(&dev->request_module_wk); ++} ++#else ++#define request_modules(dev) ++#define flush_request_modules(dev) do {} while(0) ++#endif /* CONFIG_MODULES */ ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* static data */ ++ ++/* special timing tables from conexant... */ ++static u8 SRAM_Table[][60] = ++{ ++ /* PAL digital input over GPIO[7:0] */ ++ { ++ 45, // 45 bytes following ++ 0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16, ++ 0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00, ++ 0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00, ++ 0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37, ++ 0x37,0x00,0xAF,0x21,0x00 ++ }, ++ /* NTSC digital input over GPIO[7:0] */ ++ { ++ 51, // 51 bytes following ++ 0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06, ++ 0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00, ++ 0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07, ++ 0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6, ++ 0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21, ++ 0x00, ++ }, ++ // TGB_NTSC392 // quartzsight ++ // This table has been modified to be used for Fusion Rev D ++ { ++ 0x2A, // size of table = 42 ++ 0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24, ++ 0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10, ++ 0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00, ++ 0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3, ++ 0x20, 0x00 ++ } ++}; ++ ++/* minhdelayx1 first video pixel we can capture on a line and ++ hdelayx1 start of active video, both relative to rising edge of ++ /HRESET pulse (0H) in 1 / fCLKx1. ++ swidth width of active video and ++ totalwidth total line width, both in 1 / fCLKx1. ++ sqwidth total line width in square pixels. ++ vdelay start of active video in 2 * field lines relative to ++ trailing edge of /VRESET pulse (VDELAY register). ++ sheight height of active video in 2 * field lines. ++ videostart0 ITU-R frame line number of the line corresponding ++ to vdelay in the first field. */ ++#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \ ++ vdelay, sheight, videostart0) \ ++ .cropcap.bounds.left = minhdelayx1, \ ++ /* * 2 because vertically we count field lines times two, */ \ ++ /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \ ++ .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \ ++ /* 4 is a safety margin at the end of the line. */ \ ++ .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \ ++ .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \ ++ .cropcap.defrect.left = hdelayx1, \ ++ .cropcap.defrect.top = (videostart0) * 2, \ ++ .cropcap.defrect.width = swidth, \ ++ .cropcap.defrect.height = sheight, \ ++ .cropcap.pixelaspect.numerator = totalwidth, \ ++ .cropcap.pixelaspect.denominator = sqwidth, ++ ++const struct bttv_tvnorm bttv_tvnorms[] = { ++ /* PAL-BDGHI */ ++ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ ++ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ ++ { ++ .v4l2_id = V4L2_STD_PAL, ++ .name = "PAL", ++ .Fsc = 35468950, ++ .swidth = 924, ++ .sheight = 576, ++ .totalwidth = 1135, ++ .adelay = 0x7f, ++ .bdelay = 0x72, ++ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), ++ .scaledtwidth = 1135, ++ .hdelayx1 = 186, ++ .hactivex1 = 924, ++ .vdelay = 0x20, ++ .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */ ++ .sram = 0, ++ /* ITU-R frame line number of the first VBI line ++ we can capture, of the first and second field. ++ The last line is determined by cropcap.bounds. */ ++ .vbistart = { 7, 320 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 186, ++ /* Should be (768 * 1135 + 944 / 2) / 944. ++ cropcap.defrect is used for image width ++ checks, so we keep the old value 924. */ ++ /* swidth */ 924, ++ /* totalwidth */ 1135, ++ /* sqwidth */ 944, ++ /* vdelay */ 0x20, ++ /* bt878 (and bt848?) can capture another ++ line below active video. */ ++ /* sheight */ (576 + 2) + 0x20 - 2, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, ++ .name = "NTSC", ++ .Fsc = 28636363, ++ .swidth = 768, ++ .sheight = 480, ++ .totalwidth = 910, ++ .adelay = 0x68, ++ .bdelay = 0x5d, ++ .iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0), ++ .scaledtwidth = 910, ++ .hdelayx1 = 128, ++ .hactivex1 = 910, ++ .vdelay = 0x1a, ++ .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */ ++ .sram = 1, ++ .vbistart = { 10, 273 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 128, ++ /* Should be (640 * 910 + 780 / 2) / 780? */ ++ /* swidth */ 768, ++ /* totalwidth */ 910, ++ /* sqwidth */ 780, ++ /* vdelay */ 0x1a, ++ /* sheight */ 480, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_SECAM, ++ .name = "SECAM", ++ .Fsc = 35468950, ++ .swidth = 924, ++ .sheight = 576, ++ .totalwidth = 1135, ++ .adelay = 0x7f, ++ .bdelay = 0xb0, ++ .iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1), ++ .scaledtwidth = 1135, ++ .hdelayx1 = 186, ++ .hactivex1 = 922, ++ .vdelay = 0x20, ++ .vbipack = 255, ++ .sram = 0, /* like PAL, correct? */ ++ .vbistart = { 7, 320 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 186, ++ /* swidth */ 924, ++ /* totalwidth */ 1135, ++ /* sqwidth */ 944, ++ /* vdelay */ 0x20, ++ /* sheight */ 576, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_PAL_Nc, ++ .name = "PAL-Nc", ++ .Fsc = 28636363, ++ .swidth = 640, ++ .sheight = 576, ++ .totalwidth = 910, ++ .adelay = 0x68, ++ .bdelay = 0x5d, ++ .iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), ++ .scaledtwidth = 780, ++ .hdelayx1 = 130, ++ .hactivex1 = 734, ++ .vdelay = 0x1a, ++ .vbipack = 144, ++ .sram = -1, ++ .vbistart = { 7, 320 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 130, ++ /* swidth */ (640 * 910 + 780 / 2) / 780, ++ /* totalwidth */ 910, ++ /* sqwidth */ 780, ++ /* vdelay */ 0x1a, ++ /* sheight */ 576, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_PAL_M, ++ .name = "PAL-M", ++ .Fsc = 28636363, ++ .swidth = 640, ++ .sheight = 480, ++ .totalwidth = 910, ++ .adelay = 0x68, ++ .bdelay = 0x5d, ++ .iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0), ++ .scaledtwidth = 780, ++ .hdelayx1 = 135, ++ .hactivex1 = 754, ++ .vdelay = 0x1a, ++ .vbipack = 144, ++ .sram = -1, ++ .vbistart = { 10, 273 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 135, ++ /* swidth */ (640 * 910 + 780 / 2) / 780, ++ /* totalwidth */ 910, ++ /* sqwidth */ 780, ++ /* vdelay */ 0x1a, ++ /* sheight */ 480, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_PAL_N, ++ .name = "PAL-N", ++ .Fsc = 35468950, ++ .swidth = 768, ++ .sheight = 576, ++ .totalwidth = 1135, ++ .adelay = 0x7f, ++ .bdelay = 0x72, ++ .iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1), ++ .scaledtwidth = 944, ++ .hdelayx1 = 186, ++ .hactivex1 = 922, ++ .vdelay = 0x20, ++ .vbipack = 144, ++ .sram = -1, ++ .vbistart = { 7, 320 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 186, ++ /* swidth */ (768 * 1135 + 944 / 2) / 944, ++ /* totalwidth */ 1135, ++ /* sqwidth */ 944, ++ /* vdelay */ 0x20, ++ /* sheight */ 576, ++ /* videostart0 */ 23) ++ },{ ++ .v4l2_id = V4L2_STD_NTSC_M_JP, ++ .name = "NTSC-JP", ++ .Fsc = 28636363, ++ .swidth = 640, ++ .sheight = 480, ++ .totalwidth = 910, ++ .adelay = 0x68, ++ .bdelay = 0x5d, ++ .iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), ++ .scaledtwidth = 780, ++ .hdelayx1 = 135, ++ .hactivex1 = 754, ++ .vdelay = 0x16, ++ .vbipack = 144, ++ .sram = -1, ++ .vbistart = { 10, 273 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 135, ++ /* swidth */ (640 * 910 + 780 / 2) / 780, ++ /* totalwidth */ 910, ++ /* sqwidth */ 780, ++ /* vdelay */ 0x16, ++ /* sheight */ 480, ++ /* videostart0 */ 23) ++ },{ ++ /* that one hopefully works with the strange timing ++ * which video recorders produce when playing a NTSC ++ * tape on a PAL TV ... */ ++ .v4l2_id = V4L2_STD_PAL_60, ++ .name = "PAL-60", ++ .Fsc = 35468950, ++ .swidth = 924, ++ .sheight = 480, ++ .totalwidth = 1135, ++ .adelay = 0x7f, ++ .bdelay = 0x72, ++ .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), ++ .scaledtwidth = 1135, ++ .hdelayx1 = 186, ++ .hactivex1 = 924, ++ .vdelay = 0x1a, ++ .vbipack = 255, ++ .vtotal = 524, ++ .sram = -1, ++ .vbistart = { 10, 273 }, ++ CROPCAP(/* minhdelayx1 */ 68, ++ /* hdelayx1 */ 186, ++ /* swidth */ 924, ++ /* totalwidth */ 1135, ++ /* sqwidth */ 944, ++ /* vdelay */ 0x1a, ++ /* sheight */ 480, ++ /* videostart0 */ 23) ++ } ++}; ++static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); ++ ++/* ----------------------------------------------------------------------- */ ++/* bttv format list ++ packed pixel formats must come first */ ++static const struct bttv_format formats[] = { ++ { ++ .name = "8 bpp, gray", ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .btformat = BT848_COLOR_FMT_Y8, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "8 bpp, dithered color", ++ .fourcc = V4L2_PIX_FMT_HI240, ++ .btformat = BT848_COLOR_FMT_RGB8, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER, ++ },{ ++ .name = "15 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB555, ++ .btformat = BT848_COLOR_FMT_RGB15, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "15 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB555X, ++ .btformat = BT848_COLOR_FMT_RGB15, ++ .btswap = 0x03, /* byteswap */ ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "16 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .btformat = BT848_COLOR_FMT_RGB16, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "16 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB565X, ++ .btformat = BT848_COLOR_FMT_RGB16, ++ .btswap = 0x03, /* byteswap */ ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "24 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .btformat = BT848_COLOR_FMT_RGB24, ++ .depth = 24, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "32 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .btformat = BT848_COLOR_FMT_RGB32, ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "32 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .btformat = BT848_COLOR_FMT_RGB32, ++ .btswap = 0x0f, /* byte+word swap */ ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .btformat = BT848_COLOR_FMT_YUY2, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .btformat = BT848_COLOR_FMT_YUY2, ++ .btswap = 0x03, /* byteswap */ ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "4:2:2, planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV422P, ++ .btformat = BT848_COLOR_FMT_YCrCb422, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .hshift = 1, ++ .vshift = 0, ++ },{ ++ .name = "4:2:0, planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .btformat = BT848_COLOR_FMT_YCrCb422, ++ .depth = 12, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .hshift = 1, ++ .vshift = 1, ++ },{ ++ .name = "4:2:0, planar, Y-Cr-Cb", ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .btformat = BT848_COLOR_FMT_YCrCb422, ++ .depth = 12, ++ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, ++ .hshift = 1, ++ .vshift = 1, ++ },{ ++ .name = "4:1:1, planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV411P, ++ .btformat = BT848_COLOR_FMT_YCrCb411, ++ .depth = 12, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .hshift = 2, ++ .vshift = 0, ++ },{ ++ .name = "4:1:0, planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV410, ++ .btformat = BT848_COLOR_FMT_YCrCb411, ++ .depth = 9, ++ .flags = FORMAT_FLAGS_PLANAR, ++ .hshift = 2, ++ .vshift = 2, ++ },{ ++ .name = "4:1:0, planar, Y-Cr-Cb", ++ .fourcc = V4L2_PIX_FMT_YVU410, ++ .btformat = BT848_COLOR_FMT_YCrCb411, ++ .depth = 9, ++ .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, ++ .hshift = 2, ++ .vshift = 2, ++ },{ ++ .name = "raw scanlines", ++ .fourcc = -1, ++ .btformat = BT848_COLOR_FMT_RAW, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_RAW, ++ } ++}; ++static const unsigned int FORMATS = ARRAY_SIZE(formats); ++ ++/* ----------------------------------------------------------------------- */ ++ ++#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) ++#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) ++#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) ++#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3) ++#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4) ++#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5) ++#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6) ++#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7) ++#define V4L2_CID_PRIVATE_UV_RATIO (V4L2_CID_PRIVATE_BASE + 8) ++#define V4L2_CID_PRIVATE_FULL_LUMA_RANGE (V4L2_CID_PRIVATE_BASE + 9) ++#define V4L2_CID_PRIVATE_CORING (V4L2_CID_PRIVATE_BASE + 10) ++#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 11) ++ ++static const struct v4l2_queryctrl no_ctl = { ++ .name = "42", ++ .flags = V4L2_CTRL_FLAG_DISABLED, ++}; ++static const struct v4l2_queryctrl bttv_ctls[] = { ++ /* --- video --- */ ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .name = "Brightness", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 256, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_CONTRAST, ++ .name = "Contrast", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 128, ++ .default_value = 27648, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_SATURATION, ++ .name = "Saturation", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 128, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_COLOR_KILLER, ++ .name = "Color killer", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ }, { ++ .id = V4L2_CID_HUE, ++ .name = "Hue", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 256, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ /* --- audio --- */ ++ { ++ .id = V4L2_CID_AUDIO_MUTE, ++ .name = "Mute", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_AUDIO_VOLUME, ++ .name = "Volume", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 65535/100, ++ .default_value = 65535, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_AUDIO_BALANCE, ++ .name = "Balance", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 65535/100, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_AUDIO_BASS, ++ .name = "Bass", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 65535/100, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_AUDIO_TREBLE, ++ .name = "Treble", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 65535/100, ++ .default_value = 32768, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ /* --- private --- */ ++ { ++ .id = V4L2_CID_PRIVATE_CHROMA_AGC, ++ .name = "chroma agc", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_COMBFILTER, ++ .name = "combfilter", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_AUTOMUTE, ++ .name = "automute", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_LUMAFILTER, ++ .name = "luma decimation filter", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_AGC_CRUSH, ++ .name = "agc crush", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_VCR_HACK, ++ .name = "vcr hack", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, ++ .name = "whitecrush upper", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ .default_value = 0xCF, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, ++ .name = "whitecrush lower", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ .default_value = 0x7F, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_PRIVATE_UV_RATIO, ++ .name = "uv ratio", ++ .minimum = 0, ++ .maximum = 100, ++ .step = 1, ++ .default_value = 50, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_PRIVATE_FULL_LUMA_RANGE, ++ .name = "full luma range", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_CORING, ++ .name = "coring", ++ .minimum = 0, ++ .maximum = 3, ++ .step = 1, ++ .default_value = 0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ } ++ ++ ++ ++}; ++ ++static const struct v4l2_queryctrl *ctrl_by_id(int id) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(bttv_ctls); i++) ++ if (bttv_ctls[i].id == id) ++ return bttv_ctls+i; ++ ++ return NULL; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* resource management */ ++ ++/* ++ RESOURCE_ allocated by freed by ++ ++ VIDEO_READ bttv_read 1) bttv_read 2) ++ ++ VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF ++ VIDIOC_QBUF 1) bttv_release ++ VIDIOCMCAPTURE 1) ++ ++ OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off ++ VIDIOC_OVERLAY on VIDIOC_OVERLAY off ++ 3) bttv_release ++ ++ VBI VIDIOC_STREAMON VIDIOC_STREAMOFF ++ VIDIOC_QBUF 1) bttv_release ++ bttv_read, bttv_poll 1) 4) ++ ++ 1) The resource must be allocated when we enter buffer prepare functions ++ and remain allocated while buffers are in the DMA queue. ++ 2) This is a single frame read. ++ 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when ++ RESOURCE_OVERLAY is allocated. ++ 4) This is a continuous read, implies VIDIOC_STREAMON. ++ ++ Note this driver permits video input and standard changes regardless if ++ resources are allocated. ++*/ ++ ++#define VBI_RESOURCES (RESOURCE_VBI) ++#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \ ++ RESOURCE_VIDEO_STREAM | \ ++ RESOURCE_OVERLAY) ++ ++static ++int check_alloc_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bit) ++{ ++ int xbits; /* mutual exclusive resources */ ++ ++ if (fh->resources & bit) ++ /* have it already allocated */ ++ return 1; ++ ++ xbits = bit; ++ if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM)) ++ xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM; ++ ++ /* is it free? */ ++ if (btv->resources & xbits) { ++ /* no, someone else uses it */ ++ goto fail; ++ } ++ ++ if ((bit & VIDEO_RESOURCES) ++ && 0 == (btv->resources & VIDEO_RESOURCES)) { ++ /* Do crop - use current, don't - use default parameters. */ ++ __s32 top = btv->crop[!!fh->do_crop].rect.top; ++ ++ if (btv->vbi_end > top) ++ goto fail; ++ ++ /* We cannot capture the same line as video and VBI data. ++ Claim scan lines crop[].rect.top to bottom. */ ++ btv->crop_start = top; ++ } else if (bit & VBI_RESOURCES) { ++ __s32 end = fh->vbi_fmt.end; ++ ++ if (end > btv->crop_start) ++ goto fail; ++ ++ /* Claim scan lines above fh->vbi_fmt.end. */ ++ btv->vbi_end = end; ++ } ++ ++ /* it's free, grab it */ ++ fh->resources |= bit; ++ btv->resources |= bit; ++ return 1; ++ ++ fail: ++ return 0; ++} ++ ++static ++int check_btres(struct bttv_fh *fh, int bit) ++{ ++ return (fh->resources & bit); ++} ++ ++static ++int locked_btres(struct bttv *btv, int bit) ++{ ++ return (btv->resources & bit); ++} ++ ++/* Call with btv->lock down. */ ++static void ++disclaim_vbi_lines(struct bttv *btv) ++{ ++ btv->vbi_end = 0; ++} ++ ++/* Call with btv->lock down. */ ++static void ++disclaim_video_lines(struct bttv *btv) ++{ ++ const struct bttv_tvnorm *tvnorm; ++ u8 crop; ++ ++ tvnorm = &bttv_tvnorms[btv->tvnorm]; ++ btv->crop_start = tvnorm->cropcap.bounds.top ++ + tvnorm->cropcap.bounds.height; ++ ++ /* VBI capturing ends at VDELAY, start of video capturing, no ++ matter how many lines the VBI RISC program expects. When video ++ capturing is off, it shall no longer "preempt" VBI capturing, ++ so we set VDELAY to maximum. */ ++ crop = btread(BT848_E_CROP) | 0xc0; ++ btwrite(crop, BT848_E_CROP); ++ btwrite(0xfe, BT848_E_VDELAY_LO); ++ btwrite(crop, BT848_O_CROP); ++ btwrite(0xfe, BT848_O_VDELAY_LO); ++} ++ ++static ++void free_btres_lock(struct bttv *btv, struct bttv_fh *fh, int bits) ++{ ++ if ((fh->resources & bits) != bits) { ++ /* trying to free resources not allocated by us ... */ ++ pr_err("BUG! (btres)\n"); ++ } ++ fh->resources &= ~bits; ++ btv->resources &= ~bits; ++ ++ bits = btv->resources; ++ ++ if (0 == (bits & VIDEO_RESOURCES)) ++ disclaim_video_lines(btv); ++ ++ if (0 == (bits & VBI_RESOURCES)) ++ disclaim_vbi_lines(btv); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */ ++ ++/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C ++ PLL_X = Reference pre-divider (0=1, 1=2) ++ PLL_C = Post divider (0=6, 1=4) ++ PLL_I = Integer input ++ PLL_F = Fractional input ++ ++ F_input = 28.636363 MHz: ++ PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0 ++*/ ++ ++static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) ++{ ++ unsigned char fl, fh, fi; ++ ++ /* prevent overflows */ ++ fin/=4; ++ fout/=4; ++ ++ fout*=12; ++ fi=fout/fin; ++ ++ fout=(fout%fin)*256; ++ fh=fout/fin; ++ ++ fout=(fout%fin)*256; ++ fl=fout/fin; ++ ++ btwrite(fl, BT848_PLL_F_LO); ++ btwrite(fh, BT848_PLL_F_HI); ++ btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); ++} ++ ++static void set_pll(struct bttv *btv) ++{ ++ int i; ++ ++ if (!btv->pll.pll_crystal) ++ return; ++ ++ if (btv->pll.pll_ofreq == btv->pll.pll_current) { ++ dprintk("%d: PLL: no change required\n", btv->c.nr); ++ return; ++ } ++ ++ if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { ++ /* no PLL needed */ ++ if (btv->pll.pll_current == 0) ++ return; ++ if (bttv_verbose) ++ pr_info("%d: PLL can sleep, using XTAL (%d)\n", ++ btv->c.nr, btv->pll.pll_ifreq); ++ btwrite(0x00,BT848_TGCTRL); ++ btwrite(0x00,BT848_PLL_XCI); ++ btv->pll.pll_current = 0; ++ return; ++ } ++ ++ if (bttv_verbose) ++ pr_info("%d: Setting PLL: %d => %d (needs up to 100ms)\n", ++ btv->c.nr, ++ btv->pll.pll_ifreq, btv->pll.pll_ofreq); ++ set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); ++ ++ for (i=0; i<10; i++) { ++ /* Let other people run while the PLL stabilizes */ ++ msleep(10); ++ ++ if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { ++ btwrite(0,BT848_DSTATUS); ++ } else { ++ btwrite(0x08,BT848_TGCTRL); ++ btv->pll.pll_current = btv->pll.pll_ofreq; ++ if (bttv_verbose) ++ pr_info("PLL set ok\n"); ++ return; ++ } ++ } ++ btv->pll.pll_current = -1; ++ if (bttv_verbose) ++ pr_info("Setting PLL failed\n"); ++ return; ++} ++ ++/* used to switch between the bt848's analog/digital video capture modes */ ++static void bt848A_set_timing(struct bttv *btv) ++{ ++ int i, len; ++ int table_idx = bttv_tvnorms[btv->tvnorm].sram; ++ int fsc = bttv_tvnorms[btv->tvnorm].Fsc; ++ ++ if (btv->input == btv->dig) { ++ dprintk("%d: load digital timing table (table_idx=%d)\n", ++ btv->c.nr,table_idx); ++ ++ /* timing change...reset timing generator address */ ++ btwrite(0x00, BT848_TGCTRL); ++ btwrite(0x02, BT848_TGCTRL); ++ btwrite(0x00, BT848_TGCTRL); ++ ++ len=SRAM_Table[table_idx][0]; ++ for(i = 1; i <= len; i++) ++ btwrite(SRAM_Table[table_idx][i],BT848_TGLB); ++ btv->pll.pll_ofreq = 27000000; ++ ++ set_pll(btv); ++ btwrite(0x11, BT848_TGCTRL); ++ btwrite(0x41, BT848_DVSIF); ++ } else { ++ btv->pll.pll_ofreq = fsc; ++ set_pll(btv); ++ btwrite(0x0, BT848_DVSIF); ++ } ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void bt848_bright(struct bttv *btv, int bright) ++{ ++ int value; ++ ++ // printk("set bright: %d\n", bright); // DEBUG ++ btv->bright = bright; ++ ++ /* We want -128 to 127 we get 0-65535 */ ++ value = (bright >> 8) - 128; ++ btwrite(value & 0xff, BT848_BRIGHT); ++} ++ ++static void bt848_hue(struct bttv *btv, int hue) ++{ ++ int value; ++ ++ btv->hue = hue; ++ ++ /* -128 to 127 */ ++ value = (hue >> 8) - 128; ++ btwrite(value & 0xff, BT848_HUE); ++} ++ ++static void bt848_contrast(struct bttv *btv, int cont) ++{ ++ int value,hibit; ++ ++ btv->contrast = cont; ++ ++ /* 0-511 */ ++ value = (cont >> 7); ++ hibit = (value >> 6) & 4; ++ btwrite(value & 0xff, BT848_CONTRAST_LO); ++ btaor(hibit, ~4, BT848_E_CONTROL); ++ btaor(hibit, ~4, BT848_O_CONTROL); ++} ++ ++static void bt848_sat(struct bttv *btv, int color) ++{ ++ int val_u,val_v,hibits; ++ ++ btv->saturation = color; ++ ++ /* 0-511 for the color */ ++ val_u = ((color * btv->opt_uv_ratio) / 50) >> 7; ++ val_v = (((color * (100 - btv->opt_uv_ratio) / 50) >>7)*180L)/254; ++ hibits = (val_u >> 7) & 2; ++ hibits |= (val_v >> 8) & 1; ++ btwrite(val_u & 0xff, BT848_SAT_U_LO); ++ btwrite(val_v & 0xff, BT848_SAT_V_LO); ++ btaor(hibits, ~3, BT848_E_CONTROL); ++ btaor(hibits, ~3, BT848_O_CONTROL); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int ++video_mux(struct bttv *btv, unsigned int input) ++{ ++ int mux,mask2; ++ ++ if (input >= bttv_tvcards[btv->c.type].video_inputs) ++ return -EINVAL; ++ ++ /* needed by RemoteVideo MX */ ++ mask2 = bttv_tvcards[btv->c.type].gpiomask2; ++ if (mask2) ++ gpio_inout(mask2,mask2); ++ ++ if (input == btv->svhs) { ++ btor(BT848_CONTROL_COMP, BT848_E_CONTROL); ++ btor(BT848_CONTROL_COMP, BT848_O_CONTROL); ++ } else { ++ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); ++ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); ++ } ++ mux = bttv_muxsel(btv, input); ++ btaor(mux<<5, ~(3<<5), BT848_IFORM); ++ dprintk("%d: video mux: input=%d mux=%d\n", btv->c.nr, input, mux); ++ ++ /* card specific hook */ ++ if(bttv_tvcards[btv->c.type].muxsel_hook) ++ bttv_tvcards[btv->c.type].muxsel_hook (btv, input); ++ return 0; ++} ++ ++static char *audio_modes[] = { ++ "audio: tuner", "audio: radio", "audio: extern", ++ "audio: intern", "audio: mute" ++}; ++ ++static int ++audio_mux(struct bttv *btv, int input, int mute) ++{ ++ int gpio_val, signal; ++ struct v4l2_control ctrl; ++ ++ gpio_inout(bttv_tvcards[btv->c.type].gpiomask, ++ bttv_tvcards[btv->c.type].gpiomask); ++ signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; ++ ++ btv->mute = mute; ++ btv->audio = input; ++ ++ /* automute */ ++ mute = mute || (btv->opt_automute && !signal && !btv->radio_user); ++ ++ if (mute) ++ gpio_val = bttv_tvcards[btv->c.type].gpiomute; ++ else ++ gpio_val = bttv_tvcards[btv->c.type].gpiomux[input]; ++ ++ switch (btv->c.type) { ++ case BTTV_BOARD_VOODOOTV_FM: ++ case BTTV_BOARD_VOODOOTV_200: ++ gpio_val = bttv_tda9880_setnorm(btv, gpio_val); ++ break; ++ ++ default: ++ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val); ++ } ++ ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]); ++ if (in_interrupt()) ++ return 0; ++ ++ ctrl.id = V4L2_CID_AUDIO_MUTE; ++ ctrl.value = btv->mute; ++ bttv_call_all(btv, core, s_ctrl, &ctrl); ++ if (btv->sd_msp34xx) { ++ u32 in; ++ ++ /* Note: the inputs tuner/radio/extern/intern are translated ++ to msp routings. This assumes common behavior for all msp3400 ++ based TV cards. When this assumption fails, then the ++ specific MSP routing must be added to the card table. ++ For now this is sufficient. */ ++ switch (input) { ++ case TVAUDIO_INPUT_RADIO: ++ /* Some boards need the msp do to the radio demod */ ++ if (btv->radio_uses_msp_demodulator) { ++ in = MSP_INPUT_DEFAULT; ++ break; ++ } ++ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); ++ break; ++ case TVAUDIO_INPUT_EXTERN: ++ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); ++ break; ++ case TVAUDIO_INPUT_INTERN: ++ /* Yes, this is the same input as for RADIO. I doubt ++ if this is ever used. The only board with an INTERN ++ input is the BTTV_BOARD_AVERMEDIA98. I wonder how ++ that was tested. My guess is that the whole INTERN ++ input does not work. */ ++ in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); ++ break; ++ case TVAUDIO_INPUT_TUNER: ++ default: ++ /* This is the only card that uses TUNER2, and afaik, ++ is the only difference between the VOODOOTV_FM ++ and VOODOOTV_200 */ ++ if (btv->c.type == BTTV_BOARD_VOODOOTV_200) ++ in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \ ++ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER); ++ else ++ in = MSP_INPUT_DEFAULT; ++ break; ++ } ++ v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing, ++ in, MSP_OUTPUT_DEFAULT, 0); ++ } ++ if (btv->sd_tvaudio) { ++ v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, ++ input, 0, 0); ++ } ++ return 0; ++} ++ ++static inline int ++audio_mute(struct bttv *btv, int mute) ++{ ++ return audio_mux(btv, btv->audio, mute); ++} ++ ++static inline int ++audio_input(struct bttv *btv, int input) ++{ ++ return audio_mux(btv, input, btv->mute); ++} ++ ++static void ++bttv_crop_calc_limits(struct bttv_crop *c) ++{ ++ /* Scale factor min. 1:1, max. 16:1. Min. image size ++ 48 x 32. Scaled width must be a multiple of 4. */ ++ ++ if (1) { ++ /* For bug compatibility with VIDIOCGCAP and image ++ size checks in earlier driver versions. */ ++ c->min_scaled_width = 48; ++ c->min_scaled_height = 32; ++ } else { ++ c->min_scaled_width = ++ (max(48, c->rect.width >> 4) + 3) & ~3; ++ c->min_scaled_height = ++ max(32, c->rect.height >> 4); ++ } ++ ++ c->max_scaled_width = c->rect.width & ~3; ++ c->max_scaled_height = c->rect.height; ++} ++ ++static void ++bttv_crop_reset(struct bttv_crop *c, unsigned int norm) ++{ ++ c->rect = bttv_tvnorms[norm].cropcap.defrect; ++ bttv_crop_calc_limits(c); ++} ++ ++/* Call with btv->lock down. */ ++static int ++set_tvnorm(struct bttv *btv, unsigned int norm) ++{ ++ const struct bttv_tvnorm *tvnorm; ++ v4l2_std_id id; ++ ++ BUG_ON(norm >= BTTV_TVNORMS); ++ BUG_ON(btv->tvnorm >= BTTV_TVNORMS); ++ ++ tvnorm = &bttv_tvnorms[norm]; ++ ++ if (memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap, ++ sizeof (tvnorm->cropcap))) { ++ bttv_crop_reset(&btv->crop[0], norm); ++ btv->crop[1] = btv->crop[0]; /* current = default */ ++ ++ if (0 == (btv->resources & VIDEO_RESOURCES)) { ++ btv->crop_start = tvnorm->cropcap.bounds.top ++ + tvnorm->cropcap.bounds.height; ++ } ++ } ++ ++ btv->tvnorm = norm; ++ ++ btwrite(tvnorm->adelay, BT848_ADELAY); ++ btwrite(tvnorm->bdelay, BT848_BDELAY); ++ btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), ++ BT848_IFORM); ++ btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE); ++ btwrite(1, BT848_VBI_PACK_DEL); ++ bt848A_set_timing(btv); ++ ++ switch (btv->c.type) { ++ case BTTV_BOARD_VOODOOTV_FM: ++ case BTTV_BOARD_VOODOOTV_200: ++ bttv_tda9880_setnorm(btv, gpio_read()); ++ break; ++ } ++ id = tvnorm->v4l2_id; ++ bttv_call_all(btv, core, s_std, id); ++ ++ return 0; ++} ++ ++/* Call with btv->lock down. */ ++static void ++set_input(struct bttv *btv, unsigned int input, unsigned int norm) ++{ ++ unsigned long flags; ++ ++ btv->input = input; ++ if (irq_iswitch) { ++ spin_lock_irqsave(&btv->s_lock,flags); ++ if (btv->curr.frame_irq) { ++ /* active capture -> delayed input switch */ ++ btv->new_input = input; ++ } else { ++ video_mux(btv,input); ++ } ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++ } else { ++ video_mux(btv,input); ++ } ++ audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ? ++ TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN); ++ set_tvnorm(btv, norm); ++} ++ ++static void init_irqreg(struct bttv *btv) ++{ ++ /* clear status */ ++ btwrite(0xfffffUL, BT848_INT_STAT); ++ ++ if (bttv_tvcards[btv->c.type].no_video) { ++ /* i2c only */ ++ btwrite(BT848_INT_I2CDONE, ++ BT848_INT_MASK); ++ } else { ++ /* full video */ ++ btwrite((btv->triton1) | ++ (btv->gpioirq ? BT848_INT_GPINT : 0) | ++ BT848_INT_SCERR | ++ (fdsr ? BT848_INT_FDSR : 0) | ++ BT848_INT_RISCI | BT848_INT_OCERR | ++ BT848_INT_FMTCHG|BT848_INT_HLOCK| ++ BT848_INT_I2CDONE, ++ BT848_INT_MASK); ++ } ++} ++ ++static void init_bt848(struct bttv *btv) ++{ ++ int val; ++ ++ if (bttv_tvcards[btv->c.type].no_video) { ++ /* very basic init only */ ++ init_irqreg(btv); ++ return; ++ } ++ ++ btwrite(0x00, BT848_CAP_CTL); ++ btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); ++ btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM); ++ ++ /* set planar and packed mode trigger points and */ ++ /* set rising edge of inverted GPINTR pin as irq trigger */ ++ btwrite(BT848_GPIO_DMA_CTL_PKTP_32| ++ BT848_GPIO_DMA_CTL_PLTP1_16| ++ BT848_GPIO_DMA_CTL_PLTP23_16| ++ BT848_GPIO_DMA_CTL_GPINTC| ++ BT848_GPIO_DMA_CTL_GPINTI, ++ BT848_GPIO_DMA_CTL); ++ ++ val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; ++ btwrite(val, BT848_E_SCLOOP); ++ btwrite(val, BT848_O_SCLOOP); ++ ++ btwrite(0x20, BT848_E_VSCALE_HI); ++ btwrite(0x20, BT848_O_VSCALE_HI); ++ btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), ++ BT848_ADC); ++ ++ btwrite(whitecrush_upper, BT848_WC_UP); ++ btwrite(whitecrush_lower, BT848_WC_DOWN); ++ ++ if (btv->opt_lumafilter) { ++ btwrite(0, BT848_E_CONTROL); ++ btwrite(0, BT848_O_CONTROL); ++ } else { ++ btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); ++ btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); ++ } ++ ++ bt848_bright(btv, btv->bright); ++ bt848_hue(btv, btv->hue); ++ bt848_contrast(btv, btv->contrast); ++ bt848_sat(btv, btv->saturation); ++ ++ /* interrupt */ ++ init_irqreg(btv); ++} ++ ++static void bttv_reinit_bt848(struct bttv *btv) ++{ ++ unsigned long flags; ++ ++ if (bttv_verbose) ++ pr_info("%d: reset, reinitialize\n", btv->c.nr); ++ spin_lock_irqsave(&btv->s_lock,flags); ++ btv->errors=0; ++ bttv_set_dma(btv,0); ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++ ++ init_bt848(btv); ++ btv->pll.pll_current = -1; ++ set_input(btv, btv->input, btv->tvnorm); ++} ++ ++static int bttv_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *c) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ c->value = btv->bright; ++ break; ++ case V4L2_CID_HUE: ++ c->value = btv->hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ c->value = btv->contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ c->value = btv->saturation; ++ break; ++ case V4L2_CID_COLOR_KILLER: ++ c->value = btv->opt_color_killer; ++ break; ++ ++ case V4L2_CID_AUDIO_MUTE: ++ case V4L2_CID_AUDIO_VOLUME: ++ case V4L2_CID_AUDIO_BALANCE: ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ bttv_call_all(btv, core, g_ctrl, c); ++ break; ++ ++ case V4L2_CID_PRIVATE_CHROMA_AGC: ++ c->value = btv->opt_chroma_agc; ++ break; ++ case V4L2_CID_PRIVATE_COMBFILTER: ++ c->value = btv->opt_combfilter; ++ break; ++ case V4L2_CID_PRIVATE_LUMAFILTER: ++ c->value = btv->opt_lumafilter; ++ break; ++ case V4L2_CID_PRIVATE_AUTOMUTE: ++ c->value = btv->opt_automute; ++ break; ++ case V4L2_CID_PRIVATE_AGC_CRUSH: ++ c->value = btv->opt_adc_crush; ++ break; ++ case V4L2_CID_PRIVATE_VCR_HACK: ++ c->value = btv->opt_vcr_hack; ++ break; ++ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: ++ c->value = btv->opt_whitecrush_upper; ++ break; ++ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: ++ c->value = btv->opt_whitecrush_lower; ++ break; ++ case V4L2_CID_PRIVATE_UV_RATIO: ++ c->value = btv->opt_uv_ratio; ++ break; ++ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: ++ c->value = btv->opt_full_luma_range; ++ break; ++ case V4L2_CID_PRIVATE_CORING: ++ c->value = btv->opt_coring; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int bttv_s_ctrl(struct file *file, void *f, ++ struct v4l2_control *c) ++{ ++ int err; ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ err = v4l2_prio_check(&btv->prio, fh->prio); ++ if (0 != err) ++ return err; ++ ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ bt848_bright(btv, c->value); ++ break; ++ case V4L2_CID_HUE: ++ bt848_hue(btv, c->value); ++ break; ++ case V4L2_CID_CONTRAST: ++ bt848_contrast(btv, c->value); ++ break; ++ case V4L2_CID_SATURATION: ++ bt848_sat(btv, c->value); ++ break; ++ case V4L2_CID_COLOR_KILLER: ++ btv->opt_color_killer = c->value; ++ if (btv->opt_color_killer) { ++ btor(BT848_SCLOOP_CKILL, BT848_E_SCLOOP); ++ btor(BT848_SCLOOP_CKILL, BT848_O_SCLOOP); ++ } else { ++ btand(~BT848_SCLOOP_CKILL, BT848_E_SCLOOP); ++ btand(~BT848_SCLOOP_CKILL, BT848_O_SCLOOP); ++ } ++ break; ++ case V4L2_CID_AUDIO_MUTE: ++ audio_mute(btv, c->value); ++ /* fall through */ ++ case V4L2_CID_AUDIO_VOLUME: ++ if (btv->volume_gpio) ++ btv->volume_gpio(btv, c->value); ++ ++ bttv_call_all(btv, core, s_ctrl, c); ++ break; ++ case V4L2_CID_AUDIO_BALANCE: ++ case V4L2_CID_AUDIO_BASS: ++ case V4L2_CID_AUDIO_TREBLE: ++ bttv_call_all(btv, core, s_ctrl, c); ++ break; ++ ++ case V4L2_CID_PRIVATE_CHROMA_AGC: ++ btv->opt_chroma_agc = c->value; ++ if (btv->opt_chroma_agc) { ++ btor(BT848_SCLOOP_CAGC, BT848_E_SCLOOP); ++ btor(BT848_SCLOOP_CAGC, BT848_O_SCLOOP); ++ } else { ++ btand(~BT848_SCLOOP_CAGC, BT848_E_SCLOOP); ++ btand(~BT848_SCLOOP_CAGC, BT848_O_SCLOOP); ++ } ++ break; ++ case V4L2_CID_PRIVATE_COMBFILTER: ++ btv->opt_combfilter = c->value; ++ break; ++ case V4L2_CID_PRIVATE_LUMAFILTER: ++ btv->opt_lumafilter = c->value; ++ if (btv->opt_lumafilter) { ++ btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL); ++ btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL); ++ } else { ++ btor(BT848_CONTROL_LDEC, BT848_E_CONTROL); ++ btor(BT848_CONTROL_LDEC, BT848_O_CONTROL); ++ } ++ break; ++ case V4L2_CID_PRIVATE_AUTOMUTE: ++ btv->opt_automute = c->value; ++ break; ++ case V4L2_CID_PRIVATE_AGC_CRUSH: ++ btv->opt_adc_crush = c->value; ++ btwrite(BT848_ADC_RESERVED | ++ (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), ++ BT848_ADC); ++ break; ++ case V4L2_CID_PRIVATE_VCR_HACK: ++ btv->opt_vcr_hack = c->value; ++ break; ++ case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: ++ btv->opt_whitecrush_upper = c->value; ++ btwrite(c->value, BT848_WC_UP); ++ break; ++ case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: ++ btv->opt_whitecrush_lower = c->value; ++ btwrite(c->value, BT848_WC_DOWN); ++ break; ++ case V4L2_CID_PRIVATE_UV_RATIO: ++ btv->opt_uv_ratio = c->value; ++ bt848_sat(btv, btv->saturation); ++ break; ++ case V4L2_CID_PRIVATE_FULL_LUMA_RANGE: ++ btv->opt_full_luma_range = c->value; ++ btaor((c->value<<7), ~BT848_OFORM_RANGE, BT848_OFORM); ++ break; ++ case V4L2_CID_PRIVATE_CORING: ++ btv->opt_coring = c->value; ++ btaor((c->value<<5), ~BT848_OFORM_CORE32, BT848_OFORM); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++void bttv_gpio_tracking(struct bttv *btv, char *comment) ++{ ++ unsigned int outbits, data; ++ outbits = btread(BT848_GPIO_OUT_EN); ++ data = btread(BT848_GPIO_DATA); ++ pr_debug("%d: gpio: en=%08x, out=%08x in=%08x [%s]\n", ++ btv->c.nr, outbits, data & outbits, data & ~outbits, comment); ++} ++ ++static void bttv_field_count(struct bttv *btv) ++{ ++ int need_count = 0; ++ ++ if (btv->users) ++ need_count++; ++ ++ if (need_count) { ++ /* start field counter */ ++ btor(BT848_INT_VSYNC,BT848_INT_MASK); ++ } else { ++ /* stop field counter */ ++ btand(~BT848_INT_VSYNC,BT848_INT_MASK); ++ btv->field_count = 0; ++ } ++} ++ ++static const struct bttv_format* ++format_by_fourcc(int fourcc) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < FORMATS; i++) { ++ if (-1 == formats[i].fourcc) ++ continue; ++ if (formats[i].fourcc == fourcc) ++ return formats+i; ++ } ++ return NULL; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* misc helpers */ ++ ++static int ++bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, ++ struct bttv_buffer *new) ++{ ++ struct bttv_buffer *old; ++ unsigned long flags; ++ int retval = 0; ++ ++ dprintk("switch_overlay: enter [new=%p]\n", new); ++ if (new) ++ new->vb.state = VIDEOBUF_DONE; ++ spin_lock_irqsave(&btv->s_lock,flags); ++ old = btv->screen; ++ btv->screen = new; ++ btv->loop_irq |= 1; ++ bttv_set_dma(btv, 0x03); ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++ if (NULL != old) { ++ dprintk("switch_overlay: old=%p state is %d\n", ++ old, old->vb.state); ++ bttv_dma_free(&fh->cap,btv, old); ++ kfree(old); ++ } ++ if (NULL == new) ++ free_btres_lock(btv,fh,RESOURCE_OVERLAY); ++ dprintk("switch_overlay: done\n"); ++ return retval; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* video4linux (1) interface */ ++ ++static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, ++ struct bttv_buffer *buf, ++ const struct bttv_format *fmt, ++ unsigned int width, unsigned int height, ++ enum v4l2_field field) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ int redo_dma_risc = 0; ++ struct bttv_crop c; ++ int norm; ++ int rc; ++ ++ /* check settings */ ++ if (NULL == fmt) ++ return -EINVAL; ++ if (fmt->btformat == BT848_COLOR_FMT_RAW) { ++ width = RAW_BPL; ++ height = RAW_LINES*2; ++ if (width*height > buf->vb.bsize) ++ return -EINVAL; ++ buf->vb.size = buf->vb.bsize; ++ ++ /* Make sure tvnorm and vbi_end remain consistent ++ until we're done. */ ++ ++ norm = btv->tvnorm; ++ ++ /* In this mode capturing always starts at defrect.top ++ (default VDELAY), ignoring cropping parameters. */ ++ if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) { ++ return -EINVAL; ++ } ++ ++ c.rect = bttv_tvnorms[norm].cropcap.defrect; ++ } else { ++ norm = btv->tvnorm; ++ c = btv->crop[!!fh->do_crop]; ++ ++ if (width < c.min_scaled_width || ++ width > c.max_scaled_width || ++ height < c.min_scaled_height) ++ return -EINVAL; ++ ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ case V4L2_FIELD_ALTERNATE: ++ /* btv->crop counts frame lines. Max. scale ++ factor is 16:1 for frames, 8:1 for fields. */ ++ if (height * 2 > c.max_scaled_height) ++ return -EINVAL; ++ break; ++ ++ default: ++ if (height > c.max_scaled_height) ++ return -EINVAL; ++ break; ++ } ++ ++ buf->vb.size = (width * height * fmt->depth) >> 3; ++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ } ++ ++ /* alloc + fill struct bttv_buffer (if changed) */ ++ if (buf->vb.width != width || buf->vb.height != height || ++ buf->vb.field != field || ++ buf->tvnorm != norm || buf->fmt != fmt || ++ buf->crop.top != c.rect.top || ++ buf->crop.left != c.rect.left || ++ buf->crop.width != c.rect.width || ++ buf->crop.height != c.rect.height) { ++ buf->vb.width = width; ++ buf->vb.height = height; ++ buf->vb.field = field; ++ buf->tvnorm = norm; ++ buf->fmt = fmt; ++ buf->crop = c.rect; ++ redo_dma_risc = 1; ++ } ++ ++ /* alloc risc memory */ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ redo_dma_risc = 1; ++ if (0 != (rc = videobuf_iolock(q,&buf->vb,&btv->fbuf))) ++ goto fail; ++ } ++ ++ if (redo_dma_risc) ++ if (0 != (rc = bttv_buffer_risc(btv,buf))) ++ goto fail; ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ bttv_dma_free(q,btv,buf); ++ return rc; ++} ++ ++static int ++buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ ++ *size = fh->fmt->depth*fh->width*fh->height >> 3; ++ if (0 == *count) ++ *count = gbuffers; ++ if (*size * *count > gbuffers * gbufsize) ++ *count = (gbuffers * gbufsize) / *size; ++ return 0; ++} ++ ++static int ++buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ struct bttv_fh *fh = q->priv_data; ++ ++ return bttv_prepare_buffer(q,fh->btv, buf, fh->fmt, ++ fh->width, fh->height, field); ++} ++ ++static void ++buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ struct bttv_fh *fh = q->priv_data; ++ struct bttv *btv = fh->btv; ++ ++ buf->vb.state = VIDEOBUF_QUEUED; ++ list_add_tail(&buf->vb.queue,&btv->capture); ++ if (!btv->curr.frame_irq) { ++ btv->loop_irq |= 1; ++ bttv_set_dma(btv, 0x03); ++ } ++} ++ ++static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ struct bttv_fh *fh = q->priv_data; ++ ++ bttv_dma_free(q,fh->btv,buf); ++} ++ ++static struct videobuf_queue_ops bttv_video_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++static int bttv_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ unsigned int i; ++ int err; ++ ++ err = v4l2_prio_check(&btv->prio, fh->prio); ++ if (err) ++ goto err; ++ ++ for (i = 0; i < BTTV_TVNORMS; i++) ++ if (*id & bttv_tvnorms[i].v4l2_id) ++ break; ++ if (i == BTTV_TVNORMS) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ set_tvnorm(btv, i); ++ ++err: ++ ++ return err; ++} ++ ++static int bttv_querystd(struct file *file, void *f, v4l2_std_id *id) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML) ++ *id = V4L2_STD_625_50; ++ else ++ *id = V4L2_STD_525_60; ++ return 0; ++} ++ ++static int bttv_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int rc = 0; ++ ++ if (i->index >= bttv_tvcards[btv->c.type].video_inputs) { ++ rc = -EINVAL; ++ goto err; ++ } ++ ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ i->audioset = 1; ++ ++ if (btv->tuner_type != TUNER_ABSENT && i->index == 0) { ++ sprintf(i->name, "Television"); ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ i->tuner = 0; ++ } else if (i->index == btv->svhs) { ++ sprintf(i->name, "S-Video"); ++ } else { ++ sprintf(i->name, "Composite%d", i->index); ++ } ++ ++ if (i->index == btv->input) { ++ __u32 dstatus = btread(BT848_DSTATUS); ++ if (0 == (dstatus & BT848_DSTATUS_PRES)) ++ i->status |= V4L2_IN_ST_NO_SIGNAL; ++ if (0 == (dstatus & BT848_DSTATUS_HLOC)) ++ i->status |= V4L2_IN_ST_NO_H_LOCK; ++ } ++ ++ i->std = BTTV_NORMS; ++ ++err: ++ ++ return rc; ++} ++ ++static int bttv_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ *i = btv->input; ++ ++ return 0; ++} ++ ++static int bttv_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ int err; ++ ++ err = v4l2_prio_check(&btv->prio, fh->prio); ++ if (unlikely(err)) ++ goto err; ++ ++ if (i > bttv_tvcards[btv->c.type].video_inputs) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ set_input(btv, i, btv->tvnorm); ++ ++err: ++ return 0; ++} ++ ++static int bttv_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int err; ++ ++ if (unlikely(0 != t->index)) ++ return -EINVAL; ++ ++ if (unlikely(btv->tuner_type == TUNER_ABSENT)) { ++ err = -EINVAL; ++ goto err; ++ } ++ ++ err = v4l2_prio_check(&btv->prio, fh->prio); ++ if (unlikely(err)) ++ goto err; ++ ++ bttv_call_all(btv, tuner, s_tuner, t); ++ ++ if (btv->audio_mode_gpio) ++ btv->audio_mode_gpio(btv, t, 1); ++ ++err: ++ ++ return 0; ++} ++ ++static int bttv_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ++ f->frequency = btv->freq; ++ ++ return 0; ++} ++ ++static int bttv_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int err; ++ ++ if (unlikely(f->tuner != 0)) ++ return -EINVAL; ++ ++ err = v4l2_prio_check(&btv->prio, fh->prio); ++ if (unlikely(err)) ++ goto err; ++ ++ if (unlikely(f->type != (btv->radio_user ++ ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) { ++ err = -EINVAL; ++ goto err; ++ } ++ btv->freq = f->frequency; ++ bttv_call_all(btv, tuner, s_frequency, f); ++ if (btv->has_matchbox && btv->radio_user) ++ tea5757_set_freq(btv, btv->freq); ++err: ++ ++ return 0; ++} ++ ++static int bttv_log_status(struct file *file, void *f) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ bttv_call_all(btv, core, log_status); ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int bttv_g_register(struct file *file, void *f, ++ struct v4l2_dbg_register *reg) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ ++ /* bt848 has a 12-bit register space */ ++ reg->reg &= 0xfff; ++ reg->val = btread(reg->reg); ++ reg->size = 1; ++ ++ return 0; ++} ++ ++static int bttv_s_register(struct file *file, void *f, ++ struct v4l2_dbg_register *reg) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ ++ /* bt848 has a 12-bit register space */ ++ reg->reg &= 0xfff; ++ btwrite(reg->val, reg->reg); ++ ++ return 0; ++} ++#endif ++ ++/* Given cropping boundaries b and the scaled width and height of a ++ single field or frame, which must not exceed hardware limits, this ++ function adjusts the cropping parameters c. */ ++static void ++bttv_crop_adjust (struct bttv_crop * c, ++ const struct v4l2_rect * b, ++ __s32 width, ++ __s32 height, ++ enum v4l2_field field) ++{ ++ __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field); ++ __s32 max_left; ++ __s32 max_top; ++ ++ if (width < c->min_scaled_width) { ++ /* Max. hor. scale factor 16:1. */ ++ c->rect.width = width * 16; ++ } else if (width > c->max_scaled_width) { ++ /* Min. hor. scale factor 1:1. */ ++ c->rect.width = width; ++ ++ max_left = b->left + b->width - width; ++ max_left = min(max_left, (__s32) MAX_HDELAY); ++ if (c->rect.left > max_left) ++ c->rect.left = max_left; ++ } ++ ++ if (height < c->min_scaled_height) { ++ /* Max. vert. scale factor 16:1, single fields 8:1. */ ++ c->rect.height = height * 16; ++ } else if (frame_height > c->max_scaled_height) { ++ /* Min. vert. scale factor 1:1. ++ Top and height count field lines times two. */ ++ c->rect.height = (frame_height + 1) & ~1; ++ ++ max_top = b->top + b->height - c->rect.height; ++ if (c->rect.top > max_top) ++ c->rect.top = max_top; ++ } ++ ++ bttv_crop_calc_limits(c); ++} ++ ++/* Returns an error if scaling to a frame or single field with the given ++ width and height is not possible with the current cropping parameters ++ and width aligned according to width_mask. If adjust_size is TRUE the ++ function may adjust the width and/or height instead, rounding width ++ to (width + width_bias) & width_mask. If adjust_crop is TRUE it may ++ also adjust the current cropping parameters to get closer to the ++ desired image size. */ ++static int ++limit_scaled_size_lock (struct bttv_fh * fh, ++ __s32 * width, ++ __s32 * height, ++ enum v4l2_field field, ++ unsigned int width_mask, ++ unsigned int width_bias, ++ int adjust_size, ++ int adjust_crop) ++{ ++ struct bttv *btv = fh->btv; ++ const struct v4l2_rect *b; ++ struct bttv_crop *c; ++ __s32 min_width; ++ __s32 min_height; ++ __s32 max_width; ++ __s32 max_height; ++ int rc; ++ ++ BUG_ON((int) width_mask >= 0 || ++ width_bias >= (unsigned int) -width_mask); ++ ++ /* Make sure tvnorm, vbi_end and the current cropping parameters ++ remain consistent until we're done. */ ++ ++ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; ++ ++ /* Do crop - use current, don't - use default parameters. */ ++ c = &btv->crop[!!fh->do_crop]; ++ ++ if (fh->do_crop ++ && adjust_size ++ && adjust_crop ++ && !locked_btres(btv, VIDEO_RESOURCES)) { ++ min_width = 48; ++ min_height = 32; ++ ++ /* We cannot scale up. When the scaled image is larger ++ than crop.rect we adjust the crop.rect as required ++ by the V4L2 spec, hence cropcap.bounds are our limit. */ ++ max_width = min(b->width, (__s32) MAX_HACTIVE); ++ max_height = b->height; ++ ++ /* We cannot capture the same line as video and VBI data. ++ Note btv->vbi_end is really a minimum, see ++ bttv_vbi_try_fmt(). */ ++ if (btv->vbi_end > b->top) { ++ max_height -= btv->vbi_end - b->top; ++ rc = -EBUSY; ++ if (min_height > max_height) ++ goto fail; ++ } ++ } else { ++ rc = -EBUSY; ++ if (btv->vbi_end > c->rect.top) ++ goto fail; ++ ++ min_width = c->min_scaled_width; ++ min_height = c->min_scaled_height; ++ max_width = c->max_scaled_width; ++ max_height = c->max_scaled_height; ++ ++ adjust_crop = 0; ++ } ++ ++ min_width = (min_width - width_mask - 1) & width_mask; ++ max_width = max_width & width_mask; ++ ++ /* Max. scale factor is 16:1 for frames, 8:1 for fields. */ ++ min_height = min_height; ++ /* Min. scale factor is 1:1. */ ++ max_height >>= !V4L2_FIELD_HAS_BOTH(field); ++ ++ if (adjust_size) { ++ *width = clamp(*width, min_width, max_width); ++ *height = clamp(*height, min_height, max_height); ++ ++ /* Round after clamping to avoid overflow. */ ++ *width = (*width + width_bias) & width_mask; ++ ++ if (adjust_crop) { ++ bttv_crop_adjust(c, b, *width, *height, field); ++ ++ if (btv->vbi_end > c->rect.top) { ++ /* Move the crop window out of the way. */ ++ c->rect.top = btv->vbi_end; ++ } ++ } ++ } else { ++ rc = -EINVAL; ++ if (*width < min_width || ++ *height < min_height || ++ *width > max_width || ++ *height > max_height || ++ 0 != (*width & ~width_mask)) ++ goto fail; ++ } ++ ++ rc = 0; /* success */ ++ ++ fail: ++ ++ return rc; ++} ++ ++/* Returns an error if the given overlay window dimensions are not ++ possible with the current cropping parameters. If adjust_size is ++ TRUE the function may adjust the window width and/or height ++ instead, however it always rounds the horizontal position and ++ width as btcx_align() does. If adjust_crop is TRUE the function ++ may also adjust the current cropping parameters to get closer ++ to the desired window size. */ ++static int ++verify_window_lock (struct bttv_fh * fh, ++ struct v4l2_window * win, ++ int adjust_size, ++ int adjust_crop) ++{ ++ enum v4l2_field field; ++ unsigned int width_mask; ++ int rc; ++ ++ if (win->w.width < 48 || win->w.height < 32) ++ return -EINVAL; ++ if (win->clipcount > 2048) ++ return -EINVAL; ++ ++ field = win->field; ++ ++ if (V4L2_FIELD_ANY == field) { ++ __s32 height2; ++ ++ height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1; ++ field = (win->w.height > height2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_TOP; ++ } ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* 4-byte alignment. */ ++ if (NULL == fh->ovfmt) ++ return -EINVAL; ++ width_mask = ~0; ++ switch (fh->ovfmt->depth) { ++ case 8: ++ case 24: ++ width_mask = ~3; ++ break; ++ case 16: ++ width_mask = ~1; ++ break; ++ case 32: ++ break; ++ default: ++ BUG(); ++ } ++ ++ win->w.width -= win->w.left & ~width_mask; ++ win->w.left = (win->w.left - width_mask - 1) & width_mask; ++ ++ rc = limit_scaled_size_lock(fh, &win->w.width, &win->w.height, ++ field, width_mask, ++ /* width_bias: round down */ 0, ++ adjust_size, adjust_crop); ++ if (0 != rc) ++ return rc; ++ ++ win->field = field; ++ return 0; ++} ++ ++static int setup_window_lock(struct bttv_fh *fh, struct bttv *btv, ++ struct v4l2_window *win, int fixup) ++{ ++ struct v4l2_clip *clips = NULL; ++ int n,size,retval = 0; ++ ++ if (NULL == fh->ovfmt) ++ return -EINVAL; ++ if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED)) ++ return -EINVAL; ++ retval = verify_window_lock(fh, win, ++ /* adjust_size */ fixup, ++ /* adjust_crop */ fixup); ++ if (0 != retval) ++ return retval; ++ ++ /* copy clips -- luckily v4l1 + v4l2 are binary ++ compatible here ...*/ ++ n = win->clipcount; ++ size = sizeof(*clips)*(n+4); ++ clips = kmalloc(size,GFP_KERNEL); ++ if (NULL == clips) ++ return -ENOMEM; ++ if (n > 0) { ++ if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { ++ kfree(clips); ++ return -EFAULT; ++ } ++ } ++ ++ /* clip against screen */ ++ if (NULL != btv->fbuf.base) ++ n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height, ++ &win->w, clips, n); ++ btcx_sort_clips(clips,n); ++ ++ /* 4-byte alignments */ ++ switch (fh->ovfmt->depth) { ++ case 8: ++ case 24: ++ btcx_align(&win->w, clips, n, 3); ++ break; ++ case 16: ++ btcx_align(&win->w, clips, n, 1); ++ break; ++ case 32: ++ /* no alignment fixups needed */ ++ break; ++ default: ++ BUG(); ++ } ++ ++ kfree(fh->ov.clips); ++ fh->ov.clips = clips; ++ fh->ov.nclips = n; ++ ++ fh->ov.w = win->w; ++ fh->ov.field = win->field; ++ fh->ov.setup_ok = 1; ++ ++ btv->init.ov.w.width = win->w.width; ++ btv->init.ov.w.height = win->w.height; ++ btv->init.ov.field = win->field; ++ ++ /* update overlay if needed */ ++ retval = 0; ++ if (check_btres(fh, RESOURCE_OVERLAY)) { ++ struct bttv_buffer *new; ++ ++ new = videobuf_sg_alloc(sizeof(*new)); ++ new->crop = btv->crop[!!fh->do_crop].rect; ++ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); ++ retval = bttv_switch_overlay(btv,fh,new); ++ } ++ return retval; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) ++{ ++ struct videobuf_queue* q = NULL; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ q = &fh->cap; ++ break; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ q = &fh->vbi; ++ break; ++ default: ++ BUG(); ++ } ++ return q; ++} ++ ++static int bttv_resource(struct bttv_fh *fh) ++{ ++ int res = 0; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ res = RESOURCE_VIDEO_STREAM; ++ break; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ res = RESOURCE_VBI; ++ break; ++ default: ++ BUG(); ++ } ++ return res; ++} ++ ++static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) ++{ ++ struct videobuf_queue *q = bttv_queue(fh); ++ int res = bttv_resource(fh); ++ ++ if (check_btres(fh,res)) ++ return -EBUSY; ++ if (videobuf_queue_is_busy(q)) ++ return -EBUSY; ++ fh->type = type; ++ return 0; ++} ++ ++static void ++pix_format_set_size (struct v4l2_pix_format * f, ++ const struct bttv_format * fmt, ++ unsigned int width, ++ unsigned int height) ++{ ++ f->width = width; ++ f->height = height; ++ ++ if (fmt->flags & FORMAT_FLAGS_PLANAR) { ++ f->bytesperline = width; /* Y plane */ ++ f->sizeimage = (width * height * fmt->depth) >> 3; ++ } else { ++ f->bytesperline = (width * fmt->depth) >> 3; ++ f->sizeimage = height * f->bytesperline; ++ } ++} ++ ++static int bttv_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bttv_fh *fh = priv; ++ ++ pix_format_set_size(&f->fmt.pix, fh->fmt, ++ fh->width, fh->height); ++ f->fmt.pix.field = fh->cap.field; ++ f->fmt.pix.pixelformat = fh->fmt->fourcc; ++ ++ return 0; ++} ++ ++static int bttv_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bttv_fh *fh = priv; ++ ++ f->fmt.win.w = fh->ov.w; ++ f->fmt.win.field = fh->ov.field; ++ ++ return 0; ++} ++ ++static int bttv_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ const struct bttv_format *fmt; ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ enum v4l2_field field; ++ __s32 width, height; ++ int rc; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ ++ if (V4L2_FIELD_ANY == field) { ++ __s32 height2; ++ ++ height2 = btv->crop[!!fh->do_crop].rect.height >> 1; ++ field = (f->fmt.pix.height > height2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_BOTTOM; ++ } ++ ++ if (V4L2_FIELD_SEQ_BT == field) ++ field = V4L2_FIELD_SEQ_TB; ++ ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ case V4L2_FIELD_ALTERNATE: ++ case V4L2_FIELD_INTERLACED: ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ if (fmt->flags & FORMAT_FLAGS_PLANAR) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ width = f->fmt.pix.width; ++ height = f->fmt.pix.height; ++ ++ rc = limit_scaled_size_lock(fh, &width, &height, field, ++ /* width_mask: 4 pixels */ ~3, ++ /* width_bias: nearest */ 2, ++ /* adjust_size */ 1, ++ /* adjust_crop */ 0); ++ if (0 != rc) ++ return rc; ++ ++ /* update data for the application */ ++ f->fmt.pix.field = field; ++ pix_format_set_size(&f->fmt.pix, fmt, width, height); ++ ++ return 0; ++} ++ ++static int bttv_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bttv_fh *fh = priv; ++ ++ return verify_window_lock(fh, &f->fmt.win, ++ /* adjust_size */ 1, ++ /* adjust_crop */ 0); ++} ++ ++static int bttv_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int retval; ++ const struct bttv_format *fmt; ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ __s32 width, height; ++ enum v4l2_field field; ++ ++ retval = bttv_switch_type(fh, f->type); ++ if (0 != retval) ++ return retval; ++ ++ retval = bttv_try_fmt_vid_cap(file, priv, f); ++ if (0 != retval) ++ return retval; ++ ++ width = f->fmt.pix.width; ++ height = f->fmt.pix.height; ++ field = f->fmt.pix.field; ++ ++ retval = limit_scaled_size_lock(fh, &width, &height, f->fmt.pix.field, ++ /* width_mask: 4 pixels */ ~3, ++ /* width_bias: nearest */ 2, ++ /* adjust_size */ 1, ++ /* adjust_crop */ 1); ++ if (0 != retval) ++ return retval; ++ ++ f->fmt.pix.field = field; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ ++ /* update our state informations */ ++ fh->fmt = fmt; ++ fh->cap.field = f->fmt.pix.field; ++ fh->cap.last = V4L2_FIELD_NONE; ++ fh->width = f->fmt.pix.width; ++ fh->height = f->fmt.pix.height; ++ btv->init.fmt = fmt; ++ btv->init.width = f->fmt.pix.width; ++ btv->init.height = f->fmt.pix.height; ++ ++ return 0; ++} ++ ++static int bttv_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (no_overlay > 0) { ++ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ ++ return setup_window_lock(fh, btv, &f->fmt.win, 1); ++} ++ ++static int bttv_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (0 == v4l2) ++ return -EINVAL; ++ ++ strlcpy(cap->driver, "bttv", sizeof(cap->driver)); ++ strlcpy(cap->card, btv->video_dev->name, sizeof(cap->card)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "PCI:%s", pci_name(btv->c.pci)); ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_VBI_CAPTURE | ++ V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ if (no_overlay <= 0) ++ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; ++ ++ /* ++ * No need to lock here: those vars are initialized during board ++ * probe and remains untouched during the rest of the driver lifecycle ++ */ ++ if (btv->has_saa6588) ++ cap->capabilities |= V4L2_CAP_RDS_CAPTURE; ++ if (btv->tuner_type != TUNER_ABSENT) ++ cap->capabilities |= V4L2_CAP_TUNER; ++ return 0; ++} ++ ++static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f) ++{ ++ int index = -1, i; ++ ++ for (i = 0; i < FORMATS; i++) { ++ if (formats[i].fourcc != -1) ++ index++; ++ if ((unsigned int)index == f->index) ++ break; ++ } ++ if (FORMATS == i) ++ return -EINVAL; ++ ++ f->pixelformat = formats[i].fourcc; ++ strlcpy(f->description, formats[i].name, sizeof(f->description)); ++ ++ return i; ++} ++ ++static int bttv_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ int rc = bttv_enum_fmt_cap_ovr(f); ++ ++ if (rc < 0) ++ return rc; ++ ++ return 0; ++} ++ ++static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ int rc; ++ ++ if (no_overlay > 0) { ++ pr_err("V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ ++ rc = bttv_enum_fmt_cap_ovr(f); ++ ++ if (rc < 0) ++ return rc; ++ ++ if (!(formats[rc].flags & FORMAT_FLAGS_PACKED)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int bttv_g_fbuf(struct file *file, void *f, ++ struct v4l2_framebuffer *fb) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ *fb = btv->fbuf; ++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; ++ if (fh->ovfmt) ++ fb->fmt.pixelformat = fh->ovfmt->fourcc; ++ return 0; ++} ++ ++static int bttv_overlay(struct file *file, void *f, unsigned int on) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ struct bttv_buffer *new; ++ int retval = 0; ++ ++ if (on) { ++ /* verify args */ ++ if (unlikely(!btv->fbuf.base)) { ++ return -EINVAL; ++ } ++ if (unlikely(!fh->ov.setup_ok)) { ++ dprintk("%d: overlay: !setup_ok\n", btv->c.nr); ++ retval = -EINVAL; ++ } ++ if (retval) ++ return retval; ++ } ++ ++ if (!check_alloc_btres_lock(btv, fh, RESOURCE_OVERLAY)) ++ return -EBUSY; ++ ++ if (on) { ++ fh->ov.tvnorm = btv->tvnorm; ++ new = videobuf_sg_alloc(sizeof(*new)); ++ new->crop = btv->crop[!!fh->do_crop].rect; ++ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); ++ } else { ++ new = NULL; ++ } ++ ++ /* switch over */ ++ retval = bttv_switch_overlay(btv, fh, new); ++ return retval; ++} ++ ++static int bttv_s_fbuf(struct file *file, void *f, ++ const struct v4l2_framebuffer *fb) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ const struct bttv_format *fmt; ++ int retval; ++ ++ if (!capable(CAP_SYS_ADMIN) && ++ !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ /* check args */ ++ fmt = format_by_fourcc(fb->fmt.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) ++ return -EINVAL; ++ ++ retval = -EINVAL; ++ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { ++ __s32 width = fb->fmt.width; ++ __s32 height = fb->fmt.height; ++ ++ retval = limit_scaled_size_lock(fh, &width, &height, ++ V4L2_FIELD_INTERLACED, ++ /* width_mask */ ~3, ++ /* width_bias */ 2, ++ /* adjust_size */ 0, ++ /* adjust_crop */ 0); ++ if (0 != retval) ++ return retval; ++ } ++ ++ /* ok, accept it */ ++ btv->fbuf.base = fb->base; ++ btv->fbuf.fmt.width = fb->fmt.width; ++ btv->fbuf.fmt.height = fb->fmt.height; ++ if (0 != fb->fmt.bytesperline) ++ btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline; ++ else ++ btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8; ++ ++ retval = 0; ++ fh->ovfmt = fmt; ++ btv->init.ovfmt = fmt; ++ if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { ++ fh->ov.w.left = 0; ++ fh->ov.w.top = 0; ++ fh->ov.w.width = fb->fmt.width; ++ fh->ov.w.height = fb->fmt.height; ++ btv->init.ov.w.width = fb->fmt.width; ++ btv->init.ov.w.height = fb->fmt.height; ++ kfree(fh->ov.clips); ++ fh->ov.clips = NULL; ++ fh->ov.nclips = 0; ++ ++ if (check_btres(fh, RESOURCE_OVERLAY)) { ++ struct bttv_buffer *new; ++ ++ new = videobuf_sg_alloc(sizeof(*new)); ++ new->crop = btv->crop[!!fh->do_crop].rect; ++ bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); ++ retval = bttv_switch_overlay(btv, fh, new); ++ } ++ } ++ return retval; ++} ++ ++static int bttv_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct bttv_fh *fh = priv; ++ return videobuf_reqbufs(bttv_queue(fh), p); ++} ++ ++static int bttv_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *b) ++{ ++ struct bttv_fh *fh = priv; ++ return videobuf_querybuf(bttv_queue(fh), b); ++} ++ ++static int bttv_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int res = bttv_resource(fh); ++ ++ if (!check_alloc_btres_lock(btv, fh, res)) ++ return -EBUSY; ++ ++ return videobuf_qbuf(bttv_queue(fh), b); ++} ++ ++static int bttv_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct bttv_fh *fh = priv; ++ return videobuf_dqbuf(bttv_queue(fh), b, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int bttv_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int res = bttv_resource(fh); ++ ++ if (!check_alloc_btres_lock(btv, fh, res)) ++ return -EBUSY; ++ return videobuf_streamon(bttv_queue(fh)); ++} ++ ++ ++static int bttv_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ int retval; ++ int res = bttv_resource(fh); ++ ++ ++ retval = videobuf_streamoff(bttv_queue(fh)); ++ if (retval < 0) ++ return retval; ++ free_btres_lock(btv, fh, res); ++ return 0; ++} ++ ++static int bttv_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ const struct v4l2_queryctrl *ctrl; ++ ++ if ((c->id < V4L2_CID_BASE || ++ c->id >= V4L2_CID_LASTP1) && ++ (c->id < V4L2_CID_PRIVATE_BASE || ++ c->id >= V4L2_CID_PRIVATE_LASTP1)) ++ return -EINVAL; ++ ++ if (!btv->volume_gpio && (c->id == V4L2_CID_AUDIO_VOLUME)) ++ *c = no_ctl; ++ else { ++ ctrl = ctrl_by_id(c->id); ++ ++ *c = (NULL != ctrl) ? *ctrl : no_ctl; ++ } ++ ++ return 0; ++} ++ ++static int bttv_g_parm(struct file *file, void *f, ++ struct v4l2_streamparm *parm) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id, ++ &parm->parm.capture.timeperframe); ++ ++ return 0; ++} ++ ++static int bttv_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (btv->tuner_type == TUNER_ABSENT) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ bttv_call_all(btv, tuner, g_tuner, t); ++ strcpy(t->name, "Television"); ++ t->capability = V4L2_TUNER_CAP_NORM; ++ t->type = V4L2_TUNER_ANALOG_TV; ++ if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ++ t->signal = 0xffff; ++ ++ if (btv->audio_mode_gpio) ++ btv->audio_mode_gpio(btv, t, 0); ++ ++ return 0; ++} ++ ++static int bttv_g_priority(struct file *file, void *f, enum v4l2_priority *p) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ *p = v4l2_prio_max(&btv->prio); ++ ++ return 0; ++} ++ ++static int bttv_s_priority(struct file *file, void *f, ++ enum v4l2_priority prio) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ int rc; ++ ++ rc = v4l2_prio_change(&btv->prio, &fh->prio, prio); ++ ++ return rc; ++} ++ ++static int bttv_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *cap) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ ++ *cap = bttv_tvnorms[btv->tvnorm].cropcap; ++ ++ return 0; ++} ++ ++static int bttv_g_crop(struct file *file, void *f, struct v4l2_crop *crop) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ ++ /* No fh->do_crop = 1; because btv->crop[1] may be ++ inconsistent with fh->width or fh->height and apps ++ do not expect a change here. */ ++ ++ crop->c = btv->crop[!!fh->do_crop].rect; ++ ++ return 0; ++} ++ ++static int bttv_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ const struct v4l2_rect *b; ++ int retval; ++ struct bttv_crop c; ++ __s32 b_left; ++ __s32 b_top; ++ __s32 b_right; ++ __s32 b_bottom; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ ++ /* Make sure tvnorm, vbi_end and the current cropping ++ parameters remain consistent until we're done. Note ++ read() may change vbi_end in check_alloc_btres_lock(). */ ++ retval = v4l2_prio_check(&btv->prio, fh->prio); ++ if (0 != retval) { ++ return retval; ++ } ++ ++ retval = -EBUSY; ++ ++ if (locked_btres(fh->btv, VIDEO_RESOURCES)) { ++ return retval; ++ } ++ ++ b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; ++ ++ b_left = b->left; ++ b_right = b_left + b->width; ++ b_bottom = b->top + b->height; ++ ++ b_top = max(b->top, btv->vbi_end); ++ if (b_top + 32 >= b_bottom) { ++ return retval; ++ } ++ ++ /* Min. scaled size 48 x 32. */ ++ c.rect.left = clamp_t(s32, crop->c.left, b_left, b_right - 48); ++ c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); ++ ++ c.rect.width = clamp_t(s32, crop->c.width, ++ 48, b_right - c.rect.left); ++ ++ c.rect.top = clamp_t(s32, crop->c.top, b_top, b_bottom - 32); ++ /* Top and height must be a multiple of two. */ ++ c.rect.top = (c.rect.top + 1) & ~1; ++ ++ c.rect.height = clamp_t(s32, crop->c.height, ++ 32, b_bottom - c.rect.top); ++ c.rect.height = (c.rect.height + 1) & ~1; ++ ++ bttv_crop_calc_limits(&c); ++ ++ btv->crop[1] = c; ++ ++ fh->do_crop = 1; ++ ++ if (fh->width < c.min_scaled_width) { ++ fh->width = c.min_scaled_width; ++ btv->init.width = c.min_scaled_width; ++ } else if (fh->width > c.max_scaled_width) { ++ fh->width = c.max_scaled_width; ++ btv->init.width = c.max_scaled_width; ++ } ++ ++ if (fh->height < c.min_scaled_height) { ++ fh->height = c.min_scaled_height; ++ btv->init.height = c.min_scaled_height; ++ } else if (fh->height > c.max_scaled_height) { ++ fh->height = c.max_scaled_height; ++ btv->init.height = c.max_scaled_height; ++ } ++ ++ return 0; ++} ++ ++static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a) ++{ ++ if (unlikely(a->index)) ++ return -EINVAL; ++ ++ strcpy(a->name, "audio"); ++ return 0; ++} ++ ++static int bttv_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) ++{ ++ if (unlikely(a->index)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static ssize_t bttv_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct bttv_fh *fh = file->private_data; ++ int retval = 0; ++ ++ if (fh->btv->errors) ++ bttv_reinit_bt848(fh->btv); ++ dprintk("%d: read count=%d type=%s\n", ++ fh->btv->c.nr, (int)count, v4l2_type_names[fh->type]); ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (!check_alloc_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ)) { ++ /* VIDEO_READ in use by another fh, ++ or VIDEO_STREAM by any fh. */ ++ return -EBUSY; ++ } ++ retval = videobuf_read_one(&fh->cap, data, count, ppos, ++ file->f_flags & O_NONBLOCK); ++ free_btres_lock(fh->btv, fh, RESOURCE_VIDEO_READ); ++ break; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI)) ++ return -EBUSY; ++ retval = videobuf_read_stream(&fh->vbi, data, count, ppos, 1, ++ file->f_flags & O_NONBLOCK); ++ break; ++ default: ++ BUG(); ++ } ++ return retval; ++} ++ ++static unsigned int bttv_poll(struct file *file, poll_table *wait) ++{ ++ struct bttv_fh *fh = file->private_data; ++ struct bttv_buffer *buf; ++ enum v4l2_field field; ++ unsigned int rc = POLLERR; ++ ++ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { ++ if (!check_alloc_btres_lock(fh->btv,fh,RESOURCE_VBI)) ++ return POLLERR; ++ return videobuf_poll_stream(file, &fh->vbi, wait); ++ } ++ ++ if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { ++ /* streaming capture */ ++ if (list_empty(&fh->cap.stream)) ++ goto err; ++ buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); ++ } else { ++ /* read() capture */ ++ if (NULL == fh->cap.read_buf) { ++ /* need to capture a new frame */ ++ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) ++ goto err; ++ fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize); ++ if (NULL == fh->cap.read_buf) ++ goto err; ++ fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; ++ field = videobuf_next_field(&fh->cap); ++ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,field)) { ++ kfree (fh->cap.read_buf); ++ fh->cap.read_buf = NULL; ++ goto err; ++ } ++ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); ++ fh->cap.read_off = 0; ++ } ++ buf = (struct bttv_buffer*)fh->cap.read_buf; ++ } ++ ++ poll_wait(file, &buf->vb.done, wait); ++ if (buf->vb.state == VIDEOBUF_DONE || ++ buf->vb.state == VIDEOBUF_ERROR) ++ rc = POLLIN|POLLRDNORM; ++ else ++ rc = 0; ++err: ++ return rc; ++} ++ ++static int bttv_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct bttv *btv = video_drvdata(file); ++ struct bttv_fh *fh; ++ enum v4l2_buf_type type = 0; ++ ++ dprintk("open dev=%s\n", video_device_node_name(vdev)); ++ ++ if (vdev->vfl_type == VFL_TYPE_GRABBER) { ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ } else if (vdev->vfl_type == VFL_TYPE_VBI) { ++ type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ } else { ++ WARN_ON(1); ++ return -ENODEV; ++ } ++ ++ dprintk("%d: open called (type=%s)\n", ++ btv->c.nr, v4l2_type_names[type]); ++ ++ /* allocate per filehandle data */ ++ fh = kmalloc(sizeof(*fh), GFP_KERNEL); ++ if (unlikely(!fh)) ++ return -ENOMEM; ++ file->private_data = fh; ++ ++ *fh = btv->init; ++ ++ fh->type = type; ++ fh->ov.setup_ok = 0; ++ ++ v4l2_prio_open(&btv->prio, &fh->prio); ++ ++ videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, ++ &btv->c.pci->dev, &btv->s_lock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct bttv_buffer), ++ fh, &btv->lock); ++ videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops, ++ &btv->c.pci->dev, &btv->s_lock, ++ V4L2_BUF_TYPE_VBI_CAPTURE, ++ V4L2_FIELD_SEQ_TB, ++ sizeof(struct bttv_buffer), ++ fh, &btv->lock); ++ set_tvnorm(btv,btv->tvnorm); ++ set_input(btv, btv->input, btv->tvnorm); ++ ++ btv->users++; ++ ++ /* The V4L2 spec requires one global set of cropping parameters ++ which only change on request. These are stored in btv->crop[1]. ++ However for compatibility with V4L apps and cropping unaware ++ V4L2 apps we now reset the cropping parameters as seen through ++ this fh, which is to say VIDIOC_G_CROP and scaling limit checks ++ will use btv->crop[0], the default cropping parameters for the ++ current video standard, and VIDIOC_S_FMT will not implicitely ++ change the cropping parameters until VIDIOC_S_CROP has been ++ called. */ ++ fh->do_crop = !reset_crop; /* module parameter */ ++ ++ /* Likewise there should be one global set of VBI capture ++ parameters, but for compatibility with V4L apps and earlier ++ driver versions each fh has its own parameters. */ ++ bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm); ++ ++ bttv_field_count(btv); ++ return 0; ++} ++ ++static int bttv_release(struct file *file) ++{ ++ struct bttv_fh *fh = file->private_data; ++ struct bttv *btv = fh->btv; ++ ++ /* turn off overlay */ ++ if (check_btres(fh, RESOURCE_OVERLAY)) ++ bttv_switch_overlay(btv,fh,NULL); ++ ++ /* stop video capture */ ++ if (check_btres(fh, RESOURCE_VIDEO_STREAM)) { ++ videobuf_streamoff(&fh->cap); ++ free_btres_lock(btv,fh,RESOURCE_VIDEO_STREAM); ++ } ++ if (fh->cap.read_buf) { ++ buffer_release(&fh->cap,fh->cap.read_buf); ++ kfree(fh->cap.read_buf); ++ } ++ if (check_btres(fh, RESOURCE_VIDEO_READ)) { ++ free_btres_lock(btv, fh, RESOURCE_VIDEO_READ); ++ } ++ ++ /* stop vbi capture */ ++ if (check_btres(fh, RESOURCE_VBI)) { ++ videobuf_stop(&fh->vbi); ++ free_btres_lock(btv,fh,RESOURCE_VBI); ++ } ++ ++ /* free stuff */ ++ ++ videobuf_mmap_free(&fh->cap); ++ videobuf_mmap_free(&fh->vbi); ++ v4l2_prio_close(&btv->prio, fh->prio); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ btv->users--; ++ bttv_field_count(btv); ++ ++ if (!btv->users) ++ audio_mute(btv, 1); ++ ++ return 0; ++} ++ ++static int ++bttv_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct bttv_fh *fh = file->private_data; ++ ++ dprintk("%d: mmap type=%s 0x%lx+%ld\n", ++ fh->btv->c.nr, v4l2_type_names[fh->type], ++ vma->vm_start, vma->vm_end - vma->vm_start); ++ return videobuf_mmap_mapper(bttv_queue(fh),vma); ++} ++ ++static const struct v4l2_file_operations bttv_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = bttv_open, ++ .release = bttv_release, ++ .unlocked_ioctl = video_ioctl2, ++ .read = bttv_read, ++ .mmap = bttv_mmap, ++ .poll = bttv_poll, ++}; ++ ++static const struct v4l2_ioctl_ops bttv_ioctl_ops = { ++ .vidioc_querycap = bttv_querycap, ++ .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap, ++ .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay, ++ .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap, ++ .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap, ++ .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap, ++ .vidioc_g_audio = bttv_g_audio, ++ .vidioc_s_audio = bttv_s_audio, ++ .vidioc_cropcap = bttv_cropcap, ++ .vidioc_reqbufs = bttv_reqbufs, ++ .vidioc_querybuf = bttv_querybuf, ++ .vidioc_qbuf = bttv_qbuf, ++ .vidioc_dqbuf = bttv_dqbuf, ++ .vidioc_s_std = bttv_s_std, ++ .vidioc_enum_input = bttv_enum_input, ++ .vidioc_g_input = bttv_g_input, ++ .vidioc_s_input = bttv_s_input, ++ .vidioc_queryctrl = bttv_queryctrl, ++ .vidioc_g_ctrl = bttv_g_ctrl, ++ .vidioc_s_ctrl = bttv_s_ctrl, ++ .vidioc_streamon = bttv_streamon, ++ .vidioc_streamoff = bttv_streamoff, ++ .vidioc_g_tuner = bttv_g_tuner, ++ .vidioc_s_tuner = bttv_s_tuner, ++ .vidioc_g_crop = bttv_g_crop, ++ .vidioc_s_crop = bttv_s_crop, ++ .vidioc_g_fbuf = bttv_g_fbuf, ++ .vidioc_s_fbuf = bttv_s_fbuf, ++ .vidioc_overlay = bttv_overlay, ++ .vidioc_g_priority = bttv_g_priority, ++ .vidioc_s_priority = bttv_s_priority, ++ .vidioc_g_parm = bttv_g_parm, ++ .vidioc_g_frequency = bttv_g_frequency, ++ .vidioc_s_frequency = bttv_s_frequency, ++ .vidioc_log_status = bttv_log_status, ++ .vidioc_querystd = bttv_querystd, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = bttv_g_register, ++ .vidioc_s_register = bttv_s_register, ++#endif ++}; ++ ++static struct video_device bttv_video_template = { ++ .fops = &bttv_fops, ++ .ioctl_ops = &bttv_ioctl_ops, ++ .tvnorms = BTTV_NORMS, ++ .current_norm = V4L2_STD_PAL, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* radio interface */ ++ ++static int radio_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct bttv *btv = video_drvdata(file); ++ struct bttv_fh *fh; ++ ++ dprintk("open dev=%s\n", video_device_node_name(vdev)); ++ ++ dprintk("%d: open called (radio)\n", btv->c.nr); ++ ++ /* allocate per filehandle data */ ++ fh = kmalloc(sizeof(*fh), GFP_KERNEL); ++ if (unlikely(!fh)) ++ return -ENOMEM; ++ file->private_data = fh; ++ *fh = btv->init; ++ ++ v4l2_prio_open(&btv->prio, &fh->prio); ++ ++ btv->radio_user++; ++ ++ bttv_call_all(btv, tuner, s_radio); ++ audio_input(btv,TVAUDIO_INPUT_RADIO); ++ ++ return 0; ++} ++ ++static int radio_release(struct file *file) ++{ ++ struct bttv_fh *fh = file->private_data; ++ struct bttv *btv = fh->btv; ++ struct saa6588_command cmd; ++ ++ v4l2_prio_close(&btv->prio, fh->prio); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ btv->radio_user--; ++ ++ bttv_call_all(btv, core, ioctl, SAA6588_CMD_CLOSE, &cmd); ++ ++ return 0; ++} ++ ++static int radio_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ strcpy(cap->driver, "bttv"); ++ strlcpy(cap->card, btv->radio_dev->name, sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(btv->c.pci)); ++ cap->capabilities = V4L2_CAP_TUNER; ++ ++ return 0; ++} ++ ++static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (btv->tuner_type == TUNER_ABSENT) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ strcpy(t->name, "Radio"); ++ t->type = V4L2_TUNER_RADIO; ++ ++ bttv_call_all(btv, tuner, g_tuner, t); ++ ++ if (btv->audio_mode_gpio) ++ btv->audio_mode_gpio(btv, t, 0); ++ ++ return 0; ++} ++ ++static int radio_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ if (i->index != 0) ++ return -EINVAL; ++ ++ strcpy(i->name, "Radio"); ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ ++ return 0; ++} ++ ++static int radio_g_audio(struct file *file, void *priv, ++ struct v4l2_audio *a) ++{ ++ if (unlikely(a->index)) ++ return -EINVAL; ++ ++ strcpy(a->name, "Radio"); ++ ++ return 0; ++} ++ ++static int radio_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct bttv_fh *fh = priv; ++ struct bttv *btv = fh->btv; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ ++ bttv_call_all(btv, tuner, s_tuner, t); ++ return 0; ++} ++ ++static int radio_s_audio(struct file *file, void *priv, ++ const struct v4l2_audio *a) ++{ ++ if (unlikely(a->index)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int radio_s_input(struct file *filp, void *priv, unsigned int i) ++{ ++ if (unlikely(i)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) ++{ ++ return 0; ++} ++ ++static int radio_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ const struct v4l2_queryctrl *ctrl; ++ ++ if (c->id < V4L2_CID_BASE || ++ c->id >= V4L2_CID_LASTP1) ++ return -EINVAL; ++ ++ if (c->id == V4L2_CID_AUDIO_MUTE) { ++ ctrl = ctrl_by_id(c->id); ++ *c = *ctrl; ++ } else ++ *c = no_ctl; ++ ++ return 0; ++} ++ ++static int radio_g_input(struct file *filp, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static ssize_t radio_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct bttv_fh *fh = file->private_data; ++ struct bttv *btv = fh->btv; ++ struct saa6588_command cmd; ++ cmd.block_count = count/3; ++ cmd.buffer = data; ++ cmd.instance = file; ++ cmd.result = -ENODEV; ++ ++ bttv_call_all(btv, core, ioctl, SAA6588_CMD_READ, &cmd); ++ ++ return cmd.result; ++} ++ ++static unsigned int radio_poll(struct file *file, poll_table *wait) ++{ ++ struct bttv_fh *fh = file->private_data; ++ struct bttv *btv = fh->btv; ++ struct saa6588_command cmd; ++ cmd.instance = file; ++ cmd.event_list = wait; ++ cmd.result = -ENODEV; ++ bttv_call_all(btv, core, ioctl, SAA6588_CMD_POLL, &cmd); ++ ++ return cmd.result; ++} ++ ++static const struct v4l2_file_operations radio_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = radio_open, ++ .read = radio_read, ++ .release = radio_release, ++ .unlocked_ioctl = video_ioctl2, ++ .poll = radio_poll, ++}; ++ ++static const struct v4l2_ioctl_ops radio_ioctl_ops = { ++ .vidioc_querycap = radio_querycap, ++ .vidioc_g_tuner = radio_g_tuner, ++ .vidioc_enum_input = radio_enum_input, ++ .vidioc_g_audio = radio_g_audio, ++ .vidioc_s_tuner = radio_s_tuner, ++ .vidioc_s_audio = radio_s_audio, ++ .vidioc_s_input = radio_s_input, ++ .vidioc_s_std = radio_s_std, ++ .vidioc_queryctrl = radio_queryctrl, ++ .vidioc_g_input = radio_g_input, ++ .vidioc_g_ctrl = bttv_g_ctrl, ++ .vidioc_s_ctrl = bttv_s_ctrl, ++ .vidioc_g_frequency = bttv_g_frequency, ++ .vidioc_s_frequency = bttv_s_frequency, ++}; ++ ++static struct video_device radio_template = { ++ .fops = &radio_fops, ++ .ioctl_ops = &radio_ioctl_ops, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* some debug code */ ++ ++static int bttv_risc_decode(u32 risc) ++{ ++ static char *instr[16] = { ++ [ BT848_RISC_WRITE >> 28 ] = "write", ++ [ BT848_RISC_SKIP >> 28 ] = "skip", ++ [ BT848_RISC_WRITEC >> 28 ] = "writec", ++ [ BT848_RISC_JUMP >> 28 ] = "jump", ++ [ BT848_RISC_SYNC >> 28 ] = "sync", ++ [ BT848_RISC_WRITE123 >> 28 ] = "write123", ++ [ BT848_RISC_SKIP123 >> 28 ] = "skip123", ++ [ BT848_RISC_WRITE1S23 >> 28 ] = "write1s23", ++ }; ++ static int incr[16] = { ++ [ BT848_RISC_WRITE >> 28 ] = 2, ++ [ BT848_RISC_JUMP >> 28 ] = 2, ++ [ BT848_RISC_SYNC >> 28 ] = 2, ++ [ BT848_RISC_WRITE123 >> 28 ] = 5, ++ [ BT848_RISC_SKIP123 >> 28 ] = 2, ++ [ BT848_RISC_WRITE1S23 >> 28 ] = 3, ++ }; ++ static char *bits[] = { ++ "be0", "be1", "be2", "be3/resync", ++ "set0", "set1", "set2", "set3", ++ "clr0", "clr1", "clr2", "clr3", ++ "irq", "res", "eol", "sol", ++ }; ++ int i; ++ ++ pr_cont("0x%08x [ %s", risc, ++ instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); ++ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--) ++ if (risc & (1 << (i + 12))) ++ pr_cont(" %s", bits[i]); ++ pr_cont(" count=%d ]\n", risc & 0xfff); ++ return incr[risc >> 28] ? incr[risc >> 28] : 1; ++} ++ ++static void bttv_risc_disasm(struct bttv *btv, ++ struct btcx_riscmem *risc) ++{ ++ unsigned int i,j,n; ++ ++ pr_info("%s: risc disasm: %p [dma=0x%08lx]\n", ++ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma); ++ for (i = 0; i < (risc->size >> 2); i += n) { ++ pr_info("%s: 0x%lx: ", ++ btv->c.v4l2_dev.name, ++ (unsigned long)(risc->dma + (i<<2))); ++ n = bttv_risc_decode(le32_to_cpu(risc->cpu[i])); ++ for (j = 1; j < n; j++) ++ pr_info("%s: 0x%lx: 0x%08x [ arg #%d ]\n", ++ btv->c.v4l2_dev.name, ++ (unsigned long)(risc->dma + ((i+j)<<2)), ++ risc->cpu[i+j], j); ++ if (0 == risc->cpu[i]) ++ break; ++ } ++} ++ ++static void bttv_print_riscaddr(struct bttv *btv) ++{ ++ pr_info(" main: %08llx\n", (unsigned long long)btv->main.dma); ++ pr_info(" vbi : o=%08llx e=%08llx\n", ++ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, ++ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0); ++ pr_info(" cap : o=%08llx e=%08llx\n", ++ btv->curr.top ++ ? (unsigned long long)btv->curr.top->top.dma : 0, ++ btv->curr.bottom ++ ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); ++ pr_info(" scr : o=%08llx e=%08llx\n", ++ btv->screen ? (unsigned long long)btv->screen->top.dma : 0, ++ btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); ++ bttv_risc_disasm(btv, &btv->main); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* irq handler */ ++ ++static char *irq_name[] = { ++ "FMTCHG", // format change detected (525 vs. 625) ++ "VSYNC", // vertical sync (new field) ++ "HSYNC", // horizontal sync ++ "OFLOW", // chroma/luma AGC overflow ++ "HLOCK", // horizontal lock changed ++ "VPRES", // video presence changed ++ "6", "7", ++ "I2CDONE", // hw irc operation finished ++ "GPINT", // gpio port triggered irq ++ "10", ++ "RISCI", // risc instruction triggered irq ++ "FBUS", // pixel data fifo dropped data (high pci bus latencies) ++ "FTRGT", // pixel data fifo overrun ++ "FDSR", // fifo data stream resyncronisation ++ "PPERR", // parity error (data transfer) ++ "RIPERR", // parity error (read risc instructions) ++ "PABORT", // pci abort ++ "OCERR", // risc instruction error ++ "SCERR", // syncronisation error ++}; ++ ++static void bttv_print_irqbits(u32 print, u32 mark) ++{ ++ unsigned int i; ++ ++ pr_cont("bits:"); ++ for (i = 0; i < ARRAY_SIZE(irq_name); i++) { ++ if (print & (1 << i)) ++ pr_cont(" %s", irq_name[i]); ++ if (mark & (1 << i)) ++ pr_cont("*"); ++ } ++} ++ ++static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) ++{ ++ pr_warn("%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n", ++ btv->c.nr, ++ (unsigned long)btv->main.dma, ++ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_VBI+1]), ++ (unsigned long)le32_to_cpu(btv->main.cpu[RISC_SLOT_O_FIELD+1]), ++ (unsigned long)rc); ++ ++ if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) { ++ pr_notice("%d: Oh, there (temporarily?) is no input signal. " ++ "Ok, then this is harmless, don't worry ;)\n", ++ btv->c.nr); ++ return; ++ } ++ pr_notice("%d: Uhm. Looks like we have unusual high IRQ latencies\n", ++ btv->c.nr); ++ pr_notice("%d: Lets try to catch the culpit red-handed ...\n", ++ btv->c.nr); ++ dump_stack(); ++} ++ ++static int ++bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) ++{ ++ struct bttv_buffer *item; ++ ++ memset(set,0,sizeof(*set)); ++ ++ /* capture request ? */ ++ if (!list_empty(&btv->capture)) { ++ set->frame_irq = 1; ++ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); ++ if (V4L2_FIELD_HAS_TOP(item->vb.field)) ++ set->top = item; ++ if (V4L2_FIELD_HAS_BOTTOM(item->vb.field)) ++ set->bottom = item; ++ ++ /* capture request for other field ? */ ++ if (!V4L2_FIELD_HAS_BOTH(item->vb.field) && ++ (item->vb.queue.next != &btv->capture)) { ++ item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue); ++ /* Mike Isely - Only check ++ * and set up the bottom field in the logic ++ * below. Don't ever do the top field. This ++ * of course means that if we set up the ++ * bottom field in the above code that we'll ++ * actually skip a field. But that's OK. ++ * Having processed only a single buffer this ++ * time, then the next time around the first ++ * available buffer should be for a top field. ++ * That will then cause us here to set up a ++ * top then a bottom field in the normal way. ++ * The alternative to this understanding is ++ * that we set up the second available buffer ++ * as a top field, but that's out of order ++ * since this driver always processes the top ++ * field first - the effect will be the two ++ * buffers being returned in the wrong order, ++ * with the second buffer also being delayed ++ * by one field time (owing to the fifo nature ++ * of videobuf). Worse still, we'll be stuck ++ * doing fields out of order now every time ++ * until something else causes a field to be ++ * dropped. By effectively forcing a field to ++ * drop this way then we always get back into ++ * sync within a single frame time. (Out of ++ * order fields can screw up deinterlacing ++ * algorithms.) */ ++ if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) { ++ if (NULL == set->bottom && ++ V4L2_FIELD_BOTTOM == item->vb.field) { ++ set->bottom = item; ++ } ++ if (NULL != set->top && NULL != set->bottom) ++ set->top_irq = 2; ++ } ++ } ++ } ++ ++ /* screen overlay ? */ ++ if (NULL != btv->screen) { ++ if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) { ++ if (NULL == set->top && NULL == set->bottom) { ++ set->top = btv->screen; ++ set->bottom = btv->screen; ++ } ++ } else { ++ if (V4L2_FIELD_TOP == btv->screen->vb.field && ++ NULL == set->top) { ++ set->top = btv->screen; ++ } ++ if (V4L2_FIELD_BOTTOM == btv->screen->vb.field && ++ NULL == set->bottom) { ++ set->bottom = btv->screen; ++ } ++ } ++ } ++ ++ dprintk("%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n", ++ btv->c.nr, set->top, set->bottom, ++ btv->screen, set->frame_irq, set->top_irq); ++ return 0; ++} ++ ++static void ++bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, ++ struct bttv_buffer_set *curr, unsigned int state) ++{ ++ struct timeval ts; ++ ++ v4l2_get_timestamp(&ts); ++ ++ if (wakeup->top == wakeup->bottom) { ++ if (NULL != wakeup->top && curr->top != wakeup->top) { ++ if (irq_debug > 1) ++ pr_debug("%d: wakeup: both=%p\n", ++ btv->c.nr, wakeup->top); ++ wakeup->top->vb.ts = ts; ++ wakeup->top->vb.field_count = btv->field_count; ++ wakeup->top->vb.state = state; ++ wake_up(&wakeup->top->vb.done); ++ } ++ } else { ++ if (NULL != wakeup->top && curr->top != wakeup->top) { ++ if (irq_debug > 1) ++ pr_debug("%d: wakeup: top=%p\n", ++ btv->c.nr, wakeup->top); ++ wakeup->top->vb.ts = ts; ++ wakeup->top->vb.field_count = btv->field_count; ++ wakeup->top->vb.state = state; ++ wake_up(&wakeup->top->vb.done); ++ } ++ if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) { ++ if (irq_debug > 1) ++ pr_debug("%d: wakeup: bottom=%p\n", ++ btv->c.nr, wakeup->bottom); ++ wakeup->bottom->vb.ts = ts; ++ wakeup->bottom->vb.field_count = btv->field_count; ++ wakeup->bottom->vb.state = state; ++ wake_up(&wakeup->bottom->vb.done); ++ } ++ } ++} ++ ++static void ++bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, ++ unsigned int state) ++{ ++ struct timeval ts; ++ ++ if (NULL == wakeup) ++ return; ++ ++ v4l2_get_timestamp(&ts); ++ wakeup->vb.ts = ts; ++ wakeup->vb.field_count = btv->field_count; ++ wakeup->vb.state = state; ++ wake_up(&wakeup->vb.done); ++} ++ ++static void bttv_irq_timeout(unsigned long data) ++{ ++ struct bttv *btv = (struct bttv *)data; ++ struct bttv_buffer_set old,new; ++ struct bttv_buffer *ovbi; ++ struct bttv_buffer *item; ++ unsigned long flags; ++ ++ if (bttv_verbose) { ++ pr_info("%d: timeout: drop=%d irq=%d/%d, risc=%08x, ", ++ btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total, ++ btread(BT848_RISC_COUNT)); ++ bttv_print_irqbits(btread(BT848_INT_STAT),0); ++ pr_cont("\n"); ++ } ++ ++ spin_lock_irqsave(&btv->s_lock,flags); ++ ++ /* deactivate stuff */ ++ memset(&new,0,sizeof(new)); ++ old = btv->curr; ++ ovbi = btv->cvbi; ++ btv->curr = new; ++ btv->cvbi = NULL; ++ btv->loop_irq = 0; ++ bttv_buffer_activate_video(btv, &new); ++ bttv_buffer_activate_vbi(btv, NULL); ++ bttv_set_dma(btv, 0); ++ ++ /* wake up */ ++ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_ERROR); ++ bttv_irq_wakeup_vbi(btv, ovbi, VIDEOBUF_ERROR); ++ ++ /* cancel all outstanding capture / vbi requests */ ++ while (!list_empty(&btv->capture)) { ++ item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); ++ list_del(&item->vb.queue); ++ item->vb.state = VIDEOBUF_ERROR; ++ wake_up(&item->vb.done); ++ } ++ while (!list_empty(&btv->vcapture)) { ++ item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); ++ list_del(&item->vb.queue); ++ item->vb.state = VIDEOBUF_ERROR; ++ wake_up(&item->vb.done); ++ } ++ ++ btv->errors++; ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++} ++ ++static void ++bttv_irq_wakeup_top(struct bttv *btv) ++{ ++ struct bttv_buffer *wakeup = btv->curr.top; ++ ++ if (NULL == wakeup) ++ return; ++ ++ spin_lock(&btv->s_lock); ++ btv->curr.top_irq = 0; ++ btv->curr.top = NULL; ++ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); ++ ++ v4l2_get_timestamp(&wakeup->vb.ts); ++ wakeup->vb.field_count = btv->field_count; ++ wakeup->vb.state = VIDEOBUF_DONE; ++ wake_up(&wakeup->vb.done); ++ spin_unlock(&btv->s_lock); ++} ++ ++static inline int is_active(struct btcx_riscmem *risc, u32 rc) ++{ ++ if (rc < risc->dma) ++ return 0; ++ if (rc > risc->dma + risc->size) ++ return 0; ++ return 1; ++} ++ ++static void ++bttv_irq_switch_video(struct bttv *btv) ++{ ++ struct bttv_buffer_set new; ++ struct bttv_buffer_set old; ++ dma_addr_t rc; ++ ++ spin_lock(&btv->s_lock); ++ ++ /* new buffer set */ ++ bttv_irq_next_video(btv, &new); ++ rc = btread(BT848_RISC_COUNT); ++ if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) || ++ (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) { ++ btv->framedrop++; ++ if (debug_latency) ++ bttv_irq_debug_low_latency(btv, rc); ++ spin_unlock(&btv->s_lock); ++ return; ++ } ++ ++ /* switch over */ ++ old = btv->curr; ++ btv->curr = new; ++ btv->loop_irq &= ~1; ++ bttv_buffer_activate_video(btv, &new); ++ bttv_set_dma(btv, 0); ++ ++ /* switch input */ ++ if (UNSET != btv->new_input) { ++ video_mux(btv,btv->new_input); ++ btv->new_input = UNSET; ++ } ++ ++ /* wake up finished buffers */ ++ bttv_irq_wakeup_video(btv, &old, &new, VIDEOBUF_DONE); ++ spin_unlock(&btv->s_lock); ++} ++ ++static void ++bttv_irq_switch_vbi(struct bttv *btv) ++{ ++ struct bttv_buffer *new = NULL; ++ struct bttv_buffer *old; ++ u32 rc; ++ ++ spin_lock(&btv->s_lock); ++ ++ if (!list_empty(&btv->vcapture)) ++ new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); ++ old = btv->cvbi; ++ ++ rc = btread(BT848_RISC_COUNT); ++ if (NULL != old && (is_active(&old->top, rc) || ++ is_active(&old->bottom, rc))) { ++ btv->framedrop++; ++ if (debug_latency) ++ bttv_irq_debug_low_latency(btv, rc); ++ spin_unlock(&btv->s_lock); ++ return; ++ } ++ ++ /* switch */ ++ btv->cvbi = new; ++ btv->loop_irq &= ~4; ++ bttv_buffer_activate_vbi(btv, new); ++ bttv_set_dma(btv, 0); ++ ++ bttv_irq_wakeup_vbi(btv, old, VIDEOBUF_DONE); ++ spin_unlock(&btv->s_lock); ++} ++ ++static irqreturn_t bttv_irq(int irq, void *dev_id) ++{ ++ u32 stat,astat; ++ u32 dstat; ++ int count; ++ struct bttv *btv; ++ int handled = 0; ++ ++ btv=(struct bttv *)dev_id; ++ ++ count=0; ++ while (1) { ++ /* get/clear interrupt status bits */ ++ stat=btread(BT848_INT_STAT); ++ astat=stat&btread(BT848_INT_MASK); ++ if (!astat) ++ break; ++ handled = 1; ++ btwrite(stat,BT848_INT_STAT); ++ ++ /* get device status bits */ ++ dstat=btread(BT848_DSTATUS); ++ ++ if (irq_debug) { ++ pr_debug("%d: irq loop=%d fc=%d riscs=%x, riscc=%08x, ", ++ btv->c.nr, count, btv->field_count, ++ stat>>28, btread(BT848_RISC_COUNT)); ++ bttv_print_irqbits(stat,astat); ++ if (stat & BT848_INT_HLOCK) ++ pr_cont(" HLOC => %s", ++ dstat & BT848_DSTATUS_HLOC ++ ? "yes" : "no"); ++ if (stat & BT848_INT_VPRES) ++ pr_cont(" PRES => %s", ++ dstat & BT848_DSTATUS_PRES ++ ? "yes" : "no"); ++ if (stat & BT848_INT_FMTCHG) ++ pr_cont(" NUML => %s", ++ dstat & BT848_DSTATUS_NUML ++ ? "625" : "525"); ++ pr_cont("\n"); ++ } ++ ++ if (astat&BT848_INT_VSYNC) ++ btv->field_count++; ++ ++ if ((astat & BT848_INT_GPINT) && btv->remote) { ++ bttv_input_irq(btv); ++ } ++ ++ if (astat & BT848_INT_I2CDONE) { ++ btv->i2c_done = stat; ++ wake_up(&btv->i2c_queue); ++ } ++ ++ if ((astat & BT848_INT_RISCI) && (stat & (4<<28))) ++ bttv_irq_switch_vbi(btv); ++ ++ if ((astat & BT848_INT_RISCI) && (stat & (2<<28))) ++ bttv_irq_wakeup_top(btv); ++ ++ if ((astat & BT848_INT_RISCI) && (stat & (1<<28))) ++ bttv_irq_switch_video(btv); ++ ++ if ((astat & BT848_INT_HLOCK) && btv->opt_automute) ++ audio_mute(btv, btv->mute); /* trigger automute */ ++ ++ if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { ++ pr_info("%d: %s%s @ %08x,", ++ btv->c.nr, ++ (astat & BT848_INT_SCERR) ? "SCERR" : "", ++ (astat & BT848_INT_OCERR) ? "OCERR" : "", ++ btread(BT848_RISC_COUNT)); ++ bttv_print_irqbits(stat,astat); ++ pr_cont("\n"); ++ if (bttv_debug) ++ bttv_print_riscaddr(btv); ++ } ++ if (fdsr && astat & BT848_INT_FDSR) { ++ pr_info("%d: FDSR @ %08x\n", ++ btv->c.nr, btread(BT848_RISC_COUNT)); ++ if (bttv_debug) ++ bttv_print_riscaddr(btv); ++ } ++ ++ count++; ++ if (count > 4) { ++ ++ if (count > 8 || !(astat & BT848_INT_GPINT)) { ++ btwrite(0, BT848_INT_MASK); ++ ++ pr_err("%d: IRQ lockup, cleared int mask [", ++ btv->c.nr); ++ } else { ++ pr_err("%d: IRQ lockup, clearing GPINT from int mask [", ++ btv->c.nr); ++ ++ btwrite(btread(BT848_INT_MASK) & (-1 ^ BT848_INT_GPINT), ++ BT848_INT_MASK); ++ }; ++ ++ bttv_print_irqbits(stat,astat); ++ ++ pr_cont("]\n"); ++ } ++ } ++ btv->irq_total++; ++ if (handled) ++ btv->irq_me++; ++ return IRQ_RETVAL(handled); ++} ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* initialitation */ ++ ++static struct video_device *vdev_init(struct bttv *btv, ++ const struct video_device *template, ++ const char *type_name) ++{ ++ struct video_device *vfd; ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template; ++ vfd->v4l2_dev = &btv->c.v4l2_dev; ++ vfd->release = video_device_release; ++ vfd->debug = bttv_debug; ++ video_set_drvdata(vfd, btv); ++ snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", ++ btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", ++ type_name, bttv_tvcards[btv->c.type].name); ++ return vfd; ++} ++ ++static void bttv_unregister_video(struct bttv *btv) ++{ ++ if (btv->video_dev) { ++ if (video_is_registered(btv->video_dev)) ++ video_unregister_device(btv->video_dev); ++ else ++ video_device_release(btv->video_dev); ++ btv->video_dev = NULL; ++ } ++ if (btv->vbi_dev) { ++ if (video_is_registered(btv->vbi_dev)) ++ video_unregister_device(btv->vbi_dev); ++ else ++ video_device_release(btv->vbi_dev); ++ btv->vbi_dev = NULL; ++ } ++ if (btv->radio_dev) { ++ if (video_is_registered(btv->radio_dev)) ++ video_unregister_device(btv->radio_dev); ++ else ++ video_device_release(btv->radio_dev); ++ btv->radio_dev = NULL; ++ } ++} ++ ++/* register video4linux devices */ ++static int __devinit bttv_register_video(struct bttv *btv) ++{ ++ if (no_overlay > 0) ++ pr_notice("Overlay support disabled\n"); ++ ++ /* video */ ++ btv->video_dev = vdev_init(btv, &bttv_video_template, "video"); ++ ++ if (NULL == btv->video_dev) ++ goto err; ++ if (video_register_device(btv->video_dev, VFL_TYPE_GRABBER, ++ video_nr[btv->c.nr]) < 0) ++ goto err; ++ pr_info("%d: registered device %s\n", ++ btv->c.nr, video_device_node_name(btv->video_dev)); ++ if (device_create_file(&btv->video_dev->dev, ++ &dev_attr_card)<0) { ++ pr_err("%d: device_create_file 'card' failed\n", btv->c.nr); ++ goto err; ++ } ++ ++ /* vbi */ ++ btv->vbi_dev = vdev_init(btv, &bttv_video_template, "vbi"); ++ ++ if (NULL == btv->vbi_dev) ++ goto err; ++ if (video_register_device(btv->vbi_dev, VFL_TYPE_VBI, ++ vbi_nr[btv->c.nr]) < 0) ++ goto err; ++ pr_info("%d: registered device %s\n", ++ btv->c.nr, video_device_node_name(btv->vbi_dev)); ++ ++ if (!btv->has_radio) ++ return 0; ++ /* radio */ ++ btv->radio_dev = vdev_init(btv, &radio_template, "radio"); ++ if (NULL == btv->radio_dev) ++ goto err; ++ if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO, ++ radio_nr[btv->c.nr]) < 0) ++ goto err; ++ pr_info("%d: registered device %s\n", ++ btv->c.nr, video_device_node_name(btv->radio_dev)); ++ ++ /* all done */ ++ return 0; ++ ++ err: ++ bttv_unregister_video(btv); ++ return -1; ++} ++ ++ ++/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ ++/* response on cards with no firmware is not enabled by OF */ ++static void pci_set_command(struct pci_dev *dev) ++{ ++#if defined(__powerpc__) ++ unsigned int cmd; ++ ++ pci_read_config_dword(dev, PCI_COMMAND, &cmd); ++ cmd = (cmd | PCI_COMMAND_MEMORY ); ++ pci_write_config_dword(dev, PCI_COMMAND, cmd); ++#endif ++} ++ ++static int __devinit bttv_probe(struct pci_dev *dev, ++ const struct pci_device_id *pci_id) ++{ ++ int result; ++ unsigned char lat; ++ struct bttv *btv; ++ ++ if (bttv_num == BTTV_MAX) ++ return -ENOMEM; ++ pr_info("Bt8xx card found (%d)\n", bttv_num); ++ bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL); ++ if (btv == NULL) { ++ pr_err("out of memory\n"); ++ return -ENOMEM; ++ } ++ btv->c.nr = bttv_num; ++ snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name), ++ "bttv%d", btv->c.nr); ++ ++ /* initialize structs / fill in defaults */ ++ mutex_init(&btv->lock); ++ spin_lock_init(&btv->s_lock); ++ spin_lock_init(&btv->gpio_lock); ++ init_waitqueue_head(&btv->i2c_queue); ++ INIT_LIST_HEAD(&btv->c.subs); ++ INIT_LIST_HEAD(&btv->capture); ++ INIT_LIST_HEAD(&btv->vcapture); ++ v4l2_prio_init(&btv->prio); ++ ++ init_timer(&btv->timeout); ++ btv->timeout.function = bttv_irq_timeout; ++ btv->timeout.data = (unsigned long)btv; ++ ++ btv->i2c_rc = -1; ++ btv->tuner_type = UNSET; ++ btv->new_input = UNSET; ++ btv->has_radio=radio[btv->c.nr]; ++ ++ /* pci stuff (init, get irq/mmio, ... */ ++ btv->c.pci = dev; ++ btv->id = dev->device; ++ if (pci_enable_device(dev)) { ++ pr_warn("%d: Can't enable device\n", btv->c.nr); ++ return -EIO; ++ } ++ if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { ++ pr_warn("%d: No suitable DMA available\n", btv->c.nr); ++ return -EIO; ++ } ++ if (!request_mem_region(pci_resource_start(dev,0), ++ pci_resource_len(dev,0), ++ btv->c.v4l2_dev.name)) { ++ pr_warn("%d: can't request iomem (0x%llx)\n", ++ btv->c.nr, ++ (unsigned long long)pci_resource_start(dev, 0)); ++ return -EBUSY; ++ } ++ pci_set_master(dev); ++ pci_set_command(dev); ++ ++ result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev); ++ if (result < 0) { ++ pr_warn("%d: v4l2_device_register() failed\n", btv->c.nr); ++ goto fail0; ++ } ++ ++ btv->revision = dev->revision; ++ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); ++ pr_info("%d: Bt%d (rev %d) at %s, irq: %d, latency: %d, mmio: 0x%llx\n", ++ bttv_num, btv->id, btv->revision, pci_name(dev), ++ btv->c.pci->irq, lat, ++ (unsigned long long)pci_resource_start(dev, 0)); ++ schedule(); ++ ++ btv->bt848_mmio = ioremap(pci_resource_start(dev, 0), 0x1000); ++ if (NULL == btv->bt848_mmio) { ++ pr_err("%d: ioremap() failed\n", btv->c.nr); ++ result = -EIO; ++ goto fail1; ++ } ++ ++ /* identify card */ ++ bttv_idcard(btv); ++ ++ /* disable irqs, register irq handler */ ++ btwrite(0, BT848_INT_MASK); ++ result = request_irq(btv->c.pci->irq, bttv_irq, ++ IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv); ++ if (result < 0) { ++ pr_err("%d: can't get IRQ %d\n", ++ bttv_num, btv->c.pci->irq); ++ goto fail1; ++ } ++ ++ if (0 != bttv_handle_chipset(btv)) { ++ result = -EIO; ++ goto fail2; ++ } ++ ++ /* init options from insmod args */ ++ btv->opt_combfilter = combfilter; ++ btv->opt_lumafilter = lumafilter; ++ btv->opt_automute = automute; ++ btv->opt_chroma_agc = chroma_agc; ++ btv->opt_adc_crush = adc_crush; ++ btv->opt_vcr_hack = vcr_hack; ++ btv->opt_whitecrush_upper = whitecrush_upper; ++ btv->opt_whitecrush_lower = whitecrush_lower; ++ btv->opt_uv_ratio = uv_ratio; ++ btv->opt_full_luma_range = full_luma_range; ++ btv->opt_coring = coring; ++ ++ /* fill struct bttv with some useful defaults */ ++ btv->init.btv = btv; ++ btv->init.ov.w.width = 320; ++ btv->init.ov.w.height = 240; ++ btv->init.fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); ++ btv->init.width = 320; ++ btv->init.height = 240; ++ btv->input = 0; ++ ++ /* initialize hardware */ ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"pre-init"); ++ ++ bttv_risc_init_main(btv); ++ init_bt848(btv); ++ ++ /* gpio */ ++ btwrite(0x00, BT848_GPIO_REG_INP); ++ btwrite(0x00, BT848_GPIO_OUT_EN); ++ if (bttv_verbose) ++ bttv_gpio_tracking(btv,"init"); ++ ++ /* needs to be done before i2c is registered */ ++ bttv_init_card1(btv); ++ ++ /* register i2c + gpio */ ++ init_bttv_i2c(btv); ++ ++ /* some card-specific stuff (needs working i2c) */ ++ bttv_init_card2(btv); ++ bttv_init_tuner(btv); ++ init_irqreg(btv); ++ ++ /* register video4linux + input */ ++ if (!bttv_tvcards[btv->c.type].no_video) { ++ bttv_register_video(btv); ++ bt848_bright(btv,32768); ++ bt848_contrast(btv, 27648); ++ bt848_hue(btv,32768); ++ bt848_sat(btv,32768); ++ audio_mute(btv, 1); ++ set_input(btv, 0, btv->tvnorm); ++ bttv_crop_reset(&btv->crop[0], btv->tvnorm); ++ btv->crop[1] = btv->crop[0]; /* current = default */ ++ disclaim_vbi_lines(btv); ++ disclaim_video_lines(btv); ++ } ++ ++ /* add subdevices and autoload dvb-bt8xx if needed */ ++ if (bttv_tvcards[btv->c.type].has_dvb) { ++ bttv_sub_add_device(&btv->c, "dvb"); ++ request_modules(btv); ++ } ++ ++ if (!disable_ir) { ++ init_bttv_i2c_ir(btv); ++ bttv_input_init(btv); ++ } ++ ++ /* everything is fine */ ++ bttv_num++; ++ return 0; ++ ++fail2: ++ free_irq(btv->c.pci->irq,btv); ++ ++fail1: ++ v4l2_device_unregister(&btv->c.v4l2_dev); ++ ++fail0: ++ if (btv->bt848_mmio) ++ iounmap(btv->bt848_mmio); ++ release_mem_region(pci_resource_start(btv->c.pci,0), ++ pci_resource_len(btv->c.pci,0)); ++ return result; ++} ++ ++static void __devexit bttv_remove(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct bttv *btv = to_bttv(v4l2_dev); ++ ++ if (bttv_verbose) ++ pr_info("%d: unloading\n", btv->c.nr); ++ ++ if (bttv_tvcards[btv->c.type].has_dvb) ++ flush_request_modules(btv); ++ ++ /* shutdown everything (DMA+IRQs) */ ++ btand(~15, BT848_GPIO_DMA_CTL); ++ btwrite(0, BT848_INT_MASK); ++ btwrite(~0x0, BT848_INT_STAT); ++ btwrite(0x0, BT848_GPIO_OUT_EN); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"cleanup"); ++ ++ /* tell gpio modules we are leaving ... */ ++ btv->shutdown=1; ++ bttv_input_fini(btv); ++ bttv_sub_del_devices(&btv->c); ++ ++ /* unregister i2c_bus + input */ ++ fini_bttv_i2c(btv); ++ ++ /* unregister video4linux */ ++ bttv_unregister_video(btv); ++ ++ /* free allocated memory */ ++ btcx_riscmem_free(btv->c.pci,&btv->main); ++ ++ /* free ressources */ ++ free_irq(btv->c.pci->irq,btv); ++ iounmap(btv->bt848_mmio); ++ release_mem_region(pci_resource_start(btv->c.pci,0), ++ pci_resource_len(btv->c.pci,0)); ++ ++ v4l2_device_unregister(&btv->c.v4l2_dev); ++ bttvs[btv->c.nr] = NULL; ++ kfree(btv); ++ ++ return; ++} ++ ++#ifdef CONFIG_PM ++static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct bttv *btv = to_bttv(v4l2_dev); ++ struct bttv_buffer_set idle; ++ unsigned long flags; ++ ++ dprintk("%d: suspend %d\n", btv->c.nr, state.event); ++ ++ /* stop dma + irqs */ ++ spin_lock_irqsave(&btv->s_lock,flags); ++ memset(&idle, 0, sizeof(idle)); ++ btv->state.video = btv->curr; ++ btv->state.vbi = btv->cvbi; ++ btv->state.loop_irq = btv->loop_irq; ++ btv->curr = idle; ++ btv->loop_irq = 0; ++ bttv_buffer_activate_video(btv, &idle); ++ bttv_buffer_activate_vbi(btv, NULL); ++ bttv_set_dma(btv, 0); ++ btwrite(0, BT848_INT_MASK); ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++ ++ /* save bt878 state */ ++ btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN); ++ btv->state.gpio_data = gpio_read(); ++ ++ /* save pci state */ ++ pci_save_state(pci_dev); ++ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { ++ pci_disable_device(pci_dev); ++ btv->state.disabled = 1; ++ } ++ return 0; ++} ++ ++static int bttv_resume(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct bttv *btv = to_bttv(v4l2_dev); ++ unsigned long flags; ++ int err; ++ ++ dprintk("%d: resume\n", btv->c.nr); ++ ++ /* restore pci state */ ++ if (btv->state.disabled) { ++ err=pci_enable_device(pci_dev); ++ if (err) { ++ pr_warn("%d: Can't enable device\n", btv->c.nr); ++ return err; ++ } ++ btv->state.disabled = 0; ++ } ++ err=pci_set_power_state(pci_dev, PCI_D0); ++ if (err) { ++ pci_disable_device(pci_dev); ++ pr_warn("%d: Can't enable device\n", btv->c.nr); ++ btv->state.disabled = 1; ++ return err; ++ } ++ ++ pci_restore_state(pci_dev); ++ ++ /* restore bt878 state */ ++ bttv_reinit_bt848(btv); ++ gpio_inout(0xffffff, btv->state.gpio_enable); ++ gpio_write(btv->state.gpio_data); ++ ++ /* restart dma */ ++ spin_lock_irqsave(&btv->s_lock,flags); ++ btv->curr = btv->state.video; ++ btv->cvbi = btv->state.vbi; ++ btv->loop_irq = btv->state.loop_irq; ++ bttv_buffer_activate_video(btv, &btv->curr); ++ bttv_buffer_activate_vbi(btv, btv->cvbi); ++ bttv_set_dma(btv, 0); ++ spin_unlock_irqrestore(&btv->s_lock,flags); ++ return 0; ++} ++#endif ++ ++static struct pci_device_id bttv_pci_tbl[] = { ++ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0}, ++ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0}, ++ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0}, ++ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0}, ++ {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0}, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); ++ ++static struct pci_driver bttv_pci_driver = { ++ .name = "bttv", ++ .id_table = bttv_pci_tbl, ++ .probe = bttv_probe, ++ .remove = __devexit_p(bttv_remove), ++#ifdef CONFIG_PM ++ .suspend = bttv_suspend, ++ .resume = bttv_resume, ++#endif ++}; ++ ++static int __init bttv_init_module(void) ++{ ++ int ret; ++ ++ bttv_num = 0; ++ ++ pr_info("driver version %s loaded\n", BTTV_VERSION); ++ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) ++ gbuffers = 2; ++ if (gbufsize > BTTV_MAX_FBUF) ++ gbufsize = BTTV_MAX_FBUF; ++ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; ++ if (bttv_verbose) ++ pr_info("using %d buffers with %dk (%d pages) each for capture\n", ++ gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT); ++ ++ bttv_check_chipset(); ++ ++ ret = bus_register(&bttv_sub_bus_type); ++ if (ret < 0) { ++ pr_warn("bus_register error: %d\n", ret); ++ return ret; ++ } ++ ret = pci_register_driver(&bttv_pci_driver); ++ if (ret < 0) ++ bus_unregister(&bttv_sub_bus_type); ++ ++ return ret; ++} ++ ++static void __exit bttv_cleanup_module(void) ++{ ++ pci_unregister_driver(&bttv_pci_driver); ++ bus_unregister(&bttv_sub_bus_type); ++} ++ ++module_init(bttv_init_module); ++module_exit(bttv_cleanup_module); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c +new file mode 100644 +index 0000000..c4dd6ed +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-gpio.c +@@ -0,0 +1,190 @@ ++/* ++ ++ bttv-gpio.c -- gpio sub drivers ++ ++ sysfs-based sub driver interface for bttv ++ mainly intended for gpio access ++ ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ & Marcus Metzler (mocm@thp.uni-koeln.de) ++ (c) 1999-2003 Gerd Knorr ++ ++ 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. ++ ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bttvp.h" ++ ++/* ----------------------------------------------------------------------- */ ++/* internal: the bttv "bus" */ ++ ++static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ struct bttv_sub_driver *sub = to_bttv_sub_drv(drv); ++ int len = strlen(sub->wanted); ++ ++ if (0 == strncmp(dev_name(dev), sub->wanted, len)) ++ return 1; ++ return 0; ++} ++ ++static int bttv_sub_probe(struct device *dev) ++{ ++ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev); ++ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver); ++ ++ return sub->probe ? sub->probe(sdev) : -ENODEV; ++} ++ ++static int bttv_sub_remove(struct device *dev) ++{ ++ struct bttv_sub_device *sdev = to_bttv_sub_dev(dev); ++ struct bttv_sub_driver *sub = to_bttv_sub_drv(dev->driver); ++ ++ if (sub->remove) ++ sub->remove(sdev); ++ return 0; ++} ++ ++struct bus_type bttv_sub_bus_type = { ++ .name = "bttv-sub", ++ .match = &bttv_sub_bus_match, ++ .probe = bttv_sub_probe, ++ .remove = bttv_sub_remove, ++}; ++ ++static void release_sub_device(struct device *dev) ++{ ++ struct bttv_sub_device *sub = to_bttv_sub_dev(dev); ++ kfree(sub); ++} ++ ++int bttv_sub_add_device(struct bttv_core *core, char *name) ++{ ++ struct bttv_sub_device *sub; ++ int err; ++ ++ sub = kzalloc(sizeof(*sub),GFP_KERNEL); ++ if (NULL == sub) ++ return -ENOMEM; ++ ++ sub->core = core; ++ sub->dev.parent = &core->pci->dev; ++ sub->dev.bus = &bttv_sub_bus_type; ++ sub->dev.release = release_sub_device; ++ dev_set_name(&sub->dev, "%s%d", name, core->nr); ++ ++ err = device_register(&sub->dev); ++ if (0 != err) { ++ kfree(sub); ++ return err; ++ } ++ pr_info("%d: add subdevice \"%s\"\n", core->nr, dev_name(&sub->dev)); ++ list_add_tail(&sub->list,&core->subs); ++ return 0; ++} ++ ++int bttv_sub_del_devices(struct bttv_core *core) ++{ ++ struct bttv_sub_device *sub, *save; ++ ++ list_for_each_entry_safe(sub, save, &core->subs, list) { ++ list_del(&sub->list); ++ device_unregister(&sub->dev); ++ } ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* external: sub-driver register/unregister */ ++ ++int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted) ++{ ++ sub->drv.bus = &bttv_sub_bus_type; ++ snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted); ++ return driver_register(&sub->drv); ++} ++EXPORT_SYMBOL(bttv_sub_register); ++ ++int bttv_sub_unregister(struct bttv_sub_driver *sub) ++{ ++ driver_unregister(&sub->drv); ++ return 0; ++} ++EXPORT_SYMBOL(bttv_sub_unregister); ++ ++/* ----------------------------------------------------------------------- */ ++/* external: gpio access functions */ ++ ++void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits) ++{ ++ struct bttv *btv = container_of(core, struct bttv, c); ++ unsigned long flags; ++ u32 data; ++ ++ spin_lock_irqsave(&btv->gpio_lock,flags); ++ data = btread(BT848_GPIO_OUT_EN); ++ data = data & ~mask; ++ data = data | (mask & outbits); ++ btwrite(data,BT848_GPIO_OUT_EN); ++ spin_unlock_irqrestore(&btv->gpio_lock,flags); ++} ++ ++u32 bttv_gpio_read(struct bttv_core *core) ++{ ++ struct bttv *btv = container_of(core, struct bttv, c); ++ u32 value; ++ ++ value = btread(BT848_GPIO_DATA); ++ return value; ++} ++ ++void bttv_gpio_write(struct bttv_core *core, u32 value) ++{ ++ struct bttv *btv = container_of(core, struct bttv, c); ++ ++ btwrite(value,BT848_GPIO_DATA); ++} ++ ++void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) ++{ ++ struct bttv *btv = container_of(core, struct bttv, c); ++ unsigned long flags; ++ u32 data; ++ ++ spin_lock_irqsave(&btv->gpio_lock,flags); ++ data = btread(BT848_GPIO_DATA); ++ data = data & ~mask; ++ data = data | (mask & bits); ++ btwrite(data,BT848_GPIO_DATA); ++ spin_unlock_irqrestore(&btv->gpio_lock,flags); ++} ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv-i2c.c b/drivers/media/pci/bt8xx/bttv-i2c.c +new file mode 100644 +index 0000000..be820f2 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-i2c.c +@@ -0,0 +1,397 @@ ++/* ++ ++ bttv-i2c.c -- all the i2c code is here ++ ++ bttv - Bt848 frame grabber driver ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ & Marcus Metzler (mocm@thp.uni-koeln.de) ++ (c) 1999-2003 Gerd Knorr ++ ++ (c) 2005 Mauro Carvalho Chehab ++ - Multituner support and i2c address binding ++ ++ 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. ++ ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++ ++#include "bttvp.h" ++#include ++#include ++#include ++ ++static int i2c_debug; ++static int i2c_hw; ++static int i2c_scan; ++module_param(i2c_debug, int, 0644); ++MODULE_PARM_DESC(i2c_debug, "configure i2c debug level"); ++module_param(i2c_hw, int, 0444); ++MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, " ++ "instead of software bitbang"); ++module_param(i2c_scan, int, 0444); ++MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); ++ ++static unsigned int i2c_udelay = 5; ++module_param(i2c_udelay, int, 0444); ++MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs " ++ "(should be 5 or higher). Lower value means higher bus speed."); ++ ++/* ----------------------------------------------------------------------- */ ++/* I2C functions - bitbanging adapter (software i2c) */ ++ ++static void bttv_bit_setscl(void *data, int state) ++{ ++ struct bttv *btv = (struct bttv*)data; ++ ++ if (state) ++ btv->i2c_state |= 0x02; ++ else ++ btv->i2c_state &= ~0x02; ++ btwrite(btv->i2c_state, BT848_I2C); ++ btread(BT848_I2C); ++} ++ ++static void bttv_bit_setsda(void *data, int state) ++{ ++ struct bttv *btv = (struct bttv*)data; ++ ++ if (state) ++ btv->i2c_state |= 0x01; ++ else ++ btv->i2c_state &= ~0x01; ++ btwrite(btv->i2c_state, BT848_I2C); ++ btread(BT848_I2C); ++} ++ ++static int bttv_bit_getscl(void *data) ++{ ++ struct bttv *btv = (struct bttv*)data; ++ int state; ++ ++ state = btread(BT848_I2C) & 0x02 ? 1 : 0; ++ return state; ++} ++ ++static int bttv_bit_getsda(void *data) ++{ ++ struct bttv *btv = (struct bttv*)data; ++ int state; ++ ++ state = btread(BT848_I2C) & 0x01; ++ return state; ++} ++ ++static struct i2c_algo_bit_data __devinitdata bttv_i2c_algo_bit_template = { ++ .setsda = bttv_bit_setsda, ++ .setscl = bttv_bit_setscl, ++ .getsda = bttv_bit_getsda, ++ .getscl = bttv_bit_getscl, ++ .udelay = 16, ++ .timeout = 200, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* I2C functions - hardware i2c */ ++ ++static u32 functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL; ++} ++ ++static int ++bttv_i2c_wait_done(struct bttv *btv) ++{ ++ int rc = 0; ++ ++ /* timeout */ ++ if (wait_event_interruptible_timeout(btv->i2c_queue, ++ btv->i2c_done, msecs_to_jiffies(85)) == -ERESTARTSYS) ++ rc = -EIO; ++ ++ if (btv->i2c_done & BT848_INT_RACK) ++ rc = 1; ++ btv->i2c_done = 0; ++ return rc; ++} ++ ++#define I2C_HW (BT878_I2C_MODE | BT848_I2C_SYNC |\ ++ BT848_I2C_SCL | BT848_I2C_SDA) ++ ++static int ++bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) ++{ ++ u32 xmit; ++ int retval,cnt; ++ ++ /* sanity checks */ ++ if (0 == msg->len) ++ return -EINVAL; ++ ++ /* start, address + first byte */ ++ xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW; ++ if (msg->len > 1 || !last) ++ xmit |= BT878_I2C_NOSTOP; ++ btwrite(xmit, BT848_I2C); ++ retval = bttv_i2c_wait_done(btv); ++ if (retval < 0) ++ goto err; ++ if (retval == 0) ++ goto eio; ++ if (i2c_debug) { ++ pr_cont(" addr << 1, msg->buf[0]); ++ } ++ ++ for (cnt = 1; cnt < msg->len; cnt++ ) { ++ /* following bytes */ ++ xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART; ++ if (cnt < msg->len-1 || !last) ++ xmit |= BT878_I2C_NOSTOP; ++ btwrite(xmit, BT848_I2C); ++ retval = bttv_i2c_wait_done(btv); ++ if (retval < 0) ++ goto err; ++ if (retval == 0) ++ goto eio; ++ if (i2c_debug) ++ pr_cont(" %02x", msg->buf[cnt]); ++ } ++ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) ++ pr_cont(">\n"); ++ return msg->len; ++ ++ eio: ++ retval = -EIO; ++ err: ++ if (i2c_debug) ++ pr_cont(" ERR: %d\n",retval); ++ return retval; ++} ++ ++static int ++bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) ++{ ++ u32 xmit; ++ u32 cnt; ++ int retval; ++ ++ for (cnt = 0; cnt < msg->len; cnt++) { ++ xmit = (msg->addr << 25) | (1 << 24) | I2C_HW; ++ if (cnt < msg->len-1) ++ xmit |= BT848_I2C_W3B; ++ if (cnt < msg->len-1 || !last) ++ xmit |= BT878_I2C_NOSTOP; ++ if (cnt) ++ xmit |= BT878_I2C_NOSTART; ++ ++ if (i2c_debug) { ++ if (!(xmit & BT878_I2C_NOSTART)) ++ pr_cont(" addr << 1) +1); ++ } ++ ++ btwrite(xmit, BT848_I2C); ++ retval = bttv_i2c_wait_done(btv); ++ if (retval < 0) ++ goto err; ++ if (retval == 0) ++ goto eio; ++ msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff; ++ if (i2c_debug) { ++ pr_cont(" =%02x", msg->buf[cnt]); ++ } ++ if (i2c_debug && !(xmit & BT878_I2C_NOSTOP)) ++ pr_cont(" >\n"); ++ } ++ ++ ++ return msg->len; ++ ++ eio: ++ retval = -EIO; ++ err: ++ if (i2c_debug) ++ pr_cont(" ERR: %d\n",retval); ++ return retval; ++} ++ ++static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) ++{ ++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap); ++ struct bttv *btv = to_bttv(v4l2_dev); ++ int retval = 0; ++ int i; ++ ++ if (i2c_debug) ++ pr_debug("bt-i2c:"); ++ ++ btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT); ++ for (i = 0 ; i < num; i++) { ++ if (msgs[i].flags & I2C_M_RD) { ++ /* read */ ++ retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num); ++ if (retval < 0) ++ goto err; ++ } else { ++ /* write */ ++ retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num); ++ if (retval < 0) ++ goto err; ++ } ++ } ++ return num; ++ ++ err: ++ return retval; ++} ++ ++static const struct i2c_algorithm bttv_algo = { ++ .master_xfer = bttv_i2c_xfer, ++ .functionality = functionality, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* I2C functions - common stuff */ ++ ++/* read I2C */ ++int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) ++{ ++ unsigned char buffer = 0; ++ ++ if (0 != btv->i2c_rc) ++ return -1; ++ if (bttv_verbose && NULL != probe_for) ++ pr_info("%d: i2c: checking for %s @ 0x%02x... ", ++ btv->c.nr, probe_for, addr); ++ btv->i2c_client.addr = addr >> 1; ++ if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { ++ if (NULL != probe_for) { ++ if (bttv_verbose) ++ pr_cont("not found\n"); ++ } else ++ pr_warn("%d: i2c read 0x%x: error\n", ++ btv->c.nr, addr); ++ return -1; ++ } ++ if (bttv_verbose && NULL != probe_for) ++ pr_cont("found\n"); ++ return buffer; ++} ++ ++/* write I2C */ ++int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, ++ unsigned char b2, int both) ++{ ++ unsigned char buffer[2]; ++ int bytes = both ? 2 : 1; ++ ++ if (0 != btv->i2c_rc) ++ return -1; ++ btv->i2c_client.addr = addr >> 1; ++ buffer[0] = b1; ++ buffer[1] = b2; ++ if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes)) ++ return -1; ++ return 0; ++} ++ ++/* read EEPROM content */ ++void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr) ++{ ++ memset(eedata, 0, 256); ++ if (0 != btv->i2c_rc) ++ return; ++ btv->i2c_client.addr = addr >> 1; ++ tveeprom_read(&btv->i2c_client, eedata, 256); ++} ++ ++static char *i2c_devs[128] = { ++ [ 0x1c >> 1 ] = "lgdt330x", ++ [ 0x30 >> 1 ] = "IR (hauppauge)", ++ [ 0x80 >> 1 ] = "msp34xx", ++ [ 0x86 >> 1 ] = "tda9887", ++ [ 0xa0 >> 1 ] = "eeprom", ++ [ 0xc0 >> 1 ] = "tuner (analog)", ++ [ 0xc2 >> 1 ] = "tuner (analog)", ++}; ++ ++static void do_i2c_scan(char *name, struct i2c_client *c) ++{ ++ unsigned char buf; ++ int i,rc; ++ ++ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { ++ c->addr = i; ++ rc = i2c_master_recv(c,&buf,0); ++ if (rc < 0) ++ continue; ++ pr_info("%s: i2c scan: found device @ 0x%x [%s]\n", ++ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); ++ } ++} ++ ++/* init + register i2c adapter */ ++int __devinit init_bttv_i2c(struct bttv *btv) ++{ ++ strlcpy(btv->i2c_client.name, "bttv internal", I2C_NAME_SIZE); ++ ++ if (i2c_hw) ++ btv->use_i2c_hw = 1; ++ if (btv->use_i2c_hw) { ++ /* bt878 */ ++ strlcpy(btv->c.i2c_adap.name, "bt878", ++ sizeof(btv->c.i2c_adap.name)); ++ btv->c.i2c_adap.algo = &bttv_algo; ++ } else { ++ /* bt848 */ ++ /* Prevents usage of invalid delay values */ ++ if (i2c_udelay<5) ++ i2c_udelay=5; ++ ++ strlcpy(btv->c.i2c_adap.name, "bttv", ++ sizeof(btv->c.i2c_adap.name)); ++ btv->i2c_algo = bttv_i2c_algo_bit_template; ++ btv->i2c_algo.udelay = i2c_udelay; ++ btv->i2c_algo.data = btv; ++ btv->c.i2c_adap.algo_data = &btv->i2c_algo; ++ } ++ btv->c.i2c_adap.owner = THIS_MODULE; ++ ++ btv->c.i2c_adap.dev.parent = &btv->c.pci->dev; ++ snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name), ++ "bt%d #%d [%s]", btv->id, btv->c.nr, ++ btv->use_i2c_hw ? "hw" : "sw"); ++ ++ i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev); ++ btv->i2c_client.adapter = &btv->c.i2c_adap; ++ ++ ++ if (btv->use_i2c_hw) { ++ btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap); ++ } else { ++ bttv_bit_setscl(btv,1); ++ bttv_bit_setsda(btv,1); ++ btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap); ++ } ++ if (0 == btv->i2c_rc && i2c_scan) ++ do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client); ++ ++ return btv->i2c_rc; ++} +diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c +new file mode 100644 +index 0000000..a6a540d +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-if.c +@@ -0,0 +1,121 @@ ++/* ++ ++ bttv-if.c -- old gpio interface to other kernel modules ++ don't use in new code, will go away in 2.7 ++ have a look at bttv-gpio.c instead. ++ ++ bttv - Bt848 frame grabber driver ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ & Marcus Metzler (mocm@thp.uni-koeln.de) ++ (c) 1999-2003 Gerd Knorr ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include "bttvp.h" ++ ++EXPORT_SYMBOL(bttv_get_pcidev); ++EXPORT_SYMBOL(bttv_gpio_enable); ++EXPORT_SYMBOL(bttv_read_gpio); ++EXPORT_SYMBOL(bttv_write_gpio); ++ ++/* ----------------------------------------------------------------------- */ ++/* Exported functions - for other modules which want to access the */ ++/* gpio ports (IR for example) */ ++/* see bttv.h for comments */ ++ ++struct pci_dev* bttv_get_pcidev(unsigned int card) ++{ ++ if (card >= bttv_num) ++ return NULL; ++ if (!bttvs[card]) ++ return NULL; ++ ++ return bttvs[card]->c.pci; ++} ++ ++ ++int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) ++{ ++ struct bttv *btv; ++ ++ if (card >= bttv_num) { ++ return -EINVAL; ++ } ++ ++ btv = bttvs[card]; ++ if (!btv) ++ return -ENODEV; ++ ++ gpio_inout(mask,data); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"extern enable"); ++ return 0; ++} ++ ++int bttv_read_gpio(unsigned int card, unsigned long *data) ++{ ++ struct bttv *btv; ++ ++ if (card >= bttv_num) { ++ return -EINVAL; ++ } ++ ++ btv = bttvs[card]; ++ if (!btv) ++ return -ENODEV; ++ ++ if(btv->shutdown) { ++ return -ENODEV; ++ } ++ ++/* prior setting BT848_GPIO_REG_INP is (probably) not needed ++ because we set direct input on init */ ++ *data = gpio_read(); ++ return 0; ++} ++ ++int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) ++{ ++ struct bttv *btv; ++ ++ if (card >= bttv_num) { ++ return -EINVAL; ++ } ++ ++ btv = bttvs[card]; ++ if (!btv) ++ return -ENODEV; ++ ++/* prior setting BT848_GPIO_REG_INP is (probably) not needed ++ because direct input is set on init */ ++ gpio_bits(mask,data); ++ if (bttv_gpio) ++ bttv_gpio_tracking(btv,"extern write"); ++ return 0; ++} ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c +new file mode 100644 +index 0000000..1d608de +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-input.c +@@ -0,0 +1,590 @@ ++/* ++ * ++ * Copyright (c) 2003 Gerd Knorr ++ * Copyright (c) 2003 Pavel Machek ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bttv.h" ++#include "bttvp.h" ++ ++ ++static int ir_debug; ++module_param(ir_debug, int, 0644); ++ ++static int ir_rc5_remote_gap = 885; ++module_param(ir_rc5_remote_gap, int, 0644); ++ ++#undef dprintk ++#define dprintk(fmt, ...) \ ++do { \ ++ if (ir_debug >= 1) \ ++ pr_info(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define DEVNAME "bttv-input" ++ ++#define MODULE_NAME "bttv" ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void ir_handle_key(struct bttv *btv) ++{ ++ struct bttv_ir *ir = btv->remote; ++ u32 gpio,data; ++ ++ /* read gpio value */ ++ gpio = bttv_gpio_read(&btv->c); ++ if (ir->polling) { ++ if (ir->last_gpio == gpio) ++ return; ++ ir->last_gpio = gpio; ++ } ++ ++ /* extract data */ ++ data = ir_extract_bits(gpio, ir->mask_keycode); ++ dprintk("irq gpio=0x%x code=%d | %s%s%s\n", ++ gpio, data, ++ ir->polling ? "poll" : "irq", ++ (gpio & ir->mask_keydown) ? " down" : "", ++ (gpio & ir->mask_keyup) ? " up" : ""); ++ ++ if ((ir->mask_keydown && (gpio & ir->mask_keydown)) || ++ (ir->mask_keyup && !(gpio & ir->mask_keyup))) { ++ rc_keydown_notimeout(ir->dev, data, 0); ++ } else { ++ /* HACK: Probably, ir->mask_keydown is missing ++ for this board */ ++ if (btv->c.type == BTTV_BOARD_WINFAST2000) ++ rc_keydown_notimeout(ir->dev, data, 0); ++ ++ rc_keyup(ir->dev); ++ } ++} ++ ++static void ir_enltv_handle_key(struct bttv *btv) ++{ ++ struct bttv_ir *ir = btv->remote; ++ u32 gpio, data, keyup; ++ ++ /* read gpio value */ ++ gpio = bttv_gpio_read(&btv->c); ++ ++ /* extract data */ ++ data = ir_extract_bits(gpio, ir->mask_keycode); ++ ++ /* Check if it is keyup */ ++ keyup = (gpio & ir->mask_keyup) ? 1 << 31 : 0; ++ ++ if ((ir->last_gpio & 0x7f) != data) { ++ dprintk("gpio=0x%x code=%d | %s\n", ++ gpio, data, ++ (gpio & ir->mask_keyup) ? " up" : "up/down"); ++ ++ rc_keydown_notimeout(ir->dev, data, 0); ++ if (keyup) ++ rc_keyup(ir->dev); ++ } else { ++ if ((ir->last_gpio & 1 << 31) == keyup) ++ return; ++ ++ dprintk("(cnt) gpio=0x%x code=%d | %s\n", ++ gpio, data, ++ (gpio & ir->mask_keyup) ? " up" : "down"); ++ ++ if (keyup) ++ rc_keyup(ir->dev); ++ else ++ rc_keydown_notimeout(ir->dev, data, 0); ++ } ++ ++ ir->last_gpio = data | keyup; ++} ++ ++static int bttv_rc5_irq(struct bttv *btv); ++ ++void bttv_input_irq(struct bttv *btv) ++{ ++ struct bttv_ir *ir = btv->remote; ++ ++ if (ir->rc5_gpio) ++ bttv_rc5_irq(btv); ++ else if (!ir->polling) ++ ir_handle_key(btv); ++} ++ ++static void bttv_input_timer(unsigned long data) ++{ ++ struct bttv *btv = (struct bttv*)data; ++ struct bttv_ir *ir = btv->remote; ++ ++ if (btv->c.type == BTTV_BOARD_ENLTV_FM_2) ++ ir_enltv_handle_key(btv); ++ else ++ ir_handle_key(btv); ++ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); ++} ++ ++/* ++ * FIXME: Nebula digi uses the legacy way to decode RC5, instead of relying ++ * on the rc-core way. As we need to be sure that both IRQ transitions are ++ * properly triggered, Better to touch it only with this hardware for ++ * testing. ++ */ ++ ++#define RC5_START(x) (((x) >> 12) & 3) ++#define RC5_TOGGLE(x) (((x) >> 11) & 1) ++#define RC5_ADDR(x) (((x) >> 6) & 31) ++#define RC5_INSTR(x) ((x) & 63) ++ ++/* decode raw bit pattern to RC5 code */ ++static u32 bttv_rc5_decode(unsigned int code) ++{ ++ unsigned int org_code = code; ++ unsigned int pair; ++ unsigned int rc5 = 0; ++ int i; ++ ++ for (i = 0; i < 14; ++i) { ++ pair = code & 0x3; ++ code >>= 2; ++ ++ rc5 <<= 1; ++ switch (pair) { ++ case 0: ++ case 2: ++ break; ++ case 1: ++ rc5 |= 1; ++ break; ++ case 3: ++ dprintk("rc5_decode(%x) bad code\n", ++ org_code); ++ return 0; ++ } ++ } ++ dprintk("code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " ++ "instr=%x\n", rc5, org_code, RC5_START(rc5), ++ RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); ++ return rc5; ++} ++ ++static void bttv_rc5_timer_end(unsigned long data) ++{ ++ struct bttv_ir *ir = (struct bttv_ir *)data; ++ struct timeval tv; ++ u32 gap; ++ u32 rc5 = 0; ++ ++ /* get time */ ++ do_gettimeofday(&tv); ++ ++ /* avoid overflow with gap >1s */ ++ if (tv.tv_sec - ir->base_time.tv_sec > 1) { ++ gap = 200000; ++ } else { ++ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + ++ tv.tv_usec - ir->base_time.tv_usec; ++ } ++ ++ /* signal we're ready to start a new code */ ++ ir->active = false; ++ ++ /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ ++ if (gap < 28000) { ++ dprintk("spurious timer_end\n"); ++ return; ++ } ++ ++ if (ir->last_bit < 20) { ++ /* ignore spurious codes (caused by light/other remotes) */ ++ dprintk("short code: %x\n", ir->code); ++ } else { ++ ir->code = (ir->code << ir->shift_by) | 1; ++ rc5 = bttv_rc5_decode(ir->code); ++ ++ /* two start bits? */ ++ if (RC5_START(rc5) != ir->start) { ++ pr_info(DEVNAME ":" ++ " rc5 start bits invalid: %u\n", RC5_START(rc5)); ++ ++ /* right address? */ ++ } else if (RC5_ADDR(rc5) == ir->addr) { ++ u32 toggle = RC5_TOGGLE(rc5); ++ u32 instr = RC5_INSTR(rc5); ++ ++ /* Good code */ ++ rc_keydown(ir->dev, instr, toggle); ++ dprintk("instruction %x, toggle %x\n", ++ instr, toggle); ++ } ++ } ++} ++ ++static int bttv_rc5_irq(struct bttv *btv) ++{ ++ struct bttv_ir *ir = btv->remote; ++ struct timeval tv; ++ u32 gpio; ++ u32 gap; ++ unsigned long current_jiffies; ++ ++ /* read gpio port */ ++ gpio = bttv_gpio_read(&btv->c); ++ ++ /* get time of bit */ ++ current_jiffies = jiffies; ++ do_gettimeofday(&tv); ++ ++ /* avoid overflow with gap >1s */ ++ if (tv.tv_sec - ir->base_time.tv_sec > 1) { ++ gap = 200000; ++ } else { ++ gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + ++ tv.tv_usec - ir->base_time.tv_usec; ++ } ++ ++ dprintk("RC5 IRQ: gap %d us for %s\n", ++ gap, (gpio & 0x20) ? "mark" : "space"); ++ ++ /* remote IRQ? */ ++ if (!(gpio & 0x20)) ++ return 0; ++ ++ /* active code => add bit */ ++ if (ir->active) { ++ /* only if in the code (otherwise spurious IRQ or timer ++ late) */ ++ if (ir->last_bit < 28) { ++ ir->last_bit = (gap - ir_rc5_remote_gap / 2) / ++ ir_rc5_remote_gap; ++ ir->code |= 1 << ir->last_bit; ++ } ++ /* starting new code */ ++ } else { ++ ir->active = true; ++ ir->code = 0; ++ ir->base_time = tv; ++ ir->last_bit = 0; ++ ++ mod_timer(&ir->timer, current_jiffies + msecs_to_jiffies(30)); ++ } ++ ++ /* toggle GPIO pin 4 to reset the irq */ ++ bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); ++ bttv_gpio_write(&btv->c, gpio | (1 << 4)); ++ return 1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir) ++{ ++ if (ir->polling) { ++ setup_timer(&ir->timer, bttv_input_timer, (unsigned long)btv); ++ ir->timer.expires = jiffies + msecs_to_jiffies(1000); ++ add_timer(&ir->timer); ++ } else if (ir->rc5_gpio) { ++ /* set timer_end for code completion */ ++ setup_timer(&ir->timer, bttv_rc5_timer_end, (unsigned long)ir); ++ ir->shift_by = 1; ++ ir->start = 3; ++ ir->addr = 0x0; ++ ir->rc5_remote_gap = ir_rc5_remote_gap; ++ } ++} ++ ++static void bttv_ir_stop(struct bttv *btv) ++{ ++ if (btv->remote->polling) ++ del_timer_sync(&btv->remote->timer); ++ ++ if (btv->remote->rc5_gpio) { ++ u32 gpio; ++ ++ del_timer_sync(&btv->remote->timer); ++ ++ gpio = bttv_gpio_read(&btv->c); ++ bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); ++ } ++} ++ ++/* ++ * Get_key functions used by I2C remotes ++ */ ++ ++static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char b; ++ ++ /* poll IR chip */ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ dprintk("read error\n"); ++ return -EIO; ++ } ++ ++ /* ignore 0xaa */ ++ if (b==0xaa) ++ return 0; ++ dprintk("key %02x\n", b); ++ ++ /* ++ * NOTE: ++ * lirc_i2c maps the pv951 code as: ++ * addr = 0x61D6 ++ * cmd = bit_reverse (b) ++ * So, it seems that this device uses NEC extended ++ * I decided to not fix the table, due to two reasons: ++ * 1) Without the actual device, this is only a guess; ++ * 2) As the addr is not reported via I2C, nor can be changed, ++ * the device is bound to the vendor-provided RC. ++ */ ++ ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++/* Instantiate the I2C IR receiver device, if present */ ++void __devinit init_bttv_i2c_ir(struct bttv *btv) ++{ ++ const unsigned short addr_list[] = { ++ 0x1a, 0x18, 0x64, 0x30, 0x71, ++ I2C_CLIENT_END ++ }; ++ struct i2c_board_info info; ++ ++ if (0 != btv->i2c_rc) ++ return; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ memset(&btv->init_data, 0, sizeof(btv->init_data)); ++ strlcpy(info.type, "ir_video", I2C_NAME_SIZE); ++ ++ switch (btv->c.type) { ++ case BTTV_BOARD_PV951: ++ btv->init_data.name = "PV951"; ++ btv->init_data.get_key = get_key_pv951; ++ btv->init_data.ir_codes = RC_MAP_PV951; ++ info.addr = 0x4b; ++ break; ++ default: ++ /* ++ * The external IR receiver is at i2c address 0x34 (0x35 for ++ * reads). Future Hauppauge cards will have an internal ++ * receiver at 0x30 (0x31 for reads). In theory, both can be ++ * fitted, and Hauppauge suggest an external overrides an ++ * internal. ++ * That's why we probe 0x1a (~0x34) first. CB ++ */ ++ ++ i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list, NULL); ++ return; ++ } ++ ++ if (btv->init_data.name) ++ info.platform_data = &btv->init_data; ++ i2c_new_device(&btv->c.i2c_adap, &info); ++ ++ return; ++} ++ ++int __devexit fini_bttv_i2c(struct bttv *btv) ++{ ++ if (0 != btv->i2c_rc) ++ return 0; ++ ++ return i2c_del_adapter(&btv->c.i2c_adap); ++} ++ ++int bttv_input_init(struct bttv *btv) ++{ ++ struct bttv_ir *ir; ++ char *ir_codes = NULL; ++ struct rc_dev *rc; ++ int err = -ENOMEM; ++ ++ if (!btv->has_remote) ++ return -ENODEV; ++ ++ ir = kzalloc(sizeof(*ir),GFP_KERNEL); ++ rc = rc_allocate_device(); ++ if (!ir || !rc) ++ goto err_out_free; ++ ++ /* detect & configure */ ++ switch (btv->c.type) { ++ case BTTV_BOARD_AVERMEDIA: ++ case BTTV_BOARD_AVPHONE98: ++ case BTTV_BOARD_AVERMEDIA98: ++ ir_codes = RC_MAP_AVERMEDIA; ++ ir->mask_keycode = 0xf88000; ++ ir->mask_keydown = 0x010000; ++ ir->polling = 50; // ms ++ break; ++ ++ case BTTV_BOARD_AVDVBT_761: ++ case BTTV_BOARD_AVDVBT_771: ++ ir_codes = RC_MAP_AVERMEDIA_DVBT; ++ ir->mask_keycode = 0x0f00c0; ++ ir->mask_keydown = 0x000020; ++ ir->polling = 50; // ms ++ break; ++ ++ case BTTV_BOARD_PXELVWPLTVPAK: ++ ir_codes = RC_MAP_PIXELVIEW; ++ ir->mask_keycode = 0x003e00; ++ ir->mask_keyup = 0x010000; ++ ir->polling = 50; // ms ++ break; ++ case BTTV_BOARD_PV_M4900: ++ case BTTV_BOARD_PV_BT878P_9B: ++ case BTTV_BOARD_PV_BT878P_PLUS: ++ ir_codes = RC_MAP_PIXELVIEW; ++ ir->mask_keycode = 0x001f00; ++ ir->mask_keyup = 0x008000; ++ ir->polling = 50; // ms ++ break; ++ ++ case BTTV_BOARD_WINFAST2000: ++ ir_codes = RC_MAP_WINFAST; ++ ir->mask_keycode = 0x1f8; ++ break; ++ case BTTV_BOARD_MAGICTVIEW061: ++ case BTTV_BOARD_MAGICTVIEW063: ++ ir_codes = RC_MAP_WINFAST; ++ ir->mask_keycode = 0x0008e000; ++ ir->mask_keydown = 0x00200000; ++ break; ++ case BTTV_BOARD_APAC_VIEWCOMP: ++ ir_codes = RC_MAP_APAC_VIEWCOMP; ++ ir->mask_keycode = 0x001f00; ++ ir->mask_keyup = 0x008000; ++ ir->polling = 50; // ms ++ break; ++ case BTTV_BOARD_ASKEY_CPH03X: ++ case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: ++ case BTTV_BOARD_CONTVFMI: ++ ir_codes = RC_MAP_PIXELVIEW; ++ ir->mask_keycode = 0x001F00; ++ ir->mask_keyup = 0x006000; ++ ir->polling = 50; // ms ++ break; ++ case BTTV_BOARD_NEBULA_DIGITV: ++ ir_codes = RC_MAP_NEBULA; ++ ir->rc5_gpio = true; ++ break; ++ case BTTV_BOARD_MACHTV_MAGICTV: ++ ir_codes = RC_MAP_APAC_VIEWCOMP; ++ ir->mask_keycode = 0x001F00; ++ ir->mask_keyup = 0x004000; ++ ir->polling = 50; /* ms */ ++ break; ++ case BTTV_BOARD_KOZUMI_KTV_01C: ++ ir_codes = RC_MAP_PCTV_SEDNA; ++ ir->mask_keycode = 0x001f00; ++ ir->mask_keyup = 0x006000; ++ ir->polling = 50; /* ms */ ++ break; ++ case BTTV_BOARD_ENLTV_FM_2: ++ ir_codes = RC_MAP_ENCORE_ENLTV2; ++ ir->mask_keycode = 0x00fd00; ++ ir->mask_keyup = 0x000080; ++ ir->polling = 1; /* ms */ ++ ir->last_gpio = ir_extract_bits(bttv_gpio_read(&btv->c), ++ ir->mask_keycode); ++ break; ++ } ++ if (NULL == ir_codes) { ++ dprintk("Ooops: IR config error [card=%d]\n", btv->c.type); ++ err = -ENODEV; ++ goto err_out_free; ++ } ++ ++ if (ir->rc5_gpio) { ++ u32 gpio; ++ /* enable remote irq */ ++ bttv_gpio_inout(&btv->c, (1 << 4), 1 << 4); ++ gpio = bttv_gpio_read(&btv->c); ++ bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); ++ bttv_gpio_write(&btv->c, gpio | (1 << 4)); ++ } else { ++ /* init hardware-specific stuff */ ++ bttv_gpio_inout(&btv->c, ir->mask_keycode | ir->mask_keydown, 0); ++ } ++ ++ /* init input device */ ++ ir->dev = rc; ++ ++ snprintf(ir->name, sizeof(ir->name), "bttv IR (card=%d)", ++ btv->c.type); ++ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", ++ pci_name(btv->c.pci)); ++ ++ rc->input_name = ir->name; ++ rc->input_phys = ir->phys; ++ rc->input_id.bustype = BUS_PCI; ++ rc->input_id.version = 1; ++ if (btv->c.pci->subsystem_vendor) { ++ rc->input_id.vendor = btv->c.pci->subsystem_vendor; ++ rc->input_id.product = btv->c.pci->subsystem_device; ++ } else { ++ rc->input_id.vendor = btv->c.pci->vendor; ++ rc->input_id.product = btv->c.pci->device; ++ } ++ rc->dev.parent = &btv->c.pci->dev; ++ rc->map_name = ir_codes; ++ rc->driver_name = MODULE_NAME; ++ ++ btv->remote = ir; ++ bttv_ir_start(btv, ir); ++ ++ /* all done */ ++ err = rc_register_device(rc); ++ if (err) ++ goto err_out_stop; ++ ++ return 0; ++ ++ err_out_stop: ++ bttv_ir_stop(btv); ++ btv->remote = NULL; ++ err_out_free: ++ rc_free_device(rc); ++ kfree(ir); ++ return err; ++} ++ ++void bttv_input_fini(struct bttv *btv) ++{ ++ if (btv->remote == NULL) ++ return; ++ ++ bttv_ir_stop(btv); ++ rc_unregister_device(btv->remote->dev); ++ kfree(btv->remote); ++ btv->remote = NULL; ++} +diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c +new file mode 100644 +index 0000000..55c5d82 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-risc.c +@@ -0,0 +1,910 @@ ++/* ++ ++ bttv-risc.c -- interfaces to other kernel modules ++ ++ bttv risc code handling ++ - memory management ++ - generation ++ ++ (c) 2000-2003 Gerd Knorr ++ ++ 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. ++ ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bttvp.h" ++ ++#define VCR_HACK_LINES 4 ++ ++/* ---------------------------------------------------------- */ ++/* risc code generators */ ++ ++int ++bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int offset, unsigned int bpl, ++ unsigned int padding, unsigned int skip_lines, ++ unsigned int store_lines) ++{ ++ u32 instructions,line,todo; ++ struct scatterlist *sg; ++ __le32 *rp; ++ int rc; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + sync + jump (all 2 dwords). padding ++ can cause next bpl to start close to a page border. First DMA ++ region may be smaller than PAGE_SIZE */ ++ instructions = skip_lines * 4; ++ instructions += (1 + ((bpl + padding) * store_lines) ++ / PAGE_SIZE + store_lines) * 8; ++ instructions += 2 * 8; ++ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0) ++ return rc; ++ ++ /* sync instruction */ ++ rp = risc->cpu; ++ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); ++ *(rp++) = cpu_to_le32(0); ++ ++ while (skip_lines-- > 0) { ++ *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL | ++ BT848_RISC_EOL | bpl); ++ } ++ ++ /* scan lines */ ++ sg = sglist; ++ for (line = 0; line < store_lines; line++) { ++ if ((btv->opt_vcr_hack) && ++ (line >= (store_lines - VCR_HACK_LINES))) ++ continue; ++ while (offset && offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ sg++; ++ } ++ if (bpl <= sg_dma_len(sg)-offset) { ++ /* fits into current chunk */ ++ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| ++ BT848_RISC_EOL|bpl); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); ++ offset+=bpl; ++ } else { ++ /* scanline needs to be splitted */ ++ todo = bpl; ++ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| ++ (sg_dma_len(sg)-offset)); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); ++ todo -= (sg_dma_len(sg)-offset); ++ offset = 0; ++ sg++; ++ while (todo > sg_dma_len(sg)) { ++ *(rp++)=cpu_to_le32(BT848_RISC_WRITE| ++ sg_dma_len(sg)); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)); ++ todo -= sg_dma_len(sg); ++ sg++; ++ } ++ *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL| ++ todo); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)); ++ offset += todo; ++ } ++ offset += padding; ++ } ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++ ++static int ++bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int yoffset, unsigned int ybpl, ++ unsigned int ypadding, unsigned int ylines, ++ unsigned int uoffset, unsigned int voffset, ++ unsigned int hshift, unsigned int vshift, ++ unsigned int cpadding) ++{ ++ unsigned int instructions,line,todo,ylen,chroma; ++ __le32 *rp; ++ u32 ri; ++ struct scatterlist *ysg; ++ struct scatterlist *usg; ++ struct scatterlist *vsg; ++ int topfield = (0 == yoffset); ++ int rc; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line (5 dwords) ++ plus sync + jump (2 dwords) */ ++ instructions = ((3 + (ybpl + ypadding) * ylines * 2) ++ / PAGE_SIZE) + ylines; ++ instructions += 2; ++ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0) ++ return rc; ++ ++ /* sync instruction */ ++ rp = risc->cpu; ++ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); ++ *(rp++) = cpu_to_le32(0); ++ ++ /* scan lines */ ++ ysg = sglist; ++ usg = sglist; ++ vsg = sglist; ++ for (line = 0; line < ylines; line++) { ++ if ((btv->opt_vcr_hack) && ++ (line >= (ylines - VCR_HACK_LINES))) ++ continue; ++ switch (vshift) { ++ case 0: ++ chroma = 1; ++ break; ++ case 1: ++ if (topfield) ++ chroma = ((line & 1) == 0); ++ else ++ chroma = ((line & 1) == 1); ++ break; ++ case 2: ++ if (topfield) ++ chroma = ((line & 3) == 0); ++ else ++ chroma = ((line & 3) == 2); ++ break; ++ default: ++ chroma = 0; ++ break; ++ } ++ ++ for (todo = ybpl; todo > 0; todo -= ylen) { ++ /* go to next sg entry if needed */ ++ while (yoffset && yoffset >= sg_dma_len(ysg)) { ++ yoffset -= sg_dma_len(ysg); ++ ysg++; ++ } ++ while (uoffset && uoffset >= sg_dma_len(usg)) { ++ uoffset -= sg_dma_len(usg); ++ usg++; ++ } ++ while (voffset && voffset >= sg_dma_len(vsg)) { ++ voffset -= sg_dma_len(vsg); ++ vsg++; ++ } ++ ++ /* calculate max number of bytes we can write */ ++ ylen = todo; ++ if (yoffset + ylen > sg_dma_len(ysg)) ++ ylen = sg_dma_len(ysg) - yoffset; ++ if (chroma) { ++ if (uoffset + (ylen>>hshift) > sg_dma_len(usg)) ++ ylen = (sg_dma_len(usg) - uoffset) << hshift; ++ if (voffset + (ylen>>hshift) > sg_dma_len(vsg)) ++ ylen = (sg_dma_len(vsg) - voffset) << hshift; ++ ri = BT848_RISC_WRITE123; ++ } else { ++ ri = BT848_RISC_WRITE1S23; ++ } ++ if (ybpl == todo) ++ ri |= BT848_RISC_SOL; ++ if (ylen == todo) ++ ri |= BT848_RISC_EOL; ++ ++ /* write risc instruction */ ++ *(rp++)=cpu_to_le32(ri | ylen); ++ *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) | ++ (ylen >> hshift)); ++ *(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset); ++ yoffset += ylen; ++ if (chroma) { ++ *(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset); ++ uoffset += ylen >> hshift; ++ *(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset); ++ voffset += ylen >> hshift; ++ } ++ } ++ yoffset += ypadding; ++ if (chroma) { ++ uoffset += cpadding; ++ voffset += cpadding; ++ } ++ } ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++ ++static int ++bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, ++ const struct bttv_format *fmt, struct bttv_overlay *ov, ++ int skip_even, int skip_odd) ++{ ++ int dwords, rc, line, maxy, start, end; ++ unsigned skip, nskips; ++ struct btcx_skiplist *skips; ++ __le32 *rp; ++ u32 ri,ra; ++ u32 addr; ++ ++ /* skip list for window clipping */ ++ if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL))) ++ return -ENOMEM; ++ ++ /* estimate risc mem: worst case is (1.5*clip+1) * lines instructions ++ + sync + jump (all 2 dwords) */ ++ dwords = (3 * ov->nclips + 2) * ++ ((skip_even || skip_odd) ? (ov->w.height+1)>>1 : ov->w.height); ++ dwords += 4; ++ if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,dwords*4)) < 0) { ++ kfree(skips); ++ return rc; ++ } ++ ++ /* sync instruction */ ++ rp = risc->cpu; ++ *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); ++ *(rp++) = cpu_to_le32(0); ++ ++ addr = (unsigned long)btv->fbuf.base; ++ addr += btv->fbuf.fmt.bytesperline * ov->w.top; ++ addr += (fmt->depth >> 3) * ov->w.left; ++ ++ /* scan lines */ ++ for (maxy = -1, line = 0; line < ov->w.height; ++ line++, addr += btv->fbuf.fmt.bytesperline) { ++ if ((btv->opt_vcr_hack) && ++ (line >= (ov->w.height - VCR_HACK_LINES))) ++ continue; ++ if ((line%2) == 0 && skip_even) ++ continue; ++ if ((line%2) == 1 && skip_odd) ++ continue; ++ ++ /* calculate clipping */ ++ if (line > maxy) ++ btcx_calc_skips(line, ov->w.width, &maxy, ++ skips, &nskips, ov->clips, ov->nclips); ++ ++ /* write out risc code */ ++ for (start = 0, skip = 0; start < ov->w.width; start = end) { ++ if (skip >= nskips) { ++ ri = BT848_RISC_WRITE; ++ end = ov->w.width; ++ } else if (start < skips[skip].start) { ++ ri = BT848_RISC_WRITE; ++ end = skips[skip].start; ++ } else { ++ ri = BT848_RISC_SKIP; ++ end = skips[skip].end; ++ skip++; ++ } ++ if (BT848_RISC_WRITE == ri) ++ ra = addr + (fmt->depth>>3)*start; ++ else ++ ra = 0; ++ ++ if (0 == start) ++ ri |= BT848_RISC_SOL; ++ if (ov->w.width == end) ++ ri |= BT848_RISC_EOL; ++ ri |= (fmt->depth>>3) * (end-start); ++ ++ *(rp++)=cpu_to_le32(ri); ++ if (0 != ra) ++ *(rp++)=cpu_to_le32(ra); ++ } ++ } ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ kfree(skips); ++ return 0; ++} ++ ++/* ---------------------------------------------------------- */ ++ ++static void ++bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo, ++ int width, int height, int interleaved, ++ const struct bttv_tvnorm *tvnorm) ++{ ++ u32 xsf, sr; ++ int vdelay; ++ ++ int swidth = tvnorm->swidth; ++ int totalwidth = tvnorm->totalwidth; ++ int scaledtwidth = tvnorm->scaledtwidth; ++ ++ if (btv->input == btv->dig) { ++ swidth = 720; ++ totalwidth = 858; ++ scaledtwidth = 858; ++ } ++ ++ vdelay = tvnorm->vdelay; ++ ++ xsf = (width*scaledtwidth)/swidth; ++ geo->hscale = ((totalwidth*4096UL)/xsf-4096); ++ geo->hdelay = tvnorm->hdelayx1; ++ geo->hdelay = (geo->hdelay*width)/swidth; ++ geo->hdelay &= 0x3fe; ++ sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512; ++ geo->vscale = (0x10000UL-sr) & 0x1fff; ++ geo->crop = ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) | ++ ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0); ++ geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0; ++ geo->vdelay = vdelay; ++ geo->width = width; ++ geo->sheight = tvnorm->sheight; ++ geo->vtotal = tvnorm->vtotal; ++ ++ if (btv->opt_combfilter) { ++ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); ++ geo->comb = (width < 769) ? 1 : 0; ++ } else { ++ geo->vtc = 0; ++ geo->comb = 0; ++ } ++} ++ ++static void ++bttv_calc_geo (struct bttv * btv, ++ struct bttv_geometry * geo, ++ unsigned int width, ++ unsigned int height, ++ int both_fields, ++ const struct bttv_tvnorm * tvnorm, ++ const struct v4l2_rect * crop) ++{ ++ unsigned int c_width; ++ unsigned int c_height; ++ u32 sr; ++ ++ if ((crop->left == tvnorm->cropcap.defrect.left ++ && crop->top == tvnorm->cropcap.defrect.top ++ && crop->width == tvnorm->cropcap.defrect.width ++ && crop->height == tvnorm->cropcap.defrect.height ++ && width <= tvnorm->swidth /* see PAL-Nc et al */) ++ || btv->input == btv->dig) { ++ bttv_calc_geo_old(btv, geo, width, height, ++ both_fields, tvnorm); ++ return; ++ } ++ ++ /* For bug compatibility the image size checks permit scale ++ factors > 16. See bttv_crop_calc_limits(). */ ++ c_width = min((unsigned int) crop->width, width * 16); ++ c_height = min((unsigned int) crop->height, height * 16); ++ ++ geo->width = width; ++ geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096; ++ /* Even to store Cb first, odd for Cr. */ ++ geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1; ++ ++ geo->sheight = c_height; ++ geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY; ++ sr = c_height >> !both_fields; ++ sr = (sr * 512U + (height >> 1)) / height - 512; ++ geo->vscale = (0x10000UL - sr) & 0x1fff; ++ geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0; ++ geo->vtotal = tvnorm->vtotal; ++ ++ geo->crop = (((geo->width >> 8) & 0x03) | ++ ((geo->hdelay >> 6) & 0x0c) | ++ ((geo->sheight >> 4) & 0x30) | ++ ((geo->vdelay >> 2) & 0xc0)); ++ ++ if (btv->opt_combfilter) { ++ geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); ++ geo->comb = (width < 769) ? 1 : 0; ++ } else { ++ geo->vtc = 0; ++ geo->comb = 0; ++ } ++} ++ ++static void ++bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd) ++{ ++ int off = odd ? 0x80 : 0x00; ++ ++ if (geo->comb) ++ btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); ++ else ++ btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); ++ ++ btwrite(geo->vtc, BT848_E_VTC+off); ++ btwrite(geo->hscale >> 8, BT848_E_HSCALE_HI+off); ++ btwrite(geo->hscale & 0xff, BT848_E_HSCALE_LO+off); ++ btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off); ++ btwrite(geo->vscale & 0xff, BT848_E_VSCALE_LO+off); ++ btwrite(geo->width & 0xff, BT848_E_HACTIVE_LO+off); ++ btwrite(geo->hdelay & 0xff, BT848_E_HDELAY_LO+off); ++ btwrite(geo->sheight & 0xff, BT848_E_VACTIVE_LO+off); ++ btwrite(geo->vdelay & 0xff, BT848_E_VDELAY_LO+off); ++ btwrite(geo->crop, BT848_E_CROP+off); ++ btwrite(geo->vtotal>>8, BT848_VTOTAL_HI); ++ btwrite(geo->vtotal & 0xff, BT848_VTOTAL_LO); ++} ++ ++/* ---------------------------------------------------------- */ ++/* risc group / risc main loop / dma management */ ++ ++void ++bttv_set_dma(struct bttv *btv, int override) ++{ ++ unsigned long cmd; ++ int capctl; ++ ++ btv->cap_ctl = 0; ++ if (NULL != btv->curr.top) btv->cap_ctl |= 0x02; ++ if (NULL != btv->curr.bottom) btv->cap_ctl |= 0x01; ++ if (NULL != btv->cvbi) btv->cap_ctl |= 0x0c; ++ ++ capctl = 0; ++ capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00; /* capture */ ++ capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */ ++ capctl |= override; ++ ++ d2printk("%d: capctl=%x lirq=%d top=%08llx/%08llx even=%08llx/%08llx\n", ++ btv->c.nr,capctl,btv->loop_irq, ++ btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, ++ btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, ++ btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0, ++ btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); ++ ++ cmd = BT848_RISC_JUMP; ++ if (btv->loop_irq) { ++ cmd |= BT848_RISC_IRQ; ++ cmd |= (btv->loop_irq & 0x0f) << 16; ++ cmd |= (~btv->loop_irq & 0x0f) << 20; ++ } ++ if (btv->curr.frame_irq || btv->loop_irq || btv->cvbi) { ++ mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT); ++ } else { ++ del_timer(&btv->timeout); ++ } ++ btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd); ++ ++ btaor(capctl, ~0x0f, BT848_CAP_CTL); ++ if (capctl) { ++ if (btv->dma_on) ++ return; ++ btwrite(btv->main.dma, BT848_RISC_STRT_ADD); ++ btor(3, BT848_GPIO_DMA_CTL); ++ btv->dma_on = 1; ++ } else { ++ if (!btv->dma_on) ++ return; ++ btand(~3, BT848_GPIO_DMA_CTL); ++ btv->dma_on = 0; ++ } ++ return; ++} ++ ++int ++bttv_risc_init_main(struct bttv *btv) ++{ ++ int rc; ++ ++ if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0) ++ return rc; ++ dprintk("%d: risc main @ %08llx\n", ++ btv->c.nr, (unsigned long long)btv->main.dma); ++ ++ btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC | ++ BT848_FIFO_STATUS_VRE); ++ btv->main.cpu[1] = cpu_to_le32(0); ++ btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2)); ++ ++ /* top field */ ++ btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2)); ++ btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2)); ++ ++ btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC | ++ BT848_FIFO_STATUS_VRO); ++ btv->main.cpu[9] = cpu_to_le32(0); ++ ++ /* bottom field */ ++ btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2)); ++ btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2)); ++ ++ /* jump back to top field */ ++ btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP); ++ btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2)); ++ ++ return 0; ++} ++ ++int ++bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, ++ int irqflags) ++{ ++ unsigned long cmd; ++ unsigned long next = btv->main.dma + ((slot+2) << 2); ++ ++ if (NULL == risc) { ++ d2printk("%d: risc=%p slot[%d]=NULL\n", btv->c.nr, risc, slot); ++ btv->main.cpu[slot+1] = cpu_to_le32(next); ++ } else { ++ d2printk("%d: risc=%p slot[%d]=%08llx irq=%d\n", ++ btv->c.nr, risc, slot, ++ (unsigned long long)risc->dma, irqflags); ++ cmd = BT848_RISC_JUMP; ++ if (irqflags) { ++ cmd |= BT848_RISC_IRQ; ++ cmd |= (irqflags & 0x0f) << 16; ++ cmd |= (~irqflags & 0x0f) << 20; ++ } ++ risc->jmp[0] = cpu_to_le32(cmd); ++ risc->jmp[1] = cpu_to_le32(next); ++ btv->main.cpu[slot+1] = cpu_to_le32(risc->dma); ++ } ++ return 0; ++} ++ ++void ++bttv_dma_free(struct videobuf_queue *q,struct bttv *btv, struct bttv_buffer *buf) ++{ ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ BUG_ON(in_interrupt()); ++ videobuf_waiton(q, &buf->vb, 0, 0); ++ videobuf_dma_unmap(q->dev, dma); ++ videobuf_dma_free(dma); ++ btcx_riscmem_free(btv->c.pci,&buf->bottom); ++ btcx_riscmem_free(btv->c.pci,&buf->top); ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++int ++bttv_buffer_activate_vbi(struct bttv *btv, ++ struct bttv_buffer *vbi) ++{ ++ struct btcx_riscmem *top; ++ struct btcx_riscmem *bottom; ++ int top_irq_flags; ++ int bottom_irq_flags; ++ ++ top = NULL; ++ bottom = NULL; ++ top_irq_flags = 0; ++ bottom_irq_flags = 0; ++ ++ if (vbi) { ++ unsigned int crop, vdelay; ++ ++ vbi->vb.state = VIDEOBUF_ACTIVE; ++ list_del(&vbi->vb.queue); ++ ++ /* VDELAY is start of video, end of VBI capturing. */ ++ crop = btread(BT848_E_CROP); ++ vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2); ++ ++ if (vbi->geo.vdelay > vdelay) { ++ vdelay = vbi->geo.vdelay & 0xfe; ++ crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0); ++ ++ btwrite(vdelay, BT848_E_VDELAY_LO); ++ btwrite(crop, BT848_E_CROP); ++ btwrite(vdelay, BT848_O_VDELAY_LO); ++ btwrite(crop, BT848_O_CROP); ++ } ++ ++ if (vbi->vbi_count[0] > 0) { ++ top = &vbi->top; ++ top_irq_flags = 4; ++ } ++ ++ if (vbi->vbi_count[1] > 0) { ++ top_irq_flags = 0; ++ bottom = &vbi->bottom; ++ bottom_irq_flags = 4; ++ } ++ } ++ ++ bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags); ++ bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags); ++ ++ return 0; ++} ++ ++int ++bttv_buffer_activate_video(struct bttv *btv, ++ struct bttv_buffer_set *set) ++{ ++ /* video capture */ ++ if (NULL != set->top && NULL != set->bottom) { ++ if (set->top == set->bottom) { ++ set->top->vb.state = VIDEOBUF_ACTIVE; ++ if (set->top->vb.queue.next) ++ list_del(&set->top->vb.queue); ++ } else { ++ set->top->vb.state = VIDEOBUF_ACTIVE; ++ set->bottom->vb.state = VIDEOBUF_ACTIVE; ++ if (set->top->vb.queue.next) ++ list_del(&set->top->vb.queue); ++ if (set->bottom->vb.queue.next) ++ list_del(&set->bottom->vb.queue); ++ } ++ bttv_apply_geo(btv, &set->top->geo, 1); ++ bttv_apply_geo(btv, &set->bottom->geo,0); ++ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, ++ set->top_irq); ++ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, ++ set->frame_irq); ++ btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f), ++ ~0xff, BT848_COLOR_FMT); ++ btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05), ++ ~0x0f, BT848_COLOR_CTL); ++ } else if (NULL != set->top) { ++ set->top->vb.state = VIDEOBUF_ACTIVE; ++ if (set->top->vb.queue.next) ++ list_del(&set->top->vb.queue); ++ bttv_apply_geo(btv, &set->top->geo,1); ++ bttv_apply_geo(btv, &set->top->geo,0); ++ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, ++ set->frame_irq); ++ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); ++ btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT); ++ btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); ++ } else if (NULL != set->bottom) { ++ set->bottom->vb.state = VIDEOBUF_ACTIVE; ++ if (set->bottom->vb.queue.next) ++ list_del(&set->bottom->vb.queue); ++ bttv_apply_geo(btv, &set->bottom->geo,1); ++ bttv_apply_geo(btv, &set->bottom->geo,0); ++ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); ++ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, ++ set->frame_irq); ++ btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT); ++ btaor(set->bottom->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); ++ } else { ++ bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); ++ bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); ++ } ++ return 0; ++} ++ ++/* ---------------------------------------------------------- */ ++ ++/* calculate geometry, build risc code */ ++int ++bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) ++{ ++ const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm; ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ dprintk("%d: buffer field: %s format: %s size: %dx%d\n", ++ btv->c.nr, v4l2_field_names[buf->vb.field], ++ buf->fmt->name, buf->vb.width, buf->vb.height); ++ ++ /* packed pixel modes */ ++ if (buf->fmt->flags & FORMAT_FLAGS_PACKED) { ++ int bpl = (buf->fmt->depth >> 3) * buf->vb.width; ++ int bpf = bpl * (buf->vb.height >> 1); ++ ++ bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, ++ V4L2_FIELD_HAS_BOTH(buf->vb.field), ++ tvnorm,&buf->crop); ++ ++ switch (buf->vb.field) { ++ case V4L2_FIELD_TOP: ++ bttv_risc_packed(btv,&buf->top,dma->sglist, ++ /* offset */ 0,bpl, ++ /* padding */ 0,/* skip_lines */ 0, ++ buf->vb.height); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ bttv_risc_packed(btv,&buf->bottom,dma->sglist, ++ 0,bpl,0,0,buf->vb.height); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ bttv_risc_packed(btv,&buf->top,dma->sglist, ++ 0,bpl,bpl,0,buf->vb.height >> 1); ++ bttv_risc_packed(btv,&buf->bottom,dma->sglist, ++ bpl,bpl,bpl,0,buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ bttv_risc_packed(btv,&buf->top,dma->sglist, ++ 0,bpl,0,0,buf->vb.height >> 1); ++ bttv_risc_packed(btv,&buf->bottom,dma->sglist, ++ bpf,bpl,0,0,buf->vb.height >> 1); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ ++ /* planar modes */ ++ if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) { ++ int uoffset, voffset; ++ int ypadding, cpadding, lines; ++ ++ /* calculate chroma offsets */ ++ uoffset = buf->vb.width * buf->vb.height; ++ voffset = buf->vb.width * buf->vb.height; ++ if (buf->fmt->flags & FORMAT_FLAGS_CrCb) { ++ /* Y-Cr-Cb plane order */ ++ uoffset >>= buf->fmt->hshift; ++ uoffset >>= buf->fmt->vshift; ++ uoffset += voffset; ++ } else { ++ /* Y-Cb-Cr plane order */ ++ voffset >>= buf->fmt->hshift; ++ voffset >>= buf->fmt->vshift; ++ voffset += uoffset; ++ } ++ ++ switch (buf->vb.field) { ++ case V4L2_FIELD_TOP: ++ bttv_calc_geo(btv,&buf->geo,buf->vb.width, ++ buf->vb.height,/* both_fields */ 0, ++ tvnorm,&buf->crop); ++ bttv_risc_planar(btv, &buf->top, dma->sglist, ++ 0,buf->vb.width,0,buf->vb.height, ++ uoffset,voffset,buf->fmt->hshift, ++ buf->fmt->vshift,0); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ bttv_calc_geo(btv,&buf->geo,buf->vb.width, ++ buf->vb.height,0, ++ tvnorm,&buf->crop); ++ bttv_risc_planar(btv, &buf->bottom, dma->sglist, ++ 0,buf->vb.width,0,buf->vb.height, ++ uoffset,voffset,buf->fmt->hshift, ++ buf->fmt->vshift,0); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ bttv_calc_geo(btv,&buf->geo,buf->vb.width, ++ buf->vb.height,1, ++ tvnorm,&buf->crop); ++ lines = buf->vb.height >> 1; ++ ypadding = buf->vb.width; ++ cpadding = buf->vb.width >> buf->fmt->hshift; ++ bttv_risc_planar(btv,&buf->top, ++ dma->sglist, ++ 0,buf->vb.width,ypadding,lines, ++ uoffset,voffset, ++ buf->fmt->hshift, ++ buf->fmt->vshift, ++ cpadding); ++ bttv_risc_planar(btv,&buf->bottom, ++ dma->sglist, ++ ypadding,buf->vb.width,ypadding,lines, ++ uoffset+cpadding, ++ voffset+cpadding, ++ buf->fmt->hshift, ++ buf->fmt->vshift, ++ cpadding); ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ bttv_calc_geo(btv,&buf->geo,buf->vb.width, ++ buf->vb.height,1, ++ tvnorm,&buf->crop); ++ lines = buf->vb.height >> 1; ++ ypadding = buf->vb.width; ++ cpadding = buf->vb.width >> buf->fmt->hshift; ++ bttv_risc_planar(btv,&buf->top, ++ dma->sglist, ++ 0,buf->vb.width,0,lines, ++ uoffset >> 1, ++ voffset >> 1, ++ buf->fmt->hshift, ++ buf->fmt->vshift, ++ 0); ++ bttv_risc_planar(btv,&buf->bottom, ++ dma->sglist, ++ lines * ypadding,buf->vb.width,0,lines, ++ lines * ypadding + (uoffset >> 1), ++ lines * ypadding + (voffset >> 1), ++ buf->fmt->hshift, ++ buf->fmt->vshift, ++ 0); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ ++ /* raw data */ ++ if (buf->fmt->flags & FORMAT_FLAGS_RAW) { ++ /* build risc code */ ++ buf->vb.field = V4L2_FIELD_SEQ_TB; ++ bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, ++ 1,tvnorm,&buf->crop); ++ bttv_risc_packed(btv, &buf->top, dma->sglist, ++ /* offset */ 0, RAW_BPL, /* padding */ 0, ++ /* skip_lines */ 0, RAW_LINES); ++ bttv_risc_packed(btv, &buf->bottom, dma->sglist, ++ buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES); ++ } ++ ++ /* copy format info */ ++ buf->btformat = buf->fmt->btformat; ++ buf->btswap = buf->fmt->btswap; ++ return 0; ++} ++ ++/* ---------------------------------------------------------- */ ++ ++/* calculate geometry, build risc code */ ++int ++bttv_overlay_risc(struct bttv *btv, ++ struct bttv_overlay *ov, ++ const struct bttv_format *fmt, ++ struct bttv_buffer *buf) ++{ ++ /* check interleave, bottom+top fields */ ++ dprintk("%d: overlay fields: %s format: %s size: %dx%d\n", ++ btv->c.nr, v4l2_field_names[buf->vb.field], ++ fmt->name, ov->w.width, ov->w.height); ++ ++ /* calculate geometry */ ++ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, ++ V4L2_FIELD_HAS_BOTH(ov->field), ++ &bttv_tvnorms[ov->tvnorm],&buf->crop); ++ ++ /* build risc code */ ++ switch (ov->field) { ++ case V4L2_FIELD_TOP: ++ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1); ++ bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0); ++ break; ++ default: ++ BUG(); ++ } ++ ++ /* copy format info */ ++ buf->btformat = fmt->btformat; ++ buf->btswap = fmt->btswap; ++ buf->vb.field = ov->field; ++ return 0; ++} ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c +new file mode 100644 +index 0000000..69b66bf +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv-vbi.c +@@ -0,0 +1,460 @@ ++/* ++ ++ bttv - Bt848 frame grabber driver ++ vbi interface ++ ++ (c) 2002 Gerd Knorr ++ ++ Copyright (C) 2005, 2006 Michael H. Schimek ++ Sponsored by OPQ Systems AB ++ ++ 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. ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "bttvp.h" ++ ++/* Offset from line sync pulse leading edge (0H) to start of VBI capture, ++ in fCLKx2 pixels. According to the datasheet, VBI capture starts ++ VBI_HDELAY fCLKx1 pixels from the tailing edgeof /HRESET, and /HRESET ++ is 64 fCLKx1 pixels wide. VBI_HDELAY is set to 0, so this should be ++ (64 + 0) * 2 = 128 fCLKx2 pixels. But it's not! The datasheet is ++ Just Plain Wrong. The real value appears to be different for ++ different revisions of the bt8x8 chips, and to be affected by the ++ horizontal scaling factor. Experimentally, the value is measured ++ to be about 244. */ ++#define VBI_OFFSET 244 ++ ++/* 2048 for compatibility with earlier driver versions. The driver ++ really stores 1024 + tvnorm->vbipack * 4 samples per line in the ++ buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI ++ is 0x1FF DWORDs) and VBI read()s store a frame counter in the last ++ four bytes of the VBI image. */ ++#define VBI_BPL 2048 ++ ++/* Compatibility. */ ++#define VBI_DEFLINES 16 ++ ++static unsigned int vbibufs = 4; ++static unsigned int vbi_debug; ++ ++module_param(vbibufs, int, 0444); ++module_param(vbi_debug, int, 0644); ++MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4"); ++MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)"); ++ ++#ifdef dprintk ++# undef dprintk ++#endif ++#define dprintk(fmt, ...) \ ++do { \ ++ if (vbi_debug) \ ++ pr_debug("%d: " fmt, btv->c.nr, ##__VA_ARGS__); \ ++} while (0) ++ ++#define IMAGE_SIZE(fmt) \ ++ (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line) ++ ++/* ----------------------------------------------------------------------- */ ++/* vbi risc code + mm */ ++ ++static int vbi_buffer_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ struct bttv *btv = fh->btv; ++ ++ if (0 == *count) ++ *count = vbibufs; ++ ++ *size = IMAGE_SIZE(&fh->vbi_fmt.fmt); ++ ++ dprintk("setup: samples=%u start=%d,%d count=%u,%u\n", ++ fh->vbi_fmt.fmt.samples_per_line, ++ fh->vbi_fmt.fmt.start[0], ++ fh->vbi_fmt.fmt.start[1], ++ fh->vbi_fmt.fmt.count[0], ++ fh->vbi_fmt.fmt.count[1]); ++ ++ return 0; ++} ++ ++static int vbi_buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ struct bttv *btv = fh->btv; ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ const struct bttv_tvnorm *tvnorm; ++ unsigned int skip_lines0, skip_lines1, min_vdelay; ++ int redo_dma_risc; ++ int rc; ++ ++ buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt); ++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ ++ tvnorm = fh->vbi_fmt.tvnorm; ++ ++ /* There's no VBI_VDELAY register, RISC must skip the lines ++ we don't want. With default parameters we skip zero lines ++ as earlier driver versions did. The driver permits video ++ standard changes while capturing, so we use vbi_fmt.tvnorm ++ instead of btv->tvnorm to skip zero lines after video ++ standard changes as well. */ ++ ++ skip_lines0 = 0; ++ skip_lines1 = 0; ++ ++ if (fh->vbi_fmt.fmt.count[0] > 0) ++ skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0] ++ - tvnorm->vbistart[0])); ++ if (fh->vbi_fmt.fmt.count[1] > 0) ++ skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1] ++ - tvnorm->vbistart[1])); ++ ++ redo_dma_risc = 0; ++ ++ if (buf->vbi_skip[0] != skip_lines0 || ++ buf->vbi_skip[1] != skip_lines1 || ++ buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] || ++ buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) { ++ buf->vbi_skip[0] = skip_lines0; ++ buf->vbi_skip[1] = skip_lines1; ++ buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0]; ++ buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1]; ++ redo_dma_risc = 1; ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ redo_dma_risc = 1; ++ if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL))) ++ goto fail; ++ } ++ ++ if (redo_dma_risc) { ++ unsigned int bpl, padding, offset; ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ bpl = 2044; /* max. vbipack */ ++ padding = VBI_BPL - bpl; ++ ++ if (fh->vbi_fmt.fmt.count[0] > 0) { ++ rc = bttv_risc_packed(btv, &buf->top, ++ dma->sglist, ++ /* offset */ 0, bpl, ++ padding, skip_lines0, ++ fh->vbi_fmt.fmt.count[0]); ++ if (0 != rc) ++ goto fail; ++ } ++ ++ if (fh->vbi_fmt.fmt.count[1] > 0) { ++ offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL; ++ ++ rc = bttv_risc_packed(btv, &buf->bottom, ++ dma->sglist, ++ offset, bpl, ++ padding, skip_lines1, ++ fh->vbi_fmt.fmt.count[1]); ++ if (0 != rc) ++ goto fail; ++ } ++ } ++ ++ /* VBI capturing ends at VDELAY, start of video capturing, ++ no matter where the RISC program ends. VDELAY minimum is 2, ++ bounds.top is the corresponding first field line number ++ times two. VDELAY counts half field lines. */ ++ min_vdelay = MIN_VDELAY; ++ if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top) ++ min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top; ++ ++ /* For bttv_buffer_activate_vbi(). */ ++ buf->geo.vdelay = min_vdelay; ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ buf->vb.field = field; ++ dprintk("buf prepare %p: top=%p bottom=%p field=%s\n", ++ vb, &buf->top, &buf->bottom, ++ v4l2_field_names[buf->vb.field]); ++ return 0; ++ ++ fail: ++ bttv_dma_free(q,btv,buf); ++ return rc; ++} ++ ++static void ++vbi_buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ struct bttv *btv = fh->btv; ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ ++ dprintk("queue %p\n",vb); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ list_add_tail(&buf->vb.queue,&btv->vcapture); ++ if (NULL == btv->cvbi) { ++ fh->btv->loop_irq |= 4; ++ bttv_set_dma(btv,0x0c); ++ } ++} ++ ++static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct bttv_fh *fh = q->priv_data; ++ struct bttv *btv = fh->btv; ++ struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); ++ ++ dprintk("free %p\n",vb); ++ bttv_dma_free(q,fh->btv,buf); ++} ++ ++struct videobuf_queue_ops bttv_vbi_qops = { ++ .buf_setup = vbi_buffer_setup, ++ .buf_prepare = vbi_buffer_prepare, ++ .buf_queue = vbi_buffer_queue, ++ .buf_release = vbi_buffer_release, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm, ++ __s32 crop_start) ++{ ++ __s32 min_start, max_start, max_end, f2_offset; ++ unsigned int i; ++ ++ /* For compatibility with earlier driver versions we must pretend ++ the VBI and video capture window may overlap. In reality RISC ++ magic aborts VBI capturing at the first line of video capturing, ++ leaving the rest of the buffer unchanged, usually all zero. ++ VBI capturing must always start before video capturing. >> 1 ++ because cropping counts field lines times two. */ ++ min_start = tvnorm->vbistart[0]; ++ max_start = (crop_start >> 1) - 1; ++ max_end = (tvnorm->cropcap.bounds.top ++ + tvnorm->cropcap.bounds.height) >> 1; ++ ++ if (min_start > max_start) ++ return -EBUSY; ++ ++ BUG_ON(max_start >= max_end); ++ ++ f->sampling_rate = tvnorm->Fsc; ++ f->samples_per_line = VBI_BPL; ++ f->sample_format = V4L2_PIX_FMT_GREY; ++ f->offset = VBI_OFFSET; ++ ++ f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0]; ++ ++ for (i = 0; i < 2; ++i) { ++ if (0 == f->count[i]) { ++ /* No data from this field. We leave f->start[i] ++ alone because VIDIOCSVBIFMT is w/o and EINVALs ++ when a driver does not support exactly the ++ requested parameters. */ ++ } else { ++ s64 start, count; ++ ++ start = clamp(f->start[i], min_start, max_start); ++ /* s64 to prevent overflow. */ ++ count = (s64) f->start[i] + f->count[i] - start; ++ f->start[i] = start; ++ f->count[i] = clamp(count, (s64) 1, ++ max_end - start); ++ } ++ ++ min_start += f2_offset; ++ max_start += f2_offset; ++ max_end += f2_offset; ++ } ++ ++ if (0 == (f->count[0] | f->count[1])) { ++ /* As in earlier driver versions. */ ++ f->start[0] = tvnorm->vbistart[0]; ++ f->start[1] = tvnorm->vbistart[1]; ++ f->count[0] = 1; ++ f->count[1] = 1; ++ } ++ ++ f->flags = 0; ++ ++ f->reserved[0] = 0; ++ f->reserved[1] = 0; ++ ++ return 0; ++} ++ ++int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ const struct bttv_tvnorm *tvnorm; ++ __s32 crop_start; ++ ++ mutex_lock(&btv->lock); ++ ++ tvnorm = &bttv_tvnorms[btv->tvnorm]; ++ crop_start = btv->crop_start; ++ ++ mutex_unlock(&btv->lock); ++ ++ return try_fmt(&frt->fmt.vbi, tvnorm, crop_start); ++} ++ ++ ++int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) ++{ ++ struct bttv_fh *fh = f; ++ struct bttv *btv = fh->btv; ++ const struct bttv_tvnorm *tvnorm; ++ __s32 start1, end; ++ int rc; ++ ++ mutex_lock(&btv->lock); ++ ++ rc = -EBUSY; ++ if (fh->resources & RESOURCE_VBI) ++ goto fail; ++ ++ tvnorm = &bttv_tvnorms[btv->tvnorm]; ++ ++ rc = try_fmt(&frt->fmt.vbi, tvnorm, btv->crop_start); ++ if (0 != rc) ++ goto fail; ++ ++ start1 = frt->fmt.vbi.start[1] - tvnorm->vbistart[1] + ++ tvnorm->vbistart[0]; ++ ++ /* First possible line of video capturing. Should be ++ max(f->start[0] + f->count[0], start1 + f->count[1]) * 2 ++ when capturing both fields. But for compatibility we must ++ pretend the VBI and video capture window may overlap, ++ so end = start + 1, the lowest possible value, times two ++ because vbi_fmt.end counts field lines times two. */ ++ end = max(frt->fmt.vbi.start[0], start1) * 2 + 2; ++ ++ mutex_lock(&fh->vbi.vb_lock); ++ ++ fh->vbi_fmt.fmt = frt->fmt.vbi; ++ fh->vbi_fmt.tvnorm = tvnorm; ++ fh->vbi_fmt.end = end; ++ ++ mutex_unlock(&fh->vbi.vb_lock); ++ ++ rc = 0; ++ ++ fail: ++ mutex_unlock(&btv->lock); ++ ++ return rc; ++} ++ ++ ++int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) ++{ ++ struct bttv_fh *fh = f; ++ const struct bttv_tvnorm *tvnorm; ++ ++ frt->fmt.vbi = fh->vbi_fmt.fmt; ++ ++ tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; ++ ++ if (tvnorm != fh->vbi_fmt.tvnorm) { ++ __s32 max_end; ++ unsigned int i; ++ ++ /* As in vbi_buffer_prepare() this imitates the ++ behaviour of earlier driver versions after video ++ standard changes, with default parameters anyway. */ ++ ++ max_end = (tvnorm->cropcap.bounds.top ++ + tvnorm->cropcap.bounds.height) >> 1; ++ ++ frt->fmt.vbi.sampling_rate = tvnorm->Fsc; ++ ++ for (i = 0; i < 2; ++i) { ++ __s32 new_start; ++ ++ new_start = frt->fmt.vbi.start[i] ++ + tvnorm->vbistart[i] ++ - fh->vbi_fmt.tvnorm->vbistart[i]; ++ ++ frt->fmt.vbi.start[i] = min(new_start, max_end - 1); ++ frt->fmt.vbi.count[i] = ++ min((__s32) frt->fmt.vbi.count[i], ++ max_end - frt->fmt.vbi.start[i]); ++ ++ max_end += tvnorm->vbistart[1] ++ - tvnorm->vbistart[0]; ++ } ++ } ++ return 0; ++} ++ ++void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm) ++{ ++ const struct bttv_tvnorm *tvnorm; ++ unsigned int real_samples_per_line; ++ unsigned int real_count; ++ ++ tvnorm = &bttv_tvnorms[norm]; ++ ++ f->fmt.sampling_rate = tvnorm->Fsc; ++ f->fmt.samples_per_line = VBI_BPL; ++ f->fmt.sample_format = V4L2_PIX_FMT_GREY; ++ f->fmt.offset = VBI_OFFSET; ++ f->fmt.start[0] = tvnorm->vbistart[0]; ++ f->fmt.start[1] = tvnorm->vbistart[1]; ++ f->fmt.count[0] = VBI_DEFLINES; ++ f->fmt.count[1] = VBI_DEFLINES; ++ f->fmt.flags = 0; ++ f->fmt.reserved[0] = 0; ++ f->fmt.reserved[1] = 0; ++ ++ /* For compatibility the buffer size must be 2 * VBI_DEFLINES * ++ VBI_BPL regardless of the current video standard. */ ++ real_samples_per_line = 1024 + tvnorm->vbipack * 4; ++ real_count = ((tvnorm->cropcap.defrect.top >> 1) ++ - tvnorm->vbistart[0]); ++ ++ BUG_ON(real_samples_per_line > VBI_BPL); ++ BUG_ON(real_count > VBI_DEFLINES); ++ ++ f->tvnorm = tvnorm; ++ ++ /* See bttv_vbi_fmt_set(). */ ++ f->end = tvnorm->vbistart[0] * 2 + 2; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h +new file mode 100644 +index 0000000..79a1124 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttv.h +@@ -0,0 +1,376 @@ ++/* ++ * ++ * bttv - Bt848 frame grabber driver ++ * ++ * card ID's and external interfaces of the bttv driver ++ * basically stuff needed by other drivers (i2c, lirc, ...) ++ * and is supported not to change much over time. ++ * ++ * Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ * (c) 1999,2000 Gerd Knorr ++ * ++ */ ++ ++#ifndef _BTTV_H_ ++#define _BTTV_H_ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* ---------------------------------------------------------- */ ++/* exported by bttv-cards.c */ ++ ++#define BTTV_BOARD_UNKNOWN 0x00 ++#define BTTV_BOARD_MIRO 0x01 ++#define BTTV_BOARD_HAUPPAUGE 0x02 ++#define BTTV_BOARD_STB 0x03 ++#define BTTV_BOARD_INTEL 0x04 ++#define BTTV_BOARD_DIAMOND 0x05 ++#define BTTV_BOARD_AVERMEDIA 0x06 ++#define BTTV_BOARD_MATRIX_VISION 0x07 ++#define BTTV_BOARD_FLYVIDEO 0x08 ++#define BTTV_BOARD_TURBOTV 0x09 ++#define BTTV_BOARD_HAUPPAUGE878 0x0a ++#define BTTV_BOARD_MIROPRO 0x0b ++#define BTTV_BOARD_ADSTECH_TV 0x0c ++#define BTTV_BOARD_AVERMEDIA98 0x0d ++#define BTTV_BOARD_VHX 0x0e ++#define BTTV_BOARD_ZOLTRIX 0x0f ++#define BTTV_BOARD_PIXVIEWPLAYTV 0x10 ++#define BTTV_BOARD_WINVIEW_601 0x11 ++#define BTTV_BOARD_AVEC_INTERCAP 0x12 ++#define BTTV_BOARD_LIFE_FLYKIT 0x13 ++#define BTTV_BOARD_CEI_RAFFLES 0x14 ++#define BTTV_BOARD_CONFERENCETV 0x15 ++#define BTTV_BOARD_PHOEBE_TVMAS 0x16 ++#define BTTV_BOARD_MODTEC_205 0x17 ++#define BTTV_BOARD_MAGICTVIEW061 0x18 ++#define BTTV_BOARD_VOBIS_BOOSTAR 0x19 ++#define BTTV_BOARD_HAUPPAUG_WCAM 0x1a ++#define BTTV_BOARD_MAXI 0x1b ++#define BTTV_BOARD_TERRATV 0x1c ++#define BTTV_BOARD_PXC200 0x1d ++#define BTTV_BOARD_FLYVIDEO_98 0x1e ++#define BTTV_BOARD_IPROTV 0x1f ++#define BTTV_BOARD_INTEL_C_S_PCI 0x20 ++#define BTTV_BOARD_TERRATVALUE 0x21 ++#define BTTV_BOARD_WINFAST2000 0x22 ++#define BTTV_BOARD_CHRONOS_VS2 0x23 ++#define BTTV_BOARD_TYPHOON_TVIEW 0x24 ++#define BTTV_BOARD_PXELVWPLTVPRO 0x25 ++#define BTTV_BOARD_MAGICTVIEW063 0x26 ++#define BTTV_BOARD_PINNACLE 0x27 ++#define BTTV_BOARD_STB2 0x28 ++#define BTTV_BOARD_AVPHONE98 0x29 ++#define BTTV_BOARD_PV951 0x2a ++#define BTTV_BOARD_ONAIR_TV 0x2b ++#define BTTV_BOARD_SIGMA_TVII_FM 0x2c ++#define BTTV_BOARD_MATRIX_VISION2 0x2d ++#define BTTV_BOARD_ZOLTRIX_GENIE 0x2e ++#define BTTV_BOARD_TERRATVRADIO 0x2f ++#define BTTV_BOARD_DYNALINK 0x30 ++#define BTTV_BOARD_GVBCTV3PCI 0x31 ++#define BTTV_BOARD_PXELVWPLTVPAK 0x32 ++#define BTTV_BOARD_EAGLE 0x33 ++#define BTTV_BOARD_PINNACLEPRO 0x34 ++#define BTTV_BOARD_TVIEW_RDS_FM 0x35 ++#define BTTV_BOARD_LIFETEC_9415 0x36 ++#define BTTV_BOARD_BESTBUY_EASYTV 0x37 ++#define BTTV_BOARD_FLYVIDEO_98FM 0x38 ++#define BTTV_BOARD_GRANDTEC 0x39 ++#define BTTV_BOARD_ASKEY_CPH060 0x3a ++#define BTTV_BOARD_ASKEY_CPH03X 0x3b ++#define BTTV_BOARD_MM100PCTV 0x3c ++#define BTTV_BOARD_GMV1 0x3d ++#define BTTV_BOARD_BESTBUY_EASYTV2 0x3e ++#define BTTV_BOARD_ATI_TVWONDER 0x3f ++#define BTTV_BOARD_ATI_TVWONDERVE 0x40 ++#define BTTV_BOARD_FLYVIDEO2000 0x41 ++#define BTTV_BOARD_TERRATVALUER 0x42 ++#define BTTV_BOARD_GVBCTV4PCI 0x43 ++#define BTTV_BOARD_VOODOOTV_FM 0x44 ++#define BTTV_BOARD_AIMMS 0x45 ++#define BTTV_BOARD_PV_BT878P_PLUS 0x46 ++#define BTTV_BOARD_FLYVIDEO98EZ 0x47 ++#define BTTV_BOARD_PV_BT878P_9B 0x48 ++#define BTTV_BOARD_SENSORAY311_611 0x49 ++#define BTTV_BOARD_RV605 0x4a ++#define BTTV_BOARD_POWERCLR_MTV878 0x4b ++#define BTTV_BOARD_WINDVR 0x4c ++#define BTTV_BOARD_GRANDTEC_MULTI 0x4d ++#define BTTV_BOARD_KWORLD 0x4e ++#define BTTV_BOARD_DSP_TCVIDEO 0x4f ++#define BTTV_BOARD_HAUPPAUGEPVR 0x50 ++#define BTTV_BOARD_GVBCTV5PCI 0x51 ++#define BTTV_BOARD_OSPREY1x0 0x52 ++#define BTTV_BOARD_OSPREY1x0_848 0x53 ++#define BTTV_BOARD_OSPREY101_848 0x54 ++#define BTTV_BOARD_OSPREY1x1 0x55 ++#define BTTV_BOARD_OSPREY1x1_SVID 0x56 ++#define BTTV_BOARD_OSPREY2xx 0x57 ++#define BTTV_BOARD_OSPREY2x0_SVID 0x58 ++#define BTTV_BOARD_OSPREY2x0 0x59 ++#define BTTV_BOARD_OSPREY500 0x5a ++#define BTTV_BOARD_OSPREY540 0x5b ++#define BTTV_BOARD_OSPREY2000 0x5c ++#define BTTV_BOARD_IDS_EAGLE 0x5d ++#define BTTV_BOARD_PINNACLESAT 0x5e ++#define BTTV_BOARD_FORMAC_PROTV 0x5f ++#define BTTV_BOARD_MACHTV 0x60 ++#define BTTV_BOARD_EURESYS_PICOLO 0x61 ++#define BTTV_BOARD_PV150 0x62 ++#define BTTV_BOARD_AD_TVK503 0x63 ++#define BTTV_BOARD_HERCULES_SM_TV 0x64 ++#define BTTV_BOARD_PACETV 0x65 ++#define BTTV_BOARD_IVC200 0x66 ++#define BTTV_BOARD_XGUARD 0x67 ++#define BTTV_BOARD_NEBULA_DIGITV 0x68 ++#define BTTV_BOARD_PV143 0x69 ++#define BTTV_BOARD_VD009X1_VD011_MINIDIN 0x6a ++#define BTTV_BOARD_VD009X1_VD011_COMBI 0x6b ++#define BTTV_BOARD_VD009_MINIDIN 0x6c ++#define BTTV_BOARD_VD009_COMBI 0x6d ++#define BTTV_BOARD_IVC100 0x6e ++#define BTTV_BOARD_IVC120 0x6f ++#define BTTV_BOARD_PC_HDTV 0x70 ++#define BTTV_BOARD_TWINHAN_DST 0x71 ++#define BTTV_BOARD_WINFASTVC100 0x72 ++#define BTTV_BOARD_TEV560 0x73 ++#define BTTV_BOARD_SIMUS_GVC1100 0x74 ++#define BTTV_BOARD_NGSTV_PLUS 0x75 ++#define BTTV_BOARD_LMLBT4 0x76 ++#define BTTV_BOARD_TEKRAM_M205 0x77 ++#define BTTV_BOARD_CONTVFMI 0x78 ++#define BTTV_BOARD_PICOLO_TETRA_CHIP 0x79 ++#define BTTV_BOARD_SPIRIT_TV 0x7a ++#define BTTV_BOARD_AVDVBT_771 0x7b ++#define BTTV_BOARD_AVDVBT_761 0x7c ++#define BTTV_BOARD_MATRIX_VISIONSQ 0x7d ++#define BTTV_BOARD_MATRIX_VISIONSLC 0x7e ++#define BTTV_BOARD_APAC_VIEWCOMP 0x7f ++#define BTTV_BOARD_DVICO_DVBT_LITE 0x80 ++#define BTTV_BOARD_VGEAR_MYVCD 0x81 ++#define BTTV_BOARD_SUPER_TV 0x82 ++#define BTTV_BOARD_TIBET_CS16 0x83 ++#define BTTV_BOARD_KODICOM_4400R 0x84 ++#define BTTV_BOARD_KODICOM_4400R_SL 0x85 ++#define BTTV_BOARD_ADLINK_RTV24 0x86 ++#define BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE 0x87 ++#define BTTV_BOARD_ACORP_Y878F 0x88 ++#define BTTV_BOARD_CONCEPTRONIC_CTVFMI2 0x89 ++#define BTTV_BOARD_PV_BT878P_2E 0x8a ++#define BTTV_BOARD_PV_M4900 0x8b ++#define BTTV_BOARD_OSPREY440 0x8c ++#define BTTV_BOARD_ASOUND_SKYEYE 0x8d ++#define BTTV_BOARD_SABRENT_TVFM 0x8e ++#define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f ++#define BTTV_BOARD_MACHTV_MAGICTV 0x90 ++#define BTTV_BOARD_SSAI_SECURITY 0x91 ++#define BTTV_BOARD_SSAI_ULTRASOUND 0x92 ++#define BTTV_BOARD_VOODOOTV_200 0x93 ++#define BTTV_BOARD_DVICO_FUSIONHDTV_2 0x94 ++#define BTTV_BOARD_TYPHOON_TVTUNERPCI 0x95 ++#define BTTV_BOARD_GEOVISION_GV600 0x96 ++#define BTTV_BOARD_KOZUMI_KTV_01C 0x97 ++#define BTTV_BOARD_ENLTV_FM_2 0x98 ++#define BTTV_BOARD_VD012 0x99 ++#define BTTV_BOARD_VD012_X1 0x9a ++#define BTTV_BOARD_VD012_X2 0x9b ++#define BTTV_BOARD_IVCE8784 0x9c ++#define BTTV_BOARD_GEOVISION_GV800S 0x9d ++#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e ++#define BTTV_BOARD_PV183 0x9f ++#define BTTV_BOARD_TVT_TD3116 0xa0 ++#define BTTV_BOARD_APOSONIC_WDVR 0xa1 ++ ++/* more card-specific defines */ ++#define PT2254_L_CHANNEL 0x10 ++#define PT2254_R_CHANNEL 0x08 ++#define PT2254_DBS_IN_2 0x400 ++#define PT2254_DBS_IN_10 0x20000 ++#define WINVIEW_PT2254_CLK 0x40 ++#define WINVIEW_PT2254_DATA 0x20 ++#define WINVIEW_PT2254_STROBE 0x80 ++ ++struct bttv_core { ++ /* device structs */ ++ struct v4l2_device v4l2_dev; ++ struct pci_dev *pci; ++ struct i2c_adapter i2c_adap; ++ struct list_head subs; /* struct bttv_sub_device */ ++ ++ /* device config */ ++ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */ ++ unsigned int type; /* card type (pointer into tvcards[]) */ ++}; ++ ++struct bttv; ++ ++struct tvcard { ++ char *name; ++ void (*volume_gpio)(struct bttv *btv, __u16 volume); ++ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++ void (*muxsel_hook)(struct bttv *btv, unsigned int input); ++ ++ /* MUX bits for each input, two bits per input starting with the LSB */ ++ u32 muxsel; /* Use MUXSEL() to set */ ++ ++ u32 gpiomask; ++ u32 gpiomux[4]; /* Tuner, Radio, external, internal */ ++ u32 gpiomute; /* GPIO mute setting */ ++ u32 gpiomask2; /* GPIO MUX mask */ ++ ++ unsigned int tuner_type; ++ u8 tuner_addr; ++ u8 video_inputs; /* Number of inputs */ ++ unsigned int svhs:4; /* Which input is s-video */ ++#define NO_SVHS 15 ++ unsigned int pll:2; ++#define PLL_NONE 0 ++#define PLL_28 1 ++#define PLL_35 2 ++ ++ /* i2c audio flags */ ++ unsigned int no_msp34xx:1; ++ unsigned int no_tda7432:1; ++ unsigned int msp34xx_alt:1; ++ /* Note: currently no card definition needs to mark the presence ++ of a RDS saa6588 chip. If this is ever needed, then add a new ++ 'has_saa6588' bit here. */ ++ ++ unsigned int no_video:1; /* video pci function is unused */ ++ unsigned int has_dvb:1; ++ unsigned int has_remote:1; ++ unsigned int has_radio:1; ++ unsigned int has_dig_in:1; /* Has digital input (always last input) */ ++ unsigned int no_gpioirq:1; ++}; ++ ++extern struct tvcard bttv_tvcards[]; ++ ++/* ++ * This bit of cpp voodoo is used to create a macro with a variable number of ++ * arguments (1 to 16). It will pack each argument into a word two bits at a ++ * time. It can't be a function because it needs to be compile time constant to ++ * initialize structures. Since each argument must fit in two bits, it's ok ++ * that they are changed to octal. One should not use hex number, macros, or ++ * anything else with this macro. Just use plain integers from 0 to 3. ++ */ ++#define _MUXSELf(a) 0##a << 30 ++#define _MUXSELe(a, b...) 0##a << 28 | _MUXSELf(b) ++#define _MUXSELd(a, b...) 0##a << 26 | _MUXSELe(b) ++#define _MUXSELc(a, b...) 0##a << 24 | _MUXSELd(b) ++#define _MUXSELb(a, b...) 0##a << 22 | _MUXSELc(b) ++#define _MUXSELa(a, b...) 0##a << 20 | _MUXSELb(b) ++#define _MUXSEL9(a, b...) 0##a << 18 | _MUXSELa(b) ++#define _MUXSEL8(a, b...) 0##a << 16 | _MUXSEL9(b) ++#define _MUXSEL7(a, b...) 0##a << 14 | _MUXSEL8(b) ++#define _MUXSEL6(a, b...) 0##a << 12 | _MUXSEL7(b) ++#define _MUXSEL5(a, b...) 0##a << 10 | _MUXSEL6(b) ++#define _MUXSEL4(a, b...) 0##a << 8 | _MUXSEL5(b) ++#define _MUXSEL3(a, b...) 0##a << 6 | _MUXSEL4(b) ++#define _MUXSEL2(a, b...) 0##a << 4 | _MUXSEL3(b) ++#define _MUXSEL1(a, b...) 0##a << 2 | _MUXSEL2(b) ++#define MUXSEL(a, b...) (a | _MUXSEL1(b)) ++ ++/* identification / initialization of the card */ ++extern void bttv_idcard(struct bttv *btv); ++extern void bttv_init_card1(struct bttv *btv); ++extern void bttv_init_card2(struct bttv *btv); ++extern void bttv_init_tuner(struct bttv *btv); ++ ++/* card-specific funtions */ ++extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); ++extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits); ++ ++/* extra tweaks for some chipsets */ ++extern void bttv_check_chipset(void); ++extern int bttv_handle_chipset(struct bttv *btv); ++ ++/* ---------------------------------------------------------- */ ++/* exported by bttv-if.c */ ++ ++/* this obsolete -- please use the sysfs-based ++ interface below for new code */ ++ ++extern struct pci_dev* bttv_get_pcidev(unsigned int card); ++ ++/* sets GPOE register (BT848_GPIO_OUT_EN) to new value: ++ data | (current_GPOE_value & ~mask) ++ returns negative value if error occurred ++*/ ++extern int bttv_gpio_enable(unsigned int card, ++ unsigned long mask, unsigned long data); ++ ++/* fills data with GPDATA register contents ++ returns negative value if error occurred ++*/ ++extern int bttv_read_gpio(unsigned int card, unsigned long *data); ++ ++/* sets GPDATA register to new value: ++ (data & mask) | (current_GPDATA_value & ~mask) ++ returns negative value if error occurred ++*/ ++extern int bttv_write_gpio(unsigned int card, ++ unsigned long mask, unsigned long data); ++ ++ ++ ++ ++/* ---------------------------------------------------------- */ ++/* sysfs/driver-moded based gpio access interface */ ++ ++struct bttv_sub_device { ++ struct device dev; ++ struct bttv_core *core; ++ struct list_head list; ++}; ++#define to_bttv_sub_dev(x) container_of((x), struct bttv_sub_device, dev) ++ ++struct bttv_sub_driver { ++ struct device_driver drv; ++ char wanted[20]; ++ int (*probe)(struct bttv_sub_device *sub); ++ void (*remove)(struct bttv_sub_device *sub); ++}; ++#define to_bttv_sub_drv(x) container_of((x), struct bttv_sub_driver, drv) ++ ++int bttv_sub_register(struct bttv_sub_driver *drv, char *wanted); ++int bttv_sub_unregister(struct bttv_sub_driver *drv); ++ ++/* gpio access functions */ ++void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits); ++u32 bttv_gpio_read(struct bttv_core *core); ++void bttv_gpio_write(struct bttv_core *core, u32 value); ++void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits); ++ ++#define gpio_inout(mask,bits) bttv_gpio_inout(&btv->c, mask, bits) ++#define gpio_read() bttv_gpio_read(&btv->c) ++#define gpio_write(value) bttv_gpio_write(&btv->c, value) ++#define gpio_bits(mask,bits) bttv_gpio_bits(&btv->c, mask, bits) ++ ++ ++/* ---------------------------------------------------------- */ ++/* i2c */ ++ ++#define bttv_call_all(btv, o, f, args...) \ ++ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args) ++ ++extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); ++extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, ++ unsigned char b2, int both); ++extern void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr); ++ ++extern int bttv_input_init(struct bttv *dev); ++extern void bttv_input_fini(struct bttv *dev); ++extern void bttv_input_irq(struct bttv *dev); ++ ++#endif /* _BTTV_H_ */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h +new file mode 100644 +index 0000000..9ec0adb +--- /dev/null ++++ b/drivers/media/pci/bt8xx/bttvp.h +@@ -0,0 +1,536 @@ ++/* ++ ++ bttv - Bt848 frame grabber driver ++ ++ bttv's *private* header file -- nobody other than bttv itself ++ should ever include this file. ++ ++ (c) 2000-2002 Gerd Knorr ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef _BTTVP_H_ ++#define _BTTVP_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bt848.h" ++#include "bttv.h" ++#include "btcx-risc.h" ++ ++#ifdef __KERNEL__ ++ ++#define FORMAT_FLAGS_DITHER 0x01 ++#define FORMAT_FLAGS_PACKED 0x02 ++#define FORMAT_FLAGS_PLANAR 0x04 ++#define FORMAT_FLAGS_RAW 0x08 ++#define FORMAT_FLAGS_CrCb 0x10 ++ ++#define RISC_SLOT_O_VBI 4 ++#define RISC_SLOT_O_FIELD 6 ++#define RISC_SLOT_E_VBI 10 ++#define RISC_SLOT_E_FIELD 12 ++#define RISC_SLOT_LOOP 14 ++ ++#define RESOURCE_OVERLAY 1 ++#define RESOURCE_VIDEO_STREAM 2 ++#define RESOURCE_VBI 4 ++#define RESOURCE_VIDEO_READ 8 ++ ++#define RAW_LINES 640 ++#define RAW_BPL 1024 ++ ++#define UNSET (-1U) ++ ++/* Min. value in VDELAY register. */ ++#define MIN_VDELAY 2 ++/* Even to get Cb first, odd for Cr. */ ++#define MAX_HDELAY (0x3FF & -2) ++/* Limits scaled width, which must be a multiple of 4. */ ++#define MAX_HACTIVE (0x3FF & -4) ++ ++#define BTTV_NORMS (\ ++ V4L2_STD_PAL | V4L2_STD_PAL_N | \ ++ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ ++ V4L2_STD_NTSC | V4L2_STD_PAL_M | \ ++ V4L2_STD_PAL_60) ++/* ---------------------------------------------------------- */ ++ ++struct bttv_tvnorm { ++ int v4l2_id; ++ char *name; ++ u32 Fsc; ++ u16 swidth, sheight; /* scaled standard width, height */ ++ u16 totalwidth; ++ u8 adelay, bdelay, iform; ++ u32 scaledtwidth; ++ u16 hdelayx1, hactivex1; ++ u16 vdelay; ++ u8 vbipack; ++ u16 vtotal; ++ int sram; ++ /* ITU-R frame line number of the first VBI line we can ++ capture, of the first and second field. The last possible line ++ is determined by cropcap.bounds. */ ++ u16 vbistart[2]; ++ /* Horizontally this counts fCLKx1 samples following the leading ++ edge of the horizontal sync pulse, vertically ITU-R frame line ++ numbers of the first field times two (2, 4, 6, ... 524 or 624). */ ++ struct v4l2_cropcap cropcap; ++}; ++extern const struct bttv_tvnorm bttv_tvnorms[]; ++ ++struct bttv_format { ++ char *name; ++ int fourcc; /* video4linux 2 */ ++ int btformat; /* BT848_COLOR_FMT_* */ ++ int btswap; /* BT848_COLOR_CTL_* */ ++ int depth; /* bit/pixel */ ++ int flags; ++ int hshift,vshift; /* for planar modes */ ++}; ++ ++struct bttv_ir { ++ struct rc_dev *dev; ++ struct timer_list timer; ++ ++ char name[32]; ++ char phys[32]; ++ ++ /* Usual gpio signalling */ ++ u32 mask_keycode; ++ u32 mask_keydown; ++ u32 mask_keyup; ++ u32 polling; ++ u32 last_gpio; ++ int shift_by; ++ int start; // What should RC5_START() be ++ int addr; // What RC5_ADDR() should be. ++ int rc5_remote_gap; ++ ++ /* RC5 gpio */ ++ bool rc5_gpio; /* Is RC5 legacy GPIO enabled? */ ++ u32 last_bit; /* last raw bit seen */ ++ u32 code; /* raw code under construction */ ++ struct timeval base_time; /* time of last seen code */ ++ bool active; /* building raw code */ ++}; ++ ++ ++/* ---------------------------------------------------------- */ ++ ++struct bttv_geometry { ++ u8 vtc,crop,comb; ++ u16 width,hscale,hdelay; ++ u16 sheight,vscale,vdelay,vtotal; ++}; ++ ++struct bttv_buffer { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* bttv specific */ ++ const struct bttv_format *fmt; ++ unsigned int tvnorm; ++ int btformat; ++ int btswap; ++ struct bttv_geometry geo; ++ struct btcx_riscmem top; ++ struct btcx_riscmem bottom; ++ struct v4l2_rect crop; ++ unsigned int vbi_skip[2]; ++ unsigned int vbi_count[2]; ++}; ++ ++struct bttv_buffer_set { ++ struct bttv_buffer *top; /* top field buffer */ ++ struct bttv_buffer *bottom; /* bottom field buffer */ ++ unsigned int top_irq; ++ unsigned int frame_irq; ++}; ++ ++struct bttv_overlay { ++ unsigned int tvnorm; ++ struct v4l2_rect w; ++ enum v4l2_field field; ++ struct v4l2_clip *clips; ++ int nclips; ++ int setup_ok; ++}; ++ ++struct bttv_vbi_fmt { ++ struct v4l2_vbi_format fmt; ++ ++ /* fmt.start[] and count[] refer to this video standard. */ ++ const struct bttv_tvnorm *tvnorm; ++ ++ /* Earliest possible start of video capturing with this ++ v4l2_vbi_format, in struct bttv_crop.rect units. */ ++ __s32 end; ++}; ++ ++/* bttv-vbi.c */ ++void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm); ++ ++struct bttv_crop { ++ /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */ ++ struct v4l2_rect rect; ++ ++ /* Scaled image size limits with this crop rect. Divide ++ max_height, but not min_height, by two when capturing ++ single fields. See also bttv_crop_reset() and ++ bttv_crop_adjust() in bttv-driver.c. */ ++ __s32 min_scaled_width; ++ __s32 min_scaled_height; ++ __s32 max_scaled_width; ++ __s32 max_scaled_height; ++}; ++ ++struct bttv_fh { ++ struct bttv *btv; ++ int resources; ++#ifdef VIDIOC_G_PRIORITY ++ enum v4l2_priority prio; ++#endif ++ enum v4l2_buf_type type; ++ ++ /* video capture */ ++ struct videobuf_queue cap; ++ const struct bttv_format *fmt; ++ int width; ++ int height; ++ ++ /* video overlay */ ++ const struct bttv_format *ovfmt; ++ struct bttv_overlay ov; ++ ++ /* Application called VIDIOC_S_CROP. */ ++ int do_crop; ++ ++ /* vbi capture */ ++ struct videobuf_queue vbi; ++ /* Current VBI capture window as seen through this fh (cannot ++ be global for compatibility with earlier drivers). Protected ++ by struct bttv.lock and struct bttv_fh.vbi.lock. */ ++ struct bttv_vbi_fmt vbi_fmt; ++}; ++ ++/* ---------------------------------------------------------- */ ++/* bttv-risc.c */ ++ ++/* risc code generators - capture */ ++int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int offset, unsigned int bpl, ++ unsigned int pitch, unsigned int skip_lines, ++ unsigned int store_lines); ++ ++/* control dma register + risc main loop */ ++void bttv_set_dma(struct bttv *btv, int override); ++int bttv_risc_init_main(struct bttv *btv); ++int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, ++ int irqflags); ++ ++/* capture buffer handling */ ++int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf); ++int bttv_buffer_activate_video(struct bttv *btv, ++ struct bttv_buffer_set *set); ++int bttv_buffer_activate_vbi(struct bttv *btv, ++ struct bttv_buffer *vbi); ++void bttv_dma_free(struct videobuf_queue *q, struct bttv *btv, ++ struct bttv_buffer *buf); ++ ++/* overlay handling */ ++int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, ++ const struct bttv_format *fmt, ++ struct bttv_buffer *buf); ++ ++ ++/* ---------------------------------------------------------- */ ++/* bttv-vbi.c */ ++ ++int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); ++int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); ++int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f); ++ ++extern struct videobuf_queue_ops bttv_vbi_qops; ++ ++/* ---------------------------------------------------------- */ ++/* bttv-gpio.c */ ++ ++extern struct bus_type bttv_sub_bus_type; ++int bttv_sub_add_device(struct bttv_core *core, char *name); ++int bttv_sub_del_devices(struct bttv_core *core); ++ ++/* ---------------------------------------------------------- */ ++/* bttv-cards.c */ ++ ++extern int no_overlay; ++ ++/* ---------------------------------------------------------- */ ++/* bttv-input.c */ ++ ++extern void init_bttv_i2c_ir(struct bttv *btv); ++extern int fini_bttv_i2c(struct bttv *btv); ++ ++/* ---------------------------------------------------------- */ ++/* bttv-driver.c */ ++ ++/* insmod options */ ++extern unsigned int bttv_verbose; ++extern unsigned int bttv_debug; ++extern unsigned int bttv_gpio; ++extern void bttv_gpio_tracking(struct bttv *btv, char *comment); ++extern int init_bttv_i2c(struct bttv *btv); ++ ++#define dprintk(fmt, ...) \ ++do { \ ++ if (bttv_debug >= 1) \ ++ pr_debug(fmt, ##__VA_ARGS__); \ ++} while (0) ++#define dprintk_cont(fmt, ...) \ ++do { \ ++ if (bttv_debug >= 1) \ ++ pr_cont(fmt, ##__VA_ARGS__); \ ++} while (0) ++#define d2printk(fmt, ...) \ ++do { \ ++ if (bttv_debug >= 2) \ ++ printk(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define BTTV_MAX_FBUF 0x208000 ++#define BTTV_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ ++#define BTTV_FREE_IDLE msecs_to_jiffies(1000) /* one second */ ++ ++ ++struct bttv_pll_info { ++ unsigned int pll_ifreq; /* PLL input frequency */ ++ unsigned int pll_ofreq; /* PLL output frequency */ ++ unsigned int pll_crystal; /* Crystal used for input */ ++ unsigned int pll_current; /* Currently programmed ofreq */ ++}; ++ ++/* for gpio-connected remote control */ ++struct bttv_input { ++ struct input_dev *dev; ++ char name[32]; ++ char phys[32]; ++ u32 mask_keycode; ++ u32 mask_keydown; ++}; ++ ++struct bttv_suspend_state { ++ u32 gpio_enable; ++ u32 gpio_data; ++ int disabled; ++ int loop_irq; ++ struct bttv_buffer_set video; ++ struct bttv_buffer *vbi; ++}; ++ ++struct bttv { ++ struct bttv_core c; ++ ++ /* pci device config */ ++ unsigned short id; ++ unsigned char revision; ++ unsigned char __iomem *bt848_mmio; /* pointer to mmio */ ++ ++ /* card configuration info */ ++ unsigned int cardid; /* pci subsystem id (bt878 based ones) */ ++ unsigned int tuner_type; /* tuner chip type */ ++ unsigned int tda9887_conf; ++ unsigned int svhs, dig; ++ unsigned int has_saa6588:1; ++ struct bttv_pll_info pll; ++ int triton1; ++ int gpioirq; ++ ++ int use_i2c_hw; ++ ++ /* old gpio interface */ ++ int shutdown; ++ ++ void (*volume_gpio)(struct bttv *btv, __u16 volume); ++ void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set); ++ ++ /* new gpio interface */ ++ spinlock_t gpio_lock; ++ ++ /* i2c layer */ ++ struct i2c_algo_bit_data i2c_algo; ++ struct i2c_client i2c_client; ++ int i2c_state, i2c_rc; ++ int i2c_done; ++ wait_queue_head_t i2c_queue; ++ struct v4l2_subdev *sd_msp34xx; ++ struct v4l2_subdev *sd_tvaudio; ++ ++ /* video4linux (1) */ ++ struct video_device *video_dev; ++ struct video_device *radio_dev; ++ struct video_device *vbi_dev; ++ ++ /* infrared remote */ ++ int has_remote; ++ struct bttv_ir *remote; ++ ++ /* I2C remote data */ ++ struct IR_i2c_init_data init_data; ++ ++ /* locking */ ++ spinlock_t s_lock; ++ struct mutex lock; ++ int resources; ++#ifdef VIDIOC_G_PRIORITY ++ struct v4l2_prio_state prio; ++#endif ++ ++ /* video state */ ++ unsigned int input; ++ unsigned int audio; ++ unsigned int mute; ++ unsigned long freq; ++ unsigned int tvnorm; ++ int hue, contrast, bright, saturation; ++ struct v4l2_framebuffer fbuf; ++ unsigned int field_count; ++ ++ /* various options */ ++ int opt_combfilter; ++ int opt_lumafilter; ++ int opt_automute; ++ int opt_chroma_agc; ++ int opt_color_killer; ++ int opt_adc_crush; ++ int opt_vcr_hack; ++ int opt_whitecrush_upper; ++ int opt_whitecrush_lower; ++ int opt_uv_ratio; ++ int opt_full_luma_range; ++ int opt_coring; ++ ++ /* radio data/state */ ++ int has_radio; ++ int radio_user; ++ int radio_uses_msp_demodulator; ++ ++ /* miro/pinnacle + Aimslab VHX ++ philips matchbox (tea5757 radio tuner) support */ ++ int has_matchbox; ++ int mbox_we; ++ int mbox_data; ++ int mbox_clk; ++ int mbox_most; ++ int mbox_mask; ++ ++ /* ISA stuff (Terratec Active Radio Upgrade) */ ++ int mbox_ior; ++ int mbox_iow; ++ int mbox_csel; ++ ++ /* risc memory management data ++ - must acquire s_lock before changing these ++ - only the irq handler is supported to touch top + bottom + vcurr */ ++ struct btcx_riscmem main; ++ struct bttv_buffer *screen; /* overlay */ ++ struct list_head capture; /* video capture queue */ ++ struct list_head vcapture; /* vbi capture queue */ ++ struct bttv_buffer_set curr; /* active buffers */ ++ struct bttv_buffer *cvbi; /* active vbi buffer */ ++ int loop_irq; ++ int new_input; ++ ++ unsigned long cap_ctl; ++ unsigned long dma_on; ++ struct timer_list timeout; ++ struct bttv_suspend_state state; ++ ++ /* stats */ ++ unsigned int errors; ++ unsigned int framedrop; ++ unsigned int irq_total; ++ unsigned int irq_me; ++ ++ unsigned int users; ++ struct bttv_fh init; ++ ++ /* used to make dvb-bt8xx autoloadable */ ++ struct work_struct request_module_wk; ++ ++ /* Default (0) and current (1) video capturing and overlay ++ cropping parameters in bttv_tvnorm.cropcap units. Protected ++ by bttv.lock. */ ++ struct bttv_crop crop[2]; ++ ++ /* Earliest possible start of video capturing in ++ bttv_tvnorm.cropcap line units. Set by check_alloc_btres() ++ and free_btres(). Protected by bttv.lock. */ ++ __s32 vbi_end; ++ ++ /* Latest possible end of VBI capturing (= crop[x].rect.top when ++ VIDEO_RESOURCES are locked). Set by check_alloc_btres() ++ and free_btres(). Protected by bttv.lock. */ ++ __s32 crop_start; ++}; ++ ++static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct bttv, c.v4l2_dev); ++} ++ ++/* our devices */ ++#define BTTV_MAX 32 ++extern unsigned int bttv_num; ++extern struct bttv *bttvs[BTTV_MAX]; ++ ++static inline unsigned int bttv_muxsel(const struct bttv *btv, ++ unsigned int input) ++{ ++ return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3; ++} ++ ++#endif ++ ++#define btwrite(dat,adr) writel((dat), btv->bt848_mmio+(adr)) ++#define btread(adr) readl(btv->bt848_mmio+(adr)) ++ ++#define btand(dat,adr) btwrite((dat) & btread(adr), adr) ++#define btor(dat,adr) btwrite((dat) | btread(adr), adr) ++#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) ++ ++#endif /* _BTTVP_H_ */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c +new file mode 100644 +index 0000000..430b3eb +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dst.c +@@ -0,0 +1,1873 @@ ++/* ++ Frontend/Card driver for TwinHan DST Frontend ++ Copyright (C) 2003 Jamie Honan ++ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "dst_priv.h" ++#include "dst_common.h" ++ ++static unsigned int verbose = 1; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); ++ ++static unsigned int dst_addons; ++module_param(dst_addons, int, 0644); ++MODULE_PARM_DESC(dst_addons, "CA daughterboard, default is 0 (No addons)"); ++ ++static unsigned int dst_algo; ++module_param(dst_algo, int, 0644); ++MODULE_PARM_DESC(dst_algo, "tuning algo: default is 0=(SW), 1=(HW)"); ++ ++#define HAS_LOCK 1 ++#define ATTEMPT_TUNE 2 ++#define HAS_POWER 4 ++ ++#define DST_ERROR 0 ++#define DST_NOTICE 1 ++#define DST_INFO 2 ++#define DST_DEBUG 3 ++ ++#define dprintk(x, y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((x > DST_ERROR) && (x > y)) \ ++ printk(KERN_ERR "dst(%d) %s: " format "\n", \ ++ state->bt->nr, __func__ , ##arg); \ ++ else if ((x > DST_NOTICE) && (x > y)) \ ++ printk(KERN_NOTICE "dst(%d) %s: " format "\n", \ ++ state->bt->nr, __func__ , ##arg); \ ++ else if ((x > DST_INFO) && (x > y)) \ ++ printk(KERN_INFO "dst(%d) %s: " format "\n", \ ++ state->bt->nr, __func__ , ##arg); \ ++ else if ((x > DST_DEBUG) && (x > y)) \ ++ printk(KERN_DEBUG "dst(%d) %s: " format "\n", \ ++ state->bt->nr, __func__ , ##arg); \ ++ } else { \ ++ if (x > y) \ ++ printk(format, ##arg); \ ++ } \ ++} while(0) ++ ++static int dst_command(struct dst_state *state, u8 *data, u8 len); ++ ++static void dst_packsize(struct dst_state *state, int psize) ++{ ++ union dst_gpio_packet bits; ++ ++ bits.psize = psize; ++ bt878_device_control(state->bt, DST_IG_TS, &bits); ++} ++ ++static int dst_gpio_outb(struct dst_state *state, u32 mask, u32 enbb, ++ u32 outhigh, int delay) ++{ ++ union dst_gpio_packet enb; ++ union dst_gpio_packet bits; ++ int err; ++ ++ enb.enb.mask = mask; ++ enb.enb.enable = enbb; ++ ++ dprintk(verbose, DST_INFO, 1, "mask=[%04x], enbb=[%04x], outhigh=[%04x]", mask, enbb, outhigh); ++ if ((err = bt878_device_control(state->bt, DST_IG_ENABLE, &enb)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "dst_gpio_enb error (err == %i, mask == %02x, enb == %02x)", err, mask, enbb); ++ return -EREMOTEIO; ++ } ++ udelay(1000); ++ /* because complete disabling means no output, no need to do output packet */ ++ if (enbb == 0) ++ return 0; ++ if (delay) ++ msleep(10); ++ bits.outp.mask = enbb; ++ bits.outp.highvals = outhigh; ++ if ((err = bt878_device_control(state->bt, DST_IG_WRITE, &bits)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "dst_gpio_outb error (err == %i, enbb == %02x, outhigh == %02x)", err, enbb, outhigh); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int dst_gpio_inb(struct dst_state *state, u8 *result) ++{ ++ union dst_gpio_packet rd_packet; ++ int err; ++ ++ *result = 0; ++ if ((err = bt878_device_control(state->bt, DST_IG_READ, &rd_packet)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb error (err == %i)", err); ++ return -EREMOTEIO; ++ } ++ *result = (u8) rd_packet.rd.value; ++ ++ return 0; ++} ++ ++int rdc_reset_state(struct dst_state *state) ++{ ++ dprintk(verbose, DST_INFO, 1, "Resetting state machine"); ++ if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, 0, NO_DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ return -1; ++ } ++ msleep(10); ++ if (dst_gpio_outb(state, RDC_8820_INT, RDC_8820_INT, RDC_8820_INT, NO_DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ msleep(10); ++ return -1; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(rdc_reset_state); ++ ++static int rdc_8820_reset(struct dst_state *state) ++{ ++ dprintk(verbose, DST_DEBUG, 1, "Resetting DST"); ++ if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, 0, NO_DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ return -1; ++ } ++ udelay(1000); ++ if (dst_gpio_outb(state, RDC_8820_RESET, RDC_8820_RESET, RDC_8820_RESET, DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int dst_pio_enable(struct dst_state *state) ++{ ++ if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_ENABLE, 0, NO_DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ return -1; ++ } ++ udelay(1000); ++ ++ return 0; ++} ++ ++int dst_pio_disable(struct dst_state *state) ++{ ++ if (dst_gpio_outb(state, ~0, RDC_8820_PIO_0_DISABLE, RDC_8820_PIO_0_DISABLE, NO_DELAY) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_outb ERROR !"); ++ return -1; ++ } ++ if (state->type_flags & DST_TYPE_HAS_FW_1) ++ udelay(1000); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dst_pio_disable); ++ ++int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode) ++{ ++ u8 reply; ++ int i; ++ ++ for (i = 0; i < 200; i++) { ++ if (dst_gpio_inb(state, &reply) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "dst_gpio_inb ERROR !"); ++ return -1; ++ } ++ if ((reply & RDC_8820_PIO_0_ENABLE) == 0) { ++ dprintk(verbose, DST_INFO, 1, "dst wait ready after %d", i); ++ return 1; ++ } ++ msleep(10); ++ } ++ dprintk(verbose, DST_NOTICE, 1, "dst wait NOT ready after %d", i); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dst_wait_dst_ready); ++ ++int dst_error_recovery(struct dst_state *state) ++{ ++ dprintk(verbose, DST_NOTICE, 1, "Trying to return from previous errors."); ++ dst_pio_disable(state); ++ msleep(10); ++ dst_pio_enable(state); ++ msleep(10); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dst_error_recovery); ++ ++int dst_error_bailout(struct dst_state *state) ++{ ++ dprintk(verbose, DST_INFO, 1, "Trying to bailout from previous error."); ++ rdc_8820_reset(state); ++ dst_pio_disable(state); ++ msleep(10); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dst_error_bailout); ++ ++int dst_comm_init(struct dst_state *state) ++{ ++ dprintk(verbose, DST_INFO, 1, "Initializing DST."); ++ if ((dst_pio_enable(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "PIO Enable Failed"); ++ return -1; ++ } ++ if ((rdc_reset_state(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "RDC 8820 State RESET Failed."); ++ return -1; ++ } ++ if (state->type_flags & DST_TYPE_HAS_FW_1) ++ msleep(100); ++ else ++ msleep(5); ++ ++ return 0; ++} ++EXPORT_SYMBOL(dst_comm_init); ++ ++int write_dst(struct dst_state *state, u8 *data, u8 len) ++{ ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = 0, ++ .buf = data, ++ .len = len ++ }; ++ ++ int err; ++ u8 cnt, i; ++ ++ dprintk(verbose, DST_NOTICE, 0, "writing [ "); ++ for (i = 0; i < len; i++) ++ dprintk(verbose, DST_NOTICE, 0, "%02x ", data[i]); ++ dprintk(verbose, DST_NOTICE, 0, "]\n"); ++ ++ for (cnt = 0; cnt < 2; cnt++) { ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "_write_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, data[0]); ++ dst_error_recovery(state); ++ continue; ++ } else ++ break; ++ } ++ if (cnt >= 2) { ++ dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); ++ dst_error_bailout(state); ++ ++ return -1; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(write_dst); ++ ++int read_dst(struct dst_state *state, u8 *ret, u8 len) ++{ ++ struct i2c_msg msg = { ++ .addr = state->config->demod_address, ++ .flags = I2C_M_RD, ++ .buf = ret, ++ .len = len ++ }; ++ ++ int err; ++ int cnt; ++ ++ for (cnt = 0; cnt < 2; cnt++) { ++ if ((err = i2c_transfer(state->i2c, &msg, 1)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "read_dst error (err == %i, len == 0x%02x, b0 == 0x%02x)", err, len, ret[0]); ++ dst_error_recovery(state); ++ continue; ++ } else ++ break; ++ } ++ if (cnt >= 2) { ++ dprintk(verbose, DST_INFO, 1, "RDC 8820 RESET"); ++ dst_error_bailout(state); ++ ++ return -1; ++ } ++ dprintk(verbose, DST_DEBUG, 1, "reply is 0x%x", ret[0]); ++ for (err = 1; err < len; err++) ++ dprintk(verbose, DST_DEBUG, 0, " 0x%x", ret[err]); ++ if (err > 1) ++ dprintk(verbose, DST_DEBUG, 0, "\n"); ++ ++ return 0; ++} ++EXPORT_SYMBOL(read_dst); ++ ++static int dst_set_polarization(struct dst_state *state) ++{ ++ switch (state->voltage) { ++ case SEC_VOLTAGE_13: /* Vertical */ ++ dprintk(verbose, DST_INFO, 1, "Polarization=[Vertical]"); ++ state->tx_tuna[8] &= ~0x40; ++ break; ++ case SEC_VOLTAGE_18: /* Horizontal */ ++ dprintk(verbose, DST_INFO, 1, "Polarization=[Horizontal]"); ++ state->tx_tuna[8] |= 0x40; ++ break; ++ case SEC_VOLTAGE_OFF: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int dst_set_freq(struct dst_state *state, u32 freq) ++{ ++ state->frequency = freq; ++ dprintk(verbose, DST_INFO, 1, "set Frequency %u", freq); ++ ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ freq = freq / 1000; ++ if (freq < 950 || freq > 2150) ++ return -EINVAL; ++ state->tx_tuna[2] = (freq >> 8); ++ state->tx_tuna[3] = (u8) freq; ++ state->tx_tuna[4] = 0x01; ++ state->tx_tuna[8] &= ~0x04; ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { ++ if (freq < 1531) ++ state->tx_tuna[8] |= 0x04; ++ } ++ } else if (state->dst_type == DST_TYPE_IS_TERR) { ++ freq = freq / 1000; ++ if (freq < 137000 || freq > 858000) ++ return -EINVAL; ++ state->tx_tuna[2] = (freq >> 16) & 0xff; ++ state->tx_tuna[3] = (freq >> 8) & 0xff; ++ state->tx_tuna[4] = (u8) freq; ++ } else if (state->dst_type == DST_TYPE_IS_CABLE) { ++ freq = freq / 1000; ++ state->tx_tuna[2] = (freq >> 16) & 0xff; ++ state->tx_tuna[3] = (freq >> 8) & 0xff; ++ state->tx_tuna[4] = (u8) freq; ++ } else if (state->dst_type == DST_TYPE_IS_ATSC) { ++ freq = freq / 1000; ++ if (freq < 51000 || freq > 858000) ++ return -EINVAL; ++ state->tx_tuna[2] = (freq >> 16) & 0xff; ++ state->tx_tuna[3] = (freq >> 8) & 0xff; ++ state->tx_tuna[4] = (u8) freq; ++ state->tx_tuna[5] = 0x00; /* ATSC */ ++ state->tx_tuna[6] = 0x00; ++ if (state->dst_hw_cap & DST_TYPE_HAS_ANALOG) ++ state->tx_tuna[7] = 0x00; /* Digital */ ++ } else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int dst_set_bandwidth(struct dst_state *state, u32 bandwidth) ++{ ++ state->bandwidth = bandwidth; ++ ++ if (state->dst_type != DST_TYPE_IS_TERR) ++ return -EOPNOTSUPP; ++ ++ switch (bandwidth) { ++ case 6000000: ++ if (state->dst_hw_cap & DST_TYPE_HAS_CA) ++ state->tx_tuna[7] = 0x06; ++ else { ++ state->tx_tuna[6] = 0x06; ++ state->tx_tuna[7] = 0x00; ++ } ++ break; ++ case 7000000: ++ if (state->dst_hw_cap & DST_TYPE_HAS_CA) ++ state->tx_tuna[7] = 0x07; ++ else { ++ state->tx_tuna[6] = 0x07; ++ state->tx_tuna[7] = 0x00; ++ } ++ break; ++ case 8000000: ++ if (state->dst_hw_cap & DST_TYPE_HAS_CA) ++ state->tx_tuna[7] = 0x08; ++ else { ++ state->tx_tuna[6] = 0x08; ++ state->tx_tuna[7] = 0x00; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int dst_set_inversion(struct dst_state *state, fe_spectral_inversion_t inversion) ++{ ++ state->inversion = inversion; ++ switch (inversion) { ++ case INVERSION_OFF: /* Inversion = Normal */ ++ state->tx_tuna[8] &= ~0x80; ++ break; ++ case INVERSION_ON: ++ state->tx_tuna[8] |= 0x80; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int dst_set_fec(struct dst_state *state, fe_code_rate_t fec) ++{ ++ state->fec = fec; ++ return 0; ++} ++ ++static fe_code_rate_t dst_get_fec(struct dst_state *state) ++{ ++ return state->fec; ++} ++ ++static int dst_set_symbolrate(struct dst_state *state, u32 srate) ++{ ++ u32 symcalc; ++ u64 sval; ++ ++ state->symbol_rate = srate; ++ if (state->dst_type == DST_TYPE_IS_TERR) { ++ return -EOPNOTSUPP; ++ } ++ dprintk(verbose, DST_INFO, 1, "set symrate %u", srate); ++ srate /= 1000; ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ if (state->type_flags & DST_TYPE_HAS_SYMDIV) { ++ sval = srate; ++ sval <<= 20; ++ do_div(sval, 88000); ++ symcalc = (u32) sval; ++ dprintk(verbose, DST_INFO, 1, "set symcalc %u", symcalc); ++ state->tx_tuna[5] = (u8) (symcalc >> 12); ++ state->tx_tuna[6] = (u8) (symcalc >> 4); ++ state->tx_tuna[7] = (u8) (symcalc << 4); ++ } else { ++ state->tx_tuna[5] = (u8) (srate >> 16) & 0x7f; ++ state->tx_tuna[6] = (u8) (srate >> 8); ++ state->tx_tuna[7] = (u8) srate; ++ } ++ state->tx_tuna[8] &= ~0x20; ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) { ++ if (srate > 8000) ++ state->tx_tuna[8] |= 0x20; ++ } ++ } else if (state->dst_type == DST_TYPE_IS_CABLE) { ++ dprintk(verbose, DST_DEBUG, 1, "%s", state->fw_name); ++ if (!strncmp(state->fw_name, "DCTNEW", 6)) { ++ state->tx_tuna[5] = (u8) (srate >> 8); ++ state->tx_tuna[6] = (u8) srate; ++ state->tx_tuna[7] = 0x00; ++ } else if (!strncmp(state->fw_name, "DCT-CI", 6)) { ++ state->tx_tuna[5] = 0x00; ++ state->tx_tuna[6] = (u8) (srate >> 8); ++ state->tx_tuna[7] = (u8) srate; ++ } ++ } ++ return 0; ++} ++ ++static int dst_set_modulation(struct dst_state *state, fe_modulation_t modulation) ++{ ++ if (state->dst_type != DST_TYPE_IS_CABLE) ++ return -EOPNOTSUPP; ++ ++ state->modulation = modulation; ++ switch (modulation) { ++ case QAM_16: ++ state->tx_tuna[8] = 0x10; ++ break; ++ case QAM_32: ++ state->tx_tuna[8] = 0x20; ++ break; ++ case QAM_64: ++ state->tx_tuna[8] = 0x40; ++ break; ++ case QAM_128: ++ state->tx_tuna[8] = 0x80; ++ break; ++ case QAM_256: ++ if (!strncmp(state->fw_name, "DCTNEW", 6)) ++ state->tx_tuna[8] = 0xff; ++ else if (!strncmp(state->fw_name, "DCT-CI", 6)) ++ state->tx_tuna[8] = 0x00; ++ break; ++ case QPSK: ++ case QAM_AUTO: ++ case VSB_8: ++ case VSB_16: ++ default: ++ return -EINVAL; ++ ++ } ++ ++ return 0; ++} ++ ++static fe_modulation_t dst_get_modulation(struct dst_state *state) ++{ ++ return state->modulation; ++} ++ ++ ++u8 dst_check_sum(u8 *buf, u32 len) ++{ ++ u32 i; ++ u8 val = 0; ++ if (!len) ++ return 0; ++ for (i = 0; i < len; i++) { ++ val += buf[i]; ++ } ++ return ((~val) + 1); ++} ++EXPORT_SYMBOL(dst_check_sum); ++ ++static void dst_type_flags_print(struct dst_state *state) ++{ ++ u32 type_flags = state->type_flags; ++ ++ dprintk(verbose, DST_ERROR, 0, "DST type flags :"); ++ if (type_flags & DST_TYPE_HAS_TS188) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner", DST_TYPE_HAS_TS188); ++ if (type_flags & DST_TYPE_HAS_NEWTUNE_2) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x newtuner 2", DST_TYPE_HAS_NEWTUNE_2); ++ if (type_flags & DST_TYPE_HAS_TS204) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x ts204", DST_TYPE_HAS_TS204); ++ if (type_flags & DST_TYPE_HAS_VLF) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x VLF", DST_TYPE_HAS_VLF); ++ if (type_flags & DST_TYPE_HAS_SYMDIV) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x symdiv", DST_TYPE_HAS_SYMDIV); ++ if (type_flags & DST_TYPE_HAS_FW_1) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 1", DST_TYPE_HAS_FW_1); ++ if (type_flags & DST_TYPE_HAS_FW_2) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 2", DST_TYPE_HAS_FW_2); ++ if (type_flags & DST_TYPE_HAS_FW_3) ++ dprintk(verbose, DST_ERROR, 0, " 0x%x firmware version = 3", DST_TYPE_HAS_FW_3); ++ dprintk(verbose, DST_ERROR, 0, "\n"); ++} ++ ++ ++static int dst_type_print(struct dst_state *state, u8 type) ++{ ++ char *otype; ++ switch (type) { ++ case DST_TYPE_IS_SAT: ++ otype = "satellite"; ++ break; ++ ++ case DST_TYPE_IS_TERR: ++ otype = "terrestrial"; ++ break; ++ ++ case DST_TYPE_IS_CABLE: ++ otype = "cable"; ++ break; ++ ++ case DST_TYPE_IS_ATSC: ++ otype = "atsc"; ++ break; ++ ++ default: ++ dprintk(verbose, DST_INFO, 1, "invalid dst type %d", type); ++ return -EINVAL; ++ } ++ dprintk(verbose, DST_INFO, 1, "DST type: %s", otype); ++ ++ return 0; ++} ++ ++static struct tuner_types tuner_list[] = { ++ { ++ .tuner_type = TUNER_TYPE_L64724, ++ .tuner_name = "L 64724", ++ .board_name = "UNKNOWN", ++ .fw_name = "UNKNOWN" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_STV0299, ++ .tuner_name = "STV 0299", ++ .board_name = "VP1020", ++ .fw_name = "DST-MOT" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_STV0299, ++ .tuner_name = "STV 0299", ++ .board_name = "VP1020", ++ .fw_name = "DST-03T" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_MB86A15, ++ .tuner_name = "MB 86A15", ++ .board_name = "VP1022", ++ .fw_name = "DST-03T" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_MB86A15, ++ .tuner_name = "MB 86A15", ++ .board_name = "VP1025", ++ .fw_name = "DST-03T" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_STV0299, ++ .tuner_name = "STV 0299", ++ .board_name = "VP1030", ++ .fw_name = "DST-CI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_STV0299, ++ .tuner_name = "STV 0299", ++ .board_name = "VP1030", ++ .fw_name = "DSTMCI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP2021", ++ .fw_name = "DCTNEW" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP2030", ++ .fw_name = "DCT-CI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP2031", ++ .fw_name = "DCT-CI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP2040", ++ .fw_name = "DCT-CI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP3020", ++ .fw_name = "DTTFTA" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP3021", ++ .fw_name = "DTTFTA" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_TDA10046, ++ .tuner_name = "TDA10046", ++ .board_name = "VP3040", ++ .fw_name = "DTT-CI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_UNKNOWN, ++ .tuner_name = "UNKNOWN", ++ .board_name = "VP3051", ++ .fw_name = "DTTNXT" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_NXT200x, ++ .tuner_name = "NXT200x", ++ .board_name = "VP3220", ++ .fw_name = "ATSCDI" ++ }, ++ ++ { ++ .tuner_type = TUNER_TYPE_NXT200x, ++ .tuner_name = "NXT200x", ++ .board_name = "VP3250", ++ .fw_name = "ATSCAD" ++ }, ++}; ++ ++/* ++ Known cards list ++ Satellite ++ ------------------- ++ 200103A ++ VP-1020 DST-MOT LG(old), TS=188 ++ ++ VP-1020 DST-03T LG(new), TS=204 ++ VP-1022 DST-03T LG(new), TS=204 ++ VP-1025 DST-03T LG(new), TS=204 ++ ++ VP-1030 DSTMCI, LG(new), TS=188 ++ VP-1032 DSTMCI, LG(new), TS=188 ++ ++ Cable ++ ------------------- ++ VP-2030 DCT-CI, Samsung, TS=204 ++ VP-2021 DCT-CI, Unknown, TS=204 ++ VP-2031 DCT-CI, Philips, TS=188 ++ VP-2040 DCT-CI, Philips, TS=188, with CA daughter board ++ VP-2040 DCT-CI, Philips, TS=204, without CA daughter board ++ ++ Terrestrial ++ ------------------- ++ VP-3050 DTTNXT TS=188 ++ VP-3040 DTT-CI, Philips, TS=188 ++ VP-3040 DTT-CI, Philips, TS=204 ++ ++ ATSC ++ ------------------- ++ VP-3220 ATSCDI, TS=188 ++ VP-3250 ATSCAD, TS=188 ++ ++*/ ++ ++static struct dst_types dst_tlist[] = { ++ { ++ .device_id = "200103A", ++ .offset = 0, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_OBS_REGS, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, /* obsolete */ ++ ++ { ++ .device_id = "DST-020", ++ .offset = 0, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, /* obsolete */ ++ ++ { ++ .device_id = "DST-030", ++ .offset = 0, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, /* obsolete */ ++ ++ { ++ .device_id = "DST-03T", ++ .offset = 0, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_2, ++ .dst_feature = DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 | DST_TYPE_HAS_DISEQC5 ++ | DST_TYPE_HAS_MAC | DST_TYPE_HAS_MOTO, ++ .tuner_type = TUNER_TYPE_MULTI ++ }, ++ ++ { ++ .device_id = "DST-MOT", ++ .offset = 0, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_SYMDIV | DST_TYPE_HAS_FW_1, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, /* obsolete */ ++ ++ { ++ .device_id = "DST-CI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_TS204 | DST_TYPE_HAS_FW_1, ++ .dst_feature = DST_TYPE_HAS_CA, ++ .tuner_type = 0 ++ }, /* An OEM board */ ++ ++ { ++ .device_id = "DSTMCI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_INC_COUNT | DST_TYPE_HAS_VLF, ++ .dst_feature = DST_TYPE_HAS_CA | DST_TYPE_HAS_DISEQC3 | DST_TYPE_HAS_DISEQC4 ++ | DST_TYPE_HAS_MOTO | DST_TYPE_HAS_MAC, ++ .tuner_type = TUNER_TYPE_MULTI ++ }, ++ ++ { ++ .device_id = "DSTFCI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_SAT, ++ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_1, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, /* unknown to vendor */ ++ ++ { ++ .device_id = "DCT-CI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_CABLE, ++ .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_1 | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_VLF, ++ .dst_feature = DST_TYPE_HAS_CA, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "DCTNEW", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_CABLE, ++ .type_flags = DST_TYPE_HAS_TS188 | DST_TYPE_HAS_FW_3 | DST_TYPE_HAS_FW_BUILD | DST_TYPE_HAS_MULTI_FE, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "DTT-CI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_TERR, ++ .type_flags = DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_VLF, ++ .dst_feature = DST_TYPE_HAS_CA, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "DTTDIG", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_TERR, ++ .type_flags = DST_TYPE_HAS_FW_2, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "DTTNXT", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_TERR, ++ .type_flags = DST_TYPE_HAS_FW_2, ++ .dst_feature = DST_TYPE_HAS_ANALOG, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "ATSCDI", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_ATSC, ++ .type_flags = DST_TYPE_HAS_FW_2, ++ .dst_feature = 0, ++ .tuner_type = 0 ++ }, ++ ++ { ++ .device_id = "ATSCAD", ++ .offset = 1, ++ .dst_type = DST_TYPE_IS_ATSC, ++ .type_flags = DST_TYPE_HAS_MULTI_FE | DST_TYPE_HAS_FW_2 | DST_TYPE_HAS_FW_BUILD, ++ .dst_feature = DST_TYPE_HAS_MAC | DST_TYPE_HAS_ANALOG, ++ .tuner_type = 0 ++ }, ++ ++ { } ++ ++}; ++ ++static int dst_get_mac(struct dst_state *state) ++{ ++ u8 get_mac[] = { 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ get_mac[7] = dst_check_sum(get_mac, 7); ++ if (dst_command(state, get_mac, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Unsupported Command"); ++ return -1; ++ } ++ memset(&state->mac_address, '\0', 8); ++ memcpy(&state->mac_address, &state->rxbuffer, 6); ++ dprintk(verbose, DST_ERROR, 1, "MAC Address=[%pM]", state->mac_address); ++ ++ return 0; ++} ++ ++static int dst_fw_ver(struct dst_state *state) ++{ ++ u8 get_ver[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ get_ver[7] = dst_check_sum(get_ver, 7); ++ if (dst_command(state, get_ver, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Unsupported Command"); ++ return -1; ++ } ++ memcpy(&state->fw_version, &state->rxbuffer, 8); ++ dprintk(verbose, DST_ERROR, 1, "Firmware Ver = %x.%x Build = %02x, on %x:%x, %x-%x-20%02x", ++ state->fw_version[0] >> 4, state->fw_version[0] & 0x0f, ++ state->fw_version[1], ++ state->fw_version[5], state->fw_version[6], ++ state->fw_version[4], state->fw_version[3], state->fw_version[2]); ++ ++ return 0; ++} ++ ++static int dst_card_type(struct dst_state *state) ++{ ++ int j; ++ struct tuner_types *p_tuner_list = NULL; ++ ++ u8 get_type[] = { 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ get_type[7] = dst_check_sum(get_type, 7); ++ if (dst_command(state, get_type, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Unsupported Command"); ++ return -1; ++ } ++ memset(&state->card_info, '\0', 8); ++ memcpy(&state->card_info, &state->rxbuffer, 7); ++ dprintk(verbose, DST_ERROR, 1, "Device Model=[%s]", &state->card_info[0]); ++ ++ for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { ++ if (!strcmp(&state->card_info[0], p_tuner_list->board_name)) { ++ state->tuner_type = p_tuner_list->tuner_type; ++ dprintk(verbose, DST_ERROR, 1, "DST has [%s] tuner, tuner type=[%d]", ++ p_tuner_list->tuner_name, p_tuner_list->tuner_type); ++ } ++ } ++ ++ return 0; ++} ++ ++static int dst_get_vendor(struct dst_state *state) ++{ ++ u8 get_vendor[] = { 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ get_vendor[7] = dst_check_sum(get_vendor, 7); ++ if (dst_command(state, get_vendor, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Unsupported Command"); ++ return -1; ++ } ++ memset(&state->vendor, '\0', 8); ++ memcpy(&state->vendor, &state->rxbuffer, 7); ++ dprintk(verbose, DST_ERROR, 1, "Vendor=[%s]", &state->vendor[0]); ++ ++ return 0; ++} ++ ++static void debug_dst_buffer(struct dst_state *state) ++{ ++ int i; ++ ++ if (verbose > 2) { ++ printk("%s: [", __func__); ++ for (i = 0; i < 8; i++) ++ printk(" %02x", state->rxbuffer[i]); ++ printk("]\n"); ++ } ++} ++ ++static int dst_check_stv0299(struct dst_state *state) ++{ ++ u8 check_stv0299[] = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ ++ check_stv0299[7] = dst_check_sum(check_stv0299, 7); ++ if (dst_command(state, check_stv0299, 8) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "Cmd=[0x04] failed"); ++ return -1; ++ } ++ debug_dst_buffer(state); ++ ++ if (memcmp(&check_stv0299, &state->rxbuffer, 8)) { ++ dprintk(verbose, DST_ERROR, 1, "Found a STV0299 NIM"); ++ state->tuner_type = TUNER_TYPE_STV0299; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static int dst_check_mb86a15(struct dst_state *state) ++{ ++ u8 check_mb86a15[] = { 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ ++ check_mb86a15[7] = dst_check_sum(check_mb86a15, 7); ++ if (dst_command(state, check_mb86a15, 8) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "Cmd=[0x10], failed"); ++ return -1; ++ } ++ debug_dst_buffer(state); ++ ++ if (memcmp(&check_mb86a15, &state->rxbuffer, 8) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "Found a MB86A15 NIM"); ++ state->tuner_type = TUNER_TYPE_MB86A15; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static int dst_get_tuner_info(struct dst_state *state) ++{ ++ u8 get_tuner_1[] = { 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ u8 get_tuner_2[] = { 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ++ ++ get_tuner_1[7] = dst_check_sum(get_tuner_1, 7); ++ get_tuner_2[7] = dst_check_sum(get_tuner_2, 7); ++ dprintk(verbose, DST_ERROR, 1, "DST TYpe = MULTI FE"); ++ if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { ++ if (dst_command(state, get_tuner_1, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Cmd=[0x13], Unsupported"); ++ goto force; ++ } ++ } else { ++ if (dst_command(state, get_tuner_2, 8) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Cmd=[0xb], Unsupported"); ++ goto force; ++ } ++ } ++ memcpy(&state->board_info, &state->rxbuffer, 8); ++ if (state->type_flags & DST_TYPE_HAS_MULTI_FE) { ++ dprintk(verbose, DST_ERROR, 1, "DST type has TS=188"); ++ } ++ if (state->board_info[0] == 0xbc) { ++ if (state->dst_type != DST_TYPE_IS_ATSC) ++ state->type_flags |= DST_TYPE_HAS_TS188; ++ else ++ state->type_flags |= DST_TYPE_HAS_NEWTUNE_2; ++ ++ if (state->board_info[1] == 0x01) { ++ state->dst_hw_cap |= DST_TYPE_HAS_DBOARD; ++ dprintk(verbose, DST_ERROR, 1, "DST has Daughterboard"); ++ } ++ } ++ ++ return 0; ++force: ++ if (!strncmp(state->fw_name, "DCT-CI", 6)) { ++ state->type_flags |= DST_TYPE_HAS_TS204; ++ dprintk(verbose, DST_ERROR, 1, "Forcing [%s] to TS188", state->fw_name); ++ } ++ ++ return -1; ++} ++ ++static int dst_get_device_id(struct dst_state *state) ++{ ++ u8 reply; ++ ++ int i, j; ++ struct dst_types *p_dst_type = NULL; ++ struct tuner_types *p_tuner_list = NULL; ++ ++ u8 use_dst_type = 0; ++ u32 use_type_flags = 0; ++ ++ static u8 device_type[8] = {0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; ++ ++ state->tuner_type = 0; ++ device_type[7] = dst_check_sum(device_type, 7); ++ ++ if (write_dst(state, device_type, FIXED_COMM)) ++ return -1; /* Write failed */ ++ if ((dst_pio_disable(state)) < 0) ++ return -1; ++ if (read_dst(state, &reply, GET_ACK)) ++ return -1; /* Read failure */ ++ if (reply != ACK) { ++ dprintk(verbose, DST_INFO, 1, "Write not Acknowledged! [Reply=0x%02x]", reply); ++ return -1; /* Unack'd write */ ++ } ++ if (!dst_wait_dst_ready(state, DEVICE_INIT)) ++ return -1; /* DST not ready yet */ ++ if (read_dst(state, state->rxbuffer, FIXED_COMM)) ++ return -1; ++ ++ dst_pio_disable(state); ++ if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { ++ dprintk(verbose, DST_INFO, 1, "Checksum failure!"); ++ return -1; /* Checksum failure */ ++ } ++ state->rxbuffer[7] = '\0'; ++ ++ for (i = 0, p_dst_type = dst_tlist; i < ARRAY_SIZE(dst_tlist); i++, p_dst_type++) { ++ if (!strncmp (&state->rxbuffer[p_dst_type->offset], p_dst_type->device_id, strlen (p_dst_type->device_id))) { ++ use_type_flags = p_dst_type->type_flags; ++ use_dst_type = p_dst_type->dst_type; ++ ++ /* Card capabilities */ ++ state->dst_hw_cap = p_dst_type->dst_feature; ++ dprintk(verbose, DST_ERROR, 1, "Recognise [%s]", p_dst_type->device_id); ++ strncpy(&state->fw_name[0], p_dst_type->device_id, 6); ++ /* Multiple tuners */ ++ if (p_dst_type->tuner_type & TUNER_TYPE_MULTI) { ++ switch (use_dst_type) { ++ case DST_TYPE_IS_SAT: ++ /* STV0299 check */ ++ if (dst_check_stv0299(state) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "Unsupported"); ++ state->tuner_type = TUNER_TYPE_MB86A15; ++ } ++ break; ++ default: ++ break; ++ } ++ if (dst_check_mb86a15(state) < 0) ++ dprintk(verbose, DST_ERROR, 1, "Unsupported"); ++ /* Single tuner */ ++ } else { ++ state->tuner_type = p_dst_type->tuner_type; ++ } ++ for (j = 0, p_tuner_list = tuner_list; j < ARRAY_SIZE(tuner_list); j++, p_tuner_list++) { ++ if (!(strncmp(p_dst_type->device_id, p_tuner_list->fw_name, 7)) && ++ p_tuner_list->tuner_type == state->tuner_type) { ++ dprintk(verbose, DST_ERROR, 1, "[%s] has a [%s]", ++ p_dst_type->device_id, p_tuner_list->tuner_name); ++ } ++ } ++ break; ++ } ++ } ++ ++ if (i >= ARRAY_SIZE(dst_tlist)) { ++ dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]); ++ dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in"); ++ use_dst_type = DST_TYPE_IS_SAT; ++ use_type_flags = DST_TYPE_HAS_SYMDIV; ++ } ++ dst_type_print(state, use_dst_type); ++ state->type_flags = use_type_flags; ++ state->dst_type = use_dst_type; ++ dst_type_flags_print(state); ++ ++ return 0; ++} ++ ++static int dst_probe(struct dst_state *state) ++{ ++ mutex_init(&state->dst_mutex); ++ if (dst_addons & DST_TYPE_HAS_CA) { ++ if ((rdc_8820_reset(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "RDC 8820 RESET Failed."); ++ return -1; ++ } ++ msleep(4000); ++ } else { ++ msleep(100); ++ } ++ if ((dst_comm_init(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "DST Initialization Failed."); ++ return -1; ++ } ++ msleep(100); ++ if (dst_get_device_id(state) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "unknown device."); ++ return -1; ++ } ++ if (dst_get_mac(state) < 0) { ++ dprintk(verbose, DST_INFO, 1, "MAC: Unsupported command"); ++ } ++ if ((state->type_flags & DST_TYPE_HAS_MULTI_FE) || (state->type_flags & DST_TYPE_HAS_FW_BUILD)) { ++ if (dst_get_tuner_info(state) < 0) ++ dprintk(verbose, DST_INFO, 1, "Tuner: Unsupported command"); ++ } ++ if (state->type_flags & DST_TYPE_HAS_TS204) { ++ dst_packsize(state, 204); ++ } ++ if (state->type_flags & DST_TYPE_HAS_FW_BUILD) { ++ if (dst_fw_ver(state) < 0) { ++ dprintk(verbose, DST_INFO, 1, "FW: Unsupported command"); ++ return 0; ++ } ++ if (dst_card_type(state) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Card: Unsupported command"); ++ return 0; ++ } ++ if (dst_get_vendor(state) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Vendor: Unsupported command"); ++ return 0; ++ } ++ } ++ ++ return 0; ++} ++ ++static int dst_command(struct dst_state *state, u8 *data, u8 len) ++{ ++ u8 reply; ++ ++ mutex_lock(&state->dst_mutex); ++ if ((dst_comm_init(state)) < 0) { ++ dprintk(verbose, DST_NOTICE, 1, "DST Communication Initialization Failed."); ++ goto error; ++ } ++ if (write_dst(state, data, len)) { ++ dprintk(verbose, DST_INFO, 1, "Trying to recover.. "); ++ if ((dst_error_recovery(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "Recovery Failed."); ++ goto error; ++ } ++ goto error; ++ } ++ if ((dst_pio_disable(state)) < 0) { ++ dprintk(verbose, DST_ERROR, 1, "PIO Disable Failed."); ++ goto error; ++ } ++ if (state->type_flags & DST_TYPE_HAS_FW_1) ++ mdelay(3); ++ if (read_dst(state, &reply, GET_ACK)) { ++ dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); ++ if ((dst_error_recovery(state)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Recovery Failed."); ++ goto error; ++ } ++ goto error; ++ } ++ if (reply != ACK) { ++ dprintk(verbose, DST_INFO, 1, "write not acknowledged 0x%02x ", reply); ++ goto error; ++ } ++ if (len >= 2 && data[0] == 0 && (data[1] == 1 || data[1] == 3)) ++ goto error; ++ if (state->type_flags & DST_TYPE_HAS_FW_1) ++ mdelay(3); ++ else ++ udelay(2000); ++ if (!dst_wait_dst_ready(state, NO_DELAY)) ++ goto error; ++ if (read_dst(state, state->rxbuffer, FIXED_COMM)) { ++ dprintk(verbose, DST_DEBUG, 1, "Trying to recover.. "); ++ if ((dst_error_recovery(state)) < 0) { ++ dprintk(verbose, DST_INFO, 1, "Recovery failed."); ++ goto error; ++ } ++ goto error; ++ } ++ if (state->rxbuffer[7] != dst_check_sum(state->rxbuffer, 7)) { ++ dprintk(verbose, DST_INFO, 1, "checksum failure"); ++ goto error; ++ } ++ mutex_unlock(&state->dst_mutex); ++ return 0; ++ ++error: ++ mutex_unlock(&state->dst_mutex); ++ return -EIO; ++ ++} ++ ++static int dst_get_signal(struct dst_state *state) ++{ ++ int retval; ++ u8 get_signal[] = { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb }; ++ //dprintk("%s: Getting Signal strength and other parameters\n", __func__); ++ if ((state->diseq_flags & ATTEMPT_TUNE) == 0) { ++ state->decode_lock = state->decode_strength = state->decode_snr = 0; ++ return 0; ++ } ++ if (0 == (state->diseq_flags & HAS_LOCK)) { ++ state->decode_lock = state->decode_strength = state->decode_snr = 0; ++ return 0; ++ } ++ if (time_after_eq(jiffies, state->cur_jiff + (HZ / 5))) { ++ retval = dst_command(state, get_signal, 8); ++ if (retval < 0) ++ return retval; ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ state->decode_lock = ((state->rxbuffer[6] & 0x10) == 0) ? 1 : 0; ++ state->decode_strength = state->rxbuffer[5] << 8; ++ state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; ++ } else if ((state->dst_type == DST_TYPE_IS_TERR) || (state->dst_type == DST_TYPE_IS_CABLE)) { ++ state->decode_lock = (state->rxbuffer[1]) ? 1 : 0; ++ state->decode_strength = state->rxbuffer[4] << 8; ++ state->decode_snr = state->rxbuffer[3] << 8; ++ } else if (state->dst_type == DST_TYPE_IS_ATSC) { ++ state->decode_lock = (state->rxbuffer[6] == 0x00) ? 1 : 0; ++ state->decode_strength = state->rxbuffer[4] << 8; ++ state->decode_snr = state->rxbuffer[2] << 8 | state->rxbuffer[3]; ++ } ++ state->cur_jiff = jiffies; ++ } ++ return 0; ++} ++ ++static int dst_tone_power_cmd(struct dst_state *state) ++{ ++ u8 paket[8] = { 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 }; ++ ++ if (state->dst_type != DST_TYPE_IS_SAT) ++ return -EOPNOTSUPP; ++ paket[4] = state->tx_tuna[4]; ++ paket[2] = state->tx_tuna[2]; ++ paket[3] = state->tx_tuna[3]; ++ paket[7] = dst_check_sum (paket, 7); ++ return dst_command(state, paket, 8); ++} ++ ++static int dst_get_tuna(struct dst_state *state) ++{ ++ int retval; ++ ++ if ((state->diseq_flags & ATTEMPT_TUNE) == 0) ++ return 0; ++ state->diseq_flags &= ~(HAS_LOCK); ++ if (!dst_wait_dst_ready(state, NO_DELAY)) ++ return -EIO; ++ if ((state->type_flags & DST_TYPE_HAS_VLF) && ++ !(state->dst_type == DST_TYPE_IS_ATSC)) ++ ++ retval = read_dst(state, state->rx_tuna, 10); ++ else ++ retval = read_dst(state, &state->rx_tuna[2], FIXED_COMM); ++ if (retval < 0) { ++ dprintk(verbose, DST_DEBUG, 1, "read not successful"); ++ return retval; ++ } ++ if ((state->type_flags & DST_TYPE_HAS_VLF) && ++ !(state->dst_type == DST_TYPE_IS_ATSC)) { ++ ++ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { ++ dprintk(verbose, DST_INFO, 1, "checksum failure ? "); ++ return -EIO; ++ } ++ } else { ++ if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[2], 7)) { ++ dprintk(verbose, DST_INFO, 1, "checksum failure? "); ++ return -EIO; ++ } ++ } ++ if (state->rx_tuna[2] == 0 && state->rx_tuna[3] == 0) ++ return 0; ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 8) + state->rx_tuna[3]; ++ } else { ++ state->decode_freq = ((state->rx_tuna[2] & 0x7f) << 16) + (state->rx_tuna[3] << 8) + state->rx_tuna[4]; ++ } ++ state->decode_freq = state->decode_freq * 1000; ++ state->decode_lock = 1; ++ state->diseq_flags |= HAS_LOCK; ++ ++ return 1; ++} ++ ++static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); ++ ++static int dst_write_tuna(struct dvb_frontend *fe) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ int retval; ++ u8 reply; ++ ++ dprintk(verbose, DST_INFO, 1, "type_flags 0x%x ", state->type_flags); ++ state->decode_freq = 0; ++ state->decode_lock = state->decode_strength = state->decode_snr = 0; ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ if (!(state->diseq_flags & HAS_POWER)) ++ dst_set_voltage(fe, SEC_VOLTAGE_13); ++ } ++ state->diseq_flags &= ~(HAS_LOCK | ATTEMPT_TUNE); ++ mutex_lock(&state->dst_mutex); ++ if ((dst_comm_init(state)) < 0) { ++ dprintk(verbose, DST_DEBUG, 1, "DST Communication initialization failed."); ++ goto error; ++ } ++// if (state->type_flags & DST_TYPE_HAS_NEWTUNE) { ++ if ((state->type_flags & DST_TYPE_HAS_VLF) && ++ (!(state->dst_type == DST_TYPE_IS_ATSC))) { ++ ++ state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[0], 9); ++ retval = write_dst(state, &state->tx_tuna[0], 10); ++ } else { ++ state->tx_tuna[9] = dst_check_sum(&state->tx_tuna[2], 7); ++ retval = write_dst(state, &state->tx_tuna[2], FIXED_COMM); ++ } ++ if (retval < 0) { ++ dst_pio_disable(state); ++ dprintk(verbose, DST_DEBUG, 1, "write not successful"); ++ goto werr; ++ } ++ if ((dst_pio_disable(state)) < 0) { ++ dprintk(verbose, DST_DEBUG, 1, "DST PIO disable failed !"); ++ goto error; ++ } ++ if ((read_dst(state, &reply, GET_ACK) < 0)) { ++ dprintk(verbose, DST_DEBUG, 1, "read verify not successful."); ++ goto error; ++ } ++ if (reply != ACK) { ++ dprintk(verbose, DST_DEBUG, 1, "write not acknowledged 0x%02x ", reply); ++ goto error; ++ } ++ state->diseq_flags |= ATTEMPT_TUNE; ++ retval = dst_get_tuna(state); ++werr: ++ mutex_unlock(&state->dst_mutex); ++ return retval; ++ ++error: ++ mutex_unlock(&state->dst_mutex); ++ return -EIO; ++} ++ ++/* ++ * line22k0 0x00, 0x09, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00 ++ * line22k1 0x00, 0x09, 0x01, 0xff, 0x01, 0x00, 0x00, 0x00 ++ * line22k2 0x00, 0x09, 0x02, 0xff, 0x01, 0x00, 0x00, 0x00 ++ * tone 0x00, 0x09, 0xff, 0x00, 0x01, 0x00, 0x00, 0x00 ++ * data 0x00, 0x09, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00 ++ * power_off 0x00, 0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 ++ * power_on 0x00, 0x09, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00 ++ * Diseqc 1 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec ++ * Diseqc 2 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf4, 0xe8 ++ * Diseqc 3 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf8, 0xe4 ++ * Diseqc 4 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xfc, 0xe0 ++ */ ++ ++static int dst_set_diseqc(struct dvb_frontend *fe, struct dvb_diseqc_master_cmd *cmd) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ u8 paket[8] = { 0x00, 0x08, 0x04, 0xe0, 0x10, 0x38, 0xf0, 0xec }; ++ ++ if (state->dst_type != DST_TYPE_IS_SAT) ++ return -EOPNOTSUPP; ++ if (cmd->msg_len > 0 && cmd->msg_len < 5) ++ memcpy(&paket[3], cmd->msg, cmd->msg_len); ++ else if (cmd->msg_len == 5 && state->dst_hw_cap & DST_TYPE_HAS_DISEQC5) ++ memcpy(&paket[2], cmd->msg, cmd->msg_len); ++ else ++ return -EINVAL; ++ paket[7] = dst_check_sum(&paket[0], 7); ++ return dst_command(state, paket, 8); ++} ++ ++static int dst_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ int need_cmd, retval = 0; ++ struct dst_state *state = fe->demodulator_priv; ++ ++ state->voltage = voltage; ++ if (state->dst_type != DST_TYPE_IS_SAT) ++ return -EOPNOTSUPP; ++ ++ need_cmd = 0; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ case SEC_VOLTAGE_18: ++ if ((state->diseq_flags & HAS_POWER) == 0) ++ need_cmd = 1; ++ state->diseq_flags |= HAS_POWER; ++ state->tx_tuna[4] = 0x01; ++ break; ++ case SEC_VOLTAGE_OFF: ++ need_cmd = 1; ++ state->diseq_flags &= ~(HAS_POWER | HAS_LOCK | ATTEMPT_TUNE); ++ state->tx_tuna[4] = 0x00; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (need_cmd) ++ retval = dst_tone_power_cmd(state); ++ ++ return retval; ++} ++ ++static int dst_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ state->tone = tone; ++ if (state->dst_type != DST_TYPE_IS_SAT) ++ return -EOPNOTSUPP; ++ ++ switch (tone) { ++ case SEC_TONE_OFF: ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) ++ state->tx_tuna[2] = 0x00; ++ else ++ state->tx_tuna[2] = 0xff; ++ break; ++ ++ case SEC_TONE_ON: ++ state->tx_tuna[2] = 0x02; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return dst_tone_power_cmd(state); ++} ++ ++static int dst_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ if (state->dst_type != DST_TYPE_IS_SAT) ++ return -EOPNOTSUPP; ++ state->minicmd = minicmd; ++ switch (minicmd) { ++ case SEC_MINI_A: ++ state->tx_tuna[3] = 0x02; ++ break; ++ case SEC_MINI_B: ++ state->tx_tuna[3] = 0xff; ++ break; ++ } ++ return dst_tone_power_cmd(state); ++} ++ ++ ++static int dst_init(struct dvb_frontend *fe) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ static u8 sat_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x00, 0x73, 0x21, 0x00, 0x00 }; ++ static u8 sat_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x55, 0xbd, 0x50, 0x00, 0x00 }; ++ static u8 ter_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; ++ static u8 ter_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; ++ static u8 cab_tuna_188[] = { 0x09, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; ++ static u8 cab_tuna_204[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; ++ static u8 atsc_tuner[] = { 0x00, 0x00, 0x03, 0xb6, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00 }; ++ ++ state->inversion = INVERSION_OFF; ++ state->voltage = SEC_VOLTAGE_13; ++ state->tone = SEC_TONE_OFF; ++ state->diseq_flags = 0; ++ state->k22 = 0x02; ++ state->bandwidth = 7000000; ++ state->cur_jiff = jiffies; ++ if (state->dst_type == DST_TYPE_IS_SAT) ++ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? sat_tuna_188 : sat_tuna_204), sizeof (sat_tuna_204)); ++ else if (state->dst_type == DST_TYPE_IS_TERR) ++ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? ter_tuna_188 : ter_tuna_204), sizeof (ter_tuna_204)); ++ else if (state->dst_type == DST_TYPE_IS_CABLE) ++ memcpy(state->tx_tuna, ((state->type_flags & DST_TYPE_HAS_VLF) ? cab_tuna_188 : cab_tuna_204), sizeof (cab_tuna_204)); ++ else if (state->dst_type == DST_TYPE_IS_ATSC) ++ memcpy(state->tx_tuna, atsc_tuner, sizeof (atsc_tuner)); ++ ++ return 0; ++} ++ ++static int dst_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ *status = 0; ++ if (state->diseq_flags & HAS_LOCK) { ++// dst_get_signal(state); // don't require(?) to ask MCU ++ if (state->decode_lock) ++ *status |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_SYNC | FE_HAS_VITERBI; ++ } ++ ++ return 0; ++} ++ ++static int dst_read_signal_strength(struct dvb_frontend *fe, u16 *strength) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ int retval = dst_get_signal(state); ++ *strength = state->decode_strength; ++ ++ return retval; ++} ++ ++static int dst_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ ++ int retval = dst_get_signal(state); ++ *snr = state->decode_snr; ++ ++ return retval; ++} ++ ++static int dst_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ int retval = -EINVAL; ++ struct dst_state *state = fe->demodulator_priv; ++ ++ if (p != NULL) { ++ retval = dst_set_freq(state, p->frequency); ++ if(retval != 0) ++ return retval; ++ dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); ++ ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) ++ dst_set_inversion(state, p->inversion); ++ dst_set_fec(state, p->fec_inner); ++ dst_set_symbolrate(state, p->symbol_rate); ++ dst_set_polarization(state); ++ dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); ++ ++ } else if (state->dst_type == DST_TYPE_IS_TERR) ++ dst_set_bandwidth(state, p->bandwidth_hz); ++ else if (state->dst_type == DST_TYPE_IS_CABLE) { ++ dst_set_fec(state, p->fec_inner); ++ dst_set_symbolrate(state, p->symbol_rate); ++ dst_set_modulation(state, p->modulation); ++ } ++ retval = dst_write_tuna(fe); ++ } ++ ++ return retval; ++} ++ ++static int dst_tune_frontend(struct dvb_frontend* fe, ++ bool re_tune, ++ unsigned int mode_flags, ++ unsigned int *delay, ++ fe_status_t *status) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ ++ if (re_tune) { ++ dst_set_freq(state, p->frequency); ++ dprintk(verbose, DST_DEBUG, 1, "Set Frequency=[%d]", p->frequency); ++ ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) ++ dst_set_inversion(state, p->inversion); ++ dst_set_fec(state, p->fec_inner); ++ dst_set_symbolrate(state, p->symbol_rate); ++ dst_set_polarization(state); ++ dprintk(verbose, DST_DEBUG, 1, "Set Symbolrate=[%d]", p->symbol_rate); ++ ++ } else if (state->dst_type == DST_TYPE_IS_TERR) ++ dst_set_bandwidth(state, p->bandwidth_hz); ++ else if (state->dst_type == DST_TYPE_IS_CABLE) { ++ dst_set_fec(state, p->fec_inner); ++ dst_set_symbolrate(state, p->symbol_rate); ++ dst_set_modulation(state, p->modulation); ++ } ++ dst_write_tuna(fe); ++ } ++ ++ if (!(mode_flags & FE_TUNE_MODE_ONESHOT)) ++ dst_read_status(fe, status); ++ ++ *delay = HZ/10; ++ return 0; ++} ++ ++static int dst_get_tuning_algo(struct dvb_frontend *fe) ++{ ++ return dst_algo ? DVBFE_ALGO_HW : DVBFE_ALGO_SW; ++} ++ ++static int dst_get_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct dst_state *state = fe->demodulator_priv; ++ ++ p->frequency = state->decode_freq; ++ if (state->dst_type == DST_TYPE_IS_SAT) { ++ if (state->type_flags & DST_TYPE_HAS_OBS_REGS) ++ p->inversion = state->inversion; ++ p->symbol_rate = state->symbol_rate; ++ p->fec_inner = dst_get_fec(state); ++ } else if (state->dst_type == DST_TYPE_IS_TERR) { ++ p->bandwidth_hz = state->bandwidth; ++ } else if (state->dst_type == DST_TYPE_IS_CABLE) { ++ p->symbol_rate = state->symbol_rate; ++ p->fec_inner = dst_get_fec(state); ++ p->modulation = dst_get_modulation(state); ++ } ++ ++ return 0; ++} ++ ++static void dst_release(struct dvb_frontend *fe) ++{ ++ struct dst_state *state = fe->demodulator_priv; ++ if (state->dst_ca) { ++ dvb_unregister_device(state->dst_ca); ++#ifdef CONFIG_MEDIA_ATTACH ++ symbol_put(dst_ca_attach); ++#endif ++ } ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops dst_dvbt_ops; ++static struct dvb_frontend_ops dst_dvbs_ops; ++static struct dvb_frontend_ops dst_dvbc_ops; ++static struct dvb_frontend_ops dst_atsc_ops; ++ ++struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter) ++{ ++ /* check if the ASIC is there */ ++ if (dst_probe(state) < 0) { ++ kfree(state); ++ return NULL; ++ } ++ /* determine settings based on type */ ++ /* create dvb_frontend */ ++ switch (state->dst_type) { ++ case DST_TYPE_IS_TERR: ++ memcpy(&state->frontend.ops, &dst_dvbt_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ case DST_TYPE_IS_CABLE: ++ memcpy(&state->frontend.ops, &dst_dvbc_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ case DST_TYPE_IS_SAT: ++ memcpy(&state->frontend.ops, &dst_dvbs_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ case DST_TYPE_IS_ATSC: ++ memcpy(&state->frontend.ops, &dst_atsc_ops, sizeof(struct dvb_frontend_ops)); ++ break; ++ default: ++ dprintk(verbose, DST_ERROR, 1, "unknown DST type. please report to the LinuxTV.org DVB mailinglist."); ++ kfree(state); ++ return NULL; ++ } ++ state->frontend.demodulator_priv = state; ++ ++ return state; /* Manu (DST is a card not a frontend) */ ++} ++ ++EXPORT_SYMBOL(dst_attach); ++ ++static struct dvb_frontend_ops dst_dvbt_ops = { ++ .delsys = { SYS_DVBT }, ++ .info = { ++ .name = "DST DVB-T", ++ .frequency_min = 137000000, ++ .frequency_max = 858000000, ++ .frequency_stepsize = 166667, ++ .caps = FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 | ++ FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO ++ }, ++ ++ .release = dst_release, ++ .init = dst_init, ++ .tune = dst_tune_frontend, ++ .set_frontend = dst_set_frontend, ++ .get_frontend = dst_get_frontend, ++ .get_frontend_algo = dst_get_tuning_algo, ++ .read_status = dst_read_status, ++ .read_signal_strength = dst_read_signal_strength, ++ .read_snr = dst_read_snr, ++}; ++ ++static struct dvb_frontend_ops dst_dvbs_ops = { ++ .delsys = { SYS_DVBS }, ++ .info = { ++ .name = "DST DVB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1000, /* kHz for QPSK frontends */ ++ .frequency_tolerance = 29500, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ /* . symbol_rate_tolerance = ???,*/ ++ .caps = FE_CAN_FEC_AUTO | FE_CAN_QPSK ++ }, ++ ++ .release = dst_release, ++ .init = dst_init, ++ .tune = dst_tune_frontend, ++ .set_frontend = dst_set_frontend, ++ .get_frontend = dst_get_frontend, ++ .get_frontend_algo = dst_get_tuning_algo, ++ .read_status = dst_read_status, ++ .read_signal_strength = dst_read_signal_strength, ++ .read_snr = dst_read_snr, ++ .diseqc_send_burst = dst_send_burst, ++ .diseqc_send_master_cmd = dst_set_diseqc, ++ .set_voltage = dst_set_voltage, ++ .set_tone = dst_set_tone, ++}; ++ ++static struct dvb_frontend_ops dst_dvbc_ops = { ++ .delsys = { SYS_DVBC_ANNEX_A }, ++ .info = { ++ .name = "DST DVB-C", ++ .frequency_stepsize = 62500, ++ .frequency_min = 51000000, ++ .frequency_max = 858000000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | ++ FE_CAN_QAM_16 | ++ FE_CAN_QAM_32 | ++ FE_CAN_QAM_64 | ++ FE_CAN_QAM_128 | ++ FE_CAN_QAM_256 ++ }, ++ ++ .release = dst_release, ++ .init = dst_init, ++ .tune = dst_tune_frontend, ++ .set_frontend = dst_set_frontend, ++ .get_frontend = dst_get_frontend, ++ .get_frontend_algo = dst_get_tuning_algo, ++ .read_status = dst_read_status, ++ .read_signal_strength = dst_read_signal_strength, ++ .read_snr = dst_read_snr, ++}; ++ ++static struct dvb_frontend_ops dst_atsc_ops = { ++ .delsys = { SYS_ATSC }, ++ .info = { ++ .name = "DST ATSC", ++ .frequency_stepsize = 62500, ++ .frequency_min = 510000000, ++ .frequency_max = 858000000, ++ .symbol_rate_min = 1000000, ++ .symbol_rate_max = 45000000, ++ .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB ++ }, ++ ++ .release = dst_release, ++ .init = dst_init, ++ .tune = dst_tune_frontend, ++ .set_frontend = dst_set_frontend, ++ .get_frontend = dst_get_frontend, ++ .get_frontend_algo = dst_get_tuning_algo, ++ .read_status = dst_read_status, ++ .read_signal_strength = dst_read_signal_strength, ++ .read_snr = dst_read_snr, ++}; ++ ++MODULE_DESCRIPTION("DST DVB-S/T/C/ATSC Combo Frontend driver"); ++MODULE_AUTHOR("Jamie Honan, Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c +new file mode 100644 +index 0000000..7d96fab +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dst_ca.c +@@ -0,0 +1,726 @@ ++/* ++ CA-driver for TwinHan DST Frontend/Card ++ ++ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include "dvbdev.h" ++#include "dvb_frontend.h" ++#include "dst_ca.h" ++#include "dst_common.h" ++ ++#define DST_CA_ERROR 0 ++#define DST_CA_NOTICE 1 ++#define DST_CA_INFO 2 ++#define DST_CA_DEBUG 3 ++ ++#define dprintk(x, y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((x > DST_CA_ERROR) && (x > y)) \ ++ printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > DST_CA_NOTICE) && (x > y)) \ ++ printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > DST_CA_INFO) && (x > y)) \ ++ printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ ++ else if ((x > DST_CA_DEBUG) && (x > y)) \ ++ printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ ++ } else { \ ++ if (x > y) \ ++ printk(format, ## arg); \ ++ } \ ++} while(0) ++ ++ ++static DEFINE_MUTEX(dst_ca_mutex); ++static unsigned int verbose = 5; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); ++ ++/* Need some more work */ ++static int ca_set_slot_descr(void) ++{ ++ /* We could make this more graceful ? */ ++ return -EOPNOTSUPP; ++} ++ ++/* Need some more work */ ++static int ca_set_pid(void) ++{ ++ /* We could make this more graceful ? */ ++ return -EOPNOTSUPP; ++} ++ ++static void put_command_and_length(u8 *data, int command, int length) ++{ ++ data[0] = (command >> 16) & 0xff; ++ data[1] = (command >> 8) & 0xff; ++ data[2] = command & 0xff; ++ data[3] = length; ++} ++ ++static void put_checksum(u8 *check_string, int length) ++{ ++ dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum."); ++ dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length); ++ check_string[length] = dst_check_sum (check_string, length); ++ dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]); ++} ++ ++static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read) ++{ ++ u8 reply; ++ ++ mutex_lock(&state->dst_mutex); ++ dst_comm_init(state); ++ msleep(65); ++ ++ if (write_dst(state, data, len)) { ++ dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover"); ++ dst_error_recovery(state); ++ goto error; ++ } ++ if ((dst_pio_disable(state)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed."); ++ goto error; ++ } ++ if (read_dst(state, &reply, GET_ACK) < 0) { ++ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); ++ dst_error_recovery(state); ++ goto error; ++ } ++ if (read) { ++ if (! dst_wait_dst_ready(state, LONG_DELAY)) { ++ dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready"); ++ goto error; ++ } ++ if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */ ++ dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); ++ dst_error_recovery(state); ++ goto error; ++ } ++ } ++ mutex_unlock(&state->dst_mutex); ++ return 0; ++ ++error: ++ mutex_unlock(&state->dst_mutex); ++ return -EIO; ++} ++ ++ ++static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read) ++{ ++ u8 dst_ca_comm_err = 0; ++ ++ while (dst_ca_comm_err < RETRIES) { ++ dprintk(verbose, DST_CA_NOTICE, 1, " Put Command"); ++ if (dst_ci_command(state, data, ca_string, len, read)) { // If error ++ dst_error_recovery(state); ++ dst_ca_comm_err++; // work required here. ++ } else { ++ break; ++ } ++ } ++ ++ if(dst_ca_comm_err == RETRIES) ++ return -1; ++ ++ return 0; ++} ++ ++ ++ ++static int ca_get_app_info(struct dst_state *state) ++{ ++ int length, str_length; ++ static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff}; ++ ++ put_checksum(&command[0], command[0]); ++ if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); ++ dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================"); ++ dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]", ++ state->messages[7], (state->messages[8] << 8) | state->messages[9], ++ (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12])); ++ dprintk(verbose, DST_CA_INFO, 1, " =================================================================================================="); ++ ++ // Transform dst message to correct application_info message ++ length = state->messages[5]; ++ str_length = length - 6; ++ if (str_length < 0) { ++ str_length = 0; ++ dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering."); ++ } ++ ++ // First, the command and length fields ++ put_command_and_length(&state->messages[0], CA_APP_INFO, length); ++ ++ // Copy application_type, application_manufacturer and manufacturer_code ++ memcpy(&state->messages[4], &state->messages[7], 5); ++ ++ // Set string length and copy string ++ state->messages[9] = str_length; ++ memcpy(&state->messages[10], &state->messages[12], str_length); ++ ++ return 0; ++} ++ ++static int ca_get_ca_info(struct dst_state *state) ++{ ++ int srcPtr, dstPtr, i, num_ids; ++ static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff}; ++ const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7; ++ ++ put_checksum(&slot_command[0], slot_command[0]); ++ if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); ++ ++ // Print raw data ++ dprintk(verbose, DST_CA_INFO, 0, " DST data = ["); ++ for (i = 0; i < state->messages[0] + 1; i++) { ++ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]); ++ } ++ dprintk(verbose, DST_CA_INFO, 0, "]\n"); ++ ++ // Set the command and length of the output ++ num_ids = state->messages[in_num_ids_pos]; ++ if (num_ids >= 100) { ++ num_ids = 100; ++ dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering."); ++ } ++ put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2); ++ ++ dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = ["); ++ srcPtr = in_system_id_pos; ++ dstPtr = out_system_id_pos; ++ for(i = 0; i < num_ids; i++) { ++ dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]); ++ // Append to output ++ state->messages[dstPtr + 0] = state->messages[srcPtr + 0]; ++ state->messages[dstPtr + 1] = state->messages[srcPtr + 1]; ++ srcPtr += 2; ++ dstPtr += 2; ++ } ++ dprintk(verbose, DST_CA_INFO, 0, "]\n"); ++ ++ return 0; ++} ++ ++static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg) ++{ ++ int i; ++ u8 slot_cap[256]; ++ static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff}; ++ ++ put_checksum(&slot_command[0], slot_command[0]); ++ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !"); ++ ++ /* Will implement the rest soon */ ++ ++ dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]); ++ dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); ++ for (i = 0; i < slot_cap[0] + 1; i++) ++ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]); ++ dprintk(verbose, DST_CA_INFO, 0, "\n"); ++ ++ p_ca_caps->slot_num = 1; ++ p_ca_caps->slot_type = 1; ++ p_ca_caps->descr_num = slot_cap[7]; ++ p_ca_caps->descr_type = 1; ++ ++ if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* Need some more work */ ++static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) ++{ ++ return -EOPNOTSUPP; ++} ++ ++ ++static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg) ++{ ++ int i; ++ static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; ++ ++ u8 *slot_info = state->messages; ++ ++ put_checksum(&slot_command[0], 7); ++ if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); ++ ++ /* Will implement the rest soon */ ++ ++ dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]); ++ dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); ++ for (i = 0; i < 8; i++) ++ dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]); ++ dprintk(verbose, DST_CA_INFO, 0, "\n"); ++ ++ if (slot_info[4] & 0x80) { ++ p_ca_slot_info->flags = CA_CI_MODULE_PRESENT; ++ p_ca_slot_info->num = 1; ++ p_ca_slot_info->type = CA_CI; ++ } else if (slot_info[4] & 0x40) { ++ p_ca_slot_info->flags = CA_CI_MODULE_READY; ++ p_ca_slot_info->num = 1; ++ p_ca_slot_info->type = CA_CI; ++ } else ++ p_ca_slot_info->flags = 0; ++ ++ if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info))) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++ ++static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) ++{ ++ u8 i = 0; ++ u32 command = 0; ++ ++ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) ++ return -EFAULT; ++ ++ if (p_ca_message->msg) { ++ dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%*ph]", ++ 3, p_ca_message->msg); ++ ++ for (i = 0; i < 3; i++) { ++ command = command | p_ca_message->msg[i]; ++ if (i < 2) ++ command = command << 8; ++ } ++ dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); ++ ++ switch (command) { ++ case CA_APP_INFO: ++ memcpy(p_ca_message->msg, state->messages, 128); ++ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) ++ return -EFAULT; ++ break; ++ case CA_INFO: ++ memcpy(p_ca_message->msg, state->messages, 128); ++ if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) ++ return -EFAULT; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length) ++{ ++ if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) { ++ hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */ ++ hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */ ++ } else { ++ if (length > 247) { ++ dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !"); ++ return -1; ++ } ++ hw_buffer->msg[0] = (length & 0xff) + 7; ++ hw_buffer->msg[1] = 0x40; ++ hw_buffer->msg[2] = 0x03; ++ hw_buffer->msg[3] = 0x00; ++ hw_buffer->msg[4] = 0x03; ++ hw_buffer->msg[5] = length & 0xff; ++ hw_buffer->msg[6] = 0x00; ++ ++ /* ++ * Need to compute length for EN50221 section 8.3.2, for the time being ++ * assuming 8.3.2 is not applicable ++ */ ++ memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length); ++ } ++ ++ return 0; ++} ++ ++static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply) ++{ ++ if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed."); ++ dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST."); ++ rdc_reset_state(state); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success."); ++ ++ return 0; ++} ++ ++static u32 asn_1_decode(u8 *asn_1_array) ++{ ++ u8 length_field = 0, word_count = 0, count = 0; ++ u32 length = 0; ++ ++ length_field = asn_1_array[0]; ++ dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field); ++ if (length_field < 0x80) { ++ length = length_field & 0x7f; ++ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length); ++ } else { ++ word_count = length_field & 0x7f; ++ for (count = 0; count < word_count; count++) { ++ length = length << 8; ++ length += asn_1_array[count + 1]; ++ dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length); ++ } ++ } ++ return length; ++} ++ ++static int debug_string(u8 *msg, u32 length, u32 offset) ++{ ++ u32 i; ++ ++ dprintk(verbose, DST_CA_DEBUG, 0, " String=[ "); ++ for (i = offset; i < length; i++) ++ dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]); ++ dprintk(verbose, DST_CA_DEBUG, 0, "]\n"); ++ ++ return 0; ++} ++ ++ ++static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query) ++{ ++ u32 length = 0; ++ u8 tag_length = 8; ++ ++ length = asn_1_decode(&p_ca_message->msg[3]); ++ dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length); ++ debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */ ++ ++ memset(hw_buffer->msg, '\0', length); ++ handle_dst_tag(state, p_ca_message, hw_buffer, length); ++ put_checksum(hw_buffer->msg, hw_buffer->msg[0]); ++ ++ debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */ ++ write_to_8820(state, hw_buffer, (length + tag_length), reply); ++ ++ return 0; ++} ++ ++ ++/* Board supports CA PMT reply ? */ ++static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer) ++{ ++ int ca_pmt_reply_test = 0; ++ ++ /* Do test board */ ++ /* Not there yet but soon */ ++ ++ /* CA PMT Reply capable */ ++ if (ca_pmt_reply_test) { ++ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); ++ return -1; ++ } ++ ++ /* Process CA PMT Reply */ ++ /* will implement soon */ ++ dprintk(verbose, DST_CA_ERROR, 1, " Not there yet"); ++ } ++ /* CA PMT Reply not capable */ ++ if (!ca_pmt_reply_test) { ++ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); ++ return -1; ++ } ++ dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !"); ++ /* put a dummy message */ ++ ++ } ++ return 0; ++} ++ ++static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) ++{ ++ int i = 0; ++ ++ u32 command = 0; ++ struct ca_msg *hw_buffer; ++ int result = 0; ++ ++ if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) { ++ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); ++ return -ENOMEM; ++ } ++ dprintk(verbose, DST_CA_DEBUG, 1, " "); ++ ++ if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) { ++ result = -EFAULT; ++ goto free_mem_and_exit; ++ } ++ ++ ++ if (p_ca_message->msg) { ++ /* EN50221 tag */ ++ command = 0; ++ ++ for (i = 0; i < 3; i++) { ++ command = command | p_ca_message->msg[i]; ++ if (i < 2) ++ command = command << 8; ++ } ++ dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); ++ ++ switch (command) { ++ case CA_PMT: ++ dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); ++ if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); ++ break; ++ case CA_PMT_REPLY: ++ dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); ++ /* Have to handle the 2 basic types of cards here */ ++ if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); ++ break; ++ case CA_APP_INFO_ENQUIRY: // only for debugging ++ dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); ++ ++ if ((ca_get_app_info(state)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); ++ break; ++ case CA_INFO_ENQUIRY: ++ dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); ++ ++ if ((ca_get_ca_info(state)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); ++ break; ++ } ++ } ++free_mem_and_exit: ++ kfree (hw_buffer); ++ ++ return result; ++} ++ ++static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg) ++{ ++ struct dvb_device *dvbdev; ++ struct dst_state *state; ++ struct ca_slot_info *p_ca_slot_info; ++ struct ca_caps *p_ca_caps; ++ struct ca_msg *p_ca_message; ++ void __user *arg = (void __user *)ioctl_arg; ++ int result = 0; ++ ++ mutex_lock(&dst_ca_mutex); ++ dvbdev = file->private_data; ++ state = (struct dst_state *)dvbdev->priv; ++ p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); ++ p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); ++ p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL); ++ if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) { ++ dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); ++ result = -ENOMEM; ++ goto free_mem_and_exit; ++ } ++ ++ /* We have now only the standard ioctl's, the driver is upposed to handle internals. */ ++ switch (cmd) { ++ case CA_SEND_MSG: ++ dprintk(verbose, DST_CA_INFO, 1, " Sending message"); ++ if ((ca_send_message(state, p_ca_message, arg)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ break; ++ case CA_GET_MSG: ++ dprintk(verbose, DST_CA_INFO, 1, " Getting message"); ++ if ((ca_get_message(state, p_ca_message, arg)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !"); ++ break; ++ case CA_RESET: ++ dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST"); ++ dst_error_bailout(state); ++ msleep(4000); ++ break; ++ case CA_GET_SLOT_INFO: ++ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info"); ++ if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !"); ++ break; ++ case CA_GET_CAP: ++ dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities"); ++ if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !"); ++ break; ++ case CA_GET_DESCR_INFO: ++ dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description"); ++ if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !"); ++ break; ++ case CA_SET_DESCR: ++ dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler"); ++ if ((ca_set_slot_descr()) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !"); ++ break; ++ case CA_SET_PID: ++ dprintk(verbose, DST_CA_INFO, 1, " Setting PID"); ++ if ((ca_set_pid()) < 0) { ++ dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !"); ++ result = -1; ++ goto free_mem_and_exit; ++ } ++ dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); ++ default: ++ result = -EOPNOTSUPP; ++ } ++ free_mem_and_exit: ++ kfree (p_ca_message); ++ kfree (p_ca_slot_info); ++ kfree (p_ca_caps); ++ ++ mutex_unlock(&dst_ca_mutex); ++ return result; ++} ++ ++static int dst_ca_open(struct inode *inode, struct file *file) ++{ ++ dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); ++ try_module_get(THIS_MODULE); ++ ++ return 0; ++} ++ ++static int dst_ca_release(struct inode *inode, struct file *file) ++{ ++ dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); ++ module_put(THIS_MODULE); ++ ++ return 0; ++} ++ ++static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) ++{ ++ ssize_t bytes_read = 0; ++ ++ dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); ++ ++ return bytes_read; ++} ++ ++static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) ++{ ++ dprintk(verbose, DST_CA_DEBUG, 1, " Device write."); ++ ++ return 0; ++} ++ ++static const struct file_operations dst_ca_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = dst_ca_ioctl, ++ .open = dst_ca_open, ++ .release = dst_ca_release, ++ .read = dst_ca_read, ++ .write = dst_ca_write, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_ca = { ++ .priv = NULL, ++ .users = 1, ++ .readers = 1, ++ .writers = 1, ++ .fops = &dst_ca_fops ++}; ++ ++struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) ++{ ++ struct dvb_device *dvbdev; ++ ++ dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); ++ if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { ++ dst->dst_ca = dvbdev; ++ return dst->dst_ca; ++ } ++ ++ return NULL; ++} ++ ++EXPORT_SYMBOL(dst_ca_attach); ++ ++MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/bt8xx/dst_ca.h b/drivers/media/pci/bt8xx/dst_ca.h +new file mode 100644 +index 0000000..59cd0dd +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dst_ca.h +@@ -0,0 +1,58 @@ ++/* ++ CA-driver for TwinHan DST Frontend/Card ++ ++ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. ++*/ ++ ++#ifndef _DST_CA_H_ ++#define _DST_CA_H_ ++ ++#define RETRIES 5 ++ ++ ++#define CA_APP_INFO_ENQUIRY 0x9f8020 ++#define CA_APP_INFO 0x9f8021 ++#define CA_ENTER_MENU 0x9f8022 ++#define CA_INFO_ENQUIRY 0x9f8030 ++#define CA_INFO 0x9f8031 ++#define CA_PMT 0x9f8032 ++#define CA_PMT_REPLY 0x9f8033 ++ ++#define CA_CLOSE_MMI 0x9f8800 ++#define CA_DISPLAY_CONTROL 0x9f8801 ++#define CA_DISPLAY_REPLY 0x9f8802 ++#define CA_TEXT_LAST 0x9f8803 ++#define CA_TEXT_MORE 0x9f8804 ++#define CA_KEYPAD_CONTROL 0x9f8805 ++#define CA_KEYPRESS 0x9f8806 ++ ++#define CA_ENQUIRY 0x9f8807 ++#define CA_ANSWER 0x9f8808 ++#define CA_MENU_LAST 0x9f8809 ++#define CA_MENU_MORE 0x9f880a ++#define CA_MENU_ANSWER 0x9f880b ++#define CA_LIST_LAST 0x9f880c ++#define CA_LIST_MORE 0x9f880d ++ ++ ++struct dst_ca_private { ++ struct dst_state *dst; ++ struct dvb_device *dvbdev; ++}; ++ ++ ++#endif +diff --git a/drivers/media/pci/bt8xx/dst_common.h b/drivers/media/pci/bt8xx/dst_common.h +new file mode 100644 +index 0000000..d70d98f +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dst_common.h +@@ -0,0 +1,182 @@ ++/* ++ Frontend-driver for TwinHan DST Frontend ++ ++ Copyright (C) 2003 Jamie Honan ++ Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.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. ++*/ ++ ++#ifndef DST_COMMON_H ++#define DST_COMMON_H ++ ++#include ++#include ++#include ++#include "bt878.h" ++ ++#include "dst_ca.h" ++ ++ ++#define NO_DELAY 0 ++#define LONG_DELAY 1 ++#define DEVICE_INIT 2 ++ ++#define DELAY 1 ++ ++#define DST_TYPE_IS_SAT 0 ++#define DST_TYPE_IS_TERR 1 ++#define DST_TYPE_IS_CABLE 2 ++#define DST_TYPE_IS_ATSC 3 ++ ++#define DST_TYPE_HAS_TS188 1 ++#define DST_TYPE_HAS_TS204 2 ++#define DST_TYPE_HAS_SYMDIV 4 ++#define DST_TYPE_HAS_FW_1 8 ++#define DST_TYPE_HAS_FW_2 16 ++#define DST_TYPE_HAS_FW_3 32 ++#define DST_TYPE_HAS_FW_BUILD 64 ++#define DST_TYPE_HAS_OBS_REGS 128 ++#define DST_TYPE_HAS_INC_COUNT 256 ++#define DST_TYPE_HAS_MULTI_FE 512 ++#define DST_TYPE_HAS_NEWTUNE_2 1024 ++#define DST_TYPE_HAS_DBOARD 2048 ++#define DST_TYPE_HAS_VLF 4096 ++ ++/* Card capability list */ ++ ++#define DST_TYPE_HAS_MAC 1 ++#define DST_TYPE_HAS_DISEQC3 2 ++#define DST_TYPE_HAS_DISEQC4 4 ++#define DST_TYPE_HAS_DISEQC5 8 ++#define DST_TYPE_HAS_MOTO 16 ++#define DST_TYPE_HAS_CA 32 ++#define DST_TYPE_HAS_ANALOG 64 /* Analog inputs */ ++#define DST_TYPE_HAS_SESSION 128 ++ ++#define TUNER_TYPE_MULTI 1 ++#define TUNER_TYPE_UNKNOWN 2 ++/* DVB-S */ ++#define TUNER_TYPE_L64724 4 ++#define TUNER_TYPE_STV0299 8 ++#define TUNER_TYPE_MB86A15 16 ++ ++/* DVB-T */ ++#define TUNER_TYPE_TDA10046 32 ++ ++/* ATSC */ ++#define TUNER_TYPE_NXT200x 64 ++ ++ ++#define RDC_8820_PIO_0_DISABLE 0 ++#define RDC_8820_PIO_0_ENABLE 1 ++#define RDC_8820_INT 2 ++#define RDC_8820_RESET 4 ++ ++/* DST Communication */ ++#define GET_REPLY 1 ++#define NO_REPLY 0 ++ ++#define GET_ACK 1 ++#define FIXED_COMM 8 ++ ++#define ACK 0xff ++ ++struct dst_state { ++ ++ struct i2c_adapter* i2c; ++ ++ struct bt878* bt; ++ ++ /* configuration settings */ ++ const struct dst_config* config; ++ ++ struct dvb_frontend frontend; ++ ++ /* private ASIC data */ ++ u8 tx_tuna[10]; ++ u8 rx_tuna[10]; ++ u8 rxbuffer[10]; ++ u8 diseq_flags; ++ u8 dst_type; ++ u32 type_flags; ++ u32 frequency; /* intermediate frequency in kHz for QPSK */ ++ fe_spectral_inversion_t inversion; ++ u32 symbol_rate; /* symbol rate in Symbols per second */ ++ fe_code_rate_t fec; ++ fe_sec_voltage_t voltage; ++ fe_sec_tone_mode_t tone; ++ u32 decode_freq; ++ u8 decode_lock; ++ u16 decode_strength; ++ u16 decode_snr; ++ unsigned long cur_jiff; ++ u8 k22; ++ u32 bandwidth; ++ u32 dst_hw_cap; ++ u8 dst_fw_version; ++ fe_sec_mini_cmd_t minicmd; ++ fe_modulation_t modulation; ++ u8 messages[256]; ++ u8 mac_address[8]; ++ u8 fw_version[8]; ++ u8 card_info[8]; ++ u8 vendor[8]; ++ u8 board_info[8]; ++ u32 tuner_type; ++ char *tuner_name; ++ struct mutex dst_mutex; ++ u8 fw_name[8]; ++ struct dvb_device *dst_ca; ++}; ++ ++struct tuner_types { ++ u32 tuner_type; ++ char *tuner_name; ++ char *board_name; ++ char *fw_name; ++}; ++ ++struct dst_types { ++ char *device_id; ++ int offset; ++ u8 dst_type; ++ u32 type_flags; ++ u32 dst_feature; ++ u32 tuner_type; ++}; ++ ++struct dst_config ++{ ++ /* the ASIC i2c address */ ++ u8 demod_address; ++}; ++ ++int rdc_reset_state(struct dst_state *state); ++ ++int dst_wait_dst_ready(struct dst_state *state, u8 delay_mode); ++int dst_pio_disable(struct dst_state *state); ++int dst_error_recovery(struct dst_state* state); ++int dst_error_bailout(struct dst_state *state); ++int dst_comm_init(struct dst_state* state); ++ ++int write_dst(struct dst_state *state, u8 * data, u8 len); ++int read_dst(struct dst_state *state, u8 * ret, u8 len); ++u8 dst_check_sum(u8 * buf, u32 len); ++struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter); ++struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter); ++ ++ ++#endif // DST_COMMON_H +diff --git a/drivers/media/pci/bt8xx/dst_priv.h b/drivers/media/pci/bt8xx/dst_priv.h +new file mode 100644 +index 0000000..3974a4c +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dst_priv.h +@@ -0,0 +1,35 @@ ++/* ++ * dst-bt878.h: part of the DST driver for the TwinHan DST Frontend ++ * ++ * Copyright (C) 2003 Jamie Honan ++ */ ++ ++struct dst_gpio_enable { ++ u32 mask; ++ u32 enable; ++}; ++ ++struct dst_gpio_output { ++ u32 mask; ++ u32 highvals; ++}; ++ ++struct dst_gpio_read { ++ unsigned long value; ++}; ++ ++union dst_gpio_packet { ++ struct dst_gpio_enable enb; ++ struct dst_gpio_output outp; ++ struct dst_gpio_read rd; ++ int psize; ++}; ++ ++#define DST_IG_ENABLE 0 ++#define DST_IG_WRITE 1 ++#define DST_IG_READ 2 ++#define DST_IG_TS 3 ++ ++struct bt878; ++ ++int bt878_device_control(struct bt878 *bt, unsigned int cmd, union dst_gpio_packet *mp); +diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c +new file mode 100644 +index 0000000..c919b9e +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c +@@ -0,0 +1,976 @@ ++/* ++ * Bt8xx based DVB adapter driver ++ * ++ * Copyright (C) 2002,2003 Florian Schirmer ++ * ++ * 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. ++ * ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) "dvb_bt8xx: " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb-bt8xx.h" ++#include "bt878.h" ++ ++static int debug; ++ ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define dprintk( args... ) \ ++ do { \ ++ if (debug) printk(KERN_DEBUG args); \ ++ } while (0) ++ ++#define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ ++ ++static void dvb_bt8xx_task(unsigned long data) ++{ ++ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *)data; ++ ++ //printk("%d ", card->bt->finished_block); ++ ++ while (card->bt->last_block != card->bt->finished_block) { ++ (card->bt->TS_Size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) ++ (&card->demux, ++ &card->bt->buf_cpu[card->bt->last_block * ++ card->bt->block_bytes], ++ card->bt->block_bytes); ++ card->bt->last_block = (card->bt->last_block + 1) % ++ card->bt->block_count; ++ } ++} ++ ++static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux*dvbdmx = dvbdmxfeed->demux; ++ struct dvb_bt8xx_card *card = dvbdmx->priv; ++ int rc; ++ ++ dprintk("dvb_bt8xx: start_feed\n"); ++ ++ if (!dvbdmx->dmx.frontend) ++ return -EINVAL; ++ ++ mutex_lock(&card->lock); ++ card->nfeeds++; ++ rc = card->nfeeds; ++ if (card->nfeeds == 1) ++ bt878_start(card->bt, card->gpio_mode, ++ card->op_sync_orin, card->irq_err_ignore); ++ mutex_unlock(&card->lock); ++ return rc; ++} ++ ++static int dvb_bt8xx_stop_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct dvb_bt8xx_card *card = dvbdmx->priv; ++ ++ dprintk("dvb_bt8xx: stop_feed\n"); ++ ++ if (!dvbdmx->dmx.frontend) ++ return -EINVAL; ++ ++ mutex_lock(&card->lock); ++ card->nfeeds--; ++ if (card->nfeeds == 0) ++ bt878_stop(card->bt); ++ mutex_unlock(&card->lock); ++ ++ return 0; ++} ++ ++static int is_pci_slot_eq(struct pci_dev* adev, struct pci_dev* bdev) ++{ ++ if ((adev->subsystem_vendor == bdev->subsystem_vendor) && ++ (adev->subsystem_device == bdev->subsystem_device) && ++ (adev->bus->number == bdev->bus->number) && ++ (PCI_SLOT(adev->devfn) == PCI_SLOT(bdev->devfn))) ++ return 1; ++ return 0; ++} ++ ++static struct bt878 __devinit *dvb_bt8xx_878_match(unsigned int bttv_nr, struct pci_dev* bttv_pci_dev) ++{ ++ unsigned int card_nr; ++ ++ /* Hmm, n squared. Hope n is small */ ++ for (card_nr = 0; card_nr < bt878_num; card_nr++) ++ if (is_pci_slot_eq(bt878[card_nr].dev, bttv_pci_dev)) ++ return &bt878[card_nr]; ++ return NULL; ++} ++ ++static int thomson_dtt7579_demod_init(struct dvb_frontend* fe) ++{ ++ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x38 }; ++ static u8 mt352_reset [] = { 0x50, 0x80 }; ++ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; ++ static u8 mt352_agc_cfg [] = { 0x67, 0x28, 0x20 }; ++ static u8 mt352_gpp_ctl_cfg [] = { 0x8C, 0x33 }; ++ 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_gpp_ctl_cfg, sizeof(mt352_gpp_ctl_cfg)); ++ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); ++ ++ return 0; ++} ++ ++static int thomson_dtt7579_tuner_calc_regs(struct dvb_frontend *fe, u8* pllbuf, int buf_len) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 div; ++ unsigned char bs = 0; ++ unsigned char cp = 0; ++ ++ if (buf_len < 5) ++ return -EINVAL; ++ ++ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; ++ ++ if (c->frequency < 542000000) ++ cp = 0xb4; ++ else if (c->frequency < 771000000) ++ cp = 0xbc; ++ else ++ cp = 0xf4; ++ ++ if (c->frequency == 0) ++ bs = 0x03; ++ else if (c->frequency < 443250000) ++ bs = 0x02; ++ else ++ bs = 0x08; ++ ++ pllbuf[0] = 0x60; ++ pllbuf[1] = div >> 8; ++ pllbuf[2] = div & 0xff; ++ pllbuf[3] = cp; ++ pllbuf[4] = bs; ++ ++ return 5; ++} ++ ++static struct mt352_config thomson_dtt7579_config = { ++ .demod_address = 0x0f, ++ .demod_init = thomson_dtt7579_demod_init, ++}; ++ ++static struct zl10353_config thomson_dtt7579_zl10353_config = { ++ .demod_address = 0x0f, ++}; ++ ++static int cx24108_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 freq = c->frequency; ++ int i, a, n, pump; ++ u32 band, pll; ++ u32 osci[]={950000,1019000,1075000,1178000,1296000,1432000, ++ 1576000,1718000,1856000,2036000,2150000}; ++ u32 bandsel[]={0,0x00020000,0x00040000,0x00100800,0x00101000, ++ 0x00102000,0x00104000,0x00108000,0x00110000, ++ 0x00120000,0x00140000}; ++ ++ #define XTAL 1011100 /* Hz, really 1.0111 MHz and a /10 prescaler */ ++ dprintk("cx24108 debug: entering SetTunerFreq, freq=%d\n", freq); ++ ++ /* This is really the bit driving the tuner chip cx24108 */ ++ ++ if (freq<950000) ++ freq = 950000; /* kHz */ ++ else if (freq>2150000) ++ freq = 2150000; /* satellite IF is 950..2150MHz */ ++ ++ /* decide which VCO to use for the input frequency */ ++ for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); ++ dprintk("cx24108 debug: select vco #%d (f=%d)\n", i, freq); ++ band=bandsel[i]; ++ /* the gain values must be set by SetSymbolrate */ ++ /* compute the pll divider needed, from Conexant data sheet, ++ resolved for (n*32+a), remember f(vco) is f(receive) *2 or *4, ++ depending on the divider bit. It is set to /4 on the 2 lowest ++ bands */ ++ n=((i<=2?2:1)*freq*10L)/(XTAL/100); ++ a=n%32; n/=32; if(a==0) n--; ++ pump=(freq<(osci[i-1]+osci[i])/2); ++ pll=0xf8000000| ++ ((pump?1:2)<<(14+11))| ++ ((n&0x1ff)<<(5+11))| ++ ((a&0x1f)<<11); ++ /* everything is shifted left 11 bits to left-align the bits in the ++ 32bit word. Output to the tuner goes MSB-aligned, after all */ ++ dprintk("cx24108 debug: pump=%d, n=%d, a=%d\n", pump, n, a); ++ cx24110_pll_write(fe,band); ++ /* set vga and vca to their widest-band settings, as a precaution. ++ SetSymbolrate might not be called to set this up */ ++ cx24110_pll_write(fe,0x500c0000); ++ cx24110_pll_write(fe,0x83f1f800); ++ cx24110_pll_write(fe,pll); ++ //writereg(client,0x56,0x7f); ++ ++ return 0; ++} ++ ++static int pinnsat_tuner_init(struct dvb_frontend* fe) ++{ ++ struct dvb_bt8xx_card *card = fe->dvb->priv; ++ ++ bttv_gpio_enable(card->bttv_nr, 1, 1); /* output */ ++ bttv_write_gpio(card->bttv_nr, 1, 1); /* relay on */ ++ ++ return 0; ++} ++ ++static int pinnsat_tuner_sleep(struct dvb_frontend* fe) ++{ ++ struct dvb_bt8xx_card *card = fe->dvb->priv; ++ ++ bttv_write_gpio(card->bttv_nr, 1, 0); /* relay off */ ++ ++ return 0; ++} ++ ++static struct cx24110_config pctvsat_config = { ++ .demod_address = 0x55, ++}; ++ ++static int microtune_mt7202dtf_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; ++ u8 cfg, cpump, band_select; ++ u8 data[4]; ++ u32 div; ++ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (36000000 + c->frequency + 83333) / 166666; ++ cfg = 0x88; ++ ++ if (c->frequency < 175000000) ++ cpump = 2; ++ else if (c->frequency < 390000000) ++ cpump = 1; ++ else if (c->frequency < 470000000) ++ cpump = 2; ++ else if (c->frequency < 750000000) ++ cpump = 2; ++ else ++ cpump = 3; ++ ++ if (c->frequency < 175000000) ++ band_select = 0x0e; ++ else if (c->frequency < 470000000) ++ band_select = 0x05; ++ else ++ band_select = 0x03; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = ((div >> 10) & 0x60) | cfg; ++ data[3] = (cpump << 6) | band_select; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(card->i2c_adapter, &msg, 1); ++ return (div * 166666 - 36000000); ++} ++ ++static int microtune_mt7202dtf_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) ++{ ++ struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; ++ ++ return request_firmware(fw, name, &bt->bt->dev->dev); ++} ++ ++static struct sp887x_config microtune_mt7202dtf_config = { ++ .demod_address = 0x70, ++ .request_firmware = microtune_mt7202dtf_request_firmware, ++}; ++ ++static int advbt771_samsung_tdtc9251dh0_demod_init(struct dvb_frontend* fe) ++{ ++ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; ++ static u8 mt352_reset [] = { 0x50, 0x80 }; ++ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; ++ static u8 mt352_agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, ++ 0x00, 0xFF, 0x00, 0x40, 0x40 }; ++ static u8 mt352_av771_extra[] = { 0xB5, 0x7A }; ++ 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)); ++ udelay(2000); ++ mt352_write(fe, mt352_av771_extra,sizeof(mt352_av771_extra)); ++ mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg)); ++ ++ return 0; ++} ++ ++static int advbt771_samsung_tdtc9251dh0_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 div; ++ unsigned char bs = 0; ++ unsigned char cp = 0; ++ ++ if (buf_len < 5) return -EINVAL; ++ ++ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; ++ ++ if (c->frequency < 150000000) ++ cp = 0xB4; ++ else if (c->frequency < 173000000) ++ cp = 0xBC; ++ else if (c->frequency < 250000000) ++ cp = 0xB4; ++ else if (c->frequency < 400000000) ++ cp = 0xBC; ++ else if (c->frequency < 420000000) ++ cp = 0xF4; ++ else if (c->frequency < 470000000) ++ cp = 0xFC; ++ else if (c->frequency < 600000000) ++ cp = 0xBC; ++ else if (c->frequency < 730000000) ++ cp = 0xF4; ++ else ++ cp = 0xFC; ++ ++ if (c->frequency < 150000000) ++ bs = 0x01; ++ else if (c->frequency < 173000000) ++ bs = 0x01; ++ else if (c->frequency < 250000000) ++ bs = 0x02; ++ else if (c->frequency < 400000000) ++ bs = 0x02; ++ else if (c->frequency < 420000000) ++ bs = 0x02; ++ else if (c->frequency < 470000000) ++ bs = 0x02; ++ else if (c->frequency < 600000000) ++ bs = 0x08; ++ else if (c->frequency < 730000000) ++ bs = 0x08; ++ else ++ bs = 0x08; ++ ++ pllbuf[0] = 0x61; ++ pllbuf[1] = div >> 8; ++ pllbuf[2] = div & 0xff; ++ pllbuf[3] = cp; ++ pllbuf[4] = bs; ++ ++ return 5; ++} ++ ++static struct mt352_config advbt771_samsung_tdtc9251dh0_config = { ++ .demod_address = 0x0f, ++ .demod_init = advbt771_samsung_tdtc9251dh0_demod_init, ++}; ++ ++static struct dst_config dst_config = { ++ .demod_address = 0x55, ++}; ++ ++static int or51211_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) ++{ ++ struct dvb_bt8xx_card* bt = (struct dvb_bt8xx_card*) fe->dvb->priv; ++ ++ return request_firmware(fw, name, &bt->bt->dev->dev); ++} ++ ++static void or51211_setmode(struct dvb_frontend * fe, int mode) ++{ ++ struct dvb_bt8xx_card *bt = fe->dvb->priv; ++ bttv_write_gpio(bt->bttv_nr, 0x0002, mode); /* Reset */ ++ msleep(20); ++} ++ ++static void or51211_reset(struct dvb_frontend * fe) ++{ ++ struct dvb_bt8xx_card *bt = fe->dvb->priv; ++ ++ /* RESET DEVICE ++ * reset is controlled by GPIO-0 ++ * when set to 0 causes reset and when to 1 for normal op ++ * must remain reset for 128 clock cycles on a 50Mhz clock ++ * also PRM1 PRM2 & PRM4 are controlled by GPIO-1,GPIO-2 & GPIO-4 ++ * We assume that the reset has be held low long enough or we ++ * have been reset by a power on. When the driver is unloaded ++ * reset set to 0 so if reloaded we have been reset. ++ */ ++ /* reset & PRM1,2&4 are outputs */ ++ int ret = bttv_gpio_enable(bt->bttv_nr, 0x001F, 0x001F); ++ if (ret != 0) ++ printk(KERN_WARNING "or51211: Init Error - Can't Reset DVR (%i)\n", ret); ++ bttv_write_gpio(bt->bttv_nr, 0x001F, 0x0000); /* Reset */ ++ msleep(20); ++ /* Now set for normal operation */ ++ bttv_write_gpio(bt->bttv_nr, 0x0001F, 0x0001); ++ /* wait for operation to begin */ ++ msleep(500); ++} ++ ++static void or51211_sleep(struct dvb_frontend * fe) ++{ ++ struct dvb_bt8xx_card *bt = fe->dvb->priv; ++ bttv_write_gpio(bt->bttv_nr, 0x0001, 0x0000); ++} ++ ++static struct or51211_config or51211_config = { ++ .demod_address = 0x15, ++ .request_firmware = or51211_request_firmware, ++ .setmode = or51211_setmode, ++ .reset = or51211_reset, ++ .sleep = or51211_sleep, ++}; ++ ++static int vp3021_alps_tded4_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; ++ u8 buf[4]; ++ u32 div; ++ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf) }; ++ ++ div = (c->frequency + 36166667) / 166667; ++ ++ buf[0] = (div >> 8) & 0x7F; ++ buf[1] = div & 0xFF; ++ buf[2] = 0x85; ++ if ((c->frequency >= 47000000) && (c->frequency < 153000000)) ++ buf[3] = 0x01; ++ else if ((c->frequency >= 153000000) && (c->frequency < 430000000)) ++ buf[3] = 0x02; ++ else if ((c->frequency >= 430000000) && (c->frequency < 824000000)) ++ buf[3] = 0x0C; ++ else if ((c->frequency >= 824000000) && (c->frequency < 863000000)) ++ buf[3] = 0x8C; ++ else ++ return -EINVAL; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(card->i2c_adapter, &msg, 1); ++ return 0; ++} ++ ++static struct nxt6000_config vp3021_alps_tded4_config = { ++ .demod_address = 0x0a, ++ .clock_inversion = 1, ++}; ++ ++static int digitv_alps_tded4_demod_init(struct dvb_frontend* fe) ++{ ++ static u8 mt352_clock_config [] = { 0x89, 0x38, 0x2d }; ++ static u8 mt352_reset [] = { 0x50, 0x80 }; ++ static u8 mt352_adc_ctl_1_cfg [] = { 0x8E, 0x40 }; ++ static u8 mt352_agc_cfg [] = { 0x67, 0x20, 0xa0 }; ++ 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 int digitv_alps_tded4_tuner_calc_regs(struct dvb_frontend *fe, u8 *pllbuf, int buf_len) ++{ ++ u32 div; ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ ++ if (buf_len < 5) ++ return -EINVAL; ++ ++ div = (((c->frequency + 83333) * 3) / 500000) + IF_FREQUENCYx6; ++ ++ pllbuf[0] = 0x61; ++ pllbuf[1] = (div >> 8) & 0x7F; ++ pllbuf[2] = div & 0xFF; ++ pllbuf[3] = 0x85; ++ ++ dprintk("frequency %u, div %u\n", c->frequency, div); ++ ++ if (c->frequency < 470000000) ++ pllbuf[4] = 0x02; ++ else if (c->frequency > 823000000) ++ pllbuf[4] = 0x88; ++ else ++ pllbuf[4] = 0x08; ++ ++ if (c->bandwidth_hz == 8000000) ++ pllbuf[4] |= 0x04; ++ ++ return 5; ++} ++ ++static void digitv_alps_tded4_reset(struct dvb_bt8xx_card *bt) ++{ ++ /* ++ * Reset the frontend, must be called before trying ++ * to initialise the MT352 or mt352_attach ++ * will fail. Same goes for the nxt6000 frontend. ++ * ++ */ ++ ++ int ret = bttv_gpio_enable(bt->bttv_nr, 0x08, 0x08); ++ if (ret != 0) ++ printk(KERN_WARNING "digitv_alps_tded4: Init Error - Can't Reset DVR (%i)\n", ret); ++ ++ /* Pulse the reset line */ ++ bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ ++ bttv_write_gpio(bt->bttv_nr, 0x08, 0x00); /* Low */ ++ msleep(100); ++ ++ bttv_write_gpio(bt->bttv_nr, 0x08, 0x08); /* High */ ++} ++ ++static struct mt352_config digitv_alps_tded4_config = { ++ .demod_address = 0x0a, ++ .demod_init = digitv_alps_tded4_demod_init, ++}; ++ ++static struct lgdt330x_config tdvs_tua6034_config = { ++ .demod_address = 0x0e, ++ .demod_chip = LGDT3303, ++ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ ++}; ++ ++static void lgdt330x_reset(struct dvb_bt8xx_card *bt) ++{ ++ /* Set pin 27 of the lgdt3303 chip high to reset the frontend */ ++ ++ /* Pulse the reset line */ ++ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ ++ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000000); /* Low */ ++ msleep(100); ++ ++ bttv_write_gpio(bt->bttv_nr, 0x00e00007, 0x00000001); /* High */ ++ msleep(100); ++} ++ ++static void frontend_init(struct dvb_bt8xx_card *card, u32 type) ++{ ++ struct dst_state* state = NULL; ++ ++ switch(type) { ++ case BTTV_BOARD_DVICO_DVBT_LITE: ++ card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter); ++ ++ if (card->fe == NULL) ++ card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config, ++ card->i2c_adapter); ++ ++ if (card->fe != NULL) { ++ card->fe->ops.tuner_ops.calc_regs = thomson_dtt7579_tuner_calc_regs; ++ card->fe->ops.info.frequency_min = 174000000; ++ card->fe->ops.info.frequency_max = 862000000; ++ } ++ break; ++ ++ case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: ++ lgdt330x_reset(card); ++ card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); ++ if (card->fe != NULL) { ++ dvb_attach(simple_tuner_attach, card->fe, ++ card->i2c_adapter, 0x61, ++ TUNER_LG_TDVS_H06XF); ++ dprintk ("dvb_bt8xx: lgdt330x detected\n"); ++ } ++ break; ++ ++ case BTTV_BOARD_NEBULA_DIGITV: ++ /* ++ * It is possible to determine the correct frontend using the I2C bus (see the Nebula SDK); ++ * this would be a cleaner solution than trying each frontend in turn. ++ */ ++ ++ /* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */ ++ digitv_alps_tded4_reset(card); ++ card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter); ++ if (card->fe != NULL) { ++ card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params; ++ dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n"); ++ break; ++ } ++ ++ /* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */ ++ digitv_alps_tded4_reset(card); ++ card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter); ++ ++ if (card->fe != NULL) { ++ card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs; ++ dprintk ("dvb_bt8xx: an mt352 was detected on your digitv card\n"); ++ } ++ break; ++ ++ case BTTV_BOARD_AVDVBT_761: ++ card->fe = dvb_attach(sp887x_attach, µtune_mt7202dtf_config, card->i2c_adapter); ++ if (card->fe) { ++ card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params; ++ } ++ break; ++ ++ case BTTV_BOARD_AVDVBT_771: ++ card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter); ++ if (card->fe != NULL) { ++ card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs; ++ card->fe->ops.info.frequency_min = 174000000; ++ card->fe->ops.info.frequency_max = 862000000; ++ } ++ break; ++ ++ case BTTV_BOARD_TWINHAN_DST: ++ /* DST is not a frontend driver !!! */ ++ state = kmalloc(sizeof (struct dst_state), GFP_KERNEL); ++ if (!state) { ++ pr_err("No memory\n"); ++ break; ++ } ++ /* Setup the Card */ ++ state->config = &dst_config; ++ state->i2c = card->i2c_adapter; ++ state->bt = card->bt; ++ state->dst_ca = NULL; ++ /* DST is not a frontend, attaching the ASIC */ ++ if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) { ++ pr_err("%s: Could not find a Twinhan DST\n", __func__); ++ break; ++ } ++ /* Attach other DST peripherals if any */ ++ /* Conditional Access device */ ++ card->fe = &state->frontend; ++ if (state->dst_hw_cap & DST_TYPE_HAS_CA) ++ dvb_attach(dst_ca_attach, state, &card->dvb_adapter); ++ break; ++ ++ case BTTV_BOARD_PINNACLESAT: ++ card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter); ++ if (card->fe) { ++ card->fe->ops.tuner_ops.init = pinnsat_tuner_init; ++ card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep; ++ card->fe->ops.tuner_ops.set_params = cx24108_tuner_set_params; ++ } ++ break; ++ ++ case BTTV_BOARD_PC_HDTV: ++ card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); ++ if (card->fe != NULL) ++ dvb_attach(simple_tuner_attach, card->fe, ++ card->i2c_adapter, 0x61, ++ TUNER_PHILIPS_FCV1236D); ++ break; ++ } ++ ++ if (card->fe == NULL) ++ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ card->bt->dev->vendor, ++ card->bt->dev->device, ++ card->bt->dev->subsystem_vendor, ++ card->bt->dev->subsystem_device); ++ else ++ if (dvb_register_frontend(&card->dvb_adapter, card->fe)) { ++ pr_err("Frontend registration failed!\n"); ++ dvb_frontend_detach(card->fe); ++ card->fe = NULL; ++ } ++} ++ ++static int __devinit dvb_bt8xx_load_card(struct dvb_bt8xx_card *card, u32 type) ++{ ++ int result; ++ ++ result = dvb_register_adapter(&card->dvb_adapter, card->card_name, ++ THIS_MODULE, &card->bt->dev->dev, ++ adapter_nr); ++ if (result < 0) { ++ pr_err("dvb_register_adapter failed (errno = %d)\n", result); ++ return result; ++ } ++ card->dvb_adapter.priv = card; ++ ++ card->bt->adapter = card->i2c_adapter; ++ ++ memset(&card->demux, 0, sizeof(struct dvb_demux)); ++ ++ card->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING; ++ ++ card->demux.priv = card; ++ card->demux.filternum = 256; ++ card->demux.feednum = 256; ++ card->demux.start_feed = dvb_bt8xx_start_feed; ++ card->demux.stop_feed = dvb_bt8xx_stop_feed; ++ card->demux.write_to_decoder = NULL; ++ ++ result = dvb_dmx_init(&card->demux); ++ if (result < 0) { ++ pr_err("dvb_dmx_init failed (errno = %d)\n", result); ++ goto err_unregister_adaptor; ++ } ++ ++ card->dmxdev.filternum = 256; ++ card->dmxdev.demux = &card->demux.dmx; ++ card->dmxdev.capabilities = 0; ++ ++ result = dvb_dmxdev_init(&card->dmxdev, &card->dvb_adapter); ++ if (result < 0) { ++ pr_err("dvb_dmxdev_init failed (errno = %d)\n", result); ++ goto err_dmx_release; ++ } ++ ++ card->fe_hw.source = DMX_FRONTEND_0; ++ ++ result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_hw); ++ if (result < 0) { ++ pr_err("dvb_dmx_init failed (errno = %d)\n", result); ++ goto err_dmxdev_release; ++ } ++ ++ card->fe_mem.source = DMX_MEMORY_FE; ++ ++ result = card->demux.dmx.add_frontend(&card->demux.dmx, &card->fe_mem); ++ if (result < 0) { ++ pr_err("dvb_dmx_init failed (errno = %d)\n", result); ++ goto err_remove_hw_frontend; ++ } ++ ++ result = card->demux.dmx.connect_frontend(&card->demux.dmx, &card->fe_hw); ++ if (result < 0) { ++ pr_err("dvb_dmx_init failed (errno = %d)\n", result); ++ goto err_remove_mem_frontend; ++ } ++ ++ result = dvb_net_init(&card->dvb_adapter, &card->dvbnet, &card->demux.dmx); ++ if (result < 0) { ++ pr_err("dvb_net_init failed (errno = %d)\n", result); ++ goto err_disconnect_frontend; ++ } ++ ++ tasklet_init(&card->bt->tasklet, dvb_bt8xx_task, (unsigned long) card); ++ ++ frontend_init(card, type); ++ ++ return 0; ++ ++err_disconnect_frontend: ++ card->demux.dmx.disconnect_frontend(&card->demux.dmx); ++err_remove_mem_frontend: ++ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); ++err_remove_hw_frontend: ++ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); ++err_dmxdev_release: ++ dvb_dmxdev_release(&card->dmxdev); ++err_dmx_release: ++ dvb_dmx_release(&card->demux); ++err_unregister_adaptor: ++ dvb_unregister_adapter(&card->dvb_adapter); ++ return result; ++} ++ ++static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub) ++{ ++ struct dvb_bt8xx_card *card; ++ struct pci_dev* bttv_pci_dev; ++ int ret; ++ ++ if (!(card = kzalloc(sizeof(struct dvb_bt8xx_card), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ mutex_init(&card->lock); ++ card->bttv_nr = sub->core->nr; ++ strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name)); ++ card->i2c_adapter = &sub->core->i2c_adap; ++ ++ switch(sub->core->type) { ++ case BTTV_BOARD_PINNACLESAT: ++ card->gpio_mode = 0x0400c060; ++ /* should be: BT878_A_GAIN=0,BT878_A_PWRDN,BT878_DA_DPM,BT878_DA_SBR, ++ BT878_DA_IOM=1,BT878_DA_APP to enable serial highspeed mode. */ ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ break; ++ ++ case BTTV_BOARD_DVICO_DVBT_LITE: ++ card->gpio_mode = 0x0400C060; ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ /* 26, 15, 14, 6, 5 ++ * A_PWRDN DA_DPM DA_SBR DA_IOM_DA ++ * DA_APP(parallel) */ ++ break; ++ ++ case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE: ++ card->gpio_mode = 0x0400c060; ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ break; ++ ++ case BTTV_BOARD_NEBULA_DIGITV: ++ case BTTV_BOARD_AVDVBT_761: ++ card->gpio_mode = (1 << 26) | (1 << 14) | (1 << 5); ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ /* A_PWRDN DA_SBR DA_APP (high speed serial) */ ++ break; ++ ++ case BTTV_BOARD_AVDVBT_771: //case 0x07711461: ++ card->gpio_mode = 0x0400402B; ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ /* A_PWRDN DA_SBR DA_APP[0] PKTP=10 RISC_ENABLE FIFO_ENABLE*/ ++ break; ++ ++ case BTTV_BOARD_TWINHAN_DST: ++ card->gpio_mode = 0x2204f2c; ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_APABORT | BT878_ARIPERR | ++ BT878_APPERR | BT878_AFBUS; ++ /* 25,21,14,11,10,9,8,3,2 then ++ * 0x33 = 5,4,1,0 ++ * A_SEL=SML, DA_MLB, DA_SBR, ++ * DA_SDR=f, fifo trigger = 32 DWORDS ++ * IOM = 0 == audio A/D ++ * DPM = 0 == digital audio mode ++ * == async data parallel port ++ * then 0x33 (13 is set by start_capture) ++ * DA_APP = async data parallel port, ++ * ACAP_EN = 1, ++ * RISC+FIFO ENABLE */ ++ break; ++ ++ case BTTV_BOARD_PC_HDTV: ++ card->gpio_mode = 0x0100EC7B; ++ card->op_sync_orin = BT878_RISC_SYNC_MASK; ++ card->irq_err_ignore = BT878_AFBUS | BT878_AFDSR; ++ break; ++ ++ default: ++ pr_err("Unknown bttv card type: %d\n", sub->core->type); ++ kfree(card); ++ return -ENODEV; ++ } ++ ++ dprintk("dvb_bt8xx: identified card%d as %s\n", card->bttv_nr, card->card_name); ++ ++ if (!(bttv_pci_dev = bttv_get_pcidev(card->bttv_nr))) { ++ pr_err("no pci device for card %d\n", card->bttv_nr); ++ kfree(card); ++ return -ENODEV; ++ } ++ ++ if (!(card->bt = dvb_bt8xx_878_match(card->bttv_nr, bttv_pci_dev))) { ++ pr_err("unable to determine DMA core of card %d,\n", card->bttv_nr); ++ pr_err("if you have the ALSA bt87x audio driver installed, try removing it.\n"); ++ ++ kfree(card); ++ return -ENODEV; ++ } ++ ++ mutex_init(&card->bt->gpio_lock); ++ card->bt->bttv_nr = sub->core->nr; ++ ++ if ( (ret = dvb_bt8xx_load_card(card, sub->core->type)) ) { ++ kfree(card); ++ return ret; ++ } ++ ++ dev_set_drvdata(&sub->dev, card); ++ return 0; ++} ++ ++static void dvb_bt8xx_remove(struct bttv_sub_device *sub) ++{ ++ struct dvb_bt8xx_card *card = dev_get_drvdata(&sub->dev); ++ ++ dprintk("dvb_bt8xx: unloading card%d\n", card->bttv_nr); ++ ++ bt878_stop(card->bt); ++ tasklet_kill(&card->bt->tasklet); ++ dvb_net_release(&card->dvbnet); ++ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_mem); ++ card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw); ++ dvb_dmxdev_release(&card->dmxdev); ++ dvb_dmx_release(&card->demux); ++ if (card->fe) { ++ dvb_unregister_frontend(card->fe); ++ dvb_frontend_detach(card->fe); ++ } ++ dvb_unregister_adapter(&card->dvb_adapter); ++ ++ kfree(card); ++} ++ ++static struct bttv_sub_driver driver = { ++ .drv = { ++ .name = "dvb-bt8xx", ++ }, ++ .probe = dvb_bt8xx_probe, ++ .remove = dvb_bt8xx_remove, ++ /* FIXME: ++ * .shutdown = dvb_bt8xx_shutdown, ++ * .suspend = dvb_bt8xx_suspend, ++ * .resume = dvb_bt8xx_resume, ++ */ ++}; ++ ++static int __init dvb_bt8xx_init(void) ++{ ++ return bttv_sub_register(&driver, "dvb"); ++} ++ ++static void __exit dvb_bt8xx_exit(void) ++{ ++ bttv_sub_unregister(&driver); ++} ++ ++module_init(dvb_bt8xx_init); ++module_exit(dvb_bt8xx_exit); ++ ++MODULE_DESCRIPTION("Bt8xx based DVB adapter driver"); ++MODULE_AUTHOR("Florian Schirmer "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.h b/drivers/media/pci/bt8xx/dvb-bt8xx.h +new file mode 100644 +index 0000000..4499ed2 +--- /dev/null ++++ b/drivers/media/pci/bt8xx/dvb-bt8xx.h +@@ -0,0 +1,63 @@ ++/* ++ * Bt8xx based DVB adapter driver ++ * ++ * Copyright (C) 2002,2003 Florian Schirmer ++ * Copyright (C) 2002 Peter Hettkamp ++ * Copyright (C) 1999-2001 Ralph Metzler & Marcus Metzler for convergence integrated media GmbH ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#ifndef DVB_BT8XX_H ++#define DVB_BT8XX_H ++ ++#include ++#include ++#include "dvbdev.h" ++#include "dvb_net.h" ++#include "bttv.h" ++#include "mt352.h" ++#include "sp887x.h" ++#include "dst_common.h" ++#include "nxt6000.h" ++#include "cx24110.h" ++#include "or51211.h" ++#include "lgdt330x.h" ++#include "zl10353.h" ++#include "tuner-simple.h" ++ ++struct dvb_bt8xx_card { ++ struct mutex lock; ++ int nfeeds; ++ char card_name[32]; ++ struct dvb_adapter dvb_adapter; ++ struct bt878 *bt; ++ unsigned int bttv_nr; ++ struct dvb_demux demux; ++ struct dmxdev dmxdev; ++ struct dmx_frontend fe_hw; ++ struct dmx_frontend fe_mem; ++ u32 gpio_mode; ++ u32 op_sync_orin; ++ u32 irq_err_ignore; ++ struct i2c_adapter *i2c_adapter; ++ struct dvb_net dvbnet; ++ ++ struct dvb_frontend* fe; ++}; ++ ++#endif /* DVB_BT8XX_H */ +diff --git a/drivers/media/pci/cx18/Kconfig b/drivers/media/pci/cx18/Kconfig +new file mode 100644 +index 0000000..c675b83 +--- /dev/null ++++ b/drivers/media/pci/cx18/Kconfig +@@ -0,0 +1,35 @@ ++config VIDEO_CX18 ++ tristate "Conexant cx23418 MPEG encoder support" ++ depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C ++ select I2C_ALGOBIT ++ select VIDEOBUF_VMALLOC ++ depends on RC_CORE ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select VIDEO_CX2341X ++ select VIDEO_CS5345 ++ select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_MXL5005S if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for Conexant cx23418 based ++ PCI combo video recorder devices. ++ ++ This is used in devices such as the Hauppauge HVR-1600 ++ cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx18. ++ ++config VIDEO_CX18_ALSA ++ tristate "Conexant 23418 DMA audio support" ++ depends on VIDEO_CX18 && SND ++ select SND_PCM ++ ---help--- ++ This is a video4linux driver for direct (DMA) audio on ++ Conexant 23418 based TV cards using ALSA. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx18-alsa. +diff --git a/drivers/media/pci/cx18/Makefile b/drivers/media/pci/cx18/Makefile +new file mode 100644 +index 0000000..d3ff154 +--- /dev/null ++++ b/drivers/media/pci/cx18/Makefile +@@ -0,0 +1,13 @@ ++cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \ ++ cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ ++ cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ ++ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ ++ cx18-dvb.o cx18-io.o ++cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o ++ ++obj-$(CONFIG_VIDEO_CX18) += cx18.o ++obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o ++ ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/dvb-frontends ++ccflags-y += -Idrivers/media/tuners +diff --git a/drivers/media/pci/cx18/cx18-alsa-main.c b/drivers/media/pci/cx18/cx18-alsa-main.c +new file mode 100644 +index 0000000..8e971ff +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa-main.c +@@ -0,0 +1,295 @@ ++/* ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * Copyright (C) 2009 Devin Heitmueller ++ * ++ * Portions of this work were sponsored by ONELAN Limited. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "cx18-driver.h" ++#include "cx18-version.h" ++#include "cx18-alsa.h" ++#include "cx18-alsa-mixer.h" ++#include "cx18-alsa-pcm.h" ++ ++int cx18_alsa_debug; ++ ++#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \ ++ do { \ ++ if (cx18_alsa_debug & 2) \ ++ printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \ ++ } while (0); ++ ++module_param_named(debug, cx18_alsa_debug, int, 0644); ++MODULE_PARM_DESC(debug, ++ "Debug level (bitmask). Default: 0\n" ++ "\t\t\t 1/0x0001: warning\n" ++ "\t\t\t 2/0x0002: info\n"); ++ ++MODULE_AUTHOR("Andy Walls"); ++MODULE_DESCRIPTION("CX23418 ALSA Interface"); ++MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_VERSION(CX18_VERSION); ++ ++static inline ++struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev) ++{ ++ return to_cx18(v4l2_dev)->alsa; ++} ++ ++static inline ++struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev); ++} ++ ++static void snd_cx18_card_free(struct snd_cx18_card *cxsc) ++{ ++ if (cxsc == NULL) ++ return; ++ ++ if (cxsc->v4l2_dev != NULL) ++ to_cx18(cxsc->v4l2_dev)->alsa = NULL; ++ ++ /* FIXME - take any other stopping actions needed */ ++ ++ kfree(cxsc); ++} ++ ++static void snd_cx18_card_private_free(struct snd_card *sc) ++{ ++ if (sc == NULL) ++ return; ++ snd_cx18_card_free(sc->private_data); ++ sc->private_data = NULL; ++ sc->private_free = NULL; ++} ++ ++static int snd_cx18_card_create(struct v4l2_device *v4l2_dev, ++ struct snd_card *sc, ++ struct snd_cx18_card **cxsc) ++{ ++ *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL); ++ if (*cxsc == NULL) ++ return -ENOMEM; ++ ++ (*cxsc)->v4l2_dev = v4l2_dev; ++ (*cxsc)->sc = sc; ++ ++ sc->private_data = *cxsc; ++ sc->private_free = snd_cx18_card_private_free; ++ ++ return 0; ++} ++ ++static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc) ++{ ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ struct snd_card *sc = cxsc->sc; ++ ++ /* sc->driver is used by alsa-lib's configurator: simple, unique */ ++ strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); ++ ++ /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */ ++ snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", ++ cx->instance); ++ ++ /* sc->longname is read from /proc/asound/cards */ ++ snprintf(sc->longname, sizeof(sc->longname), ++ "CX23418 #%d %s TV/FM Radio/Line-In Capture", ++ cx->instance, cx->card_name); ++ ++ return 0; ++} ++ ++static int snd_cx18_init(struct v4l2_device *v4l2_dev) ++{ ++ struct cx18 *cx = to_cx18(v4l2_dev); ++ struct snd_card *sc = NULL; ++ struct snd_cx18_card *cxsc; ++ int ret; ++ ++ /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ ++ ++ /* (1) Check and increment the device index */ ++ /* This is a no-op for us. We'll use the cx->instance */ ++ ++ /* (2) Create a card instance */ ++ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ ++ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ ++ THIS_MODULE, 0, &sc); ++ if (ret) { ++ CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit; ++ } ++ ++ /* (3) Create a main component */ ++ ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); ++ if (ret) { ++ CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ ++ /* (4) Set the driver ID and name strings */ ++ snd_cx18_card_set_names(cxsc); ++ ++ ++ ret = snd_cx18_pcm_create(cxsc); ++ if (ret) { ++ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ /* FIXME - proc files */ ++ ++ /* (7) Set the driver data and return 0 */ ++ /* We do this out of normal order for PCI drivers to avoid races */ ++ cx->alsa = cxsc; ++ ++ /* (6) Register the card instance */ ++ ret = snd_card_register(sc); ++ if (ret) { ++ cx->alsa = NULL; ++ CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ ++ return 0; ++ ++err_exit_free: ++ if (sc != NULL) ++ snd_card_free(sc); ++ kfree(cxsc); ++err_exit: ++ return ret; ++} ++ ++static int __init cx18_alsa_load(struct cx18 *cx) ++{ ++ struct v4l2_device *v4l2_dev = &cx->v4l2_dev; ++ struct cx18_stream *s; ++ ++ if (v4l2_dev == NULL) { ++ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ cx = to_cx18(v4l2_dev); ++ if (cx == NULL) { ++ printk(KERN_ERR "cx18-alsa cx is NULL\n"); ++ return 0; ++ } ++ ++ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; ++ if (s->video_dev == NULL) { ++ CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " ++ "skipping\n", __func__); ++ return 0; ++ } ++ ++ if (cx->alsa != NULL) { ++ CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n", ++ __func__); ++ return 0; ++ } ++ ++ if (snd_cx18_init(v4l2_dev)) { ++ CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n", ++ __func__); ++ } else { ++ CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " ++ "\n", __func__); ++ } ++ return 0; ++} ++ ++static int __init cx18_alsa_init(void) ++{ ++ printk(KERN_INFO "cx18-alsa: module loading...\n"); ++ cx18_ext_init = &cx18_alsa_load; ++ return 0; ++} ++ ++static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) ++{ ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ ++ /* FIXME - pointer checks & shutdown cxsc */ ++ ++ snd_card_free(cxsc->sc); ++ cx->alsa = NULL; ++} ++ ++static int __exit cx18_alsa_exit_callback(struct device *dev, void *data) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct snd_cx18_card *cxsc; ++ ++ if (v4l2_dev == NULL) { ++ printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ cxsc = to_snd_cx18_card(v4l2_dev); ++ if (cxsc == NULL) { ++ CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ snd_cx18_exit(cxsc); ++ return 0; ++} ++ ++static void __exit cx18_alsa_exit(void) ++{ ++ struct device_driver *drv; ++ int ret; ++ ++ printk(KERN_INFO "cx18-alsa: module unloading...\n"); ++ ++ drv = driver_find("cx18", &pci_bus_type); ++ ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); ++ (void)ret; /* suppress compiler warning */ ++ ++ cx18_ext_init = NULL; ++ printk(KERN_INFO "cx18-alsa: module unload complete\n"); ++} ++ ++module_init(cx18_alsa_init); ++module_exit(cx18_alsa_exit); +diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.c b/drivers/media/pci/cx18/cx18-alsa-mixer.c +new file mode 100644 +index 0000000..341bddc +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa-mixer.c +@@ -0,0 +1,175 @@ ++/* ++ * ALSA mixer controls for the ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "cx18-alsa.h" ++#include "cx18-driver.h" ++ ++/* ++ * Note the cx18-av-core volume scale is funny, due to the alignment of the ++ * scale with another chip's range: ++ * ++ * v4l2_control value /512 indicated dB actual dB reg 0x8d4 ++ * 0x0000 - 0x01ff 0 -119 -96 228 ++ * 0x0200 - 0x02ff 1 -118 -96 228 ++ * ... ++ * 0x2c00 - 0x2dff 22 -97 -96 228 ++ * 0x2e00 - 0x2fff 23 -96 -96 228 ++ * 0x3000 - 0x31ff 24 -95 -95 226 ++ * ... ++ * 0xee00 - 0xefff 119 0 0 36 ++ * ... ++ * 0xfe00 - 0xffff 127 +8 +8 20 ++ */ ++static inline int dB_to_cx18_av_vol(int dB) ++{ ++ if (dB < -96) ++ dB = -96; ++ else if (dB > 8) ++ dB = 8; ++ return (dB + 119) << 9; ++} ++ ++static inline int cx18_av_vol_to_dB(int v) ++{ ++ if (v < (23 << 9)) ++ v = (23 << 9); ++ else if (v > (127 << 9)) ++ v = (127 << 9); ++ return (v >> 9) - 119; ++} ++ ++static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ /* We're already translating values, just keep this control in dB */ ++ uinfo->value.integer.min = -96; ++ uinfo->value.integer.max = 8; ++ uinfo->value.integer.step = 1; ++ return 0; ++} ++ ++static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *uctl) ++{ ++ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ struct v4l2_control vctrl; ++ int ret; ++ ++ vctrl.id = V4L2_CID_AUDIO_VOLUME; ++ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); ++ ++ snd_cx18_lock(cxsc); ++ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); ++ snd_cx18_unlock(cxsc); ++ ++ if (!ret) ++ uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); ++ return ret; ++} ++ ++static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *uctl) ++{ ++ struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ struct v4l2_control vctrl; ++ int ret; ++ ++ vctrl.id = V4L2_CID_AUDIO_VOLUME; ++ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); ++ ++ snd_cx18_lock(cxsc); ++ ++ /* Fetch current state */ ++ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); ++ ++ if (ret || ++ (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { ++ ++ /* Set, if needed */ ++ vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); ++ ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl); ++ if (!ret) ++ ret = 1; /* Indicate control was changed w/o error */ ++ } ++ snd_cx18_unlock(cxsc); ++ ++ return ret; ++} ++ ++ ++/* This is a bit of overkill, the slider is already in dB internally */ ++static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0); ++ ++static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Analog TV Capture Volume", ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .info = snd_cx18_mixer_tv_volume_info, ++ .get = snd_cx18_mixer_tv_volume_get, ++ .put = snd_cx18_mixer_tv_volume_put, ++ .tlv.p = snd_cx18_mixer_tv_vol_db_scale ++}; ++ ++/* FIXME - add mute switch and balance, bass, treble sliders: ++ V4L2_CID_AUDIO_MUTE ++ ++ V4L2_CID_AUDIO_BALANCE ++ ++ V4L2_CID_AUDIO_BASS ++ V4L2_CID_AUDIO_TREBLE ++*/ ++ ++/* FIXME - add stereo, lang1, lang2, mono menu */ ++/* FIXME - add CS5345 I2S volume for HVR-1600 */ ++ ++int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc) ++{ ++ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; ++ struct snd_card *sc = cxsc->sc; ++ int ret; ++ ++ strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername)); ++ ++ ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc)); ++ if (ret) { ++ CX18_ALSA_WARN("%s: failed to add %s control, err %d\n", ++ __func__, snd_cx18_mixer_tv_vol.name, ret); ++ } ++ return ret; ++} +diff --git a/drivers/media/pci/cx18/cx18-alsa-mixer.h b/drivers/media/pci/cx18/cx18-alsa-mixer.h +new file mode 100644 +index 0000000..ec92387 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa-mixer.h +@@ -0,0 +1,23 @@ ++/* ++ * ALSA mixer controls for the ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc); +diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.c b/drivers/media/pci/cx18/cx18-alsa-pcm.c +new file mode 100644 +index 0000000..180077c +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa-pcm.c +@@ -0,0 +1,357 @@ ++/* ++ * ALSA PCM device for the ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * Copyright (C) 2009 Devin Heitmueller ++ * ++ * Portions of this work were sponsored by ONELAN Limited. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "cx18-driver.h" ++#include "cx18-queue.h" ++#include "cx18-streams.h" ++#include "cx18-fileops.h" ++#include "cx18-alsa.h" ++#include "cx18-alsa-pcm.h" ++ ++static unsigned int pcm_debug; ++module_param(pcm_debug, int, 0644); ++MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); ++ ++#define dprintk(fmt, arg...) do { \ ++ if (pcm_debug) \ ++ printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \ ++ __func__, ##arg); \ ++ } while (0) ++ ++static struct snd_pcm_hardware snd_cx18_hw_capture = { ++ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ ++ .rates = SNDRV_PCM_RATE_48000, ++ ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ ++ .period_bytes_min = 64, /* 12544/2, */ ++ .period_bytes_max = 12544, ++ .periods_min = 2, ++ .periods_max = 98, /* 12544, */ ++}; ++ ++void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, ++ size_t num_bytes) ++{ ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ unsigned int oldptr; ++ unsigned int stride; ++ int period_elapsed = 0; ++ int length; ++ ++ dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, ++ pcm_data, num_bytes); ++ ++ substream = cxsc->capture_pcm_substream; ++ if (substream == NULL) { ++ dprintk("substream was NULL\n"); ++ return; ++ } ++ ++ runtime = substream->runtime; ++ if (runtime == NULL) { ++ dprintk("runtime was NULL\n"); ++ return; ++ } ++ ++ stride = runtime->frame_bits >> 3; ++ if (stride == 0) { ++ dprintk("stride is zero\n"); ++ return; ++ } ++ ++ length = num_bytes / stride; ++ if (length == 0) { ++ dprintk("%s: length was zero\n", __func__); ++ return; ++ } ++ ++ if (runtime->dma_area == NULL) { ++ dprintk("dma area was NULL - ignoring\n"); ++ return; ++ } ++ ++ oldptr = cxsc->hwptr_done_capture; ++ if (oldptr + length >= runtime->buffer_size) { ++ unsigned int cnt = ++ runtime->buffer_size - oldptr; ++ memcpy(runtime->dma_area + oldptr * stride, pcm_data, ++ cnt * stride); ++ memcpy(runtime->dma_area, pcm_data + cnt * stride, ++ length * stride - cnt * stride); ++ } else { ++ memcpy(runtime->dma_area + oldptr * stride, pcm_data, ++ length * stride); ++ } ++ snd_pcm_stream_lock(substream); ++ ++ cxsc->hwptr_done_capture += length; ++ if (cxsc->hwptr_done_capture >= ++ runtime->buffer_size) ++ cxsc->hwptr_done_capture -= ++ runtime->buffer_size; ++ ++ cxsc->capture_transfer_done += length; ++ if (cxsc->capture_transfer_done >= ++ runtime->period_size) { ++ cxsc->capture_transfer_done -= ++ runtime->period_size; ++ period_elapsed = 1; ++ } ++ ++ snd_pcm_stream_unlock(substream); ++ ++ if (period_elapsed) ++ snd_pcm_period_elapsed(substream); ++} ++ ++static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; ++ struct cx18 *cx = to_cx18(v4l2_dev); ++ struct cx18_stream *s; ++ struct cx18_open_id item; ++ int ret; ++ ++ /* Instruct the cx18 to start sending packets */ ++ snd_cx18_lock(cxsc); ++ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; ++ ++ item.cx = cx; ++ item.type = s->type; ++ item.open_id = cx->open_id++; ++ ++ /* See if the stream is available */ ++ if (cx18_claim_stream(&item, item.type)) { ++ /* No, it's already in use */ ++ snd_cx18_unlock(cxsc); ++ return -EBUSY; ++ } ++ ++ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || ++ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ /* We're already streaming. No additional action required */ ++ snd_cx18_unlock(cxsc); ++ return 0; ++ } ++ ++ ++ runtime->hw = snd_cx18_hw_capture; ++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ++ cxsc->capture_pcm_substream = substream; ++ runtime->private_data = cx; ++ ++ cx->pcm_announce_callback = cx18_alsa_announce_pcm_data; ++ ++ /* Not currently streaming, so start it up */ ++ set_bit(CX18_F_S_STREAMING, &s->s_flags); ++ ret = cx18_start_v4l2_encode_stream(s); ++ snd_cx18_unlock(cxsc); ++ ++ return ret; ++} ++ ++static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; ++ struct cx18 *cx = to_cx18(v4l2_dev); ++ struct cx18_stream *s; ++ ++ /* Instruct the cx18 to stop sending packets */ ++ snd_cx18_lock(cxsc); ++ s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; ++ cx18_stop_v4l2_encode_stream(s, 0); ++ clear_bit(CX18_F_S_STREAMING, &s->s_flags); ++ ++ cx18_release_stream(s); ++ ++ cx->pcm_announce_callback = NULL; ++ snd_cx18_unlock(cxsc); ++ ++ return 0; ++} ++ ++static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg) ++{ ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ int ret; ++ ++ snd_cx18_lock(cxsc); ++ ret = snd_pcm_lib_ioctl(substream, cmd, arg); ++ snd_cx18_unlock(cxsc); ++ return ret; ++} ++ ++ ++static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, ++ size_t size) ++{ ++ struct snd_pcm_runtime *runtime = subs->runtime; ++ ++ dprintk("Allocating vbuffer\n"); ++ if (runtime->dma_area) { ++ if (runtime->dma_bytes > size) ++ return 0; ++ ++ vfree(runtime->dma_area); ++ } ++ runtime->dma_area = vmalloc(size); ++ if (!runtime->dma_area) ++ return -ENOMEM; ++ ++ runtime->dma_bytes = size; ++ ++ return 0; ++} ++ ++static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ dprintk("%s called\n", __func__); ++ ++ return snd_pcm_alloc_vmalloc_buffer(substream, ++ params_buffer_bytes(params)); ++} ++ ++static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cxsc->slock, flags); ++ if (substream->runtime->dma_area) { ++ dprintk("freeing pcm capture region\n"); ++ vfree(substream->runtime->dma_area); ++ substream->runtime->dma_area = NULL; ++ } ++ spin_unlock_irqrestore(&cxsc->slock, flags); ++ ++ return 0; ++} ++ ++static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ ++ cxsc->hwptr_done_capture = 0; ++ cxsc->capture_transfer_done = 0; ++ ++ return 0; ++} ++ ++static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ return 0; ++} ++ ++static ++snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ unsigned long flags; ++ snd_pcm_uframes_t hwptr_done; ++ struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); ++ ++ spin_lock_irqsave(&cxsc->slock, flags); ++ hwptr_done = cxsc->hwptr_done_capture; ++ spin_unlock_irqrestore(&cxsc->slock, flags); ++ ++ return hwptr_done; ++} ++ ++static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, ++ unsigned long offset) ++{ ++ void *pageptr = subs->runtime->dma_area + offset; ++ ++ return vmalloc_to_page(pageptr); ++} ++ ++static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { ++ .open = snd_cx18_pcm_capture_open, ++ .close = snd_cx18_pcm_capture_close, ++ .ioctl = snd_cx18_pcm_ioctl, ++ .hw_params = snd_cx18_pcm_hw_params, ++ .hw_free = snd_cx18_pcm_hw_free, ++ .prepare = snd_cx18_pcm_prepare, ++ .trigger = snd_cx18_pcm_trigger, ++ .pointer = snd_cx18_pcm_pointer, ++ .page = snd_pcm_get_vmalloc_page, ++}; ++ ++int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) ++{ ++ struct snd_pcm *sp; ++ struct snd_card *sc = cxsc->sc; ++ struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; ++ struct cx18 *cx = to_cx18(v4l2_dev); ++ int ret; ++ ++ ret = snd_pcm_new(sc, "CX23418 PCM", ++ 0, /* PCM device 0, the only one for this card */ ++ 0, /* 0 playback substreams */ ++ 1, /* 1 capture substream */ ++ &sp); ++ if (ret) { ++ CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit; ++ } ++ ++ spin_lock_init(&cxsc->slock); ++ ++ snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, ++ &snd_cx18_pcm_capture_ops); ++ sp->info_flags = 0; ++ sp->private_data = cxsc; ++ strlcpy(sp->name, cx->card_name, sizeof(sp->name)); ++ ++ return 0; ++ ++err_exit: ++ return ret; ++} +diff --git a/drivers/media/pci/cx18/cx18-alsa-pcm.h b/drivers/media/pci/cx18/cx18-alsa-pcm.h +new file mode 100644 +index 0000000..d26e51f +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa-pcm.h +@@ -0,0 +1,27 @@ ++/* ++ * ALSA PCM device for the ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc); ++ ++/* Used by cx18-mailbox to announce the PCM data to the module */ ++void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data, ++ size_t num_bytes); +diff --git a/drivers/media/pci/cx18/cx18-alsa.h b/drivers/media/pci/cx18/cx18-alsa.h +new file mode 100644 +index 0000000..447da37 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-alsa.h +@@ -0,0 +1,75 @@ ++/* ++ * ALSA interface to cx18 PCM capture streams ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++struct snd_card; ++ ++struct snd_cx18_card { ++ struct v4l2_device *v4l2_dev; ++ struct snd_card *sc; ++ unsigned int capture_transfer_done; ++ unsigned int hwptr_done_capture; ++ struct snd_pcm_substream *capture_pcm_substream; ++ spinlock_t slock; ++}; ++ ++extern int cx18_alsa_debug; ++ ++/* ++ * File operations that manipulate the encoder or video or audio subdevices ++ * need to be serialized. Use the same lock we use for v4l2 file ops. ++ */ ++static inline void snd_cx18_lock(struct snd_cx18_card *cxsc) ++{ ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ mutex_lock(&cx->serialize_lock); ++} ++ ++static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc) ++{ ++ struct cx18 *cx = to_cx18(cxsc->v4l2_dev); ++ mutex_unlock(&cx->serialize_lock); ++} ++ ++#define CX18_ALSA_DBGFLG_WARN (1 << 0) ++#define CX18_ALSA_DBGFLG_WARN (1 << 0) ++#define CX18_ALSA_DBGFLG_INFO (1 << 1) ++ ++#define CX18_ALSA_DEBUG(x, type, fmt, args...) \ ++ do { \ ++ if ((x) & cx18_alsa_debug) \ ++ printk(KERN_INFO "%s-alsa: " type ": " fmt, \ ++ v4l2_dev->name , ## args); \ ++ } while (0) ++ ++#define CX18_ALSA_DEBUG_WARN(fmt, args...) \ ++ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args) ++ ++#define CX18_ALSA_DEBUG_INFO(fmt, args...) \ ++ CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args) ++ ++#define CX18_ALSA_ERR(fmt, args...) \ ++ printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args) ++ ++#define CX18_ALSA_WARN(fmt, args...) \ ++ printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args) ++ ++#define CX18_ALSA_INFO(fmt, args...) \ ++ printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args) +diff --git a/drivers/media/pci/cx18/cx18-audio.c b/drivers/media/pci/cx18/cx18-audio.c +new file mode 100644 +index 0000000..3526892 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-audio.c +@@ -0,0 +1,92 @@ ++/* ++ * cx18 audio-related functions ++ * ++ * Derived from ivtv-audio.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-cards.h" ++#include "cx18-audio.h" ++ ++#define CX18_AUDIO_ENABLE 0xc72014 ++#define CX18_AI1_MUX_MASK 0x30 ++#define CX18_AI1_MUX_I2S1 0x00 ++#define CX18_AI1_MUX_I2S2 0x10 ++#define CX18_AI1_MUX_843_I2S 0x20 ++ ++/* Selects the audio input and output according to the current ++ settings. */ ++int cx18_audio_set_io(struct cx18 *cx) ++{ ++ const struct cx18_card_audio_input *in; ++ u32 u, v; ++ int err; ++ ++ /* Determine which input to use */ ++ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) ++ in = &cx->card->radio_input; ++ else ++ in = &cx->card->audio_inputs[cx->audio_input]; ++ ++ /* handle muxer chips */ ++ v4l2_subdev_call(cx->sd_extmux, audio, s_routing, ++ (u32) in->muxer_input, 0, 0); ++ ++ err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl, ++ audio, s_routing, in->audio_input, 0, 0); ++ if (err) ++ return err; ++ ++ /* FIXME - this internal mux should be abstracted to a subdev */ ++ u = cx18_read_reg(cx, CX18_AUDIO_ENABLE); ++ v = u & ~CX18_AI1_MUX_MASK; ++ switch (in->audio_input) { ++ case CX18_AV_AUDIO_SERIAL1: ++ v |= CX18_AI1_MUX_I2S1; ++ break; ++ case CX18_AV_AUDIO_SERIAL2: ++ v |= CX18_AI1_MUX_I2S2; ++ break; ++ default: ++ v |= CX18_AI1_MUX_843_I2S; ++ break; ++ } ++ if (v == u) { ++ /* force a toggle of some AI1 MUX control bits */ ++ u &= ~CX18_AI1_MUX_MASK; ++ switch (in->audio_input) { ++ case CX18_AV_AUDIO_SERIAL1: ++ u |= CX18_AI1_MUX_843_I2S; ++ break; ++ case CX18_AV_AUDIO_SERIAL2: ++ u |= CX18_AI1_MUX_843_I2S; ++ break; ++ default: ++ u |= CX18_AI1_MUX_I2S1; ++ break; ++ } ++ cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE, ++ u, CX18_AI1_MUX_MASK); ++ } ++ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, ++ v, CX18_AI1_MUX_MASK); ++ return 0; ++} +diff --git a/drivers/media/pci/cx18/cx18-audio.h b/drivers/media/pci/cx18/cx18-audio.h +new file mode 100644 +index 0000000..2731d29 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-audio.h +@@ -0,0 +1,24 @@ ++/* ++ * cx18 audio-related functions ++ * ++ * Derived from ivtv-audio.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int cx18_audio_set_io(struct cx18 *cx); +diff --git a/drivers/media/pci/cx18/cx18-av-audio.c b/drivers/media/pci/cx18/cx18-av-audio.c +new file mode 100644 +index 0000000..4a24ffb +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-av-audio.c +@@ -0,0 +1,471 @@ ++/* ++ * cx18 ADEC audio functions ++ * ++ * Derived from cx25840-audio.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include "cx18-driver.h" ++ ++static int set_audclk_freq(struct cx18 *cx, u32 freq) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ ++ if (freq != 32000 && freq != 44100 && freq != 48000) ++ return -EINVAL; ++ ++ /* ++ * The PLL parameters are based on the external crystal frequency that ++ * would ideally be: ++ * ++ * NTSC Color subcarrier freq * 8 = ++ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz ++ * ++ * The accidents of history and rationale that explain from where this ++ * combination of magic numbers originate can be found in: ++ * ++ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in ++ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 ++ * ++ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the ++ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 ++ * ++ * As Mike Bradley has rightly pointed out, it's not the exact crystal ++ * frequency that matters, only that all parts of the driver and ++ * firmware are using the same value (close to the ideal value). ++ * ++ * Since I have a strong suspicion that, if the firmware ever assumes a ++ * crystal value at all, it will assume 28.636360 MHz, the crystal ++ * freq used in calculations in this driver will be: ++ * ++ * xtal_freq = 28.636360 MHz ++ * ++ * an error of less than 0.13 ppm which is way, way better than any off ++ * the shelf crystal will have for accuracy anyway. ++ * ++ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error. ++ * ++ * Many thanks to Jeff Campbell and Mike Bradley for their extensive ++ * investigation, experimentation, testing, and suggested solutions of ++ * of audio/video sync problems with SVideo and CVBS captures. ++ */ ++ ++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { ++ switch (freq) { ++ case 32000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 ++ */ ++ cx18_av_write4(cx, 0x108, 0x200d040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x176740c */ ++ /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x0176740c); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ ++ cx18_av_write4(cx, 0x900, 0x0801f77f); ++ cx18_av_write4(cx, 0x904, 0x0801f77f); ++ cx18_av_write4(cx, 0x90c, 0x0801f77f); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ ++ cx18_av_write(cx, 0x127, 0x60); ++ ++ /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x11202fff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = ++ * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa00d2ef8); ++ break; ++ ++ case 44100: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18 ++ */ ++ cx18_av_write4(cx, 0x108, 0x180e040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x062a1f2 */ ++ /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x0062a1f2); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ ++ cx18_av_write4(cx, 0x900, 0x08016d59); ++ cx18_av_write4(cx, 0x904, 0x08016d59); ++ cx18_av_write4(cx, 0x90c, 0x08016d59); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ ++ cx18_av_write(cx, 0x127, 0x58); ++ ++ /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x112092ff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = ++ * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa01d4bf8); ++ break; ++ ++ case 48000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16 ++ */ ++ cx18_av_write4(cx, 0x108, 0x160e040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x05227ad */ ++ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x005227ad); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ ++ cx18_av_write4(cx, 0x900, 0x08014faa); ++ cx18_av_write4(cx, 0x904, 0x08014faa); ++ cx18_av_write4(cx, 0x90c, 0x08014faa); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ ++ cx18_av_write(cx, 0x127, 0x56); ++ ++ /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x11205fff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x1193f8 = 143999.000 * 8 = ++ * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa01193f8); ++ break; ++ } ++ } else { ++ switch (freq) { ++ case 32000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30 ++ */ ++ cx18_av_write4(cx, 0x108, 0x300d040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x176740c */ ++ /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x0176740c); ++ ++ /* src1_ctl */ ++ /* 0x1.0000 = 32000/32000 */ ++ cx18_av_write4(cx, 0x8f8, 0x08010000); ++ ++ /* src3/4/6_ctl */ ++ /* 0x2.0000 = 2 * (32000/32000) */ ++ cx18_av_write4(cx, 0x900, 0x08020000); ++ cx18_av_write4(cx, 0x904, 0x08020000); ++ cx18_av_write4(cx, 0x90c, 0x08020000); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ ++ cx18_av_write(cx, 0x127, 0x70); ++ ++ /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x11201fff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = ++ * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa00d2ef8); ++ break; ++ ++ case 44100: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24 ++ */ ++ cx18_av_write4(cx, 0x108, 0x240e040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x062a1f2 */ ++ /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x0062a1f2); ++ ++ /* src1_ctl */ ++ /* 0x1.60cd = 44100/32000 */ ++ cx18_av_write4(cx, 0x8f8, 0x080160cd); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.7385 = 2 * (32000/44100) */ ++ cx18_av_write4(cx, 0x900, 0x08017385); ++ cx18_av_write4(cx, 0x904, 0x08017385); ++ cx18_av_write4(cx, 0x90c, 0x08017385); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ ++ cx18_av_write(cx, 0x127, 0x64); ++ ++ /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x112061ff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = ++ * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa01d4bf8); ++ break; ++ ++ case 48000: ++ /* ++ * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 ++ * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 ++ */ ++ cx18_av_write4(cx, 0x108, 0x200d040f); ++ ++ /* VID_PLL Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ ++ cx18_av_write4(cx, 0x10c, 0x002be2fe); ++ ++ /* AUX_PLL Fraction = 0x176740c */ ++ /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/ ++ cx18_av_write4(cx, 0x110, 0x0176740c); ++ ++ /* src1_ctl */ ++ /* 0x1.8000 = 48000/32000 */ ++ cx18_av_write4(cx, 0x8f8, 0x08018000); ++ ++ /* src3/4/6_ctl */ ++ /* 0x1.5555 = 2 * (32000/48000) */ ++ cx18_av_write4(cx, 0x900, 0x08015555); ++ cx18_av_write4(cx, 0x904, 0x08015555); ++ cx18_av_write4(cx, 0x90c, 0x08015555); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ ++ cx18_av_write(cx, 0x127, 0x60); ++ ++ /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ ++ cx18_av_write4(cx, 0x12c, 0x11203fff); ++ ++ /* ++ * EN_AV_LOCK = 0 ++ * VID_COUNT = 0x1193f8 = 143999.000 * 8 = ++ * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 ++ */ ++ cx18_av_write4(cx, 0x128, 0xa01193f8); ++ break; ++ } ++ } ++ ++ state->audclk_freq = freq; ++ ++ return 0; ++} ++ ++void cx18_av_audio_set_path(struct cx18 *cx) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ u8 v; ++ ++ /* stop microcontroller */ ++ v = cx18_av_read(cx, 0x803) & ~0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ ++ /* assert soft reset */ ++ v = cx18_av_read(cx, 0x810) | 0x01; ++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f); ++ ++ /* Mute everything to prevent the PFFT! */ ++ cx18_av_write(cx, 0x8d3, 0x1f); ++ ++ if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { ++ /* Set Path1 to Serial Audio Input */ ++ cx18_av_write4(cx, 0x8d0, 0x01011012); ++ ++ /* The microcontroller should not be started for the ++ * non-tuner inputs: autodetection is specific for ++ * TV audio. */ ++ } else { ++ /* Set Path1 to Analog Demod Main Channel */ ++ cx18_av_write4(cx, 0x8d0, 0x1f063870); ++ } ++ ++ set_audclk_freq(cx, state->audclk_freq); ++ ++ /* deassert soft reset */ ++ v = cx18_av_read(cx, 0x810) & ~0x01; ++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f); ++ ++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { ++ /* When the microcontroller detects the ++ * audio format, it will unmute the lines */ ++ v = cx18_av_read(cx, 0x803) | 0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ } ++} ++ ++static void set_volume(struct cx18 *cx, int volume) ++{ ++ /* First convert the volume to msp3400 values (0-127) */ ++ int vol = volume >> 9; ++ /* now scale it up to cx18_av values ++ * -114dB to -96dB maps to 0 ++ * this should be 19, but in my testing that was 4dB too loud */ ++ if (vol <= 23) ++ vol = 0; ++ else ++ vol -= 23; ++ ++ /* PATH1_VOLUME */ ++ cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); ++} ++ ++static void set_bass(struct cx18 *cx, int bass) ++{ ++ /* PATH1_EQ_BASS_VOL */ ++ cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); ++} ++ ++static void set_treble(struct cx18 *cx, int treble) ++{ ++ /* PATH1_EQ_TREBLE_VOL */ ++ cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); ++} ++ ++static void set_balance(struct cx18 *cx, int balance) ++{ ++ int bal = balance >> 8; ++ if (bal > 0x80) { ++ /* PATH1_BAL_LEFT */ ++ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); ++ /* PATH1_BAL_LEVEL */ ++ cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); ++ } else { ++ /* PATH1_BAL_LEFT */ ++ cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); ++ /* PATH1_BAL_LEVEL */ ++ cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); ++ } ++} ++ ++static void set_mute(struct cx18 *cx, int mute) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ u8 v; ++ ++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { ++ /* Must turn off microcontroller in order to mute sound. ++ * Not sure if this is the best method, but it does work. ++ * If the microcontroller is running, then it will undo any ++ * changes to the mute register. */ ++ v = cx18_av_read(cx, 0x803); ++ if (mute) { ++ /* disable microcontroller */ ++ v &= ~0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ cx18_av_write(cx, 0x8d3, 0x1f); ++ } else { ++ /* enable microcontroller */ ++ v |= 0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ } ++ } else { ++ /* SRC1_MUTE_EN */ ++ cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); ++ } ++} ++ ++int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ struct cx18_av_state *state = &cx->av_state; ++ int retval; ++ u8 v; ++ ++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { ++ v = cx18_av_read(cx, 0x803) & ~0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ cx18_av_write(cx, 0x8d3, 0x1f); ++ } ++ v = cx18_av_read(cx, 0x810) | 0x1; ++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f); ++ ++ retval = set_audclk_freq(cx, freq); ++ ++ v = cx18_av_read(cx, 0x810) & ~0x1; ++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f); ++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { ++ v = cx18_av_read(cx, 0x803) | 0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ } ++ return retval; ++} ++ ++static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_VOLUME: ++ set_volume(cx, ctrl->val); ++ break; ++ case V4L2_CID_AUDIO_BASS: ++ set_bass(cx, ctrl->val); ++ break; ++ case V4L2_CID_AUDIO_TREBLE: ++ set_treble(cx, ctrl->val); ++ break; ++ case V4L2_CID_AUDIO_BALANCE: ++ set_balance(cx, ctrl->val); ++ break; ++ case V4L2_CID_AUDIO_MUTE: ++ set_mute(cx, ctrl->val); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = { ++ .s_ctrl = cx18_av_audio_s_ctrl, ++}; +diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c +new file mode 100644 +index 0000000..f164b7f +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-av-core.c +@@ -0,0 +1,1401 @@ ++/* ++ * cx18 ADEC audio functions ++ * ++ * Derived from cx25840-core.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-cards.h" ++ ++int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) ++{ ++ u32 reg = 0xc40000 + (addr & ~3); ++ u32 mask = 0xff; ++ int shift = (addr & 3) * 8; ++ u32 x = cx18_read_reg(cx, reg); ++ ++ x = (x & ~(mask << shift)) | ((u32)value << shift); ++ cx18_write_reg(cx, x, reg); ++ return 0; ++} ++ ++int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask) ++{ ++ u32 reg = 0xc40000 + (addr & ~3); ++ int shift = (addr & 3) * 8; ++ u32 x = cx18_read_reg(cx, reg); ++ ++ x = (x & ~((u32)0xff << shift)) | ((u32)value << shift); ++ cx18_write_reg_expect(cx, x, reg, ++ ((u32)eval << shift), ((u32)mask << shift)); ++ return 0; ++} ++ ++int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) ++{ ++ cx18_write_reg(cx, value, 0xc40000 + addr); ++ return 0; ++} ++ ++int ++cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask) ++{ ++ cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask); ++ return 0; ++} ++ ++int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value) ++{ ++ cx18_write_reg_noretry(cx, value, 0xc40000 + addr); ++ return 0; ++} ++ ++u8 cx18_av_read(struct cx18 *cx, u16 addr) ++{ ++ u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3)); ++ int shift = (addr & 3) * 8; ++ ++ return (x >> shift) & 0xff; ++} ++ ++u32 cx18_av_read4(struct cx18 *cx, u16 addr) ++{ ++ return cx18_read_reg(cx, 0xc40000 + addr); ++} ++ ++int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, ++ u8 or_value) ++{ ++ return cx18_av_write(cx, addr, ++ (cx18_av_read(cx, addr) & and_mask) | ++ or_value); ++} ++ ++int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, ++ u32 or_value) ++{ ++ return cx18_av_write4(cx, addr, ++ (cx18_av_read4(cx, addr) & and_mask) | ++ or_value); ++} ++ ++static void cx18_av_init(struct cx18 *cx) ++{ ++ /* ++ * The crystal freq used in calculations in this driver will be ++ * 28.636360 MHz. ++ * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. ++ */ ++ ++ /* ++ * VDCLK Integer = 0x0f, Post Divider = 0x04 ++ * AIMCLK Integer = 0x0e, Post Divider = 0x16 ++ */ ++ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); ++ ++ /* VDCLK Fraction = 0x2be2fe */ ++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ ++ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); ++ ++ /* AIMCLK Fraction = 0x05227ad */ ++ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ ++ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); ++ ++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ ++ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); ++} ++ ++static void cx18_av_initialize(struct v4l2_subdev *sd) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ int default_volume; ++ u32 v; ++ ++ cx18_av_loadfw(cx); ++ /* Stop 8051 code execution */ ++ cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000, ++ 0x03000000, 0x13000000); ++ ++ /* initallize the PLL by toggling sleep bit */ ++ v = cx18_av_read4(cx, CXADEC_HOST_REG1); ++ /* enable sleep mode - register appears to be read only... */ ++ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe); ++ /* disable sleep mode */ ++ cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe, ++ v & 0xfffe, 0xffff); ++ ++ /* initialize DLLs */ ++ v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; ++ /* disable FLD */ ++ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v); ++ /* enable FLD */ ++ cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100); ++ ++ v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF; ++ /* disable FLD */ ++ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v); ++ /* enable FLD */ ++ cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100); ++ ++ /* set analog bias currents. Set Vreg to 1.20V. */ ++ cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802); ++ ++ v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; ++ /* enable TUNE_FIL_RST */ ++ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F); ++ /* disable TUNE_FIL_RST */ ++ cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, ++ v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F); ++ ++ /* enable 656 output */ ++ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); ++ ++ /* video output drive strength */ ++ cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2); ++ ++ /* reset video */ ++ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); ++ cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); ++ ++ /* ++ * Disable Video Auto-config of the Analog Front End and Video PLL. ++ * ++ * Since we only use BT.656 pixel mode, which works for both 525 and 625 ++ * line systems, it's just easier for us to set registers ++ * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL), ++ * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC) ++ * ourselves, than to run around cleaning up after the auto-config. ++ * ++ * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit ++ * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL ++ * autoconfig either.) ++ * ++ * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3. ++ */ ++ cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); ++ ++ /* Setup the Video and and Aux/Audio PLLs */ ++ cx18_av_init(cx); ++ ++ /* set video to auto-detect */ ++ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ ++ /* set the comb notch = 1 */ ++ cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800); ++ ++ /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */ ++ /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */ ++ cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000); ++ ++ /* Set VGA_TRACK_RANGE to 0x20 */ ++ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); ++ ++ /* ++ * Initial VBI setup ++ * VIP-1.1, 10 bit mode, enable Raw, disable sliced, ++ * don't clamp raw samples when codes are in use, 1 byte user D-words, ++ * IDID0 has line #, RP code V bit transition on VBLANK, data during ++ * blanking intervals ++ */ ++ cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e); ++ ++ /* Set the video input. ++ The setting in MODE_CTRL gets lost when we do the above setup */ ++ /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ ++ /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ ++ ++ /* ++ * Analog Front End (AFE) ++ * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2 ++ * bypass_ch[1-3] use filter ++ * droop_comp_ch[1-3] disable ++ * clamp_en_ch[1-3] disable ++ * aud_in_sel ADC2 ++ * luma_in_sel ADC1 ++ * chroma_in_sel ADC2 ++ * clamp_sel_ch[2-3] midcode ++ * clamp_sel_ch1 video decoder ++ * vga_sel_ch3 audio decoder ++ * vga_sel_ch[1-2] video decoder ++ * half_bw_ch[1-3] disable ++ * +12db_ch[1-3] disable ++ */ ++ cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00); ++ ++/* if(dwEnable && dw3DCombAvailable) { */ ++/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ ++/* } else { */ ++/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ ++/* } */ ++ cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); ++ default_volume = cx18_av_read(cx, 0x8d4); ++ /* ++ * Enforce the legacy volume scale mapping limits to avoid ++ * -ERANGE errors when initializing the volume control ++ */ ++ if (default_volume > 228) { ++ /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ ++ default_volume = 228; ++ cx18_av_write(cx, 0x8d4, 228); ++ } else if (default_volume < 20) { ++ /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ ++ default_volume = 20; ++ cx18_av_write(cx, 0x8d4, 20); ++ } ++ default_volume = (((228 - default_volume) >> 1) + 23) << 9; ++ state->volume->cur.val = state->volume->default_value = default_volume; ++ v4l2_ctrl_handler_setup(&state->hdl); ++} ++ ++static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ cx18_av_initialize(sd); ++ return 0; ++} ++ ++static int cx18_av_load_fw(struct v4l2_subdev *sd) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ ++ if (!state->is_initialized) { ++ /* initialize on first use */ ++ state->is_initialized = 1; ++ cx18_av_initialize(sd); ++ } ++ return 0; ++} ++ ++void cx18_av_std_setup(struct cx18 *cx) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ struct v4l2_subdev *sd = &state->sd; ++ v4l2_std_id std = state->std; ++ ++ /* ++ * Video ADC crystal clock to pixel clock SRC decimation ratio ++ * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b ++ */ ++ const int src_decimation = 0x21f; ++ ++ int hblank, hactive, burst, vblank, vactive, sc; ++ int vblank656; ++ int luma_lpf, uv_lpf, comb; ++ u32 pll_int, pll_frac, pll_post; ++ ++ /* datasheet startup, step 8d */ ++ if (std & ~V4L2_STD_NTSC) ++ cx18_av_write(cx, 0x49f, 0x11); ++ else ++ cx18_av_write(cx, 0x49f, 0x14); ++ ++ /* ++ * Note: At the end of a field, there are 3 sets of half line duration ++ * (double horizontal rate) pulses: ++ * ++ * 5 (625) or 6 (525) half-lines to blank for the vertical retrace ++ * 5 (625) or 6 (525) vertical sync pulses of half line duration ++ * 5 (625) or 6 (525) half-lines of equalization pulses ++ */ ++ if (std & V4L2_STD_625_50) { ++ /* ++ * The following relationships of half line counts should hold: ++ * 625 = vblank656 + vactive ++ * 10 = vblank656 - vblank = vsync pulses + equalization pulses ++ * ++ * vblank656: half lines after line 625/mid-313 of blanked video ++ * vblank: half lines, after line 5/317, of blanked video ++ * vactive: half lines of active video + ++ * 5 half lines after the end of active video ++ * ++ * As far as I can tell: ++ * vblank656 starts counting from the falling edge of the first ++ * vsync pulse (start of line 1 or mid-313) ++ * vblank starts counting from the after the 5 vsync pulses and ++ * 5 or 4 equalization pulses (start of line 6 or 318) ++ * ++ * For 625 line systems the driver will extract VBI information ++ * from lines 6-23 and lines 318-335 (but the slicer can only ++ * handle 17 lines, not the 18 in the vblank region). ++ * In addition, we need vblank656 and vblank to be one whole ++ * line longer, to cover line 24 and 336, so the SAV/EAV RP ++ * codes get generated such that the encoder can actually ++ * extract line 23 & 335 (WSS). We'll lose 1 line in each field ++ * at the top of the screen. ++ * ++ * It appears the 5 half lines that happen after active ++ * video must be included in vactive (579 instead of 574), ++ * otherwise the colors get badly displayed in various regions ++ * of the screen. I guess the chroma comb filter gets confused ++ * without them (at least when a PVR-350 is the PAL source). ++ */ ++ vblank656 = 48; /* lines 1 - 24 & 313 - 336 */ ++ vblank = 38; /* lines 6 - 24 & 318 - 336 */ ++ vactive = 579; /* lines 24 - 313 & 337 - 626 */ ++ ++ /* ++ * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is ++ * is 864 pixels = 720 active + 144 blanking. ITU-R BT.601 ++ * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after ++ * the end of active video to start a horizontal line, so that ++ * leaves 132 pixels of hblank to ignore. ++ */ ++ hblank = 132; ++ hactive = 720; ++ ++ /* ++ * Burst gate delay (for 625 line systems) ++ * Hsync leading edge to color burst rise = 5.6 us ++ * Color burst width = 2.25 us ++ * Gate width = 4 pixel clocks ++ * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks ++ */ ++ burst = 93; ++ luma_lpf = 2; ++ if (std & V4L2_STD_PAL) { ++ uv_lpf = 1; ++ comb = 0x20; ++ /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ ++ sc = 688700; ++ } else if (std == V4L2_STD_PAL_Nc) { ++ uv_lpf = 1; ++ comb = 0x20; ++ /* sc = 3582056.25 * src_decimation/28636360 * 2^13 */ ++ sc = 556422; ++ } else { /* SECAM */ ++ uv_lpf = 0; ++ comb = 0; ++ /* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */ ++ /* sc = 4328130 * src_decimation/28636360 * 2^13 */ ++ sc = 672314; ++ } ++ } else { ++ /* ++ * The following relationships of half line counts should hold: ++ * 525 = prevsync + vblank656 + vactive ++ * 12 = vblank656 - vblank = vsync pulses + equalization pulses ++ * ++ * prevsync: 6 half-lines before the vsync pulses ++ * vblank656: half lines, after line 3/mid-266, of blanked video ++ * vblank: half lines, after line 9/272, of blanked video ++ * vactive: half lines of active video ++ * ++ * As far as I can tell: ++ * vblank656 starts counting from the falling edge of the first ++ * vsync pulse (start of line 4 or mid-266) ++ * vblank starts counting from the after the 6 vsync pulses and ++ * 6 or 5 equalization pulses (start of line 10 or 272) ++ * ++ * For 525 line systems the driver will extract VBI information ++ * from lines 10-21 and lines 273-284. ++ */ ++ vblank656 = 38; /* lines 4 - 22 & 266 - 284 */ ++ vblank = 26; /* lines 10 - 22 & 272 - 284 */ ++ vactive = 481; /* lines 23 - 263 & 285 - 525 */ ++ ++ /* ++ * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is ++ * is 858 pixels = 720 active + 138 blanking. The Hsync leading ++ * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the ++ * end of active video, leaving 122 pixels of hblank to ignore ++ * before active video starts. ++ */ ++ hactive = 720; ++ hblank = 122; ++ luma_lpf = 1; ++ uv_lpf = 1; ++ ++ /* ++ * Burst gate delay (for 525 line systems) ++ * Hsync leading edge to color burst rise = 5.3 us ++ * Color burst width = 2.5 us ++ * Gate width = 4 pixel clocks ++ * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks ++ */ ++ if (std == V4L2_STD_PAL_60) { ++ burst = 90; ++ luma_lpf = 2; ++ comb = 0x20; ++ /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ ++ sc = 688700; ++ } else if (std == V4L2_STD_PAL_M) { ++ /* The 97 needs to be verified against PAL-M timings */ ++ burst = 97; ++ comb = 0x20; ++ /* sc = 3575611.49 * src_decimation/28636360 * 2^13 */ ++ sc = 555421; ++ } else { ++ burst = 90; ++ comb = 0x66; ++ /* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */ ++ sc = 556032; ++ } ++ } ++ ++ /* DEBUG: Displays configured PLL frequency */ ++ pll_int = cx18_av_read(cx, 0x108); ++ pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; ++ pll_post = cx18_av_read(cx, 0x109); ++ CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n", ++ pll_int, pll_frac, pll_post); ++ ++ if (pll_post) { ++ int fsc, pll; ++ u64 tmp; ++ ++ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; ++ pll /= pll_post; ++ CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n", ++ pll / 1000000, pll % 1000000); ++ CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n", ++ pll / 8000000, (pll / 8) % 1000000); ++ ++ CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio " ++ "= %d.%03d\n", src_decimation / 256, ++ ((src_decimation % 256) * 1000) / 256); ++ ++ tmp = 28636360 * (u64) sc; ++ do_div(tmp, src_decimation); ++ fsc = tmp >> 13; ++ CX18_DEBUG_INFO_DEV(sd, ++ "Chroma sub-carrier initial freq = %d.%06d " ++ "MHz\n", fsc / 1000000, fsc % 1000000); ++ ++ CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " ++ "vactive %i, vblank656 %i, src_dec %i, " ++ "burst 0x%02x, luma_lpf %i, uv_lpf %i, " ++ "comb 0x%02x, sc 0x%06x\n", ++ hblank, hactive, vblank, vactive, vblank656, ++ src_decimation, burst, luma_lpf, uv_lpf, ++ comb, sc); ++ } ++ ++ /* Sets horizontal blanking delay and active lines */ ++ cx18_av_write(cx, 0x470, hblank); ++ cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | ++ (hactive << 4))); ++ cx18_av_write(cx, 0x472, hactive >> 4); ++ ++ /* Sets burst gate delay */ ++ cx18_av_write(cx, 0x473, burst); ++ ++ /* Sets vertical blanking delay and active duration */ ++ cx18_av_write(cx, 0x474, vblank); ++ cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | ++ (vactive << 4))); ++ cx18_av_write(cx, 0x476, vactive >> 4); ++ cx18_av_write(cx, 0x477, vblank656); ++ ++ /* Sets src decimation rate */ ++ cx18_av_write(cx, 0x478, 0xff & src_decimation); ++ cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); ++ ++ /* Sets Luma and UV Low pass filters */ ++ cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); ++ ++ /* Enables comb filters */ ++ cx18_av_write(cx, 0x47b, comb); ++ ++ /* Sets SC Step*/ ++ cx18_av_write(cx, 0x47c, sc); ++ cx18_av_write(cx, 0x47d, 0xff & sc >> 8); ++ cx18_av_write(cx, 0x47e, 0xff & sc >> 16); ++ ++ if (std & V4L2_STD_625_50) { ++ state->slicer_line_delay = 1; ++ state->slicer_line_offset = (6 + state->slicer_line_delay - 2); ++ } else { ++ state->slicer_line_delay = 0; ++ state->slicer_line_offset = (10 + state->slicer_line_delay - 2); ++ } ++ cx18_av_write(cx, 0x47f, state->slicer_line_delay); ++} ++ ++static void input_change(struct cx18 *cx) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ v4l2_std_id std = state->std; ++ u8 v; ++ ++ /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ ++ cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); ++ cx18_av_and_or(cx, 0x401, ~0x60, 0); ++ cx18_av_and_or(cx, 0x401, ~0x60, 0x60); ++ ++ if (std & V4L2_STD_525_60) { ++ if (std == V4L2_STD_NTSC_M_JP) { ++ /* Japan uses EIAJ audio standard */ ++ cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff); ++ cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f); ++ } else if (std == V4L2_STD_NTSC_M_KR) { ++ /* South Korea uses A2 audio standard */ ++ cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff); ++ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); ++ } else { ++ /* Others use the BTSC audio standard */ ++ cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff); ++ cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f); ++ } ++ } else if (std & V4L2_STD_PAL) { ++ /* Follow tuner change procedure for PAL */ ++ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); ++ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); ++ } else if (std & V4L2_STD_SECAM) { ++ /* Select autodetect for SECAM */ ++ cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); ++ cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); ++ } ++ ++ v = cx18_av_read(cx, 0x803); ++ if (v & 0x10) { ++ /* restart audio decoder microcontroller */ ++ v &= ~0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ v |= 0x10; ++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f); ++ } ++} ++ ++static int cx18_av_s_frequency(struct v4l2_subdev *sd, ++ struct v4l2_frequency *freq) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ input_change(cx); ++ return 0; ++} ++ ++static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, ++ enum cx18_av_audio_input aud_input) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ struct v4l2_subdev *sd = &state->sd; ++ ++ enum analog_signal_type { ++ NONE, CVBS, Y, C, SIF, Pb, Pr ++ } ch[3] = {NONE, NONE, NONE}; ++ ++ u8 afe_mux_cfg; ++ u8 adc2_cfg; ++ u8 input_mode; ++ u32 afe_cfg; ++ int i; ++ ++ CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", ++ vid_input, aud_input); ++ ++ if (vid_input >= CX18_AV_COMPOSITE1 && ++ vid_input <= CX18_AV_COMPOSITE8) { ++ afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); ++ ch[0] = CVBS; ++ input_mode = 0x0; ++ } else if (vid_input >= CX18_AV_COMPONENT_LUMA1) { ++ int luma = vid_input & 0xf000; ++ int r_chroma = vid_input & 0xf0000; ++ int b_chroma = vid_input & 0xf00000; ++ ++ if ((vid_input & ~0xfff000) || ++ luma < CX18_AV_COMPONENT_LUMA1 || ++ luma > CX18_AV_COMPONENT_LUMA8 || ++ r_chroma < CX18_AV_COMPONENT_R_CHROMA4 || ++ r_chroma > CX18_AV_COMPONENT_R_CHROMA6 || ++ b_chroma < CX18_AV_COMPONENT_B_CHROMA7 || ++ b_chroma > CX18_AV_COMPONENT_B_CHROMA8) { ++ CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", ++ vid_input); ++ return -EINVAL; ++ } ++ afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12; ++ ch[0] = Y; ++ afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12; ++ ch[1] = Pr; ++ afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14; ++ ch[2] = Pb; ++ input_mode = 0x6; ++ } else { ++ int luma = vid_input & 0xf0; ++ int chroma = vid_input & 0xf00; ++ ++ if ((vid_input & ~0xff0) || ++ luma < CX18_AV_SVIDEO_LUMA1 || ++ luma > CX18_AV_SVIDEO_LUMA8 || ++ chroma < CX18_AV_SVIDEO_CHROMA4 || ++ chroma > CX18_AV_SVIDEO_CHROMA8) { ++ CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", ++ vid_input); ++ return -EINVAL; ++ } ++ afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); ++ ch[0] = Y; ++ if (chroma >= CX18_AV_SVIDEO_CHROMA7) { ++ afe_mux_cfg &= 0x3f; ++ afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; ++ ch[2] = C; ++ } else { ++ afe_mux_cfg &= 0xcf; ++ afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; ++ ch[1] = C; ++ } ++ input_mode = 0x2; ++ } ++ ++ switch (aud_input) { ++ case CX18_AV_AUDIO_SERIAL1: ++ case CX18_AV_AUDIO_SERIAL2: ++ /* do nothing, use serial audio input */ ++ break; ++ case CX18_AV_AUDIO4: ++ afe_mux_cfg &= ~0x30; ++ ch[1] = SIF; ++ break; ++ case CX18_AV_AUDIO5: ++ afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10; ++ ch[1] = SIF; ++ break; ++ case CX18_AV_AUDIO6: ++ afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20; ++ ch[1] = SIF; ++ break; ++ case CX18_AV_AUDIO7: ++ afe_mux_cfg &= ~0xc0; ++ ch[2] = SIF; ++ break; ++ case CX18_AV_AUDIO8: ++ afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40; ++ ch[2] = SIF; ++ break; ++ ++ default: ++ CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", ++ aud_input); ++ return -EINVAL; ++ } ++ ++ /* Set up analog front end multiplexers */ ++ cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); ++ /* Set INPUT_MODE to Composite, S-Video, or Component */ ++ cx18_av_and_or(cx, 0x401, ~0x6, input_mode); ++ ++ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ ++ adc2_cfg = cx18_av_read(cx, 0x102); ++ if (ch[2] == NONE) ++ adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */ ++ else ++ adc2_cfg |= 0x2; /* Signal on CH3, set ADC2 to CH3 for input */ ++ ++ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ ++ if (ch[1] != NONE && ch[2] != NONE) ++ adc2_cfg |= 0x4; /* Set dual mode */ ++ else ++ adc2_cfg &= ~0x4; /* Clear dual mode */ ++ cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17); ++ ++ /* Configure the analog front end */ ++ afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL); ++ afe_cfg &= 0xff000000; ++ afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */ ++ if (ch[1] != NONE && ch[2] != NONE) ++ afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */ ++ ++ for (i = 0; i < 3; i++) { ++ switch (ch[i]) { ++ default: ++ case NONE: ++ /* CLAMP_SEL = Fixed to midcode clamp level */ ++ afe_cfg |= (0x00000200 << i); ++ break; ++ case CVBS: ++ case Y: ++ if (i > 0) ++ afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */ ++ break; ++ case C: ++ case Pb: ++ case Pr: ++ /* CLAMP_SEL = Fixed to midcode clamp level */ ++ afe_cfg |= (0x00000200 << i); ++ if (i == 0 && ch[i] == C) ++ afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */ ++ break; ++ case SIF: ++ /* ++ * VGA_GAIN_SEL = Audio Decoder ++ * CLAMP_SEL = Fixed to midcode clamp level ++ */ ++ afe_cfg |= (0x00000240 << i); ++ if (i == 0) ++ afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */ ++ break; ++ } ++ } ++ ++ cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg); ++ ++ state->vid_input = vid_input; ++ state->aud_input = aud_input; ++ cx18_av_audio_set_path(cx); ++ input_change(cx); ++ return 0; ++} ++ ++static int cx18_av_s_video_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ return set_input(cx, input, state->aud_input); ++} ++ ++static int cx18_av_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ return set_input(cx, state->vid_input, input); ++} ++ ++static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ u8 vpres; ++ u8 mode; ++ int val = 0; ++ ++ if (state->radio) ++ return 0; ++ ++ vpres = cx18_av_read(cx, 0x40e) & 0x20; ++ vt->signal = vpres ? 0xffff : 0x0; ++ ++ vt->capability |= ++ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | ++ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; ++ ++ mode = cx18_av_read(cx, 0x804); ++ ++ /* get rxsubchans and audmode */ ++ if ((mode & 0xf) == 1) ++ val |= V4L2_TUNER_SUB_STEREO; ++ else ++ val |= V4L2_TUNER_SUB_MONO; ++ ++ if (mode == 2 || mode == 4) ++ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ ++ if (mode & 0x10) ++ val |= V4L2_TUNER_SUB_SAP; ++ ++ vt->rxsubchans = val; ++ vt->audmode = state->audmode; ++ return 0; ++} ++ ++static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ u8 v; ++ ++ if (state->radio) ++ return 0; ++ ++ v = cx18_av_read(cx, 0x809); ++ v &= ~0xf; ++ ++ switch (vt->audmode) { ++ case V4L2_TUNER_MODE_MONO: ++ /* mono -> mono ++ stereo -> mono ++ bilingual -> lang1 */ ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang1 */ ++ v |= 0x4; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang1/lang2 */ ++ v |= 0x7; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ /* mono -> mono ++ stereo -> stereo ++ bilingual -> lang2 */ ++ v |= 0x1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ cx18_av_write_expect(cx, 0x809, v, v, 0xff); ++ state->audmode = vt->audmode; ++ return 0; ++} ++ ++static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ u8 fmt = 0; /* zero is autodetect */ ++ u8 pal_m = 0; ++ ++ if (state->radio == 0 && state->std == norm) ++ return 0; ++ ++ state->radio = 0; ++ state->std = norm; ++ ++ /* First tests should be against specific std */ ++ if (state->std == V4L2_STD_NTSC_M_JP) { ++ fmt = 0x2; ++ } else if (state->std == V4L2_STD_NTSC_443) { ++ fmt = 0x3; ++ } else if (state->std == V4L2_STD_PAL_M) { ++ pal_m = 1; ++ fmt = 0x5; ++ } else if (state->std == V4L2_STD_PAL_N) { ++ fmt = 0x6; ++ } else if (state->std == V4L2_STD_PAL_Nc) { ++ fmt = 0x7; ++ } else if (state->std == V4L2_STD_PAL_60) { ++ fmt = 0x8; ++ } else { ++ /* Then, test against generic ones */ ++ if (state->std & V4L2_STD_NTSC) ++ fmt = 0x1; ++ else if (state->std & V4L2_STD_PAL) ++ fmt = 0x4; ++ else if (state->std & V4L2_STD_SECAM) ++ fmt = 0xc; ++ } ++ ++ CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt); ++ ++ /* Follow step 9 of section 3.16 in the cx18_av datasheet. ++ Without this PAL may display a vertical ghosting effect. ++ This happens for example with the Yuan MPC622. */ ++ if (fmt >= 4 && fmt < 8) { ++ /* Set format to NTSC-M */ ++ cx18_av_and_or(cx, 0x400, ~0xf, 1); ++ /* Turn off LCOMB */ ++ cx18_av_and_or(cx, 0x47b, ~6, 0); ++ } ++ cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20); ++ cx18_av_and_or(cx, 0x403, ~0x3, pal_m); ++ cx18_av_std_setup(cx); ++ input_change(cx); ++ return 0; ++} ++ ++static int cx18_av_s_radio(struct v4l2_subdev *sd) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ state->radio = 1; ++ return 0; ++} ++ ++static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ cx18_av_write(cx, 0x414, ctrl->val - 128); ++ break; ++ ++ case V4L2_CID_CONTRAST: ++ cx18_av_write(cx, 0x415, ctrl->val << 1); ++ break; ++ ++ case V4L2_CID_SATURATION: ++ cx18_av_write(cx, 0x420, ctrl->val << 1); ++ cx18_av_write(cx, 0x421, ctrl->val << 1); ++ break; ++ ++ case V4L2_CID_HUE: ++ cx18_av_write(cx, 0x422, ctrl->val); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ int HSC, VSC, Vsrc, Hsrc, filter, Vlines; ++ int is_50Hz = !(state->std & V4L2_STD_525_60); ++ ++ if (fmt->code != V4L2_MBUS_FMT_FIXED) ++ return -EINVAL; ++ ++ fmt->field = V4L2_FIELD_INTERLACED; ++ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4; ++ Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4; ++ ++ Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; ++ Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; ++ ++ /* ++ * This adjustment reflects the excess of vactive, set in ++ * cx18_av_std_setup(), above standard values: ++ * ++ * 480 + 1 for 60 Hz systems ++ * 576 + 3 for 50 Hz systems ++ */ ++ Vlines = fmt->height + (is_50Hz ? 3 : 1); ++ ++ /* ++ * Invalid height and width scaling requests are: ++ * 1. width less than 1/16 of the source width ++ * 2. width greater than the source width ++ * 3. height less than 1/8 of the source height ++ * 4. height greater than the source height ++ */ ++ if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || ++ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { ++ CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n", ++ fmt->width, fmt->height); ++ return -ERANGE; ++ } ++ ++ HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); ++ VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); ++ VSC &= 0x1fff; ++ ++ if (fmt->width >= 385) ++ filter = 0; ++ else if (fmt->width > 192) ++ filter = 1; ++ else if (fmt->width > 96) ++ filter = 2; ++ else ++ filter = 3; ++ ++ CX18_DEBUG_INFO_DEV(sd, ++ "decoder set size %dx%d -> scale %ux%u\n", ++ fmt->width, fmt->height, HSC, VSC); ++ ++ /* HSCALE=HSC */ ++ cx18_av_write(cx, 0x418, HSC & 0xff); ++ cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff); ++ cx18_av_write(cx, 0x41a, HSC >> 16); ++ /* VSCALE=VSC */ ++ cx18_av_write(cx, 0x41c, VSC & 0xff); ++ cx18_av_write(cx, 0x41d, VSC >> 8); ++ /* VS_INTRLACE=1 VFILT=filter */ ++ cx18_av_write(cx, 0x41e, 0x8 | filter); ++ return 0; ++} ++ ++static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable"); ++ if (enable) { ++ cx18_av_write(cx, 0x115, 0x8c); ++ cx18_av_write(cx, 0x116, 0x07); ++ } else { ++ cx18_av_write(cx, 0x115, 0x00); ++ cx18_av_write(cx, 0x116, 0x00); ++ } ++ return 0; ++} ++ ++static void log_video_status(struct cx18 *cx) ++{ ++ static const char *const fmt_strs[] = { ++ "0x0", ++ "NTSC-M", "NTSC-J", "NTSC-4.43", ++ "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", ++ "0x9", "0xA", "0xB", ++ "SECAM", ++ "0xD", "0xE", "0xF" ++ }; ++ ++ struct cx18_av_state *state = &cx->av_state; ++ struct v4l2_subdev *sd = &state->sd; ++ u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; ++ u8 gen_stat1 = cx18_av_read(cx, 0x40d); ++ u8 gen_stat2 = cx18_av_read(cx, 0x40e); ++ int vid_input = state->vid_input; ++ ++ CX18_INFO_DEV(sd, "Video signal: %spresent\n", ++ (gen_stat2 & 0x20) ? "" : "not "); ++ CX18_INFO_DEV(sd, "Detected format: %s\n", ++ fmt_strs[gen_stat1 & 0xf]); ++ ++ CX18_INFO_DEV(sd, "Specified standard: %s\n", ++ vidfmt_sel ? fmt_strs[vidfmt_sel] ++ : "automatic detection"); ++ ++ if (vid_input >= CX18_AV_COMPOSITE1 && ++ vid_input <= CX18_AV_COMPOSITE8) { ++ CX18_INFO_DEV(sd, "Specified video input: Composite %d\n", ++ vid_input - CX18_AV_COMPOSITE1 + 1); ++ } else { ++ CX18_INFO_DEV(sd, "Specified video input: " ++ "S-Video (Luma In%d, Chroma In%d)\n", ++ (vid_input & 0xf0) >> 4, ++ (vid_input & 0xf00) >> 8); ++ } ++ ++ CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n", ++ state->audclk_freq); ++} ++ ++static void log_audio_status(struct cx18 *cx) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ struct v4l2_subdev *sd = &state->sd; ++ u8 download_ctl = cx18_av_read(cx, 0x803); ++ u8 mod_det_stat0 = cx18_av_read(cx, 0x804); ++ u8 mod_det_stat1 = cx18_av_read(cx, 0x805); ++ u8 audio_config = cx18_av_read(cx, 0x808); ++ u8 pref_mode = cx18_av_read(cx, 0x809); ++ u8 afc0 = cx18_av_read(cx, 0x80b); ++ u8 mute_ctl = cx18_av_read(cx, 0x8d3); ++ int aud_input = state->aud_input; ++ char *p; ++ ++ switch (mod_det_stat0) { ++ case 0x00: p = "mono"; break; ++ case 0x01: p = "stereo"; break; ++ case 0x02: p = "dual"; break; ++ case 0x04: p = "tri"; break; ++ case 0x10: p = "mono with SAP"; break; ++ case 0x11: p = "stereo with SAP"; break; ++ case 0x12: p = "dual with SAP"; break; ++ case 0x14: p = "tri with SAP"; break; ++ case 0xfe: p = "forced mode"; break; ++ default: p = "not defined"; break; ++ } ++ CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p); ++ ++ switch (mod_det_stat1) { ++ case 0x00: p = "not defined"; break; ++ case 0x01: p = "EIAJ"; break; ++ case 0x02: p = "A2-M"; break; ++ case 0x03: p = "A2-BG"; break; ++ case 0x04: p = "A2-DK1"; break; ++ case 0x05: p = "A2-DK2"; break; ++ case 0x06: p = "A2-DK3"; break; ++ case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; ++ case 0x08: p = "AM-L"; break; ++ case 0x09: p = "NICAM-BG"; break; ++ case 0x0a: p = "NICAM-DK"; break; ++ case 0x0b: p = "NICAM-I"; break; ++ case 0x0c: p = "NICAM-L"; break; ++ case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; ++ case 0x0e: p = "IF FM Radio"; break; ++ case 0x0f: p = "BTSC"; break; ++ case 0x10: p = "detected chrominance"; break; ++ case 0xfd: p = "unknown audio standard"; break; ++ case 0xfe: p = "forced audio standard"; break; ++ case 0xff: p = "no detected audio standard"; break; ++ default: p = "not defined"; break; ++ } ++ CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p); ++ CX18_INFO_DEV(sd, "Audio muted: %s\n", ++ (mute_ctl & 0x2) ? "yes" : "no"); ++ CX18_INFO_DEV(sd, "Audio microcontroller: %s\n", ++ (download_ctl & 0x10) ? "running" : "stopped"); ++ ++ switch (audio_config >> 4) { ++ case 0x00: p = "undefined"; break; ++ case 0x01: p = "BTSC"; break; ++ case 0x02: p = "EIAJ"; break; ++ case 0x03: p = "A2-M"; break; ++ case 0x04: p = "A2-BG"; break; ++ case 0x05: p = "A2-DK1"; break; ++ case 0x06: p = "A2-DK2"; break; ++ case 0x07: p = "A2-DK3"; break; ++ case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; ++ case 0x09: p = "AM-L"; break; ++ case 0x0a: p = "NICAM-BG"; break; ++ case 0x0b: p = "NICAM-DK"; break; ++ case 0x0c: p = "NICAM-I"; break; ++ case 0x0d: p = "NICAM-L"; break; ++ case 0x0e: p = "FM radio"; break; ++ case 0x0f: p = "automatic detection"; break; ++ default: p = "undefined"; break; ++ } ++ CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p); ++ ++ if ((audio_config >> 4) < 0xF) { ++ switch (audio_config & 0xF) { ++ case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; ++ case 0x01: p = "MONO2 (LANGUAGE B)"; break; ++ case 0x02: p = "MONO3 (STEREO forced MONO)"; break; ++ case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; ++ case 0x04: p = "STEREO"; break; ++ case 0x05: p = "DUAL1 (AC)"; break; ++ case 0x06: p = "DUAL2 (BC)"; break; ++ case 0x07: p = "DUAL3 (AB)"; break; ++ default: p = "undefined"; ++ } ++ CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p); ++ } else { ++ switch (audio_config & 0xF) { ++ case 0x00: p = "BG"; break; ++ case 0x01: p = "DK1"; break; ++ case 0x02: p = "DK2"; break; ++ case 0x03: p = "DK3"; break; ++ case 0x04: p = "I"; break; ++ case 0x05: p = "L"; break; ++ case 0x06: p = "BTSC"; break; ++ case 0x07: p = "EIAJ"; break; ++ case 0x08: p = "A2-M"; break; ++ case 0x09: p = "FM Radio (4.5 MHz)"; break; ++ case 0x0a: p = "FM Radio (5.5 MHz)"; break; ++ case 0x0b: p = "S-Video"; break; ++ case 0x0f: p = "automatic standard and mode detection"; break; ++ default: p = "undefined"; break; ++ } ++ CX18_INFO_DEV(sd, "Configured audio system: %s\n", p); ++ } ++ ++ if (aud_input) ++ CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n", ++ aud_input); ++ else ++ CX18_INFO_DEV(sd, "Specified audio input: External\n"); ++ ++ switch (pref_mode & 0xf) { ++ case 0: p = "mono/language A"; break; ++ case 1: p = "language B"; break; ++ case 2: p = "language C"; break; ++ case 3: p = "analog fallback"; break; ++ case 4: p = "stereo"; break; ++ case 5: p = "language AC"; break; ++ case 6: p = "language BC"; break; ++ case 7: p = "language AB"; break; ++ default: p = "undefined"; break; ++ } ++ CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p); ++ ++ if ((audio_config & 0xf) == 0xf) { ++ switch ((afc0 >> 3) & 0x1) { ++ case 0: p = "system DK"; break; ++ case 1: p = "system L"; break; ++ } ++ CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p); ++ ++ switch (afc0 & 0x7) { ++ case 0: p = "Chroma"; break; ++ case 1: p = "BTSC"; break; ++ case 2: p = "EIAJ"; break; ++ case 3: p = "A2-M"; break; ++ case 4: p = "autodetect"; break; ++ default: p = "undefined"; break; ++ } ++ CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p); ++ } ++} ++ ++static int cx18_av_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ log_video_status(cx); ++ log_audio_status(cx); ++ return 0; ++} ++ ++static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match) ++{ ++ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1; ++} ++ ++static int cx18_av_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct cx18_av_state *state = to_cx18_av_state(sd); ++ ++ if (cx18_av_dbg_match(&chip->match)) { ++ chip->ident = state->id; ++ chip->revision = state->rev; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cx18_av_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ if (!cx18_av_dbg_match(®->match)) ++ return -EINVAL; ++ if ((reg->reg & 0x3) != 0) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->size = 4; ++ reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc); ++ return 0; ++} ++ ++static int cx18_av_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ if (!cx18_av_dbg_match(®->match)) ++ return -EINVAL; ++ if ((reg->reg & 0x3) != 0) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val); ++ return 0; ++} ++#endif ++ ++static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = { ++ .s_ctrl = cx18_av_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops cx18_av_general_ops = { ++ .g_chip_ident = cx18_av_g_chip_ident, ++ .log_status = cx18_av_log_status, ++ .load_fw = cx18_av_load_fw, ++ .reset = cx18_av_reset, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++ .s_std = cx18_av_s_std, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = cx18_av_g_register, ++ .s_register = cx18_av_s_register, ++#endif ++}; ++ ++static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = { ++ .s_radio = cx18_av_s_radio, ++ .s_frequency = cx18_av_s_frequency, ++ .g_tuner = cx18_av_g_tuner, ++ .s_tuner = cx18_av_s_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { ++ .s_clock_freq = cx18_av_s_clock_freq, ++ .s_routing = cx18_av_s_audio_routing, ++}; ++ ++static const struct v4l2_subdev_video_ops cx18_av_video_ops = { ++ .s_routing = cx18_av_s_video_routing, ++ .s_stream = cx18_av_s_stream, ++ .s_mbus_fmt = cx18_av_s_mbus_fmt, ++}; ++ ++static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { ++ .decode_vbi_line = cx18_av_decode_vbi_line, ++ .g_sliced_fmt = cx18_av_g_sliced_fmt, ++ .s_sliced_fmt = cx18_av_s_sliced_fmt, ++ .s_raw_fmt = cx18_av_s_raw_fmt, ++}; ++ ++static const struct v4l2_subdev_ops cx18_av_ops = { ++ .core = &cx18_av_general_ops, ++ .tuner = &cx18_av_tuner_ops, ++ .audio = &cx18_av_audio_ops, ++ .video = &cx18_av_video_ops, ++ .vbi = &cx18_av_vbi_ops, ++}; ++ ++int cx18_av_probe(struct cx18 *cx) ++{ ++ struct cx18_av_state *state = &cx->av_state; ++ struct v4l2_subdev *sd; ++ int err; ++ ++ state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; ++ state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) ++ ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN; ++ ++ state->vid_input = CX18_AV_COMPOSITE7; ++ state->aud_input = CX18_AV_AUDIO8; ++ state->audclk_freq = 48000; ++ state->audmode = V4L2_TUNER_MODE_LANG1; ++ state->slicer_line_delay = 0; ++ state->slicer_line_offset = (10 + state->slicer_line_delay - 2); ++ ++ sd = &state->sd; ++ v4l2_subdev_init(sd, &cx18_av_ops); ++ v4l2_set_subdevdata(sd, cx); ++ snprintf(sd->name, sizeof(sd->name), ++ "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); ++ sd->grp_id = CX18_HW_418_AV; ++ v4l2_ctrl_handler_init(&state->hdl, 9); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, ++ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, ++ V4L2_CID_CONTRAST, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, ++ V4L2_CID_SATURATION, 0, 127, 1, 64); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, ++ V4L2_CID_HUE, -128, 127, 1, 0); ++ ++ state->volume = v4l2_ctrl_new_std(&state->hdl, ++ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, ++ 0, 65535, 65535 / 100, 0); ++ v4l2_ctrl_new_std(&state->hdl, ++ &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, ++ 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, ++ V4L2_CID_AUDIO_BALANCE, ++ 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, ++ V4L2_CID_AUDIO_BASS, ++ 0, 65535, 65535 / 100, 32768); ++ v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, ++ V4L2_CID_AUDIO_TREBLE, ++ 0, 65535, 65535 / 100, 32768); ++ sd->ctrl_handler = &state->hdl; ++ if (state->hdl.error) { ++ int err = state->hdl.error; ++ ++ v4l2_ctrl_handler_free(&state->hdl); ++ return err; ++ } ++ err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); ++ if (err) ++ v4l2_ctrl_handler_free(&state->hdl); ++ else ++ cx18_av_init(cx); ++ return err; ++} +diff --git a/drivers/media/pci/cx18/cx18-av-core.h b/drivers/media/pci/cx18/cx18-av-core.h +new file mode 100644 +index 0000000..e9c69d9 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-av-core.h +@@ -0,0 +1,391 @@ ++/* ++ * cx18 ADEC header ++ * ++ * Derived from cx25840-core.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#ifndef _CX18_AV_CORE_H_ ++#define _CX18_AV_CORE_H_ ++ ++#include ++#include ++ ++struct cx18; ++ ++enum cx18_av_video_input { ++ /* Composite video inputs In1-In8 */ ++ CX18_AV_COMPOSITE1 = 1, ++ CX18_AV_COMPOSITE2, ++ CX18_AV_COMPOSITE3, ++ CX18_AV_COMPOSITE4, ++ CX18_AV_COMPOSITE5, ++ CX18_AV_COMPOSITE6, ++ CX18_AV_COMPOSITE7, ++ CX18_AV_COMPOSITE8, ++ ++ /* S-Video inputs consist of one luma input (In1-In8) ORed with one ++ chroma input (In5-In8) */ ++ CX18_AV_SVIDEO_LUMA1 = 0x10, ++ CX18_AV_SVIDEO_LUMA2 = 0x20, ++ CX18_AV_SVIDEO_LUMA3 = 0x30, ++ CX18_AV_SVIDEO_LUMA4 = 0x40, ++ CX18_AV_SVIDEO_LUMA5 = 0x50, ++ CX18_AV_SVIDEO_LUMA6 = 0x60, ++ CX18_AV_SVIDEO_LUMA7 = 0x70, ++ CX18_AV_SVIDEO_LUMA8 = 0x80, ++ CX18_AV_SVIDEO_CHROMA4 = 0x400, ++ CX18_AV_SVIDEO_CHROMA5 = 0x500, ++ CX18_AV_SVIDEO_CHROMA6 = 0x600, ++ CX18_AV_SVIDEO_CHROMA7 = 0x700, ++ CX18_AV_SVIDEO_CHROMA8 = 0x800, ++ ++ /* S-Video aliases for common luma/chroma combinations */ ++ CX18_AV_SVIDEO1 = 0x510, ++ CX18_AV_SVIDEO2 = 0x620, ++ CX18_AV_SVIDEO3 = 0x730, ++ CX18_AV_SVIDEO4 = 0x840, ++ ++ /* Component Video inputs consist of one luma input (In1-In8) ORed ++ with a red chroma (In4-In6) and blue chroma input (In7-In8) */ ++ CX18_AV_COMPONENT_LUMA1 = 0x1000, ++ CX18_AV_COMPONENT_LUMA2 = 0x2000, ++ CX18_AV_COMPONENT_LUMA3 = 0x3000, ++ CX18_AV_COMPONENT_LUMA4 = 0x4000, ++ CX18_AV_COMPONENT_LUMA5 = 0x5000, ++ CX18_AV_COMPONENT_LUMA6 = 0x6000, ++ CX18_AV_COMPONENT_LUMA7 = 0x7000, ++ CX18_AV_COMPONENT_LUMA8 = 0x8000, ++ CX18_AV_COMPONENT_R_CHROMA4 = 0x40000, ++ CX18_AV_COMPONENT_R_CHROMA5 = 0x50000, ++ CX18_AV_COMPONENT_R_CHROMA6 = 0x60000, ++ CX18_AV_COMPONENT_B_CHROMA7 = 0x700000, ++ CX18_AV_COMPONENT_B_CHROMA8 = 0x800000, ++ ++ /* Component Video aliases for common combinations */ ++ CX18_AV_COMPONENT1 = 0x861000, ++}; ++ ++enum cx18_av_audio_input { ++ /* Audio inputs: serial or In4-In8 */ ++ CX18_AV_AUDIO_SERIAL1, ++ CX18_AV_AUDIO_SERIAL2, ++ CX18_AV_AUDIO4 = 4, ++ CX18_AV_AUDIO5, ++ CX18_AV_AUDIO6, ++ CX18_AV_AUDIO7, ++ CX18_AV_AUDIO8, ++}; ++ ++struct cx18_av_state { ++ struct v4l2_subdev sd; ++ struct v4l2_ctrl_handler hdl; ++ struct v4l2_ctrl *volume; ++ int radio; ++ v4l2_std_id std; ++ enum cx18_av_video_input vid_input; ++ enum cx18_av_audio_input aud_input; ++ u32 audclk_freq; ++ int audmode; ++ u32 id; ++ u32 rev; ++ int is_initialized; ++ ++ /* ++ * The VBI slicer starts operating and counting lines, beginning at ++ * slicer line count of 1, at D lines after the deassertion of VRESET. ++ * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525 ++ * line systems respectively. Sliced ancillary data captured on VBI ++ * slicer line M is inserted after the VBI slicer is done with line M, ++ * when VBI slicer line count is N = M+1. Thus when the VBI slicer ++ * reports a VBI slicer line number with ancillary data, the IDID0 byte ++ * indicates VBI slicer line N. The actual field line that the captured ++ * data comes from is ++ * ++ * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2). ++ * ++ * L is the line in the field, not frame, from which the VBI data came. ++ * N is the line reported by the slicer in the ancillary data. ++ * D is the slicer_line_delay value programmed into register 0x47f. ++ * S is 6 for 625 line systems or 10 for 525 line systems ++ * (S+D-2) is the slicer_line_offset used to convert slicer reported ++ * line counts to actual field lines. ++ */ ++ int slicer_line_delay; ++ int slicer_line_offset; ++}; ++ ++ ++/* Registers */ ++#define CXADEC_CHIP_TYPE_TIGER 0x837 ++#define CXADEC_CHIP_TYPE_MAKO 0x843 ++ ++#define CXADEC_HOST_REG1 0x000 ++#define CXADEC_HOST_REG2 0x001 ++ ++#define CXADEC_CHIP_CTRL 0x100 ++#define CXADEC_AFE_CTRL 0x104 ++#define CXADEC_PLL_CTRL1 0x108 ++#define CXADEC_VID_PLL_FRAC 0x10C ++#define CXADEC_AUX_PLL_FRAC 0x110 ++#define CXADEC_PIN_CTRL1 0x114 ++#define CXADEC_PIN_CTRL2 0x118 ++#define CXADEC_PIN_CFG1 0x11C ++#define CXADEC_PIN_CFG2 0x120 ++ ++#define CXADEC_PIN_CFG3 0x124 ++#define CXADEC_I2S_MCLK 0x127 ++ ++#define CXADEC_AUD_LOCK1 0x128 ++#define CXADEC_AUD_LOCK2 0x12C ++#define CXADEC_POWER_CTRL 0x130 ++#define CXADEC_AFE_DIAG_CTRL1 0x134 ++#define CXADEC_AFE_DIAG_CTRL2 0x138 ++#define CXADEC_AFE_DIAG_CTRL3 0x13C ++#define CXADEC_PLL_DIAG_CTRL 0x140 ++#define CXADEC_TEST_CTRL1 0x144 ++#define CXADEC_TEST_CTRL2 0x148 ++#define CXADEC_BIST_STAT 0x14C ++#define CXADEC_DLL1_DIAG_CTRL 0x158 ++#define CXADEC_DLL2_DIAG_CTRL 0x15C ++ ++/* IR registers */ ++#define CXADEC_IR_CTRL_REG 0x200 ++#define CXADEC_IR_TXCLK_REG 0x204 ++#define CXADEC_IR_RXCLK_REG 0x208 ++#define CXADEC_IR_CDUTY_REG 0x20C ++#define CXADEC_IR_STAT_REG 0x210 ++#define CXADEC_IR_IRQEN_REG 0x214 ++#define CXADEC_IR_FILTER_REG 0x218 ++#define CXADEC_IR_FIFO_REG 0x21C ++ ++/* Video Registers */ ++#define CXADEC_MODE_CTRL 0x400 ++#define CXADEC_OUT_CTRL1 0x404 ++#define CXADEC_OUT_CTRL2 0x408 ++#define CXADEC_GEN_STAT 0x40C ++#define CXADEC_INT_STAT_MASK 0x410 ++#define CXADEC_LUMA_CTRL 0x414 ++ ++#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414 ++#define CXADEC_CONTRAST_CTRL_BYTE 0x415 ++#define CXADEC_LUMA_CTRL_BYTE_3 0x416 ++ ++#define CXADEC_HSCALE_CTRL 0x418 ++#define CXADEC_VSCALE_CTRL 0x41C ++ ++#define CXADEC_CHROMA_CTRL 0x420 ++ ++#define CXADEC_USAT_CTRL_BYTE 0x420 ++#define CXADEC_VSAT_CTRL_BYTE 0x421 ++#define CXADEC_HUE_CTRL_BYTE 0x422 ++ ++#define CXADEC_VBI_LINE_CTRL1 0x424 ++#define CXADEC_VBI_LINE_CTRL2 0x428 ++#define CXADEC_VBI_LINE_CTRL3 0x42C ++#define CXADEC_VBI_LINE_CTRL4 0x430 ++#define CXADEC_VBI_LINE_CTRL5 0x434 ++#define CXADEC_VBI_FC_CFG 0x438 ++#define CXADEC_VBI_MISC_CFG1 0x43C ++#define CXADEC_VBI_MISC_CFG2 0x440 ++#define CXADEC_VBI_PAY1 0x444 ++#define CXADEC_VBI_PAY2 0x448 ++#define CXADEC_VBI_CUST1_CFG1 0x44C ++#define CXADEC_VBI_CUST1_CFG2 0x450 ++#define CXADEC_VBI_CUST1_CFG3 0x454 ++#define CXADEC_VBI_CUST2_CFG1 0x458 ++#define CXADEC_VBI_CUST2_CFG2 0x45C ++#define CXADEC_VBI_CUST2_CFG3 0x460 ++#define CXADEC_VBI_CUST3_CFG1 0x464 ++#define CXADEC_VBI_CUST3_CFG2 0x468 ++#define CXADEC_VBI_CUST3_CFG3 0x46C ++#define CXADEC_HORIZ_TIM_CTRL 0x470 ++#define CXADEC_VERT_TIM_CTRL 0x474 ++#define CXADEC_SRC_COMB_CFG 0x478 ++#define CXADEC_CHROMA_VBIOFF_CFG 0x47C ++#define CXADEC_FIELD_COUNT 0x480 ++#define CXADEC_MISC_TIM_CTRL 0x484 ++#define CXADEC_DFE_CTRL1 0x488 ++#define CXADEC_DFE_CTRL2 0x48C ++#define CXADEC_DFE_CTRL3 0x490 ++#define CXADEC_PLL_CTRL2 0x494 ++#define CXADEC_HTL_CTRL 0x498 ++#define CXADEC_COMB_CTRL 0x49C ++#define CXADEC_CRUSH_CTRL 0x4A0 ++#define CXADEC_SOFT_RST_CTRL 0x4A4 ++#define CXADEC_MV_DT_CTRL2 0x4A8 ++#define CXADEC_MV_DT_CTRL3 0x4AC ++#define CXADEC_MISC_DIAG_CTRL 0x4B8 ++ ++#define CXADEC_DL_CTL 0x800 ++#define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ ++#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ ++#define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ ++#define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ ++ ++#define CXADEC_STD_DET_STATUS 0x804 ++ ++#define CXADEC_STD_DET_CTL 0x808 ++#define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ ++#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ ++ ++#define CXADEC_DW8051_INT 0x80C ++#define CXADEC_GENERAL_CTL 0x810 ++#define CXADEC_AAGC_CTL 0x814 ++#define CXADEC_IF_SRC_CTL 0x818 ++#define CXADEC_ANLOG_DEMOD_CTL 0x81C ++#define CXADEC_ROT_FREQ_CTL 0x820 ++#define CXADEC_FM1_CTL 0x824 ++#define CXADEC_PDF_CTL 0x828 ++#define CXADEC_DFT1_CTL1 0x82C ++#define CXADEC_DFT1_CTL2 0x830 ++#define CXADEC_DFT_STATUS 0x834 ++#define CXADEC_DFT2_CTL1 0x838 ++#define CXADEC_DFT2_CTL2 0x83C ++#define CXADEC_DFT2_STATUS 0x840 ++#define CXADEC_DFT3_CTL1 0x844 ++#define CXADEC_DFT3_CTL2 0x848 ++#define CXADEC_DFT3_STATUS 0x84C ++#define CXADEC_DFT4_CTL1 0x850 ++#define CXADEC_DFT4_CTL2 0x854 ++#define CXADEC_DFT4_STATUS 0x858 ++#define CXADEC_AM_MTS_DET 0x85C ++#define CXADEC_ANALOG_MUX_CTL 0x860 ++#define CXADEC_DIG_PLL_CTL1 0x864 ++#define CXADEC_DIG_PLL_CTL2 0x868 ++#define CXADEC_DIG_PLL_CTL3 0x86C ++#define CXADEC_DIG_PLL_CTL4 0x870 ++#define CXADEC_DIG_PLL_CTL5 0x874 ++#define CXADEC_DEEMPH_GAIN_CTL 0x878 ++#define CXADEC_DEEMPH_COEF1 0x87C ++#define CXADEC_DEEMPH_COEF2 0x880 ++#define CXADEC_DBX1_CTL1 0x884 ++#define CXADEC_DBX1_CTL2 0x888 ++#define CXADEC_DBX1_STATUS 0x88C ++#define CXADEC_DBX2_CTL1 0x890 ++#define CXADEC_DBX2_CTL2 0x894 ++#define CXADEC_DBX2_STATUS 0x898 ++#define CXADEC_AM_FM_DIFF 0x89C ++ ++/* NICAM registers go here */ ++#define CXADEC_NICAM_STATUS 0x8C8 ++#define CXADEC_DEMATRIX_CTL 0x8CC ++ ++#define CXADEC_PATH1_CTL1 0x8D0 ++#define CXADEC_PATH1_VOL_CTL 0x8D4 ++#define CXADEC_PATH1_EQ_CTL 0x8D8 ++#define CXADEC_PATH1_SC_CTL 0x8DC ++ ++#define CXADEC_PATH2_CTL1 0x8E0 ++#define CXADEC_PATH2_VOL_CTL 0x8E4 ++#define CXADEC_PATH2_EQ_CTL 0x8E8 ++#define CXADEC_PATH2_SC_CTL 0x8EC ++ ++#define CXADEC_SRC_CTL 0x8F0 ++#define CXADEC_SRC_LF_COEF 0x8F4 ++#define CXADEC_SRC1_CTL 0x8F8 ++#define CXADEC_SRC2_CTL 0x8FC ++#define CXADEC_SRC3_CTL 0x900 ++#define CXADEC_SRC4_CTL 0x904 ++#define CXADEC_SRC5_CTL 0x908 ++#define CXADEC_SRC6_CTL 0x90C ++ ++#define CXADEC_BASEBAND_OUT_SEL 0x910 ++#define CXADEC_I2S_IN_CTL 0x914 ++#define CXADEC_I2S_OUT_CTL 0x918 ++#define CXADEC_AC97_CTL 0x91C ++#define CXADEC_QAM_PDF 0x920 ++#define CXADEC_QAM_CONST_DEC 0x924 ++#define CXADEC_QAM_ROTATOR_FREQ 0x948 ++ ++/* Bit definitions / settings used in Mako Audio */ ++#define CXADEC_PREF_MODE_MONO_LANGA 0 ++#define CXADEC_PREF_MODE_MONO_LANGB 1 ++#define CXADEC_PREF_MODE_MONO_LANGC 2 ++#define CXADEC_PREF_MODE_FALLBACK 3 ++#define CXADEC_PREF_MODE_STEREO 4 ++#define CXADEC_PREF_MODE_DUAL_LANG_AC 5 ++#define CXADEC_PREF_MODE_DUAL_LANG_BC 6 ++#define CXADEC_PREF_MODE_DUAL_LANG_AB 7 ++ ++ ++#define CXADEC_DETECT_STEREO 1 ++#define CXADEC_DETECT_DUAL 2 ++#define CXADEC_DETECT_TRI 4 ++#define CXADEC_DETECT_SAP 0x10 ++#define CXADEC_DETECT_NO_SIGNAL 0xFF ++ ++#define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */ ++#define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */ ++#define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2 ++#define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3 ++#define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */ ++#define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */ ++#define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6 ++#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7 ++#define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */ ++#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ ++#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ ++ ++static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct cx18_av_state, sd); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* cx18_av-core.c */ ++int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); ++int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); ++int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value); ++int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask); ++int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, ++ u32 mask); ++u8 cx18_av_read(struct cx18 *cx, u16 addr); ++u32 cx18_av_read4(struct cx18 *cx, u16 addr); ++int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); ++int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); ++void cx18_av_std_setup(struct cx18 *cx); ++ ++int cx18_av_probe(struct cx18 *cx); ++ ++/* ----------------------------------------------------------------------- */ ++/* cx18_av-firmware.c */ ++int cx18_av_loadfw(struct cx18 *cx); ++ ++/* ----------------------------------------------------------------------- */ ++/* cx18_av-audio.c */ ++int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq); ++void cx18_av_audio_set_path(struct cx18 *cx); ++extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops; ++ ++/* ----------------------------------------------------------------------- */ ++/* cx18_av-vbi.c */ ++int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, ++ struct v4l2_decode_vbi_line *vbi); ++int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); ++int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); ++int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); ++ ++#endif +diff --git a/drivers/media/pci/cx18/cx18-av-firmware.c b/drivers/media/pci/cx18/cx18-av-firmware.c +new file mode 100644 +index 0000000..a34fd08 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-av-firmware.c +@@ -0,0 +1,225 @@ ++/* ++ * cx18 ADEC firmware functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include ++ ++#define CX18_AUDIO_ENABLE 0xc72014 ++#define CX18_AI1_MUX_MASK 0x30 ++#define CX18_AI1_MUX_I2S1 0x00 ++#define CX18_AI1_MUX_I2S2 0x10 ++#define CX18_AI1_MUX_843_I2S 0x20 ++#define CX18_AI1_MUX_INVALID 0x30 ++ ++#define FWFILE "v4l-cx23418-dig.fw" ++ ++static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw) ++{ ++ struct v4l2_subdev *sd = &cx->av_state.sd; ++ int ret = 0; ++ const u8 *data; ++ u32 size; ++ int addr; ++ u32 expected, dl_control; ++ ++ /* Ensure we put the 8051 in reset and enable firmware upload mode */ ++ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); ++ do { ++ dl_control &= 0x00ffffff; ++ dl_control |= 0x0f000000; ++ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control); ++ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); ++ } while ((dl_control & 0xff000000) != 0x0f000000); ++ ++ /* Read and auto increment until at address 0x0000 */ ++ while (dl_control & 0x3fff) ++ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); ++ ++ data = fw->data; ++ size = fw->size; ++ for (addr = 0; addr < size; addr++) { ++ dl_control &= 0xffff3fff; /* ignore top 2 bits of address */ ++ expected = 0x0f000000 | ((u32)data[addr] << 16) | addr; ++ if (expected != dl_control) { ++ CX18_ERR_DEV(sd, "verification of %s firmware load " ++ "failed: expected %#010x got %#010x\n", ++ FWFILE, expected, dl_control); ++ ret = -EIO; ++ break; ++ } ++ dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); ++ } ++ if (ret == 0) ++ CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n", ++ FWFILE, size); ++ return ret; ++} ++ ++int cx18_av_loadfw(struct cx18 *cx) ++{ ++ struct v4l2_subdev *sd = &cx->av_state.sd; ++ const struct firmware *fw = NULL; ++ u32 size; ++ u32 u, v; ++ const u8 *ptr; ++ int i; ++ int retries1 = 0; ++ ++ if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) { ++ CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE); ++ return -EINVAL; ++ } ++ ++ /* The firmware load often has byte errors, so allow for several ++ retries, both at byte level and at the firmware load level. */ ++ while (retries1 < 5) { ++ cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000, ++ 0x00008430, 0xffffffff); /* cx25843 */ ++ cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff); ++ ++ /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */ ++ cx18_av_write4_expect(cx, 0x8100, 0x00010000, ++ 0x00008430, 0xffffffff); /* cx25843 */ ++ ++ /* Put the 8051 in reset and enable firmware upload */ ++ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000); ++ ++ ptr = fw->data; ++ size = fw->size; ++ ++ for (i = 0; i < size; i++) { ++ u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); ++ u32 value = 0; ++ int retries2; ++ int unrec_err = 0; ++ ++ for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES; ++ retries2++) { ++ cx18_av_write4_noretry(cx, CXADEC_DL_CTL, ++ dl_control); ++ udelay(10); ++ value = cx18_av_read4(cx, CXADEC_DL_CTL); ++ if (value == dl_control) ++ break; ++ /* Check if we can correct the byte by changing ++ the address. We can only write the lower ++ address byte of the address. */ ++ if ((value & 0x3F00) != (dl_control & 0x3F00)) { ++ unrec_err = 1; ++ break; ++ } ++ } ++ if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES) ++ break; ++ } ++ if (i == size) ++ break; ++ retries1++; ++ } ++ if (retries1 >= 5) { ++ CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE); ++ release_firmware(fw); ++ return -EIO; ++ } ++ ++ cx18_av_write4_expect(cx, CXADEC_DL_CTL, ++ 0x03000000 | fw->size, 0x03000000, 0x13000000); ++ ++ CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); ++ ++ if (cx18_av_verifyfw(cx, fw) == 0) ++ cx18_av_write4_expect(cx, CXADEC_DL_CTL, ++ 0x13000000 | fw->size, 0x13000000, 0x13000000); ++ ++ /* Output to the 416 */ ++ cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); ++ ++ /* Audio input control 1 set to Sony mode */ ++ /* Audio output input 2 is 0 for slave operation input */ ++ /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ ++ /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge ++ after WS transition for first bit of audio word. */ ++ cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0); ++ ++ /* Audio output control 1 is set to Sony mode */ ++ /* Audio output control 2 is set to 1 for master mode */ ++ /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ ++ /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge ++ after WS transition for first bit of audio word. */ ++ /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT ++ are generated) */ ++ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); ++ ++ /* set alt I2s master clock to /0x16 and enable alt divider i2s ++ passthrough */ ++ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687); ++ ++ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6, ++ 0x3F00FFFF); ++ /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ ++ ++ /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ ++ /* Register 0x09CC is defined by the Merlin firmware, and doesn't ++ have a name in the spec. */ ++ cx18_av_write4(cx, 0x09CC, 1); ++ ++ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); ++ /* If bit 11 is 1, clear bit 10 */ ++ if (v & 0x800) ++ cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, ++ 0, 0x400); ++ ++ /* Toggle the AI1 MUX */ ++ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); ++ u = v & CX18_AI1_MUX_MASK; ++ v &= ~CX18_AI1_MUX_MASK; ++ if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) { ++ /* Switch to I2S1 */ ++ v |= CX18_AI1_MUX_I2S1; ++ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, ++ v, CX18_AI1_MUX_MASK); ++ /* Switch back to the A/V decoder core I2S output */ ++ v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S; ++ } else { ++ /* Switch to the A/V decoder core I2S output */ ++ v |= CX18_AI1_MUX_843_I2S; ++ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, ++ v, CX18_AI1_MUX_MASK); ++ /* Switch back to I2S1 or I2S2 */ ++ v = (v & ~CX18_AI1_MUX_MASK) | u; ++ } ++ cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, ++ v, CX18_AI1_MUX_MASK); ++ ++ /* Enable WW auto audio standard detection */ ++ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); ++ v |= 0xFF; /* Auto by default */ ++ v |= 0x400; /* Stereo by default */ ++ v |= 0x14000000; ++ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); ++ ++ release_firmware(fw); ++ return 0; ++} ++ ++MODULE_FIRMWARE(FWFILE); +diff --git a/drivers/media/pci/cx18/cx18-av-vbi.c b/drivers/media/pci/cx18/cx18-av-vbi.c +new file mode 100644 +index 0000000..2469828 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-av-vbi.c +@@ -0,0 +1,313 @@ ++/* ++ * cx18 ADEC VBI functions ++ * ++ * Derived from cx25840-vbi.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++ ++#include "cx18-driver.h" ++ ++/* ++ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode, ++ * NN counts 1 byte Dwords, an IDID with the VBI line # in it. ++ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines ++ * (should!) look like: ++ * 4 byte EAV code: 0xff 0x00 0x00 0xRP ++ * unknown number of possible idle bytes ++ * 3 byte Anc data preamble: 0x00 0xff 0xff ++ * 1 byte data identifier: ne010iii (parity bits, 010, DID bits) ++ * 1 byte secondary data id: nessssss (parity bits, SDID bits) ++ * 1 byte data word count: necccccc (parity bits, NN Dword count) ++ * 2 byte Internal DID: VBI-line-# 0x80 ++ * NN data bytes ++ * 1 byte checksum ++ * Fill bytes needed to fil out to 4*NN bytes of payload ++ * ++ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, & ++ * in the vertical blanking interval are: ++ * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0) ++ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0) ++ * ++ * Since the V bit is only allowed to toggle in the EAV RP code, just ++ * before the first active region line and for active lines, they are: ++ * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0) ++ * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0) ++ * ++ * The user application DID bytes we care about are: ++ * 0x91 (1 0 010 0 !ActiveLine AncDataPresent) ++ * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent) ++ * ++ */ ++static const u8 sliced_vbi_did[2] = { 0x91, 0x55 }; ++ ++struct vbi_anc_data { ++ /* u8 eav[4]; */ ++ /* u8 idle[]; Variable number of idle bytes */ ++ u8 preamble[3]; ++ u8 did; ++ u8 sdid; ++ u8 data_count; ++ u8 idid[2]; ++ u8 payload[1]; /* data_count of payload */ ++ /* u8 checksum; */ ++ /* u8 fill[]; Variable number of fill bytes */ ++}; ++ ++static int odd_parity(u8 c) ++{ ++ c ^= (c >> 4); ++ c ^= (c >> 2); ++ c ^= (c >> 1); ++ ++ return c & 1; ++} ++ ++static int decode_vps(u8 *dst, u8 *p) ++{ ++ static const u8 biphase_tbl[] = { ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, ++ 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, ++ 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, ++ 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, ++ 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, ++ 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, ++ 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, ++ 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, ++ 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, ++ 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, ++ 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, ++ 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, ++ 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, ++ 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, ++ 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, ++ 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, ++ }; ++ ++ u8 c, err = 0; ++ int i; ++ ++ for (i = 0; i < 2 * 13; i += 2) { ++ err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; ++ c = (biphase_tbl[p[i + 1]] & 0xf) | ++ ((biphase_tbl[p[i]] & 0xf) << 4); ++ dst[i / 2] = c; ++ } ++ ++ return err & 0xf0; ++} ++ ++int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ struct cx18_av_state *state = &cx->av_state; ++ static const u16 lcr2vbi[] = { ++ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ ++ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ ++ V4L2_SLICED_CAPTION_525, /* 6 */ ++ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ ++ 0, 0, 0, 0 ++ }; ++ int is_pal = !(state->std & V4L2_STD_525_60); ++ int i; ++ ++ memset(svbi->service_lines, 0, sizeof(svbi->service_lines)); ++ svbi->service_set = 0; ++ ++ /* we're done if raw VBI is active */ ++ if ((cx18_av_read(cx, 0x404) & 0x10) == 0) ++ return 0; ++ ++ if (is_pal) { ++ for (i = 7; i <= 23; i++) { ++ u8 v = cx18_av_read(cx, 0x424 + i - 7); ++ ++ svbi->service_lines[0][i] = lcr2vbi[v >> 4]; ++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; ++ svbi->service_set |= svbi->service_lines[0][i] | ++ svbi->service_lines[1][i]; ++ } ++ } else { ++ for (i = 10; i <= 21; i++) { ++ u8 v = cx18_av_read(cx, 0x424 + i - 10); ++ ++ svbi->service_lines[0][i] = lcr2vbi[v >> 4]; ++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; ++ svbi->service_set |= svbi->service_lines[0][i] | ++ svbi->service_lines[1][i]; ++ } ++ } ++ return 0; ++} ++ ++int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ struct cx18_av_state *state = &cx->av_state; ++ ++ /* Setup standard */ ++ cx18_av_std_setup(cx); ++ ++ /* VBI Offset */ ++ cx18_av_write(cx, 0x47f, state->slicer_line_delay); ++ cx18_av_write(cx, 0x404, 0x2e); ++ return 0; ++} ++ ++int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ struct cx18_av_state *state = &cx->av_state; ++ int is_pal = !(state->std & V4L2_STD_525_60); ++ int i, x; ++ u8 lcr[24]; ++ ++ for (x = 0; x <= 23; x++) ++ lcr[x] = 0x00; ++ ++ /* Setup standard */ ++ cx18_av_std_setup(cx); ++ ++ /* Sliced VBI */ ++ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ ++ cx18_av_write(cx, 0x406, 0x13); ++ cx18_av_write(cx, 0x47f, state->slicer_line_delay); ++ ++ /* Force impossible lines to 0 */ ++ if (is_pal) { ++ for (i = 0; i <= 6; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ } else { ++ for (i = 0; i <= 9; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ ++ for (i = 22; i <= 23; i++) ++ svbi->service_lines[0][i] = ++ svbi->service_lines[1][i] = 0; ++ } ++ ++ /* Build register values for requested service lines */ ++ for (i = 7; i <= 23; i++) { ++ for (x = 0; x <= 1; x++) { ++ switch (svbi->service_lines[1-x][i]) { ++ case V4L2_SLICED_TELETEXT_B: ++ lcr[i] |= 1 << (4 * x); ++ break; ++ case V4L2_SLICED_WSS_625: ++ lcr[i] |= 4 << (4 * x); ++ break; ++ case V4L2_SLICED_CAPTION_525: ++ lcr[i] |= 6 << (4 * x); ++ break; ++ case V4L2_SLICED_VPS: ++ lcr[i] |= 9 << (4 * x); ++ break; ++ } ++ } ++ } ++ ++ if (is_pal) { ++ for (x = 1, i = 0x424; i <= 0x434; i++, x++) ++ cx18_av_write(cx, i, lcr[6 + x]); ++ } else { ++ for (x = 1, i = 0x424; i <= 0x430; i++, x++) ++ cx18_av_write(cx, i, lcr[9 + x]); ++ for (i = 0x431; i <= 0x434; i++) ++ cx18_av_write(cx, i, 0); ++ } ++ ++ cx18_av_write(cx, 0x43c, 0x16); ++ /* Should match vblank set in cx18_av_std_setup() */ ++ cx18_av_write(cx, 0x474, is_pal ? 38 : 26); ++ return 0; ++} ++ ++int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, ++ struct v4l2_decode_vbi_line *vbi) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ struct cx18_av_state *state = &cx->av_state; ++ struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p; ++ u8 *p; ++ int did, sdid, l, err = 0; ++ ++ /* ++ * Check for the ancillary data header for sliced VBI ++ */ ++ if (anc->preamble[0] || ++ anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || ++ (anc->did != sliced_vbi_did[0] && ++ anc->did != sliced_vbi_did[1])) { ++ vbi->line = vbi->type = 0; ++ return 0; ++ } ++ ++ did = anc->did; ++ sdid = anc->sdid & 0xf; ++ l = anc->idid[0] & 0x3f; ++ l += state->slicer_line_offset; ++ p = anc->payload; ++ ++ /* Decode the SDID set by the slicer */ ++ switch (sdid) { ++ case 1: ++ sdid = V4L2_SLICED_TELETEXT_B; ++ break; ++ case 4: ++ sdid = V4L2_SLICED_WSS_625; ++ break; ++ case 6: ++ sdid = V4L2_SLICED_CAPTION_525; ++ err = !odd_parity(p[0]) || !odd_parity(p[1]); ++ break; ++ case 9: ++ sdid = V4L2_SLICED_VPS; ++ if (decode_vps(p, p) != 0) ++ err = 1; ++ break; ++ default: ++ sdid = 0; ++ err = 1; ++ break; ++ } ++ ++ vbi->type = err ? 0 : sdid; ++ vbi->line = err ? 0 : l; ++ vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); ++ vbi->p = p; ++ return 0; ++} +diff --git a/drivers/media/pci/cx18/cx18-cards.c b/drivers/media/pci/cx18/cx18-cards.c +new file mode 100644 +index 0000000..c07c849 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-cards.c +@@ -0,0 +1,638 @@ ++/* ++ * cx18 functions to query card hardware ++ * ++ * Derived from ivtv-cards.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-cards.h" ++#include "cx18-av-core.h" ++#include "cx18-i2c.h" ++#include ++ ++#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM) ++ ++/********************** card configuration *******************************/ ++ ++/* usual i2c tuner addresses to probe */ ++static struct cx18_card_tuner_i2c cx18_i2c_std = { ++ .radio = { I2C_CLIENT_END }, ++ .demod = { 0x43, I2C_CLIENT_END }, ++ .tv = { 0x61, 0x60, I2C_CLIENT_END }, ++}; ++ ++/* ++ * usual i2c tuner addresses to probe with additional demod address for ++ * an NXP TDA8295 at 0x42 (N.B. it can possibly be at 0x4b or 0x4c too). ++ */ ++static struct cx18_card_tuner_i2c cx18_i2c_nxp = { ++ .radio = { I2C_CLIENT_END }, ++ .demod = { 0x42, 0x43, I2C_CLIENT_END }, ++ .tv = { 0x61, 0x60, I2C_CLIENT_END }, ++}; ++ ++/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ ++ This keeps the PCI ID database up to date. Note that the entries ++ must be added under vendor 0x4444 (Conexant) as subsystem IDs. ++ New vendor IDs should still be added to the vendor ID list. */ ++ ++/* Hauppauge HVR-1600 cards */ ++ ++/* Note: for Hauppauge cards the tveeprom information is used instead ++ of PCI IDs */ ++static const struct cx18_card cx18_card_hvr1600_esmt = { ++ .type = CX18_CARD_HVR_1600_ESMT, ++ .name = "Hauppauge HVR-1600", ++ .comment = "Simultaneous Digital and Analog TV capture supported\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_CS5345, ++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | ++ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | ++ CX18_HW_Z8F0811_IR_HAUP, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, ++ { CX18_CARD_INPUT_LINE_IN1, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, ++ { CX18_CARD_INPUT_LINE_IN2, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, ++ .ddr = { ++ /* ESMT M13S128324A-5B memory */ ++ .chip_config = 0x003, ++ .refresh = 0x30c, ++ .timing1 = 0x44220e82, ++ .timing2 = 0x08, ++ .tune_lane = 0, ++ .initial_emrs = 0, ++ }, ++ .gpio_init.initial_value = 0x3001, ++ .gpio_init.direction = 0x3001, ++ .gpio_i2c_slave_reset = { ++ .active_lo_mask = 0x3001, ++ .msecs_asserted = 10, ++ .msecs_recovery = 40, ++ .ir_reset_mask = 0x0001, ++ }, ++ .i2c = &cx18_i2c_std, ++}; ++ ++static const struct cx18_card cx18_card_hvr1600_s5h1411 = { ++ .type = CX18_CARD_HVR_1600_S5H1411, ++ .name = "Hauppauge HVR-1600", ++ .comment = "Simultaneous Digital and Analog TV capture supported\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_CS5345, ++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | ++ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | ++ CX18_HW_Z8F0811_IR_HAUP, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, ++ { CX18_CARD_INPUT_LINE_IN1, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, ++ { CX18_CARD_INPUT_LINE_IN2, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, ++ .ddr = { ++ /* ESMT M13S128324A-5B memory */ ++ .chip_config = 0x003, ++ .refresh = 0x30c, ++ .timing1 = 0x44220e82, ++ .timing2 = 0x08, ++ .tune_lane = 0, ++ .initial_emrs = 0, ++ }, ++ .gpio_init.initial_value = 0x3801, ++ .gpio_init.direction = 0x3801, ++ .gpio_i2c_slave_reset = { ++ .active_lo_mask = 0x3801, ++ .msecs_asserted = 10, ++ .msecs_recovery = 40, ++ .ir_reset_mask = 0x0001, ++ }, ++ .i2c = &cx18_i2c_nxp, ++}; ++ ++static const struct cx18_card cx18_card_hvr1600_samsung = { ++ .type = CX18_CARD_HVR_1600_SAMSUNG, ++ .name = "Hauppauge HVR-1600 (Preproduction)", ++ .comment = "Simultaneous Digital and Analog TV capture supported\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_CS5345, ++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | ++ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | ++ CX18_HW_Z8F0811_IR_HAUP, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, ++ { CX18_CARD_INPUT_LINE_IN1, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, ++ { CX18_CARD_INPUT_LINE_IN2, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, ++ .ddr = { ++ /* Samsung K4D263238G-VC33 memory */ ++ .chip_config = 0x003, ++ .refresh = 0x30c, ++ .timing1 = 0x23230b73, ++ .timing2 = 0x08, ++ .tune_lane = 0, ++ .initial_emrs = 2, ++ }, ++ .gpio_init.initial_value = 0x3001, ++ .gpio_init.direction = 0x3001, ++ .gpio_i2c_slave_reset = { ++ .active_lo_mask = 0x3001, ++ .msecs_asserted = 10, ++ .msecs_recovery = 40, ++ .ir_reset_mask = 0x0001, ++ }, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Compro VideoMate H900: note that this card is analog only! */ ++ ++static const struct cx18_card_pci_info cx18_pci_h900[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_h900 = { ++ .type = CX18_CARD_COMPRO_H900, ++ .name = "Compro VideoMate H900", ++ .comment = "Analog TV capture supported\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, ++ CX18_AV_AUDIO_SERIAL1, 0 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, ++ CX18_AV_AUDIO_SERIAL1, 0 }, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .ddr = { ++ /* EtronTech EM6A9160TS-5G memory */ ++ .chip_config = 0x50003, ++ .refresh = 0x753, ++ .timing1 = 0x24330e84, ++ .timing2 = 0x1f, ++ .tune_lane = 0, ++ .initial_emrs = 0, ++ }, ++ .xceive_pin = 15, ++ .pci_list = cx18_pci_h900, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan MPC718: not working at the moment! */ ++ ++static const struct cx18_card_pci_info cx18_pci_mpc718[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_mpc718 = { ++ .type = CX18_CARD_YUAN_MPC718, ++ .name = "Yuan MPC718 MiniPCI DVB-T/Analog", ++ .comment = "Experimenters needed for device to work well.\n" ++ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_GPIO_MUX, ++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | ++ CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, ++ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, ++ }, ++ .tuners = { ++ /* XC3028 tuner */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ /* FIXME - the FM radio is just a guess and driver doesn't use SIF */ ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, ++ .ddr = { ++ /* Hynix HY5DU283222B DDR RAM */ ++ .chip_config = 0x303, ++ .refresh = 0x3bd, ++ .timing1 = 0x36320966, ++ .timing2 = 0x1f, ++ .tune_lane = 0, ++ .initial_emrs = 2, ++ }, ++ .gpio_init.initial_value = 0x1, ++ .gpio_init.direction = 0x3, ++ /* FIXME - these GPIO's are just guesses */ ++ .gpio_audio_input = { .mask = 0x3, ++ .tuner = 0x1, ++ .linein = 0x3, ++ .radio = 0x1 }, ++ .xceive_pin = 0, ++ .pci_list = cx18_pci_mpc718, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* GoTView PCI */ ++ ++static const struct cx18_card_pci_info cx18_pci_gotview_dvd3[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_GOTVIEW, 0x3343 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_gotview_dvd3 = { ++ .type = CX18_CARD_GOTVIEW_PCI_DVD3, ++ .name = "GoTView PCI DVD3 Hybrid", ++ .comment = "Experimenters needed for device to work well.\n" ++ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_GPIO_MUX, ++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | ++ CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, ++ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, ++ }, ++ .tuners = { ++ /* XC3028 tuner */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ /* FIXME - the FM radio is just a guess and driver doesn't use SIF */ ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, ++ .ddr = { ++ /* Hynix HY5DU283222B DDR RAM */ ++ .chip_config = 0x303, ++ .refresh = 0x3bd, ++ .timing1 = 0x36320966, ++ .timing2 = 0x1f, ++ .tune_lane = 0, ++ .initial_emrs = 2, ++ }, ++ .gpio_init.initial_value = 0x1, ++ .gpio_init.direction = 0x3, ++ ++ .gpio_audio_input = { .mask = 0x3, ++ .tuner = 0x1, ++ .linein = 0x2, ++ .radio = 0x1 }, ++ .xceive_pin = 0, ++ .pci_list = cx18_pci_gotview_dvd3, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Conexant Raptor PAL/SECAM: note that this card is analog only! */ ++ ++static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_cnxt_raptor_pal = { ++ .type = CX18_CARD_CNXT_RAPTOR_PAL, ++ .name = "Conexant Raptor PAL/SECAM", ++ .comment = "Analog TV capture supported\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_GPIO_MUX, ++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, ++ { CX18_CARD_INPUT_SVIDEO2, 2, ++ CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, ++ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, ++ }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 }, ++ .ddr = { ++ /* MT 46V16M16 memory */ ++ .chip_config = 0x50306, ++ .refresh = 0x753, ++ .timing1 = 0x33220953, ++ .timing2 = 0x09, ++ .tune_lane = 0, ++ .initial_emrs = 0, ++ }, ++ .gpio_init.initial_value = 0x1002, ++ .gpio_init.direction = 0xf002, ++ .gpio_audio_input = { .mask = 0xf002, ++ .tuner = 0x1002, /* LED D1 Tuner AF */ ++ .linein = 0x2000, /* LED D2 Line In 1 */ ++ .radio = 0x4002 }, /* LED D3 Tuner AF */ ++ .pci_list = cx18_pci_cnxt_raptor_pal, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Toshiba Qosmio laptop internal DVB-T/Analog Hybrid Tuner */ ++ ++static const struct cx18_card_pci_info cx18_pci_toshiba_qosmio_dvbt[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_TOSHIBA, 0x0110 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { ++ .type = CX18_CARD_TOSHIBA_QOSMIO_DVBT, ++ .name = "Toshiba Qosmio DVB-T/Analog", ++ .comment = "Experimenters and photos needed for device to work well.\n" ++ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ }, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .ddr = { ++ .chip_config = 0x202, ++ .refresh = 0x3bb, ++ .timing1 = 0x33320a63, ++ .timing2 = 0x0a, ++ .tune_lane = 0, ++ .initial_emrs = 0x42, ++ }, ++ .xceive_pin = 15, ++ .pci_list = cx18_pci_toshiba_qosmio_dvbt, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Leadtek WinFast PVR2100 */ ++ ++static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_leadtek_pvr2100 = { ++ .type = CX18_CARD_LEADTEK_PVR2100, ++ .name = "Leadtek WinFast PVR2100", ++ .comment = "Experimenters and photos needed for device to work well.\n" ++ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_GPIO_MUX, ++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | ++ CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, ++ { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ }, ++ .tuners = { ++ /* XC2028 tuner */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, ++ .ddr = { ++ /* Pointer to proper DDR config values provided by Terry Wu */ ++ .chip_config = 0x303, ++ .refresh = 0x3bb, ++ .timing1 = 0x24220e83, ++ .timing2 = 0x1f, ++ .tune_lane = 0, ++ .initial_emrs = 0x2, ++ }, ++ .gpio_init.initial_value = 0x6, ++ .gpio_init.direction = 0x7, ++ .gpio_audio_input = { .mask = 0x7, ++ .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, ++ .xceive_pin = 1, ++ .pci_list = cx18_pci_leadtek_pvr2100, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Leadtek WinFast DVR3100 H */ ++ ++static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = { ++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ ++ { 0, 0, 0 } ++}; ++ ++static const struct cx18_card cx18_card_leadtek_dvr3100h = { ++ .type = CX18_CARD_LEADTEK_DVR3100H, ++ .name = "Leadtek WinFast DVR3100 H", ++ .comment = "Simultaneous DVB-T and Analog capture supported,\n" ++ "\texcept when capturing Analog from the antenna input.\n", ++ .v4l2_capabilities = CX18_CAP_ENCODER, ++ .hw_audio_ctrl = CX18_HW_418_AV, ++ .hw_muxer = CX18_HW_GPIO_MUX, ++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | ++ CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, ++ .video_inputs = { ++ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, ++ { CX18_CARD_INPUT_SVIDEO1, 1, ++ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, ++ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, ++ { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, ++ }, ++ .audio_inputs = { ++ { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, ++ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, ++ }, ++ .tuners = { ++ /* XC3028 tuner */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, ++ .ddr = { ++ /* Pointer to proper DDR config values provided by Terry Wu */ ++ .chip_config = 0x303, ++ .refresh = 0x3bb, ++ .timing1 = 0x24220e83, ++ .timing2 = 0x1f, ++ .tune_lane = 0, ++ .initial_emrs = 0x2, ++ }, ++ .gpio_init.initial_value = 0x6, ++ .gpio_init.direction = 0x7, ++ .gpio_audio_input = { .mask = 0x7, ++ .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, ++ .xceive_pin = 1, ++ .pci_list = cx18_pci_leadtek_dvr3100h, ++ .i2c = &cx18_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++static const struct cx18_card *cx18_card_list[] = { ++ &cx18_card_hvr1600_esmt, ++ &cx18_card_hvr1600_samsung, ++ &cx18_card_h900, ++ &cx18_card_mpc718, ++ &cx18_card_cnxt_raptor_pal, ++ &cx18_card_toshiba_qosmio_dvbt, ++ &cx18_card_leadtek_pvr2100, ++ &cx18_card_leadtek_dvr3100h, ++ &cx18_card_gotview_dvd3, ++ &cx18_card_hvr1600_s5h1411 ++}; ++ ++const struct cx18_card *cx18_get_card(u16 index) ++{ ++ if (index >= ARRAY_SIZE(cx18_card_list)) ++ return NULL; ++ return cx18_card_list[index]; ++} ++ ++int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input) ++{ ++ const struct cx18_card_video_input *card_input = ++ cx->card->video_inputs + index; ++ static const char * const input_strs[] = { ++ "Tuner 1", ++ "S-Video 1", ++ "S-Video 2", ++ "Composite 1", ++ "Composite 2", ++ "Component 1" ++ }; ++ ++ if (index >= cx->nof_inputs) ++ return -EINVAL; ++ input->index = index; ++ strlcpy(input->name, input_strs[card_input->video_type - 1], ++ sizeof(input->name)); ++ input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ? ++ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); ++ input->audioset = (1 << cx->nof_audio_inputs) - 1; ++ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? ++ cx->tuner_std : V4L2_STD_ALL; ++ return 0; ++} ++ ++int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio) ++{ ++ const struct cx18_card_audio_input *aud_input = ++ cx->card->audio_inputs + index; ++ static const char * const input_strs[] = { ++ "Tuner 1", ++ "Line In 1", ++ "Line In 2" ++ }; ++ ++ memset(audio, 0, sizeof(*audio)); ++ if (index >= cx->nof_audio_inputs) ++ return -EINVAL; ++ strlcpy(audio->name, input_strs[aud_input->audio_type - 1], ++ sizeof(audio->name)); ++ audio->index = index; ++ audio->capability = V4L2_AUDCAP_STEREO; ++ return 0; ++} +diff --git a/drivers/media/pci/cx18/cx18-cards.h b/drivers/media/pci/cx18/cx18-cards.h +new file mode 100644 +index 0000000..add7391 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-cards.h +@@ -0,0 +1,157 @@ ++/* ++ * cx18 functions to query card hardware ++ * ++ * Derived from ivtv-cards.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* hardware flags */ ++#define CX18_HW_TUNER (1 << 0) ++#define CX18_HW_TVEEPROM (1 << 1) ++#define CX18_HW_CS5345 (1 << 2) ++#define CX18_HW_DVB (1 << 3) ++#define CX18_HW_418_AV (1 << 4) ++#define CX18_HW_GPIO_MUX (1 << 5) ++#define CX18_HW_GPIO_RESET_CTRL (1 << 6) ++#define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7) ++#define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8) ++#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ ++ CX18_HW_Z8F0811_IR_TX_HAUP) ++ ++#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \ ++ CX18_HW_Z8F0811_IR_TX_HAUP) ++ ++/* video inputs */ ++#define CX18_CARD_INPUT_VID_TUNER 1 ++#define CX18_CARD_INPUT_SVIDEO1 2 ++#define CX18_CARD_INPUT_SVIDEO2 3 ++#define CX18_CARD_INPUT_COMPOSITE1 4 ++#define CX18_CARD_INPUT_COMPOSITE2 5 ++#define CX18_CARD_INPUT_COMPONENT1 6 ++ ++/* audio inputs */ ++#define CX18_CARD_INPUT_AUD_TUNER 1 ++#define CX18_CARD_INPUT_LINE_IN1 2 ++#define CX18_CARD_INPUT_LINE_IN2 3 ++ ++#define CX18_CARD_MAX_VIDEO_INPUTS 6 ++#define CX18_CARD_MAX_AUDIO_INPUTS 3 ++#define CX18_CARD_MAX_TUNERS 2 ++ ++/* V4L2 capability aliases */ ++#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ ++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ ++ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) ++ ++struct cx18_card_video_input { ++ u8 video_type; /* video input type */ ++ u8 audio_index; /* index in cx18_card_audio_input array */ ++ u32 video_input; /* hardware video input */ ++}; ++ ++struct cx18_card_audio_input { ++ u8 audio_type; /* audio input type */ ++ u32 audio_input; /* hardware audio input */ ++ u16 muxer_input; /* hardware muxer input for boards with a ++ multiplexer chip */ ++}; ++ ++struct cx18_card_pci_info { ++ u16 device; ++ u16 subsystem_vendor; ++ u16 subsystem_device; ++}; ++ ++/* GPIO definitions */ ++ ++/* The mask is the set of bits used by the operation */ ++ ++struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ ++ u32 direction; /* DIR setting. Leave to 0 if no init is needed */ ++ u32 initial_value; ++}; ++ ++struct cx18_gpio_i2c_slave_reset { ++ u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */ ++ u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ ++ int msecs_asserted; /* time period reset must remain asserted */ ++ int msecs_recovery; /* time after deassert for chips to be ready */ ++ u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR contoller */ ++}; ++ ++struct cx18_gpio_audio_input { /* select tuner/line in input */ ++ u32 mask; /* leave to 0 if not supported */ ++ u32 tuner; ++ u32 linein; ++ u32 radio; ++}; ++ ++struct cx18_card_tuner { ++ v4l2_std_id std; /* standard for which the tuner is suitable */ ++ int tuner; /* tuner ID (from tuner.h) */ ++}; ++ ++struct cx18_card_tuner_i2c { ++ unsigned short radio[2];/* radio tuner i2c address to probe */ ++ unsigned short demod[3];/* demodulator i2c address to probe */ ++ unsigned short tv[4]; /* tv tuner i2c addresses to probe */ ++}; ++ ++struct cx18_ddr { /* DDR config data */ ++ u32 chip_config; ++ u32 refresh; ++ u32 timing1; ++ u32 timing2; ++ u32 tune_lane; ++ u32 initial_emrs; ++}; ++ ++/* for card information/parameters */ ++struct cx18_card { ++ int type; ++ char *name; ++ char *comment; ++ u32 v4l2_capabilities; ++ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only ++ 1 dev allowed currently) */ ++ u32 hw_muxer; /* hardware used to multiplex audio input */ ++ u32 hw_all; /* all hardware used by the board */ ++ struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS]; ++ struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS]; ++ struct cx18_card_audio_input radio_input; ++ ++ /* GPIO card-specific settings */ ++ u8 xceive_pin; /* XCeive tuner GPIO reset pin */ ++ struct cx18_gpio_init gpio_init; ++ struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; ++ struct cx18_gpio_audio_input gpio_audio_input; ++ ++ struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; ++ struct cx18_card_tuner_i2c *i2c; ++ ++ struct cx18_ddr ddr; ++ ++ /* list of device and subsystem vendor/devices that ++ correspond to this card type. */ ++ const struct cx18_card_pci_info *pci_list; ++}; ++ ++int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input); ++int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input); ++const struct cx18_card *cx18_get_card(u16 index); +diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c +new file mode 100644 +index 0000000..282a3d2 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-controls.c +@@ -0,0 +1,131 @@ ++/* ++ * cx18 ioctl control functions ++ * ++ * Derived from ivtv-controls.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++#include ++#include ++ ++#include "cx18-driver.h" ++#include "cx18-cards.h" ++#include "cx18-ioctl.h" ++#include "cx18-audio.h" ++#include "cx18-mailbox.h" ++#include "cx18-controls.h" ++ ++static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) ++{ ++ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); ++ int type = cxhdl->stream_type->val; ++ ++ if (atomic_read(&cx->ana_capturing) > 0) ++ return -EBUSY; ++ ++ if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || ++ !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS || ++ type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD || ++ type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) { ++ /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */ ++ cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; ++ CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " ++ "the MPEG stream\n"); ++ return 0; ++ } ++ ++ /* Allocate sliced VBI buffers if needed. */ ++ if (cx->vbi.sliced_mpeg_data[0] == NULL) { ++ int i; ++ ++ for (i = 0; i < CX18_VBI_FRAMES; i++) { ++ cx->vbi.sliced_mpeg_data[i] = ++ kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL); ++ if (cx->vbi.sliced_mpeg_data[i] == NULL) { ++ while (--i >= 0) { ++ kfree(cx->vbi.sliced_mpeg_data[i]); ++ cx->vbi.sliced_mpeg_data[i] = NULL; ++ } ++ cx->vbi.insert_mpeg = ++ V4L2_MPEG_STREAM_VBI_FMT_NONE; ++ CX18_WARN("Unable to allocate buffers for " ++ "sliced VBI data insertion\n"); ++ return -ENOMEM; ++ } ++ } ++ } ++ ++ cx->vbi.insert_mpeg = fmt; ++ CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS," ++ "when sliced VBI is enabled\n"); ++ ++ /* ++ * If our current settings have no lines set for capture, store a valid, ++ * default set of service lines to capture, in our current settings. ++ */ ++ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) { ++ if (cx->is_60hz) ++ cx->vbi.sliced_in->service_set = ++ V4L2_SLICED_CAPTION_525; ++ else ++ cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; ++ cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz); ++ } ++ return 0; ++} ++ ++static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) ++{ ++ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); ++ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; ++ struct v4l2_mbus_framefmt fmt; ++ ++ /* fix videodecoder resolution */ ++ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); ++ fmt.height = cxhdl->height; ++ fmt.code = V4L2_MBUS_FMT_FIXED; ++ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt); ++ return 0; ++} ++ ++static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) ++{ ++ static const u32 freqs[3] = { 44100, 48000, 32000 }; ++ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); ++ ++ /* The audio clock of the digitizer must match the codec sample ++ rate otherwise you get some very strange effects. */ ++ if (idx < ARRAY_SIZE(freqs)) ++ cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); ++ return 0; ++} ++ ++static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) ++{ ++ struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); ++ ++ cx->dualwatch_stereo_mode = val; ++ return 0; ++} ++ ++struct cx2341x_handler_ops cx18_cxhdl_ops = { ++ .s_audio_mode = cx18_s_audio_mode, ++ .s_audio_sampling_freq = cx18_s_audio_sampling_freq, ++ .s_video_encoding = cx18_s_video_encoding, ++ .s_stream_vbi_fmt = cx18_s_stream_vbi_fmt, ++}; +diff --git a/drivers/media/pci/cx18/cx18-controls.h b/drivers/media/pci/cx18/cx18-controls.h +new file mode 100644 +index 0000000..cb5dfc7 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-controls.h +@@ -0,0 +1,24 @@ ++/* ++ * cx18 ioctl control functions ++ * ++ * Derived from ivtv-controls.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++extern struct cx2341x_handler_ops cx18_cxhdl_ops; +diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c +new file mode 100644 +index 0000000..039133d +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-driver.c +@@ -0,0 +1,1360 @@ ++/* ++ * cx18 driver initialization and card probing ++ * ++ * Derived from ivtv-driver.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-version.h" ++#include "cx18-cards.h" ++#include "cx18-i2c.h" ++#include "cx18-irq.h" ++#include "cx18-gpio.h" ++#include "cx18-firmware.h" ++#include "cx18-queue.h" ++#include "cx18-streams.h" ++#include "cx18-av-core.h" ++#include "cx18-scb.h" ++#include "cx18-mailbox.h" ++#include "cx18-ioctl.h" ++#include "cx18-controls.h" ++#include "tuner-xc2028.h" ++#include ++#include ++ ++/* If you have already X v4l cards, then set this to X. This way ++ the device numbers stay matched. Example: you have a WinTV card ++ without radio and a Compro H900 with. Normally this would give a ++ video1 device together with a radio0 device for the Compro. By ++ setting this to 1 you ensure that radio0 is now also radio1. */ ++int cx18_first_minor; ++ ++/* Callback for registering extensions */ ++int (*cx18_ext_init)(struct cx18 *); ++EXPORT_SYMBOL(cx18_ext_init); ++ ++/* add your revision and whatnot here */ ++static struct pci_device_id cx18_pci_tbl[] __devinitdata = { ++ {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); ++ ++static atomic_t cx18_instance = ATOMIC_INIT(0); ++ ++/* Parameter declarations */ ++static int cardtype[CX18_MAX_CARDS]; ++static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static unsigned cardtype_c = 1; ++static unsigned tuner_c = 1; ++static unsigned radio_c = 1; ++static char pal[] = "--"; ++static char secam[] = "--"; ++static char ntsc[] = "-"; ++ ++/* Buffers */ ++static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; ++static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; ++static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS; ++static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; ++static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; ++static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; ++ ++static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; ++static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; ++static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; ++static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; ++static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; ++ ++static int enc_ts_bufs = -1; ++static int enc_mpg_bufs = -1; ++static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM; ++static int enc_yuv_bufs = -1; ++static int enc_vbi_bufs = -1; ++static int enc_pcm_bufs = -1; ++ ++ ++static int cx18_pci_latency = 1; ++ ++static int mmio_ndelay; ++static int retry_mmio = 1; ++ ++int cx18_debug; ++ ++module_param_array(tuner, int, &tuner_c, 0644); ++module_param_array(radio, int, &radio_c, 0644); ++module_param_array(cardtype, int, &cardtype_c, 0644); ++module_param_string(pal, pal, sizeof(pal), 0644); ++module_param_string(secam, secam, sizeof(secam), 0644); ++module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); ++module_param_named(debug, cx18_debug, int, 0644); ++module_param(mmio_ndelay, int, 0644); ++module_param(retry_mmio, int, 0644); ++module_param(cx18_pci_latency, int, 0644); ++module_param(cx18_first_minor, int, 0644); ++ ++module_param(enc_ts_buffers, int, 0644); ++module_param(enc_mpg_buffers, int, 0644); ++module_param(enc_idx_buffers, int, 0644); ++module_param(enc_yuv_buffers, int, 0644); ++module_param(enc_vbi_buffers, int, 0644); ++module_param(enc_pcm_buffers, int, 0644); ++ ++module_param(enc_ts_bufsize, int, 0644); ++module_param(enc_mpg_bufsize, int, 0644); ++module_param(enc_idx_bufsize, int, 0644); ++module_param(enc_yuv_bufsize, int, 0644); ++module_param(enc_pcm_bufsize, int, 0644); ++ ++module_param(enc_ts_bufs, int, 0644); ++module_param(enc_mpg_bufs, int, 0644); ++module_param(enc_idx_bufs, int, 0644); ++module_param(enc_yuv_bufs, int, 0644); ++module_param(enc_vbi_bufs, int, 0644); ++module_param(enc_pcm_bufs, int, 0644); ++ ++MODULE_PARM_DESC(tuner, "Tuner type selection,\n" ++ "\t\t\tsee tuner.h for values"); ++MODULE_PARM_DESC(radio, ++ "Enable or disable the radio. Use only if autodetection\n" ++ "\t\t\tfails. 0 = disable, 1 = enable"); ++MODULE_PARM_DESC(cardtype, ++ "Only use this option if your card is not detected properly.\n" ++ "\t\tSpecify card type:\n" ++ "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n" ++ "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" ++ "\t\t\t 3 = Compro VideoMate H900\n" ++ "\t\t\t 4 = Yuan MPC718\n" ++ "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" ++ "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" ++ "\t\t\t 7 = Leadtek WinFast PVR2100\n" ++ "\t\t\t 8 = Leadtek WinFast DVR3100 H\n" ++ "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n" ++ "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n" ++ "\t\t\t 0 = Autodetect (default)\n" ++ "\t\t\t-1 = Ignore this card\n\t\t"); ++MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); ++MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); ++MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); ++MODULE_PARM_DESC(debug, ++ "Debug level (bitmask). Default: 0\n" ++ "\t\t\t 1/0x0001: warning\n" ++ "\t\t\t 2/0x0002: info\n" ++ "\t\t\t 4/0x0004: mailbox\n" ++ "\t\t\t 8/0x0008: dma\n" ++ "\t\t\t 16/0x0010: ioctl\n" ++ "\t\t\t 32/0x0020: file\n" ++ "\t\t\t 64/0x0040: i2c\n" ++ "\t\t\t128/0x0080: irq\n" ++ "\t\t\t256/0x0100: high volume\n"); ++MODULE_PARM_DESC(cx18_pci_latency, ++ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" ++ "\t\t\tDefault: Yes"); ++MODULE_PARM_DESC(retry_mmio, ++ "(Deprecated) MMIO writes are now always checked and retried\n" ++ "\t\t\tEffectively: 1 [Yes]"); ++MODULE_PARM_DESC(mmio_ndelay, ++ "(Deprecated) MMIO accesses are now never purposely delayed\n" ++ "\t\t\tEffectively: 0 ns"); ++MODULE_PARM_DESC(enc_ts_buffers, ++ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); ++MODULE_PARM_DESC(enc_ts_bufsize, ++ "Size of an encoder TS buffer (kB)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE)); ++MODULE_PARM_DESC(enc_ts_bufs, ++ "Number of encoder TS buffers\n" ++ "\t\t\tDefault is computed from other enc_ts_* parameters"); ++MODULE_PARM_DESC(enc_mpg_buffers, ++ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); ++MODULE_PARM_DESC(enc_mpg_bufsize, ++ "Size of an encoder MPG buffer (kB)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE)); ++MODULE_PARM_DESC(enc_mpg_bufs, ++ "Number of encoder MPG buffers\n" ++ "\t\t\tDefault is computed from other enc_mpg_* parameters"); ++MODULE_PARM_DESC(enc_idx_buffers, ++ "(Deprecated) Encoder IDX buffer memory (MB)\n" ++ "\t\t\tIgnored, except 0 disables IDX buffer allocations\n" ++ "\t\t\tDefault: 1 [Enabled]"); ++MODULE_PARM_DESC(enc_idx_bufsize, ++ "Size of an encoder IDX buffer (kB)\n" ++ "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n" ++ "\t\t\t(multiples of size required for 64 index entries)\n" ++ "\t\t\tDefault: 2"); ++MODULE_PARM_DESC(enc_idx_bufs, ++ "Number of encoder IDX buffers\n" ++ "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM)); ++MODULE_PARM_DESC(enc_yuv_buffers, ++ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); ++MODULE_PARM_DESC(enc_yuv_bufsize, ++ "Size of an encoder YUV buffer (kB)\n" ++ "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n" ++ "\t\t\t(multiples of size required for 32 screen lines)\n" ++ "\t\t\tDefault: 102"); ++MODULE_PARM_DESC(enc_yuv_bufs, ++ "Number of encoder YUV buffers\n" ++ "\t\t\tDefault is computed from other enc_yuv_* parameters"); ++MODULE_PARM_DESC(enc_vbi_buffers, ++ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); ++MODULE_PARM_DESC(enc_vbi_bufs, ++ "Number of encoder VBI buffers\n" ++ "\t\t\tDefault is computed from enc_vbi_buffers"); ++MODULE_PARM_DESC(enc_pcm_buffers, ++ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); ++MODULE_PARM_DESC(enc_pcm_bufsize, ++ "Size of an encoder PCM buffer (kB)\n" ++ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE)); ++MODULE_PARM_DESC(enc_pcm_bufs, ++ "Number of encoder PCM buffers\n" ++ "\t\t\tDefault is computed from other enc_pcm_* parameters"); ++ ++MODULE_PARM_DESC(cx18_first_minor, ++ "Set device node number assigned to first card"); ++ ++MODULE_AUTHOR("Hans Verkuil"); ++MODULE_DESCRIPTION("CX23418 driver"); ++MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_VERSION(CX18_VERSION); ++ ++#if defined(CONFIG_MODULES) && defined(MODULE) ++static void request_module_async(struct work_struct *work) ++{ ++ struct cx18 *dev = container_of(work, struct cx18, request_module_wk); ++ ++ /* Make sure cx18-alsa module is loaded */ ++ request_module("cx18-alsa"); ++ ++ /* Initialize cx18-alsa for this instance of the cx18 device */ ++ if (cx18_ext_init != NULL) ++ cx18_ext_init(dev); ++} ++ ++static void request_modules(struct cx18 *dev) ++{ ++ INIT_WORK(&dev->request_module_wk, request_module_async); ++ schedule_work(&dev->request_module_wk); ++} ++ ++static void flush_request_modules(struct cx18 *dev) ++{ ++ flush_work(&dev->request_module_wk); ++} ++#else ++#define request_modules(dev) ++#define flush_request_modules(dev) ++#endif /* CONFIG_MODULES */ ++ ++/* Generic utility functions */ ++int cx18_msleep_timeout(unsigned int msecs, int intr) ++{ ++ long int timeout = msecs_to_jiffies(msecs); ++ int sig; ++ ++ do { ++ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); ++ timeout = schedule_timeout(timeout); ++ sig = intr ? signal_pending(current) : 0; ++ } while (!sig && timeout); ++ return sig; ++} ++ ++/* Release ioremapped memory */ ++static void cx18_iounmap(struct cx18 *cx) ++{ ++ if (cx == NULL) ++ return; ++ ++ /* Release io memory */ ++ if (cx->enc_mem != NULL) { ++ CX18_DEBUG_INFO("releasing enc_mem\n"); ++ iounmap(cx->enc_mem); ++ cx->enc_mem = NULL; ++ } ++} ++ ++static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len) ++{ ++ int i; ++ ++ CX18_INFO("eeprom dump:\n"); ++ for (i = 0; i < len; i++) { ++ if (0 == (i % 16)) ++ CX18_INFO("eeprom %02x:", i); ++ printk(KERN_CONT " %02x", eedata[i]); ++ if (15 == (i % 16)) ++ printk(KERN_CONT "\n"); ++ } ++} ++ ++/* Hauppauge card? get values from tveeprom */ ++void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) ++{ ++ struct i2c_client c; ++ u8 eedata[256]; ++ ++ memset(&c, 0, sizeof(c)); ++ strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name)); ++ c.adapter = &cx->i2c_adap[0]; ++ c.addr = 0xA0 >> 1; ++ ++ memset(tv, 0, sizeof(*tv)); ++ if (tveeprom_read(&c, eedata, sizeof(eedata))) ++ return; ++ ++ switch (cx->card->type) { ++ case CX18_CARD_HVR_1600_ESMT: ++ case CX18_CARD_HVR_1600_SAMSUNG: ++ case CX18_CARD_HVR_1600_S5H1411: ++ tveeprom_hauppauge_analog(&c, tv, eedata); ++ break; ++ case CX18_CARD_YUAN_MPC718: ++ case CX18_CARD_GOTVIEW_PCI_DVD3: ++ tv->model = 0x718; ++ cx18_eeprom_dump(cx, eedata, sizeof(eedata)); ++ CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n", ++ eedata[2], eedata[1], eedata[4], eedata[3]); ++ break; ++ default: ++ tv->model = 0xffffffff; ++ cx18_eeprom_dump(cx, eedata, sizeof(eedata)); ++ break; ++ } ++} ++ ++static void cx18_process_eeprom(struct cx18 *cx) ++{ ++ struct tveeprom tv; ++ ++ cx18_read_eeprom(cx, &tv); ++ ++ /* Many thanks to Steven Toth from Hauppauge for providing the ++ model numbers */ ++ /* Note: the Samsung memory models cannot be reliably determined ++ from the model number. Use the cardtype module option if you ++ have one of these preproduction models. */ ++ switch (tv.model) { ++ case 74301: /* Retail models */ ++ case 74321: ++ case 74351: /* OEM models */ ++ case 74361: ++ /* Digital side is s5h1411/tda18271 */ ++ cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411); ++ break; ++ case 74021: /* Retail models */ ++ case 74031: ++ case 74041: ++ case 74141: ++ case 74541: /* OEM models */ ++ case 74551: ++ case 74591: ++ case 74651: ++ case 74691: ++ case 74751: ++ case 74891: ++ /* Digital side is s5h1409/mxl5005s */ ++ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); ++ break; ++ case 0x718: ++ return; ++ case 0xffffffff: ++ CX18_INFO("Unknown EEPROM encoding\n"); ++ return; ++ case 0: ++ CX18_ERR("Invalid EEPROM\n"); ++ return; ++ default: ++ CX18_ERR("Unknown model %d, defaulting to original HVR-1600 " ++ "(cardtype=1)\n", tv.model); ++ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); ++ break; ++ } ++ ++ cx->v4l2_cap = cx->card->v4l2_capabilities; ++ cx->card_name = cx->card->name; ++ cx->card_i2c = cx->card->i2c; ++ ++ CX18_INFO("Autodetected %s\n", cx->card_name); ++ ++ if (tv.tuner_type == TUNER_ABSENT) ++ CX18_ERR("tveeprom cannot autodetect tuner!\n"); ++ ++ if (cx->options.tuner == -1) ++ cx->options.tuner = tv.tuner_type; ++ if (cx->options.radio == -1) ++ cx->options.radio = (tv.has_radio != 0); ++ ++ if (cx->std != 0) ++ /* user specified tuner standard */ ++ return; ++ ++ /* autodetect tuner standard */ ++#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \ ++ V4L2_STD_MN | \ ++ V4L2_STD_PAL_I | \ ++ V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \ ++ V4L2_STD_DK) ++ if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL) ++ == TVEEPROM_TUNER_FORMAT_ALL) { ++ CX18_DEBUG_INFO("Worldwide tuner detected\n"); ++ cx->std = V4L2_STD_ALL; ++ } else if (tv.tuner_formats & V4L2_STD_PAL) { ++ CX18_DEBUG_INFO("PAL tuner detected\n"); ++ cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; ++ } else if (tv.tuner_formats & V4L2_STD_NTSC) { ++ CX18_DEBUG_INFO("NTSC tuner detected\n"); ++ cx->std |= V4L2_STD_NTSC_M; ++ } else if (tv.tuner_formats & V4L2_STD_SECAM) { ++ CX18_DEBUG_INFO("SECAM tuner detected\n"); ++ cx->std |= V4L2_STD_SECAM_L; ++ } else { ++ CX18_INFO("No tuner detected, default to NTSC-M\n"); ++ cx->std |= V4L2_STD_NTSC_M; ++ } ++} ++ ++static v4l2_std_id cx18_parse_std(struct cx18 *cx) ++{ ++ switch (pal[0]) { ++ case '6': ++ return V4L2_STD_PAL_60; ++ case 'b': ++ case 'B': ++ case 'g': ++ case 'G': ++ return V4L2_STD_PAL_BG; ++ case 'h': ++ case 'H': ++ return V4L2_STD_PAL_H; ++ case 'n': ++ case 'N': ++ if (pal[1] == 'c' || pal[1] == 'C') ++ return V4L2_STD_PAL_Nc; ++ return V4L2_STD_PAL_N; ++ case 'i': ++ case 'I': ++ return V4L2_STD_PAL_I; ++ case 'd': ++ case 'D': ++ case 'k': ++ case 'K': ++ return V4L2_STD_PAL_DK; ++ case 'M': ++ case 'm': ++ return V4L2_STD_PAL_M; ++ case '-': ++ break; ++ default: ++ CX18_WARN("pal= argument not recognised\n"); ++ return 0; ++ } ++ ++ switch (secam[0]) { ++ case 'b': ++ case 'B': ++ case 'g': ++ case 'G': ++ case 'h': ++ case 'H': ++ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; ++ case 'd': ++ case 'D': ++ case 'k': ++ case 'K': ++ return V4L2_STD_SECAM_DK; ++ case 'l': ++ case 'L': ++ if (secam[1] == 'C' || secam[1] == 'c') ++ return V4L2_STD_SECAM_LC; ++ return V4L2_STD_SECAM_L; ++ case '-': ++ break; ++ default: ++ CX18_WARN("secam= argument not recognised\n"); ++ return 0; ++ } ++ ++ switch (ntsc[0]) { ++ case 'm': ++ case 'M': ++ return V4L2_STD_NTSC_M; ++ case 'j': ++ case 'J': ++ return V4L2_STD_NTSC_M_JP; ++ case 'k': ++ case 'K': ++ return V4L2_STD_NTSC_M_KR; ++ case '-': ++ break; ++ default: ++ CX18_WARN("ntsc= argument not recognised\n"); ++ return 0; ++ } ++ ++ /* no match found */ ++ return 0; ++} ++ ++static void cx18_process_options(struct cx18 *cx) ++{ ++ int i, j; ++ ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; ++ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */ ++ ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs; ++ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */ ++ ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; ++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ ++ ++ /* Ensure stream_buffers & stream_buf_size are valid */ ++ for (i = 0; i < CX18_MAX_STREAMS; i++) { ++ if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */ ++ cx->options.megabytes[i] <= 0 || /* User said 0 MB total */ ++ cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */ ++ cx->options.megabytes[i] = 0; ++ cx->stream_buffers[i] = 0; ++ cx->stream_buf_size[i] = 0; ++ continue; ++ } ++ /* ++ * YUV is a special case where the stream_buf_size needs to be ++ * an integral multiple of 33.75 kB (storage for 32 screens ++ * lines to maintain alignment in case of lost buffers). ++ * ++ * IDX is a special case where the stream_buf_size should be ++ * an integral multiple of 1.5 kB (storage for 64 index entries ++ * to maintain alignment in case of lost buffers). ++ * ++ */ ++ if (i == CX18_ENC_STREAM_TYPE_YUV) { ++ cx->stream_buf_size[i] *= 1024; ++ cx->stream_buf_size[i] -= ++ (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE); ++ ++ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) ++ cx->stream_buf_size[i] = ++ CX18_UNIT_ENC_YUV_BUFSIZE; ++ } else if (i == CX18_ENC_STREAM_TYPE_IDX) { ++ cx->stream_buf_size[i] *= 1024; ++ cx->stream_buf_size[i] -= ++ (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE); ++ ++ if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE) ++ cx->stream_buf_size[i] = ++ CX18_UNIT_ENC_IDX_BUFSIZE; ++ } ++ /* ++ * YUV and IDX are special cases where the stream_buf_size is ++ * now in bytes. ++ * VBI is a special case where the stream_buf_size is fixed ++ * and already in bytes ++ */ ++ if (i == CX18_ENC_STREAM_TYPE_VBI || ++ i == CX18_ENC_STREAM_TYPE_YUV || ++ i == CX18_ENC_STREAM_TYPE_IDX) { ++ if (cx->stream_buffers[i] < 0) { ++ cx->stream_buffers[i] = ++ cx->options.megabytes[i] * 1024 * 1024 ++ / cx->stream_buf_size[i]; ++ } else { ++ /* N.B. This might round down to 0 */ ++ cx->options.megabytes[i] = ++ cx->stream_buffers[i] ++ * cx->stream_buf_size[i]/(1024 * 1024); ++ } ++ } else { ++ /* All other streams have stream_buf_size in kB here */ ++ if (cx->stream_buffers[i] < 0) { ++ cx->stream_buffers[i] = ++ cx->options.megabytes[i] * 1024 ++ / cx->stream_buf_size[i]; ++ } else { ++ /* N.B. This might round down to 0 */ ++ cx->options.megabytes[i] = ++ cx->stream_buffers[i] ++ * cx->stream_buf_size[i] / 1024; ++ } ++ /* convert from kB to bytes */ ++ cx->stream_buf_size[i] *= 1024; ++ } ++ CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, " ++ "%d bytes\n", i, cx->options.megabytes[i], ++ cx->stream_buffers[i], cx->stream_buf_size[i]); ++ } ++ ++ cx->options.cardtype = cardtype[cx->instance]; ++ cx->options.tuner = tuner[cx->instance]; ++ cx->options.radio = radio[cx->instance]; ++ ++ cx->std = cx18_parse_std(cx); ++ if (cx->options.cardtype == -1) { ++ CX18_INFO("Ignore card\n"); ++ return; ++ } ++ cx->card = cx18_get_card(cx->options.cardtype - 1); ++ if (cx->card) ++ CX18_INFO("User specified %s card\n", cx->card->name); ++ else if (cx->options.cardtype != 0) ++ CX18_ERR("Unknown user specified type, trying to autodetect card\n"); ++ if (cx->card == NULL) { ++ if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { ++ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); ++ CX18_INFO("Autodetected Hauppauge card\n"); ++ } ++ } ++ if (cx->card == NULL) { ++ for (i = 0; (cx->card = cx18_get_card(i)); i++) { ++ if (cx->card->pci_list == NULL) ++ continue; ++ for (j = 0; cx->card->pci_list[j].device; j++) { ++ if (cx->pci_dev->device != ++ cx->card->pci_list[j].device) ++ continue; ++ if (cx->pci_dev->subsystem_vendor != ++ cx->card->pci_list[j].subsystem_vendor) ++ continue; ++ if (cx->pci_dev->subsystem_device != ++ cx->card->pci_list[j].subsystem_device) ++ continue; ++ CX18_INFO("Autodetected %s card\n", cx->card->name); ++ goto done; ++ } ++ } ++ } ++done: ++ ++ if (cx->card == NULL) { ++ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); ++ CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", ++ cx->pci_dev->vendor, cx->pci_dev->device); ++ CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", ++ cx->pci_dev->subsystem_vendor, ++ cx->pci_dev->subsystem_device); ++ CX18_ERR("Defaulting to %s card\n", cx->card->name); ++ CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); ++ CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); ++ CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n"); ++ } ++ cx->v4l2_cap = cx->card->v4l2_capabilities; ++ cx->card_name = cx->card->name; ++ cx->card_i2c = cx->card->i2c; ++} ++ ++static int __devinit cx18_create_in_workq(struct cx18 *cx) ++{ ++ snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", ++ cx->v4l2_dev.name); ++ cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0); ++ if (cx->in_work_queue == NULL) { ++ CX18_ERR("Unable to create incoming mailbox handler thread\n"); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static void __devinit cx18_init_in_work_orders(struct cx18 *cx) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { ++ cx->in_work_order[i].cx = cx; ++ cx->in_work_order[i].str = cx->epu_debug_str; ++ INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); ++ } ++} ++ ++/* Precondition: the cx18 structure has been memset to 0. Only ++ the dev and instance fields have been filled in. ++ No assumptions on the card type may be made here (see cx18_init_struct2 ++ for that). ++ */ ++static int __devinit cx18_init_struct1(struct cx18 *cx) ++{ ++ int ret; ++ ++ cx->base_addr = pci_resource_start(cx->pci_dev, 0); ++ ++ mutex_init(&cx->serialize_lock); ++ mutex_init(&cx->gpio_lock); ++ mutex_init(&cx->epu2apu_mb_lock); ++ mutex_init(&cx->epu2cpu_mb_lock); ++ ++ ret = cx18_create_in_workq(cx); ++ if (ret) ++ return ret; ++ ++ cx18_init_in_work_orders(cx); ++ ++ /* start counting open_id at 1 */ ++ cx->open_id = 1; ++ ++ /* Initial settings */ ++ cx->cxhdl.port = CX2341X_PORT_MEMORY; ++ cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; ++ cx->cxhdl.ops = &cx18_cxhdl_ops; ++ cx->cxhdl.func = cx18_api_func; ++ cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; ++ ret = cx2341x_handler_init(&cx->cxhdl, 50); ++ if (ret) ++ return ret; ++ cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl; ++ ++ cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val; ++ cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val; ++ cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val | ++ (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) | ++ (cx->cxhdl.video_median_filter_type->cur.val << 2); ++ ++ init_waitqueue_head(&cx->cap_w); ++ init_waitqueue_head(&cx->mb_apu_waitq); ++ init_waitqueue_head(&cx->mb_cpu_waitq); ++ init_waitqueue_head(&cx->dma_waitq); ++ ++ /* VBI */ ++ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; ++ ++ /* IVTV style VBI insertion into MPEG streams */ ++ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list); ++ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list); ++ INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list); ++ list_add(&cx->vbi.sliced_mpeg_buf.list, ++ &cx->vbi.sliced_mpeg_mdl.buf_list); ++ return 0; ++} ++ ++/* Second initialization part. Here the card type has been ++ autodetected. */ ++static void __devinit cx18_init_struct2(struct cx18 *cx) ++{ ++ int i; ++ ++ for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) ++ if (cx->card->video_inputs[i].video_type == 0) ++ break; ++ cx->nof_inputs = i; ++ for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) ++ if (cx->card->audio_inputs[i].audio_type == 0) ++ break; ++ cx->nof_audio_inputs = i; ++ ++ /* Find tuner input */ ++ for (i = 0; i < cx->nof_inputs; i++) { ++ if (cx->card->video_inputs[i].video_type == ++ CX18_CARD_INPUT_VID_TUNER) ++ break; ++ } ++ if (i == cx->nof_inputs) ++ i = 0; ++ cx->active_input = i; ++ cx->audio_input = cx->card->video_inputs[i].audio_index; ++} ++ ++static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ u16 cmd; ++ unsigned char pci_latency; ++ ++ CX18_DEBUG_INFO("Enabling pci device\n"); ++ ++ if (pci_enable_device(pci_dev)) { ++ CX18_ERR("Can't enable device %d!\n", cx->instance); ++ return -EIO; ++ } ++ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) { ++ CX18_ERR("No suitable DMA available, card %d\n", cx->instance); ++ return -EIO; ++ } ++ if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { ++ CX18_ERR("Cannot request encoder memory region, card %d\n", ++ cx->instance); ++ return -EIO; ++ } ++ ++ /* Enable bus mastering and memory mapped IO for the CX23418 */ ++ pci_read_config_word(pci_dev, PCI_COMMAND, &cmd); ++ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; ++ pci_write_config_word(pci_dev, PCI_COMMAND, cmd); ++ ++ cx->card_rev = pci_dev->revision; ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); ++ ++ if (pci_latency < 64 && cx18_pci_latency) { ++ CX18_INFO("Unreasonably low latency timer, " ++ "setting to 64 (was %d)\n", pci_latency); ++ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64); ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); ++ } ++ ++ CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " ++ "irq: %d, latency: %d, memory: 0x%llx\n", ++ cx->pci_dev->device, cx->card_rev, pci_dev->bus->number, ++ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), ++ cx->pci_dev->irq, pci_latency, (u64)cx->base_addr); ++ ++ return 0; ++} ++ ++static void cx18_init_subdevs(struct cx18 *cx) ++{ ++ u32 hw = cx->card->hw_all; ++ u32 device; ++ int i; ++ ++ for (i = 0, device = 1; i < 32; i++, device <<= 1) { ++ ++ if (!(device & hw)) ++ continue; ++ ++ switch (device) { ++ case CX18_HW_DVB: ++ case CX18_HW_TVEEPROM: ++ /* These subordinate devices do not use probing */ ++ cx->hw_flags |= device; ++ break; ++ case CX18_HW_418_AV: ++ /* The A/V decoder gets probed earlier to set PLLs */ ++ /* Just note that the card uses it (i.e. has analog) */ ++ cx->hw_flags |= device; ++ break; ++ case CX18_HW_GPIO_RESET_CTRL: ++ /* ++ * The Reset Controller gets probed and added to ++ * hw_flags earlier for i2c adapter/bus initialization ++ */ ++ break; ++ case CX18_HW_GPIO_MUX: ++ if (cx18_gpio_register(cx, device) == 0) ++ cx->hw_flags |= device; ++ break; ++ default: ++ if (cx18_i2c_register(cx, i) == 0) ++ cx->hw_flags |= device; ++ break; ++ } ++ } ++ ++ if (cx->hw_flags & CX18_HW_418_AV) ++ cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV); ++ ++ if (cx->card->hw_muxer != 0) ++ cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer); ++} ++ ++static int __devinit cx18_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ int retval = 0; ++ int i; ++ u32 devtype; ++ struct cx18 *cx; ++ ++ /* FIXME - module parameter arrays constrain max instances */ ++ i = atomic_inc_return(&cx18_instance) - 1; ++ if (i >= CX18_MAX_CARDS) { ++ printk(KERN_ERR "cx18: cannot manage card %d, driver has a " ++ "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1); ++ return -ENOMEM; ++ } ++ ++ cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); ++ if (cx == NULL) { ++ printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n", ++ i); ++ return -ENOMEM; ++ } ++ cx->pci_dev = pci_dev; ++ cx->instance = i; ++ ++ retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev); ++ if (retval) { ++ printk(KERN_ERR "cx18: v4l2_device_register of card %d failed" ++ "\n", cx->instance); ++ kfree(cx); ++ return retval; ++ } ++ snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d", ++ cx->instance); ++ CX18_INFO("Initializing card %d\n", cx->instance); ++ ++ cx18_process_options(cx); ++ if (cx->options.cardtype == -1) { ++ retval = -ENODEV; ++ goto err; ++ } ++ ++ retval = cx18_init_struct1(cx); ++ if (retval) ++ goto err; ++ ++ CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr); ++ ++ /* PCI Device Setup */ ++ retval = cx18_setup_pci(cx, pci_dev, pci_id); ++ if (retval != 0) ++ goto free_workqueues; ++ ++ /* map io memory */ ++ CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", ++ (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); ++ cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, ++ CX18_MEM_SIZE); ++ if (!cx->enc_mem) { ++ CX18_ERR("ioremap failed. Can't get a window into CX23418 " ++ "memory and register space\n"); ++ CX18_ERR("Each capture card with a CX23418 needs 64 MB of " ++ "vmalloc address space for the window\n"); ++ CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); ++ CX18_ERR("Use the vmalloc= kernel command line option to set " ++ "VmallocTotal to a larger value\n"); ++ retval = -ENOMEM; ++ goto free_mem; ++ } ++ cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; ++ devtype = cx18_read_reg(cx, 0xC72028); ++ switch (devtype & 0xff000000) { ++ case 0xff000000: ++ CX18_INFO("cx23418 revision %08x (A)\n", devtype); ++ break; ++ case 0x01000000: ++ CX18_INFO("cx23418 revision %08x (B)\n", devtype); ++ break; ++ default: ++ CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); ++ break; ++ } ++ ++ cx18_init_power(cx, 1); ++ cx18_init_memory(cx); ++ ++ cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); ++ cx18_init_scb(cx); ++ ++ cx18_gpio_init(cx); ++ ++ /* Initialize integrated A/V decoder early to set PLLs, just in case */ ++ retval = cx18_av_probe(cx); ++ if (retval) { ++ CX18_ERR("Could not register A/V decoder subdevice\n"); ++ goto free_map; ++ } ++ ++ /* Initialize GPIO Reset Controller to do chip resets during i2c init */ ++ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { ++ if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0) ++ CX18_WARN("Could not register GPIO reset controller" ++ "subdevice; proceeding anyway.\n"); ++ else ++ cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL; ++ } ++ ++ /* active i2c */ ++ CX18_DEBUG_INFO("activating i2c...\n"); ++ retval = init_cx18_i2c(cx); ++ if (retval) { ++ CX18_ERR("Could not initialize i2c\n"); ++ goto free_map; ++ } ++ ++ if (cx->card->hw_all & CX18_HW_TVEEPROM) { ++ /* Based on the model number the cardtype may be changed. ++ The PCI IDs are not always reliable. */ ++ const struct cx18_card *orig_card = cx->card; ++ cx18_process_eeprom(cx); ++ ++ if (cx->card != orig_card) { ++ /* Changed the cardtype; re-reset the I2C chips */ ++ cx18_gpio_init(cx); ++ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, ++ core, reset, (u32) CX18_GPIO_RESET_I2C); ++ } ++ } ++ if (cx->card->comment) ++ CX18_INFO("%s", cx->card->comment); ++ if (cx->card->v4l2_capabilities == 0) { ++ retval = -ENODEV; ++ goto free_i2c; ++ } ++ cx18_init_memory(cx); ++ cx18_init_scb(cx); ++ ++ /* Register IRQ */ ++ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler, ++ IRQF_SHARED | IRQF_DISABLED, ++ cx->v4l2_dev.name, (void *)cx); ++ if (retval) { ++ CX18_ERR("Failed to register irq %d\n", retval); ++ goto free_i2c; ++ } ++ ++ if (cx->std == 0) ++ cx->std = V4L2_STD_NTSC_M; ++ ++ if (cx->options.tuner == -1) { ++ for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { ++ if ((cx->std & cx->card->tuners[i].std) == 0) ++ continue; ++ cx->options.tuner = cx->card->tuners[i].tuner; ++ break; ++ } ++ } ++ /* if no tuner was found, then pick the first tuner in the card list */ ++ if (cx->options.tuner == -1 && cx->card->tuners[0].std) { ++ cx->std = cx->card->tuners[0].std; ++ if (cx->std & V4L2_STD_PAL) ++ cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; ++ else if (cx->std & V4L2_STD_NTSC) ++ cx->std = V4L2_STD_NTSC_M; ++ else if (cx->std & V4L2_STD_SECAM) ++ cx->std = V4L2_STD_SECAM_L; ++ cx->options.tuner = cx->card->tuners[0].tuner; ++ } ++ if (cx->options.radio == -1) ++ cx->options.radio = (cx->card->radio_input.audio_type != 0); ++ ++ /* The card is now fully identified, continue with card-specific ++ initialization. */ ++ cx18_init_struct2(cx); ++ ++ cx18_init_subdevs(cx); ++ ++ if (cx->std & V4L2_STD_525_60) ++ cx->is_60hz = 1; ++ else ++ cx->is_50hz = 1; ++ ++ cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz); ++ ++ if (cx->options.radio > 0) ++ cx->v4l2_cap |= V4L2_CAP_RADIO; ++ ++ if (cx->options.tuner > -1) { ++ struct tuner_setup setup; ++ ++ setup.addr = ADDR_UNSET; ++ setup.type = cx->options.tuner; ++ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ ++ if (cx->options.radio > 0) ++ setup.mode_mask |= T_RADIO; ++ setup.tuner_callback = (setup.type == TUNER_XC2028) ? ++ cx18_reset_tuner_gpio : NULL; ++ cx18_call_all(cx, tuner, s_type_addr, &setup); ++ if (setup.type == TUNER_XC2028) { ++ static struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ }; ++ struct v4l2_priv_tun_config cfg = { ++ .tuner = cx->options.tuner, ++ .priv = &ctrl, ++ }; ++ cx18_call_all(cx, tuner, s_config, &cfg); ++ } ++ } ++ ++ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) ++ are not. */ ++ cx->tuner_std = cx->std; ++ if (cx->std == V4L2_STD_ALL) ++ cx->std = V4L2_STD_NTSC_M; ++ ++ retval = cx18_streams_setup(cx); ++ if (retval) { ++ CX18_ERR("Error %d setting up streams\n", retval); ++ goto free_irq; ++ } ++ retval = cx18_streams_register(cx); ++ if (retval) { ++ CX18_ERR("Error %d registering devices\n", retval); ++ goto free_streams; ++ } ++ ++ CX18_INFO("Initialized card: %s\n", cx->card_name); ++ ++ /* Load cx18 submodules (cx18-alsa) */ ++ request_modules(cx); ++ return 0; ++ ++free_streams: ++ cx18_streams_cleanup(cx, 1); ++free_irq: ++ free_irq(cx->pci_dev->irq, (void *)cx); ++free_i2c: ++ exit_cx18_i2c(cx); ++free_map: ++ cx18_iounmap(cx); ++free_mem: ++ release_mem_region(cx->base_addr, CX18_MEM_SIZE); ++free_workqueues: ++ destroy_workqueue(cx->in_work_queue); ++err: ++ if (retval == 0) ++ retval = -ENODEV; ++ CX18_ERR("Error %d on initialization\n", retval); ++ ++ v4l2_device_unregister(&cx->v4l2_dev); ++ kfree(cx); ++ return retval; ++} ++ ++int cx18_init_on_first_open(struct cx18 *cx) ++{ ++ int video_input; ++ int fw_retry_count = 3; ++ struct v4l2_frequency vf; ++ struct cx18_open_id fh; ++ v4l2_std_id std; ++ ++ fh.cx = cx; ++ ++ if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) ++ return -ENXIO; ++ ++ if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) ++ return 0; ++ ++ while (--fw_retry_count > 0) { ++ /* load firmware */ ++ if (cx18_firmware_init(cx) == 0) ++ break; ++ if (fw_retry_count > 1) ++ CX18_WARN("Retry loading firmware\n"); ++ } ++ ++ if (fw_retry_count == 0) { ++ set_bit(CX18_F_I_FAILED, &cx->i_flags); ++ return -ENXIO; ++ } ++ set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); ++ ++ /* ++ * Init the firmware twice to work around a silicon bug ++ * with the digital TS. ++ * ++ * The second firmware load requires us to normalize the APU state, ++ * or the audio for the first analog capture will be badly incorrect. ++ * ++ * I can't seem to call APU_RESETAI and have it succeed without the ++ * APU capturing audio, so we start and stop it here to do the reset ++ */ ++ ++ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ ++ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); ++ cx18_vapi(cx, CX18_APU_RESETAI, 0); ++ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); ++ ++ fw_retry_count = 3; ++ while (--fw_retry_count > 0) { ++ /* load firmware */ ++ if (cx18_firmware_init(cx) == 0) ++ break; ++ if (fw_retry_count > 1) ++ CX18_WARN("Retry loading firmware\n"); ++ } ++ ++ if (fw_retry_count == 0) { ++ set_bit(CX18_F_I_FAILED, &cx->i_flags); ++ return -ENXIO; ++ } ++ ++ /* ++ * The second firmware load requires us to normalize the APU state, ++ * or the audio for the first analog capture will be badly incorrect. ++ * ++ * I can't seem to call APU_RESETAI and have it succeed without the ++ * APU capturing audio, so we start and stop it here to do the reset ++ */ ++ ++ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ ++ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); ++ cx18_vapi(cx, CX18_APU_RESETAI, 0); ++ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); ++ ++ /* Init the A/V decoder, if it hasn't been already */ ++ v4l2_subdev_call(cx->sd_av, core, load_fw); ++ ++ vf.tuner = 0; ++ vf.type = V4L2_TUNER_ANALOG_TV; ++ vf.frequency = 6400; /* the tuner 'baseline' frequency */ ++ ++ /* Set initial frequency. For PAL/SECAM broadcasts no ++ 'default' channel exists AFAIK. */ ++ if (cx->std == V4L2_STD_NTSC_M_JP) ++ vf.frequency = 1460; /* ch. 1 91250*16/1000 */ ++ else if (cx->std & V4L2_STD_NTSC_M) ++ vf.frequency = 1076; /* ch. 4 67250*16/1000 */ ++ ++ video_input = cx->active_input; ++ cx->active_input++; /* Force update of input */ ++ cx18_s_input(NULL, &fh, video_input); ++ ++ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code ++ in one place. */ ++ cx->std++; /* Force full standard initialization */ ++ std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std; ++ cx18_s_std(NULL, &fh, &std); ++ cx18_s_frequency(NULL, &fh, &vf); ++ return 0; ++} ++ ++static void cx18_cancel_in_work_orders(struct cx18 *cx) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) ++ cancel_work_sync(&cx->in_work_order[i].work); ++} ++ ++static void cx18_cancel_out_work_orders(struct cx18 *cx) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_STREAMS; i++) ++ if (&cx->streams[i].video_dev != NULL) ++ cancel_work_sync(&cx->streams[i].out_work_order); ++} ++ ++static void cx18_remove(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct cx18 *cx = to_cx18(v4l2_dev); ++ int i; ++ ++ CX18_DEBUG_INFO("Removing Card\n"); ++ ++ flush_request_modules(cx); ++ ++ /* Stop all captures */ ++ CX18_DEBUG_INFO("Stopping all streams\n"); ++ if (atomic_read(&cx->tot_capturing) > 0) ++ cx18_stop_all_captures(cx); ++ ++ /* Stop interrupts that cause incoming work to be queued */ ++ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); ++ ++ /* Incoming work can cause outgoing work, so clean up incoming first */ ++ cx18_cancel_in_work_orders(cx); ++ cx18_cancel_out_work_orders(cx); ++ ++ /* Stop ack interrupts that may have been needed for work to finish */ ++ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); ++ ++ cx18_halt_firmware(cx); ++ ++ destroy_workqueue(cx->in_work_queue); ++ ++ cx18_streams_cleanup(cx, 1); ++ ++ exit_cx18_i2c(cx); ++ ++ free_irq(cx->pci_dev->irq, (void *)cx); ++ ++ cx18_iounmap(cx); ++ ++ release_mem_region(cx->base_addr, CX18_MEM_SIZE); ++ ++ pci_disable_device(cx->pci_dev); ++ ++ if (cx->vbi.sliced_mpeg_data[0] != NULL) ++ for (i = 0; i < CX18_VBI_FRAMES; i++) ++ kfree(cx->vbi.sliced_mpeg_data[i]); ++ ++ v4l2_ctrl_handler_free(&cx->av_state.hdl); ++ ++ CX18_INFO("Removed %s\n", cx->card_name); ++ ++ v4l2_device_unregister(v4l2_dev); ++ kfree(cx); ++} ++ ++ ++/* define a pci_driver for card detection */ ++static struct pci_driver cx18_pci_driver = { ++ .name = "cx18", ++ .id_table = cx18_pci_tbl, ++ .probe = cx18_probe, ++ .remove = cx18_remove, ++}; ++ ++static int __init module_start(void) ++{ ++ printk(KERN_INFO "cx18: Start initialization, version %s\n", ++ CX18_VERSION); ++ ++ /* Validate parameters */ ++ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { ++ printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n", ++ CX18_MAX_CARDS - 1); ++ return -1; ++ } ++ ++ if (cx18_debug < 0 || cx18_debug > 511) { ++ cx18_debug = 0; ++ printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); ++ } ++ ++ if (pci_register_driver(&cx18_pci_driver)) { ++ printk(KERN_ERR "cx18: Error detecting PCI card\n"); ++ return -ENODEV; ++ } ++ printk(KERN_INFO "cx18: End initialization\n"); ++ return 0; ++} ++ ++static void __exit module_cleanup(void) ++{ ++ pci_unregister_driver(&cx18_pci_driver); ++} ++ ++module_init(module_start); ++module_exit(module_cleanup); ++MODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE); +diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h +new file mode 100644 +index 0000000..2767c64 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-driver.h +@@ -0,0 +1,730 @@ ++/* ++ * cx18 driver internal defines and structures ++ * ++ * Derived from ivtv-driver.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef CX18_DRIVER_H ++#define CX18_DRIVER_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cx18-mailbox.h" ++#include "cx18-av-core.h" ++#include "cx23418.h" ++ ++/* DVB */ ++#include "demux.h" ++#include "dmxdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++#include "dvbdev.h" ++ ++/* Videobuf / YUV support */ ++#include ++#include ++ ++#ifndef CONFIG_PCI ++# error "This driver requires kernel PCI support." ++#endif ++ ++#define CX18_MEM_OFFSET 0x00000000 ++#define CX18_MEM_SIZE 0x04000000 ++#define CX18_REG_OFFSET 0x02000000 ++ ++/* Maximum cx18 driver instances. */ ++#define CX18_MAX_CARDS 32 ++ ++/* Supported cards */ ++#define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */ ++#define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ ++#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ ++#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ ++#define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ ++#define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ ++#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */ ++#define CX18_CARD_LEADTEK_DVR3100H 7 /* Leadtek WinFast DVR3100 H */ ++#define CX18_CARD_GOTVIEW_PCI_DVD3 8 /* GoTView PCI DVD3 Hybrid */ ++#define CX18_CARD_HVR_1600_S5H1411 9 /* Hauppauge HVR 1600 s5h1411/tda18271*/ ++#define CX18_CARD_LAST 9 ++ ++#define CX18_ENC_STREAM_TYPE_MPG 0 ++#define CX18_ENC_STREAM_TYPE_TS 1 ++#define CX18_ENC_STREAM_TYPE_YUV 2 ++#define CX18_ENC_STREAM_TYPE_VBI 3 ++#define CX18_ENC_STREAM_TYPE_PCM 4 ++#define CX18_ENC_STREAM_TYPE_IDX 5 ++#define CX18_ENC_STREAM_TYPE_RAD 6 ++#define CX18_MAX_STREAMS 7 ++ ++/* system vendor and device IDs */ ++#define PCI_VENDOR_ID_CX 0x14f1 ++#define PCI_DEVICE_ID_CX23418 0x5b7a ++ ++/* subsystem vendor ID */ ++#define CX18_PCI_ID_HAUPPAUGE 0x0070 ++#define CX18_PCI_ID_COMPRO 0x185b ++#define CX18_PCI_ID_YUAN 0x12ab ++#define CX18_PCI_ID_CONEXANT 0x14f1 ++#define CX18_PCI_ID_TOSHIBA 0x1179 ++#define CX18_PCI_ID_LEADTEK 0x107D ++#define CX18_PCI_ID_GOTVIEW 0x5854 ++ ++/* ======================================================================== */ ++/* ========================== START USER SETTABLE DMA VARIABLES =========== */ ++/* ======================================================================== */ ++ ++/* DMA Buffers, Default size in MB allocated */ ++#define CX18_DEFAULT_ENC_TS_BUFFERS 1 ++#define CX18_DEFAULT_ENC_MPG_BUFFERS 2 ++#define CX18_DEFAULT_ENC_IDX_BUFFERS 1 ++#define CX18_DEFAULT_ENC_YUV_BUFFERS 2 ++#define CX18_DEFAULT_ENC_VBI_BUFFERS 1 ++#define CX18_DEFAULT_ENC_PCM_BUFFERS 1 ++ ++/* Maximum firmware DMA buffers per stream */ ++#define CX18_MAX_FW_MDLS_PER_STREAM 63 ++ ++/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */ ++#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */ ++#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) ++#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) ++ ++/* IDX buffer size should be a multiple of the index entry size from the chip */ ++struct cx18_enc_idx_entry { ++ __le32 length; ++ __le32 offset_low; ++ __le32 offset_high; ++ __le32 flags; ++ __le32 pts_low; ++ __le32 pts_high; ++} __attribute__ ((packed)); ++#define CX18_UNIT_ENC_IDX_BUFSIZE \ ++ (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES) ++ ++/* DMA buffer, default size in kB allocated */ ++#define CX18_DEFAULT_ENC_TS_BUFSIZE 32 ++#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 ++#define CX18_DEFAULT_ENC_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1) ++#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) ++#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 ++ ++/* i2c stuff */ ++#define I2C_CLIENTS_MAX 16 ++ ++/* debugging */ ++ ++/* Flag to turn on high volume debugging */ ++#define CX18_DBGFLG_WARN (1 << 0) ++#define CX18_DBGFLG_INFO (1 << 1) ++#define CX18_DBGFLG_API (1 << 2) ++#define CX18_DBGFLG_DMA (1 << 3) ++#define CX18_DBGFLG_IOCTL (1 << 4) ++#define CX18_DBGFLG_FILE (1 << 5) ++#define CX18_DBGFLG_I2C (1 << 6) ++#define CX18_DBGFLG_IRQ (1 << 7) ++/* Flag to turn on high volume debugging */ ++#define CX18_DBGFLG_HIGHVOL (1 << 8) ++ ++/* NOTE: extra space before comma in 'fmt , ## args' is required for ++ gcc-2.95, otherwise it won't compile. */ ++#define CX18_DEBUG(x, type, fmt, args...) \ ++ do { \ ++ if ((x) & cx18_debug) \ ++ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ ++ } while (0) ++#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) ++#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) ++#define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args) ++#define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args) ++#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) ++#define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args) ++#define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args) ++#define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args) ++ ++#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ ++ do { \ ++ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ ++ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ ++ } while (0) ++#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) ++#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) ++#define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args) ++#define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args) ++#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) ++#define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args) ++#define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args) ++#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) ++ ++/* Standard kernel messages */ ++#define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args) ++#define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args) ++#define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args) ++ ++/* Messages for internal subdevs to use */ ++#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \ ++ do { \ ++ if ((x) & cx18_debug) \ ++ v4l2_info(dev, " " type ": " fmt , ## args); \ ++ } while (0) ++#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) ++#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) ++#define CX18_DEBUG_API_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) ++#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) ++#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) ++#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) ++#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) ++#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) ++ ++#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \ ++ do { \ ++ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ ++ v4l2_info(dev, " " type ": " fmt , ## args); \ ++ } while (0) ++#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) ++#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) ++#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) ++#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) ++#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) ++#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) ++#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) ++#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \ ++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) ++ ++#define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args) ++#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args) ++#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args) ++ ++extern int cx18_debug; ++ ++struct cx18_options { ++ int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ ++ int cardtype; /* force card type on load */ ++ int tuner; /* set tuner on load */ ++ int radio; /* enable/disable radio */ ++}; ++ ++/* per-mdl bit flags */ ++#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ ++ ++/* per-stream, s_flags */ ++#define CX18_F_S_CLAIMED 3 /* this stream is claimed */ ++#define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ ++#define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ ++#define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ ++#define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ ++#define CX18_F_S_STOPPING 9 /* telling the fw to stop capturing */ ++ ++/* per-cx18, i_flags */ ++#define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ ++#define CX18_F_I_EOS 4 /* End of encoder stream */ ++#define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ ++#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ ++#define CX18_F_I_INITED 21 /* set after first open */ ++#define CX18_F_I_FAILED 22 /* set if first open failed */ ++ ++/* These are the VBI types as they appear in the embedded VBI private packets. */ ++#define CX18_SLICED_TYPE_TELETEXT_B (1) ++#define CX18_SLICED_TYPE_CAPTION_525 (4) ++#define CX18_SLICED_TYPE_WSS_625 (5) ++#define CX18_SLICED_TYPE_VPS (7) ++ ++/** ++ * list_entry_is_past_end - check if a previous loop cursor is off list end ++ * @pos: the type * previously used as a loop cursor. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ * ++ * Check if the entry's list_head is the head of the list, thus it's not a ++ * real entry but was the loop cursor that walked past the end ++ */ ++#define list_entry_is_past_end(pos, head, member) \ ++ (&pos->member == (head)) ++ ++struct cx18_buffer { ++ struct list_head list; ++ dma_addr_t dma_handle; ++ char *buf; ++ ++ u32 bytesused; ++ u32 readpos; ++}; ++ ++struct cx18_mdl { ++ struct list_head list; ++ u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */ ++ ++ unsigned int skipped; ++ unsigned long m_flags; ++ ++ struct list_head buf_list; ++ struct cx18_buffer *curr_buf; /* current buffer in list for reading */ ++ ++ u32 bytesused; ++ u32 readpos; ++}; ++ ++struct cx18_queue { ++ struct list_head list; ++ atomic_t depth; ++ u32 bytesused; ++ spinlock_t lock; ++}; ++ ++struct cx18_stream; /* forward reference */ ++ ++struct cx18_dvb { ++ struct cx18_stream *stream; ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ struct dmxdev dmxdev; ++ struct dvb_adapter dvb_adapter; ++ struct dvb_demux demux; ++ struct dvb_frontend *fe; ++ struct dvb_net dvbnet; ++ int enabled; ++ int feeding; ++ struct mutex feedlock; ++}; ++ ++struct cx18; /* forward reference */ ++struct cx18_scb; /* forward reference */ ++ ++ ++#define CX18_MAX_MDL_ACKS 2 ++#define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) ++/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ ++ ++#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 ++#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2 ++#define CX18_F_EWO_MB_STALE \ ++ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) ++ ++struct cx18_in_work_order { ++ struct work_struct work; ++ atomic_t pending; ++ struct cx18 *cx; ++ unsigned long flags; ++ int rpu; ++ struct cx18_mailbox mb; ++ struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS]; ++ char *str; ++}; ++ ++#define CX18_INVALID_TASK_HANDLE 0xffffffff ++ ++struct cx18_stream { ++ /* These first five fields are always set, even if the stream ++ is not actually created. */ ++ struct video_device *video_dev; /* NULL when stream not created */ ++ struct cx18_dvb *dvb; /* DVB / Digital Transport */ ++ struct cx18 *cx; /* for ease of use */ ++ const char *name; /* name of the stream */ ++ int type; /* stream type */ ++ u32 handle; /* task handle */ ++ unsigned int mdl_base_idx; ++ ++ u32 id; ++ unsigned long s_flags; /* status flags, see above */ ++ int dma; /* can be PCI_DMA_TODEVICE, ++ PCI_DMA_FROMDEVICE or ++ PCI_DMA_NONE */ ++ wait_queue_head_t waitq; ++ ++ /* Buffers */ ++ struct list_head buf_pool; /* buffers not attached to an MDL */ ++ u32 buffers; /* total buffers owned by this stream */ ++ u32 buf_size; /* size in bytes of a single buffer */ ++ ++ /* MDL sizes - all stream MDLs are the same size */ ++ u32 bufs_per_mdl; ++ u32 mdl_size; /* total bytes in all buffers in a mdl */ ++ ++ /* MDL Queues */ ++ struct cx18_queue q_free; /* free - in rotation, not committed */ ++ struct cx18_queue q_busy; /* busy - in use by firmware */ ++ struct cx18_queue q_full; /* full - data for user apps */ ++ struct cx18_queue q_idle; /* idle - not in rotation */ ++ ++ struct work_struct out_work_order; ++ ++ /* Videobuf for YUV video */ ++ u32 pixelformat; ++ u32 vb_bytes_per_frame; ++ struct list_head vb_capture; /* video capture queue */ ++ spinlock_t vb_lock; ++ struct timer_list vb_timeout; ++ ++ struct videobuf_queue vbuf_q; ++ spinlock_t vbuf_q_lock; /* Protect vbuf_q */ ++ enum v4l2_buf_type vb_type; ++}; ++ ++struct cx18_videobuf_buffer { ++ /* Common video buffer sub-system struct */ ++ struct videobuf_buffer vb; ++ v4l2_std_id tvnorm; /* selected tv norm */ ++ u32 bytes_used; ++}; ++ ++struct cx18_open_id { ++ struct v4l2_fh fh; ++ u32 open_id; ++ int type; ++ struct cx18 *cx; ++}; ++ ++static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) ++{ ++ return container_of(fh, struct cx18_open_id, fh); ++} ++ ++static inline struct cx18_open_id *file2id(struct file *file) ++{ ++ return fh2id(file->private_data); ++} ++ ++/* forward declaration of struct defined in cx18-cards.h */ ++struct cx18_card; ++ ++/* ++ * A note about "sliced" VBI data as implemented in this driver: ++ * ++ * Currently we collect the sliced VBI in the form of Ancillary Data ++ * packets, inserted by the AV core decoder/digitizer/slicer in the ++ * horizontal blanking region of the VBI lines, in "raw" mode as far as ++ * the Encoder is concerned. We don't ever tell the Encoder itself ++ * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode) ++ * ++ * We then process the ancillary data ourselves to send the sliced data ++ * to the user application directly or build up MPEG-2 private stream 1 ++ * packets to splice into (only!) MPEG-2 PS streams for the user app. ++ * ++ * (That's how ivtv essentially does it.) ++ * ++ * The Encoder should be able to extract certain sliced VBI data for ++ * us and provide it in a separate stream or splice it into any type of ++ * MPEG PS or TS stream, but this isn't implemented yet. ++ */ ++ ++/* ++ * Number of "raw" VBI samples per horizontal line we tell the Encoder to ++ * grab from the decoder/digitizer/slicer output for raw or sliced VBI. ++ * It depends on the pixel clock and the horiz rate: ++ * ++ * (1/Fh)*(2*Fp) = Samples/line ++ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples ++ * ++ * Sliced VBI data is sent as ancillary data during horizontal blanking ++ * Raw VBI is sent as active video samples during vertcal blanking ++ * ++ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line ++ * length of 720 pixels @ 4:2:2 sampling. Thus... ++ * ++ * For systems that use a 15.734 kHz horizontal rate, such as ++ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: ++ * ++ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = ++ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples ++ * ++ * For systems that use a 15.625 kHz horizontal rate, such as ++ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: ++ * ++ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = ++ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples ++ */ ++static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */ ++static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */ ++static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */ ++ ++#define CX18_VBI_FRAMES 32 ++ ++struct vbi_info { ++ /* Current state of v4l2 VBI settings for this device */ ++ struct v4l2_format in; ++ struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */ ++ u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */ ++ u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */ ++ ++ u32 frame; /* Count of VBI buffers/frames received from Encoder */ ++ ++ /* ++ * Vars for creation and insertion of MPEG Private Stream 1 packets ++ * of sliced VBI data into an MPEG PS ++ */ ++ ++ /* Boolean: create and insert Private Stream 1 packets into the PS */ ++ int insert_mpeg; ++ ++ /* ++ * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. ++ * Used in cx18-vbi.c only for collecting sliced data, and as a source ++ * during conversion of sliced VBI data into MPEG Priv Stream 1 packets. ++ * We don't need to save state here, but the array may have been a bit ++ * too big (2304 bytes) to alloc from the stack. ++ */ ++ struct v4l2_sliced_vbi_data sliced_data[36]; ++ ++ /* ++ * A ring buffer of driver-generated MPEG-2 PS ++ * Program Pack/Private Stream 1 packets for sliced VBI data insertion ++ * into the MPEG PS stream. ++ * ++ * In each sliced_mpeg_data[] buffer is: ++ * 16 byte MPEG-2 PS Program Pack Header ++ * 16 byte MPEG-2 Private Stream 1 PES Header ++ * 4 byte magic number: "itv0" or "ITV0" ++ * 4 byte first field line mask, if "itv0" ++ * 4 byte second field line mask, if "itv0" ++ * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data ++ * ++ * Each line in the payload is ++ * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.) ++ * 42 bytes of line data ++ * ++ * That's a maximum 1552 bytes of payload in the Private Stream 1 packet ++ * which is the payload size a PVR-350 (CX23415) MPEG decoder will ++ * accept for VBI data. So, including the headers, it's a maximum 1584 ++ * bytes total. ++ */ ++#define CX18_SLICED_MPEG_DATA_MAXSZ 1584 ++ /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */ ++#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8) ++ u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; ++ u32 sliced_mpeg_size[CX18_VBI_FRAMES]; ++ ++ /* Count of Program Pack/Program Stream 1 packets inserted into PS */ ++ u32 inserted_frame; ++ ++ /* ++ * A dummy driver stream transfer mdl & buffer with a copy of the next ++ * sliced_mpeg_data[] buffer for output to userland apps. ++ * Only used in cx18-fileops.c, but its state needs to persist at times. ++ */ ++ struct cx18_mdl sliced_mpeg_mdl; ++ struct cx18_buffer sliced_mpeg_buf; ++}; ++ ++/* Per cx23418, per I2C bus private algo callback data */ ++struct cx18_i2c_algo_callback_data { ++ struct cx18 *cx; ++ int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ ++}; ++ ++#define CX18_MAX_MMIO_WR_RETRIES 10 ++ ++/* Struct to hold info about cx18 cards */ ++struct cx18 { ++ int instance; ++ struct pci_dev *pci_dev; ++ struct v4l2_device v4l2_dev; ++ struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */ ++ struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */ ++ ++ const struct cx18_card *card; /* card information */ ++ const char *card_name; /* full name of the card */ ++ const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ ++ u8 is_50hz; ++ u8 is_60hz; ++ u8 nof_inputs; /* number of video inputs */ ++ u8 nof_audio_inputs; /* number of audio inputs */ ++ u32 v4l2_cap; /* V4L2 capabilities of card */ ++ u32 hw_flags; /* Hardware description of the board */ ++ unsigned int free_mdl_idx; ++ struct cx18_scb __iomem *scb; /* pointer to SCB */ ++ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ ++ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ ++ ++ struct cx18_av_state av_state; ++ ++ /* codec settings */ ++ struct cx2341x_handler cxhdl; ++ u32 filter_mode; ++ u32 temporal_strength; ++ u32 spatial_strength; ++ ++ /* dualwatch */ ++ unsigned long dualwatch_jiffies; ++ u32 dualwatch_stereo_mode; ++ ++ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ ++ struct cx18_options options; /* User options */ ++ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ ++ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ ++ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ ++ struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */ ++ void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data, ++ size_t num_bytes); ++ ++ unsigned long i_flags; /* global cx18 flags */ ++ atomic_t ana_capturing; /* count number of active analog capture streams */ ++ atomic_t tot_capturing; /* total count number of active capture streams */ ++ int search_pack_header; ++ ++ int open_id; /* incremented each time an open occurs, used as ++ unique ID. Starts at 1, so 0 can be used as ++ uninitialized value in the stream->id. */ ++ ++ resource_size_t base_addr; ++ ++ u8 card_rev; ++ void __iomem *enc_mem, *reg_mem; ++ ++ struct vbi_info vbi; ++ ++ u64 mpg_data_received; ++ u64 vbi_data_inserted; ++ ++ wait_queue_head_t mb_apu_waitq; ++ wait_queue_head_t mb_cpu_waitq; ++ wait_queue_head_t cap_w; ++ /* when the current DMA is finished this queue is woken up */ ++ wait_queue_head_t dma_waitq; ++ ++ u32 sw1_irq_mask; ++ u32 sw2_irq_mask; ++ u32 hw2_irq_mask; ++ ++ struct workqueue_struct *in_work_queue; ++ char in_workq_name[11]; /* "cx18-NN-in" */ ++ struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS]; ++ char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ ++ ++ /* i2c */ ++ struct i2c_adapter i2c_adap[2]; ++ struct i2c_algo_bit_data i2c_algo[2]; ++ struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; ++ ++ struct IR_i2c_init_data ir_i2c_init_data; ++ ++ /* gpio */ ++ u32 gpio_dir; ++ u32 gpio_val; ++ struct mutex gpio_lock; ++ struct v4l2_subdev sd_gpiomux; ++ struct v4l2_subdev sd_resetctrl; ++ ++ /* v4l2 and User settings */ ++ ++ /* codec settings */ ++ u32 audio_input; ++ u32 active_input; ++ v4l2_std_id std; ++ v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ ++ ++ /* Used for cx18-alsa module loading */ ++ struct work_struct request_module_wk; ++}; ++ ++static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct cx18, v4l2_dev); ++} ++ ++/* cx18 extensions to be loaded */ ++extern int (*cx18_ext_init)(struct cx18 *); ++ ++/* Globals */ ++extern int cx18_first_minor; ++ ++/*==============Prototypes==================*/ ++ ++/* Return non-zero if a signal is pending */ ++int cx18_msleep_timeout(unsigned int msecs, int intr); ++ ++/* Read Hauppauge eeprom */ ++struct tveeprom; /* forward reference */ ++void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); ++ ++/* First-open initialization: load firmware, etc. */ ++int cx18_init_on_first_open(struct cx18 *cx); ++ ++/* Test if the current VBI mode is raw (1) or sliced (0) */ ++static inline int cx18_raw_vbi(const struct cx18 *cx) ++{ ++ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; ++} ++ ++/* Call the specified callback for all subdevs with a grp_id bit matching the ++ * mask in hw (if 0, then match them all). Ignore any errors. */ ++#define cx18_call_hw(cx, hw, o, f, args...) \ ++ do { \ ++ struct v4l2_subdev *__sd; \ ++ __v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd, \ ++ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ ++ } while (0) ++ ++#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args) ++ ++/* Call the specified callback for all subdevs with a grp_id bit matching the ++ * mask in hw (if 0, then match them all). If the callback returns an error ++ * other than 0 or -ENOIOCTLCMD, then return with that error code. */ ++#define cx18_call_hw_err(cx, hw, o, f, args...) \ ++({ \ ++ struct v4l2_subdev *__sd; \ ++ __v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev, \ ++ __sd, !(hw) || (__sd->grp_id & (hw)), o, f, \ ++ ##args); \ ++}) ++ ++#define cx18_call_all_err(cx, o, f, args...) \ ++ cx18_call_hw_err(cx, 0, o, f , ##args) ++ ++#endif /* CX18_DRIVER_H */ +diff --git a/drivers/media/pci/cx18/cx18-dvb.c b/drivers/media/pci/cx18/cx18-dvb.c +new file mode 100644 +index 0000000..3eac59c +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-dvb.c +@@ -0,0 +1,609 @@ ++/* ++ * cx18 functions for DVB support ++ * ++ * Copyright (c) 2008 Steven Toth ++ * Copyright (C) 2008 Andy Walls ++ * ++ * 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 "cx18-version.h" ++#include "cx18-dvb.h" ++#include "cx18-io.h" ++#include "cx18-queue.h" ++#include "cx18-streams.h" ++#include "cx18-cards.h" ++#include "cx18-gpio.h" ++#include "s5h1409.h" ++#include "mxl5005s.h" ++#include "s5h1411.h" ++#include "tda18271.h" ++#include "zl10353.h" ++ ++#include ++#include "mt352.h" ++#include "mt352_priv.h" ++#include "tuner-xc2028.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define FWFILE "dvb-cx18-mpc718-mt352.fw" ++ ++#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 ++#define CX18_CLOCK_ENABLE2 0xc71024 ++#define CX18_DMUX_CLK_MASK 0x0080 ++ ++/* ++ * CX18_CARD_HVR_1600_ESMT ++ * CX18_CARD_HVR_1600_SAMSUNG ++ */ ++ ++static struct mxl5005s_config hauppauge_hvr1600_tuner = { ++ .i2c_address = 0xC6 >> 1, ++ .if_freq = IF_FREQ_5380000HZ, ++ .xtal_freq = CRYSTAL_FREQ_16000000HZ, ++ .agc_mode = MXL_SINGLE_AGC, ++ .tracking_filter = MXL_TF_C_H, ++ .rssi_enable = MXL_RSSI_ENABLE, ++ .cap_select = MXL_CAP_SEL_ENABLE, ++ .div_out = MXL_DIV_OUT_4, ++ .clock_out = MXL_CLOCK_OUT_DISABLE, ++ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, ++ .top = MXL5005S_TOP_25P2, ++ .mod_mode = MXL_DIGITAL_MODE, ++ .if_mode = MXL_ZERO_IF, ++ .qam_gain = 0x02, ++ .AgcMasterByte = 0x00, ++}; ++ ++static struct s5h1409_config hauppauge_hvr1600_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_ON, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++ .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE ++}; ++ ++/* ++ * CX18_CARD_HVR_1600_S5H1411 ++ */ ++static struct s5h1411_config hcw_s5h1411_config = { ++ .output_mode = S5H1411_SERIAL_OUTPUT, ++ .gpio = S5H1411_GPIO_OFF, ++ .vsb_if = S5H1411_IF_44000, ++ .qam_if = S5H1411_IF_4000, ++ .inversion = S5H1411_INVERSION_ON, ++ .status_mode = S5H1411_DEMODLOCKING, ++ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct tda18271_std_map hauppauge_tda18271_std_map = { ++ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++}; ++ ++static struct tda18271_config hauppauge_tda18271_config = { ++ .std_map = &hauppauge_tda18271_std_map, ++ .gate = TDA18271_GATE_DIGITAL, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++/* ++ * CX18_CARD_LEADTEK_DVR3100H ++ */ ++/* Information/confirmation of proper config values provided by Terry Wu */ ++static struct zl10353_config leadtek_dvr3100h_demod = { ++ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ ++ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ ++ .parallel_ts = 1, /* Not a serial TS */ ++ .no_tuner = 1, /* XC3028 is not behind the gate */ ++ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ ++}; ++ ++/* ++ * CX18_CARD_YUAN_MPC718 ++ */ ++/* ++ * Due to ++ * ++ * 1. an absence of information on how to prgram the MT352 ++ * 2. the Linux mt352 module pushing MT352 initialzation off onto us here ++ * ++ * We have to use an init sequence that *you* must extract from the Windows ++ * driver (yuanrap.sys) and which we load as a firmware. ++ * ++ * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual ++ * with chip programming details, then I can remove this annoyance. ++ */ ++static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream, ++ const struct firmware **fw) ++{ ++ struct cx18 *cx = stream->cx; ++ const char *fn = FWFILE; ++ int ret; ++ ++ ret = request_firmware(fw, fn, &cx->pci_dev->dev); ++ if (ret) ++ CX18_ERR("Unable to open firmware file %s\n", fn); ++ else { ++ size_t sz = (*fw)->size; ++ if (sz < 2 || sz > 64 || (sz % 2) != 0) { ++ CX18_ERR("Firmware %s has a bad size: %lu bytes\n", ++ fn, (unsigned long) sz); ++ ret = -EILSEQ; ++ release_firmware(*fw); ++ *fw = NULL; ++ } ++ } ++ ++ if (ret) { ++ CX18_ERR("The MPC718 board variant with the MT352 DVB-T" ++ "demodualtor will not work without it\n"); ++ CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware " ++ "mpc718' if you need the firmware\n"); ++ } ++ return ret; ++} ++ ++static int yuan_mpc718_mt352_init(struct dvb_frontend *fe) ++{ ++ struct cx18_dvb *dvb = container_of(fe->dvb, ++ struct cx18_dvb, dvb_adapter); ++ struct cx18_stream *stream = dvb->stream; ++ const struct firmware *fw = NULL; ++ int ret; ++ int i; ++ u8 buf[3]; ++ ++ ret = yuan_mpc718_mt352_reqfw(stream, &fw); ++ if (ret) ++ return ret; ++ ++ /* Loop through all the register-value pairs in the firmware file */ ++ for (i = 0; i < fw->size; i += 2) { ++ buf[0] = fw->data[i]; ++ /* Intercept a few registers we want to set ourselves */ ++ switch (buf[0]) { ++ case TRL_NOMINAL_RATE_0: ++ /* Set our custom OFDM bandwidth in the case below */ ++ break; ++ case TRL_NOMINAL_RATE_1: ++ /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */ ++ /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */ ++ /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */ ++ buf[1] = 0x72; ++ buf[2] = 0x49; ++ mt352_write(fe, buf, 3); ++ break; ++ case INPUT_FREQ_0: ++ /* Set our custom IF in the case below */ ++ break; ++ case INPUT_FREQ_1: ++ /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */ ++ buf[1] = 0x31; ++ buf[2] = 0xc0; ++ mt352_write(fe, buf, 3); ++ break; ++ default: ++ /* Pass through the register-value pair from the fw */ ++ buf[1] = fw->data[i+1]; ++ mt352_write(fe, buf, 2); ++ break; ++ } ++ } ++ ++ buf[0] = (u8) TUNER_GO; ++ buf[1] = 0x01; /* Go */ ++ mt352_write(fe, buf, 2); ++ release_firmware(fw); ++ return 0; ++} ++ ++static struct mt352_config yuan_mpc718_mt352_demod = { ++ .demod_address = 0x1e >> 1, ++ .adc_clock = 20480, /* 20.480 MHz */ ++ .if2 = 4560, /* 4.560 MHz */ ++ .no_tuner = 1, /* XC3028 is not behind the gate */ ++ .demod_init = yuan_mpc718_mt352_init, ++}; ++ ++static struct zl10353_config yuan_mpc718_zl10353_demod = { ++ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ ++ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ ++ .parallel_ts = 1, /* Not a serial TS */ ++ .no_tuner = 1, /* XC3028 is not behind the gate */ ++ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ ++}; ++ ++static struct zl10353_config gotview_dvd3_zl10353_demod = { ++ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ ++ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ ++ .parallel_ts = 1, /* Not a serial TS */ ++ .no_tuner = 1, /* XC3028 is not behind the gate */ ++ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ ++}; ++ ++static int dvb_register(struct cx18_stream *stream); ++ ++/* Kernel DVB framework calls this when the feed needs to start. ++ * The CX18 framework should enable the transport DMA handling ++ * and queue processing. ++ */ ++static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct cx18_stream *stream = (struct cx18_stream *) demux->priv; ++ struct cx18 *cx; ++ int ret; ++ u32 v; ++ ++ if (!stream) ++ return -EINVAL; ++ ++ cx = stream->cx; ++ CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", ++ feed->pid, feed->index); ++ ++ mutex_lock(&cx->serialize_lock); ++ ret = cx18_init_on_first_open(cx); ++ mutex_unlock(&cx->serialize_lock); ++ if (ret) { ++ CX18_ERR("Failed to initialize firmware starting DVB feed\n"); ++ return ret; ++ } ++ ret = -EINVAL; ++ ++ switch (cx->card->type) { ++ case CX18_CARD_HVR_1600_ESMT: ++ case CX18_CARD_HVR_1600_SAMSUNG: ++ case CX18_CARD_HVR_1600_S5H1411: ++ v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); ++ v |= 0x00400000; /* Serial Mode */ ++ v |= 0x00002000; /* Data Length - Byte */ ++ v |= 0x00010000; /* Error - Polarity */ ++ v |= 0x00020000; /* Error - Passthru */ ++ v |= 0x000c0000; /* Error - Ignore */ ++ cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); ++ break; ++ ++ case CX18_CARD_LEADTEK_DVR3100H: ++ case CX18_CARD_YUAN_MPC718: ++ case CX18_CARD_GOTVIEW_PCI_DVD3: ++ default: ++ /* Assumption - Parallel transport - Signalling ++ * undefined or default. ++ */ ++ break; ++ } ++ ++ if (!demux->dmx.frontend) ++ return -EINVAL; ++ ++ mutex_lock(&stream->dvb->feedlock); ++ if (stream->dvb->feeding++ == 0) { ++ CX18_DEBUG_INFO("Starting Transport DMA\n"); ++ mutex_lock(&cx->serialize_lock); ++ set_bit(CX18_F_S_STREAMING, &stream->s_flags); ++ ret = cx18_start_v4l2_encode_stream(stream); ++ if (ret < 0) { ++ CX18_DEBUG_INFO("Failed to start Transport DMA\n"); ++ stream->dvb->feeding--; ++ if (stream->dvb->feeding == 0) ++ clear_bit(CX18_F_S_STREAMING, &stream->s_flags); ++ } ++ mutex_unlock(&cx->serialize_lock); ++ } else ++ ret = 0; ++ mutex_unlock(&stream->dvb->feedlock); ++ ++ return ret; ++} ++ ++/* Kernel DVB framework calls this when the feed needs to stop. */ ++static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct cx18_stream *stream = (struct cx18_stream *)demux->priv; ++ struct cx18 *cx; ++ int ret = -EINVAL; ++ ++ if (stream) { ++ cx = stream->cx; ++ CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", ++ feed->pid, feed->index); ++ ++ mutex_lock(&stream->dvb->feedlock); ++ if (--stream->dvb->feeding == 0) { ++ CX18_DEBUG_INFO("Stopping Transport DMA\n"); ++ mutex_lock(&cx->serialize_lock); ++ ret = cx18_stop_v4l2_encode_stream(stream, 0); ++ mutex_unlock(&cx->serialize_lock); ++ } else ++ ret = 0; ++ mutex_unlock(&stream->dvb->feedlock); ++ } ++ ++ return ret; ++} ++ ++int cx18_dvb_register(struct cx18_stream *stream) ++{ ++ struct cx18 *cx = stream->cx; ++ struct cx18_dvb *dvb = stream->dvb; ++ struct dvb_adapter *dvb_adapter; ++ struct dvb_demux *dvbdemux; ++ struct dmx_demux *dmx; ++ int ret; ++ ++ if (!dvb) ++ return -EINVAL; ++ ++ dvb->enabled = 0; ++ dvb->stream = stream; ++ ++ ret = dvb_register_adapter(&dvb->dvb_adapter, ++ CX18_DRIVER_NAME, ++ THIS_MODULE, &cx->pci_dev->dev, adapter_nr); ++ if (ret < 0) ++ goto err_out; ++ ++ dvb_adapter = &dvb->dvb_adapter; ++ ++ dvbdemux = &dvb->demux; ++ ++ dvbdemux->priv = (void *)stream; ++ ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = cx18_dvb_start_feed; ++ dvbdemux->stop_feed = cx18_dvb_stop_feed; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); ++ ret = dvb_dmx_init(dvbdemux); ++ if (ret < 0) ++ goto err_dvb_unregister_adapter; ++ ++ dmx = &dvbdemux->dmx; ++ ++ dvb->hw_frontend.source = DMX_FRONTEND_0; ++ dvb->mem_frontend.source = DMX_MEMORY_FE; ++ dvb->dmxdev.filternum = 256; ++ dvb->dmxdev.demux = dmx; ++ ++ ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); ++ if (ret < 0) ++ goto err_dvb_dmx_release; ++ ++ ret = dmx->add_frontend(dmx, &dvb->hw_frontend); ++ if (ret < 0) ++ goto err_dvb_dmxdev_release; ++ ++ ret = dmx->add_frontend(dmx, &dvb->mem_frontend); ++ if (ret < 0) ++ goto err_remove_hw_frontend; ++ ++ ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); ++ if (ret < 0) ++ goto err_remove_mem_frontend; ++ ++ ret = dvb_register(stream); ++ if (ret < 0) ++ goto err_disconnect_frontend; ++ ++ dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); ++ ++ CX18_INFO("DVB Frontend registered\n"); ++ CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", ++ stream->dvb->dvb_adapter.num, stream->name, ++ stream->buffers, stream->buf_size/1024, ++ (stream->buf_size * 100 / 1024) % 100); ++ ++ mutex_init(&dvb->feedlock); ++ dvb->enabled = 1; ++ return ret; ++ ++err_disconnect_frontend: ++ dmx->disconnect_frontend(dmx); ++err_remove_mem_frontend: ++ dmx->remove_frontend(dmx, &dvb->mem_frontend); ++err_remove_hw_frontend: ++ dmx->remove_frontend(dmx, &dvb->hw_frontend); ++err_dvb_dmxdev_release: ++ dvb_dmxdev_release(&dvb->dmxdev); ++err_dvb_dmx_release: ++ dvb_dmx_release(dvbdemux); ++err_dvb_unregister_adapter: ++ dvb_unregister_adapter(dvb_adapter); ++err_out: ++ return ret; ++} ++ ++void cx18_dvb_unregister(struct cx18_stream *stream) ++{ ++ struct cx18 *cx = stream->cx; ++ struct cx18_dvb *dvb = stream->dvb; ++ struct dvb_adapter *dvb_adapter; ++ struct dvb_demux *dvbdemux; ++ struct dmx_demux *dmx; ++ ++ CX18_INFO("unregister DVB\n"); ++ ++ if (dvb == NULL || !dvb->enabled) ++ return; ++ ++ dvb_adapter = &dvb->dvb_adapter; ++ dvbdemux = &dvb->demux; ++ dmx = &dvbdemux->dmx; ++ ++ dmx->close(dmx); ++ dvb_net_release(&dvb->dvbnet); ++ dmx->remove_frontend(dmx, &dvb->mem_frontend); ++ dmx->remove_frontend(dmx, &dvb->hw_frontend); ++ dvb_dmxdev_release(&dvb->dmxdev); ++ dvb_dmx_release(dvbdemux); ++ dvb_unregister_frontend(dvb->fe); ++ dvb_frontend_detach(dvb->fe); ++ dvb_unregister_adapter(dvb_adapter); ++} ++ ++/* All the DVB attach calls go here, this function get's modified ++ * for each new card. cx18_dvb_start_feed() will also need changes. ++ */ ++static int dvb_register(struct cx18_stream *stream) ++{ ++ struct cx18_dvb *dvb = stream->dvb; ++ struct cx18 *cx = stream->cx; ++ int ret = 0; ++ ++ switch (cx->card->type) { ++ case CX18_CARD_HVR_1600_ESMT: ++ case CX18_CARD_HVR_1600_SAMSUNG: ++ dvb->fe = dvb_attach(s5h1409_attach, ++ &hauppauge_hvr1600_config, ++ &cx->i2c_adap[0]); ++ if (dvb->fe != NULL) { ++ dvb_attach(mxl5005s_attach, dvb->fe, ++ &cx->i2c_adap[0], ++ &hauppauge_hvr1600_tuner); ++ ret = 0; ++ } ++ break; ++ case CX18_CARD_HVR_1600_S5H1411: ++ dvb->fe = dvb_attach(s5h1411_attach, ++ &hcw_s5h1411_config, ++ &cx->i2c_adap[0]); ++ if (dvb->fe != NULL) ++ dvb_attach(tda18271_attach, dvb->fe, ++ 0x60, &cx->i2c_adap[0], ++ &hauppauge_tda18271_config); ++ break; ++ case CX18_CARD_LEADTEK_DVR3100H: ++ dvb->fe = dvb_attach(zl10353_attach, ++ &leadtek_dvr3100h_demod, ++ &cx->i2c_adap[1]); ++ if (dvb->fe != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &cx->i2c_adap[1], ++ .i2c_addr = 0xc2 >> 1, ++ .ctrl = NULL, ++ }; ++ static struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_ZARLINK456, ++ .type = XC2028_AUTO, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctrl); ++ } ++ break; ++ case CX18_CARD_YUAN_MPC718: ++ /* ++ * TODO ++ * Apparently, these cards also could instead have a ++ * DiBcom demod supported by one of the db7000 drivers ++ */ ++ dvb->fe = dvb_attach(mt352_attach, ++ &yuan_mpc718_mt352_demod, ++ &cx->i2c_adap[1]); ++ if (dvb->fe == NULL) ++ dvb->fe = dvb_attach(zl10353_attach, ++ &yuan_mpc718_zl10353_demod, ++ &cx->i2c_adap[1]); ++ if (dvb->fe != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &cx->i2c_adap[1], ++ .i2c_addr = 0xc2 >> 1, ++ .ctrl = NULL, ++ }; ++ static struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_ZARLINK456, ++ .type = XC2028_AUTO, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctrl); ++ } ++ break; ++ case CX18_CARD_GOTVIEW_PCI_DVD3: ++ dvb->fe = dvb_attach(zl10353_attach, ++ &gotview_dvd3_zl10353_demod, ++ &cx->i2c_adap[1]); ++ if (dvb->fe != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &cx->i2c_adap[1], ++ .i2c_addr = 0xc2 >> 1, ++ .ctrl = NULL, ++ }; ++ static struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_ZARLINK456, ++ .type = XC2028_AUTO, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctrl); ++ } ++ break; ++ default: ++ /* No Digital Tv Support */ ++ break; ++ } ++ ++ if (dvb->fe == NULL) { ++ CX18_ERR("frontend initialization failed\n"); ++ return -1; ++ } ++ ++ dvb->fe->callback = cx18_reset_tuner_gpio; ++ ++ ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); ++ if (ret < 0) { ++ if (dvb->fe->ops.release) ++ dvb->fe->ops.release(dvb->fe); ++ return ret; ++ } ++ ++ /* ++ * The firmware seems to enable the TS DMUX clock ++ * under various circumstances. However, since we know we ++ * might use it, let's just turn it on ourselves here. ++ */ ++ cx18_write_reg_expect(cx, ++ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK, ++ CX18_CLOCK_ENABLE2, ++ CX18_DMUX_CLK_MASK, ++ (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK); ++ ++ return ret; ++} ++ ++MODULE_FIRMWARE(FWFILE); +diff --git a/drivers/media/pci/cx18/cx18-dvb.h b/drivers/media/pci/cx18/cx18-dvb.h +new file mode 100644 +index 0000000..bf8d8f6 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-dvb.h +@@ -0,0 +1,25 @@ ++/* ++ * cx18 functions for DVB support ++ * ++ * Copyright (c) 2008 Steven Toth ++ * ++ * 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 "cx18-driver.h" ++ ++int cx18_dvb_register(struct cx18_stream *stream); ++void cx18_dvb_unregister(struct cx18_stream *stream); +diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c +new file mode 100644 +index 0000000..4bfd865 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-fileops.c +@@ -0,0 +1,881 @@ ++/* ++ * cx18 file operation functions ++ * ++ * Derived from ivtv-fileops.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-fileops.h" ++#include "cx18-i2c.h" ++#include "cx18-queue.h" ++#include "cx18-vbi.h" ++#include "cx18-audio.h" ++#include "cx18-mailbox.h" ++#include "cx18-scb.h" ++#include "cx18-streams.h" ++#include "cx18-controls.h" ++#include "cx18-ioctl.h" ++#include "cx18-cards.h" ++ ++/* This function tries to claim the stream for a specific file descriptor. ++ If no one else is using this stream then the stream is claimed and ++ associated VBI and IDX streams are also automatically claimed. ++ Possible error returns: -EBUSY if someone else has claimed ++ the stream or 0 on success. */ ++int cx18_claim_stream(struct cx18_open_id *id, int type) ++{ ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[type]; ++ struct cx18_stream *s_assoc; ++ ++ /* Nothing should ever try to directly claim the IDX stream */ ++ if (type == CX18_ENC_STREAM_TYPE_IDX) { ++ CX18_WARN("MPEG Index stream cannot be claimed " ++ "directly, but something tried.\n"); ++ return -EINVAL; ++ } ++ ++ if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { ++ /* someone already claimed this stream */ ++ if (s->id == id->open_id) { ++ /* yes, this file descriptor did. So that's OK. */ ++ return 0; ++ } ++ if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { ++ /* VBI is handled already internally, now also assign ++ the file descriptor to this stream for external ++ reading of the stream. */ ++ s->id = id->open_id; ++ CX18_DEBUG_INFO("Start Read VBI\n"); ++ return 0; ++ } ++ /* someone else is using this stream already */ ++ CX18_DEBUG_INFO("Stream %d is busy\n", type); ++ return -EBUSY; ++ } ++ s->id = id->open_id; ++ ++ /* ++ * CX18_ENC_STREAM_TYPE_MPG needs to claim: ++ * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or ++ * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI ++ * (We don't yet fix up MPEG Index entries for our inserted packets). ++ * ++ * For all other streams we're done. ++ */ ++ if (type != CX18_ENC_STREAM_TYPE_MPG) ++ return 0; ++ ++ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) ++ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; ++ else if (!cx18_stream_enabled(s_assoc)) ++ return 0; ++ ++ set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); ++ ++ /* mark that it is used internally */ ++ set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags); ++ return 0; ++} ++EXPORT_SYMBOL(cx18_claim_stream); ++ ++/* This function releases a previously claimed stream. It will take into ++ account associated VBI streams. */ ++void cx18_release_stream(struct cx18_stream *s) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_stream *s_assoc; ++ ++ s->id = -1; ++ if (s->type == CX18_ENC_STREAM_TYPE_IDX) { ++ /* ++ * The IDX stream is only used internally, and can ++ * only be indirectly unclaimed by unclaiming the MPG stream. ++ */ ++ return; ++ } ++ ++ if (s->type == CX18_ENC_STREAM_TYPE_VBI && ++ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { ++ /* this stream is still in use internally */ ++ return; ++ } ++ if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) { ++ CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name); ++ return; ++ } ++ ++ cx18_flush_queues(s); ++ ++ /* ++ * CX18_ENC_STREAM_TYPE_MPG needs to release the ++ * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams. ++ * ++ * For all other streams we're done. ++ */ ++ if (s->type != CX18_ENC_STREAM_TYPE_MPG) ++ return; ++ ++ /* Unclaim the associated MPEG Index stream */ ++ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { ++ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); ++ cx18_flush_queues(s_assoc); ++ } ++ ++ /* Unclaim the associated VBI stream */ ++ s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; ++ if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { ++ if (s_assoc->id == -1) { ++ /* ++ * The VBI stream is not still claimed by a file ++ * descriptor, so completely unclaim it. ++ */ ++ clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); ++ cx18_flush_queues(s_assoc); ++ } ++ } ++} ++EXPORT_SYMBOL(cx18_release_stream); ++ ++static void cx18_dualwatch(struct cx18 *cx) ++{ ++ struct v4l2_tuner vt; ++ u32 new_stereo_mode; ++ const u32 dual = 0x0200; ++ ++ new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); ++ memset(&vt, 0, sizeof(vt)); ++ cx18_call_all(cx, tuner, g_tuner, &vt); ++ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && ++ (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) ++ new_stereo_mode = dual; ++ ++ if (new_stereo_mode == cx->dualwatch_stereo_mode) ++ return; ++ ++ CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n", ++ cx->dualwatch_stereo_mode, new_stereo_mode); ++ if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode)) ++ CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); ++} ++ ++ ++static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, ++ int *err) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; ++ struct cx18_mdl *mdl; ++ DEFINE_WAIT(wait); ++ ++ *err = 0; ++ while (1) { ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) { ++ /* Process pending program updates and VBI data */ ++ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { ++ cx->dualwatch_jiffies = jiffies; ++ cx18_dualwatch(cx); ++ } ++ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && ++ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { ++ while ((mdl = cx18_dequeue(s_vbi, ++ &s_vbi->q_full))) { ++ /* byteswap and process VBI data */ ++ cx18_process_vbi_data(cx, mdl, ++ s_vbi->type); ++ cx18_stream_put_mdl_fw(s_vbi, mdl); ++ } ++ } ++ mdl = &cx->vbi.sliced_mpeg_mdl; ++ if (mdl->readpos != mdl->bytesused) ++ return mdl; ++ } ++ ++ /* do we have new data? */ ++ mdl = cx18_dequeue(s, &s->q_full); ++ if (mdl) { ++ if (!test_and_clear_bit(CX18_F_M_NEED_SWAP, ++ &mdl->m_flags)) ++ return mdl; ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) ++ /* byteswap MPG data */ ++ cx18_mdl_swap(mdl); ++ else { ++ /* byteswap and process VBI data */ ++ cx18_process_vbi_data(cx, mdl, s->type); ++ } ++ return mdl; ++ } ++ ++ /* return if end of stream */ ++ if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ CX18_DEBUG_INFO("EOS %s\n", s->name); ++ return NULL; ++ } ++ ++ /* return if file was opened with O_NONBLOCK */ ++ if (non_block) { ++ *err = -EAGAIN; ++ return NULL; ++ } ++ ++ /* wait for more data to arrive */ ++ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); ++ /* New buffers might have become available before we were added ++ to the waitqueue */ ++ if (!atomic_read(&s->q_full.depth)) ++ schedule(); ++ finish_wait(&s->waitq, &wait); ++ if (signal_pending(current)) { ++ /* return if a signal was received */ ++ CX18_DEBUG_INFO("User stopped %s\n", s->name); ++ *err = -EINTR; ++ return NULL; ++ } ++ } ++} ++ ++static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx) ++{ ++ struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl; ++ struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf; ++ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; ++ ++ buf->buf = cx->vbi.sliced_mpeg_data[idx]; ++ buf->bytesused = cx->vbi.sliced_mpeg_size[idx]; ++ buf->readpos = 0; ++ ++ mdl->curr_buf = NULL; ++ mdl->bytesused = cx->vbi.sliced_mpeg_size[idx]; ++ mdl->readpos = 0; ++} ++ ++static size_t cx18_copy_buf_to_user(struct cx18_stream *s, ++ struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop) ++{ ++ struct cx18 *cx = s->cx; ++ size_t len = buf->bytesused - buf->readpos; ++ ++ *stop = false; ++ if (len > ucount) ++ len = ucount; ++ if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && ++ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { ++ /* ++ * Try to find a good splice point in the PS, just before ++ * an MPEG-2 Program Pack start code, and provide only ++ * up to that point to the user, so it's easy to insert VBI data ++ * the next time around. ++ * ++ * This will not work for an MPEG-2 TS and has only been ++ * verified by analysis to work for an MPEG-2 PS. Helen Buus ++ * pointed out this works for the CX23416 MPEG-2 DVD compatible ++ * stream, and research indicates both the MPEG 2 SVCD and DVD ++ * stream types use an MPEG-2 PS container. ++ */ ++ /* ++ * An MPEG-2 Program Stream (PS) is a series of ++ * MPEG-2 Program Packs terminated by an ++ * MPEG Program End Code after the last Program Pack. ++ * A Program Pack may hold a PS System Header packet and any ++ * number of Program Elementary Stream (PES) Packets ++ */ ++ const char *start = buf->buf + buf->readpos; ++ const char *p = start + 1; ++ const u8 *q; ++ u8 ch = cx->search_pack_header ? 0xba : 0xe0; ++ int stuffing, i; ++ ++ while (start + len > p) { ++ /* Scan for a 0 to find a potential MPEG-2 start code */ ++ q = memchr(p, 0, start + len - p); ++ if (q == NULL) ++ break; ++ p = q + 1; ++ /* ++ * Keep looking if not a ++ * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba ++ * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0 ++ */ ++ if ((char *)q + 15 >= buf->buf + buf->bytesused || ++ q[1] != 0 || q[2] != 1 || q[3] != ch) ++ continue; ++ ++ /* If expecting the primary video PES */ ++ if (!cx->search_pack_header) { ++ /* Continue if it couldn't be a PES packet */ ++ if ((q[6] & 0xc0) != 0x80) ++ continue; ++ /* Check if a PTS or PTS & DTS follow */ ++ if (((q[7] & 0xc0) == 0x80 && /* PTS only */ ++ (q[9] & 0xf0) == 0x20) || /* PTS only */ ++ ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */ ++ (q[9] & 0xf0) == 0x30)) { /* DTS follows */ ++ /* Assume we found the video PES hdr */ ++ ch = 0xba; /* next want a Program Pack*/ ++ cx->search_pack_header = 1; ++ p = q + 9; /* Skip this video PES hdr */ ++ } ++ continue; ++ } ++ ++ /* We may have found a Program Pack start code */ ++ ++ /* Get the count of stuffing bytes & verify them */ ++ stuffing = q[13] & 7; ++ /* all stuffing bytes must be 0xff */ ++ for (i = 0; i < stuffing; i++) ++ if (q[14 + i] != 0xff) ++ break; ++ if (i == stuffing && /* right number of stuffing bytes*/ ++ (q[4] & 0xc4) == 0x44 && /* marker check */ ++ (q[12] & 3) == 3 && /* marker check */ ++ q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */ ++ q[15 + stuffing] == 0 && ++ q[16 + stuffing] == 1) { ++ /* We declare we actually found a Program Pack*/ ++ cx->search_pack_header = 0; /* expect vid PES */ ++ len = (char *)q - start; ++ cx18_setup_sliced_vbi_mdl(cx); ++ *stop = true; ++ break; ++ } ++ } ++ } ++ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { ++ CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n", ++ len, s->name); ++ return -EFAULT; ++ } ++ buf->readpos += len; ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG && ++ buf != &cx->vbi.sliced_mpeg_buf) ++ cx->mpg_data_received += len; ++ return len; ++} ++ ++static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, ++ struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) ++{ ++ size_t tot_written = 0; ++ int rc; ++ bool stop = false; ++ ++ if (mdl->curr_buf == NULL) ++ mdl->curr_buf = list_first_entry(&mdl->buf_list, ++ struct cx18_buffer, list); ++ ++ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { ++ /* ++ * For some reason we've exhausted the buffers, but the MDL ++ * object still said some data was unread. ++ * Fix that and bail out. ++ */ ++ mdl->readpos = mdl->bytesused; ++ return 0; ++ } ++ ++ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { ++ ++ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) ++ continue; ++ ++ rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written, ++ ucount - tot_written, &stop); ++ if (rc < 0) ++ return rc; ++ mdl->readpos += rc; ++ tot_written += rc; ++ ++ if (stop || /* Forced stopping point for VBI insertion */ ++ tot_written >= ucount || /* Reader request statisfied */ ++ mdl->curr_buf->readpos < mdl->curr_buf->bytesused || ++ mdl->readpos >= mdl->bytesused) /* MDL buffers drained */ ++ break; ++ } ++ return tot_written; ++} ++ ++static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, ++ size_t tot_count, int non_block) ++{ ++ struct cx18 *cx = s->cx; ++ size_t tot_written = 0; ++ int single_frame = 0; ++ ++ if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { ++ /* shouldn't happen */ ++ CX18_DEBUG_WARN("Stream %s not initialized before read\n", ++ s->name); ++ return -EIO; ++ } ++ ++ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the ++ frames should arrive one-by-one, so make sure we never output more ++ than one VBI frame at a time */ ++ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx)) ++ single_frame = 1; ++ ++ for (;;) { ++ struct cx18_mdl *mdl; ++ int rc; ++ ++ mdl = cx18_get_mdl(s, non_block, &rc); ++ /* if there is no data available... */ ++ if (mdl == NULL) { ++ /* if we got data, then return that regardless */ ++ if (tot_written) ++ break; ++ /* EOS condition */ ++ if (rc == 0) { ++ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); ++ clear_bit(CX18_F_S_APPL_IO, &s->s_flags); ++ cx18_release_stream(s); ++ } ++ /* set errno */ ++ return rc; ++ } ++ ++ rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written, ++ tot_count - tot_written); ++ ++ if (mdl != &cx->vbi.sliced_mpeg_mdl) { ++ if (mdl->readpos == mdl->bytesused) ++ cx18_stream_put_mdl_fw(s, mdl); ++ else ++ cx18_push(s, mdl, &s->q_full); ++ } else if (mdl->readpos == mdl->bytesused) { ++ int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; ++ ++ cx->vbi.sliced_mpeg_size[idx] = 0; ++ cx->vbi.inserted_frame++; ++ cx->vbi_data_inserted += mdl->bytesused; ++ } ++ if (rc < 0) ++ return rc; ++ tot_written += rc; ++ ++ if (tot_written == tot_count || single_frame) ++ break; ++ } ++ return tot_written; ++} ++ ++static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf, ++ size_t count, loff_t *pos, int non_block) ++{ ++ ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0; ++ struct cx18 *cx = s->cx; ++ ++ CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); ++ if (rc > 0) ++ pos += rc; ++ return rc; ++} ++ ++int cx18_start_capture(struct cx18_open_id *id) ++{ ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ struct cx18_stream *s_vbi; ++ struct cx18_stream *s_idx; ++ ++ if (s->type == CX18_ENC_STREAM_TYPE_RAD) { ++ /* you cannot read from these stream types. */ ++ return -EPERM; ++ } ++ ++ /* Try to claim this stream. */ ++ if (cx18_claim_stream(id, s->type)) ++ return -EBUSY; ++ ++ /* If capture is already in progress, then we also have to ++ do nothing extra. */ ++ if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || ++ test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ set_bit(CX18_F_S_APPL_IO, &s->s_flags); ++ return 0; ++ } ++ ++ /* Start associated VBI or IDX stream capture if required */ ++ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; ++ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) { ++ /* ++ * The VBI and IDX streams should have been claimed ++ * automatically, if for internal use, when the MPG stream was ++ * claimed. We only need to start these streams capturing. ++ */ ++ if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) && ++ !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { ++ if (cx18_start_v4l2_encode_stream(s_idx)) { ++ CX18_DEBUG_WARN("IDX capture start failed\n"); ++ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); ++ goto start_failed; ++ } ++ CX18_DEBUG_INFO("IDX capture started\n"); ++ } ++ if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && ++ !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { ++ if (cx18_start_v4l2_encode_stream(s_vbi)) { ++ CX18_DEBUG_WARN("VBI capture start failed\n"); ++ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); ++ goto start_failed; ++ } ++ CX18_DEBUG_INFO("VBI insertion started\n"); ++ } ++ } ++ ++ /* Tell the card to start capturing */ ++ if (!cx18_start_v4l2_encode_stream(s)) { ++ /* We're done */ ++ set_bit(CX18_F_S_APPL_IO, &s->s_flags); ++ /* Resume a possibly paused encoder */ ++ if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) ++ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle); ++ return 0; ++ } ++ ++start_failed: ++ CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); ++ ++ /* ++ * The associated VBI and IDX streams for internal use are released ++ * automatically when the MPG stream is released. We only need to stop ++ * the associated stream. ++ */ ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) { ++ /* Stop the IDX stream which is always for internal use */ ++ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { ++ cx18_stop_v4l2_encode_stream(s_idx, 0); ++ clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); ++ } ++ /* Stop the VBI stream, if only running for internal use */ ++ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && ++ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { ++ cx18_stop_v4l2_encode_stream(s_vbi, 0); ++ clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); ++ } ++ } ++ clear_bit(CX18_F_S_STREAMING, &s->s_flags); ++ cx18_release_stream(s); /* Also releases associated streams */ ++ return -EIO; ++} ++ ++ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, ++ loff_t *pos) ++{ ++ struct cx18_open_id *id = file2id(filp); ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ int rc; ++ ++ CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name); ++ ++ mutex_lock(&cx->serialize_lock); ++ rc = cx18_start_capture(id); ++ mutex_unlock(&cx->serialize_lock); ++ if (rc) ++ return rc; ++ ++ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (id->type == CX18_ENC_STREAM_TYPE_YUV)) { ++ return videobuf_read_stream(&s->vbuf_q, buf, count, pos, 0, ++ filp->f_flags & O_NONBLOCK); ++ } ++ ++ return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); ++} ++ ++unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) ++{ ++ struct cx18_open_id *id = file2id(filp); ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); ++ ++ /* Start a capture if there is none */ ++ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ int rc; ++ ++ mutex_lock(&cx->serialize_lock); ++ rc = cx18_start_capture(id); ++ mutex_unlock(&cx->serialize_lock); ++ if (rc) { ++ CX18_DEBUG_INFO("Could not start capture for %s (%d)\n", ++ s->name, rc); ++ return POLLERR; ++ } ++ CX18_DEBUG_FILE("Encoder poll started capture\n"); ++ } ++ ++ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (id->type == CX18_ENC_STREAM_TYPE_YUV)) { ++ int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait); ++ if (eof && videobuf_poll == POLLERR) ++ return POLLHUP; ++ else ++ return videobuf_poll; ++ } ++ ++ /* add stream's waitq to the poll list */ ++ CX18_DEBUG_HI_FILE("Encoder poll\n"); ++ poll_wait(filp, &s->waitq, wait); ++ ++ if (atomic_read(&s->q_full.depth)) ++ return POLLIN | POLLRDNORM; ++ if (eof) ++ return POLLHUP; ++ return 0; ++} ++ ++int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); ++ ++ if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (id->type == CX18_ENC_STREAM_TYPE_YUV)) { ++ ++ /* Start a capture if there is none */ ++ if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ int rc; ++ ++ mutex_lock(&cx->serialize_lock); ++ rc = cx18_start_capture(id); ++ mutex_unlock(&cx->serialize_lock); ++ if (rc) { ++ CX18_DEBUG_INFO( ++ "Could not start capture for %s (%d)\n", ++ s->name, rc); ++ return -EINVAL; ++ } ++ CX18_DEBUG_FILE("Encoder mmap started capture\n"); ++ } ++ ++ return videobuf_mmap_mapper(&s->vbuf_q, vma); ++ } ++ ++ return -EINVAL; ++} ++ ++void cx18_vb_timeout(unsigned long data) ++{ ++ struct cx18_stream *s = (struct cx18_stream *)data; ++ struct cx18_videobuf_buffer *buf; ++ unsigned long flags; ++ ++ /* Return all of the buffers in error state, so the vbi/vid inode ++ * can return from blocking. ++ */ ++ spin_lock_irqsave(&s->vb_lock, flags); ++ while (!list_empty(&s->vb_capture)) { ++ buf = list_entry(s->vb_capture.next, ++ struct cx18_videobuf_buffer, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ } ++ spin_unlock_irqrestore(&s->vb_lock, flags); ++} ++ ++void cx18_stop_capture(struct cx18_open_id *id, int gop_end) ++{ ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; ++ struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ ++ CX18_DEBUG_IOCTL("close() of %s\n", s->name); ++ ++ /* 'Unclaim' this stream */ ++ ++ /* Stop capturing */ ++ if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) { ++ CX18_DEBUG_INFO("close stopping capture\n"); ++ if (id->type == CX18_ENC_STREAM_TYPE_MPG) { ++ /* Stop internal use associated VBI and IDX streams */ ++ if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && ++ !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { ++ CX18_DEBUG_INFO("close stopping embedded VBI " ++ "capture\n"); ++ cx18_stop_v4l2_encode_stream(s_vbi, 0); ++ } ++ if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { ++ CX18_DEBUG_INFO("close stopping IDX capture\n"); ++ cx18_stop_v4l2_encode_stream(s_idx, 0); ++ } ++ } ++ if (id->type == CX18_ENC_STREAM_TYPE_VBI && ++ test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) ++ /* Also used internally, don't stop capturing */ ++ s->id = -1; ++ else ++ cx18_stop_v4l2_encode_stream(s, gop_end); ++ } ++ if (!gop_end) { ++ clear_bit(CX18_F_S_APPL_IO, &s->s_flags); ++ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); ++ cx18_release_stream(s); ++ } ++} ++ ++int cx18_v4l2_close(struct file *filp) ++{ ++ struct v4l2_fh *fh = filp->private_data; ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ CX18_DEBUG_IOCTL("close() of %s\n", s->name); ++ ++ mutex_lock(&cx->serialize_lock); ++ /* Stop radio */ ++ if (id->type == CX18_ENC_STREAM_TYPE_RAD && ++ v4l2_fh_is_singular_file(filp)) { ++ /* Closing radio device, return to TV mode */ ++ cx18_mute(cx); ++ /* Mark that the radio is no longer in use */ ++ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); ++ /* Switch tuner to TV */ ++ cx18_call_all(cx, core, s_std, cx->std); ++ /* Select correct audio input (i.e. TV tuner or Line in) */ ++ cx18_audio_set_io(cx); ++ if (atomic_read(&cx->ana_capturing) > 0) { ++ /* Undo video mute */ ++ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, ++ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) | ++ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8))); ++ } ++ /* Done! Unmute and continue. */ ++ cx18_unmute(cx); ++ } ++ ++ v4l2_fh_del(fh); ++ v4l2_fh_exit(fh); ++ ++ /* 'Unclaim' this stream */ ++ if (s->id == id->open_id) ++ cx18_stop_capture(id, 0); ++ kfree(id); ++ mutex_unlock(&cx->serialize_lock); ++ return 0; ++} ++ ++static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_open_id *item; ++ ++ CX18_DEBUG_FILE("open %s\n", s->name); ++ ++ /* Allocate memory */ ++ item = kzalloc(sizeof(struct cx18_open_id), GFP_KERNEL); ++ if (NULL == item) { ++ CX18_DEBUG_WARN("nomem on v4l2 open\n"); ++ return -ENOMEM; ++ } ++ v4l2_fh_init(&item->fh, s->video_dev); ++ ++ item->cx = cx; ++ item->type = s->type; ++ ++ item->open_id = cx->open_id++; ++ filp->private_data = &item->fh; ++ v4l2_fh_add(&item->fh); ++ ++ if (item->type == CX18_ENC_STREAM_TYPE_RAD && ++ v4l2_fh_is_singular_file(filp)) { ++ if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { ++ if (atomic_read(&cx->ana_capturing) > 0) { ++ /* switching to radio while capture is ++ in progress is not polite */ ++ v4l2_fh_del(&item->fh); ++ v4l2_fh_exit(&item->fh); ++ kfree(item); ++ return -EBUSY; ++ } ++ } ++ ++ /* Mark that the radio is being used. */ ++ set_bit(CX18_F_I_RADIO_USER, &cx->i_flags); ++ /* We have the radio */ ++ cx18_mute(cx); ++ /* Switch tuner to radio */ ++ cx18_call_all(cx, tuner, s_radio); ++ /* Select the correct audio input (i.e. radio tuner) */ ++ cx18_audio_set_io(cx); ++ /* Done! Unmute and continue. */ ++ cx18_unmute(cx); ++ } ++ return 0; ++} ++ ++int cx18_v4l2_open(struct file *filp) ++{ ++ int res; ++ struct video_device *video_dev = video_devdata(filp); ++ struct cx18_stream *s = video_get_drvdata(video_dev); ++ struct cx18 *cx = s->cx; ++ ++ mutex_lock(&cx->serialize_lock); ++ if (cx18_init_on_first_open(cx)) { ++ CX18_ERR("Failed to initialize on %s\n", ++ video_device_node_name(video_dev)); ++ mutex_unlock(&cx->serialize_lock); ++ return -ENXIO; ++ } ++ res = cx18_serialized_open(s, filp); ++ mutex_unlock(&cx->serialize_lock); ++ return res; ++} ++ ++void cx18_mute(struct cx18 *cx) ++{ ++ u32 h; ++ if (atomic_read(&cx->ana_capturing)) { ++ h = cx18_find_handle(cx); ++ if (h != CX18_INVALID_TASK_HANDLE) ++ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1); ++ else ++ CX18_ERR("Can't find valid task handle for mute\n"); ++ } ++ CX18_DEBUG_INFO("Mute\n"); ++} ++ ++void cx18_unmute(struct cx18 *cx) ++{ ++ u32 h; ++ if (atomic_read(&cx->ana_capturing)) { ++ h = cx18_find_handle(cx); ++ if (h != CX18_INVALID_TASK_HANDLE) { ++ cx18_msleep_timeout(100, 0); ++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12); ++ cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0); ++ } else ++ CX18_ERR("Can't find valid task handle for unmute\n"); ++ } ++ CX18_DEBUG_INFO("Unmute\n"); ++} +diff --git a/drivers/media/pci/cx18/cx18-fileops.h b/drivers/media/pci/cx18/cx18-fileops.h +new file mode 100644 +index 0000000..b9e5110 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-fileops.h +@@ -0,0 +1,41 @@ ++/* ++ * cx18 file operation functions ++ * ++ * Derived from ivtv-fileops.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++/* Testing/Debugging */ ++int cx18_v4l2_open(struct file *filp); ++ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, ++ loff_t *pos); ++ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count, ++ loff_t *pos); ++int cx18_v4l2_close(struct file *filp); ++unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait); ++int cx18_start_capture(struct cx18_open_id *id); ++void cx18_stop_capture(struct cx18_open_id *id, int gop_end); ++void cx18_mute(struct cx18 *cx); ++void cx18_unmute(struct cx18 *cx); ++int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma); ++void cx18_vb_timeout(unsigned long data); ++ ++/* Shared with cx18-alsa module */ ++int cx18_claim_stream(struct cx18_open_id *id, int type); ++void cx18_release_stream(struct cx18_stream *s); +diff --git a/drivers/media/pci/cx18/cx18-firmware.c b/drivers/media/pci/cx18/cx18-firmware.c +new file mode 100644 +index 0000000..a1c1cec +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-firmware.c +@@ -0,0 +1,459 @@ ++/* ++ * cx18 firmware functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-scb.h" ++#include "cx18-irq.h" ++#include "cx18-firmware.h" ++#include "cx18-cards.h" ++#include ++ ++#define CX18_PROC_SOFT_RESET 0xc70010 ++#define CX18_DDR_SOFT_RESET 0xc70014 ++#define CX18_CLOCK_SELECT1 0xc71000 ++#define CX18_CLOCK_SELECT2 0xc71004 ++#define CX18_HALF_CLOCK_SELECT1 0xc71008 ++#define CX18_HALF_CLOCK_SELECT2 0xc7100C ++#define CX18_CLOCK_POLARITY1 0xc71010 ++#define CX18_CLOCK_POLARITY2 0xc71014 ++#define CX18_ADD_DELAY_ENABLE1 0xc71018 ++#define CX18_ADD_DELAY_ENABLE2 0xc7101C ++#define CX18_CLOCK_ENABLE1 0xc71020 ++#define CX18_CLOCK_ENABLE2 0xc71024 ++ ++#define CX18_REG_BUS_TIMEOUT_EN 0xc72024 ++ ++#define CX18_FAST_CLOCK_PLL_INT 0xc78000 ++#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 ++#define CX18_FAST_CLOCK_PLL_POST 0xc78008 ++#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C ++#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 ++ ++#define CX18_SLOW_CLOCK_PLL_INT 0xc78014 ++#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 ++#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C ++#define CX18_MPEG_CLOCK_PLL_INT 0xc78040 ++#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 ++#define CX18_MPEG_CLOCK_PLL_POST 0xc78048 ++#define CX18_PLL_POWER_DOWN 0xc78088 ++#define CX18_SW1_INT_STATUS 0xc73104 ++#define CX18_SW1_INT_ENABLE_PCI 0xc7311C ++#define CX18_SW2_INT_SET 0xc73140 ++#define CX18_SW2_INT_STATUS 0xc73144 ++#define CX18_ADEC_CONTROL 0xc78120 ++ ++#define CX18_DDR_REQUEST_ENABLE 0xc80000 ++#define CX18_DDR_CHIP_CONFIG 0xc80004 ++#define CX18_DDR_REFRESH 0xc80008 ++#define CX18_DDR_TIMING1 0xc8000C ++#define CX18_DDR_TIMING2 0xc80010 ++#define CX18_DDR_POWER_REG 0xc8001C ++ ++#define CX18_DDR_TUNE_LANE 0xc80048 ++#define CX18_DDR_INITIAL_EMRS 0xc80054 ++#define CX18_DDR_MB_PER_ROW_7 0xc8009C ++#define CX18_DDR_BASE_63_ADDR 0xc804FC ++ ++#define CX18_WMB_CLIENT02 0xc90108 ++#define CX18_WMB_CLIENT05 0xc90114 ++#define CX18_WMB_CLIENT06 0xc90118 ++#define CX18_WMB_CLIENT07 0xc9011C ++#define CX18_WMB_CLIENT08 0xc90120 ++#define CX18_WMB_CLIENT09 0xc90124 ++#define CX18_WMB_CLIENT10 0xc90128 ++#define CX18_WMB_CLIENT11 0xc9012C ++#define CX18_WMB_CLIENT12 0xc90130 ++#define CX18_WMB_CLIENT13 0xc90134 ++#define CX18_WMB_CLIENT14 0xc90138 ++ ++#define CX18_DSP0_INTERRUPT_MASK 0xd0004C ++ ++#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ ++#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ ++ ++struct cx18_apu_rom_seghdr { ++ u32 sync1; ++ u32 sync2; ++ u32 addr; ++ u32 size; ++}; ++ ++static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) ++{ ++ const struct firmware *fw = NULL; ++ int i, j; ++ unsigned size; ++ u32 __iomem *dst = (u32 __iomem *)mem; ++ const u32 *src; ++ ++ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { ++ CX18_ERR("Unable to open firmware %s\n", fn); ++ CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); ++ return -ENOMEM; ++ } ++ ++ src = (const u32 *)fw->data; ++ ++ for (i = 0; i < fw->size; i += 4096) { ++ cx18_setup_page(cx, i); ++ for (j = i; j < fw->size && j < i + 4096; j += 4) { ++ /* no need for endianness conversion on the ppc */ ++ cx18_raw_writel(cx, *src, dst); ++ if (cx18_raw_readl(cx, dst) != *src) { ++ CX18_ERR("Mismatch at offset %x\n", i); ++ release_firmware(fw); ++ cx18_setup_page(cx, 0); ++ return -EIO; ++ } ++ dst++; ++ src++; ++ } ++ } ++ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) ++ CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); ++ size = fw->size; ++ release_firmware(fw); ++ cx18_setup_page(cx, SCB_OFFSET); ++ return size; ++} ++ ++static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, ++ u32 *entry_addr) ++{ ++ const struct firmware *fw = NULL; ++ int i, j; ++ unsigned size; ++ const u32 *src; ++ struct cx18_apu_rom_seghdr seghdr; ++ const u8 *vers; ++ u32 offset = 0; ++ u32 apu_version = 0; ++ int sz; ++ ++ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { ++ CX18_ERR("unable to open firmware %s\n", fn); ++ CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); ++ cx18_setup_page(cx, 0); ++ return -ENOMEM; ++ } ++ ++ *entry_addr = 0; ++ src = (const u32 *)fw->data; ++ vers = fw->data + sizeof(seghdr); ++ sz = fw->size; ++ ++ apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; ++ while (offset + sizeof(seghdr) < fw->size) { ++ const u32 *shptr = src + offset / 4; ++ ++ seghdr.sync1 = le32_to_cpu(shptr[0]); ++ seghdr.sync2 = le32_to_cpu(shptr[1]); ++ seghdr.addr = le32_to_cpu(shptr[2]); ++ seghdr.size = le32_to_cpu(shptr[3]); ++ ++ offset += sizeof(seghdr); ++ if (seghdr.sync1 != APU_ROM_SYNC1 || ++ seghdr.sync2 != APU_ROM_SYNC2) { ++ offset += seghdr.size; ++ continue; ++ } ++ CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, ++ seghdr.addr + seghdr.size - 1); ++ if (*entry_addr == 0) ++ *entry_addr = seghdr.addr; ++ if (offset + seghdr.size > sz) ++ break; ++ for (i = 0; i < seghdr.size; i += 4096) { ++ cx18_setup_page(cx, seghdr.addr + i); ++ for (j = i; j < seghdr.size && j < i + 4096; j += 4) { ++ /* no need for endianness conversion on the ppc */ ++ cx18_raw_writel(cx, src[(offset + j) / 4], ++ dst + seghdr.addr + j); ++ if (cx18_raw_readl(cx, dst + seghdr.addr + j) ++ != src[(offset + j) / 4]) { ++ CX18_ERR("Mismatch at offset %x\n", ++ offset + j); ++ release_firmware(fw); ++ cx18_setup_page(cx, 0); ++ return -EIO; ++ } ++ } ++ } ++ offset += seghdr.size; ++ } ++ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) ++ CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", ++ fn, apu_version, fw->size); ++ size = fw->size; ++ release_firmware(fw); ++ cx18_setup_page(cx, 0); ++ return size; ++} ++ ++void cx18_halt_firmware(struct cx18 *cx) ++{ ++ CX18_DEBUG_INFO("Preparing for firmware halt.\n"); ++ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, ++ 0x0000000F, 0x000F000F); ++ cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL, ++ 0x00000002, 0x00020002); ++} ++ ++void cx18_init_power(struct cx18 *cx, int lowpwr) ++{ ++ /* power-down Spare and AOM PLLs */ ++ /* power-up fast, slow and mpeg PLLs */ ++ cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); ++ ++ /* ADEC out of sleep */ ++ cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, ++ 0x00000000, 0x00020002); ++ ++ /* ++ * The PLL parameters are based on the external crystal frequency that ++ * would ideally be: ++ * ++ * NTSC Color subcarrier freq * 8 = ++ * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz ++ * ++ * The accidents of history and rationale that explain from where this ++ * combination of magic numbers originate can be found in: ++ * ++ * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in ++ * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 ++ * ++ * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the ++ * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 ++ * ++ * As Mike Bradley has rightly pointed out, it's not the exact crystal ++ * frequency that matters, only that all parts of the driver and ++ * firmware are using the same value (close to the ideal value). ++ * ++ * Since I have a strong suspicion that, if the firmware ever assumes a ++ * crystal value at all, it will assume 28.636360 MHz, the crystal ++ * freq used in calculations in this driver will be: ++ * ++ * xtal_freq = 28.636360 MHz ++ * ++ * an error of less than 0.13 ppm which is way, way better than any off ++ * the shelf crystal will have for accuracy anyway. ++ * ++ * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors. ++ * ++ * Many thanks to Jeff Campbell and Mike Bradley for their extensive ++ * investigation, experimentation, testing, and suggested solutions of ++ * of audio/video sync problems with SVideo and CVBS captures. ++ */ ++ ++ /* the fast clock is at 200/245 MHz */ ++ /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/ ++ /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/ ++ cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); ++ cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, ++ CX18_FAST_CLOCK_PLL_FRAC); ++ ++ cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); ++ cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); ++ cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); ++ ++ /* set slow clock to 125/120 MHz */ ++ /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */ ++ /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */ ++ cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT); ++ cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F, ++ CX18_SLOW_CLOCK_PLL_FRAC); ++ cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST); ++ ++ /* mpeg clock pll 54MHz */ ++ /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */ ++ cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); ++ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); ++ cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); ++ ++ /* Defaults */ ++ /* APU = SC or SC/2 = 125/62.5 */ ++ /* EPU = SC = 125 */ ++ /* DDR = FC = 180 */ ++ /* ENC = SC = 125 */ ++ /* AI1 = SC = 125 */ ++ /* VIM2 = disabled */ ++ /* PCI = FC/2 = 90 */ ++ /* AI2 = disabled */ ++ /* DEMUX = disabled */ ++ /* AO = SC/2 = 62.5 */ ++ /* SER = 54MHz */ ++ /* VFC = disabled */ ++ /* USB = disabled */ ++ ++ if (lowpwr) { ++ cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1, ++ 0x00000020, 0xFFFFFFFF); ++ cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2, ++ 0x00000004, 0xFFFFFFFF); ++ } else { ++ /* This doesn't explicitly set every clock select */ ++ cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1, ++ 0x00000004, 0x00060006); ++ cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2, ++ 0x00000006, 0x00060006); ++ } ++ ++ cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1, ++ 0x00000002, 0xFFFFFFFF); ++ cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2, ++ 0x00000104, 0xFFFFFFFF); ++ cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1, ++ 0x00009026, 0xFFFFFFFF); ++ cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2, ++ 0x00003105, 0xFFFFFFFF); ++} ++ ++void cx18_init_memory(struct cx18 *cx) ++{ ++ cx18_msleep_timeout(10, 0); ++ cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET, ++ 0x00000000, 0x00010001); ++ cx18_msleep_timeout(10, 0); ++ ++ cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); ++ ++ cx18_msleep_timeout(10, 0); ++ ++ cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); ++ cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); ++ cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); ++ ++ cx18_msleep_timeout(10, 0); ++ ++ /* Initialize DQS pad time */ ++ cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); ++ cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); ++ ++ cx18_msleep_timeout(10, 0); ++ ++ cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET, ++ 0x00000000, 0x00020002); ++ cx18_msleep_timeout(10, 0); ++ ++ /* use power-down mode when idle */ ++ cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); ++ ++ cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN, ++ 0x00000001, 0x00010001); ++ ++ cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); ++ cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); ++ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ ++ cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ ++} ++ ++#define CX18_CPU_FIRMWARE "v4l-cx23418-cpu.fw" ++#define CX18_APU_FIRMWARE "v4l-cx23418-apu.fw" ++ ++int cx18_firmware_init(struct cx18 *cx) ++{ ++ u32 fw_entry_addr; ++ int sz, retries; ++ u32 api_args[MAX_MB_ARGUMENTS]; ++ ++ /* Allow chip to control CLKRUN */ ++ cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); ++ ++ /* Stop the firmware */ ++ cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, ++ 0x0000000F, 0x000F000F); ++ ++ cx18_msleep_timeout(1, 0); ++ ++ /* If the CPU is still running */ ++ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { ++ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); ++ return -EIO; ++ } ++ ++ cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); ++ cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); ++ ++ sz = load_cpu_fw_direct(CX18_CPU_FIRMWARE, cx->enc_mem, cx); ++ if (sz <= 0) ++ return sz; ++ ++ /* The SCB & IPC area *must* be correct before starting the firmwares */ ++ cx18_init_scb(cx); ++ ++ fw_entry_addr = 0; ++ sz = load_apu_fw_direct(CX18_APU_FIRMWARE, cx->enc_mem, cx, ++ &fw_entry_addr); ++ if (sz <= 0) ++ return sz; ++ ++ /* Start the CPU. The CPU will take care of the APU for us. */ ++ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, ++ 0x00000000, 0x00080008); ++ ++ /* Wait up to 500 ms for the APU to come out of reset */ ++ for (retries = 0; ++ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; ++ retries++) ++ cx18_msleep_timeout(10, 0); ++ ++ cx18_msleep_timeout(200, 0); ++ ++ if (retries == 50 && ++ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { ++ CX18_ERR("Could not start the CPU\n"); ++ return -EIO; ++ } ++ ++ /* ++ * The CPU had once before set up to receive an interrupt for it's ++ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an ++ * interrupt when it sends us an ack, but by the time we process it, ++ * that flag in the SW2 status register has been cleared by the CPU ++ * firmware. We'll prevent that not so useful condition from happening ++ * by clearing the CPU's interrupt enables for Ack IRQ's we want to ++ * process. ++ */ ++ cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); ++ ++ /* Try a benign command to see if the CPU is alive and well */ ++ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); ++ if (sz < 0) ++ return sz; ++ ++ /* initialize GPIO */ ++ cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); ++ return 0; ++} ++ ++MODULE_FIRMWARE(CX18_CPU_FIRMWARE); ++MODULE_FIRMWARE(CX18_APU_FIRMWARE); +diff --git a/drivers/media/pci/cx18/cx18-firmware.h b/drivers/media/pci/cx18/cx18-firmware.h +new file mode 100644 +index 0000000..38d4c05 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-firmware.h +@@ -0,0 +1,25 @@ ++/* ++ * cx18 firmware functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int cx18_firmware_init(struct cx18 *cx); ++void cx18_halt_firmware(struct cx18 *cx); ++void cx18_init_memory(struct cx18 *cx); ++void cx18_init_power(struct cx18 *cx, int lowpwr); +diff --git a/drivers/media/pci/cx18/cx18-gpio.c b/drivers/media/pci/cx18/cx18-gpio.c +new file mode 100644 +index 0000000..5374aeb +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-gpio.c +@@ -0,0 +1,347 @@ ++/* ++ * cx18 gpio functions ++ * ++ * Derived from ivtv-gpio.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-cards.h" ++#include "cx18-gpio.h" ++#include "tuner-xc2028.h" ++ ++/********************* GPIO stuffs *********************/ ++ ++/* GPIO registers */ ++#define CX18_REG_GPIO_IN 0xc72010 ++#define CX18_REG_GPIO_OUT1 0xc78100 ++#define CX18_REG_GPIO_DIR1 0xc78108 ++#define CX18_REG_GPIO_OUT2 0xc78104 ++#define CX18_REG_GPIO_DIR2 0xc7810c ++ ++/* ++ * HVR-1600 GPIO pins, courtesy of Hauppauge: ++ * ++ * gpio0: zilog ir process reset pin ++ * gpio1: zilog programming pin (you should never use this) ++ * gpio12: cx24227 reset pin ++ * gpio13: cs5345 reset pin ++*/ ++ ++/* ++ * File scope utility functions ++ */ ++static void gpio_write(struct cx18 *cx) ++{ ++ u32 dir_lo = cx->gpio_dir & 0xffff; ++ u32 val_lo = cx->gpio_val & 0xffff; ++ u32 dir_hi = cx->gpio_dir >> 16; ++ u32 val_hi = cx->gpio_val >> 16; ++ ++ cx18_write_reg_expect(cx, dir_lo << 16, ++ CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo); ++ cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo, ++ CX18_REG_GPIO_OUT1, val_lo, dir_lo); ++ cx18_write_reg_expect(cx, dir_hi << 16, ++ CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi); ++ cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi, ++ CX18_REG_GPIO_OUT2, val_hi, dir_hi); ++} ++ ++static void gpio_update(struct cx18 *cx, u32 mask, u32 data) ++{ ++ if (mask == 0) ++ return; ++ ++ mutex_lock(&cx->gpio_lock); ++ cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); ++ gpio_write(cx); ++ mutex_unlock(&cx->gpio_lock); ++} ++ ++static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi, ++ unsigned int assert_msecs, ++ unsigned int recovery_msecs) ++{ ++ u32 mask; ++ ++ mask = active_lo | active_hi; ++ if (mask == 0) ++ return; ++ ++ /* ++ * Assuming that active_hi and active_lo are a subsets of the bits in ++ * gpio_dir. Also assumes that active_lo and active_hi don't overlap ++ * in any bit position ++ */ ++ ++ /* Assert */ ++ gpio_update(cx, mask, ~active_lo); ++ schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs)); ++ ++ /* Deassert */ ++ gpio_update(cx, mask, ~active_hi); ++ schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs)); ++} ++ ++/* ++ * GPIO Multiplexer - logical device ++ */ ++static int gpiomux_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ mutex_lock(&cx->gpio_lock); ++ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", ++ cx->gpio_dir, cx->gpio_val); ++ mutex_unlock(&cx->gpio_lock); ++ return 0; ++} ++ ++static int gpiomux_s_radio(struct v4l2_subdev *sd) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ /* ++ * FIXME - work out the cx->active/audio_input mess - this is ++ * intended to handle the switch to radio mode and set the ++ * audio routing, but we need to update the state in cx ++ */ ++ gpio_update(cx, cx->card->gpio_audio_input.mask, ++ cx->card->gpio_audio_input.radio); ++ return 0; ++} ++ ++static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ u32 data; ++ ++ switch (cx->card->audio_inputs[cx->audio_input].muxer_input) { ++ case 1: ++ data = cx->card->gpio_audio_input.linein; ++ break; ++ case 0: ++ data = cx->card->gpio_audio_input.tuner; ++ break; ++ default: ++ /* ++ * FIXME - work out the cx->active/audio_input mess - this is ++ * intended to handle the switch from radio mode and set the ++ * audio routing, but we need to update the state in cx ++ */ ++ data = cx->card->gpio_audio_input.tuner; ++ break; ++ } ++ gpio_update(cx, cx->card->gpio_audio_input.mask, data); ++ return 0; ++} ++ ++static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ u32 data; ++ ++ switch (input) { ++ case 0: ++ data = cx->card->gpio_audio_input.tuner; ++ break; ++ case 1: ++ data = cx->card->gpio_audio_input.linein; ++ break; ++ case 2: ++ data = cx->card->gpio_audio_input.radio; ++ break; ++ default: ++ return -EINVAL; ++ } ++ gpio_update(cx, cx->card->gpio_audio_input.mask, data); ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops gpiomux_core_ops = { ++ .log_status = gpiomux_log_status, ++ .s_std = gpiomux_s_std, ++}; ++ ++static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { ++ .s_radio = gpiomux_s_radio, ++}; ++ ++static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { ++ .s_routing = gpiomux_s_audio_routing, ++}; ++ ++static const struct v4l2_subdev_ops gpiomux_ops = { ++ .core = &gpiomux_core_ops, ++ .tuner = &gpiomux_tuner_ops, ++ .audio = &gpiomux_audio_ops, ++}; ++ ++/* ++ * GPIO Reset Controller - logical device ++ */ ++static int resetctrl_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ ++ mutex_lock(&cx->gpio_lock); ++ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", ++ cx->gpio_dir, cx->gpio_val); ++ mutex_unlock(&cx->gpio_lock); ++ return 0; ++} ++ ++static int resetctrl_reset(struct v4l2_subdev *sd, u32 val) ++{ ++ struct cx18 *cx = v4l2_get_subdevdata(sd); ++ const struct cx18_gpio_i2c_slave_reset *p; ++ ++ p = &cx->card->gpio_i2c_slave_reset; ++ switch (val) { ++ case CX18_GPIO_RESET_I2C: ++ gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask, ++ p->msecs_asserted, p->msecs_recovery); ++ break; ++ case CX18_GPIO_RESET_Z8F0811: ++ /* ++ * Assert timing for the Z8F0811 on HVR-1600 boards: ++ * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to ++ * initiate ++ * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock ++ * cycles (6,601,085 nanoseconds ~= 7 milliseconds) ++ * 3. DBG pin must be high before chip exits reset for normal ++ * operation. DBG is open drain and hopefully pulled high ++ * since we don't normally drive it (GPIO 1?) for the ++ * HVR-1600 ++ * 4. Z8F0811 won't exit reset until RESET is deasserted ++ * 5. Zilog comes out of reset, loads reset vector address and ++ * executes from there. Required recovery delay unknown. ++ */ ++ gpio_reset_seq(cx, p->ir_reset_mask, 0, ++ p->msecs_asserted, p->msecs_recovery); ++ break; ++ case CX18_GPIO_RESET_XC2028: ++ if (cx->card->tuners[0].tuner == TUNER_XC2028) ++ gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0, ++ 1, 1); ++ break; ++ } ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops resetctrl_core_ops = { ++ .log_status = resetctrl_log_status, ++ .reset = resetctrl_reset, ++}; ++ ++static const struct v4l2_subdev_ops resetctrl_ops = { ++ .core = &resetctrl_core_ops, ++}; ++ ++/* ++ * External entry points ++ */ ++void cx18_gpio_init(struct cx18 *cx) ++{ ++ mutex_lock(&cx->gpio_lock); ++ cx->gpio_dir = cx->card->gpio_init.direction; ++ cx->gpio_val = cx->card->gpio_init.initial_value; ++ ++ if (cx->card->tuners[0].tuner == TUNER_XC2028) { ++ cx->gpio_dir |= 1 << cx->card->xceive_pin; ++ cx->gpio_val |= 1 << cx->card->xceive_pin; ++ } ++ ++ if (cx->gpio_dir == 0) { ++ mutex_unlock(&cx->gpio_lock); ++ return; ++ } ++ ++ CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", ++ cx18_read_reg(cx, CX18_REG_GPIO_DIR1), ++ cx18_read_reg(cx, CX18_REG_GPIO_DIR2), ++ cx18_read_reg(cx, CX18_REG_GPIO_OUT1), ++ cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); ++ ++ gpio_write(cx); ++ mutex_unlock(&cx->gpio_lock); ++} ++ ++int cx18_gpio_register(struct cx18 *cx, u32 hw) ++{ ++ struct v4l2_subdev *sd; ++ const struct v4l2_subdev_ops *ops; ++ char *str; ++ ++ switch (hw) { ++ case CX18_HW_GPIO_MUX: ++ sd = &cx->sd_gpiomux; ++ ops = &gpiomux_ops; ++ str = "gpio-mux"; ++ break; ++ case CX18_HW_GPIO_RESET_CTRL: ++ sd = &cx->sd_resetctrl; ++ ops = &resetctrl_ops; ++ str = "gpio-reset-ctrl"; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ v4l2_subdev_init(sd, ops); ++ v4l2_set_subdevdata(sd, cx); ++ snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str); ++ sd->grp_id = hw; ++ return v4l2_device_register_subdev(&cx->v4l2_dev, sd); ++} ++ ++void cx18_reset_ir_gpio(void *data) ++{ ++ struct cx18 *cx = to_cx18((struct v4l2_device *)data); ++ ++ if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) ++ return; ++ ++ CX18_DEBUG_INFO("Resetting IR microcontroller\n"); ++ ++ v4l2_subdev_call(&cx->sd_resetctrl, ++ core, reset, CX18_GPIO_RESET_Z8F0811); ++} ++EXPORT_SYMBOL(cx18_reset_ir_gpio); ++/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */ ++ ++/* Xceive tuner reset function */ ++int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) ++{ ++ struct i2c_algo_bit_data *algo = dev; ++ struct cx18_i2c_algo_callback_data *cb_data = algo->data; ++ struct cx18 *cx = cb_data->cx; ++ ++ if (cmd != XC2028_TUNER_RESET || ++ cx->card->tuners[0].tuner != TUNER_XC2028) ++ return 0; ++ ++ CX18_DEBUG_INFO("Resetting XCeive tuner\n"); ++ return v4l2_subdev_call(&cx->sd_resetctrl, ++ core, reset, CX18_GPIO_RESET_XC2028); ++} +diff --git a/drivers/media/pci/cx18/cx18-gpio.h b/drivers/media/pci/cx18/cx18-gpio.h +new file mode 100644 +index 0000000..4aea2ef +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-gpio.h +@@ -0,0 +1,34 @@ ++/* ++ * cx18 gpio functions ++ * ++ * Derived from ivtv-gpio.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++void cx18_gpio_init(struct cx18 *cx); ++int cx18_gpio_register(struct cx18 *cx, u32 hw); ++ ++enum cx18_gpio_reset_type { ++ CX18_GPIO_RESET_I2C = 0, ++ CX18_GPIO_RESET_Z8F0811 = 1, ++ CX18_GPIO_RESET_XC2028 = 2, ++}; ++ ++void cx18_reset_ir_gpio(void *data); ++int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value); +diff --git a/drivers/media/pci/cx18/cx18-i2c.c b/drivers/media/pci/cx18/cx18-i2c.c +new file mode 100644 +index 0000000..d61ac63 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-i2c.c +@@ -0,0 +1,328 @@ ++/* ++ * cx18 I2C functions ++ * ++ * Derived from ivtv-i2c.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-cards.h" ++#include "cx18-gpio.h" ++#include "cx18-i2c.h" ++#include "cx18-irq.h" ++ ++#define CX18_REG_I2C_1_WR 0xf15000 ++#define CX18_REG_I2C_1_RD 0xf15008 ++#define CX18_REG_I2C_2_WR 0xf25100 ++#define CX18_REG_I2C_2_RD 0xf25108 ++ ++#define SETSCL_BIT 0x0001 ++#define SETSDL_BIT 0x0002 ++#define GETSCL_BIT 0x0004 ++#define GETSDL_BIT 0x0008 ++ ++#define CX18_CS5345_I2C_ADDR 0x4c ++#define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70 ++#define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71 ++ ++/* This array should match the CX18_HW_ defines */ ++static const u8 hw_addrs[] = { ++ 0, /* CX18_HW_TUNER */ ++ 0, /* CX18_HW_TVEEPROM */ ++ CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ ++ 0, /* CX18_HW_DVB */ ++ 0, /* CX18_HW_418_AV */ ++ 0, /* CX18_HW_GPIO_MUX */ ++ 0, /* CX18_HW_GPIO_RESET_CTRL */ ++ CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */ ++ CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */ ++}; ++ ++/* This array should match the CX18_HW_ defines */ ++/* This might well become a card-specific array */ ++static const u8 hw_bus[] = { ++ 1, /* CX18_HW_TUNER */ ++ 0, /* CX18_HW_TVEEPROM */ ++ 0, /* CX18_HW_CS5345 */ ++ 0, /* CX18_HW_DVB */ ++ 0, /* CX18_HW_418_AV */ ++ 0, /* CX18_HW_GPIO_MUX */ ++ 0, /* CX18_HW_GPIO_RESET_CTRL */ ++ 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */ ++ 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */ ++}; ++ ++/* This array should match the CX18_HW_ defines */ ++static const char * const hw_devicenames[] = { ++ "tuner", ++ "tveeprom", ++ "cs5345", ++ "cx23418_DTV", ++ "cx23418_AV", ++ "gpio_mux", ++ "gpio_reset_ctrl", ++ "ir_tx_z8f0811_haup", ++ "ir_rx_z8f0811_haup", ++}; ++ ++static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, ++ const char *type, u8 addr) ++{ ++ struct i2c_board_info info; ++ struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data; ++ unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, type, I2C_NAME_SIZE); ++ ++ /* Our default information for ir-kbd-i2c.c to use */ ++ switch (hw) { ++ case CX18_HW_Z8F0811_IR_RX_HAUP: ++ init_data->ir_codes = RC_MAP_HAUPPAUGE; ++ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; ++ init_data->type = RC_BIT_RC5; ++ init_data->name = cx->card_name; ++ info.platform_data = init_data; ++ break; ++ } ++ ++ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? ++ -1 : 0; ++} ++ ++int cx18_i2c_register(struct cx18 *cx, unsigned idx) ++{ ++ struct v4l2_subdev *sd; ++ int bus = hw_bus[idx]; ++ struct i2c_adapter *adap = &cx->i2c_adap[bus]; ++ const char *type = hw_devicenames[idx]; ++ u32 hw = 1 << idx; ++ ++ if (idx >= ARRAY_SIZE(hw_addrs)) ++ return -1; ++ ++ if (hw == CX18_HW_TUNER) { ++ /* special tuner group handling */ ++ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, ++ adap, type, 0, cx->card_i2c->radio); ++ if (sd != NULL) ++ sd->grp_id = hw; ++ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, ++ adap, type, 0, cx->card_i2c->demod); ++ if (sd != NULL) ++ sd->grp_id = hw; ++ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, ++ adap, type, 0, cx->card_i2c->tv); ++ if (sd != NULL) ++ sd->grp_id = hw; ++ return sd != NULL ? 0 : -1; ++ } ++ ++ if (hw & CX18_HW_IR_ANY) ++ return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]); ++ ++ /* Is it not an I2C device or one we do not wish to register? */ ++ if (!hw_addrs[idx]) ++ return -1; ++ ++ /* It's an I2C device other than an analog tuner or IR chip */ ++ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, type, hw_addrs[idx], ++ NULL); ++ if (sd != NULL) ++ sd->grp_id = hw; ++ return sd != NULL ? 0 : -1; ++} ++ ++/* Find the first member of the subdev group id in hw */ ++struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw) ++{ ++ struct v4l2_subdev *result = NULL; ++ struct v4l2_subdev *sd; ++ ++ spin_lock(&cx->v4l2_dev.lock); ++ v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) { ++ if (sd->grp_id == hw) { ++ result = sd; ++ break; ++ } ++ } ++ spin_unlock(&cx->v4l2_dev.lock); ++ return result; ++} ++ ++static void cx18_setscl(void *data, int state) ++{ ++ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; ++ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; ++ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; ++ u32 r = cx18_read_reg(cx, addr); ++ ++ if (state) ++ cx18_write_reg(cx, r | SETSCL_BIT, addr); ++ else ++ cx18_write_reg(cx, r & ~SETSCL_BIT, addr); ++} ++ ++static void cx18_setsda(void *data, int state) ++{ ++ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; ++ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; ++ u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; ++ u32 r = cx18_read_reg(cx, addr); ++ ++ if (state) ++ cx18_write_reg(cx, r | SETSDL_BIT, addr); ++ else ++ cx18_write_reg(cx, r & ~SETSDL_BIT, addr); ++} ++ ++static int cx18_getscl(void *data) ++{ ++ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; ++ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; ++ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; ++ ++ return cx18_read_reg(cx, addr) & GETSCL_BIT; ++} ++ ++static int cx18_getsda(void *data) ++{ ++ struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; ++ int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; ++ u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; ++ ++ return cx18_read_reg(cx, addr) & GETSDL_BIT; ++} ++ ++/* template for i2c-bit-algo */ ++static struct i2c_adapter cx18_i2c_adap_template = { ++ .name = "cx18 i2c driver", ++ .algo = NULL, /* set by i2c-algo-bit */ ++ .algo_data = NULL, /* filled from template */ ++ .owner = THIS_MODULE, ++}; ++ ++#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */ ++#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */ ++ ++static struct i2c_algo_bit_data cx18_i2c_algo_template = { ++ .setsda = cx18_setsda, ++ .setscl = cx18_setscl, ++ .getsda = cx18_getsda, ++ .getscl = cx18_getscl, ++ .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/ ++ .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ ++}; ++ ++/* init + register i2c adapter */ ++int init_cx18_i2c(struct cx18 *cx) ++{ ++ int i, err; ++ CX18_DEBUG_I2C("i2c init\n"); ++ ++ for (i = 0; i < 2; i++) { ++ /* Setup algorithm for adapter */ ++ cx->i2c_algo[i] = cx18_i2c_algo_template; ++ cx->i2c_algo_cb_data[i].cx = cx; ++ cx->i2c_algo_cb_data[i].bus_index = i; ++ cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; ++ ++ /* Setup adapter */ ++ cx->i2c_adap[i] = cx18_i2c_adap_template; ++ cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; ++ sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), ++ " #%d-%d", cx->instance, i); ++ i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev); ++ cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; ++ } ++ ++ if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { ++ /* Reset/Unreset I2C hardware block */ ++ /* Clock select 220MHz */ ++ cx18_write_reg_expect(cx, 0x10000000, 0xc71004, ++ 0x00000000, 0x10001000); ++ /* Clock Enable */ ++ cx18_write_reg_expect(cx, 0x10001000, 0xc71024, ++ 0x00001000, 0x10001000); ++ } ++ /* courtesy of Steven Toth */ ++ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); ++ mdelay(10); ++ cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); ++ mdelay(10); ++ cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); ++ mdelay(10); ++ ++ /* Set to edge-triggered intrs. */ ++ cx18_write_reg(cx, 0x00c00000, 0xc730c8); ++ /* Clear any stale intrs */ ++ cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS, ++ ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT); ++ ++ /* Hw I2C1 Clock Freq ~100kHz */ ++ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); ++ cx18_setscl(&cx->i2c_algo_cb_data[0], 1); ++ cx18_setsda(&cx->i2c_algo_cb_data[0], 1); ++ ++ /* Hw I2C2 Clock Freq ~100kHz */ ++ cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); ++ cx18_setscl(&cx->i2c_algo_cb_data[1], 1); ++ cx18_setsda(&cx->i2c_algo_cb_data[1], 1); ++ ++ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, ++ core, reset, (u32) CX18_GPIO_RESET_I2C); ++ ++ err = i2c_bit_add_bus(&cx->i2c_adap[0]); ++ if (err) ++ goto err; ++ err = i2c_bit_add_bus(&cx->i2c_adap[1]); ++ if (err) ++ goto err_del_bus_0; ++ return 0; ++ ++ err_del_bus_0: ++ i2c_del_adapter(&cx->i2c_adap[0]); ++ err: ++ return err; ++} ++ ++void exit_cx18_i2c(struct cx18 *cx) ++{ ++ int i; ++ CX18_DEBUG_I2C("i2c exit\n"); ++ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4, ++ CX18_REG_I2C_1_WR); ++ cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4, ++ CX18_REG_I2C_2_WR); ++ ++ for (i = 0; i < 2; i++) { ++ i2c_del_adapter(&cx->i2c_adap[i]); ++ } ++} ++ ++/* ++ Hauppauge HVR1600 should have: ++ 32 cx24227 ++ 98 unknown ++ a0 eeprom ++ c2 tuner ++ e? zilog ir ++ */ +diff --git a/drivers/media/pci/cx18/cx18-i2c.h b/drivers/media/pci/cx18/cx18-i2c.h +new file mode 100644 +index 0000000..1180fdc +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-i2c.h +@@ -0,0 +1,29 @@ ++/* ++ * cx18 I2C functions ++ * ++ * Derived from ivtv-i2c.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int cx18_i2c_register(struct cx18 *cx, unsigned idx); ++struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw); ++ ++/* init + register i2c adapter */ ++int init_cx18_i2c(struct cx18 *cx); ++void exit_cx18_i2c(struct cx18 *cx); +diff --git a/drivers/media/pci/cx18/cx18-io.c b/drivers/media/pci/cx18/cx18-io.c +new file mode 100644 +index 0000000..49b9dbd +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-io.c +@@ -0,0 +1,97 @@ ++/* ++ * cx18 driver PCI memory mapped IO access routines ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-irq.h" ++ ++void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) ++{ ++ u8 __iomem *dst = addr; ++ u16 val2 = val | (val << 8); ++ u32 val4 = val2 | (val2 << 16); ++ ++ /* Align writes on the CX23418's addresses */ ++ if ((count > 0) && ((unsigned long)dst & 1)) { ++ cx18_writeb(cx, (u8) val, dst); ++ count--; ++ dst++; ++ } ++ if ((count > 1) && ((unsigned long)dst & 2)) { ++ cx18_writew(cx, val2, dst); ++ count -= 2; ++ dst += 2; ++ } ++ while (count > 3) { ++ cx18_writel(cx, val4, dst); ++ count -= 4; ++ dst += 4; ++ } ++ if (count > 1) { ++ cx18_writew(cx, val2, dst); ++ count -= 2; ++ dst += 2; ++ } ++ if (count > 0) ++ cx18_writeb(cx, (u8) val, dst); ++} ++ ++void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) ++{ ++ cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val); ++ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val; ++ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); ++} ++ ++void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) ++{ ++ cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val; ++ cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); ++} ++ ++void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) ++{ ++ cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val); ++ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val; ++ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); ++} ++ ++void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) ++{ ++ cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val; ++ cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); ++} ++ ++void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val) ++{ ++ u32 r; ++ r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU); ++ cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU); ++} ++ ++void cx18_setup_page(struct cx18 *cx, u32 addr) ++{ ++ u32 val; ++ val = cx18_read_reg(cx, 0xD000F8); ++ val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00); ++ cx18_write_reg(cx, val, 0xD000F8); ++} +diff --git a/drivers/media/pci/cx18/cx18-io.h b/drivers/media/pci/cx18/cx18-io.h +new file mode 100644 +index 0000000..18974d8 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-io.h +@@ -0,0 +1,191 @@ ++/* ++ * cx18 driver PCI memory mapped IO access routines ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef CX18_IO_H ++#define CX18_IO_H ++ ++#include "cx18-driver.h" ++ ++/* ++ * Readback and retry of MMIO access for reliability: ++ * The concept was suggested by Steve Toth . ++ * The implmentation is the fault of Andy Walls . ++ * ++ * *write* functions are implied to retry the mmio unless suffixed with _noretry ++ * *read* functions never retry the mmio (it never helps to do so) ++ */ ++ ++/* Non byteswapping memory mapped IO */ ++static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) ++{ ++ return __raw_readl(addr); ++} ++ ++static inline ++void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) ++{ ++ __raw_writel(val, addr); ++} ++ ++static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { ++ cx18_raw_writel_noretry(cx, val, addr); ++ if (val == cx18_raw_readl(cx, addr)) ++ break; ++ } ++} ++ ++/* Normal memory mapped IO */ ++static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) ++{ ++ return readl(addr); ++} ++ ++static inline ++void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) ++{ ++ writel(val, addr); ++} ++ ++static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { ++ cx18_writel_noretry(cx, val, addr); ++ if (val == cx18_readl(cx, addr)) ++ break; ++ } ++} ++ ++static inline ++void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, ++ u32 eval, u32 mask) ++{ ++ int i; ++ u32 r; ++ eval &= mask; ++ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { ++ cx18_writel_noretry(cx, val, addr); ++ r = cx18_readl(cx, addr); ++ if (r == 0xffffffff && eval != 0xffffffff) ++ continue; ++ if (eval == (r & mask)) ++ break; ++ } ++} ++ ++static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr) ++{ ++ return readw(addr); ++} ++ ++static inline ++void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr) ++{ ++ writew(val, addr); ++} ++ ++static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { ++ cx18_writew_noretry(cx, val, addr); ++ if (val == cx18_readw(cx, addr)) ++ break; ++ } ++} ++ ++static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) ++{ ++ return readb(addr); ++} ++ ++static inline ++void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr) ++{ ++ writeb(val, addr); ++} ++ ++static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr) ++{ ++ int i; ++ for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { ++ cx18_writeb_noretry(cx, val, addr); ++ if (val == cx18_readb(cx, addr)) ++ break; ++ } ++} ++ ++static inline ++void cx18_memcpy_fromio(struct cx18 *cx, void *to, ++ const void __iomem *from, unsigned int len) ++{ ++ memcpy_fromio(to, from, len); ++} ++ ++void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); ++ ++ ++/* Access "register" region of CX23418 memory mapped I/O */ ++static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg) ++{ ++ cx18_writel_noretry(cx, val, cx->reg_mem + reg); ++} ++ ++static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) ++{ ++ cx18_writel(cx, val, cx->reg_mem + reg); ++} ++ ++static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, ++ u32 eval, u32 mask) ++{ ++ cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask); ++} ++ ++static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg) ++{ ++ return cx18_readl(cx, cx->reg_mem + reg); ++} ++ ++ ++/* Access "encoder memory" region of CX23418 memory mapped I/O */ ++static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) ++{ ++ cx18_writel(cx, val, cx->enc_mem + addr); ++} ++ ++static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr) ++{ ++ return cx18_readl(cx, cx->enc_mem + addr); ++} ++ ++void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); ++void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); ++void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); ++void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); ++void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val); ++void cx18_setup_page(struct cx18 *cx, u32 addr); ++ ++#endif /* CX18_IO_H */ +diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c +new file mode 100644 +index 0000000..cd8d2c2 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-ioctl.c +@@ -0,0 +1,1190 @@ ++/* ++ * cx18 ioctl system call ++ * ++ * Derived from ivtv-ioctl.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-version.h" ++#include "cx18-mailbox.h" ++#include "cx18-i2c.h" ++#include "cx18-queue.h" ++#include "cx18-fileops.h" ++#include "cx18-vbi.h" ++#include "cx18-audio.h" ++#include "cx18-video.h" ++#include "cx18-streams.h" ++#include "cx18-ioctl.h" ++#include "cx18-gpio.h" ++#include "cx18-controls.h" ++#include "cx18-cards.h" ++#include "cx18-av-core.h" ++#include ++#include ++ ++u16 cx18_service2vbi(int type) ++{ ++ switch (type) { ++ case V4L2_SLICED_TELETEXT_B: ++ return CX18_SLICED_TYPE_TELETEXT_B; ++ case V4L2_SLICED_CAPTION_525: ++ return CX18_SLICED_TYPE_CAPTION_525; ++ case V4L2_SLICED_WSS_625: ++ return CX18_SLICED_TYPE_WSS_625; ++ case V4L2_SLICED_VPS: ++ return CX18_SLICED_TYPE_VPS; ++ default: ++ return 0; ++ } ++} ++ ++/* Check if VBI services are allowed on the (field, line) for the video std */ ++static int valid_service_line(int field, int line, int is_pal) ++{ ++ return (is_pal && line >= 6 && ++ ((field == 0 && line <= 23) || (field == 1 && line <= 22))) || ++ (!is_pal && line >= 10 && line < 22); ++} ++ ++/* ++ * For a (field, line, std) and inbound potential set of services for that line, ++ * return the first valid service of those passed in the incoming set for that ++ * line in priority order: ++ * CC, VPS, or WSS over TELETEXT for well known lines ++ * TELETEXT, before VPS, before CC, before WSS, for other lines ++ */ ++static u16 select_service_from_set(int field, int line, u16 set, int is_pal) ++{ ++ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); ++ int i; ++ ++ set = set & valid_set; ++ if (set == 0 || !valid_service_line(field, line, is_pal)) ++ return 0; ++ if (!is_pal) { ++ if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) ++ return V4L2_SLICED_CAPTION_525; ++ } else { ++ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) ++ return V4L2_SLICED_VPS; ++ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) ++ return V4L2_SLICED_WSS_625; ++ if (line == 23) ++ return 0; ++ } ++ for (i = 0; i < 32; i++) { ++ if ((1 << i) & set) ++ return 1 << i; ++ } ++ return 0; ++} ++ ++/* ++ * Expand the service_set of *fmt into valid service_lines for the std, ++ * and clear the passed in fmt->service_set ++ */ ++void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) ++{ ++ u16 set = fmt->service_set; ++ int f, l; ++ ++ fmt->service_set = 0; ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) ++ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); ++ } ++} ++ ++/* ++ * Sanitize the service_lines in *fmt per the video std, and return 1 ++ * if any service_line is left as valid after santization ++ */ ++static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) ++{ ++ int f, l; ++ u16 set = 0; ++ ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); ++ set |= fmt->service_lines[f][l]; ++ } ++ } ++ return set != 0; ++} ++ ++/* Compute the service_set from the assumed valid service_lines of *fmt */ ++u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) ++{ ++ int f, l; ++ u16 set = 0; ++ ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) ++ set |= fmt->service_lines[f][l]; ++ } ++ return set; ++} ++ ++static int cx18_g_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ pixfmt->width = cx->cxhdl.width; ++ pixfmt->height = cx->cxhdl.height; ++ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ pixfmt->field = V4L2_FIELD_INTERLACED; ++ pixfmt->priv = 0; ++ if (id->type == CX18_ENC_STREAM_TYPE_YUV) { ++ pixfmt->pixelformat = s->pixelformat; ++ pixfmt->sizeimage = s->vb_bytes_per_frame; ++ pixfmt->bytesperline = 720; ++ } else { ++ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; ++ pixfmt->sizeimage = 128 * 1024; ++ pixfmt->bytesperline = 0; ++ } ++ return 0; ++} ++ ++static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; ++ ++ vbifmt->sampling_rate = 27000000; ++ vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */ ++ vbifmt->samples_per_line = vbi_active_samples - 4; ++ vbifmt->sample_format = V4L2_PIX_FMT_GREY; ++ vbifmt->start[0] = cx->vbi.start[0]; ++ vbifmt->start[1] = cx->vbi.start[1]; ++ vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count; ++ vbifmt->flags = 0; ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ return 0; ++} ++ ++static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ ++ /* sane, V4L2 spec compliant, defaults */ ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); ++ vbifmt->service_set = 0; ++ ++ /* ++ * Fetch the configured service_lines and total service_set from the ++ * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in ++ * fmt->fmt.sliced under valid calling conditions ++ */ ++ if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced)) ++ return -EINVAL; ++ ++ vbifmt->service_set = cx18_get_service_set(vbifmt); ++ return 0; ++} ++ ++static int cx18_try_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ int w = fmt->fmt.pix.width; ++ int h = fmt->fmt.pix.height; ++ int min_h = 2; ++ ++ w = min(w, 720); ++ w = max(w, 2); ++ if (id->type == CX18_ENC_STREAM_TYPE_YUV) { ++ /* YUV height must be a multiple of 32 */ ++ h &= ~0x1f; ++ min_h = 32; ++ } ++ h = min(h, cx->is_50hz ? 576 : 480); ++ h = max(h, min_h); ++ ++ fmt->fmt.pix.width = w; ++ fmt->fmt.pix.height = h; ++ return 0; ++} ++ ++static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ return cx18_g_fmt_vbi_cap(file, fh, fmt); ++} ++ ++static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ ++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ ++ /* If given a service set, expand it validly & clear passed in set */ ++ if (vbifmt->service_set) ++ cx18_expand_service_set(vbifmt, cx->is_50hz); ++ /* Sanitize the service_lines, and compute the new set if any valid */ ++ if (check_service_set(vbifmt, cx->is_50hz)) ++ vbifmt->service_set = cx18_get_service_set(vbifmt); ++ return 0; ++} ++ ++static int cx18_s_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ int ret; ++ int w, h; ++ ++ ret = cx18_try_fmt_vid_cap(file, fh, fmt); ++ if (ret) ++ return ret; ++ w = fmt->fmt.pix.width; ++ h = fmt->fmt.pix.height; ++ ++ if (cx->cxhdl.width == w && cx->cxhdl.height == h && ++ s->pixelformat == fmt->fmt.pix.pixelformat) ++ return 0; ++ ++ if (atomic_read(&cx->ana_capturing) > 0) ++ return -EBUSY; ++ ++ s->pixelformat = fmt->fmt.pix.pixelformat; ++ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) ++ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ ++ if (s->pixelformat == V4L2_PIX_FMT_HM12) ++ s->vb_bytes_per_frame = h * 720 * 3 / 2; ++ else ++ s->vb_bytes_per_frame = h * 720 * 2; ++ ++ mbus_fmt.width = cx->cxhdl.width = w; ++ mbus_fmt.height = cx->cxhdl.height = h; ++ mbus_fmt.code = V4L2_MBUS_FMT_FIXED; ++ v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt); ++ return cx18_g_fmt_vid_cap(file, fh, fmt); ++} ++ ++static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ int ret; ++ ++ /* ++ * Changing the Encoder's Raw VBI parameters won't have any effect ++ * if any analog capture is ongoing ++ */ ++ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) ++ return -EBUSY; ++ ++ /* ++ * Set the digitizer registers for raw active VBI. ++ * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid ++ * calling conditions ++ */ ++ ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi); ++ if (ret) ++ return ret; ++ ++ /* Store our new v4l2 (non-)sliced VBI state */ ++ cx->vbi.sliced_in->service_set = 0; ++ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ ++ return cx18_g_fmt_vbi_cap(file, fh, fmt); ++} ++ ++static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, ++ struct v4l2_format *fmt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ int ret; ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ ++ cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); ++ ++ /* ++ * Changing the Encoder's Raw VBI parameters won't have any effect ++ * if any analog capture is ongoing ++ */ ++ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) ++ return -EBUSY; ++ ++ /* ++ * Set the service_lines requested in the digitizer/slicer registers. ++ * Note, cx18_av_vbi() wipes some "impossible" service lines in the ++ * passed in fmt->fmt.sliced under valid calling conditions ++ */ ++ ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced); ++ if (ret) ++ return ret; ++ /* Store our current v4l2 sliced VBI settings */ ++ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; ++ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); ++ return 0; ++} ++ ++static int cx18_g_chip_ident(struct file *file, void *fh, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ int err = 0; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ switch (chip->match.type) { ++ case V4L2_CHIP_MATCH_HOST: ++ switch (chip->match.addr) { ++ case 0: ++ chip->ident = V4L2_IDENT_CX23418; ++ chip->revision = cx18_read_reg(cx, 0xC72028); ++ break; ++ case 1: ++ /* ++ * The A/V decoder is always present, but in the rare ++ * case that the card doesn't have analog, we don't ++ * use it. We find it w/o using the cx->sd_av pointer ++ */ ++ cx18_call_hw(cx, CX18_HW_418_AV, ++ core, g_chip_ident, chip); ++ break; ++ default: ++ /* ++ * Could return ident = V4L2_IDENT_UNKNOWN if we had ++ * other host chips at higher addresses, but we don't ++ */ ++ err = -EINVAL; /* per V4L2 spec */ ++ break; ++ } ++ break; ++ case V4L2_CHIP_MATCH_I2C_DRIVER: ++ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ ++ cx18_call_all(cx, core, g_chip_ident, chip); ++ break; ++ case V4L2_CHIP_MATCH_I2C_ADDR: ++ /* ++ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work ++ * to look if a chip is at the address with no driver. That's a ++ * dangerous thing to do with EEPROMs anyway. ++ */ ++ cx18_call_all(cx, core, g_chip_ident, chip); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ return err; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) ++{ ++ struct v4l2_dbg_register *regs = arg; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) ++ return -EINVAL; ++ ++ regs->size = 4; ++ if (cmd == VIDIOC_DBG_S_REGISTER) ++ cx18_write_enc(cx, regs->val, regs->reg); ++ else ++ regs->val = cx18_read_enc(cx, regs->reg); ++ return 0; ++} ++ ++static int cx18_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (v4l2_chip_match_host(®->match)) ++ return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); ++ /* FIXME - errors shouldn't be ignored */ ++ cx18_call_all(cx, core, g_register, reg); ++ return 0; ++} ++ ++static int cx18_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (v4l2_chip_match_host(®->match)) ++ return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); ++ /* FIXME - errors shouldn't be ignored */ ++ cx18_call_all(cx, core, s_register, reg); ++ return 0; ++} ++#endif ++ ++static int cx18_querycap(struct file *file, void *fh, ++ struct v4l2_capability *vcap) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); ++ strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); ++ snprintf(vcap->bus_info, sizeof(vcap->bus_info), ++ "PCI:%s", pci_name(cx->pci_dev)); ++ vcap->capabilities = cx->v4l2_cap; /* capabilities */ ++ if (id->type == CX18_ENC_STREAM_TYPE_YUV) ++ vcap->capabilities |= V4L2_CAP_STREAMING; ++ return 0; ++} ++ ++static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ return cx18_get_audio_input(cx, vin->index, vin); ++} ++ ++static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ vin->index = cx->audio_input; ++ return cx18_get_audio_input(cx, vin->index, vin); ++} ++ ++static int cx18_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (vout->index >= cx->nof_audio_inputs) ++ return -EINVAL; ++ cx->audio_input = vout->index; ++ cx18_audio_set_io(cx); ++ return 0; ++} ++ ++static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ /* set it to defaults from our table */ ++ return cx18_get_input(cx, vin->index, vin); ++} ++ ++static int cx18_cropcap(struct file *file, void *fh, ++ struct v4l2_cropcap *cropcap) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ cropcap->bounds.top = cropcap->bounds.left = 0; ++ cropcap->bounds.width = 720; ++ cropcap->bounds.height = cx->is_50hz ? 576 : 480; ++ cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; ++ cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; ++ cropcap->defrect = cropcap->bounds; ++ return 0; ++} ++ ++static int cx18_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n"); ++ return -EINVAL; ++} ++ ++static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n"); ++ return -EINVAL; ++} ++ ++static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_fmtdesc *fmt) ++{ ++ static const struct v4l2_fmtdesc formats[] = { ++ { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, ++ "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } ++ }, ++ { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, ++ "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } ++ }, ++ { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, ++ "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 } ++ }, ++ }; ++ ++ if (fmt->index > ARRAY_SIZE(formats) - 1) ++ return -EINVAL; ++ *fmt = formats[fmt->index]; ++ return 0; ++} ++ ++static int cx18_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ *i = cx->active_input; ++ return 0; ++} ++ ++int cx18_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ if (inp >= cx->nof_inputs) ++ return -EINVAL; ++ ++ if (inp == cx->active_input) { ++ CX18_DEBUG_INFO("Input unchanged\n"); ++ return 0; ++ } ++ ++ CX18_DEBUG_INFO("Changing input from %d to %d\n", ++ cx->active_input, inp); ++ ++ cx->active_input = inp; ++ /* Set the audio input to whatever is appropriate for the input type. */ ++ cx->audio_input = cx->card->video_inputs[inp].audio_index; ++ ++ /* prevent others from messing with the streams until ++ we're finished changing inputs. */ ++ cx18_mute(cx); ++ cx18_video_set_io(cx); ++ cx18_audio_set_io(cx); ++ cx18_unmute(cx); ++ return 0; ++} ++ ++static int cx18_g_frequency(struct file *file, void *fh, ++ struct v4l2_frequency *vf) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (vf->tuner != 0) ++ return -EINVAL; ++ ++ cx18_call_all(cx, tuner, g_frequency, vf); ++ return 0; ++} ++ ++int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ if (vf->tuner != 0) ++ return -EINVAL; ++ ++ cx18_mute(cx); ++ CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); ++ cx18_call_all(cx, tuner, s_frequency, vf); ++ cx18_unmute(cx); ++ return 0; ++} ++ ++static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ *std = cx->std; ++ return 0; ++} ++ ++int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ if ((*std & V4L2_STD_ALL) == 0) ++ return -EINVAL; ++ ++ if (*std == cx->std) ++ return 0; ++ ++ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || ++ atomic_read(&cx->ana_capturing) > 0) { ++ /* Switching standard would turn off the radio or mess ++ with already running streams, prevent that by ++ returning EBUSY. */ ++ return -EBUSY; ++ } ++ ++ cx->std = *std; ++ cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; ++ cx->is_50hz = !cx->is_60hz; ++ cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz); ++ cx->cxhdl.width = 720; ++ cx->cxhdl.height = cx->is_50hz ? 576 : 480; ++ cx->vbi.count = cx->is_50hz ? 18 : 12; ++ cx->vbi.start[0] = cx->is_50hz ? 6 : 10; ++ cx->vbi.start[1] = cx->is_50hz ? 318 : 273; ++ CX18_DEBUG_INFO("Switching standard to %llx.\n", ++ (unsigned long long) cx->std); ++ ++ /* Tuner */ ++ cx18_call_all(cx, core, s_std, cx->std); ++ return 0; ++} ++ ++static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ ++ if (vt->index != 0) ++ return -EINVAL; ++ ++ cx18_call_all(cx, tuner, s_tuner, vt); ++ return 0; ++} ++ ++static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ if (vt->index != 0) ++ return -EINVAL; ++ ++ cx18_call_all(cx, tuner, g_tuner, vt); ++ ++ if (vt->type == V4L2_TUNER_RADIO) ++ strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); ++ else ++ strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); ++ return 0; ++} ++ ++static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, ++ struct v4l2_sliced_vbi_cap *cap) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; ++ int f, l; ++ ++ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) ++ return -EINVAL; ++ ++ cap->service_set = 0; ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ if (valid_service_line(f, l, cx->is_50hz)) { ++ /* ++ * We can find all v4l2 supported vbi services ++ * for the standard, on a valid line for the std ++ */ ++ cap->service_lines[f][l] = set; ++ cap->service_set |= set; ++ } else ++ cap->service_lines[f][l] = 0; ++ } ++ } ++ for (f = 0; f < 3; f++) ++ cap->reserved[f] = 0; ++ return 0; ++} ++ ++static int _cx18_process_idx_data(struct cx18_buffer *buf, ++ struct v4l2_enc_idx *idx) ++{ ++ int consumed, remaining; ++ struct v4l2_enc_idx_entry *e_idx; ++ struct cx18_enc_idx_entry *e_buf; ++ ++ /* Frame type lookup: 1=I, 2=P, 4=B */ ++ const int mapping[8] = { ++ -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, ++ -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 ++ }; ++ ++ /* ++ * Assumption here is that a buf holds an integral number of ++ * struct cx18_enc_idx_entry objects and is properly aligned. ++ * This is enforced by the module options on IDX buffer sizes. ++ */ ++ remaining = buf->bytesused - buf->readpos; ++ consumed = 0; ++ e_idx = &idx->entry[idx->entries]; ++ e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos]; ++ ++ while (remaining >= sizeof(struct cx18_enc_idx_entry) && ++ idx->entries < V4L2_ENC_IDX_ENTRIES) { ++ ++ e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32) ++ | le32_to_cpu(e_buf->offset_low); ++ ++ e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32) ++ | le32_to_cpu(e_buf->pts_low); ++ ++ e_idx->length = le32_to_cpu(e_buf->length); ++ ++ e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7]; ++ ++ e_idx->reserved[0] = 0; ++ e_idx->reserved[1] = 0; ++ ++ idx->entries++; ++ e_idx = &idx->entry[idx->entries]; ++ e_buf++; ++ ++ remaining -= sizeof(struct cx18_enc_idx_entry); ++ consumed += sizeof(struct cx18_enc_idx_entry); ++ } ++ ++ /* Swallow any partial entries at the end, if there are any */ ++ if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry)) ++ consumed += remaining; ++ ++ buf->readpos += consumed; ++ return consumed; ++} ++ ++static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, ++ struct v4l2_enc_idx *idx) ++{ ++ if (s->type != CX18_ENC_STREAM_TYPE_IDX) ++ return -EINVAL; ++ ++ if (mdl->curr_buf == NULL) ++ mdl->curr_buf = list_first_entry(&mdl->buf_list, ++ struct cx18_buffer, list); ++ ++ if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { ++ /* ++ * For some reason we've exhausted the buffers, but the MDL ++ * object still said some data was unread. ++ * Fix that and bail out. ++ */ ++ mdl->readpos = mdl->bytesused; ++ return 0; ++ } ++ ++ list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { ++ ++ /* Skip any empty buffers in the MDL */ ++ if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) ++ continue; ++ ++ mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx); ++ ++ /* exit when MDL drained or request satisfied */ ++ if (idx->entries >= V4L2_ENC_IDX_ENTRIES || ++ mdl->curr_buf->readpos < mdl->curr_buf->bytesused || ++ mdl->readpos >= mdl->bytesused) ++ break; ++ } ++ return 0; ++} ++ ++static int cx18_g_enc_index(struct file *file, void *fh, ++ struct v4l2_enc_idx *idx) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ s32 tmp; ++ struct cx18_mdl *mdl; ++ ++ if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */ ++ return -EINVAL; ++ ++ /* Compute the best case number of entries we can buffer */ ++ tmp = s->buffers - ++ s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN; ++ if (tmp <= 0) ++ tmp = 1; ++ tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry); ++ ++ /* Fill out the header of the return structure */ ++ idx->entries = 0; ++ idx->entries_cap = tmp; ++ memset(idx->reserved, 0, sizeof(idx->reserved)); ++ ++ /* Pull IDX MDLs and buffers from q_full and populate the entries */ ++ do { ++ mdl = cx18_dequeue(s, &s->q_full); ++ if (mdl == NULL) /* No more IDX data right now */ ++ break; ++ ++ /* Extract the Index entry data from the MDL and buffers */ ++ cx18_process_idx_data(s, mdl, idx); ++ if (mdl->readpos < mdl->bytesused) { ++ /* We finished with data remaining, push the MDL back */ ++ cx18_push(s, mdl, &s->q_full); ++ break; ++ } ++ ++ /* We drained this MDL, schedule it to go to the firmware */ ++ cx18_enqueue(s, mdl, &s->q_free); ++ ++ } while (idx->entries < V4L2_ENC_IDX_ENTRIES); ++ ++ /* Tell the work handler to send free IDX MDLs to the firmware */ ++ cx18_stream_load_fw_queue(s); ++ return 0; ++} ++ ++static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id) ++{ ++ struct videobuf_queue *q = NULL; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ switch (s->vb_type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ q = &s->vbuf_q; ++ break; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ break; ++ default: ++ break; ++ } ++ return q; ++} ++ ++static int cx18_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ /* Start the hardware only if we're the video device */ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ if (id->type != CX18_ENC_STREAM_TYPE_YUV) ++ return -EINVAL; ++ ++ /* Establish a buffer timeout */ ++ mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); ++ ++ return videobuf_streamon(cx18_vb_queue(id)); ++} ++ ++static int cx18_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ /* Start the hardware only if we're the video device */ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ if (id->type != CX18_ENC_STREAM_TYPE_YUV) ++ return -EINVAL; ++ ++ return videobuf_streamoff(cx18_vb_queue(id)); ++} ++ ++static int cx18_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *rb) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ return videobuf_reqbufs(cx18_vb_queue(id), rb); ++} ++ ++static int cx18_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *b) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ return videobuf_querybuf(cx18_vb_queue(id), b); ++} ++ ++static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ return videobuf_qbuf(cx18_vb_queue(id), b); ++} ++ ++static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct cx18_open_id *id = file->private_data; ++ struct cx18 *cx = id->cx; ++ struct cx18_stream *s = &cx->streams[id->type]; ++ ++ if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK); ++} ++ ++static int cx18_encoder_cmd(struct file *file, void *fh, ++ struct v4l2_encoder_cmd *enc) ++{ ++ struct cx18_open_id *id = fh2id(fh); ++ struct cx18 *cx = id->cx; ++ u32 h; ++ ++ switch (enc->cmd) { ++ case V4L2_ENC_CMD_START: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); ++ enc->flags = 0; ++ return cx18_start_capture(id); ++ ++ case V4L2_ENC_CMD_STOP: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); ++ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; ++ cx18_stop_capture(id, ++ enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); ++ break; ++ ++ case V4L2_ENC_CMD_PAUSE: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); ++ enc->flags = 0; ++ if (!atomic_read(&cx->ana_capturing)) ++ return -EPERM; ++ if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) ++ return 0; ++ h = cx18_find_handle(cx); ++ if (h == CX18_INVALID_TASK_HANDLE) { ++ CX18_ERR("Can't find valid task handle for " ++ "V4L2_ENC_CMD_PAUSE\n"); ++ return -EBADFD; ++ } ++ cx18_mute(cx); ++ cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); ++ break; ++ ++ case V4L2_ENC_CMD_RESUME: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); ++ enc->flags = 0; ++ if (!atomic_read(&cx->ana_capturing)) ++ return -EPERM; ++ if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) ++ return 0; ++ h = cx18_find_handle(cx); ++ if (h == CX18_INVALID_TASK_HANDLE) { ++ CX18_ERR("Can't find valid task handle for " ++ "V4L2_ENC_CMD_RESUME\n"); ++ return -EBADFD; ++ } ++ cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); ++ cx18_unmute(cx); ++ break; ++ ++ default: ++ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int cx18_try_encoder_cmd(struct file *file, void *fh, ++ struct v4l2_encoder_cmd *enc) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ switch (enc->cmd) { ++ case V4L2_ENC_CMD_START: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); ++ enc->flags = 0; ++ break; ++ ++ case V4L2_ENC_CMD_STOP: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); ++ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; ++ break; ++ ++ case V4L2_ENC_CMD_PAUSE: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); ++ enc->flags = 0; ++ break; ++ ++ case V4L2_ENC_CMD_RESUME: ++ CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); ++ enc->flags = 0; ++ break; ++ ++ default: ++ CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int cx18_log_status(struct file *file, void *fh) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ struct v4l2_input vidin; ++ struct v4l2_audio audin; ++ int i; ++ ++ CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name); ++ if (cx->hw_flags & CX18_HW_TVEEPROM) { ++ struct tveeprom tv; ++ ++ cx18_read_eeprom(cx, &tv); ++ } ++ cx18_call_all(cx, core, log_status); ++ cx18_get_input(cx, cx->active_input, &vidin); ++ cx18_get_audio_input(cx, cx->audio_input, &audin); ++ CX18_INFO("Video Input: %s\n", vidin.name); ++ CX18_INFO("Audio Input: %s\n", audin.name); ++ mutex_lock(&cx->gpio_lock); ++ CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", ++ cx->gpio_dir, cx->gpio_val); ++ mutex_unlock(&cx->gpio_lock); ++ CX18_INFO("Tuner: %s\n", ++ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); ++ v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name); ++ CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); ++ for (i = 0; i < CX18_MAX_STREAMS; i++) { ++ struct cx18_stream *s = &cx->streams[i]; ++ ++ if (s->video_dev == NULL || s->buffers == 0) ++ continue; ++ CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", ++ s->name, s->s_flags, ++ atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100 ++ / s->buffers, ++ (s->buffers * s->buf_size) / 1024, s->buffers); ++ } ++ CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", ++ (long long)cx->mpg_data_received, ++ (long long)cx->vbi_data_inserted); ++ return 0; ++} ++ ++static long cx18_default(struct file *file, void *fh, bool valid_prio, ++ int cmd, void *arg) ++{ ++ struct cx18 *cx = fh2id(fh)->cx; ++ ++ switch (cmd) { ++ case VIDIOC_INT_RESET: { ++ u32 val = *(u32 *)arg; ++ ++ if ((val == 0) || (val & 0x01)) ++ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset, ++ (u32) CX18_GPIO_RESET_Z8F0811); ++ break; ++ } ++ ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops cx18_ioctl_ops = { ++ .vidioc_querycap = cx18_querycap, ++ .vidioc_s_audio = cx18_s_audio, ++ .vidioc_g_audio = cx18_g_audio, ++ .vidioc_enumaudio = cx18_enumaudio, ++ .vidioc_enum_input = cx18_enum_input, ++ .vidioc_cropcap = cx18_cropcap, ++ .vidioc_s_crop = cx18_s_crop, ++ .vidioc_g_crop = cx18_g_crop, ++ .vidioc_g_input = cx18_g_input, ++ .vidioc_s_input = cx18_s_input, ++ .vidioc_g_frequency = cx18_g_frequency, ++ .vidioc_s_frequency = cx18_s_frequency, ++ .vidioc_s_tuner = cx18_s_tuner, ++ .vidioc_g_tuner = cx18_g_tuner, ++ .vidioc_g_enc_index = cx18_g_enc_index, ++ .vidioc_g_std = cx18_g_std, ++ .vidioc_s_std = cx18_s_std, ++ .vidioc_log_status = cx18_log_status, ++ .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap, ++ .vidioc_encoder_cmd = cx18_encoder_cmd, ++ .vidioc_try_encoder_cmd = cx18_try_encoder_cmd, ++ .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap, ++ .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap, ++ .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap, ++ .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap, ++ .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap, ++ .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap, ++ .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap, ++ .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap, ++ .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap, ++ .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap, ++ .vidioc_g_chip_ident = cx18_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = cx18_g_register, ++ .vidioc_s_register = cx18_s_register, ++#endif ++ .vidioc_default = cx18_default, ++ .vidioc_streamon = cx18_streamon, ++ .vidioc_streamoff = cx18_streamoff, ++ .vidioc_reqbufs = cx18_reqbufs, ++ .vidioc_querybuf = cx18_querybuf, ++ .vidioc_qbuf = cx18_qbuf, ++ .vidioc_dqbuf = cx18_dqbuf, ++}; ++ ++void cx18_set_funcs(struct video_device *vdev) ++{ ++ vdev->ioctl_ops = &cx18_ioctl_ops; ++} +diff --git a/drivers/media/pci/cx18/cx18-ioctl.h b/drivers/media/pci/cx18/cx18-ioctl.h +new file mode 100644 +index 0000000..2f9dd59 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-ioctl.h +@@ -0,0 +1,31 @@ ++/* ++ * cx18 ioctl system call ++ * ++ * Derived from ivtv-ioctl.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++u16 cx18_service2vbi(int type); ++void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); ++u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt); ++void cx18_set_funcs(struct video_device *vdev); ++int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std); ++int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); ++int cx18_s_input(struct file *file, void *fh, unsigned int inp); +diff --git a/drivers/media/pci/cx18/cx18-irq.c b/drivers/media/pci/cx18/cx18-irq.c +new file mode 100644 +index 0000000..80edfe9 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-irq.c +@@ -0,0 +1,81 @@ ++/* ++ * cx18 interrupt handling ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-irq.h" ++#include "cx18-mailbox.h" ++#include "cx18-scb.h" ++ ++static void xpu_ack(struct cx18 *cx, u32 sw2) ++{ ++ if (sw2 & IRQ_CPU_TO_EPU_ACK) ++ wake_up(&cx->mb_cpu_waitq); ++ if (sw2 & IRQ_APU_TO_EPU_ACK) ++ wake_up(&cx->mb_apu_waitq); ++} ++ ++static void epu_cmd(struct cx18 *cx, u32 sw1) ++{ ++ if (sw1 & IRQ_CPU_TO_EPU) ++ cx18_api_epu_cmd_irq(cx, CPU); ++ if (sw1 & IRQ_APU_TO_EPU) ++ cx18_api_epu_cmd_irq(cx, APU); ++} ++ ++irqreturn_t cx18_irq_handler(int irq, void *dev_id) ++{ ++ struct cx18 *cx = (struct cx18 *)dev_id; ++ u32 sw1, sw2, hw2; ++ ++ sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask; ++ sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask; ++ hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask; ++ ++ if (sw1) ++ cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1); ++ if (sw2) ++ cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2); ++ if (hw2) ++ cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); ++ ++ if (sw1 || sw2 || hw2) ++ CX18_DEBUG_HI_IRQ("received interrupts " ++ "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); ++ ++ /* ++ * SW1 responses have to happen first. The sending XPU times out the ++ * incoming mailboxes on us rather rapidly. ++ */ ++ if (sw1) ++ epu_cmd(cx, sw1); ++ ++ /* To do: interrupt-based I2C handling ++ if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { ++ } ++ */ ++ ++ if (sw2) ++ xpu_ack(cx, sw2); ++ ++ return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; ++} +diff --git a/drivers/media/pci/cx18/cx18-irq.h b/drivers/media/pci/cx18/cx18-irq.h +new file mode 100644 +index 0000000..30e7eaf +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-irq.h +@@ -0,0 +1,35 @@ ++/* ++ * cx18 interrupt handling ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#define HW2_I2C1_INT (1 << 22) ++#define HW2_I2C2_INT (1 << 23) ++#define HW2_INT_CLR_STATUS 0xc730c4 ++#define HW2_INT_MASK5_PCI 0xc730e4 ++#define SW1_INT_SET 0xc73100 ++#define SW1_INT_STATUS 0xc73104 ++#define SW1_INT_ENABLE_PCI 0xc7311c ++#define SW2_INT_SET 0xc73140 ++#define SW2_INT_STATUS 0xc73144 ++#define SW2_INT_ENABLE_CPU 0xc73158 ++#define SW2_INT_ENABLE_PCI 0xc7315c ++ ++irqreturn_t cx18_irq_handler(int irq, void *dev_id); +diff --git a/drivers/media/pci/cx18/cx18-mailbox.c b/drivers/media/pci/cx18/cx18-mailbox.c +new file mode 100644 +index 0000000..eabf00c +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-mailbox.c +@@ -0,0 +1,870 @@ ++/* ++ * cx18 mailbox functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-scb.h" ++#include "cx18-irq.h" ++#include "cx18-mailbox.h" ++#include "cx18-queue.h" ++#include "cx18-streams.h" ++#include "cx18-alsa-pcm.h" /* FIXME make configurable */ ++ ++static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; ++ ++#define API_FAST (1 << 2) /* Short timeout */ ++#define API_SLOW (1 << 3) /* Additional 300ms timeout */ ++ ++struct cx18_api_info { ++ u32 cmd; ++ u8 flags; /* Flags, see above */ ++ u8 rpu; /* Processing unit */ ++ const char *name; /* The name of the command */ ++}; ++ ++#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } ++ ++static const struct cx18_api_info api_info[] = { ++ /* MPEG encoder API */ ++ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), ++ API_ENTRY(CPU, CX18_EPU_DEBUG, 0), ++ API_ENTRY(CPU, CX18_CREATE_TASK, 0), ++ API_ENTRY(CPU, CX18_DESTROY_TASK, 0), ++ API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), ++ API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), ++ API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), ++ API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), ++ API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), ++ API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), ++ API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0), ++ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), ++ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), ++ API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), ++ API_ENTRY(APU, CX18_APU_START, 0), ++ API_ENTRY(APU, CX18_APU_STOP, 0), ++ API_ENTRY(APU, CX18_APU_RESETAI, 0), ++ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), ++ API_ENTRY(0, 0, 0), ++}; ++ ++static const struct cx18_api_info *find_api_info(u32 cmd) ++{ ++ int i; ++ ++ for (i = 0; api_info[i].cmd; i++) ++ if (api_info[i].cmd == cmd) ++ return &api_info[i]; ++ return NULL; ++} ++ ++/* Call with buf of n*11+1 bytes */ ++static char *u32arr2hex(u32 data[], int n, char *buf) ++{ ++ char *p; ++ int i; ++ ++ for (i = 0, p = buf; i < n; i++, p += 11) { ++ /* kernel snprintf() appends '\0' always */ ++ snprintf(p, 12, " %#010x", data[i]); ++ } ++ *p = '\0'; ++ return buf; ++} ++ ++static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) ++{ ++ char argstr[MAX_MB_ARGUMENTS*11+1]; ++ ++ if (!(cx18_debug & CX18_DBGFLG_API)) ++ return; ++ ++ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s" ++ "\n", name, mb->request, mb->ack, mb->cmd, mb->error, ++ u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr)); ++} ++ ++ ++/* ++ * Functions that run in a work_queue work handling context ++ */ ++ ++static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) ++{ ++ struct cx18_buffer *buf; ++ ++ if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0) ++ return; ++ ++ /* We ignore mdl and buf readpos accounting here - it doesn't matter */ ++ ++ /* The likely case */ ++ if (list_is_singular(&mdl->buf_list)) { ++ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, ++ list); ++ if (buf->bytesused) ++ dvb_dmx_swfilter(&s->dvb->demux, ++ buf->buf, buf->bytesused); ++ return; ++ } ++ ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ if (buf->bytesused == 0) ++ break; ++ dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused); ++ } ++} ++ ++static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ struct cx18_videobuf_buffer *vb_buf; ++ struct cx18_buffer *buf; ++ u8 *p; ++ u32 offset = 0; ++ int dispatch = 0; ++ ++ if (mdl->bytesused == 0) ++ return; ++ ++ /* Acquire a videobuf buffer, clone to and and release it */ ++ spin_lock(&s->vb_lock); ++ if (list_empty(&s->vb_capture)) ++ goto out; ++ ++ vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer, ++ vb.queue); ++ ++ p = videobuf_to_vmalloc(&vb_buf->vb); ++ if (!p) ++ goto out; ++ ++ offset = vb_buf->bytes_used; ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ if (buf->bytesused == 0) ++ break; ++ ++ if ((offset + buf->bytesused) <= vb_buf->vb.bsize) { ++ memcpy(p + offset, buf->buf, buf->bytesused); ++ offset += buf->bytesused; ++ vb_buf->bytes_used += buf->bytesused; ++ } ++ } ++ ++ /* If we've filled the buffer as per the callers res then dispatch it */ ++ if (vb_buf->bytes_used >= s->vb_bytes_per_frame) { ++ dispatch = 1; ++ vb_buf->bytes_used = 0; ++ } ++ ++ if (dispatch) { ++ vb_buf->vb.ts = ktime_to_timeval(ktime_get()); ++ list_del(&vb_buf->vb.queue); ++ vb_buf->vb.state = VIDEOBUF_DONE; ++ wake_up(&vb_buf->vb.done); ++ } ++ ++ mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); ++ ++out: ++ spin_unlock(&s->vb_lock); ++} ++ ++static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ struct cx18_buffer *buf; ++ ++ if (mdl->bytesused == 0) ++ return; ++ ++ /* We ignore mdl and buf readpos accounting here - it doesn't matter */ ++ ++ /* The likely case */ ++ if (list_is_singular(&mdl->buf_list)) { ++ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, ++ list); ++ if (buf->bytesused) ++ cx->pcm_announce_callback(cx->alsa, buf->buf, ++ buf->bytesused); ++ return; ++ } ++ ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ if (buf->bytesused == 0) ++ break; ++ cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused); ++ } ++} ++ ++static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ u32 handle, mdl_ack_count, id; ++ struct cx18_mailbox *mb; ++ struct cx18_mdl_ack *mdl_ack; ++ struct cx18_stream *s; ++ struct cx18_mdl *mdl; ++ int i; ++ ++ mb = &order->mb; ++ handle = mb->args[0]; ++ s = cx18_handle_to_stream(cx, handle); ++ ++ if (s == NULL) { ++ CX18_WARN("Got DMA done notification for unknown/inactive" ++ " handle %d, %s mailbox seq no %d\n", handle, ++ (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? ++ "stale" : "good", mb->request); ++ return; ++ } ++ ++ mdl_ack_count = mb->args[2]; ++ mdl_ack = order->mdl_ack; ++ for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { ++ id = mdl_ack->id; ++ /* ++ * Simple integrity check for processing a stale (and possibly ++ * inconsistent mailbox): make sure the MDL id is in the ++ * valid range for the stream. ++ * ++ * We go through the trouble of dealing with stale mailboxes ++ * because most of the time, the mailbox data is still valid and ++ * unchanged (and in practice the firmware ping-pongs the ++ * two mdl_ack buffers so mdl_acks are not stale). ++ * ++ * There are occasions when we get a half changed mailbox, ++ * which this check catches for a handle & id mismatch. If the ++ * handle and id do correspond, the worst case is that we ++ * completely lost the old MDL, but pick up the new MDL ++ * early (but the new mdl_ack is guaranteed to be good in this ++ * case as the firmware wouldn't point us to a new mdl_ack until ++ * it's filled in). ++ * ++ * cx18_queue_get_mdl() will detect the lost MDLs ++ * and send them back to q_free for fw rotation eventually. ++ */ ++ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && ++ !(id >= s->mdl_base_idx && ++ id < (s->mdl_base_idx + s->buffers))) { ++ CX18_WARN("Fell behind! Ignoring stale mailbox with " ++ " inconsistent data. Lost MDL for mailbox " ++ "seq no %d\n", mb->request); ++ break; ++ } ++ mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); ++ ++ CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); ++ if (mdl == NULL) { ++ CX18_WARN("Could not find MDL %d for stream %s\n", ++ id, s->name); ++ continue; ++ } ++ ++ CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", ++ s->name, mdl->bytesused); ++ ++ if (s->type == CX18_ENC_STREAM_TYPE_TS) { ++ cx18_mdl_send_to_dvb(s, mdl); ++ cx18_enqueue(s, mdl, &s->q_free); ++ } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) { ++ /* Pass the data to cx18-alsa */ ++ if (cx->pcm_announce_callback != NULL) { ++ cx18_mdl_send_to_alsa(cx, s, mdl); ++ cx18_enqueue(s, mdl, &s->q_free); ++ } else { ++ cx18_enqueue(s, mdl, &s->q_full); ++ } ++ } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) { ++ cx18_mdl_send_to_videobuf(s, mdl); ++ cx18_enqueue(s, mdl, &s->q_free); ++ } else { ++ cx18_enqueue(s, mdl, &s->q_full); ++ if (s->type == CX18_ENC_STREAM_TYPE_IDX) ++ cx18_stream_rotate_idx_mdls(cx); ++ } ++ } ++ /* Put as many MDLs as possible back into fw use */ ++ cx18_stream_load_fw_queue(s); ++ ++ wake_up(&cx->dma_waitq); ++ if (s->id != -1) ++ wake_up(&s->waitq); ++} ++ ++static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ char *p; ++ char *str = order->str; ++ ++ CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str); ++ p = strchr(str, '.'); ++ if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) ++ CX18_INFO("FW version: %s\n", p - 1); ++} ++ ++static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ switch (order->rpu) { ++ case CPU: ++ { ++ switch (order->mb.cmd) { ++ case CX18_EPU_DMA_DONE: ++ epu_dma_done(cx, order); ++ break; ++ case CX18_EPU_DEBUG: ++ epu_debug(cx, order); ++ break; ++ default: ++ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", ++ order->mb.cmd); ++ break; ++ } ++ break; ++ } ++ case APU: ++ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", ++ order->mb.cmd); ++ break; ++ default: ++ break; ++ } ++} ++ ++static ++void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ atomic_set(&order->pending, 0); ++} ++ ++void cx18_in_work_handler(struct work_struct *work) ++{ ++ struct cx18_in_work_order *order = ++ container_of(work, struct cx18_in_work_order, work); ++ struct cx18 *cx = order->cx; ++ epu_cmd(cx, order); ++ free_in_work_order(cx, order); ++} ++ ++ ++/* ++ * Functions that run in an interrupt handling context ++ */ ++ ++static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ struct cx18_mailbox __iomem *ack_mb; ++ u32 ack_irq, req; ++ ++ switch (order->rpu) { ++ case APU: ++ ack_irq = IRQ_EPU_TO_APU_ACK; ++ ack_mb = &cx->scb->apu2epu_mb; ++ break; ++ case CPU: ++ ack_irq = IRQ_EPU_TO_CPU_ACK; ++ ack_mb = &cx->scb->cpu2epu_mb; ++ break; ++ default: ++ CX18_WARN("Unhandled RPU (%d) for command %x ack\n", ++ order->rpu, order->mb.cmd); ++ return; ++ } ++ ++ req = order->mb.request; ++ /* Don't ack if the RPU has gotten impatient and timed us out */ ++ if (req != cx18_readl(cx, &ack_mb->request) || ++ req == cx18_readl(cx, &ack_mb->ack)) { ++ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " ++ "incoming %s to EPU mailbox (sequence no. %u) " ++ "while processing\n", ++ rpu_str[order->rpu], rpu_str[order->rpu], req); ++ order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; ++ return; ++ } ++ cx18_writel(cx, req, &ack_mb->ack); ++ cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); ++ return; ++} ++ ++static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ u32 handle, mdl_ack_offset, mdl_ack_count; ++ struct cx18_mailbox *mb; ++ int i; ++ ++ mb = &order->mb; ++ handle = mb->args[0]; ++ mdl_ack_offset = mb->args[1]; ++ mdl_ack_count = mb->args[2]; ++ ++ if (handle == CX18_INVALID_TASK_HANDLE || ++ mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { ++ if ((order->flags & CX18_F_EWO_MB_STALE) == 0) ++ mb_ack_irq(cx, order); ++ return -1; ++ } ++ ++ for (i = 0; i < sizeof(struct cx18_mdl_ack) * mdl_ack_count; i += sizeof(u32)) ++ ((u32 *)order->mdl_ack)[i / sizeof(u32)] = ++ cx18_readl(cx, cx->enc_mem + mdl_ack_offset + i); ++ ++ if ((order->flags & CX18_F_EWO_MB_STALE) == 0) ++ mb_ack_irq(cx, order); ++ return 1; ++} ++ ++static ++int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ u32 str_offset; ++ char *str = order->str; ++ ++ str[0] = '\0'; ++ str_offset = order->mb.args[1]; ++ if (str_offset) { ++ cx18_setup_page(cx, str_offset); ++ cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252); ++ str[252] = '\0'; ++ cx18_setup_page(cx, SCB_OFFSET); ++ } ++ ++ if ((order->flags & CX18_F_EWO_MB_STALE) == 0) ++ mb_ack_irq(cx, order); ++ ++ return str_offset ? 1 : 0; ++} ++ ++static inline ++int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order) ++{ ++ int ret = -1; ++ ++ switch (order->rpu) { ++ case CPU: ++ { ++ switch (order->mb.cmd) { ++ case CX18_EPU_DMA_DONE: ++ ret = epu_dma_done_irq(cx, order); ++ break; ++ case CX18_EPU_DEBUG: ++ ret = epu_debug_irq(cx, order); ++ break; ++ default: ++ CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", ++ order->mb.cmd); ++ break; ++ } ++ break; ++ } ++ case APU: ++ CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", ++ order->mb.cmd); ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++ ++static inline ++struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx) ++{ ++ int i; ++ struct cx18_in_work_order *order = NULL; ++ ++ for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { ++ /* ++ * We only need "pending" atomic to inspect its contents, ++ * and need not do a check and set because: ++ * 1. Any work handler thread only clears "pending" and only ++ * on one, particular work order at a time, per handler thread. ++ * 2. "pending" is only set here, and we're serialized because ++ * we're called in an IRQ handler context. ++ */ ++ if (atomic_read(&cx->in_work_order[i].pending) == 0) { ++ order = &cx->in_work_order[i]; ++ atomic_set(&order->pending, 1); ++ break; ++ } ++ } ++ return order; ++} ++ ++void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) ++{ ++ struct cx18_mailbox __iomem *mb; ++ struct cx18_mailbox *order_mb; ++ struct cx18_in_work_order *order; ++ int submit; ++ int i; ++ ++ switch (rpu) { ++ case CPU: ++ mb = &cx->scb->cpu2epu_mb; ++ break; ++ case APU: ++ mb = &cx->scb->apu2epu_mb; ++ break; ++ default: ++ return; ++ } ++ ++ order = alloc_in_work_order_irq(cx); ++ if (order == NULL) { ++ CX18_WARN("Unable to find blank work order form to schedule " ++ "incoming mailbox command processing\n"); ++ return; ++ } ++ ++ order->flags = 0; ++ order->rpu = rpu; ++ order_mb = &order->mb; ++ ++ /* mb->cmd and mb->args[0] through mb->args[2] */ ++ for (i = 0; i < 4; i++) ++ (&order_mb->cmd)[i] = cx18_readl(cx, &mb->cmd + i); ++ ++ /* mb->request and mb->ack. N.B. we want to read mb->ack last */ ++ for (i = 0; i < 2; i++) ++ (&order_mb->request)[i] = cx18_readl(cx, &mb->request + i); ++ ++ if (order_mb->request == order_mb->ack) { ++ CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " ++ "incoming %s to EPU mailbox (sequence no. %u)" ++ "\n", ++ rpu_str[rpu], rpu_str[rpu], order_mb->request); ++ if (cx18_debug & CX18_DBGFLG_WARN) ++ dump_mb(cx, order_mb, "incoming"); ++ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; ++ } ++ ++ /* ++ * Individual EPU command processing is responsible for ack-ing ++ * a non-stale mailbox as soon as possible ++ */ ++ submit = epu_cmd_irq(cx, order); ++ if (submit > 0) { ++ queue_work(cx->in_work_queue, &order->work); ++ } ++} ++ ++ ++/* ++ * Functions called from a non-interrupt, non work_queue context ++ */ ++ ++static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) ++{ ++ const struct cx18_api_info *info = find_api_info(cmd); ++ u32 irq, req, ack, err; ++ struct cx18_mailbox __iomem *mb; ++ wait_queue_head_t *waitq; ++ struct mutex *mb_lock; ++ unsigned long int t0, timeout, ret; ++ int i; ++ char argstr[MAX_MB_ARGUMENTS*11+1]; ++ DEFINE_WAIT(w); ++ ++ if (info == NULL) { ++ CX18_WARN("unknown cmd %x\n", cmd); ++ return -EINVAL; ++ } ++ ++ if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */ ++ if (cmd == CX18_CPU_DE_SET_MDL) { ++ if (cx18_debug & CX18_DBGFLG_HIGHVOL) ++ CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n", ++ info->name, cmd, ++ u32arr2hex(data, args, argstr)); ++ } else ++ CX18_DEBUG_API("%s\tcmd %#010x args%s\n", ++ info->name, cmd, ++ u32arr2hex(data, args, argstr)); ++ } ++ ++ switch (info->rpu) { ++ case APU: ++ waitq = &cx->mb_apu_waitq; ++ mb_lock = &cx->epu2apu_mb_lock; ++ irq = IRQ_EPU_TO_APU; ++ mb = &cx->scb->epu2apu_mb; ++ break; ++ case CPU: ++ waitq = &cx->mb_cpu_waitq; ++ mb_lock = &cx->epu2cpu_mb_lock; ++ irq = IRQ_EPU_TO_CPU; ++ mb = &cx->scb->epu2cpu_mb; ++ break; ++ default: ++ CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); ++ return -EINVAL; ++ } ++ ++ mutex_lock(mb_lock); ++ /* ++ * Wait for an in-use mailbox to complete ++ * ++ * If the XPU is responding with Ack's, the mailbox shouldn't be in ++ * a busy state, since we serialize access to it on our end. ++ * ++ * If the wait for ack after sending a previous command was interrupted ++ * by a signal, we may get here and find a busy mailbox. After waiting, ++ * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. ++ */ ++ req = cx18_readl(cx, &mb->request); ++ timeout = msecs_to_jiffies(10); ++ ret = wait_event_timeout(*waitq, ++ (ack = cx18_readl(cx, &mb->ack)) == req, ++ timeout); ++ if (req != ack) { ++ /* waited long enough, make the mbox "not busy" from our end */ ++ cx18_writel(cx, req, &mb->ack); ++ CX18_ERR("mbox was found stuck busy when setting up for %s; " ++ "clearing busy and trying to proceed\n", info->name); ++ } else if (ret != timeout) ++ CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n", ++ jiffies_to_msecs(timeout-ret)); ++ ++ /* Build the outgoing mailbox */ ++ req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; ++ ++ cx18_writel(cx, cmd, &mb->cmd); ++ for (i = 0; i < args; i++) ++ cx18_writel(cx, data[i], &mb->args[i]); ++ cx18_writel(cx, 0, &mb->error); ++ cx18_writel(cx, req, &mb->request); ++ cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ ++ ++ /* ++ * Notify the XPU and wait for it to send an Ack back ++ */ ++ timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20); ++ ++ CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", ++ irq, info->name); ++ ++ /* So we don't miss the wakeup, prepare to wait before notifying fw */ ++ prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE); ++ cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); ++ ++ t0 = jiffies; ++ ack = cx18_readl(cx, &mb->ack); ++ if (ack != req) { ++ schedule_timeout(timeout); ++ ret = jiffies - t0; ++ ack = cx18_readl(cx, &mb->ack); ++ } else { ++ ret = jiffies - t0; ++ } ++ ++ finish_wait(waitq, &w); ++ ++ if (req != ack) { ++ mutex_unlock(mb_lock); ++ if (ret >= timeout) { ++ /* Timed out */ ++ CX18_DEBUG_WARN("sending %s timed out waiting %d msecs " ++ "for RPU acknowledgement\n", ++ info->name, jiffies_to_msecs(ret)); ++ } else { ++ CX18_DEBUG_WARN("woken up before mailbox ack was ready " ++ "after submitting %s to RPU. only " ++ "waited %d msecs on req %u but awakened" ++ " with unmatched ack %u\n", ++ info->name, ++ jiffies_to_msecs(ret), ++ req, ack); ++ } ++ return -EINVAL; ++ } ++ ++ if (ret >= timeout) ++ CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment " ++ "sending %s; timed out waiting %d msecs\n", ++ info->name, jiffies_to_msecs(ret)); ++ else ++ CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", ++ jiffies_to_msecs(ret), info->name); ++ ++ /* Collect data returned by the XPU */ ++ for (i = 0; i < MAX_MB_ARGUMENTS; i++) ++ data[i] = cx18_readl(cx, &mb->args[i]); ++ err = cx18_readl(cx, &mb->error); ++ mutex_unlock(mb_lock); ++ ++ /* ++ * Wait for XPU to perform extra actions for the caller in some cases. ++ * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs ++ * back in a burst shortly thereafter ++ */ ++ if (info->flags & API_SLOW) ++ cx18_msleep_timeout(300, 0); ++ ++ if (err) ++ CX18_DEBUG_API("mailbox error %08x for command %s\n", err, ++ info->name); ++ return err ? -EIO : 0; ++} ++ ++int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) ++{ ++ return cx18_api_call(cx, cmd, args, data); ++} ++ ++static int cx18_set_filter_param(struct cx18_stream *s) ++{ ++ struct cx18 *cx = s->cx; ++ u32 mode; ++ int ret; ++ ++ mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); ++ ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, ++ s->handle, 1, mode, cx->spatial_strength); ++ mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); ++ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, ++ s->handle, 0, mode, cx->temporal_strength); ++ ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, ++ s->handle, 2, cx->filter_mode >> 2, 0); ++ return ret; ++} ++ ++int cx18_api_func(void *priv, u32 cmd, int in, int out, ++ u32 data[CX2341X_MBOX_MAX_DATA]) ++{ ++ struct cx18_stream *s = priv; ++ struct cx18 *cx = s->cx; ++ ++ switch (cmd) { ++ case CX2341X_ENC_SET_OUTPUT_PORT: ++ return 0; ++ case CX2341X_ENC_SET_FRAME_RATE: ++ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, ++ s->handle, 0, 0, 0, 0, data[0]); ++ case CX2341X_ENC_SET_FRAME_SIZE: ++ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, ++ s->handle, data[1], data[0]); ++ case CX2341X_ENC_SET_STREAM_TYPE: ++ return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, ++ s->handle, data[0]); ++ case CX2341X_ENC_SET_ASPECT_RATIO: ++ return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, ++ s->handle, data[0]); ++ ++ case CX2341X_ENC_SET_GOP_PROPERTIES: ++ return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, ++ s->handle, data[0], data[1]); ++ case CX2341X_ENC_SET_GOP_CLOSURE: ++ return 0; ++ case CX2341X_ENC_SET_AUDIO_PROPERTIES: ++ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, ++ s->handle, data[0]); ++ case CX2341X_ENC_MUTE_AUDIO: ++ return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, ++ s->handle, data[0]); ++ case CX2341X_ENC_SET_BIT_RATE: ++ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, ++ s->handle, data[0], data[1], data[2], data[3]); ++ case CX2341X_ENC_MUTE_VIDEO: ++ return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, ++ s->handle, data[0]); ++ case CX2341X_ENC_SET_FRAME_DROP_RATE: ++ return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, ++ s->handle, data[0]); ++ case CX2341X_ENC_MISC: ++ return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, ++ s->handle, data[0], data[1], data[2]); ++ case CX2341X_ENC_SET_DNR_FILTER_MODE: ++ cx->filter_mode = (data[0] & 3) | (data[1] << 2); ++ return cx18_set_filter_param(s); ++ case CX2341X_ENC_SET_DNR_FILTER_PROPS: ++ cx->spatial_strength = data[0]; ++ cx->temporal_strength = data[1]; ++ return cx18_set_filter_param(s); ++ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: ++ return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, ++ s->handle, data[0], data[1]); ++ case CX2341X_ENC_SET_CORING_LEVELS: ++ return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, ++ s->handle, data[0], data[1], data[2], data[3]); ++ } ++ CX18_WARN("Unknown cmd %x\n", cmd); ++ return 0; ++} ++ ++int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], ++ u32 cmd, int args, ...) ++{ ++ va_list ap; ++ int i; ++ ++ va_start(ap, args); ++ for (i = 0; i < args; i++) ++ data[i] = va_arg(ap, u32); ++ va_end(ap); ++ return cx18_api(cx, cmd, args, data); ++} ++ ++int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) ++{ ++ u32 data[MAX_MB_ARGUMENTS]; ++ va_list ap; ++ int i; ++ ++ if (cx == NULL) { ++ CX18_ERR("cx == NULL (cmd=%x)\n", cmd); ++ return 0; ++ } ++ if (args > MAX_MB_ARGUMENTS) { ++ CX18_ERR("args too big (cmd=%x)\n", cmd); ++ args = MAX_MB_ARGUMENTS; ++ } ++ va_start(ap, args); ++ for (i = 0; i < args; i++) ++ data[i] = va_arg(ap, u32); ++ va_end(ap); ++ return cx18_api(cx, cmd, args, data); ++} +diff --git a/drivers/media/pci/cx18/cx18-mailbox.h b/drivers/media/pci/cx18/cx18-mailbox.h +new file mode 100644 +index 0000000..b63fdfa +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-mailbox.h +@@ -0,0 +1,95 @@ ++/* ++ * cx18 mailbox functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef _CX18_MAILBOX_H_ ++#define _CX18_MAILBOX_H_ ++ ++/* mailbox max args */ ++#define MAX_MB_ARGUMENTS 6 ++/* compatibility, should be same as the define in cx2341x.h */ ++#define CX2341X_MBOX_MAX_DATA 16 ++ ++#define MB_RESERVED_HANDLE_0 0 ++#define MB_RESERVED_HANDLE_1 0xFFFFFFFF ++ ++#define APU 0 ++#define CPU 1 ++#define EPU 2 ++#define HPU 3 ++ ++struct cx18; ++ ++/* ++ * This structure is used by CPU to provide completed MDL & buffers information. ++ * Its structure is dictated by the layout of the SCB, required by the ++ * firmware, but its definition needs to be here, instead of in cx18-scb.h, ++ * for mailbox work order scheduling ++ */ ++struct cx18_mdl_ack { ++ u32 id; /* ID of a completed MDL */ ++ u32 data_used; /* Total data filled in the MDL with 'id' */ ++}; ++ ++/* The cx18_mailbox struct is the mailbox structure which is used for passing ++ messages between processors */ ++struct cx18_mailbox { ++ /* The sender sets a handle in 'request' after he fills the command. The ++ 'request' should be different than 'ack'. The sender, also, generates ++ an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the ++ receiver. */ ++ u32 request; ++ /* The receiver detects a new command when 'req' is different than 'ack'. ++ He sets 'ack' to the same value as 'req' to clear the command. He, also, ++ generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU ++ is the receiver. */ ++ u32 ack; ++ u32 reserved[6]; ++ /* 'cmd' identifies the command. The list of these commands are in ++ cx23418.h */ ++ u32 cmd; ++ /* Each command can have up to 6 arguments */ ++ u32 args[MAX_MB_ARGUMENTS]; ++ /* The return code can be one of the codes in the file cx23418.h. If the ++ command is completed successfully, the error will be ERR_SYS_SUCCESS. ++ If it is pending, the code is ERR_SYS_PENDING. If it failed, the error ++ code would indicate the task from which the error originated and will ++ be one of the errors in cx23418.h. In that case, the following ++ applies ((error & 0xff) != 0). ++ If the command is pending, the return will be passed in a MB from the ++ receiver to the sender. 'req' will be returned in args[0] */ ++ u32 error; ++}; ++ ++struct cx18_stream; ++ ++int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); ++int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, ++ int args, ...); ++int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); ++int cx18_api_func(void *priv, u32 cmd, int in, int out, ++ u32 data[CX2341X_MBOX_MAX_DATA]); ++ ++void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); ++ ++void cx18_in_work_handler(struct work_struct *work); ++ ++#endif +diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c +new file mode 100644 +index 0000000..8884537 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-queue.c +@@ -0,0 +1,443 @@ ++/* ++ * cx18 buffer queues ++ * ++ * Derived from ivtv-queue.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-queue.h" ++#include "cx18-streams.h" ++#include "cx18-scb.h" ++#include "cx18-io.h" ++ ++void cx18_buf_swap(struct cx18_buffer *buf) ++{ ++ int i; ++ ++ for (i = 0; i < buf->bytesused; i += 4) ++ swab32s((u32 *)(buf->buf + i)); ++} ++ ++void _cx18_mdl_swap(struct cx18_mdl *mdl) ++{ ++ struct cx18_buffer *buf; ++ ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ if (buf->bytesused == 0) ++ break; ++ cx18_buf_swap(buf); ++ } ++} ++ ++void cx18_queue_init(struct cx18_queue *q) ++{ ++ INIT_LIST_HEAD(&q->list); ++ atomic_set(&q->depth, 0); ++ q->bytesused = 0; ++} ++ ++struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, ++ struct cx18_queue *q, int to_front) ++{ ++ /* clear the mdl if it is not to be enqueued to the full queue */ ++ if (q != &s->q_full) { ++ mdl->bytesused = 0; ++ mdl->readpos = 0; ++ mdl->m_flags = 0; ++ mdl->skipped = 0; ++ mdl->curr_buf = NULL; ++ } ++ ++ /* q_busy is restricted to a max buffer count imposed by firmware */ ++ if (q == &s->q_busy && ++ atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM) ++ q = &s->q_free; ++ ++ spin_lock(&q->lock); ++ ++ if (to_front) ++ list_add(&mdl->list, &q->list); /* LIFO */ ++ else ++ list_add_tail(&mdl->list, &q->list); /* FIFO */ ++ q->bytesused += mdl->bytesused - mdl->readpos; ++ atomic_inc(&q->depth); ++ ++ spin_unlock(&q->lock); ++ return q; ++} ++ ++struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) ++{ ++ struct cx18_mdl *mdl = NULL; ++ ++ spin_lock(&q->lock); ++ if (!list_empty(&q->list)) { ++ mdl = list_first_entry(&q->list, struct cx18_mdl, list); ++ list_del_init(&mdl->list); ++ q->bytesused -= mdl->bytesused - mdl->readpos; ++ mdl->skipped = 0; ++ atomic_dec(&q->depth); ++ } ++ spin_unlock(&q->lock); ++ return mdl; ++} ++ ++static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ struct cx18_buffer *buf; ++ u32 buf_size = s->buf_size; ++ u32 bytesused = mdl->bytesused; ++ ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ buf->readpos = 0; ++ if (bytesused >= buf_size) { ++ buf->bytesused = buf_size; ++ bytesused -= buf_size; ++ } else { ++ buf->bytesused = bytesused; ++ bytesused = 0; ++ } ++ cx18_buf_sync_for_cpu(s, buf); ++ } ++} ++ ++static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ struct cx18_buffer *buf; ++ ++ if (list_is_singular(&mdl->buf_list)) { ++ buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, ++ list); ++ buf->bytesused = mdl->bytesused; ++ buf->readpos = 0; ++ cx18_buf_sync_for_cpu(s, buf); ++ } else { ++ _cx18_mdl_update_bufs_for_cpu(s, mdl); ++ } ++} ++ ++struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, ++ u32 bytesused) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_mdl *mdl; ++ struct cx18_mdl *tmp; ++ struct cx18_mdl *ret = NULL; ++ LIST_HEAD(sweep_up); ++ ++ /* ++ * We don't have to acquire multiple q locks here, because we are ++ * serialized by the single threaded work handler. ++ * MDLs from the firmware will thus remain in order as ++ * they are moved from q_busy to q_full or to the dvb ring buffer. ++ */ ++ spin_lock(&s->q_busy.lock); ++ list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) { ++ /* ++ * We should find what the firmware told us is done, ++ * right at the front of the queue. If we don't, we likely have ++ * missed an mdl done message from the firmware. ++ * Once we skip an mdl repeatedly, relative to the size of ++ * q_busy, we have high confidence we've missed it. ++ */ ++ if (mdl->id != id) { ++ mdl->skipped++; ++ if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) { ++ /* mdl must have fallen out of rotation */ ++ CX18_WARN("Skipped %s, MDL %d, %d " ++ "times - it must have dropped out of " ++ "rotation\n", s->name, mdl->id, ++ mdl->skipped); ++ /* Sweep it up to put it back into rotation */ ++ list_move_tail(&mdl->list, &sweep_up); ++ atomic_dec(&s->q_busy.depth); ++ } ++ continue; ++ } ++ /* ++ * We pull the desired mdl off of the queue here. Something ++ * will have to put it back on a queue later. ++ */ ++ list_del_init(&mdl->list); ++ atomic_dec(&s->q_busy.depth); ++ ret = mdl; ++ break; ++ } ++ spin_unlock(&s->q_busy.lock); ++ ++ /* ++ * We found the mdl for which we were looking. Get it ready for ++ * the caller to put on q_full or in the dvb ring buffer. ++ */ ++ if (ret != NULL) { ++ ret->bytesused = bytesused; ++ ret->skipped = 0; ++ /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */ ++ cx18_mdl_update_bufs_for_cpu(s, ret); ++ if (s->type != CX18_ENC_STREAM_TYPE_TS) ++ set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags); ++ } ++ ++ /* Put any mdls the firmware is ignoring back into normal rotation */ ++ list_for_each_entry_safe(mdl, tmp, &sweep_up, list) { ++ list_del_init(&mdl->list); ++ cx18_enqueue(s, mdl, &s->q_free); ++ } ++ return ret; ++} ++ ++/* Move all mdls of a queue, while flushing the mdl */ ++static void cx18_queue_flush(struct cx18_stream *s, ++ struct cx18_queue *q_src, struct cx18_queue *q_dst) ++{ ++ struct cx18_mdl *mdl; ++ ++ /* It only makes sense to flush to q_free or q_idle */ ++ if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy) ++ return; ++ ++ spin_lock(&q_src->lock); ++ spin_lock(&q_dst->lock); ++ while (!list_empty(&q_src->list)) { ++ mdl = list_first_entry(&q_src->list, struct cx18_mdl, list); ++ list_move_tail(&mdl->list, &q_dst->list); ++ mdl->bytesused = 0; ++ mdl->readpos = 0; ++ mdl->m_flags = 0; ++ mdl->skipped = 0; ++ mdl->curr_buf = NULL; ++ atomic_inc(&q_dst->depth); ++ } ++ cx18_queue_init(q_src); ++ spin_unlock(&q_src->lock); ++ spin_unlock(&q_dst->lock); ++} ++ ++void cx18_flush_queues(struct cx18_stream *s) ++{ ++ cx18_queue_flush(s, &s->q_busy, &s->q_free); ++ cx18_queue_flush(s, &s->q_full, &s->q_free); ++} ++ ++/* ++ * Note, s->buf_pool is not protected by a lock, ++ * the stream better not have *anything* going on when calling this ++ */ ++void cx18_unload_queues(struct cx18_stream *s) ++{ ++ struct cx18_queue *q_idle = &s->q_idle; ++ struct cx18_mdl *mdl; ++ struct cx18_buffer *buf; ++ ++ /* Move all MDLS to q_idle */ ++ cx18_queue_flush(s, &s->q_busy, q_idle); ++ cx18_queue_flush(s, &s->q_full, q_idle); ++ cx18_queue_flush(s, &s->q_free, q_idle); ++ ++ /* Reset MDL id's and move all buffers back to the stream's buf_pool */ ++ spin_lock(&q_idle->lock); ++ list_for_each_entry(mdl, &q_idle->list, list) { ++ while (!list_empty(&mdl->buf_list)) { ++ buf = list_first_entry(&mdl->buf_list, ++ struct cx18_buffer, list); ++ list_move_tail(&buf->list, &s->buf_pool); ++ buf->bytesused = 0; ++ buf->readpos = 0; ++ } ++ mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */ ++ /* all other mdl fields were cleared by cx18_queue_flush() */ ++ } ++ spin_unlock(&q_idle->lock); ++} ++ ++/* ++ * Note, s->buf_pool is not protected by a lock, ++ * the stream better not have *anything* going on when calling this ++ */ ++void cx18_load_queues(struct cx18_stream *s) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_mdl *mdl; ++ struct cx18_buffer *buf; ++ int mdl_id; ++ int i; ++ u32 partial_buf_size; ++ ++ /* ++ * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free ++ * Excess MDLs are left on q_idle ++ * Excess buffers are left in buf_pool and/or on an MDL in q_idle ++ */ ++ mdl_id = s->mdl_base_idx; ++ for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl; ++ mdl != NULL && i == s->bufs_per_mdl; ++ mdl = cx18_dequeue(s, &s->q_idle)) { ++ ++ mdl->id = mdl_id; ++ ++ for (i = 0; i < s->bufs_per_mdl; i++) { ++ if (list_empty(&s->buf_pool)) ++ break; ++ ++ buf = list_first_entry(&s->buf_pool, struct cx18_buffer, ++ list); ++ list_move_tail(&buf->list, &mdl->buf_list); ++ ++ /* update the firmware's MDL array with this buffer */ ++ cx18_writel(cx, buf->dma_handle, ++ &cx->scb->cpu_mdl[mdl_id + i].paddr); ++ cx18_writel(cx, s->buf_size, ++ &cx->scb->cpu_mdl[mdl_id + i].length); ++ } ++ ++ if (i == s->bufs_per_mdl) { ++ /* ++ * The encoder doesn't honor s->mdl_size. So in the ++ * case of a non-integral number of buffers to meet ++ * mdl_size, we lie about the size of the last buffer ++ * in the MDL to get the encoder to really only send ++ * us mdl_size bytes per MDL transfer. ++ */ ++ partial_buf_size = s->mdl_size % s->buf_size; ++ if (partial_buf_size) { ++ cx18_writel(cx, partial_buf_size, ++ &cx->scb->cpu_mdl[mdl_id + i - 1].length); ++ } ++ cx18_enqueue(s, mdl, &s->q_free); ++ } else { ++ /* Not enough buffers for this MDL; we won't use it */ ++ cx18_push(s, mdl, &s->q_idle); ++ } ++ mdl_id += i; ++ } ++} ++ ++void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl) ++{ ++ int dma = s->dma; ++ u32 buf_size = s->buf_size; ++ struct pci_dev *pci_dev = s->cx->pci_dev; ++ struct cx18_buffer *buf; ++ ++ list_for_each_entry(buf, &mdl->buf_list, list) ++ pci_dma_sync_single_for_device(pci_dev, buf->dma_handle, ++ buf_size, dma); ++} ++ ++int cx18_stream_alloc(struct cx18_stream *s) ++{ ++ struct cx18 *cx = s->cx; ++ int i; ++ ++ if (s->buffers == 0) ++ return 0; ++ ++ CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers " ++ "(%d.%02d kB total)\n", ++ s->name, s->buffers, s->buf_size, ++ s->buffers * s->buf_size / 1024, ++ (s->buffers * s->buf_size * 100 / 1024) % 100); ++ ++ if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] - ++ (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { ++ unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - ++ ((char __iomem *)cx->scb->cpu_mdl)); ++ ++ CX18_ERR("Too many buffers, cannot fit in SCB area\n"); ++ CX18_ERR("Max buffers = %zd\n", ++ bufsz / sizeof(struct cx18_mdl_ent)); ++ return -ENOMEM; ++ } ++ ++ s->mdl_base_idx = cx->free_mdl_idx; ++ ++ /* allocate stream buffers and MDLs */ ++ for (i = 0; i < s->buffers; i++) { ++ struct cx18_mdl *mdl; ++ struct cx18_buffer *buf; ++ ++ /* 1 MDL per buffer to handle the worst & also default case */ ++ mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN); ++ if (mdl == NULL) ++ break; ++ ++ buf = kzalloc(sizeof(struct cx18_buffer), ++ GFP_KERNEL|__GFP_NOWARN); ++ if (buf == NULL) { ++ kfree(mdl); ++ break; ++ } ++ ++ buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); ++ if (buf->buf == NULL) { ++ kfree(mdl); ++ kfree(buf); ++ break; ++ } ++ ++ INIT_LIST_HEAD(&mdl->list); ++ INIT_LIST_HEAD(&mdl->buf_list); ++ mdl->id = s->mdl_base_idx; /* a somewhat safe value */ ++ cx18_enqueue(s, mdl, &s->q_idle); ++ ++ INIT_LIST_HEAD(&buf->list); ++ buf->dma_handle = pci_map_single(s->cx->pci_dev, ++ buf->buf, s->buf_size, s->dma); ++ cx18_buf_sync_for_cpu(s, buf); ++ list_add_tail(&buf->list, &s->buf_pool); ++ } ++ if (i == s->buffers) { ++ cx->free_mdl_idx += s->buffers; ++ return 0; ++ } ++ CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); ++ cx18_stream_free(s); ++ return -ENOMEM; ++} ++ ++void cx18_stream_free(struct cx18_stream *s) ++{ ++ struct cx18_mdl *mdl; ++ struct cx18_buffer *buf; ++ struct cx18 *cx = s->cx; ++ ++ CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name); ++ ++ /* move all buffers to buf_pool and all MDLs to q_idle */ ++ cx18_unload_queues(s); ++ ++ /* empty q_idle */ ++ while ((mdl = cx18_dequeue(s, &s->q_idle))) ++ kfree(mdl); ++ ++ /* empty buf_pool */ ++ while (!list_empty(&s->buf_pool)) { ++ buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list); ++ list_del_init(&buf->list); ++ ++ pci_unmap_single(s->cx->pci_dev, buf->dma_handle, ++ s->buf_size, s->dma); ++ kfree(buf->buf); ++ kfree(buf); ++ } ++} +diff --git a/drivers/media/pci/cx18/cx18-queue.h b/drivers/media/pci/cx18/cx18-queue.h +new file mode 100644 +index 0000000..4201ddc +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-queue.h +@@ -0,0 +1,98 @@ ++/* ++ * cx18 buffer queues ++ * ++ * Derived from ivtv-queue.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#define CX18_DMA_UNMAPPED ((u32) -1) ++ ++/* cx18_buffer utility functions */ ++ ++static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, ++ struct cx18_buffer *buf) ++{ ++ pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle, ++ s->buf_size, s->dma); ++} ++ ++static inline void cx18_buf_sync_for_device(struct cx18_stream *s, ++ struct cx18_buffer *buf) ++{ ++ pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle, ++ s->buf_size, s->dma); ++} ++ ++void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); ++ ++static inline void cx18_mdl_sync_for_device(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ if (list_is_singular(&mdl->buf_list)) ++ cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list, ++ struct cx18_buffer, ++ list)); ++ else ++ _cx18_mdl_sync_for_device(s, mdl); ++} ++ ++void cx18_buf_swap(struct cx18_buffer *buf); ++void _cx18_mdl_swap(struct cx18_mdl *mdl); ++ ++static inline void cx18_mdl_swap(struct cx18_mdl *mdl) ++{ ++ if (list_is_singular(&mdl->buf_list)) ++ cx18_buf_swap(list_first_entry(&mdl->buf_list, ++ struct cx18_buffer, list)); ++ else ++ _cx18_mdl_swap(mdl); ++} ++ ++/* cx18_queue utility functions */ ++struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, ++ struct cx18_queue *q, int to_front); ++ ++static inline ++struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, ++ struct cx18_queue *q) ++{ ++ return _cx18_enqueue(s, mdl, q, 0); /* FIFO */ ++} ++ ++static inline ++struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl, ++ struct cx18_queue *q) ++{ ++ return _cx18_enqueue(s, mdl, q, 1); /* LIFO */ ++} ++ ++void cx18_queue_init(struct cx18_queue *q); ++struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); ++struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, ++ u32 bytesused); ++void cx18_flush_queues(struct cx18_stream *s); ++ ++/* queue MDL reconfiguration helpers */ ++void cx18_unload_queues(struct cx18_stream *s); ++void cx18_load_queues(struct cx18_stream *s); ++ ++/* cx18_stream utility functions */ ++int cx18_stream_alloc(struct cx18_stream *s); ++void cx18_stream_free(struct cx18_stream *s); +diff --git a/drivers/media/pci/cx18/cx18-scb.c b/drivers/media/pci/cx18/cx18-scb.c +new file mode 100644 +index 0000000..85cc596 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-scb.c +@@ -0,0 +1,122 @@ ++/* ++ * cx18 System Control Block initialization ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-scb.h" ++ ++void cx18_init_scb(struct cx18 *cx) ++{ ++ cx18_setup_page(cx, SCB_OFFSET); ++ cx18_memset_io(cx, cx->scb, 0, 0x10000); ++ ++ cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); ++ cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); ++ cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); ++ cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); ++ cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); ++ cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); ++ cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); ++ cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); ++ ++ cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); ++ cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); ++ cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); ++ cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); ++ cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); ++ cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); ++ cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); ++ cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); ++ ++ cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); ++ cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); ++ cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); ++ cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); ++ cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); ++ cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); ++ cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); ++ cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); ++ ++ cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); ++ cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); ++ cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); ++ cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); ++ cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); ++ cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); ++ cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); ++ cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); ++ ++ cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); ++ cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); ++ cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); ++ cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); ++ cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); ++ cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); ++ cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); ++ cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); ++ ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), ++ &cx->scb->apu2cpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), ++ &cx->scb->hpu2cpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), ++ &cx->scb->ppu2cpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), ++ &cx->scb->epu2cpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), ++ &cx->scb->cpu2apu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), ++ &cx->scb->hpu2apu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), ++ &cx->scb->ppu2apu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), ++ &cx->scb->epu2apu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), ++ &cx->scb->cpu2hpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), ++ &cx->scb->apu2hpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), ++ &cx->scb->ppu2hpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), ++ &cx->scb->epu2hpu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), ++ &cx->scb->cpu2ppu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), ++ &cx->scb->apu2ppu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), ++ &cx->scb->hpu2ppu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), ++ &cx->scb->epu2ppu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), ++ &cx->scb->cpu2epu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), ++ &cx->scb->apu2epu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), ++ &cx->scb->hpu2epu_mb_offset); ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), ++ &cx->scb->ppu2epu_mb_offset); ++ ++ cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), ++ &cx->scb->ipc_offset); ++ ++ cx18_writel(cx, 1, &cx->scb->epu_state); ++} +diff --git a/drivers/media/pci/cx18/cx18-scb.h b/drivers/media/pci/cx18/cx18-scb.h +new file mode 100644 +index 0000000..0887765 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-scb.h +@@ -0,0 +1,280 @@ ++/* ++ * cx18 System Control Block initialization ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef CX18_SCB_H ++#define CX18_SCB_H ++ ++#include "cx18-mailbox.h" ++ ++/* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts ++ are in the SW1 register. */ ++ ++#define IRQ_APU_TO_CPU 0x00000001 ++#define IRQ_CPU_TO_APU_ACK 0x00000001 ++#define IRQ_HPU_TO_CPU 0x00000002 ++#define IRQ_CPU_TO_HPU_ACK 0x00000002 ++#define IRQ_PPU_TO_CPU 0x00000004 ++#define IRQ_CPU_TO_PPU_ACK 0x00000004 ++#define IRQ_EPU_TO_CPU 0x00000008 ++#define IRQ_CPU_TO_EPU_ACK 0x00000008 ++ ++#define IRQ_CPU_TO_APU 0x00000010 ++#define IRQ_APU_TO_CPU_ACK 0x00000010 ++#define IRQ_HPU_TO_APU 0x00000020 ++#define IRQ_APU_TO_HPU_ACK 0x00000020 ++#define IRQ_PPU_TO_APU 0x00000040 ++#define IRQ_APU_TO_PPU_ACK 0x00000040 ++#define IRQ_EPU_TO_APU 0x00000080 ++#define IRQ_APU_TO_EPU_ACK 0x00000080 ++ ++#define IRQ_CPU_TO_HPU 0x00000100 ++#define IRQ_HPU_TO_CPU_ACK 0x00000100 ++#define IRQ_APU_TO_HPU 0x00000200 ++#define IRQ_HPU_TO_APU_ACK 0x00000200 ++#define IRQ_PPU_TO_HPU 0x00000400 ++#define IRQ_HPU_TO_PPU_ACK 0x00000400 ++#define IRQ_EPU_TO_HPU 0x00000800 ++#define IRQ_HPU_TO_EPU_ACK 0x00000800 ++ ++#define IRQ_CPU_TO_PPU 0x00001000 ++#define IRQ_PPU_TO_CPU_ACK 0x00001000 ++#define IRQ_APU_TO_PPU 0x00002000 ++#define IRQ_PPU_TO_APU_ACK 0x00002000 ++#define IRQ_HPU_TO_PPU 0x00004000 ++#define IRQ_PPU_TO_HPU_ACK 0x00004000 ++#define IRQ_EPU_TO_PPU 0x00008000 ++#define IRQ_PPU_TO_EPU_ACK 0x00008000 ++ ++#define IRQ_CPU_TO_EPU 0x00010000 ++#define IRQ_EPU_TO_CPU_ACK 0x00010000 ++#define IRQ_APU_TO_EPU 0x00020000 ++#define IRQ_EPU_TO_APU_ACK 0x00020000 ++#define IRQ_HPU_TO_EPU 0x00040000 ++#define IRQ_EPU_TO_HPU_ACK 0x00040000 ++#define IRQ_PPU_TO_EPU 0x00080000 ++#define IRQ_EPU_TO_PPU_ACK 0x00080000 ++ ++#define SCB_OFFSET 0xDC0000 ++ ++/* If Firmware uses fixed memory map, it shall not allocate the area ++ between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */ ++#define SCB_RESERVED_SIZE 0x10000 ++ ++ ++/* This structure is used by EPU to provide memory descriptors in its memory */ ++struct cx18_mdl_ent { ++ u32 paddr; /* Physical address of a buffer segment */ ++ u32 length; /* Length of the buffer segment */ ++}; ++ ++struct cx18_scb { ++ /* These fields form the System Control Block which is used at boot time ++ for localizing the IPC data as well as the code positions for all ++ processors. The offsets are from the start of this struct. */ ++ ++ /* Offset where to find the Inter-Processor Communication data */ ++ u32 ipc_offset; ++ u32 reserved01[7]; ++ /* Offset where to find the start of the CPU code */ ++ u32 cpu_code_offset; ++ u32 reserved02[3]; ++ /* Offset where to find the start of the APU code */ ++ u32 apu_code_offset; ++ u32 reserved03[3]; ++ /* Offset where to find the start of the HPU code */ ++ u32 hpu_code_offset; ++ u32 reserved04[3]; ++ /* Offset where to find the start of the PPU code */ ++ u32 ppu_code_offset; ++ u32 reserved05[3]; ++ ++ /* These fields form Inter-Processor Communication data which is used ++ by all processors to locate the information needed for communicating ++ with other processors */ ++ ++ /* Fields for CPU: */ ++ ++ /* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */ ++ u32 cpu_state; ++ u32 reserved1[7]; ++ /* Offset to the mailbox used for sending commands from APU to CPU */ ++ u32 apu2cpu_mb_offset; ++ /* Value to write to register SW1 register set (0xC7003100) after the ++ command is ready */ ++ u32 apu2cpu_irq; ++ /* Value to write to register SW2 register set (0xC7003140) after the ++ command is cleared */ ++ u32 cpu2apu_irq_ack; ++ u32 reserved2[13]; ++ ++ u32 hpu2cpu_mb_offset; ++ u32 hpu2cpu_irq; ++ u32 cpu2hpu_irq_ack; ++ u32 reserved3[13]; ++ ++ u32 ppu2cpu_mb_offset; ++ u32 ppu2cpu_irq; ++ u32 cpu2ppu_irq_ack; ++ u32 reserved4[13]; ++ ++ u32 epu2cpu_mb_offset; ++ u32 epu2cpu_irq; ++ u32 cpu2epu_irq_ack; ++ u32 reserved5[13]; ++ u32 reserved6[8]; ++ ++ /* Fields for APU: */ ++ ++ u32 apu_state; ++ u32 reserved11[7]; ++ u32 cpu2apu_mb_offset; ++ u32 cpu2apu_irq; ++ u32 apu2cpu_irq_ack; ++ u32 reserved12[13]; ++ ++ u32 hpu2apu_mb_offset; ++ u32 hpu2apu_irq; ++ u32 apu2hpu_irq_ack; ++ u32 reserved13[13]; ++ ++ u32 ppu2apu_mb_offset; ++ u32 ppu2apu_irq; ++ u32 apu2ppu_irq_ack; ++ u32 reserved14[13]; ++ ++ u32 epu2apu_mb_offset; ++ u32 epu2apu_irq; ++ u32 apu2epu_irq_ack; ++ u32 reserved15[13]; ++ u32 reserved16[8]; ++ ++ /* Fields for HPU: */ ++ ++ u32 hpu_state; ++ u32 reserved21[7]; ++ u32 cpu2hpu_mb_offset; ++ u32 cpu2hpu_irq; ++ u32 hpu2cpu_irq_ack; ++ u32 reserved22[13]; ++ ++ u32 apu2hpu_mb_offset; ++ u32 apu2hpu_irq; ++ u32 hpu2apu_irq_ack; ++ u32 reserved23[13]; ++ ++ u32 ppu2hpu_mb_offset; ++ u32 ppu2hpu_irq; ++ u32 hpu2ppu_irq_ack; ++ u32 reserved24[13]; ++ ++ u32 epu2hpu_mb_offset; ++ u32 epu2hpu_irq; ++ u32 hpu2epu_irq_ack; ++ u32 reserved25[13]; ++ u32 reserved26[8]; ++ ++ /* Fields for PPU: */ ++ ++ u32 ppu_state; ++ u32 reserved31[7]; ++ u32 cpu2ppu_mb_offset; ++ u32 cpu2ppu_irq; ++ u32 ppu2cpu_irq_ack; ++ u32 reserved32[13]; ++ ++ u32 apu2ppu_mb_offset; ++ u32 apu2ppu_irq; ++ u32 ppu2apu_irq_ack; ++ u32 reserved33[13]; ++ ++ u32 hpu2ppu_mb_offset; ++ u32 hpu2ppu_irq; ++ u32 ppu2hpu_irq_ack; ++ u32 reserved34[13]; ++ ++ u32 epu2ppu_mb_offset; ++ u32 epu2ppu_irq; ++ u32 ppu2epu_irq_ack; ++ u32 reserved35[13]; ++ u32 reserved36[8]; ++ ++ /* Fields for EPU: */ ++ ++ u32 epu_state; ++ u32 reserved41[7]; ++ u32 cpu2epu_mb_offset; ++ u32 cpu2epu_irq; ++ u32 epu2cpu_irq_ack; ++ u32 reserved42[13]; ++ ++ u32 apu2epu_mb_offset; ++ u32 apu2epu_irq; ++ u32 epu2apu_irq_ack; ++ u32 reserved43[13]; ++ ++ u32 hpu2epu_mb_offset; ++ u32 hpu2epu_irq; ++ u32 epu2hpu_irq_ack; ++ u32 reserved44[13]; ++ ++ u32 ppu2epu_mb_offset; ++ u32 ppu2epu_irq; ++ u32 epu2ppu_irq_ack; ++ u32 reserved45[13]; ++ u32 reserved46[8]; ++ ++ u32 semaphores[8]; /* Semaphores */ ++ ++ u32 reserved50[32]; /* Reserved for future use */ ++ ++ struct cx18_mailbox apu2cpu_mb; ++ struct cx18_mailbox hpu2cpu_mb; ++ struct cx18_mailbox ppu2cpu_mb; ++ struct cx18_mailbox epu2cpu_mb; ++ ++ struct cx18_mailbox cpu2apu_mb; ++ struct cx18_mailbox hpu2apu_mb; ++ struct cx18_mailbox ppu2apu_mb; ++ struct cx18_mailbox epu2apu_mb; ++ ++ struct cx18_mailbox cpu2hpu_mb; ++ struct cx18_mailbox apu2hpu_mb; ++ struct cx18_mailbox ppu2hpu_mb; ++ struct cx18_mailbox epu2hpu_mb; ++ ++ struct cx18_mailbox cpu2ppu_mb; ++ struct cx18_mailbox apu2ppu_mb; ++ struct cx18_mailbox hpu2ppu_mb; ++ struct cx18_mailbox epu2ppu_mb; ++ ++ struct cx18_mailbox cpu2epu_mb; ++ struct cx18_mailbox apu2epu_mb; ++ struct cx18_mailbox hpu2epu_mb; ++ struct cx18_mailbox ppu2epu_mb; ++ ++ struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; ++ struct cx18_mdl_ent cpu_mdl[1]; ++}; ++ ++void cx18_init_scb(struct cx18 *cx); ++ ++#endif +diff --git a/drivers/media/pci/cx18/cx18-streams.c b/drivers/media/pci/cx18/cx18-streams.c +new file mode 100644 +index 0000000..843c62b +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-streams.c +@@ -0,0 +1,1059 @@ ++/* ++ * cx18 init/start/stop/exit stream functions ++ * ++ * Derived from ivtv-streams.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-io.h" ++#include "cx18-fileops.h" ++#include "cx18-mailbox.h" ++#include "cx18-i2c.h" ++#include "cx18-queue.h" ++#include "cx18-ioctl.h" ++#include "cx18-streams.h" ++#include "cx18-cards.h" ++#include "cx18-scb.h" ++#include "cx18-dvb.h" ++ ++#define CX18_DSP0_INTERRUPT_MASK 0xd0004C ++ ++static struct v4l2_file_operations cx18_v4l2_enc_fops = { ++ .owner = THIS_MODULE, ++ .read = cx18_v4l2_read, ++ .open = cx18_v4l2_open, ++ .unlocked_ioctl = video_ioctl2, ++ .release = cx18_v4l2_close, ++ .poll = cx18_v4l2_enc_poll, ++ .mmap = cx18_v4l2_mmap, ++}; ++ ++/* offset from 0 to register ts v4l2 minors on */ ++#define CX18_V4L2_ENC_TS_OFFSET 16 ++/* offset from 0 to register pcm v4l2 minors on */ ++#define CX18_V4L2_ENC_PCM_OFFSET 24 ++/* offset from 0 to register yuv v4l2 minors on */ ++#define CX18_V4L2_ENC_YUV_OFFSET 32 ++ ++static struct { ++ const char *name; ++ int vfl_type; ++ int num_offset; ++ int dma; ++} cx18_stream_info[] = { ++ { /* CX18_ENC_STREAM_TYPE_MPG */ ++ "encoder MPEG", ++ VFL_TYPE_GRABBER, 0, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_TS */ ++ "TS", ++ VFL_TYPE_GRABBER, -1, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_YUV */ ++ "encoder YUV", ++ VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_VBI */ ++ "encoder VBI", ++ VFL_TYPE_VBI, 0, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_PCM */ ++ "encoder PCM audio", ++ VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_IDX */ ++ "encoder IDX", ++ VFL_TYPE_GRABBER, -1, ++ PCI_DMA_FROMDEVICE, ++ }, ++ { /* CX18_ENC_STREAM_TYPE_RAD */ ++ "encoder radio", ++ VFL_TYPE_RADIO, 0, ++ PCI_DMA_NONE, ++ }, ++}; ++ ++ ++static void cx18_dma_free(struct videobuf_queue *q, ++ struct cx18_stream *s, struct cx18_videobuf_buffer *buf) ++{ ++ videobuf_waiton(q, &buf->vb, 0, 0); ++ videobuf_vmalloc_free(&buf->vb); ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static int cx18_prepare_buffer(struct videobuf_queue *q, ++ struct cx18_stream *s, ++ struct cx18_videobuf_buffer *buf, ++ u32 pixelformat, ++ unsigned int width, unsigned int height, ++ enum v4l2_field field) ++{ ++ struct cx18 *cx = s->cx; ++ int rc = 0; ++ ++ /* check settings */ ++ buf->bytes_used = 0; ++ ++ if ((width < 48) || (height < 32)) ++ return -EINVAL; ++ ++ buf->vb.size = (width * height * 2); ++ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) ++ return -EINVAL; ++ ++ /* alloc + fill struct (if changed) */ ++ if (buf->vb.width != width || buf->vb.height != height || ++ buf->vb.field != field || s->pixelformat != pixelformat || ++ buf->tvnorm != cx->std) { ++ ++ buf->vb.width = width; ++ buf->vb.height = height; ++ buf->vb.field = field; ++ buf->tvnorm = cx->std; ++ s->pixelformat = pixelformat; ++ ++ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) ++ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ ++ if (s->pixelformat == V4L2_PIX_FMT_HM12) ++ s->vb_bytes_per_frame = height * 720 * 3 / 2; ++ else ++ s->vb_bytes_per_frame = height * 720 * 2; ++ cx18_dma_free(q, s, buf); ++ } ++ ++ if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) ++ return -EINVAL; ++ ++ if (buf->vb.field == 0) ++ buf->vb.field = V4L2_FIELD_INTERLACED; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ buf->vb.width = width; ++ buf->vb.height = height; ++ buf->vb.field = field; ++ buf->tvnorm = cx->std; ++ s->pixelformat = pixelformat; ++ ++ /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) ++ UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ ++ if (s->pixelformat == V4L2_PIX_FMT_HM12) ++ s->vb_bytes_per_frame = height * 720 * 3 / 2; ++ else ++ s->vb_bytes_per_frame = height * 720 * 2; ++ rc = videobuf_iolock(q, &buf->vb, NULL); ++ if (rc != 0) ++ goto fail; ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++fail: ++ cx18_dma_free(q, s, buf); ++ return rc; ++ ++} ++ ++/* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576) ++ 1440 is a single line of 4:2:2 YUV at 720 luma samples wide ++*/ ++#define VB_MIN_BUFFERS 32 ++#define VB_MIN_BUFSIZE 4147200 ++ ++static int buffer_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct cx18_stream *s = q->priv_data; ++ struct cx18 *cx = s->cx; ++ ++ *size = 2 * cx->cxhdl.width * cx->cxhdl.height; ++ if (*count == 0) ++ *count = VB_MIN_BUFFERS; ++ ++ while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE) ++ (*count)--; ++ ++ q->field = V4L2_FIELD_INTERLACED; ++ q->last = V4L2_FIELD_INTERLACED; ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx18_videobuf_buffer *buf = ++ container_of(vb, struct cx18_videobuf_buffer, vb); ++ struct cx18_stream *s = q->priv_data; ++ struct cx18 *cx = s->cx; ++ ++ return cx18_prepare_buffer(q, s, buf, s->pixelformat, ++ cx->cxhdl.width, cx->cxhdl.height, field); ++} ++ ++static void buffer_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ struct cx18_videobuf_buffer *buf = ++ container_of(vb, struct cx18_videobuf_buffer, vb); ++ struct cx18_stream *s = q->priv_data; ++ ++ cx18_dma_free(q, s, buf); ++} ++ ++static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx18_videobuf_buffer *buf = ++ container_of(vb, struct cx18_videobuf_buffer, vb); ++ struct cx18_stream *s = q->priv_data; ++ ++ buf->vb.state = VIDEOBUF_QUEUED; ++ ++ list_add_tail(&buf->vb.queue, &s->vb_capture); ++} ++ ++static struct videobuf_queue_ops cx18_videobuf_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++static void cx18_stream_init(struct cx18 *cx, int type) ++{ ++ struct cx18_stream *s = &cx->streams[type]; ++ struct video_device *video_dev = s->video_dev; ++ ++ /* we need to keep video_dev, so restore it afterwards */ ++ memset(s, 0, sizeof(*s)); ++ s->video_dev = video_dev; ++ ++ /* initialize cx18_stream fields */ ++ s->dvb = NULL; ++ s->cx = cx; ++ s->type = type; ++ s->name = cx18_stream_info[type].name; ++ s->handle = CX18_INVALID_TASK_HANDLE; ++ ++ s->dma = cx18_stream_info[type].dma; ++ s->buffers = cx->stream_buffers[type]; ++ s->buf_size = cx->stream_buf_size[type]; ++ INIT_LIST_HEAD(&s->buf_pool); ++ s->bufs_per_mdl = 1; ++ s->mdl_size = s->buf_size * s->bufs_per_mdl; ++ ++ init_waitqueue_head(&s->waitq); ++ s->id = -1; ++ spin_lock_init(&s->q_free.lock); ++ cx18_queue_init(&s->q_free); ++ spin_lock_init(&s->q_busy.lock); ++ cx18_queue_init(&s->q_busy); ++ spin_lock_init(&s->q_full.lock); ++ cx18_queue_init(&s->q_full); ++ spin_lock_init(&s->q_idle.lock); ++ cx18_queue_init(&s->q_idle); ++ ++ INIT_WORK(&s->out_work_order, cx18_out_work_handler); ++ ++ INIT_LIST_HEAD(&s->vb_capture); ++ s->vb_timeout.function = cx18_vb_timeout; ++ s->vb_timeout.data = (unsigned long)s; ++ init_timer(&s->vb_timeout); ++ spin_lock_init(&s->vb_lock); ++ if (type == CX18_ENC_STREAM_TYPE_YUV) { ++ spin_lock_init(&s->vbuf_q_lock); ++ ++ s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops, ++ &cx->pci_dev->dev, &s->vbuf_q_lock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct cx18_videobuf_buffer), ++ s, &cx->serialize_lock); ++ ++ /* Assume the previous pixel default */ ++ s->pixelformat = V4L2_PIX_FMT_HM12; ++ s->vb_bytes_per_frame = cx->cxhdl.height * 720 * 3 / 2; ++ } ++} ++ ++static int cx18_prep_dev(struct cx18 *cx, int type) ++{ ++ struct cx18_stream *s = &cx->streams[type]; ++ u32 cap = cx->v4l2_cap; ++ int num_offset = cx18_stream_info[type].num_offset; ++ int num = cx->instance + cx18_first_minor + num_offset; ++ ++ /* ++ * These five fields are always initialized. ++ * For analog capture related streams, if video_dev == NULL then the ++ * stream is not in use. ++ * For the TS stream, if dvb == NULL then the stream is not in use. ++ * In those cases no other fields but these four can be used. ++ */ ++ s->video_dev = NULL; ++ s->dvb = NULL; ++ s->cx = cx; ++ s->type = type; ++ s->name = cx18_stream_info[type].name; ++ ++ /* Check whether the radio is supported */ ++ if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) ++ return 0; ++ ++ /* Check whether VBI is supported */ ++ if (type == CX18_ENC_STREAM_TYPE_VBI && ++ !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) ++ return 0; ++ ++ /* User explicitly selected 0 buffers for these streams, so don't ++ create them. */ ++ if (cx18_stream_info[type].dma != PCI_DMA_NONE && ++ cx->stream_buffers[type] == 0) { ++ CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); ++ return 0; ++ } ++ ++ cx18_stream_init(cx, type); ++ ++ /* Allocate the cx18_dvb struct only for the TS on cards with DTV */ ++ if (type == CX18_ENC_STREAM_TYPE_TS) { ++ if (cx->card->hw_all & CX18_HW_DVB) { ++ s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL); ++ if (s->dvb == NULL) { ++ CX18_ERR("Couldn't allocate cx18_dvb structure" ++ " for %s\n", s->name); ++ return -ENOMEM; ++ } ++ } else { ++ /* Don't need buffers for the TS, if there is no DVB */ ++ s->buffers = 0; ++ } ++ } ++ ++ if (num_offset == -1) ++ return 0; ++ ++ /* allocate and initialize the v4l2 video device structure */ ++ s->video_dev = video_device_alloc(); ++ if (s->video_dev == NULL) { ++ CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", ++ s->name); ++ return -ENOMEM; ++ } ++ ++ snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s", ++ cx->v4l2_dev.name, s->name); ++ ++ s->video_dev->num = num; ++ s->video_dev->v4l2_dev = &cx->v4l2_dev; ++ s->video_dev->fops = &cx18_v4l2_enc_fops; ++ s->video_dev->release = video_device_release; ++ s->video_dev->tvnorms = V4L2_STD_ALL; ++ s->video_dev->lock = &cx->serialize_lock; ++ set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags); ++ cx18_set_funcs(s->video_dev); ++ return 0; ++} ++ ++/* Initialize v4l2 variables and register v4l2 devices */ ++int cx18_streams_setup(struct cx18 *cx) ++{ ++ int type, ret; ++ ++ /* Setup V4L2 Devices */ ++ for (type = 0; type < CX18_MAX_STREAMS; type++) { ++ /* Prepare device */ ++ ret = cx18_prep_dev(cx, type); ++ if (ret < 0) ++ break; ++ ++ /* Allocate Stream */ ++ ret = cx18_stream_alloc(&cx->streams[type]); ++ if (ret < 0) ++ break; ++ } ++ if (type == CX18_MAX_STREAMS) ++ return 0; ++ ++ /* One or more streams could not be initialized. Clean 'em all up. */ ++ cx18_streams_cleanup(cx, 0); ++ return ret; ++} ++ ++static int cx18_reg_dev(struct cx18 *cx, int type) ++{ ++ struct cx18_stream *s = &cx->streams[type]; ++ int vfl_type = cx18_stream_info[type].vfl_type; ++ const char *name; ++ int num, ret; ++ ++ if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) { ++ ret = cx18_dvb_register(s); ++ if (ret < 0) { ++ CX18_ERR("DVB failed to register\n"); ++ return ret; ++ } ++ } ++ ++ if (s->video_dev == NULL) ++ return 0; ++ ++ num = s->video_dev->num; ++ /* card number + user defined offset + device offset */ ++ if (type != CX18_ENC_STREAM_TYPE_MPG) { ++ struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; ++ ++ if (s_mpg->video_dev) ++ num = s_mpg->video_dev->num ++ + cx18_stream_info[type].num_offset; ++ } ++ video_set_drvdata(s->video_dev, s); ++ ++ /* Register device. First try the desired minor, then any free one. */ ++ ret = video_register_device_no_warn(s->video_dev, vfl_type, num); ++ if (ret < 0) { ++ CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", ++ s->name, num); ++ video_device_release(s->video_dev); ++ s->video_dev = NULL; ++ return ret; ++ } ++ ++ name = video_device_node_name(s->video_dev); ++ ++ switch (vfl_type) { ++ case VFL_TYPE_GRABBER: ++ CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n", ++ name, s->name, cx->stream_buffers[type], ++ cx->stream_buf_size[type] / 1024, ++ (cx->stream_buf_size[type] * 100 / 1024) % 100); ++ break; ++ ++ case VFL_TYPE_RADIO: ++ CX18_INFO("Registered device %s for %s\n", name, s->name); ++ break; ++ ++ case VFL_TYPE_VBI: ++ if (cx->stream_buffers[type]) ++ CX18_INFO("Registered device %s for %s " ++ "(%d x %d bytes)\n", ++ name, s->name, cx->stream_buffers[type], ++ cx->stream_buf_size[type]); ++ else ++ CX18_INFO("Registered device %s for %s\n", ++ name, s->name); ++ break; ++ } ++ ++ return 0; ++} ++ ++/* Register v4l2 devices */ ++int cx18_streams_register(struct cx18 *cx) ++{ ++ int type; ++ int err; ++ int ret = 0; ++ ++ /* Register V4L2 devices */ ++ for (type = 0; type < CX18_MAX_STREAMS; type++) { ++ err = cx18_reg_dev(cx, type); ++ if (err && ret == 0) ++ ret = err; ++ } ++ ++ if (ret == 0) ++ return 0; ++ ++ /* One or more streams could not be initialized. Clean 'em all up. */ ++ cx18_streams_cleanup(cx, 1); ++ return ret; ++} ++ ++/* Unregister v4l2 devices */ ++void cx18_streams_cleanup(struct cx18 *cx, int unregister) ++{ ++ struct video_device *vdev; ++ int type; ++ ++ /* Teardown all streams */ ++ for (type = 0; type < CX18_MAX_STREAMS; type++) { ++ ++ /* The TS has a cx18_dvb structure, not a video_device */ ++ if (type == CX18_ENC_STREAM_TYPE_TS) { ++ if (cx->streams[type].dvb != NULL) { ++ if (unregister) ++ cx18_dvb_unregister(&cx->streams[type]); ++ kfree(cx->streams[type].dvb); ++ cx->streams[type].dvb = NULL; ++ cx18_stream_free(&cx->streams[type]); ++ } ++ continue; ++ } ++ ++ /* No struct video_device, but can have buffers allocated */ ++ if (type == CX18_ENC_STREAM_TYPE_IDX) { ++ /* If the module params didn't inhibit IDX ... */ ++ if (cx->stream_buffers[type] != 0) { ++ cx->stream_buffers[type] = 0; ++ /* ++ * Before calling cx18_stream_free(), ++ * check if the IDX stream was actually set up. ++ * Needed, since the cx18_probe() error path ++ * exits through here as well as normal clean up ++ */ ++ if (cx->streams[type].buffers != 0) ++ cx18_stream_free(&cx->streams[type]); ++ } ++ continue; ++ } ++ ++ /* If struct video_device exists, can have buffers allocated */ ++ vdev = cx->streams[type].video_dev; ++ ++ cx->streams[type].video_dev = NULL; ++ if (vdev == NULL) ++ continue; ++ ++ if (type == CX18_ENC_STREAM_TYPE_YUV) ++ videobuf_mmap_free(&cx->streams[type].vbuf_q); ++ ++ cx18_stream_free(&cx->streams[type]); ++ ++ /* Unregister or release device */ ++ if (unregister) ++ video_unregister_device(vdev); ++ else ++ video_device_release(vdev); ++ } ++} ++ ++static void cx18_vbi_setup(struct cx18_stream *s) ++{ ++ struct cx18 *cx = s->cx; ++ int raw = cx18_raw_vbi(cx); ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ int lines; ++ ++ if (cx->is_60hz) { ++ cx->vbi.count = 12; ++ cx->vbi.start[0] = 10; ++ cx->vbi.start[1] = 273; ++ } else { /* PAL/SECAM */ ++ cx->vbi.count = 18; ++ cx->vbi.start[0] = 6; ++ cx->vbi.start[1] = 318; ++ } ++ ++ /* setup VBI registers */ ++ if (raw) ++ v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); ++ else ++ v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); ++ ++ /* ++ * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw ++ * VBI when the first analog capture channel starts, as once it starts ++ * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup ++ * (i.e. for the VBI capture channels). We also send it for each ++ * analog capture channel anyway just to make sure we get the proper ++ * behavior ++ */ ++ if (raw) { ++ lines = cx->vbi.count * 2; ++ } else { ++ /* ++ * For 525/60 systems, according to the VIP 2 & BT.656 std: ++ * The EAV RP code's Field bit toggles on line 4, a few lines ++ * after the Vertcal Blank bit has already toggled. ++ * Tell the encoder to capture 21-4+1=18 lines per field, ++ * since we want lines 10 through 21. ++ * ++ * For 625/50 systems, according to the VIP 2 & BT.656 std: ++ * The EAV RP code's Field bit toggles on line 1, a few lines ++ * after the Vertcal Blank bit has already toggled. ++ * (We've actually set the digitizer so that the Field bit ++ * toggles on line 2.) Tell the encoder to capture 23-2+1=22 ++ * lines per field, since we want lines 6 through 23. ++ */ ++ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; ++ } ++ ++ data[0] = s->handle; ++ /* Lines per field */ ++ data[1] = (lines / 2) | ((lines / 2) << 16); ++ /* bytes per line */ ++ data[2] = (raw ? vbi_active_samples ++ : (cx->is_60hz ? vbi_hblank_samples_60Hz ++ : vbi_hblank_samples_50Hz)); ++ /* Every X number of frames a VBI interrupt arrives ++ (frames as in 25 or 30 fps) */ ++ data[3] = 1; ++ /* ++ * Set the SAV/EAV RP codes to look for as start/stop points ++ * when in VIP-1.1 mode ++ */ ++ if (raw) { ++ /* ++ * Start codes for beginning of "active" line in vertical blank ++ * 0x20 ( VerticalBlank ) ++ * 0x60 ( EvenField VerticalBlank ) ++ */ ++ data[4] = 0x20602060; ++ /* ++ * End codes for end of "active" raw lines and regular lines ++ * 0x30 ( VerticalBlank HorizontalBlank) ++ * 0x70 ( EvenField VerticalBlank HorizontalBlank) ++ * 0x90 (Task HorizontalBlank) ++ * 0xd0 (Task EvenField HorizontalBlank) ++ */ ++ data[5] = 0x307090d0; ++ } else { ++ /* ++ * End codes for active video, we want data in the hblank region ++ * 0xb0 (Task 0 VerticalBlank HorizontalBlank) ++ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank) ++ * ++ * Since the V bit is only allowed to toggle in the EAV RP code, ++ * just before the first active region line, these two ++ * are problematic: ++ * 0x90 (Task HorizontalBlank) ++ * 0xd0 (Task EvenField HorizontalBlank) ++ * ++ * We have set the digitzer such that we don't have to worry ++ * about these problem codes. ++ */ ++ data[4] = 0xB0F0B0F0; ++ /* ++ * Start codes for beginning of active line in vertical blank ++ * 0xa0 (Task VerticalBlank ) ++ * 0xe0 (Task EvenField VerticalBlank ) ++ */ ++ data[5] = 0xA0E0A0E0; ++ } ++ ++ CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", ++ data[0], data[1], data[2], data[3], data[4], data[5]); ++ ++ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); ++} ++ ++void cx18_stream_rotate_idx_mdls(struct cx18 *cx) ++{ ++ struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ struct cx18_mdl *mdl; ++ ++ if (!cx18_stream_enabled(s)) ++ return; ++ ++ /* Return if the firmware is not running low on MDLs */ ++ if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >= ++ CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN) ++ return; ++ ++ /* Return if there are no MDLs to rotate back to the firmware */ ++ if (atomic_read(&s->q_full.depth) < 2) ++ return; ++ ++ /* ++ * Take the oldest IDX MDL still holding data, and discard its index ++ * entries by scheduling the MDL to go back to the firmware ++ */ ++ mdl = cx18_dequeue(s, &s->q_full); ++ if (mdl != NULL) ++ cx18_enqueue(s, mdl, &s->q_free); ++} ++ ++static ++struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ struct cx18 *cx = s->cx; ++ struct cx18_queue *q; ++ ++ /* Don't give it to the firmware, if we're not running a capture */ ++ if (s->handle == CX18_INVALID_TASK_HANDLE || ++ test_bit(CX18_F_S_STOPPING, &s->s_flags) || ++ !test_bit(CX18_F_S_STREAMING, &s->s_flags)) ++ return cx18_enqueue(s, mdl, &s->q_free); ++ ++ q = cx18_enqueue(s, mdl, &s->q_busy); ++ if (q != &s->q_busy) ++ return q; /* The firmware has the max MDLs it can handle */ ++ ++ cx18_mdl_sync_for_device(s, mdl); ++ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, ++ (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, ++ s->bufs_per_mdl, mdl->id, s->mdl_size); ++ return q; ++} ++ ++static ++void _cx18_stream_load_fw_queue(struct cx18_stream *s) ++{ ++ struct cx18_queue *q; ++ struct cx18_mdl *mdl; ++ ++ if (atomic_read(&s->q_free.depth) == 0 || ++ atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) ++ return; ++ ++ /* Move from q_free to q_busy notifying the firmware, until the limit */ ++ do { ++ mdl = cx18_dequeue(s, &s->q_free); ++ if (mdl == NULL) ++ break; ++ q = _cx18_stream_put_mdl_fw(s, mdl); ++ } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM ++ && q == &s->q_busy); ++} ++ ++void cx18_out_work_handler(struct work_struct *work) ++{ ++ struct cx18_stream *s = ++ container_of(work, struct cx18_stream, out_work_order); ++ ++ _cx18_stream_load_fw_queue(s); ++} ++ ++static void cx18_stream_configure_mdls(struct cx18_stream *s) ++{ ++ cx18_unload_queues(s); ++ ++ switch (s->type) { ++ case CX18_ENC_STREAM_TYPE_YUV: ++ /* ++ * Height should be a multiple of 32 lines. ++ * Set the MDL size to the exact size needed for one frame. ++ * Use enough buffers per MDL to cover the MDL size ++ */ ++ if (s->pixelformat == V4L2_PIX_FMT_HM12) ++ s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; ++ else ++ s->mdl_size = 720 * s->cx->cxhdl.height * 2; ++ s->bufs_per_mdl = s->mdl_size / s->buf_size; ++ if (s->mdl_size % s->buf_size) ++ s->bufs_per_mdl++; ++ break; ++ case CX18_ENC_STREAM_TYPE_VBI: ++ s->bufs_per_mdl = 1; ++ if (cx18_raw_vbi(s->cx)) { ++ s->mdl_size = (s->cx->is_60hz ? 12 : 18) ++ * 2 * vbi_active_samples; ++ } else { ++ /* ++ * See comment in cx18_vbi_setup() below about the ++ * extra lines we capture in sliced VBI mode due to ++ * the lines on which EAV RP codes toggle. ++ */ ++ s->mdl_size = s->cx->is_60hz ++ ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz ++ : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; ++ } ++ break; ++ default: ++ s->bufs_per_mdl = 1; ++ s->mdl_size = s->buf_size * s->bufs_per_mdl; ++ break; ++ } ++ ++ cx18_load_queues(s); ++} ++ ++int cx18_start_v4l2_encode_stream(struct cx18_stream *s) ++{ ++ u32 data[MAX_MB_ARGUMENTS]; ++ struct cx18 *cx = s->cx; ++ int captype = 0; ++ struct cx18_stream *s_idx; ++ ++ if (!cx18_stream_enabled(s)) ++ return -EINVAL; ++ ++ CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); ++ ++ switch (s->type) { ++ case CX18_ENC_STREAM_TYPE_MPG: ++ captype = CAPTURE_CHANNEL_TYPE_MPEG; ++ cx->mpg_data_received = cx->vbi_data_inserted = 0; ++ cx->dualwatch_jiffies = jiffies; ++ cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); ++ cx->search_pack_header = 0; ++ break; ++ ++ case CX18_ENC_STREAM_TYPE_IDX: ++ captype = CAPTURE_CHANNEL_TYPE_INDEX; ++ break; ++ case CX18_ENC_STREAM_TYPE_TS: ++ captype = CAPTURE_CHANNEL_TYPE_TS; ++ break; ++ case CX18_ENC_STREAM_TYPE_YUV: ++ captype = CAPTURE_CHANNEL_TYPE_YUV; ++ break; ++ case CX18_ENC_STREAM_TYPE_PCM: ++ captype = CAPTURE_CHANNEL_TYPE_PCM; ++ break; ++ case CX18_ENC_STREAM_TYPE_VBI: ++#ifdef CX18_ENCODER_PARSES_SLICED ++ captype = cx18_raw_vbi(cx) ? ++ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; ++#else ++ /* ++ * Currently we set things up so that Sliced VBI from the ++ * digitizer is handled as Raw VBI by the encoder ++ */ ++ captype = CAPTURE_CHANNEL_TYPE_VBI; ++#endif ++ cx->vbi.frame = 0; ++ cx->vbi.inserted_frame = 0; ++ memset(cx->vbi.sliced_mpeg_size, ++ 0, sizeof(cx->vbi.sliced_mpeg_size)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* Clear Streamoff flags in case left from last capture */ ++ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); ++ ++ cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); ++ s->handle = data[0]; ++ cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); ++ ++ /* ++ * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and ++ * set up all the parameters, as it is not obvious which parameters the ++ * firmware shares across capture channel types and which it does not. ++ * ++ * Some of the cx18_vapi() calls below apply to only certain capture ++ * channel types. We're hoping there's no harm in calling most of them ++ * anyway, as long as the values are all consistent. Setting some ++ * shared parameters will have no effect once an analog capture channel ++ * has started streaming. ++ */ ++ if (captype != CAPTURE_CHANNEL_TYPE_TS) { ++ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); ++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); ++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); ++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); ++ ++ /* ++ * Audio related reset according to ++ * Documentation/video4linux/cx2341x/fw-encoder-api.txt ++ */ ++ if (atomic_read(&cx->ana_capturing) == 0) ++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, ++ s->handle, 12); ++ ++ /* ++ * Number of lines for Field 1 & Field 2 according to ++ * Documentation/video4linux/cx2341x/fw-encoder-api.txt ++ * Field 1 is 312 for 625 line systems in BT.656 ++ * Field 2 is 313 for 625 line systems in BT.656 ++ */ ++ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, ++ s->handle, 312, 313); ++ ++ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) ++ cx18_vbi_setup(s); ++ ++ /* ++ * Select to receive I, P, and B frame index entries, if the ++ * index stream is enabled. Otherwise disable index entry ++ * generation. ++ */ ++ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; ++ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2, ++ s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); ++ ++ /* Call out to the common CX2341x API setup for user controls */ ++ cx->cxhdl.priv = s; ++ cx2341x_handler_setup(&cx->cxhdl); ++ ++ /* ++ * When starting a capture and we're set for radio, ++ * ensure the video is muted, despite the user control. ++ */ ++ if (!cx->cxhdl.video_mute && ++ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) ++ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, ++ (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); ++ ++ /* Enable the Video Format Converter for UYVY 4:2:2 support, ++ * rather than the default HM12 Macroblovk 4:2:0 support. ++ */ ++ if (captype == CAPTURE_CHANNEL_TYPE_YUV) { ++ if (s->pixelformat == V4L2_PIX_FMT_UYVY) ++ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, ++ s->handle, 1); ++ else ++ /* If in doubt, default to HM12 */ ++ cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, ++ s->handle, 0); ++ } ++ } ++ ++ if (atomic_read(&cx->tot_capturing) == 0) { ++ cx2341x_handler_set_busy(&cx->cxhdl, 1); ++ clear_bit(CX18_F_I_EOS, &cx->i_flags); ++ cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); ++ } ++ ++ cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, ++ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, ++ (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); ++ ++ /* Init all the cpu_mdls for this stream */ ++ cx18_stream_configure_mdls(s); ++ _cx18_stream_load_fw_queue(s); ++ ++ /* begin_capture */ ++ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { ++ CX18_DEBUG_WARN("Error starting capture!\n"); ++ /* Ensure we're really not capturing before releasing MDLs */ ++ set_bit(CX18_F_S_STOPPING, &s->s_flags); ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) ++ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); ++ else ++ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); ++ clear_bit(CX18_F_S_STREAMING, &s->s_flags); ++ /* FIXME - CX18_F_S_STREAMOFF as well? */ ++ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); ++ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); ++ s->handle = CX18_INVALID_TASK_HANDLE; ++ clear_bit(CX18_F_S_STOPPING, &s->s_flags); ++ if (atomic_read(&cx->tot_capturing) == 0) { ++ set_bit(CX18_F_I_EOS, &cx->i_flags); ++ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); ++ } ++ return -EINVAL; ++ } ++ ++ /* you're live! sit back and await interrupts :) */ ++ if (captype != CAPTURE_CHANNEL_TYPE_TS) ++ atomic_inc(&cx->ana_capturing); ++ atomic_inc(&cx->tot_capturing); ++ return 0; ++} ++EXPORT_SYMBOL(cx18_start_v4l2_encode_stream); ++ ++void cx18_stop_all_captures(struct cx18 *cx) ++{ ++ int i; ++ ++ for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { ++ struct cx18_stream *s = &cx->streams[i]; ++ ++ if (!cx18_stream_enabled(s)) ++ continue; ++ if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) ++ cx18_stop_v4l2_encode_stream(s, 0); ++ } ++} ++ ++int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) ++{ ++ struct cx18 *cx = s->cx; ++ ++ if (!cx18_stream_enabled(s)) ++ return -EINVAL; ++ ++ /* This function assumes that you are allowed to stop the capture ++ and that we are actually capturing */ ++ ++ CX18_DEBUG_INFO("Stop Capture\n"); ++ ++ if (atomic_read(&cx->tot_capturing) == 0) ++ return 0; ++ ++ set_bit(CX18_F_S_STOPPING, &s->s_flags); ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG) ++ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); ++ else ++ cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); ++ ++ if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { ++ CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); ++ } ++ ++ if (s->type != CX18_ENC_STREAM_TYPE_TS) ++ atomic_dec(&cx->ana_capturing); ++ atomic_dec(&cx->tot_capturing); ++ ++ /* Clear capture and no-read bits */ ++ clear_bit(CX18_F_S_STREAMING, &s->s_flags); ++ ++ /* Tell the CX23418 it can't use our buffers anymore */ ++ cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); ++ ++ cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); ++ s->handle = CX18_INVALID_TASK_HANDLE; ++ clear_bit(CX18_F_S_STOPPING, &s->s_flags); ++ ++ if (atomic_read(&cx->tot_capturing) > 0) ++ return 0; ++ ++ cx2341x_handler_set_busy(&cx->cxhdl, 0); ++ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); ++ wake_up(&s->waitq); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream); ++ ++u32 cx18_find_handle(struct cx18 *cx) ++{ ++ int i; ++ ++ /* find first available handle to be used for global settings */ ++ for (i = 0; i < CX18_MAX_STREAMS; i++) { ++ struct cx18_stream *s = &cx->streams[i]; ++ ++ if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE)) ++ return s->handle; ++ } ++ return CX18_INVALID_TASK_HANDLE; ++} ++ ++struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle) ++{ ++ int i; ++ struct cx18_stream *s; ++ ++ if (handle == CX18_INVALID_TASK_HANDLE) ++ return NULL; ++ ++ for (i = 0; i < CX18_MAX_STREAMS; i++) { ++ s = &cx->streams[i]; ++ if (s->handle != handle) ++ continue; ++ if (cx18_stream_enabled(s)) ++ return s; ++ } ++ return NULL; ++} +diff --git a/drivers/media/pci/cx18/cx18-streams.h b/drivers/media/pci/cx18/cx18-streams.h +new file mode 100644 +index 0000000..713b0e6 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-streams.h +@@ -0,0 +1,62 @@ ++/* ++ * cx18 init/start/stop/exit stream functions ++ * ++ * Derived from ivtv-streams.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * Copyright (C) 2008 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++u32 cx18_find_handle(struct cx18 *cx); ++struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle); ++int cx18_streams_setup(struct cx18 *cx); ++int cx18_streams_register(struct cx18 *cx); ++void cx18_streams_cleanup(struct cx18 *cx, int unregister); ++ ++#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3) ++void cx18_stream_rotate_idx_mdls(struct cx18 *cx); ++ ++static inline bool cx18_stream_enabled(struct cx18_stream *s) ++{ ++ return s->video_dev || ++ (s->dvb && s->dvb->enabled) || ++ (s->type == CX18_ENC_STREAM_TYPE_IDX && ++ s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0); ++} ++ ++/* Related to submission of mdls to firmware */ ++static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) ++{ ++ schedule_work(&s->out_work_order); ++} ++ ++static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, ++ struct cx18_mdl *mdl) ++{ ++ /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */ ++ cx18_enqueue(s, mdl, &s->q_free); ++ cx18_stream_load_fw_queue(s); ++} ++ ++void cx18_out_work_handler(struct work_struct *work); ++ ++/* Capture related */ ++int cx18_start_v4l2_encode_stream(struct cx18_stream *s); ++int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); ++ ++void cx18_stop_all_captures(struct cx18 *cx); +diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c +new file mode 100644 +index 0000000..6d3121f +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-vbi.c +@@ -0,0 +1,277 @@ ++/* ++ * cx18 Vertical Blank Interval support functions ++ * ++ * Derived from ivtv-vbi.c ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-vbi.h" ++#include "cx18-ioctl.h" ++#include "cx18-queue.h" ++ ++/* ++ * Raster Reference/Protection (RP) bytes, used in Start/End Active ++ * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start ++ * of VBI sample or VBI ancillary data regions in the digitial ratser line. ++ * ++ * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0 ++ */ ++static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */ ++static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */ ++ ++static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) ++{ ++ int line = 0; ++ int i; ++ u32 linemask[2] = { 0, 0 }; ++ unsigned short size; ++ static const u8 mpeg_hdr_data[] = { ++ /* MPEG-2 Program Pack */ ++ 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */ ++ 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */ ++ 0x01, 0xd1, 0xd3, /* Mux Rate, markers */ ++ 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */ ++ /* MPEG-2 Private Stream 1 PES Packet */ ++ 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */ ++ 0x00, 0x1a, /* length */ ++ 0x84, 0x80, 0x07, /* flags, hdr data len */ ++ 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */ ++ 0xff, 0xff /* stuffing */ ++ }; ++ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ ++ int idx = cx->vbi.frame % CX18_VBI_FRAMES; ++ u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0]; ++ ++ for (i = 0; i < lines; i++) { ++ struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i; ++ int f, l; ++ ++ if (sdata->id == 0) ++ continue; ++ ++ l = sdata->line - 6; ++ f = sdata->field; ++ if (f) ++ l += 18; ++ if (l < 32) ++ linemask[0] |= (1 << l); ++ else ++ linemask[1] |= (1 << (l - 32)); ++ dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id); ++ memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42); ++ line++; ++ } ++ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); ++ if (line == 36) { ++ /* All lines are used, so there is no space for the linemask ++ (the max size of the VBI data is 36 * 43 + 4 bytes). ++ So in this case we use the magic number 'ITV0'. */ ++ memcpy(dst + sd, "ITV0", 4); ++ memcpy(dst + sd + 4, dst + sd + 12, line * 43); ++ size = 4 + ((43 * line + 3) & ~3); ++ } else { ++ memcpy(dst + sd, "itv0", 4); ++ cpu_to_le32s(&linemask[0]); ++ cpu_to_le32s(&linemask[1]); ++ memcpy(dst + sd + 4, &linemask[0], 8); ++ size = 12 + ((43 * line + 3) & ~3); ++ } ++ dst[4+16] = (size + 10) >> 8; ++ dst[5+16] = (size + 10) & 0xff; ++ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); ++ dst[10+16] = (pts_stamp >> 22) & 0xff; ++ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); ++ dst[12+16] = (pts_stamp >> 7) & 0xff; ++ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); ++ cx->vbi.sliced_mpeg_size[idx] = sd + size; ++} ++ ++/* Compress raw VBI format, removes leading SAV codes and surplus space ++ after the frame. Returns new compressed size. */ ++/* FIXME - this function ignores the input size. */ ++static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) ++{ ++ u32 line_size = vbi_active_samples; ++ u32 lines = cx->vbi.count * 2; ++ u8 *q = buf; ++ u8 *p; ++ int i; ++ ++ /* Skip the header */ ++ buf += hdr_size; ++ ++ for (i = 0; i < lines; i++) { ++ p = buf + i * line_size; ++ ++ /* Look for SAV code */ ++ if (p[0] != 0xff || p[1] || p[2] || ++ (p[3] != raw_vbi_sav_rp[0] && ++ p[3] != raw_vbi_sav_rp[1])) ++ break; ++ if (i == lines - 1) { ++ /* last line is hdr_size bytes short - extrapolate it */ ++ memcpy(q, p + 4, line_size - 4 - hdr_size); ++ q += line_size - 4 - hdr_size; ++ p += line_size - hdr_size - 1; ++ memset(q, (int) *p, hdr_size); ++ } else { ++ memcpy(q, p + 4, line_size - 4); ++ q += line_size - 4; ++ } ++ } ++ return lines * (line_size - 4); ++} ++ ++static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, ++ const u32 hdr_size) ++{ ++ struct v4l2_decode_vbi_line vbi; ++ int i; ++ u32 line = 0; ++ u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz ++ : vbi_hblank_samples_50Hz; ++ ++ /* find the first valid line */ ++ for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { ++ if (buf[0] == 0xff && !buf[1] && !buf[2] && ++ (buf[3] == sliced_vbi_eav_rp[0] || ++ buf[3] == sliced_vbi_eav_rp[1])) ++ break; ++ } ++ ++ /* ++ * The last line is short by hdr_size bytes, but for the remaining ++ * checks against size, we pretend that it is not, by counting the ++ * header bytes we knowingly skipped ++ */ ++ size -= (i - hdr_size); ++ if (size < line_size) ++ return line; ++ ++ for (i = 0; i < size / line_size; i++) { ++ u8 *p = buf + i * line_size; ++ ++ /* Look for EAV code */ ++ if (p[0] != 0xff || p[1] || p[2] || ++ (p[3] != sliced_vbi_eav_rp[0] && ++ p[3] != sliced_vbi_eav_rp[1])) ++ continue; ++ vbi.p = p + 4; ++ v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi); ++ if (vbi.type) { ++ cx->vbi.sliced_data[line].id = vbi.type; ++ cx->vbi.sliced_data[line].field = vbi.is_second_field; ++ cx->vbi.sliced_data[line].line = vbi.line; ++ memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42); ++ line++; ++ } ++ } ++ return line; ++} ++ ++static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf) ++{ ++ /* ++ * The CX23418 provides a 12 byte header in its raw VBI buffers to us: ++ * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp] ++ */ ++ struct vbi_data_hdr { ++ __be32 magic; ++ __be32 unknown; ++ __be32 pts; ++ } *hdr = (struct vbi_data_hdr *) buf->buf; ++ ++ u8 *p = (u8 *) buf->buf; ++ u32 size = buf->bytesused; ++ u32 pts; ++ int lines; ++ ++ /* ++ * The CX23418 sends us data that is 32 bit little-endian swapped, ++ * but we want the raw VBI bytes in the order they were in the raster ++ * line. This has a side effect of making the header big endian ++ */ ++ cx18_buf_swap(buf); ++ ++ /* Raw VBI data */ ++ if (cx18_raw_vbi(cx)) { ++ ++ size = buf->bytesused = ++ compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr)); ++ ++ /* ++ * Hack needed for compatibility with old VBI software. ++ * Write the frame # at the last 4 bytes of the frame ++ */ ++ p += size - 4; ++ memcpy(p, &cx->vbi.frame, 4); ++ cx->vbi.frame++; ++ return; ++ } ++ ++ /* Sliced VBI data with data insertion */ ++ ++ pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts) ++ : 0; ++ ++ lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr)); ++ ++ /* always return at least one empty line */ ++ if (lines == 0) { ++ cx->vbi.sliced_data[0].id = 0; ++ cx->vbi.sliced_data[0].line = 0; ++ cx->vbi.sliced_data[0].field = 0; ++ lines = 1; ++ } ++ buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]); ++ memcpy(p, &cx->vbi.sliced_data[0], size); ++ ++ if (cx->vbi.insert_mpeg) ++ copy_vbi_data(cx, lines, pts); ++ cx->vbi.frame++; ++} ++ ++void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, ++ int streamtype) ++{ ++ struct cx18_buffer *buf; ++ u32 orig_used; ++ ++ if (streamtype != CX18_ENC_STREAM_TYPE_VBI) ++ return; ++ ++ /* ++ * Big assumption here: ++ * Every buffer hooked to the MDL's buf_list is a complete VBI frame ++ * that ends at the end of the buffer. ++ * ++ * To assume anything else would make the code in this file ++ * more complex, or require extra memcpy()'s to make the ++ * buffers satisfy the above assumption. It's just simpler to set ++ * up the encoder buffer transfers to make the assumption true. ++ */ ++ list_for_each_entry(buf, &mdl->buf_list, list) { ++ orig_used = buf->bytesused; ++ if (orig_used == 0) ++ break; ++ _cx18_process_vbi_data(cx, buf); ++ mdl->bytesused -= (orig_used - buf->bytesused); ++ } ++} +diff --git a/drivers/media/pci/cx18/cx18-vbi.h b/drivers/media/pci/cx18/cx18-vbi.h +new file mode 100644 +index 0000000..b365cf4 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-vbi.h +@@ -0,0 +1,26 @@ ++/* ++ * cx18 Vertical Blank Interval support functions ++ * ++ * Derived from ivtv-vbi.h ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, ++ int streamtype); ++int cx18_used_line(struct cx18 *cx, int line, int field); +diff --git a/drivers/media/pci/cx18/cx18-version.h b/drivers/media/pci/cx18/cx18-version.h +new file mode 100644 +index 0000000..fed48b6 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-version.h +@@ -0,0 +1,28 @@ ++/* ++ * cx18 driver version information ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef CX18_VERSION_H ++#define CX18_VERSION_H ++ ++#define CX18_DRIVER_NAME "cx18" ++#define CX18_VERSION "1.5.1" ++ ++#endif +diff --git a/drivers/media/pci/cx18/cx18-video.c b/drivers/media/pci/cx18/cx18-video.c +new file mode 100644 +index 0000000..6dc84aa +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-video.c +@@ -0,0 +1,32 @@ ++/* ++ * cx18 video interface functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include "cx18-driver.h" ++#include "cx18-video.h" ++#include "cx18-cards.h" ++ ++void cx18_video_set_io(struct cx18 *cx) ++{ ++ int inp = cx->active_input; ++ ++ v4l2_subdev_call(cx->sd_av, video, s_routing, ++ cx->card->video_inputs[inp].video_input, 0, 0); ++} +diff --git a/drivers/media/pci/cx18/cx18-video.h b/drivers/media/pci/cx18/cx18-video.h +new file mode 100644 +index 0000000..529006a +--- /dev/null ++++ b/drivers/media/pci/cx18/cx18-video.h +@@ -0,0 +1,22 @@ ++/* ++ * cx18 video interface functions ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++void cx18_video_set_io(struct cx18 *cx); +diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h +new file mode 100644 +index 0000000..767a8d2 +--- /dev/null ++++ b/drivers/media/pci/cx18/cx23418.h +@@ -0,0 +1,492 @@ ++/* ++ * cx18 header containing common defines. ++ * ++ * Copyright (C) 2007 Hans Verkuil ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#ifndef CX23418_H ++#define CX23418_H ++ ++#include ++ ++#define MGR_CMD_MASK 0x40000000 ++/* The MSB of the command code indicates that this is the completion of a ++ command */ ++#define MGR_CMD_MASK_ACK (MGR_CMD_MASK | 0x80000000) ++ ++/* Description: This command creates a new instance of a certain task ++ IN[0] - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is ++ the processor on which the task YYY will be created ++ OUT[0] - Task handle. This handle is passed along with commands to ++ dispatch to the right instance of the task ++ ReturnCode - One of the ERR_SYS_... */ ++#define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001) ++ ++/* Description: This command destroys an instance of a task ++ IN[0] - Task handle. Hanlde of the task to destroy ++ ReturnCode - One of the ERR_SYS_... */ ++#define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002) ++ ++/* All commands for CPU have the following mask set */ ++#define CPU_CMD_MASK 0x20000000 ++#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000) ++#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000) ++#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000) ++#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000) ++ ++#define EPU_CMD_MASK 0x02000000 ++#define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000) ++#define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000) ++ ++#define APU_CMD_MASK 0x10000000 ++#define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000) ++ ++#define CX18_APU_ENCODING_METHOD_MPEG (0 << 28) ++#define CX18_APU_ENCODING_METHOD_AC3 (1 << 28) ++ ++/* Description: Command APU to start audio ++ IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?) ++ IN[1] - caller buffer address, or 0 ++ ReturnCode - ??? */ ++#define CX18_APU_START (APU_CMD_MASK | 0x01) ++ ++/* Description: Command APU to stop audio ++ IN[0] - encoding method to stop ++ ReturnCode - ??? */ ++#define CX18_APU_STOP (APU_CMD_MASK | 0x02) ++ ++/* Description: Command APU to reset the AI ++ ReturnCode - ??? */ ++#define CX18_APU_RESETAI (APU_CMD_MASK | 0x05) ++ ++/* Description: This command indicates that a Memory Descriptor List has been ++ filled with the requested channel type ++ IN[0] - Task handle. Handle of the task ++ IN[1] - Offset of the MDL_ACK from the beginning of the local DDR. ++ IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1] ++ ReturnCode - One of the ERR_DE_... */ ++#define CX18_EPU_DMA_DONE (EPU_CMD_MASK_DE | 0x0001) ++ ++/* Something interesting happened ++ IN[0] - A value to log ++ IN[1] - An offset of a string in the MiniMe memory; ++ 0/zero/NULL means "I have nothing to say" */ ++#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003) ++ ++/* Reads memory/registers (32-bit) ++ IN[0] - Address ++ OUT[1] - Value */ ++#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003) ++ ++/* Description: This command starts streaming with the set channel type ++ IN[0] - Task handle. Handle of the task to start ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_CAPTURE_START (CPU_CMD_MASK_CAPTURE | 0x0002) ++ ++/* Description: This command stops streaming with the set channel type ++ IN[0] - Task handle. Handle of the task to stop ++ IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only) ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_CAPTURE_STOP (CPU_CMD_MASK_CAPTURE | 0x0003) ++ ++/* Description: This command pauses streaming with the set channel type ++ IN[0] - Task handle. Handle of the task to pause ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_CAPTURE_PAUSE (CPU_CMD_MASK_CAPTURE | 0x0007) ++ ++/* Description: This command resumes streaming with the set channel type ++ IN[0] - Task handle. Handle of the task to resume ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_CAPTURE_RESUME (CPU_CMD_MASK_CAPTURE | 0x0008) ++ ++#define CAPTURE_CHANNEL_TYPE_NONE 0 ++#define CAPTURE_CHANNEL_TYPE_MPEG 1 ++#define CAPTURE_CHANNEL_TYPE_INDEX 2 ++#define CAPTURE_CHANNEL_TYPE_YUV 3 ++#define CAPTURE_CHANNEL_TYPE_PCM 4 ++#define CAPTURE_CHANNEL_TYPE_VBI 5 ++#define CAPTURE_CHANNEL_TYPE_SLICED_VBI 6 ++#define CAPTURE_CHANNEL_TYPE_TS 7 ++#define CAPTURE_CHANNEL_TYPE_MAX 15 ++ ++/* Description: This command sets the channel type. This can only be done ++ when stopped. ++ IN[0] - Task handle. Handle of the task to start ++ IN[1] - Channel Type. See Below. ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_CHANNEL_TYPE (CPU_CMD_MASK_CAPTURE + 1) ++ ++/* Description: Set stream output type ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - type ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_STREAM_OUTPUT_TYPE (CPU_CMD_MASK_CAPTURE | 0x0012) ++ ++/* Description: Set video input resolution and frame rate ++ IN[0] - task handle ++ IN[1] - reserved ++ IN[2] - reserved ++ IN[3] - reserved ++ IN[4] - reserved ++ IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VIDEO_IN (CPU_CMD_MASK_CAPTURE | 0x0004) ++ ++/* Description: Set video frame rate ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - video bit rate mode ++ IN[2] - video average rate ++ IN[3] - video peak rate ++ IN[4] - system mux rate ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VIDEO_RATE (CPU_CMD_MASK_CAPTURE | 0x0005) ++ ++/* Description: Set video output resolution ++ IN[0] - task handle ++ IN[1] - horizontal size ++ IN[2] - vertical size ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VIDEO_RESOLUTION (CPU_CMD_MASK_CAPTURE | 0x0006) ++ ++/* Description: This command set filter parameters ++ IN[0] - Task handle. Handle of the task ++ IN[1] - type, 0 - temporal, 1 - spatial, 2 - median ++ IN[2] - mode, temporal/spatial: 0 - disable, 1 - static, 2 - dynamic ++ median: 0 = disable, 1 = horizontal, 2 = vertical, ++ 3 = horizontal/vertical, 4 = diagonal ++ IN[3] - strength, temporal 0 - 31, spatial 0 - 15 ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_FILTER_PARAM (CPU_CMD_MASK_CAPTURE | 0x0009) ++ ++/* Description: This command set spatial filter type ++ IN[0] - Task handle. ++ IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only, ++ 3 = 2D H/V separable, 4 = 2D symmetric non-separable ++ IN[2] - chroma type: 0 - disable, 1 = 1D horizontal ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C) ++ ++/* Description: This command set coring levels for median filter ++ IN[0] - Task handle. ++ IN[1] - luma_high ++ IN[2] - luma_low ++ IN[3] - chroma_high ++ IN[4] - chroma_low ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E) ++ ++/* Description: This command set the picture type mask for index file ++ IN[0] - Task handle (ignored by firmware) ++ IN[1] - 0 = disable index file output ++ 1 = output I picture ++ 2 = P picture ++ 4 = B picture ++ other = illegal */ ++#define CX18_CPU_SET_INDEXTABLE (CPU_CMD_MASK_CAPTURE | 0x0010) ++ ++/* Description: Set audio parameters ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - audio parameter ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_AUDIO_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0011) ++ ++/* Description: Set video mute ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - bit31-24: muteYvalue ++ bit23-16: muteUvalue ++ bit15-8: muteVvalue ++ bit0: 1:mute, 0: unmute ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VIDEO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0013) ++ ++/* Description: Set audio mute ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - mute/unmute ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_AUDIO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0014) ++ ++/* Description: Set stream output type ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - subType ++ SET_INITIAL_SCR 1 ++ SET_QUALITY_MODE 2 ++ SET_VIM_PROTECT_MODE 3 ++ SET_PTS_CORRECTION 4 ++ SET_USB_FLUSH_MODE 5 ++ SET_MERAQPAR_ENABLE 6 ++ SET_NAV_PACK_INSERTION 7 ++ SET_SCENE_CHANGE_ENABLE 8 ++ IN[2] - parameter 1 ++ IN[3] - parameter 2 ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_MISC_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0015) ++ ++/* Description: Set raw VBI parameters ++ IN[0] - Task handle ++ IN[1] - No. of input lines per field: ++ bit[15:0]: field 1, ++ bit[31:16]: field 2 ++ IN[2] - No. of input bytes per line ++ IN[3] - No. of output frames per transfer ++ IN[4] - start code ++ IN[5] - stop code ++ ReturnCode */ ++#define CX18_CPU_SET_RAW_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0016) ++ ++/* Description: Set capture line No. ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - height1 ++ IN[2] - height2 ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_CAPTURE_LINE_NO (CPU_CMD_MASK_CAPTURE | 0x0017) ++ ++/* Description: Set copyright ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - copyright ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_COPYRIGHT (CPU_CMD_MASK_CAPTURE | 0x0018) ++ ++/* Description: Set audio PID ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - PID ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_AUDIO_PID (CPU_CMD_MASK_CAPTURE | 0x0019) ++ ++/* Description: Set video PID ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - PID ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VIDEO_PID (CPU_CMD_MASK_CAPTURE | 0x001A) ++ ++/* Description: Set Vertical Crop Line ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - Line ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_VER_CROP_LINE (CPU_CMD_MASK_CAPTURE | 0x001B) ++ ++/* Description: Set COP structure ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - M ++ IN[2] - N ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_GOP_STRUCTURE (CPU_CMD_MASK_CAPTURE | 0x001C) ++ ++/* Description: Set Scene Change Detection ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - scene change ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_SCENE_CHANGE_DETECTION (CPU_CMD_MASK_CAPTURE | 0x001D) ++ ++/* Description: Set Aspect Ratio ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - AspectRatio ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_ASPECT_RATIO (CPU_CMD_MASK_CAPTURE | 0x001E) ++ ++/* Description: Set Skip Input Frame ++ IN[0] - task handle. Handle of the task to start ++ IN[1] - skip input frames ++ ReturnCode - One of the ERR_CAPTURE_... */ ++#define CX18_CPU_SET_SKIP_INPUT_FRAME (CPU_CMD_MASK_CAPTURE | 0x001F) ++ ++/* Description: Set sliced VBI parameters - ++ Note This API will only apply to MPEG and Sliced VBI Channels ++ IN[0] - Task handle ++ IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext ++ IN[2] - start / stop line ++ bit[15:0] start line number ++ bit[31:16] stop line number ++ IN[3] - number of output frames per interrupt ++ IN[4] - VBI insertion mode ++ bit 0: output user data, 1 - enable ++ bit 1: output private stream, 1 - enable ++ bit 2: mux option, 0 - in GOP, 1 - in picture ++ bit[7:0] private stream ID ++ IN[5] - insertion period while mux option is in picture ++ ReturnCode - VBI data offset */ ++#define CX18_CPU_SET_SLICED_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0020) ++ ++/* Description: Set the user data place holder ++ IN[0] - type of data (0 for user) ++ IN[1] - Stuffing period ++ IN[2] - ID data size in word (less than 10) ++ IN[3] - Pointer to ID buffer */ ++#define CX18_CPU_SET_USERDATA_PLACE_HOLDER (CPU_CMD_MASK_CAPTURE | 0x0021) ++ ++ ++/* Description: ++ In[0] Task Handle ++ return parameter: ++ Out[0] Reserved ++ Out[1] Video PTS bit[32:2] of last output video frame. ++ Out[2] Video PTS bit[ 1:0] of last output video frame. ++ Out[3] Hardware Video PTS counter bit[31:0], ++ these bits get incremented on every 90kHz clock tick. ++ Out[4] Hardware Video PTS counter bit32, ++ these bits get incremented on every 90kHz clock tick. ++ ReturnCode */ ++#define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) ++ ++/* Description: Set VFC parameters ++ IN[0] - task handle ++ IN[1] - VFC enable flag, 1 - enable, 0 - disable ++*/ ++#define CX18_CPU_SET_VFC_PARAM (CPU_CMD_MASK_CAPTURE | 0x0023) ++ ++/* Below is the list of commands related to the data exchange */ ++#define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) ++ ++/* Description: This command provides the physical base address of the local ++ DDR as viewed by EPU ++ IN[0] - Physical offset where EPU has the local DDR mapped ++ ReturnCode - One of the ERR_DE_... */ ++#define CPU_CMD_DE_SetBase (CPU_CMD_MASK_DE | 0x0001) ++ ++/* Description: This command provides the offsets in the device memory where ++ the 2 cx18_mdl_ack blocks reside ++ IN[0] - Task handle. Handle of the task to start ++ IN[1] - Offset of the first cx18_mdl_ack from the beginning of the ++ local DDR. ++ IN[2] - Offset of the second cx18_mdl_ack from the beginning of the ++ local DDR. ++ ReturnCode - One of the ERR_DE_... */ ++#define CX18_CPU_DE_SET_MDL_ACK (CPU_CMD_MASK_DE | 0x0002) ++ ++/* Description: This command provides the offset to a Memory Descriptor List ++ IN[0] - Task handle. Handle of the task to start ++ IN[1] - Offset of the MDL from the beginning of the local DDR. ++ IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1] ++ IN[3] - Buffer ID ++ IN[4] - Total buffer length ++ ReturnCode - One of the ERR_DE_... */ ++#define CX18_CPU_DE_SET_MDL (CPU_CMD_MASK_DE | 0x0005) ++ ++/* Description: This command requests return of all current Memory ++ Descriptor Lists to the driver ++ IN[0] - Task handle. Handle of the task to start ++ ReturnCode - One of the ERR_DE_... */ ++#define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006) ++ ++/* Description: This command signals the cpu that the dat buffer has been ++ consumed and ready for re-use. ++ IN[0] - Task handle. Handle of the task ++ IN[1] - Offset of the data block from the beginning of the local DDR. ++ IN[2] - Number of bytes in the data block ++ ReturnCode - One of the ERR_DE_... */ ++/* #define CX18_CPU_DE_RELEASE_BUFFER (CPU_CMD_MASK_DE | 0x0007) */ ++ ++/* No Error / Success */ ++#define CNXT_OK 0x000000 ++ ++/* Received unknown command */ ++#define CXERR_UNK_CMD 0x000001 ++ ++/* First parameter in the command is invalid */ ++#define CXERR_INVALID_PARAM1 0x000002 ++ ++/* Second parameter in the command is invalid */ ++#define CXERR_INVALID_PARAM2 0x000003 ++ ++/* Device interface is not open/found */ ++#define CXERR_DEV_NOT_FOUND 0x000004 ++ ++/* Requested function is not implemented/available */ ++#define CXERR_NOTSUPPORTED 0x000005 ++ ++/* Invalid pointer is provided */ ++#define CXERR_BADPTR 0x000006 ++ ++/* Unable to allocate memory */ ++#define CXERR_NOMEM 0x000007 ++ ++/* Object/Link not found */ ++#define CXERR_LINK 0x000008 ++ ++/* Device busy, command cannot be executed */ ++#define CXERR_BUSY 0x000009 ++ ++/* File/device/handle is not open. */ ++#define CXERR_NOT_OPEN 0x00000A ++ ++/* Value is out of range */ ++#define CXERR_OUTOFRANGE 0x00000B ++ ++/* Buffer overflow */ ++#define CXERR_OVERFLOW 0x00000C ++ ++/* Version mismatch */ ++#define CXERR_BADVER 0x00000D ++ ++/* Operation timed out */ ++#define CXERR_TIMEOUT 0x00000E ++ ++/* Operation aborted */ ++#define CXERR_ABORT 0x00000F ++ ++/* Specified I2C device not found for read/write */ ++#define CXERR_I2CDEV_NOTFOUND 0x000010 ++ ++/* Error in I2C data xfer (but I2C device is present) */ ++#define CXERR_I2CDEV_XFERERR 0x000011 ++ ++/* Chanel changing component not ready */ ++#define CXERR_CHANNELNOTREADY 0x000012 ++ ++/* PPU (Presensation/Decoder) mail box is corrupted */ ++#define CXERR_PPU_MB_CORRUPT 0x000013 ++ ++/* CPU (Capture/Encoder) mail box is corrupted */ ++#define CXERR_CPU_MB_CORRUPT 0x000014 ++ ++/* APU (Audio) mail box is corrupted */ ++#define CXERR_APU_MB_CORRUPT 0x000015 ++ ++/* Unable to open file for reading */ ++#define CXERR_FILE_OPEN_READ 0x000016 ++ ++/* Unable to open file for writing */ ++#define CXERR_FILE_OPEN_WRITE 0x000017 ++ ++/* Unable to find the I2C section specified */ ++#define CXERR_I2C_BADSECTION 0x000018 ++ ++/* Error in I2C data xfer (but I2C device is present) */ ++#define CXERR_I2CDEV_DATALOW 0x000019 ++ ++/* Error in I2C data xfer (but I2C device is present) */ ++#define CXERR_I2CDEV_CLOCKLOW 0x00001A ++ ++/* No Interrupt received from HW (for I2C access) */ ++#define CXERR_NO_HW_I2C_INTR 0x00001B ++ ++/* RPU is not ready to accept commands! */ ++#define CXERR_RPU_NOT_READY 0x00001C ++ ++/* RPU is not ready to accept commands! */ ++#define CXERR_RPU_NO_ACK 0x00001D ++ ++/* The are no buffers ready. Try again soon! */ ++#define CXERR_NODATA_AGAIN 0x00001E ++ ++/* The stream is stopping. Function not allowed now! */ ++#define CXERR_STOPPING_STATUS 0x00001F ++ ++/* Trying to access hardware when the power is turned OFF */ ++#define CXERR_DEVPOWER_OFF 0x000020 ++ ++#endif /* CX23418_H */ +diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig +new file mode 100644 +index 0000000..b3688aa +--- /dev/null ++++ b/drivers/media/pci/cx23885/Kconfig +@@ -0,0 +1,53 @@ ++config VIDEO_CX23885 ++ tristate "Conexant cx23885 (2388x successor) support" ++ depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT && SND ++ select SND_PCM ++ select I2C_ALGOBIT ++ select VIDEO_BTCX ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ depends on RC_CORE ++ select VIDEOBUF_DVB ++ select VIDEOBUF_DMA_SG ++ select VIDEO_CX25840 ++ select VIDEO_CX2341X ++ select DVB_DIB7000P if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1409 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10071 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_A8293 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_MT2063 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_XC2028 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for Conexant 23885 based ++ TV cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx23885 ++ ++config MEDIA_ALTERA_CI ++ tristate "Altera FPGA based CI module" ++ depends on VIDEO_CX23885 && DVB_CORE ++ select ALTERA_STAPL ++ ---help--- ++ An Altera FPGA CI module for NetUP Dual DVB-T/C RF CI card. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called altera-ci +diff --git a/drivers/media/pci/cx23885/Makefile b/drivers/media/pci/cx23885/Makefile +new file mode 100644 +index 0000000..a2cbdcf +--- /dev/null ++++ b/drivers/media/pci/cx23885/Makefile +@@ -0,0 +1,15 @@ ++cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ ++ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ ++ cx23885-ioctl.o cx23885-ir.o cx23885-av.o cx23885-input.o \ ++ cx23888-ir.o netup-init.o cimax2.o netup-eeprom.o \ ++ cx23885-f300.o cx23885-alsa.o ++ ++obj-$(CONFIG_VIDEO_CX23885) += cx23885.o ++obj-$(CONFIG_MEDIA_ALTERA_CI) += altera-ci.o ++ ++ccflags-y += -Idrivers/media/i2c ++ccflags-y += -Idrivers/media/tuners ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/dvb-frontends ++ ++ccflags-y += $(extra-cflags-y) $(extra-cflags-m) +diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c +new file mode 100644 +index 0000000..2926f7f +--- /dev/null ++++ b/drivers/media/pci/cx23885/altera-ci.c +@@ -0,0 +1,840 @@ ++/* ++ * altera-ci.c ++ * ++ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card ++ * ++ * Copyright (C) 2010,2011 NetUP Inc. ++ * Copyright (C) 2010,2011 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++/* ++ * currently cx23885 GPIO's used. ++ * GPIO-0 ~INT in ++ * GPIO-1 TMS out ++ * GPIO-2 ~reset chips out ++ * GPIO-3 to GPIO-10 data/addr for CA in/out ++ * GPIO-11 ~CS out ++ * GPIO-12 AD_RG out ++ * GPIO-13 ~WR out ++ * GPIO-14 ~RD out ++ * GPIO-15 ~RDY in ++ * GPIO-16 TCK out ++ * GPIO-17 TDO in ++ * GPIO-18 TDI out ++ */ ++/* ++ * Bit definitions for MC417_RWD and MC417_OEN registers ++ * bits 31-16 ++ * +-----------+ ++ * | Reserved | ++ * +-----------+ ++ * bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 ++ * +-------+-------+-------+-------+-------+-------+-------+-------+ ++ * | TDI | TDO | TCK | RDY# | #RD | #WR | AD_RG | #CS | ++ * +-------+-------+-------+-------+-------+-------+-------+-------+ ++ * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 ++ * +-------+-------+-------+-------+-------+-------+-------+-------+ ++ * | DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| ++ * +-------+-------+-------+-------+-------+-------+-------+-------+ ++ */ ++#include ++#include ++#include "altera-ci.h" ++#include "dvb_ca_en50221.h" ++ ++/* FPGA regs */ ++#define NETUP_CI_INT_CTRL 0x00 ++#define NETUP_CI_BUSCTRL2 0x01 ++#define NETUP_CI_ADDR0 0x04 ++#define NETUP_CI_ADDR1 0x05 ++#define NETUP_CI_DATA 0x06 ++#define NETUP_CI_BUSCTRL 0x07 ++#define NETUP_CI_PID_ADDR0 0x08 ++#define NETUP_CI_PID_ADDR1 0x09 ++#define NETUP_CI_PID_DATA 0x0a ++#define NETUP_CI_TSA_DIV 0x0c ++#define NETUP_CI_TSB_DIV 0x0d ++#define NETUP_CI_REVISION 0x0f ++ ++/* const for ci op */ ++#define NETUP_CI_FLG_CTL 1 ++#define NETUP_CI_FLG_RD 1 ++#define NETUP_CI_FLG_AD 1 ++ ++static unsigned int ci_dbg; ++module_param(ci_dbg, int, 0644); ++MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); ++ ++static unsigned int pid_dbg; ++module_param(pid_dbg, int, 0644); ++MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging"); ++ ++MODULE_DESCRIPTION("altera FPGA CI module"); ++MODULE_AUTHOR("Igor M. Liplianin "); ++MODULE_LICENSE("GPL"); ++ ++#define ci_dbg_print(args...) \ ++ do { \ ++ if (ci_dbg) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++#define pid_dbg_print(args...) \ ++ do { \ ++ if (pid_dbg) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++struct altera_ci_state; ++struct netup_hw_pid_filter; ++ ++struct fpga_internal { ++ void *dev; ++ struct mutex fpga_mutex;/* two CI's on the same fpga */ ++ struct netup_hw_pid_filter *pid_filt[2]; ++ struct altera_ci_state *state[2]; ++ struct work_struct work; ++ int (*fpga_rw) (void *dev, int flag, int data, int rw); ++ int cis_used; ++ int filts_used; ++ int strt_wrk; ++}; ++ ++/* stores all private variables for communication with CI */ ++struct altera_ci_state { ++ struct fpga_internal *internal; ++ struct dvb_ca_en50221 ca; ++ int status; ++ int nr; ++}; ++ ++/* stores all private variables for hardware pid filtering */ ++struct netup_hw_pid_filter { ++ struct fpga_internal *internal; ++ struct dvb_demux *demux; ++ /* save old functions */ ++ int (*start_feed)(struct dvb_demux_feed *feed); ++ int (*stop_feed)(struct dvb_demux_feed *feed); ++ ++ int status; ++ int nr; ++}; ++ ++/* internal params node */ ++struct fpga_inode { ++ /* pointer for internal params, one for each pair of CI's */ ++ struct fpga_internal *internal; ++ struct fpga_inode *next_inode; ++}; ++ ++/* first internal params */ ++static struct fpga_inode *fpga_first_inode; ++ ++/* find chip by dev */ ++static struct fpga_inode *find_inode(void *dev) ++{ ++ struct fpga_inode *temp_chip = fpga_first_inode; ++ ++ if (temp_chip == NULL) ++ return temp_chip; ++ ++ /* ++ Search for the last fpga CI chip or ++ find it by dev */ ++ while ((temp_chip != NULL) && ++ (temp_chip->internal->dev != dev)) ++ temp_chip = temp_chip->next_inode; ++ ++ return temp_chip; ++} ++/* check demux */ ++static struct fpga_internal *check_filter(struct fpga_internal *temp_int, ++ void *demux_dev, int filt_nr) ++{ ++ if (temp_int == NULL) ++ return NULL; ++ ++ if ((temp_int->pid_filt[filt_nr]) == NULL) ++ return NULL; ++ ++ if (temp_int->pid_filt[filt_nr]->demux == demux_dev) ++ return temp_int; ++ ++ return NULL; ++} ++ ++/* find chip by demux */ ++static struct fpga_inode *find_dinode(void *demux_dev) ++{ ++ struct fpga_inode *temp_chip = fpga_first_inode; ++ struct fpga_internal *temp_int; ++ ++ /* ++ * Search of the last fpga CI chip or ++ * find it by demux ++ */ ++ while (temp_chip != NULL) { ++ if (temp_chip->internal != NULL) { ++ temp_int = temp_chip->internal; ++ if (check_filter(temp_int, demux_dev, 0)) ++ break; ++ if (check_filter(temp_int, demux_dev, 1)) ++ break; ++ } ++ ++ temp_chip = temp_chip->next_inode; ++ } ++ ++ return temp_chip; ++} ++ ++/* deallocating chip */ ++static void remove_inode(struct fpga_internal *internal) ++{ ++ struct fpga_inode *prev_node = fpga_first_inode; ++ struct fpga_inode *del_node = find_inode(internal->dev); ++ ++ if (del_node != NULL) { ++ if (del_node == fpga_first_inode) { ++ fpga_first_inode = del_node->next_inode; ++ } else { ++ while (prev_node->next_inode != del_node) ++ prev_node = prev_node->next_inode; ++ ++ if (del_node->next_inode == NULL) ++ prev_node->next_inode = NULL; ++ else ++ prev_node->next_inode = ++ prev_node->next_inode->next_inode; ++ } ++ ++ kfree(del_node); ++ } ++} ++ ++/* allocating new chip */ ++static struct fpga_inode *append_internal(struct fpga_internal *internal) ++{ ++ struct fpga_inode *new_node = fpga_first_inode; ++ ++ if (new_node == NULL) { ++ new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); ++ fpga_first_inode = new_node; ++ } else { ++ while (new_node->next_inode != NULL) ++ new_node = new_node->next_inode; ++ ++ new_node->next_inode = ++ kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); ++ if (new_node->next_inode != NULL) ++ new_node = new_node->next_inode; ++ else ++ new_node = NULL; ++ } ++ ++ if (new_node != NULL) { ++ new_node->internal = internal; ++ new_node->next_inode = NULL; ++ } ++ ++ return new_node; ++} ++ ++static int netup_fpga_op_rw(struct fpga_internal *inter, int addr, ++ u8 val, u8 read) ++{ ++ inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0); ++ return inter->fpga_rw(inter->dev, 0, val, read); ++} ++ ++/* flag - mem/io, read - read/write */ ++static int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, ++ u8 flag, u8 read, int addr, u8 val) ++{ ++ ++ struct altera_ci_state *state = en50221->data; ++ struct fpga_internal *inter = state->internal; ++ ++ u8 store; ++ int mem = 0; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0); ++ netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0); ++ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); ++ ++ store &= 0x0f; ++ store |= ((state->nr << 7) | (flag << 6)); ++ ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0); ++ mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__, ++ (read) ? "read" : "write", addr, ++ (flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem", ++ (read) ? mem : val); ++ ++ return mem; ++} ++ ++static int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr) ++{ ++ return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0); ++} ++ ++static int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr, u8 data) ++{ ++ return altera_ci_op_cam(en50221, slot, 0, 0, addr, data); ++} ++ ++static int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr) ++{ ++ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, ++ NETUP_CI_FLG_RD, addr, 0); ++} ++ ++static int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, ++ u8 addr, u8 data) ++{ ++ return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data); ++} ++ ++static int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct altera_ci_state *state = en50221->data; ++ struct fpga_internal *inter = state->internal; ++ /* reasonable timeout for CI reset is 10 seconds */ ++ unsigned long t_out = jiffies + msecs_to_jiffies(9999); ++ int ret; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, ++ (ret & 0xcf) | (1 << (5 - state->nr)), 0); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ for (;;) { ++ mdelay(50); ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, ++ 0, NETUP_CI_FLG_RD); ++ mutex_unlock(&inter->fpga_mutex); ++ ++ if ((ret & (1 << (5 - state->nr))) == 0) ++ break; ++ if (time_after(jiffies, t_out)) ++ break; ++ } ++ ++ ++ ci_dbg_print("%s: %d msecs\n", __func__, ++ jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out)); ++ ++ return 0; ++} ++ ++static int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ /* not implemented */ ++ return 0; ++} ++ ++static int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct altera_ci_state *state = en50221->data; ++ struct fpga_internal *inter = state->internal; ++ int ret; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, ++ (ret & 0x0f) | (1 << (3 - state->nr)), 0); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ return 0; ++} ++ ++/* work handler */ ++static void netup_read_ci_status(struct work_struct *work) ++{ ++ struct fpga_internal *inter = ++ container_of(work, struct fpga_internal, work); ++ int ret; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ mutex_lock(&inter->fpga_mutex); ++ /* ack' irq */ ++ ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD); ++ ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ if (inter->state[1] != NULL) { ++ inter->state[1]->status = ++ ((ret & 1) == 0 ? ++ DVB_CA_EN50221_POLL_CAM_PRESENT | ++ DVB_CA_EN50221_POLL_CAM_READY : 0); ++ ci_dbg_print("%s: setting CI[1] status = 0x%x\n", ++ __func__, inter->state[1]->status); ++ } ++ ++ if (inter->state[0] != NULL) { ++ inter->state[0]->status = ++ ((ret & 2) == 0 ? ++ DVB_CA_EN50221_POLL_CAM_PRESENT | ++ DVB_CA_EN50221_POLL_CAM_READY : 0); ++ ci_dbg_print("%s: setting CI[0] status = 0x%x\n", ++ __func__, inter->state[0]->status); ++ } ++} ++ ++/* CI irq handler */ ++int altera_ci_irq(void *dev) ++{ ++ struct fpga_inode *temp_int = NULL; ++ struct fpga_internal *inter = NULL; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (dev != NULL) { ++ temp_int = find_inode(dev); ++ if (temp_int != NULL) { ++ inter = temp_int->internal; ++ schedule_work(&inter->work); ++ } ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL(altera_ci_irq); ++ ++static int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, ++ int slot, int open) ++{ ++ struct altera_ci_state *state = en50221->data; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ return state->status; ++} ++ ++static void altera_hw_filt_release(void *main_dev, int filt_nr) ++{ ++ struct fpga_inode *temp_int = find_inode(main_dev); ++ struct netup_hw_pid_filter *pid_filt = NULL; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (temp_int != NULL) { ++ pid_filt = temp_int->internal->pid_filt[filt_nr - 1]; ++ /* stored old feed controls */ ++ pid_filt->demux->start_feed = pid_filt->start_feed; ++ pid_filt->demux->stop_feed = pid_filt->stop_feed; ++ ++ if (((--(temp_int->internal->filts_used)) <= 0) && ++ ((temp_int->internal->cis_used) <= 0)) { ++ ++ ci_dbg_print("%s: Actually removing\n", __func__); ++ ++ remove_inode(temp_int->internal); ++ kfree(pid_filt->internal); ++ } ++ ++ kfree(pid_filt); ++ ++ } ++ ++} ++EXPORT_SYMBOL(altera_hw_filt_release); ++ ++void altera_ci_release(void *dev, int ci_nr) ++{ ++ struct fpga_inode *temp_int = find_inode(dev); ++ struct altera_ci_state *state = NULL; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (temp_int != NULL) { ++ state = temp_int->internal->state[ci_nr - 1]; ++ altera_hw_filt_release(dev, ci_nr); ++ ++ ++ if (((temp_int->internal->filts_used) <= 0) && ++ ((--(temp_int->internal->cis_used)) <= 0)) { ++ ++ ci_dbg_print("%s: Actually removing\n", __func__); ++ ++ remove_inode(temp_int->internal); ++ kfree(state->internal); ++ } ++ ++ if (state != NULL) { ++ if (state->ca.data != NULL) ++ dvb_ca_en50221_release(&state->ca); ++ ++ kfree(state); ++ } ++ } ++ ++} ++EXPORT_SYMBOL(altera_ci_release); ++ ++static void altera_pid_control(struct netup_hw_pid_filter *pid_filt, ++ u16 pid, int onoff) ++{ ++ struct fpga_internal *inter = pid_filt->internal; ++ u8 store = 0; ++ ++ /* pid 0-0x1f always enabled, don't touch them */ ++ if ((pid == 0x2000) || (pid < 0x20)) ++ return; ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0); ++ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, ++ ((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0); ++ ++ store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD); ++ ++ if (onoff)/* 0 - on, 1 - off */ ++ store |= (1 << (pid & 7)); ++ else ++ store &= ~(1 << (pid & 7)); ++ ++ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__, ++ pid_filt->nr, pid, pid, onoff ? "off" : "on"); ++} ++ ++static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt, ++ int filt_nr, int onoff) ++{ ++ struct fpga_internal *inter = pid_filt->internal; ++ u8 store = 0; ++ int i; ++ ++ pid_dbg_print("%s: pid_filt->nr[%d] now %s\n", __func__, pid_filt->nr, ++ onoff ? "off" : "on"); ++ ++ if (onoff)/* 0 - on, 1 - off */ ++ store = 0xff;/* ignore pid */ ++ else ++ store = 0;/* enable pid */ ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ for (i = 0; i < 1024; i++) { ++ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0); ++ ++ netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, ++ ((i >> 8) & 0x03) | (pid_filt->nr << 2), 0); ++ /* pid 0-0x1f always enabled */ ++ netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, ++ (i > 3 ? store : 0), 0); ++ } ++ ++ mutex_unlock(&inter->fpga_mutex); ++} ++ ++static int altera_pid_feed_control(void *demux_dev, int filt_nr, ++ struct dvb_demux_feed *feed, int onoff) ++{ ++ struct fpga_inode *temp_int = find_dinode(demux_dev); ++ struct fpga_internal *inter = temp_int->internal; ++ struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1]; ++ ++ altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1); ++ /* call old feed proc's */ ++ if (onoff) ++ pid_filt->start_feed(feed); ++ else ++ pid_filt->stop_feed(feed); ++ ++ if (feed->pid == 0x2000) ++ altera_toggle_fullts_streaming(pid_filt, filt_nr, ++ onoff ? 0 : 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL(altera_pid_feed_control); ++ ++static int altera_ci_start_feed(struct dvb_demux_feed *feed, int num) ++{ ++ altera_pid_feed_control(feed->demux, num, feed, 1); ++ ++ return 0; ++} ++ ++static int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num) ++{ ++ altera_pid_feed_control(feed->demux, num, feed, 0); ++ ++ return 0; ++} ++ ++static int altera_ci_start_feed_1(struct dvb_demux_feed *feed) ++{ ++ return altera_ci_start_feed(feed, 1); ++} ++ ++static int altera_ci_stop_feed_1(struct dvb_demux_feed *feed) ++{ ++ return altera_ci_stop_feed(feed, 1); ++} ++ ++static int altera_ci_start_feed_2(struct dvb_demux_feed *feed) ++{ ++ return altera_ci_start_feed(feed, 2); ++} ++ ++static int altera_ci_stop_feed_2(struct dvb_demux_feed *feed) ++{ ++ return altera_ci_stop_feed(feed, 2); ++} ++ ++static int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr) ++{ ++ struct netup_hw_pid_filter *pid_filt = NULL; ++ struct fpga_inode *temp_int = find_inode(config->dev); ++ struct fpga_internal *inter = NULL; ++ int ret = 0; ++ ++ pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL); ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (!pid_filt) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (temp_int != NULL) { ++ inter = temp_int->internal; ++ (inter->filts_used)++; ++ ci_dbg_print("%s: Find Internal Structure!\n", __func__); ++ } else { ++ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); ++ if (!inter) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ temp_int = append_internal(inter); ++ inter->filts_used = 1; ++ inter->dev = config->dev; ++ inter->fpga_rw = config->fpga_rw; ++ mutex_init(&inter->fpga_mutex); ++ inter->strt_wrk = 1; ++ ci_dbg_print("%s: Create New Internal Structure!\n", __func__); ++ } ++ ++ ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__, ++ pid_filt, hw_filt_nr - 1); ++ inter->pid_filt[hw_filt_nr - 1] = pid_filt; ++ pid_filt->demux = config->demux; ++ pid_filt->internal = inter; ++ pid_filt->nr = hw_filt_nr - 1; ++ /* store old feed controls */ ++ pid_filt->start_feed = config->demux->start_feed; ++ pid_filt->stop_feed = config->demux->stop_feed; ++ /* replace with new feed controls */ ++ if (hw_filt_nr == 1) { ++ pid_filt->demux->start_feed = altera_ci_start_feed_1; ++ pid_filt->demux->stop_feed = altera_ci_stop_feed_1; ++ } else if (hw_filt_nr == 2) { ++ pid_filt->demux->start_feed = altera_ci_start_feed_2; ++ pid_filt->demux->stop_feed = altera_ci_stop_feed_2; ++ } ++ ++ altera_toggle_fullts_streaming(pid_filt, 0, 1); ++ ++ return 0; ++err: ++ ci_dbg_print("%s: Can't init hardware filter: Error %d\n", ++ __func__, ret); ++ ++ kfree(pid_filt); ++ ++ return ret; ++} ++EXPORT_SYMBOL(altera_hw_filt_init); ++ ++int altera_ci_init(struct altera_ci_config *config, int ci_nr) ++{ ++ struct altera_ci_state *state; ++ struct fpga_inode *temp_int = find_inode(config->dev); ++ struct fpga_internal *inter = NULL; ++ int ret = 0; ++ u8 store = 0; ++ ++ state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL); ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (!state) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (temp_int != NULL) { ++ inter = temp_int->internal; ++ (inter->cis_used)++; ++ inter->fpga_rw = config->fpga_rw; ++ ci_dbg_print("%s: Find Internal Structure!\n", __func__); ++ } else { ++ inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); ++ if (!inter) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ temp_int = append_internal(inter); ++ inter->cis_used = 1; ++ inter->dev = config->dev; ++ inter->fpga_rw = config->fpga_rw; ++ mutex_init(&inter->fpga_mutex); ++ inter->strt_wrk = 1; ++ ci_dbg_print("%s: Create New Internal Structure!\n", __func__); ++ } ++ ++ ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__, ++ state, ci_nr - 1); ++ state->internal = inter; ++ state->nr = ci_nr - 1; ++ ++ state->ca.owner = THIS_MODULE; ++ state->ca.read_attribute_mem = altera_ci_read_attribute_mem; ++ state->ca.write_attribute_mem = altera_ci_write_attribute_mem; ++ state->ca.read_cam_control = altera_ci_read_cam_ctl; ++ state->ca.write_cam_control = altera_ci_write_cam_ctl; ++ state->ca.slot_reset = altera_ci_slot_reset; ++ state->ca.slot_shutdown = altera_ci_slot_shutdown; ++ state->ca.slot_ts_enable = altera_ci_slot_ts_ctl; ++ state->ca.poll_slot_status = altera_poll_ci_slot_status; ++ state->ca.data = state; ++ ++ ret = dvb_ca_en50221_init(config->adapter, ++ &state->ca, ++ /* flags */ 0, ++ /* n_slots */ 1); ++ if (0 != ret) ++ goto err; ++ ++ inter->state[ci_nr - 1] = state; ++ ++ altera_hw_filt_init(config, ci_nr); ++ ++ if (inter->strt_wrk) { ++ INIT_WORK(&inter->work, netup_read_ci_status); ++ inter->strt_wrk = 0; ++ } ++ ++ ci_dbg_print("%s: CI initialized!\n", __func__); ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ /* Enable div */ ++ netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0); ++ netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0); ++ ++ /* enable TS out */ ++ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); ++ store |= (3 << 4); ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); ++ ++ ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD); ++ /* enable irq */ ++ netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret); ++ ++ schedule_work(&inter->work); ++ ++ return 0; ++err: ++ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); ++ ++ kfree(state); ++ ++ return ret; ++} ++EXPORT_SYMBOL(altera_ci_init); ++ ++int altera_ci_tuner_reset(void *dev, int ci_nr) ++{ ++ struct fpga_inode *temp_int = find_inode(dev); ++ struct fpga_internal *inter = NULL; ++ u8 store; ++ ++ ci_dbg_print("%s\n", __func__); ++ ++ if (temp_int == NULL) ++ return -1; ++ ++ if (temp_int->internal == NULL) ++ return -1; ++ ++ inter = temp_int->internal; ++ ++ mutex_lock(&inter->fpga_mutex); ++ ++ store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); ++ store &= ~(4 << (2 - ci_nr)); ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); ++ msleep(100); ++ store |= (4 << (2 - ci_nr)); ++ netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); ++ ++ mutex_unlock(&inter->fpga_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(altera_ci_tuner_reset); +diff --git a/drivers/media/pci/cx23885/altera-ci.h b/drivers/media/pci/cx23885/altera-ci.h +new file mode 100644 +index 0000000..70e4fd6 +--- /dev/null ++++ b/drivers/media/pci/cx23885/altera-ci.h +@@ -0,0 +1,100 @@ ++/* ++ * altera-ci.c ++ * ++ * CI driver in conjunction with NetUp Dual DVB-T/C RF CI card ++ * ++ * Copyright (C) 2010 NetUP Inc. ++ * Copyright (C) 2010 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#ifndef __ALTERA_CI_H ++#define __ALTERA_CI_H ++ ++#define ALT_DATA 0x000000ff ++#define ALT_TDI 0x00008000 ++#define ALT_TDO 0x00004000 ++#define ALT_TCK 0x00002000 ++#define ALT_RDY 0x00001000 ++#define ALT_RD 0x00000800 ++#define ALT_WR 0x00000400 ++#define ALT_AD_RG 0x00000200 ++#define ALT_CS 0x00000100 ++ ++struct altera_ci_config { ++ void *dev;/* main dev, for example cx23885_dev */ ++ void *adapter;/* for CI to connect to */ ++ struct dvb_demux *demux;/* for hardware PID filter to connect to */ ++ int (*fpga_rw) (void *dev, int ad_rg, int val, int rw); ++}; ++ ++#if defined(CONFIG_MEDIA_ALTERA_CI) || (defined(CONFIG_MEDIA_ALTERA_CI_MODULE) \ ++ && defined(MODULE)) ++ ++extern int altera_ci_init(struct altera_ci_config *config, int ci_nr); ++extern void altera_ci_release(void *dev, int ci_nr); ++extern int altera_ci_irq(void *dev); ++extern int altera_ci_tuner_reset(void *dev, int ci_nr); ++ ++#else ++ ++static inline int altera_ci_init(struct altera_ci_config *config, int ci_nr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline void altera_ci_release(void *dev, int ci_nr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline int altera_ci_irq(void *dev) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline int altera_ci_tuner_reset(void *dev, int ci_nr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++#endif ++#if 0 ++static inline int altera_hw_filt_init(struct altera_ci_config *config, ++ int hw_filt_nr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++static inline void altera_hw_filt_release(void *dev, int filt_nr) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++} ++ ++static inline int altera_pid_feed_control(void *dev, int filt_nr, ++ struct dvb_demux_feed *dvbdmxfeed, int onoff) ++{ ++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); ++ return 0; ++} ++ ++#endif /* CONFIG_MEDIA_ALTERA_CI */ ++ ++#endif /* __ALTERA_CI_H */ +diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c +new file mode 100644 +index 0000000..7344849 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cimax2.c +@@ -0,0 +1,539 @@ ++/* ++ * cimax2.c ++ * ++ * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "cx23885.h" ++#include "cimax2.h" ++#include "dvb_ca_en50221.h" ++/**** Bit definitions for MC417_RWD and MC417_OEN registers *** ++ bits 31-16 +++-----------+ ++| Reserved | +++-----------+ ++ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 +++-------+-------+-------+-------+-------+-------+-------+-------+ ++| WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# | +++-------+-------+-------+-------+-------+-------+-------+-------+ ++ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 +++-------+-------+-------+-------+-------+-------+-------+-------+ ++| DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| +++-------+-------+-------+-------+-------+-------+-------+-------+ ++***/ ++/* MC417 */ ++#define NETUP_DATA 0x000000ff ++#define NETUP_WR 0x00008000 ++#define NETUP_RD 0x00004000 ++#define NETUP_ACK 0x00001000 ++#define NETUP_ADHI 0x00000800 ++#define NETUP_ADLO 0x00000400 ++#define NETUP_CS1 0x00000200 ++#define NETUP_CS0 0x00000100 ++#define NETUP_EN_ALL 0x00001000 ++#define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD) ++#define NETUP_CI_CTL 0x04 ++#define NETUP_CI_RD 1 ++ ++#define NETUP_IRQ_DETAM 0x1 ++#define NETUP_IRQ_IRQAM 0x4 ++ ++static unsigned int ci_dbg; ++module_param(ci_dbg, int, 0644); ++MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); ++ ++static unsigned int ci_irq_enable; ++module_param(ci_irq_enable, int, 0644); ++MODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM"); ++ ++#define ci_dbg_print(args...) \ ++ do { \ ++ if (ci_dbg) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++ ++#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0) ++ ++/* stores all private variables for communication with CI */ ++struct netup_ci_state { ++ struct dvb_ca_en50221 ca; ++ struct mutex ca_mutex; ++ struct i2c_adapter *i2c_adap; ++ u8 ci_i2c_addr; ++ int status; ++ struct work_struct work; ++ void *priv; ++ u8 current_irq_mode; ++ int current_ci_flag; ++ unsigned long next_status_checked_time; ++}; ++ ++ ++static int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, ++ u8 *buf, int len) ++{ ++ int ret; ++ struct i2c_msg msg[] = { ++ { ++ .addr = addr, ++ .flags = 0, ++ .buf = ®, ++ .len = 1 ++ }, { ++ .addr = addr, ++ .flags = I2C_M_RD, ++ .buf = buf, ++ .len = len ++ } ++ }; ++ ++ ret = i2c_transfer(i2c_adap, msg, 2); ++ ++ if (ret != 2) { ++ ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n", ++ __func__, reg, ret); ++ ++ return -1; ++ } ++ ++ ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n", ++ __func__, addr, reg, buf[0]); ++ ++ return 0; ++} ++ ++static int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, ++ u8 *buf, int len) ++{ ++ int ret; ++ u8 buffer[len + 1]; ++ ++ struct i2c_msg msg = { ++ .addr = addr, ++ .flags = 0, ++ .buf = &buffer[0], ++ .len = len + 1 ++ }; ++ ++ buffer[0] = reg; ++ memcpy(&buffer[1], buf, len); ++ ++ ret = i2c_transfer(i2c_adap, &msg, 1); ++ ++ if (ret != 1) { ++ ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n", ++ __func__, reg, ret); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int netup_ci_get_mem(struct cx23885_dev *dev) ++{ ++ int mem; ++ unsigned long timeout = jiffies + msecs_to_jiffies(1); ++ ++ for (;;) { ++ mem = cx_read(MC417_RWD); ++ if ((mem & NETUP_ACK) == 0) ++ break; ++ if (time_after(jiffies, timeout)) ++ break; ++ udelay(1); ++ } ++ ++ cx_set(MC417_RWD, NETUP_CTRL_OFF); ++ ++ return mem & 0xff; ++} ++ ++static int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, ++ u8 flag, u8 read, int addr, u8 data) ++{ ++ struct netup_ci_state *state = en50221->data; ++ struct cx23885_tsport *port = state->priv; ++ struct cx23885_dev *dev = port->dev; ++ ++ u8 store; ++ int mem; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ if (state->current_ci_flag != flag) { ++ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &store, 1); ++ if (ret != 0) ++ return ret; ++ ++ store &= ~0x0c; ++ store |= flag; ++ ++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &store, 1); ++ if (ret != 0) ++ return ret; ++ } ++ state->current_ci_flag = flag; ++ ++ mutex_lock(&dev->gpio_lock); ++ ++ /* write addr */ ++ cx_write(MC417_OEN, NETUP_EN_ALL); ++ cx_write(MC417_RWD, NETUP_CTRL_OFF | ++ NETUP_ADLO | (0xff & addr)); ++ cx_clear(MC417_RWD, NETUP_ADLO); ++ cx_write(MC417_RWD, NETUP_CTRL_OFF | ++ NETUP_ADHI | (0xff & (addr >> 8))); ++ cx_clear(MC417_RWD, NETUP_ADHI); ++ ++ if (read) { /* data in */ ++ cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); ++ } else /* data out */ ++ cx_write(MC417_RWD, NETUP_CTRL_OFF | data); ++ ++ /* choose chip */ ++ cx_clear(MC417_RWD, ++ (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1); ++ /* read/write */ ++ cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); ++ mem = netup_ci_get_mem(dev); ++ ++ mutex_unlock(&dev->gpio_lock); ++ ++ if (!read) ++ if (mem < 0) ++ return -EREMOTEIO; ++ ++ ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__, ++ (read) ? "read" : "write", state->ci_i2c_addr, addr, ++ (flag == NETUP_CI_CTL) ? "ctl" : "mem", ++ (read) ? mem : data); ++ ++ if (read) ++ return mem; ++ ++ return 0; ++} ++ ++int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr) ++{ ++ return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0); ++} ++ ++int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr, u8 data) ++{ ++ return netup_ci_op_cam(en50221, slot, 0, 0, addr, data); ++} ++ ++int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, ++ u8 addr) ++{ ++ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, ++ NETUP_CI_RD, addr, 0); ++} ++ ++int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, ++ u8 addr, u8 data) ++{ ++ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data); ++} ++ ++int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct netup_ci_state *state = en50221->data; ++ u8 buf = 0x80; ++ int ret; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ udelay(500); ++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &buf, 1); ++ ++ if (ret != 0) ++ return ret; ++ ++ udelay(500); ++ ++ buf = 0x00; ++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &buf, 1); ++ ++ msleep(1000); ++ dvb_ca_en50221_camready_irq(&state->ca, 0); ++ ++ return 0; ++ ++} ++ ++int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ /* not implemented */ ++ return 0; ++} ++ ++static int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode) ++{ ++ struct netup_ci_state *state = en50221->data; ++ int ret; ++ ++ if (irq_mode == state->current_irq_mode) ++ return 0; ++ ++ ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n", ++ __func__, state->ci_i2c_addr, irq_mode); ++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0x1b, &irq_mode, 1); ++ ++ if (ret != 0) ++ return ret; ++ ++ state->current_irq_mode = irq_mode; ++ ++ return 0; ++} ++ ++int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct netup_ci_state *state = en50221->data; ++ u8 buf; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &buf, 1); ++ buf |= 0x60; ++ ++ return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &buf, 1); ++} ++ ++/* work handler */ ++static void netup_read_ci_status(struct work_struct *work) ++{ ++ struct netup_ci_state *state = ++ container_of(work, struct netup_ci_state, work); ++ u8 buf[33]; ++ int ret; ++ ++ /* CAM module IRQ processing. fast operation */ ++ dvb_ca_en50221_frda_irq(&state->ca, 0); ++ ++ /* CAM module INSERT/REMOVE processing. slow operation because of i2c ++ * transfers */ ++ if (time_after(jiffies, state->next_status_checked_time) ++ || !state->status) { ++ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &buf[0], 33); ++ ++ state->next_status_checked_time = jiffies ++ + msecs_to_jiffies(1000); ++ ++ if (ret != 0) ++ return; ++ ++ ci_dbg_print("%s: Slot Status Addr=[0x%04x], " ++ "Reg=[0x%02x], data=%02x, " ++ "TS config = %02x\n", __func__, ++ state->ci_i2c_addr, 0, buf[0], ++ buf[0]); ++ ++ ++ if (buf[0] & 1) ++ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | ++ DVB_CA_EN50221_POLL_CAM_READY; ++ else ++ state->status = 0; ++ } ++} ++ ++/* CI irq handler */ ++int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status) ++{ ++ struct cx23885_tsport *port = NULL; ++ struct netup_ci_state *state = NULL; ++ ++ ci_dbg_print("%s:\n", __func__); ++ ++ if (0 == (pci_status & (PCI_MSK_GPIO0 | PCI_MSK_GPIO1))) ++ return 0; ++ ++ if (pci_status & PCI_MSK_GPIO0) { ++ port = &dev->ts1; ++ state = port->port_priv; ++ schedule_work(&state->work); ++ ci_dbg_print("%s: Wakeup CI0\n", __func__); ++ } ++ ++ if (pci_status & PCI_MSK_GPIO1) { ++ port = &dev->ts2; ++ state = port->port_priv; ++ schedule_work(&state->work); ++ ci_dbg_print("%s: Wakeup CI1\n", __func__); ++ } ++ ++ return 1; ++} ++ ++int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, ++ int slot, int open) ++{ ++ struct netup_ci_state *state = en50221->data; ++ ++ if (0 != slot) ++ return -EINVAL; ++ ++ netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags()) ++ : NETUP_IRQ_DETAM); ++ ++ return state->status; ++} ++ ++int netup_ci_init(struct cx23885_tsport *port) ++{ ++ struct netup_ci_state *state; ++ u8 cimax_init[34] = { ++ 0x00, /* module A control*/ ++ 0x00, /* auto select mask high A */ ++ 0x00, /* auto select mask low A */ ++ 0x00, /* auto select pattern high A */ ++ 0x00, /* auto select pattern low A */ ++ 0x44, /* memory access time A */ ++ 0x00, /* invert input A */ ++ 0x00, /* RFU */ ++ 0x00, /* RFU */ ++ 0x00, /* module B control*/ ++ 0x00, /* auto select mask high B */ ++ 0x00, /* auto select mask low B */ ++ 0x00, /* auto select pattern high B */ ++ 0x00, /* auto select pattern low B */ ++ 0x44, /* memory access time B */ ++ 0x00, /* invert input B */ ++ 0x00, /* RFU */ ++ 0x00, /* RFU */ ++ 0x00, /* auto select mask high Ext */ ++ 0x00, /* auto select mask low Ext */ ++ 0x00, /* auto select pattern high Ext */ ++ 0x00, /* auto select pattern low Ext */ ++ 0x00, /* RFU */ ++ 0x02, /* destination - module A */ ++ 0x01, /* power on (use it like store place) */ ++ 0x00, /* RFU */ ++ 0x00, /* int status read only */ ++ ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ ++ 0x05, /* EXTINT=active-high, INT=push-pull */ ++ 0x00, /* USCG1 */ ++ 0x04, /* ack active low */ ++ 0x00, /* LOCK = 0 */ ++ 0x33, /* serial mode, rising in, rising out, MSB first*/ ++ 0x31, /* synchronization */ ++ }; ++ int ret; ++ ++ ci_dbg_print("%s\n", __func__); ++ state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL); ++ if (!state) { ++ ci_dbg_print("%s: Unable create CI structure!\n", __func__); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ port->port_priv = state; ++ ++ switch (port->nr) { ++ case 1: ++ state->ci_i2c_addr = 0x40; ++ break; ++ case 2: ++ state->ci_i2c_addr = 0x41; ++ break; ++ } ++ ++ state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap; ++ state->ca.owner = THIS_MODULE; ++ state->ca.read_attribute_mem = netup_ci_read_attribute_mem; ++ state->ca.write_attribute_mem = netup_ci_write_attribute_mem; ++ state->ca.read_cam_control = netup_ci_read_cam_ctl; ++ state->ca.write_cam_control = netup_ci_write_cam_ctl; ++ state->ca.slot_reset = netup_ci_slot_reset; ++ state->ca.slot_shutdown = netup_ci_slot_shutdown; ++ state->ca.slot_ts_enable = netup_ci_slot_ts_ctl; ++ state->ca.poll_slot_status = netup_poll_ci_slot_status; ++ state->ca.data = state; ++ state->priv = port; ++ state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM; ++ ++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0, &cimax_init[0], 34); ++ /* lock registers */ ++ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0x1f, &cimax_init[0x18], 1); ++ /* power on slots */ ++ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, ++ 0x18, &cimax_init[0x18], 1); ++ ++ if (0 != ret) ++ goto err; ++ ++ ret = dvb_ca_en50221_init(&port->frontends.adapter, ++ &state->ca, ++ /* flags */ 0, ++ /* n_slots */ 1); ++ if (0 != ret) ++ goto err; ++ ++ INIT_WORK(&state->work, netup_read_ci_status); ++ schedule_work(&state->work); ++ ++ ci_dbg_print("%s: CI initialized!\n", __func__); ++ ++ return 0; ++err: ++ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); ++ kfree(state); ++ return ret; ++} ++ ++void netup_ci_exit(struct cx23885_tsport *port) ++{ ++ struct netup_ci_state *state; ++ ++ if (NULL == port) ++ return; ++ ++ state = (struct netup_ci_state *)port->port_priv; ++ if (NULL == state) ++ return; ++ ++ if (NULL == state->ca.data) ++ return; ++ ++ dvb_ca_en50221_release(&state->ca); ++ kfree(state); ++} +diff --git a/drivers/media/pci/cx23885/cimax2.h b/drivers/media/pci/cx23885/cimax2.h +new file mode 100644 +index 0000000..518744a +--- /dev/null ++++ b/drivers/media/pci/cx23885/cimax2.h +@@ -0,0 +1,47 @@ ++/* ++ * cimax2.h ++ * ++ * CIMax(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef CIMAX2_H ++#define CIMAX2_H ++#include "dvb_ca_en50221.h" ++ ++extern int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr); ++extern int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, ++ int slot, int addr, u8 data); ++extern int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr); ++extern int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, ++ int slot, u8 addr, u8 data); ++extern int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot); ++extern int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot); ++extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot); ++extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status); ++extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, ++ int slot, int open); ++extern int netup_ci_init(struct cx23885_tsport *port); ++extern void netup_ci_exit(struct cx23885_tsport *port); ++ ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-417.c b/drivers/media/pci/cx23885/cx23885-417.c +new file mode 100644 +index 0000000..5d5052d +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-417.c +@@ -0,0 +1,1790 @@ ++/* ++ * ++ * Support for a cx23417 mpeg encoder via cx23885 host port. ++ * ++ * (c) 2004 Jelle Foks ++ * (c) 2004 Gerd Knorr ++ * (c) 2008 Steven Toth ++ * - CX23885/7/8 support ++ * ++ * Includes parts from the ivtv driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include "cx23885-ioctl.h" ++ ++#define CX23885_FIRM_IMAGE_SIZE 376836 ++#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" ++ ++static unsigned int mpegbufs = 32; ++module_param(mpegbufs, int, 0644); ++MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); ++static unsigned int mpeglines = 32; ++module_param(mpeglines, int, 0644); ++MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); ++static unsigned int mpeglinesize = 512; ++module_param(mpeglinesize, int, 0644); ++MODULE_PARM_DESC(mpeglinesize, ++ "number of bytes in each line of an MPEG buffer, range 512-1024"); ++ ++static unsigned int v4l_debug; ++module_param(v4l_debug, int, 0644); ++MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (v4l_debug >= level) \ ++ printk(KERN_DEBUG "%s: " fmt, \ ++ (dev) ? dev->name : "cx23885[?]", ## arg); \ ++ } while (0) ++ ++static struct cx23885_tvnorm cx23885_tvnorms[] = { ++ { ++ .name = "NTSC-M", ++ .id = V4L2_STD_NTSC_M, ++ }, { ++ .name = "NTSC-JP", ++ .id = V4L2_STD_NTSC_M_JP, ++ }, { ++ .name = "PAL-BG", ++ .id = V4L2_STD_PAL_BG, ++ }, { ++ .name = "PAL-DK", ++ .id = V4L2_STD_PAL_DK, ++ }, { ++ .name = "PAL-I", ++ .id = V4L2_STD_PAL_I, ++ }, { ++ .name = "PAL-M", ++ .id = V4L2_STD_PAL_M, ++ }, { ++ .name = "PAL-N", ++ .id = V4L2_STD_PAL_N, ++ }, { ++ .name = "PAL-Nc", ++ .id = V4L2_STD_PAL_Nc, ++ }, { ++ .name = "PAL-60", ++ .id = V4L2_STD_PAL_60, ++ }, { ++ .name = "SECAM-L", ++ .id = V4L2_STD_SECAM_L, ++ }, { ++ .name = "SECAM-DK", ++ .id = V4L2_STD_SECAM_DK, ++ } ++}; ++ ++/* ------------------------------------------------------------------ */ ++enum cx23885_capture_type { ++ CX23885_MPEG_CAPTURE, ++ CX23885_RAW_CAPTURE, ++ CX23885_RAW_PASSTHRU_CAPTURE ++}; ++enum cx23885_capture_bits { ++ CX23885_RAW_BITS_NONE = 0x00, ++ CX23885_RAW_BITS_YUV_CAPTURE = 0x01, ++ CX23885_RAW_BITS_PCM_CAPTURE = 0x02, ++ CX23885_RAW_BITS_VBI_CAPTURE = 0x04, ++ CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08, ++ CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10 ++}; ++enum cx23885_capture_end { ++ CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */ ++ CX23885_END_NOW, /* stop immediately, no irq */ ++}; ++enum cx23885_framerate { ++ CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */ ++ CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */ ++}; ++enum cx23885_stream_port { ++ CX23885_OUTPUT_PORT_MEMORY, ++ CX23885_OUTPUT_PORT_STREAMING, ++ CX23885_OUTPUT_PORT_SERIAL ++}; ++enum cx23885_data_xfer_status { ++ CX23885_MORE_BUFFERS_FOLLOW, ++ CX23885_LAST_BUFFER, ++}; ++enum cx23885_picture_mask { ++ CX23885_PICTURE_MASK_NONE, ++ CX23885_PICTURE_MASK_I_FRAMES, ++ CX23885_PICTURE_MASK_I_P_FRAMES = 0x3, ++ CX23885_PICTURE_MASK_ALL_FRAMES = 0x7, ++}; ++enum cx23885_vbi_mode_bits { ++ CX23885_VBI_BITS_SLICED, ++ CX23885_VBI_BITS_RAW, ++}; ++enum cx23885_vbi_insertion_bits { ++ CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, ++ CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, ++ CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, ++ CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, ++ CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, ++}; ++enum cx23885_dma_unit { ++ CX23885_DMA_BYTES, ++ CX23885_DMA_FRAMES, ++}; ++enum cx23885_dma_transfer_status_bits { ++ CX23885_DMA_TRANSFER_BITS_DONE = 0x01, ++ CX23885_DMA_TRANSFER_BITS_ERROR = 0x04, ++ CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10, ++}; ++enum cx23885_pause { ++ CX23885_PAUSE_ENCODING, ++ CX23885_RESUME_ENCODING, ++}; ++enum cx23885_copyright { ++ CX23885_COPYRIGHT_OFF, ++ CX23885_COPYRIGHT_ON, ++}; ++enum cx23885_notification_type { ++ CX23885_NOTIFICATION_REFRESH, ++}; ++enum cx23885_notification_status { ++ CX23885_NOTIFICATION_OFF, ++ CX23885_NOTIFICATION_ON, ++}; ++enum cx23885_notification_mailbox { ++ CX23885_NOTIFICATION_NO_MAILBOX = -1, ++}; ++enum cx23885_field1_lines { ++ CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */ ++ CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */ ++ CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */ ++}; ++enum cx23885_field2_lines { ++ CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */ ++ CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */ ++ CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */ ++}; ++enum cx23885_custom_data_type { ++ CX23885_CUSTOM_EXTENSION_USR_DATA, ++ CX23885_CUSTOM_PRIVATE_PACKET, ++}; ++enum cx23885_mute { ++ CX23885_UNMUTE, ++ CX23885_MUTE, ++}; ++enum cx23885_mute_video_mask { ++ CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00, ++ CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000, ++ CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000, ++}; ++enum cx23885_mute_video_shift { ++ CX23885_MUTE_VIDEO_V_SHIFT = 8, ++ CX23885_MUTE_VIDEO_U_SHIFT = 16, ++ CX23885_MUTE_VIDEO_Y_SHIFT = 24, ++}; ++ ++/* defines below are from ivtv-driver.h */ ++#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF ++ ++/* Firmware API commands */ ++#define IVTV_API_STD_TIMEOUT 500 ++ ++/* Registers */ ++/* IVTV_REG_OFFSET */ ++#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) ++#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) ++#define IVTV_REG_SPU (0x9050) ++#define IVTV_REG_HW_BLOCKS (0x9054) ++#define IVTV_REG_VPU (0x9058) ++#define IVTV_REG_APU (0xA064) ++ ++/**** Bit definitions for MC417_RWD and MC417_OEN registers *** ++ bits 31-16 +++-----------+ ++| Reserved | +++-----------+ ++ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 +++-------+-------+-------+-------+-------+-------+-------+-------+ ++| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0| +++-------+-------+-------+-------+-------+-------+-------+-------+ ++ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 +++-------+-------+-------+-------+-------+-------+-------+-------+ ++|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0| +++-------+-------+-------+-------+-------+-------+-------+-------+ ++***/ ++#define MC417_MIWR 0x8000 ++#define MC417_MIRD 0x4000 ++#define MC417_MICS 0x2000 ++#define MC417_MIRDY 0x1000 ++#define MC417_MIADDR 0x0F00 ++#define MC417_MIDATA 0x00FF ++ ++/* MIADDR* nibble definitions */ ++#define MCI_MEMORY_DATA_BYTE0 0x000 ++#define MCI_MEMORY_DATA_BYTE1 0x100 ++#define MCI_MEMORY_DATA_BYTE2 0x200 ++#define MCI_MEMORY_DATA_BYTE3 0x300 ++#define MCI_MEMORY_ADDRESS_BYTE2 0x400 ++#define MCI_MEMORY_ADDRESS_BYTE1 0x500 ++#define MCI_MEMORY_ADDRESS_BYTE0 0x600 ++#define MCI_REGISTER_DATA_BYTE0 0x800 ++#define MCI_REGISTER_DATA_BYTE1 0x900 ++#define MCI_REGISTER_DATA_BYTE2 0xA00 ++#define MCI_REGISTER_DATA_BYTE3 0xB00 ++#define MCI_REGISTER_ADDRESS_BYTE0 0xC00 ++#define MCI_REGISTER_ADDRESS_BYTE1 0xD00 ++#define MCI_REGISTER_MODE 0xE00 ++ ++/* Read and write modes */ ++#define MCI_MODE_REGISTER_READ 0 ++#define MCI_MODE_REGISTER_WRITE 1 ++#define MCI_MODE_MEMORY_READ 0 ++#define MCI_MODE_MEMORY_WRITE 0x40 ++ ++/*** Bit definitions for MC417_CTL register **** ++ bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0 +++--------+-------------+--------+--------------+------------+ ++|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN| +++--------+-------------+--------+--------------+------------+ ++***/ ++#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030) ++#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006) ++#define MC417_UART_GPIO_EN 0x00000001 ++ ++/* Values for speed control */ ++#define MC417_SPD_CTL_SLOW 0x1 ++#define MC417_SPD_CTL_MEDIUM 0x0 ++#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */ ++ ++/* Values for GPIO select */ ++#define MC417_GPIO_SEL_GPIO3 0x3 ++#define MC417_GPIO_SEL_GPIO2 0x2 ++#define MC417_GPIO_SEL_GPIO1 0x1 ++#define MC417_GPIO_SEL_GPIO0 0x0 ++ ++void cx23885_mc417_init(struct cx23885_dev *dev) ++{ ++ u32 regval; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ /* Configure MC417_CTL register to defaults. */ ++ regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) | ++ MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) | ++ MC417_UART_GPIO_EN; ++ cx_write(MC417_CTL, regval); ++ ++ /* Configure MC417_OEN to defaults. */ ++ regval = MC417_MIRDY; ++ cx_write(MC417_OEN, regval); ++ ++ /* Configure MC417_RWD to defaults. */ ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS; ++ cx_write(MC417_RWD, regval); ++} ++ ++static int mc417_wait_ready(struct cx23885_dev *dev) ++{ ++ u32 mi_ready; ++ unsigned long timeout = jiffies + msecs_to_jiffies(1); ++ ++ for (;;) { ++ mi_ready = cx_read(MC417_RWD) & MC417_MIRDY; ++ if (mi_ready != 0) ++ return 0; ++ if (time_after(jiffies, timeout)) ++ return -1; ++ udelay(1); ++ } ++} ++ ++int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) ++{ ++ u32 regval; ++ ++ /* Enable MC417 GPIO outputs except for MC417_MIRDY, ++ * which is an input. ++ */ ++ cx_write(MC417_OEN, MC417_MIRDY); ++ ++ /* Write data byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 | ++ (value & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ ++ /* Transition CS/WR to effect write transaction across bus. */ ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 | ++ ((value >> 8) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 | ++ ((value >> 16) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 3 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 | ++ ((value >> 24) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | ++ (address & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | ++ ((address >> 8) & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Indicate that this is a write. */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | ++ MCI_MODE_REGISTER_WRITE; ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Wait for the trans to complete (MC417_MIRDY asserted). */ ++ return mc417_wait_ready(dev); ++} ++ ++int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) ++{ ++ int retval; ++ u32 regval; ++ u32 tempval; ++ u32 dataval; ++ ++ /* Enable MC417 GPIO outputs except for MC417_MIRDY, ++ * which is an input. ++ */ ++ cx_write(MC417_OEN, MC417_MIRDY); ++ ++ /* Write address byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | ++ ((address & 0x00FF)); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | ++ ((address >> 8) & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Indicate that this is a register read. */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | ++ MCI_MODE_REGISTER_READ; ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Wait for the trans to complete (MC417_MIRDY asserted). */ ++ retval = mc417_wait_ready(dev); ++ ++ /* switch the DAT0-7 GPIO[10:3] to input mode */ ++ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); ++ ++ /* Read data byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; ++ cx_write(MC417_RWD, regval); ++ ++ /* Transition RD to effect read transaction across bus. ++ * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)? ++ * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its ++ * input only...) ++ */ ++ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; ++ cx_write(MC417_RWD, regval); ++ ++ /* Collect byte */ ++ tempval = cx_read(MC417_RWD); ++ dataval = tempval & 0x000000FF; ++ ++ /* Bring CS and RD high. */ ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= ((tempval & 0x000000FF) << 8); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= ((tempval & 0x000000FF) << 16); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 3 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= ((tempval & 0x000000FF) << 24); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ *value = dataval; ++ ++ return retval; ++} ++ ++int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value) ++{ ++ u32 regval; ++ ++ /* Enable MC417 GPIO outputs except for MC417_MIRDY, ++ * which is an input. ++ */ ++ cx_write(MC417_OEN, MC417_MIRDY); ++ ++ /* Write data byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 | ++ (value & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ ++ /* Transition CS/WR to effect write transaction across bus. */ ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 | ++ ((value >> 8) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 | ++ ((value >> 16) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write data byte 3 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 | ++ ((value >> 24) & 0x000000FF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | ++ MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | ++ ((address >> 8) & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | ++ (address & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Wait for the trans to complete (MC417_MIRDY asserted). */ ++ return mc417_wait_ready(dev); ++} ++ ++int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) ++{ ++ int retval; ++ u32 regval; ++ u32 tempval; ++ u32 dataval; ++ ++ /* Enable MC417 GPIO outputs except for MC417_MIRDY, ++ * which is an input. ++ */ ++ cx_write(MC417_OEN, MC417_MIRDY); ++ ++ /* Write address byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | ++ MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | ++ ((address >> 8) & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Write address byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | ++ (address & 0xFF); ++ cx_write(MC417_RWD, regval); ++ regval |= MC417_MICS | MC417_MIWR; ++ cx_write(MC417_RWD, regval); ++ ++ /* Wait for the trans to complete (MC417_MIRDY asserted). */ ++ retval = mc417_wait_ready(dev); ++ ++ /* switch the DAT0-7 GPIO[10:3] to input mode */ ++ cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); ++ ++ /* Read data byte 3 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; ++ cx_write(MC417_RWD, regval); ++ ++ /* Transition RD to effect read transaction across bus. */ ++ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; ++ cx_write(MC417_RWD, regval); ++ ++ /* Collect byte */ ++ tempval = cx_read(MC417_RWD); ++ dataval = ((tempval & 0x000000FF) << 24); ++ ++ /* Bring CS and RD high. */ ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 2 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= ((tempval & 0x000000FF) << 16); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 1 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= ((tempval & 0x000000FF) << 8); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ /* Read data byte 0 */ ++ regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; ++ cx_write(MC417_RWD, regval); ++ regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; ++ cx_write(MC417_RWD, regval); ++ tempval = cx_read(MC417_RWD); ++ dataval |= (tempval & 0x000000FF); ++ regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; ++ cx_write(MC417_RWD, regval); ++ ++ *value = dataval; ++ ++ return retval; ++} ++ ++void mc417_gpio_set(struct cx23885_dev *dev, u32 mask) ++{ ++ u32 val; ++ ++ /* Set the gpio value */ ++ mc417_register_read(dev, 0x900C, &val); ++ val |= (mask & 0x000ffff); ++ mc417_register_write(dev, 0x900C, val); ++} ++ ++void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask) ++{ ++ u32 val; ++ ++ /* Clear the gpio value */ ++ mc417_register_read(dev, 0x900C, &val); ++ val &= ~(mask & 0x0000ffff); ++ mc417_register_write(dev, 0x900C, val); ++} ++ ++void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) ++{ ++ u32 val; ++ ++ /* Enable GPIO direction bits */ ++ mc417_register_read(dev, 0x9020, &val); ++ if (asoutput) ++ val |= (mask & 0x0000ffff); ++ else ++ val &= ~(mask & 0x0000ffff); ++ ++ mc417_register_write(dev, 0x9020, val); ++} ++/* ------------------------------------------------------------------ */ ++ ++/* MPEG encoder API */ ++static char *cmd_to_str(int cmd) ++{ ++ switch (cmd) { ++ case CX2341X_ENC_PING_FW: ++ return "PING_FW"; ++ case CX2341X_ENC_START_CAPTURE: ++ return "START_CAPTURE"; ++ case CX2341X_ENC_STOP_CAPTURE: ++ return "STOP_CAPTURE"; ++ case CX2341X_ENC_SET_AUDIO_ID: ++ return "SET_AUDIO_ID"; ++ case CX2341X_ENC_SET_VIDEO_ID: ++ return "SET_VIDEO_ID"; ++ case CX2341X_ENC_SET_PCR_ID: ++ return "SET_PCR_ID"; ++ case CX2341X_ENC_SET_FRAME_RATE: ++ return "SET_FRAME_RATE"; ++ case CX2341X_ENC_SET_FRAME_SIZE: ++ return "SET_FRAME_SIZE"; ++ case CX2341X_ENC_SET_BIT_RATE: ++ return "SET_BIT_RATE"; ++ case CX2341X_ENC_SET_GOP_PROPERTIES: ++ return "SET_GOP_PROPERTIES"; ++ case CX2341X_ENC_SET_ASPECT_RATIO: ++ return "SET_ASPECT_RATIO"; ++ case CX2341X_ENC_SET_DNR_FILTER_MODE: ++ return "SET_DNR_FILTER_MODE"; ++ case CX2341X_ENC_SET_DNR_FILTER_PROPS: ++ return "SET_DNR_FILTER_PROPS"; ++ case CX2341X_ENC_SET_CORING_LEVELS: ++ return "SET_CORING_LEVELS"; ++ case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: ++ return "SET_SPATIAL_FILTER_TYPE"; ++ case CX2341X_ENC_SET_VBI_LINE: ++ return "SET_VBI_LINE"; ++ case CX2341X_ENC_SET_STREAM_TYPE: ++ return "SET_STREAM_TYPE"; ++ case CX2341X_ENC_SET_OUTPUT_PORT: ++ return "SET_OUTPUT_PORT"; ++ case CX2341X_ENC_SET_AUDIO_PROPERTIES: ++ return "SET_AUDIO_PROPERTIES"; ++ case CX2341X_ENC_HALT_FW: ++ return "HALT_FW"; ++ case CX2341X_ENC_GET_VERSION: ++ return "GET_VERSION"; ++ case CX2341X_ENC_SET_GOP_CLOSURE: ++ return "SET_GOP_CLOSURE"; ++ case CX2341X_ENC_GET_SEQ_END: ++ return "GET_SEQ_END"; ++ case CX2341X_ENC_SET_PGM_INDEX_INFO: ++ return "SET_PGM_INDEX_INFO"; ++ case CX2341X_ENC_SET_VBI_CONFIG: ++ return "SET_VBI_CONFIG"; ++ case CX2341X_ENC_SET_DMA_BLOCK_SIZE: ++ return "SET_DMA_BLOCK_SIZE"; ++ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: ++ return "GET_PREV_DMA_INFO_MB_10"; ++ case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: ++ return "GET_PREV_DMA_INFO_MB_9"; ++ case CX2341X_ENC_SCHED_DMA_TO_HOST: ++ return "SCHED_DMA_TO_HOST"; ++ case CX2341X_ENC_INITIALIZE_INPUT: ++ return "INITIALIZE_INPUT"; ++ case CX2341X_ENC_SET_FRAME_DROP_RATE: ++ return "SET_FRAME_DROP_RATE"; ++ case CX2341X_ENC_PAUSE_ENCODER: ++ return "PAUSE_ENCODER"; ++ case CX2341X_ENC_REFRESH_INPUT: ++ return "REFRESH_INPUT"; ++ case CX2341X_ENC_SET_COPYRIGHT: ++ return "SET_COPYRIGHT"; ++ case CX2341X_ENC_SET_EVENT_NOTIFICATION: ++ return "SET_EVENT_NOTIFICATION"; ++ case CX2341X_ENC_SET_NUM_VSYNC_LINES: ++ return "SET_NUM_VSYNC_LINES"; ++ case CX2341X_ENC_SET_PLACEHOLDER: ++ return "SET_PLACEHOLDER"; ++ case CX2341X_ENC_MUTE_VIDEO: ++ return "MUTE_VIDEO"; ++ case CX2341X_ENC_MUTE_AUDIO: ++ return "MUTE_AUDIO"; ++ case CX2341X_ENC_MISC: ++ return "MISC"; ++ default: ++ return "UNKNOWN"; ++ } ++} ++ ++static int cx23885_mbox_func(void *priv, ++ u32 command, ++ int in, ++ int out, ++ u32 data[CX2341X_MBOX_MAX_DATA]) ++{ ++ struct cx23885_dev *dev = priv; ++ unsigned long timeout; ++ u32 value, flag, retval = 0; ++ int i; ++ ++ dprintk(3, "%s: command(0x%X) = %s\n", __func__, command, ++ cmd_to_str(command)); ++ ++ /* this may not be 100% safe if we can't read any memory location ++ without side effects */ ++ mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); ++ if (value != 0x12345678) { ++ printk(KERN_ERR ++ "Firmware and/or mailbox pointer not initialized " ++ "or corrupted, signature = 0x%x, cmd = %s\n", value, ++ cmd_to_str(command)); ++ return -1; ++ } ++ ++ /* This read looks at 32 bits, but flag is only 8 bits. ++ * Seems we also bail if CMD or TIMEOUT bytes are set??? ++ */ ++ mc417_memory_read(dev, dev->cx23417_mailbox, &flag); ++ if (flag) { ++ printk(KERN_ERR "ERROR: Mailbox appears to be in use " ++ "(%x), cmd = %s\n", flag, cmd_to_str(command)); ++ return -1; ++ } ++ ++ flag |= 1; /* tell 'em we're working on it */ ++ mc417_memory_write(dev, dev->cx23417_mailbox, flag); ++ ++ /* write command + args + fill remaining with zeros */ ++ /* command code */ ++ mc417_memory_write(dev, dev->cx23417_mailbox + 1, command); ++ mc417_memory_write(dev, dev->cx23417_mailbox + 3, ++ IVTV_API_STD_TIMEOUT); /* timeout */ ++ for (i = 0; i < in; i++) { ++ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]); ++ dprintk(3, "API Input %d = %d\n", i, data[i]); ++ } ++ for (; i < CX2341X_MBOX_MAX_DATA; i++) ++ mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0); ++ ++ flag |= 3; /* tell 'em we're done writing */ ++ mc417_memory_write(dev, dev->cx23417_mailbox, flag); ++ ++ /* wait for firmware to handle the API command */ ++ timeout = jiffies + msecs_to_jiffies(10); ++ for (;;) { ++ mc417_memory_read(dev, dev->cx23417_mailbox, &flag); ++ if (0 != (flag & 4)) ++ break; ++ if (time_after(jiffies, timeout)) { ++ printk(KERN_ERR "ERROR: API Mailbox timeout\n"); ++ return -1; ++ } ++ udelay(10); ++ } ++ ++ /* read output values */ ++ for (i = 0; i < out; i++) { ++ mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i); ++ dprintk(3, "API Output %d = %d\n", i, data[i]); ++ } ++ ++ mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval); ++ dprintk(3, "API result = %d\n", retval); ++ ++ flag = 0; ++ mc417_memory_write(dev, dev->cx23417_mailbox, flag); ++ ++ return retval; ++} ++ ++/* We don't need to call the API often, so using just one ++ * mailbox will probably suffice ++ */ ++static int cx23885_api_cmd(struct cx23885_dev *dev, ++ u32 command, ++ u32 inputcnt, ++ u32 outputcnt, ++ ...) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ va_list vargs; ++ int i, err; ++ ++ dprintk(3, "%s() cmds = 0x%08x\n", __func__, command); ++ ++ va_start(vargs, outputcnt); ++ for (i = 0; i < inputcnt; i++) ++ data[i] = va_arg(vargs, int); ++ ++ err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data); ++ for (i = 0; i < outputcnt; i++) { ++ int *vptr = va_arg(vargs, int *); ++ *vptr = data[i]; ++ } ++ va_end(vargs); ++ ++ return err; ++} ++ ++static int cx23885_find_mailbox(struct cx23885_dev *dev) ++{ ++ u32 signature[4] = { ++ 0x12345678, 0x34567812, 0x56781234, 0x78123456 ++ }; ++ int signaturecnt = 0; ++ u32 value; ++ int i; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) { ++ mc417_memory_read(dev, i, &value); ++ if (value == signature[signaturecnt]) ++ signaturecnt++; ++ else ++ signaturecnt = 0; ++ if (4 == signaturecnt) { ++ dprintk(1, "Mailbox signature found at 0x%x\n", i+1); ++ return i+1; ++ } ++ } ++ printk(KERN_ERR "Mailbox signature values not found!\n"); ++ return -1; ++} ++ ++static int cx23885_load_firmware(struct cx23885_dev *dev) ++{ ++ static const unsigned char magic[8] = { ++ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa ++ }; ++ const struct firmware *firmware; ++ int i, retval = 0; ++ u32 value = 0; ++ u32 gpio_output = 0; ++ u32 gpio_value; ++ u32 checksum = 0; ++ u32 *dataptr; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ /* Save GPIO settings before reset of APU */ ++ retval |= mc417_memory_read(dev, 0x9020, &gpio_output); ++ retval |= mc417_memory_read(dev, 0x900C, &gpio_value); ++ ++ retval = mc417_register_write(dev, ++ IVTV_REG_VPU, 0xFFFFFFED); ++ retval |= mc417_register_write(dev, ++ IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); ++ retval |= mc417_register_write(dev, ++ IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800); ++ retval |= mc417_register_write(dev, ++ IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); ++ retval |= mc417_register_write(dev, ++ IVTV_REG_APU, 0); ++ ++ if (retval != 0) { ++ printk(KERN_ERR "%s: Error with mc417_register_write\n", ++ __func__); ++ return -1; ++ } ++ ++ retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME, ++ &dev->pci->dev); ++ ++ if (retval != 0) { ++ printk(KERN_ERR ++ "ERROR: Hotplug firmware request failed (%s).\n", ++ CX23885_FIRM_IMAGE_NAME); ++ printk(KERN_ERR "Please fix your hotplug setup, the board will " ++ "not work without firmware loaded!\n"); ++ return -1; ++ } ++ ++ if (firmware->size != CX23885_FIRM_IMAGE_SIZE) { ++ printk(KERN_ERR "ERROR: Firmware size mismatch " ++ "(have %zd, expected %d)\n", ++ firmware->size, CX23885_FIRM_IMAGE_SIZE); ++ release_firmware(firmware); ++ return -1; ++ } ++ ++ if (0 != memcmp(firmware->data, magic, 8)) { ++ printk(KERN_ERR ++ "ERROR: Firmware magic mismatch, wrong file?\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ ++ /* transfer to the chip */ ++ dprintk(2, "Loading firmware ...\n"); ++ dataptr = (u32 *)firmware->data; ++ for (i = 0; i < (firmware->size >> 2); i++) { ++ value = *dataptr; ++ checksum += ~value; ++ if (mc417_memory_write(dev, i, value) != 0) { ++ printk(KERN_ERR "ERROR: Loading firmware failed!\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ dataptr++; ++ } ++ ++ /* read back to verify with the checksum */ ++ dprintk(1, "Verifying firmware ...\n"); ++ for (i--; i >= 0; i--) { ++ if (mc417_memory_read(dev, i, &value) != 0) { ++ printk(KERN_ERR "ERROR: Reading firmware failed!\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ checksum -= ~value; ++ } ++ if (checksum) { ++ printk(KERN_ERR ++ "ERROR: Firmware load failed (checksum mismatch).\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ release_firmware(firmware); ++ dprintk(1, "Firmware upload successful.\n"); ++ ++ retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, ++ IVTV_CMD_HW_BLOCKS_RST); ++ ++ /* F/W power up disturbs the GPIOs, restore state */ ++ retval |= mc417_register_write(dev, 0x9020, gpio_output); ++ retval |= mc417_register_write(dev, 0x900C, gpio_value); ++ ++ retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); ++ retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); ++ ++ /* Hardcoded GPIO's here */ ++ retval |= mc417_register_write(dev, 0x9020, 0x4000); ++ retval |= mc417_register_write(dev, 0x900C, 0x4000); ++ ++ mc417_register_read(dev, 0x9020, &gpio_output); ++ mc417_register_read(dev, 0x900C, &gpio_value); ++ ++ if (retval < 0) ++ printk(KERN_ERR "%s: Error with mc417_register_write\n", ++ __func__); ++ return 0; ++} ++ ++void cx23885_417_check_encoder(struct cx23885_dev *dev) ++{ ++ u32 status, seq; ++ ++ status = seq = 0; ++ cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); ++ dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); ++} ++ ++static void cx23885_codec_settings(struct cx23885_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* Dynamically change the height based on video standard */ ++ if (dev->encodernorm.id & V4L2_STD_525_60) ++ dev->ts1.height = 480; ++ else ++ dev->ts1.height = 576; ++ ++ /* assign frame size */ ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, ++ dev->ts1.height, dev->ts1.width); ++ ++ dev->mpeg_params.width = dev->ts1.width; ++ dev->mpeg_params.height = dev->ts1.height; ++ dev->mpeg_params.is_50hz = ++ (dev->encodernorm.id & V4L2_STD_625_50) != 0; ++ ++ cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params); ++ ++ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); ++ cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); ++} ++ ++static int cx23885_initialize_codec(struct cx23885_dev *dev, int startencoder) ++{ ++ int version; ++ int retval; ++ u32 i, data[7]; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ ++ if (retval < 0) { ++ dprintk(2, "%s() PING OK\n", __func__); ++ retval = cx23885_load_firmware(dev); ++ if (retval < 0) { ++ printk(KERN_ERR "%s() f/w load failed\n", __func__); ++ return retval; ++ } ++ retval = cx23885_find_mailbox(dev); ++ if (retval < 0) { ++ printk(KERN_ERR "%s() mailbox < 0, error\n", ++ __func__); ++ return -1; ++ } ++ dev->cx23417_mailbox = retval; ++ retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); ++ if (retval < 0) { ++ printk(KERN_ERR ++ "ERROR: cx23417 firmware ping failed!\n"); ++ return -1; ++ } ++ retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, ++ &version); ++ if (retval < 0) { ++ printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" ++ "version failed!\n"); ++ return -1; ++ } ++ dprintk(1, "cx23417 firmware version is 0x%08x\n", version); ++ msleep(200); ++ } ++ ++ cx23885_codec_settings(dev); ++ msleep(60); ++ ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, ++ CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115); ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, ++ CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0); ++ ++ /* Setup to capture VBI */ ++ data[0] = 0x0001BD00; ++ data[1] = 1; /* frames per interrupt */ ++ data[2] = 4; /* total bufs */ ++ data[3] = 0x91559155; /* start codes */ ++ data[4] = 0x206080C0; /* stop codes */ ++ data[5] = 6; /* lines */ ++ data[6] = 64; /* BPL */ ++ ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], ++ data[2], data[3], data[4], data[5], data[6]); ++ ++ for (i = 2; i <= 24; i++) { ++ int valid; ++ ++ valid = ((i >= 19) && (i <= 21)); ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i, ++ valid, 0 , 0, 0); ++ cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, ++ i | 0x80000000, valid, 0, 0, 0); ++ } ++ ++ cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE); ++ msleep(60); ++ ++ /* initialize the video input */ ++ cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); ++ msleep(60); ++ ++ /* Enable VIP style pixel invalidation so we work with scaled mode */ ++ mc417_memory_write(dev, 2120, 0x00000080); ++ ++ /* start capturing to the host interface */ ++ if (startencoder) { ++ cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, ++ CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE); ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int bb_buf_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ ++ fh->dev->ts1.ts_packet_size = mpeglinesize; ++ fh->dev->ts1.ts_packet_count = mpeglines; ++ ++ *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; ++ *count = mpegbufs; ++ ++ return 0; ++} ++ ++static int bb_buf_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, enum v4l2_field field) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ return cx23885_buf_prepare(q, &fh->dev->ts1, ++ (struct cx23885_buffer *)vb, ++ field); ++} ++ ++static void bb_buf_queue(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb); ++} ++ ++static void bb_buf_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ cx23885_free_buffer(q, (struct cx23885_buffer *)vb); ++} ++ ++static struct videobuf_queue_ops cx23885_qops = { ++ .buf_setup = bb_buf_setup, ++ .buf_prepare = bb_buf_prepare, ++ .buf_queue = bb_buf_queue, ++ .buf_release = bb_buf_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static const u32 *ctrl_classes[] = { ++ cx2341x_mpeg_ctrls, ++ NULL ++}; ++ ++static int cx23885_queryctrl(struct cx23885_dev *dev, ++ struct v4l2_queryctrl *qctrl) ++{ ++ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); ++ if (qctrl->id == 0) ++ return -EINVAL; ++ ++ /* MPEG V4L2 controls */ ++ if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) ++ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; ++ ++ return 0; ++} ++ ++static int cx23885_querymenu(struct cx23885_dev *dev, ++ struct v4l2_querymenu *qmenu) ++{ ++ struct v4l2_queryctrl qctrl; ++ ++ qctrl.id = qmenu->id; ++ cx23885_queryctrl(dev, &qctrl); ++ return v4l2_ctrl_query_menu(qmenu, &qctrl, ++ cx2341x_ctrl_get_menu(&dev->mpeg_params, qmenu->id)); ++} ++ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ call_all(dev, core, g_std, id); ++ ++ return 0; ++} ++ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) ++ if (*id & cx23885_tvnorms[i].id) ++ break; ++ if (i == ARRAY_SIZE(cx23885_tvnorms)) ++ return -EINVAL; ++ dev->encodernorm = cx23885_tvnorms[i]; ++ ++ /* Have the drier core notify the subdevices */ ++ mutex_lock(&dev->lock); ++ cx23885_set_tvnorm(dev, *id); ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ dprintk(1, "%s()\n", __func__); ++ return cx23885_enum_input(dev, i); ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ return cx23885_get_input(file, priv, i); ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ return cx23885_set_input(file, priv, i); ++} ++ ++static int vidioc_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (UNSET == dev->tuner_type) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ strcpy(t->name, "Television"); ++ call_all(dev, tuner, g_tuner, t); ++ ++ dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); ++ ++ return 0; ++} ++ ++static int vidioc_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (UNSET == dev->tuner_type) ++ return -EINVAL; ++ ++ /* Update the A/V core */ ++ call_all(dev, tuner, s_tuner, t); ++ ++ return 0; ++} ++ ++static int vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (UNSET == dev->tuner_type) ++ return -EINVAL; ++ f->type = V4L2_TUNER_ANALOG_TV; ++ f->frequency = dev->freq; ++ ++ call_all(dev, tuner, g_frequency, f); ++ ++ return 0; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ return cx23885_set_frequency(file, priv, f); ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ return cx23885_get_control(dev, ctl); ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ return cx23885_set_control(dev, ctl); ++} ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx23885_tsport *tsport = &dev->ts1; ++ ++ strlcpy(cap->driver, dev->name, sizeof(cap->driver)); ++ strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING | ++ 0; ++ if (UNSET != dev->tuner_type) ++ cap->capabilities |= V4L2_CAP_TUNER; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strlcpy(f->description, "MPEG", sizeof(f->description)); ++ f->pixelformat = V4L2_PIX_FMT_MPEG; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ f->fmt.pix.width = dev->ts1.width; ++ f->fmt.pix.height = dev->ts1.height; ++ f->fmt.pix.field = fh->mpegq.field; ++ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", ++ dev->ts1.width, dev->ts1.height, fh->mpegq.field); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", ++ dev->ts1.width, dev->ts1.height, fh->mpegq.field); ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); ++ return 0; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_reqbufs(&fh->mpegq, p); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_querybuf(&fh->mpegq, p); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_qbuf(&fh->mpegq, p); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct cx23885_fh *fh = priv; ++ ++ return videobuf_dqbuf(&fh->mpegq, b, file->f_flags & O_NONBLOCK); ++} ++ ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type i) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_streamon(&fh->mpegq); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_streamoff(&fh->mpegq); ++} ++ ++static int vidioc_g_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, VIDIOC_G_EXT_CTRLS); ++} ++ ++static int vidioc_s_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx2341x_mpeg_params p; ++ int err; ++ ++ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ ++ p = dev->mpeg_params; ++ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); ++ ++ if (err == 0) { ++ err = cx2341x_update(dev, cx23885_mbox_func, ++ &dev->mpeg_params, &p); ++ dev->mpeg_params = p; ++ } ++ return err; ++} ++ ++static int vidioc_try_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx2341x_mpeg_params p; ++ int err; ++ ++ if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ ++ p = dev->mpeg_params; ++ err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); ++ return err; ++} ++ ++static int vidioc_log_status(struct file *file, void *priv) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ char name[32 + 2]; ++ ++ snprintf(name, sizeof(name), "%s/2", dev->name); ++ printk(KERN_INFO ++ "%s/2: ============ START LOG STATUS ============\n", ++ dev->name); ++ call_all(dev, core, log_status); ++ cx2341x_log_status(&dev->mpeg_params, name); ++ printk(KERN_INFO ++ "%s/2: ============= END LOG STATUS =============\n", ++ dev->name); ++ return 0; ++} ++ ++static int vidioc_querymenu(struct file *file, void *priv, ++ struct v4l2_querymenu *a) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ return cx23885_querymenu(dev, a); ++} ++ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ return cx23885_queryctrl(dev, c); ++} ++ ++static int mpeg_open(struct file *file) ++{ ++ struct cx23885_dev *dev = video_drvdata(file); ++ struct cx23885_fh *fh; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (!fh) ++ return -ENOMEM; ++ ++ file->private_data = fh; ++ fh->dev = dev; ++ ++ videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops, ++ &dev->pci->dev, &dev->ts1.slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct cx23885_buffer), ++ fh, NULL); ++ return 0; ++} ++ ++static int mpeg_release(struct file *file) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ /* FIXME: Review this crap */ ++ /* Shut device down on last close */ ++ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { ++ if (atomic_dec_return(&dev->v4l_reader_count) == 0) { ++ /* stop mpeg capture */ ++ cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, ++ CX23885_END_NOW, CX23885_MPEG_CAPTURE, ++ CX23885_RAW_BITS_NONE); ++ ++ msleep(500); ++ cx23885_417_check_encoder(dev); ++ ++ cx23885_cancel_buffers(&fh->dev->ts1); ++ } ++ } ++ ++ if (fh->mpegq.streaming) ++ videobuf_streamoff(&fh->mpegq); ++ if (fh->mpegq.reading) ++ videobuf_read_stop(&fh->mpegq); ++ ++ videobuf_mmap_free(&fh->mpegq); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ return 0; ++} ++ ++static ssize_t mpeg_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ ++ /* Start mpeg encoder on first read. */ ++ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { ++ if (atomic_inc_return(&dev->v4l_reader_count) == 1) { ++ if (cx23885_initialize_codec(dev, 1) < 0) ++ return -EINVAL; ++ } ++ } ++ ++ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static unsigned int mpeg_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ dprintk(2, "%s\n", __func__); ++ ++ return videobuf_poll_stream(file, &fh->mpegq, wait); ++} ++ ++static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ dprintk(2, "%s()\n", __func__); ++ ++ return videobuf_mmap_mapper(&fh->mpegq, vma); ++} ++ ++static struct v4l2_file_operations mpeg_fops = { ++ .owner = THIS_MODULE, ++ .open = mpeg_open, ++ .release = mpeg_release, ++ .read = mpeg_read, ++ .poll = mpeg_poll, ++ .mmap = mpeg_mmap, ++ .ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { ++ .vidioc_querystd = vidioc_g_std, ++ .vidioc_g_std = vidioc_g_std, ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, ++ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, ++ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, ++ .vidioc_log_status = vidioc_log_status, ++ .vidioc_querymenu = vidioc_querymenu, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_g_chip_ident = cx23885_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = cx23885_g_register, ++ .vidioc_s_register = cx23885_s_register, ++#endif ++}; ++ ++static struct video_device cx23885_mpeg_template = { ++ .name = "cx23885", ++ .fops = &mpeg_fops, ++ .ioctl_ops = &mpeg_ioctl_ops, ++ .tvnorms = CX23885_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++}; ++ ++void cx23885_417_unregister(struct cx23885_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (dev->v4l_device) { ++ if (video_is_registered(dev->v4l_device)) ++ video_unregister_device(dev->v4l_device); ++ else ++ video_device_release(dev->v4l_device); ++ dev->v4l_device = NULL; ++ } ++} ++ ++static struct video_device *cx23885_video_dev_alloc( ++ struct cx23885_tsport *tsport, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ struct cx23885_dev *dev = tsport->dev; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template; ++ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)", ++ cx23885_boards[tsport->dev->board].name, type); ++ vfd->parent = &pci->dev; ++ vfd->release = video_device_release; ++ return vfd; ++} ++ ++int cx23885_417_register(struct cx23885_dev *dev) ++{ ++ /* FIXME: Port1 hardcoded here */ ++ int err = -ENODEV; ++ struct cx23885_tsport *tsport = &dev->ts1; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER) ++ return err; ++ ++ /* Set default TV standard */ ++ dev->encodernorm = cx23885_tvnorms[0]; ++ ++ if (dev->encodernorm.id & V4L2_STD_525_60) ++ tsport->height = 480; ++ else ++ tsport->height = 576; ++ ++ tsport->width = 720; ++ cx2341x_fill_defaults(&dev->mpeg_params); ++ ++ dev->mpeg_params.port = CX2341X_PORT_SERIAL; ++ ++ /* Allocate and initialize V4L video device */ ++ dev->v4l_device = cx23885_video_dev_alloc(tsport, ++ dev->pci, &cx23885_mpeg_template, "mpeg"); ++ video_set_drvdata(dev->v4l_device, dev); ++ err = video_register_device(dev->v4l_device, ++ VFL_TYPE_GRABBER, -1); ++ if (err < 0) { ++ printk(KERN_INFO "%s: can't register mpeg device\n", dev->name); ++ return err; ++ } ++ ++ printk(KERN_INFO "%s: registered device %s [mpeg]\n", ++ dev->name, video_device_node_name(dev->v4l_device)); ++ ++ /* ST: Configure the encoder paramaters, but don't begin ++ * encoding, this resolves an issue where the first time the ++ * encoder is started video can be choppy. ++ */ ++ cx23885_initialize_codec(dev, 0); ++ ++ return 0; ++} ++ ++MODULE_FIRMWARE(CX23885_FIRM_IMAGE_NAME); +diff --git a/drivers/media/pci/cx23885/cx23885-alsa.c b/drivers/media/pci/cx23885/cx23885-alsa.c +new file mode 100644 +index 0000000..c6c9bd5 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-alsa.c +@@ -0,0 +1,537 @@ ++/* ++ * ++ * Support for CX23885 analog audio capture ++ * ++ * (c) 2008 Mijhail Moreyra ++ * Adapted from cx88-alsa.c ++ * (c) 2009 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#include "cx23885.h" ++#include "cx23885-reg.h" ++ ++#define AUDIO_SRAM_CHANNEL SRAM_CH07 ++ ++#define dprintk(level, fmt, arg...) do { \ ++ if (audio_debug + 1 > level) \ ++ printk(KERN_INFO "%s: " fmt, chip->dev->name , ## arg); \ ++} while(0) ++ ++#define dprintk_core(level, fmt, arg...) if (audio_debug >= level) \ ++ printk(KERN_DEBUG "%s: " fmt, chip->dev->name , ## arg) ++ ++/**************************************************************************** ++ Module global static vars ++ ****************************************************************************/ ++ ++static unsigned int disable_analog_audio; ++module_param(disable_analog_audio, int, 0644); ++MODULE_PARM_DESC(disable_analog_audio, "disable analog audio ALSA driver"); ++ ++static unsigned int audio_debug; ++module_param(audio_debug, int, 0644); ++MODULE_PARM_DESC(audio_debug, "enable debug messages [analog audio]"); ++ ++/**************************************************************************** ++ Board specific funtions ++ ****************************************************************************/ ++ ++/* Constants taken from cx88-reg.h */ ++#define AUD_INT_DN_RISCI1 (1 << 0) ++#define AUD_INT_UP_RISCI1 (1 << 1) ++#define AUD_INT_RDS_DN_RISCI1 (1 << 2) ++#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ ++#define AUD_INT_UP_RISCI2 (1 << 5) ++#define AUD_INT_RDS_DN_RISCI2 (1 << 6) ++#define AUD_INT_DN_SYNC (1 << 12) ++#define AUD_INT_UP_SYNC (1 << 13) ++#define AUD_INT_RDS_DN_SYNC (1 << 14) ++#define AUD_INT_OPC_ERR (1 << 16) ++#define AUD_INT_BER_IRQ (1 << 20) ++#define AUD_INT_MCHG_IRQ (1 << 21) ++#define GP_COUNT_CONTROL_RESET 0x3 ++ ++/* ++ * BOARD Specific: Sets audio DMA ++ */ ++ ++static int cx23885_start_audio_dma(struct cx23885_audio_dev *chip) ++{ ++ struct cx23885_audio_buffer *buf = chip->buf; ++ struct cx23885_dev *dev = chip->dev; ++ struct sram_channel *audio_ch = ++ &dev->sram_channels[AUDIO_SRAM_CHANNEL]; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ ++ cx_clear(AUD_INT_DMA_CTL, 0x11); ++ ++ /* setup fifo + format - out channel */ ++ cx23885_sram_channel_setup(chip->dev, audio_ch, buf->bpl, ++ buf->risc.dma); ++ ++ /* sets bpl size */ ++ cx_write(AUD_INT_A_LNGTH, buf->bpl); ++ ++ /* This is required to get good audio (1 seems to be ok) */ ++ cx_write(AUD_INT_A_MODE, 1); ++ ++ /* reset counter */ ++ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); ++ atomic_set(&chip->count, 0); ++ ++ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " ++ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start+12)>>1, ++ chip->num_periods, buf->bpl * chip->num_periods); ++ ++ /* Enables corresponding bits at AUD_INT_STAT */ ++ cx_write(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | ++ AUD_INT_DN_RISCI1); ++ ++ /* Clean any pending interrupt bits already set */ ++ cx_write(AUDIO_INT_INT_STAT, ~0); ++ ++ /* enable audio irqs */ ++ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); ++ ++ /* start dma */ ++ cx_set(DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ ++ cx_set(AUD_INT_DMA_CTL, 0x11); /* audio downstream FIFO and ++ RISC enable */ ++ if (audio_debug) ++ cx23885_sram_channel_dump(chip->dev, audio_ch); ++ ++ return 0; ++} ++ ++/* ++ * BOARD Specific: Resets audio DMA ++ */ ++static int cx23885_stop_audio_dma(struct cx23885_audio_dev *chip) ++{ ++ struct cx23885_dev *dev = chip->dev; ++ dprintk(1, "Stopping audio DMA\n"); ++ ++ /* stop dma */ ++ cx_clear(AUD_INT_DMA_CTL, 0x11); ++ ++ /* disable irqs */ ++ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); ++ cx_clear(AUDIO_INT_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | ++ AUD_INT_DN_RISCI1); ++ ++ if (audio_debug) ++ cx23885_sram_channel_dump(chip->dev, ++ &dev->sram_channels[AUDIO_SRAM_CHANNEL]); ++ ++ return 0; ++} ++ ++/* ++ * BOARD Specific: Handles audio IRQ ++ */ ++int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask) ++{ ++ struct cx23885_audio_dev *chip = dev->audio_dev; ++ ++ if (0 == (status & mask)) ++ return 0; ++ ++ cx_write(AUDIO_INT_INT_STAT, status); ++ ++ /* risc op code error */ ++ if (status & AUD_INT_OPC_ERR) { ++ printk(KERN_WARNING "%s/1: Audio risc op code error\n", ++ dev->name); ++ cx_clear(AUD_INT_DMA_CTL, 0x11); ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[AUDIO_SRAM_CHANNEL]); ++ } ++ if (status & AUD_INT_DN_SYNC) { ++ dprintk(1, "Downstream sync error\n"); ++ cx_write(AUD_INT_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); ++ return 1; ++ } ++ /* risc1 downstream */ ++ if (status & AUD_INT_DN_RISCI1) { ++ atomic_set(&chip->count, cx_read(AUD_INT_A_GPCNT)); ++ snd_pcm_period_elapsed(chip->substream); ++ } ++ /* FIXME: Any other status should deserve a special handling? */ ++ ++ return 1; ++} ++ ++static int dsp_buffer_free(struct cx23885_audio_dev *chip) ++{ ++ BUG_ON(!chip->dma_size); ++ ++ dprintk(2, "Freeing buffer\n"); ++ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); ++ videobuf_dma_free(chip->dma_risc); ++ btcx_riscmem_free(chip->pci, &chip->buf->risc); ++ kfree(chip->buf); ++ ++ chip->dma_risc = NULL; ++ chip->dma_size = 0; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ ALSA PCM Interface ++ ****************************************************************************/ ++ ++/* ++ * Digital hardware definition ++ */ ++#define DEFAULT_FIFO_SIZE 4096 ++ ++static struct snd_pcm_hardware snd_cx23885_digital_hw = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ ++ .rates = SNDRV_PCM_RATE_48000, ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ /* Analog audio output will be full of clicks and pops if there ++ are not exactly four lines in the SRAM FIFO buffer. */ ++ .period_bytes_min = DEFAULT_FIFO_SIZE/4, ++ .period_bytes_max = DEFAULT_FIFO_SIZE/4, ++ .periods_min = 1, ++ .periods_max = 1024, ++ .buffer_bytes_max = (1024*1024), ++}; ++ ++/* ++ * audio pcm capture open callback ++ */ ++static int snd_cx23885_pcm_open(struct snd_pcm_substream *substream) ++{ ++ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ ++ if (!chip) { ++ printk(KERN_ERR "BUG: cx23885 can't find device struct." ++ " Can't proceed with open\n"); ++ return -ENODEV; ++ } ++ ++ err = snd_pcm_hw_constraint_pow2(runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) ++ goto _error; ++ ++ chip->substream = substream; ++ ++ runtime->hw = snd_cx23885_digital_hw; ++ ++ if (chip->dev->sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != ++ DEFAULT_FIFO_SIZE) { ++ unsigned int bpl = chip->dev-> ++ sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 4; ++ bpl &= ~7; /* must be multiple of 8 */ ++ runtime->hw.period_bytes_min = bpl; ++ runtime->hw.period_bytes_max = bpl; ++ } ++ ++ return 0; ++_error: ++ dprintk(1, "Error opening PCM!\n"); ++ return err; ++} ++ ++/* ++ * audio close callback ++ */ ++static int snd_cx23885_close(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * hw_params callback ++ */ ++static int snd_cx23885_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct videobuf_dmabuf *dma; ++ ++ struct cx23885_audio_buffer *buf; ++ int ret; ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ chip->period_size = params_period_bytes(hw_params); ++ chip->num_periods = params_periods(hw_params); ++ chip->dma_size = chip->period_size * params_periods(hw_params); ++ ++ BUG_ON(!chip->dma_size); ++ BUG_ON(chip->num_periods & (chip->num_periods-1)); ++ ++ buf = kzalloc(sizeof(*buf), GFP_KERNEL); ++ if (NULL == buf) ++ return -ENOMEM; ++ ++ buf->bpl = chip->period_size; ++ ++ dma = &buf->dma; ++ videobuf_dma_init(dma); ++ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, ++ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); ++ if (ret < 0) ++ goto error; ++ ++ ret = videobuf_dma_map(&chip->pci->dev, dma); ++ if (ret < 0) ++ goto error; ++ ++ ret = cx23885_risc_databuffer(chip->pci, &buf->risc, dma->sglist, ++ chip->period_size, chip->num_periods, 1); ++ if (ret < 0) ++ goto error; ++ ++ /* Loop back to start of program */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ chip->buf = buf; ++ chip->dma_risc = dma; ++ ++ substream->runtime->dma_area = chip->dma_risc->vaddr; ++ substream->runtime->dma_bytes = chip->dma_size; ++ substream->runtime->dma_addr = 0; ++ ++ return 0; ++ ++error: ++ kfree(buf); ++ return ret; ++} ++ ++/* ++ * hw free callback ++ */ ++static int snd_cx23885_hw_free(struct snd_pcm_substream *substream) ++{ ++ ++ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * prepare callback ++ */ ++static int snd_cx23885_prepare(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * trigger callback ++ */ ++static int snd_cx23885_card_trigger(struct snd_pcm_substream *substream, ++ int cmd) ++{ ++ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); ++ int err; ++ ++ /* Local interrupts are already disabled by ALSA */ ++ spin_lock(&chip->lock); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ err = cx23885_start_audio_dma(chip); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ err = cx23885_stop_audio_dma(chip); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ ++ spin_unlock(&chip->lock); ++ ++ return err; ++} ++ ++/* ++ * pointer callback ++ */ ++static snd_pcm_uframes_t snd_cx23885_pointer( ++ struct snd_pcm_substream *substream) ++{ ++ struct cx23885_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ u16 count; ++ ++ count = atomic_read(&chip->count); ++ ++ return runtime->period_size * (count & (runtime->periods-1)); ++} ++ ++/* ++ * page callback (needed for mmap) ++ */ ++static struct page *snd_cx23885_page(struct snd_pcm_substream *substream, ++ unsigned long offset) ++{ ++ void *pageptr = substream->runtime->dma_area + offset; ++ return vmalloc_to_page(pageptr); ++} ++ ++/* ++ * operators ++ */ ++static struct snd_pcm_ops snd_cx23885_pcm_ops = { ++ .open = snd_cx23885_pcm_open, ++ .close = snd_cx23885_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_cx23885_hw_params, ++ .hw_free = snd_cx23885_hw_free, ++ .prepare = snd_cx23885_prepare, ++ .trigger = snd_cx23885_card_trigger, ++ .pointer = snd_cx23885_pointer, ++ .page = snd_cx23885_page, ++}; ++ ++/* ++ * create a PCM device ++ */ ++static int snd_cx23885_pcm(struct cx23885_audio_dev *chip, int device, ++ char *name) ++{ ++ int err; ++ struct snd_pcm *pcm; ++ ++ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); ++ if (err < 0) ++ return err; ++ pcm->private_data = chip; ++ strcpy(pcm->name, name); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx23885_pcm_ops); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ Basic Flow for Sound Devices ++ ****************************************************************************/ ++ ++/* ++ * Alsa Constructor - Component probe ++ */ ++ ++struct cx23885_audio_dev *cx23885_audio_register(struct cx23885_dev *dev) ++{ ++ struct snd_card *card; ++ struct cx23885_audio_dev *chip; ++ int err; ++ ++ if (disable_analog_audio) ++ return NULL; ++ ++ if (dev->sram_channels[AUDIO_SRAM_CHANNEL].cmds_start == 0) { ++ printk(KERN_WARNING "%s(): Missing SRAM channel configuration " ++ "for analog TV Audio\n", __func__); ++ return NULL; ++ } ++ ++ err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, ++ THIS_MODULE, sizeof(struct cx23885_audio_dev), &card); ++ if (err < 0) ++ goto error; ++ ++ chip = (struct cx23885_audio_dev *) card->private_data; ++ chip->dev = dev; ++ chip->pci = dev->pci; ++ chip->card = card; ++ spin_lock_init(&chip->lock); ++ ++ snd_card_set_dev(card, &dev->pci->dev); ++ ++ err = snd_cx23885_pcm(chip, 0, "CX23885 Digital"); ++ if (err < 0) ++ goto error; ++ ++ strcpy(card->driver, "CX23885"); ++ sprintf(card->shortname, "Conexant CX23885"); ++ sprintf(card->longname, "%s at %s", card->shortname, dev->name); ++ ++ err = snd_card_register(card); ++ if (err < 0) ++ goto error; ++ ++ dprintk(0, "registered ALSA audio device\n"); ++ ++ return chip; ++ ++error: ++ snd_card_free(card); ++ printk(KERN_ERR "%s(): Failed to register analog " ++ "audio adapter\n", __func__); ++ ++ return NULL; ++} ++ ++/* ++ * ALSA destructor ++ */ ++void cx23885_audio_unregister(struct cx23885_dev *dev) ++{ ++ struct cx23885_audio_dev *chip = dev->audio_dev; ++ ++ snd_card_free(chip->card); ++} +diff --git a/drivers/media/pci/cx23885/cx23885-av.c b/drivers/media/pci/cx23885/cx23885-av.c +new file mode 100644 +index 0000000..e958a01 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-av.c +@@ -0,0 +1,36 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * AV device support routines - non-input, non-vl42_subdev routines ++ * ++ * Copyright (C) 2010 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include "cx23885.h" ++#include "cx23885-av.h" ++ ++void cx23885_av_work_handler(struct work_struct *work) ++{ ++ struct cx23885_dev *dev = ++ container_of(work, struct cx23885_dev, cx25840_work); ++ bool handled; ++ ++ v4l2_subdev_call(dev->sd_cx25840, core, interrupt_service_routine, ++ PCI_MSK_AV_CORE, &handled); ++ cx23885_irq_enable(dev, PCI_MSK_AV_CORE); ++} +diff --git a/drivers/media/pci/cx23885/cx23885-av.h b/drivers/media/pci/cx23885/cx23885-av.h +new file mode 100644 +index 0000000..d2915c3 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-av.h +@@ -0,0 +1,27 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * AV device support routines - non-input, non-vl42_subdev routines ++ * ++ * Copyright (C) 2010 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#ifndef _CX23885_AV_H_ ++#define _CX23885_AV_H_ ++void cx23885_av_work_handler(struct work_struct *work); ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c +new file mode 100644 +index 0000000..7e923f8 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-cards.c +@@ -0,0 +1,1811 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include "tuner-xc2028.h" ++#include "netup-eeprom.h" ++#include "netup-init.h" ++#include "altera-ci.h" ++#include "xc4000.h" ++#include "xc5000.h" ++#include "cx23888-ir.h" ++ ++static unsigned int netup_card_rev = 4; ++module_param(netup_card_rev, int, 0644); ++MODULE_PARM_DESC(netup_card_rev, ++ "NetUP Dual DVB-T/C CI card revision"); ++static unsigned int enable_885_ir; ++module_param(enable_885_ir, int, 0644); ++MODULE_PARM_DESC(enable_885_ir, ++ "Enable integrated IR controller for supported\n" ++ "\t\t CX2388[57] boards that are wired for it:\n" ++ "\t\t\tHVR-1250 (reported safe)\n" ++ "\t\t\tTerraTec Cinergy T PCIe Dual (not well tested, appears to be safe)\n" ++ "\t\t\tTeVii S470 (reported unsafe)\n" ++ "\t\t This can cause an interrupt storm with some cards.\n" ++ "\t\t Default: 0 [Disabled]"); ++ ++/* ------------------------------------------------------------------ */ ++/* board config info */ ++ ++struct cx23885_board cx23885_boards[] = { ++ [CX23885_BOARD_UNKNOWN] = { ++ .name = "UNKNOWN/GENERIC", ++ /* Ensure safe default for unknown boards */ ++ .clk_freq = 0, ++ .input = {{ ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE2, ++ .vmux = 1, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE3, ++ .vmux = 2, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE4, ++ .vmux = 3, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1800lp] = { ++ .name = "Hauppauge WinTV-HVR1800lp", ++ .portc = CX23885_MPEG_DVB, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xff00, ++ }, { ++ .type = CX23885_VMUX_DEBUG, ++ .vmux = 0, ++ .gpio0 = 0xff01, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xff02, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xff02, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1800] = { ++ .name = "Hauppauge WinTV-HVR1800", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_ENCODER, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .tuner_addr = 0x42, /* 0x84 >> 1 */ ++ .tuner_bus = 1, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1, ++ .amux = CX25840_AUDIO8, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN6_CH1, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1250] = { ++ .name = "Hauppauge WinTV-HVR1250", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portc = CX23885_MPEG_DVB, ++#ifdef MT2131_NO_ANALOG_SUPPORT_YET ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .tuner_addr = 0x42, /* 0x84 >> 1 */ ++ .tuner_bus = 1, ++#endif ++ .force_bff = 1, ++ .input = {{ ++#ifdef MT2131_NO_ANALOG_SUPPORT_YET ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1, ++ .amux = CX25840_AUDIO8, ++ .gpio0 = 0xff00, ++ }, { ++#endif ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN6_CH1, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0xff02, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0xff02, ++ } }, ++ }, ++ [CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP] = { ++ .name = "DViCO FusionHDTV5 Express", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1500Q] = { ++ .name = "Hauppauge WinTV-HVR1500Q", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1500] = { ++ .name = "Hauppauge WinTV-HVR1500", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, /* 0xc2 >> 1 */ ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN6_CH1, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .gpio0 = 0, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1200] = { ++ .name = "Hauppauge WinTV-HVR1200", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1700] = { ++ .name = "Hauppauge WinTV-HVR1700", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1400] = { ++ .name = "Hauppauge WinTV-HVR1400", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = { ++ .name = "DViCO FusionHDTV7 Dual Express", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP] = { ++ .name = "DViCO FusionHDTV DVB-T Dual Express", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H] = { ++ .name = "Leadtek Winfast PxDVR3200 H", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000] = { ++ .name = "Leadtek Winfast PxDVR3200 H XC4000", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_XC4000, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN2_CH1 | ++ CX25840_VIN5_CH2 | ++ CX25840_NONE0_CH3, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE1, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ }, { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_VIN7_CH1 | ++ CX25840_VIN6_CH2 | ++ CX25840_VIN8_CH3 | ++ CX25840_COMPONENT_ON, ++ } }, ++ }, ++ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = { ++ .name = "Compro VideoMate E650F", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_TBS_6920] = { ++ .name = "TurboSight TBS 6920", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_TEVII_S470] = { ++ .name = "TeVii S470", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_DVBWORLD_2005] = { ++ .name = "DVBWorld DVB-S2 2005", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = { ++ .ci_type = 1, ++ .name = "NetUP Dual DVB-S2 CI", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1270] = { ++ .name = "Hauppauge WinTV-HVR1270", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1275] = { ++ .name = "Hauppauge WinTV-HVR1275", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1255] = { ++ .name = "Hauppauge WinTV-HVR1255", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = 0x42, /* 0x84 >> 1 */ ++ .force_bff = 1, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1 | ++ CX25840_DIF_ON, ++ .amux = CX25840_AUDIO8, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN6_CH1, ++ .amux = CX25840_AUDIO7, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO7, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1255_22111] = { ++ .name = "Hauppauge WinTV-HVR1255", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = 0x42, /* 0x84 >> 1 */ ++ .force_bff = 1, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1 | ++ CX25840_DIF_ON, ++ .amux = CX25840_AUDIO8, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO7, ++ } }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1210] = { ++ .name = "Hauppauge WinTV-HVR1210", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_MYGICA_X8506] = { ++ .name = "Mygica X8506 DMB-TH", ++ .tuner_type = TUNER_XC5000, ++ .tuner_addr = 0x61, ++ .tuner_bus = 1, ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_DVB, ++ .input = { ++ { ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_COMPOSITE2, ++ }, ++ { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE8, ++ }, ++ { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ }, ++ { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_COMPONENT_ON | ++ CX25840_VIN1_CH1 | ++ CX25840_VIN6_CH2 | ++ CX25840_VIN7_CH3, ++ }, ++ }, ++ }, ++ [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { ++ .name = "Magic-Pro ProHDTV Extreme 2", ++ .tuner_type = TUNER_XC5000, ++ .tuner_addr = 0x61, ++ .tuner_bus = 1, ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_DVB, ++ .input = { ++ { ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_COMPOSITE2, ++ }, ++ { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE8, ++ }, ++ { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ }, ++ { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_COMPONENT_ON | ++ CX25840_VIN1_CH1 | ++ CX25840_VIN6_CH2 | ++ CX25840_VIN7_CH3, ++ }, ++ }, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1850] = { ++ .name = "Hauppauge WinTV-HVR1850", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_ENCODER, ++ .portc = CX23885_MPEG_DVB, ++ .tuner_type = TUNER_ABSENT, ++ .tuner_addr = 0x42, /* 0x84 >> 1 */ ++ .force_bff = 1, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN5_CH2 | ++ CX25840_VIN2_CH1 | ++ CX25840_DIF_ON, ++ .amux = CX25840_AUDIO8, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN6_CH1, ++ .amux = CX25840_AUDIO7, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN7_CH3 | ++ CX25840_VIN4_CH2 | ++ CX25840_VIN8_CH1 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO7, ++ } }, ++ }, ++ [CX23885_BOARD_COMPRO_VIDEOMATE_E800] = { ++ .name = "Compro VideoMate E800", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR1290] = { ++ .name = "Hauppauge WinTV-HVR1290", ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_MYGICA_X8558PRO] = { ++ .name = "Mygica X8558 PRO DMB-TH", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = { ++ .name = "LEADTEK WinFast PxTV1200", ++ .porta = CX23885_ANALOG_VIDEO, ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .tuner_bus = 1, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN2_CH1 | ++ CX25840_VIN5_CH2 | ++ CX25840_NONE0_CH3, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE1, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ }, { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_VIN7_CH1 | ++ CX25840_VIN6_CH2 | ++ CX25840_VIN8_CH3 | ++ CX25840_COMPONENT_ON, ++ } }, ++ }, ++ [CX23885_BOARD_GOTVIEW_X5_3D_HYBRID] = { ++ .name = "GoTView X5 3D Hybrid", ++ .tuner_type = TUNER_XC5000, ++ .tuner_addr = 0x64, ++ .tuner_bus = 1, ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_DVB, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN2_CH1 | ++ CX25840_VIN5_CH2, ++ .gpio0 = 0x02, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX23885_VMUX_COMPOSITE1, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ } }, ++ }, ++ [CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF] = { ++ .ci_type = 2, ++ .name = "NetUP Dual DVB-T/C-CI RF", ++ .porta = CX23885_ANALOG_VIDEO, ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ .num_fds_portb = 2, ++ .num_fds_portc = 2, ++ .tuner_type = TUNER_XC5000, ++ .tuner_addr = 0x64, ++ .input = { { ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_COMPOSITE1, ++ } }, ++ }, ++ [CX23885_BOARD_MPX885] = { ++ .name = "MPX-885", ++ .porta = CX23885_ANALOG_VIDEO, ++ .input = {{ ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE1, ++ .amux = CX25840_AUDIO6, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE2, ++ .vmux = CX25840_COMPOSITE2, ++ .amux = CX25840_AUDIO6, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE3, ++ .vmux = CX25840_COMPOSITE3, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0, ++ }, { ++ .type = CX23885_VMUX_COMPOSITE4, ++ .vmux = CX25840_COMPOSITE4, ++ .amux = CX25840_AUDIO7, ++ .gpio0 = 0, ++ } }, ++ }, ++ [CX23885_BOARD_MYGICA_X8507] = { ++ .name = "Mygica X8507", ++ .tuner_type = TUNER_XC5000, ++ .tuner_addr = 0x61, ++ .tuner_bus = 1, ++ .porta = CX23885_ANALOG_VIDEO, ++ .input = { ++ { ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_COMPOSITE2, ++ .amux = CX25840_AUDIO8, ++ }, ++ { ++ .type = CX23885_VMUX_COMPOSITE1, ++ .vmux = CX25840_COMPOSITE8, ++ .amux = CX25840_AUDIO7, ++ }, ++ { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_SVIDEO_LUMA3 | ++ CX25840_SVIDEO_CHROMA4, ++ .amux = CX25840_AUDIO7, ++ }, ++ { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_COMPONENT_ON | ++ CX25840_VIN1_CH1 | ++ CX25840_VIN6_CH2 | ++ CX25840_VIN7_CH3, ++ .amux = CX25840_AUDIO7, ++ }, ++ }, ++ }, ++ [CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL] = { ++ .name = "TerraTec Cinergy T PCIe Dual", ++ .portb = CX23885_MPEG_DVB, ++ .portc = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_TEVII_S471] = { ++ .name = "TeVii S471", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_PROF_8000] = { ++ .name = "Prof Revolution DVB-S2 8000", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_HAUPPAUGE_HVR4400] = { ++ .name = "Hauppauge WinTV-HVR4400", ++ .portb = CX23885_MPEG_DVB, ++ }, ++ [CX23885_BOARD_AVERMEDIA_HC81R] = { ++ .name = "AVerTV Hybrid Express Slim HC81R", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, /* 0xc2 >> 1 */ ++ .tuner_bus = 1, ++ .porta = CX23885_ANALOG_VIDEO, ++ .input = {{ ++ .type = CX23885_VMUX_TELEVISION, ++ .vmux = CX25840_VIN2_CH1 | ++ CX25840_VIN5_CH2 | ++ CX25840_NONE0_CH3 | ++ CX25840_NONE1_CH3, ++ .amux = CX25840_AUDIO8, ++ }, { ++ .type = CX23885_VMUX_SVIDEO, ++ .vmux = CX25840_VIN8_CH1 | ++ CX25840_NONE_CH2 | ++ CX25840_VIN7_CH3 | ++ CX25840_SVIDEO_ON, ++ .amux = CX25840_AUDIO6, ++ }, { ++ .type = CX23885_VMUX_COMPONENT, ++ .vmux = CX25840_VIN1_CH1 | ++ CX25840_NONE_CH2 | ++ CX25840_NONE0_CH3 | ++ CX25840_NONE1_CH3, ++ .amux = CX25840_AUDIO6, ++ } }, ++ } ++}; ++const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); ++ ++/* ------------------------------------------------------------------ */ ++/* PCI subsystem IDs */ ++ ++struct cx23885_subid cx23885_subids[] = { ++ { ++ .subvendor = 0x0070, ++ .subdevice = 0x3400, ++ .card = CX23885_BOARD_UNKNOWN, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7600, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1800lp, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7800, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1800, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7801, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1800, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7809, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1800, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7911, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1250, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xd500, ++ .card = CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7790, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7797, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1500Q, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7710, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1500, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x7717, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1500, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x71d1, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1200, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x71d3, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1200, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8101, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1700, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8010, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1400, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xd618, ++ .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb78, ++ .card = CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6681, ++ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6f39, ++ .card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000, ++ }, { ++ .subvendor = 0x185b, ++ .subdevice = 0xe800, ++ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F, ++ }, { ++ .subvendor = 0x6920, ++ .subdevice = 0x8888, ++ .card = CX23885_BOARD_TBS_6920, ++ }, { ++ .subvendor = 0xd470, ++ .subdevice = 0x9022, ++ .card = CX23885_BOARD_TEVII_S470, ++ }, { ++ .subvendor = 0x0001, ++ .subdevice = 0x2005, ++ .card = CX23885_BOARD_DVBWORLD_2005, ++ }, { ++ .subvendor = 0x1b55, ++ .subdevice = 0x2a2c, ++ .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2211, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1270, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2215, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1275, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x221d, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1275, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2251, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1255, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2259, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1255_22111, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2291, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2295, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x2299, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x229d, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f0, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f1, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1255, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f2, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1275, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f3, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f4, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x22f5, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8651, ++ .card = CX23885_BOARD_MYGICA_X8506, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8657, ++ .card = CX23885_BOARD_MAGICPRO_PROHDTVE2, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8541, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1850, ++ }, { ++ .subvendor = 0x1858, ++ .subdevice = 0xe800, ++ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8551, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR1290, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8578, ++ .card = CX23885_BOARD_MYGICA_X8558PRO, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6f22, ++ .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200, ++ }, { ++ .subvendor = 0x5654, ++ .subdevice = 0x2390, ++ .card = CX23885_BOARD_GOTVIEW_X5_3D_HYBRID, ++ }, { ++ .subvendor = 0x1b55, ++ .subdevice = 0xe2e4, ++ .card = CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8502, ++ .card = CX23885_BOARD_MYGICA_X8507, ++ }, { ++ .subvendor = 0x153b, ++ .subdevice = 0x117e, ++ .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL, ++ }, { ++ .subvendor = 0xd471, ++ .subdevice = 0x9022, ++ .card = CX23885_BOARD_TEVII_S471, ++ }, { ++ .subvendor = 0x8000, ++ .subdevice = 0x3034, ++ .card = CX23885_BOARD_PROF_8000, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0xc108, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR4400, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0xc138, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR4400, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0xc12a, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR4400, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0xc1f8, ++ .card = CX23885_BOARD_HAUPPAUGE_HVR4400, ++ }, { ++ .subvendor = 0x1461, ++ .subdevice = 0xd939, ++ .card = CX23885_BOARD_AVERMEDIA_HC81R, ++ }, ++}; ++const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); ++ ++void cx23885_card_list(struct cx23885_dev *dev) ++{ ++ int i; ++ ++ if (0 == dev->pci->subsystem_vendor && ++ 0 == dev->pci->subsystem_device) { ++ printk(KERN_INFO ++ "%s: Board has no valid PCIe Subsystem ID and can't\n" ++ "%s: be autodetected. Pass card= insmod option\n" ++ "%s: to workaround that. Redirect complaints to the\n" ++ "%s: vendor of the TV card. Best regards,\n" ++ "%s: -- tux\n", ++ dev->name, dev->name, dev->name, dev->name, dev->name); ++ } else { ++ printk(KERN_INFO ++ "%s: Your board isn't known (yet) to the driver.\n" ++ "%s: Try to pick one of the existing card configs via\n" ++ "%s: card= insmod option. Updating to the latest\n" ++ "%s: version might help as well.\n", ++ dev->name, dev->name, dev->name, dev->name); ++ } ++ printk(KERN_INFO "%s: Here is a list of valid choices for the card= insmod option:\n", ++ dev->name); ++ for (i = 0; i < cx23885_bcount; i++) ++ printk(KERN_INFO "%s: card=%d -> %s\n", ++ dev->name, i, cx23885_boards[i].name); ++} ++ ++static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) ++{ ++ struct tveeprom tv; ++ ++ tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, ++ eeprom_data); ++ ++ /* Make sure we support the board model */ ++ switch (tv.model) { ++ case 22001: ++ /* WinTV-HVR1270 (PCIe, Retail, half height) ++ * ATSC/QAM and basic analog, IR Blast */ ++ case 22009: ++ /* WinTV-HVR1210 (PCIe, Retail, half height) ++ * DVB-T and basic analog, IR Blast */ ++ case 22011: ++ /* WinTV-HVR1270 (PCIe, Retail, half height) ++ * ATSC/QAM and basic analog, IR Recv */ ++ case 22019: ++ /* WinTV-HVR1210 (PCIe, Retail, half height) ++ * DVB-T and basic analog, IR Recv */ ++ case 22021: ++ /* WinTV-HVR1275 (PCIe, Retail, half height) ++ * ATSC/QAM and basic analog, IR Recv */ ++ case 22029: ++ /* WinTV-HVR1210 (PCIe, Retail, half height) ++ * DVB-T and basic analog, IR Recv */ ++ case 22101: ++ /* WinTV-HVR1270 (PCIe, Retail, full height) ++ * ATSC/QAM and basic analog, IR Blast */ ++ case 22109: ++ /* WinTV-HVR1210 (PCIe, Retail, full height) ++ * DVB-T and basic analog, IR Blast */ ++ case 22111: ++ /* WinTV-HVR1270 (PCIe, Retail, full height) ++ * ATSC/QAM and basic analog, IR Recv */ ++ case 22119: ++ /* WinTV-HVR1210 (PCIe, Retail, full height) ++ * DVB-T and basic analog, IR Recv */ ++ case 22121: ++ /* WinTV-HVR1275 (PCIe, Retail, full height) ++ * ATSC/QAM and basic analog, IR Recv */ ++ case 22129: ++ /* WinTV-HVR1210 (PCIe, Retail, full height) ++ * DVB-T and basic analog, IR Recv */ ++ case 71009: ++ /* WinTV-HVR1200 (PCIe, Retail, full height) ++ * DVB-T and basic analog */ ++ case 71359: ++ /* WinTV-HVR1200 (PCIe, OEM, half height) ++ * DVB-T and basic analog */ ++ case 71439: ++ /* WinTV-HVR1200 (PCIe, OEM, half height) ++ * DVB-T and basic analog */ ++ case 71449: ++ /* WinTV-HVR1200 (PCIe, OEM, full height) ++ * DVB-T and basic analog */ ++ case 71939: ++ /* WinTV-HVR1200 (PCIe, OEM, half height) ++ * DVB-T and basic analog */ ++ case 71949: ++ /* WinTV-HVR1200 (PCIe, OEM, full height) ++ * DVB-T and basic analog */ ++ case 71959: ++ /* WinTV-HVR1200 (PCIe, OEM, full height) ++ * DVB-T and basic analog */ ++ case 71979: ++ /* WinTV-HVR1200 (PCIe, OEM, half height) ++ * DVB-T and basic analog */ ++ case 71999: ++ /* WinTV-HVR1200 (PCIe, OEM, full height) ++ * DVB-T and basic analog */ ++ case 76601: ++ /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual ++ channel ATSC and MPEG2 HW Encoder */ ++ case 77001: ++ /* WinTV-HVR1500 (Express Card, OEM, No IR, ATSC ++ and Basic analog */ ++ case 77011: ++ /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC ++ and Basic analog */ ++ case 77041: ++ /* WinTV-HVR1500Q (Express Card, OEM, No IR, ATSC/QAM ++ and Basic analog */ ++ case 77051: ++ /* WinTV-HVR1500Q (Express Card, Retail, No IR, ATSC/QAM ++ and Basic analog */ ++ case 78011: ++ /* WinTV-HVR1800 (PCIe, Retail, 3.5mm in, IR, No FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ case 78501: ++ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ case 78521: ++ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ case 78531: ++ /* WinTV-HVR1800 (PCIe, OEM, RCA in, No IR, No FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ case 78631: ++ /* WinTV-HVR1800 (PCIe, OEM, No IR, No FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ case 79001: ++ /* WinTV-HVR1250 (PCIe, Retail, IR, full height, ++ ATSC and Basic analog */ ++ case 79101: ++ /* WinTV-HVR1250 (PCIe, Retail, IR, half height, ++ ATSC and Basic analog */ ++ case 79501: ++ /* WinTV-HVR1250 (PCIe, No IR, half height, ++ ATSC [at least] and Basic analog) */ ++ case 79561: ++ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ++ ATSC and Basic analog */ ++ case 79571: ++ /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ++ ATSC and Basic analog */ ++ case 79671: ++ /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ++ ATSC and Basic analog */ ++ case 80019: ++ /* WinTV-HVR1400 (Express Card, Retail, IR, ++ * DVB-T and Basic analog */ ++ case 81509: ++ /* WinTV-HVR1700 (PCIe, OEM, No IR, half height) ++ * DVB-T and MPEG2 HW Encoder */ ++ case 81519: ++ /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) ++ * DVB-T and MPEG2 HW Encoder */ ++ break; ++ case 85021: ++ /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM, ++ Dual channel ATSC and MPEG2 HW Encoder */ ++ break; ++ case 85721: ++ /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, ++ Dual channel ATSC and Basic analog */ ++ break; ++ default: ++ printk(KERN_WARNING "%s: warning: " ++ "unknown hauppauge model #%d\n", ++ dev->name, tv.model); ++ break; ++ } ++ ++ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", ++ dev->name, tv.model); ++} ++ ++int cx23885_tuner_callback(void *priv, int component, int command, int arg) ++{ ++ struct cx23885_tsport *port = priv; ++ struct cx23885_dev *dev = port->dev; ++ u32 bitmask = 0; ++ ++ if ((command == XC2028_RESET_CLK) || (command == XC2028_I2C_FLUSH)) ++ return 0; ++ ++ if (command != 0) { ++ printk(KERN_ERR "%s(): Unknown command 0x%x.\n", ++ __func__, command); ++ return -EINVAL; ++ } ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E800: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: ++ /* Tuner Reset Command */ ++ bitmask = 0x04; ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: ++ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ++ /* Two identical tuners on two different i2c buses, ++ * we need to reset the correct gpio. */ ++ if (port->nr == 1) ++ bitmask = 0x01; ++ else if (port->nr == 2) ++ bitmask = 0x04; ++ break; ++ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: ++ /* Tuner Reset Command */ ++ bitmask = 0x02; ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ altera_ci_tuner_reset(dev, port->nr); ++ break; ++ case CX23885_BOARD_AVERMEDIA_HC81R: ++ /* XC3028L Reset Command */ ++ bitmask = 1 << 2; ++ break; ++ } ++ ++ if (bitmask) { ++ /* Drive the tuner into reset and back out */ ++ cx_clear(GP0_IO, bitmask); ++ mdelay(200); ++ cx_set(GP0_IO, bitmask); ++ } ++ ++ return 0; ++} ++ ++void cx23885_gpio_setup(struct cx23885_dev *dev) ++{ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ /* GPIO-0 cx24227 demodulator reset */ ++ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ /* GPIO-0 cx24227 demodulator */ ++ /* GPIO-2 xc3028 tuner */ ++ ++ /* Put the parts into reset */ ++ cx_set(GP0_IO, 0x00050000); ++ cx_clear(GP0_IO, 0x00000005); ++ msleep(5); ++ ++ /* Bring the parts out of reset */ ++ cx_set(GP0_IO, 0x00050005); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ /* GPIO-0 cx24227 demodulator reset */ ++ /* GPIO-2 xc5000 tuner reset */ ++ cx_set(GP0_IO, 0x00050005); /* Bring the part out of reset */ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ /* GPIO-0 656_CLK */ ++ /* GPIO-1 656_D0 */ ++ /* GPIO-2 8295A Reset */ ++ /* GPIO-3-10 cx23417 data0-7 */ ++ /* GPIO-11-14 cx23417 addr0-3 */ ++ /* GPIO-15-18 cx23417 READY, CS, RD, WR */ ++ /* GPIO-19 IR_RX */ ++ ++ /* CX23417 GPIO's */ ++ /* EIO15 Zilog Reset */ ++ /* EIO14 S5H1409/CX24227 Reset */ ++ mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1); ++ ++ /* Put the demod into reset and protect the eeprom */ ++ mc417_gpio_clear(dev, GPIO_15 | GPIO_14); ++ mdelay(100); ++ ++ /* Bring the demod and blaster out of reset */ ++ mc417_gpio_set(dev, GPIO_15 | GPIO_14); ++ mdelay(100); ++ ++ /* Force the TDA8295A into reset and back */ ++ cx23885_gpio_enable(dev, GPIO_2, 1); ++ cx23885_gpio_set(dev, GPIO_2); ++ mdelay(20); ++ cx23885_gpio_clear(dev, GPIO_2); ++ mdelay(20); ++ cx23885_gpio_set(dev, GPIO_2); ++ mdelay(20); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1200: ++ /* GPIO-0 tda10048 demodulator reset */ ++ /* GPIO-2 tda18271 tuner reset */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x00050000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x00000005); ++ mdelay(20); ++ cx_set(GP0_IO, 0x00050005); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1700: ++ /* GPIO-0 TDA10048 demodulator reset */ ++ /* GPIO-2 TDA8295A Reset */ ++ /* GPIO-3-10 cx23417 data0-7 */ ++ /* GPIO-11-14 cx23417 addr0-3 */ ++ /* GPIO-15-18 cx23417 READY, CS, RD, WR */ ++ ++ /* The following GPIO's are on the interna AVCore (cx25840) */ ++ /* GPIO-19 IR_RX */ ++ /* GPIO-20 IR_TX 416/DVBT Select */ ++ /* GPIO-21 IIS DAT */ ++ /* GPIO-22 IIS WCLK */ ++ /* GPIO-23 IIS BCLK */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x00050000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x00000005); ++ mdelay(20); ++ cx_set(GP0_IO, 0x00050005); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ /* GPIO-0 Dibcom7000p demodulator reset */ ++ /* GPIO-2 xc3028L tuner reset */ ++ /* GPIO-13 LED */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x00050000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x00000005); ++ mdelay(20); ++ cx_set(GP0_IO, 0x00050005); ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: ++ /* GPIO-0 xc5000 tuner reset i2c bus 0 */ ++ /* GPIO-1 s5h1409 demod reset i2c bus 0 */ ++ /* GPIO-2 xc5000 tuner reset i2c bus 1 */ ++ /* GPIO-3 s5h1409 demod reset i2c bus 0 */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x000f0000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x0000000f); ++ mdelay(20); ++ cx_set(GP0_IO, 0x000f000f); ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ++ /* GPIO-0 portb xc3028 reset */ ++ /* GPIO-1 portb zl10353 reset */ ++ /* GPIO-2 portc xc3028 reset */ ++ /* GPIO-3 portc zl10353 reset */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x000f0000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x0000000f); ++ mdelay(20); ++ cx_set(GP0_IO, 0x000f000f); ++ break; ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E800: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: ++ /* GPIO-2 xc3028 tuner reset */ ++ ++ /* The following GPIO's are on the internal AVCore (cx25840) */ ++ /* GPIO-? zl10353 demod reset */ ++ ++ /* Put the parts into reset and back */ ++ cx_set(GP0_IO, 0x00040000); ++ mdelay(20); ++ cx_clear(GP0_IO, 0x00000004); ++ mdelay(20); ++ cx_set(GP0_IO, 0x00040004); ++ break; ++ case CX23885_BOARD_TBS_6920: ++ case CX23885_BOARD_PROF_8000: ++ cx_write(MC417_CTL, 0x00000036); ++ cx_write(MC417_OEN, 0x00001000); ++ cx_set(MC417_RWD, 0x00000002); ++ mdelay(200); ++ cx_clear(MC417_RWD, 0x00000800); ++ mdelay(200); ++ cx_set(MC417_RWD, 0x00000800); ++ mdelay(200); ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ /* GPIO-0 INTA from CiMax1 ++ GPIO-1 INTB from CiMax2 ++ GPIO-2 reset chips ++ GPIO-3 to GPIO-10 data/addr for CA ++ GPIO-11 ~CS0 to CiMax1 ++ GPIO-12 ~CS1 to CiMax2 ++ GPIO-13 ADL0 load LSB addr ++ GPIO-14 ADL1 load MSB addr ++ GPIO-15 ~RDY from CiMax ++ GPIO-17 ~RD to CiMax ++ GPIO-18 ~WR to CiMax ++ */ ++ cx_set(GP0_IO, 0x00040000); /* GPIO as out */ ++ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */ ++ cx_clear(GP0_IO, 0x00030004); ++ mdelay(100);/* reset delay */ ++ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */ ++ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ ++ /* GPIO-15 IN as ~ACK, rest as OUT */ ++ cx_write(MC417_OEN, 0x00001000); ++ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */ ++ cx_write(MC417_RWD, 0x0000c300); ++ /* enable irq */ ++ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1210: ++ /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */ ++ /* GPIO-6 I2C Gate which can isolate the demod from the bus */ ++ /* GPIO-9 Demod reset */ ++ ++ /* Put the parts into reset and back */ ++ cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1); ++ cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5); ++ cx23885_gpio_clear(dev, GPIO_9); ++ mdelay(20); ++ cx23885_gpio_set(dev, GPIO_9); ++ break; ++ case CX23885_BOARD_MYGICA_X8506: ++ case CX23885_BOARD_MAGICPRO_PROHDTVE2: ++ case CX23885_BOARD_MYGICA_X8507: ++ /* GPIO-0 (0)Analog / (1)Digital TV */ ++ /* GPIO-1 reset XC5000 */ ++ /* GPIO-2 reset LGS8GL5 / LGS8G75 */ ++ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); ++ cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); ++ mdelay(100); ++ cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); ++ mdelay(100); ++ break; ++ case CX23885_BOARD_MYGICA_X8558PRO: ++ /* GPIO-0 reset first ATBM8830 */ ++ /* GPIO-1 reset second ATBM8830 */ ++ cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); ++ cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); ++ mdelay(100); ++ cx23885_gpio_set(dev, GPIO_0 | GPIO_1); ++ mdelay(100); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ /* GPIO-0 656_CLK */ ++ /* GPIO-1 656_D0 */ ++ /* GPIO-2 Wake# */ ++ /* GPIO-3-10 cx23417 data0-7 */ ++ /* GPIO-11-14 cx23417 addr0-3 */ ++ /* GPIO-15-18 cx23417 READY, CS, RD, WR */ ++ /* GPIO-19 IR_RX */ ++ /* GPIO-20 C_IR_TX */ ++ /* GPIO-21 I2S DAT */ ++ /* GPIO-22 I2S WCLK */ ++ /* GPIO-23 I2S BCLK */ ++ /* ALT GPIO: EXP GPIO LATCH */ ++ ++ /* CX23417 GPIO's */ ++ /* GPIO-14 S5H1411/CX24228 Reset */ ++ /* GPIO-13 EEPROM write protect */ ++ mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1); ++ ++ /* Put the demod into reset and protect the eeprom */ ++ mc417_gpio_clear(dev, GPIO_14 | GPIO_13); ++ mdelay(100); ++ ++ /* Bring the demod out of reset */ ++ mc417_gpio_set(dev, GPIO_14); ++ mdelay(100); ++ ++ /* CX24228 GPIO */ ++ /* Connected to IF / Mux */ ++ break; ++ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: ++ cx_set(GP0_IO, 0x00010001); /* Bring the part out of reset */ ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ /* GPIO-0 ~INT in ++ GPIO-1 TMS out ++ GPIO-2 ~reset chips out ++ GPIO-3 to GPIO-10 data/addr for CA in/out ++ GPIO-11 ~CS out ++ GPIO-12 ADDR out ++ GPIO-13 ~WR out ++ GPIO-14 ~RD out ++ GPIO-15 ~RDY in ++ GPIO-16 TCK out ++ GPIO-17 TDO in ++ GPIO-18 TDI out ++ */ ++ cx_set(GP0_IO, 0x00060000); /* GPIO-1,2 as out */ ++ /* GPIO-0 as INT, reset & TMS low */ ++ cx_clear(GP0_IO, 0x00010006); ++ mdelay(100);/* reset delay */ ++ cx_set(GP0_IO, 0x00000004); /* reset high */ ++ cx_write(MC417_CTL, 0x00000037);/* enable GPIO-3..18 pins */ ++ /* GPIO-17 is TDO in, GPIO-15 is ~RDY in, rest is out */ ++ cx_write(MC417_OEN, 0x00005000); ++ /* ~RD, ~WR high; ADDR low; ~CS high */ ++ cx_write(MC417_RWD, 0x00000d00); ++ /* enable irq */ ++ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR4400: ++ /* GPIO-8 tda10071 demod reset */ ++ ++ /* Put the parts into reset and back */ ++ cx23885_gpio_enable(dev, GPIO_8, 1); ++ cx23885_gpio_clear(dev, GPIO_8); ++ mdelay(100); ++ cx23885_gpio_set(dev, GPIO_8); ++ mdelay(100); ++ break; ++ case CX23885_BOARD_AVERMEDIA_HC81R: ++ cx_clear(MC417_CTL, 1); ++ /* GPIO-0,1,2 setup direction as output */ ++ cx_set(GP0_IO, 0x00070000); ++ mdelay(10); ++ /* AF9013 demod reset */ ++ cx_set(GP0_IO, 0x00010001); ++ mdelay(10); ++ cx_clear(GP0_IO, 0x00010001); ++ mdelay(10); ++ cx_set(GP0_IO, 0x00010001); ++ mdelay(10); ++ /* demod tune? */ ++ cx_clear(GP0_IO, 0x00030003); ++ mdelay(10); ++ cx_set(GP0_IO, 0x00020002); ++ mdelay(10); ++ cx_set(GP0_IO, 0x00010001); ++ mdelay(10); ++ cx_clear(GP0_IO, 0x00020002); ++ /* XC3028L tuner reset */ ++ cx_set(GP0_IO, 0x00040004); ++ cx_clear(GP0_IO, 0x00040004); ++ cx_set(GP0_IO, 0x00040004); ++ mdelay(60); ++ break; ++ } ++} ++ ++int cx23885_ir_init(struct cx23885_dev *dev) ++{ ++ static struct v4l2_subdev_io_pin_config ir_rxtx_pin_cfg[] = { ++ { ++ .flags = V4L2_SUBDEV_IO_PIN_INPUT, ++ .pin = CX23885_PIN_IR_RX_GPIO19, ++ .function = CX23885_PAD_IR_RX, ++ .value = 0, ++ .strength = CX25840_PIN_DRIVE_MEDIUM, ++ }, { ++ .flags = V4L2_SUBDEV_IO_PIN_OUTPUT, ++ .pin = CX23885_PIN_IR_TX_GPIO20, ++ .function = CX23885_PAD_IR_TX, ++ .value = 0, ++ .strength = CX25840_PIN_DRIVE_MEDIUM, ++ } ++ }; ++ const size_t ir_rxtx_pin_cfg_count = ARRAY_SIZE(ir_rxtx_pin_cfg); ++ ++ static struct v4l2_subdev_io_pin_config ir_rx_pin_cfg[] = { ++ { ++ .flags = V4L2_SUBDEV_IO_PIN_INPUT, ++ .pin = CX23885_PIN_IR_RX_GPIO19, ++ .function = CX23885_PAD_IR_RX, ++ .value = 0, ++ .strength = CX25840_PIN_DRIVE_MEDIUM, ++ } ++ }; ++ const size_t ir_rx_pin_cfg_count = ARRAY_SIZE(ir_rx_pin_cfg); ++ ++ struct v4l2_subdev_ir_parameters params; ++ int ret = 0; ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ case CX23885_BOARD_HAUPPAUGE_HVR1200: ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1210: ++ /* FIXME: Implement me */ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ ret = cx23888_ir_probe(dev); ++ if (ret) ++ break; ++ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); ++ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, ++ ir_rx_pin_cfg_count, ir_rx_pin_cfg); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ ret = cx23888_ir_probe(dev); ++ if (ret) ++ break; ++ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); ++ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, ++ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg); ++ /* ++ * For these boards we need to invert the Tx output via the ++ * IR controller to have the LED off while idle ++ */ ++ v4l2_subdev_call(dev->sd_ir, ir, tx_g_parameters, ¶ms); ++ params.enable = false; ++ params.shutdown = false; ++ params.invert_level = true; ++ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms); ++ params.shutdown = true; ++ v4l2_subdev_call(dev->sd_ir, ir, tx_s_parameters, ¶ms); ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_MYGICA_X8507: ++ if (!enable_885_ir) ++ break; ++ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); ++ if (dev->sd_ir == NULL) { ++ ret = -ENODEV; ++ break; ++ } ++ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, ++ ir_rx_pin_cfg_count, ir_rx_pin_cfg); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ if (!enable_885_ir) ++ break; ++ dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_AV_CORE); ++ if (dev->sd_ir == NULL) { ++ ret = -ENODEV; ++ break; ++ } ++ v4l2_subdev_call(dev->sd_cx25840, core, s_io_pin_config, ++ ir_rxtx_pin_cfg_count, ir_rxtx_pin_cfg); ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ++ request_module("ir-kbd-i2c"); ++ break; ++ } ++ ++ return ret; ++} ++ ++void cx23885_ir_fini(struct cx23885_dev *dev) ++{ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ cx23885_irq_remove(dev, PCI_MSK_IR); ++ cx23888_ir_remove(dev); ++ dev->sd_ir = NULL; ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_MYGICA_X8507: ++ cx23885_irq_remove(dev, PCI_MSK_AV_CORE); ++ /* sd_ir is a duplicate pointer to the AV Core, just clear it */ ++ dev->sd_ir = NULL; ++ break; ++ } ++} ++ ++static int netup_jtag_io(void *device, int tms, int tdi, int read_tdo) ++{ ++ int data; ++ int tdo = 0; ++ struct cx23885_dev *dev = (struct cx23885_dev *)device; ++ /*TMS*/ ++ data = ((cx_read(GP0_IO)) & (~0x00000002)); ++ data |= (tms ? 0x00020002 : 0x00020000); ++ cx_write(GP0_IO, data); ++ ++ /*TDI*/ ++ data = ((cx_read(MC417_RWD)) & (~0x0000a000)); ++ data |= (tdi ? 0x00008000 : 0); ++ cx_write(MC417_RWD, data); ++ if (read_tdo) ++ tdo = (data & 0x00004000) ? 1 : 0; /*TDO*/ ++ ++ cx_write(MC417_RWD, data | 0x00002000); ++ udelay(1); ++ /*TCK*/ ++ cx_write(MC417_RWD, data); ++ ++ return tdo; ++} ++ ++void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) ++{ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ if (dev->sd_ir) ++ cx23885_irq_add_enable(dev, PCI_MSK_IR); ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_MYGICA_X8507: ++ if (dev->sd_ir) ++ cx23885_irq_add_enable(dev, PCI_MSK_AV_CORE); ++ break; ++ } ++} ++ ++void cx23885_card_setup(struct cx23885_dev *dev) ++{ ++ struct cx23885_tsport *ts1 = &dev->ts1; ++ struct cx23885_tsport *ts2 = &dev->ts2; ++ ++ static u8 eeprom[256]; ++ ++ if (dev->i2c_bus[0].i2c_rc == 0) { ++ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&dev->i2c_bus[0].i2c_client, ++ eeprom, sizeof(eeprom)); ++ } ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ if (dev->i2c_bus[0].i2c_rc == 0) { ++ if (eeprom[0x80] != 0x84) ++ hauppauge_eeprom(dev, eeprom+0xc0); ++ else ++ hauppauge_eeprom(dev, eeprom+0x80); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ if (dev->i2c_bus[0].i2c_rc == 0) ++ hauppauge_eeprom(dev, eeprom+0x80); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800lp: ++ case CX23885_BOARD_HAUPPAUGE_HVR1200: ++ case CX23885_BOARD_HAUPPAUGE_HVR1700: ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1210: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_HAUPPAUGE_HVR4400: ++ if (dev->i2c_bus[0].i2c_rc == 0) ++ hauppauge_eeprom(dev, eeprom+0xc0); ++ break; ++ } ++ ++ switch (dev->board) { ++ case CX23885_BOARD_AVERMEDIA_HC81R: ++ /* Defaults for VID B */ ++ ts1->gen_ctrl_val = 0x4; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ /* Defaults for VID C */ ++ /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ ++ ts2->gen_ctrl_val = 0x10e; ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: ++ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: ++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ /* break omitted intentionally */ ++ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP: ++ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ /* Defaults for VID B - Analog encoder */ ++ /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ ++ ts1->gen_ctrl_val = 0x10e; ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ ++ /* APB_TSVALERR_POL (active low)*/ ++ ts1->vld_misc_val = 0x2000; ++ ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc); ++ cx_write(0x130184, 0xc); ++ ++ /* Defaults for VID C */ ++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_TBS_6920: ++ ts1->gen_ctrl_val = 0x4; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_TEVII_S471: ++ case CX23885_BOARD_DVBWORLD_2005: ++ case CX23885_BOARD_PROF_8000: ++ ts1->gen_ctrl_val = 0x5; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_MYGICA_X8506: ++ case CX23885_BOARD_MAGICPRO_PROHDTVE2: ++ ts1->gen_ctrl_val = 0x5; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_MYGICA_X8558PRO: ++ ts1->gen_ctrl_val = 0x5; /* Parallel */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR4400: ++ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800lp: ++ case CX23885_BOARD_HAUPPAUGE_HVR1200: ++ case CX23885_BOARD_HAUPPAUGE_HVR1700: ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1210: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E800: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: ++ default: ++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ++ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; ++ } ++ ++ /* Certain boards support analog, or require the avcore to be ++ * loaded, ensure this happens. ++ */ ++ switch (dev->board) { ++ case CX23885_BOARD_TEVII_S470: ++ /* Currently only enabled for the integrated IR controller */ ++ if (!enable_885_ir) ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ case CX23885_BOARD_HAUPPAUGE_HVR1800lp: ++ case CX23885_BOARD_HAUPPAUGE_HVR1700: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E800: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_MYGICA_X8506: ++ case CX23885_BOARD_MAGICPRO_PROHDTVE2: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: ++ case CX23885_BOARD_GOTVIEW_X5_3D_HYBRID: ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ case CX23885_BOARD_MPX885: ++ case CX23885_BOARD_MYGICA_X8507: ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_AVERMEDIA_HC81R: ++ dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_bus[2].i2c_adap, ++ "cx25840", 0x88 >> 1, NULL); ++ if (dev->sd_cx25840) { ++ dev->sd_cx25840->grp_id = CX23885_HW_AV_CORE; ++ v4l2_subdev_call(dev->sd_cx25840, core, load_fw); ++ } ++ break; ++ } ++ ++ /* AUX-PLL 27MHz CLK */ ++ switch (dev->board) { ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ netup_initialize(dev); ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { ++ int ret; ++ const struct firmware *fw; ++ const char *filename = "dvb-netup-altera-01.fw"; ++ char *action = "configure"; ++ static struct netup_card_info cinfo; ++ struct altera_config netup_config = { ++ .dev = dev, ++ .action = action, ++ .jtag_io = netup_jtag_io, ++ }; ++ ++ netup_initialize(dev); ++ ++ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); ++ if (netup_card_rev) ++ cinfo.rev = netup_card_rev; ++ ++ switch (cinfo.rev) { ++ case 0x4: ++ filename = "dvb-netup-altera-04.fw"; ++ break; ++ default: ++ filename = "dvb-netup-altera-01.fw"; ++ break; ++ } ++ printk(KERN_INFO "NetUP card rev=0x%x fw_filename=%s\n", ++ cinfo.rev, filename); ++ ++ ret = request_firmware(&fw, filename, &dev->pci->dev); ++ if (ret != 0) ++ printk(KERN_ERR "did not find the firmware file. (%s) " ++ "Please see linux/Documentation/dvb/ for more details " ++ "on firmware-problems.", filename); ++ else ++ altera_init(&netup_config, fw); ++ ++ release_firmware(fw); ++ break; ++ } ++ } ++} ++ ++/* ------------------------------------------------------------------ */ +diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c +new file mode 100644 +index 0000000..c757259 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-core.c +@@ -0,0 +1,2232 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include "cimax2.h" ++#include "altera-ci.h" ++#include "cx23888-ir.h" ++#include "cx23885-ir.h" ++#include "cx23885-av.h" ++#include "cx23885-input.h" ++ ++MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); ++MODULE_AUTHOR("Steven Toth "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX23885_VERSION); ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++ ++static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; ++module_param_array(card, int, NULL, 0444); ++MODULE_PARM_DESC(card, "card type"); ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (debug >= level)\ ++ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++static unsigned int cx23885_devcount; ++ ++#define NO_SYNC_LINE (-1U) ++ ++/* FIXME, these allocations will change when ++ * analog arrives. The be reviewed. ++ * CX23887 Assumptions ++ * 1 line = 16 bytes of CDT ++ * cmds size = 80 ++ * cdt size = 16 * linesize ++ * iqsize = 64 ++ * maxlines = 6 ++ * ++ * Address Space: ++ * 0x00000000 0x00008fff FIFO clusters ++ * 0x00010000 0x000104af Channel Management Data Structures ++ * 0x000104b0 0x000104ff Free ++ * 0x00010500 0x000108bf 15 channels * iqsize ++ * 0x000108c0 0x000108ff Free ++ * 0x00010900 0x00010e9f IQ's + Cluster Descriptor Tables ++ * 15 channels * (iqsize + (maxlines * linesize)) ++ * 0x00010ea0 0x00010xxx Free ++ */ ++ ++static struct sram_channel cx23885_sram_channels[] = { ++ [SRAM_CH01] = { ++ .name = "VID A", ++ .cmds_start = 0x10000, ++ .ctrl_start = 0x10380, ++ .cdt = 0x104c0, ++ .fifo_start = 0x40, ++ .fifo_size = 0x2800, ++ .ptr1_reg = DMA1_PTR1, ++ .ptr2_reg = DMA1_PTR2, ++ .cnt1_reg = DMA1_CNT1, ++ .cnt2_reg = DMA1_CNT2, ++ }, ++ [SRAM_CH02] = { ++ .name = "ch2", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA2_PTR1, ++ .ptr2_reg = DMA2_PTR2, ++ .cnt1_reg = DMA2_CNT1, ++ .cnt2_reg = DMA2_CNT2, ++ }, ++ [SRAM_CH03] = { ++ .name = "TS1 B", ++ .cmds_start = 0x100A0, ++ .ctrl_start = 0x10400, ++ .cdt = 0x10580, ++ .fifo_start = 0x5000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA3_PTR1, ++ .ptr2_reg = DMA3_PTR2, ++ .cnt1_reg = DMA3_CNT1, ++ .cnt2_reg = DMA3_CNT2, ++ }, ++ [SRAM_CH04] = { ++ .name = "ch4", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA4_PTR1, ++ .ptr2_reg = DMA4_PTR2, ++ .cnt1_reg = DMA4_CNT1, ++ .cnt2_reg = DMA4_CNT2, ++ }, ++ [SRAM_CH05] = { ++ .name = "ch5", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA5_PTR1, ++ .ptr2_reg = DMA5_PTR2, ++ .cnt1_reg = DMA5_CNT1, ++ .cnt2_reg = DMA5_CNT2, ++ }, ++ [SRAM_CH06] = { ++ .name = "TS2 C", ++ .cmds_start = 0x10140, ++ .ctrl_start = 0x10440, ++ .cdt = 0x105e0, ++ .fifo_start = 0x6000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA5_PTR1, ++ .ptr2_reg = DMA5_PTR2, ++ .cnt1_reg = DMA5_CNT1, ++ .cnt2_reg = DMA5_CNT2, ++ }, ++ [SRAM_CH07] = { ++ .name = "TV Audio", ++ .cmds_start = 0x10190, ++ .ctrl_start = 0x10480, ++ .cdt = 0x10a00, ++ .fifo_start = 0x7000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA6_PTR1, ++ .ptr2_reg = DMA6_PTR2, ++ .cnt1_reg = DMA6_CNT1, ++ .cnt2_reg = DMA6_CNT2, ++ }, ++ [SRAM_CH08] = { ++ .name = "ch8", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA7_PTR1, ++ .ptr2_reg = DMA7_PTR2, ++ .cnt1_reg = DMA7_CNT1, ++ .cnt2_reg = DMA7_CNT2, ++ }, ++ [SRAM_CH09] = { ++ .name = "ch9", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA8_PTR1, ++ .ptr2_reg = DMA8_PTR2, ++ .cnt1_reg = DMA8_CNT1, ++ .cnt2_reg = DMA8_CNT2, ++ }, ++}; ++ ++static struct sram_channel cx23887_sram_channels[] = { ++ [SRAM_CH01] = { ++ .name = "VID A", ++ .cmds_start = 0x10000, ++ .ctrl_start = 0x105b0, ++ .cdt = 0x107b0, ++ .fifo_start = 0x40, ++ .fifo_size = 0x2800, ++ .ptr1_reg = DMA1_PTR1, ++ .ptr2_reg = DMA1_PTR2, ++ .cnt1_reg = DMA1_CNT1, ++ .cnt2_reg = DMA1_CNT2, ++ }, ++ [SRAM_CH02] = { ++ .name = "VID A (VBI)", ++ .cmds_start = 0x10050, ++ .ctrl_start = 0x105F0, ++ .cdt = 0x10810, ++ .fifo_start = 0x3000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA2_PTR1, ++ .ptr2_reg = DMA2_PTR2, ++ .cnt1_reg = DMA2_CNT1, ++ .cnt2_reg = DMA2_CNT2, ++ }, ++ [SRAM_CH03] = { ++ .name = "TS1 B", ++ .cmds_start = 0x100A0, ++ .ctrl_start = 0x10630, ++ .cdt = 0x10870, ++ .fifo_start = 0x5000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA3_PTR1, ++ .ptr2_reg = DMA3_PTR2, ++ .cnt1_reg = DMA3_CNT1, ++ .cnt2_reg = DMA3_CNT2, ++ }, ++ [SRAM_CH04] = { ++ .name = "ch4", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA4_PTR1, ++ .ptr2_reg = DMA4_PTR2, ++ .cnt1_reg = DMA4_CNT1, ++ .cnt2_reg = DMA4_CNT2, ++ }, ++ [SRAM_CH05] = { ++ .name = "ch5", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA5_PTR1, ++ .ptr2_reg = DMA5_PTR2, ++ .cnt1_reg = DMA5_CNT1, ++ .cnt2_reg = DMA5_CNT2, ++ }, ++ [SRAM_CH06] = { ++ .name = "TS2 C", ++ .cmds_start = 0x10140, ++ .ctrl_start = 0x10670, ++ .cdt = 0x108d0, ++ .fifo_start = 0x6000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA5_PTR1, ++ .ptr2_reg = DMA5_PTR2, ++ .cnt1_reg = DMA5_CNT1, ++ .cnt2_reg = DMA5_CNT2, ++ }, ++ [SRAM_CH07] = { ++ .name = "TV Audio", ++ .cmds_start = 0x10190, ++ .ctrl_start = 0x106B0, ++ .cdt = 0x10930, ++ .fifo_start = 0x7000, ++ .fifo_size = 0x1000, ++ .ptr1_reg = DMA6_PTR1, ++ .ptr2_reg = DMA6_PTR2, ++ .cnt1_reg = DMA6_CNT1, ++ .cnt2_reg = DMA6_CNT2, ++ }, ++ [SRAM_CH08] = { ++ .name = "ch8", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA7_PTR1, ++ .ptr2_reg = DMA7_PTR2, ++ .cnt1_reg = DMA7_CNT1, ++ .cnt2_reg = DMA7_CNT2, ++ }, ++ [SRAM_CH09] = { ++ .name = "ch9", ++ .cmds_start = 0x0, ++ .ctrl_start = 0x0, ++ .cdt = 0x0, ++ .fifo_start = 0x0, ++ .fifo_size = 0x0, ++ .ptr1_reg = DMA8_PTR1, ++ .ptr2_reg = DMA8_PTR2, ++ .cnt1_reg = DMA8_CNT1, ++ .cnt2_reg = DMA8_CNT2, ++ }, ++}; ++ ++static void cx23885_irq_add(struct cx23885_dev *dev, u32 mask) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ dev->pci_irqmask |= mask; ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++} ++ ++void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ dev->pci_irqmask |= mask; ++ cx_set(PCI_INT_MSK, mask); ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++} ++ ++void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask) ++{ ++ u32 v; ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ v = mask & dev->pci_irqmask; ++ if (v) ++ cx_set(PCI_INT_MSK, v); ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++} ++ ++static inline void cx23885_irq_enable_all(struct cx23885_dev *dev) ++{ ++ cx23885_irq_enable(dev, 0xffffffff); ++} ++ ++void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ cx_clear(PCI_INT_MSK, mask); ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++} ++ ++static inline void cx23885_irq_disable_all(struct cx23885_dev *dev) ++{ ++ cx23885_irq_disable(dev, 0xffffffff); ++} ++ ++void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask) ++{ ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ dev->pci_irqmask &= ~mask; ++ cx_clear(PCI_INT_MSK, mask); ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++} ++ ++static u32 cx23885_irq_get_mask(struct cx23885_dev *dev) ++{ ++ u32 v; ++ unsigned long flags; ++ spin_lock_irqsave(&dev->pci_irqmask_lock, flags); ++ ++ v = cx_read(PCI_INT_MSK); ++ ++ spin_unlock_irqrestore(&dev->pci_irqmask_lock, flags); ++ return v; ++} ++ ++static int cx23885_risc_decode(u32 risc) ++{ ++ static char *instr[16] = { ++ [RISC_SYNC >> 28] = "sync", ++ [RISC_WRITE >> 28] = "write", ++ [RISC_WRITEC >> 28] = "writec", ++ [RISC_READ >> 28] = "read", ++ [RISC_READC >> 28] = "readc", ++ [RISC_JUMP >> 28] = "jump", ++ [RISC_SKIP >> 28] = "skip", ++ [RISC_WRITERM >> 28] = "writerm", ++ [RISC_WRITECM >> 28] = "writecm", ++ [RISC_WRITECR >> 28] = "writecr", ++ }; ++ static int incr[16] = { ++ [RISC_WRITE >> 28] = 3, ++ [RISC_JUMP >> 28] = 3, ++ [RISC_SKIP >> 28] = 1, ++ [RISC_SYNC >> 28] = 1, ++ [RISC_WRITERM >> 28] = 3, ++ [RISC_WRITECM >> 28] = 3, ++ [RISC_WRITECR >> 28] = 4, ++ }; ++ static char *bits[] = { ++ "12", "13", "14", "resync", ++ "cnt0", "cnt1", "18", "19", ++ "20", "21", "22", "23", ++ "irq1", "irq2", "eol", "sol", ++ }; ++ int i; ++ ++ printk("0x%08x [ %s", risc, ++ instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); ++ for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) ++ if (risc & (1 << (i + 12))) ++ printk(" %s", bits[i]); ++ printk(" count=%d ]\n", risc & 0xfff); ++ return incr[risc >> 28] ? incr[risc >> 28] : 1; ++} ++ ++void cx23885_wakeup(struct cx23885_tsport *port, ++ struct cx23885_dmaqueue *q, u32 count) ++{ ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_buffer *buf; ++ int bc; ++ ++ for (bc = 0;; bc++) { ++ if (list_empty(&q->active)) ++ break; ++ buf = list_entry(q->active.next, ++ struct cx23885_buffer, vb.queue); ++ ++ /* count comes from the hw and is is 16bit wide -- ++ * this trick handles wrap-arounds correctly for ++ * up to 32767 buffers in flight... */ ++ if ((s16) (count - buf->count) < 0) ++ break; ++ ++ v4l2_get_timestamp(&buf->vb.ts); ++ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, ++ count, buf->count); ++ buf->vb.state = VIDEOBUF_DONE; ++ list_del(&buf->vb.queue); ++ wake_up(&buf->vb.done); ++ } ++ if (list_empty(&q->active)) ++ del_timer(&q->timeout); ++ else ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ if (bc != 1) ++ printk(KERN_WARNING "%s: %d buffers handled (should be 1)\n", ++ __func__, bc); ++} ++ ++int cx23885_sram_channel_setup(struct cx23885_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i, lines; ++ u32 cdt; ++ ++ if (ch->cmds_start == 0) { ++ dprintk(1, "%s() Erasing channel [%s]\n", __func__, ++ ch->name); ++ cx_write(ch->ptr1_reg, 0); ++ cx_write(ch->ptr2_reg, 0); ++ cx_write(ch->cnt2_reg, 0); ++ cx_write(ch->cnt1_reg, 0); ++ return 0; ++ } else { ++ dprintk(1, "%s() Configuring channel [%s]\n", __func__, ++ ch->name); ++ } ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ if (lines > 6) ++ lines = 6; ++ BUG_ON(lines < 2); ++ ++ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ cx_write(8 + 4, 8); ++ cx_write(8 + 8, 0); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) { ++ dprintk(2, "%s() 0x%08x <- 0x%08x\n", __func__, cdt + 16*i, ++ ch->fifo_start + bpl*i); ++ cx_write(cdt + 16*i, ch->fifo_start + bpl*i); ++ cx_write(cdt + 16*i + 4, 0); ++ cx_write(cdt + 16*i + 8, 0); ++ cx_write(cdt + 16*i + 12, 0); ++ } ++ ++ /* write CMDS */ ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 0, 8); ++ else ++ cx_write(ch->cmds_start + 0, risc); ++ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ ++ cx_write(ch->cmds_start + 8, cdt); ++ cx_write(ch->cmds_start + 12, (lines*16) >> 3); ++ cx_write(ch->cmds_start + 16, ch->ctrl_start); ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); ++ else ++ cx_write(ch->cmds_start + 20, 64 >> 2); ++ for (i = 24; i < 80; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt2_reg, (lines*16) >> 3); ++ cx_write(ch->cnt1_reg, (bpl >> 3) - 1); ++ ++ dprintk(2, "[bridge %d] sram setup %s: bpl=%d lines=%d\n", ++ dev->bridge, ++ ch->name, ++ bpl, ++ lines); ++ ++ return 0; ++} ++ ++void cx23885_sram_channel_dump(struct cx23885_dev *dev, ++ struct sram_channel *ch) ++{ ++ static char *name[] = { ++ "init risc lo", ++ "init risc hi", ++ "cdt base", ++ "cdt size", ++ "iq base", ++ "iq size", ++ "risc pc lo", ++ "risc pc hi", ++ "iq wr ptr", ++ "iq rd ptr", ++ "cdt current", ++ "pci target lo", ++ "pci target hi", ++ "line / byte", ++ }; ++ u32 risc; ++ unsigned int i, j, n; ++ ++ printk(KERN_WARNING "%s: %s - dma channel status dump\n", ++ dev->name, ch->name); ++ for (i = 0; i < ARRAY_SIZE(name); i++) ++ printk(KERN_WARNING "%s: cmds: %-15s: 0x%08x\n", ++ dev->name, name[i], ++ cx_read(ch->cmds_start + 4*i)); ++ ++ for (i = 0; i < 4; i++) { ++ risc = cx_read(ch->cmds_start + 4 * (i + 14)); ++ printk(KERN_WARNING "%s: risc%d: ", dev->name, i); ++ cx23885_risc_decode(risc); ++ } ++ for (i = 0; i < (64 >> 2); i += n) { ++ risc = cx_read(ch->ctrl_start + 4 * i); ++ /* No consideration for bits 63-32 */ ++ ++ printk(KERN_WARNING "%s: (0x%08x) iq %x: ", dev->name, ++ ch->ctrl_start + 4 * i, i); ++ n = cx23885_risc_decode(risc); ++ for (j = 1; j < n; j++) { ++ risc = cx_read(ch->ctrl_start + 4 * (i + j)); ++ printk(KERN_WARNING "%s: iq %x: 0x%08x [ arg #%d ]\n", ++ dev->name, i+j, risc, j); ++ } ++ } ++ ++ printk(KERN_WARNING "%s: fifo: 0x%08x -> 0x%x\n", ++ dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size); ++ printk(KERN_WARNING "%s: ctrl: 0x%08x -> 0x%x\n", ++ dev->name, ch->ctrl_start, ch->ctrl_start + 6*16); ++ printk(KERN_WARNING "%s: ptr1_reg: 0x%08x\n", ++ dev->name, cx_read(ch->ptr1_reg)); ++ printk(KERN_WARNING "%s: ptr2_reg: 0x%08x\n", ++ dev->name, cx_read(ch->ptr2_reg)); ++ printk(KERN_WARNING "%s: cnt1_reg: 0x%08x\n", ++ dev->name, cx_read(ch->cnt1_reg)); ++ printk(KERN_WARNING "%s: cnt2_reg: 0x%08x\n", ++ dev->name, cx_read(ch->cnt2_reg)); ++} ++ ++static void cx23885_risc_disasm(struct cx23885_tsport *port, ++ struct btcx_riscmem *risc) ++{ ++ struct cx23885_dev *dev = port->dev; ++ unsigned int i, j, n; ++ ++ printk(KERN_INFO "%s: risc disasm: %p [dma=0x%08lx]\n", ++ dev->name, risc->cpu, (unsigned long)risc->dma); ++ for (i = 0; i < (risc->size >> 2); i += n) { ++ printk(KERN_INFO "%s: %04d: ", dev->name, i); ++ n = cx23885_risc_decode(le32_to_cpu(risc->cpu[i])); ++ for (j = 1; j < n; j++) ++ printk(KERN_INFO "%s: %04d: 0x%08x [ arg #%d ]\n", ++ dev->name, i + j, risc->cpu[i + j], j); ++ if (risc->cpu[i] == cpu_to_le32(RISC_JUMP)) ++ break; ++ } ++} ++ ++static void cx23885_shutdown(struct cx23885_dev *dev) ++{ ++ /* disable RISC controller */ ++ cx_write(DEV_CNTRL2, 0); ++ ++ /* Disable all IR activity */ ++ cx_write(IR_CNTRL_REG, 0); ++ ++ /* Disable Video A/B activity */ ++ cx_write(VID_A_DMA_CTL, 0); ++ cx_write(VID_B_DMA_CTL, 0); ++ cx_write(VID_C_DMA_CTL, 0); ++ ++ /* Disable Audio activity */ ++ cx_write(AUD_INT_DMA_CTL, 0); ++ cx_write(AUD_EXT_DMA_CTL, 0); ++ ++ /* Disable Serial port */ ++ cx_write(UART_CTL, 0); ++ ++ /* Disable Interrupts */ ++ cx23885_irq_disable_all(dev); ++ cx_write(VID_A_INT_MSK, 0); ++ cx_write(VID_B_INT_MSK, 0); ++ cx_write(VID_C_INT_MSK, 0); ++ cx_write(AUDIO_INT_INT_MSK, 0); ++ cx_write(AUDIO_EXT_INT_MSK, 0); ++ ++} ++ ++static void cx23885_reset(struct cx23885_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ cx23885_shutdown(dev); ++ ++ cx_write(PCI_INT_STAT, 0xffffffff); ++ cx_write(VID_A_INT_STAT, 0xffffffff); ++ cx_write(VID_B_INT_STAT, 0xffffffff); ++ cx_write(VID_C_INT_STAT, 0xffffffff); ++ cx_write(AUDIO_INT_INT_STAT, 0xffffffff); ++ cx_write(AUDIO_EXT_INT_STAT, 0xffffffff); ++ cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); ++ cx_write(PAD_CTRL, 0x00500300); ++ ++ mdelay(100); ++ ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], ++ 720*4, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], 128, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH03], ++ 188*4, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH04], 128, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH05], 128, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH06], ++ 188*4, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH07], 128, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH08], 128, 0); ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0); ++ ++ cx23885_gpio_setup(dev); ++} ++ ++ ++static int cx23885_pci_quirks(struct cx23885_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* The cx23885 bridge has a weird bug which causes NMI to be asserted ++ * when DMA begins if RDR_TLCTL0 bit4 is not cleared. It does not ++ * occur on the cx23887 bridge. ++ */ ++ if (dev->bridge == CX23885_BRIDGE_885) ++ cx_clear(RDR_TLCTL0, 1 << 4); ++ ++ return 0; ++} ++ ++static int get_resources(struct cx23885_dev *dev) ++{ ++ if (request_mem_region(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0), ++ dev->name)) ++ return 0; ++ ++ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n", ++ dev->name, (unsigned long long)pci_resource_start(dev->pci, 0)); ++ ++ return -EBUSY; ++} ++ ++static void cx23885_timeout(unsigned long data); ++int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value); ++ ++static int cx23885_init_tsport(struct cx23885_dev *dev, ++ struct cx23885_tsport *port, int portno) ++{ ++ dprintk(1, "%s(portno=%d)\n", __func__, portno); ++ ++ /* Transport bus init dma queue - Common settings */ ++ port->dma_ctl_val = 0x11; /* Enable RISC controller and Fifo */ ++ port->ts_int_msk_val = 0x1111; /* TS port bits for RISC */ ++ port->vld_misc_val = 0x0; ++ port->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4); ++ ++ spin_lock_init(&port->slock); ++ port->dev = dev; ++ port->nr = portno; ++ ++ INIT_LIST_HEAD(&port->mpegq.active); ++ INIT_LIST_HEAD(&port->mpegq.queued); ++ port->mpegq.timeout.function = cx23885_timeout; ++ port->mpegq.timeout.data = (unsigned long)port; ++ init_timer(&port->mpegq.timeout); ++ ++ mutex_init(&port->frontends.lock); ++ INIT_LIST_HEAD(&port->frontends.felist); ++ port->frontends.active_fe_id = 0; ++ ++ /* This should be hardcoded allow a single frontend ++ * attachment to this tsport, keeping the -dvb.c ++ * code clean and safe. ++ */ ++ if (!port->num_frontends) ++ port->num_frontends = 1; ++ ++ switch (portno) { ++ case 1: ++ port->reg_gpcnt = VID_B_GPCNT; ++ port->reg_gpcnt_ctl = VID_B_GPCNT_CTL; ++ port->reg_dma_ctl = VID_B_DMA_CTL; ++ port->reg_lngth = VID_B_LNGTH; ++ port->reg_hw_sop_ctrl = VID_B_HW_SOP_CTL; ++ port->reg_gen_ctrl = VID_B_GEN_CTL; ++ port->reg_bd_pkt_status = VID_B_BD_PKT_STATUS; ++ port->reg_sop_status = VID_B_SOP_STATUS; ++ port->reg_fifo_ovfl_stat = VID_B_FIFO_OVFL_STAT; ++ port->reg_vld_misc = VID_B_VLD_MISC; ++ port->reg_ts_clk_en = VID_B_TS_CLK_EN; ++ port->reg_src_sel = VID_B_SRC_SEL; ++ port->reg_ts_int_msk = VID_B_INT_MSK; ++ port->reg_ts_int_stat = VID_B_INT_STAT; ++ port->sram_chno = SRAM_CH03; /* VID_B */ ++ port->pci_irqmask = 0x02; /* VID_B bit1 */ ++ break; ++ case 2: ++ port->reg_gpcnt = VID_C_GPCNT; ++ port->reg_gpcnt_ctl = VID_C_GPCNT_CTL; ++ port->reg_dma_ctl = VID_C_DMA_CTL; ++ port->reg_lngth = VID_C_LNGTH; ++ port->reg_hw_sop_ctrl = VID_C_HW_SOP_CTL; ++ port->reg_gen_ctrl = VID_C_GEN_CTL; ++ port->reg_bd_pkt_status = VID_C_BD_PKT_STATUS; ++ port->reg_sop_status = VID_C_SOP_STATUS; ++ port->reg_fifo_ovfl_stat = VID_C_FIFO_OVFL_STAT; ++ port->reg_vld_misc = VID_C_VLD_MISC; ++ port->reg_ts_clk_en = VID_C_TS_CLK_EN; ++ port->reg_src_sel = 0; ++ port->reg_ts_int_msk = VID_C_INT_MSK; ++ port->reg_ts_int_stat = VID_C_INT_STAT; ++ port->sram_chno = SRAM_CH06; /* VID_C */ ++ port->pci_irqmask = 0x04; /* VID_C bit2 */ ++ break; ++ default: ++ BUG(); ++ } ++ ++ cx23885_risc_stopper(dev->pci, &port->mpegq.stopper, ++ port->reg_dma_ctl, port->dma_ctl_val, 0x00); ++ ++ return 0; ++} ++ ++static void cx23885_dev_checkrevision(struct cx23885_dev *dev) ++{ ++ switch (cx_read(RDR_CFG2) & 0xff) { ++ case 0x00: ++ /* cx23885 */ ++ dev->hwrevision = 0xa0; ++ break; ++ case 0x01: ++ /* CX23885-12Z */ ++ dev->hwrevision = 0xa1; ++ break; ++ case 0x02: ++ /* CX23885-13Z/14Z */ ++ dev->hwrevision = 0xb0; ++ break; ++ case 0x03: ++ if (dev->pci->device == 0x8880) { ++ /* CX23888-21Z/22Z */ ++ dev->hwrevision = 0xc0; ++ } else { ++ /* CX23885-14Z */ ++ dev->hwrevision = 0xa4; ++ } ++ break; ++ case 0x04: ++ if (dev->pci->device == 0x8880) { ++ /* CX23888-31Z */ ++ dev->hwrevision = 0xd0; ++ } else { ++ /* CX23885-15Z, CX23888-31Z */ ++ dev->hwrevision = 0xa5; ++ } ++ break; ++ case 0x0e: ++ /* CX23887-15Z */ ++ dev->hwrevision = 0xc0; ++ break; ++ case 0x0f: ++ /* CX23887-14Z */ ++ dev->hwrevision = 0xb1; ++ break; ++ default: ++ printk(KERN_ERR "%s() New hardware revision found 0x%x\n", ++ __func__, dev->hwrevision); ++ } ++ if (dev->hwrevision) ++ printk(KERN_INFO "%s() Hardware revision = 0x%02x\n", ++ __func__, dev->hwrevision); ++ else ++ printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n", ++ __func__, dev->hwrevision); ++} ++ ++/* Find the first v4l2_subdev member of the group id in hw */ ++struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw) ++{ ++ struct v4l2_subdev *result = NULL; ++ struct v4l2_subdev *sd; ++ ++ spin_lock(&dev->v4l2_dev.lock); ++ v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) { ++ if (sd->grp_id == hw) { ++ result = sd; ++ break; ++ } ++ } ++ spin_unlock(&dev->v4l2_dev.lock); ++ return result; ++} ++ ++static int cx23885_dev_setup(struct cx23885_dev *dev) ++{ ++ int i; ++ ++ spin_lock_init(&dev->pci_irqmask_lock); ++ ++ mutex_init(&dev->lock); ++ mutex_init(&dev->gpio_lock); ++ ++ atomic_inc(&dev->refcount); ++ ++ dev->nr = cx23885_devcount++; ++ sprintf(dev->name, "cx23885[%d]", dev->nr); ++ ++ /* Configure the internal memory */ ++ if (dev->pci->device == 0x8880) { ++ /* Could be 887 or 888, assume a default */ ++ dev->bridge = CX23885_BRIDGE_887; ++ /* Apply a sensible clock frequency for the PCIe bridge */ ++ dev->clk_freq = 25000000; ++ dev->sram_channels = cx23887_sram_channels; ++ } else ++ if (dev->pci->device == 0x8852) { ++ dev->bridge = CX23885_BRIDGE_885; ++ /* Apply a sensible clock frequency for the PCIe bridge */ ++ dev->clk_freq = 28000000; ++ dev->sram_channels = cx23885_sram_channels; ++ } else ++ BUG(); ++ ++ dprintk(1, "%s() Memory configured for PCIe bridge type %d\n", ++ __func__, dev->bridge); ++ ++ /* board config */ ++ dev->board = UNSET; ++ if (card[dev->nr] < cx23885_bcount) ++ dev->board = card[dev->nr]; ++ for (i = 0; UNSET == dev->board && i < cx23885_idcount; i++) ++ if (dev->pci->subsystem_vendor == cx23885_subids[i].subvendor && ++ dev->pci->subsystem_device == cx23885_subids[i].subdevice) ++ dev->board = cx23885_subids[i].card; ++ if (UNSET == dev->board) { ++ dev->board = CX23885_BOARD_UNKNOWN; ++ cx23885_card_list(dev); ++ } ++ ++ /* If the user specific a clk freq override, apply it */ ++ if (cx23885_boards[dev->board].clk_freq > 0) ++ dev->clk_freq = cx23885_boards[dev->board].clk_freq; ++ ++ dev->pci_bus = dev->pci->bus->number; ++ dev->pci_slot = PCI_SLOT(dev->pci->devfn); ++ cx23885_irq_add(dev, 0x001f00); ++ ++ /* External Master 1 Bus */ ++ dev->i2c_bus[0].nr = 0; ++ dev->i2c_bus[0].dev = dev; ++ dev->i2c_bus[0].reg_stat = I2C1_STAT; ++ dev->i2c_bus[0].reg_ctrl = I2C1_CTRL; ++ dev->i2c_bus[0].reg_addr = I2C1_ADDR; ++ dev->i2c_bus[0].reg_rdata = I2C1_RDATA; ++ dev->i2c_bus[0].reg_wdata = I2C1_WDATA; ++ dev->i2c_bus[0].i2c_period = (0x9d << 24); /* 100kHz */ ++ ++ /* External Master 2 Bus */ ++ dev->i2c_bus[1].nr = 1; ++ dev->i2c_bus[1].dev = dev; ++ dev->i2c_bus[1].reg_stat = I2C2_STAT; ++ dev->i2c_bus[1].reg_ctrl = I2C2_CTRL; ++ dev->i2c_bus[1].reg_addr = I2C2_ADDR; ++ dev->i2c_bus[1].reg_rdata = I2C2_RDATA; ++ dev->i2c_bus[1].reg_wdata = I2C2_WDATA; ++ dev->i2c_bus[1].i2c_period = (0x9d << 24); /* 100kHz */ ++ ++ /* Internal Master 3 Bus */ ++ dev->i2c_bus[2].nr = 2; ++ dev->i2c_bus[2].dev = dev; ++ dev->i2c_bus[2].reg_stat = I2C3_STAT; ++ dev->i2c_bus[2].reg_ctrl = I2C3_CTRL; ++ dev->i2c_bus[2].reg_addr = I2C3_ADDR; ++ dev->i2c_bus[2].reg_rdata = I2C3_RDATA; ++ dev->i2c_bus[2].reg_wdata = I2C3_WDATA; ++ dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */ ++ ++ if ((cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) || ++ (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)) ++ cx23885_init_tsport(dev, &dev->ts1, 1); ++ ++ if ((cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) || ++ (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) ++ cx23885_init_tsport(dev, &dev->ts2, 2); ++ ++ if (get_resources(dev) < 0) { ++ printk(KERN_ERR "CORE %s No more PCIe resources for " ++ "subsystem: %04x:%04x\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device); ++ ++ cx23885_devcount--; ++ return -ENODEV; ++ } ++ ++ /* PCIe stuff */ ++ dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0)); ++ ++ dev->bmmio = (u8 __iomem *)dev->lmmio; ++ ++ printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device, cx23885_boards[dev->board].name, ++ dev->board, card[dev->nr] == dev->board ? ++ "insmod option" : "autodetected"); ++ ++ cx23885_pci_quirks(dev); ++ ++ /* Assume some sensible defaults */ ++ dev->tuner_type = cx23885_boards[dev->board].tuner_type; ++ dev->tuner_addr = cx23885_boards[dev->board].tuner_addr; ++ dev->tuner_bus = cx23885_boards[dev->board].tuner_bus; ++ dev->radio_type = cx23885_boards[dev->board].radio_type; ++ dev->radio_addr = cx23885_boards[dev->board].radio_addr; ++ ++ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x tuner_bus = %d\n", ++ __func__, dev->tuner_type, dev->tuner_addr, dev->tuner_bus); ++ dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", ++ __func__, dev->radio_type, dev->radio_addr); ++ ++ /* The cx23417 encoder has GPIO's that need to be initialised ++ * before DVB, so that demodulators and tuners are out of ++ * reset before DVB uses them. ++ */ ++ if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) || ++ (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) ++ cx23885_mc417_init(dev); ++ ++ /* init hardware */ ++ cx23885_reset(dev); ++ ++ cx23885_i2c_register(&dev->i2c_bus[0]); ++ cx23885_i2c_register(&dev->i2c_bus[1]); ++ cx23885_i2c_register(&dev->i2c_bus[2]); ++ cx23885_card_setup(dev); ++ call_all(dev, core, s_power, 0); ++ cx23885_ir_init(dev); ++ ++ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { ++ if (cx23885_video_register(dev) < 0) { ++ printk(KERN_ERR "%s() Failed to register analog " ++ "video adapters on VID_A\n", __func__); ++ } ++ } ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { ++ if (cx23885_boards[dev->board].num_fds_portb) ++ dev->ts1.num_frontends = ++ cx23885_boards[dev->board].num_fds_portb; ++ if (cx23885_dvb_register(&dev->ts1) < 0) { ++ printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n", ++ __func__); ++ } ++ } else ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { ++ if (cx23885_417_register(dev) < 0) { ++ printk(KERN_ERR ++ "%s() Failed to register 417 on VID_B\n", ++ __func__); ++ } ++ } ++ ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { ++ if (cx23885_boards[dev->board].num_fds_portc) ++ dev->ts2.num_frontends = ++ cx23885_boards[dev->board].num_fds_portc; ++ if (cx23885_dvb_register(&dev->ts2) < 0) { ++ printk(KERN_ERR ++ "%s() Failed to register dvb on VID_C\n", ++ __func__); ++ } ++ } else ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) { ++ if (cx23885_417_register(dev) < 0) { ++ printk(KERN_ERR ++ "%s() Failed to register 417 on VID_C\n", ++ __func__); ++ } ++ } ++ ++ cx23885_dev_checkrevision(dev); ++ ++ /* disable MSI for NetUP cards, otherwise CI is not working */ ++ if (cx23885_boards[dev->board].ci_type > 0) ++ cx_clear(RDR_RDRCTL1, 1 << 8); ++ ++ switch (dev->board) { ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_TEVII_S471: ++ cx_clear(RDR_RDRCTL1, 1 << 8); ++ break; ++ } ++ ++ return 0; ++} ++ ++static void cx23885_dev_unregister(struct cx23885_dev *dev) ++{ ++ release_mem_region(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0)); ++ ++ if (!atomic_dec_and_test(&dev->refcount)) ++ return; ++ ++ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) ++ cx23885_video_unregister(dev); ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) ++ cx23885_dvb_unregister(&dev->ts1); ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ++ cx23885_417_unregister(dev); ++ ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) ++ cx23885_dvb_unregister(&dev->ts2); ++ ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) ++ cx23885_417_unregister(dev); ++ ++ cx23885_i2c_unregister(&dev->i2c_bus[2]); ++ cx23885_i2c_unregister(&dev->i2c_bus[1]); ++ cx23885_i2c_unregister(&dev->i2c_bus[0]); ++ ++ iounmap(dev->lmmio); ++} ++ ++static __le32 *cx23885_risc_field(__le32 *rp, struct scatterlist *sglist, ++ unsigned int offset, u32 sync_line, ++ unsigned int bpl, unsigned int padding, ++ unsigned int lines, unsigned int lpi) ++{ ++ struct scatterlist *sg; ++ unsigned int line, todo, sol; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ /* scan lines */ ++ sg = sglist; ++ for (line = 0; line < lines; line++) { ++ while (offset && offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ sg++; ++ } ++ ++ if (lpi && line > 0 && !(line % lpi)) ++ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; ++ else ++ sol = RISC_SOL; ++ ++ if (bpl <= sg_dma_len(sg)-offset) { ++ /* fits into current chunk */ ++ *(rp++) = cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += bpl; ++ } else { ++ /* scanline needs to be split */ ++ todo = bpl; ++ *(rp++) = cpu_to_le32(RISC_WRITE|sol| ++ (sg_dma_len(sg)-offset)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)+offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= (sg_dma_len(sg)-offset); ++ offset = 0; ++ sg++; ++ while (todo > sg_dma_len(sg)) { ++ *(rp++) = cpu_to_le32(RISC_WRITE| ++ sg_dma_len(sg)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= sg_dma_len(sg); ++ sg++; ++ } ++ *(rp++) = cpu_to_le32(RISC_WRITE|RISC_EOL|todo); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += todo; ++ } ++ offset += padding; ++ } ++ ++ return rp; ++} ++ ++int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, unsigned int top_offset, ++ unsigned int bottom_offset, unsigned int bpl, ++ unsigned int padding, unsigned int lines) ++{ ++ u32 instructions, fields; ++ __le32 *rp; ++ int rc; ++ ++ fields = 0; ++ if (UNSET != top_offset) ++ fields++; ++ if (UNSET != bottom_offset) ++ fields++; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Padding ++ can cause next bpl to start close to a page border. First DMA ++ region may be smaller than PAGE_SIZE */ ++ /* write and jump need and extra dword */ ++ instructions = fields * (1 + ((bpl + padding) * lines) ++ / PAGE_SIZE + lines); ++ instructions += 2; ++ rc = btcx_riscmem_alloc(pci, risc, instructions*12); ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ if (UNSET != top_offset) ++ rp = cx23885_risc_field(rp, sglist, top_offset, 0, ++ bpl, padding, lines, 0); ++ if (UNSET != bottom_offset) ++ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200, ++ bpl, padding, lines, 0); ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++ ++int cx23885_risc_databuffer(struct pci_dev *pci, ++ struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int bpl, ++ unsigned int lines, unsigned int lpi) ++{ ++ u32 instructions; ++ __le32 *rp; ++ int rc; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Here ++ there is no padding and no sync. First DMA region may be smaller ++ than PAGE_SIZE */ ++ /* Jump and write need an extra dword */ ++ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; ++ instructions += 1; ++ ++ rc = btcx_riscmem_alloc(pci, risc, instructions*12); ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, ++ bpl, 0, lines, lpi); ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++ ++int cx23885_risc_vbibuffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, unsigned int top_offset, ++ unsigned int bottom_offset, unsigned int bpl, ++ unsigned int padding, unsigned int lines) ++{ ++ u32 instructions, fields; ++ __le32 *rp; ++ int rc; ++ ++ fields = 0; ++ if (UNSET != top_offset) ++ fields++; ++ if (UNSET != bottom_offset) ++ fields++; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Padding ++ can cause next bpl to start close to a page border. First DMA ++ region may be smaller than PAGE_SIZE */ ++ /* write and jump need and extra dword */ ++ instructions = fields * (1 + ((bpl + padding) * lines) ++ / PAGE_SIZE + lines); ++ instructions += 2; ++ rc = btcx_riscmem_alloc(pci, risc, instructions*12); ++ if (rc < 0) ++ return rc; ++ /* write risc instructions */ ++ rp = risc->cpu; ++ ++ /* Sync to line 6, so US CC line 21 will appear in line '12' ++ * in the userland vbi payload */ ++ if (UNSET != top_offset) ++ rp = cx23885_risc_field(rp, sglist, top_offset, 6, ++ bpl, padding, lines, 0); ++ ++ if (UNSET != bottom_offset) ++ rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x207, ++ bpl, padding, lines, 0); ++ ++ ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++ ++ ++int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value) ++{ ++ __le32 *rp; ++ int rc; ++ ++ rc = btcx_riscmem_alloc(pci, risc, 4*16); ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2); ++ *(rp++) = cpu_to_le32(reg); ++ *(rp++) = cpu_to_le32(value); ++ *(rp++) = cpu_to_le32(mask); ++ *(rp++) = cpu_to_le32(RISC_JUMP); ++ *(rp++) = cpu_to_le32(risc->dma); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ return 0; ++} ++ ++void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) ++{ ++ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); ++ ++ BUG_ON(in_interrupt()); ++ videobuf_waiton(q, &buf->vb, 0, 0); ++ videobuf_dma_unmap(q->dev, dma); ++ videobuf_dma_free(dma); ++ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) ++{ ++ struct cx23885_dev *dev = port->dev; ++ ++ dprintk(1, "%s() Register Dump\n", __func__); ++ dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__, ++ cx_read(DEV_CNTRL2)); ++ dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__, ++ cx23885_irq_get_mask(dev)); ++ dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__, ++ cx_read(AUDIO_INT_INT_MSK)); ++ dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__, ++ cx_read(AUD_INT_DMA_CTL)); ++ dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __func__, ++ cx_read(AUDIO_EXT_INT_MSK)); ++ dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __func__, ++ cx_read(AUD_EXT_DMA_CTL)); ++ dprintk(1, "%s() PAD_CTRL 0x%08X\n", __func__, ++ cx_read(PAD_CTRL)); ++ dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __func__, ++ cx_read(ALT_PIN_OUT_SEL)); ++ dprintk(1, "%s() GPIO2 0x%08X\n", __func__, ++ cx_read(GPIO2)); ++ dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __func__, ++ port->reg_gpcnt, cx_read(port->reg_gpcnt)); ++ dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __func__, ++ port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl)); ++ dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __func__, ++ port->reg_dma_ctl, cx_read(port->reg_dma_ctl)); ++ if (port->reg_src_sel) ++ dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __func__, ++ port->reg_src_sel, cx_read(port->reg_src_sel)); ++ dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __func__, ++ port->reg_lngth, cx_read(port->reg_lngth)); ++ dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __func__, ++ port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl)); ++ dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __func__, ++ port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl)); ++ dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __func__, ++ port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status)); ++ dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __func__, ++ port->reg_sop_status, cx_read(port->reg_sop_status)); ++ dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __func__, ++ port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat)); ++ dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __func__, ++ port->reg_vld_misc, cx_read(port->reg_vld_misc)); ++ dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __func__, ++ port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en)); ++ dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __func__, ++ port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk)); ++} ++ ++static int cx23885_start_dma(struct cx23885_tsport *port, ++ struct cx23885_dmaqueue *q, ++ struct cx23885_buffer *buf) ++{ ++ struct cx23885_dev *dev = port->dev; ++ u32 reg; ++ ++ dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, ++ buf->vb.width, buf->vb.height, buf->vb.field); ++ ++ /* Stop the fifo and risc engine for this port */ ++ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); ++ ++ /* setup fifo + format */ ++ cx23885_sram_channel_setup(dev, ++ &dev->sram_channels[port->sram_chno], ++ port->ts_packet_size, buf->risc.dma); ++ if (debug > 5) { ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[port->sram_chno]); ++ cx23885_risc_disasm(port, &buf->risc); ++ } ++ ++ /* write TS length to chip */ ++ cx_write(port->reg_lngth, buf->vb.width); ++ ++ if ((!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) && ++ (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))) { ++ printk("%s() Unsupported .portb/c (0x%08x)/(0x%08x)\n", ++ __func__, ++ cx23885_boards[dev->board].portb, ++ cx23885_boards[dev->board].portc); ++ return -EINVAL; ++ } ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ++ cx23885_av_clk(dev, 0); ++ ++ udelay(100); ++ ++ /* If the port supports SRC SELECT, configure it */ ++ if (port->reg_src_sel) ++ cx_write(port->reg_src_sel, port->src_sel_val); ++ ++ cx_write(port->reg_hw_sop_ctrl, port->hw_sop_ctrl_val); ++ cx_write(port->reg_ts_clk_en, port->ts_clk_en_val); ++ cx_write(port->reg_vld_misc, port->vld_misc_val); ++ cx_write(port->reg_gen_ctrl, port->gen_ctrl_val); ++ udelay(100); ++ ++ /* NOTE: this is 2 (reserved) for portb, does it matter? */ ++ /* reset counter to zero */ ++ cx_write(port->reg_gpcnt_ctl, 3); ++ q->count = 1; ++ ++ /* Set VIDB pins to input */ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { ++ reg = cx_read(PAD_CTRL); ++ reg &= ~0x3; /* Clear TS1_OE & TS1_SOP_OE */ ++ cx_write(PAD_CTRL, reg); ++ } ++ ++ /* Set VIDC pins to input */ ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { ++ reg = cx_read(PAD_CTRL); ++ reg &= ~0x4; /* Clear TS2_SOP_OE */ ++ cx_write(PAD_CTRL, reg); ++ } ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { ++ ++ reg = cx_read(PAD_CTRL); ++ reg = reg & ~0x1; /* Clear TS1_OE */ ++ ++ /* FIXME, bit 2 writing here is questionable */ ++ /* set TS1_SOP_OE and TS1_OE_HI */ ++ reg = reg | 0xa; ++ cx_write(PAD_CTRL, reg); ++ ++ /* FIXME and these two registers should be documented. */ ++ cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011); ++ cx_write(ALT_PIN_OUT_SEL, 0x10100045); ++ } ++ ++ switch (dev->bridge) { ++ case CX23885_BRIDGE_885: ++ case CX23885_BRIDGE_887: ++ case CX23885_BRIDGE_888: ++ /* enable irqs */ ++ dprintk(1, "%s() enabling TS int's and DMA\n", __func__); ++ cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); ++ cx_set(port->reg_dma_ctl, port->dma_ctl_val); ++ cx23885_irq_add(dev, port->pci_irqmask); ++ cx23885_irq_enable_all(dev); ++ break; ++ default: ++ BUG(); ++ } ++ ++ cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */ ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ++ cx23885_av_clk(dev, 1); ++ ++ if (debug > 4) ++ cx23885_tsport_reg_dump(port); ++ ++ return 0; ++} ++ ++static int cx23885_stop_dma(struct cx23885_tsport *port) ++{ ++ struct cx23885_dev *dev = port->dev; ++ u32 reg; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* Stop interrupts and DMA */ ++ cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val); ++ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { ++ ++ reg = cx_read(PAD_CTRL); ++ ++ /* Set TS1_OE */ ++ reg = reg | 0x1; ++ ++ /* clear TS1_SOP_OE and TS1_OE_HI */ ++ reg = reg & ~0xa; ++ cx_write(PAD_CTRL, reg); ++ cx_write(port->reg_src_sel, 0); ++ cx_write(port->reg_gen_ctrl, 8); ++ ++ } ++ ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ++ cx23885_av_clk(dev, 0); ++ ++ return 0; ++} ++ ++int cx23885_restart_queue(struct cx23885_tsport *port, ++ struct cx23885_dmaqueue *q) ++{ ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_buffer *buf; ++ ++ dprintk(5, "%s()\n", __func__); ++ if (list_empty(&q->active)) { ++ struct cx23885_buffer *prev; ++ prev = NULL; ++ ++ dprintk(5, "%s() queue is empty\n", __func__); ++ ++ for (;;) { ++ if (list_empty(&q->queued)) ++ return 0; ++ buf = list_entry(q->queued.next, struct cx23885_buffer, ++ vb.queue); ++ if (NULL == prev) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ cx23885_start_dma(port, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(5, "[%p/%d] restart_queue - f/active\n", ++ buf, buf->vb.i); ++ ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ /* 64 bit bits 63-32 */ ++ prev->risc.jmp[2] = cpu_to_le32(0); ++ dprintk(5, "[%p/%d] restart_queue - m/active\n", ++ buf, buf->vb.i); ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++ return 0; ++ } ++ ++ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); ++ dprintk(2, "restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ cx23885_start_dma(port, q, buf); ++ list_for_each_entry(buf, &q->active, vb.queue) ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, ++ struct cx23885_buffer *buf, enum v4l2_field field) ++{ ++ struct cx23885_dev *dev = port->dev; ++ int size = port->ts_packet_size * port->ts_packet_count; ++ int rc; ++ ++ dprintk(1, "%s: %p\n", __func__, buf); ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ buf->vb.width = port->ts_packet_size; ++ buf->vb.height = port->ts_packet_count; ++ buf->vb.size = size; ++ buf->vb.field = field /*V4L2_FIELD_TOP*/; ++ ++ rc = videobuf_iolock(q, &buf->vb, NULL); ++ if (0 != rc) ++ goto fail; ++ cx23885_risc_databuffer(dev->pci, &buf->risc, ++ videobuf_to_dma(&buf->vb)->sglist, ++ buf->vb.width, buf->vb.height, 0); ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx23885_free_buffer(q, buf); ++ return rc; ++} ++ ++void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) ++{ ++ struct cx23885_buffer *prev; ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_dmaqueue *cx88q = &port->mpegq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if (list_empty(&cx88q->active)) { ++ dprintk(1, "queue is empty - first active\n"); ++ list_add_tail(&buf->vb.queue, &cx88q->active); ++ cx23885_start_dma(port, cx88q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = cx88q->count++; ++ mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT); ++ dprintk(1, "[%p/%d] %s - first active\n", ++ buf, buf->vb.i, __func__); ++ } else { ++ dprintk(1, "queue is not empty - append to active\n"); ++ prev = list_entry(cx88q->active.prev, struct cx23885_buffer, ++ vb.queue); ++ list_add_tail(&buf->vb.queue, &cx88q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = cx88q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */ ++ dprintk(1, "[%p/%d] %s - append to active\n", ++ buf, buf->vb.i, __func__); ++ } ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, ++ int restart) ++{ ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_dmaqueue *q = &port->mpegq; ++ struct cx23885_buffer *buf; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->slock, flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx23885_buffer, ++ vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ dprintk(1, "[%p/%d] %s - dma=0x%08lx\n", ++ buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); ++ } ++ if (restart) { ++ dprintk(1, "restarting queue\n"); ++ cx23885_restart_queue(port, q); ++ } ++ spin_unlock_irqrestore(&port->slock, flags); ++} ++ ++void cx23885_cancel_buffers(struct cx23885_tsport *port) ++{ ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_dmaqueue *q = &port->mpegq; ++ ++ dprintk(1, "%s()\n", __func__); ++ del_timer_sync(&q->timeout); ++ cx23885_stop_dma(port); ++ do_cancel_buffers(port, "cancel", 0); ++} ++ ++static void cx23885_timeout(unsigned long data) ++{ ++ struct cx23885_tsport *port = (struct cx23885_tsport *)data; ++ struct cx23885_dev *dev = port->dev; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (debug > 5) ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[port->sram_chno]); ++ ++ cx23885_stop_dma(port); ++ do_cancel_buffers(port, "timeout", 1); ++} ++ ++int cx23885_irq_417(struct cx23885_dev *dev, u32 status) ++{ ++ /* FIXME: port1 assumption here. */ ++ struct cx23885_tsport *port = &dev->ts1; ++ int count = 0; ++ int handled = 0; ++ ++ if (status == 0) ++ return handled; ++ ++ count = cx_read(port->reg_gpcnt); ++ dprintk(7, "status: 0x%08x mask: 0x%08x count: 0x%x\n", ++ status, cx_read(port->reg_ts_int_msk), count); ++ ++ if ((status & VID_B_MSK_BAD_PKT) || ++ (status & VID_B_MSK_OPC_ERR) || ++ (status & VID_B_MSK_VBI_OPC_ERR) || ++ (status & VID_B_MSK_SYNC) || ++ (status & VID_B_MSK_VBI_SYNC) || ++ (status & VID_B_MSK_OF) || ++ (status & VID_B_MSK_VBI_OF)) { ++ printk(KERN_ERR "%s: V4L mpeg risc op code error, status " ++ "= 0x%x\n", dev->name, status); ++ if (status & VID_B_MSK_BAD_PKT) ++ dprintk(1, " VID_B_MSK_BAD_PKT\n"); ++ if (status & VID_B_MSK_OPC_ERR) ++ dprintk(1, " VID_B_MSK_OPC_ERR\n"); ++ if (status & VID_B_MSK_VBI_OPC_ERR) ++ dprintk(1, " VID_B_MSK_VBI_OPC_ERR\n"); ++ if (status & VID_B_MSK_SYNC) ++ dprintk(1, " VID_B_MSK_SYNC\n"); ++ if (status & VID_B_MSK_VBI_SYNC) ++ dprintk(1, " VID_B_MSK_VBI_SYNC\n"); ++ if (status & VID_B_MSK_OF) ++ dprintk(1, " VID_B_MSK_OF\n"); ++ if (status & VID_B_MSK_VBI_OF) ++ dprintk(1, " VID_B_MSK_VBI_OF\n"); ++ ++ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[port->sram_chno]); ++ cx23885_417_check_encoder(dev); ++ } else if (status & VID_B_MSK_RISCI1) { ++ dprintk(7, " VID_B_MSK_RISCI1\n"); ++ spin_lock(&port->slock); ++ cx23885_wakeup(port, &port->mpegq, count); ++ spin_unlock(&port->slock); ++ } else if (status & VID_B_MSK_RISCI2) { ++ dprintk(7, " VID_B_MSK_RISCI2\n"); ++ spin_lock(&port->slock); ++ cx23885_restart_queue(port, &port->mpegq); ++ spin_unlock(&port->slock); ++ } ++ if (status) { ++ cx_write(port->reg_ts_int_stat, status); ++ handled = 1; ++ } ++ ++ return handled; ++} ++ ++static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) ++{ ++ struct cx23885_dev *dev = port->dev; ++ int handled = 0; ++ u32 count; ++ ++ if ((status & VID_BC_MSK_OPC_ERR) || ++ (status & VID_BC_MSK_BAD_PKT) || ++ (status & VID_BC_MSK_SYNC) || ++ (status & VID_BC_MSK_OF)) { ++ ++ if (status & VID_BC_MSK_OPC_ERR) ++ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n", ++ VID_BC_MSK_OPC_ERR); ++ ++ if (status & VID_BC_MSK_BAD_PKT) ++ dprintk(7, " (VID_BC_MSK_BAD_PKT 0x%08x)\n", ++ VID_BC_MSK_BAD_PKT); ++ ++ if (status & VID_BC_MSK_SYNC) ++ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x)\n", ++ VID_BC_MSK_SYNC); ++ ++ if (status & VID_BC_MSK_OF) ++ dprintk(7, " (VID_BC_MSK_OF 0x%08x)\n", ++ VID_BC_MSK_OF); ++ ++ printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name); ++ ++ cx_clear(port->reg_dma_ctl, port->dma_ctl_val); ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[port->sram_chno]); ++ ++ } else if (status & VID_BC_MSK_RISCI1) { ++ ++ dprintk(7, " (RISCI1 0x%08x)\n", VID_BC_MSK_RISCI1); ++ ++ spin_lock(&port->slock); ++ count = cx_read(port->reg_gpcnt); ++ cx23885_wakeup(port, &port->mpegq, count); ++ spin_unlock(&port->slock); ++ ++ } else if (status & VID_BC_MSK_RISCI2) { ++ ++ dprintk(7, " (RISCI2 0x%08x)\n", VID_BC_MSK_RISCI2); ++ ++ spin_lock(&port->slock); ++ cx23885_restart_queue(port, &port->mpegq); ++ spin_unlock(&port->slock); ++ ++ } ++ if (status) { ++ cx_write(port->reg_ts_int_stat, status); ++ handled = 1; ++ } ++ ++ return handled; ++} ++ ++static irqreturn_t cx23885_irq(int irq, void *dev_id) ++{ ++ struct cx23885_dev *dev = dev_id; ++ struct cx23885_tsport *ts1 = &dev->ts1; ++ struct cx23885_tsport *ts2 = &dev->ts2; ++ u32 pci_status, pci_mask; ++ u32 vida_status, vida_mask; ++ u32 audint_status, audint_mask; ++ u32 ts1_status, ts1_mask; ++ u32 ts2_status, ts2_mask; ++ int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; ++ int audint_count = 0; ++ bool subdev_handled; ++ ++ pci_status = cx_read(PCI_INT_STAT); ++ pci_mask = cx23885_irq_get_mask(dev); ++ vida_status = cx_read(VID_A_INT_STAT); ++ vida_mask = cx_read(VID_A_INT_MSK); ++ audint_status = cx_read(AUDIO_INT_INT_STAT); ++ audint_mask = cx_read(AUDIO_INT_INT_MSK); ++ ts1_status = cx_read(VID_B_INT_STAT); ++ ts1_mask = cx_read(VID_B_INT_MSK); ++ ts2_status = cx_read(VID_C_INT_STAT); ++ ts2_mask = cx_read(VID_C_INT_MSK); ++ ++ if ((pci_status == 0) && (ts2_status == 0) && (ts1_status == 0)) ++ goto out; ++ ++ vida_count = cx_read(VID_A_GPCNT); ++ audint_count = cx_read(AUD_INT_A_GPCNT); ++ ts1_count = cx_read(ts1->reg_gpcnt); ++ ts2_count = cx_read(ts2->reg_gpcnt); ++ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", ++ pci_status, pci_mask); ++ dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n", ++ vida_status, vida_mask, vida_count); ++ dprintk(7, "audint_status: 0x%08x audint_mask: 0x%08x count: 0x%x\n", ++ audint_status, audint_mask, audint_count); ++ dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", ++ ts1_status, ts1_mask, ts1_count); ++ dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ++ ts2_status, ts2_mask, ts2_count); ++ ++ if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR | ++ PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA | ++ PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A | ++ PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT | ++ PCI_MSK_GPIO0 | PCI_MSK_GPIO1 | ++ PCI_MSK_AV_CORE | PCI_MSK_IR)) { ++ ++ if (pci_status & PCI_MSK_RISC_RD) ++ dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", ++ PCI_MSK_RISC_RD); ++ ++ if (pci_status & PCI_MSK_RISC_WR) ++ dprintk(7, " (PCI_MSK_RISC_WR 0x%08x)\n", ++ PCI_MSK_RISC_WR); ++ ++ if (pci_status & PCI_MSK_AL_RD) ++ dprintk(7, " (PCI_MSK_AL_RD 0x%08x)\n", ++ PCI_MSK_AL_RD); ++ ++ if (pci_status & PCI_MSK_AL_WR) ++ dprintk(7, " (PCI_MSK_AL_WR 0x%08x)\n", ++ PCI_MSK_AL_WR); ++ ++ if (pci_status & PCI_MSK_APB_DMA) ++ dprintk(7, " (PCI_MSK_APB_DMA 0x%08x)\n", ++ PCI_MSK_APB_DMA); ++ ++ if (pci_status & PCI_MSK_VID_C) ++ dprintk(7, " (PCI_MSK_VID_C 0x%08x)\n", ++ PCI_MSK_VID_C); ++ ++ if (pci_status & PCI_MSK_VID_B) ++ dprintk(7, " (PCI_MSK_VID_B 0x%08x)\n", ++ PCI_MSK_VID_B); ++ ++ if (pci_status & PCI_MSK_VID_A) ++ dprintk(7, " (PCI_MSK_VID_A 0x%08x)\n", ++ PCI_MSK_VID_A); ++ ++ if (pci_status & PCI_MSK_AUD_INT) ++ dprintk(7, " (PCI_MSK_AUD_INT 0x%08x)\n", ++ PCI_MSK_AUD_INT); ++ ++ if (pci_status & PCI_MSK_AUD_EXT) ++ dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n", ++ PCI_MSK_AUD_EXT); ++ ++ if (pci_status & PCI_MSK_GPIO0) ++ dprintk(7, " (PCI_MSK_GPIO0 0x%08x)\n", ++ PCI_MSK_GPIO0); ++ ++ if (pci_status & PCI_MSK_GPIO1) ++ dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", ++ PCI_MSK_GPIO1); ++ ++ if (pci_status & PCI_MSK_AV_CORE) ++ dprintk(7, " (PCI_MSK_AV_CORE 0x%08x)\n", ++ PCI_MSK_AV_CORE); ++ ++ if (pci_status & PCI_MSK_IR) ++ dprintk(7, " (PCI_MSK_IR 0x%08x)\n", ++ PCI_MSK_IR); ++ } ++ ++ if (cx23885_boards[dev->board].ci_type == 1 && ++ (pci_status & (PCI_MSK_GPIO1 | PCI_MSK_GPIO0))) ++ handled += netup_ci_slot_status(dev, pci_status); ++ ++ if (cx23885_boards[dev->board].ci_type == 2 && ++ (pci_status & PCI_MSK_GPIO0)) ++ handled += altera_ci_irq(dev); ++ ++ if (ts1_status) { ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) ++ handled += cx23885_irq_ts(ts1, ts1_status); ++ else ++ if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) ++ handled += cx23885_irq_417(dev, ts1_status); ++ } ++ ++ if (ts2_status) { ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) ++ handled += cx23885_irq_ts(ts2, ts2_status); ++ else ++ if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) ++ handled += cx23885_irq_417(dev, ts2_status); ++ } ++ ++ if (vida_status) ++ handled += cx23885_video_irq(dev, vida_status); ++ ++ if (audint_status) ++ handled += cx23885_audio_irq(dev, audint_status, audint_mask); ++ ++ if (pci_status & PCI_MSK_IR) { ++ subdev_handled = false; ++ v4l2_subdev_call(dev->sd_ir, core, interrupt_service_routine, ++ pci_status, &subdev_handled); ++ if (subdev_handled) ++ handled++; ++ } ++ ++ if ((pci_status & pci_mask) & PCI_MSK_AV_CORE) { ++ cx23885_irq_disable(dev, PCI_MSK_AV_CORE); ++ if (!schedule_work(&dev->cx25840_work)) ++ printk(KERN_ERR "%s: failed to set up deferred work for" ++ " AV Core/IR interrupt. Interrupt is disabled" ++ " and won't be re-enabled\n", dev->name); ++ handled++; ++ } ++ ++ if (handled) ++ cx_write(PCI_INT_STAT, pci_status); ++out: ++ return IRQ_RETVAL(handled); ++} ++ ++static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, ++ unsigned int notification, void *arg) ++{ ++ struct cx23885_dev *dev; ++ ++ if (sd == NULL) ++ return; ++ ++ dev = to_cx23885(sd->v4l2_dev); ++ ++ switch (notification) { ++ case V4L2_SUBDEV_IR_RX_NOTIFY: /* Possibly called in an IRQ context */ ++ if (sd == dev->sd_ir) ++ cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg); ++ break; ++ case V4L2_SUBDEV_IR_TX_NOTIFY: /* Possibly called in an IRQ context */ ++ if (sd == dev->sd_ir) ++ cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg); ++ break; ++ } ++} ++ ++static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev) ++{ ++ INIT_WORK(&dev->cx25840_work, cx23885_av_work_handler); ++ INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler); ++ INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler); ++ dev->v4l2_dev.notify = cx23885_v4l2_dev_notify; ++} ++ ++static inline int encoder_on_portb(struct cx23885_dev *dev) ++{ ++ return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER; ++} ++ ++static inline int encoder_on_portc(struct cx23885_dev *dev) ++{ ++ return cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER; ++} ++ ++/* Mask represents 32 different GPIOs, GPIO's are split into multiple ++ * registers depending on the board configuration (and whether the ++ * 417 encoder (wi it's own GPIO's) are present. Each GPIO bit will ++ * be pushed into the correct hardware register, regardless of the ++ * physical location. Certain registers are shared so we sanity check ++ * and report errors if we think we're tampering with a GPIo that might ++ * be assigned to the encoder (and used for the host bus). ++ * ++ * GPIO 2 thru 0 - On the cx23885 bridge ++ * GPIO 18 thru 3 - On the cx23417 host bus interface ++ * GPIO 23 thru 19 - On the cx25840 a/v core ++ */ ++void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask) ++{ ++ if (mask & 0x7) ++ cx_set(GP0_IO, mask & 0x7); ++ ++ if (mask & 0x0007fff8) { ++ if (encoder_on_portb(dev) || encoder_on_portc(dev)) ++ printk(KERN_ERR ++ "%s: Setting GPIO on encoder ports\n", ++ dev->name); ++ cx_set(MC417_RWD, (mask & 0x0007fff8) >> 3); ++ } ++ ++ /* TODO: 23-19 */ ++ if (mask & 0x00f80000) ++ printk(KERN_INFO "%s: Unsupported\n", dev->name); ++} ++ ++void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask) ++{ ++ if (mask & 0x00000007) ++ cx_clear(GP0_IO, mask & 0x7); ++ ++ if (mask & 0x0007fff8) { ++ if (encoder_on_portb(dev) || encoder_on_portc(dev)) ++ printk(KERN_ERR ++ "%s: Clearing GPIO moving on encoder ports\n", ++ dev->name); ++ cx_clear(MC417_RWD, (mask & 0x7fff8) >> 3); ++ } ++ ++ /* TODO: 23-19 */ ++ if (mask & 0x00f80000) ++ printk(KERN_INFO "%s: Unsupported\n", dev->name); ++} ++ ++u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask) ++{ ++ if (mask & 0x00000007) ++ return (cx_read(GP0_IO) >> 8) & mask & 0x7; ++ ++ if (mask & 0x0007fff8) { ++ if (encoder_on_portb(dev) || encoder_on_portc(dev)) ++ printk(KERN_ERR ++ "%s: Reading GPIO moving on encoder ports\n", ++ dev->name); ++ return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3; ++ } ++ ++ /* TODO: 23-19 */ ++ if (mask & 0x00f80000) ++ printk(KERN_INFO "%s: Unsupported\n", dev->name); ++ ++ return 0; ++} ++ ++void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) ++{ ++ if ((mask & 0x00000007) && asoutput) ++ cx_set(GP0_IO, (mask & 0x7) << 16); ++ else if ((mask & 0x00000007) && !asoutput) ++ cx_clear(GP0_IO, (mask & 0x7) << 16); ++ ++ if (mask & 0x0007fff8) { ++ if (encoder_on_portb(dev) || encoder_on_portc(dev)) ++ printk(KERN_ERR ++ "%s: Enabling GPIO on encoder ports\n", ++ dev->name); ++ } ++ ++ /* MC417_OEN is active low for output, write 1 for an input */ ++ if ((mask & 0x0007fff8) && asoutput) ++ cx_clear(MC417_OEN, (mask & 0x7fff8) >> 3); ++ ++ else if ((mask & 0x0007fff8) && !asoutput) ++ cx_set(MC417_OEN, (mask & 0x7fff8) >> 3); ++ ++ /* TODO: 23-19 */ ++} ++ ++static int __devinit cx23885_initdev(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct cx23885_dev *dev; ++ int err; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (NULL == dev) ++ return -ENOMEM; ++ ++ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); ++ if (err < 0) ++ goto fail_free; ++ ++ /* Prepare to handle notifications from subdevices */ ++ cx23885_v4l2_dev_notify_init(dev); ++ ++ /* pci init */ ++ dev->pci = pci_dev; ++ if (pci_enable_device(pci_dev)) { ++ err = -EIO; ++ goto fail_unreg; ++ } ++ ++ if (cx23885_dev_setup(dev) < 0) { ++ err = -EINVAL; ++ goto fail_unreg; ++ } ++ ++ /* print pci info */ ++ dev->pci_rev = pci_dev->revision; ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); ++ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", dev->name, ++ pci_name(pci_dev), dev->pci_rev, pci_dev->irq, ++ dev->pci_lat, ++ (unsigned long long)pci_resource_start(pci_dev, 0)); ++ ++ pci_set_master(pci_dev); ++ if (!pci_dma_supported(pci_dev, 0xffffffff)) { ++ printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); ++ err = -EIO; ++ goto fail_irq; ++ } ++ ++ err = request_irq(pci_dev->irq, cx23885_irq, ++ IRQF_SHARED | IRQF_DISABLED, dev->name, dev); ++ if (err < 0) { ++ printk(KERN_ERR "%s: can't get IRQ %d\n", ++ dev->name, pci_dev->irq); ++ goto fail_irq; ++ } ++ ++ switch (dev->board) { ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ cx23885_irq_add_enable(dev, PCI_MSK_GPIO1 | PCI_MSK_GPIO0); ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ cx23885_irq_add_enable(dev, PCI_MSK_GPIO0); ++ break; ++ } ++ ++ /* ++ * The CX2388[58] IR controller can start firing interrupts when ++ * enabled, so these have to take place after the cx23885_irq() handler ++ * is hooked up by the call to request_irq() above. ++ */ ++ cx23885_ir_pci_int_enable(dev); ++ cx23885_input_init(dev); ++ ++ return 0; ++ ++fail_irq: ++ cx23885_dev_unregister(dev); ++fail_unreg: ++ v4l2_device_unregister(&dev->v4l2_dev); ++fail_free: ++ kfree(dev); ++ return err; ++} ++ ++static void __devexit cx23885_finidev(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct cx23885_dev *dev = to_cx23885(v4l2_dev); ++ ++ cx23885_input_fini(dev); ++ cx23885_ir_fini(dev); ++ ++ cx23885_shutdown(dev); ++ ++ pci_disable_device(pci_dev); ++ ++ /* unregister stuff */ ++ free_irq(pci_dev->irq, dev); ++ ++ cx23885_dev_unregister(dev); ++ v4l2_device_unregister(v4l2_dev); ++ kfree(dev); ++} ++ ++static struct pci_device_id cx23885_pci_tbl[] = { ++ { ++ /* CX23885 */ ++ .vendor = 0x14f1, ++ .device = 0x8852, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ /* CX23887 Rev 2 */ ++ .vendor = 0x14f1, ++ .device = 0x8880, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ /* --- end of list --- */ ++ } ++}; ++MODULE_DEVICE_TABLE(pci, cx23885_pci_tbl); ++ ++static struct pci_driver cx23885_pci_driver = { ++ .name = "cx23885", ++ .id_table = cx23885_pci_tbl, ++ .probe = cx23885_initdev, ++ .remove = __devexit_p(cx23885_finidev), ++ /* TODO */ ++ .suspend = NULL, ++ .resume = NULL, ++}; ++ ++static int __init cx23885_init(void) ++{ ++ printk(KERN_INFO "cx23885 driver version %s loaded\n", ++ CX23885_VERSION); ++ return pci_register_driver(&cx23885_pci_driver); ++} ++ ++static void __exit cx23885_fini(void) ++{ ++ pci_unregister_driver(&cx23885_pci_driver); ++} ++ ++module_init(cx23885_init); ++module_exit(cx23885_fini); +diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c +new file mode 100644 +index 0000000..9c5ed10 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-dvb.c +@@ -0,0 +1,1448 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include ++ ++#include "dvb_ca_en50221.h" ++#include "s5h1409.h" ++#include "s5h1411.h" ++#include "mt2131.h" ++#include "tda8290.h" ++#include "tda18271.h" ++#include "lgdt330x.h" ++#include "xc4000.h" ++#include "xc5000.h" ++#include "max2165.h" ++#include "tda10048.h" ++#include "tuner-xc2028.h" ++#include "tuner-simple.h" ++#include "dib7000p.h" ++#include "dibx000_common.h" ++#include "zl10353.h" ++#include "stv0900.h" ++#include "stv0900_reg.h" ++#include "stv6110.h" ++#include "lnbh24.h" ++#include "cx24116.h" ++#include "cimax2.h" ++#include "lgs8gxx.h" ++#include "netup-eeprom.h" ++#include "netup-init.h" ++#include "lgdt3305.h" ++#include "atbm8830.h" ++#include "ts2020.h" ++#include "ds3000.h" ++#include "cx23885-f300.h" ++#include "altera-ci.h" ++#include "stv0367.h" ++#include "drxk.h" ++#include "mt2063.h" ++#include "stv090x.h" ++#include "stb6100.h" ++#include "stb6100_cfg.h" ++#include "tda10071.h" ++#include "a8293.h" ++ ++static unsigned int debug; ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (debug >= level)\ ++ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int alt_tuner; ++module_param(alt_tuner, int, 0644); ++MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++/* ------------------------------------------------------------------ */ ++ ++static int dvb_buf_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct cx23885_tsport *port = q->priv_data; ++ ++ port->ts_packet_size = 188 * 4; ++ port->ts_packet_count = 32; ++ ++ *size = port->ts_packet_size * port->ts_packet_count; ++ *count = 32; ++ return 0; ++} ++ ++static int dvb_buf_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, enum v4l2_field field) ++{ ++ struct cx23885_tsport *port = q->priv_data; ++ return cx23885_buf_prepare(q, port, (struct cx23885_buffer *)vb, field); ++} ++ ++static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx23885_tsport *port = q->priv_data; ++ cx23885_buf_queue(port, (struct cx23885_buffer *)vb); ++} ++ ++static void dvb_buf_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ cx23885_free_buffer(q, (struct cx23885_buffer *)vb); ++} ++ ++static int cx23885_dvb_set_frontend(struct dvb_frontend *fe); ++ ++static void cx23885_dvb_gate_ctrl(struct cx23885_tsport *port, int open) ++{ ++ struct videobuf_dvb_frontends *f; ++ struct videobuf_dvb_frontend *fe; ++ ++ f = &port->frontends; ++ ++ if (f->gate <= 1) /* undefined or fe0 */ ++ fe = videobuf_dvb_get_frontend(f, 1); ++ else ++ fe = videobuf_dvb_get_frontend(f, f->gate); ++ ++ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) ++ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); ++ ++ /* ++ * FIXME: Improve this path to avoid calling the ++ * cx23885_dvb_set_frontend() every time it passes here. ++ */ ++ cx23885_dvb_set_frontend(fe->dvb.frontend); ++} ++ ++static struct videobuf_queue_ops dvb_qops = { ++ .buf_setup = dvb_buf_setup, ++ .buf_prepare = dvb_buf_prepare, ++ .buf_queue = dvb_buf_queue, ++ .buf_release = dvb_buf_release, ++}; ++ ++static struct s5h1409_config hauppauge_generic_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_ON, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct tda10048_config hauppauge_hvr1200_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_SERIAL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3800, ++ .dtv8_if_freq_khz = TDA10048_IF_4300, ++ .clk_freq_khz = TDA10048_CLK_16000, ++}; ++ ++static struct tda10048_config hauppauge_hvr1210_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_SERIAL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3500, ++ .dtv8_if_freq_khz = TDA10048_IF_4000, ++ .clk_freq_khz = TDA10048_CLK_16000, ++}; ++ ++static struct s5h1409_config hauppauge_ezqam_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .qam_if = 4000, ++ .inversion = S5H1409_INVERSION_ON, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct s5h1409_config hauppauge_hvr1800lp_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct s5h1409_config hauppauge_hvr1500_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct mt2131_config hauppauge_generic_tunerconfig = { ++ 0x61 ++}; ++ ++static struct lgdt330x_config fusionhdtv_5_express = { ++ .demod_address = 0x0e, ++ .demod_chip = LGDT3303, ++ .serial_mpeg = 0x40, ++}; ++ ++static struct s5h1409_config hauppauge_hvr1500q_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_ON, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct s5h1409_config dvico_s5h1409_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_ON, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct s5h1411_config dvico_s5h1411_config = { ++ .output_mode = S5H1411_SERIAL_OUTPUT, ++ .gpio = S5H1411_GPIO_ON, ++ .qam_if = S5H1411_IF_44000, ++ .vsb_if = S5H1411_IF_44000, ++ .inversion = S5H1411_INVERSION_OFF, ++ .status_mode = S5H1411_DEMODLOCKING, ++ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct s5h1411_config hcw_s5h1411_config = { ++ .output_mode = S5H1411_SERIAL_OUTPUT, ++ .gpio = S5H1411_GPIO_OFF, ++ .vsb_if = S5H1411_IF_44000, ++ .qam_if = S5H1411_IF_4000, ++ .inversion = S5H1411_INVERSION_ON, ++ .status_mode = S5H1411_DEMODLOCKING, ++ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { ++ .i2c_address = 0x61, ++ .if_khz = 5380, ++}; ++ ++static struct xc5000_config dvico_xc5000_tunerconfig = { ++ .i2c_address = 0x64, ++ .if_khz = 5380, ++}; ++ ++static struct tda829x_config tda829x_no_probe = { ++ .probe_tuner = TDA829X_DONT_PROBE, ++}; ++ ++static struct tda18271_std_map hauppauge_tda18271_std_map = { ++ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++}; ++ ++static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { ++ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++}; ++ ++static struct tda18271_config hauppauge_tda18271_config = { ++ .std_map = &hauppauge_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct tda18271_config hauppauge_hvr1200_tuner_config = { ++ .std_map = &hauppauge_hvr1200_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct tda18271_config hauppauge_hvr1210_tuner_config = { ++ .gate = TDA18271_GATE_DIGITAL, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct tda18271_std_map hauppauge_hvr127x_std_map = { ++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, ++ .if_lvl = 1, .rfagc_top = 0x58 }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, ++ .if_lvl = 1, .rfagc_top = 0x58 }, ++}; ++ ++static struct tda18271_config hauppauge_hvr127x_config = { ++ .std_map = &hauppauge_hvr127x_std_map, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct lgdt3305_config hauppauge_lgdt3305_config = { ++ .i2c_addr = 0x0e, ++ .mpeg_mode = LGDT3305_MPEG_SERIAL, ++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, ++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, ++ .deny_i2c_rptr = 1, ++ .spectral_inversion = 1, ++ .qam_if_khz = 4000, ++ .vsb_if_khz = 3250, ++}; ++ ++static struct dibx000_agc_config xc3028_agc_config = { ++ BAND_VHF | BAND_UHF, /* band_caps */ ++ ++ /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, ++ * P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, ++ * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, ++ * P_agc_nb_est=2, P_agc_write=0 ++ */ ++ (0 << 15) | (0 << 14) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | ++ (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ ++ ++ 712, /* inv_gain */ ++ 21, /* time_stabiliz */ ++ ++ 0, /* alpha_level */ ++ 118, /* thlock */ ++ ++ 0, /* wbd_inv */ ++ 2867, /* wbd_ref */ ++ 0, /* wbd_sel */ ++ 2, /* wbd_alpha */ ++ ++ 0, /* agc1_max */ ++ 0, /* agc1_min */ ++ 39718, /* agc2_max */ ++ 9930, /* agc2_min */ ++ 0, /* agc1_pt1 */ ++ 0, /* agc1_pt2 */ ++ 0, /* agc1_pt3 */ ++ 0, /* agc1_slope1 */ ++ 0, /* agc1_slope2 */ ++ 0, /* agc2_pt1 */ ++ 128, /* agc2_pt2 */ ++ 29, /* agc2_slope1 */ ++ 29, /* agc2_slope2 */ ++ ++ 17, /* alpha_mant */ ++ 27, /* alpha_exp */ ++ 23, /* beta_mant */ ++ 51, /* beta_exp */ ++ ++ 1, /* perform_agc_softsplit */ ++}; ++ ++/* PLL Configuration for COFDM BW_MHz = 8.000000 ++ * With external clock = 30.000000 */ ++static struct dibx000_bandwidth_config xc3028_bw_config = { ++ 60000, /* internal */ ++ 30000, /* sampling */ ++ 1, /* pll_cfg: prediv */ ++ 8, /* pll_cfg: ratio */ ++ 3, /* pll_cfg: range */ ++ 1, /* pll_cfg: reset */ ++ 0, /* pll_cfg: bypass */ ++ 0, /* misc: refdiv */ ++ 0, /* misc: bypclk_div */ ++ 1, /* misc: IO_CLK_en_core */ ++ 1, /* misc: ADClkSrc */ ++ 0, /* misc: modulo */ ++ (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ ++ (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ ++ 20452225, /* timf */ ++ 30000000 /* xtal_hz */ ++}; ++ ++static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { ++ .output_mpeg2_in_188_bytes = 1, ++ .hostbus_diversity = 1, ++ .tuner_is_baseband = 0, ++ .update_lna = NULL, ++ ++ .agc_config_count = 1, ++ .agc = &xc3028_agc_config, ++ .bw = &xc3028_bw_config, ++ ++ .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, ++ .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, ++ .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, ++ ++ .pwm_freq_div = 0, ++ .agc_control = NULL, ++ .spur_protect = 0, ++ ++ .output_mode = OUTMODE_MPEG2_SERIAL, ++}; ++ ++static struct zl10353_config dvico_fusionhdtv_xc3028 = { ++ .demod_address = 0x0f, ++ .if2 = 45600, ++ .no_tuner = 1, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static struct stv0900_reg stv0900_ts_regs[] = { ++ { R0900_TSGENERAL, 0x00 }, ++ { R0900_P1_TSSPEED, 0x40 }, ++ { R0900_P2_TSSPEED, 0x40 }, ++ { R0900_P1_TSCFGM, 0xc0 }, ++ { R0900_P2_TSCFGM, 0xc0 }, ++ { R0900_P1_TSCFGH, 0xe0 }, ++ { R0900_P2_TSCFGH, 0xe0 }, ++ { R0900_P1_TSCFGL, 0x20 }, ++ { R0900_P2_TSCFGL, 0x20 }, ++ { 0xffff, 0xff }, /* terminate */ ++}; ++ ++static struct stv0900_config netup_stv0900_config = { ++ .demod_address = 0x68, ++ .demod_mode = 1, /* dual */ ++ .xtal = 8000000, ++ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ ++ .diseqc_mode = 2,/* 2/3 PWM */ ++ .ts_config_regs = stv0900_ts_regs, ++ .tun1_maddress = 0,/* 0x60 */ ++ .tun2_maddress = 3,/* 0x63 */ ++ .tun1_adc = 1,/* 1 Vpp */ ++ .tun2_adc = 1,/* 1 Vpp */ ++}; ++ ++static struct stv6110_config netup_stv6110_tunerconfig_a = { ++ .i2c_address = 0x60, ++ .mclk = 16000000, ++ .clk_div = 1, ++ .gain = 8, /* +16 dB - maximum gain */ ++}; ++ ++static struct stv6110_config netup_stv6110_tunerconfig_b = { ++ .i2c_address = 0x63, ++ .mclk = 16000000, ++ .clk_div = 1, ++ .gain = 8, /* +16 dB - maximum gain */ ++}; ++ ++static struct cx24116_config tbs_cx24116_config = { ++ .demod_address = 0x55, ++}; ++ ++static struct ds3000_config tevii_ds3000_config = { ++ .demod_address = 0x68, ++}; ++ ++static struct ts2020_config tevii_ts2020_config = { ++ .tuner_address = 0x60, ++ .clk_out_div = 1, ++}; ++ ++static struct cx24116_config dvbworld_cx24116_config = { ++ .demod_address = 0x05, ++}; ++ ++static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = { ++ .prod = LGS8GXX_PROD_LGS8GL5, ++ .demod_address = 0x19, ++ .serial_ts = 0, ++ .ts_clk_pol = 1, ++ .ts_clk_gated = 1, ++ .if_clk_freq = 30400, /* 30.4 MHz */ ++ .if_freq = 5380, /* 5.38 MHz */ ++ .if_neg_center = 1, ++ .ext_adc = 0, ++ .adc_signed = 0, ++ .if_neg_edge = 0, ++}; ++ ++static struct xc5000_config mygica_x8506_xc5000_config = { ++ .i2c_address = 0x61, ++ .if_khz = 5380, ++}; ++ ++static struct stv090x_config prof_8000_stv090x_config = { ++ .device = STV0903, ++ .demod_mode = STV090x_SINGLE, ++ .clk_mode = STV090x_CLK_EXT, ++ .xtal = 27000000, ++ .address = 0x6A, ++ .ts1_mode = STV090x_TSMODE_PARALLEL_PUNCTURED, ++ .repeater_level = STV090x_RPTLEVEL_64, ++ .adc1_range = STV090x_ADC_2Vpp, ++ .diseqc_envelope_mode = false, ++ ++ .tuner_get_frequency = stb6100_get_frequency, ++ .tuner_set_frequency = stb6100_set_frequency, ++ .tuner_set_bandwidth = stb6100_set_bandwidth, ++ .tuner_get_bandwidth = stb6100_get_bandwidth, ++}; ++ ++static struct stb6100_config prof_8000_stb6100_config = { ++ .tuner_address = 0x60, ++ .refclock = 27000000, ++}; ++ ++static int p8000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct cx23885_tsport *port = fe->dvb->priv; ++ struct cx23885_dev *dev = port->dev; ++ ++ if (voltage == SEC_VOLTAGE_18) ++ cx_write(MC417_RWD, 0x00001e00); ++ else if (voltage == SEC_VOLTAGE_13) ++ cx_write(MC417_RWD, 0x00001a00); ++ else ++ cx_write(MC417_RWD, 0x00001800); ++ return 0; ++} ++ ++static int cx23885_dvb_set_frontend(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct cx23885_tsport *port = fe->dvb->priv; ++ struct cx23885_dev *dev = port->dev; ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ switch (p->modulation) { ++ case VSB_8: ++ cx23885_gpio_clear(dev, GPIO_5); ++ break; ++ case QAM_64: ++ case QAM_256: ++ default: ++ cx23885_gpio_set(dev, GPIO_5); ++ break; ++ } ++ break; ++ case CX23885_BOARD_MYGICA_X8506: ++ case CX23885_BOARD_MAGICPRO_PROHDTVE2: ++ /* Select Digital TV */ ++ cx23885_gpio_set(dev, GPIO_0); ++ break; ++ } ++ return 0; ++} ++ ++static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { ++ .prod = LGS8GXX_PROD_LGS8G75, ++ .demod_address = 0x19, ++ .serial_ts = 0, ++ .ts_clk_pol = 1, ++ .ts_clk_gated = 1, ++ .if_clk_freq = 30400, /* 30.4 MHz */ ++ .if_freq = 6500, /* 6.50 MHz */ ++ .if_neg_center = 1, ++ .ext_adc = 0, ++ .adc_signed = 1, ++ .adc_vpp = 2, /* 1.6 Vpp */ ++ .if_neg_edge = 1, ++}; ++ ++static struct xc5000_config magicpro_prohdtve2_xc5000_config = { ++ .i2c_address = 0x61, ++ .if_khz = 6500, ++}; ++ ++static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = { ++ .prod = ATBM8830_PROD_8830, ++ .demod_address = 0x44, ++ .serial_ts = 0, ++ .ts_sampling_edge = 1, ++ .ts_clk_gated = 0, ++ .osc_clk_freq = 30400, /* in kHz */ ++ .if_freq = 0, /* zero IF */ ++ .zif_swap_iq = 1, ++ .agc_min = 0x2E, ++ .agc_max = 0xFF, ++ .agc_hold_loop = 0, ++}; ++ ++static struct max2165_config mygic_x8558pro_max2165_cfg1 = { ++ .i2c_address = 0x60, ++ .osc_clk = 20 ++}; ++ ++static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = { ++ .prod = ATBM8830_PROD_8830, ++ .demod_address = 0x44, ++ .serial_ts = 1, ++ .ts_sampling_edge = 1, ++ .ts_clk_gated = 0, ++ .osc_clk_freq = 30400, /* in kHz */ ++ .if_freq = 0, /* zero IF */ ++ .zif_swap_iq = 1, ++ .agc_min = 0x2E, ++ .agc_max = 0xFF, ++ .agc_hold_loop = 0, ++}; ++ ++static struct max2165_config mygic_x8558pro_max2165_cfg2 = { ++ .i2c_address = 0x60, ++ .osc_clk = 20 ++}; ++static struct stv0367_config netup_stv0367_config[] = { ++ { ++ .demod_address = 0x1c, ++ .xtal = 27000000, ++ .if_khz = 4500, ++ .if_iq_mode = 0, ++ .ts_mode = 1, ++ .clk_pol = 0, ++ }, { ++ .demod_address = 0x1d, ++ .xtal = 27000000, ++ .if_khz = 4500, ++ .if_iq_mode = 0, ++ .ts_mode = 1, ++ .clk_pol = 0, ++ }, ++}; ++ ++static struct xc5000_config netup_xc5000_config[] = { ++ { ++ .i2c_address = 0x61, ++ .if_khz = 4500, ++ }, { ++ .i2c_address = 0x64, ++ .if_khz = 4500, ++ }, ++}; ++ ++static struct drxk_config terratec_drxk_config[] = { ++ { ++ .adr = 0x29, ++ .no_i2c_bridge = 1, ++ }, { ++ .adr = 0x2a, ++ .no_i2c_bridge = 1, ++ }, ++}; ++ ++static struct mt2063_config terratec_mt2063_config[] = { ++ { ++ .tuner_address = 0x60, ++ }, { ++ .tuner_address = 0x67, ++ }, ++}; ++ ++static const struct tda10071_config hauppauge_tda10071_config = { ++ .demod_i2c_addr = 0x05, ++ .tuner_i2c_addr = 0x54, ++ .i2c_wr_max = 64, ++ .ts_mode = TDA10071_TS_SERIAL, ++ .spec_inv = 0, ++ .xtal = 40444000, /* 40.444 MHz */ ++ .pll_multiplier = 20, ++}; ++ ++static const struct a8293_config hauppauge_a8293_config = { ++ .i2c_addr = 0x0b, ++}; ++ ++static int netup_altera_fpga_rw(void *device, int flag, int data, int read) ++{ ++ struct cx23885_dev *dev = (struct cx23885_dev *)device; ++ unsigned long timeout = jiffies + msecs_to_jiffies(1); ++ uint32_t mem = 0; ++ ++ mem = cx_read(MC417_RWD); ++ if (read) ++ cx_set(MC417_OEN, ALT_DATA); ++ else { ++ cx_clear(MC417_OEN, ALT_DATA);/* D0-D7 out */ ++ mem &= ~ALT_DATA; ++ mem |= (data & ALT_DATA); ++ } ++ ++ if (flag) ++ mem |= ALT_AD_RG; ++ else ++ mem &= ~ALT_AD_RG; ++ ++ mem &= ~ALT_CS; ++ if (read) ++ mem = (mem & ~ALT_RD) | ALT_WR; ++ else ++ mem = (mem & ~ALT_WR) | ALT_RD; ++ ++ cx_write(MC417_RWD, mem); /* start RW cycle */ ++ ++ for (;;) { ++ mem = cx_read(MC417_RWD); ++ if ((mem & ALT_RDY) == 0) ++ break; ++ if (time_after(jiffies, timeout)) ++ break; ++ udelay(1); ++ } ++ ++ cx_set(MC417_RWD, ALT_RD | ALT_WR | ALT_CS); ++ if (read) ++ return mem & ALT_DATA; ++ ++ return 0; ++}; ++ ++static int dvb_register(struct cx23885_tsport *port) ++{ ++ struct cx23885_dev *dev = port->dev; ++ struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; ++ struct videobuf_dvb_frontend *fe0, *fe1 = NULL; ++ int mfe_shared = 0; /* bus not shared by default */ ++ int ret; ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); ++ if (!fe0) ++ return -EINVAL; ++ ++ /* init struct videobuf_dvb */ ++ fe0->dvb.name = dev->name; ++ ++ /* multi-frontend gate control is undefined or defaults to fe0 */ ++ port->frontends.gate = 0; ++ ++ /* Sets the gate control callback to be used by i2c command calls */ ++ port->gate_ctrl = cx23885_dvb_gate_ctrl; ++ ++ /* init frontend */ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &hauppauge_generic_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(mt2131_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &hauppauge_generic_tunerconfig, 0); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1275: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(lgdt3305_attach, ++ &hauppauge_lgdt3305_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_hvr127x_config); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &hcw_s5h1411_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_tda18271_config); ++ } ++ ++ tda18271_attach(&dev->ts1.analog_fe, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_tda18271_config); ++ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1800: ++ i2c_bus = &dev->i2c_bus[0]; ++ switch (alt_tuner) { ++ case 1: ++ fe0->dvb.frontend = ++ dvb_attach(s5h1409_attach, ++ &hauppauge_ezqam_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_bus[1].i2c_adap, 0x42, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_tda18271_config); ++ } ++ break; ++ case 0: ++ default: ++ fe0->dvb.frontend = ++ dvb_attach(s5h1409_attach, ++ &hauppauge_generic_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ dvb_attach(mt2131_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &hauppauge_generic_tunerconfig, 0); ++ break; ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1800lp: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &hauppauge_hvr1800lp_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(mt2131_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &hauppauge_generic_tunerconfig, 0); ++ } ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(lgdt330x_attach, ++ &fusionhdtv_5_express, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, 0x61, ++ TUNER_LG_TDVS_H06XF); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1500Q: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &hauppauge_hvr1500q_config, ++ &dev->i2c_bus[0].i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &hauppauge_hvr1500q_tunerconfig); ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1500: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &hauppauge_hvr1500_config, ++ &dev->i2c_bus[0].i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &i2c_bus->i2c_adap, ++ .i2c_addr = 0x61, ++ }; ++ static struct xc2028_ctrl ctl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_OREN538, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, ++ fe0->dvb.frontend, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctl); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1200: ++ case CX23885_BOARD_HAUPPAUGE_HVR1700: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(tda10048_attach, ++ &hauppauge_hvr1200_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_bus[1].i2c_adap, 0x42, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_hvr1200_tuner_config); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1210: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(tda10048_attach, ++ &hauppauge_hvr1210_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_hvr1210_tuner_config); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1400: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(dib7000p_attach, ++ &i2c_bus->i2c_adap, ++ 0x12, &hauppauge_hvr1400_dib7000_config); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &dev->i2c_bus[1].i2c_adap, ++ .i2c_addr = 0x64, ++ }; ++ static struct xc2028_ctrl ctl = { ++ .fname = XC3028L_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_DIBCOM52, ++ /* This is true for all demods with ++ v36 firmware? */ ++ .type = XC2028_D2633, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, ++ fe0->dvb.frontend, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctl); ++ } ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP: ++ i2c_bus = &dev->i2c_bus[port->nr - 1]; ++ ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &dvico_s5h1409_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend == NULL) ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &dvico_s5h1411_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &dvico_xc5000_tunerconfig); ++ break; ++ case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: { ++ i2c_bus = &dev->i2c_bus[port->nr - 1]; ++ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_xc3028, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &i2c_bus->i2c_adap, ++ .i2c_addr = 0x61, ++ }; ++ static struct xc2028_ctrl ctl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_ZARLINK456, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, ++ &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctl); ++ } ++ break; ++ } ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: ++ case CX23885_BOARD_COMPRO_VIDEOMATE_E800: ++ i2c_bus = &dev->i2c_bus[0]; ++ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_xc3028, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &dev->i2c_bus[1].i2c_adap, ++ .i2c_addr = 0x61, ++ }; ++ static struct xc2028_ctrl ctl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .demod = XC3028_FE_ZARLINK456, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, ++ &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctl); ++ } ++ break; ++ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000: ++ i2c_bus = &dev->i2c_bus[0]; ++ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_xc3028, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc4000_config cfg = { ++ .i2c_address = 0x61, ++ .default_pm = 0, ++ .dvb_amplitude = 134, ++ .set_smoothedcvbs = 1, ++ .if_khz = 4560 ++ }; ++ ++ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, ++ &dev->i2c_bus[1].i2c_adap, &cfg); ++ if (!fe) { ++ printk(KERN_ERR "%s/2: xc4000 attach failed\n", ++ dev->name); ++ goto frontend_detach; ++ } ++ } ++ break; ++ case CX23885_BOARD_TBS_6920: ++ i2c_bus = &dev->i2c_bus[1]; ++ ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &tbs_cx24116_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; ++ ++ break; ++ case CX23885_BOARD_TEVII_S470: ++ i2c_bus = &dev->i2c_bus[1]; ++ ++ fe0->dvb.frontend = dvb_attach(ds3000_attach, ++ &tevii_ds3000_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(ts2020_attach, fe0->dvb.frontend, ++ &tevii_ts2020_config, &i2c_bus->i2c_adap); ++ fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; ++ } ++ ++ break; ++ case CX23885_BOARD_DVBWORLD_2005: ++ i2c_bus = &dev->i2c_bus[1]; ++ ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &dvbworld_cx24116_config, ++ &i2c_bus->i2c_adap); ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ i2c_bus = &dev->i2c_bus[0]; ++ switch (port->nr) { ++ /* port B */ ++ case 1: ++ fe0->dvb.frontend = dvb_attach(stv0900_attach, ++ &netup_stv0900_config, ++ &i2c_bus->i2c_adap, 0); ++ if (fe0->dvb.frontend != NULL) { ++ if (dvb_attach(stv6110_attach, ++ fe0->dvb.frontend, ++ &netup_stv6110_tunerconfig_a, ++ &i2c_bus->i2c_adap)) { ++ if (!dvb_attach(lnbh24_attach, ++ fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ LNBH24_PCL | LNBH24_TTX, ++ LNBH24_TEN, 0x09)) ++ printk(KERN_ERR ++ "No LNBH24 found!\n"); ++ ++ } ++ } ++ break; ++ /* port C */ ++ case 2: ++ fe0->dvb.frontend = dvb_attach(stv0900_attach, ++ &netup_stv0900_config, ++ &i2c_bus->i2c_adap, 1); ++ if (fe0->dvb.frontend != NULL) { ++ if (dvb_attach(stv6110_attach, ++ fe0->dvb.frontend, ++ &netup_stv6110_tunerconfig_b, ++ &i2c_bus->i2c_adap)) { ++ if (!dvb_attach(lnbh24_attach, ++ fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ LNBH24_PCL | LNBH24_TTX, ++ LNBH24_TEN, 0x0a)) ++ printk(KERN_ERR ++ "No LNBH24 found!\n"); ++ ++ } ++ } ++ break; ++ } ++ break; ++ case CX23885_BOARD_MYGICA_X8506: ++ i2c_bus = &dev->i2c_bus[0]; ++ i2c_bus2 = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, ++ &mygica_x8506_lgs8gl5_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(xc5000_attach, ++ fe0->dvb.frontend, ++ &i2c_bus2->i2c_adap, ++ &mygica_x8506_xc5000_config); ++ } ++ break; ++ case CX23885_BOARD_MAGICPRO_PROHDTVE2: ++ i2c_bus = &dev->i2c_bus[0]; ++ i2c_bus2 = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, ++ &magicpro_prohdtve2_lgs8g75_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(xc5000_attach, ++ fe0->dvb.frontend, ++ &i2c_bus2->i2c_adap, ++ &magicpro_prohdtve2_xc5000_config); ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &hcw_s5h1411_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[0].i2c_adap, ++ &hauppauge_tda18271_config); ++ ++ tda18271_attach(&dev->ts1.analog_fe, ++ 0x60, &dev->i2c_bus[1].i2c_adap, ++ &hauppauge_tda18271_config); ++ ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &hcw_s5h1411_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_bus[0].i2c_adap, ++ &hauppauge_tda18271_config); ++ break; ++ case CX23885_BOARD_MYGICA_X8558PRO: ++ switch (port->nr) { ++ /* port B */ ++ case 1: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(atbm8830_attach, ++ &mygica_x8558pro_atbm8830_cfg1, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(max2165_attach, ++ fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &mygic_x8558pro_max2165_cfg1); ++ } ++ break; ++ /* port C */ ++ case 2: ++ i2c_bus = &dev->i2c_bus[1]; ++ fe0->dvb.frontend = dvb_attach(atbm8830_attach, ++ &mygica_x8558pro_atbm8830_cfg2, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(max2165_attach, ++ fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &mygic_x8558pro_max2165_cfg2); ++ } ++ break; ++ } ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ i2c_bus = &dev->i2c_bus[0]; ++ mfe_shared = 1;/* MFE */ ++ port->frontends.gate = 0;/* not clear for me yet */ ++ /* ports B, C */ ++ /* MFE frontend 1 DVB-T */ ++ fe0->dvb.frontend = dvb_attach(stv0367ter_attach, ++ &netup_stv0367_config[port->nr - 1], ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (NULL == dvb_attach(xc5000_attach, ++ fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &netup_xc5000_config[port->nr - 1])) ++ goto frontend_detach; ++ /* load xc5000 firmware */ ++ fe0->dvb.frontend->ops.tuner_ops.init(fe0->dvb.frontend); ++ } ++ /* MFE frontend 2 */ ++ fe1 = videobuf_dvb_get_frontend(&port->frontends, 2); ++ if (fe1 == NULL) ++ goto frontend_detach; ++ /* DVB-C init */ ++ fe1->dvb.frontend = dvb_attach(stv0367cab_attach, ++ &netup_stv0367_config[port->nr - 1], ++ &i2c_bus->i2c_adap); ++ if (fe1->dvb.frontend != NULL) { ++ fe1->dvb.frontend->id = 1; ++ if (NULL == dvb_attach(xc5000_attach, ++ fe1->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &netup_xc5000_config[port->nr - 1])) ++ goto frontend_detach; ++ } ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ i2c_bus = &dev->i2c_bus[0]; ++ i2c_bus2 = &dev->i2c_bus[1]; ++ ++ switch (port->nr) { ++ /* port b */ ++ case 1: ++ fe0->dvb.frontend = dvb_attach(drxk_attach, ++ &terratec_drxk_config[0], ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(mt2063_attach, ++ fe0->dvb.frontend, ++ &terratec_mt2063_config[0], ++ &i2c_bus2->i2c_adap)) ++ goto frontend_detach; ++ } ++ break; ++ /* port c */ ++ case 2: ++ fe0->dvb.frontend = dvb_attach(drxk_attach, ++ &terratec_drxk_config[1], ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(mt2063_attach, ++ fe0->dvb.frontend, ++ &terratec_mt2063_config[1], ++ &i2c_bus2->i2c_adap)) ++ goto frontend_detach; ++ } ++ break; ++ } ++ break; ++ case CX23885_BOARD_TEVII_S471: ++ i2c_bus = &dev->i2c_bus[1]; ++ ++ fe0->dvb.frontend = dvb_attach(ds3000_attach, ++ &tevii_ds3000_config, ++ &i2c_bus->i2c_adap); ++ break; ++ case CX23885_BOARD_PROF_8000: ++ i2c_bus = &dev->i2c_bus[0]; ++ ++ fe0->dvb.frontend = dvb_attach(stv090x_attach, ++ &prof_8000_stv090x_config, ++ &i2c_bus->i2c_adap, ++ STV090x_DEMODULATOR_0); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(stb6100_attach, ++ fe0->dvb.frontend, ++ &prof_8000_stb6100_config, ++ &i2c_bus->i2c_adap)) ++ goto frontend_detach; ++ ++ fe0->dvb.frontend->ops.set_voltage = p8000_set_voltage; ++ } ++ break; ++ case CX23885_BOARD_HAUPPAUGE_HVR4400: ++ i2c_bus = &dev->i2c_bus[0]; ++ fe0->dvb.frontend = dvb_attach(tda10071_attach, ++ &hauppauge_tda10071_config, ++ &i2c_bus->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(a8293_attach, fe0->dvb.frontend, ++ &i2c_bus->i2c_adap, ++ &hauppauge_a8293_config); ++ } ++ break; ++ default: ++ printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " ++ " isn't supported yet\n", ++ dev->name); ++ break; ++ } ++ ++ if ((NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend)) { ++ printk(KERN_ERR "%s: frontend initialization failed\n", ++ dev->name); ++ goto frontend_detach; ++ } ++ ++ /* define general-purpose callback pointer */ ++ fe0->dvb.frontend->callback = cx23885_tuner_callback; ++ if (fe1) ++ fe1->dvb.frontend->callback = cx23885_tuner_callback; ++#if 0 ++ /* Ensure all frontends negotiate bus access */ ++ fe0->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl; ++ if (fe1) ++ fe1->dvb.frontend->ops.ts_bus_ctrl = cx23885_dvb_bus_ctrl; ++#endif ++ ++ /* Put the analog decoder in standby to keep it quiet */ ++ call_all(dev, core, s_power, 0); ++ ++ if (fe0->dvb.frontend->ops.analog_ops.standby) ++ fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); ++ ++ /* register everything */ ++ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, ++ &dev->pci->dev, adapter_nr, mfe_shared); ++ if (ret) ++ goto frontend_detach; ++ ++ /* init CI & MAC */ ++ switch (dev->board) { ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: { ++ static struct netup_card_info cinfo; ++ ++ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); ++ memcpy(port->frontends.adapter.proposed_mac, ++ cinfo.port[port->nr - 1].mac, 6); ++ printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n", ++ port->nr, port->frontends.adapter.proposed_mac); ++ ++ netup_ci_init(port); ++ break; ++ } ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { ++ struct altera_ci_config netup_ci_cfg = { ++ .dev = dev,/* magic number to identify*/ ++ .adapter = &port->frontends.adapter,/* for CI */ ++ .demux = &fe0->dvb.demux,/* for hw pid filter */ ++ .fpga_rw = netup_altera_fpga_rw, ++ }; ++ ++ altera_ci_init(&netup_ci_cfg, port->nr); ++ break; ++ } ++ case CX23885_BOARD_TEVII_S470: { ++ u8 eeprom[256]; /* 24C02 i2c eeprom */ ++ ++ if (port->nr != 1) ++ break; ++ ++ /* Read entire EEPROM */ ++ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); ++ printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0); ++ memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); ++ break; ++ } ++ } ++ ++ return ret; ++ ++frontend_detach: ++ port->gate_ctrl = NULL; ++ videobuf_dvb_dealloc_frontends(&port->frontends); ++ return -EINVAL; ++} ++ ++int cx23885_dvb_register(struct cx23885_tsport *port) ++{ ++ ++ struct videobuf_dvb_frontend *fe0; ++ struct cx23885_dev *dev = port->dev; ++ int err, i; ++ ++ /* Here we need to allocate the correct number of frontends, ++ * as reflected in the cards struct. The reality is that currently ++ * no cx23885 boards support this - yet. But, if we don't modify this ++ * code then the second frontend would never be allocated (later) ++ * and fail with error before the attach in dvb_register(). ++ * Without these changes we risk an OOPS later. The changes here ++ * are for safety, and should provide a good foundation for the ++ * future addition of any multi-frontend cx23885 based boards. ++ */ ++ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, ++ port->num_frontends); ++ ++ for (i = 1; i <= port->num_frontends; i++) { ++ if (videobuf_dvb_alloc_frontend( ++ &port->frontends, i) == NULL) { ++ printk(KERN_ERR "%s() failed to alloc\n", __func__); ++ return -ENOMEM; ++ } ++ ++ fe0 = videobuf_dvb_get_frontend(&port->frontends, i); ++ if (!fe0) ++ err = -EINVAL; ++ ++ dprintk(1, "%s\n", __func__); ++ dprintk(1, " ->probed by Card=%d Name=%s, PCI %02x:%02x\n", ++ dev->board, ++ dev->name, ++ dev->pci_bus, ++ dev->pci_slot); ++ ++ err = -ENODEV; ++ ++ /* dvb stuff */ ++ /* We have to init the queue for each frontend on a port. */ ++ printk(KERN_INFO "%s: cx23885 based dvb card\n", dev->name); ++ videobuf_queue_sg_init(&fe0->dvb.dvbq, &dvb_qops, ++ &dev->pci->dev, &port->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, ++ sizeof(struct cx23885_buffer), port, NULL); ++ } ++ err = dvb_register(port); ++ if (err != 0) ++ printk(KERN_ERR "%s() dvb_register failed err = %d\n", ++ __func__, err); ++ ++ return err; ++} ++ ++int cx23885_dvb_unregister(struct cx23885_tsport *port) ++{ ++ struct videobuf_dvb_frontend *fe0; ++ ++ /* FIXME: in an error condition where the we have ++ * an expected number of frontends (attach problem) ++ * then this might not clean up correctly, if 1 ++ * is invalid. ++ * This comment only applies to future boards IF they ++ * implement MFE support. ++ */ ++ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); ++ if (fe0 && fe0->dvb.frontend) ++ videobuf_dvb_unregister_bus(&port->frontends); ++ ++ switch (port->dev->board) { ++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: ++ netup_ci_exit(port); ++ break; ++ case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: ++ altera_ci_release(port->dev, port->nr); ++ break; ++ } ++ ++ port->gate_ctrl = NULL; ++ ++ return 0; ++} ++ +diff --git a/drivers/media/pci/cx23885/cx23885-f300.c b/drivers/media/pci/cx23885/cx23885-f300.c +new file mode 100644 +index 0000000..5444cc5 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-f300.c +@@ -0,0 +1,178 @@ ++/* ++ * Driver for Silicon Labs C8051F300 microcontroller. ++ * ++ * It is used for LNB power control in TeVii S470, ++ * TBS 6920 PCIe DVB-S2 cards. ++ * ++ * Microcontroller connected to cx23885 GPIO pins: ++ * GPIO0 - data - P0.3 F300 ++ * GPIO1 - reset - P0.2 F300 ++ * GPIO2 - clk - P0.1 F300 ++ * GPIO3 - busy - P0.0 F300 ++ * ++ * Copyright (C) 2009 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "cx23885.h" ++#include "cx23885-f300.h" ++ ++#define F300_DATA GPIO_0 ++#define F300_RESET GPIO_1 ++#define F300_CLK GPIO_2 ++#define F300_BUSY GPIO_3 ++ ++static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl) ++{ ++ cx23885_gpio_enable(dev, line, 1); ++ if (lvl == 1) ++ cx23885_gpio_set(dev, line); ++ else ++ cx23885_gpio_clear(dev, line); ++} ++ ++static u8 f300_get_line(struct cx23885_dev *dev, u32 line) ++{ ++ cx23885_gpio_enable(dev, line, 0); ++ ++ return cx23885_gpio_get(dev, line); ++} ++ ++static void f300_send_byte(struct cx23885_dev *dev, u8 dta) ++{ ++ u8 i; ++ ++ for (i = 0; i < 8; i++) { ++ f300_set_line(dev, F300_CLK, 0); ++ udelay(30); ++ f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */ ++ udelay(30); ++ dta <<= 1; ++ f300_set_line(dev, F300_CLK, 1); ++ udelay(30); ++ } ++} ++ ++static u8 f300_get_byte(struct cx23885_dev *dev) ++{ ++ u8 i, dta = 0; ++ ++ for (i = 0; i < 8; i++) { ++ f300_set_line(dev, F300_CLK, 0); ++ udelay(30); ++ dta <<= 1; ++ f300_set_line(dev, F300_CLK, 1); ++ udelay(30); ++ dta |= f300_get_line(dev, F300_DATA);/* msb first */ ++ ++ } ++ ++ return dta; ++} ++ ++static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) ++{ ++ struct cx23885_tsport *port = fe->dvb->priv; ++ struct cx23885_dev *dev = port->dev; ++ u8 i, temp, ret = 0; ++ ++ temp = buf[0]; ++ for (i = 0; i < buf[0]; i++) ++ temp += buf[i + 1]; ++ temp = (~temp + 1);/* get check sum */ ++ buf[1 + buf[0]] = temp; ++ ++ f300_set_line(dev, F300_RESET, 1); ++ f300_set_line(dev, F300_CLK, 1); ++ udelay(30); ++ f300_set_line(dev, F300_DATA, 1); ++ msleep(1); ++ ++ /* question: */ ++ f300_set_line(dev, F300_RESET, 0);/* begin to send data */ ++ msleep(1); ++ ++ f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */ ++ msleep(1); ++ ++ temp = buf[0]; ++ temp += 2; ++ for (i = 0; i < temp; i++) ++ f300_send_byte(dev, buf[i]); ++ ++ f300_set_line(dev, F300_RESET, 1);/* sent data over */ ++ f300_set_line(dev, F300_DATA, 1); ++ ++ /* answer: */ ++ temp = 0; ++ for (i = 0; ((i < 8) & (temp == 0)); i++) { ++ msleep(1); ++ if (f300_get_line(dev, F300_BUSY) == 0) ++ temp = 1; ++ } ++ ++ if (i > 7) { ++ printk(KERN_ERR "%s: timeout, the slave no response\n", ++ __func__); ++ ret = 1; /* timeout, the slave no response */ ++ } else { /* the slave not busy, prepare for getting data */ ++ f300_set_line(dev, F300_RESET, 0);/*ready...*/ ++ msleep(1); ++ f300_send_byte(dev, 0xe1);/* 0xe1 is Read */ ++ msleep(1); ++ temp = f300_get_byte(dev);/*get the data length */ ++ if (temp > 14) ++ temp = 14; ++ ++ for (i = 0; i < (temp + 1); i++) ++ f300_get_byte(dev);/* get data to empty buffer */ ++ ++ f300_set_line(dev, F300_RESET, 1);/* received data over */ ++ f300_set_line(dev, F300_DATA, 1); ++ } ++ ++ return ret; ++} ++ ++int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ u8 buf[16]; ++ ++ buf[0] = 0x05; ++ buf[1] = 0x38;/* write port */ ++ buf[2] = 0x01;/* A port, lnb power */ ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ buf[3] = 0x01;/* power on */ ++ buf[4] = 0x02;/* B port, H/V */ ++ buf[5] = 0x00;/*13V v*/ ++ break; ++ case SEC_VOLTAGE_18: ++ buf[3] = 0x01; ++ buf[4] = 0x02; ++ buf[5] = 0x01;/* 18V h*/ ++ break; ++ case SEC_VOLTAGE_OFF: ++ buf[3] = 0x00;/* power off */ ++ buf[4] = 0x00; ++ buf[5] = 0x00; ++ break; ++ } ++ ++ return f300_xfer(fe, buf); ++} +diff --git a/drivers/media/pci/cx23885/cx23885-f300.h b/drivers/media/pci/cx23885/cx23885-f300.h +new file mode 100644 +index 0000000..e73344c +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-f300.h +@@ -0,0 +1,2 @@ ++extern int f300_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage); +diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c +new file mode 100644 +index 0000000..4887314 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-i2c.c +@@ -0,0 +1,396 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++ ++#include ++ ++static unsigned int i2c_debug; ++module_param(i2c_debug, int, 0644); ++MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); ++ ++static unsigned int i2c_scan; ++module_param(i2c_scan, int, 0444); ++MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (i2c_debug >= level)\ ++ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++#define I2C_WAIT_DELAY 32 ++#define I2C_WAIT_RETRY 64 ++ ++#define I2C_EXTEND (1 << 3) ++#define I2C_NOSTOP (1 << 4) ++ ++static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) ++{ ++ struct cx23885_i2c *bus = i2c_adap->algo_data; ++ struct cx23885_dev *dev = bus->dev; ++ return cx_read(bus->reg_stat) & 0x01; ++} ++ ++static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) ++{ ++ struct cx23885_i2c *bus = i2c_adap->algo_data; ++ struct cx23885_dev *dev = bus->dev; ++ return cx_read(bus->reg_stat) & 0x02 ? 1 : 0; ++} ++ ++static int i2c_wait_done(struct i2c_adapter *i2c_adap) ++{ ++ int count; ++ ++ for (count = 0; count < I2C_WAIT_RETRY; count++) { ++ if (!i2c_is_busy(i2c_adap)) ++ break; ++ udelay(I2C_WAIT_DELAY); ++ } ++ ++ if (I2C_WAIT_RETRY == count) ++ return 0; ++ ++ return 1; ++} ++ ++static int i2c_sendbytes(struct i2c_adapter *i2c_adap, ++ const struct i2c_msg *msg, int joined_rlen) ++{ ++ struct cx23885_i2c *bus = i2c_adap->algo_data; ++ struct cx23885_dev *dev = bus->dev; ++ u32 wdata, addr, ctrl; ++ int retval, cnt; ++ ++ if (joined_rlen) ++ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, ++ msg->len, joined_rlen); ++ else ++ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); ++ ++ /* Deal with i2c probe functions with zero payload */ ++ if (msg->len == 0) { ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2)); ++ if (!i2c_wait_done(i2c_adap)) ++ return -EIO; ++ if (!i2c_slave_did_ack(i2c_adap)) ++ return -ENXIO; ++ ++ dprintk(1, "%s() returns 0\n", __func__); ++ return 0; ++ } ++ ++ ++ /* dev, reg + first byte */ ++ addr = (msg->addr << 25) | msg->buf[0]; ++ wdata = msg->buf[0]; ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2); ++ ++ if (msg->len > 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ else if (joined_rlen) ++ ctrl |= I2C_NOSTOP; ++ ++ cx_write(bus->reg_addr, addr); ++ cx_write(bus->reg_wdata, wdata); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ if (!i2c_wait_done(i2c_adap)) ++ goto eio; ++ if (i2c_debug) { ++ printk(" addr << 1, msg->buf[0]); ++ if (!(ctrl & I2C_NOSTOP)) ++ printk(" >\n"); ++ } ++ ++ for (cnt = 1; cnt < msg->len; cnt++) { ++ /* following bytes */ ++ wdata = msg->buf[cnt]; ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2); ++ ++ if (cnt < msg->len - 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ else if (joined_rlen) ++ ctrl |= I2C_NOSTOP; ++ ++ cx_write(bus->reg_addr, addr); ++ cx_write(bus->reg_wdata, wdata); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ if (!i2c_wait_done(i2c_adap)) ++ goto eio; ++ if (i2c_debug) { ++ dprintk(1, " %02x", msg->buf[cnt]); ++ if (!(ctrl & I2C_NOSTOP)) ++ dprintk(1, " >\n"); ++ } ++ } ++ return msg->len; ++ ++ eio: ++ retval = -EIO; ++ if (i2c_debug) ++ printk(KERN_ERR " ERR: %d\n", retval); ++ return retval; ++} ++ ++static int i2c_readbytes(struct i2c_adapter *i2c_adap, ++ const struct i2c_msg *msg, int joined) ++{ ++ struct cx23885_i2c *bus = i2c_adap->algo_data; ++ struct cx23885_dev *dev = bus->dev; ++ u32 ctrl, cnt; ++ int retval; ++ ++ ++ if (i2c_debug && !joined) ++ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); ++ ++ /* Deal with i2c probe functions with zero payload */ ++ if (msg->len == 0) { ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1); ++ if (!i2c_wait_done(i2c_adap)) ++ return -EIO; ++ if (!i2c_slave_did_ack(i2c_adap)) ++ return -ENXIO; ++ ++ ++ dprintk(1, "%s() returns 0\n", __func__); ++ return 0; ++ } ++ ++ if (i2c_debug) { ++ if (joined) ++ dprintk(1, " R"); ++ else ++ dprintk(1, " addr << 1) + 1); ++ } ++ ++ for (cnt = 0; cnt < msg->len; cnt++) { ++ ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1; ++ ++ if (cnt < msg->len - 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ if (!i2c_wait_done(i2c_adap)) ++ goto eio; ++ msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff; ++ if (i2c_debug) { ++ dprintk(1, " %02x", msg->buf[cnt]); ++ if (!(ctrl & I2C_NOSTOP)) ++ dprintk(1, " >\n"); ++ } ++ } ++ return msg->len; ++ ++ eio: ++ retval = -EIO; ++ if (i2c_debug) ++ printk(KERN_ERR " ERR: %d\n", retval); ++ return retval; ++} ++ ++static int i2c_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct cx23885_i2c *bus = i2c_adap->algo_data; ++ struct cx23885_dev *dev = bus->dev; ++ int i, retval = 0; ++ ++ dprintk(1, "%s(num = %d)\n", __func__, num); ++ ++ for (i = 0 ; i < num; i++) { ++ dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n", ++ __func__, num, msgs[i].addr, msgs[i].len); ++ if (msgs[i].flags & I2C_M_RD) { ++ /* read */ ++ retval = i2c_readbytes(i2c_adap, &msgs[i], 0); ++ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && ++ msgs[i].addr == msgs[i + 1].addr) { ++ /* write then read from same address */ ++ retval = i2c_sendbytes(i2c_adap, &msgs[i], ++ msgs[i + 1].len); ++ if (retval < 0) ++ goto err; ++ i++; ++ retval = i2c_readbytes(i2c_adap, &msgs[i], 1); ++ } else { ++ /* write */ ++ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); ++ } ++ if (retval < 0) ++ goto err; ++ } ++ return num; ++ ++ err: ++ return retval; ++} ++ ++static u32 cx23885_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm cx23885_i2c_algo_template = { ++ .master_xfer = i2c_xfer, ++ .functionality = cx23885_functionality, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct i2c_adapter cx23885_i2c_adap_template = { ++ .name = "cx23885", ++ .owner = THIS_MODULE, ++ .algo = &cx23885_i2c_algo_template, ++}; ++ ++static struct i2c_client cx23885_i2c_client_template = { ++ .name = "cx23885 internal", ++}; ++ ++static char *i2c_devs[128] = { ++ [0x10 >> 1] = "tda10048", ++ [0x12 >> 1] = "dib7000pc", ++ [0x1c >> 1] = "lgdt3303", ++ [0x86 >> 1] = "tda9887", ++ [0x32 >> 1] = "cx24227", ++ [0x88 >> 1] = "cx25837", ++ [0x84 >> 1] = "tda8295", ++ [0x98 >> 1] = "flatiron", ++ [0xa0 >> 1] = "eeprom", ++ [0xc0 >> 1] = "tuner/mt2131/tda8275", ++ [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028", ++ [0xc8 >> 1] = "tuner/xc3028L", ++}; ++ ++static void do_i2c_scan(char *name, struct i2c_client *c) ++{ ++ unsigned char buf; ++ int i, rc; ++ ++ for (i = 0; i < 128; i++) { ++ c->addr = i; ++ rc = i2c_master_recv(c, &buf, 0); ++ if (rc < 0) ++ continue; ++ printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", ++ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); ++ } ++} ++ ++/* init + register i2c adapter */ ++int cx23885_i2c_register(struct cx23885_i2c *bus) ++{ ++ struct cx23885_dev *dev = bus->dev; ++ ++ dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); ++ ++ bus->i2c_adap = cx23885_i2c_adap_template; ++ bus->i2c_client = cx23885_i2c_client_template; ++ bus->i2c_adap.dev.parent = &dev->pci->dev; ++ ++ strlcpy(bus->i2c_adap.name, bus->dev->name, ++ sizeof(bus->i2c_adap.name)); ++ ++ bus->i2c_adap.algo_data = bus; ++ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); ++ i2c_add_adapter(&bus->i2c_adap); ++ ++ bus->i2c_client.adapter = &bus->i2c_adap; ++ ++ if (0 == bus->i2c_rc) { ++ dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr); ++ if (i2c_scan) { ++ printk(KERN_INFO "%s: scan bus %d:\n", ++ dev->name, bus->nr); ++ do_i2c_scan(dev->name, &bus->i2c_client); ++ } ++ } else ++ printk(KERN_WARNING "%s: i2c bus %d register FAILED\n", ++ dev->name, bus->nr); ++ ++ /* Instantiate the IR receiver device, if present */ ++ if (0 == bus->i2c_rc) { ++ struct i2c_board_info info; ++ const unsigned short addr_list[] = { ++ 0x6b, I2C_CLIENT_END ++ }; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "ir_video", I2C_NAME_SIZE); ++ /* Use quick read command for probe, some IR chips don't ++ * support writes */ ++ i2c_new_probed_device(&bus->i2c_adap, &info, addr_list, ++ i2c_probe_func_quick_read); ++ } ++ ++ return bus->i2c_rc; ++} ++ ++int cx23885_i2c_unregister(struct cx23885_i2c *bus) ++{ ++ i2c_del_adapter(&bus->i2c_adap); ++ return 0; ++} ++ ++void cx23885_av_clk(struct cx23885_dev *dev, int enable) ++{ ++ /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ ++ char buffer[3]; ++ struct i2c_msg msg; ++ dprintk(1, "%s(enabled = %d)\n", __func__, enable); ++ ++ /* Register 0x144 */ ++ buffer[0] = 0x01; ++ buffer[1] = 0x44; ++ if (enable == 1) ++ buffer[2] = 0x05; ++ else ++ buffer[2] = 0x00; ++ ++ msg.addr = 0x44; ++ msg.flags = I2C_M_TEN; ++ msg.len = 3; ++ msg.buf = buffer; ++ ++ i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c +new file mode 100644 +index 0000000..7875dfb +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-input.c +@@ -0,0 +1,375 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Infrared remote control input device ++ * ++ * Most of this file is ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * However, the cx23885_input_{init,fini} functions contained herein are ++ * derived from Linux kernel files linux/media/video/.../...-input.c marked as: ++ * ++ * Copyright (C) 2008 ++ * Copyright (C) 2005 Ludovico Cavedon ++ * Markus Rechberger ++ * Mauro Carvalho Chehab ++ * Sascha Sommer ++ * Copyright (C) 2004, 2005 Chris Pascoe ++ * Copyright (C) 2003, 2004 Gerd Knorr ++ * Copyright (C) 2003 Pavel Machek ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include "cx23885-input.h" ++ ++#define MODULE_NAME "cx23885" ++ ++static void cx23885_input_process_measurements(struct cx23885_dev *dev, ++ bool overrun) ++{ ++ struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir; ++ ++ ssize_t num; ++ int count, i; ++ bool handle = false; ++ struct ir_raw_event ir_core_event[64]; ++ ++ do { ++ num = 0; ++ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ir_core_event, ++ sizeof(ir_core_event), &num); ++ ++ count = num / sizeof(struct ir_raw_event); ++ ++ for (i = 0; i < count; i++) { ++ ir_raw_event_store(kernel_ir->rc, ++ &ir_core_event[i]); ++ handle = true; ++ } ++ } while (num != 0); ++ ++ if (overrun) ++ ir_raw_event_reset(kernel_ir->rc); ++ else if (handle) ++ ir_raw_event_handle(kernel_ir->rc); ++} ++ ++void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) ++{ ++ struct v4l2_subdev_ir_parameters params; ++ int overrun, data_available; ++ ++ if (dev->sd_ir == NULL || events == 0) ++ return; ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_TEVII_S470: ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_MYGICA_X8507: ++ /* ++ * The only boards we handle right now. However other boards ++ * using the CX2388x integrated IR controller should be similar ++ */ ++ break; ++ default: ++ return; ++ } ++ ++ overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN | ++ V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN); ++ ++ data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED | ++ V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ); ++ ++ if (overrun) { ++ /* If there was a FIFO overrun, stop the device */ ++ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); ++ params.enable = false; ++ /* Mitigate race with cx23885_input_ir_stop() */ ++ params.shutdown = atomic_read(&dev->ir_input_stopping); ++ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); ++ } ++ ++ if (data_available) ++ cx23885_input_process_measurements(dev, overrun); ++ ++ if (overrun) { ++ /* If there was a FIFO overrun, clear & restart the device */ ++ params.enable = true; ++ /* Mitigate race with cx23885_input_ir_stop() */ ++ params.shutdown = atomic_read(&dev->ir_input_stopping); ++ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); ++ } ++} ++ ++static int cx23885_input_ir_start(struct cx23885_dev *dev) ++{ ++ struct v4l2_subdev_ir_parameters params; ++ ++ if (dev->sd_ir == NULL) ++ return -ENODEV; ++ ++ atomic_set(&dev->ir_input_stopping, 0); ++ ++ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ case CX23885_BOARD_MYGICA_X8507: ++ /* ++ * The IR controller on this board only returns pulse widths. ++ * Any other mode setting will fail to set up the device. ++ */ ++ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ params.enable = true; ++ params.interrupt_enable = true; ++ params.shutdown = false; ++ ++ /* Setup for baseband compatible with both RC-5 and RC-6A */ ++ params.modulation = false; ++ /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/ ++ /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/ ++ params.max_pulse_width = 3333333; /* ns */ ++ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ ++ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ ++ params.noise_filter_min_width = 333333; /* ns */ ++ /* ++ * This board has inverted receive sense: ++ * mark is received as low logic level; ++ * falling edges are detected as rising edges; etc. ++ */ ++ params.invert_level = true; ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ case CX23885_BOARD_TEVII_S470: ++ /* ++ * The IR controller on this board only returns pulse widths. ++ * Any other mode setting will fail to set up the device. ++ */ ++ params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ params.enable = true; ++ params.interrupt_enable = true; ++ params.shutdown = false; ++ ++ /* Setup for a standard NEC protocol */ ++ params.carrier_freq = 37917; /* Hz, 455 kHz/12 for NEC */ ++ params.carrier_range_lower = 33000; /* Hz */ ++ params.carrier_range_upper = 43000; /* Hz */ ++ params.duty_cycle = 33; /* percent, 33 percent for NEC */ ++ ++ /* ++ * NEC max pulse width: (64/3)/(455 kHz/12) * 16 nec_units ++ * (64/3)/(455 kHz/12) * 16 nec_units * 1.375 = 12378022 ns ++ */ ++ params.max_pulse_width = 12378022; /* ns */ ++ ++ /* ++ * NEC noise filter min width: (64/3)/(455 kHz/12) * 1 nec_unit ++ * (64/3)/(455 kHz/12) * 1 nec_units * 0.625 = 351648 ns ++ */ ++ params.noise_filter_min_width = 351648; /* ns */ ++ ++ params.modulation = false; ++ params.invert_level = true; ++ break; ++ } ++ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); ++ return 0; ++} ++ ++static int cx23885_input_ir_open(struct rc_dev *rc) ++{ ++ struct cx23885_kernel_ir *kernel_ir = rc->priv; ++ ++ if (kernel_ir->cx == NULL) ++ return -ENODEV; ++ ++ return cx23885_input_ir_start(kernel_ir->cx); ++} ++ ++static void cx23885_input_ir_stop(struct cx23885_dev *dev) ++{ ++ struct v4l2_subdev_ir_parameters params; ++ ++ if (dev->sd_ir == NULL) ++ return; ++ ++ /* ++ * Stop the sd_ir subdevice from generating notifications and ++ * scheduling work. ++ * It is shutdown this way in order to mitigate a race with ++ * cx23885_input_rx_work_handler() in the overrun case, which could ++ * re-enable the subdevice. ++ */ ++ atomic_set(&dev->ir_input_stopping, 1); ++ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); ++ while (params.shutdown == false) { ++ params.enable = false; ++ params.interrupt_enable = false; ++ params.shutdown = true; ++ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); ++ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); ++ } ++ flush_work(&dev->cx25840_work); ++ flush_work(&dev->ir_rx_work); ++ flush_work(&dev->ir_tx_work); ++} ++ ++static void cx23885_input_ir_close(struct rc_dev *rc) ++{ ++ struct cx23885_kernel_ir *kernel_ir = rc->priv; ++ ++ if (kernel_ir->cx != NULL) ++ cx23885_input_ir_stop(kernel_ir->cx); ++} ++ ++int cx23885_input_init(struct cx23885_dev *dev) ++{ ++ struct cx23885_kernel_ir *kernel_ir; ++ struct rc_dev *rc; ++ char *rc_map; ++ enum rc_driver_type driver_type; ++ unsigned long allowed_protos; ++ ++ int ret; ++ ++ /* ++ * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't ++ * encapsulated in a v4l2_subdev, then I'm not going to deal with it. ++ */ ++ if (dev->sd_ir == NULL) ++ return -ENODEV; ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1270: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ case CX23885_BOARD_HAUPPAUGE_HVR1290: ++ case CX23885_BOARD_HAUPPAUGE_HVR1250: ++ /* Integrated CX2388[58] IR controller */ ++ driver_type = RC_DRIVER_IR_RAW; ++ allowed_protos = RC_BIT_ALL; ++ /* The grey Hauppauge RC-5 remote */ ++ rc_map = RC_MAP_HAUPPAUGE; ++ break; ++ case CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL: ++ /* Integrated CX23885 IR controller */ ++ driver_type = RC_DRIVER_IR_RAW; ++ allowed_protos = RC_BIT_NEC; ++ /* The grey Terratec remote with orange buttons */ ++ rc_map = RC_MAP_NEC_TERRATEC_CINERGY_XS; ++ break; ++ case CX23885_BOARD_TEVII_S470: ++ /* Integrated CX23885 IR controller */ ++ driver_type = RC_DRIVER_IR_RAW; ++ allowed_protos = RC_BIT_ALL; ++ /* A guess at the remote */ ++ rc_map = RC_MAP_TEVII_NEC; ++ break; ++ case CX23885_BOARD_MYGICA_X8507: ++ /* Integrated CX23885 IR controller */ ++ driver_type = RC_DRIVER_IR_RAW; ++ allowed_protos = RC_BIT_ALL; ++ /* A guess at the remote */ ++ rc_map = RC_MAP_TOTAL_MEDIA_IN_HAND_02; ++ break; ++ default: ++ return -ENODEV; ++ } ++ ++ /* cx23885 board instance kernel IR state */ ++ kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL); ++ if (kernel_ir == NULL) ++ return -ENOMEM; ++ ++ kernel_ir->cx = dev; ++ kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)", ++ cx23885_boards[dev->board].name); ++ kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0", ++ pci_name(dev->pci)); ++ ++ /* input device */ ++ rc = rc_allocate_device(); ++ if (!rc) { ++ ret = -ENOMEM; ++ goto err_out_free; ++ } ++ ++ kernel_ir->rc = rc; ++ rc->input_name = kernel_ir->name; ++ rc->input_phys = kernel_ir->phys; ++ rc->input_id.bustype = BUS_PCI; ++ rc->input_id.version = 1; ++ if (dev->pci->subsystem_vendor) { ++ rc->input_id.vendor = dev->pci->subsystem_vendor; ++ rc->input_id.product = dev->pci->subsystem_device; ++ } else { ++ rc->input_id.vendor = dev->pci->vendor; ++ rc->input_id.product = dev->pci->device; ++ } ++ rc->dev.parent = &dev->pci->dev; ++ rc->driver_type = driver_type; ++ rc->allowed_protos = allowed_protos; ++ rc->priv = kernel_ir; ++ rc->open = cx23885_input_ir_open; ++ rc->close = cx23885_input_ir_close; ++ rc->map_name = rc_map; ++ rc->driver_name = MODULE_NAME; ++ ++ /* Go */ ++ dev->kernel_ir = kernel_ir; ++ ret = rc_register_device(rc); ++ if (ret) ++ goto err_out_stop; ++ ++ return 0; ++ ++err_out_stop: ++ cx23885_input_ir_stop(dev); ++ dev->kernel_ir = NULL; ++ rc_free_device(rc); ++err_out_free: ++ kfree(kernel_ir->phys); ++ kfree(kernel_ir->name); ++ kfree(kernel_ir); ++ return ret; ++} ++ ++void cx23885_input_fini(struct cx23885_dev *dev) ++{ ++ /* Always stop the IR hardware from generating interrupts */ ++ cx23885_input_ir_stop(dev); ++ ++ if (dev->kernel_ir == NULL) ++ return; ++ rc_unregister_device(dev->kernel_ir->rc); ++ kfree(dev->kernel_ir->phys); ++ kfree(dev->kernel_ir->name); ++ kfree(dev->kernel_ir); ++ dev->kernel_ir = NULL; ++} +diff --git a/drivers/media/pci/cx23885/cx23885-input.h b/drivers/media/pci/cx23885/cx23885-input.h +new file mode 100644 +index 0000000..87dc44e +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-input.h +@@ -0,0 +1,30 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Infrared remote control input device ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#ifndef _CX23885_INPUT_H_ ++#define _CX23885_INPUT_H_ ++void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events); ++ ++int cx23885_input_init(struct cx23885_dev *dev); ++void cx23885_input_fini(struct cx23885_dev *dev); ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.c b/drivers/media/pci/cx23885/cx23885-ioctl.c +new file mode 100644 +index 0000000..ea9a614 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-ioctl.c +@@ -0,0 +1,210 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Various common ioctl() support functions ++ * ++ * Copyright (c) 2009 Andy Walls ++ * ++ * 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 "cx23885.h" ++#include "cx23885-ioctl.h" ++ ++#include ++ ++int cx23885_g_chip_ident(struct file *file, void *fh, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; ++ int err = 0; ++ u8 rev; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ switch (chip->match.type) { ++ case V4L2_CHIP_MATCH_HOST: ++ switch (chip->match.addr) { ++ case 0: ++ rev = cx_read(RDR_CFG2) & 0xff; ++ switch (dev->pci->device) { ++ case 0x8852: ++ /* rev 0x04 could be '885 or '888. Pick '888. */ ++ if (rev == 0x04) ++ chip->ident = V4L2_IDENT_CX23888; ++ else ++ chip->ident = V4L2_IDENT_CX23885; ++ break; ++ case 0x8880: ++ if (rev == 0x0e || rev == 0x0f) ++ chip->ident = V4L2_IDENT_CX23887; ++ else ++ chip->ident = V4L2_IDENT_CX23888; ++ break; ++ default: ++ chip->ident = V4L2_IDENT_UNKNOWN; ++ break; ++ } ++ chip->revision = (dev->pci->device << 16) | (rev << 8) | ++ (dev->hwrevision & 0xff); ++ break; ++ case 1: ++ if (dev->v4l_device != NULL) { ++ chip->ident = V4L2_IDENT_CX23417; ++ chip->revision = 0; ++ } ++ break; ++ case 2: ++ /* ++ * The integrated IR controller on the CX23888 is ++ * host chip 2. It may not be used/initialized or sd_ir ++ * may be pointing at the cx25840 subdevice for the ++ * IR controller on the CX23885. Thus we find it ++ * without using the dev->sd_ir pointer. ++ */ ++ call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident, ++ chip); ++ break; ++ default: ++ err = -EINVAL; /* per V4L2 spec */ ++ break; ++ } ++ break; ++ case V4L2_CHIP_MATCH_I2C_DRIVER: ++ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ ++ call_all(dev, core, g_chip_ident, chip); ++ break; ++ case V4L2_CHIP_MATCH_I2C_ADDR: ++ /* ++ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work ++ * to look if a chip is at the address with no driver. That's a ++ * dangerous thing to do with EEPROMs anyway. ++ */ ++ call_all(dev, core, g_chip_ident, chip); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ return err; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cx23885_g_host_register(struct cx23885_dev *dev, ++ struct v4l2_dbg_register *reg) ++{ ++ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) ++ return -EINVAL; ++ ++ reg->size = 4; ++ reg->val = cx_read(reg->reg); ++ return 0; ++} ++ ++static int cx23417_g_register(struct cx23885_dev *dev, ++ struct v4l2_dbg_register *reg) ++{ ++ u32 value; ++ ++ if (dev->v4l_device == NULL) ++ return -EINVAL; ++ ++ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) ++ return -EINVAL; ++ ++ if (mc417_register_read(dev, (u16) reg->reg, &value)) ++ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ ++ ++ reg->size = 4; ++ reg->val = value; ++ return 0; ++} ++ ++int cx23885_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (reg->match.type == V4L2_CHIP_MATCH_HOST) { ++ switch (reg->match.addr) { ++ case 0: ++ return cx23885_g_host_register(dev, reg); ++ case 1: ++ return cx23417_g_register(dev, reg); ++ default: ++ break; ++ } ++ } ++ ++ /* FIXME - any error returns should not be ignored */ ++ call_all(dev, core, g_register, reg); ++ return 0; ++} ++ ++static int cx23885_s_host_register(struct cx23885_dev *dev, ++ struct v4l2_dbg_register *reg) ++{ ++ if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) ++ return -EINVAL; ++ ++ reg->size = 4; ++ cx_write(reg->reg, reg->val); ++ return 0; ++} ++ ++static int cx23417_s_register(struct cx23885_dev *dev, ++ struct v4l2_dbg_register *reg) ++{ ++ if (dev->v4l_device == NULL) ++ return -EINVAL; ++ ++ if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) ++ return -EINVAL; ++ ++ if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val)) ++ return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ ++ ++ reg->size = 4; ++ return 0; ++} ++ ++int cx23885_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ if (reg->match.type == V4L2_CHIP_MATCH_HOST) { ++ switch (reg->match.addr) { ++ case 0: ++ return cx23885_s_host_register(dev, reg); ++ case 1: ++ return cx23417_s_register(dev, reg); ++ default: ++ break; ++ } ++ } ++ ++ /* FIXME - any error returns should not be ignored */ ++ call_all(dev, core, s_register, reg); ++ return 0; ++} ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-ioctl.h b/drivers/media/pci/cx23885/cx23885-ioctl.h +new file mode 100644 +index 0000000..315be0c +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-ioctl.h +@@ -0,0 +1,39 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Various common ioctl() support functions ++ * ++ * Copyright (c) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _CX23885_IOCTL_H_ ++#define _CX23885_IOCTL_H_ ++ ++int cx23885_g_chip_ident(struct file *file, void *fh, ++ struct v4l2_dbg_chip_ident *chip); ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++int cx23885_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg); ++ ++ ++int cx23885_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg); ++ ++#endif ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-ir.c b/drivers/media/pci/cx23885/cx23885-ir.c +new file mode 100644 +index 0000000..bfef193 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-ir.c +@@ -0,0 +1,118 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Infrared device support routines - non-input, non-vl42_subdev routines ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++ ++#include "cx23885.h" ++#include "cx23885-ir.h" ++#include "cx23885-input.h" ++ ++#define CX23885_IR_RX_FIFO_SERVICE_REQ 0 ++#define CX23885_IR_RX_END_OF_RX_DETECTED 1 ++#define CX23885_IR_RX_HW_FIFO_OVERRUN 2 ++#define CX23885_IR_RX_SW_FIFO_OVERRUN 3 ++ ++#define CX23885_IR_TX_FIFO_SERVICE_REQ 0 ++ ++ ++void cx23885_ir_rx_work_handler(struct work_struct *work) ++{ ++ struct cx23885_dev *dev = ++ container_of(work, struct cx23885_dev, ir_rx_work); ++ u32 events = 0; ++ unsigned long *notifications = &dev->ir_rx_notifications; ++ ++ if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications)) ++ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; ++ if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications)) ++ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; ++ if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications)) ++ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; ++ if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications)) ++ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; ++ ++ if (events == 0) ++ return; ++ ++ if (dev->kernel_ir) ++ cx23885_input_rx_work_handler(dev, events); ++} ++ ++void cx23885_ir_tx_work_handler(struct work_struct *work) ++{ ++ struct cx23885_dev *dev = ++ container_of(work, struct cx23885_dev, ir_tx_work); ++ u32 events = 0; ++ unsigned long *notifications = &dev->ir_tx_notifications; ++ ++ if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications)) ++ events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; ++ ++ if (events == 0) ++ return; ++ ++} ++ ++/* Possibly called in an IRQ context */ ++void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) ++{ ++ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); ++ unsigned long *notifications = &dev->ir_rx_notifications; ++ ++ if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ) ++ set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications); ++ if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED) ++ set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications); ++ if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN) ++ set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications); ++ if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) ++ set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications); ++ ++ /* ++ * For the integrated AV core, we are already in a workqueue context. ++ * For the CX23888 integrated IR, we are in an interrupt context. ++ */ ++ if (sd == dev->sd_cx25840) ++ cx23885_ir_rx_work_handler(&dev->ir_rx_work); ++ else ++ schedule_work(&dev->ir_rx_work); ++} ++ ++/* Possibly called in an IRQ context */ ++void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) ++{ ++ struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); ++ unsigned long *notifications = &dev->ir_tx_notifications; ++ ++ if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) ++ set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications); ++ ++ /* ++ * For the integrated AV core, we are already in a workqueue context. ++ * For the CX23888 integrated IR, we are in an interrupt context. ++ */ ++ if (sd == dev->sd_cx25840) ++ cx23885_ir_tx_work_handler(&dev->ir_tx_work); ++ else ++ schedule_work(&dev->ir_tx_work); ++} +diff --git a/drivers/media/pci/cx23885/cx23885-ir.h b/drivers/media/pci/cx23885/cx23885-ir.h +new file mode 100644 +index 0000000..0c9d8bd +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-ir.h +@@ -0,0 +1,31 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * Infrared device support routines - non-input, non-vl42_subdev routines ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#ifndef _CX23885_IR_H_ ++#define _CX23885_IR_H_ ++void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); ++void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); ++ ++void cx23885_ir_rx_work_handler(struct work_struct *work); ++void cx23885_ir_tx_work_handler(struct work_struct *work); ++#endif +diff --git a/drivers/media/pci/cx23885/cx23885-reg.h b/drivers/media/pci/cx23885/cx23885-reg.h +new file mode 100644 +index 0000000..a99936e +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-reg.h +@@ -0,0 +1,452 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _CX23885_REG_H_ ++#define _CX23885_REG_H_ ++ ++/* ++Address Map ++0x00000000 -> 0x00009000 TX SRAM (Fifos) ++0x00010000 -> 0x00013c00 RX SRAM CMDS + CDT ++ ++EACH CMDS struct is 0x80 bytes long ++ ++DMAx_PTR1 = 0x03040 address of first cluster ++DMAx_PTR2 = 0x10600 address of the CDT ++DMAx_CNT1 = cluster size in (bytes >> 4) -1 ++DMAx_CNT2 = total cdt size for all entries >> 3 ++ ++Cluster Descriptor entry = 4 DWORDS ++ DWORD 0 -> ptr to cluster ++ DWORD 1 Reserved ++ DWORD 2 Reserved ++ DWORD 3 Reserved ++ ++Channel manager Data Structure entry = 20 DWORD ++ 0 IntialProgramCounterLow ++ 1 IntialProgramCounterHigh ++ 2 ClusterDescriptorTableBase ++ 3 ClusterDescriptorTableSize ++ 4 InstructionQueueBase ++ 5 InstructionQueueSize ++... Reserved ++ 19 Reserved ++*/ ++ ++/* Risc Instructions */ ++#define RISC_CNT_INC 0x00010000 ++#define RISC_CNT_RESET 0x00030000 ++#define RISC_IRQ1 0x01000000 ++#define RISC_IRQ2 0x02000000 ++#define RISC_EOL 0x04000000 ++#define RISC_SOL 0x08000000 ++#define RISC_WRITE 0x10000000 ++#define RISC_SKIP 0x20000000 ++#define RISC_JUMP 0x70000000 ++#define RISC_SYNC 0x80000000 ++#define RISC_RESYNC 0x80008000 ++#define RISC_READ 0x90000000 ++#define RISC_WRITERM 0xB0000000 ++#define RISC_WRITECM 0xC0000000 ++#define RISC_WRITECR 0xD0000000 ++#define RISC_WRITEC 0x50000000 ++#define RISC_READC 0xA0000000 ++ ++ ++/* Audio and Video Core */ ++#define HOST_REG1 0x00000000 ++#define HOST_REG2 0x00000001 ++#define HOST_REG3 0x00000002 ++ ++/* Chip Configuration Registers */ ++#define CHIP_CTRL 0x00000100 ++#define AFE_CTRL 0x00000104 ++#define VID_PLL_INT_POST 0x00000108 ++#define VID_PLL_FRAC 0x0000010C ++#define AUX_PLL_INT_POST 0x00000110 ++#define AUX_PLL_FRAC 0x00000114 ++#define SYS_PLL_INT_POST 0x00000118 ++#define SYS_PLL_FRAC 0x0000011C ++#define PIN_CTRL 0x00000120 ++#define AUD_IO_CTRL 0x00000124 ++#define AUD_LOCK1 0x00000128 ++#define AUD_LOCK2 0x0000012C ++#define POWER_CTRL 0x00000130 ++#define AFE_DIAG_CTRL1 0x00000134 ++#define AFE_DIAG_CTRL3 0x0000013C ++#define PLL_DIAG_CTRL 0x00000140 ++#define AFE_CLK_OUT_CTRL 0x00000144 ++#define DLL1_DIAG_CTRL 0x0000015C ++ ++/* GPIO[23:19] Output Enable */ ++#define GPIO2_OUT_EN_REG 0x00000160 ++/* GPIO[23:19] Data Registers */ ++#define GPIO2 0x00000164 ++ ++#define IFADC_CTRL 0x00000180 ++ ++/* Infrared Remote Registers */ ++#define IR_CNTRL_REG 0x00000200 ++#define IR_TXCLK_REG 0x00000204 ++#define IR_RXCLK_REG 0x00000208 ++#define IR_CDUTY_REG 0x0000020C ++#define IR_STAT_REG 0x00000210 ++#define IR_IRQEN_REG 0x00000214 ++#define IR_FILTR_REG 0x00000218 ++#define IR_FIFO_REG 0x0000023C ++ ++/* Video Decoder Registers */ ++#define MODE_CTRL 0x00000400 ++#define OUT_CTRL1 0x00000404 ++#define OUT_CTRL2 0x00000408 ++#define GEN_STAT 0x0000040C ++#define INT_STAT_MASK 0x00000410 ++#define LUMA_CTRL 0x00000414 ++#define HSCALE_CTRL 0x00000418 ++#define VSCALE_CTRL 0x0000041C ++#define CHROMA_CTRL 0x00000420 ++#define VBI_LINE_CTRL1 0x00000424 ++#define VBI_LINE_CTRL2 0x00000428 ++#define VBI_LINE_CTRL3 0x0000042C ++#define VBI_LINE_CTRL4 0x00000430 ++#define VBI_LINE_CTRL5 0x00000434 ++#define VBI_FC_CFG 0x00000438 ++#define VBI_MISC_CFG1 0x0000043C ++#define VBI_MISC_CFG2 0x00000440 ++#define VBI_PAY1 0x00000444 ++#define VBI_PAY2 0x00000448 ++#define VBI_CUST1_CFG1 0x0000044C ++#define VBI_CUST1_CFG2 0x00000450 ++#define VBI_CUST1_CFG3 0x00000454 ++#define VBI_CUST2_CFG1 0x00000458 ++#define VBI_CUST2_CFG2 0x0000045C ++#define VBI_CUST2_CFG3 0x00000460 ++#define VBI_CUST3_CFG1 0x00000464 ++#define VBI_CUST3_CFG2 0x00000468 ++#define VBI_CUST3_CFG3 0x0000046C ++#define HORIZ_TIM_CTRL 0x00000470 ++#define VERT_TIM_CTRL 0x00000474 ++#define SRC_COMB_CFG 0x00000478 ++#define CHROMA_VBIOFF_CFG 0x0000047C ++#define FIELD_COUNT 0x00000480 ++#define MISC_TIM_CTRL 0x00000484 ++#define DFE_CTRL1 0x00000488 ++#define DFE_CTRL2 0x0000048C ++#define DFE_CTRL3 0x00000490 ++#define PLL_CTRL 0x00000494 ++#define HTL_CTRL 0x00000498 ++#define COMB_CTRL 0x0000049C ++#define CRUSH_CTRL 0x000004A0 ++#define SOFT_RST_CTRL 0x000004A4 ++#define CX885_VERSION 0x000004B4 ++#define VBI_PASS_CTRL 0x000004BC ++ ++/* Audio Decoder Registers */ ++/* 8051 Configuration */ ++#define DL_CTL 0x00000800 ++#define STD_DET_STATUS 0x00000804 ++#define STD_DET_CTL 0x00000808 ++#define DW8051_INT 0x0000080C ++#define GENERAL_CTL 0x00000810 ++#define AAGC_CTL 0x00000814 ++#define DEMATRIX_CTL 0x000008CC ++#define PATH1_CTL1 0x000008D0 ++#define PATH1_VOL_CTL 0x000008D4 ++#define PATH1_EQ_CTL 0x000008D8 ++#define PATH1_SC_CTL 0x000008DC ++#define PATH2_CTL1 0x000008E0 ++#define PATH2_VOL_CTL 0x000008E4 ++#define PATH2_EQ_CTL 0x000008E8 ++#define PATH2_SC_CTL 0x000008EC ++ ++/* Sample Rate Converter */ ++#define SRC_CTL 0x000008F0 ++#define SRC_LF_COEF 0x000008F4 ++#define SRC1_CTL 0x000008F8 ++#define SRC2_CTL 0x000008FC ++#define SRC3_CTL 0x00000900 ++#define SRC4_CTL 0x00000904 ++#define SRC5_CTL 0x00000908 ++#define SRC6_CTL 0x0000090C ++#define BAND_OUT_SEL 0x00000910 ++#define I2S_N_CTL 0x00000914 ++#define I2S_OUT_CTL 0x00000918 ++#define AUTOCONFIG_REG 0x000009C4 ++ ++/* Audio ADC Registers */ ++#define DSM_CTRL1 0x00000000 ++#define DSM_CTRL2 0x00000001 ++#define CHP_EN_CTRL 0x00000002 ++#define CHP_CLK_CTRL1 0x00000004 ++#define CHP_CLK_CTRL2 0x00000005 ++#define BG_REF_CTRL 0x00000006 ++#define SD2_SW_CTRL1 0x00000008 ++#define SD2_SW_CTRL2 0x00000009 ++#define SD2_BIAS_CTRL 0x0000000A ++#define AMP_BIAS_CTRL 0x0000000C ++#define CH_PWR_CTRL1 0x0000000E ++#define FLD_CH_SEL (1 << 3) ++#define CH_PWR_CTRL2 0x0000000F ++#define DSM_STATUS1 0x00000010 ++#define DSM_STATUS2 0x00000011 ++#define DIG_CTL1 0x00000012 ++#define DIG_CTL2 0x00000013 ++#define I2S_TX_CFG 0x0000001A ++ ++#define DEV_CNTRL2 0x00040000 ++ ++#define PCI_MSK_IR (1 << 28) ++#define PCI_MSK_AV_CORE (1 << 27) ++#define PCI_MSK_GPIO1 (1 << 24) ++#define PCI_MSK_GPIO0 (1 << 23) ++#define PCI_MSK_APB_DMA (1 << 12) ++#define PCI_MSK_AL_WR (1 << 11) ++#define PCI_MSK_AL_RD (1 << 10) ++#define PCI_MSK_RISC_WR (1 << 9) ++#define PCI_MSK_RISC_RD (1 << 8) ++#define PCI_MSK_AUD_EXT (1 << 4) ++#define PCI_MSK_AUD_INT (1 << 3) ++#define PCI_MSK_VID_C (1 << 2) ++#define PCI_MSK_VID_B (1 << 1) ++#define PCI_MSK_VID_A 1 ++#define PCI_INT_MSK 0x00040010 ++ ++#define PCI_INT_STAT 0x00040014 ++#define PCI_INT_MSTAT 0x00040018 ++ ++#define VID_A_INT_MSK 0x00040020 ++#define VID_A_INT_STAT 0x00040024 ++#define VID_A_INT_MSTAT 0x00040028 ++#define VID_A_INT_SSTAT 0x0004002C ++ ++#define VID_B_INT_MSK 0x00040030 ++#define VID_B_MSK_BAD_PKT (1 << 20) ++#define VID_B_MSK_VBI_OPC_ERR (1 << 17) ++#define VID_B_MSK_OPC_ERR (1 << 16) ++#define VID_B_MSK_VBI_SYNC (1 << 13) ++#define VID_B_MSK_SYNC (1 << 12) ++#define VID_B_MSK_VBI_OF (1 << 9) ++#define VID_B_MSK_OF (1 << 8) ++#define VID_B_MSK_VBI_RISCI2 (1 << 5) ++#define VID_B_MSK_RISCI2 (1 << 4) ++#define VID_B_MSK_VBI_RISCI1 (1 << 1) ++#define VID_B_MSK_RISCI1 1 ++#define VID_B_INT_STAT 0x00040034 ++#define VID_B_INT_MSTAT 0x00040038 ++#define VID_B_INT_SSTAT 0x0004003C ++ ++#define VID_B_MSK_BAD_PKT (1 << 20) ++#define VID_B_MSK_OPC_ERR (1 << 16) ++#define VID_B_MSK_SYNC (1 << 12) ++#define VID_B_MSK_OF (1 << 8) ++#define VID_B_MSK_RISCI2 (1 << 4) ++#define VID_B_MSK_RISCI1 1 ++ ++#define VID_C_MSK_BAD_PKT (1 << 20) ++#define VID_C_MSK_OPC_ERR (1 << 16) ++#define VID_C_MSK_SYNC (1 << 12) ++#define VID_C_MSK_OF (1 << 8) ++#define VID_C_MSK_RISCI2 (1 << 4) ++#define VID_C_MSK_RISCI1 1 ++ ++/* A superset for testing purposes */ ++#define VID_BC_MSK_BAD_PKT (1 << 20) ++#define VID_BC_MSK_OPC_ERR (1 << 16) ++#define VID_BC_MSK_SYNC (1 << 12) ++#define VID_BC_MSK_OF (1 << 8) ++#define VID_BC_MSK_VBI_RISCI2 (1 << 5) ++#define VID_BC_MSK_RISCI2 (1 << 4) ++#define VID_BC_MSK_VBI_RISCI1 (1 << 1) ++#define VID_BC_MSK_RISCI1 1 ++ ++#define VID_C_INT_MSK 0x00040040 ++#define VID_C_INT_STAT 0x00040044 ++#define VID_C_INT_MSTAT 0x00040048 ++#define VID_C_INT_SSTAT 0x0004004C ++ ++#define AUDIO_INT_INT_MSK 0x00040050 ++#define AUDIO_INT_INT_STAT 0x00040054 ++#define AUDIO_INT_INT_MSTAT 0x00040058 ++#define AUDIO_INT_INT_SSTAT 0x0004005C ++ ++#define AUDIO_EXT_INT_MSK 0x00040060 ++#define AUDIO_EXT_INT_STAT 0x00040064 ++#define AUDIO_EXT_INT_MSTAT 0x00040068 ++#define AUDIO_EXT_INT_SSTAT 0x0004006C ++ ++#define RDR_CFG0 0x00050000 ++#define RDR_CFG1 0x00050004 ++#define RDR_CFG2 0x00050008 ++#define RDR_RDRCTL1 0x0005030c ++#define RDR_TLCTL0 0x00050318 ++ ++/* APB DMAC Current Buffer Pointer */ ++#define DMA1_PTR1 0x00100000 ++#define DMA2_PTR1 0x00100004 ++#define DMA3_PTR1 0x00100008 ++#define DMA4_PTR1 0x0010000C ++#define DMA5_PTR1 0x00100010 ++#define DMA6_PTR1 0x00100014 ++#define DMA7_PTR1 0x00100018 ++#define DMA8_PTR1 0x0010001C ++ ++/* APB DMAC Current Table Pointer */ ++#define DMA1_PTR2 0x00100040 ++#define DMA2_PTR2 0x00100044 ++#define DMA3_PTR2 0x00100048 ++#define DMA4_PTR2 0x0010004C ++#define DMA5_PTR2 0x00100050 ++#define DMA6_PTR2 0x00100054 ++#define DMA7_PTR2 0x00100058 ++#define DMA8_PTR2 0x0010005C ++ ++/* APB DMAC Buffer Limit */ ++#define DMA1_CNT1 0x00100080 ++#define DMA2_CNT1 0x00100084 ++#define DMA3_CNT1 0x00100088 ++#define DMA4_CNT1 0x0010008C ++#define DMA5_CNT1 0x00100090 ++#define DMA6_CNT1 0x00100094 ++#define DMA7_CNT1 0x00100098 ++#define DMA8_CNT1 0x0010009C ++ ++/* APB DMAC Table Size */ ++#define DMA1_CNT2 0x001000C0 ++#define DMA2_CNT2 0x001000C4 ++#define DMA3_CNT2 0x001000C8 ++#define DMA4_CNT2 0x001000CC ++#define DMA5_CNT2 0x001000D0 ++#define DMA6_CNT2 0x001000D4 ++#define DMA7_CNT2 0x001000D8 ++#define DMA8_CNT2 0x001000DC ++ ++/* Timer Counters */ ++#define TM_CNT_LDW 0x00110000 ++#define TM_CNT_UW 0x00110004 ++#define TM_LMT_LDW 0x00110008 ++#define TM_LMT_UW 0x0011000C ++ ++/* GPIO */ ++#define GP0_IO 0x00110010 ++#define GPIO_ISM 0x00110014 ++#define SOFT_RESET 0x0011001C ++ ++/* GPIO (417 Microsoftcontroller) RW Data */ ++#define MC417_RWD 0x00110020 ++ ++/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */ ++#define MC417_OEN 0x00110024 ++#define MC417_CTL 0x00110028 ++#define ALT_PIN_OUT_SEL 0x0011002C ++#define CLK_DELAY 0x00110048 ++#define PAD_CTRL 0x0011004C ++ ++/* Video A Interface */ ++#define VID_A_GPCNT 0x00130020 ++#define VBI_A_GPCNT 0x00130024 ++#define VID_A_GPCNT_CTL 0x00130030 ++#define VBI_A_GPCNT_CTL 0x00130034 ++#define VID_A_DMA_CTL 0x00130040 ++#define VID_A_VIP_CTRL 0x00130080 ++#define VID_A_PIXEL_FRMT 0x00130084 ++#define VID_A_VBI_CTRL 0x00130088 ++ ++/* Video B Interface */ ++#define VID_B_DMA 0x00130100 ++#define VBI_B_DMA 0x00130108 ++#define VID_B_GPCNT 0x00130120 ++#define VBI_B_GPCNT 0x00130124 ++#define VID_B_GPCNT_CTL 0x00130134 ++#define VBI_B_GPCNT_CTL 0x00130138 ++#define VID_B_DMA_CTL 0x00130140 ++#define VID_B_SRC_SEL 0x00130144 ++#define VID_B_LNGTH 0x00130150 ++#define VID_B_HW_SOP_CTL 0x00130154 ++#define VID_B_GEN_CTL 0x00130158 ++#define VID_B_BD_PKT_STATUS 0x0013015C ++#define VID_B_SOP_STATUS 0x00130160 ++#define VID_B_FIFO_OVFL_STAT 0x00130164 ++#define VID_B_VLD_MISC 0x00130168 ++#define VID_B_TS_CLK_EN 0x0013016C ++#define VID_B_VIP_CTRL 0x00130180 ++#define VID_B_PIXEL_FRMT 0x00130184 ++ ++/* Video C Interface */ ++#define VID_C_GPCNT 0x00130220 ++#define VID_C_GPCNT_CTL 0x00130230 ++#define VBI_C_GPCNT_CTL 0x00130234 ++#define VID_C_DMA_CTL 0x00130240 ++#define VID_C_LNGTH 0x00130250 ++#define VID_C_HW_SOP_CTL 0x00130254 ++#define VID_C_GEN_CTL 0x00130258 ++#define VID_C_BD_PKT_STATUS 0x0013025C ++#define VID_C_SOP_STATUS 0x00130260 ++#define VID_C_FIFO_OVFL_STAT 0x00130264 ++#define VID_C_VLD_MISC 0x00130268 ++#define VID_C_TS_CLK_EN 0x0013026C ++ ++/* Internal Audio Interface */ ++#define AUD_INT_A_GPCNT 0x00140020 ++#define AUD_INT_B_GPCNT 0x00140024 ++#define AUD_INT_A_GPCNT_CTL 0x00140030 ++#define AUD_INT_B_GPCNT_CTL 0x00140034 ++#define AUD_INT_DMA_CTL 0x00140040 ++#define AUD_INT_A_LNGTH 0x00140050 ++#define AUD_INT_B_LNGTH 0x00140054 ++#define AUD_INT_A_MODE 0x00140058 ++#define AUD_INT_B_MODE 0x0014005C ++ ++/* External Audio Interface */ ++#define AUD_EXT_DMA 0x00140100 ++#define AUD_EXT_GPCNT 0x00140120 ++#define AUD_EXT_GPCNT_CTL 0x00140130 ++#define AUD_EXT_DMA_CTL 0x00140140 ++#define AUD_EXT_LNGTH 0x00140150 ++#define AUD_EXT_A_MODE 0x00140158 ++ ++/* I2C Bus 1 */ ++#define I2C1_ADDR 0x00180000 ++#define I2C1_WDATA 0x00180004 ++#define I2C1_CTRL 0x00180008 ++#define I2C1_RDATA 0x0018000C ++#define I2C1_STAT 0x00180010 ++ ++/* I2C Bus 2 */ ++#define I2C2_ADDR 0x00190000 ++#define I2C2_WDATA 0x00190004 ++#define I2C2_CTRL 0x00190008 ++#define I2C2_RDATA 0x0019000C ++#define I2C2_STAT 0x00190010 ++ ++/* I2C Bus 3 */ ++#define I2C3_ADDR 0x001A0000 ++#define I2C3_WDATA 0x001A0004 ++#define I2C3_CTRL 0x001A0008 ++#define I2C3_RDATA 0x001A000C ++#define I2C3_STAT 0x001A0010 ++ ++/* UART */ ++#define UART_CTL 0x001B0000 ++#define UART_BRD 0x001B0004 ++#define UART_ISR 0x001B000C ++#define UART_CNT 0x001B0010 ++ ++#endif /* _CX23885_REG_H_ */ +diff --git a/drivers/media/pci/cx23885/cx23885-vbi.c b/drivers/media/pci/cx23885/cx23885-vbi.c +new file mode 100644 +index 0000000..a1154f0 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-vbi.c +@@ -0,0 +1,295 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2007 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++ ++static unsigned int vbibufs = 4; ++module_param(vbibufs, int, 0644); ++MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); ++ ++static unsigned int vbi_debug; ++module_param(vbi_debug, int, 0644); ++MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (vbi_debug >= level)\ ++ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++/* ------------------------------------------------------------------ */ ++ ++#define VBI_LINE_LENGTH 1440 ++#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ ++#define NTSC_VBI_END_LINE 21 ++#define NTSC_VBI_LINES (NTSC_VBI_END_LINE - NTSC_VBI_START_LINE + 1) ++ ++ ++int cx23885_vbi_fmt(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (dev->tvnorm & V4L2_STD_525_60) { ++ /* ntsc */ ++ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; ++ f->fmt.vbi.sampling_rate = 27000000; ++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; ++ f->fmt.vbi.offset = 0; ++ f->fmt.vbi.flags = 0; ++ f->fmt.vbi.start[0] = 10; ++ f->fmt.vbi.count[0] = 17; ++ f->fmt.vbi.start[1] = 263 + 10 + 1; ++ f->fmt.vbi.count[1] = 17; ++ } else if (dev->tvnorm & V4L2_STD_625_50) { ++ /* pal */ ++ f->fmt.vbi.sampling_rate = 35468950; ++ f->fmt.vbi.start[0] = 7 - 1; ++ f->fmt.vbi.start[1] = 319 - 1; ++ } ++ ++ return 0; ++} ++ ++/* We're given the Video Interrupt status register. ++ * The cx23885_video_irq() func has already validated ++ * the potential error bits, we just need to ++ * deal with vbi payload and return indication if ++ * we actually processed any payload. ++ */ ++int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status) ++{ ++ u32 count; ++ int handled = 0; ++ ++ if (status & VID_BC_MSK_VBI_RISCI1) { ++ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI1\n", __func__); ++ spin_lock(&dev->slock); ++ count = cx_read(VID_A_GPCNT); ++ cx23885_video_wakeup(dev, &dev->vbiq, count); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ ++ if (status & VID_BC_MSK_VBI_RISCI2) { ++ dprintk(1, "%s() VID_BC_MSK_VBI_RISCI2\n", __func__); ++ dprintk(2, "stopper vbi\n"); ++ spin_lock(&dev->slock); ++ cx23885_restart_vbi_queue(dev, &dev->vbiq); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ ++ return handled; ++} ++ ++static int cx23885_start_vbi_dma(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q, ++ struct cx23885_buffer *buf) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* setup fifo + format */ ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], ++ buf->vb.width, buf->risc.dma); ++ ++ /* reset counter */ ++ cx_write(VID_A_GPCNT_CTL, 3); ++ cx_write(VID_A_VBI_CTRL, 3); ++ cx_write(VBI_A_GPCNT_CTL, 3); ++ q->count = 1; ++ ++ /* enable irq */ ++ cx23885_irq_add_enable(dev, 0x01); ++ cx_set(VID_A_INT_MSK, 0x000022); ++ ++ /* start dma */ ++ cx_set(DEV_CNTRL2, (1<<5)); ++ cx_set(VID_A_DMA_CTL, 0x22); /* FIFO and RISC enable */ ++ ++ return 0; ++} ++ ++ ++int cx23885_restart_vbi_queue(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q) ++{ ++ struct cx23885_buffer *buf; ++ struct list_head *item; ++ ++ if (list_empty(&q->active)) ++ return 0; ++ ++ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue); ++ dprintk(2, "restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ cx23885_start_vbi_dma(dev, q, buf); ++ list_for_each(item, &q->active) { ++ buf = list_entry(item, struct cx23885_buffer, vb.queue); ++ buf->count = q->count++; ++ } ++ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); ++ return 0; ++} ++ ++void cx23885_vbi_timeout(unsigned long data) ++{ ++ struct cx23885_dev *dev = (struct cx23885_dev *)data; ++ struct cx23885_dmaqueue *q = &dev->vbiq; ++ struct cx23885_buffer *buf; ++ unsigned long flags; ++ ++ /* Stop the VBI engine */ ++ cx_clear(VID_A_DMA_CTL, 0x22); ++ ++ spin_lock_irqsave(&dev->slock, flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx23885_buffer, ++ vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name, ++ buf, buf->vb.i, (unsigned long)buf->risc.dma); ++ } ++ cx23885_restart_vbi_queue(dev, q); ++ spin_unlock_irqrestore(&dev->slock, flags); ++} ++ ++/* ------------------------------------------------------------------ */ ++#define VBI_LINE_LENGTH 1440 ++#define VBI_LINE_COUNT 17 ++ ++static int ++vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; ++ if (0 == *count) ++ *count = vbibufs; ++ if (*count < 2) ++ *count = 2; ++ if (*count > 32) ++ *count = 32; ++ return 0; ++} ++ ++static int ++vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx23885_buffer *buf = container_of(vb, ++ struct cx23885_buffer, vb); ++ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); ++ unsigned int size; ++ int rc; ++ ++ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ buf->vb.width = VBI_LINE_LENGTH; ++ buf->vb.height = VBI_LINE_COUNT; ++ buf->vb.size = size; ++ buf->vb.field = V4L2_FIELD_SEQ_TB; ++ ++ rc = videobuf_iolock(q, &buf->vb, NULL); ++ if (0 != rc) ++ goto fail; ++ cx23885_risc_vbibuffer(dev->pci, &buf->risc, ++ dma->sglist, ++ 0, buf->vb.width * buf->vb.height, ++ buf->vb.width, 0, ++ buf->vb.height); ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx23885_free_buffer(q, buf); ++ return rc; ++} ++ ++static void ++vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct cx23885_buffer *buf = ++ container_of(vb, struct cx23885_buffer, vb); ++ struct cx23885_buffer *prev; ++ struct cx23885_fh *fh = vq->priv_data; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx23885_dmaqueue *q = &dev->vbiq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if (list_empty(&q->active)) { ++ list_add_tail(&buf->vb.queue, &q->active); ++ cx23885_start_vbi_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies + (BUFFER_TIMEOUT / 30)); ++ dprintk(2, "[%p/%d] vbi_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ prev = list_entry(q->active.prev, struct cx23885_buffer, ++ vb.queue); ++ list_add_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */ ++ dprintk(2, "[%p/%d] buffer_queue - append to active\n", ++ buf, buf->vb.i); ++ } ++} ++ ++static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx23885_buffer *buf = ++ container_of(vb, struct cx23885_buffer, vb); ++ ++ cx23885_free_buffer(q, buf); ++} ++ ++struct videobuf_queue_ops cx23885_vbi_qops = { ++ .buf_setup = vbi_setup, ++ .buf_prepare = vbi_prepare, ++ .buf_queue = vbi_queue, ++ .buf_release = vbi_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c +new file mode 100644 +index 0000000..5991bc8 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885-video.c +@@ -0,0 +1,1939 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2007 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include ++#include ++#include "cx23885-ioctl.h" ++#include "tuner-xc2028.h" ++ ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); ++MODULE_AUTHOR("Steven Toth "); ++MODULE_LICENSE("GPL"); ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; ++static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; ++static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET }; ++ ++module_param_array(video_nr, int, NULL, 0444); ++module_param_array(vbi_nr, int, NULL, 0444); ++module_param_array(radio_nr, int, NULL, 0444); ++ ++MODULE_PARM_DESC(video_nr, "video device numbers"); ++MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); ++MODULE_PARM_DESC(radio_nr, "radio device numbers"); ++ ++static unsigned int video_debug; ++module_param(video_debug, int, 0644); ++MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); ++ ++static unsigned int irq_debug; ++module_param(irq_debug, int, 0644); ++MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); ++ ++static unsigned int vid_limit = 16; ++module_param(vid_limit, int, 0644); ++MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); ++ ++#define dprintk(level, fmt, arg...)\ ++ do { if (video_debug >= level)\ ++ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++/* ------------------------------------------------------------------- */ ++/* static data */ ++ ++#define FORMAT_FLAGS_PACKED 0x01 ++#if 0 ++static struct cx23885_fmt formats[] = { ++ { ++ .name = "8 bpp, gray", ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "15 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB555, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "15 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB555X, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "16 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "16 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB565X, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "24 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .depth = 24, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "32 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "32 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, ++}; ++#else ++static struct cx23885_fmt formats[] = { ++ { ++#if 0 ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++#endif ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ } ++}; ++#endif ++ ++static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fourcc) ++ return formats+i; ++ ++ printk(KERN_ERR "%s(%c%c%c%c) NOT FOUND\n", __func__, ++ (fourcc & 0xff), ++ ((fourcc >> 8) & 0xff), ++ ((fourcc >> 16) & 0xff), ++ ((fourcc >> 24) & 0xff) ++ ); ++ return NULL; ++} ++ ++/* ------------------------------------------------------------------- */ ++ ++static const struct v4l2_queryctrl no_ctl = { ++ .name = "42", ++ .flags = V4L2_CTRL_FLAG_DISABLED, ++}; ++ ++static struct cx23885_ctrl cx23885_ctls[] = { ++ /* --- video --- */ ++ { ++ .v = { ++ .id = V4L2_CID_BRIGHTNESS, ++ .name = "Brightness", ++ .minimum = 0x00, ++ .maximum = 0xff, ++ .step = 1, ++ .default_value = 0x7f, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ .off = 128, ++ .reg = LUMA_CTRL, ++ .mask = 0x00ff, ++ .shift = 0, ++ }, { ++ .v = { ++ .id = V4L2_CID_CONTRAST, ++ .name = "Contrast", ++ .minimum = 0, ++ .maximum = 0x7f, ++ .step = 1, ++ .default_value = 0x3f, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ .off = 0, ++ .reg = LUMA_CTRL, ++ .mask = 0xff00, ++ .shift = 8, ++ }, { ++ .v = { ++ .id = V4L2_CID_HUE, ++ .name = "Hue", ++ .minimum = -127, ++ .maximum = 128, ++ .step = 1, ++ .default_value = 0x0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ .off = 128, ++ .reg = CHROMA_CTRL, ++ .mask = 0xff0000, ++ .shift = 16, ++ }, { ++ /* strictly, this only describes only U saturation. ++ * V saturation is handled specially through code. ++ */ ++ .v = { ++ .id = V4L2_CID_SATURATION, ++ .name = "Saturation", ++ .minimum = 0, ++ .maximum = 0x7f, ++ .step = 1, ++ .default_value = 0x3f, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ .off = 0, ++ .reg = CHROMA_CTRL, ++ .mask = 0x00ff, ++ .shift = 0, ++ }, { ++ /* --- audio --- */ ++ .v = { ++ .id = V4L2_CID_AUDIO_MUTE, ++ .name = "Mute", ++ .minimum = 0, ++ .maximum = 1, ++ .default_value = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ }, ++ .reg = PATH1_CTL1, ++ .mask = (0x1f << 24), ++ .shift = 24, ++ }, { ++ .v = { ++ .id = V4L2_CID_AUDIO_VOLUME, ++ .name = "Volume", ++ .minimum = 0, ++ .maximum = 65535, ++ .step = 65535 / 100, ++ .default_value = 65535, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ .reg = PATH1_VOL_CTL, ++ .mask = 0xff, ++ .shift = 0, ++ } ++}; ++static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); ++ ++/* Must be sorted from low to high control ID! */ ++static const u32 cx23885_user_ctrls[] = { ++ V4L2_CID_USER_CLASS, ++ V4L2_CID_BRIGHTNESS, ++ V4L2_CID_CONTRAST, ++ V4L2_CID_SATURATION, ++ V4L2_CID_HUE, ++ V4L2_CID_AUDIO_VOLUME, ++ V4L2_CID_AUDIO_MUTE, ++ 0 ++}; ++ ++static const u32 *ctrl_classes[] = { ++ cx23885_user_ctrls, ++ NULL ++}; ++ ++void cx23885_video_wakeup(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q, u32 count) ++{ ++ struct cx23885_buffer *buf; ++ int bc; ++ ++ for (bc = 0;; bc++) { ++ if (list_empty(&q->active)) ++ break; ++ buf = list_entry(q->active.next, ++ struct cx23885_buffer, vb.queue); ++ ++ /* count comes from the hw and is is 16bit wide -- ++ * this trick handles wrap-arounds correctly for ++ * up to 32767 buffers in flight... */ ++ if ((s16) (count - buf->count) < 0) ++ break; ++ ++ v4l2_get_timestamp(&buf->vb.ts); ++ dprintk(2, "[%p/%d] wakeup reg=%d buf=%d\n", buf, buf->vb.i, ++ count, buf->count); ++ buf->vb.state = VIDEOBUF_DONE; ++ list_del(&buf->vb.queue); ++ wake_up(&buf->vb.done); ++ } ++ if (list_empty(&q->active)) ++ del_timer(&q->timeout); ++ else ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ if (bc != 1) ++ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n", ++ __func__, bc); ++} ++ ++int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) ++{ ++ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", ++ __func__, ++ (unsigned int)norm, ++ v4l2_norm_to_name(norm)); ++ ++ dev->tvnorm = norm; ++ ++ call_all(dev, core, s_std, norm); ++ ++ return 0; ++} ++ ++static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ dprintk(1, "%s()\n", __func__); ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template; ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ vfd->release = video_device_release; ++ snprintf(vfd->name, sizeof(vfd->name), "%s (%s)", ++ cx23885_boards[dev->board].name, type); ++ video_set_drvdata(vfd, dev); ++ return vfd; ++} ++ ++static int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl) ++{ ++ int i; ++ ++ if (qctrl->id < V4L2_CID_BASE || ++ qctrl->id >= V4L2_CID_LASTP1) ++ return -EINVAL; ++ for (i = 0; i < CX23885_CTLS; i++) ++ if (cx23885_ctls[i].v.id == qctrl->id) ++ break; ++ if (i == CX23885_CTLS) { ++ *qctrl = no_ctl; ++ return 0; ++ } ++ *qctrl = cx23885_ctls[i].v; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------- */ ++/* resource management */ ++ ++static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh, ++ unsigned int bit) ++{ ++ dprintk(1, "%s()\n", __func__); ++ if (fh->resources & bit) ++ /* have it already allocated */ ++ return 1; ++ ++ /* is it free? */ ++ mutex_lock(&dev->lock); ++ if (dev->resources & bit) { ++ /* no, someone else uses it */ ++ mutex_unlock(&dev->lock); ++ return 0; ++ } ++ /* it's free, grab it */ ++ fh->resources |= bit; ++ dev->resources |= bit; ++ dprintk(1, "res: get %d\n", bit); ++ mutex_unlock(&dev->lock); ++ return 1; ++} ++ ++static int res_check(struct cx23885_fh *fh, unsigned int bit) ++{ ++ return fh->resources & bit; ++} ++ ++static int res_locked(struct cx23885_dev *dev, unsigned int bit) ++{ ++ return dev->resources & bit; ++} ++ ++static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, ++ unsigned int bits) ++{ ++ BUG_ON((fh->resources & bits) != bits); ++ dprintk(1, "%s()\n", __func__); ++ ++ mutex_lock(&dev->lock); ++ fh->resources &= ~bits; ++ dev->resources &= ~bits; ++ dprintk(1, "res: put %d\n", bits); ++ mutex_unlock(&dev->lock); ++} ++ ++static int cx23885_flatiron_write(struct cx23885_dev *dev, u8 reg, u8 data) ++{ ++ /* 8 bit registers, 8 bit values */ ++ u8 buf[] = { reg, data }; ++ ++ struct i2c_msg msg = { .addr = 0x98 >> 1, ++ .flags = 0, .buf = buf, .len = 2 }; ++ ++ return i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); ++} ++ ++static u8 cx23885_flatiron_read(struct cx23885_dev *dev, u8 reg) ++{ ++ /* 8 bit registers, 8 bit values */ ++ int ret; ++ u8 b0[] = { reg }; ++ u8 b1[] = { 0 }; ++ ++ struct i2c_msg msg[] = { ++ { .addr = 0x98 >> 1, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = 0x98 >> 1, .flags = I2C_M_RD, .buf = b1, .len = 1 } ++ }; ++ ++ ret = i2c_transfer(&dev->i2c_bus[2].i2c_adap, &msg[0], 2); ++ if (ret != 2) ++ printk(KERN_ERR "%s() error\n", __func__); ++ ++ return b1[0]; ++} ++ ++static void cx23885_flatiron_dump(struct cx23885_dev *dev) ++{ ++ int i; ++ dprintk(1, "Flatiron dump\n"); ++ for (i = 0; i < 0x24; i++) { ++ dprintk(1, "FI[%02x] = %02x\n", i, ++ cx23885_flatiron_read(dev, i)); ++ } ++} ++ ++static int cx23885_flatiron_mux(struct cx23885_dev *dev, int input) ++{ ++ u8 val; ++ dprintk(1, "%s(input = %d)\n", __func__, input); ++ ++ if (input == 1) ++ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) & ~FLD_CH_SEL; ++ else if (input == 2) ++ val = cx23885_flatiron_read(dev, CH_PWR_CTRL1) | FLD_CH_SEL; ++ else ++ return -EINVAL; ++ ++ val |= 0x20; /* Enable clock to delta-sigma and dec filter */ ++ ++ cx23885_flatiron_write(dev, CH_PWR_CTRL1, val); ++ ++ /* Wake up */ ++ cx23885_flatiron_write(dev, CH_PWR_CTRL2, 0); ++ ++ if (video_debug) ++ cx23885_flatiron_dump(dev); ++ ++ return 0; ++} ++ ++static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) ++{ ++ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", ++ __func__, ++ input, INPUT(input)->vmux, ++ INPUT(input)->gpio0, INPUT(input)->gpio1, ++ INPUT(input)->gpio2, INPUT(input)->gpio3); ++ dev->input = input; ++ ++ if (dev->board == CX23885_BOARD_MYGICA_X8506 || ++ dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2 || ++ dev->board == CX23885_BOARD_MYGICA_X8507) { ++ /* Select Analog TV */ ++ if (INPUT(input)->type == CX23885_VMUX_TELEVISION) ++ cx23885_gpio_clear(dev, GPIO_0); ++ } ++ ++ /* Tell the internal A/V decoder */ ++ v4l2_subdev_call(dev->sd_cx25840, video, s_routing, ++ INPUT(input)->vmux, 0, 0); ++ ++ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) || ++ (dev->board == CX23885_BOARD_MPX885) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || ++ (dev->board == CX23885_BOARD_MYGICA_X8507) || ++ (dev->board == CX23885_BOARD_AVERMEDIA_HC81R)) { ++ /* Configure audio routing */ ++ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, ++ INPUT(input)->amux, 0, 0); ++ ++ if (INPUT(input)->amux == CX25840_AUDIO7) ++ cx23885_flatiron_mux(dev, 1); ++ else if (INPUT(input)->amux == CX25840_AUDIO6) ++ cx23885_flatiron_mux(dev, 2); ++ } ++ ++ return 0; ++} ++ ++static int cx23885_audio_mux(struct cx23885_dev *dev, unsigned int input) ++{ ++ dprintk(1, "%s(input=%d)\n", __func__, input); ++ ++ /* The baseband video core of the cx23885 has two audio inputs. ++ * LR1 and LR2. In almost every single case so far only HVR1xxx ++ * cards we've only ever supported LR1. Time to support LR2, ++ * which is available via the optional white breakout header on ++ * the board. ++ * We'll use a could of existing enums in the card struct to allow ++ * devs to specify which baseband input they need, or just default ++ * to what we've always used. ++ */ ++ if (INPUT(input)->amux == CX25840_AUDIO7) ++ cx23885_flatiron_mux(dev, 1); ++ else if (INPUT(input)->amux == CX25840_AUDIO6) ++ cx23885_flatiron_mux(dev, 2); ++ else { ++ /* Not specifically defined, assume the default. */ ++ cx23885_flatiron_mux(dev, 1); ++ } ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++static int cx23885_start_video_dma(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q, ++ struct cx23885_buffer *buf) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ /* Stop the dma/fifo before we tamper with it's risc programs */ ++ cx_clear(VID_A_DMA_CTL, 0x11); ++ ++ /* setup fifo + format */ ++ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], ++ buf->bpl, buf->risc.dma); ++ ++ /* reset counter */ ++ cx_write(VID_A_GPCNT_CTL, 3); ++ q->count = 1; ++ ++ /* enable irq */ ++ cx23885_irq_add_enable(dev, 0x01); ++ cx_set(VID_A_INT_MSK, 0x000011); ++ ++ /* start dma */ ++ cx_set(DEV_CNTRL2, (1<<5)); ++ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */ ++ ++ return 0; ++} ++ ++ ++static int cx23885_restart_video_queue(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q) ++{ ++ struct cx23885_buffer *buf, *prev; ++ struct list_head *item; ++ dprintk(1, "%s()\n", __func__); ++ ++ if (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx23885_buffer, ++ vb.queue); ++ dprintk(2, "restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ cx23885_start_video_dma(dev, q, buf); ++ list_for_each(item, &q->active) { ++ buf = list_entry(item, struct cx23885_buffer, ++ vb.queue); ++ buf->count = q->count++; ++ } ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++ } ++ ++ prev = NULL; ++ for (;;) { ++ if (list_empty(&q->queued)) ++ return 0; ++ buf = list_entry(q->queued.next, struct cx23885_buffer, ++ vb.queue); ++ if (NULL == prev) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ cx23885_start_video_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2, "[%p/%d] restart_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ ++ dprintk(2, "[%p/%d] restart_queue - move to active\n", ++ buf, buf->vb.i); ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++} ++ ++static int buffer_setup(struct videobuf_queue *q, unsigned int *count, ++ unsigned int *size) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ ++ *size = fh->fmt->depth*fh->width*fh->height >> 3; ++ if (0 == *count) ++ *count = 32; ++ if (*size * *count > vid_limit * 1024 * 1024) ++ *count = (vid_limit * 1024 * 1024) / *size; ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx23885_fh *fh = q->priv_data; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx23885_buffer *buf = ++ container_of(vb, struct cx23885_buffer, vb); ++ int rc, init_buffer = 0; ++ u32 line0_offset, line1_offset; ++ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); ++ int field_tff; ++ ++ BUG_ON(NULL == fh->fmt); ++ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) || ++ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm)) ++ return -EINVAL; ++ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; ++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ ++ if (buf->fmt != fh->fmt || ++ buf->vb.width != fh->width || ++ buf->vb.height != fh->height || ++ buf->vb.field != field) { ++ buf->fmt = fh->fmt; ++ buf->vb.width = fh->width; ++ buf->vb.height = fh->height; ++ buf->vb.field = field; ++ init_buffer = 1; ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ init_buffer = 1; ++ rc = videobuf_iolock(q, &buf->vb, NULL); ++ if (0 != rc) ++ goto fail; ++ } ++ ++ if (init_buffer) { ++ buf->bpl = buf->vb.width * buf->fmt->depth >> 3; ++ switch (buf->vb.field) { ++ case V4L2_FIELD_TOP: ++ cx23885_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, 0, UNSET, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ cx23885_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, UNSET, 0, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ if (dev->tvnorm & V4L2_STD_NTSC) ++ /* NTSC or */ ++ field_tff = 1; ++ else ++ field_tff = 0; ++ ++ if (cx23885_boards[dev->board].force_bff) ++ /* PAL / SECAM OR 888 in NTSC MODE */ ++ field_tff = 0; ++ ++ if (field_tff) { ++ /* cx25840 transmits NTSC bottom field first */ ++ dprintk(1, "%s() Creating TFF/NTSC risc\n", ++ __func__); ++ line0_offset = buf->bpl; ++ line1_offset = 0; ++ } else { ++ /* All other formats are top field first */ ++ dprintk(1, "%s() Creating BFF/PAL/SECAM risc\n", ++ __func__); ++ line0_offset = 0; ++ line1_offset = buf->bpl; ++ } ++ cx23885_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, line0_offset, ++ line1_offset, ++ buf->bpl, buf->bpl, ++ buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ cx23885_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ 0, buf->bpl * (buf->vb.height >> 1), ++ buf->bpl, 0, ++ buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_BT: ++ cx23885_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ buf->bpl * (buf->vb.height >> 1), 0, ++ buf->bpl, 0, ++ buf->vb.height >> 1); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", ++ buf, buf->vb.i, ++ fh->width, fh->height, fh->fmt->depth, fh->fmt->name, ++ (unsigned long)buf->risc.dma); ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx23885_free_buffer(q, buf); ++ return rc; ++} ++ ++static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct cx23885_buffer *buf = container_of(vb, ++ struct cx23885_buffer, vb); ++ struct cx23885_buffer *prev; ++ struct cx23885_fh *fh = vq->priv_data; ++ struct cx23885_dev *dev = fh->dev; ++ struct cx23885_dmaqueue *q = &dev->vidq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if (!list_empty(&q->queued)) { ++ list_add_tail(&buf->vb.queue, &q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", ++ buf, buf->vb.i); ++ ++ } else if (list_empty(&q->active)) { ++ list_add_tail(&buf->vb.queue, &q->active); ++ cx23885_start_video_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2, "[%p/%d] buffer_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ prev = list_entry(q->active.prev, struct cx23885_buffer, ++ vb.queue); ++ if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_add_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ /* 64 bit bits 63-32 */ ++ prev->risc.jmp[2] = cpu_to_le32(0); ++ dprintk(2, "[%p/%d] buffer_queue - append to active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ list_add_tail(&buf->vb.queue, &q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - first queued\n", ++ buf, buf->vb.i); ++ } ++ } ++} ++ ++static void buffer_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ struct cx23885_buffer *buf = container_of(vb, ++ struct cx23885_buffer, vb); ++ ++ cx23885_free_buffer(q, buf); ++} ++ ++static struct videobuf_queue_ops cx23885_video_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++static struct videobuf_queue *get_queue(struct cx23885_fh *fh) ++{ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &fh->vidq; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ return &fh->vbiq; ++ default: ++ BUG(); ++ return NULL; ++ } ++} ++ ++static int get_resource(struct cx23885_fh *fh) ++{ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return RESOURCE_VIDEO; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ return RESOURCE_VBI; ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static int video_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx23885_dev *dev = video_drvdata(file); ++ struct cx23885_fh *fh; ++ enum v4l2_buf_type type = 0; ++ int radio = 0; ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ break; ++ case VFL_TYPE_VBI: ++ type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ break; ++ case VFL_TYPE_RADIO: ++ radio = 1; ++ break; ++ } ++ ++ dprintk(1, "open dev=%s radio=%d type=%s\n", ++ video_device_node_name(vdev), radio, v4l2_type_names[type]); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ file->private_data = fh; ++ fh->dev = dev; ++ fh->radio = radio; ++ fh->type = type; ++ fh->width = 320; ++ fh->height = 240; ++ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); ++ ++ videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct cx23885_buffer), ++ fh, NULL); ++ ++ videobuf_queue_sg_init(&fh->vbiq, &cx23885_vbi_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VBI_CAPTURE, ++ V4L2_FIELD_SEQ_TB, ++ sizeof(struct cx23885_buffer), ++ fh, NULL); ++ ++ ++ dprintk(1, "post videobuf_queue_init()\n"); ++ ++ return 0; ++} ++ ++static ssize_t video_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (res_locked(fh->dev, RESOURCE_VIDEO)) ++ return -EBUSY; ++ return videobuf_read_one(&fh->vidq, data, count, ppos, ++ file->f_flags & O_NONBLOCK); ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ if (!res_get(fh->dev, fh, RESOURCE_VBI)) ++ return -EBUSY; ++ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, ++ file->f_flags & O_NONBLOCK); ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static unsigned int video_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_buffer *buf; ++ unsigned int rc = POLLERR; ++ ++ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { ++ if (!res_get(fh->dev, fh, RESOURCE_VBI)) ++ return POLLERR; ++ return videobuf_poll_stream(file, &fh->vbiq, wait); ++ } ++ ++ mutex_lock(&fh->vidq.vb_lock); ++ if (res_check(fh, RESOURCE_VIDEO)) { ++ /* streaming capture */ ++ if (list_empty(&fh->vidq.stream)) ++ goto done; ++ buf = list_entry(fh->vidq.stream.next, ++ struct cx23885_buffer, vb.stream); ++ } else { ++ /* read() capture */ ++ buf = (struct cx23885_buffer *)fh->vidq.read_buf; ++ if (NULL == buf) ++ goto done; ++ } ++ poll_wait(file, &buf->vb.done, wait); ++ if (buf->vb.state == VIDEOBUF_DONE || ++ buf->vb.state == VIDEOBUF_ERROR) ++ rc = POLLIN|POLLRDNORM; ++ else ++ rc = 0; ++done: ++ mutex_unlock(&fh->vidq.vb_lock); ++ return rc; ++} ++ ++static int video_release(struct file *file) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ struct cx23885_dev *dev = fh->dev; ++ ++ /* turn off overlay */ ++ if (res_check(fh, RESOURCE_OVERLAY)) { ++ /* FIXME */ ++ res_free(dev, fh, RESOURCE_OVERLAY); ++ } ++ ++ /* stop video capture */ ++ if (res_check(fh, RESOURCE_VIDEO)) { ++ videobuf_queue_cancel(&fh->vidq); ++ res_free(dev, fh, RESOURCE_VIDEO); ++ } ++ if (fh->vidq.read_buf) { ++ buffer_release(&fh->vidq, fh->vidq.read_buf); ++ kfree(fh->vidq.read_buf); ++ } ++ ++ /* stop vbi capture */ ++ if (res_check(fh, RESOURCE_VBI)) { ++ if (fh->vbiq.streaming) ++ videobuf_streamoff(&fh->vbiq); ++ if (fh->vbiq.reading) ++ videobuf_read_stop(&fh->vbiq); ++ res_free(dev, fh, RESOURCE_VBI); ++ } ++ ++ videobuf_mmap_free(&fh->vidq); ++ videobuf_mmap_free(&fh->vbiq); ++ ++ file->private_data = NULL; ++ kfree(fh); ++ ++ /* We are not putting the tuner to sleep here on exit, because ++ * we want to use the mpeg encoder in another session to capture ++ * tuner video. Closing this will result in no video to the encoder. ++ */ ++ ++ return 0; ++} ++ ++static int video_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct cx23885_fh *fh = file->private_data; ++ ++ return videobuf_mmap_mapper(get_queue(fh), vma); ++} ++ ++/* ------------------------------------------------------------------ */ ++/* VIDEO CTRL IOCTLS */ ++ ++int cx23885_get_control(struct cx23885_dev *dev, ++ struct v4l2_control *ctl) ++{ ++ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); ++ call_all(dev, core, g_ctrl, ctl); ++ return 0; ++} ++ ++int cx23885_set_control(struct cx23885_dev *dev, ++ struct v4l2_control *ctl) ++{ ++ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)\n", __func__); ++ call_all(dev, core, s_ctrl, ctl); ++ ++ return 0; ++} ++ ++static void init_controls(struct cx23885_dev *dev) ++{ ++ struct v4l2_control ctrl; ++ int i; ++ ++ for (i = 0; i < CX23885_CTLS; i++) { ++ ctrl.id = cx23885_ctls[i].v.id; ++ ctrl.value = cx23885_ctls[i].v.default_value; ++ ++ cx23885_set_control(dev, &ctrl); ++ } ++} ++ ++/* ------------------------------------------------------------------ */ ++/* VIDEO IOCTLS */ ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = priv; ++ ++ f->fmt.pix.width = fh->width; ++ f->fmt.pix.height = fh->height; ++ f->fmt.pix.field = fh->vidq.field; ++ f->fmt.pix.pixelformat = fh->fmt->fourcc; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fh->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ struct cx23885_fmt *fmt; ++ enum v4l2_field field; ++ unsigned int maxw, maxh; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ maxw = norm_maxw(dev->tvnorm); ++ maxh = norm_maxh(dev->tvnorm); ++ ++ if (V4L2_FIELD_ANY == field) { ++ field = (f->fmt.pix.height > maxh/2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_BOTTOM; ++ } ++ ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ f->fmt.pix.field = field; ++ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, ++ &f->fmt.pix.height, 32, maxh, 0, 0); ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int err; ++ ++ dprintk(2, "%s()\n", __func__); ++ err = vidioc_try_fmt_vid_cap(file, priv, f); ++ ++ if (0 != err) ++ return err; ++ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ fh->width = f->fmt.pix.width; ++ fh->height = f->fmt.pix.height; ++ fh->vidq.field = f->fmt.pix.field; ++ dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, ++ fh->width, fh->height, fh->vidq.field); ++ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); ++ call_all(dev, video, s_mbus_fmt, &mbus_fmt); ++ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); ++ return 0; ++} ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ strcpy(cap->driver, "cx23885"); ++ strlcpy(cap->card, cx23885_boards[dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING | ++ V4L2_CAP_VBI_CAPTURE; ++ if (UNSET != dev->tuner_type) ++ cap->capabilities |= V4L2_CAP_TUNER; ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (unlikely(f->index >= ARRAY_SIZE(formats))) ++ return -EINVAL; ++ ++ strlcpy(f->description, formats[f->index].name, ++ sizeof(f->description)); ++ f->pixelformat = formats[f->index].fourcc; ++ ++ return 0; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct cx23885_fh *fh = priv; ++ return videobuf_reqbufs(get_queue(fh), p); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx23885_fh *fh = priv; ++ return videobuf_querybuf(get_queue(fh), p); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx23885_fh *fh = priv; ++ return videobuf_qbuf(get_queue(fh), p); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx23885_fh *fh = priv; ++ return videobuf_dqbuf(get_queue(fh), p, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type i) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ dprintk(1, "%s()\n", __func__); ++ ++ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ if (unlikely(i != fh->type)) ++ return -EINVAL; ++ ++ if (unlikely(!res_get(dev, fh, get_resource(fh)))) ++ return -EBUSY; ++ ++ /* Don't start VBI streaming unless vida streaming ++ * has already started. ++ */ ++ if ((fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) && ++ ((cx_read(VID_A_DMA_CTL) & 0x11) == 0)) ++ return -EINVAL; ++ ++ return videobuf_streamon(get_queue(fh)); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ int err, res; ++ dprintk(1, "%s()\n", __func__); ++ ++ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && ++ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ if (i != fh->type) ++ return -EINVAL; ++ ++ res = get_resource(fh); ++ err = videobuf_streamoff(get_queue(fh)); ++ if (err < 0) ++ return err; ++ res_free(dev, fh, res); ++ return 0; ++} ++ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ dprintk(1, "%s()\n", __func__); ++ ++ call_all(dev, core, g_std, id); ++ ++ return 0; ++} ++ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ dprintk(1, "%s()\n", __func__); ++ ++ mutex_lock(&dev->lock); ++ cx23885_set_tvnorm(dev, *tvnorms); ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) ++{ ++ static const char *iname[] = { ++ [CX23885_VMUX_COMPOSITE1] = "Composite1", ++ [CX23885_VMUX_COMPOSITE2] = "Composite2", ++ [CX23885_VMUX_COMPOSITE3] = "Composite3", ++ [CX23885_VMUX_COMPOSITE4] = "Composite4", ++ [CX23885_VMUX_SVIDEO] = "S-Video", ++ [CX23885_VMUX_COMPONENT] = "Component", ++ [CX23885_VMUX_TELEVISION] = "Television", ++ [CX23885_VMUX_CABLE] = "Cable TV", ++ [CX23885_VMUX_DVB] = "DVB", ++ [CX23885_VMUX_DEBUG] = "for debug only", ++ }; ++ unsigned int n; ++ dprintk(1, "%s()\n", __func__); ++ ++ n = i->index; ++ if (n >= MAX_CX23885_INPUT) ++ return -EINVAL; ++ ++ if (0 == INPUT(n)->type) ++ return -EINVAL; ++ ++ i->index = n; ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ strcpy(i->name, iname[INPUT(n)->type]); ++ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) || ++ (CX23885_VMUX_CABLE == INPUT(n)->type)) { ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ i->std = CX23885_NORMS; ++ } ++ ++ /* Two selectable audio inputs for non-tv inputs */ ++ if (INPUT(n)->type != CX23885_VMUX_TELEVISION) ++ i->audioset = 0x3; ++ ++ if (dev->input == n) { ++ /* enum'd input matches our configured input. ++ * Ask the video decoder to process the call ++ * and give it an oppertunity to update the ++ * status field. ++ */ ++ call_all(dev, video, g_input_status, &i->status); ++ } ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ dprintk(1, "%s()\n", __func__); ++ return cx23885_enum_input(dev, i); ++} ++ ++int cx23885_get_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ *i = dev->input; ++ dprintk(1, "%s() returns %d\n", __func__, *i); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ return cx23885_get_input(file, priv, i); ++} ++ ++int cx23885_set_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ dprintk(1, "%s(%d)\n", __func__, i); ++ ++ if (i >= MAX_CX23885_INPUT) { ++ dprintk(1, "%s() -EINVAL\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (INPUT(i)->type == 0) ++ return -EINVAL; ++ ++ mutex_lock(&dev->lock); ++ cx23885_video_mux(dev, i); ++ ++ /* By default establish the default audio input for the card also */ ++ /* Caller is free to use VIDIOC_S_AUDIO to override afterwards */ ++ cx23885_audio_mux(dev, i); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ return cx23885_set_input(file, priv, i); ++} ++ ++static int vidioc_log_status(struct file *file, void *priv) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ printk(KERN_INFO ++ "%s/0: ============ START LOG STATUS ============\n", ++ dev->name); ++ call_all(dev, core, log_status); ++ printk(KERN_INFO ++ "%s/0: ============= END LOG STATUS =============\n", ++ dev->name); ++ return 0; ++} ++ ++static int cx23885_query_audinput(struct file *file, void *priv, ++ struct v4l2_audio *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ static const char *iname[] = { ++ [0] = "Baseband L/R 1", ++ [1] = "Baseband L/R 2", ++ }; ++ unsigned int n; ++ dprintk(1, "%s()\n", __func__); ++ ++ n = i->index; ++ if (n >= 2) ++ return -EINVAL; ++ ++ memset(i, 0, sizeof(*i)); ++ i->index = n; ++ strcpy(i->name, iname[n]); ++ i->capability = V4L2_AUDCAP_STEREO; ++ i->mode = V4L2_AUDMODE_AVL; ++ return 0; ++ ++} ++ ++static int vidioc_enum_audinput(struct file *file, void *priv, ++ struct v4l2_audio *i) ++{ ++ return cx23885_query_audinput(file, priv, i); ++} ++ ++static int vidioc_g_audinput(struct file *file, void *priv, ++ struct v4l2_audio *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ i->index = dev->audinput; ++ dprintk(1, "%s(input=%d)\n", __func__, i->index); ++ ++ return cx23885_query_audinput(file, priv, i); ++} ++ ++static int vidioc_s_audinput(struct file *file, void *priv, ++ const struct v4l2_audio *i) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ if (i->index >= 2) ++ return -EINVAL; ++ ++ dprintk(1, "%s(%d)\n", __func__, i->index); ++ ++ dev->audinput = i->index; ++ ++ /* Skip the audio defaults from the cards struct, caller wants ++ * directly touch the audio mux hardware. */ ++ cx23885_flatiron_mux(dev, dev->audinput + 1); ++ return 0; ++} ++ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *qctrl) ++{ ++ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); ++ if (unlikely(qctrl->id == 0)) ++ return -EINVAL; ++ return cx23885_ctrl_query(qctrl); ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ return cx23885_get_control(dev, ctl); ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ return cx23885_set_control(dev, ctl); ++} ++ ++static int vidioc_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ if (unlikely(UNSET == dev->tuner_type)) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "Television"); ++ ++ call_all(dev, tuner, g_tuner, t); ++ return 0; ++} ++ ++static int vidioc_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; ++ ++ if (UNSET == dev->tuner_type) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ /* Update the A/V core */ ++ call_all(dev, tuner, s_tuner, t); ++ ++ return 0; ++} ++ ++static int vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ ++ if (unlikely(UNSET == dev->tuner_type)) ++ return -EINVAL; ++ ++ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ ++ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ++ f->frequency = dev->freq; ++ ++ call_all(dev, tuner, g_frequency, f); ++ ++ return 0; ++} ++ ++static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) ++{ ++ struct v4l2_control ctrl; ++ ++ if (unlikely(UNSET == dev->tuner_type)) ++ return -EINVAL; ++ if (unlikely(f->tuner != 0)) ++ return -EINVAL; ++ ++ mutex_lock(&dev->lock); ++ dev->freq = f->frequency; ++ ++ /* I need to mute audio here */ ++ ctrl.id = V4L2_CID_AUDIO_MUTE; ++ ctrl.value = 1; ++ cx23885_set_control(dev, &ctrl); ++ ++ call_all(dev, tuner, s_frequency, f); ++ ++ /* When changing channels it is required to reset TVAUDIO */ ++ msleep(100); ++ ++ /* I need to unmute audio here */ ++ ctrl.value = 0; ++ cx23885_set_control(dev, &ctrl); ++ ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, ++ struct v4l2_frequency *f) ++{ ++ struct v4l2_control ctrl; ++ struct videobuf_dvb_frontend *vfe; ++ struct dvb_frontend *fe; ++ ++ struct analog_parameters params = { ++ .mode = V4L2_TUNER_ANALOG_TV, ++ .audmode = V4L2_TUNER_MODE_STEREO, ++ .std = dev->tvnorm, ++ .frequency = f->frequency ++ }; ++ ++ mutex_lock(&dev->lock); ++ dev->freq = f->frequency; ++ ++ /* I need to mute audio here */ ++ ctrl.id = V4L2_CID_AUDIO_MUTE; ++ ctrl.value = 1; ++ cx23885_set_control(dev, &ctrl); ++ ++ /* If HVR1850 */ ++ dprintk(1, "%s() frequency=%d tuner=%d std=0x%llx\n", __func__, ++ params.frequency, f->tuner, params.std); ++ ++ vfe = videobuf_dvb_get_frontend(&dev->ts2.frontends, 1); ++ if (!vfe) { ++ mutex_unlock(&dev->lock); ++ return -EINVAL; ++ } ++ ++ fe = vfe->dvb.frontend; ++ ++ if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || ++ (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111)) ++ fe = &dev->ts1.analog_fe; ++ ++ if (fe && fe->ops.tuner_ops.set_analog_params) { ++ call_all(dev, core, s_std, dev->tvnorm); ++ fe->ops.tuner_ops.set_analog_params(fe, ¶ms); ++ } ++ else ++ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); ++ ++ /* When changing channels it is required to reset TVAUDIO */ ++ msleep(100); ++ ++ /* I need to unmute audio here */ ++ ctrl.value = 0; ++ cx23885_set_control(dev, &ctrl); ++ ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++int cx23885_set_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx23885_fh *fh = priv; ++ struct cx23885_dev *dev = fh->dev; ++ int ret; ++ ++ switch (dev->board) { ++ case CX23885_BOARD_HAUPPAUGE_HVR1255: ++ case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: ++ case CX23885_BOARD_HAUPPAUGE_HVR1850: ++ ret = cx23885_set_freq_via_ops(dev, f); ++ break; ++ default: ++ ret = cx23885_set_freq(dev, f); ++ } ++ ++ return ret; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ return cx23885_set_frequency(file, priv, f); ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static void cx23885_vid_timeout(unsigned long data) ++{ ++ struct cx23885_dev *dev = (struct cx23885_dev *)data; ++ struct cx23885_dmaqueue *q = &dev->vidq; ++ struct cx23885_buffer *buf; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->slock, flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, ++ struct cx23885_buffer, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ printk(KERN_ERR "%s: [%p/%d] timeout - dma=0x%08lx\n", ++ dev->name, buf, buf->vb.i, ++ (unsigned long)buf->risc.dma); ++ } ++ cx23885_restart_video_queue(dev, q); ++ spin_unlock_irqrestore(&dev->slock, flags); ++} ++ ++int cx23885_video_irq(struct cx23885_dev *dev, u32 status) ++{ ++ u32 mask, count; ++ int handled = 0; ++ ++ mask = cx_read(VID_A_INT_MSK); ++ if (0 == (status & mask)) ++ return handled; ++ ++ cx_write(VID_A_INT_STAT, status); ++ ++ /* risc op code error, fifo overflow or line sync detection error */ ++ if ((status & VID_BC_MSK_OPC_ERR) || ++ (status & VID_BC_MSK_SYNC) || ++ (status & VID_BC_MSK_OF)) { ++ ++ if (status & VID_BC_MSK_OPC_ERR) { ++ dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n", ++ VID_BC_MSK_OPC_ERR); ++ printk(KERN_WARNING "%s: video risc op code error\n", ++ dev->name); ++ cx23885_sram_channel_dump(dev, ++ &dev->sram_channels[SRAM_CH01]); ++ } ++ ++ if (status & VID_BC_MSK_SYNC) ++ dprintk(7, " (VID_BC_MSK_SYNC 0x%08x) " ++ "video lines miss-match\n", ++ VID_BC_MSK_SYNC); ++ ++ if (status & VID_BC_MSK_OF) ++ dprintk(7, " (VID_BC_MSK_OF 0x%08x) fifo overflow\n", ++ VID_BC_MSK_OF); ++ ++ } ++ ++ /* Video */ ++ if (status & VID_BC_MSK_RISCI1) { ++ spin_lock(&dev->slock); ++ count = cx_read(VID_A_GPCNT); ++ cx23885_video_wakeup(dev, &dev->vidq, count); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ if (status & VID_BC_MSK_RISCI2) { ++ dprintk(2, "stopper video\n"); ++ spin_lock(&dev->slock); ++ cx23885_restart_video_queue(dev, &dev->vidq); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ ++ /* Allow the VBI framework to process it's payload */ ++ handled += cx23885_vbi_irq(dev, status); ++ ++ return handled; ++} ++ ++/* ----------------------------------------------------------- */ ++/* exported stuff */ ++ ++static const struct v4l2_file_operations video_fops = { ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .release = video_release, ++ .read = video_read, ++ .poll = video_poll, ++ .mmap = video_mmap, ++ .ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops video_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt, ++ .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt, ++ .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_g_std = vidioc_g_std, ++ .vidioc_querystd = vidioc_g_std, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_log_status = vidioc_log_status, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_g_chip_ident = cx23885_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = cx23885_g_register, ++ .vidioc_s_register = cx23885_s_register, ++#endif ++ .vidioc_enumaudio = vidioc_enum_audinput, ++ .vidioc_g_audio = vidioc_g_audinput, ++ .vidioc_s_audio = vidioc_s_audinput, ++}; ++ ++static struct video_device cx23885_vbi_template; ++static struct video_device cx23885_video_template = { ++ .name = "cx23885-video", ++ .fops = &video_fops, ++ .ioctl_ops = &video_ioctl_ops, ++ .tvnorms = CX23885_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++}; ++ ++static const struct v4l2_file_operations radio_fops = { ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .release = video_release, ++ .ioctl = video_ioctl2, ++}; ++ ++ ++void cx23885_video_unregister(struct cx23885_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ cx23885_irq_remove(dev, 0x01); ++ ++ if (dev->vbi_dev) { ++ if (video_is_registered(dev->vbi_dev)) ++ video_unregister_device(dev->vbi_dev); ++ else ++ video_device_release(dev->vbi_dev); ++ dev->vbi_dev = NULL; ++ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper); ++ } ++ if (dev->video_dev) { ++ if (video_is_registered(dev->video_dev)) ++ video_unregister_device(dev->video_dev); ++ else ++ video_device_release(dev->video_dev); ++ dev->video_dev = NULL; ++ ++ btcx_riscmem_free(dev->pci, &dev->vidq.stopper); ++ } ++ ++ if (dev->audio_dev) ++ cx23885_audio_unregister(dev); ++} ++ ++int cx23885_video_register(struct cx23885_dev *dev) ++{ ++ int err; ++ ++ dprintk(1, "%s()\n", __func__); ++ spin_lock_init(&dev->slock); ++ ++ /* Initialize VBI template */ ++ cx23885_vbi_template = cx23885_video_template; ++ strcpy(cx23885_vbi_template.name, "cx23885-vbi"); ++ ++ dev->tvnorm = cx23885_video_template.current_norm; ++ ++ /* init video dma queues */ ++ INIT_LIST_HEAD(&dev->vidq.active); ++ INIT_LIST_HEAD(&dev->vidq.queued); ++ dev->vidq.timeout.function = cx23885_vid_timeout; ++ dev->vidq.timeout.data = (unsigned long)dev; ++ init_timer(&dev->vidq.timeout); ++ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper, ++ VID_A_DMA_CTL, 0x11, 0x00); ++ ++ /* init vbi dma queues */ ++ INIT_LIST_HEAD(&dev->vbiq.active); ++ INIT_LIST_HEAD(&dev->vbiq.queued); ++ dev->vbiq.timeout.function = cx23885_vbi_timeout; ++ dev->vbiq.timeout.data = (unsigned long)dev; ++ init_timer(&dev->vbiq.timeout); ++ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper, ++ VID_A_DMA_CTL, 0x22, 0x00); ++ ++ cx23885_irq_add_enable(dev, 0x01); ++ ++ if ((TUNER_ABSENT != dev->tuner_type) && ++ ((dev->tuner_bus == 0) || (dev->tuner_bus == 1))) { ++ struct v4l2_subdev *sd = NULL; ++ ++ if (dev->tuner_addr) ++ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_bus[dev->tuner_bus].i2c_adap, ++ "tuner", dev->tuner_addr, NULL); ++ else ++ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_bus[dev->tuner_bus].i2c_adap, ++ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_TV)); ++ if (sd) { ++ struct tuner_setup tun_setup; ++ ++ memset(&tun_setup, 0, sizeof(tun_setup)); ++ tun_setup.mode_mask = T_ANALOG_TV; ++ tun_setup.type = dev->tuner_type; ++ tun_setup.addr = v4l2_i2c_subdev_addr(sd); ++ tun_setup.tuner_callback = cx23885_tuner_callback; ++ ++ v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); ++ ++ if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) { ++ struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64 ++ }; ++ struct v4l2_priv_tun_config cfg = { ++ .tuner = dev->tuner_type, ++ .priv = &ctrl ++ }; ++ v4l2_subdev_call(sd, tuner, s_config, &cfg); ++ } ++ ++ if (dev->board == CX23885_BOARD_AVERMEDIA_HC81R) { ++ struct xc2028_ctrl ctrl = { ++ .fname = "xc3028L-v36.fw", ++ .max_len = 64 ++ }; ++ struct v4l2_priv_tun_config cfg = { ++ .tuner = dev->tuner_type, ++ .priv = &ctrl ++ }; ++ v4l2_subdev_call(sd, tuner, s_config, &cfg); ++ } ++ } ++ } ++ ++ /* register Video device */ ++ dev->video_dev = cx23885_vdev_init(dev, dev->pci, ++ &cx23885_video_template, "video"); ++ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, ++ video_nr[dev->nr]); ++ if (err < 0) { ++ printk(KERN_INFO "%s: can't register video device\n", ++ dev->name); ++ goto fail_unreg; ++ } ++ printk(KERN_INFO "%s: registered device %s [v4l2]\n", ++ dev->name, video_device_node_name(dev->video_dev)); ++ ++ /* register VBI device */ ++ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci, ++ &cx23885_vbi_template, "vbi"); ++ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, ++ vbi_nr[dev->nr]); ++ if (err < 0) { ++ printk(KERN_INFO "%s: can't register vbi device\n", ++ dev->name); ++ goto fail_unreg; ++ } ++ printk(KERN_INFO "%s: registered device %s\n", ++ dev->name, video_device_node_name(dev->vbi_dev)); ++ ++ /* Register ALSA audio device */ ++ dev->audio_dev = cx23885_audio_register(dev); ++ ++ /* initial device configuration */ ++ mutex_lock(&dev->lock); ++ cx23885_set_tvnorm(dev, dev->tvnorm); ++ init_controls(dev); ++ cx23885_video_mux(dev, 0); ++ cx23885_audio_mux(dev, 0); ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++ ++fail_unreg: ++ cx23885_video_unregister(dev); ++ return err; ++} ++ +diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h +new file mode 100644 +index 0000000..59c322d +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23885.h +@@ -0,0 +1,656 @@ ++/* ++ * Driver for the Conexant CX23885 PCIe bridge ++ * ++ * Copyright (c) 2006 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "btcx-risc.h" ++#include "cx23885-reg.h" ++#include "media/cx2341x.h" ++ ++#include ++ ++#define CX23885_VERSION "0.0.3" ++ ++#define UNSET (-1U) ++ ++#define CX23885_MAXBOARDS 8 ++ ++/* Max number of inputs by card */ ++#define MAX_CX23885_INPUT 8 ++#define INPUT(nr) (&cx23885_boards[dev->board].input[nr]) ++#define RESOURCE_OVERLAY 1 ++#define RESOURCE_VIDEO 2 ++#define RESOURCE_VBI 4 ++ ++#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ ++ ++#define CX23885_BOARD_NOAUTO UNSET ++#define CX23885_BOARD_UNKNOWN 0 ++#define CX23885_BOARD_HAUPPAUGE_HVR1800lp 1 ++#define CX23885_BOARD_HAUPPAUGE_HVR1800 2 ++#define CX23885_BOARD_HAUPPAUGE_HVR1250 3 ++#define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4 ++#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5 ++#define CX23885_BOARD_HAUPPAUGE_HVR1500 6 ++#define CX23885_BOARD_HAUPPAUGE_HVR1200 7 ++#define CX23885_BOARD_HAUPPAUGE_HVR1700 8 ++#define CX23885_BOARD_HAUPPAUGE_HVR1400 9 ++#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10 ++#define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 ++#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 ++#define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 ++#define CX23885_BOARD_TBS_6920 14 ++#define CX23885_BOARD_TEVII_S470 15 ++#define CX23885_BOARD_DVBWORLD_2005 16 ++#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17 ++#define CX23885_BOARD_HAUPPAUGE_HVR1270 18 ++#define CX23885_BOARD_HAUPPAUGE_HVR1275 19 ++#define CX23885_BOARD_HAUPPAUGE_HVR1255 20 ++#define CX23885_BOARD_HAUPPAUGE_HVR1210 21 ++#define CX23885_BOARD_MYGICA_X8506 22 ++#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 ++#define CX23885_BOARD_HAUPPAUGE_HVR1850 24 ++#define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 ++#define CX23885_BOARD_HAUPPAUGE_HVR1290 26 ++#define CX23885_BOARD_MYGICA_X8558PRO 27 ++#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28 ++#define CX23885_BOARD_GOTVIEW_X5_3D_HYBRID 29 ++#define CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF 30 ++#define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H_XC4000 31 ++#define CX23885_BOARD_MPX885 32 ++#define CX23885_BOARD_MYGICA_X8507 33 ++#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34 ++#define CX23885_BOARD_TEVII_S471 35 ++#define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 ++#define CX23885_BOARD_PROF_8000 37 ++#define CX23885_BOARD_HAUPPAUGE_HVR4400 38 ++#define CX23885_BOARD_AVERMEDIA_HC81R 39 ++ ++#define GPIO_0 0x00000001 ++#define GPIO_1 0x00000002 ++#define GPIO_2 0x00000004 ++#define GPIO_3 0x00000008 ++#define GPIO_4 0x00000010 ++#define GPIO_5 0x00000020 ++#define GPIO_6 0x00000040 ++#define GPIO_7 0x00000080 ++#define GPIO_8 0x00000100 ++#define GPIO_9 0x00000200 ++#define GPIO_10 0x00000400 ++#define GPIO_11 0x00000800 ++#define GPIO_12 0x00001000 ++#define GPIO_13 0x00002000 ++#define GPIO_14 0x00004000 ++#define GPIO_15 0x00008000 ++ ++/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ ++#define CX23885_NORMS (\ ++ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \ ++ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ ++ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ ++ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK) ++ ++struct cx23885_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ int depth; ++ int flags; ++ u32 cxformat; ++}; ++ ++struct cx23885_ctrl { ++ struct v4l2_queryctrl v; ++ u32 off; ++ u32 reg; ++ u32 mask; ++ u32 shift; ++}; ++ ++struct cx23885_tvnorm { ++ char *name; ++ v4l2_std_id id; ++ u32 cxiformat; ++ u32 cxoformat; ++}; ++ ++struct cx23885_fh { ++ struct cx23885_dev *dev; ++ enum v4l2_buf_type type; ++ int radio; ++ u32 resources; ++ ++ /* video overlay */ ++ struct v4l2_window win; ++ struct v4l2_clip *clips; ++ unsigned int nclips; ++ ++ /* video capture */ ++ struct cx23885_fmt *fmt; ++ unsigned int width, height; ++ ++ /* vbi capture */ ++ struct videobuf_queue vidq; ++ struct videobuf_queue vbiq; ++ ++ /* MPEG Encoder specifics ONLY */ ++ struct videobuf_queue mpegq; ++ atomic_t v4l_reading; ++}; ++ ++enum cx23885_itype { ++ CX23885_VMUX_COMPOSITE1 = 1, ++ CX23885_VMUX_COMPOSITE2, ++ CX23885_VMUX_COMPOSITE3, ++ CX23885_VMUX_COMPOSITE4, ++ CX23885_VMUX_SVIDEO, ++ CX23885_VMUX_COMPONENT, ++ CX23885_VMUX_TELEVISION, ++ CX23885_VMUX_CABLE, ++ CX23885_VMUX_DVB, ++ CX23885_VMUX_DEBUG, ++ CX23885_RADIO, ++}; ++ ++enum cx23885_src_sel_type { ++ CX23885_SRC_SEL_EXT_656_VIDEO = 0, ++ CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO ++}; ++ ++/* buffer for one video frame */ ++struct cx23885_buffer { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* cx23885 specific */ ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ struct cx23885_fmt *fmt; ++ u32 count; ++}; ++ ++struct cx23885_input { ++ enum cx23885_itype type; ++ unsigned int vmux; ++ unsigned int amux; ++ u32 gpio0, gpio1, gpio2, gpio3; ++}; ++ ++typedef enum { ++ CX23885_MPEG_UNDEFINED = 0, ++ CX23885_MPEG_DVB, ++ CX23885_ANALOG_VIDEO, ++ CX23885_MPEG_ENCODER, ++} port_t; ++ ++struct cx23885_board { ++ char *name; ++ port_t porta, portb, portc; ++ int num_fds_portb, num_fds_portc; ++ unsigned int tuner_type; ++ unsigned int radio_type; ++ unsigned char tuner_addr; ++ unsigned char radio_addr; ++ unsigned int tuner_bus; ++ ++ /* Vendors can and do run the PCIe bridge at different ++ * clock rates, driven physically by crystals on the PCBs. ++ * The core has to accommodate this. This allows the user ++ * to add new boards with new frequencys. The value is ++ * expressed in Hz. ++ * ++ * The core framework will default this value based on ++ * current designs, but it can vary. ++ */ ++ u32 clk_freq; ++ struct cx23885_input input[MAX_CX23885_INPUT]; ++ int ci_type; /* for NetUP */ ++ /* Force bottom field first during DMA (888 workaround) */ ++ u32 force_bff; ++}; ++ ++struct cx23885_subid { ++ u16 subvendor; ++ u16 subdevice; ++ u32 card; ++}; ++ ++struct cx23885_i2c { ++ struct cx23885_dev *dev; ++ ++ int nr; ++ ++ /* i2c i/o */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_client i2c_client; ++ u32 i2c_rc; ++ ++ /* 885 registers used for raw addess */ ++ u32 i2c_period; ++ u32 reg_ctrl; ++ u32 reg_stat; ++ u32 reg_addr; ++ u32 reg_rdata; ++ u32 reg_wdata; ++}; ++ ++struct cx23885_dmaqueue { ++ struct list_head active; ++ struct list_head queued; ++ struct timer_list timeout; ++ struct btcx_riscmem stopper; ++ u32 count; ++}; ++ ++struct cx23885_tsport { ++ struct cx23885_dev *dev; ++ ++ int nr; ++ int sram_chno; ++ ++ struct videobuf_dvb_frontends frontends; ++ ++ /* dma queues */ ++ struct cx23885_dmaqueue mpegq; ++ u32 ts_packet_size; ++ u32 ts_packet_count; ++ ++ int width; ++ int height; ++ ++ spinlock_t slock; ++ ++ /* registers */ ++ u32 reg_gpcnt; ++ u32 reg_gpcnt_ctl; ++ u32 reg_dma_ctl; ++ u32 reg_lngth; ++ u32 reg_hw_sop_ctrl; ++ u32 reg_gen_ctrl; ++ u32 reg_bd_pkt_status; ++ u32 reg_sop_status; ++ u32 reg_fifo_ovfl_stat; ++ u32 reg_vld_misc; ++ u32 reg_ts_clk_en; ++ u32 reg_ts_int_msk; ++ u32 reg_ts_int_stat; ++ u32 reg_src_sel; ++ ++ /* Default register vals */ ++ int pci_irqmask; ++ u32 dma_ctl_val; ++ u32 ts_int_msk_val; ++ u32 gen_ctrl_val; ++ u32 ts_clk_en_val; ++ u32 src_sel_val; ++ u32 vld_misc_val; ++ u32 hw_sop_ctrl_val; ++ ++ /* Allow a single tsport to have multiple frontends */ ++ u32 num_frontends; ++ void (*gate_ctrl)(struct cx23885_tsport *port, int open); ++ void *port_priv; ++ ++ /* Workaround for a temp dvb_frontend that the tuner can attached to */ ++ struct dvb_frontend analog_fe; ++}; ++ ++struct cx23885_kernel_ir { ++ struct cx23885_dev *cx; ++ char *name; ++ char *phys; ++ ++ struct rc_dev *rc; ++}; ++ ++struct cx23885_audio_buffer { ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ struct videobuf_dmabuf dma; ++}; ++ ++struct cx23885_audio_dev { ++ struct cx23885_dev *dev; ++ ++ struct pci_dev *pci; ++ ++ struct snd_card *card; ++ ++ spinlock_t lock; ++ ++ atomic_t count; ++ ++ unsigned int dma_size; ++ unsigned int period_size; ++ unsigned int num_periods; ++ ++ struct videobuf_dmabuf *dma_risc; ++ ++ struct cx23885_audio_buffer *buf; ++ ++ struct snd_pcm_substream *substream; ++}; ++ ++struct cx23885_dev { ++ atomic_t refcount; ++ struct v4l2_device v4l2_dev; ++ ++ /* pci stuff */ ++ struct pci_dev *pci; ++ unsigned char pci_rev, pci_lat; ++ int pci_bus, pci_slot; ++ u32 __iomem *lmmio; ++ u8 __iomem *bmmio; ++ int pci_irqmask; ++ spinlock_t pci_irqmask_lock; /* protects mask reg too */ ++ int hwrevision; ++ ++ /* This valud is board specific and is used to configure the ++ * AV core so we see nice clean and stable video and audio. */ ++ u32 clk_freq; ++ ++ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ ++ struct cx23885_i2c i2c_bus[3]; ++ ++ int nr; ++ struct mutex lock; ++ struct mutex gpio_lock; ++ ++ /* board details */ ++ unsigned int board; ++ char name[32]; ++ ++ struct cx23885_tsport ts1, ts2; ++ ++ /* sram configuration */ ++ struct sram_channel *sram_channels; ++ ++ enum { ++ CX23885_BRIDGE_UNDEFINED = 0, ++ CX23885_BRIDGE_885 = 885, ++ CX23885_BRIDGE_887 = 887, ++ CX23885_BRIDGE_888 = 888, ++ } bridge; ++ ++ /* Analog video */ ++ u32 resources; ++ unsigned int input; ++ unsigned int audinput; /* Selectable audio input */ ++ u32 tvaudio; ++ v4l2_std_id tvnorm; ++ unsigned int tuner_type; ++ unsigned char tuner_addr; ++ unsigned int tuner_bus; ++ unsigned int radio_type; ++ unsigned char radio_addr; ++ unsigned int has_radio; ++ struct v4l2_subdev *sd_cx25840; ++ struct work_struct cx25840_work; ++ ++ /* Infrared */ ++ struct v4l2_subdev *sd_ir; ++ struct work_struct ir_rx_work; ++ unsigned long ir_rx_notifications; ++ struct work_struct ir_tx_work; ++ unsigned long ir_tx_notifications; ++ ++ struct cx23885_kernel_ir *kernel_ir; ++ atomic_t ir_input_stopping; ++ ++ /* V4l */ ++ u32 freq; ++ struct video_device *video_dev; ++ struct video_device *vbi_dev; ++ struct video_device *radio_dev; ++ ++ struct cx23885_dmaqueue vidq; ++ struct cx23885_dmaqueue vbiq; ++ spinlock_t slock; ++ ++ /* MPEG Encoder ONLY settings */ ++ u32 cx23417_mailbox; ++ struct cx2341x_mpeg_params mpeg_params; ++ struct video_device *v4l_device; ++ atomic_t v4l_reader_count; ++ struct cx23885_tvnorm encodernorm; ++ ++ /* Analog raw audio */ ++ struct cx23885_audio_dev *audio_dev; ++ ++}; ++ ++static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev); ++} ++ ++#define call_all(dev, o, f, args...) \ ++ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) ++ ++#define CX23885_HW_888_IR (1 << 0) ++#define CX23885_HW_AV_CORE (1 << 1) ++ ++#define call_hw(dev, grpid, o, f, args...) \ ++ v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args) ++ ++extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw); ++ ++#define SRAM_CH01 0 /* Video A */ ++#define SRAM_CH02 1 /* VBI A */ ++#define SRAM_CH03 2 /* Video B */ ++#define SRAM_CH04 3 /* Transport via B */ ++#define SRAM_CH05 4 /* VBI B */ ++#define SRAM_CH06 5 /* Video C */ ++#define SRAM_CH07 6 /* Transport via C */ ++#define SRAM_CH08 7 /* Audio Internal A */ ++#define SRAM_CH09 8 /* Audio Internal B */ ++#define SRAM_CH10 9 /* Audio External */ ++#define SRAM_CH11 10 /* COMB_3D_N */ ++#define SRAM_CH12 11 /* Comb 3D N1 */ ++#define SRAM_CH13 12 /* Comb 3D N2 */ ++#define SRAM_CH14 13 /* MOE Vid */ ++#define SRAM_CH15 14 /* MOE RSLT */ ++ ++struct sram_channel { ++ char *name; ++ u32 cmds_start; ++ u32 ctrl_start; ++ u32 cdt; ++ u32 fifo_start; ++ u32 fifo_size; ++ u32 ptr1_reg; ++ u32 ptr2_reg; ++ u32 cnt1_reg; ++ u32 cnt2_reg; ++ u32 jumponly; ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++#define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) ++#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2)) ++ ++#define cx_andor(reg, mask, value) \ ++ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ ++ ((value) & (mask)), dev->lmmio+((reg)>>2)) ++ ++#define cx_set(reg, bit) cx_andor((reg), (bit), (bit)) ++#define cx_clear(reg, bit) cx_andor((reg), (bit), 0) ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-core.c */ ++ ++extern int cx23885_sram_channel_setup(struct cx23885_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc); ++ ++extern void cx23885_sram_channel_dump(struct cx23885_dev *dev, ++ struct sram_channel *ch); ++ ++extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value); ++ ++extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int top_offset, unsigned int bottom_offset, ++ unsigned int bpl, unsigned int padding, unsigned int lines); ++ ++extern int cx23885_risc_vbibuffer(struct pci_dev *pci, ++ struct btcx_riscmem *risc, struct scatterlist *sglist, ++ unsigned int top_offset, unsigned int bottom_offset, ++ unsigned int bpl, unsigned int padding, unsigned int lines); ++ ++void cx23885_cancel_buffers(struct cx23885_tsport *port); ++ ++extern int cx23885_restart_queue(struct cx23885_tsport *port, ++ struct cx23885_dmaqueue *q); ++ ++extern void cx23885_wakeup(struct cx23885_tsport *port, ++ struct cx23885_dmaqueue *q, u32 count); ++ ++extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); ++extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); ++extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask); ++extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, ++ int asoutput); ++ ++extern void cx23885_irq_add_enable(struct cx23885_dev *dev, u32 mask); ++extern void cx23885_irq_enable(struct cx23885_dev *dev, u32 mask); ++extern void cx23885_irq_disable(struct cx23885_dev *dev, u32 mask); ++extern void cx23885_irq_remove(struct cx23885_dev *dev, u32 mask); ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-cards.c */ ++extern struct cx23885_board cx23885_boards[]; ++extern const unsigned int cx23885_bcount; ++ ++extern struct cx23885_subid cx23885_subids[]; ++extern const unsigned int cx23885_idcount; ++ ++extern int cx23885_tuner_callback(void *priv, int component, ++ int command, int arg); ++extern void cx23885_card_list(struct cx23885_dev *dev); ++extern int cx23885_ir_init(struct cx23885_dev *dev); ++extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev); ++extern void cx23885_ir_fini(struct cx23885_dev *dev); ++extern void cx23885_gpio_setup(struct cx23885_dev *dev); ++extern void cx23885_card_setup(struct cx23885_dev *dev); ++extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); ++ ++extern int cx23885_dvb_register(struct cx23885_tsport *port); ++extern int cx23885_dvb_unregister(struct cx23885_tsport *port); ++ ++extern int cx23885_buf_prepare(struct videobuf_queue *q, ++ struct cx23885_tsport *port, ++ struct cx23885_buffer *buf, ++ enum v4l2_field field); ++extern void cx23885_buf_queue(struct cx23885_tsport *port, ++ struct cx23885_buffer *buf); ++extern void cx23885_free_buffer(struct videobuf_queue *q, ++ struct cx23885_buffer *buf); ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-video.c */ ++/* Video */ ++extern int cx23885_video_register(struct cx23885_dev *dev); ++extern void cx23885_video_unregister(struct cx23885_dev *dev); ++extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status); ++extern void cx23885_video_wakeup(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q, u32 count); ++int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i); ++int cx23885_set_input(struct file *file, void *priv, unsigned int i); ++int cx23885_get_input(struct file *file, void *priv, unsigned int *i); ++int cx23885_set_frequency(struct file *file, void *priv, struct v4l2_frequency *f); ++int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl); ++int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl); ++int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm); ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-vbi.c */ ++extern int cx23885_vbi_fmt(struct file *file, void *priv, ++ struct v4l2_format *f); ++extern void cx23885_vbi_timeout(unsigned long data); ++extern struct videobuf_queue_ops cx23885_vbi_qops; ++extern int cx23885_restart_vbi_queue(struct cx23885_dev *dev, ++ struct cx23885_dmaqueue *q); ++extern int cx23885_vbi_irq(struct cx23885_dev *dev, u32 status); ++ ++/* cx23885-i2c.c */ ++extern int cx23885_i2c_register(struct cx23885_i2c *bus); ++extern int cx23885_i2c_unregister(struct cx23885_i2c *bus); ++extern void cx23885_av_clk(struct cx23885_dev *dev, int enable); ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-417.c */ ++extern int cx23885_417_register(struct cx23885_dev *dev); ++extern void cx23885_417_unregister(struct cx23885_dev *dev); ++extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status); ++extern void cx23885_417_check_encoder(struct cx23885_dev *dev); ++extern void cx23885_mc417_init(struct cx23885_dev *dev); ++extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); ++extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); ++extern int mc417_register_read(struct cx23885_dev *dev, ++ u16 address, u32 *value); ++extern int mc417_register_write(struct cx23885_dev *dev, ++ u16 address, u32 value); ++extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); ++extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); ++extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); ++ ++/* ----------------------------------------------------------- */ ++/* cx23885-alsa.c */ ++extern struct cx23885_audio_dev *cx23885_audio_register( ++ struct cx23885_dev *dev); ++extern void cx23885_audio_unregister(struct cx23885_dev *dev); ++extern int cx23885_audio_irq(struct cx23885_dev *dev, u32 status, u32 mask); ++extern int cx23885_risc_databuffer(struct pci_dev *pci, ++ struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int bpl, ++ unsigned int lines, ++ unsigned int lpi); ++ ++/* ----------------------------------------------------------- */ ++/* tv norms */ ++ ++static inline unsigned int norm_maxw(v4l2_std_id norm) ++{ ++ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; ++} ++ ++static inline unsigned int norm_maxh(v4l2_std_id norm) ++{ ++ return (norm & V4L2_STD_625_50) ? 576 : 480; ++} ++ ++static inline unsigned int norm_swidth(v4l2_std_id norm) ++{ ++ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; ++} +diff --git a/drivers/media/pci/cx23885/cx23888-ir.c b/drivers/media/pci/cx23885/cx23888-ir.c +new file mode 100644 +index 0000000..d51eed0 +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23888-ir.c +@@ -0,0 +1,1270 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * CX23888 Integrated Consumer Infrared Controller ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "cx23885.h" ++#include "cx23888-ir.h" ++ ++static unsigned int ir_888_debug; ++module_param(ir_888_debug, int, 0644); ++MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); ++ ++#define CX23888_IR_REG_BASE 0x170000 ++/* ++ * These CX23888 register offsets have a straightforward one to one mapping ++ * to the CX23885 register offsets of 0x200 through 0x218 ++ */ ++#define CX23888_IR_CNTRL_REG 0x170000 ++#define CNTRL_WIN_3_3 0x00000000 ++#define CNTRL_WIN_4_3 0x00000001 ++#define CNTRL_WIN_3_4 0x00000002 ++#define CNTRL_WIN_4_4 0x00000003 ++#define CNTRL_WIN 0x00000003 ++#define CNTRL_EDG_NONE 0x00000000 ++#define CNTRL_EDG_FALL 0x00000004 ++#define CNTRL_EDG_RISE 0x00000008 ++#define CNTRL_EDG_BOTH 0x0000000C ++#define CNTRL_EDG 0x0000000C ++#define CNTRL_DMD 0x00000010 ++#define CNTRL_MOD 0x00000020 ++#define CNTRL_RFE 0x00000040 ++#define CNTRL_TFE 0x00000080 ++#define CNTRL_RXE 0x00000100 ++#define CNTRL_TXE 0x00000200 ++#define CNTRL_RIC 0x00000400 ++#define CNTRL_TIC 0x00000800 ++#define CNTRL_CPL 0x00001000 ++#define CNTRL_LBM 0x00002000 ++#define CNTRL_R 0x00004000 ++/* CX23888 specific control flag */ ++#define CNTRL_IVO 0x00008000 ++ ++#define CX23888_IR_TXCLK_REG 0x170004 ++#define TXCLK_TCD 0x0000FFFF ++ ++#define CX23888_IR_RXCLK_REG 0x170008 ++#define RXCLK_RCD 0x0000FFFF ++ ++#define CX23888_IR_CDUTY_REG 0x17000C ++#define CDUTY_CDC 0x0000000F ++ ++#define CX23888_IR_STATS_REG 0x170010 ++#define STATS_RTO 0x00000001 ++#define STATS_ROR 0x00000002 ++#define STATS_RBY 0x00000004 ++#define STATS_TBY 0x00000008 ++#define STATS_RSR 0x00000010 ++#define STATS_TSR 0x00000020 ++ ++#define CX23888_IR_IRQEN_REG 0x170014 ++#define IRQEN_RTE 0x00000001 ++#define IRQEN_ROE 0x00000002 ++#define IRQEN_RSE 0x00000010 ++#define IRQEN_TSE 0x00000020 ++ ++#define CX23888_IR_FILTR_REG 0x170018 ++#define FILTR_LPF 0x0000FFFF ++ ++/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */ ++#define CX23888_IR_FIFO_REG 0x170040 ++#define FIFO_RXTX 0x0000FFFF ++#define FIFO_RXTX_LVL 0x00010000 ++#define FIFO_RXTX_RTO 0x0001FFFF ++#define FIFO_RX_NDV 0x00020000 ++#define FIFO_RX_DEPTH 8 ++#define FIFO_TX_DEPTH 8 ++ ++/* CX23888 unique registers */ ++#define CX23888_IR_SEEDP_REG 0x17001C ++#define CX23888_IR_TIMOL_REG 0x170020 ++#define CX23888_IR_WAKE0_REG 0x170024 ++#define CX23888_IR_WAKE1_REG 0x170028 ++#define CX23888_IR_WAKE2_REG 0x17002C ++#define CX23888_IR_MASK0_REG 0x170030 ++#define CX23888_IR_MASK1_REG 0x170034 ++#define CX23888_IR_MAKS2_REG 0x170038 ++#define CX23888_IR_DPIPG_REG 0x17003C ++#define CX23888_IR_LEARN_REG 0x170044 ++ ++#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ ++#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2) ++ ++/* ++ * We use this union internally for convenience, but callers to tx_write ++ * and rx_read will be expecting records of type struct ir_raw_event. ++ * Always ensure the size of this union is dictated by struct ir_raw_event. ++ */ ++union cx23888_ir_fifo_rec { ++ u32 hw_fifo_data; ++ struct ir_raw_event ir_core_data; ++}; ++ ++#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec)) ++#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec)) ++ ++struct cx23888_ir_state { ++ struct v4l2_subdev sd; ++ struct cx23885_dev *dev; ++ u32 id; ++ u32 rev; ++ ++ struct v4l2_subdev_ir_parameters rx_params; ++ struct mutex rx_params_lock; ++ atomic_t rxclk_divider; ++ atomic_t rx_invert; ++ ++ struct kfifo rx_kfifo; ++ spinlock_t rx_kfifo_lock; ++ ++ struct v4l2_subdev_ir_parameters tx_params; ++ struct mutex tx_params_lock; ++ atomic_t txclk_divider; ++}; ++ ++static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd) ++{ ++ return v4l2_get_subdevdata(sd); ++} ++ ++/* ++ * IR register block read and write functions ++ */ ++static ++inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value) ++{ ++ cx_write(addr, value); ++ return 0; ++} ++ ++static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr) ++{ ++ return cx_read(addr); ++} ++ ++static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr, ++ u32 and_mask, u32 or_value) ++{ ++ cx_andor(addr, ~and_mask, or_value); ++ return 0; ++} ++ ++/* ++ * Rx and Tx Clock Divider register computations ++ * ++ * Note the largest clock divider value of 0xffff corresponds to: ++ * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns ++ * which fits in 21 bits, so we'll use unsigned int for time arguments. ++ */ ++static inline u16 count_to_clock_divider(unsigned int d) ++{ ++ if (d > RXCLK_RCD + 1) ++ d = RXCLK_RCD; ++ else if (d < 2) ++ d = 1; ++ else ++ d--; ++ return (u16) d; ++} ++ ++static inline u16 ns_to_clock_divider(unsigned int ns) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); ++} ++ ++static inline unsigned int clock_divider_to_ns(unsigned int divider) ++{ ++ /* Period of the Rx or Tx clock in ns */ ++ return DIV_ROUND_CLOSEST((divider + 1) * 1000, ++ CX23888_IR_REFCLK_FREQ / 1000000); ++} ++ ++static inline u16 carrier_freq_to_clock_divider(unsigned int freq) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16)); ++} ++ ++static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) ++{ ++ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); ++} ++ ++static inline u16 freq_to_clock_divider(unsigned int freq, ++ unsigned int rollovers) ++{ ++ return count_to_clock_divider( ++ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); ++} ++ ++static inline unsigned int clock_divider_to_freq(unsigned int divider, ++ unsigned int rollovers) ++{ ++ return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, ++ (divider + 1) * rollovers); ++} ++ ++/* ++ * Low Pass Filter register calculations ++ * ++ * Note the largest count value of 0xffff corresponds to: ++ * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns ++ * which fits in 21 bits, so we'll use unsigned int for time arguments. ++ */ ++static inline u16 count_to_lpf_count(unsigned int d) ++{ ++ if (d > FILTR_LPF) ++ d = FILTR_LPF; ++ else if (d < 4) ++ d = 0; ++ return (u16) d; ++} ++ ++static inline u16 ns_to_lpf_count(unsigned int ns) ++{ ++ return count_to_lpf_count( ++ DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); ++} ++ ++static inline unsigned int lpf_count_to_ns(unsigned int count) ++{ ++ /* Duration of the Low Pass Filter rejection window in ns */ ++ return DIV_ROUND_CLOSEST(count * 1000, ++ CX23888_IR_REFCLK_FREQ / 1000000); ++} ++ ++static inline unsigned int lpf_count_to_us(unsigned int count) ++{ ++ /* Duration of the Low Pass Filter rejection window in us */ ++ return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000); ++} ++ ++/* ++ * FIFO register pulse width count compuations ++ */ ++static u32 clock_divider_to_resolution(u16 divider) ++{ ++ /* ++ * Resolution is the duration of 1 tick of the readable portion of ++ * of the pulse width counter as read from the FIFO. The two lsb's are ++ * not readable, hence the << 2. This function returns ns. ++ */ ++ return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, ++ CX23888_IR_REFCLK_FREQ / 1000000); ++} ++ ++static u64 pulse_width_count_to_ns(u16 count, u16 divider) ++{ ++ u64 n; ++ u32 rem; ++ ++ /* ++ * The 2 lsb's of the pulse width timer count are not readable, hence ++ * the (count << 2) | 0x3 ++ */ ++ n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ ++ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ ++ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) ++ n++; ++ return n; ++} ++ ++static unsigned int pulse_width_count_to_us(u16 count, u16 divider) ++{ ++ u64 n; ++ u32 rem; ++ ++ /* ++ * The 2 lsb's of the pulse width timer count are not readable, hence ++ * the (count << 2) | 0x3 ++ */ ++ n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ ++ rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ ++ if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) ++ n++; ++ return (unsigned int) n; ++} ++ ++/* ++ * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts ++ * ++ * The total pulse clock count is an 18 bit pulse width timer count as the most ++ * significant part and (up to) 16 bit clock divider count as a modulus. ++ * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse ++ * width timer count's least significant bit. ++ */ ++static u64 ns_to_pulse_clocks(u32 ns) ++{ ++ u64 clocks; ++ u32 rem; ++ clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ ++ rem = do_div(clocks, 1000); /* /1000 = cycles */ ++ if (rem >= 1000 / 2) ++ clocks++; ++ return clocks; ++} ++ ++static u16 pulse_clocks_to_clock_divider(u64 count) ++{ ++ do_div(count, (FIFO_RXTX << 2) | 0x3); ++ ++ /* net result needs to be rounded down and decremented by 1 */ ++ if (count > RXCLK_RCD + 1) ++ count = RXCLK_RCD; ++ else if (count < 2) ++ count = 1; ++ else ++ count--; ++ return (u16) count; ++} ++ ++/* ++ * IR Control Register helpers ++ */ ++enum tx_fifo_watermark { ++ TX_FIFO_HALF_EMPTY = 0, ++ TX_FIFO_EMPTY = CNTRL_TIC, ++}; ++ ++enum rx_fifo_watermark { ++ RX_FIFO_HALF_FULL = 0, ++ RX_FIFO_NOT_EMPTY = CNTRL_RIC, ++}; ++ ++static inline void control_tx_irq_watermark(struct cx23885_dev *dev, ++ enum tx_fifo_watermark level) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level); ++} ++ ++static inline void control_rx_irq_watermark(struct cx23885_dev *dev, ++ enum rx_fifo_watermark level) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level); ++} ++ ++static inline void control_tx_enable(struct cx23885_dev *dev, bool enable) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), ++ enable ? (CNTRL_TXE | CNTRL_TFE) : 0); ++} ++ ++static inline void control_rx_enable(struct cx23885_dev *dev, bool enable) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), ++ enable ? (CNTRL_RXE | CNTRL_RFE) : 0); ++} ++ ++static inline void control_tx_modulation_enable(struct cx23885_dev *dev, ++ bool enable) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD, ++ enable ? CNTRL_MOD : 0); ++} ++ ++static inline void control_rx_demodulation_enable(struct cx23885_dev *dev, ++ bool enable) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD, ++ enable ? CNTRL_DMD : 0); ++} ++ ++static inline void control_rx_s_edge_detection(struct cx23885_dev *dev, ++ u32 edge_types) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, ++ edge_types & CNTRL_EDG_BOTH); ++} ++ ++static void control_rx_s_carrier_window(struct cx23885_dev *dev, ++ unsigned int carrier, ++ unsigned int *carrier_range_low, ++ unsigned int *carrier_range_high) ++{ ++ u32 v; ++ unsigned int c16 = carrier * 16; ++ ++ if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { ++ v = CNTRL_WIN_3_4; ++ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); ++ } else { ++ v = CNTRL_WIN_3_3; ++ *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); ++ } ++ ++ if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { ++ v |= CNTRL_WIN_4_3; ++ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); ++ } else { ++ v |= CNTRL_WIN_3_3; ++ *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); ++ } ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v); ++} ++ ++static inline void control_tx_polarity_invert(struct cx23885_dev *dev, ++ bool invert) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL, ++ invert ? CNTRL_CPL : 0); ++} ++ ++static inline void control_tx_level_invert(struct cx23885_dev *dev, ++ bool invert) ++{ ++ cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_IVO, ++ invert ? CNTRL_IVO : 0); ++} ++ ++/* ++ * IR Rx & Tx Clock Register helpers ++ */ ++static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev, ++ unsigned int freq, ++ u16 *divider) ++{ ++ *divider = carrier_freq_to_clock_divider(freq); ++ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); ++ return clock_divider_to_carrier_freq(*divider); ++} ++ ++static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev, ++ unsigned int freq, ++ u16 *divider) ++{ ++ *divider = carrier_freq_to_clock_divider(freq); ++ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); ++ return clock_divider_to_carrier_freq(*divider); ++} ++ ++static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, ++ u16 *divider) ++{ ++ u64 pulse_clocks; ++ ++ if (ns > IR_MAX_DURATION) ++ ns = IR_MAX_DURATION; ++ pulse_clocks = ns_to_pulse_clocks(ns); ++ *divider = pulse_clocks_to_clock_divider(pulse_clocks); ++ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); ++ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); ++} ++ ++static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, ++ u16 *divider) ++{ ++ u64 pulse_clocks; ++ ++ if (ns > IR_MAX_DURATION) ++ ns = IR_MAX_DURATION; ++ pulse_clocks = ns_to_pulse_clocks(ns); ++ *divider = pulse_clocks_to_clock_divider(pulse_clocks); ++ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); ++ return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); ++} ++ ++/* ++ * IR Tx Carrier Duty Cycle register helpers ++ */ ++static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev, ++ unsigned int duty_cycle) ++{ ++ u32 n; ++ n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ ++ if (n != 0) ++ n--; ++ if (n > 15) ++ n = 15; ++ cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n); ++ return DIV_ROUND_CLOSEST((n + 1) * 100, 16); ++} ++ ++/* ++ * IR Filter Register helpers ++ */ ++static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns) ++{ ++ u32 count = ns_to_lpf_count(min_width_ns); ++ cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count); ++ return lpf_count_to_ns(count); ++} ++ ++/* ++ * IR IRQ Enable Register helpers ++ */ ++static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask) ++{ ++ mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); ++ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ++ ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); ++} ++ ++static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask) ++{ ++ mask &= IRQEN_TSE; ++ cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask); ++} ++ ++/* ++ * V4L2 Subdevice IR Ops ++ */ ++static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, ++ bool *handled) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ unsigned long flags; ++ ++ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); ++ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); ++ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); ++ ++ union cx23888_ir_fifo_rec rx_data[FIFO_RX_DEPTH]; ++ unsigned int i, j, k; ++ u32 events, v; ++ int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; ++ ++ tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ ++ rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ ++ rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ ++ ror = stats & STATS_ROR; /* Rx FIFO Over Run */ ++ ++ tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ ++ rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ ++ rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ ++ roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ ++ ++ *handled = false; ++ v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n", ++ tsr ? "tsr" : " ", rsr ? "rsr" : " ", ++ rto ? "rto" : " ", ror ? "ror" : " ", ++ stats & STATS_TBY ? "tby" : " ", ++ stats & STATS_RBY ? "rby" : " "); ++ ++ v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n", ++ tse ? "tse" : " ", rse ? "rse" : " ", ++ rte ? "rte" : " ", roe ? "roe" : " "); ++ ++ /* ++ * Transmitter interrupt service ++ */ ++ if (tse && tsr) { ++ /* ++ * TODO: ++ * Check the watermark threshold setting ++ * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo ++ * Push the data to the hardware FIFO. ++ * If there was nothing more to send in the tx_kfifo, disable ++ * the TSR IRQ and notify the v4l2_device. ++ * If there was something in the tx_kfifo, check the tx_kfifo ++ * level and notify the v4l2_device, if it is low. ++ */ ++ /* For now, inhibit TSR interrupt until Tx is implemented */ ++ irqenable_tx(dev, 0); ++ events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; ++ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); ++ *handled = true; ++ } ++ ++ /* ++ * Receiver interrupt service ++ */ ++ kror = 0; ++ if ((rse && rsr) || (rte && rto)) { ++ /* ++ * Receive data on RSR to clear the STATS_RSR. ++ * Receive data on RTO, since we may not have yet hit the RSR ++ * watermark when we receive the RTO. ++ */ ++ for (i = 0, v = FIFO_RX_NDV; ++ (v & FIFO_RX_NDV) && !kror; i = 0) { ++ for (j = 0; ++ (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { ++ v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); ++ rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV; ++ i++; ++ } ++ if (i == 0) ++ break; ++ j = i * sizeof(union cx23888_ir_fifo_rec); ++ k = kfifo_in_locked(&state->rx_kfifo, ++ (unsigned char *) rx_data, j, ++ &state->rx_kfifo_lock); ++ if (k != j) ++ kror++; /* rx_kfifo over run */ ++ } ++ *handled = true; ++ } ++ ++ events = 0; ++ v = 0; ++ if (kror) { ++ events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; ++ v4l2_err(sd, "IR receiver software FIFO overrun\n"); ++ } ++ if (roe && ror) { ++ /* ++ * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear ++ * the Rx FIFO Over Run status (STATS_ROR) ++ */ ++ v |= CNTRL_RFE; ++ events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; ++ v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); ++ } ++ if (rte && rto) { ++ /* ++ * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear ++ * the Rx Pulse Width Timer Time Out (STATS_RTO) ++ */ ++ v |= CNTRL_RXE; ++ events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; ++ } ++ if (v) { ++ /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ ++ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v); ++ cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl); ++ *handled = true; ++ } ++ ++ spin_lock_irqsave(&state->rx_kfifo_lock, flags); ++ if (kfifo_len(&state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2) ++ events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; ++ spin_unlock_irqrestore(&state->rx_kfifo_lock, flags); ++ ++ if (events) ++ v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); ++ return 0; ++} ++ ++/* Receiver */ ++static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, ++ ssize_t *num) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ bool invert = (bool) atomic_read(&state->rx_invert); ++ u16 divider = (u16) atomic_read(&state->rxclk_divider); ++ ++ unsigned int i, n; ++ union cx23888_ir_fifo_rec *p; ++ unsigned u, v, w; ++ ++ n = count / sizeof(union cx23888_ir_fifo_rec) ++ * sizeof(union cx23888_ir_fifo_rec); ++ if (n == 0) { ++ *num = 0; ++ return 0; ++ } ++ ++ n = kfifo_out_locked(&state->rx_kfifo, buf, n, &state->rx_kfifo_lock); ++ ++ n /= sizeof(union cx23888_ir_fifo_rec); ++ *num = n * sizeof(union cx23888_ir_fifo_rec); ++ ++ for (p = (union cx23888_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) { ++ ++ if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { ++ /* Assume RTO was because of no IR light input */ ++ u = 0; ++ w = 1; ++ } else { ++ u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0; ++ if (invert) ++ u = u ? 0 : 1; ++ w = 0; ++ } ++ ++ v = (unsigned) pulse_width_count_to_ns( ++ (u16) (p->hw_fifo_data & FIFO_RXTX), divider); ++ if (v > IR_MAX_DURATION) ++ v = IR_MAX_DURATION; ++ ++ init_ir_raw_event(&p->ir_core_data); ++ p->ir_core_data.pulse = u; ++ p->ir_core_data.duration = v; ++ p->ir_core_data.timeout = w; ++ ++ v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s %s\n", ++ v, u ? "mark" : "space", w ? "(timed out)" : ""); ++ if (w) ++ v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); ++ } ++ return 0; ++} ++ ++static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ mutex_lock(&state->rx_params_lock); ++ memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters)); ++ mutex_unlock(&state->rx_params_lock); ++ return 0; ++} ++ ++static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ ++ mutex_lock(&state->rx_params_lock); ++ ++ /* Disable or slow down all IR Rx circuits and counters */ ++ irqenable_rx(dev, 0); ++ control_rx_enable(dev, false); ++ control_rx_demodulation_enable(dev, false); ++ control_rx_s_edge_detection(dev, CNTRL_EDG_NONE); ++ filter_rx_s_min_width(dev, 0); ++ cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD); ++ ++ state->rx_params.shutdown = true; ++ ++ mutex_unlock(&state->rx_params_lock); ++ return 0; ++} ++ ++static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ struct v4l2_subdev_ir_parameters *o = &state->rx_params; ++ u16 rxclk_divider; ++ ++ if (p->shutdown) ++ return cx23888_ir_rx_shutdown(sd); ++ ++ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) ++ return -ENOSYS; ++ ++ mutex_lock(&state->rx_params_lock); ++ ++ o->shutdown = p->shutdown; ++ ++ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ ++ o->bytes_per_data_element = p->bytes_per_data_element ++ = sizeof(union cx23888_ir_fifo_rec); ++ ++ /* Before we tweak the hardware, we have to disable the receiver */ ++ irqenable_rx(dev, 0); ++ control_rx_enable(dev, false); ++ ++ control_rx_demodulation_enable(dev, p->modulation); ++ o->modulation = p->modulation; ++ ++ if (p->modulation) { ++ p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq, ++ &rxclk_divider); ++ ++ o->carrier_freq = p->carrier_freq; ++ ++ o->duty_cycle = p->duty_cycle = 50; ++ ++ control_rx_s_carrier_window(dev, p->carrier_freq, ++ &p->carrier_range_lower, ++ &p->carrier_range_upper); ++ o->carrier_range_lower = p->carrier_range_lower; ++ o->carrier_range_upper = p->carrier_range_upper; ++ ++ p->max_pulse_width = ++ (u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider); ++ } else { ++ p->max_pulse_width = ++ rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width, ++ &rxclk_divider); ++ } ++ o->max_pulse_width = p->max_pulse_width; ++ atomic_set(&state->rxclk_divider, rxclk_divider); ++ ++ p->noise_filter_min_width = ++ filter_rx_s_min_width(dev, p->noise_filter_min_width); ++ o->noise_filter_min_width = p->noise_filter_min_width; ++ ++ p->resolution = clock_divider_to_resolution(rxclk_divider); ++ o->resolution = p->resolution; ++ ++ /* FIXME - make this dependent on resolution for better performance */ ++ control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL); ++ ++ control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH); ++ ++ o->invert_level = p->invert_level; ++ atomic_set(&state->rx_invert, p->invert_level); ++ ++ o->interrupt_enable = p->interrupt_enable; ++ o->enable = p->enable; ++ if (p->enable) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&state->rx_kfifo_lock, flags); ++ kfifo_reset(&state->rx_kfifo); ++ /* reset tx_fifo too if there is one... */ ++ spin_unlock_irqrestore(&state->rx_kfifo_lock, flags); ++ if (p->interrupt_enable) ++ irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); ++ control_rx_enable(dev, p->enable); ++ } ++ ++ mutex_unlock(&state->rx_params_lock); ++ return 0; ++} ++ ++/* Transmitter */ ++static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, ++ ssize_t *num) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ /* For now enable the Tx FIFO Service interrupt & pretend we did work */ ++ irqenable_tx(dev, IRQEN_TSE); ++ *num = count; ++ return 0; ++} ++ ++static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ mutex_lock(&state->tx_params_lock); ++ memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters)); ++ mutex_unlock(&state->tx_params_lock); ++ return 0; ++} ++ ++static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ ++ mutex_lock(&state->tx_params_lock); ++ ++ /* Disable or slow down all IR Tx circuits and counters */ ++ irqenable_tx(dev, 0); ++ control_tx_enable(dev, false); ++ control_tx_modulation_enable(dev, false); ++ cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD); ++ ++ state->tx_params.shutdown = true; ++ ++ mutex_unlock(&state->tx_params_lock); ++ return 0; ++} ++ ++static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, ++ struct v4l2_subdev_ir_parameters *p) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ struct v4l2_subdev_ir_parameters *o = &state->tx_params; ++ u16 txclk_divider; ++ ++ if (p->shutdown) ++ return cx23888_ir_tx_shutdown(sd); ++ ++ if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) ++ return -ENOSYS; ++ ++ mutex_lock(&state->tx_params_lock); ++ ++ o->shutdown = p->shutdown; ++ ++ o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; ++ ++ o->bytes_per_data_element = p->bytes_per_data_element ++ = sizeof(union cx23888_ir_fifo_rec); ++ ++ /* Before we tweak the hardware, we have to disable the transmitter */ ++ irqenable_tx(dev, 0); ++ control_tx_enable(dev, false); ++ ++ control_tx_modulation_enable(dev, p->modulation); ++ o->modulation = p->modulation; ++ ++ if (p->modulation) { ++ p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq, ++ &txclk_divider); ++ o->carrier_freq = p->carrier_freq; ++ ++ p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle); ++ o->duty_cycle = p->duty_cycle; ++ ++ p->max_pulse_width = ++ (u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider); ++ } else { ++ p->max_pulse_width = ++ txclk_tx_s_max_pulse_width(dev, p->max_pulse_width, ++ &txclk_divider); ++ } ++ o->max_pulse_width = p->max_pulse_width; ++ atomic_set(&state->txclk_divider, txclk_divider); ++ ++ p->resolution = clock_divider_to_resolution(txclk_divider); ++ o->resolution = p->resolution; ++ ++ /* FIXME - make this dependent on resolution for better performance */ ++ control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY); ++ ++ control_tx_polarity_invert(dev, p->invert_carrier_sense); ++ o->invert_carrier_sense = p->invert_carrier_sense; ++ ++ control_tx_level_invert(dev, p->invert_level); ++ o->invert_level = p->invert_level; ++ ++ o->interrupt_enable = p->interrupt_enable; ++ o->enable = p->enable; ++ if (p->enable) { ++ if (p->interrupt_enable) ++ irqenable_tx(dev, IRQEN_TSE); ++ control_tx_enable(dev, p->enable); ++ } ++ ++ mutex_unlock(&state->tx_params_lock); ++ return 0; ++} ++ ++ ++/* ++ * V4L2 Subdevice Core Ops ++ */ ++static int cx23888_ir_log_status(struct v4l2_subdev *sd) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ struct cx23885_dev *dev = state->dev; ++ char *s; ++ int i, j; ++ ++ u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); ++ u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD; ++ u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD; ++ u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC; ++ u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); ++ u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); ++ u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF; ++ ++ v4l2_info(sd, "IR Receiver:\n"); ++ v4l2_info(sd, "\tEnabled: %s\n", ++ cntrl & CNTRL_RXE ? "yes" : "no"); ++ v4l2_info(sd, "\tDemodulation from a carrier: %s\n", ++ cntrl & CNTRL_DMD ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO: %s\n", ++ cntrl & CNTRL_RFE ? "enabled" : "disabled"); ++ switch (cntrl & CNTRL_EDG) { ++ case CNTRL_EDG_NONE: ++ s = "disabled"; ++ break; ++ case CNTRL_EDG_FALL: ++ s = "falling edge"; ++ break; ++ case CNTRL_EDG_RISE: ++ s = "rising edge"; ++ break; ++ case CNTRL_EDG_BOTH: ++ s = "rising & falling edges"; ++ break; ++ default: ++ s = "??? edge"; ++ break; ++ } ++ v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); ++ v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", ++ cntrl & CNTRL_R ? "not loaded" : "overflow marker"); ++ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", ++ cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); ++ v4l2_info(sd, "\tLoopback mode: %s\n", ++ cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); ++ if (cntrl & CNTRL_DMD) { ++ v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", ++ clock_divider_to_carrier_freq(rxclk)); ++ switch (cntrl & CNTRL_WIN) { ++ case CNTRL_WIN_3_3: ++ i = 3; ++ j = 3; ++ break; ++ case CNTRL_WIN_4_3: ++ i = 4; ++ j = 3; ++ break; ++ case CNTRL_WIN_3_4: ++ i = 3; ++ j = 4; ++ break; ++ case CNTRL_WIN_4_4: ++ i = 4; ++ j = 4; ++ break; ++ default: ++ i = 0; ++ j = 0; ++ break; ++ } ++ v4l2_info(sd, "\tNext carrier edge window: 16 clocks " ++ "-%1d/+%1d, %u to %u Hz\n", i, j, ++ clock_divider_to_freq(rxclk, 16 + j), ++ clock_divider_to_freq(rxclk, 16 - i)); ++ } ++ v4l2_info(sd, "\tMax measurable pulse width: %u us, %llu ns\n", ++ pulse_width_count_to_us(FIFO_RXTX, rxclk), ++ pulse_width_count_to_ns(FIFO_RXTX, rxclk)); ++ v4l2_info(sd, "\tLow pass filter: %s\n", ++ filtr ? "enabled" : "disabled"); ++ if (filtr) ++ v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " ++ "%u ns\n", ++ lpf_count_to_us(filtr), ++ lpf_count_to_ns(filtr)); ++ v4l2_info(sd, "\tPulse width timer timed-out: %s\n", ++ stats & STATS_RTO ? "yes" : "no"); ++ v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", ++ irqen & IRQEN_RTE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO overrun: %s\n", ++ stats & STATS_ROR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", ++ irqen & IRQEN_ROE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tBusy: %s\n", ++ stats & STATS_RBY ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service requested: %s\n", ++ stats & STATS_RSR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service request interrupt: %s\n", ++ irqen & IRQEN_RSE ? "enabled" : "disabled"); ++ ++ v4l2_info(sd, "IR Transmitter:\n"); ++ v4l2_info(sd, "\tEnabled: %s\n", ++ cntrl & CNTRL_TXE ? "yes" : "no"); ++ v4l2_info(sd, "\tModulation onto a carrier: %s\n", ++ cntrl & CNTRL_MOD ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO: %s\n", ++ cntrl & CNTRL_TFE ? "enabled" : "disabled"); ++ v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", ++ cntrl & CNTRL_TIC ? "not empty" : "half full or less"); ++ v4l2_info(sd, "\tOutput pin level inversion %s\n", ++ cntrl & CNTRL_IVO ? "yes" : "no"); ++ v4l2_info(sd, "\tCarrier polarity: %s\n", ++ cntrl & CNTRL_CPL ? "space:burst mark:noburst" ++ : "space:noburst mark:burst"); ++ if (cntrl & CNTRL_MOD) { ++ v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", ++ clock_divider_to_carrier_freq(txclk)); ++ v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", ++ cduty + 1); ++ } ++ v4l2_info(sd, "\tMax pulse width: %u us, %llu ns\n", ++ pulse_width_count_to_us(FIFO_RXTX, txclk), ++ pulse_width_count_to_ns(FIFO_RXTX, txclk)); ++ v4l2_info(sd, "\tBusy: %s\n", ++ stats & STATS_TBY ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service requested: %s\n", ++ stats & STATS_TSR ? "yes" : "no"); ++ v4l2_info(sd, "\tFIFO service request interrupt: %s\n", ++ irqen & IRQEN_TSE ? "enabled" : "disabled"); ++ ++ return 0; ++} ++ ++static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match) ++{ ++ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2; ++} ++ ++static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ ++ if (cx23888_ir_dbg_match(&chip->match)) { ++ chip->ident = state->id; ++ chip->revision = state->rev; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int cx23888_ir_g_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; ++ ++ if (!cx23888_ir_dbg_match(®->match)) ++ return -EINVAL; ++ if ((addr & 0x3) != 0) ++ return -EINVAL; ++ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ reg->size = 4; ++ reg->val = cx23888_ir_read4(state->dev, addr); ++ return 0; ++} ++ ++static int cx23888_ir_s_register(struct v4l2_subdev *sd, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx23888_ir_state *state = to_state(sd); ++ u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; ++ ++ if (!cx23888_ir_dbg_match(®->match)) ++ return -EINVAL; ++ if ((addr & 0x3) != 0) ++ return -EINVAL; ++ if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) ++ return -EINVAL; ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ cx23888_ir_write4(state->dev, addr, reg->val); ++ return 0; ++} ++#endif ++ ++static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { ++ .g_chip_ident = cx23888_ir_g_chip_ident, ++ .log_status = cx23888_ir_log_status, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .g_register = cx23888_ir_g_register, ++ .s_register = cx23888_ir_s_register, ++#endif ++ .interrupt_service_routine = cx23888_ir_irq_handler, ++}; ++ ++static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = { ++ .rx_read = cx23888_ir_rx_read, ++ .rx_g_parameters = cx23888_ir_rx_g_parameters, ++ .rx_s_parameters = cx23888_ir_rx_s_parameters, ++ ++ .tx_write = cx23888_ir_tx_write, ++ .tx_g_parameters = cx23888_ir_tx_g_parameters, ++ .tx_s_parameters = cx23888_ir_tx_s_parameters, ++}; ++ ++static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { ++ .core = &cx23888_ir_core_ops, ++ .ir = &cx23888_ir_ir_ops, ++}; ++ ++static const struct v4l2_subdev_ir_parameters default_rx_params = { ++ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec), ++ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, ++ ++ .enable = false, ++ .interrupt_enable = false, ++ .shutdown = true, ++ ++ .modulation = true, ++ .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */ ++ ++ /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ ++ /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ ++ .noise_filter_min_width = 333333, /* ns */ ++ .carrier_range_lower = 35000, ++ .carrier_range_upper = 37000, ++ .invert_level = false, ++}; ++ ++static const struct v4l2_subdev_ir_parameters default_tx_params = { ++ .bytes_per_data_element = sizeof(union cx23888_ir_fifo_rec), ++ .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, ++ ++ .enable = false, ++ .interrupt_enable = false, ++ .shutdown = true, ++ ++ .modulation = true, ++ .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ ++ .duty_cycle = 25, /* 25 % - RC-5 carrier */ ++ .invert_level = false, ++ .invert_carrier_sense = false, ++}; ++ ++int cx23888_ir_probe(struct cx23885_dev *dev) ++{ ++ struct cx23888_ir_state *state; ++ struct v4l2_subdev *sd; ++ struct v4l2_subdev_ir_parameters default_params; ++ int ret; ++ ++ state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); ++ if (state == NULL) ++ return -ENOMEM; ++ ++ spin_lock_init(&state->rx_kfifo_lock); ++ if (kfifo_alloc(&state->rx_kfifo, CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL)) ++ return -ENOMEM; ++ ++ state->dev = dev; ++ state->id = V4L2_IDENT_CX23888_IR; ++ state->rev = 0; ++ sd = &state->sd; ++ ++ v4l2_subdev_init(sd, &cx23888_ir_controller_ops); ++ v4l2_set_subdevdata(sd, state); ++ /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ ++ snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); ++ sd->grp_id = CX23885_HW_888_IR; ++ ++ ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd); ++ if (ret == 0) { ++ /* ++ * Ensure no interrupts arrive from '888 specific conditions, ++ * since we ignore them in this driver to have commonality with ++ * similar IR controller cores. ++ */ ++ cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); ++ ++ mutex_init(&state->rx_params_lock); ++ default_params = default_rx_params; ++ v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); ++ ++ mutex_init(&state->tx_params_lock); ++ default_params = default_tx_params; ++ v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); ++ } else { ++ kfifo_free(&state->rx_kfifo); ++ } ++ return ret; ++} ++ ++int cx23888_ir_remove(struct cx23885_dev *dev) ++{ ++ struct v4l2_subdev *sd; ++ struct cx23888_ir_state *state; ++ ++ sd = cx23885_find_hw(dev, CX23885_HW_888_IR); ++ if (sd == NULL) ++ return -ENODEV; ++ ++ cx23888_ir_rx_shutdown(sd); ++ cx23888_ir_tx_shutdown(sd); ++ ++ state = to_state(sd); ++ v4l2_device_unregister_subdev(sd); ++ kfifo_free(&state->rx_kfifo); ++ kfree(state); ++ /* Nothing more to free() as state held the actual v4l2_subdev object */ ++ return 0; ++} +diff --git a/drivers/media/pci/cx23885/cx23888-ir.h b/drivers/media/pci/cx23885/cx23888-ir.h +new file mode 100644 +index 0000000..d2de41c +--- /dev/null ++++ b/drivers/media/pci/cx23885/cx23888-ir.h +@@ -0,0 +1,28 @@ ++/* ++ * Driver for the Conexant CX23885/7/8 PCIe bridge ++ * ++ * CX23888 Integrated Consumer Infrared Controller ++ * ++ * Copyright (C) 2009 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA. ++ */ ++ ++#ifndef _CX23888_IR_H_ ++#define _CX23888_IR_H_ ++int cx23888_ir_probe(struct cx23885_dev *dev); ++int cx23888_ir_remove(struct cx23885_dev *dev); ++#endif +diff --git a/drivers/media/pci/cx23885/netup-eeprom.c b/drivers/media/pci/cx23885/netup-eeprom.c +new file mode 100644 +index 0000000..98a48f5 +--- /dev/null ++++ b/drivers/media/pci/cx23885/netup-eeprom.c +@@ -0,0 +1,107 @@ ++ ++/* ++ * netup-eeprom.c ++ * ++ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++# ++#include "cx23885.h" ++#include "netup-eeprom.h" ++ ++#define EEPROM_I2C_ADDR 0x50 ++ ++int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr) ++{ ++ int ret; ++ unsigned char buf[2]; ++ ++ /* Read from EEPROM */ ++ struct i2c_msg msg[] = { ++ { ++ .addr = EEPROM_I2C_ADDR, ++ .flags = 0, ++ .buf = &buf[0], ++ .len = 1 ++ }, { ++ .addr = EEPROM_I2C_ADDR, ++ .flags = I2C_M_RD, ++ .buf = &buf[1], ++ .len = 1 ++ } ++ ++ }; ++ ++ buf[0] = addr; ++ buf[1] = 0x0; ++ ++ ret = i2c_transfer(i2c_adap, msg, 2); ++ ++ if (ret != 2) { ++ printk(KERN_ERR "eeprom i2c read error, status=%d\n", ret); ++ return -1; ++ } ++ ++ return buf[1]; ++}; ++ ++int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data) ++{ ++ int ret; ++ unsigned char bufw[2]; ++ ++ /* Write into EEPROM */ ++ struct i2c_msg msg[] = { ++ { ++ .addr = EEPROM_I2C_ADDR, ++ .flags = 0, ++ .buf = &bufw[0], ++ .len = 2 ++ } ++ }; ++ ++ bufw[0] = addr; ++ bufw[1] = data; ++ ++ ret = i2c_transfer(i2c_adap, msg, 1); ++ ++ if (ret != 1) { ++ printk(KERN_ERR "eeprom i2c write error, status=%d\n", ret); ++ return -1; ++ } ++ ++ mdelay(10); /* prophylactic delay, datasheet write cycle time = 5 ms */ ++ return 0; ++}; ++ ++void netup_get_card_info(struct i2c_adapter *i2c_adap, ++ struct netup_card_info *cinfo) ++{ ++ int i, j; ++ ++ cinfo->rev = netup_eeprom_read(i2c_adap, 63); ++ ++ for (i = 64, j = 0; i < 70; i++, j++) ++ cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i); ++ ++ for (i = 70, j = 0; i < 76; i++, j++) ++ cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i); ++}; +diff --git a/drivers/media/pci/cx23885/netup-eeprom.h b/drivers/media/pci/cx23885/netup-eeprom.h +new file mode 100644 +index 0000000..13926e1 +--- /dev/null ++++ b/drivers/media/pci/cx23885/netup-eeprom.h +@@ -0,0 +1,42 @@ ++/* ++ * netup-eeprom.h ++ * ++ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef NETUP_EEPROM_H ++#define NETUP_EEPROM_H ++ ++struct netup_port_info { ++ u8 mac[6];/* card MAC address */ ++}; ++ ++struct netup_card_info { ++ struct netup_port_info port[2];/* ports - 1,2 */ ++ u8 rev;/* card revision */ ++}; ++ ++extern int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr); ++extern int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data); ++extern void netup_get_card_info(struct i2c_adapter *i2c_adap, ++ struct netup_card_info *cinfo); ++ ++#endif +diff --git a/drivers/media/pci/cx23885/netup-init.c b/drivers/media/pci/cx23885/netup-init.c +new file mode 100644 +index 0000000..0044fef +--- /dev/null ++++ b/drivers/media/pci/cx23885/netup-init.c +@@ -0,0 +1,126 @@ ++/* ++ * netup-init.c ++ * ++ * NetUP Dual DVB-S2 CI driver ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include "cx23885.h" ++#include "netup-init.h" ++ ++static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val) ++{ ++ int ret; ++ u8 buf[3]; ++ struct i2c_msg msg = { ++ .addr = 0x88 >> 1, ++ .flags = 0, ++ .buf = buf, ++ .len = 3 ++ }; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ buf[2] = val; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c write error!\n", __func__); ++} ++ ++static void i2c_av_write4(struct i2c_adapter *i2c, u16 reg, u32 val) ++{ ++ int ret; ++ u8 buf[6]; ++ struct i2c_msg msg = { ++ .addr = 0x88 >> 1, ++ .flags = 0, ++ .buf = buf, ++ .len = 6 ++ }; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ buf[2] = val & 0xff; ++ buf[3] = (val >> 8) & 0xff; ++ buf[4] = (val >> 16) & 0xff; ++ buf[5] = val >> 24; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c write error!\n", __func__); ++} ++ ++static u8 i2c_av_read(struct i2c_adapter *i2c, u16 reg) ++{ ++ int ret; ++ u8 buf[2]; ++ struct i2c_msg msg = { ++ .addr = 0x88 >> 1, ++ .flags = 0, ++ .buf = buf, ++ .len = 2 ++ }; ++ ++ buf[0] = reg >> 8; ++ buf[1] = reg & 0xff; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c write error!\n", __func__); ++ ++ msg.flags = I2C_M_RD; ++ msg.len = 1; ++ ++ ret = i2c_transfer(i2c, &msg, 1); ++ ++ if (ret != 1) ++ printk(KERN_ERR "%s: i2c read error!\n", __func__); ++ ++ return buf[0]; ++} ++ ++static void i2c_av_and_or(struct i2c_adapter *i2c, u16 reg, unsigned and_mask, ++ u8 or_value) ++{ ++ i2c_av_write(i2c, reg, (i2c_av_read(i2c, reg) & and_mask) | or_value); ++} ++/* set 27MHz on AUX_CLK */ ++void netup_initialize(struct cx23885_dev *dev) ++{ ++ struct cx23885_i2c *i2c_bus = &dev->i2c_bus[2]; ++ struct i2c_adapter *i2c = &i2c_bus->i2c_adap; ++ ++ /* Stop microcontroller */ ++ i2c_av_and_or(i2c, 0x803, ~0x10, 0x00); ++ ++ /* Aux PLL frac for 27 MHz */ ++ i2c_av_write4(i2c, 0x114, 0xea0eb3); ++ ++ /* Aux PLL int for 27 MHz */ ++ i2c_av_write4(i2c, 0x110, 0x090319); ++ ++ /* start microcontroller */ ++ i2c_av_and_or(i2c, 0x803, ~0x10, 0x10); ++} +diff --git a/drivers/media/pci/cx23885/netup-init.h b/drivers/media/pci/cx23885/netup-init.h +new file mode 100644 +index 0000000..d26ae4b +--- /dev/null ++++ b/drivers/media/pci/cx23885/netup-init.h +@@ -0,0 +1,25 @@ ++/* ++ * netup-init.h ++ * ++ * NetUP Dual DVB-S2 CI driver ++ * ++ * Copyright (C) 2009 NetUP Inc. ++ * Copyright (C) 2009 Igor M. Liplianin ++ * Copyright (C) 2009 Abylay Ospan ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++extern void netup_initialize(struct cx23885_dev *dev); +diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig +new file mode 100644 +index 0000000..5f6b542 +--- /dev/null ++++ b/drivers/media/pci/cx25821/Kconfig +@@ -0,0 +1,34 @@ ++config VIDEO_CX25821 ++ tristate "Conexant cx25821 support" ++ depends on DVB_CORE && VIDEO_DEV && PCI && I2C ++ select I2C_ALGOBIT ++ select VIDEO_BTCX ++ select VIDEO_TVEEPROM ++ depends on RC_CORE ++ select VIDEOBUF_DVB ++ select VIDEOBUF_DMA_SG ++ select VIDEO_CX25840 ++ select VIDEO_CX2341X ++ ---help--- ++ This is a video4linux driver for Conexant 25821 based ++ TV cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx25821 ++ ++config VIDEO_CX25821_ALSA ++ tristate "Conexant 25821 DMA audio support" ++ depends on VIDEO_CX25821 && SND && EXPERIMENTAL ++ select SND_PCM ++ ---help--- ++ This is a video4linux driver for direct (DMA) audio on ++ Conexant 25821 based capture cards using ALSA. ++ ++ It only works with boards with function 01 enabled. ++ To check if your board supports, use lspci -n. ++ If supported, you should see 14f1:8801 or 14f1:8811 ++ PCI device. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx25821-alsa. ++ +diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile +new file mode 100644 +index 0000000..5bf3ea4 +--- /dev/null ++++ b/drivers/media/pci/cx25821/Makefile +@@ -0,0 +1,13 @@ ++cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ ++ cx25821-gpio.o cx25821-medusa-video.o \ ++ cx25821-video.o cx25821-video-upstream.o \ ++ cx25821-video-upstream-ch2.o \ ++ cx25821-audio-upstream.o ++ ++obj-$(CONFIG_VIDEO_CX25821) += cx25821.o ++obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ++ ++ccflags-y += -Idrivers/media/i2c ++ccflags-y += -Idrivers/media/tuners ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/dvb-frontends +diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c +new file mode 100644 +index 0000000..35879ae +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-alsa.c +@@ -0,0 +1,785 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on SAA713x ALSA driver and CX88 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, version 2 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25821.h" ++#include "cx25821-reg.h" ++ ++#define AUDIO_SRAM_CHANNEL SRAM_CH08 ++ ++#define dprintk(level, fmt, arg...) \ ++do { \ ++ if (debug >= level) \ ++ pr_info("%s/1: " fmt, chip->dev->name, ##arg); \ ++} while (0) ++#define dprintk_core(level, fmt, arg...) \ ++do { \ ++ if (debug >= level) \ ++ printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name, ##arg); \ ++} while (0) ++ ++/**************************************************************************** ++ Data type declarations - Can be moded to a header file later ++ ****************************************************************************/ ++ ++static struct snd_card *snd_cx25821_cards[SNDRV_CARDS]; ++static int devno; ++ ++struct cx25821_audio_buffer { ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ struct videobuf_dmabuf dma; ++}; ++ ++struct cx25821_audio_dev { ++ struct cx25821_dev *dev; ++ struct cx25821_dmaqueue q; ++ ++ /* pci i/o */ ++ struct pci_dev *pci; ++ ++ /* audio controls */ ++ int irq; ++ ++ struct snd_card *card; ++ ++ unsigned long iobase; ++ spinlock_t reg_lock; ++ atomic_t count; ++ ++ unsigned int dma_size; ++ unsigned int period_size; ++ unsigned int num_periods; ++ ++ struct videobuf_dmabuf *dma_risc; ++ ++ struct cx25821_audio_buffer *buf; ++ ++ struct snd_pcm_substream *substream; ++}; ++ ++ ++/**************************************************************************** ++ Module global static vars ++ ****************************************************************************/ ++ ++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ ++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ ++static bool enable[SNDRV_CARDS] = { 1, [1 ... (SNDRV_CARDS - 1)] = 1 }; ++ ++module_param_array(enable, bool, NULL, 0444); ++MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); ++ ++module_param_array(index, int, NULL, 0444); ++MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s)."); ++ ++/**************************************************************************** ++ Module macros ++ ****************************************************************************/ ++ ++MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards"); ++MODULE_AUTHOR("Hiep Huynh"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); /* "{{Conexant,23881}," */ ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++ ++/**************************************************************************** ++ Module specific funtions ++ ****************************************************************************/ ++/* Constants taken from cx88-reg.h */ ++#define AUD_INT_DN_RISCI1 (1 << 0) ++#define AUD_INT_UP_RISCI1 (1 << 1) ++#define AUD_INT_RDS_DN_RISCI1 (1 << 2) ++#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ ++#define AUD_INT_UP_RISCI2 (1 << 5) ++#define AUD_INT_RDS_DN_RISCI2 (1 << 6) ++#define AUD_INT_DN_SYNC (1 << 12) ++#define AUD_INT_UP_SYNC (1 << 13) ++#define AUD_INT_RDS_DN_SYNC (1 << 14) ++#define AUD_INT_OPC_ERR (1 << 16) ++#define AUD_INT_BER_IRQ (1 << 20) ++#define AUD_INT_MCHG_IRQ (1 << 21) ++#define GP_COUNT_CONTROL_RESET 0x3 ++ ++#define PCI_MSK_AUD_EXT (1 << 4) ++#define PCI_MSK_AUD_INT (1 << 3) ++/* ++ * BOARD Specific: Sets audio DMA ++ */ ++ ++static int _cx25821_start_audio_dma(struct cx25821_audio_dev *chip) ++{ ++ struct cx25821_audio_buffer *buf = chip->buf; ++ struct cx25821_dev *dev = chip->dev; ++ struct sram_channel *audio_ch = ++ &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; ++ u32 tmp = 0; ++ ++ /* enable output on the GPIO 0 for the MCLK ADC (Audio) */ ++ cx25821_set_gpiopin_direction(chip->dev, 0, 0); ++ ++ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ ++ cx_clear(AUD_INT_DMA_CTL, ++ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); ++ ++ /* setup fifo + format - out channel */ ++ cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl, ++ buf->risc.dma); ++ ++ /* sets bpl size */ ++ cx_write(AUD_A_LNGTH, buf->bpl); ++ ++ /* reset counter */ ++ /* GP_COUNT_CONTROL_RESET = 0x3 */ ++ cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); ++ atomic_set(&chip->count, 0); ++ ++ /* Set the input mode to 16-bit */ ++ tmp = cx_read(AUD_A_CFG); ++ cx_write(AUD_A_CFG, tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | ++ FLD_AUD_CLK_ENABLE); ++ ++ /* ++ pr_info("DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d byte buffer\n", ++ buf->bpl, audio_ch->cmds_start, ++ cx_read(audio_ch->cmds_start + 12)>>1, ++ chip->num_periods, buf->bpl * chip->num_periods); ++ */ ++ ++ /* Enables corresponding bits at AUD_INT_STAT */ ++ cx_write(AUD_A_INT_MSK, FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF | ++ FLD_AUD_DST_SYNC | FLD_AUD_DST_OPC_ERR); ++ ++ /* Clean any pending interrupt bits already set */ ++ cx_write(AUD_A_INT_STAT, ~0); ++ ++ /* enable audio irqs */ ++ cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); ++ ++ /* Turn on audio downstream fifo and risc enable 0x101 */ ++ tmp = cx_read(AUD_INT_DMA_CTL); ++ cx_set(AUD_INT_DMA_CTL, tmp | ++ (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN)); ++ ++ mdelay(100); ++ return 0; ++} ++ ++/* ++ * BOARD Specific: Resets audio DMA ++ */ ++static int _cx25821_stop_audio_dma(struct cx25821_audio_dev *chip) ++{ ++ struct cx25821_dev *dev = chip->dev; ++ ++ /* stop dma */ ++ cx_clear(AUD_INT_DMA_CTL, ++ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); ++ ++ /* disable irqs */ ++ cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); ++ cx_clear(AUD_A_INT_MSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | ++ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); ++ ++ return 0; ++} ++ ++#define MAX_IRQ_LOOP 50 ++ ++/* ++ * BOARD Specific: IRQ dma bits ++ */ ++static char *cx25821_aud_irqs[32] = { ++ "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ ++ NULL, /* reserved */ ++ "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ ++ NULL, /* reserved */ ++ "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ ++ NULL, /* reserved */ ++ "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ ++ NULL, /* reserved */ ++ "opc_err", "par_err", "rip_err", /* 16-18 */ ++ "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ ++}; ++ ++/* ++ * BOARD Specific: Threats IRQ audio specific calls ++ */ ++static void cx25821_aud_irq(struct cx25821_audio_dev *chip, u32 status, ++ u32 mask) ++{ ++ struct cx25821_dev *dev = chip->dev; ++ ++ if (0 == (status & mask)) ++ return; ++ ++ cx_write(AUD_A_INT_STAT, status); ++ if (debug > 1 || (status & mask & ~0xff)) ++ cx25821_print_irqbits(dev->name, "irq aud", cx25821_aud_irqs, ++ ARRAY_SIZE(cx25821_aud_irqs), status, mask); ++ ++ /* risc op code error */ ++ if (status & AUD_INT_OPC_ERR) { ++ pr_warn("WARNING %s/1: Audio risc op code error\n", dev->name); ++ ++ cx_clear(AUD_INT_DMA_CTL, ++ FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); ++ cx25821_sram_channel_dump_audio(dev, ++ &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]); ++ } ++ if (status & AUD_INT_DN_SYNC) { ++ pr_warn("WARNING %s: Downstream sync error!\n", dev->name); ++ cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); ++ return; ++ } ++ ++ /* risc1 downstream */ ++ if (status & AUD_INT_DN_RISCI1) { ++ atomic_set(&chip->count, cx_read(AUD_A_GPCNT)); ++ snd_pcm_period_elapsed(chip->substream); ++ } ++} ++ ++/* ++ * BOARD Specific: Handles IRQ calls ++ */ ++static irqreturn_t cx25821_irq(int irq, void *dev_id) ++{ ++ struct cx25821_audio_dev *chip = dev_id; ++ struct cx25821_dev *dev = chip->dev; ++ u32 status, pci_status; ++ u32 audint_status, audint_mask; ++ int loop, handled = 0; ++ ++ audint_status = cx_read(AUD_A_INT_STAT); ++ audint_mask = cx_read(AUD_A_INT_MSK); ++ status = cx_read(PCI_INT_STAT); ++ ++ for (loop = 0; loop < 1; loop++) { ++ status = cx_read(PCI_INT_STAT); ++ if (0 == status) { ++ status = cx_read(PCI_INT_STAT); ++ audint_status = cx_read(AUD_A_INT_STAT); ++ audint_mask = cx_read(AUD_A_INT_MSK); ++ ++ if (status) { ++ handled = 1; ++ cx_write(PCI_INT_STAT, status); ++ ++ cx25821_aud_irq(chip, audint_status, ++ audint_mask); ++ break; ++ } else { ++ goto out; ++ } ++ } ++ ++ handled = 1; ++ cx_write(PCI_INT_STAT, status); ++ ++ cx25821_aud_irq(chip, audint_status, audint_mask); ++ } ++ ++ pci_status = cx_read(PCI_INT_STAT); ++ ++ if (handled) ++ cx_write(PCI_INT_STAT, pci_status); ++ ++out: ++ return IRQ_RETVAL(handled); ++} ++ ++static int dsp_buffer_free(struct cx25821_audio_dev *chip) ++{ ++ BUG_ON(!chip->dma_size); ++ ++ dprintk(2, "Freeing buffer\n"); ++ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); ++ videobuf_dma_free(chip->dma_risc); ++ btcx_riscmem_free(chip->pci, &chip->buf->risc); ++ kfree(chip->buf); ++ ++ chip->dma_risc = NULL; ++ chip->dma_size = 0; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ ALSA PCM Interface ++ ****************************************************************************/ ++ ++/* ++ * Digital hardware definition ++ */ ++#define DEFAULT_FIFO_SIZE 384 ++static struct snd_pcm_hardware snd_cx25821_digital_hw = { ++ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ ++ .rates = SNDRV_PCM_RATE_48000, ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ /* Analog audio output will be full of clicks and pops if there ++ are not exactly four lines in the SRAM FIFO buffer. */ ++ .period_bytes_min = DEFAULT_FIFO_SIZE / 3, ++ .period_bytes_max = DEFAULT_FIFO_SIZE / 3, ++ .periods_min = 1, ++ .periods_max = AUDIO_LINE_SIZE, ++ /* 128 * 128 = 16384 = 1024 * 16 */ ++ .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), ++}; ++ ++/* ++ * audio pcm capture open callback ++ */ ++static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) ++{ ++ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ unsigned int bpl = 0; ++ ++ if (!chip) { ++ pr_err("DEBUG: cx25821 can't find device struct. Can't proceed with open\n"); ++ return -ENODEV; ++ } ++ ++ err = snd_pcm_hw_constraint_pow2(runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) ++ goto _error; ++ ++ chip->substream = substream; ++ ++ runtime->hw = snd_cx25821_digital_hw; ++ ++ if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != ++ DEFAULT_FIFO_SIZE) { ++ /* since there are 3 audio Clusters */ ++ bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; ++ bpl &= ~7; /* must be multiple of 8 */ ++ ++ if (bpl > AUDIO_LINE_SIZE) ++ bpl = AUDIO_LINE_SIZE; ++ ++ runtime->hw.period_bytes_min = bpl; ++ runtime->hw.period_bytes_max = bpl; ++ } ++ ++ return 0; ++_error: ++ dprintk(1, "Error opening PCM!\n"); ++ return err; ++} ++ ++/* ++ * audio close callback ++ */ ++static int snd_cx25821_close(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * hw_params callback ++ */ ++static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct videobuf_dmabuf *dma; ++ ++ struct cx25821_audio_buffer *buf; ++ int ret; ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ chip->period_size = params_period_bytes(hw_params); ++ chip->num_periods = params_periods(hw_params); ++ chip->dma_size = chip->period_size * params_periods(hw_params); ++ ++ BUG_ON(!chip->dma_size); ++ BUG_ON(chip->num_periods & (chip->num_periods - 1)); ++ ++ buf = kzalloc(sizeof(*buf), GFP_KERNEL); ++ if (NULL == buf) ++ return -ENOMEM; ++ ++ if (chip->period_size > AUDIO_LINE_SIZE) ++ chip->period_size = AUDIO_LINE_SIZE; ++ ++ buf->bpl = chip->period_size; ++ ++ dma = &buf->dma; ++ videobuf_dma_init(dma); ++ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, ++ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); ++ if (ret < 0) ++ goto error; ++ ++ ret = videobuf_dma_map(&chip->pci->dev, dma); ++ if (ret < 0) ++ goto error; ++ ++ ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, ++ chip->period_size, chip->num_periods, 1); ++ if (ret < 0) { ++ pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); ++ goto error; ++ } ++ ++ /* Loop back to start of program */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ chip->buf = buf; ++ chip->dma_risc = dma; ++ ++ substream->runtime->dma_area = chip->dma_risc->vaddr; ++ substream->runtime->dma_bytes = chip->dma_size; ++ substream->runtime->dma_addr = 0; ++ ++ return 0; ++ ++error: ++ kfree(buf); ++ return ret; ++} ++ ++/* ++ * hw free callback ++ */ ++static int snd_cx25821_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * prepare callback ++ */ ++static int snd_cx25821_prepare(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * trigger callback ++ */ ++static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, ++ int cmd) ++{ ++ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); ++ int err = 0; ++ ++ /* Local interrupts are already disabled by ALSA */ ++ spin_lock(&chip->reg_lock); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ err = _cx25821_start_audio_dma(chip); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ err = _cx25821_stop_audio_dma(chip); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ ++ spin_unlock(&chip->reg_lock); ++ ++ return err; ++} ++ ++/* ++ * pointer callback ++ */ ++static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream ++ *substream) ++{ ++ struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ u16 count; ++ ++ count = atomic_read(&chip->count); ++ ++ return runtime->period_size * (count & (runtime->periods - 1)); ++} ++ ++/* ++ * page callback (needed for mmap) ++ */ ++static struct page *snd_cx25821_page(struct snd_pcm_substream *substream, ++ unsigned long offset) ++{ ++ void *pageptr = substream->runtime->dma_area + offset; ++ ++ return vmalloc_to_page(pageptr); ++} ++ ++/* ++ * operators ++ */ ++static struct snd_pcm_ops snd_cx25821_pcm_ops = { ++ .open = snd_cx25821_pcm_open, ++ .close = snd_cx25821_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_cx25821_hw_params, ++ .hw_free = snd_cx25821_hw_free, ++ .prepare = snd_cx25821_prepare, ++ .trigger = snd_cx25821_card_trigger, ++ .pointer = snd_cx25821_pointer, ++ .page = snd_cx25821_page, ++}; ++ ++/* ++ * ALSA create a PCM device: Called when initializing the board. ++ * Sets up the name and hooks up the callbacks ++ */ ++static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, ++ char *name) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); ++ if (err < 0) { ++ pr_info("ERROR: FAILED snd_pcm_new() in %s\n", __func__); ++ return err; ++ } ++ pcm->private_data = chip; ++ pcm->info_flags = 0; ++ strcpy(pcm->name, name); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ Basic Flow for Sound Devices ++ ****************************************************************************/ ++ ++/* ++ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio ++ * Only boards with eeprom and byte 1 at eeprom=1 have it ++ */ ++ ++static DEFINE_PCI_DEVICE_TABLE(cx25821_audio_pci_tbl) = { ++ {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); ++ ++/* ++ * Not used in the function snd_cx25821_dev_free so removing ++ * from the file. ++ */ ++/* ++static int snd_cx25821_free(struct cx25821_audio_dev *chip) ++{ ++ if (chip->irq >= 0) ++ free_irq(chip->irq, chip); ++ ++ cx25821_dev_unregister(chip->dev); ++ pci_disable_device(chip->pci); ++ ++ return 0; ++} ++*/ ++ ++/* ++ * Component Destructor ++ */ ++static void snd_cx25821_dev_free(struct snd_card *card) ++{ ++ struct cx25821_audio_dev *chip = card->private_data; ++ ++ /* snd_cx25821_free(chip); */ ++ snd_card_free(chip->card); ++} ++ ++/* ++ * Alsa Constructor - Component probe ++ */ ++static int cx25821_audio_initdev(struct cx25821_dev *dev) ++{ ++ struct snd_card *card; ++ struct cx25821_audio_dev *chip; ++ int err; ++ ++ if (devno >= SNDRV_CARDS) { ++ pr_info("DEBUG ERROR: devno >= SNDRV_CARDS %s\n", __func__); ++ return -ENODEV; ++ } ++ ++ if (!enable[devno]) { ++ ++devno; ++ pr_info("DEBUG ERROR: !enable[devno] %s\n", __func__); ++ return -ENOENT; ++ } ++ ++ err = snd_card_create(index[devno], id[devno], THIS_MODULE, ++ sizeof(struct cx25821_audio_dev), &card); ++ if (err < 0) { ++ pr_info("DEBUG ERROR: cannot create snd_card_new in %s\n", ++ __func__); ++ return err; ++ } ++ ++ strcpy(card->driver, "cx25821"); ++ ++ /* Card "creation" */ ++ card->private_free = snd_cx25821_dev_free; ++ chip = card->private_data; ++ spin_lock_init(&chip->reg_lock); ++ ++ chip->dev = dev; ++ chip->card = card; ++ chip->pci = dev->pci; ++ chip->iobase = pci_resource_start(dev->pci, 0); ++ ++ chip->irq = dev->pci->irq; ++ ++ err = request_irq(dev->pci->irq, cx25821_irq, ++ IRQF_SHARED, chip->dev->name, chip); ++ ++ if (err < 0) { ++ pr_err("ERROR %s: can't get IRQ %d for ALSA\n", chip->dev->name, ++ dev->pci->irq); ++ goto error; ++ } ++ ++ err = snd_cx25821_pcm(chip, 0, "cx25821 Digital"); ++ if (err < 0) { ++ pr_info("DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", ++ __func__); ++ goto error; ++ } ++ ++ snd_card_set_dev(card, &chip->pci->dev); ++ ++ strcpy(card->shortname, "cx25821"); ++ sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, ++ chip->iobase, chip->irq); ++ strcpy(card->mixername, "CX25821"); ++ ++ pr_info("%s/%i: ALSA support for cx25821 boards\n", card->driver, ++ devno); ++ ++ err = snd_card_register(card); ++ if (err < 0) { ++ pr_info("DEBUG ERROR: cannot register sound card %s\n", ++ __func__); ++ goto error; ++ } ++ ++ snd_cx25821_cards[devno] = card; ++ ++ devno++; ++ return 0; ++ ++error: ++ snd_card_free(card); ++ return err; ++} ++ ++/**************************************************************************** ++ LINUX MODULE INIT ++ ****************************************************************************/ ++static void cx25821_audio_fini(void) ++{ ++ snd_card_free(snd_cx25821_cards[0]); ++} ++ ++/* ++ * Module initializer ++ * ++ * Loops through present saa7134 cards, and assigns an ALSA device ++ * to each one ++ * ++ */ ++static int cx25821_alsa_init(void) ++{ ++ struct cx25821_dev *dev = NULL; ++ struct list_head *list; ++ ++ mutex_lock(&cx25821_devlist_mutex); ++ list_for_each(list, &cx25821_devlist) { ++ dev = list_entry(list, struct cx25821_dev, devlist); ++ cx25821_audio_initdev(dev); ++ } ++ mutex_unlock(&cx25821_devlist_mutex); ++ ++ if (dev == NULL) ++ pr_info("ERROR ALSA: no cx25821 cards found\n"); ++ ++ return 0; ++ ++} ++ ++late_initcall(cx25821_alsa_init); ++module_exit(cx25821_audio_fini); ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.c b/drivers/media/pci/cx25821/cx25821-audio-upstream.c +new file mode 100644 +index 0000000..b5f58bb +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.c +@@ -0,0 +1,779 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * 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. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821-video.h" ++#include "cx25821-audio-upstream.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); ++MODULE_AUTHOR("Hiep Huynh "); ++MODULE_LICENSE("GPL"); ++ ++static int _intr_msk = FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | ++ FLD_AUD_SRC_SYNC | FLD_AUD_SRC_OPC_ERR; ++ ++static int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i, lines; ++ u32 cdt; ++ ++ if (ch->cmds_start == 0) { ++ cx_write(ch->ptr1_reg, 0); ++ cx_write(ch->ptr2_reg, 0); ++ cx_write(ch->cnt2_reg, 0); ++ cx_write(ch->cnt1_reg, 0); ++ return 0; ++ } ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ ++ if (lines > 3) ++ lines = 3; ++ ++ BUG_ON(lines < 2); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) { ++ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); ++ cx_write(cdt + 16 * i + 4, 0); ++ cx_write(cdt + 16 * i + 8, 0); ++ cx_write(cdt + 16 * i + 12, 0); ++ } ++ ++ /* write CMDS */ ++ cx_write(ch->cmds_start + 0, risc); ++ ++ cx_write(ch->cmds_start + 4, 0); ++ cx_write(ch->cmds_start + 8, cdt); ++ cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); ++ cx_write(ch->cmds_start + 16, ch->ctrl_start); ++ ++ /* IQ size */ ++ cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); ++ ++ for (i = 24; i < 80; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); ++ cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); ++ ++ return 0; ++} ++ ++static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, ++ __le32 *rp, ++ dma_addr_t databuf_phys_addr, ++ unsigned int bpl, ++ int fifo_enable) ++{ ++ unsigned int line; ++ struct sram_channel *sram_ch = ++ dev->channels[dev->_audio_upstream_channel].sram_channels; ++ int offset = 0; ++ ++ /* scan lines */ ++ for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { ++ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); ++ *(rp++) = cpu_to_le32(databuf_phys_addr + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ ++ /* Check if we need to enable the FIFO ++ * after the first 3 lines. ++ * For the upstream audio channel, ++ * the risc engine will enable the FIFO */ ++ if (fifo_enable && line == 2) { ++ *(rp++) = RISC_WRITECR; ++ *(rp++) = sram_ch->dma_ctl; ++ *(rp++) = sram_ch->fld_aud_fifo_en; ++ *(rp++) = 0x00000020; ++ } ++ ++ offset += AUDIO_LINE_SIZE; ++ } ++ ++ return rp; ++} ++ ++static int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, ++ struct pci_dev *pci, ++ unsigned int bpl, unsigned int lines) ++{ ++ __le32 *rp; ++ int fifo_enable = 0; ++ int frame = 0, i = 0; ++ int frame_size = AUDIO_DATA_BUF_SZ; ++ int databuf_offset = 0; ++ int risc_flag = RISC_CNT_INC; ++ dma_addr_t risc_phys_jump_addr; ++ ++ /* Virtual address of Risc buffer program */ ++ rp = dev->_risc_virt_addr; ++ ++ /* sync instruction */ ++ *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); ++ ++ for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { ++ databuf_offset = frame_size * frame; ++ ++ if (frame == 0) { ++ fifo_enable = 1; ++ risc_flag = RISC_CNT_RESET; ++ } else { ++ fifo_enable = 0; ++ risc_flag = RISC_CNT_INC; ++ } ++ ++ /* Calculate physical jump address */ ++ if ((frame + 1) == NUM_AUDIO_FRAMES) { ++ risc_phys_jump_addr = ++ dev->_risc_phys_start_addr + ++ RISC_SYNC_INSTRUCTION_SIZE; ++ } else { ++ risc_phys_jump_addr = ++ dev->_risc_phys_start_addr + ++ RISC_SYNC_INSTRUCTION_SIZE + ++ AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); ++ } ++ ++ rp = cx25821_risc_field_upstream_audio(dev, rp, ++ dev->_audiodata_buf_phys_addr + databuf_offset, ++ bpl, fifo_enable); ++ ++ if (USE_RISC_NOOP_AUDIO) { ++ for (i = 0; i < NUM_NO_OPS; i++) ++ *(rp++) = cpu_to_le32(RISC_NOOP); ++ } ++ ++ /* Loop to (Nth)FrameRISC or to Start of Risc program & ++ * generate IRQ */ ++ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ ++ /* Recalculate virtual address based on frame index */ ++ rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + ++ (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); ++ } ++ ++ return 0; ++} ++ ++static void cx25821_free_memory_audio(struct cx25821_dev *dev) ++{ ++ if (dev->_risc_virt_addr) { ++ pci_free_consistent(dev->pci, dev->_audiorisc_size, ++ dev->_risc_virt_addr, dev->_risc_phys_addr); ++ dev->_risc_virt_addr = NULL; ++ } ++ ++ if (dev->_audiodata_buf_virt_addr) { ++ pci_free_consistent(dev->pci, dev->_audiodata_buf_size, ++ dev->_audiodata_buf_virt_addr, ++ dev->_audiodata_buf_phys_addr); ++ dev->_audiodata_buf_virt_addr = NULL; ++ } ++} ++ ++void cx25821_stop_upstream_audio(struct cx25821_dev *dev) ++{ ++ struct sram_channel *sram_ch = ++ dev->channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B].sram_channels; ++ u32 tmp = 0; ++ ++ if (!dev->_audio_is_running) { ++ printk(KERN_DEBUG ++ pr_fmt("No audio file is currently running so return!\n")); ++ return; ++ } ++ /* Disable RISC interrupts */ ++ cx_write(sram_ch->int_msk, 0); ++ ++ /* Turn OFF risc and fifo enable in AUD_DMA_CNTRL */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_write(sram_ch->dma_ctl, ++ tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); ++ ++ /* Clear data buffer memory */ ++ if (dev->_audiodata_buf_virt_addr) ++ memset(dev->_audiodata_buf_virt_addr, 0, ++ dev->_audiodata_buf_size); ++ ++ dev->_audio_is_running = 0; ++ dev->_is_first_audio_frame = 0; ++ dev->_audioframe_count = 0; ++ dev->_audiofile_status = END_OF_FILE; ++ ++ kfree(dev->_irq_audio_queues); ++ dev->_irq_audio_queues = NULL; ++ ++ kfree(dev->_audiofilename); ++} ++ ++void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) ++{ ++ if (dev->_audio_is_running) ++ cx25821_stop_upstream_audio(dev); ++ ++ cx25821_free_memory_audio(dev); ++} ++ ++static int cx25821_get_audio_data(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int frame_index_temp = dev->_audioframe_index; ++ int i = 0; ++ int line_size = AUDIO_LINE_SIZE; ++ int frame_size = AUDIO_DATA_BUF_SZ; ++ int frame_offset = frame_size * frame_index_temp; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t file_offset = dev->_audioframe_count * frame_size; ++ loff_t pos; ++ mm_segment_t old_fs; ++ ++ if (dev->_audiofile_status == END_OF_FILE) ++ return 0; ++ ++ myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); ++ ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_audiofilename, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (i = 0; i < dev->_audio_lines_count; i++) { ++ pos = file_offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, line_size, ++ &pos); ++ ++ if (vfs_read_retval > 0 && vfs_read_retval == line_size ++ && dev->_audiodata_buf_virt_addr != NULL) { ++ memcpy((void *)(dev->_audiodata_buf_virt_addr + ++ frame_offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ file_offset += vfs_read_retval; ++ frame_offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Audio file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_audioframe_count++; ++ ++ dev->_audiofile_status = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static void cx25821_audioups_handler(struct work_struct *work) ++{ ++ struct cx25821_dev *dev = container_of(work, struct cx25821_dev, ++ _audio_work_entry); ++ ++ if (!dev) { ++ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", ++ __func__); ++ return; ++ } ++ ++ cx25821_get_audio_data(dev, dev->channels[dev->_audio_upstream_channel]. ++ sram_channels); ++} ++ ++static int cx25821_openfile_audio(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int i = 0, j = 0; ++ int line_size = AUDIO_LINE_SIZE; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t pos; ++ loff_t offset = (unsigned long)0; ++ mm_segment_t old_fs; ++ ++ myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); ++ ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_audiofilename, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (j = 0; j < NUM_AUDIO_FRAMES; j++) { ++ for (i = 0; i < dev->_audio_lines_count; i++) { ++ pos = offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, ++ line_size, &pos); ++ ++ if (vfs_read_retval > 0 && ++ vfs_read_retval == line_size && ++ dev->_audiodata_buf_virt_addr != NULL) { ++ memcpy((void *)(dev-> ++ _audiodata_buf_virt_addr ++ + offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Audio file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_audioframe_count++; ++ ++ if (vfs_read_retval < line_size) ++ break; ++ } ++ ++ dev->_audiofile_status = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ myfile->f_pos = 0; ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch, ++ int bpl) ++{ ++ int ret = 0; ++ dma_addr_t dma_addr; ++ dma_addr_t data_dma_addr; ++ ++ cx25821_free_memory_audio(dev); ++ ++ dev->_risc_virt_addr = pci_alloc_consistent(dev->pci, ++ dev->audio_upstream_riscbuf_size, &dma_addr); ++ dev->_risc_virt_start_addr = dev->_risc_virt_addr; ++ dev->_risc_phys_start_addr = dma_addr; ++ dev->_risc_phys_addr = dma_addr; ++ dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; ++ ++ if (!dev->_risc_virt_addr) { ++ printk(KERN_DEBUG ++ pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning\n")); ++ return -ENOMEM; ++ } ++ /* Clear out memory at address */ ++ memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); ++ ++ /* For Audio Data buffer allocation */ ++ dev->_audiodata_buf_virt_addr = pci_alloc_consistent(dev->pci, ++ dev->audio_upstream_databuf_size, &data_dma_addr); ++ dev->_audiodata_buf_phys_addr = data_dma_addr; ++ dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; ++ ++ if (!dev->_audiodata_buf_virt_addr) { ++ printk(KERN_DEBUG ++ pr_fmt("ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning\n")); ++ return -ENOMEM; ++ } ++ /* Clear out memory at address */ ++ memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); ++ ++ ret = cx25821_openfile_audio(dev, sram_ch); ++ if (ret < 0) ++ return ret; ++ ++ /* Creating RISC programs */ ++ ret = cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, ++ dev->_audio_lines_count); ++ if (ret < 0) { ++ printk(KERN_DEBUG ++ pr_fmt("ERROR creating audio upstream RISC programs!\n")); ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ return ret; ++} ++ ++static int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, ++ u32 status) ++{ ++ int i = 0; ++ u32 int_msk_tmp; ++ struct sram_channel *channel = dev->channels[chan_num].sram_channels; ++ dma_addr_t risc_phys_jump_addr; ++ __le32 *rp; ++ ++ if (status & FLD_AUD_SRC_RISCI1) { ++ /* Get interrupt_index of the program that interrupted */ ++ u32 prog_cnt = cx_read(channel->gpcnt); ++ ++ /* Since we've identified our IRQ, clear our bits from the ++ * interrupt mask and interrupt status registers */ ++ cx_write(channel->int_msk, 0); ++ cx_write(channel->int_stat, cx_read(channel->int_stat)); ++ ++ spin_lock(&dev->slock); ++ ++ while (prog_cnt != dev->_last_index_irq) { ++ /* Update _last_index_irq */ ++ if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) ++ dev->_last_index_irq++; ++ else ++ dev->_last_index_irq = 0; ++ ++ dev->_audioframe_index = dev->_last_index_irq; ++ ++ queue_work(dev->_irq_audio_queues, ++ &dev->_audio_work_entry); ++ } ++ ++ if (dev->_is_first_audio_frame) { ++ dev->_is_first_audio_frame = 0; ++ ++ if (dev->_risc_virt_start_addr != NULL) { ++ risc_phys_jump_addr = ++ dev->_risc_phys_start_addr + ++ RISC_SYNC_INSTRUCTION_SIZE + ++ AUDIO_RISC_DMA_BUF_SIZE; ++ ++ rp = cx25821_risc_field_upstream_audio(dev, ++ dev->_risc_virt_start_addr + 1, ++ dev->_audiodata_buf_phys_addr, ++ AUDIO_LINE_SIZE, FIFO_DISABLE); ++ ++ if (USE_RISC_NOOP_AUDIO) { ++ for (i = 0; i < NUM_NO_OPS; i++) { ++ *(rp++) = ++ cpu_to_le32(RISC_NOOP); ++ } ++ } ++ /* Jump to 2nd Audio Frame */ ++ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | ++ RISC_CNT_RESET); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ } ++ } ++ ++ spin_unlock(&dev->slock); ++ } else { ++ if (status & FLD_AUD_SRC_OF) ++ pr_warn("%s(): Audio Received Overflow Error Interrupt!\n", ++ __func__); ++ ++ if (status & FLD_AUD_SRC_SYNC) ++ pr_warn("%s(): Audio Received Sync Error Interrupt!\n", ++ __func__); ++ ++ if (status & FLD_AUD_SRC_OPC_ERR) ++ pr_warn("%s(): Audio Received OpCode Error Interrupt!\n", ++ __func__); ++ ++ /* Read and write back the interrupt status register to clear ++ * our bits */ ++ cx_write(channel->int_stat, cx_read(channel->int_stat)); ++ } ++ ++ if (dev->_audiofile_status == END_OF_FILE) { ++ pr_warn("EOF Channel Audio Framecount = %d\n", ++ dev->_audioframe_count); ++ return -1; ++ } ++ /* ElSE, set the interrupt mask register, re-enable irq. */ ++ int_msk_tmp = cx_read(channel->int_msk); ++ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); ++ ++ return 0; ++} ++ ++static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) ++{ ++ struct cx25821_dev *dev = dev_id; ++ u32 audio_status; ++ int handled = 0; ++ struct sram_channel *sram_ch; ++ ++ if (!dev) ++ return -1; ++ ++ sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; ++ ++ audio_status = cx_read(sram_ch->int_stat); ++ ++ /* Only deal with our interrupt */ ++ if (audio_status) { ++ handled = cx25821_audio_upstream_irq(dev, ++ dev->_audio_upstream_channel, audio_status); ++ } ++ ++ if (handled < 0) ++ cx25821_stop_upstream_audio(dev); ++ else ++ handled += handled; ++ ++ return IRQ_RETVAL(handled); ++} ++ ++static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ int count = 0; ++ u32 tmp; ++ ++ do { ++ /* Wait 10 microsecond before checking to see if the FIFO is ++ * turned ON. */ ++ udelay(10); ++ ++ tmp = cx_read(sram_ch->dma_ctl); ++ ++ /* 10 millisecond timeout */ ++ if (count++ > 1000) { ++ pr_err("ERROR: %s() fifo is NOT turned on. Timeout!\n", ++ __func__); ++ return; ++ } ++ ++ } while (!(tmp & sram_ch->fld_aud_fifo_en)); ++ ++} ++ ++static int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ u32 tmp = 0; ++ int err = 0; ++ ++ /* Set the physical start address of the RISC program in the initial ++ * program counter(IPC) member of the CMDS. */ ++ cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); ++ /* Risc IPC High 64 bits 63-32 */ ++ cx_write(sram_ch->cmds_start + 4, 0); ++ ++ /* reset counter */ ++ cx_write(sram_ch->gpcnt_ctl, 3); ++ ++ /* Set the line length (It looks like we do not need to set the ++ * line length) */ ++ cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); ++ ++ /* Set the input mode to 16-bit */ ++ tmp = cx_read(sram_ch->aud_cfg); ++ tmp |= FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | ++ FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | ++ FLD_AUD_SONY_MODE; ++ cx_write(sram_ch->aud_cfg, tmp); ++ ++ /* Read and write back the interrupt status register to clear it */ ++ tmp = cx_read(sram_ch->int_stat); ++ cx_write(sram_ch->int_stat, tmp); ++ ++ /* Clear our bits from the interrupt status register. */ ++ cx_write(sram_ch->int_stat, _intr_msk); ++ ++ /* Set the interrupt mask register, enable irq. */ ++ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); ++ tmp = cx_read(sram_ch->int_msk); ++ cx_write(sram_ch->int_msk, tmp |= _intr_msk); ++ ++ err = request_irq(dev->pci->irq, cx25821_upstream_irq_audio, ++ IRQF_SHARED, dev->name, dev); ++ if (err < 0) { ++ pr_err("%s: can't get upstream IRQ %d\n", dev->name, ++ dev->pci->irq); ++ goto fail_irq; ++ } ++ ++ /* Start the DMA engine */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); ++ ++ dev->_audio_is_running = 1; ++ dev->_is_first_audio_frame = 1; ++ ++ /* The fifo_en bit turns on by the first Risc program */ ++ cx25821_wait_fifo_enable(dev, sram_ch); ++ ++ return 0; ++ ++fail_irq: ++ cx25821_dev_unregister(dev); ++ return err; ++} ++ ++int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) ++{ ++ struct sram_channel *sram_ch; ++ int err = 0; ++ ++ if (dev->_audio_is_running) { ++ pr_warn("Audio Channel is still running so return!\n"); ++ return 0; ++ } ++ ++ dev->_audio_upstream_channel = channel_select; ++ sram_ch = dev->channels[channel_select].sram_channels; ++ ++ /* Work queue */ ++ INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); ++ dev->_irq_audio_queues = ++ create_singlethread_workqueue("cx25821_audioworkqueue"); ++ ++ if (!dev->_irq_audio_queues) { ++ printk(KERN_DEBUG ++ pr_fmt("ERROR: create_singlethread_workqueue() for Audio FAILED!\n")); ++ return -ENOMEM; ++ } ++ ++ dev->_last_index_irq = 0; ++ dev->_audio_is_running = 0; ++ dev->_audioframe_count = 0; ++ dev->_audiofile_status = RESET_STATUS; ++ dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; ++ _line_size = AUDIO_LINE_SIZE; ++ ++ if (dev->input_audiofilename) { ++ dev->_audiofilename = kstrdup(dev->input_audiofilename, ++ GFP_KERNEL); ++ ++ if (!dev->_audiofilename) { ++ err = -ENOMEM; ++ goto error; ++ } ++ ++ /* Default if filename is empty string */ ++ if (strcmp(dev->input_audiofilename, "") == 0) ++ dev->_audiofilename = "/root/audioGOOD.wav"; ++ } else { ++ dev->_audiofilename = kstrdup(_defaultAudioName, ++ GFP_KERNEL); ++ ++ if (!dev->_audiofilename) { ++ err = -ENOMEM; ++ goto error; ++ } ++ } ++ ++ cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, ++ _line_size, 0); ++ ++ dev->audio_upstream_riscbuf_size = ++ AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + ++ RISC_SYNC_INSTRUCTION_SIZE; ++ dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; ++ ++ /* Allocating buffers and prepare RISC program */ ++ err = cx25821_audio_upstream_buffer_prepare(dev, sram_ch, ++ _line_size); ++ if (err < 0) { ++ pr_err("%s: Failed to set up Audio upstream buffers!\n", ++ dev->name); ++ goto error; ++ } ++ /* Start RISC engine */ ++ cx25821_start_audio_dma_upstream(dev, sram_ch); ++ ++ return 0; ++ ++error: ++ cx25821_dev_unregister(dev); ++ ++ return err; ++} +diff --git a/drivers/media/pci/cx25821/cx25821-audio-upstream.h b/drivers/media/pci/cx25821/cx25821-audio-upstream.h +new file mode 100644 +index 0000000..af2ae7c +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-audio-upstream.h +@@ -0,0 +1,62 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#define NUM_AUDIO_PROGS 8 ++#define NUM_AUDIO_FRAMES 8 ++#define END_OF_FILE 0 ++#define IN_PROGRESS 1 ++#define RESET_STATUS -1 ++#define FIFO_DISABLE 0 ++#define FIFO_ENABLE 1 ++#define NUM_NO_OPS 4 ++ ++#define RISC_READ_INSTRUCTION_SIZE 12 ++#define RISC_JUMP_INSTRUCTION_SIZE 12 ++#define RISC_WRITECR_INSTRUCTION_SIZE 16 ++#define RISC_SYNC_INSTRUCTION_SIZE 4 ++#define DWORD_SIZE 4 ++#define AUDIO_SYNC_LINE 4 ++ ++#define LINES_PER_AUDIO_BUFFER 15 ++#define AUDIO_LINE_SIZE 128 ++#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER) ++ ++#define USE_RISC_NOOP_AUDIO 1 ++ ++#ifdef USE_RISC_NOOP_AUDIO ++#define AUDIO_RISC_DMA_BUF_SIZE \ ++ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS * DWORD_SIZE + \ ++ RISC_JUMP_INSTRUCTION_SIZE) ++#endif ++ ++#ifndef USE_RISC_NOOP_AUDIO ++#define AUDIO_RISC_DMA_BUF_SIZE \ ++ (LINES_PER_AUDIO_BUFFER * RISC_READ_INSTRUCTION_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE) ++#endif ++ ++static int _line_size; ++char *_defaultAudioName = "/root/audioGOOD.wav"; +diff --git a/drivers/media/pci/cx25821/cx25821-audio.h b/drivers/media/pci/cx25821/cx25821-audio.h +new file mode 100644 +index 0000000..1fc2d24 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-audio.h +@@ -0,0 +1,62 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __CX25821_AUDIO_H__ ++#define __CX25821_AUDIO_H__ ++ ++#define USE_RISC_NOOP 1 ++#define LINES_PER_BUFFER 15 ++#define AUDIO_LINE_SIZE 128 ++ ++/* Number of buffer programs to use at once. */ ++#define NUMBER_OF_PROGRAMS 8 ++ ++/* ++ * Max size of the RISC program for a buffer. - worst case is 2 writes per line ++ * Space is also added for the 4 no-op instructions added on the end. ++ */ ++#ifndef USE_RISC_NOOP ++#define MAX_BUFFER_PROGRAM_SIZE \ ++ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE * 4) ++#endif ++ ++/* MAE 12 July 2005 Try to use NOOP RISC instruction instead */ ++#ifdef USE_RISC_NOOP ++#define MAX_BUFFER_PROGRAM_SIZE \ ++ (2 * LINES_PER_BUFFER * RISC_WRITE_INSTRUCTION_SIZE + \ ++ RISC_NOOP_INSTRUCTION_SIZE * 4) ++#endif ++ ++/* Sizes of various instructions in bytes. Used when adding instructions. */ ++#define RISC_WRITE_INSTRUCTION_SIZE 12 ++#define RISC_JUMP_INSTRUCTION_SIZE 12 ++#define RISC_SKIP_INSTRUCTION_SIZE 4 ++#define RISC_SYNC_INSTRUCTION_SIZE 4 ++#define RISC_WRITECR_INSTRUCTION_SIZE 16 ++#define RISC_NOOP_INSTRUCTION_SIZE 4 ++ ++#define MAX_AUDIO_DMA_BUFFER_SIZE \ ++ (MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS + \ ++ RISC_SYNC_INSTRUCTION_SIZE) ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-biffuncs.h b/drivers/media/pci/cx25821/cx25821-biffuncs.h +new file mode 100644 +index 0000000..937f5a7 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-biffuncs.h +@@ -0,0 +1,45 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _BITFUNCS_H ++#define _BITFUNCS_H ++ ++#define SetBit(Bit) (1 << Bit) ++ ++static inline u8 getBit(u32 sample, u8 index) ++{ ++ return (u8) ((sample >> index) & 1); ++} ++ ++static inline u32 clearBitAtPos(u32 value, u8 bit) ++{ ++ return value & ~(1 << bit); ++} ++ ++static inline u32 setBitAtPos(u32 sample, u8 bit) ++{ ++ sample |= (1 << bit); ++ return sample; ++ ++} ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-cards.c b/drivers/media/pci/cx25821/cx25821-cards.c +new file mode 100644 +index 0000000..f0adfdb +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-cards.c +@@ -0,0 +1,73 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25821.h" ++#include "tuner-xc2028.h" ++ ++/* board config info */ ++ ++struct cx25821_board cx25821_boards[] = { ++ [UNKNOWN_BOARD] = { ++ .name = "UNKNOWN/GENERIC", ++ /* Ensure safe default for unknown boards */ ++ .clk_freq = 0, ++ }, ++ ++ [CX25821_BOARD] = { ++ .name = "CX25821", ++ .portb = CX25821_RAW, ++ .portc = CX25821_264, ++ .input[0].type = CX25821_VMUX_COMPOSITE, ++ }, ++ ++}; ++ ++const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards); ++ ++struct cx25821_subid cx25821_subids[] = { ++ { ++ .subvendor = 0x14f1, ++ .subdevice = 0x0920, ++ .card = CX25821_BOARD, ++ }, ++}; ++ ++void cx25821_card_setup(struct cx25821_dev *dev) ++{ ++ static u8 eeprom[256]; ++ ++ if (dev->i2c_bus[0].i2c_rc == 0) { ++ dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, ++ sizeof(eeprom)); ++ } ++} +diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c +new file mode 100644 +index 0000000..e50265d +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-core.c +@@ -0,0 +1,1503 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include "cx25821.h" ++#include "cx25821-sram.h" ++#include "cx25821-video.h" ++ ++MODULE_DESCRIPTION("Driver for Athena cards"); ++MODULE_AUTHOR("Shu Lin - Hiep Huynh"); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++ ++static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; ++module_param_array(card, int, NULL, 0444); ++MODULE_PARM_DESC(card, "card type"); ++ ++static unsigned int cx25821_devcount; ++ ++DEFINE_MUTEX(cx25821_devlist_mutex); ++EXPORT_SYMBOL(cx25821_devlist_mutex); ++LIST_HEAD(cx25821_devlist); ++EXPORT_SYMBOL(cx25821_devlist); ++ ++struct sram_channel cx25821_sram_channels[] = { ++ [SRAM_CH00] = { ++ .i = SRAM_CH00, ++ .name = "VID A", ++ .cmds_start = VID_A_DOWN_CMDS, ++ .ctrl_start = VID_A_IQ, ++ .cdt = VID_A_CDT, ++ .fifo_start = VID_A_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA1_PTR1, ++ .ptr2_reg = DMA1_PTR2, ++ .cnt1_reg = DMA1_CNT1, ++ .cnt2_reg = DMA1_CNT2, ++ .int_msk = VID_A_INT_MSK, ++ .int_stat = VID_A_INT_STAT, ++ .int_mstat = VID_A_INT_MSTAT, ++ .dma_ctl = VID_DST_A_DMA_CTL, ++ .gpcnt_ctl = VID_DST_A_GPCNT_CTL, ++ .gpcnt = VID_DST_A_GPCNT, ++ .vip_ctl = VID_DST_A_VIP_CTL, ++ .pix_frmt = VID_DST_A_PIX_FRMT, ++ }, ++ ++ [SRAM_CH01] = { ++ .i = SRAM_CH01, ++ .name = "VID B", ++ .cmds_start = VID_B_DOWN_CMDS, ++ .ctrl_start = VID_B_IQ, ++ .cdt = VID_B_CDT, ++ .fifo_start = VID_B_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA2_PTR1, ++ .ptr2_reg = DMA2_PTR2, ++ .cnt1_reg = DMA2_CNT1, ++ .cnt2_reg = DMA2_CNT2, ++ .int_msk = VID_B_INT_MSK, ++ .int_stat = VID_B_INT_STAT, ++ .int_mstat = VID_B_INT_MSTAT, ++ .dma_ctl = VID_DST_B_DMA_CTL, ++ .gpcnt_ctl = VID_DST_B_GPCNT_CTL, ++ .gpcnt = VID_DST_B_GPCNT, ++ .vip_ctl = VID_DST_B_VIP_CTL, ++ .pix_frmt = VID_DST_B_PIX_FRMT, ++ }, ++ ++ [SRAM_CH02] = { ++ .i = SRAM_CH02, ++ .name = "VID C", ++ .cmds_start = VID_C_DOWN_CMDS, ++ .ctrl_start = VID_C_IQ, ++ .cdt = VID_C_CDT, ++ .fifo_start = VID_C_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA3_PTR1, ++ .ptr2_reg = DMA3_PTR2, ++ .cnt1_reg = DMA3_CNT1, ++ .cnt2_reg = DMA3_CNT2, ++ .int_msk = VID_C_INT_MSK, ++ .int_stat = VID_C_INT_STAT, ++ .int_mstat = VID_C_INT_MSTAT, ++ .dma_ctl = VID_DST_C_DMA_CTL, ++ .gpcnt_ctl = VID_DST_C_GPCNT_CTL, ++ .gpcnt = VID_DST_C_GPCNT, ++ .vip_ctl = VID_DST_C_VIP_CTL, ++ .pix_frmt = VID_DST_C_PIX_FRMT, ++ }, ++ ++ [SRAM_CH03] = { ++ .i = SRAM_CH03, ++ .name = "VID D", ++ .cmds_start = VID_D_DOWN_CMDS, ++ .ctrl_start = VID_D_IQ, ++ .cdt = VID_D_CDT, ++ .fifo_start = VID_D_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA4_PTR1, ++ .ptr2_reg = DMA4_PTR2, ++ .cnt1_reg = DMA4_CNT1, ++ .cnt2_reg = DMA4_CNT2, ++ .int_msk = VID_D_INT_MSK, ++ .int_stat = VID_D_INT_STAT, ++ .int_mstat = VID_D_INT_MSTAT, ++ .dma_ctl = VID_DST_D_DMA_CTL, ++ .gpcnt_ctl = VID_DST_D_GPCNT_CTL, ++ .gpcnt = VID_DST_D_GPCNT, ++ .vip_ctl = VID_DST_D_VIP_CTL, ++ .pix_frmt = VID_DST_D_PIX_FRMT, ++ }, ++ ++ [SRAM_CH04] = { ++ .i = SRAM_CH04, ++ .name = "VID E", ++ .cmds_start = VID_E_DOWN_CMDS, ++ .ctrl_start = VID_E_IQ, ++ .cdt = VID_E_CDT, ++ .fifo_start = VID_E_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA5_PTR1, ++ .ptr2_reg = DMA5_PTR2, ++ .cnt1_reg = DMA5_CNT1, ++ .cnt2_reg = DMA5_CNT2, ++ .int_msk = VID_E_INT_MSK, ++ .int_stat = VID_E_INT_STAT, ++ .int_mstat = VID_E_INT_MSTAT, ++ .dma_ctl = VID_DST_E_DMA_CTL, ++ .gpcnt_ctl = VID_DST_E_GPCNT_CTL, ++ .gpcnt = VID_DST_E_GPCNT, ++ .vip_ctl = VID_DST_E_VIP_CTL, ++ .pix_frmt = VID_DST_E_PIX_FRMT, ++ }, ++ ++ [SRAM_CH05] = { ++ .i = SRAM_CH05, ++ .name = "VID F", ++ .cmds_start = VID_F_DOWN_CMDS, ++ .ctrl_start = VID_F_IQ, ++ .cdt = VID_F_CDT, ++ .fifo_start = VID_F_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA6_PTR1, ++ .ptr2_reg = DMA6_PTR2, ++ .cnt1_reg = DMA6_CNT1, ++ .cnt2_reg = DMA6_CNT2, ++ .int_msk = VID_F_INT_MSK, ++ .int_stat = VID_F_INT_STAT, ++ .int_mstat = VID_F_INT_MSTAT, ++ .dma_ctl = VID_DST_F_DMA_CTL, ++ .gpcnt_ctl = VID_DST_F_GPCNT_CTL, ++ .gpcnt = VID_DST_F_GPCNT, ++ .vip_ctl = VID_DST_F_VIP_CTL, ++ .pix_frmt = VID_DST_F_PIX_FRMT, ++ }, ++ ++ [SRAM_CH06] = { ++ .i = SRAM_CH06, ++ .name = "VID G", ++ .cmds_start = VID_G_DOWN_CMDS, ++ .ctrl_start = VID_G_IQ, ++ .cdt = VID_G_CDT, ++ .fifo_start = VID_G_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA7_PTR1, ++ .ptr2_reg = DMA7_PTR2, ++ .cnt1_reg = DMA7_CNT1, ++ .cnt2_reg = DMA7_CNT2, ++ .int_msk = VID_G_INT_MSK, ++ .int_stat = VID_G_INT_STAT, ++ .int_mstat = VID_G_INT_MSTAT, ++ .dma_ctl = VID_DST_G_DMA_CTL, ++ .gpcnt_ctl = VID_DST_G_GPCNT_CTL, ++ .gpcnt = VID_DST_G_GPCNT, ++ .vip_ctl = VID_DST_G_VIP_CTL, ++ .pix_frmt = VID_DST_G_PIX_FRMT, ++ }, ++ ++ [SRAM_CH07] = { ++ .i = SRAM_CH07, ++ .name = "VID H", ++ .cmds_start = VID_H_DOWN_CMDS, ++ .ctrl_start = VID_H_IQ, ++ .cdt = VID_H_CDT, ++ .fifo_start = VID_H_DOWN_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA8_PTR1, ++ .ptr2_reg = DMA8_PTR2, ++ .cnt1_reg = DMA8_CNT1, ++ .cnt2_reg = DMA8_CNT2, ++ .int_msk = VID_H_INT_MSK, ++ .int_stat = VID_H_INT_STAT, ++ .int_mstat = VID_H_INT_MSTAT, ++ .dma_ctl = VID_DST_H_DMA_CTL, ++ .gpcnt_ctl = VID_DST_H_GPCNT_CTL, ++ .gpcnt = VID_DST_H_GPCNT, ++ .vip_ctl = VID_DST_H_VIP_CTL, ++ .pix_frmt = VID_DST_H_PIX_FRMT, ++ }, ++ ++ [SRAM_CH08] = { ++ .name = "audio from", ++ .cmds_start = AUD_A_DOWN_CMDS, ++ .ctrl_start = AUD_A_IQ, ++ .cdt = AUD_A_CDT, ++ .fifo_start = AUD_A_DOWN_CLUSTER_1, ++ .fifo_size = AUDIO_CLUSTER_SIZE * 3, ++ .ptr1_reg = DMA17_PTR1, ++ .ptr2_reg = DMA17_PTR2, ++ .cnt1_reg = DMA17_CNT1, ++ .cnt2_reg = DMA17_CNT2, ++ }, ++ ++ [SRAM_CH09] = { ++ .i = SRAM_CH09, ++ .name = "VID Upstream I", ++ .cmds_start = VID_I_UP_CMDS, ++ .ctrl_start = VID_I_IQ, ++ .cdt = VID_I_CDT, ++ .fifo_start = VID_I_UP_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA15_PTR1, ++ .ptr2_reg = DMA15_PTR2, ++ .cnt1_reg = DMA15_CNT1, ++ .cnt2_reg = DMA15_CNT2, ++ .int_msk = VID_I_INT_MSK, ++ .int_stat = VID_I_INT_STAT, ++ .int_mstat = VID_I_INT_MSTAT, ++ .dma_ctl = VID_SRC_I_DMA_CTL, ++ .gpcnt_ctl = VID_SRC_I_GPCNT_CTL, ++ .gpcnt = VID_SRC_I_GPCNT, ++ ++ .vid_fmt_ctl = VID_SRC_I_FMT_CTL, ++ .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1, ++ .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2, ++ .vid_cdt_size = VID_SRC_I_CDT_SZ, ++ .irq_bit = 8, ++ }, ++ ++ [SRAM_CH10] = { ++ .i = SRAM_CH10, ++ .name = "VID Upstream J", ++ .cmds_start = VID_J_UP_CMDS, ++ .ctrl_start = VID_J_IQ, ++ .cdt = VID_J_CDT, ++ .fifo_start = VID_J_UP_CLUSTER_1, ++ .fifo_size = (VID_CLUSTER_SIZE << 2), ++ .ptr1_reg = DMA16_PTR1, ++ .ptr2_reg = DMA16_PTR2, ++ .cnt1_reg = DMA16_CNT1, ++ .cnt2_reg = DMA16_CNT2, ++ .int_msk = VID_J_INT_MSK, ++ .int_stat = VID_J_INT_STAT, ++ .int_mstat = VID_J_INT_MSTAT, ++ .dma_ctl = VID_SRC_J_DMA_CTL, ++ .gpcnt_ctl = VID_SRC_J_GPCNT_CTL, ++ .gpcnt = VID_SRC_J_GPCNT, ++ ++ .vid_fmt_ctl = VID_SRC_J_FMT_CTL, ++ .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1, ++ .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2, ++ .vid_cdt_size = VID_SRC_J_CDT_SZ, ++ .irq_bit = 9, ++ }, ++ ++ [SRAM_CH11] = { ++ .i = SRAM_CH11, ++ .name = "Audio Upstream Channel B", ++ .cmds_start = AUD_B_UP_CMDS, ++ .ctrl_start = AUD_B_IQ, ++ .cdt = AUD_B_CDT, ++ .fifo_start = AUD_B_UP_CLUSTER_1, ++ .fifo_size = (AUDIO_CLUSTER_SIZE * 3), ++ .ptr1_reg = DMA22_PTR1, ++ .ptr2_reg = DMA22_PTR2, ++ .cnt1_reg = DMA22_CNT1, ++ .cnt2_reg = DMA22_CNT2, ++ .int_msk = AUD_B_INT_MSK, ++ .int_stat = AUD_B_INT_STAT, ++ .int_mstat = AUD_B_INT_MSTAT, ++ .dma_ctl = AUD_INT_DMA_CTL, ++ .gpcnt_ctl = AUD_B_GPCNT_CTL, ++ .gpcnt = AUD_B_GPCNT, ++ .aud_length = AUD_B_LNGTH, ++ .aud_cfg = AUD_B_CFG, ++ .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN, ++ .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN, ++ .irq_bit = 11, ++ }, ++}; ++EXPORT_SYMBOL(cx25821_sram_channels); ++ ++struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00]; ++struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01]; ++struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02]; ++struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03]; ++struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04]; ++struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05]; ++struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06]; ++struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07]; ++struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09]; ++struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10]; ++struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11]; ++ ++struct cx25821_dmaqueue mpegq; ++ ++static int cx25821_risc_decode(u32 risc) ++{ ++ static const char * const instr[16] = { ++ [RISC_SYNC >> 28] = "sync", ++ [RISC_WRITE >> 28] = "write", ++ [RISC_WRITEC >> 28] = "writec", ++ [RISC_READ >> 28] = "read", ++ [RISC_READC >> 28] = "readc", ++ [RISC_JUMP >> 28] = "jump", ++ [RISC_SKIP >> 28] = "skip", ++ [RISC_WRITERM >> 28] = "writerm", ++ [RISC_WRITECM >> 28] = "writecm", ++ [RISC_WRITECR >> 28] = "writecr", ++ }; ++ static const int incr[16] = { ++ [RISC_WRITE >> 28] = 3, ++ [RISC_JUMP >> 28] = 3, ++ [RISC_SKIP >> 28] = 1, ++ [RISC_SYNC >> 28] = 1, ++ [RISC_WRITERM >> 28] = 3, ++ [RISC_WRITECM >> 28] = 3, ++ [RISC_WRITECR >> 28] = 4, ++ }; ++ static const char * const bits[] = { ++ "12", "13", "14", "resync", ++ "cnt0", "cnt1", "18", "19", ++ "20", "21", "22", "23", ++ "irq1", "irq2", "eol", "sol", ++ }; ++ int i; ++ ++ pr_cont("0x%08x [ %s", ++ risc, instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); ++ for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) { ++ if (risc & (1 << (i + 12))) ++ pr_cont(" %s", bits[i]); ++ } ++ pr_cont(" count=%d ]\n", risc & 0xfff); ++ return incr[risc >> 28] ? incr[risc >> 28] : 1; ++} ++ ++static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ return cx_read(bus->reg_stat) & 0x01; ++} ++ ++static void cx25821_registers_init(struct cx25821_dev *dev) ++{ ++ u32 tmp; ++ ++ /* enable RUN_RISC in Pecos */ ++ cx_write(DEV_CNTRL2, 0x20); ++ ++ /* Set the master PCI interrupt masks to enable video, audio, MBIF, ++ * and GPIO interrupts ++ * I2C interrupt masking is handled by the I2C objects themselves. */ ++ cx_write(PCI_INT_MSK, 0x2001FFFF); ++ ++ tmp = cx_read(RDR_TLCTL0); ++ tmp &= ~FLD_CFG_RCB_CK_EN; /* Clear the RCB_CK_EN bit */ ++ cx_write(RDR_TLCTL0, tmp); ++ ++ /* PLL-A setting for the Audio Master Clock */ ++ cx_write(PLL_A_INT_FRAC, 0x9807A58B); ++ ++ /* PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 */ ++ cx_write(PLL_A_POST_STAT_BIST, 0x8000019C); ++ ++ /* clear reset bit [31] */ ++ tmp = cx_read(PLL_A_INT_FRAC); ++ cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF); ++ ++ /* PLL-B setting for Mobilygen Host Bus Interface */ ++ cx_write(PLL_B_INT_FRAC, 0x9883A86F); ++ ++ /* PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 */ ++ cx_write(PLL_B_POST_STAT_BIST, 0x8000018D); ++ ++ /* clear reset bit [31] */ ++ tmp = cx_read(PLL_B_INT_FRAC); ++ cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF); ++ ++ /* PLL-C setting for video upstream channel */ ++ cx_write(PLL_C_INT_FRAC, 0x96A0EA3F); ++ ++ /* PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 */ ++ cx_write(PLL_C_POST_STAT_BIST, 0x80000103); ++ ++ /* clear reset bit [31] */ ++ tmp = cx_read(PLL_C_INT_FRAC); ++ cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF); ++ ++ /* PLL-D setting for audio upstream channel */ ++ cx_write(PLL_D_INT_FRAC, 0x98757F5B); ++ ++ /* PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 */ ++ cx_write(PLL_D_POST_STAT_BIST, 0x80000113); ++ ++ /* clear reset bit [31] */ ++ tmp = cx_read(PLL_D_INT_FRAC); ++ cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF); ++ ++ /* This selects the PLL C clock source for the video upstream channel ++ * I and J */ ++ tmp = cx_read(VID_CH_CLK_SEL); ++ cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000); ++ ++ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for ++ * channel A-C ++ * select 656/VIP DST for downstream Channel A - C */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ /* cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); */ ++ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); ++ ++ /* enables 656 port I and J as output */ ++ tmp = cx_read(CLK_RST); ++ /* use external ALT_PLL_REF pin as its reference clock instead */ ++ tmp |= FLD_USE_ALT_PLL_REF; ++ cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); ++ ++ mdelay(100); ++} ++ ++int cx25821_sram_channel_setup(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i, lines; ++ u32 cdt; ++ ++ if (ch->cmds_start == 0) { ++ cx_write(ch->ptr1_reg, 0); ++ cx_write(ch->ptr2_reg, 0); ++ cx_write(ch->cnt2_reg, 0); ++ cx_write(ch->cnt1_reg, 0); ++ return 0; ++ } ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ ++ if (lines > 4) ++ lines = 4; ++ ++ BUG_ON(lines < 2); ++ ++ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ cx_write(8 + 4, 8); ++ cx_write(8 + 8, 0); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) { ++ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); ++ cx_write(cdt + 16 * i + 4, 0); ++ cx_write(cdt + 16 * i + 8, 0); ++ cx_write(cdt + 16 * i + 12, 0); ++ } ++ ++ /* init the first cdt buffer */ ++ for (i = 0; i < 128; i++) ++ cx_write(ch->fifo_start + 4 * i, i); ++ ++ /* write CMDS */ ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 0, 8); ++ else ++ cx_write(ch->cmds_start + 0, risc); ++ ++ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ ++ cx_write(ch->cmds_start + 8, cdt); ++ cx_write(ch->cmds_start + 12, (lines * 16) >> 3); ++ cx_write(ch->cmds_start + 16, ch->ctrl_start); ++ ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); ++ else ++ cx_write(ch->cmds_start + 20, 64 >> 2); ++ ++ for (i = 24; i < 80; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt2_reg, (lines * 16) >> 3); ++ cx_write(ch->cnt1_reg, (bpl >> 3) - 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx25821_sram_channel_setup); ++ ++int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i, lines; ++ u32 cdt; ++ ++ if (ch->cmds_start == 0) { ++ cx_write(ch->ptr1_reg, 0); ++ cx_write(ch->ptr2_reg, 0); ++ cx_write(ch->cnt2_reg, 0); ++ cx_write(ch->cnt1_reg, 0); ++ return 0; ++ } ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ ++ if (lines > 3) ++ lines = 3; /* for AUDIO */ ++ ++ BUG_ON(lines < 2); ++ ++ cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ cx_write(8 + 4, 8); ++ cx_write(8 + 8, 0); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) { ++ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); ++ cx_write(cdt + 16 * i + 4, 0); ++ cx_write(cdt + 16 * i + 8, 0); ++ cx_write(cdt + 16 * i + 12, 0); ++ } ++ ++ /* write CMDS */ ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 0, 8); ++ else ++ cx_write(ch->cmds_start + 0, risc); ++ ++ cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ ++ cx_write(ch->cmds_start + 8, cdt); ++ cx_write(ch->cmds_start + 12, (lines * 16) >> 3); ++ cx_write(ch->cmds_start + 16, ch->ctrl_start); ++ ++ /* IQ size */ ++ if (ch->jumponly) ++ cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); ++ else ++ cx_write(ch->cmds_start + 20, 64 >> 2); ++ ++ /* zero out */ ++ for (i = 24; i < 80; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt2_reg, (lines * 16) >> 3); ++ cx_write(ch->cnt1_reg, (bpl >> 3) - 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); ++ ++void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) ++{ ++ static char *name[] = { ++ "init risc lo", ++ "init risc hi", ++ "cdt base", ++ "cdt size", ++ "iq base", ++ "iq size", ++ "risc pc lo", ++ "risc pc hi", ++ "iq wr ptr", ++ "iq rd ptr", ++ "cdt current", ++ "pci target lo", ++ "pci target hi", ++ "line / byte", ++ }; ++ u32 risc; ++ unsigned int i, j, n; ++ ++ pr_warn("%s: %s - dma channel status dump\n", dev->name, ch->name); ++ for (i = 0; i < ARRAY_SIZE(name); i++) ++ pr_warn("cmds + 0x%2x: %-15s: 0x%08x\n", ++ i * 4, name[i], cx_read(ch->cmds_start + 4 * i)); ++ ++ j = i * 4; ++ for (i = 0; i < 4;) { ++ risc = cx_read(ch->cmds_start + 4 * (i + 14)); ++ pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i); ++ i += cx25821_risc_decode(risc); ++ } ++ ++ for (i = 0; i < (64 >> 2); i += n) { ++ risc = cx_read(ch->ctrl_start + 4 * i); ++ /* No consideration for bits 63-32 */ ++ ++ pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ", ++ i * 4, ch->ctrl_start + 4 * i, i); ++ n = cx25821_risc_decode(risc); ++ for (j = 1; j < n; j++) { ++ risc = cx_read(ch->ctrl_start + 4 * (i + j)); ++ pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", ++ 4 * (i + j), i + j, risc, j); ++ } ++ } ++ ++ pr_warn(" : fifo: 0x%08x -> 0x%x\n", ++ ch->fifo_start, ch->fifo_start + ch->fifo_size); ++ pr_warn(" : ctrl: 0x%08x -> 0x%x\n", ++ ch->ctrl_start, ch->ctrl_start + 6 * 16); ++ pr_warn(" : ptr1_reg: 0x%08x\n", ++ cx_read(ch->ptr1_reg)); ++ pr_warn(" : ptr2_reg: 0x%08x\n", ++ cx_read(ch->ptr2_reg)); ++ pr_warn(" : cnt1_reg: 0x%08x\n", ++ cx_read(ch->cnt1_reg)); ++ pr_warn(" : cnt2_reg: 0x%08x\n", ++ cx_read(ch->cnt2_reg)); ++} ++EXPORT_SYMBOL(cx25821_sram_channel_dump); ++ ++void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, ++ struct sram_channel *ch) ++{ ++ static const char * const name[] = { ++ "init risc lo", ++ "init risc hi", ++ "cdt base", ++ "cdt size", ++ "iq base", ++ "iq size", ++ "risc pc lo", ++ "risc pc hi", ++ "iq wr ptr", ++ "iq rd ptr", ++ "cdt current", ++ "pci target lo", ++ "pci target hi", ++ "line / byte", ++ }; ++ ++ u32 risc, value, tmp; ++ unsigned int i, j, n; ++ ++ pr_info("\n%s: %s - dma Audio channel status dump\n", ++ dev->name, ch->name); ++ ++ for (i = 0; i < ARRAY_SIZE(name); i++) ++ pr_info("%s: cmds + 0x%2x: %-15s: 0x%08x\n", ++ dev->name, i * 4, name[i], ++ cx_read(ch->cmds_start + 4 * i)); ++ ++ j = i * 4; ++ for (i = 0; i < 4;) { ++ risc = cx_read(ch->cmds_start + 4 * (i + 14)); ++ pr_warn("cmds + 0x%2x: risc%d: ", j + i * 4, i); ++ i += cx25821_risc_decode(risc); ++ } ++ ++ for (i = 0; i < (64 >> 2); i += n) { ++ risc = cx_read(ch->ctrl_start + 4 * i); ++ /* No consideration for bits 63-32 */ ++ ++ pr_warn("ctrl + 0x%2x (0x%08x): iq %x: ", ++ i * 4, ch->ctrl_start + 4 * i, i); ++ n = cx25821_risc_decode(risc); ++ ++ for (j = 1; j < n; j++) { ++ risc = cx_read(ch->ctrl_start + 4 * (i + j)); ++ pr_warn("ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", ++ 4 * (i + j), i + j, risc, j); ++ } ++ } ++ ++ pr_warn(" : fifo: 0x%08x -> 0x%x\n", ++ ch->fifo_start, ch->fifo_start + ch->fifo_size); ++ pr_warn(" : ctrl: 0x%08x -> 0x%x\n", ++ ch->ctrl_start, ch->ctrl_start + 6 * 16); ++ pr_warn(" : ptr1_reg: 0x%08x\n", ++ cx_read(ch->ptr1_reg)); ++ pr_warn(" : ptr2_reg: 0x%08x\n", ++ cx_read(ch->ptr2_reg)); ++ pr_warn(" : cnt1_reg: 0x%08x\n", ++ cx_read(ch->cnt1_reg)); ++ pr_warn(" : cnt2_reg: 0x%08x\n", ++ cx_read(ch->cnt2_reg)); ++ ++ for (i = 0; i < 4; i++) { ++ risc = cx_read(ch->cmds_start + 56 + (i * 4)); ++ pr_warn("instruction %d = 0x%x\n", i, risc); ++ } ++ ++ /* read data from the first cdt buffer */ ++ risc = cx_read(AUD_A_CDT); ++ pr_warn("\nread cdt loc=0x%x\n", risc); ++ for (i = 0; i < 8; i++) { ++ n = cx_read(risc + i * 4); ++ pr_cont("0x%x ", n); ++ } ++ pr_cont("\n\n"); ++ ++ value = cx_read(CLK_RST); ++ CX25821_INFO(" CLK_RST = 0x%x\n\n", value); ++ ++ value = cx_read(PLL_A_POST_STAT_BIST); ++ CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x\n\n", value); ++ value = cx_read(PLL_A_INT_FRAC); ++ CX25821_INFO(" PLL_A_INT_FRAC = 0x%x\n\n", value); ++ ++ value = cx_read(PLL_B_POST_STAT_BIST); ++ CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x\n\n", value); ++ value = cx_read(PLL_B_INT_FRAC); ++ CX25821_INFO(" PLL_B_INT_FRAC = 0x%x\n\n", value); ++ ++ value = cx_read(PLL_C_POST_STAT_BIST); ++ CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x\n\n", value); ++ value = cx_read(PLL_C_INT_FRAC); ++ CX25821_INFO(" PLL_C_INT_FRAC = 0x%x\n\n", value); ++ ++ value = cx_read(PLL_D_POST_STAT_BIST); ++ CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x\n\n", value); ++ value = cx_read(PLL_D_INT_FRAC); ++ CX25821_INFO(" PLL_D_INT_FRAC = 0x%x\n\n", value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); ++ CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x\n\n", value); ++} ++EXPORT_SYMBOL(cx25821_sram_channel_dump_audio); ++ ++static void cx25821_shutdown(struct cx25821_dev *dev) ++{ ++ int i; ++ ++ /* disable RISC controller */ ++ cx_write(DEV_CNTRL2, 0); ++ ++ /* Disable Video A/B activity */ ++ for (i = 0; i < VID_CHANNEL_NUM; i++) { ++ cx_write(dev->channels[i].sram_channels->dma_ctl, 0); ++ cx_write(dev->channels[i].sram_channels->int_msk, 0); ++ } ++ ++ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; ++ i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { ++ cx_write(dev->channels[i].sram_channels->dma_ctl, 0); ++ cx_write(dev->channels[i].sram_channels->int_msk, 0); ++ } ++ ++ /* Disable Audio activity */ ++ cx_write(AUD_INT_DMA_CTL, 0); ++ ++ /* Disable Serial port */ ++ cx_write(UART_CTL, 0); ++ ++ /* Disable Interrupts */ ++ cx_write(PCI_INT_MSK, 0); ++ cx_write(AUD_A_INT_MSK, 0); ++} ++ ++void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select, ++ u32 format) ++{ ++ if (channel_select <= 7 && channel_select >= 0) { ++ cx_write(dev->channels[channel_select].sram_channels->pix_frmt, ++ format); ++ dev->channels[channel_select].pixel_formats = format; ++ } ++} ++ ++static void cx25821_set_vip_mode(struct cx25821_dev *dev, ++ struct sram_channel *ch) ++{ ++ cx_write(ch->pix_frmt, PIXEL_FRMT_422); ++ cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1); ++} ++ ++static void cx25821_initialize(struct cx25821_dev *dev) ++{ ++ int i; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ cx25821_shutdown(dev); ++ cx_write(PCI_INT_STAT, 0xffffffff); ++ ++ for (i = 0; i < VID_CHANNEL_NUM; i++) ++ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); ++ ++ cx_write(AUD_A_INT_STAT, 0xffffffff); ++ cx_write(AUD_B_INT_STAT, 0xffffffff); ++ cx_write(AUD_C_INT_STAT, 0xffffffff); ++ cx_write(AUD_D_INT_STAT, 0xffffffff); ++ cx_write(AUD_E_INT_STAT, 0xffffffff); ++ ++ cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); ++ cx_write(PAD_CTRL, 0x12); /* for I2C */ ++ cx25821_registers_init(dev); /* init Pecos registers */ ++ mdelay(100); ++ ++ for (i = 0; i < VID_CHANNEL_NUM; i++) { ++ cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); ++ cx25821_sram_channel_setup(dev, dev->channels[i].sram_channels, ++ 1440, 0); ++ dev->channels[i].pixel_formats = PIXEL_FRMT_422; ++ dev->channels[i].use_cif_resolution = FALSE; ++ } ++ ++ /* Probably only affect Downstream */ ++ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; ++ i <= VID_UPSTREAM_SRAM_CHANNEL_J; i++) { ++ cx25821_set_vip_mode(dev, dev->channels[i].sram_channels); ++ } ++ ++ cx25821_sram_channel_setup_audio(dev, ++ dev->channels[SRAM_CH08].sram_channels, 128, 0); ++ ++ cx25821_gpio_init(dev); ++} ++ ++static int cx25821_get_resources(struct cx25821_dev *dev) ++{ ++ if (request_mem_region(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0), dev->name)) ++ return 0; ++ ++ pr_err("%s: can't get MMIO memory @ 0x%llx\n", ++ dev->name, (unsigned long long)pci_resource_start(dev->pci, 0)); ++ ++ return -EBUSY; ++} ++ ++static void cx25821_dev_checkrevision(struct cx25821_dev *dev) ++{ ++ dev->hwrevision = cx_read(RDR_CFG2) & 0xff; ++ ++ pr_info("%s(): Hardware revision = 0x%02x\n", ++ __func__, dev->hwrevision); ++} ++ ++static void cx25821_iounmap(struct cx25821_dev *dev) ++{ ++ if (dev == NULL) ++ return; ++ ++ /* Releasing IO memory */ ++ if (dev->lmmio != NULL) { ++ CX25821_INFO("Releasing lmmio.\n"); ++ iounmap(dev->lmmio); ++ dev->lmmio = NULL; ++ } ++} ++ ++static int cx25821_dev_setup(struct cx25821_dev *dev) ++{ ++ int i; ++ ++ pr_info("\n***********************************\n"); ++ pr_info("cx25821 set up\n"); ++ pr_info("***********************************\n\n"); ++ ++ mutex_init(&dev->lock); ++ ++ atomic_inc(&dev->refcount); ++ ++ dev->nr = ++cx25821_devcount; ++ sprintf(dev->name, "cx25821[%d]", dev->nr); ++ ++ mutex_lock(&cx25821_devlist_mutex); ++ list_add_tail(&dev->devlist, &cx25821_devlist); ++ mutex_unlock(&cx25821_devlist_mutex); ++ ++ if (dev->pci->device != 0x8210) { ++ pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", ++ __func__, dev->pci->device); ++ return -1; ++ } else { ++ pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); ++ } ++ ++ /* Apply a sensible clock frequency for the PCIe bridge */ ++ dev->clk_freq = 28000000; ++ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) ++ dev->channels[i].sram_channels = &cx25821_sram_channels[i]; ++ ++ if (dev->nr > 1) ++ CX25821_INFO("dev->nr > 1!"); ++ ++ /* board config */ ++ dev->board = 1; /* card[dev->nr]; */ ++ dev->_max_num_decoders = MAX_DECODERS; ++ ++ dev->pci_bus = dev->pci->bus->number; ++ dev->pci_slot = PCI_SLOT(dev->pci->devfn); ++ dev->pci_irqmask = 0x001f00; ++ ++ /* External Master 1 Bus */ ++ dev->i2c_bus[0].nr = 0; ++ dev->i2c_bus[0].dev = dev; ++ dev->i2c_bus[0].reg_stat = I2C1_STAT; ++ dev->i2c_bus[0].reg_ctrl = I2C1_CTRL; ++ dev->i2c_bus[0].reg_addr = I2C1_ADDR; ++ dev->i2c_bus[0].reg_rdata = I2C1_RDATA; ++ dev->i2c_bus[0].reg_wdata = I2C1_WDATA; ++ dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */ ++ ++ if (cx25821_get_resources(dev) < 0) { ++ pr_err("%s: No more PCIe resources for subsystem: %04x:%04x\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device); ++ ++ cx25821_devcount--; ++ return -EBUSY; ++ } ++ ++ /* PCIe stuff */ ++ dev->base_io_addr = pci_resource_start(dev->pci, 0); ++ ++ if (!dev->base_io_addr) { ++ CX25821_ERR("No PCI Memory resources, exiting!\n"); ++ return -ENODEV; ++ } ++ ++ dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0)); ++ ++ if (!dev->lmmio) { ++ CX25821_ERR("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n"); ++ cx25821_iounmap(dev); ++ return -ENOMEM; ++ } ++ ++ dev->bmmio = (u8 __iomem *) dev->lmmio; ++ ++ pr_info("%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device, cx25821_boards[dev->board].name, ++ dev->board, card[dev->nr] == dev->board ? ++ "insmod option" : "autodetected"); ++ ++ /* init hardware */ ++ cx25821_initialize(dev); ++ ++ cx25821_i2c_register(&dev->i2c_bus[0]); ++/* cx25821_i2c_register(&dev->i2c_bus[1]); ++ * cx25821_i2c_register(&dev->i2c_bus[2]); */ ++ ++ CX25821_INFO("i2c register! bus->i2c_rc = %d\n", ++ dev->i2c_bus[0].i2c_rc); ++ ++ cx25821_card_setup(dev); ++ ++ if (medusa_video_init(dev) < 0) ++ CX25821_ERR("%s(): Failed to initialize medusa!\n", __func__); ++ ++ cx25821_video_register(dev); ++ ++ /* register IOCTL device */ ++ dev->ioctl_dev = cx25821_vdev_init(dev, dev->pci, ++ &cx25821_videoioctl_template, "video"); ++ ++ if (video_register_device ++ (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) { ++ cx25821_videoioctl_unregister(dev); ++ pr_err("%s(): Failed to register video adapter for IOCTL, so unregistering videoioctl device\n", ++ __func__); ++ } ++ ++ cx25821_dev_checkrevision(dev); ++ CX25821_INFO("setup done!\n"); ++ ++ return 0; ++} ++ ++void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, ++ struct upstream_user_struct *up_data) ++{ ++ dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0; ++ ++ dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; ++ medusa_set_videostandard(dev); ++ ++ cx25821_vidupstream_init_ch1(dev, dev->channel_select, ++ dev->pixel_format); ++} ++ ++void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, ++ struct upstream_user_struct *up_data) ++{ ++ dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0; ++ ++ dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; ++ medusa_set_videostandard(dev); ++ ++ cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2, ++ dev->pixel_format_ch2); ++} ++ ++void cx25821_start_upstream_audio(struct cx25821_dev *dev, ++ struct upstream_user_struct *up_data) ++{ ++ cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B); ++} ++ ++void cx25821_dev_unregister(struct cx25821_dev *dev) ++{ ++ int i; ++ ++ if (!dev->base_io_addr) ++ return; ++ ++ cx25821_free_mem_upstream_ch1(dev); ++ cx25821_free_mem_upstream_ch2(dev); ++ cx25821_free_mem_upstream_audio(dev); ++ ++ release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); ++ ++ if (!atomic_dec_and_test(&dev->refcount)) ++ return; ++ ++ for (i = 0; i < VID_CHANNEL_NUM; i++) ++ cx25821_video_unregister(dev, i); ++ ++ for (i = VID_UPSTREAM_SRAM_CHANNEL_I; ++ i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { ++ cx25821_video_unregister(dev, i); ++ } ++ ++ cx25821_videoioctl_unregister(dev); ++ ++ cx25821_i2c_unregister(&dev->i2c_bus[0]); ++ cx25821_iounmap(dev); ++} ++EXPORT_SYMBOL(cx25821_dev_unregister); ++ ++static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, ++ unsigned int offset, u32 sync_line, ++ unsigned int bpl, unsigned int padding, ++ unsigned int lines) ++{ ++ struct scatterlist *sg; ++ unsigned int line, todo; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ /* scan lines */ ++ sg = sglist; ++ for (line = 0; line < lines; line++) { ++ while (offset && offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ sg++; ++ } ++ if (bpl <= sg_dma_len(sg) - offset) { ++ /* fits into current chunk */ ++ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL | ++ bpl); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += bpl; ++ } else { ++ /* scanline needs to be split */ ++ todo = bpl; ++ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_SOL | ++ (sg_dma_len(sg) - offset)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= (sg_dma_len(sg) - offset); ++ offset = 0; ++ sg++; ++ while (todo > sg_dma_len(sg)) { ++ *(rp++) = cpu_to_le32(RISC_WRITE | ++ sg_dma_len(sg)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= sg_dma_len(sg); ++ sg++; ++ } ++ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += todo; ++ } ++ ++ offset += padding; ++ } ++ ++ return rp; ++} ++ ++int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, unsigned int top_offset, ++ unsigned int bottom_offset, unsigned int bpl, ++ unsigned int padding, unsigned int lines) ++{ ++ u32 instructions; ++ u32 fields; ++ __le32 *rp; ++ int rc; ++ ++ fields = 0; ++ if (UNSET != top_offset) ++ fields++; ++ if (UNSET != bottom_offset) ++ fields++; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Padding ++ can cause next bpl to start close to a page border. First DMA ++ region may be smaller than PAGE_SIZE */ ++ /* write and jump need and extra dword */ ++ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + ++ lines); ++ instructions += 2; ++ rc = btcx_riscmem_alloc(pci, risc, instructions * 12); ++ ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ ++ if (UNSET != top_offset) { ++ rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding, ++ lines); ++ } ++ ++ if (UNSET != bottom_offset) { ++ rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl, ++ padding, lines); ++ } ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ ++ return 0; ++} ++ ++static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist, ++ unsigned int offset, u32 sync_line, ++ unsigned int bpl, unsigned int padding, ++ unsigned int lines, unsigned int lpi) ++{ ++ struct scatterlist *sg; ++ unsigned int line, todo, sol; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ /* scan lines */ ++ sg = sglist; ++ for (line = 0; line < lines; line++) { ++ while (offset && offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ sg++; ++ } ++ ++ if (lpi && line > 0 && !(line % lpi)) ++ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; ++ else ++ sol = RISC_SOL; ++ ++ if (bpl <= sg_dma_len(sg) - offset) { ++ /* fits into current chunk */ ++ *(rp++) = cpu_to_le32(RISC_WRITE | sol | RISC_EOL | ++ bpl); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += bpl; ++ } else { ++ /* scanline needs to be split */ ++ todo = bpl; ++ *(rp++) = cpu_to_le32(RISC_WRITE | sol | ++ (sg_dma_len(sg) - offset)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= (sg_dma_len(sg) - offset); ++ offset = 0; ++ sg++; ++ while (todo > sg_dma_len(sg)) { ++ *(rp++) = cpu_to_le32(RISC_WRITE | ++ sg_dma_len(sg)); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ todo -= sg_dma_len(sg); ++ sg++; ++ } ++ *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); ++ *(rp++) = cpu_to_le32(sg_dma_address(sg)); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ offset += todo; ++ } ++ offset += padding; ++ } ++ ++ return rp; ++} ++ ++int cx25821_risc_databuffer_audio(struct pci_dev *pci, ++ struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int bpl, ++ unsigned int lines, unsigned int lpi) ++{ ++ u32 instructions; ++ __le32 *rp; ++ int rc; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Here ++ there is no padding and no sync. First DMA region may be smaller ++ than PAGE_SIZE */ ++ /* Jump and write need an extra dword */ ++ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; ++ instructions += 1; ++ ++ rc = btcx_riscmem_alloc(pci, risc, instructions * 12); ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ rp = cx25821_risc_field_audio(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, ++ lines, lpi); ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); ++ return 0; ++} ++EXPORT_SYMBOL(cx25821_risc_databuffer_audio); ++ ++int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value) ++{ ++ __le32 *rp; ++ int rc; ++ ++ rc = btcx_riscmem_alloc(pci, risc, 4 * 16); ++ ++ if (rc < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ ++ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1); ++ *(rp++) = cpu_to_le32(reg); ++ *(rp++) = cpu_to_le32(value); ++ *(rp++) = cpu_to_le32(mask); ++ *(rp++) = cpu_to_le32(RISC_JUMP); ++ *(rp++) = cpu_to_le32(risc->dma); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ return 0; ++} ++ ++void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) ++{ ++ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); ++ ++ BUG_ON(in_interrupt()); ++ videobuf_waiton(q, &buf->vb, 0, 0); ++ videobuf_dma_unmap(q->dev, dma); ++ videobuf_dma_free(dma); ++ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static irqreturn_t cx25821_irq(int irq, void *dev_id) ++{ ++ struct cx25821_dev *dev = dev_id; ++ u32 pci_status; ++ u32 vid_status; ++ int i, handled = 0; ++ u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; ++ ++ pci_status = cx_read(PCI_INT_STAT); ++ ++ if (pci_status == 0) ++ goto out; ++ ++ for (i = 0; i < VID_CHANNEL_NUM; i++) { ++ if (pci_status & mask[i]) { ++ vid_status = cx_read(dev->channels[i]. ++ sram_channels->int_stat); ++ ++ if (vid_status) ++ handled += cx25821_video_irq(dev, i, ++ vid_status); ++ ++ cx_write(PCI_INT_STAT, mask[i]); ++ } ++ } ++ ++out: ++ return IRQ_RETVAL(handled); ++} ++ ++void cx25821_print_irqbits(char *name, char *tag, char **strings, ++ int len, u32 bits, u32 mask) ++{ ++ unsigned int i; ++ ++ printk(KERN_DEBUG pr_fmt("%s: %s [0x%x]"), name, tag, bits); ++ ++ for (i = 0; i < len; i++) { ++ if (!(bits & (1 << i))) ++ continue; ++ if (strings[i]) ++ pr_cont(" %s", strings[i]); ++ else ++ pr_cont(" %d", i); ++ if (!(mask & (1 << i))) ++ continue; ++ pr_cont("*"); ++ } ++ pr_cont("\n"); ++} ++EXPORT_SYMBOL(cx25821_print_irqbits); ++ ++struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci) ++{ ++ struct cx25821_dev *dev = pci_get_drvdata(pci); ++ return dev; ++} ++EXPORT_SYMBOL(cx25821_dev_get); ++ ++static int __devinit cx25821_initdev(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct cx25821_dev *dev; ++ int err = 0; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (NULL == dev) ++ return -ENOMEM; ++ ++ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); ++ if (err < 0) ++ goto fail_free; ++ ++ /* pci init */ ++ dev->pci = pci_dev; ++ if (pci_enable_device(pci_dev)) { ++ err = -EIO; ++ ++ pr_info("pci enable failed!\n"); ++ ++ goto fail_unregister_device; ++ } ++ ++ pr_info("Athena pci enable !\n"); ++ ++ err = cx25821_dev_setup(dev); ++ if (err) { ++ if (err == -EBUSY) ++ goto fail_unregister_device; ++ else ++ goto fail_unregister_pci; ++ } ++ ++ /* print pci info */ ++ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); ++ pr_info("%s/0: found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n", ++ dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, ++ dev->pci_lat, (unsigned long long)dev->base_io_addr); ++ ++ pci_set_master(pci_dev); ++ if (!pci_dma_supported(pci_dev, 0xffffffff)) { ++ pr_err("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); ++ err = -EIO; ++ goto fail_irq; ++ } ++ ++ err = request_irq(pci_dev->irq, cx25821_irq, ++ IRQF_SHARED, dev->name, dev); ++ ++ if (err < 0) { ++ pr_err("%s: can't get IRQ %d\n", dev->name, pci_dev->irq); ++ goto fail_irq; ++ } ++ ++ return 0; ++ ++fail_irq: ++ pr_info("cx25821_initdev() can't get IRQ !\n"); ++ cx25821_dev_unregister(dev); ++ ++fail_unregister_pci: ++ pci_disable_device(pci_dev); ++fail_unregister_device: ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++fail_free: ++ kfree(dev); ++ return err; ++} ++ ++static void __devexit cx25821_finidev(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct cx25821_dev *dev = get_cx25821(v4l2_dev); ++ ++ cx25821_shutdown(dev); ++ pci_disable_device(pci_dev); ++ ++ /* unregister stuff */ ++ if (pci_dev->irq) ++ free_irq(pci_dev->irq, dev); ++ ++ mutex_lock(&cx25821_devlist_mutex); ++ list_del(&dev->devlist); ++ mutex_unlock(&cx25821_devlist_mutex); ++ ++ cx25821_dev_unregister(dev); ++ v4l2_device_unregister(v4l2_dev); ++ kfree(dev); ++} ++ ++static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = { ++ { ++ /* CX25821 Athena */ ++ .vendor = 0x14f1, ++ .device = 0x8210, ++ .subvendor = 0x14f1, ++ .subdevice = 0x0920, ++ }, { ++ /* CX25821 No Brand */ ++ .vendor = 0x14f1, ++ .device = 0x8210, ++ .subvendor = 0x0000, ++ .subdevice = 0x0000, ++ }, { ++ /* --- end of list --- */ ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl); ++ ++static struct pci_driver cx25821_pci_driver = { ++ .name = "cx25821", ++ .id_table = cx25821_pci_tbl, ++ .probe = cx25821_initdev, ++ .remove = __devexit_p(cx25821_finidev), ++ /* TODO */ ++ .suspend = NULL, ++ .resume = NULL, ++}; ++ ++static int __init cx25821_init(void) ++{ ++ pr_info("driver version %d.%d.%d loaded\n", ++ (CX25821_VERSION_CODE >> 16) & 0xff, ++ (CX25821_VERSION_CODE >> 8) & 0xff, ++ CX25821_VERSION_CODE & 0xff); ++ return pci_register_driver(&cx25821_pci_driver); ++} ++ ++static void __exit cx25821_fini(void) ++{ ++ pci_unregister_driver(&cx25821_pci_driver); ++} ++ ++module_init(cx25821_init); ++module_exit(cx25821_fini); +diff --git a/drivers/media/pci/cx25821/cx25821-gpio.c b/drivers/media/pci/cx25821/cx25821-gpio.c +new file mode 100644 +index 0000000..29e43b0 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-gpio.c +@@ -0,0 +1,98 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * 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 "cx25821.h" ++ ++/********************* GPIO stuffs *********************/ ++void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, ++ int pin_number, int pin_logic_value) ++{ ++ int bit = pin_number; ++ u32 gpio_oe_reg = GPIO_LO_OE; ++ u32 gpio_register = 0; ++ u32 value = 0; ++ ++ /* Check for valid pinNumber */ ++ if (pin_number >= 47) ++ return; ++ ++ if (pin_number > 31) { ++ bit = pin_number - 31; ++ gpio_oe_reg = GPIO_HI_OE; ++ } ++ /* Here we will make sure that the GPIOs 0 and 1 are output. keep the ++ * rest as is */ ++ gpio_register = cx_read(gpio_oe_reg); ++ ++ if (pin_logic_value == 1) ++ value = gpio_register | Set_GPIO_Bit(bit); ++ else ++ value = gpio_register & Clear_GPIO_Bit(bit); ++ ++ cx_write(gpio_oe_reg, value); ++} ++EXPORT_SYMBOL(cx25821_set_gpiopin_direction); ++ ++static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev, ++ int pin_number, int pin_logic_value) ++{ ++ int bit = pin_number; ++ u32 gpio_reg = GPIO_LO; ++ u32 value = 0; ++ ++ /* Check for valid pinNumber */ ++ if (pin_number >= 47) ++ return; ++ ++ /* change to output direction */ ++ cx25821_set_gpiopin_direction(dev, pin_number, 0); ++ ++ if (pin_number > 31) { ++ bit = pin_number - 31; ++ gpio_reg = GPIO_HI; ++ } ++ ++ value = cx_read(gpio_reg); ++ ++ if (pin_logic_value == 0) ++ value &= Clear_GPIO_Bit(bit); ++ else ++ value |= Set_GPIO_Bit(bit); ++ ++ cx_write(gpio_reg, value); ++} ++ ++void cx25821_gpio_init(struct cx25821_dev *dev) ++{ ++ if (dev == NULL) ++ return; ++ ++ switch (dev->board) { ++ case CX25821_BOARD_CONEXANT_ATHENA10: ++ default: ++ /* set GPIO 5 to select the path for Medusa/Athena */ ++ cx25821_set_gpiopin_logicvalue(dev, 5, 1); ++ mdelay(20); ++ break; ++ } ++ ++} +diff --git a/drivers/media/pci/cx25821/cx25821-i2c.c b/drivers/media/pci/cx25821/cx25821-i2c.c +new file mode 100644 +index 0000000..5f7a79a +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-i2c.c +@@ -0,0 +1,419 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821.h" ++#include ++ ++static unsigned int i2c_debug; ++module_param(i2c_debug, int, 0644); ++MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); ++ ++static unsigned int i2c_scan; ++module_param(i2c_scan, int, 0444); ++MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); ++ ++#define dprintk(level, fmt, arg...) \ ++do { \ ++ if (i2c_debug >= level) \ ++ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \ ++} while (0) ++ ++#define I2C_WAIT_DELAY 32 ++#define I2C_WAIT_RETRY 64 ++ ++#define I2C_EXTEND (1 << 3) ++#define I2C_NOSTOP (1 << 4) ++ ++static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ return cx_read(bus->reg_stat) & 0x01; ++} ++ ++static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ return cx_read(bus->reg_stat) & 0x02 ? 1 : 0; ++} ++ ++static int i2c_wait_done(struct i2c_adapter *i2c_adap) ++{ ++ int count; ++ ++ for (count = 0; count < I2C_WAIT_RETRY; count++) { ++ if (!i2c_is_busy(i2c_adap)) ++ break; ++ udelay(I2C_WAIT_DELAY); ++ } ++ ++ if (I2C_WAIT_RETRY == count) ++ return 0; ++ ++ return 1; ++} ++ ++static int i2c_sendbytes(struct i2c_adapter *i2c_adap, ++ const struct i2c_msg *msg, int joined_rlen) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ u32 wdata, addr, ctrl; ++ int retval, cnt; ++ ++ if (joined_rlen) ++ dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, ++ msg->len, joined_rlen); ++ else ++ dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); ++ ++ /* Deal with i2c probe functions with zero payload */ ++ if (msg->len == 0) { ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2)); ++ ++ if (!i2c_wait_done(i2c_adap)) ++ return -EIO; ++ ++ if (!i2c_slave_did_ack(i2c_adap)) ++ return -EIO; ++ ++ dprintk(1, "%s(): returns 0\n", __func__); ++ return 0; ++ } ++ ++ /* dev, reg + first byte */ ++ addr = (msg->addr << 25) | msg->buf[0]; ++ wdata = msg->buf[0]; ++ ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2); ++ ++ if (msg->len > 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ else if (joined_rlen) ++ ctrl |= I2C_NOSTOP; ++ ++ cx_write(bus->reg_addr, addr); ++ cx_write(bus->reg_wdata, wdata); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ retval = i2c_wait_done(i2c_adap); ++ if (retval < 0) ++ goto err; ++ ++ if (retval == 0) ++ goto eio; ++ ++ if (i2c_debug) { ++ if (!(ctrl & I2C_NOSTOP)) ++ printk(" >\n"); ++ } ++ ++ for (cnt = 1; cnt < msg->len; cnt++) { ++ /* following bytes */ ++ wdata = msg->buf[cnt]; ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2); ++ ++ if (cnt < msg->len - 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ else if (joined_rlen) ++ ctrl |= I2C_NOSTOP; ++ ++ cx_write(bus->reg_addr, addr); ++ cx_write(bus->reg_wdata, wdata); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ retval = i2c_wait_done(i2c_adap); ++ if (retval < 0) ++ goto err; ++ ++ if (retval == 0) ++ goto eio; ++ ++ if (i2c_debug) { ++ dprintk(1, " %02x", msg->buf[cnt]); ++ if (!(ctrl & I2C_NOSTOP)) ++ dprintk(1, " >\n"); ++ } ++ } ++ ++ return msg->len; ++ ++eio: ++ retval = -EIO; ++err: ++ if (i2c_debug) ++ pr_err(" ERR: %d\n", retval); ++ return retval; ++} ++ ++static int i2c_readbytes(struct i2c_adapter *i2c_adap, ++ const struct i2c_msg *msg, int joined) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ u32 ctrl, cnt; ++ int retval; ++ ++ if (i2c_debug && !joined) ++ dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len); ++ ++ /* Deal with i2c probe functions with zero payload */ ++ if (msg->len == 0) { ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1); ++ if (!i2c_wait_done(i2c_adap)) ++ return -EIO; ++ if (!i2c_slave_did_ack(i2c_adap)) ++ return -EIO; ++ ++ dprintk(1, "%s(): returns 0\n", __func__); ++ return 0; ++ } ++ ++ if (i2c_debug) { ++ if (joined) ++ dprintk(1, " R"); ++ else ++ dprintk(1, " addr << 1) + 1); ++ } ++ ++ for (cnt = 0; cnt < msg->len; cnt++) { ++ ++ ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1; ++ ++ if (cnt < msg->len - 1) ++ ctrl |= I2C_NOSTOP | I2C_EXTEND; ++ ++ cx_write(bus->reg_addr, msg->addr << 25); ++ cx_write(bus->reg_ctrl, ctrl); ++ ++ retval = i2c_wait_done(i2c_adap); ++ if (retval < 0) ++ goto err; ++ if (retval == 0) ++ goto eio; ++ msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff; ++ ++ if (i2c_debug) { ++ dprintk(1, " %02x", msg->buf[cnt]); ++ if (!(ctrl & I2C_NOSTOP)) ++ dprintk(1, " >\n"); ++ } ++ } ++ ++ return msg->len; ++eio: ++ retval = -EIO; ++err: ++ if (i2c_debug) ++ pr_err(" ERR: %d\n", retval); ++ return retval; ++} ++ ++static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) ++{ ++ struct cx25821_i2c *bus = i2c_adap->algo_data; ++ struct cx25821_dev *dev = bus->dev; ++ int i, retval = 0; ++ ++ dprintk(1, "%s(num = %d)\n", __func__, num); ++ ++ for (i = 0; i < num; i++) { ++ dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n", ++ __func__, num, msgs[i].addr, msgs[i].len); ++ ++ if (msgs[i].flags & I2C_M_RD) { ++ /* read */ ++ retval = i2c_readbytes(i2c_adap, &msgs[i], 0); ++ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && ++ msgs[i].addr == msgs[i + 1].addr) { ++ /* write then read from same address */ ++ retval = i2c_sendbytes(i2c_adap, &msgs[i], ++ msgs[i + 1].len); ++ ++ if (retval < 0) ++ goto err; ++ i++; ++ retval = i2c_readbytes(i2c_adap, &msgs[i], 1); ++ } else { ++ /* write */ ++ retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); ++ } ++ ++ if (retval < 0) ++ goto err; ++ } ++ return num; ++ ++err: ++ return retval; ++} ++ ++ ++static u32 cx25821_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_WORD_DATA | ++ I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; ++} ++ ++static struct i2c_algorithm cx25821_i2c_algo_template = { ++ .master_xfer = i2c_xfer, ++ .functionality = cx25821_functionality, ++#ifdef NEED_ALGO_CONTROL ++ .algo_control = dummy_algo_control, ++#endif ++}; ++ ++static struct i2c_adapter cx25821_i2c_adap_template = { ++ .name = "cx25821", ++ .owner = THIS_MODULE, ++ .algo = &cx25821_i2c_algo_template, ++}; ++ ++static struct i2c_client cx25821_i2c_client_template = { ++ .name = "cx25821 internal", ++}; ++ ++/* init + register i2c adapter */ ++int cx25821_i2c_register(struct cx25821_i2c *bus) ++{ ++ struct cx25821_dev *dev = bus->dev; ++ ++ dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); ++ ++ bus->i2c_adap = cx25821_i2c_adap_template; ++ bus->i2c_client = cx25821_i2c_client_template; ++ bus->i2c_adap.dev.parent = &dev->pci->dev; ++ ++ strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); ++ ++ bus->i2c_adap.algo_data = bus; ++ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); ++ i2c_add_adapter(&bus->i2c_adap); ++ ++ bus->i2c_client.adapter = &bus->i2c_adap; ++ ++ /* set up the I2c */ ++ bus->i2c_client.addr = (0x88 >> 1); ++ ++ return bus->i2c_rc; ++} ++ ++int cx25821_i2c_unregister(struct cx25821_i2c *bus) ++{ ++ i2c_del_adapter(&bus->i2c_adap); ++ return 0; ++} ++ ++#if 0 /* Currently unused */ ++static void cx25821_av_clk(struct cx25821_dev *dev, int enable) ++{ ++ /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ ++ char buffer[3]; ++ struct i2c_msg msg; ++ dprintk(1, "%s(enabled = %d)\n", __func__, enable); ++ ++ /* Register 0x144 */ ++ buffer[0] = 0x01; ++ buffer[1] = 0x44; ++ if (enable == 1) ++ buffer[2] = 0x05; ++ else ++ buffer[2] = 0x00; ++ ++ msg.addr = 0x44; ++ msg.flags = I2C_M_TEN; ++ msg.len = 3; ++ msg.buf = buffer; ++ ++ i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1); ++} ++#endif ++ ++int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) ++{ ++ struct i2c_client *client = &bus->i2c_client; ++ int v = 0; ++ u8 addr[2] = { 0, 0 }; ++ u8 buf[4] = { 0, 0, 0, 0 }; ++ ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = addr, ++ }, { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = 4, ++ .buf = buf, ++ } ++ }; ++ ++ addr[0] = (reg_addr >> 8); ++ addr[1] = (reg_addr & 0xff); ++ msgs[0].addr = 0x44; ++ msgs[1].addr = 0x44; ++ ++ i2c_xfer(client->adapter, msgs, 2); ++ ++ v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; ++ *value = v; ++ ++ return v; ++} ++ ++int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value) ++{ ++ struct i2c_client *client = &bus->i2c_client; ++ int retval = 0; ++ u8 buf[6] = { 0, 0, 0, 0, 0, 0 }; ++ ++ struct i2c_msg msgs[1] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 6, ++ .buf = buf, ++ } ++ }; ++ ++ buf[0] = reg_addr >> 8; ++ buf[1] = reg_addr & 0xff; ++ buf[5] = (value >> 24) & 0xff; ++ buf[4] = (value >> 16) & 0xff; ++ buf[3] = (value >> 8) & 0xff; ++ buf[2] = value & 0xff; ++ client->flags = 0; ++ msgs[0].addr = 0x44; ++ ++ retval = i2c_xfer(client->adapter, msgs, 1); ++ ++ return retval; ++} +diff --git a/drivers/media/pci/cx25821/cx25821-medusa-defines.h b/drivers/media/pci/cx25821/cx25821-medusa-defines.h +new file mode 100644 +index 0000000..7a9e647 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-medusa-defines.h +@@ -0,0 +1,42 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _MEDUSA_DEF_H_ ++#define _MEDUSA_DEF_H_ ++ ++/* Video decoder that we supported */ ++#define VDEC_A 0 ++#define VDEC_B 1 ++#define VDEC_C 2 ++#define VDEC_D 3 ++#define VDEC_E 4 ++#define VDEC_F 5 ++#define VDEC_G 6 ++#define VDEC_H 7 ++ ++/* end of display sequence */ ++#define END_OF_SEQ 0xF; ++ ++/* registry string size */ ++#define MAX_REGISTRY_SZ 40; ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-medusa-reg.h b/drivers/media/pci/cx25821/cx25821-medusa-reg.h +new file mode 100644 +index 0000000..c98ac94 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-medusa-reg.h +@@ -0,0 +1,455 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __MEDUSA_REGISTERS__ ++#define __MEDUSA_REGISTERS__ ++ ++/* Serial Slave Registers */ ++#define HOST_REGISTER1 0x0000 ++#define HOST_REGISTER2 0x0001 ++ ++/* Chip Configuration Registers */ ++#define CHIP_CTRL 0x0100 ++#define AFE_AB_CTRL 0x0104 ++#define AFE_CD_CTRL 0x0108 ++#define AFE_EF_CTRL 0x010C ++#define AFE_GH_CTRL 0x0110 ++#define DENC_AB_CTRL 0x0114 ++#define BYP_AB_CTRL 0x0118 ++#define MON_A_CTRL 0x011C ++#define DISP_SEQ_A 0x0120 ++#define DISP_SEQ_B 0x0124 ++#define DISP_AB_CNT 0x0128 ++#define DISP_CD_CNT 0x012C ++#define DISP_EF_CNT 0x0130 ++#define DISP_GH_CNT 0x0134 ++#define DISP_IJ_CNT 0x0138 ++#define PIN_OE_CTRL 0x013C ++#define PIN_SPD_CTRL 0x0140 ++#define PIN_SPD_CTRL2 0x0144 ++#define IRQ_STAT_CTRL 0x0148 ++#define POWER_CTRL_AB 0x014C ++#define POWER_CTRL_CD 0x0150 ++#define POWER_CTRL_EF 0x0154 ++#define POWER_CTRL_GH 0x0158 ++#define TUNE_CTRL 0x015C ++#define BIAS_CTRL 0x0160 ++#define AFE_AB_DIAG_CTRL 0x0164 ++#define AFE_CD_DIAG_CTRL 0x0168 ++#define AFE_EF_DIAG_CTRL 0x016C ++#define AFE_GH_DIAG_CTRL 0x0170 ++#define PLL_AB_DIAG_CTRL 0x0174 ++#define PLL_CD_DIAG_CTRL 0x0178 ++#define PLL_EF_DIAG_CTRL 0x017C ++#define PLL_GH_DIAG_CTRL 0x0180 ++#define TEST_CTRL 0x0184 ++#define BIST_STAT 0x0188 ++#define BIST_STAT2 0x018C ++#define BIST_VID_PLL_AB_STAT 0x0190 ++#define BIST_VID_PLL_CD_STAT 0x0194 ++#define BIST_VID_PLL_EF_STAT 0x0198 ++#define BIST_VID_PLL_GH_STAT 0x019C ++#define DLL_DIAG_CTRL 0x01A0 ++#define DEV_CH_ID_CTRL 0x01A4 ++#define ABIST_CTRL_STATUS 0x01A8 ++#define ABIST_FREQ 0x01AC ++#define ABIST_GOERT_SHIFT 0x01B0 ++#define ABIST_COEF12 0x01B4 ++#define ABIST_COEF34 0x01B8 ++#define ABIST_COEF56 0x01BC ++#define ABIST_COEF7_SNR 0x01C0 ++#define ABIST_ADC_CAL 0x01C4 ++#define ABIST_BIN1_VGA0 0x01C8 ++#define ABIST_BIN2_VGA1 0x01CC ++#define ABIST_BIN3_VGA2 0x01D0 ++#define ABIST_BIN4_VGA3 0x01D4 ++#define ABIST_BIN5_VGA4 0x01D8 ++#define ABIST_BIN6_VGA5 0x01DC ++#define ABIST_BIN7_VGA6 0x0x1E0 ++#define ABIST_CLAMP_A 0x0x1E4 ++#define ABIST_CLAMP_B 0x0x1E8 ++#define ABIST_CLAMP_C 0x01EC ++#define ABIST_CLAMP_D 0x01F0 ++#define ABIST_CLAMP_E 0x01F4 ++#define ABIST_CLAMP_F 0x01F8 ++ ++/* Digital Video Encoder A Registers */ ++#define DENC_A_REG_1 0x0200 ++#define DENC_A_REG_2 0x0204 ++#define DENC_A_REG_3 0x0208 ++#define DENC_A_REG_4 0x020C ++#define DENC_A_REG_5 0x0210 ++#define DENC_A_REG_6 0x0214 ++#define DENC_A_REG_7 0x0218 ++#define DENC_A_REG_8 0x021C ++ ++/* Digital Video Encoder B Registers */ ++#define DENC_B_REG_1 0x0300 ++#define DENC_B_REG_2 0x0304 ++#define DENC_B_REG_3 0x0308 ++#define DENC_B_REG_4 0x030C ++#define DENC_B_REG_5 0x0310 ++#define DENC_B_REG_6 0x0314 ++#define DENC_B_REG_7 0x0318 ++#define DENC_B_REG_8 0x031C ++ ++/* Video Decoder A Registers */ ++#define MODE_CTRL 0x1000 ++#define OUT_CTRL1 0x1004 ++#define OUT_CTRL_NS 0x1008 ++#define GEN_STAT 0x100C ++#define INT_STAT_MASK 0x1010 ++#define LUMA_CTRL 0x1014 ++#define CHROMA_CTRL 0x1018 ++#define CRUSH_CTRL 0x101C ++#define HORIZ_TIM_CTRL 0x1020 ++#define VERT_TIM_CTRL 0x1024 ++#define MISC_TIM_CTRL 0x1028 ++#define FIELD_COUNT 0x102C ++#define HSCALE_CTRL 0x1030 ++#define VSCALE_CTRL 0x1034 ++#define MAN_VGA_CTRL 0x1038 ++#define MAN_AGC_CTRL 0x103C ++#define DFE_CTRL1 0x1040 ++#define DFE_CTRL2 0x1044 ++#define DFE_CTRL3 0x1048 ++#define PLL_CTRL 0x104C ++#define PLL_CTRL_FAST 0x1050 ++#define HTL_CTRL 0x1054 ++#define SRC_CFG 0x1058 ++#define SC_STEP_SIZE 0x105C ++#define SC_CONVERGE_CTRL 0x1060 ++#define SC_LOOP_CTRL 0x1064 ++#define COMB_2D_HFS_CFG 0x1068 ++#define COMB_2D_HFD_CFG 0x106C ++#define COMB_2D_LF_CFG 0x1070 ++#define COMB_2D_BLEND 0x1074 ++#define COMB_MISC_CTRL 0x1078 ++#define COMB_FLAT_THRESH_CTRL 0x107C ++#define COMB_TEST 0x1080 ++#define BP_MISC_CTRL 0x1084 ++#define VCR_DET_CTRL 0x1088 ++#define NOISE_DET_CTRL 0x108C ++#define COMB_FLAT_NOISE_CTRL 0x1090 ++#define VERSION 0x11F8 ++#define SOFT_RST_CTRL 0x11FC ++ ++/* Video Decoder B Registers */ ++#define VDEC_B_MODE_CTRL 0x1200 ++#define VDEC_B_OUT_CTRL1 0x1204 ++#define VDEC_B_OUT_CTRL_NS 0x1208 ++#define VDEC_B_GEN_STAT 0x120C ++#define VDEC_B_INT_STAT_MASK 0x1210 ++#define VDEC_B_LUMA_CTRL 0x1214 ++#define VDEC_B_CHROMA_CTRL 0x1218 ++#define VDEC_B_CRUSH_CTRL 0x121C ++#define VDEC_B_HORIZ_TIM_CTRL 0x1220 ++#define VDEC_B_VERT_TIM_CTRL 0x1224 ++#define VDEC_B_MISC_TIM_CTRL 0x1228 ++#define VDEC_B_FIELD_COUNT 0x122C ++#define VDEC_B_HSCALE_CTRL 0x1230 ++#define VDEC_B_VSCALE_CTRL 0x1234 ++#define VDEC_B_MAN_VGA_CTRL 0x1238 ++#define VDEC_B_MAN_AGC_CTRL 0x123C ++#define VDEC_B_DFE_CTRL1 0x1240 ++#define VDEC_B_DFE_CTRL2 0x1244 ++#define VDEC_B_DFE_CTRL3 0x1248 ++#define VDEC_B_PLL_CTRL 0x124C ++#define VDEC_B_PLL_CTRL_FAST 0x1250 ++#define VDEC_B_HTL_CTRL 0x1254 ++#define VDEC_B_SRC_CFG 0x1258 ++#define VDEC_B_SC_STEP_SIZE 0x125C ++#define VDEC_B_SC_CONVERGE_CTRL 0x1260 ++#define VDEC_B_SC_LOOP_CTRL 0x1264 ++#define VDEC_B_COMB_2D_HFS_CFG 0x1268 ++#define VDEC_B_COMB_2D_HFD_CFG 0x126C ++#define VDEC_B_COMB_2D_LF_CFG 0x1270 ++#define VDEC_B_COMB_2D_BLEND 0x1274 ++#define VDEC_B_COMB_MISC_CTRL 0x1278 ++#define VDEC_B_COMB_FLAT_THRESH_CTRL 0x127C ++#define VDEC_B_COMB_TEST 0x1280 ++#define VDEC_B_BP_MISC_CTRL 0x1284 ++#define VDEC_B_VCR_DET_CTRL 0x1288 ++#define VDEC_B_NOISE_DET_CTRL 0x128C ++#define VDEC_B_COMB_FLAT_NOISE_CTRL 0x1290 ++#define VDEC_B_VERSION 0x13F8 ++#define VDEC_B_SOFT_RST_CTRL 0x13FC ++ ++/* Video Decoder C Registers */ ++#define VDEC_C_MODE_CTRL 0x1400 ++#define VDEC_C_OUT_CTRL1 0x1404 ++#define VDEC_C_OUT_CTRL_NS 0x1408 ++#define VDEC_C_GEN_STAT 0x140C ++#define VDEC_C_INT_STAT_MASK 0x1410 ++#define VDEC_C_LUMA_CTRL 0x1414 ++#define VDEC_C_CHROMA_CTRL 0x1418 ++#define VDEC_C_CRUSH_CTRL 0x141C ++#define VDEC_C_HORIZ_TIM_CTRL 0x1420 ++#define VDEC_C_VERT_TIM_CTRL 0x1424 ++#define VDEC_C_MISC_TIM_CTRL 0x1428 ++#define VDEC_C_FIELD_COUNT 0x142C ++#define VDEC_C_HSCALE_CTRL 0x1430 ++#define VDEC_C_VSCALE_CTRL 0x1434 ++#define VDEC_C_MAN_VGA_CTRL 0x1438 ++#define VDEC_C_MAN_AGC_CTRL 0x143C ++#define VDEC_C_DFE_CTRL1 0x1440 ++#define VDEC_C_DFE_CTRL2 0x1444 ++#define VDEC_C_DFE_CTRL3 0x1448 ++#define VDEC_C_PLL_CTRL 0x144C ++#define VDEC_C_PLL_CTRL_FAST 0x1450 ++#define VDEC_C_HTL_CTRL 0x1454 ++#define VDEC_C_SRC_CFG 0x1458 ++#define VDEC_C_SC_STEP_SIZE 0x145C ++#define VDEC_C_SC_CONVERGE_CTRL 0x1460 ++#define VDEC_C_SC_LOOP_CTRL 0x1464 ++#define VDEC_C_COMB_2D_HFS_CFG 0x1468 ++#define VDEC_C_COMB_2D_HFD_CFG 0x146C ++#define VDEC_C_COMB_2D_LF_CFG 0x1470 ++#define VDEC_C_COMB_2D_BLEND 0x1474 ++#define VDEC_C_COMB_MISC_CTRL 0x1478 ++#define VDEC_C_COMB_FLAT_THRESH_CTRL 0x147C ++#define VDEC_C_COMB_TEST 0x1480 ++#define VDEC_C_BP_MISC_CTRL 0x1484 ++#define VDEC_C_VCR_DET_CTRL 0x1488 ++#define VDEC_C_NOISE_DET_CTRL 0x148C ++#define VDEC_C_COMB_FLAT_NOISE_CTRL 0x1490 ++#define VDEC_C_VERSION 0x15F8 ++#define VDEC_C_SOFT_RST_CTRL 0x15FC ++ ++/* Video Decoder D Registers */ ++#define VDEC_D_MODE_CTRL 0x1600 ++#define VDEC_D_OUT_CTRL1 0x1604 ++#define VDEC_D_OUT_CTRL_NS 0x1608 ++#define VDEC_D_GEN_STAT 0x160C ++#define VDEC_D_INT_STAT_MASK 0x1610 ++#define VDEC_D_LUMA_CTRL 0x1614 ++#define VDEC_D_CHROMA_CTRL 0x1618 ++#define VDEC_D_CRUSH_CTRL 0x161C ++#define VDEC_D_HORIZ_TIM_CTRL 0x1620 ++#define VDEC_D_VERT_TIM_CTRL 0x1624 ++#define VDEC_D_MISC_TIM_CTRL 0x1628 ++#define VDEC_D_FIELD_COUNT 0x162C ++#define VDEC_D_HSCALE_CTRL 0x1630 ++#define VDEC_D_VSCALE_CTRL 0x1634 ++#define VDEC_D_MAN_VGA_CTRL 0x1638 ++#define VDEC_D_MAN_AGC_CTRL 0x163C ++#define VDEC_D_DFE_CTRL1 0x1640 ++#define VDEC_D_DFE_CTRL2 0x1644 ++#define VDEC_D_DFE_CTRL3 0x1648 ++#define VDEC_D_PLL_CTRL 0x164C ++#define VDEC_D_PLL_CTRL_FAST 0x1650 ++#define VDEC_D_HTL_CTRL 0x1654 ++#define VDEC_D_SRC_CFG 0x1658 ++#define VDEC_D_SC_STEP_SIZE 0x165C ++#define VDEC_D_SC_CONVERGE_CTRL 0x1660 ++#define VDEC_D_SC_LOOP_CTRL 0x1664 ++#define VDEC_D_COMB_2D_HFS_CFG 0x1668 ++#define VDEC_D_COMB_2D_HFD_CFG 0x166C ++#define VDEC_D_COMB_2D_LF_CFG 0x1670 ++#define VDEC_D_COMB_2D_BLEND 0x1674 ++#define VDEC_D_COMB_MISC_CTRL 0x1678 ++#define VDEC_D_COMB_FLAT_THRESH_CTRL 0x167C ++#define VDEC_D_COMB_TEST 0x1680 ++#define VDEC_D_BP_MISC_CTRL 0x1684 ++#define VDEC_D_VCR_DET_CTRL 0x1688 ++#define VDEC_D_NOISE_DET_CTRL 0x168C ++#define VDEC_D_COMB_FLAT_NOISE_CTRL 0x1690 ++#define VDEC_D_VERSION 0x17F8 ++#define VDEC_D_SOFT_RST_CTRL 0x17FC ++ ++/* Video Decoder E Registers */ ++#define VDEC_E_MODE_CTRL 0x1800 ++#define VDEC_E_OUT_CTRL1 0x1804 ++#define VDEC_E_OUT_CTRL_NS 0x1808 ++#define VDEC_E_GEN_STAT 0x180C ++#define VDEC_E_INT_STAT_MASK 0x1810 ++#define VDEC_E_LUMA_CTRL 0x1814 ++#define VDEC_E_CHROMA_CTRL 0x1818 ++#define VDEC_E_CRUSH_CTRL 0x181C ++#define VDEC_E_HORIZ_TIM_CTRL 0x1820 ++#define VDEC_E_VERT_TIM_CTRL 0x1824 ++#define VDEC_E_MISC_TIM_CTRL 0x1828 ++#define VDEC_E_FIELD_COUNT 0x182C ++#define VDEC_E_HSCALE_CTRL 0x1830 ++#define VDEC_E_VSCALE_CTRL 0x1834 ++#define VDEC_E_MAN_VGA_CTRL 0x1838 ++#define VDEC_E_MAN_AGC_CTRL 0x183C ++#define VDEC_E_DFE_CTRL1 0x1840 ++#define VDEC_E_DFE_CTRL2 0x1844 ++#define VDEC_E_DFE_CTRL3 0x1848 ++#define VDEC_E_PLL_CTRL 0x184C ++#define VDEC_E_PLL_CTRL_FAST 0x1850 ++#define VDEC_E_HTL_CTRL 0x1854 ++#define VDEC_E_SRC_CFG 0x1858 ++#define VDEC_E_SC_STEP_SIZE 0x185C ++#define VDEC_E_SC_CONVERGE_CTRL 0x1860 ++#define VDEC_E_SC_LOOP_CTRL 0x1864 ++#define VDEC_E_COMB_2D_HFS_CFG 0x1868 ++#define VDEC_E_COMB_2D_HFD_CFG 0x186C ++#define VDEC_E_COMB_2D_LF_CFG 0x1870 ++#define VDEC_E_COMB_2D_BLEND 0x1874 ++#define VDEC_E_COMB_MISC_CTRL 0x1878 ++#define VDEC_E_COMB_FLAT_THRESH_CTRL 0x187C ++#define VDEC_E_COMB_TEST 0x1880 ++#define VDEC_E_BP_MISC_CTRL 0x1884 ++#define VDEC_E_VCR_DET_CTRL 0x1888 ++#define VDEC_E_NOISE_DET_CTRL 0x188C ++#define VDEC_E_COMB_FLAT_NOISE_CTRL 0x1890 ++#define VDEC_E_VERSION 0x19F8 ++#define VDEC_E_SOFT_RST_CTRL 0x19FC ++ ++/* Video Decoder F Registers */ ++#define VDEC_F_MODE_CTRL 0x1A00 ++#define VDEC_F_OUT_CTRL1 0x1A04 ++#define VDEC_F_OUT_CTRL_NS 0x1A08 ++#define VDEC_F_GEN_STAT 0x1A0C ++#define VDEC_F_INT_STAT_MASK 0x1A10 ++#define VDEC_F_LUMA_CTRL 0x1A14 ++#define VDEC_F_CHROMA_CTRL 0x1A18 ++#define VDEC_F_CRUSH_CTRL 0x1A1C ++#define VDEC_F_HORIZ_TIM_CTRL 0x1A20 ++#define VDEC_F_VERT_TIM_CTRL 0x1A24 ++#define VDEC_F_MISC_TIM_CTRL 0x1A28 ++#define VDEC_F_FIELD_COUNT 0x1A2C ++#define VDEC_F_HSCALE_CTRL 0x1A30 ++#define VDEC_F_VSCALE_CTRL 0x1A34 ++#define VDEC_F_MAN_VGA_CTRL 0x1A38 ++#define VDEC_F_MAN_AGC_CTRL 0x1A3C ++#define VDEC_F_DFE_CTRL1 0x1A40 ++#define VDEC_F_DFE_CTRL2 0x1A44 ++#define VDEC_F_DFE_CTRL3 0x1A48 ++#define VDEC_F_PLL_CTRL 0x1A4C ++#define VDEC_F_PLL_CTRL_FAST 0x1A50 ++#define VDEC_F_HTL_CTRL 0x1A54 ++#define VDEC_F_SRC_CFG 0x1A58 ++#define VDEC_F_SC_STEP_SIZE 0x1A5C ++#define VDEC_F_SC_CONVERGE_CTRL 0x1A60 ++#define VDEC_F_SC_LOOP_CTRL 0x1A64 ++#define VDEC_F_COMB_2D_HFS_CFG 0x1A68 ++#define VDEC_F_COMB_2D_HFD_CFG 0x1A6C ++#define VDEC_F_COMB_2D_LF_CFG 0x1A70 ++#define VDEC_F_COMB_2D_BLEND 0x1A74 ++#define VDEC_F_COMB_MISC_CTRL 0x1A78 ++#define VDEC_F_COMB_FLAT_THRESH_CTRL 0x1A7C ++#define VDEC_F_COMB_TEST 0x1A80 ++#define VDEC_F_BP_MISC_CTRL 0x1A84 ++#define VDEC_F_VCR_DET_CTRL 0x1A88 ++#define VDEC_F_NOISE_DET_CTRL 0x1A8C ++#define VDEC_F_COMB_FLAT_NOISE_CTRL 0x1A90 ++#define VDEC_F_VERSION 0x1BF8 ++#define VDEC_F_SOFT_RST_CTRL 0x1BFC ++ ++/* Video Decoder G Registers */ ++#define VDEC_G_MODE_CTRL 0x1C00 ++#define VDEC_G_OUT_CTRL1 0x1C04 ++#define VDEC_G_OUT_CTRL_NS 0x1C08 ++#define VDEC_G_GEN_STAT 0x1C0C ++#define VDEC_G_INT_STAT_MASK 0x1C10 ++#define VDEC_G_LUMA_CTRL 0x1C14 ++#define VDEC_G_CHROMA_CTRL 0x1C18 ++#define VDEC_G_CRUSH_CTRL 0x1C1C ++#define VDEC_G_HORIZ_TIM_CTRL 0x1C20 ++#define VDEC_G_VERT_TIM_CTRL 0x1C24 ++#define VDEC_G_MISC_TIM_CTRL 0x1C28 ++#define VDEC_G_FIELD_COUNT 0x1C2C ++#define VDEC_G_HSCALE_CTRL 0x1C30 ++#define VDEC_G_VSCALE_CTRL 0x1C34 ++#define VDEC_G_MAN_VGA_CTRL 0x1C38 ++#define VDEC_G_MAN_AGC_CTRL 0x1C3C ++#define VDEC_G_DFE_CTRL1 0x1C40 ++#define VDEC_G_DFE_CTRL2 0x1C44 ++#define VDEC_G_DFE_CTRL3 0x1C48 ++#define VDEC_G_PLL_CTRL 0x1C4C ++#define VDEC_G_PLL_CTRL_FAST 0x1C50 ++#define VDEC_G_HTL_CTRL 0x1C54 ++#define VDEC_G_SRC_CFG 0x1C58 ++#define VDEC_G_SC_STEP_SIZE 0x1C5C ++#define VDEC_G_SC_CONVERGE_CTRL 0x1C60 ++#define VDEC_G_SC_LOOP_CTRL 0x1C64 ++#define VDEC_G_COMB_2D_HFS_CFG 0x1C68 ++#define VDEC_G_COMB_2D_HFD_CFG 0x1C6C ++#define VDEC_G_COMB_2D_LF_CFG 0x1C70 ++#define VDEC_G_COMB_2D_BLEND 0x1C74 ++#define VDEC_G_COMB_MISC_CTRL 0x1C78 ++#define VDEC_G_COMB_FLAT_THRESH_CTRL 0x1C7C ++#define VDEC_G_COMB_TEST 0x1C80 ++#define VDEC_G_BP_MISC_CTRL 0x1C84 ++#define VDEC_G_VCR_DET_CTRL 0x1C88 ++#define VDEC_G_NOISE_DET_CTRL 0x1C8C ++#define VDEC_G_COMB_FLAT_NOISE_CTRL 0x1C90 ++#define VDEC_G_VERSION 0x1DF8 ++#define VDEC_G_SOFT_RST_CTRL 0x1DFC ++ ++/* Video Decoder H Registers */ ++#define VDEC_H_MODE_CTRL 0x1E00 ++#define VDEC_H_OUT_CTRL1 0x1E04 ++#define VDEC_H_OUT_CTRL_NS 0x1E08 ++#define VDEC_H_GEN_STAT 0x1E0C ++#define VDEC_H_INT_STAT_MASK 0x1E1E ++#define VDEC_H_LUMA_CTRL 0x1E14 ++#define VDEC_H_CHROMA_CTRL 0x1E18 ++#define VDEC_H_CRUSH_CTRL 0x1E1C ++#define VDEC_H_HORIZ_TIM_CTRL 0x1E20 ++#define VDEC_H_VERT_TIM_CTRL 0x1E24 ++#define VDEC_H_MISC_TIM_CTRL 0x1E28 ++#define VDEC_H_FIELD_COUNT 0x1E2C ++#define VDEC_H_HSCALE_CTRL 0x1E30 ++#define VDEC_H_VSCALE_CTRL 0x1E34 ++#define VDEC_H_MAN_VGA_CTRL 0x1E38 ++#define VDEC_H_MAN_AGC_CTRL 0x1E3C ++#define VDEC_H_DFE_CTRL1 0x1E40 ++#define VDEC_H_DFE_CTRL2 0x1E44 ++#define VDEC_H_DFE_CTRL3 0x1E48 ++#define VDEC_H_PLL_CTRL 0x1E4C ++#define VDEC_H_PLL_CTRL_FAST 0x1E50 ++#define VDEC_H_HTL_CTRL 0x1E54 ++#define VDEC_H_SRC_CFG 0x1E58 ++#define VDEC_H_SC_STEP_SIZE 0x1E5C ++#define VDEC_H_SC_CONVERGE_CTRL 0x1E60 ++#define VDEC_H_SC_LOOP_CTRL 0x1E64 ++#define VDEC_H_COMB_2D_HFS_CFG 0x1E68 ++#define VDEC_H_COMB_2D_HFD_CFG 0x1E6C ++#define VDEC_H_COMB_2D_LF_CFG 0x1E70 ++#define VDEC_H_COMB_2D_BLEND 0x1E74 ++#define VDEC_H_COMB_MISC_CTRL 0x1E78 ++#define VDEC_H_COMB_FLAT_THRESH_CTRL 0x1E7C ++#define VDEC_H_COMB_TEST 0x1E80 ++#define VDEC_H_BP_MISC_CTRL 0x1E84 ++#define VDEC_H_VCR_DET_CTRL 0x1E88 ++#define VDEC_H_NOISE_DET_CTRL 0x1E8C ++#define VDEC_H_COMB_FLAT_NOISE_CTRL 0x1E90 ++#define VDEC_H_VERSION 0x1FF8 ++#define VDEC_H_SOFT_RST_CTRL 0x1FFC ++ ++/*****************************************************************************/ ++/* LUMA_CTRL register fields */ ++#define VDEC_A_BRITE_CTRL 0x1014 ++#define VDEC_A_CNTRST_CTRL 0x1015 ++#define VDEC_A_PEAK_SEL 0x1016 ++ ++/*****************************************************************************/ ++/* CHROMA_CTRL register fields */ ++#define VDEC_A_USAT_CTRL 0x1018 ++#define VDEC_A_VSAT_CTRL 0x1019 ++#define VDEC_A_HUE_CTRL 0x101A ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-medusa-video.c b/drivers/media/pci/cx25821/cx25821-medusa-video.c +new file mode 100644 +index 0000000..c588b96 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-medusa-video.c +@@ -0,0 +1,788 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * 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. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821.h" ++#include "cx25821-medusa-video.h" ++#include "cx25821-biffuncs.h" ++ ++/* ++ * medusa_enable_bluefield_output() ++ * ++ * Enable the generation of blue filed output if no video ++ * ++ */ ++static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, ++ int enable) ++{ ++ u32 value = 0; ++ u32 tmp = 0; ++ int out_ctrl = OUT_CTRL1; ++ int out_ctrl_ns = OUT_CTRL_NS; ++ ++ switch (channel) { ++ default: ++ case VDEC_A: ++ break; ++ case VDEC_B: ++ out_ctrl = VDEC_B_OUT_CTRL1; ++ out_ctrl_ns = VDEC_B_OUT_CTRL_NS; ++ break; ++ case VDEC_C: ++ out_ctrl = VDEC_C_OUT_CTRL1; ++ out_ctrl_ns = VDEC_C_OUT_CTRL_NS; ++ break; ++ case VDEC_D: ++ out_ctrl = VDEC_D_OUT_CTRL1; ++ out_ctrl_ns = VDEC_D_OUT_CTRL_NS; ++ break; ++ case VDEC_E: ++ out_ctrl = VDEC_E_OUT_CTRL1; ++ out_ctrl_ns = VDEC_E_OUT_CTRL_NS; ++ return; ++ case VDEC_F: ++ out_ctrl = VDEC_F_OUT_CTRL1; ++ out_ctrl_ns = VDEC_F_OUT_CTRL_NS; ++ return; ++ case VDEC_G: ++ out_ctrl = VDEC_G_OUT_CTRL1; ++ out_ctrl_ns = VDEC_G_OUT_CTRL_NS; ++ return; ++ case VDEC_H: ++ out_ctrl = VDEC_H_OUT_CTRL1; ++ out_ctrl_ns = VDEC_H_OUT_CTRL_NS; ++ return; ++ } ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp); ++ value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ ++ if (enable) ++ value |= 0x00000080; /* set BLUE_FIELD_EN */ ++ cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); ++ value &= 0xFFFFFF7F; ++ if (enable) ++ value |= 0x00000080; /* set BLUE_FIELD_EN */ ++ cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); ++} ++ ++static int medusa_initialize_ntsc(struct cx25821_dev *dev) ++{ ++ int ret_val = 0; ++ int i = 0; ++ u32 value = 0; ++ u32 tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ for (i = 0; i < MAX_DECODERS; i++) { ++ /* set video format NTSC-M */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ MODE_CTRL + (0x200 * i), &tmp); ++ value &= 0xFFFFFFF0; ++ /* enable the fast locking mode bit[16] */ ++ value |= 0x10001; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ MODE_CTRL + (0x200 * i), value); ++ ++ /* resolution NTSC 720x480 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ HORIZ_TIM_CTRL + (0x200 * i), &tmp); ++ value &= 0x00C00C00; ++ value |= 0x612D0074; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ HORIZ_TIM_CTRL + (0x200 * i), value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ VERT_TIM_CTRL + (0x200 * i), &tmp); ++ value &= 0x00C00C00; ++ value |= 0x1C1E001A; /* vblank_cnt + 2 to get camera ID */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ VERT_TIM_CTRL + (0x200 * i), value); ++ ++ /* chroma subcarrier step size */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ SC_STEP_SIZE + (0x200 * i), 0x43E00000); ++ ++ /* enable VIP optional active */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ OUT_CTRL_NS + (0x200 * i), &tmp); ++ value &= 0xFFFBFFFF; ++ value |= 0x00040000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ OUT_CTRL_NS + (0x200 * i), value); ++ ++ /* enable VIP optional active (VIP_OPT_AL) for direct output. */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ OUT_CTRL1 + (0x200 * i), &tmp); ++ value &= 0xFFFBFFFF; ++ value |= 0x00040000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ OUT_CTRL1 + (0x200 * i), value); ++ ++ /* ++ * clear VPRES_VERT_EN bit, fixes the chroma run away problem ++ * when the input switching rate < 16 fields ++ */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ MISC_TIM_CTRL + (0x200 * i), &tmp); ++ /* disable special play detection */ ++ value = setBitAtPos(value, 14); ++ value = clearBitAtPos(value, 15); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ MISC_TIM_CTRL + (0x200 * i), value); ++ ++ /* set vbi_gate_en to 0 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DFE_CTRL1 + (0x200 * i), &tmp); ++ value = clearBitAtPos(value, 29); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DFE_CTRL1 + (0x200 * i), value); ++ ++ /* Enable the generation of blue field output if no video */ ++ medusa_enable_bluefield_output(dev, i, 1); ++ } ++ ++ for (i = 0; i < MAX_ENCODERS; i++) { ++ /* NTSC hclock */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_1 + (0x100 * i), &tmp); ++ value &= 0xF000FC00; ++ value |= 0x06B402D0; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_1 + (0x100 * i), value); ++ ++ /* burst begin and burst end */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_2 + (0x100 * i), &tmp); ++ value &= 0xFF000000; ++ value |= 0x007E9054; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_2 + (0x100 * i), value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_3 + (0x100 * i), &tmp); ++ value &= 0xFC00FE00; ++ value |= 0x00EC00F0; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_3 + (0x100 * i), value); ++ ++ /* set NTSC vblank, no phase alternation, 7.5 IRE pedestal */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_4 + (0x100 * i), &tmp); ++ value &= 0x00FCFFFF; ++ value |= 0x13020000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_4 + (0x100 * i), value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_5 + (0x100 * i), &tmp); ++ value &= 0xFFFF0000; ++ value |= 0x0000E575; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_5 + (0x100 * i), value); ++ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_6 + (0x100 * i), 0x009A89C1); ++ ++ /* Subcarrier Increment */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_7 + (0x100 * i), 0x21F07C1F); ++ } ++ ++ /* set picture resolutions */ ++ /* 0 - 720 */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); ++ /* 0 - 480 */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); ++ ++ /* set Bypass input format to NTSC 525 lines */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); ++ value |= 0x00080200; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); ++ ++ mutex_unlock(&dev->lock); ++ ++ return ret_val; ++} ++ ++static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) ++{ ++ int ret_val = -1; ++ u32 value = 0, tmp = 0; ++ ++ /* Setup for 2D threshold */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_2D_HFS_CFG + (0x200 * dec), 0x20002861); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_2D_HFD_CFG + (0x200 * dec), 0x20002861); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_2D_LF_CFG + (0x200 * dec), 0x200A1023); ++ ++ /* Setup flat chroma and luma thresholds */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp); ++ value &= 0x06230000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_FLAT_THRESH_CTRL + (0x200 * dec), value); ++ ++ /* set comb 2D blend */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_2D_BLEND + (0x200 * dec), 0x210F0F0F); ++ ++ /* COMB MISC CONTROL */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ COMB_MISC_CTRL + (0x200 * dec), 0x41120A7F); ++ ++ return ret_val; ++} ++ ++static int medusa_initialize_pal(struct cx25821_dev *dev) ++{ ++ int ret_val = 0; ++ int i = 0; ++ u32 value = 0; ++ u32 tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ for (i = 0; i < MAX_DECODERS; i++) { ++ /* set video format PAL-BDGHI */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ MODE_CTRL + (0x200 * i), &tmp); ++ value &= 0xFFFFFFF0; ++ /* enable the fast locking mode bit[16] */ ++ value |= 0x10004; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ MODE_CTRL + (0x200 * i), value); ++ ++ /* resolution PAL 720x576 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ HORIZ_TIM_CTRL + (0x200 * i), &tmp); ++ value &= 0x00C00C00; ++ value |= 0x632D007D; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ HORIZ_TIM_CTRL + (0x200 * i), value); ++ ++ /* vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ VERT_TIM_CTRL + (0x200 * i), &tmp); ++ value &= 0x00C00C00; ++ value |= 0x28240026; /* vblank_cnt + 2 to get camera ID */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ VERT_TIM_CTRL + (0x200 * i), value); ++ ++ /* chroma subcarrier step size */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ SC_STEP_SIZE + (0x200 * i), 0x5411E2D0); ++ ++ /* enable VIP optional active */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ OUT_CTRL_NS + (0x200 * i), &tmp); ++ value &= 0xFFFBFFFF; ++ value |= 0x00040000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ OUT_CTRL_NS + (0x200 * i), value); ++ ++ /* enable VIP optional active (VIP_OPT_AL) for direct output. */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ OUT_CTRL1 + (0x200 * i), &tmp); ++ value &= 0xFFFBFFFF; ++ value |= 0x00040000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ OUT_CTRL1 + (0x200 * i), value); ++ ++ /* ++ * clear VPRES_VERT_EN bit, fixes the chroma run away problem ++ * when the input switching rate < 16 fields ++ */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ MISC_TIM_CTRL + (0x200 * i), &tmp); ++ /* disable special play detection */ ++ value = setBitAtPos(value, 14); ++ value = clearBitAtPos(value, 15); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ MISC_TIM_CTRL + (0x200 * i), value); ++ ++ /* set vbi_gate_en to 0 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DFE_CTRL1 + (0x200 * i), &tmp); ++ value = clearBitAtPos(value, 29); ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DFE_CTRL1 + (0x200 * i), value); ++ ++ medusa_PALCombInit(dev, i); ++ ++ /* Enable the generation of blue field output if no video */ ++ medusa_enable_bluefield_output(dev, i, 1); ++ } ++ ++ for (i = 0; i < MAX_ENCODERS; i++) { ++ /* PAL hclock */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_1 + (0x100 * i), &tmp); ++ value &= 0xF000FC00; ++ value |= 0x06C002D0; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_1 + (0x100 * i), value); ++ ++ /* burst begin and burst end */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_2 + (0x100 * i), &tmp); ++ value &= 0xFF000000; ++ value |= 0x007E9754; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_2 + (0x100 * i), value); ++ ++ /* hblank and vactive */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_3 + (0x100 * i), &tmp); ++ value &= 0xFC00FE00; ++ value |= 0x00FC0120; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_3 + (0x100 * i), value); ++ ++ /* set PAL vblank, phase alternation, 0 IRE pedestal */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_4 + (0x100 * i), &tmp); ++ value &= 0x00FCFFFF; ++ value |= 0x14010000; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_4 + (0x100 * i), value); ++ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], ++ DENC_A_REG_5 + (0x100 * i), &tmp); ++ value &= 0xFFFF0000; ++ value |= 0x0000F078; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_5 + (0x100 * i), value); ++ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_6 + (0x100 * i), 0x00A493CF); ++ ++ /* Subcarrier Increment */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], ++ DENC_A_REG_7 + (0x100 * i), 0x2A098ACB); ++ } ++ ++ /* set picture resolutions */ ++ /* 0 - 720 */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); ++ /* 0 - 576 */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); ++ ++ /* set Bypass input format to PAL 625 lines */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); ++ value &= 0xFFF7FDFF; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); ++ ++ mutex_unlock(&dev->lock); ++ ++ return ret_val; ++} ++ ++int medusa_set_videostandard(struct cx25821_dev *dev) ++{ ++ int status = STATUS_SUCCESS; ++ u32 value = 0, tmp = 0; ++ ++ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) ++ status = medusa_initialize_pal(dev); ++ else ++ status = medusa_initialize_ntsc(dev); ++ ++ /* Enable DENC_A output */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp); ++ value = setBitAtPos(value, 4); ++ status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value); ++ ++ /* Enable DENC_B output */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp); ++ value = setBitAtPos(value, 4); ++ status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value); ++ ++ return status; ++} ++ ++void medusa_set_resolution(struct cx25821_dev *dev, int width, ++ int decoder_select) ++{ ++ int decoder = 0; ++ int decoder_count = 0; ++ u32 hscale = 0x0; ++ u32 vscale = 0x0; ++ const int MAX_WIDTH = 720; ++ ++ mutex_lock(&dev->lock); ++ ++ /* validate the width */ ++ if (width > MAX_WIDTH) { ++ pr_info("%s(): width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH\n", ++ __func__, width, MAX_WIDTH); ++ width = MAX_WIDTH; ++ } ++ ++ if (decoder_select <= 7 && decoder_select >= 0) { ++ decoder = decoder_select; ++ decoder_count = decoder_select + 1; ++ } else { ++ decoder = 0; ++ decoder_count = _num_decoders; ++ } ++ ++ switch (width) { ++ case 320: ++ hscale = 0x13E34B; ++ vscale = 0x0; ++ break; ++ ++ case 352: ++ hscale = 0x10A273; ++ vscale = 0x0; ++ break; ++ ++ case 176: ++ hscale = 0x3115B2; ++ vscale = 0x1E00; ++ break; ++ ++ case 160: ++ hscale = 0x378D84; ++ vscale = 0x1E00; ++ break; ++ ++ default: /* 720 */ ++ hscale = 0x0; ++ vscale = 0x0; ++ break; ++ } ++ ++ for (; decoder < decoder_count; decoder++) { ++ /* write scaling values for each decoder */ ++ cx25821_i2c_write(&dev->i2c_bus[0], ++ HSCALE_CTRL + (0x200 * decoder), hscale); ++ cx25821_i2c_write(&dev->i2c_bus[0], ++ VSCALE_CTRL + (0x200 * decoder), vscale); ++ } ++ ++ mutex_unlock(&dev->lock); ++} ++ ++static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, ++ int duration) ++{ ++ u32 fld_cnt = 0; ++ u32 tmp = 0; ++ u32 disp_cnt_reg = DISP_AB_CNT; ++ ++ mutex_lock(&dev->lock); ++ ++ /* no support */ ++ if (decoder < VDEC_A || decoder > VDEC_H) { ++ mutex_unlock(&dev->lock); ++ return; ++ } ++ ++ switch (decoder) { ++ default: ++ break; ++ case VDEC_C: ++ case VDEC_D: ++ disp_cnt_reg = DISP_CD_CNT; ++ break; ++ case VDEC_E: ++ case VDEC_F: ++ disp_cnt_reg = DISP_EF_CNT; ++ break; ++ case VDEC_G: ++ case VDEC_H: ++ disp_cnt_reg = DISP_GH_CNT; ++ break; ++ } ++ ++ _display_field_cnt[decoder] = duration; ++ ++ /* update hardware */ ++ fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp); ++ ++ if (!(decoder % 2)) { /* EVEN decoder */ ++ fld_cnt &= 0xFFFF0000; ++ fld_cnt |= duration; ++ } else { ++ fld_cnt &= 0x0000FFFF; ++ fld_cnt |= ((u32) duration) << 16; ++ } ++ ++ cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); ++ ++ mutex_unlock(&dev->lock); ++} ++ ++/* Map to Medusa register setting */ ++static int mapM(int srcMin, int srcMax, int srcVal, int dstMin, int dstMax, ++ int *dstVal) ++{ ++ int numerator; ++ int denominator; ++ int quotient; ++ ++ if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax)) ++ return -1; ++ /* ++ * This is the overall expression used: ++ * *dstVal = ++ * (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin; ++ * but we need to account for rounding so below we use the modulus ++ * operator to find the remainder and increment if necessary. ++ */ ++ numerator = (srcVal - srcMin) * (dstMax - dstMin); ++ denominator = srcMax - srcMin; ++ quotient = numerator / denominator; ++ ++ if (2 * (numerator % denominator) >= denominator) ++ quotient++; ++ ++ *dstVal = quotient + dstMin; ++ ++ return 0; ++} ++ ++static unsigned long convert_to_twos(long numeric, unsigned long bits_len) ++{ ++ unsigned char temp; ++ ++ if (numeric >= 0) ++ return numeric; ++ else { ++ temp = ~(abs(numeric) & 0xFF); ++ temp += 1; ++ return temp; ++ } ++} ++ ++int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) ++{ ++ int ret_val = 0; ++ int value = 0; ++ u32 val = 0, tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ if ((brightness > VIDEO_PROCAMP_MAX) || ++ (brightness < VIDEO_PROCAMP_MIN)) { ++ mutex_unlock(&dev->lock); ++ return -1; ++ } ++ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness, ++ SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value); ++ value = convert_to_twos(value, 8); ++ val = cx25821_i2c_read(&dev->i2c_bus[0], ++ VDEC_A_BRITE_CTRL + (0x200 * decoder), &tmp); ++ val &= 0xFFFFFF00; ++ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], ++ VDEC_A_BRITE_CTRL + (0x200 * decoder), val | value); ++ mutex_unlock(&dev->lock); ++ return ret_val; ++} ++ ++int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) ++{ ++ int ret_val = 0; ++ int value = 0; ++ u32 val = 0, tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) { ++ mutex_unlock(&dev->lock); ++ return -1; ++ } ++ ++ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast, ++ UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); ++ val = cx25821_i2c_read(&dev->i2c_bus[0], ++ VDEC_A_CNTRST_CTRL + (0x200 * decoder), &tmp); ++ val &= 0xFFFFFF00; ++ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], ++ VDEC_A_CNTRST_CTRL + (0x200 * decoder), val | value); ++ ++ mutex_unlock(&dev->lock); ++ return ret_val; ++} ++ ++int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) ++{ ++ int ret_val = 0; ++ int value = 0; ++ u32 val = 0, tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) { ++ mutex_unlock(&dev->lock); ++ return -1; ++ } ++ ++ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue, ++ SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value); ++ ++ value = convert_to_twos(value, 8); ++ val = cx25821_i2c_read(&dev->i2c_bus[0], ++ VDEC_A_HUE_CTRL + (0x200 * decoder), &tmp); ++ val &= 0xFFFFFF00; ++ ++ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], ++ VDEC_A_HUE_CTRL + (0x200 * decoder), val | value); ++ ++ mutex_unlock(&dev->lock); ++ return ret_val; ++} ++ ++int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) ++{ ++ int ret_val = 0; ++ int value = 0; ++ u32 val = 0, tmp = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ if ((saturation > VIDEO_PROCAMP_MAX) || ++ (saturation < VIDEO_PROCAMP_MIN)) { ++ mutex_unlock(&dev->lock); ++ return -1; ++ } ++ ++ ret_val = mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation, ++ UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); ++ ++ val = cx25821_i2c_read(&dev->i2c_bus[0], ++ VDEC_A_USAT_CTRL + (0x200 * decoder), &tmp); ++ val &= 0xFFFFFF00; ++ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], ++ VDEC_A_USAT_CTRL + (0x200 * decoder), val | value); ++ ++ val = cx25821_i2c_read(&dev->i2c_bus[0], ++ VDEC_A_VSAT_CTRL + (0x200 * decoder), &tmp); ++ val &= 0xFFFFFF00; ++ ret_val |= cx25821_i2c_write(&dev->i2c_bus[0], ++ VDEC_A_VSAT_CTRL + (0x200 * decoder), val | value); ++ ++ mutex_unlock(&dev->lock); ++ return ret_val; ++} ++ ++/* Program the display sequence and monitor output. */ ++ ++int medusa_video_init(struct cx25821_dev *dev) ++{ ++ u32 value = 0, tmp = 0; ++ int ret_val = 0; ++ int i = 0; ++ ++ mutex_lock(&dev->lock); ++ ++ _num_decoders = dev->_max_num_decoders; ++ ++ /* disable Auto source selection on all video decoders */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); ++ value &= 0xFFFFF0FF; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ /* Turn off Master source switch enable */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); ++ value &= 0xFFFFFFDF; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ mutex_unlock(&dev->lock); ++ ++ for (i = 0; i < _num_decoders; i++) ++ medusa_set_decoderduration(dev, i, _display_field_cnt[i]); ++ ++ mutex_lock(&dev->lock); ++ ++ /* Select monitor as DENC A input, power up the DAC */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp); ++ value &= 0xFF70FF70; ++ value |= 0x00090008; /* set en_active */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ /* enable input is VIP/656 */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); ++ value |= 0x00040100; /* enable VIP */ ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ /* select AFE clock to output mode */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); ++ value &= 0x83FFFFFF; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, ++ value | 0x10000000); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ /* Turn on all of the data out and control output pins. */ ++ value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp); ++ value &= 0xFEF0FE00; ++ if (_num_decoders == MAX_DECODERS) { ++ /* ++ * Note: The octal board does not support control pins(bit16-19) ++ * These bits are ignored in the octal board. ++ * ++ * disable VDEC A-C port, default to Mobilygen Interface ++ */ ++ value |= 0x010001F8; ++ } else { ++ /* disable VDEC A-C port, default to Mobilygen Interface */ ++ value |= 0x010F0108; ++ } ++ ++ value |= 7; ++ ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value); ++ ++ if (ret_val < 0) ++ goto error; ++ ++ ++ mutex_unlock(&dev->lock); ++ ++ ret_val = medusa_set_videostandard(dev); ++ ++ return ret_val; ++ ++error: ++ mutex_unlock(&dev->lock); ++ return ret_val; ++} +diff --git a/drivers/media/pci/cx25821/cx25821-medusa-video.h b/drivers/media/pci/cx25821/cx25821-medusa-video.h +new file mode 100644 +index 0000000..6175e09 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-medusa-video.h +@@ -0,0 +1,49 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _MEDUSA_VIDEO_H ++#define _MEDUSA_VIDEO_H ++ ++#include "cx25821-medusa-defines.h" ++ ++/* Color control constants */ ++#define VIDEO_PROCAMP_MIN 0 ++#define VIDEO_PROCAMP_MAX 10000 ++#define UNSIGNED_BYTE_MIN 0 ++#define UNSIGNED_BYTE_MAX 0xFF ++#define SIGNED_BYTE_MIN -128 ++#define SIGNED_BYTE_MAX 127 ++ ++/* Default video color settings */ ++#define SHARPNESS_DEFAULT 50 ++#define SATURATION_DEFAULT 5000 ++#define BRIGHTNESS_DEFAULT 6200 ++#define CONTRAST_DEFAULT 5000 ++#define HUE_DEFAULT 5000 ++ ++unsigned short _num_decoders; ++unsigned short _num_cameras; ++ ++unsigned int _video_standard; ++int _display_field_cnt[MAX_DECODERS]; ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-reg.h b/drivers/media/pci/cx25821/cx25821-reg.h +new file mode 100644 +index 0000000..a3fc25a +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-reg.h +@@ -0,0 +1,1592 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __CX25821_REGISTERS__ ++#define __CX25821_REGISTERS__ ++ ++/* Risc Instructions */ ++#define RISC_CNT_INC 0x00010000 ++#define RISC_CNT_RESET 0x00030000 ++#define RISC_IRQ1 0x01000000 ++#define RISC_IRQ2 0x02000000 ++#define RISC_EOL 0x04000000 ++#define RISC_SOL 0x08000000 ++#define RISC_WRITE 0x10000000 ++#define RISC_SKIP 0x20000000 ++#define RISC_JUMP 0x70000000 ++#define RISC_SYNC 0x80000000 ++#define RISC_RESYNC 0x80008000 ++#define RISC_READ 0x90000000 ++#define RISC_WRITERM 0xB0000000 ++#define RISC_WRITECM 0xC0000000 ++#define RISC_WRITECR 0xD0000000 ++#define RISC_WRITEC 0x50000000 ++#define RISC_READC 0xA0000000 ++ ++#define RISC_SYNC_ODD 0x00000000 ++#define RISC_SYNC_EVEN 0x00000200 ++#define RISC_SYNC_ODD_VBI 0x00000006 ++#define RISC_SYNC_EVEN_VBI 0x00000207 ++#define RISC_NOOP 0xF0000000 ++ ++/***************************************************************************** ++* ASB SRAM ++ *****************************************************************************/ ++#define TX_SRAM 0x000000 /* Transmit SRAM */ ++ ++/*****************************************************************************/ ++#define RX_RAM 0x010000 /* Receive SRAM */ ++ ++/***************************************************************************** ++* Application Layer (AL) ++ *****************************************************************************/ ++#define DEV_CNTRL2 0x040000 /* Device control */ ++#define FLD_RUN_RISC 0x00000020 ++ ++/* ***************************************************************************** */ ++#define PCI_INT_MSK 0x040010 /* PCI interrupt mask */ ++#define PCI_INT_STAT 0x040014 /* PCI interrupt status */ ++#define PCI_INT_MSTAT 0x040018 /* PCI interrupt masked status */ ++#define FLD_HAMMERHEAD_INT (1 << 27) ++#define FLD_UART_INT (1 << 26) ++#define FLD_IRQN_INT (1 << 25) ++#define FLD_TM_INT (1 << 28) ++#define FLD_I2C_3_RACK (1 << 27) ++#define FLD_I2C_3_INT (1 << 26) ++#define FLD_I2C_2_RACK (1 << 25) ++#define FLD_I2C_2_INT (1 << 24) ++#define FLD_I2C_1_RACK (1 << 23) ++#define FLD_I2C_1_INT (1 << 22) ++ ++#define FLD_APB_DMA_BERR_INT (1 << 21) ++#define FLD_AL_WR_BERR_INT (1 << 20) ++#define FLD_AL_RD_BERR_INT (1 << 19) ++#define FLD_RISC_WR_BERR_INT (1 << 18) ++#define FLD_RISC_RD_BERR_INT (1 << 17) ++ ++#define FLD_VID_I_INT (1 << 8) ++#define FLD_VID_H_INT (1 << 7) ++#define FLD_VID_G_INT (1 << 6) ++#define FLD_VID_F_INT (1 << 5) ++#define FLD_VID_E_INT (1 << 4) ++#define FLD_VID_D_INT (1 << 3) ++#define FLD_VID_C_INT (1 << 2) ++#define FLD_VID_B_INT (1 << 1) ++#define FLD_VID_A_INT (1 << 0) ++ ++/* ***************************************************************************** */ ++#define VID_A_INT_MSK 0x040020 /* Video A interrupt mask */ ++#define VID_A_INT_STAT 0x040024 /* Video A interrupt status */ ++#define VID_A_INT_MSTAT 0x040028 /* Video A interrupt masked status */ ++#define VID_A_INT_SSTAT 0x04002C /* Video A interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_B_INT_MSK 0x040030 /* Video B interrupt mask */ ++#define VID_B_INT_STAT 0x040034 /* Video B interrupt status */ ++#define VID_B_INT_MSTAT 0x040038 /* Video B interrupt masked status */ ++#define VID_B_INT_SSTAT 0x04003C /* Video B interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_C_INT_MSK 0x040040 /* Video C interrupt mask */ ++#define VID_C_INT_STAT 0x040044 /* Video C interrupt status */ ++#define VID_C_INT_MSTAT 0x040048 /* Video C interrupt masked status */ ++#define VID_C_INT_SSTAT 0x04004C /* Video C interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_D_INT_MSK 0x040050 /* Video D interrupt mask */ ++#define VID_D_INT_STAT 0x040054 /* Video D interrupt status */ ++#define VID_D_INT_MSTAT 0x040058 /* Video D interrupt masked status */ ++#define VID_D_INT_SSTAT 0x04005C /* Video D interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_E_INT_MSK 0x040060 /* Video E interrupt mask */ ++#define VID_E_INT_STAT 0x040064 /* Video E interrupt status */ ++#define VID_E_INT_MSTAT 0x040068 /* Video E interrupt masked status */ ++#define VID_E_INT_SSTAT 0x04006C /* Video E interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_F_INT_MSK 0x040070 /* Video F interrupt mask */ ++#define VID_F_INT_STAT 0x040074 /* Video F interrupt status */ ++#define VID_F_INT_MSTAT 0x040078 /* Video F interrupt masked status */ ++#define VID_F_INT_SSTAT 0x04007C /* Video F interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_G_INT_MSK 0x040080 /* Video G interrupt mask */ ++#define VID_G_INT_STAT 0x040084 /* Video G interrupt status */ ++#define VID_G_INT_MSTAT 0x040088 /* Video G interrupt masked status */ ++#define VID_G_INT_SSTAT 0x04008C /* Video G interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_H_INT_MSK 0x040090 /* Video H interrupt mask */ ++#define VID_H_INT_STAT 0x040094 /* Video H interrupt status */ ++#define VID_H_INT_MSTAT 0x040098 /* Video H interrupt masked status */ ++#define VID_H_INT_SSTAT 0x04009C /* Video H interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_I_INT_MSK 0x0400A0 /* Video I interrupt mask */ ++#define VID_I_INT_STAT 0x0400A4 /* Video I interrupt status */ ++#define VID_I_INT_MSTAT 0x0400A8 /* Video I interrupt masked status */ ++#define VID_I_INT_SSTAT 0x0400AC /* Video I interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define VID_J_INT_MSK 0x0400B0 /* Video J interrupt mask */ ++#define VID_J_INT_STAT 0x0400B4 /* Video J interrupt status */ ++#define VID_J_INT_MSTAT 0x0400B8 /* Video J interrupt masked status */ ++#define VID_J_INT_SSTAT 0x0400BC /* Video J interrupt set status */ ++ ++#define FLD_VID_SRC_OPC_ERR 0x00020000 ++#define FLD_VID_DST_OPC_ERR 0x00010000 ++#define FLD_VID_SRC_SYNC 0x00002000 ++#define FLD_VID_DST_SYNC 0x00001000 ++#define FLD_VID_SRC_UF 0x00000200 ++#define FLD_VID_DST_OF 0x00000100 ++#define FLD_VID_SRC_RISC2 0x00000020 ++#define FLD_VID_DST_RISC2 0x00000010 ++#define FLD_VID_SRC_RISC1 0x00000002 ++#define FLD_VID_DST_RISC1 0x00000001 ++#define FLD_VID_SRC_ERRORS (FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF) ++#define FLD_VID_DST_ERRORS (FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF) ++ ++/* ***************************************************************************** */ ++#define AUD_A_INT_MSK 0x0400C0 /* Audio Int interrupt mask */ ++#define AUD_A_INT_STAT 0x0400C4 /* Audio Int interrupt status */ ++#define AUD_A_INT_MSTAT 0x0400C8 /* Audio Int interrupt masked status */ ++#define AUD_A_INT_SSTAT 0x0400CC /* Audio Int interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define AUD_B_INT_MSK 0x0400D0 /* Audio Int interrupt mask */ ++#define AUD_B_INT_STAT 0x0400D4 /* Audio Int interrupt status */ ++#define AUD_B_INT_MSTAT 0x0400D8 /* Audio Int interrupt masked status */ ++#define AUD_B_INT_SSTAT 0x0400DC /* Audio Int interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define AUD_C_INT_MSK 0x0400E0 /* Audio Int interrupt mask */ ++#define AUD_C_INT_STAT 0x0400E4 /* Audio Int interrupt status */ ++#define AUD_C_INT_MSTAT 0x0400E8 /* Audio Int interrupt masked status */ ++#define AUD_C_INT_SSTAT 0x0400EC /* Audio Int interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define AUD_D_INT_MSK 0x0400F0 /* Audio Int interrupt mask */ ++#define AUD_D_INT_STAT 0x0400F4 /* Audio Int interrupt status */ ++#define AUD_D_INT_MSTAT 0x0400F8 /* Audio Int interrupt masked status */ ++#define AUD_D_INT_SSTAT 0x0400FC /* Audio Int interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define AUD_E_INT_MSK 0x040100 /* Audio Int interrupt mask */ ++#define AUD_E_INT_STAT 0x040104 /* Audio Int interrupt status */ ++#define AUD_E_INT_MSTAT 0x040108 /* Audio Int interrupt masked status */ ++#define AUD_E_INT_SSTAT 0x04010C /* Audio Int interrupt set status */ ++ ++#define FLD_AUD_SRC_OPC_ERR 0x00020000 ++#define FLD_AUD_DST_OPC_ERR 0x00010000 ++#define FLD_AUD_SRC_SYNC 0x00002000 ++#define FLD_AUD_DST_SYNC 0x00001000 ++#define FLD_AUD_SRC_OF 0x00000200 ++#define FLD_AUD_DST_OF 0x00000100 ++#define FLD_AUD_SRC_RISCI2 0x00000020 ++#define FLD_AUD_DST_RISCI2 0x00000010 ++#define FLD_AUD_SRC_RISCI1 0x00000002 ++#define FLD_AUD_DST_RISCI1 0x00000001 ++ ++/* ***************************************************************************** */ ++#define MBIF_A_INT_MSK 0x040110 /* MBIF Int interrupt mask */ ++#define MBIF_A_INT_STAT 0x040114 /* MBIF Int interrupt status */ ++#define MBIF_A_INT_MSTAT 0x040118 /* MBIF Int interrupt masked status */ ++#define MBIF_A_INT_SSTAT 0x04011C /* MBIF Int interrupt set status */ ++ ++/* ***************************************************************************** */ ++#define MBIF_B_INT_MSK 0x040120 /* MBIF Int interrupt mask */ ++#define MBIF_B_INT_STAT 0x040124 /* MBIF Int interrupt status */ ++#define MBIF_B_INT_MSTAT 0x040128 /* MBIF Int interrupt masked status */ ++#define MBIF_B_INT_SSTAT 0x04012C /* MBIF Int interrupt set status */ ++ ++#define FLD_MBIF_DST_OPC_ERR 0x00010000 ++#define FLD_MBIF_DST_SYNC 0x00001000 ++#define FLD_MBIF_DST_OF 0x00000100 ++#define FLD_MBIF_DST_RISCI2 0x00000010 ++#define FLD_MBIF_DST_RISCI1 0x00000001 ++ ++/* ***************************************************************************** */ ++#define AUD_EXT_INT_MSK 0x040060 /* Audio Ext interrupt mask */ ++#define AUD_EXT_INT_STAT 0x040064 /* Audio Ext interrupt status */ ++#define AUD_EXT_INT_MSTAT 0x040068 /* Audio Ext interrupt masked status */ ++#define AUD_EXT_INT_SSTAT 0x04006C /* Audio Ext interrupt set status */ ++#define FLD_AUD_EXT_OPC_ERR 0x00010000 ++#define FLD_AUD_EXT_SYNC 0x00001000 ++#define FLD_AUD_EXT_OF 0x00000100 ++#define FLD_AUD_EXT_RISCI2 0x00000010 ++#define FLD_AUD_EXT_RISCI1 0x00000001 ++ ++/* ***************************************************************************** */ ++#define GPIO_LO 0x110010 /* Lower of GPIO pins [31:0] */ ++#define GPIO_HI 0x110014 /* Upper WORD of GPIO pins [47:31] */ ++ ++#define GPIO_LO_OE 0x110018 /* Lower of GPIO output enable [31:0] */ ++#define GPIO_HI_OE 0x11001C /* Upper word of GPIO output enable [47:32] */ ++ ++#define GPIO_LO_INT_MSK 0x11003C /* GPIO interrupt mask */ ++#define GPIO_LO_INT_STAT 0x110044 /* GPIO interrupt status */ ++#define GPIO_LO_INT_MSTAT 0x11004C /* GPIO interrupt masked status */ ++#define GPIO_LO_ISM_SNS 0x110054 /* GPIO interrupt sensitivity */ ++#define GPIO_LO_ISM_POL 0x11005C /* GPIO interrupt polarity */ ++ ++#define GPIO_HI_INT_MSK 0x110040 /* GPIO interrupt mask */ ++#define GPIO_HI_INT_STAT 0x110048 /* GPIO interrupt status */ ++#define GPIO_HI_INT_MSTAT 0x110050 /* GPIO interrupt masked status */ ++#define GPIO_HI_ISM_SNS 0x110058 /* GPIO interrupt sensitivity */ ++#define GPIO_HI_ISM_POL 0x110060 /* GPIO interrupt polarity */ ++ ++#define FLD_GPIO43_INT (1 << 11) ++#define FLD_GPIO42_INT (1 << 10) ++#define FLD_GPIO41_INT (1 << 9) ++#define FLD_GPIO40_INT (1 << 8) ++ ++#define FLD_GPIO9_INT (1 << 9) ++#define FLD_GPIO8_INT (1 << 8) ++#define FLD_GPIO7_INT (1 << 7) ++#define FLD_GPIO6_INT (1 << 6) ++#define FLD_GPIO5_INT (1 << 5) ++#define FLD_GPIO4_INT (1 << 4) ++#define FLD_GPIO3_INT (1 << 3) ++#define FLD_GPIO2_INT (1 << 2) ++#define FLD_GPIO1_INT (1 << 1) ++#define FLD_GPIO0_INT (1 << 0) ++ ++/* ***************************************************************************** */ ++#define TC_REQ 0x040090 /* Rider PCI Express traFFic class request */ ++ ++/* ***************************************************************************** */ ++#define TC_REQ_SET 0x040094 /* Rider PCI Express traFFic class request set */ ++ ++/* ***************************************************************************** */ ++/* Rider */ ++/* ***************************************************************************** */ ++ ++/* PCI Compatible Header */ ++/* ***************************************************************************** */ ++#define RDR_CFG0 0x050000 ++#define RDR_VENDOR_DEVICE_ID_CFG 0x050000 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG1 0x050004 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG2 0x050008 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG3 0x05000C ++ ++/* ***************************************************************************** */ ++#define RDR_CFG4 0x050010 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG5 0x050014 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG6 0x050018 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG7 0x05001C ++ ++/* ***************************************************************************** */ ++#define RDR_CFG8 0x050020 ++ ++/* ***************************************************************************** */ ++#define RDR_CFG9 0x050024 ++ ++/* ***************************************************************************** */ ++#define RDR_CFGA 0x050028 ++ ++/* ***************************************************************************** */ ++#define RDR_CFGB 0x05002C ++#define RDR_SUSSYSTEM_ID_CFG 0x05002C ++ ++/* ***************************************************************************** */ ++#define RDR_CFGC 0x050030 ++ ++/* ***************************************************************************** */ ++#define RDR_CFGD 0x050034 ++ ++/* ***************************************************************************** */ ++#define RDR_CFGE 0x050038 ++ ++/* ***************************************************************************** */ ++#define RDR_CFGF 0x05003C ++ ++/* ***************************************************************************** */ ++/* PCI-Express Capabilities */ ++/* ***************************************************************************** */ ++#define RDR_PECAP 0x050040 ++ ++/* ***************************************************************************** */ ++#define RDR_PEDEVCAP 0x050044 ++ ++/* ***************************************************************************** */ ++#define RDR_PEDEVSC 0x050048 ++ ++/* ***************************************************************************** */ ++#define RDR_PELINKCAP 0x05004C ++ ++/* ***************************************************************************** */ ++#define RDR_PELINKSC 0x050050 ++ ++/* ***************************************************************************** */ ++#define RDR_PMICAP 0x050080 ++ ++/* ***************************************************************************** */ ++#define RDR_PMCSR 0x050084 ++ ++/* ***************************************************************************** */ ++#define RDR_VPDCAP 0x050090 ++ ++/* ***************************************************************************** */ ++#define RDR_VPDDATA 0x050094 ++ ++/* ***************************************************************************** */ ++#define RDR_MSICAP 0x0500A0 ++ ++/* ***************************************************************************** */ ++#define RDR_MSIARL 0x0500A4 ++ ++/* ***************************************************************************** */ ++#define RDR_MSIARU 0x0500A8 ++ ++/* ***************************************************************************** */ ++#define RDR_MSIDATA 0x0500AC ++ ++/* ***************************************************************************** */ ++/* PCI Express Extended Capabilities */ ++/* ***************************************************************************** */ ++#define RDR_AERXCAP 0x050100 ++ ++/* ***************************************************************************** */ ++#define RDR_AERUESTA 0x050104 ++ ++/* ***************************************************************************** */ ++#define RDR_AERUEMSK 0x050108 ++ ++/* ***************************************************************************** */ ++#define RDR_AERUESEV 0x05010C ++ ++/* ***************************************************************************** */ ++#define RDR_AERCESTA 0x050110 ++ ++/* ***************************************************************************** */ ++#define RDR_AERCEMSK 0x050114 ++ ++/* ***************************************************************************** */ ++#define RDR_AERCC 0x050118 ++ ++/* ***************************************************************************** */ ++#define RDR_AERHL0 0x05011C ++ ++/* ***************************************************************************** */ ++#define RDR_AERHL1 0x050120 ++ ++/* ***************************************************************************** */ ++#define RDR_AERHL2 0x050124 ++ ++/* ***************************************************************************** */ ++#define RDR_AERHL3 0x050128 ++ ++/* ***************************************************************************** */ ++#define RDR_VCXCAP 0x050200 ++ ++/* ***************************************************************************** */ ++#define RDR_VCCAP1 0x050204 ++ ++/* ***************************************************************************** */ ++#define RDR_VCCAP2 0x050208 ++ ++/* ***************************************************************************** */ ++#define RDR_VCSC 0x05020C ++ ++/* ***************************************************************************** */ ++#define RDR_VCR0_CAP 0x050210 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR0_CTRL 0x050214 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR0_STAT 0x050218 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR1_CAP 0x05021C ++ ++/* ***************************************************************************** */ ++#define RDR_VCR1_CTRL 0x050220 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR1_STAT 0x050224 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR2_CAP 0x050228 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR2_CTRL 0x05022C ++ ++/* ***************************************************************************** */ ++#define RDR_VCR2_STAT 0x050230 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR3_CAP 0x050234 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR3_CTRL 0x050238 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR3_STAT 0x05023C ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB0 0x050240 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB1 0x050244 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB2 0x050248 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB3 0x05024C ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB4 0x050250 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB5 0x050254 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB6 0x050258 ++ ++/* ***************************************************************************** */ ++#define RDR_VCARB7 0x05025C ++ ++/* ***************************************************************************** */ ++#define RDR_RDRSTAT0 0x050300 ++ ++/* ***************************************************************************** */ ++#define RDR_RDRSTAT1 0x050304 ++ ++/* ***************************************************************************** */ ++#define RDR_RDRCTL0 0x050308 ++ ++/* ***************************************************************************** */ ++#define RDR_RDRCTL1 0x05030C ++ ++/* ***************************************************************************** */ ++/* Transaction Layer Registers */ ++/* ***************************************************************************** */ ++#define RDR_TLSTAT0 0x050310 ++ ++/* ***************************************************************************** */ ++#define RDR_TLSTAT1 0x050314 ++ ++/* ***************************************************************************** */ ++#define RDR_TLCTL0 0x050318 ++#define FLD_CFG_UR_CPL_MODE 0x00000040 ++#define FLD_CFG_CORR_ERR_QUITE 0x00000020 ++#define FLD_CFG_RCB_CK_EN 0x00000010 ++#define FLD_CFG_BNDRY_CK_EN 0x00000008 ++#define FLD_CFG_BYTE_EN_CK_EN 0x00000004 ++#define FLD_CFG_RELAX_ORDER_MSK 0x00000002 ++#define FLD_CFG_TAG_ORDER_EN 0x00000001 ++ ++/* ***************************************************************************** */ ++#define RDR_TLCTL1 0x05031C ++ ++/* ***************************************************************************** */ ++#define RDR_REQRCAL 0x050320 ++ ++/* ***************************************************************************** */ ++#define RDR_REQRCAU 0x050324 ++ ++/* ***************************************************************************** */ ++#define RDR_REQEPA 0x050328 ++ ++/* ***************************************************************************** */ ++#define RDR_REQCTRL 0x05032C ++ ++/* ***************************************************************************** */ ++#define RDR_REQSTAT 0x050330 ++ ++/* ***************************************************************************** */ ++#define RDR_TL_TEST 0x050334 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR01_CTL 0x050348 ++ ++/* ***************************************************************************** */ ++#define RDR_VCR23_CTL 0x05034C ++ ++/* ***************************************************************************** */ ++#define RDR_RX_VCR0_FC 0x050350 ++ ++/* ***************************************************************************** */ ++#define RDR_RX_VCR1_FC 0x050354 ++ ++/* ***************************************************************************** */ ++#define RDR_RX_VCR2_FC 0x050358 ++ ++/* ***************************************************************************** */ ++#define RDR_RX_VCR3_FC 0x05035C ++ ++/* ***************************************************************************** */ ++/* Data Link Layer Registers */ ++/* ***************************************************************************** */ ++#define RDR_DLLSTAT 0x050360 ++ ++/* ***************************************************************************** */ ++#define RDR_DLLCTRL 0x050364 ++ ++/* ***************************************************************************** */ ++#define RDR_REPLAYTO 0x050368 ++ ++/* ***************************************************************************** */ ++#define RDR_ACKLATTO 0x05036C ++ ++/* ***************************************************************************** */ ++/* MAC Layer Registers */ ++/* ***************************************************************************** */ ++#define RDR_MACSTAT0 0x050380 ++ ++/* ***************************************************************************** */ ++#define RDR_MACSTAT1 0x050384 ++ ++/* ***************************************************************************** */ ++#define RDR_MACCTRL0 0x050388 ++ ++/* ***************************************************************************** */ ++#define RDR_MACCTRL1 0x05038C ++ ++/* ***************************************************************************** */ ++#define RDR_MACCTRL2 0x050390 ++ ++/* ***************************************************************************** */ ++#define RDR_MAC_LB_DATA 0x050394 ++ ++/* ***************************************************************************** */ ++#define RDR_L0S_EXIT_LAT 0x050398 ++ ++/* ***************************************************************************** */ ++/* DMAC */ ++/* ***************************************************************************** */ ++#define DMA1_PTR1 0x100000 /* DMA Current Ptr : Ch#1 */ ++ ++/* ***************************************************************************** */ ++#define DMA2_PTR1 0x100004 /* DMA Current Ptr : Ch#2 */ ++ ++/* ***************************************************************************** */ ++#define DMA3_PTR1 0x100008 /* DMA Current Ptr : Ch#3 */ ++ ++/* ***************************************************************************** */ ++#define DMA4_PTR1 0x10000C /* DMA Current Ptr : Ch#4 */ ++ ++/* ***************************************************************************** */ ++#define DMA5_PTR1 0x100010 /* DMA Current Ptr : Ch#5 */ ++ ++/* ***************************************************************************** */ ++#define DMA6_PTR1 0x100014 /* DMA Current Ptr : Ch#6 */ ++ ++/* ***************************************************************************** */ ++#define DMA7_PTR1 0x100018 /* DMA Current Ptr : Ch#7 */ ++ ++/* ***************************************************************************** */ ++#define DMA8_PTR1 0x10001C /* DMA Current Ptr : Ch#8 */ ++ ++/* ***************************************************************************** */ ++#define DMA9_PTR1 0x100020 /* DMA Current Ptr : Ch#9 */ ++ ++/* ***************************************************************************** */ ++#define DMA10_PTR1 0x100024 /* DMA Current Ptr : Ch#10 */ ++ ++/* ***************************************************************************** */ ++#define DMA11_PTR1 0x100028 /* DMA Current Ptr : Ch#11 */ ++ ++/* ***************************************************************************** */ ++#define DMA12_PTR1 0x10002C /* DMA Current Ptr : Ch#12 */ ++ ++/* ***************************************************************************** */ ++#define DMA13_PTR1 0x100030 /* DMA Current Ptr : Ch#13 */ ++ ++/* ***************************************************************************** */ ++#define DMA14_PTR1 0x100034 /* DMA Current Ptr : Ch#14 */ ++ ++/* ***************************************************************************** */ ++#define DMA15_PTR1 0x100038 /* DMA Current Ptr : Ch#15 */ ++ ++/* ***************************************************************************** */ ++#define DMA16_PTR1 0x10003C /* DMA Current Ptr : Ch#16 */ ++ ++/* ***************************************************************************** */ ++#define DMA17_PTR1 0x100040 /* DMA Current Ptr : Ch#17 */ ++ ++/* ***************************************************************************** */ ++#define DMA18_PTR1 0x100044 /* DMA Current Ptr : Ch#18 */ ++ ++/* ***************************************************************************** */ ++#define DMA19_PTR1 0x100048 /* DMA Current Ptr : Ch#19 */ ++ ++/* ***************************************************************************** */ ++#define DMA20_PTR1 0x10004C /* DMA Current Ptr : Ch#20 */ ++ ++/* ***************************************************************************** */ ++#define DMA21_PTR1 0x100050 /* DMA Current Ptr : Ch#21 */ ++ ++/* ***************************************************************************** */ ++#define DMA22_PTR1 0x100054 /* DMA Current Ptr : Ch#22 */ ++ ++/* ***************************************************************************** */ ++#define DMA23_PTR1 0x100058 /* DMA Current Ptr : Ch#23 */ ++ ++/* ***************************************************************************** */ ++#define DMA24_PTR1 0x10005C /* DMA Current Ptr : Ch#24 */ ++ ++/* ***************************************************************************** */ ++#define DMA25_PTR1 0x100060 /* DMA Current Ptr : Ch#25 */ ++ ++/* ***************************************************************************** */ ++#define DMA26_PTR1 0x100064 /* DMA Current Ptr : Ch#26 */ ++ ++/* ***************************************************************************** */ ++#define DMA1_PTR2 0x100080 /* DMA Tab Ptr : Ch#1 */ ++ ++/* ***************************************************************************** */ ++#define DMA2_PTR2 0x100084 /* DMA Tab Ptr : Ch#2 */ ++ ++/* ***************************************************************************** */ ++#define DMA3_PTR2 0x100088 /* DMA Tab Ptr : Ch#3 */ ++ ++/* ***************************************************************************** */ ++#define DMA4_PTR2 0x10008C /* DMA Tab Ptr : Ch#4 */ ++ ++/* ***************************************************************************** */ ++#define DMA5_PTR2 0x100090 /* DMA Tab Ptr : Ch#5 */ ++ ++/* ***************************************************************************** */ ++#define DMA6_PTR2 0x100094 /* DMA Tab Ptr : Ch#6 */ ++ ++/* ***************************************************************************** */ ++#define DMA7_PTR2 0x100098 /* DMA Tab Ptr : Ch#7 */ ++ ++/* ***************************************************************************** */ ++#define DMA8_PTR2 0x10009C /* DMA Tab Ptr : Ch#8 */ ++ ++/* ***************************************************************************** */ ++#define DMA9_PTR2 0x1000A0 /* DMA Tab Ptr : Ch#9 */ ++ ++/* ***************************************************************************** */ ++#define DMA10_PTR2 0x1000A4 /* DMA Tab Ptr : Ch#10 */ ++ ++/* ***************************************************************************** */ ++#define DMA11_PTR2 0x1000A8 /* DMA Tab Ptr : Ch#11 */ ++ ++/* ***************************************************************************** */ ++#define DMA12_PTR2 0x1000AC /* DMA Tab Ptr : Ch#12 */ ++ ++/* ***************************************************************************** */ ++#define DMA13_PTR2 0x1000B0 /* DMA Tab Ptr : Ch#13 */ ++ ++/* ***************************************************************************** */ ++#define DMA14_PTR2 0x1000B4 /* DMA Tab Ptr : Ch#14 */ ++ ++/* ***************************************************************************** */ ++#define DMA15_PTR2 0x1000B8 /* DMA Tab Ptr : Ch#15 */ ++ ++/* ***************************************************************************** */ ++#define DMA16_PTR2 0x1000BC /* DMA Tab Ptr : Ch#16 */ ++ ++/* ***************************************************************************** */ ++#define DMA17_PTR2 0x1000C0 /* DMA Tab Ptr : Ch#17 */ ++ ++/* ***************************************************************************** */ ++#define DMA18_PTR2 0x1000C4 /* DMA Tab Ptr : Ch#18 */ ++ ++/* ***************************************************************************** */ ++#define DMA19_PTR2 0x1000C8 /* DMA Tab Ptr : Ch#19 */ ++ ++/* ***************************************************************************** */ ++#define DMA20_PTR2 0x1000CC /* DMA Tab Ptr : Ch#20 */ ++ ++/* ***************************************************************************** */ ++#define DMA21_PTR2 0x1000D0 /* DMA Tab Ptr : Ch#21 */ ++ ++/* ***************************************************************************** */ ++#define DMA22_PTR2 0x1000D4 /* DMA Tab Ptr : Ch#22 */ ++ ++/* ***************************************************************************** */ ++#define DMA23_PTR2 0x1000D8 /* DMA Tab Ptr : Ch#23 */ ++ ++/* ***************************************************************************** */ ++#define DMA24_PTR2 0x1000DC /* DMA Tab Ptr : Ch#24 */ ++ ++/* ***************************************************************************** */ ++#define DMA25_PTR2 0x1000E0 /* DMA Tab Ptr : Ch#25 */ ++ ++/* ***************************************************************************** */ ++#define DMA26_PTR2 0x1000E4 /* DMA Tab Ptr : Ch#26 */ ++ ++/* ***************************************************************************** */ ++#define DMA1_CNT1 0x100100 /* DMA BuFFer Size : Ch#1 */ ++ ++/* ***************************************************************************** */ ++#define DMA2_CNT1 0x100104 /* DMA BuFFer Size : Ch#2 */ ++ ++/* ***************************************************************************** */ ++#define DMA3_CNT1 0x100108 /* DMA BuFFer Size : Ch#3 */ ++ ++/* ***************************************************************************** */ ++#define DMA4_CNT1 0x10010C /* DMA BuFFer Size : Ch#4 */ ++ ++/* ***************************************************************************** */ ++#define DMA5_CNT1 0x100110 /* DMA BuFFer Size : Ch#5 */ ++ ++/* ***************************************************************************** */ ++#define DMA6_CNT1 0x100114 /* DMA BuFFer Size : Ch#6 */ ++ ++/* ***************************************************************************** */ ++#define DMA7_CNT1 0x100118 /* DMA BuFFer Size : Ch#7 */ ++ ++/* ***************************************************************************** */ ++#define DMA8_CNT1 0x10011C /* DMA BuFFer Size : Ch#8 */ ++ ++/* ***************************************************************************** */ ++#define DMA9_CNT1 0x100120 /* DMA BuFFer Size : Ch#9 */ ++ ++/* ***************************************************************************** */ ++#define DMA10_CNT1 0x100124 /* DMA BuFFer Size : Ch#10 */ ++ ++/* ***************************************************************************** */ ++#define DMA11_CNT1 0x100128 /* DMA BuFFer Size : Ch#11 */ ++ ++/* ***************************************************************************** */ ++#define DMA12_CNT1 0x10012C /* DMA BuFFer Size : Ch#12 */ ++ ++/* ***************************************************************************** */ ++#define DMA13_CNT1 0x100130 /* DMA BuFFer Size : Ch#13 */ ++ ++/* ***************************************************************************** */ ++#define DMA14_CNT1 0x100134 /* DMA BuFFer Size : Ch#14 */ ++ ++/* ***************************************************************************** */ ++#define DMA15_CNT1 0x100138 /* DMA BuFFer Size : Ch#15 */ ++ ++/* ***************************************************************************** */ ++#define DMA16_CNT1 0x10013C /* DMA BuFFer Size : Ch#16 */ ++ ++/* ***************************************************************************** */ ++#define DMA17_CNT1 0x100140 /* DMA BuFFer Size : Ch#17 */ ++ ++/* ***************************************************************************** */ ++#define DMA18_CNT1 0x100144 /* DMA BuFFer Size : Ch#18 */ ++ ++/* ***************************************************************************** */ ++#define DMA19_CNT1 0x100148 /* DMA BuFFer Size : Ch#19 */ ++ ++/* ***************************************************************************** */ ++#define DMA20_CNT1 0x10014C /* DMA BuFFer Size : Ch#20 */ ++ ++/* ***************************************************************************** */ ++#define DMA21_CNT1 0x100150 /* DMA BuFFer Size : Ch#21 */ ++ ++/* ***************************************************************************** */ ++#define DMA22_CNT1 0x100154 /* DMA BuFFer Size : Ch#22 */ ++ ++/* ***************************************************************************** */ ++#define DMA23_CNT1 0x100158 /* DMA BuFFer Size : Ch#23 */ ++ ++/* ***************************************************************************** */ ++#define DMA24_CNT1 0x10015C /* DMA BuFFer Size : Ch#24 */ ++ ++/* ***************************************************************************** */ ++#define DMA25_CNT1 0x100160 /* DMA BuFFer Size : Ch#25 */ ++ ++/* ***************************************************************************** */ ++#define DMA26_CNT1 0x100164 /* DMA BuFFer Size : Ch#26 */ ++ ++/* ***************************************************************************** */ ++#define DMA1_CNT2 0x100180 /* DMA Table Size : Ch#1 */ ++ ++/* ***************************************************************************** */ ++#define DMA2_CNT2 0x100184 /* DMA Table Size : Ch#2 */ ++ ++/* ***************************************************************************** */ ++#define DMA3_CNT2 0x100188 /* DMA Table Size : Ch#3 */ ++ ++/* ***************************************************************************** */ ++#define DMA4_CNT2 0x10018C /* DMA Table Size : Ch#4 */ ++ ++/* ***************************************************************************** */ ++#define DMA5_CNT2 0x100190 /* DMA Table Size : Ch#5 */ ++ ++/* ***************************************************************************** */ ++#define DMA6_CNT2 0x100194 /* DMA Table Size : Ch#6 */ ++ ++/* ***************************************************************************** */ ++#define DMA7_CNT2 0x100198 /* DMA Table Size : Ch#7 */ ++ ++/* ***************************************************************************** */ ++#define DMA8_CNT2 0x10019C /* DMA Table Size : Ch#8 */ ++ ++/* ***************************************************************************** */ ++#define DMA9_CNT2 0x1001A0 /* DMA Table Size : Ch#9 */ ++ ++/* ***************************************************************************** */ ++#define DMA10_CNT2 0x1001A4 /* DMA Table Size : Ch#10 */ ++ ++/* ***************************************************************************** */ ++#define DMA11_CNT2 0x1001A8 /* DMA Table Size : Ch#11 */ ++ ++/* ***************************************************************************** */ ++#define DMA12_CNT2 0x1001AC /* DMA Table Size : Ch#12 */ ++ ++/* ***************************************************************************** */ ++#define DMA13_CNT2 0x1001B0 /* DMA Table Size : Ch#13 */ ++ ++/* ***************************************************************************** */ ++#define DMA14_CNT2 0x1001B4 /* DMA Table Size : Ch#14 */ ++ ++/* ***************************************************************************** */ ++#define DMA15_CNT2 0x1001B8 /* DMA Table Size : Ch#15 */ ++ ++/* ***************************************************************************** */ ++#define DMA16_CNT2 0x1001BC /* DMA Table Size : Ch#16 */ ++ ++/* ***************************************************************************** */ ++#define DMA17_CNT2 0x1001C0 /* DMA Table Size : Ch#17 */ ++ ++/* ***************************************************************************** */ ++#define DMA18_CNT2 0x1001C4 /* DMA Table Size : Ch#18 */ ++ ++/* ***************************************************************************** */ ++#define DMA19_CNT2 0x1001C8 /* DMA Table Size : Ch#19 */ ++ ++/* ***************************************************************************** */ ++#define DMA20_CNT2 0x1001CC /* DMA Table Size : Ch#20 */ ++ ++/* ***************************************************************************** */ ++#define DMA21_CNT2 0x1001D0 /* DMA Table Size : Ch#21 */ ++ ++/* ***************************************************************************** */ ++#define DMA22_CNT2 0x1001D4 /* DMA Table Size : Ch#22 */ ++ ++/* ***************************************************************************** */ ++#define DMA23_CNT2 0x1001D8 /* DMA Table Size : Ch#23 */ ++ ++/* ***************************************************************************** */ ++#define DMA24_CNT2 0x1001DC /* DMA Table Size : Ch#24 */ ++ ++/* ***************************************************************************** */ ++#define DMA25_CNT2 0x1001E0 /* DMA Table Size : Ch#25 */ ++ ++/* ***************************************************************************** */ ++#define DMA26_CNT2 0x1001E4 /* DMA Table Size : Ch#26 */ ++ ++/* ***************************************************************************** */ ++ /* ITG */ ++/* ***************************************************************************** */ ++#define TM_CNT_LDW 0x110000 /* Timer : Counter low */ ++ ++/* ***************************************************************************** */ ++#define TM_CNT_UW 0x110004 /* Timer : Counter high word */ ++ ++/* ***************************************************************************** */ ++#define TM_LMT_LDW 0x110008 /* Timer : Limit low */ ++ ++/* ***************************************************************************** */ ++#define TM_LMT_UW 0x11000C /* Timer : Limit high word */ ++ ++/* ***************************************************************************** */ ++#define GP0_IO 0x110010 /* GPIO output enables data I/O */ ++#define FLD_GP_OE 0x00FF0000 /* GPIO: GP_OE output enable */ ++#define FLD_GP_IN 0x0000FF00 /* GPIO: GP_IN status */ ++#define FLD_GP_OUT 0x000000FF /* GPIO: GP_OUT control */ ++ ++/* ***************************************************************************** */ ++#define GPIO_ISM 0x110014 /* GPIO interrupt sensitivity mode */ ++#define FLD_GP_ISM_SNS 0x00000070 ++#define FLD_GP_ISM_POL 0x00000007 ++ ++/* ***************************************************************************** */ ++#define SOFT_RESET 0x11001C /* Output system reset reg */ ++#define FLD_PECOS_SOFT_RESET 0x00000001 ++ ++/* ***************************************************************************** */ ++#define MC416_RWD 0x110020 /* MC416 GPIO[18:3] pin */ ++#define MC416_OEN 0x110024 /* Output enable of GPIO[18:3] */ ++#define MC416_CTL 0x110028 ++ ++/* ***************************************************************************** */ ++#define ALT_PIN_OUT_SEL 0x11002C /* Alternate GPIO output select */ ++ ++#define FLD_ALT_GPIO_OUT_SEL 0xF0000000 ++/* 0 Disabled <-- default */ ++/* 1 GPIO[0] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++/* 8 ATT_IF */ ++ ++#define FLD_AUX_PLL_CLK_ALT_SEL 0x0F000000 ++/* 0 AUX_PLL_CLK<-- default */ ++/* 1 GPIO[2] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_IR_TX_ALT_SEL 0x00F00000 ++/* 0 IR_TX <-- default */ ++/* 1 GPIO[1] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_IR_RX_ALT_SEL 0x000F0000 ++/* 0 IR_RX <-- default */ ++/* 1 GPIO[0] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_GPIO10_ALT_SEL 0x0000F000 ++/* 0 GPIO[10] <-- default */ ++/* 1 GPIO[0] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_GPIO2_ALT_SEL 0x00000F00 ++/* 0 GPIO[2] <-- default */ ++/* 1 GPIO[1] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_GPIO1_ALT_SEL 0x000000F0 ++/* 0 GPIO[1] <-- default */ ++/* 1 GPIO[0] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define FLD_GPIO0_ALT_SEL 0x0000000F ++/* 0 GPIO[0] <-- default */ ++/* 1 GPIO[1] */ ++/* 2 GPIO[10] */ ++/* 3 VIP_656_DATA_VAL */ ++/* 4 VIP_656_DATA[0] */ ++/* 5 VIP_656_CLK */ ++/* 6 VIP_656_DATA_EXT[1] */ ++/* 7 VIP_656_DATA_EXT[0] */ ++ ++#define ALT_PIN_IN_SEL 0x110030 /* Alternate GPIO input select */ ++ ++#define FLD_GPIO10_ALT_IN_SEL 0x0000F000 ++/* 0 GPIO[10] <-- default */ ++/* 1 IR_RX */ ++/* 2 IR_TX */ ++/* 3 AUX_PLL_CLK */ ++/* 4 IF_ATT_SEL */ ++/* 5 GPIO[0] */ ++/* 6 GPIO[1] */ ++/* 7 GPIO[2] */ ++ ++#define FLD_GPIO2_ALT_IN_SEL 0x00000F00 ++/* 0 GPIO[2] <-- default */ ++/* 1 IR_RX */ ++/* 2 IR_TX */ ++/* 3 AUX_PLL_CLK */ ++/* 4 IF_ATT_SEL */ ++ ++#define FLD_GPIO1_ALT_IN_SEL 0x000000F0 ++/* 0 GPIO[1] <-- default */ ++/* 1 IR_RX */ ++/* 2 IR_TX */ ++/* 3 AUX_PLL_CLK */ ++/* 4 IF_ATT_SEL */ ++ ++#define FLD_GPIO0_ALT_IN_SEL 0x0000000F ++/* 0 GPIO[0] <-- default */ ++/* 1 IR_RX */ ++/* 2 IR_TX */ ++/* 3 AUX_PLL_CLK */ ++/* 4 IF_ATT_SEL */ ++ ++/* ***************************************************************************** */ ++#define TEST_BUS_CTL1 0x110040 /* Test bus control register #1 */ ++ ++/* ***************************************************************************** */ ++#define TEST_BUS_CTL2 0x110044 /* Test bus control register #2 */ ++ ++/* ***************************************************************************** */ ++#define CLK_DELAY 0x110048 /* Clock delay */ ++#define FLD_MOE_CLK_DIS 0x80000000 /* Disable MoE clock */ ++ ++/* ***************************************************************************** */ ++#define PAD_CTRL 0x110068 /* Pad drive strength control */ ++ ++/* ***************************************************************************** */ ++#define MBIST_CTRL 0x110050 /* SRAM memory built-in self test control */ ++ ++/* ***************************************************************************** */ ++#define MBIST_STAT 0x110054 /* SRAM memory built-in self test status */ ++ ++/* ***************************************************************************** */ ++/* PLL registers */ ++/* ***************************************************************************** */ ++#define PLL_A_INT_FRAC 0x110088 ++#define PLL_A_POST_STAT_BIST 0x11008C ++#define PLL_B_INT_FRAC 0x110090 ++#define PLL_B_POST_STAT_BIST 0x110094 ++#define PLL_C_INT_FRAC 0x110098 ++#define PLL_C_POST_STAT_BIST 0x11009C ++#define PLL_D_INT_FRAC 0x1100A0 ++#define PLL_D_POST_STAT_BIST 0x1100A4 ++ ++#define CLK_RST 0x11002C ++#define FLD_VID_I_CLK_NOE 0x00001000 ++#define FLD_VID_J_CLK_NOE 0x00002000 ++#define FLD_USE_ALT_PLL_REF 0x00004000 ++ ++#define VID_CH_MODE_SEL 0x110078 ++#define VID_CH_CLK_SEL 0x11007C ++ ++/* ***************************************************************************** */ ++#define VBI_A_DMA 0x130008 /* VBI A DMA data port */ ++ ++/* ***************************************************************************** */ ++#define VID_A_VIP_CTL 0x130080 /* Video A VIP format control */ ++#define FLD_VIP_MODE 0x00000001 ++ ++/* ***************************************************************************** */ ++#define VID_A_PIXEL_FRMT 0x130084 /* Video A pixel format */ ++#define FLD_VID_A_GAMMA_DIS 0x00000008 ++#define FLD_VID_A_FORMAT 0x00000007 ++#define FLD_VID_A_GAMMA_FACTOR 0x00000010 ++ ++/* ***************************************************************************** */ ++#define VID_A_VBI_CTL 0x130088 /* Video A VBI miscellaneous control */ ++#define FLD_VID_A_VIP_EXT 0x00000003 ++ ++/* ***************************************************************************** */ ++#define VID_B_DMA 0x130100 /* Video B DMA data port */ ++ ++/* ***************************************************************************** */ ++#define VBI_B_DMA 0x130108 /* VBI B DMA data port */ ++ ++/* ***************************************************************************** */ ++#define VID_B_SRC_SEL 0x130144 /* Video B source select */ ++#define FLD_VID_B_SRC_SEL 0x00000000 ++ ++/* ***************************************************************************** */ ++#define VID_B_LNGTH 0x130150 /* Video B line length */ ++#define FLD_VID_B_LN_LNGTH 0x00000FFF ++ ++/* ***************************************************************************** */ ++#define VID_B_VIP_CTL 0x130180 /* Video B VIP format control */ ++ ++/* ***************************************************************************** */ ++#define VID_B_PIXEL_FRMT 0x130184 /* Video B pixel format */ ++#define FLD_VID_B_GAMMA_DIS 0x00000008 ++#define FLD_VID_B_FORMAT 0x00000007 ++#define FLD_VID_B_GAMMA_FACTOR 0x00000010 ++ ++/* ***************************************************************************** */ ++#define VID_C_DMA 0x130200 /* Video C DMA data port */ ++ ++/* ***************************************************************************** */ ++#define VID_C_LNGTH 0x130250 /* Video C line length */ ++#define FLD_VID_C_LN_LNGTH 0x00000FFF ++ ++/* ***************************************************************************** */ ++/* Video Destination Channels */ ++/* ***************************************************************************** */ ++ ++#define VID_DST_A_GPCNT 0x130020 /* Video A general purpose counter */ ++#define VID_DST_B_GPCNT 0x130120 /* Video B general purpose counter */ ++#define VID_DST_C_GPCNT 0x130220 /* Video C general purpose counter */ ++#define VID_DST_D_GPCNT 0x130320 /* Video D general purpose counter */ ++#define VID_DST_E_GPCNT 0x130420 /* Video E general purpose counter */ ++#define VID_DST_F_GPCNT 0x130520 /* Video F general purpose counter */ ++#define VID_DST_G_GPCNT 0x130620 /* Video G general purpose counter */ ++#define VID_DST_H_GPCNT 0x130720 /* Video H general purpose counter */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_DST_A_GPCNT_CTL 0x130030 /* Video A general purpose control */ ++#define VID_DST_B_GPCNT_CTL 0x130130 /* Video B general purpose control */ ++#define VID_DST_C_GPCNT_CTL 0x130230 /* Video C general purpose control */ ++#define VID_DST_D_GPCNT_CTL 0x130330 /* Video D general purpose control */ ++#define VID_DST_E_GPCNT_CTL 0x130430 /* Video E general purpose control */ ++#define VID_DST_F_GPCNT_CTL 0x130530 /* Video F general purpose control */ ++#define VID_DST_G_GPCNT_CTL 0x130630 /* Video G general purpose control */ ++#define VID_DST_H_GPCNT_CTL 0x130730 /* Video H general purpose control */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_DST_A_DMA_CTL 0x130040 /* Video A DMA control */ ++#define VID_DST_B_DMA_CTL 0x130140 /* Video B DMA control */ ++#define VID_DST_C_DMA_CTL 0x130240 /* Video C DMA control */ ++#define VID_DST_D_DMA_CTL 0x130340 /* Video D DMA control */ ++#define VID_DST_E_DMA_CTL 0x130440 /* Video E DMA control */ ++#define VID_DST_F_DMA_CTL 0x130540 /* Video F DMA control */ ++#define VID_DST_G_DMA_CTL 0x130640 /* Video G DMA control */ ++#define VID_DST_H_DMA_CTL 0x130740 /* Video H DMA control */ ++ ++#define FLD_VID_RISC_EN 0x00000010 ++#define FLD_VID_FIFO_EN 0x00000001 ++ ++/* ***************************************************************************** */ ++ ++#define VID_DST_A_VIP_CTL 0x130080 /* Video A VIP control */ ++#define VID_DST_B_VIP_CTL 0x130180 /* Video B VIP control */ ++#define VID_DST_C_VIP_CTL 0x130280 /* Video C VIP control */ ++#define VID_DST_D_VIP_CTL 0x130380 /* Video D VIP control */ ++#define VID_DST_E_VIP_CTL 0x130480 /* Video E VIP control */ ++#define VID_DST_F_VIP_CTL 0x130580 /* Video F VIP control */ ++#define VID_DST_G_VIP_CTL 0x130680 /* Video G VIP control */ ++#define VID_DST_H_VIP_CTL 0x130780 /* Video H VIP control */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_DST_A_PIX_FRMT 0x130084 /* Video A Pixel format */ ++#define VID_DST_B_PIX_FRMT 0x130184 /* Video B Pixel format */ ++#define VID_DST_C_PIX_FRMT 0x130284 /* Video C Pixel format */ ++#define VID_DST_D_PIX_FRMT 0x130384 /* Video D Pixel format */ ++#define VID_DST_E_PIX_FRMT 0x130484 /* Video E Pixel format */ ++#define VID_DST_F_PIX_FRMT 0x130584 /* Video F Pixel format */ ++#define VID_DST_G_PIX_FRMT 0x130684 /* Video G Pixel format */ ++#define VID_DST_H_PIX_FRMT 0x130784 /* Video H Pixel format */ ++ ++/* ***************************************************************************** */ ++/* Video Source Channels */ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_GPCNT_CTL 0x130804 /* Video A general purpose control */ ++#define VID_SRC_B_GPCNT_CTL 0x130904 /* Video B general purpose control */ ++#define VID_SRC_C_GPCNT_CTL 0x130A04 /* Video C general purpose control */ ++#define VID_SRC_D_GPCNT_CTL 0x130B04 /* Video D general purpose control */ ++#define VID_SRC_E_GPCNT_CTL 0x130C04 /* Video E general purpose control */ ++#define VID_SRC_F_GPCNT_CTL 0x130D04 /* Video F general purpose control */ ++#define VID_SRC_I_GPCNT_CTL 0x130E04 /* Video I general purpose control */ ++#define VID_SRC_J_GPCNT_CTL 0x130F04 /* Video J general purpose control */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_GPCNT 0x130808 /* Video A general purpose counter */ ++#define VID_SRC_B_GPCNT 0x130908 /* Video B general purpose counter */ ++#define VID_SRC_C_GPCNT 0x130A08 /* Video C general purpose counter */ ++#define VID_SRC_D_GPCNT 0x130B08 /* Video D general purpose counter */ ++#define VID_SRC_E_GPCNT 0x130C08 /* Video E general purpose counter */ ++#define VID_SRC_F_GPCNT 0x130D08 /* Video F general purpose counter */ ++#define VID_SRC_I_GPCNT 0x130E08 /* Video I general purpose counter */ ++#define VID_SRC_J_GPCNT 0x130F08 /* Video J general purpose counter */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_DMA_CTL 0x13080C /* Video A DMA control */ ++#define VID_SRC_B_DMA_CTL 0x13090C /* Video B DMA control */ ++#define VID_SRC_C_DMA_CTL 0x130A0C /* Video C DMA control */ ++#define VID_SRC_D_DMA_CTL 0x130B0C /* Video D DMA control */ ++#define VID_SRC_E_DMA_CTL 0x130C0C /* Video E DMA control */ ++#define VID_SRC_F_DMA_CTL 0x130D0C /* Video F DMA control */ ++#define VID_SRC_I_DMA_CTL 0x130E0C /* Video I DMA control */ ++#define VID_SRC_J_DMA_CTL 0x130F0C /* Video J DMA control */ ++ ++#define FLD_APB_RISC_EN 0x00000010 ++#define FLD_APB_FIFO_EN 0x00000001 ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_FMT_CTL 0x130810 /* Video A format control */ ++#define VID_SRC_B_FMT_CTL 0x130910 /* Video B format control */ ++#define VID_SRC_C_FMT_CTL 0x130A10 /* Video C format control */ ++#define VID_SRC_D_FMT_CTL 0x130B10 /* Video D format control */ ++#define VID_SRC_E_FMT_CTL 0x130C10 /* Video E format control */ ++#define VID_SRC_F_FMT_CTL 0x130D10 /* Video F format control */ ++#define VID_SRC_I_FMT_CTL 0x130E10 /* Video I format control */ ++#define VID_SRC_J_FMT_CTL 0x130F10 /* Video J format control */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_ACTIVE_CTL1 0x130814 /* Video A active control 1 */ ++#define VID_SRC_B_ACTIVE_CTL1 0x130914 /* Video B active control 1 */ ++#define VID_SRC_C_ACTIVE_CTL1 0x130A14 /* Video C active control 1 */ ++#define VID_SRC_D_ACTIVE_CTL1 0x130B14 /* Video D active control 1 */ ++#define VID_SRC_E_ACTIVE_CTL1 0x130C14 /* Video E active control 1 */ ++#define VID_SRC_F_ACTIVE_CTL1 0x130D14 /* Video F active control 1 */ ++#define VID_SRC_I_ACTIVE_CTL1 0x130E14 /* Video I active control 1 */ ++#define VID_SRC_J_ACTIVE_CTL1 0x130F14 /* Video J active control 1 */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_ACTIVE_CTL2 0x130818 /* Video A active control 2 */ ++#define VID_SRC_B_ACTIVE_CTL2 0x130918 /* Video B active control 2 */ ++#define VID_SRC_C_ACTIVE_CTL2 0x130A18 /* Video C active control 2 */ ++#define VID_SRC_D_ACTIVE_CTL2 0x130B18 /* Video D active control 2 */ ++#define VID_SRC_E_ACTIVE_CTL2 0x130C18 /* Video E active control 2 */ ++#define VID_SRC_F_ACTIVE_CTL2 0x130D18 /* Video F active control 2 */ ++#define VID_SRC_I_ACTIVE_CTL2 0x130E18 /* Video I active control 2 */ ++#define VID_SRC_J_ACTIVE_CTL2 0x130F18 /* Video J active control 2 */ ++ ++/* ***************************************************************************** */ ++ ++#define VID_SRC_A_CDT_SZ 0x13081C /* Video A CDT size */ ++#define VID_SRC_B_CDT_SZ 0x13091C /* Video B CDT size */ ++#define VID_SRC_C_CDT_SZ 0x130A1C /* Video C CDT size */ ++#define VID_SRC_D_CDT_SZ 0x130B1C /* Video D CDT size */ ++#define VID_SRC_E_CDT_SZ 0x130C1C /* Video E CDT size */ ++#define VID_SRC_F_CDT_SZ 0x130D1C /* Video F CDT size */ ++#define VID_SRC_I_CDT_SZ 0x130E1C /* Video I CDT size */ ++#define VID_SRC_J_CDT_SZ 0x130F1C /* Video J CDT size */ ++ ++/* ***************************************************************************** */ ++/* Audio I/F */ ++/* ***************************************************************************** */ ++#define AUD_DST_A_DMA 0x140000 /* Audio Int A DMA data port */ ++#define AUD_SRC_A_DMA 0x140008 /* Audio Int A DMA data port */ ++ ++#define AUD_A_GPCNT 0x140010 /* Audio Int A gp counter */ ++#define FLD_AUD_A_GP_CNT 0x0000FFFF ++ ++#define AUD_A_GPCNT_CTL 0x140014 /* Audio Int A gp control */ ++ ++#define AUD_A_LNGTH 0x140018 /* Audio Int A line length */ ++ ++#define AUD_A_CFG 0x14001C /* Audio Int A configuration */ ++ ++/* ***************************************************************************** */ ++#define AUD_DST_B_DMA 0x140100 /* Audio Int B DMA data port */ ++#define AUD_SRC_B_DMA 0x140108 /* Audio Int B DMA data port */ ++ ++#define AUD_B_GPCNT 0x140110 /* Audio Int B gp counter */ ++#define FLD_AUD_B_GP_CNT 0x0000FFFF ++ ++#define AUD_B_GPCNT_CTL 0x140114 /* Audio Int B gp control */ ++ ++#define AUD_B_LNGTH 0x140118 /* Audio Int B line length */ ++ ++#define AUD_B_CFG 0x14011C /* Audio Int B configuration */ ++ ++/* ***************************************************************************** */ ++#define AUD_DST_C_DMA 0x140200 /* Audio Int C DMA data port */ ++#define AUD_SRC_C_DMA 0x140208 /* Audio Int C DMA data port */ ++ ++#define AUD_C_GPCNT 0x140210 /* Audio Int C gp counter */ ++#define FLD_AUD_C_GP_CNT 0x0000FFFF ++ ++#define AUD_C_GPCNT_CTL 0x140214 /* Audio Int C gp control */ ++ ++#define AUD_C_LNGTH 0x140218 /* Audio Int C line length */ ++ ++#define AUD_C_CFG 0x14021C /* Audio Int C configuration */ ++ ++/* ***************************************************************************** */ ++#define AUD_DST_D_DMA 0x140300 /* Audio Int D DMA data port */ ++#define AUD_SRC_D_DMA 0x140308 /* Audio Int D DMA data port */ ++ ++#define AUD_D_GPCNT 0x140310 /* Audio Int D gp counter */ ++#define FLD_AUD_D_GP_CNT 0x0000FFFF ++ ++#define AUD_D_GPCNT_CTL 0x140314 /* Audio Int D gp control */ ++ ++#define AUD_D_LNGTH 0x140318 /* Audio Int D line length */ ++ ++#define AUD_D_CFG 0x14031C /* Audio Int D configuration */ ++ ++/* ***************************************************************************** */ ++#define AUD_SRC_E_DMA 0x140400 /* Audio Int E DMA data port */ ++ ++#define AUD_E_GPCNT 0x140410 /* Audio Int E gp counter */ ++#define FLD_AUD_E_GP_CNT 0x0000FFFF ++ ++#define AUD_E_GPCNT_CTL 0x140414 /* Audio Int E gp control */ ++ ++#define AUD_E_CFG 0x14041C /* Audio Int E configuration */ ++ ++/* ***************************************************************************** */ ++ ++#define FLD_AUD_DST_LN_LNGTH 0x00000FFF ++ ++#define FLD_AUD_DST_PK_MODE 0x00004000 ++ ++#define FLD_AUD_CLK_ENABLE 0x00000200 ++ ++#define FLD_AUD_MASTER_MODE 0x00000002 ++ ++#define FLD_AUD_SONY_MODE 0x00000001 ++ ++#define FLD_AUD_CLK_SELECT_PLL_D 0x00001800 ++ ++#define FLD_AUD_DST_ENABLE 0x00020000 ++ ++#define FLD_AUD_SRC_ENABLE 0x00010000 ++ ++/* ***************************************************************************** */ ++#define AUD_INT_DMA_CTL 0x140500 /* Audio Int DMA control */ ++ ++#define FLD_AUD_SRC_E_RISC_EN 0x00008000 ++#define FLD_AUD_SRC_C_RISC_EN 0x00004000 ++#define FLD_AUD_SRC_B_RISC_EN 0x00002000 ++#define FLD_AUD_SRC_A_RISC_EN 0x00001000 ++ ++#define FLD_AUD_DST_D_RISC_EN 0x00000800 ++#define FLD_AUD_DST_C_RISC_EN 0x00000400 ++#define FLD_AUD_DST_B_RISC_EN 0x00000200 ++#define FLD_AUD_DST_A_RISC_EN 0x00000100 ++ ++#define FLD_AUD_SRC_E_FIFO_EN 0x00000080 ++#define FLD_AUD_SRC_C_FIFO_EN 0x00000040 ++#define FLD_AUD_SRC_B_FIFO_EN 0x00000020 ++#define FLD_AUD_SRC_A_FIFO_EN 0x00000010 ++ ++#define FLD_AUD_DST_D_FIFO_EN 0x00000008 ++#define FLD_AUD_DST_C_FIFO_EN 0x00000004 ++#define FLD_AUD_DST_B_FIFO_EN 0x00000002 ++#define FLD_AUD_DST_A_FIFO_EN 0x00000001 ++ ++/* ***************************************************************************** */ ++/* */ ++/* Mobilygen Interface Registers */ ++/* */ ++/* ***************************************************************************** */ ++/* Mobilygen Interface A */ ++/* ***************************************************************************** */ ++#define MB_IF_A_DMA 0x150000 /* MBIF A DMA data port */ ++#define MB_IF_A_GPCN 0x150008 /* MBIF A GP counter */ ++#define MB_IF_A_GPCN_CTRL 0x15000C ++#define MB_IF_A_DMA_CTRL 0x150010 ++#define MB_IF_A_LENGTH 0x150014 ++#define MB_IF_A_HDMA_XFER_SZ 0x150018 ++#define MB_IF_A_HCMD 0x15001C ++#define MB_IF_A_HCONFIG 0x150020 ++#define MB_IF_A_DATA_STRUCT_0 0x150024 ++#define MB_IF_A_DATA_STRUCT_1 0x150028 ++#define MB_IF_A_DATA_STRUCT_2 0x15002C ++#define MB_IF_A_DATA_STRUCT_3 0x150030 ++#define MB_IF_A_DATA_STRUCT_4 0x150034 ++#define MB_IF_A_DATA_STRUCT_5 0x150038 ++#define MB_IF_A_DATA_STRUCT_6 0x15003C ++#define MB_IF_A_DATA_STRUCT_7 0x150040 ++#define MB_IF_A_DATA_STRUCT_8 0x150044 ++#define MB_IF_A_DATA_STRUCT_9 0x150048 ++#define MB_IF_A_DATA_STRUCT_A 0x15004C ++#define MB_IF_A_DATA_STRUCT_B 0x150050 ++#define MB_IF_A_DATA_STRUCT_C 0x150054 ++#define MB_IF_A_DATA_STRUCT_D 0x150058 ++#define MB_IF_A_DATA_STRUCT_E 0x15005C ++#define MB_IF_A_DATA_STRUCT_F 0x150060 ++/* ***************************************************************************** */ ++/* Mobilygen Interface B */ ++/* ***************************************************************************** */ ++#define MB_IF_B_DMA 0x160000 /* MBIF A DMA data port */ ++#define MB_IF_B_GPCN 0x160008 /* MBIF A GP counter */ ++#define MB_IF_B_GPCN_CTRL 0x16000C ++#define MB_IF_B_DMA_CTRL 0x160010 ++#define MB_IF_B_LENGTH 0x160014 ++#define MB_IF_B_HDMA_XFER_SZ 0x160018 ++#define MB_IF_B_HCMD 0x16001C ++#define MB_IF_B_HCONFIG 0x160020 ++#define MB_IF_B_DATA_STRUCT_0 0x160024 ++#define MB_IF_B_DATA_STRUCT_1 0x160028 ++#define MB_IF_B_DATA_STRUCT_2 0x16002C ++#define MB_IF_B_DATA_STRUCT_3 0x160030 ++#define MB_IF_B_DATA_STRUCT_4 0x160034 ++#define MB_IF_B_DATA_STRUCT_5 0x160038 ++#define MB_IF_B_DATA_STRUCT_6 0x16003C ++#define MB_IF_B_DATA_STRUCT_7 0x160040 ++#define MB_IF_B_DATA_STRUCT_8 0x160044 ++#define MB_IF_B_DATA_STRUCT_9 0x160048 ++#define MB_IF_B_DATA_STRUCT_A 0x16004C ++#define MB_IF_B_DATA_STRUCT_B 0x160050 ++#define MB_IF_B_DATA_STRUCT_C 0x160054 ++#define MB_IF_B_DATA_STRUCT_D 0x160058 ++#define MB_IF_B_DATA_STRUCT_E 0x16005C ++#define MB_IF_B_DATA_STRUCT_F 0x160060 ++ ++/* MB_DMA_CTRL */ ++#define FLD_MB_IF_RISC_EN 0x00000010 ++#define FLD_MB_IF_FIFO_EN 0x00000001 ++ ++/* MB_LENGTH */ ++#define FLD_MB_IF_LN_LNGTH 0x00000FFF ++ ++/* MB_HCMD register */ ++#define FLD_MB_HCMD_H_GO 0x80000000 ++#define FLD_MB_HCMD_H_BUSY 0x40000000 ++#define FLD_MB_HCMD_H_DMA_HOLD 0x10000000 ++#define FLD_MB_HCMD_H_DMA_BUSY 0x08000000 ++#define FLD_MB_HCMD_H_DMA_TYPE 0x04000000 ++#define FLD_MB_HCMD_H_DMA_XACT 0x02000000 ++#define FLD_MB_HCMD_H_RW_N 0x01000000 ++#define FLD_MB_HCMD_H_ADDR 0x00FF0000 ++#define FLD_MB_HCMD_H_DATA 0x0000FFFF ++ ++/* ***************************************************************************** */ ++/* I2C #1 */ ++/* ***************************************************************************** */ ++#define I2C1_ADDR 0x180000 /* I2C #1 address */ ++#define FLD_I2C_DADDR 0xfe000000 /* RW [31:25] I2C Device Address */ ++ /* RO [24] reserved */ ++/* ***************************************************************************** */ ++#define FLD_I2C_SADDR 0x00FFFFFF /* RW [23:0] I2C Sub-address */ ++ ++/* ***************************************************************************** */ ++#define I2C1_WDATA 0x180004 /* I2C #1 write data */ ++#define FLD_I2C_WDATA 0xFFFFFFFF /* RW [31:0] */ ++ ++/* ***************************************************************************** */ ++#define I2C1_CTRL 0x180008 /* I2C #1 control */ ++#define FLD_I2C_PERIOD 0xFF000000 /* RW [31:24] */ ++#define FLD_I2C_SCL_IN 0x00200000 /* RW [21] */ ++#define FLD_I2C_SDA_IN 0x00100000 /* RW [20] */ ++ /* RO [19:18] reserved */ ++#define FLD_I2C_SCL_OUT 0x00020000 /* RW [17] */ ++#define FLD_I2C_SDA_OUT 0x00010000 /* RW [16] */ ++ /* RO [15] reserved */ ++#define FLD_I2C_DATA_LEN 0x00007000 /* RW [14:12] */ ++#define FLD_I2C_SADDR_INC 0x00000800 /* RW [11] */ ++ /* RO [10:9] reserved */ ++#define FLD_I2C_SADDR_LEN 0x00000300 /* RW [9:8] */ ++ /* RO [7:6] reserved */ ++#define FLD_I2C_SOFT 0x00000020 /* RW [5] */ ++#define FLD_I2C_NOSTOP 0x00000010 /* RW [4] */ ++#define FLD_I2C_EXTEND 0x00000008 /* RW [3] */ ++#define FLD_I2C_SYNC 0x00000004 /* RW [2] */ ++#define FLD_I2C_READ_SA 0x00000002 /* RW [1] */ ++#define FLD_I2C_READ_WRN 0x00000001 /* RW [0] */ ++ ++/* ***************************************************************************** */ ++#define I2C1_RDATA 0x18000C /* I2C #1 read data */ ++#define FLD_I2C_RDATA 0xFFFFFFFF /* RO [31:0] */ ++ ++/* ***************************************************************************** */ ++#define I2C1_STAT 0x180010 /* I2C #1 status */ ++#define FLD_I2C_XFER_IN_PROG 0x00000002 /* RO [1] */ ++#define FLD_I2C_RACK 0x00000001 /* RO [0] */ ++ ++/* ***************************************************************************** */ ++/* I2C #2 */ ++/* ***************************************************************************** */ ++#define I2C2_ADDR 0x190000 /* I2C #2 address */ ++ ++/* ***************************************************************************** */ ++#define I2C2_WDATA 0x190004 /* I2C #2 write data */ ++ ++/* ***************************************************************************** */ ++#define I2C2_CTRL 0x190008 /* I2C #2 control */ ++ ++/* ***************************************************************************** */ ++#define I2C2_RDATA 0x19000C /* I2C #2 read data */ ++ ++/* ***************************************************************************** */ ++#define I2C2_STAT 0x190010 /* I2C #2 status */ ++ ++/* ***************************************************************************** */ ++/* I2C #3 */ ++/* ***************************************************************************** */ ++#define I2C3_ADDR 0x1A0000 /* I2C #3 address */ ++ ++/* ***************************************************************************** */ ++#define I2C3_WDATA 0x1A0004 /* I2C #3 write data */ ++ ++/* ***************************************************************************** */ ++#define I2C3_CTRL 0x1A0008 /* I2C #3 control */ ++ ++/* ***************************************************************************** */ ++#define I2C3_RDATA 0x1A000C /* I2C #3 read data */ ++ ++/* ***************************************************************************** */ ++#define I2C3_STAT 0x1A0010 /* I2C #3 status */ ++ ++/* ***************************************************************************** */ ++/* UART */ ++/* ***************************************************************************** */ ++#define UART_CTL 0x1B0000 /* UART Control Register */ ++#define FLD_LOOP_BACK_EN (1 << 7) /* RW field - default 0 */ ++#define FLD_RX_TRG_SZ (3 << 2) /* RW field - default 0 */ ++#define FLD_RX_EN (1 << 1) /* RW field - default 0 */ ++#define FLD_TX_EN (1 << 0) /* RW field - default 0 */ ++ ++/* ***************************************************************************** */ ++#define UART_BRD 0x1B0004 /* UART Baud Rate Divisor */ ++#define FLD_BRD 0x0000FFFF /* RW field - default 0x197 */ ++ ++/* ***************************************************************************** */ ++#define UART_DBUF 0x1B0008 /* UART Tx/Rx Data BuFFer */ ++#define FLD_DB 0xFFFFFFFF /* RW field - default 0 */ ++ ++/* ***************************************************************************** */ ++#define UART_ISR 0x1B000C /* UART Interrupt Status */ ++#define FLD_RXD_TIMEOUT_EN (1 << 7) /* RW field - default 0 */ ++#define FLD_FRM_ERR_EN (1 << 6) /* RW field - default 0 */ ++#define FLD_RXD_RDY_EN (1 << 5) /* RW field - default 0 */ ++#define FLD_TXD_EMPTY_EN (1 << 4) /* RW field - default 0 */ ++#define FLD_RXD_OVERFLOW (1 << 3) /* RW field - default 0 */ ++#define FLD_FRM_ERR (1 << 2) /* RW field - default 0 */ ++#define FLD_RXD_RDY (1 << 1) /* RW field - default 0 */ ++#define FLD_TXD_EMPTY (1 << 0) /* RW field - default 0 */ ++ ++/* ***************************************************************************** */ ++#define UART_CNT 0x1B0010 /* UART Tx/Rx FIFO Byte Count */ ++#define FLD_TXD_CNT (0x1F << 8) /* RW field - default 0 */ ++#define FLD_RXD_CNT (0x1F << 0) /* RW field - default 0 */ ++ ++/* ***************************************************************************** */ ++/* Motion Detection */ ++#define MD_CH0_GRID_BLOCK_YCNT 0x170014 ++#define MD_CH1_GRID_BLOCK_YCNT 0x170094 ++#define MD_CH2_GRID_BLOCK_YCNT 0x170114 ++#define MD_CH3_GRID_BLOCK_YCNT 0x170194 ++#define MD_CH4_GRID_BLOCK_YCNT 0x170214 ++#define MD_CH5_GRID_BLOCK_YCNT 0x170294 ++#define MD_CH6_GRID_BLOCK_YCNT 0x170314 ++#define MD_CH7_GRID_BLOCK_YCNT 0x170394 ++ ++#define PIXEL_FRMT_422 4 ++#define PIXEL_FRMT_411 5 ++#define PIXEL_FRMT_Y8 6 ++ ++#define PIXEL_ENGINE_VIP1 0 ++#define PIXEL_ENGINE_VIP2 1 ++ ++#endif /* Athena_REGISTERS */ +diff --git a/drivers/media/pci/cx25821/cx25821-sram.h b/drivers/media/pci/cx25821/cx25821-sram.h +new file mode 100644 +index 0000000..5f05d15 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-sram.h +@@ -0,0 +1,261 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ATHENA_SRAM_H__ ++#define __ATHENA_SRAM_H__ ++ ++/* #define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM */ ++#define VID_CMDS_SIZE 80 /* Video CMDS size in bytes */ ++#define AUDIO_CMDS_SIZE 80 /* AUDIO CMDS size in bytes */ ++#define MBIF_CMDS_SIZE 80 /* MBIF CMDS size in bytes */ ++ ++/* #define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers */ ++#define VID_IQ_SIZE 64 /* VID instruction queue size in bytes */ ++#define MBIF_IQ_SIZE 64 ++#define AUDIO_IQ_SIZE 64 /* AUD instruction queue size in bytes */ ++ ++#define VID_CDT_SIZE 64 /* VID cluster descriptor table size in bytes */ ++#define MBIF_CDT_SIZE 64 /* MBIF/HBI cluster descriptor table size in bytes */ ++#define AUDIO_CDT_SIZE 48 /* AUD cluster descriptor table size in bytes */ ++ ++/* #define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM */ ++/* #define RX_SRAM_END_SIZE = 0; // End of RX SRAM */ ++ ++/* #define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM */ ++/* #define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora */ ++ ++#define VID_CLUSTER_SIZE 1440 /* VID cluster data line */ ++#define AUDIO_CLUSTER_SIZE 128 /* AUDIO cluster data line */ ++#define MBIF_CLUSTER_SIZE 1440 /* MBIF/HBI cluster data line */ ++ ++/* #define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM */ ++/* #define TX_SRAM_END_SIZE = 0; // End of TX SRAM */ ++ ++/* Receive SRAM */ ++#define RX_SRAM_START 0x10000 ++#define VID_A_DOWN_CMDS 0x10000 ++#define VID_B_DOWN_CMDS 0x10050 ++#define VID_C_DOWN_CMDS 0x100A0 ++#define VID_D_DOWN_CMDS 0x100F0 ++#define VID_E_DOWN_CMDS 0x10140 ++#define VID_F_DOWN_CMDS 0x10190 ++#define VID_G_DOWN_CMDS 0x101E0 ++#define VID_H_DOWN_CMDS 0x10230 ++#define VID_A_UP_CMDS 0x10280 ++#define VID_B_UP_CMDS 0x102D0 ++#define VID_C_UP_CMDS 0x10320 ++#define VID_D_UP_CMDS 0x10370 ++#define VID_E_UP_CMDS 0x103C0 ++#define VID_F_UP_CMDS 0x10410 ++#define VID_I_UP_CMDS 0x10460 ++#define VID_J_UP_CMDS 0x104B0 ++#define AUD_A_DOWN_CMDS 0x10500 ++#define AUD_B_DOWN_CMDS 0x10550 ++#define AUD_C_DOWN_CMDS 0x105A0 ++#define AUD_D_DOWN_CMDS 0x105F0 ++#define AUD_A_UP_CMDS 0x10640 ++#define AUD_B_UP_CMDS 0x10690 ++#define AUD_C_UP_CMDS 0x106E0 ++#define AUD_E_UP_CMDS 0x10730 ++#define MBIF_A_DOWN_CMDS 0x10780 ++#define MBIF_B_DOWN_CMDS 0x107D0 ++#define DMA_SCRATCH_PAD 0x10820 /* Scratch pad area from 0x10820 to 0x10B40 */ ++ ++/* #define RX_SRAM_POOL_START = 0x105B0; */ ++ ++#define VID_A_IQ 0x11000 ++#define VID_B_IQ 0x11040 ++#define VID_C_IQ 0x11080 ++#define VID_D_IQ 0x110C0 ++#define VID_E_IQ 0x11100 ++#define VID_F_IQ 0x11140 ++#define VID_G_IQ 0x11180 ++#define VID_H_IQ 0x111C0 ++#define VID_I_IQ 0x11200 ++#define VID_J_IQ 0x11240 ++#define AUD_A_IQ 0x11280 ++#define AUD_B_IQ 0x112C0 ++#define AUD_C_IQ 0x11300 ++#define AUD_D_IQ 0x11340 ++#define AUD_E_IQ 0x11380 ++#define MBIF_A_IQ 0x11000 ++#define MBIF_B_IQ 0x110C0 ++ ++#define VID_A_CDT 0x10C00 ++#define VID_B_CDT 0x10C40 ++#define VID_C_CDT 0x10C80 ++#define VID_D_CDT 0x10CC0 ++#define VID_E_CDT 0x10D00 ++#define VID_F_CDT 0x10D40 ++#define VID_G_CDT 0x10D80 ++#define VID_H_CDT 0x10DC0 ++#define VID_I_CDT 0x10E00 ++#define VID_J_CDT 0x10E40 ++#define AUD_A_CDT 0x10E80 ++#define AUD_B_CDT 0x10EB0 ++#define AUD_C_CDT 0x10EE0 ++#define AUD_D_CDT 0x10F10 ++#define AUD_E_CDT 0x10F40 ++#define MBIF_A_CDT 0x10C00 ++#define MBIF_B_CDT 0x10CC0 ++ ++/* Cluster Buffer for RX */ ++#define VID_A_UP_CLUSTER_1 0x11400 ++#define VID_A_UP_CLUSTER_2 0x119A0 ++#define VID_A_UP_CLUSTER_3 0x11F40 ++#define VID_A_UP_CLUSTER_4 0x124E0 ++ ++#define VID_B_UP_CLUSTER_1 0x12A80 ++#define VID_B_UP_CLUSTER_2 0x13020 ++#define VID_B_UP_CLUSTER_3 0x135C0 ++#define VID_B_UP_CLUSTER_4 0x13B60 ++ ++#define VID_C_UP_CLUSTER_1 0x14100 ++#define VID_C_UP_CLUSTER_2 0x146A0 ++#define VID_C_UP_CLUSTER_3 0x14C40 ++#define VID_C_UP_CLUSTER_4 0x151E0 ++ ++#define VID_D_UP_CLUSTER_1 0x15780 ++#define VID_D_UP_CLUSTER_2 0x15D20 ++#define VID_D_UP_CLUSTER_3 0x162C0 ++#define VID_D_UP_CLUSTER_4 0x16860 ++ ++#define VID_E_UP_CLUSTER_1 0x16E00 ++#define VID_E_UP_CLUSTER_2 0x173A0 ++#define VID_E_UP_CLUSTER_3 0x17940 ++#define VID_E_UP_CLUSTER_4 0x17EE0 ++ ++#define VID_F_UP_CLUSTER_1 0x18480 ++#define VID_F_UP_CLUSTER_2 0x18A20 ++#define VID_F_UP_CLUSTER_3 0x18FC0 ++#define VID_F_UP_CLUSTER_4 0x19560 ++ ++#define VID_I_UP_CLUSTER_1 0x19B00 ++#define VID_I_UP_CLUSTER_2 0x1A0A0 ++#define VID_I_UP_CLUSTER_3 0x1A640 ++#define VID_I_UP_CLUSTER_4 0x1ABE0 ++ ++#define VID_J_UP_CLUSTER_1 0x1B180 ++#define VID_J_UP_CLUSTER_2 0x1B720 ++#define VID_J_UP_CLUSTER_3 0x1BCC0 ++#define VID_J_UP_CLUSTER_4 0x1C260 ++ ++#define AUD_A_UP_CLUSTER_1 0x1C800 ++#define AUD_A_UP_CLUSTER_2 0x1C880 ++#define AUD_A_UP_CLUSTER_3 0x1C900 ++ ++#define AUD_B_UP_CLUSTER_1 0x1C980 ++#define AUD_B_UP_CLUSTER_2 0x1CA00 ++#define AUD_B_UP_CLUSTER_3 0x1CA80 ++ ++#define AUD_C_UP_CLUSTER_1 0x1CB00 ++#define AUD_C_UP_CLUSTER_2 0x1CB80 ++#define AUD_C_UP_CLUSTER_3 0x1CC00 ++ ++#define AUD_E_UP_CLUSTER_1 0x1CC80 ++#define AUD_E_UP_CLUSTER_2 0x1CD00 ++#define AUD_E_UP_CLUSTER_3 0x1CD80 ++ ++#define RX_SRAM_POOL_FREE 0x1CE00 ++#define RX_SRAM_END 0x1D000 ++ ++/* Free Receive SRAM 144 Bytes */ ++ ++/* Transmit SRAM */ ++#define TX_SRAM_POOL_START 0x00000 ++ ++#define VID_A_DOWN_CLUSTER_1 0x00040 ++#define VID_A_DOWN_CLUSTER_2 0x005E0 ++#define VID_A_DOWN_CLUSTER_3 0x00B80 ++#define VID_A_DOWN_CLUSTER_4 0x01120 ++ ++#define VID_B_DOWN_CLUSTER_1 0x016C0 ++#define VID_B_DOWN_CLUSTER_2 0x01C60 ++#define VID_B_DOWN_CLUSTER_3 0x02200 ++#define VID_B_DOWN_CLUSTER_4 0x027A0 ++ ++#define VID_C_DOWN_CLUSTER_1 0x02D40 ++#define VID_C_DOWN_CLUSTER_2 0x032E0 ++#define VID_C_DOWN_CLUSTER_3 0x03880 ++#define VID_C_DOWN_CLUSTER_4 0x03E20 ++ ++#define VID_D_DOWN_CLUSTER_1 0x043C0 ++#define VID_D_DOWN_CLUSTER_2 0x04960 ++#define VID_D_DOWN_CLUSTER_3 0x04F00 ++#define VID_D_DOWN_CLUSTER_4 0x054A0 ++ ++#define VID_E_DOWN_CLUSTER_1 0x05a40 ++#define VID_E_DOWN_CLUSTER_2 0x05FE0 ++#define VID_E_DOWN_CLUSTER_3 0x06580 ++#define VID_E_DOWN_CLUSTER_4 0x06B20 ++ ++#define VID_F_DOWN_CLUSTER_1 0x070C0 ++#define VID_F_DOWN_CLUSTER_2 0x07660 ++#define VID_F_DOWN_CLUSTER_3 0x07C00 ++#define VID_F_DOWN_CLUSTER_4 0x081A0 ++ ++#define VID_G_DOWN_CLUSTER_1 0x08740 ++#define VID_G_DOWN_CLUSTER_2 0x08CE0 ++#define VID_G_DOWN_CLUSTER_3 0x09280 ++#define VID_G_DOWN_CLUSTER_4 0x09820 ++ ++#define VID_H_DOWN_CLUSTER_1 0x09DC0 ++#define VID_H_DOWN_CLUSTER_2 0x0A360 ++#define VID_H_DOWN_CLUSTER_3 0x0A900 ++#define VID_H_DOWN_CLUSTER_4 0x0AEA0 ++ ++#define AUD_A_DOWN_CLUSTER_1 0x0B500 ++#define AUD_A_DOWN_CLUSTER_2 0x0B580 ++#define AUD_A_DOWN_CLUSTER_3 0x0B600 ++ ++#define AUD_B_DOWN_CLUSTER_1 0x0B680 ++#define AUD_B_DOWN_CLUSTER_2 0x0B700 ++#define AUD_B_DOWN_CLUSTER_3 0x0B780 ++ ++#define AUD_C_DOWN_CLUSTER_1 0x0B800 ++#define AUD_C_DOWN_CLUSTER_2 0x0B880 ++#define AUD_C_DOWN_CLUSTER_3 0x0B900 ++ ++#define AUD_D_DOWN_CLUSTER_1 0x0B980 ++#define AUD_D_DOWN_CLUSTER_2 0x0BA00 ++#define AUD_D_DOWN_CLUSTER_3 0x0BA80 ++ ++#define TX_SRAM_POOL_FREE 0x0BB00 ++#define TX_SRAM_END 0x0C000 ++ ++#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2) ++#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3) ++#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4) ++ ++#define VID_IQ_SIZE_DW BYTES_TO_DWORDS(VID_IQ_SIZE) ++#define VID_CDT_SIZE_QW BYTES_TO_QWORDS(VID_CDT_SIZE) ++#define VID_CLUSTER_SIZE_OW BYTES_TO_OWORDS(VID_CLUSTER_SIZE) ++ ++#define AUDIO_IQ_SIZE_DW BYTES_TO_DWORDS(AUDIO_IQ_SIZE) ++#define AUDIO_CDT_SIZE_QW BYTES_TO_QWORDS(AUDIO_CDT_SIZE) ++#define AUDIO_CLUSTER_SIZE_QW BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE) ++ ++#define MBIF_IQ_SIZE_DW BYTES_TO_DWORDS(MBIF_IQ_SIZE) ++#define MBIF_CDT_SIZE_QW BYTES_TO_QWORDS(MBIF_CDT_SIZE) ++#define MBIF_CLUSTER_SIZE_OW BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE) ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c +new file mode 100644 +index 0000000..4413fa9 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.c +@@ -0,0 +1,801 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * 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. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821-video.h" ++#include "cx25821-video-upstream-ch2.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); ++MODULE_AUTHOR("Hiep Huynh "); ++MODULE_LICENSE("GPL"); ++ ++static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | ++ FLD_VID_SRC_OPC_ERR; ++ ++static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev, ++ __le32 *rp, unsigned int offset, ++ unsigned int bpl, u32 sync_line, ++ unsigned int lines, ++ int fifo_enable, int field_type) ++{ ++ unsigned int line, i; ++ int dist_betwn_starts = bpl * 2; ++ ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ if (USE_RISC_NOOP_VIDEO) { ++ for (i = 0; i < NUM_NO_OPS; i++) ++ *(rp++) = cpu_to_le32(RISC_NOOP); ++ } ++ ++ /* scan lines */ ++ for (line = 0; line < lines; line++) { ++ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); ++ *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if ((lines <= NTSC_FIELD_HEIGHT) || ++ (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) { ++ offset += dist_betwn_starts; ++ } ++ } ++ ++ return rp; ++} ++ ++static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, ++ __le32 *rp, ++ dma_addr_t databuf_phys_addr, ++ unsigned int offset, ++ u32 sync_line, unsigned int bpl, ++ unsigned int lines, ++ int fifo_enable, int field_type) ++{ ++ unsigned int line, i; ++ struct sram_channel *sram_ch = ++ dev->channels[dev->_channel2_upstream_select].sram_channels; ++ int dist_betwn_starts = bpl * 2; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ if (USE_RISC_NOOP_VIDEO) { ++ for (i = 0; i < NUM_NO_OPS; i++) ++ *(rp++) = cpu_to_le32(RISC_NOOP); ++ } ++ ++ /* scan lines */ ++ for (line = 0; line < lines; line++) { ++ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); ++ *(rp++) = cpu_to_le32(databuf_phys_addr + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if ((lines <= NTSC_FIELD_HEIGHT) || ++ (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC_ch2)) { ++ offset += dist_betwn_starts; ++ } ++ ++ /* ++ check if we need to enable the FIFO after the first 4 lines ++ For the upstream video channel, the risc engine will enable ++ the FIFO. ++ */ ++ if (fifo_enable && line == 3) { ++ *(rp++) = RISC_WRITECR; ++ *(rp++) = sram_ch->dma_ctl; ++ *(rp++) = FLD_VID_FIFO_EN; ++ *(rp++) = 0x00000001; ++ } ++ } ++ ++ return rp; ++} ++ ++static int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, ++ struct pci_dev *pci, ++ unsigned int top_offset, ++ unsigned int bpl, ++ unsigned int lines) ++{ ++ __le32 *rp; ++ int fifo_enable = 0; ++ int singlefield_lines = lines >> 1; /*get line count for single field */ ++ int odd_num_lines = singlefield_lines; ++ int frame = 0; ++ int frame_size = 0; ++ int databuf_offset = 0; ++ int risc_program_size = 0; ++ int risc_flag = RISC_CNT_RESET; ++ unsigned int bottom_offset = bpl; ++ dma_addr_t risc_phys_jump_addr; ++ ++ if (dev->_isNTSC_ch2) { ++ odd_num_lines = singlefield_lines + 1; ++ risc_program_size = FRAME1_VID_PROG_SIZE; ++ if (bpl == Y411_LINE_SZ) ++ frame_size = FRAME_SIZE_NTSC_Y411; ++ else ++ frame_size = FRAME_SIZE_NTSC_Y422; ++ } else { ++ risc_program_size = PAL_VID_PROG_SIZE; ++ if (bpl == Y411_LINE_SZ) ++ frame_size = FRAME_SIZE_PAL_Y411; ++ else ++ frame_size = FRAME_SIZE_PAL_Y422; ++ } ++ ++ /* Virtual address of Risc buffer program */ ++ rp = dev->_dma_virt_addr_ch2; ++ ++ for (frame = 0; frame < NUM_FRAMES; frame++) { ++ databuf_offset = frame_size * frame; ++ ++ if (UNSET != top_offset) { ++ fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; ++ rp = cx25821_risc_field_upstream_ch2(dev, rp, ++ dev->_data_buf_phys_addr_ch2 + databuf_offset, ++ top_offset, 0, bpl, odd_num_lines, fifo_enable, ++ ODD_FIELD); ++ } ++ ++ fifo_enable = FIFO_DISABLE; ++ ++ /* Even field */ ++ rp = cx25821_risc_field_upstream_ch2(dev, rp, ++ dev->_data_buf_phys_addr_ch2 + databuf_offset, ++ bottom_offset, 0x200, bpl, singlefield_lines, ++ fifo_enable, EVEN_FIELD); ++ ++ if (frame == 0) { ++ risc_flag = RISC_CNT_RESET; ++ risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2 + ++ risc_program_size; ++ } else { ++ risc_flag = RISC_CNT_INC; ++ risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2; ++ } ++ ++ /* ++ * Loop to 2ndFrameRISC or to Start of ++ * Risc program & generate IRQ ++ */ ++ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ } ++ ++ return 0; ++} ++ ++void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) ++{ ++ struct sram_channel *sram_ch = ++ dev->channels[VID_UPSTREAM_SRAM_CHANNEL_J].sram_channels; ++ u32 tmp = 0; ++ ++ if (!dev->_is_running_ch2) { ++ pr_info("No video file is currently running so return!\n"); ++ return; ++ } ++ /* Disable RISC interrupts */ ++ tmp = cx_read(sram_ch->int_msk); ++ cx_write(sram_ch->int_msk, tmp & ~_intr_msk); ++ ++ /* Turn OFF risc and fifo */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); ++ ++ /* Clear data buffer memory */ ++ if (dev->_data_buf_virt_addr_ch2) ++ memset(dev->_data_buf_virt_addr_ch2, 0, ++ dev->_data_buf_size_ch2); ++ ++ dev->_is_running_ch2 = 0; ++ dev->_is_first_frame_ch2 = 0; ++ dev->_frame_count_ch2 = 0; ++ dev->_file_status_ch2 = END_OF_FILE; ++ ++ kfree(dev->_irq_queues_ch2); ++ dev->_irq_queues_ch2 = NULL; ++ ++ kfree(dev->_filename_ch2); ++ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); ++} ++ ++void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev) ++{ ++ if (dev->_is_running_ch2) ++ cx25821_stop_upstream_video_ch2(dev); ++ ++ if (dev->_dma_virt_addr_ch2) { ++ pci_free_consistent(dev->pci, dev->_risc_size_ch2, ++ dev->_dma_virt_addr_ch2, ++ dev->_dma_phys_addr_ch2); ++ dev->_dma_virt_addr_ch2 = NULL; ++ } ++ ++ if (dev->_data_buf_virt_addr_ch2) { ++ pci_free_consistent(dev->pci, dev->_data_buf_size_ch2, ++ dev->_data_buf_virt_addr_ch2, ++ dev->_data_buf_phys_addr_ch2); ++ dev->_data_buf_virt_addr_ch2 = NULL; ++ } ++} ++ ++static int cx25821_get_frame_ch2(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int frame_index_temp = dev->_frame_index_ch2; ++ int i = 0; ++ int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? ++ Y411_LINE_SZ : Y422_LINE_SZ; ++ int frame_size = 0; ++ int frame_offset = 0; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t file_offset; ++ loff_t pos; ++ mm_segment_t old_fs; ++ ++ if (dev->_file_status_ch2 == END_OF_FILE) ++ return 0; ++ ++ if (dev->_isNTSC_ch2) { ++ frame_size = (line_size == Y411_LINE_SZ) ? ++ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; ++ } else { ++ frame_size = (line_size == Y411_LINE_SZ) ? ++ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; ++ } ++ ++ frame_offset = (frame_index_temp > 0) ? frame_size : 0; ++ file_offset = dev->_frame_count_ch2 * frame_size; ++ ++ myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_filename_ch2, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (i = 0; i < dev->_lines_count_ch2; i++) { ++ pos = file_offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, line_size, ++ &pos); ++ ++ if (vfs_read_retval > 0 && vfs_read_retval == line_size ++ && dev->_data_buf_virt_addr_ch2 != NULL) { ++ memcpy((void *)(dev->_data_buf_virt_addr_ch2 + ++ frame_offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ file_offset += vfs_read_retval; ++ frame_offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Video file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_frame_count_ch2++; ++ ++ dev->_file_status_ch2 = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static void cx25821_vidups_handler_ch2(struct work_struct *work) ++{ ++ struct cx25821_dev *dev = container_of(work, struct cx25821_dev, ++ _irq_work_entry_ch2); ++ ++ if (!dev) { ++ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", ++ __func__); ++ return; ++ } ++ ++ cx25821_get_frame_ch2(dev, dev->channels[dev-> ++ _channel2_upstream_select].sram_channels); ++} ++ ++static int cx25821_openfile_ch2(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int i = 0, j = 0; ++ int line_size = (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ? ++ Y411_LINE_SZ : Y422_LINE_SZ; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t pos; ++ loff_t offset = (unsigned long)0; ++ mm_segment_t old_fs; ++ ++ myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); ++ ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_filename_ch2, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered! Returning\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (j = 0; j < NUM_FRAMES; j++) { ++ for (i = 0; i < dev->_lines_count_ch2; i++) { ++ pos = offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, ++ line_size, &pos); ++ ++ if (vfs_read_retval > 0 && ++ vfs_read_retval == line_size && ++ dev->_data_buf_virt_addr_ch2 != NULL) { ++ memcpy((void *)(dev-> ++ _data_buf_virt_addr_ch2 ++ + offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Video file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_frame_count_ch2++; ++ ++ if (vfs_read_retval < line_size) ++ break; ++ } ++ ++ dev->_file_status_ch2 = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ myfile->f_pos = 0; ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch, ++ int bpl) ++{ ++ int ret = 0; ++ dma_addr_t dma_addr; ++ dma_addr_t data_dma_addr; ++ ++ if (dev->_dma_virt_addr_ch2 != NULL) { ++ pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, ++ dev->_dma_virt_addr_ch2, ++ dev->_dma_phys_addr_ch2); ++ } ++ ++ dev->_dma_virt_addr_ch2 = pci_alloc_consistent(dev->pci, ++ dev->upstream_riscbuf_size_ch2, &dma_addr); ++ dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2; ++ dev->_dma_phys_start_addr_ch2 = dma_addr; ++ dev->_dma_phys_addr_ch2 = dma_addr; ++ dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2; ++ ++ if (!dev->_dma_virt_addr_ch2) { ++ pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); ++ return -ENOMEM; ++ } ++ ++ /* Iniitize at this address until n bytes to 0 */ ++ memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2); ++ ++ if (dev->_data_buf_virt_addr_ch2 != NULL) { ++ pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2, ++ dev->_data_buf_virt_addr_ch2, ++ dev->_data_buf_phys_addr_ch2); ++ } ++ /* For Video Data buffer allocation */ ++ dev->_data_buf_virt_addr_ch2 = pci_alloc_consistent(dev->pci, ++ dev->upstream_databuf_size_ch2, &data_dma_addr); ++ dev->_data_buf_phys_addr_ch2 = data_dma_addr; ++ dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2; ++ ++ if (!dev->_data_buf_virt_addr_ch2) { ++ pr_err("FAILED to allocate memory for data buffer! Returning\n"); ++ return -ENOMEM; ++ } ++ ++ /* Initialize at this address until n bytes to 0 */ ++ memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); ++ ++ ret = cx25821_openfile_ch2(dev, sram_ch); ++ if (ret < 0) ++ return ret; ++ ++ /* Creating RISC programs */ ++ ret = cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, ++ dev->_lines_count_ch2); ++ if (ret < 0) { ++ pr_info("Failed creating Video Upstream Risc programs!\n"); ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ return ret; ++} ++ ++static int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, ++ int chan_num, ++ u32 status) ++{ ++ u32 int_msk_tmp; ++ struct sram_channel *channel = dev->channels[chan_num].sram_channels; ++ int singlefield_lines = NTSC_FIELD_HEIGHT; ++ int line_size_in_bytes = Y422_LINE_SZ; ++ int odd_risc_prog_size = 0; ++ dma_addr_t risc_phys_jump_addr; ++ __le32 *rp; ++ ++ if (status & FLD_VID_SRC_RISC1) { ++ /* We should only process one program per call */ ++ u32 prog_cnt = cx_read(channel->gpcnt); ++ ++ /* ++ * Since we've identified our IRQ, clear our bits from the ++ * interrupt mask and interrupt status registers ++ */ ++ int_msk_tmp = cx_read(channel->int_msk); ++ cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); ++ cx_write(channel->int_stat, _intr_msk); ++ ++ spin_lock(&dev->slock); ++ ++ dev->_frame_index_ch2 = prog_cnt; ++ ++ queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2); ++ ++ if (dev->_is_first_frame_ch2) { ++ dev->_is_first_frame_ch2 = 0; ++ ++ if (dev->_isNTSC_ch2) { ++ singlefield_lines += 1; ++ odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; ++ } else { ++ singlefield_lines = PAL_FIELD_HEIGHT; ++ odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; ++ } ++ ++ if (dev->_dma_virt_start_addr_ch2 != NULL) { ++ if (dev->_pixel_format_ch2 == PIXEL_FRMT_411) ++ line_size_in_bytes = Y411_LINE_SZ; ++ else ++ line_size_in_bytes = Y422_LINE_SZ; ++ risc_phys_jump_addr = ++ dev->_dma_phys_start_addr_ch2 + ++ odd_risc_prog_size; ++ ++ rp = cx25821_update_riscprogram_ch2(dev, ++ dev->_dma_virt_start_addr_ch2, ++ TOP_OFFSET, line_size_in_bytes, ++ 0x0, singlefield_lines, ++ FIFO_DISABLE, ODD_FIELD); ++ ++ /* Jump to Even Risc program of 1st Frame */ ++ *(rp++) = cpu_to_le32(RISC_JUMP); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ } ++ } ++ ++ spin_unlock(&dev->slock); ++ } ++ ++ if (dev->_file_status_ch2 == END_OF_FILE) { ++ pr_info("EOF Channel 2 Framecount = %d\n", ++ dev->_frame_count_ch2); ++ return -1; ++ } ++ /* ElSE, set the interrupt mask register, re-enable irq. */ ++ int_msk_tmp = cx_read(channel->int_msk); ++ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); ++ ++ return 0; ++} ++ ++static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) ++{ ++ struct cx25821_dev *dev = dev_id; ++ u32 vid_status; ++ int handled = 0; ++ int channel_num = 0; ++ struct sram_channel *sram_ch; ++ ++ if (!dev) ++ return -1; ++ ++ channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; ++ sram_ch = dev->channels[channel_num].sram_channels; ++ ++ vid_status = cx_read(sram_ch->int_stat); ++ ++ /* Only deal with our interrupt */ ++ if (vid_status) ++ handled = cx25821_video_upstream_irq_ch2(dev, channel_num, ++ vid_status); ++ ++ if (handled < 0) ++ cx25821_stop_upstream_video_ch2(dev); ++ else ++ handled += handled; ++ ++ return IRQ_RETVAL(handled); ++} ++ ++static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, ++ struct sram_channel *ch, int pix_format) ++{ ++ int width = WIDTH_D1; ++ int height = dev->_lines_count_ch2; ++ int num_lines, odd_num_lines; ++ u32 value; ++ int vip_mode = PIXEL_ENGINE_VIP1; ++ ++ value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); ++ value &= 0xFFFFFFEF; ++ value |= dev->_isNTSC_ch2 ? 0 : 0x10; ++ cx_write(ch->vid_fmt_ctl, value); ++ ++ /* ++ * set number of active pixels in each line. Default is 720 ++ * pixels in both NTSC and PAL format ++ */ ++ cx_write(ch->vid_active_ctl1, width); ++ ++ num_lines = (height / 2) & 0x3FF; ++ odd_num_lines = num_lines; ++ ++ if (dev->_isNTSC_ch2) ++ odd_num_lines += 1; ++ ++ value = (num_lines << 16) | odd_num_lines; ++ ++ /* set number of active lines in field 0 (top) and field 1 (bottom) */ ++ cx_write(ch->vid_active_ctl2, value); ++ ++ cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); ++} ++ ++static int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ u32 tmp = 0; ++ int err = 0; ++ ++ /* ++ * 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface ++ * for channel A-C ++ */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); ++ ++ /* ++ * Set the physical start address of the RISC program in the initial ++ * program counter(IPC) member of the cmds. ++ */ ++ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2); ++ cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ ++ ++ /* reset counter */ ++ cx_write(sram_ch->gpcnt_ctl, 3); ++ ++ /* Clear our bits from the interrupt status register. */ ++ cx_write(sram_ch->int_stat, _intr_msk); ++ ++ /* Set the interrupt mask register, enable irq. */ ++ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); ++ tmp = cx_read(sram_ch->int_msk); ++ cx_write(sram_ch->int_msk, tmp |= _intr_msk); ++ ++ err = request_irq(dev->pci->irq, cx25821_upstream_irq_ch2, ++ IRQF_SHARED, dev->name, dev); ++ if (err < 0) { ++ pr_err("%s: can't get upstream IRQ %d\n", ++ dev->name, dev->pci->irq); ++ goto fail_irq; ++ } ++ /* Start the DMA engine */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); ++ ++ dev->_is_running_ch2 = 1; ++ dev->_is_first_frame_ch2 = 1; ++ ++ return 0; ++ ++fail_irq: ++ cx25821_dev_unregister(dev); ++ return err; ++} ++ ++int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, ++ int pixel_format) ++{ ++ struct sram_channel *sram_ch; ++ u32 tmp; ++ int err = 0; ++ int data_frame_size = 0; ++ int risc_buffer_size = 0; ++ ++ if (dev->_is_running_ch2) { ++ pr_info("Video Channel is still running so return!\n"); ++ return 0; ++ } ++ ++ dev->_channel2_upstream_select = channel_select; ++ sram_ch = dev->channels[channel_select].sram_channels; ++ ++ INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2); ++ dev->_irq_queues_ch2 = ++ create_singlethread_workqueue("cx25821_workqueue2"); ++ ++ if (!dev->_irq_queues_ch2) { ++ pr_err("create_singlethread_workqueue() for Video FAILED!\n"); ++ return -ENOMEM; ++ } ++ /* ++ * 656/VIP SRC Upstream Channel I & J and 7 - ++ * Host Bus Interface for channel A-C ++ */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); ++ ++ dev->_is_running_ch2 = 0; ++ dev->_frame_count_ch2 = 0; ++ dev->_file_status_ch2 = RESET_STATUS; ++ dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576; ++ dev->_pixel_format_ch2 = pixel_format; ++ dev->_line_size_ch2 = (dev->_pixel_format_ch2 == PIXEL_FRMT_422) ? ++ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; ++ data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; ++ risc_buffer_size = dev->_isNTSC_ch2 ? ++ NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; ++ ++ if (dev->input_filename_ch2) ++ dev->_filename_ch2 = kstrdup(dev->input_filename_ch2, ++ GFP_KERNEL); ++ else ++ dev->_filename_ch2 = kstrdup(dev->_defaultname_ch2, ++ GFP_KERNEL); ++ ++ if (!dev->_filename_ch2) { ++ err = -ENOENT; ++ goto error; ++ } ++ ++ /* Default if filename is empty string */ ++ if (strcmp(dev->_filename_ch2, "") == 0) { ++ if (dev->_isNTSC_ch2) { ++ dev->_filename_ch2 = (dev->_pixel_format_ch2 == ++ PIXEL_FRMT_411) ? "/root/vid411.yuv" : ++ "/root/vidtest.yuv"; ++ } else { ++ dev->_filename_ch2 = (dev->_pixel_format_ch2 == ++ PIXEL_FRMT_411) ? "/root/pal411.yuv" : ++ "/root/pal422.yuv"; ++ } ++ } ++ ++ err = cx25821_sram_channel_setup_upstream(dev, sram_ch, ++ dev->_line_size_ch2, 0); ++ ++ /* setup fifo + format */ ++ cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2); ++ ++ dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2; ++ dev->upstream_databuf_size_ch2 = data_frame_size * 2; ++ ++ /* Allocating buffers and prepare RISC program */ ++ err = cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, ++ dev->_line_size_ch2); ++ if (err < 0) { ++ pr_err("%s: Failed to set up Video upstream buffers!\n", ++ dev->name); ++ goto error; ++ } ++ ++ cx25821_start_video_dma_upstream_ch2(dev, sram_ch); ++ ++ return 0; ++ ++error: ++ cx25821_dev_unregister(dev); ++ ++ return err; ++} +diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h +new file mode 100644 +index 0000000..d42dab5 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video-upstream-ch2.h +@@ -0,0 +1,138 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#define OPEN_FILE_1 0 ++#define NUM_PROGS 8 ++#define NUM_FRAMES 2 ++#define ODD_FIELD 0 ++#define EVEN_FIELD 1 ++#define TOP_OFFSET 0 ++#define FIFO_DISABLE 0 ++#define FIFO_ENABLE 1 ++#define TEST_FRAMES 5 ++#define END_OF_FILE 0 ++#define IN_PROGRESS 1 ++#define RESET_STATUS -1 ++#define NUM_NO_OPS 5 ++ ++/* PAL and NTSC line sizes and number of lines. */ ++#define WIDTH_D1 720 ++#define NTSC_LINES_PER_FRAME 480 ++#define PAL_LINES_PER_FRAME 576 ++#define PAL_LINE_SZ 1440 ++#define Y422_LINE_SZ 1440 ++#define Y411_LINE_SZ 1080 ++#define NTSC_FIELD_HEIGHT 240 ++#define NTSC_ODD_FLD_LINES 241 ++#define PAL_FIELD_HEIGHT 288 ++ ++#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) ++#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) ++#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) ++#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) ++ ++#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) ++#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) ++ ++#define RISC_WRITECR_INSTRUCTION_SIZE 16 ++#define RISC_SYNC_INSTRUCTION_SIZE 4 ++#define JUMP_INSTRUCTION_SIZE 12 ++#define MAXSIZE_NO_OPS 36 ++#define DWORD_SIZE 4 ++ ++#define USE_RISC_NOOP_VIDEO 1 ++ ++#ifdef USE_RISC_NOOP_VIDEO ++#define PAL_US_VID_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) ++ ++#define PAL_VID_PROG_SIZE \ ++ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) ++ ++#define ODD_FLD_PAL_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define NTSC_US_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define NTSC_RISC_BUF_SIZE \ ++ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) ++ ++#define FRAME1_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ ++ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ ++ 2 * NUM_NO_OPS * DWORD_SIZE) ++ ++#define ODD_FLD_NTSC_PROG_SIZE \ ++ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++#endif ++ ++#ifndef USE_RISC_NOOP_VIDEO ++#define PAL_US_VID_PROG_SIZE \ ++ ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE) ++ ++#define PAL_RISC_BUF_SIZE \ ++ (2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE)) ++ ++#define PAL_VID_PROG_SIZE \ ++ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE) ++ ++#define ODD_FLD_PAL_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) ++ ++#define ODD_FLD_NTSC_PROG_SIZE \ ++ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) ++ ++#define NTSC_US_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) ++ ++#define NTSC_RISC_BUF_SIZE \ ++ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) ++ ++#define FRAME1_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * \ ++ 3 * DWORD_SIZE + 2 * RISC_SYNC_INSTRUCTION_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.c b/drivers/media/pci/cx25821/cx25821-video-upstream.c +new file mode 100644 +index 0000000..46fd910 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video-upstream.c +@@ -0,0 +1,864 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * 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. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821-video.h" ++#include "cx25821-video-upstream.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); ++MODULE_AUTHOR("Hiep Huynh "); ++MODULE_LICENSE("GPL"); ++ ++static int _intr_msk = FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | ++ FLD_VID_SRC_OPC_ERR; ++ ++int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i, lines; ++ u32 cdt; ++ ++ if (ch->cmds_start == 0) { ++ cx_write(ch->ptr1_reg, 0); ++ cx_write(ch->ptr2_reg, 0); ++ cx_write(ch->cnt2_reg, 0); ++ cx_write(ch->cnt1_reg, 0); ++ return 0; ++ } ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ ++ if (lines > 4) ++ lines = 4; ++ ++ BUG_ON(lines < 2); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) { ++ cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); ++ cx_write(cdt + 16 * i + 4, 0); ++ cx_write(cdt + 16 * i + 8, 0); ++ cx_write(cdt + 16 * i + 12, 0); ++ } ++ ++ /* write CMDS */ ++ cx_write(ch->cmds_start + 0, risc); ++ ++ cx_write(ch->cmds_start + 4, 0); ++ cx_write(ch->cmds_start + 8, cdt); ++ cx_write(ch->cmds_start + 12, (lines * 16) >> 3); ++ cx_write(ch->cmds_start + 16, ch->ctrl_start); ++ ++ cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW); ++ ++ for (i = 24; i < 80; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt2_reg, (lines * 16) >> 3); ++ cx_write(ch->cnt1_reg, (bpl >> 3) - 1); ++ ++ return 0; ++} ++ ++static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, ++ __le32 *rp, unsigned int offset, ++ unsigned int bpl, u32 sync_line, ++ unsigned int lines, int fifo_enable, ++ int field_type) ++{ ++ unsigned int line, i; ++ int dist_betwn_starts = bpl * 2; ++ ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ if (USE_RISC_NOOP_VIDEO) { ++ for (i = 0; i < NUM_NO_OPS; i++) ++ *(rp++) = cpu_to_le32(RISC_NOOP); ++ } ++ ++ /* scan lines */ ++ for (line = 0; line < lines; line++) { ++ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); ++ *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if ((lines <= NTSC_FIELD_HEIGHT) ++ || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { ++ offset += dist_betwn_starts; ++ } ++ } ++ ++ return rp; ++} ++ ++static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, ++ dma_addr_t databuf_phys_addr, ++ unsigned int offset, u32 sync_line, ++ unsigned int bpl, unsigned int lines, ++ int fifo_enable, int field_type) ++{ ++ unsigned int line, i; ++ struct sram_channel *sram_ch = ++ dev->channels[dev->_channel_upstream_select].sram_channels; ++ int dist_betwn_starts = bpl * 2; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ if (USE_RISC_NOOP_VIDEO) { ++ for (i = 0; i < NUM_NO_OPS; i++) ++ *(rp++) = cpu_to_le32(RISC_NOOP); ++ } ++ ++ /* scan lines */ ++ for (line = 0; line < lines; line++) { ++ *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); ++ *(rp++) = cpu_to_le32(databuf_phys_addr + offset); ++ *(rp++) = cpu_to_le32(0); /* bits 63-32 */ ++ ++ if ((lines <= NTSC_FIELD_HEIGHT) ++ || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) ++ /* to skip the other field line */ ++ offset += dist_betwn_starts; ++ ++ /* check if we need to enable the FIFO after the first 4 lines ++ * For the upstream video channel, the risc engine will enable ++ * the FIFO. */ ++ if (fifo_enable && line == 3) { ++ *(rp++) = RISC_WRITECR; ++ *(rp++) = sram_ch->dma_ctl; ++ *(rp++) = FLD_VID_FIFO_EN; ++ *(rp++) = 0x00000001; ++ } ++ } ++ ++ return rp; ++} ++ ++static int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, ++ struct pci_dev *pci, ++ unsigned int top_offset, ++ unsigned int bpl, unsigned int lines) ++{ ++ __le32 *rp; ++ int fifo_enable = 0; ++ /* get line count for single field */ ++ int singlefield_lines = lines >> 1; ++ int odd_num_lines = singlefield_lines; ++ int frame = 0; ++ int frame_size = 0; ++ int databuf_offset = 0; ++ int risc_program_size = 0; ++ int risc_flag = RISC_CNT_RESET; ++ unsigned int bottom_offset = bpl; ++ dma_addr_t risc_phys_jump_addr; ++ ++ if (dev->_isNTSC) { ++ odd_num_lines = singlefield_lines + 1; ++ risc_program_size = FRAME1_VID_PROG_SIZE; ++ frame_size = (bpl == Y411_LINE_SZ) ? ++ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; ++ } else { ++ risc_program_size = PAL_VID_PROG_SIZE; ++ frame_size = (bpl == Y411_LINE_SZ) ? ++ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; ++ } ++ ++ /* Virtual address of Risc buffer program */ ++ rp = dev->_dma_virt_addr; ++ ++ for (frame = 0; frame < NUM_FRAMES; frame++) { ++ databuf_offset = frame_size * frame; ++ ++ if (UNSET != top_offset) { ++ fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; ++ rp = cx25821_risc_field_upstream(dev, rp, ++ dev->_data_buf_phys_addr + ++ databuf_offset, top_offset, 0, bpl, ++ odd_num_lines, fifo_enable, ODD_FIELD); ++ } ++ ++ fifo_enable = FIFO_DISABLE; ++ ++ /* Even Field */ ++ rp = cx25821_risc_field_upstream(dev, rp, ++ dev->_data_buf_phys_addr + ++ databuf_offset, bottom_offset, ++ 0x200, bpl, singlefield_lines, ++ fifo_enable, EVEN_FIELD); ++ ++ if (frame == 0) { ++ risc_flag = RISC_CNT_RESET; ++ risc_phys_jump_addr = dev->_dma_phys_start_addr + ++ risc_program_size; ++ } else { ++ risc_phys_jump_addr = dev->_dma_phys_start_addr; ++ risc_flag = RISC_CNT_INC; ++ } ++ ++ /* Loop to 2ndFrameRISC or to Start of Risc ++ * program & generate IRQ ++ */ ++ *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ } ++ ++ return 0; ++} ++ ++void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) ++{ ++ struct sram_channel *sram_ch = ++ dev->channels[VID_UPSTREAM_SRAM_CHANNEL_I].sram_channels; ++ u32 tmp = 0; ++ ++ if (!dev->_is_running) { ++ pr_info("No video file is currently running so return!\n"); ++ return; ++ } ++ /* Disable RISC interrupts */ ++ tmp = cx_read(sram_ch->int_msk); ++ cx_write(sram_ch->int_msk, tmp & ~_intr_msk); ++ ++ /* Turn OFF risc and fifo enable */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); ++ ++ /* Clear data buffer memory */ ++ if (dev->_data_buf_virt_addr) ++ memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); ++ ++ dev->_is_running = 0; ++ dev->_is_first_frame = 0; ++ dev->_frame_count = 0; ++ dev->_file_status = END_OF_FILE; ++ ++ kfree(dev->_irq_queues); ++ dev->_irq_queues = NULL; ++ ++ kfree(dev->_filename); ++ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); ++} ++ ++void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev) ++{ ++ if (dev->_is_running) ++ cx25821_stop_upstream_video_ch1(dev); ++ ++ if (dev->_dma_virt_addr) { ++ pci_free_consistent(dev->pci, dev->_risc_size, ++ dev->_dma_virt_addr, dev->_dma_phys_addr); ++ dev->_dma_virt_addr = NULL; ++ } ++ ++ if (dev->_data_buf_virt_addr) { ++ pci_free_consistent(dev->pci, dev->_data_buf_size, ++ dev->_data_buf_virt_addr, ++ dev->_data_buf_phys_addr); ++ dev->_data_buf_virt_addr = NULL; ++ } ++} ++ ++static int cx25821_get_frame(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int frame_index_temp = dev->_frame_index; ++ int i = 0; ++ int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? ++ Y411_LINE_SZ : Y422_LINE_SZ; ++ int frame_size = 0; ++ int frame_offset = 0; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t file_offset; ++ loff_t pos; ++ mm_segment_t old_fs; ++ ++ if (dev->_file_status == END_OF_FILE) ++ return 0; ++ ++ if (dev->_isNTSC) ++ frame_size = (line_size == Y411_LINE_SZ) ? ++ FRAME_SIZE_NTSC_Y411 : FRAME_SIZE_NTSC_Y422; ++ else ++ frame_size = (line_size == Y411_LINE_SZ) ? ++ FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; ++ ++ frame_offset = (frame_index_temp > 0) ? frame_size : 0; ++ file_offset = dev->_frame_count * frame_size; ++ ++ myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); ++ ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_filename, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (i = 0; i < dev->_lines_count; i++) { ++ pos = file_offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, line_size, ++ &pos); ++ ++ if (vfs_read_retval > 0 && vfs_read_retval == line_size ++ && dev->_data_buf_virt_addr != NULL) { ++ memcpy((void *)(dev->_data_buf_virt_addr + ++ frame_offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ file_offset += vfs_read_retval; ++ frame_offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Video file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_frame_count++; ++ ++ dev->_file_status = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static void cx25821_vidups_handler(struct work_struct *work) ++{ ++ struct cx25821_dev *dev = container_of(work, struct cx25821_dev, ++ _irq_work_entry); ++ ++ if (!dev) { ++ pr_err("ERROR %s(): since container_of(work_struct) FAILED!\n", ++ __func__); ++ return; ++ } ++ ++ cx25821_get_frame(dev, dev->channels[dev->_channel_upstream_select]. ++ sram_channels); ++} ++ ++static int cx25821_openfile(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ struct file *myfile; ++ int i = 0, j = 0; ++ int line_size = (dev->_pixel_format == PIXEL_FRMT_411) ? ++ Y411_LINE_SZ : Y422_LINE_SZ; ++ ssize_t vfs_read_retval = 0; ++ char mybuf[line_size]; ++ loff_t pos; ++ loff_t offset = (unsigned long)0; ++ mm_segment_t old_fs; ++ ++ myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); ++ ++ if (IS_ERR(myfile)) { ++ const int open_errno = -PTR_ERR(myfile); ++ pr_err("%s(): ERROR opening file(%s) with errno = %d!\n", ++ __func__, dev->_filename, open_errno); ++ return PTR_ERR(myfile); ++ } else { ++ if (!(myfile->f_op)) { ++ pr_err("%s(): File has no file operations registered!\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ if (!myfile->f_op->read) { ++ pr_err("%s(): File has no READ operations registered! Returning\n", ++ __func__); ++ filp_close(myfile, NULL); ++ return -EIO; ++ } ++ ++ pos = myfile->f_pos; ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ ++ for (j = 0; j < NUM_FRAMES; j++) { ++ for (i = 0; i < dev->_lines_count; i++) { ++ pos = offset; ++ ++ vfs_read_retval = vfs_read(myfile, mybuf, ++ line_size, &pos); ++ ++ if (vfs_read_retval > 0 ++ && vfs_read_retval == line_size ++ && dev->_data_buf_virt_addr != NULL) { ++ memcpy((void *)(dev-> ++ _data_buf_virt_addr + ++ offset / 4), mybuf, ++ vfs_read_retval); ++ } ++ ++ offset += vfs_read_retval; ++ ++ if (vfs_read_retval < line_size) { ++ pr_info("Done: exit %s() since no more bytes to read from Video file\n", ++ __func__); ++ break; ++ } ++ } ++ ++ if (i > 0) ++ dev->_frame_count++; ++ ++ if (vfs_read_retval < line_size) ++ break; ++ } ++ ++ dev->_file_status = (vfs_read_retval == line_size) ? ++ IN_PROGRESS : END_OF_FILE; ++ ++ set_fs(old_fs); ++ myfile->f_pos = 0; ++ filp_close(myfile, NULL); ++ } ++ ++ return 0; ++} ++ ++static int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch, ++ int bpl) ++{ ++ int ret = 0; ++ dma_addr_t dma_addr; ++ dma_addr_t data_dma_addr; ++ ++ if (dev->_dma_virt_addr != NULL) ++ pci_free_consistent(dev->pci, dev->upstream_riscbuf_size, ++ dev->_dma_virt_addr, dev->_dma_phys_addr); ++ ++ dev->_dma_virt_addr = pci_alloc_consistent(dev->pci, ++ dev->upstream_riscbuf_size, &dma_addr); ++ dev->_dma_virt_start_addr = dev->_dma_virt_addr; ++ dev->_dma_phys_start_addr = dma_addr; ++ dev->_dma_phys_addr = dma_addr; ++ dev->_risc_size = dev->upstream_riscbuf_size; ++ ++ if (!dev->_dma_virt_addr) { ++ pr_err("FAILED to allocate memory for Risc buffer! Returning\n"); ++ return -ENOMEM; ++ } ++ ++ /* Clear memory at address */ ++ memset(dev->_dma_virt_addr, 0, dev->_risc_size); ++ ++ if (dev->_data_buf_virt_addr != NULL) ++ pci_free_consistent(dev->pci, dev->upstream_databuf_size, ++ dev->_data_buf_virt_addr, ++ dev->_data_buf_phys_addr); ++ /* For Video Data buffer allocation */ ++ dev->_data_buf_virt_addr = pci_alloc_consistent(dev->pci, ++ dev->upstream_databuf_size, &data_dma_addr); ++ dev->_data_buf_phys_addr = data_dma_addr; ++ dev->_data_buf_size = dev->upstream_databuf_size; ++ ++ if (!dev->_data_buf_virt_addr) { ++ pr_err("FAILED to allocate memory for data buffer! Returning\n"); ++ return -ENOMEM; ++ } ++ ++ /* Clear memory at address */ ++ memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); ++ ++ ret = cx25821_openfile(dev, sram_ch); ++ if (ret < 0) ++ return ret; ++ ++ /* Create RISC programs */ ++ ret = cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, ++ dev->_lines_count); ++ if (ret < 0) { ++ pr_info("Failed creating Video Upstream Risc programs!\n"); ++ goto error; ++ } ++ ++ return 0; ++ ++error: ++ return ret; ++} ++ ++static int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, ++ u32 status) ++{ ++ u32 int_msk_tmp; ++ struct sram_channel *channel = dev->channels[chan_num].sram_channels; ++ int singlefield_lines = NTSC_FIELD_HEIGHT; ++ int line_size_in_bytes = Y422_LINE_SZ; ++ int odd_risc_prog_size = 0; ++ dma_addr_t risc_phys_jump_addr; ++ __le32 *rp; ++ ++ if (status & FLD_VID_SRC_RISC1) { ++ /* We should only process one program per call */ ++ u32 prog_cnt = cx_read(channel->gpcnt); ++ ++ /* Since we've identified our IRQ, clear our bits from the ++ * interrupt mask and interrupt status registers */ ++ int_msk_tmp = cx_read(channel->int_msk); ++ cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); ++ cx_write(channel->int_stat, _intr_msk); ++ ++ spin_lock(&dev->slock); ++ ++ dev->_frame_index = prog_cnt; ++ ++ queue_work(dev->_irq_queues, &dev->_irq_work_entry); ++ ++ if (dev->_is_first_frame) { ++ dev->_is_first_frame = 0; ++ ++ if (dev->_isNTSC) { ++ singlefield_lines += 1; ++ odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; ++ } else { ++ singlefield_lines = PAL_FIELD_HEIGHT; ++ odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; ++ } ++ ++ if (dev->_dma_virt_start_addr != NULL) { ++ line_size_in_bytes = ++ (dev->_pixel_format == ++ PIXEL_FRMT_411) ? Y411_LINE_SZ : ++ Y422_LINE_SZ; ++ risc_phys_jump_addr = ++ dev->_dma_phys_start_addr + ++ odd_risc_prog_size; ++ ++ rp = cx25821_update_riscprogram(dev, ++ dev->_dma_virt_start_addr, TOP_OFFSET, ++ line_size_in_bytes, 0x0, ++ singlefield_lines, FIFO_DISABLE, ++ ODD_FIELD); ++ ++ /* Jump to Even Risc program of 1st Frame */ ++ *(rp++) = cpu_to_le32(RISC_JUMP); ++ *(rp++) = cpu_to_le32(risc_phys_jump_addr); ++ *(rp++) = cpu_to_le32(0); ++ } ++ } ++ ++ spin_unlock(&dev->slock); ++ } else { ++ if (status & FLD_VID_SRC_UF) ++ pr_err("%s(): Video Received Underflow Error Interrupt!\n", ++ __func__); ++ ++ if (status & FLD_VID_SRC_SYNC) ++ pr_err("%s(): Video Received Sync Error Interrupt!\n", ++ __func__); ++ ++ if (status & FLD_VID_SRC_OPC_ERR) ++ pr_err("%s(): Video Received OpCode Error Interrupt!\n", ++ __func__); ++ } ++ ++ if (dev->_file_status == END_OF_FILE) { ++ pr_err("EOF Channel 1 Framecount = %d\n", dev->_frame_count); ++ return -1; ++ } ++ /* ElSE, set the interrupt mask register, re-enable irq. */ ++ int_msk_tmp = cx_read(channel->int_msk); ++ cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); ++ ++ return 0; ++} ++ ++static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) ++{ ++ struct cx25821_dev *dev = dev_id; ++ u32 vid_status; ++ int handled = 0; ++ int channel_num = 0; ++ struct sram_channel *sram_ch; ++ ++ if (!dev) ++ return -1; ++ ++ channel_num = VID_UPSTREAM_SRAM_CHANNEL_I; ++ ++ sram_ch = dev->channels[channel_num].sram_channels; ++ ++ vid_status = cx_read(sram_ch->int_stat); ++ ++ /* Only deal with our interrupt */ ++ if (vid_status) ++ handled = cx25821_video_upstream_irq(dev, channel_num, ++ vid_status); ++ ++ if (handled < 0) ++ cx25821_stop_upstream_video_ch1(dev); ++ else ++ handled += handled; ++ ++ return IRQ_RETVAL(handled); ++} ++ ++static void cx25821_set_pixelengine(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ int pix_format) ++{ ++ int width = WIDTH_D1; ++ int height = dev->_lines_count; ++ int num_lines, odd_num_lines; ++ u32 value; ++ int vip_mode = OUTPUT_FRMT_656; ++ ++ value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); ++ value &= 0xFFFFFFEF; ++ value |= dev->_isNTSC ? 0 : 0x10; ++ cx_write(ch->vid_fmt_ctl, value); ++ ++ /* set number of active pixels in each line. ++ * Default is 720 pixels in both NTSC and PAL format */ ++ cx_write(ch->vid_active_ctl1, width); ++ ++ num_lines = (height / 2) & 0x3FF; ++ odd_num_lines = num_lines; ++ ++ if (dev->_isNTSC) ++ odd_num_lines += 1; ++ ++ value = (num_lines << 16) | odd_num_lines; ++ ++ /* set number of active lines in field 0 (top) and field 1 (bottom) */ ++ cx_write(ch->vid_active_ctl2, value); ++ ++ cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); ++} ++ ++static int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, ++ struct sram_channel *sram_ch) ++{ ++ u32 tmp = 0; ++ int err = 0; ++ ++ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for ++ * channel A-C ++ */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); ++ ++ /* Set the physical start address of the RISC program in the initial ++ * program counter(IPC) member of the cmds. ++ */ ++ cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr); ++ /* Risc IPC High 64 bits 63-32 */ ++ cx_write(sram_ch->cmds_start + 4, 0); ++ ++ /* reset counter */ ++ cx_write(sram_ch->gpcnt_ctl, 3); ++ ++ /* Clear our bits from the interrupt status register. */ ++ cx_write(sram_ch->int_stat, _intr_msk); ++ ++ /* Set the interrupt mask register, enable irq. */ ++ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); ++ tmp = cx_read(sram_ch->int_msk); ++ cx_write(sram_ch->int_msk, tmp |= _intr_msk); ++ ++ err = request_irq(dev->pci->irq, cx25821_upstream_irq, ++ IRQF_SHARED, dev->name, dev); ++ if (err < 0) { ++ pr_err("%s: can't get upstream IRQ %d\n", ++ dev->name, dev->pci->irq); ++ goto fail_irq; ++ } ++ ++ /* Start the DMA engine */ ++ tmp = cx_read(sram_ch->dma_ctl); ++ cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); ++ ++ dev->_is_running = 1; ++ dev->_is_first_frame = 1; ++ ++ return 0; ++ ++fail_irq: ++ cx25821_dev_unregister(dev); ++ return err; ++} ++ ++int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, ++ int pixel_format) ++{ ++ struct sram_channel *sram_ch; ++ u32 tmp; ++ int err = 0; ++ int data_frame_size = 0; ++ int risc_buffer_size = 0; ++ int str_length = 0; ++ ++ if (dev->_is_running) { ++ pr_info("Video Channel is still running so return!\n"); ++ return 0; ++ } ++ ++ dev->_channel_upstream_select = channel_select; ++ sram_ch = dev->channels[channel_select].sram_channels; ++ ++ INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler); ++ dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue"); ++ ++ if (!dev->_irq_queues) { ++ pr_err("create_singlethread_workqueue() for Video FAILED!\n"); ++ return -ENOMEM; ++ } ++ /* 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for ++ * channel A-C ++ */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); ++ ++ dev->_is_running = 0; ++ dev->_frame_count = 0; ++ dev->_file_status = RESET_STATUS; ++ dev->_lines_count = dev->_isNTSC ? 480 : 576; ++ dev->_pixel_format = pixel_format; ++ dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? ++ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; ++ data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; ++ risc_buffer_size = dev->_isNTSC ? ++ NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; ++ ++ if (dev->input_filename) { ++ str_length = strlen(dev->input_filename); ++ dev->_filename = kmemdup(dev->input_filename, str_length + 1, ++ GFP_KERNEL); ++ ++ if (!dev->_filename) { ++ err = -ENOENT; ++ goto error; ++ } ++ } else { ++ str_length = strlen(dev->_defaultname); ++ dev->_filename = kmemdup(dev->_defaultname, str_length + 1, ++ GFP_KERNEL); ++ ++ if (!dev->_filename) { ++ err = -ENOENT; ++ goto error; ++ } ++ } ++ ++ /* Default if filename is empty string */ ++ if (strcmp(dev->_filename, "") == 0) { ++ if (dev->_isNTSC) { ++ dev->_filename = ++ (dev->_pixel_format == PIXEL_FRMT_411) ? ++ "/root/vid411.yuv" : "/root/vidtest.yuv"; ++ } else { ++ dev->_filename = ++ (dev->_pixel_format == PIXEL_FRMT_411) ? ++ "/root/pal411.yuv" : "/root/pal422.yuv"; ++ } ++ } ++ ++ dev->_is_running = 0; ++ dev->_frame_count = 0; ++ dev->_file_status = RESET_STATUS; ++ dev->_lines_count = dev->_isNTSC ? 480 : 576; ++ dev->_pixel_format = pixel_format; ++ dev->_line_size = (dev->_pixel_format == PIXEL_FRMT_422) ? ++ (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; ++ ++ err = cx25821_sram_channel_setup_upstream(dev, sram_ch, ++ dev->_line_size, 0); ++ ++ /* setup fifo + format */ ++ cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format); ++ ++ dev->upstream_riscbuf_size = risc_buffer_size * 2; ++ dev->upstream_databuf_size = data_frame_size * 2; ++ ++ /* Allocating buffers and prepare RISC program */ ++ err = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size); ++ if (err < 0) { ++ pr_err("%s: Failed to set up Video upstream buffers!\n", ++ dev->name); ++ goto error; ++ } ++ ++ cx25821_start_video_dma_upstream(dev, sram_ch); ++ ++ return 0; ++ ++error: ++ cx25821_dev_unregister(dev); ++ ++ return err; ++} +diff --git a/drivers/media/pci/cx25821/cx25821-video-upstream.h b/drivers/media/pci/cx25821/cx25821-video-upstream.h +new file mode 100644 +index 0000000..268ec8a +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video-upstream.h +@@ -0,0 +1,139 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#define OUTPUT_FRMT_656 0 ++#define OPEN_FILE_1 0 ++#define NUM_PROGS 8 ++#define NUM_FRAMES 2 ++#define ODD_FIELD 0 ++#define EVEN_FIELD 1 ++#define TOP_OFFSET 0 ++#define FIFO_DISABLE 0 ++#define FIFO_ENABLE 1 ++#define TEST_FRAMES 5 ++#define END_OF_FILE 0 ++#define IN_PROGRESS 1 ++#define RESET_STATUS -1 ++#define NUM_NO_OPS 5 ++ ++/* PAL and NTSC line sizes and number of lines. */ ++#define WIDTH_D1 720 ++#define NTSC_LINES_PER_FRAME 480 ++#define PAL_LINES_PER_FRAME 576 ++#define PAL_LINE_SZ 1440 ++#define Y422_LINE_SZ 1440 ++#define Y411_LINE_SZ 1080 ++#define NTSC_FIELD_HEIGHT 240 ++#define NTSC_ODD_FLD_LINES 241 ++#define PAL_FIELD_HEIGHT 288 ++ ++#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) ++#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) ++#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) ++#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) ++ ++#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) ++#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) ++ ++#define RISC_WRITECR_INSTRUCTION_SIZE 16 ++#define RISC_SYNC_INSTRUCTION_SIZE 4 ++#define JUMP_INSTRUCTION_SIZE 12 ++#define MAXSIZE_NO_OPS 36 ++#define DWORD_SIZE 4 ++ ++#define USE_RISC_NOOP_VIDEO 1 ++ ++#ifdef USE_RISC_NOOP_VIDEO ++#define PAL_US_VID_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) ++ ++#define PAL_VID_PROG_SIZE \ ++ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) ++ ++#define ODD_FLD_PAL_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define ODD_FLD_NTSC_PROG_SIZE \ ++ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define NTSC_US_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + \ ++ NUM_NO_OPS * DWORD_SIZE) ++ ++#define NTSC_RISC_BUF_SIZE \ ++ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) ++ ++#define FRAME1_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE + 2 * NUM_NO_OPS * DWORD_SIZE) ++ ++#endif ++ ++#ifndef USE_RISC_NOOP_VIDEO ++#define PAL_US_VID_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE) ++ ++#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) ++ ++#define PAL_VID_PROG_SIZE \ ++ ((PAL_FIELD_HEIGHT * 2) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE) ++ ++#define ODD_FLD_PAL_PROG_SIZE \ ++ (PAL_FIELD_HEIGHT * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) ++ ++#define ODD_FLD_NTSC_PROG_SIZE \ ++ (NTSC_ODD_FLD_LINES * 3 * DWORD_SIZE + \ ++ RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE) ++ ++#define NTSC_US_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + \ ++ RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) ++ ++#define NTSC_RISC_BUF_SIZE \ ++ (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) ++ ++#define FRAME1_VID_PROG_SIZE \ ++ ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + \ ++ 2 * RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ ++ JUMP_INSTRUCTION_SIZE) ++ ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c +new file mode 100644 +index 0000000..e28a7aa +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video.c +@@ -0,0 +1,1991 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * Parts adapted/taken from Eduardo Moscoso Rubino ++ * Copyright (C) 2009 Eduardo Moscoso Rubino ++ * ++ * ++ * 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. ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "cx25821-video.h" ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); ++MODULE_AUTHOR("Hiep Huynh "); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; ++static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; ++ ++module_param_array(video_nr, int, NULL, 0444); ++module_param_array(radio_nr, int, NULL, 0444); ++ ++MODULE_PARM_DESC(video_nr, "video device numbers"); ++MODULE_PARM_DESC(radio_nr, "radio device numbers"); ++ ++static unsigned int video_debug = VIDEO_DEBUG; ++module_param(video_debug, int, 0644); ++MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); ++ ++static unsigned int irq_debug; ++module_param(irq_debug, int, 0644); ++MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); ++ ++unsigned int vid_limit = 16; ++module_param(vid_limit, int, 0644); ++MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); ++ ++static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num); ++ ++static const struct v4l2_file_operations video_fops; ++static const struct v4l2_ioctl_ops video_ioctl_ops; ++ ++#define FORMAT_FLAGS_PACKED 0x01 ++ ++struct cx25821_fmt formats[] = { ++ { ++ .name = "8 bpp, gray", ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:1:1, packed, Y41P", ++ .fourcc = V4L2_PIX_FMT_Y41P, ++ .depth = 12, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, { ++ .name = "4:2:0, YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .depth = 12, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, ++}; ++ ++int cx25821_get_format_size(void) ++{ ++ return ARRAY_SIZE(formats); ++} ++ ++struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) ++{ ++ unsigned int i; ++ ++ if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) ++ return formats + 1; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fourcc) ++ return formats + i; ++ ++ pr_err("%s(0x%08x) NOT FOUND\n", __func__, fourcc); ++ return NULL; ++} ++ ++void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, ++ u32 count) ++{ ++ struct cx25821_buffer *buf; ++ int bc; ++ ++ for (bc = 0;; bc++) { ++ if (list_empty(&q->active)) { ++ dprintk(1, "bc=%d (=0: active empty)\n", bc); ++ break; ++ } ++ ++ buf = list_entry(q->active.next, struct cx25821_buffer, ++ vb.queue); ++ ++ /* count comes from the hw and it is 16bit wide -- ++ * this trick handles wrap-arounds correctly for ++ * up to 32767 buffers in flight... */ ++ if ((s16) (count - buf->count) < 0) ++ break; ++ ++ v4l2_get_timestamp(&buf->vb.ts); ++ buf->vb.state = VIDEOBUF_DONE; ++ list_del(&buf->vb.queue); ++ wake_up(&buf->vb.done); ++ } ++ ++ if (list_empty(&q->active)) ++ del_timer(&q->timeout); ++ else ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ if (bc != 1) ++ pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); ++} ++ ++#ifdef TUNER_FLAG ++int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm) ++{ ++ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", ++ __func__, (unsigned int)norm, v4l2_norm_to_name(norm)); ++ ++ dev->tvnorm = norm; ++ ++ /* Tell the internal A/V decoder */ ++ cx25821_call_all(dev, core, s_std, norm); ++ ++ return 0; ++} ++#endif ++ ++struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ dprintk(1, "%s()\n", __func__); ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template; ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ vfd->release = video_device_release; ++ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, ++ cx25821_boards[dev->board].name); ++ video_set_drvdata(vfd, dev); ++ return vfd; ++} ++ ++/* ++static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) ++{ ++ int i; ++ ++ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) ++ return -EINVAL; ++ for (i = 0; i < CX25821_CTLS; i++) ++ if (cx25821_ctls[i].v.id == qctrl->id) ++ break; ++ if (i == CX25821_CTLS) { ++ *qctrl = no_ctl; ++ return 0; ++ } ++ *qctrl = cx25821_ctls[i].v; ++ return 0; ++} ++*/ ++ ++/* resource management */ ++int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, ++ unsigned int bit) ++{ ++ dprintk(1, "%s()\n", __func__); ++ if (fh->resources & bit) ++ /* have it already allocated */ ++ return 1; ++ ++ /* is it free? */ ++ mutex_lock(&dev->lock); ++ if (dev->channels[fh->channel_id].resources & bit) { ++ /* no, someone else uses it */ ++ mutex_unlock(&dev->lock); ++ return 0; ++ } ++ /* it's free, grab it */ ++ fh->resources |= bit; ++ dev->channels[fh->channel_id].resources |= bit; ++ dprintk(1, "res: get %d\n", bit); ++ mutex_unlock(&dev->lock); ++ return 1; ++} ++ ++int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit) ++{ ++ return fh->resources & bit; ++} ++ ++int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit) ++{ ++ return fh->dev->channels[fh->channel_id].resources & bit; ++} ++ ++void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, ++ unsigned int bits) ++{ ++ BUG_ON((fh->resources & bits) != bits); ++ dprintk(1, "%s()\n", __func__); ++ ++ mutex_lock(&dev->lock); ++ fh->resources &= ~bits; ++ dev->channels[fh->channel_id].resources &= ~bits; ++ dprintk(1, "res: put %d\n", bits); ++ mutex_unlock(&dev->lock); ++} ++ ++int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input) ++{ ++ struct v4l2_routing route; ++ memset(&route, 0, sizeof(route)); ++ ++ dprintk(1, "%s(): video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", ++ __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, ++ INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); ++ dev->input = input; ++ ++ route.input = INPUT(input)->vmux; ++ ++ /* Tell the internal A/V decoder */ ++ cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0); ++ ++ return 0; ++} ++ ++int cx25821_start_video_dma(struct cx25821_dev *dev, ++ struct cx25821_dmaqueue *q, ++ struct cx25821_buffer *buf, ++ struct sram_channel *channel) ++{ ++ int tmp = 0; ++ ++ /* setup fifo + format */ ++ cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma); ++ ++ /* reset counter */ ++ cx_write(channel->gpcnt_ctl, 3); ++ q->count = 1; ++ ++ /* enable irq */ ++ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); ++ cx_set(channel->int_msk, 0x11); ++ ++ /* start dma */ ++ cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */ ++ ++ /* make sure upstream setting if any is reversed */ ++ tmp = cx_read(VID_CH_MODE_SEL); ++ cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); ++ ++ return 0; ++} ++ ++static int cx25821_restart_video_queue(struct cx25821_dev *dev, ++ struct cx25821_dmaqueue *q, ++ struct sram_channel *channel) ++{ ++ struct cx25821_buffer *buf, *prev; ++ struct list_head *item; ++ ++ if (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx25821_buffer, ++ vb.queue); ++ ++ cx25821_start_video_dma(dev, q, buf, channel); ++ ++ list_for_each(item, &q->active) { ++ buf = list_entry(item, struct cx25821_buffer, vb.queue); ++ buf->count = q->count++; ++ } ++ ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ return 0; ++ } ++ ++ prev = NULL; ++ for (;;) { ++ if (list_empty(&q->queued)) ++ return 0; ++ ++ buf = list_entry(q->queued.next, struct cx25821_buffer, ++ vb.queue); ++ ++ if (NULL == prev) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ cx25821_start_video_dma(dev, q, buf, channel); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++} ++ ++static void cx25821_vid_timeout(unsigned long data) ++{ ++ struct cx25821_data *timeout_data = (struct cx25821_data *)data; ++ struct cx25821_dev *dev = timeout_data->dev; ++ struct sram_channel *channel = timeout_data->channel; ++ struct cx25821_dmaqueue *q = &dev->channels[channel->i].vidq; ++ struct cx25821_buffer *buf; ++ unsigned long flags; ++ ++ /* cx25821_sram_channel_dump(dev, channel); */ ++ cx_clear(channel->dma_ctl, 0x11); ++ ++ spin_lock_irqsave(&dev->slock, flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx25821_buffer, ++ vb.queue); ++ list_del(&buf->vb.queue); ++ ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ } ++ ++ cx25821_restart_video_queue(dev, q, channel); ++ spin_unlock_irqrestore(&dev->slock, flags); ++} ++ ++int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) ++{ ++ u32 count = 0; ++ int handled = 0; ++ u32 mask; ++ struct sram_channel *channel = dev->channels[chan_num].sram_channels; ++ ++ mask = cx_read(channel->int_msk); ++ if (0 == (status & mask)) ++ return handled; ++ ++ cx_write(channel->int_stat, status); ++ ++ /* risc op code error */ ++ if (status & (1 << 16)) { ++ pr_warn("%s, %s: video risc op code error\n", ++ dev->name, channel->name); ++ cx_clear(channel->dma_ctl, 0x11); ++ cx25821_sram_channel_dump(dev, channel); ++ } ++ ++ /* risc1 y */ ++ if (status & FLD_VID_DST_RISC1) { ++ spin_lock(&dev->slock); ++ count = cx_read(channel->gpcnt); ++ cx25821_video_wakeup(dev, &dev->channels[channel->i].vidq, ++ count); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ ++ /* risc2 y */ ++ if (status & 0x10) { ++ dprintk(2, "stopper video\n"); ++ spin_lock(&dev->slock); ++ cx25821_restart_video_queue(dev, ++ &dev->channels[channel->i].vidq, channel); ++ spin_unlock(&dev->slock); ++ handled++; ++ } ++ return handled; ++} ++ ++void cx25821_videoioctl_unregister(struct cx25821_dev *dev) ++{ ++ if (dev->ioctl_dev) { ++ if (video_is_registered(dev->ioctl_dev)) ++ video_unregister_device(dev->ioctl_dev); ++ else ++ video_device_release(dev->ioctl_dev); ++ ++ dev->ioctl_dev = NULL; ++ } ++} ++ ++void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) ++{ ++ cx_clear(PCI_INT_MSK, 1); ++ ++ if (dev->channels[chan_num].video_dev) { ++ if (video_is_registered(dev->channels[chan_num].video_dev)) ++ video_unregister_device( ++ dev->channels[chan_num].video_dev); ++ else ++ video_device_release( ++ dev->channels[chan_num].video_dev); ++ ++ dev->channels[chan_num].video_dev = NULL; ++ ++ btcx_riscmem_free(dev->pci, ++ &dev->channels[chan_num].vidq.stopper); ++ ++ pr_warn("device %d released!\n", chan_num); ++ } ++ ++} ++ ++int cx25821_video_register(struct cx25821_dev *dev) ++{ ++ int err; ++ int i; ++ ++ struct video_device cx25821_video_device = { ++ .name = "cx25821-video", ++ .fops = &video_fops, ++ .minor = -1, ++ .ioctl_ops = &video_ioctl_ops, ++ .tvnorms = CX25821_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++ }; ++ ++ spin_lock_init(&dev->slock); ++ ++ for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { ++ cx25821_init_controls(dev, i); ++ ++ cx25821_risc_stopper(dev->pci, &dev->channels[i].vidq.stopper, ++ dev->channels[i].sram_channels->dma_ctl, 0x11, 0); ++ ++ dev->channels[i].sram_channels = &cx25821_sram_channels[i]; ++ dev->channels[i].video_dev = NULL; ++ dev->channels[i].resources = 0; ++ ++ cx_write(dev->channels[i].sram_channels->int_stat, 0xffffffff); ++ ++ INIT_LIST_HEAD(&dev->channels[i].vidq.active); ++ INIT_LIST_HEAD(&dev->channels[i].vidq.queued); ++ ++ dev->channels[i].timeout_data.dev = dev; ++ dev->channels[i].timeout_data.channel = ++ &cx25821_sram_channels[i]; ++ dev->channels[i].vidq.timeout.function = cx25821_vid_timeout; ++ dev->channels[i].vidq.timeout.data = ++ (unsigned long)&dev->channels[i].timeout_data; ++ init_timer(&dev->channels[i].vidq.timeout); ++ ++ /* register v4l devices */ ++ dev->channels[i].video_dev = cx25821_vdev_init(dev, dev->pci, ++ &cx25821_video_device, "video"); ++ ++ err = video_register_device(dev->channels[i].video_dev, ++ VFL_TYPE_GRABBER, video_nr[dev->nr]); ++ ++ if (err < 0) ++ goto fail_unreg; ++ ++ } ++ ++ /* set PCI interrupt */ ++ cx_set(PCI_INT_MSK, 0xff); ++ ++ /* initial device configuration */ ++ mutex_lock(&dev->lock); ++#ifdef TUNER_FLAG ++ dev->tvnorm = cx25821_video_device.current_norm; ++ cx25821_set_tvnorm(dev, dev->tvnorm); ++#endif ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++ ++fail_unreg: ++ cx25821_video_unregister(dev, i); ++ return err; ++} ++ ++int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, ++ unsigned int *size) ++{ ++ struct cx25821_fh *fh = q->priv_data; ++ ++ *size = fh->fmt->depth * fh->width * fh->height >> 3; ++ ++ if (0 == *count) ++ *count = 32; ++ ++ if (*size * *count > vid_limit * 1024 * 1024) ++ *count = (vid_limit * 1024 * 1024) / *size; ++ ++ return 0; ++} ++ ++int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx25821_fh *fh = q->priv_data; ++ struct cx25821_dev *dev = fh->dev; ++ struct cx25821_buffer *buf = ++ container_of(vb, struct cx25821_buffer, vb); ++ int rc, init_buffer = 0; ++ u32 line0_offset; ++ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); ++ int bpl_local = LINE_SIZE_D1; ++ int channel_opened = fh->channel_id; ++ ++ BUG_ON(NULL == fh->fmt); ++ if (fh->width < 48 || fh->width > 720 || ++ fh->height < 32 || fh->height > 576) ++ return -EINVAL; ++ ++ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; ++ ++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ ++ if (buf->fmt != fh->fmt || ++ buf->vb.width != fh->width || ++ buf->vb.height != fh->height || buf->vb.field != field) { ++ buf->fmt = fh->fmt; ++ buf->vb.width = fh->width; ++ buf->vb.height = fh->height; ++ buf->vb.field = field; ++ init_buffer = 1; ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ init_buffer = 1; ++ rc = videobuf_iolock(q, &buf->vb, NULL); ++ if (0 != rc) { ++ printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); ++ goto fail; ++ } ++ } ++ ++ dprintk(1, "init_buffer=%d\n", init_buffer); ++ ++ if (init_buffer) { ++ ++ channel_opened = dev->channel_opened; ++ if (channel_opened < 0 || channel_opened > 7) ++ channel_opened = 7; ++ ++ if (dev->channels[channel_opened].pixel_formats == ++ PIXEL_FRMT_411) ++ buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; ++ else ++ buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); ++ ++ if (dev->channels[channel_opened].pixel_formats == ++ PIXEL_FRMT_411) { ++ bpl_local = buf->bpl; ++ } else { ++ bpl_local = buf->bpl; /* Default */ ++ ++ if (channel_opened >= 0 && channel_opened <= 7) { ++ if (dev->channels[channel_opened] ++ .use_cif_resolution) { ++ if (dev->tvnorm & V4L2_STD_PAL_BG || ++ dev->tvnorm & V4L2_STD_PAL_DK) ++ bpl_local = 352 << 1; ++ else ++ bpl_local = dev->channels[ ++ channel_opened]. ++ cif_width << 1; ++ } ++ } ++ } ++ ++ switch (buf->vb.field) { ++ case V4L2_FIELD_TOP: ++ cx25821_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, 0, UNSET, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ cx25821_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, UNSET, 0, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ /* All other formats are top field first */ ++ line0_offset = 0; ++ dprintk(1, "top field first\n"); ++ ++ cx25821_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, line0_offset, ++ bpl_local, bpl_local, bpl_local, ++ buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ cx25821_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ 0, buf->bpl * (buf->vb.height >> 1), ++ buf->bpl, 0, buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_BT: ++ cx25821_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ buf->bpl * (buf->vb.height >> 1), 0, ++ buf->bpl, 0, buf->vb.height >> 1); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ ++ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", ++ buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth, ++ fh->fmt->name, (unsigned long)buf->risc.dma); ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ ++ return 0; ++ ++fail: ++ cx25821_free_buffer(q, buf); ++ return rc; ++} ++ ++void cx25821_buffer_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ struct cx25821_buffer *buf = ++ container_of(vb, struct cx25821_buffer, vb); ++ ++ cx25821_free_buffer(q, buf); ++} ++ ++struct videobuf_queue *get_queue(struct cx25821_fh *fh) ++{ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &fh->vidq; ++ default: ++ BUG(); ++ return NULL; ++ } ++} ++ ++int cx25821_get_resource(struct cx25821_fh *fh, int resource) ++{ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return resource; ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ ++ return videobuf_mmap_mapper(get_queue(fh), vma); ++} ++ ++ ++static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct cx25821_buffer *buf = ++ container_of(vb, struct cx25821_buffer, vb); ++ struct cx25821_buffer *prev; ++ struct cx25821_fh *fh = vq->priv_data; ++ struct cx25821_dev *dev = fh->dev; ++ struct cx25821_dmaqueue *q = &dev->channels[fh->channel_id].vidq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); ++ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ ++ ++ dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); ++ ++ if (!list_empty(&q->queued)) { ++ list_add_tail(&buf->vb.queue, &q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, ++ buf->vb.i); ++ ++ } else if (list_empty(&q->active)) { ++ list_add_tail(&buf->vb.queue, &q->active); ++ cx25821_start_video_dma(dev, q, buf, ++ dev->channels[fh->channel_id].sram_channels); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); ++ dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", ++ buf, buf->vb.i, buf->count, q->count); ++ } else { ++ prev = list_entry(q->active.prev, struct cx25821_buffer, ++ vb.queue); ++ if (prev->vb.width == buf->vb.width ++ && prev->vb.height == buf->vb.height ++ && prev->fmt == buf->fmt) { ++ list_add_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ ++ /* 64 bit bits 63-32 */ ++ prev->risc.jmp[2] = cpu_to_le32(0); ++ dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", ++ buf, buf->vb.i, buf->count); ++ ++ } else { ++ list_add_tail(&buf->vb.queue, &q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, ++ buf->vb.i); ++ } ++ } ++ ++ if (list_empty(&q->active)) ++ dprintk(2, "active queue empty!\n"); ++} ++ ++static struct videobuf_queue_ops cx25821_video_qops = { ++ .buf_setup = cx25821_buffer_setup, ++ .buf_prepare = cx25821_buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = cx25821_buffer_release, ++}; ++ ++static int video_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx25821_dev *h, *dev = video_drvdata(file); ++ struct cx25821_fh *fh; ++ struct list_head *list; ++ int minor = video_devdata(file)->minor; ++ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ u32 pix_format; ++ int ch_id = 0; ++ int i; ++ ++ dprintk(1, "open dev=%s type=%s\n", video_device_node_name(vdev), ++ v4l2_type_names[type]); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ mutex_lock(&cx25821_devlist_mutex); ++ ++ list_for_each(list, &cx25821_devlist) ++ { ++ h = list_entry(list, struct cx25821_dev, devlist); ++ ++ for (i = 0; i < MAX_VID_CHANNEL_NUM; i++) { ++ if (h->channels[i].video_dev && ++ h->channels[i].video_dev->minor == minor) { ++ dev = h; ++ ch_id = i; ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ } ++ } ++ } ++ ++ if (NULL == dev) { ++ mutex_unlock(&cx25821_devlist_mutex); ++ kfree(fh); ++ return -ENODEV; ++ } ++ ++ file->private_data = fh; ++ fh->dev = dev; ++ fh->type = type; ++ fh->width = 720; ++ fh->channel_id = ch_id; ++ ++ if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) ++ fh->height = 576; ++ else ++ fh->height = 480; ++ ++ dev->channel_opened = fh->channel_id; ++ if (dev->channels[ch_id].pixel_formats == PIXEL_FRMT_411) ++ pix_format = V4L2_PIX_FMT_Y41P; ++ else ++ pix_format = V4L2_PIX_FMT_YUYV; ++ fh->fmt = cx25821_format_by_fourcc(pix_format); ++ ++ v4l2_prio_open(&dev->channels[ch_id].prio, &fh->prio); ++ ++ videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, &dev->pci->dev, ++ &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), ++ fh, NULL); ++ ++ dprintk(1, "post videobuf_queue_init()\n"); ++ mutex_unlock(&cx25821_devlist_mutex); ++ ++ return 0; ++} ++ ++static ssize_t video_read(struct file *file, char __user * data, size_t count, ++ loff_t *ppos) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (cx25821_res_locked(fh, RESOURCE_VIDEO0)) ++ return -EBUSY; ++ ++ return videobuf_read_one(&fh->vidq, data, count, ppos, ++ file->f_flags & O_NONBLOCK); ++ ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static unsigned int video_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_buffer *buf; ++ ++ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { ++ /* streaming capture */ ++ if (list_empty(&fh->vidq.stream)) ++ return POLLERR; ++ buf = list_entry(fh->vidq.stream.next, ++ struct cx25821_buffer, vb.stream); ++ } else { ++ /* read() capture */ ++ buf = (struct cx25821_buffer *)fh->vidq.read_buf; ++ if (NULL == buf) ++ return POLLERR; ++ } ++ ++ poll_wait(file, &buf->vb.done, wait); ++ if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { ++ if (buf->vb.state == VIDEOBUF_DONE) { ++ struct cx25821_dev *dev = fh->dev; ++ ++ if (dev && dev->channels[fh->channel_id] ++ .use_cif_resolution) { ++ u8 cam_id = *((char *)buf->vb.baddr + 3); ++ memcpy((char *)buf->vb.baddr, ++ (char *)buf->vb.baddr + (fh->width * 2), ++ (fh->width * 2)); ++ *((char *)buf->vb.baddr + 3) = cam_id; ++ } ++ } ++ ++ return POLLIN | POLLRDNORM; ++ } ++ ++ return 0; ++} ++ ++static int video_release(struct file *file) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_dev *dev = fh->dev; ++ ++ /* stop the risc engine and fifo */ ++ cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ ++ ++ /* stop video capture */ ++ if (cx25821_res_check(fh, RESOURCE_VIDEO0)) { ++ videobuf_queue_cancel(&fh->vidq); ++ cx25821_res_free(dev, fh, RESOURCE_VIDEO0); ++ } ++ ++ if (fh->vidq.read_buf) { ++ cx25821_buffer_release(&fh->vidq, fh->vidq.read_buf); ++ kfree(fh->vidq.read_buf); ++ } ++ ++ videobuf_mmap_free(&fh->vidq); ++ ++ v4l2_prio_close(&dev->channels[fh->channel_id].prio, fh->prio); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ return 0; ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = fh->dev; ++ ++ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) ++ return -EINVAL; ++ ++ if (unlikely(i != fh->type)) ++ return -EINVAL; ++ ++ if (unlikely(!cx25821_res_get(dev, fh, cx25821_get_resource(fh, ++ RESOURCE_VIDEO0)))) ++ return -EBUSY; ++ ++ return videobuf_streamon(get_queue(fh)); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = fh->dev; ++ int err, res; ++ ++ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (i != fh->type) ++ return -EINVAL; ++ ++ res = cx25821_get_resource(fh, RESOURCE_VIDEO0); ++ err = videobuf_streamoff(get_queue(fh)); ++ if (err < 0) ++ return err; ++ cx25821_res_free(dev, fh, res); ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int err; ++ int pix_format = PIXEL_FRMT_422; ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ ++ dprintk(2, "%s()\n", __func__); ++ err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f); ++ ++ if (0 != err) ++ return err; ++ ++ fh->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); ++ fh->vidq.field = f->fmt.pix.field; ++ ++ /* check if width and height is valid based on set standard */ ++ if (cx25821_is_valid_width(f->fmt.pix.width, dev->tvnorm)) ++ fh->width = f->fmt.pix.width; ++ ++ if (cx25821_is_valid_height(f->fmt.pix.height, dev->tvnorm)) ++ fh->height = f->fmt.pix.height; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) ++ pix_format = PIXEL_FRMT_411; ++ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ++ pix_format = PIXEL_FRMT_422; ++ else ++ return -EINVAL; ++ ++ cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); ++ ++ /* check if cif resolution */ ++ if (fh->width == 320 || fh->width == 352) ++ dev->channels[fh->channel_id].use_cif_resolution = 1; ++ else ++ dev->channels[fh->channel_id].use_cif_resolution = 0; ++ ++ dev->channels[fh->channel_id].cif_width = fh->width; ++ medusa_set_resolution(dev, fh->width, SRAM_CH00); ++ ++ dprintk(2, "%s(): width=%d height=%d field=%d\n", __func__, fh->width, ++ fh->height, fh->vidq.field); ++ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); ++ cx25821_call_all(dev, video, s_mbus_fmt, &mbus_fmt); ++ ++ return 0; ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ int ret_val = 0; ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ ++ ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); ++ ++ p->sequence = dev->channels[fh->channel_id].vidq.count; ++ ++ return ret_val; ++} ++ ++static int vidioc_log_status(struct file *file, void *priv) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ struct cx25821_fh *fh = priv; ++ char name[32 + 2]; ++ ++ struct sram_channel *sram_ch = dev->channels[fh->channel_id] ++ .sram_channels; ++ u32 tmp = 0; ++ ++ snprintf(name, sizeof(name), "%s/2", dev->name); ++ pr_info("%s/2: ============ START LOG STATUS ============\n", ++ dev->name); ++ cx25821_call_all(dev, core, log_status); ++ tmp = cx_read(sram_ch->dma_ctl); ++ pr_info("Video input 0 is %s\n", ++ (tmp & 0x11) ? "streaming" : "stopped"); ++ pr_info("%s/2: ============= END LOG STATUS =============\n", ++ dev->name); ++ return 0; ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ int err; ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ ++ return cx25821_set_control(dev, ctl, fh->channel_id); ++} ++ ++/* VIDEO IOCTLS */ ++int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx25821_fh *fh = priv; ++ ++ f->fmt.pix.width = fh->width; ++ f->fmt.pix.height = fh->height; ++ f->fmt.pix.field = fh->vidq.field; ++ f->fmt.pix.pixelformat = fh->fmt->fourcc; ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx25821_fmt *fmt; ++ enum v4l2_field field; ++ unsigned int maxw, maxh; ++ ++ fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ maxw = 720; ++ maxh = 576; ++ ++ if (V4L2_FIELD_ANY == field) { ++ if (f->fmt.pix.height > maxh / 2) ++ field = V4L2_FIELD_INTERLACED; ++ else ++ field = V4L2_FIELD_TOP; ++ } ++ ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ f->fmt.pix.field = field; ++ if (f->fmt.pix.height < 32) ++ f->fmt.pix.height = 32; ++ if (f->fmt.pix.height > maxh) ++ f->fmt.pix.height = maxh; ++ if (f->fmt.pix.width < 48) ++ f->fmt.pix.width = 48; ++ if (f->fmt.pix.width > maxw) ++ f->fmt.pix.width = maxw; ++ f->fmt.pix.width &= ~0x03; ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++int cx25821_vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ ++ strcpy(cap->driver, "cx25821"); ++ strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); ++ cap->version = CX25821_VERSION_CODE; ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ if (UNSET != dev->tuner_type) ++ cap->capabilities |= V4L2_CAP_TUNER; ++ return 0; ++} ++ ++int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (unlikely(f->index >= ARRAY_SIZE(formats))) ++ return -EINVAL; ++ ++ strlcpy(f->description, formats[f->index].name, sizeof(f->description)); ++ f->pixelformat = formats[f->index].fourcc; ++ ++ return 0; ++} ++ ++int cx25821_vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct cx25821_fh *fh = priv; ++ return videobuf_reqbufs(get_queue(fh), p); ++} ++ ++int cx25821_vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct cx25821_fh *fh = priv; ++ return videobuf_querybuf(get_queue(fh), p); ++} ++ ++int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct cx25821_fh *fh = priv; ++ return videobuf_qbuf(get_queue(fh), p); ++} ++ ++int cx25821_vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; ++ struct cx25821_fh *fh = f; ++ ++ *p = v4l2_prio_max(&dev->channels[fh->channel_id].prio); ++ ++ return 0; ++} ++ ++int cx25821_vidioc_s_priority(struct file *file, void *f, ++ enum v4l2_priority prio) ++{ ++ struct cx25821_fh *fh = f; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; ++ ++ return v4l2_prio_change(&dev->channels[fh->channel_id].prio, &fh->prio, ++ prio); ++} ++ ++#ifdef TUNER_FLAG ++int cx25821_vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ int err; ++ ++ dprintk(1, "%s()\n", __func__); ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ ++ if (dev->tvnorm == *tvnorms) ++ return 0; ++ ++ mutex_lock(&dev->lock); ++ cx25821_set_tvnorm(dev, *tvnorms); ++ mutex_unlock(&dev->lock); ++ ++ medusa_set_videostandard(dev); ++ ++ return 0; ++} ++#endif ++ ++int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) ++{ ++ static const char * const iname[] = { ++ [CX25821_VMUX_COMPOSITE] = "Composite", ++ [CX25821_VMUX_SVIDEO] = "S-Video", ++ [CX25821_VMUX_DEBUG] = "for debug only", ++ }; ++ unsigned int n; ++ dprintk(1, "%s()\n", __func__); ++ ++ n = i->index; ++ if (n >= 2) ++ return -EINVAL; ++ ++ if (0 == INPUT(n)->type) ++ return -EINVAL; ++ ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ strcpy(i->name, iname[INPUT(n)->type]); ++ ++ i->std = CX25821_NORMS; ++ return 0; ++} ++ ++int cx25821_vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ dprintk(1, "%s()\n", __func__); ++ return cx25821_enum_input(dev, i); ++} ++ ++int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ ++ *i = dev->input; ++ dprintk(1, "%s(): returns %d\n", __func__, *i); ++ return 0; ++} ++ ++int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ int err; ++ ++ dprintk(1, "%s(%d)\n", __func__, i); ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ ++ if (i >= CX25821_NR_INPUT) { ++ dprintk(1, "%s(): -EINVAL\n", __func__); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&dev->lock); ++ cx25821_video_mux(dev, i); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++#ifdef TUNER_FLAG ++int cx25821_vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev = fh->dev; ++ ++ f->frequency = dev->freq; ++ ++ cx25821_call_all(dev, tuner, g_frequency, f); ++ ++ return 0; ++} ++ ++int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) ++{ ++ mutex_lock(&dev->lock); ++ dev->freq = f->frequency; ++ ++ cx25821_call_all(dev, tuner, s_frequency, f); ++ ++ /* When changing channels it is required to reset TVAUDIO */ ++ msleep(10); ++ ++ mutex_unlock(&dev->lock); ++ ++ return 0; ++} ++ ++int cx25821_vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx25821_fh *fh = priv; ++ struct cx25821_dev *dev; ++ int err; ++ ++ if (fh) { ++ dev = fh->dev; ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } else { ++ pr_err("Invalid fh pointer!\n"); ++ return -EINVAL; ++ } ++ ++ return cx25821_set_freq(dev, f); ++} ++#endif ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++int cx25821_vidioc_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ ++ cx25821_call_all(dev, core, g_register, reg); ++ ++ return 0; ++} ++ ++int cx25821_vidioc_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ ++ cx25821_call_all(dev, core, s_register, reg); ++ ++ return 0; ++} ++ ++#endif ++ ++#ifdef TUNER_FLAG ++int cx25821_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ ++ if (unlikely(UNSET == dev->tuner_type)) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "Television"); ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM; ++ t->rangehigh = 0xffffffffUL; ++ ++ t->signal = 0xffff; /* LOCKED */ ++ return 0; ++} ++ ++int cx25821_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ struct cx25821_fh *fh = priv; ++ int err; ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ ++ dprintk(1, "%s()\n", __func__); ++ if (UNSET == dev->tuner_type) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++#endif ++/*****************************************************************************/ ++static const struct v4l2_queryctrl no_ctl = { ++ .name = "42", ++ .flags = V4L2_CTRL_FLAG_DISABLED, ++}; ++ ++static struct v4l2_queryctrl cx25821_ctls[] = { ++ /* --- video --- */ ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .name = "Brightness", ++ .minimum = 0, ++ .maximum = 10000, ++ .step = 1, ++ .default_value = 6200, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, { ++ .id = V4L2_CID_CONTRAST, ++ .name = "Contrast", ++ .minimum = 0, ++ .maximum = 10000, ++ .step = 1, ++ .default_value = 5000, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, { ++ .id = V4L2_CID_SATURATION, ++ .name = "Saturation", ++ .minimum = 0, ++ .maximum = 10000, ++ .step = 1, ++ .default_value = 5000, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, { ++ .id = V4L2_CID_HUE, ++ .name = "Hue", ++ .minimum = 0, ++ .maximum = 10000, ++ .step = 1, ++ .default_value = 5000, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ } ++}; ++static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls); ++ ++static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) ++{ ++ int i; ++ ++ if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) ++ return -EINVAL; ++ for (i = 0; i < CX25821_CTLS; i++) ++ if (cx25821_ctls[i].id == qctrl->id) ++ break; ++ if (i == CX25821_CTLS) { ++ *qctrl = no_ctl; ++ return 0; ++ } ++ *qctrl = cx25821_ctls[i]; ++ return 0; ++} ++ ++int cx25821_vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *qctrl) ++{ ++ return cx25821_ctrl_query(qctrl); ++} ++ ++/* ------------------------------------------------------------------ */ ++/* VIDEO CTRL IOCTLS */ ++ ++static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < CX25821_CTLS; i++) ++ if (cx25821_ctls[i].id == id) ++ return cx25821_ctls + i; ++ return NULL; ++} ++ ++int cx25821_vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ struct cx25821_fh *fh = priv; ++ ++ const struct v4l2_queryctrl *ctrl; ++ ++ ctrl = ctrl_by_id(ctl->id); ++ ++ if (NULL == ctrl) ++ return -EINVAL; ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ ctl->value = dev->channels[fh->channel_id].ctl_bright; ++ break; ++ case V4L2_CID_HUE: ++ ctl->value = dev->channels[fh->channel_id].ctl_hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ ctl->value = dev->channels[fh->channel_id].ctl_contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ ctl->value = dev->channels[fh->channel_id].ctl_saturation; ++ break; ++ } ++ return 0; ++} ++ ++int cx25821_set_control(struct cx25821_dev *dev, ++ struct v4l2_control *ctl, int chan_num) ++{ ++ int err; ++ const struct v4l2_queryctrl *ctrl; ++ ++ err = -EINVAL; ++ ++ ctrl = ctrl_by_id(ctl->id); ++ ++ if (NULL == ctrl) ++ return err; ++ ++ switch (ctrl->type) { ++ case V4L2_CTRL_TYPE_BOOLEAN: ++ case V4L2_CTRL_TYPE_MENU: ++ case V4L2_CTRL_TYPE_INTEGER: ++ if (ctl->value < ctrl->minimum) ++ ctl->value = ctrl->minimum; ++ if (ctl->value > ctrl->maximum) ++ ctl->value = ctrl->maximum; ++ break; ++ default: ++ /* nothing */ ; ++ } ++ ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ dev->channels[chan_num].ctl_bright = ctl->value; ++ medusa_set_brightness(dev, ctl->value, chan_num); ++ break; ++ case V4L2_CID_HUE: ++ dev->channels[chan_num].ctl_hue = ctl->value; ++ medusa_set_hue(dev, ctl->value, chan_num); ++ break; ++ case V4L2_CID_CONTRAST: ++ dev->channels[chan_num].ctl_contrast = ctl->value; ++ medusa_set_contrast(dev, ctl->value, chan_num); ++ break; ++ case V4L2_CID_SATURATION: ++ dev->channels[chan_num].ctl_saturation = ctl->value; ++ medusa_set_saturation(dev, ctl->value, chan_num); ++ break; ++ } ++ ++ err = 0; ++ ++ return err; ++} ++ ++static void cx25821_init_controls(struct cx25821_dev *dev, int chan_num) ++{ ++ struct v4l2_control ctrl; ++ int i; ++ for (i = 0; i < CX25821_CTLS; i++) { ++ ctrl.id = cx25821_ctls[i].id; ++ ctrl.value = cx25821_ctls[i].default_value; ++ ++ cx25821_set_control(dev, &ctrl, chan_num); ++ } ++} ++ ++int cx25821_vidioc_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *cropcap) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ ++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ cropcap->bounds.top = 0; ++ cropcap->bounds.left = 0; ++ cropcap->bounds.width = 720; ++ cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480; ++ cropcap->pixelaspect.numerator = ++ dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10; ++ cropcap->pixelaspect.denominator = ++ dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11; ++ cropcap->defrect = cropcap->bounds; ++ return 0; ++} ++ ++int cx25821_vidioc_s_crop(struct file *file, void *priv, const struct v4l2_crop *crop) ++{ ++ struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; ++ struct cx25821_fh *fh = priv; ++ int err; ++ ++ if (fh) { ++ err = v4l2_prio_check(&dev->channels[fh->channel_id].prio, ++ fh->prio); ++ if (0 != err) ++ return err; ++ } ++ /* cx25821_vidioc_s_crop not supported */ ++ return -EINVAL; ++} ++ ++int cx25821_vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) ++{ ++ /* cx25821_vidioc_g_crop not supported */ ++ return -EINVAL; ++} ++ ++int cx25821_vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) ++{ ++ /* medusa does not support video standard sensing of current input */ ++ *norm = CX25821_NORMS; ++ ++ return 0; ++} ++ ++int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm) ++{ ++ if (tvnorm == V4L2_STD_PAL_BG) { ++ if (width == 352 || width == 720) ++ return 1; ++ else ++ return 0; ++ } ++ ++ if (tvnorm == V4L2_STD_NTSC_M) { ++ if (width == 320 || width == 352 || width == 720) ++ return 1; ++ else ++ return 0; ++ } ++ return 0; ++} ++ ++int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm) ++{ ++ if (tvnorm == V4L2_STD_PAL_BG) { ++ if (height == 576 || height == 288) ++ return 1; ++ else ++ return 0; ++ } ++ ++ if (tvnorm == V4L2_STD_NTSC_M) { ++ if (height == 480 || height == 240) ++ return 1; ++ else ++ return 0; ++ } ++ ++ return 0; ++} ++ ++static long video_ioctl_upstream9(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_dev *dev = fh->dev; ++ int command = 0; ++ struct upstream_user_struct *data_from_user; ++ ++ data_from_user = (struct upstream_user_struct *)arg; ++ ++ if (!data_from_user) { ++ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); ++ return 0; ++ } ++ ++ command = data_from_user->command; ++ ++ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) ++ return 0; ++ ++ dev->input_filename = data_from_user->input_filename; ++ dev->input_audiofilename = data_from_user->input_filename; ++ dev->vid_stdname = data_from_user->vid_stdname; ++ dev->pixel_format = data_from_user->pixel_format; ++ dev->channel_select = data_from_user->channel_select; ++ dev->command = data_from_user->command; ++ ++ switch (command) { ++ case UPSTREAM_START_VIDEO: ++ cx25821_start_upstream_video_ch1(dev, data_from_user); ++ break; ++ ++ case UPSTREAM_STOP_VIDEO: ++ cx25821_stop_upstream_video_ch1(dev); ++ break; ++ } ++ ++ return 0; ++} ++ ++static long video_ioctl_upstream10(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_dev *dev = fh->dev; ++ int command = 0; ++ struct upstream_user_struct *data_from_user; ++ ++ data_from_user = (struct upstream_user_struct *)arg; ++ ++ if (!data_from_user) { ++ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); ++ return 0; ++ } ++ ++ command = data_from_user->command; ++ ++ if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) ++ return 0; ++ ++ dev->input_filename_ch2 = data_from_user->input_filename; ++ dev->input_audiofilename = data_from_user->input_filename; ++ dev->vid_stdname_ch2 = data_from_user->vid_stdname; ++ dev->pixel_format_ch2 = data_from_user->pixel_format; ++ dev->channel_select_ch2 = data_from_user->channel_select; ++ dev->command_ch2 = data_from_user->command; ++ ++ switch (command) { ++ case UPSTREAM_START_VIDEO: ++ cx25821_start_upstream_video_ch2(dev, data_from_user); ++ break; ++ ++ case UPSTREAM_STOP_VIDEO: ++ cx25821_stop_upstream_video_ch2(dev); ++ break; ++ } ++ ++ return 0; ++} ++ ++static long video_ioctl_upstream11(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_dev *dev = fh->dev; ++ int command = 0; ++ struct upstream_user_struct *data_from_user; ++ ++ data_from_user = (struct upstream_user_struct *)arg; ++ ++ if (!data_from_user) { ++ pr_err("%s(): Upstream data is INVALID. Returning\n", __func__); ++ return 0; ++ } ++ ++ command = data_from_user->command; ++ ++ if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO) ++ return 0; ++ ++ dev->input_filename = data_from_user->input_filename; ++ dev->input_audiofilename = data_from_user->input_filename; ++ dev->vid_stdname = data_from_user->vid_stdname; ++ dev->pixel_format = data_from_user->pixel_format; ++ dev->channel_select = data_from_user->channel_select; ++ dev->command = data_from_user->command; ++ ++ switch (command) { ++ case UPSTREAM_START_AUDIO: ++ cx25821_start_upstream_audio(dev, data_from_user); ++ break; ++ ++ case UPSTREAM_STOP_AUDIO: ++ cx25821_stop_upstream_audio(dev); ++ break; ++ } ++ ++ return 0; ++} ++ ++static long video_ioctl_set(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct cx25821_fh *fh = file->private_data; ++ struct cx25821_dev *dev = fh->dev; ++ struct downstream_user_struct *data_from_user; ++ int command; ++ int width = 720; ++ int selected_channel = 0; ++ int pix_format = 0; ++ int i = 0; ++ int cif_enable = 0; ++ int cif_width = 0; ++ ++ data_from_user = (struct downstream_user_struct *)arg; ++ ++ if (!data_from_user) { ++ pr_err("%s(): User data is INVALID. Returning\n", __func__); ++ return 0; ++ } ++ ++ command = data_from_user->command; ++ ++ if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT ++ && command != ENABLE_CIF_RESOLUTION && command != REG_READ ++ && command != REG_WRITE && command != MEDUSA_READ ++ && command != MEDUSA_WRITE) { ++ return 0; ++ } ++ ++ switch (command) { ++ case SET_VIDEO_STD: ++ if (!strcmp(data_from_user->vid_stdname, "PAL")) ++ dev->tvnorm = V4L2_STD_PAL_BG; ++ else ++ dev->tvnorm = V4L2_STD_NTSC_M; ++ medusa_set_videostandard(dev); ++ break; ++ ++ case SET_PIXEL_FORMAT: ++ selected_channel = data_from_user->decoder_select; ++ pix_format = data_from_user->pixel_format; ++ ++ if (!(selected_channel <= 7 && selected_channel >= 0)) { ++ selected_channel -= 4; ++ selected_channel = selected_channel % 8; ++ } ++ ++ if (selected_channel >= 0) ++ cx25821_set_pixel_format(dev, selected_channel, ++ pix_format); ++ ++ break; ++ ++ case ENABLE_CIF_RESOLUTION: ++ selected_channel = data_from_user->decoder_select; ++ cif_enable = data_from_user->cif_resolution_enable; ++ cif_width = data_from_user->cif_width; ++ ++ if (cif_enable) { ++ if (dev->tvnorm & V4L2_STD_PAL_BG ++ || dev->tvnorm & V4L2_STD_PAL_DK) { ++ width = 352; ++ } else { ++ width = cif_width; ++ if (cif_width != 320 && cif_width != 352) ++ width = 320; ++ } ++ } ++ ++ if (!(selected_channel <= 7 && selected_channel >= 0)) { ++ selected_channel -= 4; ++ selected_channel = selected_channel % 8; ++ } ++ ++ if (selected_channel <= 7 && selected_channel >= 0) { ++ dev->channels[selected_channel].use_cif_resolution = ++ cif_enable; ++ dev->channels[selected_channel].cif_width = width; ++ } else { ++ for (i = 0; i < VID_CHANNEL_NUM; i++) { ++ dev->channels[i].use_cif_resolution = ++ cif_enable; ++ dev->channels[i].cif_width = width; ++ } ++ } ++ ++ medusa_set_resolution(dev, width, selected_channel); ++ break; ++ case REG_READ: ++ data_from_user->reg_data = cx_read(data_from_user->reg_address); ++ break; ++ case REG_WRITE: ++ cx_write(data_from_user->reg_address, data_from_user->reg_data); ++ break; ++ case MEDUSA_READ: ++ cx25821_i2c_read(&dev->i2c_bus[0], ++ (u16) data_from_user->reg_address, ++ &data_from_user->reg_data); ++ break; ++ case MEDUSA_WRITE: ++ cx25821_i2c_write(&dev->i2c_bus[0], ++ (u16) data_from_user->reg_address, ++ data_from_user->reg_data); ++ break; ++ } ++ ++ return 0; ++} ++ ++static long cx25821_video_ioctl(struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ int ret = 0; ++ ++ struct cx25821_fh *fh = file->private_data; ++ ++ /* check to see if it's the video upstream */ ++ if (fh->channel_id == SRAM_CH09) { ++ ret = video_ioctl_upstream9(file, cmd, arg); ++ return ret; ++ } else if (fh->channel_id == SRAM_CH10) { ++ ret = video_ioctl_upstream10(file, cmd, arg); ++ return ret; ++ } else if (fh->channel_id == SRAM_CH11) { ++ ret = video_ioctl_upstream11(file, cmd, arg); ++ ret = video_ioctl_set(file, cmd, arg); ++ return ret; ++ } ++ ++ return video_ioctl2(file, cmd, arg); ++} ++ ++/* exported stuff */ ++static const struct v4l2_file_operations video_fops = { ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .release = video_release, ++ .read = video_read, ++ .poll = video_poll, ++ .mmap = cx25821_video_mmap, ++ .ioctl = cx25821_video_ioctl, ++}; ++ ++static const struct v4l2_ioctl_ops video_ioctl_ops = { ++ .vidioc_querycap = cx25821_vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_reqbufs = cx25821_vidioc_reqbufs, ++ .vidioc_querybuf = cx25821_vidioc_querybuf, ++ .vidioc_qbuf = cx25821_vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++#ifdef TUNER_FLAG ++ .vidioc_s_std = cx25821_vidioc_s_std, ++ .vidioc_querystd = cx25821_vidioc_querystd, ++#endif ++ .vidioc_cropcap = cx25821_vidioc_cropcap, ++ .vidioc_s_crop = cx25821_vidioc_s_crop, ++ .vidioc_g_crop = cx25821_vidioc_g_crop, ++ .vidioc_enum_input = cx25821_vidioc_enum_input, ++ .vidioc_g_input = cx25821_vidioc_g_input, ++ .vidioc_s_input = cx25821_vidioc_s_input, ++ .vidioc_g_ctrl = cx25821_vidioc_g_ctrl, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_queryctrl = cx25821_vidioc_queryctrl, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_log_status = vidioc_log_status, ++ .vidioc_g_priority = cx25821_vidioc_g_priority, ++ .vidioc_s_priority = cx25821_vidioc_s_priority, ++#ifdef TUNER_FLAG ++ .vidioc_g_tuner = cx25821_vidioc_g_tuner, ++ .vidioc_s_tuner = cx25821_vidioc_s_tuner, ++ .vidioc_g_frequency = cx25821_vidioc_g_frequency, ++ .vidioc_s_frequency = cx25821_vidioc_s_frequency, ++#endif ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = cx25821_vidioc_g_register, ++ .vidioc_s_register = cx25821_vidioc_s_register, ++#endif ++}; ++ ++struct video_device cx25821_videoioctl_template = { ++ .name = "cx25821-videoioctl", ++ .fops = &video_fops, ++ .ioctl_ops = &video_ioctl_ops, ++ .tvnorms = CX25821_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++}; +diff --git a/drivers/media/pci/cx25821/cx25821-video.h b/drivers/media/pci/cx25821/cx25821-video.h +new file mode 100644 +index 0000000..c265e35 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821-video.h +@@ -0,0 +1,186 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef CX25821_VIDEO_H_ ++#define CX25821_VIDEO_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx25821.h" ++#include ++#include ++ ++#define TUNER_FLAG ++ ++#define VIDEO_DEBUG 0 ++ ++#define dprintk(level, fmt, arg...) \ ++do { \ ++ if (VIDEO_DEBUG >= level) \ ++ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ##arg); \ ++} while (0) ++ ++/* For IOCTL to identify running upstream */ ++#define UPSTREAM_START_VIDEO 700 ++#define UPSTREAM_STOP_VIDEO 701 ++#define UPSTREAM_START_AUDIO 702 ++#define UPSTREAM_STOP_AUDIO 703 ++#define UPSTREAM_DUMP_REGISTERS 702 ++#define SET_VIDEO_STD 800 ++#define SET_PIXEL_FORMAT 1000 ++#define ENABLE_CIF_RESOLUTION 1001 ++ ++#define REG_READ 900 ++#define REG_WRITE 901 ++#define MEDUSA_READ 910 ++#define MEDUSA_WRITE 911 ++ ++extern struct sram_channel *channel0; ++extern struct sram_channel *channel1; ++extern struct sram_channel *channel2; ++extern struct sram_channel *channel3; ++extern struct sram_channel *channel4; ++extern struct sram_channel *channel5; ++extern struct sram_channel *channel6; ++extern struct sram_channel *channel7; ++extern struct sram_channel *channel9; ++extern struct sram_channel *channel10; ++extern struct sram_channel *channel11; ++extern struct video_device cx25821_videoioctl_template; ++/* extern const u32 *ctrl_classes[]; */ ++ ++extern unsigned int vid_limit; ++ ++#define FORMAT_FLAGS_PACKED 0x01 ++extern struct cx25821_fmt formats[]; ++extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); ++extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; ++ ++extern void cx25821_video_wakeup(struct cx25821_dev *dev, ++ struct cx25821_dmaqueue *q, u32 count); ++ ++#ifdef TUNER_FLAG ++extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); ++#endif ++ ++extern int cx25821_res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, ++ unsigned int bit); ++extern int cx25821_res_check(struct cx25821_fh *fh, unsigned int bit); ++extern int cx25821_res_locked(struct cx25821_fh *fh, unsigned int bit); ++extern void cx25821_res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, ++ unsigned int bits); ++extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); ++extern int cx25821_start_video_dma(struct cx25821_dev *dev, ++ struct cx25821_dmaqueue *q, ++ struct cx25821_buffer *buf, ++ struct sram_channel *channel); ++ ++extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, ++ unsigned int height, enum v4l2_field field); ++extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); ++extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); ++extern int cx25821_video_register(struct cx25821_dev *dev); ++extern int cx25821_get_format_size(void); ++ ++extern int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, ++ unsigned int *size); ++extern int cx25821_buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field); ++extern void cx25821_buffer_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb); ++extern struct videobuf_queue *get_queue(struct cx25821_fh *fh); ++extern int cx25821_get_resource(struct cx25821_fh *fh, int resource); ++extern int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma); ++extern int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f); ++extern int cx25821_vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap); ++extern int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f); ++extern int cx25821_vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p); ++extern int cx25821_vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *p); ++extern int cx25821_vidioc_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p); ++extern int cx25821_vidioc_s_std(struct file *file, void *priv, ++ v4l2_std_id *tvnorms); ++extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i); ++extern int cx25821_vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i); ++extern int cx25821_vidioc_g_input(struct file *file, void *priv, ++ unsigned int *i); ++extern int cx25821_vidioc_s_input(struct file *file, void *priv, ++ unsigned int i); ++extern int cx25821_vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl); ++extern int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f); ++extern int cx25821_vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f); ++extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f); ++extern int cx25821_vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f); ++extern int cx25821_vidioc_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg); ++extern int cx25821_vidioc_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg); ++extern int cx25821_vidioc_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t); ++extern int cx25821_vidioc_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t); ++ ++extern int cx25821_is_valid_width(u32 width, v4l2_std_id tvnorm); ++extern int cx25821_is_valid_height(u32 height, v4l2_std_id tvnorm); ++ ++extern int cx25821_vidioc_g_priority(struct file *file, void *f, ++ enum v4l2_priority *p); ++extern int cx25821_vidioc_s_priority(struct file *file, void *f, ++ enum v4l2_priority prio); ++ ++extern int cx25821_vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *qctrl); ++extern int cx25821_set_control(struct cx25821_dev *dev, ++ struct v4l2_control *ctrl, int chan_num); ++ ++extern int cx25821_vidioc_cropcap(struct file *file, void *fh, ++ struct v4l2_cropcap *cropcap); ++extern int cx25821_vidioc_s_crop(struct file *file, void *priv, ++ const struct v4l2_crop *crop); ++extern int cx25821_vidioc_g_crop(struct file *file, void *priv, ++ struct v4l2_crop *crop); ++ ++extern int cx25821_vidioc_querystd(struct file *file, void *priv, ++ v4l2_std_id *norm); ++#endif +diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h +new file mode 100644 +index 0000000..8a9c0c8 +--- /dev/null ++++ b/drivers/media/pci/cx25821/cx25821.h +@@ -0,0 +1,615 @@ ++/* ++ * Driver for the Conexant CX25821 PCIe bridge ++ * ++ * Copyright (C) 2009 Conexant Systems Inc. ++ * Authors , ++ * Based on Steven Toth cx23885 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef CX25821_H_ ++#define CX25821_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "btcx-risc.h" ++#include "cx25821-reg.h" ++#include "cx25821-medusa-reg.h" ++#include "cx25821-sram.h" ++#include "cx25821-audio.h" ++#include "media/cx2341x.h" ++ ++#include ++#include ++ ++#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106) ++ ++#define UNSET (-1U) ++#define NO_SYNC_LINE (-1U) ++ ++#define CX25821_MAXBOARDS 2 ++ ++#define TRUE 1 ++#define FALSE 0 ++#define LINE_SIZE_D1 1440 ++ ++/* Number of decoders and encoders */ ++#define MAX_DECODERS 8 ++#define MAX_ENCODERS 2 ++#define QUAD_DECODERS 4 ++#define MAX_CAMERAS 16 ++ ++/* Max number of inputs by card */ ++#define MAX_CX25821_INPUT 8 ++#define INPUT(nr) (&cx25821_boards[dev->board].input[nr]) ++#define RESOURCE_VIDEO0 1 ++#define RESOURCE_VIDEO1 2 ++#define RESOURCE_VIDEO2 4 ++#define RESOURCE_VIDEO3 8 ++#define RESOURCE_VIDEO4 16 ++#define RESOURCE_VIDEO5 32 ++#define RESOURCE_VIDEO6 64 ++#define RESOURCE_VIDEO7 128 ++#define RESOURCE_VIDEO8 256 ++#define RESOURCE_VIDEO9 512 ++#define RESOURCE_VIDEO10 1024 ++#define RESOURCE_VIDEO11 2048 ++#define RESOURCE_VIDEO_IOCTL 4096 ++ ++#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ ++ ++#define UNKNOWN_BOARD 0 ++#define CX25821_BOARD 1 ++ ++/* Currently supported by the driver */ ++#define CX25821_NORMS (\ ++ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \ ++ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ ++ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_H | \ ++ V4L2_STD_PAL_Nc) ++ ++#define CX25821_BOARD_CONEXANT_ATHENA10 1 ++#define MAX_VID_CHANNEL_NUM 12 ++#define VID_CHANNEL_NUM 8 ++#define CX25821_NR_INPUT 2 ++ ++struct cx25821_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ int depth; ++ int flags; ++ u32 cxformat; ++}; ++ ++struct cx25821_ctrl { ++ struct v4l2_queryctrl v; ++ u32 off; ++ u32 reg; ++ u32 mask; ++ u32 shift; ++}; ++ ++struct cx25821_tvnorm { ++ char *name; ++ v4l2_std_id id; ++ u32 cxiformat; ++ u32 cxoformat; ++}; ++ ++struct cx25821_fh { ++ struct cx25821_dev *dev; ++ enum v4l2_buf_type type; ++ int radio; ++ u32 resources; ++ ++ enum v4l2_priority prio; ++ ++ /* video overlay */ ++ struct v4l2_window win; ++ struct v4l2_clip *clips; ++ unsigned int nclips; ++ ++ /* video capture */ ++ struct cx25821_fmt *fmt; ++ unsigned int width, height; ++ int channel_id; ++ ++ /* vbi capture */ ++ struct videobuf_queue vidq; ++ struct videobuf_queue vbiq; ++ ++ /* H264 Encoder specifics ONLY */ ++ struct videobuf_queue mpegq; ++ atomic_t v4l_reading; ++}; ++ ++enum cx25821_itype { ++ CX25821_VMUX_COMPOSITE = 1, ++ CX25821_VMUX_SVIDEO, ++ CX25821_VMUX_DEBUG, ++ CX25821_RADIO, ++}; ++ ++enum cx25821_src_sel_type { ++ CX25821_SRC_SEL_EXT_656_VIDEO = 0, ++ CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO ++}; ++ ++/* buffer for one video frame */ ++struct cx25821_buffer { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* cx25821 specific */ ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ struct cx25821_fmt *fmt; ++ u32 count; ++}; ++ ++struct cx25821_input { ++ enum cx25821_itype type; ++ unsigned int vmux; ++ u32 gpio0, gpio1, gpio2, gpio3; ++}; ++ ++enum port { ++ CX25821_UNDEFINED = 0, ++ CX25821_RAW, ++ CX25821_264 ++}; ++ ++struct cx25821_board { ++ const char *name; ++ enum port porta; ++ enum port portb; ++ enum port portc; ++ unsigned int tuner_type; ++ unsigned int radio_type; ++ unsigned char tuner_addr; ++ unsigned char radio_addr; ++ ++ u32 clk_freq; ++ struct cx25821_input input[CX25821_NR_INPUT]; ++}; ++ ++struct cx25821_subid { ++ u16 subvendor; ++ u16 subdevice; ++ u32 card; ++}; ++ ++struct cx25821_i2c { ++ struct cx25821_dev *dev; ++ ++ int nr; ++ ++ /* i2c i/o */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_client i2c_client; ++ u32 i2c_rc; ++ ++ /* cx25821 registers used for raw addess */ ++ u32 i2c_period; ++ u32 reg_ctrl; ++ u32 reg_stat; ++ u32 reg_addr; ++ u32 reg_rdata; ++ u32 reg_wdata; ++}; ++ ++struct cx25821_dmaqueue { ++ struct list_head active; ++ struct list_head queued; ++ struct timer_list timeout; ++ struct btcx_riscmem stopper; ++ u32 count; ++}; ++ ++struct cx25821_data { ++ struct cx25821_dev *dev; ++ struct sram_channel *channel; ++}; ++ ++struct cx25821_channel { ++ struct v4l2_prio_state prio; ++ ++ int ctl_bright; ++ int ctl_contrast; ++ int ctl_hue; ++ int ctl_saturation; ++ struct cx25821_data timeout_data; ++ ++ struct video_device *video_dev; ++ struct cx25821_dmaqueue vidq; ++ ++ struct sram_channel *sram_channels; ++ ++ struct mutex lock; ++ int resources; ++ ++ int pixel_formats; ++ int use_cif_resolution; ++ int cif_width; ++}; ++ ++struct cx25821_dev { ++ struct list_head devlist; ++ atomic_t refcount; ++ struct v4l2_device v4l2_dev; ++ ++ /* pci stuff */ ++ struct pci_dev *pci; ++ unsigned char pci_rev, pci_lat; ++ int pci_bus, pci_slot; ++ u32 base_io_addr; ++ u32 __iomem *lmmio; ++ u8 __iomem *bmmio; ++ int pci_irqmask; ++ int hwrevision; ++ ++ u32 clk_freq; ++ ++ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ ++ struct cx25821_i2c i2c_bus[3]; ++ ++ int nr; ++ struct mutex lock; ++ ++ struct cx25821_channel channels[MAX_VID_CHANNEL_NUM]; ++ ++ /* board details */ ++ unsigned int board; ++ char name[32]; ++ ++ /* Analog video */ ++ u32 resources; ++ unsigned int input; ++ u32 tvaudio; ++ v4l2_std_id tvnorm; ++ unsigned int tuner_type; ++ unsigned char tuner_addr; ++ unsigned int radio_type; ++ unsigned char radio_addr; ++ unsigned int has_radio; ++ unsigned int videc_type; ++ unsigned char videc_addr; ++ unsigned short _max_num_decoders; ++ ++ /* Analog Audio Upstream */ ++ int _audio_is_running; ++ int _audiopixel_format; ++ int _is_first_audio_frame; ++ int _audiofile_status; ++ int _audio_lines_count; ++ int _audioframe_count; ++ int _audio_upstream_channel; ++ int _last_index_irq; /* The last interrupt index processed. */ ++ ++ __le32 *_risc_audio_jmp_addr; ++ __le32 *_risc_virt_start_addr; ++ __le32 *_risc_virt_addr; ++ dma_addr_t _risc_phys_addr; ++ dma_addr_t _risc_phys_start_addr; ++ ++ unsigned int _audiorisc_size; ++ unsigned int _audiodata_buf_size; ++ __le32 *_audiodata_buf_virt_addr; ++ dma_addr_t _audiodata_buf_phys_addr; ++ char *_audiofilename; ++ ++ /* V4l */ ++ u32 freq; ++ struct video_device *vbi_dev; ++ struct video_device *radio_dev; ++ struct video_device *ioctl_dev; ++ ++ spinlock_t slock; ++ ++ /* Video Upstream */ ++ int _line_size; ++ int _prog_cnt; ++ int _pixel_format; ++ int _is_first_frame; ++ int _is_running; ++ int _file_status; ++ int _lines_count; ++ int _frame_count; ++ int _channel_upstream_select; ++ unsigned int _risc_size; ++ ++ __le32 *_dma_virt_start_addr; ++ __le32 *_dma_virt_addr; ++ dma_addr_t _dma_phys_addr; ++ dma_addr_t _dma_phys_start_addr; ++ ++ unsigned int _data_buf_size; ++ __le32 *_data_buf_virt_addr; ++ dma_addr_t _data_buf_phys_addr; ++ char *_filename; ++ char *_defaultname; ++ ++ int _line_size_ch2; ++ int _prog_cnt_ch2; ++ int _pixel_format_ch2; ++ int _is_first_frame_ch2; ++ int _is_running_ch2; ++ int _file_status_ch2; ++ int _lines_count_ch2; ++ int _frame_count_ch2; ++ int _channel2_upstream_select; ++ unsigned int _risc_size_ch2; ++ ++ __le32 *_dma_virt_start_addr_ch2; ++ __le32 *_dma_virt_addr_ch2; ++ dma_addr_t _dma_phys_addr_ch2; ++ dma_addr_t _dma_phys_start_addr_ch2; ++ ++ unsigned int _data_buf_size_ch2; ++ __le32 *_data_buf_virt_addr_ch2; ++ dma_addr_t _data_buf_phys_addr_ch2; ++ char *_filename_ch2; ++ char *_defaultname_ch2; ++ ++ /* MPEG Encoder ONLY settings */ ++ u32 cx23417_mailbox; ++ struct cx2341x_mpeg_params mpeg_params; ++ struct video_device *v4l_device; ++ atomic_t v4l_reader_count; ++ struct cx25821_tvnorm encodernorm; ++ ++ u32 upstream_riscbuf_size; ++ u32 upstream_databuf_size; ++ u32 upstream_riscbuf_size_ch2; ++ u32 upstream_databuf_size_ch2; ++ u32 audio_upstream_riscbuf_size; ++ u32 audio_upstream_databuf_size; ++ int _isNTSC; ++ int _frame_index; ++ int _audioframe_index; ++ struct workqueue_struct *_irq_queues; ++ struct work_struct _irq_work_entry; ++ struct workqueue_struct *_irq_queues_ch2; ++ struct work_struct _irq_work_entry_ch2; ++ struct workqueue_struct *_irq_audio_queues; ++ struct work_struct _audio_work_entry; ++ char *input_filename; ++ char *input_filename_ch2; ++ int _frame_index_ch2; ++ int _isNTSC_ch2; ++ char *vid_stdname_ch2; ++ int pixel_format_ch2; ++ int channel_select_ch2; ++ int command_ch2; ++ char *input_audiofilename; ++ char *vid_stdname; ++ int pixel_format; ++ int channel_select; ++ int command; ++ int channel_opened; ++}; ++ ++struct upstream_user_struct { ++ char *input_filename; ++ char *vid_stdname; ++ int pixel_format; ++ int channel_select; ++ int command; ++}; ++ ++struct downstream_user_struct { ++ char *vid_stdname; ++ int pixel_format; ++ int cif_resolution_enable; ++ int cif_width; ++ int decoder_select; ++ int command; ++ int reg_address; ++ int reg_data; ++}; ++ ++extern struct upstream_user_struct *up_data; ++ ++static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev); ++} ++ ++#define cx25821_call_all(dev, o, f, args...) \ ++ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) ++ ++extern struct list_head cx25821_devlist; ++extern struct mutex cx25821_devlist_mutex; ++ ++extern struct cx25821_board cx25821_boards[]; ++extern struct cx25821_subid cx25821_subids[]; ++ ++#define SRAM_CH00 0 /* Video A */ ++#define SRAM_CH01 1 /* Video B */ ++#define SRAM_CH02 2 /* Video C */ ++#define SRAM_CH03 3 /* Video D */ ++#define SRAM_CH04 4 /* Video E */ ++#define SRAM_CH05 5 /* Video F */ ++#define SRAM_CH06 6 /* Video G */ ++#define SRAM_CH07 7 /* Video H */ ++ ++#define SRAM_CH08 8 /* Audio A */ ++#define SRAM_CH09 9 /* Video Upstream I */ ++#define SRAM_CH10 10 /* Video Upstream J */ ++#define SRAM_CH11 11 /* Audio Upstream AUD_CHANNEL_B */ ++ ++#define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09 ++#define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10 ++#define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11 ++#define VIDEO_IOCTL_CH 11 ++ ++struct sram_channel { ++ char *name; ++ u32 i; ++ u32 cmds_start; ++ u32 ctrl_start; ++ u32 cdt; ++ u32 fifo_start; ++ u32 fifo_size; ++ u32 ptr1_reg; ++ u32 ptr2_reg; ++ u32 cnt1_reg; ++ u32 cnt2_reg; ++ u32 int_msk; ++ u32 int_stat; ++ u32 int_mstat; ++ u32 dma_ctl; ++ u32 gpcnt_ctl; ++ u32 gpcnt; ++ u32 aud_length; ++ u32 aud_cfg; ++ u32 fld_aud_fifo_en; ++ u32 fld_aud_risc_en; ++ ++ /* For Upstream Video */ ++ u32 vid_fmt_ctl; ++ u32 vid_active_ctl1; ++ u32 vid_active_ctl2; ++ u32 vid_cdt_size; ++ ++ u32 vip_ctl; ++ u32 pix_frmt; ++ u32 jumponly; ++ u32 irq_bit; ++}; ++extern struct sram_channel cx25821_sram_channels[]; ++ ++#define STATUS_SUCCESS 0 ++#define STATUS_UNSUCCESSFUL -1 ++ ++#define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) ++#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2)) ++ ++#define cx_andor(reg, mask, value) \ ++ writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ ++ ((value) & (mask)), dev->lmmio+((reg)>>2)) ++ ++#define cx_set(reg, bit) cx_andor((reg), (bit), (bit)) ++#define cx_clear(reg, bit) cx_andor((reg), (bit), 0) ++ ++#define Set_GPIO_Bit(Bit) (1 << Bit) ++#define Clear_GPIO_Bit(Bit) (~(1 << Bit)) ++ ++#define CX25821_ERR(fmt, args...) \ ++ pr_err("(%d): " fmt, dev->board, ##args) ++#define CX25821_WARN(fmt, args...) \ ++ pr_warn("(%d): " fmt, dev->board, ##args) ++#define CX25821_INFO(fmt, args...) \ ++ pr_info("(%d): " fmt, dev->board, ##args) ++ ++extern int cx25821_i2c_register(struct cx25821_i2c *bus); ++extern void cx25821_card_setup(struct cx25821_dev *dev); ++extern int cx25821_ir_init(struct cx25821_dev *dev); ++extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value); ++extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value); ++extern int cx25821_i2c_unregister(struct cx25821_i2c *bus); ++extern void cx25821_gpio_init(struct cx25821_dev *dev); ++extern void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, ++ int pin_number, int pin_logic_value); ++ ++extern int medusa_video_init(struct cx25821_dev *dev); ++extern int medusa_set_videostandard(struct cx25821_dev *dev); ++extern void medusa_set_resolution(struct cx25821_dev *dev, int width, ++ int decoder_select); ++extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness, ++ int decoder); ++extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast, ++ int decoder); ++extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder); ++extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation, ++ int decoder); ++ ++extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, ++ struct sram_channel *ch, unsigned int bpl, ++ u32 risc); ++ ++extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int top_offset, ++ unsigned int bottom_offset, ++ unsigned int bpl, ++ unsigned int padding, unsigned int lines); ++extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, ++ struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int bpl, ++ unsigned int lines, unsigned int lpi); ++extern void cx25821_free_buffer(struct videobuf_queue *q, ++ struct cx25821_buffer *buf); ++extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value); ++extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, ++ struct sram_channel *ch); ++extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, ++ struct sram_channel *ch); ++ ++extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci); ++extern void cx25821_print_irqbits(char *name, char *tag, char **strings, ++ int len, u32 bits, u32 mask); ++extern void cx25821_dev_unregister(struct cx25821_dev *dev); ++extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc); ++ ++extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, ++ int channel_select, int pixel_format); ++extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, ++ int channel_select, int pixel_format); ++extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, ++ int channel_select); ++extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev); ++extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev); ++extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); ++extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, ++ struct upstream_user_struct ++ *up_data); ++extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, ++ struct upstream_user_struct ++ *up_data); ++extern void cx25821_start_upstream_audio(struct cx25821_dev *dev, ++ struct upstream_user_struct *up_data); ++extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev); ++extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev); ++extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); ++extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, ++ struct sram_channel *ch, ++ unsigned int bpl, u32 risc); ++extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, ++ u32 format); ++extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev); ++extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type); ++#endif +diff --git a/drivers/media/pci/cx88/Kconfig b/drivers/media/pci/cx88/Kconfig +new file mode 100644 +index 0000000..bb05eca +--- /dev/null ++++ b/drivers/media/pci/cx88/Kconfig +@@ -0,0 +1,88 @@ ++config VIDEO_CX88 ++ tristate "Conexant 2388x (bt878 successor) support" ++ depends on VIDEO_DEV && PCI && I2C && RC_CORE ++ select I2C_ALGOBIT ++ select VIDEO_BTCX ++ select VIDEOBUF_DMA_SG ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select VIDEO_WM8775 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for Conexant 2388x based ++ TV cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx8800 ++ ++config VIDEO_CX88_ALSA ++ tristate "Conexant 2388x DMA audio support" ++ depends on VIDEO_CX88 && SND ++ select SND_PCM ++ ---help--- ++ This is a video4linux driver for direct (DMA) audio on ++ Conexant 2388x based TV cards using ALSA. ++ ++ It only works with boards with function 01 enabled. ++ To check if your board supports, use lspci -n. ++ If supported, you should see 14f1:8801 or 14f1:8811 ++ PCI device. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx88-alsa. ++ ++config VIDEO_CX88_BLACKBIRD ++ tristate "Blackbird MPEG encoder support (cx2388x + cx23416)" ++ depends on VIDEO_CX88 ++ select VIDEO_CX2341X ++ ---help--- ++ This adds support for MPEG encoder cards based on the ++ Blackbird reference design, using the Conexant 2388x ++ and 23416 chips. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx88-blackbird. ++ ++config VIDEO_CX88_DVB ++ tristate "DVB/ATSC Support for cx2388x based TV cards" ++ depends on VIDEO_CX88 && DVB_CORE ++ select VIDEOBUF_DVB ++ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_OR51132 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX22702 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX24123 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This adds support for DVB/ATSC cards based on the ++ Conexant 2388x chip. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called cx88-dvb. ++ ++config VIDEO_CX88_VP3054 ++ tristate "VP-3054 Secondary I2C Bus Support" ++ default m ++ depends on VIDEO_CX88_DVB && DVB_MT352 ++ ---help--- ++ This adds DVB-T support for cards based on the ++ Conexant 2388x chip and the MT352 demodulator, ++ which also require support for the VP-3054 ++ Secondary I2C bus, such at DNTV Live! DVB-T Pro. ++ ++config VIDEO_CX88_MPEG ++ tristate ++ depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD ++ default y +diff --git a/drivers/media/pci/cx88/Makefile b/drivers/media/pci/cx88/Makefile +new file mode 100644 +index 0000000..d3679c3 +--- /dev/null ++++ b/drivers/media/pci/cx88/Makefile +@@ -0,0 +1,16 @@ ++cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ ++ cx88-dsp.o cx88-input.o ++cx8800-objs := cx88-video.o cx88-vbi.o ++cx8802-objs := cx88-mpeg.o ++ ++obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o ++obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o ++obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o ++obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o ++obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o ++obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o ++ ++ccflags-y += -Idrivers/media/i2c ++ccflags-y += -Idrivers/media/tuners ++ccflags-y += -Idrivers/media/dvb-core ++ccflags-y += -Idrivers/media/dvb-frontends +diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c +new file mode 100644 +index 0000000..d2de1a9 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-alsa.c +@@ -0,0 +1,979 @@ ++/* ++ * ++ * Support for audio capture ++ * PCI function #1 of the cx2388x. ++ * ++ * (c) 2007 Trent Piepho ++ * (c) 2005,2006 Ricardo Cerqueira ++ * (c) 2005 Mauro Carvalho Chehab ++ * Based on a dummy cx88 module by Gerd Knorr ++ * Based on dummy.c by Jaroslav Kysela ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include "cx88-reg.h" ++ ++#define dprintk(level, fmt, arg...) do { \ ++ if (debug + 1 > level) \ ++ printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg);\ ++} while(0) ++ ++#define dprintk_core(level, fmt, arg...) do { \ ++ if (debug + 1 > level) \ ++ printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg);\ ++} while(0) ++ ++/**************************************************************************** ++ Data type declarations - Can be moded to a header file later ++ ****************************************************************************/ ++ ++struct cx88_audio_buffer { ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ struct videobuf_dmabuf dma; ++}; ++ ++struct cx88_audio_dev { ++ struct cx88_core *core; ++ struct cx88_dmaqueue q; ++ ++ /* pci i/o */ ++ struct pci_dev *pci; ++ ++ /* audio controls */ ++ int irq; ++ ++ struct snd_card *card; ++ ++ spinlock_t reg_lock; ++ atomic_t count; ++ ++ unsigned int dma_size; ++ unsigned int period_size; ++ unsigned int num_periods; ++ ++ struct videobuf_dmabuf *dma_risc; ++ ++ struct cx88_audio_buffer *buf; ++ ++ struct snd_pcm_substream *substream; ++}; ++typedef struct cx88_audio_dev snd_cx88_card_t; ++ ++ ++ ++/**************************************************************************** ++ Module global static vars ++ ****************************************************************************/ ++ ++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ ++static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ ++static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; ++ ++module_param_array(enable, bool, NULL, 0444); ++MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled."); ++ ++module_param_array(index, int, NULL, 0444); ++MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s)."); ++ ++ ++/**************************************************************************** ++ Module macros ++ ****************************************************************************/ ++ ++MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards"); ++MODULE_AUTHOR("Ricardo Cerqueira"); ++MODULE_AUTHOR("Mauro Carvalho Chehab "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX88_VERSION); ++ ++MODULE_SUPPORTED_DEVICE("{{Conexant,23881}," ++ "{{Conexant,23882}," ++ "{{Conexant,23883}"); ++static unsigned int debug; ++module_param(debug,int,0644); ++MODULE_PARM_DESC(debug,"enable debug messages"); ++ ++/**************************************************************************** ++ Module specific funtions ++ ****************************************************************************/ ++ ++/* ++ * BOARD Specific: Sets audio DMA ++ */ ++ ++static int _cx88_start_audio_dma(snd_cx88_card_t *chip) ++{ ++ struct cx88_audio_buffer *buf = chip->buf; ++ struct cx88_core *core=chip->core; ++ const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; ++ ++ /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ ++ cx_clear(MO_AUD_DMACNTRL, 0x11); ++ ++ /* setup fifo + format - out channel */ ++ cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma); ++ ++ /* sets bpl size */ ++ cx_write(MO_AUDD_LNGTH, buf->bpl); ++ ++ /* reset counter */ ++ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); ++ atomic_set(&chip->count, 0); ++ ++ dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " ++ "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1, ++ chip->num_periods, buf->bpl * chip->num_periods); ++ ++ /* Enables corresponding bits at AUD_INT_STAT */ ++ cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | ++ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); ++ ++ /* Clean any pending interrupt bits already set */ ++ cx_write(MO_AUD_INTSTAT, ~0); ++ ++ /* enable audio irqs */ ++ cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT); ++ ++ /* start dma */ ++ cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ ++ cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */ ++ ++ if (debug) ++ cx88_sram_channel_dump(chip->core, audio_ch); ++ ++ return 0; ++} ++ ++/* ++ * BOARD Specific: Resets audio DMA ++ */ ++static int _cx88_stop_audio_dma(snd_cx88_card_t *chip) ++{ ++ struct cx88_core *core=chip->core; ++ dprintk(1, "Stopping audio DMA\n"); ++ ++ /* stop dma */ ++ cx_clear(MO_AUD_DMACNTRL, 0x11); ++ ++ /* disable irqs */ ++ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); ++ cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | ++ AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); ++ ++ if (debug) ++ cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); ++ ++ return 0; ++} ++ ++#define MAX_IRQ_LOOP 50 ++ ++/* ++ * BOARD Specific: IRQ dma bits ++ */ ++static const char *cx88_aud_irqs[32] = { ++ "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ ++ NULL, /* reserved */ ++ "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ ++ NULL, /* reserved */ ++ "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ ++ NULL, /* reserved */ ++ "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ ++ NULL, /* reserved */ ++ "opc_err", "par_err", "rip_err", /* 16-18 */ ++ "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ ++}; ++ ++/* ++ * BOARD Specific: Threats IRQ audio specific calls ++ */ ++static void cx8801_aud_irq(snd_cx88_card_t *chip) ++{ ++ struct cx88_core *core = chip->core; ++ u32 status, mask; ++ ++ status = cx_read(MO_AUD_INTSTAT); ++ mask = cx_read(MO_AUD_INTMSK); ++ if (0 == (status & mask)) ++ return; ++ cx_write(MO_AUD_INTSTAT, status); ++ if (debug > 1 || (status & mask & ~0xff)) ++ cx88_print_irqbits(core->name, "irq aud", ++ cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), ++ status, mask); ++ /* risc op code error */ ++ if (status & AUD_INT_OPC_ERR) { ++ printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name); ++ cx_clear(MO_AUD_DMACNTRL, 0x11); ++ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]); ++ } ++ if (status & AUD_INT_DN_SYNC) { ++ dprintk(1, "Downstream sync error\n"); ++ cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); ++ return; ++ } ++ /* risc1 downstream */ ++ if (status & AUD_INT_DN_RISCI1) { ++ atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT)); ++ snd_pcm_period_elapsed(chip->substream); ++ } ++ /* FIXME: Any other status should deserve a special handling? */ ++} ++ ++/* ++ * BOARD Specific: Handles IRQ calls ++ */ ++static irqreturn_t cx8801_irq(int irq, void *dev_id) ++{ ++ snd_cx88_card_t *chip = dev_id; ++ struct cx88_core *core = chip->core; ++ u32 status; ++ int loop, handled = 0; ++ ++ for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { ++ status = cx_read(MO_PCI_INTSTAT) & ++ (core->pci_irqmask | PCI_INT_AUDINT); ++ if (0 == status) ++ goto out; ++ dprintk(3, "cx8801_irq loop %d/%d, status %x\n", ++ loop, MAX_IRQ_LOOP, status); ++ handled = 1; ++ cx_write(MO_PCI_INTSTAT, status); ++ ++ if (status & core->pci_irqmask) ++ cx88_core_irq(core, status); ++ if (status & PCI_INT_AUDINT) ++ cx8801_aud_irq(chip); ++ } ++ ++ if (MAX_IRQ_LOOP == loop) { ++ printk(KERN_ERR ++ "%s/1: IRQ loop detected, disabling interrupts\n", ++ core->name); ++ cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); ++ } ++ ++ out: ++ return IRQ_RETVAL(handled); ++} ++ ++ ++static int dsp_buffer_free(snd_cx88_card_t *chip) ++{ ++ BUG_ON(!chip->dma_size); ++ ++ dprintk(2,"Freeing buffer\n"); ++ videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); ++ videobuf_dma_free(chip->dma_risc); ++ btcx_riscmem_free(chip->pci,&chip->buf->risc); ++ kfree(chip->buf); ++ ++ chip->dma_risc = NULL; ++ chip->dma_size = 0; ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ ALSA PCM Interface ++ ****************************************************************************/ ++ ++/* ++ * Digital hardware definition ++ */ ++#define DEFAULT_FIFO_SIZE 4096 ++static const struct snd_pcm_hardware snd_cx88_digital_hw = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ ++ .rates = SNDRV_PCM_RATE_48000, ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ /* Analog audio output will be full of clicks and pops if there ++ are not exactly four lines in the SRAM FIFO buffer. */ ++ .period_bytes_min = DEFAULT_FIFO_SIZE/4, ++ .period_bytes_max = DEFAULT_FIFO_SIZE/4, ++ .periods_min = 1, ++ .periods_max = 1024, ++ .buffer_bytes_max = (1024*1024), ++}; ++ ++/* ++ * audio pcm capture open callback ++ */ ++static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) ++{ ++ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ ++ if (!chip) { ++ printk(KERN_ERR "BUG: cx88 can't find device struct." ++ " Can't proceed with open\n"); ++ return -ENODEV; ++ } ++ ++ err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) ++ goto _error; ++ ++ chip->substream = substream; ++ ++ runtime->hw = snd_cx88_digital_hw; ++ ++ if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) { ++ unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4; ++ bpl &= ~7; /* must be multiple of 8 */ ++ runtime->hw.period_bytes_min = bpl; ++ runtime->hw.period_bytes_max = bpl; ++ } ++ ++ return 0; ++_error: ++ dprintk(1,"Error opening PCM!\n"); ++ return err; ++} ++ ++/* ++ * audio close callback ++ */ ++static int snd_cx88_close(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * hw_params callback ++ */ ++static int snd_cx88_hw_params(struct snd_pcm_substream * substream, ++ struct snd_pcm_hw_params * hw_params) ++{ ++ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); ++ struct videobuf_dmabuf *dma; ++ ++ struct cx88_audio_buffer *buf; ++ int ret; ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ chip->period_size = params_period_bytes(hw_params); ++ chip->num_periods = params_periods(hw_params); ++ chip->dma_size = chip->period_size * params_periods(hw_params); ++ ++ BUG_ON(!chip->dma_size); ++ BUG_ON(chip->num_periods & (chip->num_periods-1)); ++ ++ buf = kzalloc(sizeof(*buf), GFP_KERNEL); ++ if (NULL == buf) ++ return -ENOMEM; ++ ++ buf->bpl = chip->period_size; ++ ++ dma = &buf->dma; ++ videobuf_dma_init(dma); ++ ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, ++ (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); ++ if (ret < 0) ++ goto error; ++ ++ ret = videobuf_dma_map(&chip->pci->dev, dma); ++ if (ret < 0) ++ goto error; ++ ++ ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, ++ chip->period_size, chip->num_periods, 1); ++ if (ret < 0) ++ goto error; ++ ++ /* Loop back to start of program */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ ++ chip->buf = buf; ++ chip->dma_risc = dma; ++ ++ substream->runtime->dma_area = chip->dma_risc->vaddr; ++ substream->runtime->dma_bytes = chip->dma_size; ++ substream->runtime->dma_addr = 0; ++ return 0; ++ ++error: ++ kfree(buf); ++ return ret; ++} ++ ++/* ++ * hw free callback ++ */ ++static int snd_cx88_hw_free(struct snd_pcm_substream * substream) ++{ ++ ++ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); ++ ++ if (substream->runtime->dma_area) { ++ dsp_buffer_free(chip); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * prepare callback ++ */ ++static int snd_cx88_prepare(struct snd_pcm_substream *substream) ++{ ++ return 0; ++} ++ ++/* ++ * trigger callback ++ */ ++static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); ++ int err; ++ ++ /* Local interrupts are already disabled by ALSA */ ++ spin_lock(&chip->reg_lock); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ err=_cx88_start_audio_dma(chip); ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ err=_cx88_stop_audio_dma(chip); ++ break; ++ default: ++ err=-EINVAL; ++ break; ++ } ++ ++ spin_unlock(&chip->reg_lock); ++ ++ return err; ++} ++ ++/* ++ * pointer callback ++ */ ++static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) ++{ ++ snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ u16 count; ++ ++ count = atomic_read(&chip->count); ++ ++// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__, ++// count, new, count & (runtime->periods-1), ++// runtime->period_size * (count & (runtime->periods-1))); ++ return runtime->period_size * (count & (runtime->periods-1)); ++} ++ ++/* ++ * page callback (needed for mmap) ++ */ ++static struct page *snd_cx88_page(struct snd_pcm_substream *substream, ++ unsigned long offset) ++{ ++ void *pageptr = substream->runtime->dma_area + offset; ++ return vmalloc_to_page(pageptr); ++} ++ ++/* ++ * operators ++ */ ++static struct snd_pcm_ops snd_cx88_pcm_ops = { ++ .open = snd_cx88_pcm_open, ++ .close = snd_cx88_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_cx88_hw_params, ++ .hw_free = snd_cx88_hw_free, ++ .prepare = snd_cx88_prepare, ++ .trigger = snd_cx88_card_trigger, ++ .pointer = snd_cx88_pointer, ++ .page = snd_cx88_page, ++}; ++ ++/* ++ * create a PCM device ++ */ ++static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name) ++{ ++ int err; ++ struct snd_pcm *pcm; ++ ++ err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); ++ if (err < 0) ++ return err; ++ pcm->private_data = chip; ++ strcpy(pcm->name, name); ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ CONTROL INTERFACE ++ ****************************************************************************/ ++static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *info) ++{ ++ info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ info->count = 2; ++ info->value.integer.min = 0; ++ info->value.integer.max = 0x3f; ++ ++ return 0; ++} ++ ++static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core=chip->core; ++ int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f), ++ bal = cx_read(AUD_BAL_CTL); ++ ++ value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol; ++ vol -= (bal & 0x3f); ++ value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol; ++ ++ return 0; ++} ++ ++static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core = chip->core; ++ int left = value->value.integer.value[0]; ++ int right = value->value.integer.value[1]; ++ int v, b; ++ ++ /* Pass volume & balance onto any WM8775 */ ++ if (left >= right) { ++ v = left << 10; ++ b = left ? (0x8000 * right) / left : 0x8000; ++ } else { ++ v = right << 10; ++ b = right ? 0xffff - (0x8000 * left) / right : 0x8000; ++ } ++ wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v); ++ wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b); ++} ++ ++/* OK - TODO: test it */ ++static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core=chip->core; ++ int left, right, v, b; ++ int changed = 0; ++ u32 old; ++ ++ if (core->board.audio_chip == V4L2_IDENT_WM8775) ++ snd_cx88_wm8775_volume_put(kcontrol, value); ++ ++ left = value->value.integer.value[0] & 0x3f; ++ right = value->value.integer.value[1] & 0x3f; ++ b = right - left; ++ if (b < 0) { ++ v = 0x3f - left; ++ b = (-b) | 0x40; ++ } else { ++ v = 0x3f - right; ++ } ++ /* Do we really know this will always be called with IRQs on? */ ++ spin_lock_irq(&chip->reg_lock); ++ old = cx_read(AUD_VOL_CTL); ++ if (v != (old & 0x3f)) { ++ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); ++ changed = 1; ++ } ++ if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { ++ cx_write(AUD_BAL_CTL, b); ++ changed = 1; ++ } ++ spin_unlock_irq(&chip->reg_lock); ++ ++ return changed; ++} ++ ++static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); ++ ++static const struct snd_kcontrol_new snd_cx88_volume = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .name = "Analog-TV Volume", ++ .info = snd_cx88_volume_info, ++ .get = snd_cx88_volume_get, ++ .put = snd_cx88_volume_put, ++ .tlv.p = snd_cx88_db_scale, ++}; ++ ++static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core = chip->core; ++ u32 bit = kcontrol->private_value; ++ ++ value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit); ++ return 0; ++} ++ ++static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core = chip->core; ++ u32 bit = kcontrol->private_value; ++ int ret = 0; ++ u32 vol; ++ ++ spin_lock_irq(&chip->reg_lock); ++ vol = cx_read(AUD_VOL_CTL); ++ if (value->value.integer.value[0] != !(vol & bit)) { ++ vol ^= bit; ++ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); ++ /* Pass mute onto any WM8775 */ ++ if ((core->board.audio_chip == V4L2_IDENT_WM8775) && ++ ((1<<6) == bit)) ++ wm8775_s_ctrl(core, V4L2_CID_AUDIO_MUTE, 0 != (vol & bit)); ++ ret = 1; ++ } ++ spin_unlock_irq(&chip->reg_lock); ++ return ret; ++} ++ ++static const struct snd_kcontrol_new snd_cx88_dac_switch = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Audio-Out Switch", ++ .info = snd_ctl_boolean_mono_info, ++ .get = snd_cx88_switch_get, ++ .put = snd_cx88_switch_put, ++ .private_value = (1<<8), ++}; ++ ++static const struct snd_kcontrol_new snd_cx88_source_switch = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Analog-TV Switch", ++ .info = snd_ctl_boolean_mono_info, ++ .get = snd_cx88_switch_get, ++ .put = snd_cx88_switch_put, ++ .private_value = (1<<6), ++}; ++ ++static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core = chip->core; ++ s32 val; ++ ++ val = wm8775_g_ctrl(core, V4L2_CID_AUDIO_LOUDNESS); ++ value->value.integer.value[0] = val ? 1 : 0; ++ return 0; ++} ++ ++static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *value) ++{ ++ snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); ++ struct cx88_core *core = chip->core; ++ struct v4l2_control client_ctl; ++ ++ memset(&client_ctl, 0, sizeof(client_ctl)); ++ client_ctl.value = 0 != value->value.integer.value[0]; ++ client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; ++ call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); ++ ++ return 0; ++} ++ ++static struct snd_kcontrol_new snd_cx88_alc_switch = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Line-In ALC Switch", ++ .info = snd_ctl_boolean_mono_info, ++ .get = snd_cx88_alc_get, ++ .put = snd_cx88_alc_put, ++}; ++ ++/**************************************************************************** ++ Basic Flow for Sound Devices ++ ****************************************************************************/ ++ ++/* ++ * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio ++ * Only boards with eeprom and byte 1 at eeprom=1 have it ++ */ ++ ++static const struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = { ++ {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, ++ {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, ++ {0, } ++}; ++MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); ++ ++/* ++ * Chip-specific destructor ++ */ ++ ++static int snd_cx88_free(snd_cx88_card_t *chip) ++{ ++ ++ if (chip->irq >= 0) ++ free_irq(chip->irq, chip); ++ ++ cx88_core_put(chip->core,chip->pci); ++ ++ pci_disable_device(chip->pci); ++ return 0; ++} ++ ++/* ++ * Component Destructor ++ */ ++static void snd_cx88_dev_free(struct snd_card * card) ++{ ++ snd_cx88_card_t *chip = card->private_data; ++ ++ snd_cx88_free(chip); ++} ++ ++ ++/* ++ * Alsa Constructor - Component probe ++ */ ++ ++static int devno; ++static int __devinit snd_cx88_create(struct snd_card *card, ++ struct pci_dev *pci, ++ snd_cx88_card_t **rchip, ++ struct cx88_core **core_ptr) ++{ ++ snd_cx88_card_t *chip; ++ struct cx88_core *core; ++ int err; ++ unsigned char pci_lat; ++ ++ *rchip = NULL; ++ ++ err = pci_enable_device(pci); ++ if (err < 0) ++ return err; ++ ++ pci_set_master(pci); ++ ++ chip = card->private_data; ++ ++ core = cx88_core_get(pci); ++ if (NULL == core) { ++ err = -EINVAL; ++ return err; ++ } ++ ++ if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { ++ dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); ++ err = -EIO; ++ cx88_core_put(core, pci); ++ return err; ++ } ++ ++ ++ /* pci init */ ++ chip->card = card; ++ chip->pci = pci; ++ chip->irq = -1; ++ spin_lock_init(&chip->reg_lock); ++ ++ chip->core = core; ++ ++ /* get irq */ ++ err = request_irq(chip->pci->irq, cx8801_irq, ++ IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip); ++ if (err < 0) { ++ dprintk(0, "%s: can't get IRQ %d\n", ++ chip->core->name, chip->pci->irq); ++ return err; ++ } ++ ++ /* print pci info */ ++ pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat); ++ ++ dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", core->name, devno, ++ pci_name(pci), pci->revision, pci->irq, ++ pci_lat, (unsigned long long)pci_resource_start(pci,0)); ++ ++ chip->irq = pci->irq; ++ synchronize_irq(chip->irq); ++ ++ snd_card_set_dev(card, &pci->dev); ++ ++ *rchip = chip; ++ *core_ptr = core; ++ ++ return 0; ++} ++ ++static int __devinit cx88_audio_initdev(struct pci_dev *pci, ++ const struct pci_device_id *pci_id) ++{ ++ struct snd_card *card; ++ snd_cx88_card_t *chip; ++ struct cx88_core *core = NULL; ++ int err; ++ ++ if (devno >= SNDRV_CARDS) ++ return (-ENODEV); ++ ++ if (!enable[devno]) { ++ ++devno; ++ return (-ENOENT); ++ } ++ ++ err = snd_card_create(index[devno], id[devno], THIS_MODULE, ++ sizeof(snd_cx88_card_t), &card); ++ if (err < 0) ++ return err; ++ ++ card->private_free = snd_cx88_dev_free; ++ ++ err = snd_cx88_create(card, pci, &chip, &core); ++ if (err < 0) ++ goto error; ++ ++ err = snd_cx88_pcm(chip, 0, "CX88 Digital"); ++ if (err < 0) ++ goto error; ++ ++ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); ++ if (err < 0) ++ goto error; ++ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); ++ if (err < 0) ++ goto error; ++ err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); ++ if (err < 0) ++ goto error; ++ ++ /* If there's a wm8775 then add a Line-In ALC switch */ ++ if (core->board.audio_chip == V4L2_IDENT_WM8775) ++ snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); ++ ++ strcpy (card->driver, "CX88x"); ++ sprintf(card->shortname, "Conexant CX%x", pci->device); ++ sprintf(card->longname, "%s at %#llx", ++ card->shortname,(unsigned long long)pci_resource_start(pci, 0)); ++ strcpy (card->mixername, "CX88"); ++ ++ dprintk (0, "%s/%i: ALSA support for cx2388x boards\n", ++ card->driver,devno); ++ ++ err = snd_card_register(card); ++ if (err < 0) ++ goto error; ++ pci_set_drvdata(pci,card); ++ ++ devno++; ++ return 0; ++ ++error: ++ snd_card_free(card); ++ return err; ++} ++/* ++ * ALSA destructor ++ */ ++static void __devexit cx88_audio_finidev(struct pci_dev *pci) ++{ ++ struct cx88_audio_dev *card = pci_get_drvdata(pci); ++ ++ snd_card_free((void *)card); ++ ++ pci_set_drvdata(pci, NULL); ++ ++ devno--; ++} ++ ++/* ++ * PCI driver definition ++ */ ++ ++static struct pci_driver cx88_audio_pci_driver = { ++ .name = "cx88_audio", ++ .id_table = cx88_audio_pci_tbl, ++ .probe = cx88_audio_initdev, ++ .remove = __devexit_p(cx88_audio_finidev), ++}; ++ ++/**************************************************************************** ++ LINUX MODULE INIT ++ ****************************************************************************/ ++ ++/* ++ * module init ++ */ ++static int __init cx88_audio_init(void) ++{ ++ printk(KERN_INFO "cx2388x alsa driver version %s loaded\n", ++ CX88_VERSION); ++ return pci_register_driver(&cx88_audio_pci_driver); ++} ++ ++/* ++ * module remove ++ */ ++static void __exit cx88_audio_fini(void) ++{ ++ pci_unregister_driver(&cx88_audio_pci_driver); ++} ++ ++module_init(cx88_audio_init); ++module_exit(cx88_audio_fini); +diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c +new file mode 100644 +index 0000000..a6ff8a6 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-blackbird.c +@@ -0,0 +1,1300 @@ ++/* ++ * ++ * Support for a cx23416 mpeg encoder via cx2388x host port. ++ * "blackbird" reference design. ++ * ++ * (c) 2004 Jelle Foks ++ * (c) 2004 Gerd Knorr ++ * ++ * (c) 2005-2006 Mauro Carvalho Chehab ++ * - video_ioctl2 conversion ++ * ++ * Includes parts from the ivtv driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++ ++MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); ++MODULE_AUTHOR("Jelle Foks , Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX88_VERSION); ++ ++static unsigned int mpegbufs = 32; ++module_param(mpegbufs,int,0644); ++MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32"); ++ ++static unsigned int debug; ++module_param(debug,int,0644); ++MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); ++ ++#define dprintk(level, fmt, arg...) do { \ ++ if (debug + 1 > level) \ ++ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg); \ ++} while(0) ++ ++/* ------------------------------------------------------------------ */ ++ ++#define BLACKBIRD_FIRM_IMAGE_SIZE 376836 ++ ++/* defines below are from ivtv-driver.h */ ++ ++#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF ++ ++/* Firmware API commands */ ++#define IVTV_API_STD_TIMEOUT 500 ++ ++enum blackbird_capture_type { ++ BLACKBIRD_MPEG_CAPTURE, ++ BLACKBIRD_RAW_CAPTURE, ++ BLACKBIRD_RAW_PASSTHRU_CAPTURE ++}; ++enum blackbird_capture_bits { ++ BLACKBIRD_RAW_BITS_NONE = 0x00, ++ BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01, ++ BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02, ++ BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04, ++ BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, ++ BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 ++}; ++enum blackbird_capture_end { ++ BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ ++ BLACKBIRD_END_NOW, /* stop immediately, no irq */ ++}; ++enum blackbird_framerate { ++ BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ ++ BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ ++}; ++enum blackbird_stream_port { ++ BLACKBIRD_OUTPUT_PORT_MEMORY, ++ BLACKBIRD_OUTPUT_PORT_STREAMING, ++ BLACKBIRD_OUTPUT_PORT_SERIAL ++}; ++enum blackbird_data_xfer_status { ++ BLACKBIRD_MORE_BUFFERS_FOLLOW, ++ BLACKBIRD_LAST_BUFFER, ++}; ++enum blackbird_picture_mask { ++ BLACKBIRD_PICTURE_MASK_NONE, ++ BLACKBIRD_PICTURE_MASK_I_FRAMES, ++ BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, ++ BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, ++}; ++enum blackbird_vbi_mode_bits { ++ BLACKBIRD_VBI_BITS_SLICED, ++ BLACKBIRD_VBI_BITS_RAW, ++}; ++enum blackbird_vbi_insertion_bits { ++ BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, ++ BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, ++ BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, ++ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, ++ BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, ++}; ++enum blackbird_dma_unit { ++ BLACKBIRD_DMA_BYTES, ++ BLACKBIRD_DMA_FRAMES, ++}; ++enum blackbird_dma_transfer_status_bits { ++ BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, ++ BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, ++ BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, ++}; ++enum blackbird_pause { ++ BLACKBIRD_PAUSE_ENCODING, ++ BLACKBIRD_RESUME_ENCODING, ++}; ++enum blackbird_copyright { ++ BLACKBIRD_COPYRIGHT_OFF, ++ BLACKBIRD_COPYRIGHT_ON, ++}; ++enum blackbird_notification_type { ++ BLACKBIRD_NOTIFICATION_REFRESH, ++}; ++enum blackbird_notification_status { ++ BLACKBIRD_NOTIFICATION_OFF, ++ BLACKBIRD_NOTIFICATION_ON, ++}; ++enum blackbird_notification_mailbox { ++ BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, ++}; ++enum blackbird_field1_lines { ++ BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ ++ BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ ++ BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */ ++}; ++enum blackbird_field2_lines { ++ BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */ ++ BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ ++ BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ ++}; ++enum blackbird_custom_data_type { ++ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, ++ BLACKBIRD_CUSTOM_PRIVATE_PACKET, ++}; ++enum blackbird_mute { ++ BLACKBIRD_UNMUTE, ++ BLACKBIRD_MUTE, ++}; ++enum blackbird_mute_video_mask { ++ BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00, ++ BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000, ++ BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000, ++}; ++enum blackbird_mute_video_shift { ++ BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8, ++ BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, ++ BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, ++}; ++ ++/* Registers */ ++#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) ++#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) ++#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) ++#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) ++#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) ++#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) ++ ++/* ------------------------------------------------------------------ */ ++ ++static void host_setup(struct cx88_core *core) ++{ ++ /* toggle reset of the host */ ++ cx_write(MO_GPHST_SOFT_RST, 1); ++ udelay(100); ++ cx_write(MO_GPHST_SOFT_RST, 0); ++ udelay(100); ++ ++ /* host port setup */ ++ cx_write(MO_GPHST_WSC, 0x44444444U); ++ cx_write(MO_GPHST_XFR, 0); ++ cx_write(MO_GPHST_WDTH, 15); ++ cx_write(MO_GPHST_HDSHK, 0); ++ cx_write(MO_GPHST_MUX16, 0x44448888U); ++ cx_write(MO_GPHST_MODE, 0); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++#define P1_MDATA0 0x390000 ++#define P1_MDATA1 0x390001 ++#define P1_MDATA2 0x390002 ++#define P1_MDATA3 0x390003 ++#define P1_MADDR2 0x390004 ++#define P1_MADDR1 0x390005 ++#define P1_MADDR0 0x390006 ++#define P1_RDATA0 0x390008 ++#define P1_RDATA1 0x390009 ++#define P1_RDATA2 0x39000A ++#define P1_RDATA3 0x39000B ++#define P1_RADDR0 0x39000C ++#define P1_RADDR1 0x39000D ++#define P1_RRDWR 0x39000E ++ ++static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(1); ++ u32 gpio0,need; ++ ++ need = state ? 2 : 0; ++ for (;;) { ++ gpio0 = cx_read(MO_GP0_IO) & 2; ++ if (need == gpio0) ++ return 0; ++ if (time_after(jiffies,timeout)) ++ return -1; ++ udelay(1); ++ } ++} ++ ++static int memory_write(struct cx88_core *core, u32 address, u32 value) ++{ ++ /* Warning: address is dword address (4 bytes) */ ++ cx_writeb(P1_MDATA0, (unsigned int)value); ++ cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); ++ cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); ++ cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); ++ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); ++ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); ++ cx_writeb(P1_MADDR0, (unsigned int)address); ++ cx_read(P1_MDATA0); ++ cx_read(P1_MADDR0); ++ ++ return wait_ready_gpio0_bit1(core,1); ++} ++ ++static int memory_read(struct cx88_core *core, u32 address, u32 *value) ++{ ++ int retval; ++ u32 val; ++ ++ /* Warning: address is dword address (4 bytes) */ ++ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); ++ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); ++ cx_writeb(P1_MADDR0, (unsigned int)address); ++ cx_read(P1_MADDR0); ++ ++ retval = wait_ready_gpio0_bit1(core,1); ++ ++ cx_writeb(P1_MDATA3, 0); ++ val = (unsigned char)cx_read(P1_MDATA3) << 24; ++ cx_writeb(P1_MDATA2, 0); ++ val |= (unsigned char)cx_read(P1_MDATA2) << 16; ++ cx_writeb(P1_MDATA1, 0); ++ val |= (unsigned char)cx_read(P1_MDATA1) << 8; ++ cx_writeb(P1_MDATA0, 0); ++ val |= (unsigned char)cx_read(P1_MDATA0); ++ ++ *value = val; ++ return retval; ++} ++ ++static int register_write(struct cx88_core *core, u32 address, u32 value) ++{ ++ cx_writeb(P1_RDATA0, (unsigned int)value); ++ cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); ++ cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); ++ cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); ++ cx_writeb(P1_RADDR0, (unsigned int)address); ++ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); ++ cx_writeb(P1_RRDWR, 1); ++ cx_read(P1_RDATA0); ++ cx_read(P1_RADDR0); ++ ++ return wait_ready_gpio0_bit1(core,1); ++} ++ ++ ++static int register_read(struct cx88_core *core, u32 address, u32 *value) ++{ ++ int retval; ++ u32 val; ++ ++ cx_writeb(P1_RADDR0, (unsigned int)address); ++ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); ++ cx_writeb(P1_RRDWR, 0); ++ cx_read(P1_RADDR0); ++ ++ retval = wait_ready_gpio0_bit1(core,1); ++ val = (unsigned char)cx_read(P1_RDATA0); ++ val |= (unsigned char)cx_read(P1_RDATA1) << 8; ++ val |= (unsigned char)cx_read(P1_RDATA2) << 16; ++ val |= (unsigned char)cx_read(P1_RDATA3) << 24; ++ ++ *value = val; ++ return retval; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) ++{ ++ struct cx8802_dev *dev = priv; ++ unsigned long timeout; ++ u32 value, flag, retval; ++ int i; ++ ++ dprintk(1,"%s: 0x%X\n", __func__, command); ++ ++ /* this may not be 100% safe if we can't read any memory location ++ without side effects */ ++ memory_read(dev->core, dev->mailbox - 4, &value); ++ if (value != 0x12345678) { ++ dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n"); ++ return -1; ++ } ++ ++ memory_read(dev->core, dev->mailbox, &flag); ++ if (flag) { ++ dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag); ++ return -1; ++ } ++ ++ flag |= 1; /* tell 'em we're working on it */ ++ memory_write(dev->core, dev->mailbox, flag); ++ ++ /* write command + args + fill remaining with zeros */ ++ memory_write(dev->core, dev->mailbox + 1, command); /* command code */ ++ memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */ ++ for (i = 0; i < in; i++) { ++ memory_write(dev->core, dev->mailbox + 4 + i, data[i]); ++ dprintk(1, "API Input %d = %d\n", i, data[i]); ++ } ++ for (; i < CX2341X_MBOX_MAX_DATA; i++) ++ memory_write(dev->core, dev->mailbox + 4 + i, 0); ++ ++ flag |= 3; /* tell 'em we're done writing */ ++ memory_write(dev->core, dev->mailbox, flag); ++ ++ /* wait for firmware to handle the API command */ ++ timeout = jiffies + msecs_to_jiffies(10); ++ for (;;) { ++ memory_read(dev->core, dev->mailbox, &flag); ++ if (0 != (flag & 4)) ++ break; ++ if (time_after(jiffies,timeout)) { ++ dprintk(0, "ERROR: API Mailbox timeout\n"); ++ return -1; ++ } ++ udelay(10); ++ } ++ ++ /* read output values */ ++ for (i = 0; i < out; i++) { ++ memory_read(dev->core, dev->mailbox + 4 + i, data + i); ++ dprintk(1, "API Output %d = %d\n", i, data[i]); ++ } ++ ++ memory_read(dev->core, dev->mailbox + 2, &retval); ++ dprintk(1, "API result = %d\n",retval); ++ ++ flag = 0; ++ memory_write(dev->core, dev->mailbox, flag); ++ return retval; ++} ++/* ------------------------------------------------------------------ */ ++ ++/* We don't need to call the API often, so using just one mailbox will probably suffice */ ++static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command, ++ u32 inputcnt, u32 outputcnt, ...) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ va_list vargs; ++ int i, err; ++ ++ va_start(vargs, outputcnt); ++ ++ for (i = 0; i < inputcnt; i++) { ++ data[i] = va_arg(vargs, int); ++ } ++ err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data); ++ for (i = 0; i < outputcnt; i++) { ++ int *vptr = va_arg(vargs, int *); ++ *vptr = data[i]; ++ } ++ va_end(vargs); ++ return err; ++} ++ ++static int blackbird_find_mailbox(struct cx8802_dev *dev) ++{ ++ u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456}; ++ int signaturecnt=0; ++ u32 value; ++ int i; ++ ++ for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { ++ memory_read(dev->core, i, &value); ++ if (value == signature[signaturecnt]) ++ signaturecnt++; ++ else ++ signaturecnt = 0; ++ if (4 == signaturecnt) { ++ dprintk(1, "Mailbox signature found\n"); ++ return i+1; ++ } ++ } ++ dprintk(0, "Mailbox signature values not found!\n"); ++ return -1; ++} ++ ++static int blackbird_load_firmware(struct cx8802_dev *dev) ++{ ++ static const unsigned char magic[8] = { ++ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa ++ }; ++ const struct firmware *firmware; ++ int i, retval = 0; ++ u32 value = 0; ++ u32 checksum = 0; ++ u32 *dataptr; ++ ++ retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED); ++ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); ++ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640); ++ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); ++ msleep(1); ++ retval |= register_write(dev->core, IVTV_REG_APU, 0); ++ ++ if (retval < 0) ++ dprintk(0, "Error with register_write\n"); ++ ++ retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME, ++ &dev->pci->dev); ++ ++ ++ if (retval != 0) { ++ dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n", ++ CX2341X_FIRM_ENC_FILENAME); ++ dprintk(0, "Please fix your hotplug setup, the board will " ++ "not work without firmware loaded!\n"); ++ return -1; ++ } ++ ++ if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { ++ dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", ++ firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); ++ release_firmware(firmware); ++ return -1; ++ } ++ ++ if (0 != memcmp(firmware->data, magic, 8)) { ++ dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ ++ /* transfer to the chip */ ++ dprintk(1,"Loading firmware ...\n"); ++ dataptr = (u32*)firmware->data; ++ for (i = 0; i < (firmware->size >> 2); i++) { ++ value = le32_to_cpu(*dataptr); ++ checksum += ~value; ++ memory_write(dev->core, i, value); ++ dataptr++; ++ } ++ ++ /* read back to verify with the checksum */ ++ for (i--; i >= 0; i--) { ++ memory_read(dev->core, i, &value); ++ checksum -= ~value; ++ } ++ if (checksum) { ++ dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n"); ++ release_firmware(firmware); ++ return -1; ++ } ++ release_firmware(firmware); ++ dprintk(0, "Firmware upload successful.\n"); ++ ++ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); ++ retval |= register_read(dev->core, IVTV_REG_SPU, &value); ++ retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE); ++ msleep(1); ++ ++ retval |= register_read(dev->core, IVTV_REG_VPU, &value); ++ retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8); ++ ++ if (retval < 0) ++ dprintk(0, "Error with register_write\n"); ++ return 0; ++} ++ ++/** ++ Settings used by the windows tv app for PVR2000: ++================================================================================================================= ++Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode ++----------------------------------------------------------------------------------------------------------------- ++MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo ++MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo ++VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo ++DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo ++DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo ++================================================================================================================= ++*DB: "DirectBurn" ++*/ ++ ++static void blackbird_codec_settings(struct cx8802_dev *dev) ++{ ++ /* assign frame size */ ++ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, ++ dev->height, dev->width); ++ ++ dev->cxhdl.width = dev->width; ++ dev->cxhdl.height = dev->height; ++ cx2341x_handler_set_50hz(&dev->cxhdl, dev->core->tvnorm & V4L2_STD_625_50); ++ cx2341x_handler_setup(&dev->cxhdl); ++} ++ ++static int blackbird_initialize_codec(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ int version; ++ int retval; ++ ++ dprintk(1,"Initialize codec\n"); ++ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ ++ if (retval < 0) { ++ ++ dev->mpeg_active = 0; ++ ++ /* ping was not successful, reset and upload firmware */ ++ cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ ++ cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ ++ retval = blackbird_load_firmware(dev); ++ if (retval < 0) ++ return retval; ++ ++ retval = blackbird_find_mailbox(dev); ++ if (retval < 0) ++ return -1; ++ ++ dev->mailbox = retval; ++ ++ retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ ++ if (retval < 0) { ++ dprintk(0, "ERROR: Firmware ping failed!\n"); ++ return -1; ++ } ++ ++ retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version); ++ if (retval < 0) { ++ dprintk(0, "ERROR: Firmware get encoder version failed!\n"); ++ return -1; ++ } ++ dprintk(0, "Firmware version is 0x%08x\n", version); ++ } ++ ++ cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ ++ cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ ++ cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ ++ cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ ++ ++ blackbird_codec_settings(dev); ++ ++ blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, ++ BLACKBIRD_FIELD1_SAA7115, ++ BLACKBIRD_FIELD2_SAA7115 ++ ); ++ ++ blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, ++ BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); ++ ++ return 0; ++} ++ ++static int blackbird_start_codec(struct file *file, void *priv) ++{ ++ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; ++ struct cx88_core *core = dev->core; ++ /* start capturing to the host interface */ ++ u32 reg; ++ ++ int i; ++ int lastchange = -1; ++ int lastval = 0; ++ ++ for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) { ++ reg = cx_read(AUD_STATUS); ++ ++ dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg); ++ if ((reg & 0x0F) != lastval) { ++ lastval = reg & 0x0F; ++ lastchange = i; ++ } ++ msleep(100); ++ } ++ ++ /* unmute audio source */ ++ cx_clear(AUD_VOL_CTL, (1 << 6)); ++ ++ blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0); ++ ++ /* initialize the video input */ ++ blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); ++ ++ cx2341x_handler_set_busy(&dev->cxhdl, 1); ++ ++ /* start capturing to the host interface */ ++ blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, ++ BLACKBIRD_MPEG_CAPTURE, ++ BLACKBIRD_RAW_BITS_NONE ++ ); ++ ++ dev->mpeg_active = 1; ++ return 0; ++} ++ ++static int blackbird_stop_codec(struct cx8802_dev *dev) ++{ ++ blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, ++ BLACKBIRD_END_NOW, ++ BLACKBIRD_MPEG_CAPTURE, ++ BLACKBIRD_RAW_BITS_NONE ++ ); ++ ++ cx2341x_handler_set_busy(&dev->cxhdl, 0); ++ ++ dev->mpeg_active = 0; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int bb_buf_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct cx8802_fh *fh = q->priv_data; ++ ++ fh->dev->ts_packet_size = 188 * 4; /* was: 512 */ ++ fh->dev->ts_packet_count = mpegbufs; /* was: 100 */ ++ ++ *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count; ++ *count = fh->dev->ts_packet_count; ++ return 0; ++} ++ ++static int ++bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx8802_fh *fh = q->priv_data; ++ return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field); ++} ++ ++static void ++bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx8802_fh *fh = q->priv_data; ++ cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb); ++} ++ ++static void ++bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ cx88_free_buffer(q, (struct cx88_buffer*)vb); ++} ++ ++static struct videobuf_queue_ops blackbird_qops = { ++ .buf_setup = bb_buf_setup, ++ .buf_prepare = bb_buf_prepare, ++ .buf_queue = bb_buf_queue, ++ .buf_release = bb_buf_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; ++ struct cx88_core *core = dev->core; ++ ++ strcpy(cap->driver, "cx88_blackbird"); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cx88_querycap(file, core, cap); ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strlcpy(f->description, "MPEG", sizeof(f->description)); ++ f->pixelformat = V4L2_PIX_FMT_MPEG; ++ f->flags = V4L2_FMT_FLAG_COMPRESSED; ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */ ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.width = dev->width; ++ f->fmt.pix.height = dev->height; ++ f->fmt.pix.field = fh->mpegq.field; ++ dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", ++ dev->width, dev->height, fh->mpegq.field ); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */ ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", ++ dev->width, dev->height, fh->mpegq.field ); ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ struct cx88_core *core = dev->core; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = 188 * 4 * mpegbufs; /* 188 * 4 * 1024; */ ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ dev->width = f->fmt.pix.width; ++ dev->height = f->fmt.pix.height; ++ fh->mpegq.field = f->fmt.pix.field; ++ cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); ++ blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, ++ f->fmt.pix.height, f->fmt.pix.width); ++ dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); ++ return 0; ++} ++ ++static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) ++{ ++ struct cx8802_fh *fh = priv; ++ return (videobuf_reqbufs(&fh->mpegq, p)); ++} ++ ++static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct cx8802_fh *fh = priv; ++ return (videobuf_querybuf(&fh->mpegq, p)); ++} ++ ++static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct cx8802_fh *fh = priv; ++ return (videobuf_qbuf(&fh->mpegq, p)); ++} ++ ++static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct cx8802_fh *fh = priv; ++ return (videobuf_dqbuf(&fh->mpegq, p, ++ file->f_flags & O_NONBLOCK)); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ ++ if (!dev->mpeg_active) ++ blackbird_start_codec(file, fh); ++ return videobuf_streamon(&fh->mpegq); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ ++ if (dev->mpeg_active) ++ blackbird_stop_codec(dev); ++ return videobuf_streamoff(&fh->mpegq); ++} ++ ++static int vidioc_s_frequency (struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx8802_dev *dev = fh->dev; ++ struct cx88_core *core = dev->core; ++ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (unlikely(f->tuner != 0)) ++ return -EINVAL; ++ if (dev->mpeg_active) ++ blackbird_stop_codec(dev); ++ ++ cx88_set_freq (core,f); ++ blackbird_initialize_codec(dev); ++ cx88_set_scale(dev->core, dev->width, dev->height, ++ fh->mpegq.field); ++ return 0; ++} ++ ++static int vidioc_log_status (struct file *file, void *priv) ++{ ++ struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; ++ struct cx88_core *core = dev->core; ++ char name[32 + 2]; ++ ++ snprintf(name, sizeof(name), "%s/2", core->name); ++ call_all(core, core, log_status); ++ v4l2_ctrl_handler_log_status(&dev->cxhdl.hdl, name); ++ return 0; ++} ++ ++static int vidioc_enum_input (struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ return cx88_enum_input (core,i); ++} ++ ++static int vidioc_g_frequency (struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx8802_fh *fh = priv; ++ struct cx88_core *core = fh->dev->core; ++ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (unlikely(f->tuner != 0)) ++ return -EINVAL; ++ ++ f->frequency = core->freq; ++ call_all(core, tuner, g_frequency, f); ++ ++ return 0; ++} ++ ++static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ ++ *i = core->input; ++ return 0; ++} ++ ++static int vidioc_s_input (struct file *file, void *priv, unsigned int i) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ ++ if (i >= 4) ++ return -EINVAL; ++ if (0 == INPUT(i).type) ++ return -EINVAL; ++ ++ mutex_lock(&core->lock); ++ cx88_newstation(core); ++ cx88_video_mux(core,i); ++ mutex_unlock(&core->lock); ++ return 0; ++} ++ ++static int vidioc_g_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ u32 reg; ++ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "Television"); ++ t->capability = V4L2_TUNER_CAP_NORM; ++ t->rangehigh = 0xffffffffUL; ++ call_all(core, tuner, g_tuner, t); ++ ++ cx88_get_stereo(core ,t); ++ reg = cx_read(MO_DEVICE_STATUS); ++ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; ++ return 0; ++} ++ ++static int vidioc_s_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ ++ if (UNSET == core->board.tuner_type) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ cx88_set_stereo(core, t->audmode, 1); ++ return 0; ++} ++ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ ++ *tvnorm = core->tvnorm; ++ return 0; ++} ++ ++static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; ++ ++ mutex_lock(&core->lock); ++ cx88_set_tvnorm(core,*id); ++ mutex_unlock(&core->lock); ++ return 0; ++} ++ ++/* FIXME: cx88_ioctl_hook not implemented */ ++ ++static int mpeg_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8802_dev *dev = video_drvdata(file); ++ struct cx8802_fh *fh; ++ struct cx8802_driver *drv = NULL; ++ int err; ++ ++ dprintk( 1, "%s\n", __func__); ++ ++ mutex_lock(&dev->core->lock); ++ ++ /* Make sure we can acquire the hardware */ ++ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); ++ if (!drv) { ++ dprintk(1, "%s: blackbird driver is not loaded\n", __func__); ++ mutex_unlock(&dev->core->lock); ++ return -ENODEV; ++ } ++ ++ err = drv->request_acquire(drv); ++ if (err != 0) { ++ dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); ++ mutex_unlock(&dev->core->lock); ++ return err; ++ } ++ ++ if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) { ++ drv->request_release(drv); ++ mutex_unlock(&dev->core->lock); ++ return -EINVAL; ++ } ++ dprintk(1, "open dev=%s\n", video_device_node_name(vdev)); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh),GFP_KERNEL); ++ if (NULL == fh) { ++ drv->request_release(drv); ++ mutex_unlock(&dev->core->lock); ++ return -ENOMEM; ++ } ++ v4l2_fh_init(&fh->fh, vdev); ++ file->private_data = fh; ++ fh->dev = dev; ++ ++ videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct cx88_buffer), ++ fh, NULL); ++ ++ /* FIXME: locking against other video device */ ++ cx88_set_scale(dev->core, dev->width, dev->height, ++ fh->mpegq.field); ++ ++ dev->core->mpeg_users++; ++ mutex_unlock(&dev->core->lock); ++ v4l2_fh_add(&fh->fh); ++ return 0; ++} ++ ++static int mpeg_release(struct file *file) ++{ ++ struct cx8802_fh *fh = file->private_data; ++ struct cx8802_dev *dev = fh->dev; ++ struct cx8802_driver *drv = NULL; ++ ++ mutex_lock(&dev->core->lock); ++ ++ if (dev->mpeg_active && dev->core->mpeg_users == 1) ++ blackbird_stop_codec(dev); ++ ++ cx8802_cancel_buffers(fh->dev); ++ /* stop mpeg capture */ ++ videobuf_stop(&fh->mpegq); ++ ++ videobuf_mmap_free(&fh->mpegq); ++ ++ v4l2_fh_del(&fh->fh); ++ v4l2_fh_exit(&fh->fh); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ /* Make sure we release the hardware */ ++ drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); ++ WARN_ON(!drv); ++ if (drv) ++ drv->request_release(drv); ++ ++ dev->core->mpeg_users--; ++ ++ mutex_unlock(&dev->core->lock); ++ ++ return 0; ++} ++ ++static ssize_t ++mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos) ++{ ++ struct cx8802_fh *fh = file->private_data; ++ struct cx8802_dev *dev = fh->dev; ++ ++ if (!dev->mpeg_active) ++ blackbird_start_codec(file, fh); ++ ++ return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static unsigned int ++mpeg_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ unsigned long req_events = poll_requested_events(wait); ++ struct cx8802_fh *fh = file->private_data; ++ struct cx8802_dev *dev = fh->dev; ++ ++ if (!dev->mpeg_active && (req_events & (POLLIN | POLLRDNORM))) ++ blackbird_start_codec(file, fh); ++ ++ return v4l2_ctrl_poll(file, wait) | videobuf_poll_stream(file, &fh->mpegq, wait); ++} ++ ++static int ++mpeg_mmap(struct file *file, struct vm_area_struct * vma) ++{ ++ struct cx8802_fh *fh = file->private_data; ++ ++ return videobuf_mmap_mapper(&fh->mpegq, vma); ++} ++ ++static const struct v4l2_file_operations mpeg_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = mpeg_open, ++ .release = mpeg_release, ++ .read = mpeg_read, ++ .poll = mpeg_poll, ++ .mmap = mpeg_mmap, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_log_status = vidioc_log_status, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_std = vidioc_g_std, ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static struct video_device cx8802_mpeg_template = { ++ .name = "cx8802", ++ .fops = &mpeg_fops, ++ .ioctl_ops = &mpeg_ioctl_ops, ++ .tvnorms = CX88_NORMS, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++/* The CX8802 MPEG API will call this when we can use the hardware */ ++static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ int err = 0; ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* By default, core setup will leave the cx22702 out of reset, on the bus. ++ * We left the hardware on power up with the cx22702 active. ++ * We're being given access to re-arrange the GPIOs. ++ * Take the bus off the cx22702 and put the cx23416 on it. ++ */ ++ /* Toggle reset on cx22702 leaving i2c active */ ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ cx_clear(MO_GP0_IO, 0x00000080); ++ udelay(50); ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ /* tri-state the cx22702 pins */ ++ cx_set(MO_GP0_IO, 0x00000004); ++ udelay(1000); ++ break; ++ default: ++ err = -ENODEV; ++ } ++ return err; ++} ++ ++/* The CX8802 MPEG API will call this when we need to release the hardware */ ++static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ int err = 0; ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* Exit leaving the cx23416 on the bus */ ++ break; ++ default: ++ err = -ENODEV; ++ } ++ return err; ++} ++ ++static void blackbird_unregister_video(struct cx8802_dev *dev) ++{ ++ if (dev->mpeg_dev) { ++ if (video_is_registered(dev->mpeg_dev)) ++ video_unregister_device(dev->mpeg_dev); ++ else ++ video_device_release(dev->mpeg_dev); ++ dev->mpeg_dev = NULL; ++ } ++} ++ ++static int blackbird_register_video(struct cx8802_dev *dev) ++{ ++ int err; ++ ++ dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci, ++ &cx8802_mpeg_template,"mpeg"); ++ dev->mpeg_dev->ctrl_handler = &dev->cxhdl.hdl; ++ video_set_drvdata(dev->mpeg_dev, dev); ++ err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1); ++ if (err < 0) { ++ printk(KERN_INFO "%s/2: can't register mpeg device\n", ++ dev->core->name); ++ return err; ++ } ++ printk(KERN_INFO "%s/2: registered device %s [mpeg]\n", ++ dev->core->name, video_device_node_name(dev->mpeg_dev)); ++ return 0; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static int cx8802_blackbird_probe(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ struct cx8802_dev *dev = core->dvbdev; ++ int err; ++ ++ dprintk( 1, "%s\n", __func__); ++ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", ++ core->boardnr, ++ core->name, ++ core->pci_bus, ++ core->pci_slot); ++ ++ err = -ENODEV; ++ if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD)) ++ goto fail_core; ++ ++ dev->width = 720; ++ if (core->tvnorm & V4L2_STD_525_60) { ++ dev->height = 480; ++ } else { ++ dev->height = 576; ++ } ++ dev->cxhdl.port = CX2341X_PORT_STREAMING; ++ dev->cxhdl.width = dev->width; ++ dev->cxhdl.height = dev->height; ++ dev->cxhdl.func = blackbird_mbox_func; ++ dev->cxhdl.priv = dev; ++ err = cx2341x_handler_init(&dev->cxhdl, 36); ++ if (err) ++ goto fail_core; ++ v4l2_ctrl_add_handler(&dev->cxhdl.hdl, &core->video_hdl, NULL); ++ ++ /* blackbird stuff */ ++ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", ++ core->name); ++ host_setup(dev->core); ++ ++ blackbird_initialize_codec(dev); ++ ++ /* initial device configuration: needed ? */ ++// init_controls(core); ++ cx88_set_tvnorm(core,core->tvnorm); ++ cx88_video_mux(core,0); ++ cx2341x_handler_set_50hz(&dev->cxhdl, dev->height == 576); ++ cx2341x_handler_setup(&dev->cxhdl); ++ blackbird_register_video(dev); ++ ++ return 0; ++ ++ fail_core: ++ return err; ++} ++ ++static int cx8802_blackbird_remove(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ struct cx8802_dev *dev = core->dvbdev; ++ ++ /* blackbird */ ++ blackbird_unregister_video(drv->core->dvbdev); ++ v4l2_ctrl_handler_free(&dev->cxhdl.hdl); ++ ++ return 0; ++} ++ ++static struct cx8802_driver cx8802_blackbird_driver = { ++ .type_id = CX88_MPEG_BLACKBIRD, ++ .hw_access = CX8802_DRVCTL_SHARED, ++ .probe = cx8802_blackbird_probe, ++ .remove = cx8802_blackbird_remove, ++ .advise_acquire = cx8802_blackbird_advise_acquire, ++ .advise_release = cx8802_blackbird_advise_release, ++}; ++ ++static int __init blackbird_init(void) ++{ ++ printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n", ++ CX88_VERSION); ++ return cx8802_register_driver(&cx8802_blackbird_driver); ++} ++ ++static void __exit blackbird_fini(void) ++{ ++ cx8802_unregister_driver(&cx8802_blackbird_driver); ++} ++ ++module_init(blackbird_init); ++module_exit(blackbird_fini); ++ ++module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); ++MODULE_PARM_DESC(debug,"enable debug messages [video]"); +diff --git a/drivers/media/pci/cx88/cx88-cards.c b/drivers/media/pci/cx88/cx88-cards.c +new file mode 100644 +index 0000000..e2e0b8f +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-cards.c +@@ -0,0 +1,3811 @@ ++/* ++ * ++ * device driver for Conexant 2388x based TV cards ++ * card-specific stuff. ++ * ++ * (c) 2003 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include "tea5767.h" ++#include "xc4000.h" ++ ++static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++ ++module_param_array(tuner, int, NULL, 0444); ++module_param_array(radio, int, NULL, 0444); ++module_param_array(card, int, NULL, 0444); ++ ++MODULE_PARM_DESC(tuner,"tuner type"); ++MODULE_PARM_DESC(radio,"radio tuner type"); ++MODULE_PARM_DESC(card,"card type"); ++ ++static unsigned int latency = UNSET; ++module_param(latency,int,0444); ++MODULE_PARM_DESC(latency,"pci latency timer"); ++ ++static int disable_ir; ++module_param(disable_ir, int, 0444); ++MODULE_PARM_DESC(disable_ir, "Disable IR support"); ++ ++#define info_printk(core, fmt, arg...) \ ++ printk(KERN_INFO "%s: " fmt, core->name , ## arg) ++ ++#define warn_printk(core, fmt, arg...) \ ++ printk(KERN_WARNING "%s: " fmt, core->name , ## arg) ++ ++#define err_printk(core, fmt, arg...) \ ++ printk(KERN_ERR "%s: " fmt, core->name , ## arg) ++ ++ ++/* ------------------------------------------------------------------ */ ++/* board config info */ ++ ++/* If radio_type !=UNSET, radio_addr should be specified ++ */ ++ ++static const struct cx88_board cx88_boards[] = { ++ [CX88_BOARD_UNKNOWN] = { ++ .name = "UNKNOWN/GENERIC", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE2, ++ .vmux = 1, ++ },{ ++ .type = CX88_VMUX_COMPOSITE3, ++ .vmux = 2, ++ },{ ++ .type = CX88_VMUX_COMPOSITE4, ++ .vmux = 3, ++ }}, ++ }, ++ [CX88_BOARD_HAUPPAUGE] = { ++ .name = "Hauppauge WinTV 34xxx models", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xff00, // internal decoder ++ },{ ++ .type = CX88_VMUX_DEBUG, ++ .vmux = 0, ++ .gpio0 = 0xff01, // mono from tuner chip ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xff02, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xff02, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xff01, ++ }, ++ }, ++ [CX88_BOARD_GDI] = { ++ .name = "GDI Black Gold", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ }, ++ [CX88_BOARD_PIXELVIEW] = { ++ .name = "PixelView", ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xff00, // internal decoder ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xff10, ++ }, ++ }, ++ [CX88_BOARD_ATI_WONDER_PRO] = { ++ .name = "ATI TV Wonder Pro", ++ .tuner_type = TUNER_PHILIPS_4IN1, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x03ff, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x03fe, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x03fe, ++ }}, ++ }, ++ [CX88_BOARD_WINFAST2000XP_EXPERT] = { ++ .name = "Leadtek Winfast 2000XP Expert", ++ .tuner_type = TUNER_PHILIPS_4IN1, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00F5e700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x00F5e700, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00F5c700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x00F5c700, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00F5c700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x00F5c700, ++ .gpio3 = 0x02000000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00F5d700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x00F5d700, ++ .gpio3 = 0x02000000, ++ }, ++ }, ++ [CX88_BOARD_AVERTV_STUDIO_303] = { ++ .name = "AverTV Studio 303 (M126)", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio1 = 0xe09f, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio1 = 0xe05f, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio1 = 0xe05f, ++ }}, ++ .radio = { ++ .gpio1 = 0xe0df, ++ .type = CX88_RADIO, ++ }, ++ }, ++ [CX88_BOARD_MSI_TVANYWHERE_MASTER] = { ++ // added gpio values thanks to Michal ++ // values for PAL from DScaler ++ .name = "MSI TV-@nywhere Master", ++ .tuner_type = TUNER_MT2032, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x000040bf, ++ .gpio1 = 0x000080c0, ++ .gpio2 = 0x0000ff40, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000040bf, ++ .gpio1 = 0x000080c0, ++ .gpio2 = 0x0000ff40, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000040bf, ++ .gpio1 = 0x000080c0, ++ .gpio2 = 0x0000ff40, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .vmux = 3, ++ .gpio0 = 0x000040bf, ++ .gpio1 = 0x000080c0, ++ .gpio2 = 0x0000ff20, ++ }, ++ }, ++ [CX88_BOARD_WINFAST_DV2000] = { ++ .name = "Leadtek Winfast DV2000", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0035e700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x0035e700, ++ .gpio3 = 0x02000000, ++ },{ ++ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0035c700, ++ .gpio1 = 0x00003004, ++ .gpio2 = 0x0035c700, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0035c700, ++ .gpio1 = 0x0035c700, ++ .gpio2 = 0x02000000, ++ .gpio3 = 0x02000000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0035d700, ++ .gpio1 = 0x00007004, ++ .gpio2 = 0x0035d700, ++ .gpio3 = 0x02000000, ++ }, ++ }, ++ [CX88_BOARD_LEADTEK_PVR2000] = { ++ // gpio values for PAL version from regspy by DScaler ++ .name = "Leadtek PVR 2000", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0000bde2, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0000bde6, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0000bde6, ++ .audioroute = 1, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0000bd62, ++ .audioroute = 1, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_IODATA_GVVCP3PCI] = { ++ .name = "IODATA GV-VCP3/PCI", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE2, ++ .vmux = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ }, ++ [CX88_BOARD_PROLINK_PLAYTVPVR] = { ++ .name = "Prolink PlayTV PVR", ++ .tuner_type = TUNER_PHILIPS_FM1236_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xbff0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xbff3, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xbff3, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xbff0, ++ }, ++ }, ++ [CX88_BOARD_ASUS_PVR_416] = { ++ .name = "ASUS PVR-416", ++ .tuner_type = TUNER_PHILIPS_FM1236_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0000fde6, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? ++ .audioroute = 1, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0000fde2, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_MSI_TVANYWHERE] = { ++ .name = "MSI TV-@nywhere", ++ .tuner_type = TUNER_MT2032, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00000fbf, ++ .gpio2 = 0x0000fc08, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00000fbf, ++ .gpio2 = 0x0000fc68, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00000fbf, ++ .gpio2 = 0x0000fc68, ++ }}, ++ }, ++ [CX88_BOARD_KWORLD_DVB_T] = { ++ .name = "KWorld/VStream XPert DVB-T", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { ++ .name = "DViCO FusionHDTV DVB-T1", ++ .tuner_type = TUNER_ABSENT, /* No analog tuner */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000027df, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000027df, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_KWORLD_LTV883] = { ++ .name = "KWorld LTV883RF", ++ .tuner_type = TUNER_TNF_8831BGFF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x07f8, ++ },{ ++ .type = CX88_VMUX_DEBUG, ++ .vmux = 0, ++ .gpio0 = 0x07f9, // mono from tuner chip ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000007fa, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000007fa, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x000007f8, ++ }, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = { ++ .name = "DViCO FusionHDTV 3 Gold-Q", ++ .tuner_type = TUNER_MICROTUNE_4042FI5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ /* ++ GPIO[0] resets DT3302 DTV receiver ++ 0 - reset asserted ++ 1 - normal operation ++ GPIO[1] mutes analog audio output connector ++ 0 - enable selected source ++ 1 - mute ++ GPIO[2] selects source for analog audio output connector ++ 0 - analog audio input connector on tab ++ 1 - analog DAC output from CX23881 chip ++ GPIO[3] selects RF input connector on tuner module ++ 0 - RF connector labeled CABLE ++ 1 - RF connector labeled ANT ++ GPIO[4] selects high RF for QAM256 mode ++ 0 - normal RF ++ 1 - high RF ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0f0d, ++ },{ ++ .type = CX88_VMUX_CABLE, ++ .vmux = 0, ++ .gpio0 = 0x0f05, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0f00, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0f00, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_DVB_T1] = { ++ .name = "Hauppauge Nova-T DVB-T", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_CONEXANT_DVB_T1] = { ++ .name = "Conexant DVB-T reference design", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PROVIDEO_PV259] = { ++ .name = "Provideo PV259", ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .audioroute = 1, ++ }}, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { ++ .name = "DViCO FusionHDTV DVB-T Plus", ++ .tuner_type = TUNER_ABSENT, /* No analog tuner */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000027df, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000027df, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DNTV_LIVE_DVB_T] = { ++ .name = "digitalnow DNTV Live! DVB-T", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00000700, ++ .gpio2 = 0x00000101, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00000700, ++ .gpio2 = 0x00000101, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PCHDTV_HD3000] = { ++ .name = "pcHDTV HD3000 HDTV", ++ .tuner_type = TUNER_THOMSON_DTT761X, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ /* GPIO[2] = audio source for analog audio out connector ++ * 0 = analog audio input connector ++ * 1 = CX88 audio DACs ++ * ++ * GPIO[7] = input to CX88's audio/chroma ADC ++ * 0 = FM 10.7 MHz IF ++ * 1 = Sound 4.5 MHz IF ++ * ++ * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively ++ * ++ * GPIO[16] = Remote control input ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00008484, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00008400, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00008400, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00008404, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_ROSLYN] = { ++ // entry added by Kaustubh D. Bhalerao ++ // GPIO values obtained from regspy, courtesy Sean Covel ++ .name = "Hauppauge WinTV 28xxx (Roslyn) models", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xed1a, ++ .gpio2 = 0x00ff, ++ },{ ++ .type = CX88_VMUX_DEBUG, ++ .vmux = 0, ++ .gpio0 = 0xff01, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xff02, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xed92, ++ .gpio2 = 0x00ff, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xed96, ++ .gpio2 = 0x00ff, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_DIGITALLOGIC_MEC] = { ++ .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00009d80, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00009d76, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00009d76, ++ .audioroute = 1, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00009d00, ++ .audioroute = 1, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_IODATA_GVBCTV7E] = { ++ .name = "IODATA GV/BCTV7E", ++ .tuner_type = TUNER_PHILIPS_FQ1286, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 1, ++ .gpio1 = 0x0000e03f, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 2, ++ .gpio1 = 0x0000e07f, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 3, ++ .gpio1 = 0x0000e07f, ++ }} ++ }, ++ [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = { ++ .name = "PixelView PlayTV Ultra Pro (Stereo)", ++ /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */ ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ /* Some variants use a tda9874 and so need the tvaudio module. */ ++ .audio_chip = V4L2_IDENT_TVAUDIO, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xbf61, /* internal decoder */ ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xbf63, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xbf63, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xbf60, ++ }, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = { ++ .name = "DViCO FusionHDTV 3 Gold-T", ++ .tuner_type = TUNER_THOMSON_DTT761X, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x97ed, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x97e9, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x97e9, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_ADSTECH_DVB_T_PCI] = { ++ .name = "ADS Tech Instant TV DVB-T PCI", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = { ++ .name = "TerraTec Cinergy 1400 DVB-T", ++ .tuner_type = TUNER_ABSENT, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 2, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = { ++ .name = "DViCO FusionHDTV 5 Gold", ++ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x87fd, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x87f9, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x87f9, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = { ++ .name = "AverMedia UltraTV Media Center PCI 550", ++ .tuner_type = TUNER_PHILIPS_FM1236_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 0, ++ .gpio0 = 0x0000cd73, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 1, ++ .gpio0 = 0x0000cd73, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 3, ++ .gpio0 = 0x0000cdb3, ++ .audioroute = 1, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .vmux = 2, ++ .gpio0 = 0x0000cdf3, ++ .audioroute = 1, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = { ++ /* Alexander Wold */ ++ .name = "Kworld V-Stream Xpert DVD", ++ .tuner_type = UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x03000000, ++ .gpio1 = 0x01000000, ++ .gpio2 = 0x02000000, ++ .gpio3 = 0x00100000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x03000000, ++ .gpio1 = 0x01000000, ++ .gpio2 = 0x02000000, ++ .gpio3 = 0x00100000, ++ }}, ++ }, ++ [CX88_BOARD_ATI_HDTVWONDER] = { ++ .name = "ATI HDTV Wonder", ++ .tuner_type = TUNER_PHILIPS_TUV1236D, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00000ff7, ++ .gpio1 = 0x000000ff, ++ .gpio2 = 0x00000001, ++ .gpio3 = 0x00000000, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00000ffe, ++ .gpio1 = 0x000000ff, ++ .gpio2 = 0x00000001, ++ .gpio3 = 0x00000000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00000ffe, ++ .gpio1 = 0x000000ff, ++ .gpio2 = 0x00000001, ++ .gpio3 = 0x00000000, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_WINFAST_DTV1000] = { ++ .name = "WinFast DTV1000-T", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_AVERTV_303] = { ++ .name = "AVerTV 303 (M126)", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00ff, ++ .gpio1 = 0xe09f, ++ .gpio2 = 0x0010, ++ .gpio3 = 0x0000, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00ff, ++ .gpio1 = 0xe05f, ++ .gpio2 = 0x0010, ++ .gpio3 = 0x0000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00ff, ++ .gpio1 = 0xe05f, ++ .gpio2 = 0x0010, ++ .gpio3 = 0x0000, ++ }}, ++ }, ++ [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = { ++ .name = "Hauppauge Nova-S-Plus DVB-S", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .audio_chip = V4L2_IDENT_WM8775, ++ .i2sinputcntl = 2, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = { ++ .name = "Hauppauge Nova-SE2 DVB-S", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_KWORLD_DVBS_100] = { ++ .name = "KWorld DVB-S 100", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .audio_chip = V4L2_IDENT_WM8775, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR1100] = { ++ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ }}, ++ /* fixme: Add radio support */ ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR1100LP] = { ++ .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ }}, ++ /* fixme: Add radio support */ ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = { ++ .name = "digitalnow DNTV Live! DVB-T Pro", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | ++ TDA9887_PORT2_ACTIVE, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xf80808, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xf80808, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xf80808, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xf80808, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_KWORLD_DVB_T_CX22702] = { ++ /* Kworld V-stream Xpert DVB-T with Thomson tuner */ ++ /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */ ++ /* Manenti Marco */ ++ .name = "KWorld/VStream XPert DVB-T with cx22702", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0700, ++ .gpio2 = 0x0101, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = { ++ .name = "DViCO FusionHDTV DVB-T Dual Digital", ++ .tuner_type = TUNER_ABSENT, /* No analog tuner */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000067df, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000067df, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { ++ .name = "KWorld HardwareMpegTV XPert", ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x3de2, ++ .gpio2 = 0x00ff, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x3de6, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x3de6, ++ .audioroute = 1, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x3de6, ++ .gpio2 = 0x00ff, ++ }, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = { ++ .name = "DViCO FusionHDTV DVB-T Hybrid", ++ .tuner_type = TUNER_THOMSON_FE6600, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0000a75f, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0000a75b, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0000a75b, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PCHDTV_HD5500] = { ++ .name = "pcHDTV HD5500 HDTV", ++ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x87fd, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x87f9, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x87f9, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_KWORLD_MCE200_DELUXE] = { ++ /* FIXME: tested TV input only, disabled composite, ++ svideo and radio until they can be tested also. */ ++ .name = "Kworld MCE 200 Deluxe", ++ .tuner_type = TUNER_TENA_9533_DI, ++ .radio_type = UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0000BDE6 ++ }}, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = { ++ /* FIXME: SVideo, Composite and FM inputs are untested */ ++ .name = "PixelView PlayTV P7000", ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | ++ TDA9887_PORT2_ACTIVE, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x5da6, ++ }}, ++ .mpeg = CX88_MPEG_BLACKBIRD, ++ }, ++ [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = { ++ .name = "NPG Tech Real TV FM Top 10", ++ .tuner_type = TUNER_TNF_5335MF, /* Actually a TNF9535 */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0788, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x078b, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x078b, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x074a, ++ }, ++ }, ++ [CX88_BOARD_WINFAST_DTV2000H] = { ++ .name = "WinFast DTV2000 H", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00017304, ++ .gpio1 = 0x00008203, ++ .gpio2 = 0x00017304, ++ .gpio3 = 0x02000000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0001d701, ++ .gpio1 = 0x0000b207, ++ .gpio2 = 0x0001d701, ++ .gpio3 = 0x02000000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE2, ++ .vmux = 2, ++ .gpio0 = 0x0001d503, ++ .gpio1 = 0x0000b207, ++ .gpio2 = 0x0001d503, ++ .gpio3 = 0x02000000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 3, ++ .gpio0 = 0x0001d701, ++ .gpio1 = 0x0000b207, ++ .gpio2 = 0x0001d701, ++ .gpio3 = 0x02000000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00015702, ++ .gpio1 = 0x0000f207, ++ .gpio2 = 0x00015702, ++ .gpio3 = 0x02000000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_WINFAST_DTV2000H_J] = { ++ .name = "WinFast DTV2000 H rev. J", ++ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00017300, ++ .gpio1 = 0x00008207, ++ .gpio2 = 0x00000000, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00018300, ++ .gpio1 = 0x0000f207, ++ .gpio2 = 0x00017304, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00018301, ++ .gpio1 = 0x0000f207, ++ .gpio2 = 0x00017304, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00018301, ++ .gpio1 = 0x0000f207, ++ .gpio2 = 0x00017304, ++ .gpio3 = 0x02000000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00015702, ++ .gpio1 = 0x0000f207, ++ .gpio2 = 0x00015702, ++ .gpio3 = 0x02000000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_GENIATECH_DVBS] = { ++ .name = "Geniatech DVB-S", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR3000] = { ++ .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .audio_chip = V4L2_IDENT_WM8775, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x84bf, ++ /* 1: TV Audio / FM Mono */ ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x84bf, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x84bf, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x84bf, ++ /* 4: FM Stereo (untested) */ ++ .audioroute = 8, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ .num_frontends = 2, ++ }, ++ [CX88_BOARD_NORWOOD_MICRO] = { ++ .name = "Norwood Micro TV Tuner", ++ .tuner_type = TUNER_TNF_5335MF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0709, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x070b, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x070b, ++ }}, ++ }, ++ [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { ++ .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x003fffff, ++ .gpio1 = 0x00e00000, ++ .gpio2 = 0x003fffff, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x003fffff, ++ .gpio1 = 0x00e00000, ++ .gpio2 = 0x003fffff, ++ .gpio3 = 0x02000000, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x003fffff, ++ .gpio1 = 0x00e00000, ++ .gpio2 = 0x003fffff, ++ .gpio3 = 0x02000000, ++ }}, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR1300] = { ++ .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .audio_chip = V4L2_IDENT_WM8775, ++ /* ++ * gpio0 as reported by Mike Crash ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xef88, ++ /* 1: TV Audio / FM Mono */ ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xef88, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xef88, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ }}, ++ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xef88, ++ /* 4: FM Stereo (untested) */ ++ .audioroute = 8, ++ }, ++ }, ++ [CX88_BOARD_SAMSUNG_SMT_7020] = { ++ .name = "Samsung SMT 7020 DVB-S", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_ADSTECH_PTV_390] = { ++ .name = "ADS Tech Instant Video PCI", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DEBUG, ++ .vmux = 3, ++ .gpio0 = 0x04ff, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x07fa, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x07fa, ++ }}, ++ }, ++ [CX88_BOARD_PINNACLE_PCTV_HD_800i] = { ++ .name = "Pinnacle PCTV HD 800i", ++ .tuner_type = TUNER_XC5000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x10ff, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x10ef, ++ .audioroute = 1, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x10ef, ++ .audioroute = 1, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = { ++ .name = "DViCO FusionHDTV 5 PCI nano", ++ /* xc3008 tuner, digital only for now */ ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x000027df, /* Unconfirmed */ ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000027df, /* Unconfirmed */ ++ .audioroute = 1, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000027df, /* Unconfirmed */ ++ .audioroute = 1, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PINNACLE_HYBRID_PCTV] = { ++ .name = "Pinnacle Hybrid PCTV", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x004ff, ++ .gpio1 = 0x010ff, ++ .gpio2 = 0x00001, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x004fb, ++ .gpio1 = 0x010ef, ++ .audioroute = 1, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x004fb, ++ .gpio1 = 0x010ef, ++ .audioroute = 1, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x004ff, ++ .gpio1 = 0x010ff, ++ .gpio2 = 0x0ff, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ /* Terry Wu */ ++ /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */ ++ /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */ ++ /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */ ++ /* Mute Audio : set GPIO 2 value to 1 */ ++ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { ++ .name = "Leadtek TV2000 XP Global", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */ ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ ++ .gpio3 = 0x0000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */ ++ .gpio3 = 0x0000, ++ }, ++ }, ++ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36] = { ++ .name = "Leadtek TV2000 XP Global (SC4100)", ++ .tuner_type = TUNER_XC4000, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */ ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ ++ .gpio3 = 0x0000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x0000, ++ .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */ ++ .gpio3 = 0x0000, ++ }, ++ }, ++ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43] = { ++ .name = "Leadtek TV2000 XP Global (XC4100)", ++ .tuner_type = TUNER_XC4000, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6040, /* pin 14 = 1, pin 13 = 0 */ ++ .gpio2 = 0x0000, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */ ++ .gpio2 = 0x0000, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 14 = 1, pin 13 = 1 */ ++ .gpio2 = 0x0000, ++ .gpio3 = 0x0000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6000, /* pin 14 = 1, pin 13 = 0 */ ++ .gpio2 = 0x0000, ++ .gpio3 = 0x0000, ++ }, ++ }, ++ [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { ++ .name = "PowerColor RA330", /* Long names may confuse LIRC. */ ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .input = { { ++ .type = CX88_VMUX_DEBUG, ++ .vmux = 3, /* Due to the way the cx88 driver is written, */ ++ .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */ ++ .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */ ++ .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */ ++ }, { /* from the tuner on boot for a little while. */ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00ff, ++ .gpio1 = 0xf35d, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00ff, ++ .gpio1 = 0xf37d, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000ff, ++ .gpio1 = 0x0f37d, ++ .gpio3 = 0x00000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x000ff, ++ .gpio1 = 0x0f35d, ++ .gpio3 = 0x00000, ++ }, ++ }, ++ [CX88_BOARD_GENIATECH_X8000_MT] = { ++ /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */ ++ .name = "Geniatech X8000-MT DVBT", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x00000000, ++ .gpio1 = 0x00e3e341, ++ .gpio2 = 0x00000000, ++ .gpio3 = 0x00000000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x00000000, ++ .gpio1 = 0x00e3e361, ++ .gpio2 = 0x00000000, ++ .gpio3 = 0x00000000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x00000000, ++ .gpio1 = 0x00e3e361, ++ .gpio2 = 0x00000000, ++ .gpio3 = 0x00000000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x00000000, ++ .gpio1 = 0x00e3e341, ++ .gpio2 = 0x00000000, ++ .gpio3 = 0x00000000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { ++ .name = "DViCO FusionHDTV DVB-T PRO", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000067df, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000067df, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = { ++ .name = "DViCO FusionHDTV 7 Gold", ++ .tuner_type = TUNER_XC5000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x10df, ++ },{ ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x16d9, ++ },{ ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x16d9, ++ }}, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PROLINK_PV_8000GT] = { ++ .name = "Prolink Pixelview MPEG 8000GT", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0ff, ++ .gpio2 = 0x0cfb, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio2 = 0x0cfb, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio2 = 0x0cfb, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio2 = 0x0cfb, ++ }, ++ }, ++ [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = { ++ .name = "Prolink Pixelview Global Extreme", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x04080, ++ .gpio2 = 0x0cf7, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x04080, ++ .gpio2 = 0x0cfb, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x04fb, ++ .gpio1 = 0x04080, ++ .gpio2 = 0x0cfb, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x04ff, ++ .gpio1 = 0x04080, ++ .gpio2 = 0x0cf7, ++ }, ++ }, ++ /* Both radio, analog and ATSC work with this board. ++ However, for analog to work, s5h1409 gate should be open, ++ otherwise, tuner-xc3028 won't be detected. ++ A proper fix require using the newer i2c methods to add ++ tuner-xc3028 without doing an i2c probe. ++ */ ++ [CX88_BOARD_KWORLD_ATSC_120] = { ++ .name = "Kworld PlusTV HD PCI 120 (ATSC 120)", ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x000000ff, ++ .gpio1 = 0x0000f35d, ++ .gpio2 = 0x00000000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x000000ff, ++ .gpio1 = 0x0000f37e, ++ .gpio2 = 0x00000000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x000000ff, ++ .gpio1 = 0x0000f37e, ++ .gpio2 = 0x00000000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x000000ff, ++ .gpio1 = 0x0000f35d, ++ .gpio2 = 0x00000000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR4000] = { ++ .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid", ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .audio_chip = V4L2_IDENT_WM8775, ++ /* ++ * GPIO0 (WINTV2000) ++ * ++ * Analogue SAT DVB-T ++ * Antenna 0xc4bf 0xc4bb ++ * Composite 0xc4bf 0xc4bb ++ * S-Video 0xc4bf 0xc4bb ++ * Composite1 0xc4ff 0xc4fb ++ * S-Video1 0xc4ff 0xc4fb ++ * ++ * BIT VALUE FUNCTION GP{x}_IO ++ * 0 1 I:? ++ * 1 1 I:? ++ * 2 1 O:MPEG PORT 0=DVB-T 1=DVB-S ++ * 3 1 I:? ++ * 4 1 I:? ++ * 5 1 I:? ++ * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION ++ * 7 1 O:DVB-T DEMOD RESET LOW ++ * ++ * BIT VALUE FUNCTION GP{x}_OE ++ * 8 0 I ++ * 9 0 I ++ * a 1 O ++ * b 0 I ++ * c 0 I ++ * d 0 I ++ * e 1 O ++ * f 1 O ++ * ++ * WM8775 ADC ++ * ++ * 1: TV Audio / FM Mono ++ * 2: Line-In ++ * 3: Line-In Expansion ++ * 4: FM Stereo ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0xc4bf, ++ /* 1: TV Audio / FM Mono */ ++ .audioroute = 1, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0xc4bf, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0xc4bf, ++ /* 2: Line-In */ ++ .audioroute = 2, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0xc4bf, ++ /* 4: FM Stereo */ ++ .audioroute = 8, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ .num_frontends = 2, ++ }, ++ [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = { ++ .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TEVII_S420] = { ++ .name = "TeVii S420 DVB-S", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TEVII_S460] = { ++ .name = "TeVii S460 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TEVII_S464] = { ++ .name = "TeVii S464 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_OMICOM_SS4_PCI] = { ++ .name = "Omicom SS4 DVB-S/S2 PCI", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TBS_8910] = { ++ .name = "TBS 8910 DVB-S", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TBS_8920] = { ++ .name = "TBS 8920 DVB-S/S2", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ .gpio0 = 0x8080, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PROF_6200] = { ++ .name = "Prof 6200 DVB-S", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PROF_7300] = { ++ .name = "PROF 7300 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_SATTRADE_ST4200] = { ++ .name = "SATTRADE ST4200 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { ++ .name = "Terratec Cinergy HT PCI MKII", ++ .tuner_type = TUNER_XC2028, ++ .tuner_addr = 0x61, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x004ff, ++ .gpio1 = 0x010ff, ++ .gpio2 = 0x00001, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x004fb, ++ .gpio1 = 0x010ef, ++ .audioroute = 1, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x004fb, ++ .gpio1 = 0x010ef, ++ .audioroute = 1, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x004ff, ++ .gpio1 = 0x010ff, ++ .gpio2 = 0x0ff, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_HAUPPAUGE_IRONLY] = { ++ .name = "Hauppauge WinTV-IR Only", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ }, ++ [CX88_BOARD_WINFAST_DTV1800H] = { ++ .name = "Leadtek WinFast DTV1800 Hybrid", ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ /* ++ * GPIO setting ++ * ++ * 2: mute (0=off,1=on) ++ * 12: tuner reset pin ++ * 13: audio source (0=tuner audio,1=line in) ++ * 14: FM (0=on,1=off ???) ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ } }, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */ ++ .gpio2 = 0x0000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_WINFAST_DTV1800H_XC4000] = { ++ .name = "Leadtek WinFast DTV1800 H (XC4000)", ++ .tuner_type = TUNER_XC4000, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ /* ++ * GPIO setting ++ * ++ * 2: mute (0=off,1=on) ++ * 12: tuner reset pin ++ * 13: audio source (0=tuner audio,1=line in) ++ * 14: FM (0=on,1=off ???) ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ ++ .gpio2 = 0x0000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0400, /* pin 2 = 0 */ ++ .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */ ++ .gpio2 = 0x0000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_WINFAST_DTV2000H_PLUS] = { ++ .name = "Leadtek WinFast DTV2000 H PLUS", ++ .tuner_type = TUNER_XC4000, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ /* ++ * GPIO ++ * 2: 1: mute audio ++ * 12: 0: reset XC4000 ++ * 13: 1: audio input is line in (0: tuner) ++ * 14: 0: FM radio ++ * 16: 0: RF input is cable ++ */ ++ .input = {{ ++ .type = CX88_VMUX_TELEVISION, ++ .vmux = 0, ++ .gpio0 = 0x0403, ++ .gpio1 = 0xF0D7, ++ .gpio2 = 0x0101, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_CABLE, ++ .vmux = 0, ++ .gpio0 = 0x0403, ++ .gpio1 = 0xF0D7, ++ .gpio2 = 0x0100, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_COMPOSITE1, ++ .vmux = 1, ++ .gpio0 = 0x0403, /* was 0x0407 */ ++ .gpio1 = 0xF0F7, ++ .gpio2 = 0x0101, ++ .gpio3 = 0x0000, ++ }, { ++ .type = CX88_VMUX_SVIDEO, ++ .vmux = 2, ++ .gpio0 = 0x0403, /* was 0x0407 */ ++ .gpio1 = 0xF0F7, ++ .gpio2 = 0x0101, ++ .gpio3 = 0x0000, ++ }}, ++ .radio = { ++ .type = CX88_RADIO, ++ .gpio0 = 0x0403, ++ .gpio1 = 0xF097, ++ .gpio2 = 0x0100, ++ .gpio3 = 0x0000, ++ }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_PROF_7301] = { ++ .name = "Prof 7301 DVB-S/S2", ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = { { ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++ [CX88_BOARD_TWINHAN_VP1027_DVBS] = { ++ .name = "Twinhan VP-1027 DVB-S", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .input = {{ ++ .type = CX88_VMUX_DVB, ++ .vmux = 0, ++ } }, ++ .mpeg = CX88_MPEG_DVB, ++ }, ++}; ++ ++/* ------------------------------------------------------------------ */ ++/* PCI subsystem IDs */ ++ ++static const struct cx88_subid cx88_subids[] = { ++ { ++ .subvendor = 0x0070, ++ .subdevice = 0x3400, ++ .card = CX88_BOARD_HAUPPAUGE, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x3401, ++ .card = CX88_BOARD_HAUPPAUGE, ++ },{ ++ .subvendor = 0x14c7, ++ .subdevice = 0x0106, ++ .card = CX88_BOARD_GDI, ++ },{ ++ .subvendor = 0x14c7, ++ .subdevice = 0x0107, /* with mpeg encoder */ ++ .card = CX88_BOARD_GDI, ++ },{ ++ .subvendor = PCI_VENDOR_ID_ATI, ++ .subdevice = 0x00f8, ++ .card = CX88_BOARD_ATI_WONDER_PRO, ++ }, { ++ .subvendor = PCI_VENDOR_ID_ATI, ++ .subdevice = 0x00f9, ++ .card = CX88_BOARD_ATI_WONDER_PRO, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6611, ++ .card = CX88_BOARD_WINFAST2000XP_EXPERT, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x6613, /* NTSC */ ++ .card = CX88_BOARD_WINFAST2000XP_EXPERT, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x6620, ++ .card = CX88_BOARD_WINFAST_DV2000, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x663b, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x663c, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ },{ ++ .subvendor = 0x1461, ++ .subdevice = 0x000b, ++ .card = CX88_BOARD_AVERTV_STUDIO_303, ++ },{ ++ .subvendor = 0x1462, ++ .subdevice = 0x8606, ++ .card = CX88_BOARD_MSI_TVANYWHERE_MASTER, ++ },{ ++ .subvendor = 0x10fc, ++ .subdevice = 0xd003, ++ .card = CX88_BOARD_IODATA_GVVCP3PCI, ++ },{ ++ .subvendor = 0x1043, ++ .subdevice = 0x4823, /* with mpeg encoder */ ++ .card = CX88_BOARD_ASUS_PVR_416, ++ },{ ++ .subvendor = 0x17de, ++ .subdevice = 0x08a6, ++ .card = CX88_BOARD_KWORLD_DVB_T, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xd810, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xd820, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb00, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9002, ++ .card = CX88_BOARD_HAUPPAUGE_DVB_T1, ++ },{ ++ .subvendor = 0x14f1, ++ .subdevice = 0x0187, ++ .card = CX88_BOARD_CONEXANT_DVB_T1, ++ },{ ++ .subvendor = 0x1540, ++ .subdevice = 0x2580, ++ .card = CX88_BOARD_PROVIDEO_PV259, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb10, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, ++ },{ ++ .subvendor = 0x1554, ++ .subdevice = 0x4811, ++ .card = CX88_BOARD_PIXELVIEW, ++ },{ ++ .subvendor = 0x7063, ++ .subdevice = 0x3000, /* HD-3000 card */ ++ .card = CX88_BOARD_PCHDTV_HD3000, ++ },{ ++ .subvendor = 0x17de, ++ .subdevice = 0xa8a6, ++ .card = CX88_BOARD_DNTV_LIVE_DVB_T, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x2801, ++ .card = CX88_BOARD_HAUPPAUGE_ROSLYN, ++ },{ ++ .subvendor = 0x14f1, ++ .subdevice = 0x0342, ++ .card = CX88_BOARD_DIGITALLOGIC_MEC, ++ },{ ++ .subvendor = 0x10fc, ++ .subdevice = 0xd035, ++ .card = CX88_BOARD_IODATA_GVBCTV7E, ++ },{ ++ .subvendor = 0x1421, ++ .subdevice = 0x0334, ++ .card = CX88_BOARD_ADSTECH_DVB_T_PCI, ++ },{ ++ .subvendor = 0x153b, ++ .subdevice = 0x1166, ++ .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xd500, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD, ++ },{ ++ .subvendor = 0x1461, ++ .subdevice = 0x8011, ++ .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550, ++ },{ ++ .subvendor = PCI_VENDOR_ID_ATI, ++ .subdevice = 0xa101, ++ .card = CX88_BOARD_ATI_HDTVWONDER, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x665f, ++ .card = CX88_BOARD_WINFAST_DTV1000, ++ },{ ++ .subvendor = 0x1461, ++ .subdevice = 0x000a, ++ .card = CX88_BOARD_AVERTV_303, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9200, ++ .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9201, ++ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9202, ++ .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, ++ },{ ++ .subvendor = 0x17de, ++ .subdevice = 0x08b2, ++ .card = CX88_BOARD_KWORLD_DVBS_100, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9400, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1100, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9402, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1100, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9800, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9802, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9001, ++ .card = CX88_BOARD_HAUPPAUGE_DVB_T1, ++ },{ ++ .subvendor = 0x1822, ++ .subdevice = 0x0025, ++ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, ++ },{ ++ .subvendor = 0x17de, ++ .subdevice = 0x08a1, ++ .card = CX88_BOARD_KWORLD_DVB_T_CX22702, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb50, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb54, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, ++ /* Re-branded DViCO: DigitalNow DVB-T Dual */ ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb11, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, ++ /* Re-branded DViCO: UltraView DVB-T Plus */ ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb30, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO, ++ }, { ++ .subvendor = 0x17de, ++ .subdevice = 0x0840, ++ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, ++ },{ ++ .subvendor = 0x1421, ++ .subdevice = 0x0305, ++ .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb40, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xdb44, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, ++ },{ ++ .subvendor = 0x7063, ++ .subdevice = 0x5500, ++ .card = CX88_BOARD_PCHDTV_HD5500, ++ },{ ++ .subvendor = 0x17de, ++ .subdevice = 0x0841, ++ .card = CX88_BOARD_KWORLD_MCE200_DELUXE, ++ },{ ++ .subvendor = 0x1822, ++ .subdevice = 0x0019, ++ .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, ++ },{ ++ .subvendor = 0x1554, ++ .subdevice = 0x4813, ++ .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000, ++ },{ ++ .subvendor = 0x14f1, ++ .subdevice = 0x0842, ++ .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x665e, ++ .card = CX88_BOARD_WINFAST_DTV2000H, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x6f2b, ++ .card = CX88_BOARD_WINFAST_DTV2000H_J, ++ },{ ++ .subvendor = 0x18ac, ++ .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, ++ },{ ++ .subvendor = 0x14f1, ++ .subdevice = 0x0084, ++ .card = CX88_BOARD_GENIATECH_DVBS, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x1404, ++ .card = CX88_BOARD_HAUPPAUGE_HVR3000, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xdc00, ++ .card = CX88_BOARD_SAMSUNG_SMT_7020, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xdccd, ++ .card = CX88_BOARD_SAMSUNG_SMT_7020, ++ },{ ++ .subvendor = 0x1461, ++ .subdevice = 0xc111, /* AverMedia M150-D */ ++ /* This board is known to work with the ASUS PVR416 config */ ++ .card = CX88_BOARD_ASUS_PVR_416, ++ },{ ++ .subvendor = 0xc180, ++ .subdevice = 0xc980, ++ .card = CX88_BOARD_TE_DTV_250_OEM_SWANN, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9600, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1300, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9601, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1300, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9602, ++ .card = CX88_BOARD_HAUPPAUGE_HVR1300, ++ },{ ++ .subvendor = 0x107d, ++ .subdevice = 0x6632, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ },{ ++ .subvendor = 0x12ab, ++ .subdevice = 0x2300, /* Club3D Zap TV2100 */ ++ .card = CX88_BOARD_KWORLD_DVB_T_CX22702, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x9000, ++ .card = CX88_BOARD_HAUPPAUGE_DVB_T1, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x1400, ++ .card = CX88_BOARD_HAUPPAUGE_HVR3000, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x1401, ++ .card = CX88_BOARD_HAUPPAUGE_HVR3000, ++ },{ ++ .subvendor = 0x0070, ++ .subdevice = 0x1402, ++ .card = CX88_BOARD_HAUPPAUGE_HVR3000, ++ },{ ++ .subvendor = 0x1421, ++ .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ ++ .card = CX88_BOARD_KWORLD_DVBS_100, ++ },{ ++ .subvendor = 0x1421, ++ .subdevice = 0x0390, ++ .card = CX88_BOARD_ADSTECH_PTV_390, ++ },{ ++ .subvendor = 0x11bd, ++ .subdevice = 0x0051, ++ .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xd530, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO, ++ }, { ++ .subvendor = 0x12ab, ++ .subdevice = 0x1788, ++ .card = CX88_BOARD_PINNACLE_HYBRID_PCTV, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0xea3d, ++ .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6f18, ++ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, ++ }, { ++ .subvendor = 0x14f1, ++ .subdevice = 0x8852, ++ .card = CX88_BOARD_GENIATECH_X8000_MT, ++ }, { ++ .subvendor = 0x18ac, ++ .subdevice = 0xd610, ++ .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD, ++ }, { ++ .subvendor = 0x1554, ++ .subdevice = 0x4935, ++ .card = CX88_BOARD_PROLINK_PV_8000GT, ++ }, { ++ .subvendor = 0x1554, ++ .subdevice = 0x4976, ++ .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME, ++ }, { ++ .subvendor = 0x17de, ++ .subdevice = 0x08c1, ++ .card = CX88_BOARD_KWORLD_ATSC_120, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x6900, ++ .card = CX88_BOARD_HAUPPAUGE_HVR4000, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x6904, ++ .card = CX88_BOARD_HAUPPAUGE_HVR4000, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x6902, ++ .card = CX88_BOARD_HAUPPAUGE_HVR4000, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x6905, ++ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x6906, ++ .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, ++ }, { ++ .subvendor = 0xd420, ++ .subdevice = 0x9022, ++ .card = CX88_BOARD_TEVII_S420, ++ }, { ++ .subvendor = 0xd460, ++ .subdevice = 0x9022, ++ .card = CX88_BOARD_TEVII_S460, ++ }, { ++ .subvendor = 0xd464, ++ .subdevice = 0x9022, ++ .card = CX88_BOARD_TEVII_S464, ++ }, { ++ .subvendor = 0xA044, ++ .subdevice = 0x2011, ++ .card = CX88_BOARD_OMICOM_SS4_PCI, ++ }, { ++ .subvendor = 0x8910, ++ .subdevice = 0x8888, ++ .card = CX88_BOARD_TBS_8910, ++ }, { ++ .subvendor = 0x8920, ++ .subdevice = 0x8888, ++ .card = CX88_BOARD_TBS_8920, ++ }, { ++ .subvendor = 0xb022, ++ .subdevice = 0x3022, ++ .card = CX88_BOARD_PROF_6200, ++ }, { ++ .subvendor = 0xB033, ++ .subdevice = 0x3033, ++ .card = CX88_BOARD_PROF_7300, ++ }, { ++ .subvendor = 0xb200, ++ .subdevice = 0x4200, ++ .card = CX88_BOARD_SATTRADE_ST4200, ++ }, { ++ .subvendor = 0x153b, ++ .subdevice = 0x1177, ++ .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x9290, ++ .card = CX88_BOARD_HAUPPAUGE_IRONLY, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6654, ++ .card = CX88_BOARD_WINFAST_DTV1800H, ++ }, { ++ /* WinFast DTV1800 H with XC4000 tuner */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6f38, ++ .card = CX88_BOARD_WINFAST_DTV1800H_XC4000, ++ }, { ++ .subvendor = 0x107d, ++ .subdevice = 0x6f42, ++ .card = CX88_BOARD_WINFAST_DTV2000H_PLUS, ++ }, { ++ /* PVR2000 PAL Model [107d:6630] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6630, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ }, { ++ /* PVR2000 PAL Model [107d:6638] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6638, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ }, { ++ /* PVR2000 NTSC Model [107d:6631] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6631, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ }, { ++ /* PVR2000 NTSC Model [107d:6637] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6637, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ }, { ++ /* PVR2000 NTSC Model [107d:663d] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x663d, ++ .card = CX88_BOARD_LEADTEK_PVR2000, ++ }, { ++ /* DV2000 NTSC Model [107d:6621] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6621, ++ .card = CX88_BOARD_WINFAST_DV2000, ++ }, { ++ /* TV2000 XP Global [107d:6618] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6618, ++ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, ++ }, { ++ /* TV2000 XP Global [107d:6618] */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6619, ++ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, ++ }, { ++ /* WinFast TV2000 XP Global with XC4000 tuner */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6f36, ++ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36, ++ }, { ++ /* WinFast TV2000 XP Global with XC4000 tuner and different GPIOs */ ++ .subvendor = 0x107d, ++ .subdevice = 0x6f43, ++ .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43, ++ }, { ++ .subvendor = 0xb034, ++ .subdevice = 0x3034, ++ .card = CX88_BOARD_PROF_7301, ++ }, { ++ .subvendor = 0x1822, ++ .subdevice = 0x0023, ++ .card = CX88_BOARD_TWINHAN_VP1027_DVBS, ++ }, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++/* some leadtek specific stuff */ ++ ++static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) ++{ ++ if (eeprom_data[4] != 0x7d || ++ eeprom_data[5] != 0x10 || ++ eeprom_data[7] != 0x66) { ++ warn_printk(core, "Leadtek eeprom invalid.\n"); ++ return; ++ } ++ ++ /* Terry Wu */ ++ switch (eeprom_data[6]) { ++ case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */ ++ case 0x21: /* SSID 6621 for DV2000 NTSC Model */ ++ case 0x31: /* SSID 6631 for PVR2000 NTSC Model */ ++ case 0x37: /* SSID 6637 for PVR2000 NTSC Model */ ++ case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */ ++ core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3; ++ break; ++ default: ++ core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3; ++ break; ++ } ++ ++ info_printk(core, "Leadtek Winfast 2000XP Expert config: " ++ "tuner=%d, eeprom[0]=0x%02x\n", ++ core->board.tuner_type, eeprom_data[0]); ++} ++ ++static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) ++{ ++ struct tveeprom tv; ++ ++ tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data); ++ core->board.tuner_type = tv.tuner_type; ++ core->tuner_formats = tv.tuner_formats; ++ core->board.radio.type = tv.has_radio ? CX88_RADIO : 0; ++ ++ /* Make sure we support the board model */ ++ switch (tv.model) ++ { ++ case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */ ++ case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */ ++ case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */ ++ case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */ ++ case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */ ++ case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */ ++ case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */ ++ case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */ ++ case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */ ++ case 28552: /* WinTV-PVR 'Roslyn' (No IR) */ ++ case 34519: /* WinTV-PCI-FM */ ++ case 69009: ++ /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */ ++ case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */ ++ case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */ ++ case 69559: ++ /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */ ++ case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */ ++ case 90002: /* Nova-T-PCI (9002) */ ++ case 92001: /* Nova-S-Plus (Video and IR) */ ++ case 92002: /* Nova-S-Plus (Video and IR) */ ++ case 90003: /* Nova-T-PCI (9002 No RF out) */ ++ case 90500: /* Nova-T-PCI (oem) */ ++ case 90501: /* Nova-T-PCI (oem/IR) */ ++ case 92000: /* Nova-SE2 (OEM, No Video or IR) */ ++ case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */ ++ case 94009: /* WinTV-HVR1100 (Video and IR Retail) */ ++ case 94501: /* WinTV-HVR1100 (Video and IR OEM) */ ++ case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */ ++ case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */ ++ case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */ ++ case 96569: /* WinTV-HVR1300 () */ ++ case 96659: /* WinTV-HVR1300 () */ ++ case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */ ++ /* known */ ++ break; ++ case CX88_BOARD_SAMSUNG_SMT_7020: ++ cx_set(MO_GP0_IO, 0x008989FF); ++ break; ++ default: ++ warn_printk(core, "warning: unknown hauppauge model #%d\n", ++ tv.model); ++ break; ++ } ++ ++ info_printk(core, "hauppauge eeprom: model=%d\n", tv.model); ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* some GDI (was: Modular Technology) specific stuff */ ++ ++static const struct { ++ int id; ++ int fm; ++ const char *name; ++} gdi_tuner[] = { ++ [ 0x01 ] = { .id = TUNER_ABSENT, ++ .name = "NTSC_M" }, ++ [ 0x02 ] = { .id = TUNER_ABSENT, ++ .name = "PAL_B" }, ++ [ 0x03 ] = { .id = TUNER_ABSENT, ++ .name = "PAL_I" }, ++ [ 0x04 ] = { .id = TUNER_ABSENT, ++ .name = "PAL_D" }, ++ [ 0x05 ] = { .id = TUNER_ABSENT, ++ .name = "SECAM" }, ++ ++ [ 0x10 ] = { .id = TUNER_ABSENT, ++ .fm = 1, ++ .name = "TEMIC_4049" }, ++ [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5, ++ .name = "TEMIC_4136" }, ++ [ 0x12 ] = { .id = TUNER_ABSENT, ++ .name = "TEMIC_4146" }, ++ ++ [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, ++ .fm = 1, ++ .name = "PHILIPS_FQ1216_MK3" }, ++ [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1, ++ .name = "PHILIPS_FQ1236_MK3" }, ++ [ 0x22 ] = { .id = TUNER_ABSENT, ++ .name = "PHILIPS_FI1236_MK3" }, ++ [ 0x23 ] = { .id = TUNER_ABSENT, ++ .name = "PHILIPS_FI1216_MK3" }, ++}; ++ ++static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) ++{ ++ const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner)) ++ ? gdi_tuner[eeprom_data[0x0d]].name : NULL; ++ ++ info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown"); ++ if (NULL == name) ++ return; ++ core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id; ++ core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ? ++ CX88_RADIO : 0; ++} ++ ++/* ------------------------------------------------------------------- */ ++/* some Divco specific stuff */ ++static int cx88_dvico_xc2028_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ switch (core->boardnr) { ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ++ /* GPIO-4 xc3028 tuner */ ++ ++ cx_set(MO_GP0_IO, 0x00001000); ++ cx_clear(MO_GP0_IO, 0x00000010); ++ msleep(100); ++ cx_set(MO_GP0_IO, 0x00000010); ++ msleep(100); ++ break; ++ default: ++ cx_write(MO_GP0_IO, 0x101000); ++ mdelay(5); ++ cx_set(MO_GP0_IO, 0x101010); ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ ++/* ----------------------------------------------------------------------- */ ++/* some Geniatech specific stuff */ ++ ++static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core, ++ int command, int mode) ++{ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ switch (INPUT(core->input).type) { ++ case CX88_RADIO: ++ break; ++ case CX88_VMUX_DVB: ++ cx_write(MO_GP1_IO, 0x030302); ++ mdelay(50); ++ break; ++ default: ++ cx_write(MO_GP1_IO, 0x030301); ++ mdelay(50); ++ } ++ cx_write(MO_GP1_IO, 0x101010); ++ mdelay(50); ++ cx_write(MO_GP1_IO, 0x101000); ++ mdelay(50); ++ cx_write(MO_GP1_IO, 0x101010); ++ mdelay(50); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ /* GPIO 12 (xc3028 tuner reset) */ ++ cx_set(MO_GP1_IO, 0x1010); ++ mdelay(50); ++ cx_clear(MO_GP1_IO, 0x10); ++ mdelay(75); ++ cx_set(MO_GP1_IO, 0x10); ++ mdelay(75); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ switch (command) { ++ case XC4000_TUNER_RESET: ++ /* GPIO 12 (xc4000 tuner reset) */ ++ cx_set(MO_GP1_IO, 0x1010); ++ mdelay(50); ++ cx_clear(MO_GP1_IO, 0x10); ++ mdelay(75); ++ cx_set(MO_GP1_IO, 0x10); ++ mdelay(75); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ------------------------------------------------------------------- */ ++/* some Divco specific stuff */ ++static int cx88_pv_8000gt_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ cx_write(MO_GP2_IO, 0xcf7); ++ mdelay(50); ++ cx_write(MO_GP2_IO, 0xef5); ++ mdelay(50); ++ cx_write(MO_GP2_IO, 0xcf7); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* some DViCO specific stuff */ ++ ++static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core) ++{ ++ struct i2c_msg msg = { .addr = 0x45, .flags = 0 }; ++ int i, err; ++ static u8 init_bufs[13][5] = { ++ { 0x10, 0x00, 0x20, 0x01, 0x03 }, ++ { 0x10, 0x10, 0x01, 0x00, 0x21 }, ++ { 0x10, 0x10, 0x10, 0x00, 0xCA }, ++ { 0x10, 0x10, 0x12, 0x00, 0x08 }, ++ { 0x10, 0x10, 0x13, 0x00, 0x0A }, ++ { 0x10, 0x10, 0x16, 0x01, 0xC0 }, ++ { 0x10, 0x10, 0x22, 0x01, 0x3D }, ++ { 0x10, 0x10, 0x73, 0x01, 0x2E }, ++ { 0x10, 0x10, 0x72, 0x00, 0xC5 }, ++ { 0x10, 0x10, 0x71, 0x01, 0x97 }, ++ { 0x10, 0x10, 0x70, 0x00, 0x0F }, ++ { 0x10, 0x10, 0xB0, 0x00, 0x01 }, ++ { 0x03, 0x0C }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(init_bufs); i++) { ++ msg.buf = init_bufs[i]; ++ msg.len = (i != 12 ? 5 : 2); ++ err = i2c_transfer(&core->i2c_adap, &msg, 1); ++ if (err != 1) { ++ warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d " ++ "failed (err = %d)!\n", i, err); ++ return; ++ } ++ } ++} ++ ++static int cx88_xc2028_tuner_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ /* Board-specific callbacks */ ++ switch (core->boardnr) { ++ case CX88_BOARD_POWERCOLOR_REAL_ANGEL: ++ case CX88_BOARD_GENIATECH_X8000_MT: ++ case CX88_BOARD_KWORLD_ATSC_120: ++ return cx88_xc3028_geniatech_tuner_callback(core, ++ command, arg); ++ case CX88_BOARD_PROLINK_PV_8000GT: ++ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: ++ return cx88_pv_8000gt_callback(core, command, arg); ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ++ return cx88_dvico_xc2028_callback(core, command, arg); ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ return cx88_xc3028_winfast1800h_callback(core, command, arg); ++ } ++ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ switch (INPUT(core->input).type) { ++ case CX88_RADIO: ++ info_printk(core, "setting GPIO to radio!\n"); ++ cx_write(MO_GP0_IO, 0x4ff); ++ mdelay(250); ++ cx_write(MO_GP2_IO, 0xff); ++ mdelay(250); ++ break; ++ case CX88_VMUX_DVB: /* Digital TV*/ ++ default: /* Analog TV */ ++ info_printk(core, "setting GPIO to TV!\n"); ++ break; ++ } ++ cx_write(MO_GP1_IO, 0x101010); ++ mdelay(250); ++ cx_write(MO_GP1_IO, 0x101000); ++ mdelay(250); ++ cx_write(MO_GP1_IO, 0x101010); ++ mdelay(250); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int cx88_xc4000_tuner_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ /* Board-specific callbacks */ ++ switch (core->boardnr) { ++ case CX88_BOARD_WINFAST_DTV1800H_XC4000: ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: ++ return cx88_xc4000_winfast2000h_plus_callback(core, ++ command, arg); ++ } ++ return -EINVAL; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* Tuner callback function. Currently only needed for the Pinnacle * ++ * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both * ++ * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */ ++ ++static int cx88_xc5000_tuner_callback(struct cx88_core *core, ++ int command, int arg) ++{ ++ switch (core->boardnr) { ++ case CX88_BOARD_PINNACLE_PCTV_HD_800i: ++ if (command == 0) { /* This is the reset command from xc5000 */ ++ ++ /* djh - According to the engineer at PCTV Systems, ++ the xc5000 reset pin is supposed to be on GPIO12. ++ However, despite three nights of effort, pulling ++ that GPIO low didn't reset the xc5000. While ++ pulling MO_SRST_IO low does reset the xc5000, this ++ also resets in the s5h1409 being reset as well. ++ This causes tuning to always fail since the internal ++ state of the s5h1409 does not match the driver's ++ state. Given that the only two conditions in which ++ the driver performs a reset is during firmware load ++ and powering down the chip, I am taking out the ++ reset. We know that the chip is being reset ++ when the cx88 comes online, and not being able to ++ do power management for this board is worse than ++ not having any tuning at all. */ ++ return 0; ++ } else { ++ err_printk(core, "xc5000: unknown tuner " ++ "callback command.\n"); ++ return -EINVAL; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: ++ if (command == 0) { /* This is the reset command from xc5000 */ ++ cx_clear(MO_GP0_IO, 0x00000010); ++ msleep(10); ++ cx_set(MO_GP0_IO, 0x00000010); ++ return 0; ++ } else { ++ printk(KERN_ERR ++ "xc5000: unknown tuner callback command.\n"); ++ return -EINVAL; ++ } ++ break; ++ } ++ return 0; /* Should never be here */ ++} ++ ++int cx88_tuner_callback(void *priv, int component, int command, int arg) ++{ ++ struct i2c_algo_bit_data *i2c_algo = priv; ++ struct cx88_core *core; ++ ++ if (!i2c_algo) { ++ printk(KERN_ERR "cx88: Error - i2c private data undefined.\n"); ++ return -EINVAL; ++ } ++ ++ core = i2c_algo->data; ++ ++ if (!core) { ++ printk(KERN_ERR "cx88: Error - device struct undefined.\n"); ++ return -EINVAL; ++ } ++ ++ if (component != DVB_FRONTEND_COMPONENT_TUNER) ++ return -EINVAL; ++ ++ switch (core->board.tuner_type) { ++ case TUNER_XC2028: ++ info_printk(core, "Calling XC2028/3028 callback\n"); ++ return cx88_xc2028_tuner_callback(core, command, arg); ++ case TUNER_XC4000: ++ info_printk(core, "Calling XC4000 callback\n"); ++ return cx88_xc4000_tuner_callback(core, command, arg); ++ case TUNER_XC5000: ++ info_printk(core, "Calling XC5000 callback\n"); ++ return cx88_xc5000_tuner_callback(core, command, arg); ++ } ++ err_printk(core, "Error: Calling callback for tuner %d\n", ++ core->board.tuner_type); ++ return -EINVAL; ++} ++EXPORT_SYMBOL(cx88_tuner_callback); ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) ++{ ++ int i; ++ ++ if (0 == pci->subsystem_vendor && ++ 0 == pci->subsystem_device) { ++ printk(KERN_ERR ++ "%s: Your board has no valid PCI Subsystem ID and thus can't\n" ++ "%s: be autodetected. Please pass card= insmod option to\n" ++ "%s: workaround that. Redirect complaints to the vendor of\n" ++ "%s: the TV card. Best regards,\n" ++ "%s: -- tux\n", ++ core->name,core->name,core->name,core->name,core->name); ++ } else { ++ printk(KERN_ERR ++ "%s: Your board isn't known (yet) to the driver. You can\n" ++ "%s: try to pick one of the existing card configs via\n" ++ "%s: card= insmod option. Updating to the latest\n" ++ "%s: version might help as well.\n", ++ core->name,core->name,core->name,core->name); ++ } ++ err_printk(core, "Here is a list of valid choices for the card= " ++ "insmod option:\n"); ++ for (i = 0; i < ARRAY_SIZE(cx88_boards); i++) ++ printk(KERN_ERR "%s: card=%d -> %s\n", ++ core->name, i, cx88_boards[i].name); ++} ++ ++static void cx88_card_setup_pre_i2c(struct cx88_core *core) ++{ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* ++ * Bring the 702 demod up before i2c scanning/attach or devices are hidden ++ * We leave here with the 702 on the bus ++ * ++ * "reset the IR receiver on GPIO[3]" ++ * Reported by Mike Crash ++ */ ++ cx_write(MO_GP0_IO, 0x0000ef88); ++ udelay(1000); ++ cx_clear(MO_GP0_IO, 0x00000088); ++ udelay(50); ++ cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */ ++ udelay(1000); ++ break; ++ ++ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: ++ case CX88_BOARD_PROLINK_PV_8000GT: ++ cx_write(MO_GP2_IO, 0xcf7); ++ mdelay(50); ++ cx_write(MO_GP2_IO, 0xef5); ++ mdelay(50); ++ cx_write(MO_GP2_IO, 0xcf7); ++ msleep(10); ++ break; ++ ++ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: ++ /* Enable the xc5000 tuner */ ++ cx_set(MO_GP0_IO, 0x00001010); ++ break; ++ ++ case CX88_BOARD_WINFAST_DTV2000H_J: ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ /* Init GPIO */ ++ cx_write(MO_GP0_IO, core->board.input[0].gpio0); ++ udelay(1000); ++ cx_clear(MO_GP0_IO, 0x00000080); ++ udelay(50); ++ cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ ++ udelay(1000); ++ break; ++ ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0); ++ break; ++ ++ case CX88_BOARD_WINFAST_DTV1800H_XC4000: ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: ++ cx88_xc4000_winfast2000h_plus_callback(core, ++ XC4000_TUNER_RESET, 0); ++ break; ++ ++ case CX88_BOARD_TWINHAN_VP1027_DVBS: ++ cx_write(MO_GP0_IO, 0x00003230); ++ cx_write(MO_GP0_IO, 0x00003210); ++ msleep(1); ++ cx_write(MO_GP0_IO, 0x00001230); ++ break; ++ } ++} ++ ++/* ++ * Sets board-dependent xc3028 configuration ++ */ ++void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) ++{ ++ memset(ctl, 0, sizeof(*ctl)); ++ ++ ctl->fname = XC2028_DEFAULT_FIRMWARE; ++ ctl->max_len = 64; ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_POWERCOLOR_REAL_ANGEL: ++ /* Now works with firmware version 2.7 */ ++ if (core->i2c_algo.udelay < 16) ++ core->i2c_algo.udelay = 16; ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ ctl->demod = XC3028_FE_ZARLINK456; ++ break; ++ case CX88_BOARD_KWORLD_ATSC_120: ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ++ ctl->demod = XC3028_FE_OREN538; ++ break; ++ case CX88_BOARD_GENIATECH_X8000_MT: ++ /* FIXME: For this board, the xc3028 never recovers after being ++ powered down (the reset GPIO probably is not set properly). ++ We don't have access to the hardware so we cannot determine ++ which GPIO is used for xc3028, so just disable power xc3028 ++ power management for now */ ++ ctl->disable_power_mgmt = 1; ++ break; ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ++ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: ++ case CX88_BOARD_PROLINK_PV_8000GT: ++ /* ++ * Those boards uses non-MTS firmware ++ */ ++ break; ++ case CX88_BOARD_PINNACLE_HYBRID_PCTV: ++ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ++ ctl->demod = XC3028_FE_ZARLINK456; ++ ctl->mts = 1; ++ break; ++ default: ++ ctl->demod = XC3028_FE_OREN538; ++ ctl->mts = 1; ++ } ++} ++EXPORT_SYMBOL_GPL(cx88_setup_xc3028); ++ ++static void cx88_card_setup(struct cx88_core *core) ++{ ++ static u8 eeprom[256]; ++ struct tuner_setup tun_setup; ++ unsigned int mode_mask = T_RADIO | T_ANALOG_TV; ++ ++ memset(&tun_setup, 0, sizeof(tun_setup)); ++ ++ if (0 == core->i2c_rc) { ++ core->i2c_client.addr = 0xa0 >> 1; ++ tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom)); ++ } ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE: ++ case CX88_BOARD_HAUPPAUGE_ROSLYN: ++ if (0 == core->i2c_rc) ++ hauppauge_eeprom(core, eeprom+8); ++ break; ++ case CX88_BOARD_GDI: ++ if (0 == core->i2c_rc) ++ gdi_eeprom(core, eeprom); ++ break; ++ case CX88_BOARD_LEADTEK_PVR2000: ++ case CX88_BOARD_WINFAST_DV2000: ++ case CX88_BOARD_WINFAST2000XP_EXPERT: ++ if (0 == core->i2c_rc) ++ leadtek_eeprom(core, eeprom); ++ break; ++ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: ++ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: ++ case CX88_BOARD_HAUPPAUGE_DVB_T1: ++ case CX88_BOARD_HAUPPAUGE_HVR1100: ++ case CX88_BOARD_HAUPPAUGE_HVR1100LP: ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000LITE: ++ case CX88_BOARD_HAUPPAUGE_IRONLY: ++ if (0 == core->i2c_rc) ++ hauppauge_eeprom(core, eeprom); ++ break; ++ case CX88_BOARD_KWORLD_DVBS_100: ++ cx_write(MO_GP0_IO, 0x000007f8); ++ cx_write(MO_GP1_IO, 0x00000001); ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: ++ /* GPIO0:0 is hooked to demod reset */ ++ /* GPIO0:4 is hooked to xc3028 reset */ ++ cx_write(MO_GP0_IO, 0x00111100); ++ msleep(1); ++ cx_write(MO_GP0_IO, 0x00111111); ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: ++ /* GPIO0:6 is hooked to FX2 reset pin */ ++ cx_set(MO_GP0_IO, 0x00004040); ++ cx_clear(MO_GP0_IO, 0x00000040); ++ msleep(1000); ++ cx_set(MO_GP0_IO, 0x00004040); ++ /* FALLTHROUGH */ ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: ++ /* GPIO0:0 is hooked to mt352 reset pin */ ++ cx_set(MO_GP0_IO, 0x00000101); ++ cx_clear(MO_GP0_IO, 0x00000001); ++ msleep(1); ++ cx_set(MO_GP0_IO, 0x00000101); ++ if (0 == core->i2c_rc && ++ core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID) ++ dvico_fusionhdtv_hybrid_init(core); ++ break; ++ case CX88_BOARD_KWORLD_DVB_T: ++ case CX88_BOARD_DNTV_LIVE_DVB_T: ++ cx_set(MO_GP0_IO, 0x00000707); ++ cx_set(MO_GP2_IO, 0x00000101); ++ cx_clear(MO_GP2_IO, 0x00000001); ++ msleep(1); ++ cx_clear(MO_GP0_IO, 0x00000007); ++ cx_set(MO_GP2_IO, 0x00000101); ++ break; ++ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ++ cx_write(MO_GP0_IO, 0x00080808); ++ break; ++ case CX88_BOARD_ATI_HDTVWONDER: ++ if (0 == core->i2c_rc) { ++ /* enable tuner */ ++ int i; ++ static const u8 buffer [][2] = { ++ {0x10,0x12}, ++ {0x13,0x04}, ++ {0x16,0x00}, ++ {0x14,0x04}, ++ {0x17,0x00} ++ }; ++ core->i2c_client.addr = 0x0a; ++ ++ for (i = 0; i < ARRAY_SIZE(buffer); i++) ++ if (2 != i2c_master_send(&core->i2c_client, ++ buffer[i],2)) ++ warn_printk(core, "Unable to enable " ++ "tuner(%i).\n", i); ++ } ++ break; ++ case CX88_BOARD_MSI_TVANYWHERE_MASTER: ++ { ++ struct v4l2_priv_tun_config tea5767_cfg; ++ struct tea5767_ctrl ctl; ++ ++ memset(&ctl, 0, sizeof(ctl)); ++ ++ ctl.high_cut = 1; ++ ctl.st_noise = 1; ++ ctl.deemph_75 = 1; ++ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; ++ ++ tea5767_cfg.tuner = TUNER_TEA5767; ++ tea5767_cfg.priv = &ctl; ++ ++ call_all(core, tuner, s_config, &tea5767_cfg); ++ break; ++ } ++ case CX88_BOARD_TEVII_S420: ++ case CX88_BOARD_TEVII_S460: ++ case CX88_BOARD_TEVII_S464: ++ case CX88_BOARD_OMICOM_SS4_PCI: ++ case CX88_BOARD_TBS_8910: ++ case CX88_BOARD_TBS_8920: ++ case CX88_BOARD_PROF_6200: ++ case CX88_BOARD_PROF_7300: ++ case CX88_BOARD_PROF_7301: ++ case CX88_BOARD_SATTRADE_ST4200: ++ cx_write(MO_GP0_IO, 0x8000); ++ msleep(100); ++ cx_write(MO_SRST_IO, 0); ++ msleep(10); ++ cx_write(MO_GP0_IO, 0x8080); ++ msleep(100); ++ cx_write(MO_SRST_IO, 1); ++ msleep(100); ++ break; ++ } /*end switch() */ ++ ++ ++ /* Setup tuners */ ++ if ((core->board.radio_type != UNSET)) { ++ tun_setup.mode_mask = T_RADIO; ++ tun_setup.type = core->board.radio_type; ++ tun_setup.addr = core->board.radio_addr; ++ tun_setup.tuner_callback = cx88_tuner_callback; ++ call_all(core, tuner, s_type_addr, &tun_setup); ++ mode_mask &= ~T_RADIO; ++ } ++ ++ if (core->board.tuner_type != TUNER_ABSENT) { ++ tun_setup.mode_mask = mode_mask; ++ tun_setup.type = core->board.tuner_type; ++ tun_setup.addr = core->board.tuner_addr; ++ tun_setup.tuner_callback = cx88_tuner_callback; ++ ++ call_all(core, tuner, s_type_addr, &tun_setup); ++ } ++ ++ if (core->board.tda9887_conf) { ++ struct v4l2_priv_tun_config tda9887_cfg; ++ ++ tda9887_cfg.tuner = TUNER_TDA9887; ++ tda9887_cfg.priv = &core->board.tda9887_conf; ++ ++ call_all(core, tuner, s_config, &tda9887_cfg); ++ } ++ ++ if (core->board.tuner_type == TUNER_XC2028) { ++ struct v4l2_priv_tun_config xc2028_cfg; ++ struct xc2028_ctrl ctl; ++ ++ /* Fills device-dependent initialization parameters */ ++ cx88_setup_xc3028(core, &ctl); ++ ++ /* Sends parameters to xc2028/3028 tuner */ ++ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); ++ xc2028_cfg.tuner = TUNER_XC2028; ++ xc2028_cfg.priv = &ctl; ++ info_printk(core, "Asking xc2028/3028 to load firmware %s\n", ++ ctl.fname); ++ call_all(core, tuner, s_config, &xc2028_cfg); ++ } ++ call_all(core, core, s_power, 0); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int cx88_pci_quirks(const char *name, struct pci_dev *pci) ++{ ++ unsigned int lat = UNSET; ++ u8 ctrl = 0; ++ u8 value; ++ ++ /* check pci quirks */ ++ if (pci_pci_problems & PCIPCI_TRITON) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n", ++ name); ++ ctrl |= CX88X_EN_TBFX; ++ } ++ if (pci_pci_problems & PCIPCI_NATOMA) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n", ++ name); ++ ctrl |= CX88X_EN_TBFX; ++ } ++ if (pci_pci_problems & PCIPCI_VIAETBF) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n", ++ name); ++ ctrl |= CX88X_EN_TBFX; ++ } ++ if (pci_pci_problems & PCIPCI_VSFX) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n", ++ name); ++ ctrl |= CX88X_EN_VSFX; ++ } ++#ifdef PCIPCI_ALIMAGIK ++ if (pci_pci_problems & PCIPCI_ALIMAGIK) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", ++ name); ++ lat = 0x0A; ++ } ++#endif ++ ++ /* check insmod options */ ++ if (UNSET != latency) ++ lat = latency; ++ ++ /* apply stuff */ ++ if (ctrl) { ++ pci_read_config_byte(pci, CX88X_DEVCTRL, &value); ++ value |= ctrl; ++ pci_write_config_byte(pci, CX88X_DEVCTRL, value); ++ } ++ if (UNSET != lat) { ++ printk(KERN_INFO "%s: setting pci latency timer to %d\n", ++ name, latency); ++ pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency); ++ } ++ return 0; ++} ++ ++int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci) ++{ ++ if (request_mem_region(pci_resource_start(pci,0), ++ pci_resource_len(pci,0), ++ core->name)) ++ return 0; ++ printk(KERN_ERR ++ "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n", ++ core->name, PCI_FUNC(pci->devfn), ++ (unsigned long long)pci_resource_start(pci, 0), ++ pci->subsystem_vendor, pci->subsystem_device); ++ return -EBUSY; ++} ++ ++/* Allocate and initialize the cx88 core struct. One should hold the ++ * devlist mutex before calling this. */ ++struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) ++{ ++ struct cx88_core *core; ++ int i; ++ ++ core = kzalloc(sizeof(*core), GFP_KERNEL); ++ if (core == NULL) ++ return NULL; ++ ++ atomic_inc(&core->refcount); ++ core->pci_bus = pci->bus->number; ++ core->pci_slot = PCI_SLOT(pci->devfn); ++ core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT | ++ PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT | ++ PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT; ++ mutex_init(&core->lock); ++ ++ core->nr = nr; ++ sprintf(core->name, "cx88[%d]", core->nr); ++ ++ strcpy(core->v4l2_dev.name, core->name); ++ if (v4l2_device_register(NULL, &core->v4l2_dev)) { ++ kfree(core); ++ return NULL; ++ } ++ ++ if (v4l2_ctrl_handler_init(&core->video_hdl, 13)) { ++ v4l2_device_unregister(&core->v4l2_dev); ++ kfree(core); ++ return NULL; ++ } ++ ++ if (v4l2_ctrl_handler_init(&core->audio_hdl, 13)) { ++ v4l2_ctrl_handler_free(&core->video_hdl); ++ v4l2_device_unregister(&core->v4l2_dev); ++ kfree(core); ++ return NULL; ++ } ++ ++ if (0 != cx88_get_resources(core, pci)) { ++ v4l2_ctrl_handler_free(&core->video_hdl); ++ v4l2_ctrl_handler_free(&core->audio_hdl); ++ v4l2_device_unregister(&core->v4l2_dev); ++ kfree(core); ++ return NULL; ++ } ++ ++ /* PCI stuff */ ++ cx88_pci_quirks(core->name, pci); ++ core->lmmio = ioremap(pci_resource_start(pci, 0), ++ pci_resource_len(pci, 0)); ++ core->bmmio = (u8 __iomem *)core->lmmio; ++ ++ if (core->lmmio == NULL) { ++ release_mem_region(pci_resource_start(pci, 0), ++ pci_resource_len(pci, 0)); ++ v4l2_ctrl_handler_free(&core->video_hdl); ++ v4l2_ctrl_handler_free(&core->audio_hdl); ++ v4l2_device_unregister(&core->v4l2_dev); ++ kfree(core); ++ return NULL; ++ } ++ ++ /* board config */ ++ core->boardnr = UNSET; ++ if (card[core->nr] < ARRAY_SIZE(cx88_boards)) ++ core->boardnr = card[core->nr]; ++ for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++) ++ if (pci->subsystem_vendor == cx88_subids[i].subvendor && ++ pci->subsystem_device == cx88_subids[i].subdevice) ++ core->boardnr = cx88_subids[i].card; ++ if (UNSET == core->boardnr) { ++ core->boardnr = CX88_BOARD_UNKNOWN; ++ cx88_card_list(core, pci); ++ } ++ ++ core->board = cx88_boards[core->boardnr]; ++ ++ if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB)) ++ core->board.num_frontends = 1; ++ ++ info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n", ++ pci->subsystem_vendor, pci->subsystem_device, core->board.name, ++ core->boardnr, card[core->nr] == core->boardnr ? ++ "insmod option" : "autodetected", ++ core->board.num_frontends); ++ ++ if (tuner[core->nr] != UNSET) ++ core->board.tuner_type = tuner[core->nr]; ++ if (radio[core->nr] != UNSET) ++ core->board.radio_type = radio[core->nr]; ++ ++ info_printk(core, "TV tuner type %d, Radio tuner type %d\n", ++ core->board.tuner_type, core->board.radio_type); ++ ++ /* init hardware */ ++ cx88_reset(core); ++ cx88_card_setup_pre_i2c(core); ++ cx88_i2c_init(core, pci); ++ ++ /* load tuner module, if needed */ ++ if (TUNER_ABSENT != core->board.tuner_type) { ++ /* Ignore 0x6b and 0x6f on cx88 boards. ++ * FusionHDTV5 RT Gold has an ir receiver at 0x6b ++ * and an RTC at 0x6f which can get corrupted if probed. */ ++ static const unsigned short tv_addrs[] = { ++ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ ++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, ++ 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, ++ I2C_CLIENT_END ++ }; ++ int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT); ++ ++ /* I don't trust the radio_type as is stored in the card ++ definitions, so we just probe for it. ++ The radio_type is sometimes missing, or set to UNSET but ++ later code configures a tea5767. ++ */ ++ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, ++ "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO)); ++ if (has_demod) ++ v4l2_i2c_new_subdev(&core->v4l2_dev, ++ &core->i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); ++ if (core->board.tuner_addr == ADDR_UNSET) { ++ v4l2_i2c_new_subdev(&core->v4l2_dev, ++ &core->i2c_adap, "tuner", ++ 0, has_demod ? tv_addrs + 4 : tv_addrs); ++ } else { ++ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, ++ "tuner", core->board.tuner_addr, NULL); ++ } ++ } ++ ++ cx88_card_setup(core); ++ if (!disable_ir) { ++ cx88_i2c_init_ir(core); ++ cx88_ir_init(core, pci); ++ } ++ ++ return core; ++} +diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c +new file mode 100644 +index 0000000..39f095c +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-core.c +@@ -0,0 +1,1131 @@ ++/* ++ * ++ * device driver for Conexant 2388x based TV cards ++ * driver core ++ * ++ * (c) 2003 Gerd Knorr [SuSE Labs] ++ * ++ * (c) 2005-2006 Mauro Carvalho Chehab ++ * - Multituner support ++ * - video_ioctl2 conversion ++ * - PAL/M fixes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int core_debug; ++module_param(core_debug,int,0644); ++MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); ++ ++static unsigned int nicam; ++module_param(nicam,int,0644); ++MODULE_PARM_DESC(nicam,"tv audio is nicam"); ++ ++static unsigned int nocomb; ++module_param(nocomb,int,0644); ++MODULE_PARM_DESC(nocomb,"disable comb filter"); ++ ++#define dprintk(level,fmt, arg...) if (core_debug >= level) \ ++ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) ++ ++static unsigned int cx88_devcount; ++static LIST_HEAD(cx88_devlist); ++static DEFINE_MUTEX(devlist); ++ ++#define NO_SYNC_LINE (-1U) ++ ++/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be ++ generated _after_ lpi lines are transferred. */ ++static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist, ++ unsigned int offset, u32 sync_line, ++ unsigned int bpl, unsigned int padding, ++ unsigned int lines, unsigned int lpi) ++{ ++ struct scatterlist *sg; ++ unsigned int line,todo,sol; ++ ++ /* sync instruction */ ++ if (sync_line != NO_SYNC_LINE) ++ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); ++ ++ /* scan lines */ ++ sg = sglist; ++ for (line = 0; line < lines; line++) { ++ while (offset && offset >= sg_dma_len(sg)) { ++ offset -= sg_dma_len(sg); ++ sg++; ++ } ++ if (lpi && line>0 && !(line % lpi)) ++ sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; ++ else ++ sol = RISC_SOL; ++ if (bpl <= sg_dma_len(sg)-offset) { ++ /* fits into current chunk */ ++ *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); ++ offset+=bpl; ++ } else { ++ /* scanline needs to be split */ ++ todo = bpl; ++ *(rp++)=cpu_to_le32(RISC_WRITE|sol| ++ (sg_dma_len(sg)-offset)); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); ++ todo -= (sg_dma_len(sg)-offset); ++ offset = 0; ++ sg++; ++ while (todo > sg_dma_len(sg)) { ++ *(rp++)=cpu_to_le32(RISC_WRITE| ++ sg_dma_len(sg)); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)); ++ todo -= sg_dma_len(sg); ++ sg++; ++ } ++ *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo); ++ *(rp++)=cpu_to_le32(sg_dma_address(sg)); ++ offset += todo; ++ } ++ offset += padding; ++ } ++ ++ return rp; ++} ++ ++int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int top_offset, unsigned int bottom_offset, ++ unsigned int bpl, unsigned int padding, unsigned int lines) ++{ ++ u32 instructions,fields; ++ __le32 *rp; ++ int rc; ++ ++ fields = 0; ++ if (UNSET != top_offset) ++ fields++; ++ if (UNSET != bottom_offset) ++ fields++; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Padding ++ can cause next bpl to start close to a page border. First DMA ++ region may be smaller than PAGE_SIZE */ ++ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); ++ instructions += 2; ++ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ if (UNSET != top_offset) ++ rp = cx88_risc_field(rp, sglist, top_offset, 0, ++ bpl, padding, lines, 0); ++ if (UNSET != bottom_offset) ++ rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200, ++ bpl, padding, lines, 0); ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); ++ return 0; ++} ++ ++int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, unsigned int bpl, ++ unsigned int lines, unsigned int lpi) ++{ ++ u32 instructions; ++ __le32 *rp; ++ int rc; ++ ++ /* estimate risc mem: worst case is one write per page border + ++ one write per scan line + syncs + jump (all 2 dwords). Here ++ there is no padding and no sync. First DMA region may be smaller ++ than PAGE_SIZE */ ++ instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; ++ instructions += 1; ++ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi); ++ ++ /* save pointer to jmp instruction address */ ++ risc->jmp = rp; ++ BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); ++ return 0; ++} ++ ++int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value) ++{ ++ __le32 *rp; ++ int rc; ++ ++ if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0) ++ return rc; ++ ++ /* write risc instructions */ ++ rp = risc->cpu; ++ *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM); ++ *(rp++) = cpu_to_le32(reg); ++ *(rp++) = cpu_to_le32(value); ++ *(rp++) = cpu_to_le32(mask); ++ *(rp++) = cpu_to_le32(RISC_JUMP); ++ *(rp++) = cpu_to_le32(risc->dma); ++ return 0; ++} ++ ++void ++cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) ++{ ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ BUG_ON(in_interrupt()); ++ videobuf_waiton(q, &buf->vb, 0, 0); ++ videobuf_dma_unmap(q->dev, dma); ++ videobuf_dma_free(dma); ++ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* our SRAM memory layout */ ++ ++/* we are going to put all thr risc programs into host memory, so we ++ * can use the whole SDRAM for the DMA fifos. To simplify things, we ++ * use a static memory layout. That surely will waste memory in case ++ * we don't use all DMA channels at the same time (which will be the ++ * case most of the time). But that still gives us enough FIFO space ++ * to be able to deal with insane long pci latencies ... ++ * ++ * FIFO space allocations: ++ * channel 21 (y video) - 10.0k ++ * channel 22 (u video) - 2.0k ++ * channel 23 (v video) - 2.0k ++ * channel 24 (vbi) - 4.0k ++ * channels 25+26 (audio) - 4.0k ++ * channel 28 (mpeg) - 4.0k ++ * channel 27 (audio rds)- 3.0k ++ * TOTAL = 29.0k ++ * ++ * Every channel has 160 bytes control data (64 bytes instruction ++ * queue and 6 CDT entries), which is close to 2k total. ++ * ++ * Address layout: ++ * 0x0000 - 0x03ff CMDs / reserved ++ * 0x0400 - 0x0bff instruction queues + CDs ++ * 0x0c00 - FIFOs ++ */ ++ ++const struct sram_channel cx88_sram_channels[] = { ++ [SRAM_CH21] = { ++ .name = "video y / packed", ++ .cmds_start = 0x180040, ++ .ctrl_start = 0x180400, ++ .cdt = 0x180400 + 64, ++ .fifo_start = 0x180c00, ++ .fifo_size = 0x002800, ++ .ptr1_reg = MO_DMA21_PTR1, ++ .ptr2_reg = MO_DMA21_PTR2, ++ .cnt1_reg = MO_DMA21_CNT1, ++ .cnt2_reg = MO_DMA21_CNT2, ++ }, ++ [SRAM_CH22] = { ++ .name = "video u", ++ .cmds_start = 0x180080, ++ .ctrl_start = 0x1804a0, ++ .cdt = 0x1804a0 + 64, ++ .fifo_start = 0x183400, ++ .fifo_size = 0x000800, ++ .ptr1_reg = MO_DMA22_PTR1, ++ .ptr2_reg = MO_DMA22_PTR2, ++ .cnt1_reg = MO_DMA22_CNT1, ++ .cnt2_reg = MO_DMA22_CNT2, ++ }, ++ [SRAM_CH23] = { ++ .name = "video v", ++ .cmds_start = 0x1800c0, ++ .ctrl_start = 0x180540, ++ .cdt = 0x180540 + 64, ++ .fifo_start = 0x183c00, ++ .fifo_size = 0x000800, ++ .ptr1_reg = MO_DMA23_PTR1, ++ .ptr2_reg = MO_DMA23_PTR2, ++ .cnt1_reg = MO_DMA23_CNT1, ++ .cnt2_reg = MO_DMA23_CNT2, ++ }, ++ [SRAM_CH24] = { ++ .name = "vbi", ++ .cmds_start = 0x180100, ++ .ctrl_start = 0x1805e0, ++ .cdt = 0x1805e0 + 64, ++ .fifo_start = 0x184400, ++ .fifo_size = 0x001000, ++ .ptr1_reg = MO_DMA24_PTR1, ++ .ptr2_reg = MO_DMA24_PTR2, ++ .cnt1_reg = MO_DMA24_CNT1, ++ .cnt2_reg = MO_DMA24_CNT2, ++ }, ++ [SRAM_CH25] = { ++ .name = "audio from", ++ .cmds_start = 0x180140, ++ .ctrl_start = 0x180680, ++ .cdt = 0x180680 + 64, ++ .fifo_start = 0x185400, ++ .fifo_size = 0x001000, ++ .ptr1_reg = MO_DMA25_PTR1, ++ .ptr2_reg = MO_DMA25_PTR2, ++ .cnt1_reg = MO_DMA25_CNT1, ++ .cnt2_reg = MO_DMA25_CNT2, ++ }, ++ [SRAM_CH26] = { ++ .name = "audio to", ++ .cmds_start = 0x180180, ++ .ctrl_start = 0x180720, ++ .cdt = 0x180680 + 64, /* same as audio IN */ ++ .fifo_start = 0x185400, /* same as audio IN */ ++ .fifo_size = 0x001000, /* same as audio IN */ ++ .ptr1_reg = MO_DMA26_PTR1, ++ .ptr2_reg = MO_DMA26_PTR2, ++ .cnt1_reg = MO_DMA26_CNT1, ++ .cnt2_reg = MO_DMA26_CNT2, ++ }, ++ [SRAM_CH28] = { ++ .name = "mpeg", ++ .cmds_start = 0x180200, ++ .ctrl_start = 0x1807C0, ++ .cdt = 0x1807C0 + 64, ++ .fifo_start = 0x186400, ++ .fifo_size = 0x001000, ++ .ptr1_reg = MO_DMA28_PTR1, ++ .ptr2_reg = MO_DMA28_PTR2, ++ .cnt1_reg = MO_DMA28_CNT1, ++ .cnt2_reg = MO_DMA28_CNT2, ++ }, ++ [SRAM_CH27] = { ++ .name = "audio rds", ++ .cmds_start = 0x1801C0, ++ .ctrl_start = 0x180860, ++ .cdt = 0x180860 + 64, ++ .fifo_start = 0x187400, ++ .fifo_size = 0x000C00, ++ .ptr1_reg = MO_DMA27_PTR1, ++ .ptr2_reg = MO_DMA27_PTR2, ++ .cnt1_reg = MO_DMA27_CNT1, ++ .cnt2_reg = MO_DMA27_CNT2, ++ }, ++}; ++ ++int cx88_sram_channel_setup(struct cx88_core *core, ++ const struct sram_channel *ch, ++ unsigned int bpl, u32 risc) ++{ ++ unsigned int i,lines; ++ u32 cdt; ++ ++ bpl = (bpl + 7) & ~7; /* alignment */ ++ cdt = ch->cdt; ++ lines = ch->fifo_size / bpl; ++ if (lines > 6) ++ lines = 6; ++ BUG_ON(lines < 2); ++ ++ /* write CDT */ ++ for (i = 0; i < lines; i++) ++ cx_write(cdt + 16*i, ch->fifo_start + bpl*i); ++ ++ /* write CMDS */ ++ cx_write(ch->cmds_start + 0, risc); ++ cx_write(ch->cmds_start + 4, cdt); ++ cx_write(ch->cmds_start + 8, (lines*16) >> 3); ++ cx_write(ch->cmds_start + 12, ch->ctrl_start); ++ cx_write(ch->cmds_start + 16, 64 >> 2); ++ for (i = 20; i < 64; i += 4) ++ cx_write(ch->cmds_start + i, 0); ++ ++ /* fill registers */ ++ cx_write(ch->ptr1_reg, ch->fifo_start); ++ cx_write(ch->ptr2_reg, cdt); ++ cx_write(ch->cnt1_reg, (bpl >> 3) -1); ++ cx_write(ch->cnt2_reg, (lines*16) >> 3); ++ ++ dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* debug helper code */ ++ ++static int cx88_risc_decode(u32 risc) ++{ ++ static const char * const instr[16] = { ++ [ RISC_SYNC >> 28 ] = "sync", ++ [ RISC_WRITE >> 28 ] = "write", ++ [ RISC_WRITEC >> 28 ] = "writec", ++ [ RISC_READ >> 28 ] = "read", ++ [ RISC_READC >> 28 ] = "readc", ++ [ RISC_JUMP >> 28 ] = "jump", ++ [ RISC_SKIP >> 28 ] = "skip", ++ [ RISC_WRITERM >> 28 ] = "writerm", ++ [ RISC_WRITECM >> 28 ] = "writecm", ++ [ RISC_WRITECR >> 28 ] = "writecr", ++ }; ++ static int const incr[16] = { ++ [ RISC_WRITE >> 28 ] = 2, ++ [ RISC_JUMP >> 28 ] = 2, ++ [ RISC_WRITERM >> 28 ] = 3, ++ [ RISC_WRITECM >> 28 ] = 3, ++ [ RISC_WRITECR >> 28 ] = 4, ++ }; ++ static const char * const bits[] = { ++ "12", "13", "14", "resync", ++ "cnt0", "cnt1", "18", "19", ++ "20", "21", "22", "23", ++ "irq1", "irq2", "eol", "sol", ++ }; ++ int i; ++ ++ printk("0x%08x [ %s", risc, ++ instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); ++ for (i = ARRAY_SIZE(bits)-1; i >= 0; i--) ++ if (risc & (1 << (i + 12))) ++ printk(" %s",bits[i]); ++ printk(" count=%d ]\n", risc & 0xfff); ++ return incr[risc >> 28] ? incr[risc >> 28] : 1; ++} ++ ++ ++void cx88_sram_channel_dump(struct cx88_core *core, ++ const struct sram_channel *ch) ++{ ++ static const char * const name[] = { ++ "initial risc", ++ "cdt base", ++ "cdt size", ++ "iq base", ++ "iq size", ++ "risc pc", ++ "iq wr ptr", ++ "iq rd ptr", ++ "cdt current", ++ "pci target", ++ "line / byte", ++ }; ++ u32 risc; ++ unsigned int i,j,n; ++ ++ printk("%s: %s - dma channel status dump\n", ++ core->name,ch->name); ++ for (i = 0; i < ARRAY_SIZE(name); i++) ++ printk("%s: cmds: %-12s: 0x%08x\n", ++ core->name,name[i], ++ cx_read(ch->cmds_start + 4*i)); ++ for (n = 1, i = 0; i < 4; i++) { ++ risc = cx_read(ch->cmds_start + 4 * (i+11)); ++ printk("%s: risc%d: ", core->name, i); ++ if (--n) ++ printk("0x%08x [ arg #%d ]\n", risc, n); ++ else ++ n = cx88_risc_decode(risc); ++ } ++ for (i = 0; i < 16; i += n) { ++ risc = cx_read(ch->ctrl_start + 4 * i); ++ printk("%s: iq %x: ", core->name, i); ++ n = cx88_risc_decode(risc); ++ for (j = 1; j < n; j++) { ++ risc = cx_read(ch->ctrl_start + 4 * (i+j)); ++ printk("%s: iq %x: 0x%08x [ arg #%d ]\n", ++ core->name, i+j, risc, j); ++ } ++ } ++ ++ printk("%s: fifo: 0x%08x -> 0x%x\n", ++ core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size); ++ printk("%s: ctrl: 0x%08x -> 0x%x\n", ++ core->name, ch->ctrl_start, ch->ctrl_start+6*16); ++ printk("%s: ptr1_reg: 0x%08x\n", ++ core->name,cx_read(ch->ptr1_reg)); ++ printk("%s: ptr2_reg: 0x%08x\n", ++ core->name,cx_read(ch->ptr2_reg)); ++ printk("%s: cnt1_reg: 0x%08x\n", ++ core->name,cx_read(ch->cnt1_reg)); ++ printk("%s: cnt2_reg: 0x%08x\n", ++ core->name,cx_read(ch->cnt2_reg)); ++} ++ ++static const char *cx88_pci_irqs[32] = { ++ "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1", ++ "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err", ++ "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err", ++ "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1" ++}; ++ ++void cx88_print_irqbits(const char *name, const char *tag, const char *strings[], ++ int len, u32 bits, u32 mask) ++{ ++ unsigned int i; ++ ++ printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); ++ for (i = 0; i < len; i++) { ++ if (!(bits & (1 << i))) ++ continue; ++ if (strings[i]) ++ printk(" %s", strings[i]); ++ else ++ printk(" %d", i); ++ if (!(mask & (1 << i))) ++ continue; ++ printk("*"); ++ } ++ printk("\n"); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int cx88_core_irq(struct cx88_core *core, u32 status) ++{ ++ int handled = 0; ++ ++ if (status & PCI_INT_IR_SMPINT) { ++ cx88_ir_irq(core); ++ handled++; ++ } ++ if (!handled) ++ cx88_print_irqbits(core->name, "irq pci", ++ cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs), ++ status, core->pci_irqmask); ++ return handled; ++} ++ ++void cx88_wakeup(struct cx88_core *core, ++ struct cx88_dmaqueue *q, u32 count) ++{ ++ struct cx88_buffer *buf; ++ int bc; ++ ++ for (bc = 0;; bc++) { ++ if (list_empty(&q->active)) ++ break; ++ buf = list_entry(q->active.next, ++ struct cx88_buffer, vb.queue); ++ /* count comes from the hw and is is 16bit wide -- ++ * this trick handles wrap-arounds correctly for ++ * up to 32767 buffers in flight... */ ++ if ((s16) (count - buf->count) < 0) ++ break; ++ v4l2_get_timestamp(&buf->vb.ts); ++ dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, ++ count, buf->count); ++ buf->vb.state = VIDEOBUF_DONE; ++ list_del(&buf->vb.queue); ++ wake_up(&buf->vb.done); ++ } ++ if (list_empty(&q->active)) { ++ del_timer(&q->timeout); ++ } else { ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ } ++ if (bc != 1) ++ dprintk(2, "%s: %d buffers handled (should be 1)\n", ++ __func__, bc); ++} ++ ++void cx88_shutdown(struct cx88_core *core) ++{ ++ /* disable RISC controller + IRQs */ ++ cx_write(MO_DEV_CNTRL2, 0); ++ ++ /* stop dma transfers */ ++ cx_write(MO_VID_DMACNTRL, 0x0); ++ cx_write(MO_AUD_DMACNTRL, 0x0); ++ cx_write(MO_TS_DMACNTRL, 0x0); ++ cx_write(MO_VIP_DMACNTRL, 0x0); ++ cx_write(MO_GPHST_DMACNTRL, 0x0); ++ ++ /* stop interrupts */ ++ cx_write(MO_PCI_INTMSK, 0x0); ++ cx_write(MO_VID_INTMSK, 0x0); ++ cx_write(MO_AUD_INTMSK, 0x0); ++ cx_write(MO_TS_INTMSK, 0x0); ++ cx_write(MO_VIP_INTMSK, 0x0); ++ cx_write(MO_GPHST_INTMSK, 0x0); ++ ++ /* stop capturing */ ++ cx_write(VID_CAPTURE_CONTROL, 0); ++} ++ ++int cx88_reset(struct cx88_core *core) ++{ ++ dprintk(1,"%s\n",__func__); ++ cx88_shutdown(core); ++ ++ /* clear irq status */ ++ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int ++ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int ++ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int ++ ++ /* wait a bit */ ++ msleep(100); ++ ++ /* init sram */ ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0); ++ ++ /* misc init ... */ ++ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable ++ (1 << 12) | // agc gain ++ (1 << 11) | // adaptibe agc ++ (0 << 10) | // chroma agc ++ (0 << 9) | // ckillen ++ (7))); ++ ++ /* setup image format */ ++ cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000); ++ ++ /* setup FIFO Thresholds */ ++ cx_write(MO_PDMA_STHRSH, 0x0807); ++ cx_write(MO_PDMA_DTHRSH, 0x0807); ++ ++ /* fixes flashing of image */ ++ cx_write(MO_AGC_SYNC_TIP1, 0x0380000F); ++ cx_write(MO_AGC_BACK_VBI, 0x00E00555); ++ ++ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int ++ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int ++ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int ++ ++ /* Reset on-board parts */ ++ cx_write(MO_SRST_IO, 0); ++ msleep(10); ++ cx_write(MO_SRST_IO, 1); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static inline unsigned int norm_swidth(v4l2_std_id norm) ++{ ++ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; ++} ++ ++static inline unsigned int norm_hdelay(v4l2_std_id norm) ++{ ++ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; ++} ++ ++static inline unsigned int norm_vdelay(v4l2_std_id norm) ++{ ++ return (norm & V4L2_STD_625_50) ? 0x24 : 0x18; ++} ++ ++static inline unsigned int norm_fsc8(v4l2_std_id norm) ++{ ++ if (norm & V4L2_STD_PAL_M) ++ return 28604892; // 3.575611 MHz ++ ++ if (norm & (V4L2_STD_PAL_Nc)) ++ return 28656448; // 3.582056 MHz ++ ++ if (norm & V4L2_STD_NTSC) // All NTSC/M and variants ++ return 28636360; // 3.57954545 MHz +/- 10 Hz ++ ++ /* SECAM have also different sub carrier for chroma, ++ but step_db and step_dr, at cx88_set_tvnorm already handles that. ++ ++ The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N ++ */ ++ ++ return 35468950; // 4.43361875 MHz +/- 5 Hz ++} ++ ++static inline unsigned int norm_htotal(v4l2_std_id norm) ++{ ++ ++ unsigned int fsc4=norm_fsc8(norm)/2; ++ ++ /* returns 4*FSC / vtotal / frames per seconds */ ++ return (norm & V4L2_STD_625_50) ? ++ ((fsc4+312)/625+12)/25 : ++ ((fsc4+262)/525*1001+15000)/30000; ++} ++ ++static inline unsigned int norm_vbipack(v4l2_std_id norm) ++{ ++ return (norm & V4L2_STD_625_50) ? 511 : 400; ++} ++ ++int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, ++ enum v4l2_field field) ++{ ++ unsigned int swidth = norm_swidth(core->tvnorm); ++ unsigned int sheight = norm_maxh(core->tvnorm); ++ u32 value; ++ ++ dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, ++ V4L2_FIELD_HAS_TOP(field) ? "T" : "", ++ V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", ++ v4l2_norm_to_name(core->tvnorm)); ++ if (!V4L2_FIELD_HAS_BOTH(field)) ++ height *= 2; ++ ++ // recalc H delay and scale registers ++ value = (width * norm_hdelay(core->tvnorm)) / swidth; ++ value &= 0x3fe; ++ cx_write(MO_HDELAY_EVEN, value); ++ cx_write(MO_HDELAY_ODD, value); ++ dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth); ++ ++ value = (swidth * 4096 / width) - 4096; ++ cx_write(MO_HSCALE_EVEN, value); ++ cx_write(MO_HSCALE_ODD, value); ++ dprintk(1,"set_scale: hscale 0x%04x\n", value); ++ ++ cx_write(MO_HACTIVE_EVEN, width); ++ cx_write(MO_HACTIVE_ODD, width); ++ dprintk(1,"set_scale: hactive 0x%04x\n", width); ++ ++ // recalc V scale Register (delay is constant) ++ cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm)); ++ cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm)); ++ dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm)); ++ ++ value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff; ++ cx_write(MO_VSCALE_EVEN, value); ++ cx_write(MO_VSCALE_ODD, value); ++ dprintk(1,"set_scale: vscale 0x%04x\n", value); ++ ++ cx_write(MO_VACTIVE_EVEN, sheight); ++ cx_write(MO_VACTIVE_ODD, sheight); ++ dprintk(1,"set_scale: vactive 0x%04x\n", sheight); ++ ++ // setup filters ++ value = 0; ++ value |= (1 << 19); // CFILT (default) ++ if (core->tvnorm & V4L2_STD_SECAM) { ++ value |= (1 << 15); ++ value |= (1 << 16); ++ } ++ if (INPUT(core->input).type == CX88_VMUX_SVIDEO) ++ value |= (1 << 13) | (1 << 5); ++ if (V4L2_FIELD_INTERLACED == field) ++ value |= (1 << 3); // VINT (interlaced vertical scaling) ++ if (width < 385) ++ value |= (1 << 0); // 3-tap interpolation ++ if (width < 193) ++ value |= (1 << 1); // 5-tap interpolation ++ if (nocomb) ++ value |= (3 << 5); // disable comb filter ++ ++ cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */ ++ cx_andor(MO_FILTER_ODD, 0x7ffc7f, value); ++ dprintk(1,"set_scale: filter 0x%04x\n", value); ++ ++ return 0; ++} ++ ++static const u32 xtal = 28636363; ++ ++static int set_pll(struct cx88_core *core, int prescale, u32 ofreq) ++{ ++ static const u32 pre[] = { 0, 0, 0, 3, 2, 1 }; ++ u64 pll; ++ u32 reg; ++ int i; ++ ++ if (prescale < 2) ++ prescale = 2; ++ if (prescale > 5) ++ prescale = 5; ++ ++ pll = ofreq * 8 * prescale * (u64)(1 << 20); ++ do_div(pll,xtal); ++ reg = (pll & 0x3ffffff) | (pre[prescale] << 26); ++ if (((reg >> 20) & 0x3f) < 14) { ++ printk("%s/0: pll out of range\n",core->name); ++ return -1; ++ } ++ ++ dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n", ++ reg, cx_read(MO_PLL_REG), ofreq); ++ cx_write(MO_PLL_REG, reg); ++ for (i = 0; i < 100; i++) { ++ reg = cx_read(MO_DEVICE_STATUS); ++ if (reg & (1<<2)) { ++ dprintk(1,"pll locked [pre=%d,ofreq=%d]\n", ++ prescale,ofreq); ++ return 0; ++ } ++ dprintk(1,"pll not locked yet, waiting ...\n"); ++ msleep(10); ++ } ++ dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); ++ return -1; ++} ++ ++int cx88_start_audio_dma(struct cx88_core *core) ++{ ++ /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ ++ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; ++ ++ int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES; ++ ++ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ ++ if (cx_read(MO_AUD_DMACNTRL) & 0x10) ++ return 0; ++ ++ /* setup fifo + format */ ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], ++ rds_bpl, 0); ++ ++ cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */ ++ cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */ ++ ++ /* enable Up, Down and Audio RDS fifo */ ++ cx_write(MO_AUD_DMACNTRL, 0x0007); ++ ++ return 0; ++} ++ ++int cx88_stop_audio_dma(struct cx88_core *core) ++{ ++ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ ++ if (cx_read(MO_AUD_DMACNTRL) & 0x10) ++ return 0; ++ ++ /* stop dma */ ++ cx_write(MO_AUD_DMACNTRL, 0x0000); ++ ++ return 0; ++} ++ ++static int set_tvaudio(struct cx88_core *core) ++{ ++ v4l2_std_id norm = core->tvnorm; ++ ++ if (CX88_VMUX_TELEVISION != INPUT(core->input).type && ++ CX88_VMUX_CABLE != INPUT(core->input).type) ++ return 0; ++ ++ if (V4L2_STD_PAL_BG & norm) { ++ core->tvaudio = WW_BG; ++ ++ } else if (V4L2_STD_PAL_DK & norm) { ++ core->tvaudio = WW_DK; ++ ++ } else if (V4L2_STD_PAL_I & norm) { ++ core->tvaudio = WW_I; ++ ++ } else if (V4L2_STD_SECAM_L & norm) { ++ core->tvaudio = WW_L; ++ ++ } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) { ++ core->tvaudio = WW_BG; ++ ++ } else if (V4L2_STD_SECAM_DK & norm) { ++ core->tvaudio = WW_DK; ++ ++ } else if ((V4L2_STD_NTSC_M & norm) || ++ (V4L2_STD_PAL_M & norm)) { ++ core->tvaudio = WW_BTSC; ++ ++ } else if (V4L2_STD_NTSC_M_JP & norm) { ++ core->tvaudio = WW_EIAJ; ++ ++ } else { ++ printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", ++ core->name, v4l2_norm_to_name(core->tvnorm)); ++ core->tvaudio = WW_NONE; ++ return 0; ++ } ++ ++ cx_andor(MO_AFECFG_IO, 0x1f, 0x0); ++ cx88_set_tvaudio(core); ++ /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */ ++ ++/* ++ This should be needed only on cx88-alsa. It seems that some cx88 chips have ++ bugs and does require DMA enabled for it to work. ++ */ ++ cx88_start_audio_dma(core); ++ return 0; ++} ++ ++ ++ ++int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) ++{ ++ u32 fsc8; ++ u32 adc_clock; ++ u32 vdec_clock; ++ u32 step_db,step_dr; ++ u64 tmp64; ++ u32 bdelay,agcdelay,htotal; ++ u32 cxiformat, cxoformat; ++ ++ core->tvnorm = norm; ++ fsc8 = norm_fsc8(norm); ++ adc_clock = xtal; ++ vdec_clock = fsc8; ++ step_db = fsc8; ++ step_dr = fsc8; ++ ++ if (norm & V4L2_STD_NTSC_M_JP) { ++ cxiformat = VideoFormatNTSCJapan; ++ cxoformat = 0x181f0008; ++ } else if (norm & V4L2_STD_NTSC_443) { ++ cxiformat = VideoFormatNTSC443; ++ cxoformat = 0x181f0008; ++ } else if (norm & V4L2_STD_PAL_M) { ++ cxiformat = VideoFormatPALM; ++ cxoformat = 0x1c1f0008; ++ } else if (norm & V4L2_STD_PAL_N) { ++ cxiformat = VideoFormatPALN; ++ cxoformat = 0x1c1f0008; ++ } else if (norm & V4L2_STD_PAL_Nc) { ++ cxiformat = VideoFormatPALNC; ++ cxoformat = 0x1c1f0008; ++ } else if (norm & V4L2_STD_PAL_60) { ++ cxiformat = VideoFormatPAL60; ++ cxoformat = 0x181f0008; ++ } else if (norm & V4L2_STD_NTSC) { ++ cxiformat = VideoFormatNTSC; ++ cxoformat = 0x181f0008; ++ } else if (norm & V4L2_STD_SECAM) { ++ step_db = 4250000 * 8; ++ step_dr = 4406250 * 8; ++ ++ cxiformat = VideoFormatSECAM; ++ cxoformat = 0x181f0008; ++ } else { /* PAL */ ++ cxiformat = VideoFormatPAL; ++ cxoformat = 0x181f0008; ++ } ++ ++ dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", ++ v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock, ++ step_db, step_dr); ++ set_pll(core,2,vdec_clock); ++ ++ dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", ++ cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); ++ /* Chroma AGC must be disabled if SECAM is used, we enable it ++ by default on PAL and NTSC */ ++ cx_andor(MO_INPUT_FORMAT, 0x40f, ++ norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400); ++ ++ // FIXME: as-is from DScaler ++ dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", ++ cxoformat, cx_read(MO_OUTPUT_FORMAT)); ++ cx_write(MO_OUTPUT_FORMAT, cxoformat); ++ ++ // MO_SCONV_REG = adc clock / video dec clock * 2^17 ++ tmp64 = adc_clock * (u64)(1 << 17); ++ do_div(tmp64, vdec_clock); ++ dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n", ++ (u32)tmp64, cx_read(MO_SCONV_REG)); ++ cx_write(MO_SCONV_REG, (u32)tmp64); ++ ++ // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22 ++ tmp64 = step_db * (u64)(1 << 22); ++ do_div(tmp64, vdec_clock); ++ dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n", ++ (u32)tmp64, cx_read(MO_SUB_STEP)); ++ cx_write(MO_SUB_STEP, (u32)tmp64); ++ ++ // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22 ++ tmp64 = step_dr * (u64)(1 << 22); ++ do_div(tmp64, vdec_clock); ++ dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n", ++ (u32)tmp64, cx_read(MO_SUB_STEP_DR)); ++ cx_write(MO_SUB_STEP_DR, (u32)tmp64); ++ ++ // bdelay + agcdelay ++ bdelay = vdec_clock * 65 / 20000000 + 21; ++ agcdelay = vdec_clock * 68 / 20000000 + 15; ++ dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n", ++ (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay); ++ cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay); ++ ++ // htotal ++ tmp64 = norm_htotal(norm) * (u64)vdec_clock; ++ do_div(tmp64, fsc8); ++ htotal = (u32)tmp64; ++ dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n", ++ htotal, cx_read(MO_HTOTAL), (u32)tmp64); ++ cx_andor(MO_HTOTAL, 0x07ff, htotal); ++ ++ // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes ++ // the effective vbi offset ~244 samples, the same as the Bt8x8 ++ cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm)); ++ ++ // this is needed as well to set all tvnorm parameter ++ cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED); ++ ++ // audio ++ set_tvaudio(core); ++ ++ // tell i2c chips ++ call_all(core, core, s_std, norm); ++ ++ /* The chroma_agc control should be inaccessible if the video format is SECAM */ ++ v4l2_ctrl_grab(core->chroma_agc, cxiformat == VideoFormatSECAM); ++ ++ // done ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++struct video_device *cx88_vdev_init(struct cx88_core *core, ++ struct pci_dev *pci, ++ const struct video_device *template_, ++ const char *type) ++{ ++ struct video_device *vfd; ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template_; ++ vfd->v4l2_dev = &core->v4l2_dev; ++ vfd->release = video_device_release; ++ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", ++ core->name, type, core->board.name); ++ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); ++ return vfd; ++} ++ ++struct cx88_core* cx88_core_get(struct pci_dev *pci) ++{ ++ struct cx88_core *core; ++ ++ mutex_lock(&devlist); ++ list_for_each_entry(core, &cx88_devlist, devlist) { ++ if (pci->bus->number != core->pci_bus) ++ continue; ++ if (PCI_SLOT(pci->devfn) != core->pci_slot) ++ continue; ++ ++ if (0 != cx88_get_resources(core, pci)) { ++ mutex_unlock(&devlist); ++ return NULL; ++ } ++ atomic_inc(&core->refcount); ++ mutex_unlock(&devlist); ++ return core; ++ } ++ ++ core = cx88_core_create(pci, cx88_devcount); ++ if (NULL != core) { ++ cx88_devcount++; ++ list_add_tail(&core->devlist, &cx88_devlist); ++ } ++ ++ mutex_unlock(&devlist); ++ return core; ++} ++ ++void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) ++{ ++ release_mem_region(pci_resource_start(pci,0), ++ pci_resource_len(pci,0)); ++ ++ if (!atomic_dec_and_test(&core->refcount)) ++ return; ++ ++ mutex_lock(&devlist); ++ cx88_ir_fini(core); ++ if (0 == core->i2c_rc) { ++ if (core->i2c_rtc) ++ i2c_unregister_device(core->i2c_rtc); ++ i2c_del_adapter(&core->i2c_adap); ++ } ++ list_del(&core->devlist); ++ iounmap(core->lmmio); ++ cx88_devcount--; ++ mutex_unlock(&devlist); ++ v4l2_ctrl_handler_free(&core->video_hdl); ++ v4l2_ctrl_handler_free(&core->audio_hdl); ++ v4l2_device_unregister(&core->v4l2_dev); ++ kfree(core); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++EXPORT_SYMBOL(cx88_print_irqbits); ++ ++EXPORT_SYMBOL(cx88_core_irq); ++EXPORT_SYMBOL(cx88_wakeup); ++EXPORT_SYMBOL(cx88_reset); ++EXPORT_SYMBOL(cx88_shutdown); ++ ++EXPORT_SYMBOL(cx88_risc_buffer); ++EXPORT_SYMBOL(cx88_risc_databuffer); ++EXPORT_SYMBOL(cx88_risc_stopper); ++EXPORT_SYMBOL(cx88_free_buffer); ++ ++EXPORT_SYMBOL(cx88_sram_channels); ++EXPORT_SYMBOL(cx88_sram_channel_setup); ++EXPORT_SYMBOL(cx88_sram_channel_dump); ++ ++EXPORT_SYMBOL(cx88_set_tvnorm); ++EXPORT_SYMBOL(cx88_set_scale); ++ ++EXPORT_SYMBOL(cx88_vdev_init); ++EXPORT_SYMBOL(cx88_core_get); ++EXPORT_SYMBOL(cx88_core_put); ++ ++EXPORT_SYMBOL(cx88_ir_start); ++EXPORT_SYMBOL(cx88_ir_stop); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off ++ */ +diff --git a/drivers/media/pci/cx88/cx88-dsp.c b/drivers/media/pci/cx88/cx88-dsp.c +new file mode 100644 +index 0000000..a990726 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-dsp.c +@@ -0,0 +1,322 @@ ++/* ++ * ++ * Stereo and SAP detection for cx88 ++ * ++ * Copyright (c) 2009 Marton Balint ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include "cx88-reg.h" ++ ++#define INT_PI ((s32)(3.141592653589 * 32768.0)) ++ ++#define compat_remainder(a, b) \ ++ ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0) ++ ++#define baseband_freq(carrier, srate, tone) ((s32)( \ ++ (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI)) ++ ++/* We calculate the baseband frequencies of the carrier and the pilot tones ++ * based on the the sampling rate of the audio rds fifo. */ ++ ++#define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0) ++#define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1) ++#define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5) ++ ++/* The frequencies below are from the reference driver. They probably need ++ * further adjustments, because they are not tested at all. You may even need ++ * to play a bit with the registers of the chip to select the proper signal ++ * for the input of the audio rds fifo, and measure it's sampling rate to ++ * calculate the proper baseband frequencies... */ ++ ++#define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0)) ++#define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0)) ++#define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0)) ++ ++#define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ ++#define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0)) ++#define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0)) ++ ++#define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ ++#define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */ ++ ++#define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0)) ++#define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0)) ++ ++/* The spectrum of the signal should be empty between these frequencies. */ ++#define FREQ_NOISE_START ((s32)(0.100000 * 32768.0)) ++#define FREQ_NOISE_END ((s32)(1.200000 * 32768.0)) ++ ++static unsigned int dsp_debug; ++module_param(dsp_debug, int, 0644); ++MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages"); ++ ++#define dprintk(level, fmt, arg...) if (dsp_debug >= level) \ ++ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) ++ ++static s32 int_cos(u32 x) ++{ ++ u32 t2, t4, t6, t8; ++ s32 ret; ++ u16 period = x / INT_PI; ++ if (period % 2) ++ return -int_cos(x - INT_PI); ++ x = x % INT_PI; ++ if (x > INT_PI/2) ++ return -int_cos(INT_PI/2 - (x % (INT_PI/2))); ++ /* Now x is between 0 and INT_PI/2. ++ * To calculate cos(x) we use it's Taylor polinom. */ ++ t2 = x*x/32768/2; ++ t4 = t2*x/32768*x/32768/3/4; ++ t6 = t4*x/32768*x/32768/5/6; ++ t8 = t6*x/32768*x/32768/7/8; ++ ret = 32768-t2+t4-t6+t8; ++ return ret; ++} ++ ++static u32 int_goertzel(s16 x[], u32 N, u32 freq) ++{ ++ /* We use the Goertzel algorithm to determine the power of the ++ * given frequency in the signal */ ++ s32 s_prev = 0; ++ s32 s_prev2 = 0; ++ s32 coeff = 2*int_cos(freq); ++ u32 i; ++ ++ u64 tmp; ++ u32 divisor; ++ ++ for (i = 0; i < N; i++) { ++ s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2; ++ s_prev2 = s_prev; ++ s_prev = s; ++ } ++ ++ tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev - ++ (s64)coeff * s_prev2 * s_prev / 32768; ++ ++ /* XXX: N must be low enough so that N*N fits in s32. ++ * Else we need two divisions. */ ++ divisor = N * N; ++ do_div(tmp, divisor); ++ ++ return (u32) tmp; ++} ++ ++static u32 freq_magnitude(s16 x[], u32 N, u32 freq) ++{ ++ u32 sum = int_goertzel(x, N, freq); ++ return (u32)int_sqrt(sum); ++} ++ ++static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end) ++{ ++ int i; ++ u32 sum = 0; ++ u32 freq_step; ++ int samples = 5; ++ ++ if (N > 192) { ++ /* The last 192 samples are enough for noise detection */ ++ x += (N-192); ++ N = 192; ++ } ++ ++ freq_step = (freq_end - freq_start) / (samples - 1); ++ ++ for (i = 0; i < samples; i++) { ++ sum += int_goertzel(x, N, freq_start); ++ freq_start += freq_step; ++ } ++ ++ return (u32)int_sqrt(sum / samples); ++} ++ ++static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N) ++{ ++ s32 carrier, stereo, dual, noise; ++ s32 carrier_freq, stereo_freq, dual_freq; ++ s32 ret; ++ ++ switch (core->tvaudio) { ++ case WW_BG: ++ case WW_DK: ++ carrier_freq = FREQ_A2_CARRIER; ++ stereo_freq = FREQ_A2_STEREO; ++ dual_freq = FREQ_A2_DUAL; ++ break; ++ case WW_M: ++ carrier_freq = FREQ_A2M_CARRIER; ++ stereo_freq = FREQ_A2M_STEREO; ++ dual_freq = FREQ_A2M_DUAL; ++ break; ++ case WW_EIAJ: ++ carrier_freq = FREQ_EIAJ_CARRIER; ++ stereo_freq = FREQ_EIAJ_STEREO; ++ dual_freq = FREQ_EIAJ_DUAL; ++ break; ++ default: ++ printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n", ++ core->name, core->tvaudio, __func__); ++ return UNSET; ++ } ++ ++ carrier = freq_magnitude(x, N, carrier_freq); ++ stereo = freq_magnitude(x, N, stereo_freq); ++ dual = freq_magnitude(x, N, dual_freq); ++ noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END); ++ ++ dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, " ++ "noise=%d\n", carrier, stereo, dual, noise); ++ ++ if (stereo > dual) ++ ret = V4L2_TUNER_SUB_STEREO; ++ else ++ ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ ++ if (core->tvaudio == WW_EIAJ) { ++ /* EIAJ checks may need adjustments */ ++ if ((carrier > max(stereo, dual)*2) && ++ (carrier < max(stereo, dual)*6) && ++ (carrier > 20 && carrier < 200) && ++ (max(stereo, dual) > min(stereo, dual))) { ++ /* For EIAJ the carrier is always present, ++ so we probably don't need noise detection */ ++ return ret; ++ } ++ } else { ++ if ((carrier > max(stereo, dual)*2) && ++ (carrier < max(stereo, dual)*8) && ++ (carrier > 20 && carrier < 200) && ++ (noise < 10) && ++ (max(stereo, dual) > min(stereo, dual)*2)) { ++ return ret; ++ } ++ } ++ return V4L2_TUNER_SUB_MONO; ++} ++ ++static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N) ++{ ++ s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF); ++ s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP); ++ s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF); ++ s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL); ++ dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d" ++ "\n", dual_ref, dual, sap_ref, sap); ++ /* FIXME: Currently not supported */ ++ return UNSET; ++} ++ ++static s16 *read_rds_samples(struct cx88_core *core, u32 *N) ++{ ++ const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27]; ++ s16 *samples; ++ ++ unsigned int i; ++ unsigned int bpl = srch->fifo_size/AUD_RDS_LINES; ++ unsigned int spl = bpl/4; ++ unsigned int sample_count = spl*(AUD_RDS_LINES-1); ++ ++ u32 current_address = cx_read(srch->ptr1_reg); ++ u32 offset = (current_address - srch->fifo_start + bpl); ++ ++ dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), " ++ "sample_count=%d, aud_intstat=%08x\n", current_address, ++ current_address - srch->fifo_start, sample_count, ++ cx_read(MO_AUD_INTSTAT)); ++ ++ samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL); ++ if (!samples) ++ return NULL; ++ ++ *N = sample_count; ++ ++ for (i = 0; i < sample_count; i++) { ++ offset = offset % (AUD_RDS_LINES*bpl); ++ samples[i] = cx_read(srch->fifo_start + offset); ++ offset += 4; ++ } ++ ++ if (dsp_debug >= 2) { ++ dprintk(2, "RDS samples dump: "); ++ for (i = 0; i < sample_count; i++) ++ printk("%hd ", samples[i]); ++ printk(".\n"); ++ } ++ ++ return samples; ++} ++ ++s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core) ++{ ++ s16 *samples; ++ u32 N = 0; ++ s32 ret = UNSET; ++ ++ /* If audio RDS fifo is disabled, we can't read the samples */ ++ if (!(cx_read(MO_AUD_DMACNTRL) & 0x04)) ++ return ret; ++ if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS)) ++ return ret; ++ ++ /* Wait at least 500 ms after an audio standard change */ ++ if (time_before(jiffies, core->last_change + msecs_to_jiffies(500))) ++ return ret; ++ ++ samples = read_rds_samples(core, &N); ++ ++ if (!samples) ++ return ret; ++ ++ switch (core->tvaudio) { ++ case WW_BG: ++ case WW_DK: ++ case WW_EIAJ: ++ case WW_M: ++ ret = detect_a2_a2m_eiaj(core, samples, N); ++ break; ++ case WW_BTSC: ++ ret = detect_btsc(core, samples, N); ++ break; ++ case WW_NONE: ++ case WW_I: ++ case WW_L: ++ case WW_I2SPT: ++ case WW_FM: ++ case WW_I2SADC: ++ break; ++ } ++ ++ kfree(samples); ++ ++ if (UNSET != ret) ++ dprintk(1, "stereo/sap detection result:%s%s%s\n", ++ (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "", ++ (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", ++ (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : ""); ++ ++ return ret; ++} ++EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap); ++ +diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c +new file mode 100644 +index 0000000..50b5ac5 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-dvb.c +@@ -0,0 +1,1787 @@ ++/* ++ * ++ * device driver for Conexant 2388x based TV cards ++ * MPEG Transport Stream (DVB) routines ++ * ++ * (c) 2004, 2005 Chris Pascoe ++ * (c) 2004 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include "dvb-pll.h" ++#include ++ ++#include "mt352.h" ++#include "mt352_priv.h" ++#include "cx88-vp3054-i2c.h" ++#include "zl10353.h" ++#include "cx22702.h" ++#include "or51132.h" ++#include "lgdt330x.h" ++#include "s5h1409.h" ++#include "xc4000.h" ++#include "xc5000.h" ++#include "nxt200x.h" ++#include "cx24123.h" ++#include "isl6421.h" ++#include "tuner-simple.h" ++#include "tda9887.h" ++#include "s5h1411.h" ++#include "stv0299.h" ++#include "z0194a.h" ++#include "stv0288.h" ++#include "stb6000.h" ++#include "cx24116.h" ++#include "stv0900.h" ++#include "stb6100.h" ++#include "stb6100_proc.h" ++#include "mb86a16.h" ++#include "ts2020.h" ++#include "ds3000.h" ++ ++MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); ++MODULE_AUTHOR("Chris Pascoe "); ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX88_VERSION); ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); ++ ++static unsigned int dvb_buf_tscnt = 32; ++module_param(dvb_buf_tscnt, int, 0644); ++MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define dprintk(level,fmt, arg...) if (debug >= level) \ ++ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) ++ ++/* ------------------------------------------------------------------ */ ++ ++static int dvb_buf_setup(struct videobuf_queue *q, ++ unsigned int *count, unsigned int *size) ++{ ++ struct cx8802_dev *dev = q->priv_data; ++ ++ dev->ts_packet_size = 188 * 4; ++ dev->ts_packet_count = dvb_buf_tscnt; ++ ++ *size = dev->ts_packet_size * dev->ts_packet_count; ++ *count = dvb_buf_tscnt; ++ return 0; ++} ++ ++static int dvb_buf_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, enum v4l2_field field) ++{ ++ struct cx8802_dev *dev = q->priv_data; ++ return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field); ++} ++ ++static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx8802_dev *dev = q->priv_data; ++ cx8802_buf_queue(dev, (struct cx88_buffer*)vb); ++} ++ ++static void dvb_buf_release(struct videobuf_queue *q, ++ struct videobuf_buffer *vb) ++{ ++ cx88_free_buffer(q, (struct cx88_buffer*)vb); ++} ++ ++static const struct videobuf_queue_ops dvb_qops = { ++ .buf_setup = dvb_buf_setup, ++ .buf_prepare = dvb_buf_prepare, ++ .buf_queue = dvb_buf_queue, ++ .buf_release = dvb_buf_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx8802_driver *drv = NULL; ++ int ret = 0; ++ int fe_id; ++ ++ fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe); ++ if (!fe_id) { ++ printk(KERN_ERR "%s() No frontend found\n", __func__); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&dev->core->lock); ++ drv = cx8802_get_driver(dev, CX88_MPEG_DVB); ++ if (drv) { ++ if (acquire){ ++ dev->frontends.active_fe_id = fe_id; ++ ret = drv->request_acquire(drv); ++ } else { ++ ret = drv->request_release(drv); ++ dev->frontends.active_fe_id = 0; ++ } ++ } ++ mutex_unlock(&dev->core->lock); ++ ++ return ret; ++} ++ ++static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open) ++{ ++ struct videobuf_dvb_frontends *f; ++ struct videobuf_dvb_frontend *fe; ++ ++ if (!core->dvbdev) ++ return; ++ ++ f = &core->dvbdev->frontends; ++ ++ if (!f) ++ return; ++ ++ if (f->gate <= 1) /* undefined or fe0 */ ++ fe = videobuf_dvb_get_frontend(f, 1); ++ else ++ fe = videobuf_dvb_get_frontend(f, f->gate); ++ ++ if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) ++ fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) ++{ ++ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; ++ static const u8 reset [] = { RESET, 0x80 }; ++ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; ++ static const u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; ++ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; ++ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ return 0; ++} ++ ++static int dvico_dual_demod_init(struct dvb_frontend *fe) ++{ ++ static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 }; ++ static const u8 reset [] = { RESET, 0x80 }; ++ static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; ++ static const u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; ++ static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; ++ static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ ++ return 0; ++} ++ ++static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) ++{ ++ static const u8 clock_config [] = { 0x89, 0x38, 0x39 }; ++ static const u8 reset [] = { 0x50, 0x80 }; ++ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; ++ static const u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, ++ 0x00, 0xFF, 0x00, 0x40, 0x40 }; ++ static const u8 dntv_extra[] = { 0xB5, 0x7A }; ++ static const u8 capt_range_cfg[] = { 0x75, 0x32 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(2000); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ udelay(2000); ++ mt352_write(fe, dntv_extra, sizeof(dntv_extra)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ ++ return 0; ++} ++ ++static const struct mt352_config dvico_fusionhdtv = { ++ .demod_address = 0x0f, ++ .demod_init = dvico_fusionhdtv_demod_init, ++}; ++ ++static const struct mt352_config dntv_live_dvbt_config = { ++ .demod_address = 0x0f, ++ .demod_init = dntv_live_dvbt_demod_init, ++}; ++ ++static const struct mt352_config dvico_fusionhdtv_dual = { ++ .demod_address = 0x0f, ++ .demod_init = dvico_dual_demod_init, ++}; ++ ++static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .if2 = 45600, ++}; ++ ++static struct mb86a16_config twinhan_vp1027 = { ++ .demod_address = 0x08, ++}; ++ ++#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) ++static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) ++{ ++ static const u8 clock_config [] = { 0x89, 0x38, 0x38 }; ++ static const u8 reset [] = { 0x50, 0x80 }; ++ static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; ++ static const u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF, ++ 0x00, 0xFF, 0x00, 0x40, 0x40 }; ++ static const u8 dntv_extra[] = { 0xB5, 0x7A }; ++ static const u8 capt_range_cfg[] = { 0x75, 0x32 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(2000); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ udelay(2000); ++ mt352_write(fe, dntv_extra, sizeof(dntv_extra)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ ++ return 0; ++} ++ ++static const struct mt352_config dntv_live_dvbt_pro_config = { ++ .demod_address = 0x0f, ++ .no_tuner = 1, ++ .demod_init = dntv_live_dvbt_pro_demod_init, ++}; ++#endif ++ ++static const struct zl10353_config dvico_fusionhdtv_hybrid = { ++ .demod_address = 0x0f, ++ .no_tuner = 1, ++}; ++ ++static const struct zl10353_config dvico_fusionhdtv_xc3028 = { ++ .demod_address = 0x0f, ++ .if2 = 45600, ++ .no_tuner = 1, ++}; ++ ++static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = { ++ .demod_address = 0x0f, ++ .if2 = 4560, ++ .no_tuner = 1, ++ .demod_init = dvico_fusionhdtv_demod_init, ++}; ++ ++static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { ++ .demod_address = 0x0f, ++}; ++ ++static const struct cx22702_config connexant_refboard_config = { ++ .demod_address = 0x43, ++ .output_mode = CX22702_SERIAL_OUTPUT, ++}; ++ ++static const struct cx22702_config hauppauge_hvr_config = { ++ .demod_address = 0x63, ++ .output_mode = CX22702_SERIAL_OUTPUT, ++}; ++ ++static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; ++ return 0; ++} ++ ++static const struct or51132_config pchdtv_hd3000 = { ++ .demod_address = 0x15, ++ .set_ts_params = or51132_set_ts_param, ++}; ++ ++static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ dprintk(1, "%s: index = %d\n", __func__, index); ++ if (index == 0) ++ cx_clear(MO_GP0_IO, 8); ++ else ++ cx_set(MO_GP0_IO, 8); ++ return 0; ++} ++ ++static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ if (is_punctured) ++ dev->ts_gen_cntrl |= 0x04; ++ else ++ dev->ts_gen_cntrl &= ~0x04; ++ return 0; ++} ++ ++static struct lgdt330x_config fusionhdtv_3_gold = { ++ .demod_address = 0x0e, ++ .demod_chip = LGDT3302, ++ .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ ++ .set_ts_params = lgdt330x_set_ts_param, ++}; ++ ++static const struct lgdt330x_config fusionhdtv_5_gold = { ++ .demod_address = 0x0e, ++ .demod_chip = LGDT3303, ++ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ ++ .set_ts_params = lgdt330x_set_ts_param, ++}; ++ ++static const struct lgdt330x_config pchdtv_hd5500 = { ++ .demod_address = 0x59, ++ .demod_chip = LGDT3303, ++ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ ++ .set_ts_params = lgdt330x_set_ts_param, ++}; ++ ++static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; ++ return 0; ++} ++ ++static const struct nxt200x_config ati_hdtvwonder = { ++ .demod_address = 0x0a, ++ .set_ts_params = nxt200x_set_ts_param, ++}; ++ ++static int cx24123_set_ts_param(struct dvb_frontend* fe, ++ int is_punctured) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ dev->ts_gen_cntrl = 0x02; ++ return 0; ++} ++ ++static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ if (voltage == SEC_VOLTAGE_OFF) ++ cx_write(MO_GP0_IO, 0x000006fb); ++ else ++ cx_write(MO_GP0_IO, 0x000006f9); ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} ++ ++static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ if (voltage == SEC_VOLTAGE_OFF) { ++ dprintk(1,"LNB Voltage OFF\n"); ++ cx_write(MO_GP0_IO, 0x0000efff); ++ } ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} ++ ++static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev= fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ cx_set(MO_GP0_IO, 0x6040); ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ cx_clear(MO_GP0_IO, 0x20); ++ break; ++ case SEC_VOLTAGE_18: ++ cx_set(MO_GP0_IO, 0x20); ++ break; ++ case SEC_VOLTAGE_OFF: ++ cx_clear(MO_GP0_IO, 0x20); ++ break; ++ } ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} ++ ++static int vp1027_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ dprintk(1, "LNB SEC Voltage=13\n"); ++ cx_write(MO_GP0_IO, 0x00001220); ++ break; ++ case SEC_VOLTAGE_18: ++ dprintk(1, "LNB SEC Voltage=18\n"); ++ cx_write(MO_GP0_IO, 0x00001222); ++ break; ++ case SEC_VOLTAGE_OFF: ++ dprintk(1, "LNB Voltage OFF\n"); ++ cx_write(MO_GP0_IO, 0x00001230); ++ break; ++ } ++ ++ if (core->prev_set_voltage) ++ return core->prev_set_voltage(fe, voltage); ++ return 0; ++} ++ ++static const struct cx24123_config geniatech_dvbs_config = { ++ .demod_address = 0x55, ++ .set_ts_params = cx24123_set_ts_param, ++}; ++ ++static const struct cx24123_config hauppauge_novas_config = { ++ .demod_address = 0x55, ++ .set_ts_params = cx24123_set_ts_param, ++}; ++ ++static const struct cx24123_config kworld_dvbs_100_config = { ++ .demod_address = 0x15, ++ .set_ts_params = cx24123_set_ts_param, ++ .lnb_polarity = 1, ++}; ++ ++static const struct s5h1409_config pinnacle_pctv_hd_800i_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_PARALLEL_OUTPUT, ++ .gpio = S5H1409_GPIO_ON, ++ .qam_if = 44000, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static const struct s5h1409_config dvico_hdtv5_pci_nano_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static const struct s5h1409_config kworld_atsc_120_config = { ++ .demod_address = 0x32 >> 1, ++ .output_mode = S5H1409_SERIAL_OUTPUT, ++ .gpio = S5H1409_GPIO_OFF, ++ .inversion = S5H1409_INVERSION_OFF, ++ .status_mode = S5H1409_DEMODLOCKING, ++ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = { ++ .i2c_address = 0x64, ++ .if_khz = 5380, ++}; ++ ++static const struct zl10353_config cx88_pinnacle_hybrid_pctv = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .if2 = 45600, ++}; ++ ++static const struct zl10353_config cx88_geniatech_x8000_mt = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static const struct s5h1411_config dvico_fusionhdtv7_config = { ++ .output_mode = S5H1411_SERIAL_OUTPUT, ++ .gpio = S5H1411_GPIO_ON, ++ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++ .qam_if = S5H1411_IF_44000, ++ .vsb_if = S5H1411_IF_44000, ++ .inversion = S5H1411_INVERSION_OFF, ++ .status_mode = S5H1411_DEMODLOCKING ++}; ++ ++static const struct xc5000_config dvico_fusionhdtv7_tuner_config = { ++ .i2c_address = 0xc2 >> 1, ++ .if_khz = 5380, ++}; ++ ++static int attach_xc3028(u8 addr, struct cx8802_dev *dev) ++{ ++ struct dvb_frontend *fe; ++ struct videobuf_dvb_frontend *fe0 = NULL; ++ struct xc2028_ctrl ctl; ++ struct xc2028_config cfg = { ++ .i2c_adap = &dev->core->i2c_adap, ++ .i2c_addr = addr, ++ .ctrl = &ctl, ++ }; ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); ++ if (!fe0) ++ return -EINVAL; ++ ++ if (!fe0->dvb.frontend) { ++ printk(KERN_ERR "%s/2: dvb frontend not attached. " ++ "Can't attach xc3028\n", ++ dev->core->name); ++ return -EINVAL; ++ } ++ ++ /* ++ * Some xc3028 devices may be hidden by an I2C gate. This is known ++ * to happen with some s5h1409-based devices. ++ * Now that I2C gate is open, sets up xc3028 configuration ++ */ ++ cx88_setup_xc3028(dev->core, &ctl); ++ ++ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); ++ if (!fe) { ++ printk(KERN_ERR "%s/2: xc3028 attach failed\n", ++ dev->core->name); ++ dvb_frontend_detach(fe0->dvb.frontend); ++ dvb_unregister_frontend(fe0->dvb.frontend); ++ fe0->dvb.frontend = NULL; ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO "%s/2: xc3028 attached\n", ++ dev->core->name); ++ ++ return 0; ++} ++ ++static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg) ++{ ++ struct dvb_frontend *fe; ++ struct videobuf_dvb_frontend *fe0 = NULL; ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); ++ if (!fe0) ++ return -EINVAL; ++ ++ if (!fe0->dvb.frontend) { ++ printk(KERN_ERR "%s/2: dvb frontend not attached. " ++ "Can't attach xc4000\n", ++ dev->core->name); ++ return -EINVAL; ++ } ++ ++ fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap, ++ cfg); ++ if (!fe) { ++ printk(KERN_ERR "%s/2: xc4000 attach failed\n", ++ dev->core->name); ++ dvb_frontend_detach(fe0->dvb.frontend); ++ dvb_unregister_frontend(fe0->dvb.frontend); ++ fe0->dvb.frontend = NULL; ++ return -EINVAL; ++ } ++ ++ printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name); ++ ++ return 0; ++} ++ ++static int cx24116_set_ts_param(struct dvb_frontend *fe, ++ int is_punctured) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ dev->ts_gen_cntrl = 0x2; ++ ++ return 0; ++} ++ ++static int stv0900_set_ts_param(struct dvb_frontend *fe, ++ int is_punctured) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ dev->ts_gen_cntrl = 0; ++ ++ return 0; ++} ++ ++static int cx24116_reset_device(struct dvb_frontend *fe) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ /* Reset the part */ ++ /* Put the cx24116 into reset */ ++ cx_write(MO_SRST_IO, 0); ++ msleep(10); ++ /* Take the cx24116 out of reset */ ++ cx_write(MO_SRST_IO, 1); ++ msleep(10); ++ ++ return 0; ++} ++ ++static const struct cx24116_config hauppauge_hvr4000_config = { ++ .demod_address = 0x05, ++ .set_ts_params = cx24116_set_ts_param, ++ .reset_device = cx24116_reset_device, ++}; ++ ++static const struct cx24116_config tevii_s460_config = { ++ .demod_address = 0x55, ++ .set_ts_params = cx24116_set_ts_param, ++ .reset_device = cx24116_reset_device, ++}; ++ ++static int ds3000_set_ts_param(struct dvb_frontend *fe, ++ int is_punctured) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ dev->ts_gen_cntrl = 4; ++ ++ return 0; ++} ++ ++static struct ds3000_config tevii_ds3000_config = { ++ .demod_address = 0x68, ++ .set_ts_params = ds3000_set_ts_param, ++}; ++ ++static struct ts2020_config tevii_ts2020_config = { ++ .tuner_address = 0x60, ++ .clk_out_div = 1, ++}; ++ ++static const struct stv0900_config prof_7301_stv0900_config = { ++ .demod_address = 0x6a, ++/* demod_mode = 0,*/ ++ .xtal = 27000000, ++ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ ++ .diseqc_mode = 2,/* 2/3 PWM */ ++ .tun1_maddress = 0,/* 0x60 */ ++ .tun1_adc = 0,/* 2 Vpp */ ++ .path1_mode = 3, ++ .set_ts_params = stv0900_set_ts_param, ++}; ++ ++static const struct stb6100_config prof_7301_stb6100_config = { ++ .tuner_address = 0x60, ++ .refclock = 27000000, ++}; ++ ++static const struct stv0299_config tevii_tuner_sharp_config = { ++ .demod_address = 0x68, ++ .inittab = sharp_z0194a_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .skip_reinit = 0, ++ .lock_output = 1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP1, ++ .min_delay_ms = 100, ++ .set_symbol_rate = sharp_z0194a_set_symbol_rate, ++ .set_ts_params = cx24116_set_ts_param, ++}; ++ ++static const struct stv0288_config tevii_tuner_earda_config = { ++ .demod_address = 0x68, ++ .min_delay_ms = 100, ++ .set_ts_params = cx24116_set_ts_param, ++}; ++ ++static int cx8802_alloc_frontends(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ struct videobuf_dvb_frontend *fe = NULL; ++ int i; ++ ++ mutex_init(&dev->frontends.lock); ++ INIT_LIST_HEAD(&dev->frontends.felist); ++ ++ if (!core->board.num_frontends) ++ return -ENODEV; ++ ++ printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, ++ core->board.num_frontends); ++ for (i = 1; i <= core->board.num_frontends; i++) { ++ fe = videobuf_dvb_alloc_frontend(&dev->frontends, i); ++ if (!fe) { ++ printk(KERN_ERR "%s() failed to alloc\n", __func__); ++ videobuf_dvb_dealloc_frontends(&dev->frontends); ++ return -ENOMEM; ++ } ++ } ++ return 0; ++} ++ ++ ++ ++static const u8 samsung_smt_7020_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x00, ++ 0x03, 0x00, ++ 0x04, 0x7D, ++ 0x05, 0x0F, ++ 0x06, 0x02, ++ 0x07, 0x00, ++ 0x08, 0x60, ++ ++ 0x0A, 0xC2, ++ 0x0B, 0x00, ++ 0x0C, 0x01, ++ 0x0D, 0x81, ++ 0x0E, 0x44, ++ 0x0F, 0x09, ++ 0x10, 0x3C, ++ 0x11, 0x84, ++ 0x12, 0xDA, ++ 0x13, 0x99, ++ 0x14, 0x8D, ++ 0x15, 0xCE, ++ 0x16, 0xE8, ++ 0x17, 0x43, ++ 0x18, 0x1C, ++ 0x19, 0x1B, ++ 0x1A, 0x1D, ++ ++ 0x1C, 0x12, ++ 0x1D, 0x00, ++ 0x1E, 0x00, ++ 0x1F, 0x00, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ ++ 0x28, 0x02, ++ 0x29, 0x28, ++ 0x2A, 0x14, ++ 0x2B, 0x0F, ++ 0x2C, 0x09, ++ 0x2D, 0x05, ++ ++ 0x31, 0x1F, ++ 0x32, 0x19, ++ 0x33, 0xFC, ++ 0x34, 0x13, ++ 0xff, 0xff, ++}; ++ ++ ++static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct cx8802_dev *dev = fe->dvb->priv; ++ u8 buf[4]; ++ u32 div; ++ struct i2c_msg msg = { ++ .addr = 0x61, ++ .flags = 0, ++ .buf = buf, ++ .len = sizeof(buf) }; ++ ++ div = c->frequency / 125; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x84; /* 0xC4 */ ++ buf[3] = 0x00; ++ ++ if (c->frequency < 1500000) ++ buf[3] |= 0x10; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int samsung_smt_7020_set_tone(struct dvb_frontend *fe, ++ fe_sec_tone_mode_t tone) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ cx_set(MO_GP0_IO, 0x0800); ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ cx_set(MO_GP0_IO, 0x08); ++ break; ++ case SEC_TONE_OFF: ++ cx_clear(MO_GP0_IO, 0x08); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage) ++{ ++ struct cx8802_dev *dev = fe->dvb->priv; ++ struct cx88_core *core = dev->core; ++ ++ u8 data; ++ struct i2c_msg msg = { ++ .addr = 8, ++ .flags = 0, ++ .buf = &data, ++ .len = sizeof(data) }; ++ ++ cx_set(MO_GP0_IO, 0x8000); ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_OFF: ++ break; ++ case SEC_VOLTAGE_13: ++ data = ISL6421_EN1 | ISL6421_LLC1; ++ cx_clear(MO_GP0_IO, 0x80); ++ break; ++ case SEC_VOLTAGE_18: ++ data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1; ++ cx_clear(MO_GP0_IO, 0x80); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO; ++} ++ ++static int samsung_smt_7020_stv0299_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 const struct stv0299_config samsung_stv0299_config = { ++ .demod_address = 0x68, ++ .inittab = samsung_smt_7020_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_smt_7020_stv0299_set_symbol_rate, ++}; ++ ++static int dvb_register(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ struct videobuf_dvb_frontend *fe0, *fe1 = NULL; ++ int mfe_shared = 0; /* bus not shared by default */ ++ int res = -EINVAL; ++ ++ if (0 != core->i2c_rc) { ++ printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name); ++ goto frontend_detach; ++ } ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); ++ if (!fe0) ++ goto frontend_detach; ++ ++ /* multi-frontend gate control is undefined or defaults to fe0 */ ++ dev->frontends.gate = 0; ++ ++ /* Sets the gate control callback to be used by i2c command calls */ ++ core->gate_ctrl = cx88_dvb_gate_ctrl; ++ ++ /* init frontend(s) */ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_DVB_T1: ++ fe0->dvb.frontend = dvb_attach(cx22702_attach, ++ &connexant_refboard_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x61, &core->i2c_adap, ++ DVB_PLL_THOMSON_DTT759X)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: ++ case CX88_BOARD_CONEXANT_DVB_T1: ++ case CX88_BOARD_KWORLD_DVB_T_CX22702: ++ case CX88_BOARD_WINFAST_DTV1000: ++ fe0->dvb.frontend = dvb_attach(cx22702_attach, ++ &connexant_refboard_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x60, &core->i2c_adap, ++ DVB_PLL_THOMSON_DTT7579)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_WINFAST_DTV2000H: ++ case CX88_BOARD_HAUPPAUGE_HVR1100: ++ case CX88_BOARD_HAUPPAUGE_HVR1100LP: ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ fe0->dvb.frontend = dvb_attach(cx22702_attach, ++ &hauppauge_hvr_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_PHILIPS_FMD1216ME_MK3)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_WINFAST_DTV2000H_J: ++ fe0->dvb.frontend = dvb_attach(cx22702_attach, ++ &hauppauge_hvr_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_PHILIPS_FMD1216MEX_MK3)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ /* MFE frontend 1 */ ++ mfe_shared = 1; ++ dev->frontends.gate = 2; ++ /* DVB-S init */ ++ fe0->dvb.frontend = dvb_attach(cx24123_attach, ++ &hauppauge_novas_config, ++ &dev->core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (!dvb_attach(isl6421_attach, ++ fe0->dvb.frontend, ++ &dev->core->i2c_adap, ++ 0x08, ISL6421_DCL, 0x00)) ++ goto frontend_detach; ++ } ++ /* MFE frontend 2 */ ++ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); ++ if (!fe1) ++ goto frontend_detach; ++ /* DVB-T init */ ++ fe1->dvb.frontend = dvb_attach(cx22702_attach, ++ &hauppauge_hvr_config, ++ &dev->core->i2c_adap); ++ if (fe1->dvb.frontend) { ++ fe1->dvb.frontend->id = 1; ++ if (!dvb_attach(simple_tuner_attach, ++ fe1->dvb.frontend, ++ &dev->core->i2c_adap, ++ 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &dvico_fusionhdtv, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) ++ goto frontend_detach; ++ break; ++ } ++ /* ZL10353 replaces MT352 on later cards */ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_plus_v1_1, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: ++ /* The tin box says DEE1601, but it seems to be DTT7579 ++ * compatible, with a slightly different MT352 AGC gain. */ ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &dvico_fusionhdtv_dual, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) ++ goto frontend_detach; ++ break; ++ } ++ /* ZL10353 replaces MT352 on later cards */ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_plus_v1_1, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &dvico_fusionhdtv, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x61, NULL, DVB_PLL_LG_Z201)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_KWORLD_DVB_T: ++ case CX88_BOARD_DNTV_LIVE_DVB_T: ++ case CX88_BOARD_ADSTECH_DVB_T_PCI: ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &dntv_live_dvbt_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, ++ 0x61, NULL, DVB_PLL_UNKNOWN_1)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ++#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) ++ /* MT352 is on a secondary I2C bus made from some GPIO lines */ ++ fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, ++ &dev->vp3054->adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_PHILIPS_FMD1216ME_MK3)) ++ goto frontend_detach; ++ } ++#else ++ printk(KERN_ERR "%s/2: built without vp3054 support\n", ++ core->name); ++#endif ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_hybrid, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_THOMSON_FE6600)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &dvico_fusionhdtv_xc3028, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend == NULL) ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &dvico_fusionhdtv_mt352_xc3028, ++ &core->i2c_adap); ++ /* ++ * On this board, the demod provides the I2C bus pullup. ++ * We must not permit gate_ctrl to be performed, or ++ * the xc3028 cannot communicate on the bus. ++ */ ++ if (fe0->dvb.frontend) ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; ++ if (attach_xc3028(0x61, dev) < 0) ++ goto frontend_detach; ++ break; ++ case CX88_BOARD_PCHDTV_HD3000: ++ fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_THOMSON_DTT761X)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: ++ dev->ts_gen_cntrl = 0x08; ++ ++ /* Do a hardware reset of chip before using it. */ ++ cx_clear(MO_GP0_IO, 1); ++ mdelay(100); ++ cx_set(MO_GP0_IO, 1); ++ mdelay(200); ++ ++ /* Select RF connector callback */ ++ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; ++ fe0->dvb.frontend = dvb_attach(lgdt330x_attach, ++ &fusionhdtv_3_gold, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_MICROTUNE_4042FI5)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: ++ dev->ts_gen_cntrl = 0x08; ++ ++ /* Do a hardware reset of chip before using it. */ ++ cx_clear(MO_GP0_IO, 1); ++ mdelay(100); ++ cx_set(MO_GP0_IO, 9); ++ mdelay(200); ++ fe0->dvb.frontend = dvb_attach(lgdt330x_attach, ++ &fusionhdtv_3_gold, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_THOMSON_DTT761X)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: ++ dev->ts_gen_cntrl = 0x08; ++ ++ /* Do a hardware reset of chip before using it. */ ++ cx_clear(MO_GP0_IO, 1); ++ mdelay(100); ++ cx_set(MO_GP0_IO, 1); ++ mdelay(200); ++ fe0->dvb.frontend = dvb_attach(lgdt330x_attach, ++ &fusionhdtv_5_gold, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_LG_TDVS_H06XF)) ++ goto frontend_detach; ++ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x43)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_PCHDTV_HD5500: ++ dev->ts_gen_cntrl = 0x08; ++ ++ /* Do a hardware reset of chip before using it. */ ++ cx_clear(MO_GP0_IO, 1); ++ mdelay(100); ++ cx_set(MO_GP0_IO, 1); ++ mdelay(200); ++ fe0->dvb.frontend = dvb_attach(lgdt330x_attach, ++ &pchdtv_hd5500, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_LG_TDVS_H06XF)) ++ goto frontend_detach; ++ if (!dvb_attach(tda9887_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x43)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_ATI_HDTVWONDER: ++ fe0->dvb.frontend = dvb_attach(nxt200x_attach, ++ &ati_hdtvwonder, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x61, ++ TUNER_PHILIPS_TUV1236D)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: ++ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: ++ fe0->dvb.frontend = dvb_attach(cx24123_attach, ++ &hauppauge_novas_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, ++ &core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_KWORLD_DVBS_100: ++ fe0->dvb.frontend = dvb_attach(cx24123_attach, ++ &kworld_dvbs_100_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage; ++ } ++ break; ++ case CX88_BOARD_GENIATECH_DVBS: ++ fe0->dvb.frontend = dvb_attach(cx24123_attach, ++ &geniatech_dvbs_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; ++ } ++ break; ++ case CX88_BOARD_PINNACLE_PCTV_HD_800i: ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &pinnacle_pctv_hd_800i_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &core->i2c_adap, ++ &pinnacle_pctv_hd_800i_tuner_config)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &dvico_hdtv5_pci_nano_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &core->i2c_adap, ++ .i2c_addr = 0x61, ++ }; ++ static struct xc2028_ctrl ctl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ .scode_table = XC3028_FE_OREN538, ++ }; ++ ++ fe = dvb_attach(xc2028_attach, ++ fe0->dvb.frontend, &cfg); ++ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) ++ fe->ops.tuner_ops.set_config(fe, &ctl); ++ } ++ break; ++ case CX88_BOARD_PINNACLE_HYBRID_PCTV: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &cx88_pinnacle_hybrid_pctv, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; ++ if (attach_xc3028(0x61, dev) < 0) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_WINFAST_DTV1800H_XC4000: ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &cx88_pinnacle_hybrid_pctv, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ struct xc4000_config cfg = { ++ .i2c_address = 0x61, ++ .default_pm = 0, ++ .dvb_amplitude = 134, ++ .set_smoothedcvbs = 1, ++ .if_khz = 4560 ++ }; ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; ++ if (attach_xc4000(dev, &cfg) < 0) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_GENIATECH_X8000_MT: ++ dev->ts_gen_cntrl = 0x00; ++ ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &cx88_geniatech_x8000_mt, ++ &core->i2c_adap); ++ if (attach_xc3028(0x61, dev) < 0) ++ goto frontend_detach; ++ break; ++ case CX88_BOARD_KWORLD_ATSC_120: ++ fe0->dvb.frontend = dvb_attach(s5h1409_attach, ++ &kworld_atsc_120_config, ++ &core->i2c_adap); ++ if (attach_xc3028(0x61, dev) < 0) ++ goto frontend_detach; ++ break; ++ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &dvico_fusionhdtv7_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &core->i2c_adap, ++ &dvico_fusionhdtv7_tuner_config)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ /* MFE frontend 1 */ ++ mfe_shared = 1; ++ dev->frontends.gate = 2; ++ /* DVB-S/S2 Init */ ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &hauppauge_hvr4000_config, ++ &dev->core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (!dvb_attach(isl6421_attach, ++ fe0->dvb.frontend, ++ &dev->core->i2c_adap, ++ 0x08, ISL6421_DCL, 0x00)) ++ goto frontend_detach; ++ } ++ /* MFE frontend 2 */ ++ fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); ++ if (!fe1) ++ goto frontend_detach; ++ /* DVB-T Init */ ++ fe1->dvb.frontend = dvb_attach(cx22702_attach, ++ &hauppauge_hvr_config, ++ &dev->core->i2c_adap); ++ if (fe1->dvb.frontend) { ++ fe1->dvb.frontend->id = 1; ++ if (!dvb_attach(simple_tuner_attach, ++ fe1->dvb.frontend, ++ &dev->core->i2c_adap, ++ 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_HAUPPAUGE_HVR4000LITE: ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &hauppauge_hvr4000_config, ++ &dev->core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (!dvb_attach(isl6421_attach, ++ fe0->dvb.frontend, ++ &dev->core->i2c_adap, ++ 0x08, ISL6421_DCL, 0x00)) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_PROF_6200: ++ case CX88_BOARD_TBS_8910: ++ case CX88_BOARD_TEVII_S420: ++ fe0->dvb.frontend = dvb_attach(stv0299_attach, ++ &tevii_tuner_sharp_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60, ++ &core->i2c_adap, DVB_PLL_OPERA1)) ++ goto frontend_detach; ++ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; ++ ++ } else { ++ fe0->dvb.frontend = dvb_attach(stv0288_attach, ++ &tevii_tuner_earda_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61, ++ &core->i2c_adap)) ++ goto frontend_detach; ++ core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; ++ } ++ } ++ break; ++ case CX88_BOARD_TEVII_S460: ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &tevii_s460_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; ++ break; ++ case CX88_BOARD_TEVII_S464: ++ fe0->dvb.frontend = dvb_attach(ds3000_attach, ++ &tevii_ds3000_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(ts2020_attach, fe0->dvb.frontend, ++ &tevii_ts2020_config, &core->i2c_adap); ++ fe0->dvb.frontend->ops.set_voltage = ++ tevii_dvbs_set_voltage; ++ } ++ break; ++ case CX88_BOARD_OMICOM_SS4_PCI: ++ case CX88_BOARD_TBS_8920: ++ case CX88_BOARD_PROF_7300: ++ case CX88_BOARD_SATTRADE_ST4200: ++ fe0->dvb.frontend = dvb_attach(cx24116_attach, ++ &hauppauge_hvr4000_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend != NULL) ++ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; ++ break; ++ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &cx88_terratec_cinergy_ht_pci_mkii_config, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; ++ if (attach_xc3028(0x61, dev) < 0) ++ goto frontend_detach; ++ } ++ break; ++ case CX88_BOARD_PROF_7301:{ ++ struct dvb_tuner_ops *tuner_ops = NULL; ++ ++ fe0->dvb.frontend = dvb_attach(stv0900_attach, ++ &prof_7301_stv0900_config, ++ &core->i2c_adap, 0); ++ if (fe0->dvb.frontend != NULL) { ++ if (!dvb_attach(stb6100_attach, fe0->dvb.frontend, ++ &prof_7301_stb6100_config, ++ &core->i2c_adap)) ++ goto frontend_detach; ++ ++ tuner_ops = &fe0->dvb.frontend->ops.tuner_ops; ++ tuner_ops->set_frequency = stb6100_set_freq; ++ tuner_ops->get_frequency = stb6100_get_freq; ++ tuner_ops->set_bandwidth = stb6100_set_bandw; ++ tuner_ops->get_bandwidth = stb6100_get_bandw; ++ ++ core->prev_set_voltage = ++ fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = ++ tevii_dvbs_set_voltage; ++ } ++ break; ++ } ++ case CX88_BOARD_SAMSUNG_SMT_7020: ++ dev->ts_gen_cntrl = 0x08; ++ ++ cx_set(MO_GP0_IO, 0x0101); ++ ++ cx_clear(MO_GP0_IO, 0x01); ++ mdelay(100); ++ cx_set(MO_GP0_IO, 0x01); ++ mdelay(200); ++ ++ fe0->dvb.frontend = dvb_attach(stv0299_attach, ++ &samsung_stv0299_config, ++ &dev->core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.tuner_ops.set_params = ++ samsung_smt_7020_tuner_set_params; ++ fe0->dvb.frontend->tuner_priv = ++ &dev->core->i2c_adap; ++ fe0->dvb.frontend->ops.set_voltage = ++ samsung_smt_7020_set_voltage; ++ fe0->dvb.frontend->ops.set_tone = ++ samsung_smt_7020_set_tone; ++ } ++ ++ break; ++ case CX88_BOARD_TWINHAN_VP1027_DVBS: ++ dev->ts_gen_cntrl = 0x00; ++ fe0->dvb.frontend = dvb_attach(mb86a16_attach, ++ &twinhan_vp1027, ++ &core->i2c_adap); ++ if (fe0->dvb.frontend) { ++ core->prev_set_voltage = ++ fe0->dvb.frontend->ops.set_voltage; ++ fe0->dvb.frontend->ops.set_voltage = ++ vp1027_set_voltage; ++ } ++ break; ++ ++ default: ++ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", ++ core->name); ++ break; ++ } ++ ++ if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) { ++ printk(KERN_ERR ++ "%s/2: frontend initialization failed\n", ++ core->name); ++ goto frontend_detach; ++ } ++ /* define general-purpose callback pointer */ ++ fe0->dvb.frontend->callback = cx88_tuner_callback; ++ ++ /* Ensure all frontends negotiate bus access */ ++ fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; ++ if (fe1) ++ fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; ++ ++ /* Put the analog decoder in standby to keep it quiet */ ++ call_all(core, core, s_power, 0); ++ ++ /* register everything */ ++ res = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, ++ &dev->pci->dev, adapter_nr, mfe_shared); ++ if (res) ++ goto frontend_detach; ++ return res; ++ ++frontend_detach: ++ core->gate_ctrl = NULL; ++ videobuf_dvb_dealloc_frontends(&dev->frontends); ++ return res; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++/* CX8802 MPEG -> mini driver - We have been given the hardware */ ++static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ int err = 0; ++ dprintk( 1, "%s\n", __func__); ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* We arrive here with either the cx23416 or the cx22702 ++ * on the bus. Take the bus from the cx23416 and enable the ++ * cx22702 demod ++ */ ++ /* Toggle reset on cx22702 leaving i2c active */ ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ cx_clear(MO_GP0_IO, 0x00000080); ++ udelay(50); ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ /* enable the cx22702 pins */ ++ cx_clear(MO_GP0_IO, 0x00000004); ++ udelay(1000); ++ break; ++ ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ /* Toggle reset on cx22702 leaving i2c active */ ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ cx_clear(MO_GP0_IO, 0x00000080); ++ udelay(50); ++ cx_set(MO_GP0_IO, 0x00000080); ++ udelay(1000); ++ switch (core->dvbdev->frontends.active_fe_id) { ++ case 1: /* DVB-S/S2 Enabled */ ++ /* tri-state the cx22702 pins */ ++ cx_set(MO_GP0_IO, 0x00000004); ++ /* Take the cx24116/cx24123 out of reset */ ++ cx_write(MO_SRST_IO, 1); ++ core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */ ++ break; ++ case 2: /* DVB-T Enabled */ ++ /* Put the cx24116/cx24123 into reset */ ++ cx_write(MO_SRST_IO, 0); ++ /* enable the cx22702 pins */ ++ cx_clear(MO_GP0_IO, 0x00000004); ++ core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */ ++ break; ++ } ++ udelay(1000); ++ break; ++ ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ /* set RF input to AIR for DVB-T (GPIO 16) */ ++ cx_write(MO_GP2_IO, 0x0101); ++ break; ++ ++ default: ++ err = -ENODEV; ++ } ++ return err; ++} ++ ++/* CX8802 MPEG -> mini driver - We no longer have the hardware */ ++static int cx8802_dvb_advise_release(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ int err = 0; ++ dprintk( 1, "%s\n", __func__); ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* Do Nothing, leave the cx22702 on the bus. */ ++ break; ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ break; ++ default: ++ err = -ENODEV; ++ } ++ return err; ++} ++ ++static int cx8802_dvb_probe(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ struct cx8802_dev *dev = drv->core->dvbdev; ++ int err; ++ struct videobuf_dvb_frontend *fe; ++ int i; ++ ++ dprintk( 1, "%s\n", __func__); ++ dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", ++ core->boardnr, ++ core->name, ++ core->pci_bus, ++ core->pci_slot); ++ ++ err = -ENODEV; ++ if (!(core->board.mpeg & CX88_MPEG_DVB)) ++ goto fail_core; ++ ++ /* If vp3054 isn't enabled, a stub will just return 0 */ ++ err = vp3054_i2c_probe(dev); ++ if (0 != err) ++ goto fail_core; ++ ++ /* dvb stuff */ ++ printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name); ++ dev->ts_gen_cntrl = 0x0c; ++ ++ err = cx8802_alloc_frontends(dev); ++ if (err) ++ goto fail_core; ++ ++ err = -ENODEV; ++ for (i = 1; i <= core->board.num_frontends; i++) { ++ fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i); ++ if (fe == NULL) { ++ printk(KERN_ERR "%s() failed to get frontend(%d)\n", ++ __func__, i); ++ goto fail_probe; ++ } ++ videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_TOP, ++ sizeof(struct cx88_buffer), ++ dev, NULL); ++ /* init struct videobuf_dvb */ ++ fe->dvb.name = dev->core->name; ++ } ++ ++ err = dvb_register(dev); ++ if (err) ++ /* frontends/adapter de-allocated in dvb_register */ ++ printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n", ++ core->name, err); ++ return err; ++fail_probe: ++ videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends); ++fail_core: ++ return err; ++} ++ ++static int cx8802_dvb_remove(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ struct cx8802_dev *dev = drv->core->dvbdev; ++ ++ dprintk( 1, "%s\n", __func__); ++ ++ videobuf_dvb_unregister_bus(&dev->frontends); ++ ++ vp3054_i2c_remove(dev); ++ ++ core->gate_ctrl = NULL; ++ ++ return 0; ++} ++ ++static struct cx8802_driver cx8802_dvb_driver = { ++ .type_id = CX88_MPEG_DVB, ++ .hw_access = CX8802_DRVCTL_SHARED, ++ .probe = cx8802_dvb_probe, ++ .remove = cx8802_dvb_remove, ++ .advise_acquire = cx8802_dvb_advise_acquire, ++ .advise_release = cx8802_dvb_advise_release, ++}; ++ ++static int __init dvb_init(void) ++{ ++ printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n", ++ CX88_VERSION); ++ return cx8802_register_driver(&cx8802_dvb_driver); ++} ++ ++static void __exit dvb_fini(void) ++{ ++ cx8802_unregister_driver(&cx8802_dvb_driver); ++} ++ ++module_init(dvb_init); ++module_exit(dvb_fini); +diff --git a/drivers/media/pci/cx88/cx88-i2c.c b/drivers/media/pci/cx88/cx88-i2c.c +new file mode 100644 +index 0000000..cf2d696 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-i2c.c +@@ -0,0 +1,183 @@ ++ ++/* ++ ++ cx88-i2c.c -- all the i2c code is here ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ & Marcus Metzler (mocm@thp.uni-koeln.de) ++ (c) 2002 Yurij Sysoev ++ (c) 1999-2003 Gerd Knorr ++ ++ (c) 2005 Mauro Carvalho Chehab ++ - Multituner support and i2c address binding ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++ ++#include ++ ++#include "cx88.h" ++#include ++ ++static unsigned int i2c_debug; ++module_param(i2c_debug, int, 0644); ++MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); ++ ++static unsigned int i2c_scan; ++module_param(i2c_scan, int, 0444); ++MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); ++ ++static unsigned int i2c_udelay = 5; ++module_param(i2c_udelay, int, 0644); ++MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs " ++ "(should be 5 or higher). Lower value means higher bus speed."); ++ ++#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \ ++ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void cx8800_bit_setscl(void *data, int state) ++{ ++ struct cx88_core *core = data; ++ ++ if (state) ++ core->i2c_state |= 0x02; ++ else ++ core->i2c_state &= ~0x02; ++ cx_write(MO_I2C, core->i2c_state); ++ cx_read(MO_I2C); ++} ++ ++static void cx8800_bit_setsda(void *data, int state) ++{ ++ struct cx88_core *core = data; ++ ++ if (state) ++ core->i2c_state |= 0x01; ++ else ++ core->i2c_state &= ~0x01; ++ cx_write(MO_I2C, core->i2c_state); ++ cx_read(MO_I2C); ++} ++ ++static int cx8800_bit_getscl(void *data) ++{ ++ struct cx88_core *core = data; ++ u32 state; ++ ++ state = cx_read(MO_I2C); ++ return state & 0x02 ? 1 : 0; ++} ++ ++static int cx8800_bit_getsda(void *data) ++{ ++ struct cx88_core *core = data; ++ u32 state; ++ ++ state = cx_read(MO_I2C); ++ return state & 0x01; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { ++ .setsda = cx8800_bit_setsda, ++ .setscl = cx8800_bit_setscl, ++ .getsda = cx8800_bit_getsda, ++ .getscl = cx8800_bit_getscl, ++ .udelay = 16, ++ .timeout = 200, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const char * const i2c_devs[128] = { ++ [ 0x1c >> 1 ] = "lgdt330x", ++ [ 0x86 >> 1 ] = "tda9887/cx22702", ++ [ 0xa0 >> 1 ] = "eeprom", ++ [ 0xc0 >> 1 ] = "tuner (analog)", ++ [ 0xc2 >> 1 ] = "tuner (analog/dvb)", ++ [ 0xc8 >> 1 ] = "xc5000", ++}; ++ ++static void do_i2c_scan(const char *name, struct i2c_client *c) ++{ ++ unsigned char buf; ++ int i,rc; ++ ++ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { ++ c->addr = i; ++ rc = i2c_master_recv(c,&buf,0); ++ if (rc < 0) ++ continue; ++ printk("%s: i2c scan: found device @ 0x%x [%s]\n", ++ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); ++ } ++} ++ ++/* init + register i2c adapter */ ++int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) ++{ ++ /* Prevents usage of invalid delay values */ ++ if (i2c_udelay<5) ++ i2c_udelay=5; ++ ++ core->i2c_algo = cx8800_i2c_algo_template; ++ ++ ++ core->i2c_adap.dev.parent = &pci->dev; ++ strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); ++ core->i2c_adap.owner = THIS_MODULE; ++ core->i2c_algo.udelay = i2c_udelay; ++ core->i2c_algo.data = core; ++ i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev); ++ core->i2c_adap.algo_data = &core->i2c_algo; ++ core->i2c_client.adapter = &core->i2c_adap; ++ strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE); ++ ++ cx8800_bit_setscl(core,1); ++ cx8800_bit_setsda(core,1); ++ ++ core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap); ++ if (0 == core->i2c_rc) { ++ static u8 tuner_data[] = ++ { 0x0b, 0xdc, 0x86, 0x52 }; ++ static struct i2c_msg tuner_msg = ++ { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 }; ++ ++ dprintk(1, "i2c register ok\n"); ++ switch( core->boardnr ) { ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n", ++ core->name); ++ i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1); ++ break; ++ default: ++ break; ++ } ++ if (i2c_scan) ++ do_i2c_scan(core->name,&core->i2c_client); ++ } else ++ printk("%s: i2c register FAILED\n", core->name); ++ ++ return core->i2c_rc; ++} +diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c +new file mode 100644 +index 0000000..f29e18c +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-input.c +@@ -0,0 +1,635 @@ ++/* ++ * ++ * Device driver for GPIO attached remote control interfaces ++ * on Conexant 2388x based TV/DVB cards. ++ * ++ * Copyright (c) 2003 Pavel Machek ++ * Copyright (c) 2004 Gerd Knorr ++ * Copyright (c) 2004, 2005 Chris Pascoe ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include ++ ++#define MODULE_NAME "cx88xx" ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct cx88_IR { ++ struct cx88_core *core; ++ struct rc_dev *dev; ++ ++ int users; ++ ++ char name[32]; ++ char phys[32]; ++ ++ /* sample from gpio pin 16 */ ++ u32 sampling; ++ ++ /* poll external decoder */ ++ int polling; ++ struct hrtimer timer; ++ u32 gpio_addr; ++ u32 last_gpio; ++ u32 mask_keycode; ++ u32 mask_keydown; ++ u32 mask_keyup; ++}; ++ ++static unsigned ir_samplerate = 4; ++module_param(ir_samplerate, uint, 0444); ++MODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4"); ++ ++static int ir_debug; ++module_param(ir_debug, int, 0644); /* debug level [IR] */ ++MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); ++ ++#define ir_dprintk(fmt, arg...) if (ir_debug) \ ++ printk(KERN_DEBUG "%s IR: " fmt , ir->core->name , ##arg) ++ ++#define dprintk(fmt, arg...) if (ir_debug) \ ++ printk(KERN_DEBUG "cx88 IR: " fmt , ##arg) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void cx88_ir_handle_key(struct cx88_IR *ir) ++{ ++ struct cx88_core *core = ir->core; ++ u32 gpio, data, auxgpio; ++ ++ /* read gpio value */ ++ gpio = cx_read(ir->gpio_addr); ++ switch (core->boardnr) { ++ case CX88_BOARD_NPGTECH_REALTV_TOP10FM: ++ /* This board apparently uses a combination of 2 GPIO ++ to represent the keys. Additionally, the second GPIO ++ can be used for parity. ++ ++ Example: ++ ++ for key "5" ++ gpio = 0x758, auxgpio = 0xe5 or 0xf5 ++ for key "Power" ++ gpio = 0x758, auxgpio = 0xed or 0xfd ++ */ ++ ++ auxgpio = cx_read(MO_GP1_IO); ++ /* Take out the parity part */ ++ gpio=(gpio & 0x7fd) + (auxgpio & 0xef); ++ break; ++ case CX88_BOARD_WINFAST_DTV1000: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ case CX88_BOARD_WINFAST_DTV1800H_XC4000: ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: ++ gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); ++ auxgpio = gpio; ++ break; ++ default: ++ auxgpio = gpio; ++ } ++ if (ir->polling) { ++ if (ir->last_gpio == auxgpio) ++ return; ++ ir->last_gpio = auxgpio; ++ } ++ ++ /* extract data */ ++ data = ir_extract_bits(gpio, ir->mask_keycode); ++ ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n", ++ gpio, data, ++ ir->polling ? "poll" : "irq", ++ (gpio & ir->mask_keydown) ? " down" : "", ++ (gpio & ir->mask_keyup) ? " up" : ""); ++ ++ if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) { ++ u32 gpio_key = cx_read(MO_GP0_IO); ++ ++ data = (data << 4) | ((gpio_key & 0xf0) >> 4); ++ ++ rc_keydown(ir->dev, data, 0); ++ ++ } else if (ir->mask_keydown) { ++ /* bit set on keydown */ ++ if (gpio & ir->mask_keydown) ++ rc_keydown_notimeout(ir->dev, data, 0); ++ else ++ rc_keyup(ir->dev); ++ ++ } else if (ir->mask_keyup) { ++ /* bit cleared on keydown */ ++ if (0 == (gpio & ir->mask_keyup)) ++ rc_keydown_notimeout(ir->dev, data, 0); ++ else ++ rc_keyup(ir->dev); ++ ++ } else { ++ /* can't distinguish keydown/up :-/ */ ++ rc_keydown_notimeout(ir->dev, data, 0); ++ rc_keyup(ir->dev); ++ } ++} ++ ++static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) ++{ ++ unsigned long missed; ++ struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); ++ ++ cx88_ir_handle_key(ir); ++ missed = hrtimer_forward_now(&ir->timer, ++ ktime_set(0, ir->polling * 1000000)); ++ if (missed > 1) ++ ir_dprintk("Missed ticks %ld\n", missed - 1); ++ ++ return HRTIMER_RESTART; ++} ++ ++static int __cx88_ir_start(void *priv) ++{ ++ struct cx88_core *core = priv; ++ struct cx88_IR *ir; ++ ++ if (!core || !core->ir) ++ return -EINVAL; ++ ++ ir = core->ir; ++ ++ if (ir->polling) { ++ hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ ir->timer.function = cx88_ir_work; ++ hrtimer_start(&ir->timer, ++ ktime_set(0, ir->polling * 1000000), ++ HRTIMER_MODE_REL); ++ } ++ if (ir->sampling) { ++ core->pci_irqmask |= PCI_INT_IR_SMPINT; ++ cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */ ++ cx_write(MO_DDSCFG_IO, 0x5); /* enable */ ++ } ++ return 0; ++} ++ ++static void __cx88_ir_stop(void *priv) ++{ ++ struct cx88_core *core = priv; ++ struct cx88_IR *ir; ++ ++ if (!core || !core->ir) ++ return; ++ ++ ir = core->ir; ++ if (ir->sampling) { ++ cx_write(MO_DDSCFG_IO, 0x0); ++ core->pci_irqmask &= ~PCI_INT_IR_SMPINT; ++ } ++ ++ if (ir->polling) ++ hrtimer_cancel(&ir->timer); ++} ++ ++int cx88_ir_start(struct cx88_core *core) ++{ ++ if (core->ir->users) ++ return __cx88_ir_start(core); ++ ++ return 0; ++} ++ ++void cx88_ir_stop(struct cx88_core *core) ++{ ++ if (core->ir->users) ++ __cx88_ir_stop(core); ++} ++ ++static int cx88_ir_open(struct rc_dev *rc) ++{ ++ struct cx88_core *core = rc->priv; ++ ++ core->ir->users++; ++ return __cx88_ir_start(core); ++} ++ ++static void cx88_ir_close(struct rc_dev *rc) ++{ ++ struct cx88_core *core = rc->priv; ++ ++ core->ir->users--; ++ if (!core->ir->users) ++ __cx88_ir_stop(core); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ++{ ++ struct cx88_IR *ir; ++ struct rc_dev *dev; ++ char *ir_codes = NULL; ++ u64 rc_type = RC_BIT_OTHER; ++ int err = -ENOMEM; ++ u32 hardware_mask = 0; /* For devices with a hardware mask, when ++ * used with a full-code IR table ++ */ ++ ++ ir = kzalloc(sizeof(*ir), GFP_KERNEL); ++ dev = rc_allocate_device(); ++ if (!ir || !dev) ++ goto err_out_free; ++ ++ ir->dev = dev; ++ ++ /* detect & configure */ ++ switch (core->boardnr) { ++ case CX88_BOARD_DNTV_LIVE_DVB_T: ++ case CX88_BOARD_KWORLD_DVB_T: ++ case CX88_BOARD_KWORLD_DVB_T_CX22702: ++ ir_codes = RC_MAP_DNTV_LIVE_DVB_T; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0x1f; ++ ir->mask_keyup = 0x60; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: ++ ir_codes = RC_MAP_CINERGY_1400; ++ ir->sampling = 0xeb04; /* address */ ++ break; ++ case CX88_BOARD_HAUPPAUGE: ++ case CX88_BOARD_HAUPPAUGE_DVB_T1: ++ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: ++ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: ++ case CX88_BOARD_HAUPPAUGE_HVR1100: ++ case CX88_BOARD_HAUPPAUGE_HVR3000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000: ++ case CX88_BOARD_HAUPPAUGE_HVR4000LITE: ++ case CX88_BOARD_PCHDTV_HD3000: ++ case CX88_BOARD_PCHDTV_HD5500: ++ case CX88_BOARD_HAUPPAUGE_IRONLY: ++ ir_codes = RC_MAP_HAUPPAUGE; ++ ir->sampling = 1; ++ break; ++ case CX88_BOARD_WINFAST_DTV2000H: ++ case CX88_BOARD_WINFAST_DTV2000H_J: ++ case CX88_BOARD_WINFAST_DTV1800H: ++ case CX88_BOARD_WINFAST_DTV1800H_XC4000: ++ case CX88_BOARD_WINFAST_DTV2000H_PLUS: ++ ir_codes = RC_MAP_WINFAST; ++ ir->gpio_addr = MO_GP0_IO; ++ ir->mask_keycode = 0x8f8; ++ ir->mask_keyup = 0x100; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_WINFAST2000XP_EXPERT: ++ case CX88_BOARD_WINFAST_DTV1000: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36: ++ case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43: ++ ir_codes = RC_MAP_WINFAST; ++ ir->gpio_addr = MO_GP0_IO; ++ ir->mask_keycode = 0x8f8; ++ ir->mask_keyup = 0x100; ++ ir->polling = 1; /* ms */ ++ break; ++ case CX88_BOARD_IODATA_GVBCTV7E: ++ ir_codes = RC_MAP_IODATA_BCTV7E; ++ ir->gpio_addr = MO_GP0_IO; ++ ir->mask_keycode = 0xfd; ++ ir->mask_keydown = 0x02; ++ ir->polling = 5; /* ms */ ++ break; ++ case CX88_BOARD_PROLINK_PLAYTVPVR: ++ case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: ++ /* ++ * It seems that this hardware is paired with NEC extended ++ * address 0x866b. So, unfortunately, its usage with other ++ * IR's with different address won't work. Still, there are ++ * other IR's from the same manufacturer that works, like the ++ * 002-T mini RC, provided with newer PV hardware ++ */ ++ ir_codes = RC_MAP_PIXELVIEW_MK12; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keyup = 0x80; ++ ir->polling = 10; /* ms */ ++ hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ ++ break; ++ case CX88_BOARD_PROLINK_PV_8000GT: ++ case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: ++ ir_codes = RC_MAP_PIXELVIEW_NEW; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0x3f; ++ ir->mask_keyup = 0x80; ++ ir->polling = 1; /* ms */ ++ break; ++ case CX88_BOARD_KWORLD_LTV883: ++ ir_codes = RC_MAP_PIXELVIEW; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0x1f; ++ ir->mask_keyup = 0x60; ++ ir->polling = 1; /* ms */ ++ break; ++ case CX88_BOARD_ADSTECH_DVB_T_PCI: ++ ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0xbf; ++ ir->mask_keyup = 0x40; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_MSI_TVANYWHERE_MASTER: ++ ir_codes = RC_MAP_MSI_TVANYWHERE; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0x1f; ++ ir->mask_keyup = 0x40; ++ ir->polling = 1; /* ms */ ++ break; ++ case CX88_BOARD_AVERTV_303: ++ case CX88_BOARD_AVERTV_STUDIO_303: ++ ir_codes = RC_MAP_AVERTV_303; ++ ir->gpio_addr = MO_GP2_IO; ++ ir->mask_keycode = 0xfb; ++ ir->mask_keydown = 0x02; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_OMICOM_SS4_PCI: ++ case CX88_BOARD_SATTRADE_ST4200: ++ case CX88_BOARD_TBS_8920: ++ case CX88_BOARD_TBS_8910: ++ case CX88_BOARD_PROF_7300: ++ case CX88_BOARD_PROF_7301: ++ case CX88_BOARD_PROF_6200: ++ ir_codes = RC_MAP_TBS_NEC; ++ ir->sampling = 0xff00; /* address */ ++ break; ++ case CX88_BOARD_TEVII_S464: ++ case CX88_BOARD_TEVII_S460: ++ case CX88_BOARD_TEVII_S420: ++ ir_codes = RC_MAP_TEVII_NEC; ++ ir->sampling = 0xff00; /* address */ ++ break; ++ case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ++ ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; ++ ir->sampling = 0xff00; /* address */ ++ break; ++ case CX88_BOARD_NORWOOD_MICRO: ++ ir_codes = RC_MAP_NORWOOD; ++ ir->gpio_addr = MO_GP1_IO; ++ ir->mask_keycode = 0x0e; ++ ir->mask_keyup = 0x80; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_NPGTECH_REALTV_TOP10FM: ++ ir_codes = RC_MAP_NPGTECH; ++ ir->gpio_addr = MO_GP0_IO; ++ ir->mask_keycode = 0xfa; ++ ir->polling = 50; /* ms */ ++ break; ++ case CX88_BOARD_PINNACLE_PCTV_HD_800i: ++ ir_codes = RC_MAP_PINNACLE_PCTV_HD; ++ ir->sampling = 1; ++ break; ++ case CX88_BOARD_POWERCOLOR_REAL_ANGEL: ++ ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; ++ ir->gpio_addr = MO_GP2_IO; ++ ir->mask_keycode = 0x7e; ++ ir->polling = 100; /* ms */ ++ break; ++ case CX88_BOARD_TWINHAN_VP1027_DVBS: ++ ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; ++ rc_type = RC_BIT_NEC; ++ ir->sampling = 0xff00; /* address */ ++ break; ++ } ++ ++ if (!ir_codes) { ++ err = -ENODEV; ++ goto err_out_free; ++ } ++ ++ /* ++ * The usage of mask_keycode were very convenient, due to several ++ * reasons. Among others, the scancode tables were using the scancode ++ * as the index elements. So, the less bits it was used, the smaller ++ * the table were stored. After the input changes, the better is to use ++ * the full scancodes, since it allows replacing the IR remote by ++ * another one. Unfortunately, there are still some hardware, like ++ * Pixelview Ultra Pro, where only part of the scancode is sent via ++ * GPIO. So, there's no way to get the full scancode. Due to that, ++ * hardware_mask were introduced here: it represents those hardware ++ * that has such limits. ++ */ ++ if (hardware_mask && !ir->mask_keycode) ++ ir->mask_keycode = hardware_mask; ++ ++ /* init input device */ ++ snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); ++ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); ++ ++ dev->input_name = ir->name; ++ dev->input_phys = ir->phys; ++ dev->input_id.bustype = BUS_PCI; ++ dev->input_id.version = 1; ++ if (pci->subsystem_vendor) { ++ dev->input_id.vendor = pci->subsystem_vendor; ++ dev->input_id.product = pci->subsystem_device; ++ } else { ++ dev->input_id.vendor = pci->vendor; ++ dev->input_id.product = pci->device; ++ } ++ dev->dev.parent = &pci->dev; ++ dev->map_name = ir_codes; ++ dev->driver_name = MODULE_NAME; ++ dev->priv = core; ++ dev->open = cx88_ir_open; ++ dev->close = cx88_ir_close; ++ dev->scanmask = hardware_mask; ++ ++ if (ir->sampling) { ++ dev->driver_type = RC_DRIVER_IR_RAW; ++ dev->timeout = 10 * 1000 * 1000; /* 10 ms */ ++ } else { ++ dev->driver_type = RC_DRIVER_SCANCODE; ++ dev->allowed_protos = rc_type; ++ } ++ ++ ir->core = core; ++ core->ir = ir; ++ ++ /* all done */ ++ err = rc_register_device(dev); ++ if (err) ++ goto err_out_free; ++ ++ return 0; ++ ++err_out_free: ++ rc_free_device(dev); ++ core->ir = NULL; ++ kfree(ir); ++ return err; ++} ++ ++int cx88_ir_fini(struct cx88_core *core) ++{ ++ struct cx88_IR *ir = core->ir; ++ ++ /* skip detach on non attached boards */ ++ if (NULL == ir) ++ return 0; ++ ++ cx88_ir_stop(core); ++ rc_unregister_device(ir->dev); ++ kfree(ir); ++ ++ /* done */ ++ core->ir = NULL; ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void cx88_ir_irq(struct cx88_core *core) ++{ ++ struct cx88_IR *ir = core->ir; ++ u32 samples; ++ unsigned todo, bits; ++ struct ir_raw_event ev; ++ ++ if (!ir || !ir->sampling) ++ return; ++ ++ /* ++ * Samples are stored in a 32 bit register, oldest sample in ++ * the msb. A set bit represents space and an unset bit ++ * represents a pulse. ++ */ ++ samples = cx_read(MO_SAMPLE_IO); ++ ++ if (samples == 0xff && ir->dev->idle) ++ return; ++ ++ init_ir_raw_event(&ev); ++ for (todo = 32; todo > 0; todo -= bits) { ++ ev.pulse = samples & 0x80000000 ? false : true; ++ bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); ++ ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate; ++ ir_raw_event_store_with_filter(ir->dev, &ev); ++ samples <<= bits; ++ } ++ ir_raw_event_handle(ir->dev); ++} ++ ++static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ int flags, code; ++ ++ /* poll IR chip */ ++ flags = i2c_smbus_read_byte_data(ir->c, 0x10); ++ if (flags < 0) { ++ dprintk("read error\n"); ++ return 0; ++ } ++ /* key pressed ? */ ++ if (0 == (flags & 0x80)) ++ return 0; ++ ++ /* read actual key code */ ++ code = i2c_smbus_read_byte_data(ir->c, 0x00); ++ if (code < 0) { ++ dprintk("read error\n"); ++ return 0; ++ } ++ ++ dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", ++ code & 0xff, flags & 0xff); ++ ++ *ir_key = code & 0xff; ++ *ir_raw = code; ++ return 1; ++} ++ ++void cx88_i2c_init_ir(struct cx88_core *core) ++{ ++ struct i2c_board_info info; ++ const unsigned short default_addr_list[] = { ++ 0x18, 0x6b, 0x71, ++ I2C_CLIENT_END ++ }; ++ const unsigned short pvr2000_addr_list[] = { ++ 0x18, 0x1a, ++ I2C_CLIENT_END ++ }; ++ const unsigned short *addr_list = default_addr_list; ++ const unsigned short *addrp; ++ /* Instantiate the IR receiver device, if present */ ++ if (0 != core->i2c_rc) ++ return; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "ir_video", I2C_NAME_SIZE); ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_LEADTEK_PVR2000: ++ addr_list = pvr2000_addr_list; ++ core->init_data.name = "cx88 Leadtek PVR 2000 remote"; ++ core->init_data.type = RC_BIT_UNKNOWN; ++ core->init_data.get_key = get_key_pvr2000; ++ core->init_data.ir_codes = RC_MAP_EMPTY; ++ break; ++ } ++ ++ /* ++ * We can't call i2c_new_probed_device() because it uses ++ * quick writes for probing and at least some RC receiver ++ * devices only reply to reads. ++ * Also, Hauppauge XVR needs to be specified, as address 0x71 ++ * conflicts with another remote type used with saa7134 ++ */ ++ for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) { ++ info.platform_data = NULL; ++ memset(&core->init_data, 0, sizeof(core->init_data)); ++ ++ if (*addrp == 0x71) { ++ /* Hauppauge XVR */ ++ core->init_data.name = "cx88 Hauppauge XVR remote"; ++ core->init_data.ir_codes = RC_MAP_HAUPPAUGE; ++ core->init_data.type = RC_BIT_RC5; ++ core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; ++ ++ info.platform_data = &core->init_data; ++ } ++ if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0, ++ I2C_SMBUS_READ, 0, ++ I2C_SMBUS_QUICK, NULL) >= 0) { ++ info.addr = *addrp; ++ i2c_new_device(&core->i2c_adap, &info); ++ break; ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); ++MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c +new file mode 100644 +index 0000000..d46b008 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-mpeg.c +@@ -0,0 +1,931 @@ ++/* ++ * ++ * Support for the mpeg transport stream transfers ++ * PCI function #2 of the cx2388x. ++ * ++ * (c) 2004 Jelle Foks ++ * (c) 2004 Chris Pascoe ++ * (c) 2004 Gerd Knorr ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++ ++/* ------------------------------------------------------------------ */ ++ ++MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); ++MODULE_AUTHOR("Jelle Foks "); ++MODULE_AUTHOR("Chris Pascoe "); ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX88_VERSION); ++ ++static unsigned int debug; ++module_param(debug,int,0644); ++MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); ++ ++#define dprintk(level, fmt, arg...) do { \ ++ if (debug + 1 > level) \ ++ printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg); \ ++} while(0) ++ ++#define mpeg_dbg(level, fmt, arg...) do { \ ++ if (debug + 1 > level) \ ++ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg); \ ++} while(0) ++ ++#if defined(CONFIG_MODULES) && defined(MODULE) ++static void request_module_async(struct work_struct *work) ++{ ++ struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); ++ ++ if (dev->core->board.mpeg & CX88_MPEG_DVB) ++ request_module("cx88-dvb"); ++ if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD) ++ request_module("cx88-blackbird"); ++} ++ ++static void request_modules(struct cx8802_dev *dev) ++{ ++ INIT_WORK(&dev->request_module_wk, request_module_async); ++ schedule_work(&dev->request_module_wk); ++} ++ ++static void flush_request_modules(struct cx8802_dev *dev) ++{ ++ flush_work(&dev->request_module_wk); ++} ++#else ++#define request_modules(dev) ++#define flush_request_modules(dev) ++#endif /* CONFIG_MODULES */ ++ ++ ++static LIST_HEAD(cx8802_devlist); ++static DEFINE_MUTEX(cx8802_mutex); ++/* ------------------------------------------------------------------ */ ++ ++static int cx8802_start_dma(struct cx8802_dev *dev, ++ struct cx88_dmaqueue *q, ++ struct cx88_buffer *buf) ++{ ++ struct cx88_core *core = dev->core; ++ ++ dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n", ++ buf->vb.width, buf->vb.height, buf->vb.field); ++ ++ /* setup fifo + format */ ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], ++ dev->ts_packet_size, buf->risc.dma); ++ ++ /* write TS length to chip */ ++ cx_write(MO_TS_LNGTH, buf->vb.width); ++ ++ /* FIXME: this needs a review. ++ * also: move to cx88-blackbird + cx88-dvb source files? */ ++ ++ dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); ++ ++ if ( (core->active_type_id == CX88_MPEG_DVB) && ++ (core->board.mpeg & CX88_MPEG_DVB) ) { ++ ++ dprintk( 1, "cx8802_start_dma doing .dvb\n"); ++ /* negedge driven & software reset */ ++ cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); ++ udelay(100); ++ cx_write(MO_PINMUX_IO, 0x00); ++ cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01); ++ switch (core->boardnr) { ++ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: ++ case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: ++ case CX88_BOARD_PCHDTV_HD5500: ++ cx_write(TS_SOP_STAT, 1<<13); ++ break; ++ case CX88_BOARD_SAMSUNG_SMT_7020: ++ cx_write(TS_SOP_STAT, 0x00); ++ break; ++ case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: ++ case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: ++ cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ ++ udelay(100); ++ break; ++ case CX88_BOARD_HAUPPAUGE_HVR1300: ++ /* Enable MPEG parallel IO and video signal pins */ ++ cx_write(MO_PINMUX_IO, 0x88); ++ cx_write(TS_SOP_STAT, 0); ++ cx_write(TS_VALERR_CNTRL, 0); ++ break; ++ case CX88_BOARD_PINNACLE_PCTV_HD_800i: ++ /* Enable MPEG parallel IO and video signal pins */ ++ cx_write(MO_PINMUX_IO, 0x88); ++ cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4)); ++ dev->ts_gen_cntrl = 5; ++ cx_write(TS_SOP_STAT, 0); ++ cx_write(TS_VALERR_CNTRL, 0); ++ udelay(100); ++ break; ++ default: ++ cx_write(TS_SOP_STAT, 0x00); ++ break; ++ } ++ cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); ++ udelay(100); ++ } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && ++ (core->board.mpeg & CX88_MPEG_BLACKBIRD) ) { ++ dprintk( 1, "cx8802_start_dma doing .blackbird\n"); ++ cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ ++ ++ cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ ++ udelay(100); ++ ++ cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ ++ cx_write(TS_VALERR_CNTRL, 0x2000); ++ ++ cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ ++ udelay(100); ++ } else { ++ printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__, ++ core->board.mpeg ); ++ return -EINVAL; ++ } ++ ++ /* reset counter */ ++ cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); ++ q->count = 1; ++ ++ /* enable irqs */ ++ dprintk( 1, "setting the interrupt mask\n" ); ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT); ++ cx_set(MO_TS_INTMSK, 0x1f0011); ++ ++ /* start dma */ ++ cx_set(MO_DEV_CNTRL2, (1<<5)); ++ cx_set(MO_TS_DMACNTRL, 0x11); ++ return 0; ++} ++ ++static int cx8802_stop_dma(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ dprintk( 1, "cx8802_stop_dma\n" ); ++ ++ /* stop dma */ ++ cx_clear(MO_TS_DMACNTRL, 0x11); ++ ++ /* disable irqs */ ++ cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT); ++ cx_clear(MO_TS_INTMSK, 0x1f0011); ++ ++ /* Reset the controller */ ++ cx_write(TS_GEN_CNTRL, 0xcd); ++ return 0; ++} ++ ++static int cx8802_restart_queue(struct cx8802_dev *dev, ++ struct cx88_dmaqueue *q) ++{ ++ struct cx88_buffer *buf; ++ ++ dprintk( 1, "cx8802_restart_queue\n" ); ++ if (list_empty(&q->active)) ++ { ++ struct cx88_buffer *prev; ++ prev = NULL; ++ ++ dprintk(1, "cx8802_restart_queue: queue is empty\n" ); ++ ++ for (;;) { ++ if (list_empty(&q->queued)) ++ return 0; ++ buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); ++ if (NULL == prev) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ cx8802_start_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(1,"[%p/%d] restart_queue - first active\n", ++ buf,buf->vb.i); ++ ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ dprintk(1,"[%p/%d] restart_queue - move to active\n", ++ buf,buf->vb.i); ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++ return 0; ++ } ++ ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ dprintk(2,"restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ cx8802_start_dma(dev, q, buf); ++ list_for_each_entry(buf, &q->active, vb.queue) ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, ++ struct cx88_buffer *buf, enum v4l2_field field) ++{ ++ int size = dev->ts_packet_size * dev->ts_packet_count; ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ int rc; ++ ++ dprintk(1, "%s: %p\n", __func__, buf); ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ buf->vb.width = dev->ts_packet_size; ++ buf->vb.height = dev->ts_packet_count; ++ buf->vb.size = size; ++ buf->vb.field = field /*V4L2_FIELD_TOP*/; ++ ++ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) ++ goto fail; ++ cx88_risc_databuffer(dev->pci, &buf->risc, ++ dma->sglist, ++ buf->vb.width, buf->vb.height, 0); ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx88_free_buffer(q,buf); ++ return rc; ++} ++ ++void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) ++{ ++ struct cx88_buffer *prev; ++ struct cx88_dmaqueue *cx88q = &dev->mpegq; ++ ++ dprintk( 1, "cx8802_buf_queue\n" ); ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); ++ ++ if (list_empty(&cx88q->active)) { ++ dprintk( 1, "queue is empty - first active\n" ); ++ list_add_tail(&buf->vb.queue,&cx88q->active); ++ cx8802_start_dma(dev, cx88q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = cx88q->count++; ++ mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(1,"[%p/%d] %s - first active\n", ++ buf, buf->vb.i, __func__); ++ ++ } else { ++ dprintk( 1, "queue is not empty - append to active\n" ); ++ prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue); ++ list_add_tail(&buf->vb.queue,&cx88q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = cx88q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ dprintk( 1, "[%p/%d] %s - append to active\n", ++ buf, buf->vb.i, __func__); ++ } ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart) ++{ ++ struct cx88_dmaqueue *q = &dev->mpegq; ++ struct cx88_buffer *buf; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->slock,flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ dprintk(1,"[%p/%d] %s - dma=0x%08lx\n", ++ buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); ++ } ++ if (restart) ++ { ++ dprintk(1, "restarting queue\n" ); ++ cx8802_restart_queue(dev,q); ++ } ++ spin_unlock_irqrestore(&dev->slock,flags); ++} ++ ++void cx8802_cancel_buffers(struct cx8802_dev *dev) ++{ ++ struct cx88_dmaqueue *q = &dev->mpegq; ++ ++ dprintk( 1, "cx8802_cancel_buffers" ); ++ del_timer_sync(&q->timeout); ++ cx8802_stop_dma(dev); ++ do_cancel_buffers(dev,"cancel",0); ++} ++ ++static void cx8802_timeout(unsigned long data) ++{ ++ struct cx8802_dev *dev = (struct cx8802_dev*)data; ++ ++ dprintk(1, "%s\n",__func__); ++ ++ if (debug) ++ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); ++ cx8802_stop_dma(dev); ++ do_cancel_buffers(dev,"timeout",1); ++} ++ ++static const char * cx88_mpeg_irqs[32] = { ++ "ts_risci1", NULL, NULL, NULL, ++ "ts_risci2", NULL, NULL, NULL, ++ "ts_oflow", NULL, NULL, NULL, ++ "ts_sync", NULL, NULL, NULL, ++ "opc_err", "par_err", "rip_err", "pci_abort", ++ "ts_err?", ++}; ++ ++static void cx8802_mpeg_irq(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ u32 status, mask, count; ++ ++ dprintk( 1, "cx8802_mpeg_irq\n" ); ++ status = cx_read(MO_TS_INTSTAT); ++ mask = cx_read(MO_TS_INTMSK); ++ if (0 == (status & mask)) ++ return; ++ ++ cx_write(MO_TS_INTSTAT, status); ++ ++ if (debug || (status & mask & ~0xff)) ++ cx88_print_irqbits(core->name, "irq mpeg ", ++ cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), ++ status, mask); ++ ++ /* risc op code error */ ++ if (status & (1 << 16)) { ++ printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name); ++ cx_clear(MO_TS_DMACNTRL, 0x11); ++ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); ++ } ++ ++ /* risc1 y */ ++ if (status & 0x01) { ++ dprintk( 1, "wake up\n" ); ++ spin_lock(&dev->slock); ++ count = cx_read(MO_TS_GPCNT); ++ cx88_wakeup(dev->core, &dev->mpegq, count); ++ spin_unlock(&dev->slock); ++ } ++ ++ /* risc2 y */ ++ if (status & 0x10) { ++ spin_lock(&dev->slock); ++ cx8802_restart_queue(dev,&dev->mpegq); ++ spin_unlock(&dev->slock); ++ } ++ ++ /* other general errors */ ++ if (status & 0x1f0100) { ++ dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 ); ++ spin_lock(&dev->slock); ++ cx8802_stop_dma(dev); ++ cx8802_restart_queue(dev,&dev->mpegq); ++ spin_unlock(&dev->slock); ++ } ++} ++ ++#define MAX_IRQ_LOOP 10 ++ ++static irqreturn_t cx8802_irq(int irq, void *dev_id) ++{ ++ struct cx8802_dev *dev = dev_id; ++ struct cx88_core *core = dev->core; ++ u32 status; ++ int loop, handled = 0; ++ ++ for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { ++ status = cx_read(MO_PCI_INTSTAT) & ++ (core->pci_irqmask | PCI_INT_TSINT); ++ if (0 == status) ++ goto out; ++ dprintk( 1, "cx8802_irq\n" ); ++ dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); ++ dprintk( 1, " status: %d\n", status ); ++ handled = 1; ++ cx_write(MO_PCI_INTSTAT, status); ++ ++ if (status & core->pci_irqmask) ++ cx88_core_irq(core,status); ++ if (status & PCI_INT_TSINT) ++ cx8802_mpeg_irq(dev); ++ } ++ if (MAX_IRQ_LOOP == loop) { ++ dprintk( 0, "clearing mask\n" ); ++ printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", ++ core->name); ++ cx_write(MO_PCI_INTMSK,0); ++ } ++ ++ out: ++ return IRQ_RETVAL(handled); ++} ++ ++static int cx8802_init_common(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ int err; ++ ++ /* pci init */ ++ if (pci_enable_device(dev->pci)) ++ return -EIO; ++ pci_set_master(dev->pci); ++ if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) { ++ printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); ++ return -EIO; ++ } ++ ++ dev->pci_rev = dev->pci->revision; ++ pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); ++ printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", dev->core->name, ++ pci_name(dev->pci), dev->pci_rev, dev->pci->irq, ++ dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0)); ++ ++ /* initialize driver struct */ ++ spin_lock_init(&dev->slock); ++ ++ /* init dma queue */ ++ INIT_LIST_HEAD(&dev->mpegq.active); ++ INIT_LIST_HEAD(&dev->mpegq.queued); ++ dev->mpegq.timeout.function = cx8802_timeout; ++ dev->mpegq.timeout.data = (unsigned long)dev; ++ init_timer(&dev->mpegq.timeout); ++ cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, ++ MO_TS_DMACNTRL,0x11,0x00); ++ ++ /* get irq */ ++ err = request_irq(dev->pci->irq, cx8802_irq, ++ IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev); ++ if (err < 0) { ++ printk(KERN_ERR "%s: can't get IRQ %d\n", ++ dev->core->name, dev->pci->irq); ++ return err; ++ } ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask); ++ ++ /* everything worked */ ++ pci_set_drvdata(dev->pci,dev); ++ return 0; ++} ++ ++static void cx8802_fini_common(struct cx8802_dev *dev) ++{ ++ dprintk( 2, "cx8802_fini_common\n" ); ++ cx8802_stop_dma(dev); ++ pci_disable_device(dev->pci); ++ ++ /* unregister stuff */ ++ free_irq(dev->pci->irq, dev); ++ pci_set_drvdata(dev->pci, NULL); ++ ++ /* free memory */ ++ btcx_riscmem_free(dev->pci,&dev->mpegq.stopper); ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) ++{ ++ struct cx8802_dev *dev = pci_get_drvdata(pci_dev); ++ struct cx88_core *core = dev->core; ++ ++ /* stop mpeg dma */ ++ spin_lock(&dev->slock); ++ if (!list_empty(&dev->mpegq.active)) { ++ dprintk( 2, "suspend\n" ); ++ printk("%s: suspend mpeg\n", core->name); ++ cx8802_stop_dma(dev); ++ del_timer(&dev->mpegq.timeout); ++ } ++ spin_unlock(&dev->slock); ++ ++ /* FIXME -- shutdown device */ ++ cx88_shutdown(dev->core); ++ ++ pci_save_state(pci_dev); ++ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { ++ pci_disable_device(pci_dev); ++ dev->state.disabled = 1; ++ } ++ return 0; ++} ++ ++static int cx8802_resume_common(struct pci_dev *pci_dev) ++{ ++ struct cx8802_dev *dev = pci_get_drvdata(pci_dev); ++ struct cx88_core *core = dev->core; ++ int err; ++ ++ if (dev->state.disabled) { ++ err=pci_enable_device(pci_dev); ++ if (err) { ++ printk(KERN_ERR "%s: can't enable device\n", ++ dev->core->name); ++ return err; ++ } ++ dev->state.disabled = 0; ++ } ++ err=pci_set_power_state(pci_dev, PCI_D0); ++ if (err) { ++ printk(KERN_ERR "%s: can't enable device\n", ++ dev->core->name); ++ pci_disable_device(pci_dev); ++ dev->state.disabled = 1; ++ ++ return err; ++ } ++ pci_restore_state(pci_dev); ++ ++ /* FIXME: re-initialize hardware */ ++ cx88_reset(dev->core); ++ ++ /* restart video+vbi capture */ ++ spin_lock(&dev->slock); ++ if (!list_empty(&dev->mpegq.active)) { ++ printk("%s: resume mpeg\n", core->name); ++ cx8802_restart_queue(dev,&dev->mpegq); ++ } ++ spin_unlock(&dev->slock); ++ ++ return 0; ++} ++ ++struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) ++{ ++ struct cx8802_driver *d; ++ ++ list_for_each_entry(d, &dev->drvlist, drvlist) ++ if (d->type_id == btype) ++ return d; ++ ++ return NULL; ++} ++ ++/* Driver asked for hardware access. */ ++static int cx8802_request_acquire(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ unsigned int i; ++ ++ /* Fail a request for hardware if the device is busy. */ ++ if (core->active_type_id != CX88_BOARD_NONE && ++ core->active_type_id != drv->type_id) ++ return -EBUSY; ++ ++ if (drv->type_id == CX88_MPEG_DVB) { ++ /* When switching to DVB, always set the input to the tuner */ ++ core->last_analog_input = core->input; ++ core->input = 0; ++ for (i = 0; ++ i < (sizeof(core->board.input) / sizeof(struct cx88_input)); ++ i++) { ++ if (core->board.input[i].type == CX88_VMUX_DVB) { ++ core->input = i; ++ break; ++ } ++ } ++ } ++ ++ if (drv->advise_acquire) ++ { ++ core->active_ref++; ++ if (core->active_type_id == CX88_BOARD_NONE) { ++ core->active_type_id = drv->type_id; ++ drv->advise_acquire(drv); ++ } ++ ++ mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); ++ } ++ ++ return 0; ++} ++ ++/* Driver asked to release hardware. */ ++static int cx8802_request_release(struct cx8802_driver *drv) ++{ ++ struct cx88_core *core = drv->core; ++ ++ if (drv->advise_release && --core->active_ref == 0) ++ { ++ if (drv->type_id == CX88_MPEG_DVB) { ++ /* If the DVB driver is releasing, reset the input ++ state to the last configured analog input */ ++ core->input = core->last_analog_input; ++ } ++ ++ drv->advise_release(drv); ++ core->active_type_id = CX88_BOARD_NONE; ++ mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); ++ } ++ ++ return 0; ++} ++ ++static int cx8802_check_driver(struct cx8802_driver *drv) ++{ ++ if (drv == NULL) ++ return -ENODEV; ++ ++ if ((drv->type_id != CX88_MPEG_DVB) && ++ (drv->type_id != CX88_MPEG_BLACKBIRD)) ++ return -EINVAL; ++ ++ if ((drv->hw_access != CX8802_DRVCTL_SHARED) && ++ (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) ++ return -EINVAL; ++ ++ if ((drv->probe == NULL) || ++ (drv->remove == NULL) || ++ (drv->advise_acquire == NULL) || ++ (drv->advise_release == NULL)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int cx8802_register_driver(struct cx8802_driver *drv) ++{ ++ struct cx8802_dev *dev; ++ struct cx8802_driver *driver; ++ int err, i = 0; ++ ++ printk(KERN_INFO ++ "cx88/2: registering cx8802 driver, type: %s access: %s\n", ++ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", ++ drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); ++ ++ if ((err = cx8802_check_driver(drv)) != 0) { ++ printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n"); ++ return err; ++ } ++ ++ mutex_lock(&cx8802_mutex); ++ ++ list_for_each_entry(dev, &cx8802_devlist, devlist) { ++ printk(KERN_INFO ++ "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", ++ dev->core->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device, dev->core->board.name, ++ dev->core->boardnr); ++ ++ /* Bring up a new struct for each driver instance */ ++ driver = kzalloc(sizeof(*drv),GFP_KERNEL); ++ if (driver == NULL) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Snapshot of the driver registration data */ ++ drv->core = dev->core; ++ drv->suspend = cx8802_suspend_common; ++ drv->resume = cx8802_resume_common; ++ drv->request_acquire = cx8802_request_acquire; ++ drv->request_release = cx8802_request_release; ++ memcpy(driver, drv, sizeof(*driver)); ++ ++ mutex_lock(&drv->core->lock); ++ err = drv->probe(driver); ++ if (err == 0) { ++ i++; ++ list_add_tail(&driver->drvlist, &dev->drvlist); ++ } else { ++ printk(KERN_ERR ++ "%s/2: cx8802 probe failed, err = %d\n", ++ dev->core->name, err); ++ } ++ mutex_unlock(&drv->core->lock); ++ } ++ ++ err = i ? 0 : -ENODEV; ++out: ++ mutex_unlock(&cx8802_mutex); ++ return err; ++} ++ ++int cx8802_unregister_driver(struct cx8802_driver *drv) ++{ ++ struct cx8802_dev *dev; ++ struct cx8802_driver *d, *dtmp; ++ int err = 0; ++ ++ printk(KERN_INFO ++ "cx88/2: unregistering cx8802 driver, type: %s access: %s\n", ++ drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", ++ drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); ++ ++ mutex_lock(&cx8802_mutex); ++ ++ list_for_each_entry(dev, &cx8802_devlist, devlist) { ++ printk(KERN_INFO ++ "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", ++ dev->core->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device, dev->core->board.name, ++ dev->core->boardnr); ++ ++ mutex_lock(&dev->core->lock); ++ ++ list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { ++ /* only unregister the correct driver type */ ++ if (d->type_id != drv->type_id) ++ continue; ++ ++ err = d->remove(d); ++ if (err == 0) { ++ list_del(&d->drvlist); ++ kfree(d); ++ } else ++ printk(KERN_ERR "%s/2: cx8802 driver remove " ++ "failed (%d)\n", dev->core->name, err); ++ } ++ ++ mutex_unlock(&dev->core->lock); ++ } ++ ++ mutex_unlock(&cx8802_mutex); ++ ++ return err; ++} ++ ++/* ----------------------------------------------------------- */ ++static int __devinit cx8802_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct cx8802_dev *dev; ++ struct cx88_core *core; ++ int err; ++ ++ /* general setup */ ++ core = cx88_core_get(pci_dev); ++ if (NULL == core) ++ return -EINVAL; ++ ++ printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); ++ ++ err = -ENODEV; ++ if (!core->board.mpeg) ++ goto fail_core; ++ ++ err = -ENOMEM; ++ dev = kzalloc(sizeof(*dev),GFP_KERNEL); ++ if (NULL == dev) ++ goto fail_core; ++ dev->pci = pci_dev; ++ dev->core = core; ++ ++ /* Maintain a reference so cx88-video can query the 8802 device. */ ++ core->dvbdev = dev; ++ ++ err = cx8802_init_common(dev); ++ if (err != 0) ++ goto fail_free; ++ ++ INIT_LIST_HEAD(&dev->drvlist); ++ mutex_lock(&cx8802_mutex); ++ list_add_tail(&dev->devlist,&cx8802_devlist); ++ mutex_unlock(&cx8802_mutex); ++ ++ /* now autoload cx88-dvb or cx88-blackbird */ ++ request_modules(dev); ++ return 0; ++ ++ fail_free: ++ kfree(dev); ++ fail_core: ++ core->dvbdev = NULL; ++ cx88_core_put(core,pci_dev); ++ return err; ++} ++ ++static void __devexit cx8802_remove(struct pci_dev *pci_dev) ++{ ++ struct cx8802_dev *dev; ++ ++ dev = pci_get_drvdata(pci_dev); ++ ++ dprintk( 1, "%s\n", __func__); ++ ++ flush_request_modules(dev); ++ ++ mutex_lock(&dev->core->lock); ++ ++ if (!list_empty(&dev->drvlist)) { ++ struct cx8802_driver *drv, *tmp; ++ int err; ++ ++ printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver " ++ "while cx8802 sub-drivers still loaded?!\n", ++ dev->core->name); ++ ++ list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { ++ err = drv->remove(drv); ++ if (err == 0) { ++ list_del(&drv->drvlist); ++ } else ++ printk(KERN_ERR "%s/2: cx8802 driver remove " ++ "failed (%d)\n", dev->core->name, err); ++ kfree(drv); ++ } ++ } ++ ++ mutex_unlock(&dev->core->lock); ++ ++ /* Destroy any 8802 reference. */ ++ dev->core->dvbdev = NULL; ++ ++ /* common */ ++ cx8802_fini_common(dev); ++ cx88_core_put(dev->core,dev->pci); ++ kfree(dev); ++} ++ ++static const struct pci_device_id cx8802_pci_tbl[] = { ++ { ++ .vendor = 0x14f1, ++ .device = 0x8802, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ },{ ++ /* --- end of list --- */ ++ } ++}; ++MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); ++ ++static struct pci_driver cx8802_pci_driver = { ++ .name = "cx88-mpeg driver manager", ++ .id_table = cx8802_pci_tbl, ++ .probe = cx8802_probe, ++ .remove = __devexit_p(cx8802_remove), ++}; ++ ++static int __init cx8802_init(void) ++{ ++ printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n", ++ CX88_VERSION); ++ return pci_register_driver(&cx8802_pci_driver); ++} ++ ++static void __exit cx8802_fini(void) ++{ ++ pci_unregister_driver(&cx8802_pci_driver); ++} ++ ++module_init(cx8802_init); ++module_exit(cx8802_fini); ++EXPORT_SYMBOL(cx8802_buf_prepare); ++EXPORT_SYMBOL(cx8802_buf_queue); ++EXPORT_SYMBOL(cx8802_cancel_buffers); ++ ++EXPORT_SYMBOL(cx8802_register_driver); ++EXPORT_SYMBOL(cx8802_unregister_driver); ++EXPORT_SYMBOL(cx8802_get_driver); ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off ++ */ +diff --git a/drivers/media/pci/cx88/cx88-reg.h b/drivers/media/pci/cx88/cx88-reg.h +new file mode 100644 +index 0000000..2ec52d1 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-reg.h +@@ -0,0 +1,836 @@ ++/* ++ ++ cx88x-hw.h - CX2388x register offsets ++ ++ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ 2001 Michael Eskin ++ 2002 Yurij Sysoev ++ 2003 Gerd Knorr ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef _CX88_REG_H_ ++#define _CX88_REG_H_ ++ ++/* ---------------------------------------------------------------------- */ ++/* PCI IDs and config space */ ++ ++#ifndef PCI_VENDOR_ID_CONEXANT ++# define PCI_VENDOR_ID_CONEXANT 0x14F1 ++#endif ++#ifndef PCI_DEVICE_ID_CX2300_VID ++# define PCI_DEVICE_ID_CX2300_VID 0x8800 ++#endif ++ ++#define CX88X_DEVCTRL 0x40 ++#define CX88X_EN_TBFX 0x02 ++#define CX88X_EN_VSFX 0x04 ++ ++/* ---------------------------------------------------------------------- */ ++/* PCI controller registers */ ++ ++/* Command and Status Register */ ++#define F0_CMD_STAT_MM 0x2f0004 ++#define F1_CMD_STAT_MM 0x2f0104 ++#define F2_CMD_STAT_MM 0x2f0204 ++#define F3_CMD_STAT_MM 0x2f0304 ++#define F4_CMD_STAT_MM 0x2f0404 ++ ++/* Device Control #1 */ ++#define F0_DEV_CNTRL1_MM 0x2f0040 ++#define F1_DEV_CNTRL1_MM 0x2f0140 ++#define F2_DEV_CNTRL1_MM 0x2f0240 ++#define F3_DEV_CNTRL1_MM 0x2f0340 ++#define F4_DEV_CNTRL1_MM 0x2f0440 ++ ++/* Device Control #1 */ ++#define F0_BAR0_MM 0x2f0010 ++#define F1_BAR0_MM 0x2f0110 ++#define F2_BAR0_MM 0x2f0210 ++#define F3_BAR0_MM 0x2f0310 ++#define F4_BAR0_MM 0x2f0410 ++ ++/* ---------------------------------------------------------------------- */ ++/* DMA Controller registers */ ++ ++#define MO_PDMA_STHRSH 0x200000 // Source threshold ++#define MO_PDMA_STADRS 0x200004 // Source target address ++#define MO_PDMA_SIADRS 0x200008 // Source internal address ++#define MO_PDMA_SCNTRL 0x20000C // Source control ++#define MO_PDMA_DTHRSH 0x200010 // Destination threshold ++#define MO_PDMA_DTADRS 0x200014 // Destination target address ++#define MO_PDMA_DIADRS 0x200018 // Destination internal address ++#define MO_PDMA_DCNTRL 0x20001C // Destination control ++#define MO_LD_SSID 0x200030 // Load subsystem ID ++#define MO_DEV_CNTRL2 0x200034 // Device control ++#define MO_PCI_INTMSK 0x200040 // PCI interrupt mask ++#define MO_PCI_INTSTAT 0x200044 // PCI interrupt status ++#define MO_PCI_INTMSTAT 0x200048 // PCI interrupt masked status ++#define MO_VID_INTMSK 0x200050 // Video interrupt mask ++#define MO_VID_INTSTAT 0x200054 // Video interrupt status ++#define MO_VID_INTMSTAT 0x200058 // Video interrupt masked status ++#define MO_VID_INTSSTAT 0x20005C // Video interrupt set status ++#define MO_AUD_INTMSK 0x200060 // Audio interrupt mask ++#define MO_AUD_INTSTAT 0x200064 // Audio interrupt status ++#define MO_AUD_INTMSTAT 0x200068 // Audio interrupt masked status ++#define MO_AUD_INTSSTAT 0x20006C // Audio interrupt set status ++#define MO_TS_INTMSK 0x200070 // Transport stream interrupt mask ++#define MO_TS_INTSTAT 0x200074 // Transport stream interrupt status ++#define MO_TS_INTMSTAT 0x200078 // Transport stream interrupt mask status ++#define MO_TS_INTSSTAT 0x20007C // Transport stream interrupt set status ++#define MO_VIP_INTMSK 0x200080 // VIP interrupt mask ++#define MO_VIP_INTSTAT 0x200084 // VIP interrupt status ++#define MO_VIP_INTMSTAT 0x200088 // VIP interrupt masked status ++#define MO_VIP_INTSSTAT 0x20008C // VIP interrupt set status ++#define MO_GPHST_INTMSK 0x200090 // Host interrupt mask ++#define MO_GPHST_INTSTAT 0x200094 // Host interrupt status ++#define MO_GPHST_INTMSTAT 0x200098 // Host interrupt masked status ++#define MO_GPHST_INTSSTAT 0x20009C // Host interrupt set status ++ ++// DMA Channels 1-6 belong to SPIPE ++#define MO_DMA7_PTR1 0x300018 // {24}RW* DMA Current Ptr : Ch#7 ++#define MO_DMA8_PTR1 0x30001C // {24}RW* DMA Current Ptr : Ch#8 ++ ++// DMA Channels 9-20 belong to SPIPE ++#define MO_DMA21_PTR1 0x300080 // {24}R0* DMA Current Ptr : Ch#21 ++#define MO_DMA22_PTR1 0x300084 // {24}R0* DMA Current Ptr : Ch#22 ++#define MO_DMA23_PTR1 0x300088 // {24}R0* DMA Current Ptr : Ch#23 ++#define MO_DMA24_PTR1 0x30008C // {24}R0* DMA Current Ptr : Ch#24 ++#define MO_DMA25_PTR1 0x300090 // {24}R0* DMA Current Ptr : Ch#25 ++#define MO_DMA26_PTR1 0x300094 // {24}R0* DMA Current Ptr : Ch#26 ++#define MO_DMA27_PTR1 0x300098 // {24}R0* DMA Current Ptr : Ch#27 ++#define MO_DMA28_PTR1 0x30009C // {24}R0* DMA Current Ptr : Ch#28 ++#define MO_DMA29_PTR1 0x3000A0 // {24}R0* DMA Current Ptr : Ch#29 ++#define MO_DMA30_PTR1 0x3000A4 // {24}R0* DMA Current Ptr : Ch#30 ++#define MO_DMA31_PTR1 0x3000A8 // {24}R0* DMA Current Ptr : Ch#31 ++#define MO_DMA32_PTR1 0x3000AC // {24}R0* DMA Current Ptr : Ch#32 ++ ++#define MO_DMA21_PTR2 0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21 ++#define MO_DMA22_PTR2 0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22 ++#define MO_DMA23_PTR2 0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23 ++#define MO_DMA24_PTR2 0x3000CC // {24}RW* DMA Tab Ptr : Ch#24 ++#define MO_DMA25_PTR2 0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25 ++#define MO_DMA26_PTR2 0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26 ++#define MO_DMA27_PTR2 0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27 ++#define MO_DMA28_PTR2 0x3000DC // {24}RW* DMA Tab Ptr : Ch#28 ++#define MO_DMA29_PTR2 0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29 ++#define MO_DMA30_PTR2 0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30 ++#define MO_DMA31_PTR2 0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31 ++#define MO_DMA32_PTR2 0x3000EC // {24}RW* DMA Tab Ptr : Ch#32 ++ ++#define MO_DMA21_CNT1 0x300100 // {11}RW* DMA Buffer Size : Ch#21 ++#define MO_DMA22_CNT1 0x300104 // {11}RW* DMA Buffer Size : Ch#22 ++#define MO_DMA23_CNT1 0x300108 // {11}RW* DMA Buffer Size : Ch#23 ++#define MO_DMA24_CNT1 0x30010C // {11}RW* DMA Buffer Size : Ch#24 ++#define MO_DMA25_CNT1 0x300110 // {11}RW* DMA Buffer Size : Ch#25 ++#define MO_DMA26_CNT1 0x300114 // {11}RW* DMA Buffer Size : Ch#26 ++#define MO_DMA27_CNT1 0x300118 // {11}RW* DMA Buffer Size : Ch#27 ++#define MO_DMA28_CNT1 0x30011C // {11}RW* DMA Buffer Size : Ch#28 ++#define MO_DMA29_CNT1 0x300120 // {11}RW* DMA Buffer Size : Ch#29 ++#define MO_DMA30_CNT1 0x300124 // {11}RW* DMA Buffer Size : Ch#30 ++#define MO_DMA31_CNT1 0x300128 // {11}RW* DMA Buffer Size : Ch#31 ++#define MO_DMA32_CNT1 0x30012C // {11}RW* DMA Buffer Size : Ch#32 ++ ++#define MO_DMA21_CNT2 0x300140 // {11}RW* DMA Table Size : Ch#21 ++#define MO_DMA22_CNT2 0x300144 // {11}RW* DMA Table Size : Ch#22 ++#define MO_DMA23_CNT2 0x300148 // {11}RW* DMA Table Size : Ch#23 ++#define MO_DMA24_CNT2 0x30014C // {11}RW* DMA Table Size : Ch#24 ++#define MO_DMA25_CNT2 0x300150 // {11}RW* DMA Table Size : Ch#25 ++#define MO_DMA26_CNT2 0x300154 // {11}RW* DMA Table Size : Ch#26 ++#define MO_DMA27_CNT2 0x300158 // {11}RW* DMA Table Size : Ch#27 ++#define MO_DMA28_CNT2 0x30015C // {11}RW* DMA Table Size : Ch#28 ++#define MO_DMA29_CNT2 0x300160 // {11}RW* DMA Table Size : Ch#29 ++#define MO_DMA30_CNT2 0x300164 // {11}RW* DMA Table Size : Ch#30 ++#define MO_DMA31_CNT2 0x300168 // {11}RW* DMA Table Size : Ch#31 ++#define MO_DMA32_CNT2 0x30016C // {11}RW* DMA Table Size : Ch#32 ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* Video registers */ ++ ++#define MO_VIDY_DMA 0x310000 // {64}RWp Video Y ++#define MO_VIDU_DMA 0x310008 // {64}RWp Video U ++#define MO_VIDV_DMA 0x310010 // {64}RWp Video V ++#define MO_VBI_DMA 0x310018 // {64}RWp VBI (Vertical blanking interval) ++ ++#define MO_DEVICE_STATUS 0x310100 ++#define MO_INPUT_FORMAT 0x310104 ++#define MO_AGC_BURST 0x31010c ++#define MO_CONTR_BRIGHT 0x310110 ++#define MO_UV_SATURATION 0x310114 ++#define MO_HUE 0x310118 ++#define MO_HTOTAL 0x310120 ++#define MO_HDELAY_EVEN 0x310124 ++#define MO_HDELAY_ODD 0x310128 ++#define MO_VDELAY_ODD 0x31012c ++#define MO_VDELAY_EVEN 0x310130 ++#define MO_HACTIVE_EVEN 0x31013c ++#define MO_HACTIVE_ODD 0x310140 ++#define MO_VACTIVE_EVEN 0x310144 ++#define MO_VACTIVE_ODD 0x310148 ++#define MO_HSCALE_EVEN 0x31014c ++#define MO_HSCALE_ODD 0x310150 ++#define MO_VSCALE_EVEN 0x310154 ++#define MO_FILTER_EVEN 0x31015c ++#define MO_VSCALE_ODD 0x310158 ++#define MO_FILTER_ODD 0x310160 ++#define MO_OUTPUT_FORMAT 0x310164 ++ ++#define MO_PLL_REG 0x310168 // PLL register ++#define MO_PLL_ADJ_CTRL 0x31016c // PLL adjust control register ++#define MO_SCONV_REG 0x310170 // sample rate conversion register ++#define MO_SCONV_FIFO 0x310174 // sample rate conversion fifo ++#define MO_SUB_STEP 0x310178 // subcarrier step size ++#define MO_SUB_STEP_DR 0x31017c // subcarrier step size for DR line ++ ++#define MO_CAPTURE_CTRL 0x310180 // capture control ++#define MO_COLOR_CTRL 0x310184 ++#define MO_VBI_PACKET 0x310188 // vbi packet size / delay ++#define MO_FIELD_COUNT 0x310190 // field counter ++#define MO_VIP_CONFIG 0x310194 ++#define MO_VBOS_CONTROL 0x3101a8 ++ ++#define MO_AGC_BACK_VBI 0x310200 ++#define MO_AGC_SYNC_TIP1 0x310208 ++ ++#define MO_VIDY_GPCNT 0x31C020 // {16}RO Video Y general purpose counter ++#define MO_VIDU_GPCNT 0x31C024 // {16}RO Video U general purpose counter ++#define MO_VIDV_GPCNT 0x31C028 // {16}RO Video V general purpose counter ++#define MO_VBI_GPCNT 0x31C02C // {16}RO VBI general purpose counter ++#define MO_VIDY_GPCNTRL 0x31C030 // {2}WO Video Y general purpose control ++#define MO_VIDU_GPCNTRL 0x31C034 // {2}WO Video U general purpose control ++#define MO_VIDV_GPCNTRL 0x31C038 // {2}WO Video V general purpose control ++#define MO_VBI_GPCNTRL 0x31C03C // {2}WO VBI general purpose counter ++#define MO_VID_DMACNTRL 0x31C040 // {8}RW Video DMA control ++#define MO_VID_XFR_STAT 0x31C044 // {1}RO Video transfer status ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* audio registers */ ++ ++#define MO_AUDD_DMA 0x320000 // {64}RWp Audio downstream ++#define MO_AUDU_DMA 0x320008 // {64}RWp Audio upstream ++#define MO_AUDR_DMA 0x320010 // {64}RWp Audio RDS (downstream) ++#define MO_AUDD_GPCNT 0x32C020 // {16}RO Audio down general purpose counter ++#define MO_AUDU_GPCNT 0x32C024 // {16}RO Audio up general purpose counter ++#define MO_AUDR_GPCNT 0x32C028 // {16}RO Audio RDS general purpose counter ++#define MO_AUDD_GPCNTRL 0x32C030 // {2}WO Audio down general purpose control ++#define MO_AUDU_GPCNTRL 0x32C034 // {2}WO Audio up general purpose control ++#define MO_AUDR_GPCNTRL 0x32C038 // {2}WO Audio RDS general purpose control ++#define MO_AUD_DMACNTRL 0x32C040 // {6}RW Audio DMA control ++#define MO_AUD_XFR_STAT 0x32C044 // {1}RO Audio transfer status ++#define MO_AUDD_LNGTH 0x32C048 // {12}RW Audio down line length ++#define MO_AUDR_LNGTH 0x32C04C // {12}RW Audio RDS line length ++ ++#define AUD_INIT 0x320100 ++#define AUD_INIT_LD 0x320104 ++#define AUD_SOFT_RESET 0x320108 ++#define AUD_I2SINPUTCNTL 0x320120 ++#define AUD_BAUDRATE 0x320124 ++#define AUD_I2SOUTPUTCNTL 0x320128 ++#define AAGC_HYST 0x320134 ++#define AAGC_GAIN 0x320138 ++#define AAGC_DEF 0x32013c ++#define AUD_IIR1_0_SEL 0x320150 ++#define AUD_IIR1_0_SHIFT 0x320154 ++#define AUD_IIR1_1_SEL 0x320158 ++#define AUD_IIR1_1_SHIFT 0x32015c ++#define AUD_IIR1_2_SEL 0x320160 ++#define AUD_IIR1_2_SHIFT 0x320164 ++#define AUD_IIR1_3_SEL 0x320168 ++#define AUD_IIR1_3_SHIFT 0x32016c ++#define AUD_IIR1_4_SEL 0x320170 ++#define AUD_IIR1_4_SHIFT 0x32017c ++#define AUD_IIR1_5_SEL 0x320180 ++#define AUD_IIR1_5_SHIFT 0x320184 ++#define AUD_IIR2_0_SEL 0x320190 ++#define AUD_IIR2_0_SHIFT 0x320194 ++#define AUD_IIR2_1_SEL 0x320198 ++#define AUD_IIR2_1_SHIFT 0x32019c ++#define AUD_IIR2_2_SEL 0x3201a0 ++#define AUD_IIR2_2_SHIFT 0x3201a4 ++#define AUD_IIR2_3_SEL 0x3201a8 ++#define AUD_IIR2_3_SHIFT 0x3201ac ++#define AUD_IIR3_0_SEL 0x3201c0 ++#define AUD_IIR3_0_SHIFT 0x3201c4 ++#define AUD_IIR3_1_SEL 0x3201c8 ++#define AUD_IIR3_1_SHIFT 0x3201cc ++#define AUD_IIR3_2_SEL 0x3201d0 ++#define AUD_IIR3_2_SHIFT 0x3201d4 ++#define AUD_IIR4_0_SEL 0x3201e0 ++#define AUD_IIR4_0_SHIFT 0x3201e4 ++#define AUD_IIR4_1_SEL 0x3201e8 ++#define AUD_IIR4_1_SHIFT 0x3201ec ++#define AUD_IIR4_2_SEL 0x3201f0 ++#define AUD_IIR4_2_SHIFT 0x3201f4 ++#define AUD_IIR4_0_CA0 0x320200 ++#define AUD_IIR4_0_CA1 0x320204 ++#define AUD_IIR4_0_CA2 0x320208 ++#define AUD_IIR4_0_CB0 0x32020c ++#define AUD_IIR4_0_CB1 0x320210 ++#define AUD_IIR4_1_CA0 0x320214 ++#define AUD_IIR4_1_CA1 0x320218 ++#define AUD_IIR4_1_CA2 0x32021c ++#define AUD_IIR4_1_CB0 0x320220 ++#define AUD_IIR4_1_CB1 0x320224 ++#define AUD_IIR4_2_CA0 0x320228 ++#define AUD_IIR4_2_CA1 0x32022c ++#define AUD_IIR4_2_CA2 0x320230 ++#define AUD_IIR4_2_CB0 0x320234 ++#define AUD_IIR4_2_CB1 0x320238 ++#define AUD_HP_MD_IIR4_1 0x320250 ++#define AUD_HP_PROG_IIR4_1 0x320254 ++#define AUD_FM_MODE_ENABLE 0x320258 ++#define AUD_POLY0_DDS_CONSTANT 0x320270 ++#define AUD_DN0_FREQ 0x320274 ++#define AUD_DN1_FREQ 0x320278 ++#define AUD_DN1_FREQ_SHIFT 0x32027c ++#define AUD_DN1_AFC 0x320280 ++#define AUD_DN1_SRC_SEL 0x320284 ++#define AUD_DN1_SHFT 0x320288 ++#define AUD_DN2_FREQ 0x32028c ++#define AUD_DN2_FREQ_SHIFT 0x320290 ++#define AUD_DN2_AFC 0x320294 ++#define AUD_DN2_SRC_SEL 0x320298 ++#define AUD_DN2_SHFT 0x32029c ++#define AUD_CRDC0_SRC_SEL 0x320300 ++#define AUD_CRDC0_SHIFT 0x320304 ++#define AUD_CORDIC_SHIFT_0 0x320308 ++#define AUD_CRDC1_SRC_SEL 0x32030c ++#define AUD_CRDC1_SHIFT 0x320310 ++#define AUD_CORDIC_SHIFT_1 0x320314 ++#define AUD_DCOC_0_SRC 0x320320 ++#define AUD_DCOC0_SHIFT 0x320324 ++#define AUD_DCOC_0_SHIFT_IN0 0x320328 ++#define AUD_DCOC_0_SHIFT_IN1 0x32032c ++#define AUD_DCOC_1_SRC 0x320330 ++#define AUD_DCOC1_SHIFT 0x320334 ++#define AUD_DCOC_1_SHIFT_IN0 0x320338 ++#define AUD_DCOC_1_SHIFT_IN1 0x32033c ++#define AUD_DCOC_2_SRC 0x320340 ++#define AUD_DCOC2_SHIFT 0x320344 ++#define AUD_DCOC_2_SHIFT_IN0 0x320348 ++#define AUD_DCOC_2_SHIFT_IN1 0x32034c ++#define AUD_DCOC_PASS_IN 0x320350 ++#define AUD_PDET_SRC 0x320370 ++#define AUD_PDET_SHIFT 0x320374 ++#define AUD_PILOT_BQD_1_K0 0x320380 ++#define AUD_PILOT_BQD_1_K1 0x320384 ++#define AUD_PILOT_BQD_1_K2 0x320388 ++#define AUD_PILOT_BQD_1_K3 0x32038c ++#define AUD_PILOT_BQD_1_K4 0x320390 ++#define AUD_PILOT_BQD_2_K0 0x320394 ++#define AUD_PILOT_BQD_2_K1 0x320398 ++#define AUD_PILOT_BQD_2_K2 0x32039c ++#define AUD_PILOT_BQD_2_K3 0x3203a0 ++#define AUD_PILOT_BQD_2_K4 0x3203a4 ++#define AUD_THR_FR 0x3203c0 ++#define AUD_X_PROG 0x3203c4 ++#define AUD_Y_PROG 0x3203c8 ++#define AUD_HARMONIC_MULT 0x3203cc ++#define AUD_C1_UP_THR 0x3203d0 ++#define AUD_C1_LO_THR 0x3203d4 ++#define AUD_C2_UP_THR 0x3203d8 ++#define AUD_C2_LO_THR 0x3203dc ++#define AUD_PLL_EN 0x320400 ++#define AUD_PLL_SRC 0x320404 ++#define AUD_PLL_SHIFT 0x320408 ++#define AUD_PLL_IF_SEL 0x32040c ++#define AUD_PLL_IF_SHIFT 0x320410 ++#define AUD_BIQUAD_PLL_K0 0x320414 ++#define AUD_BIQUAD_PLL_K1 0x320418 ++#define AUD_BIQUAD_PLL_K2 0x32041c ++#define AUD_BIQUAD_PLL_K3 0x320420 ++#define AUD_BIQUAD_PLL_K4 0x320424 ++#define AUD_DEEMPH0_SRC_SEL 0x320440 ++#define AUD_DEEMPH0_SHIFT 0x320444 ++#define AUD_DEEMPH0_G0 0x320448 ++#define AUD_DEEMPH0_A0 0x32044c ++#define AUD_DEEMPH0_B0 0x320450 ++#define AUD_DEEMPH0_A1 0x320454 ++#define AUD_DEEMPH0_B1 0x320458 ++#define AUD_DEEMPH1_SRC_SEL 0x32045c ++#define AUD_DEEMPH1_SHIFT 0x320460 ++#define AUD_DEEMPH1_G0 0x320464 ++#define AUD_DEEMPH1_A0 0x320468 ++#define AUD_DEEMPH1_B0 0x32046c ++#define AUD_DEEMPH1_A1 0x320470 ++#define AUD_DEEMPH1_B1 0x320474 ++#define AUD_OUT0_SEL 0x320490 ++#define AUD_OUT0_SHIFT 0x320494 ++#define AUD_OUT1_SEL 0x320498 ++#define AUD_OUT1_SHIFT 0x32049c ++#define AUD_RDSI_SEL 0x3204a0 ++#define AUD_RDSI_SHIFT 0x3204a4 ++#define AUD_RDSQ_SEL 0x3204a8 ++#define AUD_RDSQ_SHIFT 0x3204ac ++#define AUD_DBX_IN_GAIN 0x320500 ++#define AUD_DBX_WBE_GAIN 0x320504 ++#define AUD_DBX_SE_GAIN 0x320508 ++#define AUD_DBX_RMS_WBE 0x32050c ++#define AUD_DBX_RMS_SE 0x320510 ++#define AUD_DBX_SE_BYPASS 0x320514 ++#define AUD_FAWDETCTL 0x320530 ++#define AUD_FAWDETWINCTL 0x320534 ++#define AUD_DEEMPHGAIN_R 0x320538 ++#define AUD_DEEMPHNUMER1_R 0x32053c ++#define AUD_DEEMPHNUMER2_R 0x320540 ++#define AUD_DEEMPHDENOM1_R 0x320544 ++#define AUD_DEEMPHDENOM2_R 0x320548 ++#define AUD_ERRLOGPERIOD_R 0x32054c ++#define AUD_ERRINTRPTTHSHLD1_R 0x320550 ++#define AUD_ERRINTRPTTHSHLD2_R 0x320554 ++#define AUD_ERRINTRPTTHSHLD3_R 0x320558 ++#define AUD_NICAM_STATUS1 0x32055c ++#define AUD_NICAM_STATUS2 0x320560 ++#define AUD_ERRLOG1 0x320564 ++#define AUD_ERRLOG2 0x320568 ++#define AUD_ERRLOG3 0x32056c ++#define AUD_DAC_BYPASS_L 0x320580 ++#define AUD_DAC_BYPASS_R 0x320584 ++#define AUD_DAC_BYPASS_CTL 0x320588 ++#define AUD_CTL 0x32058c ++#define AUD_STATUS 0x320590 ++#define AUD_VOL_CTL 0x320594 ++#define AUD_BAL_CTL 0x320598 ++#define AUD_START_TIMER 0x3205b0 ++#define AUD_MODE_CHG_TIMER 0x3205b4 ++#define AUD_POLYPH80SCALEFAC 0x3205b8 ++#define AUD_DMD_RA_DDS 0x3205bc ++#define AUD_I2S_RA_DDS 0x3205c0 ++#define AUD_RATE_THRES_DMD 0x3205d0 ++#define AUD_RATE_THRES_I2S 0x3205d4 ++#define AUD_RATE_ADJ1 0x3205d8 ++#define AUD_RATE_ADJ2 0x3205dc ++#define AUD_RATE_ADJ3 0x3205e0 ++#define AUD_RATE_ADJ4 0x3205e4 ++#define AUD_RATE_ADJ5 0x3205e8 ++#define AUD_APB_IN_RATE_ADJ 0x3205ec ++#define AUD_I2SCNTL 0x3205ec ++#define AUD_PHASE_FIX_CTL 0x3205f0 ++#define AUD_PLL_PRESCALE 0x320600 ++#define AUD_PLL_DDS 0x320604 ++#define AUD_PLL_INT 0x320608 ++#define AUD_PLL_FRAC 0x32060c ++#define AUD_PLL_JTAG 0x320620 ++#define AUD_PLL_SPMP 0x320624 ++#define AUD_AFE_12DB_EN 0x320628 ++ ++// Audio QAM Register Addresses ++#define AUD_PDF_DDS_CNST_BYTE2 0x320d01 ++#define AUD_PDF_DDS_CNST_BYTE1 0x320d02 ++#define AUD_PDF_DDS_CNST_BYTE0 0x320d03 ++#define AUD_PHACC_FREQ_8MSB 0x320d2a ++#define AUD_PHACC_FREQ_8LSB 0x320d2b ++#define AUD_QAM_MODE 0x320d04 ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* transport stream registers */ ++ ++#define MO_TS_DMA 0x330000 // {64}RWp Transport stream downstream ++#define MO_TS_GPCNT 0x33C020 // {16}RO TS general purpose counter ++#define MO_TS_GPCNTRL 0x33C030 // {2}WO TS general purpose control ++#define MO_TS_DMACNTRL 0x33C040 // {6}RW TS DMA control ++#define MO_TS_XFR_STAT 0x33C044 // {1}RO TS transfer status ++#define MO_TS_LNGTH 0x33C048 // {12}RW TS line length ++ ++#define TS_HW_SOP_CNTRL 0x33C04C ++#define TS_GEN_CNTRL 0x33C050 ++#define TS_BD_PKT_STAT 0x33C054 ++#define TS_SOP_STAT 0x33C058 ++#define TS_FIFO_OVFL_STAT 0x33C05C ++#define TS_VALERR_CNTRL 0x33C060 ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* VIP registers */ ++ ++#define MO_VIPD_DMA 0x340000 // {64}RWp VIP downstream ++#define MO_VIPU_DMA 0x340008 // {64}RWp VIP upstream ++#define MO_VIPD_GPCNT 0x34C020 // {16}RO VIP down general purpose counter ++#define MO_VIPU_GPCNT 0x34C024 // {16}RO VIP up general purpose counter ++#define MO_VIPD_GPCNTRL 0x34C030 // {2}WO VIP down general purpose control ++#define MO_VIPU_GPCNTRL 0x34C034 // {2}WO VIP up general purpose control ++#define MO_VIP_DMACNTRL 0x34C040 // {6}RW VIP DMA control ++#define MO_VIP_XFR_STAT 0x34C044 // {1}RO VIP transfer status ++#define MO_VIP_CFG 0x340048 // VIP configuration ++#define MO_VIPU_CNTRL 0x34004C // VIP upstream control #1 ++#define MO_VIPD_CNTRL 0x340050 // VIP downstream control #2 ++#define MO_VIPD_LNGTH 0x340054 // VIP downstream line length ++#define MO_VIP_BRSTLN 0x340058 // VIP burst length ++#define MO_VIP_INTCNTRL 0x34C05C // VIP Interrupt Control ++#define MO_VIP_XFTERM 0x340060 // VIP transfer terminate ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* misc registers */ ++ ++#define MO_M2M_DMA 0x350000 // {64}RWp Mem2Mem DMA Bfr ++#define MO_GP0_IO 0x350010 // {32}RW* GPIOoutput enablesdata I/O ++#define MO_GP1_IO 0x350014 // {32}RW* GPIOoutput enablesdata I/O ++#define MO_GP2_IO 0x350018 // {32}RW* GPIOoutput enablesdata I/O ++#define MO_GP3_IO 0x35001C // {32}RW* GPIO Mode/Ctrloutput enables ++#define MO_GPIO 0x350020 // {32}RW* GPIO I2C Ctrldata I/O ++#define MO_GPOE 0x350024 // {32}RW GPIO I2C Ctrloutput enables ++#define MO_GP_ISM 0x350028 // {16}WO GPIO Intr Sens/Pol ++ ++#define MO_PLL_B 0x35C008 // {32}RW* PLL Control for ASB bus clks ++#define MO_M2M_CNT 0x35C024 // {32}RW Mem2Mem DMA Cnt ++#define MO_M2M_XSUM 0x35C028 // {32}RO M2M XOR-Checksum ++#define MO_CRC 0x35C02C // {16}RW CRC16 init/result ++#define MO_CRC_D 0x35C030 // {32}WO CRC16 new data in ++#define MO_TM_CNT_LDW 0x35C034 // {32}RO Timer : Counter low dword ++#define MO_TM_CNT_UW 0x35C038 // {16}RO Timer : Counter high word ++#define MO_TM_LMT_LDW 0x35C03C // {32}RW Timer : Limit low dword ++#define MO_TM_LMT_UW 0x35C040 // {32}RW Timer : Limit high word ++#define MO_PINMUX_IO 0x35C044 // {8}RW Pin Mux Control ++#define MO_TSTSEL_IO 0x35C048 // {2}RW Pin Mux Control ++#define MO_AFECFG_IO 0x35C04C // AFE configuration reg ++#define MO_DDS_IO 0x35C050 // DDS Increment reg ++#define MO_DDSCFG_IO 0x35C054 // DDS Configuration reg ++#define MO_SAMPLE_IO 0x35C058 // IRIn sample reg ++#define MO_SRST_IO 0x35C05C // Output system reset reg ++ ++#define MO_INT1_MSK 0x35C060 // DMA RISC interrupt mask ++#define MO_INT1_STAT 0x35C064 // DMA RISC interrupt status ++#define MO_INT1_MSTAT 0x35C068 // DMA RISC interrupt masked status ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* i2c bus registers */ ++ ++#define MO_I2C 0x368000 // I2C data/control ++#define MO_I2C_DIV (0xf<<4) ++#define MO_I2C_SYNC (1<<3) ++#define MO_I2C_W3B (1<<2) ++#define MO_I2C_SCL (1<<1) ++#define MO_I2C_SDA (1<<0) ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* general purpose host registers */ ++/* FIXME: tyops? s/0x35/0x38/ ?? */ ++ ++#define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream ++#define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream ++#define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1 ++#define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2 ++#define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length ++#define MO_GPHST_WSC 0x380054 // Host wait state control ++#define MO_GPHST_XFR 0x380058 // Host transfer control ++#define MO_GPHST_WDTH 0x38005C // Host interface width ++#define MO_GPHST_HDSHK 0x380060 // Host peripheral handshake ++#define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters ++#define MO_GPHST_MODE 0x380068 // Host mode select ++ ++#define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter ++#define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter ++#define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control ++#define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control ++#define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control ++#define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status ++#define MO_GPHST_SOFT_RST 0x38C06C // Host software reset ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* RISC instructions */ ++ ++#define RISC_SYNC 0x80000000 ++#define RISC_SYNC_ODD 0x80000000 ++#define RISC_SYNC_EVEN 0x80000200 ++#define RISC_RESYNC 0x80008000 ++#define RISC_RESYNC_ODD 0x80008000 ++#define RISC_RESYNC_EVEN 0x80008200 ++#define RISC_WRITE 0x10000000 ++#define RISC_WRITEC 0x50000000 ++#define RISC_READ 0x90000000 ++#define RISC_READC 0xA0000000 ++#define RISC_JUMP 0x70000000 ++#define RISC_SKIP 0x20000000 ++#define RISC_WRITERM 0xB0000000 ++#define RISC_WRITECM 0xC0000000 ++#define RISC_WRITECR 0xD0000000 ++#define RISC_IMM 0x00000001 ++ ++#define RISC_SOL 0x08000000 ++#define RISC_EOL 0x04000000 ++ ++#define RISC_IRQ2 0x02000000 ++#define RISC_IRQ1 0x01000000 ++ ++#define RISC_CNT_NONE 0x00000000 ++#define RISC_CNT_INC 0x00010000 ++#define RISC_CNT_RSVR 0x00020000 ++#define RISC_CNT_RESET 0x00030000 ++#define RISC_JMP_SRP 0x01 ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* various constants */ ++ ++// DMA ++/* Interrupt mask/status */ ++#define PCI_INT_VIDINT (1 << 0) ++#define PCI_INT_AUDINT (1 << 1) ++#define PCI_INT_TSINT (1 << 2) ++#define PCI_INT_VIPINT (1 << 3) ++#define PCI_INT_HSTINT (1 << 4) ++#define PCI_INT_TM1INT (1 << 5) ++#define PCI_INT_SRCDMAINT (1 << 6) ++#define PCI_INT_DSTDMAINT (1 << 7) ++#define PCI_INT_RISC_RD_BERRINT (1 << 10) ++#define PCI_INT_RISC_WR_BERRINT (1 << 11) ++#define PCI_INT_BRDG_BERRINT (1 << 12) ++#define PCI_INT_SRC_DMA_BERRINT (1 << 13) ++#define PCI_INT_DST_DMA_BERRINT (1 << 14) ++#define PCI_INT_IPB_DMA_BERRINT (1 << 15) ++#define PCI_INT_I2CDONE (1 << 16) ++#define PCI_INT_I2CRACK (1 << 17) ++#define PCI_INT_IR_SMPINT (1 << 18) ++#define PCI_INT_GPIO_INT0 (1 << 19) ++#define PCI_INT_GPIO_INT1 (1 << 20) ++ ++#define SEL_BTSC 0x01 ++#define SEL_EIAJ 0x02 ++#define SEL_A2 0x04 ++#define SEL_SAP 0x08 ++#define SEL_NICAM 0x10 ++#define SEL_FMRADIO 0x20 ++ ++// AUD_CTL ++#define AUD_INT_DN_RISCI1 (1 << 0) ++#define AUD_INT_UP_RISCI1 (1 << 1) ++#define AUD_INT_RDS_DN_RISCI1 (1 << 2) ++#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ ++#define AUD_INT_UP_RISCI2 (1 << 5) ++#define AUD_INT_RDS_DN_RISCI2 (1 << 6) ++#define AUD_INT_DN_SYNC (1 << 12) ++#define AUD_INT_UP_SYNC (1 << 13) ++#define AUD_INT_RDS_DN_SYNC (1 << 14) ++#define AUD_INT_OPC_ERR (1 << 16) ++#define AUD_INT_BER_IRQ (1 << 20) ++#define AUD_INT_MCHG_IRQ (1 << 21) ++ ++#define EN_BTSC_FORCE_MONO 0 ++#define EN_BTSC_FORCE_STEREO 1 ++#define EN_BTSC_FORCE_SAP 2 ++#define EN_BTSC_AUTO_STEREO 3 ++#define EN_BTSC_AUTO_SAP 4 ++ ++#define EN_A2_FORCE_MONO1 8 ++#define EN_A2_FORCE_MONO2 9 ++#define EN_A2_FORCE_STEREO 10 ++#define EN_A2_AUTO_MONO2 11 ++#define EN_A2_AUTO_STEREO 12 ++ ++#define EN_EIAJ_FORCE_MONO1 16 ++#define EN_EIAJ_FORCE_MONO2 17 ++#define EN_EIAJ_FORCE_STEREO 18 ++#define EN_EIAJ_AUTO_MONO2 19 ++#define EN_EIAJ_AUTO_STEREO 20 ++ ++#define EN_NICAM_FORCE_MONO1 32 ++#define EN_NICAM_FORCE_MONO2 33 ++#define EN_NICAM_FORCE_STEREO 34 ++#define EN_NICAM_AUTO_MONO2 35 ++#define EN_NICAM_AUTO_STEREO 36 ++ ++#define EN_FMRADIO_FORCE_MONO 24 ++#define EN_FMRADIO_FORCE_STEREO 25 ++#define EN_FMRADIO_AUTO_STEREO 26 ++ ++#define EN_NICAM_AUTO_FALLBACK 0x00000040 ++#define EN_FMRADIO_EN_RDS 0x00000200 ++#define EN_NICAM_TRY_AGAIN_BIT 0x00000400 ++#define EN_DAC_ENABLE 0x00001000 ++#define EN_I2SOUT_ENABLE 0x00002000 ++#define EN_I2SIN_STR2DAC 0x00004000 ++#define EN_I2SIN_ENABLE 0x00008000 ++ ++#define EN_DMTRX_SUMDIFF (0 << 7) ++#define EN_DMTRX_SUMR (1 << 7) ++#define EN_DMTRX_LR (2 << 7) ++#define EN_DMTRX_MONO (3 << 7) ++#define EN_DMTRX_BYPASS (1 << 11) ++ ++// Video ++#define VID_CAPTURE_CONTROL 0x310180 ++ ++#define CX23880_CAP_CTL_CAPTURE_VBI_ODD (1<<3) ++#define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2) ++#define CX23880_CAP_CTL_CAPTURE_ODD (1<<1) ++#define CX23880_CAP_CTL_CAPTURE_EVEN (1<<0) ++ ++#define VideoInputMux0 0x0 ++#define VideoInputMux1 0x1 ++#define VideoInputMux2 0x2 ++#define VideoInputMux3 0x3 ++#define VideoInputTuner 0x0 ++#define VideoInputComposite 0x1 ++#define VideoInputSVideo 0x2 ++#define VideoInputOther 0x3 ++ ++#define Xtal0 0x1 ++#define Xtal1 0x2 ++#define XtalAuto 0x3 ++ ++#define VideoFormatAuto 0x0 ++#define VideoFormatNTSC 0x1 ++#define VideoFormatNTSCJapan 0x2 ++#define VideoFormatNTSC443 0x3 ++#define VideoFormatPAL 0x4 ++#define VideoFormatPALB 0x4 ++#define VideoFormatPALD 0x4 ++#define VideoFormatPALG 0x4 ++#define VideoFormatPALH 0x4 ++#define VideoFormatPALI 0x4 ++#define VideoFormatPALBDGHI 0x4 ++#define VideoFormatPALM 0x5 ++#define VideoFormatPALN 0x6 ++#define VideoFormatPALNC 0x7 ++#define VideoFormatPAL60 0x8 ++#define VideoFormatSECAM 0x9 ++ ++#define VideoFormatAuto27MHz 0x10 ++#define VideoFormatNTSC27MHz 0x11 ++#define VideoFormatNTSCJapan27MHz 0x12 ++#define VideoFormatNTSC44327MHz 0x13 ++#define VideoFormatPAL27MHz 0x14 ++#define VideoFormatPALB27MHz 0x14 ++#define VideoFormatPALD27MHz 0x14 ++#define VideoFormatPALG27MHz 0x14 ++#define VideoFormatPALH27MHz 0x14 ++#define VideoFormatPALI27MHz 0x14 ++#define VideoFormatPALBDGHI27MHz 0x14 ++#define VideoFormatPALM27MHz 0x15 ++#define VideoFormatPALN27MHz 0x16 ++#define VideoFormatPALNC27MHz 0x17 ++#define VideoFormatPAL6027MHz 0x18 ++#define VideoFormatSECAM27MHz 0x19 ++ ++#define NominalUSECAM 0x87 ++#define NominalVSECAM 0x85 ++#define NominalUNTSC 0xFE ++#define NominalVNTSC 0xB4 ++ ++#define NominalContrast 0xD8 ++ ++#define HFilterAutoFormat 0x0 ++#define HFilterCIF 0x1 ++#define HFilterQCIF 0x2 ++#define HFilterICON 0x3 ++ ++#define VFilter2TapInterpolate 0 ++#define VFilter3TapInterpolate 1 ++#define VFilter4TapInterpolate 2 ++#define VFilter5TapInterpolate 3 ++#define VFilter2TapNoInterpolate 4 ++#define VFilter3TapNoInterpolate 5 ++#define VFilter4TapNoInterpolate 6 ++#define VFilter5TapNoInterpolate 7 ++ ++#define ColorFormatRGB32 0x0000 ++#define ColorFormatRGB24 0x0011 ++#define ColorFormatRGB16 0x0022 ++#define ColorFormatRGB15 0x0033 ++#define ColorFormatYUY2 0x0044 ++#define ColorFormatBTYUV 0x0055 ++#define ColorFormatY8 0x0066 ++#define ColorFormatRGB8 0x0077 ++#define ColorFormatPL422 0x0088 ++#define ColorFormatPL411 0x0099 ++#define ColorFormatYUV12 0x00AA ++#define ColorFormatYUV9 0x00BB ++#define ColorFormatRAW 0x00EE ++#define ColorFormatBSWAP 0x0300 ++#define ColorFormatWSWAP 0x0c00 ++#define ColorFormatEvenMask 0x050f ++#define ColorFormatOddMask 0x0af0 ++#define ColorFormatGamma 0x1000 ++ ++#define Interlaced 0x1 ++#define NonInterlaced 0x0 ++ ++#define FieldEven 0x1 ++#define FieldOdd 0x0 ++ ++#define TGReadWriteMode 0x0 ++#define TGEnableMode 0x1 ++ ++#define DV_CbAlign 0x0 ++#define DV_Y0Align 0x1 ++#define DV_CrAlign 0x2 ++#define DV_Y1Align 0x3 ++ ++#define DVF_Analog 0x0 ++#define DVF_CCIR656 0x1 ++#define DVF_ByteStream 0x2 ++#define DVF_ExtVSYNC 0x4 ++#define DVF_ExtField 0x5 ++ ++#define CHANNEL_VID_Y 0x1 ++#define CHANNEL_VID_U 0x2 ++#define CHANNEL_VID_V 0x3 ++#define CHANNEL_VID_VBI 0x4 ++#define CHANNEL_AUD_DN 0x5 ++#define CHANNEL_AUD_UP 0x6 ++#define CHANNEL_AUD_RDS_DN 0x7 ++#define CHANNEL_MPEG_DN 0x8 ++#define CHANNEL_VIP_DN 0x9 ++#define CHANNEL_VIP_UP 0xA ++#define CHANNEL_HOST_DN 0xB ++#define CHANNEL_HOST_UP 0xC ++#define CHANNEL_FIRST 0x1 ++#define CHANNEL_LAST 0xC ++ ++#define GP_COUNT_CONTROL_NONE 0x0 ++#define GP_COUNT_CONTROL_INC 0x1 ++#define GP_COUNT_CONTROL_RESERVED 0x2 ++#define GP_COUNT_CONTROL_RESET 0x3 ++ ++#define PLL_PRESCALE_BY_2 2 ++#define PLL_PRESCALE_BY_3 3 ++#define PLL_PRESCALE_BY_4 4 ++#define PLL_PRESCALE_BY_5 5 ++ ++#define HLNotchFilter4xFsc 0 ++#define HLNotchFilterSquare 1 ++#define HLNotchFilter135NTSC 2 ++#define HLNotchFilter135PAL 3 ++ ++#define NTSC_8x_SUB_CARRIER 28.63636E6 ++#define PAL_8x_SUB_CARRIER 35.46895E6 ++ ++// Default analog settings ++#define DEFAULT_HUE_NTSC 0x00 ++#define DEFAULT_BRIGHTNESS_NTSC 0x00 ++#define DEFAULT_CONTRAST_NTSC 0x39 ++#define DEFAULT_SAT_U_NTSC 0x7F ++#define DEFAULT_SAT_V_NTSC 0x5A ++ ++typedef enum ++{ ++ SOURCE_TUNER = 0, ++ SOURCE_COMPOSITE, ++ SOURCE_SVIDEO, ++ SOURCE_OTHER1, ++ SOURCE_OTHER2, ++ SOURCE_COMPVIASVIDEO, ++ SOURCE_CCIR656 ++} VIDEOSOURCETYPE; ++ ++#endif /* _CX88_REG_H_ */ +diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c +new file mode 100644 +index 0000000..424fd97 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-tvaudio.c +@@ -0,0 +1,1059 @@ ++/* ++ ++ cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver ++ ++ (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version] ++ (c) 2002 Yurij Sysoev ++ (c) 2003 Gerd Knorr ++ ++ ----------------------------------------------------------------------- ++ ++ Lot of voodoo here. Even the data sheet doesn't help to ++ understand what is going on here, the documentation for the audio ++ part of the cx2388x chip is *very* bad. ++ ++ Some of this comes from party done linux driver sources I got from ++ [undocumented]. ++ ++ Some comes from the dscaler sources, one of the dscaler driver guy works ++ for Conexant ... ++ ++ ----------------------------------------------------------------------- ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++ ++static unsigned int audio_debug; ++module_param(audio_debug, int, 0644); ++MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]"); ++ ++static unsigned int always_analog; ++module_param(always_analog,int,0644); ++MODULE_PARM_DESC(always_analog,"force analog audio out"); ++ ++static unsigned int radio_deemphasis; ++module_param(radio_deemphasis,int,0644); ++MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant, " ++ "0=None, 1=50us (elsewhere), 2=75us (USA)"); ++ ++#define dprintk(fmt, arg...) if (audio_debug) \ ++ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) ++ ++/* ----------------------------------------------------------- */ ++ ++static const char * const aud_ctl_names[64] = { ++ [EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO", ++ [EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO", ++ [EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP", ++ [EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO", ++ [EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP", ++ [EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1", ++ [EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2", ++ [EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO", ++ [EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2", ++ [EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO", ++ [EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1", ++ [EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2", ++ [EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO", ++ [EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2", ++ [EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO", ++ [EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1", ++ [EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2", ++ [EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO", ++ [EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2", ++ [EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO", ++ [EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO", ++ [EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO", ++ [EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO", ++}; ++ ++struct rlist { ++ u32 reg; ++ u32 val; ++}; ++ ++static void set_audio_registers(struct cx88_core *core, const struct rlist *l) ++{ ++ int i; ++ ++ for (i = 0; l[i].reg; i++) { ++ switch (l[i].reg) { ++ case AUD_PDF_DDS_CNST_BYTE2: ++ case AUD_PDF_DDS_CNST_BYTE1: ++ case AUD_PDF_DDS_CNST_BYTE0: ++ case AUD_QAM_MODE: ++ case AUD_PHACC_FREQ_8MSB: ++ case AUD_PHACC_FREQ_8LSB: ++ cx_writeb(l[i].reg, l[i].val); ++ break; ++ default: ++ cx_write(l[i].reg, l[i].val); ++ break; ++ } ++ } ++} ++ ++static void set_audio_start(struct cx88_core *core, u32 mode) ++{ ++ /* mute */ ++ cx_write(AUD_VOL_CTL, (1 << 6)); ++ ++ /* start programming */ ++ cx_write(AUD_INIT, mode); ++ cx_write(AUD_INIT_LD, 0x0001); ++ cx_write(AUD_SOFT_RESET, 0x0001); ++} ++ ++static void set_audio_finish(struct cx88_core *core, u32 ctl) ++{ ++ u32 volume; ++ ++ /* restart dma; This avoids buzz in NICAM and is good in others */ ++ cx88_stop_audio_dma(core); ++ cx_write(AUD_RATE_THRES_DMD, 0x000000C0); ++ cx88_start_audio_dma(core); ++ ++ if (core->board.mpeg & CX88_MPEG_BLACKBIRD) { ++ cx_write(AUD_I2SINPUTCNTL, 4); ++ cx_write(AUD_BAUDRATE, 1); ++ /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */ ++ cx_set(AUD_CTL, EN_I2SOUT_ENABLE); ++ cx_write(AUD_I2SOUTPUTCNTL, 1); ++ cx_write(AUD_I2SCNTL, 0); ++ /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */ ++ } ++ if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) { ++ ctl |= EN_DAC_ENABLE; ++ cx_write(AUD_CTL, ctl); ++ } ++ ++ /* finish programming */ ++ cx_write(AUD_SOFT_RESET, 0x0000); ++ ++ /* unmute */ ++ volume = cx_sread(SHADOW_AUD_VOL_CTL); ++ cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume); ++ ++ core->last_change = jiffies; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap, ++ u32 mode) ++{ ++ static const struct rlist btsc[] = { ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AUD_OUT1_SEL, 0x00000013}, ++ {AUD_OUT1_SHIFT, 0x00000000}, ++ {AUD_POLY0_DDS_CONSTANT, 0x0012010c}, ++ {AUD_DMD_RA_DDS, 0x00c3e7aa}, ++ {AUD_DBX_IN_GAIN, 0x00004734}, ++ {AUD_DBX_WBE_GAIN, 0x00004640}, ++ {AUD_DBX_SE_GAIN, 0x00008d31}, ++ {AUD_DCOC_0_SRC, 0x0000001a}, ++ {AUD_IIR1_4_SEL, 0x00000021}, ++ {AUD_DCOC_PASS_IN, 0x00000003}, ++ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, ++ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, ++ {AUD_DN0_FREQ, 0x0000283b}, ++ {AUD_DN2_SRC_SEL, 0x00000008}, ++ {AUD_DN2_FREQ, 0x00003000}, ++ {AUD_DN2_AFC, 0x00000002}, ++ {AUD_DN2_SHFT, 0x00000000}, ++ {AUD_IIR2_2_SEL, 0x00000020}, ++ {AUD_IIR2_2_SHIFT, 0x00000000}, ++ {AUD_IIR2_3_SEL, 0x0000001f}, ++ {AUD_IIR2_3_SHIFT, 0x00000000}, ++ {AUD_CRDC1_SRC_SEL, 0x000003ce}, ++ {AUD_CRDC1_SHIFT, 0x00000000}, ++ {AUD_CORDIC_SHIFT_1, 0x00000007}, ++ {AUD_DCOC_1_SRC, 0x0000001b}, ++ {AUD_DCOC1_SHIFT, 0x00000000}, ++ {AUD_RDSI_SEL, 0x00000008}, ++ {AUD_RDSQ_SEL, 0x00000008}, ++ {AUD_RDSI_SHIFT, 0x00000000}, ++ {AUD_RDSQ_SHIFT, 0x00000000}, ++ {AUD_POLYPH80SCALEFAC, 0x00000003}, ++ { /* end of list */ }, ++ }; ++ static const struct rlist btsc_sap[] = { ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AUD_DBX_IN_GAIN, 0x00007200}, ++ {AUD_DBX_WBE_GAIN, 0x00006200}, ++ {AUD_DBX_SE_GAIN, 0x00006200}, ++ {AUD_IIR1_1_SEL, 0x00000000}, ++ {AUD_IIR1_3_SEL, 0x00000001}, ++ {AUD_DN1_SRC_SEL, 0x00000007}, ++ {AUD_IIR1_4_SHIFT, 0x00000006}, ++ {AUD_IIR2_1_SHIFT, 0x00000000}, ++ {AUD_IIR2_2_SHIFT, 0x00000000}, ++ {AUD_IIR3_0_SHIFT, 0x00000000}, ++ {AUD_IIR3_1_SHIFT, 0x00000000}, ++ {AUD_IIR3_0_SEL, 0x0000000d}, ++ {AUD_IIR3_1_SEL, 0x0000000e}, ++ {AUD_DEEMPH1_SRC_SEL, 0x00000014}, ++ {AUD_DEEMPH1_SHIFT, 0x00000000}, ++ {AUD_DEEMPH1_G0, 0x00004000}, ++ {AUD_DEEMPH1_A0, 0x00000000}, ++ {AUD_DEEMPH1_B0, 0x00000000}, ++ {AUD_DEEMPH1_A1, 0x00000000}, ++ {AUD_DEEMPH1_B1, 0x00000000}, ++ {AUD_OUT0_SEL, 0x0000003f}, ++ {AUD_OUT1_SEL, 0x0000003f}, ++ {AUD_DN1_AFC, 0x00000002}, ++ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, ++ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, ++ {AUD_IIR1_0_SEL, 0x0000001d}, ++ {AUD_IIR1_2_SEL, 0x0000001e}, ++ {AUD_IIR2_1_SEL, 0x00000002}, ++ {AUD_IIR2_2_SEL, 0x00000004}, ++ {AUD_IIR3_2_SEL, 0x0000000f}, ++ {AUD_DCOC2_SHIFT, 0x00000001}, ++ {AUD_IIR3_2_SHIFT, 0x00000001}, ++ {AUD_DEEMPH0_SRC_SEL, 0x00000014}, ++ {AUD_CORDIC_SHIFT_1, 0x00000006}, ++ {AUD_POLY0_DDS_CONSTANT, 0x000e4db2}, ++ {AUD_DMD_RA_DDS, 0x00f696e6}, ++ {AUD_IIR2_3_SEL, 0x00000025}, ++ {AUD_IIR1_4_SEL, 0x00000021}, ++ {AUD_DN1_FREQ, 0x0000c965}, ++ {AUD_DCOC_PASS_IN, 0x00000003}, ++ {AUD_DCOC_0_SRC, 0x0000001a}, ++ {AUD_DCOC_1_SRC, 0x0000001b}, ++ {AUD_DCOC1_SHIFT, 0x00000000}, ++ {AUD_RDSI_SEL, 0x00000009}, ++ {AUD_RDSQ_SEL, 0x00000009}, ++ {AUD_RDSI_SHIFT, 0x00000000}, ++ {AUD_RDSQ_SHIFT, 0x00000000}, ++ {AUD_POLYPH80SCALEFAC, 0x00000003}, ++ { /* end of list */ }, ++ }; ++ ++ mode |= EN_FMRADIO_EN_RDS; ++ ++ if (sap) { ++ dprintk("%s SAP (status: unknown)\n", __func__); ++ set_audio_start(core, SEL_SAP); ++ set_audio_registers(core, btsc_sap); ++ set_audio_finish(core, mode); ++ } else { ++ dprintk("%s (status: known-good)\n", __func__); ++ set_audio_start(core, SEL_BTSC); ++ set_audio_registers(core, btsc); ++ set_audio_finish(core, mode); ++ } ++} ++ ++static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) ++{ ++ static const struct rlist nicam_l[] = { ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AUD_RATE_ADJ1, 0x00000060}, ++ {AUD_RATE_ADJ2, 0x000000F9}, ++ {AUD_RATE_ADJ3, 0x000001CC}, ++ {AUD_RATE_ADJ4, 0x000002B3}, ++ {AUD_RATE_ADJ5, 0x00000726}, ++ {AUD_DEEMPHDENOM1_R, 0x0000F3D0}, ++ {AUD_DEEMPHDENOM2_R, 0x00000000}, ++ {AUD_ERRLOGPERIOD_R, 0x00000064}, ++ {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF}, ++ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F}, ++ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F}, ++ {AUD_POLYPH80SCALEFAC, 0x00000003}, ++ {AUD_DMD_RA_DDS, 0x00C00000}, ++ {AUD_PLL_INT, 0x0000001E}, ++ {AUD_PLL_DDS, 0x00000000}, ++ {AUD_PLL_FRAC, 0x0000E542}, ++ {AUD_START_TIMER, 0x00000000}, ++ {AUD_DEEMPHNUMER1_R, 0x000353DE}, ++ {AUD_DEEMPHNUMER2_R, 0x000001B1}, ++ {AUD_PDF_DDS_CNST_BYTE2, 0x06}, ++ {AUD_PDF_DDS_CNST_BYTE1, 0x82}, ++ {AUD_PDF_DDS_CNST_BYTE0, 0x12}, ++ {AUD_QAM_MODE, 0x05}, ++ {AUD_PHACC_FREQ_8MSB, 0x34}, ++ {AUD_PHACC_FREQ_8LSB, 0x4C}, ++ {AUD_DEEMPHGAIN_R, 0x00006680}, ++ {AUD_RATE_THRES_DMD, 0x000000C0}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist nicam_bgdki_common[] = { ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AUD_RATE_ADJ1, 0x00000010}, ++ {AUD_RATE_ADJ2, 0x00000040}, ++ {AUD_RATE_ADJ3, 0x00000100}, ++ {AUD_RATE_ADJ4, 0x00000400}, ++ {AUD_RATE_ADJ5, 0x00001000}, ++ {AUD_ERRLOGPERIOD_R, 0x00000fff}, ++ {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff}, ++ {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff}, ++ {AUD_ERRINTRPTTHSHLD3_R, 0x0000003f}, ++ {AUD_POLYPH80SCALEFAC, 0x00000003}, ++ {AUD_DEEMPHGAIN_R, 0x000023c2}, ++ {AUD_DEEMPHNUMER1_R, 0x0002a7bc}, ++ {AUD_DEEMPHNUMER2_R, 0x0003023e}, ++ {AUD_DEEMPHDENOM1_R, 0x0000f3d0}, ++ {AUD_DEEMPHDENOM2_R, 0x00000000}, ++ {AUD_PDF_DDS_CNST_BYTE2, 0x06}, ++ {AUD_PDF_DDS_CNST_BYTE1, 0x82}, ++ {AUD_QAM_MODE, 0x05}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist nicam_i[] = { ++ {AUD_PDF_DDS_CNST_BYTE0, 0x12}, ++ {AUD_PHACC_FREQ_8MSB, 0x3a}, ++ {AUD_PHACC_FREQ_8LSB, 0x93}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist nicam_default[] = { ++ {AUD_PDF_DDS_CNST_BYTE0, 0x16}, ++ {AUD_PHACC_FREQ_8MSB, 0x34}, ++ {AUD_PHACC_FREQ_8LSB, 0x4c}, ++ { /* end of list */ }, ++ }; ++ ++ set_audio_start(core,SEL_NICAM); ++ switch (core->tvaudio) { ++ case WW_L: ++ dprintk("%s SECAM-L NICAM (status: devel)\n", __func__); ++ set_audio_registers(core, nicam_l); ++ break; ++ case WW_I: ++ dprintk("%s PAL-I NICAM (status: known-good)\n", __func__); ++ set_audio_registers(core, nicam_bgdki_common); ++ set_audio_registers(core, nicam_i); ++ break; ++ case WW_NONE: ++ case WW_BTSC: ++ case WW_BG: ++ case WW_DK: ++ case WW_EIAJ: ++ case WW_I2SPT: ++ case WW_FM: ++ case WW_I2SADC: ++ case WW_M: ++ dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__); ++ set_audio_registers(core, nicam_bgdki_common); ++ set_audio_registers(core, nicam_default); ++ break; ++ } ++ ++ mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS; ++ set_audio_finish(core, mode); ++} ++ ++static void set_audio_standard_A2(struct cx88_core *core, u32 mode) ++{ ++ static const struct rlist a2_bgdk_common[] = { ++ {AUD_ERRLOGPERIOD_R, 0x00000064}, ++ {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff}, ++ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f}, ++ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f}, ++ {AUD_PDF_DDS_CNST_BYTE2, 0x06}, ++ {AUD_PDF_DDS_CNST_BYTE1, 0x82}, ++ {AUD_PDF_DDS_CNST_BYTE0, 0x12}, ++ {AUD_QAM_MODE, 0x05}, ++ {AUD_PHACC_FREQ_8MSB, 0x34}, ++ {AUD_PHACC_FREQ_8LSB, 0x4c}, ++ {AUD_RATE_ADJ1, 0x00000100}, ++ {AUD_RATE_ADJ2, 0x00000200}, ++ {AUD_RATE_ADJ3, 0x00000300}, ++ {AUD_RATE_ADJ4, 0x00000400}, ++ {AUD_RATE_ADJ5, 0x00000500}, ++ {AUD_THR_FR, 0x00000000}, ++ {AAGC_HYST, 0x0000001a}, ++ {AUD_PILOT_BQD_1_K0, 0x0000755b}, ++ {AUD_PILOT_BQD_1_K1, 0x00551340}, ++ {AUD_PILOT_BQD_1_K2, 0x006d30be}, ++ {AUD_PILOT_BQD_1_K3, 0xffd394af}, ++ {AUD_PILOT_BQD_1_K4, 0x00400000}, ++ {AUD_PILOT_BQD_2_K0, 0x00040000}, ++ {AUD_PILOT_BQD_2_K1, 0x002a4841}, ++ {AUD_PILOT_BQD_2_K2, 0x00400000}, ++ {AUD_PILOT_BQD_2_K3, 0x00000000}, ++ {AUD_PILOT_BQD_2_K4, 0x00000000}, ++ {AUD_MODE_CHG_TIMER, 0x00000040}, ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AUD_CORDIC_SHIFT_0, 0x00000007}, ++ {AUD_CORDIC_SHIFT_1, 0x00000007}, ++ {AUD_DEEMPH0_G0, 0x00000380}, ++ {AUD_DEEMPH1_G0, 0x00000380}, ++ {AUD_DCOC_0_SRC, 0x0000001a}, ++ {AUD_DCOC0_SHIFT, 0x00000000}, ++ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, ++ {AUD_DCOC_PASS_IN, 0x00000003}, ++ {AUD_IIR3_0_SEL, 0x00000021}, ++ {AUD_DN2_AFC, 0x00000002}, ++ {AUD_DCOC_1_SRC, 0x0000001b}, ++ {AUD_DCOC1_SHIFT, 0x00000000}, ++ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, ++ {AUD_IIR3_1_SEL, 0x00000023}, ++ {AUD_RDSI_SEL, 0x00000017}, ++ {AUD_RDSI_SHIFT, 0x00000000}, ++ {AUD_RDSQ_SEL, 0x00000017}, ++ {AUD_RDSQ_SHIFT, 0x00000000}, ++ {AUD_PLL_INT, 0x0000001e}, ++ {AUD_PLL_DDS, 0x00000000}, ++ {AUD_PLL_FRAC, 0x0000e542}, ++ {AUD_POLYPH80SCALEFAC, 0x00000001}, ++ {AUD_START_TIMER, 0x00000000}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist a2_bg[] = { ++ {AUD_DMD_RA_DDS, 0x002a4f2f}, ++ {AUD_C1_UP_THR, 0x00007000}, ++ {AUD_C1_LO_THR, 0x00005400}, ++ {AUD_C2_UP_THR, 0x00005400}, ++ {AUD_C2_LO_THR, 0x00003000}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist a2_dk[] = { ++ {AUD_DMD_RA_DDS, 0x002a4f2f}, ++ {AUD_C1_UP_THR, 0x00007000}, ++ {AUD_C1_LO_THR, 0x00005400}, ++ {AUD_C2_UP_THR, 0x00005400}, ++ {AUD_C2_LO_THR, 0x00003000}, ++ {AUD_DN0_FREQ, 0x00003a1c}, ++ {AUD_DN2_FREQ, 0x0000d2e0}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist a1_i[] = { ++ {AUD_ERRLOGPERIOD_R, 0x00000064}, ++ {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff}, ++ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f}, ++ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f}, ++ {AUD_PDF_DDS_CNST_BYTE2, 0x06}, ++ {AUD_PDF_DDS_CNST_BYTE1, 0x82}, ++ {AUD_PDF_DDS_CNST_BYTE0, 0x12}, ++ {AUD_QAM_MODE, 0x05}, ++ {AUD_PHACC_FREQ_8MSB, 0x3a}, ++ {AUD_PHACC_FREQ_8LSB, 0x93}, ++ {AUD_DMD_RA_DDS, 0x002a4f2f}, ++ {AUD_PLL_INT, 0x0000001e}, ++ {AUD_PLL_DDS, 0x00000004}, ++ {AUD_PLL_FRAC, 0x0000e542}, ++ {AUD_RATE_ADJ1, 0x00000100}, ++ {AUD_RATE_ADJ2, 0x00000200}, ++ {AUD_RATE_ADJ3, 0x00000300}, ++ {AUD_RATE_ADJ4, 0x00000400}, ++ {AUD_RATE_ADJ5, 0x00000500}, ++ {AUD_THR_FR, 0x00000000}, ++ {AUD_PILOT_BQD_1_K0, 0x0000755b}, ++ {AUD_PILOT_BQD_1_K1, 0x00551340}, ++ {AUD_PILOT_BQD_1_K2, 0x006d30be}, ++ {AUD_PILOT_BQD_1_K3, 0xffd394af}, ++ {AUD_PILOT_BQD_1_K4, 0x00400000}, ++ {AUD_PILOT_BQD_2_K0, 0x00040000}, ++ {AUD_PILOT_BQD_2_K1, 0x002a4841}, ++ {AUD_PILOT_BQD_2_K2, 0x00400000}, ++ {AUD_PILOT_BQD_2_K3, 0x00000000}, ++ {AUD_PILOT_BQD_2_K4, 0x00000000}, ++ {AUD_MODE_CHG_TIMER, 0x00000060}, ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AAGC_HYST, 0x0000000a}, ++ {AUD_CORDIC_SHIFT_0, 0x00000007}, ++ {AUD_CORDIC_SHIFT_1, 0x00000007}, ++ {AUD_C1_UP_THR, 0x00007000}, ++ {AUD_C1_LO_THR, 0x00005400}, ++ {AUD_C2_UP_THR, 0x00005400}, ++ {AUD_C2_LO_THR, 0x00003000}, ++ {AUD_DCOC_0_SRC, 0x0000001a}, ++ {AUD_DCOC0_SHIFT, 0x00000000}, ++ {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, ++ {AUD_DCOC_PASS_IN, 0x00000003}, ++ {AUD_IIR3_0_SEL, 0x00000021}, ++ {AUD_DN2_AFC, 0x00000002}, ++ {AUD_DCOC_1_SRC, 0x0000001b}, ++ {AUD_DCOC1_SHIFT, 0x00000000}, ++ {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, ++ {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, ++ {AUD_IIR3_1_SEL, 0x00000023}, ++ {AUD_DN0_FREQ, 0x000035a3}, ++ {AUD_DN2_FREQ, 0x000029c7}, ++ {AUD_CRDC0_SRC_SEL, 0x00000511}, ++ {AUD_IIR1_0_SEL, 0x00000001}, ++ {AUD_IIR1_1_SEL, 0x00000000}, ++ {AUD_IIR3_2_SEL, 0x00000003}, ++ {AUD_IIR3_2_SHIFT, 0x00000000}, ++ {AUD_IIR3_0_SEL, 0x00000002}, ++ {AUD_IIR2_0_SEL, 0x00000021}, ++ {AUD_IIR2_0_SHIFT, 0x00000002}, ++ {AUD_DEEMPH0_SRC_SEL, 0x0000000b}, ++ {AUD_DEEMPH1_SRC_SEL, 0x0000000b}, ++ {AUD_POLYPH80SCALEFAC, 0x00000001}, ++ {AUD_START_TIMER, 0x00000000}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist am_l[] = { ++ {AUD_ERRLOGPERIOD_R, 0x00000064}, ++ {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF}, ++ {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F}, ++ {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F}, ++ {AUD_PDF_DDS_CNST_BYTE2, 0x48}, ++ {AUD_PDF_DDS_CNST_BYTE1, 0x3D}, ++ {AUD_QAM_MODE, 0x00}, ++ {AUD_PDF_DDS_CNST_BYTE0, 0xf5}, ++ {AUD_PHACC_FREQ_8MSB, 0x3a}, ++ {AUD_PHACC_FREQ_8LSB, 0x4a}, ++ {AUD_DEEMPHGAIN_R, 0x00006680}, ++ {AUD_DEEMPHNUMER1_R, 0x000353DE}, ++ {AUD_DEEMPHNUMER2_R, 0x000001B1}, ++ {AUD_DEEMPHDENOM1_R, 0x0000F3D0}, ++ {AUD_DEEMPHDENOM2_R, 0x00000000}, ++ {AUD_FM_MODE_ENABLE, 0x00000007}, ++ {AUD_POLYPH80SCALEFAC, 0x00000003}, ++ {AUD_AFE_12DB_EN, 0x00000001}, ++ {AAGC_GAIN, 0x00000000}, ++ {AAGC_HYST, 0x00000018}, ++ {AAGC_DEF, 0x00000020}, ++ {AUD_DN0_FREQ, 0x00000000}, ++ {AUD_POLY0_DDS_CONSTANT, 0x000E4DB2}, ++ {AUD_DCOC_0_SRC, 0x00000021}, ++ {AUD_IIR1_0_SEL, 0x00000000}, ++ {AUD_IIR1_0_SHIFT, 0x00000007}, ++ {AUD_IIR1_1_SEL, 0x00000002}, ++ {AUD_IIR1_1_SHIFT, 0x00000000}, ++ {AUD_DCOC_1_SRC, 0x00000003}, ++ {AUD_DCOC1_SHIFT, 0x00000000}, ++ {AUD_DCOC_PASS_IN, 0x00000000}, ++ {AUD_IIR1_2_SEL, 0x00000023}, ++ {AUD_IIR1_2_SHIFT, 0x00000000}, ++ {AUD_IIR1_3_SEL, 0x00000004}, ++ {AUD_IIR1_3_SHIFT, 0x00000007}, ++ {AUD_IIR1_4_SEL, 0x00000005}, ++ {AUD_IIR1_4_SHIFT, 0x00000007}, ++ {AUD_IIR3_0_SEL, 0x00000007}, ++ {AUD_IIR3_0_SHIFT, 0x00000000}, ++ {AUD_DEEMPH0_SRC_SEL, 0x00000011}, ++ {AUD_DEEMPH0_SHIFT, 0x00000000}, ++ {AUD_DEEMPH0_G0, 0x00007000}, ++ {AUD_DEEMPH0_A0, 0x00000000}, ++ {AUD_DEEMPH0_B0, 0x00000000}, ++ {AUD_DEEMPH0_A1, 0x00000000}, ++ {AUD_DEEMPH0_B1, 0x00000000}, ++ {AUD_DEEMPH1_SRC_SEL, 0x00000011}, ++ {AUD_DEEMPH1_SHIFT, 0x00000000}, ++ {AUD_DEEMPH1_G0, 0x00007000}, ++ {AUD_DEEMPH1_A0, 0x00000000}, ++ {AUD_DEEMPH1_B0, 0x00000000}, ++ {AUD_DEEMPH1_A1, 0x00000000}, ++ {AUD_DEEMPH1_B1, 0x00000000}, ++ {AUD_OUT0_SEL, 0x0000003F}, ++ {AUD_OUT1_SEL, 0x0000003F}, ++ {AUD_DMD_RA_DDS, 0x00F5C285}, ++ {AUD_PLL_INT, 0x0000001E}, ++ {AUD_PLL_DDS, 0x00000000}, ++ {AUD_PLL_FRAC, 0x0000E542}, ++ {AUD_RATE_ADJ1, 0x00000100}, ++ {AUD_RATE_ADJ2, 0x00000200}, ++ {AUD_RATE_ADJ3, 0x00000300}, ++ {AUD_RATE_ADJ4, 0x00000400}, ++ {AUD_RATE_ADJ5, 0x00000500}, ++ {AUD_RATE_THRES_DMD, 0x000000C0}, ++ { /* end of list */ }, ++ }; ++ ++ static const struct rlist a2_deemph50[] = { ++ {AUD_DEEMPH0_G0, 0x00000380}, ++ {AUD_DEEMPH1_G0, 0x00000380}, ++ {AUD_DEEMPHGAIN_R, 0x000011e1}, ++ {AUD_DEEMPHNUMER1_R, 0x0002a7bc}, ++ {AUD_DEEMPHNUMER2_R, 0x0003023c}, ++ { /* end of list */ }, ++ }; ++ ++ set_audio_start(core, SEL_A2); ++ switch (core->tvaudio) { ++ case WW_BG: ++ dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__); ++ set_audio_registers(core, a2_bgdk_common); ++ set_audio_registers(core, a2_bg); ++ set_audio_registers(core, a2_deemph50); ++ break; ++ case WW_DK: ++ dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__); ++ set_audio_registers(core, a2_bgdk_common); ++ set_audio_registers(core, a2_dk); ++ set_audio_registers(core, a2_deemph50); ++ break; ++ case WW_I: ++ dprintk("%s PAL-I A1 (status: known-good)\n", __func__); ++ set_audio_registers(core, a1_i); ++ set_audio_registers(core, a2_deemph50); ++ break; ++ case WW_L: ++ dprintk("%s AM-L (status: devel)\n", __func__); ++ set_audio_registers(core, am_l); ++ break; ++ case WW_NONE: ++ case WW_BTSC: ++ case WW_EIAJ: ++ case WW_I2SPT: ++ case WW_FM: ++ case WW_I2SADC: ++ case WW_M: ++ dprintk("%s Warning: wrong value\n", __func__); ++ return; ++ break; ++ } ++ ++ mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF; ++ set_audio_finish(core, mode); ++} ++ ++static void set_audio_standard_EIAJ(struct cx88_core *core) ++{ ++ static const struct rlist eiaj[] = { ++ /* TODO: eiaj register settings are not there yet ... */ ++ ++ { /* end of list */ }, ++ }; ++ dprintk("%s (status: unknown)\n", __func__); ++ ++ set_audio_start(core, SEL_EIAJ); ++ set_audio_registers(core, eiaj); ++ set_audio_finish(core, EN_EIAJ_AUTO_STEREO); ++} ++ ++static void set_audio_standard_FM(struct cx88_core *core, ++ enum cx88_deemph_type deemph) ++{ ++ static const struct rlist fm_deemph_50[] = { ++ {AUD_DEEMPH0_G0, 0x0C45}, ++ {AUD_DEEMPH0_A0, 0x6262}, ++ {AUD_DEEMPH0_B0, 0x1C29}, ++ {AUD_DEEMPH0_A1, 0x3FC66}, ++ {AUD_DEEMPH0_B1, 0x399A}, ++ ++ {AUD_DEEMPH1_G0, 0x0D80}, ++ {AUD_DEEMPH1_A0, 0x6262}, ++ {AUD_DEEMPH1_B0, 0x1C29}, ++ {AUD_DEEMPH1_A1, 0x3FC66}, ++ {AUD_DEEMPH1_B1, 0x399A}, ++ ++ {AUD_POLYPH80SCALEFAC, 0x0003}, ++ { /* end of list */ }, ++ }; ++ static const struct rlist fm_deemph_75[] = { ++ {AUD_DEEMPH0_G0, 0x091B}, ++ {AUD_DEEMPH0_A0, 0x6B68}, ++ {AUD_DEEMPH0_B0, 0x11EC}, ++ {AUD_DEEMPH0_A1, 0x3FC66}, ++ {AUD_DEEMPH0_B1, 0x399A}, ++ ++ {AUD_DEEMPH1_G0, 0x0AA0}, ++ {AUD_DEEMPH1_A0, 0x6B68}, ++ {AUD_DEEMPH1_B0, 0x11EC}, ++ {AUD_DEEMPH1_A1, 0x3FC66}, ++ {AUD_DEEMPH1_B1, 0x399A}, ++ ++ {AUD_POLYPH80SCALEFAC, 0x0003}, ++ { /* end of list */ }, ++ }; ++ ++ /* It is enough to leave default values? */ ++ /* No, it's not! The deemphasis registers are reset to the 75us ++ * values by default. Analyzing the spectrum of the decoded audio ++ * reveals that "no deemphasis" is the same as 75 us, while the 50 us ++ * setting results in less deemphasis. */ ++ static const struct rlist fm_no_deemph[] = { ++ ++ {AUD_POLYPH80SCALEFAC, 0x0003}, ++ { /* end of list */ }, ++ }; ++ ++ dprintk("%s (status: unknown)\n", __func__); ++ set_audio_start(core, SEL_FMRADIO); ++ ++ switch (deemph) { ++ default: ++ case FM_NO_DEEMPH: ++ set_audio_registers(core, fm_no_deemph); ++ break; ++ ++ case FM_DEEMPH_50: ++ set_audio_registers(core, fm_deemph_50); ++ break; ++ ++ case FM_DEEMPH_75: ++ set_audio_registers(core, fm_deemph_75); ++ break; ++ } ++ ++ set_audio_finish(core, EN_FMRADIO_AUTO_STEREO); ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static int cx88_detect_nicam(struct cx88_core *core) ++{ ++ int i, j = 0; ++ ++ dprintk("start nicam autodetect.\n"); ++ ++ for (i = 0; i < 6; i++) { ++ /* if bit1=1 then nicam is detected */ ++ j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1); ++ ++ if (j == 1) { ++ dprintk("nicam is detected.\n"); ++ return 1; ++ } ++ ++ /* wait a little bit for next reading status */ ++ msleep(10); ++ } ++ ++ dprintk("nicam is not detected.\n"); ++ return 0; ++} ++ ++void cx88_set_tvaudio(struct cx88_core *core) ++{ ++ switch (core->tvaudio) { ++ case WW_BTSC: ++ set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO); ++ break; ++ case WW_BG: ++ case WW_DK: ++ case WW_M: ++ case WW_I: ++ case WW_L: ++ /* prepare all dsp registers */ ++ set_audio_standard_A2(core, EN_A2_FORCE_MONO1); ++ ++ /* set nicam mode - otherwise ++ AUD_NICAM_STATUS2 contains wrong values */ ++ set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO); ++ if (0 == cx88_detect_nicam(core)) { ++ /* fall back to fm / am mono */ ++ set_audio_standard_A2(core, EN_A2_FORCE_MONO1); ++ core->audiomode_current = V4L2_TUNER_MODE_MONO; ++ core->use_nicam = 0; ++ } else { ++ core->use_nicam = 1; ++ } ++ break; ++ case WW_EIAJ: ++ set_audio_standard_EIAJ(core); ++ break; ++ case WW_FM: ++ set_audio_standard_FM(core, radio_deemphasis); ++ break; ++ case WW_I2SADC: ++ set_audio_start(core, 0x01); ++ /* ++ * Slave/Philips/Autobaud ++ * NB on Nova-S bit1 NPhilipsSony appears to be inverted: ++ * 0= Sony, 1=Philips ++ */ ++ cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl); ++ /* Switch to "I2S ADC mode" */ ++ cx_write(AUD_I2SCNTL, 0x1); ++ set_audio_finish(core, EN_I2SIN_ENABLE); ++ break; ++ case WW_NONE: ++ case WW_I2SPT: ++ printk("%s/0: unknown tv audio mode [%d]\n", ++ core->name, core->tvaudio); ++ break; ++ } ++ return; ++} ++ ++void cx88_newstation(struct cx88_core *core) ++{ ++ core->audiomode_manual = UNSET; ++ core->last_change = jiffies; ++} ++ ++void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) ++{ ++ static const char * const m[] = { "stereo", "dual mono", "mono", "sap" }; ++ static const char * const p[] = { "no pilot", "pilot c1", "pilot c2", "?" }; ++ u32 reg, mode, pilot; ++ ++ reg = cx_read(AUD_STATUS); ++ mode = reg & 0x03; ++ pilot = (reg >> 2) & 0x03; ++ ++ if (core->astat != reg) ++ dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n", ++ reg, m[mode], p[pilot], ++ aud_ctl_names[cx_read(AUD_CTL) & 63]); ++ core->astat = reg; ++ ++ t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; ++ t->rxsubchans = UNSET; ++ t->audmode = V4L2_TUNER_MODE_MONO; ++ ++ switch (mode) { ++ case 0: ++ t->audmode = V4L2_TUNER_MODE_STEREO; ++ break; ++ case 1: ++ t->audmode = V4L2_TUNER_MODE_LANG2; ++ break; ++ case 2: ++ t->audmode = V4L2_TUNER_MODE_MONO; ++ break; ++ case 3: ++ t->audmode = V4L2_TUNER_MODE_SAP; ++ break; ++ } ++ ++ switch (core->tvaudio) { ++ case WW_BTSC: ++ case WW_BG: ++ case WW_DK: ++ case WW_M: ++ case WW_EIAJ: ++ if (!core->use_nicam) { ++ t->rxsubchans = cx88_dsp_detect_stereo_sap(core); ++ break; ++ } ++ break; ++ case WW_NONE: ++ case WW_I: ++ case WW_L: ++ case WW_I2SPT: ++ case WW_FM: ++ case WW_I2SADC: ++ /* nothing */ ++ break; ++ } ++ ++ /* If software stereo detection is not supported... */ ++ if (UNSET == t->rxsubchans) { ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ /* If the hardware itself detected stereo, also return ++ stereo as an available subchannel */ ++ if (V4L2_TUNER_MODE_STEREO == t->audmode) ++ t->rxsubchans |= V4L2_TUNER_SUB_STEREO; ++ } ++ return; ++} ++ ++void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) ++{ ++ u32 ctl = UNSET; ++ u32 mask = UNSET; ++ ++ if (manual) { ++ core->audiomode_manual = mode; ++ } else { ++ if (UNSET != core->audiomode_manual) ++ return; ++ } ++ core->audiomode_current = mode; ++ ++ switch (core->tvaudio) { ++ case WW_BTSC: ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO); ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO); ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP); ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO); ++ break; ++ } ++ break; ++ case WW_BG: ++ case WW_DK: ++ case WW_M: ++ case WW_I: ++ case WW_L: ++ if (1 == core->use_nicam) { ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_LANG1: ++ set_audio_standard_NICAM(core, ++ EN_NICAM_FORCE_MONO1); ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ set_audio_standard_NICAM(core, ++ EN_NICAM_FORCE_MONO2); ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ set_audio_standard_NICAM(core, ++ EN_NICAM_FORCE_STEREO); ++ break; ++ } ++ } else { ++ if ((core->tvaudio == WW_I) || (core->tvaudio == WW_L)) { ++ /* fall back to fm / am mono */ ++ set_audio_standard_A2(core, EN_A2_FORCE_MONO1); ++ } else { ++ /* TODO: Add A2 autodection */ ++ mask = 0x3f; ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ case V4L2_TUNER_MODE_LANG1: ++ ctl = EN_A2_FORCE_MONO1; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ ctl = EN_A2_FORCE_MONO2; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ ctl = EN_A2_FORCE_STEREO; ++ break; ++ } ++ } ++ } ++ break; ++ case WW_FM: ++ switch (mode) { ++ case V4L2_TUNER_MODE_MONO: ++ ctl = EN_FMRADIO_FORCE_MONO; ++ mask = 0x3f; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ ctl = EN_FMRADIO_AUTO_STEREO; ++ mask = 0x3f; ++ break; ++ } ++ break; ++ case WW_I2SADC: ++ case WW_NONE: ++ case WW_EIAJ: ++ case WW_I2SPT: ++ /* DO NOTHING */ ++ break; ++ } ++ ++ if (UNSET != ctl) { ++ dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x " ++ "[status=0x%x,ctl=0x%x,vol=0x%x]\n", ++ mask, ctl, cx_read(AUD_STATUS), ++ cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL)); ++ cx_andor(AUD_CTL, mask, ctl); ++ } ++ return; ++} ++ ++int cx88_audio_thread(void *data) ++{ ++ struct cx88_core *core = data; ++ struct v4l2_tuner t; ++ u32 mode = 0; ++ ++ dprintk("cx88: tvaudio thread started\n"); ++ set_freezable(); ++ for (;;) { ++ msleep_interruptible(1000); ++ if (kthread_should_stop()) ++ break; ++ try_to_freeze(); ++ ++ switch (core->tvaudio) { ++ case WW_BG: ++ case WW_DK: ++ case WW_M: ++ case WW_I: ++ case WW_L: ++ if (core->use_nicam) ++ goto hw_autodetect; ++ ++ /* just monitor the audio status for now ... */ ++ memset(&t, 0, sizeof(t)); ++ cx88_get_stereo(core, &t); ++ ++ if (UNSET != core->audiomode_manual) ++ /* manually set, don't do anything. */ ++ continue; ++ ++ /* monitor signal and set stereo if available */ ++ if (t.rxsubchans & V4L2_TUNER_SUB_STEREO) ++ mode = V4L2_TUNER_MODE_STEREO; ++ else ++ mode = V4L2_TUNER_MODE_MONO; ++ if (mode == core->audiomode_current) ++ continue; ++ /* automatically switch to best available mode */ ++ cx88_set_stereo(core, mode, 0); ++ break; ++ case WW_NONE: ++ case WW_BTSC: ++ case WW_EIAJ: ++ case WW_I2SPT: ++ case WW_FM: ++ case WW_I2SADC: ++hw_autodetect: ++ /* stereo autodetection is supported by hardware so ++ we don't need to do it manually. Do nothing. */ ++ break; ++ } ++ } ++ ++ dprintk("cx88: tvaudio thread exiting\n"); ++ return 0; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++EXPORT_SYMBOL(cx88_set_tvaudio); ++EXPORT_SYMBOL(cx88_newstation); ++EXPORT_SYMBOL(cx88_set_stereo); ++EXPORT_SYMBOL(cx88_get_stereo); ++EXPORT_SYMBOL(cx88_audio_thread); ++ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off ++ */ +diff --git a/drivers/media/pci/cx88/cx88-vbi.c b/drivers/media/pci/cx88/cx88-vbi.c +new file mode 100644 +index 0000000..f8f8389 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-vbi.c +@@ -0,0 +1,245 @@ ++/* ++ */ ++#include ++#include ++#include ++ ++#include "cx88.h" ++ ++static unsigned int vbibufs = 4; ++module_param(vbibufs,int,0644); ++MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); ++ ++static unsigned int vbi_debug; ++module_param(vbi_debug,int,0644); ++MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); ++ ++#define dprintk(level,fmt, arg...) if (vbi_debug >= level) \ ++ printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) ++ ++/* ------------------------------------------------------------------ */ ++ ++int cx8800_vbi_fmt (struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8800_fh *fh = priv; ++ struct cx8800_dev *dev = fh->dev; ++ ++ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; ++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; ++ f->fmt.vbi.offset = 244; ++ f->fmt.vbi.count[0] = VBI_LINE_COUNT; ++ f->fmt.vbi.count[1] = VBI_LINE_COUNT; ++ ++ if (dev->core->tvnorm & V4L2_STD_525_60) { ++ /* ntsc */ ++ f->fmt.vbi.sampling_rate = 28636363; ++ f->fmt.vbi.start[0] = 10; ++ f->fmt.vbi.start[1] = 273; ++ ++ } else if (dev->core->tvnorm & V4L2_STD_625_50) { ++ /* pal */ ++ f->fmt.vbi.sampling_rate = 35468950; ++ f->fmt.vbi.start[0] = 7 -1; ++ f->fmt.vbi.start[1] = 319 -1; ++ } ++ return 0; ++} ++ ++static int cx8800_start_vbi_dma(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q, ++ struct cx88_buffer *buf) ++{ ++ struct cx88_core *core = dev->core; ++ ++ /* setup fifo + format */ ++ cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], ++ buf->vb.width, buf->risc.dma); ++ ++ cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup ++ (1 << 15) | // enable vbi capture ++ (1 << 11) )); ++ ++ /* reset counter */ ++ cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET); ++ q->count = 1; ++ ++ /* enable irqs */ ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); ++ cx_set(MO_VID_INTMSK, 0x0f0088); ++ ++ /* enable capture */ ++ cx_set(VID_CAPTURE_CONTROL,0x18); ++ ++ /* start dma */ ++ cx_set(MO_DEV_CNTRL2, (1<<5)); ++ cx_set(MO_VID_DMACNTRL, 0x88); ++ ++ return 0; ++} ++ ++int cx8800_stop_vbi_dma(struct cx8800_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ ++ /* stop dma */ ++ cx_clear(MO_VID_DMACNTRL, 0x88); ++ ++ /* disable capture */ ++ cx_clear(VID_CAPTURE_CONTROL,0x18); ++ ++ /* disable irqs */ ++ cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT); ++ cx_clear(MO_VID_INTMSK, 0x0f0088); ++ return 0; ++} ++ ++int cx8800_restart_vbi_queue(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q) ++{ ++ struct cx88_buffer *buf; ++ ++ if (list_empty(&q->active)) ++ return 0; ++ ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ dprintk(2,"restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ cx8800_start_vbi_dma(dev, q, buf); ++ list_for_each_entry(buf, &q->active, vb.queue) ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++} ++ ++void cx8800_vbi_timeout(unsigned long data) ++{ ++ struct cx8800_dev *dev = (struct cx8800_dev*)data; ++ struct cx88_core *core = dev->core; ++ struct cx88_dmaqueue *q = &dev->vbiq; ++ struct cx88_buffer *buf; ++ unsigned long flags; ++ ++ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]); ++ ++ cx_clear(MO_VID_DMACNTRL, 0x88); ++ cx_clear(VID_CAPTURE_CONTROL, 0x18); ++ ++ spin_lock_irqsave(&dev->slock,flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name, ++ buf, buf->vb.i, (unsigned long)buf->risc.dma); ++ } ++ cx8800_restart_vbi_queue(dev,q); ++ spin_unlock_irqrestore(&dev->slock,flags); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int ++vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; ++ if (0 == *count) ++ *count = vbibufs; ++ if (*count < 2) ++ *count = 2; ++ if (*count > 32) ++ *count = 32; ++ return 0; ++} ++ ++static int ++vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx8800_fh *fh = q->priv_data; ++ struct cx8800_dev *dev = fh->dev; ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ unsigned int size; ++ int rc; ++ ++ size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ buf->vb.width = VBI_LINE_LENGTH; ++ buf->vb.height = VBI_LINE_COUNT; ++ buf->vb.size = size; ++ buf->vb.field = V4L2_FIELD_SEQ_TB; ++ ++ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) ++ goto fail; ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ 0, buf->vb.width * buf->vb.height, ++ buf->vb.width, 0, ++ buf->vb.height); ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx88_free_buffer(q,buf); ++ return rc; ++} ++ ++static void ++vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ struct cx88_buffer *prev; ++ struct cx8800_fh *fh = vq->priv_data; ++ struct cx8800_dev *dev = fh->dev; ++ struct cx88_dmaqueue *q = &dev->vbiq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); ++ ++ if (list_empty(&q->active)) { ++ list_add_tail(&buf->vb.queue,&q->active); ++ cx8800_start_vbi_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2,"[%p/%d] vbi_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); ++ list_add_tail(&buf->vb.queue,&q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ dprintk(2,"[%p/%d] buffer_queue - append to active\n", ++ buf, buf->vb.i); ++ } ++} ++ ++static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ ++ cx88_free_buffer(q,buf); ++} ++ ++const struct videobuf_queue_ops cx8800_vbi_qops = { ++ .buf_setup = vbi_setup, ++ .buf_prepare = vbi_prepare, ++ .buf_queue = vbi_queue, ++ .buf_release = vbi_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c +new file mode 100644 +index 0000000..0517145 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-video.c +@@ -0,0 +1,2075 @@ ++/* ++ * ++ * device driver for Conexant 2388x based TV cards ++ * video4linux video interface ++ * ++ * (c) 2003-04 Gerd Knorr [SuSE Labs] ++ * ++ * (c) 2005-2006 Mauro Carvalho Chehab ++ * - Multituner support ++ * - video_ioctl2 conversion ++ * - PAL/M fixes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "cx88.h" ++#include ++#include ++#include ++#include ++ ++MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(CX88_VERSION); ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; ++ ++module_param_array(video_nr, int, NULL, 0444); ++module_param_array(vbi_nr, int, NULL, 0444); ++module_param_array(radio_nr, int, NULL, 0444); ++ ++MODULE_PARM_DESC(video_nr,"video device numbers"); ++MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); ++MODULE_PARM_DESC(radio_nr,"radio device numbers"); ++ ++static unsigned int video_debug; ++module_param(video_debug,int,0644); ++MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); ++ ++static unsigned int irq_debug; ++module_param(irq_debug,int,0644); ++MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); ++ ++static unsigned int vid_limit = 16; ++module_param(vid_limit,int,0644); ++MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); ++ ++#define dprintk(level,fmt, arg...) if (video_debug >= level) \ ++ printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) ++ ++/* ------------------------------------------------------------------- */ ++/* static data */ ++ ++static const struct cx8800_fmt formats[] = { ++ { ++ .name = "8 bpp, gray", ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .cxformat = ColorFormatY8, ++ .depth = 8, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "15 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB555, ++ .cxformat = ColorFormatRGB15, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "15 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB555X, ++ .cxformat = ColorFormatRGB15 | ColorFormatBSWAP, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "16 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .cxformat = ColorFormatRGB16, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "16 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB565X, ++ .cxformat = ColorFormatRGB16 | ColorFormatBSWAP, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "24 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .cxformat = ColorFormatRGB24, ++ .depth = 24, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "32 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .cxformat = ColorFormatRGB32, ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "32 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP, ++ .depth = 32, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .cxformat = ColorFormatYUY2, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ },{ ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .cxformat = ColorFormatYUY2 | ColorFormatBSWAP, ++ .depth = 16, ++ .flags = FORMAT_FLAGS_PACKED, ++ }, ++}; ++ ++static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fourcc) ++ return formats+i; ++ return NULL; ++} ++ ++/* ------------------------------------------------------------------- */ ++ ++struct cx88_ctrl { ++ /* control information */ ++ u32 id; ++ s32 minimum; ++ s32 maximum; ++ u32 step; ++ s32 default_value; ++ ++ /* control register information */ ++ u32 off; ++ u32 reg; ++ u32 sreg; ++ u32 mask; ++ u32 shift; ++}; ++ ++static const struct cx88_ctrl cx8800_vid_ctls[] = { ++ /* --- video --- */ ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .minimum = 0x00, ++ .maximum = 0xff, ++ .step = 1, ++ .default_value = 0x7f, ++ .off = 128, ++ .reg = MO_CONTR_BRIGHT, ++ .mask = 0x00ff, ++ .shift = 0, ++ },{ ++ .id = V4L2_CID_CONTRAST, ++ .minimum = 0, ++ .maximum = 0xff, ++ .step = 1, ++ .default_value = 0x3f, ++ .off = 0, ++ .reg = MO_CONTR_BRIGHT, ++ .mask = 0xff00, ++ .shift = 8, ++ },{ ++ .id = V4L2_CID_HUE, ++ .minimum = 0, ++ .maximum = 0xff, ++ .step = 1, ++ .default_value = 0x7f, ++ .off = 128, ++ .reg = MO_HUE, ++ .mask = 0x00ff, ++ .shift = 0, ++ },{ ++ /* strictly, this only describes only U saturation. ++ * V saturation is handled specially through code. ++ */ ++ .id = V4L2_CID_SATURATION, ++ .minimum = 0, ++ .maximum = 0xff, ++ .step = 1, ++ .default_value = 0x7f, ++ .off = 0, ++ .reg = MO_UV_SATURATION, ++ .mask = 0x00ff, ++ .shift = 0, ++ }, { ++ .id = V4L2_CID_SHARPNESS, ++ .minimum = 0, ++ .maximum = 4, ++ .step = 1, ++ .default_value = 0x0, ++ .off = 0, ++ /* NOTE: the value is converted and written to both even ++ and odd registers in the code */ ++ .reg = MO_FILTER_ODD, ++ .mask = 7 << 7, ++ .shift = 7, ++ }, { ++ .id = V4L2_CID_CHROMA_AGC, ++ .minimum = 0, ++ .maximum = 1, ++ .default_value = 0x1, ++ .reg = MO_INPUT_FORMAT, ++ .mask = 1 << 10, ++ .shift = 10, ++ }, { ++ .id = V4L2_CID_COLOR_KILLER, ++ .minimum = 0, ++ .maximum = 1, ++ .default_value = 0x1, ++ .reg = MO_INPUT_FORMAT, ++ .mask = 1 << 9, ++ .shift = 9, ++ }, { ++ .id = V4L2_CID_BAND_STOP_FILTER, ++ .minimum = 0, ++ .maximum = 1, ++ .step = 1, ++ .default_value = 0x0, ++ .off = 0, ++ .reg = MO_HTOTAL, ++ .mask = 3 << 11, ++ .shift = 11, ++ } ++}; ++ ++static const struct cx88_ctrl cx8800_aud_ctls[] = { ++ { ++ /* --- audio --- */ ++ .id = V4L2_CID_AUDIO_MUTE, ++ .minimum = 0, ++ .maximum = 1, ++ .default_value = 1, ++ .reg = AUD_VOL_CTL, ++ .sreg = SHADOW_AUD_VOL_CTL, ++ .mask = (1 << 6), ++ .shift = 6, ++ },{ ++ .id = V4L2_CID_AUDIO_VOLUME, ++ .minimum = 0, ++ .maximum = 0x3f, ++ .step = 1, ++ .default_value = 0x3f, ++ .reg = AUD_VOL_CTL, ++ .sreg = SHADOW_AUD_VOL_CTL, ++ .mask = 0x3f, ++ .shift = 0, ++ },{ ++ .id = V4L2_CID_AUDIO_BALANCE, ++ .minimum = 0, ++ .maximum = 0x7f, ++ .step = 1, ++ .default_value = 0x40, ++ .reg = AUD_BAL_CTL, ++ .sreg = SHADOW_AUD_BAL_CTL, ++ .mask = 0x7f, ++ .shift = 0, ++ } ++}; ++ ++enum { ++ CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls), ++ CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls), ++}; ++ ++/* ------------------------------------------------------------------- */ ++/* resource management */ ++ ++static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit) ++{ ++ struct cx88_core *core = dev->core; ++ if (fh->resources & bit) ++ /* have it already allocated */ ++ return 1; ++ ++ /* is it free? */ ++ mutex_lock(&core->lock); ++ if (dev->resources & bit) { ++ /* no, someone else uses it */ ++ mutex_unlock(&core->lock); ++ return 0; ++ } ++ /* it's free, grab it */ ++ fh->resources |= bit; ++ dev->resources |= bit; ++ dprintk(1,"res: get %d\n",bit); ++ mutex_unlock(&core->lock); ++ return 1; ++} ++ ++static ++int res_check(struct cx8800_fh *fh, unsigned int bit) ++{ ++ return (fh->resources & bit); ++} ++ ++static ++int res_locked(struct cx8800_dev *dev, unsigned int bit) ++{ ++ return (dev->resources & bit); ++} ++ ++static ++void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) ++{ ++ struct cx88_core *core = dev->core; ++ BUG_ON((fh->resources & bits) != bits); ++ ++ mutex_lock(&core->lock); ++ fh->resources &= ~bits; ++ dev->resources &= ~bits; ++ dprintk(1,"res: put %d\n",bits); ++ mutex_unlock(&core->lock); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int cx88_video_mux(struct cx88_core *core, unsigned int input) ++{ ++ /* struct cx88_core *core = dev->core; */ ++ ++ dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n", ++ input, INPUT(input).vmux, ++ INPUT(input).gpio0,INPUT(input).gpio1, ++ INPUT(input).gpio2,INPUT(input).gpio3); ++ core->input = input; ++ cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14); ++ cx_write(MO_GP3_IO, INPUT(input).gpio3); ++ cx_write(MO_GP0_IO, INPUT(input).gpio0); ++ cx_write(MO_GP1_IO, INPUT(input).gpio1); ++ cx_write(MO_GP2_IO, INPUT(input).gpio2); ++ ++ switch (INPUT(input).type) { ++ case CX88_VMUX_SVIDEO: ++ cx_set(MO_AFECFG_IO, 0x00000001); ++ cx_set(MO_INPUT_FORMAT, 0x00010010); ++ cx_set(MO_FILTER_EVEN, 0x00002020); ++ cx_set(MO_FILTER_ODD, 0x00002020); ++ break; ++ default: ++ cx_clear(MO_AFECFG_IO, 0x00000001); ++ cx_clear(MO_INPUT_FORMAT, 0x00010010); ++ cx_clear(MO_FILTER_EVEN, 0x00002020); ++ cx_clear(MO_FILTER_ODD, 0x00002020); ++ break; ++ } ++ ++ /* if there are audioroutes defined, we have an external ++ ADC to deal with audio */ ++ if (INPUT(input).audioroute) { ++ /* The wm8775 module has the "2" route hardwired into ++ the initialization. Some boards may use different ++ routes for different inputs. HVR-1300 surely does */ ++ if (core->board.audio_chip && ++ core->board.audio_chip == V4L2_IDENT_WM8775) { ++ call_all(core, audio, s_routing, ++ INPUT(input).audioroute, 0, 0); ++ } ++ /* cx2388's C-ADC is connected to the tuner only. ++ When used with S-Video, that ADC is busy dealing with ++ chroma, so an external must be used for baseband audio */ ++ if (INPUT(input).type != CX88_VMUX_TELEVISION && ++ INPUT(input).type != CX88_VMUX_CABLE) { ++ /* "I2S ADC mode" */ ++ core->tvaudio = WW_I2SADC; ++ cx88_set_tvaudio(core); ++ } else { ++ /* Normal mode */ ++ cx_write(AUD_I2SCNTL, 0x0); ++ cx_clear(AUD_CTL, EN_I2SIN_ENABLE); ++ } ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx88_video_mux); ++ ++/* ------------------------------------------------------------------ */ ++ ++static int start_video_dma(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q, ++ struct cx88_buffer *buf) ++{ ++ struct cx88_core *core = dev->core; ++ ++ /* setup fifo + format */ ++ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], ++ buf->bpl, buf->risc.dma); ++ cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field); ++ cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); ++ ++ /* reset counter */ ++ cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET); ++ q->count = 1; ++ ++ /* enable irqs */ ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); ++ ++ /* Enables corresponding bits at PCI_INT_STAT: ++ bits 0 to 4: video, audio, transport stream, VIP, Host ++ bit 7: timer ++ bits 8 and 9: DMA complete for: SRC, DST ++ bits 10 and 11: BERR signal asserted for RISC: RD, WR ++ bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB ++ */ ++ cx_set(MO_VID_INTMSK, 0x0f0011); ++ ++ /* enable capture */ ++ cx_set(VID_CAPTURE_CONTROL,0x06); ++ ++ /* start dma */ ++ cx_set(MO_DEV_CNTRL2, (1<<5)); ++ cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */ ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int stop_video_dma(struct cx8800_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ ++ /* stop dma */ ++ cx_clear(MO_VID_DMACNTRL, 0x11); ++ ++ /* disable capture */ ++ cx_clear(VID_CAPTURE_CONTROL,0x06); ++ ++ /* disable irqs */ ++ cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT); ++ cx_clear(MO_VID_INTMSK, 0x0f0011); ++ return 0; ++} ++#endif ++ ++static int restart_video_queue(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q) ++{ ++ struct cx88_core *core = dev->core; ++ struct cx88_buffer *buf, *prev; ++ ++ if (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ dprintk(2,"restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ start_video_dma(dev, q, buf); ++ list_for_each_entry(buf, &q->active, vb.queue) ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++ } ++ ++ prev = NULL; ++ for (;;) { ++ if (list_empty(&q->queued)) ++ return 0; ++ buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); ++ if (NULL == prev) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ start_video_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2,"[%p/%d] restart_queue - first active\n", ++ buf,buf->vb.i); ++ ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ dprintk(2,"[%p/%d] restart_queue - move to active\n", ++ buf,buf->vb.i); ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int ++buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ struct cx8800_fh *fh = q->priv_data; ++ struct cx8800_dev *dev = fh->dev; ++ ++ *size = dev->fmt->depth * dev->width * dev->height >> 3; ++ if (0 == *count) ++ *count = 32; ++ if (*size * *count > vid_limit * 1024 * 1024) ++ *count = (vid_limit * 1024 * 1024) / *size; ++ return 0; ++} ++ ++static int ++buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct cx8800_fh *fh = q->priv_data; ++ struct cx8800_dev *dev = fh->dev; ++ struct cx88_core *core = dev->core; ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ int rc, init_buffer = 0; ++ ++ BUG_ON(NULL == dev->fmt); ++ if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) || ++ dev->height < 32 || dev->height > norm_maxh(core->tvnorm)) ++ return -EINVAL; ++ buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3; ++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ ++ if (buf->fmt != dev->fmt || ++ buf->vb.width != dev->width || ++ buf->vb.height != dev->height || ++ buf->vb.field != field) { ++ buf->fmt = dev->fmt; ++ buf->vb.width = dev->width; ++ buf->vb.height = dev->height; ++ buf->vb.field = field; ++ init_buffer = 1; ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ init_buffer = 1; ++ if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) ++ goto fail; ++ } ++ ++ if (init_buffer) { ++ buf->bpl = buf->vb.width * buf->fmt->depth >> 3; ++ switch (buf->vb.field) { ++ case V4L2_FIELD_TOP: ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, 0, UNSET, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, UNSET, 0, ++ buf->bpl, 0, buf->vb.height); ++ break; ++ case V4L2_FIELD_INTERLACED: ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, 0, buf->bpl, ++ buf->bpl, buf->bpl, ++ buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ 0, buf->bpl * (buf->vb.height >> 1), ++ buf->bpl, 0, ++ buf->vb.height >> 1); ++ break; ++ case V4L2_FIELD_SEQ_BT: ++ cx88_risc_buffer(dev->pci, &buf->risc, ++ dma->sglist, ++ buf->bpl * (buf->vb.height >> 1), 0, ++ buf->bpl, 0, ++ buf->vb.height >> 1); ++ break; ++ default: ++ BUG(); ++ } ++ } ++ dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", ++ buf, buf->vb.i, ++ dev->width, dev->height, dev->fmt->depth, dev->fmt->name, ++ (unsigned long)buf->risc.dma); ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++ fail: ++ cx88_free_buffer(q,buf); ++ return rc; ++} ++ ++static void ++buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ struct cx88_buffer *prev; ++ struct cx8800_fh *fh = vq->priv_data; ++ struct cx8800_dev *dev = fh->dev; ++ struct cx88_core *core = dev->core; ++ struct cx88_dmaqueue *q = &dev->vidq; ++ ++ /* add jump to stopper */ ++ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); ++ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); ++ ++ if (!list_empty(&q->queued)) { ++ list_add_tail(&buf->vb.queue,&q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2,"[%p/%d] buffer_queue - append to queued\n", ++ buf, buf->vb.i); ++ ++ } else if (list_empty(&q->active)) { ++ list_add_tail(&buf->vb.queue,&q->active); ++ start_video_dma(dev, q, buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2,"[%p/%d] buffer_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); ++ if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_add_tail(&buf->vb.queue,&q->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->count = q->count++; ++ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); ++ dprintk(2,"[%p/%d] buffer_queue - append to active\n", ++ buf, buf->vb.i); ++ ++ } else { ++ list_add_tail(&buf->vb.queue,&q->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2,"[%p/%d] buffer_queue - first queued\n", ++ buf, buf->vb.i); ++ } ++ } ++} ++ ++static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); ++ ++ cx88_free_buffer(q,buf); ++} ++ ++static const struct videobuf_queue_ops cx8800_video_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct videobuf_queue *get_queue(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_fh *fh = file->private_data; ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ return &fh->vidq; ++ case VFL_TYPE_VBI: ++ return &fh->vbiq; ++ default: ++ BUG(); ++ return NULL; ++ } ++} ++ ++static int get_resource(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ return RESOURCE_VIDEO; ++ case VFL_TYPE_VBI: ++ return RESOURCE_VBI; ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static int video_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_dev *dev = video_drvdata(file); ++ struct cx88_core *core = dev->core; ++ struct cx8800_fh *fh; ++ enum v4l2_buf_type type = 0; ++ int radio = 0; ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ break; ++ case VFL_TYPE_VBI: ++ type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ break; ++ case VFL_TYPE_RADIO: ++ radio = 1; ++ break; ++ } ++ ++ dprintk(1, "open dev=%s radio=%d type=%s\n", ++ video_device_node_name(vdev), radio, v4l2_type_names[type]); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh),GFP_KERNEL); ++ if (unlikely(!fh)) ++ return -ENOMEM; ++ ++ v4l2_fh_init(&fh->fh, vdev); ++ file->private_data = fh; ++ fh->dev = dev; ++ ++ mutex_lock(&core->lock); ++ ++ videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct cx88_buffer), ++ fh, NULL); ++ videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VBI_CAPTURE, ++ V4L2_FIELD_SEQ_TB, ++ sizeof(struct cx88_buffer), ++ fh, NULL); ++ ++ if (vdev->vfl_type == VFL_TYPE_RADIO) { ++ dprintk(1,"video_open: setting radio device\n"); ++ cx_write(MO_GP3_IO, core->board.radio.gpio3); ++ cx_write(MO_GP0_IO, core->board.radio.gpio0); ++ cx_write(MO_GP1_IO, core->board.radio.gpio1); ++ cx_write(MO_GP2_IO, core->board.radio.gpio2); ++ if (core->board.radio.audioroute) { ++ if(core->board.audio_chip && ++ core->board.audio_chip == V4L2_IDENT_WM8775) { ++ call_all(core, audio, s_routing, ++ core->board.radio.audioroute, 0, 0); ++ } ++ /* "I2S ADC mode" */ ++ core->tvaudio = WW_I2SADC; ++ cx88_set_tvaudio(core); ++ } else { ++ /* FM Mode */ ++ core->tvaudio = WW_FM; ++ cx88_set_tvaudio(core); ++ cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); ++ } ++ call_all(core, tuner, s_radio); ++ } ++ ++ core->users++; ++ mutex_unlock(&core->lock); ++ v4l2_fh_add(&fh->fh); ++ ++ return 0; ++} ++ ++static ssize_t ++video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_fh *fh = file->private_data; ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ if (res_locked(fh->dev,RESOURCE_VIDEO)) ++ return -EBUSY; ++ return videobuf_read_one(&fh->vidq, data, count, ppos, ++ file->f_flags & O_NONBLOCK); ++ case VFL_TYPE_VBI: ++ if (!res_get(fh->dev,fh,RESOURCE_VBI)) ++ return -EBUSY; ++ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, ++ file->f_flags & O_NONBLOCK); ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static unsigned int ++video_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_fh *fh = file->private_data; ++ struct cx88_buffer *buf; ++ unsigned int rc = v4l2_ctrl_poll(file, wait); ++ ++ if (vdev->vfl_type == VFL_TYPE_VBI) { ++ if (!res_get(fh->dev,fh,RESOURCE_VBI)) ++ return rc | POLLERR; ++ return rc | videobuf_poll_stream(file, &fh->vbiq, wait); ++ } ++ mutex_lock(&fh->vidq.vb_lock); ++ if (res_check(fh,RESOURCE_VIDEO)) { ++ /* streaming capture */ ++ if (list_empty(&fh->vidq.stream)) ++ goto done; ++ buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream); ++ } else { ++ /* read() capture */ ++ buf = (struct cx88_buffer*)fh->vidq.read_buf; ++ if (NULL == buf) ++ goto done; ++ } ++ poll_wait(file, &buf->vb.done, wait); ++ if (buf->vb.state == VIDEOBUF_DONE || ++ buf->vb.state == VIDEOBUF_ERROR) ++ rc |= POLLIN|POLLRDNORM; ++done: ++ mutex_unlock(&fh->vidq.vb_lock); ++ return rc; ++} ++ ++static int video_release(struct file *file) ++{ ++ struct cx8800_fh *fh = file->private_data; ++ struct cx8800_dev *dev = fh->dev; ++ ++ /* turn off overlay */ ++ if (res_check(fh, RESOURCE_OVERLAY)) { ++ /* FIXME */ ++ res_free(dev,fh,RESOURCE_OVERLAY); ++ } ++ ++ /* stop video capture */ ++ if (res_check(fh, RESOURCE_VIDEO)) { ++ videobuf_queue_cancel(&fh->vidq); ++ res_free(dev,fh,RESOURCE_VIDEO); ++ } ++ if (fh->vidq.read_buf) { ++ buffer_release(&fh->vidq,fh->vidq.read_buf); ++ kfree(fh->vidq.read_buf); ++ } ++ ++ /* stop vbi capture */ ++ if (res_check(fh, RESOURCE_VBI)) { ++ videobuf_stop(&fh->vbiq); ++ res_free(dev,fh,RESOURCE_VBI); ++ } ++ ++ videobuf_mmap_free(&fh->vidq); ++ videobuf_mmap_free(&fh->vbiq); ++ ++ mutex_lock(&dev->core->lock); ++ v4l2_fh_del(&fh->fh); ++ v4l2_fh_exit(&fh->fh); ++ file->private_data = NULL; ++ kfree(fh); ++ ++ dev->core->users--; ++ if (!dev->core->users) ++ call_all(dev->core, core, s_power, 0); ++ mutex_unlock(&dev->core->lock); ++ ++ return 0; ++} ++ ++static int ++video_mmap(struct file *file, struct vm_area_struct * vma) ++{ ++ return videobuf_mmap_mapper(get_queue(file), vma); ++} ++ ++/* ------------------------------------------------------------------ */ ++/* VIDEO CTRL IOCTLS */ ++ ++static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct cx88_core *core = ++ container_of(ctrl->handler, struct cx88_core, video_hdl); ++ const struct cx88_ctrl *cc = ctrl->priv; ++ u32 value, mask; ++ ++ mask = cc->mask; ++ switch (ctrl->id) { ++ case V4L2_CID_SATURATION: ++ /* special v_sat handling */ ++ ++ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; ++ ++ if (core->tvnorm & V4L2_STD_SECAM) { ++ /* For SECAM, both U and V sat should be equal */ ++ value = value << 8 | value; ++ } else { ++ /* Keeps U Saturation proportional to V Sat */ ++ value = (value * 0x5a) / 0x7f << 8 | value; ++ } ++ mask = 0xffff; ++ break; ++ case V4L2_CID_SHARPNESS: ++ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */ ++ value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7)); ++ /* needs to be set for both fields */ ++ cx_andor(MO_FILTER_EVEN, mask, value); ++ break; ++ case V4L2_CID_CHROMA_AGC: ++ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; ++ break; ++ default: ++ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; ++ break; ++ } ++ dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", ++ ctrl->id, ctrl->name, ctrl->val, cc->reg, value, ++ mask, cc->sreg ? " [shadowed]" : ""); ++ if (cc->sreg) ++ cx_sandor(cc->sreg, cc->reg, mask, value); ++ else ++ cx_andor(cc->reg, mask, value); ++ return 0; ++} ++ ++static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct cx88_core *core = ++ container_of(ctrl->handler, struct cx88_core, audio_hdl); ++ const struct cx88_ctrl *cc = ctrl->priv; ++ u32 value,mask; ++ ++ /* Pass changes onto any WM8775 */ ++ if (core->board.audio_chip == V4L2_IDENT_WM8775) { ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ wm8775_s_ctrl(core, ctrl->id, ctrl->val); ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ? ++ (0x90 + ctrl->val) << 8 : 0); ++ break; ++ case V4L2_CID_AUDIO_BALANCE: ++ wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ mask = cc->mask; ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_BALANCE: ++ value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40); ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ value = 0x3f - (ctrl->val & 0x3f); ++ break; ++ default: ++ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask; ++ break; ++ } ++ dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", ++ ctrl->id, ctrl->name, ctrl->val, cc->reg, value, ++ mask, cc->sreg ? " [shadowed]" : ""); ++ if (cc->sreg) ++ cx_sandor(cc->sreg, cc->reg, mask, value); ++ else ++ cx_andor(cc->reg, mask, value); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* VIDEO IOCTLS */ ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8800_fh *fh = priv; ++ struct cx8800_dev *dev = fh->dev; ++ ++ f->fmt.pix.width = dev->width; ++ f->fmt.pix.height = dev->height; ++ f->fmt.pix.field = fh->vidq.field; ++ f->fmt.pix.pixelformat = dev->fmt->fourcc; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * dev->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ const struct cx8800_fmt *fmt; ++ enum v4l2_field field; ++ unsigned int maxw, maxh; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ maxw = norm_maxw(core->tvnorm); ++ maxh = norm_maxh(core->tvnorm); ++ ++ if (V4L2_FIELD_ANY == field) { ++ field = (f->fmt.pix.height > maxh/2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_BOTTOM; ++ } ++ ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ f->fmt.pix.field = field; ++ v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, ++ &f->fmt.pix.height, 32, maxh, 0, 0); ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct cx8800_fh *fh = priv; ++ struct cx8800_dev *dev = fh->dev; ++ int err = vidioc_try_fmt_vid_cap (file,priv,f); ++ ++ if (0 != err) ++ return err; ++ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ dev->width = f->fmt.pix.width; ++ dev->height = f->fmt.pix.height; ++ fh->vidq.field = f->fmt.pix.field; ++ return 0; ++} ++ ++void cx88_querycap(struct file *file, struct cx88_core *core, ++ struct v4l2_capability *cap) ++{ ++ struct video_device *vdev = video_devdata(file); ++ ++ strlcpy(cap->card, core->board.name, sizeof(cap->card)); ++ cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; ++ if (UNSET != core->board.tuner_type) ++ cap->device_caps |= V4L2_CAP_TUNER; ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_RADIO: ++ cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; ++ break; ++ case VFL_TYPE_GRABBER: ++ cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; ++ break; ++ case VFL_TYPE_VBI: ++ cap->device_caps |= V4L2_CAP_VBI_CAPTURE; ++ break; ++ } ++ cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS; ++ if (core->board.radio.type == CX88_RADIO) ++ cap->capabilities |= V4L2_CAP_RADIO; ++} ++EXPORT_SYMBOL(cx88_querycap); ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; ++ struct cx88_core *core = dev->core; ++ ++ strcpy(cap->driver, "cx8800"); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cx88_querycap(file, core, cap); ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (unlikely(f->index >= ARRAY_SIZE(formats))) ++ return -EINVAL; ++ ++ strlcpy(f->description,formats[f->index].name,sizeof(f->description)); ++ f->pixelformat = formats[f->index].fourcc; ++ ++ return 0; ++} ++ ++static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) ++{ ++ return videobuf_reqbufs(get_queue(file), p); ++} ++ ++static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ return videobuf_querybuf(get_queue(file), p); ++} ++ ++static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ return videobuf_qbuf(get_queue(file), p); ++} ++ ++static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ return videobuf_dqbuf(get_queue(file), p, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_fh *fh = priv; ++ struct cx8800_dev *dev = fh->dev; ++ ++ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) || ++ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ if (unlikely(!res_get(dev, fh, get_resource(file)))) ++ return -EBUSY; ++ return videobuf_streamon(get_queue(file)); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct cx8800_fh *fh = priv; ++ struct cx8800_dev *dev = fh->dev; ++ int err, res; ++ ++ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) || ++ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE)) ++ return -EINVAL; ++ ++ res = get_resource(file); ++ err = videobuf_streamoff(get_queue(file)); ++ if (err < 0) ++ return err; ++ res_free(dev,fh,res); ++ return 0; ++} ++ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ *tvnorm = core->tvnorm; ++ return 0; ++} ++ ++static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ mutex_lock(&core->lock); ++ cx88_set_tvnorm(core,*tvnorms); ++ mutex_unlock(&core->lock); ++ ++ return 0; ++} ++ ++/* only one input in this sample driver */ ++int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) ++{ ++ static const char * const iname[] = { ++ [ CX88_VMUX_COMPOSITE1 ] = "Composite1", ++ [ CX88_VMUX_COMPOSITE2 ] = "Composite2", ++ [ CX88_VMUX_COMPOSITE3 ] = "Composite3", ++ [ CX88_VMUX_COMPOSITE4 ] = "Composite4", ++ [ CX88_VMUX_SVIDEO ] = "S-Video", ++ [ CX88_VMUX_TELEVISION ] = "Television", ++ [ CX88_VMUX_CABLE ] = "Cable TV", ++ [ CX88_VMUX_DVB ] = "DVB", ++ [ CX88_VMUX_DEBUG ] = "for debug only", ++ }; ++ unsigned int n = i->index; ++ ++ if (n >= 4) ++ return -EINVAL; ++ if (0 == INPUT(n).type) ++ return -EINVAL; ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ strcpy(i->name,iname[INPUT(n).type]); ++ if ((CX88_VMUX_TELEVISION == INPUT(n).type) || ++ (CX88_VMUX_CABLE == INPUT(n).type)) { ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ } ++ i->std = CX88_NORMS; ++ return 0; ++} ++EXPORT_SYMBOL(cx88_enum_input); ++ ++static int vidioc_enum_input (struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ return cx88_enum_input (core,i); ++} ++ ++static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ *i = core->input; ++ return 0; ++} ++ ++static int vidioc_s_input (struct file *file, void *priv, unsigned int i) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ if (i >= 4) ++ return -EINVAL; ++ if (0 == INPUT(i).type) ++ return -EINVAL; ++ ++ mutex_lock(&core->lock); ++ cx88_newstation(core); ++ cx88_video_mux(core,i); ++ mutex_unlock(&core->lock); ++ return 0; ++} ++ ++static int vidioc_g_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ u32 reg; ++ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "Television"); ++ t->capability = V4L2_TUNER_CAP_NORM; ++ t->rangehigh = 0xffffffffUL; ++ call_all(core, tuner, g_tuner, t); ++ ++ cx88_get_stereo(core ,t); ++ reg = cx_read(MO_DEVICE_STATUS); ++ t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; ++ return 0; ++} ++ ++static int vidioc_s_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ if (UNSET == core->board.tuner_type) ++ return -EINVAL; ++ if (0 != t->index) ++ return -EINVAL; ++ ++ cx88_set_stereo(core, t->audmode, 1); ++ return 0; ++} ++ ++static int vidioc_g_frequency (struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx8800_fh *fh = priv; ++ struct cx88_core *core = fh->dev->core; ++ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (f->tuner) ++ return -EINVAL; ++ ++ f->frequency = core->freq; ++ ++ call_all(core, tuner, g_frequency, f); ++ ++ return 0; ++} ++ ++int cx88_set_freq (struct cx88_core *core, ++ struct v4l2_frequency *f) ++{ ++ if (unlikely(UNSET == core->board.tuner_type)) ++ return -EINVAL; ++ if (unlikely(f->tuner != 0)) ++ return -EINVAL; ++ ++ mutex_lock(&core->lock); ++ cx88_newstation(core); ++ call_all(core, tuner, s_frequency, f); ++ call_all(core, tuner, g_frequency, f); ++ core->freq = f->frequency; ++ ++ /* When changing channels it is required to reset TVAUDIO */ ++ msleep (10); ++ cx88_set_tvaudio(core); ++ ++ mutex_unlock(&core->lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL(cx88_set_freq); ++ ++static int vidioc_s_frequency (struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct cx8800_fh *fh = priv; ++ struct cx88_core *core = fh->dev->core; ++ ++ return cx88_set_freq(core, f); ++} ++ ++static int vidioc_g_chip_ident(struct file *file, void *priv, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ if (!v4l2_chip_match_host(&chip->match)) ++ return -EINVAL; ++ chip->revision = 0; ++ chip->ident = V4L2_IDENT_UNKNOWN; ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int vidioc_g_register (struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ /* cx2388x has a 24-bit register space */ ++ reg->val = cx_read(reg->reg & 0xffffff); ++ reg->size = 4; ++ return 0; ++} ++ ++static int vidioc_s_register (struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ cx_write(reg->reg & 0xffffff, reg->val); ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------- */ ++/* RADIO ESPECIFIC IOCTLS */ ++/* ----------------------------------------------------------- */ ++ ++static int radio_g_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ if (unlikely(t->index > 0)) ++ return -EINVAL; ++ ++ strcpy(t->name, "Radio"); ++ ++ call_all(core, tuner, g_tuner, t); ++ return 0; ++} ++ ++/* FIXME: Should add a standard for radio */ ++ ++static int radio_s_tuner (struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ if (t->audmode > V4L2_TUNER_MODE_STEREO) ++ t->audmode = V4L2_TUNER_MODE_STEREO; ++ ++ call_all(core, tuner, s_tuner, t); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static void cx8800_vid_timeout(unsigned long data) ++{ ++ struct cx8800_dev *dev = (struct cx8800_dev*)data; ++ struct cx88_core *core = dev->core; ++ struct cx88_dmaqueue *q = &dev->vidq; ++ struct cx88_buffer *buf; ++ unsigned long flags; ++ ++ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]); ++ ++ cx_clear(MO_VID_DMACNTRL, 0x11); ++ cx_clear(VID_CAPTURE_CONTROL, 0x06); ++ ++ spin_lock_irqsave(&dev->slock,flags); ++ while (!list_empty(&q->active)) { ++ buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name, ++ buf, buf->vb.i, (unsigned long)buf->risc.dma); ++ } ++ restart_video_queue(dev,q); ++ spin_unlock_irqrestore(&dev->slock,flags); ++} ++ ++static const char *cx88_vid_irqs[32] = { ++ "y_risci1", "u_risci1", "v_risci1", "vbi_risc1", ++ "y_risci2", "u_risci2", "v_risci2", "vbi_risc2", ++ "y_oflow", "u_oflow", "v_oflow", "vbi_oflow", ++ "y_sync", "u_sync", "v_sync", "vbi_sync", ++ "opc_err", "par_err", "rip_err", "pci_abort", ++}; ++ ++static void cx8800_vid_irq(struct cx8800_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ u32 status, mask, count; ++ ++ status = cx_read(MO_VID_INTSTAT); ++ mask = cx_read(MO_VID_INTMSK); ++ if (0 == (status & mask)) ++ return; ++ cx_write(MO_VID_INTSTAT, status); ++ if (irq_debug || (status & mask & ~0xff)) ++ cx88_print_irqbits(core->name, "irq vid", ++ cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs), ++ status, mask); ++ ++ /* risc op code error */ ++ if (status & (1 << 16)) { ++ printk(KERN_WARNING "%s/0: video risc op code error\n",core->name); ++ cx_clear(MO_VID_DMACNTRL, 0x11); ++ cx_clear(VID_CAPTURE_CONTROL, 0x06); ++ cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]); ++ } ++ ++ /* risc1 y */ ++ if (status & 0x01) { ++ spin_lock(&dev->slock); ++ count = cx_read(MO_VIDY_GPCNT); ++ cx88_wakeup(core, &dev->vidq, count); ++ spin_unlock(&dev->slock); ++ } ++ ++ /* risc1 vbi */ ++ if (status & 0x08) { ++ spin_lock(&dev->slock); ++ count = cx_read(MO_VBI_GPCNT); ++ cx88_wakeup(core, &dev->vbiq, count); ++ spin_unlock(&dev->slock); ++ } ++ ++ /* risc2 y */ ++ if (status & 0x10) { ++ dprintk(2,"stopper video\n"); ++ spin_lock(&dev->slock); ++ restart_video_queue(dev,&dev->vidq); ++ spin_unlock(&dev->slock); ++ } ++ ++ /* risc2 vbi */ ++ if (status & 0x80) { ++ dprintk(2,"stopper vbi\n"); ++ spin_lock(&dev->slock); ++ cx8800_restart_vbi_queue(dev,&dev->vbiq); ++ spin_unlock(&dev->slock); ++ } ++} ++ ++static irqreturn_t cx8800_irq(int irq, void *dev_id) ++{ ++ struct cx8800_dev *dev = dev_id; ++ struct cx88_core *core = dev->core; ++ u32 status; ++ int loop, handled = 0; ++ ++ for (loop = 0; loop < 10; loop++) { ++ status = cx_read(MO_PCI_INTSTAT) & ++ (core->pci_irqmask | PCI_INT_VIDINT); ++ if (0 == status) ++ goto out; ++ cx_write(MO_PCI_INTSTAT, status); ++ handled = 1; ++ ++ if (status & core->pci_irqmask) ++ cx88_core_irq(core,status); ++ if (status & PCI_INT_VIDINT) ++ cx8800_vid_irq(dev); ++ } ++ if (10 == loop) { ++ printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", ++ core->name); ++ cx_write(MO_PCI_INTMSK,0); ++ } ++ ++ out: ++ return IRQ_RETVAL(handled); ++} ++ ++/* ----------------------------------------------------------- */ ++/* exported stuff */ ++ ++static const struct v4l2_file_operations video_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .release = video_release, ++ .read = video_read, ++ .poll = video_poll, ++ .mmap = video_mmap, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops video_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_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_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++ .vidioc_g_chip_ident = vidioc_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vidioc_g_register, ++ .vidioc_s_register = vidioc_s_register, ++#endif ++}; ++ ++static const struct video_device cx8800_video_template = { ++ .name = "cx8800-video", ++ .fops = &video_fops, ++ .ioctl_ops = &video_ioctl_ops, ++ .tvnorms = CX88_NORMS, ++}; ++ ++static const struct v4l2_ioctl_ops vbi_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt, ++ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt, ++ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt, ++ .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_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_g_chip_ident = vidioc_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vidioc_g_register, ++ .vidioc_s_register = vidioc_s_register, ++#endif ++}; ++ ++static const struct video_device cx8800_vbi_template = { ++ .name = "cx8800-vbi", ++ .fops = &video_fops, ++ .ioctl_ops = &vbi_ioctl_ops, ++ .tvnorms = CX88_NORMS, ++}; ++ ++static const struct v4l2_file_operations radio_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .poll = v4l2_ctrl_poll, ++ .release = video_release, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops radio_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_g_tuner = radio_g_tuner, ++ .vidioc_s_tuner = radio_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++ .vidioc_g_chip_ident = vidioc_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vidioc_g_register, ++ .vidioc_s_register = vidioc_s_register, ++#endif ++}; ++ ++static const struct video_device cx8800_radio_template = { ++ .name = "cx8800-radio", ++ .fops = &radio_fops, ++ .ioctl_ops = &radio_ioctl_ops, ++}; ++ ++static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = { ++ .s_ctrl = cx8800_s_vid_ctrl, ++}; ++ ++static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = { ++ .s_ctrl = cx8800_s_aud_ctrl, ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++static void cx8800_unregister_video(struct cx8800_dev *dev) ++{ ++ if (dev->radio_dev) { ++ if (video_is_registered(dev->radio_dev)) ++ video_unregister_device(dev->radio_dev); ++ else ++ video_device_release(dev->radio_dev); ++ dev->radio_dev = NULL; ++ } ++ if (dev->vbi_dev) { ++ if (video_is_registered(dev->vbi_dev)) ++ video_unregister_device(dev->vbi_dev); ++ else ++ video_device_release(dev->vbi_dev); ++ dev->vbi_dev = NULL; ++ } ++ if (dev->video_dev) { ++ if (video_is_registered(dev->video_dev)) ++ video_unregister_device(dev->video_dev); ++ else ++ video_device_release(dev->video_dev); ++ dev->video_dev = NULL; ++ } ++} ++ ++static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct cx8800_dev *dev; ++ struct cx88_core *core; ++ int err; ++ int i; ++ ++ dev = kzalloc(sizeof(*dev),GFP_KERNEL); ++ if (NULL == dev) ++ return -ENOMEM; ++ ++ /* pci init */ ++ dev->pci = pci_dev; ++ if (pci_enable_device(pci_dev)) { ++ err = -EIO; ++ goto fail_free; ++ } ++ core = cx88_core_get(dev->pci); ++ if (NULL == core) { ++ err = -EINVAL; ++ goto fail_free; ++ } ++ dev->core = core; ++ ++ /* print pci info */ ++ dev->pci_rev = pci_dev->revision; ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); ++ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", core->name, ++ pci_name(pci_dev), dev->pci_rev, pci_dev->irq, ++ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); ++ ++ pci_set_master(pci_dev); ++ if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) { ++ printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); ++ err = -EIO; ++ goto fail_core; ++ } ++ ++ /* initialize driver struct */ ++ spin_lock_init(&dev->slock); ++ core->tvnorm = V4L2_STD_NTSC_M; ++ ++ /* init video dma queues */ ++ INIT_LIST_HEAD(&dev->vidq.active); ++ INIT_LIST_HEAD(&dev->vidq.queued); ++ dev->vidq.timeout.function = cx8800_vid_timeout; ++ dev->vidq.timeout.data = (unsigned long)dev; ++ init_timer(&dev->vidq.timeout); ++ cx88_risc_stopper(dev->pci,&dev->vidq.stopper, ++ MO_VID_DMACNTRL,0x11,0x00); ++ ++ /* init vbi dma queues */ ++ INIT_LIST_HEAD(&dev->vbiq.active); ++ INIT_LIST_HEAD(&dev->vbiq.queued); ++ dev->vbiq.timeout.function = cx8800_vbi_timeout; ++ dev->vbiq.timeout.data = (unsigned long)dev; ++ init_timer(&dev->vbiq.timeout); ++ cx88_risc_stopper(dev->pci,&dev->vbiq.stopper, ++ MO_VID_DMACNTRL,0x88,0x00); ++ ++ /* get irq */ ++ err = request_irq(pci_dev->irq, cx8800_irq, ++ IRQF_SHARED | IRQF_DISABLED, core->name, dev); ++ if (err < 0) { ++ printk(KERN_ERR "%s/0: can't get IRQ %d\n", ++ core->name,pci_dev->irq); ++ goto fail_core; ++ } ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask); ++ ++ for (i = 0; i < CX8800_AUD_CTLS; i++) { ++ const struct cx88_ctrl *cc = &cx8800_aud_ctls[i]; ++ struct v4l2_ctrl *vc; ++ ++ vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops, ++ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value); ++ if (vc == NULL) { ++ err = core->audio_hdl.error; ++ goto fail_core; ++ } ++ vc->priv = (void *)cc; ++ } ++ ++ for (i = 0; i < CX8800_VID_CTLS; i++) { ++ const struct cx88_ctrl *cc = &cx8800_vid_ctls[i]; ++ struct v4l2_ctrl *vc; ++ ++ vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops, ++ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value); ++ if (vc == NULL) { ++ err = core->video_hdl.error; ++ goto fail_core; ++ } ++ vc->priv = (void *)cc; ++ if (vc->id == V4L2_CID_CHROMA_AGC) ++ core->chroma_agc = vc; ++ } ++ v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL); ++ ++ /* load and configure helper modules */ ++ ++ if (core->board.audio_chip == V4L2_IDENT_WM8775) { ++ struct i2c_board_info wm8775_info = { ++ .type = "wm8775", ++ .addr = 0x36 >> 1, ++ .platform_data = &core->wm8775_data, ++ }; ++ struct v4l2_subdev *sd; ++ ++ if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1) ++ core->wm8775_data.is_nova_s = true; ++ else ++ core->wm8775_data.is_nova_s = false; ++ ++ sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap, ++ &wm8775_info, NULL); ++ if (sd != NULL) { ++ core->sd_wm8775 = sd; ++ sd->grp_id = WM8775_GID; ++ } ++ } ++ ++ if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { ++ /* This probes for a tda9874 as is used on some ++ Pixelview Ultra boards. */ ++ v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, ++ "tvaudio", 0, I2C_ADDRS(0xb0 >> 1)); ++ } ++ ++ switch (core->boardnr) { ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: ++ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: { ++ static const struct i2c_board_info rtc_info = { ++ I2C_BOARD_INFO("isl1208", 0x6f) ++ }; ++ ++ request_module("rtc-isl1208"); ++ core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); ++ } ++ /* break intentionally omitted */ ++ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ++ request_module("ir-kbd-i2c"); ++ } ++ ++ /* Sets device info at pci_dev */ ++ pci_set_drvdata(pci_dev, dev); ++ ++ dev->width = 320; ++ dev->height = 240; ++ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); ++ ++ /* initial device configuration */ ++ mutex_lock(&core->lock); ++ cx88_set_tvnorm(core, core->tvnorm); ++ v4l2_ctrl_handler_setup(&core->video_hdl); ++ v4l2_ctrl_handler_setup(&core->audio_hdl); ++ cx88_video_mux(core, 0); ++ ++ /* register v4l devices */ ++ dev->video_dev = cx88_vdev_init(core,dev->pci, ++ &cx8800_video_template,"video"); ++ video_set_drvdata(dev->video_dev, dev); ++ dev->video_dev->ctrl_handler = &core->video_hdl; ++ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, ++ video_nr[core->nr]); ++ if (err < 0) { ++ printk(KERN_ERR "%s/0: can't register video device\n", ++ core->name); ++ goto fail_unreg; ++ } ++ printk(KERN_INFO "%s/0: registered device %s [v4l2]\n", ++ core->name, video_device_node_name(dev->video_dev)); ++ ++ dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi"); ++ video_set_drvdata(dev->vbi_dev, dev); ++ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, ++ vbi_nr[core->nr]); ++ if (err < 0) { ++ printk(KERN_ERR "%s/0: can't register vbi device\n", ++ core->name); ++ goto fail_unreg; ++ } ++ printk(KERN_INFO "%s/0: registered device %s\n", ++ core->name, video_device_node_name(dev->vbi_dev)); ++ ++ if (core->board.radio.type == CX88_RADIO) { ++ dev->radio_dev = cx88_vdev_init(core,dev->pci, ++ &cx8800_radio_template,"radio"); ++ video_set_drvdata(dev->radio_dev, dev); ++ dev->radio_dev->ctrl_handler = &core->audio_hdl; ++ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, ++ radio_nr[core->nr]); ++ if (err < 0) { ++ printk(KERN_ERR "%s/0: can't register radio device\n", ++ core->name); ++ goto fail_unreg; ++ } ++ printk(KERN_INFO "%s/0: registered device %s\n", ++ core->name, video_device_node_name(dev->radio_dev)); ++ } ++ ++ /* start tvaudio thread */ ++ if (core->board.tuner_type != TUNER_ABSENT) { ++ core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio"); ++ if (IS_ERR(core->kthread)) { ++ err = PTR_ERR(core->kthread); ++ printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n", ++ core->name, err); ++ } ++ } ++ mutex_unlock(&core->lock); ++ ++ return 0; ++ ++fail_unreg: ++ cx8800_unregister_video(dev); ++ free_irq(pci_dev->irq, dev); ++ mutex_unlock(&core->lock); ++fail_core: ++ cx88_core_put(core,dev->pci); ++fail_free: ++ kfree(dev); ++ return err; ++} ++ ++static void __devexit cx8800_finidev(struct pci_dev *pci_dev) ++{ ++ struct cx8800_dev *dev = pci_get_drvdata(pci_dev); ++ struct cx88_core *core = dev->core; ++ ++ /* stop thread */ ++ if (core->kthread) { ++ kthread_stop(core->kthread); ++ core->kthread = NULL; ++ } ++ ++ if (core->ir) ++ cx88_ir_stop(core); ++ ++ cx88_shutdown(core); /* FIXME */ ++ pci_disable_device(pci_dev); ++ ++ /* unregister stuff */ ++ ++ free_irq(pci_dev->irq, dev); ++ cx8800_unregister_video(dev); ++ pci_set_drvdata(pci_dev, NULL); ++ ++ /* free memory */ ++ btcx_riscmem_free(dev->pci,&dev->vidq.stopper); ++ cx88_core_put(core,dev->pci); ++ kfree(dev); ++} ++ ++#ifdef CONFIG_PM ++static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) ++{ ++ struct cx8800_dev *dev = pci_get_drvdata(pci_dev); ++ struct cx88_core *core = dev->core; ++ ++ /* stop video+vbi capture */ ++ spin_lock(&dev->slock); ++ if (!list_empty(&dev->vidq.active)) { ++ printk("%s/0: suspend video\n", core->name); ++ stop_video_dma(dev); ++ del_timer(&dev->vidq.timeout); ++ } ++ if (!list_empty(&dev->vbiq.active)) { ++ printk("%s/0: suspend vbi\n", core->name); ++ cx8800_stop_vbi_dma(dev); ++ del_timer(&dev->vbiq.timeout); ++ } ++ spin_unlock(&dev->slock); ++ ++ if (core->ir) ++ cx88_ir_stop(core); ++ /* FIXME -- shutdown device */ ++ cx88_shutdown(core); ++ ++ pci_save_state(pci_dev); ++ if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { ++ pci_disable_device(pci_dev); ++ dev->state.disabled = 1; ++ } ++ return 0; ++} ++ ++static int cx8800_resume(struct pci_dev *pci_dev) ++{ ++ struct cx8800_dev *dev = pci_get_drvdata(pci_dev); ++ struct cx88_core *core = dev->core; ++ int err; ++ ++ if (dev->state.disabled) { ++ err=pci_enable_device(pci_dev); ++ if (err) { ++ printk(KERN_ERR "%s/0: can't enable device\n", ++ core->name); ++ return err; ++ } ++ ++ dev->state.disabled = 0; ++ } ++ err= pci_set_power_state(pci_dev, PCI_D0); ++ if (err) { ++ printk(KERN_ERR "%s/0: can't set power state\n", core->name); ++ pci_disable_device(pci_dev); ++ dev->state.disabled = 1; ++ ++ return err; ++ } ++ pci_restore_state(pci_dev); ++ ++ /* FIXME: re-initialize hardware */ ++ cx88_reset(core); ++ if (core->ir) ++ cx88_ir_start(core); ++ ++ cx_set(MO_PCI_INTMSK, core->pci_irqmask); ++ ++ /* restart video+vbi capture */ ++ spin_lock(&dev->slock); ++ if (!list_empty(&dev->vidq.active)) { ++ printk("%s/0: resume video\n", core->name); ++ restart_video_queue(dev,&dev->vidq); ++ } ++ if (!list_empty(&dev->vbiq.active)) { ++ printk("%s/0: resume vbi\n", core->name); ++ cx8800_restart_vbi_queue(dev,&dev->vbiq); ++ } ++ spin_unlock(&dev->slock); ++ ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------- */ ++ ++static const struct pci_device_id cx8800_pci_tbl[] = { ++ { ++ .vendor = 0x14f1, ++ .device = 0x8800, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ },{ ++ /* --- end of list --- */ ++ } ++}; ++MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); ++ ++static struct pci_driver cx8800_pci_driver = { ++ .name = "cx8800", ++ .id_table = cx8800_pci_tbl, ++ .probe = cx8800_initdev, ++ .remove = __devexit_p(cx8800_finidev), ++#ifdef CONFIG_PM ++ .suspend = cx8800_suspend, ++ .resume = cx8800_resume, ++#endif ++}; ++ ++static int __init cx8800_init(void) ++{ ++ printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n", ++ CX88_VERSION); ++ return pci_register_driver(&cx8800_pci_driver); ++} ++ ++static void __exit cx8800_fini(void) ++{ ++ pci_unregister_driver(&cx8800_pci_driver); ++} ++ ++module_init(cx8800_init); ++module_exit(cx8800_fini); +diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.c b/drivers/media/pci/cx88/cx88-vp3054-i2c.c +new file mode 100644 +index 0000000..deede6e +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.c +@@ -0,0 +1,158 @@ ++/* ++ ++ cx88-vp3054-i2c.c -- support for the secondary I2C bus of the ++ DNTV Live! DVB-T Pro (VP-3054), wired as: ++ GPIO[0] -> SCL, GPIO[1] -> SDA ++ ++ (c) 2005 Chris Pascoe ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "cx88.h" ++#include "cx88-vp3054-i2c.h" ++ ++MODULE_DESCRIPTION("driver for cx2388x VP3054 design"); ++MODULE_AUTHOR("Chris Pascoe "); ++MODULE_LICENSE("GPL"); ++ ++/* ----------------------------------------------------------------------- */ ++ ++static void vp3054_bit_setscl(void *data, int state) ++{ ++ struct cx8802_dev *dev = data; ++ struct cx88_core *core = dev->core; ++ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; ++ ++ if (state) { ++ vp3054_i2c->state |= 0x0001; /* SCL high */ ++ vp3054_i2c->state &= ~0x0100; /* external pullup */ ++ } else { ++ vp3054_i2c->state &= ~0x0001; /* SCL low */ ++ vp3054_i2c->state |= 0x0100; /* drive pin */ ++ } ++ cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state); ++ cx_read(MO_GP0_IO); ++} ++ ++static void vp3054_bit_setsda(void *data, int state) ++{ ++ struct cx8802_dev *dev = data; ++ struct cx88_core *core = dev->core; ++ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; ++ ++ if (state) { ++ vp3054_i2c->state |= 0x0002; /* SDA high */ ++ vp3054_i2c->state &= ~0x0200; /* tristate pin */ ++ } else { ++ vp3054_i2c->state &= ~0x0002; /* SDA low */ ++ vp3054_i2c->state |= 0x0200; /* drive pin */ ++ } ++ cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state); ++ cx_read(MO_GP0_IO); ++} ++ ++static int vp3054_bit_getscl(void *data) ++{ ++ struct cx8802_dev *dev = data; ++ struct cx88_core *core = dev->core; ++ u32 state; ++ ++ state = cx_read(MO_GP0_IO); ++ return (state & 0x01) ? 1 : 0; ++} ++ ++static int vp3054_bit_getsda(void *data) ++{ ++ struct cx8802_dev *dev = data; ++ struct cx88_core *core = dev->core; ++ u32 state; ++ ++ state = cx_read(MO_GP0_IO); ++ return (state & 0x02) ? 1 : 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct i2c_algo_bit_data vp3054_i2c_algo_template = { ++ .setsda = vp3054_bit_setsda, ++ .setscl = vp3054_bit_setscl, ++ .getsda = vp3054_bit_getsda, ++ .getscl = vp3054_bit_getscl, ++ .udelay = 16, ++ .timeout = 200, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++int vp3054_i2c_probe(struct cx8802_dev *dev) ++{ ++ struct cx88_core *core = dev->core; ++ struct vp3054_i2c_state *vp3054_i2c; ++ int rc; ++ ++ if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) ++ return 0; ++ ++ vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL); ++ if (vp3054_i2c == NULL) ++ return -ENOMEM; ++ dev->vp3054 = vp3054_i2c; ++ ++ vp3054_i2c->algo = vp3054_i2c_algo_template; ++ ++ vp3054_i2c->adap.dev.parent = &dev->pci->dev; ++ strlcpy(vp3054_i2c->adap.name, core->name, ++ sizeof(vp3054_i2c->adap.name)); ++ vp3054_i2c->adap.owner = THIS_MODULE; ++ vp3054_i2c->algo.data = dev; ++ i2c_set_adapdata(&vp3054_i2c->adap, dev); ++ vp3054_i2c->adap.algo_data = &vp3054_i2c->algo; ++ ++ vp3054_bit_setscl(dev,1); ++ vp3054_bit_setsda(dev,1); ++ ++ rc = i2c_bit_add_bus(&vp3054_i2c->adap); ++ if (0 != rc) { ++ printk("%s: vp3054_i2c register FAILED\n", core->name); ++ ++ kfree(dev->vp3054); ++ dev->vp3054 = NULL; ++ } ++ ++ return rc; ++} ++ ++void vp3054_i2c_remove(struct cx8802_dev *dev) ++{ ++ struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; ++ ++ if (vp3054_i2c == NULL || ++ dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) ++ return; ++ ++ i2c_del_adapter(&vp3054_i2c->adap); ++ kfree(vp3054_i2c); ++} ++ ++EXPORT_SYMBOL(vp3054_i2c_probe); ++EXPORT_SYMBOL(vp3054_i2c_remove); +diff --git a/drivers/media/pci/cx88/cx88-vp3054-i2c.h b/drivers/media/pci/cx88/cx88-vp3054-i2c.h +new file mode 100644 +index 0000000..be99c93 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88-vp3054-i2c.h +@@ -0,0 +1,41 @@ ++/* ++ ++ cx88-vp3054-i2c.h -- support for the secondary I2C bus of the ++ DNTV Live! DVB-T Pro (VP-3054), wired as: ++ GPIO[0] -> SCL, GPIO[1] -> SDA ++ ++ (c) 2005 Chris Pascoe ++ ++ 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. ++ ++*/ ++ ++/* ----------------------------------------------------------------------- */ ++struct vp3054_i2c_state { ++ struct i2c_adapter adap; ++ struct i2c_algo_bit_data algo; ++ u32 state; ++}; ++ ++/* ----------------------------------------------------------------------- */ ++#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) ++int vp3054_i2c_probe(struct cx8802_dev *dev); ++void vp3054_i2c_remove(struct cx8802_dev *dev); ++#else ++static inline int vp3054_i2c_probe(struct cx8802_dev *dev) ++{ return 0; } ++static inline void vp3054_i2c_remove(struct cx8802_dev *dev) ++{ } ++#endif +diff --git a/drivers/media/pci/cx88/cx88.h b/drivers/media/pci/cx88/cx88.h +new file mode 100644 +index 0000000..ba0dba4 +--- /dev/null ++++ b/drivers/media/pci/cx88/cx88.h +@@ -0,0 +1,748 @@ ++/* ++ * ++ * v4l2 device driver for cx2388x based TV cards ++ * ++ * (c) 2003,04 Gerd Knorr [SUSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "btcx-risc.h" ++#include "cx88-reg.h" ++#include "tuner-xc2028.h" ++ ++#include ++ ++#define CX88_VERSION "0.0.9" ++ ++#define UNSET (-1U) ++ ++#define CX88_MAXBOARDS 8 ++ ++/* Max number of inputs by card */ ++#define MAX_CX88_INPUT 8 ++ ++/* ----------------------------------------------------------- */ ++/* defines and enums */ ++ ++/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */ ++#define CX88_NORMS (V4L2_STD_ALL \ ++ & ~V4L2_STD_PAL_H \ ++ & ~V4L2_STD_NTSC_M_KR \ ++ & ~V4L2_STD_SECAM_LC) ++ ++#define FORMAT_FLAGS_PACKED 0x01 ++#define FORMAT_FLAGS_PLANAR 0x02 ++ ++#define VBI_LINE_COUNT 17 ++#define VBI_LINE_LENGTH 2048 ++ ++#define AUD_RDS_LINES 4 ++ ++/* need "shadow" registers for some write-only ones ... */ ++#define SHADOW_AUD_VOL_CTL 1 ++#define SHADOW_AUD_BAL_CTL 2 ++#define SHADOW_MAX 3 ++ ++/* FM Radio deemphasis type */ ++enum cx88_deemph_type { ++ FM_NO_DEEMPH = 0, ++ FM_DEEMPH_50, ++ FM_DEEMPH_75 ++}; ++ ++enum cx88_board_type { ++ CX88_BOARD_NONE = 0, ++ CX88_MPEG_DVB, ++ CX88_MPEG_BLACKBIRD ++}; ++ ++enum cx8802_board_access { ++ CX8802_DRVCTL_SHARED = 1, ++ CX8802_DRVCTL_EXCLUSIVE = 2, ++}; ++ ++/* ----------------------------------------------------------- */ ++/* tv norms */ ++ ++static inline unsigned int norm_maxw(v4l2_std_id norm) ++{ ++ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; ++} ++ ++ ++static inline unsigned int norm_maxh(v4l2_std_id norm) ++{ ++ return (norm & V4L2_STD_625_50) ? 576 : 480; ++} ++ ++/* ----------------------------------------------------------- */ ++/* static data */ ++ ++struct cx8800_fmt { ++ const char *name; ++ u32 fourcc; /* v4l2 format id */ ++ int depth; ++ int flags; ++ u32 cxformat; ++}; ++ ++/* ----------------------------------------------------------- */ ++/* SRAM memory management data (see cx88-core.c) */ ++ ++#define SRAM_CH21 0 /* video */ ++#define SRAM_CH22 1 ++#define SRAM_CH23 2 ++#define SRAM_CH24 3 /* vbi */ ++#define SRAM_CH25 4 /* audio */ ++#define SRAM_CH26 5 ++#define SRAM_CH28 6 /* mpeg */ ++#define SRAM_CH27 7 /* audio rds */ ++/* more */ ++ ++struct sram_channel { ++ const char *name; ++ u32 cmds_start; ++ u32 ctrl_start; ++ u32 cdt; ++ u32 fifo_start; ++ u32 fifo_size; ++ u32 ptr1_reg; ++ u32 ptr2_reg; ++ u32 cnt1_reg; ++ u32 cnt2_reg; ++}; ++extern const struct sram_channel cx88_sram_channels[]; ++ ++/* ----------------------------------------------------------- */ ++/* card configuration */ ++ ++#define CX88_BOARD_NOAUTO UNSET ++#define CX88_BOARD_UNKNOWN 0 ++#define CX88_BOARD_HAUPPAUGE 1 ++#define CX88_BOARD_GDI 2 ++#define CX88_BOARD_PIXELVIEW 3 ++#define CX88_BOARD_ATI_WONDER_PRO 4 ++#define CX88_BOARD_WINFAST2000XP_EXPERT 5 ++#define CX88_BOARD_AVERTV_STUDIO_303 6 ++#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7 ++#define CX88_BOARD_WINFAST_DV2000 8 ++#define CX88_BOARD_LEADTEK_PVR2000 9 ++#define CX88_BOARD_IODATA_GVVCP3PCI 10 ++#define CX88_BOARD_PROLINK_PLAYTVPVR 11 ++#define CX88_BOARD_ASUS_PVR_416 12 ++#define CX88_BOARD_MSI_TVANYWHERE 13 ++#define CX88_BOARD_KWORLD_DVB_T 14 ++#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15 ++#define CX88_BOARD_KWORLD_LTV883 16 ++#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17 ++#define CX88_BOARD_HAUPPAUGE_DVB_T1 18 ++#define CX88_BOARD_CONEXANT_DVB_T1 19 ++#define CX88_BOARD_PROVIDEO_PV259 20 ++#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21 ++#define CX88_BOARD_PCHDTV_HD3000 22 ++#define CX88_BOARD_DNTV_LIVE_DVB_T 23 ++#define CX88_BOARD_HAUPPAUGE_ROSLYN 24 ++#define CX88_BOARD_DIGITALLOGIC_MEC 25 ++#define CX88_BOARD_IODATA_GVBCTV7E 26 ++#define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27 ++#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28 ++#define CX88_BOARD_ADSTECH_DVB_T_PCI 29 ++#define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30 ++#define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31 ++#define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32 ++#define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33 ++#define CX88_BOARD_ATI_HDTVWONDER 34 ++#define CX88_BOARD_WINFAST_DTV1000 35 ++#define CX88_BOARD_AVERTV_303 36 ++#define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37 ++#define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38 ++#define CX88_BOARD_KWORLD_DVBS_100 39 ++#define CX88_BOARD_HAUPPAUGE_HVR1100 40 ++#define CX88_BOARD_HAUPPAUGE_HVR1100LP 41 ++#define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42 ++#define CX88_BOARD_KWORLD_DVB_T_CX22702 43 ++#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44 ++#define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45 ++#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46 ++#define CX88_BOARD_PCHDTV_HD5500 47 ++#define CX88_BOARD_KWORLD_MCE200_DELUXE 48 ++#define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49 ++#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50 ++#define CX88_BOARD_WINFAST_DTV2000H 51 ++#define CX88_BOARD_GENIATECH_DVBS 52 ++#define CX88_BOARD_HAUPPAUGE_HVR3000 53 ++#define CX88_BOARD_NORWOOD_MICRO 54 ++#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55 ++#define CX88_BOARD_HAUPPAUGE_HVR1300 56 ++#define CX88_BOARD_ADSTECH_PTV_390 57 ++#define CX88_BOARD_PINNACLE_PCTV_HD_800i 58 ++#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59 ++#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60 ++#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61 ++#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62 ++#define CX88_BOARD_GENIATECH_X8000_MT 63 ++#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64 ++#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65 ++#define CX88_BOARD_PROLINK_PV_8000GT 66 ++#define CX88_BOARD_KWORLD_ATSC_120 67 ++#define CX88_BOARD_HAUPPAUGE_HVR4000 68 ++#define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69 ++#define CX88_BOARD_TEVII_S460 70 ++#define CX88_BOARD_OMICOM_SS4_PCI 71 ++#define CX88_BOARD_TBS_8920 72 ++#define CX88_BOARD_TEVII_S420 73 ++#define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74 ++#define CX88_BOARD_PROF_7300 75 ++#define CX88_BOARD_SATTRADE_ST4200 76 ++#define CX88_BOARD_TBS_8910 77 ++#define CX88_BOARD_PROF_6200 78 ++#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 ++#define CX88_BOARD_HAUPPAUGE_IRONLY 80 ++#define CX88_BOARD_WINFAST_DTV1800H 81 ++#define CX88_BOARD_WINFAST_DTV2000H_J 82 ++#define CX88_BOARD_PROF_7301 83 ++#define CX88_BOARD_SAMSUNG_SMT_7020 84 ++#define CX88_BOARD_TWINHAN_VP1027_DVBS 85 ++#define CX88_BOARD_TEVII_S464 86 ++#define CX88_BOARD_WINFAST_DTV2000H_PLUS 87 ++#define CX88_BOARD_WINFAST_DTV1800H_XC4000 88 ++#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F36 89 ++#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL_6F43 90 ++ ++enum cx88_itype { ++ CX88_VMUX_COMPOSITE1 = 1, ++ CX88_VMUX_COMPOSITE2, ++ CX88_VMUX_COMPOSITE3, ++ CX88_VMUX_COMPOSITE4, ++ CX88_VMUX_SVIDEO, ++ CX88_VMUX_TELEVISION, ++ CX88_VMUX_CABLE, ++ CX88_VMUX_DVB, ++ CX88_VMUX_DEBUG, ++ CX88_RADIO, ++}; ++ ++struct cx88_input { ++ enum cx88_itype type; ++ u32 gpio0, gpio1, gpio2, gpio3; ++ unsigned int vmux:2; ++ unsigned int audioroute:4; ++}; ++ ++struct cx88_board { ++ const char *name; ++ unsigned int tuner_type; ++ unsigned int radio_type; ++ unsigned char tuner_addr; ++ unsigned char radio_addr; ++ int tda9887_conf; ++ struct cx88_input input[MAX_CX88_INPUT]; ++ struct cx88_input radio; ++ enum cx88_board_type mpeg; ++ unsigned int audio_chip; ++ int num_frontends; ++ ++ /* Used for I2S devices */ ++ int i2sinputcntl; ++}; ++ ++struct cx88_subid { ++ u16 subvendor; ++ u16 subdevice; ++ u32 card; ++}; ++ ++enum cx88_tvaudio { ++ WW_NONE = 1, ++ WW_BTSC, ++ WW_BG, ++ WW_DK, ++ WW_I, ++ WW_L, ++ WW_EIAJ, ++ WW_I2SPT, ++ WW_FM, ++ WW_I2SADC, ++ WW_M ++}; ++ ++#define INPUT(nr) (core->board.input[nr]) ++ ++/* ----------------------------------------------------------- */ ++/* device / file handle status */ ++ ++#define RESOURCE_OVERLAY 1 ++#define RESOURCE_VIDEO 2 ++#define RESOURCE_VBI 4 ++ ++#define BUFFER_TIMEOUT msecs_to_jiffies(2000) ++ ++/* buffer for one video frame */ ++struct cx88_buffer { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* cx88 specific */ ++ unsigned int bpl; ++ struct btcx_riscmem risc; ++ const struct cx8800_fmt *fmt; ++ u32 count; ++}; ++ ++struct cx88_dmaqueue { ++ struct list_head active; ++ struct list_head queued; ++ struct timer_list timeout; ++ struct btcx_riscmem stopper; ++ u32 count; ++}; ++ ++struct cx88_core { ++ struct list_head devlist; ++ atomic_t refcount; ++ ++ /* board name */ ++ int nr; ++ char name[32]; ++ ++ /* pci stuff */ ++ int pci_bus; ++ int pci_slot; ++ u32 __iomem *lmmio; ++ u8 __iomem *bmmio; ++ u32 shadow[SHADOW_MAX]; ++ int pci_irqmask; ++ ++ /* i2c i/o */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_algo_bit_data i2c_algo; ++ struct i2c_client i2c_client; ++ u32 i2c_state, i2c_rc; ++ ++ /* config info -- analog */ ++ struct v4l2_device v4l2_dev; ++ struct v4l2_ctrl_handler video_hdl; ++ struct v4l2_ctrl *chroma_agc; ++ struct v4l2_ctrl_handler audio_hdl; ++ struct v4l2_subdev *sd_wm8775; ++ struct i2c_client *i2c_rtc; ++ unsigned int boardnr; ++ struct cx88_board board; ++ ++ /* Supported V4L _STD_ tuner formats */ ++ unsigned int tuner_formats; ++ ++ /* config info -- dvb */ ++#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) ++ int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); ++#endif ++ void (*gate_ctrl)(struct cx88_core *core, int open); ++ ++ /* state info */ ++ struct task_struct *kthread; ++ v4l2_std_id tvnorm; ++ enum cx88_tvaudio tvaudio; ++ u32 audiomode_manual; ++ u32 audiomode_current; ++ u32 input; ++ u32 last_analog_input; ++ u32 astat; ++ u32 use_nicam; ++ unsigned long last_change; ++ ++ /* IR remote control state */ ++ struct cx88_IR *ir; ++ ++ /* I2C remote data */ ++ struct IR_i2c_init_data init_data; ++ struct wm8775_platform_data wm8775_data; ++ ++ struct mutex lock; ++ /* various v4l controls */ ++ u32 freq; ++ int users; ++ int mpeg_users; ++ ++ /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ ++ struct cx8802_dev *dvbdev; ++ enum cx88_board_type active_type_id; ++ int active_ref; ++ int active_fe_id; ++}; ++ ++static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct cx88_core, v4l2_dev); ++} ++ ++#define call_hw(core, grpid, o, f, args...) \ ++ do { \ ++ if (!core->i2c_rc) { \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 1); \ ++ v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 0); \ ++ } \ ++ } while (0) ++ ++#define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args) ++ ++#define WM8775_GID (1 << 0) ++ ++#define wm8775_s_ctrl(core, id, val) \ ++ do { \ ++ struct v4l2_ctrl *ctrl_ = \ ++ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \ ++ if (ctrl_ && !core->i2c_rc) { \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 1); \ ++ v4l2_ctrl_s_ctrl(ctrl_, val); \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 0); \ ++ } \ ++ } while (0) ++ ++#define wm8775_g_ctrl(core, id) \ ++ ({ \ ++ struct v4l2_ctrl *ctrl_ = \ ++ v4l2_ctrl_find(core->sd_wm8775->ctrl_handler, id); \ ++ s32 val = 0; \ ++ if (ctrl_ && !core->i2c_rc) { \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 1); \ ++ val = v4l2_ctrl_g_ctrl(ctrl_); \ ++ if (core->gate_ctrl) \ ++ core->gate_ctrl(core, 0); \ ++ } \ ++ val; \ ++ }) ++ ++struct cx8800_dev; ++struct cx8802_dev; ++ ++/* ----------------------------------------------------------- */ ++/* function 0: video stuff */ ++ ++struct cx8800_fh { ++ struct v4l2_fh fh; ++ struct cx8800_dev *dev; ++ unsigned int resources; ++ ++ /* video capture */ ++ struct videobuf_queue vidq; ++ ++ /* vbi capture */ ++ struct videobuf_queue vbiq; ++}; ++ ++struct cx8800_suspend_state { ++ int disabled; ++}; ++ ++struct cx8800_dev { ++ struct cx88_core *core; ++ spinlock_t slock; ++ ++ /* various device info */ ++ unsigned int resources; ++ struct video_device *video_dev; ++ struct video_device *vbi_dev; ++ struct video_device *radio_dev; ++ ++ /* pci i/o */ ++ struct pci_dev *pci; ++ unsigned char pci_rev,pci_lat; ++ ++ const struct cx8800_fmt *fmt; ++ unsigned int width, height; ++ ++ /* capture queues */ ++ struct cx88_dmaqueue vidq; ++ struct cx88_dmaqueue vbiq; ++ ++ /* various v4l controls */ ++ ++ /* other global state info */ ++ struct cx8800_suspend_state state; ++}; ++ ++/* ----------------------------------------------------------- */ ++/* function 1: audio/alsa stuff */ ++/* =============> moved to cx88-alsa.c <====================== */ ++ ++ ++/* ----------------------------------------------------------- */ ++/* function 2: mpeg stuff */ ++ ++struct cx8802_fh { ++ struct v4l2_fh fh; ++ struct cx8802_dev *dev; ++ struct videobuf_queue mpegq; ++}; ++ ++struct cx8802_suspend_state { ++ int disabled; ++}; ++ ++struct cx8802_driver { ++ struct cx88_core *core; ++ ++ /* List of drivers attached to device */ ++ struct list_head drvlist; ++ ++ /* Type of driver and access required */ ++ enum cx88_board_type type_id; ++ enum cx8802_board_access hw_access; ++ ++ /* MPEG 8802 internal only */ ++ int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); ++ int (*resume)(struct pci_dev *pci_dev); ++ ++ /* Callers to the following functions must hold core->lock */ ++ ++ /* MPEG 8802 -> mini driver - Driver probe and configuration */ ++ int (*probe)(struct cx8802_driver *drv); ++ int (*remove)(struct cx8802_driver *drv); ++ ++ /* MPEG 8802 -> mini driver - Access for hardware control */ ++ int (*advise_acquire)(struct cx8802_driver *drv); ++ int (*advise_release)(struct cx8802_driver *drv); ++ ++ /* MPEG 8802 <- mini driver - Access for hardware control */ ++ int (*request_acquire)(struct cx8802_driver *drv); ++ int (*request_release)(struct cx8802_driver *drv); ++}; ++ ++struct cx8802_dev { ++ struct cx88_core *core; ++ spinlock_t slock; ++ ++ /* pci i/o */ ++ struct pci_dev *pci; ++ unsigned char pci_rev,pci_lat; ++ ++ /* dma queues */ ++ struct cx88_dmaqueue mpegq; ++ u32 ts_packet_size; ++ u32 ts_packet_count; ++ ++ /* other global state info */ ++ struct cx8802_suspend_state state; ++ ++ /* for blackbird only */ ++ struct list_head devlist; ++#if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \ ++ defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE) ++ struct video_device *mpeg_dev; ++ u32 mailbox; ++ int width; ++ int height; ++ unsigned char mpeg_active; /* nonzero if mpeg encoder is active */ ++ ++ /* mpeg params */ ++ struct cx2341x_handler cxhdl; ++#endif ++ ++#if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) ++ /* for dvb only */ ++ struct videobuf_dvb_frontends frontends; ++#endif ++ ++#if defined(CONFIG_VIDEO_CX88_VP3054) || \ ++ defined(CONFIG_VIDEO_CX88_VP3054_MODULE) ++ /* For VP3045 secondary I2C bus support */ ++ struct vp3054_i2c_state *vp3054; ++#endif ++ /* for switching modulation types */ ++ unsigned char ts_gen_cntrl; ++ ++ /* List of attached drivers; must hold core->lock to access */ ++ struct list_head drvlist; ++ ++ struct work_struct request_module_wk; ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++#define cx_read(reg) readl(core->lmmio + ((reg)>>2)) ++#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2)) ++#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg)) ++ ++#define cx_andor(reg,mask,value) \ ++ writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\ ++ ((value) & (mask)), core->lmmio+((reg)>>2)) ++#define cx_set(reg,bit) cx_andor((reg),(bit),(bit)) ++#define cx_clear(reg,bit) cx_andor((reg),(bit),0) ++ ++#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); } ++ ++/* shadow registers */ ++#define cx_sread(sreg) (core->shadow[sreg]) ++#define cx_swrite(sreg,reg,value) \ ++ (core->shadow[sreg] = value, \ ++ writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) ++#define cx_sandor(sreg,reg,mask,value) \ ++ (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \ ++ writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) ++ ++/* ----------------------------------------------------------- */ ++/* cx88-core.c */ ++ ++extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[], ++ int len, u32 bits, u32 mask); ++ ++extern int cx88_core_irq(struct cx88_core *core, u32 status); ++extern void cx88_wakeup(struct cx88_core *core, ++ struct cx88_dmaqueue *q, u32 count); ++extern void cx88_shutdown(struct cx88_core *core); ++extern int cx88_reset(struct cx88_core *core); ++ ++extern int ++cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, ++ unsigned int top_offset, unsigned int bottom_offset, ++ unsigned int bpl, unsigned int padding, unsigned int lines); ++extern int ++cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, ++ struct scatterlist *sglist, unsigned int bpl, ++ unsigned int lines, unsigned int lpi); ++extern int ++cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, ++ u32 reg, u32 mask, u32 value); ++extern void ++cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf); ++ ++extern void cx88_risc_disasm(struct cx88_core *core, ++ struct btcx_riscmem *risc); ++extern int cx88_sram_channel_setup(struct cx88_core *core, ++ const struct sram_channel *ch, ++ unsigned int bpl, u32 risc); ++extern void cx88_sram_channel_dump(struct cx88_core *core, ++ const struct sram_channel *ch); ++ ++extern int cx88_set_scale(struct cx88_core *core, unsigned int width, ++ unsigned int height, enum v4l2_field field); ++extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm); ++ ++extern struct video_device *cx88_vdev_init(struct cx88_core *core, ++ struct pci_dev *pci, ++ const struct video_device *template_, ++ const char *type); ++extern struct cx88_core* cx88_core_get(struct pci_dev *pci); ++extern void cx88_core_put(struct cx88_core *core, ++ struct pci_dev *pci); ++ ++extern int cx88_start_audio_dma(struct cx88_core *core); ++extern int cx88_stop_audio_dma(struct cx88_core *core); ++ ++ ++/* ----------------------------------------------------------- */ ++/* cx88-vbi.c */ ++ ++/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */ ++int cx8800_vbi_fmt (struct file *file, void *priv, ++ struct v4l2_format *f); ++ ++/* ++int cx8800_start_vbi_dma(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q, ++ struct cx88_buffer *buf); ++*/ ++int cx8800_stop_vbi_dma(struct cx8800_dev *dev); ++int cx8800_restart_vbi_queue(struct cx8800_dev *dev, ++ struct cx88_dmaqueue *q); ++void cx8800_vbi_timeout(unsigned long data); ++ ++extern const struct videobuf_queue_ops cx8800_vbi_qops; ++ ++/* ----------------------------------------------------------- */ ++/* cx88-i2c.c */ ++ ++extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); ++ ++ ++/* ----------------------------------------------------------- */ ++/* cx88-cards.c */ ++ ++extern int cx88_tuner_callback(void *dev, int component, int command, int arg); ++extern int cx88_get_resources(const struct cx88_core *core, ++ struct pci_dev *pci); ++extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr); ++extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl); ++ ++/* ----------------------------------------------------------- */ ++/* cx88-tvaudio.c */ ++ ++void cx88_set_tvaudio(struct cx88_core *core); ++void cx88_newstation(struct cx88_core *core); ++void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t); ++void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); ++int cx88_audio_thread(void *data); ++ ++int cx8802_register_driver(struct cx8802_driver *drv); ++int cx8802_unregister_driver(struct cx8802_driver *drv); ++ ++/* Caller must hold core->lock */ ++struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); ++ ++/* ----------------------------------------------------------- */ ++/* cx88-dsp.c */ ++ ++s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core); ++ ++/* ----------------------------------------------------------- */ ++/* cx88-input.c */ ++ ++int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); ++int cx88_ir_fini(struct cx88_core *core); ++void cx88_ir_irq(struct cx88_core *core); ++int cx88_ir_start(struct cx88_core *core); ++void cx88_ir_stop(struct cx88_core *core); ++extern void cx88_i2c_init_ir(struct cx88_core *core); ++ ++/* ----------------------------------------------------------- */ ++/* cx88-mpeg.c */ ++ ++int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev, ++ struct cx88_buffer *buf, enum v4l2_field field); ++void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf); ++void cx8802_cancel_buffers(struct cx8802_dev *dev); ++ ++/* ----------------------------------------------------------- */ ++/* cx88-video.c*/ ++int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); ++int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); ++int cx88_video_mux(struct cx88_core *core, unsigned int input); ++void cx88_querycap(struct file *file, struct cx88_core *core, ++ struct v4l2_capability *cap); +diff --git a/drivers/media/pci/ddbridge/Kconfig b/drivers/media/pci/ddbridge/Kconfig +new file mode 100644 +index 0000000..44e5dc1 +--- /dev/null ++++ b/drivers/media/pci/ddbridge/Kconfig +@@ -0,0 +1,18 @@ ++config DVB_DDBRIDGE ++ tristate "Digital Devices bridge support" ++ depends on DVB_CORE && PCI && I2C ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ Support for cards with the Digital Devices PCI express bridge: ++ - Octopus PCIe Bridge ++ - Octopus mini PCIe Bridge ++ - Octopus LE ++ - DuoFlex S2 Octopus ++ - DuoFlex CT Octopus ++ - cineS2(v6) ++ ++ Say Y if you own such a card and want to use it. +diff --git a/drivers/media/pci/ddbridge/Makefile b/drivers/media/pci/ddbridge/Makefile +new file mode 100644 +index 0000000..7446c8b +--- /dev/null ++++ b/drivers/media/pci/ddbridge/Makefile +@@ -0,0 +1,14 @@ ++# ++# Makefile for the ddbridge device driver ++# ++ ++ddbridge-objs := ddbridge-core.o ++ ++obj-$(CONFIG_DVB_DDBRIDGE) += ddbridge.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ ++ccflags-y += -Idrivers/media/dvb-frontends/ ++ccflags-y += -Idrivers/media/tuners/ ++ ++# For the staging CI driver cxd2099 ++ccflags-y += -Idrivers/staging/media/cxd2099/ +diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c +new file mode 100644 +index 0000000..feff57e +--- /dev/null ++++ b/drivers/media/pci/ddbridge/ddbridge-core.c +@@ -0,0 +1,1730 @@ ++/* ++ * ddbridge.c: Digital Devices PCIe bridge driver ++ * ++ * Copyright (C) 2010-2011 Digital Devices GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ddbridge.h" ++ ++#include "ddbridge-regs.h" ++ ++#include "tda18271c2dd.h" ++#include "stv6110x.h" ++#include "stv090x.h" ++#include "lnbh24.h" ++#include "drxk.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++/* MSI had problems with lost interrupts, fixed but needs testing */ ++#undef CONFIG_PCI_MSI ++ ++/******************************************************************************/ ++ ++static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) ++{ ++ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1 } }; ++ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; ++} ++ ++static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, u8 reg, u8 *val) ++{ ++ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, ++ .buf = ®, .len = 1 }, ++ {.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1 } }; ++ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; ++} ++ ++static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, ++ u16 reg, u8 *val) ++{ ++ u8 msg[2] = {reg>>8, reg&0xff}; ++ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, ++ .buf = msg, .len = 2}, ++ {.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1} }; ++ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; ++} ++ ++static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) ++{ ++ struct ddb *dev = i2c->dev; ++ int stat; ++ u32 val; ++ ++ i2c->done = 0; ++ ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND); ++ stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ); ++ if (stat <= 0) { ++ printk(KERN_ERR "I2C timeout\n"); ++ { /* MSI debugging*/ ++ u32 istat = ddbreadl(INTERRUPT_STATUS); ++ printk(KERN_ERR "IRS %08x\n", istat); ++ ddbwritel(istat, INTERRUPT_ACK); ++ } ++ return -EIO; ++ } ++ val = ddbreadl(i2c->regs+I2C_COMMAND); ++ if (val & 0x70000) ++ return -EIO; ++ return 0; ++} ++ ++static int ddb_i2c_master_xfer(struct i2c_adapter *adapter, ++ struct i2c_msg msg[], int num) ++{ ++ struct ddb_i2c *i2c = (struct ddb_i2c *)i2c_get_adapdata(adapter); ++ struct ddb *dev = i2c->dev; ++ u8 addr = 0; ++ ++ if (num) ++ addr = msg[0].addr; ++ ++ if (num == 2 && msg[1].flags & I2C_M_RD && ++ !(msg[0].flags & I2C_M_RD)) { ++ memcpy_toio(dev->regs + I2C_TASKMEM_BASE + i2c->wbuf, ++ msg[0].buf, msg[0].len); ++ ddbwritel(msg[0].len|(msg[1].len << 16), ++ i2c->regs+I2C_TASKLENGTH); ++ if (!ddb_i2c_cmd(i2c, addr, 1)) { ++ memcpy_fromio(msg[1].buf, ++ dev->regs + I2C_TASKMEM_BASE + i2c->rbuf, ++ msg[1].len); ++ return num; ++ } ++ } ++ ++ if (num == 1 && !(msg[0].flags & I2C_M_RD)) { ++ ddbcpyto(I2C_TASKMEM_BASE + i2c->wbuf, msg[0].buf, msg[0].len); ++ ddbwritel(msg[0].len, i2c->regs + I2C_TASKLENGTH); ++ if (!ddb_i2c_cmd(i2c, addr, 2)) ++ return num; ++ } ++ if (num == 1 && (msg[0].flags & I2C_M_RD)) { ++ ddbwritel(msg[0].len << 16, i2c->regs + I2C_TASKLENGTH); ++ if (!ddb_i2c_cmd(i2c, addr, 3)) { ++ ddbcpyfrom(msg[0].buf, ++ I2C_TASKMEM_BASE + i2c->rbuf, msg[0].len); ++ return num; ++ } ++ } ++ return -EIO; ++} ++ ++ ++static u32 ddb_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL; ++} ++ ++struct i2c_algorithm ddb_i2c_algo = { ++ .master_xfer = ddb_i2c_master_xfer, ++ .functionality = ddb_i2c_functionality, ++}; ++ ++static void ddb_i2c_release(struct ddb *dev) ++{ ++ int i; ++ struct ddb_i2c *i2c; ++ struct i2c_adapter *adap; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ i2c = &dev->i2c[i]; ++ adap = &i2c->adap; ++ i2c_del_adapter(adap); ++ } ++} ++ ++static int ddb_i2c_init(struct ddb *dev) ++{ ++ int i, j, stat = 0; ++ struct ddb_i2c *i2c; ++ struct i2c_adapter *adap; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ i2c = &dev->i2c[i]; ++ i2c->dev = dev; ++ i2c->nr = i; ++ i2c->wbuf = i * (I2C_TASKMEM_SIZE / 4); ++ i2c->rbuf = i2c->wbuf + (I2C_TASKMEM_SIZE / 8); ++ i2c->regs = 0x80 + i * 0x20; ++ ddbwritel(I2C_SPEED_100, i2c->regs + I2C_TIMING); ++ ddbwritel((i2c->rbuf << 16) | i2c->wbuf, ++ i2c->regs + I2C_TASKADDRESS); ++ init_waitqueue_head(&i2c->wq); ++ ++ adap = &i2c->adap; ++ i2c_set_adapdata(adap, i2c); ++#ifdef I2C_ADAP_CLASS_TV_DIGITAL ++ adap->class = I2C_ADAP_CLASS_TV_DIGITAL|I2C_CLASS_TV_ANALOG; ++#else ++#ifdef I2C_CLASS_TV_ANALOG ++ adap->class = I2C_CLASS_TV_ANALOG; ++#endif ++#endif ++ strcpy(adap->name, "ddbridge"); ++ adap->algo = &ddb_i2c_algo; ++ adap->algo_data = (void *)i2c; ++ adap->dev.parent = &dev->pdev->dev; ++ stat = i2c_add_adapter(adap); ++ if (stat) ++ break; ++ } ++ if (stat) ++ for (j = 0; j < i; j++) { ++ i2c = &dev->i2c[j]; ++ adap = &i2c->adap; ++ i2c_del_adapter(adap); ++ } ++ return stat; ++} ++ ++ ++/******************************************************************************/ ++/******************************************************************************/ ++/******************************************************************************/ ++ ++#if 0 ++static void set_table(struct ddb *dev, u32 off, ++ dma_addr_t *pbuf, u32 num) ++{ ++ u32 i, base; ++ u64 mem; ++ ++ base = DMA_BASE_ADDRESS_TABLE + off; ++ for (i = 0; i < num; i++) { ++ mem = pbuf[i]; ++ ddbwritel(mem & 0xffffffff, base + i * 8); ++ ddbwritel(mem >> 32, base + i * 8 + 4); ++ } ++} ++#endif ++ ++static void ddb_address_table(struct ddb *dev) ++{ ++ u32 i, j, base; ++ u64 mem; ++ dma_addr_t *pbuf; ++ ++ for (i = 0; i < dev->info->port_num * 2; i++) { ++ base = DMA_BASE_ADDRESS_TABLE + i * 0x100; ++ pbuf = dev->input[i].pbuf; ++ for (j = 0; j < dev->input[i].dma_buf_num; j++) { ++ mem = pbuf[j]; ++ ddbwritel(mem & 0xffffffff, base + j * 8); ++ ddbwritel(mem >> 32, base + j * 8 + 4); ++ } ++ } ++ for (i = 0; i < dev->info->port_num; i++) { ++ base = DMA_BASE_ADDRESS_TABLE + 0x800 + i * 0x100; ++ pbuf = dev->output[i].pbuf; ++ for (j = 0; j < dev->output[i].dma_buf_num; j++) { ++ mem = pbuf[j]; ++ ddbwritel(mem & 0xffffffff, base + j * 8); ++ ddbwritel(mem >> 32, base + j * 8 + 4); ++ } ++ } ++} ++ ++static void io_free(struct pci_dev *pdev, u8 **vbuf, ++ dma_addr_t *pbuf, u32 size, int num) ++{ ++ int i; ++ ++ for (i = 0; i < num; i++) { ++ if (vbuf[i]) { ++ pci_free_consistent(pdev, size, vbuf[i], pbuf[i]); ++ vbuf[i] = 0; ++ } ++ } ++} ++ ++static int io_alloc(struct pci_dev *pdev, u8 **vbuf, ++ dma_addr_t *pbuf, u32 size, int num) ++{ ++ int i; ++ ++ for (i = 0; i < num; i++) { ++ vbuf[i] = pci_alloc_consistent(pdev, size, &pbuf[i]); ++ if (!vbuf[i]) ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static int ddb_buffers_alloc(struct ddb *dev) ++{ ++ int i; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ switch (port->class) { ++ case DDB_PORT_TUNER: ++ if (io_alloc(dev->pdev, port->input[0]->vbuf, ++ port->input[0]->pbuf, ++ port->input[0]->dma_buf_size, ++ port->input[0]->dma_buf_num) < 0) ++ return -1; ++ if (io_alloc(dev->pdev, port->input[1]->vbuf, ++ port->input[1]->pbuf, ++ port->input[1]->dma_buf_size, ++ port->input[1]->dma_buf_num) < 0) ++ return -1; ++ break; ++ case DDB_PORT_CI: ++ if (io_alloc(dev->pdev, port->input[0]->vbuf, ++ port->input[0]->pbuf, ++ port->input[0]->dma_buf_size, ++ port->input[0]->dma_buf_num) < 0) ++ return -1; ++ if (io_alloc(dev->pdev, port->output->vbuf, ++ port->output->pbuf, ++ port->output->dma_buf_size, ++ port->output->dma_buf_num) < 0) ++ return -1; ++ break; ++ default: ++ break; ++ } ++ } ++ ddb_address_table(dev); ++ return 0; ++} ++ ++static void ddb_buffers_free(struct ddb *dev) ++{ ++ int i; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ io_free(dev->pdev, port->input[0]->vbuf, ++ port->input[0]->pbuf, ++ port->input[0]->dma_buf_size, ++ port->input[0]->dma_buf_num); ++ io_free(dev->pdev, port->input[1]->vbuf, ++ port->input[1]->pbuf, ++ port->input[1]->dma_buf_size, ++ port->input[1]->dma_buf_num); ++ io_free(dev->pdev, port->output->vbuf, ++ port->output->pbuf, ++ port->output->dma_buf_size, ++ port->output->dma_buf_num); ++ } ++} ++ ++static void ddb_input_start(struct ddb_input *input) ++{ ++ struct ddb *dev = input->port->dev; ++ ++ spin_lock_irq(&input->lock); ++ input->cbuf = 0; ++ input->coff = 0; ++ ++ /* reset */ ++ ddbwritel(0, TS_INPUT_CONTROL(input->nr)); ++ ddbwritel(2, TS_INPUT_CONTROL(input->nr)); ++ ddbwritel(0, TS_INPUT_CONTROL(input->nr)); ++ ++ ddbwritel((1 << 16) | ++ (input->dma_buf_num << 11) | ++ (input->dma_buf_size >> 7), ++ DMA_BUFFER_SIZE(input->nr)); ++ ddbwritel(0, DMA_BUFFER_ACK(input->nr)); ++ ++ ddbwritel(1, DMA_BASE_WRITE); ++ ddbwritel(3, DMA_BUFFER_CONTROL(input->nr)); ++ ddbwritel(9, TS_INPUT_CONTROL(input->nr)); ++ input->running = 1; ++ spin_unlock_irq(&input->lock); ++} ++ ++static void ddb_input_stop(struct ddb_input *input) ++{ ++ struct ddb *dev = input->port->dev; ++ ++ spin_lock_irq(&input->lock); ++ ddbwritel(0, TS_INPUT_CONTROL(input->nr)); ++ ddbwritel(0, DMA_BUFFER_CONTROL(input->nr)); ++ input->running = 0; ++ spin_unlock_irq(&input->lock); ++} ++ ++static void ddb_output_start(struct ddb_output *output) ++{ ++ struct ddb *dev = output->port->dev; ++ ++ spin_lock_irq(&output->lock); ++ output->cbuf = 0; ++ output->coff = 0; ++ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); ++ ddbwritel(2, TS_OUTPUT_CONTROL(output->nr)); ++ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); ++ ddbwritel(0x3c, TS_OUTPUT_CONTROL(output->nr)); ++ ddbwritel((1 << 16) | ++ (output->dma_buf_num << 11) | ++ (output->dma_buf_size >> 7), ++ DMA_BUFFER_SIZE(output->nr + 8)); ++ ddbwritel(0, DMA_BUFFER_ACK(output->nr + 8)); ++ ++ ddbwritel(1, DMA_BASE_READ); ++ ddbwritel(3, DMA_BUFFER_CONTROL(output->nr + 8)); ++ /* ddbwritel(0xbd, TS_OUTPUT_CONTROL(output->nr)); */ ++ ddbwritel(0x1d, TS_OUTPUT_CONTROL(output->nr)); ++ output->running = 1; ++ spin_unlock_irq(&output->lock); ++} ++ ++static void ddb_output_stop(struct ddb_output *output) ++{ ++ struct ddb *dev = output->port->dev; ++ ++ spin_lock_irq(&output->lock); ++ ddbwritel(0, TS_OUTPUT_CONTROL(output->nr)); ++ ddbwritel(0, DMA_BUFFER_CONTROL(output->nr + 8)); ++ output->running = 0; ++ spin_unlock_irq(&output->lock); ++} ++ ++static u32 ddb_output_free(struct ddb_output *output) ++{ ++ u32 idx, off, stat = output->stat; ++ s32 diff; ++ ++ idx = (stat >> 11) & 0x1f; ++ off = (stat & 0x7ff) << 7; ++ ++ if (output->cbuf != idx) { ++ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && ++ (output->dma_buf_size - output->coff <= 188)) ++ return 0; ++ return 188; ++ } ++ diff = off - output->coff; ++ if (diff <= 0 || diff > 188) ++ return 188; ++ return 0; ++} ++ ++static ssize_t ddb_output_write(struct ddb_output *output, ++ const u8 *buf, size_t count) ++{ ++ struct ddb *dev = output->port->dev; ++ u32 idx, off, stat = output->stat; ++ u32 left = count, len; ++ ++ idx = (stat >> 11) & 0x1f; ++ off = (stat & 0x7ff) << 7; ++ ++ while (left) { ++ len = output->dma_buf_size - output->coff; ++ if ((((output->cbuf + 1) % output->dma_buf_num) == idx) && ++ (off == 0)) { ++ if (len <= 188) ++ break; ++ len -= 188; ++ } ++ if (output->cbuf == idx) { ++ if (off > output->coff) { ++#if 1 ++ len = off - output->coff; ++ len -= (len % 188); ++ if (len <= 188) ++ ++#endif ++ break; ++ len -= 188; ++ } ++ } ++ if (len > left) ++ len = left; ++ if (copy_from_user(output->vbuf[output->cbuf] + output->coff, ++ buf, len)) ++ return -EIO; ++ left -= len; ++ buf += len; ++ output->coff += len; ++ if (output->coff == output->dma_buf_size) { ++ output->coff = 0; ++ output->cbuf = ((output->cbuf + 1) % output->dma_buf_num); ++ } ++ ddbwritel((output->cbuf << 11) | (output->coff >> 7), ++ DMA_BUFFER_ACK(output->nr + 8)); ++ } ++ return count - left; ++} ++ ++static u32 ddb_input_avail(struct ddb_input *input) ++{ ++ struct ddb *dev = input->port->dev; ++ u32 idx, off, stat = input->stat; ++ u32 ctrl = ddbreadl(DMA_BUFFER_CONTROL(input->nr)); ++ ++ idx = (stat >> 11) & 0x1f; ++ off = (stat & 0x7ff) << 7; ++ ++ if (ctrl & 4) { ++ printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl); ++ ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr)); ++ return 0; ++ } ++ if (input->cbuf != idx) ++ return 188; ++ return 0; ++} ++ ++static ssize_t ddb_input_read(struct ddb_input *input, u8 *buf, size_t count) ++{ ++ struct ddb *dev = input->port->dev; ++ u32 left = count; ++ u32 idx, free, stat = input->stat; ++ int ret; ++ ++ idx = (stat >> 11) & 0x1f; ++ ++ while (left) { ++ if (input->cbuf == idx) ++ return count - left; ++ free = input->dma_buf_size - input->coff; ++ if (free > left) ++ free = left; ++ ret = copy_to_user(buf, input->vbuf[input->cbuf] + ++ input->coff, free); ++ if (ret) ++ return -EFAULT; ++ input->coff += free; ++ if (input->coff == input->dma_buf_size) { ++ input->coff = 0; ++ input->cbuf = (input->cbuf+1) % input->dma_buf_num; ++ } ++ left -= free; ++ ddbwritel((input->cbuf << 11) | (input->coff >> 7), ++ DMA_BUFFER_ACK(input->nr)); ++ } ++ return count; ++} ++ ++/******************************************************************************/ ++/******************************************************************************/ ++/******************************************************************************/ ++ ++#if 0 ++static struct ddb_input *fe2input(struct ddb *dev, struct dvb_frontend *fe) ++{ ++ int i; ++ ++ for (i = 0; i < dev->info->port_num * 2; i++) { ++ if (dev->input[i].fe == fe) ++ return &dev->input[i]; ++ } ++ return NULL; ++} ++#endif ++ ++static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct ddb_input *input = fe->sec_priv; ++ struct ddb_port *port = input->port; ++ int status; ++ ++ if (enable) { ++ mutex_lock(&port->i2c_gate_lock); ++ status = input->gate_ctrl(fe, 1); ++ } else { ++ status = input->gate_ctrl(fe, 0); ++ mutex_unlock(&port->i2c_gate_lock); ++ } ++ return status; ++} ++ ++static int demod_attach_drxk(struct ddb_input *input) ++{ ++ struct i2c_adapter *i2c = &input->port->i2c->adap; ++ struct dvb_frontend *fe; ++ struct drxk_config config; ++ ++ memset(&config, 0, sizeof(config)); ++ config.microcode_name = "drxk_a3.mc"; ++ config.qam_demod_parameter_count = 4; ++ config.adr = 0x29 + (input->nr & 1); ++ ++ fe = input->fe = dvb_attach(drxk_attach, &config, i2c); ++ if (!input->fe) { ++ printk(KERN_ERR "No DRXK found!\n"); ++ return -ENODEV; ++ } ++ fe->sec_priv = input; ++ input->gate_ctrl = fe->ops.i2c_gate_ctrl; ++ fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; ++ return 0; ++} ++ ++static int tuner_attach_tda18271(struct ddb_input *input) ++{ ++ struct i2c_adapter *i2c = &input->port->i2c->adap; ++ struct dvb_frontend *fe; ++ ++ if (input->fe->ops.i2c_gate_ctrl) ++ input->fe->ops.i2c_gate_ctrl(input->fe, 1); ++ fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60); ++ if (!fe) { ++ printk(KERN_ERR "No TDA18271 found!\n"); ++ return -ENODEV; ++ } ++ if (input->fe->ops.i2c_gate_ctrl) ++ input->fe->ops.i2c_gate_ctrl(input->fe, 0); ++ return 0; ++} ++ ++/******************************************************************************/ ++/******************************************************************************/ ++/******************************************************************************/ ++ ++static struct stv090x_config stv0900 = { ++ .device = STV0900, ++ .demod_mode = STV090x_DUAL, ++ .clk_mode = STV090x_CLK_EXT, ++ ++ .xtal = 27000000, ++ .address = 0x69, ++ ++ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ ++ .repeater_level = STV090x_RPTLEVEL_16, ++ ++ .adc1_range = STV090x_ADC_1Vpp, ++ .adc2_range = STV090x_ADC_1Vpp, ++ ++ .diseqc_envelope_mode = true, ++}; ++ ++static struct stv090x_config stv0900_aa = { ++ .device = STV0900, ++ .demod_mode = STV090x_DUAL, ++ .clk_mode = STV090x_CLK_EXT, ++ ++ .xtal = 27000000, ++ .address = 0x68, ++ ++ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ ++ .repeater_level = STV090x_RPTLEVEL_16, ++ ++ .adc1_range = STV090x_ADC_1Vpp, ++ .adc2_range = STV090x_ADC_1Vpp, ++ ++ .diseqc_envelope_mode = true, ++}; ++ ++static struct stv6110x_config stv6110a = { ++ .addr = 0x60, ++ .refclk = 27000000, ++ .clk_div = 1, ++}; ++ ++static struct stv6110x_config stv6110b = { ++ .addr = 0x63, ++ .refclk = 27000000, ++ .clk_div = 1, ++}; ++ ++static int demod_attach_stv0900(struct ddb_input *input, int type) ++{ ++ struct i2c_adapter *i2c = &input->port->i2c->adap; ++ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; ++ ++ input->fe = dvb_attach(stv090x_attach, feconf, i2c, ++ (input->nr & 1) ? STV090x_DEMODULATOR_1 ++ : STV090x_DEMODULATOR_0); ++ if (!input->fe) { ++ printk(KERN_ERR "No STV0900 found!\n"); ++ return -ENODEV; ++ } ++ if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0, ++ 0, (input->nr & 1) ? ++ (0x09 - type) : (0x0b - type))) { ++ printk(KERN_ERR "No LNBH24 found!\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int tuner_attach_stv6110(struct ddb_input *input, int type) ++{ ++ struct i2c_adapter *i2c = &input->port->i2c->adap; ++ struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; ++ struct stv6110x_config *tunerconf = (input->nr & 1) ? ++ &stv6110b : &stv6110a; ++ struct stv6110x_devctl *ctl; ++ ++ ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c); ++ if (!ctl) { ++ printk(KERN_ERR "No STV6110X found!\n"); ++ return -ENODEV; ++ } ++ printk(KERN_INFO "attach tuner input %d adr %02x\n", ++ input->nr, tunerconf->addr); ++ ++ feconf->tuner_init = ctl->tuner_init; ++ feconf->tuner_sleep = ctl->tuner_sleep; ++ feconf->tuner_set_mode = ctl->tuner_set_mode; ++ feconf->tuner_set_frequency = ctl->tuner_set_frequency; ++ feconf->tuner_get_frequency = ctl->tuner_get_frequency; ++ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; ++ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; ++ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; ++ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; ++ feconf->tuner_set_refclk = ctl->tuner_set_refclk; ++ feconf->tuner_get_status = ctl->tuner_get_status; ++ ++ return 0; ++} ++ ++static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, ++ int (*start_feed)(struct dvb_demux_feed *), ++ int (*stop_feed)(struct dvb_demux_feed *), ++ void *priv) ++{ ++ dvbdemux->priv = priv; ++ ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = start_feed; ++ dvbdemux->stop_feed = stop_feed; ++ dvbdemux->write_to_decoder = NULL; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING); ++ return dvb_dmx_init(dvbdemux); ++} ++ ++static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, ++ struct dvb_demux *dvbdemux, ++ struct dmx_frontend *hw_frontend, ++ struct dmx_frontend *mem_frontend, ++ struct dvb_adapter *dvb_adapter) ++{ ++ int ret; ++ ++ dmxdev->filternum = 256; ++ dmxdev->demux = &dvbdemux->dmx; ++ dmxdev->capabilities = 0; ++ ret = dvb_dmxdev_init(dmxdev, dvb_adapter); ++ if (ret < 0) ++ return ret; ++ ++ hw_frontend->source = DMX_FRONTEND_0; ++ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); ++ mem_frontend->source = DMX_MEMORY_FE; ++ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); ++ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); ++} ++ ++static int start_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct ddb_input *input = dvbdmx->priv; ++ ++ if (!input->users) ++ ddb_input_start(input); ++ ++ return ++input->users; ++} ++ ++static int stop_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct ddb_input *input = dvbdmx->priv; ++ ++ if (--input->users) ++ return input->users; ++ ++ ddb_input_stop(input); ++ return 0; ++} ++ ++ ++static void dvb_input_detach(struct ddb_input *input) ++{ ++ struct dvb_adapter *adap = &input->adap; ++ struct dvb_demux *dvbdemux = &input->demux; ++ ++ switch (input->attached) { ++ case 5: ++ if (input->fe2) ++ dvb_unregister_frontend(input->fe2); ++ if (input->fe) { ++ dvb_unregister_frontend(input->fe); ++ dvb_frontend_detach(input->fe); ++ input->fe = NULL; ++ } ++ case 4: ++ dvb_net_release(&input->dvbnet); ++ ++ case 3: ++ dvbdemux->dmx.close(&dvbdemux->dmx); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, ++ &input->hw_frontend); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, ++ &input->mem_frontend); ++ dvb_dmxdev_release(&input->dmxdev); ++ ++ case 2: ++ dvb_dmx_release(&input->demux); ++ ++ case 1: ++ dvb_unregister_adapter(adap); ++ } ++ input->attached = 0; ++} ++ ++static int dvb_input_attach(struct ddb_input *input) ++{ ++ int ret; ++ struct ddb_port *port = input->port; ++ struct dvb_adapter *adap = &input->adap; ++ struct dvb_demux *dvbdemux = &input->demux; ++ ++ ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE, ++ &input->port->dev->pdev->dev, ++ adapter_nr); ++ if (ret < 0) { ++ printk(KERN_ERR "ddbridge: Could not register adapter." ++ "Check if you enabled enough adapters in dvb-core!\n"); ++ return ret; ++ } ++ input->attached = 1; ++ ++ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", ++ start_feed, ++ stop_feed, input); ++ if (ret < 0) ++ return ret; ++ input->attached = 2; ++ ++ ret = my_dvb_dmxdev_ts_card_init(&input->dmxdev, &input->demux, ++ &input->hw_frontend, ++ &input->mem_frontend, adap); ++ if (ret < 0) ++ return ret; ++ input->attached = 3; ++ ++ ret = dvb_net_init(adap, &input->dvbnet, input->dmxdev.demux); ++ if (ret < 0) ++ return ret; ++ input->attached = 4; ++ ++ input->fe = 0; ++ switch (port->type) { ++ case DDB_TUNER_DVBS_ST: ++ if (demod_attach_stv0900(input, 0) < 0) ++ return -ENODEV; ++ if (tuner_attach_stv6110(input, 0) < 0) ++ return -ENODEV; ++ if (input->fe) { ++ if (dvb_register_frontend(adap, input->fe) < 0) ++ return -ENODEV; ++ } ++ break; ++ case DDB_TUNER_DVBS_ST_AA: ++ if (demod_attach_stv0900(input, 1) < 0) ++ return -ENODEV; ++ if (tuner_attach_stv6110(input, 1) < 0) ++ return -ENODEV; ++ if (input->fe) { ++ if (dvb_register_frontend(adap, input->fe) < 0) ++ return -ENODEV; ++ } ++ break; ++ case DDB_TUNER_DVBCT_TR: ++ if (demod_attach_drxk(input) < 0) ++ return -ENODEV; ++ if (tuner_attach_tda18271(input) < 0) ++ return -ENODEV; ++ if (input->fe) { ++ if (dvb_register_frontend(adap, input->fe) < 0) ++ return -ENODEV; ++ } ++ if (input->fe2) { ++ if (dvb_register_frontend(adap, input->fe2) < 0) ++ return -ENODEV; ++ input->fe2->tuner_priv = input->fe->tuner_priv; ++ memcpy(&input->fe2->ops.tuner_ops, ++ &input->fe->ops.tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ } ++ break; ++ } ++ input->attached = 5; ++ return 0; ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static ssize_t ts_write(struct file *file, const char *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct ddb_output *output = dvbdev->priv; ++ size_t left = count; ++ int stat; ++ ++ while (left) { ++ if (ddb_output_free(output) < 188) { ++ if (file->f_flags & O_NONBLOCK) ++ break; ++ if (wait_event_interruptible( ++ output->wq, ddb_output_free(output) >= 188) < 0) ++ break; ++ } ++ stat = ddb_output_write(output, buf, left); ++ if (stat < 0) ++ break; ++ buf += stat; ++ left -= stat; ++ } ++ return (left == count) ? -EAGAIN : (count - left); ++} ++ ++static ssize_t ts_read(struct file *file, char *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct ddb_output *output = dvbdev->priv; ++ struct ddb_input *input = output->port->input[0]; ++ int left, read; ++ ++ count -= count % 188; ++ left = count; ++ while (left) { ++ if (ddb_input_avail(input) < 188) { ++ if (file->f_flags & O_NONBLOCK) ++ break; ++ if (wait_event_interruptible( ++ input->wq, ddb_input_avail(input) >= 188) < 0) ++ break; ++ } ++ read = ddb_input_read(input, buf, left); ++ if (read < 0) ++ return read; ++ left -= read; ++ buf += read; ++ } ++ return (left == count) ? -EAGAIN : (count - left); ++} ++ ++static unsigned int ts_poll(struct file *file, poll_table *wait) ++{ ++ /* ++ struct dvb_device *dvbdev = file->private_data; ++ struct ddb_output *output = dvbdev->priv; ++ struct ddb_input *input = output->port->input[0]; ++ */ ++ unsigned int mask = 0; ++ ++#if 0 ++ if (data_avail_to_read) ++ mask |= POLLIN | POLLRDNORM; ++ if (data_avail_to_write) ++ mask |= POLLOUT | POLLWRNORM; ++ ++ poll_wait(file, &read_queue, wait); ++ poll_wait(file, &write_queue, wait); ++#endif ++ return mask; ++} ++ ++static const struct file_operations ci_fops = { ++ .owner = THIS_MODULE, ++ .read = ts_read, ++ .write = ts_write, ++ .open = dvb_generic_open, ++ .release = dvb_generic_release, ++ .poll = ts_poll, ++ .mmap = 0, ++}; ++ ++static struct dvb_device dvbdev_ci = { ++ .priv = 0, ++ .readers = -1, ++ .writers = -1, ++ .users = -1, ++ .fops = &ci_fops, ++}; ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static void input_tasklet(unsigned long data) ++{ ++ struct ddb_input *input = (struct ddb_input *) data; ++ struct ddb *dev = input->port->dev; ++ ++ spin_lock(&input->lock); ++ if (!input->running) { ++ spin_unlock(&input->lock); ++ return; ++ } ++ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); ++ ++ if (input->port->class == DDB_PORT_TUNER) { ++ if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr))) ++ printk(KERN_ERR "Overflow input %d\n", input->nr); ++ while (input->cbuf != ((input->stat >> 11) & 0x1f) ++ || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) { ++ dvb_dmx_swfilter_packets(&input->demux, ++ input->vbuf[input->cbuf], ++ input->dma_buf_size / 188); ++ ++ input->cbuf = (input->cbuf + 1) % input->dma_buf_num; ++ ddbwritel((input->cbuf << 11), ++ DMA_BUFFER_ACK(input->nr)); ++ input->stat = ddbreadl(DMA_BUFFER_CURRENT(input->nr)); ++ } ++ } ++ if (input->port->class == DDB_PORT_CI) ++ wake_up(&input->wq); ++ spin_unlock(&input->lock); ++} ++ ++static void output_tasklet(unsigned long data) ++{ ++ struct ddb_output *output = (struct ddb_output *) data; ++ struct ddb *dev = output->port->dev; ++ ++ spin_lock(&output->lock); ++ if (!output->running) { ++ spin_unlock(&output->lock); ++ return; ++ } ++ output->stat = ddbreadl(DMA_BUFFER_CURRENT(output->nr + 8)); ++ wake_up(&output->wq); ++ spin_unlock(&output->lock); ++} ++ ++ ++struct cxd2099_cfg cxd_cfg = { ++ .bitrate = 62000, ++ .adr = 0x40, ++ .polarity = 1, ++ .clock_mode = 1, ++}; ++ ++static int ddb_ci_attach(struct ddb_port *port) ++{ ++ int ret; ++ ++ ret = dvb_register_adapter(&port->output->adap, ++ "DDBridge", ++ THIS_MODULE, ++ &port->dev->pdev->dev, ++ adapter_nr); ++ if (ret < 0) ++ return ret; ++ port->en = cxd2099_attach(&cxd_cfg, port, &port->i2c->adap); ++ if (!port->en) { ++ dvb_unregister_adapter(&port->output->adap); ++ return -ENODEV; ++ } ++ ddb_input_start(port->input[0]); ++ ddb_output_start(port->output); ++ dvb_ca_en50221_init(&port->output->adap, ++ port->en, 0, 1); ++ ret = dvb_register_device(&port->output->adap, &port->output->dev, ++ &dvbdev_ci, (void *) port->output, ++ DVB_DEVICE_SEC); ++ return ret; ++} ++ ++static int ddb_port_attach(struct ddb_port *port) ++{ ++ int ret = 0; ++ ++ switch (port->class) { ++ case DDB_PORT_TUNER: ++ ret = dvb_input_attach(port->input[0]); ++ if (ret < 0) ++ break; ++ ret = dvb_input_attach(port->input[1]); ++ break; ++ case DDB_PORT_CI: ++ ret = ddb_ci_attach(port); ++ break; ++ default: ++ break; ++ } ++ if (ret < 0) ++ printk(KERN_ERR "port_attach on port %d failed\n", port->nr); ++ return ret; ++} ++ ++static int ddb_ports_attach(struct ddb *dev) ++{ ++ int i, ret = 0; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ ret = ddb_port_attach(port); ++ if (ret < 0) ++ break; ++ } ++ return ret; ++} ++ ++static void ddb_ports_detach(struct ddb *dev) ++{ ++ int i; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ switch (port->class) { ++ case DDB_PORT_TUNER: ++ dvb_input_detach(port->input[0]); ++ dvb_input_detach(port->input[1]); ++ break; ++ case DDB_PORT_CI: ++ if (port->output->dev) ++ dvb_unregister_device(port->output->dev); ++ if (port->en) { ++ ddb_input_stop(port->input[0]); ++ ddb_output_stop(port->output); ++ dvb_ca_en50221_release(port->en); ++ kfree(port->en); ++ port->en = 0; ++ dvb_unregister_adapter(&port->output->adap); ++ } ++ break; ++ } ++ } ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static int port_has_ci(struct ddb_port *port) ++{ ++ u8 val; ++ return i2c_read_reg(&port->i2c->adap, 0x40, 0, &val) ? 0 : 1; ++} ++ ++static int port_has_stv0900(struct ddb_port *port) ++{ ++ u8 val; ++ if (i2c_read_reg16(&port->i2c->adap, 0x69, 0xf100, &val) < 0) ++ return 0; ++ return 1; ++} ++ ++static int port_has_stv0900_aa(struct ddb_port *port) ++{ ++ u8 val; ++ if (i2c_read_reg16(&port->i2c->adap, 0x68, 0xf100, &val) < 0) ++ return 0; ++ return 1; ++} ++ ++static int port_has_drxks(struct ddb_port *port) ++{ ++ u8 val; ++ if (i2c_read(&port->i2c->adap, 0x29, &val) < 0) ++ return 0; ++ if (i2c_read(&port->i2c->adap, 0x2a, &val) < 0) ++ return 0; ++ return 1; ++} ++ ++static void ddb_port_probe(struct ddb_port *port) ++{ ++ struct ddb *dev = port->dev; ++ char *modname = "NO MODULE"; ++ ++ port->class = DDB_PORT_NONE; ++ ++ if (port_has_ci(port)) { ++ modname = "CI"; ++ port->class = DDB_PORT_CI; ++ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); ++ } else if (port_has_stv0900(port)) { ++ modname = "DUAL DVB-S2"; ++ port->class = DDB_PORT_TUNER; ++ port->type = DDB_TUNER_DVBS_ST; ++ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); ++ } else if (port_has_stv0900_aa(port)) { ++ modname = "DUAL DVB-S2"; ++ port->class = DDB_PORT_TUNER; ++ port->type = DDB_TUNER_DVBS_ST_AA; ++ ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); ++ } else if (port_has_drxks(port)) { ++ modname = "DUAL DVB-C/T"; ++ port->class = DDB_PORT_TUNER; ++ port->type = DDB_TUNER_DVBCT_TR; ++ ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); ++ } ++ printk(KERN_INFO "Port %d (TAB %d): %s\n", ++ port->nr, port->nr+1, modname); ++} ++ ++static void ddb_input_init(struct ddb_port *port, int nr) ++{ ++ struct ddb *dev = port->dev; ++ struct ddb_input *input = &dev->input[nr]; ++ ++ input->nr = nr; ++ input->port = port; ++ input->dma_buf_num = INPUT_DMA_BUFS; ++ input->dma_buf_size = INPUT_DMA_SIZE; ++ ddbwritel(0, TS_INPUT_CONTROL(nr)); ++ ddbwritel(2, TS_INPUT_CONTROL(nr)); ++ ddbwritel(0, TS_INPUT_CONTROL(nr)); ++ ddbwritel(0, DMA_BUFFER_ACK(nr)); ++ tasklet_init(&input->tasklet, input_tasklet, (unsigned long) input); ++ spin_lock_init(&input->lock); ++ init_waitqueue_head(&input->wq); ++} ++ ++static void ddb_output_init(struct ddb_port *port, int nr) ++{ ++ struct ddb *dev = port->dev; ++ struct ddb_output *output = &dev->output[nr]; ++ output->nr = nr; ++ output->port = port; ++ output->dma_buf_num = OUTPUT_DMA_BUFS; ++ output->dma_buf_size = OUTPUT_DMA_SIZE; ++ ++ ddbwritel(0, TS_OUTPUT_CONTROL(nr)); ++ ddbwritel(2, TS_OUTPUT_CONTROL(nr)); ++ ddbwritel(0, TS_OUTPUT_CONTROL(nr)); ++ tasklet_init(&output->tasklet, output_tasklet, (unsigned long) output); ++ init_waitqueue_head(&output->wq); ++} ++ ++static void ddb_ports_init(struct ddb *dev) ++{ ++ int i; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ port->dev = dev; ++ port->nr = i; ++ port->i2c = &dev->i2c[i]; ++ port->input[0] = &dev->input[2 * i]; ++ port->input[1] = &dev->input[2 * i + 1]; ++ port->output = &dev->output[i]; ++ ++ mutex_init(&port->i2c_gate_lock); ++ ddb_port_probe(port); ++ ddb_input_init(port, 2 * i); ++ ddb_input_init(port, 2 * i + 1); ++ ddb_output_init(port, i); ++ } ++} ++ ++static void ddb_ports_release(struct ddb *dev) ++{ ++ int i; ++ struct ddb_port *port; ++ ++ for (i = 0; i < dev->info->port_num; i++) { ++ port = &dev->port[i]; ++ port->dev = dev; ++ tasklet_kill(&port->input[0]->tasklet); ++ tasklet_kill(&port->input[1]->tasklet); ++ tasklet_kill(&port->output->tasklet); ++ } ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static void irq_handle_i2c(struct ddb *dev, int n) ++{ ++ struct ddb_i2c *i2c = &dev->i2c[n]; ++ ++ i2c->done = 1; ++ wake_up(&i2c->wq); ++} ++ ++static irqreturn_t irq_handler(int irq, void *dev_id) ++{ ++ struct ddb *dev = (struct ddb *) dev_id; ++ u32 s = ddbreadl(INTERRUPT_STATUS); ++ ++ if (!s) ++ return IRQ_NONE; ++ ++ do { ++ ddbwritel(s, INTERRUPT_ACK); ++ ++ if (s & 0x00000001) ++ irq_handle_i2c(dev, 0); ++ if (s & 0x00000002) ++ irq_handle_i2c(dev, 1); ++ if (s & 0x00000004) ++ irq_handle_i2c(dev, 2); ++ if (s & 0x00000008) ++ irq_handle_i2c(dev, 3); ++ ++ if (s & 0x00000100) ++ tasklet_schedule(&dev->input[0].tasklet); ++ if (s & 0x00000200) ++ tasklet_schedule(&dev->input[1].tasklet); ++ if (s & 0x00000400) ++ tasklet_schedule(&dev->input[2].tasklet); ++ if (s & 0x00000800) ++ tasklet_schedule(&dev->input[3].tasklet); ++ if (s & 0x00001000) ++ tasklet_schedule(&dev->input[4].tasklet); ++ if (s & 0x00002000) ++ tasklet_schedule(&dev->input[5].tasklet); ++ if (s & 0x00004000) ++ tasklet_schedule(&dev->input[6].tasklet); ++ if (s & 0x00008000) ++ tasklet_schedule(&dev->input[7].tasklet); ++ ++ if (s & 0x00010000) ++ tasklet_schedule(&dev->output[0].tasklet); ++ if (s & 0x00020000) ++ tasklet_schedule(&dev->output[1].tasklet); ++ if (s & 0x00040000) ++ tasklet_schedule(&dev->output[2].tasklet); ++ if (s & 0x00080000) ++ tasklet_schedule(&dev->output[3].tasklet); ++ ++ /* if (s & 0x000f0000) printk(KERN_DEBUG "%08x\n", istat); */ ++ } while ((s = ddbreadl(INTERRUPT_STATUS))); ++ ++ return IRQ_HANDLED; ++} ++ ++/******************************************************************************/ ++/******************************************************************************/ ++/******************************************************************************/ ++ ++static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) ++{ ++ u32 data, shift; ++ ++ if (wlen > 4) ++ ddbwritel(1, SPI_CONTROL); ++ while (wlen > 4) { ++ /* FIXME: check for big-endian */ ++ data = swab32(*(u32 *)wbuf); ++ wbuf += 4; ++ wlen -= 4; ++ ddbwritel(data, SPI_DATA); ++ while (ddbreadl(SPI_CONTROL) & 0x0004) ++ ; ++ } ++ ++ if (rlen) ++ ddbwritel(0x0001 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); ++ else ++ ddbwritel(0x0003 | ((wlen << (8 + 3)) & 0x1f00), SPI_CONTROL); ++ ++ data = 0; ++ shift = ((4 - wlen) * 8); ++ while (wlen) { ++ data <<= 8; ++ data |= *wbuf; ++ wlen--; ++ wbuf++; ++ } ++ if (shift) ++ data <<= shift; ++ ddbwritel(data, SPI_DATA); ++ while (ddbreadl(SPI_CONTROL) & 0x0004) ++ ; ++ ++ if (!rlen) { ++ ddbwritel(0, SPI_CONTROL); ++ return 0; ++ } ++ if (rlen > 4) ++ ddbwritel(1, SPI_CONTROL); ++ ++ while (rlen > 4) { ++ ddbwritel(0xffffffff, SPI_DATA); ++ while (ddbreadl(SPI_CONTROL) & 0x0004) ++ ; ++ data = ddbreadl(SPI_DATA); ++ *(u32 *) rbuf = swab32(data); ++ rbuf += 4; ++ rlen -= 4; ++ } ++ ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL); ++ ddbwritel(0xffffffff, SPI_DATA); ++ while (ddbreadl(SPI_CONTROL) & 0x0004) ++ ; ++ ++ data = ddbreadl(SPI_DATA); ++ ddbwritel(0, SPI_CONTROL); ++ ++ if (rlen < 4) ++ data <<= ((4 - rlen) * 8); ++ ++ while (rlen > 0) { ++ *rbuf = ((data >> 24) & 0xff); ++ data <<= 8; ++ rbuf++; ++ rlen--; ++ } ++ return 0; ++} ++ ++#define DDB_MAGIC 'd' ++ ++struct ddb_flashio { ++ __u8 *write_buf; ++ __u32 write_len; ++ __u8 *read_buf; ++ __u32 read_len; ++}; ++ ++#define IOCTL_DDB_FLASHIO _IOWR(DDB_MAGIC, 0x00, struct ddb_flashio) ++ ++#define DDB_NAME "ddbridge" ++ ++static u32 ddb_num; ++static struct ddb *ddbs[32]; ++static struct class *ddb_class; ++static int ddb_major; ++ ++static int ddb_open(struct inode *inode, struct file *file) ++{ ++ struct ddb *dev = ddbs[iminor(inode)]; ++ ++ file->private_data = dev; ++ return 0; ++} ++ ++static long ddb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ struct ddb *dev = file->private_data; ++ void *parg = (void *)arg; ++ int res; ++ ++ switch (cmd) { ++ case IOCTL_DDB_FLASHIO: ++ { ++ struct ddb_flashio fio; ++ u8 *rbuf, *wbuf; ++ ++ if (copy_from_user(&fio, parg, sizeof(fio))) ++ return -EFAULT; ++ ++ if (fio.write_len > 1028 || fio.read_len > 1028) ++ return -EINVAL; ++ if (fio.write_len + fio.read_len > 1028) ++ return -EINVAL; ++ ++ wbuf = &dev->iobuf[0]; ++ rbuf = wbuf + fio.write_len; ++ ++ if (copy_from_user(wbuf, fio.write_buf, fio.write_len)) ++ return -EFAULT; ++ res = flashio(dev, wbuf, fio.write_len, rbuf, fio.read_len); ++ if (res) ++ return res; ++ if (copy_to_user(fio.read_buf, rbuf, fio.read_len)) ++ return -EFAULT; ++ break; ++ } ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} ++ ++static const struct file_operations ddb_fops = { ++ .unlocked_ioctl = ddb_ioctl, ++ .open = ddb_open, ++}; ++ ++static char *ddb_devnode(struct device *device, umode_t *mode) ++{ ++ struct ddb *dev = dev_get_drvdata(device); ++ ++ return kasprintf(GFP_KERNEL, "ddbridge/card%d", dev->nr); ++} ++ ++static int ddb_class_create(void) ++{ ++ ddb_major = register_chrdev(0, DDB_NAME, &ddb_fops); ++ if (ddb_major < 0) ++ return ddb_major; ++ ++ ddb_class = class_create(THIS_MODULE, DDB_NAME); ++ if (IS_ERR(ddb_class)) { ++ unregister_chrdev(ddb_major, DDB_NAME); ++ return PTR_ERR(ddb_class); ++ } ++ ddb_class->devnode = ddb_devnode; ++ return 0; ++} ++ ++static void ddb_class_destroy(void) ++{ ++ class_destroy(ddb_class); ++ unregister_chrdev(ddb_major, DDB_NAME); ++} ++ ++static int ddb_device_create(struct ddb *dev) ++{ ++ dev->nr = ddb_num++; ++ dev->ddb_dev = device_create(ddb_class, NULL, ++ MKDEV(ddb_major, dev->nr), ++ dev, "ddbridge%d", dev->nr); ++ ddbs[dev->nr] = dev; ++ if (IS_ERR(dev->ddb_dev)) ++ return -1; ++ return 0; ++} ++ ++static void ddb_device_destroy(struct ddb *dev) ++{ ++ ddb_num--; ++ if (IS_ERR(dev->ddb_dev)) ++ return; ++ device_destroy(ddb_class, MKDEV(ddb_major, 0)); ++} ++ ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static void ddb_unmap(struct ddb *dev) ++{ ++ if (dev->regs) ++ iounmap(dev->regs); ++ vfree(dev); ++} ++ ++ ++static void __devexit ddb_remove(struct pci_dev *pdev) ++{ ++ struct ddb *dev = (struct ddb *) pci_get_drvdata(pdev); ++ ++ ddb_ports_detach(dev); ++ ddb_i2c_release(dev); ++ ++ ddbwritel(0, INTERRUPT_ENABLE); ++ free_irq(dev->pdev->irq, dev); ++#ifdef CONFIG_PCI_MSI ++ if (dev->msi) ++ pci_disable_msi(dev->pdev); ++#endif ++ ddb_ports_release(dev); ++ ddb_buffers_free(dev); ++ ddb_device_destroy(dev); ++ ++ ddb_unmap(dev); ++ pci_set_drvdata(pdev, 0); ++ pci_disable_device(pdev); ++} ++ ++ ++static int __devinit ddb_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ struct ddb *dev; ++ int stat = 0; ++ int irq_flag = IRQF_SHARED; ++ ++ if (pci_enable_device(pdev) < 0) ++ return -ENODEV; ++ ++ dev = vmalloc(sizeof(struct ddb)); ++ if (dev == NULL) ++ return -ENOMEM; ++ memset(dev, 0, sizeof(struct ddb)); ++ ++ dev->pdev = pdev; ++ pci_set_drvdata(pdev, dev); ++ dev->info = (struct ddb_info *) id->driver_data; ++ printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name); ++ ++ dev->regs = ioremap(pci_resource_start(dev->pdev, 0), ++ pci_resource_len(dev->pdev, 0)); ++ if (!dev->regs) { ++ stat = -ENOMEM; ++ goto fail; ++ } ++ printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); ++ ++#ifdef CONFIG_PCI_MSI ++ if (pci_msi_enabled()) ++ stat = pci_enable_msi(dev->pdev); ++ if (stat) { ++ printk(KERN_INFO ": MSI not available.\n"); ++ } else { ++ irq_flag = 0; ++ dev->msi = 1; ++ } ++#endif ++ stat = request_irq(dev->pdev->irq, irq_handler, ++ irq_flag, "DDBridge", (void *) dev); ++ if (stat < 0) ++ goto fail1; ++ ddbwritel(0, DMA_BASE_WRITE); ++ ddbwritel(0, DMA_BASE_READ); ++ ddbwritel(0xffffffff, INTERRUPT_ACK); ++ ddbwritel(0xfff0f, INTERRUPT_ENABLE); ++ ddbwritel(0, MSI1_ENABLE); ++ ++ if (ddb_i2c_init(dev) < 0) ++ goto fail1; ++ ddb_ports_init(dev); ++ if (ddb_buffers_alloc(dev) < 0) { ++ printk(KERN_INFO ": Could not allocate buffer memory\n"); ++ goto fail2; ++ } ++ if (ddb_ports_attach(dev) < 0) ++ goto fail3; ++ ddb_device_create(dev); ++ return 0; ++ ++fail3: ++ ddb_ports_detach(dev); ++ printk(KERN_ERR "fail3\n"); ++ ddb_ports_release(dev); ++fail2: ++ printk(KERN_ERR "fail2\n"); ++ ddb_buffers_free(dev); ++fail1: ++ printk(KERN_ERR "fail1\n"); ++ if (dev->msi) ++ pci_disable_msi(dev->pdev); ++ free_irq(dev->pdev->irq, dev); ++fail: ++ printk(KERN_ERR "fail\n"); ++ ddb_unmap(dev); ++ pci_set_drvdata(pdev, 0); ++ pci_disable_device(pdev); ++ return -1; ++} ++ ++/******************************************************************************/ ++/******************************************************************************/ ++/******************************************************************************/ ++ ++static struct ddb_info ddb_none = { ++ .type = DDB_NONE, ++ .name = "Digital Devices PCIe bridge", ++}; ++ ++static struct ddb_info ddb_octopus = { ++ .type = DDB_OCTOPUS, ++ .name = "Digital Devices Octopus DVB adapter", ++ .port_num = 4, ++}; ++ ++static struct ddb_info ddb_octopus_le = { ++ .type = DDB_OCTOPUS, ++ .name = "Digital Devices Octopus LE DVB adapter", ++ .port_num = 2, ++}; ++ ++static struct ddb_info ddb_v6 = { ++ .type = DDB_OCTOPUS, ++ .name = "Digital Devices Cine S2 V6 DVB adapter", ++ .port_num = 3, ++}; ++ ++#define DDVID 0xdd01 /* Digital Devices Vendor ID */ ++ ++#define DDB_ID(_vend, _dev, _subvend, _subdev, _driverdata) { \ ++ .vendor = _vend, .device = _dev, \ ++ .subvendor = _subvend, .subdevice = _subdev, \ ++ .driver_data = (unsigned long)&_driverdata } ++ ++static const struct pci_device_id ddb_id_tbl[] __devinitdata = { ++ DDB_ID(DDVID, 0x0002, DDVID, 0x0001, ddb_octopus), ++ DDB_ID(DDVID, 0x0003, DDVID, 0x0001, ddb_octopus), ++ DDB_ID(DDVID, 0x0003, DDVID, 0x0002, ddb_octopus_le), ++ DDB_ID(DDVID, 0x0003, DDVID, 0x0010, ddb_octopus), ++ DDB_ID(DDVID, 0x0003, DDVID, 0x0020, ddb_v6), ++ /* in case sub-ids got deleted in flash */ ++ DDB_ID(DDVID, 0x0003, PCI_ANY_ID, PCI_ANY_ID, ddb_none), ++ {0} ++}; ++MODULE_DEVICE_TABLE(pci, ddb_id_tbl); ++ ++ ++static struct pci_driver ddb_pci_driver = { ++ .name = "DDBridge", ++ .id_table = ddb_id_tbl, ++ .probe = ddb_probe, ++ .remove = __devexit_p(ddb_remove), ++}; ++ ++static __init int module_init_ddbridge(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "Digital Devices PCIE bridge driver, " ++ "Copyright (C) 2010-11 Digital Devices GmbH\n"); ++ ++ ret = ddb_class_create(); ++ if (ret < 0) ++ return ret; ++ ret = pci_register_driver(&ddb_pci_driver); ++ if (ret < 0) ++ ddb_class_destroy(); ++ return ret; ++} ++ ++static __exit void module_exit_ddbridge(void) ++{ ++ pci_unregister_driver(&ddb_pci_driver); ++ ddb_class_destroy(); ++} ++ ++module_init(module_init_ddbridge); ++module_exit(module_exit_ddbridge); ++ ++MODULE_DESCRIPTION("Digital Devices PCIe Bridge"); ++MODULE_AUTHOR("Ralph Metzler"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.5"); +diff --git a/drivers/media/pci/ddbridge/ddbridge-regs.h b/drivers/media/pci/ddbridge/ddbridge-regs.h +new file mode 100644 +index 0000000..a3ccb31 +--- /dev/null ++++ b/drivers/media/pci/ddbridge/ddbridge-regs.h +@@ -0,0 +1,151 @@ ++/* ++ * ddbridge-regs.h: Digital Devices PCIe bridge driver ++ * ++ * Copyright (C) 2010-2011 Digital Devices GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/* DD-DVBBridgeV1.h 273 2010-09-17 05:03:16Z manfred */ ++ ++/* Register Definitions */ ++ ++#define CUR_REGISTERMAP_VERSION 0x10000 ++ ++#define HARDWARE_VERSION 0x00 ++#define REGISTERMAP_VERSION 0x04 ++ ++/* ------------------------------------------------------------------------- */ ++/* SPI Controller */ ++ ++#define SPI_CONTROL 0x10 ++#define SPI_DATA 0x14 ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Interrupt controller */ ++/* How many MSI's are available depends on HW (Min 2 max 8) */ ++/* How many are usable also depends on Host platform */ ++ ++#define INTERRUPT_BASE (0x40) ++ ++#define INTERRUPT_ENABLE (INTERRUPT_BASE + 0x00) ++#define MSI0_ENABLE (INTERRUPT_BASE + 0x00) ++#define MSI1_ENABLE (INTERRUPT_BASE + 0x04) ++#define MSI2_ENABLE (INTERRUPT_BASE + 0x08) ++#define MSI3_ENABLE (INTERRUPT_BASE + 0x0C) ++#define MSI4_ENABLE (INTERRUPT_BASE + 0x10) ++#define MSI5_ENABLE (INTERRUPT_BASE + 0x14) ++#define MSI6_ENABLE (INTERRUPT_BASE + 0x18) ++#define MSI7_ENABLE (INTERRUPT_BASE + 0x1C) ++ ++#define INTERRUPT_STATUS (INTERRUPT_BASE + 0x20) ++#define INTERRUPT_ACK (INTERRUPT_BASE + 0x20) ++ ++#define INTMASK_I2C1 (0x00000001) ++#define INTMASK_I2C2 (0x00000002) ++#define INTMASK_I2C3 (0x00000004) ++#define INTMASK_I2C4 (0x00000008) ++ ++#define INTMASK_CIRQ1 (0x00000010) ++#define INTMASK_CIRQ2 (0x00000020) ++#define INTMASK_CIRQ3 (0x00000040) ++#define INTMASK_CIRQ4 (0x00000080) ++ ++#define INTMASK_TSINPUT1 (0x00000100) ++#define INTMASK_TSINPUT2 (0x00000200) ++#define INTMASK_TSINPUT3 (0x00000400) ++#define INTMASK_TSINPUT4 (0x00000800) ++#define INTMASK_TSINPUT5 (0x00001000) ++#define INTMASK_TSINPUT6 (0x00002000) ++#define INTMASK_TSINPUT7 (0x00004000) ++#define INTMASK_TSINPUT8 (0x00008000) ++ ++#define INTMASK_TSOUTPUT1 (0x00010000) ++#define INTMASK_TSOUTPUT2 (0x00020000) ++#define INTMASK_TSOUTPUT3 (0x00040000) ++#define INTMASK_TSOUTPUT4 (0x00080000) ++ ++/* ------------------------------------------------------------------------- */ ++/* I2C Master Controller */ ++ ++#define I2C_BASE (0x80) /* Byte offset */ ++ ++#define I2C_COMMAND (0x00) ++#define I2C_TIMING (0x04) ++#define I2C_TASKLENGTH (0x08) /* High read, low write */ ++#define I2C_TASKADDRESS (0x0C) /* High read, low write */ ++ ++#define I2C_MONITOR (0x1C) ++ ++#define I2C_BASE_1 (I2C_BASE + 0x00) ++#define I2C_BASE_2 (I2C_BASE + 0x20) ++#define I2C_BASE_3 (I2C_BASE + 0x40) ++#define I2C_BASE_4 (I2C_BASE + 0x60) ++ ++#define I2C_BASE_N(i) (I2C_BASE + (i) * 0x20) ++ ++#define I2C_TASKMEM_BASE (0x1000) /* Byte offset */ ++#define I2C_TASKMEM_SIZE (0x1000) ++ ++#define I2C_SPEED_400 (0x04030404) ++#define I2C_SPEED_200 (0x09080909) ++#define I2C_SPEED_154 (0x0C0B0C0C) ++#define I2C_SPEED_100 (0x13121313) ++#define I2C_SPEED_77 (0x19181919) ++#define I2C_SPEED_50 (0x27262727) ++ ++ ++/* ------------------------------------------------------------------------- */ ++/* DMA Controller */ ++ ++#define DMA_BASE_WRITE (0x100) ++#define DMA_BASE_READ (0x140) ++ ++#define DMA_CONTROL (0x00) /* 64 */ ++#define DMA_ERROR (0x04) /* 65 ( only read instance ) */ ++ ++#define DMA_DIAG_CONTROL (0x1C) /* 71 */ ++#define DMA_DIAG_PACKETCOUNTER_LOW (0x20) /* 72 */ ++#define DMA_DIAG_PACKETCOUNTER_HIGH (0x24) /* 73 */ ++#define DMA_DIAG_TIMECOUNTER_LOW (0x28) /* 74 */ ++#define DMA_DIAG_TIMECOUNTER_HIGH (0x2C) /* 75 */ ++#define DMA_DIAG_RECHECKCOUNTER (0x30) /* 76 ( Split completions on read ) */ ++#define DMA_DIAG_WAITTIMEOUTINIT (0x34) /* 77 */ ++#define DMA_DIAG_WAITOVERFLOWCOUNTER (0x38) /* 78 */ ++#define DMA_DIAG_WAITCOUNTER (0x3C) /* 79 */ ++ ++/* ------------------------------------------------------------------------- */ ++/* DMA Buffer */ ++ ++#define TS_INPUT_BASE (0x200) ++#define TS_INPUT_CONTROL(i) (TS_INPUT_BASE + (i) * 16 + 0x00) ++ ++#define TS_OUTPUT_BASE (0x280) ++#define TS_OUTPUT_CONTROL(i) (TS_OUTPUT_BASE + (i) * 16 + 0x00) ++ ++#define DMA_BUFFER_BASE (0x300) ++ ++#define DMA_BUFFER_CONTROL(i) (DMA_BUFFER_BASE + (i) * 16 + 0x00) ++#define DMA_BUFFER_ACK(i) (DMA_BUFFER_BASE + (i) * 16 + 0x04) ++#define DMA_BUFFER_CURRENT(i) (DMA_BUFFER_BASE + (i) * 16 + 0x08) ++#define DMA_BUFFER_SIZE(i) (DMA_BUFFER_BASE + (i) * 16 + 0x0c) ++ ++#define DMA_BASE_ADDRESS_TABLE (0x2000) ++#define DMA_BASE_ADDRESS_TABLE_ENTRIES (512) ++ +diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h +new file mode 100644 +index 0000000..8b1b41d +--- /dev/null ++++ b/drivers/media/pci/ddbridge/ddbridge.h +@@ -0,0 +1,185 @@ ++/* ++ * ddbridge.h: Digital Devices PCIe bridge driver ++ * ++ * Copyright (C) 2010-2011 Digital Devices GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef _DDBRIDGE_H_ ++#define _DDBRIDGE_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_ringbuffer.h" ++#include "dvb_ca_en50221.h" ++#include "dvb_net.h" ++#include "cxd2099.h" ++ ++#define DDB_MAX_I2C 4 ++#define DDB_MAX_PORT 4 ++#define DDB_MAX_INPUT 8 ++#define DDB_MAX_OUTPUT 4 ++ ++struct ddb_info { ++ int type; ++#define DDB_NONE 0 ++#define DDB_OCTOPUS 1 ++ char *name; ++ int port_num; ++ u32 port_type[DDB_MAX_PORT]; ++}; ++ ++/* DMA_SIZE MUST be divisible by 188 and 128 !!! */ ++ ++#define INPUT_DMA_MAX_BUFS 32 /* hardware table limit */ ++#define INPUT_DMA_BUFS 8 ++#define INPUT_DMA_SIZE (128*47*21) ++ ++#define OUTPUT_DMA_MAX_BUFS 32 ++#define OUTPUT_DMA_BUFS 8 ++#define OUTPUT_DMA_SIZE (128*47*21) ++ ++struct ddb; ++struct ddb_port; ++ ++struct ddb_input { ++ struct ddb_port *port; ++ u32 nr; ++ int attached; ++ ++ dma_addr_t pbuf[INPUT_DMA_MAX_BUFS]; ++ u8 *vbuf[INPUT_DMA_MAX_BUFS]; ++ u32 dma_buf_num; ++ u32 dma_buf_size; ++ ++ struct tasklet_struct tasklet; ++ spinlock_t lock; ++ wait_queue_head_t wq; ++ int running; ++ u32 stat; ++ u32 cbuf; ++ u32 coff; ++ ++ struct dvb_adapter adap; ++ struct dvb_device *dev; ++ struct dvb_frontend *fe; ++ struct dvb_frontend *fe2; ++ struct dmxdev dmxdev; ++ struct dvb_demux demux; ++ struct dvb_net dvbnet; ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ int users; ++ int (*gate_ctrl)(struct dvb_frontend *, int); ++}; ++ ++struct ddb_output { ++ struct ddb_port *port; ++ u32 nr; ++ dma_addr_t pbuf[OUTPUT_DMA_MAX_BUFS]; ++ u8 *vbuf[OUTPUT_DMA_MAX_BUFS]; ++ u32 dma_buf_num; ++ u32 dma_buf_size; ++ struct tasklet_struct tasklet; ++ spinlock_t lock; ++ wait_queue_head_t wq; ++ int running; ++ u32 stat; ++ u32 cbuf; ++ u32 coff; ++ ++ struct dvb_adapter adap; ++ struct dvb_device *dev; ++}; ++ ++struct ddb_i2c { ++ struct ddb *dev; ++ u32 nr; ++ struct i2c_adapter adap; ++ struct i2c_adapter adap2; ++ u32 regs; ++ u32 rbuf; ++ u32 wbuf; ++ int done; ++ wait_queue_head_t wq; ++}; ++ ++struct ddb_port { ++ struct ddb *dev; ++ u32 nr; ++ struct ddb_i2c *i2c; ++ struct mutex i2c_gate_lock; ++ u32 class; ++#define DDB_PORT_NONE 0 ++#define DDB_PORT_CI 1 ++#define DDB_PORT_TUNER 2 ++ u32 type; ++#define DDB_TUNER_NONE 0 ++#define DDB_TUNER_DVBS_ST 1 ++#define DDB_TUNER_DVBS_ST_AA 2 ++#define DDB_TUNER_DVBCT_TR 16 ++#define DDB_TUNER_DVBCT_ST 17 ++ u32 adr; ++ ++ struct ddb_input *input[2]; ++ struct ddb_output *output; ++ struct dvb_ca_en50221 *en; ++}; ++ ++struct ddb { ++ struct pci_dev *pdev; ++ unsigned char *regs; ++ struct ddb_port port[DDB_MAX_PORT]; ++ struct ddb_i2c i2c[DDB_MAX_I2C]; ++ struct ddb_input input[DDB_MAX_INPUT]; ++ struct ddb_output output[DDB_MAX_OUTPUT]; ++ ++ struct device *ddb_dev; ++ int nr; ++ u8 iobuf[1028]; ++ ++ struct ddb_info *info; ++ int msi; ++}; ++ ++/****************************************************************************/ ++ ++#define ddbwritel(_val, _adr) writel((_val), \ ++ (char *) (dev->regs+(_adr))) ++#define ddbreadl(_adr) readl((char *) (dev->regs+(_adr))) ++#define ddbcpyto(_adr, _src, _count) memcpy_toio((char *) \ ++ (dev->regs+(_adr)), (_src), (_count)) ++#define ddbcpyfrom(_dst, _adr, _count) memcpy_fromio((_dst), (char *) \ ++ (dev->regs+(_adr)), (_count)) ++ ++/****************************************************************************/ ++ ++#endif +diff --git a/drivers/media/pci/dm1105/Kconfig b/drivers/media/pci/dm1105/Kconfig +new file mode 100644 +index 0000000..173daf0 +--- /dev/null ++++ b/drivers/media/pci/dm1105/Kconfig +@@ -0,0 +1,21 @@ ++config DVB_DM1105 ++ tristate "SDMC DM1105 based PCI cards" ++ depends on DVB_CORE && PCI && I2C ++ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_CX24116 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SI21XX if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DS3000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TS2020 if MEDIA_SUBDRV_AUTOSELECT ++ depends on RC_CORE ++ help ++ Support for cards based on the SDMC DM1105 PCI chip like ++ DvbWorld 2002 ++ ++ Since these cards have no MPEG decoder onboard, they transmit ++ only compressed MPEG data over the PCI bus, so you need ++ an external software decoder to watch TV on your computer. ++ ++ Say Y or M if you own such a device and want to use it. +diff --git a/drivers/media/pci/dm1105/Makefile b/drivers/media/pci/dm1105/Makefile +new file mode 100644 +index 0000000..3275851 +--- /dev/null ++++ b/drivers/media/pci/dm1105/Makefile +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_DVB_DM1105) += dm1105.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends +diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c +new file mode 100644 +index 0000000..c789e7c +--- /dev/null ++++ b/drivers/media/pci/dm1105/dm1105.c +@@ -0,0 +1,1259 @@ ++/* ++ * dm1105.c - driver for DVB cards based on SDMC DM1105 PCI chip ++ * ++ * Copyright (C) 2008 Igor M. Liplianin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "demux.h" ++#include "dmxdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++#include "dvbdev.h" ++#include "dvb-pll.h" ++ ++#include "stv0299.h" ++#include "stv0288.h" ++#include "stb6000.h" ++#include "si21xx.h" ++#include "cx24116.h" ++#include "z0194a.h" ++#include "ts2020.h" ++#include "ds3000.h" ++ ++#define MODULE_NAME "dm1105" ++ ++#define UNSET (-1U) ++ ++#define DM1105_BOARD_NOAUTO UNSET ++#define DM1105_BOARD_UNKNOWN 0 ++#define DM1105_BOARD_DVBWORLD_2002 1 ++#define DM1105_BOARD_DVBWORLD_2004 2 ++#define DM1105_BOARD_AXESS_DM05 3 ++#define DM1105_BOARD_UNBRANDED_I2C_ON_GPIO 4 ++ ++/* ----------------------------------------------- */ ++/* ++ * PCI ID's ++ */ ++#ifndef PCI_VENDOR_ID_TRIGEM ++#define PCI_VENDOR_ID_TRIGEM 0x109f ++#endif ++#ifndef PCI_VENDOR_ID_AXESS ++#define PCI_VENDOR_ID_AXESS 0x195d ++#endif ++#ifndef PCI_DEVICE_ID_DM1105 ++#define PCI_DEVICE_ID_DM1105 0x036f ++#endif ++#ifndef PCI_DEVICE_ID_DW2002 ++#define PCI_DEVICE_ID_DW2002 0x2002 ++#endif ++#ifndef PCI_DEVICE_ID_DW2004 ++#define PCI_DEVICE_ID_DW2004 0x2004 ++#endif ++#ifndef PCI_DEVICE_ID_DM05 ++#define PCI_DEVICE_ID_DM05 0x1105 ++#endif ++/* ----------------------------------------------- */ ++/* sdmc dm1105 registers */ ++ ++/* TS Control */ ++#define DM1105_TSCTR 0x00 ++#define DM1105_DTALENTH 0x04 ++ ++/* GPIO Interface */ ++#define DM1105_GPIOVAL 0x08 ++#define DM1105_GPIOCTR 0x0c ++ ++/* PID serial number */ ++#define DM1105_PIDN 0x10 ++ ++/* Odd-even secret key select */ ++#define DM1105_CWSEL 0x14 ++ ++/* Host Command Interface */ ++#define DM1105_HOST_CTR 0x18 ++#define DM1105_HOST_AD 0x1c ++ ++/* PCI Interface */ ++#define DM1105_CR 0x30 ++#define DM1105_RST 0x34 ++#define DM1105_STADR 0x38 ++#define DM1105_RLEN 0x3c ++#define DM1105_WRP 0x40 ++#define DM1105_INTCNT 0x44 ++#define DM1105_INTMAK 0x48 ++#define DM1105_INTSTS 0x4c ++ ++/* CW Value */ ++#define DM1105_ODD 0x50 ++#define DM1105_EVEN 0x58 ++ ++/* PID Value */ ++#define DM1105_PID 0x60 ++ ++/* IR Control */ ++#define DM1105_IRCTR 0x64 ++#define DM1105_IRMODE 0x68 ++#define DM1105_SYSTEMCODE 0x6c ++#define DM1105_IRCODE 0x70 ++ ++/* Unknown Values */ ++#define DM1105_ENCRYPT 0x74 ++#define DM1105_VER 0x7c ++ ++/* I2C Interface */ ++#define DM1105_I2CCTR 0x80 ++#define DM1105_I2CSTS 0x81 ++#define DM1105_I2CDAT 0x82 ++#define DM1105_I2C_RA 0x83 ++/* ----------------------------------------------- */ ++/* Interrupt Mask Bits */ ++ ++#define INTMAK_TSIRQM 0x01 ++#define INTMAK_HIRQM 0x04 ++#define INTMAK_IRM 0x08 ++#define INTMAK_ALLMASK (INTMAK_TSIRQM | \ ++ INTMAK_HIRQM | \ ++ INTMAK_IRM) ++#define INTMAK_NONEMASK 0x00 ++ ++/* Interrupt Status Bits */ ++#define INTSTS_TSIRQ 0x01 ++#define INTSTS_HIRQ 0x04 ++#define INTSTS_IR 0x08 ++ ++/* IR Control Bits */ ++#define DM1105_IR_EN 0x01 ++#define DM1105_SYS_CHK 0x02 ++#define DM1105_REP_FLG 0x08 ++ ++/* EEPROM addr */ ++#define IIC_24C01_addr 0xa0 ++/* Max board count */ ++#define DM1105_MAX 0x04 ++ ++#define DRIVER_NAME "dm1105" ++#define DM1105_I2C_GPIO_NAME "dm1105-gpio" ++ ++#define DM1105_DMA_PACKETS 47 ++#define DM1105_DMA_PACKET_LENGTH (128*4) ++#define DM1105_DMA_BYTES (128 * 4 * DM1105_DMA_PACKETS) ++ ++/* */ ++#define GPIO08 (1 << 8) ++#define GPIO13 (1 << 13) ++#define GPIO14 (1 << 14) ++#define GPIO15 (1 << 15) ++#define GPIO16 (1 << 16) ++#define GPIO17 (1 << 17) ++#define GPIO_ALL 0x03ffff ++ ++/* GPIO's for LNB power control */ ++#define DM1105_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) ++#define DM1105_LNB_OFF GPIO17 ++#define DM1105_LNB_13V (GPIO16 | GPIO08) ++#define DM1105_LNB_18V GPIO08 ++ ++/* GPIO's for LNB power control for Axess DM05 */ ++#define DM05_LNB_MASK (GPIO_ALL & ~(GPIO14 | GPIO13)) ++#define DM05_LNB_OFF GPIO17/* actually 13v */ ++#define DM05_LNB_13V GPIO17 ++#define DM05_LNB_18V (GPIO17 | GPIO16) ++ ++/* GPIO's for LNB power control for unbranded with I2C on GPIO */ ++#define UNBR_LNB_MASK (GPIO17 | GPIO16) ++#define UNBR_LNB_OFF 0 ++#define UNBR_LNB_13V GPIO17 ++#define UNBR_LNB_18V (GPIO17 | GPIO16) ++ ++static unsigned int card[] = {[0 ... 3] = UNSET }; ++module_param_array(card, int, NULL, 0444); ++MODULE_PARM_DESC(card, "card type"); ++ ++static int ir_debug; ++module_param(ir_debug, int, 0644); ++MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); ++ ++static unsigned int dm1105_devcount; ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++struct dm1105_board { ++ char *name; ++ struct { ++ u32 mask, off, v13, v18; ++ } lnb; ++ u32 gpio_scl, gpio_sda; ++}; ++ ++struct dm1105_subid { ++ u16 subvendor; ++ u16 subdevice; ++ u32 card; ++}; ++ ++static const struct dm1105_board dm1105_boards[] = { ++ [DM1105_BOARD_UNKNOWN] = { ++ .name = "UNKNOWN/GENERIC", ++ .lnb = { ++ .mask = DM1105_LNB_MASK, ++ .off = DM1105_LNB_OFF, ++ .v13 = DM1105_LNB_13V, ++ .v18 = DM1105_LNB_18V, ++ }, ++ }, ++ [DM1105_BOARD_DVBWORLD_2002] = { ++ .name = "DVBWorld PCI 2002", ++ .lnb = { ++ .mask = DM1105_LNB_MASK, ++ .off = DM1105_LNB_OFF, ++ .v13 = DM1105_LNB_13V, ++ .v18 = DM1105_LNB_18V, ++ }, ++ }, ++ [DM1105_BOARD_DVBWORLD_2004] = { ++ .name = "DVBWorld PCI 2004", ++ .lnb = { ++ .mask = DM1105_LNB_MASK, ++ .off = DM1105_LNB_OFF, ++ .v13 = DM1105_LNB_13V, ++ .v18 = DM1105_LNB_18V, ++ }, ++ }, ++ [DM1105_BOARD_AXESS_DM05] = { ++ .name = "Axess/EasyTv DM05", ++ .lnb = { ++ .mask = DM05_LNB_MASK, ++ .off = DM05_LNB_OFF, ++ .v13 = DM05_LNB_13V, ++ .v18 = DM05_LNB_18V, ++ }, ++ }, ++ [DM1105_BOARD_UNBRANDED_I2C_ON_GPIO] = { ++ .name = "Unbranded DM1105 with i2c on GPIOs", ++ .lnb = { ++ .mask = UNBR_LNB_MASK, ++ .off = UNBR_LNB_OFF, ++ .v13 = UNBR_LNB_13V, ++ .v18 = UNBR_LNB_18V, ++ }, ++ .gpio_scl = GPIO14, ++ .gpio_sda = GPIO13, ++ }, ++}; ++ ++static const struct dm1105_subid dm1105_subids[] = { ++ { ++ .subvendor = 0x0000, ++ .subdevice = 0x2002, ++ .card = DM1105_BOARD_DVBWORLD_2002, ++ }, { ++ .subvendor = 0x0001, ++ .subdevice = 0x2002, ++ .card = DM1105_BOARD_DVBWORLD_2002, ++ }, { ++ .subvendor = 0x0000, ++ .subdevice = 0x2004, ++ .card = DM1105_BOARD_DVBWORLD_2004, ++ }, { ++ .subvendor = 0x0001, ++ .subdevice = 0x2004, ++ .card = DM1105_BOARD_DVBWORLD_2004, ++ }, { ++ .subvendor = 0x195d, ++ .subdevice = 0x1105, ++ .card = DM1105_BOARD_AXESS_DM05, ++ }, ++}; ++ ++static void dm1105_card_list(struct pci_dev *pci) ++{ ++ int i; ++ ++ if (0 == pci->subsystem_vendor && ++ 0 == pci->subsystem_device) { ++ printk(KERN_ERR ++ "dm1105: Your board has no valid PCI Subsystem ID\n" ++ "dm1105: and thus can't be autodetected\n" ++ "dm1105: Please pass card= insmod option to\n" ++ "dm1105: workaround that. Redirect complaints to\n" ++ "dm1105: the vendor of the TV card. Best regards,\n" ++ "dm1105: -- tux\n"); ++ } else { ++ printk(KERN_ERR ++ "dm1105: Your board isn't known (yet) to the driver.\n" ++ "dm1105: You can try to pick one of the existing\n" ++ "dm1105: card configs via card= insmod option.\n" ++ "dm1105: Updating to the latest version might help\n" ++ "dm1105: as well.\n"); ++ } ++ printk(KERN_ERR "Here is a list of valid choices for the card= " ++ "insmod option:\n"); ++ for (i = 0; i < ARRAY_SIZE(dm1105_boards); i++) ++ printk(KERN_ERR "dm1105: card=%d -> %s\n", ++ i, dm1105_boards[i].name); ++} ++ ++/* infrared remote control */ ++struct infrared { ++ struct rc_dev *dev; ++ char input_phys[32]; ++ struct work_struct work; ++ u32 ir_command; ++}; ++ ++struct dm1105_dev { ++ /* pci */ ++ struct pci_dev *pdev; ++ u8 __iomem *io_mem; ++ ++ /* ir */ ++ struct infrared ir; ++ ++ /* dvb */ ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ struct dmxdev dmxdev; ++ struct dvb_adapter dvb_adapter; ++ struct dvb_demux demux; ++ struct dvb_frontend *fe; ++ struct dvb_net dvbnet; ++ unsigned int full_ts_users; ++ unsigned int boardnr; ++ int nr; ++ ++ /* i2c */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_adapter i2c_bb_adap; ++ struct i2c_algo_bit_data i2c_bit; ++ ++ /* irq */ ++ struct work_struct work; ++ struct workqueue_struct *wq; ++ char wqn[16]; ++ ++ /* dma */ ++ dma_addr_t dma_addr; ++ unsigned char *ts_buf; ++ u32 wrp; ++ u32 nextwrp; ++ u32 buffer_size; ++ unsigned int PacketErrorCount; ++ unsigned int dmarst; ++ spinlock_t lock; ++}; ++ ++#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg])) ++ ++#define dm_readb(reg) inb(dm_io_mem(reg)) ++#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg))) ++ ++#define dm_readw(reg) inw(dm_io_mem(reg)) ++#define dm_writew(reg, value) outw((value), (dm_io_mem(reg))) ++ ++#define dm_readl(reg) inl(dm_io_mem(reg)) ++#define dm_writel(reg, value) outl((value), (dm_io_mem(reg))) ++ ++#define dm_andorl(reg, mask, value) \ ++ outl((inl(dm_io_mem(reg)) & ~(mask)) |\ ++ ((value) & (mask)), (dm_io_mem(reg))) ++ ++#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) ++#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) ++ ++/* The chip has 18 GPIOs. In HOST mode GPIO's used as 15 bit address lines, ++ so we can use only 3 GPIO's from GPIO15 to GPIO17. ++ Here I don't check whether HOST is enebled as it is not implemented yet. ++ */ ++static void dm1105_gpio_set(struct dm1105_dev *dev, u32 mask) ++{ ++ if (mask & 0xfffc0000) ++ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); ++ ++ if (mask & 0x0003ffff) ++ dm_setl(DM1105_GPIOVAL, mask & 0x0003ffff); ++ ++} ++ ++static void dm1105_gpio_clear(struct dm1105_dev *dev, u32 mask) ++{ ++ if (mask & 0xfffc0000) ++ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); ++ ++ if (mask & 0x0003ffff) ++ dm_clearl(DM1105_GPIOVAL, mask & 0x0003ffff); ++ ++} ++ ++static void dm1105_gpio_andor(struct dm1105_dev *dev, u32 mask, u32 val) ++{ ++ if (mask & 0xfffc0000) ++ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); ++ ++ if (mask & 0x0003ffff) ++ dm_andorl(DM1105_GPIOVAL, mask & 0x0003ffff, val); ++ ++} ++ ++static u32 dm1105_gpio_get(struct dm1105_dev *dev, u32 mask) ++{ ++ if (mask & 0xfffc0000) ++ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); ++ ++ if (mask & 0x0003ffff) ++ return dm_readl(DM1105_GPIOVAL) & mask & 0x0003ffff; ++ ++ return 0; ++} ++ ++static void dm1105_gpio_enable(struct dm1105_dev *dev, u32 mask, int asoutput) ++{ ++ if (mask & 0xfffc0000) ++ printk(KERN_ERR "%s: Only 18 GPIO's are allowed\n", __func__); ++ ++ if ((mask & 0x0003ffff) && asoutput) ++ dm_clearl(DM1105_GPIOCTR, mask & 0x0003ffff); ++ else if ((mask & 0x0003ffff) && !asoutput) ++ dm_setl(DM1105_GPIOCTR, mask & 0x0003ffff); ++ ++} ++ ++static void dm1105_setline(struct dm1105_dev *dev, u32 line, int state) ++{ ++ if (state) ++ dm1105_gpio_enable(dev, line, 0); ++ else { ++ dm1105_gpio_enable(dev, line, 1); ++ dm1105_gpio_clear(dev, line); ++ } ++} ++ ++static void dm1105_setsda(void *data, int state) ++{ ++ struct dm1105_dev *dev = data; ++ ++ dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_sda, state); ++} ++ ++static void dm1105_setscl(void *data, int state) ++{ ++ struct dm1105_dev *dev = data; ++ ++ dm1105_setline(dev, dm1105_boards[dev->boardnr].gpio_scl, state); ++} ++ ++static int dm1105_getsda(void *data) ++{ ++ struct dm1105_dev *dev = data; ++ ++ return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_sda) ++ ? 1 : 0; ++} ++ ++static int dm1105_getscl(void *data) ++{ ++ struct dm1105_dev *dev = data; ++ ++ return dm1105_gpio_get(dev, dm1105_boards[dev->boardnr].gpio_scl) ++ ? 1 : 0; ++} ++ ++static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct dm1105_dev *dev ; ++ ++ int addr, rc, i, j, k, len, byte, data; ++ u8 status; ++ ++ dev = i2c_adap->algo_data; ++ for (i = 0; i < num; i++) { ++ dm_writeb(DM1105_I2CCTR, 0x00); ++ if (msgs[i].flags & I2C_M_RD) { ++ /* read bytes */ ++ addr = msgs[i].addr << 1; ++ addr |= 1; ++ dm_writeb(DM1105_I2CDAT, addr); ++ for (byte = 0; byte < msgs[i].len; byte++) ++ dm_writeb(DM1105_I2CDAT + byte + 1, 0); ++ ++ dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); ++ for (j = 0; j < 55; j++) { ++ mdelay(10); ++ status = dm_readb(DM1105_I2CSTS); ++ if ((status & 0xc0) == 0x40) ++ break; ++ } ++ if (j >= 55) ++ return -1; ++ ++ for (byte = 0; byte < msgs[i].len; byte++) { ++ rc = dm_readb(DM1105_I2CDAT + byte + 1); ++ if (rc < 0) ++ goto err; ++ msgs[i].buf[byte] = rc; ++ } ++ } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { ++ /* prepaired for cx24116 firmware */ ++ /* Write in small blocks */ ++ len = msgs[i].len - 1; ++ k = 1; ++ do { ++ dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); ++ dm_writeb(DM1105_I2CDAT + 1, 0xf7); ++ for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { ++ data = msgs[i].buf[k + byte]; ++ dm_writeb(DM1105_I2CDAT + byte + 2, data); ++ } ++ dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len)); ++ for (j = 0; j < 25; j++) { ++ mdelay(10); ++ status = dm_readb(DM1105_I2CSTS); ++ if ((status & 0xc0) == 0x40) ++ break; ++ } ++ ++ if (j >= 25) ++ return -1; ++ ++ k += 48; ++ len -= 48; ++ } while (len > 0); ++ } else { ++ /* write bytes */ ++ dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); ++ for (byte = 0; byte < msgs[i].len; byte++) { ++ data = msgs[i].buf[byte]; ++ dm_writeb(DM1105_I2CDAT + byte + 1, data); ++ } ++ dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); ++ for (j = 0; j < 25; j++) { ++ mdelay(10); ++ status = dm_readb(DM1105_I2CSTS); ++ if ((status & 0xc0) == 0x40) ++ break; ++ } ++ ++ if (j >= 25) ++ return -1; ++ } ++ } ++ return num; ++ err: ++ return rc; ++} ++ ++static u32 functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm dm1105_algo = { ++ .master_xfer = dm1105_i2c_xfer, ++ .functionality = functionality, ++}; ++ ++static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed) ++{ ++ return container_of(feed->demux, struct dm1105_dev, demux); ++} ++ ++static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) ++{ ++ return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); ++} ++ ++static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); ++ ++ dm1105_gpio_enable(dev, dm1105_boards[dev->boardnr].lnb.mask, 1); ++ if (voltage == SEC_VOLTAGE_18) ++ dm1105_gpio_andor(dev, ++ dm1105_boards[dev->boardnr].lnb.mask, ++ dm1105_boards[dev->boardnr].lnb.v18); ++ else if (voltage == SEC_VOLTAGE_13) ++ dm1105_gpio_andor(dev, ++ dm1105_boards[dev->boardnr].lnb.mask, ++ dm1105_boards[dev->boardnr].lnb.v13); ++ else ++ dm1105_gpio_andor(dev, ++ dm1105_boards[dev->boardnr].lnb.mask, ++ dm1105_boards[dev->boardnr].lnb.off); ++ ++ return 0; ++} ++ ++static void dm1105_set_dma_addr(struct dm1105_dev *dev) ++{ ++ dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); ++} ++ ++static int __devinit dm1105_dma_map(struct dm1105_dev *dev) ++{ ++ dev->ts_buf = pci_alloc_consistent(dev->pdev, ++ 6 * DM1105_DMA_BYTES, ++ &dev->dma_addr); ++ ++ return !dev->ts_buf; ++} ++ ++static void dm1105_dma_unmap(struct dm1105_dev *dev) ++{ ++ pci_free_consistent(dev->pdev, ++ 6 * DM1105_DMA_BYTES, ++ dev->ts_buf, ++ dev->dma_addr); ++} ++ ++static void dm1105_enable_irqs(struct dm1105_dev *dev) ++{ ++ dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK); ++ dm_writeb(DM1105_CR, 1); ++} ++ ++static void dm1105_disable_irqs(struct dm1105_dev *dev) ++{ ++ dm_writeb(DM1105_INTMAK, INTMAK_IRM); ++ dm_writeb(DM1105_CR, 0); ++} ++ ++static int dm1105_start_feed(struct dvb_demux_feed *f) ++{ ++ struct dm1105_dev *dev = feed_to_dm1105_dev(f); ++ ++ if (dev->full_ts_users++ == 0) ++ dm1105_enable_irqs(dev); ++ ++ return 0; ++} ++ ++static int dm1105_stop_feed(struct dvb_demux_feed *f) ++{ ++ struct dm1105_dev *dev = feed_to_dm1105_dev(f); ++ ++ if (--dev->full_ts_users == 0) ++ dm1105_disable_irqs(dev); ++ ++ return 0; ++} ++ ++/* ir work handler */ ++static void dm1105_emit_key(struct work_struct *work) ++{ ++ struct infrared *ir = container_of(work, struct infrared, work); ++ u32 ircom = ir->ir_command; ++ u8 data; ++ ++ if (ir_debug) ++ printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom); ++ ++ data = (ircom >> 8) & 0x7f; ++ ++ rc_keydown(ir->dev, data, 0); ++} ++ ++/* work handler */ ++static void dm1105_dmx_buffer(struct work_struct *work) ++{ ++ struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work); ++ unsigned int nbpackets; ++ u32 oldwrp = dev->wrp; ++ u32 nextwrp = dev->nextwrp; ++ ++ if (!((dev->ts_buf[oldwrp] == 0x47) && ++ (dev->ts_buf[oldwrp + 188] == 0x47) && ++ (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) { ++ dev->PacketErrorCount++; ++ /* bad packet found */ ++ if ((dev->PacketErrorCount >= 2) && ++ (dev->dmarst == 0)) { ++ dm_writeb(DM1105_RST, 1); ++ dev->wrp = 0; ++ dev->PacketErrorCount = 0; ++ dev->dmarst = 0; ++ return; ++ } ++ } ++ ++ if (nextwrp < oldwrp) { ++ memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp); ++ nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188; ++ } else ++ nbpackets = (nextwrp - oldwrp) / 188; ++ ++ dev->wrp = nextwrp; ++ dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets); ++} ++ ++static irqreturn_t dm1105_irq(int irq, void *dev_id) ++{ ++ struct dm1105_dev *dev = dev_id; ++ ++ /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ ++ unsigned int intsts = dm_readb(DM1105_INTSTS); ++ dm_writeb(DM1105_INTSTS, intsts); ++ ++ switch (intsts) { ++ case INTSTS_TSIRQ: ++ case (INTSTS_TSIRQ | INTSTS_IR): ++ dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); ++ queue_work(dev->wq, &dev->work); ++ break; ++ case INTSTS_IR: ++ dev->ir.ir_command = dm_readl(DM1105_IRCODE); ++ schedule_work(&dev->ir.work); ++ break; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) ++{ ++ struct rc_dev *dev; ++ int err = -ENOMEM; ++ ++ dev = rc_allocate_device(); ++ if (!dev) ++ return -ENOMEM; ++ ++ snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), ++ "pci-%s/ir0", pci_name(dm1105->pdev)); ++ ++ dev->driver_name = MODULE_NAME; ++ dev->map_name = RC_MAP_DM1105_NEC; ++ dev->driver_type = RC_DRIVER_SCANCODE; ++ dev->input_name = "DVB on-card IR receiver"; ++ dev->input_phys = dm1105->ir.input_phys; ++ dev->input_id.bustype = BUS_PCI; ++ dev->input_id.version = 1; ++ if (dm1105->pdev->subsystem_vendor) { ++ dev->input_id.vendor = dm1105->pdev->subsystem_vendor; ++ dev->input_id.product = dm1105->pdev->subsystem_device; ++ } else { ++ dev->input_id.vendor = dm1105->pdev->vendor; ++ dev->input_id.product = dm1105->pdev->device; ++ } ++ dev->dev.parent = &dm1105->pdev->dev; ++ ++ INIT_WORK(&dm1105->ir.work, dm1105_emit_key); ++ ++ err = rc_register_device(dev); ++ if (err < 0) { ++ rc_free_device(dev); ++ return err; ++ } ++ ++ dm1105->ir.dev = dev; ++ return 0; ++} ++ ++static void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) ++{ ++ rc_unregister_device(dm1105->ir.dev); ++} ++ ++static int __devinit dm1105_hw_init(struct dm1105_dev *dev) ++{ ++ dm1105_disable_irqs(dev); ++ ++ dm_writeb(DM1105_HOST_CTR, 0); ++ ++ /*DATALEN 188,*/ ++ dm_writeb(DM1105_DTALENTH, 188); ++ /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ ++ dm_writew(DM1105_TSCTR, 0xc10a); ++ ++ /* map DMA and set address */ ++ dm1105_dma_map(dev); ++ dm1105_set_dma_addr(dev); ++ /* big buffer */ ++ dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); ++ dm_writeb(DM1105_INTCNT, 47); ++ ++ /* IR NEC mode enable */ ++ dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK)); ++ dm_writeb(DM1105_IRMODE, 0); ++ dm_writew(DM1105_SYSTEMCODE, 0); ++ ++ return 0; ++} ++ ++static void dm1105_hw_exit(struct dm1105_dev *dev) ++{ ++ dm1105_disable_irqs(dev); ++ ++ /* IR disable */ ++ dm_writeb(DM1105_IRCTR, 0); ++ dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK); ++ ++ dm1105_dma_unmap(dev); ++} ++ ++static struct stv0299_config sharp_z0194a_config = { ++ .demod_address = 0x68, ++ .inittab = sharp_z0194a_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP1, ++ .min_delay_ms = 100, ++ .set_symbol_rate = sharp_z0194a_set_symbol_rate, ++}; ++ ++static struct stv0288_config earda_config = { ++ .demod_address = 0x68, ++ .min_delay_ms = 100, ++}; ++ ++static struct si21xx_config serit_config = { ++ .demod_address = 0x68, ++ .min_delay_ms = 100, ++ ++}; ++ ++static struct cx24116_config serit_sp2633_config = { ++ .demod_address = 0x55, ++}; ++ ++static struct ds3000_config dvbworld_ds3000_config = { ++ .demod_address = 0x68, ++}; ++ ++static struct ts2020_config dvbworld_ts2020_config = { ++ .tuner_address = 0x60, ++ .clk_out_div = 1, ++}; ++ ++static int __devinit frontend_init(struct dm1105_dev *dev) ++{ ++ int ret; ++ ++ switch (dev->boardnr) { ++ case DM1105_BOARD_UNBRANDED_I2C_ON_GPIO: ++ dm1105_gpio_enable(dev, GPIO15, 1); ++ dm1105_gpio_clear(dev, GPIO15); ++ msleep(100); ++ dm1105_gpio_set(dev, GPIO15); ++ msleep(200); ++ dev->fe = dvb_attach( ++ stv0299_attach, &sharp_z0194a_config, ++ &dev->i2c_bb_adap); ++ if (dev->fe) { ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ dvb_attach(dvb_pll_attach, dev->fe, 0x60, ++ &dev->i2c_bb_adap, DVB_PLL_OPERA1); ++ break; ++ } ++ ++ dev->fe = dvb_attach( ++ stv0288_attach, &earda_config, ++ &dev->i2c_bb_adap); ++ if (dev->fe) { ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ dvb_attach(stb6000_attach, dev->fe, 0x61, ++ &dev->i2c_bb_adap); ++ break; ++ } ++ ++ dev->fe = dvb_attach( ++ si21xx_attach, &serit_config, ++ &dev->i2c_bb_adap); ++ if (dev->fe) ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ break; ++ case DM1105_BOARD_DVBWORLD_2004: ++ dev->fe = dvb_attach( ++ cx24116_attach, &serit_sp2633_config, ++ &dev->i2c_adap); ++ if (dev->fe) { ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ break; ++ } ++ ++ dev->fe = dvb_attach( ++ ds3000_attach, &dvbworld_ds3000_config, ++ &dev->i2c_adap); ++ if (dev->fe) { ++ dvb_attach(ts2020_attach, dev->fe, ++ &dvbworld_ts2020_config, &dev->i2c_adap); ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ } ++ ++ break; ++ case DM1105_BOARD_DVBWORLD_2002: ++ case DM1105_BOARD_AXESS_DM05: ++ default: ++ dev->fe = dvb_attach( ++ stv0299_attach, &sharp_z0194a_config, ++ &dev->i2c_adap); ++ if (dev->fe) { ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ dvb_attach(dvb_pll_attach, dev->fe, 0x60, ++ &dev->i2c_adap, DVB_PLL_OPERA1); ++ break; ++ } ++ ++ dev->fe = dvb_attach( ++ stv0288_attach, &earda_config, ++ &dev->i2c_adap); ++ if (dev->fe) { ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ dvb_attach(stb6000_attach, dev->fe, 0x61, ++ &dev->i2c_adap); ++ break; ++ } ++ ++ dev->fe = dvb_attach( ++ si21xx_attach, &serit_config, ++ &dev->i2c_adap); ++ if (dev->fe) ++ dev->fe->ops.set_voltage = dm1105_set_voltage; ++ ++ } ++ ++ if (!dev->fe) { ++ dev_err(&dev->pdev->dev, "could not attach frontend\n"); ++ return -ENODEV; ++ } ++ ++ ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe); ++ if (ret < 0) { ++ if (dev->fe->ops.release) ++ dev->fe->ops.release(dev->fe); ++ dev->fe = NULL; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac) ++{ ++ static u8 command[1] = { 0x28 }; ++ ++ struct i2c_msg msg[] = { ++ { ++ .addr = IIC_24C01_addr >> 1, ++ .flags = 0, ++ .buf = command, ++ .len = 1 ++ }, { ++ .addr = IIC_24C01_addr >> 1, ++ .flags = I2C_M_RD, ++ .buf = mac, ++ .len = 6 ++ }, ++ }; ++ ++ dm1105_i2c_xfer(&dev->i2c_adap, msg , 2); ++ dev_info(&dev->pdev->dev, "MAC %pM\n", mac); ++} ++ ++static int __devinit dm1105_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct dm1105_dev *dev; ++ struct dvb_adapter *dvb_adapter; ++ struct dvb_demux *dvbdemux; ++ struct dmx_demux *dmx; ++ int ret = -ENOMEM; ++ int i; ++ ++ dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ /* board config */ ++ dev->nr = dm1105_devcount; ++ dev->boardnr = UNSET; ++ if (card[dev->nr] < ARRAY_SIZE(dm1105_boards)) ++ dev->boardnr = card[dev->nr]; ++ for (i = 0; UNSET == dev->boardnr && ++ i < ARRAY_SIZE(dm1105_subids); i++) ++ if (pdev->subsystem_vendor == ++ dm1105_subids[i].subvendor && ++ pdev->subsystem_device == ++ dm1105_subids[i].subdevice) ++ dev->boardnr = dm1105_subids[i].card; ++ ++ if (UNSET == dev->boardnr) { ++ dev->boardnr = DM1105_BOARD_UNKNOWN; ++ dm1105_card_list(pdev); ++ } ++ ++ dm1105_devcount++; ++ dev->pdev = pdev; ++ dev->buffer_size = 5 * DM1105_DMA_BYTES; ++ dev->PacketErrorCount = 0; ++ dev->dmarst = 0; ++ ++ ret = pci_enable_device(pdev); ++ if (ret < 0) ++ goto err_kfree; ++ ++ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ pci_set_master(pdev); ++ ++ ret = pci_request_regions(pdev, DRIVER_NAME); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); ++ if (!dev->io_mem) { ++ ret = -EIO; ++ goto err_pci_release_regions; ++ } ++ ++ spin_lock_init(&dev->lock); ++ pci_set_drvdata(pdev, dev); ++ ++ ret = dm1105_hw_init(dev); ++ if (ret < 0) ++ goto err_pci_iounmap; ++ ++ /* i2c */ ++ i2c_set_adapdata(&dev->i2c_adap, dev); ++ strcpy(dev->i2c_adap.name, DRIVER_NAME); ++ dev->i2c_adap.owner = THIS_MODULE; ++ dev->i2c_adap.dev.parent = &pdev->dev; ++ dev->i2c_adap.algo = &dm1105_algo; ++ dev->i2c_adap.algo_data = dev; ++ ret = i2c_add_adapter(&dev->i2c_adap); ++ ++ if (ret < 0) ++ goto err_dm1105_hw_exit; ++ ++ i2c_set_adapdata(&dev->i2c_bb_adap, dev); ++ strcpy(dev->i2c_bb_adap.name, DM1105_I2C_GPIO_NAME); ++ dev->i2c_bb_adap.owner = THIS_MODULE; ++ dev->i2c_bb_adap.dev.parent = &pdev->dev; ++ dev->i2c_bb_adap.algo_data = &dev->i2c_bit; ++ dev->i2c_bit.data = dev; ++ dev->i2c_bit.setsda = dm1105_setsda; ++ dev->i2c_bit.setscl = dm1105_setscl; ++ dev->i2c_bit.getsda = dm1105_getsda; ++ dev->i2c_bit.getscl = dm1105_getscl; ++ dev->i2c_bit.udelay = 10; ++ dev->i2c_bit.timeout = 10; ++ ++ /* Raise SCL and SDA */ ++ dm1105_setsda(dev, 1); ++ dm1105_setscl(dev, 1); ++ ++ ret = i2c_bit_add_bus(&dev->i2c_bb_adap); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ /* dvb */ ++ ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, ++ THIS_MODULE, &pdev->dev, adapter_nr); ++ if (ret < 0) ++ goto err_i2c_del_adapters; ++ ++ dvb_adapter = &dev->dvb_adapter; ++ ++ dm1105_read_mac(dev, dvb_adapter->proposed_mac); ++ ++ dvbdemux = &dev->demux; ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = dm1105_start_feed; ++ dvbdemux->stop_feed = dm1105_stop_feed; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); ++ ret = dvb_dmx_init(dvbdemux); ++ if (ret < 0) ++ goto err_dvb_unregister_adapter; ++ ++ dmx = &dvbdemux->dmx; ++ dev->dmxdev.filternum = 256; ++ dev->dmxdev.demux = dmx; ++ dev->dmxdev.capabilities = 0; ++ ++ ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter); ++ if (ret < 0) ++ goto err_dvb_dmx_release; ++ ++ dev->hw_frontend.source = DMX_FRONTEND_0; ++ ++ ret = dmx->add_frontend(dmx, &dev->hw_frontend); ++ if (ret < 0) ++ goto err_dvb_dmxdev_release; ++ ++ dev->mem_frontend.source = DMX_MEMORY_FE; ++ ++ ret = dmx->add_frontend(dmx, &dev->mem_frontend); ++ if (ret < 0) ++ goto err_remove_hw_frontend; ++ ++ ret = dmx->connect_frontend(dmx, &dev->hw_frontend); ++ if (ret < 0) ++ goto err_remove_mem_frontend; ++ ++ ret = dvb_net_init(dvb_adapter, &dev->dvbnet, dmx); ++ if (ret < 0) ++ goto err_disconnect_frontend; ++ ++ ret = frontend_init(dev); ++ if (ret < 0) ++ goto err_dvb_net; ++ ++ dm1105_ir_init(dev); ++ ++ INIT_WORK(&dev->work, dm1105_dmx_buffer); ++ sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); ++ dev->wq = create_singlethread_workqueue(dev->wqn); ++ if (!dev->wq) { ++ ret = -ENOMEM; ++ goto err_dvb_net; ++ } ++ ++ ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED, ++ DRIVER_NAME, dev); ++ if (ret < 0) ++ goto err_workqueue; ++ ++ return 0; ++ ++err_workqueue: ++ destroy_workqueue(dev->wq); ++err_dvb_net: ++ dvb_net_release(&dev->dvbnet); ++err_disconnect_frontend: ++ dmx->disconnect_frontend(dmx); ++err_remove_mem_frontend: ++ dmx->remove_frontend(dmx, &dev->mem_frontend); ++err_remove_hw_frontend: ++ dmx->remove_frontend(dmx, &dev->hw_frontend); ++err_dvb_dmxdev_release: ++ dvb_dmxdev_release(&dev->dmxdev); ++err_dvb_dmx_release: ++ dvb_dmx_release(dvbdemux); ++err_dvb_unregister_adapter: ++ dvb_unregister_adapter(dvb_adapter); ++err_i2c_del_adapters: ++ i2c_del_adapter(&dev->i2c_bb_adap); ++err_i2c_del_adapter: ++ i2c_del_adapter(&dev->i2c_adap); ++err_dm1105_hw_exit: ++ dm1105_hw_exit(dev); ++err_pci_iounmap: ++ pci_iounmap(pdev, dev->io_mem); ++err_pci_release_regions: ++ pci_release_regions(pdev); ++err_pci_disable_device: ++ pci_disable_device(pdev); ++err_kfree: ++ pci_set_drvdata(pdev, NULL); ++ kfree(dev); ++ return ret; ++} ++ ++static void __devexit dm1105_remove(struct pci_dev *pdev) ++{ ++ struct dm1105_dev *dev = pci_get_drvdata(pdev); ++ struct dvb_adapter *dvb_adapter = &dev->dvb_adapter; ++ struct dvb_demux *dvbdemux = &dev->demux; ++ struct dmx_demux *dmx = &dvbdemux->dmx; ++ ++ dm1105_ir_exit(dev); ++ dmx->close(dmx); ++ dvb_net_release(&dev->dvbnet); ++ if (dev->fe) ++ dvb_unregister_frontend(dev->fe); ++ ++ dmx->disconnect_frontend(dmx); ++ dmx->remove_frontend(dmx, &dev->mem_frontend); ++ dmx->remove_frontend(dmx, &dev->hw_frontend); ++ dvb_dmxdev_release(&dev->dmxdev); ++ dvb_dmx_release(dvbdemux); ++ dvb_unregister_adapter(dvb_adapter); ++ if (&dev->i2c_adap) ++ i2c_del_adapter(&dev->i2c_adap); ++ ++ dm1105_hw_exit(dev); ++ synchronize_irq(pdev->irq); ++ free_irq(pdev->irq, dev); ++ pci_iounmap(pdev, dev->io_mem); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++ dm1105_devcount--; ++ kfree(dev); ++} ++ ++static struct pci_device_id dm1105_id_table[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_TRIGEM, ++ .device = PCI_DEVICE_ID_DM1105, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ .vendor = PCI_VENDOR_ID_AXESS, ++ .device = PCI_DEVICE_ID_DM05, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ /* empty */ ++ }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, dm1105_id_table); ++ ++static struct pci_driver dm1105_driver = { ++ .name = DRIVER_NAME, ++ .id_table = dm1105_id_table, ++ .probe = dm1105_probe, ++ .remove = __devexit_p(dm1105_remove), ++}; ++ ++static int __init dm1105_init(void) ++{ ++ return pci_register_driver(&dm1105_driver); ++} ++ ++static void __exit dm1105_exit(void) ++{ ++ pci_unregister_driver(&dm1105_driver); ++} ++ ++module_init(dm1105_init); ++module_exit(dm1105_exit); ++ ++MODULE_AUTHOR("Igor M. Liplianin "); ++MODULE_DESCRIPTION("SDMC DM1105 DVB driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/ivtv/Kconfig b/drivers/media/pci/ivtv/Kconfig +new file mode 100644 +index 0000000..dd6ee57 +--- /dev/null ++++ b/drivers/media/pci/ivtv/Kconfig +@@ -0,0 +1,61 @@ ++config VIDEO_IVTV ++ tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" ++ depends on VIDEO_V4L2 && PCI && I2C ++ select I2C_ALGOBIT ++ depends on RC_CORE ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select VIDEO_CX2341X ++ select VIDEO_CX25840 ++ select VIDEO_MSP3400 ++ select VIDEO_SAA711X ++ select VIDEO_SAA717X ++ select VIDEO_SAA7127 ++ select VIDEO_CS53L32A ++ select VIDEO_M52790 ++ select VIDEO_WM8775 ++ select VIDEO_WM8739 ++ select VIDEO_VP27SMPX ++ select VIDEO_UPD64031A ++ select VIDEO_UPD64083 ++ ---help--- ++ This is a video4linux driver for Conexant cx23416 or cx23415 based ++ PCI personal video recorder devices. ++ ++ This is used in devices such as the Hauppauge PVR-150/250/350/500 ++ cards. There is a driver homepage at . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ivtv. ++ ++config VIDEO_IVTV_ALSA ++ tristate "Conexant cx23415/cx23416 ALSA interface for PCM audio capture" ++ depends on VIDEO_IVTV && SND ++ select SND_PCM ++ ---help--- ++ This driver provides an ALSA interface as another method for user ++ applications to obtain PCM audio data from Conexant cx23415/cx23416 ++ based PCI TV cards supported by the ivtv driver. ++ ++ The ALSA interface has much wider use in user applications performing ++ PCM audio capture, than the V4L2 "/dev/video24" PCM audio interface ++ provided by the main ivtv driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ivtv-alsa. ++ ++config VIDEO_FB_IVTV ++ tristate "Conexant cx23415 framebuffer support" ++ depends on VIDEO_IVTV && FB ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ ---help--- ++ This is a framebuffer driver for the Conexant cx23415 MPEG ++ encoder/decoder. ++ ++ This is used in the Hauppauge PVR-350 card. There is a driver ++ homepage at . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called ivtvfb. +diff --git a/drivers/media/pci/ivtv/Makefile b/drivers/media/pci/ivtv/Makefile +new file mode 100644 +index 0000000..0eaa882 +--- /dev/null ++++ b/drivers/media/pci/ivtv/Makefile +@@ -0,0 +1,16 @@ ++ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \ ++ ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \ ++ ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \ ++ ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \ ++ ivtv-vbi.o ivtv-yuv.o ++ivtv-alsa-objs := ivtv-alsa-main.o ivtv-alsa-pcm.o ++ ++obj-$(CONFIG_VIDEO_IVTV) += ivtv.o ++obj-$(CONFIG_VIDEO_IVTV_ALSA) += ivtv-alsa.o ++obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o ++ ++ccflags-y += -I$(srctree)/drivers/media/i2c ++ccflags-y += -I$(srctree)/drivers/media/tuners ++ccflags-y += -I$(srctree)/drivers/media/dvb-core ++ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ++ +diff --git a/drivers/media/pci/ivtv/ivtv-alsa-main.c b/drivers/media/pci/ivtv/ivtv-alsa-main.c +new file mode 100644 +index 0000000..4a221c6 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa-main.c +@@ -0,0 +1,303 @@ ++/* ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * Copyright (C) 2009 Devin Heitmueller ++ * ++ * Portions of this work were sponsored by ONELAN Limited for the cx18 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "ivtv-driver.h" ++#include "ivtv-version.h" ++#include "ivtv-alsa.h" ++#include "ivtv-alsa-mixer.h" ++#include "ivtv-alsa-pcm.h" ++ ++int ivtv_alsa_debug; ++ ++#define IVTV_DEBUG_ALSA_INFO(fmt, arg...) \ ++ do { \ ++ if (ivtv_alsa_debug & 2) \ ++ pr_info("%s: " fmt, "ivtv-alsa", ## arg); \ ++ } while (0) ++ ++module_param_named(debug, ivtv_alsa_debug, int, 0644); ++MODULE_PARM_DESC(debug, ++ "Debug level (bitmask). Default: 0\n" ++ "\t\t\t 1/0x0001: warning\n" ++ "\t\t\t 2/0x0002: info\n"); ++ ++MODULE_AUTHOR("Andy Walls"); ++MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface"); ++MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_VERSION(IVTV_VERSION); ++ ++static inline ++struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev) ++{ ++ return to_ivtv(v4l2_dev)->alsa; ++} ++ ++static inline ++struct snd_ivtv_card *p_to_snd_ivtv_card(struct v4l2_device **v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct snd_ivtv_card, v4l2_dev); ++} ++ ++static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc) ++{ ++ if (itvsc == NULL) ++ return; ++ ++ if (itvsc->v4l2_dev != NULL) ++ to_ivtv(itvsc->v4l2_dev)->alsa = NULL; ++ ++ /* FIXME - take any other stopping actions needed */ ++ ++ kfree(itvsc); ++} ++ ++static void snd_ivtv_card_private_free(struct snd_card *sc) ++{ ++ if (sc == NULL) ++ return; ++ snd_ivtv_card_free(sc->private_data); ++ sc->private_data = NULL; ++ sc->private_free = NULL; ++} ++ ++static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev, ++ struct snd_card *sc, ++ struct snd_ivtv_card **itvsc) ++{ ++ *itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL); ++ if (*itvsc == NULL) ++ return -ENOMEM; ++ ++ (*itvsc)->v4l2_dev = v4l2_dev; ++ (*itvsc)->sc = sc; ++ ++ sc->private_data = *itvsc; ++ sc->private_free = snd_ivtv_card_private_free; ++ ++ return 0; ++} ++ ++static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc) ++{ ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ struct snd_card *sc = itvsc->sc; ++ ++ /* sc->driver is used by alsa-lib's configurator: simple, unique */ ++ strlcpy(sc->driver, "CX2341[56]", sizeof(sc->driver)); ++ ++ /* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */ ++ snprintf(sc->shortname, sizeof(sc->shortname), "IVTV-%d", ++ itv->instance); ++ ++ /* sc->longname is read from /proc/asound/cards */ ++ snprintf(sc->longname, sizeof(sc->longname), ++ "CX2341[56] #%d %s TV/FM Radio/Line-In Capture", ++ itv->instance, itv->card_name); ++ ++ return 0; ++} ++ ++static int snd_ivtv_init(struct v4l2_device *v4l2_dev) ++{ ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ struct snd_card *sc = NULL; ++ struct snd_ivtv_card *itvsc; ++ int ret; ++ ++ /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ ++ ++ /* (1) Check and increment the device index */ ++ /* This is a no-op for us. We'll use the itv->instance */ ++ ++ /* (2) Create a card instance */ ++ ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ ++ SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ ++ THIS_MODULE, 0, &sc); ++ if (ret) { ++ IVTV_ALSA_ERR("%s: snd_card_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit; ++ } ++ ++ /* (3) Create a main component */ ++ ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc); ++ if (ret) { ++ IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ ++ /* (4) Set the driver ID and name strings */ ++ snd_ivtv_card_set_names(itvsc); ++ ++ /* (5) Create other components: mixer, PCM, & proc files */ ++#if 0 ++ ret = snd_ivtv_mixer_create(itvsc); ++ if (ret) { ++ IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d:" ++ " proceeding anyway\n", __func__, ret); ++ } ++#endif ++ ++ ret = snd_ivtv_pcm_create(itvsc); ++ if (ret) { ++ IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ /* FIXME - proc files */ ++ ++ /* (7) Set the driver data and return 0 */ ++ /* We do this out of normal order for PCI drivers to avoid races */ ++ itv->alsa = itvsc; ++ ++ /* (6) Register the card instance */ ++ ret = snd_card_register(sc); ++ if (ret) { ++ itv->alsa = NULL; ++ IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n", ++ __func__, ret); ++ goto err_exit_free; ++ } ++ ++ return 0; ++ ++err_exit_free: ++ if (sc != NULL) ++ snd_card_free(sc); ++ kfree(itvsc); ++err_exit: ++ return ret; ++} ++ ++static int __init ivtv_alsa_load(struct ivtv *itv) ++{ ++ struct v4l2_device *v4l2_dev = &itv->v4l2_dev; ++ struct ivtv_stream *s; ++ ++ if (v4l2_dev == NULL) { ++ pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ itv = to_ivtv(v4l2_dev); ++ if (itv == NULL) { ++ pr_err("ivtv-alsa itv is NULL\n"); ++ return 0; ++ } ++ ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; ++ if (s->vdev == NULL) { ++ IVTV_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " ++ "skipping\n", __func__); ++ return 0; ++ } ++ ++ if (itv->alsa != NULL) { ++ IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n", ++ __func__); ++ return 0; ++ } ++ ++ if (snd_ivtv_init(v4l2_dev)) { ++ IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n", ++ __func__); ++ } else { ++ IVTV_DEBUG_ALSA_INFO("%s: created ivtv ALSA interface instance " ++ "\n", __func__); ++ } ++ return 0; ++} ++ ++static int __init ivtv_alsa_init(void) ++{ ++ pr_info("ivtv-alsa: module loading...\n"); ++ ivtv_ext_init = &ivtv_alsa_load; ++ return 0; ++} ++ ++static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc) ++{ ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ ++ /* FIXME - pointer checks & shutdown itvsc */ ++ ++ snd_card_free(itvsc->sc); ++ itv->alsa = NULL; ++} ++ ++static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct snd_ivtv_card *itvsc; ++ ++ if (v4l2_dev == NULL) { ++ pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ itvsc = to_snd_ivtv_card(v4l2_dev); ++ if (itvsc == NULL) { ++ IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n", ++ __func__); ++ return 0; ++ } ++ ++ snd_ivtv_exit(itvsc); ++ return 0; ++} ++ ++static void __exit ivtv_alsa_exit(void) ++{ ++ struct device_driver *drv; ++ int ret; ++ ++ pr_info("ivtv-alsa: module unloading...\n"); ++ ++ drv = driver_find("ivtv", &pci_bus_type); ++ ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback); ++ (void)ret; /* suppress compiler warning */ ++ ++ ivtv_ext_init = NULL; ++ pr_info("ivtv-alsa: module unload complete\n"); ++} ++ ++module_init(ivtv_alsa_init); ++module_exit(ivtv_alsa_exit); +diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.c b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c +new file mode 100644 +index 0000000..33ec05b +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.c +@@ -0,0 +1,175 @@ ++/* ++ * ALSA mixer controls for the ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#include "ivtv-alsa.h" ++#include "ivtv-driver.h" ++ ++/* ++ * Note the cx25840-core volume scale is funny, due to the alignment of the ++ * scale with another chip's range: ++ * ++ * v4l2_control value /512 indicated dB actual dB reg 0x8d4 ++ * 0x0000 - 0x01ff 0 -119 -96 228 ++ * 0x0200 - 0x02ff 1 -118 -96 228 ++ * ... ++ * 0x2c00 - 0x2dff 22 -97 -96 228 ++ * 0x2e00 - 0x2fff 23 -96 -96 228 ++ * 0x3000 - 0x31ff 24 -95 -95 226 ++ * ... ++ * 0xee00 - 0xefff 119 0 0 36 ++ * ... ++ * 0xfe00 - 0xffff 127 +8 +8 20 ++ */ ++static inline int dB_to_cx25840_vol(int dB) ++{ ++ if (dB < -96) ++ dB = -96; ++ else if (dB > 8) ++ dB = 8; ++ return (dB + 119) << 9; ++} ++ ++static inline int cx25840_vol_to_dB(int v) ++{ ++ if (v < (23 << 9)) ++ v = (23 << 9); ++ else if (v > (127 << 9)) ++ v = (127 << 9); ++ return (v >> 9) - 119; ++} ++ ++static int snd_ivtv_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ /* We're already translating values, just keep this control in dB */ ++ uinfo->value.integer.min = -96; ++ uinfo->value.integer.max = 8; ++ uinfo->value.integer.step = 1; ++ return 0; ++} ++ ++static int snd_ivtv_mixer_tv_vol_get(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *uctl) ++{ ++ struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl); ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ struct v4l2_control vctrl; ++ int ret; ++ ++ vctrl.id = V4L2_CID_AUDIO_VOLUME; ++ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); ++ ++ snd_ivtv_lock(itvsc); ++ ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl); ++ snd_ivtv_unlock(itvsc); ++ ++ if (!ret) ++ uctl->value.integer.value[0] = cx25840_vol_to_dB(vctrl.value); ++ return ret; ++} ++ ++static int snd_ivtv_mixer_tv_vol_put(struct snd_kcontrol *kctl, ++ struct snd_ctl_elem_value *uctl) ++{ ++ struct snd_ivtv_card *itvsc = snd_kcontrol_chip(kctl); ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ struct v4l2_control vctrl; ++ int ret; ++ ++ vctrl.id = V4L2_CID_AUDIO_VOLUME; ++ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); ++ ++ snd_ivtv_lock(itvsc); ++ ++ /* Fetch current state */ ++ ret = v4l2_subdev_call(itv->sd_audio, core, g_ctrl, &vctrl); ++ ++ if (ret || ++ (cx25840_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { ++ ++ /* Set, if needed */ ++ vctrl.value = dB_to_cx25840_vol(uctl->value.integer.value[0]); ++ ret = v4l2_subdev_call(itv->sd_audio, core, s_ctrl, &vctrl); ++ if (!ret) ++ ret = 1; /* Indicate control was changed w/o error */ ++ } ++ snd_ivtv_unlock(itvsc); ++ ++ return ret; ++} ++ ++ ++/* This is a bit of overkill, the slider is already in dB internally */ ++static DECLARE_TLV_DB_SCALE(snd_ivtv_mixer_tv_vol_db_scale, -9600, 100, 0); ++ ++static struct snd_kcontrol_new snd_ivtv_mixer_tv_vol __initdata = { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "Analog TV Capture Volume", ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | ++ SNDRV_CTL_ELEM_ACCESS_TLV_READ, ++ .info = snd_ivtv_mixer_tv_volume_info, ++ .get = snd_ivtv_mixer_tv_volume_get, ++ .put = snd_ivtv_mixer_tv_volume_put, ++ .tlv.p = snd_ivtv_mixer_tv_vol_db_scale ++}; ++ ++/* FIXME - add mute switch and balance, bass, treble sliders: ++ V4L2_CID_AUDIO_MUTE ++ ++ V4L2_CID_AUDIO_BALANCE ++ ++ V4L2_CID_AUDIO_BASS ++ V4L2_CID_AUDIO_TREBLE ++*/ ++ ++/* FIXME - add stereo, lang1, lang2, mono menu */ ++/* FIXME - add I2S volume */ ++ ++int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc) ++{ ++ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; ++ struct snd_card *sc = itvsc->sc; ++ int ret; ++ ++ strlcpy(sc->mixername, "CX2341[56] Mixer", sizeof(sc->mixername)); ++ ++ ret = snd_ctl_add(sc, snd_ctl_new1(snd_ivtv_mixer_tv_vol, itvsc)); ++ if (ret) { ++ IVTV_ALSA_WARN("%s: failed to add %s control, err %d\n", ++ __func__, snd_ivtv_mixer_tv_vol.name, ret); ++ } ++ return ret; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-alsa-mixer.h b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h +new file mode 100644 +index 0000000..cdde367 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa-mixer.h +@@ -0,0 +1,23 @@ ++/* ++ * ALSA mixer controls for the ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int __init snd_ivtv_mixer_create(struct snd_ivtv_card *itvsc); +diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.c b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +new file mode 100644 +index 0000000..e1863db +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.c +@@ -0,0 +1,358 @@ ++/* ++ * ALSA PCM device for the ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * Copyright (C) 2009 Devin Heitmueller ++ * ++ * Portions of this work were sponsored by ONELAN Limited for the cx18 driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "ivtv-driver.h" ++#include "ivtv-queue.h" ++#include "ivtv-streams.h" ++#include "ivtv-fileops.h" ++#include "ivtv-alsa.h" ++#include "ivtv-alsa-pcm.h" ++ ++static unsigned int pcm_debug; ++module_param(pcm_debug, int, 0644); ++MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); ++ ++#define dprintk(fmt, arg...) \ ++ do { \ ++ if (pcm_debug) \ ++ pr_info("ivtv-alsa-pcm %s: " fmt, __func__, ##arg); \ ++ } while (0) ++ ++static struct snd_pcm_hardware snd_ivtv_hw_capture = { ++ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_MMAP_VALID, ++ ++ .formats = SNDRV_PCM_FMTBIT_S16_LE, ++ ++ .rates = SNDRV_PCM_RATE_48000, ++ ++ .rate_min = 48000, ++ .rate_max = 48000, ++ .channels_min = 2, ++ .channels_max = 2, ++ .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ ++ .period_bytes_min = 64, /* 12544/2, */ ++ .period_bytes_max = 12544, ++ .periods_min = 2, ++ .periods_max = 98, /* 12544, */ ++}; ++ ++static void ivtv_alsa_announce_pcm_data(struct snd_ivtv_card *itvsc, ++ u8 *pcm_data, ++ size_t num_bytes) ++{ ++ struct snd_pcm_substream *substream; ++ struct snd_pcm_runtime *runtime; ++ unsigned int oldptr; ++ unsigned int stride; ++ int period_elapsed = 0; ++ int length; ++ ++ dprintk("ivtv alsa announce ptr=%p data=%p num_bytes=%zd\n", itvsc, ++ pcm_data, num_bytes); ++ ++ substream = itvsc->capture_pcm_substream; ++ if (substream == NULL) { ++ dprintk("substream was NULL\n"); ++ return; ++ } ++ ++ runtime = substream->runtime; ++ if (runtime == NULL) { ++ dprintk("runtime was NULL\n"); ++ return; ++ } ++ ++ stride = runtime->frame_bits >> 3; ++ if (stride == 0) { ++ dprintk("stride is zero\n"); ++ return; ++ } ++ ++ length = num_bytes / stride; ++ if (length == 0) { ++ dprintk("%s: length was zero\n", __func__); ++ return; ++ } ++ ++ if (runtime->dma_area == NULL) { ++ dprintk("dma area was NULL - ignoring\n"); ++ return; ++ } ++ ++ oldptr = itvsc->hwptr_done_capture; ++ if (oldptr + length >= runtime->buffer_size) { ++ unsigned int cnt = ++ runtime->buffer_size - oldptr; ++ memcpy(runtime->dma_area + oldptr * stride, pcm_data, ++ cnt * stride); ++ memcpy(runtime->dma_area, pcm_data + cnt * stride, ++ length * stride - cnt * stride); ++ } else { ++ memcpy(runtime->dma_area + oldptr * stride, pcm_data, ++ length * stride); ++ } ++ snd_pcm_stream_lock(substream); ++ ++ itvsc->hwptr_done_capture += length; ++ if (itvsc->hwptr_done_capture >= ++ runtime->buffer_size) ++ itvsc->hwptr_done_capture -= ++ runtime->buffer_size; ++ ++ itvsc->capture_transfer_done += length; ++ if (itvsc->capture_transfer_done >= ++ runtime->period_size) { ++ itvsc->capture_transfer_done -= ++ runtime->period_size; ++ period_elapsed = 1; ++ } ++ ++ snd_pcm_stream_unlock(substream); ++ ++ if (period_elapsed) ++ snd_pcm_period_elapsed(substream); ++} ++ ++static int snd_ivtv_pcm_capture_open(struct snd_pcm_substream *substream) ++{ ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ struct ivtv_stream *s; ++ struct ivtv_open_id item; ++ int ret; ++ ++ /* Instruct the CX2341[56] to start sending packets */ ++ snd_ivtv_lock(itvsc); ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; ++ ++ v4l2_fh_init(&item.fh, s->vdev); ++ item.itv = itv; ++ item.type = s->type; ++ ++ /* See if the stream is available */ ++ if (ivtv_claim_stream(&item, item.type)) { ++ /* No, it's already in use */ ++ snd_ivtv_unlock(itvsc); ++ return -EBUSY; ++ } ++ ++ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || ++ test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ /* We're already streaming. No additional action required */ ++ snd_ivtv_unlock(itvsc); ++ return 0; ++ } ++ ++ ++ runtime->hw = snd_ivtv_hw_capture; ++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); ++ itvsc->capture_pcm_substream = substream; ++ runtime->private_data = itv; ++ ++ itv->pcm_announce_callback = ivtv_alsa_announce_pcm_data; ++ ++ /* Not currently streaming, so start it up */ ++ set_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ ret = ivtv_start_v4l2_encode_stream(s); ++ snd_ivtv_unlock(itvsc); ++ ++ return ret; ++} ++ ++static int snd_ivtv_pcm_capture_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ struct ivtv_stream *s; ++ ++ /* Instruct the ivtv to stop sending packets */ ++ snd_ivtv_lock(itvsc); ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; ++ ivtv_stop_v4l2_encode_stream(s, 0); ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ ++ ivtv_release_stream(s); ++ ++ itv->pcm_announce_callback = NULL; ++ snd_ivtv_unlock(itvsc); ++ ++ return 0; ++} ++ ++static int snd_ivtv_pcm_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg) ++{ ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ int ret; ++ ++ snd_ivtv_lock(itvsc); ++ ret = snd_pcm_lib_ioctl(substream, cmd, arg); ++ snd_ivtv_unlock(itvsc); ++ return ret; ++} ++ ++ ++static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, ++ size_t size) ++{ ++ struct snd_pcm_runtime *runtime = subs->runtime; ++ ++ dprintk("Allocating vbuffer\n"); ++ if (runtime->dma_area) { ++ if (runtime->dma_bytes > size) ++ return 0; ++ ++ vfree(runtime->dma_area); ++ } ++ runtime->dma_area = vmalloc(size); ++ if (!runtime->dma_area) ++ return -ENOMEM; ++ ++ runtime->dma_bytes = size; ++ ++ return 0; ++} ++ ++static int snd_ivtv_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ dprintk("%s called\n", __func__); ++ ++ return snd_pcm_alloc_vmalloc_buffer(substream, ++ params_buffer_bytes(params)); ++} ++ ++static int snd_ivtv_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&itvsc->slock, flags); ++ if (substream->runtime->dma_area) { ++ dprintk("freeing pcm capture region\n"); ++ vfree(substream->runtime->dma_area); ++ substream->runtime->dma_area = NULL; ++ } ++ spin_unlock_irqrestore(&itvsc->slock, flags); ++ ++ return 0; ++} ++ ++static int snd_ivtv_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ ++ itvsc->hwptr_done_capture = 0; ++ itvsc->capture_transfer_done = 0; ++ ++ return 0; ++} ++ ++static int snd_ivtv_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ return 0; ++} ++ ++static ++snd_pcm_uframes_t snd_ivtv_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ unsigned long flags; ++ snd_pcm_uframes_t hwptr_done; ++ struct snd_ivtv_card *itvsc = snd_pcm_substream_chip(substream); ++ ++ spin_lock_irqsave(&itvsc->slock, flags); ++ hwptr_done = itvsc->hwptr_done_capture; ++ spin_unlock_irqrestore(&itvsc->slock, flags); ++ ++ return hwptr_done; ++} ++ ++static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, ++ unsigned long offset) ++{ ++ void *pageptr = subs->runtime->dma_area + offset; ++ ++ return vmalloc_to_page(pageptr); ++} ++ ++static struct snd_pcm_ops snd_ivtv_pcm_capture_ops = { ++ .open = snd_ivtv_pcm_capture_open, ++ .close = snd_ivtv_pcm_capture_close, ++ .ioctl = snd_ivtv_pcm_ioctl, ++ .hw_params = snd_ivtv_pcm_hw_params, ++ .hw_free = snd_ivtv_pcm_hw_free, ++ .prepare = snd_ivtv_pcm_prepare, ++ .trigger = snd_ivtv_pcm_trigger, ++ .pointer = snd_ivtv_pcm_pointer, ++ .page = snd_pcm_get_vmalloc_page, ++}; ++ ++int snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc) ++{ ++ struct snd_pcm *sp; ++ struct snd_card *sc = itvsc->sc; ++ struct v4l2_device *v4l2_dev = itvsc->v4l2_dev; ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ int ret; ++ ++ ret = snd_pcm_new(sc, "CX2341[56] PCM", ++ 0, /* PCM device 0, the only one for this card */ ++ 0, /* 0 playback substreams */ ++ 1, /* 1 capture substream */ ++ &sp); ++ if (ret) { ++ IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", ++ __func__, ret); ++ goto err_exit; ++ } ++ ++ spin_lock_init(&itvsc->slock); ++ ++ snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, ++ &snd_ivtv_pcm_capture_ops); ++ sp->info_flags = 0; ++ sp->private_data = itvsc; ++ strlcpy(sp->name, itv->card_name, sizeof(sp->name)); ++ ++ return 0; ++ ++err_exit: ++ return ret; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-alsa-pcm.h b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h +new file mode 100644 +index 0000000..23dfe0d +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa-pcm.h +@@ -0,0 +1,23 @@ ++/* ++ * ALSA PCM device for the ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++int __init snd_ivtv_pcm_create(struct snd_ivtv_card *itvsc); +diff --git a/drivers/media/pci/ivtv/ivtv-alsa.h b/drivers/media/pci/ivtv/ivtv-alsa.h +new file mode 100644 +index 0000000..4a0d8f2 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-alsa.h +@@ -0,0 +1,75 @@ ++/* ++ * ALSA interface to ivtv PCM capture streams ++ * ++ * Copyright (C) 2009,2012 Andy Walls ++ * Copyright (C) 2009 Devin Heitmueller ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ * 02111-1307 USA ++ */ ++ ++struct snd_card; ++ ++struct snd_ivtv_card { ++ struct v4l2_device *v4l2_dev; ++ struct snd_card *sc; ++ unsigned int capture_transfer_done; ++ unsigned int hwptr_done_capture; ++ struct snd_pcm_substream *capture_pcm_substream; ++ spinlock_t slock; ++}; ++ ++extern int ivtv_alsa_debug; ++ ++/* ++ * File operations that manipulate the encoder or video or audio subdevices ++ * need to be serialized. Use the same lock we use for v4l2 file ops. ++ */ ++static inline void snd_ivtv_lock(struct snd_ivtv_card *itvsc) ++{ ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ mutex_lock(&itv->serialize_lock); ++} ++ ++static inline void snd_ivtv_unlock(struct snd_ivtv_card *itvsc) ++{ ++ struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); ++ mutex_unlock(&itv->serialize_lock); ++} ++ ++#define IVTV_ALSA_DBGFLG_WARN (1 << 0) ++#define IVTV_ALSA_DBGFLG_INFO (1 << 1) ++ ++#define IVTV_ALSA_DEBUG(x, type, fmt, args...) \ ++ do { \ ++ if ((x) & ivtv_alsa_debug) \ ++ pr_info("%s-alsa: " type ": " fmt, \ ++ v4l2_dev->name , ## args); \ ++ } while (0) ++ ++#define IVTV_ALSA_DEBUG_WARN(fmt, args...) \ ++ IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_WARN, "warning", fmt , ## args) ++ ++#define IVTV_ALSA_DEBUG_INFO(fmt, args...) \ ++ IVTV_ALSA_DEBUG(IVTV_ALSA_DBGFLG_INFO, "info", fmt , ## args) ++ ++#define IVTV_ALSA_ERR(fmt, args...) \ ++ pr_err("%s-alsa: " fmt, v4l2_dev->name , ## args) ++ ++#define IVTV_ALSA_WARN(fmt, args...) \ ++ pr_warn("%s-alsa: " fmt, v4l2_dev->name , ## args) ++ ++#define IVTV_ALSA_INFO(fmt, args...) \ ++ pr_info("%s-alsa: " fmt, v4l2_dev->name , ## args) +diff --git a/drivers/media/pci/ivtv/ivtv-cards.c b/drivers/media/pci/ivtv/ivtv-cards.c +new file mode 100644 +index 0000000..145e474 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-cards.c +@@ -0,0 +1,1370 @@ ++/* ++ Functions to query card hardware ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-cards.h" ++#include "ivtv-i2c.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ ++ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER) ++#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) ++#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \ ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) ++#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \ ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) ++#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ ++ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) ++ ++#define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM) ++ ++/* usual i2c tuner addresses to probe */ ++static struct ivtv_card_tuner_i2c ivtv_i2c_std = { ++ .radio = { I2C_CLIENT_END }, ++ .demod = { 0x43, I2C_CLIENT_END }, ++ .tv = { 0x61, 0x60, I2C_CLIENT_END }, ++}; ++ ++/* as above, but with possible radio tuner */ ++static struct ivtv_card_tuner_i2c ivtv_i2c_radio = { ++ .radio = { 0x60, I2C_CLIENT_END }, ++ .demod = { 0x43, I2C_CLIENT_END }, ++ .tv = { 0x61, I2C_CLIENT_END }, ++}; ++ ++/* using the tda8290+75a combo */ ++static struct ivtv_card_tuner_i2c ivtv_i2c_tda8290 = { ++ .radio = { I2C_CLIENT_END }, ++ .demod = { I2C_CLIENT_END }, ++ .tv = { 0x4b, I2C_CLIENT_END }, ++}; ++ ++/********************** card configuration *******************************/ ++ ++/* Please add new PCI IDs to: http://pci-ids.ucw.cz/ ++ This keeps the PCI ID database up to date. Note that the entries ++ must be added under vendor 0x4444 (Conexant) as subsystem IDs. ++ New vendor IDs should still be added to the vendor ID list. */ ++ ++/* Hauppauge PVR-250 cards */ ++ ++/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */ ++static const struct ivtv_card ivtv_card_pvr250 = { ++ .type = IVTV_CARD_PVR_250, ++ .name = "Hauppauge WinTV PVR-250", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_MSP34XX, ++ .hw_audio_ctrl = IVTV_HW_MSP34XX, ++ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | ++ IVTV_HW_TVEEPROM | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, ++ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, ++ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, ++ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, ++ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Hauppauge PVR-350 cards */ ++ ++/* Outputs for Hauppauge PVR350 cards */ ++static struct ivtv_card_output ivtv_pvr350_outputs[] = { ++ { ++ .name = "S-Video + Composite", ++ .video_output = 0, ++ }, { ++ .name = "Composite", ++ .video_output = 1, ++ }, { ++ .name = "S-Video", ++ .video_output = 2, ++ }, { ++ .name = "RGB", ++ .video_output = 3, ++ }, { ++ .name = "YUV C", ++ .video_output = 4, ++ }, { ++ .name = "YUV V", ++ .video_output = 5, ++ } ++}; ++ ++static const struct ivtv_card ivtv_card_pvr350 = { ++ .type = IVTV_CARD_PVR_350, ++ .name = "Hauppauge WinTV PVR-350", ++ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, ++ .video_outputs = ivtv_pvr350_outputs, ++ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_MSP34XX, ++ .hw_audio_ctrl = IVTV_HW_MSP34XX, ++ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | ++ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER | ++ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, ++ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, ++ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, ++ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, ++ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* PVR-350 V1 boards have a different audio tuner input and use a ++ saa7114 instead of a saa7115. ++ Note that the info below comes from a pre-production model so it may ++ not be correct. Especially the audio behaves strangely (mono only it seems) */ ++static const struct ivtv_card ivtv_card_pvr350_v1 = { ++ .type = IVTV_CARD_PVR_350_V1, ++ .name = "Hauppauge WinTV PVR-350 (V1)", ++ .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, ++ .video_outputs = ivtv_pvr350_outputs, ++ .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), ++ .hw_video = IVTV_HW_SAA7114, ++ .hw_audio = IVTV_HW_MSP34XX, ++ .hw_audio_ctrl = IVTV_HW_MSP34XX, ++ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 | ++ IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, ++ { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, ++ { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, ++ { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO }, ++ { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, ++ { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Hauppauge PVR-150/PVR-500 cards */ ++ ++static const struct ivtv_card ivtv_card_pvr150 = { ++ .type = IVTV_CARD_PVR_150, ++ .name = "Hauppauge WinTV PVR-150", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_muxer = IVTV_HW_WM8775, ++ .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | ++ IVTV_HW_TVEEPROM | IVTV_HW_TUNER | ++ IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT | ++ IVTV_HW_Z8F0811_IR_HAUP, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 }, ++ { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 }, ++ { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, ++ CX25840_AUDIO8, WM8775_AIN2 }, ++ { IVTV_CARD_INPUT_LINE_IN1, ++ CX25840_AUDIO_SERIAL, WM8775_AIN2 }, ++ { IVTV_CARD_INPUT_LINE_IN2, ++ CX25840_AUDIO_SERIAL, WM8775_AIN3 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, ++ CX25840_AUDIO_SERIAL, WM8775_AIN4 }, ++ /* apparently needed for the IR blaster */ ++ .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerMedia M179 cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_m179[] = { ++ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf }, ++ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_m179 = { ++ .type = IVTV_CARD_M179, ++ .name = "AVerMedia M179", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7114, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 }, ++ .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 }, ++ .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 }, ++ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, ++ .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 }, ++ .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000, ++ .f44100 = 0x0008, .f48000 = 0x0010 }, ++ .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 }, ++ .tuners = { ++ /* As far as we know all M179 cards use this tuner */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC }, ++ }, ++ .pci_list = ivtv_pci_m179, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_mpg600 = { ++ .type = IVTV_CARD_MPG600, ++ .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 }, ++ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, ++ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, ++ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, ++ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, ++ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, ++ .tuners = { ++ /* The PAL tuner is confirmed */ ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_mpg600, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = { ++ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 }, ++ { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_mpg160 = { ++ .type = IVTV_CARD_MPG160, ++ .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7114, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0x7080, .initial_value = 0x400c }, ++ .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, ++ .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, ++ .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, ++ .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, ++ .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_mpg160, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan PG600/Diamond PVR-550 cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_pg600[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_pg600 = { ++ .type = IVTV_CARD_PG600, ++ .name = "Yuan PG600, Diamond PVR-550", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, ++ }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FQ1216ME }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_pg600, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Adaptec VideOh! AVC-2410 card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_avc2410 = { ++ .type = IVTV_CARD_AVC2410, ++ .name = "Adaptec VideOh! AVC-2410", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_MSP34XX, ++ .hw_audio_ctrl = IVTV_HW_MSP34XX, ++ .hw_muxer = IVTV_HW_CS53L32A, ++ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A | ++ IVTV_HW_SAA7115 | IVTV_HW_TUNER | ++ IVTV_HW_I2C_IR_RX_ADAPTEC, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, ++ MSP_TUNER, CS53L32A_IN0 }, ++ { IVTV_CARD_INPUT_LINE_IN1, ++ MSP_SCART1, CS53L32A_IN2 }, ++ }, ++ /* This card has no eeprom and in fact the Windows driver relies ++ on the country/region setting of the user to decide which tuner ++ is available. */ ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP, ++ .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_avc2410, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Adaptec VideOh! AVC-2010 card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_avc2010 = { ++ .type = IVTV_CARD_AVC2010, ++ .name = "Adaptec VideOh! AVC-2010", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_CS53L32A, ++ .hw_audio_ctrl = IVTV_HW_CS53L32A, ++ .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 }, ++ }, ++ /* Does not have a tuner */ ++ .pci_list = ivtv_pci_avc2010, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Nagase Transgear 5000TV card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_tg5000tv = { ++ .type = IVTV_CARD_TG5000TV, ++ .name = "Nagase Transgear 5000TV", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | ++ IVTV_HW_GPIO, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER | ++ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gr_config = UPD64031A_VERTICAL_EXTERNAL, ++ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, ++ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, ++ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, ++ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, ++ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, ++ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, ++ .composite = 0x0010, .svideo = 0x0020 }, ++ .tuners = { ++ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_tg5000tv, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AOpen VA2000MAX-SNT6 card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_va2000[] = { ++ { PCI_DEVICE_ID_IVTV16, 0, 0xff5f }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_va2000 = { ++ .type = IVTV_CARD_VA2000MAX_SNT6, ++ .name = "AOpen VA2000MAX-SNT6", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X, ++ .hw_audio = IVTV_HW_MSP34XX, ++ .hw_audio_ctrl = IVTV_HW_MSP34XX, ++ .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | ++ IVTV_HW_UPD6408X | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, ++ }, ++ .tuners = { ++ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_va2000, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_cx23416gyc = { ++ .type = IVTV_CARD_CX23416GYC, ++ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | ++ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .hw_audio = IVTV_HW_SAA717X, ++ .hw_audio_ctrl = IVTV_HW_SAA717X, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | ++ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 | ++ IVTV_SAA717X_TUNER_FLAG }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, ++ }, ++ .gr_config = UPD64031A_VERTICAL_EXTERNAL, ++ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, ++ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, ++ .composite = 0x0020, .svideo = 0x0020 }, ++ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, ++ .f44100 = 0x4000, .f48000 = 0x8000 }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ }, ++ .pci_list = ivtv_pci_cx23416gyc, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { ++ .type = IVTV_CARD_CX23416GYC_NOGR, ++ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X, ++ .hw_audio = IVTV_HW_SAA717X, ++ .hw_audio_ctrl = IVTV_HW_SAA717X, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | ++ IVTV_HW_UPD6408X, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | ++ IVTV_SAA717X_TUNER_FLAG }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, ++ }, ++ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, ++ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, ++ .composite = 0x0020, .svideo = 0x0020 }, ++ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, ++ .f44100 = 0x4000, .f48000 = 0x8000 }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { ++ .type = IVTV_CARD_CX23416GYC_NOGRYCS, ++ .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO, ++ .hw_audio = IVTV_HW_SAA717X, ++ .hw_audio_ctrl = IVTV_HW_SAA717X, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | ++ IVTV_SAA717X_TUNER_FLAG }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, ++ }, ++ .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, ++ .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, ++ .composite = 0x0020, .svideo = 0x0020 }, ++ .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, ++ .f44100 = 0x4000, .f48000 = 0x8000 }, ++ .tuners = { ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ }, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */ ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */ ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_gv_mvprx = { ++ .type = IVTV_CARD_GV_MVPRX, ++ .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_WM8739, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_VP27SMPX | ++ IVTV_HW_TUNER | IVTV_HW_WM8739 | ++ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, ++ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, ++ .tuners = { ++ /* This card has the Panasonic VP27 tuner */ ++ { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, ++ }, ++ .pci_list = ivtv_pci_gv_mvprx, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* I/O Data GV-MVP/RX2E card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 }, ++ {0, 0, 0} ++}; ++ ++static const struct ivtv_card ivtv_card_gv_mvprx2e = { ++ .type = IVTV_CARD_GV_MVPRX2E, ++ .name = "I/O Data GV-MVP/RX2E", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_WM8739, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | ++ IVTV_HW_VP27SMPX | IVTV_HW_WM8739, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, ++ .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, ++ .tuners = { ++ /* This card has the Panasonic VP27 tuner */ ++ { .std = V4L2_STD_MN, .tuner = TUNER_PANASONIC_VP27 }, ++ }, ++ .pci_list = ivtv_pci_gv_mvprx2e, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* GotVIEW PCI DVD card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_gotview_pci_dvd = { ++ .type = IVTV_CARD_GOTVIEW_PCI_DVD, ++ .name = "GotView PCI DVD", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA717X, ++ .hw_audio = IVTV_HW_SAA717X, ++ .hw_audio_ctrl = IVTV_HW_SAA717X, ++ .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */ ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */ ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */ ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 }, ++ }, ++ .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, ++ .tuners = { ++ /* This card has a Philips FQ1216ME MK3 tuner */ ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ }, ++ .pci_list = ivtv_pci_gotview_pci_dvd, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* GotVIEW PCI DVD2 Deluxe card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { ++ .type = IVTV_CARD_GOTVIEW_PCI_DVD2, ++ .name = "GotView PCI DVD2 Deluxe", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_muxer = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, ++ .gpio_init = { .direction = 0x0800, .initial_value = 0 }, ++ .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, ++ .tuners = { ++ /* This card has a Philips FQ1216ME MK5 tuner */ ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, ++ }, ++ .pci_list = ivtv_pci_gotview_pci_dvd2, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan MPC622 miniPCI card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_yuan_mpc622 = { ++ .type = IVTV_CARD_YUAN_MPC622, ++ .name = "Yuan MPC622", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, ++ }, ++ .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 }, ++ .tuners = { ++ /* This card has the TDA8290/TDA8275 tuner chips */ ++ { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 }, ++ }, ++ .pci_list = ivtv_pci_yuan_mpc622, ++ .i2c = &ivtv_i2c_tda8290, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* DIGITAL COWBOY DCT-MTVP1 card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_dctmvtvp1 = { ++ .type = IVTV_CARD_DCTMTVP1, ++ .name = "Digital Cowboy DCT-MTVP1", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | ++ IVTV_HW_GPIO, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | ++ IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, ++ .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, ++ .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, ++ .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, ++ .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, ++ .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, ++ .composite = 0x0010, .svideo = 0x0020}, ++ .tuners = { ++ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FQ1286 }, ++ }, ++ .pci_list = ivtv_pci_dctmvtvp1, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Yuan PG600-2/GotView PCI DVD Lite cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_pg600v2 = { ++ .type = IVTV_CARD_PG600V2, ++ .name = "Yuan PG600-2, GotView PCI DVD Lite", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ /* XC2028 support apparently works for the Yuan, it's still ++ uncertain whether it also works with the GotView. */ ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ .xceive_pin = 12, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .pci_list = ivtv_pci_pg600v2, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Club3D ZAP-TV1x01 cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_club3d[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_club3d = { ++ .type = IVTV_CARD_CLUB3D, ++ .name = "Club3D ZAP-TV1x01", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ .xceive_pin = 12, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .pci_list = ivtv_pci_club3d, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerTV MCE 116 Plus (M116) card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_avertv_mce116[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc439 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_avertv_mce116 = { ++ .type = IVTV_CARD_AVERTV_MCE116, ++ .name = "AVerTV MCE 116 Plus", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 | ++ IVTV_HW_I2C_IR_RX_AVER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ /* enable line-in */ ++ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, ++ .xceive_pin = 10, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .pci_list = ivtv_pci_avertv_mce116, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */ ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */ ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_aver_pvr150 = { ++ .type = IVTV_CARD_AVER_PVR150PLUS, ++ .name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_muxer = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | ++ IVTV_HW_WM8739 | IVTV_HW_GPIO, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, ++ /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ ++ .gpio_init = { .direction = 0xc000, .initial_value = 0 }, ++ .gpio_audio_input = { .mask = 0xc000, ++ .tuner = 0x0000, ++ .linein = 0x4000, ++ .radio = 0x8000 }, ++ .tuners = { ++ /* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */ ++ { .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 }, ++ }, ++ .pci_list = ivtv_pci_aver_pvr150, ++ /* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */ ++ .i2c = &ivtv_i2c_radio, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */ ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */ ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_aver_ultra1500mce = { ++ .type = IVTV_CARD_AVER_ULTRA1500MCE, ++ .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", ++ .comment = "For non-NTSC tuners, use the pal= or secam= module options", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_muxer = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | ++ IVTV_HW_WM8739 | IVTV_HW_GPIO, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, ++ /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ ++ .gpio_init = { .direction = 0xc000, .initial_value = 0 }, ++ .gpio_audio_input = { .mask = 0xc000, ++ .tuner = 0x0000, ++ .linein = 0x4000, ++ .radio = 0x8000 }, ++ .tuners = { ++ /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ ++ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 }, ++ }, ++ .pci_list = ivtv_pci_aver_ultra1500mce, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerMedia EZMaker PCI Deluxe card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_aver_ezmaker[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc03f }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_aver_ezmaker = { ++ .type = IVTV_CARD_AVER_EZMAKER, ++ .name = "AVerMedia EZMaker PCI Deluxe", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_WM8739, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 0 }, ++ }, ++ .gpio_init = { .direction = 0x4000, .initial_value = 0x4000 }, ++ /* Does not have a tuner */ ++ .pci_list = ivtv_pci_aver_ezmaker, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* ASUS Falcon2 */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_asus_falcon2[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b66 }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x462e }, ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ASUSTEK, 0x4b2e }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_asus_falcon2 = { ++ .type = IVTV_CARD_ASUS_FALCON2, ++ .name = "ASUS Falcon2", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_muxer = IVTV_HW_M52790, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_M52790 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 2, CX25840_COMPOSITE2 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, M52790_IN_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, ++ M52790_IN_V2 | M52790_SW1_YCMIX | M52790_SW2_YCMIX }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, M52790_IN_V2 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, M52790_IN_TUNER }, ++ .tuners = { ++ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, ++ }, ++ .pci_list = ivtv_pci_asus_falcon2, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* AVerMedia M104 miniPCI card */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_aver_m104[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc136 }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_aver_m104 = { ++ .type = IVTV_CARD_AVER_M104, ++ .name = "AVerMedia M104", ++ .comment = "Not yet supported!\n", ++ .v4l2_capabilities = 0, /*IVTV_CAP_ENCODER,*/ ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_SVIDEO1, 0, CX25840_SVIDEO3 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 0, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, ++ }, ++ .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, ++ /* enable line-in + reset tuner */ ++ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, ++ .xceive_pin = 10, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .pci_list = ivtv_pci_aver_m104, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* Buffalo PC-MV5L/PCI cards */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_buffalo = { ++ .type = IVTV_CARD_BUFFALO_MV5L, ++ .name = "Buffalo PC-MV5L/PCI", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_CX25840, ++ .hw_audio = IVTV_HW_CX25840, ++ .hw_audio_ctrl = IVTV_HW_CX25840, ++ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, ++ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, ++ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, ++ }, ++ .xceive_pin = 12, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, ++ }, ++ .pci_list = ivtv_pci_buffalo, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++/* ------------------------------------------------------------------------- */ ++/* Sony Kikyou */ ++ ++static const struct ivtv_card_pci_info ivtv_pci_kikyou[] = { ++ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_SONY, 0x813d }, ++ { 0, 0, 0 } ++}; ++ ++static const struct ivtv_card ivtv_card_kikyou = { ++ .type = IVTV_CARD_KIKYOU, ++ .name = "Sony VAIO Giga Pocket (ENX Kikyou)", ++ .v4l2_capabilities = IVTV_CAP_ENCODER, ++ .hw_video = IVTV_HW_SAA7115, ++ .hw_audio = IVTV_HW_GPIO, ++ .hw_audio_ctrl = IVTV_HW_GPIO, ++ .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, ++ .video_inputs = { ++ { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, ++ { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE1 }, ++ { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, ++ }, ++ .audio_inputs = { ++ { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, ++ { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, ++ { IVTV_CARD_INPUT_LINE_IN2, IVTV_GPIO_LINE_IN }, ++ }, ++ .gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 }, ++ .gpio_audio_input = { .mask = 0x0060, ++ .tuner = 0x0020, ++ .linein = 0x0000, ++ .radio = 0x0060 }, ++ .gpio_audio_mute = { .mask = 0x0000, ++ .mute = 0x0000 }, /* 0x200? Disable for now. */ ++ .gpio_audio_mode = { .mask = 0x0080, ++ .mono = 0x0000, ++ .stereo = 0x0000, /* SAP */ ++ .lang1 = 0x0080, ++ .lang2 = 0x0000, ++ .both = 0x0080 }, ++ .tuners = { ++ { .std = V4L2_STD_ALL, .tuner = TUNER_SONY_BTF_PXN01Z }, ++ }, ++ .pci_list = ivtv_pci_kikyou, ++ .i2c = &ivtv_i2c_std, ++}; ++ ++ ++static const struct ivtv_card *ivtv_card_list[] = { ++ &ivtv_card_pvr250, ++ &ivtv_card_pvr350, ++ &ivtv_card_pvr150, ++ &ivtv_card_m179, ++ &ivtv_card_mpg600, ++ &ivtv_card_mpg160, ++ &ivtv_card_pg600, ++ &ivtv_card_avc2410, ++ &ivtv_card_avc2010, ++ &ivtv_card_tg5000tv, ++ &ivtv_card_va2000, ++ &ivtv_card_cx23416gyc, ++ &ivtv_card_gv_mvprx, ++ &ivtv_card_gv_mvprx2e, ++ &ivtv_card_gotview_pci_dvd, ++ &ivtv_card_gotview_pci_dvd2, ++ &ivtv_card_yuan_mpc622, ++ &ivtv_card_dctmvtvp1, ++ &ivtv_card_pg600v2, ++ &ivtv_card_club3d, ++ &ivtv_card_avertv_mce116, ++ &ivtv_card_asus_falcon2, ++ &ivtv_card_aver_pvr150, ++ &ivtv_card_aver_ezmaker, ++ &ivtv_card_aver_m104, ++ &ivtv_card_buffalo, ++ &ivtv_card_aver_ultra1500mce, ++ &ivtv_card_kikyou, ++ ++ /* Variations of standard cards but with the same PCI IDs. ++ These cards must come last in this list. */ ++ &ivtv_card_pvr350_v1, ++ &ivtv_card_cx23416gyc_nogr, ++ &ivtv_card_cx23416gyc_nogrycs, ++}; ++ ++const struct ivtv_card *ivtv_get_card(u16 index) ++{ ++ if (index >= ARRAY_SIZE(ivtv_card_list)) ++ return NULL; ++ return ivtv_card_list[index]; ++} ++ ++int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input) ++{ ++ const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index; ++ static const char * const input_strs[] = { ++ "Tuner 1", ++ "S-Video 1", ++ "S-Video 2", ++ "Composite 1", ++ "Composite 2", ++ "Composite 3" ++ }; ++ ++ if (index >= itv->nof_inputs) ++ return -EINVAL; ++ input->index = index; ++ strlcpy(input->name, input_strs[card_input->video_type - 1], ++ sizeof(input->name)); ++ input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ? ++ V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); ++ input->audioset = (1 << itv->nof_audio_inputs) - 1; ++ input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? ++ itv->tuner_std : V4L2_STD_ALL; ++ return 0; ++} ++ ++int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output) ++{ ++ const struct ivtv_card_output *card_output = itv->card->video_outputs + index; ++ ++ if (index >= itv->card->nof_outputs) ++ return -EINVAL; ++ output->index = index; ++ strlcpy(output->name, card_output->name, sizeof(output->name)); ++ output->type = V4L2_OUTPUT_TYPE_ANALOG; ++ output->audioset = 1; ++ output->std = V4L2_STD_ALL; ++ return 0; ++} ++ ++int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio) ++{ ++ const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index; ++ static const char * const input_strs[] = { ++ "Tuner 1", ++ "Line In 1", ++ "Line In 2" ++ }; ++ ++ memset(audio, 0, sizeof(*audio)); ++ if (index >= itv->nof_audio_inputs) ++ return -EINVAL; ++ strlcpy(audio->name, input_strs[aud_input->audio_type - 1], ++ sizeof(audio->name)); ++ audio->index = index; ++ audio->capability = V4L2_AUDCAP_STEREO; ++ return 0; ++} ++ ++int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output) ++{ ++ memset(aud_output, 0, sizeof(*aud_output)); ++ if (itv->card->video_outputs == NULL || index != 0) ++ return -EINVAL; ++ strlcpy(aud_output->name, "A/V Audio Out", sizeof(aud_output->name)); ++ return 0; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-cards.h b/drivers/media/pci/ivtv/ivtv-cards.h +new file mode 100644 +index 0000000..e6f5c02 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-cards.h +@@ -0,0 +1,309 @@ ++/* ++ Functions to query card hardware ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_CARDS_H ++#define IVTV_CARDS_H ++ ++/* Supported cards */ ++#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ ++#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */ ++#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two ++ PVR150s on one PCI board) */ ++#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */ ++#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */ ++#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160 ++ cx23415 based, but does not have tv-out */ ++#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */ ++#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */ ++#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */ ++#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */ ++#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */ ++#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ ++#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */ ++#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */ ++#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */ ++#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */ ++#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */ ++#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */ ++#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite */ ++#define IVTV_CARD_CLUB3D 19 /* Club3D ZAP-TV1x01 */ ++#define IVTV_CARD_AVERTV_MCE116 20 /* AVerTV MCE 116 Plus */ ++#define IVTV_CARD_ASUS_FALCON2 21 /* ASUS Falcon2 */ ++#define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */ ++#define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */ ++#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ ++#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ ++#define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */ ++#define IVTV_CARD_KIKYOU 27 /* Sony VAIO Giga Pocket (ENX Kikyou) */ ++#define IVTV_CARD_LAST 27 ++ ++/* Variants of existing cards but with the same PCI IDs. The driver ++ detects these based on other device information. ++ These cards must always come last. ++ New cards must be inserted above, and the indices of the cards below ++ must be adjusted accordingly. */ ++ ++/* PVR-350 V1 (uses saa7114) */ ++#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1) ++/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ ++#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2) ++#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3) ++ ++/* system vendor and device IDs */ ++#define PCI_VENDOR_ID_ICOMP 0x4444 ++#define PCI_DEVICE_ID_IVTV15 0x0803 ++#define PCI_DEVICE_ID_IVTV16 0x0016 ++ ++/* subsystem vendor ID */ ++#define IVTV_PCI_ID_HAUPPAUGE 0x0070 ++#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270 ++#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070 ++#define IVTV_PCI_ID_ADAPTEC 0x9005 ++#define IVTV_PCI_ID_ASUSTEK 0x1043 ++#define IVTV_PCI_ID_AVERMEDIA 0x1461 ++#define IVTV_PCI_ID_YUAN1 0x12ab ++#define IVTV_PCI_ID_YUAN2 0xff01 ++#define IVTV_PCI_ID_YUAN3 0xffab ++#define IVTV_PCI_ID_YUAN4 0xfbab ++#define IVTV_PCI_ID_DIAMONDMM 0xff92 ++#define IVTV_PCI_ID_IODATA 0x10fc ++#define IVTV_PCI_ID_MELCO 0x1154 ++#define IVTV_PCI_ID_GOTVIEW1 0xffac ++#define IVTV_PCI_ID_GOTVIEW2 0xffad ++#define IVTV_PCI_ID_SONY 0x104d ++ ++/* hardware flags, no gaps allowed */ ++#define IVTV_HW_CX25840 (1 << 0) ++#define IVTV_HW_SAA7115 (1 << 1) ++#define IVTV_HW_SAA7127 (1 << 2) ++#define IVTV_HW_MSP34XX (1 << 3) ++#define IVTV_HW_TUNER (1 << 4) ++#define IVTV_HW_WM8775 (1 << 5) ++#define IVTV_HW_CS53L32A (1 << 6) ++#define IVTV_HW_TVEEPROM (1 << 7) ++#define IVTV_HW_SAA7114 (1 << 8) ++#define IVTV_HW_UPD64031A (1 << 9) ++#define IVTV_HW_UPD6408X (1 << 10) ++#define IVTV_HW_SAA717X (1 << 11) ++#define IVTV_HW_WM8739 (1 << 12) ++#define IVTV_HW_VP27SMPX (1 << 13) ++#define IVTV_HW_M52790 (1 << 14) ++#define IVTV_HW_GPIO (1 << 15) ++#define IVTV_HW_I2C_IR_RX_AVER (1 << 16) ++#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */ ++#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18) ++#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19) ++#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20) ++#define IVTV_HW_I2C_IR_RX_ADAPTEC (1 << 21) ++ ++#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \ ++ IVTV_HW_Z8F0811_IR_TX_HAUP) ++ ++#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) ++ ++#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \ ++ IVTV_HW_I2C_IR_RX_HAUP_EXT | \ ++ IVTV_HW_I2C_IR_RX_HAUP_INT | \ ++ IVTV_HW_Z8F0811_IR_RX_HAUP | \ ++ IVTV_HW_I2C_IR_RX_ADAPTEC) ++ ++#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP) ++ ++#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY) ++ ++/* video inputs */ ++#define IVTV_CARD_INPUT_VID_TUNER 1 ++#define IVTV_CARD_INPUT_SVIDEO1 2 ++#define IVTV_CARD_INPUT_SVIDEO2 3 ++#define IVTV_CARD_INPUT_COMPOSITE1 4 ++#define IVTV_CARD_INPUT_COMPOSITE2 5 ++#define IVTV_CARD_INPUT_COMPOSITE3 6 ++ ++/* audio inputs */ ++#define IVTV_CARD_INPUT_AUD_TUNER 1 ++#define IVTV_CARD_INPUT_LINE_IN1 2 ++#define IVTV_CARD_INPUT_LINE_IN2 3 ++ ++#define IVTV_CARD_MAX_VIDEO_INPUTS 6 ++#define IVTV_CARD_MAX_AUDIO_INPUTS 3 ++#define IVTV_CARD_MAX_TUNERS 3 ++ ++/* SAA71XX HW inputs */ ++#define IVTV_SAA71XX_COMPOSITE0 0 ++#define IVTV_SAA71XX_COMPOSITE1 1 ++#define IVTV_SAA71XX_COMPOSITE2 2 ++#define IVTV_SAA71XX_COMPOSITE3 3 ++#define IVTV_SAA71XX_COMPOSITE4 4 ++#define IVTV_SAA71XX_COMPOSITE5 5 ++#define IVTV_SAA71XX_SVIDEO0 6 ++#define IVTV_SAA71XX_SVIDEO1 7 ++#define IVTV_SAA71XX_SVIDEO2 8 ++#define IVTV_SAA71XX_SVIDEO3 9 ++ ++/* SAA717X needs to mark the tuner input by ORing with this flag */ ++#define IVTV_SAA717X_TUNER_FLAG 0x80 ++ ++/* Dummy HW input */ ++#define IVTV_DUMMY_AUDIO 0 ++ ++/* GPIO HW inputs */ ++#define IVTV_GPIO_TUNER 0 ++#define IVTV_GPIO_LINE_IN 1 ++ ++/* SAA717X HW inputs */ ++#define IVTV_SAA717X_IN0 0 ++#define IVTV_SAA717X_IN1 1 ++#define IVTV_SAA717X_IN2 2 ++ ++/* V4L2 capability aliases */ ++#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ ++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ ++ V4L2_CAP_SLICED_VBI_CAPTURE) ++#define IVTV_CAP_DECODER (V4L2_CAP_VIDEO_OUTPUT | \ ++ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY) ++ ++struct ivtv_card_video_input { ++ u8 video_type; /* video input type */ ++ u8 audio_index; /* index in ivtv_card_audio_input array */ ++ u16 video_input; /* hardware video input */ ++}; ++ ++struct ivtv_card_audio_input { ++ u8 audio_type; /* audio input type */ ++ u32 audio_input; /* hardware audio input */ ++ u16 muxer_input; /* hardware muxer input for boards with a ++ multiplexer chip */ ++}; ++ ++struct ivtv_card_output { ++ u8 name[32]; ++ u16 video_output; /* hardware video output */ ++}; ++ ++struct ivtv_card_pci_info { ++ u16 device; ++ u16 subsystem_vendor; ++ u16 subsystem_device; ++}; ++ ++/* GPIO definitions */ ++ ++/* The mask is the set of bits used by the operation */ ++ ++struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */ ++ u16 direction; /* DIR setting. Leave to 0 if no init is needed */ ++ u16 initial_value; ++}; ++ ++struct ivtv_gpio_video_input { /* select tuner/line in input */ ++ u16 mask; /* leave to 0 if not supported */ ++ u16 tuner; ++ u16 composite; ++ u16 svideo; ++}; ++ ++struct ivtv_gpio_audio_input { /* select tuner/line in input */ ++ u16 mask; /* leave to 0 if not supported */ ++ u16 tuner; ++ u16 linein; ++ u16 radio; ++}; ++ ++struct ivtv_gpio_audio_mute { ++ u16 mask; /* leave to 0 if not supported */ ++ u16 mute; /* set this value to mute, 0 to unmute */ ++}; ++ ++struct ivtv_gpio_audio_mode { ++ u16 mask; /* leave to 0 if not supported */ ++ u16 mono; /* set audio to mono */ ++ u16 stereo; /* set audio to stereo */ ++ u16 lang1; /* set audio to the first language */ ++ u16 lang2; /* set audio to the second language */ ++ u16 both; /* both languages are output */ ++}; ++ ++struct ivtv_gpio_audio_freq { ++ u16 mask; /* leave to 0 if not supported */ ++ u16 f32000; ++ u16 f44100; ++ u16 f48000; ++}; ++ ++struct ivtv_gpio_audio_detect { ++ u16 mask; /* leave to 0 if not supported */ ++ u16 stereo; /* if the input matches this value then ++ stereo is detected */ ++}; ++ ++struct ivtv_card_tuner { ++ v4l2_std_id std; /* standard for which the tuner is suitable */ ++ int tuner; /* tuner ID (from tuner.h) */ ++}; ++ ++struct ivtv_card_tuner_i2c { ++ unsigned short radio[2];/* radio tuner i2c address to probe */ ++ unsigned short demod[2];/* demodulator i2c address to probe */ ++ unsigned short tv[4]; /* tv tuner i2c addresses to probe */ ++}; ++ ++/* for card information/parameters */ ++struct ivtv_card { ++ int type; ++ char *name; ++ char *comment; ++ u32 v4l2_capabilities; ++ u32 hw_video; /* hardware used to process video */ ++ u32 hw_audio; /* hardware used to process audio */ ++ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */ ++ u32 hw_muxer; /* hardware used to multiplex audio input */ ++ u32 hw_all; /* all hardware used by the board */ ++ struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS]; ++ struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS]; ++ struct ivtv_card_audio_input radio_input; ++ int nof_outputs; ++ const struct ivtv_card_output *video_outputs; ++ u8 gr_config; /* config byte for the ghost reduction device */ ++ u8 xceive_pin; /* XCeive tuner GPIO reset pin */ ++ ++ /* GPIO card-specific settings */ ++ struct ivtv_gpio_init gpio_init; ++ struct ivtv_gpio_video_input gpio_video_input; ++ struct ivtv_gpio_audio_input gpio_audio_input; ++ struct ivtv_gpio_audio_mute gpio_audio_mute; ++ struct ivtv_gpio_audio_mode gpio_audio_mode; ++ struct ivtv_gpio_audio_freq gpio_audio_freq; ++ struct ivtv_gpio_audio_detect gpio_audio_detect; ++ ++ struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS]; ++ struct ivtv_card_tuner_i2c *i2c; ++ ++ /* list of device and subsystem vendor/devices that ++ correspond to this card type. */ ++ const struct ivtv_card_pci_info *pci_list; ++}; ++ ++int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input); ++int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output); ++int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input); ++int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output); ++const struct ivtv_card *ivtv_get_card(u16 index); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c +new file mode 100644 +index 0000000..c604246 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-controls.c +@@ -0,0 +1,163 @@ ++/* ++ ioctl control functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-controls.h" ++#include "ivtv-mailbox.h" ++ ++static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) ++{ ++ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); ++ ++ /* First try to allocate sliced VBI buffers if needed. */ ++ if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { ++ int i; ++ ++ for (i = 0; i < IVTV_VBI_FRAMES; i++) { ++ /* Yuck, hardcoded. Needs to be a define */ ++ itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); ++ if (itv->vbi.sliced_mpeg_data[i] == NULL) { ++ while (--i >= 0) { ++ kfree(itv->vbi.sliced_mpeg_data[i]); ++ itv->vbi.sliced_mpeg_data[i] = NULL; ++ } ++ return -ENOMEM; ++ } ++ } ++ } ++ ++ itv->vbi.insert_mpeg = fmt; ++ ++ if (itv->vbi.insert_mpeg == 0) { ++ return 0; ++ } ++ /* Need sliced data for mpeg insertion */ ++ if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) { ++ if (itv->is_60hz) ++ itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; ++ else ++ itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; ++ ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz); ++ } ++ return 0; ++} ++ ++static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) ++{ ++ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); ++ int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; ++ struct v4l2_mbus_framefmt fmt; ++ ++ /* fix videodecoder resolution */ ++ fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); ++ fmt.height = cxhdl->height; ++ fmt.code = V4L2_MBUS_FMT_FIXED; ++ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt); ++ return 0; ++} ++ ++static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) ++{ ++ static const u32 freqs[3] = { 44100, 48000, 32000 }; ++ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); ++ ++ /* The audio clock of the digitizer must match the codec sample ++ rate otherwise you get some very strange effects. */ ++ if (idx < ARRAY_SIZE(freqs)) ++ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); ++ return 0; ++} ++ ++static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) ++{ ++ struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl); ++ ++ itv->dualwatch_stereo_mode = val; ++ return 0; ++} ++ ++struct cx2341x_handler_ops ivtv_cxhdl_ops = { ++ .s_audio_mode = ivtv_s_audio_mode, ++ .s_audio_sampling_freq = ivtv_s_audio_sampling_freq, ++ .s_video_encoding = ivtv_s_video_encoding, ++ .s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt, ++}; ++ ++int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ ++ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { ++ *pts = (s64)((u64)itv->last_dec_timing[2] << 32) | ++ (u64)itv->last_dec_timing[1]; ++ *frame = itv->last_dec_timing[0]; ++ return 0; ++ } ++ *pts = 0; ++ *frame = 0; ++ if (atomic_read(&itv->decoding)) { ++ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { ++ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); ++ return -EIO; ++ } ++ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); ++ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); ++ *pts = (s64)((u64) data[2] << 32) | (u64) data[1]; ++ *frame = data[0]; ++ /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ ++ } ++ return 0; ++} ++ ++static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); ++ ++ switch (ctrl->id) { ++ /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME ++ control cluster */ ++ case V4L2_CID_MPEG_VIDEO_DEC_PTS: ++ return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64, ++ &itv->ctrl_frame->val64); ++ } ++ return 0; ++} ++ ++static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl); ++ ++ switch (ctrl->id) { ++ /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK ++ control cluster */ ++ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: ++ itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1; ++ itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1; ++ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); ++ break; ++ } ++ return 0; ++} ++ ++const struct v4l2_ctrl_ops ivtv_hdl_out_ops = { ++ .s_ctrl = ivtv_s_ctrl, ++ .g_volatile_ctrl = ivtv_g_volatile_ctrl, ++}; +diff --git a/drivers/media/pci/ivtv/ivtv-controls.h b/drivers/media/pci/ivtv/ivtv-controls.h +new file mode 100644 +index 0000000..3999e63 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-controls.h +@@ -0,0 +1,28 @@ ++/* ++ ioctl control functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_CONTROLS_H ++#define IVTV_CONTROLS_H ++ ++extern struct cx2341x_handler_ops ivtv_cxhdl_ops; ++extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops; ++int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c +new file mode 100644 +index 0000000..5d0a5df +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-driver.c +@@ -0,0 +1,1536 @@ ++/* ++ ivtv driver initialization and card probing ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* Main Driver file for the ivtv project: ++ * Driver for the Conexant CX23415/CX23416 chip. ++ * Author: Kevin Thayer (nufan_wfk at yahoo.com) ++ * License: GPL ++ * http://www.ivtvdriver.org ++ * ++ * ----- ++ * MPG600/MPG160 support by T.Adachi ++ * and Takeru KOMORIYA ++ * ++ * AVerMedia M179 GPIO info by Chris Pinkham ++ * using information provided by Jiun-Kuei Jung @ AVerMedia. ++ * ++ * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta ++ * using information from T.Adachi,Takeru KOMORIYA and others :-) ++ * ++ * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX ++ * version by T.Adachi. Special thanks Mr.Suzuki ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-version.h" ++#include "ivtv-fileops.h" ++#include "ivtv-i2c.h" ++#include "ivtv-firmware.h" ++#include "ivtv-queue.h" ++#include "ivtv-udma.h" ++#include "ivtv-irq.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-streams.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-cards.h" ++#include "ivtv-vbi.h" ++#include "ivtv-routing.h" ++#include "ivtv-controls.h" ++#include "ivtv-gpio.h" ++#include ++#include ++#include ++#include ++#include "tuner-xc2028.h" ++ ++/* If you have already X v4l cards, then set this to X. This way ++ the device numbers stay matched. Example: you have a WinTV card ++ without radio and a PVR-350 with. Normally this would give a ++ video1 device together with a radio0 device for the PVR. By ++ setting this to 1 you ensure that radio0 is now also radio1. */ ++int ivtv_first_minor; ++ ++/* Callback for registering extensions */ ++int (*ivtv_ext_init)(struct ivtv *); ++EXPORT_SYMBOL(ivtv_ext_init); ++ ++/* add your revision and whatnot here */ ++static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { ++ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16, ++ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, ++ {0,} ++}; ++ ++MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); ++ ++/* ivtv instance counter */ ++static atomic_t ivtv_instance = ATOMIC_INIT(0); ++ ++/* Parameter declarations */ ++static int cardtype[IVTV_MAX_CARDS]; ++static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1, ++ -1, -1, -1, -1, -1, -1, -1, -1 }; ++ ++static unsigned int cardtype_c = 1; ++static unsigned int tuner_c = 1; ++static int radio_c = 1; ++static unsigned int i2c_clock_period_c = 1; ++static char pal[] = "---"; ++static char secam[] = "--"; ++static char ntsc[] = "-"; ++ ++/* Buffers */ ++ ++/* DMA Buffers, Default size in MB allocated */ ++#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4 ++#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2 ++#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1 ++/* Exception: size in kB for this stream (MB is overkill) */ ++#define IVTV_DEFAULT_ENC_PCM_BUFFERS 320 ++#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1 ++#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1 ++/* Exception: size in kB for this stream (MB is way overkill) */ ++#define IVTV_DEFAULT_DEC_VBI_BUFFERS 64 ++ ++static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS; ++static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS; ++static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS; ++static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS; ++static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; ++static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; ++static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS; ++ ++static int ivtv_yuv_mode; ++static int ivtv_yuv_threshold = -1; ++static int ivtv_pci_latency = 1; ++ ++int ivtv_debug; ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++int ivtv_fw_debug; ++#endif ++ ++static int tunertype = -1; ++static int newi2c = -1; ++ ++module_param_array(tuner, int, &tuner_c, 0644); ++module_param_array(radio, int, &radio_c, 0644); ++module_param_array(cardtype, int, &cardtype_c, 0644); ++module_param_string(pal, pal, sizeof(pal), 0644); ++module_param_string(secam, secam, sizeof(secam), 0644); ++module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); ++module_param_named(debug,ivtv_debug, int, 0644); ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++module_param_named(fw_debug, ivtv_fw_debug, int, 0644); ++#endif ++module_param(ivtv_pci_latency, int, 0644); ++module_param(ivtv_yuv_mode, int, 0644); ++module_param(ivtv_yuv_threshold, int, 0644); ++module_param(ivtv_first_minor, int, 0644); ++ ++module_param(enc_mpg_buffers, int, 0644); ++module_param(enc_yuv_buffers, int, 0644); ++module_param(enc_vbi_buffers, int, 0644); ++module_param(enc_pcm_buffers, int, 0644); ++module_param(dec_mpg_buffers, int, 0644); ++module_param(dec_yuv_buffers, int, 0644); ++module_param(dec_vbi_buffers, int, 0644); ++ ++module_param(tunertype, int, 0644); ++module_param(newi2c, int, 0644); ++module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644); ++ ++MODULE_PARM_DESC(tuner, "Tuner type selection,\n" ++ "\t\t\tsee tuner.h for values"); ++MODULE_PARM_DESC(radio, ++ "Enable or disable the radio. Use only if autodetection\n" ++ "\t\t\tfails. 0 = disable, 1 = enable"); ++MODULE_PARM_DESC(cardtype, ++ "Only use this option if your card is not detected properly.\n" ++ "\t\tSpecify card type:\n" ++ "\t\t\t 1 = WinTV PVR 250\n" ++ "\t\t\t 2 = WinTV PVR 350\n" ++ "\t\t\t 3 = WinTV PVR-150 or PVR-500\n" ++ "\t\t\t 4 = AVerMedia M179\n" ++ "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n" ++ "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n" ++ "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n" ++ "\t\t\t 8 = Adaptec AVC-2410\n" ++ "\t\t\t 9 = Adaptec AVC-2010\n" ++ "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n" ++ "\t\t\t11 = AOpen VA2000MAX-STN6\n" ++ "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n" ++ "\t\t\t13 = I/O Data GV-MVP/RX\n" ++ "\t\t\t14 = I/O Data GV-MVP/RX2E\n" ++ "\t\t\t15 = GOTVIEW PCI DVD\n" ++ "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n" ++ "\t\t\t17 = Yuan MPC622\n" ++ "\t\t\t18 = Digital Cowboy DCT-MTVP1\n" ++ "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite\n" ++ "\t\t\t20 = Club3D ZAP-TV1x01\n" ++ "\t\t\t21 = AverTV MCE 116 Plus\n" ++ "\t\t\t22 = ASUS Falcon2\n" ++ "\t\t\t23 = AverMedia PVR-150 Plus\n" ++ "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" ++ "\t\t\t25 = AverMedia M104 (not yet working)\n" ++ "\t\t\t26 = Buffalo PC-MV5L/PCI\n" ++ "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n" ++ "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n" ++ "\t\t\t 0 = Autodetect (default)\n" ++ "\t\t\t-1 = Ignore this card\n\t\t"); ++MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); ++MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC"); ++MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)"); ++MODULE_PARM_DESC(tunertype, ++ "Specify tuner type:\n" ++ "\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n" ++ "\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n" ++ "\t\t\t-1 = Autodetect (default)\n"); ++MODULE_PARM_DESC(debug, ++ "Debug level (bitmask). Default: 0\n" ++ "\t\t\t 1/0x0001: warning\n" ++ "\t\t\t 2/0x0002: info\n" ++ "\t\t\t 4/0x0004: mailbox\n" ++ "\t\t\t 8/0x0008: ioctl\n" ++ "\t\t\t 16/0x0010: file\n" ++ "\t\t\t 32/0x0020: dma\n" ++ "\t\t\t 64/0x0040: irq\n" ++ "\t\t\t 128/0x0080: decoder\n" ++ "\t\t\t 256/0x0100: yuv\n" ++ "\t\t\t 512/0x0200: i2c\n" ++ "\t\t\t1024/0x0400: high volume\n"); ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++MODULE_PARM_DESC(fw_debug, ++ "Enable code for debugging firmware problems. Default: 0\n"); ++#endif ++MODULE_PARM_DESC(ivtv_pci_latency, ++ "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" ++ "\t\t\tDefault: Yes"); ++MODULE_PARM_DESC(ivtv_yuv_mode, ++ "Specify the yuv playback mode:\n" ++ "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n" ++ "\t\t\tDefault: 0 (interlaced)"); ++MODULE_PARM_DESC(ivtv_yuv_threshold, ++ "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" ++ "\t\t\tDefault: 480"); ++MODULE_PARM_DESC(enc_mpg_buffers, ++ "Encoder MPG Buffers (in MB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); ++MODULE_PARM_DESC(enc_yuv_buffers, ++ "Encoder YUV Buffers (in MB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS)); ++MODULE_PARM_DESC(enc_vbi_buffers, ++ "Encoder VBI Buffers (in MB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS)); ++MODULE_PARM_DESC(enc_pcm_buffers, ++ "Encoder PCM buffers (in kB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS)); ++MODULE_PARM_DESC(dec_mpg_buffers, ++ "Decoder MPG buffers (in MB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS)); ++MODULE_PARM_DESC(dec_yuv_buffers, ++ "Decoder YUV buffers (in MB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS)); ++MODULE_PARM_DESC(dec_vbi_buffers, ++ "Decoder VBI buffers (in kB)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS)); ++MODULE_PARM_DESC(newi2c, ++ "Use new I2C implementation\n" ++ "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" ++ "\t\t\tDefault is autodetect"); ++MODULE_PARM_DESC(i2c_clock_period, ++ "Period of SCL for the I2C bus controlled by the CX23415/6\n" ++ "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n" ++ "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD)); ++ ++MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); ++ ++MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); ++MODULE_DESCRIPTION("CX23415/CX23416 driver"); ++MODULE_SUPPORTED_DEVICE ++ ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n" ++ "\t\t\tYuan MPG series and similar)"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_VERSION(IVTV_VERSION); ++ ++#if defined(CONFIG_MODULES) && defined(MODULE) ++static void request_module_async(struct work_struct *work) ++{ ++ struct ivtv *dev = container_of(work, struct ivtv, request_module_wk); ++ ++ /* Make sure ivtv-alsa module is loaded */ ++ request_module("ivtv-alsa"); ++ ++ /* Initialize ivtv-alsa for this instance of the cx18 device */ ++ if (ivtv_ext_init != NULL) ++ ivtv_ext_init(dev); ++} ++ ++static void request_modules(struct ivtv *dev) ++{ ++ INIT_WORK(&dev->request_module_wk, request_module_async); ++ schedule_work(&dev->request_module_wk); ++} ++ ++static void flush_request_modules(struct ivtv *dev) ++{ ++ flush_work(&dev->request_module_wk); ++} ++#else ++#define request_modules(dev) ++#define flush_request_modules(dev) ++#endif /* CONFIG_MODULES */ ++ ++void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask) ++{ ++ itv->irqmask &= ~mask; ++ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); ++} ++ ++void ivtv_set_irq_mask(struct ivtv *itv, u32 mask) ++{ ++ itv->irqmask |= mask; ++ write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); ++} ++ ++int ivtv_set_output_mode(struct ivtv *itv, int mode) ++{ ++ int old_mode; ++ ++ spin_lock(&itv->lock); ++ old_mode = itv->output_mode; ++ if (old_mode == 0) ++ itv->output_mode = old_mode = mode; ++ spin_unlock(&itv->lock); ++ return old_mode; ++} ++ ++struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv) ++{ ++ switch (itv->output_mode) { ++ case OUT_MPG: ++ return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; ++ case OUT_YUV: ++ return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; ++ default: ++ return NULL; ++ } ++} ++ ++int ivtv_waitq(wait_queue_head_t *waitq) ++{ ++ DEFINE_WAIT(wait); ++ ++ prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); ++ schedule(); ++ finish_wait(waitq, &wait); ++ return signal_pending(current) ? -EINTR : 0; ++} ++ ++/* Generic utility functions */ ++int ivtv_msleep_timeout(unsigned int msecs, int intr) ++{ ++ int timeout = msecs_to_jiffies(msecs); ++ ++ do { ++ set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); ++ timeout = schedule_timeout(timeout); ++ if (intr) { ++ int ret = signal_pending(current); ++ ++ if (ret) ++ return ret; ++ } ++ } while (timeout); ++ return 0; ++} ++ ++/* Release ioremapped memory */ ++static void ivtv_iounmap(struct ivtv *itv) ++{ ++ if (itv == NULL) ++ return; ++ ++ /* Release registers memory */ ++ if (itv->reg_mem != NULL) { ++ IVTV_DEBUG_INFO("releasing reg_mem\n"); ++ iounmap(itv->reg_mem); ++ itv->reg_mem = NULL; ++ } ++ /* Release io memory */ ++ if (itv->has_cx23415 && itv->dec_mem != NULL) { ++ IVTV_DEBUG_INFO("releasing dec_mem\n"); ++ iounmap(itv->dec_mem); ++ } ++ itv->dec_mem = NULL; ++ ++ /* Release io memory */ ++ if (itv->enc_mem != NULL) { ++ IVTV_DEBUG_INFO("releasing enc_mem\n"); ++ iounmap(itv->enc_mem); ++ itv->enc_mem = NULL; ++ } ++} ++ ++/* Hauppauge card? get values from tveeprom */ ++void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) ++{ ++ u8 eedata[256]; ++ ++ itv->i2c_client.addr = 0xA0 >> 1; ++ tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata)); ++ tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata); ++} ++ ++static void ivtv_process_eeprom(struct ivtv *itv) ++{ ++ struct tveeprom tv; ++ int pci_slot = PCI_SLOT(itv->pdev->devfn); ++ ++ ivtv_read_eeprom(itv, &tv); ++ ++ /* Many thanks to Steven Toth from Hauppauge for providing the ++ model numbers */ ++ switch (tv.model) { ++ /* In a few cases the PCI subsystem IDs do not correctly ++ identify the card. A better method is to check the ++ model number from the eeprom instead. */ ++ case 30012 ... 30039: /* Low profile PVR250 */ ++ case 32000 ... 32999: ++ case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */ ++ case 48400 ... 48599: ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_250); ++ break; ++ case 48100 ... 48399: ++ case 48600 ... 48999: ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_350); ++ break; ++ case 23000 ... 23999: /* PVR500 */ ++ case 25000 ... 25999: /* Low profile PVR150 */ ++ case 26000 ... 26999: /* Regular PVR150 */ ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_150); ++ break; ++ case 0: ++ IVTV_ERR("Invalid EEPROM\n"); ++ return; ++ default: ++ IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model); ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_150); ++ break; ++ } ++ ++ switch (tv.model) { ++ /* Old style PVR350 (with an saa7114) uses this input for ++ the tuner. */ ++ case 48254: ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1); ++ break; ++ default: ++ break; ++ } ++ ++ itv->v4l2_cap = itv->card->v4l2_capabilities; ++ itv->card_name = itv->card->name; ++ itv->card_i2c = itv->card->i2c; ++ ++ /* If this is a PVR500 then it should be possible to detect whether it is the ++ first or second unit by looking at the subsystem device ID: is bit 4 is ++ set, then it is the second unit (according to info from Hauppauge). ++ ++ However, while this works for most cards, I have seen a few PVR500 cards ++ where both units have the same subsystem ID. ++ ++ So instead I look at the reported 'PCI slot' (which is the slot on the PVR500 ++ PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise ++ it is the second unit. It is possible that it is a different slot when ivtv is ++ used in Xen, in that case I ignore this card here. The worst that can happen ++ is that the card presents itself with a non-working radio device. ++ ++ This detection is needed since the eeprom reports incorrectly that a radio is ++ present on the second unit. */ ++ if (tv.model / 1000 == 23) { ++ static const struct ivtv_card_tuner_i2c ivtv_i2c_radio = { ++ .radio = { 0x60, I2C_CLIENT_END }, ++ .demod = { 0x43, I2C_CLIENT_END }, ++ .tv = { 0x61, I2C_CLIENT_END }, ++ }; ++ ++ itv->card_name = "WinTV PVR 500"; ++ itv->card_i2c = &ivtv_i2c_radio; ++ if (pci_slot == 8 || pci_slot == 9) { ++ int is_first = (pci_slot & 1) == 0; ++ ++ itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" : ++ "WinTV PVR 500 (unit #2)"; ++ if (!is_first) { ++ IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n"); ++ tv.has_radio = 0; ++ } ++ } ++ } ++ IVTV_INFO("Autodetected %s\n", itv->card_name); ++ ++ switch (tv.tuner_hauppauge_model) { ++ case 85: ++ case 99: ++ case 112: ++ itv->pvr150_workaround = 1; ++ break; ++ default: ++ break; ++ } ++ if (tv.tuner_type == TUNER_ABSENT) ++ IVTV_ERR("tveeprom cannot autodetect tuner!\n"); ++ ++ if (itv->options.tuner == -1) ++ itv->options.tuner = tv.tuner_type; ++ if (itv->options.radio == -1) ++ itv->options.radio = (tv.has_radio != 0); ++ /* only enable newi2c if an IR blaster is present */ ++ if (itv->options.newi2c == -1 && tv.has_ir) { ++ itv->options.newi2c = (tv.has_ir & 4) ? 1 : 0; ++ if (itv->options.newi2c) { ++ IVTV_INFO("Reopen i2c bus for IR-blaster support\n"); ++ exit_ivtv_i2c(itv); ++ init_ivtv_i2c(itv); ++ } ++ } ++ ++ if (itv->std != 0) ++ /* user specified tuner standard */ ++ return; ++ ++ /* autodetect tuner standard */ ++ if (tv.tuner_formats & V4L2_STD_PAL) { ++ IVTV_DEBUG_INFO("PAL tuner detected\n"); ++ itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; ++ } else if (tv.tuner_formats & V4L2_STD_NTSC) { ++ IVTV_DEBUG_INFO("NTSC tuner detected\n"); ++ itv->std |= V4L2_STD_NTSC_M; ++ } else if (tv.tuner_formats & V4L2_STD_SECAM) { ++ IVTV_DEBUG_INFO("SECAM tuner detected\n"); ++ itv->std |= V4L2_STD_SECAM_L; ++ } else { ++ IVTV_INFO("No tuner detected, default to NTSC-M\n"); ++ itv->std |= V4L2_STD_NTSC_M; ++ } ++} ++ ++static v4l2_std_id ivtv_parse_std(struct ivtv *itv) ++{ ++ switch (pal[0]) { ++ case '6': ++ tunertype = 0; ++ return V4L2_STD_PAL_60; ++ case 'b': ++ case 'B': ++ case 'g': ++ case 'G': ++ case 'h': ++ case 'H': ++ tunertype = 0; ++ return V4L2_STD_PAL_BG | V4L2_STD_PAL_H; ++ case 'n': ++ case 'N': ++ tunertype = 1; ++ if (pal[1] == 'c' || pal[1] == 'C') ++ return V4L2_STD_PAL_Nc; ++ return V4L2_STD_PAL_N; ++ case 'i': ++ case 'I': ++ tunertype = 0; ++ return V4L2_STD_PAL_I; ++ case 'd': ++ case 'D': ++ case 'k': ++ case 'K': ++ tunertype = 0; ++ return V4L2_STD_PAL_DK; ++ case 'M': ++ case 'm': ++ tunertype = 1; ++ return V4L2_STD_PAL_M; ++ case '-': ++ break; ++ default: ++ IVTV_WARN("pal= argument not recognised\n"); ++ return 0; ++ } ++ ++ switch (secam[0]) { ++ case 'b': ++ case 'B': ++ case 'g': ++ case 'G': ++ case 'h': ++ case 'H': ++ tunertype = 0; ++ return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; ++ case 'd': ++ case 'D': ++ case 'k': ++ case 'K': ++ tunertype = 0; ++ return V4L2_STD_SECAM_DK; ++ case 'l': ++ case 'L': ++ tunertype = 0; ++ if (secam[1] == 'C' || secam[1] == 'c') ++ return V4L2_STD_SECAM_LC; ++ return V4L2_STD_SECAM_L; ++ case '-': ++ break; ++ default: ++ IVTV_WARN("secam= argument not recognised\n"); ++ return 0; ++ } ++ ++ switch (ntsc[0]) { ++ case 'm': ++ case 'M': ++ tunertype = 1; ++ return V4L2_STD_NTSC_M; ++ case 'j': ++ case 'J': ++ tunertype = 1; ++ return V4L2_STD_NTSC_M_JP; ++ case 'k': ++ case 'K': ++ tunertype = 1; ++ return V4L2_STD_NTSC_M_KR; ++ case '-': ++ break; ++ default: ++ IVTV_WARN("ntsc= argument not recognised\n"); ++ return 0; ++ } ++ ++ /* no match found */ ++ return 0; ++} ++ ++static void ivtv_process_options(struct ivtv *itv) ++{ ++ const char *chipname; ++ int i, j; ++ ++ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers * 1024; ++ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers * 1024; ++ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers * 1024; ++ itv->options.kilobytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; ++ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024; ++ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024; ++ itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; ++ itv->options.cardtype = cardtype[itv->instance]; ++ itv->options.tuner = tuner[itv->instance]; ++ itv->options.radio = radio[itv->instance]; ++ ++ itv->options.i2c_clock_period = i2c_clock_period[itv->instance]; ++ if (itv->options.i2c_clock_period == -1) ++ itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD; ++ else if (itv->options.i2c_clock_period < 10) ++ itv->options.i2c_clock_period = 10; ++ else if (itv->options.i2c_clock_period > 4500) ++ itv->options.i2c_clock_period = 4500; ++ ++ itv->options.newi2c = newi2c; ++ if (tunertype < -1 || tunertype > 1) { ++ IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); ++ tunertype = -1; ++ } ++ itv->std = ivtv_parse_std(itv); ++ if (itv->std == 0 && tunertype >= 0) ++ itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN); ++ itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15); ++ chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; ++ if (itv->options.cardtype == -1) { ++ IVTV_INFO("Ignore card (detected %s based chip)\n", chipname); ++ return; ++ } ++ if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) { ++ IVTV_INFO("User specified %s card (detected %s based chip)\n", ++ itv->card->name, chipname); ++ } else if (itv->options.cardtype != 0) { ++ IVTV_ERR("Unknown user specified type, trying to autodetect card\n"); ++ } ++ if (itv->card == NULL) { ++ if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || ++ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || ++ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { ++ itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150); ++ IVTV_INFO("Autodetected Hauppauge card (%s based)\n", ++ chipname); ++ } ++ } ++ if (itv->card == NULL) { ++ for (i = 0; (itv->card = ivtv_get_card(i)); i++) { ++ if (itv->card->pci_list == NULL) ++ continue; ++ for (j = 0; itv->card->pci_list[j].device; j++) { ++ if (itv->pdev->device != ++ itv->card->pci_list[j].device) ++ continue; ++ if (itv->pdev->subsystem_vendor != ++ itv->card->pci_list[j].subsystem_vendor) ++ continue; ++ if (itv->pdev->subsystem_device != ++ itv->card->pci_list[j].subsystem_device) ++ continue; ++ IVTV_INFO("Autodetected %s card (%s based)\n", ++ itv->card->name, chipname); ++ goto done; ++ } ++ } ++ } ++done: ++ ++ if (itv->card == NULL) { ++ itv->card = ivtv_get_card(IVTV_CARD_PVR_150); ++ IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n", ++ itv->pdev->vendor, itv->pdev->device); ++ IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n", ++ itv->pdev->subsystem_vendor, itv->pdev->subsystem_device); ++ IVTV_ERR(" %s based\n", chipname); ++ IVTV_ERR("Defaulting to %s card\n", itv->card->name); ++ IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); ++ IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); ++ IVTV_ERR("Prefix your subject line with [UNKNOWN IVTV CARD].\n"); ++ } ++ itv->v4l2_cap = itv->card->v4l2_capabilities; ++ itv->card_name = itv->card->name; ++ itv->card_i2c = itv->card->i2c; ++} ++ ++/* Precondition: the ivtv structure has been memset to 0. Only ++ the dev and num fields have been filled in. ++ No assumptions on the card type may be made here (see ivtv_init_struct2 ++ for that). ++ */ ++static int __devinit ivtv_init_struct1(struct ivtv *itv) ++{ ++ struct sched_param param = { .sched_priority = 99 }; ++ ++ itv->base_addr = pci_resource_start(itv->pdev, 0); ++ itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ ++ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ ++ ++ mutex_init(&itv->serialize_lock); ++ mutex_init(&itv->i2c_bus_lock); ++ mutex_init(&itv->udma.lock); ++ ++ spin_lock_init(&itv->lock); ++ spin_lock_init(&itv->dma_reg_lock); ++ ++ init_kthread_worker(&itv->irq_worker); ++ itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker, ++ itv->v4l2_dev.name); ++ if (IS_ERR(itv->irq_worker_task)) { ++ IVTV_ERR("Could not create ivtv task\n"); ++ return -1; ++ } ++ /* must use the FIFO scheduler as it is realtime sensitive */ ++ sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, ¶m); ++ ++ init_kthread_work(&itv->irq_work, ivtv_irq_work_handler); ++ ++ /* Initial settings */ ++ itv->cxhdl.port = CX2341X_PORT_MEMORY; ++ itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI; ++ init_waitqueue_head(&itv->eos_waitq); ++ init_waitqueue_head(&itv->event_waitq); ++ init_waitqueue_head(&itv->vsync_waitq); ++ init_waitqueue_head(&itv->dma_waitq); ++ init_timer(&itv->dma_timer); ++ itv->dma_timer.function = ivtv_unfinished_dma; ++ itv->dma_timer.data = (unsigned long)itv; ++ ++ itv->cur_dma_stream = -1; ++ itv->cur_pio_stream = -1; ++ ++ /* Ctrls */ ++ itv->speed = 1000; ++ ++ /* VBI */ ++ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced; ++ ++ /* Init the sg table for osd/yuv output */ ++ sg_init_table(itv->udma.SGlist, IVTV_DMA_SG_OSD_ENT); ++ ++ /* OSD */ ++ itv->osd_global_alpha_state = 1; ++ itv->osd_global_alpha = 255; ++ ++ /* YUV */ ++ atomic_set(&itv->yuv_info.next_dma_frame, -1); ++ itv->yuv_info.lace_mode = ivtv_yuv_mode; ++ itv->yuv_info.lace_threshold = ivtv_yuv_threshold; ++ itv->yuv_info.max_frames_buffered = 3; ++ itv->yuv_info.track_osd = 1; ++ return 0; ++} ++ ++/* Second initialization part. Here the card type has been ++ autodetected. */ ++static void __devinit ivtv_init_struct2(struct ivtv *itv) ++{ ++ int i; ++ ++ for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++) ++ if (itv->card->video_inputs[i].video_type == 0) ++ break; ++ itv->nof_inputs = i; ++ for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++) ++ if (itv->card->audio_inputs[i].audio_type == 0) ++ break; ++ itv->nof_audio_inputs = i; ++ ++ if (itv->card->hw_all & IVTV_HW_CX25840) { ++ itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */ ++ } else { ++ itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */ ++ } ++ ++ /* Find tuner input */ ++ for (i = 0; i < itv->nof_inputs; i++) { ++ if (itv->card->video_inputs[i].video_type == ++ IVTV_CARD_INPUT_VID_TUNER) ++ break; ++ } ++ if (i == itv->nof_inputs) ++ i = 0; ++ itv->active_input = i; ++ itv->audio_input = itv->card->video_inputs[i].audio_index; ++} ++ ++static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, ++ const struct pci_device_id *pci_id) ++{ ++ u16 cmd; ++ unsigned char pci_latency; ++ ++ IVTV_DEBUG_INFO("Enabling pci device\n"); ++ ++ if (pci_enable_device(pdev)) { ++ IVTV_ERR("Can't enable device!\n"); ++ return -EIO; ++ } ++ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { ++ IVTV_ERR("No suitable DMA available.\n"); ++ return -EIO; ++ } ++ if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { ++ IVTV_ERR("Cannot request encoder memory region.\n"); ++ return -EIO; ++ } ++ ++ if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, ++ IVTV_REG_SIZE, "ivtv registers")) { ++ IVTV_ERR("Cannot request register memory region.\n"); ++ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); ++ return -EIO; ++ } ++ ++ if (itv->has_cx23415 && ++ !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, ++ IVTV_DECODER_SIZE, "ivtv decoder")) { ++ IVTV_ERR("Cannot request decoder memory region.\n"); ++ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); ++ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); ++ return -EIO; ++ } ++ ++ /* Check for bus mastering */ ++ pci_read_config_word(pdev, PCI_COMMAND, &cmd); ++ if (!(cmd & PCI_COMMAND_MASTER)) { ++ IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n"); ++ pci_set_master(pdev); ++ pci_read_config_word(pdev, PCI_COMMAND, &cmd); ++ if (!(cmd & PCI_COMMAND_MASTER)) { ++ IVTV_ERR("Bus Mastering is not enabled\n"); ++ return -ENXIO; ++ } ++ } ++ IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); ++ ++ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); ++ ++ if (pci_latency < 64 && ivtv_pci_latency) { ++ IVTV_INFO("Unreasonably low latency timer, " ++ "setting to 64 (was %d)\n", pci_latency); ++ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); ++ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); ++ } ++ /* This config space value relates to DMA latencies. The ++ default value 0x8080 is too low however and will lead ++ to DMA errors. 0xffff is the max value which solves ++ these problems. */ ++ pci_write_config_dword(pdev, 0x40, 0xffff); ++ ++ IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " ++ "irq: %d, latency: %d, memory: 0x%llx\n", ++ pdev->device, pdev->revision, pdev->bus->number, ++ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), ++ pdev->irq, pci_latency, (u64)itv->base_addr); ++ ++ return 0; ++} ++ ++static void ivtv_load_and_init_modules(struct ivtv *itv) ++{ ++ u32 hw = itv->card->hw_all; ++ unsigned i; ++ ++ /* check which i2c devices are actually found */ ++ for (i = 0; i < 32; i++) { ++ u32 device = 1 << i; ++ ++ if (!(device & hw)) ++ continue; ++ if (device == IVTV_HW_GPIO || device == IVTV_HW_TVEEPROM) { ++ /* GPIO and TVEEPROM do not use i2c probing */ ++ itv->hw_flags |= device; ++ continue; ++ } ++ if (ivtv_i2c_register(itv, i) == 0) ++ itv->hw_flags |= device; ++ } ++ ++ /* probe for legacy IR controllers that aren't in card definitions */ ++ if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0) ++ ivtv_i2c_new_ir_legacy(itv); ++ ++ if (itv->card->hw_all & IVTV_HW_CX25840) ++ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); ++ else if (itv->card->hw_all & IVTV_HW_SAA717X) ++ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X); ++ else if (itv->card->hw_all & IVTV_HW_SAA7114) ++ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114); ++ else ++ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115); ++ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl); ++ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer); ++ ++ hw = itv->hw_flags; ++ ++ if (itv->card->type == IVTV_CARD_CX23416GYC) { ++ /* Several variations of this card exist, detect which card ++ type should be used. */ ++ if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0) ++ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS); ++ else if ((hw & IVTV_HW_UPD64031A) == 0) ++ itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR); ++ } ++ else if (itv->card->type == IVTV_CARD_GV_MVPRX || ++ itv->card->type == IVTV_CARD_GV_MVPRX2E) { ++ /* The crystal frequency of GVMVPRX is 24.576MHz */ ++ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, ++ SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC); ++ } ++ ++ if (hw & IVTV_HW_CX25840) { ++ itv->vbi.raw_decoder_line_size = 1444; ++ itv->vbi.raw_decoder_sav_odd_field = 0x20; ++ itv->vbi.raw_decoder_sav_even_field = 0x60; ++ itv->vbi.sliced_decoder_line_size = 272; ++ itv->vbi.sliced_decoder_sav_odd_field = 0xB0; ++ itv->vbi.sliced_decoder_sav_even_field = 0xF0; ++ } ++ ++ if (hw & IVTV_HW_SAA711X) { ++ struct v4l2_dbg_chip_ident v; ++ ++ /* determine the exact saa711x model */ ++ itv->hw_flags &= ~IVTV_HW_SAA711X; ++ ++ v.match.type = V4L2_CHIP_MATCH_I2C_DRIVER; ++ strlcpy(v.match.name, "saa7115", sizeof(v.match.name)); ++ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v); ++ if (v.ident == V4L2_IDENT_SAA7114) { ++ itv->hw_flags |= IVTV_HW_SAA7114; ++ /* VBI is not yet supported by the saa7114 driver. */ ++ itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE); ++ } else { ++ itv->hw_flags |= IVTV_HW_SAA7115; ++ } ++ itv->vbi.raw_decoder_line_size = 1443; ++ itv->vbi.raw_decoder_sav_odd_field = 0x25; ++ itv->vbi.raw_decoder_sav_even_field = 0x62; ++ itv->vbi.sliced_decoder_line_size = 51; ++ itv->vbi.sliced_decoder_sav_odd_field = 0xAB; ++ itv->vbi.sliced_decoder_sav_even_field = 0xEC; ++ } ++ ++ if (hw & IVTV_HW_SAA717X) { ++ itv->vbi.raw_decoder_line_size = 1443; ++ itv->vbi.raw_decoder_sav_odd_field = 0x25; ++ itv->vbi.raw_decoder_sav_even_field = 0x62; ++ itv->vbi.sliced_decoder_line_size = 51; ++ itv->vbi.sliced_decoder_sav_odd_field = 0xAB; ++ itv->vbi.sliced_decoder_sav_even_field = 0xEC; ++ } ++} ++ ++static int __devinit ivtv_probe(struct pci_dev *pdev, ++ const struct pci_device_id *pci_id) ++{ ++ int retval = 0; ++ int vbi_buf_size; ++ struct ivtv *itv; ++ ++ itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); ++ if (itv == NULL) ++ return -ENOMEM; ++ itv->pdev = pdev; ++ itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv", ++ &ivtv_instance); ++ ++ retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev); ++ if (retval) { ++ kfree(itv); ++ return retval; ++ } ++ IVTV_INFO("Initializing card %d\n", itv->instance); ++ ++ ivtv_process_options(itv); ++ if (itv->options.cardtype == -1) { ++ retval = -ENODEV; ++ goto err; ++ } ++ if (ivtv_init_struct1(itv)) { ++ retval = -ENOMEM; ++ goto err; ++ } ++ retval = cx2341x_handler_init(&itv->cxhdl, 50); ++ if (retval) ++ goto err; ++ itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl; ++ itv->cxhdl.ops = &ivtv_cxhdl_ops; ++ itv->cxhdl.priv = itv; ++ itv->cxhdl.func = ivtv_api_func; ++ ++ IVTV_DEBUG_INFO("base addr: 0x%llx\n", (u64)itv->base_addr); ++ ++ /* PCI Device Setup */ ++ retval = ivtv_setup_pci(itv, pdev, pci_id); ++ if (retval == -EIO) ++ goto free_worker; ++ if (retval == -ENXIO) ++ goto free_mem; ++ ++ /* map io memory */ ++ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", ++ (u64)itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); ++ itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET, ++ IVTV_ENCODER_SIZE); ++ if (!itv->enc_mem) { ++ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 " ++ "encoder memory\n"); ++ IVTV_ERR("Each capture card with a CX23415/6 needs 8 MB of " ++ "vmalloc address space for this window\n"); ++ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); ++ IVTV_ERR("Use the vmalloc= kernel command line option to set " ++ "VmallocTotal to a larger value\n"); ++ retval = -ENOMEM; ++ goto free_mem; ++ } ++ ++ if (itv->has_cx23415) { ++ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", ++ (u64)itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); ++ itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET, ++ IVTV_DECODER_SIZE); ++ if (!itv->dec_mem) { ++ IVTV_ERR("ioremap failed. Can't get a window into " ++ "CX23415 decoder memory\n"); ++ IVTV_ERR("Each capture card with a CX23415 needs 8 MB " ++ "of vmalloc address space for this window\n"); ++ IVTV_ERR("Check the output of 'grep Vmalloc " ++ "/proc/meminfo'\n"); ++ IVTV_ERR("Use the vmalloc= kernel command line option " ++ "to set VmallocTotal to a larger value\n"); ++ retval = -ENOMEM; ++ goto free_mem; ++ } ++ } ++ else { ++ itv->dec_mem = itv->enc_mem; ++ } ++ ++ /* map registers memory */ ++ IVTV_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n", ++ (u64)itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); ++ itv->reg_mem = ++ ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); ++ if (!itv->reg_mem) { ++ IVTV_ERR("ioremap failed. Can't get a window into CX23415/6 " ++ "register space\n"); ++ IVTV_ERR("Each capture card with a CX23415/6 needs 64 kB of " ++ "vmalloc address space for this window\n"); ++ IVTV_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); ++ IVTV_ERR("Use the vmalloc= kernel command line option to set " ++ "VmallocTotal to a larger value\n"); ++ retval = -ENOMEM; ++ goto free_io; ++ } ++ ++ retval = ivtv_gpio_init(itv); ++ if (retval) ++ goto free_io; ++ ++ /* active i2c */ ++ IVTV_DEBUG_INFO("activating i2c...\n"); ++ if (init_ivtv_i2c(itv)) { ++ IVTV_ERR("Could not initialize i2c\n"); ++ goto free_io; ++ } ++ ++ if (itv->card->hw_all & IVTV_HW_TVEEPROM) { ++ /* Based on the model number the cardtype may be changed. ++ The PCI IDs are not always reliable. */ ++ ivtv_process_eeprom(itv); ++ } ++ if (itv->card->comment) ++ IVTV_INFO("%s", itv->card->comment); ++ if (itv->card->v4l2_capabilities == 0) { ++ /* card was detected but is not supported */ ++ retval = -ENODEV; ++ goto free_i2c; ++ } ++ ++ if (itv->std == 0) { ++ itv->std = V4L2_STD_NTSC_M; ++ } ++ ++ if (itv->options.tuner == -1) { ++ int i; ++ ++ for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) { ++ if ((itv->std & itv->card->tuners[i].std) == 0) ++ continue; ++ itv->options.tuner = itv->card->tuners[i].tuner; ++ break; ++ } ++ } ++ /* if no tuner was found, then pick the first tuner in the card list */ ++ if (itv->options.tuner == -1 && itv->card->tuners[0].std) { ++ itv->std = itv->card->tuners[0].std; ++ if (itv->std & V4L2_STD_PAL) ++ itv->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; ++ else if (itv->std & V4L2_STD_NTSC) ++ itv->std = V4L2_STD_NTSC_M; ++ else if (itv->std & V4L2_STD_SECAM) ++ itv->std = V4L2_STD_SECAM_L; ++ itv->options.tuner = itv->card->tuners[0].tuner; ++ } ++ if (itv->options.radio == -1) ++ itv->options.radio = (itv->card->radio_input.audio_type != 0); ++ ++ /* The card is now fully identified, continue with card-specific ++ initialization. */ ++ ivtv_init_struct2(itv); ++ ++ ivtv_load_and_init_modules(itv); ++ ++ if (itv->std & V4L2_STD_525_60) { ++ itv->is_60hz = 1; ++ itv->is_out_60hz = 1; ++ } else { ++ itv->is_50hz = 1; ++ itv->is_out_50hz = 1; ++ } ++ ++ itv->yuv_info.osd_full_w = 720; ++ itv->yuv_info.osd_full_h = itv->is_out_50hz ? 576 : 480; ++ itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w; ++ itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h; ++ ++ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz); ++ ++ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; ++ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; ++ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000; ++ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = 0x10000; ++ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = 0x08000; ++ ++ /* Setup VBI Raw Size. Should be big enough to hold PAL. ++ It is possible to switch between PAL and NTSC, so we need to ++ take the largest size here. */ ++ /* 1456 is multiple of 16, real size = 1444 */ ++ itv->vbi.raw_size = 1456; ++ /* We use a buffer size of 1/2 of the total size needed for a ++ frame. This is actually very useful, since we now receive ++ a field at a time and that makes 'compressing' the raw data ++ down to size by stripping off the SAV codes a lot easier. ++ Note: having two different buffer sizes prevents standard ++ switching on the fly. We need to find a better solution... */ ++ vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2; ++ itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size; ++ itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ ++ if (itv->options.radio > 0) ++ itv->v4l2_cap |= V4L2_CAP_RADIO; ++ ++ if (itv->options.tuner > -1) { ++ struct tuner_setup setup; ++ ++ setup.addr = ADDR_UNSET; ++ setup.type = itv->options.tuner; ++ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ ++ if (itv->options.radio > 0) ++ setup.mode_mask |= T_RADIO; ++ setup.tuner_callback = (setup.type == TUNER_XC2028) ? ++ ivtv_reset_tuner_gpio : NULL; ++ ivtv_call_all(itv, tuner, s_type_addr, &setup); ++ if (setup.type == TUNER_XC2028) { ++ static struct xc2028_ctrl ctrl = { ++ .fname = XC2028_DEFAULT_FIRMWARE, ++ .max_len = 64, ++ }; ++ struct v4l2_priv_tun_config cfg = { ++ .tuner = itv->options.tuner, ++ .priv = &ctrl, ++ }; ++ ivtv_call_all(itv, tuner, s_config, &cfg); ++ } ++ } ++ ++ /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) ++ are not. */ ++ itv->tuner_std = itv->std; ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; ++ ++ itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, ++ V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0); ++ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, ++ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0); ++ /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, ++ mask that menu item. */ ++ itv->ctrl_audio_playback = ++ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, ++ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK, ++ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, ++ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, ++ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO); ++ itv->ctrl_audio_multilingual_playback = ++ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops, ++ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK, ++ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO, ++ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO, ++ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT); ++ if (hdl->error) { ++ retval = hdl->error; ++ goto free_i2c; ++ } ++ v4l2_ctrl_cluster(2, &itv->ctrl_pts); ++ v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback); ++ ivtv_call_all(itv, video, s_std_output, itv->std); ++ /* Turn off the output signal. The mpeg decoder is not yet ++ active so without this you would get a green image until the ++ mpeg decoder becomes active. */ ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ++ } ++ ++ /* clear interrupt mask, effectively disabling interrupts */ ++ ivtv_set_irq_mask(itv, 0xffffffff); ++ ++ /* Register IRQ */ ++ retval = request_irq(itv->pdev->irq, ivtv_irq_handler, ++ IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv); ++ if (retval) { ++ IVTV_ERR("Failed to register irq %d\n", retval); ++ goto free_i2c; ++ } ++ ++ retval = ivtv_streams_setup(itv); ++ if (retval) { ++ IVTV_ERR("Error %d setting up streams\n", retval); ++ goto free_irq; ++ } ++ retval = ivtv_streams_register(itv); ++ if (retval) { ++ IVTV_ERR("Error %d registering devices\n", retval); ++ goto free_streams; ++ } ++ IVTV_INFO("Initialized card: %s\n", itv->card_name); ++ ++ /* Load ivtv submodules (ivtv-alsa) */ ++ request_modules(itv); ++ return 0; ++ ++free_streams: ++ ivtv_streams_cleanup(itv, 1); ++free_irq: ++ free_irq(itv->pdev->irq, (void *)itv); ++free_i2c: ++ v4l2_ctrl_handler_free(&itv->cxhdl.hdl); ++ exit_ivtv_i2c(itv); ++free_io: ++ ivtv_iounmap(itv); ++free_mem: ++ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); ++ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); ++ if (itv->has_cx23415) ++ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); ++free_worker: ++ kthread_stop(itv->irq_worker_task); ++err: ++ if (retval == 0) ++ retval = -ENODEV; ++ IVTV_ERR("Error %d on initialization\n", retval); ++ ++ v4l2_device_unregister(&itv->v4l2_dev); ++ kfree(itv); ++ return retval; ++} ++ ++int ivtv_init_on_first_open(struct ivtv *itv) ++{ ++ struct v4l2_frequency vf; ++ /* Needed to call ioctls later */ ++ struct ivtv_open_id fh; ++ int fw_retry_count = 3; ++ int video_input; ++ ++ fh.itv = itv; ++ fh.type = IVTV_ENC_STREAM_TYPE_MPG; ++ ++ if (test_bit(IVTV_F_I_FAILED, &itv->i_flags)) ++ return -ENXIO; ++ ++ if (test_and_set_bit(IVTV_F_I_INITED, &itv->i_flags)) ++ return 0; ++ ++ while (--fw_retry_count > 0) { ++ /* load firmware */ ++ if (ivtv_firmware_init(itv) == 0) ++ break; ++ if (fw_retry_count > 1) ++ IVTV_WARN("Retry loading firmware\n"); ++ } ++ ++ if (fw_retry_count == 0) { ++ set_bit(IVTV_F_I_FAILED, &itv->i_flags); ++ return -ENXIO; ++ } ++ ++ /* Try and get firmware versions */ ++ IVTV_DEBUG_INFO("Getting firmware version..\n"); ++ ivtv_firmware_versions(itv); ++ ++ if (itv->card->hw_all & IVTV_HW_CX25840) ++ v4l2_subdev_call(itv->sd_video, core, load_fw); ++ ++ vf.tuner = 0; ++ vf.type = V4L2_TUNER_ANALOG_TV; ++ vf.frequency = 6400; /* the tuner 'baseline' frequency */ ++ ++ /* Set initial frequency. For PAL/SECAM broadcasts no ++ 'default' channel exists AFAIK. */ ++ if (itv->std == V4L2_STD_NTSC_M_JP) { ++ vf.frequency = 1460; /* ch. 1 91250*16/1000 */ ++ } ++ else if (itv->std & V4L2_STD_NTSC_M) { ++ vf.frequency = 1076; /* ch. 4 67250*16/1000 */ ++ } ++ ++ video_input = itv->active_input; ++ itv->active_input++; /* Force update of input */ ++ ivtv_s_input(NULL, &fh, video_input); ++ ++ /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code ++ in one place. */ ++ itv->std++; /* Force full standard initialization */ ++ itv->std_out = itv->std; ++ ivtv_s_frequency(NULL, &fh, &vf); ++ ++ if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { ++ /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes ++ the mpeg decoder so now the saa7127 receives a proper ++ signal. */ ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ++ ivtv_init_mpeg_decoder(itv); ++ } ++ ++ /* On a cx23416 this seems to be able to enable DMA to the chip? */ ++ if (!itv->has_cx23415) ++ write_reg_sync(0x03, IVTV_REG_DMACONTROL); ++ ++ ivtv_s_std_enc(itv, &itv->tuner_std); ++ ++ /* Default interrupts enabled. For the PVR350 this includes the ++ decoder VSYNC interrupt, which is always on. It is not only used ++ during decoding but also by the OSD. ++ Some old PVR250 cards had a cx23415, so testing for that is too ++ general. Instead test if the card has video output capability. */ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); ++ ivtv_set_osd_alpha(itv); ++ ivtv_s_std_dec(itv, &itv->tuner_std); ++ } else { ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); ++ } ++ ++ /* Setup initial controls */ ++ cx2341x_handler_setup(&itv->cxhdl); ++ return 0; ++} ++ ++static void ivtv_remove(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ int i; ++ ++ IVTV_DEBUG_INFO("Removing card\n"); ++ ++ flush_request_modules(itv); ++ ++ if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) { ++ /* Stop all captures */ ++ IVTV_DEBUG_INFO("Stopping all streams\n"); ++ if (atomic_read(&itv->capturing) > 0) ++ ivtv_stop_all_captures(itv); ++ ++ /* Stop all decoding */ ++ IVTV_DEBUG_INFO("Stopping decoding\n"); ++ ++ /* Turn off the TV-out */ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ++ if (atomic_read(&itv->decoding) > 0) { ++ int type; ++ ++ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) ++ type = IVTV_DEC_STREAM_TYPE_YUV; ++ else ++ type = IVTV_DEC_STREAM_TYPE_MPG; ++ ivtv_stop_v4l2_decode_stream(&itv->streams[type], ++ V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); ++ } ++ ivtv_halt_firmware(itv); ++ } ++ ++ /* Interrupts */ ++ ivtv_set_irq_mask(itv, 0xffffffff); ++ del_timer_sync(&itv->dma_timer); ++ ++ /* Kill irq worker */ ++ flush_kthread_worker(&itv->irq_worker); ++ kthread_stop(itv->irq_worker_task); ++ ++ ivtv_streams_cleanup(itv, 1); ++ ivtv_udma_free(itv); ++ ++ v4l2_ctrl_handler_free(&itv->cxhdl.hdl); ++ ++ exit_ivtv_i2c(itv); ++ ++ free_irq(itv->pdev->irq, (void *)itv); ++ ivtv_iounmap(itv); ++ ++ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); ++ release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); ++ if (itv->has_cx23415) ++ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); ++ ++ pci_disable_device(itv->pdev); ++ for (i = 0; i < IVTV_VBI_FRAMES; i++) ++ kfree(itv->vbi.sliced_mpeg_data[i]); ++ ++ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name); ++ ++ v4l2_device_unregister(&itv->v4l2_dev); ++ kfree(itv); ++} ++ ++/* define a pci_driver for card detection */ ++static struct pci_driver ivtv_pci_driver = { ++ .name = "ivtv", ++ .id_table = ivtv_pci_tbl, ++ .probe = ivtv_probe, ++ .remove = ivtv_remove, ++}; ++ ++static int __init module_start(void) ++{ ++ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); ++ ++ /* Validate parameters */ ++ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { ++ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", ++ IVTV_MAX_CARDS - 1); ++ return -1; ++ } ++ ++ if (ivtv_debug < 0 || ivtv_debug > 2047) { ++ ivtv_debug = 0; ++ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); ++ } ++ ++ if (pci_register_driver(&ivtv_pci_driver)) { ++ printk(KERN_ERR "ivtv: Error detecting PCI card\n"); ++ return -ENODEV; ++ } ++ printk(KERN_INFO "ivtv: End initialization\n"); ++ return 0; ++} ++ ++static void __exit module_cleanup(void) ++{ ++ pci_unregister_driver(&ivtv_pci_driver); ++} ++ ++/* Note: These symbols are exported because they are used by the ivtvfb ++ framebuffer module and an infrared module for the IR-blaster. */ ++EXPORT_SYMBOL(ivtv_set_irq_mask); ++EXPORT_SYMBOL(ivtv_api); ++EXPORT_SYMBOL(ivtv_vapi); ++EXPORT_SYMBOL(ivtv_vapi_result); ++EXPORT_SYMBOL(ivtv_clear_irq_mask); ++EXPORT_SYMBOL(ivtv_debug); ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++EXPORT_SYMBOL(ivtv_fw_debug); ++#endif ++EXPORT_SYMBOL(ivtv_reset_ir_gpio); ++EXPORT_SYMBOL(ivtv_udma_setup); ++EXPORT_SYMBOL(ivtv_udma_unmap); ++EXPORT_SYMBOL(ivtv_udma_alloc); ++EXPORT_SYMBOL(ivtv_udma_prepare); ++EXPORT_SYMBOL(ivtv_init_on_first_open); ++EXPORT_SYMBOL(ivtv_firmware_check); ++ ++module_init(module_start); ++module_exit(module_cleanup); +diff --git a/drivers/media/pci/ivtv/ivtv-driver.h b/drivers/media/pci/ivtv/ivtv-driver.h +new file mode 100644 +index 0000000..bc309f4 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-driver.h +@@ -0,0 +1,850 @@ ++/* ++ ivtv driver internal defines and structures ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_DRIVER_H ++#define IVTV_DRIVER_H ++ ++/* Internal header for ivtv project: ++ * Driver for the cx23415/6 chip. ++ * Author: Kevin Thayer (nufan_wfk at yahoo.com) ++ * License: GPL ++ * http://www.ivtvdriver.org ++ * ++ * ----- ++ * MPG600/MPG160 support by T.Adachi ++ * and Takeru KOMORIYA ++ * ++ * AVerMedia M179 GPIO info by Chris Pinkham ++ * using information provided by Jiun-Kuei Jung @ AVerMedia. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Memory layout */ ++#define IVTV_ENCODER_OFFSET 0x00000000 ++#define IVTV_ENCODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */ ++#define IVTV_DECODER_OFFSET 0x01000000 ++#define IVTV_DECODER_SIZE 0x00800000 /* Total size is 0x01000000, but only first half is used */ ++#define IVTV_REG_OFFSET 0x02000000 ++#define IVTV_REG_SIZE 0x00010000 ++ ++/* Maximum ivtv driver instances. Some people have a huge number of ++ capture cards, so set this to a high value. */ ++#define IVTV_MAX_CARDS 32 ++ ++#define IVTV_ENC_STREAM_TYPE_MPG 0 ++#define IVTV_ENC_STREAM_TYPE_YUV 1 ++#define IVTV_ENC_STREAM_TYPE_VBI 2 ++#define IVTV_ENC_STREAM_TYPE_PCM 3 ++#define IVTV_ENC_STREAM_TYPE_RAD 4 ++#define IVTV_DEC_STREAM_TYPE_MPG 5 ++#define IVTV_DEC_STREAM_TYPE_VBI 6 ++#define IVTV_DEC_STREAM_TYPE_VOUT 7 ++#define IVTV_DEC_STREAM_TYPE_YUV 8 ++#define IVTV_MAX_STREAMS 9 ++ ++#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */ ++ ++/* DMA Registers */ ++#define IVTV_REG_DMAXFER (0x0000) ++#define IVTV_REG_DMASTATUS (0x0004) ++#define IVTV_REG_DECDMAADDR (0x0008) ++#define IVTV_REG_ENCDMAADDR (0x000c) ++#define IVTV_REG_DMACONTROL (0x0010) ++#define IVTV_REG_IRQSTATUS (0x0040) ++#define IVTV_REG_IRQMASK (0x0048) ++ ++/* Setup Registers */ ++#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) ++#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) ++#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8) ++#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC) ++#define IVTV_REG_VDM (0x2800) ++#define IVTV_REG_AO (0x2D00) ++#define IVTV_REG_BYTEFLUSH (0x2D24) ++#define IVTV_REG_SPU (0x9050) ++#define IVTV_REG_HW_BLOCKS (0x9054) ++#define IVTV_REG_VPU (0x9058) ++#define IVTV_REG_APU (0xA064) ++ ++/* Other registers */ ++#define IVTV_REG_DEC_LINE_FIELD (0x28C0) ++ ++/* debugging */ ++extern int ivtv_debug; ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++extern int ivtv_fw_debug; ++#endif ++ ++#define IVTV_DBGFLG_WARN (1 << 0) ++#define IVTV_DBGFLG_INFO (1 << 1) ++#define IVTV_DBGFLG_MB (1 << 2) ++#define IVTV_DBGFLG_IOCTL (1 << 3) ++#define IVTV_DBGFLG_FILE (1 << 4) ++#define IVTV_DBGFLG_DMA (1 << 5) ++#define IVTV_DBGFLG_IRQ (1 << 6) ++#define IVTV_DBGFLG_DEC (1 << 7) ++#define IVTV_DBGFLG_YUV (1 << 8) ++#define IVTV_DBGFLG_I2C (1 << 9) ++/* Flag to turn on high volume debugging */ ++#define IVTV_DBGFLG_HIGHVOL (1 << 10) ++ ++#define IVTV_DEBUG(x, type, fmt, args...) \ ++ do { \ ++ if ((x) & ivtv_debug) \ ++ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \ ++ } while (0) ++#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args) ++#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) ++#define IVTV_DEBUG_MB(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_MB, "mb", fmt , ## args) ++#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) ++#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) ++#define IVTV_DEBUG_FILE(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_FILE, "file", fmt , ## args) ++#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) ++#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) ++#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) ++#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) ++ ++#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ ++ do { \ ++ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ ++ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \ ++ } while (0) ++#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args) ++#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args) ++#define IVTV_DEBUG_HI_MB(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_MB, "mb", fmt , ## args) ++#define IVTV_DEBUG_HI_DMA(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA, "dma", fmt , ## args) ++#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) ++#define IVTV_DEBUG_HI_FILE(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_FILE, "file", fmt , ## args) ++#define IVTV_DEBUG_HI_I2C(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) ++#define IVTV_DEBUG_HI_IRQ(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) ++#define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args) ++#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) ++ ++/* Standard kernel messages */ ++#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args) ++#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args) ++#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args) ++ ++/* output modes (cx23415 only) */ ++#define OUT_NONE 0 ++#define OUT_MPG 1 ++#define OUT_YUV 2 ++#define OUT_UDMA_YUV 3 ++#define OUT_PASSTHROUGH 4 ++ ++#define IVTV_MAX_PGM_INDEX (400) ++ ++/* Default I2C SCL period in microseconds */ ++#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20 ++ ++struct ivtv_options { ++ int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */ ++ int cardtype; /* force card type on load */ ++ int tuner; /* set tuner on load */ ++ int radio; /* enable/disable radio */ ++ int newi2c; /* new I2C algorithm */ ++ int i2c_clock_period; /* period of SCL for I2C bus */ ++}; ++ ++/* ivtv-specific mailbox template */ ++struct ivtv_mailbox { ++ u32 flags; ++ u32 cmd; ++ u32 retval; ++ u32 timeout; ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++}; ++ ++struct ivtv_api_cache { ++ unsigned long last_jiffies; /* when last command was issued */ ++ u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */ ++}; ++ ++struct ivtv_mailbox_data { ++ volatile struct ivtv_mailbox __iomem *mbox; ++ /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes. ++ If the bit is set, then the corresponding mailbox is in use by the driver. */ ++ unsigned long busy; ++ u8 max_mbox; ++}; ++ ++/* per-buffer bit flags */ ++#define IVTV_F_B_NEED_BUF_SWAP (1 << 0) /* this buffer should be byte swapped */ ++ ++/* per-stream, s_flags */ ++#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */ ++#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */ ++#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */ ++ ++#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */ ++#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ ++#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ ++#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */ ++#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ ++#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ ++ ++#define IVTV_F_S_PIO_PENDING 9 /* this stream has pending PIO */ ++#define IVTV_F_S_PIO_HAS_VBI 1 /* the current PIO request also requests VBI data */ ++ ++/* per-ivtv, i_flags */ ++#define IVTV_F_I_DMA 0 /* DMA in progress */ ++#define IVTV_F_I_UDMA 1 /* UDMA in progress */ ++#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */ ++#define IVTV_F_I_SPEED_CHANGE 3 /* a speed change is in progress */ ++#define IVTV_F_I_EOS 4 /* end of encoder stream reached */ ++#define IVTV_F_I_RADIO_USER 5 /* the radio tuner is selected */ ++#define IVTV_F_I_DIG_RST 6 /* reset digitizer */ ++#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */ ++#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */ ++#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */ ++#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */ ++#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ ++#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ ++#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ ++#define IVTV_F_I_HAVE_WORK 15 /* used in the interrupt handler: there is work to be done */ ++#define IVTV_F_I_WORK_HANDLER_VBI 16 /* there is work to be done for VBI */ ++#define IVTV_F_I_WORK_HANDLER_YUV 17 /* there is work to be done for YUV */ ++#define IVTV_F_I_WORK_HANDLER_PIO 18 /* there is work to be done for PIO */ ++#define IVTV_F_I_PIO 19 /* PIO in progress */ ++#define IVTV_F_I_DEC_PAUSED 20 /* the decoder is paused */ ++#define IVTV_F_I_INITED 21 /* set after first open */ ++#define IVTV_F_I_FAILED 22 /* set if first open failed */ ++#define IVTV_F_I_WORK_HANDLER_PCM 23 /* there is work to be done for PCM */ ++ ++/* Event notifications */ ++#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ ++#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */ ++#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */ ++#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */ ++ ++/* Scatter-Gather array element, used in DMA transfers */ ++struct ivtv_sg_element { ++ __le32 src; ++ __le32 dst; ++ __le32 size; ++}; ++ ++struct ivtv_sg_host_element { ++ u32 src; ++ u32 dst; ++ u32 size; ++}; ++ ++struct ivtv_user_dma { ++ struct mutex lock; ++ int page_count; ++ struct page *map[IVTV_DMA_SG_OSD_ENT]; ++ /* Needed when dealing with highmem userspace buffers */ ++ struct page *bouncemap[IVTV_DMA_SG_OSD_ENT]; ++ ++ /* Base Dev SG Array for cx23415/6 */ ++ struct ivtv_sg_element SGarray[IVTV_DMA_SG_OSD_ENT]; ++ dma_addr_t SG_handle; ++ int SG_length; ++ ++ /* SG List of Buffers */ ++ struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT]; ++}; ++ ++struct ivtv_dma_page_info { ++ unsigned long uaddr; ++ unsigned long first; ++ unsigned long last; ++ unsigned int offset; ++ unsigned int tail; ++ int page_count; ++}; ++ ++struct ivtv_buffer { ++ struct list_head list; ++ dma_addr_t dma_handle; ++ unsigned short b_flags; ++ unsigned short dma_xfer_cnt; ++ char *buf; ++ u32 bytesused; ++ u32 readpos; ++}; ++ ++struct ivtv_queue { ++ struct list_head list; /* the list of buffers in this queue */ ++ u32 buffers; /* number of buffers in this queue */ ++ u32 length; /* total number of bytes of available buffer space */ ++ u32 bytesused; /* total number of bytes used in this queue */ ++}; ++ ++struct ivtv; /* forward reference */ ++ ++struct ivtv_stream { ++ /* These first four fields are always set, even if the stream ++ is not actually created. */ ++ struct video_device *vdev; /* NULL when stream not created */ ++ struct ivtv *itv; /* for ease of use */ ++ const char *name; /* name of the stream */ ++ int type; /* stream type */ ++ u32 caps; /* V4L2 capabilities */ ++ ++ struct v4l2_fh *fh; /* pointer to the streaming filehandle */ ++ spinlock_t qlock; /* locks access to the queues */ ++ unsigned long s_flags; /* status flags, see above */ ++ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */ ++ u32 pending_offset; ++ u32 pending_backup; ++ u64 pending_pts; ++ ++ u32 dma_offset; ++ u32 dma_backup; ++ u64 dma_pts; ++ ++ int subtype; ++ wait_queue_head_t waitq; ++ u32 dma_last_offset; ++ ++ /* Buffer Stats */ ++ u32 buffers; ++ u32 buf_size; ++ u32 buffers_stolen; ++ ++ /* Buffer Queues */ ++ struct ivtv_queue q_free; /* free buffers */ ++ struct ivtv_queue q_full; /* full buffers */ ++ struct ivtv_queue q_io; /* waiting for I/O */ ++ struct ivtv_queue q_dma; /* waiting for DMA */ ++ struct ivtv_queue q_predma; /* waiting for DMA */ ++ ++ /* DMA xfer counter, buffers belonging to the same DMA ++ xfer will have the same dma_xfer_cnt. */ ++ u16 dma_xfer_cnt; ++ ++ /* Base Dev SG Array for cx23415/6 */ ++ struct ivtv_sg_host_element *sg_pending; ++ struct ivtv_sg_host_element *sg_processing; ++ struct ivtv_sg_element *sg_dma; ++ dma_addr_t sg_handle; ++ int sg_pending_size; ++ int sg_processing_size; ++ int sg_processed; ++ ++ /* SG List of Buffers */ ++ struct scatterlist *SGlist; ++}; ++ ++struct ivtv_open_id { ++ struct v4l2_fh fh; ++ int type; /* stream type */ ++ int yuv_frames; /* 1: started OUT_UDMA_YUV output mode */ ++ struct ivtv *itv; ++}; ++ ++static inline struct ivtv_open_id *fh2id(struct v4l2_fh *fh) ++{ ++ return container_of(fh, struct ivtv_open_id, fh); ++} ++ ++struct yuv_frame_info ++{ ++ u32 update; ++ s32 src_x; ++ s32 src_y; ++ u32 src_w; ++ u32 src_h; ++ s32 dst_x; ++ s32 dst_y; ++ u32 dst_w; ++ u32 dst_h; ++ s32 pan_x; ++ s32 pan_y; ++ u32 vis_w; ++ u32 vis_h; ++ u32 interlaced_y; ++ u32 interlaced_uv; ++ s32 tru_x; ++ u32 tru_w; ++ u32 tru_h; ++ u32 offset_y; ++ s32 lace_mode; ++ u32 sync_field; ++ u32 delay; ++ u32 interlaced; ++}; ++ ++#define IVTV_YUV_MODE_INTERLACED 0x00 ++#define IVTV_YUV_MODE_PROGRESSIVE 0x01 ++#define IVTV_YUV_MODE_AUTO 0x02 ++#define IVTV_YUV_MODE_MASK 0x03 ++ ++#define IVTV_YUV_SYNC_EVEN 0x00 ++#define IVTV_YUV_SYNC_ODD 0x04 ++#define IVTV_YUV_SYNC_MASK 0x04 ++ ++#define IVTV_YUV_BUFFERS 8 ++ ++struct yuv_playback_info ++{ ++ u32 reg_2834; ++ u32 reg_2838; ++ u32 reg_283c; ++ u32 reg_2840; ++ u32 reg_2844; ++ u32 reg_2848; ++ u32 reg_2854; ++ u32 reg_285c; ++ u32 reg_2864; ++ ++ u32 reg_2870; ++ u32 reg_2874; ++ u32 reg_2890; ++ u32 reg_2898; ++ u32 reg_289c; ++ ++ u32 reg_2918; ++ u32 reg_291c; ++ u32 reg_2920; ++ u32 reg_2924; ++ u32 reg_2928; ++ u32 reg_292c; ++ u32 reg_2930; ++ ++ u32 reg_2934; ++ ++ u32 reg_2938; ++ u32 reg_293c; ++ u32 reg_2940; ++ u32 reg_2944; ++ u32 reg_2948; ++ u32 reg_294c; ++ u32 reg_2950; ++ u32 reg_2954; ++ u32 reg_2958; ++ u32 reg_295c; ++ u32 reg_2960; ++ u32 reg_2964; ++ u32 reg_2968; ++ u32 reg_296c; ++ ++ u32 reg_2970; ++ ++ int v_filter_1; ++ int v_filter_2; ++ int h_filter; ++ ++ u8 track_osd; /* Should yuv output track the OSD size & position */ ++ ++ u32 osd_x_offset; ++ u32 osd_y_offset; ++ ++ u32 osd_x_pan; ++ u32 osd_y_pan; ++ ++ u32 osd_vis_w; ++ u32 osd_vis_h; ++ ++ u32 osd_full_w; ++ u32 osd_full_h; ++ ++ int decode_height; ++ ++ int lace_mode; ++ int lace_threshold; ++ int lace_sync_field; ++ ++ atomic_t next_dma_frame; ++ atomic_t next_fill_frame; ++ ++ u32 yuv_forced_update; ++ int update_frame; ++ ++ u8 fields_lapsed; /* Counter used when delaying a frame */ ++ ++ struct yuv_frame_info new_frame_info[IVTV_YUV_BUFFERS]; ++ struct yuv_frame_info old_frame_info; ++ struct yuv_frame_info old_frame_info_args; ++ ++ void *blanking_ptr; ++ dma_addr_t blanking_dmaptr; ++ ++ int stream_size; ++ ++ u8 draw_frame; /* PVR350 buffer to draw into */ ++ u8 max_frames_buffered; /* Maximum number of frames to buffer */ ++ ++ struct v4l2_rect main_rect; ++ u32 v4l2_src_w; ++ u32 v4l2_src_h; ++ ++ u8 running; /* Have any frames been displayed */ ++}; ++ ++#define IVTV_VBI_FRAMES 32 ++ ++/* VBI data */ ++struct vbi_cc { ++ u8 odd[2]; /* two-byte payload of odd field */ ++ u8 even[2]; /* two-byte payload of even field */; ++}; ++ ++struct vbi_vps { ++ u8 data[5]; /* five-byte VPS payload */ ++}; ++ ++struct vbi_info { ++ /* VBI general data, does not change during streaming */ ++ ++ u32 raw_decoder_line_size; /* raw VBI line size from digitizer */ ++ u8 raw_decoder_sav_odd_field; /* raw VBI Start Active Video digitizer code of odd field */ ++ u8 raw_decoder_sav_even_field; /* raw VBI Start Active Video digitizer code of even field */ ++ u32 sliced_decoder_line_size; /* sliced VBI line size from digitizer */ ++ u8 sliced_decoder_sav_odd_field; /* sliced VBI Start Active Video digitizer code of odd field */ ++ u8 sliced_decoder_sav_even_field; /* sliced VBI Start Active Video digitizer code of even field */ ++ ++ u32 start[2]; /* start of first VBI line in the odd/even fields */ ++ u32 count; /* number of VBI lines per field */ ++ u32 raw_size; /* size of raw VBI line from the digitizer */ ++ u32 sliced_size; /* size of sliced VBI line from the digitizer */ ++ ++ u32 dec_start; /* start in decoder memory of VBI re-insertion buffers */ ++ u32 enc_start; /* start in encoder memory of VBI capture buffers */ ++ u32 enc_size; /* size of VBI capture area */ ++ int fpi; /* number of VBI frames per interrupt */ ++ ++ struct v4l2_format in; /* current VBI capture format */ ++ struct v4l2_sliced_vbi_format *sliced_in; /* convenience pointer to sliced struct in vbi.in union */ ++ int insert_mpeg; /* if non-zero, then embed VBI data in MPEG stream */ ++ ++ /* Raw VBI compatibility hack */ ++ ++ u32 frame; /* frame counter hack needed for backwards compatibility ++ of old VBI software */ ++ ++ /* Sliced VBI output data */ ++ ++ struct vbi_cc cc_payload[256]; /* sliced VBI CC payload array: it is an array to ++ prevent dropping CC data if they couldn't be ++ processed fast enough */ ++ int cc_payload_idx; /* index in cc_payload */ ++ u8 cc_missing_cnt; /* counts number of frames without CC for passthrough mode */ ++ int wss_payload; /* sliced VBI WSS payload */ ++ u8 wss_missing_cnt; /* counts number of frames without WSS for passthrough mode */ ++ struct vbi_vps vps_payload; /* sliced VBI VPS payload */ ++ ++ /* Sliced VBI capture data */ ++ ++ struct v4l2_sliced_vbi_data sliced_data[36]; /* sliced VBI storage for VBI encoder stream */ ++ struct v4l2_sliced_vbi_data sliced_dec_data[36];/* sliced VBI storage for VBI decoder stream */ ++ ++ /* VBI Embedding data */ ++ ++ /* Buffer for VBI data inserted into MPEG stream. ++ The first byte is a dummy byte that's never used. ++ The next 16 bytes contain the MPEG header for the VBI data, ++ the remainder is the actual VBI data. ++ The max size accepted by the MPEG VBI reinsertion turns out ++ to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, ++ where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is ++ a single line header byte and 2 * 18 is the number of VBI lines per frame. ++ ++ However, it seems that the data must be 1K aligned, so we have to ++ pad the data until the 1 or 2 K boundary. ++ ++ This pointer array will allocate 2049 bytes to store each VBI frame. */ ++ u8 *sliced_mpeg_data[IVTV_VBI_FRAMES]; ++ u32 sliced_mpeg_size[IVTV_VBI_FRAMES]; ++ struct ivtv_buffer sliced_mpeg_buf; /* temporary buffer holding data from sliced_mpeg_data */ ++ u32 inserted_frame; /* index in sliced_mpeg_size of next sliced data ++ to be inserted in the MPEG stream */ ++}; ++ ++/* forward declaration of struct defined in ivtv-cards.h */ ++struct ivtv_card; ++ ++/* Struct to hold info about ivtv cards */ ++struct ivtv { ++ /* General fixed card data */ ++ struct pci_dev *pdev; /* PCI device */ ++ const struct ivtv_card *card; /* card information */ ++ const char *card_name; /* full name of the card */ ++ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ ++ u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */ ++ u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */ ++ u8 nof_inputs; /* number of video inputs */ ++ u8 nof_audio_inputs; /* number of audio inputs */ ++ u32 v4l2_cap; /* V4L2 capabilities of card */ ++ u32 hw_flags; /* hardware description of the board */ ++ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */ ++ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */ ++ struct v4l2_subdev *sd_audio; /* controlling audio subdev */ ++ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */ ++ resource_size_t base_addr; /* PCI resource base address */ ++ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */ ++ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */ ++ volatile void __iomem *reg_mem; /* pointer to mapped registers */ ++ struct ivtv_options options; /* user options */ ++ ++ struct v4l2_device v4l2_dev; ++ struct cx2341x_handler cxhdl; ++ struct { ++ /* PTS/Frame count control cluster */ ++ struct v4l2_ctrl *ctrl_pts; ++ struct v4l2_ctrl *ctrl_frame; ++ }; ++ struct { ++ /* Audio Playback control cluster */ ++ struct v4l2_ctrl *ctrl_audio_playback; ++ struct v4l2_ctrl *ctrl_audio_multilingual_playback; ++ }; ++ struct v4l2_ctrl_handler hdl_gpio; ++ struct v4l2_subdev sd_gpio; /* GPIO sub-device */ ++ u16 instance; ++ ++ /* High-level state info */ ++ unsigned long i_flags; /* global ivtv flags */ ++ u8 is_50hz; /* 1 if the current capture standard is 50 Hz */ ++ u8 is_60hz /* 1 if the current capture standard is 60 Hz */; ++ u8 is_out_50hz /* 1 if the current TV output standard is 50 Hz */; ++ u8 is_out_60hz /* 1 if the current TV output standard is 60 Hz */; ++ int output_mode; /* decoder output mode: NONE, MPG, YUV, UDMA YUV, passthrough */ ++ u32 audio_input; /* current audio input */ ++ u32 active_input; /* current video input */ ++ u32 active_output; /* current video output */ ++ v4l2_std_id std; /* current capture TV standard */ ++ v4l2_std_id std_out; /* current TV output standard */ ++ u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */ ++ u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */ ++ ++ /* Locking */ ++ spinlock_t lock; /* lock access to this struct */ ++ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ ++ ++ /* Streams */ ++ int stream_buf_size[IVTV_MAX_STREAMS]; /* stream buffer size */ ++ struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* stream data */ ++ atomic_t capturing; /* count number of active capture streams */ ++ atomic_t decoding; /* count number of active decoding streams */ ++ ++ /* ALSA interface for PCM capture stream */ ++ struct snd_ivtv_card *alsa; ++ void (*pcm_announce_callback)(struct snd_ivtv_card *card, u8 *pcm_data, ++ size_t num_bytes); ++ ++ /* Used for ivtv-alsa module loading */ ++ struct work_struct request_module_wk; ++ ++ /* Interrupts & DMA */ ++ u32 irqmask; /* active interrupts */ ++ u32 irq_rr_idx; /* round-robin stream index */ ++ struct kthread_worker irq_worker; /* kthread worker for PIO/YUV/VBI actions */ ++ struct task_struct *irq_worker_task; /* task for irq_worker */ ++ struct kthread_work irq_work; /* kthread work entry */ ++ spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ ++ int cur_dma_stream; /* index of current stream doing DMA (-1 if none) */ ++ int cur_pio_stream; /* index of current stream doing PIO (-1 if none) */ ++ u32 dma_data_req_offset; /* store offset in decoder memory of current DMA request */ ++ u32 dma_data_req_size; /* store size of current DMA request */ ++ int dma_retries; /* current DMA retry attempt */ ++ struct ivtv_user_dma udma; /* user based DMA for OSD */ ++ struct timer_list dma_timer; /* timer used to catch unfinished DMAs */ ++ u32 last_vsync_field; /* last seen vsync field */ ++ wait_queue_head_t dma_waitq; /* wake up when the current DMA is finished */ ++ wait_queue_head_t eos_waitq; /* wake up when EOS arrives */ ++ wait_queue_head_t event_waitq; /* wake up when the next decoder event arrives */ ++ wait_queue_head_t vsync_waitq; /* wake up when the next decoder vsync arrives */ ++ ++ ++ /* Mailbox */ ++ struct ivtv_mailbox_data enc_mbox; /* encoder mailboxes */ ++ struct ivtv_mailbox_data dec_mbox; /* decoder mailboxes */ ++ struct ivtv_api_cache api_cache[256]; /* cached API commands */ ++ ++ ++ /* I2C */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_algo_bit_data i2c_algo; ++ struct i2c_client i2c_client; ++ int i2c_state; /* i2c bit state */ ++ struct mutex i2c_bus_lock; /* lock i2c bus */ ++ ++ struct IR_i2c_init_data ir_i2c_init_data; ++ ++ /* Program Index information */ ++ u32 pgm_info_offset; /* start of pgm info in encoder memory */ ++ u32 pgm_info_num; /* number of elements in the pgm cyclic buffer in encoder memory */ ++ u32 pgm_info_write_idx; /* last index written by the card that was transferred to pgm_info[] */ ++ u32 pgm_info_read_idx; /* last index in pgm_info read by the application */ ++ struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; /* filled from the pgm cyclic buffer on the card */ ++ ++ ++ /* Miscellaneous */ ++ u32 open_id; /* incremented each time an open occurs, is >= 1 */ ++ int search_pack_header; /* 1 if ivtv_copy_buf_to_user() is scanning for a pack header (0xba) */ ++ int speed; /* current playback speed setting */ ++ u8 speed_mute_audio; /* 1 if audio should be muted when fast forward */ ++ u64 mpg_data_received; /* number of bytes received from the MPEG stream */ ++ u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */ ++ u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */ ++ unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */ ++ u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */ ++ ++ ++ /* VBI state info */ ++ struct vbi_info vbi; /* VBI-specific data */ ++ ++ ++ /* YUV playback */ ++ struct yuv_playback_info yuv_info; /* YUV playback data */ ++ ++ ++ /* OSD support */ ++ unsigned long osd_video_pbase; ++ int osd_global_alpha_state; /* 1 = global alpha is on */ ++ int osd_local_alpha_state; /* 1 = local alpha is on */ ++ int osd_chroma_key_state; /* 1 = chroma-keying is on */ ++ u8 osd_global_alpha; /* current global alpha */ ++ u32 osd_chroma_key; /* current chroma key */ ++ struct v4l2_rect osd_rect; /* current OSD position and size */ ++ struct v4l2_rect main_rect; /* current Main window position and size */ ++ struct osd_info *osd_info; /* ivtvfb private OSD info */ ++ void (*ivtvfb_restore)(struct ivtv *itv); /* Used for a warm start */ ++}; ++ ++static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct ivtv, v4l2_dev); ++} ++ ++/* ivtv extensions to be loaded */ ++extern int (*ivtv_ext_init)(struct ivtv *); ++ ++/* Globals */ ++extern int ivtv_first_minor; ++ ++/*==============Prototypes==================*/ ++ ++/* Hardware/IRQ */ ++void ivtv_set_irq_mask(struct ivtv *itv, u32 mask); ++void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask); ++ ++/* try to set output mode, return current mode. */ ++int ivtv_set_output_mode(struct ivtv *itv, int mode); ++ ++/* return current output stream based on current mode */ ++struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv); ++ ++/* Return non-zero if a signal is pending */ ++int ivtv_msleep_timeout(unsigned int msecs, int intr); ++ ++/* Wait on queue, returns -EINTR if interrupted */ ++int ivtv_waitq(wait_queue_head_t *waitq); ++ ++/* Read Hauppauge eeprom */ ++struct tveeprom; /* forward reference */ ++void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); ++ ++/* First-open initialization: load firmware, init cx25840, etc. */ ++int ivtv_init_on_first_open(struct ivtv *itv); ++ ++/* Test if the current VBI mode is raw (1) or sliced (0) */ ++static inline int ivtv_raw_vbi(const struct ivtv *itv) ++{ ++ return itv->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; ++} ++ ++/* This is a PCI post thing, where if the pci register is not read, then ++ the write doesn't always take effect right away. By reading back the ++ register any pending PCI writes will be performed (in order), and so ++ you can be sure that the writes are guaranteed to be done. ++ ++ Rarely needed, only in some timing sensitive cases. ++ Apparently if this is not done some motherboards seem ++ to kill the firmware and get into the broken state until computer is ++ rebooted. */ ++#define write_sync(val, reg) \ ++ do { writel(val, reg); readl(reg); } while (0) ++ ++#define read_reg(reg) readl(itv->reg_mem + (reg)) ++#define write_reg(val, reg) writel(val, itv->reg_mem + (reg)) ++#define write_reg_sync(val, reg) \ ++ do { write_reg(val, reg); read_reg(reg); } while (0) ++ ++#define read_enc(addr) readl(itv->enc_mem + (u32)(addr)) ++#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr)) ++#define write_enc_sync(val, addr) \ ++ do { write_enc(val, addr); read_enc(addr); } while (0) ++ ++#define read_dec(addr) readl(itv->dec_mem + (u32)(addr)) ++#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr)) ++#define write_dec_sync(val, addr) \ ++ do { write_dec(val, addr); read_dec(addr); } while (0) ++ ++/* Call the specified callback for all subdevs matching hw (if 0, then ++ match them all). Ignore any errors. */ ++#define ivtv_call_hw(itv, hw, o, f, args...) \ ++ do { \ ++ struct v4l2_subdev *__sd; \ ++ __v4l2_device_call_subdevs_p(&(itv)->v4l2_dev, __sd, \ ++ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ ++ } while (0) ++ ++#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) ++ ++/* Call the specified callback for all subdevs matching hw (if 0, then ++ match them all). If the callback returns an error other than 0 or ++ -ENOIOCTLCMD, then return with that error code. */ ++#define ivtv_call_hw_err(itv, hw, o, f, args...) \ ++({ \ ++ struct v4l2_subdev *__sd; \ ++ __v4l2_device_call_subdevs_until_err_p(&(itv)->v4l2_dev, __sd, \ ++ !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ ++}) ++ ++#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-fileops.c b/drivers/media/pci/ivtv/ivtv-fileops.c +new file mode 100644 +index 0000000..9caffd8 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-fileops.c +@@ -0,0 +1,1073 @@ ++/* ++ file operation functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-fileops.h" ++#include "ivtv-i2c.h" ++#include "ivtv-queue.h" ++#include "ivtv-udma.h" ++#include "ivtv-irq.h" ++#include "ivtv-vbi.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-routing.h" ++#include "ivtv-streams.h" ++#include "ivtv-yuv.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-cards.h" ++#include "ivtv-firmware.h" ++#include ++#include ++ ++/* This function tries to claim the stream for a specific file descriptor. ++ If no one else is using this stream then the stream is claimed and ++ associated VBI streams are also automatically claimed. ++ Possible error returns: -EBUSY if someone else has claimed ++ the stream or 0 on success. */ ++int ivtv_claim_stream(struct ivtv_open_id *id, int type) ++{ ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[type]; ++ struct ivtv_stream *s_vbi; ++ int vbi_type; ++ ++ if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { ++ /* someone already claimed this stream */ ++ if (s->fh == &id->fh) { ++ /* yes, this file descriptor did. So that's OK. */ ++ return 0; ++ } ++ if (s->fh == NULL && (type == IVTV_DEC_STREAM_TYPE_VBI || ++ type == IVTV_ENC_STREAM_TYPE_VBI)) { ++ /* VBI is handled already internally, now also assign ++ the file descriptor to this stream for external ++ reading of the stream. */ ++ s->fh = &id->fh; ++ IVTV_DEBUG_INFO("Start Read VBI\n"); ++ return 0; ++ } ++ /* someone else is using this stream already */ ++ IVTV_DEBUG_INFO("Stream %d is busy\n", type); ++ return -EBUSY; ++ } ++ s->fh = &id->fh; ++ if (type == IVTV_DEC_STREAM_TYPE_VBI) { ++ /* Enable reinsertion interrupt */ ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); ++ } ++ ++ /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI, ++ IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI ++ (provided VBI insertion is on and sliced VBI is selected), for all ++ other streams we're done */ ++ if (type == IVTV_DEC_STREAM_TYPE_MPG) { ++ vbi_type = IVTV_DEC_STREAM_TYPE_VBI; ++ } else if (type == IVTV_ENC_STREAM_TYPE_MPG && ++ itv->vbi.insert_mpeg && !ivtv_raw_vbi(itv)) { ++ vbi_type = IVTV_ENC_STREAM_TYPE_VBI; ++ } else { ++ return 0; ++ } ++ s_vbi = &itv->streams[vbi_type]; ++ ++ if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) { ++ /* Enable reinsertion interrupt */ ++ if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI) ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); ++ } ++ /* mark that it is used internally */ ++ set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags); ++ return 0; ++} ++EXPORT_SYMBOL(ivtv_claim_stream); ++ ++/* This function releases a previously claimed stream. It will take into ++ account associated VBI streams. */ ++void ivtv_release_stream(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ struct ivtv_stream *s_vbi; ++ ++ s->fh = NULL; ++ if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) && ++ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { ++ /* this stream is still in use internally */ ++ return; ++ } ++ if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { ++ IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name); ++ return; ++ } ++ ++ ivtv_flush_queues(s); ++ ++ /* disable reinsertion interrupt */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) ++ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); ++ ++ /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI, ++ IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI, ++ for all other streams we're done */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) ++ s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; ++ else if (s->type == IVTV_ENC_STREAM_TYPE_MPG) ++ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ else ++ return; ++ ++ /* clear internal use flag */ ++ if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) { ++ /* was already cleared */ ++ return; ++ } ++ if (s_vbi->fh) { ++ /* VBI stream still claimed by a file descriptor */ ++ return; ++ } ++ /* disable reinsertion interrupt */ ++ if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI) ++ ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); ++ clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags); ++ ivtv_flush_queues(s_vbi); ++} ++EXPORT_SYMBOL(ivtv_release_stream); ++ ++static void ivtv_dualwatch(struct ivtv *itv) ++{ ++ struct v4l2_tuner vt; ++ u32 new_stereo_mode; ++ const u32 dual = 0x02; ++ ++ new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode); ++ memset(&vt, 0, sizeof(vt)); ++ ivtv_call_all(itv, tuner, g_tuner, &vt); ++ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) ++ new_stereo_mode = dual; ++ ++ if (new_stereo_mode == itv->dualwatch_stereo_mode) ++ return; ++ ++ IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n", ++ itv->dualwatch_stereo_mode, new_stereo_mode); ++ if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode)) ++ IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); ++} ++ ++static void ivtv_update_pgm_info(struct ivtv *itv) ++{ ++ u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24; ++ int cnt; ++ int i = 0; ++ ++ if (wr_idx >= itv->pgm_info_num) { ++ IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num); ++ return; ++ } ++ cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num; ++ while (i < cnt) { ++ int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; ++ struct v4l2_enc_idx_entry *e = itv->pgm_info + idx; ++ u32 addr = itv->pgm_info_offset + 4 + idx * 24; ++ const int mapping[8] = { -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, -1, ++ V4L2_ENC_IDX_FRAME_B, -1, -1, -1 }; ++ // 1=I, 2=P, 4=B ++ ++ e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); ++ if (e->offset > itv->mpg_data_received) { ++ break; ++ } ++ e->offset += itv->vbi_data_inserted; ++ e->length = read_enc(addr); ++ e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); ++ e->flags = mapping[read_enc(addr + 12) & 7]; ++ i++; ++ } ++ itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; ++} ++ ++static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) ++{ ++ struct ivtv *itv = s->itv; ++ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ struct ivtv_buffer *buf; ++ DEFINE_WAIT(wait); ++ ++ *err = 0; ++ while (1) { ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) { ++ /* Process pending program info updates and pending VBI data */ ++ ivtv_update_pgm_info(itv); ++ ++ if (time_after(jiffies, ++ itv->dualwatch_jiffies + ++ msecs_to_jiffies(1000))) { ++ itv->dualwatch_jiffies = jiffies; ++ ivtv_dualwatch(itv); ++ } ++ ++ if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && ++ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { ++ while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) { ++ /* byteswap and process VBI data */ ++ ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type); ++ ivtv_enqueue(s_vbi, buf, &s_vbi->q_free); ++ } ++ } ++ buf = &itv->vbi.sliced_mpeg_buf; ++ if (buf->readpos != buf->bytesused) { ++ return buf; ++ } ++ } ++ ++ /* do we have leftover data? */ ++ buf = ivtv_dequeue(s, &s->q_io); ++ if (buf) ++ return buf; ++ ++ /* do we have new data? */ ++ buf = ivtv_dequeue(s, &s->q_full); ++ if (buf) { ++ if ((buf->b_flags & IVTV_F_B_NEED_BUF_SWAP) == 0) ++ return buf; ++ buf->b_flags &= ~IVTV_F_B_NEED_BUF_SWAP; ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) ++ /* byteswap MPG data */ ++ ivtv_buf_swap(buf); ++ else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) { ++ /* byteswap and process VBI data */ ++ ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type); ++ } ++ return buf; ++ } ++ ++ /* return if end of stream */ ++ if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ IVTV_DEBUG_INFO("EOS %s\n", s->name); ++ return NULL; ++ } ++ ++ /* return if file was opened with O_NONBLOCK */ ++ if (non_block) { ++ *err = -EAGAIN; ++ return NULL; ++ } ++ ++ /* wait for more data to arrive */ ++ mutex_unlock(&itv->serialize_lock); ++ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); ++ /* New buffers might have become available before we were added to the waitqueue */ ++ if (!s->q_full.buffers) ++ schedule(); ++ finish_wait(&s->waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ if (signal_pending(current)) { ++ /* return if a signal was received */ ++ IVTV_DEBUG_INFO("User stopped %s\n", s->name); ++ *err = -EINTR; ++ return NULL; ++ } ++ } ++} ++ ++static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv) ++{ ++ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; ++ ++ itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx]; ++ itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx]; ++ itv->vbi.sliced_mpeg_buf.readpos = 0; ++} ++ ++static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf, ++ char __user *ubuf, size_t ucount) ++{ ++ struct ivtv *itv = s->itv; ++ size_t len = buf->bytesused - buf->readpos; ++ ++ if (len > ucount) len = ucount; ++ if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG && ++ !ivtv_raw_vbi(itv) && buf != &itv->vbi.sliced_mpeg_buf) { ++ const char *start = buf->buf + buf->readpos; ++ const char *p = start + 1; ++ const u8 *q; ++ u8 ch = itv->search_pack_header ? 0xba : 0xe0; ++ int stuffing, i; ++ ++ while (start + len > p && (q = memchr(p, 0, start + len - p))) { ++ p = q + 1; ++ if ((char *)q + 15 >= buf->buf + buf->bytesused || ++ q[1] != 0 || q[2] != 1 || q[3] != ch) { ++ continue; ++ } ++ if (!itv->search_pack_header) { ++ if ((q[6] & 0xc0) != 0x80) ++ continue; ++ if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) || ++ ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) { ++ ch = 0xba; ++ itv->search_pack_header = 1; ++ p = q + 9; ++ } ++ continue; ++ } ++ stuffing = q[13] & 7; ++ /* all stuffing bytes must be 0xff */ ++ for (i = 0; i < stuffing; i++) ++ if (q[14 + i] != 0xff) ++ break; ++ if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 && ++ q[14 + stuffing] == 0 && q[15 + stuffing] == 0 && ++ q[16 + stuffing] == 1) { ++ itv->search_pack_header = 0; ++ len = (char *)q - start; ++ ivtv_setup_sliced_vbi_buf(itv); ++ break; ++ } ++ } ++ } ++ if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { ++ IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); ++ return -EFAULT; ++ } ++ /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount, ++ buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len, ++ buf == &itv->vbi.sliced_mpeg_buf); */ ++ buf->readpos += len; ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf) ++ itv->mpg_data_received += len; ++ return len; ++} ++ ++static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block) ++{ ++ struct ivtv *itv = s->itv; ++ size_t tot_written = 0; ++ int single_frame = 0; ++ ++ if (atomic_read(&itv->capturing) == 0 && s->fh == NULL) { ++ /* shouldn't happen */ ++ IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name); ++ return -EIO; ++ } ++ ++ /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should ++ arrive one-by-one, so make sure we never output more than one VBI frame at a time */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI || ++ (s->type == IVTV_ENC_STREAM_TYPE_VBI && !ivtv_raw_vbi(itv))) ++ single_frame = 1; ++ ++ for (;;) { ++ struct ivtv_buffer *buf; ++ int rc; ++ ++ buf = ivtv_get_buffer(s, non_block, &rc); ++ /* if there is no data available... */ ++ if (buf == NULL) { ++ /* if we got data, then return that regardless */ ++ if (tot_written) ++ break; ++ /* EOS condition */ ++ if (rc == 0) { ++ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ ivtv_release_stream(s); ++ } ++ /* set errno */ ++ return rc; ++ } ++ rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); ++ if (buf != &itv->vbi.sliced_mpeg_buf) { ++ ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io); ++ } ++ else if (buf->readpos == buf->bytesused) { ++ int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; ++ itv->vbi.sliced_mpeg_size[idx] = 0; ++ itv->vbi.inserted_frame++; ++ itv->vbi_data_inserted += buf->bytesused; ++ } ++ if (rc < 0) ++ return rc; ++ tot_written += rc; ++ ++ if (tot_written == tot_count || single_frame) ++ break; ++ } ++ return tot_written; ++} ++ ++static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count, ++ loff_t *pos, int non_block) ++{ ++ ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0; ++ struct ivtv *itv = s->itv; ++ ++ IVTV_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); ++ if (rc > 0) ++ pos += rc; ++ return rc; ++} ++ ++int ivtv_start_capture(struct ivtv_open_id *id) ++{ ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ struct ivtv_stream *s_vbi; ++ ++ if (s->type == IVTV_ENC_STREAM_TYPE_RAD || ++ s->type == IVTV_DEC_STREAM_TYPE_MPG || ++ s->type == IVTV_DEC_STREAM_TYPE_YUV || ++ s->type == IVTV_DEC_STREAM_TYPE_VOUT) { ++ /* you cannot read from these stream types. */ ++ return -EINVAL; ++ } ++ ++ /* Try to claim this stream. */ ++ if (ivtv_claim_stream(id, s->type)) ++ return -EBUSY; ++ ++ /* This stream does not need to start capturing */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { ++ set_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ return 0; ++ } ++ ++ /* If capture is already in progress, then we also have to ++ do nothing extra. */ ++ if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ set_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ return 0; ++ } ++ ++ /* Start VBI capture if required */ ++ s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && ++ test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && ++ !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { ++ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed ++ automatically when the MPG stream is claimed. ++ We only need to start the VBI capturing. */ ++ if (ivtv_start_v4l2_encode_stream(s_vbi)) { ++ IVTV_DEBUG_WARN("VBI capture start failed\n"); ++ ++ /* Failure, clean up and return an error */ ++ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ /* also releases the associated VBI stream */ ++ ivtv_release_stream(s); ++ return -EIO; ++ } ++ IVTV_DEBUG_INFO("VBI insertion started\n"); ++ } ++ ++ /* Tell the card to start capturing */ ++ if (!ivtv_start_v4l2_encode_stream(s)) { ++ /* We're done */ ++ set_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ /* Resume a possibly paused encoder */ ++ if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) ++ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); ++ return 0; ++ } ++ ++ /* failure, clean up */ ++ IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); ++ ++ /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released ++ automatically when the MPG stream is released. ++ We only need to stop the VBI capturing. */ ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && ++ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { ++ ivtv_stop_v4l2_encode_stream(s_vbi, 0); ++ clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); ++ } ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ ivtv_release_stream(s); ++ return -EIO; ++} ++ ++ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) ++{ ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ ssize_t rc; ++ ++ IVTV_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name); ++ ++ if (mutex_lock_interruptible(&itv->serialize_lock)) ++ return -ERESTARTSYS; ++ rc = ivtv_start_capture(id); ++ if (!rc) ++ rc = ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); ++ mutex_unlock(&itv->serialize_lock); ++ return rc; ++} ++ ++int ivtv_start_decoding(struct ivtv_open_id *id, int speed) ++{ ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ int rc; ++ ++ if (atomic_read(&itv->decoding) == 0) { ++ if (ivtv_claim_stream(id, s->type)) { ++ /* someone else is using this stream already */ ++ IVTV_DEBUG_WARN("start decode, stream already claimed\n"); ++ return -EBUSY; ++ } ++ rc = ivtv_start_v4l2_decode_stream(s, 0); ++ if (rc < 0) { ++ if (rc == -EAGAIN) ++ rc = ivtv_start_v4l2_decode_stream(s, 0); ++ if (rc < 0) ++ return rc; ++ } ++ } ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) ++ return ivtv_set_speed(itv, speed); ++ return 0; ++} ++ ++static ssize_t ivtv_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) ++{ ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ struct ivtv_buffer *buf; ++ struct ivtv_queue q; ++ int bytes_written = 0; ++ int mode; ++ int rc; ++ DEFINE_WAIT(wait); ++ ++ IVTV_DEBUG_HI_FILE("write %zd bytes to %s\n", count, s->name); ++ ++ if (s->type != IVTV_DEC_STREAM_TYPE_MPG && ++ s->type != IVTV_DEC_STREAM_TYPE_YUV && ++ s->type != IVTV_DEC_STREAM_TYPE_VOUT) ++ /* not decoder streams */ ++ return -EINVAL; ++ ++ /* Try to claim this stream */ ++ if (ivtv_claim_stream(id, s->type)) ++ return -EBUSY; ++ ++ /* This stream does not need to start any decoding */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) { ++ int elems = count / sizeof(struct v4l2_sliced_vbi_data); ++ ++ set_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ return ivtv_write_vbi_from_user(itv, ++ (const struct v4l2_sliced_vbi_data __user *)user_buf, elems); ++ } ++ ++ mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV; ++ ++ if (ivtv_set_output_mode(itv, mode) != mode) { ++ ivtv_release_stream(s); ++ return -EBUSY; ++ } ++ ivtv_queue_init(&q); ++ set_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ ++ /* Start decoder (returns 0 if already started) */ ++ rc = ivtv_start_decoding(id, itv->speed); ++ if (rc) { ++ IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); ++ ++ /* failure, clean up */ ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ return rc; ++ } ++ ++retry: ++ /* If possible, just DMA the entire frame - Check the data transfer size ++ since we may get here before the stream has been fully set-up */ ++ if (mode == OUT_YUV && s->q_full.length == 0 && itv->dma_data_req_size) { ++ while (count >= itv->dma_data_req_size) { ++ rc = ivtv_yuv_udma_stream_frame(itv, (void __user *)user_buf); ++ ++ if (rc < 0) ++ return rc; ++ ++ bytes_written += itv->dma_data_req_size; ++ user_buf += itv->dma_data_req_size; ++ count -= itv->dma_data_req_size; ++ } ++ if (count == 0) { ++ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); ++ return bytes_written; ++ } ++ } ++ ++ for (;;) { ++ /* Gather buffers */ ++ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io))) ++ ivtv_enqueue(s, buf, &q); ++ while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) { ++ ivtv_enqueue(s, buf, &q); ++ } ++ if (q.buffers) ++ break; ++ if (filp->f_flags & O_NONBLOCK) ++ return -EAGAIN; ++ mutex_unlock(&itv->serialize_lock); ++ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); ++ /* New buffers might have become free before we were added to the waitqueue */ ++ if (!s->q_free.buffers) ++ schedule(); ++ finish_wait(&s->waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ if (signal_pending(current)) { ++ IVTV_DEBUG_INFO("User stopped %s\n", s->name); ++ return -EINTR; ++ } ++ } ++ ++ /* copy user data into buffers */ ++ while ((buf = ivtv_dequeue(s, &q))) { ++ /* yuv is a pain. Don't copy more data than needed for a single ++ frame, otherwise we lose sync with the incoming stream */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && ++ yi->stream_size + count > itv->dma_data_req_size) ++ rc = ivtv_buf_copy_from_user(s, buf, user_buf, ++ itv->dma_data_req_size - yi->stream_size); ++ else ++ rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); ++ ++ /* Make sure we really got all the user data */ ++ if (rc < 0) { ++ ivtv_queue_move(s, &q, NULL, &s->q_free, 0); ++ return rc; ++ } ++ user_buf += rc; ++ count -= rc; ++ bytes_written += rc; ++ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { ++ yi->stream_size += rc; ++ /* If we have a complete yuv frame, break loop now */ ++ if (yi->stream_size == itv->dma_data_req_size) { ++ ivtv_enqueue(s, buf, &s->q_full); ++ yi->stream_size = 0; ++ break; ++ } ++ } ++ ++ if (buf->bytesused != s->buf_size) { ++ /* incomplete, leave in q_io for next time */ ++ ivtv_enqueue(s, buf, &s->q_io); ++ break; ++ } ++ /* Byteswap MPEG buffer */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) ++ ivtv_buf_swap(buf); ++ ivtv_enqueue(s, buf, &s->q_full); ++ } ++ ++ if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { ++ if (s->q_full.length >= itv->dma_data_req_size) { ++ int got_sig; ++ ++ if (mode == OUT_YUV) ++ ivtv_yuv_setup_stream_frame(itv); ++ ++ mutex_unlock(&itv->serialize_lock); ++ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); ++ while (!(got_sig = signal_pending(current)) && ++ test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { ++ schedule(); ++ } ++ finish_wait(&itv->dma_waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ if (got_sig) { ++ IVTV_DEBUG_INFO("User interrupted %s\n", s->name); ++ return -EINTR; ++ } ++ ++ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); ++ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); ++ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1); ++ } ++ } ++ /* more user data is available, wait until buffers become free ++ to transfer the rest. */ ++ if (count && !(filp->f_flags & O_NONBLOCK)) ++ goto retry; ++ IVTV_DEBUG_HI_FILE("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); ++ return bytes_written; ++} ++ ++ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) ++{ ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ ssize_t res; ++ ++ if (mutex_lock_interruptible(&itv->serialize_lock)) ++ return -ERESTARTSYS; ++ res = ivtv_write(filp, user_buf, count, pos); ++ mutex_unlock(&itv->serialize_lock); ++ return res; ++} ++ ++unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) ++{ ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ int res = 0; ++ ++ /* add stream's waitq to the poll list */ ++ IVTV_DEBUG_HI_FILE("Decoder poll\n"); ++ ++ /* If there are subscribed events, then only use the new event ++ API instead of the old video.h based API. */ ++ if (!list_empty(&id->fh.subscribed)) { ++ poll_wait(filp, &id->fh.wait, wait); ++ /* Turn off the old-style vsync events */ ++ clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); ++ if (v4l2_event_pending(&id->fh)) ++ res = POLLPRI; ++ } else { ++ /* This is the old-style API which is here only for backwards ++ compatibility. */ ++ poll_wait(filp, &s->waitq, wait); ++ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); ++ if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || ++ test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) ++ res = POLLPRI; ++ } ++ ++ /* Allow write if buffers are available for writing */ ++ if (s->q_free.buffers) ++ res |= POLLOUT | POLLWRNORM; ++ return res; ++} ++ ++unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait) ++{ ++ unsigned long req_events = poll_requested_events(wait); ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ unsigned res = 0; ++ ++ /* Start a capture if there is none */ ++ if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) && ++ s->type != IVTV_ENC_STREAM_TYPE_RAD && ++ (req_events & (POLLIN | POLLRDNORM))) { ++ int rc; ++ ++ mutex_lock(&itv->serialize_lock); ++ rc = ivtv_start_capture(id); ++ mutex_unlock(&itv->serialize_lock); ++ if (rc) { ++ IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n", ++ s->name, rc); ++ return POLLERR; ++ } ++ IVTV_DEBUG_FILE("Encoder poll started capture\n"); ++ } ++ ++ /* add stream's waitq to the poll list */ ++ IVTV_DEBUG_HI_FILE("Encoder poll\n"); ++ poll_wait(filp, &s->waitq, wait); ++ if (v4l2_event_pending(&id->fh)) ++ res |= POLLPRI; ++ else ++ poll_wait(filp, &id->fh.wait, wait); ++ ++ if (s->q_full.length || s->q_io.length) ++ return res | POLLIN | POLLRDNORM; ++ if (eof) ++ return res | POLLHUP; ++ return res; ++} ++ ++void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end) ++{ ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ ++ IVTV_DEBUG_FILE("close() of %s\n", s->name); ++ ++ /* 'Unclaim' this stream */ ++ ++ /* Stop capturing */ ++ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ ++ IVTV_DEBUG_INFO("close stopping capture\n"); ++ /* Special case: a running VBI capture for VBI insertion ++ in the mpeg stream. Need to stop that too. */ ++ if (id->type == IVTV_ENC_STREAM_TYPE_MPG && ++ test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) && ++ !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { ++ IVTV_DEBUG_INFO("close stopping embedded VBI capture\n"); ++ ivtv_stop_v4l2_encode_stream(s_vbi, 0); ++ } ++ if ((id->type == IVTV_DEC_STREAM_TYPE_VBI || ++ id->type == IVTV_ENC_STREAM_TYPE_VBI) && ++ test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { ++ /* Also used internally, don't stop capturing */ ++ s->fh = NULL; ++ } ++ else { ++ ivtv_stop_v4l2_encode_stream(s, gop_end); ++ } ++ } ++ if (!gop_end) { ++ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ ivtv_release_stream(s); ++ } ++} ++ ++static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) ++{ ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ ++ IVTV_DEBUG_FILE("close() of %s\n", s->name); ++ ++ if (id->type == IVTV_DEC_STREAM_TYPE_YUV && ++ test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) { ++ /* Restore registers we've changed & clean up any mess */ ++ ivtv_yuv_close(itv); ++ } ++ ++ /* Stop decoding */ ++ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ IVTV_DEBUG_INFO("close stopping decode\n"); ++ ++ ivtv_stop_v4l2_decode_stream(s, flags, pts); ++ itv->output_mode = OUT_NONE; ++ } ++ clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); ++ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ ++ if (itv->output_mode == OUT_UDMA_YUV && id->yuv_frames) ++ itv->output_mode = OUT_NONE; ++ ++ itv->speed = 0; ++ clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags); ++ ivtv_release_stream(s); ++} ++ ++int ivtv_v4l2_close(struct file *filp) ++{ ++ struct v4l2_fh *fh = filp->private_data; ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ ++ IVTV_DEBUG_FILE("close %s\n", s->name); ++ ++ mutex_lock(&itv->serialize_lock); ++ ++ /* Stop radio */ ++ if (id->type == IVTV_ENC_STREAM_TYPE_RAD && ++ v4l2_fh_is_singular_file(filp)) { ++ /* Closing radio device, return to TV mode */ ++ ivtv_mute(itv); ++ /* Mark that the radio is no longer in use */ ++ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); ++ /* Switch tuner to TV */ ++ ivtv_call_all(itv, core, s_std, itv->std); ++ /* Select correct audio input (i.e. TV tuner or Line in) */ ++ ivtv_audio_set_io(itv); ++ if (itv->hw_flags & IVTV_HW_SAA711X) { ++ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, ++ SAA7115_FREQ_32_11_MHZ, 0); ++ } ++ if (atomic_read(&itv->capturing) > 0) { ++ /* Undo video mute */ ++ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, ++ v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) | ++ (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); ++ } ++ /* Done! Unmute and continue. */ ++ ivtv_unmute(itv); ++ } ++ ++ v4l2_fh_del(fh); ++ v4l2_fh_exit(fh); ++ ++ /* Easy case first: this stream was never claimed by us */ ++ if (s->fh != &id->fh) ++ goto close_done; ++ ++ /* 'Unclaim' this stream */ ++ ++ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { ++ struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT]; ++ ++ ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0); ++ ++ /* If all output streams are closed, and if the user doesn't have ++ IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */ ++ if (itv->output_mode == OUT_NONE && !test_bit(IVTV_F_S_APPL_IO, &s_vout->s_flags)) { ++ /* disable CC on TV-out */ ++ ivtv_disable_cc(itv); ++ } ++ } else { ++ ivtv_stop_capture(id, 0); ++ } ++close_done: ++ kfree(id); ++ mutex_unlock(&itv->serialize_lock); ++ return 0; ++} ++ ++static int ivtv_open(struct file *filp) ++{ ++ struct video_device *vdev = video_devdata(filp); ++ struct ivtv_stream *s = video_get_drvdata(vdev); ++ struct ivtv *itv = s->itv; ++ struct ivtv_open_id *item; ++ int res = 0; ++ ++ IVTV_DEBUG_FILE("open %s\n", s->name); ++ ++ if (ivtv_init_on_first_open(itv)) { ++ IVTV_ERR("Failed to initialize on device %s\n", ++ video_device_node_name(vdev)); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ /* Unless ivtv_fw_debug is set, error out if firmware dead. */ ++ if (ivtv_fw_debug) { ++ IVTV_WARN("Opening %s with dead firmware lockout disabled\n", ++ video_device_node_name(vdev)); ++ IVTV_WARN("Selected firmware errors will be ignored\n"); ++ } else { ++#else ++ if (1) { ++#endif ++ res = ivtv_firmware_check(itv, "ivtv_serialized_open"); ++ if (res == -EAGAIN) ++ res = ivtv_firmware_check(itv, "ivtv_serialized_open"); ++ if (res < 0) ++ return -EIO; ++ } ++ ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG && ++ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) ++ return -EBUSY; ++ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && ++ test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags)) ++ return -EBUSY; ++ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { ++ if (read_reg(0x82c) == 0) { ++ IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n"); ++ /* return -ENODEV; */ ++ } ++ ivtv_udma_alloc(itv); ++ } ++ ++ /* Allocate memory */ ++ item = kzalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); ++ if (NULL == item) { ++ IVTV_DEBUG_WARN("nomem on v4l2 open\n"); ++ return -ENOMEM; ++ } ++ v4l2_fh_init(&item->fh, s->vdev); ++ item->itv = itv; ++ item->type = s->type; ++ ++ filp->private_data = &item->fh; ++ v4l2_fh_add(&item->fh); ++ ++ if (item->type == IVTV_ENC_STREAM_TYPE_RAD && ++ v4l2_fh_is_singular_file(filp)) { ++ if (!test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { ++ if (atomic_read(&itv->capturing) > 0) { ++ /* switching to radio while capture is ++ in progress is not polite */ ++ v4l2_fh_del(&item->fh); ++ v4l2_fh_exit(&item->fh); ++ kfree(item); ++ return -EBUSY; ++ } ++ } ++ /* Mark that the radio is being used. */ ++ set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); ++ /* We have the radio */ ++ ivtv_mute(itv); ++ /* Switch tuner to radio */ ++ ivtv_call_all(itv, tuner, s_radio); ++ /* Select the correct audio input (i.e. radio tuner) */ ++ ivtv_audio_set_io(itv); ++ if (itv->hw_flags & IVTV_HW_SAA711X) { ++ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, ++ SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL); ++ } ++ /* Done! Unmute and continue. */ ++ ivtv_unmute(itv); ++ } ++ ++ /* YUV or MPG Decoding Mode? */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) { ++ clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); ++ } else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { ++ set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); ++ /* For yuv, we need to know the dma size before we start */ ++ itv->dma_data_req_size = ++ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); ++ itv->yuv_info.stream_size = 0; ++ } ++ return 0; ++} ++ ++int ivtv_v4l2_open(struct file *filp) ++{ ++ struct video_device *vdev = video_devdata(filp); ++ int res; ++ ++ if (mutex_lock_interruptible(vdev->lock)) ++ return -ERESTARTSYS; ++ res = ivtv_open(filp); ++ mutex_unlock(vdev->lock); ++ return res; ++} ++ ++void ivtv_mute(struct ivtv *itv) ++{ ++ if (atomic_read(&itv->capturing)) ++ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1); ++ IVTV_DEBUG_INFO("Mute\n"); ++} ++ ++void ivtv_unmute(struct ivtv *itv) ++{ ++ if (atomic_read(&itv->capturing)) { ++ ivtv_msleep_timeout(100, 0); ++ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); ++ ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0); ++ } ++ IVTV_DEBUG_INFO("Unmute\n"); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-fileops.h b/drivers/media/pci/ivtv/ivtv-fileops.h +new file mode 100644 +index 0000000..5e08800 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-fileops.h +@@ -0,0 +1,44 @@ ++/* ++ file operation functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_FILEOPS_H ++#define IVTV_FILEOPS_H ++ ++/* Testing/Debugging */ ++int ivtv_v4l2_open(struct file *filp); ++ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count, ++ loff_t * pos); ++ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count, ++ loff_t * pos); ++int ivtv_v4l2_close(struct file *filp); ++unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait); ++unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait); ++int ivtv_start_capture(struct ivtv_open_id *id); ++void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end); ++int ivtv_start_decoding(struct ivtv_open_id *id, int speed); ++void ivtv_mute(struct ivtv *itv); ++void ivtv_unmute(struct ivtv *itv); ++ ++/* Utilities */ ++/* Shared with ivtv-alsa module */ ++int ivtv_claim_stream(struct ivtv_open_id *id, int type); ++void ivtv_release_stream(struct ivtv_stream *s); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-firmware.c b/drivers/media/pci/ivtv/ivtv-firmware.c +new file mode 100644 +index 0000000..68387d4 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-firmware.c +@@ -0,0 +1,402 @@ ++/* ++ ivtv firmware functions. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-firmware.h" ++#include "ivtv-yuv.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-cards.h" ++#include ++#include ++ ++#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE ++#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 ++#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB ++#define IVTV_CMD_VDM_STOP 0x00000000 ++#define IVTV_CMD_AO_STOP 0x00000005 ++#define IVTV_CMD_APU_PING 0x00000000 ++#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE ++#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE ++#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF ++#define IVTV_CMD_SPU_STOP 0x00000001 ++#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A ++#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 ++#define IVTV_SDRAM_SLEEPTIME 600 ++ ++#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" ++#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) ++ ++/* Encoder/decoder firmware sizes */ ++#define IVTV_FW_ENC_SIZE (376836) ++#define IVTV_FW_DEC_SIZE (256*1024) ++ ++static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) ++{ ++ const struct firmware *fw = NULL; ++ int retries = 3; ++ ++retry: ++ if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) { ++ int i; ++ volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; ++ const u32 *src = (const u32 *)fw->data; ++ ++ if (fw->size != size) { ++ /* Due to race conditions in firmware loading (esp. with udev <0.95) ++ the wrong file was sometimes loaded. So we check filesizes to ++ see if at least the right-sized file was loaded. If not, then we ++ retry. */ ++ IVTV_INFO("Retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); ++ release_firmware(fw); ++ retries--; ++ goto retry; ++ } ++ for (i = 0; i < fw->size; i += 4) { ++ /* no need for endianness conversion on the ppc */ ++ __raw_writel(*src, dst); ++ dst++; ++ src++; ++ } ++ IVTV_INFO("Loaded %s firmware (%zd bytes)\n", fn, fw->size); ++ release_firmware(fw); ++ return size; ++ } ++ IVTV_ERR("Unable to open firmware %s (must be %ld bytes)\n", fn, size); ++ IVTV_ERR("Did you put the firmware in the hotplug firmware directory?\n"); ++ return -ENOMEM; ++} ++ ++void ivtv_halt_firmware(struct ivtv *itv) ++{ ++ IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); ++ if (itv->has_cx23415 && itv->dec_mbox.mbox) ++ ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); ++ if (itv->enc_mbox.mbox) ++ ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); ++ ++ ivtv_msleep_timeout(10, 0); ++ itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; ++ ++ IVTV_DEBUG_INFO("Stopping VDM\n"); ++ write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); ++ ++ IVTV_DEBUG_INFO("Stopping AO\n"); ++ write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); ++ ++ IVTV_DEBUG_INFO("pinging (?) APU\n"); ++ write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); ++ ++ IVTV_DEBUG_INFO("Stopping VPU\n"); ++ if (!itv->has_cx23415) ++ write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); ++ else ++ write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); ++ ++ IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); ++ write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); ++ ++ IVTV_DEBUG_INFO("Stopping SPU\n"); ++ write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); ++ ++ ivtv_msleep_timeout(10, 0); ++ ++ IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); ++ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); ++ ++ IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); ++ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); ++ ++ if (itv->has_cx23415) { ++ IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); ++ write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); ++ ++ IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); ++ write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); ++ } ++ ++ IVTV_DEBUG_INFO("Sleeping for %dms\n", IVTV_SDRAM_SLEEPTIME); ++ ivtv_msleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); ++} ++ ++void ivtv_firmware_versions(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ ++ /* Encoder */ ++ ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); ++ IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); ++ ++ if (data[0] != 0x02060039) ++ IVTV_WARN("Recommended firmware version is 0x02060039.\n"); ++ ++ if (itv->has_cx23415) { ++ /* Decoder */ ++ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); ++ IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); ++ } ++} ++ ++static int ivtv_firmware_copy(struct ivtv *itv) ++{ ++ IVTV_DEBUG_INFO("Loading encoder image\n"); ++ if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, ++ itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { ++ IVTV_DEBUG_WARN("failed loading encoder firmware\n"); ++ return -3; ++ } ++ if (!itv->has_cx23415) ++ return 0; ++ ++ IVTV_DEBUG_INFO("Loading decoder image\n"); ++ if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, ++ itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { ++ IVTV_DEBUG_WARN("failed loading decoder firmware\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) ++{ ++ int i; ++ ++ /* mailbox is preceded by a 16 byte 'magic cookie' starting at a 256-byte ++ address boundary */ ++ for (i = 0; i < size; i += 0x100) { ++ if (readl(mem + i) == 0x12345678 && ++ readl(mem + i + 4) == 0x34567812 && ++ readl(mem + i + 8) == 0x56781234 && ++ readl(mem + i + 12) == 0x78123456) { ++ return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); ++ } ++ } ++ return NULL; ++} ++ ++int ivtv_firmware_init(struct ivtv *itv) ++{ ++ int err; ++ ++ ivtv_halt_firmware(itv); ++ ++ /* load firmware */ ++ err = ivtv_firmware_copy(itv); ++ if (err) { ++ IVTV_DEBUG_WARN("Error %d loading firmware\n", err); ++ return err; ++ } ++ ++ /* start firmware */ ++ write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); ++ ivtv_msleep_timeout(100, 0); ++ if (itv->has_cx23415) ++ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); ++ else ++ write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); ++ ivtv_msleep_timeout(100, 0); ++ ++ /* find mailboxes and ping firmware */ ++ itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); ++ if (itv->enc_mbox.mbox == NULL) ++ IVTV_ERR("Encoder mailbox not found\n"); ++ else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { ++ IVTV_ERR("Encoder firmware dead!\n"); ++ itv->enc_mbox.mbox = NULL; ++ } ++ if (itv->enc_mbox.mbox == NULL) ++ return -ENODEV; ++ ++ if (!itv->has_cx23415) ++ return 0; ++ ++ itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); ++ if (itv->dec_mbox.mbox == NULL) { ++ IVTV_ERR("Decoder mailbox not found\n"); ++ } else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { ++ IVTV_ERR("Decoder firmware dead!\n"); ++ itv->dec_mbox.mbox = NULL; ++ } else { ++ /* Firmware okay, so check yuv output filter table */ ++ ivtv_yuv_filter_check(itv); ++ } ++ return itv->dec_mbox.mbox ? 0 : -ENODEV; ++} ++ ++void ivtv_init_mpeg_decoder(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ long readbytes; ++ volatile u8 __iomem *mem_offset; ++ ++ data[0] = 0; ++ data[1] = itv->cxhdl.width; /* YUV source width */ ++ data[2] = itv->cxhdl.height; ++ data[3] = itv->cxhdl.audio_properties; /* Audio settings to use, ++ bitmap. see docs. */ ++ if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { ++ IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); ++ return; ++ } ++ ++ if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { ++ IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); ++ return; ++ } ++ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); ++ mem_offset = itv->dec_mem + data[1]; ++ ++ if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, ++ mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { ++ IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", ++ IVTV_DECODE_INIT_MPEG_FILENAME); ++ } else { ++ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); ++ ivtv_msleep_timeout(100, 0); ++ } ++ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); ++} ++ ++/* Try to restart the card & restore previous settings */ ++static int ivtv_firmware_restart(struct ivtv *itv) ++{ ++ int rc = 0; ++ v4l2_std_id std; ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) ++ /* Display test image during restart */ ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, ++ SAA7127_INPUT_TYPE_TEST_IMAGE, ++ itv->card->video_outputs[itv->active_output].video_output, ++ 0); ++ ++ mutex_lock(&itv->udma.lock); ++ ++ rc = ivtv_firmware_init(itv); ++ if (rc) { ++ mutex_unlock(&itv->udma.lock); ++ return rc; ++ } ++ ++ /* Allow settings to reload */ ++ ivtv_mailbox_cache_invalidate(itv); ++ ++ /* Restore encoder video standard */ ++ std = itv->std; ++ itv->std = 0; ++ ivtv_s_std_enc(itv, &std); ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ ivtv_init_mpeg_decoder(itv); ++ ++ /* Restore decoder video standard */ ++ std = itv->std_out; ++ itv->std_out = 0; ++ ivtv_s_std_dec(itv, &std); ++ ++ /* Restore framebuffer if active */ ++ if (itv->ivtvfb_restore) ++ itv->ivtvfb_restore(itv); ++ ++ /* Restore alpha settings */ ++ ivtv_set_osd_alpha(itv); ++ ++ /* Restore normal output */ ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, ++ SAA7127_INPUT_TYPE_NORMAL, ++ itv->card->video_outputs[itv->active_output].video_output, ++ 0); ++ } ++ ++ mutex_unlock(&itv->udma.lock); ++ return rc; ++} ++ ++/* Check firmware running state. The checks fall through ++ allowing multiple failures to be logged. */ ++int ivtv_firmware_check(struct ivtv *itv, char *where) ++{ ++ int res = 0; ++ ++ /* Check encoder is still running */ ++ if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0) < 0) { ++ IVTV_WARN("Encoder has died : %s\n", where); ++ res = -1; ++ } ++ ++ /* Also check audio. Only check if not in use & encoder is okay */ ++ if (!res && !atomic_read(&itv->capturing) && ++ (!atomic_read(&itv->decoding) || ++ (atomic_read(&itv->decoding) < 2 && test_bit(IVTV_F_I_DEC_YUV, ++ &itv->i_flags)))) { ++ ++ if (ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12) < 0) { ++ IVTV_WARN("Audio has died (Encoder OK) : %s\n", where); ++ res = -2; ++ } ++ } ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ /* Second audio check. Skip if audio already failed */ ++ if (res != -2 && read_dec(0x100) != read_dec(0x104)) { ++ /* Wait & try again to be certain. */ ++ ivtv_msleep_timeout(14, 0); ++ if (read_dec(0x100) != read_dec(0x104)) { ++ IVTV_WARN("Audio has died (Decoder) : %s\n", ++ where); ++ res = -1; ++ } ++ } ++ ++ /* Check decoder is still running */ ++ if (ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0) < 0) { ++ IVTV_WARN("Decoder has died : %s\n", where); ++ res = -1; ++ } ++ } ++ ++ /* If something failed & currently idle, try to reload */ ++ if (res && !atomic_read(&itv->capturing) && ++ !atomic_read(&itv->decoding)) { ++ IVTV_INFO("Detected in %s that firmware had failed - " ++ "Reloading\n", where); ++ res = ivtv_firmware_restart(itv); ++ /* ++ * Even if restarted ok, still signal a problem had occurred. ++ * The caller can come through this function again to check ++ * if things are really ok after the restart. ++ */ ++ if (!res) { ++ IVTV_INFO("Firmware restart okay\n"); ++ res = -EAGAIN; ++ } else { ++ IVTV_INFO("Firmware restart failed\n"); ++ } ++ } else if (res) { ++ res = -EIO; ++ } ++ ++ return res; ++} ++ ++MODULE_FIRMWARE(CX2341X_FIRM_ENC_FILENAME); ++MODULE_FIRMWARE(CX2341X_FIRM_DEC_FILENAME); ++MODULE_FIRMWARE(IVTV_DECODE_INIT_MPEG_FILENAME); +diff --git a/drivers/media/pci/ivtv/ivtv-firmware.h b/drivers/media/pci/ivtv/ivtv-firmware.h +new file mode 100644 +index 0000000..52bb4e5 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-firmware.h +@@ -0,0 +1,31 @@ ++/* ++ ivtv firmware functions. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_FIRMWARE_H ++#define IVTV_FIRMWARE_H ++ ++int ivtv_firmware_init(struct ivtv *itv); ++void ivtv_firmware_versions(struct ivtv *itv); ++void ivtv_halt_firmware(struct ivtv *itv); ++void ivtv_init_mpeg_decoder(struct ivtv *itv); ++int ivtv_firmware_check(struct ivtv *itv, char *where); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-gpio.c b/drivers/media/pci/ivtv/ivtv-gpio.c +new file mode 100644 +index 0000000..8f0d077 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-gpio.c +@@ -0,0 +1,374 @@ ++/* ++ gpio functions. ++ Merging GPIO support into driver: ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-cards.h" ++#include "ivtv-gpio.h" ++#include "tuner-xc2028.h" ++#include ++#include ++ ++/* ++ * GPIO assignment of Yuan MPG600/MPG160 ++ * ++ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 ++ * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0 ++ * INPUT DM1 DM0 ++ * ++ * IN* : Input selection ++ * IN1 IN0 ++ * 1 1 N/A ++ * 1 0 Line ++ * 0 1 N/A ++ * 0 0 Tuner ++ * ++ * AM* : Audio Mode ++ * AM3 0: Normal 1: Mixed(Sub+Main channel) ++ * AM2 0: Subchannel 1: Main channel ++ * AM1 0: Stereo 1: Mono ++ * AM0 0: Normal 1: Mute ++ * ++ * DM* : Detected tuner audio Mode ++ * DM1 0: Stereo 1: Mono ++ * DM0 0: Multiplex 1: Normal ++ * ++ * GPIO Initial Settings ++ * MPG600 MPG160 ++ * DIR 0x3080 0x7080 ++ * OUTPUT 0x000C 0x400C ++ * ++ * Special thanks to Makoto Iguchi and Mr. Anonymous ++ * for analyzing GPIO of MPG160. ++ * ++ ***************************************************************************** ++ * ++ * GPIO assignment of Avermedia M179 (per information direct from AVerMedia) ++ * ++ * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 ++ * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1 ++ * INPUT ++ * ++ * IN* : Input selection ++ * IN0 IN1 IN2 ++ * * 1 * Mute ++ * 0 0 0 Line-In ++ * 1 0 0 TV Tuner Audio ++ * 0 0 1 FM Audio ++ * 1 0 1 Mute ++ * ++ * AM* : Audio Mode ++ * AM0 AM1 AM2 ++ * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP ++ * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP) ++ * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo) ++ * 0 1 1 TV Tuner Audio: mute ++ * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono) ++ * ++ * BR* : Audio Sample Rate (BR stands for bitrate for some reason) ++ * BR0 BR1 ++ * 0 0 32 kHz ++ * 0 1 44.1 kHz ++ * 1 0 48 kHz ++ * ++ * DM* : Detected tuner audio Mode ++ * Unknown currently ++ * ++ * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at ++ * AVerMedia for providing the GPIO information used to add support ++ * for the M179 cards. ++ */ ++ ++/********************* GPIO stuffs *********************/ ++ ++/* GPIO registers */ ++#define IVTV_REG_GPIO_IN 0x9008 ++#define IVTV_REG_GPIO_OUT 0x900c ++#define IVTV_REG_GPIO_DIR 0x9020 ++ ++void ivtv_reset_ir_gpio(struct ivtv *itv) ++{ ++ int curdir, curout; ++ ++ if (itv->card->type != IVTV_CARD_PVR_150) ++ return; ++ IVTV_DEBUG_INFO("Resetting PVR150 IR\n"); ++ curout = read_reg(IVTV_REG_GPIO_OUT); ++ curdir = read_reg(IVTV_REG_GPIO_DIR); ++ curdir |= 0x80; ++ write_reg(curdir, IVTV_REG_GPIO_DIR); ++ curout = (curout & ~0xF) | 1; ++ write_reg(curout, IVTV_REG_GPIO_OUT); ++ /* We could use something else for smaller time */ ++ schedule_timeout_interruptible(msecs_to_jiffies(1)); ++ curout |= 2; ++ write_reg(curout, IVTV_REG_GPIO_OUT); ++ curdir &= ~0x80; ++ write_reg(curdir, IVTV_REG_GPIO_DIR); ++} ++ ++/* Xceive tuner reset function */ ++int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value) ++{ ++ struct i2c_algo_bit_data *algo = dev; ++ struct ivtv *itv = algo->data; ++ u32 curout; ++ ++ if (cmd != XC2028_TUNER_RESET) ++ return 0; ++ IVTV_DEBUG_INFO("Resetting tuner\n"); ++ curout = read_reg(IVTV_REG_GPIO_OUT); ++ curout &= ~(1 << itv->card->xceive_pin); ++ write_reg(curout, IVTV_REG_GPIO_OUT); ++ schedule_timeout_interruptible(msecs_to_jiffies(1)); ++ ++ curout |= 1 << itv->card->xceive_pin; ++ write_reg(curout, IVTV_REG_GPIO_OUT); ++ schedule_timeout_interruptible(msecs_to_jiffies(1)); ++ return 0; ++} ++ ++static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct ivtv, sd_gpio); ++} ++ ++static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) ++{ ++ return &container_of(ctrl->handler, struct ivtv, hdl_gpio)->sd_gpio; ++} ++ ++static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ mask = itv->card->gpio_audio_freq.mask; ++ switch (freq) { ++ case 32000: ++ data = itv->card->gpio_audio_freq.f32000; ++ break; ++ case 44100: ++ data = itv->card->gpio_audio_freq.f44100; ++ break; ++ case 48000: ++ default: ++ data = itv->card->gpio_audio_freq.f48000; ++ break; ++ } ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++} ++ ++static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask; ++ ++ mask = itv->card->gpio_audio_detect.mask; ++ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) ++ vt->rxsubchans = V4L2_TUNER_SUB_STEREO | ++ V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ else ++ vt->rxsubchans = V4L2_TUNER_SUB_MONO; ++ return 0; ++} ++ ++static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ mask = itv->card->gpio_audio_mode.mask; ++ switch (vt->audmode) { ++ case V4L2_TUNER_MODE_LANG1: ++ data = itv->card->gpio_audio_mode.lang1; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ data = itv->card->gpio_audio_mode.lang2; ++ break; ++ case V4L2_TUNER_MODE_MONO: ++ data = itv->card->gpio_audio_mode.mono; ++ break; ++ case V4L2_TUNER_MODE_STEREO: ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ default: ++ data = itv->card->gpio_audio_mode.stereo; ++ break; ++ } ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++} ++ ++static int subdev_s_radio(struct v4l2_subdev *sd) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ mask = itv->card->gpio_audio_input.mask; ++ data = itv->card->gpio_audio_input.radio; ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++} ++ ++static int subdev_s_audio_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ if (input > 2) ++ return -EINVAL; ++ mask = itv->card->gpio_audio_input.mask; ++ switch (input) { ++ case 0: ++ data = itv->card->gpio_audio_input.tuner; ++ break; ++ case 1: ++ data = itv->card->gpio_audio_input.linein; ++ break; ++ case 2: ++ default: ++ data = itv->card->gpio_audio_input.radio; ++ break; ++ } ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++} ++ ++static int subdev_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct v4l2_subdev *sd = to_sd(ctrl); ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ mask = itv->card->gpio_audio_mute.mask; ++ data = ctrl->val ? itv->card->gpio_audio_mute.mute : 0; ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | ++ (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++ ++static int subdev_log_status(struct v4l2_subdev *sd) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ ++ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", ++ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), ++ read_reg(IVTV_REG_GPIO_IN)); ++ v4l2_ctrl_handler_log_status(&itv->hdl_gpio, sd->name); ++ return 0; ++} ++ ++static int subdev_s_video_routing(struct v4l2_subdev *sd, ++ u32 input, u32 output, u32 config) ++{ ++ struct ivtv *itv = sd_to_ivtv(sd); ++ u16 mask, data; ++ ++ if (input > 2) /* 0:Tuner 1:Composite 2:S-Video */ ++ return -EINVAL; ++ mask = itv->card->gpio_video_input.mask; ++ if (input == 0) ++ data = itv->card->gpio_video_input.tuner; ++ else if (input == 1) ++ data = itv->card->gpio_video_input.composite; ++ else ++ data = itv->card->gpio_video_input.svideo; ++ if (mask) ++ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops gpio_ctrl_ops = { ++ .s_ctrl = subdev_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_core_ops subdev_core_ops = { ++ .log_status = subdev_log_status, ++ .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, ++ .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, ++ .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, ++ .g_ctrl = v4l2_subdev_g_ctrl, ++ .s_ctrl = v4l2_subdev_s_ctrl, ++ .queryctrl = v4l2_subdev_queryctrl, ++ .querymenu = v4l2_subdev_querymenu, ++}; ++ ++static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { ++ .s_radio = subdev_s_radio, ++ .g_tuner = subdev_g_tuner, ++ .s_tuner = subdev_s_tuner, ++}; ++ ++static const struct v4l2_subdev_audio_ops subdev_audio_ops = { ++ .s_clock_freq = subdev_s_clock_freq, ++ .s_routing = subdev_s_audio_routing, ++}; ++ ++static const struct v4l2_subdev_video_ops subdev_video_ops = { ++ .s_routing = subdev_s_video_routing, ++}; ++ ++static const struct v4l2_subdev_ops subdev_ops = { ++ .core = &subdev_core_ops, ++ .tuner = &subdev_tuner_ops, ++ .audio = &subdev_audio_ops, ++ .video = &subdev_video_ops, ++}; ++ ++int ivtv_gpio_init(struct ivtv *itv) ++{ ++ u16 pin = 0; ++ ++ if (itv->card->xceive_pin) ++ pin = 1 << itv->card->xceive_pin; ++ ++ if ((itv->card->gpio_init.direction | pin) == 0) ++ return 0; ++ ++ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", ++ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); ++ ++ /* init output data then direction */ ++ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); ++ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); ++ v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); ++ snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name); ++ itv->sd_gpio.grp_id = IVTV_HW_GPIO; ++ v4l2_ctrl_handler_init(&itv->hdl_gpio, 1); ++ v4l2_ctrl_new_std(&itv->hdl_gpio, &gpio_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 0); ++ if (itv->hdl_gpio.error) ++ return itv->hdl_gpio.error; ++ itv->sd_gpio.ctrl_handler = &itv->hdl_gpio; ++ v4l2_ctrl_handler_setup(&itv->hdl_gpio); ++ return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-gpio.h b/drivers/media/pci/ivtv/ivtv-gpio.h +new file mode 100644 +index 0000000..0b5d19c +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-gpio.h +@@ -0,0 +1,29 @@ ++/* ++ gpio functions. ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_GPIO_H ++#define IVTV_GPIO_H ++ ++/* GPIO stuff */ ++int ivtv_gpio_init(struct ivtv *itv); ++void ivtv_reset_ir_gpio(struct ivtv *itv); ++int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-i2c.c b/drivers/media/pci/ivtv/ivtv-i2c.c +new file mode 100644 +index 0000000..a181105 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-i2c.c +@@ -0,0 +1,756 @@ ++/* ++ I2C functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* ++ This file includes an i2c implementation that was reverse engineered ++ from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit, ++ which whilst fine under most circumstances, had trouble with the Zilog ++ CPU on the PVR-150 which handles IR functions (occasional inability to ++ communicate with the chip until it was reset) and also with the i2c ++ bus being completely unreachable when multiple PVR cards were present. ++ ++ The implementation is very similar to i2c-algo-bit, but there are enough ++ subtle differences that the two are hard to merge. The general strategy ++ employed by i2c-algo-bit is to use udelay() to implement the timing ++ when putting out bits on the scl/sda lines. The general strategy taken ++ here is to poll the lines for state changes (see ivtv_waitscl and ++ ivtv_waitsda). In addition there are small delays at various locations ++ which poll the SCL line 5 times (ivtv_scldelay). I would guess that ++ since this is memory mapped I/O that the length of those delays is tied ++ to the PCI bus clock. There is some extra code to do with recovery ++ and retries. Since it is not known what causes the actual i2c problems ++ in the first place, the only goal if one was to attempt to use ++ i2c-algo-bit would be to try to make it follow the same code path. ++ This would be a lot of work, and I'm also not convinced that it would ++ provide a generic benefit to i2c-algo-bit. Therefore consider this ++ an engineering solution -- not pretty, but it works. ++ ++ Some more general comments about what we are doing: ++ ++ The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA) ++ lines. To communicate on the bus (as a master, we don't act as a slave), ++ we first initiate a start condition (ivtv_start). We then write the ++ address of the device that we want to communicate with, along with a flag ++ that indicates whether this is a read or a write. The slave then issues ++ an ACK signal (ivtv_ack), which tells us that it is ready for reading / ++ writing. We then proceed with reading or writing (ivtv_read/ivtv_write), ++ and finally issue a stop condition (ivtv_stop) to make the bus available ++ to other masters. ++ ++ There is an additional form of transaction where a write may be ++ immediately followed by a read. In this case, there is no intervening ++ stop condition. (Only the msp3400 chip uses this method of data transfer). ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-cards.h" ++#include "ivtv-gpio.h" ++#include "ivtv-i2c.h" ++#include ++ ++/* i2c implementation for cx23415/6 chip, ivtv project. ++ * Author: Kevin Thayer (nufan_wfk at yahoo.com) ++ */ ++/* i2c stuff */ ++#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000 ++#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004 ++#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008 ++#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c ++ ++#define IVTV_CS53L32A_I2C_ADDR 0x11 ++#define IVTV_M52790_I2C_ADDR 0x48 ++#define IVTV_CX25840_I2C_ADDR 0x44 ++#define IVTV_SAA7115_I2C_ADDR 0x21 ++#define IVTV_SAA7127_I2C_ADDR 0x44 ++#define IVTV_SAA717x_I2C_ADDR 0x21 ++#define IVTV_MSP3400_I2C_ADDR 0x40 ++#define IVTV_HAUPPAUGE_I2C_ADDR 0x50 ++#define IVTV_WM8739_I2C_ADDR 0x1a ++#define IVTV_WM8775_I2C_ADDR 0x1b ++#define IVTV_TEA5767_I2C_ADDR 0x60 ++#define IVTV_UPD64031A_I2C_ADDR 0x12 ++#define IVTV_UPD64083_I2C_ADDR 0x5c ++#define IVTV_VP27SMPX_I2C_ADDR 0x5b ++#define IVTV_M52790_I2C_ADDR 0x48 ++#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40 ++#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a ++#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18 ++#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70 ++#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71 ++#define IVTV_ADAPTEC_IR_ADDR 0x6b ++ ++/* This array should match the IVTV_HW_ defines */ ++static const u8 hw_addrs[] = { ++ IVTV_CX25840_I2C_ADDR, ++ IVTV_SAA7115_I2C_ADDR, ++ IVTV_SAA7127_I2C_ADDR, ++ IVTV_MSP3400_I2C_ADDR, ++ 0, ++ IVTV_WM8775_I2C_ADDR, ++ IVTV_CS53L32A_I2C_ADDR, ++ 0, ++ IVTV_SAA7115_I2C_ADDR, ++ IVTV_UPD64031A_I2C_ADDR, ++ IVTV_UPD64083_I2C_ADDR, ++ IVTV_SAA717x_I2C_ADDR, ++ IVTV_WM8739_I2C_ADDR, ++ IVTV_VP27SMPX_I2C_ADDR, ++ IVTV_M52790_I2C_ADDR, ++ 0, /* IVTV_HW_GPIO dummy driver ID */ ++ IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */ ++ IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ ++ IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ ++ IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ ++ IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ ++ IVTV_ADAPTEC_IR_ADDR, /* IVTV_HW_I2C_IR_RX_ADAPTEC */ ++}; ++ ++/* This array should match the IVTV_HW_ defines */ ++static const char * const hw_devicenames[] = { ++ "cx25840", ++ "saa7115", ++ "saa7127_auto", /* saa7127 or saa7129 */ ++ "msp3400", ++ "tuner", ++ "wm8775", ++ "cs53l32a", ++ "tveeprom", ++ "saa7114", ++ "upd64031a", ++ "upd64083", ++ "saa717x", ++ "wm8739", ++ "vp27smpx", ++ "m52790", ++ "gpio", ++ "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */ ++ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ ++ "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */ ++ "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */ ++ "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */ ++ "ir_video", /* IVTV_HW_I2C_IR_RX_ADAPTEC */ ++}; ++ ++static int get_key_adaptec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char keybuf[4]; ++ ++ keybuf[0] = 0x00; ++ i2c_master_send(ir->c, keybuf, 1); ++ /* poll IR chip */ ++ if (i2c_master_recv(ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) { ++ return 0; ++ } ++ ++ /* key pressed ? */ ++ if (keybuf[2] == 0xff) ++ return 0; ++ ++ /* remove repeat bit */ ++ keybuf[2] &= 0x7f; ++ keybuf[3] |= 0x80; ++ ++ *ir_key = keybuf[3] | keybuf[2] << 8 | keybuf[1] << 16 |keybuf[0] << 24; ++ *ir_raw = *ir_key; ++ ++ return 1; ++} ++ ++static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) ++{ ++ struct i2c_board_info info; ++ struct i2c_adapter *adap = &itv->i2c_adap; ++ struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data; ++ unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; ++ ++ /* Only allow one IR transmitter to be registered per board */ ++ if (hw & IVTV_HW_IR_TX_ANY) { ++ if (itv->hw_flags & IVTV_HW_IR_TX_ANY) ++ return -1; ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, type, I2C_NAME_SIZE); ++ return i2c_new_probed_device(adap, &info, addr_list, NULL) ++ == NULL ? -1 : 0; ++ } ++ ++ /* Only allow one IR receiver to be registered per board */ ++ if (itv->hw_flags & IVTV_HW_IR_RX_ANY) ++ return -1; ++ ++ /* Our default information for ir-kbd-i2c.c to use */ ++ switch (hw) { ++ case IVTV_HW_I2C_IR_RX_AVER: ++ init_data->ir_codes = RC_MAP_AVERMEDIA_CARDBUS; ++ init_data->internal_get_key_func = ++ IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; ++ init_data->type = RC_BIT_OTHER; ++ init_data->name = "AVerMedia AVerTV card"; ++ break; ++ case IVTV_HW_I2C_IR_RX_HAUP_EXT: ++ case IVTV_HW_I2C_IR_RX_HAUP_INT: ++ init_data->ir_codes = RC_MAP_HAUPPAUGE; ++ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; ++ init_data->type = RC_BIT_RC5; ++ init_data->name = itv->card_name; ++ break; ++ case IVTV_HW_Z8F0811_IR_RX_HAUP: ++ /* Default to grey remote */ ++ init_data->ir_codes = RC_MAP_HAUPPAUGE; ++ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; ++ init_data->type = RC_BIT_RC5; ++ init_data->name = itv->card_name; ++ break; ++ case IVTV_HW_I2C_IR_RX_ADAPTEC: ++ init_data->get_key = get_key_adaptec; ++ init_data->name = itv->card_name; ++ /* FIXME: The protocol and RC_MAP needs to be corrected */ ++ init_data->ir_codes = RC_MAP_EMPTY; ++ init_data->type = RC_BIT_UNKNOWN; ++ break; ++ } ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ info.platform_data = init_data; ++ strlcpy(info.type, type, I2C_NAME_SIZE); ++ ++ return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? ++ -1 : 0; ++} ++ ++/* Instantiate the IR receiver device using probing -- undesirable */ ++struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv) ++{ ++ struct i2c_board_info info; ++ /* ++ * The external IR receiver is at i2c address 0x34. ++ * The internal IR receiver is at i2c address 0x30. ++ * ++ * In theory, both can be fitted, and Hauppauge suggests an external ++ * overrides an internal. That's why we probe 0x1a (~0x34) first. CB ++ * ++ * Some of these addresses we probe may collide with other i2c address ++ * allocations, so this function must be called after all other i2c ++ * devices we care about are registered. ++ */ ++ const unsigned short addr_list[] = { ++ 0x1a, /* Hauppauge IR external - collides with WM8739 */ ++ 0x18, /* Hauppauge IR internal */ ++ I2C_CLIENT_END ++ }; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ strlcpy(info.type, "ir_video", I2C_NAME_SIZE); ++ return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list, NULL); ++} ++ ++int ivtv_i2c_register(struct ivtv *itv, unsigned idx) ++{ ++ struct v4l2_subdev *sd; ++ struct i2c_adapter *adap = &itv->i2c_adap; ++ const char *type = hw_devicenames[idx]; ++ u32 hw = 1 << idx; ++ ++ if (idx >= ARRAY_SIZE(hw_addrs)) ++ return -1; ++ if (hw == IVTV_HW_TUNER) { ++ /* special tuner handling */ ++ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0, ++ itv->card_i2c->radio); ++ if (sd) ++ sd->grp_id = 1 << idx; ++ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0, ++ itv->card_i2c->demod); ++ if (sd) ++ sd->grp_id = 1 << idx; ++ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, type, 0, ++ itv->card_i2c->tv); ++ if (sd) ++ sd->grp_id = 1 << idx; ++ return sd ? 0 : -1; ++ } ++ ++ if (hw & IVTV_HW_IR_ANY) ++ return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]); ++ ++ /* Is it not an I2C device or one we do not wish to register? */ ++ if (!hw_addrs[idx]) ++ return -1; ++ ++ /* It's an I2C device other than an analog tuner or IR chip */ ++ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { ++ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, ++ adap, type, 0, I2C_ADDRS(hw_addrs[idx])); ++ } else if (hw == IVTV_HW_CX25840) { ++ struct cx25840_platform_data pdata; ++ struct i2c_board_info cx25840_info = { ++ .type = "cx25840", ++ .addr = hw_addrs[idx], ++ .platform_data = &pdata, ++ }; ++ ++ pdata.pvr150_workaround = itv->pvr150_workaround; ++ sd = v4l2_i2c_new_subdev_board(&itv->v4l2_dev, adap, ++ &cx25840_info, NULL); ++ } else { ++ sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, ++ adap, type, hw_addrs[idx], NULL); ++ } ++ if (sd) ++ sd->grp_id = 1 << idx; ++ return sd ? 0 : -1; ++} ++ ++struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw) ++{ ++ struct v4l2_subdev *result = NULL; ++ struct v4l2_subdev *sd; ++ ++ spin_lock(&itv->v4l2_dev.lock); ++ v4l2_device_for_each_subdev(sd, &itv->v4l2_dev) { ++ if (sd->grp_id == hw) { ++ result = sd; ++ break; ++ } ++ } ++ spin_unlock(&itv->v4l2_dev.lock); ++ return result; ++} ++ ++/* Set the serial clock line to the desired state */ ++static void ivtv_setscl(struct ivtv *itv, int state) ++{ ++ /* write them out */ ++ /* write bits are inverted */ ++ write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET); ++} ++ ++/* Set the serial data line to the desired state */ ++static void ivtv_setsda(struct ivtv *itv, int state) ++{ ++ /* write them out */ ++ /* write bits are inverted */ ++ write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET); ++} ++ ++/* Read the serial clock line */ ++static int ivtv_getscl(struct ivtv *itv) ++{ ++ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; ++} ++ ++/* Read the serial data line */ ++static int ivtv_getsda(struct ivtv *itv) ++{ ++ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; ++} ++ ++/* Implement a short delay by polling the serial clock line */ ++static void ivtv_scldelay(struct ivtv *itv) ++{ ++ int i; ++ ++ for (i = 0; i < 5; ++i) ++ ivtv_getscl(itv); ++} ++ ++/* Wait for the serial clock line to become set to a specific value */ ++static int ivtv_waitscl(struct ivtv *itv, int val) ++{ ++ int i; ++ ++ ivtv_scldelay(itv); ++ for (i = 0; i < 1000; ++i) { ++ if (ivtv_getscl(itv) == val) ++ return 1; ++ } ++ return 0; ++} ++ ++/* Wait for the serial data line to become set to a specific value */ ++static int ivtv_waitsda(struct ivtv *itv, int val) ++{ ++ int i; ++ ++ ivtv_scldelay(itv); ++ for (i = 0; i < 1000; ++i) { ++ if (ivtv_getsda(itv) == val) ++ return 1; ++ } ++ return 0; ++} ++ ++/* Wait for the slave to issue an ACK */ ++static int ivtv_ack(struct ivtv *itv) ++{ ++ int ret = 0; ++ ++ if (ivtv_getscl(itv) == 1) { ++ IVTV_DEBUG_HI_I2C("SCL was high starting an ack\n"); ++ ivtv_setscl(itv, 0); ++ if (!ivtv_waitscl(itv, 0)) { ++ IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n"); ++ return -EREMOTEIO; ++ } ++ } ++ ivtv_setsda(itv, 1); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 1); ++ if (!ivtv_waitsda(itv, 0)) { ++ IVTV_DEBUG_I2C("Slave did not ack\n"); ++ ret = -EREMOTEIO; ++ } ++ ivtv_setscl(itv, 0); ++ if (!ivtv_waitscl(itv, 0)) { ++ IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n"); ++ ret = -EREMOTEIO; ++ } ++ return ret; ++} ++ ++/* Write a single byte to the i2c bus and wait for the slave to ACK */ ++static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte) ++{ ++ int i, bit; ++ ++ IVTV_DEBUG_HI_I2C("write %x\n",byte); ++ for (i = 0; i < 8; ++i, byte<<=1) { ++ ivtv_setscl(itv, 0); ++ if (!ivtv_waitscl(itv, 0)) { ++ IVTV_DEBUG_I2C("Error setting SCL low\n"); ++ return -EREMOTEIO; ++ } ++ bit = (byte>>7)&1; ++ ivtv_setsda(itv, bit); ++ if (!ivtv_waitsda(itv, bit)) { ++ IVTV_DEBUG_I2C("Error setting SDA\n"); ++ return -EREMOTEIO; ++ } ++ ivtv_setscl(itv, 1); ++ if (!ivtv_waitscl(itv, 1)) { ++ IVTV_DEBUG_I2C("Slave not ready for bit\n"); ++ return -EREMOTEIO; ++ } ++ } ++ ivtv_setscl(itv, 0); ++ if (!ivtv_waitscl(itv, 0)) { ++ IVTV_DEBUG_I2C("Error setting SCL low\n"); ++ return -EREMOTEIO; ++ } ++ return ivtv_ack(itv); ++} ++ ++/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the ++ final byte) */ ++static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack) ++{ ++ int i; ++ ++ *byte = 0; ++ ++ ivtv_setsda(itv, 1); ++ ivtv_scldelay(itv); ++ for (i = 0; i < 8; ++i) { ++ ivtv_setscl(itv, 0); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 1); ++ if (!ivtv_waitscl(itv, 1)) { ++ IVTV_DEBUG_I2C("Error setting SCL high\n"); ++ return -EREMOTEIO; ++ } ++ *byte = ((*byte)<<1)|ivtv_getsda(itv); ++ } ++ ivtv_setscl(itv, 0); ++ ivtv_scldelay(itv); ++ ivtv_setsda(itv, nack); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 1); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 0); ++ ivtv_scldelay(itv); ++ IVTV_DEBUG_HI_I2C("read %x\n",*byte); ++ return 0; ++} ++ ++/* Issue a start condition on the i2c bus to alert slaves to prepare for ++ an address write */ ++static int ivtv_start(struct ivtv *itv) ++{ ++ int sda; ++ ++ sda = ivtv_getsda(itv); ++ if (sda != 1) { ++ IVTV_DEBUG_HI_I2C("SDA was low at start\n"); ++ ivtv_setsda(itv, 1); ++ if (!ivtv_waitsda(itv, 1)) { ++ IVTV_DEBUG_I2C("SDA stuck low\n"); ++ return -EREMOTEIO; ++ } ++ } ++ if (ivtv_getscl(itv) != 1) { ++ ivtv_setscl(itv, 1); ++ if (!ivtv_waitscl(itv, 1)) { ++ IVTV_DEBUG_I2C("SCL stuck low at start\n"); ++ return -EREMOTEIO; ++ } ++ } ++ ivtv_setsda(itv, 0); ++ ivtv_scldelay(itv); ++ return 0; ++} ++ ++/* Issue a stop condition on the i2c bus to release it */ ++static int ivtv_stop(struct ivtv *itv) ++{ ++ int i; ++ ++ if (ivtv_getscl(itv) != 0) { ++ IVTV_DEBUG_HI_I2C("SCL not low when stopping\n"); ++ ivtv_setscl(itv, 0); ++ if (!ivtv_waitscl(itv, 0)) { ++ IVTV_DEBUG_I2C("SCL could not be set low\n"); ++ } ++ } ++ ivtv_setsda(itv, 0); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 1); ++ if (!ivtv_waitscl(itv, 1)) { ++ IVTV_DEBUG_I2C("SCL could not be set high\n"); ++ return -EREMOTEIO; ++ } ++ ivtv_scldelay(itv); ++ ivtv_setsda(itv, 1); ++ if (!ivtv_waitsda(itv, 1)) { ++ IVTV_DEBUG_I2C("resetting I2C\n"); ++ for (i = 0; i < 16; ++i) { ++ ivtv_setscl(itv, 0); ++ ivtv_scldelay(itv); ++ ivtv_setscl(itv, 1); ++ ivtv_scldelay(itv); ++ ivtv_setsda(itv, 1); ++ } ++ ivtv_waitsda(itv, 1); ++ return -EREMOTEIO; ++ } ++ return 0; ++} ++ ++/* Write a message to the given i2c slave. do_stop may be 0 to prevent ++ issuing the i2c stop condition (when following with a read) */ ++static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop) ++{ ++ int retry, ret = -EREMOTEIO; ++ u32 i; ++ ++ for (retry = 0; ret != 0 && retry < 8; ++retry) { ++ ret = ivtv_start(itv); ++ ++ if (ret == 0) { ++ ret = ivtv_sendbyte(itv, addr<<1); ++ for (i = 0; ret == 0 && i < len; ++i) ++ ret = ivtv_sendbyte(itv, data[i]); ++ } ++ if (ret != 0 || do_stop) { ++ ivtv_stop(itv); ++ } ++ } ++ if (ret) ++ IVTV_DEBUG_I2C("i2c write to %x failed\n", addr); ++ return ret; ++} ++ ++/* Read data from the given i2c slave. A stop condition is always issued. */ ++static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len) ++{ ++ int retry, ret = -EREMOTEIO; ++ u32 i; ++ ++ for (retry = 0; ret != 0 && retry < 8; ++retry) { ++ ret = ivtv_start(itv); ++ if (ret == 0) ++ ret = ivtv_sendbyte(itv, (addr << 1) | 1); ++ for (i = 0; ret == 0 && i < len; ++i) { ++ ret = ivtv_readbyte(itv, &data[i], i == len - 1); ++ } ++ ivtv_stop(itv); ++ } ++ if (ret) ++ IVTV_DEBUG_I2C("i2c read from %x failed\n", addr); ++ return ret; ++} ++ ++/* Kernel i2c transfer implementation. Takes a number of messages to be read ++ or written. If a read follows a write, this will occur without an ++ intervening stop condition */ ++static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) ++{ ++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap); ++ struct ivtv *itv = to_ivtv(v4l2_dev); ++ int retval; ++ int i; ++ ++ mutex_lock(&itv->i2c_bus_lock); ++ for (i = retval = 0; retval == 0 && i < num; i++) { ++ if (msgs[i].flags & I2C_M_RD) ++ retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len); ++ else { ++ /* if followed by a read, don't stop */ ++ int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD); ++ ++ retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop); ++ } ++ } ++ mutex_unlock(&itv->i2c_bus_lock); ++ return retval ? retval : num; ++} ++ ++/* Kernel i2c capabilities */ ++static u32 ivtv_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm ivtv_algo = { ++ .master_xfer = ivtv_xfer, ++ .functionality = ivtv_functionality, ++}; ++ ++/* template for our-bit banger */ ++static struct i2c_adapter ivtv_i2c_adap_hw_template = { ++ .name = "ivtv i2c driver", ++ .algo = &ivtv_algo, ++ .algo_data = NULL, /* filled from template */ ++ .owner = THIS_MODULE, ++}; ++ ++static void ivtv_setscl_old(void *data, int state) ++{ ++ struct ivtv *itv = (struct ivtv *)data; ++ ++ if (state) ++ itv->i2c_state |= 0x01; ++ else ++ itv->i2c_state &= ~0x01; ++ ++ /* write them out */ ++ /* write bits are inverted */ ++ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET); ++} ++ ++static void ivtv_setsda_old(void *data, int state) ++{ ++ struct ivtv *itv = (struct ivtv *)data; ++ ++ if (state) ++ itv->i2c_state |= 0x01; ++ else ++ itv->i2c_state &= ~0x01; ++ ++ /* write them out */ ++ /* write bits are inverted */ ++ write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET); ++} ++ ++static int ivtv_getscl_old(void *data) ++{ ++ struct ivtv *itv = (struct ivtv *)data; ++ ++ return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; ++} ++ ++static int ivtv_getsda_old(void *data) ++{ ++ struct ivtv *itv = (struct ivtv *)data; ++ ++ return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; ++} ++ ++/* template for i2c-bit-algo */ ++static struct i2c_adapter ivtv_i2c_adap_template = { ++ .name = "ivtv i2c driver", ++ .algo = NULL, /* set by i2c-algo-bit */ ++ .algo_data = NULL, /* filled from template */ ++ .owner = THIS_MODULE, ++}; ++ ++#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */ ++ ++static const struct i2c_algo_bit_data ivtv_i2c_algo_template = { ++ .setsda = ivtv_setsda_old, ++ .setscl = ivtv_setscl_old, ++ .getsda = ivtv_getsda_old, ++ .getscl = ivtv_getscl_old, ++ .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */ ++ .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */ ++}; ++ ++static struct i2c_client ivtv_i2c_client_template = { ++ .name = "ivtv internal", ++}; ++ ++/* init + register i2c adapter */ ++int init_ivtv_i2c(struct ivtv *itv) ++{ ++ int retval; ++ ++ IVTV_DEBUG_I2C("i2c init\n"); ++ ++ /* Sanity checks for the I2C hardware arrays. They must be the ++ * same size. ++ */ ++ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs)) { ++ IVTV_ERR("Mismatched I2C hardware arrays\n"); ++ return -ENODEV; ++ } ++ if (itv->options.newi2c > 0) { ++ itv->i2c_adap = ivtv_i2c_adap_hw_template; ++ } else { ++ itv->i2c_adap = ivtv_i2c_adap_template; ++ itv->i2c_algo = ivtv_i2c_algo_template; ++ } ++ itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; ++ itv->i2c_algo.data = itv; ++ itv->i2c_adap.algo_data = &itv->i2c_algo; ++ ++ sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", ++ itv->instance); ++ i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev); ++ ++ itv->i2c_client = ivtv_i2c_client_template; ++ itv->i2c_client.adapter = &itv->i2c_adap; ++ itv->i2c_adap.dev.parent = &itv->pdev->dev; ++ ++ IVTV_DEBUG_I2C("setting scl and sda to 1\n"); ++ ivtv_setscl(itv, 1); ++ ivtv_setsda(itv, 1); ++ ++ if (itv->options.newi2c > 0) ++ retval = i2c_add_adapter(&itv->i2c_adap); ++ else ++ retval = i2c_bit_add_bus(&itv->i2c_adap); ++ ++ return retval; ++} ++ ++void exit_ivtv_i2c(struct ivtv *itv) ++{ ++ IVTV_DEBUG_I2C("i2c exit\n"); ++ ++ i2c_del_adapter(&itv->i2c_adap); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-i2c.h b/drivers/media/pci/ivtv/ivtv-i2c.h +new file mode 100644 +index 0000000..7b9ec1c +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-i2c.h +@@ -0,0 +1,32 @@ ++/* ++ I2C functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_I2C_H ++#define IVTV_I2C_H ++ ++struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv); ++int ivtv_i2c_register(struct ivtv *itv, unsigned idx); ++struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); ++ ++/* init + register i2c adapter */ ++int init_ivtv_i2c(struct ivtv *itv); ++void exit_ivtv_i2c(struct ivtv *itv); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c +new file mode 100644 +index 0000000..7a8b0d0 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-ioctl.c +@@ -0,0 +1,1927 @@ ++/* ++ ioctl system call ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-version.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-i2c.h" ++#include "ivtv-queue.h" ++#include "ivtv-fileops.h" ++#include "ivtv-vbi.h" ++#include "ivtv-routing.h" ++#include "ivtv-streams.h" ++#include "ivtv-yuv.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-gpio.h" ++#include "ivtv-controls.h" ++#include "ivtv-cards.h" ++#include ++#include ++#include ++#include ++#include ++ ++u16 ivtv_service2vbi(int type) ++{ ++ switch (type) { ++ case V4L2_SLICED_TELETEXT_B: ++ return IVTV_SLICED_TYPE_TELETEXT_B; ++ case V4L2_SLICED_CAPTION_525: ++ return IVTV_SLICED_TYPE_CAPTION_525; ++ case V4L2_SLICED_WSS_625: ++ return IVTV_SLICED_TYPE_WSS_625; ++ case V4L2_SLICED_VPS: ++ return IVTV_SLICED_TYPE_VPS; ++ default: ++ return 0; ++ } ++} ++ ++static int valid_service_line(int field, int line, int is_pal) ++{ ++ return (is_pal && line >= 6 && (line != 23 || field == 0)) || ++ (!is_pal && line >= 10 && line < 22); ++} ++ ++static u16 select_service_from_set(int field, int line, u16 set, int is_pal) ++{ ++ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); ++ int i; ++ ++ set = set & valid_set; ++ if (set == 0 || !valid_service_line(field, line, is_pal)) { ++ return 0; ++ } ++ if (!is_pal) { ++ if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) ++ return V4L2_SLICED_CAPTION_525; ++ } ++ else { ++ if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) ++ return V4L2_SLICED_VPS; ++ if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) ++ return V4L2_SLICED_WSS_625; ++ if (line == 23) ++ return 0; ++ } ++ for (i = 0; i < 32; i++) { ++ if ((1 << i) & set) ++ return 1 << i; ++ } ++ return 0; ++} ++ ++void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) ++{ ++ u16 set = fmt->service_set; ++ int f, l; ++ ++ fmt->service_set = 0; ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); ++ } ++ } ++} ++ ++static void check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) ++{ ++ int f, l; ++ ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); ++ } ++ } ++} ++ ++u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt) ++{ ++ int f, l; ++ u16 set = 0; ++ ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ set |= fmt->service_lines[f][l]; ++ } ++ } ++ return set; ++} ++ ++void ivtv_set_osd_alpha(struct ivtv *itv) ++{ ++ ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3, ++ itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state); ++ ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_chroma_key_state, itv->osd_chroma_key); ++} ++ ++int ivtv_set_speed(struct ivtv *itv, int speed) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ int single_step = (speed == 1 || speed == -1); ++ DEFINE_WAIT(wait); ++ ++ if (speed == 0) speed = 1000; ++ ++ /* No change? */ ++ if (speed == itv->speed && !single_step) ++ return 0; ++ ++ if (single_step && (speed < 0) == (itv->speed < 0)) { ++ /* Single step video and no need to change direction */ ++ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); ++ itv->speed = speed; ++ return 0; ++ } ++ if (single_step) ++ /* Need to change direction */ ++ speed = speed < 0 ? -1000 : 1000; ++ ++ data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0; ++ data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0; ++ data[1] = (speed < 0); ++ data[2] = speed < 0 ? 3 : 7; ++ data[3] = v4l2_ctrl_g_ctrl(itv->cxhdl.video_b_frames); ++ data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0; ++ data[5] = 0; ++ data[6] = 0; ++ ++ if (speed == 1500 || speed == -1500) data[0] |= 1; ++ else if (speed == 2000 || speed == -2000) data[0] |= 2; ++ else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed); ++ else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed); ++ ++ /* If not decoding, just change speed setting */ ++ if (atomic_read(&itv->decoding) > 0) { ++ int got_sig = 0; ++ ++ /* Stop all DMA and decoding activity */ ++ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0); ++ ++ /* Wait for any DMA to finish */ ++ mutex_unlock(&itv->serialize_lock); ++ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); ++ while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) { ++ got_sig = signal_pending(current); ++ if (got_sig) ++ break; ++ got_sig = 0; ++ schedule(); ++ } ++ finish_wait(&itv->dma_waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ if (got_sig) ++ return -EINTR; ++ ++ /* Change Speed safely */ ++ ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data); ++ IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", ++ data[0], data[1], data[2], data[3], data[4], data[5], data[6]); ++ } ++ if (single_step) { ++ speed = (speed < 0) ? -1 : 1; ++ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); ++ } ++ itv->speed = speed; ++ return 0; ++} ++ ++static int ivtv_validate_speed(int cur_speed, int new_speed) ++{ ++ int fact = new_speed < 0 ? -1 : 1; ++ int s; ++ ++ if (cur_speed == 0) ++ cur_speed = 1000; ++ if (new_speed < 0) ++ new_speed = -new_speed; ++ if (cur_speed < 0) ++ cur_speed = -cur_speed; ++ ++ if (cur_speed <= new_speed) { ++ if (new_speed > 1500) ++ return fact * 2000; ++ if (new_speed > 1000) ++ return fact * 1500; ++ } ++ else { ++ if (new_speed >= 2000) ++ return fact * 2000; ++ if (new_speed >= 1500) ++ return fact * 1500; ++ if (new_speed >= 1000) ++ return fact * 1000; ++ } ++ if (new_speed == 0) ++ return 1000; ++ if (new_speed == 1 || new_speed == 1000) ++ return fact * new_speed; ++ ++ s = new_speed; ++ new_speed = 1000 / new_speed; ++ if (1000 / cur_speed == new_speed) ++ new_speed += (cur_speed < s) ? -1 : 1; ++ if (new_speed > 60) return 1000 / (fact * 60); ++ return 1000 / (fact * new_speed); ++} ++ ++static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, ++ struct v4l2_decoder_cmd *dc, int try) ++{ ++ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ ++ switch (dc->cmd) { ++ case V4L2_DEC_CMD_START: { ++ dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO; ++ dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed); ++ if (dc->start.speed < 0) ++ dc->start.format = V4L2_DEC_START_FMT_GOP; ++ else ++ dc->start.format = V4L2_DEC_START_FMT_NONE; ++ if (dc->start.speed != 500 && dc->start.speed != 1500) ++ dc->flags = dc->start.speed == 1000 ? 0 : ++ V4L2_DEC_CMD_START_MUTE_AUDIO; ++ if (try) break; ++ ++ itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO; ++ if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) ++ return -EBUSY; ++ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) { ++ /* forces ivtv_set_speed to be called */ ++ itv->speed = 0; ++ } ++ return ivtv_start_decoding(id, dc->start.speed); ++ } ++ ++ case V4L2_DEC_CMD_STOP: ++ dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK; ++ if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) ++ dc->stop.pts = 0; ++ if (try) break; ++ if (atomic_read(&itv->decoding) == 0) ++ return 0; ++ if (itv->output_mode != OUT_MPG) ++ return -EBUSY; ++ ++ itv->output_mode = OUT_NONE; ++ return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts); ++ ++ case V4L2_DEC_CMD_PAUSE: ++ dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK; ++ if (try) break; ++ if (!atomic_read(&itv->decoding)) ++ return -EPERM; ++ if (itv->output_mode != OUT_MPG) ++ return -EBUSY; ++ if (atomic_read(&itv->decoding) > 0) { ++ ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, ++ (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0); ++ set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags); ++ } ++ break; ++ ++ case V4L2_DEC_CMD_RESUME: ++ dc->flags = 0; ++ if (try) break; ++ if (!atomic_read(&itv->decoding)) ++ return -EPERM; ++ if (itv->output_mode != OUT_MPG) ++ return -EBUSY; ++ if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) { ++ int speed = itv->speed; ++ itv->speed = 0; ++ return ivtv_start_decoding(id, speed); ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int ivtv_g_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) ++ return -EINVAL; ++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); ++ if (itv->is_60hz) { ++ vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; ++ vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; ++ } else { ++ vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; ++ vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; ++ } ++ vbifmt->service_set = ivtv_get_service_set(vbifmt); ++ return 0; ++} ++ ++static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ pixfmt->width = itv->cxhdl.width; ++ pixfmt->height = itv->cxhdl.height; ++ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ pixfmt->field = V4L2_FIELD_INTERLACED; ++ pixfmt->priv = 0; ++ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { ++ pixfmt->pixelformat = V4L2_PIX_FMT_HM12; ++ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ ++ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; ++ pixfmt->bytesperline = 720; ++ } else { ++ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; ++ pixfmt->sizeimage = 128 * 1024; ++ pixfmt->bytesperline = 0; ++ } ++ return 0; ++} ++ ++static int ivtv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; ++ ++ vbifmt->sampling_rate = 27000000; ++ vbifmt->offset = 248; ++ vbifmt->samples_per_line = itv->vbi.raw_decoder_line_size - 4; ++ vbifmt->sample_format = V4L2_PIX_FMT_GREY; ++ vbifmt->start[0] = itv->vbi.start[0]; ++ vbifmt->start[1] = itv->vbi.start[1]; ++ vbifmt->count[0] = vbifmt->count[1] = itv->vbi.count; ++ vbifmt->flags = 0; ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ return 0; ++} ++ ++static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ ++ if (id->type == IVTV_DEC_STREAM_TYPE_VBI) { ++ vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : ++ V4L2_SLICED_VBI_525; ++ ivtv_expand_service_set(vbifmt, itv->is_50hz); ++ vbifmt->service_set = ivtv_get_service_set(vbifmt); ++ return 0; ++ } ++ ++ v4l2_subdev_call(itv->sd_video, vbi, g_sliced_fmt, vbifmt); ++ vbifmt->service_set = ivtv_get_service_set(vbifmt); ++ return 0; ++} ++ ++static int ivtv_g_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ pixfmt->width = itv->main_rect.width; ++ pixfmt->height = itv->main_rect.height; ++ pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ pixfmt->field = V4L2_FIELD_INTERLACED; ++ pixfmt->priv = 0; ++ if (id->type == IVTV_DEC_STREAM_TYPE_YUV) { ++ switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { ++ case IVTV_YUV_MODE_INTERLACED: ++ pixfmt->field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? ++ V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; ++ break; ++ case IVTV_YUV_MODE_PROGRESSIVE: ++ pixfmt->field = V4L2_FIELD_NONE; ++ break; ++ default: ++ pixfmt->field = V4L2_FIELD_ANY; ++ break; ++ } ++ pixfmt->pixelformat = V4L2_PIX_FMT_HM12; ++ pixfmt->bytesperline = 720; ++ pixfmt->width = itv->yuv_info.v4l2_src_w; ++ pixfmt->height = itv->yuv_info.v4l2_src_h; ++ /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ ++ pixfmt->sizeimage = ++ 1080 * ((pixfmt->height + 31) & ~31); ++ } else { ++ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; ++ pixfmt->sizeimage = 128 * 1024; ++ pixfmt->bytesperline = 0; ++ } ++ return 0; ++} ++ ++static int ivtv_g_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct v4l2_window *winfmt = &fmt->fmt.win; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ winfmt->chromakey = itv->osd_chroma_key; ++ winfmt->global_alpha = itv->osd_global_alpha; ++ winfmt->field = V4L2_FIELD_INTERLACED; ++ winfmt->clips = NULL; ++ winfmt->clipcount = 0; ++ winfmt->bitmap = NULL; ++ winfmt->w.top = winfmt->w.left = 0; ++ winfmt->w.width = itv->osd_rect.width; ++ winfmt->w.height = itv->osd_rect.height; ++ return 0; ++} ++ ++static int ivtv_try_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt); ++} ++ ++static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ int w = fmt->fmt.pix.width; ++ int h = fmt->fmt.pix.height; ++ int min_h = 2; ++ ++ w = min(w, 720); ++ w = max(w, 2); ++ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { ++ /* YUV height must be a multiple of 32 */ ++ h &= ~0x1f; ++ min_h = 32; ++ } ++ h = min(h, itv->is_50hz ? 576 : 480); ++ h = max(h, min_h); ++ ivtv_g_fmt_vid_cap(file, fh, fmt); ++ fmt->fmt.pix.width = w; ++ fmt->fmt.pix.height = h; ++ return 0; ++} ++ ++static int ivtv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ return ivtv_g_fmt_vbi_cap(file, fh, fmt); ++} ++ ++static int ivtv_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ ++ if (id->type == IVTV_DEC_STREAM_TYPE_VBI) ++ return ivtv_g_fmt_sliced_vbi_cap(file, fh, fmt); ++ ++ /* set sliced VBI capture format */ ++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; ++ vbifmt->reserved[0] = 0; ++ vbifmt->reserved[1] = 0; ++ ++ if (vbifmt->service_set) ++ ivtv_expand_service_set(vbifmt, itv->is_50hz); ++ check_service_set(vbifmt, itv->is_50hz); ++ vbifmt->service_set = ivtv_get_service_set(vbifmt); ++ return 0; ++} ++ ++static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ s32 w = fmt->fmt.pix.width; ++ s32 h = fmt->fmt.pix.height; ++ int field = fmt->fmt.pix.field; ++ int ret = ivtv_g_fmt_vid_out(file, fh, fmt); ++ ++ w = min(w, 720); ++ w = max(w, 2); ++ /* Why can the height be 576 even when the output is NTSC? ++ ++ Internally the buffers of the PVR350 are always set to 720x576. The ++ decoded video frame will always be placed in the top left corner of ++ this buffer. For any video which is not 720x576, the buffer will ++ then be cropped to remove the unused right and lower areas, with ++ the remaining image being scaled by the hardware to fit the display ++ area. The video can be scaled both up and down, so a 720x480 video ++ can be displayed full-screen on PAL and a 720x576 video can be ++ displayed without cropping on NTSC. ++ ++ Note that the scaling only occurs on the video stream, the osd ++ resolution is locked to the broadcast standard and not scaled. ++ ++ Thanks to Ian Armstrong for this explanation. */ ++ h = min(h, 576); ++ h = max(h, 2); ++ if (id->type == IVTV_DEC_STREAM_TYPE_YUV) ++ fmt->fmt.pix.field = field; ++ fmt->fmt.pix.width = w; ++ fmt->fmt.pix.height = h; ++ return ret; ++} ++ ++static int ivtv_try_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ u32 chromakey = fmt->fmt.win.chromakey; ++ u8 global_alpha = fmt->fmt.win.global_alpha; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ ivtv_g_fmt_vid_out_overlay(file, fh, fmt); ++ fmt->fmt.win.chromakey = chromakey; ++ fmt->fmt.win.global_alpha = global_alpha; ++ return 0; ++} ++ ++static int ivtv_s_fmt_sliced_vbi_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ return ivtv_g_fmt_sliced_vbi_out(file, fh, fmt); ++} ++ ++static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int ret = ivtv_try_fmt_vid_cap(file, fh, fmt); ++ int w = fmt->fmt.pix.width; ++ int h = fmt->fmt.pix.height; ++ ++ if (ret) ++ return ret; ++ ++ if (itv->cxhdl.width == w && itv->cxhdl.height == h) ++ return 0; ++ ++ if (atomic_read(&itv->capturing) > 0) ++ return -EBUSY; ++ ++ itv->cxhdl.width = w; ++ itv->cxhdl.height = h; ++ if (v4l2_ctrl_g_ctrl(itv->cxhdl.video_encoding) == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) ++ fmt->fmt.pix.width /= 2; ++ mbus_fmt.width = fmt->fmt.pix.width; ++ mbus_fmt.height = h; ++ mbus_fmt.code = V4L2_MBUS_FMT_FIXED; ++ v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &mbus_fmt); ++ return ivtv_g_fmt_vid_cap(file, fh, fmt); ++} ++ ++static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (!ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) ++ return -EBUSY; ++ itv->vbi.sliced_in->service_set = 0; ++ itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &fmt->fmt.vbi); ++ return ivtv_g_fmt_vbi_cap(file, fh, fmt); ++} ++ ++static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ int ret = ivtv_try_fmt_sliced_vbi_cap(file, fh, fmt); ++ ++ if (ret || id->type == IVTV_DEC_STREAM_TYPE_VBI) ++ return ret; ++ ++ check_service_set(vbifmt, itv->is_50hz); ++ if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) ++ return -EBUSY; ++ itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; ++ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, vbifmt); ++ memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); ++ return 0; ++} ++ ++static int ivtv_s_fmt_vid_out(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int ret = ivtv_try_fmt_vid_out(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ ++ if (id->type != IVTV_DEC_STREAM_TYPE_YUV) ++ return 0; ++ ++ /* Return now if we already have some frame data */ ++ if (yi->stream_size) ++ return -EBUSY; ++ ++ yi->v4l2_src_w = fmt->fmt.pix.width; ++ yi->v4l2_src_h = fmt->fmt.pix.height; ++ ++ switch (fmt->fmt.pix.field) { ++ case V4L2_FIELD_NONE: ++ yi->lace_mode = IVTV_YUV_MODE_PROGRESSIVE; ++ break; ++ case V4L2_FIELD_ANY: ++ yi->lace_mode = IVTV_YUV_MODE_AUTO; ++ break; ++ case V4L2_FIELD_INTERLACED_BT: ++ yi->lace_mode = ++ IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; ++ break; ++ case V4L2_FIELD_INTERLACED_TB: ++ default: ++ yi->lace_mode = IVTV_YUV_MODE_INTERLACED; ++ break; ++ } ++ yi->lace_sync_field = (yi->lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; ++ ++ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) ++ itv->dma_data_req_size = ++ 1080 * ((yi->v4l2_src_h + 31) & ~31); ++ ++ return 0; ++} ++ ++static int ivtv_s_fmt_vid_out_overlay(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ int ret = ivtv_try_fmt_vid_out_overlay(file, fh, fmt); ++ ++ if (ret == 0) { ++ itv->osd_chroma_key = fmt->fmt.win.chromakey; ++ itv->osd_global_alpha = fmt->fmt.win.global_alpha; ++ ivtv_set_osd_alpha(itv); ++ } ++ return ret; ++} ++ ++static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type == V4L2_CHIP_MATCH_HOST) { ++ if (v4l2_chip_match_host(&chip->match)) ++ chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; ++ return 0; ++ } ++ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && ++ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ /* TODO: is this correct? */ ++ return ivtv_call_all_err(itv, core, g_chip_ident, chip); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) ++{ ++ struct v4l2_dbg_register *regs = arg; ++ volatile u8 __iomem *reg_start; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) ++ reg_start = itv->reg_mem - IVTV_REG_OFFSET; ++ else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && ++ regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) ++ reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; ++ else if (regs->reg < IVTV_ENCODER_SIZE) ++ reg_start = itv->enc_mem; ++ else ++ return -EINVAL; ++ ++ regs->size = 4; ++ if (cmd == VIDIOC_DBG_G_REGISTER) ++ regs->val = readl(regs->reg + reg_start); ++ else ++ writel(regs->val, regs->reg + reg_start); ++ return 0; ++} ++ ++static int ivtv_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (v4l2_chip_match_host(®->match)) ++ return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); ++ /* TODO: subdev errors should not be ignored, this should become a ++ subdev helper function. */ ++ ivtv_call_all(itv, core, g_register, reg); ++ return 0; ++} ++ ++static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (v4l2_chip_match_host(®->match)) ++ return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); ++ /* TODO: subdev errors should not be ignored, this should become a ++ subdev helper function. */ ++ ivtv_call_all(itv, core, s_register, reg); ++ return 0; ++} ++#endif ++ ++static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap) ++{ ++ struct ivtv_open_id *id = fh2id(file->private_data); ++ struct ivtv *itv = id->itv; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ ++ strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); ++ strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); ++ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev)); ++ vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS; ++ vcap->device_caps = s->caps; ++ return 0; ++} ++ ++static int ivtv_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ return ivtv_get_audio_input(itv, vin->index, vin); ++} ++ ++static int ivtv_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ vin->index = itv->audio_input; ++ return ivtv_get_audio_input(itv, vin->index, vin); ++} ++ ++static int ivtv_s_audio(struct file *file, void *fh, const struct v4l2_audio *vout) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (vout->index >= itv->nof_audio_inputs) ++ return -EINVAL; ++ ++ itv->audio_input = vout->index; ++ ivtv_audio_set_io(itv); ++ ++ return 0; ++} ++ ++static int ivtv_enumaudout(struct file *file, void *fh, struct v4l2_audioout *vin) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ /* set it to defaults from our table */ ++ return ivtv_get_audio_output(itv, vin->index, vin); ++} ++ ++static int ivtv_g_audout(struct file *file, void *fh, struct v4l2_audioout *vin) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ vin->index = 0; ++ return ivtv_get_audio_output(itv, vin->index, vin); ++} ++ ++static int ivtv_s_audout(struct file *file, void *fh, const struct v4l2_audioout *vout) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (itv->card->video_outputs == NULL || vout->index != 0) ++ return -EINVAL; ++ return 0; ++} ++ ++static int ivtv_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ /* set it to defaults from our table */ ++ return ivtv_get_input(itv, vin->index, vin); ++} ++ ++static int ivtv_enum_output(struct file *file, void *fh, struct v4l2_output *vout) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ return ivtv_get_output(itv, vout->index, vout); ++} ++ ++static int ivtv_cropcap(struct file *file, void *fh, struct v4l2_cropcap *cropcap) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int streamtype; ++ ++ streamtype = id->type; ++ ++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ cropcap->bounds.top = cropcap->bounds.left = 0; ++ cropcap->bounds.width = 720; ++ if (cropcap->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ cropcap->bounds.height = itv->is_50hz ? 576 : 480; ++ cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10; ++ cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11; ++ } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { ++ if (yi->track_osd) { ++ cropcap->bounds.width = yi->osd_full_w; ++ cropcap->bounds.height = yi->osd_full_h; ++ } else { ++ cropcap->bounds.width = 720; ++ cropcap->bounds.height = ++ itv->is_out_50hz ? 576 : 480; ++ } ++ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; ++ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; ++ } else { ++ cropcap->bounds.height = itv->is_out_50hz ? 576 : 480; ++ cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; ++ cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; ++ } ++ cropcap->defrect = cropcap->bounds; ++ return 0; ++} ++ ++static int ivtv_s_crop(struct file *file, void *fh, const struct v4l2_crop *crop) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int streamtype; ++ ++ streamtype = id->type; ++ ++ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { ++ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { ++ yi->main_rect = crop->c; ++ return 0; ++ } else { ++ if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, ++ crop->c.width, crop->c.height, crop->c.left, crop->c.top)) { ++ itv->main_rect = crop->c; ++ return 0; ++ } ++ } ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++ ++static int ivtv_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int streamtype; ++ ++ streamtype = id->type; ++ ++ if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { ++ if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) ++ crop->c = yi->main_rect; ++ else ++ crop->c = itv->main_rect; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int ivtv_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static const struct v4l2_fmtdesc hm12 = { ++ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, ++ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, ++ { 0, 0, 0, 0 } ++ }; ++ static const struct v4l2_fmtdesc mpeg = { ++ 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, ++ "MPEG", V4L2_PIX_FMT_MPEG, ++ { 0, 0, 0, 0 } ++ }; ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; ++ ++ if (fmt->index) ++ return -EINVAL; ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) ++ *fmt = mpeg; ++ else if (s->type == IVTV_ENC_STREAM_TYPE_YUV) ++ *fmt = hm12; ++ else ++ return -EINVAL; ++ return 0; ++} ++ ++static int ivtv_enum_fmt_vid_out(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static const struct v4l2_fmtdesc hm12 = { ++ 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0, ++ "HM12 (YUV 4:2:0)", V4L2_PIX_FMT_HM12, ++ { 0, 0, 0, 0 } ++ }; ++ static const struct v4l2_fmtdesc mpeg = { ++ 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_FMT_FLAG_COMPRESSED, ++ "MPEG", V4L2_PIX_FMT_MPEG, ++ { 0, 0, 0, 0 } ++ }; ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; ++ ++ if (fmt->index) ++ return -EINVAL; ++ if (s->type == IVTV_DEC_STREAM_TYPE_MPG) ++ *fmt = mpeg; ++ else if (s->type == IVTV_DEC_STREAM_TYPE_YUV) ++ *fmt = hm12; ++ else ++ return -EINVAL; ++ return 0; ++} ++ ++static int ivtv_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ *i = itv->active_input; ++ ++ return 0; ++} ++ ++int ivtv_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ v4l2_std_id std; ++ int i; ++ ++ if (inp >= itv->nof_inputs) ++ return -EINVAL; ++ ++ if (inp == itv->active_input) { ++ IVTV_DEBUG_INFO("Input unchanged\n"); ++ return 0; ++ } ++ ++ if (atomic_read(&itv->capturing) > 0) { ++ return -EBUSY; ++ } ++ ++ IVTV_DEBUG_INFO("Changing input from %d to %d\n", ++ itv->active_input, inp); ++ ++ itv->active_input = inp; ++ /* Set the audio input to whatever is appropriate for the ++ input type. */ ++ itv->audio_input = itv->card->video_inputs[inp].audio_index; ++ ++ if (itv->card->video_inputs[inp].video_type == IVTV_CARD_INPUT_VID_TUNER) ++ std = itv->tuner_std; ++ else ++ std = V4L2_STD_ALL; ++ for (i = 0; i <= IVTV_ENC_STREAM_TYPE_VBI; i++) ++ itv->streams[i].vdev->tvnorms = std; ++ ++ /* prevent others from messing with the streams until ++ we're finished changing inputs. */ ++ ivtv_mute(itv); ++ ivtv_video_set_io(itv); ++ ivtv_audio_set_io(itv); ++ ivtv_unmute(itv); ++ ++ return 0; ++} ++ ++static int ivtv_g_output(struct file *file, void *fh, unsigned int *i) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ ++ *i = itv->active_output; ++ ++ return 0; ++} ++ ++static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (outp >= itv->card->nof_outputs) ++ return -EINVAL; ++ ++ if (outp == itv->active_output) { ++ IVTV_DEBUG_INFO("Output unchanged\n"); ++ return 0; ++ } ++ IVTV_DEBUG_INFO("Changing output from %d to %d\n", ++ itv->active_output, outp); ++ ++ itv->active_output = outp; ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, ++ SAA7127_INPUT_TYPE_NORMAL, ++ itv->card->video_outputs[outp].video_output, 0); ++ ++ return 0; ++} ++ ++static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; ++ ++ if (s->vdev->vfl_dir) ++ return -ENOTTY; ++ if (vf->tuner != 0) ++ return -EINVAL; ++ ++ ivtv_call_all(itv, tuner, g_frequency, vf); ++ return 0; ++} ++ ++int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct ivtv_stream *s = &itv->streams[fh2id(fh)->type]; ++ ++ if (s->vdev->vfl_dir) ++ return -ENOTTY; ++ if (vf->tuner != 0) ++ return -EINVAL; ++ ++ ivtv_mute(itv); ++ IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); ++ ivtv_call_all(itv, tuner, s_frequency, vf); ++ ivtv_unmute(itv); ++ return 0; ++} ++ ++static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ *std = itv->std; ++ return 0; ++} ++ ++void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std) ++{ ++ itv->std = *std; ++ itv->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; ++ itv->is_50hz = !itv->is_60hz; ++ cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz); ++ itv->cxhdl.width = 720; ++ itv->cxhdl.height = itv->is_50hz ? 576 : 480; ++ itv->vbi.count = itv->is_50hz ? 18 : 12; ++ itv->vbi.start[0] = itv->is_50hz ? 6 : 10; ++ itv->vbi.start[1] = itv->is_50hz ? 318 : 273; ++ ++ if (itv->hw_flags & IVTV_HW_CX25840) ++ itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; ++ ++ /* Tuner */ ++ ivtv_call_all(itv, core, s_std, itv->std); ++} ++ ++void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ DEFINE_WAIT(wait); ++ int f; ++ ++ /* set display standard */ ++ itv->std_out = *std; ++ itv->is_out_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; ++ itv->is_out_50hz = !itv->is_out_60hz; ++ ivtv_call_all(itv, video, s_std_output, itv->std_out); ++ ++ /* ++ * The next firmware call is time sensitive. Time it to ++ * avoid risk of a hard lock, by trying to ensure the call ++ * happens within the first 100 lines of the top field. ++ * Make 4 attempts to sync to the decoder before giving up. ++ */ ++ mutex_unlock(&itv->serialize_lock); ++ for (f = 0; f < 4; f++) { ++ prepare_to_wait(&itv->vsync_waitq, &wait, ++ TASK_UNINTERRUPTIBLE); ++ if ((read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16) < 100) ++ break; ++ schedule_timeout(msecs_to_jiffies(25)); ++ } ++ finish_wait(&itv->vsync_waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ ++ if (f == 4) ++ IVTV_WARN("Mode change failed to sync to decoder\n"); ++ ++ ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); ++ itv->main_rect.left = 0; ++ itv->main_rect.top = 0; ++ itv->main_rect.width = 720; ++ itv->main_rect.height = itv->is_out_50hz ? 576 : 480; ++ ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, ++ 720, itv->main_rect.height, 0, 0); ++ yi->main_rect = itv->main_rect; ++ if (!itv->osd_info) { ++ yi->osd_full_w = 720; ++ yi->osd_full_h = itv->is_out_50hz ? 576 : 480; ++ } ++} ++ ++static int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if ((*std & V4L2_STD_ALL) == 0) ++ return -EINVAL; ++ ++ if (*std == itv->std) ++ return 0; ++ ++ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || ++ atomic_read(&itv->capturing) > 0 || ++ atomic_read(&itv->decoding) > 0) { ++ /* Switching standard would mess with already running ++ streams, prevent that by returning EBUSY. */ ++ return -EBUSY; ++ } ++ ++ IVTV_DEBUG_INFO("Switching standard to %llx.\n", ++ (unsigned long long)itv->std); ++ ++ ivtv_s_std_enc(itv, std); ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) ++ ivtv_s_std_dec(itv, std); ++ ++ return 0; ++} ++ ++static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ ++ if (vt->index != 0) ++ return -EINVAL; ++ ++ ivtv_call_all(itv, tuner, s_tuner, vt); ++ ++ return 0; ++} ++ ++static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (vt->index != 0) ++ return -EINVAL; ++ ++ ivtv_call_all(itv, tuner, g_tuner, vt); ++ ++ if (vt->type == V4L2_TUNER_RADIO) ++ strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); ++ else ++ strlcpy(vt->name, "ivtv TV Tuner", sizeof(vt->name)); ++ return 0; ++} ++ ++static int ivtv_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; ++ int f, l; ++ ++ if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { ++ for (f = 0; f < 2; f++) { ++ for (l = 0; l < 24; l++) { ++ if (valid_service_line(f, l, itv->is_50hz)) ++ cap->service_lines[f][l] = set; ++ } ++ } ++ } else if (cap->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { ++ if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) ++ return -EINVAL; ++ if (itv->is_60hz) { ++ cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; ++ cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; ++ } else { ++ cap->service_lines[0][23] = V4L2_SLICED_WSS_625; ++ cap->service_lines[0][16] = V4L2_SLICED_VPS; ++ } ++ } else { ++ return -EINVAL; ++ } ++ ++ set = 0; ++ for (f = 0; f < 2; f++) ++ for (l = 0; l < 24; l++) ++ set |= cap->service_lines[f][l]; ++ cap->service_set = set; ++ return 0; ++} ++ ++static int ivtv_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ struct v4l2_enc_idx_entry *e = idx->entry; ++ int entries; ++ int i; ++ ++ entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % ++ IVTV_MAX_PGM_INDEX; ++ if (entries > V4L2_ENC_IDX_ENTRIES) ++ entries = V4L2_ENC_IDX_ENTRIES; ++ idx->entries = 0; ++ idx->entries_cap = IVTV_MAX_PGM_INDEX; ++ if (!atomic_read(&itv->capturing)) ++ return 0; ++ for (i = 0; i < entries; i++) { ++ *e = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; ++ if ((e->flags & V4L2_ENC_IDX_FRAME_MASK) <= V4L2_ENC_IDX_FRAME_B) { ++ idx->entries++; ++ e++; ++ } ++ } ++ itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; ++ return 0; ++} ++ ++static int ivtv_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ ++ ++ switch (enc->cmd) { ++ case V4L2_ENC_CMD_START: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); ++ enc->flags = 0; ++ return ivtv_start_capture(id); ++ ++ case V4L2_ENC_CMD_STOP: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); ++ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; ++ ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); ++ return 0; ++ ++ case V4L2_ENC_CMD_PAUSE: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); ++ enc->flags = 0; ++ ++ if (!atomic_read(&itv->capturing)) ++ return -EPERM; ++ if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) ++ return 0; ++ ++ ivtv_mute(itv); ++ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); ++ break; ++ ++ case V4L2_ENC_CMD_RESUME: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); ++ enc->flags = 0; ++ ++ if (!atomic_read(&itv->capturing)) ++ return -EPERM; ++ ++ if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) ++ return 0; ++ ++ ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); ++ ivtv_unmute(itv); ++ break; ++ default: ++ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ivtv_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *enc) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ switch (enc->cmd) { ++ case V4L2_ENC_CMD_START: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); ++ enc->flags = 0; ++ return 0; ++ ++ case V4L2_ENC_CMD_STOP: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); ++ enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; ++ return 0; ++ ++ case V4L2_ENC_CMD_PAUSE: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); ++ enc->flags = 0; ++ return 0; ++ ++ case V4L2_ENC_CMD_RESUME: ++ IVTV_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); ++ enc->flags = 0; ++ return 0; ++ default: ++ IVTV_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); ++ return -EINVAL; ++ } ++} ++ ++static int ivtv_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ ++ int pixfmt; ++ static u32 pixel_format[16] = { ++ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry RGB colormap */ ++ V4L2_PIX_FMT_RGB565, ++ V4L2_PIX_FMT_RGB555, ++ V4L2_PIX_FMT_RGB444, ++ V4L2_PIX_FMT_RGB32, ++ 0, ++ 0, ++ 0, ++ V4L2_PIX_FMT_PAL8, /* Uses a 256-entry YUV colormap */ ++ V4L2_PIX_FMT_YUV565, ++ V4L2_PIX_FMT_YUV555, ++ V4L2_PIX_FMT_YUV444, ++ V4L2_PIX_FMT_YUV32, ++ 0, ++ 0, ++ 0, ++ }; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) ++ return -EINVAL; ++ if (!itv->osd_video_pbase) ++ return -EINVAL; ++ ++ fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | ++ V4L2_FBUF_CAP_GLOBAL_ALPHA; ++ ++ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); ++ data[0] |= (read_reg(0x2a00) >> 7) & 0x40; ++ pixfmt = (data[0] >> 3) & 0xf; ++ ++ fb->fmt.pixelformat = pixel_format[pixfmt]; ++ fb->fmt.width = itv->osd_rect.width; ++ fb->fmt.height = itv->osd_rect.height; ++ fb->fmt.field = V4L2_FIELD_INTERLACED; ++ fb->fmt.bytesperline = fb->fmt.width; ++ fb->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ fb->fmt.field = V4L2_FIELD_INTERLACED; ++ fb->fmt.priv = 0; ++ if (fb->fmt.pixelformat != V4L2_PIX_FMT_PAL8) ++ fb->fmt.bytesperline *= 2; ++ if (fb->fmt.pixelformat == V4L2_PIX_FMT_RGB32 || ++ fb->fmt.pixelformat == V4L2_PIX_FMT_YUV32) ++ fb->fmt.bytesperline *= 2; ++ fb->fmt.sizeimage = fb->fmt.bytesperline * fb->fmt.height; ++ fb->base = (void *)itv->osd_video_pbase; ++ fb->flags = 0; ++ ++ if (itv->osd_chroma_key_state) ++ fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; ++ ++ if (itv->osd_global_alpha_state) ++ fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; ++ ++ if (yi->track_osd) ++ fb->flags |= V4L2_FBUF_FLAG_OVERLAY; ++ ++ pixfmt &= 7; ++ ++ /* no local alpha for RGB565 or unknown formats */ ++ if (pixfmt == 1 || pixfmt > 4) ++ return 0; ++ ++ /* 16-bit formats have inverted local alpha */ ++ if (pixfmt == 2 || pixfmt == 3) ++ fb->capability |= V4L2_FBUF_CAP_LOCAL_INV_ALPHA; ++ else ++ fb->capability |= V4L2_FBUF_CAP_LOCAL_ALPHA; ++ ++ if (itv->osd_local_alpha_state) { ++ /* 16-bit formats have inverted local alpha */ ++ if (pixfmt == 2 || pixfmt == 3) ++ fb->flags |= V4L2_FBUF_FLAG_LOCAL_INV_ALPHA; ++ else ++ fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; ++ } ++ ++ return 0; ++} ++ ++static int ivtv_s_fbuf(struct file *file, void *fh, const struct v4l2_framebuffer *fb) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) ++ return -EINVAL; ++ if (!itv->osd_video_pbase) ++ return -EINVAL; ++ ++ itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; ++ itv->osd_local_alpha_state = ++ (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0; ++ itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; ++ ivtv_set_osd_alpha(itv); ++ yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; ++ return 0; ++} ++ ++static int ivtv_overlay(struct file *file, void *fh, unsigned int on) ++{ ++ struct ivtv_open_id *id = fh2id(fh); ++ struct ivtv *itv = id->itv; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) ++ return -EINVAL; ++ ++ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, on != 0); ++ ++ return 0; ++} ++ ++static int ivtv_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub) ++{ ++ switch (sub->type) { ++ case V4L2_EVENT_VSYNC: ++ case V4L2_EVENT_EOS: ++ return v4l2_event_subscribe(fh, sub, 0, NULL); ++ case V4L2_EVENT_CTRL: ++ return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int ivtv_log_status(struct file *file, void *fh) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ ++ int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; ++ struct v4l2_input vidin; ++ struct v4l2_audio audin; ++ int i; ++ ++ IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); ++ if (itv->hw_flags & IVTV_HW_TVEEPROM) { ++ struct tveeprom tv; ++ ++ ivtv_read_eeprom(itv, &tv); ++ } ++ ivtv_call_all(itv, core, log_status); ++ ivtv_get_input(itv, itv->active_input, &vidin); ++ ivtv_get_audio_input(itv, itv->audio_input, &audin); ++ IVTV_INFO("Video Input: %s\n", vidin.name); ++ IVTV_INFO("Audio Input: %s%s\n", audin.name, ++ (itv->dualwatch_stereo_mode & ~0x300) == 0x200 ? " (Bilingual)" : ""); ++ if (has_output) { ++ struct v4l2_output vidout; ++ struct v4l2_audioout audout; ++ int mode = itv->output_mode; ++ static const char * const output_modes[5] = { ++ "None", ++ "MPEG Streaming", ++ "YUV Streaming", ++ "YUV Frames", ++ "Passthrough", ++ }; ++ static const char * const alpha_mode[4] = { ++ "None", ++ "Global", ++ "Local", ++ "Global and Local" ++ }; ++ static const char * const pixel_format[16] = { ++ "ARGB Indexed", ++ "RGB 5:6:5", ++ "ARGB 1:5:5:5", ++ "ARGB 1:4:4:4", ++ "ARGB 8:8:8:8", ++ "5", ++ "6", ++ "7", ++ "AYUV Indexed", ++ "YUV 5:6:5", ++ "AYUV 1:5:5:5", ++ "AYUV 1:4:4:4", ++ "AYUV 8:8:8:8", ++ "13", ++ "14", ++ "15", ++ }; ++ ++ ivtv_get_output(itv, itv->active_output, &vidout); ++ ivtv_get_audio_output(itv, 0, &audout); ++ IVTV_INFO("Video Output: %s\n", vidout.name); ++ if (mode < 0 || mode > OUT_PASSTHROUGH) ++ mode = OUT_NONE; ++ IVTV_INFO("Output Mode: %s\n", output_modes[mode]); ++ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_STATE, 0); ++ data[0] |= (read_reg(0x2a00) >> 7) & 0x40; ++ IVTV_INFO("Overlay: %s, Alpha: %s, Pixel Format: %s\n", ++ data[0] & 1 ? "On" : "Off", ++ alpha_mode[(data[0] >> 1) & 0x3], ++ pixel_format[(data[0] >> 3) & 0xf]); ++ } ++ IVTV_INFO("Tuner: %s\n", ++ test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); ++ v4l2_ctrl_handler_log_status(&itv->cxhdl.hdl, itv->v4l2_dev.name); ++ IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); ++ for (i = 0; i < IVTV_MAX_STREAMS; i++) { ++ struct ivtv_stream *s = &itv->streams[i]; ++ ++ if (s->vdev == NULL || s->buffers == 0) ++ continue; ++ IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, ++ (s->buffers - s->q_free.buffers) * 100 / s->buffers, ++ (s->buffers * s->buf_size) / 1024, s->buffers); ++ } ++ ++ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", ++ (long long)itv->mpg_data_received, ++ (long long)itv->vbi_data_inserted); ++ return 0; ++} ++ ++static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) ++{ ++ struct ivtv_open_id *id = fh2id(file->private_data); ++ struct ivtv *itv = id->itv; ++ ++ IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd); ++ return ivtv_video_command(itv, id, dec, false); ++} ++ ++static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec) ++{ ++ struct ivtv_open_id *id = fh2id(file->private_data); ++ struct ivtv *itv = id->itv; ++ ++ IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd); ++ return ivtv_video_command(itv, id, dec, true); ++} ++ ++static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) ++{ ++ struct ivtv_open_id *id = fh2id(filp->private_data); ++ struct ivtv *itv = id->itv; ++ int nonblocking = filp->f_flags & O_NONBLOCK; ++ struct ivtv_stream *s = &itv->streams[id->type]; ++ unsigned long iarg = (unsigned long)arg; ++ ++ switch (cmd) { ++ case IVTV_IOC_DMA_FRAME: { ++ struct ivtv_dma_frame *args = arg; ++ ++ IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n"); ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ return -EINVAL; ++ if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL) ++ return 0; ++ if (ivtv_start_decoding(id, id->type)) { ++ return -EBUSY; ++ } ++ if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) { ++ ivtv_release_stream(s); ++ return -EBUSY; ++ } ++ /* Mark that this file handle started the UDMA_YUV mode */ ++ id->yuv_frames = 1; ++ if (args->y_source == NULL) ++ return 0; ++ return ivtv_yuv_prep_frame(itv, args); ++ } ++ ++ case IVTV_IOC_PASSTHROUGH_MODE: ++ IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n"); ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ return ivtv_passthrough_mode(itv, *(int *)arg != 0); ++ ++ case VIDEO_GET_PTS: { ++ s64 *pts = arg; ++ s64 frame; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); ++ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { ++ *pts = s->dma_pts; ++ break; ++ } ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ return ivtv_g_pts_frame(itv, pts, &frame); ++ } ++ ++ case VIDEO_GET_FRAME_COUNT: { ++ s64 *frame = arg; ++ s64 pts; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); ++ if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { ++ *frame = 0; ++ break; ++ } ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ return ivtv_g_pts_frame(itv, &pts, frame); ++ } ++ ++ case VIDEO_PLAY: { ++ struct v4l2_decoder_cmd dc; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); ++ memset(&dc, 0, sizeof(dc)); ++ dc.cmd = V4L2_DEC_CMD_START; ++ return ivtv_video_command(itv, id, &dc, 0); ++ } ++ ++ case VIDEO_STOP: { ++ struct v4l2_decoder_cmd dc; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); ++ memset(&dc, 0, sizeof(dc)); ++ dc.cmd = V4L2_DEC_CMD_STOP; ++ dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY; ++ return ivtv_video_command(itv, id, &dc, 0); ++ } ++ ++ case VIDEO_FREEZE: { ++ struct v4l2_decoder_cmd dc; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); ++ memset(&dc, 0, sizeof(dc)); ++ dc.cmd = V4L2_DEC_CMD_PAUSE; ++ return ivtv_video_command(itv, id, &dc, 0); ++ } ++ ++ case VIDEO_CONTINUE: { ++ struct v4l2_decoder_cmd dc; ++ ++ IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); ++ memset(&dc, 0, sizeof(dc)); ++ dc.cmd = V4L2_DEC_CMD_RESUME; ++ return ivtv_video_command(itv, id, &dc, 0); ++ } ++ ++ case VIDEO_COMMAND: ++ case VIDEO_TRY_COMMAND: { ++ /* Note: struct v4l2_decoder_cmd has the same layout as ++ struct video_command */ ++ struct v4l2_decoder_cmd *dc = arg; ++ int try = (cmd == VIDEO_TRY_COMMAND); ++ ++ if (try) ++ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd); ++ else ++ IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd); ++ return ivtv_video_command(itv, id, dc, try); ++ } ++ ++ case VIDEO_GET_EVENT: { ++ struct video_event *ev = arg; ++ DEFINE_WAIT(wait); ++ ++ IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n"); ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ memset(ev, 0, sizeof(*ev)); ++ set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); ++ ++ while (1) { ++ if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) ++ ev->type = VIDEO_EVENT_DECODER_STOPPED; ++ else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) { ++ ev->type = VIDEO_EVENT_VSYNC; ++ ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ? ++ VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN; ++ if (itv->output_mode == OUT_UDMA_YUV && ++ (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) == ++ IVTV_YUV_MODE_PROGRESSIVE) { ++ ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE; ++ } ++ } ++ if (ev->type) ++ return 0; ++ if (nonblocking) ++ return -EAGAIN; ++ /* Wait for event. Note that serialize_lock is locked, ++ so to allow other processes to access the driver while ++ we are waiting unlock first and later lock again. */ ++ mutex_unlock(&itv->serialize_lock); ++ prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE); ++ if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) && ++ !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) ++ schedule(); ++ finish_wait(&itv->event_waitq, &wait); ++ mutex_lock(&itv->serialize_lock); ++ if (signal_pending(current)) { ++ /* return if a signal was received */ ++ IVTV_DEBUG_INFO("User stopped wait for event\n"); ++ return -EINTR; ++ } ++ } ++ break; ++ } ++ ++ case VIDEO_SELECT_SOURCE: ++ IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return -EINVAL; ++ return ivtv_passthrough_mode(itv, iarg == VIDEO_SOURCE_DEMUX); ++ ++ case AUDIO_SET_MUTE: ++ IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); ++ itv->speed_mute_audio = iarg; ++ return 0; ++ ++ case AUDIO_CHANNEL_SELECT: ++ IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); ++ if (iarg > AUDIO_STEREO_SWAPPED) ++ return -EINVAL; ++ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg + 1); ++ ++ case AUDIO_BILINGUAL_CHANNEL_SELECT: ++ IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); ++ if (iarg > AUDIO_STEREO_SWAPPED) ++ return -EINVAL; ++ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg + 1); ++ ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static long ivtv_default(struct file *file, void *fh, bool valid_prio, ++ int cmd, void *arg) ++{ ++ struct ivtv *itv = fh2id(fh)->itv; ++ ++ if (!valid_prio) { ++ switch (cmd) { ++ case IVTV_IOC_PASSTHROUGH_MODE: ++ case VIDEO_PLAY: ++ case VIDEO_STOP: ++ case VIDEO_FREEZE: ++ case VIDEO_CONTINUE: ++ case VIDEO_COMMAND: ++ case VIDEO_SELECT_SOURCE: ++ case AUDIO_SET_MUTE: ++ case AUDIO_CHANNEL_SELECT: ++ case AUDIO_BILINGUAL_CHANNEL_SELECT: ++ return -EBUSY; ++ } ++ } ++ ++ switch (cmd) { ++ case VIDIOC_INT_RESET: { ++ u32 val = *(u32 *)arg; ++ ++ if ((val == 0 && itv->options.newi2c) || (val & 0x01)) ++ ivtv_reset_ir_gpio(itv); ++ if (val & 0x02) ++ v4l2_subdev_call(itv->sd_video, core, reset, 0); ++ break; ++ } ++ ++ case IVTV_IOC_DMA_FRAME: ++ case IVTV_IOC_PASSTHROUGH_MODE: ++ case VIDEO_GET_PTS: ++ case VIDEO_GET_FRAME_COUNT: ++ case VIDEO_GET_EVENT: ++ case VIDEO_PLAY: ++ case VIDEO_STOP: ++ case VIDEO_FREEZE: ++ case VIDEO_CONTINUE: ++ case VIDEO_COMMAND: ++ case VIDEO_TRY_COMMAND: ++ case VIDEO_SELECT_SOURCE: ++ case AUDIO_SET_MUTE: ++ case AUDIO_CHANNEL_SELECT: ++ case AUDIO_BILINGUAL_CHANNEL_SELECT: ++ return ivtv_decoder_ioctls(file, cmd, (void *)arg); ++ ++ default: ++ return -ENOTTY; ++ } ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops ivtv_ioctl_ops = { ++ .vidioc_querycap = ivtv_querycap, ++ .vidioc_s_audio = ivtv_s_audio, ++ .vidioc_g_audio = ivtv_g_audio, ++ .vidioc_enumaudio = ivtv_enumaudio, ++ .vidioc_s_audout = ivtv_s_audout, ++ .vidioc_g_audout = ivtv_g_audout, ++ .vidioc_enum_input = ivtv_enum_input, ++ .vidioc_enum_output = ivtv_enum_output, ++ .vidioc_enumaudout = ivtv_enumaudout, ++ .vidioc_cropcap = ivtv_cropcap, ++ .vidioc_s_crop = ivtv_s_crop, ++ .vidioc_g_crop = ivtv_g_crop, ++ .vidioc_g_input = ivtv_g_input, ++ .vidioc_s_input = ivtv_s_input, ++ .vidioc_g_output = ivtv_g_output, ++ .vidioc_s_output = ivtv_s_output, ++ .vidioc_g_frequency = ivtv_g_frequency, ++ .vidioc_s_frequency = ivtv_s_frequency, ++ .vidioc_s_tuner = ivtv_s_tuner, ++ .vidioc_g_tuner = ivtv_g_tuner, ++ .vidioc_g_enc_index = ivtv_g_enc_index, ++ .vidioc_g_fbuf = ivtv_g_fbuf, ++ .vidioc_s_fbuf = ivtv_s_fbuf, ++ .vidioc_g_std = ivtv_g_std, ++ .vidioc_s_std = ivtv_s_std, ++ .vidioc_overlay = ivtv_overlay, ++ .vidioc_log_status = ivtv_log_status, ++ .vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap, ++ .vidioc_encoder_cmd = ivtv_encoder_cmd, ++ .vidioc_try_encoder_cmd = ivtv_try_encoder_cmd, ++ .vidioc_decoder_cmd = ivtv_decoder_cmd, ++ .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd, ++ .vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap, ++ .vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap, ++ .vidioc_g_fmt_sliced_vbi_cap = ivtv_g_fmt_sliced_vbi_cap, ++ .vidioc_g_fmt_vid_out = ivtv_g_fmt_vid_out, ++ .vidioc_g_fmt_vid_out_overlay = ivtv_g_fmt_vid_out_overlay, ++ .vidioc_g_fmt_sliced_vbi_out = ivtv_g_fmt_sliced_vbi_out, ++ .vidioc_s_fmt_vid_cap = ivtv_s_fmt_vid_cap, ++ .vidioc_s_fmt_vbi_cap = ivtv_s_fmt_vbi_cap, ++ .vidioc_s_fmt_sliced_vbi_cap = ivtv_s_fmt_sliced_vbi_cap, ++ .vidioc_s_fmt_vid_out = ivtv_s_fmt_vid_out, ++ .vidioc_s_fmt_vid_out_overlay = ivtv_s_fmt_vid_out_overlay, ++ .vidioc_s_fmt_sliced_vbi_out = ivtv_s_fmt_sliced_vbi_out, ++ .vidioc_try_fmt_vid_cap = ivtv_try_fmt_vid_cap, ++ .vidioc_try_fmt_vbi_cap = ivtv_try_fmt_vbi_cap, ++ .vidioc_try_fmt_sliced_vbi_cap = ivtv_try_fmt_sliced_vbi_cap, ++ .vidioc_try_fmt_vid_out = ivtv_try_fmt_vid_out, ++ .vidioc_try_fmt_vid_out_overlay = ivtv_try_fmt_vid_out_overlay, ++ .vidioc_try_fmt_sliced_vbi_out = ivtv_try_fmt_sliced_vbi_out, ++ .vidioc_g_sliced_vbi_cap = ivtv_g_sliced_vbi_cap, ++ .vidioc_g_chip_ident = ivtv_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = ivtv_g_register, ++ .vidioc_s_register = ivtv_s_register, ++#endif ++ .vidioc_default = ivtv_default, ++ .vidioc_subscribe_event = ivtv_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++void ivtv_set_funcs(struct video_device *vdev) ++{ ++ vdev->ioctl_ops = &ivtv_ioctl_ops; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.h b/drivers/media/pci/ivtv/ivtv-ioctl.h +new file mode 100644 +index 0000000..7c553d1 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-ioctl.h +@@ -0,0 +1,35 @@ ++/* ++ ioctl system call ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_IOCTL_H ++#define IVTV_IOCTL_H ++ ++u16 ivtv_service2vbi(int type); ++void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); ++u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt); ++void ivtv_set_osd_alpha(struct ivtv *itv); ++int ivtv_set_speed(struct ivtv *itv, int speed); ++void ivtv_set_funcs(struct video_device *vdev); ++void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id *std); ++void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id *std); ++int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); ++int ivtv_s_input(struct file *file, void *fh, unsigned int inp); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c +new file mode 100644 +index 0000000..19a7c9b +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-irq.c +@@ -0,0 +1,1088 @@ ++/* interrupt handling ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-queue.h" ++#include "ivtv-udma.h" ++#include "ivtv-irq.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-vbi.h" ++#include "ivtv-yuv.h" ++#include ++ ++#define DMA_MAGIC_COOKIE 0x000001fe ++ ++static void ivtv_dma_dec_start(struct ivtv_stream *s); ++ ++static const int ivtv_stream_map[] = { ++ IVTV_ENC_STREAM_TYPE_MPG, ++ IVTV_ENC_STREAM_TYPE_YUV, ++ IVTV_ENC_STREAM_TYPE_PCM, ++ IVTV_ENC_STREAM_TYPE_VBI, ++}; ++ ++static void ivtv_pcm_work_handler(struct ivtv *itv) ++{ ++ struct ivtv_stream *s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; ++ struct ivtv_buffer *buf; ++ ++ /* Pass the PCM data to ivtv-alsa */ ++ ++ while (1) { ++ /* ++ * Users should not be using both the ALSA and V4L2 PCM audio ++ * capture interfaces at the same time. If the user is doing ++ * this, there maybe a buffer in q_io to grab, use, and put ++ * back in rotation. ++ */ ++ buf = ivtv_dequeue(s, &s->q_io); ++ if (buf == NULL) ++ buf = ivtv_dequeue(s, &s->q_full); ++ if (buf == NULL) ++ break; ++ ++ if (buf->readpos < buf->bytesused) ++ itv->pcm_announce_callback(itv->alsa, ++ (u8 *)(buf->buf + buf->readpos), ++ (size_t)(buf->bytesused - buf->readpos)); ++ ++ ivtv_enqueue(s, buf, &s->q_free); ++ } ++} ++ ++static void ivtv_pio_work_handler(struct ivtv *itv) ++{ ++ struct ivtv_stream *s = &itv->streams[itv->cur_pio_stream]; ++ struct ivtv_buffer *buf; ++ int i = 0; ++ ++ IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n"); ++ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS || ++ s->vdev == NULL || !ivtv_use_pio(s)) { ++ itv->cur_pio_stream = -1; ++ /* trigger PIO complete user interrupt */ ++ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); ++ return; ++ } ++ IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name); ++ list_for_each_entry(buf, &s->q_dma.list, list) { ++ u32 size = s->sg_processing[i].size & 0x3ffff; ++ ++ /* Copy the data from the card to the buffer */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { ++ memcpy_fromio(buf->buf, itv->dec_mem + s->sg_processing[i].src - IVTV_DECODER_OFFSET, size); ++ } ++ else { ++ memcpy_fromio(buf->buf, itv->enc_mem + s->sg_processing[i].src, size); ++ } ++ i++; ++ if (i == s->sg_processing_size) ++ break; ++ } ++ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); ++} ++ ++void ivtv_irq_work_handler(struct kthread_work *work) ++{ ++ struct ivtv *itv = container_of(work, struct ivtv, irq_work); ++ ++ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags)) ++ ivtv_pio_work_handler(itv); ++ ++ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags)) ++ ivtv_vbi_work_handler(itv); ++ ++ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags)) ++ ivtv_yuv_work_handler(itv); ++ ++ if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags)) ++ ivtv_pcm_work_handler(itv); ++} ++ ++/* Determine the required DMA size, setup enough buffers in the predma queue and ++ actually copy the data from the card to the buffers in case a PIO transfer is ++ required for this stream. ++ */ ++static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA]) ++{ ++ struct ivtv *itv = s->itv; ++ struct ivtv_buffer *buf; ++ u32 bytes_needed = 0; ++ u32 offset, size; ++ u32 UVoffset = 0, UVsize = 0; ++ int skip_bufs = s->q_predma.buffers; ++ int idx = s->sg_pending_size; ++ int rc; ++ ++ /* sanity checks */ ++ if (s->vdev == NULL) { ++ IVTV_DEBUG_WARN("Stream %s not started\n", s->name); ++ return -1; ++ } ++ if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { ++ IVTV_DEBUG_WARN("Stream %s not open\n", s->name); ++ return -1; ++ } ++ ++ /* determine offset, size and PTS for the various streams */ ++ switch (s->type) { ++ case IVTV_ENC_STREAM_TYPE_MPG: ++ offset = data[1]; ++ size = data[2]; ++ s->pending_pts = 0; ++ break; ++ ++ case IVTV_ENC_STREAM_TYPE_YUV: ++ offset = data[1]; ++ size = data[2]; ++ UVoffset = data[3]; ++ UVsize = data[4]; ++ s->pending_pts = ((u64) data[5] << 32) | data[6]; ++ break; ++ ++ case IVTV_ENC_STREAM_TYPE_PCM: ++ offset = data[1] + 12; ++ size = data[2] - 12; ++ s->pending_pts = read_dec(offset - 8) | ++ ((u64)(read_dec(offset - 12)) << 32); ++ if (itv->has_cx23415) ++ offset += IVTV_DECODER_OFFSET; ++ break; ++ ++ case IVTV_ENC_STREAM_TYPE_VBI: ++ size = itv->vbi.enc_size * itv->vbi.fpi; ++ offset = read_enc(itv->vbi.enc_start - 4) + 12; ++ if (offset == 12) { ++ IVTV_DEBUG_INFO("VBI offset == 0\n"); ++ return -1; ++ } ++ s->pending_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32); ++ break; ++ ++ case IVTV_DEC_STREAM_TYPE_VBI: ++ size = read_dec(itv->vbi.dec_start + 4) + 8; ++ offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start; ++ s->pending_pts = 0; ++ offset += IVTV_DECODER_OFFSET; ++ break; ++ default: ++ /* shouldn't happen */ ++ return -1; ++ } ++ ++ /* if this is the start of the DMA then fill in the magic cookie */ ++ if (s->sg_pending_size == 0 && ivtv_use_dma(s)) { ++ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || ++ s->type == IVTV_DEC_STREAM_TYPE_VBI)) { ++ s->pending_backup = read_dec(offset - IVTV_DECODER_OFFSET); ++ write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); ++ } ++ else { ++ s->pending_backup = read_enc(offset); ++ write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); ++ } ++ s->pending_offset = offset; ++ } ++ ++ bytes_needed = size; ++ if (s->type == IVTV_ENC_STREAM_TYPE_YUV) { ++ /* The size for the Y samples needs to be rounded upwards to a ++ multiple of the buf_size. The UV samples then start in the ++ next buffer. */ ++ bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size); ++ bytes_needed += UVsize; ++ } ++ ++ IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n", ++ ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); ++ ++ rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); ++ if (rc < 0) { /* Insufficient buffers */ ++ IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n", ++ bytes_needed, s->name); ++ return -1; ++ } ++ if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) { ++ IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); ++ IVTV_WARN("Cause: the application is not reading fast enough.\n"); ++ } ++ s->buffers_stolen = rc; ++ ++ /* got the buffers, now fill in sg_pending */ ++ buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); ++ memset(buf->buf, 0, 128); ++ list_for_each_entry(buf, &s->q_predma.list, list) { ++ if (skip_bufs-- > 0) ++ continue; ++ s->sg_pending[idx].dst = buf->dma_handle; ++ s->sg_pending[idx].src = offset; ++ s->sg_pending[idx].size = s->buf_size; ++ buf->bytesused = min(size, s->buf_size); ++ buf->dma_xfer_cnt = s->dma_xfer_cnt; ++ ++ s->q_predma.bytesused += buf->bytesused; ++ size -= buf->bytesused; ++ offset += s->buf_size; ++ ++ /* Sync SG buffers */ ++ ivtv_buf_sync_for_device(s, buf); ++ ++ if (size == 0) { /* YUV */ ++ /* process the UV section */ ++ offset = UVoffset; ++ size = UVsize; ++ } ++ idx++; ++ } ++ s->sg_pending_size = idx; ++ return 0; ++} ++ ++static void dma_post(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ struct ivtv_buffer *buf = NULL; ++ struct list_head *p; ++ u32 offset; ++ __le32 *u32buf; ++ int x = 0; ++ ++ IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", ++ s->name, s->dma_offset); ++ list_for_each(p, &s->q_dma.list) { ++ buf = list_entry(p, struct ivtv_buffer, list); ++ u32buf = (__le32 *)buf->buf; ++ ++ /* Sync Buffer */ ++ ivtv_buf_sync_for_cpu(s, buf); ++ ++ if (x == 0 && ivtv_use_dma(s)) { ++ offset = s->dma_last_offset; ++ if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) ++ { ++ for (offset = 0; offset < 64; offset++) { ++ if (u32buf[offset] == DMA_MAGIC_COOKIE) { ++ break; ++ } ++ } ++ offset *= 4; ++ if (offset == 256) { ++ IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); ++ offset = s->dma_last_offset; ++ } ++ if (s->dma_last_offset != offset) ++ IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset); ++ s->dma_last_offset = offset; ++ } ++ if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || ++ s->type == IVTV_DEC_STREAM_TYPE_VBI)) { ++ write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET); ++ } ++ else { ++ write_enc_sync(0, s->dma_offset); ++ } ++ if (offset) { ++ buf->bytesused -= offset; ++ memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset); ++ } ++ *u32buf = cpu_to_le32(s->dma_backup); ++ } ++ x++; ++ /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */ ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG || ++ s->type == IVTV_ENC_STREAM_TYPE_VBI) ++ buf->b_flags |= IVTV_F_B_NEED_BUF_SWAP; ++ } ++ if (buf) ++ buf->bytesused += s->dma_last_offset; ++ if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { ++ list_for_each_entry(buf, &s->q_dma.list, list) { ++ /* Parse and Groom VBI Data */ ++ s->q_dma.bytesused -= buf->bytesused; ++ ivtv_process_vbi_data(itv, buf, 0, s->type); ++ s->q_dma.bytesused += buf->bytesused; ++ } ++ if (s->fh == NULL) { ++ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); ++ return; ++ } ++ } ++ ++ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); ++ ++ if (s->type == IVTV_ENC_STREAM_TYPE_PCM && ++ itv->pcm_announce_callback != NULL) { ++ /* ++ * Set up the work handler to pass the data to ivtv-alsa. ++ * ++ * We just use q_full and let the work handler race with users ++ * making ivtv-fileops.c calls on the PCM device node. ++ * ++ * Users should not be using both the ALSA and V4L2 PCM audio ++ * capture interfaces at the same time. If the user does this, ++ * fragments of data will just go out each interface as they ++ * race for PCM data. ++ */ ++ set_bit(IVTV_F_I_WORK_HANDLER_PCM, &itv->i_flags); ++ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); ++ } ++ ++ if (s->fh) ++ wake_up(&s->waitq); ++} ++ ++void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) ++{ ++ struct ivtv *itv = s->itv; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ u8 frame = yi->draw_frame; ++ struct yuv_frame_info *f = &yi->new_frame_info[frame]; ++ struct ivtv_buffer *buf; ++ u32 y_size = 720 * ((f->src_h + 31) & ~31); ++ u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; ++ int y_done = 0; ++ int bytes_written = 0; ++ unsigned long flags = 0; ++ int idx = 0; ++ ++ IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); ++ ++ /* Insert buffer block for YUV if needed */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && f->offset_y) { ++ if (yi->blanking_dmaptr) { ++ s->sg_pending[idx].src = yi->blanking_dmaptr; ++ s->sg_pending[idx].dst = offset; ++ s->sg_pending[idx].size = 720 * 16; ++ } ++ offset += 720 * 16; ++ idx++; ++ } ++ ++ list_for_each_entry(buf, &s->q_predma.list, list) { ++ /* YUV UV Offset from Y Buffer */ ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && ++ (bytes_written + buf->bytesused) >= y_size) { ++ s->sg_pending[idx].src = buf->dma_handle; ++ s->sg_pending[idx].dst = offset; ++ s->sg_pending[idx].size = y_size - bytes_written; ++ offset = uv_offset; ++ if (s->sg_pending[idx].size != buf->bytesused) { ++ idx++; ++ s->sg_pending[idx].src = ++ buf->dma_handle + s->sg_pending[idx - 1].size; ++ s->sg_pending[idx].dst = offset; ++ s->sg_pending[idx].size = ++ buf->bytesused - s->sg_pending[idx - 1].size; ++ offset += s->sg_pending[idx].size; ++ } ++ y_done = 1; ++ } else { ++ s->sg_pending[idx].src = buf->dma_handle; ++ s->sg_pending[idx].dst = offset; ++ s->sg_pending[idx].size = buf->bytesused; ++ offset += buf->bytesused; ++ } ++ bytes_written += buf->bytesused; ++ ++ /* Sync SG buffers */ ++ ivtv_buf_sync_for_device(s, buf); ++ idx++; ++ } ++ s->sg_pending_size = idx; ++ ++ /* Sync Hardware SG List of buffers */ ++ ivtv_stream_sync_for_device(s); ++ if (lock) ++ spin_lock_irqsave(&itv->dma_reg_lock, flags); ++ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { ++ ivtv_dma_dec_start(s); ++ } ++ else { ++ set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); ++ } ++ if (lock) ++ spin_unlock_irqrestore(&itv->dma_reg_lock, flags); ++} ++ ++static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ ++ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src); ++ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst); ++ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000); ++ s->sg_processed++; ++ /* Sync Hardware SG List of buffers */ ++ ivtv_stream_sync_for_device(s); ++ write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR); ++ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); ++ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300); ++ add_timer(&itv->dma_timer); ++} ++ ++static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ ++ s->sg_dma->src = cpu_to_le32(s->sg_processing[s->sg_processed].src); ++ s->sg_dma->dst = cpu_to_le32(s->sg_processing[s->sg_processed].dst); ++ s->sg_dma->size = cpu_to_le32(s->sg_processing[s->sg_processed].size | 0x80000000); ++ s->sg_processed++; ++ /* Sync Hardware SG List of buffers */ ++ ivtv_stream_sync_for_device(s); ++ write_reg(s->sg_handle, IVTV_REG_DECDMAADDR); ++ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); ++ itv->dma_timer.expires = jiffies + msecs_to_jiffies(300); ++ add_timer(&itv->dma_timer); ++} ++ ++/* start the encoder DMA */ ++static void ivtv_dma_enc_start(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ int i; ++ ++ IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name); ++ ++ if (s->q_predma.bytesused) ++ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); ++ ++ if (ivtv_use_dma(s)) ++ s->sg_pending[s->sg_pending_size - 1].size += 256; ++ ++ /* If this is an MPEG stream, and VBI data is also pending, then append the ++ VBI DMA to the MPEG DMA and transfer both sets of data at once. ++ ++ VBI DMA is a second class citizen compared to MPEG and mixing them together ++ will confuse the firmware (the end of a VBI DMA is seen as the end of a ++ MPEG DMA, thus effectively dropping an MPEG frame). So instead we make ++ sure we only use the MPEG DMA to transfer the VBI DMA if both are in ++ use. This way no conflicts occur. */ ++ clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->sg_pending_size && ++ s->sg_pending_size + s_vbi->sg_pending_size <= s->buffers) { ++ ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused); ++ if (ivtv_use_dma(s_vbi)) ++ s_vbi->sg_pending[s_vbi->sg_pending_size - 1].size += 256; ++ for (i = 0; i < s_vbi->sg_pending_size; i++) { ++ s->sg_pending[s->sg_pending_size++] = s_vbi->sg_pending[i]; ++ } ++ s_vbi->dma_offset = s_vbi->pending_offset; ++ s_vbi->sg_pending_size = 0; ++ s_vbi->dma_xfer_cnt++; ++ set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); ++ IVTV_DEBUG_HI_DMA("include DMA for %s\n", s_vbi->name); ++ } ++ ++ s->dma_xfer_cnt++; ++ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size); ++ s->sg_processing_size = s->sg_pending_size; ++ s->sg_pending_size = 0; ++ s->sg_processed = 0; ++ s->dma_offset = s->pending_offset; ++ s->dma_backup = s->pending_backup; ++ s->dma_pts = s->pending_pts; ++ ++ if (ivtv_use_pio(s)) { ++ set_bit(IVTV_F_I_WORK_HANDLER_PIO, &itv->i_flags); ++ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); ++ set_bit(IVTV_F_I_PIO, &itv->i_flags); ++ itv->cur_pio_stream = s->type; ++ } ++ else { ++ itv->dma_retries = 0; ++ ivtv_dma_enc_start_xfer(s); ++ set_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = s->type; ++ } ++} ++ ++static void ivtv_dma_dec_start(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ ++ if (s->q_predma.bytesused) ++ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); ++ s->dma_xfer_cnt++; ++ memcpy(s->sg_processing, s->sg_pending, sizeof(struct ivtv_sg_host_element) * s->sg_pending_size); ++ s->sg_processing_size = s->sg_pending_size; ++ s->sg_pending_size = 0; ++ s->sg_processed = 0; ++ ++ IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name); ++ itv->dma_retries = 0; ++ ivtv_dma_dec_start_xfer(s); ++ set_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = s->type; ++} ++ ++static void ivtv_irq_dma_read(struct ivtv *itv) ++{ ++ struct ivtv_stream *s = NULL; ++ struct ivtv_buffer *buf; ++ int hw_stream_type = 0; ++ ++ IVTV_DEBUG_HI_IRQ("DEC DMA READ\n"); ++ ++ del_timer(&itv->dma_timer); ++ ++ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0) ++ return; ++ ++ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { ++ s = &itv->streams[itv->cur_dma_stream]; ++ ivtv_stream_sync_for_cpu(s); ++ ++ if (read_reg(IVTV_REG_DMASTATUS) & 0x14) { ++ IVTV_DEBUG_WARN("DEC DMA ERROR %x (xfer %d of %d, retry %d)\n", ++ read_reg(IVTV_REG_DMASTATUS), ++ s->sg_processed, s->sg_processing_size, itv->dma_retries); ++ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); ++ if (itv->dma_retries == 3) { ++ /* Too many retries, give up on this frame */ ++ itv->dma_retries = 0; ++ s->sg_processed = s->sg_processing_size; ++ } ++ else { ++ /* Retry, starting with the first xfer segment. ++ Just retrying the current segment is not sufficient. */ ++ s->sg_processed = 0; ++ itv->dma_retries++; ++ } ++ } ++ if (s->sg_processed < s->sg_processing_size) { ++ /* DMA next buffer */ ++ ivtv_dma_dec_start_xfer(s); ++ return; ++ } ++ if (s->type == IVTV_DEC_STREAM_TYPE_YUV) ++ hw_stream_type = 2; ++ IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); ++ ++ /* For some reason must kick the firmware, like PIO mode, ++ I think this tells the firmware we are done and the size ++ of the xfer so it can calculate what we need next. ++ I think we can do this part ourselves but would have to ++ fully calculate xfer info ourselves and not use interrupts ++ */ ++ ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused, ++ hw_stream_type); ++ ++ /* Free last DMA call */ ++ while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) { ++ ivtv_buf_sync_for_cpu(s, buf); ++ ivtv_enqueue(s, buf, &s->q_free); ++ } ++ wake_up(&s->waitq); ++ } ++ clear_bit(IVTV_F_I_UDMA, &itv->i_flags); ++ clear_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = -1; ++ wake_up(&itv->dma_waitq); ++} ++ ++static void ivtv_irq_enc_dma_complete(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv_stream *s; ++ ++ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data); ++ IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream); ++ ++ del_timer(&itv->dma_timer); ++ ++ if (itv->cur_dma_stream < 0) ++ return; ++ ++ s = &itv->streams[itv->cur_dma_stream]; ++ ivtv_stream_sync_for_cpu(s); ++ ++ if (data[0] & 0x18) { ++ IVTV_DEBUG_WARN("ENC DMA ERROR %x (offset %08x, xfer %d of %d, retry %d)\n", data[0], ++ s->dma_offset, s->sg_processed, s->sg_processing_size, itv->dma_retries); ++ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); ++ if (itv->dma_retries == 3) { ++ /* Too many retries, give up on this frame */ ++ itv->dma_retries = 0; ++ s->sg_processed = s->sg_processing_size; ++ } ++ else { ++ /* Retry, starting with the first xfer segment. ++ Just retrying the current segment is not sufficient. */ ++ s->sg_processed = 0; ++ itv->dma_retries++; ++ } ++ } ++ if (s->sg_processed < s->sg_processing_size) { ++ /* DMA next buffer */ ++ ivtv_dma_enc_start_xfer(s); ++ return; ++ } ++ clear_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = -1; ++ dma_post(s); ++ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) { ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ dma_post(s); ++ } ++ s->sg_processing_size = 0; ++ s->sg_processed = 0; ++ wake_up(&itv->dma_waitq); ++} ++ ++static void ivtv_irq_enc_pio_complete(struct ivtv *itv) ++{ ++ struct ivtv_stream *s; ++ ++ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS) { ++ itv->cur_pio_stream = -1; ++ return; ++ } ++ s = &itv->streams[itv->cur_pio_stream]; ++ IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name); ++ clear_bit(IVTV_F_I_PIO, &itv->i_flags); ++ itv->cur_pio_stream = -1; ++ dma_post(s); ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG) ++ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 0); ++ else if (s->type == IVTV_ENC_STREAM_TYPE_YUV) ++ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 1); ++ else if (s->type == IVTV_ENC_STREAM_TYPE_PCM) ++ ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, 2); ++ clear_bit(IVTV_F_I_PIO, &itv->i_flags); ++ if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) { ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ dma_post(s); ++ } ++ wake_up(&itv->dma_waitq); ++} ++ ++static void ivtv_irq_dma_err(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ u32 status; ++ ++ del_timer(&itv->dma_timer); ++ ++ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data); ++ status = read_reg(IVTV_REG_DMASTATUS); ++ IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1], ++ status, itv->cur_dma_stream); ++ /* ++ * We do *not* write back to the IVTV_REG_DMASTATUS register to ++ * clear the error status, if either the encoder write (0x02) or ++ * decoder read (0x01) bus master DMA operation do not indicate ++ * completed. We can race with the DMA engine, which may have ++ * transitioned to completed status *after* we read the register. ++ * Setting a IVTV_REG_DMASTATUS flag back to "busy" status, after the ++ * DMA engine has completed, will cause the DMA engine to stop working. ++ */ ++ status &= 0x3; ++ if (status == 0x3) ++ write_reg(status, IVTV_REG_DMASTATUS); ++ ++ if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && ++ itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) { ++ struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream]; ++ ++ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { ++ /* retry */ ++ /* ++ * FIXME - handle cases of DMA error similar to ++ * encoder below, except conditioned on status & 0x1 ++ */ ++ ivtv_dma_dec_start(s); ++ return; ++ } else { ++ if ((status & 0x2) == 0) { ++ /* ++ * CX2341x Bus Master DMA write is ongoing. ++ * Reset the timer and let it complete. ++ */ ++ itv->dma_timer.expires = ++ jiffies + msecs_to_jiffies(600); ++ add_timer(&itv->dma_timer); ++ return; ++ } ++ ++ if (itv->dma_retries < 3) { ++ /* ++ * CX2341x Bus Master DMA write has ended. ++ * Retry the write, starting with the first ++ * xfer segment. Just retrying the current ++ * segment is not sufficient. ++ */ ++ s->sg_processed = 0; ++ itv->dma_retries++; ++ ivtv_dma_enc_start_xfer(s); ++ return; ++ } ++ /* Too many retries, give up on this one */ ++ } ++ ++ } ++ if (test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { ++ ivtv_udma_start(itv); ++ return; ++ } ++ clear_bit(IVTV_F_I_UDMA, &itv->i_flags); ++ clear_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = -1; ++ wake_up(&itv->dma_waitq); ++} ++ ++static void ivtv_irq_enc_start_cap(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv_stream *s; ++ ++ /* Get DMA destination and size arguments from card */ ++ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data); ++ IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); ++ ++ if (data[0] > 2 || data[1] == 0 || data[2] == 0) { ++ IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n", ++ data[0], data[1], data[2]); ++ return; ++ } ++ s = &itv->streams[ivtv_stream_map[data[0]]]; ++ if (!stream_enc_dma_append(s, data)) { ++ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags); ++ } ++} ++ ++static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv_stream *s; ++ ++ IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n"); ++ s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; ++ ++ if (!stream_enc_dma_append(s, data)) ++ set_bit(ivtv_use_pio(s) ? IVTV_F_S_PIO_PENDING : IVTV_F_S_DMA_PENDING, &s->s_flags); ++} ++ ++static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; ++ ++ IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n"); ++ if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && ++ !stream_enc_dma_append(s, data)) { ++ set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags); ++ } ++} ++ ++static void ivtv_irq_dec_data_req(struct ivtv *itv) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv_stream *s; ++ ++ /* YUV or MPG */ ++ ++ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { ++ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); ++ itv->dma_data_req_size = ++ 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); ++ itv->dma_data_req_offset = data[1]; ++ if (atomic_read(&itv->yuv_info.next_dma_frame) >= 0) ++ ivtv_yuv_frame_complete(itv); ++ s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; ++ } ++ else { ++ ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data); ++ itv->dma_data_req_size = min_t(u32, data[2], 0x10000); ++ itv->dma_data_req_offset = data[1]; ++ s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; ++ } ++ IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, ++ itv->dma_data_req_offset, itv->dma_data_req_size); ++ if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) { ++ set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); ++ } ++ else { ++ if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) ++ ivtv_yuv_setup_stream_frame(itv); ++ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); ++ ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); ++ ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0); ++ } ++} ++ ++static void ivtv_irq_vsync(struct ivtv *itv) ++{ ++ /* The vsync interrupt is unusual in that it won't clear until ++ * the end of the first line for the current field, at which ++ * point it clears itself. This can result in repeated vsync ++ * interrupts, or a missed vsync. Read some of the registers ++ * to determine the line being displayed and ensure we handle ++ * one vsync per frame. ++ */ ++ unsigned int frame = read_reg(IVTV_REG_DEC_LINE_FIELD) & 1; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int last_dma_frame = atomic_read(&yi->next_dma_frame); ++ struct yuv_frame_info *f = &yi->new_frame_info[last_dma_frame]; ++ ++ if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); ++ ++ if (((frame ^ f->sync_field) == 0 && ++ ((itv->last_vsync_field & 1) ^ f->sync_field)) || ++ (frame != (itv->last_vsync_field & 1) && !f->interlaced)) { ++ int next_dma_frame = last_dma_frame; ++ ++ if (!(f->interlaced && f->delay && yi->fields_lapsed < 1)) { ++ if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&yi->next_fill_frame)) { ++ write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); ++ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); ++ write_reg(yuv_offset[next_dma_frame] >> 4, 0x834); ++ write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); ++ next_dma_frame = (next_dma_frame + 1) % IVTV_YUV_BUFFERS; ++ atomic_set(&yi->next_dma_frame, next_dma_frame); ++ yi->fields_lapsed = -1; ++ yi->running = 1; ++ } ++ } ++ } ++ if (frame != (itv->last_vsync_field & 1)) { ++ static const struct v4l2_event evtop = { ++ .type = V4L2_EVENT_VSYNC, ++ .u.vsync.field = V4L2_FIELD_TOP, ++ }; ++ static const struct v4l2_event evbottom = { ++ .type = V4L2_EVENT_VSYNC, ++ .u.vsync.field = V4L2_FIELD_BOTTOM, ++ }; ++ struct ivtv_stream *s = ivtv_get_output_stream(itv); ++ ++ itv->last_vsync_field += 1; ++ if (frame == 0) { ++ clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); ++ clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); ++ } ++ else { ++ set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); ++ } ++ if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { ++ set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); ++ wake_up(&itv->event_waitq); ++ if (s) ++ wake_up(&s->waitq); ++ } ++ if (s && s->vdev) ++ v4l2_event_queue(s->vdev, frame ? &evtop : &evbottom); ++ wake_up(&itv->vsync_waitq); ++ ++ /* Send VBI to saa7127 */ ++ if (frame && (itv->output_mode == OUT_PASSTHROUGH || ++ test_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags) || ++ test_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags) || ++ test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags))) { ++ set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags); ++ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); ++ } ++ ++ /* Check if we need to update the yuv registers */ ++ if (yi->running && (yi->yuv_forced_update || f->update)) { ++ if (!f->update) { ++ last_dma_frame = ++ (u8)(atomic_read(&yi->next_dma_frame) - ++ 1) % IVTV_YUV_BUFFERS; ++ f = &yi->new_frame_info[last_dma_frame]; ++ } ++ ++ if (f->src_w) { ++ yi->update_frame = last_dma_frame; ++ f->update = 0; ++ yi->yuv_forced_update = 0; ++ set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); ++ set_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags); ++ } ++ } ++ ++ yi->fields_lapsed++; ++ } ++} ++ ++#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_VBI_RE_INSERT) ++ ++irqreturn_t ivtv_irq_handler(int irq, void *dev_id) ++{ ++ struct ivtv *itv = (struct ivtv *)dev_id; ++ u32 combo; ++ u32 stat; ++ int i; ++ u8 vsync_force = 0; ++ ++ spin_lock(&itv->dma_reg_lock); ++ /* get contents of irq status register */ ++ stat = read_reg(IVTV_REG_IRQSTATUS); ++ ++ combo = ~itv->irqmask & stat; ++ ++ /* Clear out IRQ */ ++ if (combo) write_reg(combo, IVTV_REG_IRQSTATUS); ++ ++ if (0 == combo) { ++ /* The vsync interrupt is unusual and clears itself. If we ++ * took too long, we may have missed it. Do some checks ++ */ ++ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { ++ /* vsync is enabled, see if we're in a new field */ ++ if ((itv->last_vsync_field & 1) != ++ (read_reg(IVTV_REG_DEC_LINE_FIELD) & 1)) { ++ /* New field, looks like we missed it */ ++ IVTV_DEBUG_YUV("VSync interrupt missed %d\n", ++ read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16); ++ vsync_force = 1; ++ } ++ } ++ ++ if (!vsync_force) { ++ /* No Vsync expected, wasn't for us */ ++ spin_unlock(&itv->dma_reg_lock); ++ return IRQ_NONE; ++ } ++ } ++ ++ /* Exclude interrupts noted below from the output, otherwise the log is flooded with ++ these messages */ ++ if (combo & ~0xff6d0400) ++ IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); ++ ++ if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { ++ IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n"); ++ } ++ ++ if (combo & IVTV_IRQ_DMA_READ) { ++ ivtv_irq_dma_read(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { ++ ivtv_irq_enc_dma_complete(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_PIO_COMPLETE) { ++ ivtv_irq_enc_pio_complete(itv); ++ } ++ ++ if (combo & IVTV_IRQ_DMA_ERR) { ++ ivtv_irq_dma_err(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_START_CAP) { ++ ivtv_irq_enc_start_cap(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_VBI_CAP) { ++ ivtv_irq_enc_vbi_cap(itv); ++ } ++ ++ if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { ++ ivtv_irq_dec_vbi_reinsert(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_EOS) { ++ IVTV_DEBUG_IRQ("ENC EOS\n"); ++ set_bit(IVTV_F_I_EOS, &itv->i_flags); ++ wake_up(&itv->eos_waitq); ++ } ++ ++ if (combo & IVTV_IRQ_DEC_DATA_REQ) { ++ ivtv_irq_dec_data_req(itv); ++ } ++ ++ /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */ ++ if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { ++ ivtv_irq_vsync(itv); ++ } ++ ++ if (combo & IVTV_IRQ_ENC_VIM_RST) { ++ IVTV_DEBUG_IRQ("VIM RST\n"); ++ /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */ ++ } ++ ++ if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) { ++ IVTV_DEBUG_INFO("Stereo mode changed\n"); ++ } ++ ++ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) { ++ itv->irq_rr_idx++; ++ for (i = 0; i < IVTV_MAX_STREAMS; i++) { ++ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS; ++ struct ivtv_stream *s = &itv->streams[idx]; ++ ++ if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) ++ continue; ++ if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) ++ ivtv_dma_dec_start(s); ++ else ++ ivtv_dma_enc_start(s); ++ break; ++ } ++ ++ if (i == IVTV_MAX_STREAMS && ++ test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) ++ ivtv_udma_start(itv); ++ } ++ ++ if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) { ++ itv->irq_rr_idx++; ++ for (i = 0; i < IVTV_MAX_STREAMS; i++) { ++ int idx = (i + itv->irq_rr_idx) % IVTV_MAX_STREAMS; ++ struct ivtv_stream *s = &itv->streams[idx]; ++ ++ if (!test_and_clear_bit(IVTV_F_S_PIO_PENDING, &s->s_flags)) ++ continue; ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type < IVTV_DEC_STREAM_TYPE_MPG) ++ ivtv_dma_enc_start(s); ++ break; ++ } ++ } ++ ++ if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) { ++ queue_kthread_work(&itv->irq_worker, &itv->irq_work); ++ } ++ ++ spin_unlock(&itv->dma_reg_lock); ++ ++ /* If we've just handled a 'forced' vsync, it's safest to say it ++ * wasn't ours. Another device may have triggered it at just ++ * the right time. ++ */ ++ return vsync_force ? IRQ_NONE : IRQ_HANDLED; ++} ++ ++void ivtv_unfinished_dma(unsigned long arg) ++{ ++ struct ivtv *itv = (struct ivtv *)arg; ++ ++ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) ++ return; ++ IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); ++ ++ write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); ++ clear_bit(IVTV_F_I_UDMA, &itv->i_flags); ++ clear_bit(IVTV_F_I_DMA, &itv->i_flags); ++ itv->cur_dma_stream = -1; ++ wake_up(&itv->dma_waitq); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-irq.h b/drivers/media/pci/ivtv/ivtv-irq.h +new file mode 100644 +index 0000000..1e84433 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-irq.h +@@ -0,0 +1,53 @@ ++/* ++ interrupt handling ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_IRQ_H ++#define IVTV_IRQ_H ++ ++#define IVTV_IRQ_ENC_START_CAP (0x1 << 31) ++#define IVTV_IRQ_ENC_EOS (0x1 << 30) ++#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) ++#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) ++#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) ++#define IVTV_IRQ_ENC_PIO_COMPLETE (0x1 << 25) ++#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) ++#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) ++#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) ++#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) ++#define IVTV_IRQ_DMA_ERR (0x1 << 18) ++#define IVTV_IRQ_DMA_WRITE (0x1 << 17) ++#define IVTV_IRQ_DMA_READ (0x1 << 16) ++#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) ++ ++/* IRQ Masks */ ++#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|\ ++ IVTV_IRQ_DMA_READ|IVTV_IRQ_ENC_PIO_COMPLETE) ++ ++#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) ++#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) ++ ++irqreturn_t ivtv_irq_handler(int irq, void *dev_id); ++ ++void ivtv_irq_work_handler(struct kthread_work *work); ++void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock); ++void ivtv_unfinished_dma(unsigned long arg); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-mailbox.c b/drivers/media/pci/ivtv/ivtv-mailbox.c +new file mode 100644 +index 0000000..e3ce967 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-mailbox.c +@@ -0,0 +1,387 @@ ++/* ++ mailbox functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++ ++#include "ivtv-driver.h" ++#include "ivtv-mailbox.h" ++ ++/* Firmware mailbox flags*/ ++#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 ++#define IVTV_MBOX_DRIVER_DONE 0x00000002 ++#define IVTV_MBOX_DRIVER_BUSY 0x00000001 ++#define IVTV_MBOX_FREE 0x00000000 ++ ++/* Firmware mailbox standard timeout */ ++#define IVTV_API_STD_TIMEOUT 0x02000000 ++ ++#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */ ++#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */ ++#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */ ++#define API_DMA (1 << 3) /* DMA mailbox, has special handling */ ++#define API_HIGH_VOL (1 << 5) /* High volume command (i.e. called during encoding or decoding) */ ++#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */ ++#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */ ++#define API_NO_POLL (1 << 6) /* Avoid pointless polling */ ++ ++struct ivtv_api_info { ++ int flags; /* Flags, see above */ ++ const char *name; /* The name of the command */ ++}; ++ ++#define API_ENTRY(x, f) [x] = { (f), #x } ++ ++static const struct ivtv_api_info api_info[256] = { ++ /* MPEG encoder API */ ++ API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT | API_NO_POLL), ++ API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE), ++ API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA | API_HIGH_VOL), ++ API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE), ++ API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT), ++ API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB | API_HIGH_VOL), ++ API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE), ++ API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE), ++ API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT), ++ API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT), ++ API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT), ++ API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT), ++ /* Obsolete PULLDOWN API command */ ++ API_ENTRY(0xb1, API_CACHE), ++ ++ /* MPEG decoder API */ ++ API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT | API_NO_POLL), ++ API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT), ++ API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE), ++ API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA | API_HIGH_VOL), ++ API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT), ++ API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE), ++ API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE), ++ API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/), ++ API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE), ++ API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE), ++ API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT), ++ API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE), ++ ++ /* OSD API */ ++ API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE), ++ API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE), ++ API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT), ++ API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT), ++ API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE), ++ API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE), ++ API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT), ++ API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE) ++}; ++ ++static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb) ++{ ++ u32 flags = readl(&mbdata->mbox[mb].flags); ++ int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE); ++ ++ /* if the mailbox is free, then try to claim it */ ++ if (is_free && !test_and_set_bit(mb, &mbdata->busy)) { ++ write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags); ++ return 1; ++ } ++ return 0; ++} ++ ++/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not ++ attempted here. */ ++static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags) ++{ ++ unsigned long then = jiffies; ++ int i, mb; ++ int max_mbox = mbdata->max_mbox; ++ int retries = 100; ++ ++ /* All slow commands use the same mailbox, serializing them and also ++ leaving the other mailbox free for simple fast commands. */ ++ if ((flags & API_FAST_RESULT) == API_RESULT) ++ max_mbox = 1; ++ ++ /* find free non-DMA mailbox */ ++ for (i = 0; i < retries; i++) { ++ for (mb = 1; mb <= max_mbox; mb++) ++ if (try_mailbox(itv, mbdata, mb)) ++ return mb; ++ ++ /* Sleep before a retry, if not atomic */ ++ if (!(flags & API_NO_WAIT_MB)) { ++ if (time_after(jiffies, ++ then + msecs_to_jiffies(10*retries))) ++ break; ++ ivtv_msleep_timeout(10, 0); ++ } ++ } ++ return -ENODEV; ++} ++ ++static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[]) ++{ ++ int i; ++ ++ write_sync(cmd, &mbox->cmd); ++ write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout); ++ ++ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) ++ write_sync(data[i], &mbox->data[i]); ++ ++ write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags); ++} ++ ++static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata) ++{ ++ int i; ++ ++ for (i = 0; i <= mbdata->max_mbox; i++) { ++ IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n", ++ i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags)); ++ write_sync(0, &mbdata->mbox[i].flags); ++ clear_bit(i, &mbdata->busy); ++ } ++} ++ ++static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) ++{ ++ struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; ++ volatile struct ivtv_mailbox __iomem *mbox; ++ int api_timeout = msecs_to_jiffies(1000); ++ int flags, mb, i; ++ unsigned long then; ++ ++ /* sanity checks */ ++ if (NULL == mbdata) { ++ IVTV_ERR("No mailbox allocated\n"); ++ return -ENODEV; ++ } ++ if (args < 0 || args > CX2341X_MBOX_MAX_DATA || ++ cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { ++ IVTV_ERR("Invalid MB call: cmd = 0x%02x, args = %d\n", cmd, args); ++ return -EINVAL; ++ } ++ ++ if (api_info[cmd].flags & API_HIGH_VOL) { ++ IVTV_DEBUG_HI_MB("MB Call: %s\n", api_info[cmd].name); ++ } ++ else { ++ IVTV_DEBUG_MB("MB Call: %s\n", api_info[cmd].name); ++ } ++ ++ /* clear possibly uninitialized part of data array */ ++ for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) ++ data[i] = 0; ++ ++ /* If this command was issued within the last 30 minutes and with identical ++ data, then just return 0 as there is no need to issue this command again. ++ Just an optimization to prevent unnecessary use of mailboxes. */ ++ if (itv->api_cache[cmd].last_jiffies && ++ time_before(jiffies, ++ itv->api_cache[cmd].last_jiffies + ++ msecs_to_jiffies(1800000)) && ++ !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { ++ itv->api_cache[cmd].last_jiffies = jiffies; ++ return 0; ++ } ++ ++ flags = api_info[cmd].flags; ++ ++ if (flags & API_DMA) { ++ for (i = 0; i < 100; i++) { ++ mb = i % (mbdata->max_mbox + 1); ++ if (try_mailbox(itv, mbdata, mb)) { ++ write_mailbox(&mbdata->mbox[mb], cmd, args, data); ++ clear_bit(mb, &mbdata->busy); ++ return 0; ++ } ++ IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", ++ api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); ++ } ++ IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); ++ clear_all_mailboxes(itv, mbdata); ++ return -EBUSY; ++ } ++ ++ if ((flags & API_FAST_RESULT) == API_FAST_RESULT) ++ api_timeout = msecs_to_jiffies(100); ++ ++ mb = get_mailbox(itv, mbdata, flags); ++ if (mb < 0) { ++ IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); ++ clear_all_mailboxes(itv, mbdata); ++ return -EBUSY; ++ } ++ mbox = &mbdata->mbox[mb]; ++ write_mailbox(mbox, cmd, args, data); ++ if (flags & API_CACHE) { ++ memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); ++ itv->api_cache[cmd].last_jiffies = jiffies; ++ } ++ if ((flags & API_RESULT) == 0) { ++ clear_bit(mb, &mbdata->busy); ++ return 0; ++ } ++ ++ /* Get results */ ++ then = jiffies; ++ ++ if (!(flags & API_NO_POLL)) { ++ /* First try to poll, then switch to delays */ ++ for (i = 0; i < 100; i++) { ++ if (readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE) ++ break; ++ } ++ } ++ while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { ++ if (time_after(jiffies, then + api_timeout)) { ++ IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); ++ /* reset the mailbox, but it is likely too late already */ ++ write_sync(0, &mbox->flags); ++ clear_bit(mb, &mbdata->busy); ++ return -EIO; ++ } ++ if (flags & API_NO_WAIT_RES) ++ mdelay(1); ++ else ++ ivtv_msleep_timeout(1, 0); ++ } ++ if (time_after(jiffies, then + msecs_to_jiffies(100))) ++ IVTV_DEBUG_WARN("%s took %u jiffies\n", ++ api_info[cmd].name, ++ jiffies_to_msecs(jiffies - then)); ++ ++ for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) ++ data[i] = readl(&mbox->data[i]); ++ write_sync(0, &mbox->flags); ++ clear_bit(mb, &mbdata->busy); ++ return 0; ++} ++ ++int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]) ++{ ++ int res = ivtv_api_call(itv, cmd, args, data); ++ ++ /* Allow a single retry, probably already too late though. ++ If there is no free mailbox then that is usually an indication ++ of a more serious problem. */ ++ return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res; ++} ++ ++int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) ++{ ++ return ivtv_api(priv, cmd, in, data); ++} ++ ++int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...) ++{ ++ va_list ap; ++ int i; ++ ++ va_start(ap, args); ++ for (i = 0; i < args; i++) { ++ data[i] = va_arg(ap, u32); ++ } ++ va_end(ap); ++ return ivtv_api(itv, cmd, args, data); ++} ++ ++int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ va_list ap; ++ int i; ++ ++ va_start(ap, args); ++ for (i = 0; i < args; i++) { ++ data[i] = va_arg(ap, u32); ++ } ++ va_end(ap); ++ return ivtv_api(itv, cmd, args, data); ++} ++ ++/* This one is for stuff that can't sleep.. irq handlers, etc.. */ ++void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, ++ int argc, u32 data[]) ++{ ++ volatile u32 __iomem *p = mbdata->mbox[mb].data; ++ int i; ++ for (i = 0; i < argc; i++, p++) ++ data[i] = readl(p); ++} ++ ++/* Wipe api cache */ ++void ivtv_mailbox_cache_invalidate(struct ivtv *itv) ++{ ++ int i; ++ for (i = 0; i < 256; i++) ++ itv->api_cache[i].last_jiffies = 0; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-mailbox.h b/drivers/media/pci/ivtv/ivtv-mailbox.h +new file mode 100644 +index 0000000..2c834d2 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-mailbox.h +@@ -0,0 +1,35 @@ ++/* ++ mailbox functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_MAILBOX_H ++#define IVTV_MAILBOX_H ++ ++#define IVTV_MBOX_DMA_END 8 ++#define IVTV_MBOX_DMA 9 ++ ++void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, ++ int argc, u32 data[]); ++int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); ++int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); ++int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); ++int ivtv_api_func(void *priv, u32 cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); ++void ivtv_mailbox_cache_invalidate(struct ivtv *itv); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-queue.c b/drivers/media/pci/ivtv/ivtv-queue.c +new file mode 100644 +index 0000000..7fde36e +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-queue.c +@@ -0,0 +1,297 @@ ++/* ++ buffer queues. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-queue.h" ++ ++int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes) ++{ ++ if (s->buf_size - buf->bytesused < copybytes) ++ copybytes = s->buf_size - buf->bytesused; ++ if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) { ++ return -EFAULT; ++ } ++ buf->bytesused += copybytes; ++ return copybytes; ++} ++ ++void ivtv_buf_swap(struct ivtv_buffer *buf) ++{ ++ int i; ++ ++ for (i = 0; i < buf->bytesused; i += 4) ++ swab32s((u32 *)(buf->buf + i)); ++} ++ ++void ivtv_queue_init(struct ivtv_queue *q) ++{ ++ INIT_LIST_HEAD(&q->list); ++ q->buffers = 0; ++ q->length = 0; ++ q->bytesused = 0; ++} ++ ++void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q) ++{ ++ unsigned long flags; ++ ++ /* clear the buffer if it is going to be enqueued to the free queue */ ++ if (q == &s->q_free) { ++ buf->bytesused = 0; ++ buf->readpos = 0; ++ buf->b_flags = 0; ++ buf->dma_xfer_cnt = 0; ++ } ++ spin_lock_irqsave(&s->qlock, flags); ++ list_add_tail(&buf->list, &q->list); ++ q->buffers++; ++ q->length += s->buf_size; ++ q->bytesused += buf->bytesused - buf->readpos; ++ spin_unlock_irqrestore(&s->qlock, flags); ++} ++ ++struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q) ++{ ++ struct ivtv_buffer *buf = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&s->qlock, flags); ++ if (!list_empty(&q->list)) { ++ buf = list_entry(q->list.next, struct ivtv_buffer, list); ++ list_del_init(q->list.next); ++ q->buffers--; ++ q->length -= s->buf_size; ++ q->bytesused -= buf->bytesused - buf->readpos; ++ } ++ spin_unlock_irqrestore(&s->qlock, flags); ++ return buf; ++} ++ ++static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from, ++ struct ivtv_queue *to, int clear) ++{ ++ struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list); ++ ++ list_move_tail(from->list.next, &to->list); ++ from->buffers--; ++ from->length -= s->buf_size; ++ from->bytesused -= buf->bytesused - buf->readpos; ++ /* special handling for q_free */ ++ if (clear) ++ buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0; ++ to->buffers++; ++ to->length += s->buf_size; ++ to->bytesused += buf->bytesused - buf->readpos; ++} ++ ++/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. ++ If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. ++ If 'steal' != NULL, then buffers may also taken from that queue if ++ needed, but only if 'from' is the free queue. ++ ++ The buffer is automatically cleared if it goes to the free queue. It is ++ also cleared if buffers need to be taken from the 'steal' queue and ++ the 'from' queue is the free queue. ++ ++ When 'from' is q_free, then needed_bytes is compared to the total ++ available buffer length, otherwise needed_bytes is compared to the ++ bytesused value. For the 'steal' queue the total available buffer ++ length is always used. ++ ++ -ENOMEM is returned if the buffers could not be obtained, 0 if all ++ buffers where obtained from the 'from' list and if non-zero then ++ the number of stolen buffers is returned. */ ++int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, ++ struct ivtv_queue *to, int needed_bytes) ++{ ++ unsigned long flags; ++ int rc = 0; ++ int from_free = from == &s->q_free; ++ int to_free = to == &s->q_free; ++ int bytes_available, bytes_steal; ++ ++ spin_lock_irqsave(&s->qlock, flags); ++ if (needed_bytes == 0) { ++ from_free = 1; ++ needed_bytes = from->length; ++ } ++ ++ bytes_available = from_free ? from->length : from->bytesused; ++ bytes_steal = (from_free && steal) ? steal->length : 0; ++ ++ if (bytes_available + bytes_steal < needed_bytes) { ++ spin_unlock_irqrestore(&s->qlock, flags); ++ return -ENOMEM; ++ } ++ while (bytes_available < needed_bytes) { ++ struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list); ++ u16 dma_xfer_cnt = buf->dma_xfer_cnt; ++ ++ /* move buffers from the tail of the 'steal' queue to the tail of the ++ 'from' queue. Always copy all the buffers with the same dma_xfer_cnt ++ value, this ensures that you do not end up with partial frame data ++ if one frame is stored in multiple buffers. */ ++ while (dma_xfer_cnt == buf->dma_xfer_cnt) { ++ list_move_tail(steal->list.prev, &from->list); ++ rc++; ++ steal->buffers--; ++ steal->length -= s->buf_size; ++ steal->bytesused -= buf->bytesused - buf->readpos; ++ buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0; ++ from->buffers++; ++ from->length += s->buf_size; ++ bytes_available += s->buf_size; ++ if (list_empty(&steal->list)) ++ break; ++ buf = list_entry(steal->list.prev, struct ivtv_buffer, list); ++ } ++ } ++ if (from_free) { ++ u32 old_length = to->length; ++ ++ while (to->length - old_length < needed_bytes) { ++ ivtv_queue_move_buf(s, from, to, 1); ++ } ++ } ++ else { ++ u32 old_bytesused = to->bytesused; ++ ++ while (to->bytesused - old_bytesused < needed_bytes) { ++ ivtv_queue_move_buf(s, from, to, to_free); ++ } ++ } ++ spin_unlock_irqrestore(&s->qlock, flags); ++ return rc; ++} ++ ++void ivtv_flush_queues(struct ivtv_stream *s) ++{ ++ ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0); ++ ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0); ++ ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); ++ ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); ++} ++ ++int ivtv_stream_alloc(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers; ++ int i; ++ ++ if (s->buffers == 0) ++ return 0; ++ ++ IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n", ++ s->dma != PCI_DMA_NONE ? "DMA " : "", ++ s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); ++ ++ s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN); ++ if (s->sg_pending == NULL) { ++ IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name); ++ return -ENOMEM; ++ } ++ s->sg_pending_size = 0; ++ ++ s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN); ++ if (s->sg_processing == NULL) { ++ IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name); ++ kfree(s->sg_pending); ++ s->sg_pending = NULL; ++ return -ENOMEM; ++ } ++ s->sg_processing_size = 0; ++ ++ s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element), ++ GFP_KERNEL|__GFP_NOWARN); ++ if (s->sg_dma == NULL) { ++ IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name); ++ kfree(s->sg_pending); ++ s->sg_pending = NULL; ++ kfree(s->sg_processing); ++ s->sg_processing = NULL; ++ return -ENOMEM; ++ } ++ if (ivtv_might_use_dma(s)) { ++ s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, ++ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); ++ ivtv_stream_sync_for_cpu(s); ++ } ++ ++ /* allocate stream buffers. Initially all buffers are in q_free. */ ++ for (i = 0; i < s->buffers; i++) { ++ struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), ++ GFP_KERNEL|__GFP_NOWARN); ++ ++ if (buf == NULL) ++ break; ++ buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN); ++ if (buf->buf == NULL) { ++ kfree(buf); ++ break; ++ } ++ INIT_LIST_HEAD(&buf->list); ++ if (ivtv_might_use_dma(s)) { ++ buf->dma_handle = pci_map_single(s->itv->pdev, ++ buf->buf, s->buf_size + 256, s->dma); ++ ivtv_buf_sync_for_cpu(s, buf); ++ } ++ ivtv_enqueue(s, buf, &s->q_free); ++ } ++ if (i == s->buffers) ++ return 0; ++ IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name); ++ ivtv_stream_free(s); ++ return -ENOMEM; ++} ++ ++void ivtv_stream_free(struct ivtv_stream *s) ++{ ++ struct ivtv_buffer *buf; ++ ++ /* move all buffers to q_free */ ++ ivtv_flush_queues(s); ++ ++ /* empty q_free */ ++ while ((buf = ivtv_dequeue(s, &s->q_free))) { ++ if (ivtv_might_use_dma(s)) ++ pci_unmap_single(s->itv->pdev, buf->dma_handle, ++ s->buf_size + 256, s->dma); ++ kfree(buf->buf); ++ kfree(buf); ++ } ++ ++ /* Free SG Array/Lists */ ++ if (s->sg_dma != NULL) { ++ if (s->sg_handle != IVTV_DMA_UNMAPPED) { ++ pci_unmap_single(s->itv->pdev, s->sg_handle, ++ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); ++ s->sg_handle = IVTV_DMA_UNMAPPED; ++ } ++ kfree(s->sg_pending); ++ kfree(s->sg_processing); ++ kfree(s->sg_dma); ++ s->sg_pending = NULL; ++ s->sg_processing = NULL; ++ s->sg_dma = NULL; ++ s->sg_pending_size = 0; ++ s->sg_processing_size = 0; ++ } ++} +diff --git a/drivers/media/pci/ivtv/ivtv-queue.h b/drivers/media/pci/ivtv/ivtv-queue.h +new file mode 100644 +index 0000000..9123383 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-queue.h +@@ -0,0 +1,96 @@ ++/* ++ buffer queues. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_QUEUE_H ++#define IVTV_QUEUE_H ++ ++#define IVTV_DMA_UNMAPPED ((u32) -1) ++#define SLICED_VBI_PIO 0 ++ ++/* ivtv_buffer utility functions */ ++ ++static inline int ivtv_might_use_pio(struct ivtv_stream *s) ++{ ++ return s->dma == PCI_DMA_NONE || (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI); ++} ++ ++static inline int ivtv_use_pio(struct ivtv_stream *s) ++{ ++ struct ivtv *itv = s->itv; ++ ++ return s->dma == PCI_DMA_NONE || ++ (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); ++} ++ ++static inline int ivtv_might_use_dma(struct ivtv_stream *s) ++{ ++ return s->dma != PCI_DMA_NONE; ++} ++ ++static inline int ivtv_use_dma(struct ivtv_stream *s) ++{ ++ return !ivtv_use_pio(s); ++} ++ ++static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) ++{ ++ if (ivtv_use_dma(s)) ++ pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle, ++ s->buf_size + 256, s->dma); ++} ++ ++static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) ++{ ++ if (ivtv_use_dma(s)) ++ pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle, ++ s->buf_size + 256, s->dma); ++} ++ ++int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes); ++void ivtv_buf_swap(struct ivtv_buffer *buf); ++ ++/* ivtv_queue utility functions */ ++void ivtv_queue_init(struct ivtv_queue *q); ++void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q); ++struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q); ++int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, ++ struct ivtv_queue *to, int needed_bytes); ++void ivtv_flush_queues(struct ivtv_stream *s); ++ ++/* ivtv_stream utility functions */ ++int ivtv_stream_alloc(struct ivtv_stream *s); ++void ivtv_stream_free(struct ivtv_stream *s); ++ ++static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) ++{ ++ if (ivtv_use_dma(s)) ++ pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle, ++ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); ++} ++ ++static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) ++{ ++ if (ivtv_use_dma(s)) ++ pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle, ++ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); ++} ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-routing.c b/drivers/media/pci/ivtv/ivtv-routing.c +new file mode 100644 +index 0000000..8898c56 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-routing.c +@@ -0,0 +1,119 @@ ++/* ++ Audio/video-routing-related ivtv functions. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-i2c.h" ++#include "ivtv-cards.h" ++#include "ivtv-gpio.h" ++#include "ivtv-routing.h" ++ ++#include ++#include ++#include ++#include ++ ++/* Selects the audio input and output according to the current ++ settings. */ ++void ivtv_audio_set_io(struct ivtv *itv) ++{ ++ const struct ivtv_card_audio_input *in; ++ u32 input, output = 0; ++ ++ /* Determine which input to use */ ++ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) ++ in = &itv->card->radio_input; ++ else ++ in = &itv->card->audio_inputs[itv->audio_input]; ++ ++ /* handle muxer chips */ ++ input = in->muxer_input; ++ if (itv->card->hw_muxer & IVTV_HW_M52790) ++ output = M52790_OUT_STEREO; ++ v4l2_subdev_call(itv->sd_muxer, audio, s_routing, ++ input, output, 0); ++ ++ input = in->audio_input; ++ output = 0; ++ if (itv->card->hw_audio & IVTV_HW_MSP34XX) ++ output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); ++ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, ++ input, output, 0); ++} ++ ++/* Selects the video input and output according to the current ++ settings. */ ++void ivtv_video_set_io(struct ivtv *itv) ++{ ++ int inp = itv->active_input; ++ u32 input; ++ u32 type; ++ ++ v4l2_subdev_call(itv->sd_video, video, s_routing, ++ itv->card->video_inputs[inp].video_input, 0, 0); ++ ++ type = itv->card->video_inputs[inp].video_type; ++ ++ if (type == IVTV_CARD_INPUT_VID_TUNER) { ++ input = 0; /* Tuner */ ++ } else if (type < IVTV_CARD_INPUT_COMPOSITE1) { ++ input = 2; /* S-Video */ ++ } else { ++ input = 1; /* Composite */ ++ } ++ ++ if (itv->card->hw_video & IVTV_HW_GPIO) ++ ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, ++ input, 0, 0); ++ ++ if (itv->card->hw_video & IVTV_HW_UPD64031A) { ++ if (type == IVTV_CARD_INPUT_VID_TUNER || ++ type >= IVTV_CARD_INPUT_COMPOSITE1) { ++ /* Composite: GR on, connect to 3DYCS */ ++ input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; ++ } else { ++ /* S-Video: GR bypassed, turn it off */ ++ input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; ++ } ++ input |= itv->card->gr_config; ++ ++ ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, ++ input, 0, 0); ++ } ++ ++ if (itv->card->hw_video & IVTV_HW_UPD6408X) { ++ input = UPD64083_YCS_MODE; ++ if (type > IVTV_CARD_INPUT_VID_TUNER && ++ type < IVTV_CARD_INPUT_COMPOSITE1) { ++ /* S-Video uses YCNR mode and internal Y-ADC, the ++ upd64031a is not used. */ ++ input |= UPD64083_YCNR_MODE; ++ } ++ else if (itv->card->hw_video & IVTV_HW_UPD64031A) { ++ /* Use upd64031a output for tuner and ++ composite(CX23416GYC only) inputs */ ++ if (type == IVTV_CARD_INPUT_VID_TUNER || ++ itv->card->type == IVTV_CARD_CX23416GYC) { ++ input |= UPD64083_EXT_Y_ADC; ++ } ++ } ++ ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, ++ input, 0, 0); ++ } ++} +diff --git a/drivers/media/pci/ivtv/ivtv-routing.h b/drivers/media/pci/ivtv/ivtv-routing.h +new file mode 100644 +index 0000000..c72a973 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-routing.h +@@ -0,0 +1,27 @@ ++/* ++ Audio/video-routing-related ivtv functions. ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_ROUTING_H ++#define IVTV_ROUTING_H ++ ++void ivtv_audio_set_io(struct ivtv *itv); ++void ivtv_video_set_io(struct ivtv *itv); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-streams.c b/drivers/media/pci/ivtv/ivtv-streams.c +new file mode 100644 +index 0000000..70dad58 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-streams.c +@@ -0,0 +1,1039 @@ ++/* ++ init/start/stop/exit stream functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* License: GPL ++ * Author: Kevin Thayer ++ * ++ * This file will hold API related functions, both internal (firmware api) ++ * and external (v4l2, etc) ++ * ++ * ----- ++ * MPG600/MPG160 support by T.Adachi ++ * and Takeru KOMORIYA ++ * ++ * AVerMedia M179 GPIO info by Chris Pinkham ++ * using information provided by Jiun-Kuei Jung @ AVerMedia. ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-fileops.h" ++#include "ivtv-queue.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-irq.h" ++#include "ivtv-yuv.h" ++#include "ivtv-cards.h" ++#include "ivtv-streams.h" ++#include "ivtv-firmware.h" ++#include ++ ++static const struct v4l2_file_operations ivtv_v4l2_enc_fops = { ++ .owner = THIS_MODULE, ++ .read = ivtv_v4l2_read, ++ .write = ivtv_v4l2_write, ++ .open = ivtv_v4l2_open, ++ .unlocked_ioctl = video_ioctl2, ++ .release = ivtv_v4l2_close, ++ .poll = ivtv_v4l2_enc_poll, ++}; ++ ++static const struct v4l2_file_operations ivtv_v4l2_dec_fops = { ++ .owner = THIS_MODULE, ++ .read = ivtv_v4l2_read, ++ .write = ivtv_v4l2_write, ++ .open = ivtv_v4l2_open, ++ .unlocked_ioctl = video_ioctl2, ++ .release = ivtv_v4l2_close, ++ .poll = ivtv_v4l2_dec_poll, ++}; ++ ++static const struct v4l2_file_operations ivtv_v4l2_radio_fops = { ++ .owner = THIS_MODULE, ++ .open = ivtv_v4l2_open, ++ .unlocked_ioctl = video_ioctl2, ++ .release = ivtv_v4l2_close, ++ .poll = ivtv_v4l2_enc_poll, ++}; ++ ++#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ ++#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ ++#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ ++#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ ++#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ ++#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ ++ ++static struct { ++ const char *name; ++ int vfl_type; ++ int num_offset; ++ int dma, pio; ++ u32 v4l2_caps; ++ const struct v4l2_file_operations *fops; ++} ivtv_stream_info[] = { ++ { /* IVTV_ENC_STREAM_TYPE_MPG */ ++ "encoder MPG", ++ VFL_TYPE_GRABBER, 0, ++ PCI_DMA_FROMDEVICE, 0, ++ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | ++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_enc_fops ++ }, ++ { /* IVTV_ENC_STREAM_TYPE_YUV */ ++ "encoder YUV", ++ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, ++ PCI_DMA_FROMDEVICE, 0, ++ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | ++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_enc_fops ++ }, ++ { /* IVTV_ENC_STREAM_TYPE_VBI */ ++ "encoder VBI", ++ VFL_TYPE_VBI, 0, ++ PCI_DMA_FROMDEVICE, 0, ++ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER | ++ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_enc_fops ++ }, ++ { /* IVTV_ENC_STREAM_TYPE_PCM */ ++ "encoder PCM", ++ VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, ++ PCI_DMA_FROMDEVICE, 0, ++ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_enc_fops ++ }, ++ { /* IVTV_ENC_STREAM_TYPE_RAD */ ++ "encoder radio", ++ VFL_TYPE_RADIO, 0, ++ PCI_DMA_NONE, 1, ++ V4L2_CAP_RADIO | V4L2_CAP_TUNER, ++ &ivtv_v4l2_radio_fops ++ }, ++ { /* IVTV_DEC_STREAM_TYPE_MPG */ ++ "decoder MPG", ++ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, ++ PCI_DMA_TODEVICE, 0, ++ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_dec_fops ++ }, ++ { /* IVTV_DEC_STREAM_TYPE_VBI */ ++ "decoder VBI", ++ VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, ++ PCI_DMA_NONE, 1, ++ V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_enc_fops ++ }, ++ { /* IVTV_DEC_STREAM_TYPE_VOUT */ ++ "decoder VOUT", ++ VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, ++ PCI_DMA_NONE, 1, ++ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_dec_fops ++ }, ++ { /* IVTV_DEC_STREAM_TYPE_YUV */ ++ "decoder YUV", ++ VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, ++ PCI_DMA_TODEVICE, 0, ++ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE, ++ &ivtv_v4l2_dec_fops ++ } ++}; ++ ++static void ivtv_stream_init(struct ivtv *itv, int type) ++{ ++ struct ivtv_stream *s = &itv->streams[type]; ++ struct video_device *vdev = s->vdev; ++ ++ /* we need to keep vdev, so restore it afterwards */ ++ memset(s, 0, sizeof(*s)); ++ s->vdev = vdev; ++ ++ /* initialize ivtv_stream fields */ ++ s->itv = itv; ++ s->type = type; ++ s->name = ivtv_stream_info[type].name; ++ s->caps = ivtv_stream_info[type].v4l2_caps; ++ ++ if (ivtv_stream_info[type].pio) ++ s->dma = PCI_DMA_NONE; ++ else ++ s->dma = ivtv_stream_info[type].dma; ++ s->buf_size = itv->stream_buf_size[type]; ++ if (s->buf_size) ++ s->buffers = (itv->options.kilobytes[type] * 1024 + s->buf_size - 1) / s->buf_size; ++ spin_lock_init(&s->qlock); ++ init_waitqueue_head(&s->waitq); ++ s->sg_handle = IVTV_DMA_UNMAPPED; ++ ivtv_queue_init(&s->q_free); ++ ivtv_queue_init(&s->q_full); ++ ivtv_queue_init(&s->q_dma); ++ ivtv_queue_init(&s->q_predma); ++ ivtv_queue_init(&s->q_io); ++} ++ ++static int ivtv_prep_dev(struct ivtv *itv, int type) ++{ ++ struct ivtv_stream *s = &itv->streams[type]; ++ int num_offset = ivtv_stream_info[type].num_offset; ++ int num = itv->instance + ivtv_first_minor + num_offset; ++ ++ /* These four fields are always initialized. If vdev == NULL, then ++ this stream is not in use. In that case no other fields but these ++ four can be used. */ ++ s->vdev = NULL; ++ s->itv = itv; ++ s->type = type; ++ s->name = ivtv_stream_info[type].name; ++ ++ /* Check whether the radio is supported */ ++ if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO)) ++ return 0; ++ if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return 0; ++ ++ /* User explicitly selected 0 buffers for these streams, so don't ++ create them. */ ++ if (ivtv_stream_info[type].dma != PCI_DMA_NONE && ++ itv->options.kilobytes[type] == 0) { ++ IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name); ++ return 0; ++ } ++ ++ ivtv_stream_init(itv, type); ++ ++ /* allocate and initialize the v4l2 video device structure */ ++ s->vdev = video_device_alloc(); ++ if (s->vdev == NULL) { ++ IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); ++ return -ENOMEM; ++ } ++ ++ snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s", ++ itv->v4l2_dev.name, s->name); ++ ++ s->vdev->num = num; ++ s->vdev->v4l2_dev = &itv->v4l2_dev; ++ if (ivtv_stream_info[type].v4l2_caps & ++ (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_SLICED_VBI_OUTPUT)) ++ s->vdev->vfl_dir = VFL_DIR_TX; ++ s->vdev->fops = ivtv_stream_info[type].fops; ++ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler; ++ s->vdev->release = video_device_release; ++ s->vdev->tvnorms = V4L2_STD_ALL; ++ s->vdev->lock = &itv->serialize_lock; ++ if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { ++ v4l2_disable_ioctl(s->vdev, VIDIOC_S_AUDIO); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_G_AUDIO); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMAUDIO); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_ENUMINPUT); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_S_INPUT); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_G_INPUT); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_S_FREQUENCY); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_G_FREQUENCY); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_S_TUNER); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_G_TUNER); ++ v4l2_disable_ioctl(s->vdev, VIDIOC_S_STD); ++ } ++ set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ++ ivtv_set_funcs(s->vdev); ++ return 0; ++} ++ ++/* Initialize v4l2 variables and prepare v4l2 devices */ ++int ivtv_streams_setup(struct ivtv *itv) ++{ ++ int type; ++ ++ /* Setup V4L2 Devices */ ++ for (type = 0; type < IVTV_MAX_STREAMS; type++) { ++ /* Prepare device */ ++ if (ivtv_prep_dev(itv, type)) ++ break; ++ ++ if (itv->streams[type].vdev == NULL) ++ continue; ++ ++ /* Allocate Stream */ ++ if (ivtv_stream_alloc(&itv->streams[type])) ++ break; ++ } ++ if (type == IVTV_MAX_STREAMS) ++ return 0; ++ ++ /* One or more streams could not be initialized. Clean 'em all up. */ ++ ivtv_streams_cleanup(itv, 0); ++ return -ENOMEM; ++} ++ ++static int ivtv_reg_dev(struct ivtv *itv, int type) ++{ ++ struct ivtv_stream *s = &itv->streams[type]; ++ int vfl_type = ivtv_stream_info[type].vfl_type; ++ const char *name; ++ int num; ++ ++ if (s->vdev == NULL) ++ return 0; ++ ++ num = s->vdev->num; ++ /* card number + user defined offset + device offset */ ++ if (type != IVTV_ENC_STREAM_TYPE_MPG) { ++ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; ++ ++ if (s_mpg->vdev) ++ num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset; ++ } ++ video_set_drvdata(s->vdev, s); ++ ++ /* Register device. First try the desired minor, then any free one. */ ++ if (video_register_device_no_warn(s->vdev, vfl_type, num)) { ++ IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", ++ s->name, num); ++ video_device_release(s->vdev); ++ s->vdev = NULL; ++ return -ENOMEM; ++ } ++ name = video_device_node_name(s->vdev); ++ ++ switch (vfl_type) { ++ case VFL_TYPE_GRABBER: ++ IVTV_INFO("Registered device %s for %s (%d kB)\n", ++ name, s->name, itv->options.kilobytes[type]); ++ break; ++ case VFL_TYPE_RADIO: ++ IVTV_INFO("Registered device %s for %s\n", ++ name, s->name); ++ break; ++ case VFL_TYPE_VBI: ++ if (itv->options.kilobytes[type]) ++ IVTV_INFO("Registered device %s for %s (%d kB)\n", ++ name, s->name, itv->options.kilobytes[type]); ++ else ++ IVTV_INFO("Registered device %s for %s\n", ++ name, s->name); ++ break; ++ } ++ return 0; ++} ++ ++/* Register v4l2 devices */ ++int ivtv_streams_register(struct ivtv *itv) ++{ ++ int type; ++ int err = 0; ++ ++ /* Register V4L2 devices */ ++ for (type = 0; type < IVTV_MAX_STREAMS; type++) ++ err |= ivtv_reg_dev(itv, type); ++ ++ if (err == 0) ++ return 0; ++ ++ /* One or more streams could not be initialized. Clean 'em all up. */ ++ ivtv_streams_cleanup(itv, 1); ++ return -ENOMEM; ++} ++ ++/* Unregister v4l2 devices */ ++void ivtv_streams_cleanup(struct ivtv *itv, int unregister) ++{ ++ int type; ++ ++ /* Teardown all streams */ ++ for (type = 0; type < IVTV_MAX_STREAMS; type++) { ++ struct video_device *vdev = itv->streams[type].vdev; ++ ++ itv->streams[type].vdev = NULL; ++ if (vdev == NULL) ++ continue; ++ ++ ivtv_stream_free(&itv->streams[type]); ++ /* Unregister or release device */ ++ if (unregister) ++ video_unregister_device(vdev); ++ else ++ video_device_release(vdev); ++ } ++} ++ ++static void ivtv_vbi_setup(struct ivtv *itv) ++{ ++ int raw = ivtv_raw_vbi(itv); ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ int lines; ++ int i; ++ ++ /* Reset VBI */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); ++ ++ /* setup VBI registers */ ++ if (raw) ++ v4l2_subdev_call(itv->sd_video, vbi, s_raw_fmt, &itv->vbi.in.fmt.vbi); ++ else ++ v4l2_subdev_call(itv->sd_video, vbi, s_sliced_fmt, &itv->vbi.in.fmt.sliced); ++ ++ /* determine number of lines and total number of VBI bytes. ++ A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 ++ The '- 1' byte is probably an unused U or V byte. Or something... ++ A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal ++ header, 42 data bytes + checksum (to be confirmed) */ ++ if (raw) { ++ lines = itv->vbi.count * 2; ++ } else { ++ lines = itv->is_60hz ? 24 : 38; ++ if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840)) ++ lines += 2; ++ } ++ ++ itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); ++ ++ /* Note: sliced vs raw flag doesn't seem to have any effect ++ TODO: check mode (0x02) value with older ivtv versions. */ ++ data[0] = raw | 0x02 | (0xbd << 8); ++ ++ /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ ++ data[1] = 1; ++ /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ ++ data[2] = raw ? 4 : 4 * (itv->vbi.raw_size / itv->vbi.enc_size); ++ /* The start/stop codes determine which VBI lines end up in the raw VBI data area. ++ The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line ++ is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) ++ code. These values for raw VBI are obtained from a driver disassembly. The sliced ++ start/stop codes was deduced from this, but they do not appear in the driver. ++ Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54. ++ However, I have no idea what these values are for. */ ++ if (itv->hw_flags & IVTV_HW_CX25840) { ++ /* Setup VBI for the cx25840 digitizer */ ++ if (raw) { ++ data[3] = 0x20602060; ++ data[4] = 0x30703070; ++ } else { ++ data[3] = 0xB0F0B0F0; ++ data[4] = 0xA0E0A0E0; ++ } ++ /* Lines per frame */ ++ data[5] = lines; ++ /* bytes per line */ ++ data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); ++ } else { ++ /* Setup VBI for the saa7115 digitizer */ ++ if (raw) { ++ data[3] = 0x25256262; ++ data[4] = 0x387F7F7F; ++ } else { ++ data[3] = 0xABABECEC; ++ data[4] = 0xB6F1F1F1; ++ } ++ /* Lines per frame */ ++ data[5] = lines; ++ /* bytes per line */ ++ data[6] = itv->vbi.enc_size / lines; ++ } ++ ++ IVTV_DEBUG_INFO( ++ "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n", ++ data[0], data[1], data[2], data[5], data[6]); ++ ++ ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data); ++ ++ /* returns the VBI encoder memory area. */ ++ itv->vbi.enc_start = data[2]; ++ itv->vbi.fpi = data[0]; ++ if (!itv->vbi.fpi) ++ itv->vbi.fpi = 1; ++ ++ IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d\n", ++ itv->vbi.enc_start, data[1], itv->vbi.fpi); ++ ++ /* select VBI lines. ++ Note that the sliced argument seems to have no effect. */ ++ for (i = 2; i <= 24; i++) { ++ int valid; ++ ++ if (itv->is_60hz) { ++ valid = i >= 10 && i < 22; ++ } else { ++ valid = i >= 6 && i < 24; ++ } ++ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1, ++ valid, 0 , 0, 0); ++ ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000, ++ valid, 0, 0, 0); ++ } ++ ++ /* Remaining VBI questions: ++ - Is it possible to select particular VBI lines only for inclusion in the MPEG ++ stream? Currently you can only get the first X lines. ++ - Is mixed raw and sliced VBI possible? ++ - What's the meaning of the raw/sliced flag? ++ - What's the meaning of params 2, 3 & 4 of the Select VBI command? */ ++} ++ ++int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv *itv = s->itv; ++ int captype = 0, subtype = 0; ++ int enable_passthrough = 0; ++ ++ if (s->vdev == NULL) ++ return -EINVAL; ++ ++ IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); ++ ++ switch (s->type) { ++ case IVTV_ENC_STREAM_TYPE_MPG: ++ captype = 0; ++ subtype = 3; ++ ++ /* Stop Passthrough */ ++ if (itv->output_mode == OUT_PASSTHROUGH) { ++ ivtv_passthrough_mode(itv, 0); ++ enable_passthrough = 1; ++ } ++ itv->mpg_data_received = itv->vbi_data_inserted = 0; ++ itv->dualwatch_jiffies = jiffies; ++ itv->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode); ++ itv->search_pack_header = 0; ++ break; ++ ++ case IVTV_ENC_STREAM_TYPE_YUV: ++ if (itv->output_mode == OUT_PASSTHROUGH) { ++ captype = 2; ++ subtype = 11; /* video+audio+decoder */ ++ break; ++ } ++ captype = 1; ++ subtype = 1; ++ break; ++ case IVTV_ENC_STREAM_TYPE_PCM: ++ captype = 1; ++ subtype = 2; ++ break; ++ case IVTV_ENC_STREAM_TYPE_VBI: ++ captype = 1; ++ subtype = 4; ++ ++ itv->vbi.frame = 0; ++ itv->vbi.inserted_frame = 0; ++ memset(itv->vbi.sliced_mpeg_size, ++ 0, sizeof(itv->vbi.sliced_mpeg_size)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ s->subtype = subtype; ++ s->buffers_stolen = 0; ++ ++ /* Clear Streamoff flags in case left from last capture */ ++ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ ++ if (atomic_read(&itv->capturing) == 0) { ++ int digitizer; ++ ++ /* Always use frame based mode. Experiments have demonstrated that byte ++ stream based mode results in dropped frames and corruption. Not often, ++ but occasionally. Many thanks go to Leonard Orb who spent a lot of ++ effort and time trying to trace the cause of the drop outs. */ ++ /* 1 frame per DMA */ ++ /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); ++ ++ /* Stuff from Windows, we don't know what it is */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); ++ /* According to the docs, this should be correct. However, this is ++ untested. I don't dare enable this without having tested it. ++ Only very few old cards actually have this hardware combination. ++ ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, ++ ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); ++ */ ++ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); ++ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); ++ ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); ++ ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); ++ ++ /* assign placeholder */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); ++ ++ if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) ++ digitizer = 0xF1; ++ else if (itv->card->hw_all & IVTV_HW_SAA7114) ++ digitizer = 0xEF; ++ else /* cx25840 */ ++ digitizer = 0x140; ++ ++ ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, digitizer, digitizer); ++ ++ /* Setup VBI */ ++ if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { ++ ivtv_vbi_setup(itv); ++ } ++ ++ /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ ++ ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); ++ itv->pgm_info_offset = data[0]; ++ itv->pgm_info_num = data[1]; ++ itv->pgm_info_write_idx = 0; ++ itv->pgm_info_read_idx = 0; ++ ++ IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", ++ itv->pgm_info_offset, itv->pgm_info_num); ++ ++ /* Setup API for Stream */ ++ cx2341x_handler_setup(&itv->cxhdl); ++ ++ /* mute if capturing radio */ ++ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) ++ ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, ++ 1 | (v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8)); ++ } ++ ++ /* Vsync Setup */ ++ if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { ++ /* event notification (on) */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); ++ } ++ ++ if (atomic_read(&itv->capturing) == 0) { ++ /* Clear all Pending Interrupts */ ++ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); ++ ++ clear_bit(IVTV_F_I_EOS, &itv->i_flags); ++ ++ cx2341x_handler_set_busy(&itv->cxhdl, 1); ++ ++ /* Initialize Digitizer for Capture */ ++ /* Avoid tinny audio problem - ensure audio clocks are going */ ++ v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); ++ /* Avoid unpredictable PCI bus hang - disable video clocks */ ++ v4l2_subdev_call(itv->sd_video, video, s_stream, 0); ++ ivtv_msleep_timeout(300, 0); ++ ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); ++ v4l2_subdev_call(itv->sd_video, video, s_stream, 1); ++ } ++ ++ /* begin_capture */ ++ if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) ++ { ++ IVTV_DEBUG_WARN( "Error starting capture!\n"); ++ return -EINVAL; ++ } ++ ++ /* Start Passthrough */ ++ if (enable_passthrough) { ++ ivtv_passthrough_mode(itv, 1); ++ } ++ ++ if (s->type == IVTV_ENC_STREAM_TYPE_VBI) ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); ++ else ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); ++ ++ /* you're live! sit back and await interrupts :) */ ++ atomic_inc(&itv->capturing); ++ return 0; ++} ++EXPORT_SYMBOL(ivtv_start_v4l2_encode_stream); ++ ++static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ struct ivtv *itv = s->itv; ++ int datatype; ++ u16 width; ++ u16 height; ++ ++ if (s->vdev == NULL) ++ return -EINVAL; ++ ++ IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); ++ ++ width = itv->cxhdl.width; ++ height = itv->cxhdl.height; ++ ++ /* set audio mode to left/stereo for dual/stereo mode. */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); ++ ++ /* set number of internal decoder buffers */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); ++ ++ /* prebuffering */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); ++ ++ /* extract from user packets */ ++ ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); ++ itv->vbi.dec_start = data[0]; ++ ++ IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", ++ itv->vbi.dec_start, data[1]); ++ ++ /* set decoder source settings */ ++ /* Data type: 0 = mpeg from host, ++ 1 = yuv from encoder, ++ 2 = yuv_from_host */ ++ switch (s->type) { ++ case IVTV_DEC_STREAM_TYPE_YUV: ++ if (itv->output_mode == OUT_PASSTHROUGH) { ++ datatype = 1; ++ } else { ++ /* Fake size to avoid switching video standard */ ++ datatype = 2; ++ width = 720; ++ height = itv->is_out_50hz ? 576 : 480; ++ } ++ IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); ++ break; ++ case IVTV_DEC_STREAM_TYPE_MPG: ++ default: ++ datatype = 0; ++ break; ++ } ++ if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, ++ width, height, itv->cxhdl.audio_properties)) { ++ IVTV_DEBUG_WARN("Couldn't initialize decoder source\n"); ++ } ++ ++ /* Decoder sometimes dies here, so wait a moment */ ++ ivtv_msleep_timeout(10, 0); ++ ++ /* Known failure point for firmware, so check */ ++ return ivtv_firmware_check(itv, "ivtv_setup_v4l2_decode_stream"); ++} ++ ++int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) ++{ ++ struct ivtv *itv = s->itv; ++ int rc; ++ ++ if (s->vdev == NULL) ++ return -EINVAL; ++ ++ if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) ++ return 0; /* already started */ ++ ++ IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); ++ ++ rc = ivtv_setup_v4l2_decode_stream(s); ++ if (rc < 0) { ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ return rc; ++ } ++ ++ /* set dma size to 65536 bytes */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); ++ ++ /* Clear Streamoff */ ++ clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ ++ /* Zero out decoder counters */ ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[0]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[1]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[2]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA_END].data[3]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); ++ writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); ++ ++ /* turn on notification of dual/stereo mode change */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); ++ ++ /* start playback */ ++ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); ++ ++ /* Let things settle before we actually start */ ++ ivtv_msleep_timeout(10, 0); ++ ++ /* Clear the following Interrupt mask bits for decoding */ ++ ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); ++ IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); ++ ++ /* you're live! sit back and await interrupts :) */ ++ atomic_inc(&itv->decoding); ++ return 0; ++} ++ ++void ivtv_stop_all_captures(struct ivtv *itv) ++{ ++ int i; ++ ++ for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { ++ struct ivtv_stream *s = &itv->streams[i]; ++ ++ if (s->vdev == NULL) ++ continue; ++ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ++ ivtv_stop_v4l2_encode_stream(s, 0); ++ } ++ } ++} ++ ++int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) ++{ ++ struct ivtv *itv = s->itv; ++ DECLARE_WAITQUEUE(wait, current); ++ int cap_type; ++ int stopmode; ++ ++ if (s->vdev == NULL) ++ return -EINVAL; ++ ++ /* This function assumes that you are allowed to stop the capture ++ and that we are actually capturing */ ++ ++ IVTV_DEBUG_INFO("Stop Capture\n"); ++ ++ if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) ++ return 0; ++ if (atomic_read(&itv->capturing) == 0) ++ return 0; ++ ++ switch (s->type) { ++ case IVTV_ENC_STREAM_TYPE_YUV: ++ cap_type = 1; ++ break; ++ case IVTV_ENC_STREAM_TYPE_PCM: ++ cap_type = 1; ++ break; ++ case IVTV_ENC_STREAM_TYPE_VBI: ++ cap_type = 1; ++ break; ++ case IVTV_ENC_STREAM_TYPE_MPG: ++ default: ++ cap_type = 0; ++ break; ++ } ++ ++ /* Stop Capture Mode */ ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { ++ stopmode = 0; ++ } else { ++ stopmode = 1; ++ } ++ ++ /* end_capture */ ++ /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ ++ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); ++ ++ if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { ++ if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { ++ /* only run these if we're shutting down the last cap */ ++ unsigned long duration; ++ unsigned long then = jiffies; ++ ++ add_wait_queue(&itv->eos_waitq, &wait); ++ ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ /* wait 2s for EOS interrupt */ ++ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && ++ time_before(jiffies, ++ then + msecs_to_jiffies(2000))) { ++ schedule_timeout(msecs_to_jiffies(10)); ++ } ++ ++ /* To convert jiffies to ms, we must multiply by 1000 ++ * and divide by HZ. To avoid runtime division, we ++ * convert this to multiplication by 1000/HZ. ++ * Since integer division truncates, we get the best ++ * accuracy if we do a rounding calculation of the constant. ++ * Think of the case where HZ is 1024. ++ */ ++ duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); ++ ++ if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { ++ IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); ++ IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); ++ } else { ++ IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); ++ } ++ set_current_state(TASK_RUNNING); ++ remove_wait_queue(&itv->eos_waitq, &wait); ++ set_bit(IVTV_F_S_STREAMOFF, &s->s_flags); ++ } ++ ++ /* Handle any pending interrupts */ ++ ivtv_msleep_timeout(100, 0); ++ } ++ ++ atomic_dec(&itv->capturing); ++ ++ /* Clear capture and no-read bits */ ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ ++ if (s->type == IVTV_ENC_STREAM_TYPE_VBI) ++ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); ++ ++ if (atomic_read(&itv->capturing) > 0) { ++ return 0; ++ } ++ ++ cx2341x_handler_set_busy(&itv->cxhdl, 0); ++ ++ /* Set the following Interrupt mask bits for capture */ ++ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); ++ del_timer(&itv->dma_timer); ++ ++ /* event notification (off) */ ++ if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { ++ /* type: 0 = refresh */ ++ /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ ++ ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); ++ ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); ++ } ++ ++ /* Raw-passthrough is implied on start. Make sure it's stopped so ++ the encoder will re-initialize when next started */ ++ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 7); ++ ++ wake_up(&s->waitq); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ivtv_stop_v4l2_encode_stream); ++ ++int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) ++{ ++ static const struct v4l2_event ev = { ++ .type = V4L2_EVENT_EOS, ++ }; ++ struct ivtv *itv = s->itv; ++ ++ if (s->vdev == NULL) ++ return -EINVAL; ++ ++ if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) ++ return -EINVAL; ++ ++ if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) ++ return 0; ++ ++ IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags); ++ ++ /* Stop Decoder */ ++ if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) { ++ u32 tmp = 0; ++ ++ /* Wait until the decoder is no longer running */ ++ if (pts) { ++ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, ++ 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); ++ } ++ while (1) { ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); ++ if (s->q_full.buffers + s->q_dma.buffers == 0) { ++ if (tmp == data[3]) ++ break; ++ tmp = data[3]; ++ } ++ if (ivtv_msleep_timeout(100, 1)) ++ break; ++ } ++ } ++ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0); ++ ++ /* turn off notification of dual/stereo mode change */ ++ ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); ++ ++ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); ++ del_timer(&itv->dma_timer); ++ ++ clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); ++ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); ++ ivtv_flush_queues(s); ++ ++ /* decoder needs time to settle */ ++ ivtv_msleep_timeout(40, 0); ++ ++ /* decrement decoding */ ++ atomic_dec(&itv->decoding); ++ ++ set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); ++ wake_up(&itv->event_waitq); ++ v4l2_event_queue(s->vdev, &ev); ++ ++ /* wake up wait queues */ ++ wake_up(&s->waitq); ++ ++ return 0; ++} ++ ++int ivtv_passthrough_mode(struct ivtv *itv, int enable) ++{ ++ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; ++ struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; ++ ++ if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL) ++ return -EINVAL; ++ ++ IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); ++ ++ /* Prevent others from starting/stopping streams while we ++ initiate/terminate passthrough mode */ ++ if (enable) { ++ if (itv->output_mode == OUT_PASSTHROUGH) { ++ return 0; ++ } ++ if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) ++ return -EBUSY; ++ ++ /* Fully initialize stream, and then unflag init */ ++ set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); ++ set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); ++ ++ /* Setup YUV Decoder */ ++ ivtv_setup_v4l2_decode_stream(dec_stream); ++ ++ /* Start Decoder */ ++ ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); ++ atomic_inc(&itv->decoding); ++ ++ /* Setup capture if not already done */ ++ if (atomic_read(&itv->capturing) == 0) { ++ cx2341x_handler_setup(&itv->cxhdl); ++ cx2341x_handler_set_busy(&itv->cxhdl, 1); ++ } ++ ++ /* Start Passthrough Mode */ ++ ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); ++ atomic_inc(&itv->capturing); ++ return 0; ++ } ++ ++ if (itv->output_mode != OUT_PASSTHROUGH) ++ return 0; ++ ++ /* Stop Passthrough Mode */ ++ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); ++ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); ++ ++ atomic_dec(&itv->capturing); ++ atomic_dec(&itv->decoding); ++ clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); ++ clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); ++ itv->output_mode = OUT_NONE; ++ if (atomic_read(&itv->capturing) == 0) ++ cx2341x_handler_set_busy(&itv->cxhdl, 0); ++ ++ return 0; ++} +diff --git a/drivers/media/pci/ivtv/ivtv-streams.h b/drivers/media/pci/ivtv/ivtv-streams.h +new file mode 100644 +index 0000000..a653a51 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-streams.h +@@ -0,0 +1,37 @@ ++/* ++ init/start/stop/exit stream functions ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_STREAMS_H ++#define IVTV_STREAMS_H ++ ++int ivtv_streams_setup(struct ivtv *itv); ++int ivtv_streams_register(struct ivtv *itv); ++void ivtv_streams_cleanup(struct ivtv *itv, int unregister); ++ ++/* Capture related */ ++int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s); ++int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end); ++int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset); ++int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts); ++ ++void ivtv_stop_all_captures(struct ivtv *itv); ++int ivtv_passthrough_mode(struct ivtv *itv, int enable); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-udma.c b/drivers/media/pci/ivtv/ivtv-udma.c +new file mode 100644 +index 0000000..7338cb2 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-udma.c +@@ -0,0 +1,234 @@ ++/* ++ User DMA ++ ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-udma.h" ++ ++void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) ++{ ++ dma_page->uaddr = first & PAGE_MASK; ++ dma_page->offset = first & ~PAGE_MASK; ++ dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK); ++ dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT; ++ dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT; ++ dma_page->page_count = dma_page->last - dma_page->first + 1; ++ if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset; ++} ++ ++int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) ++{ ++ int i, offset; ++ unsigned long flags; ++ ++ if (map_offset < 0) ++ return map_offset; ++ ++ offset = dma_page->offset; ++ ++ /* Fill SG Array with new values */ ++ for (i = 0; i < dma_page->page_count; i++) { ++ unsigned int len = (i == dma_page->page_count - 1) ? ++ dma_page->tail : PAGE_SIZE - offset; ++ ++ if (PageHighMem(dma->map[map_offset])) { ++ void *src; ++ ++ if (dma->bouncemap[map_offset] == NULL) ++ dma->bouncemap[map_offset] = alloc_page(GFP_KERNEL); ++ if (dma->bouncemap[map_offset] == NULL) ++ return -1; ++ local_irq_save(flags); ++ src = kmap_atomic(dma->map[map_offset]) + offset; ++ memcpy(page_address(dma->bouncemap[map_offset]) + offset, src, len); ++ kunmap_atomic(src); ++ local_irq_restore(flags); ++ sg_set_page(&dma->SGlist[map_offset], dma->bouncemap[map_offset], len, offset); ++ } ++ else { ++ sg_set_page(&dma->SGlist[map_offset], dma->map[map_offset], len, offset); ++ } ++ offset = 0; ++ map_offset++; ++ } ++ return map_offset; ++} ++ ++void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) { ++ int i; ++ struct scatterlist *sg; ++ ++ for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) { ++ dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg)); ++ dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg)); ++ dma->SGarray[i].dst = cpu_to_le32(buffer_offset); ++ buffer_offset += sg_dma_len(sg); ++ ++ split -= sg_dma_len(sg); ++ if (split == 0) ++ buffer_offset = buffer_offset_2; ++ } ++} ++ ++/* User DMA Buffers */ ++void ivtv_udma_alloc(struct ivtv *itv) ++{ ++ if (itv->udma.SG_handle == 0) { ++ /* Map DMA Page Array Buffer */ ++ itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray, ++ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); ++ ivtv_udma_sync_for_cpu(itv); ++ } ++} ++ ++int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, ++ void __user *userbuf, int size_in_bytes) ++{ ++ struct ivtv_dma_page_info user_dma; ++ struct ivtv_user_dma *dma = &itv->udma; ++ int i, err; ++ ++ IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); ++ ++ /* Still in USE */ ++ if (dma->SG_length || dma->page_count) { ++ IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n", ++ dma->SG_length, dma->page_count); ++ return -EBUSY; ++ } ++ ++ ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes); ++ ++ if (user_dma.page_count <= 0) { ++ IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n", ++ user_dma.page_count, size_in_bytes, user_dma.offset); ++ return -EINVAL; ++ } ++ ++ /* Get user pages for DMA Xfer */ ++ down_read(¤t->mm->mmap_sem); ++ err = get_user_pages(current, current->mm, ++ user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL); ++ up_read(¤t->mm->mmap_sem); ++ ++ if (user_dma.page_count != err) { ++ IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", ++ err, user_dma.page_count); ++ if (err >= 0) { ++ for (i = 0; i < err; i++) ++ put_page(dma->map[i]); ++ return -EINVAL; ++ } ++ return err; ++ } ++ ++ dma->page_count = user_dma.page_count; ++ ++ /* Fill SG List with new values */ ++ if (ivtv_udma_fill_sg_list(dma, &user_dma, 0) < 0) { ++ for (i = 0; i < dma->page_count; i++) { ++ put_page(dma->map[i]); ++ } ++ dma->page_count = 0; ++ return -ENOMEM; ++ } ++ ++ /* Map SG List */ ++ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); ++ ++ /* Fill SG Array with new values */ ++ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); ++ ++ /* Tag SG Array with Interrupt Bit */ ++ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); ++ ++ ivtv_udma_sync_for_device(itv); ++ return dma->page_count; ++} ++ ++void ivtv_udma_unmap(struct ivtv *itv) ++{ ++ struct ivtv_user_dma *dma = &itv->udma; ++ int i; ++ ++ IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n"); ++ ++ /* Nothing to free */ ++ if (dma->page_count == 0) ++ return; ++ ++ /* Unmap Scatterlist */ ++ if (dma->SG_length) { ++ pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); ++ dma->SG_length = 0; ++ } ++ /* sync DMA */ ++ ivtv_udma_sync_for_cpu(itv); ++ ++ /* Release User Pages */ ++ for (i = 0; i < dma->page_count; i++) { ++ put_page(dma->map[i]); ++ } ++ dma->page_count = 0; ++} ++ ++void ivtv_udma_free(struct ivtv *itv) ++{ ++ int i; ++ ++ /* Unmap SG Array */ ++ if (itv->udma.SG_handle) { ++ pci_unmap_single(itv->pdev, itv->udma.SG_handle, ++ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); ++ } ++ ++ /* Unmap Scatterlist */ ++ if (itv->udma.SG_length) { ++ pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); ++ } ++ ++ for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) { ++ if (itv->udma.bouncemap[i]) ++ __free_page(itv->udma.bouncemap[i]); ++ } ++} ++ ++void ivtv_udma_start(struct ivtv *itv) ++{ ++ IVTV_DEBUG_DMA("start UDMA\n"); ++ write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR); ++ write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); ++ set_bit(IVTV_F_I_DMA, &itv->i_flags); ++ set_bit(IVTV_F_I_UDMA, &itv->i_flags); ++ clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); ++} ++ ++void ivtv_udma_prepare(struct ivtv *itv) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&itv->dma_reg_lock, flags); ++ if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) ++ ivtv_udma_start(itv); ++ else ++ set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); ++ spin_unlock_irqrestore(&itv->dma_reg_lock, flags); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-udma.h b/drivers/media/pci/ivtv/ivtv-udma.h +new file mode 100644 +index 0000000..ee3c9ef +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-udma.h +@@ -0,0 +1,48 @@ ++/* ++ Copyright (C) 2003-2004 Kevin Thayer ++ Copyright (C) 2004 Chris Kennedy ++ Copyright (C) 2006-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_UDMA_H ++#define IVTV_UDMA_H ++ ++/* User DMA functions */ ++void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size); ++int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset); ++void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split); ++int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, ++ void __user *userbuf, int size_in_bytes); ++void ivtv_udma_unmap(struct ivtv *itv); ++void ivtv_udma_free(struct ivtv *itv); ++void ivtv_udma_alloc(struct ivtv *itv); ++void ivtv_udma_prepare(struct ivtv *itv); ++void ivtv_udma_start(struct ivtv *itv); ++ ++static inline void ivtv_udma_sync_for_device(struct ivtv *itv) ++{ ++ pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle, ++ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); ++} ++ ++static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) ++{ ++ pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle, ++ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); ++} ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c +new file mode 100644 +index 0000000..293db80 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-vbi.c +@@ -0,0 +1,549 @@ ++/* ++ Vertical Blank Interval support functions ++ Copyright (C) 2004-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-i2c.h" ++#include "ivtv-ioctl.h" ++#include "ivtv-queue.h" ++#include "ivtv-cards.h" ++#include "ivtv-vbi.h" ++ ++static void ivtv_set_vps(struct ivtv *itv, int enabled) ++{ ++ struct v4l2_sliced_vbi_data data; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return; ++ data.id = V4L2_SLICED_VPS; ++ data.field = 0; ++ data.line = enabled ? 16 : 0; ++ data.data[2] = itv->vbi.vps_payload.data[0]; ++ data.data[8] = itv->vbi.vps_payload.data[1]; ++ data.data[9] = itv->vbi.vps_payload.data[2]; ++ data.data[10] = itv->vbi.vps_payload.data[3]; ++ data.data[11] = itv->vbi.vps_payload.data[4]; ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); ++} ++ ++static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) ++{ ++ struct v4l2_sliced_vbi_data data; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return; ++ data.id = V4L2_SLICED_CAPTION_525; ++ data.field = 0; ++ data.line = (mode & 1) ? 21 : 0; ++ data.data[0] = cc->odd[0]; ++ data.data[1] = cc->odd[1]; ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); ++ data.field = 1; ++ data.line = (mode & 2) ? 21 : 0; ++ data.data[0] = cc->even[0]; ++ data.data[1] = cc->even[1]; ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); ++} ++ ++static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) ++{ ++ struct v4l2_sliced_vbi_data data; ++ ++ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) ++ return; ++ /* When using a 50 Hz system, always turn on the ++ wide screen signal with 4x3 ratio as the default. ++ Turning this signal on and off can confuse certain ++ TVs. As far as I can tell there is no reason not to ++ transmit this signal. */ ++ if ((itv->std_out & V4L2_STD_625_50) && !enabled) { ++ enabled = 1; ++ mode = 0x08; /* 4x3 full format */ ++ } ++ data.id = V4L2_SLICED_WSS_625; ++ data.field = 0; ++ data.line = enabled ? 23 : 0; ++ data.data[0] = mode & 0xff; ++ data.data[1] = (mode >> 8) & 0xff; ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, vbi, s_vbi_data, &data); ++} ++ ++static int odd_parity(u8 c) ++{ ++ c ^= (c >> 4); ++ c ^= (c >> 2); ++ c ^= (c >> 1); ++ ++ return c & 1; ++} ++ ++static void ivtv_write_vbi_line(struct ivtv *itv, ++ const struct v4l2_sliced_vbi_data *d, ++ struct vbi_cc *cc, int *found_cc) ++{ ++ struct vbi_info *vi = &itv->vbi; ++ ++ if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) { ++ if (d->field) { ++ cc->even[0] = d->data[0]; ++ cc->even[1] = d->data[1]; ++ } else { ++ cc->odd[0] = d->data[0]; ++ cc->odd[1] = d->data[1]; ++ } ++ *found_cc = 1; ++ } else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) { ++ struct vbi_vps vps; ++ ++ vps.data[0] = d->data[2]; ++ vps.data[1] = d->data[8]; ++ vps.data[2] = d->data[9]; ++ vps.data[3] = d->data[10]; ++ vps.data[4] = d->data[11]; ++ if (memcmp(&vps, &vi->vps_payload, sizeof(vps))) { ++ vi->vps_payload = vps; ++ set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); ++ } ++ } else if (d->id == V4L2_SLICED_WSS_625 && ++ d->line == 23 && d->field == 0) { ++ int wss = d->data[0] | d->data[1] << 8; ++ ++ if (vi->wss_payload != wss) { ++ vi->wss_payload = wss; ++ set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); ++ } ++ } ++} ++ ++static void ivtv_write_vbi_cc_lines(struct ivtv *itv, const struct vbi_cc *cc) ++{ ++ struct vbi_info *vi = &itv->vbi; ++ ++ if (vi->cc_payload_idx < ARRAY_SIZE(vi->cc_payload)) { ++ memcpy(&vi->cc_payload[vi->cc_payload_idx], cc, ++ sizeof(struct vbi_cc)); ++ vi->cc_payload_idx++; ++ set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); ++ } ++} ++ ++static void ivtv_write_vbi(struct ivtv *itv, ++ const struct v4l2_sliced_vbi_data *sliced, ++ size_t cnt) ++{ ++ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } }; ++ int found_cc = 0; ++ size_t i; ++ ++ for (i = 0; i < cnt; i++) ++ ivtv_write_vbi_line(itv, sliced + i, &cc, &found_cc); ++ ++ if (found_cc) ++ ivtv_write_vbi_cc_lines(itv, &cc); ++} ++ ++ssize_t ++ivtv_write_vbi_from_user(struct ivtv *itv, ++ const struct v4l2_sliced_vbi_data __user *sliced, ++ size_t cnt) ++{ ++ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } }; ++ int found_cc = 0; ++ size_t i; ++ struct v4l2_sliced_vbi_data d; ++ ssize_t ret = cnt * sizeof(struct v4l2_sliced_vbi_data); ++ ++ for (i = 0; i < cnt; i++) { ++ if (copy_from_user(&d, sliced + i, ++ sizeof(struct v4l2_sliced_vbi_data))) { ++ ret = -EFAULT; ++ break; ++ } ++ ivtv_write_vbi_line(itv, &d, &cc, &found_cc); ++ } ++ ++ if (found_cc) ++ ivtv_write_vbi_cc_lines(itv, &cc); ++ ++ return ret; ++} ++ ++static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) ++{ ++ int line = 0; ++ int i; ++ u32 linemask[2] = { 0, 0 }; ++ unsigned short size; ++ static const u8 mpeg_hdr_data[] = { ++ 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, ++ 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, ++ 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, ++ 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff ++ }; ++ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ ++ int idx = itv->vbi.frame % IVTV_VBI_FRAMES; ++ u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0]; ++ ++ for (i = 0; i < lines; i++) { ++ int f, l; ++ ++ if (itv->vbi.sliced_data[i].id == 0) ++ continue; ++ ++ l = itv->vbi.sliced_data[i].line - 6; ++ f = itv->vbi.sliced_data[i].field; ++ if (f) ++ l += 18; ++ if (l < 32) ++ linemask[0] |= (1 << l); ++ else ++ linemask[1] |= (1 << (l - 32)); ++ dst[sd + 12 + line * 43] = ++ ivtv_service2vbi(itv->vbi.sliced_data[i].id); ++ memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42); ++ line++; ++ } ++ memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); ++ if (line == 36) { ++ /* All lines are used, so there is no space for the linemask ++ (the max size of the VBI data is 36 * 43 + 4 bytes). ++ So in this case we use the magic number 'ITV0'. */ ++ memcpy(dst + sd, "ITV0", 4); ++ memcpy(dst + sd + 4, dst + sd + 12, line * 43); ++ size = 4 + ((43 * line + 3) & ~3); ++ } else { ++ memcpy(dst + sd, "itv0", 4); ++ cpu_to_le32s(&linemask[0]); ++ cpu_to_le32s(&linemask[1]); ++ memcpy(dst + sd + 4, &linemask[0], 8); ++ size = 12 + ((43 * line + 3) & ~3); ++ } ++ dst[4+16] = (size + 10) >> 8; ++ dst[5+16] = (size + 10) & 0xff; ++ dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); ++ dst[10+16] = (pts_stamp >> 22) & 0xff; ++ dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); ++ dst[12+16] = (pts_stamp >> 7) & 0xff; ++ dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); ++ itv->vbi.sliced_mpeg_size[idx] = sd + size; ++} ++ ++static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p) ++{ ++ u32 linemask[2]; ++ int i, l, id2; ++ int line = 0; ++ ++ if (!memcmp(p, "itv0", 4)) { ++ memcpy(linemask, p + 4, 8); ++ p += 12; ++ } else if (!memcmp(p, "ITV0", 4)) { ++ linemask[0] = 0xffffffff; ++ linemask[1] = 0xf; ++ p += 4; ++ } else { ++ /* unknown VBI data, convert to empty VBI frame */ ++ linemask[0] = linemask[1] = 0; ++ } ++ for (i = 0; i < 36; i++) { ++ int err = 0; ++ ++ if (i < 32 && !(linemask[0] & (1 << i))) ++ continue; ++ if (i >= 32 && !(linemask[1] & (1 << (i - 32)))) ++ continue; ++ id2 = *p & 0xf; ++ switch (id2) { ++ case IVTV_SLICED_TYPE_TELETEXT_B: ++ id2 = V4L2_SLICED_TELETEXT_B; ++ break; ++ case IVTV_SLICED_TYPE_CAPTION_525: ++ id2 = V4L2_SLICED_CAPTION_525; ++ err = !odd_parity(p[1]) || !odd_parity(p[2]); ++ break; ++ case IVTV_SLICED_TYPE_VPS: ++ id2 = V4L2_SLICED_VPS; ++ break; ++ case IVTV_SLICED_TYPE_WSS_625: ++ id2 = V4L2_SLICED_WSS_625; ++ break; ++ default: ++ id2 = 0; ++ break; ++ } ++ if (err == 0) { ++ l = (i < 18) ? i + 6 : i - 18 + 6; ++ itv->vbi.sliced_dec_data[line].line = l; ++ itv->vbi.sliced_dec_data[line].field = i >= 18; ++ itv->vbi.sliced_dec_data[line].id = id2; ++ memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42); ++ line++; ++ } ++ p += 43; ++ } ++ while (line < 36) { ++ itv->vbi.sliced_dec_data[line].id = 0; ++ itv->vbi.sliced_dec_data[line].line = 0; ++ itv->vbi.sliced_dec_data[line].field = 0; ++ line++; ++ } ++ return line * sizeof(itv->vbi.sliced_dec_data[0]); ++} ++ ++/* Compress raw VBI format, removes leading SAV codes and surplus space after the ++ field. ++ Returns new compressed size. */ ++static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size) ++{ ++ u32 line_size = itv->vbi.raw_decoder_line_size; ++ u32 lines = itv->vbi.count; ++ u8 sav1 = itv->vbi.raw_decoder_sav_odd_field; ++ u8 sav2 = itv->vbi.raw_decoder_sav_even_field; ++ u8 *q = buf; ++ u8 *p; ++ int i; ++ ++ for (i = 0; i < lines; i++) { ++ p = buf + i * line_size; ++ ++ /* Look for SAV code */ ++ if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) { ++ break; ++ } ++ memcpy(q, p + 4, line_size - 4); ++ q += line_size - 4; ++ } ++ return lines * (line_size - 4); ++} ++ ++ ++/* Compressed VBI format, all found sliced blocks put next to one another ++ Returns new compressed size */ ++static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav) ++{ ++ u32 line_size = itv->vbi.sliced_decoder_line_size; ++ struct v4l2_decode_vbi_line vbi; ++ int i; ++ unsigned lines = 0; ++ ++ /* find the first valid line */ ++ for (i = 0; i < size; i++, buf++) { ++ if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) ++ break; ++ } ++ ++ size -= i; ++ if (size < line_size) { ++ return line; ++ } ++ for (i = 0; i < size / line_size; i++) { ++ u8 *p = buf + i * line_size; ++ ++ /* Look for SAV code */ ++ if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) { ++ continue; ++ } ++ vbi.p = p + 4; ++ v4l2_subdev_call(itv->sd_video, vbi, decode_vbi_line, &vbi); ++ if (vbi.type && !(lines & (1 << vbi.line))) { ++ lines |= 1 << vbi.line; ++ itv->vbi.sliced_data[line].id = vbi.type; ++ itv->vbi.sliced_data[line].field = vbi.is_second_field; ++ itv->vbi.sliced_data[line].line = vbi.line; ++ memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42); ++ line++; ++ } ++ } ++ return line; ++} ++ ++void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, ++ u64 pts_stamp, int streamtype) ++{ ++ u8 *p = (u8 *) buf->buf; ++ u32 size = buf->bytesused; ++ int y; ++ ++ /* Raw VBI data */ ++ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && ivtv_raw_vbi(itv)) { ++ u8 type; ++ ++ ivtv_buf_swap(buf); ++ ++ type = p[3]; ++ ++ size = buf->bytesused = compress_raw_buf(itv, p, size); ++ ++ /* second field of the frame? */ ++ if (type == itv->vbi.raw_decoder_sav_even_field) { ++ /* Dirty hack needed for backwards ++ compatibility of old VBI software. */ ++ p += size - 4; ++ memcpy(p, &itv->vbi.frame, 4); ++ itv->vbi.frame++; ++ } ++ return; ++ } ++ ++ /* Sliced VBI data with data insertion */ ++ if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) { ++ int lines; ++ ++ ivtv_buf_swap(buf); ++ ++ /* first field */ ++ lines = compress_sliced_buf(itv, 0, p, size / 2, ++ itv->vbi.sliced_decoder_sav_odd_field); ++ /* second field */ ++ /* experimentation shows that the second half does not always begin ++ at the exact address. So start a bit earlier (hence 32). */ ++ lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32, ++ itv->vbi.sliced_decoder_sav_even_field); ++ /* always return at least one empty line */ ++ if (lines == 0) { ++ itv->vbi.sliced_data[0].id = 0; ++ itv->vbi.sliced_data[0].line = 0; ++ itv->vbi.sliced_data[0].field = 0; ++ lines = 1; ++ } ++ buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]); ++ memcpy(p, &itv->vbi.sliced_data[0], size); ++ ++ if (itv->vbi.insert_mpeg) { ++ copy_vbi_data(itv, lines, pts_stamp); ++ } ++ itv->vbi.frame++; ++ return; ++ } ++ ++ /* Sliced VBI re-inserted from an MPEG stream */ ++ if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { ++ /* If the size is not 4-byte aligned, then the starting address ++ for the swapping is also shifted. After swapping the data the ++ real start address of the VBI data is exactly 4 bytes after the ++ original start. It's a bit fiddly but it works like a charm. ++ Non-4-byte alignment happens when an lseek is done on the input ++ mpeg file to a non-4-byte aligned position. So on arrival here ++ the VBI data is also non-4-byte aligned. */ ++ int offset = size & 3; ++ int cnt; ++ ++ if (offset) { ++ p += 4 - offset; ++ } ++ /* Swap Buffer */ ++ for (y = 0; y < size; y += 4) { ++ swab32s((u32 *)(p + y)); ++ } ++ ++ cnt = ivtv_convert_ivtv_vbi(itv, p + offset); ++ memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt); ++ buf->bytesused = cnt; ++ ++ ivtv_write_vbi(itv, itv->vbi.sliced_dec_data, ++ cnt / sizeof(itv->vbi.sliced_dec_data[0])); ++ return; ++ } ++} ++ ++void ivtv_disable_cc(struct ivtv *itv) ++{ ++ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } }; ++ ++ clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); ++ ivtv_set_cc(itv, 0, &cc); ++ itv->vbi.cc_payload_idx = 0; ++} ++ ++ ++void ivtv_vbi_work_handler(struct ivtv *itv) ++{ ++ struct vbi_info *vi = &itv->vbi; ++ struct v4l2_sliced_vbi_data data; ++ struct vbi_cc cc = { .odd = { 0x80, 0x80 }, .even = { 0x80, 0x80 } }; ++ ++ /* Lock */ ++ if (itv->output_mode == OUT_PASSTHROUGH) { ++ if (itv->is_50hz) { ++ data.id = V4L2_SLICED_WSS_625; ++ data.field = 0; ++ ++ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { ++ ivtv_set_wss(itv, 1, data.data[0] & 0xf); ++ vi->wss_missing_cnt = 0; ++ } else if (vi->wss_missing_cnt == 4) { ++ ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */ ++ } else { ++ vi->wss_missing_cnt++; ++ } ++ } ++ else { ++ int mode = 0; ++ ++ data.id = V4L2_SLICED_CAPTION_525; ++ data.field = 0; ++ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { ++ mode |= 1; ++ cc.odd[0] = data.data[0]; ++ cc.odd[1] = data.data[1]; ++ } ++ data.field = 1; ++ if (v4l2_subdev_call(itv->sd_video, vbi, g_vbi_data, &data) == 0) { ++ mode |= 2; ++ cc.even[0] = data.data[0]; ++ cc.even[1] = data.data[1]; ++ } ++ if (mode) { ++ vi->cc_missing_cnt = 0; ++ ivtv_set_cc(itv, mode, &cc); ++ } else if (vi->cc_missing_cnt == 4) { ++ ivtv_set_cc(itv, 0, &cc); ++ } else { ++ vi->cc_missing_cnt++; ++ } ++ } ++ return; ++ } ++ ++ if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) { ++ ivtv_set_wss(itv, 1, vi->wss_payload & 0xf); ++ } ++ ++ if (test_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) { ++ if (vi->cc_payload_idx == 0) { ++ clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); ++ ivtv_set_cc(itv, 3, &cc); ++ } ++ while (vi->cc_payload_idx) { ++ cc = vi->cc_payload[0]; ++ ++ memcpy(vi->cc_payload, vi->cc_payload + 1, ++ sizeof(vi->cc_payload) - sizeof(vi->cc_payload[0])); ++ vi->cc_payload_idx--; ++ if (vi->cc_payload_idx && cc.odd[0] == 0x80 && cc.odd[1] == 0x80) ++ continue; ++ ++ ivtv_set_cc(itv, 3, &cc); ++ break; ++ } ++ } ++ ++ if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) { ++ ivtv_set_vps(itv, 1); ++ } ++} +diff --git a/drivers/media/pci/ivtv/ivtv-vbi.h b/drivers/media/pci/ivtv/ivtv-vbi.h +new file mode 100644 +index 0000000..166dd0b +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-vbi.h +@@ -0,0 +1,34 @@ ++/* ++ Vertical Blank Interval support functions ++ Copyright (C) 2004-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_VBI_H ++#define IVTV_VBI_H ++ ++ssize_t ++ivtv_write_vbi_from_user(struct ivtv *itv, ++ const struct v4l2_sliced_vbi_data __user *sliced, ++ size_t count); ++void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, ++ u64 pts_stamp, int streamtype); ++int ivtv_used_line(struct ivtv *itv, int line, int field); ++void ivtv_disable_cc(struct ivtv *itv); ++void ivtv_set_vbi(unsigned long arg); ++void ivtv_vbi_work_handler(struct ivtv *itv); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-version.h b/drivers/media/pci/ivtv/ivtv-version.h +new file mode 100644 +index 0000000..a20f346 +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-version.h +@@ -0,0 +1,26 @@ ++/* ++ ivtv driver version information ++ Copyright (C) 2005-2007 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_VERSION_H ++#define IVTV_VERSION_H ++ ++#define IVTV_DRIVER_NAME "ivtv" ++#define IVTV_VERSION "1.4.3" ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtv-yuv.c b/drivers/media/pci/ivtv/ivtv-yuv.c +new file mode 100644 +index 0000000..2ad65eb +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-yuv.c +@@ -0,0 +1,1296 @@ ++/* ++ yuv support ++ ++ Copyright (C) 2007 Ian Armstrong ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "ivtv-driver.h" ++#include "ivtv-udma.h" ++#include "ivtv-yuv.h" ++ ++/* YUV buffer offsets */ ++const u32 yuv_offset[IVTV_YUV_BUFFERS] = { ++ 0x001a8600, ++ 0x00240400, ++ 0x002d8200, ++ 0x00370000, ++ 0x00029000, ++ 0x000C0E00, ++ 0x006B0400, ++ 0x00748200 ++}; ++ ++static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, ++ struct ivtv_dma_frame *args) ++{ ++ struct ivtv_dma_page_info y_dma; ++ struct ivtv_dma_page_info uv_dma; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ u8 frame = yi->draw_frame; ++ struct yuv_frame_info *f = &yi->new_frame_info[frame]; ++ int i; ++ int y_pages, uv_pages; ++ unsigned long y_buffer_offset, uv_buffer_offset; ++ int y_decode_height, uv_decode_height, y_size; ++ ++ y_buffer_offset = IVTV_DECODER_OFFSET + yuv_offset[frame]; ++ uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; ++ ++ y_decode_height = uv_decode_height = f->src_h + f->src_y; ++ ++ if (f->offset_y) ++ y_buffer_offset += 720 * 16; ++ ++ if (y_decode_height & 15) ++ y_decode_height = (y_decode_height + 16) & ~15; ++ ++ if (uv_decode_height & 31) ++ uv_decode_height = (uv_decode_height + 32) & ~31; ++ ++ y_size = 720 * y_decode_height; ++ ++ /* Still in USE */ ++ if (dma->SG_length || dma->page_count) { ++ IVTV_DEBUG_WARN ++ ("prep_user_dma: SG_length %d page_count %d still full?\n", ++ dma->SG_length, dma->page_count); ++ return -EBUSY; ++ } ++ ++ ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height); ++ ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); ++ ++ /* Get user pages for DMA Xfer */ ++ down_read(¤t->mm->mmap_sem); ++ y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); ++ uv_pages = 0; /* silence gcc. value is set and consumed only if: */ ++ if (y_pages == y_dma.page_count) { ++ uv_pages = get_user_pages(current, current->mm, ++ uv_dma.uaddr, uv_dma.page_count, 0, 1, ++ &dma->map[y_pages], NULL); ++ } ++ up_read(¤t->mm->mmap_sem); ++ ++ if (y_pages != y_dma.page_count || uv_pages != uv_dma.page_count) { ++ int rc = -EFAULT; ++ ++ if (y_pages == y_dma.page_count) { ++ IVTV_DEBUG_WARN ++ ("failed to map uv user pages, returned %d " ++ "expecting %d\n", uv_pages, uv_dma.page_count); ++ ++ if (uv_pages >= 0) { ++ for (i = 0; i < uv_pages; i++) ++ put_page(dma->map[y_pages + i]); ++ rc = -EFAULT; ++ } else { ++ rc = uv_pages; ++ } ++ } else { ++ IVTV_DEBUG_WARN ++ ("failed to map y user pages, returned %d " ++ "expecting %d\n", y_pages, y_dma.page_count); ++ } ++ if (y_pages >= 0) { ++ for (i = 0; i < y_pages; i++) ++ put_page(dma->map[i]); ++ /* ++ * Inherit the -EFAULT from rc's ++ * initialization, but allow it to be ++ * overriden by uv_pages above if it was an ++ * actual errno. ++ */ ++ } else { ++ rc = y_pages; ++ } ++ return rc; ++ } ++ ++ dma->page_count = y_pages + uv_pages; ++ ++ /* Fill & map SG List */ ++ if (ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)) < 0) { ++ IVTV_DEBUG_WARN("could not allocate bounce buffers for highmem userspace buffers\n"); ++ for (i = 0; i < dma->page_count; i++) { ++ put_page(dma->map[i]); ++ } ++ dma->page_count = 0; ++ return -ENOMEM; ++ } ++ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); ++ ++ /* Fill SG Array with new values */ ++ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size); ++ ++ /* If we've offset the y plane, ensure top area is blanked */ ++ if (f->offset_y && yi->blanking_dmaptr) { ++ dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); ++ dma->SGarray[dma->SG_length].src = cpu_to_le32(yi->blanking_dmaptr); ++ dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DECODER_OFFSET + yuv_offset[frame]); ++ dma->SG_length++; ++ } ++ ++ /* Tag SG Array with Interrupt Bit */ ++ dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); ++ ++ ivtv_udma_sync_for_device(itv); ++ return 0; ++} ++ ++/* We rely on a table held in the firmware - Quick check. */ ++int ivtv_yuv_filter_check(struct ivtv *itv) ++{ ++ int i, y, uv; ++ ++ for (i = 0, y = 16, uv = 4; i < 16; i++, y += 24, uv += 12) { ++ if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + y) != i << 16) || ++ (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + uv) != i << 16)) { ++ IVTV_WARN ("YUV filter table not found in firmware.\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2) ++{ ++ u32 i, line; ++ ++ /* If any filter is -1, then don't update it */ ++ if (h_filter > -1) { ++ if (h_filter > 4) ++ h_filter = 4; ++ i = IVTV_YUV_HORIZONTAL_FILTER_OFFSET + (h_filter * 384); ++ for (line = 0; line < 16; line++) { ++ write_reg(read_dec(i), 0x02804); ++ write_reg(read_dec(i), 0x0281c); ++ i += 4; ++ write_reg(read_dec(i), 0x02808); ++ write_reg(read_dec(i), 0x02820); ++ i += 4; ++ write_reg(read_dec(i), 0x0280c); ++ write_reg(read_dec(i), 0x02824); ++ i += 4; ++ write_reg(read_dec(i), 0x02810); ++ write_reg(read_dec(i), 0x02828); ++ i += 4; ++ write_reg(read_dec(i), 0x02814); ++ write_reg(read_dec(i), 0x0282c); ++ i += 8; ++ write_reg(0, 0x02818); ++ write_reg(0, 0x02830); ++ } ++ IVTV_DEBUG_YUV("h_filter -> %d\n", h_filter); ++ } ++ ++ if (v_filter_1 > -1) { ++ if (v_filter_1 > 4) ++ v_filter_1 = 4; ++ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_1 * 192); ++ for (line = 0; line < 16; line++) { ++ write_reg(read_dec(i), 0x02900); ++ i += 4; ++ write_reg(read_dec(i), 0x02904); ++ i += 8; ++ write_reg(0, 0x02908); ++ } ++ IVTV_DEBUG_YUV("v_filter_1 -> %d\n", v_filter_1); ++ } ++ ++ if (v_filter_2 > -1) { ++ if (v_filter_2 > 4) ++ v_filter_2 = 4; ++ i = IVTV_YUV_VERTICAL_FILTER_OFFSET + (v_filter_2 * 192); ++ for (line = 0; line < 16; line++) { ++ write_reg(read_dec(i), 0x0290c); ++ i += 4; ++ write_reg(read_dec(i), 0x02910); ++ i += 8; ++ write_reg(0, 0x02914); ++ } ++ IVTV_DEBUG_YUV("v_filter_2 -> %d\n", v_filter_2); ++ } ++} ++ ++static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *f) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ u32 reg_2834, reg_2838, reg_283c; ++ u32 reg_2844, reg_2854, reg_285c; ++ u32 reg_2864, reg_2874, reg_2890; ++ u32 reg_2870, reg_2870_base, reg_2870_offset; ++ int x_cutoff; ++ int h_filter; ++ u32 master_width; ++ ++ IVTV_DEBUG_WARN ++ ("Adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", ++ f->tru_w, f->src_w, f->dst_w, f->src_x, f->dst_x); ++ ++ /* How wide is the src image */ ++ x_cutoff = f->src_w + f->src_x; ++ ++ /* Set the display width */ ++ reg_2834 = f->dst_w; ++ reg_2838 = reg_2834; ++ ++ /* Set the display position */ ++ reg_2890 = f->dst_x; ++ ++ /* Index into the image horizontally */ ++ reg_2870 = 0; ++ ++ /* 2870 is normally fudged to align video coords with osd coords. ++ If running full screen, it causes an unwanted left shift ++ Remove the fudge if we almost fill the screen. ++ Gradually adjust the offset to avoid the video 'snapping' ++ left/right if it gets dragged through this region. ++ Only do this if osd is full width. */ ++ if (f->vis_w == 720) { ++ if ((f->tru_x - f->pan_x > -1) && (f->tru_x - f->pan_x <= 40) && (f->dst_w >= 680)) ++ reg_2870 = 10 - (f->tru_x - f->pan_x) / 4; ++ else if ((f->tru_x - f->pan_x < 0) && (f->tru_x - f->pan_x >= -20) && (f->dst_w >= 660)) ++ reg_2870 = (10 + (f->tru_x - f->pan_x) / 2); ++ ++ if (f->dst_w >= f->src_w) ++ reg_2870 = reg_2870 << 16 | reg_2870; ++ else ++ reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1); ++ } ++ ++ if (f->dst_w < f->src_w) ++ reg_2870 = 0x000d000e - reg_2870; ++ else ++ reg_2870 = 0x0012000e - reg_2870; ++ ++ /* We're also using 2870 to shift the image left (src_x & negative dst_x) */ ++ reg_2870_offset = (f->src_x * ((f->dst_w << 21) / f->src_w)) >> 19; ++ ++ if (f->dst_w >= f->src_w) { ++ x_cutoff &= ~1; ++ master_width = (f->src_w * 0x00200000) / (f->dst_w); ++ if (master_width * f->dst_w != f->src_w * 0x00200000) ++ master_width++; ++ reg_2834 = (reg_2834 << 16) | x_cutoff; ++ reg_2838 = (reg_2838 << 16) | x_cutoff; ++ reg_283c = master_width >> 2; ++ reg_2844 = master_width >> 2; ++ reg_2854 = master_width; ++ reg_285c = master_width >> 1; ++ reg_2864 = master_width >> 1; ++ ++ /* We also need to factor in the scaling ++ (src_w - dst_w) / (src_w / 4) */ ++ if (f->dst_w > f->src_w) ++ reg_2870_base = ((f->dst_w - f->src_w)<<16) / (f->src_w <<14); ++ else ++ reg_2870_base = 0; ++ ++ reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base); ++ reg_2874 = 0; ++ } else if (f->dst_w < f->src_w / 2) { ++ master_width = (f->src_w * 0x00080000) / f->dst_w; ++ if (master_width * f->dst_w != f->src_w * 0x00080000) ++ master_width++; ++ reg_2834 = (reg_2834 << 16) | x_cutoff; ++ reg_2838 = (reg_2838 << 16) | x_cutoff; ++ reg_283c = master_width >> 2; ++ reg_2844 = master_width >> 1; ++ reg_2854 = master_width; ++ reg_285c = master_width >> 1; ++ reg_2864 = master_width >> 1; ++ reg_2870 += ((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset; ++ reg_2870 += (5 - (((f->src_w + f->src_w / 2) - 1) / f->dst_w)) << 16; ++ reg_2874 = 0x00000012; ++ } else { ++ master_width = (f->src_w * 0x00100000) / f->dst_w; ++ if (master_width * f->dst_w != f->src_w * 0x00100000) ++ master_width++; ++ reg_2834 = (reg_2834 << 16) | x_cutoff; ++ reg_2838 = (reg_2838 << 16) | x_cutoff; ++ reg_283c = master_width >> 2; ++ reg_2844 = master_width >> 1; ++ reg_2854 = master_width; ++ reg_285c = master_width >> 1; ++ reg_2864 = master_width >> 1; ++ reg_2870 += ((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1; ++ reg_2870 += (5 - (((f->src_w * 3) - 1) / f->dst_w)) << 16; ++ reg_2874 = 0x00000001; ++ } ++ ++ /* Select the horizontal filter */ ++ if (f->src_w == f->dst_w) { ++ /* An exact size match uses filter 0 */ ++ h_filter = 0; ++ } else { ++ /* Figure out which filter to use */ ++ h_filter = ((f->src_w << 16) / f->dst_w) >> 15; ++ h_filter = (h_filter >> 1) + (h_filter & 1); ++ /* Only an exact size match can use filter 0 */ ++ h_filter += !h_filter; ++ } ++ ++ write_reg(reg_2834, 0x02834); ++ write_reg(reg_2838, 0x02838); ++ IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n", ++ yi->reg_2834, reg_2834, yi->reg_2838, reg_2838); ++ ++ write_reg(reg_283c, 0x0283c); ++ write_reg(reg_2844, 0x02844); ++ ++ IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n", ++ yi->reg_283c, reg_283c, yi->reg_2844, reg_2844); ++ ++ write_reg(0x00080514, 0x02840); ++ write_reg(0x00100514, 0x02848); ++ IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n", ++ yi->reg_2840, 0x00080514, yi->reg_2848, 0x00100514); ++ ++ write_reg(reg_2854, 0x02854); ++ IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n", ++ yi->reg_2854, reg_2854); ++ ++ write_reg(reg_285c, 0x0285c); ++ write_reg(reg_2864, 0x02864); ++ IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n", ++ yi->reg_285c, reg_285c, yi->reg_2864, reg_2864); ++ ++ write_reg(reg_2874, 0x02874); ++ IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n", ++ yi->reg_2874, reg_2874); ++ ++ write_reg(reg_2870, 0x02870); ++ IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n", ++ yi->reg_2870, reg_2870); ++ ++ write_reg(reg_2890, 0x02890); ++ IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n", ++ yi->reg_2890, reg_2890); ++ ++ /* Only update the filter if we really need to */ ++ if (h_filter != yi->h_filter) { ++ ivtv_yuv_filter(itv, h_filter, -1, -1); ++ yi->h_filter = h_filter; ++ } ++} ++ ++static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *f) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ u32 master_height; ++ u32 reg_2918, reg_291c, reg_2920, reg_2928; ++ u32 reg_2930, reg_2934, reg_293c; ++ u32 reg_2940, reg_2944, reg_294c; ++ u32 reg_2950, reg_2954, reg_2958, reg_295c; ++ u32 reg_2960, reg_2964, reg_2968, reg_296c; ++ u32 reg_289c; ++ u32 src_major_y, src_minor_y; ++ u32 src_major_uv, src_minor_uv; ++ u32 reg_2964_base, reg_2968_base; ++ int v_filter_1, v_filter_2; ++ ++ IVTV_DEBUG_WARN ++ ("Adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", ++ f->tru_h, f->src_h, f->dst_h, f->src_y, f->dst_y); ++ ++ /* What scaling mode is being used... */ ++ IVTV_DEBUG_YUV("Scaling mode Y: %s\n", ++ f->interlaced_y ? "Interlaced" : "Progressive"); ++ ++ IVTV_DEBUG_YUV("Scaling mode UV: %s\n", ++ f->interlaced_uv ? "Interlaced" : "Progressive"); ++ ++ /* What is the source video being treated as... */ ++ IVTV_DEBUG_WARN("Source video: %s\n", ++ f->interlaced ? "Interlaced" : "Progressive"); ++ ++ /* We offset into the image using two different index methods, so split ++ the y source coord into two parts. */ ++ if (f->src_y < 8) { ++ src_minor_uv = f->src_y; ++ src_major_uv = 0; ++ } else { ++ src_minor_uv = 8; ++ src_major_uv = f->src_y - 8; ++ } ++ ++ src_minor_y = src_minor_uv; ++ src_major_y = src_major_uv; ++ ++ if (f->offset_y) ++ src_minor_y += 16; ++ ++ if (f->interlaced_y) ++ reg_2918 = (f->dst_h << 16) | (f->src_h + src_minor_y); ++ else ++ reg_2918 = (f->dst_h << 16) | ((f->src_h + src_minor_y) << 1); ++ ++ if (f->interlaced_uv) ++ reg_291c = (f->dst_h << 16) | ((f->src_h + src_minor_uv) >> 1); ++ else ++ reg_291c = (f->dst_h << 16) | (f->src_h + src_minor_uv); ++ ++ reg_2964_base = (src_minor_y * ((f->dst_h << 16) / f->src_h)) >> 14; ++ reg_2968_base = (src_minor_uv * ((f->dst_h << 16) / f->src_h)) >> 14; ++ ++ if (f->dst_h / 2 >= f->src_h && !f->interlaced_y) { ++ master_height = (f->src_h * 0x00400000) / f->dst_h; ++ if ((f->src_h * 0x00400000) - (master_height * f->dst_h) >= f->dst_h / 2) ++ master_height++; ++ reg_2920 = master_height >> 2; ++ reg_2928 = master_height >> 3; ++ reg_2930 = master_height; ++ reg_2940 = master_height >> 1; ++ reg_2964_base >>= 3; ++ reg_2968_base >>= 3; ++ reg_296c = 0x00000000; ++ } else if (f->dst_h >= f->src_h) { ++ master_height = (f->src_h * 0x00400000) / f->dst_h; ++ master_height = (master_height >> 1) + (master_height & 1); ++ reg_2920 = master_height >> 2; ++ reg_2928 = master_height >> 2; ++ reg_2930 = master_height; ++ reg_2940 = master_height >> 1; ++ reg_296c = 0x00000000; ++ if (f->interlaced_y) { ++ reg_2964_base >>= 3; ++ } else { ++ reg_296c++; ++ reg_2964_base >>= 2; ++ } ++ if (f->interlaced_uv) ++ reg_2928 >>= 1; ++ reg_2968_base >>= 3; ++ } else if (f->dst_h >= f->src_h / 2) { ++ master_height = (f->src_h * 0x00200000) / f->dst_h; ++ master_height = (master_height >> 1) + (master_height & 1); ++ reg_2920 = master_height >> 2; ++ reg_2928 = master_height >> 2; ++ reg_2930 = master_height; ++ reg_2940 = master_height; ++ reg_296c = 0x00000101; ++ if (f->interlaced_y) { ++ reg_2964_base >>= 2; ++ } else { ++ reg_296c++; ++ reg_2964_base >>= 1; ++ } ++ if (f->interlaced_uv) ++ reg_2928 >>= 1; ++ reg_2968_base >>= 2; ++ } else { ++ master_height = (f->src_h * 0x00100000) / f->dst_h; ++ master_height = (master_height >> 1) + (master_height & 1); ++ reg_2920 = master_height >> 2; ++ reg_2928 = master_height >> 2; ++ reg_2930 = master_height; ++ reg_2940 = master_height; ++ reg_2964_base >>= 1; ++ reg_2968_base >>= 2; ++ reg_296c = 0x00000102; ++ } ++ ++ /* FIXME These registers change depending on scaled / unscaled output ++ We really need to work out what they should be */ ++ if (f->src_h == f->dst_h) { ++ reg_2934 = 0x00020000; ++ reg_293c = 0x00100000; ++ reg_2944 = 0x00040000; ++ reg_294c = 0x000b0000; ++ } else { ++ reg_2934 = 0x00000FF0; ++ reg_293c = 0x00000FF0; ++ reg_2944 = 0x00000FF0; ++ reg_294c = 0x00000FF0; ++ } ++ ++ /* The first line to be displayed */ ++ reg_2950 = 0x00010000 + src_major_y; ++ if (f->interlaced_y) ++ reg_2950 += 0x00010000; ++ reg_2954 = reg_2950 + 1; ++ ++ reg_2958 = 0x00010000 + (src_major_y >> 1); ++ if (f->interlaced_uv) ++ reg_2958 += 0x00010000; ++ reg_295c = reg_2958 + 1; ++ ++ if (yi->decode_height == 480) ++ reg_289c = 0x011e0017; ++ else ++ reg_289c = 0x01500017; ++ ++ if (f->dst_y < 0) ++ reg_289c = (reg_289c - ((f->dst_y & ~1)<<15))-(f->dst_y >>1); ++ else ++ reg_289c = (reg_289c + ((f->dst_y & ~1)<<15))+(f->dst_y >>1); ++ ++ /* How much of the source to decode. ++ Take into account the source offset */ ++ reg_2960 = ((src_minor_y + f->src_h + src_major_y) - 1) | ++ (((src_minor_uv + f->src_h + src_major_uv - 1) & ~1) << 15); ++ ++ /* Calculate correct value for register 2964 */ ++ if (f->src_h == f->dst_h) { ++ reg_2964 = 1; ++ } else { ++ reg_2964 = 2 + ((f->dst_h << 1) / f->src_h); ++ reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); ++ } ++ reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); ++ reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); ++ ++ /* Okay, we've wasted time working out the correct value, ++ but if we use it, it fouls the the window alignment. ++ Fudge it to what we want... */ ++ reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); ++ reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); ++ ++ /* Deviate further from what it should be. I find the flicker headache ++ inducing so try to reduce it slightly. Leave 2968 as-is otherwise ++ colours foul. */ ++ if ((reg_2964 != 0x00010001) && (f->dst_h / 2 <= f->src_h)) ++ reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF) / 2); ++ ++ if (!f->interlaced_y) ++ reg_2964 -= 0x00010001; ++ if (!f->interlaced_uv) ++ reg_2968 -= 0x00010001; ++ ++ reg_2964 += ((reg_2964_base << 16) | reg_2964_base); ++ reg_2968 += ((reg_2968_base << 16) | reg_2968_base); ++ ++ /* Select the vertical filter */ ++ if (f->src_h == f->dst_h) { ++ /* An exact size match uses filter 0/1 */ ++ v_filter_1 = 0; ++ v_filter_2 = 1; ++ } else { ++ /* Figure out which filter to use */ ++ v_filter_1 = ((f->src_h << 16) / f->dst_h) >> 15; ++ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); ++ /* Only an exact size match can use filter 0 */ ++ v_filter_1 += !v_filter_1; ++ v_filter_2 = v_filter_1; ++ } ++ ++ write_reg(reg_2934, 0x02934); ++ write_reg(reg_293c, 0x0293c); ++ IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n", ++ yi->reg_2934, reg_2934, yi->reg_293c, reg_293c); ++ write_reg(reg_2944, 0x02944); ++ write_reg(reg_294c, 0x0294c); ++ IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n", ++ yi->reg_2944, reg_2944, yi->reg_294c, reg_294c); ++ ++ /* Ensure 2970 is 0 (does it ever change ?) */ ++/* write_reg(0,0x02970); */ ++/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n", yi->reg_2970, 0); */ ++ ++ write_reg(reg_2930, 0x02938); ++ write_reg(reg_2930, 0x02930); ++ IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n", ++ yi->reg_2930, reg_2930, yi->reg_2938, reg_2930); ++ ++ write_reg(reg_2928, 0x02928); ++ write_reg(reg_2928 + 0x514, 0x0292C); ++ IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n", ++ yi->reg_2928, reg_2928, yi->reg_292c, reg_2928 + 0x514); ++ ++ write_reg(reg_2920, 0x02920); ++ write_reg(reg_2920 + 0x514, 0x02924); ++ IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n", ++ yi->reg_2920, reg_2920, yi->reg_2924, reg_2920 + 0x514); ++ ++ write_reg(reg_2918, 0x02918); ++ write_reg(reg_291c, 0x0291C); ++ IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n", ++ yi->reg_2918, reg_2918, yi->reg_291c, reg_291c); ++ ++ write_reg(reg_296c, 0x0296c); ++ IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n", ++ yi->reg_296c, reg_296c); ++ ++ write_reg(reg_2940, 0x02948); ++ write_reg(reg_2940, 0x02940); ++ IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n", ++ yi->reg_2940, reg_2940, yi->reg_2948, reg_2940); ++ ++ write_reg(reg_2950, 0x02950); ++ write_reg(reg_2954, 0x02954); ++ IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n", ++ yi->reg_2950, reg_2950, yi->reg_2954, reg_2954); ++ ++ write_reg(reg_2958, 0x02958); ++ write_reg(reg_295c, 0x0295C); ++ IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n", ++ yi->reg_2958, reg_2958, yi->reg_295c, reg_295c); ++ ++ write_reg(reg_2960, 0x02960); ++ IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n", ++ yi->reg_2960, reg_2960); ++ ++ write_reg(reg_2964, 0x02964); ++ write_reg(reg_2968, 0x02968); ++ IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n", ++ yi->reg_2964, reg_2964, yi->reg_2968, reg_2968); ++ ++ write_reg(reg_289c, 0x0289c); ++ IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n", ++ yi->reg_289c, reg_289c); ++ ++ /* Only update filter 1 if we really need to */ ++ if (v_filter_1 != yi->v_filter_1) { ++ ivtv_yuv_filter(itv, -1, v_filter_1, -1); ++ yi->v_filter_1 = v_filter_1; ++ } ++ ++ /* Only update filter 2 if we really need to */ ++ if (v_filter_2 != yi->v_filter_2) { ++ ivtv_yuv_filter(itv, -1, -1, v_filter_2); ++ yi->v_filter_2 = v_filter_2; ++ } ++} ++ ++/* Modify the supplied coordinate information to fit the visible osd area */ ++static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) ++{ ++ struct yuv_frame_info *of = &itv->yuv_info.old_frame_info; ++ int osd_crop; ++ u32 osd_scale; ++ u32 yuv_update = 0; ++ ++ /* Sorry, but no negative coords for src */ ++ if (f->src_x < 0) ++ f->src_x = 0; ++ if (f->src_y < 0) ++ f->src_y = 0; ++ ++ /* Can only reduce width down to 1/4 original size */ ++ if ((osd_crop = f->src_w - 4 * f->dst_w) > 0) { ++ f->src_x += osd_crop / 2; ++ f->src_w = (f->src_w - osd_crop) & ~3; ++ f->dst_w = f->src_w / 4; ++ f->dst_w += f->dst_w & 1; ++ } ++ ++ /* Can only reduce height down to 1/4 original size */ ++ if (f->src_h / f->dst_h >= 2) { ++ /* Overflow may be because we're running progressive, ++ so force mode switch */ ++ f->interlaced_y = 1; ++ /* Make sure we're still within limits for interlace */ ++ if ((osd_crop = f->src_h - 4 * f->dst_h) > 0) { ++ /* If we reach here we'll have to force the height. */ ++ f->src_y += osd_crop / 2; ++ f->src_h = (f->src_h - osd_crop) & ~3; ++ f->dst_h = f->src_h / 4; ++ f->dst_h += f->dst_h & 1; ++ } ++ } ++ ++ /* If there's nothing to safe to display, we may as well stop now */ ++ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || ++ (int)f->src_w <= 2 || (int)f->src_h <= 2) { ++ return IVTV_YUV_UPDATE_INVALID; ++ } ++ ++ /* Ensure video remains inside OSD area */ ++ osd_scale = (f->src_h << 16) / f->dst_h; ++ ++ if ((osd_crop = f->pan_y - f->dst_y) > 0) { ++ /* Falls off the upper edge - crop */ ++ f->src_y += (osd_scale * osd_crop) >> 16; ++ f->src_h -= (osd_scale * osd_crop) >> 16; ++ f->dst_h -= osd_crop; ++ f->dst_y = 0; ++ } else { ++ f->dst_y -= f->pan_y; ++ } ++ ++ if ((osd_crop = f->dst_h + f->dst_y - f->vis_h) > 0) { ++ /* Falls off the lower edge - crop */ ++ f->dst_h -= osd_crop; ++ f->src_h -= (osd_scale * osd_crop) >> 16; ++ } ++ ++ osd_scale = (f->src_w << 16) / f->dst_w; ++ ++ if ((osd_crop = f->pan_x - f->dst_x) > 0) { ++ /* Fall off the left edge - crop */ ++ f->src_x += (osd_scale * osd_crop) >> 16; ++ f->src_w -= (osd_scale * osd_crop) >> 16; ++ f->dst_w -= osd_crop; ++ f->dst_x = 0; ++ } else { ++ f->dst_x -= f->pan_x; ++ } ++ ++ if ((osd_crop = f->dst_w + f->dst_x - f->vis_w) > 0) { ++ /* Falls off the right edge - crop */ ++ f->dst_w -= osd_crop; ++ f->src_w -= (osd_scale * osd_crop) >> 16; ++ } ++ ++ if (itv->yuv_info.track_osd) { ++ /* The OSD can be moved. Track to it */ ++ f->dst_x += itv->yuv_info.osd_x_offset; ++ f->dst_y += itv->yuv_info.osd_y_offset; ++ } ++ ++ /* Width & height for both src & dst must be even. ++ Same for coordinates. */ ++ f->dst_w &= ~1; ++ f->dst_x &= ~1; ++ ++ f->src_w += f->src_x & 1; ++ f->src_x &= ~1; ++ ++ f->src_w &= ~1; ++ f->dst_w &= ~1; ++ ++ f->dst_h &= ~1; ++ f->dst_y &= ~1; ++ ++ f->src_h += f->src_y & 1; ++ f->src_y &= ~1; ++ ++ f->src_h &= ~1; ++ f->dst_h &= ~1; ++ ++ /* Due to rounding, we may have reduced the output size to <1/4 of ++ the source. Check again, but this time just resize. Don't change ++ source coordinates */ ++ if (f->dst_w < f->src_w / 4) { ++ f->src_w &= ~3; ++ f->dst_w = f->src_w / 4; ++ f->dst_w += f->dst_w & 1; ++ } ++ if (f->dst_h < f->src_h / 4) { ++ f->src_h &= ~3; ++ f->dst_h = f->src_h / 4; ++ f->dst_h += f->dst_h & 1; ++ } ++ ++ /* Check again. If there's nothing to safe to display, stop now */ ++ if ((int)f->dst_w <= 2 || (int)f->dst_h <= 2 || ++ (int)f->src_w <= 2 || (int)f->src_h <= 2) { ++ return IVTV_YUV_UPDATE_INVALID; ++ } ++ ++ /* Both x offset & width are linked, so they have to be done together */ ++ if ((of->dst_w != f->dst_w) || (of->src_w != f->src_w) || ++ (of->dst_x != f->dst_x) || (of->src_x != f->src_x) || ++ (of->pan_x != f->pan_x) || (of->vis_w != f->vis_w)) { ++ yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; ++ } ++ ++ if ((of->src_h != f->src_h) || (of->dst_h != f->dst_h) || ++ (of->dst_y != f->dst_y) || (of->src_y != f->src_y) || ++ (of->pan_y != f->pan_y) || (of->vis_h != f->vis_h) || ++ (of->lace_mode != f->lace_mode) || ++ (of->interlaced_y != f->interlaced_y) || ++ (of->interlaced_uv != f->interlaced_uv)) { ++ yuv_update |= IVTV_YUV_UPDATE_VERTICAL; ++ } ++ ++ return yuv_update; ++} ++ ++/* Update the scaling register to the requested value */ ++void ivtv_yuv_work_handler(struct ivtv *itv) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ struct yuv_frame_info f; ++ int frame = yi->update_frame; ++ u32 yuv_update; ++ ++ IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame); ++ f = yi->new_frame_info[frame]; ++ ++ if (yi->track_osd) { ++ /* Snapshot the osd pan info */ ++ f.pan_x = yi->osd_x_pan; ++ f.pan_y = yi->osd_y_pan; ++ f.vis_w = yi->osd_vis_w; ++ f.vis_h = yi->osd_vis_h; ++ } else { ++ /* Not tracking the osd, so assume full screen */ ++ f.pan_x = 0; ++ f.pan_y = 0; ++ f.vis_w = 720; ++ f.vis_h = yi->decode_height; ++ } ++ ++ /* Calculate the display window coordinates. Exit if nothing left */ ++ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f))) ++ return; ++ ++ if (yuv_update & IVTV_YUV_UPDATE_INVALID) { ++ write_reg(0x01008080, 0x2898); ++ } else if (yuv_update) { ++ write_reg(0x00108080, 0x2898); ++ ++ if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) ++ ivtv_yuv_handle_horizontal(itv, &f); ++ ++ if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) ++ ivtv_yuv_handle_vertical(itv, &f); ++ } ++ yi->old_frame_info = f; ++} ++ ++static void ivtv_yuv_init(struct ivtv *itv) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ ++ IVTV_DEBUG_YUV("ivtv_yuv_init\n"); ++ ++ /* Take a snapshot of the current register settings */ ++ yi->reg_2834 = read_reg(0x02834); ++ yi->reg_2838 = read_reg(0x02838); ++ yi->reg_283c = read_reg(0x0283c); ++ yi->reg_2840 = read_reg(0x02840); ++ yi->reg_2844 = read_reg(0x02844); ++ yi->reg_2848 = read_reg(0x02848); ++ yi->reg_2854 = read_reg(0x02854); ++ yi->reg_285c = read_reg(0x0285c); ++ yi->reg_2864 = read_reg(0x02864); ++ yi->reg_2870 = read_reg(0x02870); ++ yi->reg_2874 = read_reg(0x02874); ++ yi->reg_2898 = read_reg(0x02898); ++ yi->reg_2890 = read_reg(0x02890); ++ ++ yi->reg_289c = read_reg(0x0289c); ++ yi->reg_2918 = read_reg(0x02918); ++ yi->reg_291c = read_reg(0x0291c); ++ yi->reg_2920 = read_reg(0x02920); ++ yi->reg_2924 = read_reg(0x02924); ++ yi->reg_2928 = read_reg(0x02928); ++ yi->reg_292c = read_reg(0x0292c); ++ yi->reg_2930 = read_reg(0x02930); ++ yi->reg_2934 = read_reg(0x02934); ++ yi->reg_2938 = read_reg(0x02938); ++ yi->reg_293c = read_reg(0x0293c); ++ yi->reg_2940 = read_reg(0x02940); ++ yi->reg_2944 = read_reg(0x02944); ++ yi->reg_2948 = read_reg(0x02948); ++ yi->reg_294c = read_reg(0x0294c); ++ yi->reg_2950 = read_reg(0x02950); ++ yi->reg_2954 = read_reg(0x02954); ++ yi->reg_2958 = read_reg(0x02958); ++ yi->reg_295c = read_reg(0x0295c); ++ yi->reg_2960 = read_reg(0x02960); ++ yi->reg_2964 = read_reg(0x02964); ++ yi->reg_2968 = read_reg(0x02968); ++ yi->reg_296c = read_reg(0x0296c); ++ yi->reg_2970 = read_reg(0x02970); ++ ++ yi->v_filter_1 = -1; ++ yi->v_filter_2 = -1; ++ yi->h_filter = -1; ++ ++ /* Set some valid size info */ ++ yi->osd_x_offset = read_reg(0x02a04) & 0x00000FFF; ++ yi->osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF; ++ ++ /* Bit 2 of reg 2878 indicates current decoder output format ++ 0 : NTSC 1 : PAL */ ++ if (read_reg(0x2878) & 4) ++ yi->decode_height = 576; ++ else ++ yi->decode_height = 480; ++ ++ if (!itv->osd_info) { ++ yi->osd_vis_w = 720 - yi->osd_x_offset; ++ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; ++ } else { ++ /* If no visible size set, assume full size */ ++ if (!yi->osd_vis_w) ++ yi->osd_vis_w = 720 - yi->osd_x_offset; ++ ++ if (!yi->osd_vis_h) { ++ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; ++ } else if (yi->osd_vis_h + yi->osd_y_offset > yi->decode_height) { ++ /* If output video standard has changed, requested height may ++ not be legal */ ++ IVTV_DEBUG_WARN("Clipping yuv output - fb size (%d) exceeds video standard limit (%d)\n", ++ yi->osd_vis_h + yi->osd_y_offset, ++ yi->decode_height); ++ yi->osd_vis_h = yi->decode_height - yi->osd_y_offset; ++ } ++ } ++ ++ /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ ++ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN); ++ if (yi->blanking_ptr) { ++ yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); ++ } else { ++ yi->blanking_dmaptr = 0; ++ IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n"); ++ } ++ ++ /* Enable YUV decoder output */ ++ write_reg_sync(0x01, IVTV_REG_VDM); ++ ++ set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); ++ atomic_set(&yi->next_dma_frame, 0); ++} ++ ++/* Get next available yuv buffer on PVR350 */ ++static void ivtv_yuv_next_free(struct ivtv *itv) ++{ ++ int draw, display; ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ ++ if (atomic_read(&yi->next_dma_frame) == -1) ++ ivtv_yuv_init(itv); ++ ++ draw = atomic_read(&yi->next_fill_frame); ++ display = atomic_read(&yi->next_dma_frame); ++ ++ if (display > draw) ++ display -= IVTV_YUV_BUFFERS; ++ ++ if (draw - display >= yi->max_frames_buffered) ++ draw = (u8)(draw - 1) % IVTV_YUV_BUFFERS; ++ else ++ yi->new_frame_info[draw].update = 0; ++ ++ yi->draw_frame = draw; ++} ++ ++/* Set up frame according to ivtv_dma_frame parameters */ ++static void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ u8 frame = yi->draw_frame; ++ u8 last_frame = (u8)(frame - 1) % IVTV_YUV_BUFFERS; ++ struct yuv_frame_info *nf = &yi->new_frame_info[frame]; ++ struct yuv_frame_info *of = &yi->new_frame_info[last_frame]; ++ int lace_threshold = yi->lace_threshold; ++ ++ /* Preserve old update flag in case we're overwriting a queued frame */ ++ int update = nf->update; ++ ++ /* Take a snapshot of the yuv coordinate information */ ++ nf->src_x = args->src.left; ++ nf->src_y = args->src.top; ++ nf->src_w = args->src.width; ++ nf->src_h = args->src.height; ++ nf->dst_x = args->dst.left; ++ nf->dst_y = args->dst.top; ++ nf->dst_w = args->dst.width; ++ nf->dst_h = args->dst.height; ++ nf->tru_x = args->dst.left; ++ nf->tru_w = args->src_width; ++ nf->tru_h = args->src_height; ++ ++ /* Are we going to offset the Y plane */ ++ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0; ++ ++ nf->update = 0; ++ nf->interlaced_y = 0; ++ nf->interlaced_uv = 0; ++ nf->delay = 0; ++ nf->sync_field = 0; ++ nf->lace_mode = yi->lace_mode & IVTV_YUV_MODE_MASK; ++ ++ if (lace_threshold < 0) ++ lace_threshold = yi->decode_height - 1; ++ ++ /* Work out the lace settings */ ++ switch (nf->lace_mode) { ++ case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ ++ nf->interlaced = 0; ++ if (nf->tru_h < 512 || (nf->tru_h > 576 && nf->tru_h < 1021)) ++ nf->interlaced_y = 0; ++ else ++ nf->interlaced_y = 1; ++ ++ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) ++ nf->interlaced_uv = 0; ++ else ++ nf->interlaced_uv = 1; ++ break; ++ ++ case IVTV_YUV_MODE_AUTO: ++ if (nf->tru_h <= lace_threshold || nf->tru_h > 576 || nf->tru_w > 720) { ++ nf->interlaced = 0; ++ if ((nf->tru_h < 512) || ++ (nf->tru_h > 576 && nf->tru_h < 1021) || ++ (nf->tru_w > 720 && nf->tru_h < 1021)) ++ nf->interlaced_y = 0; ++ else ++ nf->interlaced_y = 1; ++ if (nf->tru_h < 1021 && (nf->dst_h >= nf->src_h / 2)) ++ nf->interlaced_uv = 0; ++ else ++ nf->interlaced_uv = 1; ++ } else { ++ nf->interlaced = 1; ++ nf->interlaced_y = 1; ++ nf->interlaced_uv = 1; ++ } ++ break; ++ ++ case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ ++ default: ++ nf->interlaced = 1; ++ nf->interlaced_y = 1; ++ nf->interlaced_uv = 1; ++ break; ++ } ++ ++ if (memcmp(&yi->old_frame_info_args, nf, sizeof(*nf))) { ++ yi->old_frame_info_args = *nf; ++ nf->update = 1; ++ IVTV_DEBUG_YUV("Requesting reg update for frame %d\n", frame); ++ } ++ ++ nf->update |= update; ++ nf->sync_field = yi->lace_sync_field; ++ nf->delay = nf->sync_field != of->sync_field; ++} ++ ++/* Frame is complete & ready for display */ ++void ivtv_yuv_frame_complete(struct ivtv *itv) ++{ ++ atomic_set(&itv->yuv_info.next_fill_frame, ++ (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS); ++} ++ ++static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) ++{ ++ DEFINE_WAIT(wait); ++ int rc = 0; ++ int got_sig = 0; ++ /* DMA the frame */ ++ mutex_lock(&itv->udma.lock); ++ ++ if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) { ++ mutex_unlock(&itv->udma.lock); ++ return rc; ++ } ++ ++ ivtv_udma_prepare(itv); ++ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); ++ /* if no UDMA is pending and no UDMA is in progress, then the DMA ++ is finished */ ++ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || ++ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { ++ /* don't interrupt if the DMA is in progress but break off ++ a still pending DMA. */ ++ got_sig = signal_pending(current); ++ if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) ++ break; ++ got_sig = 0; ++ schedule(); ++ } ++ finish_wait(&itv->dma_waitq, &wait); ++ ++ /* Unmap Last DMA Xfer */ ++ ivtv_udma_unmap(itv); ++ ++ if (got_sig) { ++ IVTV_DEBUG_INFO("User stopped YUV UDMA\n"); ++ mutex_unlock(&itv->udma.lock); ++ return -EINTR; ++ } ++ ++ ivtv_yuv_frame_complete(itv); ++ ++ mutex_unlock(&itv->udma.lock); ++ return rc; ++} ++ ++/* Setup frame according to V4L2 parameters */ ++void ivtv_yuv_setup_stream_frame(struct ivtv *itv) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ struct ivtv_dma_frame dma_args; ++ ++ ivtv_yuv_next_free(itv); ++ ++ /* Copy V4L2 parameters to an ivtv_dma_frame struct... */ ++ dma_args.y_source = NULL; ++ dma_args.uv_source = NULL; ++ dma_args.src.left = 0; ++ dma_args.src.top = 0; ++ dma_args.src.width = yi->v4l2_src_w; ++ dma_args.src.height = yi->v4l2_src_h; ++ dma_args.dst = yi->main_rect; ++ dma_args.src_width = yi->v4l2_src_w; ++ dma_args.src_height = yi->v4l2_src_h; ++ ++ /* ... and use the same setup routine as ivtv_yuv_prep_frame */ ++ ivtv_yuv_setup_frame(itv, &dma_args); ++ ++ if (!itv->dma_data_req_offset) ++ itv->dma_data_req_offset = yuv_offset[yi->draw_frame]; ++} ++ ++/* Attempt to dma a frame from a user buffer */ ++int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ struct ivtv_dma_frame dma_args; ++ int res; ++ ++ ivtv_yuv_setup_stream_frame(itv); ++ ++ /* We only need to supply source addresses for this */ ++ dma_args.y_source = src; ++ dma_args.uv_source = src + 720 * ((yi->v4l2_src_h + 31) & ~31); ++ /* Wait for frame DMA. Note that serialize_lock is locked, ++ so to allow other processes to access the driver while ++ we are waiting unlock first and later lock again. */ ++ mutex_unlock(&itv->serialize_lock); ++ res = ivtv_yuv_udma_frame(itv, &dma_args); ++ mutex_lock(&itv->serialize_lock); ++ return res; ++} ++ ++/* IVTV_IOC_DMA_FRAME ioctl handler */ ++int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) ++{ ++ int res; ++ ++/* IVTV_DEBUG_INFO("yuv_prep_frame\n"); */ ++ ivtv_yuv_next_free(itv); ++ ivtv_yuv_setup_frame(itv, args); ++ /* Wait for frame DMA. Note that serialize_lock is locked, ++ so to allow other processes to access the driver while ++ we are waiting unlock first and later lock again. */ ++ mutex_unlock(&itv->serialize_lock); ++ res = ivtv_yuv_udma_frame(itv, args); ++ mutex_lock(&itv->serialize_lock); ++ return res; ++} ++ ++void ivtv_yuv_close(struct ivtv *itv) ++{ ++ struct yuv_playback_info *yi = &itv->yuv_info; ++ int h_filter, v_filter_1, v_filter_2; ++ ++ IVTV_DEBUG_YUV("ivtv_yuv_close\n"); ++ mutex_unlock(&itv->serialize_lock); ++ ivtv_waitq(&itv->vsync_waitq); ++ mutex_lock(&itv->serialize_lock); ++ ++ yi->running = 0; ++ atomic_set(&yi->next_dma_frame, -1); ++ atomic_set(&yi->next_fill_frame, 0); ++ ++ /* Reset registers we have changed so mpeg playback works */ ++ ++ /* If we fully restore this register, the display may remain active. ++ Restore, but set one bit to blank the video. Firmware will always ++ clear this bit when needed, so not a problem. */ ++ write_reg(yi->reg_2898 | 0x01000000, 0x2898); ++ ++ write_reg(yi->reg_2834, 0x02834); ++ write_reg(yi->reg_2838, 0x02838); ++ write_reg(yi->reg_283c, 0x0283c); ++ write_reg(yi->reg_2840, 0x02840); ++ write_reg(yi->reg_2844, 0x02844); ++ write_reg(yi->reg_2848, 0x02848); ++ write_reg(yi->reg_2854, 0x02854); ++ write_reg(yi->reg_285c, 0x0285c); ++ write_reg(yi->reg_2864, 0x02864); ++ write_reg(yi->reg_2870, 0x02870); ++ write_reg(yi->reg_2874, 0x02874); ++ write_reg(yi->reg_2890, 0x02890); ++ write_reg(yi->reg_289c, 0x0289c); ++ ++ write_reg(yi->reg_2918, 0x02918); ++ write_reg(yi->reg_291c, 0x0291c); ++ write_reg(yi->reg_2920, 0x02920); ++ write_reg(yi->reg_2924, 0x02924); ++ write_reg(yi->reg_2928, 0x02928); ++ write_reg(yi->reg_292c, 0x0292c); ++ write_reg(yi->reg_2930, 0x02930); ++ write_reg(yi->reg_2934, 0x02934); ++ write_reg(yi->reg_2938, 0x02938); ++ write_reg(yi->reg_293c, 0x0293c); ++ write_reg(yi->reg_2940, 0x02940); ++ write_reg(yi->reg_2944, 0x02944); ++ write_reg(yi->reg_2948, 0x02948); ++ write_reg(yi->reg_294c, 0x0294c); ++ write_reg(yi->reg_2950, 0x02950); ++ write_reg(yi->reg_2954, 0x02954); ++ write_reg(yi->reg_2958, 0x02958); ++ write_reg(yi->reg_295c, 0x0295c); ++ write_reg(yi->reg_2960, 0x02960); ++ write_reg(yi->reg_2964, 0x02964); ++ write_reg(yi->reg_2968, 0x02968); ++ write_reg(yi->reg_296c, 0x0296c); ++ write_reg(yi->reg_2970, 0x02970); ++ ++ /* Prepare to restore filters */ ++ ++ /* First the horizontal filter */ ++ if ((yi->reg_2834 & 0x0000FFFF) == (yi->reg_2834 >> 16)) { ++ /* An exact size match uses filter 0 */ ++ h_filter = 0; ++ } else { ++ /* Figure out which filter to use */ ++ h_filter = ((yi->reg_2834 << 16) / (yi->reg_2834 >> 16)) >> 15; ++ h_filter = (h_filter >> 1) + (h_filter & 1); ++ /* Only an exact size match can use filter 0. */ ++ h_filter += !h_filter; ++ } ++ ++ /* Now the vertical filter */ ++ if ((yi->reg_2918 & 0x0000FFFF) == (yi->reg_2918 >> 16)) { ++ /* An exact size match uses filter 0/1 */ ++ v_filter_1 = 0; ++ v_filter_2 = 1; ++ } else { ++ /* Figure out which filter to use */ ++ v_filter_1 = ((yi->reg_2918 << 16) / (yi->reg_2918 >> 16)) >> 15; ++ v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); ++ /* Only an exact size match can use filter 0 */ ++ v_filter_1 += !v_filter_1; ++ v_filter_2 = v_filter_1; ++ } ++ ++ /* Now restore the filters */ ++ ivtv_yuv_filter(itv, h_filter, v_filter_1, v_filter_2); ++ ++ /* and clear a few registers */ ++ write_reg(0, 0x02814); ++ write_reg(0, 0x0282c); ++ write_reg(0, 0x02904); ++ write_reg(0, 0x02910); ++ ++ /* Release the blanking buffer */ ++ if (yi->blanking_ptr) { ++ kfree(yi->blanking_ptr); ++ yi->blanking_ptr = NULL; ++ pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); ++ } ++ ++ /* Invalidate the old dimension information */ ++ yi->old_frame_info.src_w = 0; ++ yi->old_frame_info.src_h = 0; ++ yi->old_frame_info_args.src_w = 0; ++ yi->old_frame_info_args.src_h = 0; ++ ++ /* All done. */ ++ clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); ++} +diff --git a/drivers/media/pci/ivtv/ivtv-yuv.h b/drivers/media/pci/ivtv/ivtv-yuv.h +new file mode 100644 +index 0000000..ca5173f +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtv-yuv.h +@@ -0,0 +1,44 @@ ++/* ++ yuv support ++ ++ Copyright (C) 2007 Ian Armstrong ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef IVTV_YUV_H ++#define IVTV_YUV_H ++ ++#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ ++ ++/* Offset to filter table in firmware */ ++#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8 ++#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358 ++ ++#define IVTV_YUV_UPDATE_HORIZONTAL 0x01 ++#define IVTV_YUV_UPDATE_VERTICAL 0x02 ++#define IVTV_YUV_UPDATE_INVALID 0x04 ++ ++extern const u32 yuv_offset[IVTV_YUV_BUFFERS]; ++ ++int ivtv_yuv_filter_check(struct ivtv *itv); ++void ivtv_yuv_setup_stream_frame(struct ivtv *itv); ++int ivtv_yuv_udma_stream_frame(struct ivtv *itv, void __user *src); ++void ivtv_yuv_frame_complete(struct ivtv *itv); ++int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); ++void ivtv_yuv_close(struct ivtv *itv); ++void ivtv_yuv_work_handler(struct ivtv *itv); ++ ++#endif +diff --git a/drivers/media/pci/ivtv/ivtvfb.c b/drivers/media/pci/ivtv/ivtvfb.c +new file mode 100644 +index 0000000..05b94aa +--- /dev/null ++++ b/drivers/media/pci/ivtv/ivtvfb.c +@@ -0,0 +1,1317 @@ ++/* ++ On Screen Display cx23415 Framebuffer driver ++ ++ This module presents the cx23415 OSD (onscreen display) framebuffer memory ++ as a standard Linux /dev/fb style framebuffer device. The framebuffer has ++ support for 8, 16 & 32 bpp packed pixel formats with alpha channel. In 16bpp ++ mode, there is a choice of a three color depths (12, 15 or 16 bits), but no ++ local alpha. The colorspace is selectable between rgb & yuv. ++ Depending on the TV standard configured in the ivtv module at load time, ++ the initial resolution is either 640x400 (NTSC) or 640x480 (PAL) at 8bpp. ++ Video timings are locked to ensure a vertical refresh rate of 50Hz (PAL) ++ or 59.94 (NTSC) ++ ++ Copyright (c) 2003 Matt T. Yourst ++ ++ Derived from drivers/video/vesafb.c ++ Portions (c) 1998 Gerd Knorr ++ ++ 2.6 kernel port: ++ Copyright (C) 2004 Matthias Badaire ++ ++ Copyright (C) 2004 Chris Kennedy ++ ++ Copyright (C) 2006 Ian Armstrong ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MTRR ++#include ++#endif ++ ++#include "ivtv-driver.h" ++#include "ivtv-cards.h" ++#include "ivtv-i2c.h" ++#include "ivtv-udma.h" ++#include "ivtv-mailbox.h" ++#include "ivtv-firmware.h" ++ ++/* card parameters */ ++static int ivtvfb_card_id = -1; ++static int ivtvfb_debug = 0; ++static bool osd_laced; ++static int osd_depth; ++static int osd_upper; ++static int osd_left; ++static int osd_yres; ++static int osd_xres; ++ ++module_param(ivtvfb_card_id, int, 0444); ++module_param_named(debug,ivtvfb_debug, int, 0644); ++module_param(osd_laced, bool, 0444); ++module_param(osd_depth, int, 0444); ++module_param(osd_upper, int, 0444); ++module_param(osd_left, int, 0444); ++module_param(osd_yres, int, 0444); ++module_param(osd_xres, int, 0444); ++ ++MODULE_PARM_DESC(ivtvfb_card_id, ++ "Only use framebuffer of the specified ivtv card (0-31)\n" ++ "\t\t\tdefault -1: initialize all available framebuffers"); ++ ++MODULE_PARM_DESC(debug, ++ "Debug level (bitmask). Default: errors only\n" ++ "\t\t\t(debug = 3 gives full debugging)"); ++ ++/* Why upper, left, xres, yres, depth, laced ? To match terminology used ++ by fbset. ++ Why start at 1 for left & upper coordinate ? Because X doesn't allow 0 */ ++ ++MODULE_PARM_DESC(osd_laced, ++ "Interlaced mode\n" ++ "\t\t\t0=off\n" ++ "\t\t\t1=on\n" ++ "\t\t\tdefault off"); ++ ++MODULE_PARM_DESC(osd_depth, ++ "Bits per pixel - 8, 16, 32\n" ++ "\t\t\tdefault 8"); ++ ++MODULE_PARM_DESC(osd_upper, ++ "Vertical start position\n" ++ "\t\t\tdefault 0 (Centered)"); ++ ++MODULE_PARM_DESC(osd_left, ++ "Horizontal start position\n" ++ "\t\t\tdefault 0 (Centered)"); ++ ++MODULE_PARM_DESC(osd_yres, ++ "Display height\n" ++ "\t\t\tdefault 480 (PAL)\n" ++ "\t\t\t 400 (NTSC)"); ++ ++MODULE_PARM_DESC(osd_xres, ++ "Display width\n" ++ "\t\t\tdefault 640"); ++ ++MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil, John Harvey, Ian Armstrong"); ++MODULE_LICENSE("GPL"); ++ ++/* --------------------------------------------------------------------- */ ++ ++#define IVTVFB_DBGFLG_WARN (1 << 0) ++#define IVTVFB_DBGFLG_INFO (1 << 1) ++ ++#define IVTVFB_DEBUG(x, type, fmt, args...) \ ++ do { \ ++ if ((x) & ivtvfb_debug) \ ++ printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ ++ } while (0) ++#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) ++#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) ++ ++/* Standard kernel messages */ ++#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) ++#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) ++#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) ++ ++/* --------------------------------------------------------------------- */ ++ ++#define IVTV_OSD_MAX_WIDTH 720 ++#define IVTV_OSD_MAX_HEIGHT 576 ++ ++#define IVTV_OSD_BPP_8 0x00 ++#define IVTV_OSD_BPP_16_444 0x03 ++#define IVTV_OSD_BPP_16_555 0x02 ++#define IVTV_OSD_BPP_16_565 0x01 ++#define IVTV_OSD_BPP_32 0x04 ++ ++struct osd_info { ++ /* Physical base address */ ++ unsigned long video_pbase; ++ /* Relative base address (relative to start of decoder memory) */ ++ u32 video_rbase; ++ /* Mapped base address */ ++ volatile char __iomem *video_vbase; ++ /* Buffer size */ ++ u32 video_buffer_size; ++ ++#ifdef CONFIG_MTRR ++ /* video_base rounded down as required by hardware MTRRs */ ++ unsigned long fb_start_aligned_physaddr; ++ /* video_base rounded up as required by hardware MTRRs */ ++ unsigned long fb_end_aligned_physaddr; ++#endif ++ ++ /* Store the buffer offset */ ++ int set_osd_coords_x; ++ int set_osd_coords_y; ++ ++ /* Current dimensions (NOT VISIBLE SIZE!) */ ++ int display_width; ++ int display_height; ++ int display_byte_stride; ++ ++ /* Current bits per pixel */ ++ int bits_per_pixel; ++ int bytes_per_pixel; ++ ++ /* Frame buffer stuff */ ++ struct fb_info ivtvfb_info; ++ struct fb_var_screeninfo ivtvfb_defined; ++ struct fb_fix_screeninfo ivtvfb_fix; ++ ++ /* Used for a warm start */ ++ struct fb_var_screeninfo fbvar_cur; ++ int blank_cur; ++ u32 palette_cur[256]; ++ u32 pan_cur; ++}; ++ ++struct ivtv_osd_coords { ++ unsigned long offset; ++ unsigned long max_offset; ++ int pixel_stride; ++ int lines; ++ int x; ++ int y; ++}; ++ ++/* --------------------------------------------------------------------- */ ++ ++/* ivtv API calls for framebuffer related support */ ++ ++static int ivtvfb_get_framebuffer(struct ivtv *itv, u32 *fbbase, ++ u32 *fblength) ++{ ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ int rc; ++ ++ ivtv_firmware_check(itv, "ivtvfb_get_framebuffer"); ++ rc = ivtv_vapi_result(itv, data, CX2341X_OSD_GET_FRAMEBUFFER, 0); ++ *fbbase = data[0]; ++ *fblength = data[1]; ++ return rc; ++} ++ ++static int ivtvfb_get_osd_coords(struct ivtv *itv, ++ struct ivtv_osd_coords *osd) ++{ ++ struct osd_info *oi = itv->osd_info; ++ u32 data[CX2341X_MBOX_MAX_DATA]; ++ ++ ivtv_vapi_result(itv, data, CX2341X_OSD_GET_OSD_COORDS, 0); ++ ++ osd->offset = data[0] - oi->video_rbase; ++ osd->max_offset = oi->display_width * oi->display_height * 4; ++ osd->pixel_stride = data[1]; ++ osd->lines = data[2]; ++ osd->x = data[3]; ++ osd->y = data[4]; ++ return 0; ++} ++ ++static int ivtvfb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords *osd) ++{ ++ struct osd_info *oi = itv->osd_info; ++ ++ oi->display_width = osd->pixel_stride; ++ oi->display_byte_stride = osd->pixel_stride * oi->bytes_per_pixel; ++ oi->set_osd_coords_x += osd->x; ++ oi->set_osd_coords_y = osd->y; ++ ++ return ivtv_vapi(itv, CX2341X_OSD_SET_OSD_COORDS, 5, ++ osd->offset + oi->video_rbase, ++ osd->pixel_stride, ++ osd->lines, osd->x, osd->y); ++} ++ ++static int ivtvfb_set_display_window(struct ivtv *itv, struct v4l2_rect *ivtv_window) ++{ ++ int osd_height_limit = itv->is_out_50hz ? 576 : 480; ++ ++ /* Only fail if resolution too high, otherwise fudge the start coords. */ ++ if ((ivtv_window->height > osd_height_limit) || (ivtv_window->width > IVTV_OSD_MAX_WIDTH)) ++ return -EINVAL; ++ ++ /* Ensure we don't exceed display limits */ ++ if (ivtv_window->top + ivtv_window->height > osd_height_limit) { ++ IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid height setting (%d, %d)\n", ++ ivtv_window->top, ivtv_window->height); ++ ivtv_window->top = osd_height_limit - ivtv_window->height; ++ } ++ ++ if (ivtv_window->left + ivtv_window->width > IVTV_OSD_MAX_WIDTH) { ++ IVTVFB_DEBUG_WARN("ivtv_ioctl_fb_set_display_window - Invalid width setting (%d, %d)\n", ++ ivtv_window->left, ivtv_window->width); ++ ivtv_window->left = IVTV_OSD_MAX_WIDTH - ivtv_window->width; ++ } ++ ++ /* Set the OSD origin */ ++ write_reg((ivtv_window->top << 16) | ivtv_window->left, 0x02a04); ++ ++ /* How much to display */ ++ write_reg(((ivtv_window->top+ivtv_window->height) << 16) | (ivtv_window->left+ivtv_window->width), 0x02a08); ++ ++ /* Pass this info back the yuv handler */ ++ itv->yuv_info.osd_vis_w = ivtv_window->width; ++ itv->yuv_info.osd_vis_h = ivtv_window->height; ++ itv->yuv_info.osd_x_offset = ivtv_window->left; ++ itv->yuv_info.osd_y_offset = ivtv_window->top; ++ ++ return 0; ++} ++ ++static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, ++ unsigned long ivtv_dest_addr, void __user *userbuf, ++ int size_in_bytes) ++{ ++ DEFINE_WAIT(wait); ++ int got_sig = 0; ++ ++ mutex_lock(&itv->udma.lock); ++ /* Map User DMA */ ++ if (ivtv_udma_setup(itv, ivtv_dest_addr, userbuf, size_in_bytes) <= 0) { ++ mutex_unlock(&itv->udma.lock); ++ IVTVFB_WARN("ivtvfb_prep_dec_dma_to_device, " ++ "Error with get_user_pages: %d bytes, %d pages returned\n", ++ size_in_bytes, itv->udma.page_count); ++ ++ /* get_user_pages must have failed completely */ ++ return -EIO; ++ } ++ ++ IVTVFB_DEBUG_INFO("ivtvfb_prep_dec_dma_to_device, %d bytes, %d pages\n", ++ size_in_bytes, itv->udma.page_count); ++ ++ ivtv_udma_prepare(itv); ++ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); ++ /* if no UDMA is pending and no UDMA is in progress, then the DMA ++ is finished */ ++ while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || ++ test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { ++ /* don't interrupt if the DMA is in progress but break off ++ a still pending DMA. */ ++ got_sig = signal_pending(current); ++ if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) ++ break; ++ got_sig = 0; ++ schedule(); ++ } ++ finish_wait(&itv->dma_waitq, &wait); ++ ++ /* Unmap Last DMA Xfer */ ++ ivtv_udma_unmap(itv); ++ mutex_unlock(&itv->udma.lock); ++ if (got_sig) { ++ IVTV_DEBUG_INFO("User stopped OSD\n"); ++ return -EINTR; ++ } ++ ++ return 0; ++} ++ ++static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source, ++ unsigned long dest_offset, int count) ++{ ++ DEFINE_WAIT(wait); ++ struct osd_info *oi = itv->osd_info; ++ ++ /* Nothing to do */ ++ if (count == 0) { ++ IVTVFB_DEBUG_WARN("ivtvfb_prep_frame: Nothing to do. count = 0\n"); ++ return -EINVAL; ++ } ++ ++ /* Check Total FB Size */ ++ if ((dest_offset + count) > oi->video_buffer_size) { ++ IVTVFB_WARN("ivtvfb_prep_frame: Overflowing the framebuffer %ld, only %d available\n", ++ dest_offset + count, oi->video_buffer_size); ++ return -E2BIG; ++ } ++ ++ /* Not fatal, but will have undesirable results */ ++ if ((unsigned long)source & 3) ++ IVTVFB_WARN("ivtvfb_prep_frame: Source address not 32 bit aligned (0x%08lx)\n", ++ (unsigned long)source); ++ ++ if (dest_offset & 3) ++ IVTVFB_WARN("ivtvfb_prep_frame: Dest offset not 32 bit aligned (%ld)\n", dest_offset); ++ ++ if (count & 3) ++ IVTVFB_WARN("ivtvfb_prep_frame: Count not a multiple of 4 (%d)\n", count); ++ ++ /* Check Source */ ++ if (!access_ok(VERIFY_READ, source + dest_offset, count)) { ++ IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n", ++ (unsigned long)source); ++ ++ IVTVFB_DEBUG_WARN("access_ok() failed for offset 0x%08lx source 0x%08lx count %d\n", ++ dest_offset, (unsigned long)source, ++ count); ++ return -EINVAL; ++ } ++ ++ /* OSD Address to send DMA to */ ++ dest_offset += IVTV_DECODER_OFFSET + oi->video_rbase; ++ ++ /* Fill Buffers */ ++ return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count); ++} ++ ++static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ unsigned long p = *ppos; ++ void *dst; ++ int err = 0; ++ int dma_err; ++ unsigned long total_size; ++ struct ivtv *itv = (struct ivtv *) info->par; ++ unsigned long dma_offset = ++ IVTV_DECODER_OFFSET + itv->osd_info->video_rbase; ++ unsigned long dma_size; ++ u16 lead = 0, tail = 0; ++ ++ if (info->state != FBINFO_STATE_RUNNING) ++ return -EPERM; ++ ++ total_size = info->screen_size; ++ ++ if (total_size == 0) ++ total_size = info->fix.smem_len; ++ ++ if (p > total_size) ++ return -EFBIG; ++ ++ if (count > total_size) { ++ err = -EFBIG; ++ count = total_size; ++ } ++ ++ if (count + p > total_size) { ++ if (!err) ++ err = -ENOSPC; ++ count = total_size - p; ++ } ++ ++ dst = (void __force *) (info->screen_base + p); ++ ++ if (info->fbops->fb_sync) ++ info->fbops->fb_sync(info); ++ ++ /* If transfer size > threshold and both src/dst ++ addresses are aligned, use DMA */ ++ if (count >= 4096 && ++ ((unsigned long)buf & 3) == ((unsigned long)dst & 3)) { ++ /* Odd address = can't DMA. Align */ ++ if ((unsigned long)dst & 3) { ++ lead = 4 - ((unsigned long)dst & 3); ++ if (copy_from_user(dst, buf, lead)) ++ return -EFAULT; ++ buf += lead; ++ dst += lead; ++ } ++ /* DMA resolution is 32 bits */ ++ if ((count - lead) & 3) ++ tail = (count - lead) & 3; ++ /* DMA the data */ ++ dma_size = count - lead - tail; ++ dma_err = ivtvfb_prep_dec_dma_to_device(itv, ++ p + lead + dma_offset, (void __user *)buf, dma_size); ++ if (dma_err) ++ return dma_err; ++ dst += dma_size; ++ buf += dma_size; ++ /* Copy any leftover data */ ++ if (tail && copy_from_user(dst, buf, tail)) ++ return -EFAULT; ++ } else if (copy_from_user(dst, buf, count)) { ++ return -EFAULT; ++ } ++ ++ if (!err) ++ *ppos += count; ++ ++ return (err) ? err : count; ++} ++ ++static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) ++{ ++ DEFINE_WAIT(wait); ++ struct ivtv *itv = (struct ivtv *)info->par; ++ int rc = 0; ++ ++ switch (cmd) { ++ case FBIOGET_VBLANK: { ++ struct fb_vblank vblank; ++ u32 trace; ++ ++ memset(&vblank, 0, sizeof(struct fb_vblank)); ++ ++ vblank.flags = FB_VBLANK_HAVE_COUNT |FB_VBLANK_HAVE_VCOUNT | ++ FB_VBLANK_HAVE_VSYNC; ++ trace = read_reg(IVTV_REG_DEC_LINE_FIELD) >> 16; ++ if (itv->is_out_50hz && trace > 312) ++ trace -= 312; ++ else if (itv->is_out_60hz && trace > 262) ++ trace -= 262; ++ if (trace == 1) ++ vblank.flags |= FB_VBLANK_VSYNCING; ++ vblank.count = itv->last_vsync_field; ++ vblank.vcount = trace; ++ vblank.hcount = 0; ++ if (copy_to_user((void __user *)arg, &vblank, sizeof(vblank))) ++ return -EFAULT; ++ return 0; ++ } ++ ++ case FBIO_WAITFORVSYNC: ++ prepare_to_wait(&itv->vsync_waitq, &wait, TASK_INTERRUPTIBLE); ++ if (!schedule_timeout(msecs_to_jiffies(50))) ++ rc = -ETIMEDOUT; ++ finish_wait(&itv->vsync_waitq, &wait); ++ return rc; ++ ++ case IVTVFB_IOC_DMA_FRAME: { ++ struct ivtvfb_dma_frame args; ++ ++ IVTVFB_DEBUG_INFO("IVTVFB_IOC_DMA_FRAME\n"); ++ if (copy_from_user(&args, (void __user *)arg, sizeof(args))) ++ return -EFAULT; ++ ++ return ivtvfb_prep_frame(itv, cmd, args.source, args.dest_offset, args.count); ++ } ++ ++ default: ++ IVTVFB_DEBUG_INFO("Unknown ioctl %08x\n", cmd); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* Framebuffer device handling */ ++ ++static int ivtvfb_set_var(struct ivtv *itv, struct fb_var_screeninfo *var) ++{ ++ struct osd_info *oi = itv->osd_info; ++ struct ivtv_osd_coords ivtv_osd; ++ struct v4l2_rect ivtv_window; ++ int osd_mode = -1; ++ ++ IVTVFB_DEBUG_INFO("ivtvfb_set_var\n"); ++ ++ /* Select color space */ ++ if (var->nonstd) /* YUV */ ++ write_reg(read_reg(0x02a00) | 0x0002000, 0x02a00); ++ else /* RGB */ ++ write_reg(read_reg(0x02a00) & ~0x0002000, 0x02a00); ++ ++ /* Set the color mode */ ++ switch (var->bits_per_pixel) { ++ case 8: ++ osd_mode = IVTV_OSD_BPP_8; ++ break; ++ case 32: ++ osd_mode = IVTV_OSD_BPP_32; ++ break; ++ case 16: ++ switch (var->green.length) { ++ case 4: ++ osd_mode = IVTV_OSD_BPP_16_444; ++ break; ++ case 5: ++ osd_mode = IVTV_OSD_BPP_16_555; ++ break; ++ case 6: ++ osd_mode = IVTV_OSD_BPP_16_565; ++ break; ++ default: ++ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); ++ } ++ break; ++ default: ++ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid bpp\n"); ++ } ++ ++ /* Set video mode. Although rare, the display can become scrambled even ++ if we don't change mode. Always 'bounce' to osd_mode via mode 0 */ ++ if (osd_mode != -1) { ++ ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, 0); ++ ivtv_vapi(itv, CX2341X_OSD_SET_PIXEL_FORMAT, 1, osd_mode); ++ } ++ ++ oi->bits_per_pixel = var->bits_per_pixel; ++ oi->bytes_per_pixel = var->bits_per_pixel / 8; ++ ++ /* Set the flicker filter */ ++ switch (var->vmode & FB_VMODE_MASK) { ++ case FB_VMODE_NONINTERLACED: /* Filter on */ ++ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 1); ++ break; ++ case FB_VMODE_INTERLACED: /* Filter off */ ++ ivtv_vapi(itv, CX2341X_OSD_SET_FLICKER_STATE, 1, 0); ++ break; ++ default: ++ IVTVFB_DEBUG_WARN("ivtvfb_set_var - Invalid video mode\n"); ++ } ++ ++ /* Read the current osd info */ ++ ivtvfb_get_osd_coords(itv, &ivtv_osd); ++ ++ /* Now set the OSD to the size we want */ ++ ivtv_osd.pixel_stride = var->xres_virtual; ++ ivtv_osd.lines = var->yres_virtual; ++ ivtv_osd.x = 0; ++ ivtv_osd.y = 0; ++ ivtvfb_set_osd_coords(itv, &ivtv_osd); ++ ++ /* Can't seem to find the right API combo for this. ++ Use another function which does what we need through direct register access. */ ++ ivtv_window.width = var->xres; ++ ivtv_window.height = var->yres; ++ ++ /* Minimum margin cannot be 0, as X won't allow such a mode */ ++ if (!var->upper_margin) ++ var->upper_margin++; ++ if (!var->left_margin) ++ var->left_margin++; ++ ivtv_window.top = var->upper_margin - 1; ++ ivtv_window.left = var->left_margin - 1; ++ ++ ivtvfb_set_display_window(itv, &ivtv_window); ++ ++ /* Pass screen size back to yuv handler */ ++ itv->yuv_info.osd_full_w = ivtv_osd.pixel_stride; ++ itv->yuv_info.osd_full_h = ivtv_osd.lines; ++ ++ /* Force update of yuv registers */ ++ itv->yuv_info.yuv_forced_update = 1; ++ ++ /* Keep a copy of these settings */ ++ memcpy(&oi->fbvar_cur, var, sizeof(oi->fbvar_cur)); ++ ++ IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", ++ var->xres, var->yres, ++ var->xres_virtual, var->yres_virtual, ++ var->bits_per_pixel); ++ ++ IVTVFB_DEBUG_INFO("Display position: %d, %d\n", ++ var->left_margin, var->upper_margin); ++ ++ IVTVFB_DEBUG_INFO("Display filter: %s\n", ++ (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); ++ IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); ++ ++ return 0; ++} ++ ++static int ivtvfb_get_fix(struct ivtv *itv, struct fb_fix_screeninfo *fix) ++{ ++ struct osd_info *oi = itv->osd_info; ++ ++ IVTVFB_DEBUG_INFO("ivtvfb_get_fix\n"); ++ memset(fix, 0, sizeof(struct fb_fix_screeninfo)); ++ strlcpy(fix->id, "cx23415 TV out", sizeof(fix->id)); ++ fix->smem_start = oi->video_pbase; ++ fix->smem_len = oi->video_buffer_size; ++ fix->type = FB_TYPE_PACKED_PIXELS; ++ fix->visual = (oi->bits_per_pixel == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; ++ fix->xpanstep = 1; ++ fix->ypanstep = 1; ++ fix->ywrapstep = 0; ++ fix->line_length = oi->display_byte_stride; ++ fix->accel = FB_ACCEL_NONE; ++ return 0; ++} ++ ++/* Check the requested display mode, returning -EINVAL if we can't ++ handle it. */ ++ ++static int _ivtvfb_check_var(struct fb_var_screeninfo *var, struct ivtv *itv) ++{ ++ struct osd_info *oi = itv->osd_info; ++ int osd_height_limit; ++ u32 pixclock, hlimit, vlimit; ++ ++ IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); ++ ++ /* Set base references for mode calcs. */ ++ if (itv->is_out_50hz) { ++ pixclock = 84316; ++ hlimit = 776; ++ vlimit = 591; ++ osd_height_limit = 576; ++ } ++ else { ++ pixclock = 83926; ++ hlimit = 776; ++ vlimit = 495; ++ osd_height_limit = 480; ++ } ++ ++ if (var->bits_per_pixel == 8 || var->bits_per_pixel == 32) { ++ var->transp.offset = 24; ++ var->transp.length = 8; ++ var->red.offset = 16; ++ var->red.length = 8; ++ var->green.offset = 8; ++ var->green.length = 8; ++ var->blue.offset = 0; ++ var->blue.length = 8; ++ } ++ else if (var->bits_per_pixel == 16) { ++ /* To find out the true mode, check green length */ ++ switch (var->green.length) { ++ case 4: ++ var->red.offset = 8; ++ var->red.length = 4; ++ var->green.offset = 4; ++ var->green.length = 4; ++ var->blue.offset = 0; ++ var->blue.length = 4; ++ var->transp.offset = 12; ++ var->transp.length = 1; ++ break; ++ case 5: ++ var->red.offset = 10; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 5; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ var->transp.offset = 15; ++ var->transp.length = 1; ++ break; ++ default: ++ var->red.offset = 11; ++ var->red.length = 5; ++ var->green.offset = 5; ++ var->green.length = 6; ++ var->blue.offset = 0; ++ var->blue.length = 5; ++ var->transp.offset = 0; ++ var->transp.length = 0; ++ break; ++ } ++ } ++ else { ++ IVTVFB_DEBUG_WARN("Invalid colour mode: %d\n", var->bits_per_pixel); ++ return -EINVAL; ++ } ++ ++ /* Check the resolution */ ++ if (var->xres > IVTV_OSD_MAX_WIDTH || var->yres > osd_height_limit) { ++ IVTVFB_DEBUG_WARN("Invalid resolution: %dx%d\n", ++ var->xres, var->yres); ++ return -EINVAL; ++ } ++ ++ /* Max horizontal size is 1023 @ 32bpp, 2046 & 16bpp, 4092 @ 8bpp */ ++ if (var->xres_virtual > 4095 / (var->bits_per_pixel / 8) || ++ var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8) > oi->video_buffer_size || ++ var->xres_virtual < var->xres || ++ var->yres_virtual < var->yres) { ++ IVTVFB_DEBUG_WARN("Invalid virtual resolution: %dx%d\n", ++ var->xres_virtual, var->yres_virtual); ++ return -EINVAL; ++ } ++ ++ /* Some extra checks if in 8 bit mode */ ++ if (var->bits_per_pixel == 8) { ++ /* Width must be a multiple of 4 */ ++ if (var->xres & 3) { ++ IVTVFB_DEBUG_WARN("Invalid resolution for 8bpp: %d\n", var->xres); ++ return -EINVAL; ++ } ++ if (var->xres_virtual & 3) { ++ IVTVFB_DEBUG_WARN("Invalid virtual resolution for 8bpp: %d)\n", var->xres_virtual); ++ return -EINVAL; ++ } ++ } ++ else if (var->bits_per_pixel == 16) { ++ /* Width must be a multiple of 2 */ ++ if (var->xres & 1) { ++ IVTVFB_DEBUG_WARN("Invalid resolution for 16bpp: %d\n", var->xres); ++ return -EINVAL; ++ } ++ if (var->xres_virtual & 1) { ++ IVTVFB_DEBUG_WARN("Invalid virtual resolution for 16bpp: %d)\n", var->xres_virtual); ++ return -EINVAL; ++ } ++ } ++ ++ /* Now check the offsets */ ++ if (var->xoffset >= var->xres_virtual || var->yoffset >= var->yres_virtual) { ++ IVTVFB_DEBUG_WARN("Invalid offset: %d (%d) %d (%d)\n", ++ var->xoffset, var->xres_virtual, var->yoffset, var->yres_virtual); ++ return -EINVAL; ++ } ++ ++ /* Check pixel format */ ++ if (var->nonstd > 1) { ++ IVTVFB_DEBUG_WARN("Invalid nonstd % d\n", var->nonstd); ++ return -EINVAL; ++ } ++ ++ /* Check video mode */ ++ if (((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) && ++ ((var->vmode & FB_VMODE_MASK) != FB_VMODE_INTERLACED)) { ++ IVTVFB_DEBUG_WARN("Invalid video mode: %d\n", var->vmode & FB_VMODE_MASK); ++ return -EINVAL; ++ } ++ ++ /* Check the left & upper margins ++ If the margins are too large, just center the screen ++ (enforcing margins causes too many problems) */ ++ ++ if (var->left_margin + var->xres > IVTV_OSD_MAX_WIDTH + 1) ++ var->left_margin = 1 + ((IVTV_OSD_MAX_WIDTH - var->xres) / 2); ++ ++ if (var->upper_margin + var->yres > (itv->is_out_50hz ? 577 : 481)) ++ var->upper_margin = 1 + (((itv->is_out_50hz ? 576 : 480) - ++ var->yres) / 2); ++ ++ /* Maintain overall 'size' for a constant refresh rate */ ++ var->right_margin = hlimit - var->left_margin - var->xres; ++ var->lower_margin = vlimit - var->upper_margin - var->yres; ++ ++ /* Fixed sync times */ ++ var->hsync_len = 24; ++ var->vsync_len = 2; ++ ++ /* Non-interlaced / interlaced mode is used to switch the OSD filter ++ on or off. Adjust the clock timings to maintain a constant ++ vertical refresh rate. */ ++ if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED) ++ var->pixclock = pixclock / 2; ++ else ++ var->pixclock = pixclock; ++ ++ itv->osd_rect.width = var->xres; ++ itv->osd_rect.height = var->yres; ++ ++ IVTVFB_DEBUG_INFO("Display size: %dx%d (virtual %dx%d) @ %dbpp\n", ++ var->xres, var->yres, ++ var->xres_virtual, var->yres_virtual, ++ var->bits_per_pixel); ++ ++ IVTVFB_DEBUG_INFO("Display position: %d, %d\n", ++ var->left_margin, var->upper_margin); ++ ++ IVTVFB_DEBUG_INFO("Display filter: %s\n", ++ (var->vmode & FB_VMODE_MASK) == FB_VMODE_NONINTERLACED ? "on" : "off"); ++ IVTVFB_DEBUG_INFO("Color space: %s\n", var->nonstd ? "YUV" : "RGB"); ++ return 0; ++} ++ ++static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ struct ivtv *itv = (struct ivtv *) info->par; ++ IVTVFB_DEBUG_INFO("ivtvfb_check_var\n"); ++ return _ivtvfb_check_var(var, itv); ++} ++ ++static int ivtvfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ u32 osd_pan_index; ++ struct ivtv *itv = (struct ivtv *) info->par; ++ ++ if (var->yoffset + info->var.yres > info->var.yres_virtual || ++ var->xoffset + info->var.xres > info->var.xres_virtual) ++ return -EINVAL; ++ ++ osd_pan_index = var->yoffset * info->fix.line_length ++ + var->xoffset * info->var.bits_per_pixel / 8; ++ write_reg(osd_pan_index, 0x02A0C); ++ ++ /* Pass this info back the yuv handler */ ++ itv->yuv_info.osd_x_pan = var->xoffset; ++ itv->yuv_info.osd_y_pan = var->yoffset; ++ /* Force update of yuv registers */ ++ itv->yuv_info.yuv_forced_update = 1; ++ /* Remember this value */ ++ itv->osd_info->pan_cur = osd_pan_index; ++ return 0; ++} ++ ++static int ivtvfb_set_par(struct fb_info *info) ++{ ++ int rc = 0; ++ struct ivtv *itv = (struct ivtv *) info->par; ++ ++ IVTVFB_DEBUG_INFO("ivtvfb_set_par\n"); ++ ++ rc = ivtvfb_set_var(itv, &info->var); ++ ivtvfb_pan_display(&info->var, info); ++ ivtvfb_get_fix(itv, &info->fix); ++ ivtv_firmware_check(itv, "ivtvfb_set_par"); ++ return rc; ++} ++ ++static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info) ++{ ++ u32 color, *palette; ++ struct ivtv *itv = (struct ivtv *)info->par; ++ ++ if (regno >= info->cmap.len) ++ return -EINVAL; ++ ++ color = ((transp & 0xFF00) << 16) |((red & 0xFF00) << 8) | (green & 0xFF00) | ((blue & 0xFF00) >> 8); ++ if (info->var.bits_per_pixel <= 8) { ++ write_reg(regno, 0x02a30); ++ write_reg(color, 0x02a34); ++ itv->osd_info->palette_cur[regno] = color; ++ return 0; ++ } ++ if (regno >= 16) ++ return -EINVAL; ++ ++ palette = info->pseudo_palette; ++ if (info->var.bits_per_pixel == 16) { ++ switch (info->var.green.length) { ++ case 4: ++ color = ((red & 0xf000) >> 4) | ++ ((green & 0xf000) >> 8) | ++ ((blue & 0xf000) >> 12); ++ break; ++ case 5: ++ color = ((red & 0xf800) >> 1) | ++ ((green & 0xf800) >> 6) | ++ ((blue & 0xf800) >> 11); ++ break; ++ case 6: ++ color = (red & 0xf800 ) | ++ ((green & 0xfc00) >> 5) | ++ ((blue & 0xf800) >> 11); ++ break; ++ } ++ } ++ palette[regno] = color; ++ return 0; ++} ++ ++/* We don't really support blanking. All this does is enable or ++ disable the OSD. */ ++static int ivtvfb_blank(int blank_mode, struct fb_info *info) ++{ ++ struct ivtv *itv = (struct ivtv *)info->par; ++ ++ IVTVFB_DEBUG_INFO("Set blanking mode : %d\n", blank_mode); ++ switch (blank_mode) { ++ case FB_BLANK_UNBLANK: ++ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ++ break; ++ case FB_BLANK_NORMAL: ++ case FB_BLANK_HSYNC_SUSPEND: ++ case FB_BLANK_VSYNC_SUSPEND: ++ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ++ break; ++ case FB_BLANK_POWERDOWN: ++ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ++ ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); ++ break; ++ } ++ itv->osd_info->blank_cur = blank_mode; ++ return 0; ++} ++ ++static struct fb_ops ivtvfb_ops = { ++ .owner = THIS_MODULE, ++ .fb_write = ivtvfb_write, ++ .fb_check_var = ivtvfb_check_var, ++ .fb_set_par = ivtvfb_set_par, ++ .fb_setcolreg = ivtvfb_setcolreg, ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++ .fb_imageblit = cfb_imageblit, ++ .fb_cursor = NULL, ++ .fb_ioctl = ivtvfb_ioctl, ++ .fb_pan_display = ivtvfb_pan_display, ++ .fb_blank = ivtvfb_blank, ++}; ++ ++/* Restore hardware after firmware restart */ ++static void ivtvfb_restore(struct ivtv *itv) ++{ ++ struct osd_info *oi = itv->osd_info; ++ int i; ++ ++ ivtvfb_set_var(itv, &oi->fbvar_cur); ++ ivtvfb_blank(oi->blank_cur, &oi->ivtvfb_info); ++ for (i = 0; i < 256; i++) { ++ write_reg(i, 0x02a30); ++ write_reg(oi->palette_cur[i], 0x02a34); ++ } ++ write_reg(oi->pan_cur, 0x02a0c); ++} ++ ++/* Initialization */ ++ ++ ++/* Setup our initial video mode */ ++static int ivtvfb_init_vidmode(struct ivtv *itv) ++{ ++ struct osd_info *oi = itv->osd_info; ++ struct v4l2_rect start_window; ++ int max_height; ++ ++ /* Color mode */ ++ ++ if (osd_depth != 8 && osd_depth != 16 && osd_depth != 32) ++ osd_depth = 8; ++ oi->bits_per_pixel = osd_depth; ++ oi->bytes_per_pixel = oi->bits_per_pixel / 8; ++ ++ /* Horizontal size & position */ ++ ++ if (osd_xres > 720) ++ osd_xres = 720; ++ ++ /* Must be a multiple of 4 for 8bpp & 2 for 16bpp */ ++ if (osd_depth == 8) ++ osd_xres &= ~3; ++ else if (osd_depth == 16) ++ osd_xres &= ~1; ++ ++ start_window.width = osd_xres ? osd_xres : 640; ++ ++ /* Check horizontal start (osd_left). */ ++ if (osd_left && osd_left + start_window.width > 721) { ++ IVTVFB_ERR("Invalid osd_left - assuming default\n"); ++ osd_left = 0; ++ } ++ ++ /* Hardware coords start at 0, user coords start at 1. */ ++ osd_left--; ++ ++ start_window.left = osd_left >= 0 ? ++ osd_left : ((IVTV_OSD_MAX_WIDTH - start_window.width) / 2); ++ ++ oi->display_byte_stride = ++ start_window.width * oi->bytes_per_pixel; ++ ++ /* Vertical size & position */ ++ ++ max_height = itv->is_out_50hz ? 576 : 480; ++ ++ if (osd_yres > max_height) ++ osd_yres = max_height; ++ ++ start_window.height = osd_yres ? ++ osd_yres : itv->is_out_50hz ? 480 : 400; ++ ++ /* Check vertical start (osd_upper). */ ++ if (osd_upper + start_window.height > max_height + 1) { ++ IVTVFB_ERR("Invalid osd_upper - assuming default\n"); ++ osd_upper = 0; ++ } ++ ++ /* Hardware coords start at 0, user coords start at 1. */ ++ osd_upper--; ++ ++ start_window.top = osd_upper >= 0 ? osd_upper : ((max_height - start_window.height) / 2); ++ ++ oi->display_width = start_window.width; ++ oi->display_height = start_window.height; ++ ++ /* Generate a valid fb_var_screeninfo */ ++ ++ oi->ivtvfb_defined.xres = oi->display_width; ++ oi->ivtvfb_defined.yres = oi->display_height; ++ oi->ivtvfb_defined.xres_virtual = oi->display_width; ++ oi->ivtvfb_defined.yres_virtual = oi->display_height; ++ oi->ivtvfb_defined.bits_per_pixel = oi->bits_per_pixel; ++ oi->ivtvfb_defined.vmode = (osd_laced ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED); ++ oi->ivtvfb_defined.left_margin = start_window.left + 1; ++ oi->ivtvfb_defined.upper_margin = start_window.top + 1; ++ oi->ivtvfb_defined.accel_flags = FB_ACCEL_NONE; ++ oi->ivtvfb_defined.nonstd = 0; ++ ++ /* We've filled in the most data, let the usual mode check ++ routine fill in the rest. */ ++ _ivtvfb_check_var(&oi->ivtvfb_defined, itv); ++ ++ /* Generate valid fb_fix_screeninfo */ ++ ++ ivtvfb_get_fix(itv, &oi->ivtvfb_fix); ++ ++ /* Generate valid fb_info */ ++ ++ oi->ivtvfb_info.node = -1; ++ oi->ivtvfb_info.flags = FBINFO_FLAG_DEFAULT; ++ oi->ivtvfb_info.fbops = &ivtvfb_ops; ++ oi->ivtvfb_info.par = itv; ++ oi->ivtvfb_info.var = oi->ivtvfb_defined; ++ oi->ivtvfb_info.fix = oi->ivtvfb_fix; ++ oi->ivtvfb_info.screen_base = (u8 __iomem *)oi->video_vbase; ++ oi->ivtvfb_info.fbops = &ivtvfb_ops; ++ ++ /* Supply some monitor specs. Bogus values will do for now */ ++ oi->ivtvfb_info.monspecs.hfmin = 8000; ++ oi->ivtvfb_info.monspecs.hfmax = 70000; ++ oi->ivtvfb_info.monspecs.vfmin = 10; ++ oi->ivtvfb_info.monspecs.vfmax = 100; ++ ++ /* Allocate color map */ ++ if (fb_alloc_cmap(&oi->ivtvfb_info.cmap, 256, 1)) { ++ IVTVFB_ERR("abort, unable to alloc cmap\n"); ++ return -ENOMEM; ++ } ++ ++ /* Allocate the pseudo palette */ ++ oi->ivtvfb_info.pseudo_palette = ++ kmalloc(sizeof(u32) * 16, GFP_KERNEL|__GFP_NOWARN); ++ ++ if (!oi->ivtvfb_info.pseudo_palette) { ++ IVTVFB_ERR("abort, unable to alloc pseudo palette\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++/* Find OSD buffer base & size. Add to mtrr. Zero osd buffer. */ ++ ++static int ivtvfb_init_io(struct ivtv *itv) ++{ ++ struct osd_info *oi = itv->osd_info; ++ ++ mutex_lock(&itv->serialize_lock); ++ if (ivtv_init_on_first_open(itv)) { ++ mutex_unlock(&itv->serialize_lock); ++ IVTVFB_ERR("Failed to initialize ivtv\n"); ++ return -ENXIO; ++ } ++ mutex_unlock(&itv->serialize_lock); ++ ++ if (ivtvfb_get_framebuffer(itv, &oi->video_rbase, ++ &oi->video_buffer_size) < 0) { ++ IVTVFB_ERR("Firmware failed to respond\n"); ++ return -EIO; ++ } ++ ++ /* The osd buffer size depends on the number of video buffers allocated ++ on the PVR350 itself. For now we'll hardcode the smallest osd buffer ++ size to prevent any overlap. */ ++ oi->video_buffer_size = 1704960; ++ ++ oi->video_pbase = itv->base_addr + IVTV_DECODER_OFFSET + oi->video_rbase; ++ oi->video_vbase = itv->dec_mem + oi->video_rbase; ++ ++ if (!oi->video_vbase) { ++ IVTVFB_ERR("abort, video memory 0x%x @ 0x%lx isn't mapped!\n", ++ oi->video_buffer_size, oi->video_pbase); ++ return -EIO; ++ } ++ ++ IVTVFB_INFO("Framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", ++ oi->video_pbase, oi->video_vbase, ++ oi->video_buffer_size / 1024); ++ ++#ifdef CONFIG_MTRR ++ { ++ /* Find the largest power of two that maps the whole buffer */ ++ int size_shift = 31; ++ ++ while (!(oi->video_buffer_size & (1 << size_shift))) { ++ size_shift--; ++ } ++ size_shift++; ++ oi->fb_start_aligned_physaddr = oi->video_pbase & ~((1 << size_shift) - 1); ++ oi->fb_end_aligned_physaddr = oi->video_pbase + oi->video_buffer_size; ++ oi->fb_end_aligned_physaddr += (1 << size_shift) - 1; ++ oi->fb_end_aligned_physaddr &= ~((1 << size_shift) - 1); ++ if (mtrr_add(oi->fb_start_aligned_physaddr, ++ oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr, ++ MTRR_TYPE_WRCOMB, 1) < 0) { ++ IVTVFB_INFO("disabled mttr\n"); ++ oi->fb_start_aligned_physaddr = 0; ++ oi->fb_end_aligned_physaddr = 0; ++ } ++ } ++#endif ++ ++ /* Blank the entire osd. */ ++ memset_io(oi->video_vbase, 0, oi->video_buffer_size); ++ ++ return 0; ++} ++ ++/* Release any memory we've grabbed & remove mtrr entry */ ++static void ivtvfb_release_buffers (struct ivtv *itv) ++{ ++ struct osd_info *oi = itv->osd_info; ++ ++ /* Release cmap */ ++ if (oi->ivtvfb_info.cmap.len) ++ fb_dealloc_cmap(&oi->ivtvfb_info.cmap); ++ ++ /* Release pseudo palette */ ++ if (oi->ivtvfb_info.pseudo_palette) ++ kfree(oi->ivtvfb_info.pseudo_palette); ++ ++#ifdef CONFIG_MTRR ++ if (oi->fb_end_aligned_physaddr) { ++ mtrr_del(-1, oi->fb_start_aligned_physaddr, ++ oi->fb_end_aligned_physaddr - oi->fb_start_aligned_physaddr); ++ } ++#endif ++ ++ kfree(oi); ++ itv->osd_info = NULL; ++} ++ ++/* Initialize the specified card */ ++ ++static int ivtvfb_init_card(struct ivtv *itv) ++{ ++ int rc; ++ ++ if (itv->osd_info) { ++ IVTVFB_ERR("Card %d already initialised\n", ivtvfb_card_id); ++ return -EBUSY; ++ } ++ ++ itv->osd_info = kzalloc(sizeof(struct osd_info), ++ GFP_ATOMIC|__GFP_NOWARN); ++ if (itv->osd_info == NULL) { ++ IVTVFB_ERR("Failed to allocate memory for osd_info\n"); ++ return -ENOMEM; ++ } ++ ++ /* Find & setup the OSD buffer */ ++ rc = ivtvfb_init_io(itv); ++ if (rc) { ++ ivtvfb_release_buffers(itv); ++ return rc; ++ } ++ ++ /* Set the startup video mode information */ ++ if ((rc = ivtvfb_init_vidmode(itv))) { ++ ivtvfb_release_buffers(itv); ++ return rc; ++ } ++ ++ /* Register the framebuffer */ ++ if (register_framebuffer(&itv->osd_info->ivtvfb_info) < 0) { ++ ivtvfb_release_buffers(itv); ++ return -EINVAL; ++ } ++ ++ itv->osd_video_pbase = itv->osd_info->video_pbase; ++ ++ /* Set the card to the requested mode */ ++ ivtvfb_set_par(&itv->osd_info->ivtvfb_info); ++ ++ /* Set color 0 to black */ ++ write_reg(0, 0x02a30); ++ write_reg(0, 0x02a34); ++ ++ /* Enable the osd */ ++ ivtvfb_blank(FB_BLANK_UNBLANK, &itv->osd_info->ivtvfb_info); ++ ++ /* Enable restart */ ++ itv->ivtvfb_restore = ivtvfb_restore; ++ ++ /* Allocate DMA */ ++ ivtv_udma_alloc(itv); ++ return 0; ++ ++} ++ ++static int __init ivtvfb_callback_init(struct device *dev, void *p) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ if (ivtvfb_init_card(itv) == 0) { ++ IVTVFB_INFO("Framebuffer registered on %s\n", ++ itv->v4l2_dev.name); ++ (*(int *)p)++; ++ } ++ } ++ return 0; ++} ++ ++static int ivtvfb_callback_cleanup(struct device *dev, void *p) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); ++ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); ++ struct osd_info *oi = itv->osd_info; ++ ++ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { ++ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { ++ IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", ++ itv->instance); ++ return 0; ++ } ++ IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); ++ itv->ivtvfb_restore = NULL; ++ ivtvfb_blank(FB_BLANK_VSYNC_SUSPEND, &oi->ivtvfb_info); ++ ivtvfb_release_buffers(itv); ++ itv->osd_video_pbase = 0; ++ } ++ return 0; ++} ++ ++static int __init ivtvfb_init(void) ++{ ++ struct device_driver *drv; ++ int registered = 0; ++ int err; ++ ++ if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { ++ printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", ++ IVTV_MAX_CARDS - 1); ++ return -EINVAL; ++ } ++ ++ drv = driver_find("ivtv", &pci_bus_type); ++ err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); ++ (void)err; /* suppress compiler warning */ ++ if (!registered) { ++ printk(KERN_ERR "ivtvfb: no cards found\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static void ivtvfb_cleanup(void) ++{ ++ struct device_driver *drv; ++ int err; ++ ++ printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n"); ++ ++ drv = driver_find("ivtv", &pci_bus_type); ++ err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); ++ (void)err; /* suppress compiler warning */ ++} ++ ++module_init(ivtvfb_init); ++module_exit(ivtvfb_cleanup); +diff --git a/drivers/media/pci/mantis/Kconfig b/drivers/media/pci/mantis/Kconfig +new file mode 100644 +index 0000000..d3cc216 +--- /dev/null ++++ b/drivers/media/pci/mantis/Kconfig +@@ -0,0 +1,38 @@ ++config MANTIS_CORE ++ tristate "Mantis/Hopper PCI bridge based devices" ++ depends on PCI && I2C && INPUT && RC_CORE ++ ++ help ++ Support for PCI cards based on the Mantis and Hopper PCi bridge. ++ ++ Say Y if you own such a device and want to use it. ++ ++config DVB_MANTIS ++ tristate "MANTIS based cards" ++ depends on MANTIS_CORE && DVB_CORE && PCI && I2C ++ select DVB_MB86A16 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA665x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_PLL ++ help ++ Support for PCI cards based on the Mantis PCI bridge. ++ Say Y when you have a Mantis based DVB card and want to use it. ++ ++ If unsure say N. ++ ++config DVB_HOPPER ++ tristate "HOPPER based cards" ++ depends on MANTIS_CORE && DVB_CORE && PCI && I2C ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_PLL ++ help ++ Support for PCI cards based on the Hopper PCI bridge. ++ Say Y when you have a Hopper based DVB card and want to use it. ++ ++ If unsure say N +diff --git a/drivers/media/pci/mantis/Makefile b/drivers/media/pci/mantis/Makefile +new file mode 100644 +index 0000000..f715051 +--- /dev/null ++++ b/drivers/media/pci/mantis/Makefile +@@ -0,0 +1,28 @@ ++mantis_core-objs := mantis_ioc.o \ ++ mantis_uart.o \ ++ mantis_dma.o \ ++ mantis_pci.o \ ++ mantis_i2c.o \ ++ mantis_dvb.o \ ++ mantis_evm.o \ ++ mantis_hif.o \ ++ mantis_ca.o \ ++ mantis_pcmcia.o \ ++ mantis_input.o ++ ++mantis-objs := mantis_cards.o \ ++ mantis_vp1033.o \ ++ mantis_vp1034.o \ ++ mantis_vp1041.o \ ++ mantis_vp2033.o \ ++ mantis_vp2040.o \ ++ mantis_vp3030.o ++ ++hopper-objs := hopper_cards.o \ ++ hopper_vp3028.o ++ ++obj-$(CONFIG_MANTIS_CORE) += mantis_core.o ++obj-$(CONFIG_DVB_MANTIS) += mantis.o ++obj-$(CONFIG_DVB_HOPPER) += hopper.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ +diff --git a/drivers/media/pci/mantis/hopper_cards.c b/drivers/media/pci/mantis/hopper_cards.c +new file mode 100644 +index 0000000..cc0251e +--- /dev/null ++++ b/drivers/media/pci/mantis/hopper_cards.c +@@ -0,0 +1,277 @@ ++/* ++ Hopper PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "hopper_vp3028.h" ++#include "mantis_dma.h" ++#include "mantis_dvb.h" ++#include "mantis_uart.h" ++#include "mantis_ioc.h" ++#include "mantis_pci.h" ++#include "mantis_i2c.h" ++#include "mantis_reg.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); ++ ++#define DRIVER_NAME "Hopper" ++ ++static char *label[10] = { ++ "DMA", ++ "IRQ-0", ++ "IRQ-1", ++ "OCERR", ++ "PABRT", ++ "RIPRR", ++ "PPERR", ++ "FTRGT", ++ "RISCI", ++ "RACK" ++}; ++ ++static int devs; ++ ++static irqreturn_t hopper_irq_handler(int irq, void *dev_id) ++{ ++ u32 stat = 0, mask = 0; ++ u32 rst_stat = 0, rst_mask = 0; ++ ++ struct mantis_pci *mantis; ++ struct mantis_ca *ca; ++ ++ mantis = (struct mantis_pci *) dev_id; ++ if (unlikely(mantis == NULL)) { ++ dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); ++ return IRQ_NONE; ++ } ++ ca = mantis->mantis_ca; ++ ++ stat = mmread(MANTIS_INT_STAT); ++ mask = mmread(MANTIS_INT_MASK); ++ if (!(stat & mask)) ++ return IRQ_NONE; ++ ++ rst_mask = MANTIS_GPIF_WRACK | ++ MANTIS_GPIF_OTHERR | ++ MANTIS_SBUF_WSTO | ++ MANTIS_GPIF_EXTIRQ; ++ ++ rst_stat = mmread(MANTIS_GPIF_STATUS); ++ rst_stat &= rst_mask; ++ mmwrite(rst_stat, MANTIS_GPIF_STATUS); ++ ++ mantis->mantis_int_stat = stat; ++ mantis->mantis_int_mask = mask; ++ dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); ++ if (stat & MANTIS_INT_RISCEN) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); ++ } ++ if (stat & MANTIS_INT_IRQ0) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); ++ mantis->gpif_status = rst_stat; ++ wake_up(&ca->hif_write_wq); ++ schedule_work(&ca->hif_evm_work); ++ } ++ if (stat & MANTIS_INT_IRQ1) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); ++ schedule_work(&mantis->uart_work); ++ } ++ if (stat & MANTIS_INT_OCERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); ++ } ++ if (stat & MANTIS_INT_PABORT) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); ++ } ++ if (stat & MANTIS_INT_RIPERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); ++ } ++ if (stat & MANTIS_INT_PPERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); ++ } ++ if (stat & MANTIS_INT_FTRGT) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); ++ } ++ if (stat & MANTIS_INT_RISCI) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); ++ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; ++ tasklet_schedule(&mantis->tasklet); ++ } ++ if (stat & MANTIS_INT_I2CDONE) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); ++ wake_up(&mantis->i2c_wq); ++ } ++ mmwrite(stat, MANTIS_INT_STAT); ++ stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | ++ MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | ++ MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | ++ MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | ++ MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | ++ MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | ++ MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | ++ MANTIS_INT_PABORT | MANTIS_INT_RIPERR | ++ MANTIS_INT_PPERR | MANTIS_INT_FTRGT | ++ MANTIS_INT_RISCI); ++ ++ if (stat) ++ dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); ++ ++ dprintk(MANTIS_DEBUG, 0, "\n"); ++ return IRQ_HANDLED; ++} ++ ++static int __devinit hopper_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) ++{ ++ struct mantis_pci *mantis; ++ struct mantis_hwconfig *config; ++ int err = 0; ++ ++ mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); ++ if (mantis == NULL) { ++ printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); ++ err = -ENOMEM; ++ goto fail0; ++ } ++ ++ mantis->num = devs; ++ mantis->verbose = verbose; ++ mantis->pdev = pdev; ++ config = (struct mantis_hwconfig *) pci_id->driver_data; ++ config->irq_handler = &hopper_irq_handler; ++ mantis->hwconfig = config; ++ ++ err = mantis_pci_init(mantis); ++ if (err) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); ++ goto fail1; ++ } ++ ++ err = mantis_stream_control(mantis, STREAM_TO_HIF); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); ++ goto fail1; ++ } ++ ++ err = mantis_i2c_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); ++ goto fail2; ++ } ++ ++ err = mantis_get_mac(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); ++ goto fail2; ++ } ++ ++ err = mantis_dma_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); ++ goto fail3; ++ } ++ ++ err = mantis_dvb_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); ++ goto fail4; ++ } ++ devs++; ++ ++ return err; ++ ++fail4: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); ++ mantis_dma_exit(mantis); ++ ++fail3: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); ++ mantis_i2c_exit(mantis); ++ ++fail2: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); ++ mantis_pci_exit(mantis); ++ ++fail1: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); ++ kfree(mantis); ++ ++fail0: ++ return err; ++} ++ ++static void __devexit hopper_pci_remove(struct pci_dev *pdev) ++{ ++ struct mantis_pci *mantis = pci_get_drvdata(pdev); ++ ++ if (mantis) { ++ mantis_dvb_exit(mantis); ++ mantis_dma_exit(mantis); ++ mantis_i2c_exit(mantis); ++ mantis_pci_exit(mantis); ++ kfree(mantis); ++ } ++ return; ++ ++} ++ ++static struct pci_device_id hopper_pci_table[] = { ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3028_DVB_T, &vp3028_config), ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(pci, hopper_pci_table); ++ ++static struct pci_driver hopper_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = hopper_pci_table, ++ .probe = hopper_pci_probe, ++ .remove = hopper_pci_remove, ++}; ++ ++static int __devinit hopper_init(void) ++{ ++ return pci_register_driver(&hopper_pci_driver); ++} ++ ++static void __devexit hopper_exit(void) ++{ ++ return pci_unregister_driver(&hopper_pci_driver); ++} ++ ++module_init(hopper_init); ++module_exit(hopper_exit); ++ ++MODULE_DESCRIPTION("HOPPER driver"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/mantis/hopper_vp3028.c b/drivers/media/pci/mantis/hopper_vp3028.c +new file mode 100644 +index 0000000..68a29f8 +--- /dev/null ++++ b/drivers/media/pci/mantis/hopper_vp3028.c +@@ -0,0 +1,88 @@ ++/* ++ Hopper VP-3028 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "zl10353.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "hopper_vp3028.h" ++ ++struct zl10353_config hopper_vp3028_config = { ++ .demod_address = 0x0f, ++}; ++ ++#define MANTIS_MODEL_NAME "VP-3028" ++#define MANTIS_DEV_TYPE "DVB-T" ++ ++static int vp3028_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ int err = 0; ++ ++ mantis_gpio_set_bits(mantis, config->reset, 0); ++ msleep(100); ++ err = mantis_frontend_power(mantis, POWER_ON); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->reset, 1); ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ msleep(250); ++ dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); ++ fe = dvb_attach(zl10353_attach, &hopper_vp3028_config, adapter); ++ ++ if (!fe) ++ return -1; ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ dprintk(MANTIS_ERROR, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp3028_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_188, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp3028_frontend_init, ++ .power = GPIF_A00, ++ .reset = GPIF_A03, ++}; +diff --git a/drivers/media/pci/mantis/hopper_vp3028.h b/drivers/media/pci/mantis/hopper_vp3028.h +new file mode 100644 +index 0000000..5723949 +--- /dev/null ++++ b/drivers/media/pci/mantis/hopper_vp3028.h +@@ -0,0 +1,30 @@ ++/* ++ Hopper VP-3028 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP3028_H ++#define __MANTIS_VP3028_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_3028_DVB_T 0x0028 ++ ++extern struct mantis_hwconfig vp3028_config; ++ ++#endif /* __MANTIS_VP3028_H */ +diff --git a/drivers/media/pci/mantis/mantis_ca.c b/drivers/media/pci/mantis/mantis_ca.c +new file mode 100644 +index 0000000..60c6c2f +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_ca.c +@@ -0,0 +1,210 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_link.h" ++#include "mantis_hif.h" ++#include "mantis_reg.h" ++ ++#include "mantis_ca.h" ++ ++static int mantis_ca_read_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Read", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return mantis_hif_read_mem(ca, addr); ++} ++ ++static int mantis_ca_write_attr_mem(struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request Attribute Mem Write", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return mantis_hif_write_mem(ca, addr, data); ++} ++ ++static int mantis_ca_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Read", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return mantis_hif_read_iom(ca, addr); ++} ++ ++static int mantis_ca_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Request CAM control Write", slot); ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return mantis_hif_write_iom(ca, addr, data); ++} ++ ++static int mantis_ca_slot_reset(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot RESET", slot); ++ udelay(500); /* Wait.. */ ++ mmwrite(0xda, MANTIS_PCMCIA_RESET); /* Leading edge assert */ ++ udelay(500); ++ mmwrite(0x00, MANTIS_PCMCIA_RESET); /* Trailing edge deassert */ ++ msleep(1000); ++ dvb_ca_en50221_camready_irq(&ca->en50221, 0); ++ ++ return 0; ++} ++ ++static int mantis_ca_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Slot shutdown", slot); ++ ++ return 0; ++} ++ ++static int mantis_ts_control(struct dvb_ca_en50221 *en50221, int slot) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): TS control", slot); ++/* mantis_set_direction(mantis, 1); */ /* Enable TS through CAM */ ++ ++ return 0; ++} ++ ++static int mantis_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) ++{ ++ struct mantis_ca *ca = en50221->data; ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Slot(%d): Poll Slot status", slot); ++ ++ if (ca->slot_state == MODULE_INSERTED) { ++ dprintk(MANTIS_DEBUG, 1, "CA Module present and ready"); ++ return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; ++ } else { ++ dprintk(MANTIS_DEBUG, 1, "CA Module not present or not ready"); ++ } ++ ++ return 0; ++} ++ ++int mantis_ca_init(struct mantis_pci *mantis) ++{ ++ struct dvb_adapter *dvb_adapter = &mantis->dvb_adapter; ++ struct mantis_ca *ca; ++ int ca_flags = 0, result; ++ ++ dprintk(MANTIS_DEBUG, 1, "Initializing Mantis CA"); ++ ca = kzalloc(sizeof(struct mantis_ca), GFP_KERNEL); ++ if (!ca) { ++ dprintk(MANTIS_ERROR, 1, "Out of memory!, exiting .."); ++ result = -ENOMEM; ++ goto err; ++ } ++ ++ ca->ca_priv = mantis; ++ mantis->mantis_ca = ca; ++ ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE; ++ /* register CA interface */ ++ ca->en50221.owner = THIS_MODULE; ++ ca->en50221.read_attribute_mem = mantis_ca_read_attr_mem; ++ ca->en50221.write_attribute_mem = mantis_ca_write_attr_mem; ++ ca->en50221.read_cam_control = mantis_ca_read_cam_ctl; ++ ca->en50221.write_cam_control = mantis_ca_write_cam_ctl; ++ ca->en50221.slot_reset = mantis_ca_slot_reset; ++ ca->en50221.slot_shutdown = mantis_ca_slot_shutdown; ++ ca->en50221.slot_ts_enable = mantis_ts_control; ++ ca->en50221.poll_slot_status = mantis_slot_status; ++ ca->en50221.data = ca; ++ ++ mutex_init(&ca->ca_lock); ++ ++ init_waitqueue_head(&ca->hif_data_wq); ++ init_waitqueue_head(&ca->hif_opdone_wq); ++ init_waitqueue_head(&ca->hif_write_wq); ++ ++ dprintk(MANTIS_ERROR, 1, "Registering EN50221 device"); ++ result = dvb_ca_en50221_init(dvb_adapter, &ca->en50221, ca_flags, 1); ++ if (result != 0) { ++ dprintk(MANTIS_ERROR, 1, "EN50221: Initialization failed <%d>", result); ++ goto err; ++ } ++ dprintk(MANTIS_ERROR, 1, "Registered EN50221 device"); ++ mantis_evmgr_init(ca); ++ return 0; ++err: ++ kfree(ca); ++ return result; ++} ++EXPORT_SYMBOL_GPL(mantis_ca_init); ++ ++void mantis_ca_exit(struct mantis_pci *mantis) ++{ ++ struct mantis_ca *ca = mantis->mantis_ca; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis CA exit"); ++ if (!ca) ++ return; ++ ++ mantis_evmgr_exit(ca); ++ dprintk(MANTIS_ERROR, 1, "Unregistering EN50221 device"); ++ dvb_ca_en50221_release(&ca->en50221); ++ ++ kfree(ca); ++} ++EXPORT_SYMBOL_GPL(mantis_ca_exit); +diff --git a/drivers/media/pci/mantis/mantis_ca.h b/drivers/media/pci/mantis/mantis_ca.h +new file mode 100644 +index 0000000..dc63e55 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_ca.h +@@ -0,0 +1,27 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_CA_H ++#define __MANTIS_CA_H ++ ++extern int mantis_ca_init(struct mantis_pci *mantis); ++extern void mantis_ca_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_CA_H */ +diff --git a/drivers/media/pci/mantis/mantis_cards.c b/drivers/media/pci/mantis/mantis_cards.c +new file mode 100644 +index 0000000..0207d1f +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_cards.c +@@ -0,0 +1,307 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++ ++#include "mantis_vp1033.h" ++#include "mantis_vp1034.h" ++#include "mantis_vp1041.h" ++#include "mantis_vp2033.h" ++#include "mantis_vp2040.h" ++#include "mantis_vp3030.h" ++ ++#include "mantis_dma.h" ++#include "mantis_ca.h" ++#include "mantis_dvb.h" ++#include "mantis_uart.h" ++#include "mantis_ioc.h" ++#include "mantis_pci.h" ++#include "mantis_i2c.h" ++#include "mantis_reg.h" ++ ++static unsigned int verbose; ++module_param(verbose, int, 0644); ++MODULE_PARM_DESC(verbose, "verbose startup messages, default is 0 (no)"); ++ ++static int devs; ++ ++#define DRIVER_NAME "Mantis" ++ ++static char *label[10] = { ++ "DMA", ++ "IRQ-0", ++ "IRQ-1", ++ "OCERR", ++ "PABRT", ++ "RIPRR", ++ "PPERR", ++ "FTRGT", ++ "RISCI", ++ "RACK" ++}; ++ ++static irqreturn_t mantis_irq_handler(int irq, void *dev_id) ++{ ++ u32 stat = 0, mask = 0; ++ u32 rst_stat = 0, rst_mask = 0; ++ ++ struct mantis_pci *mantis; ++ struct mantis_ca *ca; ++ ++ mantis = (struct mantis_pci *) dev_id; ++ if (unlikely(mantis == NULL)) { ++ dprintk(MANTIS_ERROR, 1, "Mantis == NULL"); ++ return IRQ_NONE; ++ } ++ ca = mantis->mantis_ca; ++ ++ stat = mmread(MANTIS_INT_STAT); ++ mask = mmread(MANTIS_INT_MASK); ++ if (!(stat & mask)) ++ return IRQ_NONE; ++ ++ rst_mask = MANTIS_GPIF_WRACK | ++ MANTIS_GPIF_OTHERR | ++ MANTIS_SBUF_WSTO | ++ MANTIS_GPIF_EXTIRQ; ++ ++ rst_stat = mmread(MANTIS_GPIF_STATUS); ++ rst_stat &= rst_mask; ++ mmwrite(rst_stat, MANTIS_GPIF_STATUS); ++ ++ mantis->mantis_int_stat = stat; ++ mantis->mantis_int_mask = mask; ++ dprintk(MANTIS_DEBUG, 0, "\n-- Stat=<%02x> Mask=<%02x> --", stat, mask); ++ if (stat & MANTIS_INT_RISCEN) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[0]); ++ } ++ if (stat & MANTIS_INT_IRQ0) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[1]); ++ mantis->gpif_status = rst_stat; ++ wake_up(&ca->hif_write_wq); ++ schedule_work(&ca->hif_evm_work); ++ } ++ if (stat & MANTIS_INT_IRQ1) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[2]); ++ schedule_work(&mantis->uart_work); ++ } ++ if (stat & MANTIS_INT_OCERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[3]); ++ } ++ if (stat & MANTIS_INT_PABORT) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[4]); ++ } ++ if (stat & MANTIS_INT_RIPERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[5]); ++ } ++ if (stat & MANTIS_INT_PPERR) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[6]); ++ } ++ if (stat & MANTIS_INT_FTRGT) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[7]); ++ } ++ if (stat & MANTIS_INT_RISCI) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[8]); ++ mantis->busy_block = (stat & MANTIS_INT_RISCSTAT) >> 28; ++ tasklet_schedule(&mantis->tasklet); ++ } ++ if (stat & MANTIS_INT_I2CDONE) { ++ dprintk(MANTIS_DEBUG, 0, "<%s>", label[9]); ++ wake_up(&mantis->i2c_wq); ++ } ++ mmwrite(stat, MANTIS_INT_STAT); ++ stat &= ~(MANTIS_INT_RISCEN | MANTIS_INT_I2CDONE | ++ MANTIS_INT_I2CRACK | MANTIS_INT_PCMCIA7 | ++ MANTIS_INT_PCMCIA6 | MANTIS_INT_PCMCIA5 | ++ MANTIS_INT_PCMCIA4 | MANTIS_INT_PCMCIA3 | ++ MANTIS_INT_PCMCIA2 | MANTIS_INT_PCMCIA1 | ++ MANTIS_INT_PCMCIA0 | MANTIS_INT_IRQ1 | ++ MANTIS_INT_IRQ0 | MANTIS_INT_OCERR | ++ MANTIS_INT_PABORT | MANTIS_INT_RIPERR | ++ MANTIS_INT_PPERR | MANTIS_INT_FTRGT | ++ MANTIS_INT_RISCI); ++ ++ if (stat) ++ dprintk(MANTIS_DEBUG, 0, " Stat=<%02x> Mask=<%02x>", stat, mask); ++ ++ dprintk(MANTIS_DEBUG, 0, "\n"); ++ return IRQ_HANDLED; ++} ++ ++static int __devinit mantis_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) ++{ ++ struct mantis_pci *mantis; ++ struct mantis_hwconfig *config; ++ int err = 0; ++ ++ mantis = kzalloc(sizeof(struct mantis_pci), GFP_KERNEL); ++ if (mantis == NULL) { ++ printk(KERN_ERR "%s ERROR: Out of memory\n", __func__); ++ err = -ENOMEM; ++ goto fail0; ++ } ++ ++ mantis->num = devs; ++ mantis->verbose = verbose; ++ mantis->pdev = pdev; ++ config = (struct mantis_hwconfig *) pci_id->driver_data; ++ config->irq_handler = &mantis_irq_handler; ++ mantis->hwconfig = config; ++ ++ err = mantis_pci_init(mantis); ++ if (err) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI initialization failed <%d>", err); ++ goto fail1; ++ } ++ ++ err = mantis_stream_control(mantis, STREAM_TO_HIF); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis stream control failed <%d>", err); ++ goto fail1; ++ } ++ ++ err = mantis_i2c_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C initialization failed <%d>", err); ++ goto fail2; ++ } ++ ++ err = mantis_get_mac(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis MAC address read failed <%d>", err); ++ goto fail2; ++ } ++ ++ err = mantis_dma_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA initialization failed <%d>", err); ++ goto fail3; ++ } ++ ++ err = mantis_dvb_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DVB initialization failed <%d>", err); ++ goto fail4; ++ } ++ err = mantis_uart_init(mantis); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART initialization failed <%d>", err); ++ goto fail6; ++ } ++ ++ devs++; ++ ++ return err; ++ ++ ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis UART exit! <%d>", err); ++ mantis_uart_exit(mantis); ++ ++fail6: ++fail4: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis DMA exit! <%d>", err); ++ mantis_dma_exit(mantis); ++ ++fail3: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis I2C exit! <%d>", err); ++ mantis_i2c_exit(mantis); ++ ++fail2: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis PCI exit! <%d>", err); ++ mantis_pci_exit(mantis); ++ ++fail1: ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis free! <%d>", err); ++ kfree(mantis); ++ ++fail0: ++ return err; ++} ++ ++static void __devexit mantis_pci_remove(struct pci_dev *pdev) ++{ ++ struct mantis_pci *mantis = pci_get_drvdata(pdev); ++ ++ if (mantis) { ++ ++ mantis_uart_exit(mantis); ++ mantis_dvb_exit(mantis); ++ mantis_dma_exit(mantis); ++ mantis_i2c_exit(mantis); ++ mantis_pci_exit(mantis); ++ kfree(mantis); ++ } ++ return; ++} ++ ++static struct pci_device_id mantis_pci_table[] = { ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1033_DVB_S, &vp1033_config), ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1034_DVB_S, &vp1034_config), ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_1041_DVB_S2, &vp1041_config), ++ MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_10, &vp1041_config), ++ MAKE_ENTRY(TECHNISAT, SKYSTAR_HD2_20, &vp1041_config), ++ MAKE_ENTRY(TERRATEC, CINERGY_S2_PCI_HD, &vp1041_config), ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2033_DVB_C, &vp2033_config), ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_2040_DVB_C, &vp2040_config), ++ MAKE_ENTRY(TECHNISAT, CABLESTAR_HD2, &vp2040_config), ++ MAKE_ENTRY(TERRATEC, CINERGY_C, &vp2040_config), ++ MAKE_ENTRY(TWINHAN_TECHNOLOGIES, MANTIS_VP_3030_DVB_T, &vp3030_config), ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(pci, mantis_pci_table); ++ ++static struct pci_driver mantis_pci_driver = { ++ .name = DRIVER_NAME, ++ .id_table = mantis_pci_table, ++ .probe = mantis_pci_probe, ++ .remove = mantis_pci_remove, ++}; ++ ++static int __devinit mantis_init(void) ++{ ++ return pci_register_driver(&mantis_pci_driver); ++} ++ ++static void __devexit mantis_exit(void) ++{ ++ return pci_unregister_driver(&mantis_pci_driver); ++} ++ ++module_init(mantis_init); ++module_exit(mantis_exit); ++ ++MODULE_DESCRIPTION("MANTIS driver"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h +new file mode 100644 +index 0000000..f2410cf +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_common.h +@@ -0,0 +1,179 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_COMMON_H ++#define __MANTIS_COMMON_H ++ ++#include ++#include ++#include ++ ++#include "mantis_uart.h" ++ ++#include "mantis_link.h" ++ ++#define MANTIS_ERROR 0 ++#define MANTIS_NOTICE 1 ++#define MANTIS_INFO 2 ++#define MANTIS_DEBUG 3 ++#define MANTIS_TMG 9 ++ ++#define dprintk(y, z, format, arg...) do { \ ++ if (z) { \ ++ if ((mantis->verbose > MANTIS_ERROR) && (mantis->verbose > y)) \ ++ printk(KERN_ERR "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ ++ else if ((mantis->verbose > MANTIS_NOTICE) && (mantis->verbose > y)) \ ++ printk(KERN_NOTICE "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ ++ else if ((mantis->verbose > MANTIS_INFO) && (mantis->verbose > y)) \ ++ printk(KERN_INFO "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ ++ else if ((mantis->verbose > MANTIS_DEBUG) && (mantis->verbose > y)) \ ++ printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ ++ else if ((mantis->verbose > MANTIS_TMG) && (mantis->verbose > y)) \ ++ printk(KERN_DEBUG "%s (%d): " format "\n" , __func__ , mantis->num , ##arg); \ ++ } else { \ ++ if (mantis->verbose > y) \ ++ printk(format , ##arg); \ ++ } \ ++} while(0) ++ ++#define mwrite(dat, addr) writel((dat), addr) ++#define mread(addr) readl(addr) ++ ++#define mmwrite(dat, addr) mwrite((dat), (mantis->mmio + (addr))) ++#define mmread(addr) mread(mantis->mmio + (addr)) ++ ++#define MANTIS_TS_188 0 ++#define MANTIS_TS_204 1 ++ ++#define TWINHAN_TECHNOLOGIES 0x1822 ++#define MANTIS 0x4e35 ++ ++#define TECHNISAT 0x1ae4 ++#define TERRATEC 0x153b ++ ++#define MAKE_ENTRY(__subven, __subdev, __configptr) { \ ++ .vendor = TWINHAN_TECHNOLOGIES, \ ++ .device = MANTIS, \ ++ .subvendor = (__subven), \ ++ .subdevice = (__subdev), \ ++ .driver_data = (unsigned long) (__configptr) \ ++} ++ ++enum mantis_i2c_mode { ++ MANTIS_PAGE_MODE = 0, ++ MANTIS_BYTE_MODE, ++}; ++ ++struct mantis_pci; ++ ++struct mantis_hwconfig { ++ char *model_name; ++ char *dev_type; ++ u32 ts_size; ++ ++ enum mantis_baud baud_rate; ++ enum mantis_parity parity; ++ u32 bytes; ++ ++ irqreturn_t (*irq_handler)(int irq, void *dev_id); ++ int (*frontend_init)(struct mantis_pci *mantis, struct dvb_frontend *fe); ++ ++ u8 power; ++ u8 reset; ++ ++ enum mantis_i2c_mode i2c_mode; ++}; ++ ++struct mantis_pci { ++ unsigned int verbose; ++ ++ /* PCI stuff */ ++ u16 vendor_id; ++ u16 device_id; ++ u16 subsystem_vendor; ++ u16 subsystem_device; ++ ++ u8 latency; ++ ++ struct pci_dev *pdev; ++ ++ unsigned long mantis_addr; ++ void __iomem *mmio; ++ ++ u8 irq; ++ u8 revision; ++ ++ unsigned int num; ++ ++ /* RISC Core */ ++ u32 busy_block; ++ u32 last_block; ++ u8 *buf_cpu; ++ dma_addr_t buf_dma; ++ u32 *risc_cpu; ++ dma_addr_t risc_dma; ++ ++ struct tasklet_struct tasklet; ++ ++ struct i2c_adapter adapter; ++ int i2c_rc; ++ wait_queue_head_t i2c_wq; ++ struct mutex i2c_lock; ++ ++ /* DVB stuff */ ++ struct dvb_adapter dvb_adapter; ++ struct dvb_frontend *fe; ++ struct dvb_demux demux; ++ struct dmxdev dmxdev; ++ struct dmx_frontend fe_hw; ++ struct dmx_frontend fe_mem; ++ struct dvb_net dvbnet; ++ ++ u8 feeds; ++ ++ struct mantis_hwconfig *hwconfig; ++ ++ u32 mantis_int_stat; ++ u32 mantis_int_mask; ++ ++ /* board specific */ ++ u8 mac_address[8]; ++ u32 sub_vendor_id; ++ u32 sub_device_id; ++ ++ /* A12 A13 A14 */ ++ u32 gpio_status; ++ ++ u32 gpif_status; ++ ++ struct mantis_ca *mantis_ca; ++ ++ wait_queue_head_t uart_wq; ++ struct work_struct uart_work; ++ spinlock_t uart_lock; ++ ++ struct rc_dev *rc; ++ char input_name[80]; ++ char input_phys[80]; ++}; ++ ++#define MANTIS_HIF_STATUS (mantis->gpio_status) ++ ++#endif /* __MANTIS_COMMON_H */ +diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c +new file mode 100644 +index 0000000..684d906 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_core.c +@@ -0,0 +1,235 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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 "mantis_common.h" ++#include "mantis_core.h" ++#include "mantis_vp1033.h" ++#include "mantis_vp1034.h" ++#include "mantis_vp1041.h" ++#include "mantis_vp2033.h" ++#include "mantis_vp2040.h" ++#include "mantis_vp3030.h" ++ ++static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) ++{ ++ int err; ++ struct i2c_msg msg[] = { ++ { ++ .addr = 0x50, ++ .flags = 0, ++ .buf = data, ++ .len = 1 ++ }, { ++ .addr = 0x50, ++ .flags = I2C_M_RD, ++ .buf = data, ++ .len = length ++ }, ++ }; ++ ++ err = i2c_transfer(&mantis->adapter, msg, 2); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, ++ "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", ++ err, data[0], data[1]); ++ ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) ++{ ++ int err; ++ ++ struct i2c_msg msg = { ++ .addr = 0x50, ++ .flags = 0, ++ .buf = data, ++ .len = length ++ }; ++ ++ err = i2c_transfer(&mantis->adapter, &msg, 1); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, ++ "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", ++ err, length, data[0], data[1]); ++ ++ return err; ++ } ++ ++ return 0; ++} ++ ++static int get_mac_address(struct mantis_pci *mantis) ++{ ++ int err; ++ ++ mantis->mac_address[0] = 0x08; ++ err = read_eeprom_byte(mantis, &mantis->mac_address[0], 6); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, "Mantis EEPROM read error"); ++ ++ return err; ++ } ++ dprintk(verbose, MANTIS_ERROR, 0, ++ " MAC Address=[%pM]\n", mantis->mac_address); ++ ++ return 0; ++} ++ ++#define MANTIS_MODEL_UNKNOWN "UNKNOWN" ++#define MANTIS_DEV_UNKNOWN "UNKNOWN" ++ ++struct mantis_hwconfig unknown_device = { ++ .model_name = MANTIS_MODEL_UNKNOWN, ++ .dev_type = MANTIS_DEV_UNKNOWN, ++}; ++ ++static void mantis_load_config(struct mantis_pci *mantis) ++{ ++ switch (mantis->subsystem_device) { ++ case MANTIS_VP_1033_DVB_S: /* VP-1033 */ ++ mantis->hwconfig = &vp1033_mantis_config; ++ break; ++ case MANTIS_VP_1034_DVB_S: /* VP-1034 */ ++ mantis->hwconfig = &vp1034_mantis_config; ++ break; ++ case MANTIS_VP_1041_DVB_S2: /* VP-1041 */ ++ case TECHNISAT_SKYSTAR_HD2: ++ mantis->hwconfig = &vp1041_mantis_config; ++ break; ++ case MANTIS_VP_2033_DVB_C: /* VP-2033 */ ++ mantis->hwconfig = &vp2033_mantis_config; ++ break; ++ case MANTIS_VP_2040_DVB_C: /* VP-2040 */ ++ case CINERGY_C: /* VP-2040 clone */ ++ case TECHNISAT_CABLESTAR_HD2: ++ mantis->hwconfig = &vp2040_mantis_config; ++ break; ++ case MANTIS_VP_3030_DVB_T: /* VP-3030 */ ++ mantis->hwconfig = &vp3030_mantis_config; ++ break; ++ default: ++ mantis->hwconfig = &unknown_device; ++ break; ++ } ++} ++ ++int mantis_core_init(struct mantis_pci *mantis) ++{ ++ int err = 0; ++ ++ mantis_load_config(mantis); ++ dprintk(verbose, MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", ++ mantis->hwconfig->model_name, mantis->hwconfig->dev_type, ++ mantis->pdev->bus->number, PCI_SLOT(mantis->pdev->devfn), PCI_FUNC(mantis->pdev->devfn)); ++ dprintk(verbose, MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", ++ mantis->revision, ++ mantis->subsystem_vendor, mantis->subsystem_device); ++ dprintk(verbose, MANTIS_ERROR, 0, ++ "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", ++ mantis->pdev->irq, mantis->latency, ++ mantis->mantis_addr, mantis->mantis_mmio); ++ ++ err = mantis_i2c_init(mantis); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, "Mantis I2C init failed"); ++ return err; ++ } ++ err = get_mac_address(mantis); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, "get MAC address failed"); ++ return err; ++ } ++ err = mantis_dma_init(mantis); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_ERROR, 1, "Mantis DMA init failed"); ++ return err; ++ } ++ err = mantis_dvb_init(mantis); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_DEBUG, 1, "Mantis DVB init failed"); ++ return err; ++ } ++ err = mantis_uart_init(mantis); ++ if (err < 0) { ++ dprintk(verbose, MANTIS_DEBUG, 1, "Mantis UART init failed"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++int mantis_core_exit(struct mantis_pci *mantis) ++{ ++ mantis_dma_stop(mantis); ++ dprintk(verbose, MANTIS_ERROR, 1, "DMA engine stopping"); ++ ++ mantis_uart_exit(mantis); ++ dprintk(verbose, MANTIS_ERROR, 1, "UART exit failed"); ++ ++ if (mantis_dma_exit(mantis) < 0) ++ dprintk(verbose, MANTIS_ERROR, 1, "DMA exit failed"); ++ if (mantis_dvb_exit(mantis) < 0) ++ dprintk(verbose, MANTIS_ERROR, 1, "DVB exit failed"); ++ if (mantis_i2c_exit(mantis) < 0) ++ dprintk(verbose, MANTIS_ERROR, 1, "I2C adapter delete.. failed"); ++ ++ return 0; ++} ++ ++/* Turn the given bit on or off. */ ++void gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) ++{ ++ u32 cur; ++ ++ cur = mmread(MANTIS_GPIF_ADDR); ++ if (value) ++ mantis->gpio_status = cur | (1 << bitpos); ++ else ++ mantis->gpio_status = cur & (~(1 << bitpos)); ++ ++ mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); ++ mmwrite(0x00, MANTIS_GPIF_DOUT); ++ udelay(100); ++} ++ ++/* direction = 0 , no CI passthrough ; 1 , CI passthrough */ ++void mantis_set_direction(struct mantis_pci *mantis, int direction) ++{ ++ u32 reg; ++ ++ reg = mmread(0x28); ++ dprintk(verbose, MANTIS_DEBUG, 1, "TS direction setup"); ++ if (direction == 0x01) { ++ /* to CI */ ++ reg |= 0x04; ++ mmwrite(reg, 0x28); ++ reg &= 0xff - 0x04; ++ mmwrite(reg, 0x28); ++ } else { ++ reg &= 0xff - 0x04; ++ mmwrite(reg, 0x28); ++ reg |= 0x04; ++ mmwrite(reg, 0x28); ++ } ++} +diff --git a/drivers/media/pci/mantis/mantis_core.h b/drivers/media/pci/mantis/mantis_core.h +new file mode 100644 +index 0000000..833ee42 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_core.h +@@ -0,0 +1,57 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_CORE_H ++#define __MANTIS_CORE_H ++ ++#include "mantis_common.h" ++ ++ ++#define FE_TYPE_SAT 0 ++#define FE_TYPE_CAB 1 ++#define FE_TYPE_TER 2 ++ ++#define FE_TYPE_TS204 0 ++#define FE_TYPE_TS188 1 ++ ++ ++struct vendorname { ++ u8 *sub_vendor_name; ++ u32 sub_vendor_id; ++}; ++ ++struct devicetype { ++ u8 *sub_device_name; ++ u32 sub_device_id; ++ u8 device_type; ++ u32 type_flags; ++}; ++ ++ ++extern int mantis_dma_init(struct mantis_pci *mantis); ++extern int mantis_dma_exit(struct mantis_pci *mantis); ++extern void mantis_dma_start(struct mantis_pci *mantis); ++extern void mantis_dma_stop(struct mantis_pci *mantis); ++extern int mantis_i2c_init(struct mantis_pci *mantis); ++extern int mantis_i2c_exit(struct mantis_pci *mantis); ++extern int mantis_core_init(struct mantis_pci *mantis); ++extern int mantis_core_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_CORE_H */ +diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c +new file mode 100644 +index 0000000..566c407 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_dma.c +@@ -0,0 +1,230 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_dma.h" ++ ++#define RISC_WRITE (0x01 << 28) ++#define RISC_JUMP (0x07 << 28) ++#define RISC_IRQ (0x01 << 24) ++ ++#define RISC_STATUS(status) ((((~status) & 0x0f) << 20) | ((status & 0x0f) << 16)) ++#define RISC_FLUSH(risc_pos) (risc_pos = 0) ++#define RISC_INSTR(risc_pos, opcode) (mantis->risc_cpu[risc_pos++] = cpu_to_le32(opcode)) ++ ++#define MANTIS_BUF_SIZE (64 * 1024) ++#define MANTIS_BLOCK_BYTES (MANTIS_BUF_SIZE / 4) ++#define MANTIS_DMA_TR_BYTES (2 * 1024) /* upper limit: 4095 bytes. */ ++#define MANTIS_BLOCK_COUNT (MANTIS_BUF_SIZE / MANTIS_BLOCK_BYTES) ++ ++#define MANTIS_DMA_TR_UNITS (MANTIS_BLOCK_BYTES / MANTIS_DMA_TR_BYTES) ++/* MANTIS_BUF_SIZE / MANTIS_DMA_TR_UNITS must not exceed MANTIS_RISC_SIZE (4k RISC cmd buffer) */ ++#define MANTIS_RISC_SIZE PAGE_SIZE /* RISC program must fit here. */ ++ ++int mantis_dma_exit(struct mantis_pci *mantis) ++{ ++ if (mantis->buf_cpu) { ++ dprintk(MANTIS_ERROR, 1, ++ "DMA=0x%lx cpu=0x%p size=%d", ++ (unsigned long) mantis->buf_dma, ++ mantis->buf_cpu, ++ MANTIS_BUF_SIZE); ++ ++ pci_free_consistent(mantis->pdev, MANTIS_BUF_SIZE, ++ mantis->buf_cpu, mantis->buf_dma); ++ ++ mantis->buf_cpu = NULL; ++ } ++ if (mantis->risc_cpu) { ++ dprintk(MANTIS_ERROR, 1, ++ "RISC=0x%lx cpu=0x%p size=%lx", ++ (unsigned long) mantis->risc_dma, ++ mantis->risc_cpu, ++ MANTIS_RISC_SIZE); ++ ++ pci_free_consistent(mantis->pdev, MANTIS_RISC_SIZE, ++ mantis->risc_cpu, mantis->risc_dma); ++ ++ mantis->risc_cpu = NULL; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_dma_exit); ++ ++static inline int mantis_alloc_buffers(struct mantis_pci *mantis) ++{ ++ if (!mantis->buf_cpu) { ++ mantis->buf_cpu = pci_alloc_consistent(mantis->pdev, ++ MANTIS_BUF_SIZE, ++ &mantis->buf_dma); ++ if (!mantis->buf_cpu) { ++ dprintk(MANTIS_ERROR, 1, ++ "DMA buffer allocation failed"); ++ ++ goto err; ++ } ++ dprintk(MANTIS_ERROR, 1, ++ "DMA=0x%lx cpu=0x%p size=%d", ++ (unsigned long) mantis->buf_dma, ++ mantis->buf_cpu, MANTIS_BUF_SIZE); ++ } ++ if (!mantis->risc_cpu) { ++ mantis->risc_cpu = pci_alloc_consistent(mantis->pdev, ++ MANTIS_RISC_SIZE, ++ &mantis->risc_dma); ++ ++ if (!mantis->risc_cpu) { ++ dprintk(MANTIS_ERROR, 1, ++ "RISC program allocation failed"); ++ ++ mantis_dma_exit(mantis); ++ ++ goto err; ++ } ++ dprintk(MANTIS_ERROR, 1, ++ "RISC=0x%lx cpu=0x%p size=%lx", ++ (unsigned long) mantis->risc_dma, ++ mantis->risc_cpu, MANTIS_RISC_SIZE); ++ } ++ ++ return 0; ++err: ++ dprintk(MANTIS_ERROR, 1, "Out of memory (?) ....."); ++ return -ENOMEM; ++} ++ ++int mantis_dma_init(struct mantis_pci *mantis) ++{ ++ int err = 0; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); ++ if (mantis_alloc_buffers(mantis) < 0) { ++ dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); ++ ++ /* Stop RISC Engine */ ++ mmwrite(0, MANTIS_DMA_CTL); ++ ++ goto err; ++ } ++ ++ return 0; ++err: ++ return err; ++} ++EXPORT_SYMBOL_GPL(mantis_dma_init); ++ ++static inline void mantis_risc_program(struct mantis_pci *mantis) ++{ ++ u32 buf_pos = 0; ++ u32 line, step; ++ u32 risc_pos; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis create RISC program"); ++ RISC_FLUSH(risc_pos); ++ ++ dprintk(MANTIS_DEBUG, 1, "risc len lines %u, bytes per line %u, bytes per DMA tr %u", ++ MANTIS_BLOCK_COUNT, MANTIS_BLOCK_BYTES, MANTIS_DMA_TR_BYTES); ++ ++ for (line = 0; line < MANTIS_BLOCK_COUNT; line++) { ++ for (step = 0; step < MANTIS_DMA_TR_UNITS; step++) { ++ dprintk(MANTIS_DEBUG, 1, "RISC PROG line=[%d], step=[%d]", line, step); ++ if (step == 0) { ++ RISC_INSTR(risc_pos, RISC_WRITE | ++ RISC_IRQ | ++ RISC_STATUS(line) | ++ MANTIS_DMA_TR_BYTES); ++ } else { ++ RISC_INSTR(risc_pos, RISC_WRITE | MANTIS_DMA_TR_BYTES); ++ } ++ RISC_INSTR(risc_pos, mantis->buf_dma + buf_pos); ++ buf_pos += MANTIS_DMA_TR_BYTES; ++ } ++ } ++ RISC_INSTR(risc_pos, RISC_JUMP); ++ RISC_INSTR(risc_pos, mantis->risc_dma); ++} ++ ++void mantis_dma_start(struct mantis_pci *mantis) ++{ ++ dprintk(MANTIS_DEBUG, 1, "Mantis Start DMA engine"); ++ ++ mantis_risc_program(mantis); ++ mmwrite(mantis->risc_dma, MANTIS_RISC_START); ++ mmwrite(mmread(MANTIS_GPIF_ADDR) | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); ++ ++ mmwrite(0, MANTIS_DMA_CTL); ++ mantis->last_block = mantis->busy_block = 0; ++ ++ mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_RISCI, MANTIS_INT_MASK); ++ ++ mmwrite(MANTIS_FIFO_EN | MANTIS_DCAP_EN ++ | MANTIS_RISC_EN, MANTIS_DMA_CTL); ++ ++} ++ ++void mantis_dma_stop(struct mantis_pci *mantis) ++{ ++ dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); ++ ++ mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); ++ ++ mmwrite((mmread(MANTIS_DMA_CTL) & ~(MANTIS_FIFO_EN | ++ MANTIS_DCAP_EN | ++ MANTIS_RISC_EN)), MANTIS_DMA_CTL); ++ ++ mmwrite(mmread(MANTIS_INT_STAT), MANTIS_INT_STAT); ++ ++ mmwrite(mmread(MANTIS_INT_MASK) & ~(MANTIS_INT_RISCI | ++ MANTIS_INT_RISCEN), MANTIS_INT_MASK); ++} ++ ++ ++void mantis_dma_xfer(unsigned long data) ++{ ++ struct mantis_pci *mantis = (struct mantis_pci *) data; ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ ++ while (mantis->last_block != mantis->busy_block) { ++ dprintk(MANTIS_DEBUG, 1, "last block=[%d] finished block=[%d]", ++ mantis->last_block, mantis->busy_block); ++ ++ (config->ts_size ? dvb_dmx_swfilter_204 : dvb_dmx_swfilter) ++ (&mantis->demux, &mantis->buf_cpu[mantis->last_block * MANTIS_BLOCK_BYTES], MANTIS_BLOCK_BYTES); ++ mantis->last_block = (mantis->last_block + 1) % MANTIS_BLOCK_COUNT; ++ } ++} +diff --git a/drivers/media/pci/mantis/mantis_dma.h b/drivers/media/pci/mantis/mantis_dma.h +new file mode 100644 +index 0000000..6be00fa +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_dma.h +@@ -0,0 +1,30 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_DMA_H ++#define __MANTIS_DMA_H ++ ++extern int mantis_dma_init(struct mantis_pci *mantis); ++extern int mantis_dma_exit(struct mantis_pci *mantis); ++extern void mantis_dma_start(struct mantis_pci *mantis); ++extern void mantis_dma_stop(struct mantis_pci *mantis); ++extern void mantis_dma_xfer(unsigned long data); ++ ++#endif /* __MANTIS_DMA_H */ +diff --git a/drivers/media/pci/mantis/mantis_dvb.c b/drivers/media/pci/mantis/mantis_dvb.c +new file mode 100644 +index 0000000..5d15c6b +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_dvb.c +@@ -0,0 +1,301 @@ ++/* ++ Mantis PCI bridge driver ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_dma.h" ++#include "mantis_ca.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power) ++{ ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ ++ switch (power) { ++ case POWER_ON: ++ dprintk(MANTIS_DEBUG, 1, "Power ON"); ++ mantis_gpio_set_bits(mantis, config->power, POWER_ON); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->power, POWER_ON); ++ msleep(100); ++ break; ++ ++ case POWER_OFF: ++ dprintk(MANTIS_DEBUG, 1, "Power OFF"); ++ mantis_gpio_set_bits(mantis, config->power, POWER_OFF); ++ msleep(100); ++ break; ++ ++ default: ++ dprintk(MANTIS_DEBUG, 1, "Unknown state <%02x>", power); ++ return -1; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_frontend_power); ++ ++void mantis_frontend_soft_reset(struct mantis_pci *mantis) ++{ ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ ++ dprintk(MANTIS_DEBUG, 1, "Frontend RESET"); ++ mantis_gpio_set_bits(mantis, config->reset, 0); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->reset, 0); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->reset, 1); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->reset, 1); ++ msleep(100); ++ ++ return; ++} ++EXPORT_SYMBOL_GPL(mantis_frontend_soft_reset); ++ ++static int mantis_frontend_shutdown(struct mantis_pci *mantis) ++{ ++ int err; ++ ++ mantis_frontend_soft_reset(mantis); ++ err = mantis_frontend_power(mantis, POWER_OFF); ++ if (err != 0) { ++ dprintk(MANTIS_ERROR, 1, "Frontend POWER OFF failed! <%d>", err); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int mantis_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct mantis_pci *mantis = dvbdmx->priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis DVB Start feed"); ++ if (!dvbdmx->dmx.frontend) { ++ dprintk(MANTIS_DEBUG, 1, "no frontend ?"); ++ return -EINVAL; ++ } ++ ++ mantis->feeds++; ++ dprintk(MANTIS_DEBUG, 1, "mantis start feed, feeds=%d", mantis->feeds); ++ ++ if (mantis->feeds == 1) { ++ dprintk(MANTIS_DEBUG, 1, "mantis start feed & dma"); ++ mantis_dma_start(mantis); ++ tasklet_enable(&mantis->tasklet); ++ } ++ ++ return mantis->feeds; ++} ++ ++static int mantis_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct mantis_pci *mantis = dvbdmx->priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis DVB Stop feed"); ++ if (!dvbdmx->dmx.frontend) { ++ dprintk(MANTIS_DEBUG, 1, "no frontend ?"); ++ return -EINVAL; ++ } ++ ++ mantis->feeds--; ++ if (mantis->feeds == 0) { ++ dprintk(MANTIS_DEBUG, 1, "mantis stop feed and dma"); ++ tasklet_disable(&mantis->tasklet); ++ mantis_dma_stop(mantis); ++ } ++ ++ return 0; ++} ++ ++int __devinit mantis_dvb_init(struct mantis_pci *mantis) ++{ ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ int result = -1; ++ ++ dprintk(MANTIS_DEBUG, 1, "dvb_register_adapter"); ++ ++ result = dvb_register_adapter(&mantis->dvb_adapter, ++ "Mantis DVB adapter", ++ THIS_MODULE, ++ &mantis->pdev->dev, ++ adapter_nr); ++ ++ if (result < 0) { ++ ++ dprintk(MANTIS_ERROR, 1, "Error registering adapter"); ++ return -ENODEV; ++ } ++ ++ mantis->dvb_adapter.priv = mantis; ++ mantis->demux.dmx.capabilities = DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING; ++ ++ mantis->demux.priv = mantis; ++ mantis->demux.filternum = 256; ++ mantis->demux.feednum = 256; ++ mantis->demux.start_feed = mantis_dvb_start_feed; ++ mantis->demux.stop_feed = mantis_dvb_stop_feed; ++ mantis->demux.write_to_decoder = NULL; ++ ++ dprintk(MANTIS_DEBUG, 1, "dvb_dmx_init"); ++ result = dvb_dmx_init(&mantis->demux); ++ if (result < 0) { ++ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); ++ ++ goto err0; ++ } ++ ++ mantis->dmxdev.filternum = 256; ++ mantis->dmxdev.demux = &mantis->demux.dmx; ++ mantis->dmxdev.capabilities = 0; ++ dprintk(MANTIS_DEBUG, 1, "dvb_dmxdev_init"); ++ ++ result = dvb_dmxdev_init(&mantis->dmxdev, &mantis->dvb_adapter); ++ if (result < 0) { ++ ++ dprintk(MANTIS_ERROR, 1, "dvb_dmxdev_init failed, ERROR=%d", result); ++ goto err1; ++ } ++ ++ mantis->fe_hw.source = DMX_FRONTEND_0; ++ result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_hw); ++ if (result < 0) { ++ ++ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); ++ goto err2; ++ } ++ ++ mantis->fe_mem.source = DMX_MEMORY_FE; ++ result = mantis->demux.dmx.add_frontend(&mantis->demux.dmx, &mantis->fe_mem); ++ if (result < 0) { ++ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); ++ goto err3; ++ } ++ ++ result = mantis->demux.dmx.connect_frontend(&mantis->demux.dmx, &mantis->fe_hw); ++ if (result < 0) { ++ dprintk(MANTIS_ERROR, 1, "dvb_dmx_init failed, ERROR=%d", result); ++ goto err4; ++ } ++ ++ dvb_net_init(&mantis->dvb_adapter, &mantis->dvbnet, &mantis->demux.dmx); ++ tasklet_init(&mantis->tasklet, mantis_dma_xfer, (unsigned long) mantis); ++ tasklet_disable(&mantis->tasklet); ++ if (mantis->hwconfig) { ++ result = config->frontend_init(mantis, mantis->fe); ++ if (result < 0) { ++ dprintk(MANTIS_ERROR, 1, "!!! NO Frontends found !!!"); ++ goto err5; ++ } else { ++ if (mantis->fe == NULL) { ++ dprintk(MANTIS_ERROR, 1, "FE "); ++ goto err5; ++ } ++ ++ if (dvb_register_frontend(&mantis->dvb_adapter, mantis->fe)) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Frontend registration failed"); ++ ++ if (mantis->fe->ops.release) ++ mantis->fe->ops.release(mantis->fe); ++ ++ mantis->fe = NULL; ++ goto err5; ++ } ++ } ++ } ++ ++ return 0; ++ ++ /* Error conditions .. */ ++err5: ++ tasklet_kill(&mantis->tasklet); ++ dvb_net_release(&mantis->dvbnet); ++ if (mantis->fe) { ++ dvb_unregister_frontend(mantis->fe); ++ dvb_frontend_detach(mantis->fe); ++ } ++err4: ++ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); ++ ++err3: ++ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); ++ ++err2: ++ dvb_dmxdev_release(&mantis->dmxdev); ++ ++err1: ++ dvb_dmx_release(&mantis->demux); ++ ++err0: ++ dvb_unregister_adapter(&mantis->dvb_adapter); ++ ++ return result; ++} ++EXPORT_SYMBOL_GPL(mantis_dvb_init); ++ ++int __devexit mantis_dvb_exit(struct mantis_pci *mantis) ++{ ++ int err; ++ ++ if (mantis->fe) { ++ /* mantis_ca_exit(mantis); */ ++ err = mantis_frontend_shutdown(mantis); ++ if (err != 0) ++ dprintk(MANTIS_ERROR, 1, "Frontend exit while POWER ON! <%d>", err); ++ dvb_unregister_frontend(mantis->fe); ++ dvb_frontend_detach(mantis->fe); ++ } ++ ++ tasklet_kill(&mantis->tasklet); ++ dvb_net_release(&mantis->dvbnet); ++ ++ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_mem); ++ mantis->demux.dmx.remove_frontend(&mantis->demux.dmx, &mantis->fe_hw); ++ ++ dvb_dmxdev_release(&mantis->dmxdev); ++ dvb_dmx_release(&mantis->demux); ++ ++ dprintk(MANTIS_DEBUG, 1, "dvb_unregister_adapter"); ++ dvb_unregister_adapter(&mantis->dvb_adapter); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_dvb_exit); +diff --git a/drivers/media/pci/mantis/mantis_dvb.h b/drivers/media/pci/mantis/mantis_dvb.h +new file mode 100644 +index 0000000..464199d +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_dvb.h +@@ -0,0 +1,35 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_DVB_H ++#define __MANTIS_DVB_H ++ ++enum mantis_power { ++ POWER_OFF = 0, ++ POWER_ON = 1 ++}; ++ ++extern int mantis_frontend_power(struct mantis_pci *mantis, enum mantis_power power); ++extern void mantis_frontend_soft_reset(struct mantis_pci *mantis); ++ ++extern int mantis_dvb_init(struct mantis_pci *mantis); ++extern int mantis_dvb_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_DVB_H */ +diff --git a/drivers/media/pci/mantis/mantis_evm.c b/drivers/media/pci/mantis/mantis_evm.c +new file mode 100644 +index 0000000..909ff54 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_evm.c +@@ -0,0 +1,117 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_link.h" ++#include "mantis_hif.h" ++#include "mantis_reg.h" ++ ++static void mantis_hifevm_work(struct work_struct *work) ++{ ++ struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ u32 gpif_stat; ++ ++ gpif_stat = mmread(MANTIS_GPIF_STATUS); ++ ++ if (gpif_stat & MANTIS_GPIF_DETSTAT) { ++ if (gpif_stat & MANTIS_CARD_PLUGIN) { ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Plugin", mantis->num); ++ mmwrite(0xdada0000, MANTIS_CARD_RESET); ++ mantis_event_cam_plugin(ca); ++ dvb_ca_en50221_camchange_irq(&ca->en50221, ++ 0, ++ DVB_CA_EN50221_CAMCHANGE_INSERTED); ++ } ++ } else { ++ if (gpif_stat & MANTIS_CARD_PLUGOUT) { ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): CAM Unplug", mantis->num); ++ mmwrite(0xdada0000, MANTIS_CARD_RESET); ++ mantis_event_cam_unplug(ca); ++ dvb_ca_en50221_camchange_irq(&ca->en50221, ++ 0, ++ DVB_CA_EN50221_CAMCHANGE_REMOVED); ++ } ++ } ++ ++ if (mantis->gpif_status & MANTIS_GPIF_EXTIRQ) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Ext IRQ", mantis->num); ++ ++ if (mantis->gpif_status & MANTIS_SBUF_WSTO) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Timeout", mantis->num); ++ ++ if (mantis->gpif_status & MANTIS_GPIF_OTHERR) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Alignment Error", mantis->num); ++ ++ if (gpif_stat & MANTIS_SBUF_OVFLW) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Overflow", mantis->num); ++ ++ if (gpif_stat & MANTIS_GPIF_BRRDY) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Read Ready", mantis->num); ++ ++ if (gpif_stat & MANTIS_GPIF_INTSTAT) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): GPIF IRQ", mantis->num); ++ ++ if (gpif_stat & MANTIS_SBUF_EMPTY) ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer Empty", mantis->num); ++ ++ if (gpif_stat & MANTIS_SBUF_OPDONE) { ++ dprintk(MANTIS_DEBUG, 1, "Event Mgr: Adapter(%d) Slot(0): Smart Buffer operation complete", mantis->num); ++ ca->sbuf_status = MANTIS_SBUF_DATA_AVAIL; ++ ca->hif_event = MANTIS_SBUF_OPDONE; ++ wake_up(&ca->hif_opdone_wq); ++ } ++} ++ ++int mantis_evmgr_init(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Initializing Mantis Host I/F Event manager"); ++ INIT_WORK(&ca->hif_evm_work, mantis_hifevm_work); ++ mantis_pcmcia_init(ca); ++ schedule_work(&ca->hif_evm_work); ++ mantis_hif_init(ca); ++ return 0; ++} ++ ++void mantis_evmgr_exit(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ dprintk(MANTIS_DEBUG, 1, "Mantis Host I/F Event manager exiting"); ++ flush_work(&ca->hif_evm_work); ++ mantis_hif_exit(ca); ++ mantis_pcmcia_exit(ca); ++} +diff --git a/drivers/media/pci/mantis/mantis_hif.c b/drivers/media/pci/mantis/mantis_hif.c +new file mode 100644 +index 0000000..10c68df +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_hif.c +@@ -0,0 +1,239 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++ ++#include "mantis_hif.h" ++#include "mantis_link.h" /* temporary due to physical layer stuff */ ++ ++#include "mantis_reg.h" ++ ++ ++static int mantis_hif_sbuf_opdone_wait(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ int rc = 0; ++ ++ if (wait_event_timeout(ca->hif_opdone_wq, ++ ca->hif_event & MANTIS_SBUF_OPDONE, ++ msecs_to_jiffies(500)) == -ERESTARTSYS) { ++ ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Smart buffer operation timeout !", mantis->num); ++ rc = -EREMOTEIO; ++ } ++ dprintk(MANTIS_DEBUG, 1, "Smart Buffer Operation complete"); ++ ca->hif_event &= ~MANTIS_SBUF_OPDONE; ++ return rc; ++} ++ ++static int mantis_hif_write_wait(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 opdone = 0, timeout = 0; ++ int rc = 0; ++ ++ if (wait_event_timeout(ca->hif_write_wq, ++ mantis->gpif_status & MANTIS_GPIF_WRACK, ++ msecs_to_jiffies(500)) == -ERESTARTSYS) { ++ ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write ACK timed out !", mantis->num); ++ rc = -EREMOTEIO; ++ } ++ dprintk(MANTIS_DEBUG, 1, "Write Acknowledged"); ++ mantis->gpif_status &= ~MANTIS_GPIF_WRACK; ++ while (!opdone) { ++ opdone = (mmread(MANTIS_GPIF_STATUS) & MANTIS_SBUF_OPDONE); ++ udelay(500); ++ timeout++; ++ if (timeout > 100) { ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): Write operation timed out!", mantis->num); ++ rc = -ETIMEDOUT; ++ break; ++ } ++ } ++ dprintk(MANTIS_DEBUG, 1, "HIF Write success"); ++ return rc; ++} ++ ++ ++int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 hif_addr = 0, data, count = 4; ++ ++ dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Read", mantis->num); ++ mutex_lock(&ca->ca_lock); ++ hif_addr &= ~MANTIS_GPIF_PCMCIAREG; ++ hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; ++ hif_addr |= MANTIS_HIF_STATUS; ++ hif_addr |= addr; ++ ++ mmwrite(hif_addr, MANTIS_GPIF_BRADDR); ++ mmwrite(count, MANTIS_GPIF_BRBYTES); ++ udelay(20); ++ mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); ++ ++ if (mantis_hif_sbuf_opdone_wait(ca) != 0) { ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): GPIF Smart Buffer operation failed", mantis->num); ++ mutex_unlock(&ca->ca_lock); ++ return -EREMOTEIO; ++ } ++ data = mmread(MANTIS_GPIF_DIN); ++ mutex_unlock(&ca->ca_lock); ++ dprintk(MANTIS_DEBUG, 1, "Mem Read: 0x%02x", data); ++ return (data >> 24) & 0xff; ++} ++ ++int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data) ++{ ++ struct mantis_slot *slot = ca->slot; ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 hif_addr = 0; ++ ++ dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF Mem Write", mantis->num); ++ mutex_lock(&ca->ca_lock); ++ hif_addr &= ~MANTIS_GPIF_HIFRDWRN; ++ hif_addr &= ~MANTIS_GPIF_PCMCIAREG; ++ hif_addr &= ~MANTIS_GPIF_PCMCIAIOM; ++ hif_addr |= MANTIS_HIF_STATUS; ++ hif_addr |= addr; ++ ++ mmwrite(slot->slave_cfg, MANTIS_GPIF_CFGSLA); /* Slot0 alone for now */ ++ mmwrite(hif_addr, MANTIS_GPIF_ADDR); ++ mmwrite(data, MANTIS_GPIF_DOUT); ++ ++ if (mantis_hif_write_wait(ca) != 0) { ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); ++ mutex_unlock(&ca->ca_lock); ++ return -EREMOTEIO; ++ } ++ dprintk(MANTIS_DEBUG, 1, "Mem Write: (0x%02x to 0x%02x)", data, addr); ++ mutex_unlock(&ca->ca_lock); ++ ++ return 0; ++} ++ ++int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 data, hif_addr = 0; ++ ++ dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Read", mantis->num); ++ mutex_lock(&ca->ca_lock); ++ hif_addr &= ~MANTIS_GPIF_PCMCIAREG; ++ hif_addr |= MANTIS_GPIF_PCMCIAIOM; ++ hif_addr |= MANTIS_HIF_STATUS; ++ hif_addr |= addr; ++ ++ mmwrite(hif_addr, MANTIS_GPIF_BRADDR); ++ mmwrite(1, MANTIS_GPIF_BRBYTES); ++ udelay(20); ++ mmwrite(hif_addr | MANTIS_GPIF_HIFRDWRN, MANTIS_GPIF_ADDR); ++ ++ if (mantis_hif_sbuf_opdone_wait(ca) != 0) { ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); ++ mutex_unlock(&ca->ca_lock); ++ return -EREMOTEIO; ++ } ++ data = mmread(MANTIS_GPIF_DIN); ++ dprintk(MANTIS_DEBUG, 1, "I/O Read: 0x%02x", data); ++ udelay(50); ++ mutex_unlock(&ca->ca_lock); ++ ++ return (u8) data; ++} ++ ++int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 hif_addr = 0; ++ ++ dprintk(MANTIS_DEBUG, 1, "Adapter(%d) Slot(0): Request HIF I/O Write", mantis->num); ++ mutex_lock(&ca->ca_lock); ++ hif_addr &= ~MANTIS_GPIF_PCMCIAREG; ++ hif_addr &= ~MANTIS_GPIF_HIFRDWRN; ++ hif_addr |= MANTIS_GPIF_PCMCIAIOM; ++ hif_addr |= MANTIS_HIF_STATUS; ++ hif_addr |= addr; ++ ++ mmwrite(hif_addr, MANTIS_GPIF_ADDR); ++ mmwrite(data, MANTIS_GPIF_DOUT); ++ ++ if (mantis_hif_write_wait(ca) != 0) { ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Slot(0): HIF Smart Buffer operation failed", mantis->num); ++ mutex_unlock(&ca->ca_lock); ++ return -EREMOTEIO; ++ } ++ dprintk(MANTIS_DEBUG, 1, "I/O Write: (0x%02x to 0x%02x)", data, addr); ++ mutex_unlock(&ca->ca_lock); ++ udelay(50); ++ ++ return 0; ++} ++ ++int mantis_hif_init(struct mantis_ca *ca) ++{ ++ struct mantis_slot *slot = ca->slot; ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 irqcfg; ++ ++ slot[0].slave_cfg = 0x70773028; ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Initializing Mantis Host Interface", mantis->num); ++ ++ mutex_lock(&ca->ca_lock); ++ irqcfg = mmread(MANTIS_GPIF_IRQCFG); ++ irqcfg = MANTIS_MASK_BRRDY | ++ MANTIS_MASK_WRACK | ++ MANTIS_MASK_EXTIRQ | ++ MANTIS_MASK_WSTO | ++ MANTIS_MASK_OTHERR | ++ MANTIS_MASK_OVFLW; ++ ++ mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); ++ mutex_unlock(&ca->ca_lock); ++ ++ return 0; ++} ++ ++void mantis_hif_exit(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ u32 irqcfg; ++ ++ dprintk(MANTIS_ERROR, 1, "Adapter(%d) Exiting Mantis Host Interface", mantis->num); ++ mutex_lock(&ca->ca_lock); ++ irqcfg = mmread(MANTIS_GPIF_IRQCFG); ++ irqcfg &= ~MANTIS_MASK_BRRDY; ++ mmwrite(irqcfg, MANTIS_GPIF_IRQCFG); ++ mutex_unlock(&ca->ca_lock); ++} +diff --git a/drivers/media/pci/mantis/mantis_hif.h b/drivers/media/pci/mantis/mantis_hif.h +new file mode 100644 +index 0000000..9094f9e +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_hif.h +@@ -0,0 +1,29 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_HIF_H ++#define __MANTIS_HIF_H ++ ++#define MANTIS_HIF_MEMRD 1 ++#define MANTIS_HIF_MEMWR 2 ++#define MANTIS_HIF_IOMRD 3 ++#define MANTIS_HIF_IOMWR 4 ++ ++#endif /* __MANTIS_HIF_H */ +diff --git a/drivers/media/pci/mantis/mantis_i2c.c b/drivers/media/pci/mantis/mantis_i2c.c +new file mode 100644 +index 0000000..e779451 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_i2c.c +@@ -0,0 +1,266 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_i2c.h" ++ ++#define TRIALS 10000 ++ ++static int mantis_i2c_read(struct mantis_pci *mantis, const struct i2c_msg *msg) ++{ ++ u32 rxd, i, stat, trials; ++ ++ dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", ++ __func__, msg->addr); ++ ++ for (i = 0; i < msg->len; i++) { ++ rxd = (msg->addr << 25) | (1 << 24) ++ | MANTIS_I2C_RATE_3 ++ | MANTIS_I2C_STOP ++ | MANTIS_I2C_PGMODE; ++ ++ if (i == (msg->len - 1)) ++ rxd &= ~MANTIS_I2C_STOP; ++ ++ mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); ++ mmwrite(rxd, MANTIS_I2CDATA_CTL); ++ ++ /* wait for xfer completion */ ++ for (trials = 0; trials < TRIALS; trials++) { ++ stat = mmread(MANTIS_INT_STAT); ++ if (stat & MANTIS_INT_I2CDONE) ++ break; ++ } ++ ++ dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); ++ ++ /* wait for xfer completion */ ++ for (trials = 0; trials < TRIALS; trials++) { ++ stat = mmread(MANTIS_INT_STAT); ++ if (stat & MANTIS_INT_I2CRACK) ++ break; ++ } ++ ++ dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); ++ ++ rxd = mmread(MANTIS_I2CDATA_CTL); ++ msg->buf[i] = (u8)((rxd >> 8) & 0xFF); ++ dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); ++ } ++ dprintk(MANTIS_INFO, 0, "]\n"); ++ ++ return 0; ++} ++ ++static int mantis_i2c_write(struct mantis_pci *mantis, const struct i2c_msg *msg) ++{ ++ int i; ++ u32 txd = 0, stat, trials; ++ ++ dprintk(MANTIS_INFO, 0, " %s: Address=[0x%02x] [ ", ++ __func__, msg->addr); ++ ++ for (i = 0; i < msg->len; i++) { ++ dprintk(MANTIS_INFO, 0, "%02x ", msg->buf[i]); ++ txd = (msg->addr << 25) | (msg->buf[i] << 8) ++ | MANTIS_I2C_RATE_3 ++ | MANTIS_I2C_STOP ++ | MANTIS_I2C_PGMODE; ++ ++ if (i == (msg->len - 1)) ++ txd &= ~MANTIS_I2C_STOP; ++ ++ mmwrite(MANTIS_INT_I2CDONE, MANTIS_INT_STAT); ++ mmwrite(txd, MANTIS_I2CDATA_CTL); ++ ++ /* wait for xfer completion */ ++ for (trials = 0; trials < TRIALS; trials++) { ++ stat = mmread(MANTIS_INT_STAT); ++ if (stat & MANTIS_INT_I2CDONE) ++ break; ++ } ++ ++ dprintk(MANTIS_TMG, 0, "I2CDONE: trials=%d\n", trials); ++ ++ /* wait for xfer completion */ ++ for (trials = 0; trials < TRIALS; trials++) { ++ stat = mmread(MANTIS_INT_STAT); ++ if (stat & MANTIS_INT_I2CRACK) ++ break; ++ } ++ ++ dprintk(MANTIS_TMG, 0, "I2CRACK: trials=%d\n", trials); ++ } ++ dprintk(MANTIS_INFO, 0, "]\n"); ++ ++ return 0; ++} ++ ++static int mantis_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) ++{ ++ int ret = 0, i = 0, trials; ++ u32 stat, data, txd; ++ struct mantis_pci *mantis; ++ struct mantis_hwconfig *config; ++ ++ mantis = i2c_get_adapdata(adapter); ++ BUG_ON(!mantis); ++ config = mantis->hwconfig; ++ BUG_ON(!config); ++ ++ dprintk(MANTIS_DEBUG, 1, "Messages:%d", num); ++ mutex_lock(&mantis->i2c_lock); ++ ++ while (i < num) { ++ /* Byte MODE */ ++ if ((config->i2c_mode & MANTIS_BYTE_MODE) && ++ ((i + 1) < num) && ++ (msgs[i].len < 2) && ++ (msgs[i + 1].len < 2) && ++ (msgs[i + 1].flags & I2C_M_RD)) { ++ ++ dprintk(MANTIS_DEBUG, 0, " Byte MODE:\n"); ++ ++ /* Read operation */ ++ txd = msgs[i].addr << 25 | (0x1 << 24) ++ | (msgs[i].buf[0] << 16) ++ | MANTIS_I2C_RATE_3; ++ ++ mmwrite(txd, MANTIS_I2CDATA_CTL); ++ /* wait for xfer completion */ ++ for (trials = 0; trials < TRIALS; trials++) { ++ stat = mmread(MANTIS_INT_STAT); ++ if (stat & MANTIS_INT_I2CDONE) ++ break; ++ } ++ ++ /* check for xfer completion */ ++ if (stat & MANTIS_INT_I2CDONE) { ++ /* check xfer was acknowledged */ ++ if (stat & MANTIS_INT_I2CRACK) { ++ data = mmread(MANTIS_I2CDATA_CTL); ++ msgs[i + 1].buf[0] = (data >> 8) & 0xff; ++ dprintk(MANTIS_DEBUG, 0, " Byte <%d> RXD=0x%02x [%02x]\n", 0x0, data, msgs[i + 1].buf[0]); ++ } else { ++ /* I/O error */ ++ dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); ++ ret = -EIO; ++ break; ++ } ++ } else { ++ /* I/O error */ ++ dprintk(MANTIS_ERROR, 1, " I/O error, LINE:%d", __LINE__); ++ ret = -EIO; ++ break; ++ } ++ i += 2; /* Write/Read operation in one go */ ++ } ++ ++ if (i < num) { ++ if (msgs[i].flags & I2C_M_RD) ++ ret = mantis_i2c_read(mantis, &msgs[i]); ++ else ++ ret = mantis_i2c_write(mantis, &msgs[i]); ++ ++ i++; ++ if (ret < 0) ++ goto bail_out; ++ } ++ ++ } ++ ++ mutex_unlock(&mantis->i2c_lock); ++ ++ return num; ++ ++bail_out: ++ mutex_unlock(&mantis->i2c_lock); ++ return ret; ++} ++ ++static u32 mantis_i2c_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm mantis_algo = { ++ .master_xfer = mantis_i2c_xfer, ++ .functionality = mantis_i2c_func, ++}; ++ ++int __devinit mantis_i2c_init(struct mantis_pci *mantis) ++{ ++ u32 intstat, intmask; ++ struct i2c_adapter *i2c_adapter = &mantis->adapter; ++ struct pci_dev *pdev = mantis->pdev; ++ ++ init_waitqueue_head(&mantis->i2c_wq); ++ mutex_init(&mantis->i2c_lock); ++ strncpy(i2c_adapter->name, "Mantis I2C", sizeof(i2c_adapter->name)); ++ i2c_set_adapdata(i2c_adapter, mantis); ++ ++ i2c_adapter->owner = THIS_MODULE; ++ i2c_adapter->algo = &mantis_algo; ++ i2c_adapter->algo_data = NULL; ++ i2c_adapter->timeout = 500; ++ i2c_adapter->retries = 3; ++ i2c_adapter->dev.parent = &pdev->dev; ++ ++ mantis->i2c_rc = i2c_add_adapter(i2c_adapter); ++ if (mantis->i2c_rc < 0) ++ return mantis->i2c_rc; ++ ++ dprintk(MANTIS_DEBUG, 1, "Initializing I2C .."); ++ ++ intstat = mmread(MANTIS_INT_STAT); ++ intmask = mmread(MANTIS_INT_MASK); ++ mmwrite(intstat, MANTIS_INT_STAT); ++ dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); ++ intmask = mmread(MANTIS_INT_MASK); ++ mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_i2c_init); ++ ++int mantis_i2c_exit(struct mantis_pci *mantis) ++{ ++ u32 intmask; ++ ++ dprintk(MANTIS_DEBUG, 1, "Disabling I2C interrupt"); ++ intmask = mmread(MANTIS_INT_MASK); ++ mmwrite((intmask & ~MANTIS_INT_I2CDONE), MANTIS_INT_MASK); ++ ++ dprintk(MANTIS_DEBUG, 1, "Removing I2C adapter"); ++ return i2c_del_adapter(&mantis->adapter); ++} ++EXPORT_SYMBOL_GPL(mantis_i2c_exit); +diff --git a/drivers/media/pci/mantis/mantis_i2c.h b/drivers/media/pci/mantis/mantis_i2c.h +new file mode 100644 +index 0000000..1342df2 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_i2c.h +@@ -0,0 +1,30 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_I2C_H ++#define __MANTIS_I2C_H ++ ++#define I2C_STOP (1 << 0) ++#define I2C_READ (1 << 1) ++ ++extern int mantis_i2c_init(struct mantis_pci *mantis); ++extern int mantis_i2c_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_I2C_H */ +diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c +new file mode 100644 +index 0000000..0e5252e +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_input.c +@@ -0,0 +1,162 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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. ++*/ ++ ++#if 0 /* Currently unused */ ++ ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_uart.h" ++ ++#define MODULE_NAME "mantis_core" ++#define RC_MAP_MANTIS "rc-mantis" ++ ++static struct rc_map_table mantis_ir_table[] = { ++ { 0x29, KEY_POWER }, ++ { 0x28, KEY_FAVORITES }, ++ { 0x30, KEY_TEXT }, ++ { 0x17, KEY_INFO }, /* Preview */ ++ { 0x23, KEY_EPG }, ++ { 0x3b, KEY_F22 }, /* Record List */ ++ { 0x3c, KEY_1 }, ++ { 0x3e, KEY_2 }, ++ { 0x39, KEY_3 }, ++ { 0x36, KEY_4 }, ++ { 0x22, KEY_5 }, ++ { 0x20, KEY_6 }, ++ { 0x32, KEY_7 }, ++ { 0x26, KEY_8 }, ++ { 0x24, KEY_9 }, ++ { 0x2a, KEY_0 }, ++ ++ { 0x33, KEY_CANCEL }, ++ { 0x2c, KEY_BACK }, ++ { 0x15, KEY_CLEAR }, ++ { 0x3f, KEY_TAB }, ++ { 0x10, KEY_ENTER }, ++ { 0x14, KEY_UP }, ++ { 0x0d, KEY_RIGHT }, ++ { 0x0e, KEY_DOWN }, ++ { 0x11, KEY_LEFT }, ++ ++ { 0x21, KEY_VOLUMEUP }, ++ { 0x35, KEY_VOLUMEDOWN }, ++ { 0x3d, KEY_CHANNELDOWN }, ++ { 0x3a, KEY_CHANNELUP }, ++ { 0x2e, KEY_RECORD }, ++ { 0x2b, KEY_PLAY }, ++ { 0x13, KEY_PAUSE }, ++ { 0x25, KEY_STOP }, ++ ++ { 0x1f, KEY_REWIND }, ++ { 0x2d, KEY_FASTFORWARD }, ++ { 0x1e, KEY_PREVIOUS }, /* Replay |< */ ++ { 0x1d, KEY_NEXT }, /* Skip >| */ ++ ++ { 0x0b, KEY_CAMERA }, /* Capture */ ++ { 0x0f, KEY_LANGUAGE }, /* SAP */ ++ { 0x18, KEY_MODE }, /* PIP */ ++ { 0x12, KEY_ZOOM }, /* Full screen */ ++ { 0x1c, KEY_SUBTITLE }, ++ { 0x2f, KEY_MUTE }, ++ { 0x16, KEY_F20 }, /* L/R */ ++ { 0x38, KEY_F21 }, /* Hibernate */ ++ ++ { 0x37, KEY_SWITCHVIDEOMODE }, /* A/V */ ++ { 0x31, KEY_AGAIN }, /* Recall */ ++ { 0x1a, KEY_KPPLUS }, /* Zoom+ */ ++ { 0x19, KEY_KPMINUS }, /* Zoom- */ ++ { 0x27, KEY_RED }, ++ { 0x0C, KEY_GREEN }, ++ { 0x01, KEY_YELLOW }, ++ { 0x00, KEY_BLUE }, ++}; ++ ++static struct rc_map_list ir_mantis_map = { ++ .map = { ++ .scan = mantis_ir_table, ++ .size = ARRAY_SIZE(mantis_ir_table), ++ .rc_type = RC_TYPE_UNKNOWN, ++ .name = RC_MAP_MANTIS, ++ } ++}; ++ ++int mantis_input_init(struct mantis_pci *mantis) ++{ ++ struct rc_dev *dev; ++ int err; ++ ++ err = rc_map_register(&ir_mantis_map); ++ if (err) ++ goto out; ++ ++ dev = rc_allocate_device(); ++ if (!dev) { ++ dprintk(MANTIS_ERROR, 1, "Remote device allocation failed"); ++ err = -ENOMEM; ++ goto out_map; ++ } ++ ++ sprintf(mantis->input_name, "Mantis %s IR receiver", mantis->hwconfig->model_name); ++ sprintf(mantis->input_phys, "pci-%s/ir0", pci_name(mantis->pdev)); ++ ++ dev->input_name = mantis->input_name; ++ dev->input_phys = mantis->input_phys; ++ dev->input_id.bustype = BUS_PCI; ++ dev->input_id.vendor = mantis->vendor_id; ++ dev->input_id.product = mantis->device_id; ++ dev->input_id.version = 1; ++ dev->driver_name = MODULE_NAME; ++ dev->map_name = RC_MAP_MANTIS; ++ dev->dev.parent = &mantis->pdev->dev; ++ ++ err = rc_register_device(dev); ++ if (err) { ++ dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); ++ goto out_dev; ++ } ++ ++ mantis->rc = dev; ++ return 0; ++ ++out_dev: ++ rc_free_device(dev); ++out_map: ++ rc_map_unregister(&ir_mantis_map); ++out: ++ return err; ++} ++ ++int mantis_init_exit(struct mantis_pci *mantis) ++{ ++ rc_unregister_device(mantis->rc); ++ rc_map_unregister(&ir_mantis_map); ++ return 0; ++} ++ ++#endif +diff --git a/drivers/media/pci/mantis/mantis_ioc.c b/drivers/media/pci/mantis/mantis_ioc.c +new file mode 100644 +index 0000000..24fcdc6 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_ioc.c +@@ -0,0 +1,124 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_ioc.h" ++ ++static int read_eeprom_bytes(struct mantis_pci *mantis, u8 reg, u8 *data, u8 length) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ int err; ++ u8 buf = reg; ++ ++ struct i2c_msg msg[] = { ++ { .addr = 0x50, .flags = 0, .buf = &buf, .len = 1 }, ++ { .addr = 0x50, .flags = I2C_M_RD, .buf = data, .len = length }, ++ }; ++ ++ err = i2c_transfer(adapter, msg, 2); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: i2c read: < err=%i d0=0x%02x d1=0x%02x >", ++ err, data[0], data[1]); ++ ++ return err; ++ } ++ ++ return 0; ++} ++int mantis_get_mac(struct mantis_pci *mantis) ++{ ++ int err; ++ u8 mac_addr[6] = {0}; ++ ++ err = read_eeprom_bytes(mantis, 0x08, mac_addr, 6); ++ if (err < 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Mantis EEPROM read error <%d>", err); ++ ++ return err; ++ } ++ ++ dprintk(MANTIS_ERROR, 0, " MAC Address=[%pM]\n", mac_addr); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_get_mac); ++ ++/* Turn the given bit on or off. */ ++void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value) ++{ ++ u32 cur; ++ ++ dprintk(MANTIS_DEBUG, 1, "Set Bit <%d> to <%d>", bitpos, value); ++ cur = mmread(MANTIS_GPIF_ADDR); ++ if (value) ++ mantis->gpio_status = cur | (1 << bitpos); ++ else ++ mantis->gpio_status = cur & (~(1 << bitpos)); ++ ++ dprintk(MANTIS_DEBUG, 1, "GPIO Value <%02x>", mantis->gpio_status); ++ mmwrite(mantis->gpio_status, MANTIS_GPIF_ADDR); ++ mmwrite(0x00, MANTIS_GPIF_DOUT); ++} ++EXPORT_SYMBOL_GPL(mantis_gpio_set_bits); ++ ++int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl) ++{ ++ u32 reg; ++ ++ reg = mmread(MANTIS_CONTROL); ++ switch (stream_ctl) { ++ case STREAM_TO_HIF: ++ dprintk(MANTIS_DEBUG, 1, "Set stream to HIF"); ++ reg &= 0xff - MANTIS_BYPASS; ++ mmwrite(reg, MANTIS_CONTROL); ++ reg |= MANTIS_BYPASS; ++ mmwrite(reg, MANTIS_CONTROL); ++ break; ++ ++ case STREAM_TO_CAM: ++ dprintk(MANTIS_DEBUG, 1, "Set stream to CAM"); ++ reg |= MANTIS_BYPASS; ++ mmwrite(reg, MANTIS_CONTROL); ++ reg &= 0xff - MANTIS_BYPASS; ++ mmwrite(reg, MANTIS_CONTROL); ++ break; ++ default: ++ dprintk(MANTIS_ERROR, 1, "Unknown MODE <%02x>", stream_ctl); ++ return -1; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_stream_control); +diff --git a/drivers/media/pci/mantis/mantis_ioc.h b/drivers/media/pci/mantis/mantis_ioc.h +new file mode 100644 +index 0000000..d56e002 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_ioc.h +@@ -0,0 +1,51 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_IOC_H ++#define __MANTIS_IOC_H ++ ++#define GPIF_A00 0x00 ++#define GPIF_A01 0x01 ++#define GPIF_A02 0x02 ++#define GPIF_A03 0x03 ++#define GPIF_A04 0x04 ++#define GPIF_A05 0x05 ++#define GPIF_A06 0x06 ++#define GPIF_A07 0x07 ++#define GPIF_A08 0x08 ++#define GPIF_A09 0x09 ++#define GPIF_A10 0x0a ++#define GPIF_A11 0x0b ++ ++#define GPIF_A12 0x0c ++#define GPIF_A13 0x0d ++#define GPIF_A14 0x0e ++ ++enum mantis_stream_control { ++ STREAM_TO_HIF = 0, ++ STREAM_TO_CAM ++}; ++ ++extern int mantis_get_mac(struct mantis_pci *mantis); ++extern void mantis_gpio_set_bits(struct mantis_pci *mantis, u32 bitpos, u8 value); ++ ++extern int mantis_stream_control(struct mantis_pci *mantis, enum mantis_stream_control stream_ctl); ++ ++#endif /* __MANTIS_IOC_H */ +diff --git a/drivers/media/pci/mantis/mantis_link.h b/drivers/media/pci/mantis/mantis_link.h +new file mode 100644 +index 0000000..2a81477 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_link.h +@@ -0,0 +1,83 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_LINK_H ++#define __MANTIS_LINK_H ++ ++#include ++#include ++#include "dvb_ca_en50221.h" ++ ++enum mantis_sbuf_status { ++ MANTIS_SBUF_DATA_AVAIL = 1, ++ MANTIS_SBUF_DATA_EMPTY = 2, ++ MANTIS_SBUF_DATA_OVFLW = 3 ++}; ++ ++struct mantis_slot { ++ u32 timeout; ++ u32 slave_cfg; ++ u32 bar; ++}; ++ ++/* Physical layer */ ++enum mantis_slot_state { ++ MODULE_INSERTED = 3, ++ MODULE_XTRACTED = 4 ++}; ++ ++struct mantis_ca { ++ struct mantis_slot slot[4]; ++ ++ struct work_struct hif_evm_work; ++ ++ u32 hif_event; ++ wait_queue_head_t hif_opdone_wq; ++ wait_queue_head_t hif_brrdyw_wq; ++ wait_queue_head_t hif_data_wq; ++ wait_queue_head_t hif_write_wq; /* HIF Write op */ ++ ++ enum mantis_sbuf_status sbuf_status; ++ ++ enum mantis_slot_state slot_state; ++ ++ void *ca_priv; ++ ++ struct dvb_ca_en50221 en50221; ++ struct mutex ca_lock; ++}; ++ ++/* CA */ ++extern void mantis_event_cam_plugin(struct mantis_ca *ca); ++extern void mantis_event_cam_unplug(struct mantis_ca *ca); ++extern int mantis_pcmcia_init(struct mantis_ca *ca); ++extern void mantis_pcmcia_exit(struct mantis_ca *ca); ++extern int mantis_evmgr_init(struct mantis_ca *ca); ++extern void mantis_evmgr_exit(struct mantis_ca *ca); ++ ++/* HIF */ ++extern int mantis_hif_init(struct mantis_ca *ca); ++extern void mantis_hif_exit(struct mantis_ca *ca); ++extern int mantis_hif_read_mem(struct mantis_ca *ca, u32 addr); ++extern int mantis_hif_write_mem(struct mantis_ca *ca, u32 addr, u8 data); ++extern int mantis_hif_read_iom(struct mantis_ca *ca, u32 addr); ++extern int mantis_hif_write_iom(struct mantis_ca *ca, u32 addr, u8 data); ++ ++#endif /* __MANTIS_LINK_H */ +diff --git a/drivers/media/pci/mantis/mantis_pci.c b/drivers/media/pci/mantis/mantis_pci.c +new file mode 100644 +index 0000000..371558a +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_pci.c +@@ -0,0 +1,170 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_pci.h" ++ ++#define DRIVER_NAME "Mantis Core" ++ ++int __devinit mantis_pci_init(struct mantis_pci *mantis) ++{ ++ u8 latency; ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ struct pci_dev *pdev = mantis->pdev; ++ int err, ret = 0; ++ ++ dprintk(MANTIS_ERROR, 0, "found a %s PCI %s device on (%02x:%02x.%x),\n", ++ config->model_name, ++ config->dev_type, ++ mantis->pdev->bus->number, ++ PCI_SLOT(mantis->pdev->devfn), ++ PCI_FUNC(mantis->pdev->devfn)); ++ ++ err = pci_enable_device(pdev); ++ if (err != 0) { ++ ret = -ENODEV; ++ dprintk(MANTIS_ERROR, 1, "ERROR: PCI enable failed <%i>", err); ++ goto fail0; ++ } ++ ++ err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (err != 0) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: Unable to obtain 32 bit DMA <%i>", err); ++ ret = -ENOMEM; ++ goto fail1; ++ } ++ ++ pci_set_master(pdev); ++ ++ if (!request_mem_region(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0), ++ DRIVER_NAME)) { ++ ++ dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 Request failed !"); ++ ret = -ENODEV; ++ goto fail1; ++ } ++ ++ mantis->mmio = ioremap(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0)); ++ ++ if (!mantis->mmio) { ++ dprintk(MANTIS_ERROR, 1, "ERROR: BAR0 remap failed !"); ++ ret = -ENODEV; ++ goto fail2; ++ } ++ ++ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &latency); ++ mantis->latency = latency; ++ mantis->revision = pdev->revision; ++ ++ dprintk(MANTIS_ERROR, 0, " Mantis Rev %d [%04x:%04x], ", ++ mantis->revision, ++ mantis->pdev->subsystem_vendor, ++ mantis->pdev->subsystem_device); ++ ++ dprintk(MANTIS_ERROR, 0, ++ "irq: %d, latency: %d\n memory: 0x%lx, mmio: 0x%p\n", ++ mantis->pdev->irq, ++ mantis->latency, ++ mantis->mantis_addr, ++ mantis->mmio); ++ ++ err = request_irq(pdev->irq, ++ config->irq_handler, ++ IRQF_SHARED, ++ DRIVER_NAME, ++ mantis); ++ ++ if (err != 0) { ++ ++ dprintk(MANTIS_ERROR, 1, "ERROR: IRQ registration failed ! <%d>", err); ++ ret = -ENODEV; ++ goto fail3; ++ } ++ ++ pci_set_drvdata(pdev, mantis); ++ return ret; ++ ++ /* Error conditions */ ++fail3: ++ dprintk(MANTIS_ERROR, 1, "ERROR: <%d> I/O unmap", ret); ++ if (mantis->mmio) ++ iounmap(mantis->mmio); ++ ++fail2: ++ dprintk(MANTIS_ERROR, 1, "ERROR: <%d> releasing regions", ret); ++ release_mem_region(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0)); ++ ++fail1: ++ dprintk(MANTIS_ERROR, 1, "ERROR: <%d> disabling device", ret); ++ pci_disable_device(pdev); ++ ++fail0: ++ dprintk(MANTIS_ERROR, 1, "ERROR: <%d> exiting", ret); ++ pci_set_drvdata(pdev, NULL); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(mantis_pci_init); ++ ++void mantis_pci_exit(struct mantis_pci *mantis) ++{ ++ struct pci_dev *pdev = mantis->pdev; ++ ++ dprintk(MANTIS_NOTICE, 1, " mem: 0x%p", mantis->mmio); ++ free_irq(pdev->irq, mantis); ++ if (mantis->mmio) { ++ iounmap(mantis->mmio); ++ release_mem_region(pci_resource_start(pdev, 0), ++ pci_resource_len(pdev, 0)); ++ } ++ ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++} ++EXPORT_SYMBOL_GPL(mantis_pci_exit); ++ ++MODULE_DESCRIPTION("Mantis PCI DTV bridge driver"); ++MODULE_AUTHOR("Manu Abraham"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/mantis/mantis_pci.h b/drivers/media/pci/mantis/mantis_pci.h +new file mode 100644 +index 0000000..65f0045 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_pci.h +@@ -0,0 +1,27 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_PCI_H ++#define __MANTIS_PCI_H ++ ++extern int mantis_pci_init(struct mantis_pci *mantis); ++extern void mantis_pci_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_PCI_H */ +diff --git a/drivers/media/pci/mantis/mantis_pcmcia.c b/drivers/media/pci/mantis/mantis_pcmcia.c +new file mode 100644 +index 0000000..2f188c0 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_pcmcia.c +@@ -0,0 +1,121 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_link.h" /* temporary due to physical layer stuff */ ++#include "mantis_reg.h" ++ ++/* ++ * If Slot state is already PLUG_IN event and we are called ++ * again, definitely it is jitter alone ++ */ ++void mantis_event_cam_plugin(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ u32 gpif_irqcfg; ++ ++ if (ca->slot_state == MODULE_XTRACTED) { ++ dprintk(MANTIS_DEBUG, 1, "Event: CAM Plugged IN: Adapter(%d) Slot(0)", mantis->num); ++ udelay(50); ++ mmwrite(0xda000000, MANTIS_CARD_RESET); ++ gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); ++ gpif_irqcfg |= MANTIS_MASK_PLUGOUT; ++ gpif_irqcfg &= ~MANTIS_MASK_PLUGIN; ++ mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); ++ udelay(500); ++ ca->slot_state = MODULE_INSERTED; ++ } ++ udelay(100); ++} ++ ++/* ++ * If Slot state is already UN_PLUG event and we are called ++ * again, definitely it is jitter alone ++ */ ++void mantis_event_cam_unplug(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ u32 gpif_irqcfg; ++ ++ if (ca->slot_state == MODULE_INSERTED) { ++ dprintk(MANTIS_DEBUG, 1, "Event: CAM Unplugged: Adapter(%d) Slot(0)", mantis->num); ++ udelay(50); ++ mmwrite(0x00da0000, MANTIS_CARD_RESET); ++ gpif_irqcfg = mmread(MANTIS_GPIF_IRQCFG); ++ gpif_irqcfg |= MANTIS_MASK_PLUGIN; ++ gpif_irqcfg &= ~MANTIS_MASK_PLUGOUT; ++ mmwrite(gpif_irqcfg, MANTIS_GPIF_IRQCFG); ++ udelay(500); ++ ca->slot_state = MODULE_XTRACTED; ++ } ++ udelay(100); ++} ++ ++int mantis_pcmcia_init(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ u32 gpif_stat, card_stat; ++ ++ mmwrite(mmread(MANTIS_INT_MASK) | MANTIS_INT_IRQ0, MANTIS_INT_MASK); ++ gpif_stat = mmread(MANTIS_GPIF_STATUS); ++ card_stat = mmread(MANTIS_GPIF_IRQCFG); ++ ++ if (gpif_stat & MANTIS_GPIF_DETSTAT) { ++ dprintk(MANTIS_DEBUG, 1, "CAM found on Adapter(%d) Slot(0)", mantis->num); ++ mmwrite(card_stat | MANTIS_MASK_PLUGOUT, MANTIS_GPIF_IRQCFG); ++ ca->slot_state = MODULE_INSERTED; ++ dvb_ca_en50221_camchange_irq(&ca->en50221, ++ 0, ++ DVB_CA_EN50221_CAMCHANGE_INSERTED); ++ } else { ++ dprintk(MANTIS_DEBUG, 1, "Empty Slot on Adapter(%d) Slot(0)", mantis->num); ++ mmwrite(card_stat | MANTIS_MASK_PLUGIN, MANTIS_GPIF_IRQCFG); ++ ca->slot_state = MODULE_XTRACTED; ++ dvb_ca_en50221_camchange_irq(&ca->en50221, ++ 0, ++ DVB_CA_EN50221_CAMCHANGE_REMOVED); ++ } ++ ++ return 0; ++} ++ ++void mantis_pcmcia_exit(struct mantis_ca *ca) ++{ ++ struct mantis_pci *mantis = ca->ca_priv; ++ ++ mmwrite(mmread(MANTIS_GPIF_STATUS) & (~MANTIS_CARD_PLUGOUT | ~MANTIS_CARD_PLUGIN), MANTIS_GPIF_STATUS); ++ mmwrite(mmread(MANTIS_INT_MASK) & ~MANTIS_INT_IRQ0, MANTIS_INT_MASK); ++} +diff --git a/drivers/media/pci/mantis/mantis_reg.h b/drivers/media/pci/mantis/mantis_reg.h +new file mode 100644 +index 0000000..7761f9d +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_reg.h +@@ -0,0 +1,197 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_REG_H ++#define __MANTIS_REG_H ++ ++/* Interrupts */ ++#define MANTIS_INT_STAT 0x00 ++#define MANTIS_INT_MASK 0x04 ++ ++#define MANTIS_INT_RISCSTAT (0x0f << 28) ++#define MANTIS_INT_RISCEN (0x01 << 27) ++#define MANTIS_INT_I2CRACK (0x01 << 26) ++ ++/* #define MANTIS_INT_GPIF (0xff << 12) */ ++ ++#define MANTIS_INT_PCMCIA7 (0x01 << 19) ++#define MANTIS_INT_PCMCIA6 (0x01 << 18) ++#define MANTIS_INT_PCMCIA5 (0x01 << 17) ++#define MANTIS_INT_PCMCIA4 (0x01 << 16) ++#define MANTIS_INT_PCMCIA3 (0x01 << 15) ++#define MANTIS_INT_PCMCIA2 (0x01 << 14) ++#define MANTIS_INT_PCMCIA1 (0x01 << 13) ++#define MANTIS_INT_PCMCIA0 (0x01 << 12) ++#define MANTIS_INT_IRQ1 (0x01 << 11) ++#define MANTIS_INT_IRQ0 (0x01 << 10) ++#define MANTIS_INT_OCERR (0x01 << 8) ++#define MANTIS_INT_PABORT (0x01 << 7) ++#define MANTIS_INT_RIPERR (0x01 << 6) ++#define MANTIS_INT_PPERR (0x01 << 5) ++#define MANTIS_INT_FTRGT (0x01 << 3) ++#define MANTIS_INT_RISCI (0x01 << 1) ++#define MANTIS_INT_I2CDONE (0x01 << 0) ++ ++/* DMA */ ++#define MANTIS_DMA_CTL 0x08 ++#define MANTIS_GPIF_RD (0xff << 24) ++#define MANTIS_GPIF_WR (0xff << 16) ++#define MANTIS_CPU_DO (0x01 << 10) ++#define MANTIS_DRV_DO (0x01 << 9) ++#define MANTIS_I2C_RD (0x01 << 7) ++#define MANTIS_I2C_WR (0x01 << 6) ++#define MANTIS_DCAP_MODE (0x01 << 5) ++#define MANTIS_FIFO_TP_4 (0x00 << 3) ++#define MANTIS_FIFO_TP_8 (0x01 << 3) ++#define MANTIS_FIFO_TP_16 (0x02 << 3) ++#define MANTIS_FIFO_EN (0x01 << 2) ++#define MANTIS_DCAP_EN (0x01 << 1) ++#define MANTIS_RISC_EN (0x01 << 0) ++ ++/* DEBUG */ ++#define MANTIS_DEBUGREG 0x0c ++#define MANTIS_DATINV (0x0e << 7) ++#define MANTIS_TOP_DEBUGSEL (0x07 << 4) ++#define MANTIS_PCMCIA_DEBUGSEL (0x0f << 0) ++ ++#define MANTIS_RISC_START 0x10 ++#define MANTIS_RISC_PC 0x14 ++ ++/* I2C */ ++#define MANTIS_I2CDATA_CTL 0x18 ++#define MANTIS_I2C_RATE_1 (0x00 << 6) ++#define MANTIS_I2C_RATE_2 (0x01 << 6) ++#define MANTIS_I2C_RATE_3 (0x02 << 6) ++#define MANTIS_I2C_RATE_4 (0x03 << 6) ++#define MANTIS_I2C_STOP (0x01 << 5) ++#define MANTIS_I2C_PGMODE (0x01 << 3) ++ ++/* DATA */ ++#define MANTIS_CMD_DATA_R1 0x20 ++#define MANTIS_CMD_DATA_3 (0xff << 24) ++#define MANTIS_CMD_DATA_2 (0xff << 16) ++#define MANTIS_CMD_DATA_1 (0xff << 8) ++#define MANTIS_CMD_DATA_0 (0xff << 0) ++ ++#define MANTIS_CMD_DATA_R2 0x24 ++#define MANTIS_CMD_DATA_7 (0xff << 24) ++#define MANTIS_CMD_DATA_6 (0xff << 16) ++#define MANTIS_CMD_DATA_5 (0xff << 8) ++#define MANTIS_CMD_DATA_4 (0xff << 0) ++ ++#define MANTIS_CONTROL 0x28 ++#define MANTIS_DET (0x01 << 7) ++#define MANTIS_DAT_CF_EN (0x01 << 6) ++#define MANTIS_ACS (0x03 << 4) ++#define MANTIS_VCCEN (0x01 << 3) ++#define MANTIS_BYPASS (0x01 << 2) ++#define MANTIS_MRST (0x01 << 1) ++#define MANTIS_CRST_INT (0x01 << 0) ++ ++#define MANTIS_GPIF_CFGSLA 0x84 ++#define MANTIS_GPIF_WAITSMPL (0x07 << 28) ++#define MANTIS_GPIF_BYTEADDRSUB (0x01 << 25) ++#define MANTIS_GPIF_WAITPOL (0x01 << 24) ++#define MANTIS_GPIF_NCDELAY (0x07 << 20) ++#define MANTIS_GPIF_RW2CSDELAY (0x07 << 16) ++#define MANTIS_GPIF_SLFTIMEDMODE (0x01 << 15) ++#define MANTIS_GPIF_SLFTIMEDDELY (0x7f << 8) ++#define MANTIS_GPIF_DEVTYPE (0x07 << 4) ++#define MANTIS_GPIF_BIGENDIAN (0x01 << 3) ++#define MANTIS_GPIF_FETCHCMD (0x03 << 1) ++#define MANTIS_GPIF_HWORDDEV (0x01 << 0) ++ ++#define MANTIS_GPIF_WSTOPER 0x90 ++#define MANTIS_GPIF_WSTOPERWREN3 (0x01 << 31) ++#define MANTIS_GPIF_PARBOOTN (0x01 << 29) ++#define MANTIS_GPIF_WSTOPERSLID3 (0x1f << 24) ++#define MANTIS_GPIF_WSTOPERWREN2 (0x01 << 23) ++#define MANTIS_GPIF_WSTOPERSLID2 (0x1f << 16) ++#define MANTIS_GPIF_WSTOPERWREN1 (0x01 << 15) ++#define MANTIS_GPIF_WSTOPERSLID1 (0x1f << 8) ++#define MANTIS_GPIF_WSTOPERWREN0 (0x01 << 7) ++#define MANTIS_GPIF_WSTOPERSLID0 (0x1f << 0) ++ ++#define MANTIS_GPIF_CS2RW 0x94 ++#define MANTIS_GPIF_CS2RWWREN3 (0x01 << 31) ++#define MANTIS_GPIF_CS2RWDELY3 (0x3f << 24) ++#define MANTIS_GPIF_CS2RWWREN2 (0x01 << 23) ++#define MANTIS_GPIF_CS2RWDELY2 (0x3f << 16) ++#define MANTIS_GPIF_CS2RWWREN1 (0x01 << 15) ++#define MANTIS_GPIF_CS2RWDELY1 (0x3f << 8) ++#define MANTIS_GPIF_CS2RWWREN0 (0x01 << 7) ++#define MANTIS_GPIF_CS2RWDELY0 (0x3f << 0) ++ ++#define MANTIS_GPIF_IRQCFG 0x98 ++#define MANTIS_GPIF_IRQPOL (0x01 << 8) ++#define MANTIS_MASK_WRACK (0x01 << 7) ++#define MANTIS_MASK_BRRDY (0x01 << 6) ++#define MANTIS_MASK_OVFLW (0x01 << 5) ++#define MANTIS_MASK_OTHERR (0x01 << 4) ++#define MANTIS_MASK_WSTO (0x01 << 3) ++#define MANTIS_MASK_EXTIRQ (0x01 << 2) ++#define MANTIS_MASK_PLUGIN (0x01 << 1) ++#define MANTIS_MASK_PLUGOUT (0x01 << 0) ++ ++#define MANTIS_GPIF_STATUS 0x9c ++#define MANTIS_SBUF_KILLOP (0x01 << 15) ++#define MANTIS_SBUF_OPDONE (0x01 << 14) ++#define MANTIS_SBUF_EMPTY (0x01 << 13) ++#define MANTIS_GPIF_DETSTAT (0x01 << 9) ++#define MANTIS_GPIF_INTSTAT (0x01 << 8) ++#define MANTIS_GPIF_WRACK (0x01 << 7) ++#define MANTIS_GPIF_BRRDY (0x01 << 6) ++#define MANTIS_SBUF_OVFLW (0x01 << 5) ++#define MANTIS_GPIF_OTHERR (0x01 << 4) ++#define MANTIS_SBUF_WSTO (0x01 << 3) ++#define MANTIS_GPIF_EXTIRQ (0x01 << 2) ++#define MANTIS_CARD_PLUGIN (0x01 << 1) ++#define MANTIS_CARD_PLUGOUT (0x01 << 0) ++ ++#define MANTIS_GPIF_BRADDR 0xa0 ++#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) ++#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) ++#define MANTIS_GPIF_BR_ADDR (0xfffffff << 0) ++ ++#define MANTIS_GPIF_BRBYTES 0xa4 ++#define MANTIS_GPIF_BRCNT (0xfff << 0) ++ ++#define MANTIS_PCMCIA_RESET 0xa8 ++#define MANTIS_PCMCIA_RSTVAL (0xff << 0) ++ ++#define MANTIS_CARD_RESET 0xac ++ ++#define MANTIS_GPIF_ADDR 0xb0 ++#define MANTIS_GPIF_HIFRDWRN (0x01 << 31) ++#define MANTIS_GPIF_PCMCIAREG (0x01 << 27) ++#define MANTIS_GPIF_PCMCIAIOM (0x01 << 26) ++#define MANTIS_GPIF_HIFADDR (0xfffffff << 0) ++ ++#define MANTIS_GPIF_DOUT 0xb4 ++#define MANTIS_GPIF_HIFDOUT (0xfffffff << 0) ++ ++#define MANTIS_GPIF_DIN 0xb8 ++#define MANTIS_GPIF_HIFDIN (0xfffffff << 0) ++ ++#define MANTIS_GPIF_SPARE 0xbc ++#define MANTIS_GPIF_LOGICRD (0xffff << 16) ++#define MANTIS_GPIF_LOGICRW (0xffff << 0) ++ ++#endif /* __MANTIS_REG_H */ +diff --git a/drivers/media/pci/mantis/mantis_uart.c b/drivers/media/pci/mantis/mantis_uart.c +new file mode 100644 +index 0000000..a707192 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_uart.c +@@ -0,0 +1,188 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_reg.h" ++#include "mantis_uart.h" ++ ++struct mantis_uart_params { ++ enum mantis_baud baud_rate; ++ enum mantis_parity parity; ++}; ++ ++static struct { ++ char string[7]; ++} rates[5] = { ++ { "9600" }, ++ { "19200" }, ++ { "38400" }, ++ { "57600" }, ++ { "115200" } ++}; ++ ++static struct { ++ char string[5]; ++} parity[3] = { ++ { "NONE" }, ++ { "ODD" }, ++ { "EVEN" } ++}; ++ ++#define UART_MAX_BUF 16 ++ ++static int mantis_uart_read(struct mantis_pci *mantis, u8 *data) ++{ ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ u32 stat = 0, i; ++ ++ /* get data */ ++ for (i = 0; i < (config->bytes + 1); i++) { ++ ++ stat = mmread(MANTIS_UART_STAT); ++ ++ if (stat & MANTIS_UART_RXFIFO_FULL) { ++ dprintk(MANTIS_ERROR, 1, "RX Fifo FULL"); ++ } ++ data[i] = mmread(MANTIS_UART_RXD) & 0x3f; ++ ++ dprintk(MANTIS_DEBUG, 1, "Reading ... <%02x>", data[i] & 0x3f); ++ ++ if (data[i] & (1 << 7)) { ++ dprintk(MANTIS_ERROR, 1, "UART framing error"); ++ return -EINVAL; ++ } ++ if (data[i] & (1 << 6)) { ++ dprintk(MANTIS_ERROR, 1, "UART parity error"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static void mantis_uart_work(struct work_struct *work) ++{ ++ struct mantis_pci *mantis = container_of(work, struct mantis_pci, uart_work); ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ u8 buf[16]; ++ int i; ++ ++ mantis_uart_read(mantis, buf); ++ ++ for (i = 0; i < (config->bytes + 1); i++) ++ dprintk(MANTIS_INFO, 1, "UART BUF:%d <%02x> ", i, buf[i]); ++ ++ dprintk(MANTIS_DEBUG, 0, "\n"); ++} ++ ++static int mantis_uart_setup(struct mantis_pci *mantis, ++ struct mantis_uart_params *params) ++{ ++ u32 reg; ++ ++ mmwrite((mmread(MANTIS_UART_CTL) | (params->parity & 0x3)), MANTIS_UART_CTL); ++ ++ reg = mmread(MANTIS_UART_BAUD); ++ ++ switch (params->baud_rate) { ++ case MANTIS_BAUD_9600: ++ reg |= 0xd8; ++ break; ++ case MANTIS_BAUD_19200: ++ reg |= 0x6c; ++ break; ++ case MANTIS_BAUD_38400: ++ reg |= 0x36; ++ break; ++ case MANTIS_BAUD_57600: ++ reg |= 0x23; ++ break; ++ case MANTIS_BAUD_115200: ++ reg |= 0x11; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ mmwrite(reg, MANTIS_UART_BAUD); ++ ++ return 0; ++} ++ ++int mantis_uart_init(struct mantis_pci *mantis) ++{ ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ struct mantis_uart_params params; ++ ++ /* default parity: */ ++ params.baud_rate = config->baud_rate; ++ params.parity = config->parity; ++ dprintk(MANTIS_INFO, 1, "Initializing UART @ %sbps parity:%s", ++ rates[params.baud_rate].string, ++ parity[params.parity].string); ++ ++ init_waitqueue_head(&mantis->uart_wq); ++ spin_lock_init(&mantis->uart_lock); ++ ++ INIT_WORK(&mantis->uart_work, mantis_uart_work); ++ ++ /* disable interrupt */ ++ mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); ++ ++ mantis_uart_setup(mantis, ¶ms); ++ ++ /* default 1 byte */ ++ mmwrite((mmread(MANTIS_UART_BAUD) | (config->bytes << 8)), MANTIS_UART_BAUD); ++ ++ /* flush buffer */ ++ mmwrite((mmread(MANTIS_UART_CTL) | MANTIS_UART_RXFLUSH), MANTIS_UART_CTL); ++ ++ /* enable interrupt */ ++ mmwrite(mmread(MANTIS_INT_MASK) | 0x800, MANTIS_INT_MASK); ++ mmwrite(mmread(MANTIS_UART_CTL) | MANTIS_UART_RXINT, MANTIS_UART_CTL); ++ ++ schedule_work(&mantis->uart_work); ++ dprintk(MANTIS_DEBUG, 1, "UART successfully initialized"); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(mantis_uart_init); ++ ++void mantis_uart_exit(struct mantis_pci *mantis) ++{ ++ /* disable interrupt */ ++ mmwrite(mmread(MANTIS_UART_CTL) & 0xffef, MANTIS_UART_CTL); ++ flush_work(&mantis->uart_work); ++} ++EXPORT_SYMBOL_GPL(mantis_uart_exit); +diff --git a/drivers/media/pci/mantis/mantis_uart.h b/drivers/media/pci/mantis/mantis_uart.h +new file mode 100644 +index 0000000..ffb62a0 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_uart.h +@@ -0,0 +1,58 @@ ++/* ++ Mantis PCI bridge driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_UART_H ++#define __MANTIS_UART_H ++ ++#define MANTIS_UART_CTL 0xe0 ++#define MANTIS_UART_RXINT (1 << 4) ++#define MANTIS_UART_RXFLUSH (1 << 2) ++ ++#define MANTIS_UART_RXD 0xe8 ++#define MANTIS_UART_BAUD 0xec ++ ++#define MANTIS_UART_STAT 0xf0 ++#define MANTIS_UART_RXFIFO_DATA (1 << 7) ++#define MANTIS_UART_RXFIFO_EMPTY (1 << 6) ++#define MANTIS_UART_RXFIFO_FULL (1 << 3) ++#define MANTIS_UART_FRAME_ERR (1 << 2) ++#define MANTIS_UART_PARITY_ERR (1 << 1) ++#define MANTIS_UART_RXTHRESH_INT (1 << 0) ++ ++enum mantis_baud { ++ MANTIS_BAUD_9600 = 0, ++ MANTIS_BAUD_19200, ++ MANTIS_BAUD_38400, ++ MANTIS_BAUD_57600, ++ MANTIS_BAUD_115200 ++}; ++ ++enum mantis_parity { ++ MANTIS_PARITY_NONE = 0, ++ MANTIS_PARITY_EVEN, ++ MANTIS_PARITY_ODD, ++}; ++ ++struct mantis_pci; ++ ++extern int mantis_uart_init(struct mantis_pci *mantis); ++extern void mantis_uart_exit(struct mantis_pci *mantis); ++ ++#endif /* __MANTIS_UART_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp1033.c b/drivers/media/pci/mantis/mantis_vp1033.c +new file mode 100644 +index 0000000..115003e +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1033.c +@@ -0,0 +1,212 @@ ++/* ++ Mantis VP-1033 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "stv0299.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp1033.h" ++#include "mantis_reg.h" ++ ++u8 lgtdqcs001f_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x2a, ++ 0x05, 0x85, ++ 0x06, 0x02, ++ 0x07, 0x00, ++ 0x08, 0x00, ++ 0x0c, 0x01, ++ 0x0d, 0x81, ++ 0x0e, 0x44, ++ 0x0f, 0x94, ++ 0x10, 0x3c, ++ 0x11, 0x84, ++ 0x12, 0xb9, ++ 0x13, 0xb5, ++ 0x14, 0x4f, ++ 0x15, 0xc9, ++ 0x16, 0x80, ++ 0x17, 0x36, ++ 0x18, 0xfb, ++ 0x19, 0xcf, ++ 0x1a, 0xbc, ++ 0x1c, 0x2b, ++ 0x1d, 0x27, ++ 0x1e, 0x00, ++ 0x1f, 0x0b, ++ 0x20, 0xa1, ++ 0x21, 0x60, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, ++ 0x29, 0x28, ++ 0x2a, 0x14, ++ 0x2b, 0x0f, ++ 0x2c, 0x09, ++ 0x2d, 0x05, ++ 0x31, 0x1f, ++ 0x32, 0x19, ++ 0x33, 0xfc, ++ 0x34, 0x13, ++ 0xff, 0xff, ++}; ++ ++#define MANTIS_MODEL_NAME "VP-1033" ++#define MANTIS_DEV_TYPE "DVB-S/DSS" ++ ++static int lgtdqcs001f_tuner_set(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mantis_pci *mantis = fe->dvb->priv; ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ u8 buf[4]; ++ u32 div; ++ ++ ++ struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf)}; ++ ++ div = p->frequency / 250; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x83; ++ buf[3] = 0xc0; ++ ++ if (p->frequency < 1531000) ++ buf[3] |= 0x04; ++ else ++ buf[3] &= ~0x04; ++ if (i2c_transfer(adapter, &msg, 1) < 0) { ++ dprintk(MANTIS_ERROR, 1, "Write: I2C Transfer failed"); ++ return -EIO; ++ } ++ msleep_interruptible(100); ++ ++ return 0; ++} ++ ++static int lgtdqcs001f_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; ++} ++ ++struct stv0299_config lgtdqcs001f_config = { ++ .demod_address = 0x68, ++ .inittab = lgtdqcs001f_inittab, ++ .mclk = 88000000UL, ++ .invert = 0, ++ .skip_reinit = 0, ++ .volt13_op0_op1 = STV0299_VOLT13_OP0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = lgtdqcs001f_set_symbol_rate, ++}; ++ ++static int vp1033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ int err = 0; ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ mantis_frontend_soft_reset(mantis); ++ msleep(250); ++ ++ dprintk(MANTIS_ERROR, 1, "Probing for STV0299 (DVB-S)"); ++ fe = dvb_attach(stv0299_attach, &lgtdqcs001f_config, adapter); ++ ++ if (fe) { ++ fe->ops.tuner_ops.set_params = lgtdqcs001f_tuner_set; ++ dprintk(MANTIS_ERROR, 1, "found STV0299 DVB-S frontend @ 0x%02x", ++ lgtdqcs001f_config.demod_address); ++ ++ dprintk(MANTIS_ERROR, 1, "Mantis DVB-S STV0299 frontend attach success"); ++ } else { ++ return -1; ++ } ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ mantis->fe = fe; ++ dprintk(MANTIS_ERROR, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp1033_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_204, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp1033_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp1033.h b/drivers/media/pci/mantis/mantis_vp1033.h +new file mode 100644 +index 0000000..7daaa1b +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1033.h +@@ -0,0 +1,30 @@ ++/* ++ Mantis VP-1033 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP1033_H ++#define __MANTIS_VP1033_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_1033_DVB_S 0x0016 ++ ++extern struct mantis_hwconfig vp1033_config; ++ ++#endif /* __MANTIS_VP1033_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp1034.c b/drivers/media/pci/mantis/mantis_vp1034.c +new file mode 100644 +index 0000000..430ae84 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1034.c +@@ -0,0 +1,120 @@ ++/* ++ Mantis VP-1034 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mb86a16.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp1034.h" ++#include "mantis_reg.h" ++ ++struct mb86a16_config vp1034_mb86a16_config = { ++ .demod_address = 0x08, ++ .set_voltage = vp1034_set_voltage, ++}; ++ ++#define MANTIS_MODEL_NAME "VP-1034" ++#define MANTIS_DEV_TYPE "DVB-S/DSS" ++ ++int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct mantis_pci *mantis = fe->dvb->priv; ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ dprintk(MANTIS_ERROR, 1, "Polarization=[13V]"); ++ mantis_gpio_set_bits(mantis, 13, 1); ++ mantis_gpio_set_bits(mantis, 14, 0); ++ break; ++ case SEC_VOLTAGE_18: ++ dprintk(MANTIS_ERROR, 1, "Polarization=[18V]"); ++ mantis_gpio_set_bits(mantis, 13, 1); ++ mantis_gpio_set_bits(mantis, 14, 1); ++ break; ++ case SEC_VOLTAGE_OFF: ++ dprintk(MANTIS_ERROR, 1, "Frontend (dummy) POWERDOWN"); ++ break; ++ default: ++ dprintk(MANTIS_ERROR, 1, "Invalid = (%d)", (u32) voltage); ++ return -EINVAL; ++ } ++ mmwrite(0x00, MANTIS_GPIF_DOUT); ++ ++ return 0; ++} ++ ++static int vp1034_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ int err = 0; ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ mantis_frontend_soft_reset(mantis); ++ msleep(250); ++ ++ dprintk(MANTIS_ERROR, 1, "Probing for MB86A16 (DVB-S/DSS)"); ++ fe = dvb_attach(mb86a16_attach, &vp1034_mb86a16_config, adapter); ++ if (fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found MB86A16 DVB-S/DSS frontend @0x%02x", ++ vp1034_mb86a16_config.demod_address); ++ ++ } else { ++ return -1; ++ } ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ mantis->fe = fe; ++ dprintk(MANTIS_ERROR, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp1034_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_204, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp1034_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp1034.h b/drivers/media/pci/mantis/mantis_vp1034.h +new file mode 100644 +index 0000000..323f38e +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1034.h +@@ -0,0 +1,33 @@ ++/* ++ Mantis VP-1034 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP1034_H ++#define __MANTIS_VP1034_H ++ ++#include "dvb_frontend.h" ++#include "mantis_common.h" ++ ++ ++#define MANTIS_VP_1034_DVB_S 0x0014 ++ ++extern struct mantis_hwconfig vp1034_config; ++extern int vp1034_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage); ++ ++#endif /* __MANTIS_VP1034_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp1041.c b/drivers/media/pci/mantis/mantis_vp1041.c +new file mode 100644 +index 0000000..07aa887 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1041.c +@@ -0,0 +1,357 @@ ++/* ++ Mantis VP-1041 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp1041.h" ++#include "stb0899_reg.h" ++#include "stb0899_drv.h" ++#include "stb0899_cfg.h" ++#include "stb6100_cfg.h" ++#include "stb6100.h" ++#include "lnbp21.h" ++ ++#define MANTIS_MODEL_NAME "VP-1041" ++#define MANTIS_DEV_TYPE "DSS/DVB-S/DVB-S2" ++ ++static const struct stb0899_s1_reg vp1041_stb0899_s1_init_1[] = { ++ ++ /* 0x0000000b, *//* SYSREG */ ++ { STB0899_DEV_ID , 0x30 }, ++ { STB0899_DISCNTRL1 , 0x32 }, ++ { STB0899_DISCNTRL2 , 0x80 }, ++ { STB0899_DISRX_ST0 , 0x04 }, ++ { STB0899_DISRX_ST1 , 0x00 }, ++ { STB0899_DISPARITY , 0x00 }, ++ { STB0899_DISSTATUS , 0x20 }, ++ { STB0899_DISF22 , 0x99 }, ++ { STB0899_DISF22RX , 0xa8 }, ++ /* SYSREG ? */ ++ { STB0899_ACRPRESC , 0x11 }, ++ { STB0899_ACRDIV1 , 0x0a }, ++ { STB0899_ACRDIV2 , 0x05 }, ++ { STB0899_DACR1 , 0x00 }, ++ { STB0899_DACR2 , 0x00 }, ++ { STB0899_OUTCFG , 0x00 }, ++ { STB0899_MODECFG , 0x00 }, ++ { STB0899_IRQSTATUS_3 , 0xfe }, ++ { STB0899_IRQSTATUS_2 , 0x03 }, ++ { STB0899_IRQSTATUS_1 , 0x7c }, ++ { STB0899_IRQSTATUS_0 , 0xf4 }, ++ { STB0899_IRQMSK_3 , 0xf3 }, ++ { STB0899_IRQMSK_2 , 0xfc }, ++ { STB0899_IRQMSK_1 , 0xff }, ++ { STB0899_IRQMSK_0 , 0xff }, ++ { STB0899_IRQCFG , 0x00 }, ++ { STB0899_I2CCFG , 0x88 }, ++ { STB0899_I2CRPT , 0x58 }, ++ { STB0899_IOPVALUE5 , 0x00 }, ++ { STB0899_IOPVALUE4 , 0x33 }, ++ { STB0899_IOPVALUE3 , 0x6d }, ++ { STB0899_IOPVALUE2 , 0x90 }, ++ { STB0899_IOPVALUE1 , 0x60 }, ++ { STB0899_IOPVALUE0 , 0x00 }, ++ { STB0899_GPIO00CFG , 0x82 }, ++ { STB0899_GPIO01CFG , 0x82 }, ++ { STB0899_GPIO02CFG , 0x82 }, ++ { STB0899_GPIO03CFG , 0x82 }, ++ { STB0899_GPIO04CFG , 0x82 }, ++ { STB0899_GPIO05CFG , 0x82 }, ++ { STB0899_GPIO06CFG , 0x82 }, ++ { STB0899_GPIO07CFG , 0x82 }, ++ { STB0899_GPIO08CFG , 0x82 }, ++ { STB0899_GPIO09CFG , 0x82 }, ++ { STB0899_GPIO10CFG , 0x82 }, ++ { STB0899_GPIO11CFG , 0x82 }, ++ { STB0899_GPIO12CFG , 0x82 }, ++ { STB0899_GPIO13CFG , 0x82 }, ++ { STB0899_GPIO14CFG , 0x82 }, ++ { STB0899_GPIO15CFG , 0x82 }, ++ { STB0899_GPIO16CFG , 0x82 }, ++ { STB0899_GPIO17CFG , 0x82 }, ++ { STB0899_GPIO18CFG , 0x82 }, ++ { STB0899_GPIO19CFG , 0x82 }, ++ { STB0899_GPIO20CFG , 0x82 }, ++ { STB0899_SDATCFG , 0xb8 }, ++ { STB0899_SCLTCFG , 0xba }, ++ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ ++ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ ++ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ ++ { STB0899_DIRCLKCFG , 0x82 }, ++ { STB0899_CLKOUT27CFG , 0x7e }, ++ { STB0899_STDBYCFG , 0x82 }, ++ { STB0899_CS0CFG , 0x82 }, ++ { STB0899_CS1CFG , 0x82 }, ++ { STB0899_DISEQCOCFG , 0x20 }, ++ { STB0899_GPIO32CFG , 0x82 }, ++ { STB0899_GPIO33CFG , 0x82 }, ++ { STB0899_GPIO34CFG , 0x82 }, ++ { STB0899_GPIO35CFG , 0x82 }, ++ { STB0899_GPIO36CFG , 0x82 }, ++ { STB0899_GPIO37CFG , 0x82 }, ++ { STB0899_GPIO38CFG , 0x82 }, ++ { STB0899_GPIO39CFG , 0x82 }, ++ { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ ++ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ ++ { STB0899_FILTCTRL , 0x00 }, ++ { STB0899_SYSCTRL , 0x01 }, ++ { STB0899_STOPCLK1 , 0x20 }, ++ { STB0899_STOPCLK2 , 0x00 }, ++ { STB0899_INTBUFSTATUS , 0x00 }, ++ { STB0899_INTBUFCTRL , 0x0a }, ++ { 0xffff , 0xff }, ++}; ++ ++static const struct stb0899_s1_reg vp1041_stb0899_s1_init_3[] = { ++ { STB0899_DEMOD , 0x00 }, ++ { STB0899_RCOMPC , 0xc9 }, ++ { STB0899_AGC1CN , 0x01 }, ++ { STB0899_AGC1REF , 0x10 }, ++ { STB0899_RTC , 0x23 }, ++ { STB0899_TMGCFG , 0x4e }, ++ { STB0899_AGC2REF , 0x34 }, ++ { STB0899_TLSR , 0x84 }, ++ { STB0899_CFD , 0xf7 }, ++ { STB0899_ACLC , 0x87 }, ++ { STB0899_BCLC , 0x94 }, ++ { STB0899_EQON , 0x41 }, ++ { STB0899_LDT , 0xf1 }, ++ { STB0899_LDT2 , 0xe3 }, ++ { STB0899_EQUALREF , 0xb4 }, ++ { STB0899_TMGRAMP , 0x10 }, ++ { STB0899_TMGTHD , 0x30 }, ++ { STB0899_IDCCOMP , 0xfd }, ++ { STB0899_QDCCOMP , 0xff }, ++ { STB0899_POWERI , 0x0c }, ++ { STB0899_POWERQ , 0x0f }, ++ { STB0899_RCOMP , 0x6c }, ++ { STB0899_AGCIQIN , 0x80 }, ++ { STB0899_AGC2I1 , 0x06 }, ++ { STB0899_AGC2I2 , 0x00 }, ++ { STB0899_TLIR , 0x30 }, ++ { STB0899_RTF , 0x7f }, ++ { STB0899_DSTATUS , 0x00 }, ++ { STB0899_LDI , 0xbc }, ++ { STB0899_CFRM , 0xea }, ++ { STB0899_CFRL , 0x31 }, ++ { STB0899_NIRM , 0x2b }, ++ { STB0899_NIRL , 0x80 }, ++ { STB0899_ISYMB , 0x1d }, ++ { STB0899_QSYMB , 0xa6 }, ++ { STB0899_SFRH , 0x2f }, ++ { STB0899_SFRM , 0x68 }, ++ { STB0899_SFRL , 0x40 }, ++ { STB0899_SFRUPH , 0x2f }, ++ { STB0899_SFRUPM , 0x68 }, ++ { STB0899_SFRUPL , 0x40 }, ++ { STB0899_EQUAI1 , 0x02 }, ++ { STB0899_EQUAQ1 , 0xff }, ++ { STB0899_EQUAI2 , 0x04 }, ++ { STB0899_EQUAQ2 , 0x05 }, ++ { STB0899_EQUAI3 , 0x02 }, ++ { STB0899_EQUAQ3 , 0xfd }, ++ { STB0899_EQUAI4 , 0x03 }, ++ { STB0899_EQUAQ4 , 0x07 }, ++ { STB0899_EQUAI5 , 0x08 }, ++ { STB0899_EQUAQ5 , 0xf5 }, ++ { STB0899_DSTATUS2 , 0x00 }, ++ { STB0899_VSTATUS , 0x00 }, ++ { STB0899_VERROR , 0x86 }, ++ { STB0899_IQSWAP , 0x2a }, ++ { STB0899_ECNT1M , 0x00 }, ++ { STB0899_ECNT1L , 0x00 }, ++ { STB0899_ECNT2M , 0x00 }, ++ { STB0899_ECNT2L , 0x00 }, ++ { STB0899_ECNT3M , 0x0a }, ++ { STB0899_ECNT3L , 0xad }, ++ { STB0899_FECAUTO1 , 0x06 }, ++ { STB0899_FECM , 0x01 }, ++ { STB0899_VTH12 , 0xb0 }, ++ { STB0899_VTH23 , 0x7a }, ++ { STB0899_VTH34 , 0x58 }, ++ { STB0899_VTH56 , 0x38 }, ++ { STB0899_VTH67 , 0x34 }, ++ { STB0899_VTH78 , 0x24 }, ++ { STB0899_PRVIT , 0xff }, ++ { STB0899_VITSYNC , 0x19 }, ++ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ ++ { STB0899_TSULC , 0x42 }, ++ { STB0899_RSLLC , 0x41 }, ++ { STB0899_TSLPL , 0x12 }, ++ { STB0899_TSCFGH , 0x0c }, ++ { STB0899_TSCFGM , 0x00 }, ++ { STB0899_TSCFGL , 0x00 }, ++ { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ ++ { STB0899_RSSYNCDEL , 0x00 }, ++ { STB0899_TSINHDELH , 0x02 }, ++ { STB0899_TSINHDELM , 0x00 }, ++ { STB0899_TSINHDELL , 0x00 }, ++ { STB0899_TSLLSTKM , 0x1b }, ++ { STB0899_TSLLSTKL , 0xb3 }, ++ { STB0899_TSULSTKM , 0x00 }, ++ { STB0899_TSULSTKL , 0x00 }, ++ { STB0899_PCKLENUL , 0xbc }, ++ { STB0899_PCKLENLL , 0xcc }, ++ { STB0899_RSPCKLEN , 0xbd }, ++ { STB0899_TSSTATUS , 0x90 }, ++ { STB0899_ERRCTRL1 , 0xb6 }, ++ { STB0899_ERRCTRL2 , 0x95 }, ++ { STB0899_ERRCTRL3 , 0x8d }, ++ { STB0899_DMONMSK1 , 0x27 }, ++ { STB0899_DMONMSK0 , 0x03 }, ++ { STB0899_DEMAPVIT , 0x5c }, ++ { STB0899_PLPARM , 0x19 }, ++ { STB0899_PDELCTRL , 0x48 }, ++ { STB0899_PDELCTRL2 , 0x00 }, ++ { STB0899_BBHCTRL1 , 0x00 }, ++ { STB0899_BBHCTRL2 , 0x00 }, ++ { STB0899_HYSTTHRESH , 0x77 }, ++ { STB0899_MATCSTM , 0x00 }, ++ { STB0899_MATCSTL , 0x00 }, ++ { STB0899_UPLCSTM , 0x00 }, ++ { STB0899_UPLCSTL , 0x00 }, ++ { STB0899_DFLCSTM , 0x00 }, ++ { STB0899_DFLCSTL , 0x00 }, ++ { STB0899_SYNCCST , 0x00 }, ++ { STB0899_SYNCDCSTM , 0x00 }, ++ { STB0899_SYNCDCSTL , 0x00 }, ++ { STB0899_ISI_ENTRY , 0x00 }, ++ { STB0899_ISI_BIT_EN , 0x00 }, ++ { STB0899_MATSTRM , 0xf0 }, ++ { STB0899_MATSTRL , 0x02 }, ++ { STB0899_UPLSTRM , 0x45 }, ++ { STB0899_UPLSTRL , 0x60 }, ++ { STB0899_DFLSTRM , 0xe3 }, ++ { STB0899_DFLSTRL , 0x00 }, ++ { STB0899_SYNCSTR , 0x47 }, ++ { STB0899_SYNCDSTRM , 0x05 }, ++ { STB0899_SYNCDSTRL , 0x18 }, ++ { STB0899_CFGPDELSTATUS1 , 0x19 }, ++ { STB0899_CFGPDELSTATUS2 , 0x2b }, ++ { STB0899_BBFERRORM , 0x00 }, ++ { STB0899_BBFERRORL , 0x01 }, ++ { STB0899_UPKTERRORM , 0x00 }, ++ { STB0899_UPKTERRORL , 0x00 }, ++ { 0xffff , 0xff }, ++}; ++ ++struct stb0899_config vp1041_stb0899_config = { ++ .init_dev = vp1041_stb0899_s1_init_1, ++ .init_s2_demod = stb0899_s2_init_2, ++ .init_s1_demod = vp1041_stb0899_s1_init_3, ++ .init_s2_fec = stb0899_s2_init_4, ++ .init_tst = stb0899_s1_init_5, ++ ++ .demod_address = 0x68, /* 0xd0 >> 1 */ ++ ++ .xtal_freq = 27000000, ++ .inversion = IQ_SWAP_ON, /* 1 */ ++ ++ .lo_clk = 76500000, ++ .hi_clk = 99000000, ++ ++ .esno_ave = STB0899_DVBS2_ESNO_AVE, ++ .esno_quant = STB0899_DVBS2_ESNO_QUANT, ++ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, ++ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, ++ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, ++ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, ++ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, ++ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, ++ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, ++ ++ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, ++ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, ++ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, ++ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, ++ ++ .tuner_get_frequency = stb6100_get_frequency, ++ .tuner_set_frequency = stb6100_set_frequency, ++ .tuner_set_bandwidth = stb6100_set_bandwidth, ++ .tuner_get_bandwidth = stb6100_get_bandwidth, ++ .tuner_set_rfsiggain = NULL, ++}; ++ ++struct stb6100_config vp1041_stb6100_config = { ++ .tuner_address = 0x60, ++ .refclock = 27000000, ++}; ++ ++static int vp1041_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ int err = 0; ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ mantis_frontend_soft_reset(mantis); ++ msleep(250); ++ mantis->fe = dvb_attach(stb0899_attach, &vp1041_stb0899_config, adapter); ++ if (mantis->fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found STB0899 DVB-S/DVB-S2 frontend @0x%02x", ++ vp1041_stb0899_config.demod_address); ++ ++ if (dvb_attach(stb6100_attach, mantis->fe, &vp1041_stb6100_config, adapter)) { ++ if (!dvb_attach(lnbp21_attach, mantis->fe, adapter, 0, 0)) ++ dprintk(MANTIS_ERROR, 1, "No LNBP21 found!"); ++ } ++ } else { ++ return -EREMOTEIO; ++ } ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ ++ ++ dprintk(MANTIS_ERROR, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp1041_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_188, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp1041_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp1041.h b/drivers/media/pci/mantis/mantis_vp1041.h +new file mode 100644 +index 0000000..1ae5b3d +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp1041.h +@@ -0,0 +1,33 @@ ++/* ++ Mantis VP-1041 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP1041_H ++#define __MANTIS_VP1041_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_1041_DVB_S2 0x0031 ++#define SKYSTAR_HD2_10 0x0001 ++#define SKYSTAR_HD2_20 0x0003 ++#define CINERGY_S2_PCI_HD 0x1179 ++ ++extern struct mantis_hwconfig vp1041_config; ++ ++#endif /* __MANTIS_VP1041_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp2033.c b/drivers/media/pci/mantis/mantis_vp2033.c +new file mode 100644 +index 0000000..1ca6837 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp2033.c +@@ -0,0 +1,188 @@ ++/* ++ Mantis VP-2033 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "tda1002x.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp2033.h" ++ ++#define MANTIS_MODEL_NAME "VP-2033" ++#define MANTIS_DEV_TYPE "DVB-C" ++ ++struct tda1002x_config vp2033_tda1002x_cu1216_config = { ++ .demod_address = 0x18 >> 1, ++ .invert = 1, ++}; ++ ++struct tda10023_config vp2033_tda10023_cu1216_config = { ++ .demod_address = 0x18 >> 1, ++ .invert = 1, ++}; ++ ++static u8 read_pwm(struct mantis_pci *mantis) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ u8 b = 0xff; ++ u8 pwm; ++ struct i2c_msg msg[] = { ++ {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, ++ {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} ++ }; ++ ++ if ((i2c_transfer(adapter, msg, 2) != 2) ++ || (pwm == 0xff)) ++ pwm = 0x48; ++ ++ return pwm; ++} ++ ++static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mantis_pci *mantis = fe->dvb->priv; ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ u8 buf[6]; ++ struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; ++ int i; ++ ++#define CU1216_IF 36125000 ++#define TUNER_MUL 62500 ++ ++ u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0xce; ++ buf[3] = (p->frequency < 150000000 ? 0x01 : ++ p->frequency < 445000000 ? 0x02 : 0x04); ++ buf[4] = 0xde; ++ buf[5] = 0x20; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) != 1) ++ return -EIO; ++ ++ /* wait for the pll lock */ ++ msg.flags = I2C_M_RD; ++ msg.len = 1; ++ for (i = 0; i < 20; i++) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) ++ break; ++ ++ msleep(10); ++ } ++ ++ /* switch the charge pump to the lower current */ ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = &buf[2]; ++ buf[2] &= ~0x40; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int vp2033_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ int err = 0; ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ mantis_frontend_soft_reset(mantis); ++ msleep(250); ++ ++ dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); ++ fe = dvb_attach(tda10021_attach, &vp2033_tda1002x_cu1216_config, ++ adapter, ++ read_pwm(mantis)); ++ ++ if (fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", ++ vp2033_tda1002x_cu1216_config.demod_address); ++ } else { ++ fe = dvb_attach(tda10023_attach, &vp2033_tda10023_cu1216_config, ++ adapter, ++ read_pwm(mantis)); ++ ++ if (fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", ++ vp2033_tda1002x_cu1216_config.demod_address); ++ } ++ } ++ ++ if (fe) { ++ fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; ++ dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); ++ } else { ++ return -1; ++ } ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ ++ mantis->fe = fe; ++ dprintk(MANTIS_DEBUG, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp2033_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_204, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp2033_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp2033.h b/drivers/media/pci/mantis/mantis_vp2033.h +new file mode 100644 +index 0000000..c55242b +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp2033.h +@@ -0,0 +1,30 @@ ++/* ++ Mantis VP-2033 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP2033_H ++#define __MANTIS_VP2033_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_2033_DVB_C 0x0008 ++ ++extern struct mantis_hwconfig vp2033_config; ++ ++#endif /* __MANTIS_VP2033_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp2040.c b/drivers/media/pci/mantis/mantis_vp2040.c +new file mode 100644 +index 0000000..d480741 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp2040.c +@@ -0,0 +1,187 @@ ++/* ++ Mantis VP-2040 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "tda1002x.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp2040.h" ++ ++#define MANTIS_MODEL_NAME "VP-2040" ++#define MANTIS_DEV_TYPE "DVB-C" ++ ++struct tda1002x_config vp2040_tda1002x_cu1216_config = { ++ .demod_address = 0x18 >> 1, ++ .invert = 1, ++}; ++ ++struct tda10023_config vp2040_tda10023_cu1216_config = { ++ .demod_address = 0x18 >> 1, ++ .invert = 1, ++}; ++ ++static int tda1002x_cu1216_tuner_set(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct mantis_pci *mantis = fe->dvb->priv; ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ u8 buf[6]; ++ struct i2c_msg msg = {.addr = 0x60, .flags = 0, .buf = buf, .len = sizeof(buf)}; ++ int i; ++ ++#define CU1216_IF 36125000 ++#define TUNER_MUL 62500 ++ ++ u32 div = (p->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0xce; ++ buf[3] = (p->frequency < 150000000 ? 0x01 : ++ p->frequency < 445000000 ? 0x02 : 0x04); ++ buf[4] = 0xde; ++ buf[5] = 0x20; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) != 1) ++ return -EIO; ++ ++ /* wait for the pll lock */ ++ msg.flags = I2C_M_RD; ++ msg.len = 1; ++ for (i = 0; i < 20; i++) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) == 1 && (buf[0] & 0x40)) ++ break; ++ ++ msleep(10); ++ } ++ ++ /* switch the charge pump to the lower current */ ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = &buf[2]; ++ buf[2] &= ~0x40; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ++ if (i2c_transfer(adapter, &msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static u8 read_pwm(struct mantis_pci *mantis) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ u8 b = 0xff; ++ u8 pwm; ++ struct i2c_msg msg[] = { ++ {.addr = 0x50, .flags = 0, .buf = &b, .len = 1}, ++ {.addr = 0x50, .flags = I2C_M_RD, .buf = &pwm, .len = 1} ++ }; ++ ++ if ((i2c_transfer(adapter, msg, 2) != 2) ++ || (pwm == 0xff)) ++ pwm = 0x48; ++ ++ return pwm; ++} ++ ++static int vp2040_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ ++ int err = 0; ++ ++ err = mantis_frontend_power(mantis, POWER_ON); ++ if (err == 0) { ++ mantis_frontend_soft_reset(mantis); ++ msleep(250); ++ ++ dprintk(MANTIS_ERROR, 1, "Probing for CU1216 (DVB-C)"); ++ fe = dvb_attach(tda10021_attach, &vp2040_tda1002x_cu1216_config, ++ adapter, ++ read_pwm(mantis)); ++ ++ if (fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found Philips CU1216 DVB-C frontend (TDA10021) @ 0x%02x", ++ vp2040_tda1002x_cu1216_config.demod_address); ++ } else { ++ fe = dvb_attach(tda10023_attach, &vp2040_tda10023_cu1216_config, ++ adapter, ++ read_pwm(mantis)); ++ ++ if (fe) { ++ dprintk(MANTIS_ERROR, 1, ++ "found Philips CU1216 DVB-C frontend (TDA10023) @ 0x%02x", ++ vp2040_tda1002x_cu1216_config.demod_address); ++ } ++ } ++ ++ if (fe) { ++ fe->ops.tuner_ops.set_params = tda1002x_cu1216_tuner_set; ++ dprintk(MANTIS_ERROR, 1, "Mantis DVB-C Philips CU1216 frontend attach success"); ++ } else { ++ return -1; ++ } ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ } ++ mantis->fe = fe; ++ dprintk(MANTIS_DEBUG, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp2040_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_204, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp2040_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp2040.h b/drivers/media/pci/mantis/mantis_vp2040.h +new file mode 100644 +index 0000000..d125e21 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp2040.h +@@ -0,0 +1,32 @@ ++/* ++ Mantis VP-2040 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP2040_H ++#define __MANTIS_VP2040_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_2040_DVB_C 0x0043 ++#define CINERGY_C 0x1178 ++#define CABLESTAR_HD2 0x0002 ++ ++extern struct mantis_hwconfig vp2040_config; ++ ++#endif /* __MANTIS_VP2040_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp3028.c b/drivers/media/pci/mantis/mantis_vp3028.c +new file mode 100644 +index 0000000..4155c83 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp3028.c +@@ -0,0 +1,38 @@ ++/* ++ Mantis VP-3028 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ 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 "mantis_common.h" ++#include "mantis_vp3028.h" ++ ++struct zl10353_config mantis_vp3028_config = { ++ .demod_address = 0x0f, ++}; ++ ++#define MANTIS_MODEL_NAME "VP-3028" ++#define MANTIS_DEV_TYPE "DVB-T" ++ ++struct mantis_hwconfig vp3028_mantis_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_188, ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp3028.h b/drivers/media/pci/mantis/mantis_vp3028.h +new file mode 100644 +index 0000000..b07be6a +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp3028.h +@@ -0,0 +1,33 @@ ++/* ++ Mantis VP-3028 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP3028_H ++#define __MANTIS_VP3028_H ++ ++#include "dvb_frontend.h" ++#include "mantis_common.h" ++#include "zl10353.h" ++ ++#define MANTIS_VP_3028_DVB_T 0x0028 ++ ++extern struct zl10353_config mantis_vp3028_config; ++extern struct mantis_hwconfig vp3028_mantis_config; ++ ++#endif /* __MANTIS_VP3028_H */ +diff --git a/drivers/media/pci/mantis/mantis_vp3030.c b/drivers/media/pci/mantis/mantis_vp3030.c +new file mode 100644 +index 0000000..c09308c +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp3030.c +@@ -0,0 +1,105 @@ ++/* ++ Mantis VP-3030 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#include ++#include ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++ ++#include "zl10353.h" ++#include "tda665x.h" ++#include "mantis_common.h" ++#include "mantis_ioc.h" ++#include "mantis_dvb.h" ++#include "mantis_vp3030.h" ++ ++struct zl10353_config mantis_vp3030_config = { ++ .demod_address = 0x0f, ++}; ++ ++struct tda665x_config env57h12d5_config = { ++ .name = "ENV57H12D5 (ET-50DT)", ++ .addr = 0x60, ++ .frequency_min = 47000000, ++ .frequency_max = 862000000, ++ .frequency_offst = 3616667, ++ .ref_multiplier = 6, /* 1/6 MHz */ ++ .ref_divider = 100000, /* 1/6 MHz */ ++}; ++ ++#define MANTIS_MODEL_NAME "VP-3030" ++#define MANTIS_DEV_TYPE "DVB-T" ++ ++ ++static int vp3030_frontend_init(struct mantis_pci *mantis, struct dvb_frontend *fe) ++{ ++ struct i2c_adapter *adapter = &mantis->adapter; ++ struct mantis_hwconfig *config = mantis->hwconfig; ++ int err = 0; ++ ++ mantis_gpio_set_bits(mantis, config->reset, 0); ++ msleep(100); ++ err = mantis_frontend_power(mantis, POWER_ON); ++ msleep(100); ++ mantis_gpio_set_bits(mantis, config->reset, 1); ++ ++ if (err == 0) { ++ msleep(250); ++ dprintk(MANTIS_ERROR, 1, "Probing for 10353 (DVB-T)"); ++ fe = dvb_attach(zl10353_attach, &mantis_vp3030_config, adapter); ++ ++ if (!fe) ++ return -1; ++ ++ dvb_attach(tda665x_attach, fe, &env57h12d5_config, adapter); ++ } else { ++ dprintk(MANTIS_ERROR, 1, "Frontend on <%s> POWER ON failed! <%d>", ++ adapter->name, ++ err); ++ ++ return -EIO; ++ ++ } ++ mantis->fe = fe; ++ dprintk(MANTIS_ERROR, 1, "Done!"); ++ ++ return 0; ++} ++ ++struct mantis_hwconfig vp3030_config = { ++ .model_name = MANTIS_MODEL_NAME, ++ .dev_type = MANTIS_DEV_TYPE, ++ .ts_size = MANTIS_TS_188, ++ ++ .baud_rate = MANTIS_BAUD_9600, ++ .parity = MANTIS_PARITY_NONE, ++ .bytes = 0, ++ ++ .frontend_init = vp3030_frontend_init, ++ .power = GPIF_A12, ++ .reset = GPIF_A13, ++ ++ .i2c_mode = MANTIS_BYTE_MODE ++}; +diff --git a/drivers/media/pci/mantis/mantis_vp3030.h b/drivers/media/pci/mantis/mantis_vp3030.h +new file mode 100644 +index 0000000..5f12c42 +--- /dev/null ++++ b/drivers/media/pci/mantis/mantis_vp3030.h +@@ -0,0 +1,30 @@ ++/* ++ Mantis VP-3030 driver ++ ++ Copyright (C) Manu Abraham (abraham.manu@gmail.com) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++*/ ++ ++#ifndef __MANTIS_VP3030_H ++#define __MANTIS_VP3030_H ++ ++#include "mantis_common.h" ++ ++#define MANTIS_VP_3030_DVB_T 0x0024 ++ ++extern struct mantis_hwconfig vp3030_config; ++ ++#endif /* __MANTIS_VP3030_H */ +diff --git a/drivers/media/pci/meye/Kconfig b/drivers/media/pci/meye/Kconfig +new file mode 100644 +index 0000000..b4bf848 +--- /dev/null ++++ b/drivers/media/pci/meye/Kconfig +@@ -0,0 +1,13 @@ ++config VIDEO_MEYE ++ tristate "Sony Vaio Picturebook Motion Eye Video For Linux" ++ depends on PCI && SONY_LAPTOP && VIDEO_V4L2 ++ ---help--- ++ This is the video4linux driver for the Motion Eye camera found ++ in the Vaio Picturebook laptops. Please read the material in ++ for more information. ++ ++ If you say Y or M here, you need to say Y or M to "Sony Laptop ++ Extras" in the misc device section. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called meye. +diff --git a/drivers/media/pci/meye/Makefile b/drivers/media/pci/meye/Makefile +new file mode 100644 +index 0000000..4938851 +--- /dev/null ++++ b/drivers/media/pci/meye/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_VIDEO_MEYE) += meye.o +diff --git a/drivers/media/pci/meye/meye.c b/drivers/media/pci/meye/meye.c +new file mode 100644 +index 0000000..ac7ab6e +--- /dev/null ++++ b/drivers/media/pci/meye/meye.c +@@ -0,0 +1,1964 @@ ++/* ++ * Motion Eye video4linux driver for Sony Vaio PictureBook ++ * ++ * Copyright (C) 2001-2004 Stelian Pop ++ * ++ * Copyright (C) 2001-2002 Alcôve ++ * ++ * Copyright (C) 2000 Andrew Tridgell ++ * ++ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. ++ * ++ * Some parts borrowed from various video4linux drivers, especially ++ * bttv-driver.c and zoran.c, see original files for credits. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "meye.h" ++#include ++ ++MODULE_AUTHOR("Stelian Pop "); ++MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(MEYE_DRIVER_VERSION); ++ ++/* number of grab buffers */ ++static unsigned int gbuffers = 2; ++module_param(gbuffers, int, 0444); ++MODULE_PARM_DESC(gbuffers, "number of capture buffers, default is 2 (32 max)"); ++ ++/* size of a grab buffer */ ++static unsigned int gbufsize = MEYE_MAX_BUFSIZE; ++module_param(gbufsize, int, 0444); ++MODULE_PARM_DESC(gbufsize, "size of the capture buffers, default is 614400" ++ " (will be rounded up to a page multiple)"); ++ ++/* /dev/videoX registration number */ ++static int video_nr = -1; ++module_param(video_nr, int, 0444); ++MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); ++ ++/* driver structure - only one possible */ ++static struct meye meye; ++ ++/****************************************************************************/ ++/* Memory allocation routines (stolen from bttv-driver.c) */ ++/****************************************************************************/ ++static void *rvmalloc(unsigned long size) ++{ ++ void *mem; ++ unsigned long adr; ++ ++ size = PAGE_ALIGN(size); ++ mem = vmalloc_32(size); ++ if (mem) { ++ memset(mem, 0, size); ++ adr = (unsigned long) mem; ++ while (size > 0) { ++ SetPageReserved(vmalloc_to_page((void *)adr)); ++ adr += PAGE_SIZE; ++ size -= PAGE_SIZE; ++ } ++ } ++ return mem; ++} ++ ++static void rvfree(void * mem, unsigned long size) ++{ ++ unsigned long adr; ++ ++ if (mem) { ++ adr = (unsigned long) mem; ++ while ((long) size > 0) { ++ ClearPageReserved(vmalloc_to_page((void *)adr)); ++ adr += PAGE_SIZE; ++ size -= PAGE_SIZE; ++ } ++ vfree(mem); ++ } ++} ++ ++/* ++ * return a page table pointing to N pages of locked memory ++ * ++ * NOTE: The meye device expects DMA addresses on 32 bits, we build ++ * a table of 1024 entries = 4 bytes * 1024 = 4096 bytes. ++ */ ++static int ptable_alloc(void) ++{ ++ u32 *pt; ++ int i; ++ ++ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); ++ ++ /* give only 32 bit DMA addresses */ ++ if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32))) ++ return -1; ++ ++ meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ &meye.mchip_dmahandle, ++ GFP_KERNEL); ++ if (!meye.mchip_ptable_toc) { ++ meye.mchip_dmahandle = 0; ++ return -1; ++ } ++ ++ pt = meye.mchip_ptable_toc; ++ for (i = 0; i < MCHIP_NB_PAGES; i++) { ++ dma_addr_t dma; ++ meye.mchip_ptable[i] = dma_alloc_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ &dma, ++ GFP_KERNEL); ++ if (!meye.mchip_ptable[i]) { ++ int j; ++ pt = meye.mchip_ptable_toc; ++ for (j = 0; j < i; ++j) { ++ dma = (dma_addr_t) *pt; ++ dma_free_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ meye.mchip_ptable[j], dma); ++ pt++; ++ } ++ dma_free_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ meye.mchip_ptable_toc, ++ meye.mchip_dmahandle); ++ meye.mchip_ptable_toc = NULL; ++ meye.mchip_dmahandle = 0; ++ return -1; ++ } ++ *pt = (u32) dma; ++ pt++; ++ } ++ return 0; ++} ++ ++static void ptable_free(void) ++{ ++ u32 *pt; ++ int i; ++ ++ pt = meye.mchip_ptable_toc; ++ for (i = 0; i < MCHIP_NB_PAGES; i++) { ++ dma_addr_t dma = (dma_addr_t) *pt; ++ if (meye.mchip_ptable[i]) ++ dma_free_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ meye.mchip_ptable[i], dma); ++ pt++; ++ } ++ ++ if (meye.mchip_ptable_toc) ++ dma_free_coherent(&meye.mchip_dev->dev, ++ PAGE_SIZE, ++ meye.mchip_ptable_toc, ++ meye.mchip_dmahandle); ++ ++ memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); ++ meye.mchip_ptable_toc = NULL; ++ meye.mchip_dmahandle = 0; ++} ++ ++/* copy data from ptable into buf */ ++static void ptable_copy(u8 *buf, int start, int size, int pt_pages) ++{ ++ int i; ++ ++ for (i = 0; i < (size / PAGE_SIZE) * PAGE_SIZE; i += PAGE_SIZE) { ++ memcpy(buf + i, meye.mchip_ptable[start++], PAGE_SIZE); ++ if (start >= pt_pages) ++ start = 0; ++ } ++ memcpy(buf + i, meye.mchip_ptable[start], size % PAGE_SIZE); ++} ++ ++/****************************************************************************/ ++/* JPEG tables at different qualities to load into the VRJ chip */ ++/****************************************************************************/ ++ ++/* return a set of quantisation tables based on a quality from 1 to 10 */ ++static u16 *jpeg_quantisation_tables(int *length, int quality) ++{ ++ static u16 jpeg_tables[][70] = { { ++ 0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, ++ 0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, ++ }, ++ { ++ 0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, ++ 0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, ++ 0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, ++ 0xffff, 0xffff, 0xffff, ++ }, ++ { ++ 0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, ++ 0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, ++ 0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, ++ 0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, ++ 0xe6ff, 0xfffd, 0xfff8, ++ 0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, ++ 0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, ++ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, ++ 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, ++ 0xf8f8, 0xf8f8, 0xfff8, ++ }, ++ { ++ 0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, ++ 0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, ++ 0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, ++ 0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, ++ 0x99c7, 0xaba8, 0xffa4, ++ 0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, ++ 0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, ++ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, ++ 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, ++ 0xa4a4, 0xa4a4, 0xffa4, ++ }, ++ { ++ 0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, ++ 0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, ++ 0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, ++ 0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, ++ 0x7396, 0x817e, 0xff7c, ++ 0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, ++ 0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, ++ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, ++ 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, ++ 0x7c7c, 0x7c7c, 0xff7c, ++ }, ++ { ++ 0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, ++ 0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, ++ 0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, ++ 0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, ++ 0x5c78, 0x6765, 0xff63, ++ 0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, ++ 0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, ++ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, ++ 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, ++ 0x6363, 0x6363, 0xff63, ++ }, ++ { ++ 0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, ++ 0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, ++ 0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, ++ 0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, ++ 0x4a60, 0x5251, 0xff4f, ++ 0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, ++ 0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, ++ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, ++ 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, ++ 0x4f4f, 0x4f4f, 0xff4f, ++ }, ++ { ++ 0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, ++ 0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, ++ 0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, ++ 0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, ++ 0x3748, 0x3e3d, 0xff3b, ++ 0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, ++ 0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, ++ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, ++ 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, ++ 0x3b3b, 0x3b3b, 0xff3b, ++ }, ++ { ++ 0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, ++ 0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, ++ 0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, ++ 0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, ++ 0x2530, 0x2928, 0xff28, ++ 0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, ++ 0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, ++ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, ++ 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, ++ 0x2828, 0x2828, 0xff28, ++ }, ++ { ++ 0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, ++ 0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, ++ 0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, ++ 0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, ++ 0x1218, 0x1514, 0xff14, ++ 0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, ++ 0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, ++ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, ++ 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, ++ 0x1414, 0x1414, 0xff14, ++ }, ++ { ++ 0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0xff01, ++ 0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0101, 0x0101, 0xff01, ++ } }; ++ ++ if (quality < 0 || quality > 10) { ++ printk(KERN_WARNING ++ "meye: invalid quality level %d - using 8\n", quality); ++ quality = 8; ++ } ++ ++ *length = ARRAY_SIZE(jpeg_tables[quality]); ++ return jpeg_tables[quality]; ++} ++ ++/* return a generic set of huffman tables */ ++static u16 *jpeg_huffman_tables(int *length) ++{ ++ static u16 tables[] = { ++ 0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, ++ 0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, ++ 0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, ++ 0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, ++ 0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, ++ 0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, ++ 0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, ++ 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, ++ 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, ++ 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, ++ 0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, ++ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, ++ 0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, ++ 0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, ++ 0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, ++ 0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, ++ 0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, ++ 0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, ++ 0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, ++ 0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, ++ 0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, ++ 0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, ++ 0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, ++ 0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, ++ 0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, ++ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, ++ 0xFF0B, ++ 0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, ++ 0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, ++ 0xFF0B ++ }; ++ ++ *length = ARRAY_SIZE(tables); ++ return tables; ++} ++ ++/****************************************************************************/ ++/* MCHIP low-level functions */ ++/****************************************************************************/ ++ ++/* returns the horizontal capture size */ ++static inline int mchip_hsize(void) ++{ ++ return meye.params.subsample ? 320 : 640; ++} ++ ++/* returns the vertical capture size */ ++static inline int mchip_vsize(void) ++{ ++ return meye.params.subsample ? 240 : 480; ++} ++ ++/* waits for a register to be available */ ++static void mchip_sync(int reg) ++{ ++ u32 status; ++ int i; ++ ++ if (reg == MCHIP_MM_FIFO_DATA) { ++ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { ++ status = readl(meye.mchip_mmregs + ++ MCHIP_MM_FIFO_STATUS); ++ if (!(status & MCHIP_MM_FIFO_WAIT)) { ++ printk(KERN_WARNING "meye: fifo not ready\n"); ++ return; ++ } ++ if (status & MCHIP_MM_FIFO_READY) ++ return; ++ udelay(1); ++ } ++ } else if (reg > 0x80) { ++ u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY ++ : MCHIP_HIC_STATUS_VRJ_RDY; ++ for (i = 0; i < MCHIP_REG_TIMEOUT; i++) { ++ status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS); ++ if (status & mask) ++ return; ++ udelay(1); ++ } ++ } else ++ return; ++ printk(KERN_WARNING ++ "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", ++ reg, status); ++} ++ ++/* sets a value into the register */ ++static inline void mchip_set(int reg, u32 v) ++{ ++ mchip_sync(reg); ++ writel(v, meye.mchip_mmregs + reg); ++} ++ ++/* get the register value */ ++static inline u32 mchip_read(int reg) ++{ ++ mchip_sync(reg); ++ return readl(meye.mchip_mmregs + reg); ++} ++ ++/* wait for a register to become a particular value */ ++static inline int mchip_delay(u32 reg, u32 v) ++{ ++ int n = 10; ++ while (--n && mchip_read(reg) != v) ++ udelay(1); ++ return n; ++} ++ ++/* setup subsampling */ ++static void mchip_subsample(void) ++{ ++ mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample); ++ mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize()); ++ mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize()); ++ mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize()); ++ mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize()); ++ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); ++} ++ ++/* set the framerate into the mchip */ ++static void mchip_set_framerate(void) ++{ ++ mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate); ++} ++ ++/* load some huffman and quantisation tables into the VRJ chip ready ++ for JPEG compression */ ++static void mchip_load_tables(void) ++{ ++ int i; ++ int length; ++ u16 *tables; ++ ++ tables = jpeg_huffman_tables(&length); ++ for (i = 0; i < length; i++) ++ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); ++ ++ tables = jpeg_quantisation_tables(&length, meye.params.quality); ++ for (i = 0; i < length; i++) ++ writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA); ++} ++ ++/* setup the VRJ parameters in the chip */ ++static void mchip_vrj_setup(u8 mode) ++{ ++ mchip_set(MCHIP_VRJ_BUS_MODE, 5); ++ mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f); ++ mchip_set(MCHIP_VRJ_PDAT_USE, 1); ++ mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0); ++ mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode); ++ mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize()); ++ mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize()); ++ mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b); ++ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF); ++ mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF); ++ mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC); ++ mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0); ++ mchip_set(MCHIP_VRJ_SOF1, 0x601); ++ mchip_set(MCHIP_VRJ_SOF2, 0x1502); ++ mchip_set(MCHIP_VRJ_SOF3, 0x1503); ++ mchip_set(MCHIP_VRJ_SOF4, 0x1596); ++ mchip_set(MCHIP_VRJ_SOS, 0x0ed0); ++ ++ mchip_load_tables(); ++} ++ ++/* sets the DMA parameters into the chip */ ++static void mchip_dma_setup(dma_addr_t dma_addr) ++{ ++ int i; ++ ++ mchip_set(MCHIP_MM_PT_ADDR, (u32)dma_addr); ++ for (i = 0; i < 4; i++) ++ mchip_set(MCHIP_MM_FIR(i), 0); ++ meye.mchip_fnum = 0; ++} ++ ++/* setup for DMA transfers - also zeros the framebuffer */ ++static int mchip_dma_alloc(void) ++{ ++ if (!meye.mchip_dmahandle) ++ if (ptable_alloc()) ++ return -1; ++ return 0; ++} ++ ++/* frees the DMA buffer */ ++static void mchip_dma_free(void) ++{ ++ if (meye.mchip_dmahandle) { ++ mchip_dma_setup(0); ++ ptable_free(); ++ } ++} ++ ++/* stop any existing HIC action and wait for any dma to complete then ++ reset the dma engine */ ++static void mchip_hic_stop(void) ++{ ++ int i, j; ++ ++ meye.mchip_mode = MCHIP_HIC_MODE_NOOP; ++ if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) ++ return; ++ for (i = 0; i < 20; ++i) { ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP); ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ for (j = 0; j < 100; ++j) { ++ if (mchip_delay(MCHIP_HIC_STATUS, ++ MCHIP_HIC_STATUS_IDLE)) ++ return; ++ msleep(1); ++ } ++ printk(KERN_ERR "meye: need to reset HIC!\n"); ++ ++ mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET); ++ msleep(250); ++ } ++ printk(KERN_ERR "meye: resetting HIC hanged!\n"); ++} ++ ++/****************************************************************************/ ++/* MCHIP frame processing functions */ ++/****************************************************************************/ ++ ++/* get the next ready frame from the dma engine */ ++static u32 mchip_get_frame(void) ++{ ++ u32 v; ++ ++ v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum)); ++ return v; ++} ++ ++/* frees the current frame from the dma engine */ ++static void mchip_free_frame(void) ++{ ++ mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0); ++ meye.mchip_fnum++; ++ meye.mchip_fnum %= 4; ++} ++ ++/* read one frame from the framebuffer assuming it was captured using ++ a uncompressed transfer */ ++static void mchip_cont_read_frame(u32 v, u8 *buf, int size) ++{ ++ int pt_id; ++ ++ pt_id = (v >> 17) & 0x3FF; ++ ++ ptable_copy(buf, pt_id, size, MCHIP_NB_PAGES); ++} ++ ++/* read a compressed frame from the framebuffer */ ++static int mchip_comp_read_frame(u32 v, u8 *buf, int size) ++{ ++ int pt_start, pt_end, trailer; ++ int fsize; ++ int i; ++ ++ pt_start = (v >> 19) & 0xFF; ++ pt_end = (v >> 11) & 0xFF; ++ trailer = (v >> 1) & 0x3FF; ++ ++ if (pt_end < pt_start) ++ fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE + ++ pt_end * PAGE_SIZE + trailer * 4; ++ else ++ fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4; ++ ++ if (fsize > size) { ++ printk(KERN_WARNING "meye: oversized compressed frame %d\n", ++ fsize); ++ return -1; ++ } ++ ++ ptable_copy(buf, pt_start, fsize, MCHIP_NB_PAGES_MJPEG); ++ ++#ifdef MEYE_JPEG_CORRECTION ++ ++ /* Some mchip generated jpeg frames are incorrect. In most ++ * (all ?) of those cases, the final EOI (0xff 0xd9) marker ++ * is not present at the end of the frame. ++ * ++ * Since adding the final marker is not enough to restore ++ * the jpeg integrity, we drop the frame. ++ */ ++ ++ for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ; ++ ++ if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9) ++ return -1; ++ ++#endif ++ ++ return fsize; ++} ++ ++/* take a picture into SDRAM */ ++static void mchip_take_picture(void) ++{ ++ int i; ++ ++ mchip_hic_stop(); ++ mchip_subsample(); ++ mchip_dma_setup(meye.mchip_dmahandle); ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ ++ for (i = 0; i < 100; ++i) { ++ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) ++ break; ++ msleep(1); ++ } ++} ++ ++/* dma a previously taken picture into a buffer */ ++static void mchip_get_picture(u8 *buf, int bufsize) ++{ ++ u32 v; ++ int i; ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ for (i = 0; i < 100; ++i) { ++ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) ++ break; ++ msleep(1); ++ } ++ for (i = 0; i < 4; ++i) { ++ v = mchip_get_frame(); ++ if (v & MCHIP_MM_FIR_RDY) { ++ mchip_cont_read_frame(v, buf, bufsize); ++ break; ++ } ++ mchip_free_frame(); ++ } ++} ++ ++/* start continuous dma capture */ ++static void mchip_continuous_start(void) ++{ ++ mchip_hic_stop(); ++ mchip_subsample(); ++ mchip_set_framerate(); ++ mchip_dma_setup(meye.mchip_dmahandle); ++ ++ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++} ++ ++/* compress one frame into a buffer */ ++static int mchip_compress_frame(u8 *buf, int bufsize) ++{ ++ u32 v; ++ int len = -1, i; ++ ++ mchip_vrj_setup(0x3f); ++ udelay(50); ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ for (i = 0; i < 100; ++i) { ++ if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) ++ break; ++ msleep(1); ++ } ++ ++ for (i = 0; i < 4; ++i) { ++ v = mchip_get_frame(); ++ if (v & MCHIP_MM_FIR_RDY) { ++ len = mchip_comp_read_frame(v, buf, bufsize); ++ break; ++ } ++ mchip_free_frame(); ++ } ++ return len; ++} ++ ++#if 0 ++/* uncompress one image into a buffer */ ++static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) ++{ ++ mchip_vrj_setup(0x3f); ++ udelay(50); ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ ++ return mchip_comp_read_frame(buf, bufsize); ++} ++#endif ++ ++/* start continuous compressed capture */ ++static void mchip_cont_compression_start(void) ++{ ++ mchip_hic_stop(); ++ mchip_vrj_setup(0x3f); ++ mchip_subsample(); ++ mchip_set_framerate(); ++ mchip_dma_setup(meye.mchip_dmahandle); ++ ++ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; ++ ++ mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP); ++ mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++} ++ ++/****************************************************************************/ ++/* Interrupt handling */ ++/****************************************************************************/ ++ ++static irqreturn_t meye_irq(int irq, void *dev_id) ++{ ++ u32 v; ++ int reqnr; ++ static int sequence; ++ ++ v = mchip_read(MCHIP_MM_INTA); ++ ++ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT && ++ meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) ++ return IRQ_NONE; ++ ++again: ++ v = mchip_get_frame(); ++ if (!(v & MCHIP_MM_FIR_RDY)) ++ return IRQ_HANDLED; ++ ++ if (meye.mchip_mode == MCHIP_HIC_MODE_CONT_OUT) { ++ if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, ++ sizeof(int), &meye.grabq_lock) != sizeof(int)) { ++ mchip_free_frame(); ++ return IRQ_HANDLED; ++ } ++ mchip_cont_read_frame(v, meye.grab_fbuffer + gbufsize * reqnr, ++ mchip_hsize() * mchip_vsize() * 2); ++ meye.grab_buffer[reqnr].size = mchip_hsize() * mchip_vsize() * 2; ++ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; ++ v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); ++ meye.grab_buffer[reqnr].sequence = sequence++; ++ kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, ++ sizeof(int), &meye.doneq_lock); ++ wake_up_interruptible(&meye.proc_list); ++ } else { ++ int size; ++ size = mchip_comp_read_frame(v, meye.grab_temp, gbufsize); ++ if (size == -1) { ++ mchip_free_frame(); ++ goto again; ++ } ++ if (kfifo_out_locked(&meye.grabq, (unsigned char *)&reqnr, ++ sizeof(int), &meye.grabq_lock) != sizeof(int)) { ++ mchip_free_frame(); ++ goto again; ++ } ++ memcpy(meye.grab_fbuffer + gbufsize * reqnr, meye.grab_temp, ++ size); ++ meye.grab_buffer[reqnr].size = size; ++ meye.grab_buffer[reqnr].state = MEYE_BUF_DONE; ++ v4l2_get_timestamp(&meye.grab_buffer[reqnr].timestamp); ++ meye.grab_buffer[reqnr].sequence = sequence++; ++ kfifo_in_locked(&meye.doneq, (unsigned char *)&reqnr, ++ sizeof(int), &meye.doneq_lock); ++ wake_up_interruptible(&meye.proc_list); ++ } ++ mchip_free_frame(); ++ goto again; ++} ++ ++/****************************************************************************/ ++/* video4linux integration */ ++/****************************************************************************/ ++ ++static int meye_open(struct file *file) ++{ ++ int i; ++ ++ if (test_and_set_bit(0, &meye.in_use)) ++ return -EBUSY; ++ ++ mchip_hic_stop(); ++ ++ if (mchip_dma_alloc()) { ++ printk(KERN_ERR "meye: mchip framebuffer allocation failed\n"); ++ clear_bit(0, &meye.in_use); ++ return -ENOBUFS; ++ } ++ ++ for (i = 0; i < MEYE_MAX_BUFNBRS; i++) ++ meye.grab_buffer[i].state = MEYE_BUF_UNUSED; ++ kfifo_reset(&meye.grabq); ++ kfifo_reset(&meye.doneq); ++ return 0; ++} ++ ++static int meye_release(struct file *file) ++{ ++ mchip_hic_stop(); ++ mchip_dma_free(); ++ clear_bit(0, &meye.in_use); ++ return 0; ++} ++ ++static int meyeioc_g_params(struct meye_params *p) ++{ ++ *p = meye.params; ++ return 0; ++} ++ ++static int meyeioc_s_params(struct meye_params *jp) ++{ ++ if (jp->subsample > 1) ++ return -EINVAL; ++ ++ if (jp->quality > 10) ++ return -EINVAL; ++ ++ if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) ++ return -EINVAL; ++ ++ if (jp->framerate > 31) ++ return -EINVAL; ++ ++ mutex_lock(&meye.lock); ++ ++ if (meye.params.subsample != jp->subsample || ++ meye.params.quality != jp->quality) ++ mchip_hic_stop(); /* need restart */ ++ ++ meye.params = *jp; ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, ++ meye.params.sharpness); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, ++ meye.params.agc); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, ++ meye.params.picture); ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int meyeioc_qbuf_capt(int *nb) ++{ ++ if (!meye.grab_fbuffer) ++ return -EINVAL; ++ ++ if (*nb >= gbuffers) ++ return -EINVAL; ++ ++ if (*nb < 0) { ++ /* stop capture */ ++ mchip_hic_stop(); ++ return 0; ++ } ++ ++ if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) ++ return -EBUSY; ++ ++ mutex_lock(&meye.lock); ++ ++ if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) ++ mchip_cont_compression_start(); ++ ++ meye.grab_buffer[*nb].state = MEYE_BUF_USING; ++ kfifo_in_locked(&meye.grabq, (unsigned char *)nb, sizeof(int), ++ &meye.grabq_lock); ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int meyeioc_sync(struct file *file, void *fh, int *i) ++{ ++ int unused; ++ ++ if (*i < 0 || *i >= gbuffers) ++ return -EINVAL; ++ ++ mutex_lock(&meye.lock); ++ switch (meye.grab_buffer[*i].state) { ++ ++ case MEYE_BUF_UNUSED: ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ case MEYE_BUF_USING: ++ if (file->f_flags & O_NONBLOCK) { ++ mutex_unlock(&meye.lock); ++ return -EAGAIN; ++ } ++ if (wait_event_interruptible(meye.proc_list, ++ (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { ++ mutex_unlock(&meye.lock); ++ return -EINTR; ++ } ++ /* fall through */ ++ case MEYE_BUF_DONE: ++ meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; ++ if (kfifo_out_locked(&meye.doneq, (unsigned char *)&unused, ++ sizeof(int), &meye.doneq_lock) != sizeof(int)) ++ break; ++ } ++ *i = meye.grab_buffer[*i].size; ++ mutex_unlock(&meye.lock); ++ return 0; ++} ++ ++static int meyeioc_stillcapt(void) ++{ ++ if (!meye.grab_fbuffer) ++ return -EINVAL; ++ ++ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) ++ return -EBUSY; ++ ++ mutex_lock(&meye.lock); ++ meye.grab_buffer[0].state = MEYE_BUF_USING; ++ mchip_take_picture(); ++ ++ mchip_get_picture(meye.grab_fbuffer, ++ mchip_hsize() * mchip_vsize() * 2); ++ ++ meye.grab_buffer[0].state = MEYE_BUF_DONE; ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int meyeioc_stilljcapt(int *len) ++{ ++ if (!meye.grab_fbuffer) ++ return -EINVAL; ++ ++ if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) ++ return -EBUSY; ++ ++ mutex_lock(&meye.lock); ++ meye.grab_buffer[0].state = MEYE_BUF_USING; ++ *len = -1; ++ ++ while (*len == -1) { ++ mchip_take_picture(); ++ *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); ++ } ++ ++ meye.grab_buffer[0].state = MEYE_BUF_DONE; ++ mutex_unlock(&meye.lock); ++ return 0; ++} ++ ++static int vidioc_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ strcpy(cap->driver, "meye"); ++ strcpy(cap->card, "meye"); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); ++ ++ cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + ++ MEYE_DRIVER_MINORVERSION; ++ ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_STREAMING; ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ if (i->index != 0) ++ return -EINVAL; ++ ++ strcpy(i->name, "Camera"); ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int vidioc_queryctrl(struct file *file, void *fh, ++ struct v4l2_queryctrl *c) ++{ ++ switch (c->id) { ++ ++ case V4L2_CID_BRIGHTNESS: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Brightness"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 32; ++ c->flags = 0; ++ break; ++ case V4L2_CID_HUE: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Hue"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 32; ++ c->flags = 0; ++ break; ++ case V4L2_CID_CONTRAST: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Contrast"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 32; ++ c->flags = 0; ++ break; ++ case V4L2_CID_SATURATION: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Saturation"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 32; ++ c->flags = 0; ++ break; ++ case V4L2_CID_AGC: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Agc"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 48; ++ c->flags = 0; ++ break; ++ case V4L2_CID_MEYE_SHARPNESS: ++ case V4L2_CID_SHARPNESS: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Sharpness"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 32; ++ ++ /* Continue to report legacy private SHARPNESS ctrl but ++ * say it is disabled in preference to ctrl in the spec ++ */ ++ c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : ++ V4L2_CTRL_FLAG_DISABLED; ++ break; ++ case V4L2_CID_PICTURE: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Picture"); ++ c->minimum = 0; ++ c->maximum = 63; ++ c->step = 1; ++ c->default_value = 0; ++ c->flags = 0; ++ break; ++ case V4L2_CID_JPEGQUAL: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "JPEG quality"); ++ c->minimum = 0; ++ c->maximum = 10; ++ c->step = 1; ++ c->default_value = 8; ++ c->flags = 0; ++ break; ++ case V4L2_CID_FRAMERATE: ++ c->type = V4L2_CTRL_TYPE_INTEGER; ++ strcpy(c->name, "Framerate"); ++ c->minimum = 0; ++ c->maximum = 31; ++ c->step = 1; ++ c->default_value = 0; ++ c->flags = 0; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) ++{ ++ mutex_lock(&meye.lock); ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); ++ meye.brightness = c->value << 10; ++ break; ++ case V4L2_CID_HUE: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERAHUE, c->value); ++ meye.hue = c->value << 10; ++ break; ++ case V4L2_CID_CONTRAST: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); ++ meye.contrast = c->value << 10; ++ break; ++ case V4L2_CID_SATURATION: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); ++ meye.colour = c->value << 10; ++ break; ++ case V4L2_CID_AGC: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERAAGC, c->value); ++ meye.params.agc = c->value; ++ break; ++ case V4L2_CID_SHARPNESS: ++ case V4L2_CID_MEYE_SHARPNESS: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); ++ meye.params.sharpness = c->value; ++ break; ++ case V4L2_CID_PICTURE: ++ sony_pic_camera_command( ++ SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); ++ meye.params.picture = c->value; ++ break; ++ case V4L2_CID_JPEGQUAL: ++ meye.params.quality = c->value; ++ break; ++ case V4L2_CID_FRAMERATE: ++ meye.params.framerate = c->value; ++ break; ++ default: ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) ++{ ++ mutex_lock(&meye.lock); ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ c->value = meye.brightness >> 10; ++ break; ++ case V4L2_CID_HUE: ++ c->value = meye.hue >> 10; ++ break; ++ case V4L2_CID_CONTRAST: ++ c->value = meye.contrast >> 10; ++ break; ++ case V4L2_CID_SATURATION: ++ c->value = meye.colour >> 10; ++ break; ++ case V4L2_CID_AGC: ++ c->value = meye.params.agc; ++ break; ++ case V4L2_CID_SHARPNESS: ++ case V4L2_CID_MEYE_SHARPNESS: ++ c->value = meye.params.sharpness; ++ break; ++ case V4L2_CID_PICTURE: ++ c->value = meye.params.picture; ++ break; ++ case V4L2_CID_JPEGQUAL: ++ c->value = meye.params.quality; ++ break; ++ case V4L2_CID_FRAMERATE: ++ c->value = meye.params.framerate; ++ break; ++ default: ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index > 1) ++ return -EINVAL; ++ ++ if (f->index == 0) { ++ /* standard YUV 422 capture */ ++ f->flags = 0; ++ strcpy(f->description, "YUV422"); ++ f->pixelformat = V4L2_PIX_FMT_YUYV; ++ } else { ++ /* compressed MJPEG capture */ ++ f->flags = V4L2_FMT_FLAG_COMPRESSED; ++ strcpy(f->description, "MJPEG"); ++ f->pixelformat = V4L2_PIX_FMT_MJPEG; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && ++ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) ++ return -EINVAL; ++ ++ if (f->fmt.pix.field != V4L2_FIELD_ANY && ++ f->fmt.pix.field != V4L2_FIELD_NONE) ++ return -EINVAL; ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ ++ if (f->fmt.pix.width <= 320) { ++ f->fmt.pix.width = 320; ++ f->fmt.pix.height = 240; ++ } else { ++ f->fmt.pix.width = 640; ++ f->fmt.pix.height = 480; ++ } ++ ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * ++ f->fmt.pix.bytesperline; ++ f->fmt.pix.colorspace = 0; ++ f->fmt.pix.priv = 0; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ switch (meye.mchip_mode) { ++ case MCHIP_HIC_MODE_CONT_OUT: ++ default: ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; ++ break; ++ case MCHIP_HIC_MODE_CONT_COMP: ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; ++ break; ++ } ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.width = mchip_hsize(); ++ f->fmt.pix.height = mchip_vsize(); ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * ++ f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && ++ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) ++ return -EINVAL; ++ ++ if (f->fmt.pix.field != V4L2_FIELD_ANY && ++ f->fmt.pix.field != V4L2_FIELD_NONE) ++ return -EINVAL; ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ mutex_lock(&meye.lock); ++ ++ if (f->fmt.pix.width <= 320) { ++ f->fmt.pix.width = 320; ++ f->fmt.pix.height = 240; ++ meye.params.subsample = 1; ++ } else { ++ f->fmt.pix.width = 640; ++ f->fmt.pix.height = 480; ++ meye.params.subsample = 0; ++ } ++ ++ switch (f->fmt.pix.pixelformat) { ++ case V4L2_PIX_FMT_YUYV: ++ meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; ++ break; ++ case V4L2_PIX_FMT_MJPEG: ++ meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; ++ break; ++ } ++ ++ mutex_unlock(&meye.lock); ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * ++ f->fmt.pix.bytesperline; ++ f->fmt.pix.colorspace = 0; ++ f->fmt.pix.priv = 0; ++ ++ return 0; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *fh, ++ struct v4l2_requestbuffers *req) ++{ ++ int i; ++ ++ if (req->memory != V4L2_MEMORY_MMAP) ++ return -EINVAL; ++ ++ if (meye.grab_fbuffer && req->count == gbuffers) { ++ /* already allocated, no modifications */ ++ return 0; ++ } ++ ++ mutex_lock(&meye.lock); ++ if (meye.grab_fbuffer) { ++ for (i = 0; i < gbuffers; i++) ++ if (meye.vma_use_count[i]) { ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ rvfree(meye.grab_fbuffer, gbuffers * gbufsize); ++ meye.grab_fbuffer = NULL; ++ } ++ ++ gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); ++ req->count = gbuffers; ++ meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); ++ ++ if (!meye.grab_fbuffer) { ++ printk(KERN_ERR "meye: v4l framebuffer allocation" ++ " failed\n"); ++ mutex_unlock(&meye.lock); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < gbuffers; i++) ++ meye.vma_use_count[i] = 0; ++ ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) ++{ ++ unsigned int index = buf->index; ++ ++ if (index >= gbuffers) ++ return -EINVAL; ++ ++ buf->bytesused = meye.grab_buffer[index].size; ++ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ++ if (meye.grab_buffer[index].state == MEYE_BUF_USING) ++ buf->flags |= V4L2_BUF_FLAG_QUEUED; ++ ++ if (meye.grab_buffer[index].state == MEYE_BUF_DONE) ++ buf->flags |= V4L2_BUF_FLAG_DONE; ++ ++ buf->field = V4L2_FIELD_NONE; ++ buf->timestamp = meye.grab_buffer[index].timestamp; ++ buf->sequence = meye.grab_buffer[index].sequence; ++ buf->memory = V4L2_MEMORY_MMAP; ++ buf->m.offset = index * gbufsize; ++ buf->length = gbufsize; ++ ++ return 0; ++} ++ ++static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) ++{ ++ if (buf->memory != V4L2_MEMORY_MMAP) ++ return -EINVAL; ++ ++ if (buf->index >= gbuffers) ++ return -EINVAL; ++ ++ if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) ++ return -EINVAL; ++ ++ mutex_lock(&meye.lock); ++ buf->flags |= V4L2_BUF_FLAG_QUEUED; ++ buf->flags &= ~V4L2_BUF_FLAG_DONE; ++ meye.grab_buffer[buf->index].state = MEYE_BUF_USING; ++ kfifo_in_locked(&meye.grabq, (unsigned char *)&buf->index, ++ sizeof(int), &meye.grabq_lock); ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) ++{ ++ int reqnr; ++ ++ if (buf->memory != V4L2_MEMORY_MMAP) ++ return -EINVAL; ++ ++ mutex_lock(&meye.lock); ++ ++ if (kfifo_len(&meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { ++ mutex_unlock(&meye.lock); ++ return -EAGAIN; ++ } ++ ++ if (wait_event_interruptible(meye.proc_list, ++ kfifo_len(&meye.doneq) != 0) < 0) { ++ mutex_unlock(&meye.lock); ++ return -EINTR; ++ } ++ ++ if (!kfifo_out_locked(&meye.doneq, (unsigned char *)&reqnr, ++ sizeof(int), &meye.doneq_lock)) { ++ mutex_unlock(&meye.lock); ++ return -EBUSY; ++ } ++ ++ if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ ++ buf->index = reqnr; ++ buf->bytesused = meye.grab_buffer[reqnr].size; ++ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ buf->field = V4L2_FIELD_NONE; ++ buf->timestamp = meye.grab_buffer[reqnr].timestamp; ++ buf->sequence = meye.grab_buffer[reqnr].sequence; ++ buf->memory = V4L2_MEMORY_MMAP; ++ buf->m.offset = reqnr * gbufsize; ++ buf->length = gbufsize; ++ meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) ++{ ++ mutex_lock(&meye.lock); ++ ++ switch (meye.mchip_mode) { ++ case MCHIP_HIC_MODE_CONT_OUT: ++ mchip_continuous_start(); ++ break; ++ case MCHIP_HIC_MODE_CONT_COMP: ++ mchip_cont_compression_start(); ++ break; ++ default: ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ ++ mutex_unlock(&meye.lock); ++ ++ return 0; ++} ++ ++static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) ++{ ++ mutex_lock(&meye.lock); ++ mchip_hic_stop(); ++ kfifo_reset(&meye.grabq); ++ kfifo_reset(&meye.doneq); ++ ++ for (i = 0; i < MEYE_MAX_BUFNBRS; i++) ++ meye.grab_buffer[i].state = MEYE_BUF_UNUSED; ++ ++ mutex_unlock(&meye.lock); ++ return 0; ++} ++ ++static long vidioc_default(struct file *file, void *fh, bool valid_prio, ++ int cmd, void *arg) ++{ ++ switch (cmd) { ++ case MEYEIOC_G_PARAMS: ++ return meyeioc_g_params((struct meye_params *) arg); ++ ++ case MEYEIOC_S_PARAMS: ++ return meyeioc_s_params((struct meye_params *) arg); ++ ++ case MEYEIOC_QBUF_CAPT: ++ return meyeioc_qbuf_capt((int *) arg); ++ ++ case MEYEIOC_SYNC: ++ return meyeioc_sync(file, fh, (int *) arg); ++ ++ case MEYEIOC_STILLCAPT: ++ return meyeioc_stillcapt(); ++ ++ case MEYEIOC_STILLJCAPT: ++ return meyeioc_stilljcapt((int *) arg); ++ ++ default: ++ return -ENOTTY; ++ } ++ ++} ++ ++static unsigned int meye_poll(struct file *file, poll_table *wait) ++{ ++ unsigned int res = 0; ++ ++ mutex_lock(&meye.lock); ++ poll_wait(file, &meye.proc_list, wait); ++ if (kfifo_len(&meye.doneq)) ++ res = POLLIN | POLLRDNORM; ++ mutex_unlock(&meye.lock); ++ return res; ++} ++ ++static void meye_vm_open(struct vm_area_struct *vma) ++{ ++ long idx = (long)vma->vm_private_data; ++ meye.vma_use_count[idx]++; ++} ++ ++static void meye_vm_close(struct vm_area_struct *vma) ++{ ++ long idx = (long)vma->vm_private_data; ++ meye.vma_use_count[idx]--; ++} ++ ++static const struct vm_operations_struct meye_vm_ops = { ++ .open = meye_vm_open, ++ .close = meye_vm_close, ++}; ++ ++static int meye_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ unsigned long start = vma->vm_start; ++ unsigned long size = vma->vm_end - vma->vm_start; ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ unsigned long page, pos; ++ ++ mutex_lock(&meye.lock); ++ if (size > gbuffers * gbufsize) { ++ mutex_unlock(&meye.lock); ++ return -EINVAL; ++ } ++ if (!meye.grab_fbuffer) { ++ int i; ++ ++ /* lazy allocation */ ++ meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize); ++ if (!meye.grab_fbuffer) { ++ printk(KERN_ERR "meye: v4l framebuffer allocation failed\n"); ++ mutex_unlock(&meye.lock); ++ return -ENOMEM; ++ } ++ for (i = 0; i < gbuffers; i++) ++ meye.vma_use_count[i] = 0; ++ } ++ pos = (unsigned long)meye.grab_fbuffer + offset; ++ ++ while (size > 0) { ++ page = vmalloc_to_pfn((void *)pos); ++ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { ++ mutex_unlock(&meye.lock); ++ return -EAGAIN; ++ } ++ start += PAGE_SIZE; ++ pos += PAGE_SIZE; ++ if (size > PAGE_SIZE) ++ size -= PAGE_SIZE; ++ else ++ size = 0; ++ } ++ ++ vma->vm_ops = &meye_vm_ops; ++ vma->vm_flags &= ~VM_IO; /* not I/O memory */ ++ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; ++ vma->vm_private_data = (void *) (offset / gbufsize); ++ meye_vm_open(vma); ++ ++ mutex_unlock(&meye.lock); ++ return 0; ++} ++ ++static const struct v4l2_file_operations meye_fops = { ++ .owner = THIS_MODULE, ++ .open = meye_open, ++ .release = meye_release, ++ .mmap = meye_mmap, ++ .unlocked_ioctl = video_ioctl2, ++ .poll = meye_poll, ++}; ++ ++static const struct v4l2_ioctl_ops meye_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_default = vidioc_default, ++}; ++ ++static struct video_device meye_template = { ++ .name = "meye", ++ .fops = &meye_fops, ++ .ioctl_ops = &meye_ioctl_ops, ++ .release = video_device_release, ++}; ++ ++#ifdef CONFIG_PM ++static int meye_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ pci_save_state(pdev); ++ meye.pm_mchip_mode = meye.mchip_mode; ++ mchip_hic_stop(); ++ mchip_set(MCHIP_MM_INTA, 0x0); ++ return 0; ++} ++ ++static int meye_resume(struct pci_dev *pdev) ++{ ++ pci_restore_state(pdev); ++ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); ++ msleep(1); ++ mchip_set(MCHIP_VRJ_SOFT_RESET, 1); ++ msleep(1); ++ mchip_set(MCHIP_MM_PCI_MODE, 5); ++ msleep(1); ++ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); ++ ++ switch (meye.pm_mchip_mode) { ++ case MCHIP_HIC_MODE_CONT_OUT: ++ mchip_continuous_start(); ++ break; ++ case MCHIP_HIC_MODE_CONT_COMP: ++ mchip_cont_compression_start(); ++ break; ++ } ++ return 0; ++} ++#endif ++ ++static int __devinit meye_probe(struct pci_dev *pcidev, ++ const struct pci_device_id *ent) ++{ ++ struct v4l2_device *v4l2_dev = &meye.v4l2_dev; ++ int ret = -EBUSY; ++ unsigned long mchip_adr; ++ ++ if (meye.mchip_dev != NULL) { ++ printk(KERN_ERR "meye: only one device allowed!\n"); ++ goto outnotdev; ++ } ++ ++ ret = v4l2_device_register(&pcidev->dev, v4l2_dev); ++ if (ret < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ return ret; ++ } ++ ret = -ENOMEM; ++ meye.mchip_dev = pcidev; ++ meye.vdev = video_device_alloc(); ++ if (!meye.vdev) { ++ v4l2_err(v4l2_dev, "video_device_alloc() failed!\n"); ++ goto outnotdev; ++ } ++ ++ meye.grab_temp = vmalloc(MCHIP_NB_PAGES_MJPEG * PAGE_SIZE); ++ if (!meye.grab_temp) { ++ v4l2_err(v4l2_dev, "grab buffer allocation failed\n"); ++ goto outvmalloc; ++ } ++ ++ spin_lock_init(&meye.grabq_lock); ++ if (kfifo_alloc(&meye.grabq, sizeof(int) * MEYE_MAX_BUFNBRS, ++ GFP_KERNEL)) { ++ v4l2_err(v4l2_dev, "fifo allocation failed\n"); ++ goto outkfifoalloc1; ++ } ++ spin_lock_init(&meye.doneq_lock); ++ if (kfifo_alloc(&meye.doneq, sizeof(int) * MEYE_MAX_BUFNBRS, ++ GFP_KERNEL)) { ++ v4l2_err(v4l2_dev, "fifo allocation failed\n"); ++ goto outkfifoalloc2; ++ } ++ ++ memcpy(meye.vdev, &meye_template, sizeof(meye_template)); ++ meye.vdev->v4l2_dev = &meye.v4l2_dev; ++ ++ ret = -EIO; ++ if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { ++ v4l2_err(v4l2_dev, "meye: unable to power on the camera\n"); ++ v4l2_err(v4l2_dev, "meye: did you enable the camera in " ++ "sonypi using the module options ?\n"); ++ goto outsonypienable; ++ } ++ ++ if ((ret = pci_enable_device(meye.mchip_dev))) { ++ v4l2_err(v4l2_dev, "meye: pci_enable_device failed\n"); ++ goto outenabledev; ++ } ++ ++ mchip_adr = pci_resource_start(meye.mchip_dev,0); ++ if (!mchip_adr) { ++ v4l2_err(v4l2_dev, "meye: mchip has no device base address\n"); ++ goto outregions; ++ } ++ if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0), ++ pci_resource_len(meye.mchip_dev, 0), ++ "meye")) { ++ v4l2_err(v4l2_dev, "meye: request_mem_region failed\n"); ++ goto outregions; ++ } ++ meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS); ++ if (!meye.mchip_mmregs) { ++ v4l2_err(v4l2_dev, "meye: ioremap failed\n"); ++ goto outremap; ++ } ++ ++ meye.mchip_irq = pcidev->irq; ++ if (request_irq(meye.mchip_irq, meye_irq, ++ IRQF_DISABLED | IRQF_SHARED, "meye", meye_irq)) { ++ v4l2_err(v4l2_dev, "request_irq failed\n"); ++ goto outreqirq; ++ } ++ ++ pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8); ++ pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64); ++ ++ pci_set_master(meye.mchip_dev); ++ ++ /* Ask the camera to perform a soft reset. */ ++ pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1); ++ ++ mchip_delay(MCHIP_HIC_CMD, 0); ++ mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE); ++ ++ msleep(1); ++ mchip_set(MCHIP_VRJ_SOFT_RESET, 1); ++ ++ msleep(1); ++ mchip_set(MCHIP_MM_PCI_MODE, 5); ++ ++ msleep(1); ++ mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK); ++ ++ mutex_init(&meye.lock); ++ init_waitqueue_head(&meye.proc_list); ++ meye.brightness = 32 << 10; ++ meye.hue = 32 << 10; ++ meye.colour = 32 << 10; ++ meye.contrast = 32 << 10; ++ meye.params.subsample = 0; ++ meye.params.quality = 8; ++ meye.params.sharpness = 32; ++ meye.params.agc = 48; ++ meye.params.picture = 0; ++ meye.params.framerate = 0; ++ ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); ++ ++ if (video_register_device(meye.vdev, VFL_TYPE_GRABBER, ++ video_nr) < 0) { ++ v4l2_err(v4l2_dev, "video_register_device failed\n"); ++ goto outvideoreg; ++ } ++ ++ v4l2_info(v4l2_dev, "Motion Eye Camera Driver v%s.\n", ++ MEYE_DRIVER_VERSION); ++ v4l2_info(v4l2_dev, "mchip KL5A72002 rev. %d, base %lx, irq %d\n", ++ meye.mchip_dev->revision, mchip_adr, meye.mchip_irq); ++ ++ return 0; ++ ++outvideoreg: ++ free_irq(meye.mchip_irq, meye_irq); ++outreqirq: ++ iounmap(meye.mchip_mmregs); ++outremap: ++ release_mem_region(pci_resource_start(meye.mchip_dev, 0), ++ pci_resource_len(meye.mchip_dev, 0)); ++outregions: ++ pci_disable_device(meye.mchip_dev); ++outenabledev: ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); ++outsonypienable: ++ kfifo_free(&meye.doneq); ++outkfifoalloc2: ++ kfifo_free(&meye.grabq); ++outkfifoalloc1: ++ vfree(meye.grab_temp); ++outvmalloc: ++ video_device_release(meye.vdev); ++outnotdev: ++ return ret; ++} ++ ++static void __devexit meye_remove(struct pci_dev *pcidev) ++{ ++ video_unregister_device(meye.vdev); ++ ++ mchip_hic_stop(); ++ ++ mchip_dma_free(); ++ ++ /* disable interrupts */ ++ mchip_set(MCHIP_MM_INTA, 0x0); ++ ++ free_irq(meye.mchip_irq, meye_irq); ++ ++ iounmap(meye.mchip_mmregs); ++ ++ release_mem_region(pci_resource_start(meye.mchip_dev, 0), ++ pci_resource_len(meye.mchip_dev, 0)); ++ ++ pci_disable_device(meye.mchip_dev); ++ ++ sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); ++ ++ kfifo_free(&meye.doneq); ++ kfifo_free(&meye.grabq); ++ ++ vfree(meye.grab_temp); ++ ++ if (meye.grab_fbuffer) { ++ rvfree(meye.grab_fbuffer, gbuffers*gbufsize); ++ meye.grab_fbuffer = NULL; ++ } ++ ++ printk(KERN_INFO "meye: removed\n"); ++} ++ ++static struct pci_device_id meye_pci_tbl[] = { ++ { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, ++ { } ++}; ++ ++MODULE_DEVICE_TABLE(pci, meye_pci_tbl); ++ ++static struct pci_driver meye_driver = { ++ .name = "meye", ++ .id_table = meye_pci_tbl, ++ .probe = meye_probe, ++ .remove = __devexit_p(meye_remove), ++#ifdef CONFIG_PM ++ .suspend = meye_suspend, ++ .resume = meye_resume, ++#endif ++}; ++ ++static int __init meye_init(void) ++{ ++ gbuffers = max(2, min((int)gbuffers, MEYE_MAX_BUFNBRS)); ++ if (gbufsize > MEYE_MAX_BUFSIZE) ++ gbufsize = MEYE_MAX_BUFSIZE; ++ gbufsize = PAGE_ALIGN(gbufsize); ++ printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) " ++ "for capture\n", ++ gbuffers, ++ gbufsize / 1024, gbuffers * gbufsize / 1024); ++ return pci_register_driver(&meye_driver); ++} ++ ++static void __exit meye_exit(void) ++{ ++ pci_unregister_driver(&meye_driver); ++} ++ ++module_init(meye_init); ++module_exit(meye_exit); +diff --git a/drivers/media/pci/meye/meye.h b/drivers/media/pci/meye/meye.h +new file mode 100644 +index 0000000..4bdeb03 +--- /dev/null ++++ b/drivers/media/pci/meye/meye.h +@@ -0,0 +1,324 @@ ++/* ++ * Motion Eye video4linux driver for Sony Vaio PictureBook ++ * ++ * Copyright (C) 2001-2004 Stelian Pop ++ * ++ * Copyright (C) 2001-2002 Alcôve ++ * ++ * Copyright (C) 2000 Andrew Tridgell ++ * ++ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. ++ * ++ * Some parts borrowed from various video4linux drivers, especially ++ * bttv-driver.c and zoran.c, see original files for credits. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _MEYE_PRIV_H_ ++#define _MEYE_PRIV_H_ ++ ++#define MEYE_DRIVER_MAJORVERSION 1 ++#define MEYE_DRIVER_MINORVERSION 14 ++ ++#define MEYE_DRIVER_VERSION __stringify(MEYE_DRIVER_MAJORVERSION) "." \ ++ __stringify(MEYE_DRIVER_MINORVERSION) ++ ++#include ++#include ++#include ++ ++/****************************************************************************/ ++/* Motion JPEG chip registers */ ++/****************************************************************************/ ++ ++/* Motion JPEG chip PCI configuration registers */ ++#define MCHIP_PCI_POWER_CSR 0x54 ++#define MCHIP_PCI_MCORE_STATUS 0x60 /* see HIC_STATUS */ ++#define MCHIP_PCI_HOSTUSEREQ_SET 0x64 ++#define MCHIP_PCI_HOSTUSEREQ_CLR 0x68 ++#define MCHIP_PCI_LOWPOWER_SET 0x6c ++#define MCHIP_PCI_LOWPOWER_CLR 0x70 ++#define MCHIP_PCI_SOFTRESET_SET 0x74 ++ ++/* Motion JPEG chip memory mapped registers */ ++#define MCHIP_MM_REGS 0x200 /* 512 bytes */ ++#define MCHIP_REG_TIMEOUT 1000 /* reg access, ~us */ ++#define MCHIP_MCC_VRJ_TIMEOUT 1000 /* MCC & VRJ access */ ++ ++#define MCHIP_MM_PCI_MODE 0x00 /* PCI access mode */ ++#define MCHIP_MM_PCI_MODE_RETRY 0x00000001 /* retry mode */ ++#define MCHIP_MM_PCI_MODE_MASTER 0x00000002 /* master access */ ++#define MCHIP_MM_PCI_MODE_READ_LINE 0x00000004 /* read line */ ++ ++#define MCHIP_MM_INTA 0x04 /* Int status/mask */ ++#define MCHIP_MM_INTA_MCC 0x00000001 /* MCC interrupt */ ++#define MCHIP_MM_INTA_VRJ 0x00000002 /* VRJ interrupt */ ++#define MCHIP_MM_INTA_HIC_1 0x00000004 /* one frame done */ ++#define MCHIP_MM_INTA_HIC_1_MASK 0x00000400 /* 1: enable */ ++#define MCHIP_MM_INTA_HIC_END 0x00000008 /* all frames done */ ++#define MCHIP_MM_INTA_HIC_END_MASK 0x00000800 ++#define MCHIP_MM_INTA_JPEG 0x00000010 /* decompress. error */ ++#define MCHIP_MM_INTA_JPEG_MASK 0x00001000 ++#define MCHIP_MM_INTA_CAPTURE 0x00000020 /* capture end */ ++#define MCHIP_MM_INTA_PCI_ERR 0x00000040 /* PCI error */ ++#define MCHIP_MM_INTA_PCI_ERR_MASK 0x00004000 ++ ++#define MCHIP_MM_PT_ADDR 0x08 /* page table address*/ ++ /* n*4kB */ ++#define MCHIP_NB_PAGES 1024 /* pages for display */ ++#define MCHIP_NB_PAGES_MJPEG 256 /* pages for mjpeg */ ++ ++#define MCHIP_MM_FIR(n) (0x0c+(n)*4) /* Frame info 0-3 */ ++#define MCHIP_MM_FIR_RDY 0x00000001 /* frame ready */ ++#define MCHIP_MM_FIR_FAILFR_MASK 0xf8000000 /* # of failed frames */ ++#define MCHIP_MM_FIR_FAILFR_SHIFT 27 ++ ++ /* continuous comp/decomp mode */ ++#define MCHIP_MM_FIR_C_ENDL_MASK 0x000007fe /* end DW [10] */ ++#define MCHIP_MM_FIR_C_ENDL_SHIFT 1 ++#define MCHIP_MM_FIR_C_ENDP_MASK 0x0007f800 /* end page [8] */ ++#define MCHIP_MM_FIR_C_ENDP_SHIFT 11 ++#define MCHIP_MM_FIR_C_STARTP_MASK 0x07f80000 /* start page [8] */ ++#define MCHIP_MM_FIR_C_STARTP_SHIFT 19 ++ ++ /* continuous picture output mode */ ++#define MCHIP_MM_FIR_O_STARTP_MASK 0x7ffe0000 /* start page [10] */ ++#define MCHIP_MM_FIR_O_STARTP_SHIFT 17 ++ ++#define MCHIP_MM_FIFO_DATA 0x1c /* PCI TGT FIFO data */ ++#define MCHIP_MM_FIFO_STATUS 0x20 /* PCI TGT FIFO stat */ ++#define MCHIP_MM_FIFO_MASK 0x00000003 ++#define MCHIP_MM_FIFO_WAIT_OR_READY 0x00000002 /* Bits common to WAIT & READY*/ ++#define MCHIP_MM_FIFO_IDLE 0x0 /* HIC idle */ ++#define MCHIP_MM_FIFO_IDLE1 0x1 /* idem ??? */ ++#define MCHIP_MM_FIFO_WAIT 0x2 /* wait request */ ++#define MCHIP_MM_FIFO_READY 0x3 /* data ready */ ++ ++#define MCHIP_HIC_HOST_USEREQ 0x40 /* host uses MCORE */ ++ ++#define MCHIP_HIC_TP_BUSY 0x44 /* taking picture */ ++ ++#define MCHIP_HIC_PIC_SAVED 0x48 /* pic in SDRAM */ ++ ++#define MCHIP_HIC_LOWPOWER 0x4c /* clock stopped */ ++ ++#define MCHIP_HIC_CTL 0x50 /* HIC control */ ++#define MCHIP_HIC_CTL_SOFT_RESET 0x00000001 /* MCORE reset */ ++#define MCHIP_HIC_CTL_MCORE_RDY 0x00000002 /* MCORE ready */ ++ ++#define MCHIP_HIC_CMD 0x54 /* HIC command */ ++#define MCHIP_HIC_CMD_BITS 0x00000003 /* cmd width=[1:0]*/ ++#define MCHIP_HIC_CMD_NOOP 0x0 ++#define MCHIP_HIC_CMD_START 0x1 ++#define MCHIP_HIC_CMD_STOP 0x2 ++ ++#define MCHIP_HIC_MODE 0x58 ++#define MCHIP_HIC_MODE_NOOP 0x0 ++#define MCHIP_HIC_MODE_STILL_CAP 0x1 /* still pic capt */ ++#define MCHIP_HIC_MODE_DISPLAY 0x2 /* display */ ++#define MCHIP_HIC_MODE_STILL_COMP 0x3 /* still pic comp. */ ++#define MCHIP_HIC_MODE_STILL_DECOMP 0x4 /* still pic decomp. */ ++#define MCHIP_HIC_MODE_CONT_COMP 0x5 /* cont capt+comp */ ++#define MCHIP_HIC_MODE_CONT_DECOMP 0x6 /* cont decomp+disp */ ++#define MCHIP_HIC_MODE_STILL_OUT 0x7 /* still pic output */ ++#define MCHIP_HIC_MODE_CONT_OUT 0x8 /* cont output */ ++ ++#define MCHIP_HIC_STATUS 0x5c ++#define MCHIP_HIC_STATUS_MCC_RDY 0x00000001 /* MCC reg acc ok */ ++#define MCHIP_HIC_STATUS_VRJ_RDY 0x00000002 /* VRJ reg acc ok */ ++#define MCHIP_HIC_STATUS_IDLE 0x00000003 ++#define MCHIP_HIC_STATUS_CAPDIS 0x00000004 /* cap/disp in prog */ ++#define MCHIP_HIC_STATUS_COMPDEC 0x00000008 /* (de)comp in prog */ ++#define MCHIP_HIC_STATUS_BUSY 0x00000010 /* HIC busy */ ++ ++#define MCHIP_HIC_S_RATE 0x60 /* MJPEG # frames */ ++ ++#define MCHIP_HIC_PCI_VFMT 0x64 /* video format */ ++#define MCHIP_HIC_PCI_VFMT_YVYU 0x00000001 /* 0: V Y' U Y */ ++ /* 1: Y' V Y U */ ++ ++#define MCHIP_MCC_CMD 0x80 /* MCC commands */ ++#define MCHIP_MCC_CMD_INITIAL 0x0 /* idle ? */ ++#define MCHIP_MCC_CMD_IIC_START_SET 0x1 ++#define MCHIP_MCC_CMD_IIC_END_SET 0x2 ++#define MCHIP_MCC_CMD_FM_WRITE 0x3 /* frame memory */ ++#define MCHIP_MCC_CMD_FM_READ 0x4 ++#define MCHIP_MCC_CMD_FM_STOP 0x5 ++#define MCHIP_MCC_CMD_CAPTURE 0x6 ++#define MCHIP_MCC_CMD_DISPLAY 0x7 ++#define MCHIP_MCC_CMD_END_DISP 0x8 ++#define MCHIP_MCC_CMD_STILL_COMP 0x9 ++#define MCHIP_MCC_CMD_STILL_DECOMP 0xa ++#define MCHIP_MCC_CMD_STILL_OUTPUT 0xb ++#define MCHIP_MCC_CMD_CONT_OUTPUT 0xc ++#define MCHIP_MCC_CMD_CONT_COMP 0xd ++#define MCHIP_MCC_CMD_CONT_DECOMP 0xe ++#define MCHIP_MCC_CMD_RESET 0xf /* MCC reset */ ++ ++#define MCHIP_MCC_IIC_WR 0x84 ++ ++#define MCHIP_MCC_MCC_WR 0x88 ++ ++#define MCHIP_MCC_MCC_RD 0x8c ++ ++#define MCHIP_MCC_STATUS 0x90 ++#define MCHIP_MCC_STATUS_CAPT 0x00000001 /* capturing */ ++#define MCHIP_MCC_STATUS_DISP 0x00000002 /* displaying */ ++#define MCHIP_MCC_STATUS_COMP 0x00000004 /* compressing */ ++#define MCHIP_MCC_STATUS_DECOMP 0x00000008 /* decompressing */ ++#define MCHIP_MCC_STATUS_MCC_WR 0x00000010 /* register ready */ ++#define MCHIP_MCC_STATUS_MCC_RD 0x00000020 /* register ready */ ++#define MCHIP_MCC_STATUS_IIC_WR 0x00000040 /* register ready */ ++#define MCHIP_MCC_STATUS_OUTPUT 0x00000080 /* output in prog */ ++ ++#define MCHIP_MCC_SIG_POLARITY 0x94 ++#define MCHIP_MCC_SIG_POL_VS_H 0x00000001 /* VS active-high */ ++#define MCHIP_MCC_SIG_POL_HS_H 0x00000002 /* HS active-high */ ++#define MCHIP_MCC_SIG_POL_DOE_H 0x00000004 /* DOE active-high */ ++ ++#define MCHIP_MCC_IRQ 0x98 ++#define MCHIP_MCC_IRQ_CAPDIS_STRT 0x00000001 /* cap/disp started */ ++#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010 ++#define MCHIP_MCC_IRQ_CAPDIS_END 0x00000002 /* cap/disp ended */ ++#define MCHIP_MCC_IRQ_CAPDIS_END_MASK 0x00000020 ++#define MCHIP_MCC_IRQ_COMPDEC_STRT 0x00000004 /* (de)comp started */ ++#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK 0x00000040 ++#define MCHIP_MCC_IRQ_COMPDEC_END 0x00000008 /* (de)comp ended */ ++#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080 ++ ++#define MCHIP_MCC_HSTART 0x9c /* video in */ ++#define MCHIP_MCC_VSTART 0xa0 ++#define MCHIP_MCC_HCOUNT 0xa4 ++#define MCHIP_MCC_VCOUNT 0xa8 ++#define MCHIP_MCC_R_XBASE 0xac /* capt/disp */ ++#define MCHIP_MCC_R_YBASE 0xb0 ++#define MCHIP_MCC_R_XRANGE 0xb4 ++#define MCHIP_MCC_R_YRANGE 0xb8 ++#define MCHIP_MCC_B_XBASE 0xbc /* comp/decomp */ ++#define MCHIP_MCC_B_YBASE 0xc0 ++#define MCHIP_MCC_B_XRANGE 0xc4 ++#define MCHIP_MCC_B_YRANGE 0xc8 ++ ++#define MCHIP_MCC_R_SAMPLING 0xcc /* 1: 1:4 */ ++ ++#define MCHIP_VRJ_CMD 0x100 /* VRJ commands */ ++ ++/* VRJ registers (see table 12.2.4) */ ++#define MCHIP_VRJ_COMPRESSED_DATA 0x1b0 ++#define MCHIP_VRJ_PIXEL_DATA 0x1b8 ++ ++#define MCHIP_VRJ_BUS_MODE 0x100 ++#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL 0x108 ++#define MCHIP_VRJ_PDAT_USE 0x110 ++#define MCHIP_VRJ_MODE_SPECIFY 0x118 ++#define MCHIP_VRJ_LIMIT_COMPRESSED_LO 0x120 ++#define MCHIP_VRJ_LIMIT_COMPRESSED_HI 0x124 ++#define MCHIP_VRJ_COMP_DATA_FORMAT 0x128 ++#define MCHIP_VRJ_TABLE_DATA 0x140 ++#define MCHIP_VRJ_RESTART_INTERVAL 0x148 ++#define MCHIP_VRJ_NUM_LINES 0x150 ++#define MCHIP_VRJ_NUM_PIXELS 0x158 ++#define MCHIP_VRJ_NUM_COMPONENTS 0x160 ++#define MCHIP_VRJ_SOF1 0x168 ++#define MCHIP_VRJ_SOF2 0x170 ++#define MCHIP_VRJ_SOF3 0x178 ++#define MCHIP_VRJ_SOF4 0x180 ++#define MCHIP_VRJ_SOS 0x188 ++#define MCHIP_VRJ_SOFT_RESET 0x190 ++ ++#define MCHIP_VRJ_STATUS 0x1c0 ++#define MCHIP_VRJ_STATUS_BUSY 0x00001 ++#define MCHIP_VRJ_STATUS_COMP_ACCESS 0x00002 ++#define MCHIP_VRJ_STATUS_PIXEL_ACCESS 0x00004 ++#define MCHIP_VRJ_STATUS_ERROR 0x00008 ++ ++#define MCHIP_VRJ_IRQ_FLAG 0x1c8 ++#define MCHIP_VRJ_ERROR_REPORT 0x1d8 ++ ++#define MCHIP_VRJ_START_COMMAND 0x1a0 ++ ++/****************************************************************************/ ++/* Driver definitions. */ ++/****************************************************************************/ ++ ++/* Sony Programmable I/O Controller for accessing the camera commands */ ++#include ++ ++/* private API definitions */ ++#include ++#include ++ ++ ++/* Enable jpg software correction */ ++#define MEYE_JPEG_CORRECTION 1 ++ ++/* Maximum size of a buffer */ ++#define MEYE_MAX_BUFSIZE 614400 /* 640 * 480 * 2 */ ++ ++/* Maximum number of buffers */ ++#define MEYE_MAX_BUFNBRS 32 ++ ++/* State of a buffer */ ++#define MEYE_BUF_UNUSED 0 /* not used */ ++#define MEYE_BUF_USING 1 /* currently grabbing / playing */ ++#define MEYE_BUF_DONE 2 /* done */ ++ ++/* grab buffer */ ++struct meye_grab_buffer { ++ int state; /* state of buffer */ ++ unsigned long size; /* size of jpg frame */ ++ struct timeval timestamp; /* timestamp */ ++ unsigned long sequence; /* sequence number */ ++}; ++ ++/* size of kfifos containings buffer indices */ ++#define MEYE_QUEUE_SIZE MEYE_MAX_BUFNBRS ++ ++/* Motion Eye device structure */ ++struct meye { ++ struct v4l2_device v4l2_dev; /* Main v4l2_device struct */ ++ struct pci_dev *mchip_dev; /* pci device */ ++ u8 mchip_irq; /* irq */ ++ u8 mchip_mode; /* actual mchip mode: HIC_MODE... */ ++ u8 mchip_fnum; /* current mchip frame number */ ++ unsigned char __iomem *mchip_mmregs;/* mchip: memory mapped registers */ ++ u8 *mchip_ptable[MCHIP_NB_PAGES];/* mchip: ptable */ ++ void *mchip_ptable_toc; /* mchip: ptable toc */ ++ dma_addr_t mchip_dmahandle; /* mchip: dma handle to ptable toc */ ++ unsigned char *grab_fbuffer; /* capture framebuffer */ ++ unsigned char *grab_temp; /* temporary buffer */ ++ /* list of buffers */ ++ struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS]; ++ int vma_use_count[MEYE_MAX_BUFNBRS]; /* mmap count */ ++ struct mutex lock; /* mutex for open/mmap... */ ++ struct kfifo grabq; /* queue for buffers to be grabbed */ ++ spinlock_t grabq_lock; /* lock protecting the queue */ ++ struct kfifo doneq; /* queue for grabbed buffers */ ++ spinlock_t doneq_lock; /* lock protecting the queue */ ++ wait_queue_head_t proc_list; /* wait queue */ ++ struct video_device *vdev; /* video device parameters */ ++ u16 brightness; ++ u16 hue; ++ u16 contrast; ++ u16 colour; ++ struct meye_params params; /* additional parameters */ ++ unsigned long in_use; /* set to 1 if the device is in use */ ++#ifdef CONFIG_PM ++ u8 pm_mchip_mode; /* old mchip mode */ ++#endif ++}; ++ ++#endif +diff --git a/drivers/media/pci/ngene/Kconfig b/drivers/media/pci/ngene/Kconfig +new file mode 100644 +index 0000000..637d506 +--- /dev/null ++++ b/drivers/media/pci/ngene/Kconfig +@@ -0,0 +1,13 @@ ++config DVB_NGENE ++ tristate "Micronas nGene support" ++ depends on DVB_CORE && PCI && I2C ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT330X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_DRXK if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA18271C2DD if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_MT2131 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ Support for Micronas PCI express cards with nGene bridge. ++ +diff --git a/drivers/media/pci/ngene/Makefile b/drivers/media/pci/ngene/Makefile +new file mode 100644 +index 0000000..5c0b5d6 +--- /dev/null ++++ b/drivers/media/pci/ngene/Makefile +@@ -0,0 +1,14 @@ ++# ++# Makefile for the nGene device driver ++# ++ ++ngene-objs := ngene-core.o ngene-i2c.o ngene-cards.o ngene-dvb.o ++ ++obj-$(CONFIG_DVB_NGENE) += ngene.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ ++ccflags-y += -Idrivers/media/dvb-frontends/ ++ccflags-y += -Idrivers/media/tuners/ ++ ++# For the staging CI driver cxd2099 ++ccflags-y += -Idrivers/staging/media/cxd2099/ +diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c +new file mode 100644 +index 0000000..82318d1 +--- /dev/null ++++ b/drivers/media/pci/ngene/ngene-cards.c +@@ -0,0 +1,834 @@ ++/* ++ * ngene-cards.c: nGene PCIe bridge driver - card specific info ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * Copyright (C) 2008-2009 Ralph Metzler ++ * Modifications for new nGene firmware, ++ * support for EEPROM-copying, ++ * support for new dual DVB-S2 card prototype ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "ngene.h" ++ ++/* demods/tuners */ ++#include "stv6110x.h" ++#include "stv090x.h" ++#include "lnbh24.h" ++#include "lgdt330x.h" ++#include "mt2131.h" ++#include "tda18271c2dd.h" ++#include "drxk.h" ++#include "drxd.h" ++#include "dvb-pll.h" ++ ++ ++/****************************************************************************/ ++/* Demod/tuner attachment ***************************************************/ ++/****************************************************************************/ ++ ++static int tuner_attach_stv6110(struct ngene_channel *chan) ++{ ++ struct i2c_adapter *i2c; ++ struct stv090x_config *feconf = (struct stv090x_config *) ++ chan->dev->card_info->fe_config[chan->number]; ++ struct stv6110x_config *tunerconf = (struct stv6110x_config *) ++ chan->dev->card_info->tuner_config[chan->number]; ++ struct stv6110x_devctl *ctl; ++ ++ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ ++ if (chan->number < 2) ++ i2c = &chan->dev->channel[0].i2c_adapter; ++ else ++ i2c = &chan->dev->channel[1].i2c_adapter; ++ ++ ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, i2c); ++ if (ctl == NULL) { ++ printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); ++ return -ENODEV; ++ } ++ ++ feconf->tuner_init = ctl->tuner_init; ++ feconf->tuner_sleep = ctl->tuner_sleep; ++ feconf->tuner_set_mode = ctl->tuner_set_mode; ++ feconf->tuner_set_frequency = ctl->tuner_set_frequency; ++ feconf->tuner_get_frequency = ctl->tuner_get_frequency; ++ feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; ++ feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; ++ feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; ++ feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; ++ feconf->tuner_set_refclk = ctl->tuner_set_refclk; ++ feconf->tuner_get_status = ctl->tuner_get_status; ++ ++ return 0; ++} ++ ++ ++static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) ++{ ++ struct ngene_channel *chan = fe->sec_priv; ++ int status; ++ ++ if (enable) { ++ down(&chan->dev->pll_mutex); ++ status = chan->gate_ctrl(fe, 1); ++ } else { ++ status = chan->gate_ctrl(fe, 0); ++ up(&chan->dev->pll_mutex); ++ } ++ return status; ++} ++ ++static int tuner_attach_tda18271(struct ngene_channel *chan) ++{ ++ struct i2c_adapter *i2c; ++ struct dvb_frontend *fe; ++ ++ i2c = &chan->dev->channel[0].i2c_adapter; ++ if (chan->fe->ops.i2c_gate_ctrl) ++ chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); ++ fe = dvb_attach(tda18271c2dd_attach, chan->fe, i2c, 0x60); ++ if (chan->fe->ops.i2c_gate_ctrl) ++ chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); ++ if (!fe) { ++ printk(KERN_ERR "No TDA18271 found!\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static int tuner_attach_probe(struct ngene_channel *chan) ++{ ++ if (chan->demod_type == 0) ++ return tuner_attach_stv6110(chan); ++ if (chan->demod_type == 1) ++ return tuner_attach_tda18271(chan); ++ return -EINVAL; ++} ++ ++static int demod_attach_stv0900(struct ngene_channel *chan) ++{ ++ struct i2c_adapter *i2c; ++ struct stv090x_config *feconf = (struct stv090x_config *) ++ chan->dev->card_info->fe_config[chan->number]; ++ ++ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ ++ /* Note: Both adapters share the same i2c bus, but the demod */ ++ /* driver requires that each demod has its own i2c adapter */ ++ if (chan->number < 2) ++ i2c = &chan->dev->channel[0].i2c_adapter; ++ else ++ i2c = &chan->dev->channel[1].i2c_adapter; ++ ++ chan->fe = dvb_attach(stv090x_attach, feconf, i2c, ++ (chan->number & 1) == 0 ? STV090x_DEMODULATOR_0 ++ : STV090x_DEMODULATOR_1); ++ if (chan->fe == NULL) { ++ printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); ++ return -ENODEV; ++ } ++ ++ /* store channel info */ ++ if (feconf->tuner_i2c_lock) ++ chan->fe->analog_demod_priv = chan; ++ ++ if (!dvb_attach(lnbh24_attach, chan->fe, i2c, 0, ++ 0, chan->dev->card_info->lnb[chan->number])) { ++ printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); ++ dvb_frontend_detach(chan->fe); ++ chan->fe = NULL; ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void cineS2_tuner_i2c_lock(struct dvb_frontend *fe, int lock) ++{ ++ struct ngene_channel *chan = fe->analog_demod_priv; ++ ++ if (lock) ++ down(&chan->dev->pll_mutex); ++ else ++ up(&chan->dev->pll_mutex); ++} ++ ++static int i2c_read(struct i2c_adapter *adapter, u8 adr, u8 *val) ++{ ++ struct i2c_msg msgs[1] = {{.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1 } }; ++ return (i2c_transfer(adapter, msgs, 1) == 1) ? 0 : -1; ++} ++ ++static int i2c_read_reg16(struct i2c_adapter *adapter, u8 adr, ++ u16 reg, u8 *val) ++{ ++ u8 msg[2] = {reg>>8, reg&0xff}; ++ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, ++ .buf = msg, .len = 2}, ++ {.addr = adr, .flags = I2C_M_RD, ++ .buf = val, .len = 1} }; ++ return (i2c_transfer(adapter, msgs, 2) == 2) ? 0 : -1; ++} ++ ++static int port_has_stv0900(struct i2c_adapter *i2c, int port) ++{ ++ u8 val; ++ if (i2c_read_reg16(i2c, 0x68+port/2, 0xf100, &val) < 0) ++ return 0; ++ return 1; ++} ++ ++static int port_has_drxk(struct i2c_adapter *i2c, int port) ++{ ++ u8 val; ++ ++ if (i2c_read(i2c, 0x29+port, &val) < 0) ++ return 0; ++ return 1; ++} ++ ++static int demod_attach_drxk(struct ngene_channel *chan, ++ struct i2c_adapter *i2c) ++{ ++ struct drxk_config config; ++ ++ memset(&config, 0, sizeof(config)); ++ config.microcode_name = "drxk_a3.mc"; ++ config.qam_demod_parameter_count = 4; ++ config.adr = 0x29 + (chan->number ^ 2); ++ ++ chan->fe = dvb_attach(drxk_attach, &config, i2c); ++ if (!chan->fe) { ++ printk(KERN_ERR "No DRXK found!\n"); ++ return -ENODEV; ++ } ++ chan->fe->sec_priv = chan; ++ chan->gate_ctrl = chan->fe->ops.i2c_gate_ctrl; ++ chan->fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; ++ return 0; ++} ++ ++static int cineS2_probe(struct ngene_channel *chan) ++{ ++ struct i2c_adapter *i2c; ++ struct stv090x_config *fe_conf; ++ u8 buf[3]; ++ struct i2c_msg i2c_msg = { .flags = 0, .buf = buf }; ++ int rc; ++ ++ /* tuner 1+2: i2c adapter #0, tuner 3+4: i2c adapter #1 */ ++ if (chan->number < 2) ++ i2c = &chan->dev->channel[0].i2c_adapter; ++ else ++ i2c = &chan->dev->channel[1].i2c_adapter; ++ ++ if (port_has_stv0900(i2c, chan->number)) { ++ chan->demod_type = 0; ++ fe_conf = chan->dev->card_info->fe_config[chan->number]; ++ /* demod found, attach it */ ++ rc = demod_attach_stv0900(chan); ++ if (rc < 0 || chan->number < 2) ++ return rc; ++ ++ /* demod #2: reprogram outputs DPN1 & DPN2 */ ++ i2c_msg.addr = fe_conf->address; ++ i2c_msg.len = 3; ++ buf[0] = 0xf1; ++ switch (chan->number) { ++ case 2: ++ buf[1] = 0x5c; ++ buf[2] = 0xc2; ++ break; ++ case 3: ++ buf[1] = 0x61; ++ buf[2] = 0xcc; ++ break; ++ default: ++ return -ENODEV; ++ } ++ rc = i2c_transfer(i2c, &i2c_msg, 1); ++ if (rc != 1) { ++ printk(KERN_ERR DEVICE_NAME ": could not setup DPNx\n"); ++ return -EIO; ++ } ++ } else if (port_has_drxk(i2c, chan->number^2)) { ++ chan->demod_type = 1; ++ demod_attach_drxk(chan, i2c); ++ } else { ++ printk(KERN_ERR "No demod found on chan %d\n", chan->number); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++ ++static struct lgdt330x_config aver_m780 = { ++ .demod_address = 0xb2 >> 1, ++ .demod_chip = LGDT3303, ++ .serial_mpeg = 0x00, /* PARALLEL */ ++ .clock_polarity_flip = 1, ++}; ++ ++static struct mt2131_config m780_tunerconfig = { ++ 0xc0 >> 1 ++}; ++ ++/* A single func to attach the demo and tuner, rather than ++ * use two sep funcs like the current design mandates. ++ */ ++static int demod_attach_lg330x(struct ngene_channel *chan) ++{ ++ chan->fe = dvb_attach(lgdt330x_attach, &aver_m780, &chan->i2c_adapter); ++ if (chan->fe == NULL) { ++ printk(KERN_ERR DEVICE_NAME ": No LGDT330x found!\n"); ++ return -ENODEV; ++ } ++ ++ dvb_attach(mt2131_attach, chan->fe, &chan->i2c_adapter, ++ &m780_tunerconfig, 0); ++ ++ return (chan->fe) ? 0 : -ENODEV; ++} ++ ++static int demod_attach_drxd(struct ngene_channel *chan) ++{ ++ struct drxd_config *feconf; ++ ++ feconf = chan->dev->card_info->fe_config[chan->number]; ++ ++ chan->fe = dvb_attach(drxd_attach, feconf, chan, ++ &chan->i2c_adapter, &chan->dev->pci_dev->dev); ++ if (!chan->fe) { ++ pr_err("No DRXD found!\n"); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++static int tuner_attach_dtt7520x(struct ngene_channel *chan) ++{ ++ struct drxd_config *feconf; ++ ++ feconf = chan->dev->card_info->fe_config[chan->number]; ++ ++ if (!dvb_attach(dvb_pll_attach, chan->fe, feconf->pll_address, ++ &chan->i2c_adapter, ++ feconf->pll_type)) { ++ pr_err("No pll(%d) found!\n", feconf->pll_type); ++ return -ENODEV; ++ } ++ return 0; ++} ++ ++/****************************************************************************/ ++/* EEPROM TAGS **************************************************************/ ++/****************************************************************************/ ++ ++#define MICNG_EE_START 0x0100 ++#define MICNG_EE_END 0x0FF0 ++ ++#define MICNG_EETAG_END0 0x0000 ++#define MICNG_EETAG_END1 0xFFFF ++ ++/* 0x0001 - 0x000F reserved for housekeeping */ ++/* 0xFFFF - 0xFFFE reserved for housekeeping */ ++ ++/* Micronas assigned tags ++ EEProm tags for hardware support */ ++ ++#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ ++#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ ++ ++#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ ++#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ ++ ++/* Tag range for OEMs */ ++ ++#define MICNG_EETAG_OEM_FIRST 0xC000 ++#define MICNG_EETAG_OEM_LAST 0xFFEF ++ ++static int i2c_write_eeprom(struct i2c_adapter *adapter, ++ u8 adr, u16 reg, u8 data) ++{ ++ u8 m[3] = {(reg >> 8), (reg & 0xff), data}; ++ struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, ++ .len = sizeof(m)}; ++ ++ if (i2c_transfer(adapter, &msg, 1) != 1) { ++ pr_err(DEVICE_NAME ": Error writing EEPROM!\n"); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int i2c_read_eeprom(struct i2c_adapter *adapter, ++ u8 adr, u16 reg, u8 *data, int len) ++{ ++ u8 msg[2] = {(reg >> 8), (reg & 0xff)}; ++ struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, ++ .buf = msg, .len = 2 }, ++ {.addr = adr, .flags = I2C_M_RD, ++ .buf = data, .len = len} }; ++ ++ if (i2c_transfer(adapter, msgs, 2) != 2) { ++ pr_err(DEVICE_NAME ": Error reading EEPROM\n"); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int ReadEEProm(struct i2c_adapter *adapter, ++ u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) ++{ ++ int status = 0; ++ u16 Addr = MICNG_EE_START, Length, tag = 0; ++ u8 EETag[3]; ++ ++ while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { ++ if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) ++ return -1; ++ tag = (EETag[0] << 8) | EETag[1]; ++ if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) ++ return -1; ++ if (tag == Tag) ++ break; ++ Addr += sizeof(u16) + 1 + EETag[2]; ++ } ++ if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { ++ pr_err(DEVICE_NAME ++ ": Reached EOEE @ Tag = %04x Length = %3d\n", ++ tag, EETag[2]); ++ return -1; ++ } ++ Length = EETag[2]; ++ if (Length > MaxLen) ++ Length = (u16) MaxLen; ++ if (Length > 0) { ++ Addr += sizeof(u16) + 1; ++ status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); ++ if (!status) { ++ *pLength = EETag[2]; ++#if 0 ++ if (Length < EETag[2]) ++ status = STATUS_BUFFER_OVERFLOW; ++#endif ++ } ++ } ++ return status; ++} ++ ++static int WriteEEProm(struct i2c_adapter *adapter, ++ u16 Tag, u32 Length, u8 *data) ++{ ++ int status = 0; ++ u16 Addr = MICNG_EE_START; ++ u8 EETag[3]; ++ u16 tag = 0; ++ int retry, i; ++ ++ while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { ++ if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) ++ return -1; ++ tag = (EETag[0] << 8) | EETag[1]; ++ if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) ++ return -1; ++ if (tag == Tag) ++ break; ++ Addr += sizeof(u16) + 1 + EETag[2]; ++ } ++ if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { ++ pr_err(DEVICE_NAME ++ ": Reached EOEE @ Tag = %04x Length = %3d\n", ++ tag, EETag[2]); ++ return -1; ++ } ++ ++ if (Length > EETag[2]) ++ return -EINVAL; ++ /* Note: We write the data one byte at a time to avoid ++ issues with page sizes. (which are different for ++ each manufacture and eeprom size) ++ */ ++ Addr += sizeof(u16) + 1; ++ for (i = 0; i < Length; i++, Addr++) { ++ status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); ++ ++ if (status) ++ break; ++ ++ /* Poll for finishing write cycle */ ++ retry = 10; ++ while (retry) { ++ u8 Tmp; ++ ++ msleep(50); ++ status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); ++ if (status) ++ break; ++ if (Tmp != data[i]) ++ pr_err(DEVICE_NAME ++ "eeprom write error\n"); ++ retry -= 1; ++ } ++ if (status) { ++ pr_err(DEVICE_NAME ++ ": Timeout polling eeprom\n"); ++ break; ++ } ++ } ++ return status; ++} ++ ++static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) ++{ ++ int stat; ++ u8 buf[2]; ++ u32 len = 0; ++ ++ stat = ReadEEProm(adapter, tag, 2, buf, &len); ++ if (stat) ++ return stat; ++ if (len != 2) ++ return -EINVAL; ++ ++ *data = (buf[0] << 8) | buf[1]; ++ return 0; ++} ++ ++static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) ++{ ++ int stat; ++ u8 buf[2]; ++ ++ buf[0] = data >> 8; ++ buf[1] = data & 0xff; ++ stat = WriteEEProm(adapter, tag, 2, buf); ++ if (stat) ++ return stat; ++ return 0; ++} ++ ++static s16 osc_deviation(void *priv, s16 deviation, int flag) ++{ ++ struct ngene_channel *chan = priv; ++ struct i2c_adapter *adap = &chan->i2c_adapter; ++ u16 data = 0; ++ ++ if (flag) { ++ data = (u16) deviation; ++ pr_info(DEVICE_NAME ": write deviation %d\n", ++ deviation); ++ eeprom_write_ushort(adap, 0x1000 + chan->number, data); ++ } else { ++ if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) ++ data = 0; ++ pr_info(DEVICE_NAME ": read deviation %d\n", ++ (s16) data); ++ } ++ ++ return (s16) data; ++} ++ ++/****************************************************************************/ ++/* Switch control (I2C gates, etc.) *****************************************/ ++/****************************************************************************/ ++ ++ ++static struct stv090x_config fe_cineS2 = { ++ .device = STV0900, ++ .demod_mode = STV090x_DUAL, ++ .clk_mode = STV090x_CLK_EXT, ++ ++ .xtal = 27000000, ++ .address = 0x68, ++ ++ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ ++ .repeater_level = STV090x_RPTLEVEL_16, ++ ++ .adc1_range = STV090x_ADC_1Vpp, ++ .adc2_range = STV090x_ADC_1Vpp, ++ ++ .diseqc_envelope_mode = true, ++ ++ .tuner_i2c_lock = cineS2_tuner_i2c_lock, ++}; ++ ++static struct stv090x_config fe_cineS2_2 = { ++ .device = STV0900, ++ .demod_mode = STV090x_DUAL, ++ .clk_mode = STV090x_CLK_EXT, ++ ++ .xtal = 27000000, ++ .address = 0x69, ++ ++ .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, ++ ++ .repeater_level = STV090x_RPTLEVEL_16, ++ ++ .adc1_range = STV090x_ADC_1Vpp, ++ .adc2_range = STV090x_ADC_1Vpp, ++ ++ .diseqc_envelope_mode = true, ++ ++ .tuner_i2c_lock = cineS2_tuner_i2c_lock, ++}; ++ ++static struct stv6110x_config tuner_cineS2_0 = { ++ .addr = 0x60, ++ .refclk = 27000000, ++ .clk_div = 1, ++}; ++ ++static struct stv6110x_config tuner_cineS2_1 = { ++ .addr = 0x63, ++ .refclk = 27000000, ++ .clk_div = 1, ++}; ++ ++static struct ngene_info ngene_info_cineS2 = { ++ .type = NGENE_SIDEWINDER, ++ .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, ++ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, ++ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, ++ .fe_config = {&fe_cineS2, &fe_cineS2}, ++ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, ++ .lnb = {0x0b, 0x08}, ++ .tsf = {3, 3}, ++ .fw_version = 18, ++ .msi_supported = true, ++}; ++ ++static struct ngene_info ngene_info_satixS2 = { ++ .type = NGENE_SIDEWINDER, ++ .name = "Mystique SaTiX-S2 Dual", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, ++ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, ++ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, ++ .fe_config = {&fe_cineS2, &fe_cineS2}, ++ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, ++ .lnb = {0x0b, 0x08}, ++ .tsf = {3, 3}, ++ .fw_version = 18, ++ .msi_supported = true, ++}; ++ ++static struct ngene_info ngene_info_satixS2v2 = { ++ .type = NGENE_SIDEWINDER, ++ .name = "Mystique SaTiX-S2 Dual (v2)", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, ++ NGENE_IO_TSOUT}, ++ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, ++ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, ++ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, ++ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, ++ .lnb = {0x0a, 0x08, 0x0b, 0x09}, ++ .tsf = {3, 3}, ++ .fw_version = 18, ++ .msi_supported = true, ++}; ++ ++static struct ngene_info ngene_info_cineS2v5 = { ++ .type = NGENE_SIDEWINDER, ++ .name = "Linux4Media cineS2 DVB-S2 Twin Tuner (v5)", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, ++ NGENE_IO_TSOUT}, ++ .demod_attach = {demod_attach_stv0900, demod_attach_stv0900, cineS2_probe, cineS2_probe}, ++ .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110, tuner_attach_probe, tuner_attach_probe}, ++ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, ++ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, ++ .lnb = {0x0a, 0x08, 0x0b, 0x09}, ++ .tsf = {3, 3}, ++ .fw_version = 18, ++ .msi_supported = true, ++}; ++ ++ ++static struct ngene_info ngene_info_duoFlex = { ++ .type = NGENE_SIDEWINDER, ++ .name = "Digital Devices DuoFlex PCIe or miniPCIe", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, NGENE_IO_TSIN, ++ NGENE_IO_TSOUT}, ++ .demod_attach = {cineS2_probe, cineS2_probe, cineS2_probe, cineS2_probe}, ++ .tuner_attach = {tuner_attach_probe, tuner_attach_probe, tuner_attach_probe, tuner_attach_probe}, ++ .fe_config = {&fe_cineS2, &fe_cineS2, &fe_cineS2_2, &fe_cineS2_2}, ++ .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1, &tuner_cineS2_0, &tuner_cineS2_1}, ++ .lnb = {0x0a, 0x08, 0x0b, 0x09}, ++ .tsf = {3, 3}, ++ .fw_version = 18, ++ .msi_supported = true, ++}; ++ ++static struct ngene_info ngene_info_m780 = { ++ .type = NGENE_APP, ++ .name = "Aver M780 ATSC/QAM-B", ++ ++ /* Channel 0 is analog, which is currently unsupported */ ++ .io_type = { NGENE_IO_NONE, NGENE_IO_TSIN }, ++ .demod_attach = { NULL, demod_attach_lg330x }, ++ ++ /* Ensure these are NULL else the frame will call them (as funcs) */ ++ .tuner_attach = { 0, 0, 0, 0 }, ++ .fe_config = { NULL, &aver_m780 }, ++ .avf = { 0 }, ++ ++ /* A custom electrical interface config for the demod to bridge */ ++ .tsf = { 4, 4 }, ++ .fw_version = 15, ++}; ++ ++static struct drxd_config fe_terratec_dvbt_0 = { ++ .index = 0, ++ .demod_address = 0x70, ++ .demod_revision = 0xa2, ++ .demoda_address = 0x00, ++ .pll_address = 0x60, ++ .pll_type = DVB_PLL_THOMSON_DTT7520X, ++ .clock = 20000, ++ .osc_deviation = osc_deviation, ++}; ++ ++static struct drxd_config fe_terratec_dvbt_1 = { ++ .index = 1, ++ .demod_address = 0x71, ++ .demod_revision = 0xa2, ++ .demoda_address = 0x00, ++ .pll_address = 0x60, ++ .pll_type = DVB_PLL_THOMSON_DTT7520X, ++ .clock = 20000, ++ .osc_deviation = osc_deviation, ++}; ++ ++static struct ngene_info ngene_info_terratec = { ++ .type = NGENE_TERRATEC, ++ .name = "Terratec Integra/Cinergy2400i Dual DVB-T", ++ .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, ++ .demod_attach = {demod_attach_drxd, demod_attach_drxd}, ++ .tuner_attach = {tuner_attach_dtt7520x, tuner_attach_dtt7520x}, ++ .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, ++ .i2c_access = 1, ++}; ++ ++/****************************************************************************/ ++ ++ ++ ++/****************************************************************************/ ++/* PCI Subsystem ID *********************************************************/ ++/****************************************************************************/ ++ ++#define NGENE_ID(_subvend, _subdev, _driverdata) { \ ++ .vendor = NGENE_VID, .device = NGENE_PID, \ ++ .subvendor = _subvend, .subdevice = _subdev, \ ++ .driver_data = (unsigned long) &_driverdata } ++ ++/****************************************************************************/ ++ ++static const struct pci_device_id ngene_id_tbl[] __devinitdata = { ++ NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), ++ NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), ++ NGENE_ID(0x18c3, 0xdb01, ngene_info_satixS2), ++ NGENE_ID(0x18c3, 0xdb02, ngene_info_satixS2v2), ++ NGENE_ID(0x18c3, 0xdd00, ngene_info_cineS2v5), ++ NGENE_ID(0x18c3, 0xdd10, ngene_info_duoFlex), ++ NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), ++ NGENE_ID(0x1461, 0x062e, ngene_info_m780), ++ NGENE_ID(0x153b, 0x1167, ngene_info_terratec), ++ {0} ++}; ++MODULE_DEVICE_TABLE(pci, ngene_id_tbl); ++ ++/****************************************************************************/ ++/* Init/Exit ****************************************************************/ ++/****************************************************************************/ ++ ++static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, ++ enum pci_channel_state state) ++{ ++ printk(KERN_ERR DEVICE_NAME ": PCI error\n"); ++ if (state == pci_channel_io_perm_failure) ++ return PCI_ERS_RESULT_DISCONNECT; ++ if (state == pci_channel_io_frozen) ++ return PCI_ERS_RESULT_NEED_RESET; ++ return PCI_ERS_RESULT_CAN_RECOVER; ++} ++ ++static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) ++{ ++ printk(KERN_INFO DEVICE_NAME ": link reset\n"); ++ return 0; ++} ++ ++static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) ++{ ++ printk(KERN_INFO DEVICE_NAME ": slot reset\n"); ++ return 0; ++} ++ ++static void ngene_resume(struct pci_dev *dev) ++{ ++ printk(KERN_INFO DEVICE_NAME ": resume\n"); ++} ++ ++static const struct pci_error_handlers ngene_errors = { ++ .error_detected = ngene_error_detected, ++ .link_reset = ngene_link_reset, ++ .slot_reset = ngene_slot_reset, ++ .resume = ngene_resume, ++}; ++ ++static struct pci_driver ngene_pci_driver = { ++ .name = "ngene", ++ .id_table = ngene_id_tbl, ++ .probe = ngene_probe, ++ .remove = __devexit_p(ngene_remove), ++ .err_handler = &ngene_errors, ++ .shutdown = ngene_shutdown, ++}; ++ ++static __init int module_init_ngene(void) ++{ ++ printk(KERN_INFO ++ "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); ++ return pci_register_driver(&ngene_pci_driver); ++} ++ ++static __exit void module_exit_ngene(void) ++{ ++ pci_unregister_driver(&ngene_pci_driver); ++} ++ ++module_init(module_init_ngene); ++module_exit(module_exit_ngene); ++ ++MODULE_DESCRIPTION("nGene"); ++MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c +new file mode 100644 +index 0000000..8eeec4f +--- /dev/null ++++ b/drivers/media/pci/ngene/ngene-core.c +@@ -0,0 +1,1708 @@ ++/* ++ * ngene.c: nGene PCIe bridge driver ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * Copyright (C) 2008-2009 Ralph Metzler ++ * Modifications for new nGene firmware, ++ * support for EEPROM-copying, ++ * support for new dual DVB-S2 card prototype ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ngene.h" ++ ++static int one_adapter; ++module_param(one_adapter, int, 0444); ++MODULE_PARM_DESC(one_adapter, "Use only one adapter."); ++ ++static int shutdown_workaround; ++module_param(shutdown_workaround, int, 0644); ++MODULE_PARM_DESC(shutdown_workaround, "Activate workaround for shutdown problem with some chipsets."); ++ ++static int debug; ++module_param(debug, int, 0444); ++MODULE_PARM_DESC(debug, "Print debugging information."); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define dprintk if (debug) printk ++ ++#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) ++#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) ++#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) ++#define ngreadl(adr) readl(dev->iomem + (adr)) ++#define ngreadb(adr) readb(dev->iomem + (adr)) ++#define ngcpyto(adr, src, count) memcpy_toio((char *) \ ++ (dev->iomem + (adr)), (src), (count)) ++#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ ++ (dev->iomem + (adr)), (count)) ++ ++/****************************************************************************/ ++/* nGene interrupt handler **************************************************/ ++/****************************************************************************/ ++ ++static void event_tasklet(unsigned long data) ++{ ++ struct ngene *dev = (struct ngene *)data; ++ ++ while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { ++ struct EVENT_BUFFER Event = ++ dev->EventQueue[dev->EventQueueReadIndex]; ++ dev->EventQueueReadIndex = ++ (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1); ++ ++ if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify)) ++ dev->TxEventNotify(dev, Event.TimeStamp); ++ if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify)) ++ dev->RxEventNotify(dev, Event.TimeStamp, ++ Event.RXCharacter); ++ } ++} ++ ++static void demux_tasklet(unsigned long data) ++{ ++ struct ngene_channel *chan = (struct ngene_channel *)data; ++ struct SBufferHeader *Cur = chan->nextBuffer; ++ ++ spin_lock_irq(&chan->state_lock); ++ ++ while (Cur->ngeneBuffer.SR.Flags & 0x80) { ++ if (chan->mode & NGENE_IO_TSOUT) { ++ u32 Flags = chan->DataFormatFlags; ++ if (Cur->ngeneBuffer.SR.Flags & 0x20) ++ Flags |= BEF_OVERFLOW; ++ if (chan->pBufferExchange) { ++ if (!chan->pBufferExchange(chan, ++ Cur->Buffer1, ++ chan->Capture1Length, ++ Cur->ngeneBuffer.SR. ++ Clock, Flags)) { ++ /* ++ We didn't get data ++ Clear in service flag to make sure we ++ get called on next interrupt again. ++ leave fill/empty (0x80) flag alone ++ to avoid hardware running out of ++ buffers during startup, we hold only ++ in run state ( the source may be late ++ delivering data ) ++ */ ++ ++ if (chan->HWState == HWSTATE_RUN) { ++ Cur->ngeneBuffer.SR.Flags &= ++ ~0x40; ++ break; ++ /* Stop processing stream */ ++ } ++ } else { ++ /* We got a valid buffer, ++ so switch to run state */ ++ chan->HWState = HWSTATE_RUN; ++ } ++ } else { ++ printk(KERN_ERR DEVICE_NAME ": OOPS\n"); ++ if (chan->HWState == HWSTATE_RUN) { ++ Cur->ngeneBuffer.SR.Flags &= ~0x40; ++ break; /* Stop processing stream */ ++ } ++ } ++ if (chan->AudioDTOUpdated) { ++ printk(KERN_INFO DEVICE_NAME ++ ": Update AudioDTO = %d\n", ++ chan->AudioDTOValue); ++ Cur->ngeneBuffer.SR.DTOUpdate = ++ chan->AudioDTOValue; ++ chan->AudioDTOUpdated = 0; ++ } ++ } else { ++ if (chan->HWState == HWSTATE_RUN) { ++ u32 Flags = chan->DataFormatFlags; ++ IBufferExchange *exch1 = chan->pBufferExchange; ++ IBufferExchange *exch2 = chan->pBufferExchange2; ++ if (Cur->ngeneBuffer.SR.Flags & 0x01) ++ Flags |= BEF_EVEN_FIELD; ++ if (Cur->ngeneBuffer.SR.Flags & 0x20) ++ Flags |= BEF_OVERFLOW; ++ spin_unlock_irq(&chan->state_lock); ++ if (exch1) ++ exch1(chan, Cur->Buffer1, ++ chan->Capture1Length, ++ Cur->ngeneBuffer.SR.Clock, ++ Flags); ++ if (exch2) ++ exch2(chan, Cur->Buffer2, ++ chan->Capture2Length, ++ Cur->ngeneBuffer.SR.Clock, ++ Flags); ++ spin_lock_irq(&chan->state_lock); ++ } else if (chan->HWState != HWSTATE_STOP) ++ chan->HWState = HWSTATE_RUN; ++ } ++ Cur->ngeneBuffer.SR.Flags = 0x00; ++ Cur = Cur->Next; ++ } ++ chan->nextBuffer = Cur; ++ ++ spin_unlock_irq(&chan->state_lock); ++} ++ ++static irqreturn_t irq_handler(int irq, void *dev_id) ++{ ++ struct ngene *dev = (struct ngene *)dev_id; ++ u32 icounts = 0; ++ irqreturn_t rc = IRQ_NONE; ++ u32 i = MAX_STREAM; ++ u8 *tmpCmdDoneByte; ++ ++ if (dev->BootFirmware) { ++ icounts = ngreadl(NGENE_INT_COUNTS); ++ if (icounts != dev->icounts) { ++ ngwritel(0, FORCE_NMI); ++ dev->cmd_done = 1; ++ wake_up(&dev->cmd_wq); ++ dev->icounts = icounts; ++ rc = IRQ_HANDLED; ++ } ++ return rc; ++ } ++ ++ ngwritel(0, FORCE_NMI); ++ ++ spin_lock(&dev->cmd_lock); ++ tmpCmdDoneByte = dev->CmdDoneByte; ++ if (tmpCmdDoneByte && ++ (*tmpCmdDoneByte || ++ (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) { ++ dev->CmdDoneByte = NULL; ++ dev->cmd_done = 1; ++ wake_up(&dev->cmd_wq); ++ rc = IRQ_HANDLED; ++ } ++ spin_unlock(&dev->cmd_lock); ++ ++ if (dev->EventBuffer->EventStatus & 0x80) { ++ u8 nextWriteIndex = ++ (dev->EventQueueWriteIndex + 1) & ++ (EVENT_QUEUE_SIZE - 1); ++ if (nextWriteIndex != dev->EventQueueReadIndex) { ++ dev->EventQueue[dev->EventQueueWriteIndex] = ++ *(dev->EventBuffer); ++ dev->EventQueueWriteIndex = nextWriteIndex; ++ } else { ++ printk(KERN_ERR DEVICE_NAME ": event overflow\n"); ++ dev->EventQueueOverflowCount += 1; ++ dev->EventQueueOverflowFlag = 1; ++ } ++ dev->EventBuffer->EventStatus &= ~0x80; ++ tasklet_schedule(&dev->event_tasklet); ++ rc = IRQ_HANDLED; ++ } ++ ++ while (i > 0) { ++ i--; ++ spin_lock(&dev->channel[i].state_lock); ++ /* if (dev->channel[i].State>=KSSTATE_RUN) { */ ++ if (dev->channel[i].nextBuffer) { ++ if ((dev->channel[i].nextBuffer-> ++ ngeneBuffer.SR.Flags & 0xC0) == 0x80) { ++ dev->channel[i].nextBuffer-> ++ ngeneBuffer.SR.Flags |= 0x40; ++ tasklet_schedule( ++ &dev->channel[i].demux_tasklet); ++ rc = IRQ_HANDLED; ++ } ++ } ++ spin_unlock(&dev->channel[i].state_lock); ++ } ++ ++ /* Request might have been processed by a previous call. */ ++ return IRQ_HANDLED; ++} ++ ++/****************************************************************************/ ++/* nGene command interface **************************************************/ ++/****************************************************************************/ ++ ++static void dump_command_io(struct ngene *dev) ++{ ++ u8 buf[8], *b; ++ ++ ngcpyfrom(buf, HOST_TO_NGENE, 8); ++ printk(KERN_ERR "host_to_ngene (%04x): %*ph\n", HOST_TO_NGENE, 8, buf); ++ ++ ngcpyfrom(buf, NGENE_TO_HOST, 8); ++ printk(KERN_ERR "ngene_to_host (%04x): %*ph\n", NGENE_TO_HOST, 8, buf); ++ ++ b = dev->hosttongene; ++ printk(KERN_ERR "dev->hosttongene (%p): %*ph\n", b, 8, b); ++ ++ b = dev->ngenetohost; ++ printk(KERN_ERR "dev->ngenetohost (%p): %*ph\n", b, 8, b); ++} ++ ++static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) ++{ ++ int ret; ++ u8 *tmpCmdDoneByte; ++ ++ dev->cmd_done = 0; ++ ++ if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) { ++ dev->BootFirmware = 1; ++ dev->icounts = ngreadl(NGENE_INT_COUNTS); ++ ngwritel(0, NGENE_COMMAND); ++ ngwritel(0, NGENE_COMMAND_HI); ++ ngwritel(0, NGENE_STATUS); ++ ngwritel(0, NGENE_STATUS_HI); ++ ngwritel(0, NGENE_EVENT); ++ ngwritel(0, NGENE_EVENT_HI); ++ } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) { ++ u64 fwio = dev->PAFWInterfaceBuffer; ++ ++ ngwritel(fwio & 0xffffffff, NGENE_COMMAND); ++ ngwritel(fwio >> 32, NGENE_COMMAND_HI); ++ ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS); ++ ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI); ++ ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT); ++ ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI); ++ } ++ ++ memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2); ++ ++ if (dev->BootFirmware) ++ ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2); ++ ++ spin_lock_irq(&dev->cmd_lock); ++ tmpCmdDoneByte = dev->ngenetohost + com->out_len; ++ if (!com->out_len) ++ tmpCmdDoneByte++; ++ *tmpCmdDoneByte = 0; ++ dev->ngenetohost[0] = 0; ++ dev->ngenetohost[1] = 0; ++ dev->CmdDoneByte = tmpCmdDoneByte; ++ spin_unlock_irq(&dev->cmd_lock); ++ ++ /* Notify 8051. */ ++ ngwritel(1, FORCE_INT); ++ ++ ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ); ++ if (!ret) { ++ /*ngwritel(0, FORCE_NMI);*/ ++ ++ printk(KERN_ERR DEVICE_NAME ++ ": Command timeout cmd=%02x prev=%02x\n", ++ com->cmd.hdr.Opcode, dev->prev_cmd); ++ dump_command_io(dev); ++ return -1; ++ } ++ if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) ++ dev->BootFirmware = 0; ++ ++ dev->prev_cmd = com->cmd.hdr.Opcode; ++ ++ if (!com->out_len) ++ return 0; ++ ++ memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len); ++ ++ return 0; ++} ++ ++int ngene_command(struct ngene *dev, struct ngene_command *com) ++{ ++ int result; ++ ++ down(&dev->cmd_mutex); ++ result = ngene_command_mutex(dev, com); ++ up(&dev->cmd_mutex); ++ return result; ++} ++ ++ ++static int ngene_command_load_firmware(struct ngene *dev, ++ u8 *ngene_fw, u32 size) ++{ ++#define FIRSTCHUNK (1024) ++ u32 cleft; ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE; ++ com.cmd.hdr.Length = 0; ++ com.in_len = 0; ++ com.out_len = 0; ++ ++ ngene_command(dev, &com); ++ ++ cleft = (size + 3) & ~3; ++ if (cleft > FIRSTCHUNK) { ++ ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK, ++ cleft - FIRSTCHUNK); ++ cleft = FIRSTCHUNK; ++ } ++ ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); ++ ++ memset(&com, 0, sizeof(struct ngene_command)); ++ com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH; ++ com.cmd.hdr.Length = 4; ++ com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA; ++ com.cmd.FWLoadFinish.Length = (unsigned short)cleft; ++ com.in_len = 4; ++ com.out_len = 0; ++ ++ return ngene_command(dev, &com); ++} ++ ++ ++static int ngene_command_config_buf(struct ngene *dev, u8 config) ++{ ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER; ++ com.cmd.hdr.Length = 1; ++ com.cmd.ConfigureBuffers.config = config; ++ com.in_len = 1; ++ com.out_len = 0; ++ ++ if (ngene_command(dev, &com) < 0) ++ return -EIO; ++ return 0; ++} ++ ++static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) ++{ ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER; ++ com.cmd.hdr.Length = 6; ++ memcpy(&com.cmd.ConfigureBuffers.config, config, 6); ++ com.in_len = 6; ++ com.out_len = 0; ++ ++ if (ngene_command(dev, &com) < 0) ++ return -EIO; ++ ++ return 0; ++} ++ ++int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) ++{ ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN; ++ com.cmd.hdr.Length = 1; ++ com.cmd.SetGpioPin.select = select | (level << 7); ++ com.in_len = 1; ++ com.out_len = 0; ++ ++ return ngene_command(dev, &com); ++} ++ ++ ++/* ++ 02000640 is sample on rising edge. ++ 02000740 is sample on falling edge. ++ 02000040 is ignore "valid" signal ++ ++ 0: FD_CTL1 Bit 7,6 must be 0,1 ++ 7 disable(fw controlled) ++ 6 0-AUX,1-TS ++ 5 0-par,1-ser ++ 4 0-lsb/1-msb ++ 3,2 reserved ++ 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both ++ 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge ++ 2: FD_STA is read-only. 0-sync ++ 3: FD_INSYNC is number of 47s to trigger "in sync". ++ 4: FD_OUTSYNC is number of 47s to trigger "out of sync". ++ 5: FD_MAXBYTE1 is low-order of bytes per packet. ++ 6: FD_MAXBYTE2 is high-order of bytes per packet. ++ 7: Top byte is unused. ++*/ ++ ++/****************************************************************************/ ++ ++static u8 TSFeatureDecoderSetup[8 * 5] = { ++ 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, ++ 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ ++ 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ ++ 0x72, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ ++ 0x40, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* LGDT3303 */ ++}; ++ ++/* Set NGENE I2S Config to 16 bit packed */ ++static u8 I2SConfiguration[] = { ++ 0x00, 0x10, 0x00, 0x00, ++ 0x80, 0x10, 0x00, 0x00, ++}; ++ ++static u8 SPDIFConfiguration[10] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ++}; ++ ++/* Set NGENE I2S Config to transport stream compatible mode */ ++ ++static u8 TS_I2SConfiguration[4] = { 0x3E, 0x18, 0x00, 0x00 }; ++ ++static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x04, 0x00, 0x00 }; ++ ++static u8 ITUDecoderSetup[4][16] = { ++ {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ ++ 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00}, ++ {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, ++ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, ++ {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */ ++ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, ++ {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */ ++ 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, ++}; ++ ++/* ++ * 50 48 60 gleich ++ * 27p50 9f 00 22 80 42 69 18 ... ++ * 27p60 93 00 22 80 82 69 1c ... ++ */ ++ ++/* Maxbyte to 1144 (for raw data) */ ++static u8 ITUFeatureDecoderSetup[8] = { ++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 ++}; ++ ++void FillTSBuffer(void *Buffer, int Length, u32 Flags) ++{ ++ u32 *ptr = Buffer; ++ ++ memset(Buffer, TS_FILLER, Length); ++ while (Length > 0) { ++ if (Flags & DF_SWAP32) ++ *ptr = 0x471FFF10; ++ else ++ *ptr = 0x10FF1F47; ++ ptr += (188 / 4); ++ Length -= 188; ++ } ++} ++ ++ ++static void flush_buffers(struct ngene_channel *chan) ++{ ++ u8 val; ++ ++ do { ++ msleep(1); ++ spin_lock_irq(&chan->state_lock); ++ val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80; ++ spin_unlock_irq(&chan->state_lock); ++ } while (val); ++} ++ ++static void clear_buffers(struct ngene_channel *chan) ++{ ++ struct SBufferHeader *Cur = chan->nextBuffer; ++ ++ do { ++ memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); ++ if (chan->mode & NGENE_IO_TSOUT) ++ FillTSBuffer(Cur->Buffer1, ++ chan->Capture1Length, ++ chan->DataFormatFlags); ++ Cur = Cur->Next; ++ } while (Cur != chan->nextBuffer); ++ ++ if (chan->mode & NGENE_IO_TSOUT) { ++ chan->nextBuffer->ngeneBuffer.SR.DTOUpdate = ++ chan->AudioDTOValue; ++ chan->AudioDTOUpdated = 0; ++ ++ Cur = chan->TSIdleBuffer.Head; ++ ++ do { ++ memset(&Cur->ngeneBuffer.SR, 0, ++ sizeof(Cur->ngeneBuffer.SR)); ++ FillTSBuffer(Cur->Buffer1, ++ chan->Capture1Length, ++ chan->DataFormatFlags); ++ Cur = Cur->Next; ++ } while (Cur != chan->TSIdleBuffer.Head); ++ } ++} ++ ++static int ngene_command_stream_control(struct ngene *dev, u8 stream, ++ u8 control, u8 mode, u8 flags) ++{ ++ struct ngene_channel *chan = &dev->channel[stream]; ++ struct ngene_command com; ++ u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); ++ u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500); ++ u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); ++ u16 BsSDO = 0x9B00; ++ ++ down(&dev->stream_mutex); ++ memset(&com, 0, sizeof(com)); ++ com.cmd.hdr.Opcode = CMD_CONTROL; ++ com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; ++ com.cmd.StreamControl.Stream = stream | (control ? 8 : 0); ++ if (chan->mode & NGENE_IO_TSOUT) ++ com.cmd.StreamControl.Stream |= 0x07; ++ com.cmd.StreamControl.Control = control | ++ (flags & SFLAG_ORDER_LUMA_CHROMA); ++ com.cmd.StreamControl.Mode = mode; ++ com.in_len = sizeof(struct FW_STREAM_CONTROL); ++ com.out_len = 0; ++ ++ dprintk(KERN_INFO DEVICE_NAME ++ ": Stream=%02x, Control=%02x, Mode=%02x\n", ++ com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, ++ com.cmd.StreamControl.Mode); ++ ++ chan->Mode = mode; ++ ++ if (!(control & 0x80)) { ++ spin_lock_irq(&chan->state_lock); ++ if (chan->State == KSSTATE_RUN) { ++ chan->State = KSSTATE_ACQUIRE; ++ chan->HWState = HWSTATE_STOP; ++ spin_unlock_irq(&chan->state_lock); ++ if (ngene_command(dev, &com) < 0) { ++ up(&dev->stream_mutex); ++ return -1; ++ } ++ /* clear_buffers(chan); */ ++ flush_buffers(chan); ++ up(&dev->stream_mutex); ++ return 0; ++ } ++ spin_unlock_irq(&chan->state_lock); ++ up(&dev->stream_mutex); ++ return 0; ++ } ++ ++ if (mode & SMODE_AUDIO_CAPTURE) { ++ com.cmd.StreamControl.CaptureBlockCount = ++ chan->Capture1Length / AUDIO_BLOCK_SIZE; ++ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; ++ } else if (mode & SMODE_TRANSPORT_STREAM) { ++ com.cmd.StreamControl.CaptureBlockCount = ++ chan->Capture1Length / TS_BLOCK_SIZE; ++ com.cmd.StreamControl.MaxLinesPerField = ++ chan->Capture1Length / TS_BLOCK_SIZE; ++ com.cmd.StreamControl.Buffer_Address = ++ chan->TSRingBuffer.PAHead; ++ if (chan->mode & NGENE_IO_TSOUT) { ++ com.cmd.StreamControl.BytesPerVBILine = ++ chan->Capture1Length / TS_BLOCK_SIZE; ++ com.cmd.StreamControl.Stream |= 0x07; ++ } ++ } else { ++ com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine; ++ com.cmd.StreamControl.MaxLinesPerField = chan->nLines; ++ com.cmd.StreamControl.MinLinesPerField = 100; ++ com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; ++ ++ if (mode & SMODE_VBI_CAPTURE) { ++ com.cmd.StreamControl.MaxVBILinesPerField = ++ chan->nVBILines; ++ com.cmd.StreamControl.MinVBILinesPerField = 0; ++ com.cmd.StreamControl.BytesPerVBILine = ++ chan->nBytesPerVBILine; ++ } ++ if (flags & SFLAG_COLORBAR) ++ com.cmd.StreamControl.Stream |= 0x04; ++ } ++ ++ spin_lock_irq(&chan->state_lock); ++ if (mode & SMODE_AUDIO_CAPTURE) { ++ chan->nextBuffer = chan->RingBuffer.Head; ++ if (mode & SMODE_AUDIO_SPDIF) { ++ com.cmd.StreamControl.SetupDataLen = ++ sizeof(SPDIFConfiguration); ++ com.cmd.StreamControl.SetupDataAddr = BsSPI; ++ memcpy(com.cmd.StreamControl.SetupData, ++ SPDIFConfiguration, sizeof(SPDIFConfiguration)); ++ } else { ++ com.cmd.StreamControl.SetupDataLen = 4; ++ com.cmd.StreamControl.SetupDataAddr = BsSDI; ++ memcpy(com.cmd.StreamControl.SetupData, ++ I2SConfiguration + ++ 4 * dev->card_info->i2s[stream], 4); ++ } ++ } else if (mode & SMODE_TRANSPORT_STREAM) { ++ chan->nextBuffer = chan->TSRingBuffer.Head; ++ if (stream >= STREAM_AUDIOIN1) { ++ if (chan->mode & NGENE_IO_TSOUT) { ++ com.cmd.StreamControl.SetupDataLen = ++ sizeof(TS_I2SOutConfiguration); ++ com.cmd.StreamControl.SetupDataAddr = BsSDO; ++ memcpy(com.cmd.StreamControl.SetupData, ++ TS_I2SOutConfiguration, ++ sizeof(TS_I2SOutConfiguration)); ++ } else { ++ com.cmd.StreamControl.SetupDataLen = ++ sizeof(TS_I2SConfiguration); ++ com.cmd.StreamControl.SetupDataAddr = BsSDI; ++ memcpy(com.cmd.StreamControl.SetupData, ++ TS_I2SConfiguration, ++ sizeof(TS_I2SConfiguration)); ++ } ++ } else { ++ com.cmd.StreamControl.SetupDataLen = 8; ++ com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10; ++ memcpy(com.cmd.StreamControl.SetupData, ++ TSFeatureDecoderSetup + ++ 8 * dev->card_info->tsf[stream], 8); ++ } ++ } else { ++ chan->nextBuffer = chan->RingBuffer.Head; ++ com.cmd.StreamControl.SetupDataLen = ++ 16 + sizeof(ITUFeatureDecoderSetup); ++ com.cmd.StreamControl.SetupDataAddr = BsUVI; ++ memcpy(com.cmd.StreamControl.SetupData, ++ ITUDecoderSetup[chan->itumode], 16); ++ memcpy(com.cmd.StreamControl.SetupData + 16, ++ ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup)); ++ } ++ clear_buffers(chan); ++ chan->State = KSSTATE_RUN; ++ if (mode & SMODE_TRANSPORT_STREAM) ++ chan->HWState = HWSTATE_RUN; ++ else ++ chan->HWState = HWSTATE_STARTUP; ++ spin_unlock_irq(&chan->state_lock); ++ ++ if (ngene_command(dev, &com) < 0) { ++ up(&dev->stream_mutex); ++ return -1; ++ } ++ up(&dev->stream_mutex); ++ return 0; ++} ++ ++void set_transfer(struct ngene_channel *chan, int state) ++{ ++ u8 control = 0, mode = 0, flags = 0; ++ struct ngene *dev = chan->dev; ++ int ret; ++ ++ /* ++ printk(KERN_INFO DEVICE_NAME ": st %d\n", state); ++ msleep(100); ++ */ ++ ++ if (state) { ++ if (chan->running) { ++ printk(KERN_INFO DEVICE_NAME ": already running\n"); ++ return; ++ } ++ } else { ++ if (!chan->running) { ++ printk(KERN_INFO DEVICE_NAME ": already stopped\n"); ++ return; ++ } ++ } ++ ++ if (dev->card_info->switch_ctrl) ++ dev->card_info->switch_ctrl(chan, 1, state ^ 1); ++ ++ if (state) { ++ spin_lock_irq(&chan->state_lock); ++ ++ /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", ++ ngreadl(0x9310)); */ ++ dvb_ringbuffer_flush(&dev->tsout_rbuf); ++ control = 0x80; ++ if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { ++ chan->Capture1Length = 512 * 188; ++ mode = SMODE_TRANSPORT_STREAM; ++ } ++ if (chan->mode & NGENE_IO_TSOUT) { ++ chan->pBufferExchange = tsout_exchange; ++ /* 0x66666666 = 50MHz *2^33 /250MHz */ ++ chan->AudioDTOValue = 0x80000000; ++ chan->AudioDTOUpdated = 1; ++ } ++ if (chan->mode & NGENE_IO_TSIN) ++ chan->pBufferExchange = tsin_exchange; ++ spin_unlock_irq(&chan->state_lock); ++ } ++ /* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", ++ ngreadl(0x9310)); */ ++ ++ ret = ngene_command_stream_control(dev, chan->number, ++ control, mode, flags); ++ if (!ret) ++ chan->running = state; ++ else ++ printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", ++ state); ++ if (!state) { ++ spin_lock_irq(&chan->state_lock); ++ chan->pBufferExchange = NULL; ++ dvb_ringbuffer_flush(&dev->tsout_rbuf); ++ spin_unlock_irq(&chan->state_lock); ++ } ++} ++ ++ ++/****************************************************************************/ ++/* nGene hardware init and release functions ********************************/ ++/****************************************************************************/ ++ ++static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) ++{ ++ struct SBufferHeader *Cur = rb->Head; ++ u32 j; ++ ++ if (!Cur) ++ return; ++ ++ for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) { ++ if (Cur->Buffer1) ++ pci_free_consistent(dev->pci_dev, ++ rb->Buffer1Length, ++ Cur->Buffer1, ++ Cur->scList1->Address); ++ ++ if (Cur->Buffer2) ++ pci_free_consistent(dev->pci_dev, ++ rb->Buffer2Length, ++ Cur->Buffer2, ++ Cur->scList2->Address); ++ } ++ ++ if (rb->SCListMem) ++ pci_free_consistent(dev->pci_dev, rb->SCListMemSize, ++ rb->SCListMem, rb->PASCListMem); ++ ++ pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); ++} ++ ++static void free_idlebuffer(struct ngene *dev, ++ struct SRingBufferDescriptor *rb, ++ struct SRingBufferDescriptor *tb) ++{ ++ int j; ++ struct SBufferHeader *Cur = tb->Head; ++ ++ if (!rb->Head) ++ return; ++ free_ringbuffer(dev, rb); ++ for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { ++ Cur->Buffer2 = NULL; ++ Cur->scList2 = NULL; ++ Cur->ngeneBuffer.Address_of_first_entry_2 = 0; ++ Cur->ngeneBuffer.Number_of_entries_2 = 0; ++ } ++} ++ ++static void free_common_buffers(struct ngene *dev) ++{ ++ u32 i; ++ struct ngene_channel *chan; ++ ++ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { ++ chan = &dev->channel[i]; ++ free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer); ++ free_ringbuffer(dev, &chan->RingBuffer); ++ free_ringbuffer(dev, &chan->TSRingBuffer); ++ } ++ ++ if (dev->OverflowBuffer) ++ pci_free_consistent(dev->pci_dev, ++ OVERFLOW_BUFFER_SIZE, ++ dev->OverflowBuffer, dev->PAOverflowBuffer); ++ ++ if (dev->FWInterfaceBuffer) ++ pci_free_consistent(dev->pci_dev, ++ 4096, ++ dev->FWInterfaceBuffer, ++ dev->PAFWInterfaceBuffer); ++} ++ ++/****************************************************************************/ ++/* Ring buffer handling *****************************************************/ ++/****************************************************************************/ ++ ++static int create_ring_buffer(struct pci_dev *pci_dev, ++ struct SRingBufferDescriptor *descr, u32 NumBuffers) ++{ ++ dma_addr_t tmp; ++ struct SBufferHeader *Head; ++ u32 i; ++ u32 MemSize = SIZEOF_SBufferHeader * NumBuffers; ++ u64 PARingBufferHead; ++ u64 PARingBufferCur; ++ u64 PARingBufferNext; ++ struct SBufferHeader *Cur, *Next; ++ ++ descr->Head = NULL; ++ descr->MemSize = 0; ++ descr->PAHead = 0; ++ descr->NumBuffers = 0; ++ ++ if (MemSize < 4096) ++ MemSize = 4096; ++ ++ Head = pci_alloc_consistent(pci_dev, MemSize, &tmp); ++ PARingBufferHead = tmp; ++ ++ if (!Head) ++ return -ENOMEM; ++ ++ memset(Head, 0, MemSize); ++ ++ PARingBufferCur = PARingBufferHead; ++ Cur = Head; ++ ++ for (i = 0; i < NumBuffers - 1; i++) { ++ Next = (struct SBufferHeader *) ++ (((u8 *) Cur) + SIZEOF_SBufferHeader); ++ PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader; ++ Cur->Next = Next; ++ Cur->ngeneBuffer.Next = PARingBufferNext; ++ Cur = Next; ++ PARingBufferCur = PARingBufferNext; ++ } ++ /* Last Buffer points back to first one */ ++ Cur->Next = Head; ++ Cur->ngeneBuffer.Next = PARingBufferHead; ++ ++ descr->Head = Head; ++ descr->MemSize = MemSize; ++ descr->PAHead = PARingBufferHead; ++ descr->NumBuffers = NumBuffers; ++ ++ return 0; ++} ++ ++static int AllocateRingBuffers(struct pci_dev *pci_dev, ++ dma_addr_t of, ++ struct SRingBufferDescriptor *pRingBuffer, ++ u32 Buffer1Length, u32 Buffer2Length) ++{ ++ dma_addr_t tmp; ++ u32 i, j; ++ int status = 0; ++ u32 SCListMemSize = pRingBuffer->NumBuffers ++ * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : ++ NUM_SCATTER_GATHER_ENTRIES) ++ * sizeof(struct HW_SCATTER_GATHER_ELEMENT); ++ ++ u64 PASCListMem; ++ struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; ++ u64 PASCListEntry; ++ struct SBufferHeader *Cur; ++ void *SCListMem; ++ ++ if (SCListMemSize < 4096) ++ SCListMemSize = 4096; ++ ++ SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp); ++ ++ PASCListMem = tmp; ++ if (SCListMem == NULL) ++ return -ENOMEM; ++ ++ memset(SCListMem, 0, SCListMemSize); ++ ++ pRingBuffer->SCListMem = SCListMem; ++ pRingBuffer->PASCListMem = PASCListMem; ++ pRingBuffer->SCListMemSize = SCListMemSize; ++ pRingBuffer->Buffer1Length = Buffer1Length; ++ pRingBuffer->Buffer2Length = Buffer2Length; ++ ++ SCListEntry = SCListMem; ++ PASCListEntry = PASCListMem; ++ Cur = pRingBuffer->Head; ++ ++ for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) { ++ u64 PABuffer; ++ ++ void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length, ++ &tmp); ++ PABuffer = tmp; ++ ++ if (Buffer == NULL) ++ return -ENOMEM; ++ ++ Cur->Buffer1 = Buffer; ++ ++ SCListEntry->Address = PABuffer; ++ SCListEntry->Length = Buffer1Length; ++ ++ Cur->scList1 = SCListEntry; ++ Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry; ++ Cur->ngeneBuffer.Number_of_entries_1 = ++ NUM_SCATTER_GATHER_ENTRIES; ++ ++ SCListEntry += 1; ++ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); ++ ++#if NUM_SCATTER_GATHER_ENTRIES > 1 ++ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) { ++ SCListEntry->Address = of; ++ SCListEntry->Length = OVERFLOW_BUFFER_SIZE; ++ SCListEntry += 1; ++ PASCListEntry += ++ sizeof(struct HW_SCATTER_GATHER_ELEMENT); ++ } ++#endif ++ ++ if (!Buffer2Length) ++ continue; ++ ++ Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp); ++ PABuffer = tmp; ++ ++ if (Buffer == NULL) ++ return -ENOMEM; ++ ++ Cur->Buffer2 = Buffer; ++ ++ SCListEntry->Address = PABuffer; ++ SCListEntry->Length = Buffer2Length; ++ ++ Cur->scList2 = SCListEntry; ++ Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry; ++ Cur->ngeneBuffer.Number_of_entries_2 = ++ NUM_SCATTER_GATHER_ENTRIES; ++ ++ SCListEntry += 1; ++ PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); ++ ++#if NUM_SCATTER_GATHER_ENTRIES > 1 ++ for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) { ++ SCListEntry->Address = of; ++ SCListEntry->Length = OVERFLOW_BUFFER_SIZE; ++ SCListEntry += 1; ++ PASCListEntry += ++ sizeof(struct HW_SCATTER_GATHER_ELEMENT); ++ } ++#endif ++ ++ } ++ ++ return status; ++} ++ ++static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, ++ struct SRingBufferDescriptor *pRingBuffer) ++{ ++ int status = 0; ++ ++ /* Copy pointer to scatter gather list in TSRingbuffer ++ structure for buffer 2 ++ Load number of buffer ++ */ ++ u32 n = pRingBuffer->NumBuffers; ++ ++ /* Point to first buffer entry */ ++ struct SBufferHeader *Cur = pRingBuffer->Head; ++ int i; ++ /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */ ++ for (i = 0; i < n; i++) { ++ Cur->Buffer2 = pIdleBuffer->Head->Buffer1; ++ Cur->scList2 = pIdleBuffer->Head->scList1; ++ Cur->ngeneBuffer.Address_of_first_entry_2 = ++ pIdleBuffer->Head->ngeneBuffer. ++ Address_of_first_entry_1; ++ Cur->ngeneBuffer.Number_of_entries_2 = ++ pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; ++ Cur = Cur->Next; ++ } ++ return status; ++} ++ ++static u32 RingBufferSizes[MAX_STREAM] = { ++ RING_SIZE_VIDEO, ++ RING_SIZE_VIDEO, ++ RING_SIZE_AUDIO, ++ RING_SIZE_AUDIO, ++ RING_SIZE_AUDIO, ++}; ++ ++static u32 Buffer1Sizes[MAX_STREAM] = { ++ MAX_VIDEO_BUFFER_SIZE, ++ MAX_VIDEO_BUFFER_SIZE, ++ MAX_AUDIO_BUFFER_SIZE, ++ MAX_AUDIO_BUFFER_SIZE, ++ MAX_AUDIO_BUFFER_SIZE ++}; ++ ++static u32 Buffer2Sizes[MAX_STREAM] = { ++ MAX_VBI_BUFFER_SIZE, ++ MAX_VBI_BUFFER_SIZE, ++ 0, ++ 0, ++ 0 ++}; ++ ++ ++static int AllocCommonBuffers(struct ngene *dev) ++{ ++ int status = 0, i; ++ ++ dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096, ++ &dev->PAFWInterfaceBuffer); ++ if (!dev->FWInterfaceBuffer) ++ return -ENOMEM; ++ dev->hosttongene = dev->FWInterfaceBuffer; ++ dev->ngenetohost = dev->FWInterfaceBuffer + 256; ++ dev->EventBuffer = dev->FWInterfaceBuffer + 512; ++ ++ dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev, ++ OVERFLOW_BUFFER_SIZE, ++ &dev->PAOverflowBuffer); ++ if (!dev->OverflowBuffer) ++ return -ENOMEM; ++ memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE); ++ ++ for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { ++ int type = dev->card_info->io_type[i]; ++ ++ dev->channel[i].State = KSSTATE_STOP; ++ ++ if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { ++ status = create_ring_buffer(dev->pci_dev, ++ &dev->channel[i].RingBuffer, ++ RingBufferSizes[i]); ++ if (status < 0) ++ break; ++ ++ if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { ++ status = AllocateRingBuffers(dev->pci_dev, ++ dev-> ++ PAOverflowBuffer, ++ &dev->channel[i]. ++ RingBuffer, ++ Buffer1Sizes[i], ++ Buffer2Sizes[i]); ++ if (status < 0) ++ break; ++ } else if (type & NGENE_IO_HDTV) { ++ status = AllocateRingBuffers(dev->pci_dev, ++ dev-> ++ PAOverflowBuffer, ++ &dev->channel[i]. ++ RingBuffer, ++ MAX_HDTV_BUFFER_SIZE, ++ 0); ++ if (status < 0) ++ break; ++ } ++ } ++ ++ if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { ++ ++ status = create_ring_buffer(dev->pci_dev, ++ &dev->channel[i]. ++ TSRingBuffer, RING_SIZE_TS); ++ if (status < 0) ++ break; ++ ++ status = AllocateRingBuffers(dev->pci_dev, ++ dev->PAOverflowBuffer, ++ &dev->channel[i]. ++ TSRingBuffer, ++ MAX_TS_BUFFER_SIZE, 0); ++ if (status) ++ break; ++ } ++ ++ if (type & NGENE_IO_TSOUT) { ++ status = create_ring_buffer(dev->pci_dev, ++ &dev->channel[i]. ++ TSIdleBuffer, 1); ++ if (status < 0) ++ break; ++ status = AllocateRingBuffers(dev->pci_dev, ++ dev->PAOverflowBuffer, ++ &dev->channel[i]. ++ TSIdleBuffer, ++ MAX_TS_BUFFER_SIZE, 0); ++ if (status) ++ break; ++ FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer, ++ &dev->channel[i].TSRingBuffer); ++ } ++ } ++ return status; ++} ++ ++static void ngene_release_buffers(struct ngene *dev) ++{ ++ if (dev->iomem) ++ iounmap(dev->iomem); ++ free_common_buffers(dev); ++ vfree(dev->tsout_buf); ++ vfree(dev->tsin_buf); ++ vfree(dev->ain_buf); ++ vfree(dev->vin_buf); ++ vfree(dev); ++} ++ ++static int ngene_get_buffers(struct ngene *dev) ++{ ++ if (AllocCommonBuffers(dev)) ++ return -ENOMEM; ++ if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) { ++ dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE); ++ if (!dev->tsout_buf) ++ return -ENOMEM; ++ dvb_ringbuffer_init(&dev->tsout_rbuf, ++ dev->tsout_buf, TSOUT_BUF_SIZE); ++ } ++ if (dev->card_info->io_type[2]&NGENE_IO_TSIN) { ++ dev->tsin_buf = vmalloc(TSIN_BUF_SIZE); ++ if (!dev->tsin_buf) ++ return -ENOMEM; ++ dvb_ringbuffer_init(&dev->tsin_rbuf, ++ dev->tsin_buf, TSIN_BUF_SIZE); ++ } ++ if (dev->card_info->io_type[2] & NGENE_IO_AIN) { ++ dev->ain_buf = vmalloc(AIN_BUF_SIZE); ++ if (!dev->ain_buf) ++ return -ENOMEM; ++ dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE); ++ } ++ if (dev->card_info->io_type[0] & NGENE_IO_HDTV) { ++ dev->vin_buf = vmalloc(VIN_BUF_SIZE); ++ if (!dev->vin_buf) ++ return -ENOMEM; ++ dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE); ++ } ++ dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0), ++ pci_resource_len(dev->pci_dev, 0)); ++ if (!dev->iomem) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static void ngene_init(struct ngene *dev) ++{ ++ int i; ++ ++ tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); ++ ++ memset_io(dev->iomem + 0xc000, 0x00, 0x220); ++ memset_io(dev->iomem + 0xc400, 0x00, 0x100); ++ ++ for (i = 0; i < MAX_STREAM; i++) { ++ dev->channel[i].dev = dev; ++ dev->channel[i].number = i; ++ } ++ ++ dev->fw_interface_version = 0; ++ ++ ngwritel(0, NGENE_INT_ENABLE); ++ ++ dev->icounts = ngreadl(NGENE_INT_COUNTS); ++ ++ dev->device_version = ngreadl(DEV_VER) & 0x0f; ++ printk(KERN_INFO DEVICE_NAME ": Device version %d\n", ++ dev->device_version); ++} ++ ++static int ngene_load_firm(struct ngene *dev) ++{ ++ u32 size; ++ const struct firmware *fw = NULL; ++ u8 *ngene_fw; ++ char *fw_name; ++ int err, version; ++ ++ version = dev->card_info->fw_version; ++ ++ switch (version) { ++ default: ++ case 15: ++ version = 15; ++ size = 23466; ++ fw_name = "ngene_15.fw"; ++ dev->cmd_timeout_workaround = true; ++ break; ++ case 16: ++ size = 23498; ++ fw_name = "ngene_16.fw"; ++ dev->cmd_timeout_workaround = true; ++ break; ++ case 17: ++ size = 24446; ++ fw_name = "ngene_17.fw"; ++ dev->cmd_timeout_workaround = true; ++ break; ++ case 18: ++ size = 0; ++ fw_name = "ngene_18.fw"; ++ break; ++ } ++ ++ if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { ++ printk(KERN_ERR DEVICE_NAME ++ ": Could not load firmware file %s.\n", fw_name); ++ printk(KERN_INFO DEVICE_NAME ++ ": Copy %s to your hotplug directory!\n", fw_name); ++ return -1; ++ } ++ if (size == 0) ++ size = fw->size; ++ if (size != fw->size) { ++ printk(KERN_ERR DEVICE_NAME ++ ": Firmware %s has invalid size!", fw_name); ++ err = -1; ++ } else { ++ printk(KERN_INFO DEVICE_NAME ++ ": Loading firmware file %s.\n", fw_name); ++ ngene_fw = (u8 *) fw->data; ++ err = ngene_command_load_firmware(dev, ngene_fw, size); ++ } ++ ++ release_firmware(fw); ++ ++ return err; ++} ++ ++static void ngene_stop(struct ngene *dev) ++{ ++ down(&dev->cmd_mutex); ++ i2c_del_adapter(&(dev->channel[0].i2c_adapter)); ++ i2c_del_adapter(&(dev->channel[1].i2c_adapter)); ++ ngwritel(0, NGENE_INT_ENABLE); ++ ngwritel(0, NGENE_COMMAND); ++ ngwritel(0, NGENE_COMMAND_HI); ++ ngwritel(0, NGENE_STATUS); ++ ngwritel(0, NGENE_STATUS_HI); ++ ngwritel(0, NGENE_EVENT); ++ ngwritel(0, NGENE_EVENT_HI); ++ free_irq(dev->pci_dev->irq, dev); ++#ifdef CONFIG_PCI_MSI ++ if (dev->msi_enabled) ++ pci_disable_msi(dev->pci_dev); ++#endif ++} ++ ++static int ngene_buffer_config(struct ngene *dev) ++{ ++ int stat; ++ ++ if (dev->card_info->fw_version >= 17) { ++ u8 tsin12_config[6] = { 0x60, 0x60, 0x00, 0x00, 0x00, 0x00 }; ++ u8 tsin1234_config[6] = { 0x30, 0x30, 0x00, 0x30, 0x30, 0x00 }; ++ u8 tsio1235_config[6] = { 0x30, 0x30, 0x00, 0x28, 0x00, 0x38 }; ++ u8 *bconf = tsin12_config; ++ ++ if (dev->card_info->io_type[2]&NGENE_IO_TSIN && ++ dev->card_info->io_type[3]&NGENE_IO_TSIN) { ++ bconf = tsin1234_config; ++ if (dev->card_info->io_type[4]&NGENE_IO_TSOUT && ++ dev->ci.en) ++ bconf = tsio1235_config; ++ } ++ stat = ngene_command_config_free_buf(dev, bconf); ++ } else { ++ int bconf = BUFFER_CONFIG_4422; ++ ++ if (dev->card_info->io_type[3] == NGENE_IO_TSIN) ++ bconf = BUFFER_CONFIG_3333; ++ stat = ngene_command_config_buf(dev, bconf); ++ } ++ return stat; ++} ++ ++ ++static int ngene_start(struct ngene *dev) ++{ ++ int stat; ++ int i; ++ ++ pci_set_master(dev->pci_dev); ++ ngene_init(dev); ++ ++ stat = request_irq(dev->pci_dev->irq, irq_handler, ++ IRQF_SHARED, "nGene", ++ (void *)dev); ++ if (stat < 0) ++ return stat; ++ ++ init_waitqueue_head(&dev->cmd_wq); ++ init_waitqueue_head(&dev->tx_wq); ++ init_waitqueue_head(&dev->rx_wq); ++ sema_init(&dev->cmd_mutex, 1); ++ sema_init(&dev->stream_mutex, 1); ++ sema_init(&dev->pll_mutex, 1); ++ sema_init(&dev->i2c_switch_mutex, 1); ++ spin_lock_init(&dev->cmd_lock); ++ for (i = 0; i < MAX_STREAM; i++) ++ spin_lock_init(&dev->channel[i].state_lock); ++ ngwritel(1, TIMESTAMPS); ++ ++ ngwritel(1, NGENE_INT_ENABLE); ++ ++ stat = ngene_load_firm(dev); ++ if (stat < 0) ++ goto fail; ++ ++#ifdef CONFIG_PCI_MSI ++ /* enable MSI if kernel and card support it */ ++ if (pci_msi_enabled() && dev->card_info->msi_supported) { ++ unsigned long flags; ++ ++ ngwritel(0, NGENE_INT_ENABLE); ++ free_irq(dev->pci_dev->irq, dev); ++ stat = pci_enable_msi(dev->pci_dev); ++ if (stat) { ++ printk(KERN_INFO DEVICE_NAME ++ ": MSI not available\n"); ++ flags = IRQF_SHARED; ++ } else { ++ flags = 0; ++ dev->msi_enabled = true; ++ } ++ stat = request_irq(dev->pci_dev->irq, irq_handler, ++ flags, "nGene", dev); ++ if (stat < 0) ++ goto fail2; ++ ngwritel(1, NGENE_INT_ENABLE); ++ } ++#endif ++ ++ stat = ngene_i2c_init(dev, 0); ++ if (stat < 0) ++ goto fail; ++ ++ stat = ngene_i2c_init(dev, 1); ++ if (stat < 0) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ ngwritel(0, NGENE_INT_ENABLE); ++ free_irq(dev->pci_dev->irq, dev); ++#ifdef CONFIG_PCI_MSI ++fail2: ++ if (dev->msi_enabled) ++ pci_disable_msi(dev->pci_dev); ++#endif ++ return stat; ++} ++ ++/****************************************************************************/ ++/****************************************************************************/ ++/****************************************************************************/ ++ ++static void release_channel(struct ngene_channel *chan) ++{ ++ struct dvb_demux *dvbdemux = &chan->demux; ++ struct ngene *dev = chan->dev; ++ ++ if (chan->running) ++ set_transfer(chan, 0); ++ ++ tasklet_kill(&chan->demux_tasklet); ++ ++ if (chan->ci_dev) { ++ dvb_unregister_device(chan->ci_dev); ++ chan->ci_dev = NULL; ++ } ++ ++ if (chan->fe2) ++ dvb_unregister_frontend(chan->fe2); ++ ++ if (chan->fe) { ++ dvb_unregister_frontend(chan->fe); ++ dvb_frontend_detach(chan->fe); ++ chan->fe = NULL; ++ } ++ ++ if (chan->has_demux) { ++ dvb_net_release(&chan->dvbnet); ++ dvbdemux->dmx.close(&dvbdemux->dmx); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, ++ &chan->hw_frontend); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, ++ &chan->mem_frontend); ++ dvb_dmxdev_release(&chan->dmxdev); ++ dvb_dmx_release(&chan->demux); ++ chan->has_demux = false; ++ } ++ ++ if (chan->has_adapter) { ++ dvb_unregister_adapter(&dev->adapter[chan->number]); ++ chan->has_adapter = false; ++ } ++} ++ ++static int init_channel(struct ngene_channel *chan) ++{ ++ int ret = 0, nr = chan->number; ++ struct dvb_adapter *adapter = NULL; ++ struct dvb_demux *dvbdemux = &chan->demux; ++ struct ngene *dev = chan->dev; ++ struct ngene_info *ni = dev->card_info; ++ int io = ni->io_type[nr]; ++ ++ tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); ++ chan->users = 0; ++ chan->type = io; ++ chan->mode = chan->type; /* for now only one mode */ ++ ++ if (io & NGENE_IO_TSIN) { ++ chan->fe = NULL; ++ if (ni->demod_attach[nr]) { ++ ret = ni->demod_attach[nr](chan); ++ if (ret < 0) ++ goto err; ++ } ++ if (chan->fe && ni->tuner_attach[nr]) { ++ ret = ni->tuner_attach[nr](chan); ++ if (ret < 0) ++ goto err; ++ } ++ } ++ ++ if (!dev->ci.en && (io & NGENE_IO_TSOUT)) ++ return 0; ++ ++ if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { ++ if (nr >= STREAM_AUDIOIN1) ++ chan->DataFormatFlags = DF_SWAP32; ++ ++ if (nr == 0 || !one_adapter || dev->first_adapter == NULL) { ++ adapter = &dev->adapter[nr]; ++ ret = dvb_register_adapter(adapter, "nGene", ++ THIS_MODULE, ++ &chan->dev->pci_dev->dev, ++ adapter_nr); ++ if (ret < 0) ++ goto err; ++ if (dev->first_adapter == NULL) ++ dev->first_adapter = adapter; ++ chan->has_adapter = true; ++ } else ++ adapter = dev->first_adapter; ++ } ++ ++ if (dev->ci.en && (io & NGENE_IO_TSOUT)) { ++ dvb_ca_en50221_init(adapter, dev->ci.en, 0, 1); ++ set_transfer(chan, 1); ++ chan->dev->channel[2].DataFormatFlags = DF_SWAP32; ++ set_transfer(&chan->dev->channel[2], 1); ++ dvb_register_device(adapter, &chan->ci_dev, ++ &ngene_dvbdev_ci, (void *) chan, ++ DVB_DEVICE_SEC); ++ if (!chan->ci_dev) ++ goto err; ++ } ++ ++ if (chan->fe) { ++ if (dvb_register_frontend(adapter, chan->fe) < 0) ++ goto err; ++ chan->has_demux = true; ++ } ++ if (chan->fe2) { ++ if (dvb_register_frontend(adapter, chan->fe2) < 0) ++ goto err; ++ chan->fe2->tuner_priv = chan->fe->tuner_priv; ++ memcpy(&chan->fe2->ops.tuner_ops, ++ &chan->fe->ops.tuner_ops, ++ sizeof(struct dvb_tuner_ops)); ++ } ++ ++ if (chan->has_demux) { ++ ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", ++ ngene_start_feed, ++ ngene_stop_feed, chan); ++ ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, ++ &chan->hw_frontend, ++ &chan->mem_frontend, adapter); ++ ret = dvb_net_init(adapter, &chan->dvbnet, &chan->demux.dmx); ++ } ++ ++ return ret; ++ ++err: ++ if (chan->fe) { ++ dvb_frontend_detach(chan->fe); ++ chan->fe = NULL; ++ } ++ release_channel(chan); ++ return 0; ++} ++ ++static int init_channels(struct ngene *dev) ++{ ++ int i, j; ++ ++ for (i = 0; i < MAX_STREAM; i++) { ++ dev->channel[i].number = i; ++ if (init_channel(&dev->channel[i]) < 0) { ++ for (j = i - 1; j >= 0; j--) ++ release_channel(&dev->channel[j]); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static struct cxd2099_cfg cxd_cfg = { ++ .bitrate = 62000, ++ .adr = 0x40, ++ .polarity = 0, ++ .clock_mode = 0, ++}; ++ ++static void cxd_attach(struct ngene *dev) ++{ ++ struct ngene_ci *ci = &dev->ci; ++ ++ ci->en = cxd2099_attach(&cxd_cfg, dev, &dev->channel[0].i2c_adapter); ++ ci->dev = dev; ++ return; ++} ++ ++static void cxd_detach(struct ngene *dev) ++{ ++ struct ngene_ci *ci = &dev->ci; ++ ++ dvb_ca_en50221_release(ci->en); ++ kfree(ci->en); ++ ci->en = 0; ++} ++ ++/***********************************/ ++/* workaround for shutdown failure */ ++/***********************************/ ++ ++static void ngene_unlink(struct ngene *dev) ++{ ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_MEM_WRITE; ++ com.cmd.hdr.Length = 3; ++ com.cmd.MemoryWrite.address = 0x910c; ++ com.cmd.MemoryWrite.data = 0xff; ++ com.in_len = 3; ++ com.out_len = 1; ++ ++ down(&dev->cmd_mutex); ++ ngwritel(0, NGENE_INT_ENABLE); ++ ngene_command_mutex(dev, &com); ++ up(&dev->cmd_mutex); ++} ++ ++void ngene_shutdown(struct pci_dev *pdev) ++{ ++ struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); ++ ++ if (!dev || !shutdown_workaround) ++ return; ++ ++ printk(KERN_INFO DEVICE_NAME ": shutdown workaround...\n"); ++ ngene_unlink(dev); ++ pci_disable_device(pdev); ++} ++ ++/****************************************************************************/ ++/* device probe/remove calls ************************************************/ ++/****************************************************************************/ ++ ++void __devexit ngene_remove(struct pci_dev *pdev) ++{ ++ struct ngene *dev = pci_get_drvdata(pdev); ++ int i; ++ ++ tasklet_kill(&dev->event_tasklet); ++ for (i = MAX_STREAM - 1; i >= 0; i--) ++ release_channel(&dev->channel[i]); ++ if (dev->ci.en) ++ cxd_detach(dev); ++ ngene_stop(dev); ++ ngene_release_buffers(dev); ++ pci_set_drvdata(pdev, NULL); ++ pci_disable_device(pdev); ++} ++ ++int __devinit ngene_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *id) ++{ ++ struct ngene *dev; ++ int stat = 0; ++ ++ if (pci_enable_device(pci_dev) < 0) ++ return -ENODEV; ++ ++ dev = vzalloc(sizeof(struct ngene)); ++ if (dev == NULL) { ++ stat = -ENOMEM; ++ goto fail0; ++ } ++ ++ dev->pci_dev = pci_dev; ++ dev->card_info = (struct ngene_info *)id->driver_data; ++ printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); ++ ++ pci_set_drvdata(pci_dev, dev); ++ ++ /* Alloc buffers and start nGene */ ++ stat = ngene_get_buffers(dev); ++ if (stat < 0) ++ goto fail1; ++ stat = ngene_start(dev); ++ if (stat < 0) ++ goto fail1; ++ ++ cxd_attach(dev); ++ ++ stat = ngene_buffer_config(dev); ++ if (stat < 0) ++ goto fail1; ++ ++ ++ dev->i2c_current_bus = -1; ++ ++ /* Register DVB adapters and devices for both channels */ ++ stat = init_channels(dev); ++ if (stat < 0) ++ goto fail2; ++ ++ return 0; ++ ++fail2: ++ ngene_stop(dev); ++fail1: ++ ngene_release_buffers(dev); ++fail0: ++ pci_disable_device(pci_dev); ++ pci_set_drvdata(pci_dev, NULL); ++ return stat; ++} +diff --git a/drivers/media/pci/ngene/ngene-dvb.c b/drivers/media/pci/ngene/ngene-dvb.c +new file mode 100644 +index 0000000..fcb16a6 +--- /dev/null ++++ b/drivers/media/pci/ngene/ngene-dvb.c +@@ -0,0 +1,261 @@ ++/* ++ * ngene-dvb.c: nGene PCIe bridge driver - DVB functions ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * Copyright (C) 2008-2009 Ralph Metzler ++ * Modifications for new nGene firmware, ++ * support for EEPROM-copying, ++ * support for new dual DVB-S2 card prototype ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ngene.h" ++ ++ ++/****************************************************************************/ ++/* COMMAND API interface ****************************************************/ ++/****************************************************************************/ ++ ++static ssize_t ts_write(struct file *file, const char *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct ngene_channel *chan = dvbdev->priv; ++ struct ngene *dev = chan->dev; ++ ++ if (wait_event_interruptible(dev->tsout_rbuf.queue, ++ dvb_ringbuffer_free ++ (&dev->tsout_rbuf) >= count) < 0) ++ return 0; ++ ++ dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); ++ ++ return count; ++} ++ ++static ssize_t ts_read(struct file *file, char *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct ngene_channel *chan = dvbdev->priv; ++ struct ngene *dev = chan->dev; ++ int left, avail; ++ ++ left = count; ++ while (left) { ++ if (wait_event_interruptible( ++ dev->tsin_rbuf.queue, ++ dvb_ringbuffer_avail(&dev->tsin_rbuf) > 0) < 0) ++ return -EAGAIN; ++ avail = dvb_ringbuffer_avail(&dev->tsin_rbuf); ++ if (avail > left) ++ avail = left; ++ dvb_ringbuffer_read_user(&dev->tsin_rbuf, buf, avail); ++ left -= avail; ++ buf += avail; ++ } ++ return count; ++} ++ ++static const struct file_operations ci_fops = { ++ .owner = THIS_MODULE, ++ .read = ts_read, ++ .write = ts_write, ++ .open = dvb_generic_open, ++ .release = dvb_generic_release, ++}; ++ ++struct dvb_device ngene_dvbdev_ci = { ++ .priv = 0, ++ .readers = -1, ++ .writers = -1, ++ .users = -1, ++ .fops = &ci_fops, ++}; ++ ++ ++/****************************************************************************/ ++/* DVB functions and API interface ******************************************/ ++/****************************************************************************/ ++ ++static void swap_buffer(u32 *p, u32 len) ++{ ++ while (len) { ++ *p = swab32(*p); ++ p++; ++ len -= 4; ++ } ++} ++ ++/* start of filler packet */ ++static u8 fill_ts[] = { 0x47, 0x1f, 0xff, 0x10, TS_FILLER }; ++ ++/* #define DEBUG_CI_XFER */ ++#ifdef DEBUG_CI_XFER ++static u32 ok; ++static u32 overflow; ++static u32 stripped; ++#endif ++ ++void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) ++{ ++ struct ngene_channel *chan = priv; ++ struct ngene *dev = chan->dev; ++ ++ ++ if (flags & DF_SWAP32) ++ swap_buffer(buf, len); ++ ++ if (dev->ci.en && chan->number == 2) { ++ while (len >= 188) { ++ if (memcmp(buf, fill_ts, sizeof fill_ts) != 0) { ++ if (dvb_ringbuffer_free(&dev->tsin_rbuf) >= 188) { ++ dvb_ringbuffer_write(&dev->tsin_rbuf, buf, 188); ++ wake_up(&dev->tsin_rbuf.queue); ++#ifdef DEBUG_CI_XFER ++ ok++; ++#endif ++ } ++#ifdef DEBUG_CI_XFER ++ else ++ overflow++; ++#endif ++ } ++#ifdef DEBUG_CI_XFER ++ else ++ stripped++; ++ ++ if (ok % 100 == 0 && overflow) ++ printk(KERN_WARNING "%s: ok %u overflow %u dropped %u\n", __func__, ok, overflow, stripped); ++#endif ++ buf += 188; ++ len -= 188; ++ } ++ return NULL; ++ } ++ ++ if (chan->users > 0) ++ dvb_dmx_swfilter(&chan->demux, buf, len); ++ ++ return NULL; ++} ++ ++void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) ++{ ++ struct ngene_channel *chan = priv; ++ struct ngene *dev = chan->dev; ++ u32 alen; ++ ++ alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); ++ alen -= alen % 188; ++ ++ if (alen < len) ++ FillTSBuffer(buf + alen, len - alen, flags); ++ else ++ alen = len; ++ dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); ++ if (flags & DF_SWAP32) ++ swap_buffer((u32 *)buf, alen); ++ wake_up_interruptible(&dev->tsout_rbuf.queue); ++ return buf; ++} ++ ++ ++ ++int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct ngene_channel *chan = dvbdmx->priv; ++ ++ if (chan->users == 0) { ++ if (!chan->dev->cmd_timeout_workaround || !chan->running) ++ set_transfer(chan, 1); ++ } ++ ++ return ++chan->users; ++} ++ ++int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct ngene_channel *chan = dvbdmx->priv; ++ ++ if (--chan->users) ++ return chan->users; ++ ++ if (!chan->dev->cmd_timeout_workaround) ++ set_transfer(chan, 0); ++ ++ return 0; ++} ++ ++int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, ++ int (*start_feed)(struct dvb_demux_feed *), ++ int (*stop_feed)(struct dvb_demux_feed *), ++ void *priv) ++{ ++ dvbdemux->priv = priv; ++ ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = start_feed; ++ dvbdemux->stop_feed = stop_feed; ++ dvbdemux->write_to_decoder = NULL; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING); ++ return dvb_dmx_init(dvbdemux); ++} ++ ++int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, ++ struct dvb_demux *dvbdemux, ++ struct dmx_frontend *hw_frontend, ++ struct dmx_frontend *mem_frontend, ++ struct dvb_adapter *dvb_adapter) ++{ ++ int ret; ++ ++ dmxdev->filternum = 256; ++ dmxdev->demux = &dvbdemux->dmx; ++ dmxdev->capabilities = 0; ++ ret = dvb_dmxdev_init(dmxdev, dvb_adapter); ++ if (ret < 0) ++ return ret; ++ ++ hw_frontend->source = DMX_FRONTEND_0; ++ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); ++ mem_frontend->source = DMX_MEMORY_FE; ++ dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); ++ return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); ++} +diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c +new file mode 100644 +index 0000000..d28554f +--- /dev/null ++++ b/drivers/media/pci/ngene/ngene-i2c.c +@@ -0,0 +1,176 @@ ++/* ++ * ngene-i2c.c: nGene PCIe bridge driver i2c functions ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * Copyright (C) 2008-2009 Ralph Metzler ++ * Modifications for new nGene firmware, ++ * support for EEPROM-copying, ++ * support for new dual DVB-S2 card prototype ++ * ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/* FIXME - some of these can probably be removed */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ngene.h" ++ ++/* Firmware command for i2c operations */ ++static int ngene_command_i2c_read(struct ngene *dev, u8 adr, ++ u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) ++{ ++ struct ngene_command com; ++ ++ com.cmd.hdr.Opcode = CMD_I2C_READ; ++ com.cmd.hdr.Length = outlen + 3; ++ com.cmd.I2CRead.Device = adr << 1; ++ memcpy(com.cmd.I2CRead.Data, out, outlen); ++ com.cmd.I2CRead.Data[outlen] = inlen; ++ com.cmd.I2CRead.Data[outlen + 1] = 0; ++ com.in_len = outlen + 3; ++ com.out_len = inlen + 1; ++ ++ if (ngene_command(dev, &com) < 0) ++ return -EIO; ++ ++ if ((com.cmd.raw8[0] >> 1) != adr) ++ return -EIO; ++ ++ if (flag) ++ memcpy(in, com.cmd.raw8, inlen + 1); ++ else ++ memcpy(in, com.cmd.raw8 + 1, inlen); ++ return 0; ++} ++ ++static int ngene_command_i2c_write(struct ngene *dev, u8 adr, ++ u8 *out, u8 outlen) ++{ ++ struct ngene_command com; ++ ++ ++ com.cmd.hdr.Opcode = CMD_I2C_WRITE; ++ com.cmd.hdr.Length = outlen + 1; ++ com.cmd.I2CRead.Device = adr << 1; ++ memcpy(com.cmd.I2CRead.Data, out, outlen); ++ com.in_len = outlen + 1; ++ com.out_len = 1; ++ ++ if (ngene_command(dev, &com) < 0) ++ return -EIO; ++ ++ if (com.cmd.raw8[0] == 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static void ngene_i2c_set_bus(struct ngene *dev, int bus) ++{ ++ if (!(dev->card_info->i2c_access & 2)) ++ return; ++ if (dev->i2c_current_bus == bus) ++ return; ++ ++ switch (bus) { ++ case 0: ++ ngene_command_gpio_set(dev, 3, 0); ++ ngene_command_gpio_set(dev, 2, 1); ++ break; ++ ++ case 1: ++ ngene_command_gpio_set(dev, 2, 0); ++ ngene_command_gpio_set(dev, 3, 1); ++ break; ++ } ++ dev->i2c_current_bus = bus; ++} ++ ++static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, ++ struct i2c_msg msg[], int num) ++{ ++ struct ngene_channel *chan = ++ (struct ngene_channel *)i2c_get_adapdata(adapter); ++ struct ngene *dev = chan->dev; ++ ++ down(&dev->i2c_switch_mutex); ++ ngene_i2c_set_bus(dev, chan->number); ++ ++ if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) ++ if (!ngene_command_i2c_read(dev, msg[0].addr, ++ msg[0].buf, msg[0].len, ++ msg[1].buf, msg[1].len, 0)) ++ goto done; ++ ++ if (num == 1 && !(msg[0].flags & I2C_M_RD)) ++ if (!ngene_command_i2c_write(dev, msg[0].addr, ++ msg[0].buf, msg[0].len)) ++ goto done; ++ if (num == 1 && (msg[0].flags & I2C_M_RD)) ++ if (!ngene_command_i2c_read(dev, msg[0].addr, NULL, 0, ++ msg[0].buf, msg[0].len, 0)) ++ goto done; ++ ++ up(&dev->i2c_switch_mutex); ++ return -EIO; ++ ++done: ++ up(&dev->i2c_switch_mutex); ++ return num; ++} ++ ++ ++static u32 ngene_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm ngene_i2c_algo = { ++ .master_xfer = ngene_i2c_master_xfer, ++ .functionality = ngene_i2c_functionality, ++}; ++ ++int ngene_i2c_init(struct ngene *dev, int dev_nr) ++{ ++ struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); ++ ++ i2c_set_adapdata(adap, &(dev->channel[dev_nr])); ++ ++ strcpy(adap->name, "nGene"); ++ ++ adap->algo = &ngene_i2c_algo; ++ adap->algo_data = (void *)&(dev->channel[dev_nr]); ++ adap->dev.parent = &dev->pci_dev->dev; ++ ++ return i2c_add_adapter(adap); ++} ++ +diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h +new file mode 100644 +index 0000000..5443dc0 +--- /dev/null ++++ b/drivers/media/pci/ngene/ngene.h +@@ -0,0 +1,921 @@ ++/* ++ * ngene.h: nGene PCIe bridge driver ++ * ++ * Copyright (C) 2005-2007 Micronas ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 only, as published by the Free Software Foundation. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ++ * 02110-1301, USA ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++#ifndef _NGENE_H_ ++#define _NGENE_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dmxdev.h" ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dvb_ca_en50221.h" ++#include "dvb_frontend.h" ++#include "dvb_ringbuffer.h" ++#include "dvb_net.h" ++#include "cxd2099.h" ++ ++#define DEVICE_NAME "ngene" ++ ++#define NGENE_VID 0x18c3 ++#define NGENE_PID 0x0720 ++ ++#ifndef VIDEO_CAP_VC1 ++#define VIDEO_CAP_AVC 128 ++#define VIDEO_CAP_H264 128 ++#define VIDEO_CAP_VC1 256 ++#define VIDEO_CAP_WMV9 256 ++#define VIDEO_CAP_MPEG4 512 ++#endif ++ ++enum STREAM { ++ STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ ++ STREAM_VIDEOIN2, ++ STREAM_AUDIOIN1, /* I2S or SPI Input */ ++ STREAM_AUDIOIN2, ++ STREAM_AUDIOOUT, ++ MAX_STREAM ++}; ++ ++enum SMODE_BITS { ++ SMODE_AUDIO_SPDIF = 0x20, ++ SMODE_AVSYNC = 0x10, ++ SMODE_TRANSPORT_STREAM = 0x08, ++ SMODE_AUDIO_CAPTURE = 0x04, ++ SMODE_VBI_CAPTURE = 0x02, ++ SMODE_VIDEO_CAPTURE = 0x01 ++}; ++ ++enum STREAM_FLAG_BITS { ++ SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */ ++ SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */ ++ SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */ ++ SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */ ++ SFLAG_COLORBAR = 0x04, /* Select colorbar */ ++}; ++ ++#define PROGRAM_ROM 0x0000 ++#define PROGRAM_SRAM 0x1000 ++#define PERIPHERALS0 0x8000 ++#define PERIPHERALS1 0x9000 ++#define SHARED_BUFFER 0xC000 ++ ++#define HOST_TO_NGENE (SHARED_BUFFER+0x0000) ++#define NGENE_TO_HOST (SHARED_BUFFER+0x0100) ++#define NGENE_COMMAND (SHARED_BUFFER+0x0200) ++#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204) ++#define NGENE_STATUS (SHARED_BUFFER+0x0208) ++#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C) ++#define NGENE_EVENT (SHARED_BUFFER+0x0210) ++#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214) ++#define VARIABLES (SHARED_BUFFER+0x0210) ++ ++#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260) ++#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264) ++#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268) ++ ++#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800) ++#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900) ++#define EEPROM_AREA (SHARED_BUFFER+0x0A00) ++ ++#define SG_V_IN_1 (SHARED_BUFFER+0x0A80) ++#define SG_VBI_1 (SHARED_BUFFER+0x0B00) ++#define SG_A_IN_1 (SHARED_BUFFER+0x0B80) ++#define SG_V_IN_2 (SHARED_BUFFER+0x0C00) ++#define SG_VBI_2 (SHARED_BUFFER+0x0C80) ++#define SG_A_IN_2 (SHARED_BUFFER+0x0D00) ++#define SG_V_OUT (SHARED_BUFFER+0x0D80) ++#define SG_A_OUT2 (SHARED_BUFFER+0x0E00) ++ ++#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80) ++#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00) ++#define DATA_A_OUT (SHARED_BUFFER+0x0F80) ++#define DATA_V_IN_1 (SHARED_BUFFER+0x1000) ++#define DATA_V_IN_2 (SHARED_BUFFER+0x2000) ++#define DATA_V_OUT (SHARED_BUFFER+0x3000) ++ ++#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000) ++ ++#define TIMESTAMPS 0xA000 ++#define SCRATCHPAD 0xA080 ++#define FORCE_INT 0xA088 ++#define FORCE_NMI 0xA090 ++#define INT_STATUS 0xA0A0 ++ ++#define DEV_VER 0x9004 ++ ++#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF) ++ ++struct SG_ADDR { ++ u64 start; ++ u64 curr; ++ u16 curr_ptr; ++ u16 elements; ++ u32 pad[3]; ++} __attribute__ ((__packed__)); ++ ++struct SHARED_MEMORY { ++ /* C000 */ ++ u32 HostToNgene[64]; ++ ++ /* C100 */ ++ u32 NgeneToHost[64]; ++ ++ /* C200 */ ++ u64 NgeneCommand; ++ u64 NgeneStatus; ++ u64 NgeneEvent; ++ ++ /* C210 */ ++ u8 pad1[0xc260 - 0xc218]; ++ ++ /* C260 */ ++ u32 IntCounts; ++ u32 IntEnable; ++ ++ /* C268 */ ++ u8 pad2[0xd000 - 0xc268]; ++ ++} __attribute__ ((__packed__)); ++ ++struct BUFFER_STREAM_RESULTS { ++ u32 Clock; /* Stream time in 100ns units */ ++ u16 RemainingLines; /* Remaining lines in this field. ++ 0 for complete field */ ++ u8 FieldCount; /* Video field number */ ++ u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow, ++ Bit 0 = FieldID */ ++ u16 BlockCount; /* Audio block count (unused) */ ++ u8 Reserved[2]; ++ u32 DTOUpdate; ++} __attribute__ ((__packed__)); ++ ++struct HW_SCATTER_GATHER_ELEMENT { ++ u64 Address; ++ u32 Length; ++ u32 Reserved; ++} __attribute__ ((__packed__)); ++ ++struct BUFFER_HEADER { ++ u64 Next; ++ struct BUFFER_STREAM_RESULTS SR; ++ ++ u32 Number_of_entries_1; ++ u32 Reserved5; ++ u64 Address_of_first_entry_1; ++ ++ u32 Number_of_entries_2; ++ u32 Reserved7; ++ u64 Address_of_first_entry_2; ++} __attribute__ ((__packed__)); ++ ++struct EVENT_BUFFER { ++ u32 TimeStamp; ++ u8 GPIOStatus; ++ u8 UARTStatus; ++ u8 RXCharacter; ++ u8 EventStatus; ++ u32 Reserved[2]; ++} __attribute__ ((__packed__)); ++ ++/* Firmware commands. */ ++ ++enum OPCODES { ++ CMD_NOP = 0, ++ CMD_FWLOAD_PREPARE = 0x01, ++ CMD_FWLOAD_FINISH = 0x02, ++ CMD_I2C_READ = 0x03, ++ CMD_I2C_WRITE = 0x04, ++ ++ CMD_I2C_WRITE_NOSTOP = 0x05, ++ CMD_I2C_CONTINUE_WRITE = 0x06, ++ CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07, ++ ++ CMD_DEBUG_OUTPUT = 0x09, ++ ++ CMD_CONTROL = 0x10, ++ CMD_CONFIGURE_BUFFER = 0x11, ++ CMD_CONFIGURE_FREE_BUFFER = 0x12, ++ ++ CMD_SPI_READ = 0x13, ++ CMD_SPI_WRITE = 0x14, ++ ++ CMD_MEM_READ = 0x20, ++ CMD_MEM_WRITE = 0x21, ++ CMD_SFR_READ = 0x22, ++ CMD_SFR_WRITE = 0x23, ++ CMD_IRAM_READ = 0x24, ++ CMD_IRAM_WRITE = 0x25, ++ CMD_SET_GPIO_PIN = 0x26, ++ CMD_SET_GPIO_INT = 0x27, ++ CMD_CONFIGURE_UART = 0x28, ++ CMD_WRITE_UART = 0x29, ++ MAX_CMD ++}; ++ ++enum RESPONSES { ++ OK = 0, ++ ERROR = 1 ++}; ++ ++struct FW_HEADER { ++ u8 Opcode; ++ u8 Length; ++} __attribute__ ((__packed__)); ++ ++struct FW_I2C_WRITE { ++ struct FW_HEADER hdr; ++ u8 Device; ++ u8 Data[250]; ++} __attribute__ ((__packed__)); ++ ++struct FW_I2C_CONTINUE_WRITE { ++ struct FW_HEADER hdr; ++ u8 Data[250]; ++} __attribute__ ((__packed__)); ++ ++struct FW_I2C_READ { ++ struct FW_HEADER hdr; ++ u8 Device; ++ u8 Data[252]; /* followed by two bytes of read data count */ ++} __attribute__ ((__packed__)); ++ ++struct FW_SPI_WRITE { ++ struct FW_HEADER hdr; ++ u8 ModeSelect; ++ u8 Data[250]; ++} __attribute__ ((__packed__)); ++ ++struct FW_SPI_READ { ++ struct FW_HEADER hdr; ++ u8 ModeSelect; ++ u8 Data[252]; /* followed by two bytes of read data count */ ++} __attribute__ ((__packed__)); ++ ++struct FW_FWLOAD_PREPARE { ++ struct FW_HEADER hdr; ++} __attribute__ ((__packed__)); ++ ++struct FW_FWLOAD_FINISH { ++ struct FW_HEADER hdr; ++ u16 Address; /* address of final block */ ++ u16 Length; ++} __attribute__ ((__packed__)); ++ ++/* ++ * Meaning of FW_STREAM_CONTROL::Mode bits: ++ * Bit 7: Loopback PEXin to PEXout using TVOut channel ++ * Bit 6: AVLOOP ++ * Bit 5: Audio select; 0=I2S, 1=SPDIF ++ * Bit 4: AVSYNC ++ * Bit 3: Enable transport stream ++ * Bit 2: Enable audio capture ++ * Bit 1: Enable ITU-Video VBI capture ++ * Bit 0: Enable ITU-Video capture ++ * ++ * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL) ++ * Bit 7: continuous capture ++ * Bit 6: capture one field ++ * Bit 5: capture one frame ++ * Bit 4: unused ++ * Bit 3: starting field; 0=odd, 1=even ++ * Bit 2: sample size; 0=8-bit, 1=10-bit ++ * Bit 1: data format; 0=UYVY, 1=YUY2 ++ * Bit 0: resets buffer pointers ++*/ ++ ++enum FSC_MODE_BITS { ++ SMODE_LOOPBACK = 0x80, ++ SMODE_AVLOOP = 0x40, ++ _SMODE_AUDIO_SPDIF = 0x20, ++ _SMODE_AVSYNC = 0x10, ++ _SMODE_TRANSPORT_STREAM = 0x08, ++ _SMODE_AUDIO_CAPTURE = 0x04, ++ _SMODE_VBI_CAPTURE = 0x02, ++ _SMODE_VIDEO_CAPTURE = 0x01 ++}; ++ ++ ++/* Meaning of FW_STREAM_CONTROL::Stream bits: ++ * Bit 3: Audio sample count: 0 = relative, 1 = absolute ++ * Bit 2: color bar select; 1=color bars, 0=CV3 decoder ++ * Bits 1-0: stream select, UVI1, UVI2, TVOUT ++ */ ++ ++struct FW_STREAM_CONTROL { ++ struct FW_HEADER hdr; ++ u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */ ++ u8 Control; /* Value written to UVI1_CTL */ ++ u8 Mode; /* Controls clock source */ ++ u8 SetupDataLen; /* Length of setup data, MSB=1 write ++ backwards */ ++ u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer ++ for TS and Audio */ ++ u64 Buffer_Address; /* Address of first buffer header */ ++ u16 BytesPerVideoLine; ++ u16 MaxLinesPerField; ++ u16 MinLinesPerField; ++ u16 Reserved_1; ++ u16 BytesPerVBILine; ++ u16 MaxVBILinesPerField; ++ u16 MinVBILinesPerField; ++ u16 SetupDataAddr; /* ngene relative address of setup data */ ++ u8 SetupData[32]; /* setup data */ ++} __attribute__((__packed__)); ++ ++#define AUDIO_BLOCK_SIZE 256 ++#define TS_BLOCK_SIZE 256 ++ ++struct FW_MEM_READ { ++ struct FW_HEADER hdr; ++ u16 address; ++} __attribute__ ((__packed__)); ++ ++struct FW_MEM_WRITE { ++ struct FW_HEADER hdr; ++ u16 address; ++ u8 data; ++} __attribute__ ((__packed__)); ++ ++struct FW_SFR_IRAM_READ { ++ struct FW_HEADER hdr; ++ u8 address; ++} __attribute__ ((__packed__)); ++ ++struct FW_SFR_IRAM_WRITE { ++ struct FW_HEADER hdr; ++ u8 address; ++ u8 data; ++} __attribute__ ((__packed__)); ++ ++struct FW_SET_GPIO_PIN { ++ struct FW_HEADER hdr; ++ u8 select; ++} __attribute__ ((__packed__)); ++ ++struct FW_SET_GPIO_INT { ++ struct FW_HEADER hdr; ++ u8 select; ++} __attribute__ ((__packed__)); ++ ++struct FW_SET_DEBUGMODE { ++ struct FW_HEADER hdr; ++ u8 debug_flags; ++} __attribute__ ((__packed__)); ++ ++struct FW_CONFIGURE_BUFFERS { ++ struct FW_HEADER hdr; ++ u8 config; ++} __attribute__ ((__packed__)); ++ ++enum _BUFFER_CONFIGS { ++ /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */ ++ BUFFER_CONFIG_4422 = 0, ++ /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */ ++ BUFFER_CONFIG_3333 = 1, ++ /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */ ++ BUFFER_CONFIG_8022 = 2, ++ BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */ ++}; ++ ++struct FW_CONFIGURE_FREE_BUFFERS { ++ struct FW_HEADER hdr; ++ u8 UVI1_BufferLength; ++ u8 UVI2_BufferLength; ++ u8 TVO_BufferLength; ++ u8 AUD1_BufferLength; ++ u8 AUD2_BufferLength; ++ u8 TVA_BufferLength; ++} __attribute__ ((__packed__)); ++ ++struct FW_CONFIGURE_UART { ++ struct FW_HEADER hdr; ++ u8 UartControl; ++} __attribute__ ((__packed__)); ++ ++enum _UART_CONFIG { ++ _UART_BAUDRATE_19200 = 0, ++ _UART_BAUDRATE_9600 = 1, ++ _UART_BAUDRATE_4800 = 2, ++ _UART_BAUDRATE_2400 = 3, ++ _UART_RX_ENABLE = 0x40, ++ _UART_TX_ENABLE = 0x80, ++}; ++ ++struct FW_WRITE_UART { ++ struct FW_HEADER hdr; ++ u8 Data[252]; ++} __attribute__ ((__packed__)); ++ ++ ++struct ngene_command { ++ u32 in_len; ++ u32 out_len; ++ union { ++ u32 raw[64]; ++ u8 raw8[256]; ++ struct FW_HEADER hdr; ++ struct FW_I2C_WRITE I2CWrite; ++ struct FW_I2C_CONTINUE_WRITE I2CContinueWrite; ++ struct FW_I2C_READ I2CRead; ++ struct FW_STREAM_CONTROL StreamControl; ++ struct FW_FWLOAD_PREPARE FWLoadPrepare; ++ struct FW_FWLOAD_FINISH FWLoadFinish; ++ struct FW_MEM_READ MemoryRead; ++ struct FW_MEM_WRITE MemoryWrite; ++ struct FW_SFR_IRAM_READ SfrIramRead; ++ struct FW_SFR_IRAM_WRITE SfrIramWrite; ++ struct FW_SPI_WRITE SPIWrite; ++ struct FW_SPI_READ SPIRead; ++ struct FW_SET_GPIO_PIN SetGpioPin; ++ struct FW_SET_GPIO_INT SetGpioInt; ++ struct FW_SET_DEBUGMODE SetDebugMode; ++ struct FW_CONFIGURE_BUFFERS ConfigureBuffers; ++ struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers; ++ struct FW_CONFIGURE_UART ConfigureUart; ++ struct FW_WRITE_UART WriteUart; ++ } cmd; ++} __attribute__ ((__packed__)); ++ ++#define NGENE_INTERFACE_VERSION 0x103 ++#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */ ++#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */ ++#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */ ++#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */ ++#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page ++ Max: (1920x1080i60) */ ++ ++#define OVERFLOW_BUFFER_SIZE (8192) ++ ++#define RING_SIZE_VIDEO 4 ++#define RING_SIZE_AUDIO 8 ++#define RING_SIZE_TS 8 ++ ++#define NUM_SCATTER_GATHER_ENTRIES 8 ++ ++#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \ ++ RING_SIZE_VIDEO * 2) + \ ++ (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \ ++ (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \ ++ (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \ ++ (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \ ++ (RING_SIZE_TS * PAGE_SIZE * 4) + \ ++ 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE) ++ ++#define EVENT_QUEUE_SIZE 16 ++ ++/* Gathers the current state of a single channel. */ ++ ++struct SBufferHeader { ++ struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ ++ struct SBufferHeader *Next; ++ void *Buffer1; ++ struct HW_SCATTER_GATHER_ELEMENT *scList1; ++ void *Buffer2; ++ struct HW_SCATTER_GATHER_ELEMENT *scList2; ++}; ++ ++/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ ++#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63) ++ ++enum HWSTATE { ++ HWSTATE_STOP, ++ HWSTATE_STARTUP, ++ HWSTATE_RUN, ++ HWSTATE_PAUSE, ++}; ++ ++enum KSSTATE { ++ KSSTATE_STOP, ++ KSSTATE_ACQUIRE, ++ KSSTATE_PAUSE, ++ KSSTATE_RUN, ++}; ++ ++struct SRingBufferDescriptor { ++ struct SBufferHeader *Head; /* Points to first buffer in ring buffer ++ structure*/ ++ u64 PAHead; /* Physical address of first buffer */ ++ u32 MemSize; /* Memory size of allocated ring buffers ++ (needed for freeing) */ ++ u32 NumBuffers; /* Number of buffers in the ring */ ++ u32 Buffer1Length; /* Allocated length of Buffer 1 */ ++ u32 Buffer2Length; /* Allocated length of Buffer 2 */ ++ void *SCListMem; /* Memory to hold scatter gather lists for this ++ ring */ ++ u64 PASCListMem; /* Physical address .. */ ++ u32 SCListMemSize; /* Size of this memory */ ++}; ++ ++enum STREAMMODEFLAGS { ++ StreamMode_NONE = 0, /* Stream not used */ ++ StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */ ++ StreamMode_TSIN = 2, /* Transport stream input (all) */ ++ StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60 ++ (only stream 0) */ ++ StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */ ++}; ++ ++ ++enum BufferExchangeFlags { ++ BEF_EVEN_FIELD = 0x00000001, ++ BEF_CONTINUATION = 0x00000002, ++ BEF_MORE_DATA = 0x00000004, ++ BEF_OVERFLOW = 0x00000008, ++ DF_SWAP32 = 0x00010000, ++}; ++ ++typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); ++ ++struct MICI_STREAMINFO { ++ IBufferExchange *pExchange; ++ IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ ++ u8 Stream; ++ u8 Flags; ++ u8 Mode; ++ u8 Reserved; ++ u16 nLinesVideo; ++ u16 nBytesPerLineVideo; ++ u16 nLinesVBI; ++ u16 nBytesPerLineVBI; ++ u32 CaptureLength; /* Used for audio and transport stream */ ++}; ++ ++/****************************************************************************/ ++/* STRUCTS ******************************************************************/ ++/****************************************************************************/ ++ ++/* sound hardware definition */ ++#define MIXER_ADDR_TVTUNER 0 ++#define MIXER_ADDR_LAST 0 ++ ++struct ngene_channel; ++ ++/*struct sound chip*/ ++ ++struct mychip { ++ struct ngene_channel *chan; ++ struct snd_card *card; ++ struct pci_dev *pci; ++ struct snd_pcm_substream *substream; ++ struct snd_pcm *pcm; ++ unsigned long port; ++ int irq; ++ spinlock_t mixer_lock; ++ spinlock_t lock; ++ int mixer_volume[MIXER_ADDR_LAST + 1][2]; ++ int capture_source[MIXER_ADDR_LAST + 1][2]; ++}; ++ ++#ifdef NGENE_V4L ++struct ngene_overlay { ++ int tvnorm; ++ struct v4l2_rect w; ++ enum v4l2_field field; ++ struct v4l2_clip *clips; ++ int nclips; ++ int setup_ok; ++}; ++ ++struct ngene_tvnorm { ++ int v4l2_id; ++ char *name; ++ u16 swidth, sheight; /* scaled standard width, height */ ++ int tuner_norm; ++ int soundstd; ++}; ++ ++struct ngene_vopen { ++ struct ngene_channel *ch; ++ enum v4l2_priority prio; ++ int width; ++ int height; ++ int depth; ++ struct videobuf_queue vbuf_q; ++ struct videobuf_queue vbi; ++ int fourcc; ++ int picxcount; ++ int resources; ++ enum v4l2_buf_type type; ++ const struct ngene_format *fmt; ++ ++ const struct ngene_format *ovfmt; ++ struct ngene_overlay ov; ++}; ++#endif ++ ++struct ngene_channel { ++ struct device device; ++ struct i2c_adapter i2c_adapter; ++ ++ struct ngene *dev; ++ int number; ++ int type; ++ int mode; ++ bool has_adapter; ++ bool has_demux; ++ int demod_type; ++ int (*gate_ctrl)(struct dvb_frontend *, int); ++ ++ struct dvb_frontend *fe; ++ struct dvb_frontend *fe2; ++ struct dmxdev dmxdev; ++ struct dvb_demux demux; ++ struct dvb_net dvbnet; ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ int users; ++ struct video_device *v4l_dev; ++ struct dvb_device *ci_dev; ++ struct tasklet_struct demux_tasklet; ++ ++ struct SBufferHeader *nextBuffer; ++ enum KSSTATE State; ++ enum HWSTATE HWState; ++ u8 Stream; ++ u8 Flags; ++ u8 Mode; ++ IBufferExchange *pBufferExchange; ++ IBufferExchange *pBufferExchange2; ++ ++ spinlock_t state_lock; ++ u16 nLines; ++ u16 nBytesPerLine; ++ u16 nVBILines; ++ u16 nBytesPerVBILine; ++ u16 itumode; ++ u32 Capture1Length; ++ u32 Capture2Length; ++ struct SRingBufferDescriptor RingBuffer; ++ struct SRingBufferDescriptor TSRingBuffer; ++ struct SRingBufferDescriptor TSIdleBuffer; ++ ++ u32 DataFormatFlags; ++ ++ int AudioDTOUpdated; ++ u32 AudioDTOValue; ++ ++ int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); ++ u8 lnbh; ++ ++ /* stuff from analog driver */ ++ ++ int minor; ++ struct mychip *mychip; ++ struct snd_card *soundcard; ++ u8 *evenbuffer; ++ u8 dma_on; ++ int soundstreamon; ++ int audiomute; ++ int soundbuffisallocated; ++ int sndbuffflag; ++ int tun_rdy; ++ int dec_rdy; ++ int tun_dec_rdy; ++ int lastbufferflag; ++ ++ struct ngene_tvnorm *tvnorms; ++ int tvnorm_num; ++ int tvnorm; ++ ++#ifdef NGENE_V4L ++ int videousers; ++ struct v4l2_prio_state prio; ++ struct ngene_vopen init; ++ int resources; ++ struct v4l2_framebuffer fbuf; ++ struct ngene_buffer *screen; /* overlay */ ++ struct list_head capture; /* video capture queue */ ++ spinlock_t s_lock; ++ struct semaphore reslock; ++#endif ++ ++ int running; ++}; ++ ++ ++struct ngene_ci { ++ struct device device; ++ struct i2c_adapter i2c_adapter; ++ ++ struct ngene *dev; ++ struct dvb_ca_en50221 *en; ++}; ++ ++struct ngene; ++ ++typedef void (rx_cb_t)(struct ngene *, u32, u8); ++typedef void (tx_cb_t)(struct ngene *, u32); ++ ++struct ngene { ++ int nr; ++ struct pci_dev *pci_dev; ++ unsigned char *iomem; ++ ++ /*struct i2c_adapter i2c_adapter;*/ ++ ++ u32 device_version; ++ u32 fw_interface_version; ++ u32 icounts; ++ bool msi_enabled; ++ bool cmd_timeout_workaround; ++ ++ u8 *CmdDoneByte; ++ int BootFirmware; ++ void *OverflowBuffer; ++ dma_addr_t PAOverflowBuffer; ++ void *FWInterfaceBuffer; ++ dma_addr_t PAFWInterfaceBuffer; ++ u8 *ngenetohost; ++ u8 *hosttongene; ++ ++ struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; ++ int EventQueueOverflowCount; ++ int EventQueueOverflowFlag; ++ struct tasklet_struct event_tasklet; ++ struct EVENT_BUFFER *EventBuffer; ++ int EventQueueWriteIndex; ++ int EventQueueReadIndex; ++ ++ wait_queue_head_t cmd_wq; ++ int cmd_done; ++ struct semaphore cmd_mutex; ++ struct semaphore stream_mutex; ++ struct semaphore pll_mutex; ++ struct semaphore i2c_switch_mutex; ++ int i2c_current_channel; ++ int i2c_current_bus; ++ spinlock_t cmd_lock; ++ ++ struct dvb_adapter adapter[MAX_STREAM]; ++ struct dvb_adapter *first_adapter; /* "one_adapter" modprobe opt */ ++ struct ngene_channel channel[MAX_STREAM]; ++ ++ struct ngene_info *card_info; ++ ++ tx_cb_t *TxEventNotify; ++ rx_cb_t *RxEventNotify; ++ int tx_busy; ++ wait_queue_head_t tx_wq; ++ wait_queue_head_t rx_wq; ++#define UART_RBUF_LEN 4096 ++ u8 uart_rbuf[UART_RBUF_LEN]; ++ int uart_rp, uart_wp; ++ ++#define TS_FILLER 0x6f ++ ++ u8 *tsout_buf; ++#define TSOUT_BUF_SIZE (512*188*8) ++ struct dvb_ringbuffer tsout_rbuf; ++ ++ u8 *tsin_buf; ++#define TSIN_BUF_SIZE (512*188*8) ++ struct dvb_ringbuffer tsin_rbuf; ++ ++ u8 *ain_buf; ++#define AIN_BUF_SIZE (128*1024) ++ struct dvb_ringbuffer ain_rbuf; ++ ++ ++ u8 *vin_buf; ++#define VIN_BUF_SIZE (4*1920*1080) ++ struct dvb_ringbuffer vin_rbuf; ++ ++ unsigned long exp_val; ++ int prev_cmd; ++ ++ struct ngene_ci ci; ++}; ++ ++struct ngene_info { ++ int type; ++#define NGENE_APP 0 ++#define NGENE_TERRATEC 1 ++#define NGENE_SIDEWINDER 2 ++#define NGENE_RACER 3 ++#define NGENE_VIPER 4 ++#define NGENE_PYTHON 5 ++#define NGENE_VBOX_V1 6 ++#define NGENE_VBOX_V2 7 ++ ++ int fw_version; ++ bool msi_supported; ++ char *name; ++ ++ int io_type[MAX_STREAM]; ++#define NGENE_IO_NONE 0 ++#define NGENE_IO_TV 1 ++#define NGENE_IO_HDTV 2 ++#define NGENE_IO_TSIN 4 ++#define NGENE_IO_TSOUT 8 ++#define NGENE_IO_AIN 16 ++ ++ void *fe_config[4]; ++ void *tuner_config[4]; ++ ++ int (*demod_attach[4])(struct ngene_channel *); ++ int (*tuner_attach[4])(struct ngene_channel *); ++ ++ u8 avf[4]; ++ u8 msp[4]; ++ u8 demoda[4]; ++ u8 lnb[4]; ++ int i2c_access; ++ u8 ntsc; ++ u8 tsf[4]; ++ u8 i2s[4]; ++ ++ int (*gate_ctrl)(struct dvb_frontend *, int); ++ int (*switch_ctrl)(struct ngene_channel *, int, int); ++}; ++ ++#ifdef NGENE_V4L ++struct ngene_format { ++ char *name; ++ int fourcc; /* video4linux 2 */ ++ int btformat; /* BT848_COLOR_FMT_* */ ++ int format; ++ int btswap; /* BT848_COLOR_CTL_* */ ++ int depth; /* bit/pixel */ ++ int flags; ++ int hshift, vshift; /* for planar modes */ ++ int palette; ++}; ++ ++#define RESOURCE_OVERLAY 1 ++#define RESOURCE_VIDEO 2 ++#define RESOURCE_VBI 4 ++ ++struct ngene_buffer { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* ngene specific */ ++ const struct ngene_format *fmt; ++ int tvnorm; ++ int btformat; ++ int btswap; ++}; ++#endif ++ ++ ++/* Provided by ngene-core.c */ ++int __devinit ngene_probe(struct pci_dev *pci_dev, ++ const struct pci_device_id *id); ++void __devexit ngene_remove(struct pci_dev *pdev); ++void ngene_shutdown(struct pci_dev *pdev); ++int ngene_command(struct ngene *dev, struct ngene_command *com); ++int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level); ++void set_transfer(struct ngene_channel *chan, int state); ++void FillTSBuffer(void *Buffer, int Length, u32 Flags); ++ ++/* Provided by ngene-i2c.c */ ++int ngene_i2c_init(struct ngene *dev, int dev_nr); ++ ++/* Provided by ngene-dvb.c */ ++extern struct dvb_device ngene_dvbdev_ci; ++void *tsout_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); ++void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags); ++int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed); ++int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed); ++int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, ++ int (*start_feed)(struct dvb_demux_feed *), ++ int (*stop_feed)(struct dvb_demux_feed *), ++ void *priv); ++int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, ++ struct dvb_demux *dvbdemux, ++ struct dmx_frontend *hw_frontend, ++ struct dmx_frontend *mem_frontend, ++ struct dvb_adapter *dvb_adapter); ++ ++#endif ++ ++/* LocalWords: Endif ++ */ +diff --git a/drivers/media/pci/pluto2/Kconfig b/drivers/media/pci/pluto2/Kconfig +new file mode 100644 +index 0000000..7d8e6e8 +--- /dev/null ++++ b/drivers/media/pci/pluto2/Kconfig +@@ -0,0 +1,15 @@ ++config DVB_PLUTO2 ++ tristate "Pluto2 cards" ++ depends on DVB_CORE && PCI && I2C ++ select I2C_ALGOBIT ++ select DVB_TDA1004X ++ help ++ Support for PCI cards based on the Pluto2 FPGA like the Satelco ++ Easywatch Mobile Terrestrial DVB-T Receiver. ++ ++ Since these cards have no MPEG decoder onboard, they transmit ++ only compressed MPEG data over the PCI bus, so you need ++ an external software decoder to watch TV on your computer. ++ ++ Say Y or M if you own such a device and want to use it. ++ +diff --git a/drivers/media/pci/pluto2/Makefile b/drivers/media/pci/pluto2/Makefile +new file mode 100644 +index 0000000..524bf84 +--- /dev/null ++++ b/drivers/media/pci/pluto2/Makefile +@@ -0,0 +1,3 @@ ++obj-$(CONFIG_DVB_PLUTO2) += pluto2.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ +diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c +new file mode 100644 +index 0000000..f148b19 +--- /dev/null ++++ b/drivers/media/pci/pluto2/pluto2.c +@@ -0,0 +1,815 @@ ++/* ++ * pluto2.c - Satelco Easywatch Mobile Terrestrial Receiver [DVB-T] ++ * ++ * Copyright (C) 2005 Andreas Oberritter ++ * ++ * based on pluto2.c 1.10 - http://instinct-wp8.no-ip.org/pluto/ ++ * by Dany Salman ++ * Copyright (c) 2004 TDF ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "demux.h" ++#include "dmxdev.h" ++#include "dvb_demux.h" ++#include "dvb_frontend.h" ++#include "dvb_net.h" ++#include "dvbdev.h" ++#include "tda1004x.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define DRIVER_NAME "pluto2" ++ ++#define REG_PIDn(n) ((n) << 2) /* PID n pattern registers */ ++#define REG_PCAR 0x0020 /* PC address register */ ++#define REG_TSCR 0x0024 /* TS ctrl & status */ ++#define REG_MISC 0x0028 /* miscellaneous */ ++#define REG_MMAC 0x002c /* MSB MAC address */ ++#define REG_IMAC 0x0030 /* ISB MAC address */ ++#define REG_LMAC 0x0034 /* LSB MAC address */ ++#define REG_SPID 0x0038 /* SPI data */ ++#define REG_SLCS 0x003c /* serial links ctrl/status */ ++ ++#define PID0_NOFIL (0x0001 << 16) ++#define PIDn_ENP (0x0001 << 15) ++#define PID0_END (0x0001 << 14) ++#define PID0_AFIL (0x0001 << 13) ++#define PIDn_PID (0x1fff << 0) ++ ++#define TSCR_NBPACKETS (0x00ff << 24) ++#define TSCR_DEM (0x0001 << 17) ++#define TSCR_DE (0x0001 << 16) ++#define TSCR_RSTN (0x0001 << 15) ++#define TSCR_MSKO (0x0001 << 14) ++#define TSCR_MSKA (0x0001 << 13) ++#define TSCR_MSKL (0x0001 << 12) ++#define TSCR_OVR (0x0001 << 11) ++#define TSCR_AFUL (0x0001 << 10) ++#define TSCR_LOCK (0x0001 << 9) ++#define TSCR_IACK (0x0001 << 8) ++#define TSCR_ADEF (0x007f << 0) ++ ++#define MISC_DVR (0x0fff << 4) ++#define MISC_ALED (0x0001 << 3) ++#define MISC_FRST (0x0001 << 2) ++#define MISC_LED1 (0x0001 << 1) ++#define MISC_LED0 (0x0001 << 0) ++ ++#define SPID_SPIDR (0x00ff << 0) ++ ++#define SLCS_SCL (0x0001 << 7) ++#define SLCS_SDA (0x0001 << 6) ++#define SLCS_CSN (0x0001 << 2) ++#define SLCS_OVR (0x0001 << 1) ++#define SLCS_SWC (0x0001 << 0) ++ ++#define TS_DMA_PACKETS (8) ++#define TS_DMA_BYTES (188 * TS_DMA_PACKETS) ++ ++#define I2C_ADDR_TDA10046 0x10 ++#define I2C_ADDR_TUA6034 0xc2 ++#define NHWFILTERS 8 ++ ++struct pluto { ++ /* pci */ ++ struct pci_dev *pdev; ++ u8 __iomem *io_mem; ++ ++ /* dvb */ ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ struct dmxdev dmxdev; ++ struct dvb_adapter dvb_adapter; ++ struct dvb_demux demux; ++ struct dvb_frontend *fe; ++ struct dvb_net dvbnet; ++ unsigned int full_ts_users; ++ unsigned int users; ++ ++ /* i2c */ ++ struct i2c_algo_bit_data i2c_bit; ++ struct i2c_adapter i2c_adap; ++ unsigned int i2cbug; ++ ++ /* irq */ ++ unsigned int overflow; ++ unsigned int dead; ++ ++ /* dma */ ++ dma_addr_t dma_addr; ++ u8 dma_buf[TS_DMA_BYTES]; ++ u8 dummy[4096]; ++}; ++ ++static inline struct pluto *feed_to_pluto(struct dvb_demux_feed *feed) ++{ ++ return container_of(feed->demux, struct pluto, demux); ++} ++ ++static inline struct pluto *frontend_to_pluto(struct dvb_frontend *fe) ++{ ++ return container_of(fe->dvb, struct pluto, dvb_adapter); ++} ++ ++static inline u32 pluto_readreg(struct pluto *pluto, u32 reg) ++{ ++ return readl(&pluto->io_mem[reg]); ++} ++ ++static inline void pluto_writereg(struct pluto *pluto, u32 reg, u32 val) ++{ ++ writel(val, &pluto->io_mem[reg]); ++} ++ ++static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) ++{ ++ u32 val = readl(&pluto->io_mem[reg]); ++ val &= ~mask; ++ val |= bits; ++ writel(val, &pluto->io_mem[reg]); ++} ++ ++static void pluto_write_tscr(struct pluto *pluto, u32 val) ++{ ++ /* set the number of packets */ ++ val &= ~TSCR_ADEF; ++ val |= TS_DMA_PACKETS / 2; ++ ++ pluto_writereg(pluto, REG_TSCR, val); ++} ++ ++static void pluto_setsda(void *data, int state) ++{ ++ struct pluto *pluto = data; ++ ++ if (state) ++ pluto_rw(pluto, REG_SLCS, SLCS_SDA, SLCS_SDA); ++ else ++ pluto_rw(pluto, REG_SLCS, SLCS_SDA, 0); ++} ++ ++static void pluto_setscl(void *data, int state) ++{ ++ struct pluto *pluto = data; ++ ++ if (state) ++ pluto_rw(pluto, REG_SLCS, SLCS_SCL, SLCS_SCL); ++ else ++ pluto_rw(pluto, REG_SLCS, SLCS_SCL, 0); ++ ++ /* try to detect i2c_inb() to workaround hardware bug: ++ * reset SDA to high after SCL has been set to low */ ++ if ((state) && (pluto->i2cbug == 0)) { ++ pluto->i2cbug = 1; ++ } else { ++ if ((!state) && (pluto->i2cbug == 1)) ++ pluto_setsda(pluto, 1); ++ pluto->i2cbug = 0; ++ } ++} ++ ++static int pluto_getsda(void *data) ++{ ++ struct pluto *pluto = data; ++ ++ return pluto_readreg(pluto, REG_SLCS) & SLCS_SDA; ++} ++ ++static int pluto_getscl(void *data) ++{ ++ struct pluto *pluto = data; ++ ++ return pluto_readreg(pluto, REG_SLCS) & SLCS_SCL; ++} ++ ++static void pluto_reset_frontend(struct pluto *pluto, int reenable) ++{ ++ u32 val = pluto_readreg(pluto, REG_MISC); ++ ++ if (val & MISC_FRST) { ++ val &= ~MISC_FRST; ++ pluto_writereg(pluto, REG_MISC, val); ++ } ++ if (reenable) { ++ val |= MISC_FRST; ++ pluto_writereg(pluto, REG_MISC, val); ++ } ++} ++ ++static void pluto_reset_ts(struct pluto *pluto, int reenable) ++{ ++ u32 val = pluto_readreg(pluto, REG_TSCR); ++ ++ if (val & TSCR_RSTN) { ++ val &= ~TSCR_RSTN; ++ pluto_write_tscr(pluto, val); ++ } ++ if (reenable) { ++ val |= TSCR_RSTN; ++ pluto_write_tscr(pluto, val); ++ } ++} ++ ++static void pluto_set_dma_addr(struct pluto *pluto) ++{ ++ pluto_writereg(pluto, REG_PCAR, pluto->dma_addr); ++} ++ ++static int __devinit pluto_dma_map(struct pluto *pluto) ++{ ++ pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf, ++ TS_DMA_BYTES, PCI_DMA_FROMDEVICE); ++ ++ return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr); ++} ++ ++static void pluto_dma_unmap(struct pluto *pluto) ++{ ++ pci_unmap_single(pluto->pdev, pluto->dma_addr, ++ TS_DMA_BYTES, PCI_DMA_FROMDEVICE); ++} ++ ++static int pluto_start_feed(struct dvb_demux_feed *f) ++{ ++ struct pluto *pluto = feed_to_pluto(f); ++ ++ /* enable PID filtering */ ++ if (pluto->users++ == 0) ++ pluto_rw(pluto, REG_PIDn(0), PID0_AFIL | PID0_NOFIL, 0); ++ ++ if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) ++ pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, PIDn_ENP | f->pid); ++ else if (pluto->full_ts_users++ == 0) ++ pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, PID0_NOFIL); ++ ++ return 0; ++} ++ ++static int pluto_stop_feed(struct dvb_demux_feed *f) ++{ ++ struct pluto *pluto = feed_to_pluto(f); ++ ++ /* disable PID filtering */ ++ if (--pluto->users == 0) ++ pluto_rw(pluto, REG_PIDn(0), PID0_AFIL, PID0_AFIL); ++ ++ if ((f->pid < 0x2000) && (f->index < NHWFILTERS)) ++ pluto_rw(pluto, REG_PIDn(f->index), PIDn_ENP | PIDn_PID, 0x1fff); ++ else if (--pluto->full_ts_users == 0) ++ pluto_rw(pluto, REG_PIDn(0), PID0_NOFIL, 0); ++ ++ return 0; ++} ++ ++static void pluto_dma_end(struct pluto *pluto, unsigned int nbpackets) ++{ ++ /* synchronize the DMA transfer with the CPU ++ * first so that we see updated contents. */ ++ pci_dma_sync_single_for_cpu(pluto->pdev, pluto->dma_addr, ++ TS_DMA_BYTES, PCI_DMA_FROMDEVICE); ++ ++ /* Workaround for broken hardware: ++ * [1] On startup NBPACKETS seems to contain an uninitialized value, ++ * but no packets have been transferred. ++ * [2] Sometimes (actually very often) NBPACKETS stays at zero ++ * although one packet has been transferred. ++ * [3] Sometimes (actually rarely), the card gets into an erroneous ++ * mode where it continuously generates interrupts, claiming it ++ * has received nbpackets>TS_DMA_PACKETS packets, but no packet ++ * has been transferred. Only a reset seems to solve this ++ */ ++ if ((nbpackets == 0) || (nbpackets > TS_DMA_PACKETS)) { ++ unsigned int i = 0; ++ while (pluto->dma_buf[i] == 0x47) ++ i += 188; ++ nbpackets = i / 188; ++ if (i == 0) { ++ pluto_reset_ts(pluto, 1); ++ dev_printk(KERN_DEBUG, &pluto->pdev->dev, "resetting TS because of invalid packet counter\n"); ++ } ++ } ++ ++ dvb_dmx_swfilter_packets(&pluto->demux, pluto->dma_buf, nbpackets); ++ ++ /* clear the dma buffer. this is needed to be able to identify ++ * new valid ts packets above */ ++ memset(pluto->dma_buf, 0, nbpackets * 188); ++ ++ /* reset the dma address */ ++ pluto_set_dma_addr(pluto); ++ ++ /* sync the buffer and give it back to the card */ ++ pci_dma_sync_single_for_device(pluto->pdev, pluto->dma_addr, ++ TS_DMA_BYTES, PCI_DMA_FROMDEVICE); ++} ++ ++static irqreturn_t pluto_irq(int irq, void *dev_id) ++{ ++ struct pluto *pluto = dev_id; ++ u32 tscr; ++ ++ /* check whether an interrupt occurred on this device */ ++ tscr = pluto_readreg(pluto, REG_TSCR); ++ if (!(tscr & (TSCR_DE | TSCR_OVR))) ++ return IRQ_NONE; ++ ++ if (tscr == 0xffffffff) { ++ if (pluto->dead == 0) ++ dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n"); ++ /* It's dead Jim */ ++ pluto->dead = 1; ++ return IRQ_HANDLED; ++ } ++ ++ /* dma end interrupt */ ++ if (tscr & TSCR_DE) { ++ pluto_dma_end(pluto, (tscr & TSCR_NBPACKETS) >> 24); ++ /* overflow interrupt */ ++ if (tscr & TSCR_OVR) ++ pluto->overflow++; ++ if (pluto->overflow) { ++ dev_err(&pluto->pdev->dev, "overflow irq (%d)\n", ++ pluto->overflow); ++ pluto_reset_ts(pluto, 1); ++ pluto->overflow = 0; ++ } ++ } else if (tscr & TSCR_OVR) { ++ pluto->overflow++; ++ } ++ ++ /* ACK the interrupt */ ++ pluto_write_tscr(pluto, tscr | TSCR_IACK); ++ ++ return IRQ_HANDLED; ++} ++ ++static void __devinit pluto_enable_irqs(struct pluto *pluto) ++{ ++ u32 val = pluto_readreg(pluto, REG_TSCR); ++ ++ /* disable AFUL and LOCK interrupts */ ++ val |= (TSCR_MSKA | TSCR_MSKL); ++ /* enable DMA and OVERFLOW interrupts */ ++ val &= ~(TSCR_DEM | TSCR_MSKO); ++ /* clear pending interrupts */ ++ val |= TSCR_IACK; ++ ++ pluto_write_tscr(pluto, val); ++} ++ ++static void pluto_disable_irqs(struct pluto *pluto) ++{ ++ u32 val = pluto_readreg(pluto, REG_TSCR); ++ ++ /* disable all interrupts */ ++ val |= (TSCR_DEM | TSCR_MSKO | TSCR_MSKA | TSCR_MSKL); ++ /* clear pending interrupts */ ++ val |= TSCR_IACK; ++ ++ pluto_write_tscr(pluto, val); ++} ++ ++static int __devinit pluto_hw_init(struct pluto *pluto) ++{ ++ pluto_reset_frontend(pluto, 1); ++ ++ /* set automatic LED control by FPGA */ ++ pluto_rw(pluto, REG_MISC, MISC_ALED, MISC_ALED); ++ ++ /* set data endianess */ ++#ifdef __LITTLE_ENDIAN ++ pluto_rw(pluto, REG_PIDn(0), PID0_END, PID0_END); ++#else ++ pluto_rw(pluto, REG_PIDn(0), PID0_END, 0); ++#endif ++ /* map DMA and set address */ ++ pluto_dma_map(pluto); ++ pluto_set_dma_addr(pluto); ++ ++ /* enable interrupts */ ++ pluto_enable_irqs(pluto); ++ ++ /* reset TS logic */ ++ pluto_reset_ts(pluto, 1); ++ ++ return 0; ++} ++ ++static void pluto_hw_exit(struct pluto *pluto) ++{ ++ /* disable interrupts */ ++ pluto_disable_irqs(pluto); ++ ++ pluto_reset_ts(pluto, 0); ++ ++ /* LED: disable automatic control, enable yellow, disable green */ ++ pluto_rw(pluto, REG_MISC, MISC_ALED | MISC_LED1 | MISC_LED0, MISC_LED1); ++ ++ /* unmap DMA */ ++ pluto_dma_unmap(pluto); ++ ++ pluto_reset_frontend(pluto, 0); ++} ++ ++static inline u32 divide(u32 numerator, u32 denominator) ++{ ++ if (denominator == 0) ++ return ~0; ++ ++ return DIV_ROUND_CLOSEST(numerator, denominator); ++} ++ ++/* LG Innotek TDTE-E001P (Infineon TUA6034) */ ++static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct pluto *pluto = frontend_to_pluto(fe); ++ struct i2c_msg msg; ++ int ret; ++ u8 buf[4]; ++ u32 div; ++ ++ // Fref = 166.667 Hz ++ // Fref * 3 = 500.000 Hz ++ // IF = 36166667 ++ // IF / Fref = 217 ++ //div = divide(p->frequency + 36166667, 166667); ++ div = divide(p->frequency * 3, 500000) + 217; ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = (div >> 0) & 0xff; ++ ++ if (p->frequency < 611000000) ++ buf[2] = 0xb4; ++ else if (p->frequency < 811000000) ++ buf[2] = 0xbc; ++ else ++ buf[2] = 0xf4; ++ ++ // VHF: 174-230 MHz ++ // center: 350 MHz ++ // UHF: 470-862 MHz ++ if (p->frequency < 350000000) ++ buf[3] = 0x02; ++ else ++ buf[3] = 0x04; ++ ++ if (p->bandwidth_hz == 8000000) ++ buf[3] |= 0x08; ++ ++ msg.addr = I2C_ADDR_TUA6034 >> 1; ++ msg.flags = 0; ++ msg.buf = buf; ++ msg.len = sizeof(buf); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ ret = i2c_transfer(&pluto->i2c_adap, &msg, 1); ++ if (ret < 0) ++ return ret; ++ else if (ret == 0) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int pluto2_request_firmware(struct dvb_frontend *fe, ++ const struct firmware **fw, char *name) ++{ ++ struct pluto *pluto = frontend_to_pluto(fe); ++ ++ return request_firmware(fw, name, &pluto->pdev->dev); ++} ++ ++static struct tda1004x_config pluto2_fe_config __devinitdata = { ++ .demod_address = I2C_ADDR_TDA10046 >> 1, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .request_firmware = pluto2_request_firmware, ++}; ++ ++static int __devinit frontend_init(struct pluto *pluto) ++{ ++ int ret; ++ ++ pluto->fe = tda10046_attach(&pluto2_fe_config, &pluto->i2c_adap); ++ if (!pluto->fe) { ++ dev_err(&pluto->pdev->dev, "could not attach frontend\n"); ++ return -ENODEV; ++ } ++ pluto->fe->ops.tuner_ops.set_params = lg_tdtpe001p_tuner_set_params; ++ ++ ret = dvb_register_frontend(&pluto->dvb_adapter, pluto->fe); ++ if (ret < 0) { ++ if (pluto->fe->ops.release) ++ pluto->fe->ops.release(pluto->fe); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void __devinit pluto_read_rev(struct pluto *pluto) ++{ ++ u32 val = pluto_readreg(pluto, REG_MISC) & MISC_DVR; ++ dev_info(&pluto->pdev->dev, "board revision %d.%d\n", ++ (val >> 12) & 0x0f, (val >> 4) & 0xff); ++} ++ ++static void __devinit pluto_read_mac(struct pluto *pluto, u8 *mac) ++{ ++ u32 val = pluto_readreg(pluto, REG_MMAC); ++ mac[0] = (val >> 8) & 0xff; ++ mac[1] = (val >> 0) & 0xff; ++ ++ val = pluto_readreg(pluto, REG_IMAC); ++ mac[2] = (val >> 8) & 0xff; ++ mac[3] = (val >> 0) & 0xff; ++ ++ val = pluto_readreg(pluto, REG_LMAC); ++ mac[4] = (val >> 8) & 0xff; ++ mac[5] = (val >> 0) & 0xff; ++ ++ dev_info(&pluto->pdev->dev, "MAC %pM\n", mac); ++} ++ ++static int __devinit pluto_read_serial(struct pluto *pluto) ++{ ++ struct pci_dev *pdev = pluto->pdev; ++ unsigned int i, j; ++ u8 __iomem *cis; ++ ++ cis = pci_iomap(pdev, 1, 0); ++ if (!cis) ++ return -EIO; ++ ++ dev_info(&pdev->dev, "S/N "); ++ ++ for (i = 0xe0; i < 0x100; i += 4) { ++ u32 val = readl(&cis[i]); ++ for (j = 0; j < 32; j += 8) { ++ if ((val & 0xff) == 0xff) ++ goto out; ++ printk("%c", val & 0xff); ++ val >>= 8; ++ } ++ } ++out: ++ printk("\n"); ++ pci_iounmap(pdev, cis); ++ ++ return 0; ++} ++ ++static int __devinit pluto2_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ struct pluto *pluto; ++ struct dvb_adapter *dvb_adapter; ++ struct dvb_demux *dvbdemux; ++ struct dmx_demux *dmx; ++ int ret = -ENOMEM; ++ ++ pluto = kzalloc(sizeof(struct pluto), GFP_KERNEL); ++ if (!pluto) ++ goto out; ++ ++ pluto->pdev = pdev; ++ ++ ret = pci_enable_device(pdev); ++ if (ret < 0) ++ goto err_kfree; ++ ++ /* enable interrupts */ ++ pci_write_config_dword(pdev, 0x6c, 0x8000); ++ ++ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ pci_set_master(pdev); ++ ++ ret = pci_request_regions(pdev, DRIVER_NAME); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ pluto->io_mem = pci_iomap(pdev, 0, 0x40); ++ if (!pluto->io_mem) { ++ ret = -EIO; ++ goto err_pci_release_regions; ++ } ++ ++ pci_set_drvdata(pdev, pluto); ++ ++ ret = request_irq(pdev->irq, pluto_irq, IRQF_SHARED, DRIVER_NAME, pluto); ++ if (ret < 0) ++ goto err_pci_iounmap; ++ ++ ret = pluto_hw_init(pluto); ++ if (ret < 0) ++ goto err_free_irq; ++ ++ /* i2c */ ++ i2c_set_adapdata(&pluto->i2c_adap, pluto); ++ strcpy(pluto->i2c_adap.name, DRIVER_NAME); ++ pluto->i2c_adap.owner = THIS_MODULE; ++ pluto->i2c_adap.dev.parent = &pdev->dev; ++ pluto->i2c_adap.algo_data = &pluto->i2c_bit; ++ pluto->i2c_bit.data = pluto; ++ pluto->i2c_bit.setsda = pluto_setsda; ++ pluto->i2c_bit.setscl = pluto_setscl; ++ pluto->i2c_bit.getsda = pluto_getsda; ++ pluto->i2c_bit.getscl = pluto_getscl; ++ pluto->i2c_bit.udelay = 10; ++ pluto->i2c_bit.timeout = 10; ++ ++ /* Raise SCL and SDA */ ++ pluto_setsda(pluto, 1); ++ pluto_setscl(pluto, 1); ++ ++ ret = i2c_bit_add_bus(&pluto->i2c_adap); ++ if (ret < 0) ++ goto err_pluto_hw_exit; ++ ++ /* dvb */ ++ ret = dvb_register_adapter(&pluto->dvb_adapter, DRIVER_NAME, ++ THIS_MODULE, &pdev->dev, adapter_nr); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ dvb_adapter = &pluto->dvb_adapter; ++ ++ pluto_read_rev(pluto); ++ pluto_read_serial(pluto); ++ pluto_read_mac(pluto, dvb_adapter->proposed_mac); ++ ++ dvbdemux = &pluto->demux; ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = pluto_start_feed; ++ dvbdemux->stop_feed = pluto_stop_feed; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | ++ DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); ++ ret = dvb_dmx_init(dvbdemux); ++ if (ret < 0) ++ goto err_dvb_unregister_adapter; ++ ++ dmx = &dvbdemux->dmx; ++ ++ pluto->hw_frontend.source = DMX_FRONTEND_0; ++ pluto->mem_frontend.source = DMX_MEMORY_FE; ++ pluto->dmxdev.filternum = NHWFILTERS; ++ pluto->dmxdev.demux = dmx; ++ ++ ret = dvb_dmxdev_init(&pluto->dmxdev, dvb_adapter); ++ if (ret < 0) ++ goto err_dvb_dmx_release; ++ ++ ret = dmx->add_frontend(dmx, &pluto->hw_frontend); ++ if (ret < 0) ++ goto err_dvb_dmxdev_release; ++ ++ ret = dmx->add_frontend(dmx, &pluto->mem_frontend); ++ if (ret < 0) ++ goto err_remove_hw_frontend; ++ ++ ret = dmx->connect_frontend(dmx, &pluto->hw_frontend); ++ if (ret < 0) ++ goto err_remove_mem_frontend; ++ ++ ret = frontend_init(pluto); ++ if (ret < 0) ++ goto err_disconnect_frontend; ++ ++ dvb_net_init(dvb_adapter, &pluto->dvbnet, dmx); ++out: ++ return ret; ++ ++err_disconnect_frontend: ++ dmx->disconnect_frontend(dmx); ++err_remove_mem_frontend: ++ dmx->remove_frontend(dmx, &pluto->mem_frontend); ++err_remove_hw_frontend: ++ dmx->remove_frontend(dmx, &pluto->hw_frontend); ++err_dvb_dmxdev_release: ++ dvb_dmxdev_release(&pluto->dmxdev); ++err_dvb_dmx_release: ++ dvb_dmx_release(dvbdemux); ++err_dvb_unregister_adapter: ++ dvb_unregister_adapter(dvb_adapter); ++err_i2c_del_adapter: ++ i2c_del_adapter(&pluto->i2c_adap); ++err_pluto_hw_exit: ++ pluto_hw_exit(pluto); ++err_free_irq: ++ free_irq(pdev->irq, pluto); ++err_pci_iounmap: ++ pci_iounmap(pdev, pluto->io_mem); ++err_pci_release_regions: ++ pci_release_regions(pdev); ++err_pci_disable_device: ++ pci_disable_device(pdev); ++err_kfree: ++ pci_set_drvdata(pdev, NULL); ++ kfree(pluto); ++ goto out; ++} ++ ++static void __devexit pluto2_remove(struct pci_dev *pdev) ++{ ++ struct pluto *pluto = pci_get_drvdata(pdev); ++ struct dvb_adapter *dvb_adapter = &pluto->dvb_adapter; ++ struct dvb_demux *dvbdemux = &pluto->demux; ++ struct dmx_demux *dmx = &dvbdemux->dmx; ++ ++ dmx->close(dmx); ++ dvb_net_release(&pluto->dvbnet); ++ if (pluto->fe) ++ dvb_unregister_frontend(pluto->fe); ++ ++ dmx->disconnect_frontend(dmx); ++ dmx->remove_frontend(dmx, &pluto->mem_frontend); ++ dmx->remove_frontend(dmx, &pluto->hw_frontend); ++ dvb_dmxdev_release(&pluto->dmxdev); ++ dvb_dmx_release(dvbdemux); ++ dvb_unregister_adapter(dvb_adapter); ++ i2c_del_adapter(&pluto->i2c_adap); ++ pluto_hw_exit(pluto); ++ free_irq(pdev->irq, pluto); ++ pci_iounmap(pdev, pluto->io_mem); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++ pci_set_drvdata(pdev, NULL); ++ kfree(pluto); ++} ++ ++#ifndef PCI_VENDOR_ID_SCM ++#define PCI_VENDOR_ID_SCM 0x0432 ++#endif ++#ifndef PCI_DEVICE_ID_PLUTO2 ++#define PCI_DEVICE_ID_PLUTO2 0x0001 ++#endif ++ ++static struct pci_device_id pluto2_id_table[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_SCM, ++ .device = PCI_DEVICE_ID_PLUTO2, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ /* empty */ ++ }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, pluto2_id_table); ++ ++static struct pci_driver pluto2_driver = { ++ .name = DRIVER_NAME, ++ .id_table = pluto2_id_table, ++ .probe = pluto2_probe, ++ .remove = __devexit_p(pluto2_remove), ++}; ++ ++static int __init pluto2_init(void) ++{ ++ return pci_register_driver(&pluto2_driver); ++} ++ ++static void __exit pluto2_exit(void) ++{ ++ pci_unregister_driver(&pluto2_driver); ++} ++ ++module_init(pluto2_init); ++module_exit(pluto2_exit); ++ ++MODULE_AUTHOR("Andreas Oberritter "); ++MODULE_DESCRIPTION("Pluto2 driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/pt1/Kconfig b/drivers/media/pci/pt1/Kconfig +new file mode 100644 +index 0000000..24501d5 +--- /dev/null ++++ b/drivers/media/pci/pt1/Kconfig +@@ -0,0 +1,12 @@ ++config DVB_PT1 ++ tristate "PT1 cards" ++ depends on DVB_CORE && PCI && I2C ++ help ++ Support for Earthsoft PT1 PCI cards. ++ ++ Since these cards have no MPEG decoder onboard, they transmit ++ only compressed MPEG data over the PCI bus, so you need ++ an external software decoder to watch TV on your computer. ++ ++ Say Y or M if you own such a device and want to use it. ++ +diff --git a/drivers/media/pci/pt1/Makefile b/drivers/media/pci/pt1/Makefile +new file mode 100644 +index 0000000..98e3912 +--- /dev/null ++++ b/drivers/media/pci/pt1/Makefile +@@ -0,0 +1,5 @@ ++earth-pt1-objs := pt1.o va1j5jf8007s.o va1j5jf8007t.o ++ ++obj-$(CONFIG_DVB_PT1) += earth-pt1.o ++ ++ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends +diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c +new file mode 100644 +index 0000000..15b35c4 +--- /dev/null ++++ b/drivers/media/pci/pt1/pt1.c +@@ -0,0 +1,1246 @@ ++/* ++ * driver for Earthsoft PT1/PT2 ++ * ++ * Copyright (C) 2009 HIRANO Takahito ++ * ++ * based on pt1dvr - http://pt1dvr.sourceforge.jp/ ++ * by Tomoaki Ishikawa ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvbdev.h" ++#include "dvb_demux.h" ++#include "dmxdev.h" ++#include "dvb_net.h" ++#include "dvb_frontend.h" ++ ++#include "va1j5jf8007t.h" ++#include "va1j5jf8007s.h" ++ ++#define DRIVER_NAME "earth-pt1" ++ ++#define PT1_PAGE_SHIFT 12 ++#define PT1_PAGE_SIZE (1 << PT1_PAGE_SHIFT) ++#define PT1_NR_UPACKETS 1024 ++#define PT1_NR_BUFS 511 ++ ++struct pt1_buffer_page { ++ __le32 upackets[PT1_NR_UPACKETS]; ++}; ++ ++struct pt1_table_page { ++ __le32 next_pfn; ++ __le32 buf_pfns[PT1_NR_BUFS]; ++}; ++ ++struct pt1_buffer { ++ struct pt1_buffer_page *page; ++ dma_addr_t addr; ++}; ++ ++struct pt1_table { ++ struct pt1_table_page *page; ++ dma_addr_t addr; ++ struct pt1_buffer bufs[PT1_NR_BUFS]; ++}; ++ ++#define PT1_NR_ADAPS 4 ++ ++struct pt1_adapter; ++ ++struct pt1 { ++ struct pci_dev *pdev; ++ void __iomem *regs; ++ struct i2c_adapter i2c_adap; ++ int i2c_running; ++ struct pt1_adapter *adaps[PT1_NR_ADAPS]; ++ struct pt1_table *tables; ++ struct task_struct *kthread; ++ int table_index; ++ int buf_index; ++ ++ struct mutex lock; ++ int power; ++ int reset; ++}; ++ ++struct pt1_adapter { ++ struct pt1 *pt1; ++ int index; ++ ++ u8 *buf; ++ int upacket_count; ++ int packet_count; ++ int st_count; ++ ++ struct dvb_adapter adap; ++ struct dvb_demux demux; ++ int users; ++ struct dmxdev dmxdev; ++ struct dvb_frontend *fe; ++ int (*orig_set_voltage)(struct dvb_frontend *fe, ++ fe_sec_voltage_t voltage); ++ int (*orig_sleep)(struct dvb_frontend *fe); ++ int (*orig_init)(struct dvb_frontend *fe); ++ ++ fe_sec_voltage_t voltage; ++ int sleep; ++}; ++ ++#define pt1_printk(level, pt1, format, arg...) \ ++ dev_printk(level, &(pt1)->pdev->dev, format, ##arg) ++ ++static void pt1_write_reg(struct pt1 *pt1, int reg, u32 data) ++{ ++ writel(data, pt1->regs + reg * 4); ++} ++ ++static u32 pt1_read_reg(struct pt1 *pt1, int reg) ++{ ++ return readl(pt1->regs + reg * 4); ++} ++ ++static int pt1_nr_tables = 8; ++module_param_named(nr_tables, pt1_nr_tables, int, 0); ++ ++static void pt1_increment_table_count(struct pt1 *pt1) ++{ ++ pt1_write_reg(pt1, 0, 0x00000020); ++} ++ ++static void pt1_init_table_count(struct pt1 *pt1) ++{ ++ pt1_write_reg(pt1, 0, 0x00000010); ++} ++ ++static void pt1_register_tables(struct pt1 *pt1, u32 first_pfn) ++{ ++ pt1_write_reg(pt1, 5, first_pfn); ++ pt1_write_reg(pt1, 0, 0x0c000040); ++} ++ ++static void pt1_unregister_tables(struct pt1 *pt1) ++{ ++ pt1_write_reg(pt1, 0, 0x08080000); ++} ++ ++static int pt1_sync(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < 57; i++) { ++ if (pt1_read_reg(pt1, 0) & 0x20000000) ++ return 0; ++ pt1_write_reg(pt1, 0, 0x00000008); ++ } ++ pt1_printk(KERN_ERR, pt1, "could not sync\n"); ++ return -EIO; ++} ++ ++static u64 pt1_identify(struct pt1 *pt1) ++{ ++ int i; ++ u64 id; ++ id = 0; ++ for (i = 0; i < 57; i++) { ++ id |= (u64)(pt1_read_reg(pt1, 0) >> 30 & 1) << i; ++ pt1_write_reg(pt1, 0, 0x00000008); ++ } ++ return id; ++} ++ ++static int pt1_unlock(struct pt1 *pt1) ++{ ++ int i; ++ pt1_write_reg(pt1, 0, 0x00000008); ++ for (i = 0; i < 3; i++) { ++ if (pt1_read_reg(pt1, 0) & 0x80000000) ++ return 0; ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ } ++ pt1_printk(KERN_ERR, pt1, "could not unlock\n"); ++ return -EIO; ++} ++ ++static int pt1_reset_pci(struct pt1 *pt1) ++{ ++ int i; ++ pt1_write_reg(pt1, 0, 0x01010000); ++ pt1_write_reg(pt1, 0, 0x01000000); ++ for (i = 0; i < 10; i++) { ++ if (pt1_read_reg(pt1, 0) & 0x00000001) ++ return 0; ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ } ++ pt1_printk(KERN_ERR, pt1, "could not reset PCI\n"); ++ return -EIO; ++} ++ ++static int pt1_reset_ram(struct pt1 *pt1) ++{ ++ int i; ++ pt1_write_reg(pt1, 0, 0x02020000); ++ pt1_write_reg(pt1, 0, 0x02000000); ++ for (i = 0; i < 10; i++) { ++ if (pt1_read_reg(pt1, 0) & 0x00000002) ++ return 0; ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ } ++ pt1_printk(KERN_ERR, pt1, "could not reset RAM\n"); ++ return -EIO; ++} ++ ++static int pt1_do_enable_ram(struct pt1 *pt1) ++{ ++ int i, j; ++ u32 status; ++ status = pt1_read_reg(pt1, 0) & 0x00000004; ++ pt1_write_reg(pt1, 0, 0x00000002); ++ for (i = 0; i < 10; i++) { ++ for (j = 0; j < 1024; j++) { ++ if ((pt1_read_reg(pt1, 0) & 0x00000004) != status) ++ return 0; ++ } ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ } ++ pt1_printk(KERN_ERR, pt1, "could not enable RAM\n"); ++ return -EIO; ++} ++ ++static int pt1_enable_ram(struct pt1 *pt1) ++{ ++ int i, ret; ++ int phase; ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ phase = pt1->pdev->device == 0x211a ? 128 : 166; ++ for (i = 0; i < phase; i++) { ++ ret = pt1_do_enable_ram(pt1); ++ if (ret < 0) ++ return ret; ++ } ++ return 0; ++} ++ ++static void pt1_disable_ram(struct pt1 *pt1) ++{ ++ pt1_write_reg(pt1, 0, 0x0b0b0000); ++} ++ ++static void pt1_set_stream(struct pt1 *pt1, int index, int enabled) ++{ ++ pt1_write_reg(pt1, 2, 1 << (index + 8) | enabled << index); ++} ++ ++static void pt1_init_streams(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < PT1_NR_ADAPS; i++) ++ pt1_set_stream(pt1, i, 0); ++} ++ ++static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page) ++{ ++ u32 upacket; ++ int i; ++ int index; ++ struct pt1_adapter *adap; ++ int offset; ++ u8 *buf; ++ int sc; ++ ++ if (!page->upackets[PT1_NR_UPACKETS - 1]) ++ return 0; ++ ++ for (i = 0; i < PT1_NR_UPACKETS; i++) { ++ upacket = le32_to_cpu(page->upackets[i]); ++ index = (upacket >> 29) - 1; ++ if (index < 0 || index >= PT1_NR_ADAPS) ++ continue; ++ ++ adap = pt1->adaps[index]; ++ if (upacket >> 25 & 1) ++ adap->upacket_count = 0; ++ else if (!adap->upacket_count) ++ continue; ++ ++ if (upacket >> 24 & 1) ++ printk_ratelimited(KERN_INFO "earth-pt1: device " ++ "buffer overflowing. table[%d] buf[%d]\n", ++ pt1->table_index, pt1->buf_index); ++ sc = upacket >> 26 & 0x7; ++ if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7)) ++ printk_ratelimited(KERN_INFO "earth-pt1: data loss" ++ " in streamID(adapter)[%d]\n", index); ++ adap->st_count = sc; ++ ++ buf = adap->buf; ++ offset = adap->packet_count * 188 + adap->upacket_count * 3; ++ buf[offset] = upacket >> 16; ++ buf[offset + 1] = upacket >> 8; ++ if (adap->upacket_count != 62) ++ buf[offset + 2] = upacket; ++ ++ if (++adap->upacket_count >= 63) { ++ adap->upacket_count = 0; ++ if (++adap->packet_count >= 21) { ++ dvb_dmx_swfilter_packets(&adap->demux, buf, 21); ++ adap->packet_count = 0; ++ } ++ } ++ } ++ ++ page->upackets[PT1_NR_UPACKETS - 1] = 0; ++ return 1; ++} ++ ++static int pt1_thread(void *data) ++{ ++ struct pt1 *pt1; ++ struct pt1_buffer_page *page; ++ ++ pt1 = data; ++ set_freezable(); ++ ++ while (!kthread_should_stop()) { ++ try_to_freeze(); ++ ++ page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page; ++ if (!pt1_filter(pt1, page)) { ++ schedule_timeout_interruptible((HZ + 999) / 1000); ++ continue; ++ } ++ ++ if (++pt1->buf_index >= PT1_NR_BUFS) { ++ pt1_increment_table_count(pt1); ++ pt1->buf_index = 0; ++ if (++pt1->table_index >= pt1_nr_tables) ++ pt1->table_index = 0; ++ } ++ } ++ ++ return 0; ++} ++ ++static void pt1_free_page(struct pt1 *pt1, void *page, dma_addr_t addr) ++{ ++ dma_free_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, page, addr); ++} ++ ++static void *pt1_alloc_page(struct pt1 *pt1, dma_addr_t *addrp, u32 *pfnp) ++{ ++ void *page; ++ dma_addr_t addr; ++ ++ page = dma_alloc_coherent(&pt1->pdev->dev, PT1_PAGE_SIZE, &addr, ++ GFP_KERNEL); ++ if (page == NULL) ++ return NULL; ++ ++ BUG_ON(addr & (PT1_PAGE_SIZE - 1)); ++ BUG_ON(addr >> PT1_PAGE_SHIFT >> 31 >> 1); ++ ++ *addrp = addr; ++ *pfnp = addr >> PT1_PAGE_SHIFT; ++ return page; ++} ++ ++static void pt1_cleanup_buffer(struct pt1 *pt1, struct pt1_buffer *buf) ++{ ++ pt1_free_page(pt1, buf->page, buf->addr); ++} ++ ++static int ++pt1_init_buffer(struct pt1 *pt1, struct pt1_buffer *buf, u32 *pfnp) ++{ ++ struct pt1_buffer_page *page; ++ dma_addr_t addr; ++ ++ page = pt1_alloc_page(pt1, &addr, pfnp); ++ if (page == NULL) ++ return -ENOMEM; ++ ++ page->upackets[PT1_NR_UPACKETS - 1] = 0; ++ ++ buf->page = page; ++ buf->addr = addr; ++ return 0; ++} ++ ++static void pt1_cleanup_table(struct pt1 *pt1, struct pt1_table *table) ++{ ++ int i; ++ ++ for (i = 0; i < PT1_NR_BUFS; i++) ++ pt1_cleanup_buffer(pt1, &table->bufs[i]); ++ ++ pt1_free_page(pt1, table->page, table->addr); ++} ++ ++static int ++pt1_init_table(struct pt1 *pt1, struct pt1_table *table, u32 *pfnp) ++{ ++ struct pt1_table_page *page; ++ dma_addr_t addr; ++ int i, ret; ++ u32 buf_pfn; ++ ++ page = pt1_alloc_page(pt1, &addr, pfnp); ++ if (page == NULL) ++ return -ENOMEM; ++ ++ for (i = 0; i < PT1_NR_BUFS; i++) { ++ ret = pt1_init_buffer(pt1, &table->bufs[i], &buf_pfn); ++ if (ret < 0) ++ goto err; ++ ++ page->buf_pfns[i] = cpu_to_le32(buf_pfn); ++ } ++ ++ pt1_increment_table_count(pt1); ++ table->page = page; ++ table->addr = addr; ++ return 0; ++ ++err: ++ while (i--) ++ pt1_cleanup_buffer(pt1, &table->bufs[i]); ++ ++ pt1_free_page(pt1, page, addr); ++ return ret; ++} ++ ++static void pt1_cleanup_tables(struct pt1 *pt1) ++{ ++ struct pt1_table *tables; ++ int i; ++ ++ tables = pt1->tables; ++ pt1_unregister_tables(pt1); ++ ++ for (i = 0; i < pt1_nr_tables; i++) ++ pt1_cleanup_table(pt1, &tables[i]); ++ ++ vfree(tables); ++} ++ ++static int pt1_init_tables(struct pt1 *pt1) ++{ ++ struct pt1_table *tables; ++ int i, ret; ++ u32 first_pfn, pfn; ++ ++ tables = vmalloc(sizeof(struct pt1_table) * pt1_nr_tables); ++ if (tables == NULL) ++ return -ENOMEM; ++ ++ pt1_init_table_count(pt1); ++ ++ i = 0; ++ if (pt1_nr_tables) { ++ ret = pt1_init_table(pt1, &tables[0], &first_pfn); ++ if (ret) ++ goto err; ++ i++; ++ } ++ ++ while (i < pt1_nr_tables) { ++ ret = pt1_init_table(pt1, &tables[i], &pfn); ++ if (ret) ++ goto err; ++ tables[i - 1].page->next_pfn = cpu_to_le32(pfn); ++ i++; ++ } ++ ++ tables[pt1_nr_tables - 1].page->next_pfn = cpu_to_le32(first_pfn); ++ ++ pt1_register_tables(pt1, first_pfn); ++ pt1->tables = tables; ++ return 0; ++ ++err: ++ while (i--) ++ pt1_cleanup_table(pt1, &tables[i]); ++ ++ vfree(tables); ++ return ret; ++} ++ ++static int pt1_start_polling(struct pt1 *pt1) ++{ ++ int ret = 0; ++ ++ mutex_lock(&pt1->lock); ++ if (!pt1->kthread) { ++ pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1"); ++ if (IS_ERR(pt1->kthread)) { ++ ret = PTR_ERR(pt1->kthread); ++ pt1->kthread = NULL; ++ } ++ } ++ mutex_unlock(&pt1->lock); ++ return ret; ++} ++ ++static int pt1_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct pt1_adapter *adap; ++ adap = container_of(feed->demux, struct pt1_adapter, demux); ++ if (!adap->users++) { ++ int ret; ++ ++ ret = pt1_start_polling(adap->pt1); ++ if (ret) ++ return ret; ++ pt1_set_stream(adap->pt1, adap->index, 1); ++ } ++ return 0; ++} ++ ++static void pt1_stop_polling(struct pt1 *pt1) ++{ ++ int i, count; ++ ++ mutex_lock(&pt1->lock); ++ for (i = 0, count = 0; i < PT1_NR_ADAPS; i++) ++ count += pt1->adaps[i]->users; ++ ++ if (count == 0 && pt1->kthread) { ++ kthread_stop(pt1->kthread); ++ pt1->kthread = NULL; ++ } ++ mutex_unlock(&pt1->lock); ++} ++ ++static int pt1_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct pt1_adapter *adap; ++ adap = container_of(feed->demux, struct pt1_adapter, demux); ++ if (!--adap->users) { ++ pt1_set_stream(adap->pt1, adap->index, 0); ++ pt1_stop_polling(adap->pt1); ++ } ++ return 0; ++} ++ ++static void ++pt1_update_power(struct pt1 *pt1) ++{ ++ int bits; ++ int i; ++ struct pt1_adapter *adap; ++ static const int sleep_bits[] = { ++ 1 << 4, ++ 1 << 6 | 1 << 7, ++ 1 << 5, ++ 1 << 6 | 1 << 8, ++ }; ++ ++ bits = pt1->power | !pt1->reset << 3; ++ mutex_lock(&pt1->lock); ++ for (i = 0; i < PT1_NR_ADAPS; i++) { ++ adap = pt1->adaps[i]; ++ switch (adap->voltage) { ++ case SEC_VOLTAGE_13: /* actually 11V */ ++ bits |= 1 << 1; ++ break; ++ case SEC_VOLTAGE_18: /* actually 15V */ ++ bits |= 1 << 1 | 1 << 2; ++ break; ++ default: ++ break; ++ } ++ ++ /* XXX: The bits should be changed depending on adap->sleep. */ ++ bits |= sleep_bits[i]; ++ } ++ pt1_write_reg(pt1, 1, bits); ++ mutex_unlock(&pt1->lock); ++} ++ ++static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct pt1_adapter *adap; ++ ++ adap = container_of(fe->dvb, struct pt1_adapter, adap); ++ adap->voltage = voltage; ++ pt1_update_power(adap->pt1); ++ ++ if (adap->orig_set_voltage) ++ return adap->orig_set_voltage(fe, voltage); ++ else ++ return 0; ++} ++ ++static int pt1_sleep(struct dvb_frontend *fe) ++{ ++ struct pt1_adapter *adap; ++ ++ adap = container_of(fe->dvb, struct pt1_adapter, adap); ++ adap->sleep = 1; ++ pt1_update_power(adap->pt1); ++ ++ if (adap->orig_sleep) ++ return adap->orig_sleep(fe); ++ else ++ return 0; ++} ++ ++static int pt1_wakeup(struct dvb_frontend *fe) ++{ ++ struct pt1_adapter *adap; ++ ++ adap = container_of(fe->dvb, struct pt1_adapter, adap); ++ adap->sleep = 0; ++ pt1_update_power(adap->pt1); ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ ++ if (adap->orig_init) ++ return adap->orig_init(fe); ++ else ++ return 0; ++} ++ ++static void pt1_free_adapter(struct pt1_adapter *adap) ++{ ++ adap->demux.dmx.close(&adap->demux.dmx); ++ dvb_dmxdev_release(&adap->dmxdev); ++ dvb_dmx_release(&adap->demux); ++ dvb_unregister_adapter(&adap->adap); ++ free_page((unsigned long)adap->buf); ++ kfree(adap); ++} ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++static struct pt1_adapter * ++pt1_alloc_adapter(struct pt1 *pt1) ++{ ++ struct pt1_adapter *adap; ++ void *buf; ++ struct dvb_adapter *dvb_adap; ++ struct dvb_demux *demux; ++ struct dmxdev *dmxdev; ++ int ret; ++ ++ adap = kzalloc(sizeof(struct pt1_adapter), GFP_KERNEL); ++ if (!adap) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ adap->pt1 = pt1; ++ ++ adap->voltage = SEC_VOLTAGE_OFF; ++ adap->sleep = 1; ++ ++ buf = (u8 *)__get_free_page(GFP_KERNEL); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto err_kfree; ++ } ++ ++ adap->buf = buf; ++ adap->upacket_count = 0; ++ adap->packet_count = 0; ++ adap->st_count = -1; ++ ++ dvb_adap = &adap->adap; ++ dvb_adap->priv = adap; ++ ret = dvb_register_adapter(dvb_adap, DRIVER_NAME, THIS_MODULE, ++ &pt1->pdev->dev, adapter_nr); ++ if (ret < 0) ++ goto err_free_page; ++ ++ demux = &adap->demux; ++ demux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; ++ demux->priv = adap; ++ demux->feednum = 256; ++ demux->filternum = 256; ++ demux->start_feed = pt1_start_feed; ++ demux->stop_feed = pt1_stop_feed; ++ demux->write_to_decoder = NULL; ++ ret = dvb_dmx_init(demux); ++ if (ret < 0) ++ goto err_unregister_adapter; ++ ++ dmxdev = &adap->dmxdev; ++ dmxdev->filternum = 256; ++ dmxdev->demux = &demux->dmx; ++ dmxdev->capabilities = 0; ++ ret = dvb_dmxdev_init(dmxdev, dvb_adap); ++ if (ret < 0) ++ goto err_dmx_release; ++ ++ return adap; ++ ++err_dmx_release: ++ dvb_dmx_release(demux); ++err_unregister_adapter: ++ dvb_unregister_adapter(dvb_adap); ++err_free_page: ++ free_page((unsigned long)buf); ++err_kfree: ++ kfree(adap); ++err: ++ return ERR_PTR(ret); ++} ++ ++static void pt1_cleanup_adapters(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < PT1_NR_ADAPS; i++) ++ pt1_free_adapter(pt1->adaps[i]); ++} ++ ++static int pt1_init_adapters(struct pt1 *pt1) ++{ ++ int i; ++ struct pt1_adapter *adap; ++ int ret; ++ ++ for (i = 0; i < PT1_NR_ADAPS; i++) { ++ adap = pt1_alloc_adapter(pt1); ++ if (IS_ERR(adap)) { ++ ret = PTR_ERR(adap); ++ goto err; ++ } ++ ++ adap->index = i; ++ pt1->adaps[i] = adap; ++ } ++ return 0; ++ ++err: ++ while (i--) ++ pt1_free_adapter(pt1->adaps[i]); ++ ++ return ret; ++} ++ ++static void pt1_cleanup_frontend(struct pt1_adapter *adap) ++{ ++ dvb_unregister_frontend(adap->fe); ++} ++ ++static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) ++{ ++ int ret; ++ ++ adap->orig_set_voltage = fe->ops.set_voltage; ++ adap->orig_sleep = fe->ops.sleep; ++ adap->orig_init = fe->ops.init; ++ fe->ops.set_voltage = pt1_set_voltage; ++ fe->ops.sleep = pt1_sleep; ++ fe->ops.init = pt1_wakeup; ++ ++ ret = dvb_register_frontend(&adap->adap, fe); ++ if (ret < 0) ++ return ret; ++ ++ adap->fe = fe; ++ return 0; ++} ++ ++static void pt1_cleanup_frontends(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < PT1_NR_ADAPS; i++) ++ pt1_cleanup_frontend(pt1->adaps[i]); ++} ++ ++struct pt1_config { ++ struct va1j5jf8007s_config va1j5jf8007s_config; ++ struct va1j5jf8007t_config va1j5jf8007t_config; ++}; ++ ++static const struct pt1_config pt1_configs[2] = { ++ { ++ { ++ .demod_address = 0x1b, ++ .frequency = VA1J5JF8007S_20MHZ, ++ }, ++ { ++ .demod_address = 0x1a, ++ .frequency = VA1J5JF8007T_20MHZ, ++ }, ++ }, { ++ { ++ .demod_address = 0x19, ++ .frequency = VA1J5JF8007S_20MHZ, ++ }, ++ { ++ .demod_address = 0x18, ++ .frequency = VA1J5JF8007T_20MHZ, ++ }, ++ }, ++}; ++ ++static const struct pt1_config pt2_configs[2] = { ++ { ++ { ++ .demod_address = 0x1b, ++ .frequency = VA1J5JF8007S_25MHZ, ++ }, ++ { ++ .demod_address = 0x1a, ++ .frequency = VA1J5JF8007T_25MHZ, ++ }, ++ }, { ++ { ++ .demod_address = 0x19, ++ .frequency = VA1J5JF8007S_25MHZ, ++ }, ++ { ++ .demod_address = 0x18, ++ .frequency = VA1J5JF8007T_25MHZ, ++ }, ++ }, ++}; ++ ++static int pt1_init_frontends(struct pt1 *pt1) ++{ ++ int i, j; ++ struct i2c_adapter *i2c_adap; ++ const struct pt1_config *configs, *config; ++ struct dvb_frontend *fe[4]; ++ int ret; ++ ++ i = 0; ++ j = 0; ++ ++ i2c_adap = &pt1->i2c_adap; ++ configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; ++ do { ++ config = &configs[i / 2]; ++ ++ fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, ++ i2c_adap); ++ if (!fe[i]) { ++ ret = -ENODEV; /* This does not sound nice... */ ++ goto err; ++ } ++ i++; ++ ++ fe[i] = va1j5jf8007t_attach(&config->va1j5jf8007t_config, ++ i2c_adap); ++ if (!fe[i]) { ++ ret = -ENODEV; ++ goto err; ++ } ++ i++; ++ ++ ret = va1j5jf8007s_prepare(fe[i - 2]); ++ if (ret < 0) ++ goto err; ++ ++ ret = va1j5jf8007t_prepare(fe[i - 1]); ++ if (ret < 0) ++ goto err; ++ ++ } while (i < 4); ++ ++ do { ++ ret = pt1_init_frontend(pt1->adaps[j], fe[j]); ++ if (ret < 0) ++ goto err; ++ } while (++j < 4); ++ ++ return 0; ++ ++err: ++ while (i-- > j) ++ fe[i]->ops.release(fe[i]); ++ ++ while (j--) ++ dvb_unregister_frontend(fe[j]); ++ ++ return ret; ++} ++ ++static void pt1_i2c_emit(struct pt1 *pt1, int addr, int busy, int read_enable, ++ int clock, int data, int next_addr) ++{ ++ pt1_write_reg(pt1, 4, addr << 18 | busy << 13 | read_enable << 12 | ++ !clock << 11 | !data << 10 | next_addr); ++} ++ ++static void pt1_i2c_write_bit(struct pt1 *pt1, int addr, int *addrp, int data) ++{ ++ pt1_i2c_emit(pt1, addr, 1, 0, 0, data, addr + 1); ++ pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, data, addr + 2); ++ pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, data, addr + 3); ++ *addrp = addr + 3; ++} ++ ++static void pt1_i2c_read_bit(struct pt1 *pt1, int addr, int *addrp) ++{ ++ pt1_i2c_emit(pt1, addr, 1, 0, 0, 1, addr + 1); ++ pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 1, addr + 2); ++ pt1_i2c_emit(pt1, addr + 2, 1, 1, 1, 1, addr + 3); ++ pt1_i2c_emit(pt1, addr + 3, 1, 0, 0, 1, addr + 4); ++ *addrp = addr + 4; ++} ++ ++static void pt1_i2c_write_byte(struct pt1 *pt1, int addr, int *addrp, int data) ++{ ++ int i; ++ for (i = 0; i < 8; i++) ++ pt1_i2c_write_bit(pt1, addr, &addr, data >> (7 - i) & 1); ++ pt1_i2c_write_bit(pt1, addr, &addr, 1); ++ *addrp = addr; ++} ++ ++static void pt1_i2c_read_byte(struct pt1 *pt1, int addr, int *addrp, int last) ++{ ++ int i; ++ for (i = 0; i < 8; i++) ++ pt1_i2c_read_bit(pt1, addr, &addr); ++ pt1_i2c_write_bit(pt1, addr, &addr, last); ++ *addrp = addr; ++} ++ ++static void pt1_i2c_prepare(struct pt1 *pt1, int addr, int *addrp) ++{ ++ pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); ++ pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); ++ pt1_i2c_emit(pt1, addr + 2, 1, 0, 0, 0, addr + 3); ++ *addrp = addr + 3; ++} ++ ++static void ++pt1_i2c_write_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) ++{ ++ int i; ++ pt1_i2c_prepare(pt1, addr, &addr); ++ pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1); ++ for (i = 0; i < msg->len; i++) ++ pt1_i2c_write_byte(pt1, addr, &addr, msg->buf[i]); ++ *addrp = addr; ++} ++ ++static void ++pt1_i2c_read_msg(struct pt1 *pt1, int addr, int *addrp, struct i2c_msg *msg) ++{ ++ int i; ++ pt1_i2c_prepare(pt1, addr, &addr); ++ pt1_i2c_write_byte(pt1, addr, &addr, msg->addr << 1 | 1); ++ for (i = 0; i < msg->len; i++) ++ pt1_i2c_read_byte(pt1, addr, &addr, i == msg->len - 1); ++ *addrp = addr; ++} ++ ++static int pt1_i2c_end(struct pt1 *pt1, int addr) ++{ ++ pt1_i2c_emit(pt1, addr, 1, 0, 0, 0, addr + 1); ++ pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); ++ pt1_i2c_emit(pt1, addr + 2, 1, 0, 1, 1, 0); ++ ++ pt1_write_reg(pt1, 0, 0x00000004); ++ do { ++ if (signal_pending(current)) ++ return -EINTR; ++ schedule_timeout_interruptible((HZ + 999) / 1000); ++ } while (pt1_read_reg(pt1, 0) & 0x00000080); ++ return 0; ++} ++ ++static void pt1_i2c_begin(struct pt1 *pt1, int *addrp) ++{ ++ int addr; ++ addr = 0; ++ ++ pt1_i2c_emit(pt1, addr, 0, 0, 1, 1, addr /* itself */); ++ addr = addr + 1; ++ ++ if (!pt1->i2c_running) { ++ pt1_i2c_emit(pt1, addr, 1, 0, 1, 1, addr + 1); ++ pt1_i2c_emit(pt1, addr + 1, 1, 0, 1, 0, addr + 2); ++ addr = addr + 2; ++ pt1->i2c_running = 1; ++ } ++ *addrp = addr; ++} ++ ++static int pt1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ++{ ++ struct pt1 *pt1; ++ int i; ++ struct i2c_msg *msg, *next_msg; ++ int addr, ret; ++ u16 len; ++ u32 word; ++ ++ pt1 = i2c_get_adapdata(adap); ++ ++ for (i = 0; i < num; i++) { ++ msg = &msgs[i]; ++ if (msg->flags & I2C_M_RD) ++ return -ENOTSUPP; ++ ++ if (i + 1 < num) ++ next_msg = &msgs[i + 1]; ++ else ++ next_msg = NULL; ++ ++ if (next_msg && next_msg->flags & I2C_M_RD) { ++ i++; ++ ++ len = next_msg->len; ++ if (len > 4) ++ return -ENOTSUPP; ++ ++ pt1_i2c_begin(pt1, &addr); ++ pt1_i2c_write_msg(pt1, addr, &addr, msg); ++ pt1_i2c_read_msg(pt1, addr, &addr, next_msg); ++ ret = pt1_i2c_end(pt1, addr); ++ if (ret < 0) ++ return ret; ++ ++ word = pt1_read_reg(pt1, 2); ++ while (len--) { ++ next_msg->buf[len] = word; ++ word >>= 8; ++ } ++ } else { ++ pt1_i2c_begin(pt1, &addr); ++ pt1_i2c_write_msg(pt1, addr, &addr, msg); ++ ret = pt1_i2c_end(pt1, addr); ++ if (ret < 0) ++ return ret; ++ } ++ } ++ ++ return num; ++} ++ ++static u32 pt1_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static const struct i2c_algorithm pt1_i2c_algo = { ++ .master_xfer = pt1_i2c_xfer, ++ .functionality = pt1_i2c_func, ++}; ++ ++static void pt1_i2c_wait(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < 128; i++) ++ pt1_i2c_emit(pt1, 0, 0, 0, 1, 1, 0); ++} ++ ++static void pt1_i2c_init(struct pt1 *pt1) ++{ ++ int i; ++ for (i = 0; i < 1024; i++) ++ pt1_i2c_emit(pt1, i, 0, 0, 1, 1, 0); ++} ++ ++static void __devexit pt1_remove(struct pci_dev *pdev) ++{ ++ struct pt1 *pt1; ++ void __iomem *regs; ++ ++ pt1 = pci_get_drvdata(pdev); ++ regs = pt1->regs; ++ ++ if (pt1->kthread) ++ kthread_stop(pt1->kthread); ++ pt1_cleanup_tables(pt1); ++ pt1_cleanup_frontends(pt1); ++ pt1_disable_ram(pt1); ++ pt1->power = 0; ++ pt1->reset = 1; ++ pt1_update_power(pt1); ++ pt1_cleanup_adapters(pt1); ++ i2c_del_adapter(&pt1->i2c_adap); ++ pci_set_drvdata(pdev, NULL); ++ kfree(pt1); ++ pci_iounmap(pdev, regs); ++ pci_release_regions(pdev); ++ pci_disable_device(pdev); ++} ++ ++static int __devinit ++pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ++{ ++ int ret; ++ void __iomem *regs; ++ struct pt1 *pt1; ++ struct i2c_adapter *i2c_adap; ++ ++ ret = pci_enable_device(pdev); ++ if (ret < 0) ++ goto err; ++ ++ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ pci_set_master(pdev); ++ ++ ret = pci_request_regions(pdev, DRIVER_NAME); ++ if (ret < 0) ++ goto err_pci_disable_device; ++ ++ regs = pci_iomap(pdev, 0, 0); ++ if (!regs) { ++ ret = -EIO; ++ goto err_pci_release_regions; ++ } ++ ++ pt1 = kzalloc(sizeof(struct pt1), GFP_KERNEL); ++ if (!pt1) { ++ ret = -ENOMEM; ++ goto err_pci_iounmap; ++ } ++ ++ mutex_init(&pt1->lock); ++ pt1->pdev = pdev; ++ pt1->regs = regs; ++ pci_set_drvdata(pdev, pt1); ++ ++ ret = pt1_init_adapters(pt1); ++ if (ret < 0) ++ goto err_kfree; ++ ++ mutex_init(&pt1->lock); ++ ++ pt1->power = 0; ++ pt1->reset = 1; ++ pt1_update_power(pt1); ++ ++ i2c_adap = &pt1->i2c_adap; ++ i2c_adap->algo = &pt1_i2c_algo; ++ i2c_adap->algo_data = NULL; ++ i2c_adap->dev.parent = &pdev->dev; ++ strcpy(i2c_adap->name, DRIVER_NAME); ++ i2c_set_adapdata(i2c_adap, pt1); ++ ret = i2c_add_adapter(i2c_adap); ++ if (ret < 0) ++ goto err_pt1_cleanup_adapters; ++ ++ pt1_i2c_init(pt1); ++ pt1_i2c_wait(pt1); ++ ++ ret = pt1_sync(pt1); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ pt1_identify(pt1); ++ ++ ret = pt1_unlock(pt1); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ ret = pt1_reset_pci(pt1); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ ret = pt1_reset_ram(pt1); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ ret = pt1_enable_ram(pt1); ++ if (ret < 0) ++ goto err_i2c_del_adapter; ++ ++ pt1_init_streams(pt1); ++ ++ pt1->power = 1; ++ pt1_update_power(pt1); ++ schedule_timeout_uninterruptible((HZ + 49) / 50); ++ ++ pt1->reset = 0; ++ pt1_update_power(pt1); ++ schedule_timeout_uninterruptible((HZ + 999) / 1000); ++ ++ ret = pt1_init_frontends(pt1); ++ if (ret < 0) ++ goto err_pt1_disable_ram; ++ ++ ret = pt1_init_tables(pt1); ++ if (ret < 0) ++ goto err_pt1_cleanup_frontends; ++ ++ return 0; ++ ++err_pt1_cleanup_frontends: ++ pt1_cleanup_frontends(pt1); ++err_pt1_disable_ram: ++ pt1_disable_ram(pt1); ++ pt1->power = 0; ++ pt1->reset = 1; ++ pt1_update_power(pt1); ++err_i2c_del_adapter: ++ i2c_del_adapter(i2c_adap); ++err_pt1_cleanup_adapters: ++ pt1_cleanup_adapters(pt1); ++err_kfree: ++ pci_set_drvdata(pdev, NULL); ++ kfree(pt1); ++err_pci_iounmap: ++ pci_iounmap(pdev, regs); ++err_pci_release_regions: ++ pci_release_regions(pdev); ++err_pci_disable_device: ++ pci_disable_device(pdev); ++err: ++ return ret; ++ ++} ++ ++static struct pci_device_id pt1_id_table[] = { ++ { PCI_DEVICE(0x10ee, 0x211a) }, ++ { PCI_DEVICE(0x10ee, 0x222a) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(pci, pt1_id_table); ++ ++static struct pci_driver pt1_driver = { ++ .name = DRIVER_NAME, ++ .probe = pt1_probe, ++ .remove = __devexit_p(pt1_remove), ++ .id_table = pt1_id_table, ++}; ++ ++ ++static int __init pt1_init(void) ++{ ++ return pci_register_driver(&pt1_driver); ++} ++ ++ ++static void __exit pt1_cleanup(void) ++{ ++ pci_unregister_driver(&pt1_driver); ++} ++ ++module_init(pt1_init); ++module_exit(pt1_cleanup); ++ ++MODULE_AUTHOR("Takahito HIRANO "); ++MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/pt1/va1j5jf8007s.c b/drivers/media/pci/pt1/va1j5jf8007s.c +new file mode 100644 +index 0000000..1b637b7 +--- /dev/null ++++ b/drivers/media/pci/pt1/va1j5jf8007s.c +@@ -0,0 +1,736 @@ ++/* ++ * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 ++ * ++ * Copyright (C) 2009 HIRANO Takahito ++ * ++ * based on pt1dvr - http://pt1dvr.sourceforge.jp/ ++ * by Tomoaki Ishikawa ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "va1j5jf8007s.h" ++ ++enum va1j5jf8007s_tune_state { ++ VA1J5JF8007S_IDLE, ++ VA1J5JF8007S_SET_FREQUENCY_1, ++ VA1J5JF8007S_SET_FREQUENCY_2, ++ VA1J5JF8007S_SET_FREQUENCY_3, ++ VA1J5JF8007S_CHECK_FREQUENCY, ++ VA1J5JF8007S_SET_MODULATION, ++ VA1J5JF8007S_CHECK_MODULATION, ++ VA1J5JF8007S_SET_TS_ID, ++ VA1J5JF8007S_CHECK_TS_ID, ++ VA1J5JF8007S_TRACK, ++}; ++ ++struct va1j5jf8007s_state { ++ const struct va1j5jf8007s_config *config; ++ struct i2c_adapter *adap; ++ struct dvb_frontend fe; ++ enum va1j5jf8007s_tune_state tune_state; ++}; ++ ++static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct va1j5jf8007s_state *state; ++ u8 addr; ++ int i; ++ u8 write_buf[1], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ s32 word, x1, x2, x3, x4, x5, y; ++ ++ state = fe->demodulator_priv; ++ addr = state->config->demod_address; ++ ++ word = 0; ++ for (i = 0; i < 2; i++) { ++ write_buf[0] = 0xbc + i; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ word <<= 8; ++ word |= read_buf[0]; ++ } ++ ++ word -= 3000; ++ if (word < 0) ++ word = 0; ++ ++ x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); ++ x2 = (s64)x1 * x1 >> 31; ++ x3 = (s64)x2 * x1 >> 31; ++ x4 = (s64)x2 * x2 >> 31; ++ x5 = (s64)x4 * x1 >> 31; ++ ++ y = (58857ll << 23) / 1000; ++ y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; ++ y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; ++ y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; ++ y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; ++ y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; ++ ++ *snr = y < 0 ? 0 : y >> 15; ++ return 0; ++} ++ ++static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_HW; ++} ++ ++static int ++va1j5jf8007s_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct va1j5jf8007s_state *state; ++ ++ state = fe->demodulator_priv; ++ ++ switch (state->tune_state) { ++ case VA1J5JF8007S_IDLE: ++ case VA1J5JF8007S_SET_FREQUENCY_1: ++ case VA1J5JF8007S_SET_FREQUENCY_2: ++ case VA1J5JF8007S_SET_FREQUENCY_3: ++ case VA1J5JF8007S_CHECK_FREQUENCY: ++ *status = 0; ++ return 0; ++ ++ ++ case VA1J5JF8007S_SET_MODULATION: ++ case VA1J5JF8007S_CHECK_MODULATION: ++ *status |= FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007S_SET_TS_ID: ++ case VA1J5JF8007S_CHECK_TS_ID: ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ return 0; ++ ++ case VA1J5JF8007S_TRACK: ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ return 0; ++ } ++ ++ BUG(); ++} ++ ++struct va1j5jf8007s_cb_map { ++ u32 frequency; ++ u8 cb; ++}; ++ ++static const struct va1j5jf8007s_cb_map va1j5jf8007s_cb_maps[] = { ++ { 986000, 0xb2 }, ++ { 1072000, 0xd2 }, ++ { 1154000, 0xe2 }, ++ { 1291000, 0x20 }, ++ { 1447000, 0x40 }, ++ { 1615000, 0x60 }, ++ { 1791000, 0x80 }, ++ { 1972000, 0xa0 }, ++}; ++ ++static u8 va1j5jf8007s_lookup_cb(u32 frequency) ++{ ++ int i; ++ const struct va1j5jf8007s_cb_map *map; ++ ++ for (i = 0; i < ARRAY_SIZE(va1j5jf8007s_cb_maps); i++) { ++ map = &va1j5jf8007s_cb_maps[i]; ++ if (frequency < map->frequency) ++ return map->cb; ++ } ++ return 0xc0; ++} ++ ++static int va1j5jf8007s_set_frequency_1(struct va1j5jf8007s_state *state) ++{ ++ u32 frequency; ++ u16 word; ++ u8 buf[6]; ++ struct i2c_msg msg; ++ ++ frequency = state->fe.dtv_property_cache.frequency; ++ ++ word = (frequency + 500) / 1000; ++ if (frequency < 1072000) ++ word = (word << 1 & ~0x1f) | (word & 0x0f); ++ ++ buf[0] = 0xfe; ++ buf[1] = 0xc0; ++ buf[2] = 0x40 | word >> 8; ++ buf[3] = word; ++ buf[4] = 0xe0; ++ buf[5] = va1j5jf8007s_lookup_cb(frequency); ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007s_set_frequency_2(struct va1j5jf8007s_state *state) ++{ ++ u8 buf[3]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0xfe; ++ buf[1] = 0xc0; ++ buf[2] = 0xe4; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007s_set_frequency_3(struct va1j5jf8007s_state *state) ++{ ++ u32 frequency; ++ u8 buf[4]; ++ struct i2c_msg msg; ++ ++ frequency = state->fe.dtv_property_cache.frequency; ++ ++ buf[0] = 0xfe; ++ buf[1] = 0xc0; ++ buf[2] = 0xf4; ++ buf[3] = va1j5jf8007s_lookup_cb(frequency) | 0x4; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int ++va1j5jf8007s_check_frequency(struct va1j5jf8007s_state *state, int *lock) ++{ ++ u8 addr; ++ u8 write_buf[2], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0xfe; ++ write_buf[1] = 0xc1; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ *lock = read_buf[0] & 0x40; ++ return 0; ++} ++ ++static int va1j5jf8007s_set_modulation(struct va1j5jf8007s_state *state) ++{ ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0x03; ++ buf[1] = 0x01; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int ++va1j5jf8007s_check_modulation(struct va1j5jf8007s_state *state, int *lock) ++{ ++ u8 addr; ++ u8 write_buf[1], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0xc3; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ *lock = !(read_buf[0] & 0x10); ++ return 0; ++} ++ ++static int ++va1j5jf8007s_set_ts_id(struct va1j5jf8007s_state *state) ++{ ++ u32 ts_id; ++ u8 buf[3]; ++ struct i2c_msg msg; ++ ++ ts_id = state->fe.dtv_property_cache.stream_id; ++ if (!ts_id || ts_id == NO_STREAM_ID_FILTER) ++ return 0; ++ ++ buf[0] = 0x8f; ++ buf[1] = ts_id >> 8; ++ buf[2] = ts_id; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int ++va1j5jf8007s_check_ts_id(struct va1j5jf8007s_state *state, int *lock) ++{ ++ u8 addr; ++ u8 write_buf[1], read_buf[2]; ++ struct i2c_msg msgs[2]; ++ u32 ts_id; ++ ++ ts_id = state->fe.dtv_property_cache.stream_id; ++ if (!ts_id || ts_id == NO_STREAM_ID_FILTER) { ++ *lock = 1; ++ return 0; ++ } ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0xe6; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ *lock = (read_buf[0] << 8 | read_buf[1]) == ts_id; ++ return 0; ++} ++ ++static int ++va1j5jf8007s_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, unsigned int *delay, ++ fe_status_t *status) ++{ ++ struct va1j5jf8007s_state *state; ++ int ret; ++ int lock = 0; ++ ++ state = fe->demodulator_priv; ++ ++ if (re_tune) ++ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_1; ++ ++ switch (state->tune_state) { ++ case VA1J5JF8007S_IDLE: ++ *delay = 3 * HZ; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007S_SET_FREQUENCY_1: ++ ret = va1j5jf8007s_set_frequency_1(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_2; ++ *delay = 0; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007S_SET_FREQUENCY_2: ++ ret = va1j5jf8007s_set_frequency_2(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007S_SET_FREQUENCY_3; ++ *delay = (HZ + 99) / 100; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007S_SET_FREQUENCY_3: ++ ret = va1j5jf8007s_set_frequency_3(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007S_CHECK_FREQUENCY; ++ *delay = 0; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007S_CHECK_FREQUENCY: ++ ret = va1j5jf8007s_check_frequency(state, &lock); ++ if (ret < 0) ++ return ret; ++ ++ if (!lock) { ++ *delay = (HZ + 999) / 1000; ++ *status = 0; ++ return 0; ++ } ++ ++ state->tune_state = VA1J5JF8007S_SET_MODULATION; ++ *delay = 0; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007S_SET_MODULATION: ++ ret = va1j5jf8007s_set_modulation(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007S_CHECK_MODULATION; ++ *delay = 0; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007S_CHECK_MODULATION: ++ ret = va1j5jf8007s_check_modulation(state, &lock); ++ if (ret < 0) ++ return ret; ++ ++ if (!lock) { ++ *delay = (HZ + 49) / 50; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ } ++ ++ state->tune_state = VA1J5JF8007S_SET_TS_ID; ++ *delay = 0; ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ return 0; ++ ++ case VA1J5JF8007S_SET_TS_ID: ++ ret = va1j5jf8007s_set_ts_id(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007S_CHECK_TS_ID; ++ return 0; ++ ++ case VA1J5JF8007S_CHECK_TS_ID: ++ ret = va1j5jf8007s_check_ts_id(state, &lock); ++ if (ret < 0) ++ return ret; ++ ++ if (!lock) { ++ *delay = (HZ + 99) / 100; ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; ++ return 0; ++ } ++ ++ state->tune_state = VA1J5JF8007S_TRACK; ++ /* fall through */ ++ ++ case VA1J5JF8007S_TRACK: ++ *delay = 3 * HZ; ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ return 0; ++ } ++ ++ BUG(); ++} ++ ++static int va1j5jf8007s_init_frequency(struct va1j5jf8007s_state *state) ++{ ++ u8 buf[4]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0xfe; ++ buf[1] = 0xc0; ++ buf[2] = 0xf0; ++ buf[3] = 0x04; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007s_set_sleep(struct va1j5jf8007s_state *state, int sleep) ++{ ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0x17; ++ buf[1] = sleep ? 0x01 : 0x00; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007s_sleep(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007s_state *state; ++ int ret; ++ ++ state = fe->demodulator_priv; ++ ++ ret = va1j5jf8007s_init_frequency(state); ++ if (ret < 0) ++ return ret; ++ ++ return va1j5jf8007s_set_sleep(state, 1); ++} ++ ++static int va1j5jf8007s_init(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007s_state *state; ++ ++ state = fe->demodulator_priv; ++ state->tune_state = VA1J5JF8007S_IDLE; ++ ++ return va1j5jf8007s_set_sleep(state, 0); ++} ++ ++static void va1j5jf8007s_release(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007s_state *state; ++ state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops va1j5jf8007s_ops = { ++ .delsys = { SYS_ISDBS }, ++ .info = { ++ .name = "VA1J5JF8007/VA1J5JF8011 ISDB-S", ++ .frequency_min = 950000, ++ .frequency_max = 2150000, ++ .frequency_stepsize = 1000, ++ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO | ++ FE_CAN_MULTISTREAM, ++ }, ++ ++ .read_snr = va1j5jf8007s_read_snr, ++ .get_frontend_algo = va1j5jf8007s_get_frontend_algo, ++ .read_status = va1j5jf8007s_read_status, ++ .tune = va1j5jf8007s_tune, ++ .sleep = va1j5jf8007s_sleep, ++ .init = va1j5jf8007s_init, ++ .release = va1j5jf8007s_release, ++}; ++ ++static int va1j5jf8007s_prepare_1(struct va1j5jf8007s_state *state) ++{ ++ u8 addr; ++ u8 write_buf[1], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0x07; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ if (read_buf[0] != 0x41) ++ return -EIO; ++ ++ return 0; ++} ++ ++static const u8 va1j5jf8007s_20mhz_prepare_bufs[][2] = { ++ {0x04, 0x02}, {0x0d, 0x55}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, ++ {0x1c, 0x0a}, {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, ++ {0x52, 0x89}, {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, ++ {0x87, 0x04}, {0x8e, 0x02}, {0xa3, 0xf7}, {0xa5, 0xc0}, ++}; ++ ++static const u8 va1j5jf8007s_25mhz_prepare_bufs[][2] = { ++ {0x04, 0x02}, {0x11, 0x40}, {0x13, 0x80}, {0x17, 0x01}, {0x1c, 0x0a}, ++ {0x1d, 0xaa}, {0x1e, 0x20}, {0x1f, 0x88}, {0x51, 0xb0}, {0x52, 0x89}, ++ {0x53, 0xb3}, {0x5a, 0x2d}, {0x5b, 0xd3}, {0x85, 0x69}, {0x87, 0x04}, ++ {0x8e, 0x26}, {0xa3, 0xf7}, {0xa5, 0xc0}, ++}; ++ ++static int va1j5jf8007s_prepare_2(struct va1j5jf8007s_state *state) ++{ ++ const u8 (*bufs)[2]; ++ int size; ++ u8 addr; ++ u8 buf[2]; ++ struct i2c_msg msg; ++ int i; ++ ++ switch (state->config->frequency) { ++ case VA1J5JF8007S_20MHZ: ++ bufs = va1j5jf8007s_20mhz_prepare_bufs; ++ size = ARRAY_SIZE(va1j5jf8007s_20mhz_prepare_bufs); ++ break; ++ case VA1J5JF8007S_25MHZ: ++ bufs = va1j5jf8007s_25mhz_prepare_bufs; ++ size = ARRAY_SIZE(va1j5jf8007s_25mhz_prepare_bufs); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ addr = state->config->demod_address; ++ ++ msg.addr = addr; ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = buf; ++ for (i = 0; i < size; i++) { ++ memcpy(buf, bufs[i], sizeof(buf)); ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++/* must be called after va1j5jf8007t_attach */ ++int va1j5jf8007s_prepare(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007s_state *state; ++ int ret; ++ ++ state = fe->demodulator_priv; ++ ++ ret = va1j5jf8007s_prepare_1(state); ++ if (ret < 0) ++ return ret; ++ ++ ret = va1j5jf8007s_prepare_2(state); ++ if (ret < 0) ++ return ret; ++ ++ return va1j5jf8007s_init_frequency(state); ++} ++ ++struct dvb_frontend * ++va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, ++ struct i2c_adapter *adap) ++{ ++ struct va1j5jf8007s_state *state; ++ struct dvb_frontend *fe; ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ state = kzalloc(sizeof(struct va1j5jf8007s_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->config = config; ++ state->adap = adap; ++ ++ fe = &state->fe; ++ memcpy(&fe->ops, &va1j5jf8007s_ops, sizeof(struct dvb_frontend_ops)); ++ fe->demodulator_priv = state; ++ ++ buf[0] = 0x01; ++ buf[1] = 0x80; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) { ++ kfree(state); ++ return NULL; ++ } ++ ++ return fe; ++} +diff --git a/drivers/media/pci/pt1/va1j5jf8007s.h b/drivers/media/pci/pt1/va1j5jf8007s.h +new file mode 100644 +index 0000000..b7d6f05 +--- /dev/null ++++ b/drivers/media/pci/pt1/va1j5jf8007s.h +@@ -0,0 +1,46 @@ ++/* ++ * ISDB-S driver for VA1J5JF8007/VA1J5JF8011 ++ * ++ * Copyright (C) 2009 HIRANO Takahito ++ * ++ * based on pt1dvr - http://pt1dvr.sourceforge.jp/ ++ * by Tomoaki Ishikawa ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef VA1J5JF8007S_H ++#define VA1J5JF8007S_H ++ ++enum va1j5jf8007s_frequency { ++ VA1J5JF8007S_20MHZ, ++ VA1J5JF8007S_25MHZ, ++}; ++ ++struct va1j5jf8007s_config { ++ u8 demod_address; ++ enum va1j5jf8007s_frequency frequency; ++}; ++ ++struct i2c_adapter; ++ ++struct dvb_frontend * ++va1j5jf8007s_attach(const struct va1j5jf8007s_config *config, ++ struct i2c_adapter *adap); ++ ++/* must be called after va1j5jf8007t_attach */ ++int va1j5jf8007s_prepare(struct dvb_frontend *fe); ++ ++#endif +diff --git a/drivers/media/pci/pt1/va1j5jf8007t.c b/drivers/media/pci/pt1/va1j5jf8007t.c +new file mode 100644 +index 0000000..2db1515 +--- /dev/null ++++ b/drivers/media/pci/pt1/va1j5jf8007t.c +@@ -0,0 +1,536 @@ ++/* ++ * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 ++ * ++ * Copyright (C) 2009 HIRANO Takahito ++ * ++ * based on pt1dvr - http://pt1dvr.sourceforge.jp/ ++ * by Tomoaki Ishikawa ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "dvb_frontend.h" ++#include "dvb_math.h" ++#include "va1j5jf8007t.h" ++ ++enum va1j5jf8007t_tune_state { ++ VA1J5JF8007T_IDLE, ++ VA1J5JF8007T_SET_FREQUENCY, ++ VA1J5JF8007T_CHECK_FREQUENCY, ++ VA1J5JF8007T_SET_MODULATION, ++ VA1J5JF8007T_CHECK_MODULATION, ++ VA1J5JF8007T_TRACK, ++ VA1J5JF8007T_ABORT, ++}; ++ ++struct va1j5jf8007t_state { ++ const struct va1j5jf8007t_config *config; ++ struct i2c_adapter *adap; ++ struct dvb_frontend fe; ++ enum va1j5jf8007t_tune_state tune_state; ++}; ++ ++static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) ++{ ++ struct va1j5jf8007t_state *state; ++ u8 addr; ++ int i; ++ u8 write_buf[1], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ s32 word, x, y; ++ ++ state = fe->demodulator_priv; ++ addr = state->config->demod_address; ++ ++ word = 0; ++ for (i = 0; i < 3; i++) { ++ write_buf[0] = 0x8b + i; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ word <<= 8; ++ word |= read_buf[0]; ++ } ++ ++ if (!word) ++ return -EIO; ++ ++ x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); ++ y = (24ll << 46) / 1000000; ++ y = ((s64)y * x >> 30) - (16ll << 40) / 10000; ++ y = ((s64)y * x >> 29) + (398ll << 35) / 10000; ++ y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; ++ y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; ++ *snr = y >> 15; ++ return 0; ++} ++ ++static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) ++{ ++ return DVBFE_ALGO_HW; ++} ++ ++static int ++va1j5jf8007t_read_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct va1j5jf8007t_state *state; ++ ++ state = fe->demodulator_priv; ++ ++ switch (state->tune_state) { ++ case VA1J5JF8007T_IDLE: ++ case VA1J5JF8007T_SET_FREQUENCY: ++ case VA1J5JF8007T_CHECK_FREQUENCY: ++ *status = 0; ++ return 0; ++ ++ ++ case VA1J5JF8007T_SET_MODULATION: ++ case VA1J5JF8007T_CHECK_MODULATION: ++ case VA1J5JF8007T_ABORT: ++ *status |= FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007T_TRACK: ++ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ return 0; ++ } ++ ++ BUG(); ++} ++ ++struct va1j5jf8007t_cb_map { ++ u32 frequency; ++ u8 cb; ++}; ++ ++static const struct va1j5jf8007t_cb_map va1j5jf8007t_cb_maps[] = { ++ { 90000000, 0x80 }, ++ { 140000000, 0x81 }, ++ { 170000000, 0xa1 }, ++ { 220000000, 0x62 }, ++ { 330000000, 0xa2 }, ++ { 402000000, 0xe2 }, ++ { 450000000, 0x64 }, ++ { 550000000, 0x84 }, ++ { 600000000, 0xa4 }, ++ { 700000000, 0xc4 }, ++}; ++ ++static u8 va1j5jf8007t_lookup_cb(u32 frequency) ++{ ++ int i; ++ const struct va1j5jf8007t_cb_map *map; ++ ++ for (i = 0; i < ARRAY_SIZE(va1j5jf8007t_cb_maps); i++) { ++ map = &va1j5jf8007t_cb_maps[i]; ++ if (frequency < map->frequency) ++ return map->cb; ++ } ++ return 0xe4; ++} ++ ++static int va1j5jf8007t_set_frequency(struct va1j5jf8007t_state *state) ++{ ++ u32 frequency; ++ u16 word; ++ u8 buf[6]; ++ struct i2c_msg msg; ++ ++ frequency = state->fe.dtv_property_cache.frequency; ++ ++ word = (frequency + 71428) / 142857 + 399; ++ buf[0] = 0xfe; ++ buf[1] = 0xc2; ++ buf[2] = word >> 8; ++ buf[3] = word; ++ buf[4] = 0x80; ++ buf[5] = va1j5jf8007t_lookup_cb(frequency); ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int ++va1j5jf8007t_check_frequency(struct va1j5jf8007t_state *state, int *lock) ++{ ++ u8 addr; ++ u8 write_buf[2], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0xfe; ++ write_buf[1] = 0xc3; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ *lock = read_buf[0] & 0x40; ++ return 0; ++} ++ ++static int va1j5jf8007t_set_modulation(struct va1j5jf8007t_state *state) ++{ ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0x01; ++ buf[1] = 0x40; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007t_check_modulation(struct va1j5jf8007t_state *state, ++ int *lock, int *retry) ++{ ++ u8 addr; ++ u8 write_buf[1], read_buf[1]; ++ struct i2c_msg msgs[2]; ++ ++ addr = state->config->demod_address; ++ ++ write_buf[0] = 0x80; ++ ++ msgs[0].addr = addr; ++ msgs[0].flags = 0; ++ msgs[0].len = sizeof(write_buf); ++ msgs[0].buf = write_buf; ++ ++ msgs[1].addr = addr; ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].len = sizeof(read_buf); ++ msgs[1].buf = read_buf; ++ ++ if (i2c_transfer(state->adap, msgs, 2) != 2) ++ return -EREMOTEIO; ++ ++ *lock = !(read_buf[0] & 0x10); ++ *retry = read_buf[0] & 0x80; ++ return 0; ++} ++ ++static int ++va1j5jf8007t_tune(struct dvb_frontend *fe, ++ bool re_tune, ++ unsigned int mode_flags, unsigned int *delay, ++ fe_status_t *status) ++{ ++ struct va1j5jf8007t_state *state; ++ int ret; ++ int lock = 0, retry = 0; ++ ++ state = fe->demodulator_priv; ++ ++ if (re_tune) ++ state->tune_state = VA1J5JF8007T_SET_FREQUENCY; ++ ++ switch (state->tune_state) { ++ case VA1J5JF8007T_IDLE: ++ *delay = 3 * HZ; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007T_SET_FREQUENCY: ++ ret = va1j5jf8007t_set_frequency(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007T_CHECK_FREQUENCY; ++ *delay = 0; ++ *status = 0; ++ return 0; ++ ++ case VA1J5JF8007T_CHECK_FREQUENCY: ++ ret = va1j5jf8007t_check_frequency(state, &lock); ++ if (ret < 0) ++ return ret; ++ ++ if (!lock) { ++ *delay = (HZ + 999) / 1000; ++ *status = 0; ++ return 0; ++ } ++ ++ state->tune_state = VA1J5JF8007T_SET_MODULATION; ++ *delay = 0; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007T_SET_MODULATION: ++ ret = va1j5jf8007t_set_modulation(state); ++ if (ret < 0) ++ return ret; ++ ++ state->tune_state = VA1J5JF8007T_CHECK_MODULATION; ++ *delay = 0; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ ++ case VA1J5JF8007T_CHECK_MODULATION: ++ ret = va1j5jf8007t_check_modulation(state, &lock, &retry); ++ if (ret < 0) ++ return ret; ++ ++ if (!lock) { ++ if (!retry) { ++ state->tune_state = VA1J5JF8007T_ABORT; ++ *delay = 3 * HZ; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ } ++ *delay = (HZ + 999) / 1000; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ } ++ ++ state->tune_state = VA1J5JF8007T_TRACK; ++ /* fall through */ ++ ++ case VA1J5JF8007T_TRACK: ++ *delay = 3 * HZ; ++ *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK; ++ return 0; ++ ++ case VA1J5JF8007T_ABORT: ++ *delay = 3 * HZ; ++ *status = FE_HAS_SIGNAL; ++ return 0; ++ } ++ ++ BUG(); ++} ++ ++static int va1j5jf8007t_init_frequency(struct va1j5jf8007t_state *state) ++{ ++ u8 buf[7]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0xfe; ++ buf[1] = 0xc2; ++ buf[2] = 0x01; ++ buf[3] = 0x8f; ++ buf[4] = 0xc1; ++ buf[5] = 0x80; ++ buf[6] = 0x80; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007t_set_sleep(struct va1j5jf8007t_state *state, int sleep) ++{ ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ buf[0] = 0x03; ++ buf[1] = sleep ? 0x90 : 0x80; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ ++ return 0; ++} ++ ++static int va1j5jf8007t_sleep(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007t_state *state; ++ int ret; ++ ++ state = fe->demodulator_priv; ++ ++ ret = va1j5jf8007t_init_frequency(state); ++ if (ret < 0) ++ return ret; ++ ++ return va1j5jf8007t_set_sleep(state, 1); ++} ++ ++static int va1j5jf8007t_init(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007t_state *state; ++ ++ state = fe->demodulator_priv; ++ state->tune_state = VA1J5JF8007T_IDLE; ++ ++ return va1j5jf8007t_set_sleep(state, 0); ++} ++ ++static void va1j5jf8007t_release(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007t_state *state; ++ state = fe->demodulator_priv; ++ kfree(state); ++} ++ ++static struct dvb_frontend_ops va1j5jf8007t_ops = { ++ .delsys = { SYS_ISDBT }, ++ .info = { ++ .name = "VA1J5JF8007/VA1J5JF8011 ISDB-T", ++ .frequency_min = 90000000, ++ .frequency_max = 770000000, ++ .frequency_stepsize = 142857, ++ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | ++ FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | ++ FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, ++ }, ++ ++ .read_snr = va1j5jf8007t_read_snr, ++ .get_frontend_algo = va1j5jf8007t_get_frontend_algo, ++ .read_status = va1j5jf8007t_read_status, ++ .tune = va1j5jf8007t_tune, ++ .sleep = va1j5jf8007t_sleep, ++ .init = va1j5jf8007t_init, ++ .release = va1j5jf8007t_release, ++}; ++ ++static const u8 va1j5jf8007t_20mhz_prepare_bufs[][2] = { ++ {0x03, 0x90}, {0x14, 0x8f}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, ++ {0x22, 0x83}, {0x31, 0x0d}, {0x32, 0xe0}, {0x39, 0xd3}, {0x3a, 0x00}, ++ {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x02}, {0x76, 0x4e}, {0x77, 0x03}, ++ {0xef, 0x01} ++}; ++ ++static const u8 va1j5jf8007t_25mhz_prepare_bufs[][2] = { ++ {0x03, 0x90}, {0x1c, 0x2a}, {0x1d, 0xa8}, {0x1e, 0xa2}, {0x22, 0x83}, ++ {0x3a, 0x00}, {0x5c, 0x40}, {0x5f, 0x80}, {0x75, 0x0a}, {0x76, 0x4c}, ++ {0x77, 0x03}, {0xef, 0x01} ++}; ++ ++int va1j5jf8007t_prepare(struct dvb_frontend *fe) ++{ ++ struct va1j5jf8007t_state *state; ++ const u8 (*bufs)[2]; ++ int size; ++ u8 buf[2]; ++ struct i2c_msg msg; ++ int i; ++ ++ state = fe->demodulator_priv; ++ ++ switch (state->config->frequency) { ++ case VA1J5JF8007T_20MHZ: ++ bufs = va1j5jf8007t_20mhz_prepare_bufs; ++ size = ARRAY_SIZE(va1j5jf8007t_20mhz_prepare_bufs); ++ break; ++ case VA1J5JF8007T_25MHZ: ++ bufs = va1j5jf8007t_25mhz_prepare_bufs; ++ size = ARRAY_SIZE(va1j5jf8007t_25mhz_prepare_bufs); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ for (i = 0; i < size; i++) { ++ memcpy(buf, bufs[i], sizeof(buf)); ++ if (i2c_transfer(state->adap, &msg, 1) != 1) ++ return -EREMOTEIO; ++ } ++ ++ return va1j5jf8007t_init_frequency(state); ++} ++ ++struct dvb_frontend * ++va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, ++ struct i2c_adapter *adap) ++{ ++ struct va1j5jf8007t_state *state; ++ struct dvb_frontend *fe; ++ u8 buf[2]; ++ struct i2c_msg msg; ++ ++ state = kzalloc(sizeof(struct va1j5jf8007t_state), GFP_KERNEL); ++ if (!state) ++ return NULL; ++ ++ state->config = config; ++ state->adap = adap; ++ ++ fe = &state->fe; ++ memcpy(&fe->ops, &va1j5jf8007t_ops, sizeof(struct dvb_frontend_ops)); ++ fe->demodulator_priv = state; ++ ++ buf[0] = 0x01; ++ buf[1] = 0x80; ++ ++ msg.addr = state->config->demod_address; ++ msg.flags = 0; ++ msg.len = sizeof(buf); ++ msg.buf = buf; ++ ++ if (i2c_transfer(state->adap, &msg, 1) != 1) { ++ kfree(state); ++ return NULL; ++ } ++ ++ return fe; ++} +diff --git a/drivers/media/pci/pt1/va1j5jf8007t.h b/drivers/media/pci/pt1/va1j5jf8007t.h +new file mode 100644 +index 0000000..2903be5 +--- /dev/null ++++ b/drivers/media/pci/pt1/va1j5jf8007t.h +@@ -0,0 +1,46 @@ ++/* ++ * ISDB-T driver for VA1J5JF8007/VA1J5JF8011 ++ * ++ * Copyright (C) 2009 HIRANO Takahito ++ * ++ * based on pt1dvr - http://pt1dvr.sourceforge.jp/ ++ * by Tomoaki Ishikawa ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef VA1J5JF8007T_H ++#define VA1J5JF8007T_H ++ ++enum va1j5jf8007t_frequency { ++ VA1J5JF8007T_20MHZ, ++ VA1J5JF8007T_25MHZ, ++}; ++ ++struct va1j5jf8007t_config { ++ u8 demod_address; ++ enum va1j5jf8007t_frequency frequency; ++}; ++ ++struct i2c_adapter; ++ ++struct dvb_frontend * ++va1j5jf8007t_attach(const struct va1j5jf8007t_config *config, ++ struct i2c_adapter *adap); ++ ++/* must be called after va1j5jf8007s_attach */ ++int va1j5jf8007t_prepare(struct dvb_frontend *fe); ++ ++#endif +diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig +new file mode 100644 +index 0000000..15b90d6 +--- /dev/null ++++ b/drivers/media/pci/saa7134/Kconfig +@@ -0,0 +1,64 @@ ++config VIDEO_SAA7134 ++ tristate "Philips SAA7134 support" ++ depends on VIDEO_DEV && PCI && I2C ++ select VIDEOBUF_DMA_SG ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select CRC32 ++ select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for Philips SAA713x based ++ TV cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7134. ++ ++config VIDEO_SAA7134_ALSA ++ tristate "Philips SAA7134 DMA audio support" ++ depends on VIDEO_SAA7134 && SND ++ select SND_PCM ++ ---help--- ++ This is a video4linux driver for direct (DMA) audio in ++ Philips SAA713x based TV cards using ALSA ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7134-alsa. ++ ++config VIDEO_SAA7134_RC ++ bool "Philips SAA7134 Remote Controller support" ++ depends on RC_CORE ++ depends on VIDEO_SAA7134 ++ depends on !(RC_CORE=m && VIDEO_SAA7134=y) ++ default y ++ ---help--- ++ Enables Remote Controller support on saa7134 driver. ++ ++config VIDEO_SAA7134_DVB ++ tristate "DVB/ATSC Support for saa7134 based TV cards" ++ depends on VIDEO_SAA7134 && DVB_CORE ++ select VIDEOBUF_DVB ++ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ISL6405 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10036 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This adds support for DVB cards based on the ++ Philips saa7134 chip. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7134-dvb. +diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile +new file mode 100644 +index 0000000..3537548 +--- /dev/null ++++ b/drivers/media/pci/saa7134/Makefile +@@ -0,0 +1,16 @@ ++ ++saa7134-y += saa7134-cards.o saa7134-core.o saa7134-i2c.o ++saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o ++saa7134-y += saa7134-video.o ++saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o ++ ++obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o ++ ++obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o ++ ++obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o ++ ++ccflags-y += -I$(srctree)/drivers/media/i2c ++ccflags-y += -I$(srctree)/drivers/media/tuners ++ccflags-y += -I$(srctree)/drivers/media/dvb-core ++ccflags-y += -I$(srctree)/drivers/media/dvb-frontends +diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c +new file mode 100644 +index 0000000..f147b05 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa6752hs.c +@@ -0,0 +1,1012 @@ ++ /* ++ saa6752hs - i2c-driver for the saa6752hs by Philips ++ ++ Copyright (C) 2004 Andrew de Quincey ++ ++ AC-3 support: ++ ++ Copyright (C) 2008 Hans Verkuil ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License vs 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 Mvss Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 ++#define MPEG_VIDEO_MAX_BITRATE_MAX 27000 ++#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 ++#define MPEG_PID_MAX ((1 << 14) - 1) ++ ++ ++MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); ++MODULE_AUTHOR("Andrew de Quincey"); ++MODULE_LICENSE("GPL"); ++ ++enum saa6752hs_videoformat { ++ SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ ++ SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ ++ SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */ ++ SAA6752HS_VF_SIF = 3, /* SIF video format: 352x288 */ ++ SAA6752HS_VF_UNKNOWN, ++}; ++ ++struct saa6752hs_mpeg_params { ++ /* transport streams */ ++ __u16 ts_pid_pmt; ++ __u16 ts_pid_audio; ++ __u16 ts_pid_video; ++ __u16 ts_pid_pcr; ++ ++ /* audio */ ++ enum v4l2_mpeg_audio_encoding au_encoding; ++ enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; ++ enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; ++ ++ /* video */ ++ enum v4l2_mpeg_video_aspect vi_aspect; ++ enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode; ++ __u32 vi_bitrate; ++ __u32 vi_bitrate_peak; ++}; ++ ++static const struct v4l2_format v4l2_format_table[] = ++{ ++ [SAA6752HS_VF_D1] = ++ { .fmt = { .pix = { .width = 720, .height = 576 }}}, ++ [SAA6752HS_VF_2_3_D1] = ++ { .fmt = { .pix = { .width = 480, .height = 576 }}}, ++ [SAA6752HS_VF_1_2_D1] = ++ { .fmt = { .pix = { .width = 352, .height = 576 }}}, ++ [SAA6752HS_VF_SIF] = ++ { .fmt = { .pix = { .width = 352, .height = 288 }}}, ++ [SAA6752HS_VF_UNKNOWN] = ++ { .fmt = { .pix = { .width = 0, .height = 0}}}, ++}; ++ ++struct saa6752hs_state { ++ struct v4l2_subdev sd; ++ int chip; ++ u32 revision; ++ int has_ac3; ++ struct saa6752hs_mpeg_params params; ++ enum saa6752hs_videoformat video_format; ++ v4l2_std_id standard; ++}; ++ ++enum saa6752hs_command { ++ SAA6752HS_COMMAND_RESET = 0, ++ SAA6752HS_COMMAND_STOP = 1, ++ SAA6752HS_COMMAND_START = 2, ++ SAA6752HS_COMMAND_PAUSE = 3, ++ SAA6752HS_COMMAND_RECONFIGURE = 4, ++ SAA6752HS_COMMAND_SLEEP = 5, ++ SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, ++ ++ SAA6752HS_COMMAND_MAX ++}; ++ ++static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct saa6752hs_state, sd); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static u8 PAT[] = { ++ 0xc2, /* i2c register */ ++ 0x00, /* table number for encoder */ ++ ++ 0x47, /* sync */ ++ 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */ ++ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ ++ ++ 0x00, /* PSI pointer to start of table */ ++ ++ 0x00, /* tid(0) */ ++ 0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */ ++ ++ 0x00, 0x01, /* transport_stream_id(1) */ ++ ++ 0xc1, /* version_number(0), current_next_indicator(1) */ ++ ++ 0x00, 0x00, /* section_number(0), last_section_number(0) */ ++ ++ 0x00, 0x01, /* program_number(1) */ ++ ++ 0xe0, 0x00, /* PMT PID */ ++ ++ 0x00, 0x00, 0x00, 0x00 /* CRC32 */ ++}; ++ ++static u8 PMT[] = { ++ 0xc2, /* i2c register */ ++ 0x01, /* table number for encoder */ ++ ++ 0x47, /* sync */ ++ 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */ ++ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ ++ ++ 0x00, /* PSI pointer to start of table */ ++ ++ 0x02, /* tid(2) */ ++ 0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */ ++ ++ 0x00, 0x01, /* program_number(1) */ ++ ++ 0xc1, /* version_number(0), current_next_indicator(1) */ ++ ++ 0x00, 0x00, /* section_number(0), last_section_number(0) */ ++ ++ 0xe0, 0x00, /* PCR_PID */ ++ ++ 0xf0, 0x00, /* program_info_length(0) */ ++ ++ 0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ ++ 0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */ ++ ++ 0x00, 0x00, 0x00, 0x00 /* CRC32 */ ++}; ++ ++static u8 PMT_AC3[] = { ++ 0xc2, /* i2c register */ ++ 0x01, /* table number for encoder(1) */ ++ 0x47, /* sync */ ++ ++ 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ ++ 0x10, /* PMT PID (0x0010) */ ++ 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ ++ ++ 0x00, /* PSI pointer to start of table */ ++ ++ 0x02, /* TID (2) */ ++ 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ ++ ++ 0x00, 0x01, /* program_number(1) */ ++ ++ 0xc1, /* version_number(0), current_next_indicator(1) */ ++ ++ 0x00, 0x00, /* section_number(0), last_section_number(0) */ ++ ++ 0xe1, 0x04, /* PCR_PID (0x0104) */ ++ ++ 0xf0, 0x00, /* program_info_length(0) */ ++ ++ 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ ++ 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ ++ 0x6a, /* AC3 */ ++ 0x01, /* Descriptor_length(1) */ ++ 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ ++ ++ 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ ++}; ++ ++static struct saa6752hs_mpeg_params param_defaults = ++{ ++ .ts_pid_pmt = 16, ++ .ts_pid_video = 260, ++ .ts_pid_audio = 256, ++ .ts_pid_pcr = 259, ++ ++ .vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, ++ .vi_bitrate = 4000, ++ .vi_bitrate_peak = 6000, ++ .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ ++ .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, ++ .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, ++ .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int saa6752hs_chip_command(struct i2c_client *client, ++ enum saa6752hs_command command) ++{ ++ unsigned char buf[3]; ++ unsigned long timeout; ++ int status = 0; ++ ++ /* execute the command */ ++ switch(command) { ++ case SAA6752HS_COMMAND_RESET: ++ buf[0] = 0x00; ++ break; ++ ++ case SAA6752HS_COMMAND_STOP: ++ buf[0] = 0x03; ++ break; ++ ++ case SAA6752HS_COMMAND_START: ++ buf[0] = 0x02; ++ break; ++ ++ case SAA6752HS_COMMAND_PAUSE: ++ buf[0] = 0x04; ++ break; ++ ++ case SAA6752HS_COMMAND_RECONFIGURE: ++ buf[0] = 0x05; ++ break; ++ ++ case SAA6752HS_COMMAND_SLEEP: ++ buf[0] = 0x06; ++ break; ++ ++ case SAA6752HS_COMMAND_RECONFIGURE_FORCE: ++ buf[0] = 0x07; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ /* set it and wait for it to be so */ ++ i2c_master_send(client, buf, 1); ++ timeout = jiffies + HZ * 3; ++ for (;;) { ++ /* get the current status */ ++ buf[0] = 0x10; ++ i2c_master_send(client, buf, 1); ++ i2c_master_recv(client, buf, 1); ++ ++ if (!(buf[0] & 0x20)) ++ break; ++ if (time_after(jiffies,timeout)) { ++ status = -ETIMEDOUT; ++ break; ++ } ++ ++ msleep(10); ++ } ++ ++ /* delay a bit to let encoder settle */ ++ msleep(50); ++ ++ return status; ++} ++ ++ ++static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) ++{ ++ u8 buf[2]; ++ ++ buf[0] = reg; ++ buf[1] = val; ++ i2c_master_send(client, buf, 2); ++} ++ ++static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) ++{ ++ u8 buf[3]; ++ ++ buf[0] = reg; ++ buf[1] = val >> 8; ++ buf[2] = val & 0xff; ++ i2c_master_send(client, buf, 3); ++} ++ ++static int saa6752hs_set_bitrate(struct i2c_client *client, ++ struct saa6752hs_state *h) ++{ ++ struct saa6752hs_mpeg_params *params = &h->params; ++ int tot_bitrate; ++ int is_384k; ++ ++ /* set the bitrate mode */ ++ set_reg8(client, 0x71, ++ params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); ++ ++ /* set the video bitrate */ ++ if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { ++ /* set the target bitrate */ ++ set_reg16(client, 0x80, params->vi_bitrate); ++ ++ /* set the max bitrate */ ++ set_reg16(client, 0x81, params->vi_bitrate_peak); ++ tot_bitrate = params->vi_bitrate_peak; ++ } else { ++ /* set the target bitrate (no max bitrate for CBR) */ ++ set_reg16(client, 0x81, params->vi_bitrate); ++ tot_bitrate = params->vi_bitrate; ++ } ++ ++ /* set the audio encoding */ ++ set_reg8(client, 0x93, ++ params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); ++ ++ /* set the audio bitrate */ ++ if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) ++ is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; ++ else ++ is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; ++ set_reg8(client, 0x94, is_384k); ++ tot_bitrate += is_384k ? 384 : 256; ++ ++ /* Note: the total max bitrate is determined by adding the video and audio ++ bitrates together and also adding an extra 768kbit/s to stay on the ++ safe side. If more control should be required, then an extra MPEG control ++ should be added. */ ++ tot_bitrate += 768; ++ if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX) ++ tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; ++ ++ /* set the total bitrate */ ++ set_reg16(client, 0xb1, tot_bitrate); ++ return 0; ++} ++ ++ ++static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, ++ struct v4l2_ext_control *ctrl) ++{ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_PMT: ++ ctrl->value = params->ts_pid_pmt; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_AUDIO: ++ ctrl->value = params->ts_pid_audio; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_VIDEO: ++ ctrl->value = params->ts_pid_video; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_PCR: ++ ctrl->value = params->ts_pid_pcr; ++ break; ++ case V4L2_CID_MPEG_AUDIO_ENCODING: ++ ctrl->value = params->au_encoding; ++ break; ++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: ++ ctrl->value = params->au_l2_bitrate; ++ break; ++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: ++ if (!has_ac3) ++ return -EINVAL; ++ ctrl->value = params->au_ac3_bitrate; ++ break; ++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: ++ ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ENCODING: ++ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ ctrl->value = params->vi_aspect; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ ctrl->value = params->vi_bitrate * 1000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ ctrl->value = params->vi_bitrate_peak * 1000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ ctrl->value = params->vi_bitrate_mode; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, ++ struct v4l2_ext_control *ctrl, int set) ++{ ++ int old = 0, new; ++ ++ new = ctrl->value; ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; ++ if (set && new != old) ++ return -ERANGE; ++ new = old; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_PMT: ++ old = params->ts_pid_pmt; ++ if (set && new > MPEG_PID_MAX) ++ return -ERANGE; ++ if (new > MPEG_PID_MAX) ++ new = MPEG_PID_MAX; ++ params->ts_pid_pmt = new; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_AUDIO: ++ old = params->ts_pid_audio; ++ if (set && new > MPEG_PID_MAX) ++ return -ERANGE; ++ if (new > MPEG_PID_MAX) ++ new = MPEG_PID_MAX; ++ params->ts_pid_audio = new; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_VIDEO: ++ old = params->ts_pid_video; ++ if (set && new > MPEG_PID_MAX) ++ return -ERANGE; ++ if (new > MPEG_PID_MAX) ++ new = MPEG_PID_MAX; ++ params->ts_pid_video = new; ++ break; ++ case V4L2_CID_MPEG_STREAM_PID_PCR: ++ old = params->ts_pid_pcr; ++ if (set && new > MPEG_PID_MAX) ++ return -ERANGE; ++ if (new > MPEG_PID_MAX) ++ new = MPEG_PID_MAX; ++ params->ts_pid_pcr = new; ++ break; ++ case V4L2_CID_MPEG_AUDIO_ENCODING: ++ old = params->au_encoding; ++ if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && ++ (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) ++ return -ERANGE; ++ params->au_encoding = new; ++ break; ++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: ++ old = params->au_l2_bitrate; ++ if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K && ++ new != V4L2_MPEG_AUDIO_L2_BITRATE_384K) ++ return -ERANGE; ++ if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K) ++ new = V4L2_MPEG_AUDIO_L2_BITRATE_256K; ++ else ++ new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; ++ params->au_l2_bitrate = new; ++ break; ++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: ++ if (!has_ac3) ++ return -EINVAL; ++ old = params->au_ac3_bitrate; ++ if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && ++ new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) ++ return -ERANGE; ++ if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) ++ new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; ++ else ++ new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; ++ params->au_ac3_bitrate = new; ++ break; ++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: ++ old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; ++ if (set && new != old) ++ return -ERANGE; ++ new = old; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ENCODING: ++ old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; ++ if (set && new != old) ++ return -ERANGE; ++ new = old; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ old = params->vi_aspect; ++ if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 && ++ new != V4L2_MPEG_VIDEO_ASPECT_4x3) ++ return -ERANGE; ++ if (new != V4L2_MPEG_VIDEO_ASPECT_16x9) ++ new = V4L2_MPEG_VIDEO_ASPECT_4x3; ++ params->vi_aspect = new; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ old = params->vi_bitrate * 1000; ++ new = 1000 * (new / 1000); ++ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) ++ return -ERANGE; ++ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) ++ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; ++ params->vi_bitrate = new / 1000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ old = params->vi_bitrate_peak * 1000; ++ new = 1000 * (new / 1000); ++ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) ++ return -ERANGE; ++ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) ++ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; ++ params->vi_bitrate_peak = new / 1000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ old = params->vi_bitrate_mode; ++ params->vi_bitrate_mode = new; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ctrl->value = new; ++ return 0; ++} ++ ++ ++static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ struct saa6752hs_mpeg_params *params = &h->params; ++ int err; ++ ++ switch (qctrl->id) { ++ case V4L2_CID_MPEG_AUDIO_ENCODING: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, ++ h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : ++ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, ++ 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); ++ ++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_AUDIO_L2_BITRATE_256K, ++ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, ++ V4L2_MPEG_AUDIO_L2_BITRATE_256K); ++ ++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: ++ if (!h->has_ac3) ++ return -EINVAL; ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_AUDIO_AC3_BITRATE_256K, ++ V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1, ++ V4L2_MPEG_AUDIO_AC3_BITRATE_256K); ++ ++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, ++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1, ++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); ++ ++ case V4L2_CID_MPEG_VIDEO_ENCODING: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, ++ V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, ++ V4L2_MPEG_VIDEO_ENCODING_MPEG_2); ++ ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_VIDEO_ASPECT_4x3, ++ V4L2_MPEG_VIDEO_ASPECT_16x9, 1, ++ V4L2_MPEG_VIDEO_ASPECT_4x3); ++ ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); ++ if (err == 0 && ++ params->vi_bitrate_mode == ++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ++ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; ++ return err; ++ ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS); ++ ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ return v4l2_ctrl_query_fill(qctrl, ++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, ++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); ++ case V4L2_CID_MPEG_STREAM_PID_PMT: ++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); ++ case V4L2_CID_MPEG_STREAM_PID_AUDIO: ++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); ++ case V4L2_CID_MPEG_STREAM_PID_VIDEO: ++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); ++ case V4L2_CID_MPEG_STREAM_PID_PCR: ++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); ++ ++ default: ++ break; ++ } ++ return -EINVAL; ++} ++ ++static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu) ++{ ++ static const u32 mpeg_audio_encoding[] = { ++ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, ++ V4L2_CTRL_MENU_IDS_END ++ }; ++ static const u32 mpeg_audio_ac3_encoding[] = { ++ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, ++ V4L2_MPEG_AUDIO_ENCODING_AC3, ++ V4L2_CTRL_MENU_IDS_END ++ }; ++ static u32 mpeg_audio_l2_bitrate[] = { ++ V4L2_MPEG_AUDIO_L2_BITRATE_256K, ++ V4L2_MPEG_AUDIO_L2_BITRATE_384K, ++ V4L2_CTRL_MENU_IDS_END ++ }; ++ static u32 mpeg_audio_ac3_bitrate[] = { ++ V4L2_MPEG_AUDIO_AC3_BITRATE_256K, ++ V4L2_MPEG_AUDIO_AC3_BITRATE_384K, ++ V4L2_CTRL_MENU_IDS_END ++ }; ++ struct saa6752hs_state *h = to_state(sd); ++ struct v4l2_queryctrl qctrl; ++ int err; ++ ++ qctrl.id = qmenu->id; ++ err = saa6752hs_queryctrl(sd, &qctrl); ++ if (err) ++ return err; ++ switch (qmenu->id) { ++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE: ++ return v4l2_ctrl_query_menu_valid_items(qmenu, ++ mpeg_audio_l2_bitrate); ++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: ++ if (!h->has_ac3) ++ return -EINVAL; ++ return v4l2_ctrl_query_menu_valid_items(qmenu, ++ mpeg_audio_ac3_bitrate); ++ case V4L2_CID_MPEG_AUDIO_ENCODING: ++ return v4l2_ctrl_query_menu_valid_items(qmenu, ++ h->has_ac3 ? mpeg_audio_ac3_encoding : ++ mpeg_audio_encoding); ++ } ++ return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); ++} ++ ++static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) ++{ ++ unsigned char buf[9], buf2[4]; ++ struct saa6752hs_state *h = to_state(sd); ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ unsigned size; ++ u32 crc; ++ unsigned char localPAT[256]; ++ unsigned char localPMT[256]; ++ ++ /* Set video format - must be done first as it resets other settings */ ++ set_reg8(client, 0x41, h->video_format); ++ ++ /* Set number of lines in input signal */ ++ set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); ++ ++ /* set bitrate */ ++ saa6752hs_set_bitrate(client, h); ++ ++ /* Set GOP structure {3, 13} */ ++ set_reg16(client, 0x72, 0x030d); ++ ++ /* Set minimum Q-scale {4} */ ++ set_reg8(client, 0x82, 0x04); ++ ++ /* Set maximum Q-scale {12} */ ++ set_reg8(client, 0x83, 0x0c); ++ ++ /* Set Output Protocol */ ++ set_reg8(client, 0xd0, 0x81); ++ ++ /* Set video output stream format {TS} */ ++ set_reg8(client, 0xb0, 0x05); ++ ++ /* Set leading null byte for TS */ ++ set_reg16(client, 0xf6, leading_null_bytes); ++ ++ /* compute PAT */ ++ memcpy(localPAT, PAT, sizeof(PAT)); ++ localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f); ++ localPAT[18] = h->params.ts_pid_pmt & 0xff; ++ crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); ++ localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; ++ localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; ++ localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; ++ localPAT[sizeof(PAT) - 1] = crc & 0xFF; ++ ++ /* compute PMT */ ++ if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { ++ size = sizeof(PMT_AC3); ++ memcpy(localPMT, PMT_AC3, size); ++ } else { ++ size = sizeof(PMT); ++ memcpy(localPMT, PMT, size); ++ } ++ localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); ++ localPMT[4] = h->params.ts_pid_pmt & 0xff; ++ localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); ++ localPMT[16] = h->params.ts_pid_pcr & 0xFF; ++ localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F); ++ localPMT[21] = h->params.ts_pid_video & 0xFF; ++ localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); ++ localPMT[26] = h->params.ts_pid_audio & 0xFF; ++ crc = crc32_be(~0, &localPMT[7], size - 7 - 4); ++ localPMT[size - 4] = (crc >> 24) & 0xFF; ++ localPMT[size - 3] = (crc >> 16) & 0xFF; ++ localPMT[size - 2] = (crc >> 8) & 0xFF; ++ localPMT[size - 1] = crc & 0xFF; ++ ++ /* Set Audio PID */ ++ set_reg16(client, 0xc1, h->params.ts_pid_audio); ++ ++ /* Set Video PID */ ++ set_reg16(client, 0xc0, h->params.ts_pid_video); ++ ++ /* Set PCR PID */ ++ set_reg16(client, 0xc4, h->params.ts_pid_pcr); ++ ++ /* Send SI tables */ ++ i2c_master_send(client, localPAT, sizeof(PAT)); ++ i2c_master_send(client, localPMT, size); ++ ++ /* mute then unmute audio. This removes buzzing artefacts */ ++ set_reg8(client, 0xa4, 1); ++ set_reg8(client, 0xa4, 0); ++ ++ /* start it going */ ++ saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); ++ ++ /* readout current state */ ++ buf[0] = 0xE1; ++ buf[1] = 0xA7; ++ buf[2] = 0xFE; ++ buf[3] = 0x82; ++ buf[4] = 0xB0; ++ i2c_master_send(client, buf, 5); ++ i2c_master_recv(client, buf2, 4); ++ ++ /* change aspect ratio */ ++ buf[0] = 0xE0; ++ buf[1] = 0xA7; ++ buf[2] = 0xFE; ++ buf[3] = 0x82; ++ buf[4] = 0xB0; ++ buf[5] = buf2[0]; ++ switch (h->params.vi_aspect) { ++ case V4L2_MPEG_VIDEO_ASPECT_16x9: ++ buf[6] = buf2[1] | 0x40; ++ break; ++ case V4L2_MPEG_VIDEO_ASPECT_4x3: ++ default: ++ buf[6] = buf2[1] & 0xBF; ++ break; ++ } ++ buf[7] = buf2[2]; ++ buf[8] = buf2[3]; ++ i2c_master_send(client, buf, 9); ++ ++ return 0; ++} ++ ++static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ struct saa6752hs_mpeg_params params; ++ int i; ++ ++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ ++ params = h->params; ++ for (i = 0; i < ctrls->count; i++) { ++ int err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, set); ++ ++ if (err) { ++ ctrls->error_idx = i; ++ return err; ++ } ++ } ++ if (set) ++ h->params = params; ++ return 0; ++} ++ ++static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) ++{ ++ return saa6752hs_do_ext_ctrls(sd, ctrls, 1); ++} ++ ++static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) ++{ ++ return saa6752hs_do_ext_ctrls(sd, ctrls, 0); ++} ++ ++static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ int i; ++ ++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ ++ for (i = 0; i < ctrls->count; i++) { ++ int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i); ++ ++ if (err) { ++ ctrls->error_idx = i; ++ return err; ++ } ++ } ++ return 0; ++} ++ ++static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ ++ if (h->video_format == SAA6752HS_VF_UNKNOWN) ++ h->video_format = SAA6752HS_VF_D1; ++ f->width = v4l2_format_table[h->video_format].fmt.pix.width; ++ f->height = v4l2_format_table[h->video_format].fmt.pix.height; ++ f->code = V4L2_MBUS_FMT_FIXED; ++ f->field = V4L2_FIELD_INTERLACED; ++ f->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ int dist_352, dist_480, dist_720; ++ ++ if (f->code != V4L2_MBUS_FMT_FIXED) ++ return -EINVAL; ++ ++ /* ++ FIXME: translate and round width/height into EMPRESS ++ subsample type: ++ ++ type | PAL | NTSC ++ --------------------------- ++ SIF | 352x288 | 352x240 ++ 1/2 D1 | 352x576 | 352x480 ++ 2/3 D1 | 480x576 | 480x480 ++ D1 | 720x576 | 720x480 ++ */ ++ ++ dist_352 = abs(f->width - 352); ++ dist_480 = abs(f->width - 480); ++ dist_720 = abs(f->width - 720); ++ if (dist_720 < dist_480) { ++ f->width = 720; ++ f->height = 576; ++ h->video_format = SAA6752HS_VF_D1; ++ } else if (dist_480 < dist_352) { ++ f->width = 480; ++ f->height = 576; ++ h->video_format = SAA6752HS_VF_2_3_D1; ++ } else { ++ f->width = 352; ++ if (abs(f->height - 576) < ++ abs(f->height - 288)) { ++ f->height = 576; ++ h->video_format = SAA6752HS_VF_1_2_D1; ++ } else { ++ f->height = 288; ++ h->video_format = SAA6752HS_VF_SIF; ++ } ++ } ++ f->field = V4L2_FIELD_INTERLACED; ++ f->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ++{ ++ struct saa6752hs_state *h = to_state(sd); ++ ++ h->standard = std; ++ return 0; ++} ++ ++static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct saa6752hs_state *h = to_state(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, ++ chip, h->chip, h->revision); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { ++ .g_chip_ident = saa6752hs_g_chip_ident, ++ .init = saa6752hs_init, ++ .queryctrl = saa6752hs_queryctrl, ++ .querymenu = saa6752hs_querymenu, ++ .g_ext_ctrls = saa6752hs_g_ext_ctrls, ++ .s_ext_ctrls = saa6752hs_s_ext_ctrls, ++ .try_ext_ctrls = saa6752hs_try_ext_ctrls, ++ .s_std = saa6752hs_s_std, ++}; ++ ++static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { ++ .s_mbus_fmt = saa6752hs_s_mbus_fmt, ++ .g_mbus_fmt = saa6752hs_g_mbus_fmt, ++}; ++ ++static const struct v4l2_subdev_ops saa6752hs_ops = { ++ .core = &saa6752hs_core_ops, ++ .video = &saa6752hs_video_ops, ++}; ++ ++static int saa6752hs_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); ++ struct v4l2_subdev *sd; ++ u8 addr = 0x13; ++ u8 data[12]; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ if (h == NULL) ++ return -ENOMEM; ++ sd = &h->sd; ++ v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); ++ ++ i2c_master_send(client, &addr, 1); ++ i2c_master_recv(client, data, sizeof(data)); ++ h->chip = V4L2_IDENT_SAA6752HS; ++ h->revision = (data[8] << 8) | data[9]; ++ h->has_ac3 = 0; ++ if (h->revision == 0x0206) { ++ h->chip = V4L2_IDENT_SAA6752HS_AC3; ++ h->has_ac3 = 1; ++ v4l_info(client, "support AC-3\n"); ++ } ++ h->params = param_defaults; ++ h->standard = 0; /* Assume 625 input lines */ ++ return 0; ++} ++ ++static int saa6752hs_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_state(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id saa6752hs_id[] = { ++ { "saa6752hs", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, saa6752hs_id); ++ ++static struct i2c_driver saa6752hs_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "saa6752hs", ++ }, ++ .probe = saa6752hs_probe, ++ .remove = saa6752hs_remove, ++ .id_table = saa6752hs_id, ++}; ++ ++module_i2c_driver(saa6752hs_driver); ++ ++/* ++ * Overrides for Emacs so that we follow Linus's tabbing style. ++ * --------------------------------------------------------------------------- ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c +new file mode 100644 +index 0000000..10460fd +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-alsa.c +@@ -0,0 +1,1209 @@ ++/* ++ * SAA713x ALSA support for V4L ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, version 2 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134.h" ++#include "saa7134-reg.h" ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); ++ ++/* ++ * Configuration macros ++ */ ++ ++/* defaults */ ++#define MIXER_ADDR_UNSELECTED -1 ++#define MIXER_ADDR_TVTUNER 0 ++#define MIXER_ADDR_LINE1 1 ++#define MIXER_ADDR_LINE2 2 ++#define MIXER_ADDR_LAST 2 ++ ++ ++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ ++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ ++static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; ++ ++module_param_array(index, int, NULL, 0444); ++module_param_array(enable, int, NULL, 0444); ++MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s)."); ++MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); ++ ++#define dprintk(fmt, arg...) if (debug) \ ++ printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg) ++ ++ ++ ++/* ++ * Main chip structure ++ */ ++ ++typedef struct snd_card_saa7134 { ++ struct snd_card *card; ++ spinlock_t mixer_lock; ++ int mixer_volume[MIXER_ADDR_LAST+1][2]; ++ int capture_source_addr; ++ int capture_source[2]; ++ struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1]; ++ struct pci_dev *pci; ++ struct saa7134_dev *dev; ++ ++ unsigned long iobase; ++ s16 irq; ++ u16 mute_was_on; ++ ++ spinlock_t lock; ++} snd_card_saa7134_t; ++ ++ ++/* ++ * PCM structure ++ */ ++ ++typedef struct snd_card_saa7134_pcm { ++ struct saa7134_dev *dev; ++ ++ spinlock_t lock; ++ ++ struct snd_pcm_substream *substream; ++} snd_card_saa7134_pcm_t; ++ ++static struct snd_card *snd_saa7134_cards[SNDRV_CARDS]; ++ ++ ++/* ++ * saa7134 DMA audio stop ++ * ++ * Called when the capture device is released or the buffer overflows ++ * ++ * - Copied verbatim from saa7134-oss's dsp_dma_stop. ++ * ++ */ ++ ++static void saa7134_dma_stop(struct saa7134_dev *dev) ++{ ++ dev->dmasound.dma_blk = -1; ++ dev->dmasound.dma_running = 0; ++ saa7134_set_dmabits(dev); ++} ++ ++/* ++ * saa7134 DMA audio start ++ * ++ * Called when preparing the capture device for use ++ * ++ * - Copied verbatim from saa7134-oss's dsp_dma_start. ++ * ++ */ ++ ++static void saa7134_dma_start(struct saa7134_dev *dev) ++{ ++ dev->dmasound.dma_blk = 0; ++ dev->dmasound.dma_running = 1; ++ saa7134_set_dmabits(dev); ++} ++ ++/* ++ * saa7134 audio DMA IRQ handler ++ * ++ * Called whenever we get an SAA7134_IRQ_REPORT_DONE_RA3 interrupt ++ * Handles shifting between the 2 buffers, manages the read counters, ++ * and notifies ALSA when periods elapse ++ * ++ * - Mostly copied from saa7134-oss's saa7134_irq_oss_done. ++ * ++ */ ++ ++static void saa7134_irq_alsa_done(struct saa7134_dev *dev, ++ unsigned long status) ++{ ++ int next_blk, reg = 0; ++ ++ spin_lock(&dev->slock); ++ if (UNSET == dev->dmasound.dma_blk) { ++ dprintk("irq: recording stopped\n"); ++ goto done; ++ } ++ if (0 != (status & 0x0f000000)) ++ dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); ++ if (0 == (status & 0x10000000)) { ++ /* odd */ ++ if (0 == (dev->dmasound.dma_blk & 0x01)) ++ reg = SAA7134_RS_BA1(6); ++ } else { ++ /* even */ ++ if (1 == (dev->dmasound.dma_blk & 0x01)) ++ reg = SAA7134_RS_BA2(6); ++ } ++ if (0 == reg) { ++ dprintk("irq: field oops [%s]\n", ++ (status & 0x10000000) ? "even" : "odd"); ++ goto done; ++ } ++ ++ if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { ++ dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count, ++ dev->dmasound.bufsize, dev->dmasound.blocks); ++ spin_unlock(&dev->slock); ++ snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN); ++ return; ++ } ++ ++ /* next block addr */ ++ next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; ++ saa_writel(reg,next_blk * dev->dmasound.blksize); ++ if (debug > 2) ++ dprintk("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n", ++ (status & 0x10000000) ? "even" : "odd ", next_blk, ++ next_blk * dev->dmasound.blksize, dev->dmasound.blocks, dev->dmasound.blksize, dev->dmasound.read_count); ++ ++ /* update status & wake waiting readers */ ++ dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks; ++ dev->dmasound.read_count += dev->dmasound.blksize; ++ ++ dev->dmasound.recording_on = reg; ++ ++ if (dev->dmasound.read_count >= snd_pcm_lib_period_bytes(dev->dmasound.substream)) { ++ spin_unlock(&dev->slock); ++ snd_pcm_period_elapsed(dev->dmasound.substream); ++ spin_lock(&dev->slock); ++ } ++ ++ done: ++ spin_unlock(&dev->slock); ++ ++} ++ ++/* ++ * IRQ request handler ++ * ++ * Runs along with saa7134's IRQ handler, discards anything that isn't ++ * DMA sound ++ * ++ */ ++ ++static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id) ++{ ++ struct saa7134_dmasound *dmasound = dev_id; ++ struct saa7134_dev *dev = dmasound->priv_data; ++ ++ unsigned long report, status; ++ int loop, handled = 0; ++ ++ for (loop = 0; loop < 10; loop++) { ++ report = saa_readl(SAA7134_IRQ_REPORT); ++ status = saa_readl(SAA7134_IRQ_STATUS); ++ ++ if (report & SAA7134_IRQ_REPORT_DONE_RA3) { ++ handled = 1; ++ saa_writel(SAA7134_IRQ_REPORT, ++ SAA7134_IRQ_REPORT_DONE_RA3); ++ saa7134_irq_alsa_done(dev, status); ++ } else { ++ goto out; ++ } ++ } ++ ++ if (loop == 10) { ++ dprintk("error! looping IRQ!"); ++ } ++ ++out: ++ return IRQ_RETVAL(handled); ++} ++ ++/* ++ * ALSA capture trigger ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called whenever a capture is started or stopped. Must be defined, ++ * but there's nothing we want to do here ++ * ++ */ ++ ++static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream, ++ int cmd) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_card_saa7134_pcm_t *pcm = runtime->private_data; ++ struct saa7134_dev *dev=pcm->dev; ++ int err = 0; ++ ++ spin_lock(&dev->slock); ++ if (cmd == SNDRV_PCM_TRIGGER_START) { ++ /* start dma */ ++ saa7134_dma_start(dev); ++ } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { ++ /* stop dma */ ++ saa7134_dma_stop(dev); ++ } else { ++ err = -EINVAL; ++ } ++ spin_unlock(&dev->slock); ++ ++ return err; ++} ++ ++/* ++ * DMA buffer initialization ++ * ++ * Uses V4L functions to initialize the DMA. Shouldn't be necessary in ++ * ALSA, but I was unable to use ALSA's own DMA, and had to force the ++ * usage of V4L's ++ * ++ * - Copied verbatim from saa7134-oss. ++ * ++ */ ++ ++static int dsp_buffer_init(struct saa7134_dev *dev) ++{ ++ int err; ++ ++ BUG_ON(!dev->dmasound.bufsize); ++ ++ videobuf_dma_init(&dev->dmasound.dma); ++ err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE, ++ (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); ++ if (0 != err) ++ return err; ++ return 0; ++} ++ ++/* ++ * DMA buffer release ++ * ++ * Called after closing the device, during snd_card_saa7134_capture_close ++ * ++ */ ++ ++static int dsp_buffer_free(struct saa7134_dev *dev) ++{ ++ BUG_ON(!dev->dmasound.blksize); ++ ++ videobuf_dma_free(&dev->dmasound.dma); ++ ++ dev->dmasound.blocks = 0; ++ dev->dmasound.blksize = 0; ++ dev->dmasound.bufsize = 0; ++ ++ return 0; ++} ++ ++/* ++ * Setting the capture source and updating the ALSA controls ++ */ ++static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol, ++ int left, int right, bool force_notify) ++{ ++ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); ++ int change = 0, addr = kcontrol->private_value; ++ int active, old_addr; ++ u32 anabar, xbarin; ++ int analog_io, rate; ++ struct saa7134_dev *dev; ++ ++ dev = chip->dev; ++ ++ spin_lock_irq(&chip->mixer_lock); ++ ++ active = left != 0 || right != 0; ++ old_addr = chip->capture_source_addr; ++ ++ /* The active capture source cannot be deactivated */ ++ if (active) { ++ change = old_addr != addr || ++ chip->capture_source[0] != left || ++ chip->capture_source[1] != right; ++ ++ chip->capture_source[0] = left; ++ chip->capture_source[1] = right; ++ chip->capture_source_addr = addr; ++ dev->dmasound.input = addr; ++ } ++ spin_unlock_irq(&chip->mixer_lock); ++ ++ if (change) { ++ switch (dev->pci->device) { ++ ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ switch (addr) { ++ case MIXER_ADDR_TVTUNER: ++ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, ++ 0xc0, 0xc0); ++ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, ++ 0x03, 0x00); ++ break; ++ case MIXER_ADDR_LINE1: ++ case MIXER_ADDR_LINE2: ++ analog_io = (MIXER_ADDR_LINE1 == addr) ? ++ 0x00 : 0x08; ++ rate = (32000 == dev->dmasound.rate) ? ++ 0x01 : 0x03; ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, ++ 0x08, analog_io); ++ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, ++ 0xc0, 0x80); ++ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, ++ 0x03, rate); ++ break; ++ } ++ ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ xbarin = 0x03; /* adc */ ++ anabar = 0; ++ switch (addr) { ++ case MIXER_ADDR_TVTUNER: ++ xbarin = 0; /* Demodulator */ ++ anabar = 2; /* DACs */ ++ break; ++ case MIXER_ADDR_LINE1: ++ anabar = 0; /* aux1, aux1 */ ++ break; ++ case MIXER_ADDR_LINE2: ++ anabar = 9; /* aux2, aux2 */ ++ break; ++ } ++ ++ /* output xbar always main channel */ ++ saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, ++ 0xbbbb10); ++ ++ if (left || right) { ++ /* We've got data, turn the input on */ ++ saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, ++ xbarin); ++ saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); ++ } else { ++ saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, ++ 0); ++ saa_writel(SAA7133_ANALOG_IO_SELECT, 0); ++ } ++ break; ++ } ++ } ++ ++ if (change) { ++ if (force_notify) ++ snd_ctl_notify(chip->card, ++ SNDRV_CTL_EVENT_MASK_VALUE, ++ &chip->capture_ctl[addr]->id); ++ ++ if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr) ++ snd_ctl_notify(chip->card, ++ SNDRV_CTL_EVENT_MASK_VALUE, ++ &chip->capture_ctl[old_addr]->id); ++ } ++ ++ return change; ++} ++ ++/* ++ * ALSA PCM preparation ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called right after the capture device is opened, this function configures ++ * the buffer using the previously defined functions, allocates the memory, ++ * sets up the hardware registers, and then starts the DMA. When this function ++ * returns, the audio should be flowing. ++ * ++ */ ++ ++static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int bswap, sign; ++ u32 fmt, control; ++ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); ++ struct saa7134_dev *dev; ++ snd_card_saa7134_pcm_t *pcm = runtime->private_data; ++ ++ pcm->dev->dmasound.substream = substream; ++ ++ dev = saa7134->dev; ++ ++ if (snd_pcm_format_width(runtime->format) == 8) ++ fmt = 0x00; ++ else ++ fmt = 0x01; ++ ++ if (snd_pcm_format_signed(runtime->format)) ++ sign = 1; ++ else ++ sign = 0; ++ ++ if (snd_pcm_format_big_endian(runtime->format)) ++ bswap = 1; ++ else ++ bswap = 0; ++ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ if (1 == runtime->channels) ++ fmt |= (1 << 3); ++ if (2 == runtime->channels) ++ fmt |= (3 << 3); ++ if (sign) ++ fmt |= 0x04; ++ ++ fmt |= (MIXER_ADDR_TVTUNER == dev->dmasound.input) ? 0xc0 : 0x80; ++ saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff)); ++ saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8); ++ saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16); ++ saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); ++ ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ if (1 == runtime->channels) ++ fmt |= (1 << 4); ++ if (2 == runtime->channels) ++ fmt |= (2 << 4); ++ if (!sign) ++ fmt |= 0x04; ++ saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1); ++ saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24)); ++ break; ++ } ++ ++ dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", ++ runtime->format, runtime->channels, fmt, ++ bswap ? 'b' : '-'); ++ /* dma: setup channel 6 (= AUDIO) */ ++ control = SAA7134_RS_CONTROL_BURST_16 | ++ SAA7134_RS_CONTROL_ME | ++ (dev->dmasound.pt.dma >> 12); ++ if (bswap) ++ control |= SAA7134_RS_CONTROL_BSWAP; ++ ++ saa_writel(SAA7134_RS_BA1(6),0); ++ saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize); ++ saa_writel(SAA7134_RS_PITCH(6),0); ++ saa_writel(SAA7134_RS_CONTROL(6),control); ++ ++ dev->dmasound.rate = runtime->rate; ++ ++ /* Setup and update the card/ALSA controls */ ++ snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1, ++ true); ++ ++ return 0; ++ ++} ++ ++/* ++ * ALSA pointer fetching ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called whenever a period elapses, it must return the current hardware ++ * position of the buffer. ++ * Also resets the read counter used to prevent overruns ++ * ++ */ ++ ++static snd_pcm_uframes_t ++snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_card_saa7134_pcm_t *pcm = runtime->private_data; ++ struct saa7134_dev *dev=pcm->dev; ++ ++ if (dev->dmasound.read_count) { ++ dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream); ++ dev->dmasound.read_offset += snd_pcm_lib_period_bytes(substream); ++ if (dev->dmasound.read_offset == dev->dmasound.bufsize) ++ dev->dmasound.read_offset = 0; ++ } ++ ++ return bytes_to_frames(runtime, dev->dmasound.read_offset); ++} ++ ++/* ++ * ALSA hardware capabilities definition ++ * ++ * Report only 32kHz for ALSA: ++ * ++ * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz ++ * only ++ * - SAA7134 for TV mode uses DemDec mode (32kHz) ++ * - Radio works in 32kHz only ++ * - When recording 48kHz from Line1/Line2, switching of capture source to TV ++ * means ++ * switching to 32kHz without any frequency translation ++ */ ++ ++static struct snd_pcm_hardware snd_card_saa7134_capture = ++{ ++ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_BLOCK_TRANSFER | ++ SNDRV_PCM_INFO_MMAP_VALID), ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ ++ SNDRV_PCM_FMTBIT_S16_BE | \ ++ SNDRV_PCM_FMTBIT_S8 | \ ++ SNDRV_PCM_FMTBIT_U8 | \ ++ SNDRV_PCM_FMTBIT_U16_LE | \ ++ SNDRV_PCM_FMTBIT_U16_BE, ++ .rates = SNDRV_PCM_RATE_32000, ++ .rate_min = 32000, ++ .rate_max = 32000, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = (256*1024), ++ .period_bytes_min = 64, ++ .period_bytes_max = (256*1024), ++ .periods_min = 4, ++ .periods_max = 1024, ++}; ++ ++static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) ++{ ++ snd_card_saa7134_pcm_t *pcm = runtime->private_data; ++ ++ kfree(pcm); ++} ++ ++ ++/* ++ * ALSA hardware params ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called on initialization, right before the PCM preparation ++ * ++ */ ++ ++static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, ++ struct snd_pcm_hw_params * hw_params) ++{ ++ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); ++ struct saa7134_dev *dev; ++ unsigned int period_size, periods; ++ int err; ++ ++ period_size = params_period_bytes(hw_params); ++ periods = params_periods(hw_params); ++ ++ if (period_size < 0x100 || period_size > 0x10000) ++ return -EINVAL; ++ if (periods < 4) ++ return -EINVAL; ++ if (period_size * periods > 1024 * 1024) ++ return -EINVAL; ++ ++ dev = saa7134->dev; ++ ++ if (dev->dmasound.blocks == periods && ++ dev->dmasound.blksize == period_size) ++ return 0; ++ ++ /* release the old buffer */ ++ if (substream->runtime->dma_area) { ++ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); ++ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); ++ dsp_buffer_free(dev); ++ substream->runtime->dma_area = NULL; ++ } ++ dev->dmasound.blocks = periods; ++ dev->dmasound.blksize = period_size; ++ dev->dmasound.bufsize = period_size * periods; ++ ++ err = dsp_buffer_init(dev); ++ if (0 != err) { ++ dev->dmasound.blocks = 0; ++ dev->dmasound.blksize = 0; ++ dev->dmasound.bufsize = 0; ++ return err; ++ } ++ ++ if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { ++ dsp_buffer_free(dev); ++ return err; ++ } ++ if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { ++ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); ++ dsp_buffer_free(dev); ++ return err; ++ } ++ if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, ++ dev->dmasound.dma.sglist, ++ dev->dmasound.dma.sglen, ++ 0))) { ++ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); ++ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); ++ dsp_buffer_free(dev); ++ return err; ++ } ++ ++ /* I should be able to use runtime->dma_addr in the control ++ byte, but it doesn't work. So I allocate the DMA using the ++ V4L functions, and force ALSA to use that as the DMA area */ ++ ++ substream->runtime->dma_area = dev->dmasound.dma.vaddr; ++ substream->runtime->dma_bytes = dev->dmasound.bufsize; ++ substream->runtime->dma_addr = 0; ++ ++ return 0; ++ ++} ++ ++/* ++ * ALSA hardware release ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called after closing the device, but before snd_card_saa7134_capture_close ++ * It stops the DMA audio and releases the buffers. ++ * ++ */ ++ ++static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) ++{ ++ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); ++ struct saa7134_dev *dev; ++ ++ dev = saa7134->dev; ++ ++ if (substream->runtime->dma_area) { ++ saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); ++ videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); ++ dsp_buffer_free(dev); ++ substream->runtime->dma_area = NULL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * ALSA capture finish ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called after closing the device. ++ * ++ */ ++ ++static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) ++{ ++ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); ++ struct saa7134_dev *dev = saa7134->dev; ++ ++ if (saa7134->mute_was_on) { ++ dev->ctl_mute = 1; ++ saa7134_tvaudio_setmute(dev); ++ } ++ return 0; ++} ++ ++/* ++ * ALSA capture start ++ * ++ * - One of the ALSA capture callbacks. ++ * ++ * Called when opening the device. It creates and populates the PCM ++ * structure ++ * ++ */ ++ ++static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ snd_card_saa7134_pcm_t *pcm; ++ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); ++ struct saa7134_dev *dev; ++ int amux, err; ++ ++ if (!saa7134) { ++ printk(KERN_ERR "BUG: saa7134 can't find device struct." ++ " Can't proceed with open\n"); ++ return -ENODEV; ++ } ++ dev = saa7134->dev; ++ mutex_lock(&dev->dmasound.lock); ++ ++ dev->dmasound.read_count = 0; ++ dev->dmasound.read_offset = 0; ++ ++ amux = dev->input->amux; ++ if ((amux < 1) || (amux > 3)) ++ amux = 1; ++ dev->dmasound.input = amux - 1; ++ ++ mutex_unlock(&dev->dmasound.lock); ++ ++ pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); ++ if (pcm == NULL) ++ return -ENOMEM; ++ ++ pcm->dev=saa7134->dev; ++ ++ spin_lock_init(&pcm->lock); ++ ++ pcm->substream = substream; ++ runtime->private_data = pcm; ++ runtime->private_free = snd_card_saa7134_runtime_free; ++ runtime->hw = snd_card_saa7134_capture; ++ ++ if (dev->ctl_mute != 0) { ++ saa7134->mute_was_on = 1; ++ dev->ctl_mute = 0; ++ saa7134_tvaudio_setmute(dev); ++ } ++ ++ err = snd_pcm_hw_constraint_integer(runtime, ++ SNDRV_PCM_HW_PARAM_PERIODS); ++ if (err < 0) ++ return err; ++ ++ err = snd_pcm_hw_constraint_step(runtime, 0, ++ SNDRV_PCM_HW_PARAM_PERIODS, 2); ++ if (err < 0) ++ return err; ++ ++ return 0; ++} ++ ++/* ++ * page callback (needed for mmap) ++ */ ++ ++static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream, ++ unsigned long offset) ++{ ++ void *pageptr = substream->runtime->dma_area + offset; ++ return vmalloc_to_page(pageptr); ++} ++ ++/* ++ * ALSA capture callbacks definition ++ */ ++ ++static struct snd_pcm_ops snd_card_saa7134_capture_ops = { ++ .open = snd_card_saa7134_capture_open, ++ .close = snd_card_saa7134_capture_close, ++ .ioctl = snd_pcm_lib_ioctl, ++ .hw_params = snd_card_saa7134_hw_params, ++ .hw_free = snd_card_saa7134_hw_free, ++ .prepare = snd_card_saa7134_capture_prepare, ++ .trigger = snd_card_saa7134_capture_trigger, ++ .pointer = snd_card_saa7134_capture_pointer, ++ .page = snd_card_saa7134_page, ++}; ++ ++/* ++ * ALSA PCM setup ++ * ++ * Called when initializing the board. Sets up the name and hooks up ++ * the callbacks ++ * ++ */ ++ ++static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0) ++ return err; ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_saa7134_capture_ops); ++ pcm->private_data = saa7134; ++ pcm->info_flags = 0; ++ strcpy(pcm->name, "SAA7134 PCM"); ++ return 0; ++} ++ ++#define SAA713x_VOLUME(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ ++ .info = snd_saa7134_volume_info, \ ++ .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \ ++ .private_value = addr } ++ ++static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_info * uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 2; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 20; ++ return 0; ++} ++ ++static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_value * ucontrol) ++{ ++ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); ++ int addr = kcontrol->private_value; ++ ++ ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; ++ ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; ++ return 0; ++} ++ ++static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_value * ucontrol) ++{ ++ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); ++ struct saa7134_dev *dev = chip->dev; ++ ++ int change, addr = kcontrol->private_value; ++ int left, right; ++ ++ left = ucontrol->value.integer.value[0]; ++ if (left < 0) ++ left = 0; ++ if (left > 20) ++ left = 20; ++ right = ucontrol->value.integer.value[1]; ++ if (right < 0) ++ right = 0; ++ if (right > 20) ++ right = 20; ++ spin_lock_irq(&chip->mixer_lock); ++ change = 0; ++ if (chip->mixer_volume[addr][0] != left) { ++ change = 1; ++ right = left; ++ } ++ if (chip->mixer_volume[addr][1] != right) { ++ change = 1; ++ left = right; ++ } ++ if (change) { ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ switch (addr) { ++ case MIXER_ADDR_TVTUNER: ++ left = 20; ++ break; ++ case MIXER_ADDR_LINE1: ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, ++ (left > 10) ? 0x00 : 0x10); ++ break; ++ case MIXER_ADDR_LINE2: ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, ++ (left > 10) ? 0x00 : 0x20); ++ break; ++ } ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ switch (addr) { ++ case MIXER_ADDR_TVTUNER: ++ left = 20; ++ break; ++ case MIXER_ADDR_LINE1: ++ saa_andorb(0x0594, 0x10, ++ (left > 10) ? 0x00 : 0x10); ++ break; ++ case MIXER_ADDR_LINE2: ++ saa_andorb(0x0594, 0x20, ++ (left > 10) ? 0x00 : 0x20); ++ break; ++ } ++ break; ++ } ++ chip->mixer_volume[addr][0] = left; ++ chip->mixer_volume[addr][1] = right; ++ } ++ spin_unlock_irq(&chip->mixer_lock); ++ return change; ++} ++ ++#define SAA713x_CAPSRC(xname, xindex, addr) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ ++ .info = snd_saa7134_capsrc_info, \ ++ .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \ ++ .private_value = addr } ++ ++static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_info * uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ uinfo->count = 2; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 1; ++ return 0; ++} ++ ++static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_value * ucontrol) ++{ ++ snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); ++ int addr = kcontrol->private_value; ++ ++ spin_lock_irq(&chip->mixer_lock); ++ if (chip->capture_source_addr == addr) { ++ ucontrol->value.integer.value[0] = chip->capture_source[0]; ++ ucontrol->value.integer.value[1] = chip->capture_source[1]; ++ } else { ++ ucontrol->value.integer.value[0] = 0; ++ ucontrol->value.integer.value[1] = 0; ++ } ++ spin_unlock_irq(&chip->mixer_lock); ++ ++ return 0; ++} ++ ++static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, ++ struct snd_ctl_elem_value * ucontrol) ++{ ++ int left, right; ++ left = ucontrol->value.integer.value[0] & 1; ++ right = ucontrol->value.integer.value[1] & 1; ++ ++ return snd_saa7134_capsrc_set(kcontrol, left, right, false); ++} ++ ++static struct snd_kcontrol_new snd_saa7134_volume_controls[] = { ++SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), ++SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), ++SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2), ++}; ++ ++static struct snd_kcontrol_new snd_saa7134_capture_controls[] = { ++SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), ++SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), ++SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), ++}; ++ ++/* ++ * ALSA mixer setup ++ * ++ * Called when initializing the board. Sets up the name and hooks up ++ * the callbacks ++ * ++ */ ++ ++static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) ++{ ++ struct snd_card *card = chip->card; ++ struct snd_kcontrol *kcontrol; ++ unsigned int idx; ++ int err, addr; ++ ++ strcpy(card->mixername, "SAA7134 Mixer"); ++ ++ for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { ++ kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx], ++ chip); ++ err = snd_ctl_add(card, kcontrol); ++ if (err < 0) ++ return err; ++ } ++ ++ for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) { ++ kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx], ++ chip); ++ addr = snd_saa7134_capture_controls[idx].private_value; ++ chip->capture_ctl[addr] = kcontrol; ++ err = snd_ctl_add(card, kcontrol); ++ if (err < 0) ++ return err; ++ } ++ ++ chip->capture_source_addr = MIXER_ADDR_UNSELECTED; ++ return 0; ++} ++ ++static void snd_saa7134_free(struct snd_card * card) ++{ ++ snd_card_saa7134_t *chip = card->private_data; ++ ++ if (chip->dev->dmasound.priv_data == NULL) ++ return; ++ ++ if (chip->irq >= 0) ++ free_irq(chip->irq, &chip->dev->dmasound); ++ ++ chip->dev->dmasound.priv_data = NULL; ++ ++} ++ ++/* ++ * ALSA initialization ++ * ++ * Called by the init routine, once for each saa7134 device present, ++ * it creates the basic structures and registers the ALSA devices ++ * ++ */ ++ ++static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) ++{ ++ ++ struct snd_card *card; ++ snd_card_saa7134_t *chip; ++ int err; ++ ++ ++ if (devnum >= SNDRV_CARDS) ++ return -ENODEV; ++ if (!enable[devnum]) ++ return -ENODEV; ++ ++ err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, ++ sizeof(snd_card_saa7134_t), &card); ++ if (err < 0) ++ return err; ++ ++ strcpy(card->driver, "SAA7134"); ++ ++ /* Card "creation" */ ++ ++ card->private_free = snd_saa7134_free; ++ chip = card->private_data; ++ ++ spin_lock_init(&chip->lock); ++ spin_lock_init(&chip->mixer_lock); ++ ++ chip->dev = dev; ++ ++ chip->card = card; ++ ++ chip->pci = dev->pci; ++ chip->iobase = pci_resource_start(dev->pci, 0); ++ ++ ++ err = request_irq(dev->pci->irq, saa7134_alsa_irq, ++ IRQF_SHARED | IRQF_DISABLED, dev->name, ++ (void*) &dev->dmasound); ++ ++ if (err < 0) { ++ printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n", ++ dev->name, dev->pci->irq); ++ goto __nodev; ++ } ++ ++ chip->irq = dev->pci->irq; ++ ++ mutex_init(&dev->dmasound.lock); ++ ++ if ((err = snd_card_saa7134_new_mixer(chip)) < 0) ++ goto __nodev; ++ ++ if ((err = snd_card_saa7134_pcm(chip, 0)) < 0) ++ goto __nodev; ++ ++ snd_card_set_dev(card, &chip->pci->dev); ++ ++ /* End of "creation" */ ++ ++ strcpy(card->shortname, "SAA7134"); ++ sprintf(card->longname, "%s at 0x%lx irq %d", ++ chip->dev->name, chip->iobase, chip->irq); ++ ++ printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]); ++ ++ if ((err = snd_card_register(card)) == 0) { ++ snd_saa7134_cards[devnum] = card; ++ return 0; ++ } ++ ++__nodev: ++ snd_card_free(card); ++ return err; ++} ++ ++ ++static int alsa_device_init(struct saa7134_dev *dev) ++{ ++ dev->dmasound.priv_data = dev; ++ alsa_card_saa7134_create(dev,dev->nr); ++ return 1; ++} ++ ++static int alsa_device_exit(struct saa7134_dev *dev) ++{ ++ ++ snd_card_free(snd_saa7134_cards[dev->nr]); ++ snd_saa7134_cards[dev->nr] = NULL; ++ return 1; ++} ++ ++/* ++ * Module initializer ++ * ++ * Loops through present saa7134 cards, and assigns an ALSA device ++ * to each one ++ * ++ */ ++ ++static int saa7134_alsa_init(void) ++{ ++ struct saa7134_dev *dev = NULL; ++ struct list_head *list; ++ ++ saa7134_dmasound_init = alsa_device_init; ++ saa7134_dmasound_exit = alsa_device_exit; ++ ++ printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n"); ++ ++ list_for_each(list,&saa7134_devlist) { ++ dev = list_entry(list, struct saa7134_dev, devlist); ++ if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) ++ printk(KERN_INFO "%s/alsa: %s doesn't support digital audio\n", ++ dev->name, saa7134_boards[dev->board].name); ++ else ++ alsa_device_init(dev); ++ } ++ ++ if (dev == NULL) ++ printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n"); ++ ++ return 0; ++ ++} ++ ++/* ++ * Module destructor ++ */ ++ ++static void saa7134_alsa_exit(void) ++{ ++ int idx; ++ ++ for (idx = 0; idx < SNDRV_CARDS; idx++) { ++ snd_card_free(snd_saa7134_cards[idx]); ++ } ++ ++ saa7134_dmasound_init = NULL; ++ saa7134_dmasound_exit = NULL; ++ printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n"); ++ ++ return; ++} ++ ++/* We initialize this late, to make sure the sound system is up and running */ ++late_initcall(saa7134_alsa_init); ++module_exit(saa7134_alsa_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ricardo Cerqueira"); +diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c +new file mode 100644 +index 0000000..bc08f1d +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-cards.c +@@ -0,0 +1,8026 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * card-specific stuff. ++ * ++ * (c) 2001-04 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++#include "tuner-xc2028.h" ++#include ++#include ++#include "tea5767.h" ++#include "tda18271.h" ++#include "xc5000.h" ++#include "s5h1411.h" ++ ++/* commly used strings */ ++static char name_mute[] = "mute"; ++static char name_radio[] = "Radio"; ++static char name_tv[] = "Television"; ++static char name_tv_mono[] = "TV (mono only)"; ++static char name_comp[] = "Composite"; ++static char name_comp1[] = "Composite1"; ++static char name_comp2[] = "Composite2"; ++static char name_comp3[] = "Composite3"; ++static char name_comp4[] = "Composite4"; ++static char name_svideo[] = "S-Video"; ++ ++/* ------------------------------------------------------------------ */ ++/* board config info */ ++ ++/* If radio_type !=UNSET, radio_addr should be specified ++ */ ++ ++struct saa7134_board saa7134_boards[] = { ++ [SAA7134_BOARD_UNKNOWN] = { ++ .name = "UNKNOWN/GENERIC", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = "default", ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_PROTEUS_PRO] = { ++ /* /me */ ++ .name = "Proteus Pro [philips reference design]", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_FLYVIDEO3000] = { ++ /* "Marco d'Itri" */ ++ .name = "LifeView FlyVIDEO3000", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .gpiomask = 0xe000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x8000, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x2000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x8000, ++ }, ++ }, ++ [SAA7134_BOARD_FLYVIDEO2000] = { ++ /* "TC Wan" */ ++ .name = "LifeView/Typhoon FlyVIDEO2000", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .gpiomask = 0xe000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x2000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x8000, ++ }, ++ }, ++ [SAA7134_BOARD_FLYTVPLATINUM_MINI] = { ++ /* "Arnaud Quette" */ ++ .name = "LifeView FlyTV Platinum Mini", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_FLYTVPLATINUM_FM] = { ++ /* LifeView FlyTV Platinum FM (LR214WF) */ ++ /* "Peter Missel */ ++ .name = "LifeView FlyTV Platinum FM / Gold", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .gpiomask = 0x1E000, /* Set GP16 and unused 15,14,13 to Output */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x10000, /* GP16=1 selects TV input */ ++ .tv = 1, ++ },{ ++/* .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++*/ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++/* .gpio = 0x4000, */ ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++/* .gpio = 0x4000, */ ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++/* .gpio = 0x4000, */ ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00000, /* GP16=0 selects FM radio antenna */ ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x10000, ++ }, ++ }, ++ [SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = { ++ /* RoverMedia TV Link Pro FM (LR138 REV:I) */ ++ /* Eugene Yudin */ ++ .name = "RoverMedia TV Link Pro FM", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0xe000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x8000, ++ .tv = 1, ++ }, { ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }, { ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x2000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x8000, ++ }, ++ }, ++ [SAA7134_BOARD_EMPRESS] = { ++ /* "Gert Vervoort" */ ++ .name = "EMPRESS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ }, ++ [SAA7134_BOARD_MONSTERTV] = { ++ /* "K.Ohta" */ ++ .name = "SKNet Monster TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_MD9717] = { ++ .name = "Tevion MD 9717", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ /* workaround for problems with normal TV sound */ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_TVSTATION_RDS] = { ++ /* Typhoon TV Tuner RDS: Art.Nr. 50694 */ ++ .name = "KNC One TV-Station RDS / Typhoon TV Tuner RDS", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ ++ .name = "CVid over SVid", ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_TVSTATION_DVR] = { ++ .name = "KNC One TV-Station DVR", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x820000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x20000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x20000, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x20000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x20000, ++ }, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ }, ++ [SAA7134_BOARD_CINERGY400] = { ++ .name = "Terratec Cinergy 400 TV", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, /* CVideo over SVideo Connector */ ++ .vmux = 0, ++ .amux = LINE1, ++ }} ++ }, ++ [SAA7134_BOARD_MD5044] = { ++ .name = "Medion 5044", ++ .audio_clock = 0x00187de7, /* was: 0x00200000, */ ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ /* workaround for problems with normal TV sound */ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD] = { ++ .name = "Kworld/KuroutoShikou SAA7130-TVPCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_CINERGY600] = { ++ .name = "Terratec Cinergy 600 TV", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, /* CVideo over SVideo Connector */ ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_MD7134] = { ++ .name = "Medion 7134", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_TYPHOON_90031] = { ++ /* aka Typhoon "TV+Radio", Art.Nr 90031 */ ++ /* Tom Zoerner */ ++ .name = "Typhoon TV+Radio 90031", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ELSA] = { ++ .name = "ELSA EX-VISION 300TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_HITACHI_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 4, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_ELSA_500TV] = { ++ .name = "ELSA EX-VISION 500TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_HITACHI_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 7, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 8, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 8, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_ELSA_700TV] = { ++ .name = "ELSA EX-VISION 700TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_HITACHI_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 4, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 6, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 7, ++ .amux = LINE1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_TVFM7134] = { ++ .name = "ASUS TV-FM 7134", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_TVFM7135] = { ++ .name = "ASUS TV-FM 7135", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x200000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .gpio = 0x0000, ++ }, ++ ++ }, ++ [SAA7134_BOARD_VA1000POWER] = { ++ .name = "AOPEN VA1000 POWER", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_10MOONSTVMASTER] = { ++ /* "lilicheng" */ ++ .name = "10MOONS PCI TV CAPTURE CARD", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0xe000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x2000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x8000, ++ }, ++ }, ++ [SAA7134_BOARD_BMK_MPEX_NOTUNER] = { ++ /* "Andrew de Quincey" */ ++ .name = "BMK MPEX No Tuner", ++ .audio_clock = 0x200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_comp3, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_comp4, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_TV] = { ++ .name = "Compro VideoMate TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = { ++ .name = "Compro VideoMate TV Gold+", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .gpiomask = 0x800c0000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x06c00012, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x0ac20012, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x08c20012, ++ .tv = 1, ++ }}, /* radio and probably mute is missing */ ++ }, ++ [SAA7134_BOARD_CRONOS_PLUS] = { ++ /* ++ gpio pins: ++ 0 .. 3 BASE_ID ++ 4 .. 7 PROTECT_ID ++ 8 .. 11 USER_OUT ++ 12 .. 13 USER_IN ++ 14 .. 15 VIDIN_SEL ++ */ ++ .name = "Matrox CronosPlus", ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0xcf00, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .gpio = 2 << 14, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .gpio = 1 << 14, ++ },{ ++ .name = name_comp3, ++ .vmux = 0, ++ .gpio = 0 << 14, ++ },{ ++ .name = name_comp4, ++ .vmux = 0, ++ .gpio = 3 << 14, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .gpio = 2 << 14, ++ }}, ++ }, ++ [SAA7134_BOARD_MD2819] = { ++ .name = "AverMedia M156 / Medion 2819", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x03, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x02, ++ }, { ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE1, ++ .gpio = 0x02, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x02, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x01, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x00, ++ }, ++ }, ++ [SAA7134_BOARD_BMK_MPEX_TUNER] = { ++ /* "Greg Wickham */ ++ .name = "BMK MPEX Tuner", ++ .audio_clock = 0x200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }}, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ }, ++ [SAA7134_BOARD_ASUSTEK_TVFM7133] = { ++ .name = "ASUS TV-FM 7133", ++ .audio_clock = 0x00187de7, ++ /* probably wrong, the 7133 one is the NTSC version ... ++ * .tuner_type = TUNER_PHILIPS_FM1236_MK3 */ ++ .tuner_type = TUNER_LG_NTSC_NEW_TAPC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_PINNACLE_PCTV_STEREO] = { ++ .name = "Pinnacle PCTV Stereo (saa7134)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_MT2032, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_MANLI_MTV002] = { ++ /* Ognjen Nastic */ ++ .name = "Manli MuchTV M-TV002", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_MANLI_MTV001] = { ++ /* Ognjen Nastic UNTESTED */ ++ .name = "Manli MuchTV M-TV001", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_TG3000TV] = { ++ /* TransGear 3000TV */ ++ .name = "Nagase Sangyo TransGear 3000TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_ECS_TVP3XP] = { ++ .name = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ", ++ .audio_clock = 0x187de7, /* xtal 32.1 MHz */ ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = "CVid over SVid", ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ECS_TVP3XP_4CB5] = { ++ .name = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = "CVid over SVid", ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ECS_TVP3XP_4CB6] = { ++ /* Barry Scott */ ++ .name = "Elitegroup ECS TVP3XP FM1246 Tuner Card (PAL,FM)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = "CVid over SVid", ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_AVACSSMARTTV] = { ++ /* Roman Pszonczenko */ ++ .name = "AVACS SmartTV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x200000, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = { ++ /* Michael Smith */ ++ .name = "AVerMedia DVD EZMaker", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 3, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_M103] = { ++ /* Massimo Piccioni */ ++ .name = "AVerMedia MiniPCI DVB-T Hybrid M103", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ } }, ++ }, ++ [SAA7134_BOARD_NOVAC_PRIMETV7133] = { ++ /* toshii@netbsd.org */ ++ .name = "Noval Prime TV 7133", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ALPS_TSBH1_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 3, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_STUDIO_305] = { ++ .name = "AverMedia AverTV Studio 305", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1256_IH3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_STUDIO_505] = { ++ /* Vasiliy Temnikov */ ++ .name = "AverMedia AverTV Studio 505", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_UPMOST_PURPLE_TV] = { ++ .name = "UPMOST PURPLE TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1236_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 7, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_svideo, ++ .vmux = 7, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_ITEMS_MTV005] = { ++ /* Norman Jonas */ ++ .name = "Items MuchTV Plus / IT-005", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_CINERGY200] = { ++ .name = "Terratec Cinergy 200 TV", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, /* CVideo over SVideo Connector */ ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_TV_PVR] = { ++ /* Alain St-Denis */ ++ .name = "Compro VideoMate TV PVR/FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x808c0080, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x00080, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x00080, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2_LEFT, ++ .tv = 1, ++ .gpio = 0x00080, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x80000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x40000, ++ }, ++ }, ++ [SAA7134_BOARD_SABRENT_SBTTVFM] = { ++ /* Michael Rodriguez-Torrent */ ++ .name = "Sabrent SBT-TVFM (saa7130)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC_M, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ZOLID_XPERT_TV7134] = { ++ /* Helge Jensen */ ++ .name = ":Zolid Xpert TV7134", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = { ++ /* "Matteo Az" ;-) */ ++ .name = "Empire PCI TV-Radio LE", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x4000, ++ .inputs = {{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x8000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x8000, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ .gpio = 0x8000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x8000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio =0x8000, ++ } ++ }, ++ [SAA7134_BOARD_AVERMEDIA_STUDIO_307] = { ++ /* ++ Nickolay V. Shmyrev ++ Lots of thanks to Andrey Zolotarev ++ */ ++ .name = "Avermedia AVerTV Studio 307", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1256_IH3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x03, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00, ++ },{ ++ .name = name_comp, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x02, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x02, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x01, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ .gpio = 0x00, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_GO_007_FM] = { ++ .name = "Avermedia AVerTV GO 007 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00300003, ++ /* .gpiomask = 0x8c240003, */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x01, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ .gpio = 0x02, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ .gpio = 0x02, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00300001, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x01, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_CARDBUS] = { ++ /* Kees.Blom@cwi.nl */ ++ .name = "AVerMedia Cardbus TV/Radio (E500)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_CARDBUS_501] = { ++ /* Oldrich Jedlicka */ ++ .name = "AVerMedia Cardbus TV/Radio (E501R)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_ALPS_TSBE5_PAL, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = 0x61, ++ .radio_addr = 0x60, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x08000000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x08000000, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x08000000, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x08000000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x00000000, ++ }, ++ }, ++ [SAA7134_BOARD_CINERGY400_CARDBUS] = { ++ .name = "Terratec Cinergy 400 mobile", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_ALPS_TSBE5_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_CINERGY600_MK3] = { ++ .name = "Terratec Cinergy 600 TV MK3", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, /* CVideo over SVideo Connector */ ++ .vmux = 0, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_GOLD_PLUS] = { ++ /* Dylan Walkden */ ++ .name = "Compro VideoMate Gold+ Pal", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x1ce780, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 0, /* CVideo over SVideo Connector - ok? */ ++ .amux = LINE1, ++ .gpio = 0x008080, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x008080, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x008080, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x80000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x0c8000, ++ }, ++ }, ++ [SAA7134_BOARD_PINNACLE_300I_DVBT_PAL] = { ++ .name = "Pinnacle PCTV 300i DVB-T + PAL", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_MT2032, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_PROVIDEO_PV952] = { ++ /* andreas.kretschmer@web.de */ ++ .name = "ProVideo PV952", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_305] = { ++ /* much like the "studio" version but without radio ++ * and another tuner (sirspiritus@yandex.ru) */ ++ .name = "AverMedia AverTV/305", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_FLYDVBTDUO] = { ++ /* LifeView FlyDVB-T DUO */ ++ /* "Nico Sabbi Hartmut Hackmann hartmut.hackmann@t-online.de*/ ++ .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00200000, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x200000, /* GPIO21=High for TV input */ ++ .tv = 1, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ ++ }, ++ }, ++ [SAA7134_BOARD_PHILIPS_TOUGH] = { ++ .name = "Philips TOUGH DVB-T reference design", ++ .tuner_type = TUNER_ABSENT, ++ .audio_clock = 0x00187de7, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_307] = { ++ /* ++ Davydov Vladimir ++ */ ++ .name = "Avermedia AVerTV 307", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_ADS_INSTANT_TV] = { ++ .name = "ADS Tech Instant TV (saa7135)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_KWORLD_VSTREAM_XPERT] = { ++ .name = "Kworld/Tevion V-Stream Xpert TV PVR7134", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_PAL_I, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x0700, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x000, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x200, /* gpio by DScaler */ ++ },{ ++ .name = name_svideo, ++ .vmux = 0, ++ .amux = LINE1, ++ .gpio = 0x200, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x100, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x000, ++ }, ++ }, ++ [SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = { ++ .name = "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x00200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x200000, /* GPIO21=High for TV input */ ++ .tv = 1, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ ++ }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII] = { ++ .name = "Compro VideoMate TV Gold+II", ++ .audio_clock = 0x002187de7, ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = 0x63, ++ .radio_addr = 0x60, ++ .gpiomask = 0x8c1880, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 0, ++ .amux = LINE1, ++ .gpio = 0x800800, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x801000, ++ },{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x800000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x880000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x840000, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_XPERT] = { ++ /* ++ FIXME: ++ - Remote control doesn't initialize properly. ++ - Audio volume starts muted, ++ then gradually increases after channel change. ++ - Overlay scaling problems (application error?) ++ - Composite S-Video untested. ++ From: Konrad Rzepecki ++ */ ++ .name = "Kworld Xpert TV PVR7134", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_TENA_9533_DI, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = 0x61, ++ .radio_addr = 0x60, ++ .gpiomask = 0x0700, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x000, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x200, /* gpio by DScaler */ ++ },{ ++ .name = name_svideo, ++ .vmux = 0, ++ .amux = LINE1, ++ .gpio = 0x200, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x100, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x000, ++ }, ++ }, ++ [SAA7134_BOARD_FLYTV_DIGIMATRIX] = { ++ .name = "FlyTV mini Asus Digimatrix", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_LG_TALN, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, /* radio unconfirmed */ ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_TERMINATOR] = { ++ /* Kworld V-Stream Studio TV Terminator */ ++ /* "James Webb */ ++ .name = "V-Stream Studio TV Terminator", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 1 << 21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x0000000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x0000000, ++ },{ ++ .name = name_svideo, /* S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x0000000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_YUAN_TUN900] = { ++ /* FIXME: ++ * S-Video and composite sources untested. ++ * Radio not working. ++ * Remote control not yet implemented. ++ * From : codemaster@webgeeks.be */ ++ .name = "Yuan TUN-900 (saa7135)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr= ADDR_UNSET, ++ .radio_addr= ADDR_UNSET, ++ .gpiomask = 0x00010003, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x01, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x02, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE2, ++ .gpio = 0x02, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ .gpio = 0x00010003, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x01, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_409FM] = { ++ /* , Sergey */ ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 409 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_GOTVIEW_7135] = { ++ /* Mike Baikov */ ++ /* Andrey Cvetcov */ ++ .name = "GoTView 7135 PCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00200003, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00200003, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x00200003, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x00200003, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x00200003, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x00200003, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x00200003, ++ }, ++ }, ++ [SAA7134_BOARD_PHILIPS_EUROPA] = { ++ .name = "Philips EUROPA V3 reference design", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TD1316, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_DVBT_300] = { ++ .name = "Compro Videomate DVB-T300", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TD1316, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_DVBT_200] = { ++ .name = "Compro Videomate DVB-T200", ++ .tuner_type = TUNER_ABSENT, ++ .audio_clock = 0x00187de7, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_RTD_VFG7350] = { ++ .name = "RTD Embedded Technologies VFG7350", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x21, ++ .inputs = {{ ++ .name = "Composite 0", ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = "Composite 1", ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = "Composite 2", ++ .vmux = 2, ++ .amux = LINE1, ++ },{ ++ .name = "Composite 3", ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = "S-Video 0", ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = "S-Video 1", ++ .vmux = 9, ++ .amux = LINE2, ++ }}, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ .vid_port_opts = ( SET_T_CODE_POLARITY_NON_INVERTED | ++ SET_CLOCK_NOT_DELAYED | ++ SET_CLOCK_INVERTED | ++ SET_VSYNC_OFF ), ++ }, ++ [SAA7134_BOARD_RTD_VFG7330] = { ++ .name = "RTD Embedded Technologies VFG7330", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = "Composite 0", ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = "Composite 1", ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = "Composite 2", ++ .vmux = 2, ++ .amux = LINE1, ++ },{ ++ .name = "Composite 3", ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = "S-Video 0", ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = "S-Video 1", ++ .vmux = 9, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_FLYTVPLATINUM_MINI2] = { ++ .name = "LifeView FlyTV Platinum Mini2", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180] = { ++ /* Michael Krufky ++ * Uses Alps Electric TDHU2, containing NXT2004 ATSC Decoder ++ * AFAIK, there is no analog demod, thus, ++ * no support for analog television. ++ */ ++ .name = "AVerMedia AVerTVHD MCE A180", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_MONSTERTV_MOBILE] = { ++ .name = "SKNet MonsterTV Mobile", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_PINNACLE_PCTV_110i] = { ++ .name = "Pinnacle PCTV 40i/50i/110i (saa7133)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x080200000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 4, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE2, ++ }, { ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_P7131_DUAL] = { ++ .name = "ASUSTeK P7131 Dual", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 1 << 21, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000000, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_SEDNA_PC_TV_CARDBUS] = { ++ /* Paul Tom Zalac */ ++ /* Pavel Mihaylov */ ++ .name = "Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)", ++ /* Sedna/MuchTV (OEM) Cardbus TV Tuner */ ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0xe880c0, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV] = { ++ /* "Cyril Lacoux (Yack)" */ ++ .name = "ASUS Digimatrix TV", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .tda9887_conf = TDA9887_PRESENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_PHILIPS_TIGER] = { ++ .name = "Philips Tiger reference design", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = { ++ .name = "MSI TV@Anywhere plus", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 1 << 21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, /* unconfirmed, taken from Philips driver */ ++ },{ ++ .name = name_comp2, ++ .vmux = 0, /* untested, Composite over S-Video */ ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_CINERGY250PCI] = { ++ /* remote-control does not work. The signal about a ++ key press comes in via gpio, but the key code ++ doesn't. Neither does it have an i2c remote control ++ interface. */ ++ .name = "Terratec Cinergy 250 PCI TV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x80200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_svideo, /* NOT tested */ ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_FLYDVB_TRIO] = { ++ /* LifeView LR319 FlyDVB Trio */ ++ /* Peter Missel */ ++ .name = "LifeView FlyDVB Trio", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00200000, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, /* Analog broadcast/cable TV */ ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x200000, /* GPIO21=High for TV input */ ++ .tv = 1, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_777] = { ++ .name = "AverTV DVB-T 777", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_FLYDVBT_LR301] = { ++ /* LifeView FlyDVB-T */ ++ /* Giampiero Giancipoli */ ++ .name = "LifeView FlyDVB-T / Genius VideoWonder DVB-T", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331] = { ++ .name = "ADS Instant TV Duo Cardbus PTV331", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00200000, ++ }}, ++ }, ++ [SAA7134_BOARD_TEVION_DVBT_220RF] = { ++ .name = "Tevion/KWorld DVB-T 220RF", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 1 << 21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_DVBT_210] = { ++ .name = "KWorld DVB-T 210", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 1 << 21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_ATSC110] = { ++ .name = "Kworld ATSC110/115", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TUV1236D, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A169_B] = { ++ /* AVerMedia A169 */ ++ /* Rickard Osser */ ++ /* This card has two saa7134 chips on it, ++ but only one of them is currently working. */ ++ .name = "AVerMedia A169 B", ++ .audio_clock = 0x02187de7, ++ .tuner_type = TUNER_LG_TALN, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x0a60000, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A169_B1] = { ++ /* AVerMedia A169 */ ++ /* Rickard Osser */ ++ .name = "AVerMedia A169 B1", ++ .audio_clock = 0x02187de7, ++ .tuner_type = TUNER_LG_TALN, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0xca60000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 4, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x04a61000, ++ },{ ++ .name = name_comp2, /* Composite SVIDEO (B/W if signal is carried with SVIDEO) */ ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 9, /* 9 is correct as S-VIDEO1 according to a169.inf! */ ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_MD7134_BRIDGE_2] = { ++ /* The second saa7134 on this card only serves as DVB-S host bridge */ ++ .name = "Medion 7134 Bridge #2", ++ .audio_clock = 0x00187de7, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ }, ++ [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = { ++ .name = "LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x200000, /* GPIO21=High for TV input */ ++ .tv = 1, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE2, ++ },{ ++ .name = name_comp1, /* Composite signal on S-Video input */ ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ ++ }, ++ }, ++ [SAA7134_BOARD_FLYVIDEO3000_NTSC] = { ++ /* "Zac Bowling" */ ++ .name = "LifeView FlyVIDEO3000 (NTSC)", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_NTSC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ ++ .gpiomask = 0xe000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .gpio = 0x8000, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x4000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x2000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x8000, ++ }, ++ }, ++ [SAA7134_BOARD_MEDION_MD8800_QUADRO] = { ++ .name = "Medion Md8800 Quadro", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_FLYDVBS_LR300] = { ++ /* LifeView FlyDVB-s */ ++ /* Igor M. Liplianin */ ++ .name = "LifeView FlyDVB-S /Acorp TV134DS", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, /* Composite input */ ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, /* S-Video signal on S-Video input */ ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_PROTEUS_2309] = { ++ .name = "Proteus Pro 2309", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A16AR] = { ++ /* Petr Baudis */ ++ .name = "AVerMedia TV Hybrid A16AR", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_PHILIPS_TD1316, /* untested */ ++ .radio_type = TUNER_TEA5767, /* untested */ ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = 0x60, ++ .tda9887_conf = TDA9887_PRESENT, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_ASUS_EUROPA2_HYBRID] = { ++ .name = "Asus Europa2 OEM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT| TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_PINNACLE_PCTV_310i] = { ++ .name = "Pinnacle PCTV 310i", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 1, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x000200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 4, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = { ++ /* Mikhail Fedotov */ ++ .name = "Avermedia AVerTV Studio 507", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1256_IH3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x03, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x00, ++ },{ ++ .name = name_comp2, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x00, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x00, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x01, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ .gpio = 0x00, ++ }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = { ++ /* Francis Barber */ ++ .name = "Compro Videomate DVB-T200A", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { ++ /* Thomas Genty */ ++ /* David Bentham */ ++ .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 1, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200100, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000100, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200100, ++ }, ++ }, ++ [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { ++ .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 3, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_SERIAL, ++ .ts_force_val = 1, ++ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000100, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0800100, /* GPIO 23 HI for FM */ ++ }, ++ }, ++ [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { ++ .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 3, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_SERIAL, ++ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000100, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0800100, /* GPIO 23 HI for FM */ ++ }, ++ }, ++ [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { ++ .name = "Terratec Cinergy HT PCMCIA", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_ENCORE_ENLTV] = { ++ /* Steven Walter ++ Juan Pablo Sormani */ ++ .name = "Encore ENLTV", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_TNF_5335MF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = 3, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 7, ++ .amux = 4, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = 2, ++ },{ ++ .name = name_svideo, ++ .vmux = 0, ++ .amux = 2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++/* .gpio = 0x00300001,*/ ++ .gpio = 0x20000, ++ ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = 0, ++ }, ++ }, ++ [SAA7134_BOARD_ENCORE_ENLTV_FM] = { ++ /* Juan Pablo Sormani */ ++ .name = "Encore ENLTV-FM", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FCV1236D, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = 3, ++ .tv = 1, ++ },{ ++ .name = name_tv_mono, ++ .vmux = 7, ++ .amux = 4, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = 2, ++ },{ ++ .name = name_svideo, ++ .vmux = 0, ++ .amux = 2, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x20000, ++ ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = 0, ++ }, ++ }, ++ [SAA7134_BOARD_ENCORE_ENLTV_FM53] = { ++ .name = "Encore ENLTV-FM v5.3", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_TNF_5335MF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x7000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = 1, ++ .tv = 1, ++ .gpio = 0x50000, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = 2, ++ .gpio = 0x2000, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = 2, ++ .gpio = 0x2000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .vmux = 1, ++ .amux = 1, ++ }, ++ .mute = { ++ .name = name_mute, ++ .gpio = 0xf000, ++ .amux = 0, ++ }, ++ }, ++ [SAA7134_BOARD_ENCORE_ENLTV_FM3] = { ++ .name = "Encore ENLTV-FM 3", ++ .audio_clock = 0x02187de7, ++ .tuner_type = TUNER_TENA_TNF_5337, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = 0x61, ++ .radio_addr = 0x60, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .vmux = 1, ++ .amux = LINE1, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ .gpio = 0x43000, ++ }, ++ }, ++ [SAA7134_BOARD_CINERGY_HT_PCI] = { ++ .name = "Terratec Cinergy HT PCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_PHILIPS_TIGER_S] = { ++ .name = "Philips Tiger - S Reference design", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_M102] = { ++ .name = "Avermedia M102", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 1<<21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ },{ ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE2, ++ }}, ++ }, ++ [SAA7134_BOARD_ASUS_P7131_4871] = { ++ .name = "ASUS P7131 4871", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0200000, ++ }}, ++ }, ++ [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = { ++ .name = "ASUSTeK P7131 Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .gpiomask = 1 << 21, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000000, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ .gpio = 0x0200000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { ++ .name = "ASUSTeK P7131 Analog", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 1 << 21, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x0000000, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ }, { ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_SABRENT_TV_PCB05] = { ++ .name = "Sabrent PCMCIA TV-PCB05", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_10MOONSTVMASTER3] = { ++ /* Tony Wan */ ++ .name = "10MOONS TM300 TV Card", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x7000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x2000, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x2000, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x3000, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_SUPER_007] = { ++ .name = "Avermedia Super 007", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, /* FIXME: analog tv untested */ ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_M135A] = { ++ .name = "Avermedia PCI pure analog (M135A)", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .gpiomask = 0x020200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00200000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x01, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_M733A] = { ++ .name = "Avermedia PCI M733A", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .gpiomask = 0x020200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00200000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x01, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_401] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 401", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_403] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 403", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_BEHOLD_403FM] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 403 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FQ1216ME, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_405] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 405", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ }, ++ [SAA7134_BOARD_BEHOLD_405FM] = { ++ /* Sergey */ ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 405 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ },{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_407] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 407", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0xc0c000, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0xc0c000, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0xc0c000, ++ }}, ++ }, ++ [SAA7134_BOARD_BEHOLD_407FM] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 407 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0xc0c000, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0xc0c000, ++ },{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0xc0c000, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0xc0c000, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_409] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 409", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ }, ++ [SAA7134_BOARD_BEHOLD_505FM] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 505 FM", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_505RDS_MK5] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 505 RDS", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_507_9FM] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 507 FM / BeholdTV 509 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_507RDS_MK5] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 507 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_507RDS_MK3] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 507 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /* Dmitry Belimov */ ++ .name = "Beholder BeholdTV Columbus TV/FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ALPS_TSBE5_PAL, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = 0xc2 >> 1, ++ .radio_addr = 0xc0 >> 1, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x000A8004, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x000A8004, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0x000A8000, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x000A8000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x000A8000, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_607FM_MK3] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 607 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_609FM_MK3] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 609 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_607FM_MK5] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 607 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_609FM_MK5] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 609 FM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_607RDS_MK3] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 607 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_609RDS_MK3] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 609 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_607RDS_MK5] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 607 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_609RDS_MK5] = { ++ /* Andrey Melnikoff */ ++ .name = "Beholder BeholdTV 609 RDS", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ },{ ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ },{ ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }}, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_M6] = { ++ /* Igor Kuznetsov */ ++ /* Andrey Melnikoff */ ++ /* Beholder Intl. Ltd. Dmitry Belimov */ ++ /* Alexey Osipov */ ++ .name = "Beholder BeholdTV M6", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | ++ SET_CLOCK_NOT_DELAYED | ++ SET_CLOCK_INVERTED | ++ SET_VSYNC_OFF), ++ }, ++ [SAA7134_BOARD_BEHOLD_M63] = { ++ /* Igor Kuznetsov */ ++ /* Andrey Melnikoff */ ++ /* Beholder Intl. Ltd. Dmitry Belimov */ ++ .name = "Beholder BeholdTV M63", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .empress_addr = 0x20, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | ++ SET_CLOCK_NOT_DELAYED | ++ SET_CLOCK_INVERTED | ++ SET_VSYNC_OFF), ++ }, ++ [SAA7134_BOARD_BEHOLD_M6_EXTRA] = { ++ /* Igor Kuznetsov */ ++ /* Andrey Melnikoff */ ++ /* Beholder Intl. Ltd. Dmitry Belimov */ ++ /* Alexey Osipov */ ++ .name = "Beholder BeholdTV M6 Extra", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216MK5, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .empress_addr = 0x20, ++ .tda9887_conf = TDA9887_PRESENT, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ .mpeg = SAA7134_MPEG_EMPRESS, ++ .video_out = CCIR656, ++ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | ++ SET_CLOCK_NOT_DELAYED | ++ SET_CLOCK_INVERTED | ++ SET_VSYNC_OFF), ++ }, ++ [SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = { ++ .name = "Twinhan Hybrid DTV-DVB 3056 PCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, /* untested */ ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_GENIUS_TVGO_A11MCE] = { ++ /* Adrian Pardini */ ++ .name = "Genius TVGO AM11MCE", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_TNF_5335MF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0xf000, ++ .inputs = {{ ++ .name = name_tv_mono, ++ .vmux = 1, ++ .amux = LINE2, ++ .gpio = 0x0000, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x2000, ++ .tv = 1 ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x2000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x1000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE2, ++ .gpio = 0x6000, ++ }, ++ }, ++ [SAA7134_BOARD_PHILIPS_SNAKE] = { ++ .name = "NXP Snake DVB-S reference design", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ }, ++ [SAA7134_BOARD_CREATIX_CTX953] = { ++ .name = "Medion/Creatix CTX953 Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ }, ++ [SAA7134_BOARD_MSI_TVANYWHERE_AD11] = { ++ .name = "MSI TV@nywhere A/D v1.1", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_CARDBUS_506] = { ++ .name = "AVerMedia Cardbus TV/Radio (E506R)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A16D] = { ++ .name = "AVerMedia Hybrid TV/Radio (A16D)", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ }, { ++ .name = name_comp, ++ .vmux = 0, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_M115] = { ++ .name = "Avermedia M115", ++ .audio_clock = 0x187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_T750] = { ++ /* John Newbigin */ ++ .name = "Compro VideoMate T750", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ } ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A700_PRO] = { ++ /* Matthias Schwarzott */ ++ .name = "Avermedia DVB-S Pro A700", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_comp, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ } }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_A700_HYBRID] = { ++ /* Matthias Schwarzott */ ++ .name = "Avermedia DVB-S Hybrid+FM A700", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_XC2028, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 4, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_H6] = { ++ /* Igor Kuznetsov */ ++ .name = "Beholder BeholdTV H6", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_TIGER_3IN1] = { ++ .name = "Asus Tiger 3in1", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .gpiomask = 1 << 21, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_PS3_100] = { ++ .name = "Asus My Cinema PS3-100", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 2, ++ .gpiomask = 1 << 21, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_REAL_ANGEL_220] = { ++ .name = "Zogis Real Angel 220", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_TNF_5335MF, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x801a8087, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ .gpio = 0x624000, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0x624000, ++ }, { ++ .name = name_svideo, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0x624000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x624001, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = { ++ .name = "ADS Tech Instant HDTV", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TUV1236D, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp, ++ .vmux = 4, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ }, ++ [SAA7134_BOARD_ASUSTeK_TIGER] = { ++ .name = "Asus Tiger Rev:1.00", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 0x0200000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ }, { ++ .name = name_comp2, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0200000, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = { ++ .name = "Kworld Plus TV Analog Lite PCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_YMEC_TVF_5533MF, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = 0x60, ++ .gpiomask = 0x80000700, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = LINE2, ++ .tv = 1, ++ .gpio = 0x100, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x200, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x200, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0x100, ++ }, ++ .mute = { ++ .name = name_mute, ++ .vmux = 8, ++ .amux = 2, ++ }, ++ }, ++ [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { ++ .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .tuner_addr = ADDR_UNSET, ++ .radio_type = UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x8e054000, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_PARALLEL, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++#if 0 /* FIXME */ ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x200, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x200, ++#endif ++ } }, ++#if 0 ++ .radio = { ++ .name = name_radio, ++ .vmux = 1, ++ .amux = LINE1, ++ .gpio = 0x100, ++ }, ++#endif ++ .mute = { ++ .name = name_mute, ++ .vmux = 0, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS] = { ++ .name = "Avermedia AVerTV GO 007 FM Plus", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00300003, ++ /* .gpiomask = 0x8c240003, */ ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x01, ++ }, { ++ .name = name_svideo, ++ .vmux = 6, ++ .amux = LINE1, ++ .gpio = 0x02, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00300001, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ .gpio = 0x01, ++ }, ++ }, ++ [SAA7134_BOARD_AVERMEDIA_STUDIO_507UA] = { ++ /* Andy Shevchenko */ ++ .name = "Avermedia AVerTV Studio 507UA", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* Should be MK5 */ ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x03, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x00, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x00, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ .gpio = 0x01, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ .gpio = 0x00, ++ }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_S350] = { ++ /* Jan D. Louw */ ++ .name = "Beholder BeholdTV X7", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_XC5000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 2, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 9, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_ZOLID_HYBRID_PCI] = { ++ .name = "Zolid Hybrid TV Tuner PCI", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .tuner_config = 0, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_PARALLEL, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ } }, ++ .radio = { /* untested */ ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = { ++ .name = "Asus Europa Hybrid OEM", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TD1316, ++ .radio_type = UNSET, ++ .tuner_addr = 0x61, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 4, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ }, ++ [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = { ++ .name = "Leadtek Winfast DTV1000S", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = { { ++ .name = name_comp1, ++ .vmux = 3, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ } }, ++ }, ++ [SAA7134_BOARD_BEHOLD_505RDS_MK3] = { ++ /* Beholder Intl. Ltd. 2008 */ ++ /*Dmitry Belimov */ ++ .name = "Beholder BeholdTV 505 RDS", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .rds_addr = 0x10, ++ .tda9887_conf = TDA9887_PRESENT, ++ .gpiomask = 0x00008000, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE2, ++ }, ++ }, ++ [SAA7134_BOARD_HAWELL_HW_404M7] = { ++ /* Hawell HW-404M7 & Hawell HW-808M7 */ ++ /* Bogoslovskiy Viktor */ ++ .name = "Hawell HW-404M7", ++ .audio_clock = 0x00200000, ++ .tuner_type = UNSET, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x389c00, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x01fc00, ++ } }, ++ }, ++ [SAA7134_BOARD_BEHOLD_H7] = { ++ /* Beholder Intl. Ltd. Dmitry Belimov */ ++ .name = "Beholder BeholdTV H7", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_XC5000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_PARALLEL, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 2, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 9, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_A7] = { ++ /* Beholder Intl. Ltd. Dmitry Belimov */ ++ .name = "Beholder BeholdTV A7", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_XC5000, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 2, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 9, ++ .amux = LINE1, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = { ++ .name = "TechoTrend TT-budget T-3000", ++ .tuner_type = TUNER_PHILIPS_TD1316, ++ .audio_clock = 0x00187de7, ++ .radio_type = UNSET, ++ .tuner_addr = 0x63, ++ .radio_addr = ADDR_UNSET, ++ .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, ++ .mpeg = SAA7134_MPEG_DVB, ++ .inputs = {{ ++ .name = name_tv, ++ .vmux = 3, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ }, ++ [SAA7134_BOARD_VIDEOMATE_M1F] = { ++ /* Pavel Osnova */ ++ .name = "Compro VideoMate Vista M1F", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_LG_PAL_NEW_TAPC, ++ .radio_type = TUNER_TEA5767, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = 0x60, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE2, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = LINE1, ++ }, ++ .mute = { ++ .name = name_mute, ++ .amux = TV, ++ }, ++ }, ++ [SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2] = { ++ /* Timothy Lee */ ++ .name = "MagicPro ProHDTV Pro2 DMB-TH/Hybrid", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_config = 3, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x02050000, ++ .mpeg = SAA7134_MPEG_DVB, ++ .ts_type = SAA7134_MPEG_TS_PARALLEL, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ .gpio = 0x00050000, ++ }, { ++ .name = name_comp1, ++ .vmux = 3, ++ .amux = LINE1, ++ .gpio = 0x00050000, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ .gpio = 0x00050000, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x00050000, ++ }, ++ .mute = { ++ .name = name_mute, ++ .vmux = 0, ++ .amux = TV, ++ .gpio = 0x00050000, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_501] = { ++ /* Beholder Intl. Ltd. 2010 */ ++ /* Dmitry Belimov */ ++ .name = "Beholder BeholdTV 501", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00008000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_BEHOLD_503FM] = { ++ /* Beholder Intl. Ltd. 2010 */ ++ /* Dmitry Belimov */ ++ .name = "Beholder BeholdTV 503 FM", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .gpiomask = 0x00008000, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 3, ++ .amux = LINE2, ++ .tv = 1, ++ }, { ++ .name = name_comp1, ++ .vmux = 1, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ .mute = { ++ .name = name_mute, ++ .amux = LINE1, ++ }, ++ }, ++ [SAA7134_BOARD_SENSORAY811_911] = { ++ .name = "Sensoray 811/911", ++ .audio_clock = 0x00200000, ++ .tuner_type = TUNER_ABSENT, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .inputs = {{ ++ .name = name_comp1, ++ .vmux = 0, ++ .amux = LINE1, ++ }, { ++ .name = name_comp3, ++ .vmux = 2, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE1, ++ } }, ++ }, ++ [SAA7134_BOARD_KWORLD_PC150U] = { ++ .name = "Kworld PC150-U", ++ .audio_clock = 0x00187de7, ++ .tuner_type = TUNER_PHILIPS_TDA8290, ++ .radio_type = UNSET, ++ .tuner_addr = ADDR_UNSET, ++ .radio_addr = ADDR_UNSET, ++ .mpeg = SAA7134_MPEG_DVB, ++ .gpiomask = 1 << 21, ++ .ts_type = SAA7134_MPEG_TS_PARALLEL, ++ .inputs = { { ++ .name = name_tv, ++ .vmux = 1, ++ .amux = TV, ++ .tv = 1, ++ }, { ++ .name = name_comp, ++ .vmux = 3, ++ .amux = LINE1, ++ }, { ++ .name = name_svideo, ++ .vmux = 8, ++ .amux = LINE2, ++ } }, ++ .radio = { ++ .name = name_radio, ++ .amux = TV, ++ .gpio = 0x0000000, ++ }, ++ }, ++ ++}; ++ ++const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); ++ ++/* ------------------------------------------------------------------ */ ++/* PCI ids + subsystem IDs */ ++ ++struct pci_device_id saa7134_pci_tbl[] = { ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2001, ++ .driver_data = SAA7134_BOARD_PROTEUS_PRO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2001, ++ .driver_data = SAA7134_BOARD_PROTEUS_PRO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x6752, ++ .driver_data = SAA7134_BOARD_EMPRESS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1131, ++ .subdevice = 0x4e85, ++ .driver_data = SAA7134_BOARD_MONSTERTV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x153b, ++ .subdevice = 0x1142, ++ .driver_data = SAA7134_BOARD_CINERGY400, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x153b, ++ .subdevice = 0x1143, ++ .driver_data = SAA7134_BOARD_CINERGY600, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x153b, ++ .subdevice = 0x1158, ++ .driver_data = SAA7134_BOARD_CINERGY600_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x153b, ++ .subdevice = 0x1162, ++ .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5169, ++ .subdevice = 0x0138, ++ .driver_data = SAA7134_BOARD_FLYVIDEO3000_NTSC, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5168, ++ .subdevice = 0x0138, ++ .driver_data = SAA7134_BOARD_FLYVIDEO3000, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x4e42, /* "Typhoon PCI Capture TV Card" Art.No. 50673 */ ++ .subdevice = 0x0138, ++ .driver_data = SAA7134_BOARD_FLYVIDEO3000, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x5168, ++ .subdevice = 0x0138, ++ .driver_data = SAA7134_BOARD_FLYVIDEO2000, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x4e42, /* Typhoon */ ++ .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ ++ .driver_data = SAA7134_BOARD_FLYVIDEO2000, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x0212, /* minipci, LR212 */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x14c0, ++ .subdevice = 0x1212, /* minipci, LR1212 */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI2, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x4e42, ++ .subdevice = 0x0212, /* OEM minipci, LR212 */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, /* Animation Technologies (LifeView) */ ++ .subdevice = 0x0214, /* Standard PCI, LR214 Rev E and earlier (SAA7135) */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, /* Animation Technologies (LifeView) */ ++ .subdevice = 0x5214, /* Standard PCI, LR214 Rev F onwards (SAA7131) */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1489, /* KYE */ ++ .subdevice = 0x0214, /* Genius VideoWonder ProTV */ ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x16be, ++ .subdevice = 0x0003, ++ .driver_data = SAA7134_BOARD_MD7134, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */ ++ .subdevice = 0x5000, /* only analog TV and DVB-T for now */ ++ .driver_data = SAA7134_BOARD_MD7134, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1048, ++ .subdevice = 0x226b, ++ .driver_data = SAA7134_BOARD_ELSA, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1048, ++ .subdevice = 0x226a, ++ .driver_data = SAA7134_BOARD_ELSA_500TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1048, ++ .subdevice = 0x226c, ++ .driver_data = SAA7134_BOARD_ELSA_700TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_ASUSTEK, ++ .subdevice = 0x4842, ++ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_VENDOR_ID_ASUSTEK, ++ .subdevice = 0x4845, ++ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7135, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_ASUSTEK, ++ .subdevice = 0x4830, ++ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_VENDOR_ID_ASUSTEK, ++ .subdevice = 0x4843, ++ .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_ASUSTEK, ++ .subdevice = 0x4840, ++ .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0xfe01, ++ .driver_data = SAA7134_BOARD_TVSTATION_RDS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1894, ++ .subdevice = 0xfe01, ++ .driver_data = SAA7134_BOARD_TVSTATION_RDS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1894, ++ .subdevice = 0xa006, ++ .driver_data = SAA7134_BOARD_TVSTATION_DVR, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1131, ++ .subdevice = 0x7133, ++ .driver_data = SAA7134_BOARD_VA1000POWER, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2001, ++ .driver_data = SAA7134_BOARD_10MOONSTVMASTER, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x185b, ++ .subdevice = 0xc100, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x185b, ++ .subdevice = 0xc100, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_MATROX, ++ .subdevice = 0x48d0, ++ .driver_data = SAA7134_BOARD_CRONOS_PLUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa70b, ++ .driver_data = SAA7134_BOARD_MD2819, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa7a1, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa7a2, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x2115, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa115, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x2108, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_305, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x10ff, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER, ++ },{ ++ /* AVerMedia CardBus */ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xd6ee, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS, ++ },{ ++ /* AVerMedia CardBus */ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xb7e9, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501, ++ }, { ++ /* TransGear 3000TV */ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x050c, ++ .driver_data = SAA7134_BOARD_TG3000TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x11bd, ++ .subdevice = 0x002b, ++ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x11bd, ++ .subdevice = 0x002d, ++ .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1019, ++ .subdevice = 0x4cb4, ++ .driver_data = SAA7134_BOARD_ECS_TVP3XP, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1019, ++ .subdevice = 0x4cb5, ++ .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1019, ++ .subdevice = 0x4cb6, ++ .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB6, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x12ab, ++ .subdevice = 0x0800, ++ .driver_data = SAA7134_BOARD_UPMOST_PURPLE_TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x153b, ++ .subdevice = 0x1152, ++ .driver_data = SAA7134_BOARD_CINERGY200, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x185b, ++ .subdevice = 0xc100, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x9715, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa70a, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_307, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x185b, ++ .subdevice = 0xc200, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1540, ++ .subdevice = 0x9524, ++ .driver_data = SAA7134_BOARD_PROVIDEO_PV952, ++ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x0502, /* Cardbus version */ ++ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x0306, /* PCI version */ ++ .driver_data = SAA7134_BOARD_FLYDVBTDUO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf31f, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM, ++ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf11d, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M135A, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x4155, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x4255, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2004, ++ .driver_data = SAA7134_BOARD_PHILIPS_TOUGH, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1421, ++ .subdevice = 0x0350, /* PCI version */ ++ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1421, ++ .subdevice = 0x0351, /* PCI version, new revision */ ++ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1421, ++ .subdevice = 0x0370, /* cardbus version */ ++ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1421, ++ .subdevice = 0x1370, /* cardbus version */ ++ .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, ++ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x4e42, /* Typhoon */ ++ .subdevice = 0x0502, /* LifeView LR502 OEM */ ++ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x0210, /* mini pci NTSC version */ ++ .driver_data = SAA7134_BOARD_FLYTV_DIGIMATRIX, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1043, ++ .subdevice = 0x0210, /* mini pci PAL/SECAM version */ ++ .driver_data = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV, ++ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0000, /* It shouldn't break anything, since subdevice id seems unique */ ++ .subdevice = 0x4091, ++ .driver_data = SAA7134_BOARD_BEHOLD_409FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5456, /* GoTView */ ++ .subdevice = 0x7135, ++ .driver_data = SAA7134_BOARD_GOTVIEW_7135, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2004, ++ .driver_data = SAA7134_BOARD_PHILIPS_EUROPA, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x185b, ++ .subdevice = 0xc900, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_300, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x185b, ++ .subdevice = 0xc901, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_200, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1435, ++ .subdevice = 0x7350, ++ .driver_data = SAA7134_BOARD_RTD_VFG7350, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1435, ++ .subdevice = 0x7330, ++ .driver_data = SAA7134_BOARD_RTD_VFG7330, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, ++ .subdevice = 0x1044, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1131, ++ .subdevice = 0x4ee9, ++ .driver_data = SAA7134_BOARD_MONSTERTV_MOBILE, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x11bd, ++ .subdevice = 0x002e, ++ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_110i, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x4862, ++ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2018, ++ .driver_data = SAA7134_BOARD_PHILIPS_TIGER, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1462, ++ .subdevice = 0x6231, /* tda8275a, ks003 IR */ ++ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1462, ++ .subdevice = 0x8624, /* tda8275, ks003 IR */ ++ .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x153b, ++ .subdevice = 0x1160, ++ .driver_data = SAA7134_BOARD_CINERGY250PCI, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA 7131E */ ++ .subvendor = 0x5168, ++ .subdevice = 0x0319, ++ .driver_data = SAA7134_BOARD_FLYDVB_TRIO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, ++ .subdevice = 0x2c05, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_777, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5168, ++ .subdevice = 0x0301, ++ .driver_data = SAA7134_BOARD_FLYDVBT_LR301, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0331, ++ .subdevice = 0x1421, ++ .driver_data = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x17de, ++ .subdevice = 0x7201, ++ .driver_data = SAA7134_BOARD_TEVION_DVBT_220RF, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x17de, ++ .subdevice = 0x7250, ++ .driver_data = SAA7134_BOARD_KWORLD_DVBT_210, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ ++ .subvendor = 0x17de, ++ .subdevice = 0x7350, ++ .driver_data = SAA7134_BOARD_KWORLD_ATSC110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ ++ .subvendor = 0x17de, ++ .subdevice = 0x7352, ++ .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ ++ .subvendor = 0x17de, ++ .subdevice = 0xa134, ++ .driver_data = SAA7134_BOARD_KWORLD_PC150U, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, ++ .subdevice = 0x7360, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, ++ .subdevice = 0x6360, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B1, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x16be, ++ .subdevice = 0x0005, ++ .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5168, ++ .subdevice = 0x0300, ++ .driver_data = SAA7134_BOARD_FLYDVBS_LR300, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x4e42, ++ .subdevice = 0x0300,/* LR300 */ ++ .driver_data = SAA7134_BOARD_FLYDVBS_LR300, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1489, ++ .subdevice = 0x0301, ++ .driver_data = SAA7134_BOARD_FLYDVBT_LR301, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, /* Animation Technologies (LifeView) */ ++ .subdevice = 0x0304, ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x3306, ++ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/ ++ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5168, ++ .subdevice = 0x3307, /* FlyDVB-T Hybrid Mini PCI */ ++ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x16be, ++ .subdevice = 0x0007, ++ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x16be, ++ .subdevice = 0x0008, ++ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x16be, ++ .subdevice = 0x000d, /* triple CTX948_V1.1.1 */ ++ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, ++ .subdevice = 0x2c05, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_777, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1489, ++ .subdevice = 0x0502, /* Cardbus version */ ++ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0919, /* Philips Proteus PRO 2309 */ ++ .subdevice = 0x2003, ++ .driver_data = SAA7134_BOARD_PROTEUS_2309, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, ++ .subdevice = 0x2c00, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A16AR, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1043, ++ .subdevice = 0x4860, ++ .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x11bd, ++ .subdevice = 0x002f, ++ .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0x9715, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa11b, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x4876, ++ .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6700, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6701, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6702, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6703, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6704, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6705, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6706, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6707, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6708, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x6709, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0070, ++ .subdevice = 0x670a, ++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x153b, ++ .subdevice = 0x1172, ++ .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2342, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1131, ++ .subdevice = 0x2341, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x3016, ++ .subdevice = 0x2344, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1131, ++ .subdevice = 0x230f, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x1a7f, ++ .subdevice = 0x2008, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1a7f, ++ .subdevice = 0x2108, ++ .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x153b, ++ .subdevice = 0x1175, ++ .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf31e, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M102, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x4E42, /* MSI */ ++ .subdevice = 0x0306, /* TV@nywhere DUO */ ++ .driver_data = SAA7134_BOARD_FLYDVBTDUO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x4871, ++ .driver_data = SAA7134_BOARD_ASUS_P7131_4871, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x4857, /* REV:1.00 */ ++ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x0919, /* SinoVideo PCI 2309 Proteus (7134) */ ++ .subdevice = 0x2003, /* OEM cardbus */ ++ .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2304, ++ .driver_data = SAA7134_BOARD_10MOONSTVMASTER3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf01d, /* AVerTV DVB-T Super 007 */ ++ .driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0000, ++ .subdevice = 0x4016, ++ .driver_data = SAA7134_BOARD_BEHOLD_401, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x0000, ++ .subdevice = 0x4036, ++ .driver_data = SAA7134_BOARD_BEHOLD_403, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x0000, ++ .subdevice = 0x4037, ++ .driver_data = SAA7134_BOARD_BEHOLD_403FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0000, ++ .subdevice = 0x4050, ++ .driver_data = SAA7134_BOARD_BEHOLD_405, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0000, ++ .subdevice = 0x4051, ++ .driver_data = SAA7134_BOARD_BEHOLD_405FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x0000, ++ .subdevice = 0x4070, ++ .driver_data = SAA7134_BOARD_BEHOLD_407, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x0000, ++ .subdevice = 0x4071, ++ .driver_data = SAA7134_BOARD_BEHOLD_407FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0000, ++ .subdevice = 0x4090, ++ .driver_data = SAA7134_BOARD_BEHOLD_409, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0000, ++ .subdevice = 0x505B, ++ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x0000, ++ .subdevice = 0x5051, ++ .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x5ace, ++ .subdevice = 0x5050, ++ .driver_data = SAA7134_BOARD_BEHOLD_505FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0000, ++ .subdevice = 0x5071, ++ .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0000, ++ .subdevice = 0x507B, ++ .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5ace, ++ .subdevice = 0x5070, ++ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x5090, ++ .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x0000, ++ .subdevice = 0x5201, ++ .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6070, ++ .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6071, ++ .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6072, ++ .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6073, ++ .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6090, ++ .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6091, ++ .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6092, ++ .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6093, ++ .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6190, ++ .driver_data = SAA7134_BOARD_BEHOLD_M6, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6193, ++ .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6191, ++ .driver_data = SAA7134_BOARD_BEHOLD_M63, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x4e42, ++ .subdevice = 0x3502, ++ .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1822, /*Twinhan Technology Co. Ltd*/ ++ .subdevice = 0x0022, ++ .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x16be, ++ .subdevice = 0x0010, /* Medion version CTX953_V.1.4.3 */ ++ .driver_data = SAA7134_BOARD_CREATIX_CTX953, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1462, /* MSI */ ++ .subdevice = 0x8625, /* TV@nywhere A/D v1.1 */ ++ .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf436, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf936, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_A16D, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xa836, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M115, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x185b, ++ .subdevice = 0xc900, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_T750, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ ++ .subvendor = 0x1421, ++ .subdevice = 0x0380, ++ .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5169, ++ .subdevice = 0x1502, ++ .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x6290, ++ .driver_data = SAA7134_BOARD_BEHOLD_H6, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf636, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M103, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf736, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_M103, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x4878, /* REV:1.02G */ ++ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1043, ++ .subdevice = 0x48cd, ++ .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x17de, ++ .subdevice = 0x7128, ++ .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x17de, ++ .subdevice = 0xb136, ++ .driver_data = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x1461, /* Avermedia Technologies Inc */ ++ .subdevice = 0xf31d, ++ .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x185b, ++ .subdevice = 0xc900, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_S350, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ ++ .subdevice = 0x7595, ++ .driver_data = SAA7134_BOARD_BEHOLD_X7, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x19d1, /* RoverMedia */ ++ .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ ++ .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0x2004, ++ .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x1043, ++ .subdevice = 0x4847, ++ .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x107d, ++ .subdevice = 0x6655, ++ .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x13c2, ++ .subdevice = 0x2804, ++ .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ ++ .subdevice = 0x7190, ++ .driver_data = SAA7134_BOARD_BEHOLD_H7, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ ++ .subdevice = 0x7090, ++ .driver_data = SAA7134_BOARD_BEHOLD_A7, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7135, ++ .subvendor = 0x185b, ++ .subdevice = 0xc900, ++ .driver_data = SAA7134_BOARD_VIDEOMATE_M1F, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x5ace, ++ .subdevice = 0x5030, ++ .driver_data = SAA7134_BOARD_BEHOLD_503FM, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = 0x5ace, ++ .subdevice = 0x5010, ++ .driver_data = SAA7134_BOARD_BEHOLD_501, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = 0x17de, ++ .subdevice = 0xd136, ++ .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x6000, ++ .subdevice = 0x0811, ++ .driver_data = SAA7134_BOARD_SENSORAY811_911, ++ }, { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = 0x6000, ++ .subdevice = 0x0911, ++ .driver_data = SAA7134_BOARD_SENSORAY811_911, ++ }, { ++ /* --- boards without eeprom + subsystem ID --- */ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0, ++ .driver_data = SAA7134_BOARD_NOAUTO, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_VENDOR_ID_PHILIPS, ++ .subdevice = 0, ++ .driver_data = SAA7134_BOARD_NOAUTO, ++ },{ ++ /* --- default catch --- */ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7130, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SAA7134_BOARD_UNKNOWN, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SAA7134_BOARD_UNKNOWN, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7134, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SAA7134_BOARD_UNKNOWN, ++ },{ ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7135, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = SAA7134_BOARD_UNKNOWN, ++ },{ ++ /* --- end of list --- */ ++ } ++}; ++MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl); ++ ++/* ----------------------------------------------------------- */ ++/* flyvideo tweaks */ ++ ++ ++static void board_flyvideo(struct saa7134_dev *dev) ++{ ++ printk("%s: there are different flyvideo cards with different tuners\n" ++ "%s: out there, you might have to use the tuner= insmod\n" ++ "%s: option to override the default value.\n", ++ dev->name, dev->name, dev->name); ++} ++ ++static int saa7134_xc2028_callback(struct saa7134_dev *dev, ++ int command, int arg) ++{ ++ switch (command) { ++ case XC2028_TUNER_RESET: ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); ++ switch (dev->board) { ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ++ case SAA7134_BOARD_AVERMEDIA_M103: ++ saa7134_set_gpio(dev, 23, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 23, 1); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ saa7134_set_gpio(dev, 21, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 21, 1); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: ++ saa7134_set_gpio(dev, 18, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 18, 1); ++ break; ++ case SAA7134_BOARD_VIDEOMATE_T750: ++ saa7134_set_gpio(dev, 20, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 20, 1); ++ break; ++ } ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int saa7134_xc5000_callback(struct saa7134_dev *dev, ++ int command, int arg) ++{ ++ switch (dev->board) { ++ case SAA7134_BOARD_BEHOLD_X7: ++ case SAA7134_BOARD_BEHOLD_H7: ++ case SAA7134_BOARD_BEHOLD_A7: ++ if (command == XC5000_TUNER_RESET) { ++ /* Down and UP pheripherial RESET pin for reset all chips */ ++ saa_writeb(SAA7134_SPECIAL_MODE, 0x00); ++ msleep(10); ++ saa_writeb(SAA7134_SPECIAL_MODE, 0x01); ++ msleep(10); ++ } ++ break; ++ default: ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); ++ saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); ++ saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); ++ saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); ++ saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); ++ saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, ++ 0x0001e000, 0x0001e000); ++ break; ++ } ++ return 0; ++} ++ ++static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, ++ int command, int arg) ++{ ++ u8 sync_control; ++ ++ switch (command) { ++ case 0: /* switch LNA gain through GPIO 22*/ ++ saa7134_set_gpio(dev, 22, arg) ; ++ break; ++ case 1: /* vsync output at GPIO22. 50 / 60Hz */ ++ saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); ++ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); ++ if (arg == 1) ++ sync_control = 11; ++ else ++ sync_control = 17; ++ saa_writeb(SAA7134_VGATE_START, sync_control); ++ saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); ++ saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev, ++ enum tda18271_mode mode) ++{ ++ /* toggle AGC switch through GPIO 26 */ ++ switch (mode) { ++ case TDA18271_ANALOG: ++ saa7134_set_gpio(dev, 26, 0); ++ break; ++ case TDA18271_DIGITAL: ++ saa7134_set_gpio(dev, 26, 1); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev, ++ enum tda18271_mode mode) ++{ ++ /* toggle AGC switch through GPIO 27 */ ++ switch (mode) { ++ case TDA18271_ANALOG: ++ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); ++ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); ++ msleep(20); ++ break; ++ case TDA18271_DIGITAL: ++ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000); ++ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000); ++ msleep(20); ++ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000); ++ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000); ++ msleep(30); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev, ++ enum tda18271_mode mode) ++{ ++ switch (mode) { ++ case TDA18271_ANALOG: ++ saa7134_set_gpio(dev, 18, 0); ++ break; ++ case TDA18271_DIGITAL: ++ saa7134_set_gpio(dev, 18, 1); ++ msleep(30); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, ++ int command, int arg) ++{ ++ int ret = 0; ++ ++ switch (command) { ++ case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ ++ switch (dev->board) { ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: ++ ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); ++ break; ++ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ++ ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg); ++ break; ++ case SAA7134_BOARD_KWORLD_PC150U: ++ ret = saa7134_kworld_pc150u_toggle_agc(dev, arg); ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int saa7134_tda8290_callback(struct saa7134_dev *dev, ++ int command, int arg) ++{ ++ int ret; ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ case SAA7134_BOARD_AVERMEDIA_M733A: ++ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ++ case SAA7134_BOARD_KWORLD_PC150U: ++ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: ++ /* tda8290 + tda18271 */ ++ ret = saa7134_tda8290_18271_callback(dev, command, arg); ++ break; ++ default: ++ /* tda8290 + tda827x */ ++ ret = saa7134_tda8290_827x_callback(dev, command, arg); ++ break; ++ } ++ return ret; ++} ++ ++int saa7134_tuner_callback(void *priv, int component, int command, int arg) ++{ ++ struct saa7134_dev *dev = priv; ++ ++ if (dev != NULL) { ++ switch (dev->tuner_type) { ++ case TUNER_PHILIPS_TDA8290: ++ return saa7134_tda8290_callback(dev, command, arg); ++ case TUNER_XC2028: ++ return saa7134_xc2028_callback(dev, command, arg); ++ case TUNER_XC5000: ++ return saa7134_xc5000_callback(dev, command, arg); ++ } ++ } else { ++ printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); ++ return -EINVAL; ++ } ++ return -EINVAL; ++} ++EXPORT_SYMBOL(saa7134_tuner_callback); ++ ++/* ----------------------------------------------------------- */ ++ ++static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) ++{ ++ struct tveeprom tv; ++ ++ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); ++ ++ /* Make sure we support the board model */ ++ switch (tv.model) { ++ case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ ++ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ ++ case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ ++ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ ++ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ ++ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ ++ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */ ++ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ ++ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ ++ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ ++ case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ ++ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ ++ break; ++ default: ++ printk(KERN_WARNING "%s: warning: " ++ "unknown hauppauge model #%d\n", dev->name, tv.model); ++ break; ++ } ++ ++ printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", ++ dev->name, tv.model); ++} ++ ++/* ----------------------------------------------------------- */ ++ ++int saa7134_board_init1(struct saa7134_dev *dev) ++{ ++ /* Always print gpio, often manufacturers encode tuner type and other info. */ ++ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0); ++ dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value); ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_FLYVIDEO2000: ++ case SAA7134_BOARD_FLYVIDEO3000: ++ case SAA7134_BOARD_FLYVIDEO3000_NTSC: ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ board_flyvideo(dev); ++ break; ++ case SAA7134_BOARD_FLYTVPLATINUM_MINI2: ++ case SAA7134_BOARD_FLYTVPLATINUM_FM: ++ case SAA7134_BOARD_CINERGY400: ++ case SAA7134_BOARD_CINERGY600: ++ case SAA7134_BOARD_CINERGY600_MK3: ++ case SAA7134_BOARD_ECS_TVP3XP: ++ case SAA7134_BOARD_ECS_TVP3XP_4CB5: ++ case SAA7134_BOARD_ECS_TVP3XP_4CB6: ++ case SAA7134_BOARD_MD2819: ++ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: ++ case SAA7134_BOARD_KWORLD_XPERT: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_305: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_505: ++ case SAA7134_BOARD_AVERMEDIA_305: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_307: ++ case SAA7134_BOARD_AVERMEDIA_307: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_507: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ++ case SAA7134_BOARD_AVERMEDIA_777: ++ case SAA7134_BOARD_AVERMEDIA_M135A: ++/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ ++ case SAA7134_BOARD_VIDEOMATE_TV_PVR: ++ case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: ++ case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: ++ case SAA7134_BOARD_VIDEOMATE_M1F: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_300: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200A: ++ case SAA7134_BOARD_MANLI_MTV001: ++ case SAA7134_BOARD_MANLI_MTV002: ++ case SAA7134_BOARD_BEHOLD_409FM: ++ case SAA7134_BOARD_AVACSSMARTTV: ++ case SAA7134_BOARD_GOTVIEW_7135: ++ case SAA7134_BOARD_KWORLD_TERMINATOR: ++ case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: ++ case SAA7134_BOARD_FLYDVBT_LR301: ++ case SAA7134_BOARD_ASUSTeK_PS3_100: ++ case SAA7134_BOARD_ASUSTeK_P7131_DUAL: ++ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: ++ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: ++ case SAA7134_BOARD_FLYDVBTDUO: ++ case SAA7134_BOARD_PROTEUS_2309: ++ case SAA7134_BOARD_AVERMEDIA_A16AR: ++ case SAA7134_BOARD_ENCORE_ENLTV: ++ case SAA7134_BOARD_ENCORE_ENLTV_FM: ++ case SAA7134_BOARD_ENCORE_ENLTV_FM53: ++ case SAA7134_BOARD_ENCORE_ENLTV_FM3: ++ case SAA7134_BOARD_10MOONSTVMASTER3: ++ case SAA7134_BOARD_BEHOLD_401: ++ case SAA7134_BOARD_BEHOLD_403: ++ case SAA7134_BOARD_BEHOLD_403FM: ++ case SAA7134_BOARD_BEHOLD_405: ++ case SAA7134_BOARD_BEHOLD_405FM: ++ case SAA7134_BOARD_BEHOLD_407: ++ case SAA7134_BOARD_BEHOLD_407FM: ++ case SAA7134_BOARD_BEHOLD_409: ++ case SAA7134_BOARD_BEHOLD_505FM: ++ case SAA7134_BOARD_BEHOLD_505RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_505RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_507_9FM: ++ case SAA7134_BOARD_BEHOLD_507RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_507RDS_MK5: ++ case SAA7134_BOARD_GENIUS_TVGO_A11MCE: ++ case SAA7134_BOARD_REAL_ANGEL_220: ++ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: ++ case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: ++ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ break; ++ case SAA7134_BOARD_FLYDVBS_LR300: ++ saa_writeb(SAA7134_GPIO_GPMODE3, 0x80); ++ saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40); ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ break; ++ case SAA7134_BOARD_MD5044: ++ printk("%s: seems there are two different versions of the MD5044\n" ++ "%s: (with the same ID) out there. If sound doesn't work for\n" ++ "%s: you try the audio_clock_override=0x200000 insmod option.\n", ++ dev->name,dev->name,dev->name); ++ break; ++ case SAA7134_BOARD_CINERGY400_CARDBUS: ++ /* power-up tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000); ++ break; ++ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: ++ /* this turns the remote control chip off to work around a bug in it */ ++ saa_writeb(SAA7134_GPIO_GPMODE1, 0x80); ++ saa_writeb(SAA7134_GPIO_GPSTATUS1, 0x80); ++ break; ++ case SAA7134_BOARD_MONSTERTV_MOBILE: ++ /* power-up tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000004); ++ break; ++ case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: ++ /* turn the fan on */ ++ saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); ++ saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06); ++ break; ++ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: ++ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS: ++ case SAA7134_BOARD_AVERMEDIA_M115: ++ /* power-down tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); ++ msleep(10); ++ /* power-up tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); ++ msleep(10); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: ++ /* power-down tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0); ++ msleep(10); ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0x08400000); ++ msleep(10); ++ dev->has_remote = SAA7134_REMOTE_I2C; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ++ saa7134_set_gpio(dev, 23, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 23, 1); ++ dev->has_remote = SAA7134_REMOTE_I2C; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M103: ++ saa7134_set_gpio(dev, 23, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 23, 1); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ saa7134_set_gpio(dev, 21, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 21, 1); ++ msleep(1); ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ break; ++ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: ++ /* power-down tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0); ++ msleep(10); ++ /* power-up tuner chip */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004); ++ msleep(10); ++ /* remote via GPIO */ ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ break; ++ case SAA7134_BOARD_RTD_VFG7350: ++ ++ /* ++ * Make sure Production Test Register at offset 0x1D1 is cleared ++ * to take chip out of test mode. Clearing bit 4 (TST_EN_AOUT) ++ * prevents pin 105 from remaining low; keeping pin 105 low ++ * continually resets the SAA6752 chip. ++ */ ++ ++ saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ /* GPIO 26 high for digital, low for analog */ ++ saa7134_set_gpio(dev, 26, 0); ++ msleep(1); ++ ++ saa7134_set_gpio(dev, 22, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 22, 1); ++ break; ++ /* i2c remotes */ ++ case SAA7134_BOARD_PINNACLE_PCTV_110i: ++ case SAA7134_BOARD_PINNACLE_PCTV_310i: ++ case SAA7134_BOARD_UPMOST_PURPLE_TV: ++ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1110: ++ case SAA7134_BOARD_BEHOLD_607FM_MK3: ++ case SAA7134_BOARD_BEHOLD_607FM_MK5: ++ case SAA7134_BOARD_BEHOLD_609FM_MK3: ++ case SAA7134_BOARD_BEHOLD_609FM_MK5: ++ case SAA7134_BOARD_BEHOLD_607RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_607RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_609RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_609RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_M6: ++ case SAA7134_BOARD_BEHOLD_M63: ++ case SAA7134_BOARD_BEHOLD_M6_EXTRA: ++ case SAA7134_BOARD_BEHOLD_H6: ++ case SAA7134_BOARD_BEHOLD_X7: ++ case SAA7134_BOARD_BEHOLD_H7: ++ case SAA7134_BOARD_BEHOLD_A7: ++ case SAA7134_BOARD_KWORLD_PC150U: ++ dev->has_remote = SAA7134_REMOTE_I2C; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A169_B: ++ printk("%s: %s: dual saa713x broadcast decoders\n" ++ "%s: Sorry, none of the inputs to this chip are supported yet.\n" ++ "%s: Dual decoder functionality is disabled for now, use the other chip.\n", ++ dev->name,card(dev).name,dev->name,dev->name); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M102: ++ /* enable tuner */ ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: ++ case SAA7134_BOARD_AVERMEDIA_A700_PRO: ++ /* write windows gpio values */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); ++ break; ++ case SAA7134_BOARD_VIDEOMATE_S350: ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M733A: ++ saa7134_set_gpio(dev, 1, 1); ++ msleep(10); ++ saa7134_set_gpio(dev, 1, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 1, 1); ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ break; ++ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: ++ /* enable LGS-8G75 */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0e050000, 0x0c050000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000); ++ break; ++ case SAA7134_BOARD_VIDEOMATE_T750: ++ /* enable the analog tuner */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); ++ break; ++ } ++ return 0; ++} ++ ++static void saa7134_tuner_setup(struct saa7134_dev *dev) ++{ ++ struct tuner_setup tun_setup; ++ unsigned int mode_mask = T_RADIO | T_ANALOG_TV; ++ ++ memset(&tun_setup, 0, sizeof(tun_setup)); ++ tun_setup.tuner_callback = saa7134_tuner_callback; ++ ++ if (saa7134_boards[dev->board].radio_type != UNSET) { ++ tun_setup.type = saa7134_boards[dev->board].radio_type; ++ tun_setup.addr = saa7134_boards[dev->board].radio_addr; ++ ++ tun_setup.mode_mask = T_RADIO; ++ ++ saa_call_all(dev, tuner, s_type_addr, &tun_setup); ++ mode_mask &= ~T_RADIO; ++ } ++ ++ if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) { ++ tun_setup.type = dev->tuner_type; ++ tun_setup.addr = dev->tuner_addr; ++ tun_setup.config = saa7134_boards[dev->board].tuner_config; ++ tun_setup.tuner_callback = saa7134_tuner_callback; ++ ++ tun_setup.mode_mask = mode_mask; ++ ++ saa_call_all(dev, tuner, s_type_addr, &tun_setup); ++ } ++ ++ if (dev->tda9887_conf) { ++ struct v4l2_priv_tun_config tda9887_cfg; ++ ++ tda9887_cfg.tuner = TUNER_TDA9887; ++ tda9887_cfg.priv = &dev->tda9887_conf; ++ ++ saa_call_all(dev, tuner, s_config, &tda9887_cfg); ++ } ++ ++ if (dev->tuner_type == TUNER_XC2028) { ++ struct v4l2_priv_tun_config xc2028_cfg; ++ struct xc2028_ctrl ctl; ++ ++ memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); ++ memset(&ctl, 0, sizeof(ctl)); ++ ++ ctl.fname = XC2028_DEFAULT_FIRMWARE; ++ ctl.max_len = 64; ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ++ case SAA7134_BOARD_AVERMEDIA_M103: ++ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: ++ ctl.demod = XC3028_FE_ZARLINK456; ++ break; ++ default: ++ ctl.demod = XC3028_FE_OREN538; ++ ctl.mts = 1; ++ } ++ ++ xc2028_cfg.tuner = TUNER_XC2028; ++ xc2028_cfg.priv = &ctl; ++ ++ saa_call_all(dev, tuner, s_config, &xc2028_cfg); ++ } ++} ++ ++/* stuff which needs working i2c */ ++int saa7134_board_init2(struct saa7134_dev *dev) ++{ ++ unsigned char buf; ++ int board; ++ ++ /* Put here the code that enables the chips that are needed ++ for analog mode and doesn't depend on the tuner attachment. ++ It is also a good idea to get tuner type from eeprom, etc before ++ initializing tuner, since we can avoid loading tuner driver ++ on devices that has TUNER_ABSENT ++ */ ++ switch (dev->board) { ++ case SAA7134_BOARD_BMK_MPEX_NOTUNER: ++ case SAA7134_BOARD_BMK_MPEX_TUNER: ++ /* Checks if the device has a tuner at 0x60 addr ++ If the device doesn't have a tuner, TUNER_ABSENT ++ will be used at tuner_type, avoiding loading tuner ++ without needing it ++ */ ++ dev->i2c_client.addr = 0x60; ++ board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0) ++ ? SAA7134_BOARD_BMK_MPEX_NOTUNER ++ : SAA7134_BOARD_BMK_MPEX_TUNER; ++ if (board == dev->board) ++ break; ++ dev->board = board; ++ printk("%s: board type fixup: %s\n", dev->name, ++ saa7134_boards[dev->board].name); ++ dev->tuner_type = saa7134_boards[dev->board].tuner_type; ++ ++ break; ++ case SAA7134_BOARD_MD7134: ++ { ++ u8 subaddr; ++ u8 data[3]; ++ int ret, tuner_t; ++ struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1}, ++ {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}}; ++ ++ subaddr= 0x14; ++ tuner_t = 0; ++ ++ /* Retrieve device data from eeprom, checking for the ++ proper tuner_type. ++ */ ++ ret = i2c_transfer(&dev->i2c_adap, msg, 2); ++ if (ret != 2) { ++ printk(KERN_ERR "EEPROM read failure\n"); ++ } else if ((data[0] != 0) && (data[0] != 0xff)) { ++ /* old config structure */ ++ subaddr = data[0] + 2; ++ msg[1].len = 2; ++ i2c_transfer(&dev->i2c_adap, msg, 2); ++ tuner_t = (data[0] << 8) + data[1]; ++ switch (tuner_t){ ++ case 0x0103: ++ dev->tuner_type = TUNER_PHILIPS_PAL; ++ break; ++ case 0x010C: ++ dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3; ++ break; ++ default: ++ printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); ++ } ++ } else if ((data[1] != 0) && (data[1] != 0xff)) { ++ /* new config structure */ ++ subaddr = data[1] + 1; ++ msg[1].len = 1; ++ i2c_transfer(&dev->i2c_adap, msg, 2); ++ subaddr = data[0] + 1; ++ msg[1].len = 2; ++ i2c_transfer(&dev->i2c_adap, msg, 2); ++ tuner_t = (data[1] << 8) + data[0]; ++ switch (tuner_t) { ++ case 0x0005: ++ dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3; ++ break; ++ case 0x001d: ++ dev->tuner_type = TUNER_PHILIPS_FMD1216ME_MK3; ++ printk(KERN_INFO "%s Board has DVB-T\n", dev->name); ++ break; ++ default: ++ printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); ++ } ++ } else { ++ printk(KERN_ERR "%s unexpected config structure\n", dev->name); ++ } ++ ++ printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type); ++ break; ++ } ++ case SAA7134_BOARD_PHILIPS_EUROPA: ++ if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) { ++ /* Reconfigure board as Snake reference design */ ++ dev->board = SAA7134_BOARD_PHILIPS_SNAKE; ++ dev->tuner_type = saa7134_boards[dev->board].tuner_type; ++ printk(KERN_INFO "%s: Reconfigured board as %s\n", ++ dev->name, saa7134_boards[dev->board].name); ++ break; ++ } ++ /* break intentionally omitted */ ++ case SAA7134_BOARD_VIDEOMATE_DVBT_300: ++ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: ++ case SAA7134_BOARD_ASUS_EUROPA_HYBRID: ++ case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: ++ { ++ ++ /* The Philips EUROPA based hybrid boards have the tuner ++ connected through the channel decoder. We have to make it ++ transparent to find it ++ */ ++ u8 data[] = { 0x07, 0x02}; ++ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ ++ break; ++ } ++ case SAA7134_BOARD_PHILIPS_TIGER: ++ case SAA7134_BOARD_PHILIPS_TIGER_S: ++ { ++ u8 data[] = { 0x3c, 0x33, 0x60}; ++ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; ++ if (dev->autodetected && (dev->eedata[0x49] == 0x50)) { ++ dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; ++ printk(KERN_INFO "%s: Reconfigured board as %s\n", ++ dev->name, saa7134_boards[dev->board].name); ++ } ++ if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { ++ dev->tuner_type = TUNER_PHILIPS_TDA8290; ++ ++ data[2] = 0x68; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_ASUSTeK_TVFM7135: ++ /* The card below is detected as card=53, but is different */ ++ if (dev->autodetected && (dev->eedata[0x27] == 0x03)) { ++ dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; ++ printk(KERN_INFO "%s: P7131 analog only, using " ++ "entry of %s\n", ++ dev->name, saa7134_boards[dev->board].name); ++ ++ /* IR init has already happened for other cards, so ++ * we have to catch up. */ ++ dev->has_remote = SAA7134_REMOTE_GPIO; ++ saa7134_input_init1(dev); ++ } ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ hauppauge_eeprom(dev, dev->eedata+0x80); ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1110: ++ hauppauge_eeprom(dev, dev->eedata+0x80); ++ /* break intentionally omitted */ ++ case SAA7134_BOARD_PINNACLE_PCTV_310i: ++ case SAA7134_BOARD_KWORLD_DVBT_210: ++ case SAA7134_BOARD_TEVION_DVBT_220RF: ++ case SAA7134_BOARD_ASUSTeK_TIGER: ++ case SAA7134_BOARD_ASUSTeK_P7131_DUAL: ++ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: ++ case SAA7134_BOARD_MEDION_MD8800_QUADRO: ++ case SAA7134_BOARD_AVERMEDIA_SUPER_007: ++ case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: ++ case SAA7134_BOARD_CREATIX_CTX953: ++ { ++ /* this is a hybrid board, initialize to analog mode ++ * and configure firmware eeprom address ++ */ ++ u8 data[] = { 0x3c, 0x33, 0x60}; ++ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1: ++ { ++ u8 data[] = { 0x3c, 0x33, 0x60}; ++ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, ++ .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_ASUSTeK_PS3_100: ++ { ++ u8 data[] = { 0x3c, 0x33, 0x60}; ++ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, ++ .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_FLYDVB_TRIO: ++ { ++ u8 temp = 0; ++ int rc; ++ u8 data[] = { 0x3c, 0x33, 0x62}; ++ struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ ++ /* ++ * send weak up message to pic16C505 chip ++ * @ LifeView FlyDVB Trio ++ */ ++ msg.buf = &temp; ++ msg.addr = 0x0b; ++ msg.len = 1; ++ if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) { ++ printk(KERN_WARNING "%s: send wake up byte to pic16C505" ++ "(IR chip) failed\n", dev->name); ++ } else { ++ msg.flags = I2C_M_RD; ++ rc = i2c_transfer(&dev->i2c_adap, &msg, 1); ++ printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n", ++ dev->name, msg.addr, ++ (1 == rc) ? "yes" : "no"); ++ if (rc == 1) ++ dev->has_remote = SAA7134_REMOTE_I2C; ++ } ++ break; ++ } ++ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: ++ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: ++ { ++ /* initialize analog mode */ ++ u8 data[] = { 0x3c, 0x33, 0x6a}; ++ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_CINERGY_HT_PCMCIA: ++ case SAA7134_BOARD_CINERGY_HT_PCI: ++ { ++ /* initialize analog mode */ ++ u8 data[] = { 0x3c, 0x33, 0x68}; ++ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ break; ++ } ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200A: ++ /* The T200 and the T200A share the same pci id. Consequently, ++ * we are going to query eeprom to try to find out which one we ++ * are actually looking at. */ ++ ++ /* Don't do this if the board was specifically selected with an ++ * insmod option or if we have the default configuration T200*/ ++ if (!dev->autodetected || (dev->eedata[0x41] == 0xd0)) ++ break; ++ if (dev->eedata[0x41] == 0x02) { ++ /* Reconfigure board as T200A */ ++ dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; ++ dev->tuner_type = saa7134_boards[dev->board].tuner_type; ++ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; ++ printk(KERN_INFO "%s: Reconfigured board as %s\n", ++ dev->name, saa7134_boards[dev->board].name); ++ } else { ++ printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n", ++ dev->name, dev->eedata[0x41]); ++ break; ++ } ++ break; ++ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: ++ case SAA7134_BOARD_KWORLD_ATSC110: ++ { ++ struct i2c_msg msg = { .addr = 0x0a, .flags = 0 }; ++ int i; ++ static u8 buffer[][2] = { ++ { 0x10, 0x12 }, ++ { 0x13, 0x04 }, ++ { 0x16, 0x00 }, ++ { 0x14, 0x04 }, ++ { 0x17, 0x00 }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(buffer); i++) { ++ msg.buf = &buffer[i][0]; ++ msg.len = ARRAY_SIZE(buffer[0]); ++ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) ++ printk(KERN_WARNING ++ "%s: Unable to enable tuner(%i).\n", ++ dev->name, i); ++ } ++ break; ++ } ++ case SAA7134_BOARD_BEHOLD_H6: ++ { ++ u8 data[] = { 0x09, 0x9f, 0x86, 0x11}; ++ struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data, ++ .len = sizeof(data)}; ++ ++ /* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware */ ++ /* start has disabled IF and enabled DVB-T. When saa7134 */ ++ /* scan I2C devices it not detect IF tda9887 and can`t */ ++ /* watch TV without software reboot. For solve this problem */ ++ /* switch the tuner to analog TV mode manually. */ ++ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) ++ printk(KERN_WARNING ++ "%s: Unable to enable IF of the tuner.\n", ++ dev->name); ++ break; ++ } ++ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ++ saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); ++ saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); ++ ++ saa7134_set_gpio(dev, 27, 0); ++ break; ++ } /* switch() */ ++ ++ /* initialize tuner */ ++ if (TUNER_ABSENT != dev->tuner_type) { ++ int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); ++ ++ /* Note: radio tuner address is always filled in, ++ so we do not need to probe for a radio tuner device. */ ++ if (dev->radio_type != UNSET) ++ v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_adap, "tuner", ++ dev->radio_addr, NULL); ++ if (has_demod) ++ v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); ++ if (dev->tuner_addr == ADDR_UNSET) { ++ enum v4l2_i2c_tuner_type type = ++ has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; ++ ++ v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_adap, "tuner", ++ 0, v4l2_i2c_tuner_addrs(type)); ++ } else { ++ v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_adap, "tuner", ++ dev->tuner_addr, NULL); ++ } ++ } ++ ++ saa7134_tuner_setup(dev); ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: ++ { ++ struct v4l2_priv_tun_config tea5767_cfg; ++ struct tea5767_ctrl ctl; ++ ++ dev->i2c_client.addr = 0xC0; ++ /* set TEA5767(analog FM) defines */ ++ memset(&ctl, 0, sizeof(ctl)); ++ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; ++ tea5767_cfg.tuner = TUNER_TEA5767; ++ tea5767_cfg.priv = &ctl; ++ saa_call_all(dev, tuner, s_config, &tea5767_cfg); ++ break; ++ } ++ } /* switch() */ ++ ++ return 0; ++} +diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c +new file mode 100644 +index 0000000..e1aeb51 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-core.c +@@ -0,0 +1,1367 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * driver core ++ * ++ * (c) 2001-03 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(SAA7134_VERSION); ++ ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int irq_debug; ++module_param(irq_debug, int, 0644); ++MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); ++ ++static unsigned int core_debug; ++module_param(core_debug, int, 0644); ++MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); ++ ++static unsigned int gpio_tracking; ++module_param(gpio_tracking, int, 0644); ++MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); ++ ++static unsigned int alsa = 1; ++module_param(alsa, int, 0644); ++MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]"); ++ ++static unsigned int latency = UNSET; ++module_param(latency, int, 0444); ++MODULE_PARM_DESC(latency,"pci latency timer"); ++ ++int saa7134_no_overlay=-1; ++module_param_named(no_overlay, saa7134_no_overlay, int, 0444); ++MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" ++ " [some VIA/SIS chipsets are known to have problem with overlay]"); ++ ++static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++ ++ ++module_param_array(video_nr, int, NULL, 0444); ++module_param_array(vbi_nr, int, NULL, 0444); ++module_param_array(radio_nr, int, NULL, 0444); ++module_param_array(tuner, int, NULL, 0444); ++module_param_array(card, int, NULL, 0444); ++ ++MODULE_PARM_DESC(video_nr, "video device number"); ++MODULE_PARM_DESC(vbi_nr, "vbi device number"); ++MODULE_PARM_DESC(radio_nr, "radio device number"); ++MODULE_PARM_DESC(tuner, "tuner type"); ++MODULE_PARM_DESC(card, "card type"); ++ ++DEFINE_MUTEX(saa7134_devlist_lock); ++EXPORT_SYMBOL(saa7134_devlist_lock); ++LIST_HEAD(saa7134_devlist); ++EXPORT_SYMBOL(saa7134_devlist); ++static LIST_HEAD(mops_list); ++static unsigned int saa7134_devcount; ++ ++int (*saa7134_dmasound_init)(struct saa7134_dev *dev); ++int (*saa7134_dmasound_exit)(struct saa7134_dev *dev); ++ ++#define dprintk(fmt, arg...) if (core_debug) \ ++ printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) ++ ++void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) ++{ ++ unsigned long mode,status; ++ ++ if (!gpio_tracking) ++ return; ++ /* rising SAA7134_GPIO_GPRESCAN reads the status */ ++ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0); ++ saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN); ++ mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff; ++ status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff; ++ printk(KERN_DEBUG ++ "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", ++ dev->name, mode, (~mode) & status, mode & status, msg); ++} ++ ++void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) ++{ ++ u32 index, bitval; ++ ++ index = 1 << bit_no; ++ switch (value) { ++ case 0: /* static value */ ++ case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); ++ /* turn sync mode off if necessary */ ++ if (index & 0x00c00000) ++ saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); ++ if (value) ++ bitval = index; ++ else ++ bitval = 0; ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); ++ break; ++ case 3: /* tristate */ ++ dprintk("setting GPIO%d to tristate\n", bit_no); ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); ++ break; ++ } ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++ ++/* ----------------------------------------------------------- */ ++/* delayed request_module */ ++ ++#if defined(CONFIG_MODULES) && defined(MODULE) ++ ++static void request_module_async(struct work_struct *work){ ++ struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk); ++ if (card_is_empress(dev)) ++ request_module("saa7134-empress"); ++ if (card_is_dvb(dev)) ++ request_module("saa7134-dvb"); ++ if (alsa) { ++ if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) ++ request_module("saa7134-alsa"); ++ } ++} ++ ++static void request_submodules(struct saa7134_dev *dev) ++{ ++ INIT_WORK(&dev->request_module_wk, request_module_async); ++ schedule_work(&dev->request_module_wk); ++} ++ ++static void flush_request_submodules(struct saa7134_dev *dev) ++{ ++ flush_work(&dev->request_module_wk); ++} ++ ++#else ++#define request_submodules(dev) ++#define flush_request_submodules(dev) ++#endif /* CONFIG_MODULES */ ++ ++/* ------------------------------------------------------------------ */ ++ ++/* nr of (saa7134-)pages for the given buffer size */ ++static int saa7134_buffer_pages(int size) ++{ ++ size = PAGE_ALIGN(size); ++ size += PAGE_SIZE; /* for non-page-aligned buffers */ ++ size /= 4096; ++ return size; ++} ++ ++/* calc max # of buffers from size (must not exceed the 4MB virtual ++ * address space per DMA channel) */ ++int saa7134_buffer_count(unsigned int size, unsigned int count) ++{ ++ unsigned int maxcount; ++ ++ maxcount = 1024 / saa7134_buffer_pages(size); ++ if (count > maxcount) ++ count = maxcount; ++ return count; ++} ++ ++int saa7134_buffer_startpage(struct saa7134_buf *buf) ++{ ++ return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i; ++} ++ ++unsigned long saa7134_buffer_base(struct saa7134_buf *buf) ++{ ++ unsigned long base; ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ base = saa7134_buffer_startpage(buf) * 4096; ++ base += dma->sglist[0].offset; ++ return base; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) ++{ ++ __le32 *cpu; ++ dma_addr_t dma_addr = 0; ++ ++ cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); ++ if (NULL == cpu) ++ return -ENOMEM; ++ pt->size = SAA7134_PGTABLE_SIZE; ++ pt->cpu = cpu; ++ pt->dma = dma_addr; ++ return 0; ++} ++ ++int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, ++ struct scatterlist *list, unsigned int length, ++ unsigned int startpage) ++{ ++ __le32 *ptr; ++ unsigned int i,p; ++ ++ BUG_ON(NULL == pt || NULL == pt->cpu); ++ ++ ptr = pt->cpu + startpage; ++ for (i = 0; i < length; i++, list++) ++ for (p = 0; p * 4096 < list->length; p++, ptr++) ++ *ptr = cpu_to_le32(sg_dma_address(list) - list->offset); ++ return 0; ++} ++ ++void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) ++{ ++ if (NULL == pt->cpu) ++ return; ++ pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); ++ pt->cpu = NULL; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) ++{ ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ 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; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int saa7134_buffer_queue(struct saa7134_dev *dev, ++ struct saa7134_dmaqueue *q, ++ struct saa7134_buf *buf) ++{ ++ struct saa7134_buf *next = NULL; ++ ++ assert_spin_locked(&dev->slock); ++ dprintk("buffer_queue %p\n",buf); ++ if (NULL == q->curr) { ++ if (!q->need_two) { ++ q->curr = buf; ++ buf->activate(dev,buf,NULL); ++ } else if (list_empty(&q->queue)) { ++ list_add_tail(&buf->vb.queue,&q->queue); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ } else { ++ next = list_entry(q->queue.next,struct saa7134_buf, ++ vb.queue); ++ q->curr = buf; ++ buf->activate(dev,buf,next); ++ } ++ } else { ++ list_add_tail(&buf->vb.queue,&q->queue); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ } ++ return 0; ++} ++ ++void saa7134_buffer_finish(struct saa7134_dev *dev, ++ struct saa7134_dmaqueue *q, ++ unsigned int state) ++{ ++ assert_spin_locked(&dev->slock); ++ dprintk("buffer_finish %p\n",q->curr); ++ ++ /* finish current buffer */ ++ q->curr->vb.state = state; ++ v4l2_get_timestamp(&q->curr->vb.ts); ++ wake_up(&q->curr->vb.done); ++ q->curr = NULL; ++} ++ ++void saa7134_buffer_next(struct saa7134_dev *dev, ++ struct saa7134_dmaqueue *q) ++{ ++ struct saa7134_buf *buf,*next = NULL; ++ ++ assert_spin_locked(&dev->slock); ++ BUG_ON(NULL != q->curr); ++ ++ if (!list_empty(&q->queue)) { ++ /* activate next one from queue */ ++ buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue); ++ dprintk("buffer_next %p [prev=%p/next=%p]\n", ++ buf,q->queue.prev,q->queue.next); ++ list_del(&buf->vb.queue); ++ if (!list_empty(&q->queue)) ++ next = list_entry(q->queue.next,struct saa7134_buf, ++ vb.queue); ++ q->curr = buf; ++ buf->activate(dev,buf,next); ++ dprintk("buffer_next #2 prev=%p/next=%p\n", ++ q->queue.prev,q->queue.next); ++ } else { ++ /* nothing to do -- just stop DMA */ ++ dprintk("buffer_next %p\n",NULL); ++ saa7134_set_dmabits(dev); ++ del_timer(&q->timeout); ++ ++ if (card_has_mpeg(dev)) ++ if (dev->ts_started) ++ saa7134_ts_stop(dev); ++ } ++} ++ ++void saa7134_buffer_timeout(unsigned long data) ++{ ++ struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data; ++ struct saa7134_dev *dev = q->dev; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&dev->slock,flags); ++ ++ /* try to reset the hardware (SWRST) */ ++ saa_writeb(SAA7134_REGION_ENABLE, 0x00); ++ saa_writeb(SAA7134_REGION_ENABLE, 0x80); ++ saa_writeb(SAA7134_REGION_ENABLE, 0x00); ++ ++ /* flag current buffer as failed, ++ try to start over with the next one. */ ++ if (q->curr) { ++ dprintk("timeout on %p\n",q->curr); ++ saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR); ++ } ++ saa7134_buffer_next(dev,q); ++ spin_unlock_irqrestore(&dev->slock,flags); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++int saa7134_set_dmabits(struct saa7134_dev *dev) ++{ ++ u32 split, task=0, ctrl=0, irq=0; ++ enum v4l2_field cap = V4L2_FIELD_ANY; ++ enum v4l2_field ov = V4L2_FIELD_ANY; ++ ++ assert_spin_locked(&dev->slock); ++ ++ if (dev->insuspend) ++ return 0; ++ ++ /* video capture -- dma 0 + video task A */ ++ if (dev->video_q.curr) { ++ task |= 0x01; ++ ctrl |= SAA7134_MAIN_CTRL_TE0; ++ irq |= SAA7134_IRQ1_INTE_RA0_1 | ++ SAA7134_IRQ1_INTE_RA0_0; ++ cap = dev->video_q.curr->vb.field; ++ } ++ ++ /* video capture -- dma 1+2 (planar modes) */ ++ if (dev->video_q.curr && ++ dev->video_q.curr->fmt->planar) { ++ ctrl |= SAA7134_MAIN_CTRL_TE4 | ++ SAA7134_MAIN_CTRL_TE5; ++ } ++ ++ /* screen overlay -- dma 0 + video task B */ ++ if (dev->ovenable) { ++ task |= 0x10; ++ ctrl |= SAA7134_MAIN_CTRL_TE1; ++ ov = dev->ovfield; ++ } ++ ++ /* vbi capture -- dma 0 + vbi task A+B */ ++ if (dev->vbi_q.curr) { ++ task |= 0x22; ++ ctrl |= SAA7134_MAIN_CTRL_TE2 | ++ SAA7134_MAIN_CTRL_TE3; ++ irq |= SAA7134_IRQ1_INTE_RA0_7 | ++ SAA7134_IRQ1_INTE_RA0_6 | ++ SAA7134_IRQ1_INTE_RA0_5 | ++ SAA7134_IRQ1_INTE_RA0_4; ++ } ++ ++ /* audio capture -- dma 3 */ ++ if (dev->dmasound.dma_running) { ++ ctrl |= SAA7134_MAIN_CTRL_TE6; ++ irq |= SAA7134_IRQ1_INTE_RA3_1 | ++ SAA7134_IRQ1_INTE_RA3_0; ++ } ++ ++ /* TS capture -- dma 5 */ ++ if (dev->ts_q.curr) { ++ ctrl |= SAA7134_MAIN_CTRL_TE5; ++ irq |= SAA7134_IRQ1_INTE_RA2_1 | ++ SAA7134_IRQ1_INTE_RA2_0; ++ } ++ ++ /* set task conditions + field handling */ ++ if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) { ++ /* default config -- use full frames */ ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); ++ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02); ++ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02); ++ split = 0; ++ } else { ++ /* split fields between tasks */ ++ if (V4L2_FIELD_TOP == cap) { ++ /* odd A, even B, repeat */ ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e); ++ } else { ++ /* odd B, even A, repeat */ ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e); ++ saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); ++ } ++ saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x01); ++ saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x01); ++ split = 1; ++ } ++ ++ /* irqs */ ++ saa_writeb(SAA7134_REGION_ENABLE, task); ++ saa_writel(SAA7134_IRQ1, irq); ++ saa_andorl(SAA7134_MAIN_CTRL, ++ SAA7134_MAIN_CTRL_TE0 | ++ SAA7134_MAIN_CTRL_TE1 | ++ SAA7134_MAIN_CTRL_TE2 | ++ SAA7134_MAIN_CTRL_TE3 | ++ SAA7134_MAIN_CTRL_TE4 | ++ SAA7134_MAIN_CTRL_TE5 | ++ SAA7134_MAIN_CTRL_TE6, ++ ctrl); ++ dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n", ++ task, ctrl, irq, split ? "no" : "yes"); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* IRQ handler + helpers */ ++ ++static char *irqbits[] = { ++ "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3", ++ "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", ++ "TRIG_ERR", "CONF_ERR", "LOAD_ERR", ++ "GPIO16", "GPIO18", "GPIO22", "GPIO23" ++}; ++#define IRQBITS ARRAY_SIZE(irqbits) ++ ++static void print_irqstatus(struct saa7134_dev *dev, int loop, ++ unsigned long report, unsigned long status) ++{ ++ unsigned int i; ++ ++ printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx", ++ dev->name,loop,jiffies,report,status); ++ for (i = 0; i < IRQBITS; i++) { ++ if (!(report & (1 << i))) ++ continue; ++ printk(" %s",irqbits[i]); ++ } ++ if (report & SAA7134_IRQ_REPORT_DONE_RA0) { ++ printk(" | RA0=%s,%s,%s,%ld", ++ (status & 0x40) ? "vbi" : "video", ++ (status & 0x20) ? "b" : "a", ++ (status & 0x10) ? "odd" : "even", ++ (status & 0x0f)); ++ } ++ printk("\n"); ++} ++ ++static irqreturn_t saa7134_irq(int irq, void *dev_id) ++{ ++ struct saa7134_dev *dev = (struct saa7134_dev*) dev_id; ++ unsigned long report,status; ++ int loop, handled = 0; ++ ++ if (dev->insuspend) ++ goto out; ++ ++ for (loop = 0; loop < 10; loop++) { ++ report = saa_readl(SAA7134_IRQ_REPORT); ++ status = saa_readl(SAA7134_IRQ_STATUS); ++ ++ /* If dmasound support is active and we get a sound report, ++ * mask out the report and let the saa7134-alsa module deal ++ * with it */ ++ if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && ++ (dev->dmasound.priv_data != NULL) ) ++ { ++ if (irq_debug > 1) ++ printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n", ++ dev->name); ++ report &= ~SAA7134_IRQ_REPORT_DONE_RA3; ++ } ++ ++ if (0 == report) { ++ if (irq_debug > 1) ++ printk(KERN_DEBUG "%s/irq: no (more) work\n", ++ dev->name); ++ goto out; ++ } ++ ++ handled = 1; ++ saa_writel(SAA7134_IRQ_REPORT,report); ++ if (irq_debug) ++ print_irqstatus(dev,loop,report,status); ++ ++ ++ if ((report & SAA7134_IRQ_REPORT_RDCAP) || ++ (report & SAA7134_IRQ_REPORT_INTL)) ++ saa7134_irq_video_signalchange(dev); ++ ++ ++ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && ++ (status & 0x60) == 0) ++ saa7134_irq_video_done(dev,status); ++ ++ if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && ++ (status & 0x40) == 0x40) ++ saa7134_irq_vbi_done(dev,status); ++ ++ if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && ++ card_has_mpeg(dev)) ++ saa7134_irq_ts_done(dev,status); ++ ++ if (report & SAA7134_IRQ_REPORT_GPIO16) { ++ switch (dev->has_remote) { ++ case SAA7134_REMOTE_GPIO: ++ if (!dev->remote) ++ break; ++ if (dev->remote->mask_keydown & 0x10000) { ++ saa7134_input_irq(dev); ++ } ++ break; ++ ++ case SAA7134_REMOTE_I2C: ++ break; /* FIXME: invoke I2C get_key() */ ++ ++ default: /* GPIO16 not used by IR remote */ ++ break; ++ } ++ } ++ ++ if (report & SAA7134_IRQ_REPORT_GPIO18) { ++ switch (dev->has_remote) { ++ case SAA7134_REMOTE_GPIO: ++ if (!dev->remote) ++ break; ++ if ((dev->remote->mask_keydown & 0x40000) || ++ (dev->remote->mask_keyup & 0x40000)) { ++ saa7134_input_irq(dev); ++ } ++ break; ++ ++ case SAA7134_REMOTE_I2C: ++ break; /* FIXME: invoke I2C get_key() */ ++ ++ default: /* GPIO18 not used by IR remote */ ++ break; ++ } ++ } ++ } ++ ++ if (10 == loop) { ++ print_irqstatus(dev,loop,report,status); ++ if (report & SAA7134_IRQ_REPORT_PE) { ++ /* disable all parity error */ ++ printk(KERN_WARNING "%s/irq: looping -- " ++ "clearing PE (parity error!) enable bit\n",dev->name); ++ saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE); ++ } else if (report & SAA7134_IRQ_REPORT_GPIO16) { ++ /* disable gpio16 IRQ */ ++ printk(KERN_WARNING "%s/irq: looping -- " ++ "clearing GPIO16 enable bit\n",dev->name); ++ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P); ++ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N); ++ } else if (report & SAA7134_IRQ_REPORT_GPIO18) { ++ /* disable gpio18 IRQs */ ++ printk(KERN_WARNING "%s/irq: looping -- " ++ "clearing GPIO18 enable bit\n",dev->name); ++ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); ++ saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N); ++ } else { ++ /* disable all irqs */ ++ printk(KERN_WARNING "%s/irq: looping -- " ++ "clearing all enable bits\n",dev->name); ++ saa_writel(SAA7134_IRQ1,0); ++ saa_writel(SAA7134_IRQ2,0); ++ } ++ } ++ ++ out: ++ return IRQ_RETVAL(handled); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++/* early init (no i2c, no irq) */ ++ ++static int saa7134_hw_enable1(struct saa7134_dev *dev) ++{ ++ /* RAM FIFO config */ ++ saa_writel(SAA7134_FIFO_SIZE, 0x08070503); ++ saa_writel(SAA7134_THRESHOULD, 0x02020202); ++ ++ /* enable audio + video processing */ ++ saa_writel(SAA7134_MAIN_CTRL, ++ SAA7134_MAIN_CTRL_VPLLE | ++ SAA7134_MAIN_CTRL_APLLE | ++ SAA7134_MAIN_CTRL_EXOSC | ++ SAA7134_MAIN_CTRL_EVFE1 | ++ SAA7134_MAIN_CTRL_EVFE2 | ++ SAA7134_MAIN_CTRL_ESFE | ++ SAA7134_MAIN_CTRL_EBDAC); ++ ++ /* ++ * Initialize OSS _after_ enabling audio clock PLL and audio processing. ++ * OSS initialization writes to registers via the audio DSP; these ++ * writes will fail unless the audio clock has been started. At worst, ++ * audio will not work. ++ */ ++ ++ /* enable peripheral devices */ ++ saa_writeb(SAA7134_SPECIAL_MODE, 0x01); ++ ++ /* set vertical line numbering start (vbi needs this) */ ++ saa_writeb(SAA7134_SOURCE_TIMING2, 0x20); ++ ++ return 0; ++} ++ ++static int saa7134_hwinit1(struct saa7134_dev *dev) ++{ ++ dprintk("hwinit1\n"); ++ ++ saa_writel(SAA7134_IRQ1, 0); ++ saa_writel(SAA7134_IRQ2, 0); ++ ++ /* Clear any stale IRQ reports */ ++ saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT)); ++ ++ mutex_init(&dev->lock); ++ spin_lock_init(&dev->slock); ++ ++ saa7134_track_gpio(dev,"pre-init"); ++ saa7134_video_init1(dev); ++ saa7134_vbi_init1(dev); ++ if (card_has_mpeg(dev)) ++ saa7134_ts_init1(dev); ++ saa7134_input_init1(dev); ++ ++ saa7134_hw_enable1(dev); ++ ++ return 0; ++} ++ ++/* late init (with i2c + irq) */ ++static int saa7134_hw_enable2(struct saa7134_dev *dev) ++{ ++ ++ unsigned int irq2_mask; ++ ++ /* enable IRQ's */ ++ irq2_mask = ++ SAA7134_IRQ2_INTE_DEC3 | ++ SAA7134_IRQ2_INTE_DEC2 | ++ SAA7134_IRQ2_INTE_DEC1 | ++ SAA7134_IRQ2_INTE_DEC0 | ++ SAA7134_IRQ2_INTE_PE | ++ SAA7134_IRQ2_INTE_AR; ++ ++ if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) { ++ if (dev->remote->mask_keydown & 0x10000) ++ irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N; ++ else { /* Allow enabling both IRQ edge triggers */ ++ if (dev->remote->mask_keydown & 0x40000) ++ irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P; ++ if (dev->remote->mask_keyup & 0x40000) ++ irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N; ++ } ++ } ++ ++ if (dev->has_remote == SAA7134_REMOTE_I2C) { ++ request_module("ir-kbd-i2c"); ++ } ++ ++ saa_writel(SAA7134_IRQ1, 0); ++ saa_writel(SAA7134_IRQ2, irq2_mask); ++ ++ return 0; ++} ++ ++static int saa7134_hwinit2(struct saa7134_dev *dev) ++{ ++ ++ dprintk("hwinit2\n"); ++ ++ saa7134_video_init2(dev); ++ saa7134_tvaudio_init2(dev); ++ ++ saa7134_hw_enable2(dev); ++ ++ return 0; ++} ++ ++ ++/* shutdown */ ++static int saa7134_hwfini(struct saa7134_dev *dev) ++{ ++ dprintk("hwfini\n"); ++ ++ if (card_has_mpeg(dev)) ++ saa7134_ts_fini(dev); ++ saa7134_input_fini(dev); ++ saa7134_vbi_fini(dev); ++ saa7134_tvaudio_fini(dev); ++ return 0; ++} ++ ++static void __devinit must_configure_manually(int has_eeprom) ++{ ++ unsigned int i,p; ++ ++ if (!has_eeprom) ++ printk(KERN_WARNING ++ "saa7134: \n" ++ "saa7134: Congratulations! Your TV card vendor saved a few\n" ++ "saa7134: cents for a eeprom, thus your pci board has no\n" ++ "saa7134: subsystem ID and I can't identify it automatically\n" ++ "saa7134: \n" ++ "saa7134: I feel better now. Ok, here are the good news:\n" ++ "saa7134: You can use the card= insmod option to specify\n" ++ "saa7134: which board do you have. The list:\n"); ++ else ++ printk(KERN_WARNING ++ "saa7134: Board is currently unknown. You might try to use the card=\n" ++ "saa7134: insmod option to specify which board do you have, but this is\n" ++ "saa7134: somewhat risky, as might damage your card. It is better to ask\n" ++ "saa7134: for support at linux-media@vger.kernel.org.\n" ++ "saa7134: The supported cards are:\n"); ++ ++ for (i = 0; i < saa7134_bcount; i++) { ++ printk(KERN_WARNING "saa7134: card=%d -> %-40.40s", ++ i,saa7134_boards[i].name); ++ for (p = 0; saa7134_pci_tbl[p].driver_data; p++) { ++ if (saa7134_pci_tbl[p].driver_data != i) ++ continue; ++ printk(" %04x:%04x", ++ saa7134_pci_tbl[p].subvendor, ++ saa7134_pci_tbl[p].subdevice); ++ } ++ printk("\n"); ++ } ++} ++ ++static struct video_device *vdev_init(struct saa7134_dev *dev, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ *vfd = *template; ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ vfd->release = video_device_release; ++ vfd->debug = video_debug; ++ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", ++ dev->name, type, saa7134_boards[dev->board].name); ++ video_set_drvdata(vfd, dev); ++ return vfd; ++} ++ ++static void saa7134_unregister_video(struct saa7134_dev *dev) ++{ ++ if (dev->video_dev) { ++ if (video_is_registered(dev->video_dev)) ++ video_unregister_device(dev->video_dev); ++ else ++ video_device_release(dev->video_dev); ++ dev->video_dev = NULL; ++ } ++ if (dev->vbi_dev) { ++ if (video_is_registered(dev->vbi_dev)) ++ video_unregister_device(dev->vbi_dev); ++ else ++ video_device_release(dev->vbi_dev); ++ dev->vbi_dev = NULL; ++ } ++ if (dev->radio_dev) { ++ if (video_is_registered(dev->radio_dev)) ++ video_unregister_device(dev->radio_dev); ++ else ++ video_device_release(dev->radio_dev); ++ dev->radio_dev = NULL; ++ } ++} ++ ++static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops, ++ struct saa7134_dev *dev) ++{ ++ int err; ++ ++ if (NULL != dev->mops) ++ return; ++ if (saa7134_boards[dev->board].mpeg != ops->type) ++ return; ++ err = ops->init(dev); ++ if (0 != err) ++ return; ++ dev->mops = ops; ++} ++ ++static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops, ++ struct saa7134_dev *dev) ++{ ++ if (NULL == dev->mops) ++ return; ++ if (dev->mops != ops) ++ return; ++ dev->mops->fini(dev); ++ dev->mops = NULL; ++} ++ ++static int __devinit saa7134_initdev(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct saa7134_dev *dev; ++ struct saa7134_mpeg_ops *mops; ++ int err; ++ ++ if (saa7134_devcount == SAA7134_MAXBOARDS) ++ return -ENOMEM; ++ ++ dev = kzalloc(sizeof(*dev),GFP_KERNEL); ++ if (NULL == dev) ++ return -ENOMEM; ++ ++ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); ++ if (err) ++ goto fail0; ++ ++ /* pci init */ ++ dev->pci = pci_dev; ++ if (pci_enable_device(pci_dev)) { ++ err = -EIO; ++ goto fail1; ++ } ++ ++ dev->nr = saa7134_devcount; ++ sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr); ++ ++ /* pci quirks */ ++ if (pci_pci_problems) { ++ if (pci_pci_problems & PCIPCI_TRITON) ++ printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name); ++ if (pci_pci_problems & PCIPCI_NATOMA) ++ printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name); ++ if (pci_pci_problems & PCIPCI_VIAETBF) ++ printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name); ++ if (pci_pci_problems & PCIPCI_VSFX) ++ printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name); ++#ifdef PCIPCI_ALIMAGIK ++ if (pci_pci_problems & PCIPCI_ALIMAGIK) { ++ printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", ++ dev->name); ++ latency = 0x0A; ++ } ++#endif ++ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) { ++ printk(KERN_INFO "%s: quirk: this driver and your " ++ "chipset may not work together" ++ " in overlay mode.\n",dev->name); ++ if (!saa7134_no_overlay) { ++ printk(KERN_INFO "%s: quirk: overlay " ++ "mode will be disabled.\n", ++ dev->name); ++ saa7134_no_overlay = 1; ++ } else { ++ printk(KERN_INFO "%s: quirk: overlay " ++ "mode will be forced. Use this" ++ " option at your own risk.\n", ++ dev->name); ++ } ++ } ++ } ++ if (UNSET != latency) { ++ printk(KERN_INFO "%s: setting pci latency timer to %d\n", ++ dev->name,latency); ++ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); ++ } ++ ++ /* print pci info */ ++ dev->pci_rev = pci_dev->revision; ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); ++ printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", dev->name, ++ pci_name(pci_dev), dev->pci_rev, pci_dev->irq, ++ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); ++ pci_set_master(pci_dev); ++ if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { ++ printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); ++ err = -EIO; ++ goto fail1; ++ } ++ ++ /* board config */ ++ dev->board = pci_id->driver_data; ++ if ((unsigned)card[dev->nr] < saa7134_bcount) ++ dev->board = card[dev->nr]; ++ if (SAA7134_BOARD_UNKNOWN == dev->board) ++ must_configure_manually(0); ++ else if (SAA7134_BOARD_NOAUTO == dev->board) { ++ must_configure_manually(1); ++ dev->board = SAA7134_BOARD_UNKNOWN; ++ } ++ dev->autodetected = card[dev->nr] != dev->board; ++ dev->tuner_type = saa7134_boards[dev->board].tuner_type; ++ dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; ++ dev->radio_type = saa7134_boards[dev->board].radio_type; ++ dev->radio_addr = saa7134_boards[dev->board].radio_addr; ++ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; ++ if (UNSET != tuner[dev->nr]) ++ dev->tuner_type = tuner[dev->nr]; ++ printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", ++ dev->name,pci_dev->subsystem_vendor, ++ pci_dev->subsystem_device,saa7134_boards[dev->board].name, ++ dev->board, dev->autodetected ? ++ "autodetected" : "insmod option"); ++ ++ /* get mmio */ ++ if (!request_mem_region(pci_resource_start(pci_dev,0), ++ pci_resource_len(pci_dev,0), ++ dev->name)) { ++ err = -EBUSY; ++ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n", ++ dev->name,(unsigned long long)pci_resource_start(pci_dev,0)); ++ goto fail1; ++ } ++ dev->lmmio = ioremap(pci_resource_start(pci_dev, 0), ++ pci_resource_len(pci_dev, 0)); ++ dev->bmmio = (__u8 __iomem *)dev->lmmio; ++ if (NULL == dev->lmmio) { ++ err = -EIO; ++ printk(KERN_ERR "%s: can't ioremap() MMIO memory\n", ++ dev->name); ++ goto fail2; ++ } ++ ++ /* initialize hardware #1 */ ++ saa7134_board_init1(dev); ++ saa7134_hwinit1(dev); ++ ++ /* get irq */ ++ err = request_irq(pci_dev->irq, saa7134_irq, ++ IRQF_SHARED | IRQF_DISABLED, dev->name, dev); ++ if (err < 0) { ++ printk(KERN_ERR "%s: can't get IRQ %d\n", ++ dev->name,pci_dev->irq); ++ goto fail3; ++ } ++ ++ /* wait a bit, register i2c bus */ ++ msleep(100); ++ saa7134_i2c_register(dev); ++ saa7134_board_init2(dev); ++ ++ saa7134_hwinit2(dev); ++ ++ /* load i2c helpers */ ++ if (card_is_empress(dev)) { ++ struct v4l2_subdev *sd = ++ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, ++ "saa6752hs", ++ saa7134_boards[dev->board].empress_addr, NULL); ++ ++ if (sd) ++ sd->grp_id = GRP_EMPRESS; ++ } ++ ++ if (saa7134_boards[dev->board].rds_addr) { ++ struct v4l2_subdev *sd; ++ ++ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, ++ &dev->i2c_adap, "saa6588", ++ 0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr)); ++ if (sd) { ++ printk(KERN_INFO "%s: found RDS decoder\n", dev->name); ++ dev->has_rds = 1; ++ } ++ } ++ ++ v4l2_prio_init(&dev->prio); ++ ++ mutex_lock(&saa7134_devlist_lock); ++ list_for_each_entry(mops, &mops_list, next) ++ mpeg_ops_attach(mops, dev); ++ list_add_tail(&dev->devlist, &saa7134_devlist); ++ mutex_unlock(&saa7134_devlist_lock); ++ ++ /* check for signal */ ++ saa7134_irq_video_signalchange(dev); ++ ++ if (TUNER_ABSENT != dev->tuner_type) ++ saa_call_all(dev, core, s_power, 0); ++ ++ /* register v4l devices */ ++ if (saa7134_no_overlay > 0) ++ printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); ++ ++ dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); ++ err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, ++ video_nr[dev->nr]); ++ if (err < 0) { ++ printk(KERN_INFO "%s: can't register video device\n", ++ dev->name); ++ goto fail4; ++ } ++ printk(KERN_INFO "%s: registered device %s [v4l2]\n", ++ dev->name, video_device_node_name(dev->video_dev)); ++ ++ dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); ++ ++ err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, ++ vbi_nr[dev->nr]); ++ if (err < 0) ++ goto fail4; ++ printk(KERN_INFO "%s: registered device %s\n", ++ dev->name, video_device_node_name(dev->vbi_dev)); ++ ++ if (card_has_radio(dev)) { ++ dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio"); ++ err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, ++ radio_nr[dev->nr]); ++ if (err < 0) ++ goto fail4; ++ printk(KERN_INFO "%s: registered device %s\n", ++ dev->name, video_device_node_name(dev->radio_dev)); ++ } ++ ++ /* everything worked */ ++ saa7134_devcount++; ++ ++ if (saa7134_dmasound_init && !dev->dmasound.priv_data) ++ saa7134_dmasound_init(dev); ++ ++ request_submodules(dev); ++ return 0; ++ ++ fail4: ++ saa7134_unregister_video(dev); ++ saa7134_i2c_unregister(dev); ++ free_irq(pci_dev->irq, dev); ++ fail3: ++ saa7134_hwfini(dev); ++ iounmap(dev->lmmio); ++ fail2: ++ release_mem_region(pci_resource_start(pci_dev,0), ++ pci_resource_len(pci_dev,0)); ++ fail1: ++ v4l2_device_unregister(&dev->v4l2_dev); ++ fail0: ++ kfree(dev); ++ return err; ++} ++ ++static void __devexit saa7134_finidev(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); ++ struct saa7134_mpeg_ops *mops; ++ ++ flush_request_submodules(dev); ++ ++ /* Release DMA sound modules if present */ ++ if (saa7134_dmasound_exit && dev->dmasound.priv_data) { ++ saa7134_dmasound_exit(dev); ++ } ++ ++ /* debugging ... */ ++ if (irq_debug) { ++ u32 report = saa_readl(SAA7134_IRQ_REPORT); ++ u32 status = saa_readl(SAA7134_IRQ_STATUS); ++ print_irqstatus(dev,42,report,status); ++ } ++ ++ /* disable peripheral devices */ ++ saa_writeb(SAA7134_SPECIAL_MODE,0); ++ ++ /* shutdown hardware */ ++ saa_writel(SAA7134_IRQ1,0); ++ saa_writel(SAA7134_IRQ2,0); ++ saa_writel(SAA7134_MAIN_CTRL,0); ++ ++ /* shutdown subsystems */ ++ saa7134_hwfini(dev); ++ ++ /* unregister */ ++ mutex_lock(&saa7134_devlist_lock); ++ list_del(&dev->devlist); ++ list_for_each_entry(mops, &mops_list, next) ++ mpeg_ops_detach(mops, dev); ++ mutex_unlock(&saa7134_devlist_lock); ++ saa7134_devcount--; ++ ++ saa7134_i2c_unregister(dev); ++ saa7134_unregister_video(dev); ++ ++ ++ /* the DMA sound modules should be unloaded before reaching ++ this, but just in case they are still present... */ ++ if (dev->dmasound.priv_data != NULL) { ++ free_irq(pci_dev->irq, &dev->dmasound); ++ dev->dmasound.priv_data = NULL; ++ } ++ ++ ++ /* release resources */ ++ free_irq(pci_dev->irq, dev); ++ iounmap(dev->lmmio); ++ release_mem_region(pci_resource_start(pci_dev,0), ++ pci_resource_len(pci_dev,0)); ++ ++ ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++ /* free memory */ ++ kfree(dev); ++} ++ ++#ifdef CONFIG_PM ++ ++/* resends a current buffer in queue after resume */ ++static int saa7134_buffer_requeue(struct saa7134_dev *dev, ++ struct saa7134_dmaqueue *q) ++{ ++ struct saa7134_buf *buf, *next; ++ ++ assert_spin_locked(&dev->slock); ++ ++ buf = q->curr; ++ next = buf; ++ dprintk("buffer_requeue\n"); ++ ++ if (!buf) ++ return 0; ++ ++ dprintk("buffer_requeue : resending active buffers \n"); ++ ++ if (!list_empty(&q->queue)) ++ next = list_entry(q->queue.next, struct saa7134_buf, ++ vb.queue); ++ buf->activate(dev, buf, next); ++ ++ return 0; ++} ++ ++static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); ++ ++ /* disable overlay - apps should enable it explicitly on resume*/ ++ dev->ovenable = 0; ++ ++ /* Disable interrupts, DMA, and rest of the chip*/ ++ saa_writel(SAA7134_IRQ1, 0); ++ saa_writel(SAA7134_IRQ2, 0); ++ saa_writel(SAA7134_MAIN_CTRL, 0); ++ ++ dev->insuspend = 1; ++ synchronize_irq(pci_dev->irq); ++ ++ /* ACK interrupts once more, just in case, ++ since the IRQ handler won't ack them anymore*/ ++ ++ saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT)); ++ ++ /* Disable timeout timers - if we have active buffers, we will ++ fill them on resume*/ ++ ++ del_timer(&dev->video_q.timeout); ++ del_timer(&dev->vbi_q.timeout); ++ del_timer(&dev->ts_q.timeout); ++ ++ if (dev->remote) ++ saa7134_ir_stop(dev); ++ ++ pci_save_state(pci_dev); ++ pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); ++ ++ return 0; ++} ++ ++static int saa7134_resume(struct pci_dev *pci_dev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); ++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); ++ unsigned long flags; ++ ++ pci_set_power_state(pci_dev, PCI_D0); ++ pci_restore_state(pci_dev); ++ ++ /* Do things that are done in saa7134_initdev , ++ except of initializing memory structures.*/ ++ ++ saa7134_board_init1(dev); ++ ++ /* saa7134_hwinit1 */ ++ if (saa7134_boards[dev->board].video_out) ++ saa7134_videoport_init(dev); ++ if (card_has_mpeg(dev)) ++ saa7134_ts_init_hw(dev); ++ if (dev->remote) ++ saa7134_ir_start(dev); ++ saa7134_hw_enable1(dev); ++ ++ msleep(100); ++ ++ saa7134_board_init2(dev); ++ ++ /*saa7134_hwinit2*/ ++ saa7134_set_tvnorm_hw(dev); ++ saa7134_tvaudio_setmute(dev); ++ saa7134_tvaudio_setvolume(dev, dev->ctl_volume); ++ saa7134_tvaudio_init(dev); ++ saa7134_enable_i2s(dev); ++ saa7134_hw_enable2(dev); ++ ++ saa7134_irq_video_signalchange(dev); ++ ++ /*resume unfinished buffer(s)*/ ++ spin_lock_irqsave(&dev->slock, flags); ++ saa7134_buffer_requeue(dev, &dev->video_q); ++ saa7134_buffer_requeue(dev, &dev->vbi_q); ++ saa7134_buffer_requeue(dev, &dev->ts_q); ++ ++ /* FIXME: Disable DMA audio sound - temporary till proper support ++ is implemented*/ ++ ++ dev->dmasound.dma_running = 0; ++ ++ /* start DMA now*/ ++ dev->insuspend = 0; ++ smp_wmb(); ++ saa7134_set_dmabits(dev); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ ++ return 0; ++} ++#endif ++ ++/* ----------------------------------------------------------- */ ++ ++int saa7134_ts_register(struct saa7134_mpeg_ops *ops) ++{ ++ struct saa7134_dev *dev; ++ ++ mutex_lock(&saa7134_devlist_lock); ++ list_for_each_entry(dev, &saa7134_devlist, devlist) ++ mpeg_ops_attach(ops, dev); ++ list_add_tail(&ops->next,&mops_list); ++ mutex_unlock(&saa7134_devlist_lock); ++ return 0; ++} ++ ++void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops) ++{ ++ struct saa7134_dev *dev; ++ ++ mutex_lock(&saa7134_devlist_lock); ++ list_del(&ops->next); ++ list_for_each_entry(dev, &saa7134_devlist, devlist) ++ mpeg_ops_detach(ops, dev); ++ mutex_unlock(&saa7134_devlist_lock); ++} ++ ++EXPORT_SYMBOL(saa7134_ts_register); ++EXPORT_SYMBOL(saa7134_ts_unregister); ++ ++/* ----------------------------------------------------------- */ ++ ++static struct pci_driver saa7134_pci_driver = { ++ .name = "saa7134", ++ .id_table = saa7134_pci_tbl, ++ .probe = saa7134_initdev, ++ .remove = __devexit_p(saa7134_finidev), ++#ifdef CONFIG_PM ++ .suspend = saa7134_suspend, ++ .resume = saa7134_resume ++#endif ++}; ++ ++static int __init saa7134_init(void) ++{ ++ INIT_LIST_HEAD(&saa7134_devlist); ++ printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n", ++ SAA7134_VERSION); ++ return pci_register_driver(&saa7134_pci_driver); ++} ++ ++static void __exit saa7134_fini(void) ++{ ++ pci_unregister_driver(&saa7134_pci_driver); ++} ++ ++module_init(saa7134_init); ++module_exit(saa7134_fini); ++ ++/* ----------------------------------------------------------- */ ++ ++EXPORT_SYMBOL(saa7134_set_gpio); ++EXPORT_SYMBOL(saa7134_boards); ++ ++/* ----------------- for the DMA sound modules --------------- */ ++ ++EXPORT_SYMBOL(saa7134_dmasound_init); ++EXPORT_SYMBOL(saa7134_dmasound_exit); ++EXPORT_SYMBOL(saa7134_pgtable_free); ++EXPORT_SYMBOL(saa7134_pgtable_build); ++EXPORT_SYMBOL(saa7134_pgtable_alloc); ++EXPORT_SYMBOL(saa7134_set_dmabits); ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c +new file mode 100644 +index 0000000..27915e5 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-dvb.c +@@ -0,0 +1,1939 @@ ++/* ++ * ++ * (c) 2004 Gerd Knorr [SuSE Labs] ++ * ++ * Extended 3 / 2005 by Hartmut Hackmann to support various ++ * cards with the tda10046 DVB-T channel decoder ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++#include ++#include "dvb-pll.h" ++#include ++ ++#include "mt352.h" ++#include "mt352_priv.h" /* FIXME */ ++#include "tda1004x.h" ++#include "nxt200x.h" ++#include "tuner-xc2028.h" ++#include "xc5000.h" ++ ++#include "tda10086.h" ++#include "tda826x.h" ++#include "tda827x.h" ++#include "isl6421.h" ++#include "isl6405.h" ++#include "lnbp21.h" ++#include "tuner-simple.h" ++#include "tda10048.h" ++#include "tda18271.h" ++#include "lgdt3305.h" ++#include "tda8290.h" ++#include "mb86a20s.h" ++#include "lgs8gxx.h" ++ ++#include "zl10353.h" ++#include "qt1010.h" ++ ++#include "zl10036.h" ++#include "zl10039.h" ++#include "mt312.h" ++#include "s5h1411.h" ++ ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int antenna_pwr; ++ ++module_param(antenna_pwr, int, 0444); ++MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); ++ ++static int use_frontend; ++module_param(use_frontend, int, 0644); ++MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define dprintk(fmt, arg...) do { if (debug) \ ++ printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) ++ ++/* Print a warning */ ++#define wprintk(fmt, arg...) \ ++ printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg) ++ ++/* ------------------------------------------------------------------ ++ * mt352 based DVB-T cards ++ */ ++ ++static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) ++{ ++ u32 ok; ++ ++ if (!on) { ++ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26)); ++ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); ++ return 0; ++ } ++ ++ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26)); ++ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); ++ udelay(10); ++ ++ saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28)); ++ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); ++ udelay(10); ++ saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); ++ udelay(10); ++ ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); ++ dprintk("%s %s\n", __func__, ok ? "on" : "off"); ++ ++ if (!ok) ++ saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); ++ return ok; ++} ++ ++static int mt352_pinnacle_init(struct dvb_frontend* fe) ++{ ++ static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 }; ++ static u8 reset [] = { RESET, 0x80 }; ++ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; ++ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; ++ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 }; ++ static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 }; ++ static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f }; ++ static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d }; ++ static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; ++ struct saa7134_dev *dev= fe->dvb->priv; ++ ++ dprintk("%s called\n", __func__); ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); ++ ++ mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg)); ++ mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg)); ++ mt352_write(fe, irq_cfg, sizeof(irq_cfg)); ++ ++ return 0; ++} ++ ++static int mt352_aver777_init(struct dvb_frontend* fe) ++{ ++ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; ++ static u8 reset [] = { RESET, 0x80 }; ++ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; ++ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; ++ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ ++ return 0; ++} ++ ++static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe) ++{ ++ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; ++ static u8 reset [] = { RESET, 0x80 }; ++ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; ++ static u8 agc_cfg [] = { AGC_TARGET, 0xe }; ++ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; ++ ++ mt352_write(fe, clock_config, sizeof(clock_config)); ++ udelay(200); ++ mt352_write(fe, reset, sizeof(reset)); ++ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); ++ mt352_write(fe, agc_cfg, sizeof(agc_cfg)); ++ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); ++ return 0; ++} ++ ++static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u8 off[] = { 0x00, 0xf1}; ++ u8 on[] = { 0x00, 0x71}; ++ struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)}; ++ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct v4l2_frequency f; ++ ++ /* set frequency (mt2050) */ ++ f.tuner = 0; ++ f.type = V4L2_TUNER_DIGITAL_TV; ++ f.frequency = c->frequency / 1000 * 16 / 1000; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ saa_call_all(dev, tuner, s_frequency, &f); ++ msg.buf = on; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ ++ pinnacle_antenna_pwr(dev, antenna_pwr); ++ ++ /* mt352 setup */ ++ return mt352_pinnacle_init(fe); ++} ++ ++static struct mt352_config pinnacle_300i = { ++ .demod_address = 0x3c >> 1, ++ .adc_clock = 20333, ++ .if2 = 36150, ++ .no_tuner = 1, ++ .demod_init = mt352_pinnacle_init, ++}; ++ ++static struct mt352_config avermedia_777 = { ++ .demod_address = 0xf, ++ .demod_init = mt352_aver777_init, ++}; ++ ++static struct mt352_config avermedia_xc3028_mt352_dev = { ++ .demod_address = (0x1e >> 1), ++ .no_tuner = 1, ++ .demod_init = mt352_avermedia_xc3028_init, ++}; ++ ++static struct tda18271_std_map mb86a20s_tda18271_std_map = { ++ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, ++ .if_lvl = 7, .rfagc_top = 0x37, }, ++}; ++ ++static struct tda18271_config kworld_tda18271_config = { ++ .std_map = &mb86a20s_tda18271_std_map, ++ .gate = TDA18271_GATE_DIGITAL, ++ .config = 3, /* Use tuner callback for AGC */ ++ ++}; ++ ++static const struct mb86a20s_config kworld_mb86a20s_config = { ++ .demod_address = 0x10, ++}; ++ ++static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ ++ unsigned char initmsg[] = {0x45, 0x97}; ++ unsigned char msg_enable[] = {0x45, 0xc1}; ++ unsigned char msg_disable[] = {0x45, 0x81}; ++ struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2}; ++ ++ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { ++ wprintk("could not access the I2C gate\n"); ++ return -EIO; ++ } ++ if (enable) ++ msg.buf = msg_enable; ++ else ++ msg.buf = msg_disable; ++ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { ++ wprintk("could not access the I2C gate\n"); ++ return -EIO; ++ } ++ msleep(20); ++ return 0; ++} ++ ++/* ================================================================== ++ * tda1004x based DVB-T cards, helper functions ++ */ ++ ++static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, ++ const struct firmware **fw, char *name) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ return request_firmware(fw, name, &dev->pci->dev); ++} ++ ++/* ------------------------------------------------------------------ ++ * these tuners are tu1216, td1316(a) ++ */ ++ ++static int philips_tda6651_pll_set(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ u8 addr = state->config->tuner_address; ++ u8 tuner_buf[4]; ++ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = ++ sizeof(tuner_buf) }; ++ int tuner_frequency = 0; ++ u8 band, cp, filter; ++ ++ /* determine charge pump */ ++ tuner_frequency = c->frequency + 36166000; ++ if (tuner_frequency < 87000000) ++ return -EINVAL; ++ else if (tuner_frequency < 130000000) ++ cp = 3; ++ else if (tuner_frequency < 160000000) ++ cp = 5; ++ else if (tuner_frequency < 200000000) ++ cp = 6; ++ else if (tuner_frequency < 290000000) ++ cp = 3; ++ else if (tuner_frequency < 420000000) ++ cp = 5; ++ else if (tuner_frequency < 480000000) ++ cp = 6; ++ else if (tuner_frequency < 620000000) ++ cp = 3; ++ else if (tuner_frequency < 830000000) ++ cp = 5; ++ else if (tuner_frequency < 895000000) ++ cp = 7; ++ else ++ return -EINVAL; ++ ++ /* determine band */ ++ if (c->frequency < 49000000) ++ return -EINVAL; ++ else if (c->frequency < 161000000) ++ band = 1; ++ else if (c->frequency < 444000000) ++ band = 2; ++ else if (c->frequency < 861000000) ++ band = 4; ++ else ++ return -EINVAL; ++ ++ /* setup PLL filter */ ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ filter = 0; ++ break; ++ ++ case 7000000: ++ filter = 0; ++ break; ++ ++ case 8000000: ++ filter = 1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ /* calculate divisor ++ * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) ++ */ ++ tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; ++ ++ /* setup tuner buffer */ ++ tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; ++ tuner_buf[1] = tuner_frequency & 0xff; ++ tuner_buf[2] = 0xca; ++ tuner_buf[3] = (cp << 5) | (filter << 3) | band; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { ++ wprintk("could not write to tuner at addr: 0x%02x\n", ++ addr << 1); ++ return -EIO; ++ } ++ msleep(1); ++ return 0; ++} ++ ++static int philips_tu1216_init(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ u8 addr = state->config->tuner_address; ++ static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; ++ struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; ++ ++ /* setup PLL configuration */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ msleep(1); ++ ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct tda1004x_config philips_tu1216_60_config = { ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config philips_tu1216_61_config = { ++ ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static int philips_td1316_tuner_init(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ u8 addr = state->config->tuner_address; ++ static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; ++ struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; ++ ++ /* setup PLL configuration */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static int philips_td1316_tuner_set_params(struct dvb_frontend *fe) ++{ ++ return philips_tda6651_pll_set(fe); ++} ++ ++static int philips_td1316_tuner_sleep(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ u8 addr = state->config->tuner_address; ++ static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; ++ struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; ++ ++ /* switch the tuner to analog mode */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int philips_europa_tuner_init(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ static u8 msg[] = { 0x00, 0x40}; ++ struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; ++ ++ ++ if (philips_td1316_tuner_init(fe)) ++ return -EIO; ++ msleep(1); ++ if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int philips_europa_tuner_sleep(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ ++ static u8 msg[] = { 0x00, 0x14 }; ++ struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; ++ ++ if (philips_td1316_tuner_sleep(fe)) ++ return -EIO; ++ ++ /* switch the board to analog mode */ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(&dev->i2c_adap, &analog_msg, 1); ++ return 0; ++} ++ ++static int philips_europa_demod_sleep(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ ++ if (dev->original_demod_sleep) ++ dev->original_demod_sleep(fe); ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ return 0; ++} ++ ++static struct tda1004x_config philips_europa_config = { ++ ++ .demod_address = 0x8, ++ .invert = 0, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_IFO_AUTO_POS, ++ .if_freq = TDA10046_FREQ_052, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config medion_cardbus = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_IFO_AUTO_NEG, ++ .if_freq = TDA10046_FREQ_3613, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config technotrend_budget_t3000_config = { ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .tuner_address = 0x63, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++/* ------------------------------------------------------------------ ++ * tda 1004x based cards with philips silicon tuner ++ */ ++ ++static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) ++{ ++ struct tda1004x_state *state = fe->demodulator_priv; ++ ++ u8 addr = state->config->i2c_gate; ++ static u8 tda8290_close[] = { 0x21, 0xc0}; ++ static u8 tda8290_open[] = { 0x21, 0x80}; ++ struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2}; ++ if (enable) { ++ tda8290_msg.buf = tda8290_close; ++ } else { ++ tda8290_msg.buf = tda8290_open; ++ } ++ if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) { ++ struct saa7134_dev *dev = fe->dvb->priv; ++ wprintk("could not access tda8290 I2C gate\n"); ++ return -EIO; ++ } ++ msleep(20); ++ return 0; ++} ++ ++static int philips_tda827x_tuner_init(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ ++ switch (state->config->antenna_switch) { ++ case 0: break; ++ case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); ++ saa7134_set_gpio(dev, 21, 0); ++ break; ++ case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); ++ saa7134_set_gpio(dev, 21, 1); ++ break; ++ } ++ return 0; ++} ++ ++static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ struct tda1004x_state *state = fe->demodulator_priv; ++ ++ switch (state->config->antenna_switch) { ++ case 0: break; ++ case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); ++ saa7134_set_gpio(dev, 21, 1); ++ break; ++ case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); ++ saa7134_set_gpio(dev, 21, 0); ++ break; ++ } ++ return 0; ++} ++ ++static int configure_tda827x_fe(struct saa7134_dev *dev, ++ struct tda1004x_config *cdec_conf, ++ struct tda827x_config *tuner_conf) ++{ ++ struct videobuf_dvb_frontend *fe0; ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); ++ ++ if (!fe0) ++ return -EINVAL; ++ ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (cdec_conf->i2c_gate) ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; ++ if (dvb_attach(tda827x_attach, fe0->dvb.frontend, ++ cdec_conf->tuner_address, ++ &dev->i2c_adap, tuner_conf)) ++ return 0; ++ ++ wprintk("no tda827x tuner found at addr: %02x\n", ++ cdec_conf->tuner_address); ++ } ++ return -EINVAL; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct tda827x_config tda827x_cfg_0 = { ++ .init = philips_tda827x_tuner_init, ++ .sleep = philips_tda827x_tuner_sleep, ++ .config = 0, ++ .switch_addr = 0 ++}; ++ ++static struct tda827x_config tda827x_cfg_1 = { ++ .init = philips_tda827x_tuner_init, ++ .sleep = philips_tda827x_tuner_sleep, ++ .config = 1, ++ .switch_addr = 0x4b ++}; ++ ++static struct tda827x_config tda827x_cfg_2 = { ++ .init = philips_tda827x_tuner_init, ++ .sleep = philips_tda827x_tuner_sleep, ++ .config = 2, ++ .switch_addr = 0x4b ++}; ++ ++static struct tda827x_config tda827x_cfg_2_sw42 = { ++ .init = philips_tda827x_tuner_init, ++ .sleep = philips_tda827x_tuner_sleep, ++ .config = 2, ++ .switch_addr = 0x42 ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct tda1004x_config tda827x_lifeview_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config philips_tiger_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config cinergy_ht_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config cinergy_ht_pci_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config philips_tiger_s_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config pinnacle_pctv_310i_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config hauppauge_hvr_1110_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config asus_p7131_dual_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 2, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config lifeview_trio_config = { ++ .demod_address = 0x09, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP00_I, ++ .if_freq = TDA10046_FREQ_045, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config tevion_dvbt220rf_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config md8800_dvbt_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x60, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config asus_p7131_4871_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 2, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config asus_p7131_hybrid_lna_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 2, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config kworld_dvb_t_210_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch= 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config avermedia_super_007_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x60, ++ .antenna_switch= 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config twinhan_dtv_dvb_3056_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP01_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x42, ++ .tuner_address = 0x61, ++ .antenna_switch = 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config asus_tiger_3in1_config = { ++ .demod_address = 0x0b, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch = 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct tda1004x_config asus_ps3_100_config = { ++ .demod_address = 0x0b, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP11_I, ++ .if_freq = TDA10046_FREQ_045, ++ .i2c_gate = 0x4b, ++ .tuner_address = 0x61, ++ .antenna_switch = 1, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++/* ------------------------------------------------------------------ ++ * special case: this card uses saa713x GPIO22 for the mode switch ++ */ ++ ++static int ads_duo_tuner_init(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ philips_tda827x_tuner_init(fe); ++ /* route TDA8275a AGC input to the channel decoder */ ++ saa7134_set_gpio(dev, 22, 1); ++ return 0; ++} ++ ++static int ads_duo_tuner_sleep(struct dvb_frontend *fe) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ /* route TDA8275a AGC input to the analog IF chip*/ ++ saa7134_set_gpio(dev, 22, 0); ++ philips_tda827x_tuner_sleep(fe); ++ return 0; ++} ++ ++static struct tda827x_config ads_duo_cfg = { ++ .init = ads_duo_tuner_init, ++ .sleep = ads_duo_tuner_sleep, ++ .config = 0 ++}; ++ ++static struct tda1004x_config ads_tech_duo_config = { ++ .demod_address = 0x08, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_16M, ++ .agc_config = TDA10046_AGC_TDA827X, ++ .gpio_config = TDA10046_GP00_I, ++ .if_freq = TDA10046_FREQ_045, ++ .tuner_address = 0x61, ++ .request_firmware = philips_tda1004x_request_firmware ++}; ++ ++static struct zl10353_config behold_h6_config = { ++ .demod_address = 0x1e>>1, ++ .no_tuner = 1, ++ .parallel_ts = 1, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static struct xc5000_config behold_x7_tunerconfig = { ++ .i2c_address = 0xc2>>1, ++ .if_khz = 4560, ++ .radio_input = XC5000_RADIO_FM1, ++}; ++ ++static struct zl10353_config behold_x7_config = { ++ .demod_address = 0x1e>>1, ++ .if2 = 45600, ++ .no_tuner = 1, ++ .parallel_ts = 1, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static struct zl10353_config videomate_t750_zl10353_config = { ++ .demod_address = 0x0f, ++ .no_tuner = 1, ++ .parallel_ts = 1, ++ .disable_i2c_gate_ctrl = 1, ++}; ++ ++static struct qt1010_config videomate_t750_qt1010_config = { ++ .i2c_address = 0x62 ++}; ++ ++ ++/* ================================================================== ++ * tda10086 based DVB-S cards, helper functions ++ */ ++ ++static struct tda10086_config flydvbs = { ++ .demod_address = 0x0e, ++ .invert = 0, ++ .diseqc_tone = 0, ++ .xtal_freq = TDA10086_XTAL_16M, ++}; ++ ++static struct tda10086_config sd1878_4m = { ++ .demod_address = 0x0e, ++ .invert = 0, ++ .diseqc_tone = 0, ++ .xtal_freq = TDA10086_XTAL_4M, ++}; ++ ++/* ------------------------------------------------------------------ ++ * special case: lnb supply is connected to the gated i2c ++ */ ++ ++static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ int res = -EIO; ++ struct saa7134_dev *dev = fe->dvb->priv; ++ if (fe->ops.i2c_gate_ctrl) { ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (dev->original_set_voltage) ++ res = dev->original_set_voltage(fe, voltage); ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ return res; ++}; ++ ++static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg) ++{ ++ int res = -EIO; ++ struct saa7134_dev *dev = fe->dvb->priv; ++ if (fe->ops.i2c_gate_ctrl) { ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (dev->original_set_high_voltage) ++ res = dev->original_set_high_voltage(fe, arg); ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ return res; ++}; ++ ++static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ u8 wbuf[2] = { 0x1f, 00 }; ++ u8 rbuf; ++ struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 }, ++ { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } }; ++ ++ if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2) ++ return -EIO; ++ /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */ ++ if (voltage == SEC_VOLTAGE_18) ++ wbuf[1] = rbuf | 0x10; ++ else ++ wbuf[1] = rbuf & 0xef; ++ msg[0].len = 2; ++ i2c_transfer(&dev->i2c_adap, msg, 1); ++ return 0; ++} ++ ++static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg) ++{ ++ struct saa7134_dev *dev = fe->dvb->priv; ++ wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__); ++ return -EIO; ++} ++ ++/* ================================================================== ++ * nxt200x based ATSC cards, helper functions ++ */ ++ ++static struct nxt200x_config avertvhda180 = { ++ .demod_address = 0x0a, ++}; ++ ++static struct nxt200x_config kworldatsc110 = { ++ .demod_address = 0x0a, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct mt312_config avertv_a700_mt312 = { ++ .demod_address = 0x0e, ++ .voltage_inverted = 1, ++}; ++ ++static struct zl10036_config avertv_a700_tuner = { ++ .tuner_address = 0x60, ++}; ++ ++static struct mt312_config zl10313_compro_s350_config = { ++ .demod_address = 0x0e, ++}; ++ ++static struct lgdt3305_config hcw_lgdt3305_config = { ++ .i2c_addr = 0x0e, ++ .mpeg_mode = LGDT3305_MPEG_SERIAL, ++ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, ++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, ++ .deny_i2c_rptr = 1, ++ .spectral_inversion = 1, ++ .qam_if_khz = 4000, ++ .vsb_if_khz = 3250, ++}; ++ ++static struct tda10048_config hcw_tda10048_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_SERIAL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3500, ++ .dtv8_if_freq_khz = TDA10048_IF_4000, ++ .clk_freq_khz = TDA10048_CLK_16000, ++ .disable_gate_access = 1, ++}; ++ ++static struct tda18271_std_map hauppauge_tda18271_std_map = { ++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, ++ .if_lvl = 1, .rfagc_top = 0x58, }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, ++ .if_lvl = 1, .rfagc_top = 0x58, }, ++}; ++ ++static struct tda18271_config hcw_tda18271_config = { ++ .std_map = &hauppauge_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .config = 3, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct tda829x_config tda829x_no_probe = { ++ .probe_tuner = TDA829X_DONT_PROBE, ++}; ++ ++static struct tda10048_config zolid_tda10048_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_PARALLEL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3500, ++ .dtv8_if_freq_khz = TDA10048_IF_4000, ++ .clk_freq_khz = TDA10048_CLK_16000, ++ .disable_gate_access = 1, ++}; ++ ++static struct tda18271_config zolid_tda18271_config = { ++ .gate = TDA18271_GATE_ANALOG, ++}; ++ ++static struct tda10048_config dtv1000s_tda10048_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_PARALLEL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3800, ++ .dtv8_if_freq_khz = TDA10048_IF_4300, ++ .clk_freq_khz = TDA10048_CLK_16000, ++ .disable_gate_access = 1, ++}; ++ ++static struct tda18271_std_map dtv1000s_tda18271_std_map = { ++ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, ++ .if_lvl = 1, .rfagc_top = 0x37, }, ++}; ++ ++static struct tda18271_config dtv1000s_tda18271_config = { ++ .std_map = &dtv1000s_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++}; ++ ++static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = { ++ .prod = LGS8GXX_PROD_LGS8G75, ++ .demod_address = 0x1d, ++ .serial_ts = 0, ++ .ts_clk_pol = 1, ++ .ts_clk_gated = 0, ++ .if_clk_freq = 30400, /* 30.4 MHz */ ++ .if_freq = 4000, /* 4.00 MHz */ ++ .if_neg_center = 0, ++ .ext_adc = 0, ++ .adc_signed = 1, ++ .adc_vpp = 3, /* 2.0 Vpp */ ++ .if_neg_edge = 1, ++}; ++ ++static struct tda18271_config prohdtv_pro2_tda18271_config = { ++ .gate = TDA18271_GATE_ANALOG, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++}; ++ ++static struct tda18271_std_map kworld_tda18271_std_map = { ++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++}; ++ ++static struct tda18271_config kworld_pc150u_tda18271_config = { ++ .std_map = &kworld_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++ .config = 3, /* Use tuner callback for AGC */ ++ .rf_cal_on_startup = 1 ++}; ++ ++static struct s5h1411_config kworld_s5h1411_config = { ++ .output_mode = S5H1411_PARALLEL_OUTPUT, ++ .gpio = S5H1411_GPIO_OFF, ++ .qam_if = S5H1411_IF_4000, ++ .vsb_if = S5H1411_IF_3250, ++ .inversion = S5H1411_INVERSION_ON, ++ .status_mode = S5H1411_DEMODLOCKING, ++ .mpeg_timing = ++ S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++ ++/* ================================================================== ++ * Core code ++ */ ++ ++static int dvb_init(struct saa7134_dev *dev) ++{ ++ int ret; ++ int attach_xc3028 = 0; ++ struct videobuf_dvb_frontend *fe0; ++ ++ /* FIXME: add support for multi-frontend */ ++ mutex_init(&dev->frontends.lock); ++ INIT_LIST_HEAD(&dev->frontends.felist); ++ ++ printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); ++ fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1); ++ if (!fe0) { ++ printk(KERN_ERR "%s() failed to alloc\n", __func__); ++ return -ENOMEM; ++ } ++ ++ /* init struct videobuf_dvb */ ++ dev->ts.nr_bufs = 32; ++ dev->ts.nr_packets = 32*4; ++ fe0->dvb.name = dev->name; ++ videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_ALTERNATE, ++ sizeof(struct saa7134_buf), ++ dev, NULL); ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: ++ dprintk("pinnacle 300i dvb setup\n"); ++ fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params; ++ } ++ break; ++ case SAA7134_BOARD_AVERMEDIA_777: ++ case SAA7134_BOARD_AVERMEDIA_A16AR: ++ dprintk("avertv 777 dvb setup\n"); ++ fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x61, ++ TUNER_PHILIPS_TD1316); ++ } ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ dprintk("AverMedia A16D dvb setup\n"); ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &avermedia_xc3028_mt352_dev, ++ &dev->i2c_adap); ++ attach_xc3028 = 1; ++ break; ++ case SAA7134_BOARD_MD7134: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &medion_cardbus, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, medion_cardbus.tuner_address, ++ TUNER_PHILIPS_FMD1216ME_MK3); ++ } ++ break; ++ case SAA7134_BOARD_PHILIPS_TOUGH: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &philips_tu1216_60_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; ++ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; ++ } ++ break; ++ case SAA7134_BOARD_FLYDVBTDUO: ++ case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: ++ if (configure_tda827x_fe(dev, &tda827x_lifeview_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_PHILIPS_EUROPA: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_300: ++ case SAA7134_BOARD_ASUS_EUROPA_HYBRID: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &philips_europa_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; ++ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; ++ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; ++ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; ++ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; ++ } ++ break; ++ case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &technotrend_budget_t3000_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; ++ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; ++ fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; ++ fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; ++ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; ++ } ++ break; ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &philips_tu1216_61_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; ++ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; ++ } ++ break; ++ case SAA7134_BOARD_KWORLD_DVBT_210: ++ if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ fe0->dvb.frontend = dvb_attach(tda10048_attach, ++ &hcw_tda10048_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &hcw_tda18271_config); ++ } ++ break; ++ case SAA7134_BOARD_PHILIPS_TIGER: ++ if (configure_tda827x_fe(dev, &philips_tiger_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_PINNACLE_PCTV_310i: ++ if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, ++ &tda827x_cfg_1) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1110: ++ if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, ++ &tda827x_cfg_1) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ fe0->dvb.frontend = dvb_attach(lgdt3305_attach, ++ &hcw_lgdt3305_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &hcw_tda18271_config); ++ } ++ break; ++ case SAA7134_BOARD_ASUSTeK_P7131_DUAL: ++ if (configure_tda827x_fe(dev, &asus_p7131_dual_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_FLYDVBT_LR301: ++ if (configure_tda827x_fe(dev, &tda827x_lifeview_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_FLYDVB_TRIO: ++ if (!use_frontend) { /* terrestrial */ ++ if (configure_tda827x_fe(dev, &lifeview_trio_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ } else { /* satellite */ ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63, ++ &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); ++ goto detach_frontend; ++ } ++ if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, ++ 0x08, 0, 0) == NULL) { ++ wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); ++ goto detach_frontend; ++ } ++ } ++ } ++ break; ++ case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: ++ case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &ads_tech_duo_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda827x_attach,fe0->dvb.frontend, ++ ads_tech_duo_config.tuner_address, &dev->i2c_adap, ++ &ads_duo_cfg) == NULL) { ++ wprintk("no tda827x tuner found at addr: %02x\n", ++ ads_tech_duo_config.tuner_address); ++ goto detach_frontend; ++ } ++ } else ++ wprintk("failed to attach tda10046\n"); ++ break; ++ case SAA7134_BOARD_TEVION_DVBT_220RF: ++ if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_MEDION_MD8800_QUADRO: ++ if (!use_frontend) { /* terrestrial */ ++ if (configure_tda827x_fe(dev, &md8800_dvbt_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ } else { /* satellite */ ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, ++ &flydvbs, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ struct dvb_frontend *fe = fe0->dvb.frontend; ++ u8 dev_id = dev->eedata[2]; ++ u8 data = 0xc4; ++ struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1}; ++ ++ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: Medion Quadro, no tda826x " ++ "found !\n", __func__); ++ goto detach_frontend; ++ } ++ if (dev_id != 0x08) { ++ /* we need to open the i2c gate (we know it exists) */ ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (dvb_attach(isl6405_attach, fe, ++ &dev->i2c_adap, 0x08, 0, 0) == NULL) { ++ wprintk("%s: Medion Quadro, no ISL6405 " ++ "found !\n", __func__); ++ goto detach_frontend; ++ } ++ if (dev_id == 0x07) { ++ /* fire up the 2nd section of the LNB supply since ++ we can't do this from the other section */ ++ msg.buf = &data; ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ } ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ dev->original_set_voltage = fe->ops.set_voltage; ++ fe->ops.set_voltage = md8800_set_voltage; ++ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; ++ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; ++ } else { ++ fe->ops.set_voltage = md8800_set_voltage2; ++ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2; ++ } ++ } ++ } ++ break; ++ case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: ++ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) ++ dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61, ++ NULL, DVB_PLL_TDHU2); ++ break; ++ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: ++ case SAA7134_BOARD_KWORLD_ATSC110: ++ fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x61, ++ TUNER_PHILIPS_TUV1236D); ++ break; ++ case SAA7134_BOARD_KWORLD_PC150U: ++ saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */ ++ saa7134_tuner_callback(dev, 0, ++ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); ++ fe0->dvb.frontend = dvb_attach(s5h1411_attach, ++ &kworld_s5h1411_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &kworld_pc150u_tda18271_config); ++ } ++ break; ++ case SAA7134_BOARD_FLYDVBS_LR300: ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, ++ &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: No tda826x found!\n", __func__); ++ goto detach_frontend; ++ } ++ if (dvb_attach(isl6421_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x08, 0, 0) == NULL) { ++ wprintk("%s: No ISL6421 found!\n", __func__); ++ goto detach_frontend; ++ } ++ } ++ break; ++ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &medion_cardbus, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; ++ fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; ++ ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, medion_cardbus.tuner_address, ++ TUNER_PHILIPS_FMD1216ME_MK3); ++ } ++ break; ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200A: ++ fe0->dvb.frontend = dvb_attach(tda10046_attach, ++ &philips_europa_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init; ++ fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; ++ } ++ break; ++ case SAA7134_BOARD_CINERGY_HT_PCMCIA: ++ if (configure_tda827x_fe(dev, &cinergy_ht_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_CINERGY_HT_PCI: ++ if (configure_tda827x_fe(dev, &cinergy_ht_pci_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_PHILIPS_TIGER_S: ++ if (configure_tda827x_fe(dev, &philips_tiger_s_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_ASUS_P7131_4871: ++ if (configure_tda827x_fe(dev, &asus_p7131_4871_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: ++ if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_SUPER_007: ++ if (configure_tda827x_fe(dev, &avermedia_super_007_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: ++ if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, ++ &tda827x_cfg_2_sw42) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_PHILIPS_SNAKE: ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, ++ &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: No tda826x found!\n", __func__); ++ goto detach_frontend; ++ } ++ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0, 0) == NULL) { ++ wprintk("%s: No lnbp21 found!\n", __func__); ++ goto detach_frontend; ++ } ++ } ++ break; ++ case SAA7134_BOARD_CREATIX_CTX953: ++ if (configure_tda827x_fe(dev, &md8800_dvbt_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_MSI_TVANYWHERE_AD11: ++ if (configure_tda827x_fe(dev, &philips_tiger_s_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ++ dprintk("AverMedia E506R dvb setup\n"); ++ saa7134_set_gpio(dev, 25, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 25, 1); ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &avermedia_xc3028_mt352_dev, ++ &dev->i2c_adap); ++ attach_xc3028 = 1; ++ break; ++ case SAA7134_BOARD_MD7134_BRIDGE_2: ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, ++ &sd1878_4m, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ struct dvb_frontend *fe; ++ if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60, ++ &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) { ++ wprintk("%s: MD7134 DVB-S, no SD1878 " ++ "found !\n", __func__); ++ goto detach_frontend; ++ } ++ /* we need to open the i2c gate (we know it exists) */ ++ fe = fe0->dvb.frontend; ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (dvb_attach(isl6405_attach, fe, ++ &dev->i2c_adap, 0x08, 0, 0) == NULL) { ++ wprintk("%s: MD7134 DVB-S, no ISL6405 " ++ "found !\n", __func__); ++ goto detach_frontend; ++ } ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ dev->original_set_voltage = fe->ops.set_voltage; ++ fe->ops.set_voltage = md8800_set_voltage; ++ dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; ++ fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; ++ } ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M103: ++ saa7134_set_gpio(dev, 25, 0); ++ msleep(10); ++ saa7134_set_gpio(dev, 25, 1); ++ fe0->dvb.frontend = dvb_attach(mt352_attach, ++ &avermedia_xc3028_mt352_dev, ++ &dev->i2c_adap); ++ attach_xc3028 = 1; ++ break; ++ case SAA7134_BOARD_ASUSTeK_TIGER_3IN1: ++ if (!use_frontend) { /* terrestrial */ ++ if (configure_tda827x_fe(dev, &asus_tiger_3in1_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ } else { /* satellite */ ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, ++ &flydvbs, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda826x_attach, ++ fe0->dvb.frontend, 0x60, ++ &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: Asus Tiger 3in1, no " ++ "tda826x found!\n", __func__); ++ goto detach_frontend; ++ } ++ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0, 0) == NULL) { ++ wprintk("%s: Asus Tiger 3in1, no lnbp21" ++ " found!\n", __func__); ++ goto detach_frontend; ++ } ++ } ++ } ++ break; ++ case SAA7134_BOARD_ASUSTeK_PS3_100: ++ if (!use_frontend) { /* terrestrial */ ++ if (configure_tda827x_fe(dev, &asus_ps3_100_config, ++ &tda827x_cfg_2) < 0) ++ goto detach_frontend; ++ } else { /* satellite */ ++ fe0->dvb.frontend = dvb_attach(tda10086_attach, ++ &flydvbs, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(tda826x_attach, ++ fe0->dvb.frontend, 0x60, ++ &dev->i2c_adap, 0) == NULL) { ++ wprintk("%s: Asus My Cinema PS3-100, no " ++ "tda826x found!\n", __func__); ++ goto detach_frontend; ++ } ++ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0, 0) == NULL) { ++ wprintk("%s: Asus My Cinema PS3-100, no lnbp21" ++ " found!\n", __func__); ++ goto detach_frontend; ++ } ++ } ++ } ++ break; ++ case SAA7134_BOARD_ASUSTeK_TIGER: ++ if (configure_tda827x_fe(dev, &philips_tiger_config, ++ &tda827x_cfg_0) < 0) ++ goto detach_frontend; ++ break; ++ case SAA7134_BOARD_BEHOLD_H6: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &behold_h6_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(simple_tuner_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x61, ++ TUNER_PHILIPS_FMD1216MEX_MK3); ++ } ++ break; ++ case SAA7134_BOARD_BEHOLD_X7: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &behold_x7_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, &behold_x7_tunerconfig); ++ } ++ break; ++ case SAA7134_BOARD_BEHOLD_H7: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &behold_x7_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ dvb_attach(xc5000_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, &behold_x7_tunerconfig); ++ } ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A700_PRO: ++ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: ++ /* Zarlink ZL10313 */ ++ fe0->dvb.frontend = dvb_attach(mt312_attach, ++ &avertv_a700_mt312, &dev->i2c_adap); ++ if (fe0->dvb.frontend) { ++ if (dvb_attach(zl10036_attach, fe0->dvb.frontend, ++ &avertv_a700_tuner, &dev->i2c_adap) == NULL) { ++ wprintk("%s: No zl10036 found!\n", ++ __func__); ++ } ++ } ++ break; ++ case SAA7134_BOARD_VIDEOMATE_S350: ++ fe0->dvb.frontend = dvb_attach(mt312_attach, ++ &zl10313_compro_s350_config, &dev->i2c_adap); ++ if (fe0->dvb.frontend) ++ if (dvb_attach(zl10039_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap) == NULL) ++ wprintk("%s: No zl10039 found!\n", ++ __func__); ++ ++ break; ++ case SAA7134_BOARD_VIDEOMATE_T750: ++ fe0->dvb.frontend = dvb_attach(zl10353_attach, ++ &videomate_t750_zl10353_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ if (dvb_attach(qt1010_attach, ++ fe0->dvb.frontend, ++ &dev->i2c_adap, ++ &videomate_t750_qt1010_config) == NULL) ++ wprintk("error attaching QT1010\n"); ++ } ++ break; ++ case SAA7134_BOARD_ZOLID_HYBRID_PCI: ++ fe0->dvb.frontend = dvb_attach(tda10048_attach, ++ &zolid_tda10048_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &zolid_tda18271_config); ++ } ++ break; ++ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: ++ fe0->dvb.frontend = dvb_attach(tda10048_attach, ++ &dtv1000s_tda10048_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &dtv1000s_tda18271_config); ++ } ++ break; ++ case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: ++ /* Switch to digital mode */ ++ saa7134_tuner_callback(dev, 0, ++ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); ++ fe0->dvb.frontend = dvb_attach(mb86a20s_attach, ++ &kworld_mb86a20s_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl; ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &kworld_tda18271_config); ++ } ++ ++ /* mb86a20s need to use the I2C gateway */ ++ break; ++ case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: ++ fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, ++ &prohdtv_pro2_lgs8g75_config, ++ &dev->i2c_adap); ++ if (fe0->dvb.frontend != NULL) { ++ dvb_attach(tda829x_attach, fe0->dvb.frontend, ++ &dev->i2c_adap, 0x4b, ++ &tda829x_no_probe); ++ dvb_attach(tda18271_attach, fe0->dvb.frontend, ++ 0x60, &dev->i2c_adap, ++ &prohdtv_pro2_tda18271_config); ++ } ++ break; ++ default: ++ wprintk("Huh? unknown DVB card?\n"); ++ break; ++ } ++ ++ if (attach_xc3028) { ++ struct dvb_frontend *fe; ++ struct xc2028_config cfg = { ++ .i2c_adap = &dev->i2c_adap, ++ .i2c_addr = 0x61, ++ }; ++ ++ if (!fe0->dvb.frontend) ++ goto detach_frontend; ++ ++ fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); ++ if (!fe) { ++ printk(KERN_ERR "%s/2: xc3028 attach failed\n", ++ dev->name); ++ goto detach_frontend; ++ } ++ } ++ ++ if (NULL == fe0->dvb.frontend) { ++ printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); ++ goto detach_frontend; ++ } ++ /* define general-purpose callback pointer */ ++ fe0->dvb.frontend->callback = saa7134_tuner_callback; ++ ++ /* register everything else */ ++ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, ++ &dev->pci->dev, adapter_nr, 0); ++ ++ /* this sequence is necessary to make the tda1004x load its firmware ++ * and to enter analog mode of hybrid boards ++ */ ++ if (!ret) { ++ if (fe0->dvb.frontend->ops.init) ++ fe0->dvb.frontend->ops.init(fe0->dvb.frontend); ++ if (fe0->dvb.frontend->ops.sleep) ++ fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend); ++ if (fe0->dvb.frontend->ops.tuner_ops.sleep) ++ fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend); ++ } ++ return ret; ++ ++detach_frontend: ++ videobuf_dvb_dealloc_frontends(&dev->frontends); ++ return -EINVAL; ++} ++ ++static int dvb_fini(struct saa7134_dev *dev) ++{ ++ struct videobuf_dvb_frontend *fe0; ++ ++ /* Get the first frontend */ ++ fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); ++ if (!fe0) ++ return -EINVAL; ++ ++ /* FIXME: I suspect that this code is bogus, since the entry for ++ Pinnacle 300I DVB-T PAL already defines the proper init to allow ++ the detection of mt2032 (TDA9887_PORT2_INACTIVE) ++ */ ++ if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) { ++ struct v4l2_priv_tun_config tda9887_cfg; ++ static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE; ++ ++ tda9887_cfg.tuner = TUNER_TDA9887; ++ tda9887_cfg.priv = &on; ++ ++ /* otherwise we don't detect the tuner on next insmod */ ++ saa_call_all(dev, tuner, s_config, &tda9887_cfg); ++ } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) { ++ if ((dev->eedata[2] == 0x07) && use_frontend) { ++ /* turn off the 2nd lnb supply */ ++ u8 data = 0x80; ++ struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1}; ++ struct dvb_frontend *fe; ++ fe = fe0->dvb.frontend; ++ if (fe->ops.i2c_gate_ctrl) { ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(&dev->i2c_adap, &msg, 1); ++ fe->ops.i2c_gate_ctrl(fe, 0); ++ } ++ } ++ } ++ videobuf_dvb_unregister_bus(&dev->frontends); ++ return 0; ++} ++ ++static struct saa7134_mpeg_ops dvb_ops = { ++ .type = SAA7134_MPEG_DVB, ++ .init = dvb_init, ++ .fini = dvb_fini, ++}; ++ ++static int __init dvb_register(void) ++{ ++ return saa7134_ts_register(&dvb_ops); ++} ++ ++static void __exit dvb_unregister(void) ++{ ++ saa7134_ts_unregister(&dvb_ops); ++} ++ ++module_init(dvb_register); ++module_exit(dvb_unregister); ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c +new file mode 100644 +index 0000000..4df79c6 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-empress.c +@@ -0,0 +1,590 @@ ++/* ++ * ++ * (c) 2004 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++#include ++#include ++#include ++ ++/* ------------------------------------------------------------------ */ ++ ++MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); ++MODULE_LICENSE("GPL"); ++ ++static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; ++ ++module_param_array(empress_nr, int, NULL, 0444); ++MODULE_PARM_DESC(empress_nr,"ts device number"); ++ ++static unsigned int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug,"enable debug messages"); ++ ++#define dprintk(fmt, arg...) if (debug) \ ++ printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg) ++ ++/* ------------------------------------------------------------------ */ ++ ++static void ts_reset_encoder(struct saa7134_dev* dev) ++{ ++ if (!dev->empress_started) ++ return; ++ ++ saa_writeb(SAA7134_SPECIAL_MODE, 0x00); ++ msleep(10); ++ saa_writeb(SAA7134_SPECIAL_MODE, 0x01); ++ msleep(100); ++ dev->empress_started = 0; ++} ++ ++static int ts_init_encoder(struct saa7134_dev* dev) ++{ ++ u32 leading_null_bytes = 0; ++ ++ /* If more cards start to need this, then this ++ should probably be added to the card definitions. */ ++ switch (dev->board) { ++ case SAA7134_BOARD_BEHOLD_M6: ++ case SAA7134_BOARD_BEHOLD_M63: ++ case SAA7134_BOARD_BEHOLD_M6_EXTRA: ++ leading_null_bytes = 1; ++ break; ++ } ++ ts_reset_encoder(dev); ++ saa_call_all(dev, core, init, leading_null_bytes); ++ dev->empress_started = 1; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int ts_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct saa7134_dev *dev = video_drvdata(file); ++ int err; ++ ++ dprintk("open dev=%s\n", video_device_node_name(vdev)); ++ err = -EBUSY; ++ if (!mutex_trylock(&dev->empress_tsq.vb_lock)) ++ return err; ++ if (atomic_read(&dev->empress_users)) ++ goto done; ++ ++ /* Unmute audio */ ++ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, ++ saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); ++ ++ atomic_inc(&dev->empress_users); ++ file->private_data = dev; ++ err = 0; ++ ++done: ++ mutex_unlock(&dev->empress_tsq.vb_lock); ++ return err; ++} ++ ++static int ts_release(struct file *file) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ videobuf_stop(&dev->empress_tsq); ++ videobuf_mmap_free(&dev->empress_tsq); ++ ++ /* stop the encoder */ ++ ts_reset_encoder(dev); ++ ++ /* Mute audio */ ++ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, ++ saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); ++ ++ atomic_dec(&dev->empress_users); ++ ++ return 0; ++} ++ ++static ssize_t ++ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ if (!dev->empress_started) ++ ts_init_encoder(dev); ++ ++ return videobuf_read_stream(&dev->empress_tsq, ++ data, count, ppos, 0, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static unsigned int ++ts_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_poll_stream(file, &dev->empress_tsq, wait); ++} ++ ++ ++static int ++ts_mmap(struct file *file, struct vm_area_struct * vma) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_mmap_mapper(&dev->empress_tsq, vma); ++} ++ ++/* ++ * This function is _not_ called directly, but from ++ * video_generic_ioctl (and maybe others). userspace ++ * copying is done already, arg is a kernel pointer. ++ */ ++ ++static int empress_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ strcpy(cap->driver, "saa7134"); ++ strlcpy(cap->card, saa7134_boards[dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ return 0; ++} ++ ++static int empress_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ if (i->index != 0) ++ return -EINVAL; ++ ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ strcpy(i->name, "CCIR656"); ++ ++ return 0; ++} ++ ++static int empress_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int empress_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int empress_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strlcpy(f->description, "MPEG TS", sizeof(f->description)); ++ f->pixelformat = V4L2_PIX_FMT_MPEG; ++ ++ return 0; ++} ++ ++static int empress_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ ++ saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt); ++ ++ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; ++ ++ return 0; ++} ++ ++static int empress_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ ++ v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); ++ saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt); ++ v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; ++ ++ return 0; ++} ++ ++static int empress_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; ++ ++ return 0; ++} ++ ++static int empress_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_reqbufs(&dev->empress_tsq, p); ++} ++ ++static int empress_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *b) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_querybuf(&dev->empress_tsq, b); ++} ++ ++static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_qbuf(&dev->empress_tsq, b); ++} ++ ++static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_dqbuf(&dev->empress_tsq, b, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int empress_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_streamon(&dev->empress_tsq); ++} ++ ++static int empress_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return videobuf_streamoff(&dev->empress_tsq); ++} ++ ++static int empress_s_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ int err; ++ ++ /* count == 0 is abused in saa6752hs.c, so that special ++ case is handled here explicitly. */ ++ if (ctrls->count == 0) ++ return 0; ++ ++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ ++ err = saa_call_empress(dev, core, s_ext_ctrls, ctrls); ++ ts_init_encoder(dev); ++ ++ return err; ++} ++ ++static int empress_g_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ return saa_call_empress(dev, core, g_ext_ctrls, ctrls); ++} ++ ++static int empress_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *c) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return saa7134_g_ctrl_internal(dev, NULL, c); ++} ++ ++static int empress_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *c) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return saa7134_s_ctrl_internal(dev, NULL, c); ++} ++ ++static int empress_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ /* Must be sorted from low to high control ID! */ ++ static const u32 user_ctrls[] = { ++ V4L2_CID_USER_CLASS, ++ V4L2_CID_BRIGHTNESS, ++ V4L2_CID_CONTRAST, ++ V4L2_CID_SATURATION, ++ V4L2_CID_HUE, ++ V4L2_CID_AUDIO_VOLUME, ++ V4L2_CID_AUDIO_MUTE, ++ V4L2_CID_HFLIP, ++ 0 ++ }; ++ ++ /* Must be sorted from low to high control ID! */ ++ static const u32 mpeg_ctrls[] = { ++ V4L2_CID_MPEG_CLASS, ++ V4L2_CID_MPEG_STREAM_TYPE, ++ V4L2_CID_MPEG_STREAM_PID_PMT, ++ V4L2_CID_MPEG_STREAM_PID_AUDIO, ++ V4L2_CID_MPEG_STREAM_PID_VIDEO, ++ V4L2_CID_MPEG_STREAM_PID_PCR, ++ V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, ++ V4L2_CID_MPEG_AUDIO_ENCODING, ++ V4L2_CID_MPEG_AUDIO_L2_BITRATE, ++ V4L2_CID_MPEG_VIDEO_ENCODING, ++ V4L2_CID_MPEG_VIDEO_ASPECT, ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, ++ V4L2_CID_MPEG_VIDEO_BITRATE, ++ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, ++ 0 ++ }; ++ static const u32 *ctrl_classes[] = { ++ user_ctrls, ++ mpeg_ctrls, ++ NULL ++ }; ++ struct saa7134_dev *dev = file->private_data; ++ ++ c->id = v4l2_ctrl_next(ctrl_classes, c->id); ++ if (c->id == 0) ++ return -EINVAL; ++ if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) ++ return v4l2_ctrl_query_fill(c, 0, 0, 0, 0); ++ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) ++ return saa7134_queryctrl(file, priv, c); ++ return saa_call_empress(dev, core, queryctrl, c); ++} ++ ++static int empress_querymenu(struct file *file, void *priv, ++ struct v4l2_querymenu *c) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) ++ return -EINVAL; ++ return saa_call_empress(dev, core, querymenu, c); ++} ++ ++static int empress_g_chip_ident(struct file *file, void *fh, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER && ++ !strcmp(chip->match.name, "saa6752hs")) ++ return saa_call_empress(dev, core, g_chip_ident, chip); ++ if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR) ++ return saa_call_empress(dev, core, g_chip_ident, chip); ++ return -EINVAL; ++} ++ ++static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ return saa7134_s_std_internal(dev, NULL, id); ++} ++ ++static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7134_dev *dev = file->private_data; ++ ++ *id = dev->tvnorm->id; ++ return 0; ++} ++ ++static const struct v4l2_file_operations ts_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = ts_open, ++ .release = ts_release, ++ .read = ts_read, ++ .poll = ts_poll, ++ .mmap = ts_mmap, ++ .ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops ts_ioctl_ops = { ++ .vidioc_querycap = empress_querycap, ++ .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, ++ .vidioc_reqbufs = empress_reqbufs, ++ .vidioc_querybuf = empress_querybuf, ++ .vidioc_qbuf = empress_qbuf, ++ .vidioc_dqbuf = empress_dqbuf, ++ .vidioc_streamon = empress_streamon, ++ .vidioc_streamoff = empress_streamoff, ++ .vidioc_s_ext_ctrls = empress_s_ext_ctrls, ++ .vidioc_g_ext_ctrls = empress_g_ext_ctrls, ++ .vidioc_enum_input = empress_enum_input, ++ .vidioc_g_input = empress_g_input, ++ .vidioc_s_input = empress_s_input, ++ .vidioc_queryctrl = empress_queryctrl, ++ .vidioc_querymenu = empress_querymenu, ++ .vidioc_g_ctrl = empress_g_ctrl, ++ .vidioc_s_ctrl = empress_s_ctrl, ++ .vidioc_g_chip_ident = empress_g_chip_ident, ++ .vidioc_s_std = empress_s_std, ++ .vidioc_g_std = empress_g_std, ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++static struct video_device saa7134_empress_template = { ++ .name = "saa7134-empress", ++ .fops = &ts_fops, ++ .ioctl_ops = &ts_ioctl_ops, ++ ++ .tvnorms = SAA7134_NORMS, ++ .current_norm = V4L2_STD_PAL, ++}; ++ ++static void empress_signal_update(struct work_struct *work) ++{ ++ struct saa7134_dev* dev = ++ container_of(work, struct saa7134_dev, empress_workqueue); ++ ++ if (dev->nosignal) { ++ dprintk("no video signal\n"); ++ } else { ++ dprintk("video signal acquired\n"); ++ } ++} ++ ++static void empress_signal_change(struct saa7134_dev *dev) ++{ ++ schedule_work(&dev->empress_workqueue); ++} ++ ++ ++static int empress_init(struct saa7134_dev *dev) ++{ ++ int err; ++ ++ dprintk("%s: %s\n",dev->name,__func__); ++ dev->empress_dev = video_device_alloc(); ++ if (NULL == dev->empress_dev) ++ return -ENOMEM; ++ *(dev->empress_dev) = saa7134_empress_template; ++ dev->empress_dev->parent = &dev->pci->dev; ++ dev->empress_dev->release = video_device_release; ++ snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), ++ "%s empress (%s)", dev->name, ++ saa7134_boards[dev->board].name); ++ ++ INIT_WORK(&dev->empress_workqueue, empress_signal_update); ++ ++ video_set_drvdata(dev->empress_dev, dev); ++ err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, ++ empress_nr[dev->nr]); ++ if (err < 0) { ++ printk(KERN_INFO "%s: can't register video device\n", ++ dev->name); ++ video_device_release(dev->empress_dev); ++ dev->empress_dev = NULL; ++ return err; ++ } ++ printk(KERN_INFO "%s: registered device %s [mpeg]\n", ++ dev->name, video_device_node_name(dev->empress_dev)); ++ ++ videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_ALTERNATE, ++ sizeof(struct saa7134_buf), ++ dev, NULL); ++ ++ empress_signal_update(&dev->empress_workqueue); ++ return 0; ++} ++ ++static int empress_fini(struct saa7134_dev *dev) ++{ ++ dprintk("%s: %s\n",dev->name,__func__); ++ ++ if (NULL == dev->empress_dev) ++ return 0; ++ flush_work(&dev->empress_workqueue); ++ video_unregister_device(dev->empress_dev); ++ dev->empress_dev = NULL; ++ return 0; ++} ++ ++static struct saa7134_mpeg_ops empress_ops = { ++ .type = SAA7134_MPEG_EMPRESS, ++ .init = empress_init, ++ .fini = empress_fini, ++ .signal_change = empress_signal_change, ++}; ++ ++static int __init empress_register(void) ++{ ++ return saa7134_ts_register(&empress_ops); ++} ++ ++static void __exit empress_unregister(void) ++{ ++ saa7134_ts_unregister(&empress_ops); ++} ++ ++module_init(empress_register); ++module_exit(empress_unregister); ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c +new file mode 100644 +index 0000000..a176ec3 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-i2c.c +@@ -0,0 +1,435 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * i2c interface support ++ * ++ * (c) 2001,02 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++#include ++ ++/* ----------------------------------------------------------- */ ++ ++static unsigned int i2c_debug; ++module_param(i2c_debug, int, 0644); ++MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); ++ ++static unsigned int i2c_scan; ++module_param(i2c_scan, int, 0444); ++MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); ++ ++#define d1printk if (1 == i2c_debug) printk ++#define d2printk if (2 == i2c_debug) printk ++ ++#define I2C_WAIT_DELAY 32 ++#define I2C_WAIT_RETRY 16 ++ ++/* ----------------------------------------------------------- */ ++ ++static char *str_i2c_status[] = { ++ "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE", ++ "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE", ++ "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR" ++}; ++ ++enum i2c_status { ++ IDLE = 0, // no I2C command pending ++ DONE_STOP = 1, // I2C command done and STOP executed ++ BUSY = 2, // executing I2C command ++ TO_SCL = 3, // executing I2C command, time out on clock stretching ++ TO_ARB = 4, // time out on arbitration trial, still trying ++ DONE_WRITE = 5, // I2C command done and awaiting next write command ++ DONE_READ = 6, // I2C command done and awaiting next read command ++ DONE_WRITE_TO = 7, // see 5, and time out on status echo ++ DONE_READ_TO = 8, // see 6, and time out on status echo ++ NO_DEVICE = 9, // no acknowledge on device slave address ++ NO_ACKN = 10, // no acknowledge after data byte transfer ++ BUS_ERR = 11, // bus error ++ ARB_LOST = 12, // arbitration lost during transfer ++ SEQ_ERR = 13, // erroneous programming sequence ++ ST_ERR = 14, // wrong status echoing ++ SW_ERR = 15 // software error ++}; ++ ++static char *str_i2c_attr[] = { ++ "NOP", "STOP", "CONTINUE", "START" ++}; ++ ++enum i2c_attr { ++ NOP = 0, // no operation on I2C bus ++ STOP = 1, // stop condition, no associated byte transfer ++ CONTINUE = 2, // continue with byte transfer ++ START = 3 // start condition with byte transfer ++}; ++ ++static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) ++{ ++ enum i2c_status status; ++ ++ status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; ++ d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name, ++ str_i2c_status[status]); ++ return status; ++} ++ ++static inline void i2c_set_status(struct saa7134_dev *dev, ++ enum i2c_status status) ++{ ++ d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name, ++ str_i2c_status[status]); ++ saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); ++} ++ ++static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) ++{ ++ d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name, ++ str_i2c_attr[attr]); ++ saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); ++} ++ ++static inline int i2c_is_error(enum i2c_status status) ++{ ++ switch (status) { ++ case NO_DEVICE: ++ case NO_ACKN: ++ case BUS_ERR: ++ case ARB_LOST: ++ case SEQ_ERR: ++ case ST_ERR: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static inline int i2c_is_idle(enum i2c_status status) ++{ ++ switch (status) { ++ case IDLE: ++ case DONE_STOP: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static inline int i2c_is_busy(enum i2c_status status) ++{ ++ switch (status) { ++ case BUSY: ++ case TO_SCL: ++ case TO_ARB: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static int i2c_is_busy_wait(struct saa7134_dev *dev) ++{ ++ enum i2c_status status; ++ int count; ++ ++ for (count = 0; count < I2C_WAIT_RETRY; count++) { ++ status = i2c_get_status(dev); ++ if (!i2c_is_busy(status)) ++ break; ++ saa_wait(I2C_WAIT_DELAY); ++ } ++ if (I2C_WAIT_RETRY == count) ++ return false; ++ return true; ++} ++ ++static int i2c_reset(struct saa7134_dev *dev) ++{ ++ enum i2c_status status; ++ int count; ++ ++ d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); ++ status = i2c_get_status(dev); ++ if (!i2c_is_error(status)) ++ return true; ++ i2c_set_status(dev,status); ++ ++ for (count = 0; count < I2C_WAIT_RETRY; count++) { ++ status = i2c_get_status(dev); ++ if (!i2c_is_error(status)) ++ break; ++ udelay(I2C_WAIT_DELAY); ++ } ++ if (I2C_WAIT_RETRY == count) ++ return false; ++ ++ if (!i2c_is_idle(status)) ++ return false; ++ ++ i2c_set_attr(dev,NOP); ++ return true; ++} ++ ++static inline int i2c_send_byte(struct saa7134_dev *dev, ++ enum i2c_attr attr, ++ unsigned char data) ++{ ++ enum i2c_status status; ++ __u32 dword; ++ ++ /* have to write both attr + data in one 32bit word */ ++ dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2); ++ dword &= 0x0f; ++ dword |= (attr << 6); ++ dword |= ((__u32)data << 8); ++ dword |= 0x00 << 16; /* 100 kHz */ ++// dword |= 0x40 << 16; /* 400 kHz */ ++ dword |= 0xf0 << 24; ++ saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); ++ d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data); ++ ++ if (!i2c_is_busy_wait(dev)) ++ return -EIO; ++ status = i2c_get_status(dev); ++ if (i2c_is_error(status)) ++ return -EIO; ++ return 0; ++} ++ ++static inline int i2c_recv_byte(struct saa7134_dev *dev) ++{ ++ enum i2c_status status; ++ unsigned char data; ++ ++ i2c_set_attr(dev,CONTINUE); ++ if (!i2c_is_busy_wait(dev)) ++ return -EIO; ++ status = i2c_get_status(dev); ++ if (i2c_is_error(status)) ++ return -EIO; ++ data = saa_readb(SAA7134_I2C_DATA); ++ d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data); ++ return data; ++} ++ ++static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct saa7134_dev *dev = i2c_adap->algo_data; ++ enum i2c_status status; ++ unsigned char data; ++ int addr,rc,i,byte; ++ ++ status = i2c_get_status(dev); ++ if (!i2c_is_idle(status)) ++ if (!i2c_reset(dev)) ++ return -EIO; ++ ++ d2printk("start xfer\n"); ++ d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name); ++ for (i = 0; i < num; i++) { ++ if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { ++ /* send address */ ++ d2printk("send address\n"); ++ addr = msgs[i].addr << 1; ++ if (msgs[i].flags & I2C_M_RD) ++ addr |= 1; ++ if (i > 0 && msgs[i].flags & ++ I2C_M_RD && msgs[i].addr != 0x40 && ++ msgs[i].addr != 0x19) { ++ /* workaround for a saa7134 i2c bug ++ * needed to talk to the mt352 demux ++ * thanks to pinnacle for the hint */ ++ int quirk = 0xfe; ++ d1printk(" [%02x quirk]",quirk); ++ i2c_send_byte(dev,START,quirk); ++ i2c_recv_byte(dev); ++ } ++ d1printk(" < %02x", addr); ++ rc = i2c_send_byte(dev,START,addr); ++ if (rc < 0) ++ goto err; ++ } ++ if (msgs[i].flags & I2C_M_RD) { ++ /* read bytes */ ++ d2printk("read bytes\n"); ++ for (byte = 0; byte < msgs[i].len; byte++) { ++ d1printk(" ="); ++ rc = i2c_recv_byte(dev); ++ if (rc < 0) ++ goto err; ++ d1printk("%02x", rc); ++ msgs[i].buf[byte] = rc; ++ } ++ /* discard mysterious extra byte when reading ++ from Samsung S5H1411. i2c bus gets error ++ if we do not. */ ++ if (0x19 == msgs[i].addr) { ++ d1printk(" ?"); ++ rc = i2c_recv_byte(dev); ++ if (rc < 0) ++ goto err; ++ d1printk("%02x", rc); ++ } ++ } else { ++ /* write bytes */ ++ d2printk("write bytes\n"); ++ for (byte = 0; byte < msgs[i].len; byte++) { ++ data = msgs[i].buf[byte]; ++ d1printk(" %02x", data); ++ rc = i2c_send_byte(dev,CONTINUE,data); ++ if (rc < 0) ++ goto err; ++ } ++ } ++ } ++ d2printk("xfer done\n"); ++ d1printk(" >"); ++ i2c_set_attr(dev,STOP); ++ rc = -EIO; ++ if (!i2c_is_busy_wait(dev)) ++ goto err; ++ status = i2c_get_status(dev); ++ if (i2c_is_error(status)) ++ goto err; ++ /* ensure that the bus is idle for at least one bit slot */ ++ msleep(1); ++ ++ d1printk("\n"); ++ return num; ++ err: ++ if (1 == i2c_debug) { ++ status = i2c_get_status(dev); ++ printk(" ERROR: %s\n",str_i2c_status[status]); ++ } ++ return rc; ++} ++ ++/* ----------------------------------------------------------- */ ++ ++static u32 functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm saa7134_algo = { ++ .master_xfer = saa7134_i2c_xfer, ++ .functionality = functionality, ++}; ++ ++static struct i2c_adapter saa7134_adap_template = { ++ .owner = THIS_MODULE, ++ .name = "saa7134", ++ .algo = &saa7134_algo, ++}; ++ ++static struct i2c_client saa7134_client_template = { ++ .name = "saa7134 internal", ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++static int ++saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) ++{ ++ unsigned char buf; ++ int i,err; ++ ++ dev->i2c_client.addr = 0xa0 >> 1; ++ buf = 0; ++ if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { ++ printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", ++ dev->name,err); ++ return -1; ++ } ++ if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) { ++ printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", ++ dev->name,err); ++ return -1; ++ } ++ for (i = 0; i < len; i++) { ++ if (0 == (i % 16)) ++ printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i); ++ printk(" %02x",eedata[i]); ++ if (15 == (i % 16)) ++ printk("\n"); ++ } ++ return 0; ++} ++ ++static char *i2c_devs[128] = { ++ [ 0x20 ] = "mpeg encoder (saa6752hs)", ++ [ 0xa0 >> 1 ] = "eeprom", ++ [ 0xc0 >> 1 ] = "tuner (analog)", ++ [ 0x86 >> 1 ] = "tda9887", ++ [ 0x5a >> 1 ] = "remote control", ++}; ++ ++static void do_i2c_scan(char *name, struct i2c_client *c) ++{ ++ unsigned char buf; ++ int i,rc; ++ ++ for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { ++ c->addr = i; ++ rc = i2c_master_recv(c,&buf,0); ++ if (rc < 0) ++ continue; ++ printk("%s: i2c scan: found device @ 0x%x [%s]\n", ++ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); ++ } ++} ++ ++int saa7134_i2c_register(struct saa7134_dev *dev) ++{ ++ dev->i2c_adap = saa7134_adap_template; ++ dev->i2c_adap.dev.parent = &dev->pci->dev; ++ strcpy(dev->i2c_adap.name,dev->name); ++ dev->i2c_adap.algo_data = dev; ++ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); ++ i2c_add_adapter(&dev->i2c_adap); ++ ++ dev->i2c_client = saa7134_client_template; ++ dev->i2c_client.adapter = &dev->i2c_adap; ++ ++ saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); ++ if (i2c_scan) ++ do_i2c_scan(dev->name,&dev->i2c_client); ++ ++ /* Instantiate the IR receiver device, if present */ ++ saa7134_probe_i2c_ir(dev); ++ return 0; ++} ++ ++int saa7134_i2c_unregister(struct saa7134_dev *dev) ++{ ++ i2c_del_adapter(&dev->i2c_adap); ++ return 0; ++} ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c +new file mode 100644 +index 0000000..e761262 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-input.c +@@ -0,0 +1,1041 @@ ++/* ++ * ++ * handle saa7134 IR remotes via linux kernel input layer. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++#define MODULE_NAME "saa7134" ++ ++static unsigned int disable_ir; ++module_param(disable_ir, int, 0444); ++MODULE_PARM_DESC(disable_ir,"disable infrared remote support"); ++ ++static unsigned int ir_debug; ++module_param(ir_debug, int, 0644); ++MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); ++ ++static int pinnacle_remote; ++module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ ++MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); ++ ++#define dprintk(fmt, arg...) if (ir_debug) \ ++ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg) ++#define i2cdprintk(fmt, arg...) if (ir_debug) \ ++ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg) ++ ++/* Helper function for raw decoding at GPIO16 or GPIO18 */ ++static int saa7134_raw_decode_irq(struct saa7134_dev *dev); ++ ++/* -------------------- GPIO generic keycode builder -------------------- */ ++ ++static int build_key(struct saa7134_dev *dev) ++{ ++ struct saa7134_card_ir *ir = dev->remote; ++ u32 gpio, data; ++ ++ /* here comes the additional handshake steps for some cards */ ++ switch (dev->board) { ++ case SAA7134_BOARD_GOTVIEW_7135: ++ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80); ++ saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80); ++ break; ++ } ++ /* rising SAA7134_GPIO_GPRESCAN reads the status */ ++ saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN); ++ ++ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ if (ir->polling) { ++ if (ir->last_gpio == gpio) ++ return 0; ++ ir->last_gpio = gpio; ++ } ++ ++ data = ir_extract_bits(gpio, ir->mask_keycode); ++ dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", ++ gpio, ir->mask_keycode, data); ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: ++ if (data == ir->mask_keycode) ++ rc_keyup(ir->dev); ++ else ++ rc_keydown_notimeout(ir->dev, data, 0); ++ return 0; ++ } ++ ++ if (ir->polling) { ++ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || ++ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { ++ rc_keydown_notimeout(ir->dev, data, 0); ++ } else { ++ rc_keyup(ir->dev); ++ } ++ } ++ else { /* IRQ driven mode - handle key press and release in one go */ ++ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || ++ (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { ++ rc_keydown_notimeout(ir->dev, data, 0); ++ rc_keyup(ir->dev); ++ } ++ } ++ ++ return 0; ++} ++ ++/* --------------------- Chip specific I2C key builders ----------------- */ ++ ++static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ int gpio; ++ int attempt = 0; ++ unsigned char b; ++ ++ /* We need this to access GPI Used by the saa_readl macro. */ ++ struct saa7134_dev *dev = ir->c->adapter->algo_data; ++ ++ if (dev == NULL) { ++ i2cdprintk("get_key_flydvb_trio: " ++ "ir->c->adapter->algo_data is NULL!\n"); ++ return -EIO; ++ } ++ ++ /* rising SAA7134_GPIGPRESCAN reads the status */ ++ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ ++ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ ++ if (0x40000 & ~gpio) ++ return 0; /* No button press */ ++ ++ /* No button press - only before first key pressed */ ++ if (b == 0xFF) ++ return 0; ++ ++ /* poll IR chip */ ++ /* weak up the IR chip */ ++ b = 0; ++ ++ while (1 != i2c_master_send(ir->c, &b, 1)) { ++ if ((attempt++) < 10) { ++ /* ++ * wait a bit for next attempt - ++ * I don't know how make it better ++ */ ++ msleep(10); ++ continue; ++ } ++ i2cdprintk("send wake up byte to pic16C505 (IR chip)" ++ "failed %dx\n", attempt); ++ return -EIO; ++ } ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, ++ u32 *ir_raw) ++{ ++ unsigned char b; ++ int gpio; ++ ++ /* is needed to access GPIO. Used by the saa_readl macro. */ ++ struct saa7134_dev *dev = ir->c->adapter->algo_data; ++ if (dev == NULL) { ++ i2cdprintk("get_key_msi_tvanywhere_plus: " ++ "ir->c->adapter->algo_data is NULL!\n"); ++ return -EIO; ++ } ++ ++ /* rising SAA7134_GPIO_GPRESCAN reads the status */ ++ ++ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ ++ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ ++ /* GPIO&0x40 is pulsed low when a button is pressed. Don't do ++ I2C receive if gpio&0x40 is not low. */ ++ ++ if (gpio & 0x40) ++ return 0; /* No button press */ ++ ++ /* GPIO says there is a button press. Get it. */ ++ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ /* No button press */ ++ ++ if (b == 0xff) ++ return 0; ++ ++ /* Button pressed */ ++ ++ dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++/* copied and modified from get_key_msi_tvanywhere_plus() */ ++static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, ++ u32 *ir_raw) ++{ ++ unsigned char b; ++ unsigned int gpio; ++ ++ /* is needed to access GPIO. Used by the saa_readl macro. */ ++ struct saa7134_dev *dev = ir->c->adapter->algo_data; ++ if (dev == NULL) { ++ i2cdprintk("get_key_kworld_pc150u: " ++ "ir->c->adapter->algo_data is NULL!\n"); ++ return -EIO; ++ } ++ ++ /* rising SAA7134_GPIO_GPRESCAN reads the status */ ++ ++ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ ++ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ ++ /* GPIO&0x100 is pulsed low when a button is pressed. Don't do ++ I2C receive if gpio&0x100 is not low. */ ++ ++ if (gpio & 0x100) ++ return 0; /* No button press */ ++ ++ /* GPIO says there is a button press. Get it. */ ++ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ /* No button press */ ++ ++ if (b == 0xff) ++ return 0; ++ ++ /* Button pressed */ ++ ++ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char b; ++ ++ /* poll IR chip */ ++ if (1 != i2c_master_recv(ir->c, &b, 1)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ /* no button press */ ++ if (b==0) ++ return 0; ++ ++ /* repeating */ ++ if (b & 0x80) ++ return 1; ++ ++ *ir_key = b; ++ *ir_raw = b; ++ return 1; ++} ++ ++static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char buf[5]; ++ ++ /* poll IR chip */ ++ if (5 != i2c_master_recv(ir->c, buf, 5)) ++ return -EIO; ++ ++ /* Check if some key were pressed */ ++ if (!(buf[0] & 0x80)) ++ return 0; ++ ++ /* ++ * buf[3] & 0x80 is always high. ++ * buf[3] & 0x40 is a parity bit. A repeat event is marked ++ * by preserving it into two separate readings ++ * buf[4] bits 0 and 1, and buf[1] and buf[2] are always ++ * zero. ++ */ ++ *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2)); ++ *ir_raw = *ir_key; ++ return 1; ++} ++ ++ ++static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ unsigned char data[12]; ++ u32 gpio; ++ ++ struct saa7134_dev *dev = ir->c->adapter->algo_data; ++ ++ /* rising SAA7134_GPIO_GPRESCAN reads the status */ ++ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ ++ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); ++ ++ if (0x400000 & ~gpio) ++ return 0; /* No button press */ ++ ++ ir->c->addr = 0x5a >> 1; ++ ++ if (12 != i2c_master_recv(ir->c, data, 12)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ if (data[9] != (unsigned char)(~data[8])) ++ return 0; ++ ++ *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0)); ++ *ir_key = *ir_raw; ++ ++ return 1; ++} ++ ++/* Common (grey or coloured) pinnacle PCTV remote handling ++ * ++ */ ++static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, ++ int parity_offset, int marker, int code_modulo) ++{ ++ unsigned char b[4]; ++ unsigned int start = 0,parity = 0,code = 0; ++ ++ /* poll IR chip */ ++ if (4 != i2c_master_recv(ir->c, b, 4)) { ++ i2cdprintk("read error\n"); ++ return -EIO; ++ } ++ ++ for (start = 0; start < ARRAY_SIZE(b); start++) { ++ if (b[start] == marker) { ++ code=b[(start+parity_offset + 1) % 4]; ++ parity=b[(start+parity_offset) % 4]; ++ } ++ } ++ ++ /* Empty Request */ ++ if (parity == 0) ++ return 0; ++ ++ /* Repeating... */ ++ if (ir->old == parity) ++ return 0; ++ ++ ir->old = parity; ++ ++ /* drop special codes when a key is held down a long time for the grey controller ++ In this case, the second bit of the code is asserted */ ++ if (marker == 0xfe && (code & 0x40)) ++ return 0; ++ ++ code %= code_modulo; ++ ++ *ir_raw = code; ++ *ir_key = code; ++ ++ i2cdprintk("Pinnacle PCTV key %02x\n", code); ++ ++ return 1; ++} ++ ++/* The grey pinnacle PCTV remote ++ * ++ * There are one issue with this remote: ++ * - I2c packet does not change when the same key is pressed quickly. The workaround ++ * is to hold down each key for about half a second, so that another code is generated ++ * in the i2c packet, and the function can distinguish key presses. ++ * ++ * Sylvain Pasche ++ */ ++static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ ++ return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); ++} ++ ++ ++/* The new pinnacle PCTV remote (with the colored buttons) ++ * ++ * Ricardo Cerqueira ++ */ ++static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) ++{ ++ /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE ++ * ++ * this is the only value that results in 42 unique ++ * codes < 128 ++ */ ++ ++ return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); ++} ++ ++void saa7134_input_irq(struct saa7134_dev *dev) ++{ ++ struct saa7134_card_ir *ir; ++ ++ if (!dev || !dev->remote) ++ return; ++ ++ ir = dev->remote; ++ if (!ir->running) ++ return; ++ ++ if (!ir->polling && !ir->raw_decode) { ++ build_key(dev); ++ } else if (ir->raw_decode) { ++ saa7134_raw_decode_irq(dev); ++ } ++} ++ ++static void saa7134_input_timer(unsigned long data) ++{ ++ struct saa7134_dev *dev = (struct saa7134_dev *)data; ++ struct saa7134_card_ir *ir = dev->remote; ++ ++ build_key(dev); ++ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); ++} ++ ++static void ir_raw_decode_timer_end(unsigned long data) ++{ ++ struct saa7134_dev *dev = (struct saa7134_dev *)data; ++ ++ ir_raw_event_handle(dev->remote->dev); ++} ++ ++static int __saa7134_ir_start(void *priv) ++{ ++ struct saa7134_dev *dev = priv; ++ struct saa7134_card_ir *ir; ++ ++ if (!dev || !dev->remote) ++ return -EINVAL; ++ ++ ir = dev->remote; ++ if (ir->running) ++ return 0; ++ ++ /* Moved here from saa7134_input_init1() because the latter ++ * is not called on device resume */ ++ switch (dev->board) { ++ case SAA7134_BOARD_MD2819: ++ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: ++ case SAA7134_BOARD_AVERMEDIA_305: ++ case SAA7134_BOARD_AVERMEDIA_307: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_305: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_505: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_307: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_507: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ++ case SAA7134_BOARD_AVERMEDIA_M102: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: ++ /* Without this we won't receive key up events */ ++ saa_setb(SAA7134_GPIO_GPMODE0, 0x4); ++ saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_777: ++ case SAA7134_BOARD_AVERMEDIA_A16AR: ++ /* Without this we won't receive key up events */ ++ saa_setb(SAA7134_GPIO_GPMODE1, 0x1); ++ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ /* Without this we won't receive key up events */ ++ saa_setb(SAA7134_GPIO_GPMODE1, 0x1); ++ saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); ++ break; ++ case SAA7134_BOARD_GOTVIEW_7135: ++ saa_setb(SAA7134_GPIO_GPMODE1, 0x80); ++ break; ++ } ++ ++ ir->running = true; ++ ++ if (ir->polling) { ++ setup_timer(&ir->timer, saa7134_input_timer, ++ (unsigned long)dev); ++ ir->timer.expires = jiffies + HZ; ++ add_timer(&ir->timer); ++ } else if (ir->raw_decode) { ++ /* set timer_end for code completion */ ++ setup_timer(&ir->timer, ir_raw_decode_timer_end, ++ (unsigned long)dev); ++ } ++ ++ return 0; ++} ++ ++static void __saa7134_ir_stop(void *priv) ++{ ++ struct saa7134_dev *dev = priv; ++ struct saa7134_card_ir *ir; ++ ++ if (!dev || !dev->remote) ++ return; ++ ++ ir = dev->remote; ++ if (!ir->running) ++ return; ++ ++ if (ir->polling || ir->raw_decode) ++ del_timer_sync(&ir->timer); ++ ++ ir->running = false; ++ ++ return; ++} ++ ++int saa7134_ir_start(struct saa7134_dev *dev) ++{ ++ if (dev->remote->users) ++ return __saa7134_ir_start(dev); ++ ++ return 0; ++} ++ ++void saa7134_ir_stop(struct saa7134_dev *dev) ++{ ++ if (dev->remote->users) ++ __saa7134_ir_stop(dev); ++} ++ ++static int saa7134_ir_open(struct rc_dev *rc) ++{ ++ struct saa7134_dev *dev = rc->priv; ++ ++ dev->remote->users++; ++ return __saa7134_ir_start(dev); ++} ++ ++static void saa7134_ir_close(struct rc_dev *rc) ++{ ++ struct saa7134_dev *dev = rc->priv; ++ ++ dev->remote->users--; ++ if (!dev->remote->users) ++ __saa7134_ir_stop(dev); ++} ++ ++int saa7134_input_init1(struct saa7134_dev *dev) ++{ ++ struct saa7134_card_ir *ir; ++ struct rc_dev *rc; ++ char *ir_codes = NULL; ++ u32 mask_keycode = 0; ++ u32 mask_keydown = 0; ++ u32 mask_keyup = 0; ++ unsigned polling = 0; ++ bool raw_decode = false; ++ int err; ++ ++ if (dev->has_remote != SAA7134_REMOTE_GPIO) ++ return -ENODEV; ++ if (disable_ir) ++ return -ENODEV; ++ ++ /* detect & configure */ ++ switch (dev->board) { ++ case SAA7134_BOARD_FLYVIDEO2000: ++ case SAA7134_BOARD_FLYVIDEO3000: ++ case SAA7134_BOARD_FLYTVPLATINUM_FM: ++ case SAA7134_BOARD_FLYTVPLATINUM_MINI2: ++ case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: ++ ir_codes = RC_MAP_FLYVIDEO; ++ mask_keycode = 0xEC00000; ++ mask_keydown = 0x0040000; ++ break; ++ case SAA7134_BOARD_CINERGY400: ++ case SAA7134_BOARD_CINERGY600: ++ case SAA7134_BOARD_CINERGY600_MK3: ++ ir_codes = RC_MAP_CINERGY; ++ mask_keycode = 0x00003f; ++ mask_keyup = 0x040000; ++ break; ++ case SAA7134_BOARD_ECS_TVP3XP: ++ case SAA7134_BOARD_ECS_TVP3XP_4CB5: ++ ir_codes = RC_MAP_EZTV; ++ mask_keycode = 0x00017c; ++ mask_keyup = 0x000002; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_KWORLD_XPERT: ++ case SAA7134_BOARD_AVACSSMARTTV: ++ ir_codes = RC_MAP_PIXELVIEW; ++ mask_keycode = 0x00001F; ++ mask_keyup = 0x000020; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_MD2819: ++ case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: ++ case SAA7134_BOARD_AVERMEDIA_305: ++ case SAA7134_BOARD_AVERMEDIA_307: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_305: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_505: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_307: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_507: ++ case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ++ case SAA7134_BOARD_AVERMEDIA_M102: ++ case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: ++ ir_codes = RC_MAP_AVERMEDIA; ++ mask_keycode = 0x0007C8; ++ mask_keydown = 0x000010; ++ polling = 50; // ms ++ /* GPIO stuff moved to __saa7134_ir_start() */ ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M135A: ++ ir_codes = RC_MAP_AVERMEDIA_M135A; ++ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_M733A: ++ ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6; ++ mask_keydown = 0x0040000; ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_777: ++ case SAA7134_BOARD_AVERMEDIA_A16AR: ++ ir_codes = RC_MAP_AVERMEDIA; ++ mask_keycode = 0x02F200; ++ mask_keydown = 0x000400; ++ polling = 50; // ms ++ /* GPIO stuff moved to __saa7134_ir_start() */ ++ break; ++ case SAA7134_BOARD_AVERMEDIA_A16D: ++ ir_codes = RC_MAP_AVERMEDIA_A16D; ++ mask_keycode = 0x02F200; ++ mask_keydown = 0x000400; ++ polling = 50; /* ms */ ++ /* GPIO stuff moved to __saa7134_ir_start() */ ++ break; ++ case SAA7134_BOARD_KWORLD_TERMINATOR: ++ ir_codes = RC_MAP_PIXELVIEW; ++ mask_keycode = 0x00001f; ++ mask_keyup = 0x000060; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_MANLI_MTV001: ++ case SAA7134_BOARD_MANLI_MTV002: ++ ir_codes = RC_MAP_MANLI; ++ mask_keycode = 0x001f00; ++ mask_keyup = 0x004000; ++ polling = 50; /* ms */ ++ break; ++ case SAA7134_BOARD_BEHOLD_409FM: ++ case SAA7134_BOARD_BEHOLD_401: ++ case SAA7134_BOARD_BEHOLD_403: ++ case SAA7134_BOARD_BEHOLD_403FM: ++ case SAA7134_BOARD_BEHOLD_405: ++ case SAA7134_BOARD_BEHOLD_405FM: ++ case SAA7134_BOARD_BEHOLD_407: ++ case SAA7134_BOARD_BEHOLD_407FM: ++ case SAA7134_BOARD_BEHOLD_409: ++ case SAA7134_BOARD_BEHOLD_505FM: ++ case SAA7134_BOARD_BEHOLD_505RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_505RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_507_9FM: ++ case SAA7134_BOARD_BEHOLD_507RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_507RDS_MK5: ++ ir_codes = RC_MAP_MANLI; ++ mask_keycode = 0x003f00; ++ mask_keyup = 0x004000; ++ polling = 50; /* ms */ ++ break; ++ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: ++ ir_codes = RC_MAP_BEHOLD_COLUMBUS; ++ mask_keycode = 0x003f00; ++ mask_keyup = 0x004000; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: ++ ir_codes = RC_MAP_PCTV_SEDNA; ++ mask_keycode = 0x001f00; ++ mask_keyup = 0x004000; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_GOTVIEW_7135: ++ ir_codes = RC_MAP_GOTVIEW7135; ++ mask_keycode = 0x0003CC; ++ mask_keydown = 0x000010; ++ polling = 5; /* ms */ ++ /* GPIO stuff moved to __saa7134_ir_start() */ ++ break; ++ case SAA7134_BOARD_VIDEOMATE_TV_PVR: ++ case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: ++ case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: ++ ir_codes = RC_MAP_VIDEOMATE_TV_PVR; ++ mask_keycode = 0x00003F; ++ mask_keyup = 0x400000; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_PROTEUS_2309: ++ ir_codes = RC_MAP_PROTEUS_2309; ++ mask_keycode = 0x00007F; ++ mask_keyup = 0x000080; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_VIDEOMATE_DVBT_300: ++ case SAA7134_BOARD_VIDEOMATE_DVBT_200: ++ ir_codes = RC_MAP_VIDEOMATE_TV_PVR; ++ mask_keycode = 0x003F00; ++ mask_keyup = 0x040000; ++ break; ++ case SAA7134_BOARD_FLYDVBS_LR300: ++ case SAA7134_BOARD_FLYDVBT_LR301: ++ case SAA7134_BOARD_FLYDVBTDUO: ++ ir_codes = RC_MAP_FLYDVB; ++ mask_keycode = 0x0001F00; ++ mask_keydown = 0x0040000; ++ break; ++ case SAA7134_BOARD_ASUSTeK_P7131_DUAL: ++ case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: ++ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: ++ ir_codes = RC_MAP_ASUS_PC39; ++ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ case SAA7134_BOARD_ASUSTeK_PS3_100: ++ ir_codes = RC_MAP_ASUS_PS3_100; ++ mask_keydown = 0x0040000; ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ case SAA7134_BOARD_ENCORE_ENLTV: ++ case SAA7134_BOARD_ENCORE_ENLTV_FM: ++ ir_codes = RC_MAP_ENCORE_ENLTV; ++ mask_keycode = 0x00007f; ++ mask_keyup = 0x040000; ++ polling = 50; // ms ++ break; ++ case SAA7134_BOARD_ENCORE_ENLTV_FM53: ++ case SAA7134_BOARD_ENCORE_ENLTV_FM3: ++ ir_codes = RC_MAP_ENCORE_ENLTV_FM53; ++ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ case SAA7134_BOARD_10MOONSTVMASTER3: ++ ir_codes = RC_MAP_ENCORE_ENLTV; ++ mask_keycode = 0x5f80000; ++ mask_keyup = 0x8000000; ++ polling = 50; //ms ++ break; ++ case SAA7134_BOARD_GENIUS_TVGO_A11MCE: ++ ir_codes = RC_MAP_GENIUS_TVGO_A11MCE; ++ mask_keycode = 0xff; ++ mask_keydown = 0xf00000; ++ polling = 50; /* ms */ ++ break; ++ case SAA7134_BOARD_REAL_ANGEL_220: ++ ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS; ++ mask_keycode = 0x3f00; ++ mask_keyup = 0x4000; ++ polling = 50; /* ms */ ++ break; ++ case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: ++ ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG; ++ mask_keycode = 0x7f; ++ polling = 40; /* ms */ ++ break; ++ case SAA7134_BOARD_VIDEOMATE_S350: ++ ir_codes = RC_MAP_VIDEOMATE_S350; ++ mask_keycode = 0x003f00; ++ mask_keydown = 0x040000; ++ break; ++ case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: ++ ir_codes = RC_MAP_WINFAST; ++ mask_keycode = 0x5f00; ++ mask_keyup = 0x020000; ++ polling = 50; /* ms */ ++ break; ++ case SAA7134_BOARD_VIDEOMATE_M1F: ++ ir_codes = RC_MAP_VIDEOMATE_K100; ++ mask_keycode = 0x0ff00; ++ mask_keyup = 0x040000; ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1150: ++ case SAA7134_BOARD_HAUPPAUGE_HVR1120: ++ ir_codes = RC_MAP_HAUPPAUGE; ++ mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ ++ mask_keyup = 0x0040000; ++ mask_keycode = 0xffff; ++ raw_decode = true; ++ break; ++ } ++ if (NULL == ir_codes) { ++ printk("%s: Oops: IR config error [card=%d]\n", ++ dev->name, dev->board); ++ return -ENODEV; ++ } ++ ++ ir = kzalloc(sizeof(*ir), GFP_KERNEL); ++ rc = rc_allocate_device(); ++ if (!ir || !rc) { ++ err = -ENOMEM; ++ goto err_out_free; ++ } ++ ++ ir->dev = rc; ++ dev->remote = ir; ++ ++ /* init hardware-specific stuff */ ++ ir->mask_keycode = mask_keycode; ++ ir->mask_keydown = mask_keydown; ++ ir->mask_keyup = mask_keyup; ++ ir->polling = polling; ++ ir->raw_decode = raw_decode; ++ ++ /* init input device */ ++ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", ++ saa7134_boards[dev->board].name); ++ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", ++ pci_name(dev->pci)); ++ ++ rc->priv = dev; ++ rc->open = saa7134_ir_open; ++ rc->close = saa7134_ir_close; ++ if (raw_decode) ++ rc->driver_type = RC_DRIVER_IR_RAW; ++ ++ rc->input_name = ir->name; ++ rc->input_phys = ir->phys; ++ rc->input_id.bustype = BUS_PCI; ++ rc->input_id.version = 1; ++ if (dev->pci->subsystem_vendor) { ++ rc->input_id.vendor = dev->pci->subsystem_vendor; ++ rc->input_id.product = dev->pci->subsystem_device; ++ } else { ++ rc->input_id.vendor = dev->pci->vendor; ++ rc->input_id.product = dev->pci->device; ++ } ++ rc->dev.parent = &dev->pci->dev; ++ rc->map_name = ir_codes; ++ rc->driver_name = MODULE_NAME; ++ ++ err = rc_register_device(rc); ++ if (err) ++ goto err_out_free; ++ ++ return 0; ++ ++err_out_free: ++ rc_free_device(rc); ++ dev->remote = NULL; ++ kfree(ir); ++ return err; ++} ++ ++void saa7134_input_fini(struct saa7134_dev *dev) ++{ ++ if (NULL == dev->remote) ++ return; ++ ++ saa7134_ir_stop(dev); ++ rc_unregister_device(dev->remote->dev); ++ kfree(dev->remote); ++ dev->remote = NULL; ++} ++ ++void saa7134_probe_i2c_ir(struct saa7134_dev *dev) ++{ ++ struct i2c_board_info info; ++ struct i2c_msg msg_msi = { ++ .addr = 0x50, ++ .flags = I2C_M_RD, ++ .len = 0, ++ .buf = NULL, ++ }; ++ int rc; ++ ++ if (disable_ir) { ++ dprintk("IR has been disabled, not probing for i2c remote\n"); ++ return; ++ } ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ memset(&dev->init_data, 0, sizeof(dev->init_data)); ++ strlcpy(info.type, "ir_video", I2C_NAME_SIZE); ++ ++ switch (dev->board) { ++ case SAA7134_BOARD_PINNACLE_PCTV_110i: ++ case SAA7134_BOARD_PINNACLE_PCTV_310i: ++ dev->init_data.name = "Pinnacle PCTV"; ++ if (pinnacle_remote == 0) { ++ dev->init_data.get_key = get_key_pinnacle_color; ++ dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR; ++ info.addr = 0x47; ++ } else { ++ dev->init_data.get_key = get_key_pinnacle_grey; ++ dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; ++ info.addr = 0x47; ++ } ++ break; ++ case SAA7134_BOARD_UPMOST_PURPLE_TV: ++ dev->init_data.name = "Purple TV"; ++ dev->init_data.get_key = get_key_purpletv; ++ dev->init_data.ir_codes = RC_MAP_PURPLETV; ++ info.addr = 0x7a; ++ break; ++ case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: ++ dev->init_data.name = "MSI TV@nywhere Plus"; ++ dev->init_data.get_key = get_key_msi_tvanywhere_plus; ++ dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS; ++ /* ++ * MSI TV@nyware Plus requires more frequent polling ++ * otherwise it will miss some keypresses ++ */ ++ dev->init_data.polling_interval = 50; ++ info.addr = 0x30; ++ /* MSI TV@nywhere Plus controller doesn't seem to ++ respond to probes unless we read something from ++ an existing device. Weird... ++ REVISIT: might no longer be needed */ ++ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); ++ dprintk("probe 0x%02x @ %s: %s\n", ++ msg_msi.addr, dev->i2c_adap.name, ++ (1 == rc) ? "yes" : "no"); ++ break; ++ case SAA7134_BOARD_KWORLD_PC150U: ++ /* copied and modified from MSI TV@nywhere Plus */ ++ dev->init_data.name = "Kworld PC150-U"; ++ dev->init_data.get_key = get_key_kworld_pc150u; ++ dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U; ++ info.addr = 0x30; ++ /* MSI TV@nywhere Plus controller doesn't seem to ++ respond to probes unless we read something from ++ an existing device. Weird... ++ REVISIT: might no longer be needed */ ++ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); ++ dprintk("probe 0x%02x @ %s: %s\n", ++ msg_msi.addr, dev->i2c_adap.name, ++ (1 == rc) ? "yes" : "no"); ++ break; ++ case SAA7134_BOARD_HAUPPAUGE_HVR1110: ++ dev->init_data.name = "HVR 1110"; ++ dev->init_data.get_key = get_key_hvr1110; ++ dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; ++ info.addr = 0x71; ++ break; ++ case SAA7134_BOARD_BEHOLD_607FM_MK3: ++ case SAA7134_BOARD_BEHOLD_607FM_MK5: ++ case SAA7134_BOARD_BEHOLD_609FM_MK3: ++ case SAA7134_BOARD_BEHOLD_609FM_MK5: ++ case SAA7134_BOARD_BEHOLD_607RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_607RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_609RDS_MK3: ++ case SAA7134_BOARD_BEHOLD_609RDS_MK5: ++ case SAA7134_BOARD_BEHOLD_M6: ++ case SAA7134_BOARD_BEHOLD_M63: ++ case SAA7134_BOARD_BEHOLD_M6_EXTRA: ++ case SAA7134_BOARD_BEHOLD_H6: ++ case SAA7134_BOARD_BEHOLD_X7: ++ case SAA7134_BOARD_BEHOLD_H7: ++ case SAA7134_BOARD_BEHOLD_A7: ++ dev->init_data.name = "BeholdTV"; ++ dev->init_data.get_key = get_key_beholdm6xx; ++ dev->init_data.ir_codes = RC_MAP_BEHOLD; ++ dev->init_data.type = RC_BIT_NEC; ++ info.addr = 0x2d; ++ break; ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: ++ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: ++ info.addr = 0x40; ++ break; ++ case SAA7134_BOARD_FLYDVB_TRIO: ++ dev->init_data.name = "FlyDVB Trio"; ++ dev->init_data.get_key = get_key_flydvb_trio; ++ dev->init_data.ir_codes = RC_MAP_FLYDVB; ++ info.addr = 0x0b; ++ break; ++ default: ++ dprintk("No I2C IR support for board %x\n", dev->board); ++ return; ++ } ++ ++ if (dev->init_data.name) ++ info.platform_data = &dev->init_data; ++ i2c_new_device(&dev->i2c_adap, &info); ++} ++ ++static int saa7134_raw_decode_irq(struct saa7134_dev *dev) ++{ ++ struct saa7134_card_ir *ir = dev->remote; ++ unsigned long timeout; ++ int space; ++ ++ /* Generate initial event */ ++ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); ++ space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; ++ ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE); ++ ++ /* ++ * Wait 15 ms from the start of the first IR event before processing ++ * the event. This time is enough for NEC protocol. May need adjustments ++ * to work with other protocols. ++ */ ++ smp_mb(); ++ ++ if (!timer_pending(&ir->timer)) { ++ timeout = jiffies + msecs_to_jiffies(15); ++ mod_timer(&ir->timer, timeout); ++ } ++ ++ return 1; ++} +diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h +new file mode 100644 +index 0000000..e7e0af1 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-reg.h +@@ -0,0 +1,378 @@ ++/* ++ * ++ * philips saa7134 registers ++ */ ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * PCI ID's ++ */ ++#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130 ++# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130 ++#endif ++#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133 ++# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133 ++#endif ++#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134 ++# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134 ++#endif ++#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135 ++# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135 ++#endif ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * registers -- 32 bit ++ */ ++ ++/* DMA channels, n = 0 ... 6 */ ++#define SAA7134_RS_BA1(n) ((0x200 >> 2) + 4*n) ++#define SAA7134_RS_BA2(n) ((0x204 >> 2) + 4*n) ++#define SAA7134_RS_PITCH(n) ((0x208 >> 2) + 4*n) ++#define SAA7134_RS_CONTROL(n) ((0x20c >> 2) + 4*n) ++#define SAA7134_RS_CONTROL_WSWAP (0x01 << 25) ++#define SAA7134_RS_CONTROL_BSWAP (0x01 << 24) ++#define SAA7134_RS_CONTROL_BURST_2 (0x01 << 21) ++#define SAA7134_RS_CONTROL_BURST_4 (0x02 << 21) ++#define SAA7134_RS_CONTROL_BURST_8 (0x03 << 21) ++#define SAA7134_RS_CONTROL_BURST_16 (0x04 << 21) ++#define SAA7134_RS_CONTROL_BURST_32 (0x05 << 21) ++#define SAA7134_RS_CONTROL_BURST_64 (0x06 << 21) ++#define SAA7134_RS_CONTROL_BURST_MAX (0x07 << 21) ++#define SAA7134_RS_CONTROL_ME (0x01 << 20) ++#define SAA7134_FIFO_SIZE (0x2a0 >> 2) ++#define SAA7134_THRESHOULD (0x2a4 >> 2) ++ ++#define SAA7133_NUM_SAMPLES (0x588 >> 2) ++#define SAA7133_AUDIO_CHANNEL (0x58c >> 2) ++#define SAA7133_AUDIO_FORMAT (0x58f >> 2) ++#define SAA7133_DIGITAL_OUTPUT_SEL1 (0x46c >> 2) ++#define SAA7133_DIGITAL_OUTPUT_SEL2 (0x470 >> 2) ++#define SAA7133_DIGITAL_INPUT_XBAR1 (0x464 >> 2) ++#define SAA7133_ANALOG_IO_SELECT (0x594 >> 2) ++ ++/* main control */ ++#define SAA7134_MAIN_CTRL (0x2a8 >> 2) ++#define SAA7134_MAIN_CTRL_VPLLE (1 << 15) ++#define SAA7134_MAIN_CTRL_APLLE (1 << 14) ++#define SAA7134_MAIN_CTRL_EXOSC (1 << 13) ++#define SAA7134_MAIN_CTRL_EVFE1 (1 << 12) ++#define SAA7134_MAIN_CTRL_EVFE2 (1 << 11) ++#define SAA7134_MAIN_CTRL_ESFE (1 << 10) ++#define SAA7134_MAIN_CTRL_EBADC (1 << 9) ++#define SAA7134_MAIN_CTRL_EBDAC (1 << 8) ++#define SAA7134_MAIN_CTRL_TE6 (1 << 6) ++#define SAA7134_MAIN_CTRL_TE5 (1 << 5) ++#define SAA7134_MAIN_CTRL_TE4 (1 << 4) ++#define SAA7134_MAIN_CTRL_TE3 (1 << 3) ++#define SAA7134_MAIN_CTRL_TE2 (1 << 2) ++#define SAA7134_MAIN_CTRL_TE1 (1 << 1) ++#define SAA7134_MAIN_CTRL_TE0 (1 << 0) ++ ++/* DMA status */ ++#define SAA7134_DMA_STATUS (0x2ac >> 2) ++ ++/* audio / video status */ ++#define SAA7134_AV_STATUS (0x2c0 >> 2) ++#define SAA7134_AV_STATUS_STEREO (1 << 17) ++#define SAA7134_AV_STATUS_DUAL (1 << 16) ++#define SAA7134_AV_STATUS_PILOT (1 << 15) ++#define SAA7134_AV_STATUS_SMB (1 << 14) ++#define SAA7134_AV_STATUS_DMB (1 << 13) ++#define SAA7134_AV_STATUS_VDSP (1 << 12) ++#define SAA7134_AV_STATUS_IIC_STATUS (3 << 10) ++#define SAA7134_AV_STATUS_MVM (7 << 7) ++#define SAA7134_AV_STATUS_FIDT (1 << 6) ++#define SAA7134_AV_STATUS_INTL (1 << 5) ++#define SAA7134_AV_STATUS_RDCAP (1 << 4) ++#define SAA7134_AV_STATUS_PWR_ON (1 << 3) ++#define SAA7134_AV_STATUS_LOAD_ERR (1 << 2) ++#define SAA7134_AV_STATUS_TRIG_ERR (1 << 1) ++#define SAA7134_AV_STATUS_CONF_ERR (1 << 0) ++ ++/* interrupt */ ++#define SAA7134_IRQ1 (0x2c4 >> 2) ++#define SAA7134_IRQ1_INTE_RA3_1 (1 << 25) ++#define SAA7134_IRQ1_INTE_RA3_0 (1 << 24) ++#define SAA7134_IRQ1_INTE_RA2_3 (1 << 19) ++#define SAA7134_IRQ1_INTE_RA2_2 (1 << 18) ++#define SAA7134_IRQ1_INTE_RA2_1 (1 << 17) ++#define SAA7134_IRQ1_INTE_RA2_0 (1 << 16) ++#define SAA7134_IRQ1_INTE_RA1_3 (1 << 11) ++#define SAA7134_IRQ1_INTE_RA1_2 (1 << 10) ++#define SAA7134_IRQ1_INTE_RA1_1 (1 << 9) ++#define SAA7134_IRQ1_INTE_RA1_0 (1 << 8) ++#define SAA7134_IRQ1_INTE_RA0_7 (1 << 7) ++#define SAA7134_IRQ1_INTE_RA0_6 (1 << 6) ++#define SAA7134_IRQ1_INTE_RA0_5 (1 << 5) ++#define SAA7134_IRQ1_INTE_RA0_4 (1 << 4) ++#define SAA7134_IRQ1_INTE_RA0_3 (1 << 3) ++#define SAA7134_IRQ1_INTE_RA0_2 (1 << 2) ++#define SAA7134_IRQ1_INTE_RA0_1 (1 << 1) ++#define SAA7134_IRQ1_INTE_RA0_0 (1 << 0) ++ ++#define SAA7134_IRQ2 (0x2c8 >> 2) ++#define SAA7134_IRQ2_INTE_GPIO23_N (1 << 17) /* negative edge */ ++#define SAA7134_IRQ2_INTE_GPIO23_P (1 << 16) /* positive edge */ ++#define SAA7134_IRQ2_INTE_GPIO22_N (1 << 15) /* negative edge */ ++#define SAA7134_IRQ2_INTE_GPIO22_P (1 << 14) /* positive edge */ ++#define SAA7134_IRQ2_INTE_GPIO18_N (1 << 13) /* negative edge */ ++#define SAA7134_IRQ2_INTE_GPIO18_P (1 << 12) /* positive edge */ ++#define SAA7134_IRQ2_INTE_GPIO16_N (1 << 11) /* negative edge */ ++#define SAA7134_IRQ2_INTE_GPIO16_P (1 << 10) /* positive edge */ ++#define SAA7134_IRQ2_INTE_SC2 (1 << 9) ++#define SAA7134_IRQ2_INTE_SC1 (1 << 8) ++#define SAA7134_IRQ2_INTE_SC0 (1 << 7) ++#define SAA7134_IRQ2_INTE_DEC4 (1 << 6) ++#define SAA7134_IRQ2_INTE_DEC3 (1 << 5) ++#define SAA7134_IRQ2_INTE_DEC2 (1 << 4) ++#define SAA7134_IRQ2_INTE_DEC1 (1 << 3) ++#define SAA7134_IRQ2_INTE_DEC0 (1 << 2) ++#define SAA7134_IRQ2_INTE_PE (1 << 1) ++#define SAA7134_IRQ2_INTE_AR (1 << 0) ++ ++#define SAA7134_IRQ_REPORT (0x2cc >> 2) ++#define SAA7134_IRQ_REPORT_GPIO23 (1 << 17) ++#define SAA7134_IRQ_REPORT_GPIO22 (1 << 16) ++#define SAA7134_IRQ_REPORT_GPIO18 (1 << 15) ++#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) ++#define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13) ++#define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12) ++#define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11) ++#define SAA7134_IRQ_REPORT_MMC (1 << 10) ++#define SAA7134_IRQ_REPORT_FIDT (1 << 9) ++#define SAA7134_IRQ_REPORT_INTL (1 << 8) ++#define SAA7134_IRQ_REPORT_RDCAP (1 << 7) ++#define SAA7134_IRQ_REPORT_PWR_ON (1 << 6) ++#define SAA7134_IRQ_REPORT_PE (1 << 5) ++#define SAA7134_IRQ_REPORT_AR (1 << 4) ++#define SAA7134_IRQ_REPORT_DONE_RA3 (1 << 3) ++#define SAA7134_IRQ_REPORT_DONE_RA2 (1 << 2) ++#define SAA7134_IRQ_REPORT_DONE_RA1 (1 << 1) ++#define SAA7134_IRQ_REPORT_DONE_RA0 (1 << 0) ++#define SAA7134_IRQ_STATUS (0x2d0 >> 2) ++ ++ ++/* ------------------------------------------------------------------ */ ++/* ++ * registers -- 8 bit ++ */ ++ ++/* video decoder */ ++#define SAA7134_INCR_DELAY 0x101 ++#define SAA7134_ANALOG_IN_CTRL1 0x102 ++#define SAA7134_ANALOG_IN_CTRL2 0x103 ++#define SAA7134_ANALOG_IN_CTRL3 0x104 ++#define SAA7134_ANALOG_IN_CTRL4 0x105 ++#define SAA7134_HSYNC_START 0x106 ++#define SAA7134_HSYNC_STOP 0x107 ++#define SAA7134_SYNC_CTRL 0x108 ++#define SAA7134_LUMA_CTRL 0x109 ++#define SAA7134_DEC_LUMA_BRIGHT 0x10a ++#define SAA7134_DEC_LUMA_CONTRAST 0x10b ++#define SAA7134_DEC_CHROMA_SATURATION 0x10c ++#define SAA7134_DEC_CHROMA_HUE 0x10d ++#define SAA7134_CHROMA_CTRL1 0x10e ++#define SAA7134_CHROMA_GAIN 0x10f ++#define SAA7134_CHROMA_CTRL2 0x110 ++#define SAA7134_MODE_DELAY_CTRL 0x111 ++ ++#define SAA7134_ANALOG_ADC 0x114 ++#define SAA7134_VGATE_START 0x115 ++#define SAA7134_VGATE_STOP 0x116 ++#define SAA7134_MISC_VGATE_MSB 0x117 ++#define SAA7134_RAW_DATA_GAIN 0x118 ++#define SAA7134_RAW_DATA_OFFSET 0x119 ++#define SAA7134_STATUS_VIDEO1 0x11e ++#define SAA7134_STATUS_VIDEO2 0x11f ++ ++/* video scaler */ ++#define SAA7134_SOURCE_TIMING1 0x000 ++#define SAA7134_SOURCE_TIMING2 0x001 ++#define SAA7134_REGION_ENABLE 0x004 ++#define SAA7134_SCALER_STATUS0 0x006 ++#define SAA7134_SCALER_STATUS1 0x007 ++#define SAA7134_START_GREEN 0x00c ++#define SAA7134_START_BLUE 0x00d ++#define SAA7134_START_RED 0x00e ++#define SAA7134_GREEN_PATH(x) (0x010 +x) ++#define SAA7134_BLUE_PATH(x) (0x020 +x) ++#define SAA7134_RED_PATH(x) (0x030 +x) ++ ++#define TASK_A 0x040 ++#define TASK_B 0x080 ++#define SAA7134_TASK_CONDITIONS(t) (0x000 +t) ++#define SAA7134_FIELD_HANDLING(t) (0x001 +t) ++#define SAA7134_DATA_PATH(t) (0x002 +t) ++#define SAA7134_VBI_H_START1(t) (0x004 +t) ++#define SAA7134_VBI_H_START2(t) (0x005 +t) ++#define SAA7134_VBI_H_STOP1(t) (0x006 +t) ++#define SAA7134_VBI_H_STOP2(t) (0x007 +t) ++#define SAA7134_VBI_V_START1(t) (0x008 +t) ++#define SAA7134_VBI_V_START2(t) (0x009 +t) ++#define SAA7134_VBI_V_STOP1(t) (0x00a +t) ++#define SAA7134_VBI_V_STOP2(t) (0x00b +t) ++#define SAA7134_VBI_H_LEN1(t) (0x00c +t) ++#define SAA7134_VBI_H_LEN2(t) (0x00d +t) ++#define SAA7134_VBI_V_LEN1(t) (0x00e +t) ++#define SAA7134_VBI_V_LEN2(t) (0x00f +t) ++ ++#define SAA7134_VIDEO_H_START1(t) (0x014 +t) ++#define SAA7134_VIDEO_H_START2(t) (0x015 +t) ++#define SAA7134_VIDEO_H_STOP1(t) (0x016 +t) ++#define SAA7134_VIDEO_H_STOP2(t) (0x017 +t) ++#define SAA7134_VIDEO_V_START1(t) (0x018 +t) ++#define SAA7134_VIDEO_V_START2(t) (0x019 +t) ++#define SAA7134_VIDEO_V_STOP1(t) (0x01a +t) ++#define SAA7134_VIDEO_V_STOP2(t) (0x01b +t) ++#define SAA7134_VIDEO_PIXELS1(t) (0x01c +t) ++#define SAA7134_VIDEO_PIXELS2(t) (0x01d +t) ++#define SAA7134_VIDEO_LINES1(t) (0x01e +t) ++#define SAA7134_VIDEO_LINES2(t) (0x01f +t) ++ ++#define SAA7134_H_PRESCALE(t) (0x020 +t) ++#define SAA7134_ACC_LENGTH(t) (0x021 +t) ++#define SAA7134_LEVEL_CTRL(t) (0x022 +t) ++#define SAA7134_FIR_PREFILTER_CTRL(t) (0x023 +t) ++#define SAA7134_LUMA_BRIGHT(t) (0x024 +t) ++#define SAA7134_LUMA_CONTRAST(t) (0x025 +t) ++#define SAA7134_CHROMA_SATURATION(t) (0x026 +t) ++#define SAA7134_VBI_H_SCALE_INC1(t) (0x028 +t) ++#define SAA7134_VBI_H_SCALE_INC2(t) (0x029 +t) ++#define SAA7134_VBI_PHASE_OFFSET_LUMA(t) (0x02a +t) ++#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t) (0x02b +t) ++#define SAA7134_H_SCALE_INC1(t) (0x02c +t) ++#define SAA7134_H_SCALE_INC2(t) (0x02d +t) ++#define SAA7134_H_PHASE_OFF_LUMA(t) (0x02e +t) ++#define SAA7134_H_PHASE_OFF_CHROMA(t) (0x02f +t) ++#define SAA7134_V_SCALE_RATIO1(t) (0x030 +t) ++#define SAA7134_V_SCALE_RATIO2(t) (0x031 +t) ++#define SAA7134_V_FILTER(t) (0x032 +t) ++#define SAA7134_V_PHASE_OFFSET0(t) (0x034 +t) ++#define SAA7134_V_PHASE_OFFSET1(t) (0x035 +t) ++#define SAA7134_V_PHASE_OFFSET2(t) (0x036 +t) ++#define SAA7134_V_PHASE_OFFSET3(t) (0x037 +t) ++ ++/* clipping & dma */ ++#define SAA7134_OFMT_VIDEO_A 0x300 ++#define SAA7134_OFMT_DATA_A 0x301 ++#define SAA7134_OFMT_VIDEO_B 0x302 ++#define SAA7134_OFMT_DATA_B 0x303 ++#define SAA7134_ALPHA_NOCLIP 0x304 ++#define SAA7134_ALPHA_CLIP 0x305 ++#define SAA7134_UV_PIXEL 0x308 ++#define SAA7134_CLIP_RED 0x309 ++#define SAA7134_CLIP_GREEN 0x30a ++#define SAA7134_CLIP_BLUE 0x30b ++ ++/* i2c bus */ ++#define SAA7134_I2C_ATTR_STATUS 0x180 ++#define SAA7134_I2C_DATA 0x181 ++#define SAA7134_I2C_CLOCK_SELECT 0x182 ++#define SAA7134_I2C_TIMER 0x183 ++ ++/* audio */ ++#define SAA7134_NICAM_ADD_DATA1 0x140 ++#define SAA7134_NICAM_ADD_DATA2 0x141 ++#define SAA7134_NICAM_STATUS 0x142 ++#define SAA7134_AUDIO_STATUS 0x143 ++#define SAA7134_NICAM_ERROR_COUNT 0x144 ++#define SAA7134_IDENT_SIF 0x145 ++#define SAA7134_LEVEL_READOUT1 0x146 ++#define SAA7134_LEVEL_READOUT2 0x147 ++#define SAA7134_NICAM_ERROR_LOW 0x148 ++#define SAA7134_NICAM_ERROR_HIGH 0x149 ++#define SAA7134_DCXO_IDENT_CTRL 0x14a ++#define SAA7134_DEMODULATOR 0x14b ++#define SAA7134_AGC_GAIN_SELECT 0x14c ++#define SAA7134_CARRIER1_FREQ0 0x150 ++#define SAA7134_CARRIER1_FREQ1 0x151 ++#define SAA7134_CARRIER1_FREQ2 0x152 ++#define SAA7134_CARRIER2_FREQ0 0x154 ++#define SAA7134_CARRIER2_FREQ1 0x155 ++#define SAA7134_CARRIER2_FREQ2 0x156 ++#define SAA7134_NUM_SAMPLES0 0x158 ++#define SAA7134_NUM_SAMPLES1 0x159 ++#define SAA7134_NUM_SAMPLES2 0x15a ++#define SAA7134_AUDIO_FORMAT_CTRL 0x15b ++#define SAA7134_MONITOR_SELECT 0x160 ++#define SAA7134_FM_DEEMPHASIS 0x161 ++#define SAA7134_FM_DEMATRIX 0x162 ++#define SAA7134_CHANNEL1_LEVEL 0x163 ++#define SAA7134_CHANNEL2_LEVEL 0x164 ++#define SAA7134_NICAM_CONFIG 0x165 ++#define SAA7134_NICAM_LEVEL_ADJUST 0x166 ++#define SAA7134_STEREO_DAC_OUTPUT_SELECT 0x167 ++#define SAA7134_I2S_OUTPUT_FORMAT 0x168 ++#define SAA7134_I2S_OUTPUT_SELECT 0x169 ++#define SAA7134_I2S_OUTPUT_LEVEL 0x16a ++#define SAA7134_DSP_OUTPUT_SELECT 0x16b ++#define SAA7134_AUDIO_MUTE_CTRL 0x16c ++#define SAA7134_SIF_SAMPLE_FREQ 0x16d ++#define SAA7134_ANALOG_IO_SELECT 0x16e ++#define SAA7134_AUDIO_CLOCK0 0x170 ++#define SAA7134_AUDIO_CLOCK1 0x171 ++#define SAA7134_AUDIO_CLOCK2 0x172 ++#define SAA7134_AUDIO_PLL_CTRL 0x173 ++#define SAA7134_AUDIO_CLOCKS_PER_FIELD0 0x174 ++#define SAA7134_AUDIO_CLOCKS_PER_FIELD1 0x175 ++#define SAA7134_AUDIO_CLOCKS_PER_FIELD2 0x176 ++ ++/* video port output */ ++#define SAA7134_VIDEO_PORT_CTRL0 0x190 ++#define SAA7134_VIDEO_PORT_CTRL1 0x191 ++#define SAA7134_VIDEO_PORT_CTRL2 0x192 ++#define SAA7134_VIDEO_PORT_CTRL3 0x193 ++#define SAA7134_VIDEO_PORT_CTRL4 0x194 ++#define SAA7134_VIDEO_PORT_CTRL5 0x195 ++#define SAA7134_VIDEO_PORT_CTRL6 0x196 ++#define SAA7134_VIDEO_PORT_CTRL7 0x197 ++#define SAA7134_VIDEO_PORT_CTRL8 0x198 ++ ++/* transport stream interface */ ++#define SAA7134_TS_PARALLEL 0x1a0 ++#define SAA7134_TS_PARALLEL_SERIAL 0x1a1 ++#define SAA7134_TS_SERIAL0 0x1a2 ++#define SAA7134_TS_SERIAL1 0x1a3 ++#define SAA7134_TS_DMA0 0x1a4 ++#define SAA7134_TS_DMA1 0x1a5 ++#define SAA7134_TS_DMA2 0x1a6 ++ ++/* GPIO Controls */ ++#define SAA7134_GPIO_GPRESCAN 0x80 ++#define SAA7134_GPIO_27_25 0x0E ++ ++#define SAA7134_GPIO_GPMODE0 0x1B0 ++#define SAA7134_GPIO_GPMODE1 0x1B1 ++#define SAA7134_GPIO_GPMODE2 0x1B2 ++#define SAA7134_GPIO_GPMODE3 0x1B3 ++#define SAA7134_GPIO_GPSTATUS0 0x1B4 ++#define SAA7134_GPIO_GPSTATUS1 0x1B5 ++#define SAA7134_GPIO_GPSTATUS2 0x1B6 ++#define SAA7134_GPIO_GPSTATUS3 0x1B7 ++ ++/* I2S output */ ++#define SAA7134_I2S_AUDIO_OUTPUT 0x1c0 ++ ++/* test modes */ ++#define SAA7134_SPECIAL_MODE 0x1d0 ++#define SAA7134_PRODUCTION_TEST_MODE 0x1d1 ++ ++/* audio -- saa7133 + saa7135 only */ ++#define SAA7135_DSP_RWSTATE 0x580 ++#define SAA7135_DSP_RWSTATE_ERR (1 << 3) ++#define SAA7135_DSP_RWSTATE_IDA (1 << 2) ++#define SAA7135_DSP_RWSTATE_RDB (1 << 1) ++#define SAA7135_DSP_RWSTATE_WRR (1 << 0) ++ ++#define SAA7135_DSP_RWCLEAR 0x586 ++#define SAA7135_DSP_RWCLEAR_RERR 1 ++ ++#define SAA7133_I2S_AUDIO_CONTROL 0x591 ++/* ------------------------------------------------------------------ */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ ++ +diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c +new file mode 100644 +index 0000000..2e3f4b4 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-ts.c +@@ -0,0 +1,327 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * video4linux video interface ++ * ++ * (c) 2001,02 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int ts_debug; ++module_param(ts_debug, int, 0644); ++MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); ++ ++#define dprintk(fmt, arg...) if (ts_debug) \ ++ printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg) ++ ++/* ------------------------------------------------------------------ */ ++ ++static int buffer_activate(struct saa7134_dev *dev, ++ struct saa7134_buf *buf, ++ struct saa7134_buf *next) ++{ ++ ++ dprintk("buffer_activate [%p]",buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->top_seen = 0; ++ ++ if (NULL == next) ++ next = buf; ++ if (V4L2_FIELD_TOP == buf->vb.field) { ++ dprintk("- [top] buf=%p next=%p\n",buf,next); ++ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); ++ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); ++ } else { ++ dprintk("- [bottom] buf=%p next=%p\n",buf,next); ++ saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); ++ saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); ++ } ++ ++ /* start DMA */ ++ saa7134_set_dmabits(dev); ++ ++ mod_timer(&dev->ts_q.timeout, jiffies+TS_BUFFER_TIMEOUT); ++ ++ if (!dev->ts_started) ++ saa7134_ts_start(dev); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct saa7134_dev *dev = q->priv_data; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ unsigned int lines, llength, size; ++ int err; ++ ++ dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]); ++ ++ llength = TS_PACKET_SIZE; ++ lines = dev->ts.nr_packets; ++ ++ size = lines * llength; ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (buf->vb.size != size) { ++ saa7134_dma_free(q,buf); ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ dprintk("buffer_prepare: needs_init\n"); ++ ++ buf->vb.width = llength; ++ buf->vb.height = lines; ++ buf->vb.size = size; ++ buf->pt = &dev->ts.pt_ts; ++ ++ err = videobuf_iolock(q,&buf->vb,NULL); ++ if (err) ++ goto oops; ++ err = saa7134_pgtable_build(dev->pci,buf->pt, ++ dma->sglist, ++ dma->sglen, ++ saa7134_buffer_startpage(buf)); ++ if (err) ++ goto oops; ++ } ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ buf->activate = buffer_activate; ++ buf->vb.field = field; ++ return 0; ++ ++ oops: ++ saa7134_dma_free(q,buf); ++ return err; ++} ++ ++static int ++buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ struct saa7134_dev *dev = q->priv_data; ++ ++ *size = TS_PACKET_SIZE * dev->ts.nr_packets; ++ if (0 == *count) ++ *count = dev->ts.nr_bufs; ++ *count = saa7134_buffer_count(*size,*count); ++ ++ return 0; ++} ++ ++static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_dev *dev = q->priv_data; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ ++ saa7134_buffer_queue(dev,&dev->ts_q,buf); ++} ++ ++static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ struct saa7134_dev *dev = q->priv_data; ++ ++ if (dev->ts_started) ++ saa7134_ts_stop(dev); ++ ++ saa7134_dma_free(q,buf); ++} ++ ++struct videobuf_queue_ops saa7134_ts_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++EXPORT_SYMBOL_GPL(saa7134_ts_qops); ++ ++/* ----------------------------------------------------------- */ ++/* exported stuff */ ++ ++static unsigned int tsbufs = 8; ++module_param(tsbufs, int, 0444); ++MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32"); ++ ++static unsigned int ts_nr_packets = 64; ++module_param(ts_nr_packets, int, 0444); ++MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)"); ++ ++int saa7134_ts_init_hw(struct saa7134_dev *dev) ++{ ++ /* deactivate TS softreset */ ++ saa_writeb(SAA7134_TS_SERIAL1, 0x00); ++ /* TSSOP high active, TSVAL high active, TSLOCK ignored */ ++ saa_writeb(SAA7134_TS_PARALLEL, 0x6c); ++ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); ++ saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff)); ++ saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff)); ++ /* TSNOPIT=0, TSCOLAP=0 */ ++ saa_writeb(SAA7134_TS_DMA2, ++ ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00)); ++ ++ return 0; ++} ++ ++int saa7134_ts_init1(struct saa7134_dev *dev) ++{ ++ /* sanitycheck insmod options */ ++ if (tsbufs < 2) ++ tsbufs = 2; ++ if (tsbufs > VIDEO_MAX_FRAME) ++ tsbufs = VIDEO_MAX_FRAME; ++ if (ts_nr_packets < 4) ++ ts_nr_packets = 4; ++ if (ts_nr_packets > 312) ++ ts_nr_packets = 312; ++ dev->ts.nr_bufs = tsbufs; ++ dev->ts.nr_packets = ts_nr_packets; ++ ++ INIT_LIST_HEAD(&dev->ts_q.queue); ++ init_timer(&dev->ts_q.timeout); ++ dev->ts_q.timeout.function = saa7134_buffer_timeout; ++ dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q); ++ dev->ts_q.dev = dev; ++ dev->ts_q.need_two = 1; ++ dev->ts_started = 0; ++ saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts); ++ ++ /* init TS hw */ ++ saa7134_ts_init_hw(dev); ++ ++ return 0; ++} ++ ++/* Function for stop TS */ ++int saa7134_ts_stop(struct saa7134_dev *dev) ++{ ++ dprintk("TS stop\n"); ++ ++ BUG_ON(!dev->ts_started); ++ ++ /* Stop TS stream */ ++ switch (saa7134_boards[dev->board].ts_type) { ++ case SAA7134_MPEG_TS_PARALLEL: ++ saa_writeb(SAA7134_TS_PARALLEL, 0x6c); ++ dev->ts_started = 0; ++ break; ++ case SAA7134_MPEG_TS_SERIAL: ++ saa_writeb(SAA7134_TS_SERIAL0, 0x40); ++ dev->ts_started = 0; ++ break; ++ } ++ return 0; ++} ++ ++/* Function for start TS */ ++int saa7134_ts_start(struct saa7134_dev *dev) ++{ ++ dprintk("TS start\n"); ++ ++ BUG_ON(dev->ts_started); ++ ++ /* dma: setup channel 5 (= TS) */ ++ saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff); ++ saa_writeb(SAA7134_TS_DMA1, ++ ((dev->ts.nr_packets - 1) >> 8) & 0xff); ++ /* TSNOPIT=0, TSCOLAP=0 */ ++ saa_writeb(SAA7134_TS_DMA2, ++ (((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00); ++ saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); ++ saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 | ++ SAA7134_RS_CONTROL_ME | ++ (dev->ts.pt_ts.dma >> 12)); ++ ++ /* reset hardware TS buffers */ ++ saa_writeb(SAA7134_TS_SERIAL1, 0x00); ++ saa_writeb(SAA7134_TS_SERIAL1, 0x03); ++ saa_writeb(SAA7134_TS_SERIAL1, 0x00); ++ saa_writeb(SAA7134_TS_SERIAL1, 0x01); ++ ++ /* TS clock non-inverted */ ++ saa_writeb(SAA7134_TS_SERIAL1, 0x00); ++ ++ /* Start TS stream */ ++ switch (saa7134_boards[dev->board].ts_type) { ++ case SAA7134_MPEG_TS_PARALLEL: ++ saa_writeb(SAA7134_TS_SERIAL0, 0x40); ++ saa_writeb(SAA7134_TS_PARALLEL, 0xec | ++ (saa7134_boards[dev->board].ts_force_val << 4)); ++ break; ++ case SAA7134_MPEG_TS_SERIAL: ++ saa_writeb(SAA7134_TS_SERIAL0, 0xd8); ++ saa_writeb(SAA7134_TS_PARALLEL, 0x6c | ++ (saa7134_boards[dev->board].ts_force_val << 4)); ++ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); ++ saa_writeb(SAA7134_TS_SERIAL1, 0x02); ++ break; ++ } ++ ++ dev->ts_started = 1; ++ ++ return 0; ++} ++ ++int saa7134_ts_fini(struct saa7134_dev *dev) ++{ ++ saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); ++ return 0; ++} ++ ++void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) ++{ ++ enum v4l2_field field; ++ ++ spin_lock(&dev->slock); ++ if (dev->ts_q.curr) { ++ field = dev->ts_q.curr->vb.field; ++ if (field == V4L2_FIELD_TOP) { ++ if ((status & 0x100000) != 0x000000) ++ goto done; ++ } else { ++ if ((status & 0x100000) != 0x100000) ++ goto done; ++ } ++ saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE); ++ } ++ saa7134_buffer_next(dev,&dev->ts_q); ++ ++ done: ++ spin_unlock(&dev->slock); ++} ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c +new file mode 100644 +index 0000000..b7a99be +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c +@@ -0,0 +1,1087 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * tv audio decoder (fm stereo, nicam, ...) ++ * ++ * (c) 2001-03 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int audio_debug; ++module_param(audio_debug, int, 0644); ++MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]"); ++ ++static unsigned int audio_ddep; ++module_param(audio_ddep, int, 0644); ++MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite"); ++ ++static int audio_clock_override = UNSET; ++module_param(audio_clock_override, int, 0644); ++ ++static int audio_clock_tweak; ++module_param(audio_clock_tweak, int, 0644); ++MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])"); ++ ++#define dprintk(fmt, arg...) if (audio_debug) \ ++ printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg) ++#define d2printk(fmt, arg...) if (audio_debug > 1) \ ++ printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) ++ ++#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \ ++ dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg))) ++ ++/* msecs */ ++#define SCAN_INITIAL_DELAY 1000 ++#define SCAN_SAMPLE_DELAY 200 ++#define SCAN_SUBCARRIER_DELAY 2000 ++ ++/* ------------------------------------------------------------------ */ ++/* saa7134 code */ ++ ++static struct mainscan { ++ char *name; ++ v4l2_std_id std; ++ int carr; ++} mainscan[] = { ++ { ++ .name = "MN", ++ .std = V4L2_STD_MN, ++ .carr = 4500, ++ },{ ++ .name = "BGH", ++ .std = V4L2_STD_B | V4L2_STD_GH, ++ .carr = 5500, ++ },{ ++ .name = "I", ++ .std = V4L2_STD_PAL_I, ++ .carr = 6000, ++ },{ ++ .name = "DKL", ++ .std = V4L2_STD_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC, ++ .carr = 6500, ++ } ++}; ++ ++static struct saa7134_tvaudio tvaudio[] = { ++ { ++ .name = "PAL-B/G FM-stereo", ++ .std = V4L2_STD_PAL_BG, ++ .mode = TVAUDIO_FM_BG_STEREO, ++ .carr1 = 5500, ++ .carr2 = 5742, ++ },{ ++ .name = "PAL-D/K1 FM-stereo", ++ .std = V4L2_STD_PAL_DK, ++ .carr1 = 6500, ++ .carr2 = 6258, ++ .mode = TVAUDIO_FM_BG_STEREO, ++ },{ ++ .name = "PAL-D/K2 FM-stereo", ++ .std = V4L2_STD_PAL_DK, ++ .carr1 = 6500, ++ .carr2 = 6742, ++ .mode = TVAUDIO_FM_BG_STEREO, ++ },{ ++ .name = "PAL-D/K3 FM-stereo", ++ .std = V4L2_STD_PAL_DK, ++ .carr1 = 6500, ++ .carr2 = 5742, ++ .mode = TVAUDIO_FM_BG_STEREO, ++ },{ ++ .name = "PAL-B/G NICAM", ++ .std = V4L2_STD_PAL_BG, ++ .carr1 = 5500, ++ .carr2 = 5850, ++ .mode = TVAUDIO_NICAM_FM, ++ },{ ++ .name = "PAL-I NICAM", ++ .std = V4L2_STD_PAL_I, ++ .carr1 = 6000, ++ .carr2 = 6552, ++ .mode = TVAUDIO_NICAM_FM, ++ },{ ++ .name = "PAL-D/K NICAM", ++ .std = V4L2_STD_PAL_DK, ++ .carr1 = 6500, ++ .carr2 = 5850, ++ .mode = TVAUDIO_NICAM_FM, ++ },{ ++ .name = "SECAM-L NICAM", ++ .std = V4L2_STD_SECAM_L, ++ .carr1 = 6500, ++ .carr2 = 5850, ++ .mode = TVAUDIO_NICAM_AM, ++ },{ ++ .name = "SECAM-D/K NICAM", ++ .std = V4L2_STD_SECAM_DK, ++ .carr1 = 6500, ++ .carr2 = 5850, ++ .mode = TVAUDIO_NICAM_FM, ++ },{ ++ .name = "NTSC-A2 FM-stereo", ++ .std = V4L2_STD_NTSC, ++ .carr1 = 4500, ++ .carr2 = 4724, ++ .mode = TVAUDIO_FM_K_STEREO, ++ },{ ++ .name = "NTSC-M", ++ .std = V4L2_STD_NTSC, ++ .carr1 = 4500, ++ .carr2 = -1, ++ .mode = TVAUDIO_FM_MONO, ++ } ++}; ++#define TVAUDIO ARRAY_SIZE(tvaudio) ++ ++/* ------------------------------------------------------------------ */ ++ ++static u32 tvaudio_carr2reg(u32 carrier) ++{ ++ u64 a = carrier; ++ ++ a <<= 24; ++ do_div(a,12288); ++ return a; ++} ++ ++static void tvaudio_setcarrier(struct saa7134_dev *dev, ++ int primary, int secondary) ++{ ++ if (-1 == secondary) ++ secondary = primary; ++ saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary)); ++ saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary)); ++} ++ ++#define SAA7134_MUTE_MASK 0xbb ++#define SAA7134_MUTE_ANALOG 0x04 ++#define SAA7134_MUTE_I2S 0x40 ++ ++static void mute_input_7134(struct saa7134_dev *dev) ++{ ++ unsigned int mute; ++ struct saa7134_input *in; ++ int ausel=0, ics=0, ocs=0; ++ int mask; ++ ++ /* look what is to do ... */ ++ in = dev->input; ++ mute = (dev->ctl_mute || ++ (dev->automute && (&card(dev).radio) != in)); ++ if (card(dev).mute.name) { ++ /* ++ * 7130 - we'll mute using some unconnected audio input ++ * 7134 - we'll probably should switch external mux with gpio ++ */ ++ if (mute) ++ in = &card(dev).mute; ++ } ++ ++ if (dev->hw_mute == mute && ++ dev->hw_input == in && !dev->insuspend) { ++ dprintk("mute/input: nothing to do [mute=%d,input=%s]\n", ++ mute,in->name); ++ return; ++ } ++ ++ dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", ++ dev->ctl_mute,dev->automute,dev->input->name,mute,in->name); ++ dev->hw_mute = mute; ++ dev->hw_input = in; ++ ++ if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device) ++ /* 7134 mute */ ++ saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ? ++ SAA7134_MUTE_MASK | ++ SAA7134_MUTE_ANALOG | ++ SAA7134_MUTE_I2S : ++ SAA7134_MUTE_MASK); ++ ++ /* switch internal audio mux */ ++ switch (in->amux) { ++ case TV: ausel=0xc0; ics=0x00; ocs=0x02; break; ++ case LINE1: ausel=0x80; ics=0x00; ocs=0x00; break; ++ case LINE2: ausel=0x80; ics=0x08; ocs=0x01; break; ++ case LINE2_LEFT: ausel=0x80; ics=0x08; ocs=0x05; break; ++ } ++ saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel); ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics); ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs); ++ // for oss, we need to change the clock configuration ++ if (in->amux == TV) ++ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); ++ else ++ saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x01); ++ ++ /* switch gpio-connected external audio mux */ ++ if (0 == card(dev).gpiomask) ++ return; ++ ++ mask = card(dev).gpiomask; ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio); ++ saa7134_track_gpio(dev,in->name); ++} ++ ++static void tvaudio_setmode(struct saa7134_dev *dev, ++ struct saa7134_tvaudio *audio, ++ char *note) ++{ ++ int acpf, tweak = 0; ++ ++ if (dev->tvnorm->id == V4L2_STD_NTSC) { ++ acpf = 0x19066; ++ } else { ++ acpf = 0x1e000; ++ } ++ if (audio_clock_tweak > -1024 && audio_clock_tweak < 1024) ++ tweak = audio_clock_tweak; ++ ++ if (note) ++ dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n", ++ note,audio->name, ++ audio->carr1 / 1000, audio->carr1 % 1000, ++ audio->carr2 / 1000, audio->carr2 % 1000, ++ acpf, tweak); ++ ++ acpf += tweak; ++ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, (acpf & 0x0000ff) >> 0); ++ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, (acpf & 0x00ff00) >> 8); ++ saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, (acpf & 0x030000) >> 16); ++ tvaudio_setcarrier(dev,audio->carr1,audio->carr2); ++ ++ switch (audio->mode) { ++ case TVAUDIO_FM_MONO: ++ case TVAUDIO_FM_BG_STEREO: ++ saa_writeb(SAA7134_DEMODULATOR, 0x00); ++ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); ++ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); ++ saa_writeb(SAA7134_FM_DEMATRIX, 0x80); ++ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); ++ break; ++ case TVAUDIO_FM_K_STEREO: ++ saa_writeb(SAA7134_DEMODULATOR, 0x00); ++ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x01); ++ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); ++ saa_writeb(SAA7134_FM_DEMATRIX, 0x80); ++ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); ++ break; ++ case TVAUDIO_NICAM_FM: ++ saa_writeb(SAA7134_DEMODULATOR, 0x10); ++ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); ++ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); ++ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); ++ saa_writeb(SAA7134_NICAM_CONFIG, 0x00); ++ break; ++ case TVAUDIO_NICAM_AM: ++ saa_writeb(SAA7134_DEMODULATOR, 0x12); ++ saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); ++ saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); ++ saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); ++ saa_writeb(SAA7134_NICAM_CONFIG, 0x00); ++ break; ++ case TVAUDIO_FM_SAT_STEREO: ++ /* not implemented (yet) */ ++ break; ++ } ++} ++ ++static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) ++{ ++ if (dev->thread.scan1 == dev->thread.scan2 && ++ !kthread_should_stop()) { ++ if (timeout < 0) { ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ } else { ++ schedule_timeout_interruptible ++ (msecs_to_jiffies(timeout)); ++ } ++ } ++ return dev->thread.scan1 != dev->thread.scan2; ++} ++ ++static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) ++{ ++ __s32 left,right,value; ++ ++ if (!(dev->tvnorm->id & scan->std)) { ++ value = 0; ++ dprintk("skipping %d.%03d MHz [%4s]\n", ++ scan->carr / 1000, scan->carr % 1000, scan->name); ++ return 0; ++ } ++ ++ if (audio_debug > 1) { ++ int i; ++ dprintk("debug %d:",scan->carr); ++ for (i = -150; i <= 150; i += 30) { ++ tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i); ++ saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) ++ return -1; ++ value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ if (0 == i) ++ printk(" # %6d # ",value >> 16); ++ else ++ printk(" %6d",value >> 16); ++ } ++ printk("\n"); ++ } ++ ++ tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90); ++ saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) ++ return -1; ++ left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ ++ tvaudio_setcarrier(dev,scan->carr+90,scan->carr+90); ++ saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) ++ return -1; ++ right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); ++ ++ left >>= 16; ++ right >>= 16; ++ value = left > right ? left - right : right - left; ++ dprintk("scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n", ++ scan->carr / 1000, scan->carr % 1000, ++ scan->name, value, left, right); ++ return value; ++} ++ ++ ++static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio) ++{ ++ __u32 idp, nicam, nicam_status; ++ int retval = -1; ++ ++ switch (audio->mode) { ++ case TVAUDIO_FM_MONO: ++ return V4L2_TUNER_SUB_MONO; ++ case TVAUDIO_FM_K_STEREO: ++ case TVAUDIO_FM_BG_STEREO: ++ idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5; ++ dprintk("getstereo: fm/stereo: idp=0x%x\n",idp); ++ if (0x03 == (idp & 0x03)) ++ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ else if (0x05 == (idp & 0x05)) ++ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; ++ else if (0x01 == (idp & 0x01)) ++ retval = V4L2_TUNER_SUB_MONO; ++ break; ++ case TVAUDIO_FM_SAT_STEREO: ++ /* not implemented (yet) */ ++ break; ++ case TVAUDIO_NICAM_FM: ++ case TVAUDIO_NICAM_AM: ++ nicam = saa_readb(SAA7134_AUDIO_STATUS); ++ dprintk("getstereo: nicam=0x%x\n",nicam); ++ if (nicam & 0x1) { ++ nicam_status = saa_readb(SAA7134_NICAM_STATUS); ++ dprintk("getstereo: nicam_status=0x%x\n", nicam_status); ++ ++ switch (nicam_status & 0x03) { ++ case 0x01: ++ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ break; ++ case 0x02: ++ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; ++ break; ++ default: ++ retval = V4L2_TUNER_SUB_MONO; ++ } ++ } else { ++ /* No nicam detected */ ++ } ++ break; ++ } ++ if (retval != -1) ++ dprintk("found audio subchannels:%s%s%s%s\n", ++ (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "", ++ (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", ++ (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "", ++ (retval & V4L2_TUNER_SUB_LANG2) ? " lang2" : ""); ++ return retval; ++} ++ ++static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio, ++ u32 mode) ++{ ++ static char *name[] = { ++ [ V4L2_TUNER_MODE_MONO ] = "mono", ++ [ V4L2_TUNER_MODE_STEREO ] = "stereo", ++ [ V4L2_TUNER_MODE_LANG1 ] = "lang1", ++ [ V4L2_TUNER_MODE_LANG2 ] = "lang2", ++ [ V4L2_TUNER_MODE_LANG1_LANG2 ] = "lang1+lang2", ++ }; ++ static u32 fm[] = { ++ [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */ ++ [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */ ++ [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */ ++ [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */ ++ [ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80, /* auto */ ++ }; ++ u32 reg; ++ ++ switch (audio->mode) { ++ case TVAUDIO_FM_MONO: ++ /* nothing to do ... */ ++ break; ++ case TVAUDIO_FM_K_STEREO: ++ case TVAUDIO_FM_BG_STEREO: ++ case TVAUDIO_NICAM_AM: ++ case TVAUDIO_NICAM_FM: ++ dprintk("setstereo [fm] => %s\n", ++ name[ mode % ARRAY_SIZE(name) ]); ++ reg = fm[ mode % ARRAY_SIZE(fm) ]; ++ saa_writeb(SAA7134_FM_DEMATRIX, reg); ++ break; ++ case TVAUDIO_FM_SAT_STEREO: ++ /* Not implemented */ ++ break; ++ } ++ return 0; ++} ++ ++static int tvaudio_thread(void *data) ++{ ++ struct saa7134_dev *dev = data; ++ int carr_vals[ARRAY_SIZE(mainscan)]; ++ unsigned int i, audio, nscan; ++ int max1,max2,carrier,rx,mode,lastmode,default_carrier; ++ ++ set_freezable(); ++ ++ for (;;) { ++ tvaudio_sleep(dev,-1); ++ if (kthread_should_stop()) ++ goto done; ++ ++ restart: ++ try_to_freeze(); ++ ++ dev->thread.scan1 = dev->thread.scan2; ++ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); ++ dev->tvaudio = NULL; ++ ++ saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); ++ saa_writeb(SAA7134_FM_DEMATRIX, 0x80); ++ ++ if (dev->ctl_automute) ++ dev->automute = 1; ++ ++ mute_input_7134(dev); ++ ++ /* give the tuner some time */ ++ if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY)) ++ goto restart; ++ ++ max1 = 0; ++ max2 = 0; ++ nscan = 0; ++ carrier = 0; ++ default_carrier = 0; ++ for (i = 0; i < ARRAY_SIZE(mainscan); i++) { ++ if (!(dev->tvnorm->id & mainscan[i].std)) ++ continue; ++ if (!default_carrier) ++ default_carrier = mainscan[i].carr; ++ nscan++; ++ } ++ ++ if (1 == nscan) { ++ /* only one candidate -- skip scan ;) */ ++ dprintk("only one main carrier candidate - skipping scan\n"); ++ max1 = 12345; ++ carrier = default_carrier; ++ } else { ++ /* scan for the main carrier */ ++ saa_writeb(SAA7134_MONITOR_SELECT,0x00); ++ tvaudio_setmode(dev,&tvaudio[0],NULL); ++ for (i = 0; i < ARRAY_SIZE(mainscan); i++) { ++ carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); ++ if (dev->thread.scan1 != dev->thread.scan2) ++ goto restart; ++ } ++ for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { ++ if (max1 < carr_vals[i]) { ++ max2 = max1; ++ max1 = carr_vals[i]; ++ carrier = mainscan[i].carr; ++ } else if (max2 < carr_vals[i]) { ++ max2 = carr_vals[i]; ++ } ++ } ++ } ++ ++ if (0 != carrier && max1 > 2000 && max1 > max2*3) { ++ /* found good carrier */ ++ dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n", ++ dev->tvnorm->name, carrier/1000, carrier%1000, ++ max1, max2); ++ dev->last_carrier = carrier; ++ dev->automute = 0; ++ ++ } else if (0 != dev->last_carrier) { ++ /* no carrier -- try last detected one as fallback */ ++ carrier = dev->last_carrier; ++ dprintk("audio carrier scan failed, " ++ "using %d.%03d MHz [last detected]\n", ++ carrier/1000, carrier%1000); ++ dev->automute = 1; ++ ++ } else { ++ /* no carrier + no fallback -- use default */ ++ carrier = default_carrier; ++ dprintk("audio carrier scan failed, " ++ "using %d.%03d MHz [default]\n", ++ carrier/1000, carrier%1000); ++ dev->automute = 1; ++ } ++ tvaudio_setcarrier(dev,carrier,carrier); ++ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00); ++ saa7134_tvaudio_setmute(dev); ++ /* find the exact tv audio norm */ ++ for (audio = UNSET, i = 0; i < TVAUDIO; i++) { ++ if (dev->tvnorm->id != UNSET && ++ !(dev->tvnorm->id & tvaudio[i].std)) ++ continue; ++ if (tvaudio[i].carr1 != carrier) ++ continue; ++ /* Note: at least the primary carrier is right here */ ++ if (UNSET == audio) ++ audio = i; ++ tvaudio_setmode(dev,&tvaudio[i],"trying"); ++ if (tvaudio_sleep(dev,SCAN_SUBCARRIER_DELAY)) ++ goto restart; ++ if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) { ++ audio = i; ++ break; ++ } ++ } ++ saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30); ++ if (UNSET == audio) ++ continue; ++ tvaudio_setmode(dev,&tvaudio[audio],"using"); ++ ++ tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO); ++ dev->tvaudio = &tvaudio[audio]; ++ ++ lastmode = 42; ++ for (;;) { ++ ++ try_to_freeze(); ++ ++ if (tvaudio_sleep(dev,5000)) ++ goto restart; ++ if (kthread_should_stop()) ++ break; ++ if (UNSET == dev->thread.mode) { ++ rx = tvaudio_getstereo(dev, &tvaudio[audio]); ++ mode = saa7134_tvaudio_rx2mode(rx); ++ } else { ++ mode = dev->thread.mode; ++ } ++ if (lastmode != mode) { ++ tvaudio_setstereo(dev,&tvaudio[audio],mode); ++ lastmode = mode; ++ } ++ } ++ } ++ ++ done: ++ dev->thread.stopped = 1; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* saa7133 / saa7135 code */ ++ ++static char *stdres[0x20] = { ++ [0x00] = "no standard detected", ++ [0x01] = "B/G (in progress)", ++ [0x02] = "D/K (in progress)", ++ [0x03] = "M (in progress)", ++ ++ [0x04] = "B/G A2", ++ [0x05] = "B/G NICAM", ++ [0x06] = "D/K A2 (1)", ++ [0x07] = "D/K A2 (2)", ++ [0x08] = "D/K A2 (3)", ++ [0x09] = "D/K NICAM", ++ [0x0a] = "L NICAM", ++ [0x0b] = "I NICAM", ++ ++ [0x0c] = "M Korea", ++ [0x0d] = "M BTSC ", ++ [0x0e] = "M EIAJ", ++ ++ [0x0f] = "FM radio / IF 10.7 / 50 deemp", ++ [0x10] = "FM radio / IF 10.7 / 75 deemp", ++ [0x11] = "FM radio / IF sel / 50 deemp", ++ [0x12] = "FM radio / IF sel / 75 deemp", ++ ++ [0x13 ... 0x1e ] = "unknown", ++ [0x1f] = "??? [in progress]", ++}; ++ ++#define DSP_RETRY 32 ++#define DSP_DELAY 16 ++#define SAA7135_DSP_RWCLEAR_RERR 1 ++ ++static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev) ++{ ++ int state = saa_readb(SAA7135_DSP_RWSTATE); ++ if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { ++ d2printk("%s: resetting error bit\n", dev->name); ++ saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR); ++ } ++ return 0; ++} ++ ++static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) ++{ ++ int state, count = DSP_RETRY; ++ ++ state = saa_readb(SAA7135_DSP_RWSTATE); ++ if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { ++ printk(KERN_WARNING "%s: dsp access error\n", dev->name); ++ saa_dsp_reset_error_bit(dev); ++ return -EIO; ++ } ++ while (0 == (state & bit)) { ++ if (unlikely(0 == count)) { ++ printk("%s: dsp access wait timeout [bit=%s]\n", ++ dev->name, ++ (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" : ++ (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" : ++ (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" : ++ "???"); ++ return -EIO; ++ } ++ saa_wait(DSP_DELAY); ++ state = saa_readb(SAA7135_DSP_RWSTATE); ++ count--; ++ } ++ return 0; ++} ++ ++ ++int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value) ++{ ++ int err; ++ ++ d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value); ++ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); ++ if (err < 0) ++ return err; ++ saa_writel(reg,value); ++ err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); ++ if (err < 0) ++ return err; ++ return 0; ++} ++ ++static int getstereo_7133(struct saa7134_dev *dev) ++{ ++ int retval = V4L2_TUNER_SUB_MONO; ++ u32 value; ++ ++ value = saa_readl(0x528 >> 2); ++ if (value & 0x20) ++ retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; ++ if (value & 0x40) ++ retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ return retval; ++} ++ ++static int mute_input_7133(struct saa7134_dev *dev) ++{ ++ u32 reg = 0; ++ u32 xbarin, xbarout; ++ int mask; ++ struct saa7134_input *in; ++ ++ xbarin = 0x03; ++ switch (dev->input->amux) { ++ case TV: ++ reg = 0x02; ++ xbarin = 0; ++ break; ++ case LINE1: ++ reg = 0x00; ++ break; ++ case LINE2: ++ case LINE2_LEFT: ++ reg = 0x09; ++ break; ++ } ++ saa_dsp_writel(dev, 0x464 >> 2, xbarin); ++ if (dev->ctl_mute) { ++ reg = 0x07; ++ xbarout = 0xbbbbbb; ++ } else ++ xbarout = 0xbbbb10; ++ saa_dsp_writel(dev, 0x46c >> 2, xbarout); ++ ++ saa_writel(0x594 >> 2, reg); ++ ++ ++ /* switch gpio-connected external audio mux */ ++ if (0 != card(dev).gpiomask) { ++ mask = card(dev).gpiomask; ++ ++ if (card(dev).mute.name && dev->ctl_mute) ++ in = &card(dev).mute; ++ else ++ in = dev->input; ++ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); ++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio); ++ saa7134_track_gpio(dev,in->name); ++ } ++ ++ return 0; ++} ++ ++static int tvaudio_thread_ddep(void *data) ++{ ++ struct saa7134_dev *dev = data; ++ u32 value, norms; ++ ++ set_freezable(); ++ for (;;) { ++ tvaudio_sleep(dev,-1); ++ if (kthread_should_stop()) ++ goto done; ++ restart: ++ try_to_freeze(); ++ ++ dev->thread.scan1 = dev->thread.scan2; ++ dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); ++ ++ if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { ++ /* insmod option override */ ++ norms = (audio_ddep << 2) | 0x01; ++ dprintk("ddep override: %s\n",stdres[audio_ddep]); ++ } else if (&card(dev).radio == dev->input) { ++ dprintk("FM Radio\n"); ++ if (dev->tuner_type == TUNER_PHILIPS_TDA8290) { ++ norms = (0x11 << 2) | 0x01; ++ saa_dsp_writel(dev, 0x42c >> 2, 0x729555); ++ } else { ++ norms = (0x0f << 2) | 0x01; ++ } ++ } else { ++ /* (let chip) scan for sound carrier */ ++ norms = 0; ++ if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) ++ norms |= 0x04; ++ if (dev->tvnorm->id & V4L2_STD_PAL_I) ++ norms |= 0x20; ++ if (dev->tvnorm->id & V4L2_STD_DK) ++ norms |= 0x08; ++ if (dev->tvnorm->id & V4L2_STD_MN) ++ norms |= 0x40; ++ if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) ++ norms |= 0x10; ++ if (0 == norms) ++ norms = 0x7c; /* all */ ++ dprintk("scanning:%s%s%s%s%s\n", ++ (norms & 0x04) ? " B/G" : "", ++ (norms & 0x08) ? " D/K" : "", ++ (norms & 0x10) ? " L/L'" : "", ++ (norms & 0x20) ? " I" : "", ++ (norms & 0x40) ? " M" : ""); ++ } ++ ++ /* kick automatic standard detection */ ++ saa_dsp_writel(dev, 0x454 >> 2, 0); ++ saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80); ++ ++ /* setup crossbars */ ++ saa_dsp_writel(dev, 0x464 >> 2, 0x000000); ++ saa_dsp_writel(dev, 0x470 >> 2, 0x101010); ++ ++ if (tvaudio_sleep(dev,3000)) ++ goto restart; ++ value = saa_readl(0x528 >> 2) & 0xffffff; ++ ++ dprintk("tvaudio thread status: 0x%x [%s%s%s]\n", ++ value, stdres[value & 0x1f], ++ (value & 0x000020) ? ",stereo" : "", ++ (value & 0x000040) ? ",dual" : ""); ++ dprintk("detailed status: " ++ "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", ++ (value & 0x000080) ? " A2/EIAJ pilot tone " : "", ++ (value & 0x000100) ? " A2/EIAJ dual " : "", ++ (value & 0x000200) ? " A2/EIAJ stereo " : "", ++ (value & 0x000400) ? " A2/EIAJ noise mute " : "", ++ ++ (value & 0x000800) ? " BTSC/FM radio pilot " : "", ++ (value & 0x001000) ? " SAP carrier " : "", ++ (value & 0x002000) ? " BTSC stereo noise mute " : "", ++ (value & 0x004000) ? " SAP noise mute " : "", ++ (value & 0x008000) ? " VDSP " : "", ++ ++ (value & 0x010000) ? " NICST " : "", ++ (value & 0x020000) ? " NICDU " : "", ++ (value & 0x040000) ? " NICAM muted " : "", ++ (value & 0x080000) ? " NICAM reserve sound " : "", ++ ++ (value & 0x100000) ? " init done " : ""); ++ } ++ ++ done: ++ dev->thread.stopped = 1; ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++/* common stuff + external entry points */ ++ ++void saa7134_enable_i2s(struct saa7134_dev *dev) ++{ ++ int i2s_format; ++ ++ if (!card_is_empress(dev)) ++ return; ++ ++ if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) ++ return; ++ ++ /* configure GPIO for out */ ++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000); ++ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ /* Set I2S format (SONY)  */ ++ saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00); ++ /* Start I2S */ ++ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11); ++ break; ++ ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; ++ ++ /* enable I2S audio output for the mpeg encoder */ ++ saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); ++ saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); ++ saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); ++ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); ++ ++ default: ++ break; ++ } ++} ++ ++int saa7134_tvaudio_rx2mode(u32 rx) ++{ ++ u32 mode; ++ ++ mode = V4L2_TUNER_MODE_MONO; ++ if (rx & V4L2_TUNER_SUB_STEREO) ++ mode = V4L2_TUNER_MODE_STEREO; ++ else if (rx & V4L2_TUNER_SUB_LANG1) ++ mode = V4L2_TUNER_MODE_LANG1; ++ else if (rx & V4L2_TUNER_SUB_LANG2) ++ mode = V4L2_TUNER_MODE_LANG2; ++ return mode; ++} ++ ++void saa7134_tvaudio_setmute(struct saa7134_dev *dev) ++{ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7130: ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ mute_input_7134(dev); ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ mute_input_7133(dev); ++ break; ++ } ++} ++ ++void saa7134_tvaudio_setinput(struct saa7134_dev *dev, ++ struct saa7134_input *in) ++{ ++ dev->input = in; ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7130: ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ mute_input_7134(dev); ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ mute_input_7133(dev); ++ break; ++ } ++ saa7134_enable_i2s(dev); ++} ++ ++void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level) ++{ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f); ++ saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f); ++ saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f); ++ break; ++ } ++} ++ ++int saa7134_tvaudio_getstereo(struct saa7134_dev *dev) ++{ ++ int retval = V4L2_TUNER_SUB_MONO; ++ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ if (dev->tvaudio) ++ retval = tvaudio_getstereo(dev,dev->tvaudio); ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ retval = getstereo_7133(dev); ++ break; ++ } ++ return retval; ++} ++ ++void saa7134_tvaudio_init(struct saa7134_dev *dev) ++{ ++ int clock = saa7134_boards[dev->board].audio_clock; ++ ++ if (UNSET != audio_clock_override) ++ clock = audio_clock_override; ++ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ /* init all audio registers */ ++ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00); ++ if (need_resched()) ++ schedule(); ++ else ++ udelay(10); ++ ++ saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); ++ saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); ++ saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); ++ /* frame locked audio is mandatory for NICAM */ ++ saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01); ++ saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14); ++ saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50); ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ saa_writel(0x598 >> 2, clock); ++ saa_dsp_writel(dev, 0x474 >> 2, 0x00); ++ saa_dsp_writel(dev, 0x450 >> 2, 0x00); ++ } ++} ++ ++int saa7134_tvaudio_init2(struct saa7134_dev *dev) ++{ ++ int (*my_thread)(void *data) = NULL; ++ ++ switch (dev->pci->device) { ++ case PCI_DEVICE_ID_PHILIPS_SAA7134: ++ my_thread = tvaudio_thread; ++ break; ++ case PCI_DEVICE_ID_PHILIPS_SAA7133: ++ case PCI_DEVICE_ID_PHILIPS_SAA7135: ++ my_thread = tvaudio_thread_ddep; ++ break; ++ } ++ ++ dev->thread.thread = NULL; ++ dev->thread.scan1 = dev->thread.scan2 = 0; ++ if (my_thread) { ++ saa7134_tvaudio_init(dev); ++ /* start tvaudio thread */ ++ dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); ++ if (IS_ERR(dev->thread.thread)) { ++ printk(KERN_WARNING "%s: kernel_thread() failed\n", ++ dev->name); ++ /* XXX: missing error handling here */ ++ } ++ } ++ ++ saa7134_enable_i2s(dev); ++ return 0; ++} ++ ++int saa7134_tvaudio_close(struct saa7134_dev *dev) ++{ ++ dev->automute = 1; ++ /* anything else to undo? */ ++ return 0; ++} ++ ++int saa7134_tvaudio_fini(struct saa7134_dev *dev) ++{ ++ /* shutdown tvaudio thread */ ++ if (dev->thread.thread && !dev->thread.stopped) ++ kthread_stop(dev->thread.thread); ++ ++ saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ ++ return 0; ++} ++ ++int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) ++{ ++ if (dev->input->amux != TV) { ++ dprintk("sound IF not in use, skipping scan\n"); ++ dev->automute = 0; ++ saa7134_tvaudio_setmute(dev); ++ } else if (dev->thread.thread) { ++ dev->thread.mode = UNSET; ++ dev->thread.scan2++; ++ ++ if (!dev->insuspend && !dev->thread.stopped) ++ wake_up_process(dev->thread.thread); ++ } else { ++ dev->automute = 0; ++ saa7134_tvaudio_setmute(dev); ++ } ++ return 0; ++} ++ ++EXPORT_SYMBOL(saa_dsp_writel); ++EXPORT_SYMBOL(saa7134_tvaudio_setmute); ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c +new file mode 100644 +index 0000000..e9aa94b +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-vbi.c +@@ -0,0 +1,255 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * video4linux video interface ++ * ++ * (c) 2001,02 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++ ++/* ------------------------------------------------------------------ */ ++ ++static unsigned int vbi_debug; ++module_param(vbi_debug, int, 0644); ++MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); ++ ++static unsigned int vbibufs = 4; ++module_param(vbibufs, int, 0444); ++MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); ++ ++#define dprintk(fmt, arg...) if (vbi_debug) \ ++ printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg) ++ ++/* ------------------------------------------------------------------ */ ++ ++#define VBI_LINE_COUNT 16 ++#define VBI_LINE_LENGTH 2048 ++#define VBI_SCALE 0x200 ++ ++static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf, ++ int task) ++{ ++ struct saa7134_tvnorm *norm = dev->tvnorm; ++ ++ /* setup video scaler */ ++ saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff); ++ saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8); ++ saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff); ++ saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8); ++ saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start_0 & 0xff); ++ saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start_0 >> 8); ++ saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop_0 & 0xff); ++ saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop_0 >> 8); ++ ++ saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff); ++ saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8); ++ saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00); ++ saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00); ++ ++ saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff); ++ saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8); ++ saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff); ++ saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8); ++ ++ saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int buffer_activate(struct saa7134_dev *dev, ++ struct saa7134_buf *buf, ++ struct saa7134_buf *next) ++{ ++ unsigned long control,base; ++ ++ dprintk("buffer_activate [%p]\n",buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->top_seen = 0; ++ ++ task_init(dev,buf,TASK_A); ++ task_init(dev,buf,TASK_B); ++ saa_writeb(SAA7134_OFMT_DATA_A, 0x06); ++ saa_writeb(SAA7134_OFMT_DATA_B, 0x06); ++ ++ /* DMA: setup channel 2+3 (= VBI Task A+B) */ ++ base = saa7134_buffer_base(buf); ++ control = SAA7134_RS_CONTROL_BURST_16 | ++ SAA7134_RS_CONTROL_ME | ++ (buf->pt->dma >> 12); ++ saa_writel(SAA7134_RS_BA1(2),base); ++ saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2); ++ saa_writel(SAA7134_RS_PITCH(2),buf->vb.width); ++ saa_writel(SAA7134_RS_CONTROL(2),control); ++ saa_writel(SAA7134_RS_BA1(3),base); ++ saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2); ++ saa_writel(SAA7134_RS_PITCH(3),buf->vb.width); ++ saa_writel(SAA7134_RS_CONTROL(3),control); ++ ++ /* start DMA */ ++ saa7134_set_dmabits(dev); ++ mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ struct saa7134_tvnorm *norm = dev->tvnorm; ++ unsigned int lines, llength, size; ++ int err; ++ ++ lines = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; ++ if (lines > VBI_LINE_COUNT) ++ lines = VBI_LINE_COUNT; ++ llength = VBI_LINE_LENGTH; ++ size = lines * llength * 2; ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ if (buf->vb.size != size) ++ saa7134_dma_free(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->pt = &fh->pt_vbi; ++ ++ err = videobuf_iolock(q,&buf->vb,NULL); ++ if (err) ++ goto oops; ++ err = saa7134_pgtable_build(dev->pci,buf->pt, ++ dma->sglist, ++ dma->sglen, ++ saa7134_buffer_startpage(buf)); ++ if (err) ++ goto oops; ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ buf->activate = buffer_activate; ++ buf->vb.field = field; ++ return 0; ++ ++ oops: ++ saa7134_dma_free(q,buf); ++ return err; ++} ++ ++static int ++buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ struct saa7134_dev *dev = fh->dev; ++ int llength,lines; ++ ++ lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1; ++ llength = VBI_LINE_LENGTH; ++ *size = lines * llength * 2; ++ if (0 == *count) ++ *count = vbibufs; ++ *count = saa7134_buffer_count(*size,*count); ++ return 0; ++} ++ ++static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ ++ saa7134_buffer_queue(dev,&dev->vbi_q,buf); ++} ++ ++static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ ++ saa7134_dma_free(q,buf); ++} ++ ++struct videobuf_queue_ops saa7134_vbi_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++int saa7134_vbi_init1(struct saa7134_dev *dev) ++{ ++ INIT_LIST_HEAD(&dev->vbi_q.queue); ++ init_timer(&dev->vbi_q.timeout); ++ dev->vbi_q.timeout.function = saa7134_buffer_timeout; ++ dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q); ++ dev->vbi_q.dev = dev; ++ ++ if (vbibufs < 2) ++ vbibufs = 2; ++ if (vbibufs > VIDEO_MAX_FRAME) ++ vbibufs = VIDEO_MAX_FRAME; ++ return 0; ++} ++ ++int saa7134_vbi_fini(struct saa7134_dev *dev) ++{ ++ /* nothing */ ++ return 0; ++} ++ ++void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status) ++{ ++ spin_lock(&dev->slock); ++ if (dev->vbi_q.curr) { ++ dev->vbi_fieldcount++; ++ /* make sure we have seen both fields */ ++ if ((status & 0x10) == 0x00) { ++ dev->vbi_q.curr->top_seen = 1; ++ goto done; ++ } ++ if (!dev->vbi_q.curr->top_seen) ++ goto done; ++ ++ dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount; ++ saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE); ++ } ++ saa7134_buffer_next(dev,&dev->vbi_q); ++ ++ done: ++ spin_unlock(&dev->slock); ++} ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c +new file mode 100644 +index 0000000..3abf527 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134-video.c +@@ -0,0 +1,2661 @@ ++/* ++ * ++ * device driver for philips saa7134 based TV cards ++ * video4linux video interface ++ * ++ * (c) 2001-03 Gerd Knorr [SuSE Labs] ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7134-reg.h" ++#include "saa7134.h" ++#include ++#include ++ ++/* ------------------------------------------------------------------ */ ++ ++unsigned int video_debug; ++static unsigned int gbuffers = 8; ++static unsigned int noninterlaced; /* 0 */ ++static unsigned int gbufsize = 720*576*4; ++static unsigned int gbufsize_max = 720*576*4; ++static char secam[] = "--"; ++module_param(video_debug, int, 0644); ++MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); ++module_param(gbuffers, int, 0444); ++MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32"); ++module_param(noninterlaced, int, 0644); ++MODULE_PARM_DESC(noninterlaced,"capture non interlaced video"); ++module_param_string(secam, secam, sizeof(secam), 0644); ++MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc"); ++ ++ ++#define dprintk(fmt, arg...) if (video_debug&0x04) \ ++ printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg) ++ ++/* ------------------------------------------------------------------ */ ++/* Defines for Video Output Port Register at address 0x191 */ ++ ++/* Bit 0: VIP code T bit polarity */ ++ ++#define VP_T_CODE_P_NON_INVERTED 0x00 ++#define VP_T_CODE_P_INVERTED 0x01 ++ ++/* ------------------------------------------------------------------ */ ++/* Defines for Video Output Port Register at address 0x195 */ ++ ++/* Bit 2: Video output clock delay control */ ++ ++#define VP_CLK_CTRL2_NOT_DELAYED 0x00 ++#define VP_CLK_CTRL2_DELAYED 0x04 ++ ++/* Bit 1: Video output clock invert control */ ++ ++#define VP_CLK_CTRL1_NON_INVERTED 0x00 ++#define VP_CLK_CTRL1_INVERTED 0x02 ++ ++/* ------------------------------------------------------------------ */ ++/* Defines for Video Output Port Register at address 0x196 */ ++ ++/* Bits 2 to 0: VSYNC pin video vertical sync type */ ++ ++#define VP_VS_TYPE_MASK 0x07 ++ ++#define VP_VS_TYPE_OFF 0x00 ++#define VP_VS_TYPE_V123 0x01 ++#define VP_VS_TYPE_V_ITU 0x02 ++#define VP_VS_TYPE_VGATE_L 0x03 ++#define VP_VS_TYPE_RESERVED1 0x04 ++#define VP_VS_TYPE_RESERVED2 0x05 ++#define VP_VS_TYPE_F_ITU 0x06 ++#define VP_VS_TYPE_SC_FID 0x07 ++ ++/* ------------------------------------------------------------------ */ ++/* data structs for video */ ++ ++static int video_out[][9] = { ++ [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 }, ++}; ++ ++static struct saa7134_format formats[] = { ++ { ++ .name = "8 bpp gray", ++ .fourcc = V4L2_PIX_FMT_GREY, ++ .depth = 8, ++ .pm = 0x06, ++ },{ ++ .name = "15 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB555, ++ .depth = 16, ++ .pm = 0x13 | 0x80, ++ },{ ++ .name = "15 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB555X, ++ .depth = 16, ++ .pm = 0x13 | 0x80, ++ .bswap = 1, ++ },{ ++ .name = "16 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .depth = 16, ++ .pm = 0x10 | 0x80, ++ },{ ++ .name = "16 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB565X, ++ .depth = 16, ++ .pm = 0x10 | 0x80, ++ .bswap = 1, ++ },{ ++ .name = "24 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .depth = 24, ++ .pm = 0x11, ++ },{ ++ .name = "24 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB24, ++ .depth = 24, ++ .pm = 0x11, ++ .bswap = 1, ++ },{ ++ .name = "32 bpp RGB, le", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .depth = 32, ++ .pm = 0x12, ++ },{ ++ .name = "32 bpp RGB, be", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .depth = 32, ++ .pm = 0x12, ++ .bswap = 1, ++ .wswap = 1, ++ },{ ++ .name = "4:2:2 packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ .pm = 0x00, ++ .bswap = 1, ++ .yuv = 1, ++ },{ ++ .name = "4:2:2 packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .depth = 16, ++ .pm = 0x00, ++ .yuv = 1, ++ },{ ++ .name = "4:2:2 planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV422P, ++ .depth = 16, ++ .pm = 0x09, ++ .yuv = 1, ++ .planar = 1, ++ .hshift = 1, ++ .vshift = 0, ++ },{ ++ .name = "4:2:0 planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .depth = 12, ++ .pm = 0x0a, ++ .yuv = 1, ++ .planar = 1, ++ .hshift = 1, ++ .vshift = 1, ++ },{ ++ .name = "4:2:0 planar, Y-Cb-Cr", ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .depth = 12, ++ .pm = 0x0a, ++ .yuv = 1, ++ .planar = 1, ++ .uvswap = 1, ++ .hshift = 1, ++ .vshift = 1, ++ } ++}; ++#define FORMATS ARRAY_SIZE(formats) ++ ++#define NORM_625_50 \ ++ .h_start = 0, \ ++ .h_stop = 719, \ ++ .video_v_start = 24, \ ++ .video_v_stop = 311, \ ++ .vbi_v_start_0 = 7, \ ++ .vbi_v_stop_0 = 22, \ ++ .vbi_v_start_1 = 319, \ ++ .src_timing = 4 ++ ++#define NORM_525_60 \ ++ .h_start = 0, \ ++ .h_stop = 719, \ ++ .video_v_start = 23, \ ++ .video_v_stop = 262, \ ++ .vbi_v_start_0 = 10, \ ++ .vbi_v_stop_0 = 21, \ ++ .vbi_v_start_1 = 273, \ ++ .src_timing = 7 ++ ++static struct saa7134_tvnorm tvnorms[] = { ++ { ++ .name = "PAL", /* autodetect */ ++ .id = V4L2_STD_PAL, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x81, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "PAL-BG", ++ .id = V4L2_STD_PAL_BG, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x81, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "PAL-I", ++ .id = V4L2_STD_PAL_I, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x81, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "PAL-DK", ++ .id = V4L2_STD_PAL_DK, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x81, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "NTSC", ++ .id = V4L2_STD_NTSC, ++ NORM_525_60, ++ ++ .sync_control = 0x59, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x89, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x0e, ++ .vgate_misc = 0x18, ++ ++ },{ ++ .name = "SECAM", ++ .id = V4L2_STD_SECAM, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x1b, ++ .chroma_ctrl1 = 0xd1, ++ .chroma_gain = 0x80, ++ .chroma_ctrl2 = 0x00, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "SECAM-DK", ++ .id = V4L2_STD_SECAM_DK, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x1b, ++ .chroma_ctrl1 = 0xd1, ++ .chroma_gain = 0x80, ++ .chroma_ctrl2 = 0x00, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "SECAM-L", ++ .id = V4L2_STD_SECAM_L, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x1b, ++ .chroma_ctrl1 = 0xd1, ++ .chroma_gain = 0x80, ++ .chroma_ctrl2 = 0x00, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "SECAM-Lc", ++ .id = V4L2_STD_SECAM_LC, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x1b, ++ .chroma_ctrl1 = 0xd1, ++ .chroma_gain = 0x80, ++ .chroma_ctrl2 = 0x00, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "PAL-M", ++ .id = V4L2_STD_PAL_M, ++ NORM_525_60, ++ ++ .sync_control = 0x59, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0xb9, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x0e, ++ .vgate_misc = 0x18, ++ ++ },{ ++ .name = "PAL-Nc", ++ .id = V4L2_STD_PAL_Nc, ++ NORM_625_50, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0xa1, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ ++ },{ ++ .name = "PAL-60", ++ .id = V4L2_STD_PAL_60, ++ ++ .h_start = 0, ++ .h_stop = 719, ++ .video_v_start = 23, ++ .video_v_stop = 262, ++ .vbi_v_start_0 = 10, ++ .vbi_v_stop_0 = 21, ++ .vbi_v_start_1 = 273, ++ .src_timing = 7, ++ ++ .sync_control = 0x18, ++ .luma_control = 0x40, ++ .chroma_ctrl1 = 0x81, ++ .chroma_gain = 0x2a, ++ .chroma_ctrl2 = 0x06, ++ .vgate_misc = 0x1c, ++ } ++}; ++#define TVNORMS ARRAY_SIZE(tvnorms) ++ ++#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) ++#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) ++#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2) ++#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3) ++#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4) ++ ++static const struct v4l2_queryctrl no_ctrl = { ++ .name = "42", ++ .flags = V4L2_CTRL_FLAG_DISABLED, ++}; ++static const struct v4l2_queryctrl video_ctrls[] = { ++ /* --- video --- */ ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .name = "Brightness", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ .default_value = 128, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_CONTRAST, ++ .name = "Contrast", ++ .minimum = 0, ++ .maximum = 127, ++ .step = 1, ++ .default_value = 68, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_SATURATION, ++ .name = "Saturation", ++ .minimum = 0, ++ .maximum = 127, ++ .step = 1, ++ .default_value = 64, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_HUE, ++ .name = "Hue", ++ .minimum = -128, ++ .maximum = 127, ++ .step = 1, ++ .default_value = 0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_HFLIP, ++ .name = "Mirror", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ }, ++ /* --- audio --- */ ++ { ++ .id = V4L2_CID_AUDIO_MUTE, ++ .name = "Mute", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_AUDIO_VOLUME, ++ .name = "Volume", ++ .minimum = -15, ++ .maximum = 15, ++ .step = 1, ++ .default_value = 0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ }, ++ /* --- private --- */ ++ { ++ .id = V4L2_CID_PRIVATE_INVERT, ++ .name = "Invert", ++ .minimum = 0, ++ .maximum = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ },{ ++ .id = V4L2_CID_PRIVATE_Y_ODD, ++ .name = "y offset odd field", ++ .minimum = 0, ++ .maximum = 128, ++ .step = 1, ++ .default_value = 0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_PRIVATE_Y_EVEN, ++ .name = "y offset even field", ++ .minimum = 0, ++ .maximum = 128, ++ .step = 1, ++ .default_value = 0, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ },{ ++ .id = V4L2_CID_PRIVATE_AUTOMUTE, ++ .name = "automute", ++ .minimum = 0, ++ .maximum = 1, ++ .default_value = 1, ++ .type = V4L2_CTRL_TYPE_BOOLEAN, ++ } ++}; ++static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls); ++ ++static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < CTRLS; i++) ++ if (video_ctrls[i].id == id) ++ return video_ctrls+i; ++ return NULL; ++} ++ ++static struct saa7134_format* format_by_fourcc(unsigned int fourcc) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < FORMATS; i++) ++ if (formats[i].fourcc == fourcc) ++ return formats+i; ++ return NULL; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* resource management */ ++ ++static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit) ++{ ++ if (fh->resources & bit) ++ /* have it already allocated */ ++ return 1; ++ ++ /* is it free? */ ++ mutex_lock(&dev->lock); ++ if (dev->resources & bit) { ++ /* no, someone else uses it */ ++ mutex_unlock(&dev->lock); ++ return 0; ++ } ++ /* it's free, grab it */ ++ fh->resources |= bit; ++ dev->resources |= bit; ++ dprintk("res: get %d\n",bit); ++ mutex_unlock(&dev->lock); ++ return 1; ++} ++ ++static int res_check(struct saa7134_fh *fh, unsigned int bit) ++{ ++ return (fh->resources & bit); ++} ++ ++static int res_locked(struct saa7134_dev *dev, unsigned int bit) ++{ ++ return (dev->resources & bit); ++} ++ ++static ++void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) ++{ ++ BUG_ON((fh->resources & bits) != bits); ++ ++ mutex_lock(&dev->lock); ++ fh->resources &= ~bits; ++ dev->resources &= ~bits; ++ dprintk("res: put %d\n",bits); ++ mutex_unlock(&dev->lock); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) ++{ ++ dprintk("set tv norm = %s\n",norm->name); ++ dev->tvnorm = norm; ++ ++ /* setup cropping */ ++ dev->crop_bounds.left = norm->h_start; ++ dev->crop_defrect.left = norm->h_start; ++ dev->crop_bounds.width = norm->h_stop - norm->h_start +1; ++ dev->crop_defrect.width = norm->h_stop - norm->h_start +1; ++ ++ dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2; ++ dev->crop_defrect.top = norm->video_v_start*2; ++ dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624) ++ - dev->crop_bounds.top; ++ dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2; ++ ++ dev->crop_current = dev->crop_defrect; ++ ++ saa7134_set_tvnorm_hw(dev); ++} ++ ++static void video_mux(struct saa7134_dev *dev, int input) ++{ ++ dprintk("video input = %d [%s]\n", input, card_in(dev, input).name); ++ dev->ctl_input = input; ++ set_tvnorm(dev, dev->tvnorm); ++ saa7134_tvaudio_setinput(dev, &card_in(dev, input)); ++} ++ ++ ++static void saa7134_set_decoder(struct saa7134_dev *dev) ++{ ++ int luma_control, sync_control, mux; ++ ++ struct saa7134_tvnorm *norm = dev->tvnorm; ++ mux = card_in(dev, dev->ctl_input).vmux; ++ ++ luma_control = norm->luma_control; ++ sync_control = norm->sync_control; ++ ++ if (mux > 5) ++ luma_control |= 0x80; /* svideo */ ++ if (noninterlaced || dev->nosignal) ++ sync_control |= 0x20; ++ ++ /* setup video decoder */ ++ saa_writeb(SAA7134_INCR_DELAY, 0x08); ++ saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); ++ saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00); ++ ++ saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90); ++ saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90); ++ saa_writeb(SAA7134_HSYNC_START, 0xeb); ++ saa_writeb(SAA7134_HSYNC_STOP, 0xe0); ++ saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing); ++ ++ saa_writeb(SAA7134_SYNC_CTRL, sync_control); ++ saa_writeb(SAA7134_LUMA_CTRL, luma_control); ++ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); ++ ++ saa_writeb(SAA7134_DEC_LUMA_CONTRAST, ++ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); ++ ++ saa_writeb(SAA7134_DEC_CHROMA_SATURATION, ++ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); ++ ++ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); ++ saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); ++ saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); ++ ++ saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); ++ saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); ++ ++ saa_writeb(SAA7134_ANALOG_ADC, 0x01); ++ saa_writeb(SAA7134_VGATE_START, 0x11); ++ saa_writeb(SAA7134_VGATE_STOP, 0xfe); ++ saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); ++ saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); ++ saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); ++} ++ ++void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) ++{ ++ saa7134_set_decoder(dev); ++ ++ if (card_in(dev, dev->ctl_input).tv) ++ saa_call_all(dev, core, s_std, dev->tvnorm->id); ++ /* Set the correct norm for the saa6752hs. This function ++ does nothing if there is no saa6752hs. */ ++ saa_call_empress(dev, core, s_std, dev->tvnorm->id); ++} ++ ++static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) ++{ ++ static const struct { ++ int xpsc; ++ int xacl; ++ int xc2_1; ++ int xdcg; ++ int vpfy; ++ } vals[] = { ++ /* XPSC XACL XC2_1 XDCG VPFY */ ++ { 1, 0, 0, 0, 0 }, ++ { 2, 2, 1, 2, 2 }, ++ { 3, 4, 1, 3, 2 }, ++ { 4, 8, 1, 4, 2 }, ++ { 5, 8, 1, 4, 2 }, ++ { 6, 8, 1, 4, 3 }, ++ { 7, 8, 1, 4, 3 }, ++ { 8, 15, 0, 4, 3 }, ++ { 9, 15, 0, 4, 3 }, ++ { 10, 16, 1, 5, 3 }, ++ }; ++ static const int count = ARRAY_SIZE(vals); ++ int i; ++ ++ for (i = 0; i < count; i++) ++ if (vals[i].xpsc == prescale) ++ break; ++ if (i == count) ++ return; ++ ++ saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc); ++ saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl); ++ saa_writeb(SAA7134_LEVEL_CTRL(task), ++ (vals[i].xc2_1 << 3) | (vals[i].xdcg)); ++ saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f, ++ (vals[i].vpfy << 2) | vals[i].vpfy); ++} ++ ++static void set_v_scale(struct saa7134_dev *dev, int task, int yscale) ++{ ++ int val,mirror; ++ ++ saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff); ++ saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8); ++ ++ mirror = (dev->ctl_mirror) ? 0x02 : 0x00; ++ if (yscale < 2048) { ++ /* LPI */ ++ dprintk("yscale LPI yscale=%d\n",yscale); ++ saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror); ++ saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40); ++ saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40); ++ } else { ++ /* ACM */ ++ val = 0x40 * 1024 / yscale; ++ dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val); ++ saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror); ++ saa_writeb(SAA7134_LUMA_CONTRAST(task), val); ++ saa_writeb(SAA7134_CHROMA_SATURATION(task), val); ++ } ++ saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80); ++} ++ ++static void set_size(struct saa7134_dev *dev, int task, ++ int width, int height, int interlace) ++{ ++ int prescale,xscale,yscale,y_even,y_odd; ++ int h_start, h_stop, v_start, v_stop; ++ int div = interlace ? 2 : 1; ++ ++ /* setup video scaler */ ++ h_start = dev->crop_current.left; ++ v_start = dev->crop_current.top/2; ++ h_stop = (dev->crop_current.left + dev->crop_current.width -1); ++ v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2; ++ ++ saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff); ++ saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8); ++ saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff); ++ saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8); ++ saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff); ++ saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8); ++ saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff); ++ saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8); ++ ++ prescale = dev->crop_current.width / width; ++ if (0 == prescale) ++ prescale = 1; ++ xscale = 1024 * dev->crop_current.width / prescale / width; ++ yscale = 512 * div * dev->crop_current.height / height; ++ dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale); ++ set_h_prescale(dev,task,prescale); ++ saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff); ++ saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8); ++ set_v_scale(dev,task,yscale); ++ ++ saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff); ++ saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8); ++ saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff); ++ saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8); ++ ++ /* deinterlace y offsets */ ++ y_odd = dev->ctl_y_odd; ++ y_even = dev->ctl_y_even; ++ saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); ++ saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); ++ saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); ++ saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++struct cliplist { ++ __u16 position; ++ __u8 enable; ++ __u8 disable; ++}; ++ ++static void set_cliplist(struct saa7134_dev *dev, int reg, ++ struct cliplist *cl, int entries, char *name) ++{ ++ __u8 winbits = 0; ++ int i; ++ ++ for (i = 0; i < entries; i++) { ++ winbits |= cl[i].enable; ++ winbits &= ~cl[i].disable; ++ if (i < 15 && cl[i].position == cl[i+1].position) ++ continue; ++ saa_writeb(reg + 0, winbits); ++ saa_writeb(reg + 2, cl[i].position & 0xff); ++ saa_writeb(reg + 3, cl[i].position >> 8); ++ dprintk("clip: %s winbits=%02x pos=%d\n", ++ name,winbits,cl[i].position); ++ reg += 8; ++ } ++ for (; reg < 0x400; reg += 8) { ++ saa_writeb(reg+ 0, 0); ++ saa_writeb(reg + 1, 0); ++ saa_writeb(reg + 2, 0); ++ saa_writeb(reg + 3, 0); ++ } ++} ++ ++static int clip_range(int val) ++{ ++ if (val < 0) ++ val = 0; ++ return val; ++} ++ ++/* Sort into smallest position first order */ ++static int cliplist_cmp(const void *a, const void *b) ++{ ++ const struct cliplist *cla = a; ++ const struct cliplist *clb = b; ++ if (cla->position < clb->position) ++ return -1; ++ if (cla->position > clb->position) ++ return 1; ++ return 0; ++} ++ ++static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, ++ int nclips, int interlace) ++{ ++ struct cliplist col[16], row[16]; ++ int cols = 0, rows = 0, i; ++ int div = interlace ? 2 : 1; ++ ++ memset(col, 0, sizeof(col)); ++ memset(row, 0, sizeof(row)); ++ for (i = 0; i < nclips && i < 8; i++) { ++ col[cols].position = clip_range(clips[i].c.left); ++ col[cols].enable = (1 << i); ++ cols++; ++ col[cols].position = clip_range(clips[i].c.left+clips[i].c.width); ++ col[cols].disable = (1 << i); ++ cols++; ++ row[rows].position = clip_range(clips[i].c.top / div); ++ row[rows].enable = (1 << i); ++ rows++; ++ row[rows].position = clip_range((clips[i].c.top + clips[i].c.height) ++ / div); ++ row[rows].disable = (1 << i); ++ rows++; ++ } ++ sort(col, cols, sizeof col[0], cliplist_cmp, NULL); ++ sort(row, rows, sizeof row[0], cliplist_cmp, NULL); ++ set_cliplist(dev,0x380,col,cols,"cols"); ++ set_cliplist(dev,0x384,row,rows,"rows"); ++ return 0; ++} ++ ++static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win) ++{ ++ enum v4l2_field field; ++ int maxw, maxh; ++ ++ if (NULL == dev->ovbuf.base) ++ return -EINVAL; ++ if (NULL == dev->ovfmt) ++ return -EINVAL; ++ if (win->w.width < 48 || win->w.height < 32) ++ return -EINVAL; ++ if (win->clipcount > 2048) ++ return -EINVAL; ++ ++ field = win->field; ++ maxw = dev->crop_current.width; ++ maxh = dev->crop_current.height; ++ ++ 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: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ 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 start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) ++{ ++ unsigned long base,control,bpl; ++ int err; ++ ++ err = verify_preview(dev,&fh->win); ++ if (0 != err) ++ return err; ++ ++ dev->ovfield = fh->win.field; ++ dprintk("start_preview %dx%d+%d+%d %s field=%s\n", ++ fh->win.w.width,fh->win.w.height, ++ fh->win.w.left,fh->win.w.top, ++ dev->ovfmt->name,v4l2_field_names[dev->ovfield]); ++ ++ /* setup window + clipping */ ++ set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height, ++ V4L2_FIELD_HAS_BOTH(dev->ovfield)); ++ setup_clipping(dev,fh->clips,fh->nclips, ++ V4L2_FIELD_HAS_BOTH(dev->ovfield)); ++ if (dev->ovfmt->yuv) ++ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03); ++ else ++ saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01); ++ saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20); ++ ++ /* dma: setup channel 1 (= Video Task B) */ ++ base = (unsigned long)dev->ovbuf.base; ++ base += dev->ovbuf.fmt.bytesperline * fh->win.w.top; ++ base += dev->ovfmt->depth/8 * fh->win.w.left; ++ bpl = dev->ovbuf.fmt.bytesperline; ++ control = SAA7134_RS_CONTROL_BURST_16; ++ if (dev->ovfmt->bswap) ++ control |= SAA7134_RS_CONTROL_BSWAP; ++ if (dev->ovfmt->wswap) ++ control |= SAA7134_RS_CONTROL_WSWAP; ++ if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) { ++ saa_writel(SAA7134_RS_BA1(1),base); ++ saa_writel(SAA7134_RS_BA2(1),base+bpl); ++ saa_writel(SAA7134_RS_PITCH(1),bpl*2); ++ saa_writel(SAA7134_RS_CONTROL(1),control); ++ } else { ++ saa_writel(SAA7134_RS_BA1(1),base); ++ saa_writel(SAA7134_RS_BA2(1),base); ++ saa_writel(SAA7134_RS_PITCH(1),bpl); ++ saa_writel(SAA7134_RS_CONTROL(1),control); ++ } ++ ++ /* start dma */ ++ dev->ovenable = 1; ++ saa7134_set_dmabits(dev); ++ ++ return 0; ++} ++ ++static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) ++{ ++ dev->ovenable = 0; ++ saa7134_set_dmabits(dev); ++ return 0; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int buffer_activate(struct saa7134_dev *dev, ++ struct saa7134_buf *buf, ++ struct saa7134_buf *next) ++{ ++ unsigned long base,control,bpl; ++ unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ ++ ++ dprintk("buffer_activate buf=%p\n",buf); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buf->top_seen = 0; ++ ++ set_size(dev,TASK_A,buf->vb.width,buf->vb.height, ++ V4L2_FIELD_HAS_BOTH(buf->vb.field)); ++ if (buf->fmt->yuv) ++ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03); ++ else ++ saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01); ++ saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm); ++ ++ /* DMA: setup channel 0 (= Video Task A0) */ ++ base = saa7134_buffer_base(buf); ++ if (buf->fmt->planar) ++ bpl = buf->vb.width; ++ else ++ bpl = (buf->vb.width * buf->fmt->depth) / 8; ++ control = SAA7134_RS_CONTROL_BURST_16 | ++ SAA7134_RS_CONTROL_ME | ++ (buf->pt->dma >> 12); ++ if (buf->fmt->bswap) ++ control |= SAA7134_RS_CONTROL_BSWAP; ++ if (buf->fmt->wswap) ++ control |= SAA7134_RS_CONTROL_WSWAP; ++ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { ++ /* interlaced */ ++ saa_writel(SAA7134_RS_BA1(0),base); ++ saa_writel(SAA7134_RS_BA2(0),base+bpl); ++ saa_writel(SAA7134_RS_PITCH(0),bpl*2); ++ } else { ++ /* non-interlaced */ ++ saa_writel(SAA7134_RS_BA1(0),base); ++ saa_writel(SAA7134_RS_BA2(0),base); ++ saa_writel(SAA7134_RS_PITCH(0),bpl); ++ } ++ saa_writel(SAA7134_RS_CONTROL(0),control); ++ ++ if (buf->fmt->planar) { ++ /* DMA: setup channel 4+5 (= planar task A) */ ++ bpl_uv = bpl >> buf->fmt->hshift; ++ lines_uv = buf->vb.height >> buf->fmt->vshift; ++ base2 = base + bpl * buf->vb.height; ++ base3 = base2 + bpl_uv * lines_uv; ++ if (buf->fmt->uvswap) ++ tmp = base2, base2 = base3, base3 = tmp; ++ dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", ++ bpl_uv,lines_uv,base2,base3); ++ if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { ++ /* interlaced */ ++ saa_writel(SAA7134_RS_BA1(4),base2); ++ saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv); ++ saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2); ++ saa_writel(SAA7134_RS_BA1(5),base3); ++ saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv); ++ saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2); ++ } else { ++ /* non-interlaced */ ++ saa_writel(SAA7134_RS_BA1(4),base2); ++ saa_writel(SAA7134_RS_BA2(4),base2); ++ saa_writel(SAA7134_RS_PITCH(4),bpl_uv); ++ saa_writel(SAA7134_RS_BA1(5),base3); ++ saa_writel(SAA7134_RS_BA2(5),base3); ++ saa_writel(SAA7134_RS_PITCH(5),bpl_uv); ++ } ++ saa_writel(SAA7134_RS_CONTROL(4),control); ++ saa_writel(SAA7134_RS_CONTROL(5),control); ++ } ++ ++ /* start DMA */ ++ saa7134_set_dmabits(dev); ++ mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *q, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ unsigned int size; ++ int err; ++ ++ /* sanity checks */ ++ if (NULL == fh->fmt) ++ return -EINVAL; ++ if (fh->width < 48 || ++ fh->height < 32 || ++ fh->width/4 > dev->crop_current.width || ++ fh->height/4 > dev->crop_current.height || ++ fh->width > dev->crop_bounds.width || ++ fh->height > dev->crop_bounds.height) ++ return -EINVAL; ++ size = (fh->width * fh->height * fh->fmt->depth) >> 3; ++ if (0 != buf->vb.baddr && buf->vb.bsize < size) ++ return -EINVAL; ++ ++ dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n", ++ vb->i,fh->width,fh->height,size,v4l2_field_names[field], ++ fh->fmt->name); ++ if (buf->vb.width != fh->width || ++ buf->vb.height != fh->height || ++ buf->vb.size != size || ++ buf->vb.field != field || ++ buf->fmt != fh->fmt) { ++ saa7134_dma_free(q,buf); ++ } ++ ++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { ++ struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); ++ ++ buf->vb.width = fh->width; ++ buf->vb.height = fh->height; ++ buf->vb.size = size; ++ buf->vb.field = field; ++ buf->fmt = fh->fmt; ++ buf->pt = &fh->pt_cap; ++ dev->video_q.curr = NULL; ++ ++ err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); ++ if (err) ++ goto oops; ++ err = saa7134_pgtable_build(dev->pci,buf->pt, ++ dma->sglist, ++ dma->sglen, ++ saa7134_buffer_startpage(buf)); ++ if (err) ++ goto oops; ++ } ++ buf->vb.state = VIDEOBUF_PREPARED; ++ buf->activate = buffer_activate; ++ return 0; ++ ++ oops: ++ saa7134_dma_free(q,buf); ++ return err; ++} ++ ++static int ++buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ ++ *size = fh->fmt->depth * fh->width * fh->height >> 3; ++ if (0 == *count) ++ *count = gbuffers; ++ *count = saa7134_buffer_count(*size,*count); ++ return 0; ++} ++ ++static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_fh *fh = q->priv_data; ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ ++ saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf); ++} ++ ++static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) ++{ ++ struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); ++ ++ saa7134_dma_free(q,buf); ++} ++ ++static struct videobuf_queue_ops video_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++/* ------------------------------------------------------------------ */ ++ ++int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) ++{ ++ const struct v4l2_queryctrl* ctrl; ++ ++ ctrl = ctrl_by_id(c->id); ++ if (NULL == ctrl) ++ return -EINVAL; ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ c->value = dev->ctl_bright; ++ break; ++ case V4L2_CID_HUE: ++ c->value = dev->ctl_hue; ++ break; ++ case V4L2_CID_CONTRAST: ++ c->value = dev->ctl_contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ c->value = dev->ctl_saturation; ++ break; ++ case V4L2_CID_AUDIO_MUTE: ++ c->value = dev->ctl_mute; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ c->value = dev->ctl_volume; ++ break; ++ case V4L2_CID_PRIVATE_INVERT: ++ c->value = dev->ctl_invert; ++ break; ++ case V4L2_CID_HFLIP: ++ c->value = dev->ctl_mirror; ++ break; ++ case V4L2_CID_PRIVATE_Y_EVEN: ++ c->value = dev->ctl_y_even; ++ break; ++ case V4L2_CID_PRIVATE_Y_ODD: ++ c->value = dev->ctl_y_odd; ++ break; ++ case V4L2_CID_PRIVATE_AUTOMUTE: ++ c->value = dev->ctl_automute; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal); ++ ++static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) ++{ ++ struct saa7134_fh *fh = priv; ++ ++ return saa7134_g_ctrl_internal(fh->dev, fh, c); ++} ++ ++int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) ++{ ++ const struct v4l2_queryctrl* ctrl; ++ unsigned long flags; ++ int restart_overlay = 0; ++ int err; ++ ++ /* When called from the empress code fh == NULL. ++ That needs to be fixed somehow, but for now this is ++ good enough. */ ++ if (fh) { ++ err = v4l2_prio_check(&dev->prio, fh->prio); ++ if (0 != err) ++ return err; ++ } ++ err = -EINVAL; ++ ++ mutex_lock(&dev->lock); ++ ++ ctrl = ctrl_by_id(c->id); ++ if (NULL == ctrl) ++ goto error; ++ ++ dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); ++ switch (ctrl->type) { ++ case V4L2_CTRL_TYPE_BOOLEAN: ++ case V4L2_CTRL_TYPE_MENU: ++ case V4L2_CTRL_TYPE_INTEGER: ++ if (c->value < ctrl->minimum) ++ c->value = ctrl->minimum; ++ if (c->value > ctrl->maximum) ++ c->value = ctrl->maximum; ++ break; ++ default: ++ /* nothing */; ++ } ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ dev->ctl_bright = c->value; ++ saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); ++ break; ++ case V4L2_CID_HUE: ++ dev->ctl_hue = c->value; ++ saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); ++ break; ++ case V4L2_CID_CONTRAST: ++ dev->ctl_contrast = c->value; ++ saa_writeb(SAA7134_DEC_LUMA_CONTRAST, ++ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); ++ break; ++ case V4L2_CID_SATURATION: ++ dev->ctl_saturation = c->value; ++ saa_writeb(SAA7134_DEC_CHROMA_SATURATION, ++ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); ++ break; ++ case V4L2_CID_AUDIO_MUTE: ++ dev->ctl_mute = c->value; ++ saa7134_tvaudio_setmute(dev); ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ dev->ctl_volume = c->value; ++ saa7134_tvaudio_setvolume(dev,dev->ctl_volume); ++ break; ++ case V4L2_CID_PRIVATE_INVERT: ++ dev->ctl_invert = c->value; ++ saa_writeb(SAA7134_DEC_LUMA_CONTRAST, ++ dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); ++ saa_writeb(SAA7134_DEC_CHROMA_SATURATION, ++ dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); ++ break; ++ case V4L2_CID_HFLIP: ++ dev->ctl_mirror = c->value; ++ restart_overlay = 1; ++ break; ++ case V4L2_CID_PRIVATE_Y_EVEN: ++ dev->ctl_y_even = c->value; ++ restart_overlay = 1; ++ break; ++ case V4L2_CID_PRIVATE_Y_ODD: ++ dev->ctl_y_odd = c->value; ++ restart_overlay = 1; ++ break; ++ case V4L2_CID_PRIVATE_AUTOMUTE: ++ { ++ struct v4l2_priv_tun_config tda9887_cfg; ++ ++ tda9887_cfg.tuner = TUNER_TDA9887; ++ tda9887_cfg.priv = &dev->tda9887_conf; ++ ++ dev->ctl_automute = c->value; ++ if (dev->tda9887_conf) { ++ if (dev->ctl_automute) ++ dev->tda9887_conf |= TDA9887_AUTOMUTE; ++ else ++ dev->tda9887_conf &= ~TDA9887_AUTOMUTE; ++ ++ saa_call_all(dev, tuner, s_config, &tda9887_cfg); ++ } ++ break; ++ } ++ default: ++ goto error; ++ } ++ if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { ++ spin_lock_irqsave(&dev->slock,flags); ++ stop_preview(dev,fh); ++ start_preview(dev,fh); ++ spin_unlock_irqrestore(&dev->slock,flags); ++ } ++ err = 0; ++ ++error: ++ mutex_unlock(&dev->lock); ++ return err; ++} ++EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal); ++ ++static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) ++{ ++ struct saa7134_fh *fh = f; ++ ++ return saa7134_s_ctrl_internal(fh->dev, fh, c); ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh) ++{ ++ struct videobuf_queue* q = NULL; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ q = &fh->cap; ++ break; ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ q = &fh->vbi; ++ break; ++ default: ++ BUG(); ++ } ++ return q; ++} ++ ++static int saa7134_resource(struct saa7134_fh *fh) ++{ ++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return RESOURCE_VIDEO; ++ ++ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) ++ return RESOURCE_VBI; ++ ++ BUG(); ++ return 0; ++} ++ ++static int video_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct saa7134_dev *dev = video_drvdata(file); ++ struct saa7134_fh *fh; ++ enum v4l2_buf_type type = 0; ++ int radio = 0; ++ ++ switch (vdev->vfl_type) { ++ case VFL_TYPE_GRABBER: ++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ break; ++ case VFL_TYPE_VBI: ++ type = V4L2_BUF_TYPE_VBI_CAPTURE; ++ break; ++ case VFL_TYPE_RADIO: ++ radio = 1; ++ break; ++ } ++ ++ dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev), ++ radio, v4l2_type_names[type]); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh),GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ file->private_data = fh; ++ fh->dev = dev; ++ fh->radio = radio; ++ fh->type = type; ++ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); ++ fh->width = 720; ++ fh->height = 576; ++ v4l2_prio_open(&dev->prio, &fh->prio); ++ ++ videobuf_queue_sg_init(&fh->cap, &video_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct saa7134_buf), ++ fh, NULL); ++ videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops, ++ &dev->pci->dev, &dev->slock, ++ V4L2_BUF_TYPE_VBI_CAPTURE, ++ V4L2_FIELD_SEQ_TB, ++ sizeof(struct saa7134_buf), ++ fh, NULL); ++ saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); ++ saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); ++ ++ if (fh->radio) { ++ /* switch to radio mode */ ++ saa7134_tvaudio_setinput(dev,&card(dev).radio); ++ saa_call_all(dev, tuner, s_radio); ++ } else { ++ /* switch to video/vbi mode */ ++ video_mux(dev,dev->ctl_input); ++ } ++ return 0; ++} ++ ++static ssize_t ++video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ ++ switch (fh->type) { ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ if (res_locked(fh->dev,RESOURCE_VIDEO)) ++ return -EBUSY; ++ return videobuf_read_one(saa7134_queue(fh), ++ data, count, ppos, ++ file->f_flags & O_NONBLOCK); ++ case V4L2_BUF_TYPE_VBI_CAPTURE: ++ if (!res_get(fh->dev,fh,RESOURCE_VBI)) ++ return -EBUSY; ++ return videobuf_read_stream(saa7134_queue(fh), ++ data, count, ppos, 1, ++ file->f_flags & O_NONBLOCK); ++ break; ++ default: ++ BUG(); ++ return 0; ++ } ++} ++ ++static unsigned int ++video_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct videobuf_buffer *buf = NULL; ++ unsigned int rc = 0; ++ ++ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) ++ return videobuf_poll_stream(file, &fh->vbi, wait); ++ ++ if (res_check(fh,RESOURCE_VIDEO)) { ++ mutex_lock(&fh->cap.vb_lock); ++ if (!list_empty(&fh->cap.stream)) ++ buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); ++ } else { ++ mutex_lock(&fh->cap.vb_lock); ++ if (UNSET == fh->cap.read_off) { ++ /* need to capture a new frame */ ++ if (res_locked(fh->dev,RESOURCE_VIDEO)) ++ goto err; ++ if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field)) ++ goto err; ++ fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); ++ fh->cap.read_off = 0; ++ } ++ buf = fh->cap.read_buf; ++ } ++ ++ if (!buf) ++ goto err; ++ ++ poll_wait(file, &buf->done, wait); ++ if (buf->state == VIDEOBUF_DONE || ++ buf->state == VIDEOBUF_ERROR) ++ rc = POLLIN|POLLRDNORM; ++ mutex_unlock(&fh->cap.vb_lock); ++ return rc; ++ ++err: ++ mutex_unlock(&fh->cap.vb_lock); ++ return POLLERR; ++} ++ ++static int video_release(struct file *file) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa6588_command cmd; ++ unsigned long flags; ++ ++ saa7134_tvaudio_close(dev); ++ ++ /* turn off overlay */ ++ if (res_check(fh, RESOURCE_OVERLAY)) { ++ spin_lock_irqsave(&dev->slock,flags); ++ stop_preview(dev,fh); ++ spin_unlock_irqrestore(&dev->slock,flags); ++ res_free(dev,fh,RESOURCE_OVERLAY); ++ } ++ ++ /* stop video capture */ ++ if (res_check(fh, RESOURCE_VIDEO)) { ++ videobuf_streamoff(&fh->cap); ++ res_free(dev,fh,RESOURCE_VIDEO); ++ } ++ if (fh->cap.read_buf) { ++ buffer_release(&fh->cap,fh->cap.read_buf); ++ kfree(fh->cap.read_buf); ++ } ++ ++ /* stop vbi capture */ ++ if (res_check(fh, RESOURCE_VBI)) { ++ videobuf_stop(&fh->vbi); ++ res_free(dev,fh,RESOURCE_VBI); ++ } ++ ++ /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/ ++ saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0); ++ saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0); ++ saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); ++ saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); ++ ++ saa_call_all(dev, core, s_power, 0); ++ if (fh->radio) ++ saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd); ++ ++ /* free stuff */ ++ videobuf_mmap_free(&fh->cap); ++ videobuf_mmap_free(&fh->vbi); ++ saa7134_pgtable_free(dev->pci,&fh->pt_cap); ++ saa7134_pgtable_free(dev->pci,&fh->pt_vbi); ++ ++ v4l2_prio_close(&dev->prio, fh->prio); ++ file->private_data = NULL; ++ kfree(fh); ++ return 0; ++} ++ ++static int video_mmap(struct file *file, struct vm_area_struct * vma) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ ++ return videobuf_mmap_mapper(saa7134_queue(fh), vma); ++} ++ ++static ssize_t radio_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa6588_command cmd; ++ ++ cmd.block_count = count/3; ++ cmd.buffer = data; ++ cmd.instance = file; ++ cmd.result = -ENODEV; ++ ++ saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd); ++ ++ return cmd.result; ++} ++ ++static unsigned int radio_poll(struct file *file, poll_table *wait) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa6588_command cmd; ++ ++ cmd.instance = file; ++ cmd.event_list = wait; ++ cmd.result = -ENODEV; ++ saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); ++ ++ return cmd.result; ++} ++ ++/* ------------------------------------------------------------------ */ ++ ++static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_tvnorm *norm = dev->tvnorm; ++ ++ f->fmt.vbi.sampling_rate = 6750000 * 4; ++ f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; ++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; ++ f->fmt.vbi.offset = 64 * 4; ++ f->fmt.vbi.start[0] = norm->vbi_v_start_0; ++ f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; ++ f->fmt.vbi.start[1] = norm->vbi_v_start_1; ++ f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; ++ f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ ++ ++ return 0; ++} ++ ++static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ ++ f->fmt.pix.width = fh->width; ++ f->fmt.pix.height = fh->height; ++ f->fmt.pix.field = fh->cap.field; ++ f->fmt.pix.pixelformat = fh->fmt->fourcc; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fh->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ return 0; ++} ++ ++static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ ++ if (saa7134_no_overlay > 0) { ++ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ f->fmt.win = fh->win; ++ ++ return 0; ++} ++ ++static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_format *fmt; ++ enum v4l2_field field; ++ unsigned int maxw, maxh; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ maxw = min(dev->crop_current.width*4, dev->crop_bounds.width); ++ maxh = min(dev->crop_current.height*4, dev->crop_bounds.height); ++ ++ if (V4L2_FIELD_ANY == field) { ++ field = (f->fmt.pix.height > maxh/2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_BOTTOM; ++ } ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ f->fmt.pix.field = field; ++ if (f->fmt.pix.width < 48) ++ f->fmt.pix.width = 48; ++ if (f->fmt.pix.height < 32) ++ f->fmt.pix.height = 32; ++ if (f->fmt.pix.width > maxw) ++ f->fmt.pix.width = maxw; ++ if (f->fmt.pix.height > maxh) ++ f->fmt.pix.height = maxh; ++ f->fmt.pix.width &= ~0x03; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = ++ f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (saa7134_no_overlay > 0) { ++ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ ++ return verify_preview(dev, &f->fmt.win); ++} ++ ++static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ int err; ++ ++ err = saa7134_try_fmt_vid_cap(file, priv, f); ++ if (0 != err) ++ return err; ++ ++ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ fh->width = f->fmt.pix.width; ++ fh->height = f->fmt.pix.height; ++ fh->cap.field = f->fmt.pix.field; ++ return 0; ++} ++ ++static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int err; ++ unsigned long flags; ++ ++ if (saa7134_no_overlay > 0) { ++ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ err = verify_preview(dev, &f->fmt.win); ++ if (0 != err) ++ return err; ++ ++ mutex_lock(&dev->lock); ++ ++ fh->win = f->fmt.win; ++ fh->nclips = f->fmt.win.clipcount; ++ ++ if (fh->nclips > 8) ++ fh->nclips = 8; ++ ++ if (copy_from_user(fh->clips, f->fmt.win.clips, ++ sizeof(struct v4l2_clip)*fh->nclips)) { ++ mutex_unlock(&dev->lock); ++ return -EFAULT; ++ } ++ ++ if (res_check(fh, RESOURCE_OVERLAY)) { ++ spin_lock_irqsave(&dev->slock, flags); ++ stop_preview(dev, fh); ++ start_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ } ++ ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) ++{ ++ const struct v4l2_queryctrl *ctrl; ++ ++ if ((c->id < V4L2_CID_BASE || ++ c->id >= V4L2_CID_LASTP1) && ++ (c->id < V4L2_CID_PRIVATE_BASE || ++ c->id >= V4L2_CID_PRIVATE_LASTP1)) ++ return -EINVAL; ++ ctrl = ctrl_by_id(c->id); ++ *c = (NULL != ctrl) ? *ctrl : no_ctrl; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(saa7134_queryctrl); ++ ++static int saa7134_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ unsigned int n; ++ ++ n = i->index; ++ if (n >= SAA7134_INPUT_MAX) ++ return -EINVAL; ++ if (NULL == card_in(dev, i->index).name) ++ return -EINVAL; ++ i->index = n; ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ strcpy(i->name, card_in(dev, n).name); ++ if (card_in(dev, n).tv) ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ i->audioset = 1; ++ if (n == dev->ctl_input) { ++ int v1 = saa_readb(SAA7134_STATUS_VIDEO1); ++ int v2 = saa_readb(SAA7134_STATUS_VIDEO2); ++ ++ if (0 != (v1 & 0x40)) ++ i->status |= V4L2_IN_ST_NO_H_LOCK; ++ if (0 != (v2 & 0x40)) ++ i->status |= V4L2_IN_ST_NO_SYNC; ++ if (0 != (v2 & 0x0e)) ++ i->status |= V4L2_IN_ST_MACROVISION; ++ } ++ i->std = SAA7134_NORMS; ++ return 0; ++} ++ ++static int saa7134_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ *i = dev->ctl_input; ++ return 0; ++} ++ ++static int saa7134_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int err; ++ ++ err = v4l2_prio_check(&dev->prio, fh->prio); ++ if (0 != err) ++ return err; ++ ++ if (i >= SAA7134_INPUT_MAX) ++ return -EINVAL; ++ if (NULL == card_in(dev, i).name) ++ return -EINVAL; ++ mutex_lock(&dev->lock); ++ video_mux(dev, i); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++static int saa7134_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ unsigned int tuner_type = dev->tuner_type; ++ ++ strcpy(cap->driver, "saa7134"); ++ strlcpy(cap->card, saa7134_boards[dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_VBI_CAPTURE | ++ V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING | ++ V4L2_CAP_TUNER; ++ if (dev->has_rds) ++ cap->capabilities |= V4L2_CAP_RDS_CAPTURE; ++ if (saa7134_no_overlay <= 0) ++ cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; ++ ++ if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET)) ++ cap->capabilities &= ~V4L2_CAP_TUNER; ++ return 0; ++} ++ ++int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id) ++{ ++ unsigned long flags; ++ unsigned int i; ++ v4l2_std_id fixup; ++ int err; ++ ++ /* When called from the empress code fh == NULL. ++ That needs to be fixed somehow, but for now this is ++ good enough. */ ++ if (fh) { ++ err = v4l2_prio_check(&dev->prio, fh->prio); ++ if (0 != err) ++ return err; ++ } else if (res_locked(dev, RESOURCE_OVERLAY)) { ++ /* Don't change the std from the mpeg device ++ if overlay is active. */ ++ return -EBUSY; ++ } ++ ++ for (i = 0; i < TVNORMS; i++) ++ if (*id == tvnorms[i].id) ++ break; ++ ++ if (i == TVNORMS) ++ for (i = 0; i < TVNORMS; i++) ++ if (*id & tvnorms[i].id) ++ break; ++ if (i == TVNORMS) ++ return -EINVAL; ++ ++ if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { ++ if (secam[0] == 'L' || secam[0] == 'l') { ++ if (secam[1] == 'C' || secam[1] == 'c') ++ fixup = V4L2_STD_SECAM_LC; ++ else ++ fixup = V4L2_STD_SECAM_L; ++ } else { ++ if (secam[0] == 'D' || secam[0] == 'd') ++ fixup = V4L2_STD_SECAM_DK; ++ else ++ fixup = V4L2_STD_SECAM; ++ } ++ for (i = 0; i < TVNORMS; i++) { ++ if (fixup == tvnorms[i].id) ++ break; ++ } ++ if (i == TVNORMS) ++ return -EINVAL; ++ } ++ ++ *id = tvnorms[i].id; ++ ++ mutex_lock(&dev->lock); ++ if (fh && res_check(fh, RESOURCE_OVERLAY)) { ++ spin_lock_irqsave(&dev->slock, flags); ++ stop_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ ++ set_tvnorm(dev, &tvnorms[i]); ++ ++ spin_lock_irqsave(&dev->slock, flags); ++ start_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ } else ++ set_tvnorm(dev, &tvnorms[i]); ++ ++ saa7134_tvaudio_do_scan(dev); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(saa7134_s_std_internal); ++ ++static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7134_fh *fh = priv; ++ ++ return saa7134_s_std_internal(fh->dev, fh, id); ++} ++ ++static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ *id = dev->tvnorm->id; ++ return 0; ++} ++ ++static int saa7134_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *cap) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ cap->bounds = dev->crop_bounds; ++ cap->defrect = dev->crop_defrect; ++ cap->pixelaspect.numerator = 1; ++ cap->pixelaspect.denominator = 1; ++ if (dev->tvnorm->id & V4L2_STD_525_60) { ++ cap->pixelaspect.numerator = 11; ++ cap->pixelaspect.denominator = 10; ++ } ++ if (dev->tvnorm->id & V4L2_STD_625_50) { ++ cap->pixelaspect.numerator = 54; ++ cap->pixelaspect.denominator = 59; ++ } ++ return 0; ++} ++ ++static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ crop->c = dev->crop_current; ++ return 0; ++} ++ ++static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ struct v4l2_rect *b = &dev->crop_bounds; ++ struct v4l2_rect *c = &dev->crop_current; ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && ++ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) ++ return -EINVAL; ++ if (crop->c.height < 0) ++ return -EINVAL; ++ if (crop->c.width < 0) ++ return -EINVAL; ++ ++ if (res_locked(fh->dev, RESOURCE_OVERLAY)) ++ return -EBUSY; ++ if (res_locked(fh->dev, RESOURCE_VIDEO)) ++ return -EBUSY; ++ ++ *c = crop->c; ++ if (c->top < b->top) ++ c->top = b->top; ++ if (c->top > b->top + b->height) ++ c->top = b->top + b->height; ++ if (c->height > b->top - c->top + b->height) ++ c->height = b->top - c->top + b->height; ++ ++ if (c->left < b->left) ++ c->left = b->left; ++ if (c->left > b->left + b->width) ++ c->left = b->left + b->width; ++ if (c->width > b->left - c->left + b->width) ++ c->width = b->left - c->left + b->width; ++ return 0; ++} ++ ++static int saa7134_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int n; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ memset(t, 0, sizeof(*t)); ++ for (n = 0; n < SAA7134_INPUT_MAX; n++) { ++ if (card_in(dev, n).tv) ++ break; ++ } ++ if (n == SAA7134_INPUT_MAX) ++ return -EINVAL; ++ if (NULL != card_in(dev, n).name) { ++ strcpy(t->name, "Television"); ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM | ++ V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | ++ V4L2_TUNER_CAP_LANG2; ++ t->rangehigh = 0xffffffffUL; ++ t->rxsubchans = saa7134_tvaudio_getstereo(dev); ++ t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); ++ } ++ if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) ++ t->signal = 0xffff; ++ return 0; ++} ++ ++static int saa7134_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int rx, mode, err; ++ ++ err = v4l2_prio_check(&dev->prio, fh->prio); ++ if (0 != err) ++ return err; ++ ++ mode = dev->thread.mode; ++ if (UNSET == mode) { ++ rx = saa7134_tvaudio_getstereo(dev); ++ mode = saa7134_tvaudio_rx2mode(rx); ++ } ++ if (mode != t->audmode) ++ dev->thread.mode = t->audmode; ++ ++ return 0; ++} ++ ++static int saa7134_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ++ f->frequency = dev->ctl_freq; ++ ++ return 0; ++} ++ ++static int saa7134_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int err; ++ ++ err = v4l2_prio_check(&dev->prio, fh->prio); ++ if (0 != err) ++ return err; ++ ++ if (0 != f->tuner) ++ return -EINVAL; ++ if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) ++ return -EINVAL; ++ if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) ++ return -EINVAL; ++ mutex_lock(&dev->lock); ++ dev->ctl_freq = f->frequency; ++ ++ saa_call_all(dev, tuner, s_frequency, f); ++ ++ saa7134_tvaudio_do_scan(dev); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a) ++{ ++ strcpy(a->name, "audio"); ++ return 0; ++} ++ ++static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) ++{ ++ return 0; ++} ++ ++static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ ++ *p = v4l2_prio_max(&dev->prio); ++ return 0; ++} ++ ++static int saa7134_s_priority(struct file *file, void *f, ++ enum v4l2_priority prio) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ ++ return v4l2_prio_change(&dev->prio, &fh->prio, prio); ++} ++ ++static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index >= FORMATS) ++ return -EINVAL; ++ ++ strlcpy(f->description, formats[f->index].name, ++ sizeof(f->description)); ++ ++ f->pixelformat = formats[f->index].fourcc; ++ ++ return 0; ++} ++ ++static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (saa7134_no_overlay > 0) { ++ printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); ++ return -EINVAL; ++ } ++ ++ if ((f->index >= FORMATS) || formats[f->index].planar) ++ return -EINVAL; ++ ++ strlcpy(f->description, formats[f->index].name, ++ sizeof(f->description)); ++ ++ f->pixelformat = formats[f->index].fourcc; ++ ++ return 0; ++} ++ ++static int saa7134_g_fbuf(struct file *file, void *f, ++ struct v4l2_framebuffer *fb) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ ++ *fb = dev->ovbuf; ++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; ++ ++ return 0; ++} ++ ++static int saa7134_s_fbuf(struct file *file, void *f, ++ const struct v4l2_framebuffer *fb) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ struct saa7134_format *fmt; ++ ++ if (!capable(CAP_SYS_ADMIN) && ++ !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ /* check args */ ++ fmt = format_by_fourcc(fb->fmt.pixelformat); ++ if (NULL == fmt) ++ return -EINVAL; ++ ++ /* ok, accept it */ ++ dev->ovbuf = *fb; ++ dev->ovfmt = fmt; ++ if (0 == dev->ovbuf.fmt.bytesperline) ++ dev->ovbuf.fmt.bytesperline = ++ dev->ovbuf.fmt.width*fmt->depth/8; ++ return 0; ++} ++ ++static int saa7134_overlay(struct file *file, void *f, unsigned int on) ++{ ++ struct saa7134_fh *fh = f; ++ struct saa7134_dev *dev = fh->dev; ++ unsigned long flags; ++ ++ if (on) { ++ if (saa7134_no_overlay > 0) { ++ dprintk("no_overlay\n"); ++ return -EINVAL; ++ } ++ ++ if (!res_get(dev, fh, RESOURCE_OVERLAY)) ++ return -EBUSY; ++ spin_lock_irqsave(&dev->slock, flags); ++ start_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ } ++ if (!on) { ++ if (!res_check(fh, RESOURCE_OVERLAY)) ++ return -EINVAL; ++ spin_lock_irqsave(&dev->slock, flags); ++ stop_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ res_free(dev, fh, RESOURCE_OVERLAY); ++ } ++ return 0; ++} ++ ++static int saa7134_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct saa7134_fh *fh = priv; ++ return videobuf_reqbufs(saa7134_queue(fh), p); ++} ++ ++static int saa7134_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *b) ++{ ++ struct saa7134_fh *fh = priv; ++ return videobuf_querybuf(saa7134_queue(fh), b); ++} ++ ++static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct saa7134_fh *fh = priv; ++ return videobuf_qbuf(saa7134_queue(fh), b); ++} ++ ++static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) ++{ ++ struct saa7134_fh *fh = priv; ++ return videobuf_dqbuf(saa7134_queue(fh), b, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int saa7134_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int res = saa7134_resource(fh); ++ ++ if (!res_get(dev, fh, res)) ++ return -EBUSY; ++ ++ return videobuf_streamon(saa7134_queue(fh)); ++} ++ ++static int saa7134_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ int err; ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ int res = saa7134_resource(fh); ++ ++ err = videobuf_streamoff(saa7134_queue(fh)); ++ if (err < 0) ++ return err; ++ res_free(dev, fh, res); ++ return 0; ++} ++ ++static int saa7134_g_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *parm) ++{ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int vidioc_g_register (struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ reg->val = saa_readb(reg->reg); ++ reg->size = 1; ++ return 0; ++} ++ ++static int vidioc_s_register (struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct saa7134_fh *fh = priv; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (!v4l2_chip_match_host(®->match)) ++ return -EINVAL; ++ saa_writeb(reg->reg&0xffffff, reg->val); ++ return 0; ++} ++#endif ++ ++static int radio_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ ++ strcpy(cap->driver, "saa7134"); ++ strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ cap->capabilities = V4L2_CAP_TUNER; ++ return 0; ++} ++ ++static int radio_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ ++ memset(t, 0, sizeof(*t)); ++ strcpy(t->name, "Radio"); ++ t->type = V4L2_TUNER_RADIO; ++ ++ saa_call_all(dev, tuner, g_tuner, t); ++ if (dev->input->amux == TV) { ++ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); ++ t->rxsubchans = (saa_readb(0x529) & 0x08) ? ++ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; ++ } ++ return 0; ++} ++static int radio_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7134_fh *fh = file->private_data; ++ struct saa7134_dev *dev = fh->dev; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ ++ saa_call_all(dev, tuner, s_tuner, t); ++ return 0; ++} ++ ++static int radio_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ if (i->index != 0) ++ return -EINVAL; ++ ++ strcpy(i->name, "Radio"); ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ ++ return 0; ++} ++ ++static int radio_g_input(struct file *filp, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int radio_g_audio(struct file *file, void *priv, ++ struct v4l2_audio *a) ++{ ++ memset(a, 0, sizeof(*a)); ++ strcpy(a->name, "Radio"); ++ return 0; ++} ++ ++static int radio_s_audio(struct file *file, void *priv, ++ const struct v4l2_audio *a) ++{ ++ return 0; ++} ++ ++static int radio_s_input(struct file *filp, void *priv, unsigned int i) ++{ ++ return 0; ++} ++ ++static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) ++{ ++ return 0; ++} ++ ++static int radio_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ const struct v4l2_queryctrl *ctrl; ++ ++ if (c->id < V4L2_CID_BASE || ++ c->id >= V4L2_CID_LASTP1) ++ return -EINVAL; ++ if (c->id == V4L2_CID_AUDIO_MUTE) { ++ ctrl = ctrl_by_id(c->id); ++ *c = *ctrl; ++ } else ++ *c = no_ctrl; ++ return 0; ++} ++ ++static const struct v4l2_file_operations video_fops = ++{ ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .release = video_release, ++ .read = video_read, ++ .poll = video_poll, ++ .mmap = video_mmap, ++ .ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops video_ioctl_ops = { ++ .vidioc_querycap = saa7134_querycap, ++ .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap, ++ .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay, ++ .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, ++ .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, ++ .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, ++ .vidioc_g_audio = saa7134_g_audio, ++ .vidioc_s_audio = saa7134_s_audio, ++ .vidioc_cropcap = saa7134_cropcap, ++ .vidioc_reqbufs = saa7134_reqbufs, ++ .vidioc_querybuf = saa7134_querybuf, ++ .vidioc_qbuf = saa7134_qbuf, ++ .vidioc_dqbuf = saa7134_dqbuf, ++ .vidioc_s_std = saa7134_s_std, ++ .vidioc_g_std = saa7134_g_std, ++ .vidioc_enum_input = saa7134_enum_input, ++ .vidioc_g_input = saa7134_g_input, ++ .vidioc_s_input = saa7134_s_input, ++ .vidioc_queryctrl = saa7134_queryctrl, ++ .vidioc_g_ctrl = saa7134_g_ctrl, ++ .vidioc_s_ctrl = saa7134_s_ctrl, ++ .vidioc_streamon = saa7134_streamon, ++ .vidioc_streamoff = saa7134_streamoff, ++ .vidioc_g_tuner = saa7134_g_tuner, ++ .vidioc_s_tuner = saa7134_s_tuner, ++ .vidioc_g_crop = saa7134_g_crop, ++ .vidioc_s_crop = saa7134_s_crop, ++ .vidioc_g_fbuf = saa7134_g_fbuf, ++ .vidioc_s_fbuf = saa7134_s_fbuf, ++ .vidioc_overlay = saa7134_overlay, ++ .vidioc_g_priority = saa7134_g_priority, ++ .vidioc_s_priority = saa7134_s_priority, ++ .vidioc_g_parm = saa7134_g_parm, ++ .vidioc_g_frequency = saa7134_g_frequency, ++ .vidioc_s_frequency = saa7134_s_frequency, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vidioc_g_register, ++ .vidioc_s_register = vidioc_s_register, ++#endif ++}; ++ ++static const struct v4l2_file_operations radio_fops = { ++ .owner = THIS_MODULE, ++ .open = video_open, ++ .read = radio_read, ++ .release = video_release, ++ .ioctl = video_ioctl2, ++ .poll = radio_poll, ++}; ++ ++static const struct v4l2_ioctl_ops radio_ioctl_ops = { ++ .vidioc_querycap = radio_querycap, ++ .vidioc_g_tuner = radio_g_tuner, ++ .vidioc_enum_input = radio_enum_input, ++ .vidioc_g_audio = radio_g_audio, ++ .vidioc_s_tuner = radio_s_tuner, ++ .vidioc_s_audio = radio_s_audio, ++ .vidioc_s_input = radio_s_input, ++ .vidioc_s_std = radio_s_std, ++ .vidioc_queryctrl = radio_queryctrl, ++ .vidioc_g_input = radio_g_input, ++ .vidioc_g_ctrl = saa7134_g_ctrl, ++ .vidioc_s_ctrl = saa7134_s_ctrl, ++ .vidioc_g_frequency = saa7134_g_frequency, ++ .vidioc_s_frequency = saa7134_s_frequency, ++}; ++ ++/* ----------------------------------------------------------- */ ++/* exported stuff */ ++ ++struct video_device saa7134_video_template = { ++ .name = "saa7134-video", ++ .fops = &video_fops, ++ .ioctl_ops = &video_ioctl_ops, ++ .tvnorms = SAA7134_NORMS, ++ .current_norm = V4L2_STD_PAL, ++}; ++ ++struct video_device saa7134_radio_template = { ++ .name = "saa7134-radio", ++ .fops = &radio_fops, ++ .ioctl_ops = &radio_ioctl_ops, ++}; ++ ++int saa7134_video_init1(struct saa7134_dev *dev) ++{ ++ /* sanitycheck insmod options */ ++ if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) ++ gbuffers = 2; ++ if (gbufsize > gbufsize_max) ++ gbufsize = gbufsize_max; ++ gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; ++ ++ /* put some sensible defaults into the data structures ... */ ++ dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value; ++ dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value; ++ dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value; ++ dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value; ++ dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value; ++ dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value; ++ dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value; ++ dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value; ++ ++ if (dev->tda9887_conf && dev->ctl_automute) ++ dev->tda9887_conf |= TDA9887_AUTOMUTE; ++ dev->automute = 0; ++ ++ INIT_LIST_HEAD(&dev->video_q.queue); ++ init_timer(&dev->video_q.timeout); ++ dev->video_q.timeout.function = saa7134_buffer_timeout; ++ dev->video_q.timeout.data = (unsigned long)(&dev->video_q); ++ dev->video_q.dev = dev; ++ ++ if (saa7134_boards[dev->board].video_out) ++ saa7134_videoport_init(dev); ++ ++ return 0; ++} ++ ++int saa7134_videoport_init(struct saa7134_dev *dev) ++{ ++ /* enable video output */ ++ int vo = saa7134_boards[dev->board].video_out; ++ int video_reg; ++ unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts; ++ ++ /* Configure videoport */ ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]); ++ video_reg = video_out[vo][1]; ++ if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED) ++ video_reg &= ~VP_T_CODE_P_INVERTED; ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg); ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]); ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]); ++ video_reg = video_out[vo][5]; ++ if (vid_port_opts & SET_CLOCK_NOT_DELAYED) ++ video_reg &= ~VP_CLK_CTRL2_DELAYED; ++ if (vid_port_opts & SET_CLOCK_INVERTED) ++ video_reg |= VP_CLK_CTRL1_INVERTED; ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg); ++ video_reg = video_out[vo][6]; ++ if (vid_port_opts & SET_VSYNC_OFF) { ++ video_reg &= ~VP_VS_TYPE_MASK; ++ video_reg |= VP_VS_TYPE_OFF; ++ } ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg); ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]); ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]); ++ ++ /* Start videoport */ ++ saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]); ++ ++ return 0; ++} ++ ++int saa7134_video_init2(struct saa7134_dev *dev) ++{ ++ /* init video hw */ ++ set_tvnorm(dev,&tvnorms[0]); ++ video_mux(dev,0); ++ saa7134_tvaudio_setmute(dev); ++ saa7134_tvaudio_setvolume(dev,dev->ctl_volume); ++ return 0; ++} ++ ++void saa7134_irq_video_signalchange(struct saa7134_dev *dev) ++{ ++ static const char *st[] = { ++ "(no signal)", "NTSC", "PAL", "SECAM" }; ++ u32 st1,st2; ++ ++ st1 = saa_readb(SAA7134_STATUS_VIDEO1); ++ st2 = saa_readb(SAA7134_STATUS_VIDEO2); ++ dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", ++ (st1 & 0x40) ? "not locked" : "locked", ++ (st2 & 0x40) ? "no" : "yes", ++ st[st1 & 0x03]); ++ dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); ++ ++ if (dev->nosignal) { ++ /* no video signal -> mute audio */ ++ if (dev->ctl_automute) ++ dev->automute = 1; ++ saa7134_tvaudio_setmute(dev); ++ } else { ++ /* wake up tvaudio audio carrier scan thread */ ++ saa7134_tvaudio_do_scan(dev); ++ } ++ ++ if ((st2 & 0x80) && !noninterlaced && !dev->nosignal) ++ saa_clearb(SAA7134_SYNC_CTRL, 0x20); ++ else ++ saa_setb(SAA7134_SYNC_CTRL, 0x20); ++ ++ if (dev->mops && dev->mops->signal_change) ++ dev->mops->signal_change(dev); ++} ++ ++ ++void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) ++{ ++ enum v4l2_field field; ++ ++ spin_lock(&dev->slock); ++ if (dev->video_q.curr) { ++ dev->video_fieldcount++; ++ field = dev->video_q.curr->vb.field; ++ if (V4L2_FIELD_HAS_BOTH(field)) { ++ /* make sure we have seen both fields */ ++ if ((status & 0x10) == 0x00) { ++ dev->video_q.curr->top_seen = 1; ++ goto done; ++ } ++ if (!dev->video_q.curr->top_seen) ++ goto done; ++ } else if (field == V4L2_FIELD_TOP) { ++ if ((status & 0x10) != 0x10) ++ goto done; ++ } else if (field == V4L2_FIELD_BOTTOM) { ++ if ((status & 0x10) != 0x00) ++ goto done; ++ } ++ dev->video_q.curr->vb.field_count = dev->video_fieldcount; ++ saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE); ++ } ++ saa7134_buffer_next(dev,&dev->video_q); ++ ++ done: ++ spin_unlock(&dev->slock); ++} ++ ++/* ----------------------------------------------------------- */ ++/* ++ * Local variables: ++ * c-basic-offset: 8 ++ * End: ++ */ +diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h +new file mode 100644 +index 0000000..c24b651 +--- /dev/null ++++ b/drivers/media/pci/saa7134/saa7134.h +@@ -0,0 +1,855 @@ ++/* ++ * ++ * v4l2 device driver for philips saa7134 based TV cards ++ * ++ * (c) 2001,02 Gerd Knorr ++ * ++ * 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 SAA7134_VERSION "0, 2, 17" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) ++#include ++#endif ++ ++#define UNSET (-1U) ++ ++/* ----------------------------------------------------------- */ ++/* enums */ ++ ++enum saa7134_tvaudio_mode { ++ TVAUDIO_FM_MONO = 1, ++ TVAUDIO_FM_BG_STEREO = 2, ++ TVAUDIO_FM_SAT_STEREO = 3, ++ TVAUDIO_FM_K_STEREO = 4, ++ TVAUDIO_NICAM_AM = 5, ++ TVAUDIO_NICAM_FM = 6, ++}; ++ ++enum saa7134_audio_in { ++ TV = 1, ++ LINE1 = 2, ++ LINE2 = 3, ++ LINE2_LEFT, ++}; ++ ++enum saa7134_video_out { ++ CCIR656 = 1, ++}; ++ ++/* ----------------------------------------------------------- */ ++/* static data */ ++ ++struct saa7134_tvnorm { ++ char *name; ++ v4l2_std_id id; ++ ++ /* video decoder */ ++ unsigned int sync_control; ++ unsigned int luma_control; ++ unsigned int chroma_ctrl1; ++ unsigned int chroma_gain; ++ unsigned int chroma_ctrl2; ++ unsigned int vgate_misc; ++ ++ /* video scaler */ ++ unsigned int h_start; ++ unsigned int h_stop; ++ unsigned int video_v_start; ++ unsigned int video_v_stop; ++ unsigned int vbi_v_start_0; ++ unsigned int vbi_v_stop_0; ++ unsigned int src_timing; ++ unsigned int vbi_v_start_1; ++}; ++ ++struct saa7134_tvaudio { ++ char *name; ++ v4l2_std_id std; ++ enum saa7134_tvaudio_mode mode; ++ int carr1; ++ int carr2; ++}; ++ ++struct saa7134_format { ++ char *name; ++ unsigned int fourcc; ++ unsigned int depth; ++ unsigned int pm; ++ unsigned int vshift; /* vertical downsampling (for planar yuv) */ ++ unsigned int hshift; /* horizontal downsampling (for planar yuv) */ ++ unsigned int bswap:1; ++ unsigned int wswap:1; ++ unsigned int yuv:1; ++ unsigned int planar:1; ++ unsigned int uvswap:1; ++}; ++ ++struct saa7134_card_ir { ++ struct rc_dev *dev; ++ ++ char name[32]; ++ char phys[32]; ++ unsigned users; ++ ++ u32 polling; ++ u32 last_gpio; ++ u32 mask_keycode, mask_keydown, mask_keyup; ++ ++ bool running; ++ ++ struct timer_list timer; ++ ++ /* IR core raw decoding */ ++ u32 raw_decode; ++}; ++ ++/* ----------------------------------------------------------- */ ++/* card configuration */ ++ ++#define SAA7134_BOARD_NOAUTO UNSET ++#define SAA7134_BOARD_UNKNOWN 0 ++#define SAA7134_BOARD_PROTEUS_PRO 1 ++#define SAA7134_BOARD_FLYVIDEO3000 2 ++#define SAA7134_BOARD_FLYVIDEO2000 3 ++#define SAA7134_BOARD_EMPRESS 4 ++#define SAA7134_BOARD_MONSTERTV 5 ++#define SAA7134_BOARD_MD9717 6 ++#define SAA7134_BOARD_TVSTATION_RDS 7 ++#define SAA7134_BOARD_CINERGY400 8 ++#define SAA7134_BOARD_MD5044 9 ++#define SAA7134_BOARD_KWORLD 10 ++#define SAA7134_BOARD_CINERGY600 11 ++#define SAA7134_BOARD_MD7134 12 ++#define SAA7134_BOARD_TYPHOON_90031 13 ++#define SAA7134_BOARD_ELSA 14 ++#define SAA7134_BOARD_ELSA_500TV 15 ++#define SAA7134_BOARD_ASUSTeK_TVFM7134 16 ++#define SAA7134_BOARD_VA1000POWER 17 ++#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18 ++#define SAA7134_BOARD_VIDEOMATE_TV 19 ++#define SAA7134_BOARD_CRONOS_PLUS 20 ++#define SAA7134_BOARD_10MOONSTVMASTER 21 ++#define SAA7134_BOARD_MD2819 22 ++#define SAA7134_BOARD_BMK_MPEX_TUNER 23 ++#define SAA7134_BOARD_TVSTATION_DVR 24 ++#define SAA7134_BOARD_ASUSTEK_TVFM7133 25 ++#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26 ++#define SAA7134_BOARD_MANLI_MTV002 27 ++#define SAA7134_BOARD_MANLI_MTV001 28 ++#define SAA7134_BOARD_TG3000TV 29 ++#define SAA7134_BOARD_ECS_TVP3XP 30 ++#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31 ++#define SAA7134_BOARD_AVACSSMARTTV 32 ++#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33 ++#define SAA7134_BOARD_NOVAC_PRIMETV7133 34 ++#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35 ++#define SAA7134_BOARD_UPMOST_PURPLE_TV 36 ++#define SAA7134_BOARD_ITEMS_MTV005 37 ++#define SAA7134_BOARD_CINERGY200 38 ++#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39 ++#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40 ++#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41 ++#define SAA7134_BOARD_SABRENT_SBTTVFM 42 ++#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43 ++#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44 ++#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45 ++#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46 ++#define SAA7134_BOARD_CINERGY400_CARDBUS 47 ++#define SAA7134_BOARD_CINERGY600_MK3 48 ++#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49 ++#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50 ++#define SAA7134_BOARD_PROVIDEO_PV952 51 ++#define SAA7134_BOARD_AVERMEDIA_305 52 ++#define SAA7134_BOARD_ASUSTeK_TVFM7135 53 ++#define SAA7134_BOARD_FLYTVPLATINUM_FM 54 ++#define SAA7134_BOARD_FLYDVBTDUO 55 ++#define SAA7134_BOARD_AVERMEDIA_307 56 ++#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57 ++#define SAA7134_BOARD_ADS_INSTANT_TV 58 ++#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59 ++#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60 ++#define SAA7134_BOARD_PHILIPS_TOUGH 61 ++#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62 ++#define SAA7134_BOARD_KWORLD_XPERT 63 ++#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64 ++#define SAA7134_BOARD_KWORLD_TERMINATOR 65 ++#define SAA7134_BOARD_YUAN_TUN900 66 ++#define SAA7134_BOARD_BEHOLD_409FM 67 ++#define SAA7134_BOARD_GOTVIEW_7135 68 ++#define SAA7134_BOARD_PHILIPS_EUROPA 69 ++#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70 ++#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71 ++#define SAA7134_BOARD_RTD_VFG7350 72 ++#define SAA7134_BOARD_RTD_VFG7330 73 ++#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74 ++#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75 ++#define SAA7134_BOARD_MONSTERTV_MOBILE 76 ++#define SAA7134_BOARD_PINNACLE_PCTV_110i 77 ++#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78 ++#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79 ++#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80 ++#define SAA7134_BOARD_PHILIPS_TIGER 81 ++#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82 ++#define SAA7134_BOARD_CINERGY250PCI 83 ++#define SAA7134_BOARD_FLYDVB_TRIO 84 ++#define SAA7134_BOARD_AVERMEDIA_777 85 ++#define SAA7134_BOARD_FLYDVBT_LR301 86 ++#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87 ++#define SAA7134_BOARD_TEVION_DVBT_220RF 88 ++#define SAA7134_BOARD_ELSA_700TV 89 ++#define SAA7134_BOARD_KWORLD_ATSC110 90 ++#define SAA7134_BOARD_AVERMEDIA_A169_B 91 ++#define SAA7134_BOARD_AVERMEDIA_A169_B1 92 ++#define SAA7134_BOARD_MD7134_BRIDGE_2 93 ++#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94 ++#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95 ++#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96 ++#define SAA7134_BOARD_FLYDVBS_LR300 97 ++#define SAA7134_BOARD_PROTEUS_2309 98 ++#define SAA7134_BOARD_AVERMEDIA_A16AR 99 ++#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 ++#define SAA7134_BOARD_PINNACLE_PCTV_310i 101 ++#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102 ++#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103 ++#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 ++#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 ++#define SAA7134_BOARD_ENCORE_ENLTV 106 ++#define SAA7134_BOARD_ENCORE_ENLTV_FM 107 ++#define SAA7134_BOARD_CINERGY_HT_PCI 108 ++#define SAA7134_BOARD_PHILIPS_TIGER_S 109 ++#define SAA7134_BOARD_AVERMEDIA_M102 110 ++#define SAA7134_BOARD_ASUS_P7131_4871 111 ++#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112 ++#define SAA7134_BOARD_ECS_TVP3XP_4CB6 113 ++#define SAA7134_BOARD_KWORLD_DVBT_210 114 ++#define SAA7134_BOARD_SABRENT_TV_PCB05 115 ++#define SAA7134_BOARD_10MOONSTVMASTER3 116 ++#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117 ++#define SAA7134_BOARD_BEHOLD_401 118 ++#define SAA7134_BOARD_BEHOLD_403 119 ++#define SAA7134_BOARD_BEHOLD_403FM 120 ++#define SAA7134_BOARD_BEHOLD_405 121 ++#define SAA7134_BOARD_BEHOLD_405FM 122 ++#define SAA7134_BOARD_BEHOLD_407 123 ++#define SAA7134_BOARD_BEHOLD_407FM 124 ++#define SAA7134_BOARD_BEHOLD_409 125 ++#define SAA7134_BOARD_BEHOLD_505FM 126 ++#define SAA7134_BOARD_BEHOLD_507_9FM 127 ++#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128 ++#define SAA7134_BOARD_BEHOLD_607FM_MK3 129 ++#define SAA7134_BOARD_BEHOLD_M6 130 ++#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131 ++#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 ++#define SAA7134_BOARD_PHILIPS_SNAKE 133 ++#define SAA7134_BOARD_CREATIX_CTX953 134 ++#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135 ++#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136 ++#define SAA7134_BOARD_AVERMEDIA_A16D 137 ++#define SAA7134_BOARD_AVERMEDIA_M115 138 ++#define SAA7134_BOARD_VIDEOMATE_T750 139 ++#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140 ++#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141 ++#define SAA7134_BOARD_BEHOLD_H6 142 ++#define SAA7134_BOARD_BEHOLD_M63 143 ++#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144 ++#define SAA7134_BOARD_AVERMEDIA_M103 145 ++#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146 ++#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147 ++#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148 ++#define SAA7134_BOARD_AVERMEDIA_M135A 149 ++#define SAA7134_BOARD_REAL_ANGEL_220 150 ++#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 ++#define SAA7134_BOARD_ASUSTeK_TIGER 152 ++#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 ++#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 ++#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 ++#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 ++#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 ++#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 ++#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159 ++#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160 ++#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161 ++#define SAA7134_BOARD_BEHOLD_607FM_MK5 162 ++#define SAA7134_BOARD_BEHOLD_609FM_MK3 163 ++#define SAA7134_BOARD_BEHOLD_609FM_MK5 164 ++#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165 ++#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166 ++#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167 ++#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168 ++#define SAA7134_BOARD_VIDEOMATE_S350 169 ++#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170 ++#define SAA7134_BOARD_BEHOLD_X7 171 ++#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 ++#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 ++#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 ++#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 ++#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176 ++#define SAA7134_BOARD_HAWELL_HW_404M7 177 ++#define SAA7134_BOARD_BEHOLD_H7 178 ++#define SAA7134_BOARD_BEHOLD_A7 179 ++#define SAA7134_BOARD_AVERMEDIA_M733A 180 ++#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181 ++#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182 ++#define SAA7134_BOARD_VIDEOMATE_M1F 183 ++#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184 ++#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185 ++#define SAA7134_BOARD_BEHOLD_501 186 ++#define SAA7134_BOARD_BEHOLD_503FM 187 ++#define SAA7134_BOARD_SENSORAY811_911 188 ++#define SAA7134_BOARD_KWORLD_PC150U 189 ++#define SAA7134_BOARD_ASUSTeK_PS3_100 190 ++ ++#define SAA7134_MAXBOARDS 32 ++#define SAA7134_INPUT_MAX 8 ++ ++/* ----------------------------------------------------------- */ ++/* Since we support 2 remote types, lets tell them apart */ ++ ++#define SAA7134_REMOTE_GPIO 1 ++#define SAA7134_REMOTE_I2C 2 ++ ++/* ----------------------------------------------------------- */ ++/* Video Output Port Register Initialization Options */ ++ ++#define SET_T_CODE_POLARITY_NON_INVERTED (1 << 0) ++#define SET_CLOCK_NOT_DELAYED (1 << 1) ++#define SET_CLOCK_INVERTED (1 << 2) ++#define SET_VSYNC_OFF (1 << 3) ++ ++struct saa7134_input { ++ char *name; ++ unsigned int vmux; ++ enum saa7134_audio_in amux; ++ unsigned int gpio; ++ unsigned int tv:1; ++}; ++ ++enum saa7134_mpeg_type { ++ SAA7134_MPEG_UNUSED, ++ SAA7134_MPEG_EMPRESS, ++ SAA7134_MPEG_DVB, ++}; ++ ++enum saa7134_mpeg_ts_type { ++ SAA7134_MPEG_TS_PARALLEL = 0, ++ SAA7134_MPEG_TS_SERIAL, ++}; ++ ++struct saa7134_board { ++ char *name; ++ unsigned int audio_clock; ++ ++ /* input switching */ ++ unsigned int gpiomask; ++ struct saa7134_input inputs[SAA7134_INPUT_MAX]; ++ struct saa7134_input radio; ++ struct saa7134_input mute; ++ ++ /* i2c chip info */ ++ unsigned int tuner_type; ++ unsigned int radio_type; ++ unsigned char tuner_addr; ++ unsigned char radio_addr; ++ unsigned char empress_addr; ++ unsigned char rds_addr; ++ ++ unsigned int tda9887_conf; ++ unsigned int tuner_config; ++ ++ /* peripheral I/O */ ++ enum saa7134_video_out video_out; ++ enum saa7134_mpeg_type mpeg; ++ enum saa7134_mpeg_ts_type ts_type; ++ unsigned int vid_port_opts; ++ unsigned int ts_force_val:1; ++}; ++ ++#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) ++#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) ++#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) ++#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) ++#define card(dev) (saa7134_boards[dev->board]) ++#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) ++ ++/* ----------------------------------------------------------- */ ++/* device / file handle status */ ++ ++#define RESOURCE_OVERLAY 1 ++#define RESOURCE_VIDEO 2 ++#define RESOURCE_VBI 4 ++ ++#define INTERLACE_AUTO 0 ++#define INTERLACE_ON 1 ++#define INTERLACE_OFF 2 ++ ++#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ ++#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ ++ ++struct saa7134_dev; ++struct saa7134_dma; ++ ++/* saa7134 page table */ ++struct saa7134_pgtable { ++ unsigned int size; ++ __le32 *cpu; ++ dma_addr_t dma; ++}; ++ ++/* tvaudio thread status */ ++struct saa7134_thread { ++ struct task_struct *thread; ++ unsigned int scan1; ++ unsigned int scan2; ++ unsigned int mode; ++ unsigned int stopped; ++}; ++ ++/* buffer for one video/vbi/ts frame */ ++struct saa7134_buf { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ ++ /* saa7134 specific */ ++ struct saa7134_format *fmt; ++ unsigned int top_seen; ++ int (*activate)(struct saa7134_dev *dev, ++ struct saa7134_buf *buf, ++ struct saa7134_buf *next); ++ ++ /* page tables */ ++ struct saa7134_pgtable *pt; ++}; ++ ++struct saa7134_dmaqueue { ++ struct saa7134_dev *dev; ++ struct saa7134_buf *curr; ++ struct list_head queue; ++ struct timer_list timeout; ++ unsigned int need_two; ++}; ++ ++/* video filehandle status */ ++struct saa7134_fh { ++ struct saa7134_dev *dev; ++ unsigned int radio; ++ enum v4l2_buf_type type; ++ unsigned int resources; ++ enum v4l2_priority prio; ++ ++ /* video overlay */ ++ struct v4l2_window win; ++ struct v4l2_clip clips[8]; ++ unsigned int nclips; ++ ++ /* video capture */ ++ struct saa7134_format *fmt; ++ unsigned int width,height; ++ struct videobuf_queue cap; ++ struct saa7134_pgtable pt_cap; ++ ++ /* vbi capture */ ++ struct videobuf_queue vbi; ++ struct saa7134_pgtable pt_vbi; ++}; ++ ++/* dmasound dsp status */ ++struct saa7134_dmasound { ++ struct mutex lock; ++ int minor_mixer; ++ int minor_dsp; ++ unsigned int users_dsp; ++ ++ /* mixer */ ++ enum saa7134_audio_in input; ++ unsigned int count; ++ unsigned int line1; ++ unsigned int line2; ++ ++ /* dsp */ ++ unsigned int afmt; ++ unsigned int rate; ++ unsigned int channels; ++ unsigned int recording_on; ++ unsigned int dma_running; ++ unsigned int blocks; ++ unsigned int blksize; ++ unsigned int bufsize; ++ struct saa7134_pgtable pt; ++ struct videobuf_dmabuf dma; ++ unsigned int dma_blk; ++ unsigned int read_offset; ++ unsigned int read_count; ++ void * priv_data; ++ struct snd_pcm_substream *substream; ++}; ++ ++/* ts/mpeg status */ ++struct saa7134_ts { ++ /* TS capture */ ++ struct saa7134_pgtable pt_ts; ++ int nr_packets; ++ int nr_bufs; ++}; ++ ++/* ts/mpeg ops */ ++struct saa7134_mpeg_ops { ++ enum saa7134_mpeg_type type; ++ struct list_head next; ++ int (*init)(struct saa7134_dev *dev); ++ int (*fini)(struct saa7134_dev *dev); ++ void (*signal_change)(struct saa7134_dev *dev); ++}; ++ ++/* global device status */ ++struct saa7134_dev { ++ struct list_head devlist; ++ struct mutex lock; ++ spinlock_t slock; ++ struct v4l2_prio_state prio; ++ struct v4l2_device v4l2_dev; ++ /* workstruct for loading modules */ ++ struct work_struct request_module_wk; ++ ++ /* insmod option/autodetected */ ++ int autodetected; ++ ++ /* various device info */ ++ unsigned int resources; ++ struct video_device *video_dev; ++ struct video_device *radio_dev; ++ struct video_device *vbi_dev; ++ struct saa7134_dmasound dmasound; ++ ++ /* infrared remote */ ++ int has_remote; ++ struct saa7134_card_ir *remote; ++ ++ /* pci i/o */ ++ char name[32]; ++ int nr; ++ struct pci_dev *pci; ++ unsigned char pci_rev,pci_lat; ++ __u32 __iomem *lmmio; ++ __u8 __iomem *bmmio; ++ ++ /* config info */ ++ unsigned int board; ++ unsigned int tuner_type; ++ unsigned int radio_type; ++ unsigned char tuner_addr; ++ unsigned char radio_addr; ++ ++ unsigned int tda9887_conf; ++ unsigned int gpio_value; ++ ++ /* i2c i/o */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_client i2c_client; ++ unsigned char eedata[256]; ++ int has_rds; ++ ++ /* video overlay */ ++ struct v4l2_framebuffer ovbuf; ++ struct saa7134_format *ovfmt; ++ unsigned int ovenable; ++ enum v4l2_field ovfield; ++ ++ /* video+ts+vbi capture */ ++ struct saa7134_dmaqueue video_q; ++ struct saa7134_dmaqueue vbi_q; ++ unsigned int video_fieldcount; ++ unsigned int vbi_fieldcount; ++ ++ /* various v4l controls */ ++ struct saa7134_tvnorm *tvnorm; /* video */ ++ struct saa7134_tvaudio *tvaudio; ++ unsigned int ctl_input; ++ int ctl_bright; ++ int ctl_contrast; ++ int ctl_hue; ++ int ctl_saturation; ++ int ctl_freq; ++ int ctl_mute; /* audio */ ++ int ctl_volume; ++ int ctl_invert; /* private */ ++ int ctl_mirror; ++ int ctl_y_odd; ++ int ctl_y_even; ++ int ctl_automute; ++ ++ /* crop */ ++ struct v4l2_rect crop_bounds; ++ struct v4l2_rect crop_defrect; ++ struct v4l2_rect crop_current; ++ ++ /* other global state info */ ++ unsigned int automute; ++ struct saa7134_thread thread; ++ struct saa7134_input *input; ++ struct saa7134_input *hw_input; ++ unsigned int hw_mute; ++ int last_carrier; ++ int nosignal; ++ unsigned int insuspend; ++ ++ /* I2C keyboard data */ ++ struct IR_i2c_init_data init_data; ++ ++ /* SAA7134_MPEG_* */ ++ struct saa7134_ts ts; ++ struct saa7134_dmaqueue ts_q; ++ int ts_started; ++ struct saa7134_mpeg_ops *mops; ++ ++ /* SAA7134_MPEG_EMPRESS only */ ++ struct video_device *empress_dev; ++ struct videobuf_queue empress_tsq; ++ atomic_t empress_users; ++ struct work_struct empress_workqueue; ++ int empress_started; ++ ++#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) ++ /* SAA7134_MPEG_DVB only */ ++ struct videobuf_dvb_frontends frontends; ++ int (*original_demod_sleep)(struct dvb_frontend *fe); ++ int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); ++ int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); ++#endif ++ void (*gate_ctrl)(struct saa7134_dev *dev, int open); ++}; ++ ++/* ----------------------------------------------------------- */ ++ ++#define saa_readl(reg) readl(dev->lmmio + (reg)) ++#define saa_writel(reg,value) writel((value), dev->lmmio + (reg)); ++#define saa_andorl(reg,mask,value) \ ++ writel((readl(dev->lmmio+(reg)) & ~(mask)) |\ ++ ((value) & (mask)), dev->lmmio+(reg)) ++#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit)) ++#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0) ++ ++#define saa_readb(reg) readb(dev->bmmio + (reg)) ++#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg)); ++#define saa_andorb(reg,mask,value) \ ++ writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\ ++ ((value) & (mask)), dev->bmmio+(reg)) ++#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit)) ++#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0) ++ ++#define saa_wait(us) { udelay(us); } ++ ++#define SAA7134_NORMS (\ ++ V4L2_STD_PAL | V4L2_STD_PAL_N | \ ++ V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ ++ V4L2_STD_NTSC | V4L2_STD_PAL_M | \ ++ V4L2_STD_PAL_60) ++ ++#define GRP_EMPRESS (1) ++#define saa_call_all(dev, o, f, args...) do { \ ++ if (dev->gate_ctrl) \ ++ dev->gate_ctrl(dev, 1); \ ++ v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \ ++ if (dev->gate_ctrl) \ ++ dev->gate_ctrl(dev, 0); \ ++} while (0) ++ ++#define saa_call_empress(dev, o, f, args...) ({ \ ++ long _rc; \ ++ if (dev->gate_ctrl) \ ++ dev->gate_ctrl(dev, 1); \ ++ _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \ ++ GRP_EMPRESS, o, f , ##args); \ ++ if (dev->gate_ctrl) \ ++ dev->gate_ctrl(dev, 0); \ ++ _rc; \ ++}) ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-core.c */ ++ ++extern struct list_head saa7134_devlist; ++extern struct mutex saa7134_devlist_lock; ++extern int saa7134_no_overlay; ++ ++void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); ++void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); ++ ++#define SAA7134_PGTABLE_SIZE 4096 ++ ++int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt); ++int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, ++ struct scatterlist *list, unsigned int length, ++ unsigned int startpage); ++void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt); ++ ++int saa7134_buffer_count(unsigned int size, unsigned int count); ++int saa7134_buffer_startpage(struct saa7134_buf *buf); ++unsigned long saa7134_buffer_base(struct saa7134_buf *buf); ++ ++int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, ++ struct saa7134_buf *buf); ++void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, ++ unsigned int state); ++void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); ++void saa7134_buffer_timeout(unsigned long data); ++void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf); ++ ++int saa7134_set_dmabits(struct saa7134_dev *dev); ++ ++extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev); ++extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev); ++ ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-cards.c */ ++ ++extern struct saa7134_board saa7134_boards[]; ++extern const unsigned int saa7134_bcount; ++extern struct pci_device_id __devinitdata saa7134_pci_tbl[]; ++ ++extern int saa7134_board_init1(struct saa7134_dev *dev); ++extern int saa7134_board_init2(struct saa7134_dev *dev); ++int saa7134_tuner_callback(void *priv, int component, int command, int arg); ++ ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-i2c.c */ ++ ++int saa7134_i2c_register(struct saa7134_dev *dev); ++int saa7134_i2c_unregister(struct saa7134_dev *dev); ++ ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-video.c */ ++ ++extern unsigned int video_debug; ++extern struct video_device saa7134_video_template; ++extern struct video_device saa7134_radio_template; ++ ++int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); ++int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); ++int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); ++int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id); ++ ++int saa7134_videoport_init(struct saa7134_dev *dev); ++void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); ++ ++int saa7134_video_init1(struct saa7134_dev *dev); ++int saa7134_video_init2(struct saa7134_dev *dev); ++void saa7134_irq_video_signalchange(struct saa7134_dev *dev); ++void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); ++ ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-ts.c */ ++ ++#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */ ++ ++extern struct videobuf_queue_ops saa7134_ts_qops; ++ ++int saa7134_ts_init1(struct saa7134_dev *dev); ++int saa7134_ts_fini(struct saa7134_dev *dev); ++void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status); ++ ++int saa7134_ts_register(struct saa7134_mpeg_ops *ops); ++void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops); ++ ++int saa7134_ts_init_hw(struct saa7134_dev *dev); ++ ++int saa7134_ts_start(struct saa7134_dev *dev); ++int saa7134_ts_stop(struct saa7134_dev *dev); ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-vbi.c */ ++ ++extern struct videobuf_queue_ops saa7134_vbi_qops; ++extern struct video_device saa7134_vbi_template; ++ ++int saa7134_vbi_init1(struct saa7134_dev *dev); ++int saa7134_vbi_fini(struct saa7134_dev *dev); ++void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status); ++ ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-tvaudio.c */ ++ ++int saa7134_tvaudio_rx2mode(u32 rx); ++ ++void saa7134_tvaudio_setmute(struct saa7134_dev *dev); ++void saa7134_tvaudio_setinput(struct saa7134_dev *dev, ++ struct saa7134_input *in); ++void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level); ++int saa7134_tvaudio_getstereo(struct saa7134_dev *dev); ++ ++void saa7134_tvaudio_init(struct saa7134_dev *dev); ++int saa7134_tvaudio_init2(struct saa7134_dev *dev); ++int saa7134_tvaudio_fini(struct saa7134_dev *dev); ++int saa7134_tvaudio_do_scan(struct saa7134_dev *dev); ++int saa7134_tvaudio_close(struct saa7134_dev *dev); ++ ++int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value); ++ ++void saa7134_enable_i2s(struct saa7134_dev *dev); ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-oss.c */ ++ ++extern const struct file_operations saa7134_dsp_fops; ++extern const struct file_operations saa7134_mixer_fops; ++ ++int saa7134_oss_init1(struct saa7134_dev *dev); ++int saa7134_oss_fini(struct saa7134_dev *dev); ++void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status); ++ ++/* ----------------------------------------------------------- */ ++/* saa7134-input.c */ ++ ++#if defined(CONFIG_VIDEO_SAA7134_RC) ++int saa7134_input_init1(struct saa7134_dev *dev); ++void saa7134_input_fini(struct saa7134_dev *dev); ++void saa7134_input_irq(struct saa7134_dev *dev); ++void saa7134_probe_i2c_ir(struct saa7134_dev *dev); ++int saa7134_ir_start(struct saa7134_dev *dev); ++void saa7134_ir_stop(struct saa7134_dev *dev); ++#else ++#define saa7134_input_init1(dev) ((void)0) ++#define saa7134_input_fini(dev) ((void)0) ++#define saa7134_input_irq(dev) ((void)0) ++#define saa7134_probe_i2c_ir(dev) ((void)0) ++#define saa7134_ir_start(dev) ((void)0) ++#define saa7134_ir_stop(dev) ((void)0) ++#endif +diff --git a/drivers/media/pci/saa7146/Kconfig b/drivers/media/pci/saa7146/Kconfig +new file mode 100644 +index 0000000..da88b77 +--- /dev/null ++++ b/drivers/media/pci/saa7146/Kconfig +@@ -0,0 +1,38 @@ ++config VIDEO_HEXIUM_GEMINI ++ tristate "Hexium Gemini frame grabber" ++ depends on PCI && VIDEO_V4L2 && I2C ++ select VIDEO_SAA7146_VV ++ ---help--- ++ This is a video4linux driver for the Hexium Gemini frame ++ grabber card by Hexium. Please note that the Gemini Dual ++ card is *not* fully supported. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hexium_gemini. ++ ++config VIDEO_HEXIUM_ORION ++ tristate "Hexium HV-PCI6 and Orion frame grabber" ++ depends on PCI && VIDEO_V4L2 && I2C ++ select VIDEO_SAA7146_VV ++ ---help--- ++ This is a video4linux driver for the Hexium HV-PCI6 and ++ Orion frame grabber cards by Hexium. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called hexium_orion. ++ ++config VIDEO_MXB ++ tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" ++ depends on PCI && VIDEO_V4L2 && I2C ++ select VIDEO_SAA7146_VV ++ select VIDEO_TUNER ++ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_TDA9840 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_TEA6415C if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_TEA6420 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for the 'Multimedia eXtension Board' ++ TV card by Siemens-Nixdorf. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mxb. +diff --git a/drivers/media/pci/saa7146/Makefile b/drivers/media/pci/saa7146/Makefile +new file mode 100644 +index 0000000..f3566a9 +--- /dev/null ++++ b/drivers/media/pci/saa7146/Makefile +@@ -0,0 +1,5 @@ ++obj-$(CONFIG_VIDEO_MXB) += mxb.o ++obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o ++obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o ++ ++ccflags-y += -I$(srctree)/drivers/media/i2c +diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c +new file mode 100644 +index 0000000..65c5bec +--- /dev/null ++++ b/drivers/media/pci/saa7146/hexium_gemini.c +@@ -0,0 +1,431 @@ ++/* ++ hexium_gemini.c - v4l2 driver for Hexium Gemini frame grabber cards ++ ++ Visit http://www.mihu.de/linux/saa7146/ and follow the link ++ to "hexium" for further details about this card. ++ ++ Copyright (C) 2003 Michael Hunold ++ ++ 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. ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#define DEBUG_VARIABLE debug ++ ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "debug verbosity"); ++ ++/* global variables */ ++static int hexium_num; ++ ++#define HEXIUM_GEMINI 4 ++#define HEXIUM_GEMINI_DUAL 5 ++ ++#define HEXIUM_INPUTS 9 ++static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { ++ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++}; ++ ++#define HEXIUM_AUDIOS 0 ++ ++struct hexium_data ++{ ++ s8 adr; ++ u8 byte; ++}; ++ ++#define HEXIUM_GEMINI_V_1_0 1 ++#define HEXIUM_GEMINI_DUAL_V_1_0 2 ++ ++struct hexium ++{ ++ int type; ++ ++ struct video_device *video_dev; ++ struct i2c_adapter i2c_adapter; ++ ++ int cur_input; /* current input */ ++ v4l2_std_id cur_std; /* current standard */ ++}; ++ ++/* Samsung KS0127B decoder default registers */ ++static u8 hexium_ks0127b[0x100]={ ++/*00*/ 0x00,0x52,0x30,0x40,0x01,0x0C,0x2A,0x10, ++/*08*/ 0x00,0x00,0x00,0x60,0x00,0x00,0x0F,0x06, ++/*10*/ 0x00,0x00,0xE4,0xC0,0x00,0x00,0x00,0x00, ++/*18*/ 0x14,0x9B,0xFE,0xFF,0xFC,0xFF,0x03,0x22, ++/*20*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*28*/ 0x00,0x00,0x00,0x00,0x00,0x2C,0x9B,0x00, ++/*30*/ 0x00,0x00,0x10,0x80,0x80,0x10,0x80,0x80, ++/*38*/ 0x01,0x04,0x00,0x00,0x00,0x29,0xC0,0x00, ++/*40*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*48*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*50*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*58*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*60*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*68*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*70*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*78*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*80*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*88*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*90*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*98*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*A0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*A8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*B0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*B8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*C0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*C8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*D0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*D8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*E0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*E8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*F0*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, ++/*F8*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ++}; ++ ++static struct hexium_data hexium_pal[] = { ++ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } ++}; ++ ++static struct hexium_data hexium_ntsc[] = { ++ { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } ++}; ++ ++static struct hexium_data hexium_secam[] = { ++ { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } ++}; ++ ++static struct hexium_data hexium_input_select[] = { ++ { 0x02, 0x60 }, ++ { 0x02, 0x64 }, ++ { 0x02, 0x61 }, ++ { 0x02, 0x65 }, ++ { 0x02, 0x62 }, ++ { 0x02, 0x66 }, ++ { 0x02, 0x68 }, ++ { 0x02, 0x69 }, ++ { 0x02, 0x6A }, ++}; ++ ++/* fixme: h_offset = 0 for Hexium Gemini *Dual*, which ++ are currently *not* supported*/ ++static struct saa7146_standard hexium_standards[] = { ++ { ++ .name = "PAL", .id = V4L2_STD_PAL, ++ .v_offset = 28, .v_field = 288, ++ .h_offset = 1, .h_pixels = 680, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 28, .v_field = 240, ++ .h_offset = 1, .h_pixels = 640, ++ .v_max_out = 480, .h_max_out = 640, ++ }, { ++ .name = "SECAM", .id = V4L2_STD_SECAM, ++ .v_offset = 28, .v_field = 288, ++ .h_offset = 1, .h_pixels = 720, ++ .v_max_out = 576, .h_max_out = 768, ++ } ++}; ++ ++/* bring hardware to a sane state. this has to be done, just in case someone ++ wants to capture from this device before it has been properly initialized. ++ the capture engine would badly fail, because no valid signal arrives on the ++ saa7146, thus leading to timeouts and stuff. */ ++static int hexium_init_done(struct saa7146_dev *dev) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ union i2c_smbus_data data; ++ int i = 0; ++ ++ DEB_D("hexium_init_done called\n"); ++ ++ /* initialize the helper ics to useful values */ ++ for (i = 0; i < sizeof(hexium_ks0127b); i++) { ++ data.byte = hexium_ks0127b[i]; ++ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { ++ pr_err("hexium_init_done() failed for address 0x%02x\n", ++ i); ++ } ++ } ++ ++ return 0; ++} ++ ++static int hexium_set_input(struct hexium *hexium, int input) ++{ ++ union i2c_smbus_data data; ++ ++ DEB_D("\n"); ++ ++ data.byte = hexium_input_select[input].byte; ++ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, hexium_input_select[input].adr, I2C_SMBUS_BYTE_DATA, &data)) { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) ++{ ++ union i2c_smbus_data data; ++ int i = 0; ++ ++ DEB_D("\n"); ++ ++ while (vdec[i].adr != -1) { ++ data.byte = vdec[i].byte; ++ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x6c, 0, I2C_SMBUS_WRITE, vdec[i].adr, I2C_SMBUS_BYTE_DATA, &data)) { ++ pr_err("hexium_init_done: hexium_set_standard() failed for address 0x%02x\n", ++ i); ++ return -1; ++ } ++ i++; ++ } ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); ++ ++ if (i->index >= HEXIUM_INPUTS) ++ return -EINVAL; ++ ++ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); ++ ++ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ *input = hexium->cur_input; ++ ++ DEB_D("VIDIOC_G_INPUT: %d\n", *input); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ DEB_EE("VIDIOC_S_INPUT %d\n", input); ++ ++ if (input >= HEXIUM_INPUTS) ++ return -EINVAL; ++ ++ hexium->cur_input = input; ++ hexium_set_input(hexium, input); ++ return 0; ++} ++ ++static struct saa7146_ext_vv vv_data; ++ ++/* this function only gets called when the probing was successful */ ++static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) ++{ ++ struct hexium *hexium; ++ int ret; ++ ++ DEB_EE("\n"); ++ ++ hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); ++ if (NULL == hexium) { ++ pr_err("not enough kernel memory in hexium_attach()\n"); ++ return -ENOMEM; ++ } ++ dev->ext_priv = hexium; ++ ++ /* enable i2c-port pins */ ++ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); ++ ++ hexium->i2c_adapter = (struct i2c_adapter) { ++ .name = "hexium gemini", ++ }; ++ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); ++ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { ++ DEB_S("cannot register i2c-device. skipping.\n"); ++ kfree(hexium); ++ return -EFAULT; ++ } ++ ++ /* set HWControl GPIO number 2 */ ++ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); ++ ++ saa7146_write(dev, DD1_INIT, 0x07000700); ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ /* the rest */ ++ hexium->cur_input = 0; ++ hexium_init_done(dev); ++ ++ hexium_set_standard(hexium, hexium_pal); ++ hexium->cur_std = V4L2_STD_PAL; ++ ++ hexium_set_input(hexium, 0); ++ hexium->cur_input = 0; ++ ++ saa7146_vv_init(dev, &vv_data); ++ ++ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; ++ vv_data.vid_ops.vidioc_g_input = vidioc_g_input; ++ vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ++ ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); ++ if (ret < 0) { ++ pr_err("cannot register capture v4l2 device. skipping.\n"); ++ return ret; ++ } ++ ++ pr_info("found 'hexium gemini' frame grabber-%d\n", hexium_num); ++ hexium_num++; ++ ++ return 0; ++} ++ ++static int hexium_detach(struct saa7146_dev *dev) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ DEB_EE("dev:%p\n", dev); ++ ++ saa7146_unregister_device(&hexium->video_dev, dev); ++ saa7146_vv_release(dev); ++ ++ hexium_num--; ++ ++ i2c_del_adapter(&hexium->i2c_adapter); ++ kfree(hexium); ++ return 0; ++} ++ ++static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ if (V4L2_STD_PAL == std->id) { ++ hexium_set_standard(hexium, hexium_pal); ++ hexium->cur_std = V4L2_STD_PAL; ++ return 0; ++ } else if (V4L2_STD_NTSC == std->id) { ++ hexium_set_standard(hexium, hexium_ntsc); ++ hexium->cur_std = V4L2_STD_NTSC; ++ return 0; ++ } else if (V4L2_STD_SECAM == std->id) { ++ hexium_set_standard(hexium, hexium_secam); ++ hexium->cur_std = V4L2_STD_SECAM; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++static struct saa7146_extension hexium_extension; ++ ++static struct saa7146_pci_extension_data hexium_gemini_4bnc = { ++ .ext_priv = "Hexium Gemini (4 BNC)", ++ .ext = &hexium_extension, ++}; ++ ++static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { ++ .ext_priv = "Hexium Gemini Dual (4 BNC)", ++ .ext = &hexium_extension, ++}; ++ ++static struct pci_device_id pci_tbl[] = { ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x17c8, ++ .subdevice = 0x2401, ++ .driver_data = (unsigned long) &hexium_gemini_4bnc, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x17c8, ++ .subdevice = 0x2402, ++ .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, ++ }, ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_ext_vv vv_data = { ++ .inputs = HEXIUM_INPUTS, ++ .capabilities = 0, ++ .stds = &hexium_standards[0], ++ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard), ++ .std_callback = &std_callback, ++}; ++ ++static struct saa7146_extension hexium_extension = { ++ .name = "hexium gemini", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .pci_tbl = &pci_tbl[0], ++ .module = THIS_MODULE, ++ ++ .attach = hexium_attach, ++ .detach = hexium_detach, ++ ++ .irq_mask = 0, ++ .irq_func = NULL, ++}; ++ ++static int __init hexium_init_module(void) ++{ ++ if (0 != saa7146_register_extension(&hexium_extension)) { ++ DEB_S("failed to register extension\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void __exit hexium_cleanup_module(void) ++{ ++ saa7146_unregister_extension(&hexium_extension); ++} ++ ++module_init(hexium_init_module); ++module_exit(hexium_cleanup_module); ++ ++MODULE_DESCRIPTION("video4linux-2 driver for Hexium Gemini frame grabber cards"); ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c +new file mode 100644 +index 0000000..d6b66f6 +--- /dev/null ++++ b/drivers/media/pci/saa7146/hexium_orion.c +@@ -0,0 +1,503 @@ ++/* ++ hexium_orion.c - v4l2 driver for the Hexium Orion frame grabber cards ++ ++ Visit http://www.mihu.de/linux/saa7146/ and follow the link ++ to "hexium" for further details about this card. ++ ++ Copyright (C) 2003 Michael Hunold ++ ++ 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. ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#define DEBUG_VARIABLE debug ++ ++#include ++#include ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "debug verbosity"); ++ ++/* global variables */ ++static int hexium_num; ++ ++#define HEXIUM_HV_PCI6_ORION 1 ++#define HEXIUM_ORION_1SVHS_3BNC 2 ++#define HEXIUM_ORION_4BNC 3 ++ ++#define HEXIUM_INPUTS 9 ++static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { ++ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++}; ++ ++#define HEXIUM_AUDIOS 0 ++ ++struct hexium_data ++{ ++ s8 adr; ++ u8 byte; ++}; ++ ++struct hexium ++{ ++ int type; ++ struct video_device *video_dev; ++ struct i2c_adapter i2c_adapter; ++ ++ int cur_input; /* current input */ ++}; ++ ++/* Philips SAA7110 decoder default registers */ ++static u8 hexium_saa7110[53]={ ++/*00*/ 0x4C,0x3C,0x0D,0xEF,0xBD,0xF0,0x00,0x00, ++/*08*/ 0xF8,0xF8,0x60,0x60,0x40,0x86,0x18,0x90, ++/*10*/ 0x00,0x2C,0x40,0x46,0x42,0x1A,0xFF,0xDA, ++/*18*/ 0xF0,0x8B,0x00,0x00,0x00,0x00,0x00,0x00, ++/*20*/ 0xD9,0x17,0x40,0x41,0x80,0x41,0x80,0x4F, ++/*28*/ 0xFE,0x01,0x0F,0x0F,0x03,0x01,0x81,0x03, ++/*30*/ 0x44,0x75,0x01,0x8C,0x03 ++}; ++ ++static struct { ++ struct hexium_data data[8]; ++} hexium_input_select[] = { ++{ ++ { /* cvbs 1 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0xD9 }, ++ { 0x21, 0x17 }, // 0x16, ++ { 0x22, 0x40 }, ++ { 0x2C, 0x03 }, ++ { 0x30, 0x44 }, ++ { 0x31, 0x75 }, // ?? ++ { 0x21, 0x16 }, // 0x03, ++ } ++}, { ++ { /* cvbs 2 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0x78 }, ++ { 0x21, 0x07 }, // 0x03, ++ { 0x22, 0xD2 }, ++ { 0x2C, 0x83 }, ++ { 0x30, 0x60 }, ++ { 0x31, 0xB5 }, // ? ++ { 0x21, 0x03 }, ++ } ++}, { ++ { /* cvbs 3 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0xBA }, ++ { 0x21, 0x07 }, // 0x05, ++ { 0x22, 0x91 }, ++ { 0x2C, 0x03 }, ++ { 0x30, 0x60 }, ++ { 0x31, 0xB5 }, // ?? ++ { 0x21, 0x05 }, // 0x03, ++ } ++}, { ++ { /* cvbs 4 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0xD8 }, ++ { 0x21, 0x17 }, // 0x16, ++ { 0x22, 0x40 }, ++ { 0x2C, 0x03 }, ++ { 0x30, 0x44 }, ++ { 0x31, 0x75 }, // ?? ++ { 0x21, 0x16 }, // 0x03, ++ } ++}, { ++ { /* cvbs 5 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0xB8 }, ++ { 0x21, 0x07 }, // 0x05, ++ { 0x22, 0x91 }, ++ { 0x2C, 0x03 }, ++ { 0x30, 0x60 }, ++ { 0x31, 0xB5 }, // ?? ++ { 0x21, 0x05 }, // 0x03, ++ } ++}, { ++ { /* cvbs 6 */ ++ { 0x06, 0x00 }, ++ { 0x20, 0x7C }, ++ { 0x21, 0x07 }, // 0x03 ++ { 0x22, 0xD2 }, ++ { 0x2C, 0x83 }, ++ { 0x30, 0x60 }, ++ { 0x31, 0xB5 }, // ?? ++ { 0x21, 0x03 }, ++ } ++}, { ++ { /* y/c 1 */ ++ { 0x06, 0x80 }, ++ { 0x20, 0x59 }, ++ { 0x21, 0x17 }, ++ { 0x22, 0x42 }, ++ { 0x2C, 0xA3 }, ++ { 0x30, 0x44 }, ++ { 0x31, 0x75 }, ++ { 0x21, 0x12 }, ++ } ++}, { ++ { /* y/c 2 */ ++ { 0x06, 0x80 }, ++ { 0x20, 0x9A }, ++ { 0x21, 0x17 }, ++ { 0x22, 0xB1 }, ++ { 0x2C, 0x13 }, ++ { 0x30, 0x60 }, ++ { 0x31, 0xB5 }, ++ { 0x21, 0x14 }, ++ } ++}, { ++ { /* y/c 3 */ ++ { 0x06, 0x80 }, ++ { 0x20, 0x3C }, ++ { 0x21, 0x27 }, ++ { 0x22, 0xC1 }, ++ { 0x2C, 0x23 }, ++ { 0x30, 0x44 }, ++ { 0x31, 0x75 }, ++ { 0x21, 0x21 }, ++ } ++} ++}; ++ ++static struct saa7146_standard hexium_standards[] = { ++ { ++ .name = "PAL", .id = V4L2_STD_PAL, ++ .v_offset = 16, .v_field = 288, ++ .h_offset = 1, .h_pixels = 680, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 16, .v_field = 240, ++ .h_offset = 1, .h_pixels = 640, ++ .v_max_out = 480, .h_max_out = 640, ++ }, { ++ .name = "SECAM", .id = V4L2_STD_SECAM, ++ .v_offset = 16, .v_field = 288, ++ .h_offset = 1, .h_pixels = 720, ++ .v_max_out = 576, .h_max_out = 768, ++ } ++}; ++ ++/* this is only called for old HV-PCI6/Orion cards ++ without eeprom */ ++static int hexium_probe(struct saa7146_dev *dev) ++{ ++ struct hexium *hexium = NULL; ++ union i2c_smbus_data data; ++ int err = 0; ++ ++ DEB_EE("\n"); ++ ++ /* there are no hexium orion cards with revision 0 saa7146s */ ++ if (0 == dev->revision) { ++ return -EFAULT; ++ } ++ ++ hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); ++ if (NULL == hexium) { ++ pr_err("hexium_probe: not enough kernel memory\n"); ++ return -ENOMEM; ++ } ++ ++ /* enable i2c-port pins */ ++ saa7146_write(dev, MC1, (MASK_08 | MASK_24 | MASK_10 | MASK_26)); ++ ++ saa7146_write(dev, DD1_INIT, 0x01000100); ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ hexium->i2c_adapter = (struct i2c_adapter) { ++ .name = "hexium orion", ++ }; ++ saa7146_i2c_adapter_prepare(dev, &hexium->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); ++ if (i2c_add_adapter(&hexium->i2c_adapter) < 0) { ++ DEB_S("cannot register i2c-device. skipping.\n"); ++ kfree(hexium); ++ return -EFAULT; ++ } ++ ++ /* set SAA7110 control GPIO 0 */ ++ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTHI); ++ /* set HWControl GPIO number 2 */ ++ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); ++ ++ mdelay(10); ++ ++ /* detect newer Hexium Orion cards by subsystem ids */ ++ if (0x17c8 == dev->pci->subsystem_vendor && 0x0101 == dev->pci->subsystem_device) { ++ pr_info("device is a Hexium Orion w/ 1 SVHS + 3 BNC inputs\n"); ++ /* we store the pointer in our private data field */ ++ dev->ext_priv = hexium; ++ hexium->type = HEXIUM_ORION_1SVHS_3BNC; ++ return 0; ++ } ++ ++ if (0x17c8 == dev->pci->subsystem_vendor && 0x2101 == dev->pci->subsystem_device) { ++ pr_info("device is a Hexium Orion w/ 4 BNC inputs\n"); ++ /* we store the pointer in our private data field */ ++ dev->ext_priv = hexium; ++ hexium->type = HEXIUM_ORION_4BNC; ++ return 0; ++ } ++ ++ /* check if this is an old hexium Orion card by looking at ++ a saa7110 at address 0x4e */ ++ if (0 == (err = i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_READ, 0x00, I2C_SMBUS_BYTE_DATA, &data))) { ++ pr_info("device is a Hexium HV-PCI6/Orion (old)\n"); ++ /* we store the pointer in our private data field */ ++ dev->ext_priv = hexium; ++ hexium->type = HEXIUM_HV_PCI6_ORION; ++ return 0; ++ } ++ ++ i2c_del_adapter(&hexium->i2c_adapter); ++ kfree(hexium); ++ return -EFAULT; ++} ++ ++/* bring hardware to a sane state. this has to be done, just in case someone ++ wants to capture from this device before it has been properly initialized. ++ the capture engine would badly fail, because no valid signal arrives on the ++ saa7146, thus leading to timeouts and stuff. */ ++static int hexium_init_done(struct saa7146_dev *dev) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ union i2c_smbus_data data; ++ int i = 0; ++ ++ DEB_D("hexium_init_done called\n"); ++ ++ /* initialize the helper ics to useful values */ ++ for (i = 0; i < sizeof(hexium_saa7110); i++) { ++ data.byte = hexium_saa7110[i]; ++ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, i, I2C_SMBUS_BYTE_DATA, &data)) { ++ pr_err("failed for address 0x%02x\n", i); ++ } ++ } ++ ++ return 0; ++} ++ ++static int hexium_set_input(struct hexium *hexium, int input) ++{ ++ union i2c_smbus_data data; ++ int i = 0; ++ ++ DEB_D("\n"); ++ ++ for (i = 0; i < 8; i++) { ++ int adr = hexium_input_select[input].data[i].adr; ++ data.byte = hexium_input_select[input].data[i].byte; ++ if (0 != i2c_smbus_xfer(&hexium->i2c_adapter, 0x4e, 0, I2C_SMBUS_WRITE, adr, I2C_SMBUS_BYTE_DATA, &data)) { ++ return -1; ++ } ++ pr_debug("%d: 0x%02x => 0x%02x\n", input, adr, data.byte); ++ } ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); ++ ++ if (i->index >= HEXIUM_INPUTS) ++ return -EINVAL; ++ ++ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); ++ ++ DEB_D("v4l2_ioctl: VIDIOC_ENUMINPUT %d\n", i->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ *input = hexium->cur_input; ++ ++ DEB_D("VIDIOC_G_INPUT: %d\n", *input); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ if (input >= HEXIUM_INPUTS) ++ return -EINVAL; ++ ++ hexium->cur_input = input; ++ hexium_set_input(hexium, input); ++ ++ return 0; ++} ++ ++static struct saa7146_ext_vv vv_data; ++ ++/* this function only gets called when the probing was successful */ ++static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ DEB_EE("\n"); ++ ++ saa7146_vv_init(dev, &vv_data); ++ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; ++ vv_data.vid_ops.vidioc_g_input = vidioc_g_input; ++ vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ++ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { ++ pr_err("cannot register capture v4l2 device. skipping.\n"); ++ return -1; ++ } ++ ++ pr_err("found 'hexium orion' frame grabber-%d\n", hexium_num); ++ hexium_num++; ++ ++ /* the rest */ ++ hexium->cur_input = 0; ++ hexium_init_done(dev); ++ ++ return 0; ++} ++ ++static int hexium_detach(struct saa7146_dev *dev) ++{ ++ struct hexium *hexium = (struct hexium *) dev->ext_priv; ++ ++ DEB_EE("dev:%p\n", dev); ++ ++ saa7146_unregister_device(&hexium->video_dev, dev); ++ saa7146_vv_release(dev); ++ ++ hexium_num--; ++ ++ i2c_del_adapter(&hexium->i2c_adapter); ++ kfree(hexium); ++ return 0; ++} ++ ++static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) ++{ ++ return 0; ++} ++ ++static struct saa7146_extension extension; ++ ++static struct saa7146_pci_extension_data hexium_hv_pci6 = { ++ .ext_priv = "Hexium HV-PCI6 / Orion", ++ .ext = &extension, ++}; ++ ++static struct saa7146_pci_extension_data hexium_orion_1svhs_3bnc = { ++ .ext_priv = "Hexium HV-PCI6 / Orion (1 SVHS/3 BNC)", ++ .ext = &extension, ++}; ++ ++static struct saa7146_pci_extension_data hexium_orion_4bnc = { ++ .ext_priv = "Hexium HV-PCI6 / Orion (4 BNC)", ++ .ext = &extension, ++}; ++ ++static struct pci_device_id pci_tbl[] = { ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x0000, ++ .subdevice = 0x0000, ++ .driver_data = (unsigned long) &hexium_hv_pci6, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x17c8, ++ .subdevice = 0x0101, ++ .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x17c8, ++ .subdevice = 0x2101, ++ .driver_data = (unsigned long) &hexium_orion_4bnc, ++ }, ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_ext_vv vv_data = { ++ .inputs = HEXIUM_INPUTS, ++ .capabilities = 0, ++ .stds = &hexium_standards[0], ++ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard), ++ .std_callback = &std_callback, ++}; ++ ++static struct saa7146_extension extension = { ++ .name = "hexium HV-PCI6 Orion", ++ .flags = 0, // SAA7146_USE_I2C_IRQ, ++ ++ .pci_tbl = &pci_tbl[0], ++ .module = THIS_MODULE, ++ ++ .probe = hexium_probe, ++ .attach = hexium_attach, ++ .detach = hexium_detach, ++ ++ .irq_mask = 0, ++ .irq_func = NULL, ++}; ++ ++static int __init hexium_init_module(void) ++{ ++ if (0 != saa7146_register_extension(&extension)) { ++ DEB_S("failed to register extension\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void __exit hexium_cleanup_module(void) ++{ ++ saa7146_unregister_extension(&extension); ++} ++ ++module_init(hexium_init_module); ++module_exit(hexium_cleanup_module); ++ ++MODULE_DESCRIPTION("video4linux-2 driver for Hexium Orion frame grabber cards"); ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c +new file mode 100644 +index 0000000..34df430 +--- /dev/null ++++ b/drivers/media/pci/saa7146/mxb.c +@@ -0,0 +1,887 @@ ++/* ++ mxb - v4l2 driver for the Multimedia eXtension Board ++ ++ Copyright (C) 1998-2006 Michael Hunold ++ ++ Visit http://www.themm.net/~mihu/linux/saa7146/mxb.html ++ for further details about this card. ++ ++ 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. ++*/ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#define DEBUG_VARIABLE debug ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "tea6415c.h" ++#include "tea6420.h" ++ ++#define MXB_AUDIOS 6 ++ ++#define I2C_SAA7111A 0x24 ++#define I2C_TDA9840 0x42 ++#define I2C_TEA6415C 0x43 ++#define I2C_TEA6420_1 0x4c ++#define I2C_TEA6420_2 0x4d ++#define I2C_TUNER 0x60 ++ ++#define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) ++ ++/* global variable */ ++static int mxb_num; ++ ++/* initial frequence the tuner will be tuned to. ++ in verden (lower saxony, germany) 4148 is a ++ channel called "phoenix" */ ++static int freq = 4148; ++module_param(freq, int, 0644); ++MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); ++ ++#define MXB_INPUTS 4 ++enum { TUNER, AUX1, AUX3, AUX3_YC }; ++ ++static struct v4l2_input mxb_inputs[MXB_INPUTS] = { ++ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, ++ V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, ++ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, ++ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, ++ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, ++ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, ++}; ++ ++/* this array holds the information, which port of the saa7146 each ++ input actually uses. the mxb uses port 0 for every input */ ++static struct { ++ int hps_source; ++ int hps_sync; ++} input_port_selection[MXB_INPUTS] = { ++ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, ++ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, ++ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, ++ { SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A }, ++}; ++ ++/* this array holds the information of the audio source (mxb_audios), ++ which has to be switched corresponding to the video source (mxb_channels) */ ++static int video_audio_connect[MXB_INPUTS] = ++ { 0, 1, 3, 3 }; ++ ++struct mxb_routing { ++ u32 input; ++ u32 output; ++}; ++ ++/* these are the available audio sources, which can switched ++ to the line- and cd-output individually */ ++static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { ++ { ++ .index = 0, ++ .name = "Tuner", ++ .capability = V4L2_AUDCAP_STEREO, ++ } , { ++ .index = 1, ++ .name = "AUX1", ++ .capability = V4L2_AUDCAP_STEREO, ++ } , { ++ .index = 2, ++ .name = "AUX2", ++ .capability = V4L2_AUDCAP_STEREO, ++ } , { ++ .index = 3, ++ .name = "AUX3", ++ .capability = V4L2_AUDCAP_STEREO, ++ } , { ++ .index = 4, ++ .name = "Radio (X9)", ++ .capability = V4L2_AUDCAP_STEREO, ++ } , { ++ .index = 5, ++ .name = "CD-ROM (X10)", ++ .capability = V4L2_AUDCAP_STEREO, ++ } ++}; ++ ++/* These are the necessary input-output-pins for bringing one audio source ++ (see above) to the CD-output. Note that gain is set to 0 in this table. */ ++static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { ++ { { 1, 1 }, { 1, 1 } }, /* Tuner */ ++ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ ++ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ ++ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ ++ { { 1, 1 }, { 3, 1 } }, /* Radio */ ++ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ ++ { { 6, 1 }, { 6, 1 } } /* Mute */ ++}; ++ ++/* These are the necessary input-output-pins for bringing one audio source ++ (see above) to the line-output. Note that gain is set to 0 in this table. */ ++static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { ++ { { 2, 3 }, { 1, 2 } }, ++ { { 5, 3 }, { 6, 2 } }, ++ { { 4, 3 }, { 6, 2 } }, ++ { { 3, 3 }, { 6, 2 } }, ++ { { 2, 3 }, { 3, 2 } }, ++ { { 2, 3 }, { 2, 2 } }, ++ { { 6, 3 }, { 6, 2 } } /* Mute */ ++}; ++ ++struct mxb ++{ ++ struct video_device *video_dev; ++ struct video_device *vbi_dev; ++ ++ struct i2c_adapter i2c_adapter; ++ ++ struct v4l2_subdev *saa7111a; ++ struct v4l2_subdev *tda9840; ++ struct v4l2_subdev *tea6415c; ++ struct v4l2_subdev *tuner; ++ struct v4l2_subdev *tea6420_1; ++ struct v4l2_subdev *tea6420_2; ++ ++ int cur_mode; /* current audio mode (mono, stereo, ...) */ ++ int cur_input; /* current input */ ++ int cur_audinput; /* current audio input */ ++ int cur_mute; /* current mute status */ ++ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ ++}; ++ ++#define saa7111a_call(mxb, o, f, args...) \ ++ v4l2_subdev_call(mxb->saa7111a, o, f, ##args) ++#define tda9840_call(mxb, o, f, args...) \ ++ v4l2_subdev_call(mxb->tda9840, o, f, ##args) ++#define tea6415c_call(mxb, o, f, args...) \ ++ v4l2_subdev_call(mxb->tea6415c, o, f, ##args) ++#define tuner_call(mxb, o, f, args...) \ ++ v4l2_subdev_call(mxb->tuner, o, f, ##args) ++#define call_all(dev, o, f, args...) \ ++ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) ++ ++static void mxb_update_audmode(struct mxb *mxb) ++{ ++ struct v4l2_tuner t = { ++ .audmode = mxb->cur_mode, ++ }; ++ ++ tda9840_call(mxb, tuner, s_tuner, &t); ++} ++ ++static inline void tea6420_route(struct mxb *mxb, int idx) ++{ ++ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, ++ TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); ++ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, ++ TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); ++ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, ++ TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); ++ v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, ++ TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); ++} ++ ++static struct saa7146_extension extension; ++ ++static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct saa7146_dev *dev = container_of(ctrl->handler, ++ struct saa7146_dev, ctrl_handler); ++ struct mxb *mxb = dev->ext_priv; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUDIO_MUTE: ++ mxb->cur_mute = ctrl->val; ++ /* switch the audio-source */ ++ tea6420_route(mxb, ctrl->val ? 6 : ++ video_audio_connect[mxb->cur_input]); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops mxb_ctrl_ops = { ++ .s_ctrl = mxb_s_ctrl, ++}; ++ ++static int mxb_probe(struct saa7146_dev *dev) ++{ ++ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; ++ struct mxb *mxb = NULL; ++ ++ v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, ++ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); ++ if (hdl->error) ++ return hdl->error; ++ mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); ++ if (mxb == NULL) { ++ DEB_D("not enough kernel memory\n"); ++ return -ENOMEM; ++ } ++ ++ ++ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); ++ ++ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); ++ if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { ++ DEB_S("cannot register i2c-device. skipping.\n"); ++ kfree(mxb); ++ return -EFAULT; ++ } ++ ++ mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "saa7111", I2C_SAA7111A, NULL); ++ mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "tea6420", I2C_TEA6420_1, NULL); ++ mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "tea6420", I2C_TEA6420_2, NULL); ++ mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "tea6415c", I2C_TEA6415C, NULL); ++ mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "tda9840", I2C_TDA9840, NULL); ++ mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, ++ "tuner", I2C_TUNER, NULL); ++ ++ /* check if all devices are present */ ++ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || ++ !mxb->tda9840 || !mxb->saa7111a || !mxb->tuner) { ++ pr_err("did not find all i2c devices. aborting\n"); ++ i2c_del_adapter(&mxb->i2c_adapter); ++ kfree(mxb); ++ return -ENODEV; ++ } ++ ++ /* all devices are present, probe was successful */ ++ ++ /* we store the pointer in our private data field */ ++ dev->ext_priv = mxb; ++ ++ v4l2_ctrl_handler_setup(hdl); ++ ++ return 0; ++} ++ ++/* some init data for the saa7740, the so-called 'sound arena module'. ++ there are no specs available, so we simply use some init values */ ++static struct { ++ int length; ++ char data[9]; ++} mxb_saa7740_init[] = { ++ { 3, { 0x80, 0x00, 0x00 } },{ 3, { 0x80, 0x89, 0x00 } }, ++ { 3, { 0x80, 0xb0, 0x0a } },{ 3, { 0x00, 0x00, 0x00 } }, ++ { 3, { 0x49, 0x00, 0x00 } },{ 3, { 0x4a, 0x00, 0x00 } }, ++ { 3, { 0x4b, 0x00, 0x00 } },{ 3, { 0x4c, 0x00, 0x00 } }, ++ { 3, { 0x4d, 0x00, 0x00 } },{ 3, { 0x4e, 0x00, 0x00 } }, ++ { 3, { 0x4f, 0x00, 0x00 } },{ 3, { 0x50, 0x00, 0x00 } }, ++ { 3, { 0x51, 0x00, 0x00 } },{ 3, { 0x52, 0x00, 0x00 } }, ++ { 3, { 0x53, 0x00, 0x00 } },{ 3, { 0x54, 0x00, 0x00 } }, ++ { 3, { 0x55, 0x00, 0x00 } },{ 3, { 0x56, 0x00, 0x00 } }, ++ { 3, { 0x57, 0x00, 0x00 } },{ 3, { 0x58, 0x00, 0x00 } }, ++ { 3, { 0x59, 0x00, 0x00 } },{ 3, { 0x5a, 0x00, 0x00 } }, ++ { 3, { 0x5b, 0x00, 0x00 } },{ 3, { 0x5c, 0x00, 0x00 } }, ++ { 3, { 0x5d, 0x00, 0x00 } },{ 3, { 0x5e, 0x00, 0x00 } }, ++ { 3, { 0x5f, 0x00, 0x00 } },{ 3, { 0x60, 0x00, 0x00 } }, ++ { 3, { 0x61, 0x00, 0x00 } },{ 3, { 0x62, 0x00, 0x00 } }, ++ { 3, { 0x63, 0x00, 0x00 } },{ 3, { 0x64, 0x00, 0x00 } }, ++ { 3, { 0x65, 0x00, 0x00 } },{ 3, { 0x66, 0x00, 0x00 } }, ++ { 3, { 0x67, 0x00, 0x00 } },{ 3, { 0x68, 0x00, 0x00 } }, ++ { 3, { 0x69, 0x00, 0x00 } },{ 3, { 0x6a, 0x00, 0x00 } }, ++ { 3, { 0x6b, 0x00, 0x00 } },{ 3, { 0x6c, 0x00, 0x00 } }, ++ { 3, { 0x6d, 0x00, 0x00 } },{ 3, { 0x6e, 0x00, 0x00 } }, ++ { 3, { 0x6f, 0x00, 0x00 } },{ 3, { 0x70, 0x00, 0x00 } }, ++ { 3, { 0x71, 0x00, 0x00 } },{ 3, { 0x72, 0x00, 0x00 } }, ++ { 3, { 0x73, 0x00, 0x00 } },{ 3, { 0x74, 0x00, 0x00 } }, ++ { 3, { 0x75, 0x00, 0x00 } },{ 3, { 0x76, 0x00, 0x00 } }, ++ { 3, { 0x77, 0x00, 0x00 } },{ 3, { 0x41, 0x00, 0x42 } }, ++ { 3, { 0x42, 0x10, 0x42 } },{ 3, { 0x43, 0x20, 0x42 } }, ++ { 3, { 0x44, 0x30, 0x42 } },{ 3, { 0x45, 0x00, 0x01 } }, ++ { 3, { 0x46, 0x00, 0x01 } },{ 3, { 0x47, 0x00, 0x01 } }, ++ { 3, { 0x48, 0x00, 0x01 } }, ++ { 9, { 0x01, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, ++ { 9, { 0x21, 0x03, 0xc5, 0x5c, 0x7a, 0x85, 0x01, 0x00, 0x54 } }, ++ { 9, { 0x09, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, ++ { 9, { 0x29, 0x0b, 0xb4, 0x6b, 0x74, 0x85, 0x95, 0x00, 0x34 } }, ++ { 9, { 0x11, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, ++ { 9, { 0x31, 0x17, 0x43, 0x62, 0x68, 0x89, 0xd1, 0xff, 0xb0 } }, ++ { 9, { 0x19, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, ++ { 9, { 0x39, 0x20, 0x62, 0x51, 0x5a, 0x95, 0x19, 0x01, 0x50 } }, ++ { 9, { 0x05, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, ++ { 9, { 0x25, 0x3e, 0xd2, 0x69, 0x4e, 0x9a, 0x51, 0x00, 0xf0 } }, ++ { 9, { 0x0d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, ++ { 9, { 0x2d, 0x3d, 0xa1, 0x40, 0x7d, 0x9f, 0x29, 0xfe, 0x14 } }, ++ { 9, { 0x15, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, ++ { 9, { 0x35, 0x73, 0xa1, 0x50, 0x5d, 0xa6, 0xf5, 0xfe, 0x38 } }, ++ { 9, { 0x1d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, ++ { 9, { 0x3d, 0xed, 0xd0, 0x68, 0x29, 0xb4, 0xe1, 0x00, 0xb8 } }, ++ { 3, { 0x80, 0xb3, 0x0a } }, ++ {-1, { 0 } } ++}; ++ ++/* bring hardware to a sane state. this has to be done, just in case someone ++ wants to capture from this device before it has been properly initialized. ++ the capture engine would badly fail, because no valid signal arrives on the ++ saa7146, thus leading to timeouts and stuff. */ ++static int mxb_init_done(struct saa7146_dev* dev) ++{ ++ struct mxb* mxb = (struct mxb*)dev->ext_priv; ++ struct i2c_msg msg; ++ struct tuner_setup tun_setup; ++ v4l2_std_id std = V4L2_STD_PAL_BG; ++ ++ int i = 0, err = 0; ++ ++ /* mute audio on tea6420s */ ++ tea6420_route(mxb, 6); ++ ++ /* select video mode in saa7111a */ ++ saa7111a_call(mxb, core, s_std, std); ++ ++ /* select tuner-output on saa7111a */ ++ i = 0; ++ saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, ++ SAA7111_FMT_CCIR, 0); ++ ++ /* select a tuner type */ ++ tun_setup.mode_mask = T_ANALOG_TV; ++ tun_setup.addr = ADDR_UNSET; ++ tun_setup.type = TUNER_PHILIPS_PAL; ++ tuner_call(mxb, tuner, s_type_addr, &tun_setup); ++ /* tune in some frequency on tuner */ ++ mxb->cur_freq.tuner = 0; ++ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; ++ mxb->cur_freq.frequency = freq; ++ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); ++ ++ /* set a default video standard */ ++ /* These two gpio calls set the GPIO pins that control the tda9820 */ ++ saa7146_write(dev, GPIO_CTRL, 0x00404050); ++ saa7111a_call(mxb, core, s_gpio, 1); ++ saa7111a_call(mxb, core, s_std, std); ++ tuner_call(mxb, core, s_std, std); ++ ++ /* switch to tuner-channel on tea6415c */ ++ tea6415c_call(mxb, video, s_routing, 3, 17, 0); ++ ++ /* select tuner-output on multicable on tea6415c */ ++ tea6415c_call(mxb, video, s_routing, 3, 13, 0); ++ ++ /* the rest for mxb */ ++ mxb->cur_input = 0; ++ mxb->cur_audinput = video_audio_connect[mxb->cur_input]; ++ mxb->cur_mute = 1; ++ ++ mxb->cur_mode = V4L2_TUNER_MODE_STEREO; ++ mxb_update_audmode(mxb); ++ ++ /* check if the saa7740 (aka 'sound arena module') is present ++ on the mxb. if so, we must initialize it. due to lack of ++ informations about the saa7740, the values were reverse ++ engineered. */ ++ msg.addr = 0x1b; ++ msg.flags = 0; ++ msg.len = mxb_saa7740_init[0].length; ++ msg.buf = &mxb_saa7740_init[0].data[0]; ++ ++ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); ++ if (err == 1) { ++ /* the sound arena module is a pos, that's probably the reason ++ philips refuses to hand out a datasheet for the saa7740... ++ it seems to screw up the i2c bus, so we disable fast irq ++ based i2c transactions here and rely on the slow and safe ++ polling method ... */ ++ extension.flags &= ~SAA7146_USE_I2C_IRQ; ++ for (i = 1; ; i++) { ++ if (-1 == mxb_saa7740_init[i].length) ++ break; ++ ++ msg.len = mxb_saa7740_init[i].length; ++ msg.buf = &mxb_saa7740_init[i].data[0]; ++ err = i2c_transfer(&mxb->i2c_adapter, &msg, 1); ++ if (err != 1) { ++ DEB_D("failed to initialize 'sound arena module'\n"); ++ goto err; ++ } ++ } ++ pr_info("'sound arena module' detected\n"); ++ } ++err: ++ /* the rest for saa7146: you should definitely set some basic values ++ for the input-port handling of the saa7146. */ ++ ++ /* ext->saa has been filled by the core driver */ ++ ++ /* some stuff is done via variables */ ++ saa7146_set_hps_source_and_sync(dev, input_port_selection[mxb->cur_input].hps_source, ++ input_port_selection[mxb->cur_input].hps_sync); ++ ++ /* some stuff is done via direct write to the registers */ ++ ++ /* this is ugly, but because of the fact that this is completely ++ hardware dependend, it should be done directly... */ ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, DD1_INIT, 0x02000200); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ return 0; ++} ++ ++/* interrupt-handler. this gets called when irq_mask is != 0. ++ it must clear the interrupt-bits in irq_mask it has handled */ ++/* ++void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) ++{ ++ struct mxb* mxb = (struct mxb*)dev->ext_priv; ++} ++*/ ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); ++ if (i->index >= MXB_INPUTS) ++ return -EINVAL; ++ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ *i = mxb->cur_input; ++ ++ DEB_EE("VIDIOC_G_INPUT %d\n", *i); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ int err = 0; ++ int i = 0; ++ ++ DEB_EE("VIDIOC_S_INPUT %d\n", input); ++ ++ if (input >= MXB_INPUTS) ++ return -EINVAL; ++ ++ mxb->cur_input = input; ++ ++ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, ++ input_port_selection[input].hps_sync); ++ ++ /* prepare switching of tea6415c and saa7111a; ++ have a look at the 'background'-file for further informations */ ++ switch (input) { ++ case TUNER: ++ i = SAA7115_COMPOSITE0; ++ ++ err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); ++ ++ /* connect tuner-output always to multicable */ ++ if (!err) ++ err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); ++ break; ++ case AUX3_YC: ++ /* nothing to be done here. aux3_yc is ++ directly connected to the saa711a */ ++ i = SAA7115_SVIDEO1; ++ break; ++ case AUX3: ++ /* nothing to be done here. aux3 is ++ directly connected to the saa711a */ ++ i = SAA7115_COMPOSITE1; ++ break; ++ case AUX1: ++ i = SAA7115_COMPOSITE0; ++ err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); ++ break; ++ } ++ ++ if (err) ++ return err; ++ ++ /* switch video in saa7111a */ ++ if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) ++ pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); ++ ++ mxb->cur_audinput = video_audio_connect[input]; ++ /* switch the audio-source only if necessary */ ++ if (0 == mxb->cur_mute) ++ tea6420_route(mxb, mxb->cur_audinput); ++ if (mxb->cur_audinput == 0) ++ mxb_update_audmode(mxb); ++ ++ return 0; ++} ++ ++static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ if (t->index) { ++ DEB_D("VIDIOC_G_TUNER: channel %d does not have a tuner attached\n", ++ t->index); ++ return -EINVAL; ++ } ++ ++ DEB_EE("VIDIOC_G_TUNER: %d\n", t->index); ++ ++ memset(t, 0, sizeof(*t)); ++ strlcpy(t->name, "TV Tuner", sizeof(t->name)); ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; ++ t->audmode = mxb->cur_mode; ++ return call_all(dev, tuner, g_tuner, t); ++} ++ ++static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ if (t->index) { ++ DEB_D("VIDIOC_S_TUNER: channel %d does not have a tuner attached\n", ++ t->index); ++ return -EINVAL; ++ } ++ ++ mxb->cur_mode = t->audmode; ++ return call_all(dev, tuner, s_tuner, t); ++} ++ ++static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ ++ return call_all(dev, video, querystd, norm); ++} ++ ++static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ if (f->tuner) ++ return -EINVAL; ++ *f = mxb->cur_freq; ++ ++ DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); ++ return 0; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ struct saa7146_vv *vv = dev->vv_data; ++ ++ if (f->tuner) ++ return -EINVAL; ++ ++ if (V4L2_TUNER_ANALOG_TV != f->type) ++ return -EINVAL; ++ ++ DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); ++ ++ /* tune in desired frequency */ ++ tuner_call(mxb, tuner, s_frequency, f); ++ /* let the tuner subdev clamp the frequency to the tuner range */ ++ tuner_call(mxb, tuner, g_frequency, f); ++ mxb->cur_freq = *f; ++ if (mxb->cur_audinput == 0) ++ mxb_update_audmode(mxb); ++ ++ if (mxb->cur_input) ++ return 0; ++ ++ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ ++ spin_lock(&dev->slock); ++ vv->vbi_fieldcount = 0; ++ spin_unlock(&dev->slock); ++ ++ return 0; ++} ++ ++static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) ++{ ++ if (a->index >= MXB_AUDIOS) ++ return -EINVAL; ++ *a = mxb_audios[a->index]; ++ return 0; ++} ++ ++static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ DEB_EE("VIDIOC_G_AUDIO\n"); ++ *a = mxb_audios[mxb->cur_audinput]; ++ return 0; ++} ++ ++static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ DEB_D("VIDIOC_S_AUDIO %d\n", a->index); ++ if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) { ++ if (mxb->cur_audinput != a->index) { ++ mxb->cur_audinput = a->index; ++ tea6420_route(mxb, a->index); ++ if (mxb->cur_audinput == 0) ++ mxb_update_audmode(mxb); ++ } ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (v4l2_chip_match_host(®->match)) { ++ reg->val = saa7146_read(dev, reg->reg); ++ reg->size = 4; ++ return 0; ++ } ++ call_all(dev, core, g_register, reg); ++ return 0; ++} ++ ++static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ if (v4l2_chip_match_host(®->match)) { ++ saa7146_write(dev, reg->reg, reg->val); ++ reg->size = 4; ++ return 0; ++ } ++ return call_all(dev, core, s_register, reg); ++} ++#endif ++ ++static struct saa7146_ext_vv vv_data; ++ ++/* this function only gets called when the probing was successful */ ++static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) ++{ ++ struct mxb *mxb; ++ ++ DEB_EE("dev:%p\n", dev); ++ ++ saa7146_vv_init(dev, &vv_data); ++ if (mxb_probe(dev)) { ++ saa7146_vv_release(dev); ++ return -1; ++ } ++ mxb = (struct mxb *)dev->ext_priv; ++ ++ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; ++ vv_data.vid_ops.vidioc_g_input = vidioc_g_input; ++ vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ++ vv_data.vid_ops.vidioc_querystd = vidioc_querystd; ++ vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; ++ vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; ++ vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; ++ vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; ++ vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; ++ vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; ++ vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ vv_data.vid_ops.vidioc_g_register = vidioc_g_register; ++ vv_data.vid_ops.vidioc_s_register = vidioc_s_register; ++#endif ++ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ++ ERR("cannot register capture v4l2 device. skipping.\n"); ++ saa7146_vv_release(dev); ++ return -1; ++ } ++ ++ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ ++ if (MXB_BOARD_CAN_DO_VBI(dev)) { ++ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { ++ ERR("cannot register vbi v4l2 device. skipping.\n"); ++ } ++ } ++ ++ pr_info("found Multimedia eXtension Board #%d\n", mxb_num); ++ ++ mxb_num++; ++ mxb_init_done(dev); ++ return 0; ++} ++ ++static int mxb_detach(struct saa7146_dev *dev) ++{ ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ DEB_EE("dev:%p\n", dev); ++ ++ /* mute audio on tea6420s */ ++ tea6420_route(mxb, 6); ++ ++ saa7146_unregister_device(&mxb->video_dev,dev); ++ if (MXB_BOARD_CAN_DO_VBI(dev)) ++ saa7146_unregister_device(&mxb->vbi_dev, dev); ++ saa7146_vv_release(dev); ++ ++ mxb_num--; ++ ++ i2c_del_adapter(&mxb->i2c_adapter); ++ kfree(mxb); ++ ++ return 0; ++} ++ ++static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) ++{ ++ struct mxb *mxb = (struct mxb *)dev->ext_priv; ++ ++ if (V4L2_STD_PAL_I == standard->id) { ++ v4l2_std_id std = V4L2_STD_PAL_I; ++ ++ DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); ++ /* These two gpio calls set the GPIO pins that control the tda9820 */ ++ saa7146_write(dev, GPIO_CTRL, 0x00404050); ++ saa7111a_call(mxb, core, s_gpio, 0); ++ saa7111a_call(mxb, core, s_std, std); ++ if (mxb->cur_input == 0) ++ tuner_call(mxb, core, s_std, std); ++ } else { ++ v4l2_std_id std = V4L2_STD_PAL_BG; ++ ++ if (mxb->cur_input) ++ std = standard->id; ++ DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); ++ /* These two gpio calls set the GPIO pins that control the tda9820 */ ++ saa7146_write(dev, GPIO_CTRL, 0x00404050); ++ saa7111a_call(mxb, core, s_gpio, 1); ++ saa7111a_call(mxb, core, s_std, std); ++ if (mxb->cur_input == 0) ++ tuner_call(mxb, core, s_std, std); ++ } ++ return 0; ++} ++ ++static struct saa7146_standard standard[] = { ++ { ++ .name = "PAL-BG", .id = V4L2_STD_PAL_BG, ++ .v_offset = 0x17, .v_field = 288, ++ .h_offset = 0x14, .h_pixels = 680, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "PAL-I", .id = V4L2_STD_PAL_I, ++ .v_offset = 0x17, .v_field = 288, ++ .h_offset = 0x14, .h_pixels = 680, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 0x16, .v_field = 240, ++ .h_offset = 0x06, .h_pixels = 708, ++ .v_max_out = 480, .h_max_out = 640, ++ }, { ++ .name = "SECAM", .id = V4L2_STD_SECAM, ++ .v_offset = 0x14, .v_field = 288, ++ .h_offset = 0x14, .h_pixels = 720, ++ .v_max_out = 576, .h_max_out = 768, ++ } ++}; ++ ++static struct saa7146_pci_extension_data mxb = { ++ .ext_priv = "Multimedia eXtension Board", ++ .ext = &extension, ++}; ++ ++static struct pci_device_id pci_tbl[] = { ++ { ++ .vendor = PCI_VENDOR_ID_PHILIPS, ++ .device = PCI_DEVICE_ID_PHILIPS_SAA7146, ++ .subvendor = 0x0000, ++ .subdevice = 0x0000, ++ .driver_data = (unsigned long)&mxb, ++ }, { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_ext_vv vv_data = { ++ .inputs = MXB_INPUTS, ++ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, ++ .stds = &standard[0], ++ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), ++ .std_callback = &std_callback, ++}; ++ ++static struct saa7146_extension extension = { ++ .name = "Multimedia eXtension Board", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .pci_tbl = &pci_tbl[0], ++ .module = THIS_MODULE, ++ ++ .attach = mxb_attach, ++ .detach = mxb_detach, ++ ++ .irq_mask = 0, ++ .irq_func = NULL, ++}; ++ ++static int __init mxb_init_module(void) ++{ ++ if (saa7146_register_extension(&extension)) { ++ DEB_S("failed to register extension\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static void __exit mxb_cleanup_module(void) ++{ ++ saa7146_unregister_extension(&extension); ++} ++ ++module_init(mxb_init_module); ++module_exit(mxb_cleanup_module); ++ ++MODULE_DESCRIPTION("video4linux-2 driver for the Siemens-Nixdorf 'Multimedia eXtension board'"); ++MODULE_AUTHOR("Michael Hunold "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/saa7164/Kconfig b/drivers/media/pci/saa7164/Kconfig +new file mode 100644 +index 0000000..a53db7d +--- /dev/null ++++ b/drivers/media/pci/saa7164/Kconfig +@@ -0,0 +1,18 @@ ++config VIDEO_SAA7164 ++ tristate "NXP SAA7164 support" ++ depends on DVB_CORE && VIDEO_DEV && PCI && I2C ++ select I2C_ALGOBIT ++ select FW_LOADER ++ select VIDEO_TUNER ++ select VIDEO_TVEEPROM ++ select VIDEOBUF_DVB ++ select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1411 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT ++ ---help--- ++ This is a video4linux driver for NXP SAA7164 based ++ TV cards. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called saa7164 ++ +diff --git a/drivers/media/pci/saa7164/Makefile b/drivers/media/pci/saa7164/Makefile +new file mode 100644 +index 0000000..ba0e33a +--- /dev/null ++++ b/drivers/media/pci/saa7164/Makefile +@@ -0,0 +1,12 @@ ++saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \ ++ saa7164-fw.o saa7164-bus.o saa7164-cmd.o saa7164-api.o \ ++ saa7164-buffer.o saa7164-encoder.o saa7164-vbi.o ++ ++obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o ++ ++ccflags-y += -I$(srctree)/drivers/media/i2c ++ccflags-y += -I$(srctree)/drivers/media/tuners ++ccflags-y += -I$(srctree)/drivers/media/dvb-core ++ccflags-y += -I$(srctree)/drivers/media/dvb-frontends ++ ++ccflags-y += $(extra-cflags-y) $(extra-cflags-m) +diff --git a/drivers/media/pci/saa7164/saa7164-api.c b/drivers/media/pci/saa7164/saa7164-api.c +new file mode 100644 +index 0000000..e042963 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-api.c +@@ -0,0 +1,1526 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#include "saa7164.h" ++ ++int saa7164_api_get_load_info(struct saa7164_dev *dev, struct tmFwInfoStruct *i) ++{ ++ int ret; ++ ++ if (!(saa_debug & DBGLVL_CPU)) ++ return 0; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ i->deviceinst = 0; ++ i->devicespec = 0; ++ i->mode = 0; ++ i->status = 0; ++ ++ ret = saa7164_cmd_send(dev, 0, GET_CUR, ++ GET_FW_STATUS_CONTROL, sizeof(struct tmFwInfoStruct), i); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ printk(KERN_INFO "saa7164[%d]-CPU: %d percent", dev->nr, i->CPULoad); ++ ++ return ret; ++} ++ ++int saa7164_api_collect_debug(struct saa7164_dev *dev) ++{ ++ struct tmComResDebugGetData d; ++ u8 more = 255; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ while (more--) { ++ ++ memset(&d, 0, sizeof(d)); ++ ++ ret = saa7164_cmd_send(dev, 0, GET_CUR, ++ GET_DEBUG_DATA_CONTROL, sizeof(d), &d); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", ++ __func__, ret); ++ ++ if (d.dwResult != SAA_OK) ++ break; ++ ++ printk(KERN_INFO "saa7164[%d]-FWMSG: %s", dev->nr, ++ d.ucDebugData); ++ } ++ ++ return 0; ++} ++ ++int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level) ++{ ++ struct tmComResDebugSetLevel lvl; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(level=%d)\n", __func__, level); ++ ++ /* Retrieve current state */ ++ ret = saa7164_cmd_send(dev, 0, GET_CUR, ++ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_API, "%s() Was %d\n", __func__, lvl.dwDebugLevel); ++ ++ lvl.dwDebugLevel = level; ++ ++ /* set new state */ ++ ret = saa7164_cmd_send(dev, 0, SET_CUR, ++ SET_DEBUG_LEVEL_CONTROL, sizeof(lvl), &lvl); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_set_vbi_format(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResProbeCommit fmt, rsp; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(nr=%d, unitid=0x%x)\n", __func__, ++ port->nr, port->hwcfg.unitid); ++ ++ fmt.bmHint = 0; ++ fmt.bFormatIndex = 1; ++ fmt.bFrameIndex = 1; ++ ++ /* Probe, see if it can support this format */ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, ++ SET_CUR, SAA_PROBE_CONTROL, sizeof(fmt), &fmt); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() set error, ret = 0x%x\n", __func__, ret); ++ ++ /* See of the format change was successful */ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, ++ GET_CUR, SAA_PROBE_CONTROL, sizeof(rsp), &rsp); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() get error, ret = 0x%x\n", __func__, ret); ++ } else { ++ /* Compare requested vs received, should be same */ ++ if (memcmp(&fmt, &rsp, sizeof(rsp)) == 0) { ++ dprintk(DBGLVL_API, "SET/PROBE Verified\n"); ++ ++ /* Ask the device to select the negotiated format */ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, ++ SET_CUR, SAA_COMMIT_CONTROL, sizeof(fmt), &fmt); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() commit error, ret = 0x%x\n", ++ __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, ++ GET_CUR, SAA_COMMIT_CONTROL, sizeof(rsp), &rsp); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() GET commit error, ret = 0x%x\n", ++ __func__, ret); ++ ++ if (memcmp(&fmt, &rsp, sizeof(rsp)) != 0) { ++ printk(KERN_ERR "%s() memcmp error, ret = 0x%x\n", ++ __func__, ret); ++ } else ++ dprintk(DBGLVL_API, "SET/COMMIT Verified\n"); ++ ++ dprintk(DBGLVL_API, "rsp.bmHint = 0x%x\n", rsp.bmHint); ++ dprintk(DBGLVL_API, "rsp.bFormatIndex = 0x%x\n", ++ rsp.bFormatIndex); ++ dprintk(DBGLVL_API, "rsp.bFrameIndex = 0x%x\n", ++ rsp.bFrameIndex); ++ } else ++ printk(KERN_ERR "%s() compare failed\n", __func__); ++ } ++ ++ if (ret == SAA_OK) ++ dprintk(DBGLVL_API, "%s(nr=%d) Success\n", __func__, port->nr); ++ ++ return ret; ++} ++ ++static int saa7164_api_set_gop_size(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResEncVideoGopStructure gs; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ gs.ucRefFrameDist = port->encoder_params.refdist; ++ gs.ucGOPSize = port->encoder_params.gop_size; ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_VIDEO_GOP_STRUCTURE_CONTROL, ++ sizeof(gs), &gs); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_set_encoder(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResEncVideoBitRate vb; ++ struct tmComResEncAudioBitRate ab; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__, ++ port->hwcfg.sourceid); ++ ++ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ++ port->encoder_profile = EU_PROFILE_PS_DVD; ++ else ++ port->encoder_profile = EU_PROFILE_TS_HQ; ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Resolution */ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Establish video bitrates */ ++ if (port->encoder_params.bitrate_mode == ++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) ++ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_CONSTANT; ++ else ++ vb.ucVideoBitRateMode = EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK; ++ vb.dwVideoBitRate = port->encoder_params.bitrate; ++ vb.dwVideoBitRatePeak = port->encoder_params.bitrate_peak; ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_VIDEO_BIT_RATE_CONTROL, ++ sizeof(struct tmComResEncVideoBitRate), ++ &vb); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Establish audio bitrates */ ++ ab.ucAudioBitRateMode = 0; ++ ab.dwAudioBitRate = 384000; ++ ab.dwAudioBitRatePeak = ab.dwAudioBitRate; ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_AUDIO_BIT_RATE_CONTROL, ++ sizeof(struct tmComResEncAudioBitRate), ++ &ab); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ++ ret); ++ ++ saa7164_api_set_aspect_ratio(port); ++ saa7164_api_set_gop_size(port); ++ ++ return ret; ++} ++ ++int saa7164_api_get_encoder(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResEncVideoBitRate v; ++ struct tmComResEncAudioBitRate a; ++ struct tmComResEncVideoInputAspectRatio ar; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s() unitid=0x%x\n", __func__, ++ port->hwcfg.sourceid); ++ ++ port->encoder_profile = 0; ++ port->video_format = 0; ++ port->video_resolution = 0; ++ port->audio_format = 0; ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_PROFILE_CONTROL, sizeof(u8), &port->encoder_profile); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_VIDEO_RESOLUTION_CONTROL, sizeof(u8), ++ &port->video_resolution); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_VIDEO_FORMAT_CONTROL, sizeof(u8), &port->video_format); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_VIDEO_BIT_RATE_CONTROL, sizeof(v), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_AUDIO_FORMAT_CONTROL, sizeof(u8), &port->audio_format); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_AUDIO_BIT_RATE_CONTROL, sizeof(a), &a); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Aspect Ratio */ ++ ar.width = 0; ++ ar.height = 0; ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, GET_CUR, ++ EU_VIDEO_INPUT_ASPECT_CONTROL, ++ sizeof(struct tmComResEncVideoInputAspectRatio), &ar); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_ENC, "encoder_profile = %d\n", port->encoder_profile); ++ dprintk(DBGLVL_ENC, "video_format = %d\n", port->video_format); ++ dprintk(DBGLVL_ENC, "audio_format = %d\n", port->audio_format); ++ dprintk(DBGLVL_ENC, "video_resolution= %d\n", port->video_resolution); ++ dprintk(DBGLVL_ENC, "v.ucVideoBitRateMode = %d\n", ++ v.ucVideoBitRateMode); ++ dprintk(DBGLVL_ENC, "v.dwVideoBitRate = %d\n", ++ v.dwVideoBitRate); ++ dprintk(DBGLVL_ENC, "v.dwVideoBitRatePeak = %d\n", ++ v.dwVideoBitRatePeak); ++ dprintk(DBGLVL_ENC, "a.ucVideoBitRateMode = %d\n", ++ a.ucAudioBitRateMode); ++ dprintk(DBGLVL_ENC, "a.dwVideoBitRate = %d\n", ++ a.dwAudioBitRate); ++ dprintk(DBGLVL_ENC, "a.dwVideoBitRatePeak = %d\n", ++ a.dwAudioBitRatePeak); ++ dprintk(DBGLVL_ENC, "aspect.width / height = %d:%d\n", ++ ar.width, ar.height); ++ ++ return ret; ++} ++ ++int saa7164_api_set_aspect_ratio(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResEncVideoInputAspectRatio ar; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s(%d)\n", __func__, ++ port->encoder_params.ctl_aspect); ++ ++ switch (port->encoder_params.ctl_aspect) { ++ case V4L2_MPEG_VIDEO_ASPECT_1x1: ++ ar.width = 1; ++ ar.height = 1; ++ break; ++ case V4L2_MPEG_VIDEO_ASPECT_4x3: ++ ar.width = 4; ++ ar.height = 3; ++ break; ++ case V4L2_MPEG_VIDEO_ASPECT_16x9: ++ ar.width = 16; ++ ar.height = 9; ++ break; ++ case V4L2_MPEG_VIDEO_ASPECT_221x100: ++ ar.width = 221; ++ ar.height = 100; ++ break; ++ default: ++ BUG(); ++ } ++ ++ dprintk(DBGLVL_ENC, "%s(%d) now %d:%d\n", __func__, ++ port->encoder_params.ctl_aspect, ++ ar.width, ar.height); ++ ++ /* Aspect Ratio */ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.sourceid, SET_CUR, ++ EU_VIDEO_INPUT_ASPECT_CONTROL, ++ sizeof(struct tmComResEncVideoInputAspectRatio), &ar); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ u16 val; ++ ++ if (ctl == PU_BRIGHTNESS_CONTROL) ++ val = port->ctl_brightness; ++ else ++ if (ctl == PU_CONTRAST_CONTROL) ++ val = port->ctl_contrast; ++ else ++ if (ctl == PU_HUE_CONTROL) ++ val = port->ctl_hue; ++ else ++ if (ctl == PU_SATURATION_CONTROL) ++ val = port->ctl_saturation; ++ else ++ if (ctl == PU_SHARPNESS_CONTROL) ++ val = port->ctl_sharpness; ++ else ++ return -EINVAL; ++ ++ dprintk(DBGLVL_ENC, "%s() unitid=0x%x ctl=%d, val=%d\n", ++ __func__, port->encunit.vsourceid, ctl, val); ++ ++ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, SET_CUR, ++ ctl, sizeof(u16), &val); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ u16 val; ++ ++ ret = saa7164_cmd_send(port->dev, port->encunit.vsourceid, GET_CUR, ++ ctl, sizeof(u16), &val); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ return ret; ++ } ++ ++ dprintk(DBGLVL_ENC, "%s() ctl=%d, val=%d\n", ++ __func__, ctl, val); ++ ++ if (ctl == PU_BRIGHTNESS_CONTROL) ++ port->ctl_brightness = val; ++ else ++ if (ctl == PU_CONTRAST_CONTROL) ++ port->ctl_contrast = val; ++ else ++ if (ctl == PU_HUE_CONTROL) ++ port->ctl_hue = val; ++ else ++ if (ctl == PU_SATURATION_CONTROL) ++ port->ctl_saturation = val; ++ else ++ if (ctl == PU_SHARPNESS_CONTROL) ++ port->ctl_sharpness = val; ++ ++ return ret; ++} ++ ++int saa7164_api_set_videomux(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ u8 inputs[] = { 1, 2, 2, 2, 5, 5, 5 }; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s() v_mux=%d a_mux=%d\n", ++ __func__, port->mux_input, inputs[port->mux_input - 1]); ++ ++ /* Audio Mute */ ++ ret = saa7164_api_audio_mute(port, 1); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Video Mux */ ++ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, SET_CUR, ++ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Audio Mux */ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.sourceid, SET_CUR, ++ SU_INPUT_SELECT_CONTROL, sizeof(u8), ++ &inputs[port->mux_input - 1]); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Audio UnMute */ ++ ret = saa7164_api_audio_mute(port, 0); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_audio_mute(struct saa7164_port *port, int mute) ++{ ++ struct saa7164_dev *dev = port->dev; ++ u8 v = mute; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(%d)\n", __func__, mute); ++ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, ++ MUTE_CONTROL, sizeof(u8), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++/* 0 = silence, 0xff = full */ ++int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level) ++{ ++ struct saa7164_dev *dev = port->dev; ++ s16 v, min, max; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(%d)\n", __func__, level); ++ ++ /* Obtain the min/max ranges */ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MIN, ++ VOLUME_CONTROL, sizeof(u16), &min); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_MAX, ++ VOLUME_CONTROL, sizeof(u16), &max); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR, ++ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, ++ level, min, max, v); ++ ++ v = level; ++ if (v < min) ++ v = min; ++ if (v > max) ++ v = max; ++ ++ /* Left */ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, ++ (0x01 << 8) | VOLUME_CONTROL, sizeof(s16), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Right */ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, ++ (0x02 << 8) | VOLUME_CONTROL, sizeof(s16), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, GET_CUR, ++ (0x01 << 8) | VOLUME_CONTROL, sizeof(u16), &v); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_API, "%s(%d) min=%d max=%d cur=%d\n", __func__, ++ level, min, max, v); ++ ++ return ret; ++} ++ ++int saa7164_api_set_audio_std(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResAudioDefaults lvl; ++ struct tmComResTunerStandard tvaudio; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ /* Establish default levels */ ++ lvl.ucDecoderLevel = TMHW_LEV_ADJ_DECLEV_DEFAULT; ++ lvl.ucDecoderFM_Level = TMHW_LEV_ADJ_DECLEV_DEFAULT; ++ lvl.ucMonoLevel = TMHW_LEV_ADJ_MONOLEV_DEFAULT; ++ lvl.ucNICAM_Level = TMHW_LEV_ADJ_NICLEV_DEFAULT; ++ lvl.ucSAP_Level = TMHW_LEV_ADJ_SAPLEV_DEFAULT; ++ lvl.ucADC_Level = TMHW_LEV_ADJ_ADCLEV_DEFAULT; ++ ret = saa7164_cmd_send(port->dev, port->audfeat.unitid, SET_CUR, ++ AUDIO_DEFAULT_CONTROL, sizeof(struct tmComResAudioDefaults), ++ &lvl); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ /* Manually select the appropriate TV audio standard */ ++ if (port->encodernorm.id & V4L2_STD_NTSC) { ++ tvaudio.std = TU_STANDARD_NTSC_M; ++ tvaudio.country = 1; ++ } else { ++ tvaudio.std = TU_STANDARD_PAL_I; ++ tvaudio.country = 44; ++ } ++ ++ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR, ++ TU_STANDARD_CONTROL, sizeof(tvaudio), &tvaudio); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() TU_STANDARD_CONTROL error, ret = 0x%x\n", ++ __func__, ret); ++ return ret; ++} ++ ++int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct tmComResTunerStandardAuto p; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(%d)\n", __func__, autodetect); ++ ++ /* Disable TV Audio autodetect if not already set (buggy) */ ++ if (autodetect) ++ p.mode = TU_STANDARD_AUTO; ++ else ++ p.mode = TU_STANDARD_MANUAL; ++ ret = saa7164_cmd_send(port->dev, port->tunerunit.unitid, SET_CUR, ++ TU_STANDARD_AUTO_CONTROL, sizeof(p), &p); ++ if (ret != SAA_OK) ++ printk(KERN_ERR ++ "%s() TU_STANDARD_AUTO_CONTROL error, ret = 0x%x\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_get_videomux(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_cmd_send(port->dev, port->vidproc.sourceid, GET_CUR, ++ SU_INPUT_SELECT_CONTROL, sizeof(u8), &port->mux_input); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_ENC, "%s() v_mux=%d\n", ++ __func__, port->mux_input); ++ ++ return ret; ++} ++ ++static int saa7164_api_set_dif(struct saa7164_port *port, u8 reg, u8 val) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ u16 len = 0; ++ u8 buf[256]; ++ int ret; ++ u8 mas; ++ ++ dprintk(DBGLVL_API, "%s(nr=%d type=%d val=%x)\n", __func__, ++ port->nr, port->type, val); ++ ++ if (port->nr == 0) ++ mas = 0xd0; ++ else ++ mas = 0xe0; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ buf[0x00] = 0x04; ++ buf[0x01] = 0x00; ++ buf[0x02] = 0x00; ++ buf[0x03] = 0x00; ++ ++ buf[0x04] = 0x04; ++ buf[0x05] = 0x00; ++ buf[0x06] = 0x00; ++ buf[0x07] = 0x00; ++ ++ buf[0x08] = reg; ++ buf[0x09] = 0x26; ++ buf[0x0a] = mas; ++ buf[0x0b] = 0xb0; ++ ++ buf[0x0c] = val; ++ buf[0x0d] = 0x00; ++ buf[0x0e] = 0x00; ++ buf[0x0f] = 0x00; ++ ++ ret = saa7164_cmd_send(dev, port->ifunit.unitid, GET_LEN, ++ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); ++ return -EIO; ++ } ++ ++ ret = saa7164_cmd_send(dev, port->ifunit.unitid, SET_CUR, ++ EXU_REGISTER_ACCESS_CONTROL, len, &buf); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); ++#if 0 ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf, 16, ++ false); ++#endif ++ return ret == SAA_OK ? 0 : -EIO; ++} ++ ++/* Disable the IF block AGC controls */ ++int saa7164_api_configure_dif(struct saa7164_port *port, u32 std) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ u8 agc_disable; ++ ++ dprintk(DBGLVL_API, "%s(nr=%d, 0x%x)\n", __func__, port->nr, std); ++ ++ if (std & V4L2_STD_NTSC) { ++ dprintk(DBGLVL_API, " NTSC\n"); ++ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_I) { ++ dprintk(DBGLVL_API, " PAL-I\n"); ++ saa7164_api_set_dif(port, 0x00, 0x08); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_M) { ++ dprintk(DBGLVL_API, " PAL-M\n"); ++ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_N) { ++ dprintk(DBGLVL_API, " PAL-N\n"); ++ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_Nc) { ++ dprintk(DBGLVL_API, " PAL-Nc\n"); ++ saa7164_api_set_dif(port, 0x00, 0x01); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_B) { ++ dprintk(DBGLVL_API, " PAL-B\n"); ++ saa7164_api_set_dif(port, 0x00, 0x02); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_PAL_DK) { ++ dprintk(DBGLVL_API, " PAL-DK\n"); ++ saa7164_api_set_dif(port, 0x00, 0x10); /* Video Standard */ ++ agc_disable = 0; ++ } else if (std & V4L2_STD_SECAM_L) { ++ dprintk(DBGLVL_API, " SECAM-L\n"); ++ saa7164_api_set_dif(port, 0x00, 0x20); /* Video Standard */ ++ agc_disable = 0; ++ } else { ++ /* Unknown standard, assume DTV */ ++ dprintk(DBGLVL_API, " Unknown (assuming DTV)\n"); ++ /* Undefinded Video Standard */ ++ saa7164_api_set_dif(port, 0x00, 0x80); ++ agc_disable = 1; ++ } ++ ++ saa7164_api_set_dif(port, 0x48, 0xa0); /* AGC Functions 1 */ ++ saa7164_api_set_dif(port, 0xc0, agc_disable); /* AGC Output Disable */ ++ saa7164_api_set_dif(port, 0x7c, 0x04); /* CVBS EQ */ ++ saa7164_api_set_dif(port, 0x04, 0x01); /* Active */ ++ msleep(100); ++ saa7164_api_set_dif(port, 0x04, 0x00); /* Active (again) */ ++ msleep(100); ++ ++ return ret; ++} ++ ++/* Ensure the dif is in the correct state for the operating mode ++ * (analog / dtv). We only configure the diff through the analog encoder ++ * so when we're in digital mode we need to find the appropriate encoder ++ * and use it to configure the DIF. ++ */ ++int saa7164_api_initialize_dif(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_port *p = NULL; ++ int ret = -EINVAL; ++ u32 std = 0; ++ ++ dprintk(DBGLVL_API, "%s(nr=%d type=%d)\n", __func__, ++ port->nr, port->type); ++ ++ if (port->type == SAA7164_MPEG_ENCODER) { ++ /* Pick any analog standard to init the diff. ++ * we'll come back during encoder_init' ++ * and set the correct standard if requried. ++ */ ++ std = V4L2_STD_NTSC; ++ } else ++ if (port->type == SAA7164_MPEG_DVB) { ++ if (port->nr == SAA7164_PORT_TS1) ++ p = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ p = &dev->ports[SAA7164_PORT_ENC2]; ++ } else ++ if (port->type == SAA7164_MPEG_VBI) { ++ std = V4L2_STD_NTSC; ++ if (port->nr == SAA7164_PORT_VBI1) ++ p = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ p = &dev->ports[SAA7164_PORT_ENC2]; ++ } else ++ BUG(); ++ ++ if (p) ++ ret = saa7164_api_configure_dif(p, std); ++ ++ return ret; ++} ++ ++int saa7164_api_transition_port(struct saa7164_port *port, u8 mode) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s(nr=%d unitid=0x%x,%d)\n", ++ __func__, port->nr, port->hwcfg.unitid, mode); ++ ++ ret = saa7164_cmd_send(port->dev, port->hwcfg.unitid, SET_CUR, ++ SAA_STATE_CONTROL, sizeof(mode), &mode); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s(portnr %d unitid 0x%x) error, ret = 0x%x\n", ++ __func__, port->nr, port->hwcfg.unitid, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version) ++{ ++ int ret; ++ ++ ret = saa7164_cmd_send(dev, 0, GET_CUR, ++ GET_FW_VERSION_CONTROL, sizeof(u32), version); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen) ++{ ++ u8 reg[] = { 0x0f, 0x00 }; ++ ++ if (buflen < 128) ++ return -ENOMEM; ++ ++ /* Assumption: Hauppauge eeprom is at 0xa0 on on bus 0 */ ++ /* TODO: Pull the details from the boards struct */ ++ return saa7164_api_i2c_read(&dev->i2c_bus[0], 0xa0 >> 1, sizeof(reg), ++ ®[0], 128, buf); ++} ++ ++static int saa7164_api_configure_port_vbi(struct saa7164_dev *dev, ++ struct saa7164_port *port) ++{ ++ struct tmComResVBIFormatDescrHeader *fmt = &port->vbi_fmt_ntsc; ++ ++ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex); ++ dprintk(DBGLVL_API, " VideoStandard = 0x%x\n", fmt->VideoStandard); ++ dprintk(DBGLVL_API, " StartLine = %d\n", fmt->StartLine); ++ dprintk(DBGLVL_API, " EndLine = %d\n", fmt->EndLine); ++ dprintk(DBGLVL_API, " FieldRate = %d\n", fmt->FieldRate); ++ dprintk(DBGLVL_API, " bNumLines = %d\n", fmt->bNumLines); ++ ++ /* Cache the hardware configuration in the port */ ++ ++ port->bufcounter = port->hwcfg.BARLocation; ++ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); ++ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); ++ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); ++ port->bufptr32l = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); ++ port->bufptr32h = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ port->bufptr64 = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", ++ port->hwcfg.BARLocation); ++ ++ dprintk(DBGLVL_API, " = VS_FORMAT_VBI (becomes dev->en[%d])\n", ++ port->nr); ++ ++ return 0; ++} ++ ++static int ++saa7164_api_configure_port_mpeg2ts(struct saa7164_dev *dev, ++ struct saa7164_port *port, ++ struct tmComResTSFormatDescrHeader *tsfmt) ++{ ++ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", tsfmt->bFormatIndex); ++ dprintk(DBGLVL_API, " bDataOffset = 0x%x\n", tsfmt->bDataOffset); ++ dprintk(DBGLVL_API, " bPacketLength= 0x%x\n", tsfmt->bPacketLength); ++ dprintk(DBGLVL_API, " bStrideLength= 0x%x\n", tsfmt->bStrideLength); ++ dprintk(DBGLVL_API, " bguid = (....)\n"); ++ ++ /* Cache the hardware configuration in the port */ ++ ++ port->bufcounter = port->hwcfg.BARLocation; ++ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); ++ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); ++ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); ++ port->bufptr32l = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); ++ port->bufptr32h = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ port->bufptr64 = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", ++ port->hwcfg.BARLocation); ++ ++ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGTS (becomes dev->ts[%d])\n", ++ port->nr); ++ ++ return 0; ++} ++ ++static int ++saa7164_api_configure_port_mpeg2ps(struct saa7164_dev *dev, ++ struct saa7164_port *port, ++ struct tmComResPSFormatDescrHeader *fmt) ++{ ++ dprintk(DBGLVL_API, " bFormatIndex = 0x%x\n", fmt->bFormatIndex); ++ dprintk(DBGLVL_API, " wPacketLength= 0x%x\n", fmt->wPacketLength); ++ dprintk(DBGLVL_API, " wPackLength= 0x%x\n", fmt->wPackLength); ++ dprintk(DBGLVL_API, " bPackDataType= 0x%x\n", fmt->bPackDataType); ++ ++ /* Cache the hardware configuration in the port */ ++ /* TODO: CHECK THIS in the port config */ ++ port->bufcounter = port->hwcfg.BARLocation; ++ port->pitch = port->hwcfg.BARLocation + (2 * sizeof(u32)); ++ port->bufsize = port->hwcfg.BARLocation + (3 * sizeof(u32)); ++ port->bufoffset = port->hwcfg.BARLocation + (4 * sizeof(u32)); ++ port->bufptr32l = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount) + sizeof(u32); ++ port->bufptr32h = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ port->bufptr64 = port->hwcfg.BARLocation + ++ (4 * sizeof(u32)) + ++ (sizeof(u32) * port->hwcfg.buffercount); ++ dprintk(DBGLVL_API, " = port->hwcfg.BARLocation = 0x%x\n", ++ port->hwcfg.BARLocation); ++ ++ dprintk(DBGLVL_API, " = VS_FORMAT_MPEGPS (becomes dev->enc[%d])\n", ++ port->nr); ++ ++ return 0; ++} ++ ++static int saa7164_api_dump_subdevs(struct saa7164_dev *dev, u8 *buf, int len) ++{ ++ struct saa7164_port *tsport = NULL; ++ struct saa7164_port *encport = NULL; ++ struct saa7164_port *vbiport = NULL; ++ u32 idx, next_offset; ++ int i; ++ struct tmComResDescrHeader *hdr, *t; ++ struct tmComResExtDevDescrHeader *exthdr; ++ struct tmComResPathDescrHeader *pathhdr; ++ struct tmComResAntTermDescrHeader *anttermhdr; ++ struct tmComResTunerDescrHeader *tunerunithdr; ++ struct tmComResDMATermDescrHeader *vcoutputtermhdr; ++ struct tmComResTSFormatDescrHeader *tsfmt; ++ struct tmComResPSFormatDescrHeader *psfmt; ++ struct tmComResSelDescrHeader *psel; ++ struct tmComResProcDescrHeader *pdh; ++ struct tmComResAFeatureDescrHeader *afd; ++ struct tmComResEncoderDescrHeader *edh; ++ struct tmComResVBIFormatDescrHeader *vbifmt; ++ u32 currpath = 0; ++ ++ dprintk(DBGLVL_API, ++ "%s(?,?,%d) sizeof(struct tmComResDescrHeader) = %d bytes\n", ++ __func__, len, (u32)sizeof(struct tmComResDescrHeader)); ++ ++ for (idx = 0; idx < (len - sizeof(struct tmComResDescrHeader));) { ++ ++ hdr = (struct tmComResDescrHeader *)(buf + idx); ++ ++ if (hdr->type != CS_INTERFACE) ++ return SAA_ERR_NOT_SUPPORTED; ++ ++ dprintk(DBGLVL_API, "@ 0x%x =\n", idx); ++ switch (hdr->subtype) { ++ case GENERAL_REQUEST: ++ dprintk(DBGLVL_API, " GENERAL_REQUEST\n"); ++ break; ++ case VC_TUNER_PATH: ++ dprintk(DBGLVL_API, " VC_TUNER_PATH\n"); ++ pathhdr = (struct tmComResPathDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " pathid = 0x%x\n", ++ pathhdr->pathid); ++ currpath = pathhdr->pathid; ++ break; ++ case VC_INPUT_TERMINAL: ++ dprintk(DBGLVL_API, " VC_INPUT_TERMINAL\n"); ++ anttermhdr = ++ (struct tmComResAntTermDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " terminalid = 0x%x\n", ++ anttermhdr->terminalid); ++ dprintk(DBGLVL_API, " terminaltype = 0x%x\n", ++ anttermhdr->terminaltype); ++ switch (anttermhdr->terminaltype) { ++ case ITT_ANTENNA: ++ dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); ++ break; ++ case LINE_CONNECTOR: ++ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); ++ break; ++ case SPDIF_CONNECTOR: ++ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); ++ break; ++ case COMPOSITE_CONNECTOR: ++ dprintk(DBGLVL_API, ++ " = COMPOSITE_CONNECTOR\n"); ++ break; ++ case SVIDEO_CONNECTOR: ++ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); ++ break; ++ case COMPONENT_CONNECTOR: ++ dprintk(DBGLVL_API, ++ " = COMPONENT_CONNECTOR\n"); ++ break; ++ case STANDARD_DMA: ++ dprintk(DBGLVL_API, " = STANDARD_DMA\n"); ++ break; ++ default: ++ dprintk(DBGLVL_API, " = undefined (0x%x)\n", ++ anttermhdr->terminaltype); ++ } ++ dprintk(DBGLVL_API, " assocterminal= 0x%x\n", ++ anttermhdr->assocterminal); ++ dprintk(DBGLVL_API, " iterminal = 0x%x\n", ++ anttermhdr->iterminal); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ anttermhdr->controlsize); ++ break; ++ case VC_OUTPUT_TERMINAL: ++ dprintk(DBGLVL_API, " VC_OUTPUT_TERMINAL\n"); ++ vcoutputtermhdr = ++ (struct tmComResDMATermDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ vcoutputtermhdr->unitid); ++ dprintk(DBGLVL_API, " terminaltype = 0x%x\n", ++ vcoutputtermhdr->terminaltype); ++ switch (vcoutputtermhdr->terminaltype) { ++ case ITT_ANTENNA: ++ dprintk(DBGLVL_API, " = ITT_ANTENNA\n"); ++ break; ++ case LINE_CONNECTOR: ++ dprintk(DBGLVL_API, " = LINE_CONNECTOR\n"); ++ break; ++ case SPDIF_CONNECTOR: ++ dprintk(DBGLVL_API, " = SPDIF_CONNECTOR\n"); ++ break; ++ case COMPOSITE_CONNECTOR: ++ dprintk(DBGLVL_API, ++ " = COMPOSITE_CONNECTOR\n"); ++ break; ++ case SVIDEO_CONNECTOR: ++ dprintk(DBGLVL_API, " = SVIDEO_CONNECTOR\n"); ++ break; ++ case COMPONENT_CONNECTOR: ++ dprintk(DBGLVL_API, ++ " = COMPONENT_CONNECTOR\n"); ++ break; ++ case STANDARD_DMA: ++ dprintk(DBGLVL_API, " = STANDARD_DMA\n"); ++ break; ++ default: ++ dprintk(DBGLVL_API, " = undefined (0x%x)\n", ++ vcoutputtermhdr->terminaltype); ++ } ++ dprintk(DBGLVL_API, " assocterminal= 0x%x\n", ++ vcoutputtermhdr->assocterminal); ++ dprintk(DBGLVL_API, " sourceid = 0x%x\n", ++ vcoutputtermhdr->sourceid); ++ dprintk(DBGLVL_API, " iterminal = 0x%x\n", ++ vcoutputtermhdr->iterminal); ++ dprintk(DBGLVL_API, " BARLocation = 0x%x\n", ++ vcoutputtermhdr->BARLocation); ++ dprintk(DBGLVL_API, " flags = 0x%x\n", ++ vcoutputtermhdr->flags); ++ dprintk(DBGLVL_API, " interruptid = 0x%x\n", ++ vcoutputtermhdr->interruptid); ++ dprintk(DBGLVL_API, " buffercount = 0x%x\n", ++ vcoutputtermhdr->buffercount); ++ dprintk(DBGLVL_API, " metadatasize = 0x%x\n", ++ vcoutputtermhdr->metadatasize); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ vcoutputtermhdr->controlsize); ++ dprintk(DBGLVL_API, " numformats = 0x%x\n", ++ vcoutputtermhdr->numformats); ++ ++ t = (struct tmComResDescrHeader *) ++ ((struct tmComResDMATermDescrHeader *)(buf + idx)); ++ next_offset = idx + (vcoutputtermhdr->len); ++ for (i = 0; i < vcoutputtermhdr->numformats; i++) { ++ t = (struct tmComResDescrHeader *) ++ (buf + next_offset); ++ switch (t->subtype) { ++ case VS_FORMAT_MPEG2TS: ++ tsfmt = ++ (struct tmComResTSFormatDescrHeader *)t; ++ if (currpath == 1) ++ tsport = &dev->ports[SAA7164_PORT_TS1]; ++ else ++ tsport = &dev->ports[SAA7164_PORT_TS2]; ++ memcpy(&tsport->hwcfg, vcoutputtermhdr, ++ sizeof(*vcoutputtermhdr)); ++ saa7164_api_configure_port_mpeg2ts(dev, ++ tsport, tsfmt); ++ break; ++ case VS_FORMAT_MPEG2PS: ++ psfmt = ++ (struct tmComResPSFormatDescrHeader *)t; ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->hwcfg, vcoutputtermhdr, ++ sizeof(*vcoutputtermhdr)); ++ saa7164_api_configure_port_mpeg2ps(dev, ++ encport, psfmt); ++ break; ++ case VS_FORMAT_VBI: ++ vbifmt = ++ (struct tmComResVBIFormatDescrHeader *)t; ++ if (currpath == 1) ++ vbiport = &dev->ports[SAA7164_PORT_VBI1]; ++ else ++ vbiport = &dev->ports[SAA7164_PORT_VBI2]; ++ memcpy(&vbiport->hwcfg, vcoutputtermhdr, ++ sizeof(*vcoutputtermhdr)); ++ memcpy(&vbiport->vbi_fmt_ntsc, vbifmt, ++ sizeof(*vbifmt)); ++ saa7164_api_configure_port_vbi(dev, ++ vbiport); ++ break; ++ case VS_FORMAT_RDS: ++ dprintk(DBGLVL_API, ++ " = VS_FORMAT_RDS\n"); ++ break; ++ case VS_FORMAT_UNCOMPRESSED: ++ dprintk(DBGLVL_API, ++ " = VS_FORMAT_UNCOMPRESSED\n"); ++ break; ++ case VS_FORMAT_TYPE: ++ dprintk(DBGLVL_API, ++ " = VS_FORMAT_TYPE\n"); ++ break; ++ default: ++ dprintk(DBGLVL_API, ++ " = undefined (0x%x)\n", ++ t->subtype); ++ } ++ next_offset += t->len; ++ } ++ ++ break; ++ case TUNER_UNIT: ++ dprintk(DBGLVL_API, " TUNER_UNIT\n"); ++ tunerunithdr = ++ (struct tmComResTunerDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ tunerunithdr->unitid); ++ dprintk(DBGLVL_API, " sourceid = 0x%x\n", ++ tunerunithdr->sourceid); ++ dprintk(DBGLVL_API, " iunit = 0x%x\n", ++ tunerunithdr->iunit); ++ dprintk(DBGLVL_API, " tuningstandards = 0x%x\n", ++ tunerunithdr->tuningstandards); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ tunerunithdr->controlsize); ++ dprintk(DBGLVL_API, " controls = 0x%x\n", ++ tunerunithdr->controls); ++ ++ if (tunerunithdr->unitid == tunerunithdr->iunit) { ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->tunerunit, tunerunithdr, ++ sizeof(struct tmComResTunerDescrHeader)); ++ dprintk(DBGLVL_API, ++ " (becomes dev->enc[%d] tuner)\n", ++ encport->nr); ++ } ++ break; ++ case VC_SELECTOR_UNIT: ++ psel = (struct tmComResSelDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " VC_SELECTOR_UNIT\n"); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ psel->unitid); ++ dprintk(DBGLVL_API, " nrinpins = 0x%x\n", ++ psel->nrinpins); ++ dprintk(DBGLVL_API, " sourceid = 0x%x\n", ++ psel->sourceid); ++ break; ++ case VC_PROCESSING_UNIT: ++ pdh = (struct tmComResProcDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " VC_PROCESSING_UNIT\n"); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ pdh->unitid); ++ dprintk(DBGLVL_API, " sourceid = 0x%x\n", ++ pdh->sourceid); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ pdh->controlsize); ++ if (pdh->controlsize == 0x04) { ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->vidproc, pdh, ++ sizeof(struct tmComResProcDescrHeader)); ++ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n", ++ encport->nr); ++ } ++ break; ++ case FEATURE_UNIT: ++ afd = (struct tmComResAFeatureDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " FEATURE_UNIT\n"); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ afd->unitid); ++ dprintk(DBGLVL_API, " sourceid = 0x%x\n", ++ afd->sourceid); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ afd->controlsize); ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->audfeat, afd, ++ sizeof(struct tmComResAFeatureDescrHeader)); ++ dprintk(DBGLVL_API, " (becomes dev->enc[%d])\n", ++ encport->nr); ++ break; ++ case ENCODER_UNIT: ++ edh = (struct tmComResEncoderDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " ENCODER_UNIT\n"); ++ dprintk(DBGLVL_API, " subtype = 0x%x\n", edh->subtype); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", edh->unitid); ++ dprintk(DBGLVL_API, " vsourceid = 0x%x\n", ++ edh->vsourceid); ++ dprintk(DBGLVL_API, " asourceid = 0x%x\n", ++ edh->asourceid); ++ dprintk(DBGLVL_API, " iunit = 0x%x\n", edh->iunit); ++ if (edh->iunit == edh->unitid) { ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->encunit, edh, ++ sizeof(struct tmComResEncoderDescrHeader)); ++ dprintk(DBGLVL_API, ++ " (becomes dev->enc[%d])\n", ++ encport->nr); ++ } ++ break; ++ case EXTENSION_UNIT: ++ dprintk(DBGLVL_API, " EXTENSION_UNIT\n"); ++ exthdr = (struct tmComResExtDevDescrHeader *)(buf + idx); ++ dprintk(DBGLVL_API, " unitid = 0x%x\n", ++ exthdr->unitid); ++ dprintk(DBGLVL_API, " deviceid = 0x%x\n", ++ exthdr->deviceid); ++ dprintk(DBGLVL_API, " devicetype = 0x%x\n", ++ exthdr->devicetype); ++ if (exthdr->devicetype & 0x1) ++ dprintk(DBGLVL_API, " = Decoder Device\n"); ++ if (exthdr->devicetype & 0x2) ++ dprintk(DBGLVL_API, " = GPIO Source\n"); ++ if (exthdr->devicetype & 0x4) ++ dprintk(DBGLVL_API, " = Video Decoder\n"); ++ if (exthdr->devicetype & 0x8) ++ dprintk(DBGLVL_API, " = Audio Decoder\n"); ++ if (exthdr->devicetype & 0x20) ++ dprintk(DBGLVL_API, " = Crossbar\n"); ++ if (exthdr->devicetype & 0x40) ++ dprintk(DBGLVL_API, " = Tuner\n"); ++ if (exthdr->devicetype & 0x80) ++ dprintk(DBGLVL_API, " = IF PLL\n"); ++ if (exthdr->devicetype & 0x100) ++ dprintk(DBGLVL_API, " = Demodulator\n"); ++ if (exthdr->devicetype & 0x200) ++ dprintk(DBGLVL_API, " = RDS Decoder\n"); ++ if (exthdr->devicetype & 0x400) ++ dprintk(DBGLVL_API, " = Encoder\n"); ++ if (exthdr->devicetype & 0x800) ++ dprintk(DBGLVL_API, " = IR Decoder\n"); ++ if (exthdr->devicetype & 0x1000) ++ dprintk(DBGLVL_API, " = EEPROM\n"); ++ if (exthdr->devicetype & 0x2000) ++ dprintk(DBGLVL_API, ++ " = VBI Decoder\n"); ++ if (exthdr->devicetype & 0x10000) ++ dprintk(DBGLVL_API, ++ " = Streaming Device\n"); ++ if (exthdr->devicetype & 0x20000) ++ dprintk(DBGLVL_API, ++ " = DRM Device\n"); ++ if (exthdr->devicetype & 0x40000000) ++ dprintk(DBGLVL_API, ++ " = Generic Device\n"); ++ if (exthdr->devicetype & 0x80000000) ++ dprintk(DBGLVL_API, ++ " = Config Space Device\n"); ++ dprintk(DBGLVL_API, " numgpiopins = 0x%x\n", ++ exthdr->numgpiopins); ++ dprintk(DBGLVL_API, " numgpiogroups = 0x%x\n", ++ exthdr->numgpiogroups); ++ dprintk(DBGLVL_API, " controlsize = 0x%x\n", ++ exthdr->controlsize); ++ if (exthdr->devicetype & 0x80) { ++ if (currpath == 1) ++ encport = &dev->ports[SAA7164_PORT_ENC1]; ++ else ++ encport = &dev->ports[SAA7164_PORT_ENC2]; ++ memcpy(&encport->ifunit, exthdr, ++ sizeof(struct tmComResExtDevDescrHeader)); ++ dprintk(DBGLVL_API, ++ " (becomes dev->enc[%d])\n", ++ encport->nr); ++ } ++ break; ++ case PVC_INFRARED_UNIT: ++ dprintk(DBGLVL_API, " PVC_INFRARED_UNIT\n"); ++ break; ++ case DRM_UNIT: ++ dprintk(DBGLVL_API, " DRM_UNIT\n"); ++ break; ++ default: ++ dprintk(DBGLVL_API, "default %d\n", hdr->subtype); ++ } ++ ++ dprintk(DBGLVL_API, " 1.%x\n", hdr->len); ++ dprintk(DBGLVL_API, " 2.%x\n", hdr->type); ++ dprintk(DBGLVL_API, " 3.%x\n", hdr->subtype); ++ dprintk(DBGLVL_API, " 4.%x\n", hdr->unitid); ++ ++ idx += hdr->len; ++ } ++ ++ return 0; ++} ++ ++int saa7164_api_enum_subdevs(struct saa7164_dev *dev) ++{ ++ int ret; ++ u32 buflen = 0; ++ u8 *buf; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ /* Get the total descriptor length */ ++ ret = saa7164_cmd_send(dev, 0, GET_LEN, ++ GET_DESCRIPTORS_CONTROL, sizeof(buflen), &buflen); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ ++ dprintk(DBGLVL_API, "%s() total descriptor size = %d bytes.\n", ++ __func__, buflen); ++ ++ /* Allocate enough storage for all of the descs */ ++ buf = kzalloc(buflen, GFP_KERNEL); ++ if (!buf) ++ return SAA_ERR_NO_RESOURCES; ++ ++ /* Retrieve them */ ++ ret = saa7164_cmd_send(dev, 0, GET_CUR, ++ GET_DESCRIPTORS_CONTROL, buflen, buf); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ret); ++ goto out; ++ } ++ ++ if (saa_debug & DBGLVL_API) ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf, ++ buflen & ~15, false); ++ ++ saa7164_api_dump_subdevs(dev, buf, buflen); ++ ++out: ++ kfree(buf); ++ return ret; ++} ++ ++int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, ++ u32 datalen, u8 *data) ++{ ++ struct saa7164_dev *dev = bus->dev; ++ u16 len = 0; ++ int unitid; ++ u8 buf[256]; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ if (reglen > 4) ++ return -EIO; ++ ++ /* Prepare the send buffer */ ++ /* Bytes 00-03 source register length ++ * 04-07 source bytes to read ++ * 08... register address ++ */ ++ memset(buf, 0, sizeof(buf)); ++ memcpy((buf + 2 * sizeof(u32) + 0), reg, reglen); ++ *((u32 *)(buf + 0 * sizeof(u32))) = reglen; ++ *((u32 *)(buf + 1 * sizeof(u32))) = datalen; ++ ++ unitid = saa7164_i2caddr_to_unitid(bus, addr); ++ if (unitid < 0) { ++ printk(KERN_ERR ++ "%s() error, cannot translate regaddr 0x%x to unitid\n", ++ __func__, addr); ++ return -EIO; ++ } ++ ++ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, ++ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); ++ return -EIO; ++ } ++ ++ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); ++ ++ if (saa_debug & DBGLVL_I2C) ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, buf, ++ 32, false); ++ ++ ret = saa7164_cmd_send(bus->dev, unitid, GET_CUR, ++ EXU_REGISTER_ACCESS_CONTROL, len, &buf); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); ++ else { ++ if (saa_debug & DBGLVL_I2C) ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, ++ buf, sizeof(buf), false); ++ memcpy(data, (buf + 2 * sizeof(u32) + reglen), datalen); ++ } ++ ++ return ret == SAA_OK ? 0 : -EIO; ++} ++ ++/* For a given 8 bit i2c address device, write the buffer */ ++int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, ++ u8 *data) ++{ ++ struct saa7164_dev *dev = bus->dev; ++ u16 len = 0; ++ int unitid; ++ int reglen; ++ u8 buf[256]; ++ int ret; ++ ++ dprintk(DBGLVL_API, "%s()\n", __func__); ++ ++ if ((datalen == 0) || (datalen > 232)) ++ return -EIO; ++ ++ memset(buf, 0, sizeof(buf)); ++ ++ unitid = saa7164_i2caddr_to_unitid(bus, addr); ++ if (unitid < 0) { ++ printk(KERN_ERR ++ "%s() error, cannot translate regaddr 0x%x to unitid\n", ++ __func__, addr); ++ return -EIO; ++ } ++ ++ reglen = saa7164_i2caddr_to_reglen(bus, addr); ++ if (reglen < 0) { ++ printk(KERN_ERR ++ "%s() error, cannot translate regaddr to reglen\n", ++ __func__); ++ return -EIO; ++ } ++ ++ ret = saa7164_cmd_send(bus->dev, unitid, GET_LEN, ++ EXU_REGISTER_ACCESS_CONTROL, sizeof(len), &len); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret(1) = 0x%x\n", __func__, ret); ++ return -EIO; ++ } ++ ++ dprintk(DBGLVL_API, "%s() len = %d bytes\n", __func__, len); ++ ++ /* Prepare the send buffer */ ++ /* Bytes 00-03 dest register length ++ * 04-07 dest bytes to write ++ * 08... register address ++ */ ++ *((u32 *)(buf + 0 * sizeof(u32))) = reglen; ++ *((u32 *)(buf + 1 * sizeof(u32))) = datalen - reglen; ++ memcpy((buf + 2 * sizeof(u32)), data, datalen); ++ ++ if (saa_debug & DBGLVL_I2C) ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, ++ buf, sizeof(buf), false); ++ ++ ret = saa7164_cmd_send(bus->dev, unitid, SET_CUR, ++ EXU_REGISTER_ACCESS_CONTROL, len, &buf); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret(2) = 0x%x\n", __func__, ret); ++ ++ return ret == SAA_OK ? 0 : -EIO; ++} ++ ++static int saa7164_api_modify_gpio(struct saa7164_dev *dev, u8 unitid, ++ u8 pin, u8 state) ++{ ++ int ret; ++ struct tmComResGPIO t; ++ ++ dprintk(DBGLVL_API, "%s(0x%x, %d, %d)\n", ++ __func__, unitid, pin, state); ++ ++ if ((pin > 7) || (state > 2)) ++ return SAA_ERR_BAD_PARAMETER; ++ ++ t.pin = pin; ++ t.state = state; ++ ++ ret = saa7164_cmd_send(dev, unitid, SET_CUR, ++ EXU_GPIO_CONTROL, sizeof(t), &t); ++ if (ret != SAA_OK) ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, ++ u8 pin) ++{ ++ return saa7164_api_modify_gpio(dev, unitid, pin, 1); ++} ++ ++int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, ++ u8 pin) ++{ ++ return saa7164_api_modify_gpio(dev, unitid, pin, 0); ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c +new file mode 100644 +index 0000000..66696fa +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-buffer.c +@@ -0,0 +1,322 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++ ++#include "saa7164.h" ++ ++/* The PCI address space for buffer handling looks like this: ++ * ++ * +-u32 wide-------------+ ++ * | + ++ * +-u64 wide------------------------------------+ ++ * + + ++ * +----------------------+ ++ * | CurrentBufferPtr + Pointer to current PCI buffer >-+ ++ * +----------------------+ | ++ * | Unused + | ++ * +----------------------+ | ++ * | Pitch + = 188 (bytes) | ++ * +----------------------+ | ++ * | PCI buffer size + = pitch * number of lines (312) | ++ * +----------------------+ | ++ * |0| Buf0 Write Offset + | ++ * +----------------------+ v ++ * |1| Buf1 Write Offset + | ++ * +----------------------+ | ++ * |2| Buf2 Write Offset + | ++ * +----------------------+ | ++ * |3| Buf3 Write Offset + | ++ * +----------------------+ | ++ * ... More write offsets | ++ * +---------------------------------------------+ | ++ * +0| set of ptrs to PCI pagetables + | ++ * +---------------------------------------------+ | ++ * +1| set of ptrs to PCI pagetables + <--------+ ++ * +---------------------------------------------+ ++ * +2| set of ptrs to PCI pagetables + ++ * +---------------------------------------------+ ++ * +3| set of ptrs to PCI pagetables + >--+ ++ * +---------------------------------------------+ | ++ * ... More buffer pointers | +----------------+ ++ * +->| pt[0] TS data | ++ * | +----------------+ ++ * | ++ * | +----------------+ ++ * +->| pt[1] TS data | ++ * | +----------------+ ++ * | etc ++ */ ++ ++void saa7164_buffer_display(struct saa7164_buffer *buf) ++{ ++ struct saa7164_dev *dev = buf->port->dev; ++ int i; ++ ++ dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n", ++ __func__, buf, buf->idx); ++ dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n", ++ buf->cpu, (long long)buf->dma, buf->pci_size); ++ dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n", ++ buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size); ++ ++ /* Format the Page Table Entries to point into the data buffer */ ++ for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) { ++ ++ dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", ++ i, buf->pt_cpu, (u64)*(buf->pt_cpu)); ++ ++ } ++} ++/* Allocate a new buffer structure and associated PCI space in bytes. ++ * len must be a multiple of sizeof(u64) ++ */ ++struct saa7164_buffer *saa7164_buffer_alloc(struct saa7164_port *port, ++ u32 len) ++{ ++ struct tmHWStreamParameters *params = &port->hw_streamingparams; ++ struct saa7164_buffer *buf = NULL; ++ struct saa7164_dev *dev = port->dev; ++ int i; ++ ++ if ((len == 0) || (len >= 65536) || (len % sizeof(u64))) { ++ log_warn("%s() SAA_ERR_BAD_PARAMETER\n", __func__); ++ goto ret; ++ } ++ ++ buf = kzalloc(sizeof(struct saa7164_buffer), GFP_KERNEL); ++ if (!buf) { ++ log_warn("%s() SAA_ERR_NO_RESOURCES\n", __func__); ++ goto ret; ++ } ++ ++ buf->idx = -1; ++ buf->port = port; ++ buf->flags = SAA7164_BUFFER_FREE; ++ buf->pos = 0; ++ buf->actual_size = params->pitch * params->numberoflines; ++ buf->crc = 0; ++ /* TODO: arg len is being ignored */ ++ buf->pci_size = SAA7164_PT_ENTRIES * 0x1000; ++ buf->pt_size = (SAA7164_PT_ENTRIES * sizeof(u64)) + 0x1000; ++ ++ /* Allocate contiguous memory */ ++ buf->cpu = pci_alloc_consistent(port->dev->pci, buf->pci_size, ++ &buf->dma); ++ if (!buf->cpu) ++ goto fail1; ++ ++ buf->pt_cpu = pci_alloc_consistent(port->dev->pci, buf->pt_size, ++ &buf->pt_dma); ++ if (!buf->pt_cpu) ++ goto fail2; ++ ++ /* init the buffers to a known pattern, easier during debugging */ ++ memset_io(buf->cpu, 0xff, buf->pci_size); ++ buf->crc = crc32(0, buf->cpu, buf->actual_size); ++ memset_io(buf->pt_cpu, 0xff, buf->pt_size); ++ ++ dprintk(DBGLVL_BUF, "%s() allocated buffer @ 0x%p (%d pageptrs)\n", ++ __func__, buf, params->numpagetables); ++ dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08lx len = 0x%x\n", ++ buf->cpu, (long)buf->dma, buf->pci_size); ++ dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08lx len = 0x%x\n", ++ buf->pt_cpu, (long)buf->pt_dma, buf->pt_size); ++ ++ /* Format the Page Table Entries to point into the data buffer */ ++ for (i = 0 ; i < params->numpagetables; i++) { ++ ++ *(buf->pt_cpu + i) = buf->dma + (i * 0x1000); /* TODO */ ++ dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n", ++ i, buf->pt_cpu, (u64)*(buf->pt_cpu)); ++ ++ } ++ ++ goto ret; ++ ++fail2: ++ pci_free_consistent(port->dev->pci, buf->pci_size, buf->cpu, buf->dma); ++fail1: ++ kfree(buf); ++ ++ buf = NULL; ++ret: ++ return buf; ++} ++ ++int saa7164_buffer_dealloc(struct saa7164_buffer *buf) ++{ ++ struct saa7164_dev *dev; ++ ++ if (!buf || !buf->port) ++ return SAA_ERR_BAD_PARAMETER; ++ dev = buf->port->dev; ++ ++ dprintk(DBGLVL_BUF, "%s() deallocating buffer @ 0x%p\n", ++ __func__, buf); ++ ++ if (buf->flags != SAA7164_BUFFER_FREE) ++ log_warn(" freeing a non-free buffer\n"); ++ ++ pci_free_consistent(dev->pci, buf->pci_size, buf->cpu, buf->dma); ++ pci_free_consistent(dev->pci, buf->pt_size, buf->pt_cpu, buf->pt_dma); ++ ++ kfree(buf); ++ ++ return SAA_OK; ++} ++ ++int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ if ((i < 0) || (i >= port->hwcfg.buffercount)) ++ return -EINVAL; ++ ++ dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); ++ ++ saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); ++ ++ return 0; ++} ++ ++/* Write a buffer into the hardware */ ++int saa7164_buffer_activate(struct saa7164_buffer *buf, int i) ++{ ++ struct saa7164_port *port = buf->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ if ((i < 0) || (i >= port->hwcfg.buffercount)) ++ return -EINVAL; ++ ++ dprintk(DBGLVL_BUF, "%s(idx = %d)\n", __func__, i); ++ ++ buf->idx = i; /* Note of which buffer list index position we occupy */ ++ buf->flags = SAA7164_BUFFER_BUSY; ++ buf->pos = 0; ++ ++ /* TODO: Review this in light of 32v64 assignments */ ++ saa7164_writel(port->bufoffset + (sizeof(u32) * i), 0); ++ saa7164_writel(port->bufptr32h + ((sizeof(u32) * 2) * i), buf->pt_dma); ++ saa7164_writel(port->bufptr32l + ((sizeof(u32) * 2) * i), 0); ++ ++ dprintk(DBGLVL_BUF, " buf[%d] offset 0x%llx (0x%x) " ++ "buf 0x%llx/%llx (0x%x/%x) nr=%d\n", ++ buf->idx, ++ (u64)port->bufoffset + (i * sizeof(u32)), ++ saa7164_readl(port->bufoffset + (sizeof(u32) * i)), ++ (u64)port->bufptr32h + ((sizeof(u32) * 2) * i), ++ (u64)port->bufptr32l + ((sizeof(u32) * 2) * i), ++ saa7164_readl(port->bufptr32h + ((sizeof(u32) * i) * 2)), ++ saa7164_readl(port->bufptr32l + ((sizeof(u32) * i) * 2)), ++ buf->idx); ++ ++ return 0; ++} ++ ++int saa7164_buffer_cfg_port(struct saa7164_port *port) ++{ ++ struct tmHWStreamParameters *params = &port->hw_streamingparams; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct list_head *c, *n; ++ int i = 0; ++ ++ dprintk(DBGLVL_BUF, "%s(port=%d)\n", __func__, port->nr); ++ ++ saa7164_writel(port->bufcounter, 0); ++ saa7164_writel(port->pitch, params->pitch); ++ saa7164_writel(port->bufsize, params->pitch * params->numberoflines); ++ ++ dprintk(DBGLVL_BUF, " configured:\n"); ++ dprintk(DBGLVL_BUF, " lmmio 0x%p\n", dev->lmmio); ++ dprintk(DBGLVL_BUF, " bufcounter 0x%x = 0x%x\n", port->bufcounter, ++ saa7164_readl(port->bufcounter)); ++ ++ dprintk(DBGLVL_BUF, " pitch 0x%x = %d\n", port->pitch, ++ saa7164_readl(port->pitch)); ++ ++ dprintk(DBGLVL_BUF, " bufsize 0x%x = %d\n", port->bufsize, ++ saa7164_readl(port->bufsize)); ++ ++ dprintk(DBGLVL_BUF, " buffercount = %d\n", port->hwcfg.buffercount); ++ dprintk(DBGLVL_BUF, " bufoffset = 0x%x\n", port->bufoffset); ++ dprintk(DBGLVL_BUF, " bufptr32h = 0x%x\n", port->bufptr32h); ++ dprintk(DBGLVL_BUF, " bufptr32l = 0x%x\n", port->bufptr32l); ++ ++ /* Poke the buffers and offsets into PCI space */ ++ mutex_lock(&port->dmaqueue_lock); ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ ++ if (buf->flags != SAA7164_BUFFER_FREE) ++ BUG(); ++ ++ /* Place the buffer in the h/w queue */ ++ saa7164_buffer_activate(buf, i); ++ ++ /* Don't exceed the device maximum # bufs */ ++ if (i++ > port->hwcfg.buffercount) ++ BUG(); ++ ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ return 0; ++} ++ ++struct saa7164_user_buffer *saa7164_buffer_alloc_user(struct saa7164_dev *dev, ++ u32 len) ++{ ++ struct saa7164_user_buffer *buf; ++ ++ buf = kzalloc(sizeof(struct saa7164_user_buffer), GFP_KERNEL); ++ if (!buf) ++ return NULL; ++ ++ buf->data = kzalloc(len, GFP_KERNEL); ++ ++ if (!buf->data) { ++ kfree(buf); ++ return NULL; ++ } ++ ++ buf->actual_size = len; ++ buf->pos = 0; ++ buf->crc = 0; ++ ++ dprintk(DBGLVL_BUF, "%s() allocated user buffer @ 0x%p\n", ++ __func__, buf); ++ ++ return buf; ++} ++ ++void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf) ++{ ++ if (!buf) ++ return; ++ ++ kfree(buf->data); ++ buf->data = NULL; ++ ++ kfree(buf); ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c +new file mode 100644 +index 0000000..5f6f309 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-bus.c +@@ -0,0 +1,475 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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 "saa7164.h" ++ ++/* The message bus to/from the firmware is a ring buffer in PCI address ++ * space. Establish the defaults. ++ */ ++int saa7164_bus_setup(struct saa7164_dev *dev) ++{ ++ struct tmComResBusInfo *b = &dev->bus; ++ ++ mutex_init(&b->lock); ++ ++ b->Type = TYPE_BUS_PCIe; ++ b->m_wMaxReqSize = SAA_DEVICE_MAXREQUESTSIZE; ++ ++ b->m_pdwSetRing = (u8 *)(dev->bmmio + ++ ((u32)dev->busdesc.CommandRing)); ++ ++ b->m_dwSizeSetRing = SAA_DEVICE_BUFFERBLOCKSIZE; ++ ++ b->m_pdwGetRing = (u8 *)(dev->bmmio + ++ ((u32)dev->busdesc.ResponseRing)); ++ ++ b->m_dwSizeGetRing = SAA_DEVICE_BUFFERBLOCKSIZE; ++ ++ b->m_dwSetWritePos = ((u32)dev->intfdesc.BARLocation) + ++ (2 * sizeof(u64)); ++ b->m_dwSetReadPos = b->m_dwSetWritePos + (1 * sizeof(u32)); ++ ++ b->m_dwGetWritePos = b->m_dwSetWritePos + (2 * sizeof(u32)); ++ b->m_dwGetReadPos = b->m_dwSetWritePos + (3 * sizeof(u32)); ++ ++ return 0; ++} ++ ++void saa7164_bus_dump(struct saa7164_dev *dev) ++{ ++ struct tmComResBusInfo *b = &dev->bus; ++ ++ dprintk(DBGLVL_BUS, "Dumping the bus structure:\n"); ++ dprintk(DBGLVL_BUS, " .type = %d\n", b->Type); ++ dprintk(DBGLVL_BUS, " .dev->bmmio = 0x%p\n", dev->bmmio); ++ dprintk(DBGLVL_BUS, " .m_wMaxReqSize = 0x%x\n", b->m_wMaxReqSize); ++ dprintk(DBGLVL_BUS, " .m_pdwSetRing = 0x%p\n", b->m_pdwSetRing); ++ dprintk(DBGLVL_BUS, " .m_dwSizeSetRing = 0x%x\n", b->m_dwSizeSetRing); ++ dprintk(DBGLVL_BUS, " .m_pdwGetRing = 0x%p\n", b->m_pdwGetRing); ++ dprintk(DBGLVL_BUS, " .m_dwSizeGetRing = 0x%x\n", b->m_dwSizeGetRing); ++ ++ dprintk(DBGLVL_BUS, " .m_dwSetReadPos = 0x%x (0x%08x)\n", ++ b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); ++ ++ dprintk(DBGLVL_BUS, " .m_dwSetWritePos = 0x%x (0x%08x)\n", ++ b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); ++ ++ dprintk(DBGLVL_BUS, " .m_dwGetReadPos = 0x%x (0x%08x)\n", ++ b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); ++ ++ dprintk(DBGLVL_BUS, " .m_dwGetWritePos = 0x%x (0x%08x)\n", ++ b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); ++ ++} ++ ++/* Intensionally throw a BUG() if the state of the message bus looks corrupt */ ++static void saa7164_bus_verify(struct saa7164_dev *dev) ++{ ++ struct tmComResBusInfo *b = &dev->bus; ++ int bug = 0; ++ ++ if (saa7164_readl(b->m_dwSetReadPos) > b->m_dwSizeSetRing) ++ bug++; ++ ++ if (saa7164_readl(b->m_dwSetWritePos) > b->m_dwSizeSetRing) ++ bug++; ++ ++ if (saa7164_readl(b->m_dwGetReadPos) > b->m_dwSizeGetRing) ++ bug++; ++ ++ if (saa7164_readl(b->m_dwGetWritePos) > b->m_dwSizeGetRing) ++ bug++; ++ ++ if (bug) { ++ saa_debug = 0xffff; /* Ensure we get the bus dump */ ++ saa7164_bus_dump(dev); ++ saa_debug = 1024; /* Ensure we get the bus dump */ ++ BUG(); ++ } ++} ++ ++static void saa7164_bus_dumpmsg(struct saa7164_dev *dev, struct tmComResInfo *m, ++ void *buf) ++{ ++ dprintk(DBGLVL_BUS, "Dumping msg structure:\n"); ++ dprintk(DBGLVL_BUS, " .id = %d\n", m->id); ++ dprintk(DBGLVL_BUS, " .flags = 0x%x\n", m->flags); ++ dprintk(DBGLVL_BUS, " .size = 0x%x\n", m->size); ++ dprintk(DBGLVL_BUS, " .command = 0x%x\n", m->command); ++ dprintk(DBGLVL_BUS, " .controlselector = 0x%x\n", m->controlselector); ++ dprintk(DBGLVL_BUS, " .seqno = %d\n", m->seqno); ++ if (buf) ++ dprintk(DBGLVL_BUS, " .buffer (ignored)\n"); ++} ++ ++/* ++ * Places a command or a response on the bus. The implementation does not ++ * know if it is a command or a response it just places the data on the ++ * bus depending on the bus information given in the struct tmComResBusInfo ++ * structure. If the command or response does not fit into the bus ring ++ * buffer it will be refused. ++ * ++ * Return Value: ++ * SAA_OK The function executed successfully. ++ * < 0 One or more members are not initialized. ++ */ ++int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, ++ void *buf) ++{ ++ struct tmComResBusInfo *bus = &dev->bus; ++ u32 bytes_to_write, free_write_space, timeout, curr_srp, curr_swp; ++ u32 new_swp, space_rem; ++ int ret = SAA_ERR_BAD_PARAMETER; ++ ++ if (!msg) { ++ printk(KERN_ERR "%s() !msg\n", __func__); ++ return SAA_ERR_BAD_PARAMETER; ++ } ++ ++ dprintk(DBGLVL_BUS, "%s()\n", __func__); ++ ++ saa7164_bus_verify(dev); ++ ++ msg->size = cpu_to_le16(msg->size); ++ msg->command = cpu_to_le32(msg->command); ++ msg->controlselector = cpu_to_le16(msg->controlselector); ++ ++ if (msg->size > dev->bus.m_wMaxReqSize) { ++ printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", ++ __func__); ++ return SAA_ERR_BAD_PARAMETER; ++ } ++ ++ if ((msg->size > 0) && (buf == NULL)) { ++ printk(KERN_ERR "%s() Missing message buffer\n", __func__); ++ return SAA_ERR_BAD_PARAMETER; ++ } ++ ++ /* Lock the bus from any other access */ ++ mutex_lock(&bus->lock); ++ ++ bytes_to_write = sizeof(*msg) + msg->size; ++ free_write_space = 0; ++ timeout = SAA_BUS_TIMEOUT; ++ curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos)); ++ curr_swp = le32_to_cpu(saa7164_readl(bus->m_dwSetWritePos)); ++ ++ /* Deal with ring wrapping issues */ ++ if (curr_srp > curr_swp) ++ /* Deal with the wrapped ring */ ++ free_write_space = curr_srp - curr_swp; ++ else ++ /* The ring has not wrapped yet */ ++ free_write_space = (curr_srp + bus->m_dwSizeSetRing) - curr_swp; ++ ++ dprintk(DBGLVL_BUS, "%s() bytes_to_write = %d\n", __func__, ++ bytes_to_write); ++ ++ dprintk(DBGLVL_BUS, "%s() free_write_space = %d\n", __func__, ++ free_write_space); ++ ++ dprintk(DBGLVL_BUS, "%s() curr_srp = %x\n", __func__, curr_srp); ++ dprintk(DBGLVL_BUS, "%s() curr_swp = %x\n", __func__, curr_swp); ++ ++ /* Process the msg and write the content onto the bus */ ++ while (bytes_to_write >= free_write_space) { ++ ++ if (timeout-- == 0) { ++ printk(KERN_ERR "%s() bus timeout\n", __func__); ++ ret = SAA_ERR_NO_RESOURCES; ++ goto out; ++ } ++ ++ /* TODO: Review this delay, efficient? */ ++ /* Wait, allowing the hardware fetch time */ ++ mdelay(1); ++ ++ /* Check the space usage again */ ++ curr_srp = le32_to_cpu(saa7164_readl(bus->m_dwSetReadPos)); ++ ++ /* Deal with ring wrapping issues */ ++ if (curr_srp > curr_swp) ++ /* Deal with the wrapped ring */ ++ free_write_space = curr_srp - curr_swp; ++ else ++ /* Read didn't wrap around the buffer */ ++ free_write_space = (curr_srp + bus->m_dwSizeSetRing) - ++ curr_swp; ++ ++ } ++ ++ /* Calculate the new write position */ ++ new_swp = curr_swp + bytes_to_write; ++ ++ dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); ++ dprintk(DBGLVL_BUS, "%s() bus->m_dwSizeSetRing = %x\n", __func__, ++ bus->m_dwSizeSetRing); ++ ++ /* Mental Note: line 462 tmmhComResBusPCIe.cpp */ ++ ++ /* Check if we're going to wrap again */ ++ if (new_swp > bus->m_dwSizeSetRing) { ++ ++ /* Ring wraps */ ++ new_swp -= bus->m_dwSizeSetRing; ++ ++ space_rem = bus->m_dwSizeSetRing - curr_swp; ++ ++ dprintk(DBGLVL_BUS, "%s() space_rem = %x\n", __func__, ++ space_rem); ++ ++ dprintk(DBGLVL_BUS, "%s() sizeof(*msg) = %d\n", __func__, ++ (u32)sizeof(*msg)); ++ ++ if (space_rem < sizeof(*msg)) { ++ dprintk(DBGLVL_BUS, "%s() tr4\n", __func__); ++ ++ /* Split the msg into pieces as the ring wraps */ ++ memcpy(bus->m_pdwSetRing + curr_swp, msg, space_rem); ++ memcpy(bus->m_pdwSetRing, (u8 *)msg + space_rem, ++ sizeof(*msg) - space_rem); ++ ++ memcpy(bus->m_pdwSetRing + sizeof(*msg) - space_rem, ++ buf, msg->size); ++ ++ } else if (space_rem == sizeof(*msg)) { ++ dprintk(DBGLVL_BUS, "%s() tr5\n", __func__); ++ ++ /* Additional data at the beginning of the ring */ ++ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); ++ memcpy(bus->m_pdwSetRing, buf, msg->size); ++ ++ } else { ++ /* Additional data wraps around the ring */ ++ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); ++ if (msg->size > 0) { ++ memcpy(bus->m_pdwSetRing + curr_swp + ++ sizeof(*msg), buf, space_rem - ++ sizeof(*msg)); ++ memcpy(bus->m_pdwSetRing, (u8 *)buf + ++ space_rem - sizeof(*msg), ++ bytes_to_write - space_rem); ++ } ++ ++ } ++ ++ } /* (new_swp > bus->m_dwSizeSetRing) */ ++ else { ++ dprintk(DBGLVL_BUS, "%s() tr6\n", __func__); ++ ++ /* The ring buffer doesn't wrap, two simple copies */ ++ memcpy(bus->m_pdwSetRing + curr_swp, msg, sizeof(*msg)); ++ memcpy(bus->m_pdwSetRing + curr_swp + sizeof(*msg), buf, ++ msg->size); ++ } ++ ++ dprintk(DBGLVL_BUS, "%s() new_swp = %x\n", __func__, new_swp); ++ ++ /* Update the bus write position */ ++ saa7164_writel(bus->m_dwSetWritePos, cpu_to_le32(new_swp)); ++ ret = SAA_OK; ++ ++out: ++ saa7164_bus_dump(dev); ++ mutex_unlock(&bus->lock); ++ saa7164_bus_verify(dev); ++ return ret; ++} ++ ++/* ++ * Receive a command or a response from the bus. The implementation does not ++ * know if it is a command or a response it simply dequeues the data, ++ * depending on the bus information given in the struct tmComResBusInfo ++ * structure. ++ * ++ * Return Value: ++ * 0 The function executed successfully. ++ * < 0 One or more members are not initialized. ++ */ ++int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, ++ void *buf, int peekonly) ++{ ++ struct tmComResBusInfo *bus = &dev->bus; ++ u32 bytes_to_read, write_distance, curr_grp, curr_gwp, ++ new_grp, buf_size, space_rem; ++ struct tmComResInfo msg_tmp; ++ int ret = SAA_ERR_BAD_PARAMETER; ++ ++ saa7164_bus_verify(dev); ++ ++ if (msg == NULL) ++ return ret; ++ ++ if (msg->size > dev->bus.m_wMaxReqSize) { ++ printk(KERN_ERR "%s() Exceeded dev->bus.m_wMaxReqSize\n", ++ __func__); ++ return ret; ++ } ++ ++ if ((peekonly == 0) && (msg->size > 0) && (buf == NULL)) { ++ printk(KERN_ERR ++ "%s() Missing msg buf, size should be %d bytes\n", ++ __func__, msg->size); ++ return ret; ++ } ++ ++ mutex_lock(&bus->lock); ++ ++ /* Peek the bus to see if a msg exists, if it's not what we're expecting ++ * then return cleanly else read the message from the bus. ++ */ ++ curr_gwp = le32_to_cpu(saa7164_readl(bus->m_dwGetWritePos)); ++ curr_grp = le32_to_cpu(saa7164_readl(bus->m_dwGetReadPos)); ++ ++ if (curr_gwp == curr_grp) { ++ ret = SAA_ERR_EMPTY; ++ goto out; ++ } ++ ++ bytes_to_read = sizeof(*msg); ++ ++ /* Calculate write distance to current read position */ ++ write_distance = 0; ++ if (curr_gwp >= curr_grp) ++ /* Write doesn't wrap around the ring */ ++ write_distance = curr_gwp - curr_grp; ++ else ++ /* Write wraps around the ring */ ++ write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; ++ ++ if (bytes_to_read > write_distance) { ++ printk(KERN_ERR "%s() No message/response found\n", __func__); ++ ret = SAA_ERR_INVALID_COMMAND; ++ goto out; ++ } ++ ++ /* Calculate the new read position */ ++ new_grp = curr_grp + bytes_to_read; ++ if (new_grp > bus->m_dwSizeGetRing) { ++ ++ /* Ring wraps */ ++ new_grp -= bus->m_dwSizeGetRing; ++ space_rem = bus->m_dwSizeGetRing - curr_grp; ++ ++ memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, space_rem); ++ memcpy((u8 *)&msg_tmp + space_rem, bus->m_pdwGetRing, ++ bytes_to_read - space_rem); ++ ++ } else { ++ /* No wrapping */ ++ memcpy(&msg_tmp, bus->m_pdwGetRing + curr_grp, bytes_to_read); ++ } ++ ++ /* No need to update the read positions, because this was a peek */ ++ /* If the caller specifically want to peek, return */ ++ if (peekonly) { ++ memcpy(msg, &msg_tmp, sizeof(*msg)); ++ goto peekout; ++ } ++ ++ /* Check if the command/response matches what is expected */ ++ if ((msg_tmp.id != msg->id) || (msg_tmp.command != msg->command) || ++ (msg_tmp.controlselector != msg->controlselector) || ++ (msg_tmp.seqno != msg->seqno) || (msg_tmp.size != msg->size)) { ++ ++ printk(KERN_ERR "%s() Unexpected msg miss-match\n", __func__); ++ saa7164_bus_dumpmsg(dev, msg, buf); ++ saa7164_bus_dumpmsg(dev, &msg_tmp, NULL); ++ ret = SAA_ERR_INVALID_COMMAND; ++ goto out; ++ } ++ ++ /* Get the actual command and response from the bus */ ++ buf_size = msg->size; ++ ++ bytes_to_read = sizeof(*msg) + msg->size; ++ /* Calculate write distance to current read position */ ++ write_distance = 0; ++ if (curr_gwp >= curr_grp) ++ /* Write doesn't wrap around the ring */ ++ write_distance = curr_gwp - curr_grp; ++ else ++ /* Write wraps around the ring */ ++ write_distance = curr_gwp + bus->m_dwSizeGetRing - curr_grp; ++ ++ if (bytes_to_read > write_distance) { ++ printk(KERN_ERR "%s() Invalid bus state, missing msg " ++ "or mangled ring, faulty H/W / bad code?\n", __func__); ++ ret = SAA_ERR_INVALID_COMMAND; ++ goto out; ++ } ++ ++ /* Calculate the new read position */ ++ new_grp = curr_grp + bytes_to_read; ++ if (new_grp > bus->m_dwSizeGetRing) { ++ ++ /* Ring wraps */ ++ new_grp -= bus->m_dwSizeGetRing; ++ space_rem = bus->m_dwSizeGetRing - curr_grp; ++ ++ if (space_rem < sizeof(*msg)) { ++ /* msg wraps around the ring */ ++ memcpy(msg, bus->m_pdwGetRing + curr_grp, space_rem); ++ memcpy((u8 *)msg + space_rem, bus->m_pdwGetRing, ++ sizeof(*msg) - space_rem); ++ if (buf) ++ memcpy(buf, bus->m_pdwGetRing + sizeof(*msg) - ++ space_rem, buf_size); ++ ++ } else if (space_rem == sizeof(*msg)) { ++ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); ++ if (buf) ++ memcpy(buf, bus->m_pdwGetRing, buf_size); ++ } else { ++ /* Additional data wraps around the ring */ ++ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); ++ if (buf) { ++ memcpy(buf, bus->m_pdwGetRing + curr_grp + ++ sizeof(*msg), space_rem - sizeof(*msg)); ++ memcpy(buf + space_rem - sizeof(*msg), ++ bus->m_pdwGetRing, bytes_to_read - ++ space_rem); ++ } ++ ++ } ++ ++ } else { ++ /* No wrapping */ ++ memcpy(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg)); ++ if (buf) ++ memcpy(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg), ++ buf_size); ++ } ++ ++ /* Update the read positions, adjusting the ring */ ++ saa7164_writel(bus->m_dwGetReadPos, cpu_to_le32(new_grp)); ++ ++peekout: ++ msg->size = le16_to_cpu(msg->size); ++ msg->command = le32_to_cpu(msg->command); ++ msg->controlselector = le16_to_cpu(msg->controlselector); ++ ret = SAA_OK; ++out: ++ mutex_unlock(&bus->lock); ++ saa7164_bus_verify(dev); ++ return ret; ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-cards.c b/drivers/media/pci/saa7164/saa7164-cards.c +new file mode 100644 +index 0000000..5b72da5 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-cards.c +@@ -0,0 +1,773 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "saa7164.h" ++ ++/* The Bridge API needs to understand register widths (in bytes) for the ++ * attached I2C devices, so we can simplify the virtual i2c mechansms ++ * and keep the -i2c.c implementation clean. ++ */ ++#define REGLEN_8bit 1 ++#define REGLEN_16bit 2 ++ ++struct saa7164_board saa7164_boards[] = { ++ [SAA7164_BOARD_UNKNOWN] = { ++ /* Bridge will not load any firmware, without knowing ++ * the rev this would be fatal. */ ++ .name = "Unknown", ++ }, ++ [SAA7164_BOARD_UNKNOWN_REV2] = { ++ /* Bridge will load the v2 f/w and dump descriptors */ ++ /* Required during new board bringup */ ++ .name = "Generic Rev2", ++ .chiprev = SAA7164_CHIP_REV2, ++ }, ++ [SAA7164_BOARD_UNKNOWN_REV3] = { ++ /* Bridge will load the v2 f/w and dump descriptors */ ++ /* Required during new board bringup */ ++ .name = "Generic Rev3", ++ .chiprev = SAA7164_CHIP_REV3, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2200] = { ++ .name = "Hauppauge WinTV-HVR2200", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x1d, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1b, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1e, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x10 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1f, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x12 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2200_2] = { ++ .name = "Hauppauge WinTV-HVR2200", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV2, ++ .unit = {{ ++ .id = 0x06, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x05, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x10 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1e, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1f, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x12 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2200_3] = { ++ .name = "Hauppauge WinTV-HVR2200", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV2, ++ .unit = {{ ++ .id = 0x1d, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x05, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1b, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1c, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1e, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x10 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1f, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x12 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2200_4] = { ++ .name = "Hauppauge WinTV-HVR2200", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x1d, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x05, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1b, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1c, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1e, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x10 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1f, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x12 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2250] = { ++ .name = "Hauppauge WinTV-HVR2250", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x22, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x07, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x08, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x1e, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x20, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x23, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2250_2] = { ++ .name = "Hauppauge WinTV-HVR2250", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x28, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x07, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x08, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x24, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x26, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x29, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2250_3] = { ++ .name = "Hauppauge WinTV-HVR2250", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .portc = SAA7164_MPEG_ENCODER, ++ .portd = SAA7164_MPEG_ENCODER, ++ .porte = SAA7164_MPEG_VBI, ++ .portf = SAA7164_MPEG_VBI, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x26, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x07, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x08, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-1 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x22, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x24, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (TOP)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x32 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x27, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "CX24228/S5H1411-2 (QAM)", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x34 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++ [SAA7164_BOARD_HAUPPAUGE_HVR2200_5] = { ++ .name = "Hauppauge WinTV-HVR2200", ++ .porta = SAA7164_MPEG_DVB, ++ .portb = SAA7164_MPEG_DVB, ++ .chiprev = SAA7164_CHIP_REV3, ++ .unit = {{ ++ .id = 0x23, ++ .type = SAA7164_UNIT_EEPROM, ++ .name = "4K EEPROM", ++ .i2c_bus_nr = SAA7164_I2C_BUS_0, ++ .i2c_bus_addr = 0xa0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x04, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x05, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x21, ++ .type = SAA7164_UNIT_TUNER, ++ .name = "TDA18271-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0xc0 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x22, ++ .type = SAA7164_UNIT_ANALOG_DEMODULATOR, ++ .name = "TDA8290-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x84 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x24, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-1", ++ .i2c_bus_nr = SAA7164_I2C_BUS_1, ++ .i2c_bus_addr = 0x10 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ }, { ++ .id = 0x25, ++ .type = SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ .name = "TDA10048-2", ++ .i2c_bus_nr = SAA7164_I2C_BUS_2, ++ .i2c_bus_addr = 0x12 >> 1, ++ .i2c_reg_len = REGLEN_8bit, ++ } }, ++ }, ++}; ++const unsigned int saa7164_bcount = ARRAY_SIZE(saa7164_boards); ++ ++/* ------------------------------------------------------------------ */ ++/* PCI subsystem IDs */ ++ ++struct saa7164_subid saa7164_subids[] = { ++ { ++ .subvendor = 0x0070, ++ .subdevice = 0x8880, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8810, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8980, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8900, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_2, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8901, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_3, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x88A1, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_3, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8891, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8851, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2250_2, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8940, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_4, ++ }, { ++ .subvendor = 0x0070, ++ .subdevice = 0x8953, ++ .card = SAA7164_BOARD_HAUPPAUGE_HVR2200_5, ++ }, ++}; ++const unsigned int saa7164_idcount = ARRAY_SIZE(saa7164_subids); ++ ++void saa7164_card_list(struct saa7164_dev *dev) ++{ ++ int i; ++ ++ if (0 == dev->pci->subsystem_vendor && ++ 0 == dev->pci->subsystem_device) { ++ printk(KERN_ERR ++ "%s: Board has no valid PCIe Subsystem ID and can't\n" ++ "%s: be autodetected. Pass card= insmod option to\n" ++ "%s: workaround that. Send complaints to the vendor\n" ++ "%s: of the TV card. Best regards,\n" ++ "%s: -- tux\n", ++ dev->name, dev->name, dev->name, dev->name, dev->name); ++ } else { ++ printk(KERN_ERR ++ "%s: Your board isn't known (yet) to the driver.\n" ++ "%s: Try to pick one of the existing card configs via\n" ++ "%s: card= insmod option. Updating to the latest\n" ++ "%s: version might help as well.\n", ++ dev->name, dev->name, dev->name, dev->name); ++ } ++ ++ printk(KERN_ERR "%s: Here are valid choices for the card= insmod " ++ "option:\n", dev->name); ++ ++ for (i = 0; i < saa7164_bcount; i++) ++ printk(KERN_ERR "%s: card=%d -> %s\n", ++ dev->name, i, saa7164_boards[i].name); ++} ++ ++/* TODO: clean this define up into the -cards.c structs */ ++#define PCIEBRIDGE_UNITID 2 ++ ++void saa7164_gpio_setup(struct saa7164_dev *dev) ++{ ++ switch (dev->board) { ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: ++ /* ++ GPIO 2: s5h1411 / tda10048-1 demod reset ++ GPIO 3: s5h1411 / tda10048-2 demod reset ++ GPIO 7: IRBlaster Zilog reset ++ */ ++ ++ /* Reset parts by going in and out of reset */ ++ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 2); ++ saa7164_api_clear_gpiobit(dev, PCIEBRIDGE_UNITID, 3); ++ ++ msleep(20); ++ ++ saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 2); ++ saa7164_api_set_gpiobit(dev, PCIEBRIDGE_UNITID, 3); ++ break; ++ } ++} ++ ++static void hauppauge_eeprom(struct saa7164_dev *dev, u8 *eeprom_data) ++{ ++ struct tveeprom tv; ++ ++ /* TODO: Assumption: eeprom on bus 0 */ ++ tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, ++ eeprom_data); ++ ++ /* Make sure we support the board model */ ++ switch (tv.model) { ++ case 88001: ++ /* Development board - Limit circulation */ ++ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) ++ * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ ++ case 88021: ++ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) ++ * ATSC/QAM (TDA18271/S5H1411) and basic analog, MCE CIR, FM */ ++ break; ++ case 88041: ++ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) ++ * ATSC/QAM (TDA18271/S5H1411) and basic analog, no IR, FM */ ++ break; ++ case 88061: ++ /* WinTV-HVR2250 (PCIe, Retail, full-height bracket) ++ * ATSC/QAM (TDA18271/S5H1411) and basic analog, FM */ ++ break; ++ case 89519: ++ case 89609: ++ /* WinTV-HVR2200 (PCIe, Retail, full-height) ++ * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ ++ break; ++ case 89619: ++ /* WinTV-HVR2200 (PCIe, Retail, half-height) ++ * DVB-T (TDA18271/TDA10048) and basic analog, no IR */ ++ break; ++ default: ++ printk(KERN_ERR "%s: Warning: Unknown Hauppauge model #%d\n", ++ dev->name, tv.model); ++ break; ++ } ++ ++ printk(KERN_INFO "%s: Hauppauge eeprom: model=%d\n", dev->name, ++ tv.model); ++} ++ ++void saa7164_card_setup(struct saa7164_dev *dev) ++{ ++ static u8 eeprom[256]; ++ ++ if (dev->i2c_bus[0].i2c_rc == 0) { ++ if (saa7164_api_read_eeprom(dev, &eeprom[0], ++ sizeof(eeprom)) < 0) ++ return; ++ } ++ ++ switch (dev->board) { ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: ++ hauppauge_eeprom(dev, &eeprom[0]); ++ break; ++ } ++} ++ ++/* With most other drivers, the kernel expects to communicate with subdrivers ++ * through i2c. This bridge does not allow that, it does not expose any direct ++ * access to I2C. Instead we have to communicate through the device f/w for ++ * register access to 'processing units'. Each unit has a unique ++ * id, regardless of how the physical implementation occurs across ++ * the three physical i2c busses. The being said if we want leverge of ++ * the existing kernel drivers for tuners and demods we have to 'speak i2c', ++ * to this bridge implements 3 virtual i2c buses. This is a helper function ++ * for those. ++ * ++ * Description: Translate the kernels notion of an i2c address and bus into ++ * the appropriate unitid. ++ */ ++int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr) ++{ ++ /* For a given bus and i2c device address, return the saa7164 unique ++ * unitid. < 0 on error */ ++ ++ struct saa7164_dev *dev = bus->dev; ++ struct saa7164_unit *unit; ++ int i; ++ ++ for (i = 0; i < SAA7164_MAX_UNITS; i++) { ++ unit = &saa7164_boards[dev->board].unit[i]; ++ ++ if (unit->type == SAA7164_UNIT_UNDEFINED) ++ continue; ++ if ((bus->nr == unit->i2c_bus_nr) && ++ (addr == unit->i2c_bus_addr)) ++ return unit->id; ++ } ++ ++ return -1; ++} ++ ++/* The 7164 API needs to know the i2c register length in advance. ++ * this is a helper function. Based on a specific chip addr and bus return the ++ * reg length. ++ */ ++int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr) ++{ ++ /* For a given bus and i2c device address, return the ++ * saa7164 registry address width. < 0 on error ++ */ ++ ++ struct saa7164_dev *dev = bus->dev; ++ struct saa7164_unit *unit; ++ int i; ++ ++ for (i = 0; i < SAA7164_MAX_UNITS; i++) { ++ unit = &saa7164_boards[dev->board].unit[i]; ++ ++ if (unit->type == SAA7164_UNIT_UNDEFINED) ++ continue; ++ ++ if ((bus->nr == unit->i2c_bus_nr) && ++ (addr == unit->i2c_bus_addr)) ++ return unit->i2c_reg_len; ++ } ++ ++ return -1; ++} ++/* TODO: implement a 'findeeprom' functio like the above and fix any other ++ * eeprom related todo's in -api.c. ++ */ ++ ++/* Translate a unitid into a x readable device name, for display purposes. */ ++char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid) ++{ ++ char *undefed = "UNDEFINED"; ++ char *bridge = "BRIDGE"; ++ struct saa7164_unit *unit; ++ int i; ++ ++ if (unitid == 0) ++ return bridge; ++ ++ for (i = 0; i < SAA7164_MAX_UNITS; i++) { ++ unit = &saa7164_boards[dev->board].unit[i]; ++ ++ if (unit->type == SAA7164_UNIT_UNDEFINED) ++ continue; ++ ++ if (unitid == unit->id) ++ return unit->name; ++ } ++ ++ return undefed; ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c +new file mode 100644 +index 0000000..cfabcba +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-cmd.c +@@ -0,0 +1,589 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++ ++#include "saa7164.h" ++ ++static int saa7164_cmd_alloc_seqno(struct saa7164_dev *dev) ++{ ++ int i, ret = -1; ++ ++ mutex_lock(&dev->lock); ++ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { ++ if (dev->cmds[i].inuse == 0) { ++ dev->cmds[i].inuse = 1; ++ dev->cmds[i].signalled = 0; ++ dev->cmds[i].timeout = 0; ++ ret = dev->cmds[i].seqno; ++ break; ++ } ++ } ++ mutex_unlock(&dev->lock); ++ ++ return ret; ++} ++ ++static void saa7164_cmd_free_seqno(struct saa7164_dev *dev, u8 seqno) ++{ ++ mutex_lock(&dev->lock); ++ if ((dev->cmds[seqno].inuse == 1) && ++ (dev->cmds[seqno].seqno == seqno)) { ++ dev->cmds[seqno].inuse = 0; ++ dev->cmds[seqno].signalled = 0; ++ dev->cmds[seqno].timeout = 0; ++ } ++ mutex_unlock(&dev->lock); ++} ++ ++static void saa7164_cmd_timeout_seqno(struct saa7164_dev *dev, u8 seqno) ++{ ++ mutex_lock(&dev->lock); ++ if ((dev->cmds[seqno].inuse == 1) && ++ (dev->cmds[seqno].seqno == seqno)) { ++ dev->cmds[seqno].timeout = 1; ++ } ++ mutex_unlock(&dev->lock); ++} ++ ++static u32 saa7164_cmd_timeout_get(struct saa7164_dev *dev, u8 seqno) ++{ ++ int ret = 0; ++ ++ mutex_lock(&dev->lock); ++ if ((dev->cmds[seqno].inuse == 1) && ++ (dev->cmds[seqno].seqno == seqno)) { ++ ret = dev->cmds[seqno].timeout; ++ } ++ mutex_unlock(&dev->lock); ++ ++ return ret; ++} ++ ++/* Commands to the f/w get marshelled to/from this code then onto the PCI ++ * -bus/c running buffer. */ ++int saa7164_irq_dequeue(struct saa7164_dev *dev) ++{ ++ int ret = SAA_OK, i = 0; ++ u32 timeout; ++ wait_queue_head_t *q = NULL; ++ u8 tmp[512]; ++ dprintk(DBGLVL_CMD, "%s()\n", __func__); ++ ++ /* While any outstand message on the bus exists... */ ++ do { ++ ++ /* Peek the msg bus */ ++ struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 }; ++ ret = saa7164_bus_get(dev, &tRsp, NULL, 1); ++ if (ret != SAA_OK) ++ break; ++ ++ q = &dev->cmds[tRsp.seqno].wait; ++ timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); ++ dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); ++ if (!timeout) { ++ dprintk(DBGLVL_CMD, ++ "%s() signalled seqno(%d) (for dequeue)\n", ++ __func__, tRsp.seqno); ++ dev->cmds[tRsp.seqno].signalled = 1; ++ wake_up(q); ++ } else { ++ printk(KERN_ERR ++ "%s() found timed out command on the bus\n", ++ __func__); ++ ++ /* Clean the bus */ ++ ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); ++ printk(KERN_ERR "%s() ret = %x\n", __func__, ret); ++ if (ret == SAA_ERR_EMPTY) ++ /* Someone else already fetched the response */ ++ return SAA_OK; ++ ++ if (ret != SAA_OK) ++ return ret; ++ } ++ ++ /* It's unlikely to have more than 4 or 5 pending messages, ++ * ensure we exit at some point regardless. ++ */ ++ } while (i++ < 32); ++ ++ return ret; ++} ++ ++/* Commands to the f/w get marshelled to/from this code then onto the PCI ++ * -bus/c running buffer. */ ++static int saa7164_cmd_dequeue(struct saa7164_dev *dev) ++{ ++ int loop = 1; ++ int ret; ++ u32 timeout; ++ wait_queue_head_t *q = NULL; ++ u8 tmp[512]; ++ dprintk(DBGLVL_CMD, "%s()\n", __func__); ++ ++ while (loop) { ++ ++ struct tmComResInfo tRsp = { 0, 0, 0, 0, 0, 0 }; ++ ret = saa7164_bus_get(dev, &tRsp, NULL, 1); ++ if (ret == SAA_ERR_EMPTY) ++ return SAA_OK; ++ ++ if (ret != SAA_OK) ++ return ret; ++ ++ q = &dev->cmds[tRsp.seqno].wait; ++ timeout = saa7164_cmd_timeout_get(dev, tRsp.seqno); ++ dprintk(DBGLVL_CMD, "%s() timeout = %d\n", __func__, timeout); ++ if (timeout) { ++ printk(KERN_ERR "found timed out command on the bus\n"); ++ ++ /* Clean the bus */ ++ ret = saa7164_bus_get(dev, &tRsp, &tmp, 0); ++ printk(KERN_ERR "ret = %x\n", ret); ++ if (ret == SAA_ERR_EMPTY) ++ /* Someone else already fetched the response */ ++ return SAA_OK; ++ ++ if (ret != SAA_OK) ++ return ret; ++ ++ if (tRsp.flags & PVC_CMDFLAG_CONTINUE) ++ printk(KERN_ERR "split response\n"); ++ else ++ saa7164_cmd_free_seqno(dev, tRsp.seqno); ++ ++ printk(KERN_ERR " timeout continue\n"); ++ continue; ++ } ++ ++ dprintk(DBGLVL_CMD, "%s() signalled seqno(%d) (for dequeue)\n", ++ __func__, tRsp.seqno); ++ dev->cmds[tRsp.seqno].signalled = 1; ++ wake_up(q); ++ return SAA_OK; ++ } ++ ++ return SAA_OK; ++} ++ ++static int saa7164_cmd_set(struct saa7164_dev *dev, struct tmComResInfo *msg, ++ void *buf) ++{ ++ struct tmComResBusInfo *bus = &dev->bus; ++ u8 cmd_sent; ++ u16 size, idx; ++ u32 cmds; ++ void *tmp; ++ int ret = -1; ++ ++ if (!msg) { ++ printk(KERN_ERR "%s() !msg\n", __func__); ++ return SAA_ERR_BAD_PARAMETER; ++ } ++ ++ mutex_lock(&dev->cmds[msg->id].lock); ++ ++ size = msg->size; ++ idx = 0; ++ cmds = size / bus->m_wMaxReqSize; ++ if (size % bus->m_wMaxReqSize == 0) ++ cmds -= 1; ++ ++ cmd_sent = 0; ++ ++ /* Split the request into smaller chunks */ ++ for (idx = 0; idx < cmds; idx++) { ++ ++ msg->flags |= SAA_CMDFLAG_CONTINUE; ++ msg->size = bus->m_wMaxReqSize; ++ tmp = buf + idx * bus->m_wMaxReqSize; ++ ++ ret = saa7164_bus_set(dev, msg, tmp); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() set failed %d\n", __func__, ret); ++ ++ if (cmd_sent) { ++ ret = SAA_ERR_BUSY; ++ goto out; ++ } ++ ret = SAA_ERR_OVERFLOW; ++ goto out; ++ } ++ cmd_sent = 1; ++ } ++ ++ /* If not the last command... */ ++ if (idx != 0) ++ msg->flags &= ~SAA_CMDFLAG_CONTINUE; ++ ++ msg->size = size - idx * bus->m_wMaxReqSize; ++ ++ ret = saa7164_bus_set(dev, msg, buf + idx * bus->m_wMaxReqSize); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() set last failed %d\n", __func__, ret); ++ ++ if (cmd_sent) { ++ ret = SAA_ERR_BUSY; ++ goto out; ++ } ++ ret = SAA_ERR_OVERFLOW; ++ goto out; ++ } ++ ret = SAA_OK; ++ ++out: ++ mutex_unlock(&dev->cmds[msg->id].lock); ++ return ret; ++} ++ ++/* Wait for a signal event, without holding a mutex. Either return TIMEOUT if ++ * the event never occurred, or SAA_OK if it was signaled during the wait. ++ */ ++static int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno) ++{ ++ wait_queue_head_t *q = NULL; ++ int ret = SAA_BUS_TIMEOUT; ++ unsigned long stamp; ++ int r; ++ ++ if (saa_debug >= 4) ++ saa7164_bus_dump(dev); ++ ++ dprintk(DBGLVL_CMD, "%s(seqno=%d)\n", __func__, seqno); ++ ++ mutex_lock(&dev->lock); ++ if ((dev->cmds[seqno].inuse == 1) && ++ (dev->cmds[seqno].seqno == seqno)) { ++ q = &dev->cmds[seqno].wait; ++ } ++ mutex_unlock(&dev->lock); ++ ++ if (q) { ++ /* If we haven't been signalled we need to wait */ ++ if (dev->cmds[seqno].signalled == 0) { ++ stamp = jiffies; ++ dprintk(DBGLVL_CMD, ++ "%s(seqno=%d) Waiting (signalled=%d)\n", ++ __func__, seqno, dev->cmds[seqno].signalled); ++ ++ /* Wait for signalled to be flagged or timeout */ ++ /* In a highly stressed system this can easily extend ++ * into multiple seconds before the deferred worker ++ * is scheduled, and we're woken up via signal. ++ * We typically are signalled in < 50ms but it can ++ * take MUCH longer. ++ */ ++ wait_event_timeout(*q, dev->cmds[seqno].signalled, ++ (HZ * waitsecs)); ++ r = time_before(jiffies, stamp + (HZ * waitsecs)); ++ if (r) ++ ret = SAA_OK; ++ else ++ saa7164_cmd_timeout_seqno(dev, seqno); ++ ++ dprintk(DBGLVL_CMD, "%s(seqno=%d) Waiting res = %d " ++ "(signalled=%d)\n", __func__, seqno, r, ++ dev->cmds[seqno].signalled); ++ } else ++ ret = SAA_OK; ++ } else ++ printk(KERN_ERR "%s(seqno=%d) seqno is invalid\n", ++ __func__, seqno); ++ ++ return ret; ++} ++ ++void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno) ++{ ++ int i; ++ dprintk(DBGLVL_CMD, "%s()\n", __func__); ++ ++ mutex_lock(&dev->lock); ++ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { ++ if (dev->cmds[i].inuse == 1) { ++ dprintk(DBGLVL_CMD, ++ "seqno %d inuse, sig = %d, t/out = %d\n", ++ dev->cmds[i].seqno, ++ dev->cmds[i].signalled, ++ dev->cmds[i].timeout); ++ } ++ } ++ ++ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { ++ if ((dev->cmds[i].inuse == 1) && ((i == 0) || ++ (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) { ++ dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n", ++ __func__, i); ++ dev->cmds[i].signalled = 1; ++ wake_up(&dev->cmds[i].wait); ++ } ++ } ++ mutex_unlock(&dev->lock); ++} ++ ++int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command, ++ u16 controlselector, u16 size, void *buf) ++{ ++ struct tmComResInfo command_t, *pcommand_t; ++ struct tmComResInfo response_t, *presponse_t; ++ u8 errdata[256]; ++ u16 resp_dsize; ++ u16 data_recd; ++ u32 loop; ++ int ret; ++ int safety = 0; ++ ++ dprintk(DBGLVL_CMD, "%s(unitid = %s (%d) , command = 0x%x, " ++ "sel = 0x%x)\n", __func__, saa7164_unitid_name(dev, id), id, ++ command, controlselector); ++ ++ if ((size == 0) || (buf == NULL)) { ++ printk(KERN_ERR "%s() Invalid param\n", __func__); ++ return SAA_ERR_BAD_PARAMETER; ++ } ++ ++ /* Prepare some basic command/response structures */ ++ memset(&command_t, 0, sizeof(command_t)); ++ memset(&response_t, 0, sizeof(response_t)); ++ pcommand_t = &command_t; ++ presponse_t = &response_t; ++ command_t.id = id; ++ command_t.command = command; ++ command_t.controlselector = controlselector; ++ command_t.size = size; ++ ++ /* Allocate a unique sequence number */ ++ ret = saa7164_cmd_alloc_seqno(dev); ++ if (ret < 0) { ++ printk(KERN_ERR "%s() No free sequences\n", __func__); ++ ret = SAA_ERR_NO_RESOURCES; ++ goto out; ++ } ++ ++ command_t.seqno = (u8)ret; ++ ++ /* Send Command */ ++ resp_dsize = size; ++ pcommand_t->size = size; ++ ++ dprintk(DBGLVL_CMD, "%s() pcommand_t.seqno = %d\n", ++ __func__, pcommand_t->seqno); ++ ++ dprintk(DBGLVL_CMD, "%s() pcommand_t.size = %d\n", ++ __func__, pcommand_t->size); ++ ++ ret = saa7164_cmd_set(dev, pcommand_t, buf); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() set command failed %d\n", __func__, ret); ++ ++ if (ret != SAA_ERR_BUSY) ++ saa7164_cmd_free_seqno(dev, pcommand_t->seqno); ++ else ++ /* Flag a timeout, because at least one ++ * command was sent */ ++ saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); ++ ++ goto out; ++ } ++ ++ /* With split responses we have to collect the msgs piece by piece */ ++ data_recd = 0; ++ loop = 1; ++ while (loop) { ++ dprintk(DBGLVL_CMD, "%s() loop\n", __func__); ++ ++ ret = saa7164_cmd_wait(dev, pcommand_t->seqno); ++ dprintk(DBGLVL_CMD, "%s() loop ret = %d\n", __func__, ret); ++ ++ /* if power is down and this is not a power command ... */ ++ ++ if (ret == SAA_BUS_TIMEOUT) { ++ printk(KERN_ERR "Event timed out\n"); ++ saa7164_cmd_timeout_seqno(dev, pcommand_t->seqno); ++ return ret; ++ } ++ ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "spurious error\n"); ++ return ret; ++ } ++ ++ /* Peek response */ ++ ret = saa7164_bus_get(dev, presponse_t, NULL, 1); ++ if (ret == SAA_ERR_EMPTY) { ++ dprintk(4, "%s() SAA_ERR_EMPTY\n", __func__); ++ continue; ++ } ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "peek failed\n"); ++ return ret; ++ } ++ ++ dprintk(DBGLVL_CMD, "%s() presponse_t->seqno = %d\n", ++ __func__, presponse_t->seqno); ++ ++ dprintk(DBGLVL_CMD, "%s() presponse_t->flags = 0x%x\n", ++ __func__, presponse_t->flags); ++ ++ dprintk(DBGLVL_CMD, "%s() presponse_t->size = %d\n", ++ __func__, presponse_t->size); ++ ++ /* Check if the response was for our command */ ++ if (presponse_t->seqno != pcommand_t->seqno) { ++ ++ dprintk(DBGLVL_CMD, ++ "wrong event: seqno = %d, " ++ "expected seqno = %d, " ++ "will dequeue regardless\n", ++ presponse_t->seqno, pcommand_t->seqno); ++ ++ ret = saa7164_cmd_dequeue(dev); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "dequeue failed, ret = %d\n", ++ ret); ++ if (safety++ > 16) { ++ printk(KERN_ERR ++ "dequeue exceeded, safety exit\n"); ++ return SAA_ERR_BUSY; ++ } ++ } ++ ++ continue; ++ } ++ ++ if ((presponse_t->flags & PVC_RESPONSEFLAG_ERROR) != 0) { ++ ++ memset(&errdata[0], 0, sizeof(errdata)); ++ ++ ret = saa7164_bus_get(dev, presponse_t, &errdata[0], 0); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "get error(2)\n"); ++ return ret; ++ } ++ ++ saa7164_cmd_free_seqno(dev, pcommand_t->seqno); ++ ++ dprintk(DBGLVL_CMD, "%s() errdata %02x%02x%02x%02x\n", ++ __func__, errdata[0], errdata[1], errdata[2], ++ errdata[3]); ++ ++ /* Map error codes */ ++ dprintk(DBGLVL_CMD, "%s() cmd, error code = 0x%x\n", ++ __func__, errdata[0]); ++ ++ switch (errdata[0]) { ++ case PVC_ERRORCODE_INVALID_COMMAND: ++ dprintk(DBGLVL_CMD, "%s() INVALID_COMMAND\n", ++ __func__); ++ ret = SAA_ERR_INVALID_COMMAND; ++ break; ++ case PVC_ERRORCODE_INVALID_DATA: ++ dprintk(DBGLVL_CMD, "%s() INVALID_DATA\n", ++ __func__); ++ ret = SAA_ERR_BAD_PARAMETER; ++ break; ++ case PVC_ERRORCODE_TIMEOUT: ++ dprintk(DBGLVL_CMD, "%s() TIMEOUT\n", __func__); ++ ret = SAA_ERR_TIMEOUT; ++ break; ++ case PVC_ERRORCODE_NAK: ++ dprintk(DBGLVL_CMD, "%s() NAK\n", __func__); ++ ret = SAA_ERR_NULL_PACKET; ++ break; ++ case PVC_ERRORCODE_UNKNOWN: ++ case PVC_ERRORCODE_INVALID_CONTROL: ++ dprintk(DBGLVL_CMD, ++ "%s() UNKNOWN OR INVALID CONTROL\n", ++ __func__); ++ default: ++ dprintk(DBGLVL_CMD, "%s() UNKNOWN\n", __func__); ++ ret = SAA_ERR_NOT_SUPPORTED; ++ } ++ ++ /* See of other commands are on the bus */ ++ if (saa7164_cmd_dequeue(dev) != SAA_OK) ++ printk(KERN_ERR "dequeue(2) failed\n"); ++ ++ return ret; ++ } ++ ++ /* If response is invalid */ ++ if ((presponse_t->id != pcommand_t->id) || ++ (presponse_t->command != pcommand_t->command) || ++ (presponse_t->controlselector != ++ pcommand_t->controlselector) || ++ (((resp_dsize - data_recd) != presponse_t->size) && ++ !(presponse_t->flags & PVC_CMDFLAG_CONTINUE)) || ++ ((resp_dsize - data_recd) < presponse_t->size)) { ++ ++ /* Invalid */ ++ dprintk(DBGLVL_CMD, "%s() Invalid\n", __func__); ++ ret = saa7164_bus_get(dev, presponse_t, NULL, 0); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "get failed\n"); ++ return ret; ++ } ++ ++ /* See of other commands are on the bus */ ++ if (saa7164_cmd_dequeue(dev) != SAA_OK) ++ printk(KERN_ERR "dequeue(3) failed\n"); ++ continue; ++ } ++ ++ /* OK, now we're actually getting out correct response */ ++ ret = saa7164_bus_get(dev, presponse_t, buf + data_recd, 0); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "get failed\n"); ++ return ret; ++ } ++ ++ data_recd = presponse_t->size + data_recd; ++ if (resp_dsize == data_recd) { ++ dprintk(DBGLVL_CMD, "%s() Resp recd\n", __func__); ++ break; ++ } ++ ++ /* See of other commands are on the bus */ ++ if (saa7164_cmd_dequeue(dev) != SAA_OK) ++ printk(KERN_ERR "dequeue(3) failed\n"); ++ ++ continue; ++ ++ } /* (loop) */ ++ ++ /* Release the sequence number allocation */ ++ saa7164_cmd_free_seqno(dev, pcommand_t->seqno); ++ ++ /* if powerdown signal all pending commands */ ++ ++ dprintk(DBGLVL_CMD, "%s() Calling dequeue then exit\n", __func__); ++ ++ /* See of other commands are on the bus */ ++ if (saa7164_cmd_dequeue(dev) != SAA_OK) ++ printk(KERN_ERR "dequeue(4) failed\n"); ++ ++ ret = SAA_OK; ++out: ++ return ret; ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c +new file mode 100644 +index 0000000..063047f +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-core.c +@@ -0,0 +1,1488 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_PROC_FS ++#include ++#endif ++#include "saa7164.h" ++ ++MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); ++MODULE_AUTHOR("Steven Toth "); ++MODULE_LICENSE("GPL"); ++ ++/* ++ * 1 Basic ++ * 2 ++ * 4 i2c ++ * 8 api ++ * 16 cmd ++ * 32 bus ++ */ ++ ++unsigned int saa_debug; ++module_param_named(debug, saa_debug, int, 0644); ++MODULE_PARM_DESC(debug, "enable debug messages"); ++ ++unsigned int fw_debug; ++module_param(fw_debug, int, 0644); ++MODULE_PARM_DESC(fw_debug, "Firware debug level def:2"); ++ ++unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS; ++module_param(encoder_buffers, int, 0644); ++MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64"); ++ ++unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS; ++module_param(vbi_buffers, int, 0644); ++MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64"); ++ ++unsigned int waitsecs = 10; ++module_param(waitsecs, int, 0644); ++MODULE_PARM_DESC(waitsecs, "timeout on firmware messages"); ++ ++static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; ++module_param_array(card, int, NULL, 0444); ++MODULE_PARM_DESC(card, "card type"); ++ ++unsigned int print_histogram = 64; ++module_param(print_histogram, int, 0644); ++MODULE_PARM_DESC(print_histogram, "print histogram values once"); ++ ++unsigned int crc_checking = 1; ++module_param(crc_checking, int, 0644); ++MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); ++ ++unsigned int guard_checking = 1; ++module_param(guard_checking, int, 0644); ++MODULE_PARM_DESC(guard_checking, ++ "enable dma sanity checking for buffer overruns"); ++ ++static unsigned int saa7164_devcount; ++ ++static DEFINE_MUTEX(devlist); ++LIST_HEAD(saa7164_devlist); ++ ++#define INT_SIZE 16 ++ ++static void saa7164_pack_verifier(struct saa7164_buffer *buf) ++{ ++ u8 *p = (u8 *)buf->cpu; ++ int i; ++ ++ for (i = 0; i < buf->actual_size; i += 2048) { ++ ++ if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || ++ (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) { ++ printk(KERN_ERR "No pack at 0x%x\n", i); ++#if 0 ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, ++ p + 1, 32, false); ++#endif ++ } ++ } ++} ++ ++#define FIXED_VIDEO_PID 0xf1 ++#define FIXED_AUDIO_PID 0xf2 ++ ++static void saa7164_ts_verifier(struct saa7164_buffer *buf) ++{ ++ struct saa7164_port *port = buf->port; ++ u32 i; ++ u8 cc, a; ++ u16 pid; ++ u8 __iomem *bufcpu = (u8 *)buf->cpu; ++ ++ port->sync_errors = 0; ++ port->v_cc_errors = 0; ++ port->a_cc_errors = 0; ++ ++ for (i = 0; i < buf->actual_size; i += 188) { ++ if (*(bufcpu + i) != 0x47) ++ port->sync_errors++; ++ ++ /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */ ++ pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2); ++ cc = *(bufcpu + i + 3) & 0x0f; ++ ++ if (pid == FIXED_VIDEO_PID) { ++ a = ((port->last_v_cc + 1) & 0x0f); ++ if (a != cc) { ++ printk(KERN_ERR "video cc last = %x current = %x i = %d\n", ++ port->last_v_cc, cc, i); ++ port->v_cc_errors++; ++ } ++ ++ port->last_v_cc = cc; ++ } else ++ if (pid == FIXED_AUDIO_PID) { ++ a = ((port->last_a_cc + 1) & 0x0f); ++ if (a != cc) { ++ printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", ++ port->last_a_cc, cc, i); ++ port->a_cc_errors++; ++ } ++ ++ port->last_a_cc = cc; ++ } ++ ++ } ++ ++ /* Only report errors if we've been through this function atleast ++ * once already and the cached cc values are primed. First time through ++ * always generates errors. ++ */ ++ if (port->v_cc_errors && (port->done_first_interrupt > 1)) ++ printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors); ++ ++ if (port->a_cc_errors && (port->done_first_interrupt > 1)) ++ printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors); ++ ++ if (port->sync_errors && (port->done_first_interrupt > 1)) ++ printk(KERN_ERR "sync_errors = %d\n", port->sync_errors); ++ ++ if (port->done_first_interrupt == 1) ++ port->done_first_interrupt++; ++} ++ ++static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name) ++{ ++ int i; ++ ++ memset(hg, 0, sizeof(struct saa7164_histogram)); ++ strcpy(hg->name, name); ++ ++ /* First 30ms x 1ms */ ++ for (i = 0; i < 30; i++) ++ hg->counter1[0 + i].val = i; ++ ++ /* 30 - 200ms x 10ms */ ++ for (i = 0; i < 18; i++) ++ hg->counter1[30 + i].val = 30 + (i * 10); ++ ++ /* 200 - 2000ms x 100ms */ ++ for (i = 0; i < 15; i++) ++ hg->counter1[48 + i].val = 200 + (i * 200); ++ ++ /* Catch all massive value (2secs) */ ++ hg->counter1[55].val = 2000; ++ ++ /* Catch all massive value (4secs) */ ++ hg->counter1[56].val = 4000; ++ ++ /* Catch all massive value (8secs) */ ++ hg->counter1[57].val = 8000; ++ ++ /* Catch all massive value (15secs) */ ++ hg->counter1[58].val = 15000; ++ ++ /* Catch all massive value (30secs) */ ++ hg->counter1[59].val = 30000; ++ ++ /* Catch all massive value (60secs) */ ++ hg->counter1[60].val = 60000; ++ ++ /* Catch all massive value (5mins) */ ++ hg->counter1[61].val = 300000; ++ ++ /* Catch all massive value (15mins) */ ++ hg->counter1[62].val = 900000; ++ ++ /* Catch all massive values (1hr) */ ++ hg->counter1[63].val = 3600000; ++} ++ ++void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val) ++{ ++ int i; ++ for (i = 0; i < 64; i++) { ++ if (val <= hg->counter1[i].val) { ++ hg->counter1[i].count++; ++ hg->counter1[i].update_time = jiffies; ++ break; ++ } ++ } ++} ++ ++static void saa7164_histogram_print(struct saa7164_port *port, ++ struct saa7164_histogram *hg) ++{ ++ u32 entries = 0; ++ int i; ++ ++ printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name); ++ for (i = 0; i < 64; i++) { ++ if (hg->counter1[i].count == 0) ++ continue; ++ ++ printk(KERN_ERR " %4d %12d %Ld\n", ++ hg->counter1[i].val, ++ hg->counter1[i].count, ++ hg->counter1[i].update_time); ++ ++ entries++; ++ } ++ printk(KERN_ERR "Total: %d\n", entries); ++} ++ ++static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf = NULL; ++ struct saa7164_user_buffer *ubuf = NULL; ++ struct list_head *c, *n; ++ int i = 0; ++ u8 __iomem *p; ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ ++ buf = list_entry(c, struct saa7164_buffer, list); ++ if (i++ > port->hwcfg.buffercount) { ++ printk(KERN_ERR "%s() illegal i count %d\n", ++ __func__, i); ++ break; ++ } ++ ++ if (buf->idx == bufnr) { ++ ++ /* Found the buffer, deal with it */ ++ dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr); ++ ++ if (crc_checking) { ++ /* Throw a new checksum on the dma buffer */ ++ buf->crc = crc32(0, buf->cpu, buf->actual_size); ++ } ++ ++ if (guard_checking) { ++ p = (u8 *)buf->cpu; ++ if ((*(p + buf->actual_size + 0) != 0xff) || ++ (*(p + buf->actual_size + 1) != 0xff) || ++ (*(p + buf->actual_size + 2) != 0xff) || ++ (*(p + buf->actual_size + 3) != 0xff) || ++ (*(p + buf->actual_size + 0x10) != 0xff) || ++ (*(p + buf->actual_size + 0x11) != 0xff) || ++ (*(p + buf->actual_size + 0x12) != 0xff) || ++ (*(p + buf->actual_size + 0x13) != 0xff)) { ++ printk(KERN_ERR "%s() buf %p guard buffer breach\n", ++ __func__, buf); ++#if 0 ++ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, ++ p + buf->actual_size - 32, 64, false); ++#endif ++ } ++ } ++ ++ if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) { ++ /* Validate the incoming buffer content */ ++ if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) ++ saa7164_ts_verifier(buf); ++ else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) ++ saa7164_pack_verifier(buf); ++ } ++ ++ /* find a free user buffer and clone to it */ ++ if (!list_empty(&port->list_buf_free.list)) { ++ ++ /* Pull the first buffer from the used list */ ++ ubuf = list_first_entry(&port->list_buf_free.list, ++ struct saa7164_user_buffer, list); ++ ++ if (buf->actual_size <= ubuf->actual_size) { ++ ++ memcpy_fromio(ubuf->data, buf->cpu, ++ ubuf->actual_size); ++ ++ if (crc_checking) { ++ /* Throw a new checksum on the read buffer */ ++ ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size); ++ } ++ ++ /* Requeue the buffer on the free list */ ++ ubuf->pos = 0; ++ ++ list_move_tail(&ubuf->list, ++ &port->list_buf_used.list); ++ ++ /* Flag any userland waiters */ ++ wake_up_interruptible(&port->wait_read); ++ ++ } else { ++ printk(KERN_ERR "buf %p bufsize fails match\n", buf); ++ } ++ ++ } else ++ printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n"); ++ ++ /* Ensure offset into buffer remains 0, fill buffer ++ * with known bad data. We check for this data at a later point ++ * in time. */ ++ saa7164_buffer_zero_offsets(port, bufnr); ++ memset_io(buf->cpu, 0xff, buf->pci_size); ++ if (crc_checking) { ++ /* Throw yet aanother new checksum on the dma buffer */ ++ buf->crc = crc32(0, buf->cpu, buf->actual_size); ++ } ++ ++ break; ++ } ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++} ++ ++static void saa7164_work_enchandler(struct work_struct *w) ++{ ++ struct saa7164_port *port = ++ container_of(w, struct saa7164_port, workenc); ++ struct saa7164_dev *dev = port->dev; ++ ++ u32 wp, mcb, rp, cnt = 0; ++ ++ port->last_svc_msecs_diff = port->last_svc_msecs; ++ port->last_svc_msecs = jiffies_to_msecs(jiffies); ++ ++ port->last_svc_msecs_diff = port->last_svc_msecs - ++ port->last_svc_msecs_diff; ++ ++ saa7164_histogram_update(&port->svc_interval, ++ port->last_svc_msecs_diff); ++ ++ port->last_irq_svc_msecs_diff = port->last_svc_msecs - ++ port->last_irq_msecs; ++ ++ saa7164_histogram_update(&port->irq_svc_interval, ++ port->last_irq_svc_msecs_diff); ++ ++ dprintk(DBGLVL_IRQ, ++ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", ++ __func__, ++ port->last_svc_msecs_diff, ++ port->last_irq_svc_msecs_diff, ++ port->last_svc_wp, ++ port->last_svc_rp ++ ); ++ ++ /* Current write position */ ++ wp = saa7164_readl(port->bufcounter); ++ if (wp > (port->hwcfg.buffercount - 1)) { ++ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); ++ return; ++ } ++ ++ /* Most current complete buffer */ ++ if (wp == 0) ++ mcb = (port->hwcfg.buffercount - 1); ++ else ++ mcb = wp - 1; ++ ++ while (1) { ++ if (port->done_first_interrupt == 0) { ++ port->done_first_interrupt++; ++ rp = mcb; ++ } else ++ rp = (port->last_svc_rp + 1) % 8; ++ ++ if (rp > (port->hwcfg.buffercount - 1)) { ++ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); ++ break; ++ } ++ ++ saa7164_work_enchandler_helper(port, rp); ++ port->last_svc_rp = rp; ++ cnt++; ++ ++ if (rp == mcb) ++ break; ++ } ++ ++ /* TODO: Convert this into a /proc/saa7164 style readable file */ ++ if (print_histogram == port->nr) { ++ saa7164_histogram_print(port, &port->irq_interval); ++ saa7164_histogram_print(port, &port->svc_interval); ++ saa7164_histogram_print(port, &port->irq_svc_interval); ++ saa7164_histogram_print(port, &port->read_interval); ++ saa7164_histogram_print(port, &port->poll_interval); ++ /* TODO: fix this to preserve any previous state */ ++ print_histogram = 64 + port->nr; ++ } ++} ++ ++static void saa7164_work_vbihandler(struct work_struct *w) ++{ ++ struct saa7164_port *port = ++ container_of(w, struct saa7164_port, workenc); ++ struct saa7164_dev *dev = port->dev; ++ ++ u32 wp, mcb, rp, cnt = 0; ++ ++ port->last_svc_msecs_diff = port->last_svc_msecs; ++ port->last_svc_msecs = jiffies_to_msecs(jiffies); ++ port->last_svc_msecs_diff = port->last_svc_msecs - ++ port->last_svc_msecs_diff; ++ ++ saa7164_histogram_update(&port->svc_interval, ++ port->last_svc_msecs_diff); ++ ++ port->last_irq_svc_msecs_diff = port->last_svc_msecs - ++ port->last_irq_msecs; ++ ++ saa7164_histogram_update(&port->irq_svc_interval, ++ port->last_irq_svc_msecs_diff); ++ ++ dprintk(DBGLVL_IRQ, ++ "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", ++ __func__, ++ port->last_svc_msecs_diff, ++ port->last_irq_svc_msecs_diff, ++ port->last_svc_wp, ++ port->last_svc_rp ++ ); ++ ++ /* Current write position */ ++ wp = saa7164_readl(port->bufcounter); ++ if (wp > (port->hwcfg.buffercount - 1)) { ++ printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); ++ return; ++ } ++ ++ /* Most current complete buffer */ ++ if (wp == 0) ++ mcb = (port->hwcfg.buffercount - 1); ++ else ++ mcb = wp - 1; ++ ++ while (1) { ++ if (port->done_first_interrupt == 0) { ++ port->done_first_interrupt++; ++ rp = mcb; ++ } else ++ rp = (port->last_svc_rp + 1) % 8; ++ ++ if (rp > (port->hwcfg.buffercount - 1)) { ++ printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); ++ break; ++ } ++ ++ saa7164_work_enchandler_helper(port, rp); ++ port->last_svc_rp = rp; ++ cnt++; ++ ++ if (rp == mcb) ++ break; ++ } ++ ++ /* TODO: Convert this into a /proc/saa7164 style readable file */ ++ if (print_histogram == port->nr) { ++ saa7164_histogram_print(port, &port->irq_interval); ++ saa7164_histogram_print(port, &port->svc_interval); ++ saa7164_histogram_print(port, &port->irq_svc_interval); ++ saa7164_histogram_print(port, &port->read_interval); ++ saa7164_histogram_print(port, &port->poll_interval); ++ /* TODO: fix this to preserve any previous state */ ++ print_histogram = 64 + port->nr; ++ } ++} ++ ++static void saa7164_work_cmdhandler(struct work_struct *w) ++{ ++ struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd); ++ ++ /* Wake up any complete commands */ ++ saa7164_irq_dequeue(dev); ++} ++ ++static void saa7164_buffer_deliver(struct saa7164_buffer *buf) ++{ ++ struct saa7164_port *port = buf->port; ++ ++ /* Feed the transport payload into the kernel demux */ ++ dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu, ++ SAA7164_TS_NUMBER_OF_LINES); ++ ++} ++ ++static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ /* Store old time */ ++ port->last_irq_msecs_diff = port->last_irq_msecs; ++ ++ /* Collect new stats */ ++ port->last_irq_msecs = jiffies_to_msecs(jiffies); ++ ++ /* Calculate stats */ ++ port->last_irq_msecs_diff = port->last_irq_msecs - ++ port->last_irq_msecs_diff; ++ ++ saa7164_histogram_update(&port->irq_interval, ++ port->last_irq_msecs_diff); ++ ++ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, ++ port->last_irq_msecs_diff); ++ ++ /* Tis calls the vbi irq handler */ ++ schedule_work(&port->workenc); ++ return 0; ++} ++ ++static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ /* Store old time */ ++ port->last_irq_msecs_diff = port->last_irq_msecs; ++ ++ /* Collect new stats */ ++ port->last_irq_msecs = jiffies_to_msecs(jiffies); ++ ++ /* Calculate stats */ ++ port->last_irq_msecs_diff = port->last_irq_msecs - ++ port->last_irq_msecs_diff; ++ ++ saa7164_histogram_update(&port->irq_interval, ++ port->last_irq_msecs_diff); ++ ++ dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, ++ port->last_irq_msecs_diff); ++ ++ schedule_work(&port->workenc); ++ return 0; ++} ++ ++static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct list_head *c, *n; ++ int wp, i = 0, rp; ++ ++ /* Find the current write point from the hardware */ ++ wp = saa7164_readl(port->bufcounter); ++ if (wp > (port->hwcfg.buffercount - 1)) ++ BUG(); ++ ++ /* Find the previous buffer to the current write point */ ++ if (wp == 0) ++ rp = (port->hwcfg.buffercount - 1); ++ else ++ rp = wp - 1; ++ ++ /* Lookup the WP in the buffer list */ ++ /* TODO: turn this into a worker thread */ ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ if (i++ > port->hwcfg.buffercount) ++ BUG(); ++ ++ if (buf->idx == rp) { ++ /* Found the buffer, deal with it */ ++ dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n", ++ __func__, wp, rp); ++ saa7164_buffer_deliver(buf); ++ break; ++ } ++ ++ } ++ return 0; ++} ++ ++/* Primary IRQ handler and dispatch mechanism */ ++static irqreturn_t saa7164_irq(int irq, void *dev_id) ++{ ++ struct saa7164_dev *dev = dev_id; ++ struct saa7164_port *porta = &dev->ports[SAA7164_PORT_TS1]; ++ struct saa7164_port *portb = &dev->ports[SAA7164_PORT_TS2]; ++ struct saa7164_port *portc = &dev->ports[SAA7164_PORT_ENC1]; ++ struct saa7164_port *portd = &dev->ports[SAA7164_PORT_ENC2]; ++ struct saa7164_port *porte = &dev->ports[SAA7164_PORT_VBI1]; ++ struct saa7164_port *portf = &dev->ports[SAA7164_PORT_VBI2]; ++ ++ u32 intid, intstat[INT_SIZE/4]; ++ int i, handled = 0, bit; ++ ++ if (dev == NULL) { ++ printk(KERN_ERR "%s() No device specified\n", __func__); ++ handled = 0; ++ goto out; ++ } ++ ++ /* Check that the hardware is accessible. If the status bytes are ++ * 0xFF then the device is not accessible, the the IRQ belongs ++ * to another driver. ++ * 4 x u32 interrupt registers. ++ */ ++ for (i = 0; i < INT_SIZE/4; i++) { ++ ++ /* TODO: Convert into saa7164_readl() */ ++ /* Read the 4 hardware interrupt registers */ ++ intstat[i] = saa7164_readl(dev->int_status + (i * 4)); ++ ++ if (intstat[i]) ++ handled = 1; ++ } ++ if (handled == 0) ++ goto out; ++ ++ /* For each of the HW interrupt registers */ ++ for (i = 0; i < INT_SIZE/4; i++) { ++ ++ if (intstat[i]) { ++ /* Each function of the board has it's own interruptid. ++ * Find the function that triggered then call ++ * it's handler. ++ */ ++ for (bit = 0; bit < 32; bit++) { ++ ++ if (((intstat[i] >> bit) & 0x00000001) == 0) ++ continue; ++ ++ /* Calculate the interrupt id (0x00 to 0x7f) */ ++ ++ intid = (i * 32) + bit; ++ if (intid == dev->intfdesc.bInterruptId) { ++ /* A response to an cmd/api call */ ++ schedule_work(&dev->workcmd); ++ } else if (intid == porta->hwcfg.interruptid) { ++ ++ /* Transport path 1 */ ++ saa7164_irq_ts(porta); ++ ++ } else if (intid == portb->hwcfg.interruptid) { ++ ++ /* Transport path 2 */ ++ saa7164_irq_ts(portb); ++ ++ } else if (intid == portc->hwcfg.interruptid) { ++ ++ /* Encoder path 1 */ ++ saa7164_irq_encoder(portc); ++ ++ } else if (intid == portd->hwcfg.interruptid) { ++ ++ /* Encoder path 2 */ ++ saa7164_irq_encoder(portd); ++ ++ } else if (intid == porte->hwcfg.interruptid) { ++ ++ /* VBI path 1 */ ++ saa7164_irq_vbi(porte); ++ ++ } else if (intid == portf->hwcfg.interruptid) { ++ ++ /* VBI path 2 */ ++ saa7164_irq_vbi(portf); ++ ++ } else { ++ /* Find the function */ ++ dprintk(DBGLVL_IRQ, ++ "%s() unhandled interrupt " ++ "reg 0x%x bit 0x%x " ++ "intid = 0x%x\n", ++ __func__, i, bit, intid); ++ } ++ } ++ ++ /* Ack it */ ++ saa7164_writel(dev->int_ack + (i * 4), intstat[i]); ++ ++ } ++ } ++out: ++ return IRQ_RETVAL(handled); ++} ++ ++void saa7164_getfirmwarestatus(struct saa7164_dev *dev) ++{ ++ struct saa7164_fw_status *s = &dev->fw_status; ++ ++ dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS); ++ dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE); ++ dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC); ++ dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST); ++ dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD); ++ dev->fw_status.remainheap = ++ saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP); ++ ++ dprintk(1, "Firmware status:\n"); ++ dprintk(1, " .status = 0x%08x\n", s->status); ++ dprintk(1, " .mode = 0x%08x\n", s->mode); ++ dprintk(1, " .spec = 0x%08x\n", s->spec); ++ dprintk(1, " .inst = 0x%08x\n", s->inst); ++ dprintk(1, " .cpuload = 0x%08x\n", s->cpuload); ++ dprintk(1, " .remainheap = 0x%08x\n", s->remainheap); ++} ++ ++u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) ++{ ++ u32 reg; ++ ++ reg = saa7164_readl(SAA_DEVICE_VERSION); ++ dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n", ++ (reg & 0x0000fc00) >> 10, ++ (reg & 0x000003e0) >> 5, ++ (reg & 0x0000001f), ++ (reg & 0xffff0000) >> 16, ++ reg); ++ ++ return reg; ++} ++ ++/* TODO: Debugging func, remove */ ++void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) ++{ ++ int i; ++ ++ dprintk(1, "--------------------> " ++ "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); ++ ++ for (i = 0; i < 0x100; i += 16) ++ dprintk(1, "region0[0x%08x] = " ++ "%02x %02x %02x %02x %02x %02x %02x %02x" ++ " %02x %02x %02x %02x %02x %02x %02x %02x\n", i, ++ (u8)saa7164_readb(addr + i + 0), ++ (u8)saa7164_readb(addr + i + 1), ++ (u8)saa7164_readb(addr + i + 2), ++ (u8)saa7164_readb(addr + i + 3), ++ (u8)saa7164_readb(addr + i + 4), ++ (u8)saa7164_readb(addr + i + 5), ++ (u8)saa7164_readb(addr + i + 6), ++ (u8)saa7164_readb(addr + i + 7), ++ (u8)saa7164_readb(addr + i + 8), ++ (u8)saa7164_readb(addr + i + 9), ++ (u8)saa7164_readb(addr + i + 10), ++ (u8)saa7164_readb(addr + i + 11), ++ (u8)saa7164_readb(addr + i + 12), ++ (u8)saa7164_readb(addr + i + 13), ++ (u8)saa7164_readb(addr + i + 14), ++ (u8)saa7164_readb(addr + i + 15) ++ ); ++} ++ ++static void saa7164_dump_hwdesc(struct saa7164_dev *dev) ++{ ++ dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n", ++ &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr)); ++ ++ dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength); ++ dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType); ++ dprintk(1, " .bDescriptorSubtype = 0x%x\n", ++ dev->hwdesc.bDescriptorSubtype); ++ ++ dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion); ++ dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency); ++ dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes); ++ dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities); ++ dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n", ++ dev->hwdesc.dwDeviceRegistersLocation); ++ ++ dprintk(1, " .dwHostMemoryRegion = 0x%x\n", ++ dev->hwdesc.dwHostMemoryRegion); ++ ++ dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n", ++ dev->hwdesc.dwHostMemoryRegionSize); ++ ++ dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n", ++ dev->hwdesc.dwHostHibernatMemRegion); ++ ++ dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n", ++ dev->hwdesc.dwHostHibernatMemRegionSize); ++} ++ ++static void saa7164_dump_intfdesc(struct saa7164_dev *dev) ++{ ++ dprintk(1, "@0x%p intfdesc " ++ "sizeof(struct tmComResInterfaceDescr) = %d bytes\n", ++ &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr)); ++ ++ dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength); ++ dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType); ++ dprintk(1, " .bDescriptorSubtype = 0x%x\n", ++ dev->intfdesc.bDescriptorSubtype); ++ ++ dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags); ++ dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType); ++ dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId); ++ dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface); ++ dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId); ++ dprintk(1, " .bDebugInterruptId = 0x%x\n", ++ dev->intfdesc.bDebugInterruptId); ++ ++ dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation); ++} ++ ++static void saa7164_dump_busdesc(struct saa7164_dev *dev) ++{ ++ dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n", ++ &dev->busdesc, (u32)sizeof(struct tmComResBusDescr)); ++ ++ dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing); ++ dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing); ++ dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite); ++ dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead); ++ dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite); ++ dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead); ++} ++ ++/* Much of the hardware configuration and PCI registers are configured ++ * dynamically depending on firmware. We have to cache some initial ++ * structures then use these to locate other important structures ++ * from PCI space. ++ */ ++static void saa7164_get_descriptors(struct saa7164_dev *dev) ++{ ++ memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr)); ++ memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr), ++ sizeof(struct tmComResInterfaceDescr)); ++ memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation, ++ sizeof(struct tmComResBusDescr)); ++ ++ if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) { ++ printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n"); ++ printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength, ++ (u32)sizeof(struct tmComResHWDescr)); ++ } else ++ saa7164_dump_hwdesc(dev); ++ ++ if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) { ++ printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n"); ++ printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength, ++ (u32)sizeof(struct tmComResInterfaceDescr)); ++ } else ++ saa7164_dump_intfdesc(dev); ++ ++ saa7164_dump_busdesc(dev); ++} ++ ++static int saa7164_pci_quirks(struct saa7164_dev *dev) ++{ ++ return 0; ++} ++ ++static int get_resources(struct saa7164_dev *dev) ++{ ++ if (request_mem_region(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0), dev->name)) { ++ ++ if (request_mem_region(pci_resource_start(dev->pci, 2), ++ pci_resource_len(dev->pci, 2), dev->name)) ++ return 0; ++ } ++ ++ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", ++ dev->name, ++ (u64)pci_resource_start(dev->pci, 0), ++ (u64)pci_resource_start(dev->pci, 2)); ++ ++ return -EBUSY; ++} ++ ++static int saa7164_port_init(struct saa7164_dev *dev, int portnr) ++{ ++ struct saa7164_port *port = NULL; ++ ++ if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)) ++ BUG(); ++ ++ port = &dev->ports[portnr]; ++ ++ port->dev = dev; ++ port->nr = portnr; ++ ++ if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2)) ++ port->type = SAA7164_MPEG_DVB; ++ else ++ if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) { ++ port->type = SAA7164_MPEG_ENCODER; ++ ++ /* We need a deferred interrupt handler for cmd handling */ ++ INIT_WORK(&port->workenc, saa7164_work_enchandler); ++ } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) { ++ port->type = SAA7164_MPEG_VBI; ++ ++ /* We need a deferred interrupt handler for cmd handling */ ++ INIT_WORK(&port->workenc, saa7164_work_vbihandler); ++ } else ++ BUG(); ++ ++ /* Init all the critical resources */ ++ mutex_init(&port->dvb.lock); ++ INIT_LIST_HEAD(&port->dmaqueue.list); ++ mutex_init(&port->dmaqueue_lock); ++ ++ INIT_LIST_HEAD(&port->list_buf_used.list); ++ INIT_LIST_HEAD(&port->list_buf_free.list); ++ init_waitqueue_head(&port->wait_read); ++ ++ ++ saa7164_histogram_reset(&port->irq_interval, "irq intervals"); ++ saa7164_histogram_reset(&port->svc_interval, "deferred intervals"); ++ saa7164_histogram_reset(&port->irq_svc_interval, ++ "irq to deferred intervals"); ++ saa7164_histogram_reset(&port->read_interval, ++ "encoder/vbi read() intervals"); ++ saa7164_histogram_reset(&port->poll_interval, ++ "encoder/vbi poll() intervals"); ++ ++ return 0; ++} ++ ++static int saa7164_dev_setup(struct saa7164_dev *dev) ++{ ++ int i; ++ ++ mutex_init(&dev->lock); ++ atomic_inc(&dev->refcount); ++ dev->nr = saa7164_devcount++; ++ ++ snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr); ++ ++ mutex_lock(&devlist); ++ list_add_tail(&dev->devlist, &saa7164_devlist); ++ mutex_unlock(&devlist); ++ ++ /* board config */ ++ dev->board = UNSET; ++ if (card[dev->nr] < saa7164_bcount) ++ dev->board = card[dev->nr]; ++ ++ for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++) ++ if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor && ++ dev->pci->subsystem_device == ++ saa7164_subids[i].subdevice) ++ dev->board = saa7164_subids[i].card; ++ ++ if (UNSET == dev->board) { ++ dev->board = SAA7164_BOARD_UNKNOWN; ++ saa7164_card_list(dev); ++ } ++ ++ dev->pci_bus = dev->pci->bus->number; ++ dev->pci_slot = PCI_SLOT(dev->pci->devfn); ++ ++ /* I2C Defaults / setup */ ++ dev->i2c_bus[0].dev = dev; ++ dev->i2c_bus[0].nr = 0; ++ dev->i2c_bus[1].dev = dev; ++ dev->i2c_bus[1].nr = 1; ++ dev->i2c_bus[2].dev = dev; ++ dev->i2c_bus[2].nr = 2; ++ ++ /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */ ++ saa7164_port_init(dev, SAA7164_PORT_TS1); ++ saa7164_port_init(dev, SAA7164_PORT_TS2); ++ saa7164_port_init(dev, SAA7164_PORT_ENC1); ++ saa7164_port_init(dev, SAA7164_PORT_ENC2); ++ saa7164_port_init(dev, SAA7164_PORT_VBI1); ++ saa7164_port_init(dev, SAA7164_PORT_VBI2); ++ ++ if (get_resources(dev) < 0) { ++ printk(KERN_ERR "CORE %s No more PCIe resources for " ++ "subsystem: %04x:%04x\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device); ++ ++ saa7164_devcount--; ++ return -ENODEV; ++ } ++ ++ /* PCI/e allocations */ ++ dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0)); ++ ++ dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), ++ pci_resource_len(dev->pci, 2)); ++ ++ dev->bmmio = (u8 __iomem *)dev->lmmio; ++ dev->bmmio2 = (u8 __iomem *)dev->lmmio2; ++ ++ /* Inerrupt and ack register locations offset of bmmio */ ++ dev->int_status = 0x183000 + 0xf80; ++ dev->int_ack = 0x183000 + 0xf90; ++ ++ printk(KERN_INFO ++ "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", ++ dev->name, dev->pci->subsystem_vendor, ++ dev->pci->subsystem_device, saa7164_boards[dev->board].name, ++ dev->board, card[dev->nr] == dev->board ? ++ "insmod option" : "autodetected"); ++ ++ saa7164_pci_quirks(dev); ++ ++ return 0; ++} ++ ++static void saa7164_dev_unregister(struct saa7164_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++ ++ release_mem_region(pci_resource_start(dev->pci, 0), ++ pci_resource_len(dev->pci, 0)); ++ ++ release_mem_region(pci_resource_start(dev->pci, 2), ++ pci_resource_len(dev->pci, 2)); ++ ++ if (!atomic_dec_and_test(&dev->refcount)) ++ return; ++ ++ iounmap(dev->lmmio); ++ iounmap(dev->lmmio2); ++ ++ return; ++} ++ ++#ifdef CONFIG_PROC_FS ++static int saa7164_proc_show(struct seq_file *m, void *v) ++{ ++ struct saa7164_dev *dev; ++ struct tmComResBusInfo *b; ++ struct list_head *list; ++ int i, c; ++ ++ if (saa7164_devcount == 0) ++ return 0; ++ ++ list_for_each(list, &saa7164_devlist) { ++ dev = list_entry(list, struct saa7164_dev, devlist); ++ seq_printf(m, "%s = %p\n", dev->name, dev); ++ ++ /* Lock the bus from any other access */ ++ b = &dev->bus; ++ mutex_lock(&b->lock); ++ ++ seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n", ++ b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); ++ ++ seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n", ++ b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); ++ ++ seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n", ++ b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); ++ ++ seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n", ++ b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); ++ c = 0; ++ seq_printf(m, "\n Set Ring:\n"); ++ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); ++ for (i = 0; i < b->m_dwSizeSetRing; i++) { ++ if (c == 0) ++ seq_printf(m, " %04x:", i); ++ ++ seq_printf(m, " %02x", *(b->m_pdwSetRing + i)); ++ ++ if (++c == 16) { ++ seq_printf(m, "\n"); ++ c = 0; ++ } ++ } ++ ++ c = 0; ++ seq_printf(m, "\n Get Ring:\n"); ++ seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); ++ for (i = 0; i < b->m_dwSizeGetRing; i++) { ++ if (c == 0) ++ seq_printf(m, " %04x:", i); ++ ++ seq_printf(m, " %02x", *(b->m_pdwGetRing + i)); ++ ++ if (++c == 16) { ++ seq_printf(m, "\n"); ++ c = 0; ++ } ++ } ++ ++ mutex_unlock(&b->lock); ++ ++ } ++ ++ return 0; ++} ++ ++static int saa7164_proc_open(struct inode *inode, struct file *filp) ++{ ++ return single_open(filp, saa7164_proc_show, NULL); ++} ++ ++static const struct file_operations saa7164_proc_fops = { ++ .open = saa7164_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int saa7164_proc_create(void) ++{ ++ struct proc_dir_entry *pe; ++ ++ pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops); ++ if (!pe) ++ return -ENOMEM; ++ ++ return 0; ++} ++#endif ++ ++static int saa7164_thread_function(void *data) ++{ ++ struct saa7164_dev *dev = data; ++ struct tmFwInfoStruct fwinfo; ++ u64 last_poll_time = 0; ++ ++ dprintk(DBGLVL_THR, "thread started\n"); ++ ++ set_freezable(); ++ ++ while (1) { ++ msleep_interruptible(100); ++ if (kthread_should_stop()) ++ break; ++ try_to_freeze(); ++ ++ dprintk(DBGLVL_THR, "thread running\n"); ++ ++ /* Dump the firmware debug message to console */ ++ /* Polling this costs us 1-2% of the arm CPU */ ++ /* convert this into a respnde to interrupt 0x7a */ ++ saa7164_api_collect_debug(dev); ++ ++ /* Monitor CPU load every 1 second */ ++ if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) { ++ saa7164_api_get_load_info(dev, &fwinfo); ++ last_poll_time = jiffies_to_msecs(jiffies); ++ } ++ ++ } ++ ++ dprintk(DBGLVL_THR, "thread exiting\n"); ++ return 0; ++} ++ ++static int __devinit saa7164_initdev(struct pci_dev *pci_dev, ++ const struct pci_device_id *pci_id) ++{ ++ struct saa7164_dev *dev; ++ int err, i; ++ u32 version; ++ ++ dev = kzalloc(sizeof(*dev), GFP_KERNEL); ++ if (NULL == dev) ++ return -ENOMEM; ++ ++ /* pci init */ ++ dev->pci = pci_dev; ++ if (pci_enable_device(pci_dev)) { ++ err = -EIO; ++ goto fail_free; ++ } ++ ++ if (saa7164_dev_setup(dev) < 0) { ++ err = -EINVAL; ++ goto fail_free; ++ } ++ ++ /* print pci info */ ++ dev->pci_rev = pci_dev->revision; ++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); ++ printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " ++ "latency: %d, mmio: 0x%llx\n", dev->name, ++ pci_name(pci_dev), dev->pci_rev, pci_dev->irq, ++ dev->pci_lat, ++ (unsigned long long)pci_resource_start(pci_dev, 0)); ++ ++ pci_set_master(pci_dev); ++ /* TODO */ ++ if (!pci_dma_supported(pci_dev, 0xffffffff)) { ++ printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); ++ err = -EIO; ++ goto fail_irq; ++ } ++ ++ err = request_irq(pci_dev->irq, saa7164_irq, ++ IRQF_SHARED | IRQF_DISABLED, dev->name, dev); ++ if (err < 0) { ++ printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, ++ pci_dev->irq); ++ err = -EIO; ++ goto fail_irq; ++ } ++ ++ pci_set_drvdata(pci_dev, dev); ++ ++ /* Init the internal command list */ ++ for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { ++ dev->cmds[i].seqno = i; ++ dev->cmds[i].inuse = 0; ++ mutex_init(&dev->cmds[i].lock); ++ init_waitqueue_head(&dev->cmds[i].wait); ++ } ++ ++ /* We need a deferred interrupt handler for cmd handling */ ++ INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler); ++ ++ /* Only load the firmware if we know the board */ ++ if (dev->board != SAA7164_BOARD_UNKNOWN) { ++ ++ err = saa7164_downloadfirmware(dev); ++ if (err < 0) { ++ printk(KERN_ERR ++ "Failed to boot firmware, no features " ++ "registered\n"); ++ goto fail_fw; ++ } ++ ++ saa7164_get_descriptors(dev); ++ saa7164_dumpregs(dev, 0); ++ saa7164_getcurrentfirmwareversion(dev); ++ saa7164_getfirmwarestatus(dev); ++ err = saa7164_bus_setup(dev); ++ if (err < 0) ++ printk(KERN_ERR ++ "Failed to setup the bus, will continue\n"); ++ saa7164_bus_dump(dev); ++ ++ /* Ping the running firmware via the command bus and get the ++ * firmware version, this checks the bus is running OK. ++ */ ++ version = 0; ++ if (saa7164_api_get_fw_version(dev, &version) == SAA_OK) ++ dprintk(1, "Bus is operating correctly using " ++ "version %d.%d.%d.%d (0x%x)\n", ++ (version & 0x0000fc00) >> 10, ++ (version & 0x000003e0) >> 5, ++ (version & 0x0000001f), ++ (version & 0xffff0000) >> 16, ++ version); ++ else ++ printk(KERN_ERR ++ "Failed to communicate with the firmware\n"); ++ ++ /* Bring up the I2C buses */ ++ saa7164_i2c_register(&dev->i2c_bus[0]); ++ saa7164_i2c_register(&dev->i2c_bus[1]); ++ saa7164_i2c_register(&dev->i2c_bus[2]); ++ saa7164_gpio_setup(dev); ++ saa7164_card_setup(dev); ++ ++ /* Parse the dynamic device configuration, find various ++ * media endpoints (MPEG, WMV, PS, TS) and cache their ++ * configuration details into the driver, so we can ++ * reference them later during simething_register() func, ++ * interrupt handlers, deferred work handlers etc. ++ */ ++ saa7164_api_enum_subdevs(dev); ++ ++ /* Begin to create the video sub-systems and register funcs */ ++ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) { ++ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) { ++ printk(KERN_ERR "%s() Failed to register " ++ "dvb adapters on porta\n", ++ __func__); ++ } ++ } ++ ++ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) { ++ if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) { ++ printk(KERN_ERR"%s() Failed to register " ++ "dvb adapters on portb\n", ++ __func__); ++ } ++ } ++ ++ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) { ++ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) { ++ printk(KERN_ERR"%s() Failed to register " ++ "mpeg encoder\n", __func__); ++ } ++ } ++ ++ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) { ++ if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) { ++ printk(KERN_ERR"%s() Failed to register " ++ "mpeg encoder\n", __func__); ++ } ++ } ++ ++ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) { ++ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) { ++ printk(KERN_ERR"%s() Failed to register " ++ "vbi device\n", __func__); ++ } ++ } ++ ++ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) { ++ if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) { ++ printk(KERN_ERR"%s() Failed to register " ++ "vbi device\n", __func__); ++ } ++ } ++ saa7164_api_set_debug(dev, fw_debug); ++ ++ if (fw_debug) { ++ dev->kthread = kthread_run(saa7164_thread_function, dev, ++ "saa7164 debug"); ++ if (!dev->kthread) ++ printk(KERN_ERR "%s() Failed to create " ++ "debug kernel thread\n", __func__); ++ } ++ ++ } /* != BOARD_UNKNOWN */ ++ else ++ printk(KERN_ERR "%s() Unsupported board detected, " ++ "registering without firmware\n", __func__); ++ ++ dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug); ++ dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs); ++ ++fail_fw: ++ return 0; ++ ++fail_irq: ++ saa7164_dev_unregister(dev); ++fail_free: ++ kfree(dev); ++ return err; ++} ++ ++static void saa7164_shutdown(struct saa7164_dev *dev) ++{ ++ dprintk(1, "%s()\n", __func__); ++} ++ ++static void __devexit saa7164_finidev(struct pci_dev *pci_dev) ++{ ++ struct saa7164_dev *dev = pci_get_drvdata(pci_dev); ++ ++ if (dev->board != SAA7164_BOARD_UNKNOWN) { ++ if (fw_debug && dev->kthread) { ++ kthread_stop(dev->kthread); ++ dev->kthread = NULL; ++ } ++ if (dev->firmwareloaded) ++ saa7164_api_set_debug(dev, 0x00); ++ } ++ ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], ++ &dev->ports[SAA7164_PORT_ENC1].irq_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], ++ &dev->ports[SAA7164_PORT_ENC1].svc_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], ++ &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], ++ &dev->ports[SAA7164_PORT_ENC1].read_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], ++ &dev->ports[SAA7164_PORT_ENC1].poll_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1], ++ &dev->ports[SAA7164_PORT_VBI1].read_interval); ++ saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2], ++ &dev->ports[SAA7164_PORT_VBI2].poll_interval); ++ ++ saa7164_shutdown(dev); ++ ++ if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) ++ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]); ++ ++ if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) ++ saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]); ++ ++ if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) ++ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]); ++ ++ if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) ++ saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]); ++ ++ if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) ++ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]); ++ ++ if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) ++ saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]); ++ ++ saa7164_i2c_unregister(&dev->i2c_bus[0]); ++ saa7164_i2c_unregister(&dev->i2c_bus[1]); ++ saa7164_i2c_unregister(&dev->i2c_bus[2]); ++ ++ pci_disable_device(pci_dev); ++ ++ /* unregister stuff */ ++ free_irq(pci_dev->irq, dev); ++ pci_set_drvdata(pci_dev, NULL); ++ ++ mutex_lock(&devlist); ++ list_del(&dev->devlist); ++ mutex_unlock(&devlist); ++ ++ saa7164_dev_unregister(dev); ++ kfree(dev); ++} ++ ++static struct pci_device_id saa7164_pci_tbl[] = { ++ { ++ /* SAA7164 */ ++ .vendor = 0x1131, ++ .device = 0x7164, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ }, { ++ /* --- end of list --- */ ++ } ++}; ++MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl); ++ ++static struct pci_driver saa7164_pci_driver = { ++ .name = "saa7164", ++ .id_table = saa7164_pci_tbl, ++ .probe = saa7164_initdev, ++ .remove = __devexit_p(saa7164_finidev), ++ /* TODO */ ++ .suspend = NULL, ++ .resume = NULL, ++}; ++ ++static int __init saa7164_init(void) ++{ ++ printk(KERN_INFO "saa7164 driver loaded\n"); ++ ++#ifdef CONFIG_PROC_FS ++ saa7164_proc_create(); ++#endif ++ return pci_register_driver(&saa7164_pci_driver); ++} ++ ++static void __exit saa7164_fini(void) ++{ ++#ifdef CONFIG_PROC_FS ++ remove_proc_entry("saa7164", NULL); ++#endif ++ pci_unregister_driver(&saa7164_pci_driver); ++} ++ ++module_init(saa7164_init); ++module_exit(saa7164_fini); ++ +diff --git a/drivers/media/pci/saa7164/saa7164-dvb.c b/drivers/media/pci/saa7164/saa7164-dvb.c +new file mode 100644 +index 0000000..5c5cc3e +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-dvb.c +@@ -0,0 +1,556 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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 "saa7164.h" ++ ++#include "tda10048.h" ++#include "tda18271.h" ++#include "s5h1411.h" ++ ++#define DRIVER_NAME "saa7164" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++/* addr is in the card struct, get it from there */ ++static struct tda10048_config hauppauge_hvr2200_1_config = { ++ .demod_address = 0x10 >> 1, ++ .output_mode = TDA10048_SERIAL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3500, ++ .dtv8_if_freq_khz = TDA10048_IF_4000, ++ .clk_freq_khz = TDA10048_CLK_16000, ++}; ++static struct tda10048_config hauppauge_hvr2200_2_config = { ++ .demod_address = 0x12 >> 1, ++ .output_mode = TDA10048_SERIAL_OUTPUT, ++ .fwbulkwritelen = TDA10048_BULKWRITE_200, ++ .inversion = TDA10048_INVERSION_ON, ++ .dtv6_if_freq_khz = TDA10048_IF_3300, ++ .dtv7_if_freq_khz = TDA10048_IF_3500, ++ .dtv8_if_freq_khz = TDA10048_IF_4000, ++ .clk_freq_khz = TDA10048_CLK_16000, ++}; ++ ++static struct tda18271_std_map hauppauge_tda18271_std_map = { ++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, ++ .if_lvl = 6, .rfagc_top = 0x37 }, ++}; ++ ++static struct tda18271_config hauppauge_hvr22x0_tuner_config = { ++ .std_map = &hauppauge_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .role = TDA18271_MASTER, ++}; ++ ++static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { ++ .std_map = &hauppauge_tda18271_std_map, ++ .gate = TDA18271_GATE_ANALOG, ++ .role = TDA18271_SLAVE, ++ .output_opt = TDA18271_OUTPUT_LT_OFF, ++ .rf_cal_on_startup = 1 ++}; ++ ++static struct s5h1411_config hauppauge_s5h1411_config = { ++ .output_mode = S5H1411_SERIAL_OUTPUT, ++ .gpio = S5H1411_GPIO_ON, ++ .qam_if = S5H1411_IF_4000, ++ .vsb_if = S5H1411_IF_3250, ++ .inversion = S5H1411_INVERSION_ON, ++ .status_mode = S5H1411_DEMODLOCKING, ++ .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, ++}; ++ ++static int saa7164_dvb_stop_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_DVB, "%s() Stopped\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_dvb_acquire_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_dvb_pause_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/* Firmware is very windows centric, meaning you have to transition ++ * the part through AVStream / KS Windows stages, forwards or backwards. ++ * States are: stopped, acquired (h/w), paused, started. ++ */ ++static int saa7164_dvb_stop_streaming(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct list_head *p, *q; ++ int ret; ++ ++ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); ++ ++ ret = saa7164_dvb_pause_port(port); ++ ret = saa7164_dvb_acquire_port(port); ++ ret = saa7164_dvb_stop_port(port); ++ ++ /* Mark the hardware buffers as free */ ++ mutex_lock(&port->dmaqueue_lock); ++ list_for_each_safe(p, q, &port->dmaqueue.list) { ++ buf = list_entry(p, struct saa7164_buffer, list); ++ buf->flags = SAA7164_BUFFER_FREE; ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ return ret; ++} ++ ++static int saa7164_dvb_start_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0, result; ++ ++ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); ++ ++ saa7164_buffer_cfg_port(port); ++ ++ /* Acquire the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_DVB, "%s() Acquired\n", __func__); ++ ++ /* Pause the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_DVB, "%s() Paused\n", __func__); ++ ++ /* Start the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() run/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ } else ++ dprintk(DBGLVL_DVB, "%s() Running\n", __func__); ++ ++out: ++ return ret; ++} ++ ++static int saa7164_dvb_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct saa7164_port *port = (struct saa7164_port *) demux->priv; ++ struct saa7164_dvb *dvb = &port->dvb; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ ++ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); ++ ++ if (!demux->dmx.frontend) ++ return -EINVAL; ++ ++ if (dvb) { ++ mutex_lock(&dvb->lock); ++ if (dvb->feeding++ == 0) { ++ /* Start transport */ ++ ret = saa7164_dvb_start_port(port); ++ } ++ mutex_unlock(&dvb->lock); ++ dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", ++ __func__, port->nr, dvb->feeding); ++ } ++ ++ return ret; ++} ++ ++static int saa7164_dvb_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct saa7164_port *port = (struct saa7164_port *) demux->priv; ++ struct saa7164_dvb *dvb = &port->dvb; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ ++ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); ++ ++ if (dvb) { ++ mutex_lock(&dvb->lock); ++ if (--dvb->feeding == 0) { ++ /* Stop transport */ ++ ret = saa7164_dvb_stop_streaming(port); ++ } ++ mutex_unlock(&dvb->lock); ++ dprintk(DBGLVL_DVB, "%s(port=%d) now feeding = %d\n", ++ __func__, port->nr, dvb->feeding); ++ } ++ ++ return ret; ++} ++ ++static int dvb_register(struct saa7164_port *port) ++{ ++ struct saa7164_dvb *dvb = &port->dvb; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ int result, i; ++ ++ dprintk(DBGLVL_DVB, "%s(port=%d)\n", __func__, port->nr); ++ ++ if (port->type != SAA7164_MPEG_DVB) ++ BUG(); ++ ++ /* Sanity check that the PCI configuration space is active */ ++ if (port->hwcfg.BARLocation == 0) { ++ result = -ENOMEM; ++ printk(KERN_ERR "%s: dvb_register_adapter failed " ++ "(errno = %d), NO PCI configuration\n", ++ DRIVER_NAME, result); ++ goto fail_adapter; ++ } ++ ++ /* Init and establish defaults */ ++ port->hw_streamingparams.bitspersample = 8; ++ port->hw_streamingparams.samplesperline = 188; ++ port->hw_streamingparams.numberoflines = ++ (SAA7164_TS_NUMBER_OF_LINES * 188) / 188; ++ ++ port->hw_streamingparams.pitch = 188; ++ port->hw_streamingparams.linethreshold = 0; ++ port->hw_streamingparams.pagetablelistvirt = NULL; ++ port->hw_streamingparams.pagetablelistphys = NULL; ++ port->hw_streamingparams.numpagetables = 2 + ++ ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); ++ ++ port->hw_streamingparams.numpagetableentries = port->hwcfg.buffercount; ++ ++ /* Allocate the PCI resources */ ++ for (i = 0; i < port->hwcfg.buffercount; i++) { ++ buf = saa7164_buffer_alloc(port, ++ port->hw_streamingparams.numberoflines * ++ port->hw_streamingparams.pitch); ++ ++ if (!buf) { ++ result = -ENOMEM; ++ printk(KERN_ERR "%s: dvb_register_adapter failed " ++ "(errno = %d), unable to allocate buffers\n", ++ DRIVER_NAME, result); ++ goto fail_adapter; ++ } ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_add_tail(&buf->list, &port->dmaqueue.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ } ++ ++ /* register adapter */ ++ result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, ++ &dev->pci->dev, adapter_nr); ++ if (result < 0) { ++ printk(KERN_ERR "%s: dvb_register_adapter failed " ++ "(errno = %d)\n", DRIVER_NAME, result); ++ goto fail_adapter; ++ } ++ dvb->adapter.priv = port; ++ ++ /* register frontend */ ++ result = dvb_register_frontend(&dvb->adapter, dvb->frontend); ++ if (result < 0) { ++ printk(KERN_ERR "%s: dvb_register_frontend failed " ++ "(errno = %d)\n", DRIVER_NAME, result); ++ goto fail_frontend; ++ } ++ ++ /* register demux stuff */ ++ dvb->demux.dmx.capabilities = ++ DMX_TS_FILTERING | DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING; ++ dvb->demux.priv = port; ++ dvb->demux.filternum = 256; ++ dvb->demux.feednum = 256; ++ dvb->demux.start_feed = saa7164_dvb_start_feed; ++ dvb->demux.stop_feed = saa7164_dvb_stop_feed; ++ result = dvb_dmx_init(&dvb->demux); ++ if (result < 0) { ++ printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", ++ DRIVER_NAME, result); ++ goto fail_dmx; ++ } ++ ++ dvb->dmxdev.filternum = 256; ++ dvb->dmxdev.demux = &dvb->demux.dmx; ++ dvb->dmxdev.capabilities = 0; ++ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); ++ if (result < 0) { ++ printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", ++ DRIVER_NAME, result); ++ goto fail_dmxdev; ++ } ++ ++ dvb->fe_hw.source = DMX_FRONTEND_0; ++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ if (result < 0) { ++ printk(KERN_ERR "%s: add_frontend failed " ++ "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); ++ goto fail_fe_hw; ++ } ++ ++ dvb->fe_mem.source = DMX_MEMORY_FE; ++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++ if (result < 0) { ++ printk(KERN_ERR "%s: add_frontend failed " ++ "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); ++ goto fail_fe_mem; ++ } ++ ++ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ if (result < 0) { ++ printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", ++ DRIVER_NAME, result); ++ goto fail_fe_conn; ++ } ++ ++ /* register network adapter */ ++ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); ++ return 0; ++ ++fail_fe_conn: ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++fail_fe_mem: ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++fail_fe_hw: ++ dvb_dmxdev_release(&dvb->dmxdev); ++fail_dmxdev: ++ dvb_dmx_release(&dvb->demux); ++fail_dmx: ++ dvb_unregister_frontend(dvb->frontend); ++fail_frontend: ++ dvb_frontend_detach(dvb->frontend); ++ dvb_unregister_adapter(&dvb->adapter); ++fail_adapter: ++ return result; ++} ++ ++int saa7164_dvb_unregister(struct saa7164_port *port) ++{ ++ struct saa7164_dvb *dvb = &port->dvb; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *b; ++ struct list_head *c, *n; ++ ++ dprintk(DBGLVL_DVB, "%s()\n", __func__); ++ ++ if (port->type != SAA7164_MPEG_DVB) ++ BUG(); ++ ++ /* Remove any allocated buffers */ ++ mutex_lock(&port->dmaqueue_lock); ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ b = list_entry(c, struct saa7164_buffer, list); ++ list_del(c); ++ saa7164_buffer_dealloc(b); ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ if (dvb->frontend == NULL) ++ return 0; ++ ++ dvb_net_release(&dvb->net); ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); ++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); ++ dvb_dmxdev_release(&dvb->dmxdev); ++ dvb_dmx_release(&dvb->demux); ++ dvb_unregister_frontend(dvb->frontend); ++ dvb_frontend_detach(dvb->frontend); ++ dvb_unregister_adapter(&dvb->adapter); ++ return 0; ++} ++ ++/* All the DVB attach calls go here, this function get's modified ++ * for each new card. ++ */ ++int saa7164_dvb_register(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_dvb *dvb = &port->dvb; ++ struct saa7164_i2c *i2c_bus = NULL; ++ int ret; ++ ++ dprintk(DBGLVL_DVB, "%s()\n", __func__); ++ ++ /* init frontend */ ++ switch (dev->board) { ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_3: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_4: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2200_5: ++ i2c_bus = &dev->i2c_bus[port->nr + 1]; ++ switch (port->nr) { ++ case 0: ++ port->dvb.frontend = dvb_attach(tda10048_attach, ++ &hauppauge_hvr2200_1_config, ++ &i2c_bus->i2c_adap); ++ ++ if (port->dvb.frontend != NULL) { ++ /* TODO: addr is in the card struct */ ++ dvb_attach(tda18271_attach, port->dvb.frontend, ++ 0xc0 >> 1, &i2c_bus->i2c_adap, ++ &hauppauge_hvr22x0_tuner_config); ++ } ++ ++ break; ++ case 1: ++ port->dvb.frontend = dvb_attach(tda10048_attach, ++ &hauppauge_hvr2200_2_config, ++ &i2c_bus->i2c_adap); ++ ++ if (port->dvb.frontend != NULL) { ++ /* TODO: addr is in the card struct */ ++ dvb_attach(tda18271_attach, port->dvb.frontend, ++ 0xc0 >> 1, &i2c_bus->i2c_adap, ++ &hauppauge_hvr22x0s_tuner_config); ++ } ++ ++ break; ++ } ++ break; ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_2: ++ case SAA7164_BOARD_HAUPPAUGE_HVR2250_3: ++ i2c_bus = &dev->i2c_bus[port->nr + 1]; ++ ++ port->dvb.frontend = dvb_attach(s5h1411_attach, ++ &hauppauge_s5h1411_config, ++ &i2c_bus->i2c_adap); ++ ++ if (port->dvb.frontend != NULL) { ++ if (port->nr == 0) { ++ /* Master TDA18271 */ ++ /* TODO: addr is in the card struct */ ++ dvb_attach(tda18271_attach, port->dvb.frontend, ++ 0xc0 >> 1, &i2c_bus->i2c_adap, ++ &hauppauge_hvr22x0_tuner_config); ++ } else { ++ /* Slave TDA18271 */ ++ dvb_attach(tda18271_attach, port->dvb.frontend, ++ 0xc0 >> 1, &i2c_bus->i2c_adap, ++ &hauppauge_hvr22x0s_tuner_config); ++ } ++ } ++ ++ break; ++ default: ++ printk(KERN_ERR "%s: The frontend isn't supported\n", ++ dev->name); ++ break; ++ } ++ if (NULL == dvb->frontend) { ++ printk(KERN_ERR "%s() Frontend initialization failed\n", ++ __func__); ++ return -1; ++ } ++ ++ /* register everything */ ++ ret = dvb_register(port); ++ if (ret < 0) { ++ if (dvb->frontend->ops.release) ++ dvb->frontend->ops.release(dvb->frontend); ++ return ret; ++ } ++ ++ return 0; ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-encoder.c b/drivers/media/pci/saa7164/saa7164-encoder.c +new file mode 100644 +index 0000000..994018e +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-encoder.c +@@ -0,0 +1,1501 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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 "saa7164.h" ++ ++#define ENCODER_MAX_BITRATE 6500000 ++#define ENCODER_MIN_BITRATE 1000000 ++#define ENCODER_DEF_BITRATE 5000000 ++ ++static struct saa7164_tvnorm saa7164_tvnorms[] = { ++ { ++ .name = "NTSC-M", ++ .id = V4L2_STD_NTSC_M, ++ }, { ++ .name = "NTSC-JP", ++ .id = V4L2_STD_NTSC_M_JP, ++ } ++}; ++ ++static const u32 saa7164_v4l2_ctrls[] = { ++ V4L2_CID_BRIGHTNESS, ++ V4L2_CID_CONTRAST, ++ V4L2_CID_SATURATION, ++ V4L2_CID_HUE, ++ V4L2_CID_AUDIO_VOLUME, ++ V4L2_CID_SHARPNESS, ++ V4L2_CID_MPEG_STREAM_TYPE, ++ V4L2_CID_MPEG_VIDEO_ASPECT, ++ V4L2_CID_MPEG_VIDEO_B_FRAMES, ++ V4L2_CID_MPEG_VIDEO_GOP_SIZE, ++ V4L2_CID_MPEG_AUDIO_MUTE, ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, ++ V4L2_CID_MPEG_VIDEO_BITRATE, ++ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, ++ 0 ++}; ++ ++/* Take the encoder configuration form the port struct and ++ * flush it to the hardware. ++ */ ++static void saa7164_encoder_configure(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ port->encoder_params.width = port->width; ++ port->encoder_params.height = port->height; ++ port->encoder_params.is_50hz = ++ (port->encodernorm.id & V4L2_STD_625_50) != 0; ++ ++ /* Set up the DIF (enable it) for analog mode by default */ ++ saa7164_api_initialize_dif(port); ++ ++ /* Configure the correct video standard */ ++ saa7164_api_configure_dif(port, port->encodernorm.id); ++ ++ /* Ensure the audio decoder is correct configured */ ++ saa7164_api_set_audio_std(port); ++} ++ ++static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port) ++{ ++ struct list_head *c, *n, *p, *q, *l, *v; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ ++ /* Remove any allocated buffers */ ++ mutex_lock(&port->dmaqueue_lock); ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr); ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ list_del(c); ++ saa7164_buffer_dealloc(buf); ++ } ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr); ++ list_for_each_safe(p, q, &port->list_buf_used.list) { ++ ubuf = list_entry(p, struct saa7164_user_buffer, list); ++ list_del(p); ++ saa7164_buffer_dealloc_user(ubuf); ++ } ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr); ++ list_for_each_safe(l, v, &port->list_buf_free.list) { ++ ubuf = list_entry(l, struct saa7164_user_buffer, list); ++ list_del(l); ++ saa7164_buffer_dealloc_user(ubuf); ++ } ++ ++ mutex_unlock(&port->dmaqueue_lock); ++ dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); ++ ++ return 0; ++} ++ ++/* Dynamic buffer switch at encoder start time */ ++static int saa7164_encoder_buffers_alloc(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ struct tmHWStreamParameters *params = &port->hw_streamingparams; ++ int result = -ENODEV, i; ++ int len = 0; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ if (port->encoder_params.stream_type == ++ V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { ++ dprintk(DBGLVL_ENC, ++ "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n", ++ __func__); ++ params->samplesperline = 128; ++ params->numberoflines = 256; ++ params->pitch = 128; ++ params->numpagetables = 2 + ++ ((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE); ++ } else ++ if (port->encoder_params.stream_type == ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS) { ++ dprintk(DBGLVL_ENC, ++ "%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n", ++ __func__); ++ params->samplesperline = 188; ++ params->numberoflines = 312; ++ params->pitch = 188; ++ params->numpagetables = 2 + ++ ((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE); ++ } else ++ BUG(); ++ ++ /* Init and establish defaults */ ++ params->bitspersample = 8; ++ params->linethreshold = 0; ++ params->pagetablelistvirt = NULL; ++ params->pagetablelistphys = NULL; ++ params->numpagetableentries = port->hwcfg.buffercount; ++ ++ /* Allocate the PCI resources, buffers (hard) */ ++ for (i = 0; i < port->hwcfg.buffercount; i++) { ++ buf = saa7164_buffer_alloc(port, ++ params->numberoflines * ++ params->pitch); ++ ++ if (!buf) { ++ printk(KERN_ERR "%s() failed " ++ "(errno = %d), unable to allocate buffer\n", ++ __func__, result); ++ result = -ENOMEM; ++ goto failed; ++ } else { ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_add_tail(&buf->list, &port->dmaqueue.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ } ++ } ++ ++ /* Allocate some kernel buffers for copying ++ * to userpsace. ++ */ ++ len = params->numberoflines * params->pitch; ++ ++ if (encoder_buffers < 16) ++ encoder_buffers = 16; ++ if (encoder_buffers > 512) ++ encoder_buffers = 512; ++ ++ for (i = 0; i < encoder_buffers; i++) { ++ ++ ubuf = saa7164_buffer_alloc_user(dev, len); ++ if (ubuf) { ++ mutex_lock(&port->dmaqueue_lock); ++ list_add_tail(&ubuf->list, &port->list_buf_free.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ } ++ ++ } ++ ++ result = 0; ++ ++failed: ++ return result; ++} ++ ++static int saa7164_encoder_initialize(struct saa7164_port *port) ++{ ++ saa7164_encoder_configure(port); ++ return 0; ++} ++ ++/* -- V4L2 --------------------------------------------------------- */ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ unsigned int i; ++ ++ dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)*id); ++ ++ for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { ++ if (*id & saa7164_tvnorms[i].id) ++ break; ++ } ++ if (i == ARRAY_SIZE(saa7164_tvnorms)) ++ return -EINVAL; ++ ++ port->encodernorm = saa7164_tvnorms[i]; ++ ++ /* Update the audio decoder while is not running in ++ * auto detect mode. ++ */ ++ saa7164_api_set_audio_std(port); ++ ++ dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)*id); ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ int n; ++ ++ char *inputs[] = { "tuner", "composite", "svideo", "aux", ++ "composite 2", "svideo 2", "aux 2" }; ++ ++ if (i->index >= 7) ++ return -EINVAL; ++ ++ strcpy(i->name, inputs[i->index]); ++ ++ if (i->index == 0) ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ else ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ ++ for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++) ++ i->std |= saa7164_tvnorms[n].id; ++ ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ if (saa7164_api_get_videomux(port) != SAA_OK) ++ return -EIO; ++ ++ *i = (port->mux_input - 1); ++ ++ dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i); ++ ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i); ++ ++ if (i >= 7) ++ return -EINVAL; ++ ++ port->mux_input = i + 1; ++ ++ if (saa7164_api_set_videomux(port) != SAA_OK) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int vidioc_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "tuner"); ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; ++ ++ dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type); ++ ++ return 0; ++} ++ ++static int vidioc_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ /* Update the A/V core */ ++ return 0; ++} ++ ++static int vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ ++ f->type = V4L2_TUNER_ANALOG_TV; ++ f->frequency = port->freq; ++ ++ return 0; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_port *tsport; ++ struct dvb_frontend *fe; ++ ++ /* TODO: Pull this for the std */ ++ struct analog_parameters params = { ++ .mode = V4L2_TUNER_ANALOG_TV, ++ .audmode = V4L2_TUNER_MODE_STEREO, ++ .std = port->encodernorm.id, ++ .frequency = f->frequency ++ }; ++ ++ /* Stop the encoder */ ++ dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__, ++ f->frequency, f->tuner); ++ ++ if (f->tuner != 0) ++ return -EINVAL; ++ ++ if (f->type != V4L2_TUNER_ANALOG_TV) ++ return -EINVAL; ++ ++ port->freq = f->frequency; ++ ++ /* Update the hardware */ ++ if (port->nr == SAA7164_PORT_ENC1) ++ tsport = &dev->ports[SAA7164_PORT_TS1]; ++ else ++ if (port->nr == SAA7164_PORT_ENC2) ++ tsport = &dev->ports[SAA7164_PORT_TS2]; ++ else ++ BUG(); ++ ++ fe = tsport->dvb.frontend; ++ ++ if (fe && fe->ops.tuner_ops.set_analog_params) ++ fe->ops.tuner_ops.set_analog_params(fe, ¶ms); ++ else ++ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); ++ ++ saa7164_encoder_initialize(port); ++ ++ return 0; ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, ++ ctl->id, ctl->value); ++ ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ ctl->value = port->ctl_brightness; ++ break; ++ case V4L2_CID_CONTRAST: ++ ctl->value = port->ctl_contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ ctl->value = port->ctl_saturation; ++ break; ++ case V4L2_CID_HUE: ++ ctl->value = port->ctl_hue; ++ break; ++ case V4L2_CID_SHARPNESS: ++ ctl->value = port->ctl_sharpness; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ ctl->value = port->ctl_volume; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ ++ dprintk(DBGLVL_ENC, "%s(id=%d, value=%d)\n", __func__, ++ ctl->id, ctl->value); ++ ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_brightness = ctl->value; ++ saa7164_api_set_usercontrol(port, ++ PU_BRIGHTNESS_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_CONTRAST: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_contrast = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_SATURATION: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_saturation = ctl->value; ++ saa7164_api_set_usercontrol(port, ++ PU_SATURATION_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_HUE: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_hue = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_SHARPNESS: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_sharpness = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ if ((ctl->value >= -83) && (ctl->value <= 24)) { ++ port->ctl_volume = ctl->value; ++ saa7164_api_set_audio_volume(port, port->ctl_volume); ++ } else ++ ret = -EINVAL; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_get_ctrl(struct saa7164_port *port, ++ struct v4l2_ext_control *ctrl) ++{ ++ struct saa7164_encoder_params *params = &port->encoder_params; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ ctrl->value = params->bitrate; ++ break; ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ ctrl->value = params->stream_type; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ ctrl->value = params->ctl_mute; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ ctrl->value = params->ctl_aspect; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ ctrl->value = params->bitrate_mode; ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ ctrl->value = params->refdist; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ ctrl->value = params->bitrate_peak; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ ctrl->value = params->gop_size; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int vidioc_g_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_get_ctrl(port, ctrl); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ if ((ctrl->value >= ENCODER_MIN_BITRATE) && ++ (ctrl->value <= ENCODER_MAX_BITRATE)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || ++ (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ if ((ctrl->value >= 0) && ++ (ctrl->value <= 1)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && ++ (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ if ((ctrl->value >= 0) && ++ (ctrl->value <= 255)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ if ((ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) || ++ (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ if ((ctrl->value >= 1) && ++ (ctrl->value <= 3)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ if ((ctrl->value >= ENCODER_MIN_BITRATE) && ++ (ctrl->value <= ENCODER_MAX_BITRATE)) ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int vidioc_try_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_try_ctrl(ctrl, 0); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_set_ctrl(struct saa7164_port *port, ++ struct v4l2_ext_control *ctrl) ++{ ++ struct saa7164_encoder_params *params = &port->encoder_params; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ params->bitrate = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ params->stream_type = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ params->ctl_mute = ctrl->value; ++ ret = saa7164_api_audio_mute(port, params->ctl_mute); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ++ ret); ++ ret = -EIO; ++ } ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ params->ctl_aspect = ctrl->value; ++ ret = saa7164_api_set_aspect_ratio(port); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ++ ret); ++ ret = -EIO; ++ } ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ params->bitrate_mode = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ params->refdist = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ params->bitrate_peak = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ params->gop_size = ctrl->value; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* TODO: Update the hardware */ ++ ++ return ret; ++} ++ ++static int vidioc_s_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_try_ctrl(ctrl, 0); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ err = saa7164_set_ctrl(port, ctrl); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ ++ } ++ ++ return -EINVAL; ++} ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ strcpy(cap->driver, dev->name); ++ strlcpy(cap->card, saa7164_boards[dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ ++ cap->capabilities = ++ V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_READWRITE | ++ 0; ++ ++ cap->capabilities |= V4L2_CAP_TUNER; ++ cap->version = 0; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strlcpy(f->description, "MPEG", sizeof(f->description)); ++ f->pixelformat = V4L2_PIX_FMT_MPEG; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ f->fmt.pix.width = port->width; ++ f->fmt.pix.height = port->height; ++ ++ dprintk(DBGLVL_ENC, "VIDIOC_G_FMT: w: %d, h: %d\n", ++ port->width, port->height); ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ dprintk(DBGLVL_ENC, "VIDIOC_TRY_FMT: w: %d, h: %d\n", ++ port->width, port->height); ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ ++ dprintk(DBGLVL_ENC, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); ++ ++ return 0; ++} ++ ++static int fill_queryctrl(struct saa7164_encoder_params *params, ++ struct v4l2_queryctrl *c) ++{ ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); ++ case V4L2_CID_CONTRAST: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); ++ case V4L2_CID_SATURATION: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); ++ case V4L2_CID_HUE: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); ++ case V4L2_CID_SHARPNESS: ++ return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); ++ case V4L2_CID_AUDIO_VOLUME: ++ return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ return v4l2_ctrl_query_fill(c, ++ ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, ++ 100000, ENCODER_DEF_BITRATE); ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ return v4l2_ctrl_query_fill(c, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_PS, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS, ++ 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ return v4l2_ctrl_query_fill(c, ++ V4L2_MPEG_VIDEO_ASPECT_1x1, ++ V4L2_MPEG_VIDEO_ASPECT_221x100, ++ 1, V4L2_MPEG_VIDEO_ASPECT_4x3); ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); ++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ++ return v4l2_ctrl_query_fill(c, ++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ++ 1, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ return v4l2_ctrl_query_fill(c, ++ 1, 3, 1, 1); ++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: ++ return v4l2_ctrl_query_fill(c, ++ ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE, ++ 100000, ENCODER_DEF_BITRATE); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ struct saa7164_encoder_fh *fh = priv; ++ struct saa7164_port *port = fh->port; ++ int i, next; ++ u32 id = c->id; ++ ++ memset(c, 0, sizeof(*c)); ++ ++ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); ++ c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; ++ ++ for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { ++ if (next) { ++ if (c->id < saa7164_v4l2_ctrls[i]) ++ c->id = saa7164_v4l2_ctrls[i]; ++ else ++ continue; ++ } ++ ++ if (c->id == saa7164_v4l2_ctrls[i]) ++ return fill_queryctrl(&port->encoder_params, c); ++ ++ if (c->id < saa7164_v4l2_ctrls[i]) ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_encoder_stop_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_ENC, "%s() Stopped\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_encoder_acquire_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_encoder_pause_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_ENC, "%s() Paused\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/* Firmware is very windows centric, meaning you have to transition ++ * the part through AVStream / KS Windows stages, forwards or backwards. ++ * States are: stopped, acquired (h/w), paused, started. ++ * We have to leave here will all of the soft buffers on the free list, ++ * else the cfg_post() func won't have soft buffers to correctly configure. ++ */ ++static int saa7164_encoder_stop_streaming(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ struct list_head *c, *n; ++ int ret; ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); ++ ++ ret = saa7164_encoder_pause_port(port); ++ ret = saa7164_encoder_acquire_port(port); ++ ret = saa7164_encoder_stop_port(port); ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__, ++ port->nr); ++ ++ /* Reset the state of any allocated buffer resources */ ++ mutex_lock(&port->dmaqueue_lock); ++ ++ /* Reset the hard and soft buffer state */ ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ buf->flags = SAA7164_BUFFER_FREE; ++ buf->pos = 0; ++ } ++ ++ list_for_each_safe(c, n, &port->list_buf_used.list) { ++ ubuf = list_entry(c, struct saa7164_user_buffer, list); ++ ubuf->pos = 0; ++ list_move_tail(&ubuf->list, &port->list_buf_free.list); ++ } ++ ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ /* Free any allocated resources */ ++ saa7164_encoder_buffers_dealloc(port); ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr); ++ ++ return ret; ++} ++ ++static int saa7164_encoder_start_streaming(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int result, ret = 0; ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); ++ ++ port->done_first_interrupt = 0; ++ ++ /* allocate all of the PCIe DMA buffer resources on the fly, ++ * allowing switching between TS and PS payloads without ++ * requiring a complete driver reload. ++ */ ++ saa7164_encoder_buffers_alloc(port); ++ ++ /* Configure the encoder with any cache values */ ++ saa7164_api_set_encoder(port); ++ saa7164_api_get_encoder(port); ++ ++ /* Place the empty buffers on the hardware */ ++ saa7164_buffer_cfg_port(port); ++ ++ /* Acquire the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__); ++ ++ /* Pause the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_ENC, "%s() Paused\n", __func__); ++ ++ /* Start the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() run/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ } else ++ dprintk(DBGLVL_ENC, "%s() Running\n", __func__); ++ ++out: ++ return ret; ++} ++ ++static int fops_open(struct file *file) ++{ ++ struct saa7164_dev *dev; ++ struct saa7164_port *port; ++ struct saa7164_encoder_fh *fh; ++ ++ port = (struct saa7164_port *)video_get_drvdata(video_devdata(file)); ++ if (!port) ++ return -ENODEV; ++ ++ dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ file->private_data = fh; ++ fh->port = port; ++ ++ return 0; ++} ++ ++static int fops_release(struct file *file) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ /* Shut device down on last close */ ++ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { ++ if (atomic_dec_return(&port->v4l_reader_count) == 0) { ++ /* stop mpeg capture then cancel buffers */ ++ saa7164_encoder_stop_streaming(port); ++ } ++ } ++ ++ file->private_data = NULL; ++ kfree(fh); ++ ++ return 0; ++} ++ ++static struct ++saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port) ++{ ++ struct saa7164_user_buffer *ubuf = NULL; ++ struct saa7164_dev *dev = port->dev; ++ u32 crc; ++ ++ mutex_lock(&port->dmaqueue_lock); ++ if (!list_empty(&port->list_buf_used.list)) { ++ ubuf = list_first_entry(&port->list_buf_used.list, ++ struct saa7164_user_buffer, list); ++ ++ if (crc_checking) { ++ crc = crc32(0, ubuf->data, ubuf->actual_size); ++ if (crc != ubuf->crc) { ++ printk(KERN_ERR ++ "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", ++ __func__, ++ ubuf, ubuf->crc, crc); ++ } ++ } ++ ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf); ++ ++ return ubuf; ++} ++ ++static ssize_t fops_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *pos) ++{ ++ struct saa7164_encoder_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_user_buffer *ubuf = NULL; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ int rem, cnt; ++ u8 *p; ++ ++ port->last_read_msecs_diff = port->last_read_msecs; ++ port->last_read_msecs = jiffies_to_msecs(jiffies); ++ port->last_read_msecs_diff = port->last_read_msecs - ++ port->last_read_msecs_diff; ++ ++ saa7164_histogram_update(&port->read_interval, ++ port->last_read_msecs_diff); ++ ++ if (*pos) { ++ printk(KERN_ERR "%s() ESPIPE\n", __func__); ++ return -ESPIPE; ++ } ++ ++ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { ++ if (atomic_inc_return(&port->v4l_reader_count) == 1) { ++ ++ if (saa7164_encoder_initialize(port) < 0) { ++ printk(KERN_ERR "%s() EINVAL\n", __func__); ++ return -EINVAL; ++ } ++ ++ saa7164_encoder_start_streaming(port); ++ msleep(200); ++ } ++ } ++ ++ /* blocking wait for buffer */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_enc_next_buf(port))) { ++ printk(KERN_ERR "%s() ERESTARTSYS\n", __func__); ++ return -ERESTARTSYS; ++ } ++ } ++ ++ /* Pull the first buffer from the used list */ ++ ubuf = saa7164_enc_next_buf(port); ++ ++ while ((count > 0) && ubuf) { ++ ++ /* set remaining bytes to copy */ ++ rem = ubuf->actual_size - ubuf->pos; ++ cnt = rem > count ? count : rem; ++ ++ p = ubuf->data + ubuf->pos; ++ ++ dprintk(DBGLVL_ENC, ++ "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n", ++ __func__, (int)count, cnt, rem, ubuf, ubuf->pos); ++ ++ if (copy_to_user(buffer, p, cnt)) { ++ printk(KERN_ERR "%s() copy_to_user failed\n", __func__); ++ if (!ret) { ++ printk(KERN_ERR "%s() EFAULT\n", __func__); ++ ret = -EFAULT; ++ } ++ goto err; ++ } ++ ++ ubuf->pos += cnt; ++ count -= cnt; ++ buffer += cnt; ++ ret += cnt; ++ ++ if (ubuf->pos > ubuf->actual_size) ++ printk(KERN_ERR "read() pos > actual, huh?\n"); ++ ++ if (ubuf->pos == ubuf->actual_size) { ++ ++ /* finished with current buffer, take next buffer */ ++ ++ /* Requeue the buffer on the free list */ ++ ubuf->pos = 0; ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_move_tail(&ubuf->list, &port->list_buf_free.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ /* Dequeue next */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_enc_next_buf(port))) { ++ break; ++ } ++ } ++ ubuf = saa7164_enc_next_buf(port); ++ } ++ } ++err: ++ if (!ret && !ubuf) ++ ret = -EAGAIN; ++ ++ return ret; ++} ++ ++static unsigned int fops_poll(struct file *file, poll_table *wait) ++{ ++ struct saa7164_encoder_fh *fh = ++ (struct saa7164_encoder_fh *)file->private_data; ++ struct saa7164_port *port = fh->port; ++ unsigned int mask = 0; ++ ++ port->last_poll_msecs_diff = port->last_poll_msecs; ++ port->last_poll_msecs = jiffies_to_msecs(jiffies); ++ port->last_poll_msecs_diff = port->last_poll_msecs - ++ port->last_poll_msecs_diff; ++ ++ saa7164_histogram_update(&port->poll_interval, ++ port->last_poll_msecs_diff); ++ ++ if (!video_is_registered(port->v4l_device)) ++ return -EIO; ++ ++ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { ++ if (atomic_inc_return(&port->v4l_reader_count) == 1) { ++ if (saa7164_encoder_initialize(port) < 0) ++ return -EINVAL; ++ saa7164_encoder_start_streaming(port); ++ msleep(200); ++ } ++ } ++ ++ /* blocking wait for buffer */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_enc_next_buf(port))) { ++ return -ERESTARTSYS; ++ } ++ } ++ ++ /* Pull the first buffer from the used list */ ++ if (!list_empty(&port->list_buf_used.list)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++ ++static const struct v4l2_file_operations mpeg_fops = { ++ .owner = THIS_MODULE, ++ .open = fops_open, ++ .release = fops_release, ++ .read = fops_read, ++ .poll = fops_poll, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static int saa7164_g_chip_ident(struct file *file, void *fh, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; ++ struct saa7164_dev *dev = port->dev; ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ return 0; ++} ++ ++static int saa7164_g_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; ++ struct saa7164_dev *dev = port->dev; ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ return 0; ++} ++ ++static int saa7164_s_register(struct file *file, void *fh, ++ struct v4l2_dbg_register *reg) ++{ ++ struct saa7164_port *port = ((struct saa7164_encoder_fh *)fh)->port; ++ struct saa7164_dev *dev = port->dev; ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, ++ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, ++ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_g_chip_ident = saa7164_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = saa7164_g_register, ++ .vidioc_s_register = saa7164_s_register, ++#endif ++}; ++ ++static struct video_device saa7164_mpeg_template = { ++ .name = "saa7164", ++ .fops = &mpeg_fops, ++ .ioctl_ops = &mpeg_ioctl_ops, ++ .minor = -1, ++ .tvnorms = SAA7164_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++}; ++ ++static struct video_device *saa7164_encoder_alloc( ++ struct saa7164_port *port, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ ++ *vfd = *template; ++ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, ++ type, saa7164_boards[dev->board].name); ++ ++ vfd->parent = &pci->dev; ++ vfd->release = video_device_release; ++ return vfd; ++} ++ ++int saa7164_encoder_register(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int result = -ENODEV; ++ ++ dprintk(DBGLVL_ENC, "%s()\n", __func__); ++ ++ if (port->type != SAA7164_MPEG_ENCODER) ++ BUG(); ++ ++ /* Sanity check that the PCI configuration space is active */ ++ if (port->hwcfg.BARLocation == 0) { ++ printk(KERN_ERR "%s() failed " ++ "(errno = %d), NO PCI configuration\n", ++ __func__, result); ++ result = -ENOMEM; ++ goto failed; ++ } ++ ++ /* Establish encoder defaults here */ ++ /* Set default TV standard */ ++ port->encodernorm = saa7164_tvnorms[0]; ++ port->width = 720; ++ port->mux_input = 1; /* Composite */ ++ port->video_format = EU_VIDEO_FORMAT_MPEG_2; ++ port->audio_format = 0; ++ port->video_resolution = 0; ++ port->ctl_brightness = 127; ++ port->ctl_contrast = 66; ++ port->ctl_hue = 128; ++ port->ctl_saturation = 62; ++ port->ctl_sharpness = 8; ++ port->encoder_params.bitrate = ENCODER_DEF_BITRATE; ++ port->encoder_params.bitrate_peak = ENCODER_DEF_BITRATE; ++ port->encoder_params.bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR; ++ port->encoder_params.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS; ++ port->encoder_params.ctl_mute = 0; ++ port->encoder_params.ctl_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3; ++ port->encoder_params.refdist = 1; ++ port->encoder_params.gop_size = SAA7164_ENCODER_DEFAULT_GOP_SIZE; ++ ++ if (port->encodernorm.id & V4L2_STD_525_60) ++ port->height = 480; ++ else ++ port->height = 576; ++ ++ /* Allocate and register the video device node */ ++ port->v4l_device = saa7164_encoder_alloc(port, ++ dev->pci, &saa7164_mpeg_template, "mpeg"); ++ ++ if (!port->v4l_device) { ++ printk(KERN_INFO "%s: can't allocate mpeg device\n", ++ dev->name); ++ result = -ENOMEM; ++ goto failed; ++ } ++ ++ video_set_drvdata(port->v4l_device, port); ++ result = video_register_device(port->v4l_device, ++ VFL_TYPE_GRABBER, -1); ++ if (result < 0) { ++ printk(KERN_INFO "%s: can't register mpeg device\n", ++ dev->name); ++ /* TODO: We're going to leak here if we don't dealloc ++ The buffers above. The unreg function can't deal wit it. ++ */ ++ goto failed; ++ } ++ ++ printk(KERN_INFO "%s: registered device video%d [mpeg]\n", ++ dev->name, port->v4l_device->num); ++ ++ /* Configure the hardware defaults */ ++ saa7164_api_set_videomux(port); ++ saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL); ++ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); ++ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); ++ saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL); ++ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); ++ saa7164_api_audio_mute(port, 0); ++ saa7164_api_set_audio_volume(port, 20); ++ saa7164_api_set_aspect_ratio(port); ++ ++ /* Disable audio standard detection, it's buggy */ ++ saa7164_api_set_audio_detection(port, 0); ++ ++ saa7164_api_set_encoder(port); ++ saa7164_api_get_encoder(port); ++ ++ result = 0; ++failed: ++ return result; ++} ++ ++void saa7164_encoder_unregister(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr); ++ ++ if (port->type != SAA7164_MPEG_ENCODER) ++ BUG(); ++ ++ if (port->v4l_device) { ++ if (port->v4l_device->minor != -1) ++ video_unregister_device(port->v4l_device); ++ else ++ video_device_release(port->v4l_device); ++ ++ port->v4l_device = NULL; ++ } ++ ++ dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr); ++} ++ +diff --git a/drivers/media/pci/saa7164/saa7164-fw.c b/drivers/media/pci/saa7164/saa7164-fw.c +new file mode 100644 +index 0000000..8676320 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-fw.c +@@ -0,0 +1,613 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#include "saa7164.h" ++ ++#define SAA7164_REV2_FIRMWARE "NXP7164-2010-03-10.1.fw" ++#define SAA7164_REV2_FIRMWARE_SIZE 4019072 ++ ++#define SAA7164_REV3_FIRMWARE "NXP7164-2010-03-10.1.fw" ++#define SAA7164_REV3_FIRMWARE_SIZE 4019072 ++ ++struct fw_header { ++ u32 firmwaresize; ++ u32 bslsize; ++ u32 reserved; ++ u32 version; ++}; ++ ++static int saa7164_dl_wait_ack(struct saa7164_dev *dev, u32 reg) ++{ ++ u32 timeout = SAA_DEVICE_TIMEOUT; ++ while ((saa7164_readl(reg) & 0x01) == 0) { ++ timeout -= 10; ++ if (timeout == 0) { ++ printk(KERN_ERR "%s() timeout (no d/l ack)\n", ++ __func__); ++ return -EBUSY; ++ } ++ msleep(100); ++ } ++ ++ return 0; ++} ++ ++static int saa7164_dl_wait_clr(struct saa7164_dev *dev, u32 reg) ++{ ++ u32 timeout = SAA_DEVICE_TIMEOUT; ++ while (saa7164_readl(reg) & 0x01) { ++ timeout -= 10; ++ if (timeout == 0) { ++ printk(KERN_ERR "%s() timeout (no d/l clr)\n", ++ __func__); ++ return -EBUSY; ++ } ++ msleep(100); ++ } ++ ++ return 0; ++} ++ ++/* TODO: move dlflags into dev-> and change to write/readl/b */ ++/* TODO: Excessive levels of debug */ ++static int saa7164_downloadimage(struct saa7164_dev *dev, u8 *src, u32 srcsize, ++ u32 dlflags, u8 *dst, u32 dstsize) ++{ ++ u32 reg, timeout, offset; ++ u8 *srcbuf = NULL; ++ int ret; ++ ++ u32 dlflag = dlflags; ++ u32 dlflag_ack = dlflag + 4; ++ u32 drflag = dlflag_ack + 4; ++ u32 drflag_ack = drflag + 4; ++ u32 bleflag = drflag_ack + 4; ++ ++ dprintk(DBGLVL_FW, ++ "%s(image=%p, size=%d, flags=0x%x, dst=%p, dstsize=0x%x)\n", ++ __func__, src, srcsize, dlflags, dst, dstsize); ++ ++ if ((src == NULL) || (dst == NULL)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ srcbuf = kzalloc(4 * 1048576, GFP_KERNEL); ++ if (NULL == srcbuf) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ if (srcsize > (4*1048576)) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ memcpy(srcbuf, src, srcsize); ++ ++ dprintk(DBGLVL_FW, "%s() dlflag = 0x%x\n", __func__, dlflag); ++ dprintk(DBGLVL_FW, "%s() dlflag_ack = 0x%x\n", __func__, dlflag_ack); ++ dprintk(DBGLVL_FW, "%s() drflag = 0x%x\n", __func__, drflag); ++ dprintk(DBGLVL_FW, "%s() drflag_ack = 0x%x\n", __func__, drflag_ack); ++ dprintk(DBGLVL_FW, "%s() bleflag = 0x%x\n", __func__, bleflag); ++ ++ reg = saa7164_readl(dlflag); ++ dprintk(DBGLVL_FW, "%s() dlflag (0x%x)= 0x%x\n", __func__, dlflag, reg); ++ if (reg == 1) ++ dprintk(DBGLVL_FW, ++ "%s() Download flag already set, please reboot\n", ++ __func__); ++ ++ /* Indicate download start */ ++ saa7164_writel(dlflag, 1); ++ ret = saa7164_dl_wait_ack(dev, dlflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ /* Ack download start, then wait for wait */ ++ saa7164_writel(dlflag, 0); ++ ret = saa7164_dl_wait_clr(dev, dlflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ /* Deal with the raw firmware, in the appropriate chunk size */ ++ for (offset = 0; srcsize > dstsize; ++ srcsize -= dstsize, offset += dstsize) { ++ ++ dprintk(DBGLVL_FW, "%s() memcpy %d\n", __func__, dstsize); ++ memcpy(dst, srcbuf + offset, dstsize); ++ ++ /* Flag the data as ready */ ++ saa7164_writel(drflag, 1); ++ ret = saa7164_dl_wait_ack(dev, drflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ /* Wait for indication data was received */ ++ saa7164_writel(drflag, 0); ++ ret = saa7164_dl_wait_clr(dev, drflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ } ++ ++ dprintk(DBGLVL_FW, "%s() memcpy(l) %d\n", __func__, dstsize); ++ /* Write last block to the device */ ++ memcpy(dst, srcbuf+offset, srcsize); ++ ++ /* Flag the data as ready */ ++ saa7164_writel(drflag, 1); ++ ret = saa7164_dl_wait_ack(dev, drflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ saa7164_writel(drflag, 0); ++ timeout = 0; ++ while (saa7164_readl(bleflag) != SAA_DEVICE_IMAGE_BOOTING) { ++ if (saa7164_readl(bleflag) & SAA_DEVICE_IMAGE_CORRUPT) { ++ printk(KERN_ERR "%s() image corrupt\n", __func__); ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ if (saa7164_readl(bleflag) & SAA_DEVICE_MEMORY_CORRUPT) { ++ printk(KERN_ERR "%s() device memory corrupt\n", ++ __func__); ++ ret = -EBUSY; ++ goto out; ++ } ++ ++ msleep(10); /* Checkpatch throws a < 20ms warning */ ++ if (timeout++ > 60) ++ break; ++ } ++ ++ printk(KERN_INFO "%s() Image downloaded, booting...\n", __func__); ++ ++ ret = saa7164_dl_wait_clr(dev, drflag_ack); ++ if (ret < 0) ++ goto out; ++ ++ printk(KERN_INFO "%s() Image booted successfully.\n", __func__); ++ ret = 0; ++ ++out: ++ kfree(srcbuf); ++ return ret; ++} ++ ++/* TODO: Excessive debug */ ++/* Load the firmware. Optionally it can be in ROM or newer versions ++ * can be on disk, saving the expense of the ROM hardware. */ ++int saa7164_downloadfirmware(struct saa7164_dev *dev) ++{ ++ /* u32 second_timeout = 60 * SAA_DEVICE_TIMEOUT; */ ++ u32 tmp, filesize, version, err_flags, first_timeout, fwlength; ++ u32 second_timeout, updatebootloader = 1, bootloadersize = 0; ++ const struct firmware *fw = NULL; ++ struct fw_header *hdr, *boothdr = NULL, *fwhdr; ++ u32 bootloaderversion = 0, fwloadersize; ++ u8 *bootloaderoffset = NULL, *fwloaderoffset; ++ char *fwname; ++ int ret; ++ ++ dprintk(DBGLVL_FW, "%s()\n", __func__); ++ ++ if (saa7164_boards[dev->board].chiprev == SAA7164_CHIP_REV2) { ++ fwname = SAA7164_REV2_FIRMWARE; ++ fwlength = SAA7164_REV2_FIRMWARE_SIZE; ++ } else { ++ fwname = SAA7164_REV3_FIRMWARE; ++ fwlength = SAA7164_REV3_FIRMWARE_SIZE; ++ } ++ ++ version = saa7164_getcurrentfirmwareversion(dev); ++ ++ if (version == 0x00) { ++ ++ second_timeout = 100; ++ first_timeout = 100; ++ err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); ++ dprintk(DBGLVL_FW, "%s() err_flags = %x\n", ++ __func__, err_flags); ++ ++ while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { ++ dprintk(DBGLVL_FW, "%s() err_flags = %x\n", ++ __func__, err_flags); ++ msleep(10); /* Checkpatch throws a < 20ms warning */ ++ ++ if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { ++ printk(KERN_ERR "%s() firmware corrupt\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { ++ printk(KERN_ERR "%s() device memory corrupt\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_NO_IMAGE) { ++ printk(KERN_ERR "%s() no first image\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { ++ first_timeout -= 10; ++ if (first_timeout == 0) { ++ printk(KERN_ERR ++ "%s() no first image\n", ++ __func__); ++ break; ++ } ++ } else if (err_flags & SAA_DEVICE_IMAGE_LOADING) { ++ second_timeout -= 10; ++ if (second_timeout == 0) { ++ printk(KERN_ERR ++ "%s() FW load time exceeded\n", ++ __func__); ++ break; ++ } ++ } else { ++ second_timeout -= 10; ++ if (second_timeout == 0) { ++ printk(KERN_ERR ++ "%s() Unknown bootloader flags 0x%x\n", ++ __func__, err_flags); ++ break; ++ } ++ } ++ ++ err_flags = saa7164_readl(SAA_BOOTLOADERERROR_FLAGS); ++ } /* While != Booting */ ++ ++ if (err_flags == SAA_DEVICE_IMAGE_BOOTING) { ++ dprintk(DBGLVL_FW, "%s() Loader 1 has loaded.\n", ++ __func__); ++ first_timeout = SAA_DEVICE_TIMEOUT; ++ second_timeout = 60 * SAA_DEVICE_TIMEOUT; ++ second_timeout = 100; ++ ++ err_flags = saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); ++ dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", ++ __func__, err_flags); ++ while (err_flags != SAA_DEVICE_IMAGE_BOOTING) { ++ dprintk(DBGLVL_FW, "%s() err_flags2 = %x\n", ++ __func__, err_flags); ++ msleep(10); /* Checkpatch throws a < 20ms warning */ ++ ++ if (err_flags & SAA_DEVICE_IMAGE_CORRUPT) { ++ printk(KERN_ERR ++ "%s() firmware corrupt\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_MEMORY_CORRUPT) { ++ printk(KERN_ERR ++ "%s() device memory corrupt\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_NO_IMAGE) { ++ printk(KERN_ERR "%s() no first image\n", ++ __func__); ++ break; ++ } ++ if (err_flags & SAA_DEVICE_IMAGE_SEARCHING) { ++ first_timeout -= 10; ++ if (first_timeout == 0) { ++ printk(KERN_ERR ++ "%s() no second image\n", ++ __func__); ++ break; ++ } ++ } else if (err_flags & ++ SAA_DEVICE_IMAGE_LOADING) { ++ second_timeout -= 10; ++ if (second_timeout == 0) { ++ printk(KERN_ERR ++ "%s() FW load time exceeded\n", ++ __func__); ++ break; ++ } ++ } else { ++ second_timeout -= 10; ++ if (second_timeout == 0) { ++ printk(KERN_ERR ++ "%s() Unknown bootloader flags 0x%x\n", ++ __func__, err_flags); ++ break; ++ } ++ } ++ ++ err_flags = ++ saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS); ++ } /* err_flags != SAA_DEVICE_IMAGE_BOOTING */ ++ ++ dprintk(DBGLVL_FW, "%s() Loader flags 1:0x%x 2:0x%x.\n", ++ __func__, ++ saa7164_readl(SAA_BOOTLOADERERROR_FLAGS), ++ saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS)); ++ ++ } /* err_flags == SAA_DEVICE_IMAGE_BOOTING */ ++ ++ /* It's possible for both firmwares to have booted, ++ * but that doesn't mean they've finished booting yet. ++ */ ++ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == ++ SAA_DEVICE_IMAGE_BOOTING) && ++ (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == ++ SAA_DEVICE_IMAGE_BOOTING)) { ++ ++ ++ dprintk(DBGLVL_FW, "%s() Loader 2 has loaded.\n", ++ __func__); ++ ++ first_timeout = SAA_DEVICE_TIMEOUT; ++ while (first_timeout) { ++ msleep(10); /* Checkpatch throws a < 20ms warning */ ++ ++ version = ++ saa7164_getcurrentfirmwareversion(dev); ++ if (version) { ++ dprintk(DBGLVL_FW, ++ "%s() All f/w loaded successfully\n", ++ __func__); ++ break; ++ } else { ++ first_timeout -= 10; ++ if (first_timeout == 0) { ++ printk(KERN_ERR ++ "%s() FW did not boot\n", ++ __func__); ++ break; ++ } ++ } ++ } ++ } ++ version = saa7164_getcurrentfirmwareversion(dev); ++ } /* version == 0 */ ++ ++ /* Has the firmware really booted? */ ++ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == ++ SAA_DEVICE_IMAGE_BOOTING) && ++ (saa7164_readl(SAA_SECONDSTAGEERROR_FLAGS) == ++ SAA_DEVICE_IMAGE_BOOTING) && (version == 0)) { ++ ++ printk(KERN_ERR ++ "%s() The firmware hung, probably bad firmware\n", ++ __func__); ++ ++ /* Tell the second stage loader we have a deadlock */ ++ saa7164_writel(SAA_DEVICE_DEADLOCK_DETECTED_OFFSET, ++ SAA_DEVICE_DEADLOCK_DETECTED); ++ ++ saa7164_getfirmwarestatus(dev); ++ ++ return -ENOMEM; ++ } ++ ++ dprintk(DBGLVL_FW, "Device has Firmware Version %d.%d.%d.%d\n", ++ (version & 0x0000fc00) >> 10, ++ (version & 0x000003e0) >> 5, ++ (version & 0x0000001f), ++ (version & 0xffff0000) >> 16); ++ ++ /* Load the firmwware from the disk if required */ ++ if (version == 0) { ++ ++ printk(KERN_INFO "%s() Waiting for firmware upload (%s)\n", ++ __func__, fwname); ++ ++ ret = request_firmware(&fw, fwname, &dev->pci->dev); ++ if (ret) { ++ printk(KERN_ERR "%s() Upload failed. " ++ "(file not found?)\n", __func__); ++ return -ENOMEM; ++ } ++ ++ printk(KERN_INFO "%s() firmware read %Zu bytes.\n", ++ __func__, fw->size); ++ ++ if (fw->size != fwlength) { ++ printk(KERN_ERR "xc5000: firmware incorrect size\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ printk(KERN_INFO "%s() firmware loaded.\n", __func__); ++ ++ hdr = (struct fw_header *)fw->data; ++ printk(KERN_INFO "Firmware file header part 1:\n"); ++ printk(KERN_INFO " .FirmwareSize = 0x%x\n", hdr->firmwaresize); ++ printk(KERN_INFO " .BSLSize = 0x%x\n", hdr->bslsize); ++ printk(KERN_INFO " .Reserved = 0x%x\n", hdr->reserved); ++ printk(KERN_INFO " .Version = 0x%x\n", hdr->version); ++ ++ /* Retrieve bootloader if reqd */ ++ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) ++ /* Second bootloader in the firmware file */ ++ filesize = hdr->reserved * 16; ++ else ++ filesize = (hdr->firmwaresize + hdr->bslsize) * ++ 16 + sizeof(struct fw_header); ++ ++ printk(KERN_INFO "%s() SecBootLoader.FileSize = %d\n", ++ __func__, filesize); ++ ++ /* Get bootloader (if reqd) and firmware header */ ++ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { ++ /* Second boot loader is required */ ++ ++ /* Get the loader header */ ++ boothdr = (struct fw_header *)(fw->data + ++ sizeof(struct fw_header)); ++ ++ bootloaderversion = ++ saa7164_readl(SAA_DEVICE_2ND_VERSION); ++ dprintk(DBGLVL_FW, "Onboard BootLoader:\n"); ++ dprintk(DBGLVL_FW, "->Flag 0x%x\n", ++ saa7164_readl(SAA_BOOTLOADERERROR_FLAGS)); ++ dprintk(DBGLVL_FW, "->Ack 0x%x\n", ++ saa7164_readl(SAA_DATAREADY_FLAG_ACK)); ++ dprintk(DBGLVL_FW, "->FW Version 0x%x\n", version); ++ dprintk(DBGLVL_FW, "->Loader Version 0x%x\n", ++ bootloaderversion); ++ ++ if ((saa7164_readl(SAA_BOOTLOADERERROR_FLAGS) == ++ 0x03) && (saa7164_readl(SAA_DATAREADY_FLAG_ACK) ++ == 0x00) && (version == 0x00)) { ++ ++ dprintk(DBGLVL_FW, "BootLoader version in " ++ "rom %d.%d.%d.%d\n", ++ (bootloaderversion & 0x0000fc00) >> 10, ++ (bootloaderversion & 0x000003e0) >> 5, ++ (bootloaderversion & 0x0000001f), ++ (bootloaderversion & 0xffff0000) >> 16 ++ ); ++ dprintk(DBGLVL_FW, "BootLoader version " ++ "in file %d.%d.%d.%d\n", ++ (boothdr->version & 0x0000fc00) >> 10, ++ (boothdr->version & 0x000003e0) >> 5, ++ (boothdr->version & 0x0000001f), ++ (boothdr->version & 0xffff0000) >> 16 ++ ); ++ ++ if (bootloaderversion == boothdr->version) ++ updatebootloader = 0; ++ } ++ ++ /* Calculate offset to firmware header */ ++ tmp = (boothdr->firmwaresize + boothdr->bslsize) * 16 + ++ (sizeof(struct fw_header) + ++ sizeof(struct fw_header)); ++ ++ fwhdr = (struct fw_header *)(fw->data+tmp); ++ } else { ++ /* No second boot loader */ ++ fwhdr = hdr; ++ } ++ ++ dprintk(DBGLVL_FW, "Firmware version in file %d.%d.%d.%d\n", ++ (fwhdr->version & 0x0000fc00) >> 10, ++ (fwhdr->version & 0x000003e0) >> 5, ++ (fwhdr->version & 0x0000001f), ++ (fwhdr->version & 0xffff0000) >> 16 ++ ); ++ ++ if (version == fwhdr->version) { ++ /* No download, firmware already on board */ ++ ret = 0; ++ goto out; ++ } ++ ++ if ((hdr->firmwaresize == 0) && (hdr->bslsize == 0)) { ++ if (updatebootloader) { ++ /* Get ready to upload the bootloader */ ++ bootloadersize = (boothdr->firmwaresize + ++ boothdr->bslsize) * 16 + ++ sizeof(struct fw_header); ++ ++ bootloaderoffset = (u8 *)(fw->data + ++ sizeof(struct fw_header)); ++ ++ dprintk(DBGLVL_FW, "bootloader d/l starts.\n"); ++ printk(KERN_INFO "%s() FirmwareSize = 0x%x\n", ++ __func__, boothdr->firmwaresize); ++ printk(KERN_INFO "%s() BSLSize = 0x%x\n", ++ __func__, boothdr->bslsize); ++ printk(KERN_INFO "%s() Reserved = 0x%x\n", ++ __func__, boothdr->reserved); ++ printk(KERN_INFO "%s() Version = 0x%x\n", ++ __func__, boothdr->version); ++ ret = saa7164_downloadimage( ++ dev, ++ bootloaderoffset, ++ bootloadersize, ++ SAA_DOWNLOAD_FLAGS, ++ dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, ++ SAA_DEVICE_BUFFERBLOCKSIZE); ++ if (ret < 0) { ++ printk(KERN_ERR ++ "bootloader d/l has failed\n"); ++ goto out; ++ } ++ dprintk(DBGLVL_FW, ++ "bootloader download complete.\n"); ++ ++ } ++ ++ printk(KERN_ERR "starting firmware download(2)\n"); ++ bootloadersize = (boothdr->firmwaresize + ++ boothdr->bslsize) * 16 + ++ sizeof(struct fw_header); ++ ++ bootloaderoffset = ++ (u8 *)(fw->data + sizeof(struct fw_header)); ++ ++ fwloaderoffset = bootloaderoffset + bootloadersize; ++ ++ /* TODO: fix this bounds overrun here with old f/ws */ ++ fwloadersize = (fwhdr->firmwaresize + fwhdr->bslsize) * ++ 16 + sizeof(struct fw_header); ++ ++ ret = saa7164_downloadimage( ++ dev, ++ fwloaderoffset, ++ fwloadersize, ++ SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET, ++ dev->bmmio + SAA_DEVICE_2ND_DOWNLOAD_OFFSET, ++ SAA_DEVICE_2ND_BUFFERBLOCKSIZE); ++ if (ret < 0) { ++ printk(KERN_ERR "firmware download failed\n"); ++ goto out; ++ } ++ printk(KERN_ERR "firmware download complete.\n"); ++ ++ } else { ++ ++ /* No bootloader update reqd, download firmware only */ ++ printk(KERN_ERR "starting firmware download(3)\n"); ++ ++ ret = saa7164_downloadimage( ++ dev, ++ (u8 *)fw->data, ++ fw->size, ++ SAA_DOWNLOAD_FLAGS, ++ dev->bmmio + SAA_DEVICE_DOWNLOAD_OFFSET, ++ SAA_DEVICE_BUFFERBLOCKSIZE); ++ if (ret < 0) { ++ printk(KERN_ERR "firmware download failed\n"); ++ goto out; ++ } ++ printk(KERN_ERR "firmware download complete.\n"); ++ } ++ } ++ ++ dev->firmwareloaded = 1; ++ ret = 0; ++ ++out: ++ release_firmware(fw); ++ return ret; ++} +diff --git a/drivers/media/pci/saa7164/saa7164-i2c.c b/drivers/media/pci/saa7164/saa7164-i2c.c +new file mode 100644 +index 0000000..4f7e3b4 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-i2c.c +@@ -0,0 +1,125 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7164.h" ++ ++static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) ++{ ++ struct saa7164_i2c *bus = i2c_adap->algo_data; ++ struct saa7164_dev *dev = bus->dev; ++ int i, retval = 0; ++ ++ dprintk(DBGLVL_I2C, "%s(num = %d)\n", __func__, num); ++ ++ for (i = 0 ; i < num; i++) { ++ dprintk(DBGLVL_I2C, "%s(num = %d) addr = 0x%02x len = 0x%x\n", ++ __func__, num, msgs[i].addr, msgs[i].len); ++ if (msgs[i].flags & I2C_M_RD) { ++ /* Unsupported - Yet*/ ++ printk(KERN_ERR "%s() Unsupported - Yet\n", __func__); ++ continue; ++ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && ++ msgs[i].addr == msgs[i + 1].addr) { ++ /* write then read from same address */ ++ ++ retval = saa7164_api_i2c_read(bus, msgs[i].addr, ++ msgs[i].len, msgs[i].buf, ++ msgs[i+1].len, msgs[i+1].buf ++ ); ++ ++ i++; ++ ++ if (retval < 0) ++ goto err; ++ } else { ++ /* write */ ++ retval = saa7164_api_i2c_write(bus, msgs[i].addr, ++ msgs[i].len, msgs[i].buf); ++ } ++ if (retval < 0) ++ goto err; ++ } ++ return num; ++ ++err: ++ return retval; ++} ++ ++static u32 saa7164_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C; ++} ++ ++static struct i2c_algorithm saa7164_i2c_algo_template = { ++ .master_xfer = i2c_xfer, ++ .functionality = saa7164_functionality, ++}; ++ ++/* ----------------------------------------------------------------------- */ ++ ++static struct i2c_adapter saa7164_i2c_adap_template = { ++ .name = "saa7164", ++ .owner = THIS_MODULE, ++ .algo = &saa7164_i2c_algo_template, ++}; ++ ++static struct i2c_client saa7164_i2c_client_template = { ++ .name = "saa7164 internal", ++}; ++ ++int saa7164_i2c_register(struct saa7164_i2c *bus) ++{ ++ struct saa7164_dev *dev = bus->dev; ++ ++ dprintk(DBGLVL_I2C, "%s(bus = %d)\n", __func__, bus->nr); ++ ++ bus->i2c_adap = saa7164_i2c_adap_template; ++ bus->i2c_client = saa7164_i2c_client_template; ++ ++ bus->i2c_adap.dev.parent = &dev->pci->dev; ++ ++ strlcpy(bus->i2c_adap.name, bus->dev->name, ++ sizeof(bus->i2c_adap.name)); ++ ++ bus->i2c_adap.algo_data = bus; ++ i2c_set_adapdata(&bus->i2c_adap, bus); ++ i2c_add_adapter(&bus->i2c_adap); ++ ++ bus->i2c_client.adapter = &bus->i2c_adap; ++ ++ if (0 != bus->i2c_rc) ++ printk(KERN_ERR "%s: i2c bus %d register FAILED\n", ++ dev->name, bus->nr); ++ ++ return bus->i2c_rc; ++} ++ ++int saa7164_i2c_unregister(struct saa7164_i2c *bus) ++{ ++ i2c_del_adapter(&bus->i2c_adap); ++ return 0; ++} +diff --git a/drivers/media/pci/saa7164/saa7164-reg.h b/drivers/media/pci/saa7164/saa7164-reg.h +new file mode 100644 +index 0000000..2bbf815 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-reg.h +@@ -0,0 +1,219 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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. ++ */ ++ ++/* TODO: Retest the driver with errors expressed as negatives */ ++ ++/* Result codes */ ++#define SAA_OK 0 ++#define SAA_ERR_BAD_PARAMETER 0x09 ++#define SAA_ERR_NO_RESOURCES 0x0c ++#define SAA_ERR_NOT_SUPPORTED 0x13 ++#define SAA_ERR_BUSY 0x15 ++#define SAA_ERR_READ 0x17 ++#define SAA_ERR_TIMEOUT 0x1f ++#define SAA_ERR_OVERFLOW 0x20 ++#define SAA_ERR_EMPTY 0x22 ++#define SAA_ERR_NOT_STARTED 0x23 ++#define SAA_ERR_ALREADY_STARTED 0x24 ++#define SAA_ERR_NOT_STOPPED 0x25 ++#define SAA_ERR_ALREADY_STOPPED 0x26 ++#define SAA_ERR_INVALID_COMMAND 0x3e ++#define SAA_ERR_NULL_PACKET 0x59 ++ ++/* Errors and flags from the silicon */ ++#define PVC_ERRORCODE_UNKNOWN 0x00 ++#define PVC_ERRORCODE_INVALID_COMMAND 0x01 ++#define PVC_ERRORCODE_INVALID_CONTROL 0x02 ++#define PVC_ERRORCODE_INVALID_DATA 0x03 ++#define PVC_ERRORCODE_TIMEOUT 0x04 ++#define PVC_ERRORCODE_NAK 0x05 ++#define PVC_RESPONSEFLAG_ERROR 0x01 ++#define PVC_RESPONSEFLAG_OVERFLOW 0x02 ++#define PVC_RESPONSEFLAG_RESET 0x04 ++#define PVC_RESPONSEFLAG_INTERFACE 0x08 ++#define PVC_RESPONSEFLAG_CONTINUED 0x10 ++#define PVC_CMDFLAG_INTERRUPT 0x02 ++#define PVC_CMDFLAG_INTERFACE 0x04 ++#define PVC_CMDFLAG_SERIALIZE 0x08 ++#define PVC_CMDFLAG_CONTINUE 0x10 ++ ++/* Silicon Commands */ ++#define GET_DESCRIPTORS_CONTROL 0x01 ++#define GET_STRING_CONTROL 0x03 ++#define GET_LANGUAGE_CONTROL 0x05 ++#define SET_POWER_CONTROL 0x07 ++#define GET_FW_STATUS_CONTROL 0x08 ++#define GET_FW_VERSION_CONTROL 0x09 ++#define SET_DEBUG_LEVEL_CONTROL 0x0B ++#define GET_DEBUG_DATA_CONTROL 0x0C ++#define GET_PRODUCTION_INFO_CONTROL 0x0D ++ ++/* cmd defines */ ++#define SAA_CMDFLAG_CONTINUE 0x10 ++#define SAA_CMD_MAX_MSG_UNITS 256 ++ ++/* Some defines */ ++#define SAA_BUS_TIMEOUT 50 ++#define SAA_DEVICE_TIMEOUT 5000 ++#define SAA_DEVICE_MAXREQUESTSIZE 256 ++ ++/* Register addresses */ ++#define SAA_DEVICE_VERSION 0x30 ++#define SAA_DOWNLOAD_FLAGS 0x34 ++#define SAA_DOWNLOAD_FLAG 0x34 ++#define SAA_DOWNLOAD_FLAG_ACK 0x38 ++#define SAA_DATAREADY_FLAG 0x3C ++#define SAA_DATAREADY_FLAG_ACK 0x40 ++ ++/* Boot loader register and bit definitions */ ++#define SAA_BOOTLOADERERROR_FLAGS 0x44 ++#define SAA_DEVICE_IMAGE_SEARCHING 0x01 ++#define SAA_DEVICE_IMAGE_LOADING 0x02 ++#define SAA_DEVICE_IMAGE_BOOTING 0x03 ++#define SAA_DEVICE_IMAGE_CORRUPT 0x04 ++#define SAA_DEVICE_MEMORY_CORRUPT 0x08 ++#define SAA_DEVICE_NO_IMAGE 0x10 ++ ++/* Register addresses */ ++#define SAA_DEVICE_2ND_VERSION 0x50 ++#define SAA_DEVICE_2ND_DOWNLOADFLAG_OFFSET 0x54 ++ ++/* Register addresses */ ++#define SAA_SECONDSTAGEERROR_FLAGS 0x64 ++ ++/* Bootloader regs and flags */ ++#define SAA_DEVICE_DEADLOCK_DETECTED_OFFSET 0x6C ++#define SAA_DEVICE_DEADLOCK_DETECTED 0xDEADDEAD ++ ++/* Basic firmware status registers */ ++#define SAA_DEVICE_SYSINIT_STATUS_OFFSET 0x70 ++#define SAA_DEVICE_SYSINIT_STATUS 0x70 ++#define SAA_DEVICE_SYSINIT_MODE 0x74 ++#define SAA_DEVICE_SYSINIT_SPEC 0x78 ++#define SAA_DEVICE_SYSINIT_INST 0x7C ++#define SAA_DEVICE_SYSINIT_CPULOAD 0x80 ++#define SAA_DEVICE_SYSINIT_REMAINHEAP 0x84 ++ ++#define SAA_DEVICE_DOWNLOAD_OFFSET 0x1000 ++#define SAA_DEVICE_BUFFERBLOCKSIZE 0x1000 ++ ++#define SAA_DEVICE_2ND_BUFFERBLOCKSIZE 0x100000 ++#define SAA_DEVICE_2ND_DOWNLOAD_OFFSET 0x200000 ++ ++/* Descriptors */ ++#define CS_INTERFACE 0x24 ++ ++/* Descriptor subtypes */ ++#define VC_INPUT_TERMINAL 0x02 ++#define VC_OUTPUT_TERMINAL 0x03 ++#define VC_SELECTOR_UNIT 0x04 ++#define VC_PROCESSING_UNIT 0x05 ++#define FEATURE_UNIT 0x06 ++#define TUNER_UNIT 0x09 ++#define ENCODER_UNIT 0x0A ++#define EXTENSION_UNIT 0x0B ++#define VC_TUNER_PATH 0xF0 ++#define PVC_HARDWARE_DESCRIPTOR 0xF1 ++#define PVC_INTERFACE_DESCRIPTOR 0xF2 ++#define PVC_INFRARED_UNIT 0xF3 ++#define DRM_UNIT 0xF4 ++#define GENERAL_REQUEST 0xF5 ++ ++/* Format Types */ ++#define VS_FORMAT_TYPE 0x02 ++#define VS_FORMAT_TYPE_I 0x01 ++#define VS_FORMAT_UNCOMPRESSED 0x04 ++#define VS_FRAME_UNCOMPRESSED 0x05 ++#define VS_FORMAT_MPEG2PS 0x09 ++#define VS_FORMAT_MPEG2TS 0x0A ++#define VS_FORMAT_MPEG4SL 0x0B ++#define VS_FORMAT_WM9 0x0C ++#define VS_FORMAT_DIVX 0x0D ++#define VS_FORMAT_VBI 0x0E ++#define VS_FORMAT_RDS 0x0F ++ ++/* Device extension commands */ ++#define EXU_REGISTER_ACCESS_CONTROL 0x00 ++#define EXU_GPIO_CONTROL 0x01 ++#define EXU_GPIO_GROUP_CONTROL 0x02 ++#define EXU_INTERRUPT_CONTROL 0x03 ++ ++/* State Transition and args */ ++#define SAA_PROBE_CONTROL 0x01 ++#define SAA_COMMIT_CONTROL 0x02 ++#define SAA_STATE_CONTROL 0x03 ++#define SAA_DMASTATE_STOP 0x00 ++#define SAA_DMASTATE_ACQUIRE 0x01 ++#define SAA_DMASTATE_PAUSE 0x02 ++#define SAA_DMASTATE_RUN 0x03 ++ ++/* A/V Mux Input Selector */ ++#define SU_INPUT_SELECT_CONTROL 0x01 ++ ++/* Encoder Profiles */ ++#define EU_PROFILE_PS_DVD 0x06 ++#define EU_PROFILE_TS_HQ 0x09 ++#define EU_VIDEO_FORMAT_MPEG_2 0x02 ++ ++/* Tuner */ ++#define TU_AUDIO_MODE_CONTROL 0x17 ++ ++/* Video Formats */ ++#define TU_STANDARD_CONTROL 0x00 ++#define TU_STANDARD_AUTO_CONTROL 0x01 ++#define TU_STANDARD_NONE 0x00 ++#define TU_STANDARD_NTSC_M 0x01 ++#define TU_STANDARD_PAL_I 0x08 ++#define TU_STANDARD_MANUAL 0x00 ++#define TU_STANDARD_AUTO 0x01 ++ ++/* Video Controls */ ++#define PU_BRIGHTNESS_CONTROL 0x02 ++#define PU_CONTRAST_CONTROL 0x03 ++#define PU_HUE_CONTROL 0x06 ++#define PU_SATURATION_CONTROL 0x07 ++#define PU_SHARPNESS_CONTROL 0x08 ++ ++/* Audio Controls */ ++#define MUTE_CONTROL 0x01 ++#define VOLUME_CONTROL 0x02 ++#define AUDIO_DEFAULT_CONTROL 0x0D ++ ++/* Default Volume Levels */ ++#define TMHW_LEV_ADJ_DECLEV_DEFAULT 0x00 ++#define TMHW_LEV_ADJ_MONOLEV_DEFAULT 0x00 ++#define TMHW_LEV_ADJ_NICLEV_DEFAULT 0x00 ++#define TMHW_LEV_ADJ_SAPLEV_DEFAULT 0x00 ++#define TMHW_LEV_ADJ_ADCLEV_DEFAULT 0x00 ++ ++/* Encoder Related Commands */ ++#define EU_PROFILE_CONTROL 0x00 ++#define EU_VIDEO_FORMAT_CONTROL 0x01 ++#define EU_VIDEO_BIT_RATE_CONTROL 0x02 ++#define EU_VIDEO_RESOLUTION_CONTROL 0x03 ++#define EU_VIDEO_GOP_STRUCTURE_CONTROL 0x04 ++#define EU_VIDEO_INPUT_ASPECT_CONTROL 0x0A ++#define EU_AUDIO_FORMAT_CONTROL 0x0C ++#define EU_AUDIO_BIT_RATE_CONTROL 0x0D ++ ++/* Firmware Debugging */ ++#define SET_DEBUG_LEVEL_CONTROL 0x0B ++#define GET_DEBUG_DATA_CONTROL 0x0C +diff --git a/drivers/media/pci/saa7164/saa7164-types.h b/drivers/media/pci/saa7164/saa7164-types.h +new file mode 100644 +index 0000000..1d2140a +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-types.h +@@ -0,0 +1,442 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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. ++ */ ++ ++/* TODO: Cleanup and shorten the namespace */ ++ ++/* Some structues are passed directly to/from the firmware and ++ * have strict alignment requirements. This is one of them. ++ */ ++struct tmComResHWDescr { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u16 bcdSpecVersion; ++ u32 dwClockFrequency; ++ u32 dwClockUpdateRes; ++ u8 bCapabilities; ++ u32 dwDeviceRegistersLocation; ++ u32 dwHostMemoryRegion; ++ u32 dwHostMemoryRegionSize; ++ u32 dwHostHibernatMemRegion; ++ u32 dwHostHibernatMemRegionSize; ++} __attribute__((packed)); ++ ++/* This is DWORD aligned on windows but I can't find the right ++ * gcc syntax to match the binary data from the device. ++ * I've manually padded with Reserved[3] bytes to match the hardware, ++ * but this could break if GCC decies to pack in a different way. ++ */ ++struct tmComResInterfaceDescr { ++ u8 bLength; ++ u8 bDescriptorType; ++ u8 bDescriptorSubtype; ++ u8 bFlags; ++ u8 bInterfaceType; ++ u8 bInterfaceId; ++ u8 bBaseInterface; ++ u8 bInterruptId; ++ u8 bDebugInterruptId; ++ u8 BARLocation; ++ u8 Reserved[3]; ++}; ++ ++struct tmComResBusDescr { ++ u64 CommandRing; ++ u64 ResponseRing; ++ u32 CommandWrite; ++ u32 CommandRead; ++ u32 ResponseWrite; ++ u32 ResponseRead; ++}; ++ ++enum tmBusType { ++ NONE = 0, ++ TYPE_BUS_PCI = 1, ++ TYPE_BUS_PCIe = 2, ++ TYPE_BUS_USB = 3, ++ TYPE_BUS_I2C = 4 ++}; ++ ++struct tmComResBusInfo { ++ enum tmBusType Type; ++ u16 m_wMaxReqSize; ++ u8 *m_pdwSetRing; ++ u32 m_dwSizeSetRing; ++ u8 *m_pdwGetRing; ++ u32 m_dwSizeGetRing; ++ u32 m_dwSetWritePos; ++ u32 m_dwSetReadPos; ++ u32 m_dwGetWritePos; ++ u32 m_dwGetReadPos; ++ ++ /* All access is protected */ ++ struct mutex lock; ++ ++}; ++ ++struct tmComResInfo { ++ u8 id; ++ u8 flags; ++ u16 size; ++ u32 command; ++ u16 controlselector; ++ u8 seqno; ++} __attribute__((packed)); ++ ++enum tmComResCmd { ++ SET_CUR = 0x01, ++ GET_CUR = 0x81, ++ GET_MIN = 0x82, ++ GET_MAX = 0x83, ++ GET_RES = 0x84, ++ GET_LEN = 0x85, ++ GET_INFO = 0x86, ++ GET_DEF = 0x87 ++}; ++ ++struct cmd { ++ u8 seqno; ++ u32 inuse; ++ u32 timeout; ++ u32 signalled; ++ struct mutex lock; ++ wait_queue_head_t wait; ++}; ++ ++struct tmDescriptor { ++ u32 pathid; ++ u32 size; ++ void *descriptor; ++}; ++ ++struct tmComResDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++} __attribute__((packed)); ++ ++struct tmComResExtDevDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u32 devicetype; ++ u16 deviceid; ++ u32 numgpiopins; ++ u8 numgpiogroups; ++ u8 controlsize; ++} __attribute__((packed)); ++ ++struct tmComResGPIO { ++ u32 pin; ++ u8 state; ++} __attribute__((packed)); ++ ++struct tmComResPathDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 pathid; ++} __attribute__((packed)); ++ ++/* terminaltype */ ++enum tmComResTermType { ++ ITT_ANTENNA = 0x0203, ++ LINE_CONNECTOR = 0x0603, ++ SPDIF_CONNECTOR = 0x0605, ++ COMPOSITE_CONNECTOR = 0x0401, ++ SVIDEO_CONNECTOR = 0x0402, ++ COMPONENT_CONNECTOR = 0x0403, ++ STANDARD_DMA = 0xF101 ++}; ++ ++struct tmComResAntTermDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 terminalid; ++ u16 terminaltype; ++ u8 assocterminal; ++ u8 iterminal; ++ u8 controlsize; ++} __attribute__((packed)); ++ ++struct tmComResTunerDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u8 sourceid; ++ u8 iunit; ++ u32 tuningstandards; ++ u8 controlsize; ++ u32 controls; ++} __attribute__((packed)); ++ ++enum tmBufferFlag { ++ /* the buffer does not contain any valid data */ ++ TM_BUFFER_FLAG_EMPTY, ++ ++ /* the buffer is filled with valid data */ ++ TM_BUFFER_FLAG_DONE, ++ ++ /* the buffer is the dummy buffer - TODO??? */ ++ TM_BUFFER_FLAG_DUMMY_BUFFER ++}; ++ ++struct tmBuffer { ++ u64 *pagetablevirt; ++ u64 pagetablephys; ++ u16 offset; ++ u8 *context; ++ u64 timestamp; ++ enum tmBufferFlag BufferFlag; ++ u32 lostbuffers; ++ u32 validbuffers; ++ u64 *dummypagevirt; ++ u64 dummypagephys; ++ u64 *addressvirt; ++}; ++ ++struct tmHWStreamParameters { ++ u32 bitspersample; ++ u32 samplesperline; ++ u32 numberoflines; ++ u32 pitch; ++ u32 linethreshold; ++ u64 **pagetablelistvirt; ++ u64 *pagetablelistphys; ++ u32 numpagetables; ++ u32 numpagetableentries; ++}; ++ ++struct tmStreamParameters { ++ struct tmHWStreamParameters HWStreamParameters; ++ u64 qwDummyPageTablePhys; ++ u64 *pDummyPageTableVirt; ++}; ++ ++struct tmComResDMATermDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtyle; ++ u8 unitid; ++ u16 terminaltype; ++ u8 assocterminal; ++ u8 sourceid; ++ u8 iterminal; ++ u32 BARLocation; ++ u8 flags; ++ u8 interruptid; ++ u8 buffercount; ++ u8 metadatasize; ++ u8 numformats; ++ u8 controlsize; ++} __attribute__((packed)); ++ ++/* ++ * ++ * Description: ++ * This is the transport stream format header. ++ * ++ * Settings: ++ * bLength - The size of this descriptor in bytes. ++ * bDescriptorType - CS_INTERFACE. ++ * bDescriptorSubtype - VS_FORMAT_MPEG2TS descriptor subtype. ++ * bFormatIndex - A non-zero constant that uniquely identifies the ++ * format. ++ * bDataOffset - Offset to TSP packet within MPEG-2 TS transport ++ * stride, in bytes. ++ * bPacketLength - Length of TSP packet, in bytes (typically 188). ++ * bStrideLength - Length of MPEG-2 TS transport stride. ++ * guidStrideFormat - A Globally Unique Identifier indicating the ++ * format of the stride data (if any). Set to zeros ++ * if there is no Stride Data, or if the Stride ++ * Data is to be ignored by the application. ++ * ++ */ ++struct tmComResTSFormatDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 bFormatIndex; ++ u8 bDataOffset; ++ u8 bPacketLength; ++ u8 bStrideLength; ++ u8 guidStrideFormat[16]; ++} __attribute__((packed)); ++ ++/* Encoder related structures */ ++ ++/* A/V Mux Selector */ ++struct tmComResSelDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u8 nrinpins; ++ u8 sourceid; ++} __attribute__((packed)); ++ ++/* A/V Audio processor definitions */ ++struct tmComResProcDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u8 sourceid; ++ u16 wreserved; ++ u8 controlsize; ++} __attribute__((packed)); ++ ++/* Video bitrate control message */ ++#define EU_VIDEO_BIT_RATE_MODE_CONSTANT (0) ++#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_AVERAGE (1) ++#define EU_VIDEO_BIT_RATE_MODE_VARIABLE_PEAK (2) ++struct tmComResEncVideoBitRate { ++ u8 ucVideoBitRateMode; ++ u32 dwVideoBitRate; ++ u32 dwVideoBitRatePeak; ++} __attribute__((packed)); ++ ++/* Video Encoder Aspect Ratio message */ ++struct tmComResEncVideoInputAspectRatio { ++ u8 width; ++ u8 height; ++} __attribute__((packed)); ++ ++/* Video Encoder GOP IBP message */ ++/* 1. IPPPPPPPPPPPPPP */ ++/* 2. IBPBPBPBPBPBPBP */ ++/* 3. IBBPBBPBBPBBP */ ++#define SAA7164_ENCODER_DEFAULT_GOP_DIST (1) ++#define SAA7164_ENCODER_DEFAULT_GOP_SIZE (15) ++struct tmComResEncVideoGopStructure { ++ u8 ucGOPSize; /* GOP Size 12, 15 */ ++ u8 ucRefFrameDist; /* Reference Frame Distance */ ++} __attribute__((packed)); ++ ++/* Encoder processor definition */ ++struct tmComResEncoderDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u8 vsourceid; ++ u8 asourceid; ++ u8 iunit; ++ u32 dwmControlCap; ++ u32 dwmProfileCap; ++ u32 dwmVidFormatCap; ++ u8 bmVidBitrateCap; ++ u16 wmVidResolutionsCap; ++ u16 wmVidFrmRateCap; ++ u32 dwmAudFormatCap; ++ u8 bmAudBitrateCap; ++} __attribute__((packed)); ++ ++/* Audio processor definition */ ++struct tmComResAFeatureDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 unitid; ++ u8 sourceid; ++ u8 controlsize; ++} __attribute__((packed)); ++ ++/* Audio control messages */ ++struct tmComResAudioDefaults { ++ u8 ucDecoderLevel; ++ u8 ucDecoderFM_Level; ++ u8 ucMonoLevel; ++ u8 ucNICAM_Level; ++ u8 ucSAP_Level; ++ u8 ucADC_Level; ++} __attribute__((packed)); ++ ++/* Audio bitrate control message */ ++struct tmComResEncAudioBitRate { ++ u8 ucAudioBitRateMode; ++ u32 dwAudioBitRate; ++ u32 dwAudioBitRatePeak; ++} __attribute__((packed)); ++ ++/* Tuner / AV Decoder messages */ ++struct tmComResTunerStandard { ++ u8 std; ++ u32 country; ++} __attribute__((packed)); ++ ++struct tmComResTunerStandardAuto { ++ u8 mode; ++} __attribute__((packed)); ++ ++/* EEPROM definition for PS stream types */ ++struct tmComResPSFormatDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; ++ u8 bFormatIndex; ++ u16 wPacketLength; ++ u16 wPackLength; ++ u8 bPackDataType; ++} __attribute__((packed)); ++ ++/* VBI control structure */ ++struct tmComResVBIFormatDescrHeader { ++ u8 len; ++ u8 type; ++ u8 subtype; /* VS_FORMAT_VBI */ ++ u8 bFormatIndex; ++ u32 VideoStandard; /* See KS_AnalogVideoStandard, NTSC = 1 */ ++ u8 StartLine; /* NTSC Start = 10 */ ++ u8 EndLine; /* NTSC = 21 */ ++ u8 FieldRate; /* 60 for NTSC */ ++ u8 bNumLines; /* Unused - scheduled for removal */ ++} __attribute__((packed)); ++ ++struct tmComResProbeCommit { ++ u16 bmHint; ++ u8 bFormatIndex; ++ u8 bFrameIndex; ++} __attribute__((packed)); ++ ++struct tmComResDebugSetLevel { ++ u32 dwDebugLevel; ++} __attribute__((packed)); ++ ++struct tmComResDebugGetData { ++ u32 dwResult; ++ u8 ucDebugData[256]; ++} __attribute__((packed)); ++ ++struct tmFwInfoStruct { ++ u32 status; ++ u32 mode; ++ u32 devicespec; ++ u32 deviceinst; ++ u32 CPULoad; ++ u32 RemainHeap; ++ u32 CPUClock; ++ u32 RAMSpeed; ++} __attribute__((packed)); +diff --git a/drivers/media/pci/saa7164/saa7164-vbi.c b/drivers/media/pci/saa7164/saa7164-vbi.c +new file mode 100644 +index 0000000..b453229 +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164-vbi.c +@@ -0,0 +1,1376 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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 "saa7164.h" ++ ++static struct saa7164_tvnorm saa7164_tvnorms[] = { ++ { ++ .name = "NTSC-M", ++ .id = V4L2_STD_NTSC_M, ++ }, { ++ .name = "NTSC-JP", ++ .id = V4L2_STD_NTSC_M_JP, ++ } ++}; ++ ++static const u32 saa7164_v4l2_ctrls[] = { ++ 0 ++}; ++ ++/* Take the encoder configuration from the port struct and ++ * flush it to the hardware. ++ */ ++static void saa7164_vbi_configure(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ port->vbi_params.width = port->width; ++ port->vbi_params.height = port->height; ++ port->vbi_params.is_50hz = ++ (port->encodernorm.id & V4L2_STD_625_50) != 0; ++ ++ /* Set up the DIF (enable it) for analog mode by default */ ++ saa7164_api_initialize_dif(port); ++ ++ /* Configure the correct video standard */ ++#if 0 ++ saa7164_api_configure_dif(port, port->encodernorm.id); ++#endif ++ ++#if 0 ++ /* Ensure the audio decoder is correct configured */ ++ saa7164_api_set_audio_std(port); ++#endif ++ dprintk(DBGLVL_VBI, "%s() ends\n", __func__); ++} ++ ++static int saa7164_vbi_buffers_dealloc(struct saa7164_port *port) ++{ ++ struct list_head *c, *n, *p, *q, *l, *v; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ ++ /* Remove any allocated buffers */ ++ mutex_lock(&port->dmaqueue_lock); ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d) dmaqueue\n", __func__, port->nr); ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ list_del(c); ++ saa7164_buffer_dealloc(buf); ++ } ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d) used\n", __func__, port->nr); ++ list_for_each_safe(p, q, &port->list_buf_used.list) { ++ ubuf = list_entry(p, struct saa7164_user_buffer, list); ++ list_del(p); ++ saa7164_buffer_dealloc_user(ubuf); ++ } ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d) free\n", __func__, port->nr); ++ list_for_each_safe(l, v, &port->list_buf_free.list) { ++ ubuf = list_entry(l, struct saa7164_user_buffer, list); ++ list_del(l); ++ saa7164_buffer_dealloc_user(ubuf); ++ } ++ ++ mutex_unlock(&port->dmaqueue_lock); ++ dprintk(DBGLVL_VBI, "%s(port=%d) done\n", __func__, port->nr); ++ ++ return 0; ++} ++ ++/* Dynamic buffer switch at vbi start time */ ++static int saa7164_vbi_buffers_alloc(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ struct tmHWStreamParameters *params = &port->hw_streamingparams; ++ int result = -ENODEV, i; ++ int len = 0; ++ ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ /* TODO: NTSC SPECIFIC */ ++ /* Init and establish defaults */ ++ params->samplesperline = 1440; ++ params->numberoflines = 12; ++ params->numberoflines = 18; ++ params->pitch = 1600; ++ params->pitch = 1440; ++ params->numpagetables = 2 + ++ ((params->numberoflines * params->pitch) / PAGE_SIZE); ++ params->bitspersample = 8; ++ params->linethreshold = 0; ++ params->pagetablelistvirt = NULL; ++ params->pagetablelistphys = NULL; ++ params->numpagetableentries = port->hwcfg.buffercount; ++ ++ /* Allocate the PCI resources, buffers (hard) */ ++ for (i = 0; i < port->hwcfg.buffercount; i++) { ++ buf = saa7164_buffer_alloc(port, ++ params->numberoflines * ++ params->pitch); ++ ++ if (!buf) { ++ printk(KERN_ERR "%s() failed " ++ "(errno = %d), unable to allocate buffer\n", ++ __func__, result); ++ result = -ENOMEM; ++ goto failed; ++ } else { ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_add_tail(&buf->list, &port->dmaqueue.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ } ++ } ++ ++ /* Allocate some kernel buffers for copying ++ * to userpsace. ++ */ ++ len = params->numberoflines * params->pitch; ++ ++ if (vbi_buffers < 16) ++ vbi_buffers = 16; ++ if (vbi_buffers > 512) ++ vbi_buffers = 512; ++ ++ for (i = 0; i < vbi_buffers; i++) { ++ ++ ubuf = saa7164_buffer_alloc_user(dev, len); ++ if (ubuf) { ++ mutex_lock(&port->dmaqueue_lock); ++ list_add_tail(&ubuf->list, &port->list_buf_free.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ } ++ ++ } ++ ++ result = 0; ++ ++failed: ++ return result; ++} ++ ++ ++static int saa7164_vbi_initialize(struct saa7164_port *port) ++{ ++ saa7164_vbi_configure(port); ++ return 0; ++} ++ ++/* -- V4L2 --------------------------------------------------------- */ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ unsigned int i; ++ ++ dprintk(DBGLVL_VBI, "%s(id=0x%x)\n", __func__, (u32)*id); ++ ++ for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) { ++ if (*id & saa7164_tvnorms[i].id) ++ break; ++ } ++ if (i == ARRAY_SIZE(saa7164_tvnorms)) ++ return -EINVAL; ++ ++ port->encodernorm = saa7164_tvnorms[i]; ++ ++ /* Update the audio decoder while is not running in ++ * auto detect mode. ++ */ ++ saa7164_api_set_audio_std(port); ++ ++ dprintk(DBGLVL_VBI, "%s(id=0x%x) OK\n", __func__, (u32)*id); ++ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *i) ++{ ++ int n; ++ ++ char *inputs[] = { "tuner", "composite", "svideo", "aux", ++ "composite 2", "svideo 2", "aux 2" }; ++ ++ if (i->index >= 7) ++ return -EINVAL; ++ ++ strcpy(i->name, inputs[i->index]); ++ ++ if (i->index == 0) ++ i->type = V4L2_INPUT_TYPE_TUNER; ++ else ++ i->type = V4L2_INPUT_TYPE_CAMERA; ++ ++ for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++) ++ i->std |= saa7164_tvnorms[n].id; ++ ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ if (saa7164_api_get_videomux(port) != SAA_OK) ++ return -EIO; ++ ++ *i = (port->mux_input - 1); ++ ++ dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, *i); ++ ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s() input=%d\n", __func__, i); ++ ++ if (i >= 7) ++ return -EINVAL; ++ ++ port->mux_input = i + 1; ++ ++ if (saa7164_api_set_videomux(port) != SAA_OK) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int vidioc_g_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ if (0 != t->index) ++ return -EINVAL; ++ ++ strcpy(t->name, "tuner"); ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO; ++ ++ dprintk(DBGLVL_VBI, "VIDIOC_G_TUNER: tuner type %d\n", t->type); ++ ++ return 0; ++} ++ ++static int vidioc_s_tuner(struct file *file, void *priv, ++ struct v4l2_tuner *t) ++{ ++ /* Update the A/V core */ ++ return 0; ++} ++ ++static int vidioc_g_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ ++ f->type = V4L2_TUNER_ANALOG_TV; ++ f->frequency = port->freq; ++ ++ return 0; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *priv, ++ struct v4l2_frequency *f) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_port *tsport; ++ struct dvb_frontend *fe; ++ ++ /* TODO: Pull this for the std */ ++ struct analog_parameters params = { ++ .mode = V4L2_TUNER_ANALOG_TV, ++ .audmode = V4L2_TUNER_MODE_STEREO, ++ .std = port->encodernorm.id, ++ .frequency = f->frequency ++ }; ++ ++ /* Stop the encoder */ ++ dprintk(DBGLVL_VBI, "%s() frequency=%d tuner=%d\n", __func__, ++ f->frequency, f->tuner); ++ ++ if (f->tuner != 0) ++ return -EINVAL; ++ ++ if (f->type != V4L2_TUNER_ANALOG_TV) ++ return -EINVAL; ++ ++ port->freq = f->frequency; ++ ++ /* Update the hardware */ ++ if (port->nr == SAA7164_PORT_VBI1) ++ tsport = &dev->ports[SAA7164_PORT_TS1]; ++ else ++ if (port->nr == SAA7164_PORT_VBI2) ++ tsport = &dev->ports[SAA7164_PORT_TS2]; ++ else ++ BUG(); ++ ++ fe = tsport->dvb.frontend; ++ ++ if (fe && fe->ops.tuner_ops.set_analog_params) ++ fe->ops.tuner_ops.set_analog_params(fe, ¶ms); ++ else ++ printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__); ++ ++ saa7164_vbi_initialize(port); ++ ++ return 0; ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, ++ ctl->id, ctl->value); ++ ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ ctl->value = port->ctl_brightness; ++ break; ++ case V4L2_CID_CONTRAST: ++ ctl->value = port->ctl_contrast; ++ break; ++ case V4L2_CID_SATURATION: ++ ctl->value = port->ctl_saturation; ++ break; ++ case V4L2_CID_HUE: ++ ctl->value = port->ctl_hue; ++ break; ++ case V4L2_CID_SHARPNESS: ++ ctl->value = port->ctl_sharpness; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ ctl->value = port->ctl_volume; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctl) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ ++ dprintk(DBGLVL_VBI, "%s(id=%d, value=%d)\n", __func__, ++ ctl->id, ctl->value); ++ ++ switch (ctl->id) { ++ case V4L2_CID_BRIGHTNESS: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_brightness = ctl->value; ++ saa7164_api_set_usercontrol(port, ++ PU_BRIGHTNESS_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_CONTRAST: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_contrast = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_SATURATION: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_saturation = ctl->value; ++ saa7164_api_set_usercontrol(port, ++ PU_SATURATION_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_HUE: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_hue = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_HUE_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_SHARPNESS: ++ if ((ctl->value >= 0) && (ctl->value <= 255)) { ++ port->ctl_sharpness = ctl->value; ++ saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL); ++ } else ++ ret = -EINVAL; ++ break; ++ case V4L2_CID_AUDIO_VOLUME: ++ if ((ctl->value >= -83) && (ctl->value <= 24)) { ++ port->ctl_volume = ctl->value; ++ saa7164_api_set_audio_volume(port, port->ctl_volume); ++ } else ++ ret = -EINVAL; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_get_ctrl(struct saa7164_port *port, ++ struct v4l2_ext_control *ctrl) ++{ ++ struct saa7164_vbi_params *params = &port->vbi_params; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ ctrl->value = params->stream_type; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ ctrl->value = params->ctl_mute; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ ctrl->value = params->ctl_aspect; ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ ctrl->value = params->refdist; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ ctrl->value = params->gop_size; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static int vidioc_g_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_get_ctrl(port, ctrl); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) ++{ ++ int ret = -EINVAL; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ if ((ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) || ++ (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ if ((ctrl->value >= 0) && ++ (ctrl->value <= 1)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ if ((ctrl->value >= V4L2_MPEG_VIDEO_ASPECT_1x1) && ++ (ctrl->value <= V4L2_MPEG_VIDEO_ASPECT_221x100)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ if ((ctrl->value >= 0) && ++ (ctrl->value <= 255)) ++ ret = 0; ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ if ((ctrl->value >= 1) && ++ (ctrl->value <= 3)) ++ ret = 0; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int vidioc_try_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_try_ctrl(ctrl, 0); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_set_ctrl(struct saa7164_port *port, ++ struct v4l2_ext_control *ctrl) ++{ ++ struct saa7164_vbi_params *params = &port->vbi_params; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ params->stream_type = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ params->ctl_mute = ctrl->value; ++ ret = saa7164_api_audio_mute(port, params->ctl_mute); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ++ ret); ++ ret = -EIO; ++ } ++ break; ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ params->ctl_aspect = ctrl->value; ++ ret = saa7164_api_set_aspect_ratio(port); ++ if (ret != SAA_OK) { ++ printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__, ++ ret); ++ ret = -EIO; ++ } ++ break; ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ params->refdist = ctrl->value; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ params->gop_size = ctrl->value; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* TODO: Update the hardware */ ++ ++ return ret; ++} ++ ++static int vidioc_s_ext_ctrls(struct file *file, void *priv, ++ struct v4l2_ext_controls *ctrls) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ int i, err = 0; ++ ++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { ++ for (i = 0; i < ctrls->count; i++) { ++ struct v4l2_ext_control *ctrl = ctrls->controls + i; ++ ++ err = saa7164_try_ctrl(ctrl, 0); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ err = saa7164_set_ctrl(port, ctrl); ++ if (err) { ++ ctrls->error_idx = i; ++ break; ++ } ++ } ++ return err; ++ ++ } ++ ++ return -EINVAL; ++} ++ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ strcpy(cap->driver, dev->name); ++ strlcpy(cap->card, saa7164_boards[dev->board].name, ++ sizeof(cap->card)); ++ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); ++ ++ cap->capabilities = ++ V4L2_CAP_VBI_CAPTURE | ++ V4L2_CAP_READWRITE | ++ 0; ++ ++ cap->capabilities |= V4L2_CAP_TUNER; ++ cap->version = 0; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strlcpy(f->description, "VBI", sizeof(f->description)); ++ f->pixelformat = V4L2_PIX_FMT_MPEG; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ f->fmt.pix.width = port->width; ++ f->fmt.pix.height = port->height; ++ ++ dprintk(DBGLVL_VBI, "VIDIOC_G_FMT: w: %d, h: %d\n", ++ port->width, port->height); ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ dprintk(DBGLVL_VBI, "VIDIOC_TRY_FMT: w: %d, h: %d\n", ++ port->width, port->height); ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = ++ port->ts_packet_size * port->ts_packet_count; ++ f->fmt.pix.colorspace = 0; ++ ++ dprintk(DBGLVL_VBI, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); ++ ++ return 0; ++} ++ ++static int fill_queryctrl(struct saa7164_vbi_params *params, ++ struct v4l2_queryctrl *c) ++{ ++ switch (c->id) { ++ case V4L2_CID_BRIGHTNESS: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 127); ++ case V4L2_CID_CONTRAST: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 66); ++ case V4L2_CID_SATURATION: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 62); ++ case V4L2_CID_HUE: ++ return v4l2_ctrl_query_fill(c, 0x0, 0xff, 1, 128); ++ case V4L2_CID_SHARPNESS: ++ return v4l2_ctrl_query_fill(c, 0x0, 0x0f, 1, 8); ++ case V4L2_CID_MPEG_AUDIO_MUTE: ++ return v4l2_ctrl_query_fill(c, 0x0, 0x01, 1, 0); ++ case V4L2_CID_AUDIO_VOLUME: ++ return v4l2_ctrl_query_fill(c, -83, 24, 1, 20); ++ case V4L2_CID_MPEG_STREAM_TYPE: ++ return v4l2_ctrl_query_fill(c, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_PS, ++ V4L2_MPEG_STREAM_TYPE_MPEG2_TS, ++ 1, V4L2_MPEG_STREAM_TYPE_MPEG2_PS); ++ case V4L2_CID_MPEG_VIDEO_ASPECT: ++ return v4l2_ctrl_query_fill(c, ++ V4L2_MPEG_VIDEO_ASPECT_1x1, ++ V4L2_MPEG_VIDEO_ASPECT_221x100, ++ 1, V4L2_MPEG_VIDEO_ASPECT_4x3); ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ return v4l2_ctrl_query_fill(c, 1, 255, 1, 15); ++ case V4L2_CID_MPEG_VIDEO_B_FRAMES: ++ return v4l2_ctrl_query_fill(c, ++ 1, 3, 1, 1); ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *c) ++{ ++ struct saa7164_vbi_fh *fh = priv; ++ struct saa7164_port *port = fh->port; ++ int i, next; ++ u32 id = c->id; ++ ++ memset(c, 0, sizeof(*c)); ++ ++ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); ++ c->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; ++ ++ for (i = 0; i < ARRAY_SIZE(saa7164_v4l2_ctrls); i++) { ++ if (next) { ++ if (c->id < saa7164_v4l2_ctrls[i]) ++ c->id = saa7164_v4l2_ctrls[i]; ++ else ++ continue; ++ } ++ ++ if (c->id == saa7164_v4l2_ctrls[i]) ++ return fill_queryctrl(&port->vbi_params, c); ++ ++ if (c->id < saa7164_v4l2_ctrls[i]) ++ break; ++ } ++ ++ return -EINVAL; ++} ++ ++static int saa7164_vbi_stop_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_VBI, "%s() Stopped\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_vbi_acquire_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++static int saa7164_vbi_pause_port(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int ret; ++ ++ ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n", ++ __func__, ret); ++ ret = -EIO; ++ } else { ++ dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/* Firmware is very windows centric, meaning you have to transition ++ * the part through AVStream / KS Windows stages, forwards or backwards. ++ * States are: stopped, acquired (h/w), paused, started. ++ * We have to leave here will all of the soft buffers on the free list, ++ * else the cfg_post() func won't have soft buffers to correctly configure. ++ */ ++static int saa7164_vbi_stop_streaming(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ struct saa7164_buffer *buf; ++ struct saa7164_user_buffer *ubuf; ++ struct list_head *c, *n; ++ int ret; ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); ++ ++ ret = saa7164_vbi_pause_port(port); ++ ret = saa7164_vbi_acquire_port(port); ++ ret = saa7164_vbi_stop_port(port); ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d) Hardware stopped\n", __func__, ++ port->nr); ++ ++ /* Reset the state of any allocated buffer resources */ ++ mutex_lock(&port->dmaqueue_lock); ++ ++ /* Reset the hard and soft buffer state */ ++ list_for_each_safe(c, n, &port->dmaqueue.list) { ++ buf = list_entry(c, struct saa7164_buffer, list); ++ buf->flags = SAA7164_BUFFER_FREE; ++ buf->pos = 0; ++ } ++ ++ list_for_each_safe(c, n, &port->list_buf_used.list) { ++ ubuf = list_entry(c, struct saa7164_user_buffer, list); ++ ubuf->pos = 0; ++ list_move_tail(&ubuf->list, &port->list_buf_free.list); ++ } ++ ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ /* Free any allocated resources */ ++ saa7164_vbi_buffers_dealloc(port); ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d) Released\n", __func__, port->nr); ++ ++ return ret; ++} ++ ++static int saa7164_vbi_start_streaming(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int result, ret = 0; ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); ++ ++ port->done_first_interrupt = 0; ++ ++ /* allocate all of the PCIe DMA buffer resources on the fly, ++ * allowing switching between TS and PS payloads without ++ * requiring a complete driver reload. ++ */ ++ saa7164_vbi_buffers_alloc(port); ++ ++ /* Configure the encoder with any cache values */ ++#if 0 ++ saa7164_api_set_encoder(port); ++ saa7164_api_get_encoder(port); ++#endif ++ ++ /* Place the empty buffers on the hardware */ ++ saa7164_buffer_cfg_port(port); ++ ++ /* Negotiate format */ ++ if (saa7164_api_set_vbi_format(port) != SAA_OK) { ++ printk(KERN_ERR "%s() No supported VBI format\n", __func__); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* Acquire the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_VBI, "%s() Acquired\n", __func__); ++ ++ /* Pause the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_vbi_stop_port(port); ++ if (result != SAA_OK) { ++ printk(KERN_ERR "%s() pause/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ goto out; ++ } else ++ dprintk(DBGLVL_VBI, "%s() Paused\n", __func__); ++ ++ /* Start the hardware */ ++ result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN); ++ if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { ++ printk(KERN_ERR "%s() run transition failed, result = 0x%x\n", ++ __func__, result); ++ ++ /* Stop the hardware, regardless */ ++ result = saa7164_vbi_acquire_port(port); ++ result = saa7164_vbi_stop_port(port); ++ if (result != SAA_OK) { ++ printk(KERN_ERR "%s() run/forced stop transition " ++ "failed, res = 0x%x\n", __func__, result); ++ } ++ ++ ret = -EIO; ++ } else ++ dprintk(DBGLVL_VBI, "%s() Running\n", __func__); ++ ++out: ++ return ret; ++} ++ ++static int saa7164_vbi_fmt(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ /* ntsc */ ++ f->fmt.vbi.samples_per_line = 1600; ++ f->fmt.vbi.samples_per_line = 1440; ++ f->fmt.vbi.sampling_rate = 27000000; ++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; ++ f->fmt.vbi.offset = 0; ++ f->fmt.vbi.flags = 0; ++ f->fmt.vbi.start[0] = 10; ++ f->fmt.vbi.count[0] = 18; ++ f->fmt.vbi.start[1] = 263 + 10 + 1; ++ f->fmt.vbi.count[1] = 18; ++ return 0; ++} ++ ++static int fops_open(struct file *file) ++{ ++ struct saa7164_dev *dev; ++ struct saa7164_port *port; ++ struct saa7164_vbi_fh *fh; ++ ++ port = (struct saa7164_port *)video_get_drvdata(video_devdata(file)); ++ if (!port) ++ return -ENODEV; ++ ++ dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ /* allocate + initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (NULL == fh) ++ return -ENOMEM; ++ ++ file->private_data = fh; ++ fh->port = port; ++ ++ return 0; ++} ++ ++static int fops_release(struct file *file) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ /* Shut device down on last close */ ++ if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { ++ if (atomic_dec_return(&port->v4l_reader_count) == 0) { ++ /* stop vbi capture then cancel buffers */ ++ saa7164_vbi_stop_streaming(port); ++ } ++ } ++ ++ file->private_data = NULL; ++ kfree(fh); ++ ++ return 0; ++} ++ ++static struct ++saa7164_user_buffer *saa7164_vbi_next_buf(struct saa7164_port *port) ++{ ++ struct saa7164_user_buffer *ubuf = NULL; ++ struct saa7164_dev *dev = port->dev; ++ u32 crc; ++ ++ mutex_lock(&port->dmaqueue_lock); ++ if (!list_empty(&port->list_buf_used.list)) { ++ ubuf = list_first_entry(&port->list_buf_used.list, ++ struct saa7164_user_buffer, list); ++ ++ if (crc_checking) { ++ crc = crc32(0, ubuf->data, ubuf->actual_size); ++ if (crc != ubuf->crc) { ++ printk(KERN_ERR "%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n", ++ __func__, ++ ubuf, ubuf->crc, crc); ++ } ++ } ++ ++ } ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ dprintk(DBGLVL_VBI, "%s() returns %p\n", __func__, ubuf); ++ ++ return ubuf; ++} ++ ++static ssize_t fops_read(struct file *file, char __user *buffer, ++ size_t count, loff_t *pos) ++{ ++ struct saa7164_vbi_fh *fh = file->private_data; ++ struct saa7164_port *port = fh->port; ++ struct saa7164_user_buffer *ubuf = NULL; ++ struct saa7164_dev *dev = port->dev; ++ int ret = 0; ++ int rem, cnt; ++ u8 *p; ++ ++ port->last_read_msecs_diff = port->last_read_msecs; ++ port->last_read_msecs = jiffies_to_msecs(jiffies); ++ port->last_read_msecs_diff = port->last_read_msecs - ++ port->last_read_msecs_diff; ++ ++ saa7164_histogram_update(&port->read_interval, ++ port->last_read_msecs_diff); ++ ++ if (*pos) { ++ printk(KERN_ERR "%s() ESPIPE\n", __func__); ++ return -ESPIPE; ++ } ++ ++ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { ++ if (atomic_inc_return(&port->v4l_reader_count) == 1) { ++ ++ if (saa7164_vbi_initialize(port) < 0) { ++ printk(KERN_ERR "%s() EINVAL\n", __func__); ++ return -EINVAL; ++ } ++ ++ saa7164_vbi_start_streaming(port); ++ msleep(200); ++ } ++ } ++ ++ /* blocking wait for buffer */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_vbi_next_buf(port))) { ++ printk(KERN_ERR "%s() ERESTARTSYS\n", __func__); ++ return -ERESTARTSYS; ++ } ++ } ++ ++ /* Pull the first buffer from the used list */ ++ ubuf = saa7164_vbi_next_buf(port); ++ ++ while ((count > 0) && ubuf) { ++ ++ /* set remaining bytes to copy */ ++ rem = ubuf->actual_size - ubuf->pos; ++ cnt = rem > count ? count : rem; ++ ++ p = ubuf->data + ubuf->pos; ++ ++ dprintk(DBGLVL_VBI, ++ "%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n", ++ __func__, (int)count, cnt, rem, ubuf, ubuf->pos); ++ ++ if (copy_to_user(buffer, p, cnt)) { ++ printk(KERN_ERR "%s() copy_to_user failed\n", __func__); ++ if (!ret) { ++ printk(KERN_ERR "%s() EFAULT\n", __func__); ++ ret = -EFAULT; ++ } ++ goto err; ++ } ++ ++ ubuf->pos += cnt; ++ count -= cnt; ++ buffer += cnt; ++ ret += cnt; ++ ++ if (ubuf->pos > ubuf->actual_size) ++ printk(KERN_ERR "read() pos > actual, huh?\n"); ++ ++ if (ubuf->pos == ubuf->actual_size) { ++ ++ /* finished with current buffer, take next buffer */ ++ ++ /* Requeue the buffer on the free list */ ++ ubuf->pos = 0; ++ ++ mutex_lock(&port->dmaqueue_lock); ++ list_move_tail(&ubuf->list, &port->list_buf_free.list); ++ mutex_unlock(&port->dmaqueue_lock); ++ ++ /* Dequeue next */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_vbi_next_buf(port))) { ++ break; ++ } ++ } ++ ubuf = saa7164_vbi_next_buf(port); ++ } ++ } ++err: ++ if (!ret && !ubuf) { ++ printk(KERN_ERR "%s() EAGAIN\n", __func__); ++ ret = -EAGAIN; ++ } ++ ++ return ret; ++} ++ ++static unsigned int fops_poll(struct file *file, poll_table *wait) ++{ ++ struct saa7164_vbi_fh *fh = (struct saa7164_vbi_fh *)file->private_data; ++ struct saa7164_port *port = fh->port; ++ unsigned int mask = 0; ++ ++ port->last_poll_msecs_diff = port->last_poll_msecs; ++ port->last_poll_msecs = jiffies_to_msecs(jiffies); ++ port->last_poll_msecs_diff = port->last_poll_msecs - ++ port->last_poll_msecs_diff; ++ ++ saa7164_histogram_update(&port->poll_interval, ++ port->last_poll_msecs_diff); ++ ++ if (!video_is_registered(port->v4l_device)) ++ return -EIO; ++ ++ if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { ++ if (atomic_inc_return(&port->v4l_reader_count) == 1) { ++ if (saa7164_vbi_initialize(port) < 0) ++ return -EINVAL; ++ saa7164_vbi_start_streaming(port); ++ msleep(200); ++ } ++ } ++ ++ /* blocking wait for buffer */ ++ if ((file->f_flags & O_NONBLOCK) == 0) { ++ if (wait_event_interruptible(port->wait_read, ++ saa7164_vbi_next_buf(port))) { ++ return -ERESTARTSYS; ++ } ++ } ++ ++ /* Pull the first buffer from the used list */ ++ if (!list_empty(&port->list_buf_used.list)) ++ mask |= POLLIN | POLLRDNORM; ++ ++ return mask; ++} ++static const struct v4l2_file_operations vbi_fops = { ++ .owner = THIS_MODULE, ++ .open = fops_open, ++ .release = fops_release, ++ .read = fops_read, ++ .poll = fops_poll, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops vbi_ioctl_ops = { ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_g_tuner = vidioc_g_tuner, ++ .vidioc_s_tuner = vidioc_s_tuner, ++ .vidioc_g_frequency = vidioc_g_frequency, ++ .vidioc_s_frequency = vidioc_s_frequency, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, ++ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, ++ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, ++ .vidioc_queryctrl = vidioc_queryctrl, ++#if 0 ++ .vidioc_g_chip_ident = saa7164_g_chip_ident, ++#endif ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++#if 0 ++ .vidioc_g_register = saa7164_g_register, ++ .vidioc_s_register = saa7164_s_register, ++#endif ++#endif ++ .vidioc_g_fmt_vbi_cap = saa7164_vbi_fmt, ++ .vidioc_try_fmt_vbi_cap = saa7164_vbi_fmt, ++ .vidioc_s_fmt_vbi_cap = saa7164_vbi_fmt, ++}; ++ ++static struct video_device saa7164_vbi_template = { ++ .name = "saa7164", ++ .fops = &vbi_fops, ++ .ioctl_ops = &vbi_ioctl_ops, ++ .minor = -1, ++ .tvnorms = SAA7164_NORMS, ++ .current_norm = V4L2_STD_NTSC_M, ++}; ++ ++static struct video_device *saa7164_vbi_alloc( ++ struct saa7164_port *port, ++ struct pci_dev *pci, ++ struct video_device *template, ++ char *type) ++{ ++ struct video_device *vfd; ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) ++ return NULL; ++ ++ *vfd = *template; ++ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, ++ type, saa7164_boards[dev->board].name); ++ ++ vfd->parent = &pci->dev; ++ vfd->release = video_device_release; ++ return vfd; ++} ++ ++int saa7164_vbi_register(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ int result = -ENODEV; ++ ++ dprintk(DBGLVL_VBI, "%s()\n", __func__); ++ ++ if (port->type != SAA7164_MPEG_VBI) ++ BUG(); ++ ++ /* Sanity check that the PCI configuration space is active */ ++ if (port->hwcfg.BARLocation == 0) { ++ printk(KERN_ERR "%s() failed " ++ "(errno = %d), NO PCI configuration\n", ++ __func__, result); ++ result = -ENOMEM; ++ goto failed; ++ } ++ ++ /* Establish VBI defaults here */ ++ ++ /* Allocate and register the video device node */ ++ port->v4l_device = saa7164_vbi_alloc(port, ++ dev->pci, &saa7164_vbi_template, "vbi"); ++ ++ if (!port->v4l_device) { ++ printk(KERN_INFO "%s: can't allocate vbi device\n", ++ dev->name); ++ result = -ENOMEM; ++ goto failed; ++ } ++ ++ video_set_drvdata(port->v4l_device, port); ++ result = video_register_device(port->v4l_device, ++ VFL_TYPE_VBI, -1); ++ if (result < 0) { ++ printk(KERN_INFO "%s: can't register vbi device\n", ++ dev->name); ++ /* TODO: We're going to leak here if we don't dealloc ++ The buffers above. The unreg function can't deal wit it. ++ */ ++ goto failed; ++ } ++ ++ printk(KERN_INFO "%s: registered device vbi%d [vbi]\n", ++ dev->name, port->v4l_device->num); ++ ++ /* Configure the hardware defaults */ ++ ++ result = 0; ++failed: ++ return result; ++} ++ ++void saa7164_vbi_unregister(struct saa7164_port *port) ++{ ++ struct saa7164_dev *dev = port->dev; ++ ++ dprintk(DBGLVL_VBI, "%s(port=%d)\n", __func__, port->nr); ++ ++ if (port->type != SAA7164_MPEG_VBI) ++ BUG(); ++ ++ if (port->v4l_device) { ++ if (port->v4l_device->minor != -1) ++ video_unregister_device(port->v4l_device); ++ else ++ video_device_release(port->v4l_device); ++ ++ port->v4l_device = NULL; ++ } ++ ++} +diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h +new file mode 100644 +index 0000000..437284e +--- /dev/null ++++ b/drivers/media/pci/saa7164/saa7164.h +@@ -0,0 +1,616 @@ ++/* ++ * Driver for the NXP SAA7164 PCIe bridge ++ * ++ * Copyright (c) 2010 Steven Toth ++ * ++ * 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. ++ */ ++ ++/* ++ Driver architecture ++ ******************* ++ ++ saa7164_core.c/buffer.c/cards.c/i2c.c/dvb.c ++ | : Standard Linux driver framework for creating ++ | : exposing and managing interfaces to the rest ++ | : of the kernel or userland. Also uses _fw.c to load ++ | : firmware direct into the PCIe bus, bypassing layers. ++ V ++ saa7164_api..() : Translate kernel specific functions/features ++ | : into command buffers. ++ V ++ saa7164_cmd..() : Manages the flow of command packets on/off, ++ | : the bus. Deal with bus errors, timeouts etc. ++ V ++ saa7164_bus..() : Manage a read/write memory ring buffer in the ++ | : PCIe Address space. ++ | ++ | saa7164_fw...() : Load any frimware ++ | | : direct into the device ++ V V ++ <- ----------------- PCIe address space -------------------- -> ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "saa7164-reg.h" ++#include "saa7164-types.h" ++ ++#define SAA7164_MAXBOARDS 8 ++ ++#define UNSET (-1U) ++#define SAA7164_BOARD_NOAUTO UNSET ++#define SAA7164_BOARD_UNKNOWN 0 ++#define SAA7164_BOARD_UNKNOWN_REV2 1 ++#define SAA7164_BOARD_UNKNOWN_REV3 2 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2250 3 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2200 4 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2200_2 5 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2200_3 6 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2250_2 7 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2250_3 8 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2200_4 9 ++#define SAA7164_BOARD_HAUPPAUGE_HVR2200_5 10 ++ ++#define SAA7164_MAX_UNITS 8 ++#define SAA7164_TS_NUMBER_OF_LINES 312 ++#define SAA7164_PS_NUMBER_OF_LINES 256 ++#define SAA7164_PT_ENTRIES 16 /* (312 * 188) / 4096 */ ++#define SAA7164_MAX_ENCODER_BUFFERS 64 /* max 5secs of latency at 6Mbps */ ++#define SAA7164_MAX_VBI_BUFFERS 64 ++ ++/* Port related defines */ ++#define SAA7164_PORT_TS1 (0) ++#define SAA7164_PORT_TS2 (SAA7164_PORT_TS1 + 1) ++#define SAA7164_PORT_ENC1 (SAA7164_PORT_TS2 + 1) ++#define SAA7164_PORT_ENC2 (SAA7164_PORT_ENC1 + 1) ++#define SAA7164_PORT_VBI1 (SAA7164_PORT_ENC2 + 1) ++#define SAA7164_PORT_VBI2 (SAA7164_PORT_VBI1 + 1) ++#define SAA7164_MAX_PORTS (SAA7164_PORT_VBI2 + 1) ++ ++#define DBGLVL_FW 4 ++#define DBGLVL_DVB 8 ++#define DBGLVL_I2C 16 ++#define DBGLVL_API 32 ++#define DBGLVL_CMD 64 ++#define DBGLVL_BUS 128 ++#define DBGLVL_IRQ 256 ++#define DBGLVL_BUF 512 ++#define DBGLVL_ENC 1024 ++#define DBGLVL_VBI 2048 ++#define DBGLVL_THR 4096 ++#define DBGLVL_CPU 8192 ++ ++#define SAA7164_NORMS \ ++ (V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443) ++ ++enum port_t { ++ SAA7164_MPEG_UNDEFINED = 0, ++ SAA7164_MPEG_DVB, ++ SAA7164_MPEG_ENCODER, ++ SAA7164_MPEG_VBI, ++}; ++ ++enum saa7164_i2c_bus_nr { ++ SAA7164_I2C_BUS_0 = 0, ++ SAA7164_I2C_BUS_1, ++ SAA7164_I2C_BUS_2, ++}; ++ ++enum saa7164_buffer_flags { ++ SAA7164_BUFFER_UNDEFINED = 0, ++ SAA7164_BUFFER_FREE, ++ SAA7164_BUFFER_BUSY, ++ SAA7164_BUFFER_FULL ++}; ++ ++enum saa7164_unit_type { ++ SAA7164_UNIT_UNDEFINED = 0, ++ SAA7164_UNIT_DIGITAL_DEMODULATOR, ++ SAA7164_UNIT_ANALOG_DEMODULATOR, ++ SAA7164_UNIT_TUNER, ++ SAA7164_UNIT_EEPROM, ++ SAA7164_UNIT_ZILOG_IRBLASTER, ++ SAA7164_UNIT_ENCODER, ++}; ++ ++/* The PCIe bridge doesn't grant direct access to i2c. ++ * Instead, you address i2c devices using a uniqely ++ * allocated 'unitid' value via a messaging API. This ++ * is a problem. The kernel and existing demod/tuner ++ * drivers expect to talk 'i2c', so we have to maintain ++ * a translation layer, and a series of functions to ++ * convert i2c bus + device address into a unit id. ++ */ ++struct saa7164_unit { ++ enum saa7164_unit_type type; ++ u8 id; ++ char *name; ++ enum saa7164_i2c_bus_nr i2c_bus_nr; ++ u8 i2c_bus_addr; ++ u8 i2c_reg_len; ++}; ++ ++struct saa7164_board { ++ char *name; ++ enum port_t porta, portb, portc, ++ portd, porte, portf; ++ enum { ++ SAA7164_CHIP_UNDEFINED = 0, ++ SAA7164_CHIP_REV2, ++ SAA7164_CHIP_REV3, ++ } chiprev; ++ struct saa7164_unit unit[SAA7164_MAX_UNITS]; ++}; ++ ++struct saa7164_subid { ++ u16 subvendor; ++ u16 subdevice; ++ u32 card; ++}; ++ ++struct saa7164_encoder_fh { ++ struct saa7164_port *port; ++ atomic_t v4l_reading; ++}; ++ ++struct saa7164_vbi_fh { ++ struct saa7164_port *port; ++ atomic_t v4l_reading; ++}; ++ ++struct saa7164_histogram_bucket { ++ u32 val; ++ u32 count; ++ u64 update_time; ++}; ++ ++struct saa7164_histogram { ++ char name[32]; ++ struct saa7164_histogram_bucket counter1[64]; ++}; ++ ++struct saa7164_user_buffer { ++ struct list_head list; ++ ++ /* Attributes */ ++ u8 *data; ++ u32 pos; ++ u32 actual_size; ++ ++ u32 crc; ++}; ++ ++struct saa7164_fw_status { ++ ++ /* RISC Core details */ ++ u32 status; ++ u32 mode; ++ u32 spec; ++ u32 inst; ++ u32 cpuload; ++ u32 remainheap; ++ ++ /* Firmware version */ ++ u32 version; ++ u32 major; ++ u32 sub; ++ u32 rel; ++ u32 buildnr; ++}; ++ ++struct saa7164_dvb { ++ struct mutex lock; ++ struct dvb_adapter adapter; ++ struct dvb_frontend *frontend; ++ struct dvb_demux demux; ++ struct dmxdev dmxdev; ++ struct dmx_frontend fe_hw; ++ struct dmx_frontend fe_mem; ++ struct dvb_net net; ++ int feeding; ++}; ++ ++struct saa7164_i2c { ++ struct saa7164_dev *dev; ++ ++ enum saa7164_i2c_bus_nr nr; ++ ++ /* I2C I/O */ ++ struct i2c_adapter i2c_adap; ++ struct i2c_client i2c_client; ++ u32 i2c_rc; ++}; ++ ++struct saa7164_ctrl { ++ struct v4l2_queryctrl v; ++}; ++ ++struct saa7164_tvnorm { ++ char *name; ++ v4l2_std_id id; ++}; ++ ++struct saa7164_encoder_params { ++ struct saa7164_tvnorm encodernorm; ++ u32 height; ++ u32 width; ++ u32 is_50hz; ++ u32 bitrate; /* bps */ ++ u32 bitrate_peak; /* bps */ ++ u32 bitrate_mode; ++ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */ ++ ++ u32 audio_sampling_freq; ++ u32 ctl_mute; ++ u32 ctl_aspect; ++ u32 refdist; ++ u32 gop_size; ++}; ++ ++struct saa7164_vbi_params { ++ struct saa7164_tvnorm encodernorm; ++ u32 height; ++ u32 width; ++ u32 is_50hz; ++ u32 bitrate; /* bps */ ++ u32 bitrate_peak; /* bps */ ++ u32 bitrate_mode; ++ u32 stream_type; /* V4L2_MPEG_STREAM_TYPE_MPEG2_TS */ ++ ++ u32 audio_sampling_freq; ++ u32 ctl_mute; ++ u32 ctl_aspect; ++ u32 refdist; ++ u32 gop_size; ++}; ++ ++struct saa7164_port; ++ ++struct saa7164_buffer { ++ struct list_head list; ++ ++ /* Note of which h/w buffer list index position we occupy */ ++ int idx; ++ ++ struct saa7164_port *port; ++ ++ /* Hardware Specific */ ++ /* PCI Memory allocations */ ++ enum saa7164_buffer_flags flags; /* Free, Busy, Full */ ++ ++ /* A block of page align PCI memory */ ++ u32 pci_size; /* PCI allocation size in bytes */ ++ u64 __iomem *cpu; /* Virtual address */ ++ dma_addr_t dma; /* Physical address */ ++ u32 crc; /* Checksum for the entire buffer data */ ++ ++ /* A page table that splits the block into a number of entries */ ++ u32 pt_size; /* PCI allocation size in bytes */ ++ u64 __iomem *pt_cpu; /* Virtual address */ ++ dma_addr_t pt_dma; /* Physical address */ ++ ++ /* Encoder fops */ ++ u32 pos; ++ u32 actual_size; ++}; ++ ++struct saa7164_port { ++ ++ struct saa7164_dev *dev; ++ enum port_t type; ++ int nr; ++ ++ /* --- Generic port attributes --- */ ++ ++ /* HW stream parameters */ ++ struct tmHWStreamParameters hw_streamingparams; ++ ++ /* DMA configuration values, is seeded during initialization */ ++ struct tmComResDMATermDescrHeader hwcfg; ++ ++ /* hardware specific registers */ ++ u32 bufcounter; ++ u32 pitch; ++ u32 bufsize; ++ u32 bufoffset; ++ u32 bufptr32l; ++ u32 bufptr32h; ++ u64 bufptr64; ++ ++ u32 numpte; /* Number of entries in array, only valid in head */ ++ ++ struct mutex dmaqueue_lock; ++ struct saa7164_buffer dmaqueue; ++ ++ u64 last_irq_msecs, last_svc_msecs; ++ u64 last_irq_msecs_diff, last_svc_msecs_diff; ++ u32 last_svc_wp; ++ u32 last_svc_rp; ++ u64 last_irq_svc_msecs_diff; ++ u64 last_read_msecs, last_read_msecs_diff; ++ u64 last_poll_msecs, last_poll_msecs_diff; ++ ++ struct saa7164_histogram irq_interval; ++ struct saa7164_histogram svc_interval; ++ struct saa7164_histogram irq_svc_interval; ++ struct saa7164_histogram read_interval; ++ struct saa7164_histogram poll_interval; ++ ++ /* --- DVB Transport Specific --- */ ++ struct saa7164_dvb dvb; ++ ++ /* --- Encoder/V4L related attributes --- */ ++ /* Encoder */ ++ /* Defaults established in saa7164-encoder.c */ ++ struct saa7164_tvnorm encodernorm; ++ u32 height; ++ u32 width; ++ u32 freq; ++ u32 ts_packet_size; ++ u32 ts_packet_count; ++ u8 mux_input; ++ u8 encoder_profile; ++ u8 video_format; ++ u8 audio_format; ++ u8 video_resolution; ++ u16 ctl_brightness; ++ u16 ctl_contrast; ++ u16 ctl_hue; ++ u16 ctl_saturation; ++ u16 ctl_sharpness; ++ s8 ctl_volume; ++ ++ struct tmComResAFeatureDescrHeader audfeat; ++ struct tmComResEncoderDescrHeader encunit; ++ struct tmComResProcDescrHeader vidproc; ++ struct tmComResExtDevDescrHeader ifunit; ++ struct tmComResTunerDescrHeader tunerunit; ++ ++ struct work_struct workenc; ++ ++ /* V4L Encoder Video */ ++ struct saa7164_encoder_params encoder_params; ++ struct video_device *v4l_device; ++ atomic_t v4l_reader_count; ++ ++ struct saa7164_buffer list_buf_used; ++ struct saa7164_buffer list_buf_free; ++ wait_queue_head_t wait_read; ++ ++ /* V4L VBI */ ++ struct tmComResVBIFormatDescrHeader vbi_fmt_ntsc; ++ struct saa7164_vbi_params vbi_params; ++ ++ /* Debug */ ++ u32 sync_errors; ++ u32 v_cc_errors; ++ u32 a_cc_errors; ++ u8 last_v_cc; ++ u8 last_a_cc; ++ u32 done_first_interrupt; ++}; ++ ++struct saa7164_dev { ++ struct list_head devlist; ++ atomic_t refcount; ++ ++ /* pci stuff */ ++ struct pci_dev *pci; ++ unsigned char pci_rev, pci_lat; ++ int pci_bus, pci_slot; ++ u32 __iomem *lmmio; ++ u8 __iomem *bmmio; ++ u32 __iomem *lmmio2; ++ u8 __iomem *bmmio2; ++ int pci_irqmask; ++ ++ /* board details */ ++ int nr; ++ int hwrevision; ++ u32 board; ++ char name[16]; ++ ++ /* firmware status */ ++ struct saa7164_fw_status fw_status; ++ u32 firmwareloaded; ++ ++ struct tmComResHWDescr hwdesc; ++ struct tmComResInterfaceDescr intfdesc; ++ struct tmComResBusDescr busdesc; ++ ++ struct tmComResBusInfo bus; ++ ++ /* Interrupt status and ack registers */ ++ u32 int_status; ++ u32 int_ack; ++ ++ struct cmd cmds[SAA_CMD_MAX_MSG_UNITS]; ++ struct mutex lock; ++ ++ /* I2c related */ ++ struct saa7164_i2c i2c_bus[3]; ++ ++ /* Transport related */ ++ struct saa7164_port ports[SAA7164_MAX_PORTS]; ++ ++ /* Deferred command/api interrupts handling */ ++ struct work_struct workcmd; ++ ++ /* A kernel thread to monitor the firmware log, used ++ * only in debug mode. ++ */ ++ struct task_struct *kthread; ++ ++}; ++ ++extern struct list_head saa7164_devlist; ++extern unsigned int waitsecs; ++extern unsigned int encoder_buffers; ++extern unsigned int vbi_buffers; ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-core.c */ ++void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr); ++void saa7164_getfirmwarestatus(struct saa7164_dev *dev); ++u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev); ++void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-fw.c */ ++int saa7164_downloadfirmware(struct saa7164_dev *dev); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-i2c.c */ ++extern int saa7164_i2c_register(struct saa7164_i2c *bus); ++extern int saa7164_i2c_unregister(struct saa7164_i2c *bus); ++extern void saa7164_call_i2c_clients(struct saa7164_i2c *bus, ++ unsigned int cmd, void *arg); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-bus.c */ ++int saa7164_bus_setup(struct saa7164_dev *dev); ++void saa7164_bus_dump(struct saa7164_dev *dev); ++int saa7164_bus_set(struct saa7164_dev *dev, struct tmComResInfo* msg, ++ void *buf); ++int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg, ++ void *buf, int peekonly); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-cmd.c */ ++int saa7164_cmd_send(struct saa7164_dev *dev, ++ u8 id, enum tmComResCmd command, u16 controlselector, ++ u16 size, void *buf); ++void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno); ++int saa7164_irq_dequeue(struct saa7164_dev *dev); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-api.c */ ++int saa7164_api_get_fw_version(struct saa7164_dev *dev, u32 *version); ++int saa7164_api_enum_subdevs(struct saa7164_dev *dev); ++int saa7164_api_i2c_read(struct saa7164_i2c *bus, u8 addr, u32 reglen, u8 *reg, ++ u32 datalen, u8 *data); ++int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, ++ u32 datalen, u8 *data); ++int saa7164_api_dif_write(struct saa7164_i2c *bus, u8 addr, ++ u32 datalen, u8 *data); ++int saa7164_api_read_eeprom(struct saa7164_dev *dev, u8 *buf, int buflen); ++int saa7164_api_set_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); ++int saa7164_api_clear_gpiobit(struct saa7164_dev *dev, u8 unitid, u8 pin); ++int saa7164_api_transition_port(struct saa7164_port *port, u8 mode); ++int saa7164_api_initialize_dif(struct saa7164_port *port); ++int saa7164_api_configure_dif(struct saa7164_port *port, u32 std); ++int saa7164_api_set_encoder(struct saa7164_port *port); ++int saa7164_api_get_encoder(struct saa7164_port *port); ++int saa7164_api_set_aspect_ratio(struct saa7164_port *port); ++int saa7164_api_set_usercontrol(struct saa7164_port *port, u8 ctl); ++int saa7164_api_get_usercontrol(struct saa7164_port *port, u8 ctl); ++int saa7164_api_set_videomux(struct saa7164_port *port); ++int saa7164_api_audio_mute(struct saa7164_port *port, int mute); ++int saa7164_api_set_audio_volume(struct saa7164_port *port, s8 level); ++int saa7164_api_set_audio_std(struct saa7164_port *port); ++int saa7164_api_set_audio_detection(struct saa7164_port *port, int autodetect); ++int saa7164_api_get_videomux(struct saa7164_port *port); ++int saa7164_api_set_vbi_format(struct saa7164_port *port); ++int saa7164_api_set_debug(struct saa7164_dev *dev, u8 level); ++int saa7164_api_collect_debug(struct saa7164_dev *dev); ++int saa7164_api_get_load_info(struct saa7164_dev *dev, ++ struct tmFwInfoStruct *i); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-cards.c */ ++extern struct saa7164_board saa7164_boards[]; ++extern const unsigned int saa7164_bcount; ++ ++extern struct saa7164_subid saa7164_subids[]; ++extern const unsigned int saa7164_idcount; ++ ++extern void saa7164_card_list(struct saa7164_dev *dev); ++extern void saa7164_gpio_setup(struct saa7164_dev *dev); ++extern void saa7164_card_setup(struct saa7164_dev *dev); ++ ++extern int saa7164_i2caddr_to_reglen(struct saa7164_i2c *bus, int addr); ++extern int saa7164_i2caddr_to_unitid(struct saa7164_i2c *bus, int addr); ++extern char *saa7164_unitid_name(struct saa7164_dev *dev, u8 unitid); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-dvb.c */ ++extern int saa7164_dvb_register(struct saa7164_port *port); ++extern int saa7164_dvb_unregister(struct saa7164_port *port); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-buffer.c */ ++extern struct saa7164_buffer *saa7164_buffer_alloc( ++ struct saa7164_port *port, u32 len); ++extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf); ++extern void saa7164_buffer_display(struct saa7164_buffer *buf); ++extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i); ++extern int saa7164_buffer_cfg_port(struct saa7164_port *port); ++extern struct saa7164_user_buffer *saa7164_buffer_alloc_user( ++ struct saa7164_dev *dev, u32 len); ++extern void saa7164_buffer_dealloc_user(struct saa7164_user_buffer *buf); ++extern int saa7164_buffer_zero_offsets(struct saa7164_port *port, int i); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-encoder.c */ ++int saa7164_encoder_register(struct saa7164_port *port); ++void saa7164_encoder_unregister(struct saa7164_port *port); ++ ++/* ----------------------------------------------------------- */ ++/* saa7164-vbi.c */ ++int saa7164_vbi_register(struct saa7164_port *port); ++void saa7164_vbi_unregister(struct saa7164_port *port); ++ ++/* ----------------------------------------------------------- */ ++ ++extern unsigned int crc_checking; ++ ++extern unsigned int saa_debug; ++#define dprintk(level, fmt, arg...)\ ++ do { if (saa_debug & level)\ ++ printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++#define log_warn(fmt, arg...)\ ++ do { \ ++ printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ ++ } while (0) ++ ++#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) ++#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) ++ ++#define saa7164_readb(reg) readl(dev->bmmio + (reg)) ++#define saa7164_writeb(reg, value) writel((value), dev->bmmio + (reg)) ++ +diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig +new file mode 100644 +index 0000000..6749f67 +--- /dev/null ++++ b/drivers/media/pci/sta2x11/Kconfig +@@ -0,0 +1,12 @@ ++config STA2X11_VIP ++ tristate "STA2X11 VIP Video For Linux" ++ depends on STA2X11 ++ select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEOBUF_DMA_CONTIG ++ depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS ++ help ++ Say Y for support for STA2X11 VIP (Video Input Port) capture ++ device. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sta2x11_vip. +diff --git a/drivers/media/pci/sta2x11/Makefile b/drivers/media/pci/sta2x11/Makefile +new file mode 100644 +index 0000000..d6c471d +--- /dev/null ++++ b/drivers/media/pci/sta2x11/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o +diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.c b/drivers/media/pci/sta2x11/sta2x11_vip.c +new file mode 100644 +index 0000000..ed1337a +--- /dev/null ++++ b/drivers/media/pci/sta2x11/sta2x11_vip.c +@@ -0,0 +1,1550 @@ ++/* ++ * This is the driver for the STA2x11 Video Input Port. ++ * ++ * Copyright (C) 2010 WindRiver Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program; if not, write to the Free Software Foundation, Inc., ++ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * The full GNU General Public License is included in this distribution in ++ * the file called "COPYING". ++ * ++ * Author: Andreas Kies ++ * Vlad Lungu ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sta2x11_vip.h" ++ ++#define DRV_NAME "sta2x11_vip" ++#define DRV_VERSION "1.3" ++ ++#ifndef PCI_DEVICE_ID_STMICRO_VIP ++#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D ++#endif ++ ++#define MAX_FRAMES 4 ++ ++/*Register offsets*/ ++#define DVP_CTL 0x00 ++#define DVP_TFO 0x04 ++#define DVP_TFS 0x08 ++#define DVP_BFO 0x0C ++#define DVP_BFS 0x10 ++#define DVP_VTP 0x14 ++#define DVP_VBP 0x18 ++#define DVP_VMP 0x1C ++#define DVP_ITM 0x98 ++#define DVP_ITS 0x9C ++#define DVP_STA 0xA0 ++#define DVP_HLFLN 0xA8 ++#define DVP_RGB 0xC0 ++#define DVP_PKZ 0xF0 ++ ++/*Register fields*/ ++#define DVP_CTL_ENA 0x00000001 ++#define DVP_CTL_RST 0x80000000 ++#define DVP_CTL_DIS (~0x00040001) ++ ++#define DVP_IT_VSB 0x00000008 ++#define DVP_IT_VST 0x00000010 ++#define DVP_IT_FIFO 0x00000020 ++ ++#define DVP_HLFLN_SD 0x00000001 ++ ++#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg)) ++#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg)) ++ ++#define SAVE_COUNT 8 ++#define AUX_COUNT 3 ++#define IRQ_COUNT 1 ++ ++/** ++ * struct sta2x11_vip - All internal data for one instance of device ++ * @v4l2_dev: device registered in v4l layer ++ * @video_dev: properties of our device ++ * @pdev: PCI device ++ * @adapter: contains I2C adapter information ++ * @register_save_area: All relevant register are saved here during suspend ++ * @decoder: contains information about video DAC ++ * @format: pixel format, fixed UYVY ++ * @std: video standard (e.g. PAL/NTSC) ++ * @input: input line for video signal ( 0 or 1 ) ++ * @users: Number of open of device ( max. 1 ) ++ * @disabled: Device is in power down state ++ * @mutex: ensures exclusive opening of device ++ * @slock: for excluse acces of registers ++ * @vb_vidq: queue maintained by videobuf layer ++ * @capture: linked list of capture buffer ++ * @active: struct videobuf_buffer currently beingg filled ++ * @started: device is ready to capture frame ++ * @closing: device will be shut down ++ * @tcount: Number of top frames ++ * @bcount: Number of bottom frames ++ * @overflow: Number of FIFO overflows ++ * @mem_spare: small buffer of unused frame ++ * @dma_spare: dma addres of mem_spare ++ * @iomem: hardware base address ++ * @config: I2C and gpio config from platform ++ * ++ * All non-local data is accessed via this structure. ++ */ ++ ++struct sta2x11_vip { ++ struct v4l2_device v4l2_dev; ++ struct video_device *video_dev; ++ struct pci_dev *pdev; ++ struct i2c_adapter *adapter; ++ unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; ++ struct v4l2_subdev *decoder; ++ struct v4l2_pix_format format; ++ v4l2_std_id std; ++ unsigned int input; ++ int users; ++ int disabled; ++ struct mutex mutex; /* exclusive access during open */ ++ spinlock_t slock; /* spin lock for hardware and queue access */ ++ struct videobuf_queue vb_vidq; ++ struct list_head capture; ++ struct videobuf_buffer *active; ++ int started, closing, tcount, bcount; ++ int overflow; ++ void *mem_spare; ++ dma_addr_t dma_spare; ++ void *iomem; ++ struct vip_config *config; ++}; ++ ++static const unsigned int registers_to_save[AUX_COUNT] = { ++ DVP_HLFLN, DVP_RGB, DVP_PKZ ++}; ++ ++static struct v4l2_pix_format formats_50[] = { ++ { /*PAL interlaced */ ++ .width = 720, ++ .height = 576, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_INTERLACED, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 576, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++ { /*PAL top */ ++ .width = 720, ++ .height = 288, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_TOP, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 288, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++ { /*PAL bottom */ ++ .width = 720, ++ .height = 288, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_BOTTOM, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 288, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++ ++}; ++ ++static struct v4l2_pix_format formats_60[] = { ++ { /*NTSC interlaced */ ++ .width = 720, ++ .height = 480, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_INTERLACED, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 480, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++ { /*NTSC top */ ++ .width = 720, ++ .height = 240, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_TOP, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 240, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++ { /*NTSC bottom */ ++ .width = 720, ++ .height = 240, ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .field = V4L2_FIELD_BOTTOM, ++ .bytesperline = 720 * 2, ++ .sizeimage = 720 * 2 * 240, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M}, ++}; ++ ++/** ++ * buf_setup - Get size and number of video buffer ++ * @vq: queue in videobuf ++ * @count: Number of buffers (1..MAX_FRAMES). ++ * 0 use default value. ++ * @size: size of buffer in bytes ++ * ++ * returns size and number of buffers ++ * a preset value of 0 returns the default number. ++ * return value: 0, always succesfull. ++ */ ++static int buf_setup(struct videobuf_queue *vq, unsigned int *count, ++ unsigned int *size) ++{ ++ struct sta2x11_vip *vip = vq->priv_data; ++ ++ *size = vip->format.width * vip->format.height * 2; ++ if (0 == *count || MAX_FRAMES < *count) ++ *count = MAX_FRAMES; ++ return 0; ++}; ++ ++/** ++ * buf_prepare - prepare buffer for usage ++ * @vq: queue in videobuf layer ++ * @vb: buffer to be prepared ++ * @field: type of video data (interlaced/non-interlaced) ++ * ++ * Allocate or realloc buffer ++ * return value: 0, successful. ++ * ++ * -EINVAL, supplied buffer is too small. ++ * ++ * other, buffer could not be locked. ++ */ ++static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct sta2x11_vip *vip = vq->priv_data; ++ int ret; ++ ++ vb->size = vip->format.width * vip->format.height * 2; ++ if ((0 != vb->baddr) && (vb->bsize < vb->size)) ++ return -EINVAL; ++ vb->width = vip->format.width; ++ vb->height = vip->format.height; ++ vb->field = field; ++ ++ if (VIDEOBUF_NEEDS_INIT == vb->state) { ++ ret = videobuf_iolock(vq, vb, NULL); ++ if (ret) ++ goto fail; ++ } ++ vb->state = VIDEOBUF_PREPARED; ++ return 0; ++fail: ++ videobuf_dma_contig_free(vq, vb); ++ vb->state = VIDEOBUF_NEEDS_INIT; ++ return ret; ++} ++ ++/** ++ * buf_queu - queue buffer for filling ++ * @vq: queue in videobuf layer ++ * @vb: buffer to be queued ++ * ++ * if capturing is already running, the buffer will be queued. Otherwise ++ * capture is started and the buffer is used directly. ++ */ ++static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct sta2x11_vip *vip = vq->priv_data; ++ u32 dma; ++ ++ vb->state = VIDEOBUF_QUEUED; ++ ++ if (vip->active) { ++ list_add_tail(&vb->queue, &vip->capture); ++ return; ++ } ++ ++ vip->started = 1; ++ vip->tcount = 0; ++ vip->bcount = 0; ++ vip->active = vb; ++ vb->state = VIDEOBUF_ACTIVE; ++ ++ dma = videobuf_to_dma_contig(vb); ++ ++ REG_WRITE(vip, DVP_TFO, (0 << 16) | (0)); ++ /* despite of interlace mode, upper and lower frames start at zero */ ++ REG_WRITE(vip, DVP_BFO, (0 << 16) | (0)); ++ ++ switch (vip->format.field) { ++ case V4L2_FIELD_INTERLACED: ++ REG_WRITE(vip, DVP_TFS, ++ ((vip->format.height / 2 - 1) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_VTP, dma); ++ REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); ++ REG_WRITE(vip, DVP_VMP, 4 * vip->format.width); ++ break; ++ case V4L2_FIELD_TOP: ++ REG_WRITE(vip, DVP_TFS, ++ ((vip->format.height - 1) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_BFS, ((0) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_VTP, dma); ++ REG_WRITE(vip, DVP_VBP, dma); ++ REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); ++ break; ++ case V4L2_FIELD_BOTTOM: ++ REG_WRITE(vip, DVP_TFS, ((0) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_BFS, ++ ((vip->format.height) << 16) | ++ (2 * vip->format.width - 1)); ++ REG_WRITE(vip, DVP_VTP, dma); ++ REG_WRITE(vip, DVP_VBP, dma); ++ REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); ++ break; ++ ++ default: ++ pr_warning("VIP: unknown field format\n"); ++ return; ++ } ++ ++ REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA); ++} ++ ++/** ++ * buff_release - release buffer ++ * @vq: queue in videobuf layer ++ * @vb: buffer to be released ++ * ++ * release buffer in videobuf layer ++ */ ++static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ ++ videobuf_dma_contig_free(vq, vb); ++ vb->state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static struct videobuf_queue_ops vip_qops = { ++ .buf_setup = buf_setup, ++ .buf_prepare = buf_prepare, ++ .buf_queue = buf_queue, ++ .buf_release = buf_release, ++}; ++ ++/** ++ * vip_open - open video device ++ * @file: descriptor of device ++ * ++ * open device, make sure it is only opened once. ++ * return value: 0, no error. ++ * ++ * -EBUSY, device is already opened ++ * ++ * -ENOMEM, no memory for auxiliary DMA buffer ++ */ ++static int vip_open(struct file *file) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ mutex_lock(&vip->mutex); ++ vip->users++; ++ ++ if (vip->users > 1) { ++ vip->users--; ++ mutex_unlock(&vip->mutex); ++ return -EBUSY; ++ } ++ ++ file->private_data = dev; ++ vip->overflow = 0; ++ vip->started = 0; ++ vip->closing = 0; ++ vip->active = NULL; ++ ++ INIT_LIST_HEAD(&vip->capture); ++ vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64, ++ &vip->dma_spare, GFP_KERNEL); ++ if (!vip->mem_spare) { ++ vip->users--; ++ mutex_unlock(&vip->mutex); ++ return -ENOMEM; ++ } ++ ++ mutex_unlock(&vip->mutex); ++ videobuf_queue_dma_contig_init_cached(&vip->vb_vidq, ++ &vip_qops, ++ &vip->pdev->dev, ++ &vip->slock, ++ V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ V4L2_FIELD_INTERLACED, ++ sizeof(struct videobuf_buffer), ++ vip, NULL); ++ REG_READ(vip, DVP_ITS); ++ REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD); ++ REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); ++ REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); ++ REG_WRITE(vip, DVP_CTL, 0); ++ REG_READ(vip, DVP_ITS); ++ return 0; ++} ++ ++/** ++ * vip_close - close video device ++ * @file: descriptor of device ++ * ++ * close video device, wait until all pending operations are finished ++ * ( maximum FRAME_MAX buffers pending ) ++ * Turn off interrupts. ++ * ++ * return value: 0, always succesful. ++ */ ++static int vip_close(struct file *file) ++{ ++ struct video_device *dev = video_devdata(file); ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ vip->closing = 1; ++ if (vip->active) ++ videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0); ++ spin_lock_irq(&vip->slock); ++ ++ REG_WRITE(vip, DVP_ITM, 0); ++ REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); ++ REG_WRITE(vip, DVP_CTL, 0); ++ REG_READ(vip, DVP_ITS); ++ ++ vip->started = 0; ++ vip->active = NULL; ++ ++ spin_unlock_irq(&vip->slock); ++ ++ videobuf_stop(&vip->vb_vidq); ++ videobuf_mmap_free(&vip->vb_vidq); ++ ++ dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare); ++ file->private_data = NULL; ++ mutex_lock(&vip->mutex); ++ vip->users--; ++ mutex_unlock(&vip->mutex); ++ return 0; ++} ++ ++/** ++ * vip_read - read from video input ++ * @file: descriptor of device ++ * @data: user buffer ++ * @count: number of bytes to be read ++ * @ppos: position within stream ++ * ++ * read video data from video device. ++ * handling is done in generic videobuf layer ++ * return value: provided by videobuf layer ++ */ ++static ssize_t vip_read(struct file *file, char __user *data, ++ size_t count, loff_t *ppos) ++{ ++ struct video_device *dev = file->private_data; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0, ++ file->f_flags & O_NONBLOCK); ++} ++ ++/** ++ * vip_mmap - map user buffer ++ * @file: descriptor of device ++ * @vma: user buffer ++ * ++ * map user space buffer into kernel mode, including DMA address. ++ * handling is done in generic videobuf layer. ++ * return value: provided by videobuf layer ++ */ ++static int vip_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct video_device *dev = file->private_data; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_mmap_mapper(&vip->vb_vidq, vma); ++} ++ ++/** ++ * vip_poll - poll for event ++ * @file: descriptor of device ++ * @wait: contains events to be waited for ++ * ++ * wait for event related to video device. ++ * handling is done in generic videobuf layer. ++ * return value: provided by videobuf layer ++ */ ++static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct video_device *dev = file->private_data; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_poll_stream(file, &vip->vb_vidq, wait); ++} ++ ++/** ++ * vidioc_querycap - return capabilities of device ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @cap: contains return values ++ * ++ * the capabilities of the device are returned ++ * ++ * return value: 0, no error. ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ memset(cap, 0, sizeof(struct v4l2_capability)); ++ strcpy(cap->driver, DRV_NAME); ++ strcpy(cap->card, DRV_NAME); ++ cap->version = 0; ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", ++ pci_name(vip->pdev)); ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | ++ V4L2_CAP_STREAMING; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_s_std - set video standard ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @std: contains standard to be set ++ * ++ * the video standard is set ++ * ++ * return value: 0, no error. ++ * ++ * -EIO, no input signal detected ++ * ++ * other, returned from video DAC. ++ */ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ v4l2_std_id oldstd = vip->std, newstd; ++ int status; ++ ++ if (V4L2_STD_ALL == *std) { ++ v4l2_subdev_call(vip->decoder, core, s_std, *std); ++ ssleep(2); ++ v4l2_subdev_call(vip->decoder, video, querystd, &newstd); ++ v4l2_subdev_call(vip->decoder, video, g_input_status, &status); ++ if (status & V4L2_IN_ST_NO_SIGNAL) ++ return -EIO; ++ *std = vip->std = newstd; ++ if (oldstd != *std) { ++ if (V4L2_STD_525_60 & (*std)) ++ vip->format = formats_60[0]; ++ else ++ vip->format = formats_50[0]; ++ } ++ return 0; ++ } ++ ++ if (oldstd != *std) { ++ if (V4L2_STD_525_60 & (*std)) ++ vip->format = formats_60[0]; ++ else ++ vip->format = formats_50[0]; ++ } ++ ++ return v4l2_subdev_call(vip->decoder, core, s_std, *std); ++} ++ ++/** ++ * vidioc_g_std - get video standard ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @std: contains return values ++ * ++ * the current video standard is returned ++ * ++ * return value: 0, no error. ++ */ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ *std = vip->std; ++ return 0; ++} ++ ++/** ++ * vidioc_querystd - get possible video standards ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @std: contains return values ++ * ++ * all possible video standards are returned ++ * ++ * return value: delivered by video DAC routine. ++ */ ++static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return v4l2_subdev_call(vip->decoder, video, querystd, std); ++ ++} ++ ++/** ++ * vidioc_queryctl - get possible control settings ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @ctrl: contains return values ++ * ++ * return possible values for a control ++ * return value: delivered by video DAC routine. ++ */ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *ctrl) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl); ++} ++ ++/** ++ * vidioc_g_ctl - get control value ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @ctrl: contains return values ++ * ++ * return setting for a control value ++ * return value: delivered by video DAC routine. ++ */ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl); ++} ++ ++/** ++ * vidioc_s_ctl - set control value ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @ctrl: contains value to be set ++ * ++ * set value for a specific control ++ * return value: delivered by video DAC routine. ++ */ ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl); ++} ++ ++/** ++ * vidioc_enum_input - return name of input line ++ * @file: descriptor of device (not used) ++ * @priv: points to current videodevice ++ * @inp: contains return values ++ * ++ * the user friendly name of the input line is returned ++ * ++ * return value: 0, no error. ++ * ++ * -EINVAL, input line number out of range ++ */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ if (inp->index > 1) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ inp->std = V4L2_STD_ALL; ++ sprintf(inp->name, "Camera %u", inp->index); ++ ++ return 0; ++} ++ ++/** ++ * vidioc_s_input - set input line ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @i: new input line number ++ * ++ * the current active input line is set ++ * ++ * return value: 0, no error. ++ * ++ * -EINVAL, line number out of range ++ */ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ int ret; ++ ++ if (i > 1) ++ return -EINVAL; ++ ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0); ++ ++ if (!ret) ++ vip->input = i; ++ ++ return 0; ++} ++ ++/** ++ * vidioc_g_input - return input line ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @i: returned input line number ++ * ++ * the current active input line is returned ++ * ++ * return value: always 0. ++ */ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ *i = vip->input; ++ return 0; ++} ++ ++/** ++ * vidioc_enum_fmt_vid_cap - return video capture format ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @f: returned format information ++ * ++ * returns name and format of video capture ++ * Only UYVY is supported by hardware. ++ * ++ * return value: always 0. ++ */ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ ++ if (f->index != 0) ++ return -EINVAL; ++ ++ strcpy(f->description, "4:2:2, packed, UYVY"); ++ f->pixelformat = V4L2_PIX_FMT_UYVY; ++ f->flags = 0; ++ return 0; ++} ++ ++/** ++ * vidioc_try_fmt_vid_cap - set video capture format ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @f: new format ++ * ++ * new video format is set which includes width and ++ * field type. width is fixed to 720, no scaling. ++ * Only UYVY is supported by this hardware. ++ * the minimum height is 200, the maximum is 576 (PAL) ++ * ++ * return value: 0, no error ++ * ++ * -EINVAL, pixel or field format not supported ++ * ++ */ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ int interlace_lim; ++ ++ if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) ++ return -EINVAL; ++ ++ if (V4L2_STD_525_60 & vip->std) ++ interlace_lim = 240; ++ else ++ interlace_lim = 288; ++ ++ switch (f->fmt.pix.field) { ++ case V4L2_FIELD_ANY: ++ if (interlace_lim < f->fmt.pix.height) ++ f->fmt.pix.field = V4L2_FIELD_INTERLACED; ++ else ++ f->fmt.pix.field = V4L2_FIELD_BOTTOM; ++ break; ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ if (interlace_lim < f->fmt.pix.height) ++ f->fmt.pix.height = interlace_lim; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ f->fmt.pix.height &= ~1; ++ if (2 * interlace_lim < f->fmt.pix.height) ++ f->fmt.pix.height = 2 * interlace_lim; ++ if (200 > f->fmt.pix.height) ++ f->fmt.pix.height = 200; ++ f->fmt.pix.width = 720; ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.priv = 0; ++ return 0; ++} ++ ++/** ++ * vidioc_s_fmt_vid_cap - set current video format parameters ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @f: returned format information ++ * ++ * set new capture format ++ * return value: 0, no error ++ * ++ * other, delivered by video DAC routine. ++ */ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) ++ return ret; ++ ++ memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format)); ++ return 0; ++} ++ ++/** ++ * vidioc_g_fmt_vid_cap - get current video format parameters ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @f: contains format information ++ * ++ * returns current video format parameters ++ * ++ * return value: 0, always successful ++ */ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format)); ++ return 0; ++} ++ ++/** ++ * vidioc_reqfs - request buffer ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @p: video buffer ++ * ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_reqbufs(&vip->vb_vidq, p); ++} ++ ++/** ++ * vidioc_querybuf - query buffer ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @p: video buffer ++ * ++ * query buffer state. ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_querybuf(&vip->vb_vidq, p); ++} ++ ++/** ++ * vidioc_qbuf - queue a buffer ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @p: video buffer ++ * ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_qbuf(&vip->vb_vidq, p); ++} ++ ++/** ++ * vidioc_dqbuf - dequeue a buffer ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @p: video buffer ++ * ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK); ++} ++ ++/** ++ * vidioc_streamon - turn on streaming ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @type: type of capture ++ * ++ * turn on streaming. ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_streamon(&vip->vb_vidq); ++} ++ ++/** ++ * vidioc_streamoff - turn off streaming ++ * @file: descriptor of device ( not used) ++ * @priv: points to current videodevice ++ * @type: type of capture ++ * ++ * turn off streaming. ++ * Handling is done in generic videobuf layer. ++ */ ++static int vidioc_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct video_device *dev = priv; ++ struct sta2x11_vip *vip = video_get_drvdata(dev); ++ ++ return videobuf_streamoff(&vip->vb_vidq); ++} ++ ++static const struct v4l2_file_operations vip_fops = { ++ .owner = THIS_MODULE, ++ .open = vip_open, ++ .release = vip_close, ++ .ioctl = video_ioctl2, ++ .read = vip_read, ++ .mmap = vip_mmap, ++ .poll = vip_poll ++}; ++ ++static const struct v4l2_ioctl_ops vip_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_s_std = vidioc_s_std, ++ .vidioc_g_std = vidioc_g_std, ++ .vidioc_querystd = vidioc_querystd, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++}; ++ ++static struct video_device video_dev_template = { ++ .name = DRV_NAME, ++ .release = video_device_release, ++ .fops = &vip_fops, ++ .ioctl_ops = &vip_ioctl_ops, ++ .tvnorms = V4L2_STD_ALL, ++}; ++ ++/** ++ * vip_irq - interrupt routine ++ * @irq: Number of interrupt ( not used, correct number is assumed ) ++ * @vip: local data structure containing all information ++ * ++ * check for both frame interrupts set ( top and bottom ). ++ * check FIFO overflow, but limit number of log messages after open. ++ * signal a complete buffer if done. ++ * dequeue a new buffer if available. ++ * disable VIP if no buffer available. ++ * ++ * return value: IRQ_NONE, interrupt was not generated by VIP ++ * ++ * IRQ_HANDLED, interrupt done. ++ */ ++static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) ++{ ++ u32 status, dma; ++ unsigned long flags; ++ struct videobuf_buffer *vb; ++ ++ status = REG_READ(vip, DVP_ITS); ++ ++ if (!status) { ++ pr_debug("VIP: irq ignored\n"); ++ return IRQ_NONE; ++ } ++ ++ if (!vip->started) ++ return IRQ_HANDLED; ++ ++ if (status & DVP_IT_VSB) ++ vip->bcount++; ++ ++ if (status & DVP_IT_VST) ++ vip->tcount++; ++ ++ if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) { ++ /* this is bad, we are too slow, hope the condition is gone ++ * on the next frame */ ++ pr_info("VIP: both irqs\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (status & DVP_IT_FIFO) { ++ if (5 > vip->overflow++) ++ pr_info("VIP: fifo overflow\n"); ++ } ++ ++ if (2 > vip->tcount) ++ return IRQ_HANDLED; ++ ++ if (status & DVP_IT_VSB) ++ return IRQ_HANDLED; ++ ++ spin_lock_irqsave(&vip->slock, flags); ++ ++ REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); ++ if (vip->active) { ++ v4l2_get_timestamp(&vip->active->ts); ++ vip->active->field_count++; ++ vip->active->state = VIDEOBUF_DONE; ++ wake_up(&vip->active->done); ++ vip->active = NULL; ++ } ++ if (!vip->closing) { ++ if (list_empty(&vip->capture)) ++ goto done; ++ ++ vb = list_first_entry(&vip->capture, struct videobuf_buffer, ++ queue); ++ if (NULL == vb) { ++ pr_info("VIP: no buffer\n"); ++ goto done; ++ } ++ vb->state = VIDEOBUF_ACTIVE; ++ list_del(&vb->queue); ++ vip->active = vb; ++ dma = videobuf_to_dma_contig(vb); ++ switch (vip->format.field) { ++ case V4L2_FIELD_INTERLACED: ++ REG_WRITE(vip, DVP_VTP, dma); ++ REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); ++ break; ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ REG_WRITE(vip, DVP_VTP, dma); ++ REG_WRITE(vip, DVP_VBP, dma); ++ break; ++ default: ++ pr_warning("VIP: unknown field format\n"); ++ goto done; ++ break; ++ } ++ REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA); ++ } ++done: ++ spin_unlock_irqrestore(&vip->slock, flags); ++ return IRQ_HANDLED; ++} ++ ++/** ++ * vip_gpio_reserve - reserve gpio pin ++ * @dev: device ++ * @pin: GPIO pin number ++ * @dir: direction, input or output ++ * @name: GPIO pin name ++ * ++ */ ++static int vip_gpio_reserve(struct device *dev, int pin, int dir, ++ const char *name) ++{ ++ int ret; ++ ++ if (pin == -1) ++ return 0; ++ ++ ret = gpio_request(pin, name); ++ if (ret) { ++ dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name); ++ return ret; ++ } ++ ++ ret = gpio_direction_output(pin, dir); ++ if (ret) { ++ dev_err(dev, "Failed to set direction for pin %d (%s)\n", ++ pin, name); ++ gpio_free(pin); ++ return ret; ++ } ++ ++ ret = gpio_export(pin, false); ++ if (ret) { ++ dev_err(dev, "Failed to export pin %d (%s)\n", pin, name); ++ gpio_free(pin); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vip_gpio_release - release gpio pin ++ * @dev: device ++ * @pin: GPIO pin number ++ * @name: GPIO pin name ++ * ++ */ ++static void vip_gpio_release(struct device *dev, int pin, const char *name) ++{ ++ if (pin != -1) { ++ dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); ++ gpio_unexport(pin); ++ gpio_free(pin); ++ } ++} ++ ++/** ++ * sta2x11_vip_init_one - init one instance of video device ++ * @pdev: PCI device ++ * @ent: (not used) ++ * ++ * allocate reset pins for DAC. ++ * Reset video DAC, this is done via reset line. ++ * allocate memory for managing device ++ * request interrupt ++ * map IO region ++ * register device ++ * find and initialize video DAC ++ * ++ * return value: 0, no error ++ * ++ * -ENOMEM, no memory ++ * ++ * -ENODEV, device could not be detected or registered ++ */ ++static int __devinit sta2x11_vip_init_one(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ int ret; ++ struct sta2x11_vip *vip; ++ struct vip_config *config; ++ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ return ret; ++ ++ config = dev_get_platdata(&pdev->dev); ++ if (!config) { ++ dev_info(&pdev->dev, "VIP slot disabled\n"); ++ ret = -EINVAL; ++ goto disable; ++ } ++ ++ ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, ++ config->pwr_name); ++ if (ret) ++ goto disable; ++ ++ if (config->reset_pin >= 0) { ++ ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, ++ config->reset_name); ++ if (ret) { ++ vip_gpio_release(&pdev->dev, config->pwr_pin, ++ config->pwr_name); ++ goto disable; ++ } ++ } ++ ++ if (config->pwr_pin != -1) { ++ /* Datasheet says 5ms between PWR and RST */ ++ usleep_range(5000, 25000); ++ ret = gpio_direction_output(config->pwr_pin, 1); ++ } ++ ++ if (config->reset_pin != -1) { ++ /* Datasheet says 5ms between PWR and RST */ ++ usleep_range(5000, 25000); ++ ret = gpio_direction_output(config->reset_pin, 1); ++ } ++ usleep_range(5000, 25000); ++ ++ vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); ++ if (!vip) { ++ ret = -ENOMEM; ++ goto release_gpios; ++ } ++ ++ vip->pdev = pdev; ++ vip->std = V4L2_STD_PAL; ++ vip->format = formats_50[0]; ++ vip->config = config; ++ ++ if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev)) ++ goto free_mem; ++ ++ dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n", ++ (unsigned long)pci_resource_start(pdev, 0), ++ (unsigned long)pci_resource_len(pdev, 0), pdev->irq); ++ ++ pci_set_master(pdev); ++ ++ ret = pci_request_regions(pdev, DRV_NAME); ++ if (ret) ++ goto unreg; ++ ++ vip->iomem = pci_iomap(pdev, 0, 0x100); ++ if (!vip->iomem) { ++ ret = -ENOMEM; /* FIXME */ ++ goto release; ++ } ++ ++ pci_enable_msi(pdev); ++ ++ INIT_LIST_HEAD(&vip->capture); ++ spin_lock_init(&vip->slock); ++ mutex_init(&vip->mutex); ++ vip->started = 0; ++ vip->disabled = 0; ++ ++ ret = request_irq(pdev->irq, ++ (irq_handler_t) vip_irq, ++ IRQF_SHARED, DRV_NAME, vip); ++ if (ret) { ++ dev_err(&pdev->dev, "request_irq failed\n"); ++ ret = -ENODEV; ++ goto unmap; ++ } ++ ++ vip->video_dev = video_device_alloc(); ++ if (!vip->video_dev) { ++ ret = -ENOMEM; ++ goto release_irq; ++ } ++ ++ *(vip->video_dev) = video_dev_template; ++ video_set_drvdata(vip->video_dev, vip); ++ ++ ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); ++ if (ret) ++ goto vrelease; ++ ++ vip->adapter = i2c_get_adapter(vip->config->i2c_id); ++ if (!vip->adapter) { ++ ret = -ENODEV; ++ dev_err(&pdev->dev, "no I2C adapter found\n"); ++ goto vunreg; ++ } ++ ++ vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter, ++ "adv7180", vip->config->i2c_addr, ++ NULL); ++ if (!vip->decoder) { ++ ret = -ENODEV; ++ dev_err(&pdev->dev, "no decoder found\n"); ++ goto vunreg; ++ } ++ ++ i2c_put_adapter(vip->adapter); ++ ++ v4l2_subdev_call(vip->decoder, core, init, 0); ++ ++ pr_info("STA2X11 Video Input Port (VIP) loaded\n"); ++ return 0; ++ ++vunreg: ++ video_set_drvdata(vip->video_dev, NULL); ++vrelease: ++ if (video_is_registered(vip->video_dev)) ++ video_unregister_device(vip->video_dev); ++ else ++ video_device_release(vip->video_dev); ++release_irq: ++ free_irq(pdev->irq, vip); ++ pci_disable_msi(pdev); ++unmap: ++ pci_iounmap(pdev, vip->iomem); ++ mutex_destroy(&vip->mutex); ++release: ++ pci_release_regions(pdev); ++unreg: ++ v4l2_device_unregister(&vip->v4l2_dev); ++free_mem: ++ kfree(vip); ++release_gpios: ++ vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name); ++ vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name); ++disable: ++ /* ++ * do not call pci_disable_device on sta2x11 because it break all ++ * other Bus masters on this EP ++ */ ++ return ret; ++} ++ ++/** ++ * sta2x11_vip_remove_one - release device ++ * @pdev: PCI device ++ * ++ * Undo everything done in .._init_one ++ * ++ * unregister video device ++ * free interrupt ++ * unmap ioadresses ++ * free memory ++ * free GPIO pins ++ */ ++static void __devexit sta2x11_vip_remove_one(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); ++ struct sta2x11_vip *vip = ++ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); ++ ++ video_set_drvdata(vip->video_dev, NULL); ++ video_unregister_device(vip->video_dev); ++ /*do not call video_device_release() here, is already done */ ++ free_irq(pdev->irq, vip); ++ pci_disable_msi(pdev); ++ pci_iounmap(pdev, vip->iomem); ++ pci_release_regions(pdev); ++ ++ v4l2_device_unregister(&vip->v4l2_dev); ++ mutex_destroy(&vip->mutex); ++ ++ vip_gpio_release(&pdev->dev, vip->config->pwr_pin, ++ vip->config->pwr_name); ++ vip_gpio_release(&pdev->dev, vip->config->reset_pin, ++ vip->config->reset_name); ++ ++ kfree(vip); ++ /* ++ * do not call pci_disable_device on sta2x11 because it break all ++ * other Bus masters on this EP ++ */ ++} ++ ++#ifdef CONFIG_PM ++ ++/** ++ * sta2x11_vip_suspend - set device into power save mode ++ * @pdev: PCI device ++ * @state: new state of device ++ * ++ * all relevant registers are saved and an attempt to set a new state is made. ++ * ++ * return value: 0 always indicate success, ++ * even if device could not be disabled. (workaround for hardware problem) ++ * ++ * reurn value : 0, always succesful, even if hardware does not not support ++ * power down mode. ++ */ ++static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); ++ struct sta2x11_vip *vip = ++ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&vip->slock, flags); ++ vip->register_save_area[0] = REG_READ(vip, DVP_CTL); ++ REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); ++ vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM); ++ REG_WRITE(vip, DVP_ITM, 0); ++ for (i = 1; i < SAVE_COUNT; i++) ++ vip->register_save_area[i] = REG_READ(vip, 4 * i); ++ for (i = 0; i < AUX_COUNT; i++) ++ vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = ++ REG_READ(vip, registers_to_save[i]); ++ spin_unlock_irqrestore(&vip->slock, flags); ++ /* save pci state */ ++ pci_save_state(pdev); ++ if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { ++ /* ++ * do not call pci_disable_device on sta2x11 because it ++ * break all other Bus masters on this EP ++ */ ++ vip->disabled = 1; ++ } ++ ++ pr_info("VIP: suspend\n"); ++ return 0; ++} ++ ++/** ++ * sta2x11_vip_resume - resume device operation ++ * @pdev : PCI device ++ * ++ * re-enable device, set PCI state to powered and restore registers. ++ * resume normal device operation afterwards. ++ * ++ * return value: 0, no error. ++ * ++ * other, could not set device to power on state. ++ */ ++static int sta2x11_vip_resume(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); ++ struct sta2x11_vip *vip = ++ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); ++ unsigned long flags; ++ int ret, i; ++ ++ pr_info("VIP: resume\n"); ++ /* restore pci state */ ++ if (vip->disabled) { ++ ret = pci_enable_device(pdev); ++ if (ret) { ++ pr_warning("VIP: Can't enable device.\n"); ++ return ret; ++ } ++ vip->disabled = 0; ++ } ++ ret = pci_set_power_state(pdev, PCI_D0); ++ if (ret) { ++ /* ++ * do not call pci_disable_device on sta2x11 because it ++ * break all other Bus masters on this EP ++ */ ++ pr_warning("VIP: Can't enable device.\n"); ++ vip->disabled = 1; ++ return ret; ++ } ++ ++ pci_restore_state(pdev); ++ ++ spin_lock_irqsave(&vip->slock, flags); ++ for (i = 1; i < SAVE_COUNT; i++) ++ REG_WRITE(vip, 4 * i, vip->register_save_area[i]); ++ for (i = 0; i < AUX_COUNT; i++) ++ REG_WRITE(vip, registers_to_save[i], ++ vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); ++ REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]); ++ REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); ++ spin_unlock_irqrestore(&vip->slock, flags); ++ return 0; ++} ++ ++#endif ++ ++static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { ++ {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, ++ {0,} ++}; ++ ++static struct pci_driver sta2x11_vip_driver = { ++ .name = DRV_NAME, ++ .probe = sta2x11_vip_init_one, ++ .remove = __devexit_p(sta2x11_vip_remove_one), ++ .id_table = sta2x11_vip_pci_tbl, ++#ifdef CONFIG_PM ++ .suspend = sta2x11_vip_suspend, ++ .resume = sta2x11_vip_resume, ++#endif ++}; ++ ++static int __init sta2x11_vip_init_module(void) ++{ ++ return pci_register_driver(&sta2x11_vip_driver); ++} ++ ++static void __exit sta2x11_vip_exit_module(void) ++{ ++ pci_unregister_driver(&sta2x11_vip_driver); ++} ++ ++#ifdef MODULE ++module_init(sta2x11_vip_init_module); ++module_exit(sta2x11_vip_exit_module); ++#else ++late_initcall_sync(sta2x11_vip_init_module); ++#endif ++ ++MODULE_DESCRIPTION("STA2X11 Video Input Port driver"); ++MODULE_AUTHOR("Wind River"); ++MODULE_LICENSE("GPL v2"); ++MODULE_SUPPORTED_DEVICE("sta2x11 video input"); ++MODULE_VERSION(DRV_VERSION); ++MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl); +diff --git a/drivers/media/pci/sta2x11/sta2x11_vip.h b/drivers/media/pci/sta2x11/sta2x11_vip.h +new file mode 100644 +index 0000000..4f81a13 +--- /dev/null ++++ b/drivers/media/pci/sta2x11/sta2x11_vip.h +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2011 Wind River Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Anders Wallin ++ * ++ */ ++ ++#ifndef __STA2X11_VIP_H ++#define __STA2X11_VIP_H ++ ++/** ++ * struct vip_config - video input configuration data ++ * @pwr_name: ADV powerdown name ++ * @pwr_pin: ADV powerdown pin ++ * @reset_name: ADV reset name ++ * @reset_pin: ADV reset pin ++ */ ++struct vip_config { ++ const char *pwr_name; ++ int pwr_pin; ++ const char *reset_name; ++ int reset_pin; ++ int i2c_id; ++ int i2c_addr; ++}; ++ ++#endif /* __STA2X11_VIP_H */ +diff --git a/drivers/media/pci/ttpci/Kconfig b/drivers/media/pci/ttpci/Kconfig +new file mode 100644 +index 0000000..0dcb8cd +--- /dev/null ++++ b/drivers/media/pci/ttpci/Kconfig +@@ -0,0 +1,154 @@ ++config DVB_AV7110 ++ tristate "AV7110 cards" ++ depends on DVB_CORE && PCI && I2C ++ select TTPCI_EEPROM ++ select VIDEO_SAA7146_VV ++ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV ++ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_SP8870 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for SAA7146 and AV7110 based DVB cards as produced ++ by Fujitsu-Siemens, Technotrend, Hauppauge and others. ++ ++ This driver only supports the fullfeatured cards with ++ onboard MPEG2 decoder. ++ ++ This driver needs an external firmware. Please use the script ++ "/Documentation/dvb/get_dvb_firmware av7110" to ++ download/extract it, and then copy it to /usr/lib/hotplug/firmware ++ or /lib/firmware (depending on configuration of firmware hotplug). ++ ++ Alternatively, you can download the file and use the kernel's ++ EXTRA_FIRMWARE configuration option to build it into your ++ kernel image by adding the filename to the EXTRA_FIRMWARE ++ configuration option string. ++ ++ Say Y if you own such a card and want to use it. ++ ++config DVB_AV7110_OSD ++ bool "AV7110 OSD support" ++ depends on DVB_AV7110 ++ default y if DVB_AV7110=y || DVB_AV7110=m ++ help ++ The AV7110 firmware provides some code to generate an OnScreenDisplay ++ on the video output. This is kind of nonstandard and not guaranteed to ++ be maintained. ++ ++ Anyway, some popular DVB software like VDR uses this OSD to render ++ its menus, so say Y if you want to use this software. ++ ++ All other people say N. ++ ++config DVB_BUDGET_CORE ++ tristate "SAA7146 DVB cards (aka Budget, Nova-PCI)" ++ depends on DVB_CORE && PCI && I2C ++ select VIDEO_SAA7146 ++ select TTPCI_EEPROM ++ help ++ Support for simple SAA7146 based DVB cards ++ (so called Budget- or Nova-PCI cards) without onboard ++ MPEG2 decoder. ++ ++config DVB_BUDGET ++ tristate "Budget cards" ++ depends on DVB_BUDGET_CORE && I2C ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_VES1820 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_L64781 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_S5H1420 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_ISL6423 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV6110x if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for simple SAA7146 based DVB cards (so called Budget- ++ or Nova-PCI cards) without onboard MPEG2 decoder, and without ++ analog inputs or an onboard Common Interface connector. ++ ++ Say Y if you own such a card and want to use it. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called budget. ++ ++config DVB_BUDGET_CI ++ tristate "Budget cards with onboard CI connector" ++ depends on DVB_BUDGET_CORE && I2C ++ select DVB_STV0297 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0288 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB6000 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT ++ select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT ++ depends on RC_CORE ++ help ++ Support for simple SAA7146 based DVB cards ++ (so called Budget- or Nova-PCI cards) without onboard ++ MPEG2 decoder, but with onboard Common Interface connector. ++ ++ Note: The Common Interface is not yet supported by this driver ++ due to lack of information from the vendor. ++ ++ Say Y if you own such a card and want to use it. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called budget-ci. ++ ++config DVB_BUDGET_AV ++ tristate "Budget cards with analog video inputs" ++ depends on DVB_BUDGET_CORE && I2C ++ select VIDEO_SAA7146_VV ++ depends on VIDEO_DEV # dependencies of VIDEO_SAA7146_VV ++ select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10021 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA10023 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_STB0899 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA8261 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TUA6100 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for simple SAA7146 based DVB cards ++ (so called Budget- or Nova-PCI cards) without onboard ++ MPEG2 decoder, but with one or more analog video inputs. ++ ++ Say Y if you own such a card and want to use it. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called budget-av. ++ ++config DVB_BUDGET_PATCH ++ tristate "AV7110 cards with Budget Patch" ++ depends on DVB_BUDGET_CORE && I2C ++ depends on DVB_AV7110 ++ select DVB_STV0299 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_VES1X93 if MEDIA_SUBDRV_AUTOSELECT ++ select DVB_TDA8083 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for Budget Patch (full TS) modification on ++ SAA7146+AV7110 based cards (DVB-S cards). This ++ driver doesn't use onboard MPEG2 decoder. The ++ card is driven in Budget-only mode. Card is ++ required to have loaded firmware to tune properly. ++ Firmware can be loaded by insertion and removal of ++ standard AV7110 driver prior to loading this ++ driver. ++ ++ Say Y if you own such a card and want to use it. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called budget-patch. +diff --git a/drivers/media/pci/ttpci/Makefile b/drivers/media/pci/ttpci/Makefile +new file mode 100644 +index 0000000..9890596 +--- /dev/null ++++ b/drivers/media/pci/ttpci/Makefile +@@ -0,0 +1,21 @@ ++# ++# Makefile for the kernel SAA7146 FULL TS DVB device driver ++# and the AV7110 DVB device driver ++# ++ ++dvb-ttpci-objs := av7110_hw.o av7110_v4l.o av7110_av.o av7110_ca.o av7110.o av7110_ipack.o ++ ++ifdef CONFIG_INPUT_EVDEV ++dvb-ttpci-objs += av7110_ir.o ++endif ++ ++obj-$(CONFIG_TTPCI_EEPROM) += ttpci-eeprom.o ++obj-$(CONFIG_DVB_BUDGET_CORE) += budget-core.o ++obj-$(CONFIG_DVB_BUDGET) += budget.o ++obj-$(CONFIG_DVB_BUDGET_AV) += budget-av.o ++obj-$(CONFIG_DVB_BUDGET_CI) += budget-ci.o ++obj-$(CONFIG_DVB_BUDGET_PATCH) += budget-patch.o ++obj-$(CONFIG_DVB_AV7110) += dvb-ttpci.o ++ ++ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ ++ccflags-y += -Idrivers/media/tuners +diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c +new file mode 100644 +index 0000000..2f54e2b +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110.c +@@ -0,0 +1,2941 @@ ++/* ++ * driver for the SAA7146 based AV110 cards (like the Fujitsu-Siemens DVB) ++ * av7110.c: initialization and demux stuff ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * originally based on code by: ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#include ++ ++#include "dvb_frontend.h" ++ ++#include "ttpci-eeprom.h" ++#include "av7110.h" ++#include "av7110_hw.h" ++#include "av7110_av.h" ++#include "av7110_ca.h" ++#include "av7110_ipack.h" ++ ++#include "bsbe1.h" ++#include "lnbp21.h" ++#include "bsru6.h" ++ ++#define TS_WIDTH 376 ++#define TS_HEIGHT 512 ++#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) ++#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) ++ ++ ++int av7110_debug; ++ ++static int vidmode = CVBS_RGB_OUT; ++static int pids_off; ++static int adac = DVB_ADAC_TI; ++static int hw_sections; ++static int rgb_on; ++static int volume = 255; ++static int budgetpatch; ++static int wss_cfg_4_3 = 0x4008; ++static int wss_cfg_16_9 = 0x0007; ++static int tv_standard; ++static int full_ts; ++ ++module_param_named(debug, av7110_debug, int, 0644); ++MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); ++module_param(vidmode, int, 0444); ++MODULE_PARM_DESC(vidmode,"analog video out: 0 off, 1 CVBS+RGB (default), 2 CVBS+YC, 3 YC"); ++module_param(pids_off, int, 0444); ++MODULE_PARM_DESC(pids_off,"clear video/audio/PCR PID filters when demux is closed"); ++module_param(adac, int, 0444); ++MODULE_PARM_DESC(adac,"audio DAC type: 0 TI, 1 CRYSTAL, 2 MSP (use if autodetection fails)"); ++module_param(hw_sections, int, 0444); ++MODULE_PARM_DESC(hw_sections, "0 use software section filter, 1 use hardware"); ++module_param(rgb_on, int, 0444); ++MODULE_PARM_DESC(rgb_on, "For Siemens DVB-C cards only: Enable RGB control" ++ " signal on SCART pin 16 to switch SCART video mode from CVBS to RGB"); ++module_param(volume, int, 0444); ++MODULE_PARM_DESC(volume, "initial volume: default 255 (range 0-255)"); ++module_param(budgetpatch, int, 0444); ++MODULE_PARM_DESC(budgetpatch, "use budget-patch hardware modification: default 0 (0 no, 1 autodetect, 2 always)"); ++module_param(full_ts, int, 0444); ++MODULE_PARM_DESC(full_ts, "enable code for full-ts hardware modification: 0 disable (default), 1 enable"); ++module_param(wss_cfg_4_3, int, 0444); ++MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); ++module_param(wss_cfg_16_9, int, 0444); ++MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); ++module_param(tv_standard, int, 0444); ++MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++static void restart_feeds(struct av7110 *av7110); ++static int budget_start_feed(struct dvb_demux_feed *feed); ++static int budget_stop_feed(struct dvb_demux_feed *feed); ++ ++static int av7110_num; ++ ++#define FE_FUNC_OVERRIDE(fe_func, av7110_copy, av7110_func) \ ++{\ ++ if (fe_func != NULL) { \ ++ av7110_copy = fe_func; \ ++ fe_func = av7110_func; \ ++ } \ ++} ++ ++ ++static void init_av7110_av(struct av7110 *av7110) ++{ ++ int ret; ++ struct saa7146_dev *dev = av7110->dev; ++ ++ /* set internal volume control to maximum */ ++ av7110->adac_type = DVB_ADAC_TI; ++ ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); ++ if (ret < 0) ++ printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); ++ ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, ++ 1, (u16) av7110->display_ar); ++ if (ret < 0) ++ printk("dvb-ttpci: unable to set aspect ratio\n"); ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, ++ 1, av7110->display_panscan); ++ if (ret < 0) ++ printk("dvb-ttpci: unable to set pan scan\n"); ++ ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); ++ if (ret < 0) ++ printk("dvb-ttpci: unable to configure 4:3 wss\n"); ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9); ++ if (ret < 0) ++ printk("dvb-ttpci: unable to configure 16:9 wss\n"); ++ ++ ret = av7710_set_video_mode(av7110, vidmode); ++ if (ret < 0) ++ printk("dvb-ttpci:cannot set video mode:%d\n",ret); ++ ++ /* handle different card types */ ++ /* remaining inits according to card and frontend type */ ++ av7110->analog_tuner_flags = 0; ++ av7110->current_input = 0; ++ if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) ++ av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on ++ if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) { ++ printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n", ++ av7110->dvb_adapter.num); ++ av7110->adac_type = DVB_ADAC_CRYSTAL; ++ i2c_writereg(av7110, 0x20, 0x01, 0xd2); ++ i2c_writereg(av7110, 0x20, 0x02, 0x49); ++ i2c_writereg(av7110, 0x20, 0x03, 0x00); ++ i2c_writereg(av7110, 0x20, 0x04, 0x00); ++ ++ /** ++ * some special handling for the Siemens DVB-C cards... ++ */ ++ } else if (0 == av7110_init_analog_module(av7110)) { ++ /* done. */ ++ } ++ else if (dev->pci->subsystem_vendor == 0x110a) { ++ printk("dvb-ttpci: DVB-C w/o analog module @ card %d detected\n", ++ av7110->dvb_adapter.num); ++ av7110->adac_type = DVB_ADAC_NONE; ++ } ++ else { ++ av7110->adac_type = adac; ++ printk("dvb-ttpci: adac type set to %d @ card %d\n", ++ av7110->adac_type, av7110->dvb_adapter.num); ++ } ++ ++ if (av7110->adac_type == DVB_ADAC_NONE || av7110->adac_type == DVB_ADAC_MSP34x0) { ++ // switch DVB SCART on ++ ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, MainSwitch, 1, 0); ++ if (ret < 0) ++ printk("dvb-ttpci:cannot switch on SCART(Main):%d\n",ret); ++ ret = av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 1); ++ if (ret < 0) ++ printk("dvb-ttpci:cannot switch on SCART(AD):%d\n",ret); ++ if (rgb_on && ++ ((av7110->dev->pci->subsystem_vendor == 0x110a) || ++ (av7110->dev->pci->subsystem_vendor == 0x13c2)) && ++ (av7110->dev->pci->subsystem_device == 0x0000)) { ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // RGB on, SCART pin 16 ++ //saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // SCARTpin 8 ++ } ++ } ++ ++ if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000e) ++ av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, SpdifSwitch, 1, 0); // SPDIF on ++ ++ ret = av7110_set_volume(av7110, av7110->mixer.volume_left, av7110->mixer.volume_right); ++ if (ret < 0) ++ printk("dvb-ttpci:cannot set volume :%d\n",ret); ++} ++ ++static void recover_arm(struct av7110 *av7110) ++{ ++ dprintk(4, "%p\n",av7110); ++ ++ av7110_bootarm(av7110); ++ msleep(100); ++ ++ init_av7110_av(av7110); ++ ++ /* card-specific recovery */ ++ if (av7110->recover) ++ av7110->recover(av7110); ++ ++ restart_feeds(av7110); ++ ++#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) ++ av7110_check_ir_config(av7110, true); ++#endif ++} ++ ++static void av7110_arm_sync(struct av7110 *av7110) ++{ ++ if (av7110->arm_thread) ++ kthread_stop(av7110->arm_thread); ++ ++ av7110->arm_thread = NULL; ++} ++ ++static int arm_thread(void *data) ++{ ++ struct av7110 *av7110 = data; ++ u16 newloops = 0; ++ int timeout; ++ ++ dprintk(4, "%p\n",av7110); ++ ++ for (;;) { ++ timeout = wait_event_interruptible_timeout(av7110->arm_wait, ++ kthread_should_stop(), 5 * HZ); ++ ++ if (-ERESTARTSYS == timeout || kthread_should_stop()) { ++ /* got signal or told to quit*/ ++ break; ++ } ++ ++ if (!av7110->arm_ready) ++ continue; ++ ++#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) ++ av7110_check_ir_config(av7110, false); ++#endif ++ ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ break; ++ newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); ++ mutex_unlock(&av7110->dcomlock); ++ ++ if (newloops == av7110->arm_loops || av7110->arm_errors > 3) { ++ printk(KERN_ERR "dvb-ttpci: ARM crashed @ card %d\n", ++ av7110->dvb_adapter.num); ++ ++ recover_arm(av7110); ++ ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ break; ++ newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2) - 1; ++ mutex_unlock(&av7110->dcomlock); ++ } ++ av7110->arm_loops = newloops; ++ av7110->arm_errors = 0; ++ } ++ ++ return 0; ++} ++ ++ ++/**************************************************************************** ++ * IRQ handling ++ ****************************************************************************/ ++ ++static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len, ++ u8 *buffer2, size_t buffer2_len, ++ struct dvb_demux_filter *dvbdmxfilter, ++ enum dmx_success success, ++ struct av7110 *av7110) ++{ ++ if (!dvbdmxfilter->feed->demux->dmx.frontend) ++ return 0; ++ if (dvbdmxfilter->feed->demux->dmx.frontend->source == DMX_MEMORY_FE) ++ return 0; ++ ++ switch (dvbdmxfilter->type) { ++ case DMX_TYPE_SEC: ++ if ((((buffer1[1] << 8) | buffer1[2]) & 0xfff) + 3 != buffer1_len) ++ return 0; ++ if (dvbdmxfilter->doneq) { ++ struct dmx_section_filter *filter = &dvbdmxfilter->filter; ++ int i; ++ u8 xor, neq = 0; ++ ++ for (i = 0; i < DVB_DEMUX_MASK_MAX; i++) { ++ xor = filter->filter_value[i] ^ buffer1[i]; ++ neq |= dvbdmxfilter->maskandnotmode[i] & xor; ++ } ++ if (!neq) ++ return 0; ++ } ++ return dvbdmxfilter->feed->cb.sec(buffer1, buffer1_len, ++ buffer2, buffer2_len, ++ &dvbdmxfilter->filter, ++ DMX_OK); ++ case DMX_TYPE_TS: ++ if (!(dvbdmxfilter->feed->ts_type & TS_PACKET)) ++ return 0; ++ if (dvbdmxfilter->feed->ts_type & TS_PAYLOAD_ONLY) ++ return dvbdmxfilter->feed->cb.ts(buffer1, buffer1_len, ++ buffer2, buffer2_len, ++ &dvbdmxfilter->feed->feed.ts, ++ DMX_OK); ++ else ++ av7110_p2t_write(buffer1, buffer1_len, ++ dvbdmxfilter->feed->pid, ++ &av7110->p2t_filter[dvbdmxfilter->index]); ++ default: ++ return 0; ++ } ++} ++ ++ ++//#define DEBUG_TIMING ++static inline void print_time(char *s) ++{ ++#ifdef DEBUG_TIMING ++ struct timeval tv; ++ do_gettimeofday(&tv); ++ printk("%s: %d.%d\n", s, (int)tv.tv_sec, (int)tv.tv_usec); ++#endif ++} ++ ++#define DEBI_READ 0 ++#define DEBI_WRITE 1 ++static inline void start_debi_dma(struct av7110 *av7110, int dir, ++ unsigned long addr, unsigned int len) ++{ ++ dprintk(8, "%c %08lx %u\n", dir == DEBI_READ ? 'R' : 'W', addr, len); ++ if (saa7146_wait_for_debi_done(av7110->dev, 0)) { ++ printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); ++ return; ++ } ++ ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19); /* for good measure */ ++ SAA7146_IER_ENABLE(av7110->dev, MASK_19); ++ if (len < 5) ++ len = 5; /* we want a real DEBI DMA */ ++ if (dir == DEBI_WRITE) ++ iwdebi(av7110, DEBISWAB, addr, 0, (len + 3) & ~3); ++ else ++ irdebi(av7110, DEBISWAB, addr, 0, len); ++} ++ ++static void debiirq(unsigned long cookie) ++{ ++ struct av7110 *av7110 = (struct av7110 *)cookie; ++ int type = av7110->debitype; ++ int handle = (type >> 8) & 0x1f; ++ unsigned int xfer = 0; ++ ++ print_time("debi"); ++ dprintk(4, "type 0x%04x\n", type); ++ ++ if (type == -1) { ++ printk("DEBI irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", ++ jiffies, saa7146_read(av7110->dev, PSR), ++ saa7146_read(av7110->dev, SSR)); ++ goto debi_done; ++ } ++ av7110->debitype = -1; ++ ++ switch (type & 0xff) { ++ ++ case DATA_TS_RECORD: ++ dvb_dmx_swfilter_packets(&av7110->demux, ++ (const u8 *) av7110->debi_virt, ++ av7110->debilen / 188); ++ xfer = RX_BUFF; ++ break; ++ ++ case DATA_PES_RECORD: ++ if (av7110->demux.recording) ++ av7110_record_cb(&av7110->p2t[handle], ++ (u8 *) av7110->debi_virt, ++ av7110->debilen); ++ xfer = RX_BUFF; ++ break; ++ ++ case DATA_IPMPE: ++ case DATA_FSECTION: ++ case DATA_PIPING: ++ if (av7110->handle2filter[handle]) ++ DvbDmxFilterCallback((u8 *)av7110->debi_virt, ++ av7110->debilen, NULL, 0, ++ av7110->handle2filter[handle], ++ DMX_OK, av7110); ++ xfer = RX_BUFF; ++ break; ++ ++ case DATA_CI_GET: ++ { ++ u8 *data = av7110->debi_virt; ++ ++ if ((data[0] < 2) && data[2] == 0xff) { ++ int flags = 0; ++ if (data[5] > 0) ++ flags |= CA_CI_MODULE_PRESENT; ++ if (data[5] > 5) ++ flags |= CA_CI_MODULE_READY; ++ av7110->ci_slot[data[0]].flags = flags; ++ } else ++ ci_get_data(&av7110->ci_rbuffer, ++ av7110->debi_virt, ++ av7110->debilen); ++ xfer = RX_BUFF; ++ break; ++ } ++ ++ case DATA_COMMON_INTERFACE: ++ CI_handle(av7110, (u8 *)av7110->debi_virt, av7110->debilen); ++#if 0 ++ { ++ int i; ++ ++ printk("av7110%d: ", av7110->num); ++ printk("%02x ", *(u8 *)av7110->debi_virt); ++ printk("%02x ", *(1+(u8 *)av7110->debi_virt)); ++ for (i = 2; i < av7110->debilen; i++) ++ printk("%02x ", (*(i+(unsigned char *)av7110->debi_virt))); ++ for (i = 2; i < av7110->debilen; i++) ++ printk("%c", chtrans(*(i+(unsigned char *)av7110->debi_virt))); ++ ++ printk("\n"); ++ } ++#endif ++ xfer = RX_BUFF; ++ break; ++ ++ case DATA_DEBUG_MESSAGE: ++ ((s8*)av7110->debi_virt)[Reserved_SIZE - 1] = 0; ++ printk("%s\n", (s8 *) av7110->debi_virt); ++ xfer = RX_BUFF; ++ break; ++ ++ case DATA_CI_PUT: ++ dprintk(4, "debi DATA_CI_PUT\n"); ++ case DATA_MPEG_PLAY: ++ dprintk(4, "debi DATA_MPEG_PLAY\n"); ++ case DATA_BMP_LOAD: ++ dprintk(4, "debi DATA_BMP_LOAD\n"); ++ xfer = TX_BUFF; ++ break; ++ default: ++ break; ++ } ++debi_done: ++ spin_lock(&av7110->debilock); ++ if (xfer) ++ iwdebi(av7110, DEBINOSWAP, xfer, 0, 2); ++ ARM_ClearMailBox(av7110); ++ spin_unlock(&av7110->debilock); ++} ++ ++/* irq from av7110 firmware writing the mailbox register in the DPRAM */ ++static void gpioirq(unsigned long cookie) ++{ ++ struct av7110 *av7110 = (struct av7110 *)cookie; ++ u32 rxbuf, txbuf; ++ int len; ++ ++ if (av7110->debitype != -1) ++ /* we shouldn't get any irq while a debi xfer is running */ ++ printk("dvb-ttpci: GPIO0 irq oops @ %ld, psr:0x%08x, ssr:0x%08x\n", ++ jiffies, saa7146_read(av7110->dev, PSR), ++ saa7146_read(av7110->dev, SSR)); ++ ++ if (saa7146_wait_for_debi_done(av7110->dev, 0)) { ++ printk(KERN_ERR "%s: saa7146_wait_for_debi_done timed out\n", __func__); ++ BUG(); /* maybe we should try resetting the debi? */ ++ } ++ ++ spin_lock(&av7110->debilock); ++ ARM_ClearIrq(av7110); ++ ++ /* see what the av7110 wants */ ++ av7110->debitype = irdebi(av7110, DEBINOSWAP, IRQ_STATE, 0, 2); ++ av7110->debilen = irdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ rxbuf = irdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ++ txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ len = (av7110->debilen + 3) & ~3; ++ ++ print_time("gpio"); ++ dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen); ++ ++ switch (av7110->debitype & 0xff) { ++ ++ case DATA_TS_PLAY: ++ case DATA_PES_PLAY: ++ break; ++ ++ case DATA_MPEG_VIDEO_EVENT: ++ { ++ u32 h_ar; ++ struct video_event event; ++ ++ av7110->video_size.w = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_WIDTH, 0, 2); ++ h_ar = irdebi(av7110, DEBINOSWAP, STATUS_MPEG_HEIGHT_AR, 0, 2); ++ ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ++ ++ av7110->video_size.h = h_ar & 0xfff; ++ ++ event.type = VIDEO_EVENT_SIZE_CHANGED; ++ event.u.size.w = av7110->video_size.w; ++ event.u.size.h = av7110->video_size.h; ++ switch ((h_ar >> 12) & 0xf) ++ { ++ case 3: ++ av7110->video_size.aspect_ratio = VIDEO_FORMAT_16_9; ++ event.u.size.aspect_ratio = VIDEO_FORMAT_16_9; ++ av7110->videostate.video_format = VIDEO_FORMAT_16_9; ++ break; ++ case 4: ++ av7110->video_size.aspect_ratio = VIDEO_FORMAT_221_1; ++ event.u.size.aspect_ratio = VIDEO_FORMAT_221_1; ++ av7110->videostate.video_format = VIDEO_FORMAT_221_1; ++ break; ++ default: ++ av7110->video_size.aspect_ratio = VIDEO_FORMAT_4_3; ++ event.u.size.aspect_ratio = VIDEO_FORMAT_4_3; ++ av7110->videostate.video_format = VIDEO_FORMAT_4_3; ++ } ++ ++ dprintk(8, "GPIO0 irq: DATA_MPEG_VIDEO_EVENT: w/h/ar = %u/%u/%u\n", ++ av7110->video_size.w, av7110->video_size.h, ++ av7110->video_size.aspect_ratio); ++ ++ dvb_video_add_event(av7110, &event); ++ break; ++ } ++ ++ case DATA_CI_PUT: ++ { ++ int avail; ++ struct dvb_ringbuffer *cibuf = &av7110->ci_wbuffer; ++ ++ avail = dvb_ringbuffer_avail(cibuf); ++ if (avail <= 2) { ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ break; ++ } ++ len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; ++ len |= DVB_RINGBUFFER_PEEK(cibuf, 1); ++ if (avail < len + 2) { ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ break; ++ } ++ DVB_RINGBUFFER_SKIP(cibuf, 2); ++ ++ dvb_ringbuffer_read(cibuf, av7110->debi_virt, len); ++ ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); ++ dprintk(8, "DMA: CI\n"); ++ start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); ++ spin_unlock(&av7110->debilock); ++ wake_up(&cibuf->queue); ++ return; ++ } ++ ++ case DATA_MPEG_PLAY: ++ if (!av7110->playing) { ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ break; ++ } ++ len = 0; ++ if (av7110->debitype & 0x100) { ++ spin_lock(&av7110->aout.lock); ++ len = av7110_pes_play(av7110->debi_virt, &av7110->aout, 2048); ++ spin_unlock(&av7110->aout.lock); ++ } ++ if (len <= 0 && (av7110->debitype & 0x200) ++ &&av7110->videostate.play_state != VIDEO_FREEZED) { ++ spin_lock(&av7110->avout.lock); ++ len = av7110_pes_play(av7110->debi_virt, &av7110->avout, 2048); ++ spin_unlock(&av7110->avout.lock); ++ } ++ if (len <= 0) { ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ break; ++ } ++ dprintk(8, "GPIO0 PES_PLAY len=%04x\n", len); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); ++ dprintk(8, "DMA: MPEG_PLAY\n"); ++ start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE + txbuf, len); ++ spin_unlock(&av7110->debilock); ++ return; ++ ++ case DATA_BMP_LOAD: ++ len = av7110->debilen; ++ dprintk(8, "gpio DATA_BMP_LOAD len %d\n", len); ++ if (!len) { ++ av7110->bmp_state = BMP_LOADED; ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2); ++ wake_up(&av7110->bmpq); ++ dprintk(8, "gpio DATA_BMP_LOAD done\n"); ++ break; ++ } ++ if (len > av7110->bmplen) ++ len = av7110->bmplen; ++ if (len > 2 * 1024) ++ len = 2 * 1024; ++ iwdebi(av7110, DEBINOSWAP, TX_LEN, len, 2); ++ iwdebi(av7110, DEBINOSWAP, IRQ_STATE_EXT, len, 2); ++ memcpy(av7110->debi_virt, av7110->bmpbuf+av7110->bmpp, len); ++ av7110->bmpp += len; ++ av7110->bmplen -= len; ++ dprintk(8, "gpio DATA_BMP_LOAD DMA len %d\n", len); ++ start_debi_dma(av7110, DEBI_WRITE, DPRAM_BASE+txbuf, len); ++ spin_unlock(&av7110->debilock); ++ return; ++ ++ case DATA_CI_GET: ++ case DATA_COMMON_INTERFACE: ++ case DATA_FSECTION: ++ case DATA_IPMPE: ++ case DATA_PIPING: ++ if (!len || len > 4 * 1024) { ++ iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ++ break; ++ } ++ /* fall through */ ++ ++ case DATA_TS_RECORD: ++ case DATA_PES_RECORD: ++ dprintk(8, "DMA: TS_REC etc.\n"); ++ start_debi_dma(av7110, DEBI_READ, DPRAM_BASE+rxbuf, len); ++ spin_unlock(&av7110->debilock); ++ return; ++ ++ case DATA_DEBUG_MESSAGE: ++ if (!len || len > 0xff) { ++ iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ++ break; ++ } ++ start_debi_dma(av7110, DEBI_READ, Reserved, len); ++ spin_unlock(&av7110->debilock); ++ return; ++ ++ case DATA_IRCOMMAND: ++ if (av7110->ir.ir_handler) ++ av7110->ir.ir_handler(av7110, ++ swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); ++ iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); ++ break; ++ ++ default: ++ printk("dvb-ttpci: gpioirq unknown type=%d len=%d\n", ++ av7110->debitype, av7110->debilen); ++ break; ++ } ++ av7110->debitype = -1; ++ ARM_ClearMailBox(av7110); ++ spin_unlock(&av7110->debilock); ++} ++ ++ ++#ifdef CONFIG_DVB_AV7110_OSD ++static int dvb_osd_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (cmd == OSD_SEND_CMD) ++ return av7110_osd_cmd(av7110, (osd_cmd_t *) parg); ++ if (cmd == OSD_GET_CAPABILITY) ++ return av7110_osd_capability(av7110, (osd_cap_t *) parg); ++ ++ return -EINVAL; ++} ++ ++ ++static const struct file_operations dvb_osd_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .open = dvb_generic_open, ++ .release = dvb_generic_release, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_osd = { ++ .priv = NULL, ++ .users = 1, ++ .writers = 1, ++ .fops = &dvb_osd_fops, ++ .kernel_ioctl = dvb_osd_ioctl, ++}; ++#endif /* CONFIG_DVB_AV7110_OSD */ ++ ++ ++static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, ++ u16 subpid, u16 pcrpid) ++{ ++ u16 aflags = 0; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (vpid == 0x1fff || apid == 0x1fff || ++ ttpid == 0x1fff || subpid == 0x1fff || pcrpid == 0x1fff) { ++ vpid = apid = ttpid = subpid = pcrpid = 0; ++ av7110->pids[DMX_PES_VIDEO] = 0; ++ av7110->pids[DMX_PES_AUDIO] = 0; ++ av7110->pids[DMX_PES_TELETEXT] = 0; ++ av7110->pids[DMX_PES_PCR] = 0; ++ } ++ ++ if (av7110->audiostate.bypass_mode) ++ aflags |= 0x8000; ++ ++ return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6, ++ pcrpid, vpid, apid, ttpid, subpid, aflags); ++} ++ ++int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, ++ u16 subpid, u16 pcrpid) ++{ ++ int ret = 0; ++ dprintk(4, "%p\n", av7110); ++ ++ if (mutex_lock_interruptible(&av7110->pid_mutex)) ++ return -ERESTARTSYS; ++ ++ if (!(vpid & 0x8000)) ++ av7110->pids[DMX_PES_VIDEO] = vpid; ++ if (!(apid & 0x8000)) ++ av7110->pids[DMX_PES_AUDIO] = apid; ++ if (!(ttpid & 0x8000)) ++ av7110->pids[DMX_PES_TELETEXT] = ttpid; ++ if (!(pcrpid & 0x8000)) ++ av7110->pids[DMX_PES_PCR] = pcrpid; ++ ++ av7110->pids[DMX_PES_SUBTITLE] = 0; ++ ++ if (av7110->fe_synced) { ++ pcrpid = av7110->pids[DMX_PES_PCR]; ++ ret = SetPIDs(av7110, vpid, apid, ttpid, subpid, pcrpid); ++ } ++ ++ mutex_unlock(&av7110->pid_mutex); ++ return ret; ++} ++ ++ ++/****************************************************************************** ++ * hardware filter functions ++ ******************************************************************************/ ++ ++static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; ++ struct av7110 *av7110 = dvbdmxfeed->demux->priv; ++ u16 buf[20]; ++ int ret, i; ++ u16 handle; ++// u16 mode = 0x0320; ++ u16 mode = 0xb96a; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (av7110->full_ts) ++ return 0; ++ ++ if (dvbdmxfilter->type == DMX_TYPE_SEC) { ++ if (hw_sections) { ++ buf[4] = (dvbdmxfilter->filter.filter_value[0] << 8) | ++ dvbdmxfilter->maskandmode[0]; ++ for (i = 3; i < 18; i++) ++ buf[i + 4 - 2] = ++ (dvbdmxfilter->filter.filter_value[i] << 8) | ++ dvbdmxfilter->maskandmode[i]; ++ mode = 4; ++ } ++ } else if ((dvbdmxfeed->ts_type & TS_PACKET) && ++ !(dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)) { ++ av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed); ++ } ++ ++ buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter; ++ buf[1] = 16; ++ buf[2] = dvbdmxfeed->pid; ++ buf[3] = mode; ++ ++ ret = av7110_fw_request(av7110, buf, 20, &handle, 1); ++ if (ret != 0 || handle >= 32) { ++ printk("dvb-ttpci: %s error buf %04x %04x %04x %04x " ++ "ret %d handle %04x\n", ++ __func__, buf[0], buf[1], buf[2], buf[3], ++ ret, handle); ++ dvbdmxfilter->hw_handle = 0xffff; ++ if (!ret) ++ ret = -1; ++ return ret; ++ } ++ ++ av7110->handle2filter[handle] = dvbdmxfilter; ++ dvbdmxfilter->hw_handle = handle; ++ ++ return ret; ++} ++ ++static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) ++{ ++ struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; ++ u16 buf[3]; ++ u16 answ[2]; ++ int ret; ++ u16 handle; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (av7110->full_ts) ++ return 0; ++ ++ handle = dvbdmxfilter->hw_handle; ++ if (handle >= 32) { ++ printk("%s tried to stop invalid filter %04x, filter type = %x\n", ++ __func__, handle, dvbdmxfilter->type); ++ return -EINVAL; ++ } ++ ++ av7110->handle2filter[handle] = NULL; ++ ++ buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter; ++ buf[1] = 1; ++ buf[2] = handle; ++ ret = av7110_fw_request(av7110, buf, 3, answ, 2); ++ if (ret != 0 || answ[1] != handle) { ++ printk("dvb-ttpci: %s error cmd %04x %04x %04x ret %x " ++ "resp %04x %04x pid %d\n", ++ __func__, buf[0], buf[1], buf[2], ret, ++ answ[0], answ[1], dvbdmxfilter->feed->pid); ++ if (!ret) ++ ret = -1; ++ } ++ return ret; ++} ++ ++ ++static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct av7110 *av7110 = dvbdmx->priv; ++ u16 *pid = dvbdmx->pids, npids[5]; ++ int i; ++ int ret = 0; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; ++ i = dvbdmxfeed->pes_type; ++ npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; ++ if ((i == 2) && npids[i] && (dvbdmxfeed->ts_type & TS_PACKET)) { ++ npids[i] = 0; ++ ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); ++ if (!ret) ++ ret = StartHWFilter(dvbdmxfeed->filter); ++ return ret; ++ } ++ if (dvbdmxfeed->pes_type <= 2 || dvbdmxfeed->pes_type == 4) { ++ ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); ++ if (ret) ++ return ret; ++ } ++ ++ if (dvbdmxfeed->pes_type < 2 && npids[0]) ++ if (av7110->fe_synced) ++ { ++ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); ++ if (ret) ++ return ret; ++ } ++ ++ if ((dvbdmxfeed->ts_type & TS_PACKET) && !av7110->full_ts) { ++ if (dvbdmxfeed->pes_type == 0 && !(dvbdmx->pids[0] & 0x8000)) ++ ret = av7110_av_start_record(av7110, RP_AUDIO, dvbdmxfeed); ++ if (dvbdmxfeed->pes_type == 1 && !(dvbdmx->pids[1] & 0x8000)) ++ ret = av7110_av_start_record(av7110, RP_VIDEO, dvbdmxfeed); ++ } ++ return ret; ++} ++ ++static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) ++{ ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ struct av7110 *av7110 = dvbdmx->priv; ++ u16 *pid = dvbdmx->pids, npids[5]; ++ int i; ++ ++ int ret = 0; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (dvbdmxfeed->pes_type <= 1) { ++ ret = av7110_av_stop(av7110, dvbdmxfeed->pes_type ? RP_VIDEO : RP_AUDIO); ++ if (ret) ++ return ret; ++ if (!av7110->rec_mode) ++ dvbdmx->recording = 0; ++ if (!av7110->playing) ++ dvbdmx->playing = 0; ++ } ++ npids[0] = npids[1] = npids[2] = npids[3] = npids[4] = 0xffff; ++ i = dvbdmxfeed->pes_type; ++ switch (i) { ++ case 2: //teletext ++ if (dvbdmxfeed->ts_type & TS_PACKET) ++ ret = StopHWFilter(dvbdmxfeed->filter); ++ npids[2] = 0; ++ break; ++ case 0: ++ case 1: ++ case 4: ++ if (!pids_off) ++ return 0; ++ npids[i] = (pid[i]&0x8000) ? 0 : pid[i]; ++ break; ++ } ++ if (!ret) ++ ret = ChangePIDs(av7110, npids[1], npids[0], npids[2], npids[3], npids[4]); ++ return ret; ++} ++ ++static int av7110_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct av7110 *av7110 = demux->priv; ++ int ret = 0; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (!demux->dmx.frontend) ++ return -EINVAL; ++ ++ if (!av7110->full_ts && feed->pid > 0x1fff) ++ return -EINVAL; ++ ++ if (feed->type == DMX_TYPE_TS) { ++ if ((feed->ts_type & TS_DECODER) && ++ (feed->pes_type <= DMX_TS_PES_PCR)) { ++ switch (demux->dmx.frontend->source) { ++ case DMX_MEMORY_FE: ++ if (feed->ts_type & TS_DECODER) ++ if (feed->pes_type < 2 && ++ !(demux->pids[0] & 0x8000) && ++ !(demux->pids[1] & 0x8000)) { ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); ++ ret = av7110_av_start_play(av7110,RP_AV); ++ if (!ret) ++ demux->playing = 1; ++ } ++ break; ++ default: ++ ret = dvb_feed_start_pid(feed); ++ break; ++ } ++ } else if ((feed->ts_type & TS_PACKET) && ++ (demux->dmx.frontend->source != DMX_MEMORY_FE)) { ++ ret = StartHWFilter(feed->filter); ++ } ++ } ++ ++ if (av7110->full_ts) { ++ budget_start_feed(feed); ++ return ret; ++ } ++ ++ if (feed->type == DMX_TYPE_SEC) { ++ int i; ++ ++ for (i = 0; i < demux->filternum; i++) { ++ if (demux->filter[i].state != DMX_STATE_READY) ++ continue; ++ if (demux->filter[i].type != DMX_TYPE_SEC) ++ continue; ++ if (demux->filter[i].filter.parent != &feed->feed.sec) ++ continue; ++ demux->filter[i].state = DMX_STATE_GO; ++ if (demux->dmx.frontend->source != DMX_MEMORY_FE) { ++ ret = StartHWFilter(&demux->filter[i]); ++ if (ret) ++ break; ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++ ++static int av7110_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct av7110 *av7110 = demux->priv; ++ int i, rc, ret = 0; ++ dprintk(4, "%p\n", av7110); ++ ++ if (feed->type == DMX_TYPE_TS) { ++ if (feed->ts_type & TS_DECODER) { ++ if (feed->pes_type >= DMX_TS_PES_OTHER || ++ !demux->pesfilter[feed->pes_type]) ++ return -EINVAL; ++ demux->pids[feed->pes_type] |= 0x8000; ++ demux->pesfilter[feed->pes_type] = NULL; ++ } ++ if (feed->ts_type & TS_DECODER && ++ feed->pes_type < DMX_TS_PES_OTHER) { ++ ret = dvb_feed_stop_pid(feed); ++ } else ++ if ((feed->ts_type & TS_PACKET) && ++ (demux->dmx.frontend->source != DMX_MEMORY_FE)) ++ ret = StopHWFilter(feed->filter); ++ } ++ ++ if (av7110->full_ts) { ++ budget_stop_feed(feed); ++ return ret; ++ } ++ ++ if (feed->type == DMX_TYPE_SEC) { ++ for (i = 0; ifilternum; i++) { ++ if (demux->filter[i].state == DMX_STATE_GO && ++ demux->filter[i].filter.parent == &feed->feed.sec) { ++ demux->filter[i].state = DMX_STATE_READY; ++ if (demux->dmx.frontend->source != DMX_MEMORY_FE) { ++ rc = StopHWFilter(&demux->filter[i]); ++ if (!ret) ++ ret = rc; ++ /* keep going, stop as many filters as possible */ ++ } ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++ ++static void restart_feeds(struct av7110 *av7110) ++{ ++ struct dvb_demux *dvbdmx = &av7110->demux; ++ struct dvb_demux_feed *feed; ++ int mode; ++ int feeding; ++ int i, j; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ mode = av7110->playing; ++ av7110->playing = 0; ++ av7110->rec_mode = 0; ++ ++ feeding = av7110->feeding1; /* full_ts mod */ ++ ++ for (i = 0; i < dvbdmx->feednum; i++) { ++ feed = &dvbdmx->feed[i]; ++ if (feed->state == DMX_STATE_GO) { ++ if (feed->type == DMX_TYPE_SEC) { ++ for (j = 0; j < dvbdmx->filternum; j++) { ++ if (dvbdmx->filter[j].type != DMX_TYPE_SEC) ++ continue; ++ if (dvbdmx->filter[j].filter.parent != &feed->feed.sec) ++ continue; ++ if (dvbdmx->filter[j].state == DMX_STATE_GO) ++ dvbdmx->filter[j].state = DMX_STATE_READY; ++ } ++ } ++ av7110_start_feed(feed); ++ } ++ } ++ ++ av7110->feeding1 = feeding; /* full_ts mod */ ++ ++ if (mode) ++ av7110_av_start_play(av7110, mode); ++} ++ ++static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, ++ uint64_t *stc, unsigned int *base) ++{ ++ int ret; ++ u16 fwstc[4]; ++ u16 tag = ((COMTYPE_REQUEST << 8) + ReqSTC); ++ struct dvb_demux *dvbdemux; ++ struct av7110 *av7110; ++ ++ /* pointer casting paranoia... */ ++ BUG_ON(!demux); ++ dvbdemux = demux->priv; ++ BUG_ON(!dvbdemux); ++ av7110 = dvbdemux->priv; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (num != 0) ++ return -EINVAL; ++ ++ ret = av7110_fw_request(av7110, &tag, 0, fwstc, 4); ++ if (ret) { ++ printk(KERN_ERR "%s: av7110_fw_request error\n", __func__); ++ return ret; ++ } ++ dprintk(2, "fwstc = %04hx %04hx %04hx %04hx\n", ++ fwstc[0], fwstc[1], fwstc[2], fwstc[3]); ++ ++ *stc = (((uint64_t) ((fwstc[3] & 0x8000) >> 15)) << 32) | ++ (((uint64_t) fwstc[1]) << 16) | ((uint64_t) fwstc[0]); ++ *base = 1; ++ ++ dprintk(4, "stc = %lu\n", (unsigned long)*stc); ++ ++ return 0; ++} ++ ++ ++/****************************************************************************** ++ * SEC device file operations ++ ******************************************************************************/ ++ ++ ++static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ return Set22K(av7110, 1); ++ ++ case SEC_TONE_OFF: ++ return Set22K(av7110, 0); ++ ++ default: ++ return -EINVAL; ++ } ++} ++ ++static int av7110_diseqc_send_master_cmd(struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ return av7110_diseqc_send(av7110, cmd->msg_len, cmd->msg, -1); ++} ++ ++static int av7110_diseqc_send_burst(struct dvb_frontend* fe, ++ fe_sec_mini_cmd_t minicmd) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ return av7110_diseqc_send(av7110, 0, NULL, minicmd); ++} ++ ++/* simplified code from budget-core.c */ ++static int stop_ts_capture(struct av7110 *budget) ++{ ++ dprintk(2, "budget: %p\n", budget); ++ ++ if (--budget->feeding1) ++ return budget->feeding1; ++ saa7146_write(budget->dev, MC1, MASK_20); /* DMA3 off */ ++ SAA7146_IER_DISABLE(budget->dev, MASK_10); ++ SAA7146_ISR_CLEAR(budget->dev, MASK_10); ++ return 0; ++} ++ ++static int start_ts_capture(struct av7110 *budget) ++{ ++ dprintk(2, "budget: %p\n", budget); ++ ++ if (budget->feeding1) ++ return ++budget->feeding1; ++ memset(budget->grabbing, 0x00, TS_BUFLEN); ++ budget->ttbp = 0; ++ SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ ++ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ ++ saa7146_write(budget->dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ ++ return ++budget->feeding1; ++} ++ ++static int budget_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct av7110 *budget = demux->priv; ++ int status; ++ ++ dprintk(2, "av7110: %p\n", budget); ++ ++ spin_lock(&budget->feedlock1); ++ feed->pusi_seen = 0; /* have a clean section start */ ++ status = start_ts_capture(budget); ++ spin_unlock(&budget->feedlock1); ++ return status; ++} ++ ++static int budget_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct av7110 *budget = demux->priv; ++ int status; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ spin_lock(&budget->feedlock1); ++ status = stop_ts_capture(budget); ++ spin_unlock(&budget->feedlock1); ++ return status; ++} ++ ++static void vpeirq(unsigned long cookie) ++{ ++ struct av7110 *budget = (struct av7110 *)cookie; ++ u8 *mem = (u8 *) (budget->grabbing); ++ u32 olddma = budget->ttbp; ++ u32 newdma = saa7146_read(budget->dev, PCI_VDP3); ++ struct dvb_demux *demux = budget->full_ts ? &budget->demux : &budget->demux1; ++ ++ /* nearest lower position divisible by 188 */ ++ newdma -= newdma % 188; ++ ++ if (newdma >= TS_BUFLEN) ++ return; ++ ++ budget->ttbp = newdma; ++ ++ if (!budget->feeding1 || (newdma == olddma)) ++ return; ++ ++ /* Ensure streamed PCI data is synced to CPU */ ++ pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); ++ ++#if 0 ++ /* track rps1 activity */ ++ printk("vpeirq: %02x Event Counter 1 0x%04x\n", ++ mem[olddma], ++ saa7146_read(budget->dev, EC1R) & 0x3fff); ++#endif ++ ++ if (newdma > olddma) ++ /* no wraparound, dump olddma..newdma */ ++ dvb_dmx_swfilter_packets(demux, mem + olddma, (newdma - olddma) / 188); ++ else { ++ /* wraparound, dump olddma..buflen and 0..newdma */ ++ dvb_dmx_swfilter_packets(demux, mem + olddma, (TS_BUFLEN - olddma) / 188); ++ dvb_dmx_swfilter_packets(demux, mem, newdma / 188); ++ } ++} ++ ++static int av7110_register(struct av7110 *av7110) ++{ ++ int ret, i; ++ struct dvb_demux *dvbdemux = &av7110->demux; ++ struct dvb_demux *dvbdemux1 = &av7110->demux1; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (av7110->registered) ++ return -1; ++ ++ av7110->registered = 1; ++ ++ dvbdemux->priv = (void *) av7110; ++ ++ for (i = 0; i < 32; i++) ++ av7110->handle2filter[i] = NULL; ++ ++ dvbdemux->filternum = (av7110->full_ts) ? 256 : 32; ++ dvbdemux->feednum = (av7110->full_ts) ? 256 : 32; ++ dvbdemux->start_feed = av7110_start_feed; ++ dvbdemux->stop_feed = av7110_stop_feed; ++ dvbdemux->write_to_decoder = av7110_write_to_decoder; ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING); ++ ++ dvb_dmx_init(&av7110->demux); ++ av7110->demux.dmx.get_stc = dvb_get_stc; ++ ++ av7110->dmxdev.filternum = (av7110->full_ts) ? 256 : 32; ++ av7110->dmxdev.demux = &dvbdemux->dmx; ++ av7110->dmxdev.capabilities = 0; ++ ++ dvb_dmxdev_init(&av7110->dmxdev, &av7110->dvb_adapter); ++ ++ av7110->hw_frontend.source = DMX_FRONTEND_0; ++ ++ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->hw_frontend); ++ ++ if (ret < 0) ++ return ret; ++ ++ av7110->mem_frontend.source = DMX_MEMORY_FE; ++ ++ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &av7110->mem_frontend); ++ ++ if (ret < 0) ++ return ret; ++ ++ ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, ++ &av7110->hw_frontend); ++ if (ret < 0) ++ return ret; ++ ++ av7110_av_register(av7110); ++ av7110_ca_register(av7110); ++ ++#ifdef CONFIG_DVB_AV7110_OSD ++ dvb_register_device(&av7110->dvb_adapter, &av7110->osd_dev, ++ &dvbdev_osd, av7110, DVB_DEVICE_OSD); ++#endif ++ ++ dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net, &dvbdemux->dmx); ++ ++ if (budgetpatch) { ++ /* initialize software demux1 without its own frontend ++ * demux1 hardware is connected to frontend0 of demux0 ++ */ ++ dvbdemux1->priv = (void *) av7110; ++ ++ dvbdemux1->filternum = 256; ++ dvbdemux1->feednum = 256; ++ dvbdemux1->start_feed = budget_start_feed; ++ dvbdemux1->stop_feed = budget_stop_feed; ++ dvbdemux1->write_to_decoder = NULL; ++ ++ dvbdemux1->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING); ++ ++ dvb_dmx_init(&av7110->demux1); ++ ++ av7110->dmxdev1.filternum = 256; ++ av7110->dmxdev1.demux = &dvbdemux1->dmx; ++ av7110->dmxdev1.capabilities = 0; ++ ++ dvb_dmxdev_init(&av7110->dmxdev1, &av7110->dvb_adapter); ++ ++ dvb_net_init(&av7110->dvb_adapter, &av7110->dvb_net1, &dvbdemux1->dmx); ++ printk("dvb-ttpci: additional demux1 for budget-patch registered\n"); ++ } ++ return 0; ++} ++ ++ ++static void dvb_unregister(struct av7110 *av7110) ++{ ++ struct dvb_demux *dvbdemux = &av7110->demux; ++ struct dvb_demux *dvbdemux1 = &av7110->demux1; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (!av7110->registered) ++ return; ++ ++ if (budgetpatch) { ++ dvb_net_release(&av7110->dvb_net1); ++ dvbdemux->dmx.close(&dvbdemux1->dmx); ++ dvb_dmxdev_release(&av7110->dmxdev1); ++ dvb_dmx_release(&av7110->demux1); ++ } ++ ++ dvb_net_release(&av7110->dvb_net); ++ ++ dvbdemux->dmx.close(&dvbdemux->dmx); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->hw_frontend); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &av7110->mem_frontend); ++ ++ dvb_dmxdev_release(&av7110->dmxdev); ++ dvb_dmx_release(&av7110->demux); ++ ++ if (av7110->fe != NULL) { ++ dvb_unregister_frontend(av7110->fe); ++ dvb_frontend_detach(av7110->fe); ++ } ++ dvb_unregister_device(av7110->osd_dev); ++ av7110_av_unregister(av7110); ++ av7110_ca_unregister(av7110); ++} ++ ++ ++/**************************************************************************** ++ * I2C client commands ++ ****************************************************************************/ ++ ++int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val) ++{ ++ u8 msg[2] = { reg, val }; ++ struct i2c_msg msgs; ++ ++ msgs.flags = 0; ++ msgs.addr = id / 2; ++ msgs.len = 2; ++ msgs.buf = msg; ++ return i2c_transfer(&av7110->i2c_adap, &msgs, 1); ++} ++ ++u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg) ++{ ++ u8 mm1[] = {0x00}; ++ u8 mm2[] = {0x00}; ++ struct i2c_msg msgs[2]; ++ ++ msgs[0].flags = 0; ++ msgs[1].flags = I2C_M_RD; ++ msgs[0].addr = msgs[1].addr = id / 2; ++ mm1[0] = reg; ++ msgs[0].len = 1; msgs[1].len = 1; ++ msgs[0].buf = mm1; msgs[1].buf = mm2; ++ i2c_transfer(&av7110->i2c_adap, msgs, 2); ++ ++ return mm2[0]; ++} ++ ++/**************************************************************************** ++ * INITIALIZATION ++ ****************************************************************************/ ++ ++ ++static int check_firmware(struct av7110* av7110) ++{ ++ u32 crc = 0, len = 0; ++ unsigned char *ptr; ++ ++ /* check for firmware magic */ ++ ptr = av7110->bin_fw; ++ if (ptr[0] != 'A' || ptr[1] != 'V' || ++ ptr[2] != 'F' || ptr[3] != 'W') { ++ printk("dvb-ttpci: this is not an av7110 firmware\n"); ++ return -EINVAL; ++ } ++ ptr += 4; ++ ++ /* check dpram file */ ++ crc = get_unaligned_be32(ptr); ++ ptr += 4; ++ len = get_unaligned_be32(ptr); ++ ptr += 4; ++ if (len >= 512) { ++ printk("dvb-ttpci: dpram file is way too big.\n"); ++ return -EINVAL; ++ } ++ if (crc != crc32_le(0, ptr, len)) { ++ printk("dvb-ttpci: crc32 of dpram file does not match.\n"); ++ return -EINVAL; ++ } ++ av7110->bin_dpram = ptr; ++ av7110->size_dpram = len; ++ ptr += len; ++ ++ /* check root file */ ++ crc = get_unaligned_be32(ptr); ++ ptr += 4; ++ len = get_unaligned_be32(ptr); ++ ptr += 4; ++ ++ if (len <= 200000 || len >= 300000 || ++ len > ((av7110->bin_fw + av7110->size_fw) - ptr)) { ++ printk("dvb-ttpci: root file has strange size (%d). aborting.\n", len); ++ return -EINVAL; ++ } ++ if( crc != crc32_le(0, ptr, len)) { ++ printk("dvb-ttpci: crc32 of root file does not match.\n"); ++ return -EINVAL; ++ } ++ av7110->bin_root = ptr; ++ av7110->size_root = len; ++ return 0; ++} ++ ++static void put_firmware(struct av7110* av7110) ++{ ++ vfree(av7110->bin_fw); ++} ++ ++static int get_firmware(struct av7110* av7110) ++{ ++ int ret; ++ const struct firmware *fw; ++ ++ /* request the av7110 firmware, this will block until someone uploads it */ ++ ret = request_firmware(&fw, "dvb-ttpci-01.fw", &av7110->dev->pci->dev); ++ if (ret) { ++ if (ret == -ENOENT) { ++ printk(KERN_ERR "dvb-ttpci: could not load firmware," ++ " file not found: dvb-ttpci-01.fw\n"); ++ printk(KERN_ERR "dvb-ttpci: usually this should be in " ++ "/usr/lib/hotplug/firmware or /lib/firmware\n"); ++ printk(KERN_ERR "dvb-ttpci: and can be downloaded from" ++ " http://www.linuxtv.org/download/dvb/firmware/\n"); ++ } else ++ printk(KERN_ERR "dvb-ttpci: cannot request firmware" ++ " (error %i)\n", ret); ++ return -EINVAL; ++ } ++ ++ if (fw->size <= 200000) { ++ printk("dvb-ttpci: this firmware is way too small.\n"); ++ release_firmware(fw); ++ return -EINVAL; ++ } ++ ++ /* check if the firmware is available */ ++ av7110->bin_fw = vmalloc(fw->size); ++ if (NULL == av7110->bin_fw) { ++ dprintk(1, "out of memory\n"); ++ release_firmware(fw); ++ return -ENOMEM; ++ } ++ ++ memcpy(av7110->bin_fw, fw->data, fw->size); ++ av7110->size_fw = fw->size; ++ if ((ret = check_firmware(av7110))) ++ vfree(av7110->bin_fw); ++ ++ release_firmware(fw); ++ return ret; ++} ++ ++static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u8 pwr = 0; ++ u8 buf[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; ++ u32 div = (p->frequency + 479500) / 125; ++ ++ if (p->frequency > 2000000) ++ pwr = 3; ++ else if (p->frequency > 1800000) ++ pwr = 2; ++ else if (p->frequency > 1600000) ++ pwr = 1; ++ else if (p->frequency > 1200000) ++ pwr = 0; ++ else if (p->frequency >= 1100000) ++ pwr = 1; ++ else ++ pwr = 2; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = ((div & 0x18000) >> 10) | 0x95; ++ buf[3] = (pwr << 6) | 0x30; ++ ++ // NOTE: since we're using a prescaler of 2, we set the ++ // divisor frequency to 62.5kHz and divide by 125 above ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct ves1x93_config alps_bsrv2_config = { ++ .demod_address = 0x08, ++ .xin = 90100000UL, ++ .invert_pwm = 0, ++}; ++ ++static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (p->frequency + 35937500 + 31250) / 62500; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x85 | ((div >> 10) & 0x60); ++ data[3] = (p->frequency < 174000000 ? 0x88 : p->frequency < 470000000 ? 0x84 : 0x81); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct ves1820_config alps_tdbe2_config = { ++ .demod_address = 0x09, ++ .xin = 57840000UL, ++ .invert = 1, ++ .selagc = VES1820_SELAGC_SIGNAMPERR, ++}; ++ ++ ++ ++ ++static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = p->frequency / 125; ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x8e; ++ data[3] = 0x00; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct tda8083_config grundig_29504_451_config = { ++ .demod_address = 0x68, ++}; ++ ++ ++ ++static int philips_cd1516_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div; ++ u32 f = p->frequency; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (f + 36125000 + 31250) / 62500; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x8e; ++ data[3] = (f < 174000000 ? 0xa1 : f < 470000000 ? 0x92 : 0x34); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct ves1820_config philips_cd1516_config = { ++ .demod_address = 0x09, ++ .xin = 57840000UL, ++ .invert = 1, ++ .selagc = VES1820_SELAGC_SIGNAMPERR, ++}; ++ ++ ++ ++static int alps_tdlb7_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div, pwr; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x60, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (p->frequency + 36200000) / 166666; ++ ++ if (p->frequency <= 782000000) ++ pwr = 1; ++ else ++ pwr = 2; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x85; ++ data[3] = pwr << 6; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) ++{ ++#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ return request_firmware(fw, name, &av7110->dev->pci->dev); ++#else ++ return -EINVAL; ++#endif ++} ++ ++static struct sp8870_config alps_tdlb7_config = { ++ ++ .demod_address = 0x71, ++ .request_firmware = alps_tdlb7_request_firmware, ++}; ++ ++ ++static u8 nexusca_stv0297_inittab[] = { ++ 0x80, 0x01, ++ 0x80, 0x00, ++ 0x81, 0x01, ++ 0x81, 0x00, ++ 0x00, 0x09, ++ 0x01, 0x69, ++ 0x03, 0x00, ++ 0x04, 0x00, ++ 0x07, 0x00, ++ 0x08, 0x00, ++ 0x20, 0x00, ++ 0x21, 0x40, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x24, 0x40, ++ 0x25, 0x88, ++ 0x30, 0xff, ++ 0x31, 0x00, ++ 0x32, 0xff, ++ 0x33, 0x00, ++ 0x34, 0x50, ++ 0x35, 0x7f, ++ 0x36, 0x00, ++ 0x37, 0x20, ++ 0x38, 0x00, ++ 0x40, 0x1c, ++ 0x41, 0xff, ++ 0x42, 0x29, ++ 0x43, 0x00, ++ 0x44, 0xff, ++ 0x45, 0x00, ++ 0x46, 0x00, ++ 0x49, 0x04, ++ 0x4a, 0x00, ++ 0x4b, 0x7b, ++ 0x52, 0x30, ++ 0x55, 0xae, ++ 0x56, 0x47, ++ 0x57, 0xe1, ++ 0x58, 0x3a, ++ 0x5a, 0x1e, ++ 0x5b, 0x34, ++ 0x60, 0x00, ++ 0x63, 0x00, ++ 0x64, 0x00, ++ 0x65, 0x00, ++ 0x66, 0x00, ++ 0x67, 0x00, ++ 0x68, 0x00, ++ 0x69, 0x00, ++ 0x6a, 0x02, ++ 0x6b, 0x00, ++ 0x70, 0xff, ++ 0x71, 0x00, ++ 0x72, 0x00, ++ 0x73, 0x00, ++ 0x74, 0x0c, ++ 0x80, 0x00, ++ 0x81, 0x00, ++ 0x82, 0x00, ++ 0x83, 0x00, ++ 0x84, 0x04, ++ 0x85, 0x80, ++ 0x86, 0x24, ++ 0x87, 0x78, ++ 0x88, 0x10, ++ 0x89, 0x00, ++ 0x90, 0x01, ++ 0x91, 0x01, ++ 0xa0, 0x04, ++ 0xa1, 0x00, ++ 0xa2, 0x00, ++ 0xb0, 0x91, ++ 0xb1, 0x0b, ++ 0xc0, 0x53, ++ 0xc1, 0x70, ++ 0xc2, 0x12, ++ 0xd0, 0x00, ++ 0xd1, 0x00, ++ 0xd2, 0x00, ++ 0xd3, 0x00, ++ 0xd4, 0x00, ++ 0xd5, 0x00, ++ 0xde, 0x00, ++ 0xdf, 0x00, ++ 0x61, 0x49, ++ 0x62, 0x0b, ++ 0x53, 0x08, ++ 0x59, 0x08, ++ 0xff, 0xff, ++}; ++ ++static int nexusca_stv0297_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x63, .flags = 0, .buf = data, .len = sizeof(data) }; ++ struct i2c_msg readmsg = { .addr = 0x63, .flags = I2C_M_RD, .buf = data, .len = 1 }; ++ int i; ++ ++ div = (p->frequency + 36150000 + 31250) / 62500; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0xce; ++ ++ if (p->frequency < 45000000) ++ return -EINVAL; ++ else if (p->frequency < 137000000) ++ data[3] = 0x01; ++ else if (p->frequency < 403000000) ++ data[3] = 0x02; ++ else if (p->frequency < 860000000) ++ data[3] = 0x04; ++ else ++ return -EINVAL; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &msg, 1) != 1) { ++ printk("nexusca: pll transfer failed!\n"); ++ return -EIO; ++ } ++ ++ // wait for PLL lock ++ for(i = 0; i < 20; i++) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&av7110->i2c_adap, &readmsg, 1) == 1) ++ if (data[0] & 0x40) break; ++ msleep(10); ++ } ++ ++ return 0; ++} ++ ++static struct stv0297_config nexusca_stv0297_config = { ++ ++ .demod_address = 0x1C, ++ .inittab = nexusca_stv0297_inittab, ++ .invert = 1, ++ .stop_during_read = 1, ++}; ++ ++ ++ ++static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct av7110* av7110 = fe->dvb->priv; ++ u32 div; ++ u8 cfg, cpump, band_select; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (36125000 + p->frequency) / 166666; ++ ++ cfg = 0x88; ++ ++ if (p->frequency < 175000000) ++ cpump = 2; ++ else if (p->frequency < 390000000) ++ cpump = 1; ++ else if (p->frequency < 470000000) ++ cpump = 2; ++ else if (p->frequency < 750000000) ++ cpump = 1; ++ else ++ cpump = 3; ++ ++ if (p->frequency < 175000000) ++ band_select = 0x0e; ++ else if (p->frequency < 470000000) ++ band_select = 0x05; ++ else ++ band_select = 0x03; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = ((div >> 10) & 0x60) | cfg; ++ data[3] = (cpump << 6) | band_select; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&av7110->i2c_adap, &msg, 1) != 1) return -EIO; ++ return 0; ++} ++ ++static struct l64781_config grundig_29504_401_config = { ++ .demod_address = 0x55, ++}; ++ ++ ++ ++static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) ++{ ++ int ret = 0; ++ int synced = (status & FE_HAS_LOCK) ? 1 : 0; ++ ++ av7110->fe_status = status; ++ ++ if (av7110->fe_synced == synced) ++ return 0; ++ ++ if (av7110->playing) { ++ av7110->fe_synced = synced; ++ return 0; ++ } ++ ++ if (mutex_lock_interruptible(&av7110->pid_mutex)) ++ return -ERESTARTSYS; ++ ++ if (synced) { ++ ret = SetPIDs(av7110, av7110->pids[DMX_PES_VIDEO], ++ av7110->pids[DMX_PES_AUDIO], ++ av7110->pids[DMX_PES_TELETEXT], 0, ++ av7110->pids[DMX_PES_PCR]); ++ if (!ret) ++ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); ++ } else { ++ ret = SetPIDs(av7110, 0, 0, 0, 0, 0); ++ if (!ret) { ++ ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0); ++ if (!ret) ++ ret = av7110_wait_msgstate(av7110, GPMQBusy); ++ } ++ } ++ ++ if (!ret) ++ av7110->fe_synced = synced; ++ ++ mutex_unlock(&av7110->pid_mutex); ++ return ret; ++} ++ ++static int av7110_fe_set_frontend(struct dvb_frontend *fe) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) ++ ret = av7110->fe_set_frontend(fe); ++ ++ return ret; ++} ++ ++static int av7110_fe_init(struct dvb_frontend* fe) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) ++ ret = av7110->fe_init(fe); ++ return ret; ++} ++ ++static int av7110_fe_read_status(struct dvb_frontend* fe, fe_status_t* status) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ /* call the real implementation */ ++ int ret = av7110->fe_read_status(fe, status); ++ if (!ret) ++ if (((*status ^ av7110->fe_status) & FE_HAS_LOCK) && (*status & FE_HAS_LOCK)) ++ ret = av7110_fe_lock_fix(av7110, *status); ++ return ret; ++} ++ ++static int av7110_fe_diseqc_reset_overload(struct dvb_frontend* fe) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) ++ ret = av7110->fe_diseqc_reset_overload(fe); ++ return ret; ++} ++ ++static int av7110_fe_diseqc_send_master_cmd(struct dvb_frontend* fe, ++ struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) { ++ av7110->saved_master_cmd = *cmd; ++ ret = av7110->fe_diseqc_send_master_cmd(fe, cmd); ++ } ++ return ret; ++} ++ ++static int av7110_fe_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) { ++ av7110->saved_minicmd = minicmd; ++ ret = av7110->fe_diseqc_send_burst(fe, minicmd); ++ } ++ return ret; ++} ++ ++static int av7110_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) { ++ av7110->saved_tone = tone; ++ ret = av7110->fe_set_tone(fe, tone); ++ } ++ return ret; ++} ++ ++static int av7110_fe_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) { ++ av7110->saved_voltage = voltage; ++ ret = av7110->fe_set_voltage(fe, voltage); ++ } ++ return ret; ++} ++ ++static int av7110_fe_dishnetwork_send_legacy_command(struct dvb_frontend* fe, unsigned long cmd) ++{ ++ struct av7110* av7110 = fe->dvb->priv; ++ ++ int ret = av7110_fe_lock_fix(av7110, 0); ++ if (!ret) ++ ret = av7110->fe_dishnetwork_send_legacy_command(fe, cmd); ++ return ret; ++} ++ ++static void dvb_s_recover(struct av7110* av7110) ++{ ++ av7110_fe_init(av7110->fe); ++ ++ av7110_fe_set_voltage(av7110->fe, av7110->saved_voltage); ++ if (av7110->saved_master_cmd.msg_len) { ++ msleep(20); ++ av7110_fe_diseqc_send_master_cmd(av7110->fe, &av7110->saved_master_cmd); ++ } ++ msleep(20); ++ av7110_fe_diseqc_send_burst(av7110->fe, av7110->saved_minicmd); ++ msleep(20); ++ av7110_fe_set_tone(av7110->fe, av7110->saved_tone); ++ ++ av7110_fe_set_frontend(av7110->fe); ++} ++ ++static u8 read_pwm(struct av7110* av7110) ++{ ++ u8 b = 0xff; ++ u8 pwm; ++ struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, ++ { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; ++ ++ if ((i2c_transfer(&av7110->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) ++ pwm = 0x48; ++ ++ return pwm; ++} ++ ++static int frontend_init(struct av7110 *av7110) ++{ ++ int ret; ++ ++ if (av7110->dev->pci->subsystem_vendor == 0x110a) { ++ switch(av7110->dev->pci->subsystem_device) { ++ case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??)) ++ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, ++ &av7110->i2c_adap, read_pwm(av7110)); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; ++ } ++ break; ++ } ++ ++ } else if (av7110->dev->pci->subsystem_vendor == 0x13c2) { ++ switch(av7110->dev->pci->subsystem_device) { ++ case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X ++ case 0x0003: // Hauppauge/TT WinTV Nexus-S Rev 2.X ++ case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE ++ ++ // try the ALPS BSRV2 first of all ++ av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; ++ av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; ++ av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; ++ av7110->fe->ops.set_tone = av7110_set_tone; ++ av7110->recover = dvb_s_recover; ++ break; ++ } ++ ++ // try the ALPS BSRU6 now ++ av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; ++ av7110->fe->tuner_priv = &av7110->i2c_adap; ++ ++ av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; ++ av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; ++ av7110->fe->ops.set_tone = av7110_set_tone; ++ av7110->recover = dvb_s_recover; ++ break; ++ } ++ ++ // Try the grundig 29504-451 ++ av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; ++ av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; ++ av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; ++ av7110->fe->ops.set_tone = av7110_set_tone; ++ av7110->recover = dvb_s_recover; ++ break; ++ } ++ ++ /* Try DVB-C cards */ ++ switch(av7110->dev->pci->subsystem_device) { ++ case 0x0000: ++ /* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */ ++ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap, ++ read_pwm(av7110)); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params; ++ } ++ break; ++ case 0x0003: ++ /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ ++ av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, ++ read_pwm(av7110)); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; ++ } ++ break; ++ } ++ break; ++ ++ case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X ++ // try ALPS TDLB7 first, then Grundig 29504-401 ++ av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params; ++ break; ++ } ++ /* fall-thru */ ++ ++ case 0x0008: // Hauppauge/TT DVB-T ++ // Grundig 29504-401 ++ av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap); ++ if (av7110->fe) ++ av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; ++ break; ++ ++ case 0x0002: // Hauppauge/TT DVB-C premium rev2.X ++ ++ av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; ++ } ++ break; ++ ++ case 0x0004: // Galaxis DVB-S rev1.3 ++ /* ALPS BSRV2 */ ++ av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; ++ av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; ++ av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; ++ av7110->fe->ops.set_tone = av7110_set_tone; ++ av7110->recover = dvb_s_recover; ++ } ++ break; ++ ++ case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */ ++ /* Grundig 29504-451 */ ++ av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; ++ av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd; ++ av7110->fe->ops.diseqc_send_burst = av7110_diseqc_send_burst; ++ av7110->fe->ops.set_tone = av7110_set_tone; ++ av7110->recover = dvb_s_recover; ++ } ++ break; ++ ++ case 0x000A: // Hauppauge/TT Nexus-CA rev1.X ++ ++ av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params; ++ ++ /* set TDA9819 into DVB mode */ ++ saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) ++ saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) ++ ++ /* tuner on this needs a slower i2c bus speed */ ++ av7110->dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; ++ break; ++ } ++ break; ++ ++ case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */ ++ /* ALPS BSBE1 */ ++ av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap); ++ if (av7110->fe) { ++ av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; ++ av7110->fe->tuner_priv = &av7110->i2c_adap; ++ ++ if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) { ++ printk("dvb-ttpci: LNBP21 not found!\n"); ++ if (av7110->fe->ops.release) ++ av7110->fe->ops.release(av7110->fe); ++ av7110->fe = NULL; ++ } else { ++ av7110->fe->ops.dishnetwork_send_legacy_command = NULL; ++ av7110->recover = dvb_s_recover; ++ } ++ } ++ break; ++ } ++ } ++ ++ if (!av7110->fe) { ++ /* FIXME: propagate the failure code from the lower layers */ ++ ret = -ENOMEM; ++ printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ av7110->dev->pci->vendor, ++ av7110->dev->pci->device, ++ av7110->dev->pci->subsystem_vendor, ++ av7110->dev->pci->subsystem_device); ++ } else { ++ FE_FUNC_OVERRIDE(av7110->fe->ops.init, av7110->fe_init, av7110_fe_init); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.read_status, av7110->fe_read_status, av7110_fe_read_status); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_reset_overload, av7110->fe_diseqc_reset_overload, av7110_fe_diseqc_reset_overload); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_master_cmd, av7110->fe_diseqc_send_master_cmd, av7110_fe_diseqc_send_master_cmd); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.diseqc_send_burst, av7110->fe_diseqc_send_burst, av7110_fe_diseqc_send_burst); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.set_tone, av7110->fe_set_tone, av7110_fe_set_tone); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.set_voltage, av7110->fe_set_voltage, av7110_fe_set_voltage); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.dishnetwork_send_legacy_command, av7110->fe_dishnetwork_send_legacy_command, av7110_fe_dishnetwork_send_legacy_command); ++ FE_FUNC_OVERRIDE(av7110->fe->ops.set_frontend, av7110->fe_set_frontend, av7110_fe_set_frontend); ++ ++ ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe); ++ if (ret < 0) { ++ printk("av7110: Frontend registration failed!\n"); ++ dvb_frontend_detach(av7110->fe); ++ av7110->fe = NULL; ++ } ++ } ++ return ret; ++} ++ ++/* Budgetpatch note: ++ * Original hardware design by Roberto Deza: ++ * There is a DVB_Wiki at ++ * http://www.linuxtv.org/ ++ * ++ * New software triggering design by Emard that works on ++ * original Roberto Deza's hardware: ++ * ++ * rps1 code for budgetpatch will copy internal HS event to GPIO3 pin. ++ * GPIO3 is in budget-patch hardware connectd to port B VSYNC ++ * HS is an internal event of 7146, accessible with RPS ++ * and temporarily raised high every n lines ++ * (n in defined in the RPS_THRESH1 counter threshold) ++ * I think HS is raised high on the beginning of the n-th line ++ * and remains high until this n-th line that triggered ++ * it is completely received. When the receiption of n-th line ++ * ends, HS is lowered. ++ * ++ * To transmit data over DMA, 7146 needs changing state at ++ * port B VSYNC pin. Any changing of port B VSYNC will ++ * cause some DMA data transfer, with more or less packets loss. ++ * It depends on the phase and frequency of VSYNC and ++ * the way of 7146 is instructed to trigger on port B (defined ++ * in DD1_INIT register, 3rd nibble from the right valid ++ * numbers are 0-7, see datasheet) ++ * ++ * The correct triggering can minimize packet loss, ++ * dvbtraffic should give this stable bandwidths: ++ * 22k transponder = 33814 kbit/s ++ * 27.5k transponder = 38045 kbit/s ++ * by experiment it is found that the best results ++ * (stable bandwidths and almost no packet loss) ++ * are obtained using DD1_INIT triggering number 2 ++ * (Va at rising edge of VS Fa = HS x VS-failing forced toggle) ++ * and a VSYNC phase that occurs in the middle of DMA transfer ++ * (about byte 188*512=96256 in the DMA window). ++ * ++ * Phase of HS is still not clear to me how to control, ++ * It just happens to be so. It can be seen if one enables ++ * RPS_IRQ and print Event Counter 1 in vpeirq(). Every ++ * time RPS_INTERRUPT is called, the Event Counter 1 will ++ * increment. That's how the 7146 is programmed to do event ++ * counting in this budget-patch.c ++ * I *think* HPS setting has something to do with the phase ++ * of HS but I can't be 100% sure in that. ++ * ++ * hardware debug note: a working budget card (including budget patch) ++ * with vpeirq() interrupt setup in mode "0x90" (every 64K) will ++ * generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes ++ * and that means 3*25=75 Hz of interrupt freqency, as seen by ++ * watch cat /proc/interrupts ++ * ++ * If this frequency is 3x lower (and data received in the DMA ++ * buffer don't start with 0x47, but in the middle of packets, ++ * whose lengths appear to be like 188 292 188 104 etc. ++ * this means VSYNC line is not connected in the hardware. ++ * (check soldering pcb and pins) ++ * The same behaviour of missing VSYNC can be duplicated on budget ++ * cards, by seting DD1_INIT trigger mode 7 in 3rd nibble. ++ */ ++static int __devinit av7110_attach(struct saa7146_dev* dev, ++ struct saa7146_pci_extension_data *pci_ext) ++{ ++ const int length = TS_WIDTH * TS_HEIGHT; ++ struct pci_dev *pdev = dev->pci; ++ struct av7110 *av7110; ++ struct task_struct *thread; ++ int ret, count = 0; ++ ++ dprintk(4, "dev: %p\n", dev); ++ ++ /* Set RPS_IRQ to 1 to track rps1 activity. ++ * Enabling this won't send any interrupt to PC CPU. ++ */ ++#define RPS_IRQ 0 ++ ++ if (budgetpatch == 1) { ++ budgetpatch = 0; ++ /* autodetect the presence of budget patch ++ * this only works if saa7146 has been recently ++ * reset with with MASK_31 to MC1 ++ * ++ * will wait for VBI_B event (vertical blank at port B) ++ * and will reset GPIO3 after VBI_B is detected. ++ * (GPIO3 should be raised high by CPU to ++ * test if GPIO3 will generate vertical blank signal ++ * in budget patch GPIO3 is connected to VSYNC_B ++ */ ++ ++ /* RESET SAA7146 */ ++ saa7146_write(dev, MC1, MASK_31); ++ /* autodetection success seems to be time-dependend after reset */ ++ ++ /* Fix VSYNC level */ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ /* set vsync_b triggering */ ++ saa7146_write(dev, DD1_STREAM_B, 0); ++ /* port B VSYNC at rising edge */ ++ saa7146_write(dev, DD1_INIT, 0x00000200); ++ saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI ++ saa7146_write(dev, MC2, ++ 1 * (MASK_08 | MASK_24) | // BRS control ++ 0 * (MASK_09 | MASK_25) | // a ++ 1 * (MASK_10 | MASK_26) | // b ++ 0 * (MASK_06 | MASK_22) | // HPS_CTRL1 ++ 0 * (MASK_05 | MASK_21) | // HPS_CTRL2 ++ 0 * (MASK_01 | MASK_15) // DEBI ++ ); ++ ++ /* start writing RPS1 code from beginning */ ++ count = 0; ++ /* Disable RPS1 */ ++ saa7146_write(dev, MC1, MASK_29); ++ /* RPS1 timeout disable */ ++ saa7146_write(dev, RPS_TOV1, 0); ++ WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); ++#if RPS_IRQ ++ /* issue RPS1 interrupt to increment counter */ ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ WRITE_RPS1(CMD_STOP); ++ /* Jump to begin of RPS program as safety measure (p37) */ ++ WRITE_RPS1(CMD_JUMP); ++ WRITE_RPS1(dev->d_rps1.dma_handle); ++ ++#if RPS_IRQ ++ /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) ++ * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled ++ * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called ++ */ ++ saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); ++ /* set event counter 1 threshold to maximum allowed value (rEC p55) */ ++ saa7146_write(dev, ECT1R, 0x3fff ); ++#endif ++ /* Set RPS1 Address register to point to RPS code (r108 p42) */ ++ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); ++ /* Enable RPS1, (rFC p33) */ ++ saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); ++ ++ mdelay(10); ++ /* now send VSYNC_B to rps1 by rising GPIO3 */ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); ++ mdelay(10); ++ /* if rps1 responded by lowering the GPIO3, ++ * then we have budgetpatch hardware ++ */ ++ if ((saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) { ++ budgetpatch = 1; ++ printk("dvb-ttpci: BUDGET-PATCH DETECTED.\n"); ++ } ++ /* Disable RPS1 */ ++ saa7146_write(dev, MC1, ( MASK_29 )); ++#if RPS_IRQ ++ printk("dvb-ttpci: Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); ++#endif ++ } ++ ++ /* prepare the av7110 device struct */ ++ av7110 = kzalloc(sizeof(struct av7110), GFP_KERNEL); ++ if (!av7110) { ++ dprintk(1, "out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ av7110->card_name = (char*) pci_ext->ext_priv; ++ av7110->dev = dev; ++ dev->ext_priv = av7110; ++ ++ ret = get_firmware(av7110); ++ if (ret < 0) ++ goto err_kfree_0; ++ ++ ret = dvb_register_adapter(&av7110->dvb_adapter, av7110->card_name, ++ THIS_MODULE, &dev->pci->dev, adapter_nr); ++ if (ret < 0) ++ goto err_put_firmware_1; ++ ++ /* the Siemens DVB needs this if you want to have the i2c chips ++ get recognized before the main driver is fully loaded */ ++ saa7146_write(dev, GPIO_CTRL, 0x500000); ++ ++ strlcpy(av7110->i2c_adap.name, pci_ext->ext_priv, sizeof(av7110->i2c_adap.name)); ++ ++ saa7146_i2c_adapter_prepare(dev, &av7110->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); /* 275 kHz */ ++ ++ ret = i2c_add_adapter(&av7110->i2c_adap); ++ if (ret < 0) ++ goto err_dvb_unregister_adapter_2; ++ ++ ttpci_eeprom_parse_mac(&av7110->i2c_adap, ++ av7110->dvb_adapter.proposed_mac); ++ ret = -ENOMEM; ++ ++ /* full-ts mod? */ ++ if (full_ts) ++ av7110->full_ts = true; ++ ++ /* check for full-ts flag in eeprom */ ++ if (i2c_readreg(av7110, 0xaa, 0) == 0x4f && i2c_readreg(av7110, 0xaa, 1) == 0x45) { ++ u8 flags = i2c_readreg(av7110, 0xaa, 2); ++ if (flags != 0xff && (flags & 0x01)) ++ av7110->full_ts = true; ++ } ++ ++ if (av7110->full_ts) { ++ printk(KERN_INFO "dvb-ttpci: full-ts mode enabled for saa7146 port B\n"); ++ spin_lock_init(&av7110->feedlock1); ++ av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, ++ &av7110->pt); ++ if (!av7110->grabbing) ++ goto err_i2c_del_3; ++ ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, MC2, (MASK_10 | MASK_26)); ++ ++ saa7146_write(dev, DD1_INIT, 0x00000600); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ saa7146_write(dev, BRS_CTRL, 0x60000000); ++ saa7146_write(dev, MC2, MASK_08 | MASK_24); ++ ++ /* dma3 */ ++ saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); ++ saa7146_write(dev, BASE_ODD3, 0); ++ saa7146_write(dev, BASE_EVEN3, 0); ++ saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); ++ saa7146_write(dev, PITCH3, TS_WIDTH); ++ saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); ++ saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); ++ saa7146_write(dev, MC2, MASK_04 | MASK_20); ++ ++ tasklet_init(&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); ++ ++ } else if (budgetpatch) { ++ spin_lock_init(&av7110->feedlock1); ++ av7110->grabbing = saa7146_vmalloc_build_pgtable(pdev, length, ++ &av7110->pt); ++ if (!av7110->grabbing) ++ goto err_i2c_del_3; ++ ++ saa7146_write(dev, PCI_BT_V1, 0x1c1f101f); ++ saa7146_write(dev, BCS_CTRL, 0x80400040); ++ /* set dd1 stream a & b */ ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, DD1_INIT, 0x03000200); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x60000000); ++ saa7146_write(dev, BASE_ODD3, 0); ++ saa7146_write(dev, BASE_EVEN3, 0); ++ saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); ++ saa7146_write(dev, BASE_PAGE3, av7110->pt.dma | ME1 | 0x90); ++ ++ saa7146_write(dev, PITCH3, TS_WIDTH); ++ saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); ++ ++ /* upload all */ ++ saa7146_write(dev, MC2, 0x077c077c); ++ saa7146_write(dev, GPIO_CTRL, 0x000000); ++#if RPS_IRQ ++ /* set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) ++ * use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled ++ * use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called ++ */ ++ saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); ++ /* set event counter 1 threshold to maximum allowed value (rEC p55) */ ++ saa7146_write(dev, ECT1R, 0x3fff ); ++#endif ++ /* Setup BUDGETPATCH MAIN RPS1 "program" (p35) */ ++ count = 0; ++ ++ /* Wait Source Line Counter Threshold (p36) */ ++ WRITE_RPS1(CMD_PAUSE | EVT_HS); ++ /* Set GPIO3=1 (p42) */ ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); ++#if RPS_IRQ ++ /* issue RPS1 interrupt */ ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ /* Wait reset Source Line Counter Threshold (p36) */ ++ WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); ++ /* Set GPIO3=0 (p42) */ ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); ++#if RPS_IRQ ++ /* issue RPS1 interrupt */ ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ /* Jump to begin of RPS program (p37) */ ++ WRITE_RPS1(CMD_JUMP); ++ WRITE_RPS1(dev->d_rps1.dma_handle); ++ ++ /* Fix VSYNC level */ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ /* Set RPS1 Address register to point to RPS code (r108 p42) */ ++ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); ++ /* Set Source Line Counter Threshold, using BRS (rCC p43) ++ * It generates HS event every TS_HEIGHT lines ++ * this is related to TS_WIDTH set in register ++ * NUM_LINE_BYTE3. If NUM_LINE_BYTE low 16 bits ++ * are set to TS_WIDTH bytes (TS_WIDTH=2*188), ++ * then RPS_THRESH1 should be set to trigger ++ * every TS_HEIGHT (512) lines. ++ */ ++ saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); ++ ++ /* Enable RPS1 (rFC p33) */ ++ saa7146_write(dev, MC1, (MASK_13 | MASK_29)); ++ ++ /* end of budgetpatch register initialization */ ++ tasklet_init (&av7110->vpe_tasklet, vpeirq, (unsigned long) av7110); ++ } else { ++ saa7146_write(dev, PCI_BT_V1, 0x1c00101f); ++ saa7146_write(dev, BCS_CTRL, 0x80400040); ++ ++ /* set dd1 stream a & b */ ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, DD1_INIT, 0x03000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ /* upload all */ ++ saa7146_write(dev, MC2, 0x077c077c); ++ saa7146_write(dev, GPIO_CTRL, 0x000000); ++ } ++ ++ tasklet_init (&av7110->debi_tasklet, debiirq, (unsigned long) av7110); ++ tasklet_init (&av7110->gpio_tasklet, gpioirq, (unsigned long) av7110); ++ ++ mutex_init(&av7110->pid_mutex); ++ ++ /* locks for data transfers from/to AV7110 */ ++ spin_lock_init(&av7110->debilock); ++ mutex_init(&av7110->dcomlock); ++ av7110->debitype = -1; ++ ++ /* default OSD window */ ++ av7110->osdwin = 1; ++ mutex_init(&av7110->osd_mutex); ++ ++ /* TV standard */ ++ av7110->vidmode = tv_standard == 1 ? AV7110_VIDEO_MODE_NTSC ++ : AV7110_VIDEO_MODE_PAL; ++ ++ /* ARM "watchdog" */ ++ init_waitqueue_head(&av7110->arm_wait); ++ av7110->arm_thread = NULL; ++ ++ /* allocate and init buffers */ ++ av7110->debi_virt = pci_alloc_consistent(pdev, 8192, &av7110->debi_bus); ++ if (!av7110->debi_virt) ++ goto err_saa71466_vfree_4; ++ ++ ++ av7110->iobuf = vmalloc(AVOUTLEN+AOUTLEN+BMPLEN+4*IPACKS); ++ if (!av7110->iobuf) ++ goto err_pci_free_5; ++ ++ ret = av7110_av_init(av7110); ++ if (ret < 0) ++ goto err_iobuf_vfree_6; ++ ++ /* init BMP buffer */ ++ av7110->bmpbuf = av7110->iobuf+AVOUTLEN+AOUTLEN; ++ init_waitqueue_head(&av7110->bmpq); ++ ++ ret = av7110_ca_init(av7110); ++ if (ret < 0) ++ goto err_av7110_av_exit_7; ++ ++ /* load firmware into AV7110 cards */ ++ ret = av7110_bootarm(av7110); ++ if (ret < 0) ++ goto err_av7110_ca_exit_8; ++ ++ ret = av7110_firmversion(av7110); ++ if (ret < 0) ++ goto err_stop_arm_9; ++ ++ if (FW_VERSION(av7110->arm_app)<0x2501) ++ printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. " ++ "System might be unstable!\n", FW_VERSION(av7110->arm_app)); ++ ++ thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); ++ if (IS_ERR(thread)) { ++ ret = PTR_ERR(thread); ++ goto err_stop_arm_9; ++ } ++ av7110->arm_thread = thread; ++ ++ /* set initial volume in mixer struct */ ++ av7110->mixer.volume_left = volume; ++ av7110->mixer.volume_right = volume; ++ ++ ret = av7110_register(av7110); ++ if (ret < 0) ++ goto err_arm_thread_stop_10; ++ ++ init_av7110_av(av7110); ++ ++ /* special case DVB-C: these cards have an analog tuner ++ plus need some special handling, so we have separate ++ saa7146_ext_vv data for these... */ ++ ret = av7110_init_v4l(av7110); ++ if (ret < 0) ++ goto err_av7110_unregister_11; ++ ++ av7110->dvb_adapter.priv = av7110; ++ ret = frontend_init(av7110); ++ if (ret < 0) ++ goto err_av7110_exit_v4l_12; ++ ++ mutex_init(&av7110->ioctl_mutex); ++ ++#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) ++ av7110_ir_init(av7110); ++#endif ++ printk(KERN_INFO "dvb-ttpci: found av7110-%d.\n", av7110_num); ++ av7110_num++; ++out: ++ return ret; ++ ++err_av7110_exit_v4l_12: ++ av7110_exit_v4l(av7110); ++err_av7110_unregister_11: ++ dvb_unregister(av7110); ++err_arm_thread_stop_10: ++ av7110_arm_sync(av7110); ++err_stop_arm_9: ++ /* Nothing to do. Rejoice. */ ++err_av7110_ca_exit_8: ++ av7110_ca_exit(av7110); ++err_av7110_av_exit_7: ++ av7110_av_exit(av7110); ++err_iobuf_vfree_6: ++ vfree(av7110->iobuf); ++err_pci_free_5: ++ pci_free_consistent(pdev, 8192, av7110->debi_virt, av7110->debi_bus); ++err_saa71466_vfree_4: ++ if (av7110->grabbing) ++ saa7146_vfree_destroy_pgtable(pdev, av7110->grabbing, &av7110->pt); ++err_i2c_del_3: ++ i2c_del_adapter(&av7110->i2c_adap); ++err_dvb_unregister_adapter_2: ++ dvb_unregister_adapter(&av7110->dvb_adapter); ++err_put_firmware_1: ++ put_firmware(av7110); ++err_kfree_0: ++ kfree(av7110); ++ goto out; ++} ++ ++static int __devexit av7110_detach(struct saa7146_dev* saa) ++{ ++ struct av7110 *av7110 = saa->ext_priv; ++ dprintk(4, "%p\n", av7110); ++ ++#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) ++ av7110_ir_exit(av7110); ++#endif ++ if (budgetpatch || av7110->full_ts) { ++ if (budgetpatch) { ++ /* Disable RPS1 */ ++ saa7146_write(saa, MC1, MASK_29); ++ /* VSYNC LOW (inactive) */ ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); ++ } ++ saa7146_write(saa, MC1, MASK_20); /* DMA3 off */ ++ SAA7146_IER_DISABLE(saa, MASK_10); ++ SAA7146_ISR_CLEAR(saa, MASK_10); ++ msleep(50); ++ tasklet_kill(&av7110->vpe_tasklet); ++ saa7146_vfree_destroy_pgtable(saa->pci, av7110->grabbing, &av7110->pt); ++ } ++ av7110_exit_v4l(av7110); ++ ++ av7110_arm_sync(av7110); ++ ++ tasklet_kill(&av7110->debi_tasklet); ++ tasklet_kill(&av7110->gpio_tasklet); ++ ++ dvb_unregister(av7110); ++ ++ SAA7146_IER_DISABLE(saa, MASK_19 | MASK_03); ++ SAA7146_ISR_CLEAR(saa, MASK_19 | MASK_03); ++ ++ av7110_ca_exit(av7110); ++ av7110_av_exit(av7110); ++ ++ vfree(av7110->iobuf); ++ pci_free_consistent(saa->pci, 8192, av7110->debi_virt, ++ av7110->debi_bus); ++ ++ i2c_del_adapter(&av7110->i2c_adap); ++ ++ dvb_unregister_adapter (&av7110->dvb_adapter); ++ ++ av7110_num--; ++ ++ put_firmware(av7110); ++ ++ kfree(av7110); ++ ++ saa->ext_priv = NULL; ++ ++ return 0; ++} ++ ++ ++static void av7110_irq(struct saa7146_dev* dev, u32 *isr) ++{ ++ struct av7110 *av7110 = dev->ext_priv; ++ ++ //print_time("av7110_irq"); ++ ++ /* Note: Don't try to handle the DEBI error irq (MASK_18), in ++ * intel mode the timeout is asserted all the time... ++ */ ++ ++ if (*isr & MASK_19) { ++ //printk("av7110_irq: DEBI\n"); ++ /* Note 1: The DEBI irq is level triggered: We must enable it ++ * only after we started a DMA xfer, and disable it here ++ * immediately, or it will be signalled all the time while ++ * DEBI is idle. ++ * Note 2: You would think that an irq which is masked is ++ * not signalled by the hardware. Not so for the SAA7146: ++ * An irq is signalled as long as the corresponding bit ++ * in the ISR is set, and disabling irqs just prevents the ++ * hardware from setting the ISR bit. This means a) that we ++ * must clear the ISR *after* disabling the irq (which is why ++ * we must do it here even though saa7146_core did it already), ++ * and b) that if we were to disable an edge triggered irq ++ * (like the gpio irqs sadly are) temporarily we would likely ++ * loose some. This sucks :-( ++ */ ++ SAA7146_IER_DISABLE(av7110->dev, MASK_19); ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19); ++ tasklet_schedule(&av7110->debi_tasklet); ++ } ++ ++ if (*isr & MASK_03) { ++ //printk("av7110_irq: GPIO\n"); ++ tasklet_schedule(&av7110->gpio_tasklet); ++ } ++ ++ if (*isr & MASK_10) ++ tasklet_schedule(&av7110->vpe_tasklet); ++} ++ ++ ++static struct saa7146_extension av7110_extension_driver; ++ ++#define MAKE_AV7110_INFO(x_var,x_name) \ ++static struct saa7146_pci_extension_data x_var = { \ ++ .ext_priv = x_name, \ ++ .ext = &av7110_extension_driver } ++ ++MAKE_AV7110_INFO(tts_1_X_fsc,"Technotrend/Hauppauge WinTV DVB-S rev1.X or Fujitsu Siemens DVB-C"); ++MAKE_AV7110_INFO(ttt_1_X, "Technotrend/Hauppauge WinTV DVB-T rev1.X"); ++MAKE_AV7110_INFO(ttc_1_X, "Technotrend/Hauppauge WinTV Nexus-CA rev1.X"); ++MAKE_AV7110_INFO(ttc_2_X, "Technotrend/Hauppauge WinTV DVB-C rev2.X"); ++MAKE_AV7110_INFO(tts_2_X, "Technotrend/Hauppauge WinTV Nexus-S rev2.X"); ++MAKE_AV7110_INFO(tts_2_3, "Technotrend/Hauppauge WinTV Nexus-S rev2.3"); ++MAKE_AV7110_INFO(tts_1_3se, "Technotrend/Hauppauge WinTV DVB-S rev1.3 SE"); ++MAKE_AV7110_INFO(ttt, "Technotrend/Hauppauge DVB-T"); ++MAKE_AV7110_INFO(fsc, "Fujitsu Siemens DVB-C"); ++MAKE_AV7110_INFO(fss, "Fujitsu Siemens DVB-S rev1.6"); ++MAKE_AV7110_INFO(gxs_1_3, "Galaxis DVB-S rev1.3"); ++ ++static struct pci_device_id pci_tbl[] = { ++ MAKE_EXTENSION_PCI(fsc, 0x110a, 0x0000), ++ MAKE_EXTENSION_PCI(tts_1_X_fsc, 0x13c2, 0x0000), ++ MAKE_EXTENSION_PCI(ttt_1_X, 0x13c2, 0x0001), ++ MAKE_EXTENSION_PCI(ttc_2_X, 0x13c2, 0x0002), ++ MAKE_EXTENSION_PCI(tts_2_X, 0x13c2, 0x0003), ++ MAKE_EXTENSION_PCI(gxs_1_3, 0x13c2, 0x0004), ++ MAKE_EXTENSION_PCI(fss, 0x13c2, 0x0006), ++ MAKE_EXTENSION_PCI(ttt, 0x13c2, 0x0008), ++ MAKE_EXTENSION_PCI(ttc_1_X, 0x13c2, 0x000a), ++ MAKE_EXTENSION_PCI(tts_2_3, 0x13c2, 0x000e), ++ MAKE_EXTENSION_PCI(tts_1_3se, 0x13c2, 0x1002), ++ ++/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0005), UNDEFINED CARD */ // Technisat SkyStar1 ++/* MAKE_EXTENSION_PCI(???, 0x13c2, 0x0009), UNDEFINED CARD */ // TT/Hauppauge WinTV Nexus-CA v???? ++ ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++ ++static struct saa7146_extension av7110_extension_driver = { ++ .name = "av7110", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .module = THIS_MODULE, ++ .pci_tbl = &pci_tbl[0], ++ .attach = av7110_attach, ++ .detach = __devexit_p(av7110_detach), ++ ++ .irq_mask = MASK_19 | MASK_03 | MASK_10, ++ .irq_func = av7110_irq, ++}; ++ ++ ++static int __init av7110_init(void) ++{ ++ int retval; ++ retval = saa7146_register_extension(&av7110_extension_driver); ++ return retval; ++} ++ ++ ++static void __exit av7110_exit(void) ++{ ++ saa7146_unregister_extension(&av7110_extension_driver); ++} ++ ++module_init(av7110_init); ++module_exit(av7110_exit); ++ ++MODULE_DESCRIPTION("driver for the SAA7146 based AV110 PCI DVB cards by " ++ "Siemens, Technotrend, Hauppauge"); ++MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h +new file mode 100644 +index 0000000..ef3d960 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110.h +@@ -0,0 +1,317 @@ ++#ifndef _AV7110_H_ ++#define _AV7110_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvbdev.h" ++#include "demux.h" ++#include "dvb_demux.h" ++#include "dmxdev.h" ++#include "dvb_filter.h" ++#include "dvb_net.h" ++#include "dvb_ringbuffer.h" ++#include "dvb_frontend.h" ++#include "ves1820.h" ++#include "ves1x93.h" ++#include "stv0299.h" ++#include "tda8083.h" ++#include "sp8870.h" ++#include "stv0297.h" ++#include "l64781.h" ++ ++#include ++ ++ ++#define ANALOG_TUNER_VES1820 1 ++#define ANALOG_TUNER_STV0297 2 ++ ++extern int av7110_debug; ++ ++#define dprintk(level,args...) \ ++ do { if ((av7110_debug & level)) { printk("dvb-ttpci: %s(): ", __func__); printk(args); } } while (0) ++ ++#define MAXFILT 32 ++ ++enum {AV_PES_STREAM, PS_STREAM, TS_STREAM, PES_STREAM}; ++ ++enum av7110_video_mode { ++ AV7110_VIDEO_MODE_PAL = 0, ++ AV7110_VIDEO_MODE_NTSC = 1 ++}; ++ ++struct av7110_p2t { ++ u8 pes[TS_SIZE]; ++ u8 counter; ++ long int pos; ++ int frags; ++ struct dvb_demux_feed *feed; ++}; ++ ++/* video MPEG decoder events: */ ++/* (code copied from dvb_frontend.c, should maybe be factored out...) */ ++#define MAX_VIDEO_EVENT 8 ++struct dvb_video_events { ++ struct video_event events[MAX_VIDEO_EVENT]; ++ int eventw; ++ int eventr; ++ int overflow; ++ wait_queue_head_t wait_queue; ++ spinlock_t lock; ++}; ++ ++ ++struct av7110; ++ ++/* infrared remote control */ ++struct infrared { ++ u16 key_map[256]; ++ struct input_dev *input_dev; ++ char input_phys[32]; ++ struct timer_list keyup_timer; ++ struct tasklet_struct ir_tasklet; ++ void (*ir_handler)(struct av7110 *av7110, u32 ircom); ++ u32 ir_command; ++ u32 ir_config; ++ u32 device_mask; ++ u8 protocol; ++ u8 inversion; ++ u16 last_key; ++ u16 last_toggle; ++ u8 delay_timer_finished; ++}; ++ ++ ++/* place to store all the necessary device information */ ++struct av7110 { ++ ++ /* devices */ ++ ++ struct dvb_device dvb_dev; ++ struct dvb_net dvb_net; ++ ++ struct video_device *v4l_dev; ++ struct video_device *vbi_dev; ++ ++ struct saa7146_dev *dev; ++ ++ struct i2c_adapter i2c_adap; ++ ++ char *card_name; ++ ++ /* support for analog module of dvb-c */ ++ int analog_tuner_flags; ++ int current_input; ++ u32 current_freq; ++ ++ struct tasklet_struct debi_tasklet; ++ struct tasklet_struct gpio_tasklet; ++ ++ int adac_type; /* audio DAC type */ ++#define DVB_ADAC_TI 0 ++#define DVB_ADAC_CRYSTAL 1 ++#define DVB_ADAC_MSP34x0 2 ++#define DVB_ADAC_MSP34x5 3 ++#define DVB_ADAC_NONE -1 ++ ++ ++ /* buffers */ ++ ++ void *iobuf; /* memory for all buffers */ ++ struct dvb_ringbuffer avout; /* buffer for video or A/V mux */ ++#define AVOUTLEN (128*1024) ++ struct dvb_ringbuffer aout; /* buffer for audio */ ++#define AOUTLEN (64*1024) ++ void *bmpbuf; ++#define BMPLEN (8*32768+1024) ++ ++ /* bitmap buffers and states */ ++ ++ int bmpp; ++ int bmplen; ++ volatile int bmp_state; ++#define BMP_NONE 0 ++#define BMP_LOADING 1 ++#define BMP_LOADED 2 ++ wait_queue_head_t bmpq; ++ ++ ++ /* DEBI and polled command interface */ ++ ++ spinlock_t debilock; ++ struct mutex dcomlock; ++ volatile int debitype; ++ volatile int debilen; ++ ++ ++ /* Recording and playback flags */ ++ ++ int rec_mode; ++ int playing; ++#define RP_NONE 0 ++#define RP_VIDEO 1 ++#define RP_AUDIO 2 ++#define RP_AV 3 ++ ++ ++ /* OSD */ ++ ++ int osdwin; /* currently active window */ ++ u16 osdbpp[8]; ++ struct mutex osd_mutex; ++ ++ /* CA */ ++ ++ ca_slot_info_t ci_slot[2]; ++ ++ enum av7110_video_mode vidmode; ++ struct dmxdev dmxdev; ++ struct dvb_demux demux; ++ ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ ++ /* for budget mode demux1 */ ++ struct dmxdev dmxdev1; ++ struct dvb_demux demux1; ++ struct dvb_net dvb_net1; ++ spinlock_t feedlock1; ++ int feeding1; ++ u32 ttbp; ++ unsigned char *grabbing; ++ struct saa7146_pgtable pt; ++ struct tasklet_struct vpe_tasklet; ++ bool full_ts; ++ ++ int fe_synced; ++ struct mutex pid_mutex; ++ ++ int video_blank; ++ struct video_status videostate; ++ u16 display_panscan; ++ int display_ar; ++ int trickmode; ++#define TRICK_NONE 0 ++#define TRICK_FAST 1 ++#define TRICK_SLOW 2 ++#define TRICK_FREEZE 3 ++ struct audio_status audiostate; ++ ++ struct dvb_demux_filter *handle2filter[32]; ++ struct av7110_p2t p2t_filter[MAXFILT]; ++ struct dvb_filter_pes2ts p2t[2]; ++ struct ipack ipack[2]; ++ u8 *kbuf[2]; ++ ++ int sinfo; ++ int feeding; ++ ++ int arm_errors; ++ int registered; ++ ++ ++ /* AV711X */ ++ ++ u32 arm_fw; ++ u32 arm_rtsl; ++ u32 arm_vid; ++ u32 arm_app; ++ u32 avtype; ++ int arm_ready; ++ struct task_struct *arm_thread; ++ wait_queue_head_t arm_wait; ++ u16 arm_loops; ++ ++ void *debi_virt; ++ dma_addr_t debi_bus; ++ ++ u16 pids[DMX_PES_OTHER]; ++ ++ struct dvb_ringbuffer ci_rbuffer; ++ struct dvb_ringbuffer ci_wbuffer; ++ ++ struct audio_mixer mixer; ++ ++ struct dvb_adapter dvb_adapter; ++ struct dvb_device *video_dev; ++ struct dvb_device *audio_dev; ++ struct dvb_device *ca_dev; ++ struct dvb_device *osd_dev; ++ ++ struct dvb_video_events video_events; ++ video_size_t video_size; ++ ++ u16 wssMode; ++ u16 wssData; ++ ++ struct infrared ir; ++ ++ /* firmware stuff */ ++ unsigned char *bin_fw; ++ unsigned long size_fw; ++ ++ unsigned char *bin_dpram; ++ unsigned long size_dpram; ++ ++ unsigned char *bin_root; ++ unsigned long size_root; ++ ++ struct dvb_frontend* fe; ++ fe_status_t fe_status; ++ ++ struct mutex ioctl_mutex; ++ ++ /* crash recovery */ ++ void (*recover)(struct av7110* av7110); ++ fe_sec_voltage_t saved_voltage; ++ fe_sec_tone_mode_t saved_tone; ++ struct dvb_diseqc_master_cmd saved_master_cmd; ++ fe_sec_mini_cmd_t saved_minicmd; ++ ++ int (*fe_init)(struct dvb_frontend* fe); ++ int (*fe_read_status)(struct dvb_frontend* fe, fe_status_t* status); ++ int (*fe_diseqc_reset_overload)(struct dvb_frontend* fe); ++ int (*fe_diseqc_send_master_cmd)(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd); ++ int (*fe_diseqc_send_burst)(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd); ++ int (*fe_set_tone)(struct dvb_frontend* fe, fe_sec_tone_mode_t tone); ++ int (*fe_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); ++ int (*fe_dishnetwork_send_legacy_command)(struct dvb_frontend* fe, unsigned long cmd); ++ int (*fe_set_frontend)(struct dvb_frontend *fe); ++}; ++ ++ ++extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, ++ u16 subpid, u16 pcrpid); ++ ++extern int av7110_check_ir_config(struct av7110 *av7110, int force); ++extern int av7110_ir_init(struct av7110 *av7110); ++extern void av7110_ir_exit(struct av7110 *av7110); ++ ++/* msp3400 i2c subaddresses */ ++#define MSP_WR_DEM 0x10 ++#define MSP_RD_DEM 0x11 ++#define MSP_WR_DSP 0x12 ++#define MSP_RD_DSP 0x13 ++ ++extern int i2c_writereg(struct av7110 *av7110, u8 id, u8 reg, u8 val); ++extern u8 i2c_readreg(struct av7110 *av7110, u8 id, u8 reg); ++extern int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val); ++ ++ ++extern int av7110_init_analog_module(struct av7110 *av7110); ++extern int av7110_init_v4l(struct av7110 *av7110); ++extern int av7110_exit_v4l(struct av7110 *av7110); ++ ++#endif /* _AV7110_H_ */ +diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c +new file mode 100644 +index 0000000..301029c +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_av.c +@@ -0,0 +1,1634 @@ ++/* ++ * av7110_av.c: audio and video MPEG decoder stuff ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * originally based on code by: ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++#include "av7110_av.h" ++#include "av7110_ipack.h" ++ ++/* MPEG-2 (ISO 13818 / H.222.0) stream types */ ++#define PROG_STREAM_MAP 0xBC ++#define PRIVATE_STREAM1 0xBD ++#define PADDING_STREAM 0xBE ++#define PRIVATE_STREAM2 0xBF ++#define AUDIO_STREAM_S 0xC0 ++#define AUDIO_STREAM_E 0xDF ++#define VIDEO_STREAM_S 0xE0 ++#define VIDEO_STREAM_E 0xEF ++#define ECM_STREAM 0xF0 ++#define EMM_STREAM 0xF1 ++#define DSM_CC_STREAM 0xF2 ++#define ISO13522_STREAM 0xF3 ++#define PROG_STREAM_DIR 0xFF ++ ++#define PTS_DTS_FLAGS 0xC0 ++ ++//pts_dts flags ++#define PTS_ONLY 0x80 ++#define PTS_DTS 0xC0 ++#define TS_SIZE 188 ++#define TRANS_ERROR 0x80 ++#define PAY_START 0x40 ++#define TRANS_PRIO 0x20 ++#define PID_MASK_HI 0x1F ++//flags ++#define TRANS_SCRMBL1 0x80 ++#define TRANS_SCRMBL2 0x40 ++#define ADAPT_FIELD 0x20 ++#define PAYLOAD 0x10 ++#define COUNT_MASK 0x0F ++ ++// adaptation flags ++#define DISCON_IND 0x80 ++#define RAND_ACC_IND 0x40 ++#define ES_PRI_IND 0x20 ++#define PCR_FLAG 0x10 ++#define OPCR_FLAG 0x08 ++#define SPLICE_FLAG 0x04 ++#define TRANS_PRIV 0x02 ++#define ADAP_EXT_FLAG 0x01 ++ ++// adaptation extension flags ++#define LTW_FLAG 0x80 ++#define PIECE_RATE 0x40 ++#define SEAM_SPLICE 0x20 ++ ++ ++static void p_to_t(u8 const *buf, long int length, u16 pid, ++ u8 *counter, struct dvb_demux_feed *feed); ++static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len); ++ ++ ++int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) p2t->priv; ++ ++ if (!(dvbdmxfeed->ts_type & TS_PACKET)) ++ return 0; ++ if (buf[3] == 0xe0) // video PES do not have a length in TS ++ buf[4] = buf[5] = 0; ++ if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY) ++ return dvbdmxfeed->cb.ts(buf, len, NULL, 0, ++ &dvbdmxfeed->feed.ts, DMX_OK); ++ else ++ return dvb_filter_pes2ts(p2t, buf, len, 1); ++} ++ ++static int dvb_filter_pes2ts_cb(void *priv, unsigned char *data) ++{ ++ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv; ++ ++ dvbdmxfeed->cb.ts(data, 188, NULL, 0, ++ &dvbdmxfeed->feed.ts, DMX_OK); ++ return 0; ++} ++ ++int av7110_av_start_record(struct av7110 *av7110, int av, ++ struct dvb_demux_feed *dvbdmxfeed) ++{ ++ int ret = 0; ++ struct dvb_demux *dvbdmx = dvbdmxfeed->demux; ++ ++ dprintk(2, "av7110:%p, , dvb_demux_feed:%p\n", av7110, dvbdmxfeed); ++ ++ if (av7110->playing || (av7110->rec_mode & av)) ++ return -EBUSY; ++ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); ++ dvbdmx->recording = 1; ++ av7110->rec_mode |= av; ++ ++ switch (av7110->rec_mode) { ++ case RP_AUDIO: ++ dvb_filter_pes2ts_init(&av7110->p2t[0], ++ dvbdmx->pesfilter[0]->pid, ++ dvb_filter_pes2ts_cb, ++ (void *) dvbdmx->pesfilter[0]); ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); ++ break; ++ ++ case RP_VIDEO: ++ dvb_filter_pes2ts_init(&av7110->p2t[1], ++ dvbdmx->pesfilter[1]->pid, ++ dvb_filter_pes2ts_cb, ++ (void *) dvbdmx->pesfilter[1]); ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); ++ break; ++ ++ case RP_AV: ++ dvb_filter_pes2ts_init(&av7110->p2t[0], ++ dvbdmx->pesfilter[0]->pid, ++ dvb_filter_pes2ts_cb, ++ (void *) dvbdmx->pesfilter[0]); ++ dvb_filter_pes2ts_init(&av7110->p2t[1], ++ dvbdmx->pesfilter[1]->pid, ++ dvb_filter_pes2ts_cb, ++ (void *) dvbdmx->pesfilter[1]); ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0); ++ break; ++ } ++ return ret; ++} ++ ++int av7110_av_start_play(struct av7110 *av7110, int av) ++{ ++ int ret = 0; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (av7110->rec_mode) ++ return -EBUSY; ++ if (av7110->playing & av) ++ return -EBUSY; ++ ++ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); ++ ++ if (av7110->playing == RP_NONE) { ++ av7110_ipack_reset(&av7110->ipack[0]); ++ av7110_ipack_reset(&av7110->ipack[1]); ++ } ++ ++ av7110->playing |= av; ++ switch (av7110->playing) { ++ case RP_AUDIO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); ++ break; ++ case RP_VIDEO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); ++ av7110->sinfo = 0; ++ break; ++ case RP_AV: ++ av7110->sinfo = 0; ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0); ++ break; ++ } ++ return ret; ++} ++ ++int av7110_av_stop(struct av7110 *av7110, int av) ++{ ++ int ret = 0; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (!(av7110->playing & av) && !(av7110->rec_mode & av)) ++ return 0; ++ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); ++ if (av7110->playing) { ++ av7110->playing &= ~av; ++ switch (av7110->playing) { ++ case RP_AUDIO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0); ++ break; ++ case RP_VIDEO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0); ++ break; ++ case RP_NONE: ++ ret = av7110_set_vidmode(av7110, av7110->vidmode); ++ break; ++ } ++ } else { ++ av7110->rec_mode &= ~av; ++ switch (av7110->rec_mode) { ++ case RP_AUDIO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0); ++ break; ++ case RP_VIDEO: ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0); ++ break; ++ case RP_NONE: ++ break; ++ } ++ } ++ return ret; ++} ++ ++ ++int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen) ++{ ++ int len; ++ u32 sync; ++ u16 blen; ++ ++ if (!dlen) { ++ wake_up(&buf->queue); ++ return -1; ++ } ++ while (1) { ++ len = dvb_ringbuffer_avail(buf); ++ if (len < 6) { ++ wake_up(&buf->queue); ++ return -1; ++ } ++ sync = DVB_RINGBUFFER_PEEK(buf, 0) << 24; ++ sync |= DVB_RINGBUFFER_PEEK(buf, 1) << 16; ++ sync |= DVB_RINGBUFFER_PEEK(buf, 2) << 8; ++ sync |= DVB_RINGBUFFER_PEEK(buf, 3); ++ ++ if (((sync &~ 0x0f) == 0x000001e0) || ++ ((sync &~ 0x1f) == 0x000001c0) || ++ (sync == 0x000001bd)) ++ break; ++ printk("resync\n"); ++ DVB_RINGBUFFER_SKIP(buf, 1); ++ } ++ blen = DVB_RINGBUFFER_PEEK(buf, 4) << 8; ++ blen |= DVB_RINGBUFFER_PEEK(buf, 5); ++ blen += 6; ++ if (len < blen || blen > dlen) { ++ //printk("buffer empty - avail %d blen %u dlen %d\n", len, blen, dlen); ++ wake_up(&buf->queue); ++ return -1; ++ } ++ ++ dvb_ringbuffer_read(buf, dest, (size_t) blen); ++ ++ dprintk(2, "pread=0x%08lx, pwrite=0x%08lx\n", ++ (unsigned long) buf->pread, (unsigned long) buf->pwrite); ++ wake_up(&buf->queue); ++ return blen; ++} ++ ++ ++int av7110_set_volume(struct av7110 *av7110, int volleft, int volright) ++{ ++ int err, vol, val, balance = 0; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ av7110->mixer.volume_left = volleft; ++ av7110->mixer.volume_right = volright; ++ ++ switch (av7110->adac_type) { ++ case DVB_ADAC_TI: ++ volleft = (volleft * 256) / 1036; ++ volright = (volright * 256) / 1036; ++ if (volleft > 0x3f) ++ volleft = 0x3f; ++ if (volright > 0x3f) ++ volright = 0x3f; ++ if ((err = SendDAC(av7110, 3, 0x80 + volleft))) ++ return err; ++ return SendDAC(av7110, 4, volright); ++ ++ case DVB_ADAC_CRYSTAL: ++ volleft = 127 - volleft / 2; ++ volright = 127 - volright / 2; ++ i2c_writereg(av7110, 0x20, 0x03, volleft); ++ i2c_writereg(av7110, 0x20, 0x04, volright); ++ return 0; ++ ++ case DVB_ADAC_MSP34x0: ++ vol = (volleft > volright) ? volleft : volright; ++ val = (vol * 0x73 / 255) << 8; ++ if (vol > 0) ++ balance = ((volright - volleft) * 127) / vol; ++ msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */ ++ return 0; ++ ++ case DVB_ADAC_MSP34x5: ++ vol = (volleft > volright) ? volleft : volright; ++ val = (vol * 0x73 / 255) << 8; ++ if (vol > 0) ++ balance = ((volright - volleft) * 127) / vol; ++ msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */ ++ return 0; ++ } ++ ++ return 0; ++} ++ ++int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode) ++{ ++ int ret; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode); ++ ++ if (!ret && !av7110->playing) { ++ ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO], ++ av7110->pids[DMX_PES_AUDIO], ++ av7110->pids[DMX_PES_TELETEXT], ++ 0, av7110->pids[DMX_PES_PCR]); ++ if (!ret) ++ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0); ++ } ++ return ret; ++} ++ ++ ++static enum av7110_video_mode sw2mode[16] = { ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, ++ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_PAL, ++ AV7110_VIDEO_MODE_NTSC, AV7110_VIDEO_MODE_NTSC, ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_NTSC, ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, ++ AV7110_VIDEO_MODE_PAL, AV7110_VIDEO_MODE_PAL, ++}; ++ ++static int get_video_format(struct av7110 *av7110, u8 *buf, int count) ++{ ++ int i; ++ int hsize, vsize; ++ int sw; ++ u8 *p; ++ int ret = 0; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (av7110->sinfo) ++ return 0; ++ for (i = 7; i < count - 10; i++) { ++ p = buf + i; ++ if (p[0] || p[1] || p[2] != 0x01 || p[3] != 0xb3) ++ continue; ++ p += 4; ++ hsize = ((p[1] &0xF0) >> 4) | (p[0] << 4); ++ vsize = ((p[1] &0x0F) << 8) | (p[2]); ++ sw = (p[3] & 0x0F); ++ ret = av7110_set_vidmode(av7110, sw2mode[sw]); ++ if (!ret) { ++ dprintk(2, "playback %dx%d fr=%d\n", hsize, vsize, sw); ++ av7110->sinfo = 1; ++ } ++ break; ++ } ++ return ret; ++} ++ ++ ++/**************************************************************************** ++ * I/O buffer management and control ++ ****************************************************************************/ ++ ++static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, ++ const u8 *buf, unsigned long count) ++{ ++ unsigned long todo = count; ++ int free; ++ ++ while (todo > 0) { ++ if (dvb_ringbuffer_free(rbuf) < 2048) { ++ if (wait_event_interruptible(rbuf->queue, ++ (dvb_ringbuffer_free(rbuf) >= 2048))) ++ return count - todo; ++ } ++ free = dvb_ringbuffer_free(rbuf); ++ if (free > todo) ++ free = todo; ++ dvb_ringbuffer_write(rbuf, buf, free); ++ todo -= free; ++ buf += free; ++ } ++ ++ return count - todo; ++} ++ ++static void play_video_cb(u8 *buf, int count, void *priv) ++{ ++ struct av7110 *av7110 = (struct av7110 *) priv; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if ((buf[3] & 0xe0) == 0xe0) { ++ get_video_format(av7110, buf, count); ++ aux_ring_buffer_write(&av7110->avout, buf, count); ++ } else ++ aux_ring_buffer_write(&av7110->aout, buf, count); ++} ++ ++static void play_audio_cb(u8 *buf, int count, void *priv) ++{ ++ struct av7110 *av7110 = (struct av7110 *) priv; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ aux_ring_buffer_write(&av7110->aout, buf, count); ++} ++ ++ ++#define FREE_COND_TS (dvb_ringbuffer_free(rb) >= 4096) ++ ++static ssize_t ts_play(struct av7110 *av7110, const char __user *buf, ++ unsigned long count, int nonblock, int type) ++{ ++ struct dvb_ringbuffer *rb; ++ u8 *kb; ++ unsigned long todo = count; ++ ++ dprintk(2, "%s: type %d cnt %lu\n", __func__, type, count); ++ ++ rb = (type) ? &av7110->avout : &av7110->aout; ++ kb = av7110->kbuf[type]; ++ ++ if (!kb) ++ return -ENOBUFS; ++ ++ if (nonblock && !FREE_COND_TS) ++ return -EWOULDBLOCK; ++ ++ while (todo >= TS_SIZE) { ++ if (!FREE_COND_TS) { ++ if (nonblock) ++ return count - todo; ++ if (wait_event_interruptible(rb->queue, FREE_COND_TS)) ++ return count - todo; ++ } ++ if (copy_from_user(kb, buf, TS_SIZE)) ++ return -EFAULT; ++ write_ts_to_decoder(av7110, type, kb, TS_SIZE); ++ todo -= TS_SIZE; ++ buf += TS_SIZE; ++ } ++ ++ return count - todo; ++} ++ ++ ++#define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ ++ dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) ++ ++static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, ++ unsigned long count, int nonblock, int type) ++{ ++ unsigned long todo = count, n; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (!av7110->kbuf[type]) ++ return -ENOBUFS; ++ ++ if (nonblock && !FREE_COND) ++ return -EWOULDBLOCK; ++ ++ while (todo > 0) { ++ if (!FREE_COND) { ++ if (nonblock) ++ return count - todo; ++ if (wait_event_interruptible(av7110->avout.queue, ++ FREE_COND)) ++ return count - todo; ++ } ++ n = todo; ++ if (n > IPACKS * 2) ++ n = IPACKS * 2; ++ if (copy_from_user(av7110->kbuf[type], buf, n)) ++ return -EFAULT; ++ av7110_ipack_instant_repack(av7110->kbuf[type], n, ++ &av7110->ipack[type]); ++ todo -= n; ++ buf += n; ++ } ++ return count - todo; ++} ++ ++static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, ++ unsigned long count, int nonblock, int type) ++{ ++ unsigned long todo = count, n; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (!av7110->kbuf[type]) ++ return -ENOBUFS; ++ ++ if (nonblock && !FREE_COND) ++ return -EWOULDBLOCK; ++ ++ while (todo > 0) { ++ if (!FREE_COND) { ++ if (nonblock) ++ return count - todo; ++ if (wait_event_interruptible(av7110->avout.queue, ++ FREE_COND)) ++ return count - todo; ++ } ++ n = todo; ++ if (n > IPACKS * 2) ++ n = IPACKS * 2; ++ av7110_ipack_instant_repack(buf, n, &av7110->ipack[type]); ++ todo -= n; ++ buf += n; ++ } ++ return count - todo; ++} ++ ++static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, ++ unsigned long count, int nonblock, int type) ++{ ++ unsigned long todo = count, n; ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (!av7110->kbuf[type]) ++ return -ENOBUFS; ++ if (nonblock && dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) ++ return -EWOULDBLOCK; ++ ++ while (todo > 0) { ++ if (dvb_ringbuffer_free(&av7110->aout) < 20 * 1024) { ++ if (nonblock) ++ return count - todo; ++ if (wait_event_interruptible(av7110->aout.queue, ++ (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024))) ++ return count-todo; ++ } ++ n = todo; ++ if (n > IPACKS * 2) ++ n = IPACKS * 2; ++ if (copy_from_user(av7110->kbuf[type], buf, n)) ++ return -EFAULT; ++ av7110_ipack_instant_repack(av7110->kbuf[type], n, ++ &av7110->ipack[type]); ++ todo -= n; ++ buf += n; ++ } ++ return count - todo; ++} ++ ++void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed) ++{ ++ memset(p->pes, 0, TS_SIZE); ++ p->counter = 0; ++ p->pos = 0; ++ p->frags = 0; ++ if (feed) ++ p->feed = feed; ++} ++ ++static void clear_p2t(struct av7110_p2t *p) ++{ ++ memset(p->pes, 0, TS_SIZE); ++// p->counter = 0; ++ p->pos = 0; ++ p->frags = 0; ++} ++ ++ ++static int find_pes_header(u8 const *buf, long int length, int *frags) ++{ ++ int c = 0; ++ int found = 0; ++ ++ *frags = 0; ++ ++ while (c < length - 3 && !found) { ++ if (buf[c] == 0x00 && buf[c + 1] == 0x00 && ++ buf[c + 2] == 0x01) { ++ switch ( buf[c + 3] ) { ++ case PROG_STREAM_MAP: ++ case PRIVATE_STREAM2: ++ case PROG_STREAM_DIR: ++ case ECM_STREAM : ++ case EMM_STREAM : ++ case PADDING_STREAM : ++ case DSM_CC_STREAM : ++ case ISO13522_STREAM: ++ case PRIVATE_STREAM1: ++ case AUDIO_STREAM_S ... AUDIO_STREAM_E: ++ case VIDEO_STREAM_S ... VIDEO_STREAM_E: ++ found = 1; ++ break; ++ ++ default: ++ c++; ++ break; ++ } ++ } else ++ c++; ++ } ++ if (c == length - 3 && !found) { ++ if (buf[length - 1] == 0x00) ++ *frags = 1; ++ if (buf[length - 2] == 0x00 && ++ buf[length - 1] == 0x00) ++ *frags = 2; ++ if (buf[length - 3] == 0x00 && ++ buf[length - 2] == 0x00 && ++ buf[length - 1] == 0x01) ++ *frags = 3; ++ return -1; ++ } ++ ++ return c; ++} ++ ++void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p) ++{ ++ int c, c2, l, add; ++ int check, rest; ++ ++ c = 0; ++ c2 = 0; ++ if (p->frags){ ++ check = 0; ++ switch(p->frags) { ++ case 1: ++ if (buf[c] == 0x00 && buf[c + 1] == 0x01) { ++ check = 1; ++ c += 2; ++ } ++ break; ++ case 2: ++ if (buf[c] == 0x01) { ++ check = 1; ++ c++; ++ } ++ break; ++ case 3: ++ check = 1; ++ } ++ if (check) { ++ switch (buf[c]) { ++ case PROG_STREAM_MAP: ++ case PRIVATE_STREAM2: ++ case PROG_STREAM_DIR: ++ case ECM_STREAM : ++ case EMM_STREAM : ++ case PADDING_STREAM : ++ case DSM_CC_STREAM : ++ case ISO13522_STREAM: ++ case PRIVATE_STREAM1: ++ case AUDIO_STREAM_S ... AUDIO_STREAM_E: ++ case VIDEO_STREAM_S ... VIDEO_STREAM_E: ++ p->pes[0] = 0x00; ++ p->pes[1] = 0x00; ++ p->pes[2] = 0x01; ++ p->pes[3] = buf[c]; ++ p->pos = 4; ++ memcpy(p->pes + p->pos, buf + c, (TS_SIZE - 4) - p->pos); ++ c += (TS_SIZE - 4) - p->pos; ++ p_to_t(p->pes, (TS_SIZE - 4), pid, &p->counter, p->feed); ++ clear_p2t(p); ++ break; ++ ++ default: ++ c = 0; ++ break; ++ } ++ } ++ p->frags = 0; ++ } ++ ++ if (p->pos) { ++ c2 = find_pes_header(buf + c, length - c, &p->frags); ++ if (c2 >= 0 && c2 < (TS_SIZE - 4) - p->pos) ++ l = c2+c; ++ else ++ l = (TS_SIZE - 4) - p->pos; ++ memcpy(p->pes + p->pos, buf, l); ++ c += l; ++ p->pos += l; ++ p_to_t(p->pes, p->pos, pid, &p->counter, p->feed); ++ clear_p2t(p); ++ } ++ ++ add = 0; ++ while (c < length) { ++ c2 = find_pes_header(buf + c + add, length - c - add, &p->frags); ++ if (c2 >= 0) { ++ c2 += c + add; ++ if (c2 > c){ ++ p_to_t(buf + c, c2 - c, pid, &p->counter, p->feed); ++ c = c2; ++ clear_p2t(p); ++ add = 0; ++ } else ++ add = 1; ++ } else { ++ l = length - c; ++ rest = l % (TS_SIZE - 4); ++ l -= rest; ++ p_to_t(buf + c, l, pid, &p->counter, p->feed); ++ memcpy(p->pes, buf + c + l, rest); ++ p->pos = rest; ++ c = length; ++ } ++ } ++} ++ ++ ++static int write_ts_header2(u16 pid, u8 *counter, int pes_start, u8 *buf, u8 length) ++{ ++ int i; ++ int c = 0; ++ int fill; ++ u8 tshead[4] = { 0x47, 0x00, 0x00, 0x10 }; ++ ++ fill = (TS_SIZE - 4) - length; ++ if (pes_start) ++ tshead[1] = 0x40; ++ if (fill) ++ tshead[3] = 0x30; ++ tshead[1] |= (u8)((pid & 0x1F00) >> 8); ++ tshead[2] |= (u8)(pid & 0x00FF); ++ tshead[3] |= ((*counter)++ & 0x0F); ++ memcpy(buf, tshead, 4); ++ c += 4; ++ ++ if (fill) { ++ buf[4] = fill - 1; ++ c++; ++ if (fill > 1) { ++ buf[5] = 0x00; ++ c++; ++ } ++ for (i = 6; i < fill + 4; i++) { ++ buf[i] = 0xFF; ++ c++; ++ } ++ } ++ ++ return c; ++} ++ ++ ++static void p_to_t(u8 const *buf, long int length, u16 pid, u8 *counter, ++ struct dvb_demux_feed *feed) ++{ ++ int l, pes_start; ++ u8 obuf[TS_SIZE]; ++ long c = 0; ++ ++ pes_start = 0; ++ if (length > 3 && ++ buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x01) ++ switch (buf[3]) { ++ case PROG_STREAM_MAP: ++ case PRIVATE_STREAM2: ++ case PROG_STREAM_DIR: ++ case ECM_STREAM : ++ case EMM_STREAM : ++ case PADDING_STREAM : ++ case DSM_CC_STREAM : ++ case ISO13522_STREAM: ++ case PRIVATE_STREAM1: ++ case AUDIO_STREAM_S ... AUDIO_STREAM_E: ++ case VIDEO_STREAM_S ... VIDEO_STREAM_E: ++ pes_start = 1; ++ break; ++ ++ default: ++ break; ++ } ++ ++ while (c < length) { ++ memset(obuf, 0, TS_SIZE); ++ if (length - c >= (TS_SIZE - 4)){ ++ l = write_ts_header2(pid, counter, pes_start, ++ obuf, (TS_SIZE - 4)); ++ memcpy(obuf + l, buf + c, TS_SIZE - l); ++ c += TS_SIZE - l; ++ } else { ++ l = write_ts_header2(pid, counter, pes_start, ++ obuf, length - c); ++ memcpy(obuf + l, buf + c, TS_SIZE - l); ++ c = length; ++ } ++ feed->cb.ts(obuf, 188, NULL, 0, &feed->feed.ts, DMX_OK); ++ pes_start = 0; ++ } ++} ++ ++ ++static int write_ts_to_decoder(struct av7110 *av7110, int type, const u8 *buf, size_t len) ++{ ++ struct ipack *ipack = &av7110->ipack[type]; ++ ++ if (buf[1] & TRANS_ERROR) { ++ av7110_ipack_reset(ipack); ++ return -1; ++ } ++ ++ if (!(buf[3] & PAYLOAD)) ++ return -1; ++ ++ if (buf[1] & PAY_START) ++ av7110_ipack_flush(ipack); ++ ++ if (buf[3] & ADAPT_FIELD) { ++ len -= buf[4] + 1; ++ buf += buf[4] + 1; ++ if (!len) ++ return 0; ++ } ++ ++ av7110_ipack_instant_repack(buf + 4, len - 4, ipack); ++ return 0; ++} ++ ++ ++int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct av7110 *av7110 = (struct av7110 *) demux->priv; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (av7110->full_ts && demux->dmx.frontend->source != DMX_MEMORY_FE) ++ return 0; ++ ++ switch (feed->pes_type) { ++ case 0: ++ if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) ++ return -EINVAL; ++ break; ++ case 1: ++ if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) ++ return -EINVAL; ++ break; ++ default: ++ return -1; ++ } ++ ++ return write_ts_to_decoder(av7110, feed->pes_type, buf, len); ++} ++ ++ ++ ++/****************************************************************************** ++ * Video MPEG decoder events ++ ******************************************************************************/ ++void dvb_video_add_event(struct av7110 *av7110, struct video_event *event) ++{ ++ struct dvb_video_events *events = &av7110->video_events; ++ int wp; ++ ++ spin_lock_bh(&events->lock); ++ ++ wp = (events->eventw + 1) % MAX_VIDEO_EVENT; ++ if (wp == events->eventr) { ++ events->overflow = 1; ++ events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; ++ } ++ ++ //FIXME: timestamp? ++ memcpy(&events->events[events->eventw], event, sizeof(struct video_event)); ++ events->eventw = wp; ++ ++ spin_unlock_bh(&events->lock); ++ ++ wake_up_interruptible(&events->wait_queue); ++} ++ ++ ++static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event, int flags) ++{ ++ struct dvb_video_events *events = &av7110->video_events; ++ ++ if (events->overflow) { ++ events->overflow = 0; ++ return -EOVERFLOW; ++ } ++ if (events->eventw == events->eventr) { ++ int ret; ++ ++ if (flags & O_NONBLOCK) ++ return -EWOULDBLOCK; ++ ++ ret = wait_event_interruptible(events->wait_queue, ++ events->eventw != events->eventr); ++ if (ret < 0) ++ return ret; ++ } ++ ++ spin_lock_bh(&events->lock); ++ ++ memcpy(event, &events->events[events->eventr], ++ sizeof(struct video_event)); ++ events->eventr = (events->eventr + 1) % MAX_VIDEO_EVENT; ++ ++ spin_unlock_bh(&events->lock); ++ ++ return 0; ++} ++ ++ ++/****************************************************************************** ++ * DVB device file operations ++ ******************************************************************************/ ++ ++static unsigned int dvb_video_poll(struct file *file, poll_table *wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned int mask = 0; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) ++ poll_wait(file, &av7110->avout.queue, wait); ++ ++ poll_wait(file, &av7110->video_events.wait_queue, wait); ++ ++ if (av7110->video_events.eventw != av7110->video_events.eventr) ++ mask = POLLPRI; ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ if (av7110->playing) { ++ if (FREE_COND) ++ mask |= (POLLOUT | POLLWRNORM); ++ } else /* if not playing: may play if asked for */ ++ mask |= (POLLOUT | POLLWRNORM); ++ } ++ ++ return mask; ++} ++ ++static ssize_t dvb_video_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned char c; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) ++ return -EPERM; ++ ++ if (av7110->videostate.stream_source != VIDEO_SOURCE_MEMORY) ++ return -EPERM; ++ ++ if (get_user(c, buf)) ++ return -EFAULT; ++ if (c == 0x47 && count % TS_SIZE == 0) ++ return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); ++ else ++ return dvb_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 1); ++} ++ ++static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned int mask = 0; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ poll_wait(file, &av7110->aout.queue, wait); ++ ++ if (av7110->playing) { ++ if (dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) ++ mask |= (POLLOUT | POLLWRNORM); ++ } else /* if not playing: may play if asked for */ ++ mask = (POLLOUT | POLLWRNORM); ++ ++ return mask; ++} ++ ++static ssize_t dvb_audio_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned char c; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (av7110->audiostate.stream_source != AUDIO_SOURCE_MEMORY) { ++ printk(KERN_ERR "not audio source memory\n"); ++ return -EPERM; ++ } ++ ++ if (get_user(c, buf)) ++ return -EFAULT; ++ if (c == 0x47 && count % TS_SIZE == 0) ++ return ts_play(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); ++ else ++ return dvb_aplay(av7110, buf, count, file->f_flags & O_NONBLOCK, 0); ++} ++ ++static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; ++ ++#define MIN_IFRAME 400000 ++ ++static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) ++{ ++ unsigned i, n; ++ int progressive = 0; ++ int match = 0; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (!(av7110->playing & RP_VIDEO)) { ++ if (av7110_av_start_play(av7110, RP_VIDEO) < 0) ++ return -EBUSY; ++ } ++ ++ /* search in buf for instances of 00 00 01 b5 1? */ ++ for (i = 0; i < len; i++) { ++ unsigned char c; ++ if (get_user(c, buf + i)) ++ return -EFAULT; ++ if (match == 5) { ++ progressive = c & 0x08; ++ match = 0; ++ } ++ if (c == 0x00) { ++ match = (match == 1 || match == 2) ? 2 : 1; ++ continue; ++ } ++ switch (match++) { ++ case 2: if (c == 0x01) ++ continue; ++ break; ++ case 3: if (c == 0xb5) ++ continue; ++ break; ++ case 4: if ((c & 0xf0) == 0x10) ++ continue; ++ break; ++ } ++ match = 0; ++ } ++ ++ /* setting n always > 1, fixes problems when playing stillframes ++ consisting of I- and P-Frames */ ++ n = MIN_IFRAME / len + 1; ++ ++ /* FIXME: nonblock? */ ++ dvb_play_kernel(av7110, iframe_header, sizeof(iframe_header), 0, 1); ++ ++ for (i = 0; i < n; i++) ++ dvb_play(av7110, buf, len, 0, 1); ++ ++ av7110_ipack_flush(&av7110->ipack[1]); ++ ++ if (progressive) ++ return vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); ++ else ++ return 0; ++} ++ ++ ++static int dvb_video_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned long arg = (unsigned long) parg; ++ int ret = 0; ++ ++ dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); ++ ++ if ((file->f_flags & O_ACCMODE) == O_RDONLY) { ++ if ( cmd != VIDEO_GET_STATUS && cmd != VIDEO_GET_EVENT && ++ cmd != VIDEO_GET_SIZE ) { ++ return -EPERM; ++ } ++ } ++ ++ if (mutex_lock_interruptible(&av7110->ioctl_mutex)) ++ return -ERESTARTSYS; ++ ++ switch (cmd) { ++ case VIDEO_STOP: ++ av7110->videostate.play_state = VIDEO_STOPPED; ++ if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) ++ ret = av7110_av_stop(av7110, RP_VIDEO); ++ else ++ ret = vidcom(av7110, AV_VIDEO_CMD_STOP, ++ av7110->videostate.video_blank ? 0 : 1); ++ if (!ret) ++ av7110->trickmode = TRICK_NONE; ++ break; ++ ++ case VIDEO_PLAY: ++ av7110->trickmode = TRICK_NONE; ++ if (av7110->videostate.play_state == VIDEO_FREEZED) { ++ av7110->videostate.play_state = VIDEO_PLAYING; ++ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); ++ if (ret) ++ break; ++ } ++ if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) { ++ if (av7110->playing == RP_AV) { ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0); ++ if (ret) ++ break; ++ av7110->playing &= ~RP_VIDEO; ++ } ++ ret = av7110_av_start_play(av7110, RP_VIDEO); ++ } ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); ++ if (!ret) ++ av7110->videostate.play_state = VIDEO_PLAYING; ++ break; ++ ++ case VIDEO_FREEZE: ++ av7110->videostate.play_state = VIDEO_FREEZED; ++ if (av7110->playing & RP_VIDEO) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); ++ else ++ ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); ++ if (!ret) ++ av7110->trickmode = TRICK_FREEZE; ++ break; ++ ++ case VIDEO_CONTINUE: ++ if (av7110->playing & RP_VIDEO) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); ++ if (!ret) { ++ av7110->videostate.play_state = VIDEO_PLAYING; ++ av7110->trickmode = TRICK_NONE; ++ } ++ break; ++ ++ case VIDEO_SELECT_SOURCE: ++ av7110->videostate.stream_source = (video_stream_source_t) arg; ++ break; ++ ++ case VIDEO_SET_BLANK: ++ av7110->videostate.video_blank = (int) arg; ++ break; ++ ++ case VIDEO_GET_STATUS: ++ memcpy(parg, &av7110->videostate, sizeof(struct video_status)); ++ break; ++ ++ case VIDEO_GET_EVENT: ++ ret = dvb_video_get_event(av7110, parg, file->f_flags); ++ break; ++ ++ case VIDEO_GET_SIZE: ++ memcpy(parg, &av7110->video_size, sizeof(video_size_t)); ++ break; ++ ++ case VIDEO_SET_DISPLAY_FORMAT: ++ { ++ video_displayformat_t format = (video_displayformat_t) arg; ++ switch (format) { ++ case VIDEO_PAN_SCAN: ++ av7110->display_panscan = VID_PAN_SCAN_PREF; ++ break; ++ case VIDEO_LETTER_BOX: ++ av7110->display_panscan = VID_VC_AND_PS_PREF; ++ break; ++ case VIDEO_CENTER_CUT_OUT: ++ av7110->display_panscan = VID_CENTRE_CUT_PREF; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ if (ret < 0) ++ break; ++ av7110->videostate.display_format = format; ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, ++ 1, av7110->display_panscan); ++ break; ++ } ++ ++ case VIDEO_SET_FORMAT: ++ if (arg > 1) { ++ ret = -EINVAL; ++ break; ++ } ++ av7110->display_ar = arg; ++ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, ++ 1, (u16) arg); ++ break; ++ ++ case VIDEO_STILLPICTURE: ++ { ++ struct video_still_picture *pic = ++ (struct video_still_picture *) parg; ++ av7110->videostate.stream_source = VIDEO_SOURCE_MEMORY; ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ++ ret = play_iframe(av7110, pic->iFrame, pic->size, ++ file->f_flags & O_NONBLOCK); ++ break; ++ } ++ ++ case VIDEO_FAST_FORWARD: ++ //note: arg is ignored by firmware ++ if (av7110->playing & RP_VIDEO) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, ++ __Scan_I, 2, AV_PES, 0); ++ else ++ ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); ++ if (!ret) { ++ av7110->trickmode = TRICK_FAST; ++ av7110->videostate.play_state = VIDEO_PLAYING; ++ } ++ break; ++ ++ case VIDEO_SLOWMOTION: ++ if (av7110->playing&RP_VIDEO) { ++ if (av7110->trickmode != TRICK_SLOW) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); ++ } else { ++ ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); ++ } ++ if (!ret) { ++ av7110->trickmode = TRICK_SLOW; ++ av7110->videostate.play_state = VIDEO_PLAYING; ++ } ++ break; ++ ++ case VIDEO_GET_CAPABILITIES: ++ *(int *)parg = VIDEO_CAP_MPEG1 | VIDEO_CAP_MPEG2 | ++ VIDEO_CAP_SYS | VIDEO_CAP_PROG; ++ break; ++ ++ case VIDEO_CLEAR_BUFFER: ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ++ av7110_ipack_reset(&av7110->ipack[1]); ++ if (av7110->playing == RP_AV) { ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, ++ __Play, 2, AV_PES, 0); ++ if (ret) ++ break; ++ if (av7110->trickmode == TRICK_FAST) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, ++ __Scan_I, 2, AV_PES, 0); ++ if (av7110->trickmode == TRICK_SLOW) { ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, ++ __Slow, 2, 0, 0); ++ if (!ret) ++ ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); ++ } ++ if (av7110->trickmode == TRICK_FREEZE) ++ ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); ++ } ++ break; ++ ++ case VIDEO_SET_STREAMTYPE: ++ break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ break; ++ } ++ ++ mutex_unlock(&av7110->ioctl_mutex); ++ return ret; ++} ++ ++static int dvb_audio_ioctl(struct file *file, ++ unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned long arg = (unsigned long) parg; ++ int ret = 0; ++ ++ dprintk(1, "av7110:%p, cmd=%04x\n", av7110,cmd); ++ ++ if (((file->f_flags & O_ACCMODE) == O_RDONLY) && ++ (cmd != AUDIO_GET_STATUS)) ++ return -EPERM; ++ ++ if (mutex_lock_interruptible(&av7110->ioctl_mutex)) ++ return -ERESTARTSYS; ++ ++ switch (cmd) { ++ case AUDIO_STOP: ++ if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) ++ ret = av7110_av_stop(av7110, RP_AUDIO); ++ else ++ ret = audcom(av7110, AUDIO_CMD_MUTE); ++ if (!ret) ++ av7110->audiostate.play_state = AUDIO_STOPPED; ++ break; ++ ++ case AUDIO_PLAY: ++ if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) ++ ret = av7110_av_start_play(av7110, RP_AUDIO); ++ if (!ret) ++ ret = audcom(av7110, AUDIO_CMD_UNMUTE); ++ if (!ret) ++ av7110->audiostate.play_state = AUDIO_PLAYING; ++ break; ++ ++ case AUDIO_PAUSE: ++ ret = audcom(av7110, AUDIO_CMD_MUTE); ++ if (!ret) ++ av7110->audiostate.play_state = AUDIO_PAUSED; ++ break; ++ ++ case AUDIO_CONTINUE: ++ if (av7110->audiostate.play_state == AUDIO_PAUSED) { ++ av7110->audiostate.play_state = AUDIO_PLAYING; ++ ret = audcom(av7110, AUDIO_CMD_UNMUTE | AUDIO_CMD_PCM16); ++ } ++ break; ++ ++ case AUDIO_SELECT_SOURCE: ++ av7110->audiostate.stream_source = (audio_stream_source_t) arg; ++ break; ++ ++ case AUDIO_SET_MUTE: ++ { ++ ret = audcom(av7110, arg ? AUDIO_CMD_MUTE : AUDIO_CMD_UNMUTE); ++ if (!ret) ++ av7110->audiostate.mute_state = (int) arg; ++ break; ++ } ++ ++ case AUDIO_SET_AV_SYNC: ++ av7110->audiostate.AV_sync_state = (int) arg; ++ ret = audcom(av7110, arg ? AUDIO_CMD_SYNC_ON : AUDIO_CMD_SYNC_OFF); ++ break; ++ ++ case AUDIO_SET_BYPASS_MODE: ++ if (FW_VERSION(av7110->arm_app) < 0x2621) ++ ret = -EINVAL; ++ av7110->audiostate.bypass_mode = (int)arg; ++ break; ++ ++ case AUDIO_CHANNEL_SELECT: ++ av7110->audiostate.channel_select = (audio_channel_select_t) arg; ++ switch(av7110->audiostate.channel_select) { ++ case AUDIO_STEREO: ++ ret = audcom(av7110, AUDIO_CMD_STEREO); ++ if (!ret) { ++ if (av7110->adac_type == DVB_ADAC_CRYSTAL) ++ i2c_writereg(av7110, 0x20, 0x02, 0x49); ++ else if (av7110->adac_type == DVB_ADAC_MSP34x5) ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); ++ } ++ break; ++ case AUDIO_MONO_LEFT: ++ ret = audcom(av7110, AUDIO_CMD_MONO_L); ++ if (!ret) { ++ if (av7110->adac_type == DVB_ADAC_CRYSTAL) ++ i2c_writereg(av7110, 0x20, 0x02, 0x4a); ++ else if (av7110->adac_type == DVB_ADAC_MSP34x5) ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200); ++ } ++ break; ++ case AUDIO_MONO_RIGHT: ++ ret = audcom(av7110, AUDIO_CMD_MONO_R); ++ if (!ret) { ++ if (av7110->adac_type == DVB_ADAC_CRYSTAL) ++ i2c_writereg(av7110, 0x20, 0x02, 0x45); ++ else if (av7110->adac_type == DVB_ADAC_MSP34x5) ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210); ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ break; ++ ++ case AUDIO_GET_STATUS: ++ memcpy(parg, &av7110->audiostate, sizeof(struct audio_status)); ++ break; ++ ++ case AUDIO_GET_CAPABILITIES: ++ if (FW_VERSION(av7110->arm_app) < 0x2621) ++ *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_MP1 | AUDIO_CAP_MP2; ++ else ++ *(unsigned int *)parg = AUDIO_CAP_LPCM | AUDIO_CAP_DTS | AUDIO_CAP_AC3 | ++ AUDIO_CAP_MP1 | AUDIO_CAP_MP2; ++ break; ++ ++ case AUDIO_CLEAR_BUFFER: ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); ++ av7110_ipack_reset(&av7110->ipack[0]); ++ if (av7110->playing == RP_AV) ++ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, ++ __Play, 2, AV_PES, 0); ++ break; ++ ++ case AUDIO_SET_ID: ++ break; ++ ++ case AUDIO_SET_MIXER: ++ { ++ struct audio_mixer *amix = (struct audio_mixer *)parg; ++ ret = av7110_set_volume(av7110, amix->volume_left, amix->volume_right); ++ break; ++ } ++ ++ case AUDIO_SET_STREAMTYPE: ++ break; ++ ++ default: ++ ret = -ENOIOCTLCMD; ++ } ++ ++ mutex_unlock(&av7110->ioctl_mutex); ++ return ret; ++} ++ ++ ++static int dvb_video_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ int err; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if ((err = dvb_generic_open(inode, file)) < 0) ++ return err; ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); ++ av7110->video_blank = 1; ++ av7110->audiostate.AV_sync_state = 1; ++ av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; ++ ++ /* empty event queue */ ++ av7110->video_events.eventr = av7110->video_events.eventw = 0; ++ } ++ ++ return 0; ++} ++ ++static int dvb_video_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if ((file->f_flags & O_ACCMODE) != O_RDONLY) { ++ av7110_av_stop(av7110, RP_VIDEO); ++ } ++ ++ return dvb_generic_release(inode, file); ++} ++ ++static int dvb_audio_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ int err = dvb_generic_open(inode, file); ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ if (err < 0) ++ return err; ++ dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); ++ av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; ++ return 0; ++} ++ ++static int dvb_audio_release(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ ++ dprintk(2, "av7110:%p, \n", av7110); ++ ++ av7110_av_stop(av7110, RP_AUDIO); ++ return dvb_generic_release(inode, file); ++} ++ ++ ++ ++/****************************************************************************** ++ * driver registration ++ ******************************************************************************/ ++ ++static const struct file_operations dvb_video_fops = { ++ .owner = THIS_MODULE, ++ .write = dvb_video_write, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .open = dvb_video_open, ++ .release = dvb_video_release, ++ .poll = dvb_video_poll, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_video = { ++ .priv = NULL, ++ .users = 6, ++ .readers = 5, /* arbitrary */ ++ .writers = 1, ++ .fops = &dvb_video_fops, ++ .kernel_ioctl = dvb_video_ioctl, ++}; ++ ++static const struct file_operations dvb_audio_fops = { ++ .owner = THIS_MODULE, ++ .write = dvb_audio_write, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .open = dvb_audio_open, ++ .release = dvb_audio_release, ++ .poll = dvb_audio_poll, ++ .llseek = noop_llseek, ++}; ++ ++static struct dvb_device dvbdev_audio = { ++ .priv = NULL, ++ .users = 1, ++ .writers = 1, ++ .fops = &dvb_audio_fops, ++ .kernel_ioctl = dvb_audio_ioctl, ++}; ++ ++ ++int av7110_av_register(struct av7110 *av7110) ++{ ++ av7110->audiostate.AV_sync_state = 0; ++ av7110->audiostate.mute_state = 0; ++ av7110->audiostate.play_state = AUDIO_STOPPED; ++ av7110->audiostate.stream_source = AUDIO_SOURCE_DEMUX; ++ av7110->audiostate.channel_select = AUDIO_STEREO; ++ av7110->audiostate.bypass_mode = 0; ++ ++ av7110->videostate.video_blank = 0; ++ av7110->videostate.play_state = VIDEO_STOPPED; ++ av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; ++ av7110->videostate.video_format = VIDEO_FORMAT_4_3; ++ av7110->videostate.display_format = VIDEO_LETTER_BOX; ++ av7110->display_ar = VIDEO_FORMAT_4_3; ++ av7110->display_panscan = VID_VC_AND_PS_PREF; ++ ++ init_waitqueue_head(&av7110->video_events.wait_queue); ++ spin_lock_init(&av7110->video_events.lock); ++ av7110->video_events.eventw = av7110->video_events.eventr = 0; ++ av7110->video_events.overflow = 0; ++ memset(&av7110->video_size, 0, sizeof (video_size_t)); ++ ++ dvb_register_device(&av7110->dvb_adapter, &av7110->video_dev, ++ &dvbdev_video, av7110, DVB_DEVICE_VIDEO); ++ ++ dvb_register_device(&av7110->dvb_adapter, &av7110->audio_dev, ++ &dvbdev_audio, av7110, DVB_DEVICE_AUDIO); ++ ++ return 0; ++} ++ ++void av7110_av_unregister(struct av7110 *av7110) ++{ ++ dvb_unregister_device(av7110->audio_dev); ++ dvb_unregister_device(av7110->video_dev); ++} ++ ++int av7110_av_init(struct av7110 *av7110) ++{ ++ void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; ++ int i, ret; ++ ++ for (i = 0; i < 2; i++) { ++ struct ipack *ipack = av7110->ipack + i; ++ ++ ret = av7110_ipack_init(ipack, IPACKS, play[i]); ++ if (ret < 0) { ++ if (i) ++ av7110_ipack_free(--ipack); ++ goto out; ++ } ++ ipack->data = av7110; ++ } ++ ++ dvb_ringbuffer_init(&av7110->avout, av7110->iobuf, AVOUTLEN); ++ dvb_ringbuffer_init(&av7110->aout, av7110->iobuf + AVOUTLEN, AOUTLEN); ++ ++ av7110->kbuf[0] = (u8 *)(av7110->iobuf + AVOUTLEN + AOUTLEN + BMPLEN); ++ av7110->kbuf[1] = av7110->kbuf[0] + 2 * IPACKS; ++out: ++ return ret; ++} ++ ++void av7110_av_exit(struct av7110 *av7110) ++{ ++ av7110_ipack_free(&av7110->ipack[0]); ++ av7110_ipack_free(&av7110->ipack[1]); ++} +diff --git a/drivers/media/pci/ttpci/av7110_av.h b/drivers/media/pci/ttpci/av7110_av.h +new file mode 100644 +index 0000000..5f02ef8 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_av.h +@@ -0,0 +1,30 @@ ++#ifndef _AV7110_AV_H_ ++#define _AV7110_AV_H_ ++ ++struct av7110; ++ ++extern int av7110_set_vidmode(struct av7110 *av7110, ++ enum av7110_video_mode mode); ++ ++extern int av7110_record_cb(struct dvb_filter_pes2ts *p2t, u8 *buf, size_t len); ++extern int av7110_pes_play(void *dest, struct dvb_ringbuffer *buf, int dlen); ++extern int av7110_write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len); ++ ++extern int av7110_set_volume(struct av7110 *av7110, int volleft, int volright); ++extern int av7110_av_stop(struct av7110 *av7110, int av); ++extern int av7110_av_start_record(struct av7110 *av7110, int av, ++ struct dvb_demux_feed *dvbdmxfeed); ++extern int av7110_av_start_play(struct av7110 *av7110, int av); ++ ++extern void dvb_video_add_event(struct av7110 *av7110, struct video_event *event); ++ ++extern void av7110_p2t_init(struct av7110_p2t *p, struct dvb_demux_feed *feed); ++extern void av7110_p2t_write(u8 const *buf, long int length, u16 pid, struct av7110_p2t *p); ++ ++extern int av7110_av_register(struct av7110 *av7110); ++extern void av7110_av_unregister(struct av7110 *av7110); ++extern int av7110_av_init(struct av7110 *av7110); ++extern void av7110_av_exit(struct av7110 *av7110); ++ ++ ++#endif /* _AV7110_AV_H_ */ +diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c +new file mode 100644 +index 0000000..a6079b9 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_ca.c +@@ -0,0 +1,397 @@ ++/* ++ * av7110_ca.c: CA and CI stuff ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * originally based on code by: ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++#include "av7110_ca.h" ++ ++ ++void CI_handle(struct av7110 *av7110, u8 *data, u16 len) ++{ ++ dprintk(8, "av7110:%p\n",av7110); ++ ++ if (len < 3) ++ return; ++ switch (data[0]) { ++ case CI_MSG_CI_INFO: ++ if (data[2] != 1 && data[2] != 2) ++ break; ++ switch (data[1]) { ++ case 0: ++ av7110->ci_slot[data[2] - 1].flags = 0; ++ break; ++ case 1: ++ av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; ++ break; ++ case 2: ++ av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; ++ break; ++ } ++ break; ++ case CI_SWITCH_PRG_REPLY: ++ //av7110->ci_stat=data[1]; ++ break; ++ default: ++ break; ++ } ++} ++ ++ ++void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) ++{ ++ if (dvb_ringbuffer_free(cibuf) < len + 2) ++ return; ++ ++ DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); ++ DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); ++ dvb_ringbuffer_write(cibuf, data, len); ++ wake_up_interruptible(&cibuf->queue); ++} ++ ++ ++/****************************************************************************** ++ * CI link layer file ops ++ ******************************************************************************/ ++ ++static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) ++{ ++ struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; ++ void *data; ++ ++ for (p = tab; *p; p++) { ++ data = vmalloc(size); ++ if (!data) { ++ while (p-- != tab) { ++ vfree(p[0]->data); ++ p[0]->data = NULL; ++ } ++ return -ENOMEM; ++ } ++ dvb_ringbuffer_init(*p, data, size); ++ } ++ return 0; ++} ++ ++static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) ++{ ++ dvb_ringbuffer_flush_spinlock_wakeup(cirbuf); ++ dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf); ++} ++ ++static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) ++{ ++ vfree(cirbuf->data); ++ cirbuf->data = NULL; ++ vfree(ciwbuf->data); ++ ciwbuf->data = NULL; ++} ++ ++static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, ++ int slots, ca_slot_info_t *slot) ++{ ++ int i; ++ int len = 0; ++ u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; ++ ++ for (i = 0; i < 2; i++) { ++ if (slots & (1 << i)) ++ len += 8; ++ } ++ ++ if (dvb_ringbuffer_free(cibuf) < len) ++ return -EBUSY; ++ ++ for (i = 0; i < 2; i++) { ++ if (slots & (1 << i)) { ++ msg[2] = i; ++ dvb_ringbuffer_write(cibuf, msg, 8); ++ slot[i].flags = 0; ++ } ++ } ++ ++ return 0; ++} ++ ++static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, ++ const char __user *buf, size_t count, loff_t *ppos) ++{ ++ int free; ++ int non_blocking = file->f_flags & O_NONBLOCK; ++ u8 *page = (u8 *)__get_free_page(GFP_USER); ++ int res; ++ ++ if (!page) ++ return -ENOMEM; ++ ++ res = -EINVAL; ++ if (count > 2048) ++ goto out; ++ ++ res = -EFAULT; ++ if (copy_from_user(page, buf, count)) ++ goto out; ++ ++ free = dvb_ringbuffer_free(cibuf); ++ if (count + 2 > free) { ++ res = -EWOULDBLOCK; ++ if (non_blocking) ++ goto out; ++ res = -ERESTARTSYS; ++ if (wait_event_interruptible(cibuf->queue, ++ (dvb_ringbuffer_free(cibuf) >= count + 2))) ++ goto out; ++ } ++ ++ DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); ++ DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); ++ ++ res = dvb_ringbuffer_write(cibuf, page, count); ++out: ++ free_page((unsigned long)page); ++ return res; ++} ++ ++static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, ++ char __user *buf, size_t count, loff_t *ppos) ++{ ++ int avail; ++ int non_blocking = file->f_flags & O_NONBLOCK; ++ ssize_t len; ++ ++ if (!cibuf->data || !count) ++ return 0; ++ if (non_blocking && (dvb_ringbuffer_empty(cibuf))) ++ return -EWOULDBLOCK; ++ if (wait_event_interruptible(cibuf->queue, ++ !dvb_ringbuffer_empty(cibuf))) ++ return -ERESTARTSYS; ++ avail = dvb_ringbuffer_avail(cibuf); ++ if (avail < 4) ++ return 0; ++ len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; ++ len |= DVB_RINGBUFFER_PEEK(cibuf, 1); ++ if (avail < len + 2 || count < len) ++ return -EINVAL; ++ DVB_RINGBUFFER_SKIP(cibuf, 2); ++ ++ return dvb_ringbuffer_read_user(cibuf, buf, len); ++} ++ ++static int dvb_ca_open(struct inode *inode, struct file *file) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ int err = dvb_generic_open(inode, file); ++ ++ dprintk(8, "av7110:%p\n",av7110); ++ ++ if (err < 0) ++ return err; ++ ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer); ++ return 0; ++} ++ ++static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; ++ struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; ++ unsigned int mask = 0; ++ ++ dprintk(8, "av7110:%p\n",av7110); ++ ++ poll_wait(file, &rbuf->queue, wait); ++ poll_wait(file, &wbuf->queue, wait); ++ ++ if (!dvb_ringbuffer_empty(rbuf)) ++ mask |= (POLLIN | POLLRDNORM); ++ ++ if (dvb_ringbuffer_free(wbuf) > 1024) ++ mask |= (POLLOUT | POLLWRNORM); ++ ++ return mask; ++} ++ ++static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ unsigned long arg = (unsigned long) parg; ++ int ret = 0; ++ ++ dprintk(8, "av7110:%p\n",av7110); ++ ++ if (mutex_lock_interruptible(&av7110->ioctl_mutex)) ++ return -ERESTARTSYS; ++ ++ switch (cmd) { ++ case CA_RESET: ++ ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, ++ &av7110->ci_slot[0]); ++ break; ++ case CA_GET_CAP: ++ { ++ ca_caps_t cap; ++ ++ cap.slot_num = 2; ++ cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? ++ CA_CI_LINK : CA_CI) | CA_DESCR; ++ cap.descr_num = 16; ++ cap.descr_type = CA_ECD; ++ memcpy(parg, &cap, sizeof(cap)); ++ break; ++ } ++ ++ case CA_GET_SLOT_INFO: ++ { ++ ca_slot_info_t *info=(ca_slot_info_t *)parg; ++ ++ if (info->num < 0 || info->num > 1) { ++ mutex_unlock(&av7110->ioctl_mutex); ++ return -EINVAL; ++ } ++ av7110->ci_slot[info->num].num = info->num; ++ av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? ++ CA_CI_LINK : CA_CI; ++ memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t)); ++ break; ++ } ++ ++ case CA_GET_MSG: ++ break; ++ ++ case CA_SEND_MSG: ++ break; ++ ++ case CA_GET_DESCR_INFO: ++ { ++ ca_descr_info_t info; ++ ++ info.num = 16; ++ info.type = CA_ECD; ++ memcpy(parg, &info, sizeof (info)); ++ break; ++ } ++ ++ case CA_SET_DESCR: ++ { ++ ca_descr_t *descr = (ca_descr_t*) parg; ++ ++ if (descr->index >= 16 || descr->parity > 1) { ++ mutex_unlock(&av7110->ioctl_mutex); ++ return -EINVAL; ++ } ++ av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, ++ (descr->index<<8)|descr->parity, ++ (descr->cw[0]<<8)|descr->cw[1], ++ (descr->cw[2]<<8)|descr->cw[3], ++ (descr->cw[4]<<8)|descr->cw[5], ++ (descr->cw[6]<<8)|descr->cw[7]); ++ break; ++ } ++ ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ mutex_unlock(&av7110->ioctl_mutex); ++ return ret; ++} ++ ++static ssize_t dvb_ca_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ ++ dprintk(8, "av7110:%p\n",av7110); ++ return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); ++} ++ ++static ssize_t dvb_ca_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dvb_device *dvbdev = file->private_data; ++ struct av7110 *av7110 = dvbdev->priv; ++ ++ dprintk(8, "av7110:%p\n",av7110); ++ return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); ++} ++ ++static const struct file_operations dvb_ca_fops = { ++ .owner = THIS_MODULE, ++ .read = dvb_ca_read, ++ .write = dvb_ca_write, ++ .unlocked_ioctl = dvb_generic_ioctl, ++ .open = dvb_ca_open, ++ .release = dvb_generic_release, ++ .poll = dvb_ca_poll, ++ .llseek = default_llseek, ++}; ++ ++static struct dvb_device dvbdev_ca = { ++ .priv = NULL, ++ .users = 1, ++ .writers = 1, ++ .fops = &dvb_ca_fops, ++ .kernel_ioctl = dvb_ca_ioctl, ++}; ++ ++ ++int av7110_ca_register(struct av7110 *av7110) ++{ ++ return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev, ++ &dvbdev_ca, av7110, DVB_DEVICE_CA); ++} ++ ++void av7110_ca_unregister(struct av7110 *av7110) ++{ ++ dvb_unregister_device(av7110->ca_dev); ++} ++ ++int av7110_ca_init(struct av7110* av7110) ++{ ++ return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192); ++} ++ ++void av7110_ca_exit(struct av7110* av7110) ++{ ++ ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer); ++} +diff --git a/drivers/media/pci/ttpci/av7110_ca.h b/drivers/media/pci/ttpci/av7110_ca.h +new file mode 100644 +index 0000000..70ee855 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_ca.h +@@ -0,0 +1,14 @@ ++#ifndef _AV7110_CA_H_ ++#define _AV7110_CA_H_ ++ ++struct av7110; ++ ++extern void CI_handle(struct av7110 *av7110, u8 *data, u16 len); ++extern void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len); ++ ++extern int av7110_ca_register(struct av7110 *av7110); ++extern void av7110_ca_unregister(struct av7110 *av7110); ++extern int av7110_ca_init(struct av7110* av7110); ++extern void av7110_ca_exit(struct av7110* av7110); ++ ++#endif /* _AV7110_CA_H_ */ +diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c +new file mode 100644 +index 0000000..f1cbfe5 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_hw.c +@@ -0,0 +1,1208 @@ ++/* ++ * av7110_hw.c: av7110 low level hardware access and firmware interface ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * originally based on code by: ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++/* for debugging ARM communication: */ ++//#define COM_DEBUG ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++ ++#define _NOHANDSHAKE ++ ++/**************************************************************************** ++ * DEBI functions ++ ****************************************************************************/ ++ ++/* This DEBI code is based on the Stradis driver ++ by Nathan Laredo */ ++ ++int av7110_debiwrite(struct av7110 *av7110, u32 config, ++ int addr, u32 val, int count) ++{ ++ struct saa7146_dev *dev = av7110->dev; ++ ++ if (count <= 0 || count > 32764) { ++ printk("%s: invalid count %d\n", __func__, count); ++ return -1; ++ } ++ if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { ++ printk("%s: wait_for_debi_done failed\n", __func__); ++ return -1; ++ } ++ saa7146_write(dev, DEBI_CONFIG, config); ++ if (count <= 4) /* immediate transfer */ ++ saa7146_write(dev, DEBI_AD, val); ++ else /* block transfer */ ++ saa7146_write(dev, DEBI_AD, av7110->debi_bus); ++ saa7146_write(dev, DEBI_COMMAND, (count << 17) | (addr & 0xffff)); ++ saa7146_write(dev, MC2, (2 << 16) | 2); ++ return 0; ++} ++ ++u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count) ++{ ++ struct saa7146_dev *dev = av7110->dev; ++ u32 result = 0; ++ ++ if (count > 32764 || count <= 0) { ++ printk("%s: invalid count %d\n", __func__, count); ++ return 0; ++ } ++ if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { ++ printk("%s: wait_for_debi_done #1 failed\n", __func__); ++ return 0; ++ } ++ saa7146_write(dev, DEBI_AD, av7110->debi_bus); ++ saa7146_write(dev, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); ++ ++ saa7146_write(dev, DEBI_CONFIG, config); ++ saa7146_write(dev, MC2, (2 << 16) | 2); ++ if (count > 4) ++ return count; ++ if (saa7146_wait_for_debi_done(av7110->dev, 0) < 0) { ++ printk("%s: wait_for_debi_done #2 failed\n", __func__); ++ return 0; ++ } ++ ++ result = saa7146_read(dev, DEBI_AD); ++ result &= (0xffffffffUL >> ((4 - count) * 8)); ++ return result; ++} ++ ++ ++ ++/* av7110 ARM core boot stuff */ ++#if 0 ++void av7110_reset_arm(struct av7110 *av7110) ++{ ++ saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO); ++ ++ /* Disable DEBI and GPIO irq */ ++ SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03); ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); ++ ++ saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI); ++ msleep(30); /* the firmware needs some time to initialize */ ++ ++ ARM_ResetMailBox(av7110); ++ ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); ++ SAA7146_IER_ENABLE(av7110->dev, MASK_03); ++ ++ av7110->arm_ready = 1; ++ dprintk(1, "reset ARM\n"); ++} ++#endif /* 0 */ ++ ++static int waitdebi(struct av7110 *av7110, int adr, int state) ++{ ++ int k; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ for (k = 0; k < 100; k++) { ++ if (irdebi(av7110, DEBINOSWAP, adr, 0, 2) == state) ++ return 0; ++ udelay(5); ++ } ++ return -ETIMEDOUT; ++} ++ ++static int load_dram(struct av7110 *av7110, u32 *data, int len) ++{ ++ int i; ++ int blocks, rest; ++ u32 base, bootblock = AV7110_BOOT_BLOCK; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ blocks = len / AV7110_BOOT_MAX_SIZE; ++ rest = len % AV7110_BOOT_MAX_SIZE; ++ base = DRAM_START_CODE; ++ ++ for (i = 0; i < blocks; i++) { ++ if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { ++ printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at block %d\n", i); ++ return -ETIMEDOUT; ++ } ++ dprintk(4, "writing DRAM block %d\n", i); ++ mwdebi(av7110, DEBISWAB, bootblock, ++ ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); ++ bootblock ^= 0x1400; ++ iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); ++ base += AV7110_BOOT_MAX_SIZE; ++ } ++ ++ if (rest > 0) { ++ if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { ++ printk(KERN_ERR "dvb-ttpci: load_dram(): timeout at last block\n"); ++ return -ETIMEDOUT; ++ } ++ if (rest > 4) ++ mwdebi(av7110, DEBISWAB, bootblock, ++ ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); ++ else ++ mwdebi(av7110, DEBISWAB, bootblock, ++ ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); ++ ++ iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); ++ } ++ if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_EMPTY) < 0) { ++ printk(KERN_ERR "dvb-ttpci: load_dram(): timeout after last block\n"); ++ return -ETIMEDOUT; ++ } ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, 0, 2); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); ++ if (waitdebi(av7110, AV7110_BOOT_STATE, BOOTSTATE_AV7110_BOOT_COMPLETE) < 0) { ++ printk(KERN_ERR "dvb-ttpci: load_dram(): final handshake timeout\n"); ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++ ++/* we cannot write av7110 DRAM directly, so load a bootloader into ++ * the DPRAM which implements a simple boot protocol */ ++int av7110_bootarm(struct av7110 *av7110) ++{ ++ const struct firmware *fw; ++ const char *fw_name = "av7110/bootcode.bin"; ++ struct saa7146_dev *dev = av7110->dev; ++ u32 ret; ++ int i; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ av7110->arm_ready = 0; ++ ++ saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); ++ ++ /* Disable DEBI and GPIO irq */ ++ SAA7146_IER_DISABLE(av7110->dev, MASK_03 | MASK_19); ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); ++ ++ /* enable DEBI */ ++ saa7146_write(av7110->dev, MC1, 0x08800880); ++ saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ /* test DEBI */ ++ iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); ++ /* FIXME: Why does Nexus CA require 2x iwdebi for first init? */ ++ iwdebi(av7110, DEBISWAP, DPRAM_BASE, 0x76543210, 4); ++ ++ if ((ret=irdebi(av7110, DEBINOSWAP, DPRAM_BASE, 0, 4)) != 0x10325476) { ++ printk(KERN_ERR "dvb-ttpci: debi test in av7110_bootarm() failed: " ++ "%08x != %08x (check your BIOS 'Plug&Play OS' settings)\n", ++ ret, 0x10325476); ++ return -1; ++ } ++ for (i = 0; i < 8192; i += 4) ++ iwdebi(av7110, DEBISWAP, DPRAM_BASE + i, 0x00, 4); ++ dprintk(2, "debi test OK\n"); ++ ++ /* boot */ ++ dprintk(1, "load boot code\n"); ++ saa7146_setgpio(dev, ARM_IRQ_LINE, SAA7146_GPIO_IRQLO); ++ //saa7146_setgpio(dev, DEBI_DONE_LINE, SAA7146_GPIO_INPUT); ++ //saa7146_setgpio(dev, 3, SAA7146_GPIO_INPUT); ++ ++ ret = request_firmware(&fw, fw_name, &dev->pci->dev); ++ if (ret) { ++ printk(KERN_ERR "dvb-ttpci: Failed to load firmware \"%s\"\n", ++ fw_name); ++ return ret; ++ } ++ ++ mwdebi(av7110, DEBISWAB, DPRAM_BASE, fw->data, fw->size); ++ release_firmware(fw); ++ iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_STATE, BOOTSTATE_BUFFER_FULL, 2); ++ ++ if (saa7146_wait_for_debi_done(av7110->dev, 1)) { ++ printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " ++ "saa7146_wait_for_debi_done() timed out\n"); ++ return -ETIMEDOUT; ++ } ++ saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); ++ mdelay(1); ++ ++ dprintk(1, "load dram code\n"); ++ if (load_dram(av7110, (u32 *)av7110->bin_root, av7110->size_root) < 0) { ++ printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " ++ "load_dram() failed\n"); ++ return -1; ++ } ++ ++ saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTLO); ++ mdelay(1); ++ ++ dprintk(1, "load dpram code\n"); ++ mwdebi(av7110, DEBISWAB, DPRAM_BASE, av7110->bin_dpram, av7110->size_dpram); ++ ++ if (saa7146_wait_for_debi_done(av7110->dev, 1)) { ++ printk(KERN_ERR "dvb-ttpci: av7110_bootarm(): " ++ "saa7146_wait_for_debi_done() timed out after loading DRAM\n"); ++ return -ETIMEDOUT; ++ } ++ saa7146_setgpio(dev, RESET_LINE, SAA7146_GPIO_OUTHI); ++ msleep(30); /* the firmware needs some time to initialize */ ++ ++ //ARM_ClearIrq(av7110); ++ ARM_ResetMailBox(av7110); ++ SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03); ++ SAA7146_IER_ENABLE(av7110->dev, MASK_03); ++ ++ av7110->arm_errors = 0; ++ av7110->arm_ready = 1; ++ return 0; ++} ++MODULE_FIRMWARE("av7110/bootcode.bin"); ++ ++/**************************************************************************** ++ * DEBI command polling ++ ****************************************************************************/ ++ ++int av7110_wait_msgstate(struct av7110 *av7110, u16 flags) ++{ ++ unsigned long start; ++ u32 stat; ++ int err; ++ ++ if (FW_VERSION(av7110->arm_app) <= 0x261c) { ++ /* not supported by old firmware */ ++ msleep(50); ++ return 0; ++ } ++ ++ /* new firmware */ ++ start = jiffies; ++ for (;;) { ++ err = time_after(jiffies, start + ARM_WAIT_FREE); ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ return -ERESTARTSYS; ++ stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); ++ mutex_unlock(&av7110->dcomlock); ++ if ((stat & flags) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "%s: timeout waiting for MSGSTATE %04x\n", ++ __func__, stat & flags); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++ return 0; ++} ++ ++static int __av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) ++{ ++ int i; ++ unsigned long start; ++ char *type = NULL; ++ u16 flags[2] = {0, 0}; ++ u32 stat; ++ int err; ++ ++// dprintk(4, "%p\n", av7110); ++ ++ if (!av7110->arm_ready) { ++ dprintk(1, "arm not ready.\n"); ++ return -ENXIO; ++ } ++ ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_FREE); ++ if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND idle\n", __func__); ++ av7110->arm_errors++; ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++ ++ if (FW_VERSION(av7110->arm_app) <= 0x261f) ++ wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0xffff, 2); ++ ++#ifndef _NOHANDSHAKE ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_SHAKE); ++ if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for HANDSHAKE_REG\n", __func__); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++#endif ++ ++ switch ((buf[0] >> 8) & 0xff) { ++ case COMTYPE_PIDFILTER: ++ case COMTYPE_ENCODER: ++ case COMTYPE_REC_PLAY: ++ case COMTYPE_MPEGDECODER: ++ type = "MSG"; ++ flags[0] = GPMQOver; ++ flags[1] = GPMQFull; ++ break; ++ case COMTYPE_OSD: ++ type = "OSD"; ++ flags[0] = OSDQOver; ++ flags[1] = OSDQFull; ++ break; ++ case COMTYPE_MISC: ++ if (FW_VERSION(av7110->arm_app) >= 0x261d) { ++ type = "MSG"; ++ flags[0] = GPMQOver; ++ flags[1] = GPMQBusy; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (type != NULL) { ++ /* non-immediate COMMAND type */ ++ start = jiffies; ++ for (;;) { ++ err = time_after(jiffies, start + ARM_WAIT_FREE); ++ stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); ++ if (stat & flags[0]) { ++ printk(KERN_ERR "%s: %s QUEUE overflow\n", ++ __func__, type); ++ return -1; ++ } ++ if ((stat & flags[1]) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "%s: timeout waiting on busy %s QUEUE\n", ++ __func__, type); ++ av7110->arm_errors++; ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++ } ++ ++ for (i = 2; i < length; i++) ++ wdebi(av7110, DEBINOSWAP, COMMAND + 2 * i, (u32) buf[i], 2); ++ ++ if (length) ++ wdebi(av7110, DEBINOSWAP, COMMAND + 2, (u32) buf[1], 2); ++ else ++ wdebi(av7110, DEBINOSWAP, COMMAND + 2, 0, 2); ++ ++ wdebi(av7110, DEBINOSWAP, COMMAND, (u32) buf[0], 2); ++ ++ if (FW_VERSION(av7110->arm_app) <= 0x261f) ++ wdebi(av7110, DEBINOSWAP, COM_IF_LOCK, 0x0000, 2); ++ ++#ifdef COM_DEBUG ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_FREE); ++ if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for COMMAND %d to complete\n", ++ __func__, (buf[0] >> 8) & 0xff); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++ ++ stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); ++ if (stat & GPMQOver) { ++ printk(KERN_ERR "dvb-ttpci: %s(): GPMQOver\n", __func__); ++ return -ENOSPC; ++ } ++ else if (stat & OSDQOver) { ++ printk(KERN_ERR "dvb-ttpci: %s(): OSDQOver\n", __func__); ++ return -ENOSPC; ++ } ++#endif ++ ++ return 0; ++} ++ ++static int av7110_send_fw_cmd(struct av7110 *av7110, u16* buf, int length) ++{ ++ int ret; ++ ++// dprintk(4, "%p\n", av7110); ++ ++ if (!av7110->arm_ready) { ++ dprintk(1, "arm not ready.\n"); ++ return -1; ++ } ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ return -ERESTARTSYS; ++ ++ ret = __av7110_send_fw_cmd(av7110, buf, length); ++ mutex_unlock(&av7110->dcomlock); ++ if (ret && ret!=-ERESTARTSYS) ++ printk(KERN_ERR "dvb-ttpci: %s(): av7110_send_fw_cmd error %d\n", ++ __func__, ret); ++ return ret; ++} ++ ++int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...) ++{ ++ va_list args; ++ u16 buf[num + 2]; ++ int i, ret; ++ ++// dprintk(4, "%p\n", av7110); ++ ++ buf[0] = ((type << 8) | com); ++ buf[1] = num; ++ ++ if (num) { ++ va_start(args, num); ++ for (i = 0; i < num; i++) ++ buf[i + 2] = va_arg(args, u32); ++ va_end(args); ++ } ++ ++ ret = av7110_send_fw_cmd(av7110, buf, num + 2); ++ if (ret && ret != -ERESTARTSYS) ++ printk(KERN_ERR "dvb-ttpci: av7110_fw_cmd error %d\n", ret); ++ return ret; ++} ++ ++#if 0 ++int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len) ++{ ++ int i, ret; ++ u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom), ++ 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ for(i = 0; i < len && i < 32; i++) ++ { ++ if(i % 2 == 0) ++ cmd[(i / 2) + 2] = (u16)(buf[i]) << 8; ++ else ++ cmd[(i / 2) + 2] |= buf[i]; ++ } ++ ++ ret = av7110_send_fw_cmd(av7110, cmd, 18); ++ if (ret && ret != -ERESTARTSYS) ++ printk(KERN_ERR "dvb-ttpci: av7110_send_ci_cmd error %d\n", ret); ++ return ret; ++} ++#endif /* 0 */ ++ ++int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, ++ int request_buf_len, u16 *reply_buf, int reply_buf_len) ++{ ++ int err; ++ s16 i; ++ unsigned long start; ++#ifdef COM_DEBUG ++ u32 stat; ++#endif ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (!av7110->arm_ready) { ++ dprintk(1, "arm not ready.\n"); ++ return -1; ++ } ++ ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ return -ERESTARTSYS; ++ ++ if ((err = __av7110_send_fw_cmd(av7110, request_buf, request_buf_len)) < 0) { ++ mutex_unlock(&av7110->dcomlock); ++ printk(KERN_ERR "dvb-ttpci: av7110_fw_request error %d\n", err); ++ return err; ++ } ++ ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_FREE); ++ if (rdebi(av7110, DEBINOSWAP, COMMAND, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "%s: timeout waiting for COMMAND to complete\n", __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -ETIMEDOUT; ++ } ++#ifdef _NOHANDSHAKE ++ msleep(1); ++#endif ++ } ++ ++#ifndef _NOHANDSHAKE ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_SHAKE); ++ if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "%s: timeout waiting for HANDSHAKE_REG\n", __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++#endif ++ ++#ifdef COM_DEBUG ++ stat = rdebi(av7110, DEBINOSWAP, MSGSTATE, 0, 2); ++ if (stat & GPMQOver) { ++ printk(KERN_ERR "%s: GPMQOver\n", __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -1; ++ } ++ else if (stat & OSDQOver) { ++ printk(KERN_ERR "%s: OSDQOver\n", __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -1; ++ } ++#endif ++ ++ for (i = 0; i < reply_buf_len; i++) ++ reply_buf[i] = rdebi(av7110, DEBINOSWAP, COM_BUFF + 2 * i, 0, 2); ++ ++ mutex_unlock(&av7110->dcomlock); ++ return 0; ++} ++ ++static int av7110_fw_query(struct av7110 *av7110, u16 tag, u16* buf, s16 length) ++{ ++ int ret; ++ ret = av7110_fw_request(av7110, &tag, 0, buf, length); ++ if (ret) ++ printk(KERN_ERR "dvb-ttpci: av7110_fw_query error %d\n", ret); ++ return ret; ++} ++ ++ ++/**************************************************************************** ++ * Firmware commands ++ ****************************************************************************/ ++ ++/* get version of the firmware ROM, RTSL, video ucode and ARM application */ ++int av7110_firmversion(struct av7110 *av7110) ++{ ++ u16 buf[20]; ++ u16 tag = ((COMTYPE_REQUEST << 8) + ReqVersion); ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (av7110_fw_query(av7110, tag, buf, 16)) { ++ printk("dvb-ttpci: failed to boot firmware @ card %d\n", ++ av7110->dvb_adapter.num); ++ return -EIO; ++ } ++ ++ av7110->arm_fw = (buf[0] << 16) + buf[1]; ++ av7110->arm_rtsl = (buf[2] << 16) + buf[3]; ++ av7110->arm_vid = (buf[4] << 16) + buf[5]; ++ av7110->arm_app = (buf[6] << 16) + buf[7]; ++ av7110->avtype = (buf[8] << 16) + buf[9]; ++ ++ printk("dvb-ttpci: info @ card %d: firm %08x, rtsl %08x, vid %08x, app %08x\n", ++ av7110->dvb_adapter.num, av7110->arm_fw, ++ av7110->arm_rtsl, av7110->arm_vid, av7110->arm_app); ++ ++ /* print firmware capabilities */ ++ if (FW_CI_LL_SUPPORT(av7110->arm_app)) ++ printk("dvb-ttpci: firmware @ card %d supports CI link layer interface\n", ++ av7110->dvb_adapter.num); ++ else ++ printk("dvb-ttpci: no firmware support for CI link layer interface @ card %d\n", ++ av7110->dvb_adapter.num); ++ ++ return 0; ++} ++ ++ ++int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst) ++{ ++ int i, ret; ++ u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) + SendDiSEqC), ++ 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ if (len > 10) ++ len = 10; ++ ++ buf[1] = len + 2; ++ buf[2] = len; ++ ++ if (burst != -1) ++ buf[3] = burst ? 0x01 : 0x00; ++ else ++ buf[3] = 0xffff; ++ ++ for (i = 0; i < len; i++) ++ buf[i + 4] = msg[i]; ++ ++ ret = av7110_send_fw_cmd(av7110, buf, 18); ++ if (ret && ret!=-ERESTARTSYS) ++ printk(KERN_ERR "dvb-ttpci: av7110_diseqc_send error %d\n", ret); ++ return ret; ++} ++ ++ ++#ifdef CONFIG_DVB_AV7110_OSD ++ ++static inline int SetColorBlend(struct av7110 *av7110, u8 windownr) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, SetCBlend, 1, windownr); ++} ++ ++static inline int SetBlend_(struct av7110 *av7110, u8 windownr, ++ enum av7110_osd_palette_type colordepth, u16 index, u8 blending) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, SetBlend, 4, ++ windownr, colordepth, index, blending); ++} ++ ++static inline int SetColor_(struct av7110 *av7110, u8 windownr, ++ enum av7110_osd_palette_type colordepth, u16 index, u16 colorhi, u16 colorlo) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, SetColor, 5, ++ windownr, colordepth, index, colorhi, colorlo); ++} ++ ++static inline int SetFont(struct av7110 *av7110, u8 windownr, u8 fontsize, ++ u16 colorfg, u16 colorbg) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Font, 4, ++ windownr, fontsize, colorfg, colorbg); ++} ++ ++static int FlushText(struct av7110 *av7110) ++{ ++ unsigned long start; ++ int err; ++ ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ return -ERESTARTSYS; ++ start = jiffies; ++ while (1) { ++ err = time_after(jiffies, start + ARM_WAIT_OSD); ++ if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) ++ break; ++ if (err) { ++ printk(KERN_ERR "dvb-ttpci: %s(): timeout waiting for BUFF1_BASE == 0\n", ++ __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++ mutex_unlock(&av7110->dcomlock); ++ return 0; ++} ++ ++static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) ++{ ++ int i, ret; ++ unsigned long start; ++ int length = strlen(buf) + 1; ++ u16 cbuf[5] = { (COMTYPE_OSD << 8) + DText, 3, win, x, y }; ++ ++ if (mutex_lock_interruptible(&av7110->dcomlock)) ++ return -ERESTARTSYS; ++ ++ start = jiffies; ++ while (1) { ++ ret = time_after(jiffies, start + ARM_WAIT_OSD); ++ if (rdebi(av7110, DEBINOSWAP, BUFF1_BASE, 0, 2) == 0) ++ break; ++ if (ret) { ++ printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for BUFF1_BASE == 0\n", ++ __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++#ifndef _NOHANDSHAKE ++ start = jiffies; ++ while (1) { ++ ret = time_after(jiffies, start + ARM_WAIT_SHAKE); ++ if (rdebi(av7110, DEBINOSWAP, HANDSHAKE_REG, 0, 2) == 0) ++ break; ++ if (ret) { ++ printk(KERN_ERR "dvb-ttpci: %s: timeout waiting for HANDSHAKE_REG\n", ++ __func__); ++ mutex_unlock(&av7110->dcomlock); ++ return -ETIMEDOUT; ++ } ++ msleep(1); ++ } ++#endif ++ for (i = 0; i < length / 2; i++) ++ wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, ++ swab16(*(u16 *)(buf + 2 * i)), 2); ++ if (length & 1) ++ wdebi(av7110, DEBINOSWAP, BUFF1_BASE + i * 2, 0, 2); ++ ret = __av7110_send_fw_cmd(av7110, cbuf, 5); ++ mutex_unlock(&av7110->dcomlock); ++ if (ret && ret!=-ERESTARTSYS) ++ printk(KERN_ERR "dvb-ttpci: WriteText error %d\n", ret); ++ return ret; ++} ++ ++static inline int DrawLine(struct av7110 *av7110, u8 windownr, ++ u16 x, u16 y, u16 dx, u16 dy, u16 color) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, DLine, 6, ++ windownr, x, y, dx, dy, color); ++} ++ ++static inline int DrawBlock(struct av7110 *av7110, u8 windownr, ++ u16 x, u16 y, u16 dx, u16 dy, u16 color) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, DBox, 6, ++ windownr, x, y, dx, dy, color); ++} ++ ++static inline int HideWindow(struct av7110 *av7110, u8 windownr) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, WHide, 1, windownr); ++} ++ ++static inline int MoveWindowRel(struct av7110 *av7110, u8 windownr, u16 x, u16 y) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveD, 3, windownr, x, y); ++} ++ ++static inline int MoveWindowAbs(struct av7110 *av7110, u8 windownr, u16 x, u16 y) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, WMoveA, 3, windownr, x, y); ++} ++ ++static inline int DestroyOSDWindow(struct av7110 *av7110, u8 windownr) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, WDestroy, 1, windownr); ++} ++ ++static inline int CreateOSDWindow(struct av7110 *av7110, u8 windownr, ++ osd_raw_window_t disptype, ++ u16 width, u16 height) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, WCreate, 4, ++ windownr, disptype, width, height); ++} ++ ++ ++static enum av7110_osd_palette_type bpp2pal[8] = { ++ Pal1Bit, Pal2Bit, 0, Pal4Bit, 0, 0, 0, Pal8Bit ++}; ++static osd_raw_window_t bpp2bit[8] = { ++ OSD_BITMAP1, OSD_BITMAP2, 0, OSD_BITMAP4, 0, 0, 0, OSD_BITMAP8 ++}; ++ ++static inline int WaitUntilBmpLoaded(struct av7110 *av7110) ++{ ++ int ret = wait_event_timeout(av7110->bmpq, ++ av7110->bmp_state != BMP_LOADING, 10*HZ); ++ if (ret == 0) { ++ printk("dvb-ttpci: warning: timeout waiting in LoadBitmap: %d, %d\n", ++ ret, av7110->bmp_state); ++ av7110->bmp_state = BMP_NONE; ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++static inline int LoadBitmap(struct av7110 *av7110, ++ u16 dx, u16 dy, int inc, u8 __user * data) ++{ ++ u16 format; ++ int bpp; ++ int i; ++ int d, delta; ++ u8 c; ++ int ret; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ format = bpp2bit[av7110->osdbpp[av7110->osdwin]]; ++ ++ av7110->bmp_state = BMP_LOADING; ++ if (format == OSD_BITMAP8) { ++ bpp=8; delta = 1; ++ } else if (format == OSD_BITMAP4) { ++ bpp=4; delta = 2; ++ } else if (format == OSD_BITMAP2) { ++ bpp=2; delta = 4; ++ } else if (format == OSD_BITMAP1) { ++ bpp=1; delta = 8; ++ } else { ++ av7110->bmp_state = BMP_NONE; ++ return -EINVAL; ++ } ++ av7110->bmplen = ((dx * dy * bpp + 7) & ~7) / 8; ++ av7110->bmpp = 0; ++ if (av7110->bmplen > 32768) { ++ av7110->bmp_state = BMP_NONE; ++ return -EINVAL; ++ } ++ for (i = 0; i < dy; i++) { ++ if (copy_from_user(av7110->bmpbuf + 1024 + i * dx, data + i * inc, dx)) { ++ av7110->bmp_state = BMP_NONE; ++ return -EINVAL; ++ } ++ } ++ if (format != OSD_BITMAP8) { ++ for (i = 0; i < dx * dy / delta; i++) { ++ c = ((u8 *)av7110->bmpbuf)[1024 + i * delta + delta - 1]; ++ for (d = delta - 2; d >= 0; d--) { ++ c |= (((u8 *)av7110->bmpbuf)[1024 + i * delta + d] ++ << ((delta - d - 1) * bpp)); ++ ((u8 *)av7110->bmpbuf)[1024 + i] = c; ++ } ++ } ++ } ++ av7110->bmplen += 1024; ++ dprintk(4, "av7110_fw_cmd: LoadBmp size %d\n", av7110->bmplen); ++ ret = av7110_fw_cmd(av7110, COMTYPE_OSD, LoadBmp, 3, format, dx, dy); ++ if (!ret) ++ ret = WaitUntilBmpLoaded(av7110); ++ return ret; ++} ++ ++static int BlitBitmap(struct av7110 *av7110, u16 x, u16 y) ++{ ++ dprintk(4, "%p\n", av7110); ++ ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, BlitBmp, 4, av7110->osdwin, x, y, 0); ++} ++ ++static inline int ReleaseBitmap(struct av7110 *av7110) ++{ ++ dprintk(4, "%p\n", av7110); ++ ++ if (av7110->bmp_state != BMP_LOADED && FW_VERSION(av7110->arm_app) < 0x261e) ++ return -1; ++ if (av7110->bmp_state == BMP_LOADING) ++ dprintk(1,"ReleaseBitmap called while BMP_LOADING\n"); ++ av7110->bmp_state = BMP_NONE; ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, ReleaseBmp, 0); ++} ++ ++static u32 RGB2YUV(u16 R, u16 G, u16 B) ++{ ++ u16 y, u, v; ++ u16 Y, Cr, Cb; ++ ++ y = R * 77 + G * 150 + B * 29; /* Luma=0.299R+0.587G+0.114B 0..65535 */ ++ u = 2048 + B * 8 -(y >> 5); /* Cr 0..4095 */ ++ v = 2048 + R * 8 -(y >> 5); /* Cb 0..4095 */ ++ ++ Y = y / 256; ++ Cb = u / 16; ++ Cr = v / 16; ++ ++ return Cr | (Cb << 16) | (Y << 8); ++} ++ ++static int OSDSetColor(struct av7110 *av7110, u8 color, u8 r, u8 g, u8 b, u8 blend) ++{ ++ int ret; ++ ++ u16 ch, cl; ++ u32 yuv; ++ ++ yuv = blend ? RGB2YUV(r,g,b) : 0; ++ cl = (yuv & 0xffff); ++ ch = ((yuv >> 16) & 0xffff); ++ ret = SetColor_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], ++ color, ch, cl); ++ if (!ret) ++ ret = SetBlend_(av7110, av7110->osdwin, bpp2pal[av7110->osdbpp[av7110->osdwin]], ++ color, ((blend >> 4) & 0x0f)); ++ return ret; ++} ++ ++static int OSDSetPalette(struct av7110 *av7110, u32 __user * colors, u8 first, u8 last) ++{ ++ int i; ++ int length = last - first + 1; ++ ++ if (length * 4 > DATA_BUFF3_SIZE) ++ return -EINVAL; ++ ++ for (i = 0; i < length; i++) { ++ u32 color, blend, yuv; ++ ++ if (get_user(color, colors + i)) ++ return -EFAULT; ++ blend = (color & 0xF0000000) >> 4; ++ yuv = blend ? RGB2YUV(color & 0xFF, (color >> 8) & 0xFF, ++ (color >> 16) & 0xFF) | blend : 0; ++ yuv = ((yuv & 0xFFFF0000) >> 16) | ((yuv & 0x0000FFFF) << 16); ++ wdebi(av7110, DEBINOSWAP, DATA_BUFF3_BASE + i * 4, yuv, 4); ++ } ++ return av7110_fw_cmd(av7110, COMTYPE_OSD, Set_Palette, 4, ++ av7110->osdwin, ++ bpp2pal[av7110->osdbpp[av7110->osdwin]], ++ first, last); ++} ++ ++static int OSDSetBlock(struct av7110 *av7110, int x0, int y0, ++ int x1, int y1, int inc, u8 __user * data) ++{ ++ uint w, h, bpp, bpl, size, lpb, bnum, brest; ++ int i; ++ int rc,release_rc; ++ ++ w = x1 - x0 + 1; ++ h = y1 - y0 + 1; ++ if (inc <= 0) ++ inc = w; ++ if (w <= 0 || w > 720 || h <= 0 || h > 576) ++ return -EINVAL; ++ bpp = av7110->osdbpp[av7110->osdwin] + 1; ++ bpl = ((w * bpp + 7) & ~7) / 8; ++ size = h * bpl; ++ lpb = (32 * 1024) / bpl; ++ bnum = size / (lpb * bpl); ++ brest = size - bnum * lpb * bpl; ++ ++ if (av7110->bmp_state == BMP_LOADING) { ++ /* possible if syscall is repeated by -ERESTARTSYS and if firmware cannot abort */ ++ BUG_ON (FW_VERSION(av7110->arm_app) >= 0x261e); ++ rc = WaitUntilBmpLoaded(av7110); ++ if (rc) ++ return rc; ++ /* just continue. This should work for all fw versions ++ * if bnum==1 && !brest && LoadBitmap was successful ++ */ ++ } ++ ++ rc = 0; ++ for (i = 0; i < bnum; i++) { ++ rc = LoadBitmap(av7110, w, lpb, inc, data); ++ if (rc) ++ break; ++ rc = BlitBitmap(av7110, x0, y0 + i * lpb); ++ if (rc) ++ break; ++ data += lpb * inc; ++ } ++ if (!rc && brest) { ++ rc = LoadBitmap(av7110, w, brest / bpl, inc, data); ++ if (!rc) ++ rc = BlitBitmap(av7110, x0, y0 + bnum * lpb); ++ } ++ release_rc = ReleaseBitmap(av7110); ++ if (!rc) ++ rc = release_rc; ++ if (rc) ++ dprintk(1,"returns %d\n",rc); ++ return rc; ++} ++ ++int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&av7110->osd_mutex)) ++ return -ERESTARTSYS; ++ ++ switch (dc->cmd) { ++ case OSD_Close: ++ ret = DestroyOSDWindow(av7110, av7110->osdwin); ++ break; ++ case OSD_Open: ++ av7110->osdbpp[av7110->osdwin] = (dc->color - 1) & 7; ++ ret = CreateOSDWindow(av7110, av7110->osdwin, ++ bpp2bit[av7110->osdbpp[av7110->osdwin]], ++ dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); ++ if (ret) ++ break; ++ if (!dc->data) { ++ ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); ++ if (ret) ++ break; ++ ret = SetColorBlend(av7110, av7110->osdwin); ++ } ++ break; ++ case OSD_Show: ++ ret = MoveWindowRel(av7110, av7110->osdwin, 0, 0); ++ break; ++ case OSD_Hide: ++ ret = HideWindow(av7110, av7110->osdwin); ++ break; ++ case OSD_Clear: ++ ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, 0); ++ break; ++ case OSD_Fill: ++ ret = DrawBlock(av7110, av7110->osdwin, 0, 0, 720, 576, dc->color); ++ break; ++ case OSD_SetColor: ++ ret = OSDSetColor(av7110, dc->color, dc->x0, dc->y0, dc->x1, dc->y1); ++ break; ++ case OSD_SetPalette: ++ if (FW_VERSION(av7110->arm_app) >= 0x2618) ++ ret = OSDSetPalette(av7110, dc->data, dc->color, dc->x0); ++ else { ++ int i, len = dc->x0-dc->color+1; ++ u8 __user *colors = (u8 __user *)dc->data; ++ u8 r, g = 0, b = 0, blend = 0; ++ ret = 0; ++ for (i = 0; icolor + i, r, g, b, blend); ++ if (ret) ++ break; ++ } ++ } ++ break; ++ case OSD_SetPixel: ++ ret = DrawLine(av7110, av7110->osdwin, ++ dc->x0, dc->y0, 0, 0, dc->color); ++ break; ++ case OSD_SetRow: ++ dc->y1 = dc->y0; ++ /* fall through */ ++ case OSD_SetBlock: ++ ret = OSDSetBlock(av7110, dc->x0, dc->y0, dc->x1, dc->y1, dc->color, dc->data); ++ break; ++ case OSD_FillRow: ++ ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, ++ dc->x1-dc->x0+1, dc->y1, dc->color); ++ break; ++ case OSD_FillBlock: ++ ret = DrawBlock(av7110, av7110->osdwin, dc->x0, dc->y0, ++ dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1, dc->color); ++ break; ++ case OSD_Line: ++ ret = DrawLine(av7110, av7110->osdwin, ++ dc->x0, dc->y0, dc->x1 - dc->x0, dc->y1 - dc->y0, dc->color); ++ break; ++ case OSD_Text: ++ { ++ char textbuf[240]; ++ ++ if (strncpy_from_user(textbuf, dc->data, 240) < 0) { ++ ret = -EFAULT; ++ break; ++ } ++ textbuf[239] = 0; ++ if (dc->x1 > 3) ++ dc->x1 = 3; ++ ret = SetFont(av7110, av7110->osdwin, dc->x1, ++ (u16) (dc->color & 0xffff), (u16) (dc->color >> 16)); ++ if (!ret) ++ ret = FlushText(av7110); ++ if (!ret) ++ ret = WriteText(av7110, av7110->osdwin, dc->x0, dc->y0, textbuf); ++ break; ++ } ++ case OSD_SetWindow: ++ if (dc->x0 < 1 || dc->x0 > 7) ++ ret = -EINVAL; ++ else { ++ av7110->osdwin = dc->x0; ++ ret = 0; ++ } ++ break; ++ case OSD_MoveWindow: ++ ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); ++ if (!ret) ++ ret = SetColorBlend(av7110, av7110->osdwin); ++ break; ++ case OSD_OpenRaw: ++ if (dc->color < OSD_BITMAP1 || dc->color > OSD_CURSOR) { ++ ret = -EINVAL; ++ break; ++ } ++ if (dc->color >= OSD_BITMAP1 && dc->color <= OSD_BITMAP8HR) ++ av7110->osdbpp[av7110->osdwin] = (1 << (dc->color & 3)) - 1; ++ else ++ av7110->osdbpp[av7110->osdwin] = 0; ++ ret = CreateOSDWindow(av7110, av7110->osdwin, (osd_raw_window_t)dc->color, ++ dc->x1 - dc->x0 + 1, dc->y1 - dc->y0 + 1); ++ if (ret) ++ break; ++ if (!dc->data) { ++ ret = MoveWindowAbs(av7110, av7110->osdwin, dc->x0, dc->y0); ++ if (!ret) ++ ret = SetColorBlend(av7110, av7110->osdwin); ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ mutex_unlock(&av7110->osd_mutex); ++ if (ret==-ERESTARTSYS) ++ dprintk(1, "av7110_osd_cmd(%d) returns with -ERESTARTSYS\n",dc->cmd); ++ else if (ret) ++ dprintk(1, "av7110_osd_cmd(%d) returns with %d\n",dc->cmd,ret); ++ ++ return ret; ++} ++ ++int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap) ++{ ++ switch (cap->cmd) { ++ case OSD_CAP_MEMSIZE: ++ if (FW_4M_SDRAM(av7110->arm_app)) ++ cap->val = 1000000; ++ else ++ cap->val = 92000; ++ return 0; ++ default: ++ return -EINVAL; ++ } ++} ++#endif /* CONFIG_DVB_AV7110_OSD */ +diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h +new file mode 100644 +index 0000000..1634aba +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_hw.h +@@ -0,0 +1,495 @@ ++#ifndef _AV7110_HW_H_ ++#define _AV7110_HW_H_ ++ ++#include "av7110.h" ++ ++/* DEBI transfer mode defs */ ++ ++#define DEBINOSWAP 0x000e0000 ++#define DEBISWAB 0x001e0000 ++#define DEBISWAP 0x002e0000 ++ ++#define ARM_WAIT_FREE (HZ) ++#define ARM_WAIT_SHAKE (HZ/5) ++#define ARM_WAIT_OSD (HZ) ++ ++ ++enum av7110_bootstate ++{ ++ BOOTSTATE_BUFFER_EMPTY = 0, ++ BOOTSTATE_BUFFER_FULL = 1, ++ BOOTSTATE_AV7110_BOOT_COMPLETE = 2 ++}; ++ ++enum av7110_type_rec_play_format ++{ RP_None, ++ AudioPES, ++ AudioMp2, ++ AudioPCM, ++ VideoPES, ++ AV_PES ++}; ++ ++enum av7110_osd_palette_type ++{ ++ NoPalet = 0, /* No palette */ ++ Pal1Bit = 2, /* 2 colors for 1 Bit Palette */ ++ Pal2Bit = 4, /* 4 colors for 2 bit palette */ ++ Pal4Bit = 16, /* 16 colors for 4 bit palette */ ++ Pal8Bit = 256 /* 256 colors for 16 bit palette */ ++}; ++ ++/* switch defines */ ++#define SB_GPIO 3 ++#define SB_OFF SAA7146_GPIO_OUTLO /* SlowBlank off (TV-Mode) */ ++#define SB_ON SAA7146_GPIO_INPUT /* SlowBlank on (AV-Mode) */ ++#define SB_WIDE SAA7146_GPIO_OUTHI /* SlowBlank 6V (16/9-Mode) (not implemented) */ ++ ++#define FB_GPIO 1 ++#define FB_OFF SAA7146_GPIO_LO /* FastBlank off (CVBS-Mode) */ ++#define FB_ON SAA7146_GPIO_OUTHI /* FastBlank on (RGB-Mode) */ ++#define FB_LOOP SAA7146_GPIO_INPUT /* FastBlank loop-through (PC graphics ???) */ ++ ++enum av7110_video_output_mode ++{ ++ NO_OUT = 0, /* disable analog output */ ++ CVBS_RGB_OUT = 1, ++ CVBS_YC_OUT = 2, ++ YC_OUT = 3 ++}; ++ ++/* firmware internal msg q status: */ ++#define GPMQFull 0x0001 /* Main Message Queue Full */ ++#define GPMQOver 0x0002 /* Main Message Queue Overflow */ ++#define HPQFull 0x0004 /* High Priority Msg Queue Full */ ++#define HPQOver 0x0008 ++#define OSDQFull 0x0010 /* OSD Queue Full */ ++#define OSDQOver 0x0020 ++#define GPMQBusy 0x0040 /* Queue not empty, FW >= 261d */ ++#define HPQBusy 0x0080 ++#define OSDQBusy 0x0100 ++ ++/* hw section filter flags */ ++#define SECTION_EIT 0x01 ++#define SECTION_SINGLE 0x00 ++#define SECTION_CYCLE 0x02 ++#define SECTION_CONTINUOS 0x04 ++#define SECTION_MODE 0x06 ++#define SECTION_IPMPE 0x0C /* size up to 4k */ ++#define SECTION_HIGH_SPEED 0x1C /* larger buffer */ ++#define DATA_PIPING_FLAG 0x20 /* for Data Piping Filter */ ++ ++#define PBUFSIZE_NONE 0x0000 ++#define PBUFSIZE_1P 0x0100 ++#define PBUFSIZE_2P 0x0200 ++#define PBUFSIZE_1K 0x0300 ++#define PBUFSIZE_2K 0x0400 ++#define PBUFSIZE_4K 0x0500 ++#define PBUFSIZE_8K 0x0600 ++#define PBUFSIZE_16K 0x0700 ++#define PBUFSIZE_32K 0x0800 ++ ++ ++/* firmware command codes */ ++enum av7110_osd_command { ++ WCreate, ++ WDestroy, ++ WMoveD, ++ WMoveA, ++ WHide, ++ WTop, ++ DBox, ++ DLine, ++ DText, ++ Set_Font, ++ SetColor, ++ SetBlend, ++ SetWBlend, ++ SetCBlend, ++ SetNonBlend, ++ LoadBmp, ++ BlitBmp, ++ ReleaseBmp, ++ SetWTrans, ++ SetWNoTrans, ++ Set_Palette ++}; ++ ++enum av7110_pid_command { ++ MultiPID, ++ VideoPID, ++ AudioPID, ++ InitFilt, ++ FiltError, ++ NewVersion, ++ CacheError, ++ AddPIDFilter, ++ DelPIDFilter, ++ Scan, ++ SetDescr, ++ SetIR, ++ FlushTSQueue ++}; ++ ++enum av7110_mpeg_command { ++ SelAudChannels ++}; ++ ++enum av7110_audio_command { ++ AudioDAC, ++ CabADAC, ++ ON22K, ++ OFF22K, ++ MainSwitch, ++ ADSwitch, ++ SendDiSEqC, ++ SetRegister, ++ SpdifSwitch ++}; ++ ++enum av7110_request_command { ++ AudioState, ++ AudioBuffState, ++ VideoState1, ++ VideoState2, ++ VideoState3, ++ CrashCounter, ++ ReqVersion, ++ ReqVCXO, ++ ReqRegister, ++ ReqSecFilterError, ++ ReqSTC ++}; ++ ++enum av7110_encoder_command { ++ SetVidMode, ++ SetTestMode, ++ LoadVidCode, ++ SetMonitorType, ++ SetPanScanType, ++ SetFreezeMode, ++ SetWSSConfig ++}; ++ ++enum av7110_rec_play_state { ++ __Record, ++ __Stop, ++ __Play, ++ __Pause, ++ __Slow, ++ __FF_IP, ++ __Scan_I, ++ __Continue ++}; ++ ++enum av7110_fw_cmd_misc { ++ AV7110_FW_VIDEO_ZOOM = 1, ++ AV7110_FW_VIDEO_COMMAND, ++ AV7110_FW_AUDIO_COMMAND ++}; ++ ++enum av7110_command_type { ++ COMTYPE_NOCOM, ++ COMTYPE_PIDFILTER, ++ COMTYPE_MPEGDECODER, ++ COMTYPE_OSD, ++ COMTYPE_BMP, ++ COMTYPE_ENCODER, ++ COMTYPE_AUDIODAC, ++ COMTYPE_REQUEST, ++ COMTYPE_SYSTEM, ++ COMTYPE_REC_PLAY, ++ COMTYPE_COMMON_IF, ++ COMTYPE_PID_FILTER, ++ COMTYPE_PES, ++ COMTYPE_TS, ++ COMTYPE_VIDEO, ++ COMTYPE_AUDIO, ++ COMTYPE_CI_LL, ++ COMTYPE_MISC = 0x80 ++}; ++ ++#define VID_NONE_PREF 0x00 /* No aspect ration processing preferred */ ++#define VID_PAN_SCAN_PREF 0x01 /* Pan and Scan Display preferred */ ++#define VID_VERT_COMP_PREF 0x02 /* Vertical compression display preferred */ ++#define VID_VC_AND_PS_PREF 0x03 /* PanScan and vertical Compression if allowed */ ++#define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ ++ ++/* MPEG video decoder commands */ ++#define AV_VIDEO_CMD_STOP 0x000e ++#define AV_VIDEO_CMD_PLAY 0x000d ++#define AV_VIDEO_CMD_FREEZE 0x0102 ++#define AV_VIDEO_CMD_FFWD 0x0016 ++#define AV_VIDEO_CMD_SLOW 0x0022 ++ ++/* MPEG audio decoder commands */ ++#define AUDIO_CMD_MUTE 0x0001 ++#define AUDIO_CMD_UNMUTE 0x0002 ++#define AUDIO_CMD_PCM16 0x0010 ++#define AUDIO_CMD_STEREO 0x0080 ++#define AUDIO_CMD_MONO_L 0x0100 ++#define AUDIO_CMD_MONO_R 0x0200 ++#define AUDIO_CMD_SYNC_OFF 0x000e ++#define AUDIO_CMD_SYNC_ON 0x000f ++ ++/* firmware data interface codes */ ++#define DATA_NONE 0x00 ++#define DATA_FSECTION 0x01 ++#define DATA_IPMPE 0x02 ++#define DATA_MPEG_RECORD 0x03 ++#define DATA_DEBUG_MESSAGE 0x04 ++#define DATA_COMMON_INTERFACE 0x05 ++#define DATA_MPEG_PLAY 0x06 ++#define DATA_BMP_LOAD 0x07 ++#define DATA_IRCOMMAND 0x08 ++#define DATA_PIPING 0x09 ++#define DATA_STREAMING 0x0a ++#define DATA_CI_GET 0x0b ++#define DATA_CI_PUT 0x0c ++#define DATA_MPEG_VIDEO_EVENT 0x0d ++ ++#define DATA_PES_RECORD 0x10 ++#define DATA_PES_PLAY 0x11 ++#define DATA_TS_RECORD 0x12 ++#define DATA_TS_PLAY 0x13 ++ ++/* ancient CI command codes, only two are actually still used ++ * by the link level CI firmware */ ++#define CI_CMD_ERROR 0x00 ++#define CI_CMD_ACK 0x01 ++#define CI_CMD_SYSTEM_READY 0x02 ++#define CI_CMD_KEYPRESS 0x03 ++#define CI_CMD_ON_TUNED 0x04 ++#define CI_CMD_ON_SWITCH_PROGRAM 0x05 ++#define CI_CMD_SECTION_ARRIVED 0x06 ++#define CI_CMD_SECTION_TIMEOUT 0x07 ++#define CI_CMD_TIME 0x08 ++#define CI_CMD_ENTER_MENU 0x09 ++#define CI_CMD_FAST_PSI 0x0a ++#define CI_CMD_GET_SLOT_INFO 0x0b ++ ++#define CI_MSG_NONE 0x00 ++#define CI_MSG_CI_INFO 0x01 ++#define CI_MSG_MENU 0x02 ++#define CI_MSG_LIST 0x03 ++#define CI_MSG_TEXT 0x04 ++#define CI_MSG_REQUEST_INPUT 0x05 ++#define CI_MSG_INPUT_COMPLETE 0x06 ++#define CI_MSG_LIST_MORE 0x07 ++#define CI_MSG_MENU_MORE 0x08 ++#define CI_MSG_CLOSE_MMI_IMM 0x09 ++#define CI_MSG_SECTION_REQUEST 0x0a ++#define CI_MSG_CLOSE_FILTER 0x0b ++#define CI_PSI_COMPLETE 0x0c ++#define CI_MODULE_READY 0x0d ++#define CI_SWITCH_PRG_REPLY 0x0e ++#define CI_MSG_TEXT_MORE 0x0f ++ ++#define CI_MSG_CA_PMT 0xe0 ++#define CI_MSG_ERROR 0xf0 ++ ++ ++/* base address of the dual ported RAM which serves as communication ++ * area between PCI bus and av7110, ++ * as seen by the DEBI bus of the saa7146 */ ++#define DPRAM_BASE 0x4000 ++ ++/* boot protocol area */ ++#define AV7110_BOOT_STATE (DPRAM_BASE + 0x3F8) ++#define AV7110_BOOT_SIZE (DPRAM_BASE + 0x3FA) ++#define AV7110_BOOT_BASE (DPRAM_BASE + 0x3FC) ++#define AV7110_BOOT_BLOCK (DPRAM_BASE + 0x400) ++#define AV7110_BOOT_MAX_SIZE 0xc00 ++ ++/* firmware command protocol area */ ++#define IRQ_STATE (DPRAM_BASE + 0x0F4) ++#define IRQ_STATE_EXT (DPRAM_BASE + 0x0F6) ++#define MSGSTATE (DPRAM_BASE + 0x0F8) ++#define COMMAND (DPRAM_BASE + 0x0FC) ++#define COM_BUFF (DPRAM_BASE + 0x100) ++#define COM_BUFF_SIZE 0x20 ++ ++/* various data buffers */ ++#define BUFF1_BASE (DPRAM_BASE + 0x120) ++#define BUFF1_SIZE 0xE0 ++ ++#define DATA_BUFF0_BASE (DPRAM_BASE + 0x200) ++#define DATA_BUFF0_SIZE 0x0800 ++ ++#define DATA_BUFF1_BASE (DATA_BUFF0_BASE+DATA_BUFF0_SIZE) ++#define DATA_BUFF1_SIZE 0x0800 ++ ++#define DATA_BUFF2_BASE (DATA_BUFF1_BASE+DATA_BUFF1_SIZE) ++#define DATA_BUFF2_SIZE 0x0800 ++ ++#define DATA_BUFF3_BASE (DATA_BUFF2_BASE+DATA_BUFF2_SIZE) ++#define DATA_BUFF3_SIZE 0x0400 ++ ++#define Reserved (DPRAM_BASE + 0x1E00) ++#define Reserved_SIZE 0x1C0 ++ ++ ++/* firmware status area */ ++#define STATUS_BASE (DPRAM_BASE + 0x1FC0) ++#define STATUS_LOOPS (STATUS_BASE + 0x08) ++ ++#define STATUS_MPEG_WIDTH (STATUS_BASE + 0x0C) ++/* ((aspect_ratio & 0xf) << 12) | (height & 0xfff) */ ++#define STATUS_MPEG_HEIGHT_AR (STATUS_BASE + 0x0E) ++ ++/* firmware data protocol area */ ++#define RX_TYPE (DPRAM_BASE + 0x1FE8) ++#define RX_LEN (DPRAM_BASE + 0x1FEA) ++#define TX_TYPE (DPRAM_BASE + 0x1FEC) ++#define TX_LEN (DPRAM_BASE + 0x1FEE) ++ ++#define RX_BUFF (DPRAM_BASE + 0x1FF4) ++#define TX_BUFF (DPRAM_BASE + 0x1FF6) ++ ++#define HANDSHAKE_REG (DPRAM_BASE + 0x1FF8) ++#define COM_IF_LOCK (DPRAM_BASE + 0x1FFA) ++ ++#define IRQ_RX (DPRAM_BASE + 0x1FFC) ++#define IRQ_TX (DPRAM_BASE + 0x1FFE) ++ ++/* used by boot protocol to load firmware into av7110 DRAM */ ++#define DRAM_START_CODE 0x2e000404 ++#define DRAM_MAX_CODE_SIZE 0x00100000 ++ ++/* saa7146 gpio lines */ ++#define RESET_LINE 2 ++#define DEBI_DONE_LINE 1 ++#define ARM_IRQ_LINE 0 ++ ++ ++ ++extern int av7110_bootarm(struct av7110 *av7110); ++extern int av7110_firmversion(struct av7110 *av7110); ++#define FW_CI_LL_SUPPORT(arm_app) ((arm_app) & 0x80000000) ++#define FW_4M_SDRAM(arm_app) ((arm_app) & 0x40000000) ++#define FW_VERSION(arm_app) ((arm_app) & 0x0000FFFF) ++ ++extern int av7110_wait_msgstate(struct av7110 *av7110, u16 flags); ++extern int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...); ++extern int av7110_fw_request(struct av7110 *av7110, u16 *request_buf, ++ int request_buf_len, u16 *reply_buf, int reply_buf_len); ++ ++ ++/* DEBI (saa7146 data extension bus interface) access */ ++extern int av7110_debiwrite(struct av7110 *av7110, u32 config, ++ int addr, u32 val, int count); ++extern u32 av7110_debiread(struct av7110 *av7110, u32 config, ++ int addr, int count); ++ ++ ++/* DEBI during interrupt */ ++/* single word writes */ ++static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) ++{ ++ av7110_debiwrite(av7110, config, addr, val, count); ++} ++ ++/* buffer writes */ ++static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, ++ const u8 *val, int count) ++{ ++ memcpy(av7110->debi_virt, val, count); ++ av7110_debiwrite(av7110, config, addr, 0, count); ++} ++ ++static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) ++{ ++ u32 res; ++ ++ res=av7110_debiread(av7110, config, addr, count); ++ if (count<=4) ++ memcpy(av7110->debi_virt, (char *) &res, count); ++ return res; ++} ++ ++/* DEBI outside interrupts, only for count <= 4! */ ++static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&av7110->debilock, flags); ++ av7110_debiwrite(av7110, config, addr, val, count); ++ spin_unlock_irqrestore(&av7110->debilock, flags); ++} ++ ++static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count) ++{ ++ unsigned long flags; ++ u32 res; ++ ++ spin_lock_irqsave(&av7110->debilock, flags); ++ res=av7110_debiread(av7110, config, addr, count); ++ spin_unlock_irqrestore(&av7110->debilock, flags); ++ return res; ++} ++ ++/* handle mailbox registers of the dual ported RAM */ ++static inline void ARM_ResetMailBox(struct av7110 *av7110) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&av7110->debilock, flags); ++ av7110_debiread(av7110, DEBINOSWAP, IRQ_RX, 2); ++ av7110_debiwrite(av7110, DEBINOSWAP, IRQ_RX, 0, 2); ++ spin_unlock_irqrestore(&av7110->debilock, flags); ++} ++ ++static inline void ARM_ClearMailBox(struct av7110 *av7110) ++{ ++ iwdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); ++} ++ ++static inline void ARM_ClearIrq(struct av7110 *av7110) ++{ ++ irdebi(av7110, DEBINOSWAP, IRQ_RX, 0, 2); ++} ++ ++/**************************************************************************** ++ * Firmware commands ++ ****************************************************************************/ ++ ++static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, AudioDAC, 2, addr, data); ++} ++ ++static inline int av7710_set_video_mode(struct av7110 *av7110, int mode) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode); ++} ++ ++static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_VIDEO_COMMAND, 4, ++ (com>>16), (com&0xffff), ++ (arg>>16), (arg&0xffff)); ++} ++ ++static inline int audcom(struct av7110 *av7110, u32 com) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_MISC, AV7110_FW_AUDIO_COMMAND, 2, ++ (com>>16), (com&0xffff)); ++} ++ ++static inline int Set22K(struct av7110 *av7110, int state) ++{ ++ return av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, (state ? ON22K : OFF22K), 0); ++} ++ ++ ++extern int av7110_diseqc_send(struct av7110 *av7110, int len, u8 *msg, unsigned long burst); ++ ++ ++#ifdef CONFIG_DVB_AV7110_OSD ++extern int av7110_osd_cmd(struct av7110 *av7110, osd_cmd_t *dc); ++extern int av7110_osd_capability(struct av7110 *av7110, osd_cap_t *cap); ++#endif /* CONFIG_DVB_AV7110_OSD */ ++ ++ ++ ++#endif /* _AV7110_HW_H_ */ +diff --git a/drivers/media/pci/ttpci/av7110_ipack.c b/drivers/media/pci/ttpci/av7110_ipack.c +new file mode 100644 +index 0000000..699ef8b +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_ipack.c +@@ -0,0 +1,403 @@ ++#include "dvb_filter.h" ++#include "av7110_ipack.h" ++#include /* for memcpy() */ ++#include ++ ++ ++void av7110_ipack_reset(struct ipack *p) ++{ ++ p->found = 0; ++ p->cid = 0; ++ p->plength = 0; ++ p->flag1 = 0; ++ p->flag2 = 0; ++ p->hlength = 0; ++ p->mpeg = 0; ++ p->check = 0; ++ p->which = 0; ++ p->done = 0; ++ p->count = 0; ++} ++ ++ ++int av7110_ipack_init(struct ipack *p, int size, ++ void (*func)(u8 *buf, int size, void *priv)) ++{ ++ if (!(p->buf = vmalloc(size*sizeof(u8)))) { ++ printk(KERN_WARNING "Couldn't allocate memory for ipack\n"); ++ return -ENOMEM; ++ } ++ p->size = size; ++ p->func = func; ++ p->repack_subids = 0; ++ av7110_ipack_reset(p); ++ return 0; ++} ++ ++ ++void av7110_ipack_free(struct ipack *p) ++{ ++ vfree(p->buf); ++} ++ ++ ++static void send_ipack(struct ipack *p) ++{ ++ int off; ++ struct dvb_audio_info ai; ++ int ac3_off = 0; ++ int streamid = 0; ++ int nframes = 0; ++ int f = 0; ++ ++ switch (p->mpeg) { ++ case 2: ++ if (p->count < 10) ++ return; ++ p->buf[3] = p->cid; ++ p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); ++ p->buf[5] = (u8)((p->count - 6) & 0x00ff); ++ if (p->repack_subids && p->cid == PRIVATE_STREAM1) { ++ off = 9 + p->buf[8]; ++ streamid = p->buf[off]; ++ if ((streamid & 0xf8) == 0x80) { ++ ai.off = 0; ++ ac3_off = ((p->buf[off + 2] << 8)| ++ p->buf[off + 3]); ++ if (ac3_off < p->count) ++ f = dvb_filter_get_ac3info(p->buf + off + 3 + ac3_off, ++ p->count - ac3_off, &ai, 0); ++ if (!f) { ++ nframes = (p->count - off - 3 - ac3_off) / ++ ai.framesize + 1; ++ p->buf[off + 2] = (ac3_off >> 8) & 0xff; ++ p->buf[off + 3] = (ac3_off) & 0xff; ++ p->buf[off + 1] = nframes; ++ ac3_off += nframes * ai.framesize - p->count; ++ } ++ } ++ } ++ p->func(p->buf, p->count, p->data); ++ ++ p->buf[6] = 0x80; ++ p->buf[7] = 0x00; ++ p->buf[8] = 0x00; ++ p->count = 9; ++ if (p->repack_subids && p->cid == PRIVATE_STREAM1 ++ && (streamid & 0xf8) == 0x80) { ++ p->count += 4; ++ p->buf[9] = streamid; ++ p->buf[10] = (ac3_off >> 8) & 0xff; ++ p->buf[11] = (ac3_off) & 0xff; ++ p->buf[12] = 0; ++ } ++ break; ++ ++ case 1: ++ if (p->count < 8) ++ return; ++ p->buf[3] = p->cid; ++ p->buf[4] = (u8)(((p->count - 6) & 0xff00) >> 8); ++ p->buf[5] = (u8)((p->count - 6) & 0x00ff); ++ p->func(p->buf, p->count, p->data); ++ ++ p->buf[6] = 0x0f; ++ p->count = 7; ++ break; ++ } ++} ++ ++ ++void av7110_ipack_flush(struct ipack *p) ++{ ++ if (p->plength != MMAX_PLENGTH - 6 || p->found <= 6) ++ return; ++ p->plength = p->found - 6; ++ p->found = 0; ++ send_ipack(p); ++ av7110_ipack_reset(p); ++} ++ ++ ++static void write_ipack(struct ipack *p, const u8 *data, int count) ++{ ++ u8 headr[3] = { 0x00, 0x00, 0x01 }; ++ ++ if (p->count < 6) { ++ memcpy(p->buf, headr, 3); ++ p->count = 6; ++ } ++ ++ if (p->count + count < p->size){ ++ memcpy(p->buf+p->count, data, count); ++ p->count += count; ++ } else { ++ int rest = p->size - p->count; ++ memcpy(p->buf+p->count, data, rest); ++ p->count += rest; ++ send_ipack(p); ++ if (count - rest > 0) ++ write_ipack(p, data + rest, count - rest); ++ } ++} ++ ++ ++int av7110_ipack_instant_repack (const u8 *buf, int count, struct ipack *p) ++{ ++ int l; ++ int c = 0; ++ ++ while (c < count && (p->mpeg == 0 || ++ (p->mpeg == 1 && p->found < 7) || ++ (p->mpeg == 2 && p->found < 9)) ++ && (p->found < 5 || !p->done)) { ++ switch (p->found) { ++ case 0: ++ case 1: ++ if (buf[c] == 0x00) ++ p->found++; ++ else ++ p->found = 0; ++ c++; ++ break; ++ case 2: ++ if (buf[c] == 0x01) ++ p->found++; ++ else if (buf[c] == 0) ++ p->found = 2; ++ else ++ p->found = 0; ++ c++; ++ break; ++ case 3: ++ p->cid = 0; ++ switch (buf[c]) { ++ case PROG_STREAM_MAP: ++ case PRIVATE_STREAM2: ++ case PROG_STREAM_DIR: ++ case ECM_STREAM : ++ case EMM_STREAM : ++ case PADDING_STREAM : ++ case DSM_CC_STREAM : ++ case ISO13522_STREAM: ++ p->done = 1; ++ /* fall through */ ++ case PRIVATE_STREAM1: ++ case VIDEO_STREAM_S ... VIDEO_STREAM_E: ++ case AUDIO_STREAM_S ... AUDIO_STREAM_E: ++ p->found++; ++ p->cid = buf[c]; ++ c++; ++ break; ++ default: ++ p->found = 0; ++ break; ++ } ++ break; ++ ++ case 4: ++ if (count-c > 1) { ++ p->plen[0] = buf[c]; ++ c++; ++ p->plen[1] = buf[c]; ++ c++; ++ p->found += 2; ++ p->plength = (p->plen[0] << 8) | p->plen[1]; ++ } else { ++ p->plen[0] = buf[c]; ++ p->found++; ++ return count; ++ } ++ break; ++ case 5: ++ p->plen[1] = buf[c]; ++ c++; ++ p->found++; ++ p->plength = (p->plen[0] << 8) | p->plen[1]; ++ break; ++ case 6: ++ if (!p->done) { ++ p->flag1 = buf[c]; ++ c++; ++ p->found++; ++ if ((p->flag1 & 0xc0) == 0x80) ++ p->mpeg = 2; ++ else { ++ p->hlength = 0; ++ p->which = 0; ++ p->mpeg = 1; ++ p->flag2 = 0; ++ } ++ } ++ break; ++ ++ case 7: ++ if (!p->done && p->mpeg == 2) { ++ p->flag2 = buf[c]; ++ c++; ++ p->found++; ++ } ++ break; ++ ++ case 8: ++ if (!p->done && p->mpeg == 2) { ++ p->hlength = buf[c]; ++ c++; ++ p->found++; ++ } ++ break; ++ } ++ } ++ ++ if (c == count) ++ return count; ++ ++ if (!p->plength) ++ p->plength = MMAX_PLENGTH - 6; ++ ++ if (p->done || ((p->mpeg == 2 && p->found >= 9) || ++ (p->mpeg == 1 && p->found >= 7))) { ++ switch (p->cid) { ++ case AUDIO_STREAM_S ... AUDIO_STREAM_E: ++ case VIDEO_STREAM_S ... VIDEO_STREAM_E: ++ case PRIVATE_STREAM1: ++ if (p->mpeg == 2 && p->found == 9) { ++ write_ipack(p, &p->flag1, 1); ++ write_ipack(p, &p->flag2, 1); ++ write_ipack(p, &p->hlength, 1); ++ } ++ ++ if (p->mpeg == 1 && p->found == 7) ++ write_ipack(p, &p->flag1, 1); ++ ++ if (p->mpeg == 2 && (p->flag2 & PTS_ONLY) && ++ p->found < 14) { ++ while (c < count && p->found < 14) { ++ p->pts[p->found - 9] = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ } ++ if (c == count) ++ return count; ++ } ++ ++ if (p->mpeg == 1 && p->which < 2000) { ++ ++ if (p->found == 7) { ++ p->check = p->flag1; ++ p->hlength = 1; ++ } ++ ++ while (!p->which && c < count && ++ p->check == 0xff){ ++ p->check = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->hlength++; ++ } ++ ++ if (c == count) ++ return count; ++ ++ if ((p->check & 0xc0) == 0x40 && !p->which) { ++ p->check = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->hlength++; ++ ++ p->which = 1; ++ if (c == count) ++ return count; ++ p->check = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->hlength++; ++ p->which = 2; ++ if (c == count) ++ return count; ++ } ++ ++ if (p->which == 1) { ++ p->check = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->hlength++; ++ p->which = 2; ++ if (c == count) ++ return count; ++ } ++ ++ if ((p->check & 0x30) && p->check != 0xff) { ++ p->flag2 = (p->check & 0xf0) << 2; ++ p->pts[0] = p->check; ++ p->which = 3; ++ } ++ ++ if (c == count) ++ return count; ++ if (p->which > 2){ ++ if ((p->flag2 & PTS_DTS_FLAGS) == PTS_ONLY) { ++ while (c < count && p->which < 7) { ++ p->pts[p->which - 2] = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->which++; ++ p->hlength++; ++ } ++ if (c == count) ++ return count; ++ } else if ((p->flag2 & PTS_DTS_FLAGS) == PTS_DTS) { ++ while (c < count && p->which < 12) { ++ if (p->which < 7) ++ p->pts[p->which - 2] = buf[c]; ++ write_ipack(p, buf + c, 1); ++ c++; ++ p->found++; ++ p->which++; ++ p->hlength++; ++ } ++ if (c == count) ++ return count; ++ } ++ p->which = 2000; ++ } ++ ++ } ++ ++ while (c < count && p->found < p->plength + 6) { ++ l = count - c; ++ if (l + p->found > p->plength + 6) ++ l = p->plength + 6 - p->found; ++ write_ipack(p, buf + c, l); ++ p->found += l; ++ c += l; ++ } ++ break; ++ } ++ ++ ++ if (p->done) { ++ if (p->found + count - c < p->plength + 6) { ++ p->found += count - c; ++ c = count; ++ } else { ++ c += p->plength + 6 - p->found; ++ p->found = p->plength + 6; ++ } ++ } ++ ++ if (p->plength && p->found == p->plength + 6) { ++ send_ipack(p); ++ av7110_ipack_reset(p); ++ if (c < count) ++ av7110_ipack_instant_repack(buf + c, count - c, p); ++ } ++ } ++ return count; ++} +diff --git a/drivers/media/pci/ttpci/av7110_ipack.h b/drivers/media/pci/ttpci/av7110_ipack.h +new file mode 100644 +index 0000000..becf94d +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_ipack.h +@@ -0,0 +1,12 @@ ++#ifndef _AV7110_IPACK_H_ ++#define _AV7110_IPACK_H_ ++ ++extern int av7110_ipack_init(struct ipack *p, int size, ++ void (*func)(u8 *buf, int size, void *priv)); ++ ++extern void av7110_ipack_reset(struct ipack *p); ++extern int av7110_ipack_instant_repack(const u8 *buf, int count, struct ipack *p); ++extern void av7110_ipack_free(struct ipack * p); ++extern void av7110_ipack_flush(struct ipack *p); ++ ++#endif +diff --git a/drivers/media/pci/ttpci/av7110_ir.c b/drivers/media/pci/ttpci/av7110_ir.c +new file mode 100644 +index 0000000..908f272 +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_ir.c +@@ -0,0 +1,415 @@ ++/* ++ * Driver for the remote control of SAA7146 based AV7110 cards ++ * ++ * Copyright (C) 1999-2003 Holger Waechtler ++ * Copyright (C) 2003-2007 Oliver Endriss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++ ++ ++#define AV_CNT 4 ++ ++#define IR_RC5 0 ++#define IR_RCMM 1 ++#define IR_RC5_EXT 2 /* internal only */ ++ ++#define IR_ALL 0xffffffff ++ ++#define UP_TIMEOUT (HZ*7/25) ++ ++ ++/* Note: enable ir debugging by or'ing debug with 16 */ ++ ++static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; ++module_param_array(ir_protocol, int, NULL, 0644); ++MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); ++ ++static int ir_inversion[AV_CNT]; ++module_param_array(ir_inversion, int, NULL, 0644); ++MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); ++ ++static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; ++module_param_array(ir_device_mask, uint, NULL, 0644); ++MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); ++ ++ ++static int av_cnt; ++static struct av7110 *av_list[AV_CNT]; ++ ++static u16 default_key_map [256] = { ++ KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, ++ KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, ++ KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, ++ 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, ++ KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, ++ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, ++ KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, ++ 0, 0, 0, 0, KEY_EPG, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR ++}; ++ ++ ++/* key-up timer */ ++static void av7110_emit_keyup(unsigned long parm) ++{ ++ struct infrared *ir = (struct infrared *) parm; ++ ++ if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) ++ return; ++ ++ input_report_key(ir->input_dev, ir->last_key, 0); ++ input_sync(ir->input_dev); ++} ++ ++ ++/* tasklet */ ++static void av7110_emit_key(unsigned long parm) ++{ ++ struct infrared *ir = (struct infrared *) parm; ++ u32 ircom = ir->ir_command; ++ u8 data; ++ u8 addr; ++ u16 toggle; ++ u16 keycode; ++ ++ /* extract device address and data */ ++ switch (ir->protocol) { ++ case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ ++ data = ircom & 0x3f; ++ addr = (ircom >> 6) & 0x1f; ++ toggle = ircom & 0x0800; ++ break; ++ ++ case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ ++ data = ircom & 0xff; ++ addr = (ircom >> 8) & 0x1f; ++ toggle = ircom & 0x8000; ++ break; ++ ++ case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ ++ data = ircom & 0x3f; ++ addr = (ircom >> 6) & 0x1f; ++ /* invert 7th data bit for backward compatibility with RC5 keymaps */ ++ if (!(ircom & 0x1000)) ++ data |= 0x40; ++ toggle = ircom & 0x0800; ++ break; ++ ++ default: ++ printk("%s invalid protocol %x\n", __func__, ir->protocol); ++ return; ++ } ++ ++ input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); ++ input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); ++ ++ keycode = ir->key_map[data]; ++ ++ dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", ++ __func__, ircom, addr, data, keycode); ++ ++ /* check device address */ ++ if (!(ir->device_mask & (1 << addr))) ++ return; ++ ++ if (!keycode) { ++ printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", ++ __func__, ircom, addr, data); ++ return; ++ } ++ ++ if (timer_pending(&ir->keyup_timer)) { ++ del_timer(&ir->keyup_timer); ++ if (ir->last_key != keycode || toggle != ir->last_toggle) { ++ ir->delay_timer_finished = 0; ++ input_event(ir->input_dev, EV_KEY, ir->last_key, 0); ++ input_event(ir->input_dev, EV_KEY, keycode, 1); ++ input_sync(ir->input_dev); ++ } else if (ir->delay_timer_finished) { ++ input_event(ir->input_dev, EV_KEY, keycode, 2); ++ input_sync(ir->input_dev); ++ } ++ } else { ++ ir->delay_timer_finished = 0; ++ input_event(ir->input_dev, EV_KEY, keycode, 1); ++ input_sync(ir->input_dev); ++ } ++ ++ ir->last_key = keycode; ++ ir->last_toggle = toggle; ++ ++ ir->keyup_timer.expires = jiffies + UP_TIMEOUT; ++ add_timer(&ir->keyup_timer); ++ ++} ++ ++ ++/* register with input layer */ ++static void input_register_keys(struct infrared *ir) ++{ ++ int i; ++ ++ set_bit(EV_KEY, ir->input_dev->evbit); ++ set_bit(EV_REP, ir->input_dev->evbit); ++ set_bit(EV_MSC, ir->input_dev->evbit); ++ ++ set_bit(MSC_RAW, ir->input_dev->mscbit); ++ set_bit(MSC_SCAN, ir->input_dev->mscbit); ++ ++ memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); ++ ++ for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { ++ if (ir->key_map[i] > KEY_MAX) ++ ir->key_map[i] = 0; ++ else if (ir->key_map[i] > KEY_RESERVED) ++ set_bit(ir->key_map[i], ir->input_dev->keybit); ++ } ++ ++ ir->input_dev->keycode = ir->key_map; ++ ir->input_dev->keycodesize = sizeof(ir->key_map[0]); ++ ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); ++} ++ ++ ++/* called by the input driver after rep[REP_DELAY] ms */ ++static void input_repeat_key(unsigned long parm) ++{ ++ struct infrared *ir = (struct infrared *) parm; ++ ++ ir->delay_timer_finished = 1; ++} ++ ++ ++/* check for configuration changes */ ++int av7110_check_ir_config(struct av7110 *av7110, int force) ++{ ++ int i; ++ int modified = force; ++ int ret = -ENODEV; ++ ++ for (i = 0; i < av_cnt; i++) ++ if (av7110 == av_list[i]) ++ break; ++ ++ if (i < av_cnt && av7110) { ++ if ((av7110->ir.protocol & 1) != ir_protocol[i] || ++ av7110->ir.inversion != ir_inversion[i]) ++ modified = true; ++ ++ if (modified) { ++ /* protocol */ ++ if (ir_protocol[i]) { ++ ir_protocol[i] = 1; ++ av7110->ir.protocol = IR_RCMM; ++ av7110->ir.ir_config = 0x0001; ++ } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { ++ av7110->ir.protocol = IR_RC5_EXT; ++ av7110->ir.ir_config = 0x0002; ++ } else { ++ av7110->ir.protocol = IR_RC5; ++ av7110->ir.ir_config = 0x0000; ++ } ++ /* inversion */ ++ if (ir_inversion[i]) { ++ ir_inversion[i] = 1; ++ av7110->ir.ir_config |= 0x8000; ++ } ++ av7110->ir.inversion = ir_inversion[i]; ++ /* update ARM */ ++ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ++ av7110->ir.ir_config); ++ } else ++ ret = 0; ++ ++ /* address */ ++ if (av7110->ir.device_mask != ir_device_mask[i]) ++ av7110->ir.device_mask = ir_device_mask[i]; ++ } ++ ++ return ret; ++} ++ ++ ++/* /proc/av7110_ir interface */ ++static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *pos) ++{ ++ char *page; ++ u32 ir_config; ++ int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; ++ int i; ++ ++ if (count < size) ++ return -EINVAL; ++ ++ page = vmalloc(size); ++ if (!page) ++ return -ENOMEM; ++ ++ if (copy_from_user(page, buffer, size)) { ++ vfree(page); ++ return -EFAULT; ++ } ++ ++ memcpy(&ir_config, page, sizeof ir_config); ++ ++ for (i = 0; i < av_cnt; i++) { ++ /* keymap */ ++ memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, ++ sizeof(av_list[i]->ir.key_map)); ++ /* protocol, inversion, address */ ++ ir_protocol[i] = ir_config & 0x0001; ++ ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; ++ if (ir_config & 0x4000) ++ ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); ++ else ++ ir_device_mask[i] = IR_ALL; ++ /* update configuration */ ++ av7110_check_ir_config(av_list[i], false); ++ input_register_keys(&av_list[i]->ir); ++ } ++ vfree(page); ++ return count; ++} ++ ++static const struct file_operations av7110_ir_proc_fops = { ++ .owner = THIS_MODULE, ++ .write = av7110_ir_proc_write, ++ .llseek = noop_llseek, ++}; ++ ++/* interrupt handler */ ++static void ir_handler(struct av7110 *av7110, u32 ircom) ++{ ++ dprintk(4, "ir command = %08x\n", ircom); ++ av7110->ir.ir_command = ircom; ++ tasklet_schedule(&av7110->ir.ir_tasklet); ++} ++ ++ ++int __devinit av7110_ir_init(struct av7110 *av7110) ++{ ++ struct input_dev *input_dev; ++ static struct proc_dir_entry *e; ++ int err; ++ ++ if (av_cnt >= ARRAY_SIZE(av_list)) ++ return -ENOSPC; ++ ++ av_list[av_cnt++] = av7110; ++ av7110_check_ir_config(av7110, true); ++ ++ init_timer(&av7110->ir.keyup_timer); ++ av7110->ir.keyup_timer.function = av7110_emit_keyup; ++ av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ av7110->ir.input_dev = input_dev; ++ snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), ++ "pci-%s/ir0", pci_name(av7110->dev->pci)); ++ ++ input_dev->name = "DVB on-card IR receiver"; ++ ++ input_dev->phys = av7110->ir.input_phys; ++ input_dev->id.bustype = BUS_PCI; ++ input_dev->id.version = 2; ++ if (av7110->dev->pci->subsystem_vendor) { ++ input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; ++ input_dev->id.product = av7110->dev->pci->subsystem_device; ++ } else { ++ input_dev->id.vendor = av7110->dev->pci->vendor; ++ input_dev->id.product = av7110->dev->pci->device; ++ } ++ input_dev->dev.parent = &av7110->dev->pci->dev; ++ /* initial keymap */ ++ memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); ++ input_register_keys(&av7110->ir); ++ err = input_register_device(input_dev); ++ if (err) { ++ input_free_device(input_dev); ++ return err; ++ } ++ input_dev->timer.function = input_repeat_key; ++ input_dev->timer.data = (unsigned long) &av7110->ir; ++ ++ if (av_cnt == 1) { ++ e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); ++ if (e) ++ e->size = 4 + 256 * sizeof(u16); ++ } ++ ++ tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); ++ av7110->ir.ir_handler = ir_handler; ++ ++ return 0; ++} ++ ++ ++void __devexit av7110_ir_exit(struct av7110 *av7110) ++{ ++ int i; ++ ++ if (av_cnt == 0) ++ return; ++ ++ del_timer_sync(&av7110->ir.keyup_timer); ++ av7110->ir.ir_handler = NULL; ++ tasklet_kill(&av7110->ir.ir_tasklet); ++ ++ for (i = 0; i < av_cnt; i++) ++ if (av_list[i] == av7110) { ++ av_list[i] = av_list[av_cnt-1]; ++ av_list[av_cnt-1] = NULL; ++ break; ++ } ++ ++ if (av_cnt == 1) ++ remove_proc_entry("av7110_ir", NULL); ++ ++ input_unregister_device(av7110->ir.input_dev); ++ ++ av_cnt--; ++} ++ ++//MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); ++//MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/ttpci/av7110_v4l.c b/drivers/media/pci/ttpci/av7110_v4l.c +new file mode 100644 +index 0000000..712931d +--- /dev/null ++++ b/drivers/media/pci/ttpci/av7110_v4l.c +@@ -0,0 +1,967 @@ ++/* ++ * av7110_v4l.c: av7110 video4linux interface for DVB and Siemens DVB-C analog module ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * originally based on code by: ++ * Copyright (C) 1998,1999 Christian Theiss ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++#include "av7110_av.h" ++ ++int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val) ++{ ++ u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff }; ++ struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg }; ++ ++ switch (av7110->adac_type) { ++ case DVB_ADAC_MSP34x0: ++ msgs.addr = 0x40; ++ break; ++ case DVB_ADAC_MSP34x5: ++ msgs.addr = 0x42; ++ break; ++ default: ++ return 0; ++ } ++ ++ if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) { ++ dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n", ++ av7110->dvb_adapter.num, reg, val); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val) ++{ ++ u8 msg1[3] = { dev, reg >> 8, reg & 0xff }; ++ u8 msg2[2]; ++ struct i2c_msg msgs[2] = { ++ { .flags = 0 , .len = 3, .buf = msg1 }, ++ { .flags = I2C_M_RD, .len = 2, .buf = msg2 } ++ }; ++ ++ switch (av7110->adac_type) { ++ case DVB_ADAC_MSP34x0: ++ msgs[0].addr = 0x40; ++ msgs[1].addr = 0x40; ++ break; ++ case DVB_ADAC_MSP34x5: ++ msgs[0].addr = 0x42; ++ msgs[1].addr = 0x42; ++ break; ++ default: ++ return 0; ++ } ++ ++ if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) { ++ dprintk(1, "dvb-ttpci: failed @ card %d, %u\n", ++ av7110->dvb_adapter.num, reg); ++ return -EIO; ++ } ++ *val = (msg2[0] << 8) | msg2[1]; ++ return 0; ++} ++ ++static struct v4l2_input inputs[4] = { ++ { ++ .index = 0, ++ .name = "DVB", ++ .type = V4L2_INPUT_TYPE_CAMERA, ++ .audioset = 1, ++ .tuner = 0, /* ignored */ ++ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, ++ .status = 0, ++ .capabilities = V4L2_IN_CAP_STD, ++ }, { ++ .index = 1, ++ .name = "Television", ++ .type = V4L2_INPUT_TYPE_TUNER, ++ .audioset = 1, ++ .tuner = 0, ++ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, ++ .status = 0, ++ .capabilities = V4L2_IN_CAP_STD, ++ }, { ++ .index = 2, ++ .name = "Video", ++ .type = V4L2_INPUT_TYPE_CAMERA, ++ .audioset = 0, ++ .tuner = 0, ++ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, ++ .status = 0, ++ .capabilities = V4L2_IN_CAP_STD, ++ }, { ++ .index = 3, ++ .name = "Y/C", ++ .type = V4L2_INPUT_TYPE_CAMERA, ++ .audioset = 0, ++ .tuner = 0, ++ .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, ++ .status = 0, ++ .capabilities = V4L2_IN_CAP_STD, ++ } ++}; ++ ++static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) ++{ ++ struct av7110 *av7110 = dev->ext_priv; ++ u8 buf[] = { 0x00, reg, data }; ++ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 3 }; ++ ++ dprintk(4, "dev: %p\n", dev); ++ ++ if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) ++ return -1; ++ return 0; ++} ++ ++static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) ++{ ++ struct av7110 *av7110 = dev->ext_priv; ++ struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; ++ ++ dprintk(4, "dev: %p\n", dev); ++ ++ if (1 != i2c_transfer(&av7110->i2c_adap, &msg, 1)) ++ return -1; ++ return 0; ++} ++ ++static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) ++{ ++ u32 div; ++ u8 config; ++ u8 buf[4]; ++ ++ dprintk(4, "freq: 0x%08x\n", freq); ++ ++ /* magic number: 614. tuning with the frequency given by v4l2 ++ is always off by 614*62.5 = 38375 kHz...*/ ++ div = freq + 614; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x8e; ++ ++ if (freq < (u32) (16 * 168.25)) ++ config = 0xa0; ++ else if (freq < (u32) (16 * 447.25)) ++ config = 0x90; ++ else ++ config = 0x30; ++ config &= ~0x02; ++ ++ buf[3] = config; ++ ++ return tuner_write(dev, 0x61, buf); ++} ++ ++static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) ++{ ++ struct av7110 *av7110 = (struct av7110*)dev->ext_priv; ++ u32 div; ++ u8 data[4]; ++ ++ div = (freq + 38900000 + 31250) / 62500; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0xce; ++ ++ if (freq < 45000000) ++ return -EINVAL; ++ else if (freq < 137000000) ++ data[3] = 0x01; ++ else if (freq < 403000000) ++ data[3] = 0x02; ++ else if (freq < 860000000) ++ data[3] = 0x04; ++ else ++ return -EINVAL; ++ ++ if (av7110->fe->ops.i2c_gate_ctrl) ++ av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); ++ return tuner_write(dev, 0x63, data); ++} ++ ++ ++ ++static struct saa7146_standard analog_standard[]; ++static struct saa7146_standard dvb_standard[]; ++static struct saa7146_standard standard[]; ++ ++static struct v4l2_audio msp3400_v4l2_audio = { ++ .index = 0, ++ .name = "Television", ++ .capability = V4L2_AUDCAP_STEREO ++}; ++ ++static int av7110_dvb_c_switch(struct saa7146_fh *fh) ++{ ++ struct saa7146_dev *dev = fh->dev; ++ struct saa7146_vv *vv = dev->vv_data; ++ struct av7110 *av7110 = (struct av7110*)dev->ext_priv; ++ u16 adswitch; ++ int source, sync, err; ++ ++ dprintk(4, "%p\n", av7110); ++ ++ 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 (err != 0) { ++ dprintk(2, "suspending video failed\n"); ++ vv->ov_suspend = NULL; ++ } ++ } ++ ++ if (0 != av7110->current_input) { ++ dprintk(1, "switching to analog TV:\n"); ++ adswitch = 1; ++ source = SAA7146_HPS_SOURCE_PORT_B; ++ sync = SAA7146_HPS_SYNC_PORT_B; ++ memcpy(standard, analog_standard, sizeof(struct saa7146_standard) * 2); ++ ++ switch (av7110->current_input) { ++ case 1: ++ dprintk(1, "switching SAA7113 to Analog Tuner Input\n"); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0000); // loudspeaker source ++ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0000); // headphone source ++ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0000); // SCART 1 source ++ msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone ++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume ++ ++ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { ++ if (ves1820_writereg(dev, 0x09, 0x0f, 0x60)) ++ dprintk(1, "setting band in demodulator failed\n"); ++ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); // TDA9819 pin9(STD) ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); // TDA9819 pin30(VIF) ++ } ++ if (i2c_writereg(av7110, 0x48, 0x02, 0xd0) != 1) ++ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); ++ break; ++ case 2: ++ dprintk(1, "switching SAA7113 to Video AV CVBS Input\n"); ++ if (i2c_writereg(av7110, 0x48, 0x02, 0xd2) != 1) ++ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); ++ break; ++ case 3: ++ dprintk(1, "switching SAA7113 to Video AV Y/C Input\n"); ++ if (i2c_writereg(av7110, 0x48, 0x02, 0xd9) != 1) ++ dprintk(1, "saa7113 write failed @ card %d", av7110->dvb_adapter.num); ++ break; ++ default: ++ dprintk(1, "switching SAA7113 to Input: AV7110: SAA7113: invalid input\n"); ++ } ++ } else { ++ adswitch = 0; ++ source = SAA7146_HPS_SOURCE_PORT_A; ++ sync = SAA7146_HPS_SYNC_PORT_A; ++ memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); ++ dprintk(1, "switching DVB mode\n"); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source ++ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source ++ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source ++ msp_writereg(av7110, MSP_WR_DSP, 0x000e, 0x3000); // FM matrix, mono ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone ++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume ++ ++ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { ++ if (ves1820_writereg(dev, 0x09, 0x0f, 0x20)) ++ dprintk(1, "setting band in demodulator failed\n"); ++ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) ++ } ++ } ++ ++ /* hmm, this does not do anything!? */ ++ if (av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, adswitch)) ++ dprintk(1, "ADSwitch error\n"); ++ ++ saa7146_set_hps_source_and_sync(dev, source, sync); ++ ++ if (vv->ov_suspend != NULL) { ++ saa7146_start_preview(vv->ov_suspend); ++ vv->ov_suspend = NULL; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ u16 stereo_det; ++ s8 stereo; ++ ++ dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index); ++ ++ if (!av7110->analog_tuner_flags || t->index != 0) ++ return -EINVAL; ++ ++ memset(t, 0, sizeof(*t)); ++ strcpy((char *)t->name, "Television"); ++ ++ t->type = V4L2_TUNER_ANALOG_TV; ++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | ++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; ++ t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */ ++ t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */ ++ /* FIXME: add the real signal strength here */ ++ t->signal = 0xffff; ++ t->afc = 0; ++ ++ /* FIXME: standard / stereo detection is still broken */ ++ msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det); ++ dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det); ++ msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det); ++ dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det); ++ stereo = (s8)(stereo_det >> 8); ++ if (stereo > 0x10) { ++ /* stereo */ ++ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO; ++ t->audmode = V4L2_TUNER_MODE_STEREO; ++ } else if (stereo < -0x10) { ++ /* bilingual */ ++ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; ++ t->audmode = V4L2_TUNER_MODE_LANG1; ++ } else /* mono */ ++ t->rxsubchans = V4L2_TUNER_SUB_MONO; ++ ++ return 0; ++} ++ ++static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ u16 fm_matrix, src; ++ dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index); ++ ++ if (!av7110->analog_tuner_flags || av7110->current_input != 1) ++ return -EINVAL; ++ ++ switch (t->audmode) { ++ case V4L2_TUNER_MODE_STEREO: ++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n"); ++ fm_matrix = 0x3001; /* stereo */ ++ src = 0x0020; ++ break; ++ case V4L2_TUNER_MODE_LANG1_LANG2: ++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n"); ++ fm_matrix = 0x3000; /* bilingual */ ++ src = 0x0020; ++ break; ++ case V4L2_TUNER_MODE_LANG1: ++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n"); ++ fm_matrix = 0x3000; /* mono */ ++ src = 0x0000; ++ break; ++ case V4L2_TUNER_MODE_LANG2: ++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n"); ++ fm_matrix = 0x3000; /* mono */ ++ src = 0x0010; ++ break; ++ default: /* case V4L2_TUNER_MODE_MONO: */ ++ dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n"); ++ fm_matrix = 0x3000; /* mono */ ++ src = 0x0030; ++ break; ++ } ++ msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, src); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0009, src); ++ msp_writereg(av7110, MSP_WR_DSP, 0x000a, src); ++ return 0; ++} ++ ++static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x\n", f->frequency); ++ ++ if (!av7110->analog_tuner_flags || av7110->current_input != 1) ++ return -EINVAL; ++ ++ memset(f, 0, sizeof(*f)); ++ f->type = V4L2_TUNER_ANALOG_TV; ++ f->frequency = av7110->current_freq; ++ return 0; ++} ++ ++static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x\n", f->frequency); ++ ++ if (!av7110->analog_tuner_flags || av7110->current_input != 1) ++ return -EINVAL; ++ ++ if (V4L2_TUNER_ANALOG_TV != f->type) ++ return -EINVAL; ++ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0); ++ ++ /* tune in desired frequency */ ++ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) ++ ves1820_set_tv_freq(dev, f->frequency); ++ else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) ++ stv0297_set_tv_freq(dev, f->frequency); ++ av7110->current_freq = f->frequency; ++ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */ ++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */ ++ return 0; ++} ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index); ++ ++ if (av7110->analog_tuner_flags) { ++ if (i->index >= 4) ++ return -EINVAL; ++ } else { ++ if (i->index != 0) ++ return -EINVAL; ++ } ++ ++ memcpy(i, &inputs[i->index], sizeof(struct v4l2_input)); ++ ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ *input = av7110->current_input; ++ dprintk(2, "VIDIOC_G_INPUT: %d\n", *input); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_S_INPUT: %d\n", input); ++ ++ if (!av7110->analog_tuner_flags) ++ return input ? -EINVAL : 0; ++ ++ if (input >= 4) ++ return -EINVAL; ++ ++ av7110->current_input = input; ++ return av7110_dvb_c_switch(fh); ++} ++ ++static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) ++{ ++ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); ++ if (a->index != 0) ++ return -EINVAL; ++ *a = msp3400_v4l2_audio; ++ return 0; ++} ++ ++static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); ++ if (a->index != 0) ++ return -EINVAL; ++ if (av7110->current_input >= 2) ++ return -EINVAL; ++ *a = msp3400_v4l2_audio; ++ return 0; ++} ++ ++static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); ++ if (av7110->current_input >= 2) ++ return -EINVAL; ++ return a->index ? -EINVAL : 0; ++} ++ ++static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, ++ struct v4l2_sliced_vbi_cap *cap) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n"); ++ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) ++ return -EINVAL; ++ if (FW_VERSION(av7110->arm_app) >= 0x2623) { ++ cap->service_set = V4L2_SLICED_WSS_625; ++ cap->service_lines[0][23] = V4L2_SLICED_WSS_625; ++ } ++ return 0; ++} ++ ++static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_G_FMT:\n"); ++ if (FW_VERSION(av7110->arm_app) < 0x2623) ++ return -EINVAL; ++ memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced); ++ if (av7110->wssMode) { ++ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; ++ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; ++ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); ++ } ++ return 0; ++} ++ ++static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; ++ ++ dprintk(2, "VIDIOC_S_FMT\n"); ++ if (FW_VERSION(av7110->arm_app) < 0x2623) ++ return -EINVAL; ++ if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 && ++ f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) { ++ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); ++ /* WSS controlled by firmware */ ++ av7110->wssMode = 0; ++ av7110->wssData = 0; ++ return av7110_fw_cmd(av7110, COMTYPE_ENCODER, ++ SetWSSConfig, 1, 0); ++ } else { ++ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced)); ++ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625; ++ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; ++ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data); ++ /* WSS controlled by userspace */ ++ av7110->wssMode = 1; ++ av7110->wssData = 0; ++ } ++ return 0; ++} ++ ++static int av7110_vbi_reset(struct file *file) ++{ ++ struct saa7146_fh *fh = file->private_data; ++ struct saa7146_dev *dev = fh->dev; ++ struct av7110 *av7110 = (struct av7110*) dev->ext_priv; ++ ++ dprintk(2, "%s\n", __func__); ++ av7110->wssMode = 0; ++ av7110->wssData = 0; ++ if (FW_VERSION(av7110->arm_app) < 0x2623) ++ return 0; ++ else ++ return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 1, 0); ++} ++ ++static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) ++{ ++ struct saa7146_fh *fh = file->private_data; ++ struct saa7146_dev *dev = fh->dev; ++ struct av7110 *av7110 = (struct av7110*) dev->ext_priv; ++ struct v4l2_sliced_vbi_data d; ++ int rc; ++ ++ dprintk(2, "%s\n", __func__); ++ if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof d) ++ return -EINVAL; ++ if (copy_from_user(&d, data, count)) ++ return -EFAULT; ++ if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23) ++ return -EINVAL; ++ if (d.id) ++ av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0]; ++ else ++ av7110->wssData = 0x8000; ++ rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData); ++ return (rc < 0) ? rc : count; ++} ++ ++/**************************************************************************** ++ * INITIALIZATION ++ ****************************************************************************/ ++ ++static u8 saa7113_init_regs[] = { ++ 0x02, 0xd0, ++ 0x03, 0x23, ++ 0x04, 0x00, ++ 0x05, 0x00, ++ 0x06, 0xe9, ++ 0x07, 0x0d, ++ 0x08, 0x98, ++ 0x09, 0x02, ++ 0x0a, 0x80, ++ 0x0b, 0x40, ++ 0x0c, 0x40, ++ 0x0d, 0x00, ++ 0x0e, 0x01, ++ 0x0f, 0x7c, ++ 0x10, 0x48, ++ 0x11, 0x0c, ++ 0x12, 0x8b, ++ 0x13, 0x1a, ++ 0x14, 0x00, ++ 0x15, 0x00, ++ 0x16, 0x00, ++ 0x17, 0x00, ++ 0x18, 0x00, ++ 0x19, 0x00, ++ 0x1a, 0x00, ++ 0x1b, 0x00, ++ 0x1c, 0x00, ++ 0x1d, 0x00, ++ 0x1e, 0x00, ++ ++ 0x41, 0x77, ++ 0x42, 0x77, ++ 0x43, 0x77, ++ 0x44, 0x77, ++ 0x45, 0x77, ++ 0x46, 0x77, ++ 0x47, 0x77, ++ 0x48, 0x77, ++ 0x49, 0x77, ++ 0x4a, 0x77, ++ 0x4b, 0x77, ++ 0x4c, 0x77, ++ 0x4d, 0x77, ++ 0x4e, 0x77, ++ 0x4f, 0x77, ++ 0x50, 0x77, ++ 0x51, 0x77, ++ 0x52, 0x77, ++ 0x53, 0x77, ++ 0x54, 0x77, ++ 0x55, 0x77, ++ 0x56, 0x77, ++ 0x57, 0xff, ++ ++ 0xff ++}; ++ ++ ++static struct saa7146_ext_vv av7110_vv_data_st; ++static struct saa7146_ext_vv av7110_vv_data_c; ++ ++int av7110_init_analog_module(struct av7110 *av7110) ++{ ++ u16 version1, version2; ++ ++ if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 && ++ i2c_writereg(av7110, 0x80, 0x0, 0) == 1) { ++ pr_info("DVB-C analog module @ card %d detected, initializing MSP3400\n", ++ av7110->dvb_adapter.num); ++ av7110->adac_type = DVB_ADAC_MSP34x0; ++ } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 && ++ i2c_writereg(av7110, 0x84, 0x0, 0) == 1) { ++ pr_info("DVB-C analog module @ card %d detected, initializing MSP3415\n", ++ av7110->dvb_adapter.num); ++ av7110->adac_type = DVB_ADAC_MSP34x5; ++ } else ++ return -ENODEV; ++ ++ msleep(100); // the probing above resets the msp... ++ msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1); ++ msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2); ++ dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n", ++ av7110->dvb_adapter.num, version1, version2); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00); ++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone ++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220); // loudspeaker source ++ msp_writereg(av7110, MSP_WR_DSP, 0x0009, 0x0220); // headphone source ++ msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume ++ msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source ++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume ++ msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART ++ ++ if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) { ++ pr_info("saa7113 not accessible\n"); ++ } else { ++ u8 *i = saa7113_init_regs; ++ ++ if ((av7110->dev->pci->subsystem_vendor == 0x110a) && (av7110->dev->pci->subsystem_device == 0x0000)) { ++ /* Fujitsu/Siemens DVB-Cable */ ++ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; ++ } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x0002)) { ++ /* Hauppauge/TT DVB-C premium */ ++ av7110->analog_tuner_flags |= ANALOG_TUNER_VES1820; ++ } else if ((av7110->dev->pci->subsystem_vendor == 0x13c2) && (av7110->dev->pci->subsystem_device == 0x000A)) { ++ /* Hauppauge/TT DVB-C premium */ ++ av7110->analog_tuner_flags |= ANALOG_TUNER_STV0297; ++ } ++ ++ /* setup for DVB by default */ ++ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) { ++ if (ves1820_writereg(av7110->dev, 0x09, 0x0f, 0x20)) ++ dprintk(1, "setting band in demodulator failed\n"); ++ } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) { ++ saa7146_setgpio(av7110->dev, 1, SAA7146_GPIO_OUTLO); // TDA9819 pin9(STD) ++ saa7146_setgpio(av7110->dev, 3, SAA7146_GPIO_OUTLO); // TDA9819 pin30(VIF) ++ } ++ ++ /* init the saa7113 */ ++ while (*i != 0xff) { ++ if (i2c_writereg(av7110, 0x48, i[0], i[1]) != 1) { ++ dprintk(1, "saa7113 initialization failed @ card %d", av7110->dvb_adapter.num); ++ break; ++ } ++ i += 2; ++ } ++ /* setup msp for analog sound: B/G Dual-FM */ ++ msp_writereg(av7110, MSP_WR_DEM, 0x00bb, 0x02d0); // AD_CV ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 3); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 18); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 27); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 48); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 66); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0001, 72); // FIR1 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 4); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 64); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 0); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 3); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 18); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 27); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 48); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 66); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0005, 72); // FIR2 ++ msp_writereg(av7110, MSP_WR_DEM, 0x0083, 0xa000); // MODE_REG ++ msp_writereg(av7110, MSP_WR_DEM, 0x0093, 0x00aa); // DCO1_LO 5.74MHz ++ msp_writereg(av7110, MSP_WR_DEM, 0x009b, 0x04fc); // DCO1_HI ++ msp_writereg(av7110, MSP_WR_DEM, 0x00a3, 0x038e); // DCO2_LO 5.5MHz ++ msp_writereg(av7110, MSP_WR_DEM, 0x00ab, 0x04c6); // DCO2_HI ++ msp_writereg(av7110, MSP_WR_DEM, 0x0056, 0); // LOAD_REG 1/2 ++ } ++ ++ memcpy(standard, dvb_standard, sizeof(struct saa7146_standard) * 2); ++ /* set dd1 stream a & b */ ++ saa7146_write(av7110->dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(av7110->dev, DD1_INIT, 0x03000700); ++ saa7146_write(av7110->dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ return 0; ++} ++ ++int av7110_init_v4l(struct av7110 *av7110) ++{ ++ struct saa7146_dev* dev = av7110->dev; ++ struct saa7146_ext_vv *vv_data; ++ int ret; ++ ++ /* special case DVB-C: these cards have an analog tuner ++ plus need some special handling, so we have separate ++ saa7146_ext_vv data for these... */ ++ if (av7110->analog_tuner_flags) ++ vv_data = &av7110_vv_data_c; ++ else ++ vv_data = &av7110_vv_data_st; ++ ret = saa7146_vv_init(dev, vv_data); ++ ++ if (ret) { ++ ERR("cannot init capture device. skipping\n"); ++ return -ENODEV; ++ } ++ vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; ++ vv_data->vid_ops.vidioc_g_input = vidioc_g_input; ++ vv_data->vid_ops.vidioc_s_input = vidioc_s_input; ++ vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; ++ vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; ++ vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; ++ vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; ++ vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; ++ vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; ++ vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; ++ vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; ++ ++ vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; ++ vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; ++ vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; ++ vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; ++ vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; ++ vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; ++ vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; ++ vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; ++ ++ if (FW_VERSION(av7110->arm_app) < 0x2623) ++ vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; ++ ++ if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { ++ ERR("cannot register capture device. skipping\n"); ++ saa7146_vv_release(dev); ++ return -ENODEV; ++ } ++ if (FW_VERSION(av7110->arm_app) >= 0x2623) { ++ if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) ++ ERR("cannot register vbi v4l2 device. skipping\n"); ++ } ++ return 0; ++} ++ ++int av7110_exit_v4l(struct av7110 *av7110) ++{ ++ struct saa7146_dev* dev = av7110->dev; ++ ++ saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); ++ saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); ++ ++ saa7146_vv_release(dev); ++ ++ return 0; ++} ++ ++ ++ ++/* FIXME: these values are experimental values that look better than the ++ values from the latest "official" driver -- at least for me... (MiHu) */ ++static struct saa7146_standard standard[] = { ++ { ++ .name = "PAL", .id = V4L2_STD_PAL_BG, ++ .v_offset = 0x15, .v_field = 288, ++ .h_offset = 0x48, .h_pixels = 708, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 0x10, .v_field = 244, ++ .h_offset = 0x40, .h_pixels = 708, ++ .v_max_out = 480, .h_max_out = 640, ++ } ++}; ++ ++static struct saa7146_standard analog_standard[] = { ++ { ++ .name = "PAL", .id = V4L2_STD_PAL_BG, ++ .v_offset = 0x1b, .v_field = 288, ++ .h_offset = 0x08, .h_pixels = 708, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 0x10, .v_field = 244, ++ .h_offset = 0x40, .h_pixels = 708, ++ .v_max_out = 480, .h_max_out = 640, ++ } ++}; ++ ++static struct saa7146_standard dvb_standard[] = { ++ { ++ .name = "PAL", .id = V4L2_STD_PAL_BG, ++ .v_offset = 0x14, .v_field = 288, ++ .h_offset = 0x48, .h_pixels = 708, ++ .v_max_out = 576, .h_max_out = 768, ++ }, { ++ .name = "NTSC", .id = V4L2_STD_NTSC, ++ .v_offset = 0x10, .v_field = 244, ++ .h_offset = 0x40, .h_pixels = 708, ++ .v_max_out = 480, .h_max_out = 640, ++ } ++}; ++ ++static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) ++{ ++ struct av7110 *av7110 = (struct av7110*) dev->ext_priv; ++ ++ if (std->id & V4L2_STD_PAL) { ++ av7110->vidmode = AV7110_VIDEO_MODE_PAL; ++ av7110_set_vidmode(av7110, av7110->vidmode); ++ } ++ else if (std->id & V4L2_STD_NTSC) { ++ av7110->vidmode = AV7110_VIDEO_MODE_NTSC; ++ av7110_set_vidmode(av7110, av7110->vidmode); ++ } ++ else ++ return -1; ++ ++ return 0; ++} ++ ++ ++static struct saa7146_ext_vv av7110_vv_data_st = { ++ .inputs = 1, ++ .audios = 1, ++ .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, ++ .flags = 0, ++ ++ .stds = &standard[0], ++ .num_stds = ARRAY_SIZE(standard), ++ .std_callback = &std_callback, ++ ++ .vbi_fops.open = av7110_vbi_reset, ++ .vbi_fops.release = av7110_vbi_reset, ++ .vbi_fops.write = av7110_vbi_write, ++}; ++ ++static struct saa7146_ext_vv av7110_vv_data_c = { ++ .inputs = 1, ++ .audios = 1, ++ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, ++ .flags = SAA7146_USE_PORT_B_FOR_VBI, ++ ++ .stds = &standard[0], ++ .num_stds = ARRAY_SIZE(standard), ++ .std_callback = &std_callback, ++ ++ .vbi_fops.open = av7110_vbi_reset, ++ .vbi_fops.release = av7110_vbi_reset, ++ .vbi_fops.write = av7110_vbi_write, ++}; ++ +diff --git a/drivers/media/pci/ttpci/budget-av.c b/drivers/media/pci/ttpci/budget-av.c +new file mode 100644 +index 0000000..417f37a +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget-av.c +@@ -0,0 +1,1641 @@ ++/* ++ * budget-av.c: driver for the SAA7146 based Budget DVB cards ++ * with analog video in ++ * ++ * Compiled from various sources by Michael Hunold ++ * ++ * CI interface support (c) 2004 Olivier Gournet & ++ * Andrew de Quincey ++ * ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#undef pr_fmt ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include "budget.h" ++#include "stv0299.h" ++#include "stb0899_drv.h" ++#include "stb0899_reg.h" ++#include "stb0899_cfg.h" ++#include "tda8261.h" ++#include "tda8261_cfg.h" ++#include "tda1002x.h" ++#include "tda1004x.h" ++#include "tua6100.h" ++#include "dvb-pll.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dvb_ca_en50221.h" ++ ++#define DEBICICAM 0x02420000 ++ ++#define SLOTSTATUS_NONE 1 ++#define SLOTSTATUS_PRESENT 2 ++#define SLOTSTATUS_RESET 4 ++#define SLOTSTATUS_READY 8 ++#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++struct budget_av { ++ struct budget budget; ++ struct video_device *vd; ++ int cur_input; ++ int has_saa7113; ++ struct tasklet_struct ciintf_irq_tasklet; ++ int slot_status; ++ struct dvb_ca_en50221 ca; ++ u8 reinitialise_demod:1; ++}; ++ ++static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); ++ ++ ++/* GPIO Connections: ++ * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! ++ * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory ++ * 2 - CI Card Enable (Active Low) ++ * 3 - CI Card Detect ++ */ ++ ++/**************************************************************************** ++ * INITIALIZATION ++ ****************************************************************************/ ++ ++static u8 i2c_readreg(struct i2c_adapter *i2c, u8 id, u8 reg) ++{ ++ u8 mm1[] = { 0x00 }; ++ u8 mm2[] = { 0x00 }; ++ struct i2c_msg msgs[2]; ++ ++ msgs[0].flags = 0; ++ msgs[1].flags = I2C_M_RD; ++ msgs[0].addr = msgs[1].addr = id / 2; ++ mm1[0] = reg; ++ msgs[0].len = 1; ++ msgs[1].len = 1; ++ msgs[0].buf = mm1; ++ msgs[1].buf = mm2; ++ ++ i2c_transfer(i2c, msgs, 2); ++ ++ return mm2[0]; ++} ++ ++static int i2c_readregs(struct i2c_adapter *i2c, u8 id, u8 reg, u8 * buf, u8 len) ++{ ++ u8 mm1[] = { reg }; ++ struct i2c_msg msgs[2] = { ++ {.addr = id / 2,.flags = 0,.buf = mm1,.len = 1}, ++ {.addr = id / 2,.flags = I2C_M_RD,.buf = buf,.len = len} ++ }; ++ ++ if (i2c_transfer(i2c, msgs, 2) != 2) ++ return -EIO; ++ ++ return 0; ++} ++ ++static int i2c_writereg(struct i2c_adapter *i2c, u8 id, u8 reg, u8 val) ++{ ++ u8 msg[2] = { reg, val }; ++ struct i2c_msg msgs; ++ ++ msgs.flags = 0; ++ msgs.addr = id / 2; ++ msgs.len = 2; ++ msgs.buf = msg; ++ return i2c_transfer(i2c, &msgs, 1); ++} ++ ++static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ int result; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); ++ udelay(1); ++ ++ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 0xfff, 1, 0, 1); ++ if (result == -ETIMEDOUT) { ++ ciintf_slot_shutdown(ca, slot); ++ pr_info("cam ejected 1\n"); ++ } ++ return result; ++} ++ ++static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ int result; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTHI); ++ udelay(1); ++ ++ result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 0xfff, 1, value, 0, 1); ++ if (result == -ETIMEDOUT) { ++ ciintf_slot_shutdown(ca, slot); ++ pr_info("cam ejected 2\n"); ++ } ++ return result; ++} ++ ++static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ int result; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); ++ udelay(1); ++ ++ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, address & 3, 1, 0, 0); ++ if (result == -ETIMEDOUT) { ++ ciintf_slot_shutdown(ca, slot); ++ pr_info("cam ejected 3\n"); ++ return -ETIMEDOUT; ++ } ++ return result; ++} ++ ++static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ int result; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); ++ udelay(1); ++ ++ result = ttpci_budget_debiwrite(&budget_av->budget, DEBICICAM, address & 3, 1, value, 0, 0); ++ if (result == -ETIMEDOUT) { ++ ciintf_slot_shutdown(ca, slot); ++ pr_info("cam ejected 5\n"); ++ } ++ return result; ++} ++ ++static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ dprintk(1, "ciintf_slot_reset\n"); ++ budget_av->slot_status = SLOTSTATUS_RESET; ++ ++ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTHI); /* disable card */ ++ ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); /* Vcc off */ ++ msleep(2); ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); /* Vcc on */ ++ msleep(20); /* 20 ms Vcc settling time */ ++ ++ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); /* enable card */ ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); ++ msleep(20); ++ ++ /* reinitialise the frontend if necessary */ ++ if (budget_av->reinitialise_demod) ++ dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); ++ ++ return 0; ++} ++ ++static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ dprintk(1, "ciintf_slot_shutdown\n"); ++ ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); ++ budget_av->slot_status = SLOTSTATUS_NONE; ++ ++ return 0; ++} ++ ++static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ dprintk(1, "ciintf_slot_ts_enable: %d\n", budget_av->slot_status); ++ ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); ++ ++ return 0; ++} ++ ++static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) ++{ ++ struct budget_av *budget_av = (struct budget_av *) ca->data; ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ int result; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ /* test the card detect line - needs to be done carefully ++ * since it never goes high for some CAMs on this interface (e.g. topuptv) */ ++ if (budget_av->slot_status == SLOTSTATUS_NONE) { ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); ++ udelay(1); ++ if (saa7146_read(saa, PSR) & MASK_06) { ++ if (budget_av->slot_status == SLOTSTATUS_NONE) { ++ budget_av->slot_status = SLOTSTATUS_PRESENT; ++ pr_info("cam inserted A\n"); ++ } ++ } ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); ++ } ++ ++ /* We also try and read from IO memory to work round the above detection bug. If ++ * there is no CAM, we will get a timeout. Only done if there is no cam ++ * present, since this test actually breaks some cams :( ++ * ++ * if the CI interface is not open, we also do the above test since we ++ * don't care if the cam has problems - we'll be resetting it on open() anyway */ ++ if ((budget_av->slot_status == SLOTSTATUS_NONE) || (!open)) { ++ saa7146_setgpio(budget_av->budget.dev, 1, SAA7146_GPIO_OUTLO); ++ result = ttpci_budget_debiread(&budget_av->budget, DEBICICAM, 0, 1, 0, 1); ++ if ((result >= 0) && (budget_av->slot_status == SLOTSTATUS_NONE)) { ++ budget_av->slot_status = SLOTSTATUS_PRESENT; ++ pr_info("cam inserted B\n"); ++ } else if (result < 0) { ++ if (budget_av->slot_status != SLOTSTATUS_NONE) { ++ ciintf_slot_shutdown(ca, slot); ++ pr_info("cam ejected 5\n"); ++ return 0; ++ } ++ } ++ } ++ ++ /* read from attribute memory in reset/ready state to know when the CAM is ready */ ++ if (budget_av->slot_status == SLOTSTATUS_RESET) { ++ result = ciintf_read_attribute_mem(ca, slot, 0); ++ if (result == 0x1d) { ++ budget_av->slot_status = SLOTSTATUS_READY; ++ } ++ } ++ ++ /* work out correct return code */ ++ if (budget_av->slot_status != SLOTSTATUS_NONE) { ++ if (budget_av->slot_status & SLOTSTATUS_READY) { ++ return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; ++ } ++ return DVB_CA_EN50221_POLL_CAM_PRESENT; ++ } ++ return 0; ++} ++ ++static int ciintf_init(struct budget_av *budget_av) ++{ ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ int result; ++ ++ memset(&budget_av->ca, 0, sizeof(struct dvb_ca_en50221)); ++ ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); ++ saa7146_setgpio(saa, 2, SAA7146_GPIO_OUTLO); ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); ++ ++ /* Enable DEBI pins */ ++ saa7146_write(saa, MC1, MASK_27 | MASK_11); ++ ++ /* register CI interface */ ++ budget_av->ca.owner = THIS_MODULE; ++ budget_av->ca.read_attribute_mem = ciintf_read_attribute_mem; ++ budget_av->ca.write_attribute_mem = ciintf_write_attribute_mem; ++ budget_av->ca.read_cam_control = ciintf_read_cam_control; ++ budget_av->ca.write_cam_control = ciintf_write_cam_control; ++ budget_av->ca.slot_reset = ciintf_slot_reset; ++ budget_av->ca.slot_shutdown = ciintf_slot_shutdown; ++ budget_av->ca.slot_ts_enable = ciintf_slot_ts_enable; ++ budget_av->ca.poll_slot_status = ciintf_poll_slot_status; ++ budget_av->ca.data = budget_av; ++ budget_av->budget.ci_present = 1; ++ budget_av->slot_status = SLOTSTATUS_NONE; ++ ++ if ((result = dvb_ca_en50221_init(&budget_av->budget.dvb_adapter, ++ &budget_av->ca, 0, 1)) != 0) { ++ pr_err("ci initialisation failed\n"); ++ goto error; ++ } ++ ++ pr_info("ci interface initialised\n"); ++ return 0; ++ ++error: ++ saa7146_write(saa, MC1, MASK_27); ++ return result; ++} ++ ++static void ciintf_deinit(struct budget_av *budget_av) ++{ ++ struct saa7146_dev *saa = budget_av->budget.dev; ++ ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); ++ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); ++ ++ /* release the CA device */ ++ dvb_ca_en50221_release(&budget_av->ca); ++ ++ /* disable DEBI pins */ ++ saa7146_write(saa, MC1, MASK_27); ++} ++ ++ ++static const u8 saa7113_tab[] = { ++ 0x01, 0x08, ++ 0x02, 0xc0, ++ 0x03, 0x33, ++ 0x04, 0x00, ++ 0x05, 0x00, ++ 0x06, 0xeb, ++ 0x07, 0xe0, ++ 0x08, 0x28, ++ 0x09, 0x00, ++ 0x0a, 0x80, ++ 0x0b, 0x47, ++ 0x0c, 0x40, ++ 0x0d, 0x00, ++ 0x0e, 0x01, ++ 0x0f, 0x44, ++ ++ 0x10, 0x08, ++ 0x11, 0x0c, ++ 0x12, 0x7b, ++ 0x13, 0x00, ++ 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, ++ ++ 0x57, 0xff, ++ 0x40, 0x82, 0x58, 0x00, 0x59, 0x54, 0x5a, 0x07, ++ 0x5b, 0x83, 0x5e, 0x00, ++ 0xff ++}; ++ ++static int saa7113_init(struct budget_av *budget_av) ++{ ++ struct budget *budget = &budget_av->budget; ++ struct saa7146_dev *saa = budget->dev; ++ const u8 *data = saa7113_tab; ++ ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTHI); ++ msleep(200); ++ ++ if (i2c_writereg(&budget->i2c_adap, 0x4a, 0x01, 0x08) != 1) { ++ dprintk(1, "saa7113 not found on KNC card\n"); ++ return -ENODEV; ++ } ++ ++ dprintk(1, "saa7113 detected and initializing\n"); ++ ++ while (*data != 0xff) { ++ i2c_writereg(&budget->i2c_adap, 0x4a, *data, *(data + 1)); ++ data += 2; ++ } ++ ++ dprintk(1, "saa7113 status=%02x\n", i2c_readreg(&budget->i2c_adap, 0x4a, 0x1f)); ++ ++ return 0; ++} ++ ++static int saa7113_setinput(struct budget_av *budget_av, int input) ++{ ++ struct budget *budget = &budget_av->budget; ++ ++ if (1 != budget_av->has_saa7113) ++ return -ENODEV; ++ ++ if (input == 1) { ++ i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc7); ++ i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x80); ++ } else if (input == 0) { ++ i2c_writereg(&budget->i2c_adap, 0x4a, 0x02, 0xc0); ++ i2c_writereg(&budget->i2c_adap, 0x4a, 0x09, 0x00); ++ } else ++ return -EINVAL; ++ ++ budget_av->cur_input = input; ++ return 0; ++} ++ ++ ++static int philips_su1278_ty_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) ++{ ++ u8 aclk = 0; ++ u8 bclk = 0; ++ u8 m1; ++ ++ aclk = 0xb5; ++ if (srate < 2000000) ++ bclk = 0x86; ++ else if (srate < 5000000) ++ bclk = 0x89; ++ else if (srate < 15000000) ++ bclk = 0x8f; ++ else if (srate < 45000000) ++ bclk = 0x95; ++ ++ m1 = 0x14; ++ if (srate < 4000000) ++ m1 = 0x10; ++ ++ 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); ++ stv0299_writereg(fe, 0x0f, 0x80 | m1); ++ ++ return 0; ++} ++ ++static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ u32 div; ++ u8 buf[4]; ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) }; ++ ++ if ((c->frequency < 950000) || (c->frequency > 2150000)) ++ return -EINVAL; ++ ++ div = (c->frequency + (125 - 1)) / 125; /* round correctly */ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x80 | ((div & 0x18000) >> 10) | 4; ++ buf[3] = 0x20; ++ ++ if (c->symbol_rate < 4000000) ++ buf[3] |= 1; ++ ++ if (c->frequency < 1250000) ++ buf[3] |= 0; ++ else if (c->frequency < 1550000) ++ buf[3] |= 0x40; ++ else if (c->frequency < 2050000) ++ buf[3] |= 0x80; ++ else if (c->frequency < 2150000) ++ buf[3] |= 0xC0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static u8 typhoon_cinergy1200s_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x7d, /* F22FR = 0x7d, F22 = f_VCO / 128 / 0x7d = 22 kHz */ ++ 0x05, 0x35, /* I2CT = 0, SCLT = 1, SDAT = 1 */ ++ 0x06, 0x40, /* DAC not used, set to high impendance mode */ ++ 0x07, 0x00, /* DAC LSB */ ++ 0x08, 0x40, /* DiSEqC off */ ++ 0x09, 0x00, /* FIFO */ ++ 0x0c, 0x51, /* OP1 ctl = Normal, OP1 val = 1 (LNB Power ON) */ ++ 0x0d, 0x82, /* DC offset compensation = ON, beta_agc1 = 2 */ ++ 0x0e, 0x23, /* alpha_tmg = 2, beta_tmg = 3 */ ++ 0x10, 0x3f, // AGC2 0x3d ++ 0x11, 0x84, ++ 0x12, 0xb9, ++ 0x15, 0xc9, // lock detector threshold ++ 0x16, 0x00, ++ 0x17, 0x00, ++ 0x18, 0x00, ++ 0x19, 0x00, ++ 0x1a, 0x00, ++ 0x1f, 0x50, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, // out imp: normal out type: parallel FEC mode:0 ++ 0x29, 0x1e, // 1/2 threshold ++ 0x2a, 0x14, // 2/3 threshold ++ 0x2b, 0x0f, // 3/4 threshold ++ 0x2c, 0x09, // 5/6 threshold ++ 0x2d, 0x05, // 7/8 threshold ++ 0x2e, 0x01, ++ 0x31, 0x1f, // test all FECs ++ 0x32, 0x19, // viterbi and synchro search ++ 0x33, 0xfc, // rs control ++ 0x34, 0x93, // error control ++ 0x0f, 0x92, ++ 0xff, 0xff ++}; ++ ++static struct stv0299_config typhoon_config = { ++ .demod_address = 0x68, ++ .inittab = typhoon_cinergy1200s_inittab, ++ .mclk = 88000000UL, ++ .invert = 0, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, ++}; ++ ++ ++static struct stv0299_config cinergy_1200s_config = { ++ .demod_address = 0x68, ++ .inittab = typhoon_cinergy1200s_inittab, ++ .mclk = 88000000UL, ++ .invert = 0, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_0, ++ .volt13_op0_op1 = STV0299_VOLT13_OP0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, ++}; ++ ++static struct stv0299_config cinergy_1200s_1894_0010_config = { ++ .demod_address = 0x68, ++ .inittab = typhoon_cinergy1200s_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, ++}; ++ ++static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ u8 buf[6]; ++ struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; ++ int i; ++ ++#define CU1216_IF 36125000 ++#define TUNER_MUL 62500 ++ ++ u32 div = (c->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0xce; ++ buf[3] = (c->frequency < 150000000 ? 0x01 : ++ c->frequency < 445000000 ? 0x02 : 0x04); ++ buf[4] = 0xde; ++ buf[5] = 0x20; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ ++ /* wait for the pll lock */ ++ msg.flags = I2C_M_RD; ++ msg.len = 1; ++ for (i = 0; i < 20; i++) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) ++ break; ++ msleep(10); ++ } ++ ++ /* switch the charge pump to the lower current */ ++ msg.flags = 0; ++ msg.len = 2; ++ msg.buf = &buf[2]; ++ buf[2] &= ~0x40; ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ ++ return 0; ++} ++ ++static struct tda1002x_config philips_cu1216_config = { ++ .demod_address = 0x0c, ++ .invert = 1, ++}; ++ ++static struct tda1002x_config philips_cu1216_config_altaddress = { ++ .demod_address = 0x0d, ++ .invert = 0, ++}; ++ ++static struct tda10023_config philips_cu1216_tda10023_config = { ++ .demod_address = 0x0c, ++ .invert = 1, ++}; ++ ++static int philips_tu1216_tuner_init(struct dvb_frontend *fe) ++{ ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; ++ struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; ++ ++ // setup PLL configuration ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ msleep(1); ++ ++ return 0; ++} ++ ++static int philips_tu1216_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ u8 tuner_buf[4]; ++ struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = ++ sizeof(tuner_buf) }; ++ int tuner_frequency = 0; ++ u8 band, cp, filter; ++ ++ // determine charge pump ++ tuner_frequency = c->frequency + 36166000; ++ if (tuner_frequency < 87000000) ++ return -EINVAL; ++ else if (tuner_frequency < 130000000) ++ cp = 3; ++ else if (tuner_frequency < 160000000) ++ cp = 5; ++ else if (tuner_frequency < 200000000) ++ cp = 6; ++ else if (tuner_frequency < 290000000) ++ cp = 3; ++ else if (tuner_frequency < 420000000) ++ cp = 5; ++ else if (tuner_frequency < 480000000) ++ cp = 6; ++ else if (tuner_frequency < 620000000) ++ cp = 3; ++ else if (tuner_frequency < 830000000) ++ cp = 5; ++ else if (tuner_frequency < 895000000) ++ cp = 7; ++ else ++ return -EINVAL; ++ ++ // determine band ++ if (c->frequency < 49000000) ++ return -EINVAL; ++ else if (c->frequency < 161000000) ++ band = 1; ++ else if (c->frequency < 444000000) ++ band = 2; ++ else if (c->frequency < 861000000) ++ band = 4; ++ else ++ return -EINVAL; ++ ++ // setup PLL filter ++ switch (c->bandwidth_hz) { ++ case 6000000: ++ filter = 0; ++ break; ++ ++ case 7000000: ++ filter = 0; ++ break; ++ ++ case 8000000: ++ filter = 1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // calculate divisor ++ // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) ++ tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; ++ ++ // setup tuner buffer ++ tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; ++ tuner_buf[1] = tuner_frequency & 0xff; ++ tuner_buf[2] = 0xca; ++ tuner_buf[3] = (cp << 5) | (filter << 3) | band; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ ++ msleep(1); ++ return 0; ++} ++ ++static int philips_tu1216_request_firmware(struct dvb_frontend *fe, ++ const struct firmware **fw, char *name) ++{ ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ ++ return request_firmware(fw, name, &budget->dev->pci->dev); ++} ++ ++static struct tda1004x_config philips_tu1216_config = { ++ ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 1, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .request_firmware = philips_tu1216_request_firmware, ++}; ++ ++static u8 philips_sd1878_inittab[] = { ++ 0x01, 0x15, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x7d, ++ 0x05, 0x35, ++ 0x06, 0x40, ++ 0x07, 0x00, ++ 0x08, 0x43, ++ 0x09, 0x02, ++ 0x0C, 0x51, ++ 0x0D, 0x82, ++ 0x0E, 0x23, ++ 0x10, 0x3f, ++ 0x11, 0x84, ++ 0x12, 0xb9, ++ 0x15, 0xc9, ++ 0x16, 0x19, ++ 0x17, 0x8c, ++ 0x18, 0x59, ++ 0x19, 0xf8, ++ 0x1a, 0xfe, ++ 0x1c, 0x7f, ++ 0x1d, 0x00, ++ 0x1e, 0x00, ++ 0x1f, 0x50, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, ++ 0x29, 0x28, ++ 0x2a, 0x14, ++ 0x2b, 0x0f, ++ 0x2c, 0x09, ++ 0x2d, 0x09, ++ 0x31, 0x1f, ++ 0x32, 0x19, ++ 0x33, 0xfc, ++ 0x34, 0x93, ++ 0xff, 0xff ++}; ++ ++static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, ++ u32 srate, u32 ratio) ++{ ++ u8 aclk = 0; ++ u8 bclk = 0; ++ u8 m1; ++ ++ aclk = 0xb5; ++ if (srate < 2000000) ++ bclk = 0x86; ++ else if (srate < 5000000) ++ bclk = 0x89; ++ else if (srate < 15000000) ++ bclk = 0x8f; ++ else if (srate < 45000000) ++ bclk = 0x95; ++ ++ m1 = 0x14; ++ if (srate < 4000000) ++ m1 = 0x10; ++ ++ stv0299_writereg(fe, 0x0e, 0x23); ++ stv0299_writereg(fe, 0x0f, 0x94); ++ stv0299_writereg(fe, 0x10, 0x39); ++ stv0299_writereg(fe, 0x13, aclk); ++ stv0299_writereg(fe, 0x14, bclk); ++ stv0299_writereg(fe, 0x15, 0xc9); ++ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); ++ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); ++ stv0299_writereg(fe, 0x21, (ratio) & 0xf0); ++ stv0299_writereg(fe, 0x0f, 0x80 | m1); ++ ++ return 0; ++} ++ ++static struct stv0299_config philips_sd1878_config = { ++ .demod_address = 0x68, ++ .inittab = philips_sd1878_inittab, ++ .mclk = 88000000UL, ++ .invert = 0, ++ .skip_reinit = 0, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP0, ++ .min_delay_ms = 100, ++ .set_symbol_rate = philips_sd1878_ci_set_symbol_rate, ++}; ++ ++/* KNC1 DVB-S (STB0899) Inittab */ ++static const struct stb0899_s1_reg knc1_stb0899_s1_init_1[] = { ++ ++ { STB0899_DEV_ID , 0x81 }, ++ { STB0899_DISCNTRL1 , 0x32 }, ++ { STB0899_DISCNTRL2 , 0x80 }, ++ { STB0899_DISRX_ST0 , 0x04 }, ++ { STB0899_DISRX_ST1 , 0x00 }, ++ { STB0899_DISPARITY , 0x00 }, ++ { STB0899_DISSTATUS , 0x20 }, ++ { STB0899_DISF22 , 0x8c }, ++ { STB0899_DISF22RX , 0x9a }, ++ { STB0899_SYSREG , 0x0b }, ++ { STB0899_ACRPRESC , 0x11 }, ++ { STB0899_ACRDIV1 , 0x0a }, ++ { STB0899_ACRDIV2 , 0x05 }, ++ { STB0899_DACR1 , 0x00 }, ++ { STB0899_DACR2 , 0x00 }, ++ { STB0899_OUTCFG , 0x00 }, ++ { STB0899_MODECFG , 0x00 }, ++ { STB0899_IRQSTATUS_3 , 0x30 }, ++ { STB0899_IRQSTATUS_2 , 0x00 }, ++ { STB0899_IRQSTATUS_1 , 0x00 }, ++ { STB0899_IRQSTATUS_0 , 0x00 }, ++ { STB0899_IRQMSK_3 , 0xf3 }, ++ { STB0899_IRQMSK_2 , 0xfc }, ++ { STB0899_IRQMSK_1 , 0xff }, ++ { STB0899_IRQMSK_0 , 0xff }, ++ { STB0899_IRQCFG , 0x00 }, ++ { STB0899_I2CCFG , 0x88 }, ++ { STB0899_I2CRPT , 0x58 }, /* Repeater=8, Stop=disabled */ ++ { STB0899_IOPVALUE5 , 0x00 }, ++ { STB0899_IOPVALUE4 , 0x20 }, ++ { STB0899_IOPVALUE3 , 0xc9 }, ++ { STB0899_IOPVALUE2 , 0x90 }, ++ { STB0899_IOPVALUE1 , 0x40 }, ++ { STB0899_IOPVALUE0 , 0x00 }, ++ { STB0899_GPIO00CFG , 0x82 }, ++ { STB0899_GPIO01CFG , 0x82 }, ++ { STB0899_GPIO02CFG , 0x82 }, ++ { STB0899_GPIO03CFG , 0x82 }, ++ { STB0899_GPIO04CFG , 0x82 }, ++ { STB0899_GPIO05CFG , 0x82 }, ++ { STB0899_GPIO06CFG , 0x82 }, ++ { STB0899_GPIO07CFG , 0x82 }, ++ { STB0899_GPIO08CFG , 0x82 }, ++ { STB0899_GPIO09CFG , 0x82 }, ++ { STB0899_GPIO10CFG , 0x82 }, ++ { STB0899_GPIO11CFG , 0x82 }, ++ { STB0899_GPIO12CFG , 0x82 }, ++ { STB0899_GPIO13CFG , 0x82 }, ++ { STB0899_GPIO14CFG , 0x82 }, ++ { STB0899_GPIO15CFG , 0x82 }, ++ { STB0899_GPIO16CFG , 0x82 }, ++ { STB0899_GPIO17CFG , 0x82 }, ++ { STB0899_GPIO18CFG , 0x82 }, ++ { STB0899_GPIO19CFG , 0x82 }, ++ { STB0899_GPIO20CFG , 0x82 }, ++ { STB0899_SDATCFG , 0xb8 }, ++ { STB0899_SCLTCFG , 0xba }, ++ { STB0899_AGCRFCFG , 0x08 }, /* 0x1c */ ++ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ ++ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ ++ { STB0899_DIRCLKCFG , 0x82 }, ++ { STB0899_CLKOUT27CFG , 0x7e }, ++ { STB0899_STDBYCFG , 0x82 }, ++ { STB0899_CS0CFG , 0x82 }, ++ { STB0899_CS1CFG , 0x82 }, ++ { STB0899_DISEQCOCFG , 0x20 }, ++ { STB0899_GPIO32CFG , 0x82 }, ++ { STB0899_GPIO33CFG , 0x82 }, ++ { STB0899_GPIO34CFG , 0x82 }, ++ { STB0899_GPIO35CFG , 0x82 }, ++ { STB0899_GPIO36CFG , 0x82 }, ++ { STB0899_GPIO37CFG , 0x82 }, ++ { STB0899_GPIO38CFG , 0x82 }, ++ { STB0899_GPIO39CFG , 0x82 }, ++ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ ++ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ ++ { STB0899_FILTCTRL , 0x00 }, ++ { STB0899_SYSCTRL , 0x00 }, ++ { STB0899_STOPCLK1 , 0x20 }, ++ { STB0899_STOPCLK2 , 0x00 }, ++ { STB0899_INTBUFSTATUS , 0x00 }, ++ { STB0899_INTBUFCTRL , 0x0a }, ++ { 0xffff , 0xff }, ++}; ++ ++static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { ++ { STB0899_DEMOD , 0x00 }, ++ { STB0899_RCOMPC , 0xc9 }, ++ { STB0899_AGC1CN , 0x41 }, ++ { STB0899_AGC1REF , 0x08 }, ++ { STB0899_RTC , 0x7a }, ++ { STB0899_TMGCFG , 0x4e }, ++ { STB0899_AGC2REF , 0x33 }, ++ { STB0899_TLSR , 0x84 }, ++ { STB0899_CFD , 0xee }, ++ { STB0899_ACLC , 0x87 }, ++ { STB0899_BCLC , 0x94 }, ++ { STB0899_EQON , 0x41 }, ++ { STB0899_LDT , 0xdd }, ++ { STB0899_LDT2 , 0xc9 }, ++ { STB0899_EQUALREF , 0xb4 }, ++ { STB0899_TMGRAMP , 0x10 }, ++ { STB0899_TMGTHD , 0x30 }, ++ { STB0899_IDCCOMP , 0xfb }, ++ { STB0899_QDCCOMP , 0x03 }, ++ { STB0899_POWERI , 0x3b }, ++ { STB0899_POWERQ , 0x3d }, ++ { STB0899_RCOMP , 0x81 }, ++ { STB0899_AGCIQIN , 0x80 }, ++ { STB0899_AGC2I1 , 0x04 }, ++ { STB0899_AGC2I2 , 0xf5 }, ++ { STB0899_TLIR , 0x25 }, ++ { STB0899_RTF , 0x80 }, ++ { STB0899_DSTATUS , 0x00 }, ++ { STB0899_LDI , 0xca }, ++ { STB0899_CFRM , 0xf1 }, ++ { STB0899_CFRL , 0xf3 }, ++ { STB0899_NIRM , 0x2a }, ++ { STB0899_NIRL , 0x05 }, ++ { STB0899_ISYMB , 0x17 }, ++ { STB0899_QSYMB , 0xfa }, ++ { STB0899_SFRH , 0x2f }, ++ { STB0899_SFRM , 0x68 }, ++ { STB0899_SFRL , 0x40 }, ++ { STB0899_SFRUPH , 0x2f }, ++ { STB0899_SFRUPM , 0x68 }, ++ { STB0899_SFRUPL , 0x40 }, ++ { STB0899_EQUAI1 , 0xfd }, ++ { STB0899_EQUAQ1 , 0x04 }, ++ { STB0899_EQUAI2 , 0x0f }, ++ { STB0899_EQUAQ2 , 0xff }, ++ { STB0899_EQUAI3 , 0xdf }, ++ { STB0899_EQUAQ3 , 0xfa }, ++ { STB0899_EQUAI4 , 0x37 }, ++ { STB0899_EQUAQ4 , 0x0d }, ++ { STB0899_EQUAI5 , 0xbd }, ++ { STB0899_EQUAQ5 , 0xf7 }, ++ { STB0899_DSTATUS2 , 0x00 }, ++ { STB0899_VSTATUS , 0x00 }, ++ { STB0899_VERROR , 0xff }, ++ { STB0899_IQSWAP , 0x2a }, ++ { STB0899_ECNT1M , 0x00 }, ++ { STB0899_ECNT1L , 0x00 }, ++ { STB0899_ECNT2M , 0x00 }, ++ { STB0899_ECNT2L , 0x00 }, ++ { STB0899_ECNT3M , 0x00 }, ++ { STB0899_ECNT3L , 0x00 }, ++ { STB0899_FECAUTO1 , 0x06 }, ++ { STB0899_FECM , 0x01 }, ++ { STB0899_VTH12 , 0xf0 }, ++ { STB0899_VTH23 , 0xa0 }, ++ { STB0899_VTH34 , 0x78 }, ++ { STB0899_VTH56 , 0x4e }, ++ { STB0899_VTH67 , 0x48 }, ++ { STB0899_VTH78 , 0x38 }, ++ { STB0899_PRVIT , 0xff }, ++ { STB0899_VITSYNC , 0x19 }, ++ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ ++ { STB0899_TSULC , 0x42 }, ++ { STB0899_RSLLC , 0x40 }, ++ { STB0899_TSLPL , 0x12 }, ++ { STB0899_TSCFGH , 0x0c }, ++ { STB0899_TSCFGM , 0x00 }, ++ { STB0899_TSCFGL , 0x0c }, ++ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ ++ { STB0899_RSSYNCDEL , 0x00 }, ++ { STB0899_TSINHDELH , 0x02 }, ++ { STB0899_TSINHDELM , 0x00 }, ++ { STB0899_TSINHDELL , 0x00 }, ++ { STB0899_TSLLSTKM , 0x00 }, ++ { STB0899_TSLLSTKL , 0x00 }, ++ { STB0899_TSULSTKM , 0x00 }, ++ { STB0899_TSULSTKL , 0xab }, ++ { STB0899_PCKLENUL , 0x00 }, ++ { STB0899_PCKLENLL , 0xcc }, ++ { STB0899_RSPCKLEN , 0xcc }, ++ { STB0899_TSSTATUS , 0x80 }, ++ { STB0899_ERRCTRL1 , 0xb6 }, ++ { STB0899_ERRCTRL2 , 0x96 }, ++ { STB0899_ERRCTRL3 , 0x89 }, ++ { STB0899_DMONMSK1 , 0x27 }, ++ { STB0899_DMONMSK0 , 0x03 }, ++ { STB0899_DEMAPVIT , 0x5c }, ++ { STB0899_PLPARM , 0x1f }, ++ { STB0899_PDELCTRL , 0x48 }, ++ { STB0899_PDELCTRL2 , 0x00 }, ++ { STB0899_BBHCTRL1 , 0x00 }, ++ { STB0899_BBHCTRL2 , 0x00 }, ++ { STB0899_HYSTTHRESH , 0x77 }, ++ { STB0899_MATCSTM , 0x00 }, ++ { STB0899_MATCSTL , 0x00 }, ++ { STB0899_UPLCSTM , 0x00 }, ++ { STB0899_UPLCSTL , 0x00 }, ++ { STB0899_DFLCSTM , 0x00 }, ++ { STB0899_DFLCSTL , 0x00 }, ++ { STB0899_SYNCCST , 0x00 }, ++ { STB0899_SYNCDCSTM , 0x00 }, ++ { STB0899_SYNCDCSTL , 0x00 }, ++ { STB0899_ISI_ENTRY , 0x00 }, ++ { STB0899_ISI_BIT_EN , 0x00 }, ++ { STB0899_MATSTRM , 0x00 }, ++ { STB0899_MATSTRL , 0x00 }, ++ { STB0899_UPLSTRM , 0x00 }, ++ { STB0899_UPLSTRL , 0x00 }, ++ { STB0899_DFLSTRM , 0x00 }, ++ { STB0899_DFLSTRL , 0x00 }, ++ { STB0899_SYNCSTR , 0x00 }, ++ { STB0899_SYNCDSTRM , 0x00 }, ++ { STB0899_SYNCDSTRL , 0x00 }, ++ { STB0899_CFGPDELSTATUS1 , 0x10 }, ++ { STB0899_CFGPDELSTATUS2 , 0x00 }, ++ { STB0899_BBFERRORM , 0x00 }, ++ { STB0899_BBFERRORL , 0x00 }, ++ { STB0899_UPKTERRORM , 0x00 }, ++ { STB0899_UPKTERRORL , 0x00 }, ++ { 0xffff , 0xff }, ++}; ++ ++/* STB0899 demodulator config for the KNC1 and clones */ ++static struct stb0899_config knc1_dvbs2_config = { ++ .init_dev = knc1_stb0899_s1_init_1, ++ .init_s2_demod = stb0899_s2_init_2, ++ .init_s1_demod = knc1_stb0899_s1_init_3, ++ .init_s2_fec = stb0899_s2_init_4, ++ .init_tst = stb0899_s1_init_5, ++ ++ .postproc = NULL, ++ ++ .demod_address = 0x68, ++// .ts_output_mode = STB0899_OUT_PARALLEL, /* types = SERIAL/PARALLEL */ ++ .block_sync_mode = STB0899_SYNC_FORCED, /* DSS, SYNC_FORCED/UNSYNCED */ ++// .ts_pfbit_toggle = STB0899_MPEG_NORMAL, /* DirecTV, MPEG toggling seq */ ++ ++ .xtal_freq = 27000000, ++ .inversion = IQ_SWAP_OFF, /* 1 */ ++ ++ .lo_clk = 76500000, ++ .hi_clk = 90000000, ++ ++ .esno_ave = STB0899_DVBS2_ESNO_AVE, ++ .esno_quant = STB0899_DVBS2_ESNO_QUANT, ++ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, ++ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, ++ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, ++ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, ++ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, ++ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, ++ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, ++ ++ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, ++ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, ++ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, ++ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, ++ ++ .tuner_get_frequency = tda8261_get_frequency, ++ .tuner_set_frequency = tda8261_set_frequency, ++ .tuner_set_bandwidth = NULL, ++ .tuner_get_bandwidth = tda8261_get_bandwidth, ++ .tuner_set_rfsiggain = NULL ++}; ++ ++/* ++ * SD1878/SHA tuner config ++ * 1F, Single I/P, Horizontal mount, High Sensitivity ++ */ ++static const struct tda8261_config sd1878c_config = { ++// .name = "SD1878/SHA", ++ .addr = 0x60, ++ .step_size = TDA8261_STEP_1000 /* kHz */ ++}; ++ ++static u8 read_pwm(struct budget_av *budget_av) ++{ ++ u8 b = 0xff; ++ u8 pwm; ++ struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, ++ {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} ++ }; ++ ++ if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) ++ || (pwm == 0xff)) ++ pwm = 0x48; ++ ++ return pwm; ++} ++ ++#define SUBID_DVBS_KNC1 0x0010 ++#define SUBID_DVBS_KNC1_PLUS 0x0011 ++#define SUBID_DVBS_TYPHOON 0x4f56 ++#define SUBID_DVBS_CINERGY1200 0x1154 ++#define SUBID_DVBS_CYNERGY1200N 0x1155 ++#define SUBID_DVBS_TV_STAR 0x0014 ++#define SUBID_DVBS_TV_STAR_PLUS_X4 0x0015 ++#define SUBID_DVBS_TV_STAR_CI 0x0016 ++#define SUBID_DVBS2_KNC1 0x0018 ++#define SUBID_DVBS2_KNC1_OEM 0x0019 ++#define SUBID_DVBS_EASYWATCH_1 0x001a ++#define SUBID_DVBS_EASYWATCH_2 0x001b ++#define SUBID_DVBS2_EASYWATCH 0x001d ++#define SUBID_DVBS_EASYWATCH 0x001e ++ ++#define SUBID_DVBC_EASYWATCH 0x002a ++#define SUBID_DVBC_EASYWATCH_MK3 0x002c ++#define SUBID_DVBC_KNC1 0x0020 ++#define SUBID_DVBC_KNC1_PLUS 0x0021 ++#define SUBID_DVBC_KNC1_MK3 0x0022 ++#define SUBID_DVBC_KNC1_TDA10024 0x0028 ++#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 ++#define SUBID_DVBC_CINERGY1200 0x1156 ++#define SUBID_DVBC_CINERGY1200_MK3 0x1176 ++ ++#define SUBID_DVBT_EASYWATCH 0x003a ++#define SUBID_DVBT_KNC1_PLUS 0x0031 ++#define SUBID_DVBT_KNC1 0x0030 ++#define SUBID_DVBT_CINERGY1200 0x1157 ++ ++static void frontend_init(struct budget_av *budget_av) ++{ ++ struct saa7146_dev * saa = budget_av->budget.dev; ++ struct dvb_frontend * fe = NULL; ++ ++ /* Enable / PowerON Frontend */ ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); ++ ++ /* Wait for PowerON */ ++ msleep(100); ++ ++ /* additional setup necessary for the PLUS cards */ ++ switch (saa->pci->subsystem_device) { ++ case SUBID_DVBS_KNC1_PLUS: ++ case SUBID_DVBC_KNC1_PLUS: ++ case SUBID_DVBT_KNC1_PLUS: ++ case SUBID_DVBC_EASYWATCH: ++ case SUBID_DVBC_KNC1_PLUS_MK3: ++ case SUBID_DVBS2_KNC1: ++ case SUBID_DVBS2_KNC1_OEM: ++ case SUBID_DVBS2_EASYWATCH: ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); ++ break; ++ } ++ ++ switch (saa->pci->subsystem_device) { ++ ++ case SUBID_DVBS_KNC1: ++ /* ++ * maybe that setting is needed for other dvb-s cards as well, ++ * but so far it has been only confirmed for this type ++ */ ++ budget_av->reinitialise_demod = 1; ++ /* fall through */ ++ case SUBID_DVBS_KNC1_PLUS: ++ case SUBID_DVBS_EASYWATCH_1: ++ if (saa->pci->subsystem_vendor == 0x1894) { ++ fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap); ++ } ++ } else { ++ fe = dvb_attach(stv0299_attach, &typhoon_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; ++ } ++ } ++ break; ++ ++ case SUBID_DVBS_TV_STAR: ++ case SUBID_DVBS_TV_STAR_PLUS_X4: ++ case SUBID_DVBS_TV_STAR_CI: ++ case SUBID_DVBS_CYNERGY1200N: ++ case SUBID_DVBS_EASYWATCH: ++ case SUBID_DVBS_EASYWATCH_2: ++ fe = dvb_attach(stv0299_attach, &philips_sd1878_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ dvb_attach(dvb_pll_attach, fe, 0x60, ++ &budget_av->budget.i2c_adap, ++ DVB_PLL_PHILIPS_SD1878_TDA8261); ++ } ++ break; ++ ++ case SUBID_DVBS_TYPHOON: ++ fe = dvb_attach(stv0299_attach, &typhoon_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; ++ } ++ break; ++ case SUBID_DVBS2_KNC1: ++ case SUBID_DVBS2_KNC1_OEM: ++ case SUBID_DVBS2_EASYWATCH: ++ budget_av->reinitialise_demod = 1; ++ if ((fe = dvb_attach(stb0899_attach, &knc1_dvbs2_config, &budget_av->budget.i2c_adap))) ++ dvb_attach(tda8261_attach, fe, &sd1878c_config, &budget_av->budget.i2c_adap); ++ ++ break; ++ case SUBID_DVBS_CINERGY1200: ++ fe = dvb_attach(stv0299_attach, &cinergy_1200s_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params; ++ } ++ break; ++ ++ case SUBID_DVBC_KNC1: ++ case SUBID_DVBC_KNC1_PLUS: ++ case SUBID_DVBC_CINERGY1200: ++ case SUBID_DVBC_EASYWATCH: ++ budget_av->reinitialise_demod = 1; ++ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; ++ fe = dvb_attach(tda10021_attach, &philips_cu1216_config, ++ &budget_av->budget.i2c_adap, ++ read_pwm(budget_av)); ++ if (fe == NULL) ++ fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, ++ &budget_av->budget.i2c_adap, ++ read_pwm(budget_av)); ++ if (fe) { ++ fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; ++ } ++ break; ++ ++ case SUBID_DVBC_EASYWATCH_MK3: ++ case SUBID_DVBC_CINERGY1200_MK3: ++ case SUBID_DVBC_KNC1_MK3: ++ case SUBID_DVBC_KNC1_TDA10024: ++ case SUBID_DVBC_KNC1_PLUS_MK3: ++ budget_av->reinitialise_demod = 1; ++ budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; ++ fe = dvb_attach(tda10023_attach, ++ &philips_cu1216_tda10023_config, ++ &budget_av->budget.i2c_adap, ++ read_pwm(budget_av)); ++ if (fe) { ++ fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; ++ } ++ break; ++ ++ case SUBID_DVBT_EASYWATCH: ++ case SUBID_DVBT_KNC1: ++ case SUBID_DVBT_KNC1_PLUS: ++ case SUBID_DVBT_CINERGY1200: ++ budget_av->reinitialise_demod = 1; ++ fe = dvb_attach(tda10046_attach, &philips_tu1216_config, ++ &budget_av->budget.i2c_adap); ++ if (fe) { ++ fe->ops.tuner_ops.init = philips_tu1216_tuner_init; ++ fe->ops.tuner_ops.set_params = philips_tu1216_tuner_set_params; ++ } ++ break; ++ } ++ ++ if (fe == NULL) { ++ pr_err("A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ saa->pci->vendor, ++ saa->pci->device, ++ saa->pci->subsystem_vendor, ++ saa->pci->subsystem_device); ++ return; ++ } ++ ++ budget_av->budget.dvb_frontend = fe; ++ ++ if (dvb_register_frontend(&budget_av->budget.dvb_adapter, ++ budget_av->budget.dvb_frontend)) { ++ pr_err("Frontend registration failed!\n"); ++ dvb_frontend_detach(budget_av->budget.dvb_frontend); ++ budget_av->budget.dvb_frontend = NULL; ++ } ++} ++ ++ ++static void budget_av_irq(struct saa7146_dev *dev, u32 * isr) ++{ ++ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; ++ ++ dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); ++ ++ if (*isr & MASK_10) ++ ttpci_budget_irq10_handler(dev, isr); ++} ++ ++static int budget_av_detach(struct saa7146_dev *dev) ++{ ++ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; ++ int err; ++ ++ dprintk(2, "dev: %p\n", dev); ++ ++ if (1 == budget_av->has_saa7113) { ++ saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); ++ ++ msleep(200); ++ ++ saa7146_unregister_device(&budget_av->vd, dev); ++ ++ saa7146_vv_release(dev); ++ } ++ ++ if (budget_av->budget.ci_present) ++ ciintf_deinit(budget_av); ++ ++ if (budget_av->budget.dvb_frontend != NULL) { ++ dvb_unregister_frontend(budget_av->budget.dvb_frontend); ++ dvb_frontend_detach(budget_av->budget.dvb_frontend); ++ } ++ err = ttpci_budget_deinit(&budget_av->budget); ++ ++ kfree(budget_av); ++ ++ return err; ++} ++ ++#define KNC1_INPUTS 2 ++static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { ++ { 0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, ++ V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, ++ { 1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, ++ V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, ++}; ++ ++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) ++{ ++ dprintk(1, "VIDIOC_ENUMINPUT %d\n", i->index); ++ if (i->index >= KNC1_INPUTS) ++ return -EINVAL; ++ memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; ++ ++ *i = budget_av->cur_input; ++ ++ dprintk(1, "VIDIOC_G_INPUT %d\n", *i); ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *fh, unsigned int input) ++{ ++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; ++ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv; ++ ++ dprintk(1, "VIDIOC_S_INPUT %d\n", input); ++ return saa7113_setinput(budget_av, input); ++} ++ ++static struct saa7146_ext_vv vv_data; ++ ++static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) ++{ ++ struct budget_av *budget_av; ++ u8 *mac; ++ int err; ++ ++ dprintk(2, "dev: %p\n", dev); ++ ++ if (!(budget_av = kzalloc(sizeof(struct budget_av), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ budget_av->has_saa7113 = 0; ++ budget_av->budget.ci_present = 0; ++ ++ dev->ext_priv = budget_av; ++ ++ err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE, ++ adapter_nr); ++ if (err) { ++ kfree(budget_av); ++ return err; ++ } ++ ++ /* knc1 initialization */ ++ saa7146_write(dev, DD1_STREAM_B, 0x04000000); ++ saa7146_write(dev, DD1_INIT, 0x07000600); ++ saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); ++ ++ if (saa7113_init(budget_av) == 0) { ++ budget_av->has_saa7113 = 1; ++ err = saa7146_vv_init(dev, &vv_data); ++ if (err != 0) { ++ /* fixme: proper cleanup here */ ++ ERR("cannot init vv subsystem\n"); ++ return err; ++ } ++ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; ++ vv_data.vid_ops.vidioc_g_input = vidioc_g_input; ++ vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ++ ++ if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { ++ /* fixme: proper cleanup here */ ++ ERR("cannot register capture v4l2 device\n"); ++ saa7146_vv_release(dev); ++ return err; ++ } ++ ++ /* beware: this modifies dev->vv ... */ ++ saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, ++ SAA7146_HPS_SYNC_PORT_A); ++ ++ saa7113_setinput(budget_av, 0); ++ } ++ ++ /* fixme: find some sane values here... */ ++ saa7146_write(dev, PCI_BT_V1, 0x1c00101f); ++ ++ mac = budget_av->budget.dvb_adapter.proposed_mac; ++ if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { ++ pr_err("KNC1-%d: Could not read MAC from KNC1 card\n", ++ budget_av->budget.dvb_adapter.num); ++ memset(mac, 0, 6); ++ } else { ++ pr_info("KNC1-%d: MAC addr = %pM\n", ++ budget_av->budget.dvb_adapter.num, mac); ++ } ++ ++ budget_av->budget.dvb_adapter.priv = budget_av; ++ frontend_init(budget_av); ++ ciintf_init(budget_av); ++ ++ ttpci_budget_init_hooks(&budget_av->budget); ++ ++ return 0; ++} ++ ++static struct saa7146_standard standard[] = { ++ {.name = "PAL",.id = V4L2_STD_PAL, ++ .v_offset = 0x17,.v_field = 288, ++ .h_offset = 0x14,.h_pixels = 680, ++ .v_max_out = 576,.h_max_out = 768 }, ++ ++ {.name = "NTSC",.id = V4L2_STD_NTSC, ++ .v_offset = 0x16,.v_field = 240, ++ .h_offset = 0x06,.h_pixels = 708, ++ .v_max_out = 480,.h_max_out = 640, }, ++}; ++ ++static struct saa7146_ext_vv vv_data = { ++ .inputs = 2, ++ .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 ++ .flags = 0, ++ .stds = &standard[0], ++ .num_stds = ARRAY_SIZE(standard), ++}; ++ ++static struct saa7146_extension budget_extension; ++ ++MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S); ++MAKE_BUDGET_INFO(knc1s2,"KNC1 DVB-S2", BUDGET_KNC1S2); ++MAKE_BUDGET_INFO(sates2,"Satelco EasyWatch DVB-S2", BUDGET_KNC1S2); ++MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C); ++MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); ++MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); ++MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); ++MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); ++MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); ++MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); ++MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); ++MAKE_BUDGET_INFO(satewt, "Satelco EasyWatch DVB-T", BUDGET_KNC1T); ++MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); ++MAKE_BUDGET_INFO(knc1spx4, "KNC1 DVB-S Plus X4", BUDGET_KNC1SP); ++MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); ++MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); ++MAKE_BUDGET_INFO(knc1ctda10024, "KNC1 DVB-C TDA10024", BUDGET_KNC1C_TDA10024); ++MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); ++MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); ++MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); ++MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); ++MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); ++MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); ++MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); ++ ++static struct pci_device_id pci_tbl[] = { ++ MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), ++ MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), ++ MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010), ++ MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), ++ MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011), ++ MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014), ++ MAKE_EXTENSION_PCI(knc1spx4, 0x1894, 0x0015), ++ MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), ++ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0018), ++ MAKE_EXTENSION_PCI(knc1s2, 0x1894, 0x0019), ++ MAKE_EXTENSION_PCI(sates2, 0x1894, 0x001d), ++ MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), ++ MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), ++ MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), ++ MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), ++ MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), ++ MAKE_EXTENSION_PCI(satewt, 0x1894, 0x003a), ++ MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), ++ MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), ++ MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), ++ MAKE_EXTENSION_PCI(knc1ctda10024, 0x1894, 0x0028), ++ MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), ++ MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), ++ MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), ++ MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), ++ MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), ++ MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), ++ MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), ++ MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_extension budget_extension = { ++ .name = "budget_av", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .pci_tbl = pci_tbl, ++ ++ .module = THIS_MODULE, ++ .attach = budget_av_attach, ++ .detach = budget_av_detach, ++ ++ .irq_mask = MASK_10, ++ .irq_func = budget_av_irq, ++}; ++ ++static int __init budget_av_init(void) ++{ ++ return saa7146_register_extension(&budget_extension); ++} ++ ++static void __exit budget_av_exit(void) ++{ ++ saa7146_unregister_extension(&budget_extension); ++} ++ ++module_init(budget_av_init); ++module_exit(budget_av_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); ++MODULE_DESCRIPTION("driver for the SAA7146 based so-called " ++ "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)"); +diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c +new file mode 100644 +index 0000000..98e5241 +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget-ci.c +@@ -0,0 +1,1591 @@ ++/* ++ * budget-ci.c: driver for the SAA7146 based Budget DVB cards ++ * ++ * Compiled from various sources by Michael Hunold ++ * ++ * msp430 IR support contributed by Jack Thomasson ++ * partially based on the Siemens DVB driver by Ralph+Marcus Metzler ++ * ++ * CI interface support (c) 2004 Andrew de Quincey ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "budget.h" ++ ++#include "dvb_ca_en50221.h" ++#include "stv0299.h" ++#include "stv0297.h" ++#include "tda1004x.h" ++#include "stb0899_drv.h" ++#include "stb0899_reg.h" ++#include "stb0899_cfg.h" ++#include "stb6100.h" ++#include "stb6100_cfg.h" ++#include "lnbp21.h" ++#include "bsbe1.h" ++#include "bsru6.h" ++#include "tda1002x.h" ++#include "tda827x.h" ++#include "bsbe1-d01a.h" ++ ++#define MODULE_NAME "budget_ci" ++ ++/* ++ * Regarding DEBIADDR_IR: ++ * Some CI modules hang if random addresses are read. ++ * Using address 0x4000 for the IR read means that we ++ * use the same address as for CI version, which should ++ * be a safe default. ++ */ ++#define DEBIADDR_IR 0x4000 ++#define DEBIADDR_CICONTROL 0x0000 ++#define DEBIADDR_CIVERSION 0x4000 ++#define DEBIADDR_IO 0x1000 ++#define DEBIADDR_ATTR 0x3000 ++ ++#define CICONTROL_RESET 0x01 ++#define CICONTROL_ENABLETS 0x02 ++#define CICONTROL_CAMDETECT 0x08 ++ ++#define DEBICICTL 0x00420000 ++#define DEBICICAM 0x02420000 ++ ++#define SLOTSTATUS_NONE 1 ++#define SLOTSTATUS_PRESENT 2 ++#define SLOTSTATUS_RESET 4 ++#define SLOTSTATUS_READY 8 ++#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) ++ ++/* RC5 device wildcard */ ++#define IR_DEVICE_ANY 255 ++ ++static int rc5_device = -1; ++module_param(rc5_device, int, 0644); ++MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); ++ ++static int ir_debug; ++module_param(ir_debug, int, 0644); ++MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++struct budget_ci_ir { ++ struct rc_dev *dev; ++ struct tasklet_struct msp430_irq_tasklet; ++ char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ ++ char phys[32]; ++ int rc5_device; ++ u32 ir_key; ++ bool have_command; ++ bool full_rc5; /* Outputs a full RC5 code */ ++}; ++ ++struct budget_ci { ++ struct budget budget; ++ struct tasklet_struct ciintf_irq_tasklet; ++ int slot_status; ++ int ci_irq; ++ struct dvb_ca_en50221 ca; ++ struct budget_ci_ir ir; ++ u8 tuner_pll_address; /* used for philips_tdm1316l configs */ ++}; ++ ++static void msp430_ir_interrupt(unsigned long data) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) data; ++ struct rc_dev *dev = budget_ci->ir.dev; ++ u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; ++ ++ /* ++ * The msp430 chip can generate two different bytes, command and device ++ * ++ * type1: X1CCCCCC, C = command bits (0 - 63) ++ * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit ++ * ++ * Each signal from the remote control can generate one or more command ++ * bytes and one or more device bytes. For the repeated bytes, the ++ * highest bit (X) is set. The first command byte is always generated ++ * before the first device byte. Other than that, no specific order ++ * seems to apply. To make life interesting, bytes can also be lost. ++ * ++ * Only when we have a command and device byte, a keypress is ++ * generated. ++ */ ++ ++ if (ir_debug) ++ printk("budget_ci: received byte 0x%02x\n", command); ++ ++ /* Remove repeat bit, we use every command */ ++ command = command & 0x7f; ++ ++ /* Is this a RC5 command byte? */ ++ if (command & 0x40) { ++ budget_ci->ir.have_command = true; ++ budget_ci->ir.ir_key = command & 0x3f; ++ return; ++ } ++ ++ /* It's a RC5 device byte */ ++ if (!budget_ci->ir.have_command) ++ return; ++ budget_ci->ir.have_command = false; ++ ++ if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && ++ budget_ci->ir.rc5_device != (command & 0x1f)) ++ return; ++ ++ if (budget_ci->ir.full_rc5) { ++ rc_keydown(dev, ++ budget_ci->ir.rc5_device <<8 | budget_ci->ir.ir_key, ++ (command & 0x20) ? 1 : 0); ++ return; ++ } ++ ++ /* FIXME: We should generate complete scancodes for all devices */ ++ rc_keydown(dev, budget_ci->ir.ir_key, (command & 0x20) ? 1 : 0); ++} ++ ++static int msp430_ir_init(struct budget_ci *budget_ci) ++{ ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ struct rc_dev *dev; ++ int error; ++ ++ dev = rc_allocate_device(); ++ if (!dev) { ++ printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); ++ return -ENOMEM; ++ } ++ ++ snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), ++ "Budget-CI dvb ir receiver %s", saa->name); ++ snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), ++ "pci-%s/ir0", pci_name(saa->pci)); ++ ++ dev->driver_name = MODULE_NAME; ++ dev->input_name = budget_ci->ir.name; ++ dev->input_phys = budget_ci->ir.phys; ++ dev->input_id.bustype = BUS_PCI; ++ dev->input_id.version = 1; ++ if (saa->pci->subsystem_vendor) { ++ dev->input_id.vendor = saa->pci->subsystem_vendor; ++ dev->input_id.product = saa->pci->subsystem_device; ++ } else { ++ dev->input_id.vendor = saa->pci->vendor; ++ dev->input_id.product = saa->pci->device; ++ } ++ dev->dev.parent = &saa->pci->dev; ++ ++ if (rc5_device < 0) ++ budget_ci->ir.rc5_device = IR_DEVICE_ANY; ++ else ++ budget_ci->ir.rc5_device = rc5_device; ++ ++ /* Select keymap and address */ ++ switch (budget_ci->budget.dev->pci->subsystem_device) { ++ case 0x100c: ++ case 0x100f: ++ case 0x1011: ++ case 0x1012: ++ /* The hauppauge keymap is a superset of these remotes */ ++ dev->map_name = RC_MAP_HAUPPAUGE; ++ budget_ci->ir.full_rc5 = true; ++ ++ if (rc5_device < 0) ++ budget_ci->ir.rc5_device = 0x1f; ++ break; ++ case 0x1010: ++ case 0x1017: ++ case 0x1019: ++ case 0x101a: ++ case 0x101b: ++ /* for the Technotrend 1500 bundled remote */ ++ dev->map_name = RC_MAP_TT_1500; ++ break; ++ default: ++ /* unknown remote */ ++ dev->map_name = RC_MAP_BUDGET_CI_OLD; ++ break; ++ } ++ if (!budget_ci->ir.full_rc5) ++ dev->scanmask = 0xff; ++ ++ error = rc_register_device(dev); ++ if (error) { ++ printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); ++ rc_free_device(dev); ++ return error; ++ } ++ ++ budget_ci->ir.dev = dev; ++ ++ tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, ++ (unsigned long) budget_ci); ++ ++ SAA7146_IER_ENABLE(saa, MASK_06); ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); ++ ++ return 0; ++} ++ ++static void msp430_ir_deinit(struct budget_ci *budget_ci) ++{ ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ ++ SAA7146_IER_DISABLE(saa, MASK_06); ++ saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); ++ tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); ++ ++ rc_unregister_device(budget_ci->ir.dev); ++} ++ ++static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, ++ DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0); ++} ++ ++static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, ++ DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0); ++} ++ ++static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM, ++ DEBIADDR_IO | (address & 3), 1, 1, 0); ++} ++ ++static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM, ++ DEBIADDR_IO | (address & 3), 1, value, 1, 0); ++} ++ ++static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ if (budget_ci->ci_irq) { ++ // trigger on RISING edge during reset so we know when READY is re-asserted ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); ++ } ++ budget_ci->slot_status = SLOTSTATUS_RESET; ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); ++ msleep(1); ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ++ CICONTROL_RESET, 1, 0); ++ ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); ++ return 0; ++} ++ ++static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI); ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); ++ return 0; ++} ++ ++static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ int tmp; ++ ++ if (slot != 0) ++ return -EINVAL; ++ ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO); ++ ++ tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ++ tmp | CICONTROL_ENABLETS, 1, 0); ++ ++ ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); ++ return 0; ++} ++ ++static void ciintf_interrupt(unsigned long data) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) data; ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ unsigned int flags; ++ ++ // ensure we don't get spurious IRQs during initialisation ++ if (!budget_ci->budget.ci_present) ++ return; ++ ++ // read the CAM status ++ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); ++ if (flags & CICONTROL_CAMDETECT) { ++ ++ // GPIO should be set to trigger on falling edge if a CAM is present ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); ++ ++ if (budget_ci->slot_status & SLOTSTATUS_NONE) { ++ // CAM insertion IRQ ++ budget_ci->slot_status = SLOTSTATUS_PRESENT; ++ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, ++ DVB_CA_EN50221_CAMCHANGE_INSERTED); ++ ++ } else if (budget_ci->slot_status & SLOTSTATUS_RESET) { ++ // CAM ready (reset completed) ++ budget_ci->slot_status = SLOTSTATUS_READY; ++ dvb_ca_en50221_camready_irq(&budget_ci->ca, 0); ++ ++ } else if (budget_ci->slot_status & SLOTSTATUS_READY) { ++ // FR/DA IRQ ++ dvb_ca_en50221_frda_irq(&budget_ci->ca, 0); ++ } ++ } else { ++ ++ // trigger on rising edge if a CAM is not present - when a CAM is inserted, we ++ // only want to get the IRQ when it sets READY. If we trigger on the falling edge, ++ // the CAM might not actually be ready yet. ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); ++ ++ // generate a CAM removal IRQ if we haven't already ++ if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) { ++ // CAM removal IRQ ++ budget_ci->slot_status = SLOTSTATUS_NONE; ++ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, ++ DVB_CA_EN50221_CAMCHANGE_REMOVED); ++ } ++ } ++} ++ ++static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) ca->data; ++ unsigned int flags; ++ ++ // ensure we don't get spurious IRQs during initialisation ++ if (!budget_ci->budget.ci_present) ++ return -EINVAL; ++ ++ // read the CAM status ++ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); ++ if (flags & CICONTROL_CAMDETECT) { ++ // mark it as present if it wasn't before ++ if (budget_ci->slot_status & SLOTSTATUS_NONE) { ++ budget_ci->slot_status = SLOTSTATUS_PRESENT; ++ } ++ ++ // during a RESET, we check if we can read from IO memory to see when CAM is ready ++ if (budget_ci->slot_status & SLOTSTATUS_RESET) { ++ if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) { ++ budget_ci->slot_status = SLOTSTATUS_READY; ++ } ++ } ++ } else { ++ budget_ci->slot_status = SLOTSTATUS_NONE; ++ } ++ ++ if (budget_ci->slot_status != SLOTSTATUS_NONE) { ++ if (budget_ci->slot_status & SLOTSTATUS_READY) { ++ return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; ++ } ++ return DVB_CA_EN50221_POLL_CAM_PRESENT; ++ } ++ ++ return 0; ++} ++ ++static int ciintf_init(struct budget_ci *budget_ci) ++{ ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ int flags; ++ int result; ++ int ci_version; ++ int ca_flags; ++ ++ memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); ++ ++ // enable DEBI pins ++ saa7146_write(saa, MC1, MASK_27 | MASK_11); ++ ++ // test if it is there ++ ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); ++ if ((ci_version & 0xa0) != 0xa0) { ++ result = -ENODEV; ++ goto error; ++ } ++ ++ // determine whether a CAM is present or not ++ flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); ++ budget_ci->slot_status = SLOTSTATUS_NONE; ++ if (flags & CICONTROL_CAMDETECT) ++ budget_ci->slot_status = SLOTSTATUS_PRESENT; ++ ++ // version 0xa2 of the CI firmware doesn't generate interrupts ++ if (ci_version == 0xa2) { ++ ca_flags = 0; ++ budget_ci->ci_irq = 0; ++ } else { ++ ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | ++ DVB_CA_EN50221_FLAG_IRQ_FR | ++ DVB_CA_EN50221_FLAG_IRQ_DA; ++ budget_ci->ci_irq = 1; ++ } ++ ++ // register CI interface ++ budget_ci->ca.owner = THIS_MODULE; ++ budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; ++ budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem; ++ budget_ci->ca.read_cam_control = ciintf_read_cam_control; ++ budget_ci->ca.write_cam_control = ciintf_write_cam_control; ++ budget_ci->ca.slot_reset = ciintf_slot_reset; ++ budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; ++ budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; ++ budget_ci->ca.poll_slot_status = ciintf_poll_slot_status; ++ budget_ci->ca.data = budget_ci; ++ if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, ++ &budget_ci->ca, ++ ca_flags, 1)) != 0) { ++ printk("budget_ci: CI interface detected, but initialisation failed.\n"); ++ goto error; ++ } ++ ++ // Setup CI slot IRQ ++ if (budget_ci->ci_irq) { ++ tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); ++ if (budget_ci->slot_status != SLOTSTATUS_NONE) { ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); ++ } else { ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); ++ } ++ SAA7146_IER_ENABLE(saa, MASK_03); ++ } ++ ++ // enable interface ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ++ CICONTROL_RESET, 1, 0); ++ ++ // success! ++ printk("budget_ci: CI interface initialised\n"); ++ budget_ci->budget.ci_present = 1; ++ ++ // forge a fake CI IRQ so the CAM state is setup correctly ++ if (budget_ci->ci_irq) { ++ flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; ++ if (budget_ci->slot_status != SLOTSTATUS_NONE) ++ flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; ++ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); ++ } ++ ++ return 0; ++ ++error: ++ saa7146_write(saa, MC1, MASK_27); ++ return result; ++} ++ ++static void ciintf_deinit(struct budget_ci *budget_ci) ++{ ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ ++ // disable CI interrupts ++ if (budget_ci->ci_irq) { ++ SAA7146_IER_DISABLE(saa, MASK_03); ++ saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); ++ tasklet_kill(&budget_ci->ciintf_irq_tasklet); ++ } ++ ++ // reset interface ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); ++ msleep(1); ++ ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ++ CICONTROL_RESET, 1, 0); ++ ++ // disable TS data stream to CI interface ++ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT); ++ ++ // release the CA device ++ dvb_ca_en50221_release(&budget_ci->ca); ++ ++ // disable DEBI pins ++ saa7146_write(saa, MC1, MASK_27); ++} ++ ++static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; ++ ++ dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); ++ ++ if (*isr & MASK_06) ++ tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); ++ ++ if (*isr & MASK_10) ++ ttpci_budget_irq10_handler(dev, isr); ++ ++ if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq)) ++ tasklet_schedule(&budget_ci->ciintf_irq_tasklet); ++} ++ ++static u8 philips_su1278_tt_inittab[] = { ++ 0x01, 0x0f, ++ 0x02, 0x30, ++ 0x03, 0x00, ++ 0x04, 0x5b, ++ 0x05, 0x85, ++ 0x06, 0x02, ++ 0x07, 0x00, ++ 0x08, 0x02, ++ 0x09, 0x00, ++ 0x0C, 0x01, ++ 0x0D, 0x81, ++ 0x0E, 0x44, ++ 0x0f, 0x14, ++ 0x10, 0x3c, ++ 0x11, 0x84, ++ 0x12, 0xda, ++ 0x13, 0x97, ++ 0x14, 0x95, ++ 0x15, 0xc9, ++ 0x16, 0x19, ++ 0x17, 0x8c, ++ 0x18, 0x59, ++ 0x19, 0xf8, ++ 0x1a, 0xfe, ++ 0x1c, 0x7f, ++ 0x1d, 0x00, ++ 0x1e, 0x00, ++ 0x1f, 0x50, ++ 0x20, 0x00, ++ 0x21, 0x00, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x28, 0x00, ++ 0x29, 0x28, ++ 0x2a, 0x14, ++ 0x2b, 0x0f, ++ 0x2c, 0x09, ++ 0x2d, 0x09, ++ 0x31, 0x1f, ++ 0x32, 0x19, ++ 0x33, 0xfc, ++ 0x34, 0x93, ++ 0xff, 0xff ++}; ++ ++static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) ++{ ++ stv0299_writereg(fe, 0x0e, 0x44); ++ if (srate >= 10000000) { ++ stv0299_writereg(fe, 0x13, 0x97); ++ stv0299_writereg(fe, 0x14, 0x95); ++ stv0299_writereg(fe, 0x15, 0xc9); ++ stv0299_writereg(fe, 0x17, 0x8c); ++ stv0299_writereg(fe, 0x1a, 0xfe); ++ stv0299_writereg(fe, 0x1c, 0x7f); ++ stv0299_writereg(fe, 0x2d, 0x09); ++ } else { ++ stv0299_writereg(fe, 0x13, 0x99); ++ stv0299_writereg(fe, 0x14, 0x8d); ++ stv0299_writereg(fe, 0x15, 0xce); ++ stv0299_writereg(fe, 0x17, 0x43); ++ stv0299_writereg(fe, 0x1a, 0x1d); ++ stv0299_writereg(fe, 0x1c, 0x12); ++ stv0299_writereg(fe, 0x2d, 0x05); ++ } ++ stv0299_writereg(fe, 0x0e, 0x23); ++ stv0299_writereg(fe, 0x0f, 0x94); ++ stv0299_writereg(fe, 0x10, 0x39); ++ stv0299_writereg(fe, 0x15, 0xc9); ++ ++ stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); ++ stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); ++ stv0299_writereg(fe, 0x21, (ratio) & 0xf0); ++ ++ return 0; ++} ++ ++static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; ++ u32 div; ++ u8 buf[4]; ++ struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; ++ ++ if ((p->frequency < 950000) || (p->frequency > 2150000)) ++ return -EINVAL; ++ ++ div = (p->frequency + (500 - 1)) / 500; /* round correctly */ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2; ++ buf[3] = 0x20; ++ ++ if (p->symbol_rate < 4000000) ++ buf[3] |= 1; ++ ++ if (p->frequency < 1250000) ++ buf[3] |= 0; ++ else if (p->frequency < 1550000) ++ buf[3] |= 0x40; ++ else if (p->frequency < 2050000) ++ buf[3] |= 0x80; ++ else if (p->frequency < 2150000) ++ buf[3] |= 0xC0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct stv0299_config philips_su1278_tt_config = { ++ ++ .demod_address = 0x68, ++ .inittab = philips_su1278_tt_inittab, ++ .mclk = 64000000UL, ++ .invert = 0, ++ .skip_reinit = 1, ++ .lock_output = STV0299_LOCKOUTPUT_1, ++ .volt13_op0_op1 = STV0299_VOLT13_OP1, ++ .min_delay_ms = 50, ++ .set_symbol_rate = philips_su1278_tt_set_symbol_rate, ++}; ++ ++ ++ ++static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; ++ static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab }; ++ static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 }; ++ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len = ++ sizeof(td1316_init) }; ++ ++ // setup PLL configuration ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ msleep(1); ++ ++ // disable the mc44BC374c (do not check for errors) ++ tuner_msg.addr = 0x65; ++ tuner_msg.buf = disable_mc44BC374c; ++ tuner_msg.len = sizeof(disable_mc44BC374c); ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) { ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1); ++ } ++ ++ return 0; ++} ++ ++static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; ++ u8 tuner_buf[4]; ++ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; ++ int tuner_frequency = 0; ++ u8 band, cp, filter; ++ ++ // determine charge pump ++ tuner_frequency = p->frequency + 36130000; ++ if (tuner_frequency < 87000000) ++ return -EINVAL; ++ else if (tuner_frequency < 130000000) ++ cp = 3; ++ else if (tuner_frequency < 160000000) ++ cp = 5; ++ else if (tuner_frequency < 200000000) ++ cp = 6; ++ else if (tuner_frequency < 290000000) ++ cp = 3; ++ else if (tuner_frequency < 420000000) ++ cp = 5; ++ else if (tuner_frequency < 480000000) ++ cp = 6; ++ else if (tuner_frequency < 620000000) ++ cp = 3; ++ else if (tuner_frequency < 830000000) ++ cp = 5; ++ else if (tuner_frequency < 895000000) ++ cp = 7; ++ else ++ return -EINVAL; ++ ++ // determine band ++ if (p->frequency < 49000000) ++ return -EINVAL; ++ else if (p->frequency < 159000000) ++ band = 1; ++ else if (p->frequency < 444000000) ++ band = 2; ++ else if (p->frequency < 861000000) ++ band = 4; ++ else ++ return -EINVAL; ++ ++ // setup PLL filter and TDA9889 ++ switch (p->bandwidth_hz) { ++ case 6000000: ++ tda1004x_writereg(fe, 0x0C, 0x14); ++ filter = 0; ++ break; ++ ++ case 7000000: ++ tda1004x_writereg(fe, 0x0C, 0x80); ++ filter = 0; ++ break; ++ ++ case 8000000: ++ tda1004x_writereg(fe, 0x0C, 0x14); ++ filter = 1; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ // calculate divisor ++ // ((36130000+((1000000/6)/2)) + Finput)/(1000000/6) ++ tuner_frequency = (((p->frequency / 1000) * 6) + 217280) / 1000; ++ ++ // setup tuner buffer ++ tuner_buf[0] = tuner_frequency >> 8; ++ tuner_buf[1] = tuner_frequency & 0xff; ++ tuner_buf[2] = 0xca; ++ tuner_buf[3] = (cp << 5) | (filter << 3) | band; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ ++ msleep(1); ++ return 0; ++} ++ ++static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe, ++ const struct firmware **fw, char *name) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; ++ ++ return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev); ++} ++ ++static struct tda1004x_config philips_tdm1316l_config = { ++ ++ .demod_address = 0x8, ++ .invert = 0, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .request_firmware = philips_tdm1316l_request_firmware, ++}; ++ ++static struct tda1004x_config philips_tdm1316l_config_invert = { ++ ++ .demod_address = 0x8, ++ .invert = 1, ++ .invert_oclk = 0, ++ .xtal_freq = TDA10046_XTAL_4M, ++ .agc_config = TDA10046_AGC_DEFAULT, ++ .if_freq = TDA10046_FREQ_3617, ++ .request_firmware = philips_tdm1316l_request_firmware, ++}; ++ ++static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; ++ u8 tuner_buf[5]; ++ struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address, ++ .flags = 0, ++ .buf = tuner_buf, ++ .len = sizeof(tuner_buf) }; ++ int tuner_frequency = 0; ++ u8 band, cp, filter; ++ ++ // determine charge pump ++ tuner_frequency = p->frequency + 36125000; ++ if (tuner_frequency < 87000000) ++ return -EINVAL; ++ else if (tuner_frequency < 130000000) { ++ cp = 3; ++ band = 1; ++ } else if (tuner_frequency < 160000000) { ++ cp = 5; ++ band = 1; ++ } else if (tuner_frequency < 200000000) { ++ cp = 6; ++ band = 1; ++ } else if (tuner_frequency < 290000000) { ++ cp = 3; ++ band = 2; ++ } else if (tuner_frequency < 420000000) { ++ cp = 5; ++ band = 2; ++ } else if (tuner_frequency < 480000000) { ++ cp = 6; ++ band = 2; ++ } else if (tuner_frequency < 620000000) { ++ cp = 3; ++ band = 4; ++ } else if (tuner_frequency < 830000000) { ++ cp = 5; ++ band = 4; ++ } else if (tuner_frequency < 895000000) { ++ cp = 7; ++ band = 4; ++ } else ++ return -EINVAL; ++ ++ // assume PLL filter should always be 8MHz for the moment. ++ filter = 1; ++ ++ // calculate divisor ++ tuner_frequency = (p->frequency + 36125000 + (62500/2)) / 62500; ++ ++ // setup tuner buffer ++ tuner_buf[0] = tuner_frequency >> 8; ++ tuner_buf[1] = tuner_frequency & 0xff; ++ tuner_buf[2] = 0xc8; ++ tuner_buf[3] = (cp << 5) | (filter << 3) | band; ++ tuner_buf[4] = 0x80; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ ++ msleep(50); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) ++ return -EIO; ++ ++ msleep(1); ++ ++ return 0; ++} ++ ++static u8 dvbc_philips_tdm1316l_inittab[] = { ++ 0x80, 0x01, ++ 0x80, 0x00, ++ 0x81, 0x01, ++ 0x81, 0x00, ++ 0x00, 0x09, ++ 0x01, 0x69, ++ 0x03, 0x00, ++ 0x04, 0x00, ++ 0x07, 0x00, ++ 0x08, 0x00, ++ 0x20, 0x00, ++ 0x21, 0x40, ++ 0x22, 0x00, ++ 0x23, 0x00, ++ 0x24, 0x40, ++ 0x25, 0x88, ++ 0x30, 0xff, ++ 0x31, 0x00, ++ 0x32, 0xff, ++ 0x33, 0x00, ++ 0x34, 0x50, ++ 0x35, 0x7f, ++ 0x36, 0x00, ++ 0x37, 0x20, ++ 0x38, 0x00, ++ 0x40, 0x1c, ++ 0x41, 0xff, ++ 0x42, 0x29, ++ 0x43, 0x20, ++ 0x44, 0xff, ++ 0x45, 0x00, ++ 0x46, 0x00, ++ 0x49, 0x04, ++ 0x4a, 0x00, ++ 0x4b, 0x7b, ++ 0x52, 0x30, ++ 0x55, 0xae, ++ 0x56, 0x47, ++ 0x57, 0xe1, ++ 0x58, 0x3a, ++ 0x5a, 0x1e, ++ 0x5b, 0x34, ++ 0x60, 0x00, ++ 0x63, 0x00, ++ 0x64, 0x00, ++ 0x65, 0x00, ++ 0x66, 0x00, ++ 0x67, 0x00, ++ 0x68, 0x00, ++ 0x69, 0x00, ++ 0x6a, 0x02, ++ 0x6b, 0x00, ++ 0x70, 0xff, ++ 0x71, 0x00, ++ 0x72, 0x00, ++ 0x73, 0x00, ++ 0x74, 0x0c, ++ 0x80, 0x00, ++ 0x81, 0x00, ++ 0x82, 0x00, ++ 0x83, 0x00, ++ 0x84, 0x04, ++ 0x85, 0x80, ++ 0x86, 0x24, ++ 0x87, 0x78, ++ 0x88, 0x10, ++ 0x89, 0x00, ++ 0x90, 0x01, ++ 0x91, 0x01, ++ 0xa0, 0x04, ++ 0xa1, 0x00, ++ 0xa2, 0x00, ++ 0xb0, 0x91, ++ 0xb1, 0x0b, ++ 0xc0, 0x53, ++ 0xc1, 0x70, ++ 0xc2, 0x12, ++ 0xd0, 0x00, ++ 0xd1, 0x00, ++ 0xd2, 0x00, ++ 0xd3, 0x00, ++ 0xd4, 0x00, ++ 0xd5, 0x00, ++ 0xde, 0x00, ++ 0xdf, 0x00, ++ 0x61, 0x38, ++ 0x62, 0x0a, ++ 0x53, 0x13, ++ 0x59, 0x08, ++ 0xff, 0xff, ++}; ++ ++static struct stv0297_config dvbc_philips_tdm1316l_config = { ++ .demod_address = 0x1c, ++ .inittab = dvbc_philips_tdm1316l_inittab, ++ .invert = 0, ++ .stop_during_read = 1, ++}; ++ ++static struct tda10023_config tda10023_config = { ++ .demod_address = 0xc, ++ .invert = 0, ++ .xtal = 16000000, ++ .pll_m = 11, ++ .pll_p = 3, ++ .pll_n = 1, ++ .deltaf = 0xa511, ++}; ++ ++static struct tda827x_config tda827x_config = { ++ .config = 0, ++}; ++ ++/* TT S2-3200 DVB-S (STB0899) Inittab */ ++static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = { ++ ++ { STB0899_DEV_ID , 0x81 }, ++ { STB0899_DISCNTRL1 , 0x32 }, ++ { STB0899_DISCNTRL2 , 0x80 }, ++ { STB0899_DISRX_ST0 , 0x04 }, ++ { STB0899_DISRX_ST1 , 0x00 }, ++ { STB0899_DISPARITY , 0x00 }, ++ { STB0899_DISSTATUS , 0x20 }, ++ { STB0899_DISF22 , 0x8c }, ++ { STB0899_DISF22RX , 0x9a }, ++ { STB0899_SYSREG , 0x0b }, ++ { STB0899_ACRPRESC , 0x11 }, ++ { STB0899_ACRDIV1 , 0x0a }, ++ { STB0899_ACRDIV2 , 0x05 }, ++ { STB0899_DACR1 , 0x00 }, ++ { STB0899_DACR2 , 0x00 }, ++ { STB0899_OUTCFG , 0x00 }, ++ { STB0899_MODECFG , 0x00 }, ++ { STB0899_IRQSTATUS_3 , 0x30 }, ++ { STB0899_IRQSTATUS_2 , 0x00 }, ++ { STB0899_IRQSTATUS_1 , 0x00 }, ++ { STB0899_IRQSTATUS_0 , 0x00 }, ++ { STB0899_IRQMSK_3 , 0xf3 }, ++ { STB0899_IRQMSK_2 , 0xfc }, ++ { STB0899_IRQMSK_1 , 0xff }, ++ { STB0899_IRQMSK_0 , 0xff }, ++ { STB0899_IRQCFG , 0x00 }, ++ { STB0899_I2CCFG , 0x88 }, ++ { STB0899_I2CRPT , 0x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */ ++ { STB0899_IOPVALUE5 , 0x00 }, ++ { STB0899_IOPVALUE4 , 0x20 }, ++ { STB0899_IOPVALUE3 , 0xc9 }, ++ { STB0899_IOPVALUE2 , 0x90 }, ++ { STB0899_IOPVALUE1 , 0x40 }, ++ { STB0899_IOPVALUE0 , 0x00 }, ++ { STB0899_GPIO00CFG , 0x82 }, ++ { STB0899_GPIO01CFG , 0x82 }, ++ { STB0899_GPIO02CFG , 0x82 }, ++ { STB0899_GPIO03CFG , 0x82 }, ++ { STB0899_GPIO04CFG , 0x82 }, ++ { STB0899_GPIO05CFG , 0x82 }, ++ { STB0899_GPIO06CFG , 0x82 }, ++ { STB0899_GPIO07CFG , 0x82 }, ++ { STB0899_GPIO08CFG , 0x82 }, ++ { STB0899_GPIO09CFG , 0x82 }, ++ { STB0899_GPIO10CFG , 0x82 }, ++ { STB0899_GPIO11CFG , 0x82 }, ++ { STB0899_GPIO12CFG , 0x82 }, ++ { STB0899_GPIO13CFG , 0x82 }, ++ { STB0899_GPIO14CFG , 0x82 }, ++ { STB0899_GPIO15CFG , 0x82 }, ++ { STB0899_GPIO16CFG , 0x82 }, ++ { STB0899_GPIO17CFG , 0x82 }, ++ { STB0899_GPIO18CFG , 0x82 }, ++ { STB0899_GPIO19CFG , 0x82 }, ++ { STB0899_GPIO20CFG , 0x82 }, ++ { STB0899_SDATCFG , 0xb8 }, ++ { STB0899_SCLTCFG , 0xba }, ++ { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ ++ { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ ++ { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ ++ { STB0899_DIRCLKCFG , 0x82 }, ++ { STB0899_CLKOUT27CFG , 0x7e }, ++ { STB0899_STDBYCFG , 0x82 }, ++ { STB0899_CS0CFG , 0x82 }, ++ { STB0899_CS1CFG , 0x82 }, ++ { STB0899_DISEQCOCFG , 0x20 }, ++ { STB0899_GPIO32CFG , 0x82 }, ++ { STB0899_GPIO33CFG , 0x82 }, ++ { STB0899_GPIO34CFG , 0x82 }, ++ { STB0899_GPIO35CFG , 0x82 }, ++ { STB0899_GPIO36CFG , 0x82 }, ++ { STB0899_GPIO37CFG , 0x82 }, ++ { STB0899_GPIO38CFG , 0x82 }, ++ { STB0899_GPIO39CFG , 0x82 }, ++ { STB0899_NCOARSE , 0x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ ++ { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ ++ { STB0899_FILTCTRL , 0x00 }, ++ { STB0899_SYSCTRL , 0x00 }, ++ { STB0899_STOPCLK1 , 0x20 }, ++ { STB0899_STOPCLK2 , 0x00 }, ++ { STB0899_INTBUFSTATUS , 0x00 }, ++ { STB0899_INTBUFCTRL , 0x0a }, ++ { 0xffff , 0xff }, ++}; ++ ++static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { ++ { STB0899_DEMOD , 0x00 }, ++ { STB0899_RCOMPC , 0xc9 }, ++ { STB0899_AGC1CN , 0x41 }, ++ { STB0899_AGC1REF , 0x10 }, ++ { STB0899_RTC , 0x7a }, ++ { STB0899_TMGCFG , 0x4e }, ++ { STB0899_AGC2REF , 0x34 }, ++ { STB0899_TLSR , 0x84 }, ++ { STB0899_CFD , 0xc7 }, ++ { STB0899_ACLC , 0x87 }, ++ { STB0899_BCLC , 0x94 }, ++ { STB0899_EQON , 0x41 }, ++ { STB0899_LDT , 0xdd }, ++ { STB0899_LDT2 , 0xc9 }, ++ { STB0899_EQUALREF , 0xb4 }, ++ { STB0899_TMGRAMP , 0x10 }, ++ { STB0899_TMGTHD , 0x30 }, ++ { STB0899_IDCCOMP , 0xfb }, ++ { STB0899_QDCCOMP , 0x03 }, ++ { STB0899_POWERI , 0x3b }, ++ { STB0899_POWERQ , 0x3d }, ++ { STB0899_RCOMP , 0x81 }, ++ { STB0899_AGCIQIN , 0x80 }, ++ { STB0899_AGC2I1 , 0x04 }, ++ { STB0899_AGC2I2 , 0xf5 }, ++ { STB0899_TLIR , 0x25 }, ++ { STB0899_RTF , 0x80 }, ++ { STB0899_DSTATUS , 0x00 }, ++ { STB0899_LDI , 0xca }, ++ { STB0899_CFRM , 0xf1 }, ++ { STB0899_CFRL , 0xf3 }, ++ { STB0899_NIRM , 0x2a }, ++ { STB0899_NIRL , 0x05 }, ++ { STB0899_ISYMB , 0x17 }, ++ { STB0899_QSYMB , 0xfa }, ++ { STB0899_SFRH , 0x2f }, ++ { STB0899_SFRM , 0x68 }, ++ { STB0899_SFRL , 0x40 }, ++ { STB0899_SFRUPH , 0x2f }, ++ { STB0899_SFRUPM , 0x68 }, ++ { STB0899_SFRUPL , 0x40 }, ++ { STB0899_EQUAI1 , 0xfd }, ++ { STB0899_EQUAQ1 , 0x04 }, ++ { STB0899_EQUAI2 , 0x0f }, ++ { STB0899_EQUAQ2 , 0xff }, ++ { STB0899_EQUAI3 , 0xdf }, ++ { STB0899_EQUAQ3 , 0xfa }, ++ { STB0899_EQUAI4 , 0x37 }, ++ { STB0899_EQUAQ4 , 0x0d }, ++ { STB0899_EQUAI5 , 0xbd }, ++ { STB0899_EQUAQ5 , 0xf7 }, ++ { STB0899_DSTATUS2 , 0x00 }, ++ { STB0899_VSTATUS , 0x00 }, ++ { STB0899_VERROR , 0xff }, ++ { STB0899_IQSWAP , 0x2a }, ++ { STB0899_ECNT1M , 0x00 }, ++ { STB0899_ECNT1L , 0x00 }, ++ { STB0899_ECNT2M , 0x00 }, ++ { STB0899_ECNT2L , 0x00 }, ++ { STB0899_ECNT3M , 0x00 }, ++ { STB0899_ECNT3L , 0x00 }, ++ { STB0899_FECAUTO1 , 0x06 }, ++ { STB0899_FECM , 0x01 }, ++ { STB0899_VTH12 , 0xf0 }, ++ { STB0899_VTH23 , 0xa0 }, ++ { STB0899_VTH34 , 0x78 }, ++ { STB0899_VTH56 , 0x4e }, ++ { STB0899_VTH67 , 0x48 }, ++ { STB0899_VTH78 , 0x38 }, ++ { STB0899_PRVIT , 0xff }, ++ { STB0899_VITSYNC , 0x19 }, ++ { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ ++ { STB0899_TSULC , 0x42 }, ++ { STB0899_RSLLC , 0x40 }, ++ { STB0899_TSLPL , 0x12 }, ++ { STB0899_TSCFGH , 0x0c }, ++ { STB0899_TSCFGM , 0x00 }, ++ { STB0899_TSCFGL , 0x0c }, ++ { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ ++ { STB0899_RSSYNCDEL , 0x00 }, ++ { STB0899_TSINHDELH , 0x02 }, ++ { STB0899_TSINHDELM , 0x00 }, ++ { STB0899_TSINHDELL , 0x00 }, ++ { STB0899_TSLLSTKM , 0x00 }, ++ { STB0899_TSLLSTKL , 0x00 }, ++ { STB0899_TSULSTKM , 0x00 }, ++ { STB0899_TSULSTKL , 0xab }, ++ { STB0899_PCKLENUL , 0x00 }, ++ { STB0899_PCKLENLL , 0xcc }, ++ { STB0899_RSPCKLEN , 0xcc }, ++ { STB0899_TSSTATUS , 0x80 }, ++ { STB0899_ERRCTRL1 , 0xb6 }, ++ { STB0899_ERRCTRL2 , 0x96 }, ++ { STB0899_ERRCTRL3 , 0x89 }, ++ { STB0899_DMONMSK1 , 0x27 }, ++ { STB0899_DMONMSK0 , 0x03 }, ++ { STB0899_DEMAPVIT , 0x5c }, ++ { STB0899_PLPARM , 0x1f }, ++ { STB0899_PDELCTRL , 0x48 }, ++ { STB0899_PDELCTRL2 , 0x00 }, ++ { STB0899_BBHCTRL1 , 0x00 }, ++ { STB0899_BBHCTRL2 , 0x00 }, ++ { STB0899_HYSTTHRESH , 0x77 }, ++ { STB0899_MATCSTM , 0x00 }, ++ { STB0899_MATCSTL , 0x00 }, ++ { STB0899_UPLCSTM , 0x00 }, ++ { STB0899_UPLCSTL , 0x00 }, ++ { STB0899_DFLCSTM , 0x00 }, ++ { STB0899_DFLCSTL , 0x00 }, ++ { STB0899_SYNCCST , 0x00 }, ++ { STB0899_SYNCDCSTM , 0x00 }, ++ { STB0899_SYNCDCSTL , 0x00 }, ++ { STB0899_ISI_ENTRY , 0x00 }, ++ { STB0899_ISI_BIT_EN , 0x00 }, ++ { STB0899_MATSTRM , 0x00 }, ++ { STB0899_MATSTRL , 0x00 }, ++ { STB0899_UPLSTRM , 0x00 }, ++ { STB0899_UPLSTRL , 0x00 }, ++ { STB0899_DFLSTRM , 0x00 }, ++ { STB0899_DFLSTRL , 0x00 }, ++ { STB0899_SYNCSTR , 0x00 }, ++ { STB0899_SYNCDSTRM , 0x00 }, ++ { STB0899_SYNCDSTRL , 0x00 }, ++ { STB0899_CFGPDELSTATUS1 , 0x10 }, ++ { STB0899_CFGPDELSTATUS2 , 0x00 }, ++ { STB0899_BBFERRORM , 0x00 }, ++ { STB0899_BBFERRORL , 0x00 }, ++ { STB0899_UPKTERRORM , 0x00 }, ++ { STB0899_UPKTERRORL , 0x00 }, ++ { 0xffff , 0xff }, ++}; ++ ++static struct stb0899_config tt3200_config = { ++ .init_dev = tt3200_stb0899_s1_init_1, ++ .init_s2_demod = stb0899_s2_init_2, ++ .init_s1_demod = tt3200_stb0899_s1_init_3, ++ .init_s2_fec = stb0899_s2_init_4, ++ .init_tst = stb0899_s1_init_5, ++ ++ .postproc = NULL, ++ ++ .demod_address = 0x68, ++ ++ .xtal_freq = 27000000, ++ .inversion = IQ_SWAP_ON, /* 1 */ ++ ++ .lo_clk = 76500000, ++ .hi_clk = 99000000, ++ ++ .esno_ave = STB0899_DVBS2_ESNO_AVE, ++ .esno_quant = STB0899_DVBS2_ESNO_QUANT, ++ .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, ++ .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, ++ .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, ++ .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, ++ .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, ++ .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, ++ .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, ++ ++ .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, ++ .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, ++ .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, ++ .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, ++ ++ .tuner_get_frequency = stb6100_get_frequency, ++ .tuner_set_frequency = stb6100_set_frequency, ++ .tuner_set_bandwidth = stb6100_set_bandwidth, ++ .tuner_get_bandwidth = stb6100_get_bandwidth, ++ .tuner_set_rfsiggain = NULL ++}; ++ ++static struct stb6100_config tt3200_stb6100_config = { ++ .tuner_address = 0x60, ++ .refclock = 27000000, ++}; ++ ++static void frontend_init(struct budget_ci *budget_ci) ++{ ++ switch (budget_ci->budget.dev->pci->subsystem_device) { ++ case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059)) ++ budget_ci->budget.dvb_frontend = ++ dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; ++ budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; ++ break; ++ } ++ break; ++ ++ case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059)) ++ budget_ci->budget.dvb_frontend = ++ dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params; ++ break; ++ } ++ break; ++ ++ case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt)) ++ budget_ci->tuner_pll_address = 0x61; ++ budget_ci->budget.dvb_frontend = ++ dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params; ++ break; ++ } ++ break; ++ ++ case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889) ++ budget_ci->tuner_pll_address = 0x63; ++ budget_ci->budget.dvb_frontend = ++ dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; ++ break; ++ } ++ break; ++ ++ case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) ++ budget_ci->tuner_pll_address = 0x60; ++ budget_ci->budget.dvb_frontend = ++ dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; ++ break; ++ } ++ break; ++ ++ case 0x1017: // TT S-1500 PCI ++ budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; ++ budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap; ++ ++ budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; ++ if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) { ++ printk("%s: No LNBP21 found!\n", __func__); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } ++ break; ++ ++ case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */ ++ budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48); ++ if (budget_ci->budget.dvb_frontend) { ++ if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) { ++ printk(KERN_ERR "%s: No tda827x found!\n", __func__); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } ++ break; ++ ++ case 0x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */ ++ budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0x63, &budget_ci->budget.i2c_adap)) { ++ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { ++ printk(KERN_ERR "%s: No LNBP21 found!\n", __func__); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } else { ++ printk(KERN_ERR "%s: No STB6000 found!\n", __func__); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } ++ break; ++ ++ case 0x1019: // TT S2-3200 PCI ++ /* ++ * NOTE! on some STB0899 versions, the internal PLL takes a longer time ++ * to settle, aka LOCK. On the older revisions of the chip, we don't see ++ * this, as a result on the newer chips the entire clock tree, will not ++ * be stable after a freshly POWER 'ed up situation. ++ * In this case, we should RESET the STB0899 (Active LOW) and wait for ++ * PLL stabilization. ++ * ++ * On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is ++ * connected to the SAA7146 GPIO, GPIO2, Pin 142 ++ */ ++ /* Reset Demodulator */ ++ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTLO); ++ /* Wait for everything to die */ ++ msleep(50); ++ /* Pull it up out of Reset state */ ++ saa7146_setgpio(budget_ci->budget.dev, 2, SAA7146_GPIO_OUTHI); ++ /* Wait for PLL to stabilize */ ++ msleep(250); ++ /* ++ * PLL state should be stable now. Ideally, we should check ++ * for PLL LOCK status. But well, never mind! ++ */ ++ budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap); ++ if (budget_ci->budget.dvb_frontend) { ++ if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) { ++ if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0, 0)) { ++ printk("%s: No LNBP21 found!\n", __func__); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } else { ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } ++ break; ++ ++ } ++ ++ if (budget_ci->budget.dvb_frontend == NULL) { ++ printk("budget-ci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ budget_ci->budget.dev->pci->vendor, ++ budget_ci->budget.dev->pci->device, ++ budget_ci->budget.dev->pci->subsystem_vendor, ++ budget_ci->budget.dev->pci->subsystem_device); ++ } else { ++ if (dvb_register_frontend ++ (&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) { ++ printk("budget-ci: Frontend registration failed!\n"); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ budget_ci->budget.dvb_frontend = NULL; ++ } ++ } ++} ++ ++static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) ++{ ++ struct budget_ci *budget_ci; ++ int err; ++ ++ budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); ++ if (!budget_ci) { ++ err = -ENOMEM; ++ goto out1; ++ } ++ ++ dprintk(2, "budget_ci: %p\n", budget_ci); ++ ++ dev->ext_priv = budget_ci; ++ ++ err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE, ++ adapter_nr); ++ if (err) ++ goto out2; ++ ++ err = msp430_ir_init(budget_ci); ++ if (err) ++ goto out3; ++ ++ ciintf_init(budget_ci); ++ ++ budget_ci->budget.dvb_adapter.priv = budget_ci; ++ frontend_init(budget_ci); ++ ++ ttpci_budget_init_hooks(&budget_ci->budget); ++ ++ return 0; ++ ++out3: ++ ttpci_budget_deinit(&budget_ci->budget); ++out2: ++ kfree(budget_ci); ++out1: ++ return err; ++} ++ ++static int budget_ci_detach(struct saa7146_dev *dev) ++{ ++ struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv; ++ struct saa7146_dev *saa = budget_ci->budget.dev; ++ int err; ++ ++ if (budget_ci->budget.ci_present) ++ ciintf_deinit(budget_ci); ++ msp430_ir_deinit(budget_ci); ++ if (budget_ci->budget.dvb_frontend) { ++ dvb_unregister_frontend(budget_ci->budget.dvb_frontend); ++ dvb_frontend_detach(budget_ci->budget.dvb_frontend); ++ } ++ err = ttpci_budget_deinit(&budget_ci->budget); ++ ++ // disable frontend and CI interface ++ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); ++ ++ kfree(budget_ci); ++ ++ return err; ++} ++ ++static struct saa7146_extension budget_extension; ++ ++MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC); ++MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI", BUDGET_TT); ++ ++static struct pci_device_id pci_tbl[] = { ++ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c), ++ MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f), ++ MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010), ++ MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011), ++ MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012), ++ MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017), ++ MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), ++ MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), ++ MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_extension budget_extension = { ++ .name = "budget_ci dvb", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .module = THIS_MODULE, ++ .pci_tbl = &pci_tbl[0], ++ .attach = budget_ci_attach, ++ .detach = budget_ci_detach, ++ ++ .irq_mask = MASK_03 | MASK_06 | MASK_10, ++ .irq_func = budget_ci_irq, ++}; ++ ++static int __init budget_ci_init(void) ++{ ++ return saa7146_register_extension(&budget_extension); ++} ++ ++static void __exit budget_ci_exit(void) ++{ ++ saa7146_unregister_extension(&budget_extension); ++} ++ ++module_init(budget_ci_init); ++module_exit(budget_ci_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others"); ++MODULE_DESCRIPTION("driver for the SAA7146 based so-called " ++ "budget PCI DVB cards w/ CI-module produced by " ++ "Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c +new file mode 100644 +index 0000000..37d02fe +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget-core.c +@@ -0,0 +1,602 @@ ++/* ++ * budget-core.c: driver for the SAA7146 based Budget DVB cards ++ * ++ * Compiled from various sources by Michael Hunold ++ * ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * 26feb2004 Support for FS Activy Card (Grundig tuner) by ++ * Michael Dreher , ++ * Oliver Endriss , ++ * Andreas 'randy' Weinberger ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++ ++#include "budget.h" ++#include "ttpci-eeprom.h" ++ ++#define TS_WIDTH (2 * TS_SIZE) ++#define TS_WIDTH_ACTIVY TS_SIZE ++#define TS_WIDTH_DVBC TS_SIZE ++#define TS_HEIGHT_MASK 0xf00 ++#define TS_HEIGHT_MASK_ACTIVY 0xc00 ++#define TS_HEIGHT_MASK_DVBC 0xe00 ++#define TS_MIN_BUFSIZE_K 188 ++#define TS_MAX_BUFSIZE_K 1410 ++#define TS_MAX_BUFSIZE_K_ACTIVY 564 ++#define TS_MAX_BUFSIZE_K_DVBC 1316 ++#define BUFFER_WARNING_WAIT (30*HZ) ++ ++int budget_debug; ++static int dma_buffer_size = TS_MIN_BUFSIZE_K; ++module_param_named(debug, budget_debug, int, 0644); ++module_param_named(bufsize, dma_buffer_size, int, 0444); ++MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); ++MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); ++ ++/**************************************************************************** ++ * TT budget / WinTV Nova ++ ****************************************************************************/ ++ ++static int stop_ts_capture(struct budget *budget) ++{ ++ dprintk(2, "budget: %p\n", budget); ++ ++ saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off ++ SAA7146_IER_DISABLE(budget->dev, MASK_10); ++ return 0; ++} ++ ++static int start_ts_capture(struct budget *budget) ++{ ++ struct saa7146_dev *dev = budget->dev; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ if (!budget->feeding || !budget->fe_synced) ++ return 0; ++ ++ saa7146_write(dev, MC1, MASK_20); // DMA3 off ++ ++ memset(budget->grabbing, 0x00, budget->buffer_size); ++ ++ saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); ++ ++ budget->ttbp = 0; ++ ++ /* ++ * Signal path on the Activy: ++ * ++ * tuner -> SAA7146 port A -> SAA7146 BRS -> SAA7146 DMA3 -> memory ++ * ++ * Since the tuner feeds 204 bytes packets into the SAA7146, ++ * DMA3 is configured to strip the trailing 16 FEC bytes: ++ * Pitch: 188, NumBytes3: 188, NumLines3: 1024 ++ */ ++ ++ switch(budget->card->type) { ++ case BUDGET_FS_ACTIVY: ++ saa7146_write(dev, DD1_INIT, 0x04000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25)); ++ saa7146_write(dev, BRS_CTRL, 0x00000000); ++ break; ++ case BUDGET_PATCH: ++ saa7146_write(dev, DD1_INIT, 0x00000200); ++ saa7146_write(dev, MC2, (MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x60000000); ++ break; ++ case BUDGET_CIN1200C_MK3: ++ case BUDGET_KNC1C_MK3: ++ case BUDGET_KNC1C_TDA10024: ++ case BUDGET_KNC1CP_MK3: ++ if (budget->video_port == BUDGET_VIDEO_PORTA) { ++ saa7146_write(dev, DD1_INIT, 0x06000200); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x00000000); ++ } else { ++ saa7146_write(dev, DD1_INIT, 0x00000600); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x60000000); ++ } ++ break; ++ default: ++ if (budget->video_port == BUDGET_VIDEO_PORTA) { ++ saa7146_write(dev, DD1_INIT, 0x06000200); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x00000000); ++ } else { ++ saa7146_write(dev, DD1_INIT, 0x02000600); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ saa7146_write(dev, BRS_CTRL, 0x60000000); ++ } ++ } ++ ++ saa7146_write(dev, MC2, (MASK_08 | MASK_24)); ++ mdelay(10); ++ ++ saa7146_write(dev, BASE_ODD3, 0); ++ if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { ++ // using odd/even buffers ++ saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); ++ } else { ++ // using a single buffer ++ saa7146_write(dev, BASE_EVEN3, 0); ++ } ++ saa7146_write(dev, PROT_ADDR3, budget->buffer_size); ++ saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); ++ ++ saa7146_write(dev, PITCH3, budget->buffer_width); ++ saa7146_write(dev, NUM_LINE_BYTE3, ++ (budget->buffer_height << 16) | budget->buffer_width); ++ ++ saa7146_write(dev, MC2, (MASK_04 | MASK_20)); ++ ++ SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ ++ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ ++ saa7146_write(dev, MC1, (MASK_04 | MASK_20)); /* DMA3 on */ ++ ++ return 0; ++} ++ ++static int budget_read_fe_status(struct dvb_frontend *fe, fe_status_t *status) ++{ ++ struct budget *budget = (struct budget *) fe->dvb->priv; ++ int synced; ++ int ret; ++ ++ if (budget->read_fe_status) ++ ret = budget->read_fe_status(fe, status); ++ else ++ ret = -EINVAL; ++ ++ if (!ret) { ++ synced = (*status & FE_HAS_LOCK); ++ if (synced != budget->fe_synced) { ++ budget->fe_synced = synced; ++ spin_lock(&budget->feedlock); ++ if (synced) ++ start_ts_capture(budget); ++ else ++ stop_ts_capture(budget); ++ spin_unlock(&budget->feedlock); ++ } ++ } ++ return ret; ++} ++ ++static void vpeirq(unsigned long data) ++{ ++ struct budget *budget = (struct budget *) data; ++ u8 *mem = (u8 *) (budget->grabbing); ++ u32 olddma = budget->ttbp; ++ u32 newdma = saa7146_read(budget->dev, PCI_VDP3); ++ u32 count; ++ ++ /* Ensure streamed PCI data is synced to CPU */ ++ pci_dma_sync_sg_for_cpu(budget->dev->pci, budget->pt.slist, budget->pt.nents, PCI_DMA_FROMDEVICE); ++ ++ /* nearest lower position divisible by 188 */ ++ newdma -= newdma % 188; ++ ++ if (newdma >= budget->buffer_size) ++ return; ++ ++ budget->ttbp = newdma; ++ ++ if (budget->feeding == 0 || newdma == olddma) ++ return; ++ ++ if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ ++ count = newdma - olddma; ++ dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); ++ } else { /* wraparound, dump olddma..buflen and 0..newdma */ ++ count = budget->buffer_size - olddma; ++ dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); ++ count += newdma; ++ dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); ++ } ++ ++ if (count > budget->buffer_warning_threshold) ++ budget->buffer_warnings++; ++ ++ if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { ++ printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", ++ budget->dev->name, __func__, budget->buffer_warnings, count); ++ budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; ++ budget->buffer_warnings = 0; ++ } ++} ++ ++ ++int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, ++ int uselocks, int nobusyloop) ++{ ++ struct saa7146_dev *saa = budget->dev; ++ int result = 0; ++ unsigned long flags = 0; ++ ++ if (count > 4 || count <= 0) ++ return 0; ++ ++ if (uselocks) ++ spin_lock_irqsave(&budget->debilock, flags); ++ ++ if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ return result; ++ } ++ ++ saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); ++ saa7146_write(saa, DEBI_CONFIG, config); ++ saa7146_write(saa, DEBI_PAGE, 0); ++ saa7146_write(saa, MC2, (2 << 16) | 2); ++ ++ if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ return result; ++ } ++ ++ result = saa7146_read(saa, DEBI_AD); ++ result &= (0xffffffffUL >> ((4 - count) * 8)); ++ ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ ++ return result; ++} ++ ++int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, ++ int count, u32 value, int uselocks, int nobusyloop) ++{ ++ struct saa7146_dev *saa = budget->dev; ++ unsigned long flags = 0; ++ int result; ++ ++ if (count > 4 || count <= 0) ++ return 0; ++ ++ if (uselocks) ++ spin_lock_irqsave(&budget->debilock, flags); ++ ++ if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ return result; ++ } ++ ++ saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); ++ saa7146_write(saa, DEBI_CONFIG, config); ++ saa7146_write(saa, DEBI_PAGE, 0); ++ saa7146_write(saa, DEBI_AD, value); ++ saa7146_write(saa, MC2, (2 << 16) | 2); ++ ++ if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ return result; ++ } ++ ++ if (uselocks) ++ spin_unlock_irqrestore(&budget->debilock, flags); ++ return 0; ++} ++ ++ ++/**************************************************************************** ++ * DVB API SECTION ++ ****************************************************************************/ ++ ++static int budget_start_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct budget *budget = (struct budget *) demux->priv; ++ int status = 0; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ if (!demux->dmx.frontend) ++ return -EINVAL; ++ ++ spin_lock(&budget->feedlock); ++ feed->pusi_seen = 0; /* have a clean section start */ ++ if (budget->feeding++ == 0) ++ status = start_ts_capture(budget); ++ spin_unlock(&budget->feedlock); ++ return status; ++} ++ ++static int budget_stop_feed(struct dvb_demux_feed *feed) ++{ ++ struct dvb_demux *demux = feed->demux; ++ struct budget *budget = (struct budget *) demux->priv; ++ int status = 0; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ spin_lock(&budget->feedlock); ++ if (--budget->feeding == 0) ++ status = stop_ts_capture(budget); ++ spin_unlock(&budget->feedlock); ++ return status; ++} ++ ++static int budget_register(struct budget *budget) ++{ ++ struct dvb_demux *dvbdemux = &budget->demux; ++ int ret; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ dvbdemux->priv = (void *) budget; ++ ++ dvbdemux->filternum = 256; ++ dvbdemux->feednum = 256; ++ dvbdemux->start_feed = budget_start_feed; ++ dvbdemux->stop_feed = budget_stop_feed; ++ dvbdemux->write_to_decoder = NULL; ++ ++ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | ++ DMX_MEMORY_BASED_FILTERING); ++ ++ dvb_dmx_init(&budget->demux); ++ ++ budget->dmxdev.filternum = 256; ++ budget->dmxdev.demux = &dvbdemux->dmx; ++ budget->dmxdev.capabilities = 0; ++ ++ dvb_dmxdev_init(&budget->dmxdev, &budget->dvb_adapter); ++ ++ budget->hw_frontend.source = DMX_FRONTEND_0; ++ ++ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend); ++ ++ if (ret < 0) ++ return ret; ++ ++ budget->mem_frontend.source = DMX_MEMORY_FE; ++ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->mem_frontend); ++ if (ret < 0) ++ return ret; ++ ++ ret = dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, &budget->hw_frontend); ++ if (ret < 0) ++ return ret; ++ ++ dvb_net_init(&budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx); ++ ++ return 0; ++} ++ ++static void budget_unregister(struct budget *budget) ++{ ++ struct dvb_demux *dvbdemux = &budget->demux; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ dvb_net_release(&budget->dvb_net); ++ ++ dvbdemux->dmx.close(&dvbdemux->dmx); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend); ++ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend); ++ ++ dvb_dmxdev_release(&budget->dmxdev); ++ dvb_dmx_release(&budget->demux); ++} ++ ++int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, ++ struct saa7146_pci_extension_data *info, ++ struct module *owner, short *adapter_nums) ++{ ++ int ret = 0; ++ struct budget_info *bi = info->ext_priv; ++ int max_bufsize; ++ int height_mask; ++ ++ memset(budget, 0, sizeof(struct budget)); ++ ++ dprintk(2, "dev: %p, budget: %p\n", dev, budget); ++ ++ budget->card = bi; ++ budget->dev = (struct saa7146_dev *) dev; ++ ++ switch(budget->card->type) { ++ case BUDGET_FS_ACTIVY: ++ budget->buffer_width = TS_WIDTH_ACTIVY; ++ max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; ++ height_mask = TS_HEIGHT_MASK_ACTIVY; ++ break; ++ ++ case BUDGET_KNC1C: ++ case BUDGET_KNC1CP: ++ case BUDGET_CIN1200C: ++ case BUDGET_KNC1C_MK3: ++ case BUDGET_KNC1C_TDA10024: ++ case BUDGET_KNC1CP_MK3: ++ case BUDGET_CIN1200C_MK3: ++ budget->buffer_width = TS_WIDTH_DVBC; ++ max_bufsize = TS_MAX_BUFSIZE_K_DVBC; ++ height_mask = TS_HEIGHT_MASK_DVBC; ++ break; ++ ++ default: ++ budget->buffer_width = TS_WIDTH; ++ max_bufsize = TS_MAX_BUFSIZE_K; ++ height_mask = TS_HEIGHT_MASK; ++ } ++ ++ if (dma_buffer_size < TS_MIN_BUFSIZE_K) ++ dma_buffer_size = TS_MIN_BUFSIZE_K; ++ else if (dma_buffer_size > max_bufsize) ++ dma_buffer_size = max_bufsize; ++ ++ budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; ++ if (budget->buffer_height > 0xfff) { ++ budget->buffer_height /= 2; ++ budget->buffer_height &= height_mask; ++ budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; ++ } else { ++ budget->buffer_height &= height_mask; ++ budget->buffer_size = budget->buffer_height * budget->buffer_width; ++ } ++ budget->buffer_warning_threshold = budget->buffer_size * 80/100; ++ budget->buffer_warnings = 0; ++ budget->buffer_warning_time = jiffies; ++ ++ dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", ++ budget->dev->name, ++ budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", ++ budget->buffer_width, budget->buffer_height); ++ printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); ++ ++ ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, ++ owner, &budget->dev->pci->dev, adapter_nums); ++ if (ret < 0) ++ return ret; ++ ++ /* set dd1 stream a & b */ ++ saa7146_write(dev, DD1_STREAM_B, 0x00000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25)); ++ saa7146_write(dev, MC2, (MASK_10 | MASK_26)); ++ saa7146_write(dev, DD1_INIT, 0x02000000); ++ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); ++ ++ if (bi->type != BUDGET_FS_ACTIVY) ++ budget->video_port = BUDGET_VIDEO_PORTB; ++ else ++ budget->video_port = BUDGET_VIDEO_PORTA; ++ spin_lock_init(&budget->feedlock); ++ spin_lock_init(&budget->debilock); ++ ++ /* the Siemens DVB needs this if you want to have the i2c chips ++ get recognized before the main driver is loaded */ ++ if (bi->type != BUDGET_FS_ACTIVY) ++ saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */ ++ ++ strlcpy(budget->i2c_adap.name, budget->card->name, sizeof(budget->i2c_adap.name)); ++ ++ saa7146_i2c_adapter_prepare(dev, &budget->i2c_adap, SAA7146_I2C_BUS_BIT_RATE_120); ++ strcpy(budget->i2c_adap.name, budget->card->name); ++ ++ if (i2c_add_adapter(&budget->i2c_adap) < 0) { ++ ret = -ENOMEM; ++ goto err_dvb_unregister; ++ } ++ ++ ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); ++ ++ budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt); ++ if (NULL == budget->grabbing) { ++ ret = -ENOMEM; ++ goto err_del_i2c; ++ } ++ ++ saa7146_write(dev, PCI_BT_V1, 0x001c0000); ++ /* upload all */ ++ saa7146_write(dev, GPIO_CTRL, 0x000000); ++ ++ tasklet_init(&budget->vpe_tasklet, vpeirq, (unsigned long) budget); ++ ++ /* frontend power on */ ++ if (bi->type != BUDGET_FS_ACTIVY) ++ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); ++ ++ if ((ret = budget_register(budget)) == 0) ++ return 0; /* Everything OK */ ++ ++ /* An error occurred, cleanup resources */ ++ saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); ++ ++err_del_i2c: ++ i2c_del_adapter(&budget->i2c_adap); ++ ++err_dvb_unregister: ++ dvb_unregister_adapter(&budget->dvb_adapter); ++ ++ return ret; ++} ++ ++void ttpci_budget_init_hooks(struct budget *budget) ++{ ++ if (budget->dvb_frontend && !budget->read_fe_status) { ++ budget->read_fe_status = budget->dvb_frontend->ops.read_status; ++ budget->dvb_frontend->ops.read_status = budget_read_fe_status; ++ } ++} ++ ++int ttpci_budget_deinit(struct budget *budget) ++{ ++ struct saa7146_dev *dev = budget->dev; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ budget_unregister(budget); ++ ++ tasklet_kill(&budget->vpe_tasklet); ++ ++ saa7146_vfree_destroy_pgtable(dev->pci, budget->grabbing, &budget->pt); ++ ++ i2c_del_adapter(&budget->i2c_adap); ++ ++ dvb_unregister_adapter(&budget->dvb_adapter); ++ ++ return 0; ++} ++ ++void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr) ++{ ++ struct budget *budget = (struct budget *) dev->ext_priv; ++ ++ dprintk(8, "dev: %p, budget: %p\n", dev, budget); ++ ++ if (*isr & MASK_10) ++ tasklet_schedule(&budget->vpe_tasklet); ++} ++ ++void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port) ++{ ++ struct budget *budget = (struct budget *) dev->ext_priv; ++ ++ spin_lock(&budget->feedlock); ++ budget->video_port = video_port; ++ if (budget->feeding) { ++ stop_ts_capture(budget); ++ start_ts_capture(budget); ++ } ++ spin_unlock(&budget->feedlock); ++} ++ ++EXPORT_SYMBOL_GPL(ttpci_budget_debiread); ++EXPORT_SYMBOL_GPL(ttpci_budget_debiwrite); ++EXPORT_SYMBOL_GPL(ttpci_budget_init); ++EXPORT_SYMBOL_GPL(ttpci_budget_init_hooks); ++EXPORT_SYMBOL_GPL(ttpci_budget_deinit); ++EXPORT_SYMBOL_GPL(ttpci_budget_irq10_handler); ++EXPORT_SYMBOL_GPL(ttpci_budget_set_video_port); ++EXPORT_SYMBOL_GPL(budget_debug); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/ttpci/budget-patch.c b/drivers/media/pci/ttpci/budget-patch.c +new file mode 100644 +index 0000000..2cb35c2 +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget-patch.c +@@ -0,0 +1,680 @@ ++/* ++ * budget-patch.c: driver for Budget Patch, ++ * hardware modification of DVB-S cards enabling full TS ++ * ++ * Written by Emard ++ * ++ * Original idea by Roberto Deza ++ * ++ * Special thanks to Holger Waechtler, Michael Hunold, Marian Durkovic ++ * and Metzlerbros ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#include "av7110.h" ++#include "av7110_hw.h" ++#include "budget.h" ++#include "stv0299.h" ++#include "ves1x93.h" ++#include "tda8083.h" ++ ++#include "bsru6.h" ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++#define budget_patch budget ++ ++static struct saa7146_extension budget_extension; ++ ++MAKE_BUDGET_INFO(ttbp, "TT-Budget/Patch DVB-S 1.x PCI", BUDGET_PATCH); ++//MAKE_BUDGET_INFO(satel,"TT-Budget/Patch SATELCO PCI", BUDGET_TT_HW_DISEQC); ++ ++static struct pci_device_id pci_tbl[] = { ++ MAKE_EXTENSION_PCI(ttbp,0x13c2, 0x0000), ++// MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), ++ { ++ .vendor = 0, ++ } ++}; ++ ++/* those lines are for budget-patch to be tried ++** on a true budget card and observe the ++** behaviour of VSYNC generated by rps1. ++** this code was shamelessly copy/pasted from budget.c ++*/ ++static void gpio_Set22K (struct budget *budget, int state) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ dprintk(2, "budget: %p\n", budget); ++ saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); ++} ++ ++/* Diseqc functions only for TT Budget card */ ++/* taken from the Skyvision DVB driver by ++ Ralph Metzler */ ++ ++static void DiseqcSendBit (struct budget *budget, int data) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ dprintk(2, "budget: %p\n", budget); ++ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); ++ udelay(data ? 500 : 1000); ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ udelay(data ? 1000 : 500); ++} ++ ++static void DiseqcSendByte (struct budget *budget, int data) ++{ ++ int i, par=1, d; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ for (i=7; i>=0; i--) { ++ d = (data>>i)&1; ++ par ^= d; ++ DiseqcSendBit(budget, d); ++ } ++ ++ DiseqcSendBit(budget, par); ++} ++ ++static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ int i; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ mdelay(16); ++ ++ for (i=0; idvb->priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ gpio_Set22K (budget, 1); ++ break; ++ ++ case SEC_TONE_OFF: ++ gpio_Set22K (budget, 0); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); ++ ++ return 0; ++} ++ ++static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ SendDiSEqCMsg (budget, 0, NULL, minicmd); ++ ++ return 0; ++} ++ ++static int budget_av7110_send_fw_cmd(struct budget_patch *budget, u16* buf, int length) ++{ ++ int i; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ for (i = 2; i < length; i++) ++ { ++ ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2*i, 2, (u32) buf[i], 0,0); ++ msleep(5); ++ } ++ if (length) ++ ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, (u32) buf[1], 0,0); ++ else ++ ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND + 2, 2, 0, 0,0); ++ msleep(5); ++ ttpci_budget_debiwrite(budget, DEBINOSWAP, COMMAND, 2, (u32) buf[0], 0,0); ++ msleep(5); ++ return 0; ++} ++ ++static void av7110_set22k(struct budget_patch *budget, int state) ++{ ++ u16 buf[2] = {( COMTYPE_AUDIODAC << 8) | (state ? ON22K : OFF22K), 0}; ++ ++ dprintk(2, "budget: %p\n", budget); ++ budget_av7110_send_fw_cmd(budget, buf, 2); ++} ++ ++static int av7110_send_diseqc_msg(struct budget_patch *budget, int len, u8 *msg, int burst) ++{ ++ int i; ++ u16 buf[18] = { ((COMTYPE_AUDIODAC << 8) | SendDiSEqC), ++ 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ if (len>10) ++ len=10; ++ ++ buf[1] = len+2; ++ buf[2] = len; ++ ++ if (burst != -1) ++ buf[3]=burst ? 0x01 : 0x00; ++ else ++ buf[3]=0xffff; ++ ++ for (i=0; idvb->priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ av7110_set22k (budget, 1); ++ break; ++ ++ case SEC_TONE_OFF: ++ av7110_set22k (budget, 0); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int budget_patch_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; ++ ++ av7110_send_diseqc_msg (budget, cmd->msg_len, cmd->msg, 0); ++ ++ return 0; ++} ++ ++static int budget_patch_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; ++ ++ av7110_send_diseqc_msg (budget, 0, NULL, minicmd); ++ ++ return 0; ++} ++ ++static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; ++ u8 pwr = 0; ++ u8 buf[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; ++ u32 div = (p->frequency + 479500) / 125; ++ ++ if (p->frequency > 2000000) ++ pwr = 3; ++ else if (p->frequency > 1800000) ++ pwr = 2; ++ else if (p->frequency > 1600000) ++ pwr = 1; ++ else if (p->frequency > 1200000) ++ pwr = 0; ++ else if (p->frequency >= 1100000) ++ pwr = 1; ++ else pwr = 2; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = ((div & 0x18000) >> 10) | 0x95; ++ buf[3] = (pwr << 6) | 0x30; ++ ++ // NOTE: since we're using a prescaler of 2, we set the ++ // divisor frequency to 62.5kHz and divide by 125 above ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct ves1x93_config alps_bsrv2_config = { ++ .demod_address = 0x08, ++ .xin = 90100000UL, ++ .invert_pwm = 0, ++}; ++ ++static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *p = &fe->dtv_property_cache; ++ struct budget_patch* budget = (struct budget_patch*) fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = p->frequency / 125; ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x8e; ++ data[3] = 0x00; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) ++ return -EIO; ++ return 0; ++} ++ ++static struct tda8083_config grundig_29504_451_config = { ++ .demod_address = 0x68, ++}; ++ ++static void frontend_init(struct budget_patch* budget) ++{ ++ switch(budget->dev->pci->subsystem_device) { ++ case 0x0000: // Hauppauge/TT WinTV DVB-S rev1.X ++ case 0x1013: // SATELCO Multimedia PCI ++ ++ // try the ALPS BSRV2 first of all ++ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; ++ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd; ++ budget->dvb_frontend->ops.diseqc_send_burst = budget_patch_diseqc_send_burst; ++ budget->dvb_frontend->ops.set_tone = budget_patch_set_tone; ++ break; ++ } ++ ++ // try the ALPS BSRU6 now ++ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = &budget->i2c_adap; ++ ++ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; ++ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; ++ budget->dvb_frontend->ops.set_tone = budget_set_tone; ++ break; ++ } ++ ++ // Try the grundig 29504-451 ++ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; ++ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; ++ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; ++ budget->dvb_frontend->ops.set_tone = budget_set_tone; ++ break; ++ } ++ break; ++ } ++ ++ if (budget->dvb_frontend == NULL) { ++ printk("dvb-ttpci: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ budget->dev->pci->vendor, ++ budget->dev->pci->device, ++ budget->dev->pci->subsystem_vendor, ++ budget->dev->pci->subsystem_device); ++ } else { ++ if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) { ++ printk("budget-av: Frontend registration failed!\n"); ++ dvb_frontend_detach(budget->dvb_frontend); ++ budget->dvb_frontend = NULL; ++ } ++ } ++} ++ ++/* written by Emard */ ++static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) ++{ ++ struct budget_patch *budget; ++ int err; ++ int count = 0; ++ int detected = 0; ++ ++#define PATCH_RESET 0 ++#define RPS_IRQ 0 ++#define HPS_SETUP 0 ++#if PATCH_RESET ++ saa7146_write(dev, MC1, MASK_31); ++ msleep(40); ++#endif ++#if HPS_SETUP ++ // initialize registers. Better to have it like this ++ // than leaving something unconfigured ++ saa7146_write(dev, DD1_STREAM_B, 0); ++ // port B VSYNC at rising edge ++ saa7146_write(dev, DD1_INIT, 0x00000200); // have this in budget-core too! ++ saa7146_write(dev, BRS_CTRL, 0x00000000); // VBI ++ ++ // debi config ++ // saa7146_write(dev, DEBI_CONFIG, MASK_30|MASK_28|MASK_18); ++ ++ // zero all HPS registers ++ saa7146_write(dev, HPS_H_PRESCALE, 0); // r68 ++ saa7146_write(dev, HPS_H_SCALE, 0); // r6c ++ saa7146_write(dev, BCS_CTRL, 0); // r70 ++ saa7146_write(dev, HPS_V_SCALE, 0); // r60 ++ saa7146_write(dev, HPS_V_GAIN, 0); // r64 ++ saa7146_write(dev, CHROMA_KEY_RANGE, 0); // r74 ++ saa7146_write(dev, CLIP_FORMAT_CTRL, 0); // r78 ++ // Set HPS prescaler for port B input ++ saa7146_write(dev, HPS_CTRL, (1<<30) | (0<<29) | (1<<28) | (0<<12) ); ++ saa7146_write(dev, MC2, ++ 0 * (MASK_08 | MASK_24) | // BRS control ++ 0 * (MASK_09 | MASK_25) | // a ++ 0 * (MASK_10 | MASK_26) | // b ++ 1 * (MASK_06 | MASK_22) | // HPS_CTRL1 ++ 1 * (MASK_05 | MASK_21) | // HPS_CTRL2 ++ 0 * (MASK_01 | MASK_15) // DEBI ++ ); ++#endif ++ // Disable RPS1 and RPS0 ++ saa7146_write(dev, MC1, ( MASK_29 | MASK_28)); ++ // RPS1 timeout disable ++ saa7146_write(dev, RPS_TOV1, 0); ++ ++ // code for autodetection ++ // will wait for VBI_B event (vertical blank at port B) ++ // and will reset GPIO3 after VBI_B is detected. ++ // (GPIO3 should be raised high by CPU to ++ // test if GPIO3 will generate vertical blank signal ++ // in budget patch GPIO3 is connected to VSYNC_B ++ count = 0; ++#if 0 ++ WRITE_RPS1(CMD_UPLOAD | ++ MASK_10 | MASK_09 | MASK_08 | MASK_06 | MASK_05 | MASK_04 | MASK_03 | MASK_02 ); ++#endif ++ WRITE_RPS1(CMD_PAUSE | EVT_VBI_B); ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); ++#if RPS_IRQ ++ // issue RPS1 interrupt to increment counter ++ WRITE_RPS1(CMD_INTERRUPT); ++ // at least a NOP is neede between two interrupts ++ WRITE_RPS1(CMD_NOP); ++ // interrupt again ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ WRITE_RPS1(CMD_STOP); ++ ++#if RPS_IRQ ++ // set event counter 1 source as RPS1 interrupt (0x03) (rE4 p53) ++ // use 0x03 to track RPS1 interrupts - increase by 1 every gpio3 is toggled ++ // use 0x15 to track VPE interrupts - increase by 1 every vpeirq() is called ++ saa7146_write(dev, EC1SSR, (0x03<<2) | 3 ); ++ // set event counter 1 threshold to maximum allowed value (rEC p55) ++ saa7146_write(dev, ECT1R, 0x3fff ); ++#endif ++ // Fix VSYNC level ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ // Set RPS1 Address register to point to RPS code (r108 p42) ++ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); ++ // Enable RPS1, (rFC p33) ++ saa7146_write(dev, MC1, (MASK_13 | MASK_29 )); ++ ++ ++ mdelay(50); ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); ++ mdelay(150); ++ ++ ++ if( (saa7146_read(dev, GPIO_CTRL) & 0x10000000) == 0) ++ detected = 1; ++ ++#if RPS_IRQ ++ printk("Event Counter 1 0x%04x\n", saa7146_read(dev, EC1R) & 0x3fff ); ++#endif ++ // Disable RPS1 ++ saa7146_write(dev, MC1, ( MASK_29 )); ++ ++ if(detected == 0) ++ printk("budget-patch not detected or saa7146 in non-default state.\n" ++ "try enabling ressetting of 7146 with MASK_31 in MC1 register\n"); ++ ++ else ++ printk("BUDGET-PATCH DETECTED.\n"); ++ ++ ++/* OLD (Original design by Roberto Deza): ++** This code will setup the SAA7146_RPS1 to generate a square ++** wave on GPIO3, changing when a field (TS_HEIGHT/2 "lines" of ++** TS_WIDTH packets) has been acquired on SAA7146_D1B video port; ++** then, this GPIO3 output which is connected to the D1B_VSYNC ++** input, will trigger the acquisition of the alternate field ++** and so on. ++** Currently, the TT_budget / WinTV_Nova cards have two ICs ++** (74HCT4040, LVC74) for the generation of this VSYNC signal, ++** which seems that can be done perfectly without this :-)). ++*/ ++ ++/* New design (By Emard) ++** this rps1 code will copy internal HS event to GPIO3 pin. ++** GPIO3 is in budget-patch hardware connected to port B VSYNC ++ ++** HS is an internal event of 7146, accessible with RPS ++** and temporarily raised high every n lines ++** (n in defined in the RPS_THRESH1 counter threshold) ++** I think HS is raised high on the beginning of the n-th line ++** and remains high until this n-th line that triggered ++** it is completely received. When the reception of n-th line ++** ends, HS is lowered. ++ ++** To transmit data over DMA, 7146 needs changing state at ++** port B VSYNC pin. Any changing of port B VSYNC will ++** cause some DMA data transfer, with more or less packets loss. ++** It depends on the phase and frequency of VSYNC and ++** the way of 7146 is instructed to trigger on port B (defined ++** in DD1_INIT register, 3rd nibble from the right valid ++** numbers are 0-7, see datasheet) ++** ++** The correct triggering can minimize packet loss, ++** dvbtraffic should give this stable bandwidths: ++** 22k transponder = 33814 kbit/s ++** 27.5k transponder = 38045 kbit/s ++** by experiment it is found that the best results ++** (stable bandwidths and almost no packet loss) ++** are obtained using DD1_INIT triggering number 2 ++** (Va at rising edge of VS Fa = HS x VS-failing forced toggle) ++** and a VSYNC phase that occurs in the middle of DMA transfer ++** (about byte 188*512=96256 in the DMA window). ++** ++** Phase of HS is still not clear to me how to control, ++** It just happens to be so. It can be seen if one enables ++** RPS_IRQ and print Event Counter 1 in vpeirq(). Every ++** time RPS_INTERRUPT is called, the Event Counter 1 will ++** increment. That's how the 7146 is programmed to do event ++** counting in this budget-patch.c ++** I *think* HPS setting has something to do with the phase ++** of HS but I can't be 100% sure in that. ++ ++** hardware debug note: a working budget card (including budget patch) ++** with vpeirq() interrupt setup in mode "0x90" (every 64K) will ++** generate 3 interrupts per 25-Hz DMA frame of 2*188*512 bytes ++** and that means 3*25=75 Hz of interrupt frequency, as seen by ++** watch cat /proc/interrupts ++** ++** If this frequency is 3x lower (and data received in the DMA ++** buffer don't start with 0x47, but in the middle of packets, ++** whose lengths appear to be like 188 292 188 104 etc. ++** this means VSYNC line is not connected in the hardware. ++** (check soldering pcb and pins) ++** The same behaviour of missing VSYNC can be duplicated on budget ++** cards, by setting DD1_INIT trigger mode 7 in 3rd nibble. ++*/ ++ ++ // Setup RPS1 "program" (p35) ++ count = 0; ++ ++ ++ // Wait Source Line Counter Threshold (p36) ++ WRITE_RPS1(CMD_PAUSE | EVT_HS); ++ // Set GPIO3=1 (p42) ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTHI<<24); ++#if RPS_IRQ ++ // issue RPS1 interrupt ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ // Wait reset Source Line Counter Threshold (p36) ++ WRITE_RPS1(CMD_PAUSE | RPS_INV | EVT_HS); ++ // Set GPIO3=0 (p42) ++ WRITE_RPS1(CMD_WR_REG_MASK | (GPIO_CTRL>>2)); ++ WRITE_RPS1(GPIO3_MSK); ++ WRITE_RPS1(SAA7146_GPIO_OUTLO<<24); ++#if RPS_IRQ ++ // issue RPS1 interrupt ++ WRITE_RPS1(CMD_INTERRUPT); ++#endif ++ // Jump to begin of RPS program (p37) ++ WRITE_RPS1(CMD_JUMP); ++ WRITE_RPS1(dev->d_rps1.dma_handle); ++ ++ // Fix VSYNC level ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ // Set RPS1 Address register to point to RPS code (r108 p42) ++ saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); ++ ++ if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) ++ return -ENOMEM; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); ++ if (err) { ++ kfree(budget); ++ return err; ++ } ++ ++ // Set Source Line Counter Threshold, using BRS (rCC p43) ++ // It generates HS event every TS_HEIGHT lines ++ // this is related to TS_WIDTH set in register ++ // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE ++ // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 ++ //,then RPS_THRESH1 ++ // should be set to trigger every TS_HEIGHT (512) lines. ++ // ++ saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); ++ ++ // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); ++ // Enable RPS1 (rFC p33) ++ saa7146_write(dev, MC1, (MASK_13 | MASK_29)); ++ ++ ++ dev->ext_priv = budget; ++ ++ budget->dvb_adapter.priv = budget; ++ frontend_init(budget); ++ ++ ttpci_budget_init_hooks(budget); ++ ++ return 0; ++} ++ ++static int budget_patch_detach (struct saa7146_dev* dev) ++{ ++ struct budget_patch *budget = (struct budget_patch*) dev->ext_priv; ++ int err; ++ ++ if (budget->dvb_frontend) { ++ dvb_unregister_frontend(budget->dvb_frontend); ++ dvb_frontend_detach(budget->dvb_frontend); ++ } ++ err = ttpci_budget_deinit (budget); ++ ++ kfree (budget); ++ ++ return err; ++} ++ ++static int __init budget_patch_init(void) ++{ ++ return saa7146_register_extension(&budget_extension); ++} ++ ++static void __exit budget_patch_exit(void) ++{ ++ saa7146_unregister_extension(&budget_extension); ++} ++ ++static struct saa7146_extension budget_extension = { ++ .name = "budget_patch dvb", ++ .flags = 0, ++ ++ .module = THIS_MODULE, ++ .pci_tbl = pci_tbl, ++ .attach = budget_patch_attach, ++ .detach = budget_patch_detach, ++ ++ .irq_mask = MASK_10, ++ .irq_func = ttpci_budget_irq10_handler, ++}; ++ ++module_init(budget_patch_init); ++module_exit(budget_patch_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Emard, Roberto Deza, Holger Waechtler, Michael Hunold, others"); ++MODULE_DESCRIPTION("Driver for full TS modified DVB-S SAA7146+AV7110 " ++ "based so-called Budget Patch cards"); +diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c +new file mode 100644 +index 0000000..7e6e43a +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget.c +@@ -0,0 +1,871 @@ ++/* ++ * budget.c: driver for the SAA7146 based Budget DVB cards ++ * ++ * Compiled from various sources by Michael Hunold ++ * ++ * Copyright (C) 2002 Ralph Metzler ++ * ++ * Copyright (C) 1999-2002 Ralph Metzler ++ * & Marcus Metzler for convergence integrated media GmbH ++ * ++ * 26feb2004 Support for FS Activy Card (Grundig tuner) by ++ * Michael Dreher , ++ * Oliver Endriss and ++ * Andreas 'randy' Weinberger ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html ++ * ++ * ++ * the project's page is at http://www.linuxtv.org/ ++ */ ++ ++#include "budget.h" ++#include "stv0299.h" ++#include "ves1x93.h" ++#include "ves1820.h" ++#include "l64781.h" ++#include "tda8083.h" ++#include "s5h1420.h" ++#include "tda10086.h" ++#include "tda826x.h" ++#include "lnbp21.h" ++#include "bsru6.h" ++#include "bsbe1.h" ++#include "tdhd1.h" ++#include "stv6110x.h" ++#include "stv090x.h" ++#include "isl6423.h" ++#include "lnbh24.h" ++ ++ ++static int diseqc_method; ++module_param(diseqc_method, int, 0444); ++MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); ++ ++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); ++ ++static void Set22K (struct budget *budget, int state) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ dprintk(2, "budget: %p\n", budget); ++ saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO)); ++} ++ ++/* Diseqc functions only for TT Budget card */ ++/* taken from the Skyvision DVB driver by ++ Ralph Metzler */ ++ ++static void DiseqcSendBit (struct budget *budget, int data) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ dprintk(2, "budget: %p\n", budget); ++ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI); ++ udelay(data ? 500 : 1000); ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ udelay(data ? 1000 : 500); ++} ++ ++static void DiseqcSendByte (struct budget *budget, int data) ++{ ++ int i, par=1, d; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ for (i=7; i>=0; i--) { ++ d = (data>>i)&1; ++ par ^= d; ++ DiseqcSendBit(budget, d); ++ } ++ ++ DiseqcSendBit(budget, par); ++} ++ ++static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst) ++{ ++ struct saa7146_dev *dev=budget->dev; ++ int i; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); ++ mdelay(16); ++ ++ for (i=0; idev; ++ ++ dprintk(2, "budget: %p\n", budget); ++ ++ switch (voltage) { ++ case SEC_VOLTAGE_13: ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); ++ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO); ++ break; ++ case SEC_VOLTAGE_18: ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTHI); ++ saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI); ++ break; ++ case SEC_VOLTAGE_OFF: ++ saa7146_setgpio(dev, 1, SAA7146_GPIO_OUTLO); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int siemens_budget_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ return SetVoltage_Activy (budget, voltage); ++} ++ ++static int budget_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ switch (tone) { ++ case SEC_TONE_ON: ++ Set22K (budget, 1); ++ break; ++ ++ case SEC_TONE_OFF: ++ Set22K (budget, 0); ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int budget_diseqc_send_master_cmd(struct dvb_frontend* fe, struct dvb_diseqc_master_cmd* cmd) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0); ++ ++ return 0; ++} ++ ++static int budget_diseqc_send_burst(struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd) ++{ ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ ++ SendDiSEqCMsg (budget, 0, NULL, minicmd); ++ ++ return 0; ++} ++ ++static int alps_bsrv2_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ u8 pwr = 0; ++ u8 buf[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; ++ u32 div = (c->frequency + 479500) / 125; ++ ++ if (c->frequency > 2000000) ++ pwr = 3; ++ else if (c->frequency > 1800000) ++ pwr = 2; ++ else if (c->frequency > 1600000) ++ pwr = 1; ++ else if (c->frequency > 1200000) ++ pwr = 0; ++ else if (c->frequency >= 1100000) ++ pwr = 1; ++ else pwr = 2; ++ ++ buf[0] = (div >> 8) & 0x7f; ++ buf[1] = div & 0xff; ++ buf[2] = ((div & 0x18000) >> 10) | 0x95; ++ buf[3] = (pwr << 6) | 0x30; ++ ++ // NOTE: since we're using a prescaler of 2, we set the ++ // divisor frequency to 62.5kHz and divide by 125 above ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; ++ return 0; ++} ++ ++static struct ves1x93_config alps_bsrv2_config = ++{ ++ .demod_address = 0x08, ++ .xin = 90100000UL, ++ .invert_pwm = 0, ++}; ++ ++static int alps_tdbe2_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x62, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = (c->frequency + 35937500 + 31250) / 62500; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x85 | ((div >> 10) & 0x60); ++ data[3] = (c->frequency < 174000000 ? 0x88 : c->frequency < 470000000 ? 0x84 : 0x81); ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; ++ return 0; ++} ++ ++static struct ves1820_config alps_tdbe2_config = { ++ .demod_address = 0x09, ++ .xin = 57840000UL, ++ .invert = 1, ++ .selagc = VES1820_SELAGC_SIGNAMPERR, ++}; ++ ++static int grundig_29504_401_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget *budget = fe->dvb->priv; ++ u8 *tuner_addr = fe->tuner_priv; ++ u32 div; ++ u8 cfg, cpump, band_select; ++ u8 data[4]; ++ struct i2c_msg msg = { .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ if (tuner_addr) ++ msg.addr = *tuner_addr; ++ else ++ msg.addr = 0x61; ++ ++ div = (36125000 + c->frequency) / 166666; ++ ++ cfg = 0x88; ++ ++ if (c->frequency < 175000000) ++ cpump = 2; ++ else if (c->frequency < 390000000) ++ cpump = 1; ++ else if (c->frequency < 470000000) ++ cpump = 2; ++ else if (c->frequency < 750000000) ++ cpump = 1; ++ else ++ cpump = 3; ++ ++ if (c->frequency < 175000000) ++ band_select = 0x0e; ++ else if (c->frequency < 470000000) ++ band_select = 0x05; ++ else ++ band_select = 0x03; ++ ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = ((div >> 10) & 0x60) | cfg; ++ data[3] = (cpump << 6) | band_select; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; ++ return 0; ++} ++ ++static struct l64781_config grundig_29504_401_config = { ++ .demod_address = 0x55, ++}; ++ ++static struct l64781_config grundig_29504_401_config_activy = { ++ .demod_address = 0x54, ++}; ++ ++static u8 tuner_address_grundig_29504_401_activy = 0x60; ++ ++static int grundig_29504_451_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = c->frequency / 125; ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0x8e; ++ data[3] = 0x00; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; ++ return 0; ++} ++ ++static struct tda8083_config grundig_29504_451_config = { ++ .demod_address = 0x68, ++}; ++ ++static int s5h1420_tuner_set_params(struct dvb_frontend *fe) ++{ ++ struct dtv_frontend_properties *c = &fe->dtv_property_cache; ++ struct budget* budget = (struct budget*) fe->dvb->priv; ++ u32 div; ++ u8 data[4]; ++ struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = data, .len = sizeof(data) }; ++ ++ div = c->frequency / 1000; ++ data[0] = (div >> 8) & 0x7f; ++ data[1] = div & 0xff; ++ data[2] = 0xc2; ++ ++ if (div < 1450) ++ data[3] = 0x00; ++ else if (div < 1850) ++ data[3] = 0x40; ++ else if (div < 2000) ++ data[3] = 0x80; ++ else ++ data[3] = 0xc0; ++ ++ if (fe->ops.i2c_gate_ctrl) ++ fe->ops.i2c_gate_ctrl(fe, 1); ++ if (i2c_transfer (&budget->i2c_adap, &msg, 1) != 1) return -EIO; ++ ++ return 0; ++} ++ ++static struct s5h1420_config s5h1420_config = { ++ .demod_address = 0x53, ++ .invert = 1, ++ .cdclk_polarity = 1, ++}; ++ ++static struct tda10086_config tda10086_config = { ++ .demod_address = 0x0e, ++ .invert = 0, ++ .diseqc_tone = 1, ++ .xtal_freq = TDA10086_XTAL_16M, ++}; ++ ++static struct stv0299_config alps_bsru6_config_activy = { ++ .demod_address = 0x68, ++ .inittab = alps_bsru6_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .op0_off = 1, ++ .min_delay_ms = 100, ++ .set_symbol_rate = alps_bsru6_set_symbol_rate, ++}; ++ ++static struct stv0299_config alps_bsbe1_config_activy = { ++ .demod_address = 0x68, ++ .inittab = alps_bsbe1_inittab, ++ .mclk = 88000000UL, ++ .invert = 1, ++ .op0_off = 1, ++ .min_delay_ms = 100, ++ .set_symbol_rate = alps_bsbe1_set_symbol_rate, ++}; ++ ++static int alps_tdhd1_204_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) ++{ ++ struct budget *budget = (struct budget *)fe->dvb->priv; ++ ++ return request_firmware(fw, name, &budget->dev->pci->dev); ++} ++ ++ ++static int i2c_readreg(struct i2c_adapter *i2c, u8 adr, u8 reg) ++{ ++ u8 val; ++ struct i2c_msg msg[] = { ++ { .addr = adr, .flags = 0, .buf = ®, .len = 1 }, ++ { .addr = adr, .flags = I2C_M_RD, .buf = &val, .len = 1 } ++ }; ++ ++ return (i2c_transfer(i2c, msg, 2) != 2) ? -EIO : val; ++} ++ ++static u8 read_pwm(struct budget* budget) ++{ ++ u8 b = 0xff; ++ u8 pwm; ++ struct i2c_msg msg[] = { { .addr = 0x50,.flags = 0,.buf = &b,.len = 1 }, ++ { .addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; ++ ++ if ((i2c_transfer(&budget->i2c_adap, msg, 2) != 2) || (pwm == 0xff)) ++ pwm = 0x48; ++ ++ return pwm; ++} ++ ++static struct stv090x_config tt1600_stv090x_config = { ++ .device = STV0903, ++ .demod_mode = STV090x_SINGLE, ++ .clk_mode = STV090x_CLK_EXT, ++ ++ .xtal = 13500000, ++ .address = 0x68, ++ ++ .ts1_mode = STV090x_TSMODE_DVBCI, ++ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, ++ ++ .repeater_level = STV090x_RPTLEVEL_16, ++ ++ .tuner_init = NULL, ++ .tuner_sleep = NULL, ++ .tuner_set_mode = NULL, ++ .tuner_set_frequency = NULL, ++ .tuner_get_frequency = NULL, ++ .tuner_set_bandwidth = NULL, ++ .tuner_get_bandwidth = NULL, ++ .tuner_set_bbgain = NULL, ++ .tuner_get_bbgain = NULL, ++ .tuner_set_refclk = NULL, ++ .tuner_get_status = NULL, ++}; ++ ++static struct stv6110x_config tt1600_stv6110x_config = { ++ .addr = 0x60, ++ .refclk = 27000000, ++ .clk_div = 2, ++}; ++ ++static struct isl6423_config tt1600_isl6423_config = { ++ .current_max = SEC_CURRENT_515m, ++ .curlim = SEC_CURRENT_LIM_ON, ++ .mod_extern = 1, ++ .addr = 0x08, ++}; ++ ++static void frontend_init(struct budget *budget) ++{ ++ (void)alps_bsbe1_config; /* avoid warning */ ++ ++ switch(budget->dev->pci->subsystem_device) { ++ case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659)) ++ case 0x1013: ++ // try the ALPS BSRV2 first of all ++ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params; ++ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; ++ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; ++ budget->dvb_frontend->ops.set_tone = budget_set_tone; ++ break; ++ } ++ ++ // try the ALPS BSRU6 now ++ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = &budget->i2c_adap; ++ if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { ++ budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; ++ budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; ++ budget->dvb_frontend->ops.set_tone = budget_set_tone; ++ } ++ break; ++ } ++ break; ++ ++ case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659)) ++ ++ budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget)); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params; ++ break; ++ } ++ break; ++ ++ case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060)) ++ ++ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = NULL; ++ break; ++ } ++ break; ++ ++ case 0x4f60: /* Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/tsa5059) */ ++ { ++ int subtype = i2c_readreg(&budget->i2c_adap, 0x50, 0x67); ++ ++ if (subtype < 0) ++ break; ++ /* fixme: find a better way to identify the card */ ++ if (subtype < 0x36) { ++ /* assume ALPS BSRU6 */ ++ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config_activy, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ printk(KERN_INFO "budget: tuner ALPS BSRU6 detected\n"); ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = &budget->i2c_adap; ++ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; ++ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; ++ break; ++ } ++ } else { ++ /* assume ALPS BSBE1 */ ++ /* reset tuner */ ++ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTLO); ++ msleep(50); ++ saa7146_setgpio(budget->dev, 3, SAA7146_GPIO_OUTHI); ++ msleep(250); ++ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config_activy, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ printk(KERN_INFO "budget: tuner ALPS BSBE1 detected\n"); ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = &budget->i2c_adap; ++ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; ++ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; ++ break; ++ } ++ } ++ break; ++ } ++ ++ case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522)) ++ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params; ++ budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage; ++ budget->dvb_frontend->ops.dishnetwork_send_legacy_command = NULL; ++ } ++ break; ++ ++ case 0x5f60: /* Fujitsu Siemens Activy Budget-T PCI rev AL (tda10046/ALPS TDHD1-204A) */ ++ budget->dvb_frontend = dvb_attach(tda10046_attach, &alps_tdhd1_204a_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdhd1_204a_tuner_set_params; ++ budget->dvb_frontend->tuner_priv = &budget->i2c_adap; ++ } ++ break; ++ ++ case 0x5f61: /* Fujitsu Siemens Activy Budget-T PCI rev GR (L64781/Grundig 29504-401(tsa5060)) */ ++ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config_activy, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->tuner_priv = &tuner_address_grundig_29504_401_activy; ++ budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params; ++ } ++ break; ++ ++ case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260)) ++ budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params; ++ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { ++ printk("%s: No LNBP21 found!\n", __func__); ++ goto error_out; ++ } ++ break; ++ } ++ ++ case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262) ++ // gpio2 is connected to CLB - reset it + leave it high ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); ++ msleep(1); ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); ++ msleep(1); ++ ++ budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap); ++ if (budget->dvb_frontend) { ++ if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL) ++ printk("%s: No tda826x found!\n", __func__); ++ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) { ++ printk("%s: No LNBP21 found!\n", __func__); ++ goto error_out; ++ } ++ break; ++ } ++ ++ case 0x101c: { /* TT S2-1600 */ ++ struct stv6110x_devctl *ctl; ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); ++ msleep(50); ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); ++ msleep(250); ++ ++ budget->dvb_frontend = dvb_attach(stv090x_attach, ++ &tt1600_stv090x_config, ++ &budget->i2c_adap, ++ STV090x_DEMODULATOR_0); ++ ++ if (budget->dvb_frontend) { ++ ++ ctl = dvb_attach(stv6110x_attach, ++ budget->dvb_frontend, ++ &tt1600_stv6110x_config, ++ &budget->i2c_adap); ++ ++ if (ctl) { ++ tt1600_stv090x_config.tuner_init = ctl->tuner_init; ++ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; ++ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; ++ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; ++ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; ++ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; ++ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; ++ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; ++ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; ++ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; ++ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; ++ ++ /* call the init function once to initialize ++ tuner's clock output divider and demod's ++ master clock */ ++ if (budget->dvb_frontend->ops.init) ++ budget->dvb_frontend->ops.init(budget->dvb_frontend); ++ ++ if (dvb_attach(isl6423_attach, ++ budget->dvb_frontend, ++ &budget->i2c_adap, ++ &tt1600_isl6423_config) == NULL) { ++ printk(KERN_ERR "%s: No Intersil ISL6423 found!\n", __func__); ++ goto error_out; ++ } ++ } else { ++ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); ++ goto error_out; ++ } ++ } ++ } ++ break; ++ ++ case 0x1020: { /* Omicom S2 */ ++ struct stv6110x_devctl *ctl; ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO); ++ msleep(50); ++ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI); ++ msleep(250); ++ ++ budget->dvb_frontend = dvb_attach(stv090x_attach, ++ &tt1600_stv090x_config, ++ &budget->i2c_adap, ++ STV090x_DEMODULATOR_0); ++ ++ if (budget->dvb_frontend) { ++ printk(KERN_INFO "budget: Omicom S2 detected\n"); ++ ++ ctl = dvb_attach(stv6110x_attach, ++ budget->dvb_frontend, ++ &tt1600_stv6110x_config, ++ &budget->i2c_adap); ++ ++ if (ctl) { ++ tt1600_stv090x_config.tuner_init = ctl->tuner_init; ++ tt1600_stv090x_config.tuner_sleep = ctl->tuner_sleep; ++ tt1600_stv090x_config.tuner_set_mode = ctl->tuner_set_mode; ++ tt1600_stv090x_config.tuner_set_frequency = ctl->tuner_set_frequency; ++ tt1600_stv090x_config.tuner_get_frequency = ctl->tuner_get_frequency; ++ tt1600_stv090x_config.tuner_set_bandwidth = ctl->tuner_set_bandwidth; ++ tt1600_stv090x_config.tuner_get_bandwidth = ctl->tuner_get_bandwidth; ++ tt1600_stv090x_config.tuner_set_bbgain = ctl->tuner_set_bbgain; ++ tt1600_stv090x_config.tuner_get_bbgain = ctl->tuner_get_bbgain; ++ tt1600_stv090x_config.tuner_set_refclk = ctl->tuner_set_refclk; ++ tt1600_stv090x_config.tuner_get_status = ctl->tuner_get_status; ++ ++ /* call the init function once to initialize ++ tuner's clock output divider and demod's ++ master clock */ ++ if (budget->dvb_frontend->ops.init) ++ budget->dvb_frontend->ops.init(budget->dvb_frontend); ++ ++ if (dvb_attach(lnbh24_attach, ++ budget->dvb_frontend, ++ &budget->i2c_adap, ++ LNBH24_PCL | LNBH24_TTX, ++ LNBH24_TEN, 0x14>>1) == NULL) { ++ printk(KERN_ERR ++ "No LNBH24 found!\n"); ++ goto error_out; ++ } ++ } else { ++ printk(KERN_ERR "%s: No STV6110(A) Silicon Tuner found!\n", __func__); ++ goto error_out; ++ } ++ } ++ } ++ break; ++ } ++ ++ if (budget->dvb_frontend == NULL) { ++ printk("budget: A frontend driver was not found for device [%04x:%04x] subsystem [%04x:%04x]\n", ++ budget->dev->pci->vendor, ++ budget->dev->pci->device, ++ budget->dev->pci->subsystem_vendor, ++ budget->dev->pci->subsystem_device); ++ } else { ++ if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) ++ goto error_out; ++ } ++ return; ++ ++error_out: ++ printk("budget: Frontend registration failed!\n"); ++ dvb_frontend_detach(budget->dvb_frontend); ++ budget->dvb_frontend = NULL; ++ return; ++} ++ ++static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info) ++{ ++ struct budget *budget = NULL; ++ int err; ++ ++ budget = kmalloc(sizeof(struct budget), GFP_KERNEL); ++ if( NULL == budget ) { ++ return -ENOMEM; ++ } ++ ++ dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget); ++ ++ dev->ext_priv = budget; ++ ++ err = ttpci_budget_init(budget, dev, info, THIS_MODULE, adapter_nr); ++ if (err) { ++ printk("==> failed\n"); ++ kfree (budget); ++ return err; ++ } ++ ++ budget->dvb_adapter.priv = budget; ++ frontend_init(budget); ++ ++ ttpci_budget_init_hooks(budget); ++ ++ return 0; ++} ++ ++static int budget_detach (struct saa7146_dev* dev) ++{ ++ struct budget *budget = (struct budget*) dev->ext_priv; ++ int err; ++ ++ if (budget->dvb_frontend) { ++ dvb_unregister_frontend(budget->dvb_frontend); ++ dvb_frontend_detach(budget->dvb_frontend); ++ } ++ ++ err = ttpci_budget_deinit (budget); ++ ++ kfree (budget); ++ dev->ext_priv = NULL; ++ ++ return err; ++} ++ ++static struct saa7146_extension budget_extension; ++ ++MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC); ++MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(tt1600, "TT-Budget S2-1600 PCI", BUDGET_TT); ++MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY); ++MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY); ++MAKE_BUDGET_INFO(fsact, "Fujitsu Siemens Activy Budget-T PCI (rev GR/Grundig frontend)", BUDGET_FS_ACTIVY); ++MAKE_BUDGET_INFO(fsact1, "Fujitsu Siemens Activy Budget-T PCI (rev AL/ALPS TDHD1-204A)", BUDGET_FS_ACTIVY); ++MAKE_BUDGET_INFO(omicom, "Omicom S2 PCI", BUDGET_TT); ++ ++static struct pci_device_id pci_tbl[] = { ++ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003), ++ MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004), ++ MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005), ++ MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013), ++ MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016), ++ MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018), ++ MAKE_EXTENSION_PCI(tt1600, 0x13c2, 0x101c), ++ MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60), ++ MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61), ++ MAKE_EXTENSION_PCI(fsact1, 0x1131, 0x5f60), ++ MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), ++ MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), ++ { ++ .vendor = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, pci_tbl); ++ ++static struct saa7146_extension budget_extension = { ++ .name = "budget dvb", ++ .flags = SAA7146_USE_I2C_IRQ, ++ ++ .module = THIS_MODULE, ++ .pci_tbl = pci_tbl, ++ .attach = budget_attach, ++ .detach = budget_detach, ++ ++ .irq_mask = MASK_10, ++ .irq_func = ttpci_budget_irq10_handler, ++}; ++ ++static int __init budget_init(void) ++{ ++ return saa7146_register_extension(&budget_extension); ++} ++ ++static void __exit budget_exit(void) ++{ ++ saa7146_unregister_extension(&budget_extension); ++} ++ ++module_init(budget_init); ++module_exit(budget_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others"); ++MODULE_DESCRIPTION("driver for the SAA7146 based so-called " ++ "budget PCI DVB cards by Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/pci/ttpci/budget.h b/drivers/media/pci/ttpci/budget.h +new file mode 100644 +index 0000000..3d8a806 +--- /dev/null ++++ b/drivers/media/pci/ttpci/budget.h +@@ -0,0 +1,124 @@ ++ ++#ifndef __BUDGET_DVB__ ++#define __BUDGET_DVB__ ++ ++#include "dvb_frontend.h" ++#include "dvbdev.h" ++#include "demux.h" ++#include "dvb_demux.h" ++#include "dmxdev.h" ++#include "dvb_filter.h" ++#include "dvb_net.h" ++ ++#include ++#include ++ ++#include ++ ++extern int budget_debug; ++ ++#ifdef dprintk ++#undef dprintk ++#endif ++ ++#define dprintk(level,args...) \ ++ do { if ((budget_debug & level)) { printk("%s: %s(): ", KBUILD_MODNAME, __func__); printk(args); } } while (0) ++ ++struct budget_info { ++ char *name; ++ int type; ++}; ++ ++/* place to store all the necessary device information */ ++struct budget { ++ ++ /* devices */ ++ struct dvb_device dvb_dev; ++ struct dvb_net dvb_net; ++ ++ struct saa7146_dev *dev; ++ ++ struct i2c_adapter i2c_adap; ++ struct budget_info *card; ++ ++ unsigned char *grabbing; ++ struct saa7146_pgtable pt; ++ ++ struct tasklet_struct fidb_tasklet; ++ struct tasklet_struct vpe_tasklet; ++ ++ struct dmxdev dmxdev; ++ struct dvb_demux demux; ++ ++ struct dmx_frontend hw_frontend; ++ struct dmx_frontend mem_frontend; ++ ++ int ci_present; ++ int video_port; ++ ++ u32 buffer_width; ++ u32 buffer_height; ++ u32 buffer_size; ++ u32 buffer_warning_threshold; ++ u32 buffer_warnings; ++ unsigned long buffer_warning_time; ++ ++ u32 ttbp; ++ int feeding; ++ ++ spinlock_t feedlock; ++ ++ spinlock_t debilock; ++ ++ struct dvb_adapter dvb_adapter; ++ struct dvb_frontend *dvb_frontend; ++ int (*read_fe_status)(struct dvb_frontend *fe, fe_status_t *status); ++ int fe_synced; ++ ++ void *priv; ++}; ++ ++#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \ ++static struct budget_info x_var ## _info = { \ ++ .name=x_name, \ ++ .type=x_type }; \ ++static struct saa7146_pci_extension_data x_var = { \ ++ .ext_priv = &x_var ## _info, \ ++ .ext = &budget_extension }; ++ ++#define BUDGET_TT 0 ++#define BUDGET_TT_HW_DISEQC 1 ++#define BUDGET_PATCH 3 ++#define BUDGET_FS_ACTIVY 4 ++#define BUDGET_CIN1200S 5 ++#define BUDGET_CIN1200C 6 ++#define BUDGET_CIN1200T 7 ++#define BUDGET_KNC1S 8 ++#define BUDGET_KNC1C 9 ++#define BUDGET_KNC1T 10 ++#define BUDGET_KNC1SP 11 ++#define BUDGET_KNC1CP 12 ++#define BUDGET_KNC1TP 13 ++#define BUDGET_TVSTAR 14 ++#define BUDGET_CIN1200C_MK3 15 ++#define BUDGET_KNC1C_MK3 16 ++#define BUDGET_KNC1CP_MK3 17 ++#define BUDGET_KNC1S2 18 ++#define BUDGET_KNC1C_TDA10024 19 ++ ++#define BUDGET_VIDEO_PORTA 0 ++#define BUDGET_VIDEO_PORTB 1 ++ ++extern int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, ++ struct saa7146_pci_extension_data *info, ++ struct module *owner, short *adapter_nums); ++extern void ttpci_budget_init_hooks(struct budget *budget); ++extern int ttpci_budget_deinit(struct budget *budget); ++extern void ttpci_budget_irq10_handler(struct saa7146_dev *dev, u32 * isr); ++extern void ttpci_budget_set_video_port(struct saa7146_dev *dev, int video_port); ++extern int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, ++ int uselocks, int nobusyloop); ++extern int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, int count, u32 value, ++ int uselocks, int nobusyloop); ++ ++#endif +diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.c b/drivers/media/pci/ttpci/ttpci-eeprom.c +new file mode 100644 +index 0000000..32d4315 +--- /dev/null ++++ b/drivers/media/pci/ttpci/ttpci-eeprom.c +@@ -0,0 +1,176 @@ ++/* ++ Retrieve encoded MAC address from 24C16 serial 2-wire EEPROM, ++ decode it and store it in the associated adapter struct for ++ use by dvb_net.c ++ ++ This card appear to have the 24C16 write protect held to ground, ++ thus permitting normal read/write operation. Theoretically it ++ would be possible to write routines to burn a different (encoded) ++ MAC address into the EEPROM. ++ ++ Robert Schlabbach GMX ++ Michael Glaum KVH Industries ++ Holger Waechtler Convergence ++ ++ Copyright (C) 2002-2003 Ralph Metzler ++ Metzler Brothers Systementwicklung GbR ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "ttpci-eeprom.h" ++ ++#if 1 ++#define dprintk(x...) do { printk(x); } while (0) ++#else ++#define dprintk(x...) do { } while (0) ++#endif ++ ++ ++static int check_mac_tt(u8 *buf) ++{ ++ int i; ++ u16 tmp = 0xffff; ++ ++ for (i = 0; i < 8; i++) { ++ tmp = (tmp << 8) | ((tmp >> 8) ^ buf[i]); ++ tmp ^= (tmp >> 4) & 0x0f; ++ tmp ^= (tmp << 12) ^ ((tmp & 0xff) << 5); ++ } ++ tmp ^= 0xffff; ++ return (((tmp >> 8) ^ buf[8]) | ((tmp & 0xff) ^ buf[9])); ++} ++ ++static int getmac_tt(u8 * decodedMAC, u8 * encodedMAC) ++{ ++ u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, ++ 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, ++ 0x1d, 0x36, 0x64, 0x78}; ++ u8 data[20]; ++ int i; ++ ++ /* In case there is a sig check failure have the orig contents available */ ++ memcpy(data, encodedMAC, 20); ++ ++ for (i = 0; i < 20; i++) ++ data[i] ^= xor[i]; ++ for (i = 0; i < 10; i++) ++ data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) ++ >> ((data[2 * i + 1] >> 6) & 3); ++ ++ if (check_mac_tt(data)) ++ return -ENODEV; ++ ++ decodedMAC[0] = data[2]; decodedMAC[1] = data[1]; decodedMAC[2] = data[0]; ++ decodedMAC[3] = data[6]; decodedMAC[4] = data[5]; decodedMAC[5] = data[4]; ++ return 0; ++} ++ ++int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC) ++{ ++ u8 xor[20] = { 0x72, 0x23, 0x68, 0x19, 0x5c, 0xa8, 0x71, 0x2c, ++ 0x54, 0xd3, 0x7b, 0xf1, 0x9E, 0x23, 0x16, 0xf6, ++ 0x1d, 0x36, 0x64, 0x78}; ++ u8 data[20]; ++ int i; ++ ++ memcpy(data, encodedMAC, 20); ++ ++ for (i = 0; i < 20; i++) ++ data[i] ^= xor[i]; ++ for (i = 0; i < 10; i++) ++ data[i] = ((data[2 * i + 1] << 8) | data[2 * i]) ++ >> ((data[2 * i + 1] >> 6) & 3); ++ ++ if (check_mac_tt(data)) ++ return -ENODEV; ++ ++ decodedMAC[0] = data[2]; ++ decodedMAC[1] = data[1]; ++ decodedMAC[2] = data[0]; ++ decodedMAC[3] = data[6]; ++ decodedMAC[4] = data[5]; ++ decodedMAC[5] = data[4]; ++ return 0; ++} ++EXPORT_SYMBOL(ttpci_eeprom_decode_mac); ++ ++static int ttpci_eeprom_read_encodedMAC(struct i2c_adapter *adapter, u8 * encodedMAC) ++{ ++ int ret; ++ u8 b0[] = { 0xcc }; ++ ++ struct i2c_msg msg[] = { ++ { .addr = 0x50, .flags = 0, .buf = b0, .len = 1 }, ++ { .addr = 0x50, .flags = I2C_M_RD, .buf = encodedMAC, .len = 20 } ++ }; ++ ++ /* dprintk("%s\n", __func__); */ ++ ++ ret = i2c_transfer(adapter, msg, 2); ++ ++ if (ret != 2) /* Assume EEPROM isn't there */ ++ return (-ENODEV); ++ ++ return 0; ++} ++ ++ ++int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *proposed_mac) ++{ ++ int ret, i; ++ u8 encodedMAC[20]; ++ u8 decodedMAC[6]; ++ ++ ret = ttpci_eeprom_read_encodedMAC(adapter, encodedMAC); ++ ++ if (ret != 0) { /* Will only be -ENODEV */ ++ dprintk("Couldn't read from EEPROM: not there?\n"); ++ memset(proposed_mac, 0, 6); ++ return ret; ++ } ++ ++ ret = getmac_tt(decodedMAC, encodedMAC); ++ if( ret != 0 ) { ++ dprintk("adapter failed MAC signature check\n"); ++ dprintk("encoded MAC from EEPROM was " ); ++ for(i=0; i<19; i++) { ++ dprintk( "%.2x:", encodedMAC[i]); ++ } ++ dprintk("%.2x\n", encodedMAC[19]); ++ memset(proposed_mac, 0, 6); ++ return ret; ++ } ++ ++ memcpy(proposed_mac, decodedMAC, 6); ++ dprintk("adapter has MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", ++ decodedMAC[0], decodedMAC[1], decodedMAC[2], ++ decodedMAC[3], decodedMAC[4], decodedMAC[5]); ++ return 0; ++} ++ ++EXPORT_SYMBOL(ttpci_eeprom_parse_mac); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, others"); ++MODULE_DESCRIPTION("Decode dvb_net MAC address from EEPROM of PCI DVB cards " ++ "made by Siemens, Technotrend, Hauppauge"); +diff --git a/drivers/media/pci/ttpci/ttpci-eeprom.h b/drivers/media/pci/ttpci/ttpci-eeprom.h +new file mode 100644 +index 0000000..dcc33d5 +--- /dev/null ++++ b/drivers/media/pci/ttpci/ttpci-eeprom.h +@@ -0,0 +1,34 @@ ++/* ++ Retrieve encoded MAC address from ATMEL ttpci_eeprom serial 2-wire EEPROM, ++ decode it and store it in associated adapter net device ++ ++ Robert Schlabbach GMX ++ Michael Glaum KVH Industries ++ Holger Waechtler Convergence ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++*/ ++ ++#ifndef __TTPCI_EEPROM_H__ ++#define __TTPCI_EEPROM_H__ ++ ++#include ++#include ++ ++extern int ttpci_eeprom_decode_mac(u8 *decodedMAC, u8 *encodedMAC); ++extern int ttpci_eeprom_parse_mac(struct i2c_adapter *adapter, u8 *propsed_mac); ++ ++#endif +diff --git a/drivers/media/pci/zoran/Kconfig b/drivers/media/pci/zoran/Kconfig +new file mode 100644 +index 0000000..26ca870 +--- /dev/null ++++ b/drivers/media/pci/zoran/Kconfig +@@ -0,0 +1,74 @@ ++config VIDEO_ZORAN ++ tristate "Zoran ZR36057/36067 Video For Linux" ++ depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS ++ help ++ Say Y for support for MJPEG capture cards based on the Zoran ++ 36057/36067 PCI controller chipset. This includes the Iomega ++ Buz, Pinnacle DC10+ and the Linux Media Labs LML33. There is ++ a driver homepage at . For ++ more information, check . ++ ++ To compile this driver as a module, choose M here: the ++ module will be called zr36067. ++ ++config VIDEO_ZORAN_DC30 ++ tristate "Pinnacle/Miro DC30(+) support" ++ depends on VIDEO_ZORAN ++ select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_VPX3220 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback ++ card. This also supports really old DC10 cards based on the ++ zr36050 MJPEG codec and zr36016 VFE. ++ ++config VIDEO_ZORAN_ZR36060 ++ tristate "Zoran ZR36060" ++ depends on VIDEO_ZORAN ++ help ++ Say Y to support Zoran boards based on 36060 chips. ++ This includes Iomega Buz, Pinnacle DC10, Linux media Labs 33 ++ and 33 R10 and AverMedia 6 boards. ++ ++config VIDEO_ZORAN_BUZ ++ tristate "Iomega Buz support" ++ depends on VIDEO_ZORAN_ZR36060 ++ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_SAA7185 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for the Iomega Buz MJPEG capture/playback card. ++ ++config VIDEO_ZORAN_DC10 ++ tristate "Pinnacle/Miro DC10(+) support" ++ depends on VIDEO_ZORAN_ZR36060 ++ select VIDEO_SAA7110 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_ADV7175 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback ++ card. ++ ++config VIDEO_ZORAN_LML33 ++ tristate "Linux Media Labs LML33 support" ++ depends on VIDEO_ZORAN_ZR36060 ++ select VIDEO_BT819 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for the Linux Media Labs LML33 MJPEG capture/playback ++ card. ++ ++config VIDEO_ZORAN_LML33R10 ++ tristate "Linux Media Labs LML33R10 support" ++ depends on VIDEO_ZORAN_ZR36060 ++ select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_ADV7170 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ support for the Linux Media Labs LML33R10 MJPEG capture/playback ++ card. ++ ++config VIDEO_ZORAN_AVS6EYES ++ tristate "AverMedia 6 Eyes support" ++ depends on VIDEO_ZORAN_ZR36060 ++ select VIDEO_BT856 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_BT866 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_KS0127 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Support for the AverMedia 6 Eyes video surveillance card. +diff --git a/drivers/media/pci/zoran/Makefile b/drivers/media/pci/zoran/Makefile +new file mode 100644 +index 0000000..44cc133 +--- /dev/null ++++ b/drivers/media/pci/zoran/Makefile +@@ -0,0 +1,6 @@ ++zr36067-objs := zoran_procfs.o zoran_device.o \ ++ zoran_driver.o zoran_card.o ++ ++obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o ++obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o ++obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o +diff --git a/drivers/media/pci/zoran/videocodec.c b/drivers/media/pci/zoran/videocodec.c +new file mode 100644 +index 0000000..c010716 +--- /dev/null ++++ b/drivers/media/pci/zoran/videocodec.c +@@ -0,0 +1,407 @@ ++/* ++ * VIDEO MOTION CODECs internal API for video devices ++ * ++ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's ++ * bound to a master device. ++ * ++ * (c) 2002 Wolfgang Scherr ++ * ++ * $Id: videocodec.c,v 1.1.2.8 2003/03/29 07:16:04 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * 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 VIDEOCODEC_VERSION "v0.2" ++ ++#include ++#include ++#include ++#include ++#include ++ ++// kernel config is here (procfs flag) ++ ++#ifdef CONFIG_PROC_FS ++#include ++#include ++#include ++#endif ++ ++#include "videocodec.h" ++ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-4)"); ++ ++#define dprintk(num, format, args...) \ ++ do { \ ++ if (debug >= num) \ ++ printk(format, ##args); \ ++ } while (0) ++ ++struct attached_list { ++ struct videocodec *codec; ++ struct attached_list *next; ++}; ++ ++struct codec_list { ++ const struct videocodec *codec; ++ int attached; ++ struct attached_list *list; ++ struct codec_list *next; ++}; ++ ++static struct codec_list *codeclist_top = NULL; ++ ++/* ================================================= */ ++/* function prototypes of the master/slave interface */ ++/* ================================================= */ ++ ++struct videocodec * ++videocodec_attach (struct videocodec_master *master) ++{ ++ struct codec_list *h = codeclist_top; ++ struct attached_list *a, *ptr; ++ struct videocodec *codec; ++ int res; ++ ++ if (!master) { ++ dprintk(1, KERN_ERR "videocodec_attach: no data\n"); ++ return NULL; ++ } ++ ++ dprintk(2, ++ "videocodec_attach: '%s', flags %lx, magic %lx\n", ++ master->name, master->flags, master->magic); ++ ++ if (!h) { ++ dprintk(1, ++ KERN_ERR ++ "videocodec_attach: no device available\n"); ++ return NULL; ++ } ++ ++ while (h) { ++ // attach only if the slave has at least the flags ++ // expected by the master ++ if ((master->flags & h->codec->flags) == master->flags) { ++ dprintk(4, "videocodec_attach: try '%s'\n", ++ h->codec->name); ++ ++ if (!try_module_get(h->codec->owner)) ++ return NULL; ++ ++ codec = kmemdup(h->codec, sizeof(struct videocodec), ++ GFP_KERNEL); ++ if (!codec) { ++ dprintk(1, ++ KERN_ERR ++ "videocodec_attach: no mem\n"); ++ goto out_module_put; ++ } ++ ++ snprintf(codec->name, sizeof(codec->name), ++ "%s[%d]", codec->name, h->attached); ++ codec->master_data = master; ++ res = codec->setup(codec); ++ if (res == 0) { ++ dprintk(3, "videocodec_attach '%s'\n", ++ codec->name); ++ ptr = kzalloc(sizeof(struct attached_list), GFP_KERNEL); ++ if (!ptr) { ++ dprintk(1, ++ KERN_ERR ++ "videocodec_attach: no memory\n"); ++ goto out_kfree; ++ } ++ ptr->codec = codec; ++ ++ a = h->list; ++ if (!a) { ++ h->list = ptr; ++ dprintk(4, ++ "videocodec: first element\n"); ++ } else { ++ while (a->next) ++ a = a->next; // find end ++ a->next = ptr; ++ dprintk(4, ++ "videocodec: in after '%s'\n", ++ h->codec->name); ++ } ++ ++ h->attached += 1; ++ return codec; ++ } else { ++ kfree(codec); ++ } ++ } ++ h = h->next; ++ } ++ ++ dprintk(1, KERN_ERR "videocodec_attach: no codec found!\n"); ++ return NULL; ++ ++ out_module_put: ++ module_put(h->codec->owner); ++ out_kfree: ++ kfree(codec); ++ return NULL; ++} ++ ++int ++videocodec_detach (struct videocodec *codec) ++{ ++ struct codec_list *h = codeclist_top; ++ struct attached_list *a, *prev; ++ int res; ++ ++ if (!codec) { ++ dprintk(1, KERN_ERR "videocodec_detach: no data\n"); ++ return -EINVAL; ++ } ++ ++ dprintk(2, ++ "videocodec_detach: '%s', type: %x, flags %lx, magic %lx\n", ++ codec->name, codec->type, codec->flags, codec->magic); ++ ++ if (!h) { ++ dprintk(1, ++ KERN_ERR "videocodec_detach: no device left...\n"); ++ return -ENXIO; ++ } ++ ++ while (h) { ++ a = h->list; ++ prev = NULL; ++ while (a) { ++ if (codec == a->codec) { ++ res = a->codec->unset(a->codec); ++ if (res >= 0) { ++ dprintk(3, ++ "videocodec_detach: '%s'\n", ++ a->codec->name); ++ a->codec->master_data = NULL; ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "videocodec_detach: '%s'\n", ++ a->codec->name); ++ a->codec->master_data = NULL; ++ } ++ if (prev == NULL) { ++ h->list = a->next; ++ dprintk(4, ++ "videocodec: delete first\n"); ++ } else { ++ prev->next = a->next; ++ dprintk(4, ++ "videocodec: delete middle\n"); ++ } ++ module_put(a->codec->owner); ++ kfree(a->codec); ++ kfree(a); ++ h->attached -= 1; ++ return 0; ++ } ++ prev = a; ++ a = a->next; ++ } ++ h = h->next; ++ } ++ ++ dprintk(1, KERN_ERR "videocodec_detach: given codec not found!\n"); ++ return -EINVAL; ++} ++ ++int ++videocodec_register (const struct videocodec *codec) ++{ ++ struct codec_list *ptr, *h = codeclist_top; ++ ++ if (!codec) { ++ dprintk(1, KERN_ERR "videocodec_register: no data!\n"); ++ return -EINVAL; ++ } ++ ++ dprintk(2, ++ "videocodec: register '%s', type: %x, flags %lx, magic %lx\n", ++ codec->name, codec->type, codec->flags, codec->magic); ++ ++ ptr = kzalloc(sizeof(struct codec_list), GFP_KERNEL); ++ if (!ptr) { ++ dprintk(1, KERN_ERR "videocodec_register: no memory\n"); ++ return -ENOMEM; ++ } ++ ptr->codec = codec; ++ ++ if (!h) { ++ codeclist_top = ptr; ++ dprintk(4, "videocodec: hooked in as first element\n"); ++ } else { ++ while (h->next) ++ h = h->next; // find the end ++ h->next = ptr; ++ dprintk(4, "videocodec: hooked in after '%s'\n", ++ h->codec->name); ++ } ++ ++ return 0; ++} ++ ++int ++videocodec_unregister (const struct videocodec *codec) ++{ ++ struct codec_list *prev = NULL, *h = codeclist_top; ++ ++ if (!codec) { ++ dprintk(1, KERN_ERR "videocodec_unregister: no data!\n"); ++ return -EINVAL; ++ } ++ ++ dprintk(2, ++ "videocodec: unregister '%s', type: %x, flags %lx, magic %lx\n", ++ codec->name, codec->type, codec->flags, codec->magic); ++ ++ if (!h) { ++ dprintk(1, ++ KERN_ERR ++ "videocodec_unregister: no device left...\n"); ++ return -ENXIO; ++ } ++ ++ while (h) { ++ if (codec == h->codec) { ++ if (h->attached) { ++ dprintk(1, ++ KERN_ERR ++ "videocodec: '%s' is used\n", ++ h->codec->name); ++ return -EBUSY; ++ } ++ dprintk(3, "videocodec: unregister '%s' is ok.\n", ++ h->codec->name); ++ if (prev == NULL) { ++ codeclist_top = h->next; ++ dprintk(4, ++ "videocodec: delete first element\n"); ++ } else { ++ prev->next = h->next; ++ dprintk(4, ++ "videocodec: delete middle element\n"); ++ } ++ kfree(h); ++ return 0; ++ } ++ prev = h; ++ h = h->next; ++ } ++ ++ dprintk(1, ++ KERN_ERR ++ "videocodec_unregister: given codec not found!\n"); ++ return -EINVAL; ++} ++ ++#ifdef CONFIG_PROC_FS ++static int proc_videocodecs_show(struct seq_file *m, void *v) ++{ ++ struct codec_list *h = codeclist_top; ++ struct attached_list *a; ++ ++ seq_printf(m, "lave or attached aster name type flags magic "); ++ seq_printf(m, "(connected as)\n"); ++ ++ h = codeclist_top; ++ while (h) { ++ seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", ++ h->codec->name, h->codec->type, ++ h->codec->flags, h->codec->magic); ++ a = h->list; ++ while (a) { ++ seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", ++ a->codec->master_data->name, ++ a->codec->master_data->type, ++ a->codec->master_data->flags, ++ a->codec->master_data->magic, ++ a->codec->name); ++ a = a->next; ++ } ++ h = h->next; ++ } ++ ++ return 0; ++} ++ ++static int proc_videocodecs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, proc_videocodecs_show, NULL); ++} ++ ++static const struct file_operations videocodecs_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = proc_videocodecs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++#endif ++ ++/* ===================== */ ++/* hook in driver module */ ++/* ===================== */ ++static int __init ++videocodec_init (void) ++{ ++#ifdef CONFIG_PROC_FS ++ static struct proc_dir_entry *videocodec_proc_entry; ++#endif ++ ++ printk(KERN_INFO "Linux video codec intermediate layer: %s\n", ++ VIDEOCODEC_VERSION); ++ ++#ifdef CONFIG_PROC_FS ++ videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops); ++ if (!videocodec_proc_entry) { ++ dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); ++ } ++#endif ++ return 0; ++} ++ ++static void __exit ++videocodec_exit (void) ++{ ++#ifdef CONFIG_PROC_FS ++ remove_proc_entry("videocodecs", NULL); ++#endif ++} ++ ++EXPORT_SYMBOL(videocodec_attach); ++EXPORT_SYMBOL(videocodec_detach); ++EXPORT_SYMBOL(videocodec_register); ++EXPORT_SYMBOL(videocodec_unregister); ++ ++module_init(videocodec_init); ++module_exit(videocodec_exit); ++ ++MODULE_AUTHOR("Wolfgang Scherr "); ++MODULE_DESCRIPTION("Intermediate API module for video codecs " ++ VIDEOCODEC_VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/zoran/videocodec.h b/drivers/media/pci/zoran/videocodec.h +new file mode 100644 +index 0000000..def5558 +--- /dev/null ++++ b/drivers/media/pci/zoran/videocodec.h +@@ -0,0 +1,353 @@ ++/* ++ * VIDEO MOTION CODECs internal API for video devices ++ * ++ * Interface for MJPEG (and maybe later MPEG/WAVELETS) codec's ++ * bound to a master device. ++ * ++ * (c) 2002 Wolfgang Scherr ++ * ++ * $Id: videocodec.h,v 1.1.2.4 2003/01/14 21:15:03 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * 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. ++ * ++ * ------------------------------------------------------------------------ ++ */ ++ ++/* =================== */ ++/* general description */ ++/* =================== */ ++ ++/* Should ease the (re-)usage of drivers supporting cards with (different) ++ video codecs. The codecs register to this module their functionality, ++ and the processors (masters) can attach to them if they fit. ++ ++ The codecs are typically have a "strong" binding to their master - so I ++ don't think it makes sense to have a full blown interfacing as with e.g. ++ i2c. If you have an other opinion, let's discuss & implement it :-))) ++ ++ Usage: ++ ++ The slave has just to setup the videocodec structure and use two functions: ++ videocodec_register(codecdata); ++ videocodec_unregister(codecdata); ++ The best is just calling them at module (de-)initialisation. ++ ++ The master sets up the structure videocodec_master and calls: ++ codecdata=videocodec_attach(master_codecdata); ++ videocodec_detach(codecdata); ++ ++ The slave is called during attach/detach via functions setup previously ++ during register. At that time, the master_data pointer is set up ++ and the slave can access any io registers of the master device (in the case ++ the slave is bound to it). Otherwise it doesn't need this functions and ++ therfor they may not be initialized. ++ ++ The other functions are just for convenience, as they are for sure used by ++ most/all of the codecs. The last ones may be omitted, too. ++ ++ See the structure declaration below for more information and which data has ++ to be set up for the master and the slave. ++ ++ ---------------------------------------------------------------------------- ++ The master should have "knowledge" of the slave and vice versa. So the data ++ structures sent to/from slave via set_data/get_data set_image/get_image are ++ device dependent and vary between MJPEG/MPEG/WAVELET/... devices. (!!!!) ++ ---------------------------------------------------------------------------- ++*/ ++ ++ ++/* ========================================== */ ++/* description of the videocodec_io structure */ ++/* ========================================== */ ++ ++/* ++ ==== master setup ==== ++ name -> name of the device structure for reference and debugging ++ master_data -> data ref. for the master (e.g. the zr36055,57,67) ++ readreg -> ref. to read-fn from register (setup by master, used by slave) ++ writereg -> ref. to write-fn to register (setup by master, used by slave) ++ this two functions do the lowlevel I/O job ++ ++ ==== slave functionality setup ==== ++ slave_data -> data ref. for the slave (e.g. the zr36050,60) ++ check -> fn-ref. checks availability of an device, returns -EIO on failure or ++ the type on success ++ this makes espcecially sense if a driver module supports more than ++ one codec which may be quite similar to access, nevertheless it ++ is good for a first functionality check ++ ++ -- main functions you always need for compression/decompression -- ++ ++ set_mode -> this fn-ref. resets the entire codec, and sets up the mode ++ with the last defined norm/size (or device default if not ++ available) - it returns 0 if the mode is possible ++ set_size -> this fn-ref. sets the norm and image size for ++ compression/decompression (returns 0 on success) ++ the norm param is defined in videodev2.h (V4L2_STD_*) ++ ++ additional setup may be available, too - but the codec should work with ++ some default values even without this ++ ++ set_data -> sets device-specific data (tables, quality etc.) ++ get_data -> query device-specific data (tables, quality etc.) ++ ++ if the device delivers interrupts, they may be setup/handled here ++ setup_interrupt -> codec irq setup (not needed for 36050/60) ++ handle_interrupt -> codec irq handling (not needed for 36050/60) ++ ++ if the device delivers pictures, they may be handled here ++ put_image -> puts image data to the codec (not needed for 36050/60) ++ get_image -> gets image data from the codec (not needed for 36050/60) ++ the calls include frame numbers and flags (even/odd/...) ++ if needed and a flag which allows blocking until its ready ++*/ ++ ++/* ============== */ ++/* user interface */ ++/* ============== */ ++ ++/* ++ Currently there is only a information display planned, as the layer ++ is not visible for the user space at all. ++ ++ Information is available via procfs. The current entry is "/proc/videocodecs" ++ but it makes sense to "hide" it in the /proc/video tree of v4l(2) --TODO--. ++ ++A example for such an output is: ++ ++lave or attached aster name type flags magic (connected as) ++S zr36050 0002 0000d001 00000000 (TEMPLATE) ++M zr36055[0] 0001 0000c001 00000000 (zr36050[0]) ++M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) ++ ++*/ ++ ++ ++/* =============================================== */ ++/* special defines for the videocodec_io structure */ ++/* =============================================== */ ++ ++#ifndef __LINUX_VIDEOCODEC_H ++#define __LINUX_VIDEOCODEC_H ++ ++#include ++ ++#define CODEC_DO_COMPRESSION 0 ++#define CODEC_DO_EXPANSION 1 ++ ++/* this are the current codec flags I think they are needed */ ++/* -> type value in structure */ ++#define CODEC_FLAG_JPEG 0x00000001L // JPEG codec ++#define CODEC_FLAG_MPEG 0x00000002L // MPEG1/2/4 codec ++#define CODEC_FLAG_DIVX 0x00000004L // DIVX codec ++#define CODEC_FLAG_WAVELET 0x00000008L // WAVELET codec ++ // room for other types ++ ++#define CODEC_FLAG_MAGIC 0x00000800L // magic key must match ++#define CODEC_FLAG_HARDWARE 0x00001000L // is a hardware codec ++#define CODEC_FLAG_VFE 0x00002000L // has direct video frontend ++#define CODEC_FLAG_ENCODER 0x00004000L // compression capability ++#define CODEC_FLAG_DECODER 0x00008000L // decompression capability ++#define CODEC_FLAG_NEEDIRQ 0x00010000L // needs irq handling ++#define CODEC_FLAG_RDWRPIC 0x00020000L // handles picture I/O ++ ++/* a list of modes, some are just examples (is there any HW?) */ ++#define CODEC_MODE_BJPG 0x0001 // Baseline JPEG ++#define CODEC_MODE_LJPG 0x0002 // Lossless JPEG ++#define CODEC_MODE_MPEG1 0x0003 // MPEG 1 ++#define CODEC_MODE_MPEG2 0x0004 // MPEG 2 ++#define CODEC_MODE_MPEG4 0x0005 // MPEG 4 ++#define CODEC_MODE_MSDIVX 0x0006 // MS DivX ++#define CODEC_MODE_ODIVX 0x0007 // Open DivX ++#define CODEC_MODE_WAVELET 0x0008 // Wavelet ++ ++/* this are the current codec types I want to implement */ ++/* -> type value in structure */ ++#define CODEC_TYPE_NONE 0 ++#define CODEC_TYPE_L64702 1 ++#define CODEC_TYPE_ZR36050 2 ++#define CODEC_TYPE_ZR36016 3 ++#define CODEC_TYPE_ZR36060 4 ++ ++/* the type of data may be enhanced by future implementations (data-fn.'s) */ ++/* -> used in command */ ++#define CODEC_G_STATUS 0x0000 /* codec status (query only) */ ++#define CODEC_S_CODEC_MODE 0x0001 /* codec mode (baseline JPEG, MPEG1,... */ ++#define CODEC_G_CODEC_MODE 0x8001 ++#define CODEC_S_VFE 0x0002 /* additional video frontend setup */ ++#define CODEC_G_VFE 0x8002 ++#define CODEC_S_MMAP 0x0003 /* MMAP setup (if available) */ ++ ++#define CODEC_S_JPEG_TDS_BYTE 0x0010 /* target data size in bytes */ ++#define CODEC_G_JPEG_TDS_BYTE 0x8010 ++#define CODEC_S_JPEG_SCALE 0x0011 /* scaling factor for quant. tables */ ++#define CODEC_G_JPEG_SCALE 0x8011 ++#define CODEC_S_JPEG_HDT_DATA 0x0018 /* huffman-tables */ ++#define CODEC_G_JPEG_HDT_DATA 0x8018 ++#define CODEC_S_JPEG_QDT_DATA 0x0019 /* quantizing-tables */ ++#define CODEC_G_JPEG_QDT_DATA 0x8019 ++#define CODEC_S_JPEG_APP_DATA 0x001A /* APP marker */ ++#define CODEC_G_JPEG_APP_DATA 0x801A ++#define CODEC_S_JPEG_COM_DATA 0x001B /* COM marker */ ++#define CODEC_G_JPEG_COM_DATA 0x801B ++ ++#define CODEC_S_PRIVATE 0x1000 /* "private" commands start here */ ++#define CODEC_G_PRIVATE 0x9000 ++ ++#define CODEC_G_FLAG 0x8000 /* this is how 'get' is detected */ ++ ++/* types of transfer, directly user space or a kernel buffer (image-fn.'s) */ ++/* -> used in get_image, put_image */ ++#define CODEC_TRANSFER_KERNEL 0 /* use "memcopy" */ ++#define CODEC_TRANSFER_USER 1 /* use "to/from_user" */ ++ ++ ++/* ========================= */ ++/* the structures itself ... */ ++/* ========================= */ ++ ++struct vfe_polarity { ++ unsigned int vsync_pol:1; ++ unsigned int hsync_pol:1; ++ unsigned int field_pol:1; ++ unsigned int blank_pol:1; ++ unsigned int subimg_pol:1; ++ unsigned int poe_pol:1; ++ unsigned int pvalid_pol:1; ++ unsigned int vclk_pol:1; ++}; ++ ++struct vfe_settings { ++ __u32 x, y; /* Offsets into image */ ++ __u32 width, height; /* Area to capture */ ++ __u16 decimation; /* Decimation divider */ ++ __u16 flags; /* Flags for capture */ ++ __u16 quality; /* quality of the video */ ++}; ++ ++struct tvnorm { ++ u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; ++}; ++ ++struct jpeg_com_marker { ++ int len; /* number of usable bytes in data */ ++ char data[60]; ++}; ++ ++struct jpeg_app_marker { ++ int appn; /* number app segment */ ++ int len; /* number of usable bytes in data */ ++ char data[60]; ++}; ++ ++struct videocodec { ++ struct module *owner; ++ /* -- filled in by slave device during register -- */ ++ char name[32]; ++ unsigned long magic; /* may be used for client<->master attaching */ ++ unsigned long flags; /* functionality flags */ ++ unsigned int type; /* codec type */ ++ ++ /* -- these is filled in later during master device attach -- */ ++ ++ struct videocodec_master *master_data; ++ ++ /* -- these are filled in by the slave device during register -- */ ++ ++ void *data; /* private slave data */ ++ ++ /* attach/detach client functions (indirect call) */ ++ int (*setup) (struct videocodec * codec); ++ int (*unset) (struct videocodec * codec); ++ ++ /* main functions, every client needs them for sure! */ ++ // set compression or decompression (or freeze, stop, standby, etc) ++ int (*set_mode) (struct videocodec * codec, ++ int mode); ++ // setup picture size and norm (for the codec's video frontend) ++ int (*set_video) (struct videocodec * codec, ++ struct tvnorm * norm, ++ struct vfe_settings * cap, ++ struct vfe_polarity * pol); ++ // other control commands, also mmap setup etc. ++ int (*control) (struct videocodec * codec, ++ int type, ++ int size, ++ void *data); ++ ++ /* additional setup/query/processing (may be NULL pointer) */ ++ // interrupt setup / handling (for irq's delivered by master) ++ int (*setup_interrupt) (struct videocodec * codec, ++ long mode); ++ int (*handle_interrupt) (struct videocodec * codec, ++ int source, ++ long flag); ++ // picture interface (if any) ++ long (*put_image) (struct videocodec * codec, ++ int tr_type, ++ int block, ++ long *fr_num, ++ long *flag, ++ long size, ++ void *buf); ++ long (*get_image) (struct videocodec * codec, ++ int tr_type, ++ int block, ++ long *fr_num, ++ long *flag, ++ long size, ++ void *buf); ++}; ++ ++struct videocodec_master { ++ /* -- filled in by master device for registration -- */ ++ char name[32]; ++ unsigned long magic; /* may be used for client<->master attaching */ ++ unsigned long flags; /* functionality flags */ ++ unsigned int type; /* master type */ ++ ++ void *data; /* private master data */ ++ ++ __u32(*readreg) (struct videocodec * codec, ++ __u16 reg); ++ void (*writereg) (struct videocodec * codec, ++ __u16 reg, ++ __u32 value); ++}; ++ ++ ++/* ================================================= */ ++/* function prototypes of the master/slave interface */ ++/* ================================================= */ ++ ++/* attach and detach commands for the master */ ++// * master structure needs to be kmalloc'ed before calling attach ++// and free'd after calling detach ++// * returns pointer on success, NULL on failure ++extern struct videocodec *videocodec_attach(struct videocodec_master *); ++// * 0 on success, <0 (errno) on failure ++extern int videocodec_detach(struct videocodec *); ++ ++/* register and unregister commands for the slaves */ ++// * 0 on success, <0 (errno) on failure ++extern int videocodec_register(const struct videocodec *); ++// * 0 on success, <0 (errno) on failure ++extern int videocodec_unregister(const struct videocodec *); ++ ++/* the other calls are directly done via the videocodec structure! */ ++ ++#endif /*ifndef __LINUX_VIDEOCODEC_H */ +diff --git a/drivers/media/pci/zoran/zoran.h b/drivers/media/pci/zoran/zoran.h +new file mode 100644 +index 0000000..ca2754a +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran.h +@@ -0,0 +1,403 @@ ++/* ++ * zoran - Iomega Buz driver ++ * ++ * Copyright (C) 1999 Rainer Johanni ++ * ++ * based on ++ * ++ * zoran.0.0.3 Copyright (C) 1998 Dave Perks ++ * ++ * and ++ * ++ * bttv - Bt848 frame grabber driver ++ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ * & Marcus Metzler (mocm@thp.uni-koeln.de) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _BUZ_H_ ++#define _BUZ_H_ ++ ++#include ++ ++struct zoran_sync { ++ unsigned long frame; /* number of buffer that has been free'd */ ++ unsigned long length; /* number of code bytes in buffer (capture only) */ ++ unsigned long seq; /* frame sequence number */ ++ struct timeval timestamp; /* timestamp */ ++}; ++ ++ ++#define ZORAN_NAME "ZORAN" /* name of the device */ ++ ++#define ZR_DEVNAME(zr) ((zr)->name) ++ ++#define BUZ_MAX_WIDTH (zr->timing->Wa) ++#define BUZ_MAX_HEIGHT (zr->timing->Ha) ++#define BUZ_MIN_WIDTH 32 /* never display less than 32 pixels */ ++#define BUZ_MIN_HEIGHT 24 /* never display less than 24 rows */ ++ ++#define BUZ_NUM_STAT_COM 4 ++#define BUZ_MASK_STAT_COM 3 ++ ++#define BUZ_MAX_FRAME 256 /* Must be a power of 2 */ ++#define BUZ_MASK_FRAME 255 /* Must be BUZ_MAX_FRAME-1 */ ++ ++#define BUZ_MAX_INPUT 16 ++ ++#if VIDEO_MAX_FRAME <= 32 ++# define V4L_MAX_FRAME 32 ++#elif VIDEO_MAX_FRAME <= 64 ++# define V4L_MAX_FRAME 64 ++#else ++# error "Too many video frame buffers to handle" ++#endif ++#define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) ++ ++#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME) ++ ++#include "zr36057.h" ++ ++enum card_type { ++ UNKNOWN = -1, ++ ++ /* Pinnacle/Miro */ ++ DC10_old, /* DC30 like */ ++ DC10_new, /* DC10plus like */ ++ DC10plus, ++ DC30, ++ DC30plus, ++ ++ /* Linux Media Labs */ ++ LML33, ++ LML33R10, ++ ++ /* Iomega */ ++ BUZ, ++ ++ /* AverMedia */ ++ AVS6EYES, ++ ++ /* total number of cards */ ++ NUM_CARDS ++}; ++ ++enum zoran_codec_mode { ++ BUZ_MODE_IDLE, /* nothing going on */ ++ BUZ_MODE_MOTION_COMPRESS, /* grabbing frames */ ++ BUZ_MODE_MOTION_DECOMPRESS, /* playing frames */ ++ BUZ_MODE_STILL_COMPRESS, /* still frame conversion */ ++ BUZ_MODE_STILL_DECOMPRESS /* still frame conversion */ ++}; ++ ++enum zoran_buffer_state { ++ BUZ_STATE_USER, /* buffer is owned by application */ ++ BUZ_STATE_PEND, /* buffer is queued in pend[] ready to feed to I/O */ ++ BUZ_STATE_DMA, /* buffer is queued in dma[] for I/O */ ++ BUZ_STATE_DONE /* buffer is ready to return to application */ ++}; ++ ++enum zoran_map_mode { ++ ZORAN_MAP_MODE_RAW, ++ ZORAN_MAP_MODE_JPG_REC, ++#define ZORAN_MAP_MODE_JPG ZORAN_MAP_MODE_JPG_REC ++ ZORAN_MAP_MODE_JPG_PLAY, ++}; ++ ++enum gpio_type { ++ ZR_GPIO_JPEG_SLEEP = 0, ++ ZR_GPIO_JPEG_RESET, ++ ZR_GPIO_JPEG_FRAME, ++ ZR_GPIO_VID_DIR, ++ ZR_GPIO_VID_EN, ++ ZR_GPIO_VID_RESET, ++ ZR_GPIO_CLK_SEL1, ++ ZR_GPIO_CLK_SEL2, ++ ZR_GPIO_MAX, ++}; ++ ++enum gpcs_type { ++ GPCS_JPEG_RESET = 0, ++ GPCS_JPEG_START, ++ GPCS_MAX, ++}; ++ ++struct zoran_format { ++ char *name; ++ __u32 fourcc; ++ int colorspace; ++ int depth; ++ __u32 flags; ++ __u32 vfespfr; ++}; ++/* flags */ ++#define ZORAN_FORMAT_COMPRESSED 1<<0 ++#define ZORAN_FORMAT_OVERLAY 1<<1 ++#define ZORAN_FORMAT_CAPTURE 1<<2 ++#define ZORAN_FORMAT_PLAYBACK 1<<3 ++ ++/* overlay-settings */ ++struct zoran_overlay_settings { ++ int is_set; ++ int x, y, width, height; /* position */ ++ int clipcount; /* position and number of clips */ ++ const struct zoran_format *format; /* overlay format */ ++}; ++ ++/* v4l-capture settings */ ++struct zoran_v4l_settings { ++ int width, height, bytesperline; /* capture size */ ++ const struct zoran_format *format; /* capture format */ ++}; ++ ++/* jpg-capture/-playback settings */ ++struct zoran_jpg_settings { ++ int decimation; /* this bit is used to set everything to default */ ++ int HorDcm, VerDcm, TmpDcm; /* capture decimation settings (TmpDcm=1 means both fields) */ ++ int field_per_buff, odd_even; /* field-settings (odd_even=1 (+TmpDcm=1) means top-field-first) */ ++ int img_x, img_y, img_width, img_height; /* crop settings (subframe capture) */ ++ struct v4l2_jpegcompression jpg_comp; /* JPEG-specific capture settings */ ++}; ++ ++struct zoran_fh; ++ ++struct zoran_mapping { ++ struct zoran_fh *fh; ++ int count; ++}; ++ ++struct zoran_buffer { ++ struct zoran_mapping *map; ++ enum zoran_buffer_state state; /* state: unused/pending/dma/done */ ++ struct zoran_sync bs; /* DONE: info to return to application */ ++ union { ++ struct { ++ __le32 *frag_tab; /* addresses of frag table */ ++ u32 frag_tab_bus; /* same value cached to save time in ISR */ ++ } jpg; ++ struct { ++ char *fbuffer; /* virtual address of frame buffer */ ++ unsigned long fbuffer_phys;/* physical address of frame buffer */ ++ unsigned long fbuffer_bus;/* bus address of frame buffer */ ++ } v4l; ++ }; ++}; ++ ++enum zoran_lock_activity { ++ ZORAN_FREE, /* free for use */ ++ ZORAN_ACTIVE, /* active but unlocked */ ++ ZORAN_LOCKED, /* locked */ ++}; ++ ++/* buffer collections */ ++struct zoran_buffer_col { ++ enum zoran_lock_activity active; /* feature currently in use? */ ++ unsigned int num_buffers, buffer_size; ++ struct zoran_buffer buffer[MAX_FRAME]; /* buffers */ ++ u8 allocated; /* Flag if buffers are allocated */ ++ u8 need_contiguous; /* Flag if contiguous buffers are needed */ ++ /* only applies to jpg buffers, raw buffers are always contiguous */ ++}; ++ ++struct zoran; ++ ++/* zoran_fh contains per-open() settings */ ++struct zoran_fh { ++ struct zoran *zr; ++ ++ enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ ++ ++ struct zoran_overlay_settings overlay_settings; ++ u32 *overlay_mask; /* overlay mask */ ++ enum zoran_lock_activity overlay_active;/* feature currently in use? */ ++ ++ struct zoran_buffer_col buffers; /* buffers' info */ ++ ++ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ ++ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ ++}; ++ ++struct card_info { ++ enum card_type type; ++ char name[32]; ++ const char *i2c_decoder; /* i2c decoder device */ ++ const unsigned short *addrs_decoder; ++ const char *i2c_encoder; /* i2c encoder device */ ++ const unsigned short *addrs_encoder; ++ u16 video_vfe, video_codec; /* videocodec types */ ++ u16 audio_chip; /* audio type */ ++ ++ int inputs; /* number of video inputs */ ++ struct input { ++ int muxsel; ++ char name[32]; ++ } input[BUZ_MAX_INPUT]; ++ ++ v4l2_std_id norms; ++ struct tvnorm *tvn[3]; /* supported TV norms */ ++ ++ u32 jpeg_int; /* JPEG interrupt */ ++ u32 vsync_int; /* VSYNC interrupt */ ++ s8 gpio[ZR_GPIO_MAX]; ++ u8 gpcs[GPCS_MAX]; ++ ++ struct vfe_polarity vfe_pol; ++ u8 gpio_pol[ZR_GPIO_MAX]; ++ ++ /* is the /GWS line connected? */ ++ u8 gws_not_connected; ++ ++ /* avs6eyes mux setting */ ++ u8 input_mux; ++ ++ void (*init) (struct zoran * zr); ++}; ++ ++struct zoran { ++ struct v4l2_device v4l2_dev; ++ struct video_device *video_dev; ++ ++ struct i2c_adapter i2c_adapter; /* */ ++ struct i2c_algo_bit_data i2c_algo; /* */ ++ u32 i2cbr; ++ ++ struct v4l2_subdev *decoder; /* video decoder sub-device */ ++ struct v4l2_subdev *encoder; /* video encoder sub-device */ ++ ++ struct videocodec *codec; /* video codec */ ++ struct videocodec *vfe; /* video front end */ ++ ++ struct mutex resource_lock; /* prevent evil stuff */ ++ struct mutex other_lock; /* please merge with above */ ++ ++ u8 initialized; /* flag if zoran has been correctly initialized */ ++ int user; /* number of current users */ ++ struct card_info card; ++ struct tvnorm *timing; ++ ++ unsigned short id; /* number of this device */ ++ char name[32]; /* name of this device */ ++ struct pci_dev *pci_dev; /* PCI device */ ++ unsigned char revision; /* revision of zr36057 */ ++ unsigned char __iomem *zr36057_mem;/* pointer to mapped IO memory */ ++ ++ spinlock_t spinlock; /* Spinlock */ ++ ++ /* Video for Linux parameters */ ++ int input; /* card's norm and input */ ++ v4l2_std_id norm; ++ ++ /* Current buffer params */ ++ void *vbuf_base; ++ int vbuf_height, vbuf_width; ++ int vbuf_depth; ++ int vbuf_bytesperline; ++ ++ struct zoran_overlay_settings overlay_settings; ++ u32 *overlay_mask; /* overlay mask */ ++ enum zoran_lock_activity overlay_active; /* feature currently in use? */ ++ ++ wait_queue_head_t v4l_capq; ++ ++ int v4l_overlay_active; /* Overlay grab is activated */ ++ int v4l_memgrab_active; /* Memory grab is activated */ ++ ++ int v4l_grab_frame; /* Frame number being currently grabbed */ ++#define NO_GRAB_ACTIVE (-1) ++ unsigned long v4l_grab_seq; /* Number of frames grabbed */ ++ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ ++ ++ /* V4L grab queue of frames pending */ ++ unsigned long v4l_pend_head; ++ unsigned long v4l_pend_tail; ++ unsigned long v4l_sync_tail; ++ int v4l_pend[V4L_MAX_FRAME]; ++ struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */ ++ ++ /* Buz MJPEG parameters */ ++ enum zoran_codec_mode codec_mode; /* status of codec */ ++ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ ++ ++ wait_queue_head_t jpg_capq; /* wait here for grab to finish */ ++ ++ /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */ ++ /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */ ++ /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */ ++ unsigned long jpg_que_head; /* Index where to put next buffer which is queued */ ++ unsigned long jpg_dma_head; /* Index of next buffer which goes into stat_com */ ++ unsigned long jpg_dma_tail; /* Index of last buffer in stat_com */ ++ unsigned long jpg_que_tail; /* Index of last buffer in queue */ ++ unsigned long jpg_seq_num; /* count of frames since grab/play started */ ++ unsigned long jpg_err_seq; /* last seq_num before error */ ++ unsigned long jpg_err_shift; ++ unsigned long jpg_queued_num; /* count of frames queued since grab/play started */ ++ ++ /* zr36057's code buffer table */ ++ __le32 *stat_com; /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */ ++ ++ /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */ ++ int jpg_pend[BUZ_MAX_FRAME]; ++ ++ /* array indexed by frame number */ ++ struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */ ++ ++ /* Additional stuff for testing */ ++#ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *zoran_proc; ++#else ++ void *zoran_proc; ++#endif ++ int testing; ++ int jpeg_error; ++ int intr_counter_GIRQ1; ++ int intr_counter_GIRQ0; ++ int intr_counter_CodRepIRQ; ++ int intr_counter_JPEGRepIRQ; ++ int field_counter; ++ int IRQ1_in; ++ int IRQ1_out; ++ int JPEG_in; ++ int JPEG_out; ++ int JPEG_0; ++ int JPEG_1; ++ int END_event_missed; ++ int JPEG_missed; ++ int JPEG_error; ++ int num_errors; ++ int JPEG_max_missed; ++ int JPEG_min_missed; ++ ++ u32 last_isr; ++ unsigned long frame_num; ++ ++ wait_queue_head_t test_q; ++}; ++ ++static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) ++{ ++ return container_of(v4l2_dev, struct zoran, v4l2_dev); ++} ++ ++/* There was something called _ALPHA_BUZ that used the PCI address instead of ++ * the kernel iomapped address for btread/btwrite. */ ++#define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr)) ++#define btread(adr) readl(zr->zr36057_mem+(adr)) ++ ++#define btand(dat,adr) btwrite((dat) & btread(adr), adr) ++#define btor(dat,adr) btwrite((dat) | btread(adr), adr) ++#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) ++ ++#endif +diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c +new file mode 100644 +index 0000000..cea325d +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_card.c +@@ -0,0 +1,1527 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles card-specific data and detection ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "videocodec.h" ++#include "zoran.h" ++#include "zoran_card.h" ++#include "zoran_device.h" ++#include "zoran_procfs.h" ++ ++extern const struct zoran_format zoran_formats[]; ++ ++static int card[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; ++module_param_array(card, int, NULL, 0444); ++MODULE_PARM_DESC(card, "Card type"); ++ ++/* ++ The video mem address of the video card. ++ The driver has a little database for some videocards ++ to determine it from there. If your video card is not in there ++ you have either to give it to the driver as a parameter ++ or set in in a VIDIOCSFBUF ioctl ++ */ ++ ++static unsigned long vidmem; /* default = 0 - Video memory base address */ ++module_param(vidmem, ulong, 0444); ++MODULE_PARM_DESC(vidmem, "Default video memory base address"); ++ ++/* ++ Default input and video norm at startup of the driver. ++*/ ++ ++static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ ++module_param(default_input, uint, 0444); ++MODULE_PARM_DESC(default_input, ++ "Default input (0=Composite, 1=S-Video, 2=Internal)"); ++ ++static int default_mux = 1; /* 6 Eyes input selection */ ++module_param(default_mux, int, 0644); ++MODULE_PARM_DESC(default_mux, ++ "Default 6 Eyes mux setting (Input selection)"); ++ ++static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ ++module_param(default_norm, int, 0444); ++MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); ++ ++/* /dev/videoN, -1 for autodetect */ ++static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; ++module_param_array(video_nr, int, NULL, 0444); ++MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); ++ ++int v4l_nbufs = 4; ++int v4l_bufsize = 864; /* Everybody should be able to work with this setting */ ++module_param(v4l_nbufs, int, 0644); ++MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); ++module_param(v4l_bufsize, int, 0644); ++MODULE_PARM_DESC(v4l_bufsize, "Maximum size per V4L buffer (in kB)"); ++ ++int jpg_nbufs = 32; ++int jpg_bufsize = 512; /* max size for 100% quality full-PAL frame */ ++module_param(jpg_nbufs, int, 0644); ++MODULE_PARM_DESC(jpg_nbufs, "Maximum number of JPG buffers to use"); ++module_param(jpg_bufsize, int, 0644); ++MODULE_PARM_DESC(jpg_bufsize, "Maximum size per JPG buffer (in kB)"); ++ ++int pass_through = 0; /* 1=Pass through TV signal when device is not used */ ++ /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */ ++module_param(pass_through, int, 0644); ++MODULE_PARM_DESC(pass_through, ++ "Pass TV signal through to TV-out when idling"); ++ ++int zr36067_debug = 1; ++module_param_named(debug, zr36067_debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level (0-5)"); ++ ++#define ZORAN_VERSION "0.10.1" ++ ++MODULE_DESCRIPTION("Zoran-36057/36067 JPEG codec driver"); ++MODULE_AUTHOR("Serguei Miridonov"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(ZORAN_VERSION); ++ ++#define ZR_DEVICE(subven, subdev, data) { \ ++ .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ ++ .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } ++ ++static struct pci_device_id zr36067_pci_tbl[] = { ++ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10plus), ++ ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC30PLUS, DC30plus), ++ ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), ++ ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), ++ ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), ++ {0} ++}; ++MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); ++ ++static unsigned int zoran_num; /* number of cards found */ ++ ++/* videocodec bus functions ZR36060 */ ++static u32 ++zr36060_read (struct videocodec *codec, ++ u16 reg) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ __u32 data; ++ ++ if (post_office_wait(zr) ++ || post_office_write(zr, 0, 1, reg >> 8) ++ || post_office_write(zr, 0, 2, reg & 0xff)) { ++ return -1; ++ } ++ ++ data = post_office_read(zr, 0, 3) & 0xff; ++ return data; ++} ++ ++static void ++zr36060_write (struct videocodec *codec, ++ u16 reg, ++ u32 val) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ ++ if (post_office_wait(zr) ++ || post_office_write(zr, 0, 1, reg >> 8) ++ || post_office_write(zr, 0, 2, reg & 0xff)) { ++ return; ++ } ++ ++ post_office_write(zr, 0, 3, val & 0xff); ++} ++ ++/* videocodec bus functions ZR36050 */ ++static u32 ++zr36050_read (struct videocodec *codec, ++ u16 reg) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ __u32 data; ++ ++ if (post_office_wait(zr) ++ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES ++ return -1; ++ } ++ ++ data = post_office_read(zr, 0, reg & 0x03) & 0xff; // reg. LOWBYTES + read ++ return data; ++} ++ ++static void ++zr36050_write (struct videocodec *codec, ++ u16 reg, ++ u32 val) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ ++ if (post_office_wait(zr) ++ || post_office_write(zr, 1, 0, reg >> 2)) { // reg. HIGHBYTES ++ return; ++ } ++ ++ post_office_write(zr, 0, reg & 0x03, val & 0xff); // reg. LOWBYTES + wr. data ++} ++ ++/* videocodec bus functions ZR36016 */ ++static u32 ++zr36016_read (struct videocodec *codec, ++ u16 reg) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ __u32 data; ++ ++ if (post_office_wait(zr)) { ++ return -1; ++ } ++ ++ data = post_office_read(zr, 2, reg & 0x03) & 0xff; // read ++ return data; ++} ++ ++/* hack for in zoran_device.c */ ++void ++zr36016_write (struct videocodec *codec, ++ u16 reg, ++ u32 val) ++{ ++ struct zoran *zr = (struct zoran *) codec->master_data->data; ++ ++ if (post_office_wait(zr)) { ++ return; ++ } ++ ++ post_office_write(zr, 2, reg & 0x03, val & 0x0ff); // wr. data ++} ++ ++/* ++ * Board specific information ++ */ ++ ++static void ++dc10_init (struct zoran *zr) ++{ ++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); ++ ++ /* Pixel clock selection */ ++ GPIO(zr, 4, 0); ++ GPIO(zr, 5, 1); ++ /* Enable the video bus sync signals */ ++ GPIO(zr, 7, 0); ++} ++ ++static void ++dc10plus_init (struct zoran *zr) ++{ ++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); ++} ++ ++static void ++buz_init (struct zoran *zr) ++{ ++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); ++ ++ /* some stuff from Iomega */ ++ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); ++ pci_write_config_dword(zr->pci_dev, 0x0c, 0x00012020); ++ pci_write_config_dword(zr->pci_dev, 0xe8, 0xc0200000); ++} ++ ++static void ++lml33_init (struct zoran *zr) ++{ ++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); ++ ++ GPIO(zr, 2, 1); // Set Composite input/output ++} ++ ++static void ++avs6eyes_init (struct zoran *zr) ++{ ++ // AverMedia 6-Eyes original driver by Christer Weinigel ++ ++ // Lifted straight from Christer's old driver and ++ // modified slightly by Martin Samuelsson. ++ ++ int mux = default_mux; /* 1 = BT866, 7 = VID1 */ ++ ++ GPIO(zr, 4, 1); /* Bt866 SLEEP on */ ++ udelay(2); ++ ++ GPIO(zr, 0, 1); /* ZR36060 /RESET on */ ++ GPIO(zr, 1, 0); /* ZR36060 /SLEEP on */ ++ GPIO(zr, 2, mux & 1); /* MUX S0 */ ++ GPIO(zr, 3, 0); /* /FRAME on */ ++ GPIO(zr, 4, 0); /* Bt866 SLEEP off */ ++ GPIO(zr, 5, mux & 2); /* MUX S1 */ ++ GPIO(zr, 6, 0); /* ? */ ++ GPIO(zr, 7, mux & 4); /* MUX S2 */ ++ ++} ++ ++static char * ++codecid_to_modulename (u16 codecid) ++{ ++ char *name = NULL; ++ ++ switch (codecid) { ++ case CODEC_TYPE_ZR36060: ++ name = "zr36060"; ++ break; ++ case CODEC_TYPE_ZR36050: ++ name = "zr36050"; ++ break; ++ case CODEC_TYPE_ZR36016: ++ name = "zr36016"; ++ break; ++ } ++ ++ return name; ++} ++ ++// struct tvnorm { ++// u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart; ++// }; ++ ++static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 }; ++static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 }; ++static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 }; ++static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 }; ++ ++static struct tvnorm f50ccir601_lml33 = { 864, 720, 75+34, 804, 625, 576, 18 }; ++static struct tvnorm f60ccir601_lml33 = { 858, 720, 57+34, 788, 525, 480, 16 }; ++ ++/* The DC10 (57/16/50) uses VActive as HSync, so HStart must be 0 */ ++static struct tvnorm f50sqpixel_dc10 = { 944, 768, 0, 880, 625, 576, 0 }; ++static struct tvnorm f60sqpixel_dc10 = { 780, 640, 0, 716, 525, 480, 12 }; ++ ++/* FIXME: I cannot swap U and V in saa7114, so i do one ++ * pixel left shift in zoran (75 -> 74) ++ * (Maxim Yevtyushkin ) */ ++static struct tvnorm f50ccir601_lm33r10 = { 864, 720, 74+54, 804, 625, 576, 18 }; ++static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 }; ++ ++/* FIXME: The ks0127 seem incapable of swapping U and V, too, which is why I ++ * copy Maxim's left shift hack for the 6 Eyes. ++ * ++ * Christer's driver used the unshifted norms, though... ++ * /Sam */ ++static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; ++static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; ++ ++static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; ++static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; ++static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; ++static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; ++static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; ++static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; ++static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; ++static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; ++static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; ++static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; ++ ++static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { ++ { ++ .type = DC10_old, ++ .name = "DC10(old)", ++ .i2c_decoder = "vpx3220a", ++ .addrs_decoder = vpx3220_addrs, ++ .video_codec = CODEC_TYPE_ZR36050, ++ .video_vfe = CODEC_TYPE_ZR36016, ++ ++ .inputs = 3, ++ .input = { ++ { 1, "Composite" }, ++ { 2, "S-Video" }, ++ { 0, "Internal/comp" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50sqpixel_dc10, ++ &f60sqpixel_dc10, ++ &f50sqpixel_dc10 ++ }, ++ .jpeg_int = 0, ++ .vsync_int = ZR36057_ISR_GIRQ1, ++ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, ++ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, ++ .gpcs = { -1, 0 }, ++ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gws_not_connected = 0, ++ .input_mux = 0, ++ .init = &dc10_init, ++ }, { ++ .type = DC10_new, ++ .name = "DC10(new)", ++ .i2c_decoder = "saa7110", ++ .addrs_decoder = saa7110_addrs, ++ .i2c_encoder = "adv7175", ++ .addrs_encoder = adv717x_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 3, ++ .input = { ++ { 0, "Composite" }, ++ { 7, "S-Video" }, ++ { 5, "Internal/comp" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50sqpixel, ++ &f60sqpixel, ++ &f50sqpixel}, ++ .jpeg_int = ZR36057_ISR_GIRQ0, ++ .vsync_int = ZR36057_ISR_GIRQ1, ++ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, ++ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gpcs = { -1, 1}, ++ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, ++ .gws_not_connected = 0, ++ .input_mux = 0, ++ .init = &dc10plus_init, ++ }, { ++ .type = DC10plus, ++ .name = "DC10plus", ++ .i2c_decoder = "saa7110", ++ .addrs_decoder = saa7110_addrs, ++ .i2c_encoder = "adv7175", ++ .addrs_encoder = adv717x_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 3, ++ .input = { ++ { 0, "Composite" }, ++ { 7, "S-Video" }, ++ { 5, "Internal/comp" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50sqpixel, ++ &f60sqpixel, ++ &f50sqpixel ++ }, ++ .jpeg_int = ZR36057_ISR_GIRQ0, ++ .vsync_int = ZR36057_ISR_GIRQ1, ++ .gpio = { 3, 0, 6, 1, 2, -1, 4, 5 }, ++ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gpcs = { -1, 1 }, ++ .vfe_pol = { 1, 1, 1, 1, 0, 0, 0, 0 }, ++ .gws_not_connected = 0, ++ .input_mux = 0, ++ .init = &dc10plus_init, ++ }, { ++ .type = DC30, ++ .name = "DC30", ++ .i2c_decoder = "vpx3220a", ++ .addrs_decoder = vpx3220_addrs, ++ .i2c_encoder = "adv7175", ++ .addrs_encoder = adv717x_addrs, ++ .video_codec = CODEC_TYPE_ZR36050, ++ .video_vfe = CODEC_TYPE_ZR36016, ++ ++ .inputs = 3, ++ .input = { ++ { 1, "Composite" }, ++ { 2, "S-Video" }, ++ { 0, "Internal/comp" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50sqpixel_dc10, ++ &f60sqpixel_dc10, ++ &f50sqpixel_dc10 ++ }, ++ .jpeg_int = 0, ++ .vsync_int = ZR36057_ISR_GIRQ1, ++ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, ++ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, ++ .gpcs = { -1, 0 }, ++ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gws_not_connected = 0, ++ .input_mux = 0, ++ .init = &dc10_init, ++ }, { ++ .type = DC30plus, ++ .name = "DC30plus", ++ .i2c_decoder = "vpx3220a", ++ .addrs_decoder = vpx3220_addrs, ++ .i2c_encoder = "adv7175", ++ .addrs_encoder = adv717x_addrs, ++ .video_codec = CODEC_TYPE_ZR36050, ++ .video_vfe = CODEC_TYPE_ZR36016, ++ ++ .inputs = 3, ++ .input = { ++ { 1, "Composite" }, ++ { 2, "S-Video" }, ++ { 0, "Internal/comp" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50sqpixel_dc10, ++ &f60sqpixel_dc10, ++ &f50sqpixel_dc10 ++ }, ++ .jpeg_int = 0, ++ .vsync_int = ZR36057_ISR_GIRQ1, ++ .gpio = { 2, 1, -1, 3, 7, 0, 4, 5 }, ++ .gpio_pol = { 0, 0, 0, 1, 0, 0, 0, 0 }, ++ .gpcs = { -1, 0 }, ++ .vfe_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gws_not_connected = 0, ++ .input_mux = 0, ++ .init = &dc10_init, ++ }, { ++ .type = LML33, ++ .name = "LML33", ++ .i2c_decoder = "bt819a", ++ .addrs_decoder = bt819_addrs, ++ .i2c_encoder = "bt856", ++ .addrs_encoder = bt856_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 2, ++ .input = { ++ { 0, "Composite" }, ++ { 7, "S-Video" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL, ++ .tvn = { ++ &f50ccir601_lml33, ++ &f60ccir601_lml33, ++ NULL ++ }, ++ .jpeg_int = ZR36057_ISR_GIRQ1, ++ .vsync_int = ZR36057_ISR_GIRQ0, ++ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, ++ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, ++ .gpcs = { 3, 1 }, ++ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, ++ .gws_not_connected = 1, ++ .input_mux = 0, ++ .init = &lml33_init, ++ }, { ++ .type = LML33R10, ++ .name = "LML33R10", ++ .i2c_decoder = "saa7114", ++ .addrs_decoder = saa7114_addrs, ++ .i2c_encoder = "adv7170", ++ .addrs_encoder = adv717x_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 2, ++ .input = { ++ { 0, "Composite" }, ++ { 7, "S-Video" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL, ++ .tvn = { ++ &f50ccir601_lm33r10, ++ &f60ccir601_lm33r10, ++ NULL ++ }, ++ .jpeg_int = ZR36057_ISR_GIRQ1, ++ .vsync_int = ZR36057_ISR_GIRQ0, ++ .gpio = { 1, -1, 3, 5, 7, -1, -1, -1 }, ++ .gpio_pol = { 0, 0, 0, 0, 1, 0, 0, 0 }, ++ .gpcs = { 3, 1 }, ++ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, ++ .gws_not_connected = 1, ++ .input_mux = 0, ++ .init = &lml33_init, ++ }, { ++ .type = BUZ, ++ .name = "Buz", ++ .i2c_decoder = "saa7111", ++ .addrs_decoder = saa7111_addrs, ++ .i2c_encoder = "saa7185", ++ .addrs_encoder = saa7185_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 2, ++ .input = { ++ { 3, "Composite" }, ++ { 7, "S-Video" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, ++ .tvn = { ++ &f50ccir601, ++ &f60ccir601, ++ &f50ccir601 ++ }, ++ .jpeg_int = ZR36057_ISR_GIRQ1, ++ .vsync_int = ZR36057_ISR_GIRQ0, ++ .gpio = { 1, -1, 3, -1, -1, -1, -1, -1 }, ++ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, ++ .gpcs = { 3, 1 }, ++ .vfe_pol = { 1, 1, 0, 0, 0, 1, 0, 0 }, ++ .gws_not_connected = 1, ++ .input_mux = 0, ++ .init = &buz_init, ++ }, { ++ .type = AVS6EYES, ++ .name = "6-Eyes", ++ /* AverMedia chose not to brand the 6-Eyes. Thus it ++ can't be autodetected, and requires card=x. */ ++ .i2c_decoder = "ks0127", ++ .addrs_decoder = ks0127_addrs, ++ .i2c_encoder = "bt866", ++ .addrs_encoder = bt866_addrs, ++ .video_codec = CODEC_TYPE_ZR36060, ++ ++ .inputs = 10, ++ .input = { ++ { 0, "Composite 1" }, ++ { 1, "Composite 2" }, ++ { 2, "Composite 3" }, ++ { 4, "Composite 4" }, ++ { 5, "Composite 5" }, ++ { 6, "Composite 6" }, ++ { 8, "S-Video 1" }, ++ { 9, "S-Video 2" }, ++ {10, "S-Video 3" }, ++ {15, "YCbCr" } ++ }, ++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL, ++ .tvn = { ++ &f50ccir601_avs6eyes, ++ &f60ccir601_avs6eyes, ++ NULL ++ }, ++ .jpeg_int = ZR36057_ISR_GIRQ1, ++ .vsync_int = ZR36057_ISR_GIRQ0, ++ .gpio = { 1, 0, 3, -1, -1, -1, -1, -1 },// Validity unknown /Sam ++ .gpio_pol = { 0, 0, 0, 0, 0, 0, 0, 0 }, // Validity unknown /Sam ++ .gpcs = { 3, 1 }, // Validity unknown /Sam ++ .vfe_pol = { 1, 0, 0, 0, 0, 1, 0, 0 }, // Validity unknown /Sam ++ .gws_not_connected = 1, ++ .input_mux = 1, ++ .init = &avs6eyes_init, ++ } ++ ++}; ++ ++/* ++ * I2C functions ++ */ ++/* software I2C functions */ ++static int ++zoran_i2c_getsda (void *data) ++{ ++ struct zoran *zr = (struct zoran *) data; ++ ++ return (btread(ZR36057_I2CBR) >> 1) & 1; ++} ++ ++static int ++zoran_i2c_getscl (void *data) ++{ ++ struct zoran *zr = (struct zoran *) data; ++ ++ return btread(ZR36057_I2CBR) & 1; ++} ++ ++static void ++zoran_i2c_setsda (void *data, ++ int state) ++{ ++ struct zoran *zr = (struct zoran *) data; ++ ++ if (state) ++ zr->i2cbr |= 2; ++ else ++ zr->i2cbr &= ~2; ++ btwrite(zr->i2cbr, ZR36057_I2CBR); ++} ++ ++static void ++zoran_i2c_setscl (void *data, ++ int state) ++{ ++ struct zoran *zr = (struct zoran *) data; ++ ++ if (state) ++ zr->i2cbr |= 1; ++ else ++ zr->i2cbr &= ~1; ++ btwrite(zr->i2cbr, ZR36057_I2CBR); ++} ++ ++static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { ++ .setsda = zoran_i2c_setsda, ++ .setscl = zoran_i2c_setscl, ++ .getsda = zoran_i2c_getsda, ++ .getscl = zoran_i2c_getscl, ++ .udelay = 10, ++ .timeout = 100, ++}; ++ ++static int ++zoran_register_i2c (struct zoran *zr) ++{ ++ zr->i2c_algo = zoran_i2c_bit_data_template; ++ zr->i2c_algo.data = zr; ++ strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), ++ sizeof(zr->i2c_adapter.name)); ++ i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); ++ zr->i2c_adapter.algo_data = &zr->i2c_algo; ++ zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; ++ return i2c_bit_add_bus(&zr->i2c_adapter); ++} ++ ++static void ++zoran_unregister_i2c (struct zoran *zr) ++{ ++ i2c_del_adapter(&zr->i2c_adapter); ++} ++ ++/* Check a zoran_params struct for correctness, insert default params */ ++ ++int ++zoran_check_jpg_settings (struct zoran *zr, ++ struct zoran_jpg_settings *settings, ++ int try) ++{ ++ int err = 0, err0 = 0; ++ ++ dprintk(4, ++ KERN_DEBUG ++ "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", ++ ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm, ++ settings->VerDcm, settings->TmpDcm); ++ dprintk(4, ++ KERN_DEBUG ++ "%s: %s - x: %d, y: %d, w: %d, y: %d\n", ++ ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y, ++ settings->img_width, settings->img_height); ++ /* Check decimation, set default values for decimation = 1, 2, 4 */ ++ switch (settings->decimation) { ++ case 1: ++ ++ settings->HorDcm = 1; ++ settings->VerDcm = 1; ++ settings->TmpDcm = 1; ++ settings->field_per_buff = 2; ++ settings->img_x = 0; ++ settings->img_y = 0; ++ settings->img_width = BUZ_MAX_WIDTH; ++ settings->img_height = BUZ_MAX_HEIGHT / 2; ++ break; ++ case 2: ++ ++ settings->HorDcm = 2; ++ settings->VerDcm = 1; ++ settings->TmpDcm = 2; ++ settings->field_per_buff = 1; ++ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; ++ settings->img_y = 0; ++ settings->img_width = ++ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; ++ settings->img_height = BUZ_MAX_HEIGHT / 2; ++ break; ++ case 4: ++ ++ if (zr->card.type == DC10_new) { ++ dprintk(1, ++ KERN_DEBUG ++ "%s: %s - HDec by 4 is not supported on the DC10\n", ++ ZR_DEVNAME(zr), __func__); ++ err0++; ++ break; ++ } ++ ++ settings->HorDcm = 4; ++ settings->VerDcm = 2; ++ settings->TmpDcm = 2; ++ settings->field_per_buff = 1; ++ settings->img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; ++ settings->img_y = 0; ++ settings->img_width = ++ (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; ++ settings->img_height = BUZ_MAX_HEIGHT / 2; ++ break; ++ case 0: ++ ++ /* We have to check the data the user has set */ ++ ++ if (settings->HorDcm != 1 && settings->HorDcm != 2 && ++ (zr->card.type == DC10_new || settings->HorDcm != 4)) { ++ settings->HorDcm = clamp(settings->HorDcm, 1, 2); ++ err0++; ++ } ++ if (settings->VerDcm != 1 && settings->VerDcm != 2) { ++ settings->VerDcm = clamp(settings->VerDcm, 1, 2); ++ err0++; ++ } ++ if (settings->TmpDcm != 1 && settings->TmpDcm != 2) { ++ settings->TmpDcm = clamp(settings->TmpDcm, 1, 2); ++ err0++; ++ } ++ if (settings->field_per_buff != 1 && ++ settings->field_per_buff != 2) { ++ settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); ++ err0++; ++ } ++ if (settings->img_x < 0) { ++ settings->img_x = 0; ++ err0++; ++ } ++ if (settings->img_y < 0) { ++ settings->img_y = 0; ++ err0++; ++ } ++ if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { ++ settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); ++ err0++; ++ } ++ if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { ++ settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); ++ err0++; ++ } ++ if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { ++ settings->img_x = BUZ_MAX_WIDTH - settings->img_width; ++ err0++; ++ } ++ if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { ++ settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; ++ err0++; ++ } ++ if (settings->img_width % (16 * settings->HorDcm) != 0) { ++ settings->img_width -= settings->img_width % (16 * settings->HorDcm); ++ if (settings->img_width == 0) ++ settings->img_width = 16 * settings->HorDcm; ++ err0++; ++ } ++ if (settings->img_height % (8 * settings->VerDcm) != 0) { ++ settings->img_height -= settings->img_height % (8 * settings->VerDcm); ++ if (settings->img_height == 0) ++ settings->img_height = 8 * settings->VerDcm; ++ err0++; ++ } ++ ++ if (!try && err0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - error in params for decimation = 0\n", ++ ZR_DEVNAME(zr), __func__); ++ err++; ++ } ++ break; ++ default: ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n", ++ ZR_DEVNAME(zr), __func__, settings->decimation); ++ err++; ++ break; ++ } ++ ++ if (settings->jpg_comp.quality > 100) ++ settings->jpg_comp.quality = 100; ++ if (settings->jpg_comp.quality < 5) ++ settings->jpg_comp.quality = 5; ++ if (settings->jpg_comp.APPn < 0) ++ settings->jpg_comp.APPn = 0; ++ if (settings->jpg_comp.APPn > 15) ++ settings->jpg_comp.APPn = 15; ++ if (settings->jpg_comp.APP_len < 0) ++ settings->jpg_comp.APP_len = 0; ++ if (settings->jpg_comp.APP_len > 60) ++ settings->jpg_comp.APP_len = 60; ++ if (settings->jpg_comp.COM_len < 0) ++ settings->jpg_comp.COM_len = 0; ++ if (settings->jpg_comp.COM_len > 60) ++ settings->jpg_comp.COM_len = 60; ++ if (err) ++ return -EINVAL; ++ return 0; ++} ++ ++void ++zoran_open_init_params (struct zoran *zr) ++{ ++ int i; ++ ++ /* User must explicitly set a window */ ++ zr->overlay_settings.is_set = 0; ++ zr->overlay_mask = NULL; ++ zr->overlay_active = ZORAN_FREE; ++ ++ zr->v4l_memgrab_active = 0; ++ zr->v4l_overlay_active = 0; ++ zr->v4l_grab_frame = NO_GRAB_ACTIVE; ++ zr->v4l_grab_seq = 0; ++ zr->v4l_settings.width = 192; ++ zr->v4l_settings.height = 144; ++ zr->v4l_settings.format = &zoran_formats[7]; /* YUY2 - YUV-4:2:2 packed */ ++ zr->v4l_settings.bytesperline = ++ zr->v4l_settings.width * ++ ((zr->v4l_settings.format->depth + 7) / 8); ++ ++ /* DMA ring stuff for V4L */ ++ zr->v4l_pend_tail = 0; ++ zr->v4l_pend_head = 0; ++ zr->v4l_sync_tail = 0; ++ zr->v4l_buffers.active = ZORAN_FREE; ++ for (i = 0; i < VIDEO_MAX_FRAME; i++) { ++ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ ++ } ++ zr->v4l_buffers.allocated = 0; ++ ++ for (i = 0; i < BUZ_MAX_FRAME; i++) { ++ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ ++ } ++ zr->jpg_buffers.active = ZORAN_FREE; ++ zr->jpg_buffers.allocated = 0; ++ /* Set necessary params and call zoran_check_jpg_settings to set the defaults */ ++ zr->jpg_settings.decimation = 1; ++ zr->jpg_settings.jpg_comp.quality = 50; /* default compression factor 8 */ ++ if (zr->card.type != BUZ) ++ zr->jpg_settings.odd_even = 1; ++ else ++ zr->jpg_settings.odd_even = 0; ++ zr->jpg_settings.jpg_comp.APPn = 0; ++ zr->jpg_settings.jpg_comp.APP_len = 0; /* No APPn marker */ ++ memset(zr->jpg_settings.jpg_comp.APP_data, 0, ++ sizeof(zr->jpg_settings.jpg_comp.APP_data)); ++ zr->jpg_settings.jpg_comp.COM_len = 0; /* No COM marker */ ++ memset(zr->jpg_settings.jpg_comp.COM_data, 0, ++ sizeof(zr->jpg_settings.jpg_comp.COM_data)); ++ zr->jpg_settings.jpg_comp.jpeg_markers = ++ V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; ++ i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); ++ if (i) ++ dprintk(1, KERN_ERR "%s: %s internal error\n", ++ ZR_DEVNAME(zr), __func__); ++ ++ clear_interrupt_counters(zr); ++ zr->testing = 0; ++} ++ ++static void __devinit ++test_interrupts (struct zoran *zr) ++{ ++ DEFINE_WAIT(wait); ++ int timeout, icr; ++ ++ clear_interrupt_counters(zr); ++ ++ zr->testing = 1; ++ icr = btread(ZR36057_ICR); ++ btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR); ++ prepare_to_wait(&zr->test_q, &wait, TASK_INTERRUPTIBLE); ++ timeout = schedule_timeout(HZ); ++ finish_wait(&zr->test_q, &wait); ++ btwrite(0, ZR36057_ICR); ++ btwrite(0x78000000, ZR36057_ISR); ++ zr->testing = 0; ++ dprintk(5, KERN_INFO "%s: Testing interrupts...\n", ZR_DEVNAME(zr)); ++ if (timeout) { ++ dprintk(1, ": time spent: %d\n", 1 * HZ - timeout); ++ } ++ if (zr36067_debug > 1) ++ print_interrupts(zr); ++ btwrite(icr, ZR36057_ICR); ++} ++ ++static int __devinit ++zr36057_init (struct zoran *zr) ++{ ++ int j, err; ++ ++ dprintk(1, ++ KERN_INFO ++ "%s: %s - initializing card[%d], zr=%p\n", ++ ZR_DEVNAME(zr), __func__, zr->id, zr); ++ ++ /* default setup of all parameters which will persist between opens */ ++ zr->user = 0; ++ ++ init_waitqueue_head(&zr->v4l_capq); ++ init_waitqueue_head(&zr->jpg_capq); ++ init_waitqueue_head(&zr->test_q); ++ zr->jpg_buffers.allocated = 0; ++ zr->v4l_buffers.allocated = 0; ++ ++ zr->vbuf_base = (void *) vidmem; ++ zr->vbuf_width = 0; ++ zr->vbuf_height = 0; ++ zr->vbuf_depth = 0; ++ zr->vbuf_bytesperline = 0; ++ ++ /* Avoid nonsense settings from user for default input/norm */ ++ if (default_norm < 0 || default_norm > 2) ++ default_norm = 0; ++ if (default_norm == 0) { ++ zr->norm = V4L2_STD_PAL; ++ zr->timing = zr->card.tvn[0]; ++ } else if (default_norm == 1) { ++ zr->norm = V4L2_STD_NTSC; ++ zr->timing = zr->card.tvn[1]; ++ } else { ++ zr->norm = V4L2_STD_SECAM; ++ zr->timing = zr->card.tvn[2]; ++ } ++ if (zr->timing == NULL) { ++ dprintk(1, ++ KERN_WARNING ++ "%s: %s - default TV standard not supported by hardware. PAL will be used.\n", ++ ZR_DEVNAME(zr), __func__); ++ zr->norm = V4L2_STD_PAL; ++ zr->timing = zr->card.tvn[0]; ++ } ++ ++ if (default_input > zr->card.inputs-1) { ++ dprintk(1, ++ KERN_WARNING ++ "%s: default_input value %d out of range (0-%d)\n", ++ ZR_DEVNAME(zr), default_input, zr->card.inputs-1); ++ default_input = 0; ++ } ++ zr->input = default_input; ++ ++ /* default setup (will be repeated at every open) */ ++ zoran_open_init_params(zr); ++ ++ /* allocate memory *before* doing anything to the hardware ++ * in case allocation fails */ ++ zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL); ++ zr->video_dev = video_device_alloc(); ++ if (!zr->stat_com || !zr->video_dev) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - kmalloc (STAT_COM) failed\n", ++ ZR_DEVNAME(zr), __func__); ++ err = -ENOMEM; ++ goto exit_free; ++ } ++ for (j = 0; j < BUZ_NUM_STAT_COM; j++) { ++ zr->stat_com[j] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ ++ } ++ ++ /* ++ * Now add the template and register the device unit. ++ */ ++ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); ++ zr->video_dev->parent = &zr->pci_dev->dev; ++ strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); ++ /* It's not a mem2mem device, but you can both capture and output from ++ one and the same device. This should really be split up into two ++ device nodes, but that's a job for another day. */ ++ zr->video_dev->vfl_dir = VFL_DIR_M2M; ++ err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]); ++ if (err < 0) ++ goto exit_free; ++ video_set_drvdata(zr->video_dev, zr); ++ ++ zoran_init_hardware(zr); ++ if (zr36067_debug > 2) ++ detect_guest_activity(zr); ++ test_interrupts(zr); ++ if (!pass_through) { ++ decoder_call(zr, video, s_stream, 0); ++ encoder_call(zr, video, s_routing, 2, 0, 0); ++ } ++ ++ zr->zoran_proc = NULL; ++ zr->initialized = 1; ++ return 0; ++ ++exit_free: ++ kfree(zr->stat_com); ++ kfree(zr->video_dev); ++ return err; ++} ++ ++static void __devexit zoran_remove(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct zoran *zr = to_zoran(v4l2_dev); ++ ++ if (!zr->initialized) ++ goto exit_free; ++ ++ /* unregister videocodec bus */ ++ if (zr->codec) { ++ struct videocodec_master *master = zr->codec->master_data; ++ ++ videocodec_detach(zr->codec); ++ kfree(master); ++ } ++ if (zr->vfe) { ++ struct videocodec_master *master = zr->vfe->master_data; ++ ++ videocodec_detach(zr->vfe); ++ kfree(master); ++ } ++ ++ /* unregister i2c bus */ ++ zoran_unregister_i2c(zr); ++ /* disable PCI bus-mastering */ ++ zoran_set_pci_master(zr, 0); ++ /* put chip into reset */ ++ btwrite(0, ZR36057_SPGPPCR); ++ free_irq(zr->pci_dev->irq, zr); ++ /* unmap and free memory */ ++ kfree(zr->stat_com); ++ zoran_proc_cleanup(zr); ++ iounmap(zr->zr36057_mem); ++ pci_disable_device(zr->pci_dev); ++ video_unregister_device(zr->video_dev); ++exit_free: ++ v4l2_device_unregister(&zr->v4l2_dev); ++ kfree(zr); ++} ++ ++void ++zoran_vdev_release (struct video_device *vdev) ++{ ++ kfree(vdev); ++} ++ ++static struct videocodec_master * __devinit ++zoran_setup_videocodec (struct zoran *zr, ++ int type) ++{ ++ struct videocodec_master *m = NULL; ++ ++ m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL); ++ if (!m) { ++ dprintk(1, KERN_ERR "%s: %s - no memory\n", ++ ZR_DEVNAME(zr), __func__); ++ return m; ++ } ++ ++ /* magic and type are unused for master struct. Makes sense only at ++ codec structs. ++ In the past, .type were initialized to the old V4L1 .hardware ++ value, as VID_HARDWARE_ZR36067 ++ */ ++ m->magic = 0L; ++ m->type = 0; ++ ++ m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; ++ strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); ++ m->data = zr; ++ ++ switch (type) ++ { ++ case CODEC_TYPE_ZR36060: ++ m->readreg = zr36060_read; ++ m->writereg = zr36060_write; ++ m->flags |= CODEC_FLAG_JPEG | CODEC_FLAG_VFE; ++ break; ++ case CODEC_TYPE_ZR36050: ++ m->readreg = zr36050_read; ++ m->writereg = zr36050_write; ++ m->flags |= CODEC_FLAG_JPEG; ++ break; ++ case CODEC_TYPE_ZR36016: ++ m->readreg = zr36016_read; ++ m->writereg = zr36016_write; ++ m->flags |= CODEC_FLAG_VFE; ++ break; ++ } ++ ++ return m; ++} ++ ++static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) ++{ ++ struct zoran *zr = to_zoran(sd->v4l2_dev); ++ ++ /* Bt819 needs to reset its FIFO buffer using #FRST pin and ++ LML33 card uses GPIO(7) for that. */ ++ if (cmd == BT819_FIFO_RESET_LOW) ++ GPIO(zr, 7, 0); ++ else if (cmd == BT819_FIFO_RESET_HIGH) ++ GPIO(zr, 7, 1); ++} ++ ++/* ++ * Scan for a Buz card (actually for the PCI controller ZR36057), ++ * request the irq and map the io memory ++ */ ++static int __devinit zoran_probe(struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ unsigned char latency, need_latency; ++ struct zoran *zr; ++ int result; ++ struct videocodec_master *master_vfe = NULL; ++ struct videocodec_master *master_codec = NULL; ++ int card_num; ++ char *codec_name, *vfe_name; ++ unsigned int nr; ++ ++ ++ nr = zoran_num++; ++ if (nr >= BUZ_MAX) { ++ dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n", ++ ZORAN_NAME, BUZ_MAX); ++ return -ENOENT; ++ } ++ ++ zr = kzalloc(sizeof(struct zoran), GFP_KERNEL); ++ if (!zr) { ++ dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n", ++ ZORAN_NAME, __func__); ++ return -ENOMEM; ++ } ++ zr->v4l2_dev.notify = zoran_subdev_notify; ++ if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) ++ goto zr_free_mem; ++ zr->pci_dev = pdev; ++ zr->id = nr; ++ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); ++ spin_lock_init(&zr->spinlock); ++ mutex_init(&zr->resource_lock); ++ mutex_init(&zr->other_lock); ++ if (pci_enable_device(pdev)) ++ goto zr_unreg; ++ zr->revision = zr->pci_dev->revision; ++ ++ dprintk(1, ++ KERN_INFO ++ "%s: Zoran ZR360%c7 (rev %d), irq: %d, memory: 0x%08llx\n", ++ ZR_DEVNAME(zr), zr->revision < 2 ? '5' : '6', zr->revision, ++ zr->pci_dev->irq, (uint64_t)pci_resource_start(zr->pci_dev, 0)); ++ if (zr->revision >= 2) { ++ dprintk(1, ++ KERN_INFO ++ "%s: Subsystem vendor=0x%04x id=0x%04x\n", ++ ZR_DEVNAME(zr), zr->pci_dev->subsystem_vendor, ++ zr->pci_dev->subsystem_device); ++ } ++ ++ /* Use auto-detected card type? */ ++ if (card[nr] == -1) { ++ if (zr->revision < 2) { ++ dprintk(1, ++ KERN_ERR ++ "%s: No card type specified, please use the card=X module parameter\n", ++ ZR_DEVNAME(zr)); ++ dprintk(1, ++ KERN_ERR ++ "%s: It is not possible to auto-detect ZR36057 based cards\n", ++ ZR_DEVNAME(zr)); ++ goto zr_unreg; ++ } ++ ++ card_num = ent->driver_data; ++ if (card_num >= NUM_CARDS) { ++ dprintk(1, ++ KERN_ERR ++ "%s: Unknown card, try specifying card=X module parameter\n", ++ ZR_DEVNAME(zr)); ++ goto zr_unreg; ++ } ++ dprintk(3, ++ KERN_DEBUG ++ "%s: %s() - card %s detected\n", ++ ZR_DEVNAME(zr), __func__, zoran_cards[card_num].name); ++ } else { ++ card_num = card[nr]; ++ if (card_num >= NUM_CARDS || card_num < 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: User specified card type %d out of range (0 .. %d)\n", ++ ZR_DEVNAME(zr), card_num, NUM_CARDS - 1); ++ goto zr_unreg; ++ } ++ } ++ ++ /* even though we make this a non pointer and thus ++ * theoretically allow for making changes to this struct ++ * on a per-individual card basis at runtime, this is ++ * strongly discouraged. This structure is intended to ++ * keep general card information, no settings or anything */ ++ zr->card = zoran_cards[card_num]; ++ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), ++ "%s[%u]", zr->card.name, zr->id); ++ ++ zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0); ++ if (!zr->zr36057_mem) { ++ dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_unreg; ++ } ++ ++ result = request_irq(zr->pci_dev->irq, zoran_irq, ++ IRQF_SHARED | IRQF_DISABLED, ZR_DEVNAME(zr), zr); ++ if (result < 0) { ++ if (result == -EINVAL) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - bad irq number or handler\n", ++ ZR_DEVNAME(zr), __func__); ++ } else if (result == -EBUSY) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - IRQ %d busy, change your PnP config in BIOS\n", ++ ZR_DEVNAME(zr), __func__, zr->pci_dev->irq); ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - can't assign irq, error code %d\n", ++ ZR_DEVNAME(zr), __func__, result); ++ } ++ goto zr_unmap; ++ } ++ ++ /* set PCI latency timer */ ++ pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, ++ &latency); ++ need_latency = zr->revision > 1 ? 32 : 48; ++ if (latency != need_latency) { ++ dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n", ++ ZR_DEVNAME(zr), latency, need_latency); ++ pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, ++ need_latency); ++ } ++ ++ zr36057_restart(zr); ++ /* i2c */ ++ dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n", ++ ZR_DEVNAME(zr)); ++ ++ if (zoran_register_i2c(zr) < 0) { ++ dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_free_irq; ++ } ++ ++ zr->decoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, ++ &zr->i2c_adapter, zr->card.i2c_decoder, ++ 0, zr->card.addrs_decoder); ++ ++ if (zr->card.i2c_encoder) ++ zr->encoder = v4l2_i2c_new_subdev(&zr->v4l2_dev, ++ &zr->i2c_adapter, zr->card.i2c_encoder, ++ 0, zr->card.addrs_encoder); ++ ++ dprintk(2, ++ KERN_INFO "%s: Initializing videocodec bus...\n", ++ ZR_DEVNAME(zr)); ++ ++ if (zr->card.video_codec) { ++ codec_name = codecid_to_modulename(zr->card.video_codec); ++ if (codec_name) { ++ result = request_module(codec_name); ++ if (result) { ++ dprintk(1, ++ KERN_ERR ++ "%s: failed to load modules %s: %d\n", ++ ZR_DEVNAME(zr), codec_name, result); ++ } ++ } ++ } ++ if (zr->card.video_vfe) { ++ vfe_name = codecid_to_modulename(zr->card.video_vfe); ++ if (vfe_name) { ++ result = request_module(vfe_name); ++ if (result < 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: failed to load modules %s: %d\n", ++ ZR_DEVNAME(zr), vfe_name, result); ++ } ++ } ++ } ++ ++ /* reset JPEG codec */ ++ jpeg_codec_sleep(zr, 1); ++ jpeg_codec_reset(zr); ++ /* video bus enabled */ ++ /* display codec revision */ ++ if (zr->card.video_codec != 0) { ++ master_codec = zoran_setup_videocodec(zr, zr->card.video_codec); ++ if (!master_codec) ++ goto zr_unreg_i2c; ++ zr->codec = videocodec_attach(master_codec); ++ if (!zr->codec) { ++ dprintk(1, KERN_ERR "%s: %s - no codec found\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_free_codec; ++ } ++ if (zr->codec->type != zr->card.video_codec) { ++ dprintk(1, KERN_ERR "%s: %s - wrong codec\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_detach_codec; ++ } ++ } ++ if (zr->card.video_vfe != 0) { ++ master_vfe = zoran_setup_videocodec(zr, zr->card.video_vfe); ++ if (!master_vfe) ++ goto zr_detach_codec; ++ zr->vfe = videocodec_attach(master_vfe); ++ if (!zr->vfe) { ++ dprintk(1, KERN_ERR "%s: %s - no VFE found\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_free_vfe; ++ } ++ if (zr->vfe->type != zr->card.video_vfe) { ++ dprintk(1, KERN_ERR "%s: %s = wrong VFE\n", ++ ZR_DEVNAME(zr), __func__); ++ goto zr_detach_vfe; ++ } ++ } ++ ++ /* take care of Natoma chipset and a revision 1 zr36057 */ ++ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { ++ zr->jpg_buffers.need_contiguous = 1; ++ dprintk(1, KERN_INFO ++ "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", ++ ZR_DEVNAME(zr)); ++ } ++ ++ if (zr36057_init(zr) < 0) ++ goto zr_detach_vfe; ++ ++ zoran_proc_init(zr); ++ ++ return 0; ++ ++zr_detach_vfe: ++ videocodec_detach(zr->vfe); ++zr_free_vfe: ++ kfree(master_vfe); ++zr_detach_codec: ++ videocodec_detach(zr->codec); ++zr_free_codec: ++ kfree(master_codec); ++zr_unreg_i2c: ++ zoran_unregister_i2c(zr); ++zr_free_irq: ++ btwrite(0, ZR36057_SPGPPCR); ++ free_irq(zr->pci_dev->irq, zr); ++zr_unmap: ++ iounmap(zr->zr36057_mem); ++zr_unreg: ++ v4l2_device_unregister(&zr->v4l2_dev); ++zr_free_mem: ++ kfree(zr); ++ ++ return -ENODEV; ++} ++ ++static struct pci_driver zoran_driver = { ++ .name = "zr36067", ++ .id_table = zr36067_pci_tbl, ++ .probe = zoran_probe, ++ .remove = __devexit_p(zoran_remove), ++}; ++ ++static int __init zoran_init(void) ++{ ++ int res; ++ ++ printk(KERN_INFO "Zoran MJPEG board driver version %s\n", ++ ZORAN_VERSION); ++ ++ /* check the parameters we have been given, adjust if necessary */ ++ if (v4l_nbufs < 2) ++ v4l_nbufs = 2; ++ if (v4l_nbufs > VIDEO_MAX_FRAME) ++ v4l_nbufs = VIDEO_MAX_FRAME; ++ /* The user specfies the in KB, we want them in byte ++ * (and page aligned) */ ++ v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024); ++ if (v4l_bufsize < 32768) ++ v4l_bufsize = 32768; ++ /* 2 MB is arbitrary but sufficient for the maximum possible images */ ++ if (v4l_bufsize > 2048 * 1024) ++ v4l_bufsize = 2048 * 1024; ++ if (jpg_nbufs < 4) ++ jpg_nbufs = 4; ++ if (jpg_nbufs > BUZ_MAX_FRAME) ++ jpg_nbufs = BUZ_MAX_FRAME; ++ jpg_bufsize = PAGE_ALIGN(jpg_bufsize * 1024); ++ if (jpg_bufsize < 8192) ++ jpg_bufsize = 8192; ++ if (jpg_bufsize > (512 * 1024)) ++ jpg_bufsize = 512 * 1024; ++ /* Use parameter for vidmem or try to find a video card */ ++ if (vidmem) { ++ dprintk(1, ++ KERN_INFO ++ "%s: Using supplied video memory base address @ 0x%lx\n", ++ ZORAN_NAME, vidmem); ++ } ++ ++ /* some mainboards might not do PCI-PCI data transfer well */ ++ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) { ++ dprintk(1, ++ KERN_WARNING ++ "%s: chipset does not support reliable PCI-PCI DMA\n", ++ ZORAN_NAME); ++ } ++ ++ res = pci_register_driver(&zoran_driver); ++ if (res) { ++ dprintk(1, ++ KERN_ERR ++ "%s: Unable to register ZR36057 driver\n", ++ ZORAN_NAME); ++ return res; ++ } ++ ++ return 0; ++} ++ ++static void __exit zoran_exit(void) ++{ ++ pci_unregister_driver(&zoran_driver); ++} ++ ++module_init(zoran_init); ++module_exit(zoran_exit); +diff --git a/drivers/media/pci/zoran/zoran_card.h b/drivers/media/pci/zoran/zoran_card.h +new file mode 100644 +index 0000000..4936fea +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_card.h +@@ -0,0 +1,54 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles card-specific data and detection ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ZORAN_CARD_H__ ++#define __ZORAN_CARD_H__ ++ ++extern int zr36067_debug; ++ ++#define dprintk(num, format, args...) \ ++ do { \ ++ if (zr36067_debug >= num) \ ++ printk(format, ##args); \ ++ } while (0) ++ ++/* Anybody who uses more than four? */ ++#define BUZ_MAX 4 ++ ++extern struct video_device zoran_template; ++ ++extern int zoran_check_jpg_settings(struct zoran *zr, ++ struct zoran_jpg_settings *settings, ++ int try); ++extern void zoran_open_init_params(struct zoran *zr); ++extern void zoran_vdev_release(struct video_device *vdev); ++ ++void zr36016_write(struct videocodec *codec, u16 reg, u32 val); ++ ++#endif /* __ZORAN_CARD_H__ */ +diff --git a/drivers/media/pci/zoran/zoran_device.c b/drivers/media/pci/zoran/zoran_device.c +new file mode 100644 +index 0000000..519164c +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_device.c +@@ -0,0 +1,1640 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles device access (PCI/I2C/codec/...) ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "videocodec.h" ++#include "zoran.h" ++#include "zoran_device.h" ++#include "zoran_card.h" ++ ++#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | \ ++ ZR36057_ISR_GIRQ1 | \ ++ ZR36057_ISR_JPEGRepIRQ ) ++ ++static bool lml33dpath; /* default = 0 ++ * 1 will use digital path in capture ++ * mode instead of analog. It can be ++ * used for picture adjustments using ++ * tool like xawtv while watching image ++ * on TV monitor connected to the output. ++ * However, due to absence of 75 Ohm ++ * load on Bt819 input, there will be ++ * some image imperfections */ ++ ++module_param(lml33dpath, bool, 0644); ++MODULE_PARM_DESC(lml33dpath, ++ "Use digital path capture mode (on LML33 cards)"); ++ ++static void ++zr36057_init_vfe (struct zoran *zr); ++ ++/* ++ * General Purpose I/O and Guest bus access ++ */ ++ ++/* ++ * This is a bit tricky. When a board lacks a GPIO function, the corresponding ++ * GPIO bit number in the card_info structure is set to 0. ++ */ ++ ++void ++GPIO (struct zoran *zr, ++ int bit, ++ unsigned int value) ++{ ++ u32 reg; ++ u32 mask; ++ ++ /* Make sure the bit number is legal ++ * A bit number of -1 (lacking) gives a mask of 0, ++ * making it harmless */ ++ mask = (1 << (24 + bit)) & 0xff000000; ++ reg = btread(ZR36057_GPPGCR1) & ~mask; ++ if (value) { ++ reg |= mask; ++ } ++ btwrite(reg, ZR36057_GPPGCR1); ++ udelay(1); ++} ++ ++/* ++ * Wait til post office is no longer busy ++ */ ++ ++int ++post_office_wait (struct zoran *zr) ++{ ++ u32 por; ++ ++// while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) { ++ while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) { ++ /* wait for something to happen */ ++ } ++ if ((por & ZR36057_POR_POTime) && !zr->card.gws_not_connected) { ++ /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */ ++ dprintk(1, KERN_INFO "%s: pop timeout %08x\n", ZR_DEVNAME(zr), ++ por); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++int ++post_office_write (struct zoran *zr, ++ unsigned int guest, ++ unsigned int reg, ++ unsigned int value) ++{ ++ u32 por; ++ ++ por = ++ ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ++ ((reg & 7) << 16) | (value & 0xFF); ++ btwrite(por, ZR36057_POR); ++ ++ return post_office_wait(zr); ++} ++ ++int ++post_office_read (struct zoran *zr, ++ unsigned int guest, ++ unsigned int reg) ++{ ++ u32 por; ++ ++ por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16); ++ btwrite(por, ZR36057_POR); ++ if (post_office_wait(zr) < 0) { ++ return -1; ++ } ++ ++ return btread(ZR36057_POR) & 0xFF; ++} ++ ++/* ++ * detect guests ++ */ ++ ++static void ++dump_guests (struct zoran *zr) ++{ ++ if (zr36067_debug > 2) { ++ int i, guest[8]; ++ ++ for (i = 1; i < 8; i++) { // Don't read jpeg codec here ++ guest[i] = post_office_read(zr, i, 0); ++ } ++ ++ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); ++ ++ for (i = 1; i < 8; i++) { ++ printk(" 0x%02x", guest[i]); ++ } ++ printk("\n"); ++ } ++} ++ ++static inline unsigned long ++get_time (void) ++{ ++ struct timeval tv; ++ ++ do_gettimeofday(&tv); ++ return (1000000 * tv.tv_sec + tv.tv_usec); ++} ++ ++void ++detect_guest_activity (struct zoran *zr) ++{ ++ int timeout, i, j, res, guest[8], guest0[8], change[8][3]; ++ unsigned long t0, t1; ++ ++ dump_guests(zr); ++ printk(KERN_INFO "%s: Detecting guests activity, please wait...\n", ++ ZR_DEVNAME(zr)); ++ for (i = 1; i < 8; i++) { // Don't read jpeg codec here ++ guest0[i] = guest[i] = post_office_read(zr, i, 0); ++ } ++ ++ timeout = 0; ++ j = 0; ++ t0 = get_time(); ++ while (timeout < 10000) { ++ udelay(10); ++ timeout++; ++ for (i = 1; (i < 8) && (j < 8); i++) { ++ res = post_office_read(zr, i, 0); ++ if (res != guest[i]) { ++ t1 = get_time(); ++ change[j][0] = (t1 - t0); ++ t0 = t1; ++ change[j][1] = i; ++ change[j][2] = res; ++ j++; ++ guest[i] = res; ++ } ++ } ++ if (j >= 8) ++ break; ++ } ++ printk(KERN_INFO "%s: Guests:", ZR_DEVNAME(zr)); ++ ++ for (i = 1; i < 8; i++) { ++ printk(" 0x%02x", guest0[i]); ++ } ++ printk("\n"); ++ if (j == 0) { ++ printk(KERN_INFO "%s: No activity detected.\n", ZR_DEVNAME(zr)); ++ return; ++ } ++ for (i = 0; i < j; i++) { ++ printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", ZR_DEVNAME(zr), ++ change[i][0], change[i][1], change[i][2]); ++ } ++} ++ ++/* ++ * JPEG Codec access ++ */ ++ ++void ++jpeg_codec_sleep (struct zoran *zr, ++ int sleep) ++{ ++ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_SLEEP], !sleep); ++ if (!sleep) { ++ dprintk(3, ++ KERN_DEBUG ++ "%s: jpeg_codec_sleep() - wake GPIO=0x%08x\n", ++ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); ++ udelay(500); ++ } else { ++ dprintk(3, ++ KERN_DEBUG ++ "%s: jpeg_codec_sleep() - sleep GPIO=0x%08x\n", ++ ZR_DEVNAME(zr), btread(ZR36057_GPPGCR1)); ++ udelay(2); ++ } ++} ++ ++int ++jpeg_codec_reset (struct zoran *zr) ++{ ++ /* Take the codec out of sleep */ ++ jpeg_codec_sleep(zr, 0); ++ ++ if (zr->card.gpcs[GPCS_JPEG_RESET] != 0xff) { ++ post_office_write(zr, zr->card.gpcs[GPCS_JPEG_RESET], 0, ++ 0); ++ udelay(2); ++ } else { ++ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 0); ++ udelay(2); ++ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_RESET], 1); ++ udelay(2); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Set the registers for the size we have specified. Don't bother ++ * trying to understand this without the ZR36057 manual in front of ++ * you [AC]. ++ * ++ * PS: The manual is free for download in .pdf format from ++ * www.zoran.com - nicely done those folks. ++ */ ++ ++static void ++zr36057_adjust_vfe (struct zoran *zr, ++ enum zoran_codec_mode mode) ++{ ++ u32 reg; ++ ++ switch (mode) { ++ case BUZ_MODE_MOTION_DECOMPRESS: ++ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); ++ reg = btread(ZR36057_VFEHCR); ++ if ((reg & (1 << 10)) && zr->card.type != LML33R10) { ++ reg += ((1 << 10) | 1); ++ } ++ btwrite(reg, ZR36057_VFEHCR); ++ break; ++ case BUZ_MODE_MOTION_COMPRESS: ++ case BUZ_MODE_IDLE: ++ default: ++ if ((zr->norm & V4L2_STD_NTSC) || ++ (zr->card.type == LML33R10 && ++ (zr->norm & V4L2_STD_PAL))) ++ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); ++ else ++ btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); ++ reg = btread(ZR36057_VFEHCR); ++ if (!(reg & (1 << 10)) && zr->card.type != LML33R10) { ++ reg -= ((1 << 10) | 1); ++ } ++ btwrite(reg, ZR36057_VFEHCR); ++ break; ++ } ++} ++ ++/* ++ * set geometry ++ */ ++ ++static void ++zr36057_set_vfe (struct zoran *zr, ++ int video_width, ++ int video_height, ++ const struct zoran_format *format) ++{ ++ struct tvnorm *tvn; ++ unsigned HStart, HEnd, VStart, VEnd; ++ unsigned DispMode; ++ unsigned VidWinWid, VidWinHt; ++ unsigned hcrop1, hcrop2, vcrop1, vcrop2; ++ unsigned Wa, We, Ha, He; ++ unsigned X, Y, HorDcm, VerDcm; ++ u32 reg; ++ unsigned mask_line_size; ++ ++ tvn = zr->timing; ++ ++ Wa = tvn->Wa; ++ Ha = tvn->Ha; ++ ++ dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n", ++ ZR_DEVNAME(zr), video_width, video_height); ++ ++ if (video_width < BUZ_MIN_WIDTH || ++ video_height < BUZ_MIN_HEIGHT || ++ video_width > Wa || video_height > Ha) { ++ dprintk(1, KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", ++ ZR_DEVNAME(zr), video_width, video_height); ++ return; ++ } ++ ++ /**** zr36057 ****/ ++ ++ /* horizontal */ ++ VidWinWid = video_width; ++ X = DIV_ROUND_UP(VidWinWid * 64, tvn->Wa); ++ We = (VidWinWid * 64) / X; ++ HorDcm = 64 - X; ++ hcrop1 = 2 * ((tvn->Wa - We) / 4); ++ hcrop2 = tvn->Wa - We - hcrop1; ++ HStart = tvn->HStart ? tvn->HStart : 1; ++ /* (Ronald) Original comment: ++ * "| 1 Doesn't have any effect, tested on both a DC10 and a DC10+" ++ * this is false. It inverses chroma values on the LML33R10 (so Cr ++ * suddenly is shown as Cb and reverse, really cool effect if you ++ * want to see blue faces, not useful otherwise). So don't use |1. ++ * However, the DC10 has '0' as HStart, but does need |1, so we ++ * use a dirty check... ++ */ ++ HEnd = HStart + tvn->Wa - 1; ++ HStart += hcrop1; ++ HEnd -= hcrop2; ++ reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart) ++ | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd); ++ if (zr->card.vfe_pol.hsync_pol) ++ reg |= ZR36057_VFEHCR_HSPol; ++ btwrite(reg, ZR36057_VFEHCR); ++ ++ /* Vertical */ ++ DispMode = !(video_height > BUZ_MAX_HEIGHT / 2); ++ VidWinHt = DispMode ? video_height : video_height / 2; ++ Y = DIV_ROUND_UP(VidWinHt * 64 * 2, tvn->Ha); ++ He = (VidWinHt * 64) / Y; ++ VerDcm = 64 - Y; ++ vcrop1 = (tvn->Ha / 2 - He) / 2; ++ vcrop2 = tvn->Ha / 2 - He - vcrop1; ++ VStart = tvn->VStart; ++ VEnd = VStart + tvn->Ha / 2; // - 1; FIXME SnapShot times out with -1 in 768*576 on the DC10 - LP ++ VStart += vcrop1; ++ VEnd -= vcrop2; ++ reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart) ++ | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd); ++ if (zr->card.vfe_pol.vsync_pol) ++ reg |= ZR36057_VFEVCR_VSPol; ++ btwrite(reg, ZR36057_VFEVCR); ++ ++ /* scaler and pixel format */ ++ reg = 0; ++ reg |= (HorDcm << ZR36057_VFESPFR_HorDcm); ++ reg |= (VerDcm << ZR36057_VFESPFR_VerDcm); ++ reg |= (DispMode << ZR36057_VFESPFR_DispMode); ++ /* RJ: I don't know, why the following has to be the opposite ++ * of the corresponding ZR36060 setting, but only this way ++ * we get the correct colors when uncompressing to the screen */ ++ //reg |= ZR36057_VFESPFR_VCLKPol; /**/ ++ /* RJ: Don't know if that is needed for NTSC also */ ++ if (!(zr->norm & V4L2_STD_NTSC)) ++ reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang ++ reg |= ZR36057_VFESPFR_TopField; ++ if (HorDcm >= 48) { ++ reg |= 3 << ZR36057_VFESPFR_HFilter; /* 5 tap filter */ ++ } else if (HorDcm >= 32) { ++ reg |= 2 << ZR36057_VFESPFR_HFilter; /* 4 tap filter */ ++ } else if (HorDcm >= 16) { ++ reg |= 1 << ZR36057_VFESPFR_HFilter; /* 3 tap filter */ ++ } ++ reg |= format->vfespfr; ++ btwrite(reg, ZR36057_VFESPFR); ++ ++ /* display configuration */ ++ reg = (16 << ZR36057_VDCR_MinPix) ++ | (VidWinHt << ZR36057_VDCR_VidWinHt) ++ | (VidWinWid << ZR36057_VDCR_VidWinWid); ++ if (pci_pci_problems & PCIPCI_TRITON) ++ // || zr->revision < 1) // Revision 1 has also Triton support ++ reg &= ~ZR36057_VDCR_Triton; ++ else ++ reg |= ZR36057_VDCR_Triton; ++ btwrite(reg, ZR36057_VDCR); ++ ++ /* (Ronald) don't write this if overlay_mask = NULL */ ++ if (zr->overlay_mask) { ++ /* Write overlay clipping mask data, but don't enable overlay clipping */ ++ /* RJ: since this makes only sense on the screen, we use ++ * zr->overlay_settings.width instead of video_width */ ++ ++ mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; ++ reg = virt_to_bus(zr->overlay_mask); ++ btwrite(reg, ZR36057_MMTR); ++ reg = virt_to_bus(zr->overlay_mask + mask_line_size); ++ btwrite(reg, ZR36057_MMBR); ++ reg = ++ mask_line_size - (zr->overlay_settings.width + ++ 31) / 32; ++ if (DispMode == 0) ++ reg += mask_line_size; ++ reg <<= ZR36057_OCR_MaskStride; ++ btwrite(reg, ZR36057_OCR); ++ } ++ ++ zr36057_adjust_vfe(zr, zr->codec_mode); ++} ++ ++/* ++ * Switch overlay on or off ++ */ ++ ++void ++zr36057_overlay (struct zoran *zr, ++ int on) ++{ ++ u32 reg; ++ ++ if (on) { ++ /* do the necessary settings ... */ ++ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); /* switch it off first */ ++ ++ zr36057_set_vfe(zr, ++ zr->overlay_settings.width, ++ zr->overlay_settings.height, ++ zr->overlay_settings.format); ++ ++ /* Start and length of each line MUST be 4-byte aligned. ++ * This should be already checked before the call to this routine. ++ * All error messages are internal driver checking only! */ ++ ++ /* video display top and bottom registers */ ++ reg = (long) zr->vbuf_base + ++ zr->overlay_settings.x * ++ ((zr->overlay_settings.format->depth + 7) / 8) + ++ zr->overlay_settings.y * ++ zr->vbuf_bytesperline; ++ btwrite(reg, ZR36057_VDTR); ++ if (reg & 3) ++ dprintk(1, ++ KERN_ERR ++ "%s: zr36057_overlay() - video_address not aligned\n", ++ ZR_DEVNAME(zr)); ++ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) ++ reg += zr->vbuf_bytesperline; ++ btwrite(reg, ZR36057_VDBR); ++ ++ /* video stride, status, and frame grab register */ ++ reg = zr->vbuf_bytesperline - ++ zr->overlay_settings.width * ++ ((zr->overlay_settings.format->depth + 7) / 8); ++ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) ++ reg += zr->vbuf_bytesperline; ++ if (reg & 3) ++ dprintk(1, ++ KERN_ERR ++ "%s: zr36057_overlay() - video_stride not aligned\n", ++ ZR_DEVNAME(zr)); ++ reg = (reg << ZR36057_VSSFGR_DispStride); ++ reg |= ZR36057_VSSFGR_VidOvf; /* clear overflow status */ ++ btwrite(reg, ZR36057_VSSFGR); ++ ++ /* Set overlay clipping */ ++ if (zr->overlay_settings.clipcount > 0) ++ btor(ZR36057_OCR_OvlEnable, ZR36057_OCR); ++ ++ /* ... and switch it on */ ++ btor(ZR36057_VDCR_VidEn, ZR36057_VDCR); ++ } else { ++ /* Switch it off */ ++ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); ++ } ++} ++ ++/* ++ * The overlay mask has one bit for each pixel on a scan line, ++ * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. ++ */ ++ ++void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; ++ u32 *mask; ++ int x, y, width, height; ++ unsigned i, j, k; ++ ++ /* fill mask with one bits */ ++ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); ++ ++ for (i = 0; i < count; ++i) { ++ /* pick up local copy of clip */ ++ x = vp[i].c.left; ++ y = vp[i].c.top; ++ width = vp[i].c.width; ++ height = vp[i].c.height; ++ ++ /* trim clips that extend beyond the window */ ++ if (x < 0) { ++ width += x; ++ x = 0; ++ } ++ if (y < 0) { ++ height += y; ++ y = 0; ++ } ++ if (x + width > fh->overlay_settings.width) { ++ width = fh->overlay_settings.width - x; ++ } ++ if (y + height > fh->overlay_settings.height) { ++ height = fh->overlay_settings.height - y; ++ } ++ ++ /* ignore degenerate clips */ ++ if (height <= 0) { ++ continue; ++ } ++ if (width <= 0) { ++ continue; ++ } ++ ++ /* apply clip for each scan line */ ++ for (j = 0; j < height; ++j) { ++ /* reset bit for each pixel */ ++ /* this can be optimized later if need be */ ++ mask = fh->overlay_mask + (y + j) * mask_line_size; ++ for (k = 0; k < width; ++k) { ++ mask[(x + k) / 32] &= ++ ~((u32) 1 << (x + k) % 32); ++ } ++ } ++ } ++} ++ ++/* Enable/Disable uncompressed memory grabbing of the 36057 */ ++ ++void ++zr36057_set_memgrab (struct zoran *zr, ++ int mode) ++{ ++ if (mode) { ++ /* We only check SnapShot and not FrameGrab here. SnapShot==1 ++ * means a capture is already in progress, but FrameGrab==1 ++ * doesn't necessary mean that. It's more correct to say a 1 ++ * to 0 transition indicates a capture completed. If a ++ * capture is pending when capturing is tuned off, FrameGrab ++ * will be stuck at 1 until capturing is turned back on. ++ */ ++ if (btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) ++ dprintk(1, ++ KERN_WARNING ++ "%s: zr36057_set_memgrab(1) with SnapShot on!?\n", ++ ZR_DEVNAME(zr)); ++ ++ /* switch on VSync interrupts */ ++ btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts ++ btor(zr->card.vsync_int, ZR36057_ICR); // SW ++ ++ /* enable SnapShot */ ++ btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); ++ ++ /* Set zr36057 video front end and enable video */ ++ zr36057_set_vfe(zr, zr->v4l_settings.width, ++ zr->v4l_settings.height, ++ zr->v4l_settings.format); ++ ++ zr->v4l_memgrab_active = 1; ++ } else { ++ /* switch off VSync interrupts */ ++ btand(~zr->card.vsync_int, ZR36057_ICR); // SW ++ ++ zr->v4l_memgrab_active = 0; ++ zr->v4l_grab_frame = NO_GRAB_ACTIVE; ++ ++ /* reenable grabbing to screen if it was running */ ++ if (zr->v4l_overlay_active) { ++ zr36057_overlay(zr, 1); ++ } else { ++ btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR); ++ btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR); ++ } ++ } ++} ++ ++int ++wait_grab_pending (struct zoran *zr) ++{ ++ unsigned long flags; ++ ++ /* wait until all pending grabs are finished */ ++ ++ if (!zr->v4l_memgrab_active) ++ return 0; ++ ++ wait_event_interruptible(zr->v4l_capq, ++ (zr->v4l_pend_tail == zr->v4l_pend_head)); ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ zr36057_set_memgrab(zr, 0); ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ return 0; ++} ++ ++/***************************************************************************** ++ * * ++ * Set up the Buz-specific MJPEG part * ++ * * ++ *****************************************************************************/ ++ ++static inline void ++set_frame (struct zoran *zr, ++ int val) ++{ ++ GPIO(zr, zr->card.gpio[ZR_GPIO_JPEG_FRAME], val); ++} ++ ++static void ++set_videobus_dir (struct zoran *zr, ++ int val) ++{ ++ switch (zr->card.type) { ++ case LML33: ++ case LML33R10: ++ if (lml33dpath == 0) ++ GPIO(zr, 5, val); ++ else ++ GPIO(zr, 5, 1); ++ break; ++ default: ++ GPIO(zr, zr->card.gpio[ZR_GPIO_VID_DIR], ++ zr->card.gpio_pol[ZR_GPIO_VID_DIR] ? !val : val); ++ break; ++ } ++} ++ ++static void ++init_jpeg_queue (struct zoran *zr) ++{ ++ int i; ++ ++ /* re-initialize DMA ring stuff */ ++ zr->jpg_que_head = 0; ++ zr->jpg_dma_head = 0; ++ zr->jpg_dma_tail = 0; ++ zr->jpg_que_tail = 0; ++ zr->jpg_seq_num = 0; ++ zr->JPEG_error = 0; ++ zr->num_errors = 0; ++ zr->jpg_err_seq = 0; ++ zr->jpg_err_shift = 0; ++ zr->jpg_queued_num = 0; ++ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { ++ zr->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ ++ } ++ for (i = 0; i < BUZ_NUM_STAT_COM; i++) { ++ zr->stat_com[i] = cpu_to_le32(1); /* mark as unavailable to zr36057 */ ++ } ++} ++ ++static void ++zr36057_set_jpg (struct zoran *zr, ++ enum zoran_codec_mode mode) ++{ ++ struct tvnorm *tvn; ++ u32 reg; ++ ++ tvn = zr->timing; ++ ++ /* assert P_Reset, disable code transfer, deassert Active */ ++ btwrite(0, ZR36057_JPC); ++ ++ /* MJPEG compression mode */ ++ switch (mode) { ++ ++ case BUZ_MODE_MOTION_COMPRESS: ++ default: ++ reg = ZR36057_JMC_MJPGCmpMode; ++ break; ++ ++ case BUZ_MODE_MOTION_DECOMPRESS: ++ reg = ZR36057_JMC_MJPGExpMode; ++ reg |= ZR36057_JMC_SyncMstr; ++ /* RJ: The following is experimental - improves the output to screen */ ++ //if(zr->jpg_settings.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM ++ break; ++ ++ case BUZ_MODE_STILL_COMPRESS: ++ reg = ZR36057_JMC_JPGCmpMode; ++ break; ++ ++ case BUZ_MODE_STILL_DECOMPRESS: ++ reg = ZR36057_JMC_JPGExpMode; ++ break; ++ ++ } ++ reg |= ZR36057_JMC_JPG; ++ if (zr->jpg_settings.field_per_buff == 1) ++ reg |= ZR36057_JMC_Fld_per_buff; ++ btwrite(reg, ZR36057_JMC); ++ ++ /* vertical */ ++ btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR); ++ reg = (6 << ZR36057_VSP_VsyncSize) | ++ (tvn->Ht << ZR36057_VSP_FrmTot); ++ btwrite(reg, ZR36057_VSP); ++ reg = ((zr->jpg_settings.img_y + tvn->VStart) << ZR36057_FVAP_NAY) | ++ (zr->jpg_settings.img_height << ZR36057_FVAP_PAY); ++ btwrite(reg, ZR36057_FVAP); ++ ++ /* horizontal */ ++ if (zr->card.vfe_pol.hsync_pol) ++ btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); ++ else ++ btand(~ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR); ++ reg = ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | ++ (tvn->Wt << ZR36057_HSP_LineTot); ++ btwrite(reg, ZR36057_HSP); ++ reg = ((zr->jpg_settings.img_x + ++ tvn->HStart + 4) << ZR36057_FHAP_NAX) | ++ (zr->jpg_settings.img_width << ZR36057_FHAP_PAX); ++ btwrite(reg, ZR36057_FHAP); ++ ++ /* field process parameters */ ++ if (zr->jpg_settings.odd_even) ++ reg = ZR36057_FPP_Odd_Even; ++ else ++ reg = 0; ++ ++ btwrite(reg, ZR36057_FPP); ++ ++ /* Set proper VCLK Polarity, else colors will be wrong during playback */ ++ //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR); ++ ++ /* code base address */ ++ reg = virt_to_bus(zr->stat_com); ++ btwrite(reg, ZR36057_JCBA); ++ ++ /* FIFO threshold (FIFO is 160. double words) */ ++ /* NOTE: decimal values here */ ++ switch (mode) { ++ ++ case BUZ_MODE_STILL_COMPRESS: ++ case BUZ_MODE_MOTION_COMPRESS: ++ if (zr->card.type != BUZ) ++ reg = 140; ++ else ++ reg = 60; ++ break; ++ ++ case BUZ_MODE_STILL_DECOMPRESS: ++ case BUZ_MODE_MOTION_DECOMPRESS: ++ reg = 20; ++ break; ++ ++ default: ++ reg = 80; ++ break; ++ ++ } ++ btwrite(reg, ZR36057_JCFT); ++ zr36057_adjust_vfe(zr, mode); ++ ++} ++ ++void ++print_interrupts (struct zoran *zr) ++{ ++ int res, noerr = 0; ++ ++ printk(KERN_INFO "%s: interrupts received:", ZR_DEVNAME(zr)); ++ if ((res = zr->field_counter) < -1 || res > 1) { ++ printk(" FD:%d", res); ++ } ++ if ((res = zr->intr_counter_GIRQ1) != 0) { ++ printk(" GIRQ1:%d", res); ++ noerr++; ++ } ++ if ((res = zr->intr_counter_GIRQ0) != 0) { ++ printk(" GIRQ0:%d", res); ++ noerr++; ++ } ++ if ((res = zr->intr_counter_CodRepIRQ) != 0) { ++ printk(" CodRepIRQ:%d", res); ++ noerr++; ++ } ++ if ((res = zr->intr_counter_JPEGRepIRQ) != 0) { ++ printk(" JPEGRepIRQ:%d", res); ++ noerr++; ++ } ++ if (zr->JPEG_max_missed) { ++ printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed, ++ zr->JPEG_min_missed); ++ } ++ if (zr->END_event_missed) { ++ printk(" ENDs missed: %d", zr->END_event_missed); ++ } ++ //if (zr->jpg_queued_num) { ++ printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail, ++ zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head); ++ //} ++ if (!noerr) { ++ printk(": no interrupts detected."); ++ } ++ printk("\n"); ++} ++ ++void ++clear_interrupt_counters (struct zoran *zr) ++{ ++ zr->intr_counter_GIRQ1 = 0; ++ zr->intr_counter_GIRQ0 = 0; ++ zr->intr_counter_CodRepIRQ = 0; ++ zr->intr_counter_JPEGRepIRQ = 0; ++ zr->field_counter = 0; ++ zr->IRQ1_in = 0; ++ zr->IRQ1_out = 0; ++ zr->JPEG_in = 0; ++ zr->JPEG_out = 0; ++ zr->JPEG_0 = 0; ++ zr->JPEG_1 = 0; ++ zr->END_event_missed = 0; ++ zr->JPEG_missed = 0; ++ zr->JPEG_max_missed = 0; ++ zr->JPEG_min_missed = 0x7fffffff; ++} ++ ++static u32 ++count_reset_interrupt (struct zoran *zr) ++{ ++ u32 isr; ++ ++ if ((isr = btread(ZR36057_ISR) & 0x78000000)) { ++ if (isr & ZR36057_ISR_GIRQ1) { ++ btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR); ++ zr->intr_counter_GIRQ1++; ++ } ++ if (isr & ZR36057_ISR_GIRQ0) { ++ btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR); ++ zr->intr_counter_GIRQ0++; ++ } ++ if (isr & ZR36057_ISR_CodRepIRQ) { ++ btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR); ++ zr->intr_counter_CodRepIRQ++; ++ } ++ if (isr & ZR36057_ISR_JPEGRepIRQ) { ++ btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR); ++ zr->intr_counter_JPEGRepIRQ++; ++ } ++ } ++ return isr; ++} ++ ++void ++jpeg_start (struct zoran *zr) ++{ ++ int reg; ++ ++ zr->frame_num = 0; ++ ++ /* deassert P_reset, disable code transfer, deassert Active */ ++ btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC); ++ /* stop flushing the internal code buffer */ ++ btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR); ++ /* enable code transfer */ ++ btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC); ++ ++ /* clear IRQs */ ++ btwrite(IRQ_MASK, ZR36057_ISR); ++ /* enable the JPEG IRQs */ ++ btwrite(zr->card.jpeg_int | ++ ZR36057_ICR_JPEGRepIRQ | ++ ZR36057_ICR_IntPinEn, ++ ZR36057_ICR); ++ ++ set_frame(zr, 0); // \FRAME ++ ++ /* set the JPEG codec guest ID */ ++ reg = (zr->card.gpcs[1] << ZR36057_JCGI_JPEGuestID) | ++ (0 << ZR36057_JCGI_JPEGuestReg); ++ btwrite(reg, ZR36057_JCGI); ++ ++ if (zr->card.video_vfe == CODEC_TYPE_ZR36016 && ++ zr->card.video_codec == CODEC_TYPE_ZR36050) { ++ /* Enable processing on the ZR36016 */ ++ if (zr->vfe) ++ zr36016_write(zr->vfe, 0, 1); ++ ++ /* load the address of the GO register in the ZR36050 latch */ ++ post_office_write(zr, 0, 0, 0); ++ } ++ ++ /* assert Active */ ++ btor(ZR36057_JPC_Active, ZR36057_JPC); ++ ++ /* enable the Go generation */ ++ btor(ZR36057_JMC_Go_en, ZR36057_JMC); ++ udelay(30); ++ ++ set_frame(zr, 1); // /FRAME ++ ++ dprintk(3, KERN_DEBUG "%s: jpeg_start\n", ZR_DEVNAME(zr)); ++} ++ ++void ++zr36057_enable_jpg (struct zoran *zr, ++ enum zoran_codec_mode mode) ++{ ++ struct vfe_settings cap; ++ int field_size = ++ zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff; ++ ++ zr->codec_mode = mode; ++ ++ cap.x = zr->jpg_settings.img_x; ++ cap.y = zr->jpg_settings.img_y; ++ cap.width = zr->jpg_settings.img_width; ++ cap.height = zr->jpg_settings.img_height; ++ cap.decimation = ++ zr->jpg_settings.HorDcm | (zr->jpg_settings.VerDcm << 8); ++ cap.quality = zr->jpg_settings.jpg_comp.quality; ++ ++ switch (mode) { ++ ++ case BUZ_MODE_MOTION_COMPRESS: { ++ struct jpeg_app_marker app; ++ struct jpeg_com_marker com; ++ ++ /* In motion compress mode, the decoder output must be enabled, and ++ * the video bus direction set to input. ++ */ ++ set_videobus_dir(zr, 0); ++ decoder_call(zr, video, s_stream, 1); ++ encoder_call(zr, video, s_routing, 0, 0, 0); ++ ++ /* Take the JPEG codec and the VFE out of sleep */ ++ jpeg_codec_sleep(zr, 0); ++ ++ /* set JPEG app/com marker */ ++ app.appn = zr->jpg_settings.jpg_comp.APPn; ++ app.len = zr->jpg_settings.jpg_comp.APP_len; ++ memcpy(app.data, zr->jpg_settings.jpg_comp.APP_data, 60); ++ zr->codec->control(zr->codec, CODEC_S_JPEG_APP_DATA, ++ sizeof(struct jpeg_app_marker), &app); ++ ++ com.len = zr->jpg_settings.jpg_comp.COM_len; ++ memcpy(com.data, zr->jpg_settings.jpg_comp.COM_data, 60); ++ zr->codec->control(zr->codec, CODEC_S_JPEG_COM_DATA, ++ sizeof(struct jpeg_com_marker), &com); ++ ++ /* Setup the JPEG codec */ ++ zr->codec->control(zr->codec, CODEC_S_JPEG_TDS_BYTE, ++ sizeof(int), &field_size); ++ zr->codec->set_video(zr->codec, zr->timing, &cap, ++ &zr->card.vfe_pol); ++ zr->codec->set_mode(zr->codec, CODEC_DO_COMPRESSION); ++ ++ /* Setup the VFE */ ++ if (zr->vfe) { ++ zr->vfe->control(zr->vfe, CODEC_S_JPEG_TDS_BYTE, ++ sizeof(int), &field_size); ++ zr->vfe->set_video(zr->vfe, zr->timing, &cap, ++ &zr->card.vfe_pol); ++ zr->vfe->set_mode(zr->vfe, CODEC_DO_COMPRESSION); ++ } ++ ++ init_jpeg_queue(zr); ++ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO ++ ++ clear_interrupt_counters(zr); ++ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_COMPRESS)\n", ++ ZR_DEVNAME(zr)); ++ break; ++ } ++ ++ case BUZ_MODE_MOTION_DECOMPRESS: ++ /* In motion decompression mode, the decoder output must be disabled, and ++ * the video bus direction set to output. ++ */ ++ decoder_call(zr, video, s_stream, 0); ++ set_videobus_dir(zr, 1); ++ encoder_call(zr, video, s_routing, 1, 0, 0); ++ ++ /* Take the JPEG codec and the VFE out of sleep */ ++ jpeg_codec_sleep(zr, 0); ++ /* Setup the VFE */ ++ if (zr->vfe) { ++ zr->vfe->set_video(zr->vfe, zr->timing, &cap, ++ &zr->card.vfe_pol); ++ zr->vfe->set_mode(zr->vfe, CODEC_DO_EXPANSION); ++ } ++ /* Setup the JPEG codec */ ++ zr->codec->set_video(zr->codec, zr->timing, &cap, ++ &zr->card.vfe_pol); ++ zr->codec->set_mode(zr->codec, CODEC_DO_EXPANSION); ++ ++ init_jpeg_queue(zr); ++ zr36057_set_jpg(zr, mode); // \P_Reset, ... Video param, FIFO ++ ++ clear_interrupt_counters(zr); ++ dprintk(2, KERN_INFO "%s: enable_jpg(MOTION_DECOMPRESS)\n", ++ ZR_DEVNAME(zr)); ++ break; ++ ++ case BUZ_MODE_IDLE: ++ default: ++ /* shut down processing */ ++ btand(~(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ), ++ ZR36057_ICR); ++ btwrite(zr->card.jpeg_int | ZR36057_ICR_JPEGRepIRQ, ++ ZR36057_ISR); ++ btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en ++ ++ msleep(50); ++ ++ set_videobus_dir(zr, 0); ++ set_frame(zr, 1); // /FRAME ++ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); // /CFlush ++ btwrite(0, ZR36057_JPC); // \P_Reset,\CodTrnsEn,\Active ++ btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC); ++ btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC); ++ jpeg_codec_reset(zr); ++ jpeg_codec_sleep(zr, 1); ++ zr36057_adjust_vfe(zr, mode); ++ ++ decoder_call(zr, video, s_stream, 1); ++ encoder_call(zr, video, s_routing, 0, 0, 0); ++ ++ dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr)); ++ break; ++ ++ } ++} ++ ++/* when this is called the spinlock must be held */ ++void ++zoran_feed_stat_com (struct zoran *zr) ++{ ++ /* move frames from pending queue to DMA */ ++ ++ int frame, i, max_stat_com; ++ ++ max_stat_com = ++ (zr->jpg_settings.TmpDcm == ++ 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1); ++ ++ while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com && ++ zr->jpg_dma_head < zr->jpg_que_head) { ++ ++ frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME]; ++ if (zr->jpg_settings.TmpDcm == 1) { ++ /* fill 1 stat_com entry */ ++ i = (zr->jpg_dma_head - ++ zr->jpg_err_shift) & BUZ_MASK_STAT_COM; ++ if (!(zr->stat_com[i] & cpu_to_le32(1))) ++ break; ++ zr->stat_com[i] = ++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); ++ } else { ++ /* fill 2 stat_com entries */ ++ i = ((zr->jpg_dma_head - ++ zr->jpg_err_shift) & 1) * 2; ++ if (!(zr->stat_com[i] & cpu_to_le32(1))) ++ break; ++ zr->stat_com[i] = ++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); ++ zr->stat_com[i + 1] = ++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); ++ } ++ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA; ++ zr->jpg_dma_head++; ++ ++ } ++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) ++ zr->jpg_queued_num++; ++} ++ ++/* when this is called the spinlock must be held */ ++static void ++zoran_reap_stat_com (struct zoran *zr) ++{ ++ /* move frames from DMA queue to done queue */ ++ ++ int i; ++ u32 stat_com; ++ unsigned int seq; ++ unsigned int dif; ++ struct zoran_buffer *buffer; ++ int frame; ++ ++ /* In motion decompress we don't have a hardware frame counter, ++ * we just count the interrupts here */ ++ ++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { ++ zr->jpg_seq_num++; ++ } ++ while (zr->jpg_dma_tail < zr->jpg_dma_head) { ++ if (zr->jpg_settings.TmpDcm == 1) ++ i = (zr->jpg_dma_tail - ++ zr->jpg_err_shift) & BUZ_MASK_STAT_COM; ++ else ++ i = ((zr->jpg_dma_tail - ++ zr->jpg_err_shift) & 1) * 2 + 1; ++ ++ stat_com = le32_to_cpu(zr->stat_com[i]); ++ ++ if ((stat_com & 1) == 0) { ++ return; ++ } ++ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; ++ buffer = &zr->jpg_buffers.buffer[frame]; ++ v4l2_get_timestamp(&buffer->bs.timestamp); ++ ++ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { ++ buffer->bs.length = (stat_com & 0x7fffff) >> 1; ++ ++ /* update sequence number with the help of the counter in stat_com */ ++ ++ seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff; ++ dif = (seq - zr->jpg_seq_num) & 0xff; ++ zr->jpg_seq_num += dif; ++ } else { ++ buffer->bs.length = 0; ++ } ++ buffer->bs.seq = ++ zr->jpg_settings.TmpDcm == ++ 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num; ++ buffer->state = BUZ_STATE_DONE; ++ ++ zr->jpg_dma_tail++; ++ } ++} ++ ++static void zoran_restart(struct zoran *zr) ++{ ++ /* Now the stat_comm buffer is ready for restart */ ++ unsigned int status = 0; ++ int mode; ++ ++ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { ++ decoder_call(zr, video, g_input_status, &status); ++ mode = CODEC_DO_COMPRESSION; ++ } else { ++ status = V4L2_IN_ST_NO_SIGNAL; ++ mode = CODEC_DO_EXPANSION; ++ } ++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || ++ !(status & V4L2_IN_ST_NO_SIGNAL)) { ++ /********** RESTART code *************/ ++ jpeg_codec_reset(zr); ++ zr->codec->set_mode(zr->codec, mode); ++ zr36057_set_jpg(zr, zr->codec_mode); ++ jpeg_start(zr); ++ ++ if (zr->num_errors <= 8) ++ dprintk(2, KERN_INFO "%s: Restart\n", ++ ZR_DEVNAME(zr)); ++ ++ zr->JPEG_missed = 0; ++ zr->JPEG_error = 2; ++ /********** End RESTART code ***********/ ++ } ++} ++ ++static void ++error_handler (struct zoran *zr, ++ u32 astat, ++ u32 stat) ++{ ++ int i; ++ ++ /* This is JPEG error handling part */ ++ if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS && ++ zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) { ++ return; ++ } ++ ++ if ((stat & 1) == 0 && ++ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS && ++ zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) { ++ /* No free buffers... */ ++ zoran_reap_stat_com(zr); ++ zoran_feed_stat_com(zr); ++ wake_up_interruptible(&zr->jpg_capq); ++ zr->JPEG_missed = 0; ++ return; ++ } ++ ++ if (zr->JPEG_error == 1) { ++ zoran_restart(zr); ++ return; ++ } ++ ++ /* ++ * First entry: error just happened during normal operation ++ * ++ * In BUZ_MODE_MOTION_COMPRESS: ++ * ++ * Possible glitch in TV signal. In this case we should ++ * stop the codec and wait for good quality signal before ++ * restarting it to avoid further problems ++ * ++ * In BUZ_MODE_MOTION_DECOMPRESS: ++ * ++ * Bad JPEG frame: we have to mark it as processed (codec crashed ++ * and was not able to do it itself), and to remove it from queue. ++ */ ++ btand(~ZR36057_JMC_Go_en, ZR36057_JMC); ++ udelay(1); ++ stat = stat | (post_office_read(zr, 7, 0) & 3) << 8; ++ btwrite(0, ZR36057_JPC); ++ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); ++ jpeg_codec_reset(zr); ++ jpeg_codec_sleep(zr, 1); ++ zr->JPEG_error = 1; ++ zr->num_errors++; ++ ++ /* Report error */ ++ if (zr36067_debug > 1 && zr->num_errors <= 8) { ++ long frame; ++ int j; ++ ++ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; ++ printk(KERN_ERR ++ "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", ++ ZR_DEVNAME(zr), stat, zr->last_isr, ++ zr->jpg_que_tail, zr->jpg_dma_tail, ++ zr->jpg_dma_head, zr->jpg_que_head, ++ zr->jpg_seq_num, frame); ++ printk(KERN_INFO "stat_com frames:"); ++ for (j = 0; j < BUZ_NUM_STAT_COM; j++) { ++ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { ++ if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus) ++ printk(KERN_CONT "% d->%d", j, i); ++ } ++ } ++ printk(KERN_CONT "\n"); ++ } ++ /* Find an entry in stat_com and rotate contents */ ++ if (zr->jpg_settings.TmpDcm == 1) ++ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; ++ else ++ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; ++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { ++ /* Mimic zr36067 operation */ ++ zr->stat_com[i] |= cpu_to_le32(1); ++ if (zr->jpg_settings.TmpDcm != 1) ++ zr->stat_com[i + 1] |= cpu_to_le32(1); ++ /* Refill */ ++ zoran_reap_stat_com(zr); ++ zoran_feed_stat_com(zr); ++ wake_up_interruptible(&zr->jpg_capq); ++ /* Find an entry in stat_com again after refill */ ++ if (zr->jpg_settings.TmpDcm == 1) ++ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; ++ else ++ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; ++ } ++ if (i) { ++ /* Rotate stat_comm entries to make current entry first */ ++ int j; ++ __le32 bus_addr[BUZ_NUM_STAT_COM]; ++ ++ /* Here we are copying the stat_com array, which ++ * is already in little endian format, so ++ * no endian conversions here ++ */ ++ memcpy(bus_addr, zr->stat_com, sizeof(bus_addr)); ++ ++ for (j = 0; j < BUZ_NUM_STAT_COM; j++) ++ zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM]; ++ ++ zr->jpg_err_shift += i; ++ zr->jpg_err_shift &= BUZ_MASK_STAT_COM; ++ } ++ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) ++ zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ ++ zoran_restart(zr); ++} ++ ++irqreturn_t ++zoran_irq (int irq, ++ void *dev_id) ++{ ++ u32 stat, astat; ++ int count; ++ struct zoran *zr; ++ unsigned long flags; ++ ++ zr = dev_id; ++ count = 0; ++ ++ if (zr->testing) { ++ /* Testing interrupts */ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ while ((stat = count_reset_interrupt(zr))) { ++ if (count++ > 100) { ++ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); ++ dprintk(1, ++ KERN_ERR ++ "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n", ++ ZR_DEVNAME(zr), stat); ++ wake_up_interruptible(&zr->test_q); ++ } ++ } ++ zr->last_isr = stat; ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ while (1) { ++ /* get/clear interrupt status bits */ ++ stat = count_reset_interrupt(zr); ++ astat = stat & IRQ_MASK; ++ if (!astat) { ++ break; ++ } ++ dprintk(4, ++ KERN_DEBUG ++ "zoran_irq: astat: 0x%08x, mask: 0x%08x\n", ++ astat, btread(ZR36057_ICR)); ++ if (astat & zr->card.vsync_int) { // SW ++ ++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || ++ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { ++ /* count missed interrupts */ ++ zr->JPEG_missed++; ++ } ++ //post_office_read(zr,1,0); ++ /* Interrupts may still happen when ++ * zr->v4l_memgrab_active is switched off. ++ * We simply ignore them */ ++ ++ if (zr->v4l_memgrab_active) { ++ /* A lot more checks should be here ... */ ++ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) ++ dprintk(1, ++ KERN_WARNING ++ "%s: BuzIRQ with SnapShot off ???\n", ++ ZR_DEVNAME(zr)); ++ ++ if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { ++ /* There is a grab on a frame going on, check if it has finished */ ++ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { ++ /* it is finished, notify the user */ ++ ++ zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; ++ zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.seq = zr->v4l_grab_seq; ++ v4l2_get_timestamp(&zr->v4l_buffers.buffer[zr->v4l_grab_frame].bs.timestamp); ++ zr->v4l_grab_frame = NO_GRAB_ACTIVE; ++ zr->v4l_pend_tail++; ++ } ++ } ++ ++ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE) ++ wake_up_interruptible(&zr->v4l_capq); ++ ++ /* Check if there is another grab queued */ ++ ++ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && ++ zr->v4l_pend_tail != zr->v4l_pend_head) { ++ int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; ++ u32 reg; ++ ++ zr->v4l_grab_frame = frame; ++ ++ /* Set zr36057 video front end and enable video */ ++ ++ /* Buffer address */ ++ ++ reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus; ++ btwrite(reg, ZR36057_VDTR); ++ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) ++ reg += zr->v4l_settings.bytesperline; ++ btwrite(reg, ZR36057_VDBR); ++ ++ /* video stride, status, and frame grab register */ ++ reg = 0; ++ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) ++ reg += zr->v4l_settings.bytesperline; ++ reg = (reg << ZR36057_VSSFGR_DispStride); ++ reg |= ZR36057_VSSFGR_VidOvf; ++ reg |= ZR36057_VSSFGR_SnapShot; ++ reg |= ZR36057_VSSFGR_FrameGrab; ++ btwrite(reg, ZR36057_VSSFGR); ++ ++ btor(ZR36057_VDCR_VidEn, ++ ZR36057_VDCR); ++ } ++ } ++ ++ /* even if we don't grab, we do want to increment ++ * the sequence counter to see lost frames */ ++ zr->v4l_grab_seq++; ++ } ++#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) ++ if (astat & ZR36057_ISR_CodRepIRQ) { ++ zr->intr_counter_CodRepIRQ++; ++ IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", ++ ZR_DEVNAME(zr))); ++ btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); ++ } ++#endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ ++ ++#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) ++ if ((astat & ZR36057_ISR_JPEGRepIRQ) && ++ (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || ++ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { ++ if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) { ++ char sv[BUZ_NUM_STAT_COM + 1]; ++ int i; ++ ++ printk(KERN_INFO ++ "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", ++ ZR_DEVNAME(zr), stat, ++ zr->jpg_settings.odd_even, ++ zr->jpg_settings.field_per_buff, ++ zr->JPEG_missed); ++ ++ for (i = 0; i < BUZ_NUM_STAT_COM; i++) ++ sv[i] = le32_to_cpu(zr->stat_com[i]) & 1 ? '1' : '0'; ++ sv[BUZ_NUM_STAT_COM] = 0; ++ printk(KERN_INFO ++ "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", ++ ZR_DEVNAME(zr), sv, ++ zr->jpg_que_tail, ++ zr->jpg_dma_tail, ++ zr->jpg_dma_head, ++ zr->jpg_que_head); ++ } else { ++ /* Get statistics */ ++ if (zr->JPEG_missed > zr->JPEG_max_missed) ++ zr->JPEG_max_missed = zr->JPEG_missed; ++ if (zr->JPEG_missed < zr->JPEG_min_missed) ++ zr->JPEG_min_missed = zr->JPEG_missed; ++ } ++ ++ if (zr36067_debug > 2 && zr->frame_num < 6) { ++ int i; ++ ++ printk(KERN_INFO "%s: seq=%ld stat_com:", ++ ZR_DEVNAME(zr), zr->jpg_seq_num); ++ for (i = 0; i < 4; i++) { ++ printk(KERN_CONT " %08x", ++ le32_to_cpu(zr->stat_com[i])); ++ } ++ printk(KERN_CONT "\n"); ++ } ++ zr->frame_num++; ++ zr->JPEG_missed = 0; ++ zr->JPEG_error = 0; ++ zoran_reap_stat_com(zr); ++ zoran_feed_stat_com(zr); ++ wake_up_interruptible(&zr->jpg_capq); ++ } ++#endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ ++ ++ /* DATERR, too many fields missed, error processing */ ++ if ((astat & zr->card.jpeg_int) || ++ zr->JPEG_missed > 25 || ++ zr->JPEG_error == 1 || ++ ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) && ++ (zr->frame_num && (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) { ++ error_handler(zr, astat, stat); ++ } ++ ++ count++; ++ if (count > 10) { ++ dprintk(2, KERN_WARNING "%s: irq loop %d\n", ++ ZR_DEVNAME(zr), count); ++ if (count > 20) { ++ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); ++ dprintk(2, ++ KERN_ERR ++ "%s: IRQ lockup, cleared int mask\n", ++ ZR_DEVNAME(zr)); ++ break; ++ } ++ } ++ zr->last_isr = stat; ++ } ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++void ++zoran_set_pci_master (struct zoran *zr, ++ int set_master) ++{ ++ if (set_master) { ++ pci_set_master(zr->pci_dev); ++ } else { ++ u16 command; ++ ++ pci_read_config_word(zr->pci_dev, PCI_COMMAND, &command); ++ command &= ~PCI_COMMAND_MASTER; ++ pci_write_config_word(zr->pci_dev, PCI_COMMAND, command); ++ } ++} ++ ++void ++zoran_init_hardware (struct zoran *zr) ++{ ++ /* Enable bus-mastering */ ++ zoran_set_pci_master(zr, 1); ++ ++ /* Initialize the board */ ++ if (zr->card.init) { ++ zr->card.init(zr); ++ } ++ ++ decoder_call(zr, core, init, 0); ++ decoder_call(zr, core, s_std, zr->norm); ++ decoder_call(zr, video, s_routing, ++ zr->card.input[zr->input].muxsel, 0, 0); ++ ++ encoder_call(zr, core, init, 0); ++ encoder_call(zr, video, s_std_output, zr->norm); ++ encoder_call(zr, video, s_routing, 0, 0, 0); ++ ++ /* toggle JPEG codec sleep to sync PLL */ ++ jpeg_codec_sleep(zr, 1); ++ jpeg_codec_sleep(zr, 0); ++ ++ /* set individual interrupt enables (without GIRQ1) ++ * but don't global enable until zoran_open() */ ++ ++ //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR); // SW ++ // It looks like using only JPEGRepIRQEn is not always reliable, ++ // may be when JPEG codec crashes it won't generate IRQ? So, ++ /*CP*/ // btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM WHY ? LP ++ zr36057_init_vfe(zr); ++ ++ zr36057_enable_jpg(zr, BUZ_MODE_IDLE); ++ ++ btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts ++} ++ ++void ++zr36057_restart (struct zoran *zr) ++{ ++ btwrite(0, ZR36057_SPGPPCR); ++ mdelay(1); ++ btor(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR); ++ mdelay(1); ++ ++ /* assert P_Reset */ ++ btwrite(0, ZR36057_JPC); ++ /* set up GPIO direction - all output */ ++ btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR); ++ ++ /* set up GPIO pins and guest bus timing */ ++ btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1); ++} ++ ++/* ++ * initialize video front end ++ */ ++ ++static void ++zr36057_init_vfe (struct zoran *zr) ++{ ++ u32 reg; ++ ++ reg = btread(ZR36057_VFESPFR); ++ reg |= ZR36057_VFESPFR_LittleEndian; ++ reg &= ~ZR36057_VFESPFR_VCLKPol; ++ reg |= ZR36057_VFESPFR_ExtFl; ++ reg |= ZR36057_VFESPFR_TopField; ++ btwrite(reg, ZR36057_VFESPFR); ++ reg = btread(ZR36057_VDCR); ++ if (pci_pci_problems & PCIPCI_TRITON) ++ // || zr->revision < 1) // Revision 1 has also Triton support ++ reg &= ~ZR36057_VDCR_Triton; ++ else ++ reg |= ZR36057_VDCR_Triton; ++ btwrite(reg, ZR36057_VDCR); ++} +diff --git a/drivers/media/pci/zoran/zoran_device.h b/drivers/media/pci/zoran/zoran_device.h +new file mode 100644 +index 0000000..07f2c23 +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_device.h +@@ -0,0 +1,95 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles card-specific data and detection ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ZORAN_DEVICE_H__ ++#define __ZORAN_DEVICE_H__ ++ ++/* general purpose I/O */ ++extern void GPIO(struct zoran *zr, ++ int bit, ++ unsigned int value); ++ ++/* codec (or actually: guest bus) access */ ++extern int post_office_wait(struct zoran *zr); ++extern int post_office_write(struct zoran *zr, ++ unsigned guest, ++ unsigned reg, ++ unsigned value); ++extern int post_office_read(struct zoran *zr, ++ unsigned guest, ++ unsigned reg); ++ ++extern void detect_guest_activity(struct zoran *zr); ++ ++extern void jpeg_codec_sleep(struct zoran *zr, ++ int sleep); ++extern int jpeg_codec_reset(struct zoran *zr); ++ ++/* zr360x7 access to raw capture */ ++extern void zr36057_overlay(struct zoran *zr, ++ int on); ++extern void write_overlay_mask(struct zoran_fh *fh, ++ struct v4l2_clip *vp, ++ int count); ++extern void zr36057_set_memgrab(struct zoran *zr, ++ int mode); ++extern int wait_grab_pending(struct zoran *zr); ++ ++/* interrupts */ ++extern void print_interrupts(struct zoran *zr); ++extern void clear_interrupt_counters(struct zoran *zr); ++extern irqreturn_t zoran_irq(int irq, void *dev_id); ++ ++/* JPEG codec access */ ++extern void jpeg_start(struct zoran *zr); ++extern void zr36057_enable_jpg(struct zoran *zr, ++ enum zoran_codec_mode mode); ++extern void zoran_feed_stat_com(struct zoran *zr); ++ ++/* general */ ++extern void zoran_set_pci_master(struct zoran *zr, ++ int set_master); ++extern void zoran_init_hardware(struct zoran *zr); ++extern void zr36057_restart(struct zoran *zr); ++ ++extern const struct zoran_format zoran_formats[]; ++ ++extern int v4l_nbufs; ++extern int v4l_bufsize; ++extern int jpg_nbufs; ++extern int jpg_bufsize; ++extern int pass_through; ++ ++/* i2c */ ++#define decoder_call(zr, o, f, args...) \ ++ v4l2_subdev_call(zr->decoder, o, f, ##args) ++#define encoder_call(zr, o, f, args...) \ ++ v4l2_subdev_call(zr->encoder, o, f, ##args) ++ ++#endif /* __ZORAN_DEVICE_H__ */ +diff --git a/drivers/media/pci/zoran/zoran_driver.c b/drivers/media/pci/zoran/zoran_driver.c +new file mode 100644 +index 0000000..33521a4 +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_driver.c +@@ -0,0 +1,3090 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Changes for BUZ by Wolfgang Scherr ++ * ++ * Changes for DC10/DC30 by Laurent Pinchart ++ * ++ * Changes for LML33R10 by Maxim Yevtyushkin ++ * ++ * Changes for videodev2/v4l2 by Ronald Bultje ++ * ++ * Based on ++ * ++ * Miro DC10 driver ++ * Copyright (C) 1999 Wolfgang Scherr ++ * ++ * Iomega Buz driver version 1.0 ++ * Copyright (C) 1999 Rainer Johanni ++ * ++ * buz.0.0.3 ++ * Copyright (C) 1998 Dave Perks ++ * ++ * bttv - Bt848 frame grabber driver ++ * Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) ++ * & Marcus Metzler (mocm@thp.uni-koeln.de) ++ * ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include "videocodec.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include "zoran.h" ++#include "zoran_device.h" ++#include "zoran_card.h" ++ ++ ++const struct zoran_format zoran_formats[] = { ++ { ++ .name = "15-bit RGB LE", ++ .fourcc = V4L2_PIX_FMT_RGB555, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 15, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif| ++ ZR36057_VFESPFR_LittleEndian, ++ }, { ++ .name = "15-bit RGB BE", ++ .fourcc = V4L2_PIX_FMT_RGB555X, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 15, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif, ++ }, { ++ .name = "16-bit RGB LE", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 16, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif| ++ ZR36057_VFESPFR_LittleEndian, ++ }, { ++ .name = "16-bit RGB BE", ++ .fourcc = V4L2_PIX_FMT_RGB565X, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 16, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif, ++ }, { ++ .name = "24-bit RGB", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 24, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24, ++ }, { ++ .name = "32-bit RGB LE", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 32, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian, ++ }, { ++ .name = "32-bit RGB BE", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .colorspace = V4L2_COLORSPACE_SRGB, ++ .depth = 32, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_RGB888, ++ }, { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .depth = 16, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_YUV422, ++ }, { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .depth = 16, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_OVERLAY, ++ .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian, ++ }, { ++ .name = "Hardware-encoded Motion-JPEG", ++ .fourcc = V4L2_PIX_FMT_MJPEG, ++ .colorspace = V4L2_COLORSPACE_SMPTE170M, ++ .depth = 0, ++ .flags = ZORAN_FORMAT_CAPTURE | ++ ZORAN_FORMAT_PLAYBACK | ++ ZORAN_FORMAT_COMPRESSED, ++ } ++}; ++#define NUM_FORMATS ARRAY_SIZE(zoran_formats) ++ ++ /* small helper function for calculating buffersizes for v4l2 ++ * we calculate the nearest higher power-of-two, which ++ * will be the recommended buffersize */ ++static __u32 ++zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) ++{ ++ __u8 div = settings->VerDcm * settings->HorDcm * settings->TmpDcm; ++ __u32 num = (1024 * 512) / (div); ++ __u32 result = 2; ++ ++ num--; ++ while (num) { ++ num >>= 1; ++ result <<= 1; ++ } ++ ++ if (result > jpg_bufsize) ++ return jpg_bufsize; ++ if (result < 8192) ++ return 8192; ++ return result; ++} ++ ++/* forward references */ ++static void v4l_fbuffer_free(struct zoran_fh *fh); ++static void jpg_fbuffer_free(struct zoran_fh *fh); ++ ++/* Set mapping mode */ ++static void map_mode_raw(struct zoran_fh *fh) ++{ ++ fh->map_mode = ZORAN_MAP_MODE_RAW; ++ fh->buffers.buffer_size = v4l_bufsize; ++ fh->buffers.num_buffers = v4l_nbufs; ++} ++static void map_mode_jpg(struct zoran_fh *fh, int play) ++{ ++ fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC; ++ fh->buffers.buffer_size = jpg_bufsize; ++ fh->buffers.num_buffers = jpg_nbufs; ++} ++static inline const char *mode_name(enum zoran_map_mode mode) ++{ ++ return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG"; ++} ++ ++/* ++ * Allocate the V4L grab buffers ++ * ++ * These have to be pysically contiguous. ++ */ ++ ++static int v4l_fbuffer_alloc(struct zoran_fh *fh) ++{ ++ struct zoran *zr = fh->zr; ++ int i, off; ++ unsigned char *mem; ++ ++ for (i = 0; i < fh->buffers.num_buffers; i++) { ++ if (fh->buffers.buffer[i].v4l.fbuffer) ++ dprintk(2, ++ KERN_WARNING ++ "%s: %s - buffer %d already allocated!?\n", ++ ZR_DEVNAME(zr), __func__, i); ++ ++ //udelay(20); ++ mem = kmalloc(fh->buffers.buffer_size, ++ GFP_KERNEL | __GFP_NOWARN); ++ if (!mem) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - kmalloc for V4L buf %d failed\n", ++ ZR_DEVNAME(zr), __func__, i); ++ v4l_fbuffer_free(fh); ++ return -ENOBUFS; ++ } ++ fh->buffers.buffer[i].v4l.fbuffer = mem; ++ fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem); ++ fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem); ++ for (off = 0; off < fh->buffers.buffer_size; ++ off += PAGE_SIZE) ++ SetPageReserved(virt_to_page(mem + off)); ++ dprintk(4, ++ KERN_INFO ++ "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n", ++ ZR_DEVNAME(zr), __func__, i, (unsigned long) mem, ++ (unsigned long long)virt_to_bus(mem)); ++ } ++ ++ fh->buffers.allocated = 1; ++ ++ return 0; ++} ++ ++/* free the V4L grab buffers */ ++static void v4l_fbuffer_free(struct zoran_fh *fh) ++{ ++ struct zoran *zr = fh->zr; ++ int i, off; ++ unsigned char *mem; ++ ++ dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__); ++ ++ for (i = 0; i < fh->buffers.num_buffers; i++) { ++ if (!fh->buffers.buffer[i].v4l.fbuffer) ++ continue; ++ ++ mem = fh->buffers.buffer[i].v4l.fbuffer; ++ for (off = 0; off < fh->buffers.buffer_size; ++ off += PAGE_SIZE) ++ ClearPageReserved(virt_to_page(mem + off)); ++ kfree(fh->buffers.buffer[i].v4l.fbuffer); ++ fh->buffers.buffer[i].v4l.fbuffer = NULL; ++ } ++ ++ fh->buffers.allocated = 0; ++} ++ ++/* ++ * Allocate the MJPEG grab buffers. ++ * ++ * If a Natoma chipset is present and this is a revision 1 zr36057, ++ * each MJPEG buffer needs to be physically contiguous. ++ * (RJ: This statement is from Dave Perks' original driver, ++ * I could never check it because I have a zr36067) ++ * ++ * RJ: The contents grab buffers needs never be accessed in the driver. ++ * Therefore there is no need to allocate them with vmalloc in order ++ * to get a contiguous virtual memory space. ++ * I don't understand why many other drivers first allocate them with ++ * vmalloc (which uses internally also get_zeroed_page, but delivers you ++ * virtual addresses) and then again have to make a lot of efforts ++ * to get the physical address. ++ * ++ * Ben Capper: ++ * On big-endian architectures (such as ppc) some extra steps ++ * are needed. When reading and writing to the stat_com array ++ * and fragment buffers, the device expects to see little- ++ * endian values. The use of cpu_to_le32() and le32_to_cpu() ++ * in this function (and one or two others in zoran_device.c) ++ * ensure that these values are always stored in little-endian ++ * form, regardless of architecture. The zr36057 does Very Bad ++ * Things on big endian architectures if the stat_com array ++ * and fragment buffers are not little-endian. ++ */ ++ ++static int jpg_fbuffer_alloc(struct zoran_fh *fh) ++{ ++ struct zoran *zr = fh->zr; ++ int i, j, off; ++ u8 *mem; ++ ++ for (i = 0; i < fh->buffers.num_buffers; i++) { ++ if (fh->buffers.buffer[i].jpg.frag_tab) ++ dprintk(2, ++ KERN_WARNING ++ "%s: %s - buffer %d already allocated!?\n", ++ ZR_DEVNAME(zr), __func__, i); ++ ++ /* Allocate fragment table for this buffer */ ++ ++ mem = (void *)get_zeroed_page(GFP_KERNEL); ++ if (!mem) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n", ++ ZR_DEVNAME(zr), __func__, i); ++ jpg_fbuffer_free(fh); ++ return -ENOBUFS; ++ } ++ fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem; ++ fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem); ++ ++ if (fh->buffers.need_contiguous) { ++ mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL); ++ if (mem == NULL) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - kmalloc failed for buffer %d\n", ++ ZR_DEVNAME(zr), __func__, i); ++ jpg_fbuffer_free(fh); ++ return -ENOBUFS; ++ } ++ fh->buffers.buffer[i].jpg.frag_tab[0] = ++ cpu_to_le32(virt_to_bus(mem)); ++ fh->buffers.buffer[i].jpg.frag_tab[1] = ++ cpu_to_le32((fh->buffers.buffer_size >> 1) | 1); ++ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) ++ SetPageReserved(virt_to_page(mem + off)); ++ } else { ++ /* jpg_bufsize is already page aligned */ ++ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { ++ mem = (void *)get_zeroed_page(GFP_KERNEL); ++ if (mem == NULL) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - get_zeroed_page failed for buffer %d\n", ++ ZR_DEVNAME(zr), __func__, i); ++ jpg_fbuffer_free(fh); ++ return -ENOBUFS; ++ } ++ ++ fh->buffers.buffer[i].jpg.frag_tab[2 * j] = ++ cpu_to_le32(virt_to_bus(mem)); ++ fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] = ++ cpu_to_le32((PAGE_SIZE >> 2) << 1); ++ SetPageReserved(virt_to_page(mem)); ++ } ++ ++ fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1); ++ } ++ } ++ ++ dprintk(4, ++ KERN_DEBUG "%s: %s - %d KB allocated\n", ++ ZR_DEVNAME(zr), __func__, ++ (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10); ++ ++ fh->buffers.allocated = 1; ++ ++ return 0; ++} ++ ++/* free the MJPEG grab buffers */ ++static void jpg_fbuffer_free(struct zoran_fh *fh) ++{ ++ struct zoran *zr = fh->zr; ++ int i, j, off; ++ unsigned char *mem; ++ __le32 frag_tab; ++ struct zoran_buffer *buffer; ++ ++ dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); ++ ++ for (i = 0, buffer = &fh->buffers.buffer[0]; ++ i < fh->buffers.num_buffers; i++, buffer++) { ++ if (!buffer->jpg.frag_tab) ++ continue; ++ ++ if (fh->buffers.need_contiguous) { ++ frag_tab = buffer->jpg.frag_tab[0]; ++ ++ if (frag_tab) { ++ mem = bus_to_virt(le32_to_cpu(frag_tab)); ++ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) ++ ClearPageReserved(virt_to_page(mem + off)); ++ kfree(mem); ++ buffer->jpg.frag_tab[0] = 0; ++ buffer->jpg.frag_tab[1] = 0; ++ } ++ } else { ++ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { ++ frag_tab = buffer->jpg.frag_tab[2 * j]; ++ ++ if (!frag_tab) ++ break; ++ ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab)))); ++ free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab))); ++ buffer->jpg.frag_tab[2 * j] = 0; ++ buffer->jpg.frag_tab[2 * j + 1] = 0; ++ } ++ } ++ ++ free_page((unsigned long)buffer->jpg.frag_tab); ++ buffer->jpg.frag_tab = NULL; ++ } ++ ++ fh->buffers.allocated = 0; ++} ++ ++/* ++ * V4L Buffer grabbing ++ */ ++ ++static int ++zoran_v4l_set_format (struct zoran_fh *fh, ++ int width, ++ int height, ++ const struct zoran_format *format) ++{ ++ struct zoran *zr = fh->zr; ++ int bpp; ++ ++ /* Check size and format of the grab wanted */ ++ ++ if (height < BUZ_MIN_HEIGHT || width < BUZ_MIN_WIDTH || ++ height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - wrong frame size (%dx%d)\n", ++ ZR_DEVNAME(zr), __func__, width, height); ++ return -EINVAL; ++ } ++ ++ bpp = (format->depth + 7) / 8; ++ ++ /* Check against available buffer size */ ++ if (height * width * bpp > fh->buffers.buffer_size) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - video buffer size (%d kB) is too small\n", ++ ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10); ++ return -EINVAL; ++ } ++ ++ /* The video front end needs 4-byte alinged line sizes */ ++ ++ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - wrong frame alignment\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ fh->v4l_settings.width = width; ++ fh->v4l_settings.height = height; ++ fh->v4l_settings.format = format; ++ fh->v4l_settings.bytesperline = bpp * fh->v4l_settings.width; ++ ++ return 0; ++} ++ ++static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned long flags; ++ int res = 0; ++ ++ if (!fh->buffers.allocated) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - buffers not yet allocated\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -ENOMEM; ++ } ++ ++ /* No grabbing outside the buffer range! */ ++ if (num >= fh->buffers.num_buffers || num < 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - buffer %d is out of range\n", ++ ZR_DEVNAME(zr), __func__, num); ++ res = -EINVAL; ++ } ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ ++ if (fh->buffers.active == ZORAN_FREE) { ++ if (zr->v4l_buffers.active == ZORAN_FREE) { ++ zr->v4l_buffers = fh->buffers; ++ fh->buffers.active = ZORAN_ACTIVE; ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - another session is already capturing\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -EBUSY; ++ } ++ } ++ ++ /* make sure a grab isn't going on currently with this buffer */ ++ if (!res) { ++ switch (zr->v4l_buffers.buffer[num].state) { ++ default: ++ case BUZ_STATE_PEND: ++ if (zr->v4l_buffers.active == ZORAN_FREE) { ++ fh->buffers.active = ZORAN_FREE; ++ zr->v4l_buffers.allocated = 0; ++ } ++ res = -EBUSY; /* what are you doing? */ ++ break; ++ case BUZ_STATE_DONE: ++ dprintk(2, ++ KERN_WARNING ++ "%s: %s - queueing buffer %d in state DONE!?\n", ++ ZR_DEVNAME(zr), __func__, num); ++ case BUZ_STATE_USER: ++ /* since there is at least one unused buffer there's room for at least ++ * one more pend[] entry */ ++ zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num; ++ zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND; ++ zr->v4l_buffers.buffer[num].bs.length = ++ fh->v4l_settings.bytesperline * ++ zr->v4l_settings.height; ++ fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num]; ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ if (!res && zr->v4l_buffers.active == ZORAN_FREE) ++ zr->v4l_buffers.active = fh->buffers.active; ++ ++ return res; ++} ++ ++/* ++ * Sync on a V4L buffer ++ */ ++ ++static int v4l_sync(struct zoran_fh *fh, int frame) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned long flags; ++ ++ if (fh->buffers.active == ZORAN_FREE) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no grab active for this session\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ /* check passed-in frame number */ ++ if (frame >= fh->buffers.num_buffers || frame < 0) { ++ dprintk(1, ++ KERN_ERR "%s: %s - frame %d is invalid\n", ++ ZR_DEVNAME(zr), __func__, frame); ++ return -EINVAL; ++ } ++ ++ /* Check if is buffer was queued at all */ ++ if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - attempt to sync on a buffer which was not queued?\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EPROTO; ++ } ++ ++ /* wait on this buffer to get ready */ ++ if (!wait_event_interruptible_timeout(zr->v4l_capq, ++ (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) ++ return -ETIME; ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ /* buffer should now be in BUZ_STATE_DONE */ ++ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE) ++ dprintk(2, ++ KERN_ERR "%s: %s - internal state error\n", ++ ZR_DEVNAME(zr), __func__); ++ ++ zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER; ++ fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ ++ /* Check if streaming capture has finished */ ++ if (zr->v4l_pend_tail == zr->v4l_pend_head) { ++ zr36057_set_memgrab(zr, 0); ++ if (zr->v4l_buffers.active == ZORAN_ACTIVE) { ++ fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE; ++ zr->v4l_buffers.allocated = 0; ++ } ++ } ++ ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ return 0; ++} ++ ++/* ++ * Queue a MJPEG buffer for capture/playback ++ */ ++ ++static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, ++ enum zoran_codec_mode mode) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned long flags; ++ int res = 0; ++ ++ /* Check if buffers are allocated */ ++ if (!fh->buffers.allocated) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - buffers not yet allocated\n", ++ ZR_DEVNAME(zr), __func__); ++ return -ENOMEM; ++ } ++ ++ /* No grabbing outside the buffer range! */ ++ if (num >= fh->buffers.num_buffers || num < 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - buffer %d out of range\n", ++ ZR_DEVNAME(zr), __func__, num); ++ return -EINVAL; ++ } ++ ++ /* what is the codec mode right now? */ ++ if (zr->codec_mode == BUZ_MODE_IDLE) { ++ zr->jpg_settings = fh->jpg_settings; ++ } else if (zr->codec_mode != mode) { ++ /* wrong codec mode active - invalid */ ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - codec in wrong mode\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ if (fh->buffers.active == ZORAN_FREE) { ++ if (zr->jpg_buffers.active == ZORAN_FREE) { ++ zr->jpg_buffers = fh->buffers; ++ fh->buffers.active = ZORAN_ACTIVE; ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - another session is already capturing\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -EBUSY; ++ } ++ } ++ ++ if (!res && zr->codec_mode == BUZ_MODE_IDLE) { ++ /* Ok load up the jpeg codec */ ++ zr36057_enable_jpg(zr, mode); ++ } ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ ++ if (!res) { ++ switch (zr->jpg_buffers.buffer[num].state) { ++ case BUZ_STATE_DONE: ++ dprintk(2, ++ KERN_WARNING ++ "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", ++ ZR_DEVNAME(zr), __func__); ++ case BUZ_STATE_USER: ++ /* since there is at least one unused buffer there's room for at ++ *least one more pend[] entry */ ++ zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num; ++ zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND; ++ fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num]; ++ zoran_feed_stat_com(zr); ++ break; ++ default: ++ case BUZ_STATE_DMA: ++ case BUZ_STATE_PEND: ++ if (zr->jpg_buffers.active == ZORAN_FREE) { ++ fh->buffers.active = ZORAN_FREE; ++ zr->jpg_buffers.allocated = 0; ++ } ++ res = -EBUSY; /* what are you doing? */ ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ if (!res && zr->jpg_buffers.active == ZORAN_FREE) ++ zr->jpg_buffers.active = fh->buffers.active; ++ ++ return res; ++} ++ ++static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode) ++{ ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ ++ /* Does the user want to stop streaming? */ ++ if (frame < 0) { ++ if (zr->codec_mode == mode) { ++ if (fh->buffers.active == ZORAN_FREE) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(-1) - session not active\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE; ++ zr->jpg_buffers.allocated = 0; ++ zr36057_enable_jpg(zr, BUZ_MODE_IDLE); ++ return 0; ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - stop streaming but not in streaming mode\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ } ++ ++ if ((res = zoran_jpg_queue_frame(fh, frame, mode))) ++ return res; ++ ++ /* Start the jpeg codec when the first frame is queued */ ++ if (!res && zr->jpg_que_head == 1) ++ jpeg_start(zr); ++ ++ return res; ++} ++ ++/* ++ * Sync on a MJPEG buffer ++ */ ++ ++static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned long flags; ++ int frame; ++ ++ if (fh->buffers.active == ZORAN_FREE) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - capture is not currently active\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && ++ zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - codec not in streaming mode\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ if (!wait_event_interruptible_timeout(zr->jpg_capq, ++ (zr->jpg_que_tail != zr->jpg_dma_tail || ++ zr->jpg_dma_tail == zr->jpg_dma_head), ++ 10*HZ)) { ++ int isr; ++ ++ btand(~ZR36057_JMC_Go_en, ZR36057_JMC); ++ udelay(1); ++ zr->codec->control(zr->codec, CODEC_G_STATUS, ++ sizeof(isr), &isr); ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - timeout: codec isr=0x%02x\n", ++ ZR_DEVNAME(zr), __func__, isr); ++ ++ return -ETIME; ++ ++ } ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ ++ if (zr->jpg_dma_tail != zr->jpg_dma_head) ++ frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME]; ++ else ++ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; ++ ++ /* buffer should now be in BUZ_STATE_DONE */ ++ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) ++ dprintk(2, ++ KERN_ERR "%s: %s - internal state error\n", ++ ZR_DEVNAME(zr), __func__); ++ ++ *bs = zr->jpg_buffers.buffer[frame].bs; ++ bs->frame = frame; ++ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER; ++ fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; ++ ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ return 0; ++} ++ ++static void zoran_open_init_session(struct zoran_fh *fh) ++{ ++ int i; ++ struct zoran *zr = fh->zr; ++ ++ /* Per default, map the V4L Buffers */ ++ map_mode_raw(fh); ++ ++ /* take over the card's current settings */ ++ fh->overlay_settings = zr->overlay_settings; ++ fh->overlay_settings.is_set = 0; ++ fh->overlay_settings.format = zr->overlay_settings.format; ++ fh->overlay_active = ZORAN_FREE; ++ ++ /* v4l settings */ ++ fh->v4l_settings = zr->v4l_settings; ++ /* jpg settings */ ++ fh->jpg_settings = zr->jpg_settings; ++ ++ /* buffers */ ++ memset(&fh->buffers, 0, sizeof(fh->buffers)); ++ for (i = 0; i < MAX_FRAME; i++) { ++ fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ ++ fh->buffers.buffer[i].bs.frame = i; ++ } ++ fh->buffers.allocated = 0; ++ fh->buffers.active = ZORAN_FREE; ++} ++ ++static void zoran_close_end_session(struct zoran_fh *fh) ++{ ++ struct zoran *zr = fh->zr; ++ ++ /* overlay */ ++ if (fh->overlay_active != ZORAN_FREE) { ++ fh->overlay_active = zr->overlay_active = ZORAN_FREE; ++ zr->v4l_overlay_active = 0; ++ if (!zr->v4l_memgrab_active) ++ zr36057_overlay(zr, 0); ++ zr->overlay_mask = NULL; ++ } ++ ++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) { ++ /* v4l capture */ ++ if (fh->buffers.active != ZORAN_FREE) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ zr36057_set_memgrab(zr, 0); ++ zr->v4l_buffers.allocated = 0; ++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ } ++ ++ /* v4l buffers */ ++ if (fh->buffers.allocated) ++ v4l_fbuffer_free(fh); ++ } else { ++ /* jpg capture */ ++ if (fh->buffers.active != ZORAN_FREE) { ++ zr36057_enable_jpg(zr, BUZ_MODE_IDLE); ++ zr->jpg_buffers.allocated = 0; ++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; ++ } ++ ++ /* jpg buffers */ ++ if (fh->buffers.allocated) ++ jpg_fbuffer_free(fh); ++ } ++} ++ ++/* ++ * Open a zoran card. Right now the flags stuff is just playing ++ */ ++ ++static int zoran_open(struct file *file) ++{ ++ struct zoran *zr = video_drvdata(file); ++ struct zoran_fh *fh; ++ int res, first_open = 0; ++ ++ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", ++ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); ++ ++ mutex_lock(&zr->other_lock); ++ ++ if (zr->user >= 2048) { ++ dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", ++ ZR_DEVNAME(zr), zr->user); ++ res = -EBUSY; ++ goto fail_unlock; ++ } ++ ++ /* now, create the open()-specific file_ops struct */ ++ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); ++ if (!fh) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - allocation of zoran_fh failed\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -ENOMEM; ++ goto fail_unlock; ++ } ++ /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows ++ * on norm-change! */ ++ fh->overlay_mask = ++ kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL); ++ if (!fh->overlay_mask) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - allocation of overlay_mask failed\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -ENOMEM; ++ goto fail_fh; ++ } ++ ++ if (zr->user++ == 0) ++ first_open = 1; ++ ++ /*mutex_unlock(&zr->resource_lock);*/ ++ ++ /* default setup - TODO: look at flags */ ++ if (first_open) { /* First device open */ ++ zr36057_restart(zr); ++ zoran_open_init_params(zr); ++ zoran_init_hardware(zr); ++ ++ btor(ZR36057_ICR_IntPinEn, ZR36057_ICR); ++ } ++ ++ /* set file_ops stuff */ ++ file->private_data = fh; ++ fh->zr = zr; ++ zoran_open_init_session(fh); ++ mutex_unlock(&zr->other_lock); ++ ++ return 0; ++ ++fail_fh: ++ kfree(fh); ++fail_unlock: ++ mutex_unlock(&zr->other_lock); ++ ++ dprintk(2, KERN_INFO "%s: open failed (%d), users(-)=%d\n", ++ ZR_DEVNAME(zr), res, zr->user); ++ ++ return res; ++} ++ ++static int ++zoran_close(struct file *file) ++{ ++ struct zoran_fh *fh = file->private_data; ++ struct zoran *zr = fh->zr; ++ ++ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n", ++ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1); ++ ++ /* kernel locks (fs/device.c), so don't do that ourselves ++ * (prevents deadlocks) */ ++ mutex_lock(&zr->other_lock); ++ ++ zoran_close_end_session(fh); ++ ++ if (zr->user-- == 1) { /* Last process */ ++ /* Clean up JPEG process */ ++ wake_up_interruptible(&zr->jpg_capq); ++ zr36057_enable_jpg(zr, BUZ_MODE_IDLE); ++ zr->jpg_buffers.allocated = 0; ++ zr->jpg_buffers.active = ZORAN_FREE; ++ ++ /* disable interrupts */ ++ btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR); ++ ++ if (zr36067_debug > 1) ++ print_interrupts(zr); ++ ++ /* Overlay off */ ++ zr->v4l_overlay_active = 0; ++ zr36057_overlay(zr, 0); ++ zr->overlay_mask = NULL; ++ ++ /* capture off */ ++ wake_up_interruptible(&zr->v4l_capq); ++ zr36057_set_memgrab(zr, 0); ++ zr->v4l_buffers.allocated = 0; ++ zr->v4l_buffers.active = ZORAN_FREE; ++ zoran_set_pci_master(zr, 0); ++ ++ if (!pass_through) { /* Switch to color bar */ ++ decoder_call(zr, video, s_stream, 0); ++ encoder_call(zr, video, s_routing, 2, 0, 0); ++ } ++ } ++ mutex_unlock(&zr->other_lock); ++ ++ file->private_data = NULL; ++ kfree(fh->overlay_mask); ++ kfree(fh); ++ ++ dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__); ++ ++ return 0; ++} ++ ++ ++static ssize_t ++zoran_read (struct file *file, ++ char __user *data, ++ size_t count, ++ loff_t *ppos) ++{ ++ /* we simply don't support read() (yet)... */ ++ ++ return -EINVAL; ++} ++ ++static ssize_t ++zoran_write (struct file *file, ++ const char __user *data, ++ size_t count, ++ loff_t *ppos) ++{ ++ /* ...and the same goes for write() */ ++ ++ return -EINVAL; ++} ++ ++static int setup_fbuffer(struct zoran_fh *fh, ++ void *base, ++ const struct zoran_format *fmt, ++ int width, ++ int height, ++ int bytesperline) ++{ ++ struct zoran *zr = fh->zr; ++ ++ /* (Ronald) v4l/v4l2 guidelines */ ++ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on ++ ALi Magik (that needs very low latency while the card needs a ++ higher value always) */ ++ ++ if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK)) ++ return -ENXIO; ++ ++ /* we need a bytesperline value, even if not given */ ++ if (!bytesperline) ++ bytesperline = width * ((fmt->depth + 7) & ~7) / 8; ++ ++#if 0 ++ if (zr->overlay_active) { ++ /* dzjee... stupid users... don't even bother to turn off ++ * overlay before changing the memory location... ++ * normally, we would return errors here. However, one of ++ * the tools that does this is... xawtv! and since xawtv ++ * is used by +/- 99% of the users, we'd rather be user- ++ * friendly and silently do as if nothing went wrong */ ++ dprintk(3, ++ KERN_ERR ++ "%s: %s - forced overlay turnoff because framebuffer changed\n", ++ ZR_DEVNAME(zr), __func__); ++ zr36057_overlay(zr, 0); ++ } ++#endif ++ ++ if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no valid overlay format given\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ if (height <= 0 || width <= 0 || bytesperline <= 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n", ++ ZR_DEVNAME(zr), __func__, width, height, bytesperline); ++ return -EINVAL; ++ } ++ if (bytesperline & 3) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - bytesperline (%d) must be 4-byte aligned\n", ++ ZR_DEVNAME(zr), __func__, bytesperline); ++ return -EINVAL; ++ } ++ ++ zr->vbuf_base = (void *) ((unsigned long) base & ~3); ++ zr->vbuf_height = height; ++ zr->vbuf_width = width; ++ zr->vbuf_depth = fmt->depth; ++ zr->overlay_settings.format = fmt; ++ zr->vbuf_bytesperline = bytesperline; ++ ++ /* The user should set new window parameters */ ++ zr->overlay_settings.is_set = 0; ++ ++ return 0; ++} ++ ++ ++static int setup_window(struct zoran_fh *fh, ++ int x, ++ int y, ++ int width, ++ int height, ++ struct v4l2_clip __user *clips, ++ unsigned int clipcount, ++ void __user *bitmap) ++{ ++ struct zoran *zr = fh->zr; ++ struct v4l2_clip *vcp = NULL; ++ int on, end; ++ ++ ++ if (!zr->vbuf_base) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - frame buffer has to be set first\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ if (!fh->overlay_settings.format) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no overlay format set\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ if (clipcount > 2048) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - invalid clipcount\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * The video front end needs 4-byte alinged line sizes, we correct that ++ * silently here if necessary ++ */ ++ if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) { ++ end = (x + width) & ~1; /* round down */ ++ x = (x + 1) & ~1; /* round up */ ++ width = end - x; ++ } ++ ++ if (zr->vbuf_depth == 24) { ++ end = (x + width) & ~3; /* round down */ ++ x = (x + 3) & ~3; /* round up */ ++ width = end - x; ++ } ++ ++ if (width > BUZ_MAX_WIDTH) ++ width = BUZ_MAX_WIDTH; ++ if (height > BUZ_MAX_HEIGHT) ++ height = BUZ_MAX_HEIGHT; ++ ++ /* Check for invalid parameters */ ++ if (width < BUZ_MIN_WIDTH || height < BUZ_MIN_HEIGHT || ++ width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - width = %d or height = %d invalid\n", ++ ZR_DEVNAME(zr), __func__, width, height); ++ return -EINVAL; ++ } ++ ++ fh->overlay_settings.x = x; ++ fh->overlay_settings.y = y; ++ fh->overlay_settings.width = width; ++ fh->overlay_settings.height = height; ++ fh->overlay_settings.clipcount = clipcount; ++ ++ /* ++ * If an overlay is running, we have to switch it off ++ * and switch it on again in order to get the new settings in effect. ++ * ++ * We also want to avoid that the overlay mask is written ++ * when an overlay is running. ++ */ ++ ++ on = zr->v4l_overlay_active && !zr->v4l_memgrab_active && ++ zr->overlay_active != ZORAN_FREE && ++ fh->overlay_active != ZORAN_FREE; ++ if (on) ++ zr36057_overlay(zr, 0); ++ ++ /* ++ * Write the overlay mask if clips are wanted. ++ * We prefer a bitmap. ++ */ ++ if (bitmap) { ++ /* fake value - it just means we want clips */ ++ fh->overlay_settings.clipcount = 1; ++ ++ if (copy_from_user(fh->overlay_mask, bitmap, ++ (width * height + 7) / 8)) { ++ return -EFAULT; ++ } ++ } else if (clipcount) { ++ /* write our own bitmap from the clips */ ++ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); ++ if (vcp == NULL) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - Alloc of clip mask failed\n", ++ ZR_DEVNAME(zr), __func__); ++ return -ENOMEM; ++ } ++ if (copy_from_user ++ (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) { ++ vfree(vcp); ++ return -EFAULT; ++ } ++ write_overlay_mask(fh, vcp, clipcount); ++ vfree(vcp); ++ } ++ ++ fh->overlay_settings.is_set = 1; ++ if (fh->overlay_active != ZORAN_FREE && ++ zr->overlay_active != ZORAN_FREE) ++ zr->overlay_settings = fh->overlay_settings; ++ ++ if (on) ++ zr36057_overlay(zr, 1); ++ ++ /* Make sure the changes come into effect */ ++ return wait_grab_pending(zr); ++} ++ ++static int setup_overlay(struct zoran_fh *fh, int on) ++{ ++ struct zoran *zr = fh->zr; ++ ++ /* If there is nothing to do, return immediately */ ++ if ((on && fh->overlay_active != ZORAN_FREE) || ++ (!on && fh->overlay_active == ZORAN_FREE)) ++ return 0; ++ ++ /* check whether we're touching someone else's overlay */ ++ if (on && zr->overlay_active != ZORAN_FREE && ++ fh->overlay_active == ZORAN_FREE) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - overlay is already active for another session\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EBUSY; ++ } ++ if (!on && zr->overlay_active != ZORAN_FREE && ++ fh->overlay_active == ZORAN_FREE) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - you cannot cancel someone else's session\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EPERM; ++ } ++ ++ if (on == 0) { ++ zr->overlay_active = fh->overlay_active = ZORAN_FREE; ++ zr->v4l_overlay_active = 0; ++ /* When a grab is running, the video simply ++ * won't be switched on any more */ ++ if (!zr->v4l_memgrab_active) ++ zr36057_overlay(zr, 0); ++ zr->overlay_mask = NULL; ++ } else { ++ if (!zr->vbuf_base || !fh->overlay_settings.is_set) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - buffer or window not set\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ if (!fh->overlay_settings.format) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no overlay format set\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ zr->overlay_active = fh->overlay_active = ZORAN_LOCKED; ++ zr->v4l_overlay_active = 1; ++ zr->overlay_mask = fh->overlay_mask; ++ zr->overlay_settings = fh->overlay_settings; ++ if (!zr->v4l_memgrab_active) ++ zr36057_overlay(zr, 1); ++ /* When a grab is running, the video will be ++ * switched on when grab is finished */ ++ } ++ ++ /* Make sure the changes come into effect */ ++ return wait_grab_pending(zr); ++} ++ ++/* get the status of a buffer in the clients buffer queue */ ++static int zoran_v4l2_buffer_status(struct zoran_fh *fh, ++ struct v4l2_buffer *buf, int num) ++{ ++ struct zoran *zr = fh->zr; ++ unsigned long flags; ++ ++ buf->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: ++ /* check range */ ++ if (num < 0 || num >= fh->buffers.num_buffers || ++ !fh->buffers.allocated) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - wrong number or buffers not allocated\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ dprintk(3, ++ KERN_DEBUG ++ "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n", ++ ZR_DEVNAME(zr), __func__, ++ "FAL"[fh->buffers.active], num, ++ "UPMD"[zr->v4l_buffers.buffer[num].state], ++ fh->buffers.buffer[num].map ? 'Y' : 'N'); ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ buf->length = fh->buffers.buffer_size; ++ ++ /* get buffer */ ++ buf->bytesused = fh->buffers.buffer[num].bs.length; ++ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || ++ fh->buffers.buffer[num].state == BUZ_STATE_USER) { ++ buf->sequence = fh->buffers.buffer[num].bs.seq; ++ buf->flags |= V4L2_BUF_FLAG_DONE; ++ buf->timestamp = fh->buffers.buffer[num].bs.timestamp; ++ } else { ++ buf->flags |= V4L2_BUF_FLAG_QUEUED; ++ } ++ ++ if (fh->v4l_settings.height <= BUZ_MAX_HEIGHT / 2) ++ buf->field = V4L2_FIELD_TOP; ++ else ++ buf->field = V4L2_FIELD_INTERLACED; ++ ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ ++ /* check range */ ++ if (num < 0 || num >= fh->buffers.num_buffers || ++ !fh->buffers.allocated) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - wrong number or buffers not allocated\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? ++ V4L2_BUF_TYPE_VIDEO_CAPTURE : ++ V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ buf->length = fh->buffers.buffer_size; ++ ++ /* these variables are only written after frame has been captured */ ++ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || ++ fh->buffers.buffer[num].state == BUZ_STATE_USER) { ++ buf->sequence = fh->buffers.buffer[num].bs.seq; ++ buf->timestamp = fh->buffers.buffer[num].bs.timestamp; ++ buf->bytesused = fh->buffers.buffer[num].bs.length; ++ buf->flags |= V4L2_BUF_FLAG_DONE; ++ } else { ++ buf->flags |= V4L2_BUF_FLAG_QUEUED; ++ } ++ ++ /* which fields are these? */ ++ if (fh->jpg_settings.TmpDcm != 1) ++ buf->field = fh->jpg_settings.odd_even ? ++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; ++ else ++ buf->field = fh->jpg_settings.odd_even ? ++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; ++ ++ break; ++ ++ default: ++ ++ dprintk(5, ++ KERN_ERR ++ "%s: %s - invalid buffer type|map_mode (%d|%d)\n", ++ ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode); ++ return -EINVAL; ++ } ++ ++ buf->memory = V4L2_MEMORY_MMAP; ++ buf->index = num; ++ buf->m.offset = buf->length * num; ++ ++ return 0; ++} ++ ++static int ++zoran_set_norm (struct zoran *zr, ++ v4l2_std_id norm) ++{ ++ int on; ++ ++ if (zr->v4l_buffers.active != ZORAN_FREE || ++ zr->jpg_buffers.active != ZORAN_FREE) { ++ dprintk(1, ++ KERN_WARNING ++ "%s: %s called while in playback/capture mode\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EBUSY; ++ } ++ ++ if (!(norm & zr->card.norms)) { ++ dprintk(1, ++ KERN_ERR "%s: %s - unsupported norm %llx\n", ++ ZR_DEVNAME(zr), __func__, norm); ++ return -EINVAL; ++ } ++ ++ if (norm == V4L2_STD_ALL) { ++ unsigned int status = 0; ++ v4l2_std_id std = 0; ++ ++ decoder_call(zr, video, querystd, &std); ++ decoder_call(zr, core, s_std, std); ++ ++ /* let changes come into effect */ ++ ssleep(2); ++ ++ decoder_call(zr, video, g_input_status, &status); ++ if (status & V4L2_IN_ST_NO_SIGNAL) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no norm detected\n", ++ ZR_DEVNAME(zr), __func__); ++ /* reset norm */ ++ decoder_call(zr, core, s_std, zr->norm); ++ return -EIO; ++ } ++ ++ norm = std; ++ } ++ if (norm & V4L2_STD_SECAM) ++ zr->timing = zr->card.tvn[2]; ++ else if (norm & V4L2_STD_NTSC) ++ zr->timing = zr->card.tvn[1]; ++ else ++ zr->timing = zr->card.tvn[0]; ++ ++ /* We switch overlay off and on since a change in the ++ * norm needs different VFE settings */ ++ on = zr->overlay_active && !zr->v4l_memgrab_active; ++ if (on) ++ zr36057_overlay(zr, 0); ++ ++ decoder_call(zr, core, s_std, norm); ++ encoder_call(zr, video, s_std_output, norm); ++ ++ if (on) ++ zr36057_overlay(zr, 1); ++ ++ /* Make sure the changes come into effect */ ++ zr->norm = norm; ++ ++ return 0; ++} ++ ++static int ++zoran_set_input (struct zoran *zr, ++ int input) ++{ ++ if (input == zr->input) { ++ return 0; ++ } ++ ++ if (zr->v4l_buffers.active != ZORAN_FREE || ++ zr->jpg_buffers.active != ZORAN_FREE) { ++ dprintk(1, ++ KERN_WARNING ++ "%s: %s called while in playback/capture mode\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EBUSY; ++ } ++ ++ if (input < 0 || input >= zr->card.inputs) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - unnsupported input %d\n", ++ ZR_DEVNAME(zr), __func__, input); ++ return -EINVAL; ++ } ++ ++ zr->input = input; ++ ++ decoder_call(zr, video, s_routing, ++ zr->card.input[input].muxsel, 0, 0); ++ ++ return 0; ++} ++ ++/* ++ * ioctl routine ++ */ ++ ++static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ memset(cap, 0, sizeof(*cap)); ++ strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); ++ strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", ++ pci_name(zr->pci_dev)); ++ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY; ++ return 0; ++} ++ ++static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) ++{ ++ unsigned int num, i; ++ ++ for (num = i = 0; i < NUM_FORMATS; i++) { ++ if (zoran_formats[i].flags & flag && num++ == fmt->index) { ++ strncpy(fmt->description, zoran_formats[i].name, ++ sizeof(fmt->description) - 1); ++ /* fmt struct pre-zeroed, so adding '\0' not needed */ ++ fmt->pixelformat = zoran_formats[i].fourcc; ++ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) ++ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, ++ struct v4l2_fmtdesc *f) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); ++} ++ ++static int zoran_enum_fmt_vid_out(struct file *file, void *__fh, ++ struct v4l2_fmtdesc *f) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK); ++} ++ ++static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh, ++ struct v4l2_fmtdesc *f) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY); ++} ++ ++static int zoran_g_fmt_vid_out(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm; ++ fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 / ++ (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm); ++ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings); ++ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; ++ if (fh->jpg_settings.TmpDcm == 1) ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); ++ else ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); ++ fmt->fmt.pix.bytesperline = 0; ++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ if (fh->map_mode != ZORAN_MAP_MODE_RAW) ++ return zoran_g_fmt_vid_out(file, fh, fmt); ++ ++ mutex_lock(&zr->resource_lock); ++ fmt->fmt.pix.width = fh->v4l_settings.width; ++ fmt->fmt.pix.height = fh->v4l_settings.height; ++ fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline * ++ fh->v4l_settings.height; ++ fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc; ++ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; ++ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; ++ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) ++ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; ++ else ++ fmt->fmt.pix.field = V4L2_FIELD_TOP; ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ fmt->fmt.win.w.left = fh->overlay_settings.x; ++ fmt->fmt.win.w.top = fh->overlay_settings.y; ++ fmt->fmt.win.w.width = fh->overlay_settings.width; ++ fmt->fmt.win.w.height = fh->overlay_settings.height; ++ if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT) ++ fmt->fmt.win.field = V4L2_FIELD_INTERLACED; ++ else ++ fmt->fmt.win.field = V4L2_FIELD_TOP; ++ ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) ++ fmt->fmt.win.w.width = BUZ_MAX_WIDTH; ++ if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) ++ fmt->fmt.win.w.width = BUZ_MIN_WIDTH; ++ if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT) ++ fmt->fmt.win.w.height = BUZ_MAX_HEIGHT; ++ if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) ++ fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; ++ ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_try_fmt_vid_out(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ struct zoran_jpg_settings settings; ++ int res = 0; ++ ++ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) ++ return -EINVAL; ++ ++ mutex_lock(&zr->resource_lock); ++ settings = fh->jpg_settings; ++ ++ /* we actually need to set 'real' parameters now */ ++ if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) ++ settings.TmpDcm = 1; ++ else ++ settings.TmpDcm = 2; ++ settings.decimation = 0; ++ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) ++ settings.VerDcm = 2; ++ else ++ settings.VerDcm = 1; ++ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) ++ settings.HorDcm = 4; ++ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) ++ settings.HorDcm = 2; ++ else ++ settings.HorDcm = 1; ++ if (settings.TmpDcm == 1) ++ settings.field_per_buff = 2; ++ else ++ settings.field_per_buff = 1; ++ ++ if (settings.HorDcm > 1) { ++ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; ++ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; ++ } else { ++ settings.img_x = 0; ++ settings.img_width = BUZ_MAX_WIDTH; ++ } ++ ++ /* check */ ++ res = zoran_check_jpg_settings(zr, &settings, 1); ++ if (res) ++ goto tryfmt_unlock_and_return; ++ ++ /* tell the user what we actually did */ ++ fmt->fmt.pix.width = settings.img_width / settings.HorDcm; ++ fmt->fmt.pix.height = settings.img_height * 2 / ++ (settings.TmpDcm * settings.VerDcm); ++ if (settings.TmpDcm == 1) ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); ++ else ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); ++ ++ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); ++ fmt->fmt.pix.bytesperline = 0; ++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++tryfmt_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int bpp; ++ int i; ++ ++ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) ++ return zoran_try_fmt_vid_out(file, fh, fmt); ++ ++ mutex_lock(&zr->resource_lock); ++ ++ for (i = 0; i < NUM_FORMATS; i++) ++ if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) ++ break; ++ ++ if (i == NUM_FORMATS) { ++ mutex_unlock(&zr->resource_lock); ++ return -EINVAL; ++ } ++ ++ bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); ++ v4l_bound_align_image( ++ &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, ++ &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); ++ mutex_unlock(&zr->resource_lock); ++ ++ return 0; ++} ++ ++static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res; ++ ++ dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", ++ fmt->fmt.win.w.left, fmt->fmt.win.w.top, ++ fmt->fmt.win.w.width, ++ fmt->fmt.win.w.height, ++ fmt->fmt.win.clipcount, ++ fmt->fmt.win.bitmap); ++ mutex_lock(&zr->resource_lock); ++ res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, ++ fmt->fmt.win.w.width, fmt->fmt.win.w.height, ++ (struct v4l2_clip __user *)fmt->fmt.win.clips, ++ fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_s_fmt_vid_out(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); ++ struct zoran_jpg_settings settings; ++ int res = 0; ++ ++ dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n", ++ fmt->fmt.pix.width, fmt->fmt.pix.height, ++ fmt->fmt.pix.pixelformat, ++ (char *) &printformat); ++ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) ++ return -EINVAL; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (fh->buffers.allocated) { ++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ++ ZR_DEVNAME(zr)); ++ res = -EBUSY; ++ goto sfmtjpg_unlock_and_return; ++ } ++ ++ settings = fh->jpg_settings; ++ ++ /* we actually need to set 'real' parameters now */ ++ if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) ++ settings.TmpDcm = 1; ++ else ++ settings.TmpDcm = 2; ++ settings.decimation = 0; ++ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) ++ settings.VerDcm = 2; ++ else ++ settings.VerDcm = 1; ++ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) ++ settings.HorDcm = 4; ++ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) ++ settings.HorDcm = 2; ++ else ++ settings.HorDcm = 1; ++ if (settings.TmpDcm == 1) ++ settings.field_per_buff = 2; ++ else ++ settings.field_per_buff = 1; ++ ++ if (settings.HorDcm > 1) { ++ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; ++ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; ++ } else { ++ settings.img_x = 0; ++ settings.img_width = BUZ_MAX_WIDTH; ++ } ++ ++ /* check */ ++ res = zoran_check_jpg_settings(zr, &settings, 0); ++ if (res) ++ goto sfmtjpg_unlock_and_return; ++ ++ /* it's ok, so set them */ ++ fh->jpg_settings = settings; ++ ++ map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); ++ ++ /* tell the user what we actually did */ ++ fmt->fmt.pix.width = settings.img_width / settings.HorDcm; ++ fmt->fmt.pix.height = settings.img_height * 2 / ++ (settings.TmpDcm * settings.VerDcm); ++ if (settings.TmpDcm == 1) ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); ++ else ++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? ++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); ++ fmt->fmt.pix.bytesperline = 0; ++ fmt->fmt.pix.sizeimage = fh->buffers.buffer_size; ++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++sfmtjpg_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, ++ struct v4l2_format *fmt) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int i; ++ int res = 0; ++ ++ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) ++ return zoran_s_fmt_vid_out(file, fh, fmt); ++ ++ for (i = 0; i < NUM_FORMATS; i++) ++ if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) ++ break; ++ if (i == NUM_FORMATS) { ++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", ++ ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || ++ fh->buffers.active != ZORAN_FREE) { ++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", ++ ZR_DEVNAME(zr)); ++ res = -EBUSY; ++ goto sfmtv4l_unlock_and_return; ++ } ++ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) ++ fmt->fmt.pix.height = BUZ_MAX_HEIGHT; ++ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) ++ fmt->fmt.pix.width = BUZ_MAX_WIDTH; ++ ++ map_mode_raw(fh); ++ ++ res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, ++ &zoran_formats[i]); ++ if (res) ++ goto sfmtv4l_unlock_and_return; ++ ++ /* tell the user the results/missing stuff */ ++ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; ++ fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline; ++ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; ++ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) ++ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; ++ else ++ fmt->fmt.pix.field = V4L2_FIELD_TOP; ++ ++sfmtv4l_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_g_fbuf(struct file *file, void *__fh, ++ struct v4l2_framebuffer *fb) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ memset(fb, 0, sizeof(*fb)); ++ mutex_lock(&zr->resource_lock); ++ fb->base = zr->vbuf_base; ++ fb->fmt.width = zr->vbuf_width; ++ fb->fmt.height = zr->vbuf_height; ++ if (zr->overlay_settings.format) ++ fb->fmt.pixelformat = fh->overlay_settings.format->fourcc; ++ fb->fmt.bytesperline = zr->vbuf_bytesperline; ++ mutex_unlock(&zr->resource_lock); ++ fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; ++ fb->fmt.field = V4L2_FIELD_INTERLACED; ++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; ++ ++ return 0; ++} ++ ++static int zoran_s_fbuf(struct file *file, void *__fh, ++ const struct v4l2_framebuffer *fb) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int i, res = 0; ++ __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat); ++ ++ for (i = 0; i < NUM_FORMATS; i++) ++ if (zoran_formats[i].fourcc == fb->fmt.pixelformat) ++ break; ++ if (i == NUM_FORMATS) { ++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n", ++ ZR_DEVNAME(zr), fb->fmt.pixelformat, ++ (char *)&printformat); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&zr->resource_lock); ++ res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, ++ fb->fmt.height, fb->fmt.bytesperline); ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_overlay(struct file *file, void *__fh, unsigned int on) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res; ++ ++ mutex_lock(&zr->resource_lock); ++ res = setup_overlay(fh, on); ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type); ++ ++static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ ++ if (req->memory != V4L2_MEMORY_MMAP) { ++ dprintk(2, ++ KERN_ERR ++ "%s: only MEMORY_MMAP capture is supported, not %d\n", ++ ZR_DEVNAME(zr), req->memory); ++ return -EINVAL; ++ } ++ ++ if (req->count == 0) ++ return zoran_streamoff(file, fh, req->type); ++ ++ mutex_lock(&zr->resource_lock); ++ if (fh->buffers.allocated) { ++ dprintk(2, ++ KERN_ERR ++ "%s: VIDIOC_REQBUFS - buffers already allocated\n", ++ ZR_DEVNAME(zr)); ++ res = -EBUSY; ++ goto v4l2reqbuf_unlock_and_return; ++ } ++ ++ if (fh->map_mode == ZORAN_MAP_MODE_RAW && ++ req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ /* control user input */ ++ if (req->count < 2) ++ req->count = 2; ++ if (req->count > v4l_nbufs) ++ req->count = v4l_nbufs; ++ ++ /* The next mmap will map the V4L buffers */ ++ map_mode_raw(fh); ++ fh->buffers.num_buffers = req->count; ++ ++ if (v4l_fbuffer_alloc(fh)) { ++ res = -ENOMEM; ++ goto v4l2reqbuf_unlock_and_return; ++ } ++ } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || ++ fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { ++ /* we need to calculate size ourselves now */ ++ if (req->count < 4) ++ req->count = 4; ++ if (req->count > jpg_nbufs) ++ req->count = jpg_nbufs; ++ ++ /* The next mmap will map the MJPEG buffers */ ++ map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ fh->buffers.num_buffers = req->count; ++ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); ++ ++ if (jpg_fbuffer_alloc(fh)) { ++ res = -ENOMEM; ++ goto v4l2reqbuf_unlock_and_return; ++ } ++ } else { ++ dprintk(1, ++ KERN_ERR ++ "%s: VIDIOC_REQBUFS - unknown type %d\n", ++ ZR_DEVNAME(zr), req->type); ++ res = -EINVAL; ++ goto v4l2reqbuf_unlock_and_return; ++ } ++v4l2reqbuf_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res; ++ ++ mutex_lock(&zr->resource_lock); ++ res = zoran_v4l2_buffer_status(fh, buf, buf->index); ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0, codec_mode, buf_type; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: ++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ++ ZR_DEVNAME(zr), buf->type, fh->map_mode); ++ res = -EINVAL; ++ goto qbuf_unlock_and_return; ++ } ++ ++ res = zoran_v4l_queue_frame(fh, buf->index); ++ if (res) ++ goto qbuf_unlock_and_return; ++ if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) ++ zr36057_set_memgrab(zr, 1); ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { ++ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ codec_mode = BUZ_MODE_MOTION_DECOMPRESS; ++ } else { ++ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ codec_mode = BUZ_MODE_MOTION_COMPRESS; ++ } ++ ++ if (buf->type != buf_type) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ++ ZR_DEVNAME(zr), buf->type, fh->map_mode); ++ res = -EINVAL; ++ goto qbuf_unlock_and_return; ++ } ++ ++ res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); ++ if (res != 0) ++ goto qbuf_unlock_and_return; ++ if (zr->codec_mode == BUZ_MODE_IDLE && ++ fh->buffers.active == ZORAN_LOCKED) ++ zr36057_enable_jpg(zr, codec_mode); ++ ++ break; ++ ++ default: ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_QBUF - unsupported type %d\n", ++ ZR_DEVNAME(zr), buf->type); ++ res = -EINVAL; ++ break; ++ } ++qbuf_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0, buf_type, num = -1; /* compiler borks here (?) */ ++ ++ mutex_lock(&zr->resource_lock); ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: ++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ++ ZR_DEVNAME(zr), buf->type, fh->map_mode); ++ res = -EINVAL; ++ goto dqbuf_unlock_and_return; ++ } ++ ++ num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; ++ if (file->f_flags & O_NONBLOCK && ++ zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) { ++ res = -EAGAIN; ++ goto dqbuf_unlock_and_return; ++ } ++ res = v4l_sync(fh, num); ++ if (res) ++ goto dqbuf_unlock_and_return; ++ zr->v4l_sync_tail++; ++ res = zoran_v4l2_buffer_status(fh, buf, num); ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ { ++ struct zoran_sync bs; ++ ++ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) ++ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ else ++ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ++ if (buf->type != buf_type) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", ++ ZR_DEVNAME(zr), buf->type, fh->map_mode); ++ res = -EINVAL; ++ goto dqbuf_unlock_and_return; ++ } ++ ++ num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; ++ ++ if (file->f_flags & O_NONBLOCK && ++ zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) { ++ res = -EAGAIN; ++ goto dqbuf_unlock_and_return; ++ } ++ bs.frame = 0; /* suppress compiler warning */ ++ res = jpg_sync(fh, &bs); ++ if (res) ++ goto dqbuf_unlock_and_return; ++ res = zoran_v4l2_buffer_status(fh, buf, bs.frame); ++ break; ++ } ++ ++ default: ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_DQBUF - unsupported type %d\n", ++ ZR_DEVNAME(zr), buf->type); ++ res = -EINVAL; ++ break; ++ } ++dqbuf_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: /* raw capture */ ++ if (zr->v4l_buffers.active != ZORAN_ACTIVE || ++ fh->buffers.active != ZORAN_ACTIVE) { ++ res = -EBUSY; ++ goto strmon_unlock_and_return; ++ } ++ ++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; ++ zr->v4l_settings = fh->v4l_settings; ++ ++ zr->v4l_sync_tail = zr->v4l_pend_tail; ++ if (!zr->v4l_memgrab_active && ++ zr->v4l_pend_head != zr->v4l_pend_tail) { ++ zr36057_set_memgrab(zr, 1); ++ } ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ /* what is the codec mode right now? */ ++ if (zr->jpg_buffers.active != ZORAN_ACTIVE || ++ fh->buffers.active != ZORAN_ACTIVE) { ++ res = -EBUSY; ++ goto strmon_unlock_and_return; ++ } ++ ++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; ++ ++ if (zr->jpg_que_head != zr->jpg_que_tail) { ++ /* Start the jpeg codec when the first frame is queued */ ++ jpeg_start(zr); ++ } ++ break; ++ ++ default: ++ dprintk(1, ++ KERN_ERR ++ "%s: VIDIOC_STREAMON - invalid map mode %d\n", ++ ZR_DEVNAME(zr), fh->map_mode); ++ res = -EINVAL; ++ break; ++ } ++strmon_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int i, res = 0; ++ unsigned long flags; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: /* raw capture */ ++ if (fh->buffers.active == ZORAN_FREE && ++ zr->v4l_buffers.active != ZORAN_FREE) { ++ res = -EPERM; /* stay off other's settings! */ ++ goto strmoff_unlock_and_return; ++ } ++ if (zr->v4l_buffers.active == ZORAN_FREE) ++ goto strmoff_unlock_and_return; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ /* unload capture */ ++ if (zr->v4l_memgrab_active) { ++ ++ zr36057_set_memgrab(zr, 0); ++ } ++ ++ for (i = 0; i < fh->buffers.num_buffers; i++) ++ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; ++ fh->buffers = zr->v4l_buffers; ++ ++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; ++ ++ zr->v4l_grab_seq = 0; ++ zr->v4l_pend_head = zr->v4l_pend_tail = 0; ++ zr->v4l_sync_tail = 0; ++ ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ if (fh->buffers.active == ZORAN_FREE && ++ zr->jpg_buffers.active != ZORAN_FREE) { ++ res = -EPERM; /* stay off other's settings! */ ++ goto strmoff_unlock_and_return; ++ } ++ if (zr->jpg_buffers.active == ZORAN_FREE) ++ goto strmoff_unlock_and_return; ++ ++ res = jpg_qbuf(fh, -1, ++ (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? ++ BUZ_MODE_MOTION_COMPRESS : ++ BUZ_MODE_MOTION_DECOMPRESS); ++ if (res) ++ goto strmoff_unlock_and_return; ++ break; ++ default: ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_STREAMOFF - invalid map mode %d\n", ++ ZR_DEVNAME(zr), fh->map_mode); ++ res = -EINVAL; ++ break; ++ } ++strmoff_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_queryctrl(struct file *file, void *__fh, ++ struct v4l2_queryctrl *ctrl) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ /* we only support hue/saturation/contrast/brightness */ ++ if (ctrl->id < V4L2_CID_BRIGHTNESS || ++ ctrl->id > V4L2_CID_HUE) ++ return -EINVAL; ++ ++ decoder_call(zr, core, queryctrl, ctrl); ++ ++ return 0; ++} ++ ++static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ /* we only support hue/saturation/contrast/brightness */ ++ if (ctrl->id < V4L2_CID_BRIGHTNESS || ++ ctrl->id > V4L2_CID_HUE) ++ return -EINVAL; ++ ++ mutex_lock(&zr->resource_lock); ++ decoder_call(zr, core, g_ctrl, ctrl); ++ mutex_unlock(&zr->resource_lock); ++ ++ return 0; ++} ++ ++static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ /* we only support hue/saturation/contrast/brightness */ ++ if (ctrl->id < V4L2_CID_BRIGHTNESS || ++ ctrl->id > V4L2_CID_HUE) ++ return -EINVAL; ++ ++ mutex_lock(&zr->resource_lock); ++ decoder_call(zr, core, s_ctrl, ctrl); ++ mutex_unlock(&zr->resource_lock); ++ ++ return 0; ++} ++ ++static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ mutex_lock(&zr->resource_lock); ++ *std = zr->norm; ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ ++ mutex_lock(&zr->resource_lock); ++ res = zoran_set_norm(zr, *std); ++ if (res) ++ goto sstd_unlock_and_return; ++ ++ res = wait_grab_pending(zr); ++sstd_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_enum_input(struct file *file, void *__fh, ++ struct v4l2_input *inp) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ if (inp->index >= zr->card.inputs) ++ return -EINVAL; ++ ++ strncpy(inp->name, zr->card.input[inp->index].name, ++ sizeof(inp->name) - 1); ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ inp->std = V4L2_STD_ALL; ++ ++ /* Get status of video decoder */ ++ mutex_lock(&zr->resource_lock); ++ decoder_call(zr, video, g_input_status, &inp->status); ++ mutex_unlock(&zr->resource_lock); ++ return 0; ++} ++ ++static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ ++ mutex_lock(&zr->resource_lock); ++ *input = zr->input; ++ mutex_unlock(&zr->resource_lock); ++ ++ return 0; ++} ++ ++static int zoran_s_input(struct file *file, void *__fh, unsigned int input) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res; ++ ++ mutex_lock(&zr->resource_lock); ++ res = zoran_set_input(zr, input); ++ if (res) ++ goto sinput_unlock_and_return; ++ ++ /* Make sure the changes come into effect */ ++ res = wait_grab_pending(zr); ++sinput_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_enum_output(struct file *file, void *__fh, ++ struct v4l2_output *outp) ++{ ++ if (outp->index != 0) ++ return -EINVAL; ++ ++ outp->index = 0; ++ outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; ++ strncpy(outp->name, "Autodetect", sizeof(outp->name)-1); ++ ++ return 0; ++} ++ ++static int zoran_g_output(struct file *file, void *__fh, unsigned int *output) ++{ ++ *output = 0; ++ ++ return 0; ++} ++ ++static int zoran_s_output(struct file *file, void *__fh, unsigned int output) ++{ ++ if (output != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* cropping (sub-frame capture) */ ++static int zoran_cropcap(struct file *file, void *__fh, ++ struct v4l2_cropcap *cropcap) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int type = cropcap->type, res = 0; ++ ++ memset(cropcap, 0, sizeof(*cropcap)); ++ cropcap->type = type; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ fh->map_mode == ZORAN_MAP_MODE_RAW)) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", ++ ZR_DEVNAME(zr)); ++ res = -EINVAL; ++ goto cropcap_unlock_and_return; ++ } ++ ++ cropcap->bounds.top = cropcap->bounds.left = 0; ++ cropcap->bounds.width = BUZ_MAX_WIDTH; ++ cropcap->bounds.height = BUZ_MAX_HEIGHT; ++ cropcap->defrect.top = cropcap->defrect.left = 0; ++ cropcap->defrect.width = BUZ_MIN_WIDTH; ++ cropcap->defrect.height = BUZ_MIN_HEIGHT; ++cropcap_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int type = crop->type, res = 0; ++ ++ memset(crop, 0, sizeof(*crop)); ++ crop->type = type; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ fh->map_mode == ZORAN_MAP_MODE_RAW)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ++ ZR_DEVNAME(zr)); ++ res = -EINVAL; ++ goto gcrop_unlock_and_return; ++ } ++ ++ crop->c.top = fh->jpg_settings.img_y; ++ crop->c.left = fh->jpg_settings.img_x; ++ crop->c.width = fh->jpg_settings.img_width; ++ crop->c.height = fh->jpg_settings.img_height; ++ ++gcrop_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static int zoran_s_crop(struct file *file, void *__fh, const struct v4l2_crop *crop) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ struct zoran_jpg_settings settings; ++ ++ settings = fh->jpg_settings; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (fh->buffers.allocated) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_S_CROP - cannot change settings while active\n", ++ ZR_DEVNAME(zr)); ++ res = -EBUSY; ++ goto scrop_unlock_and_return; ++ } ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && ++ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || ++ fh->map_mode == ZORAN_MAP_MODE_RAW)) { ++ dprintk(1, KERN_ERR ++ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", ++ ZR_DEVNAME(zr)); ++ res = -EINVAL; ++ goto scrop_unlock_and_return; ++ } ++ ++ /* move into a form that we understand */ ++ settings.img_x = crop->c.left; ++ settings.img_y = crop->c.top; ++ settings.img_width = crop->c.width; ++ settings.img_height = crop->c.height; ++ ++ /* check validity */ ++ res = zoran_check_jpg_settings(zr, &settings, 0); ++ if (res) ++ goto scrop_unlock_and_return; ++ ++ /* accept */ ++ fh->jpg_settings = settings; ++ ++scrop_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ return res; ++} ++ ++static int zoran_g_jpegcomp(struct file *file, void *__fh, ++ struct v4l2_jpegcompression *params) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ memset(params, 0, sizeof(*params)); ++ ++ mutex_lock(&zr->resource_lock); ++ ++ params->quality = fh->jpg_settings.jpg_comp.quality; ++ params->APPn = fh->jpg_settings.jpg_comp.APPn; ++ memcpy(params->APP_data, ++ fh->jpg_settings.jpg_comp.APP_data, ++ fh->jpg_settings.jpg_comp.APP_len); ++ params->APP_len = fh->jpg_settings.jpg_comp.APP_len; ++ memcpy(params->COM_data, ++ fh->jpg_settings.jpg_comp.COM_data, ++ fh->jpg_settings.jpg_comp.COM_len); ++ params->COM_len = fh->jpg_settings.jpg_comp.COM_len; ++ params->jpeg_markers = ++ fh->jpg_settings.jpg_comp.jpeg_markers; ++ ++ mutex_unlock(&zr->resource_lock); ++ ++ return 0; ++} ++ ++static int zoran_s_jpegcomp(struct file *file, void *__fh, ++ const struct v4l2_jpegcompression *params) ++{ ++ struct zoran_fh *fh = __fh; ++ struct zoran *zr = fh->zr; ++ int res = 0; ++ struct zoran_jpg_settings settings; ++ ++ settings = fh->jpg_settings; ++ ++ settings.jpg_comp = *params; ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (fh->buffers.active != ZORAN_FREE) { ++ dprintk(1, KERN_WARNING ++ "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", ++ ZR_DEVNAME(zr)); ++ res = -EBUSY; ++ goto sjpegc_unlock_and_return; ++ } ++ ++ res = zoran_check_jpg_settings(zr, &settings, 0); ++ if (res) ++ goto sjpegc_unlock_and_return; ++ if (!fh->buffers.allocated) ++ fh->buffers.buffer_size = ++ zoran_v4l2_calc_bufsize(&fh->jpg_settings); ++ fh->jpg_settings.jpg_comp = settings.jpg_comp; ++sjpegc_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static unsigned int ++zoran_poll (struct file *file, ++ poll_table *wait) ++{ ++ struct zoran_fh *fh = file->private_data; ++ struct zoran *zr = fh->zr; ++ int res = 0, frame; ++ unsigned long flags; ++ ++ /* we should check whether buffers are ready to be synced on ++ * (w/o waits - O_NONBLOCK) here ++ * if ready for read (sync), return POLLIN|POLLRDNORM, ++ * if ready for write (sync), return POLLOUT|POLLWRNORM, ++ * if error, return POLLERR, ++ * if no buffers queued or so, return POLLNVAL ++ */ ++ ++ mutex_lock(&zr->resource_lock); ++ ++ switch (fh->map_mode) { ++ case ZORAN_MAP_MODE_RAW: ++ poll_wait(file, &zr->v4l_capq, wait); ++ frame = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ dprintk(3, ++ KERN_DEBUG ++ "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n", ++ ZR_DEVNAME(zr), __func__, ++ "FAL"[fh->buffers.active], zr->v4l_sync_tail, ++ "UPMD"[zr->v4l_buffers.buffer[frame].state], ++ zr->v4l_pend_tail, zr->v4l_pend_head); ++ /* Process is the one capturing? */ ++ if (fh->buffers.active != ZORAN_FREE && ++ /* Buffer ready to DQBUF? */ ++ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) ++ res = POLLIN | POLLRDNORM; ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ break; ++ ++ case ZORAN_MAP_MODE_JPG_REC: ++ case ZORAN_MAP_MODE_JPG_PLAY: ++ poll_wait(file, &zr->jpg_capq, wait); ++ frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ dprintk(3, ++ KERN_DEBUG ++ "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n", ++ ZR_DEVNAME(zr), __func__, ++ "FAL"[fh->buffers.active], zr->jpg_que_tail, ++ "UPMD"[zr->jpg_buffers.buffer[frame].state], ++ zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head); ++ if (fh->buffers.active != ZORAN_FREE && ++ zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { ++ if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ++ res = POLLIN | POLLRDNORM; ++ else ++ res = POLLOUT | POLLWRNORM; ++ } ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ ++ break; ++ ++ default: ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - internal error, unknown map_mode=%d\n", ++ ZR_DEVNAME(zr), __func__, fh->map_mode); ++ res = POLLNVAL; ++ } ++ ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++ ++/* ++ * This maps the buffers to user space. ++ * ++ * Depending on the state of fh->map_mode ++ * the V4L or the MJPEG buffers are mapped ++ * per buffer or all together ++ * ++ * Note that we need to connect to some ++ * unmap signal event to unmap the de-allocate ++ * the buffer accordingly (zoran_vm_close()) ++ */ ++ ++static void ++zoran_vm_open (struct vm_area_struct *vma) ++{ ++ struct zoran_mapping *map = vma->vm_private_data; ++ ++ map->count++; ++} ++ ++static void ++zoran_vm_close (struct vm_area_struct *vma) ++{ ++ struct zoran_mapping *map = vma->vm_private_data; ++ struct zoran_fh *fh = map->fh; ++ struct zoran *zr = fh->zr; ++ int i; ++ ++ if (--map->count > 0) ++ return; ++ ++ dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), ++ __func__, mode_name(fh->map_mode)); ++ ++ for (i = 0; i < fh->buffers.num_buffers; i++) { ++ if (fh->buffers.buffer[i].map == map) ++ fh->buffers.buffer[i].map = NULL; ++ } ++ kfree(map); ++ ++ /* Any buffers still mapped? */ ++ for (i = 0; i < fh->buffers.num_buffers; i++) ++ if (fh->buffers.buffer[i].map) ++ return; ++ ++ dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), ++ __func__, mode_name(fh->map_mode)); ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) { ++ if (fh->buffers.active != ZORAN_FREE) { ++ unsigned long flags; ++ ++ spin_lock_irqsave(&zr->spinlock, flags); ++ zr36057_set_memgrab(zr, 0); ++ zr->v4l_buffers.allocated = 0; ++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; ++ spin_unlock_irqrestore(&zr->spinlock, flags); ++ } ++ v4l_fbuffer_free(fh); ++ } else { ++ if (fh->buffers.active != ZORAN_FREE) { ++ jpg_qbuf(fh, -1, zr->codec_mode); ++ zr->jpg_buffers.allocated = 0; ++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; ++ } ++ jpg_fbuffer_free(fh); ++ } ++ ++ mutex_unlock(&zr->resource_lock); ++} ++ ++static const struct vm_operations_struct zoran_vm_ops = { ++ .open = zoran_vm_open, ++ .close = zoran_vm_close, ++}; ++ ++static int ++zoran_mmap (struct file *file, ++ struct vm_area_struct *vma) ++{ ++ struct zoran_fh *fh = file->private_data; ++ struct zoran *zr = fh->zr; ++ unsigned long size = (vma->vm_end - vma->vm_start); ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ int i, j; ++ unsigned long page, start = vma->vm_start, todo, pos, fraglen; ++ int first, last; ++ struct zoran_mapping *map; ++ int res = 0; ++ ++ dprintk(3, ++ KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n", ++ ZR_DEVNAME(zr), __func__, ++ mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size); ++ ++ if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) || ++ !(vma->vm_flags & VM_WRITE)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n", ++ ZR_DEVNAME(zr), __func__); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&zr->resource_lock); ++ ++ if (!fh->buffers.allocated) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(%s) - buffers not yet allocated\n", ++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); ++ res = -ENOMEM; ++ goto mmap_unlock_and_return; ++ } ++ ++ first = offset / fh->buffers.buffer_size; ++ last = first - 1 + size / fh->buffers.buffer_size; ++ if (offset % fh->buffers.buffer_size != 0 || ++ size % fh->buffers.buffer_size != 0 || first < 0 || ++ last < 0 || first >= fh->buffers.num_buffers || ++ last >= fh->buffers.buffer_size) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", ++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size, ++ fh->buffers.buffer_size, ++ fh->buffers.num_buffers); ++ res = -EINVAL; ++ goto mmap_unlock_and_return; ++ } ++ ++ /* Check if any buffers are already mapped */ ++ for (i = first; i <= last; i++) { ++ if (fh->buffers.buffer[i].map) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(%s) - buffer %d already mapped\n", ++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); ++ res = -EBUSY; ++ goto mmap_unlock_and_return; ++ } ++ } ++ ++ /* map these buffers */ ++ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); ++ if (!map) { ++ res = -ENOMEM; ++ goto mmap_unlock_and_return; ++ } ++ map->fh = fh; ++ map->count = 1; ++ ++ vma->vm_ops = &zoran_vm_ops; ++ vma->vm_flags |= VM_DONTEXPAND; ++ vma->vm_private_data = map; ++ ++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) { ++ for (i = first; i <= last; i++) { ++ todo = size; ++ if (todo > fh->buffers.buffer_size) ++ todo = fh->buffers.buffer_size; ++ page = fh->buffers.buffer[i].v4l.fbuffer_phys; ++ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, ++ todo, PAGE_SHARED)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(V4L) - remap_pfn_range failed\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -EAGAIN; ++ goto mmap_unlock_and_return; ++ } ++ size -= todo; ++ start += todo; ++ fh->buffers.buffer[i].map = map; ++ if (size == 0) ++ break; ++ } ++ } else { ++ for (i = first; i <= last; i++) { ++ for (j = 0; ++ j < fh->buffers.buffer_size / PAGE_SIZE; ++ j++) { ++ fraglen = ++ (le32_to_cpu(fh->buffers.buffer[i].jpg. ++ frag_tab[2 * j + 1]) & ~1) << 1; ++ todo = size; ++ if (todo > fraglen) ++ todo = fraglen; ++ pos = ++ le32_to_cpu(fh->buffers. ++ buffer[i].jpg.frag_tab[2 * j]); ++ /* should just be pos on i386 */ ++ page = virt_to_phys(bus_to_virt(pos)) ++ >> PAGE_SHIFT; ++ if (remap_pfn_range(vma, start, page, ++ todo, PAGE_SHARED)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: %s(V4L) - remap_pfn_range failed\n", ++ ZR_DEVNAME(zr), __func__); ++ res = -EAGAIN; ++ goto mmap_unlock_and_return; ++ } ++ size -= todo; ++ start += todo; ++ if (size == 0) ++ break; ++ if (le32_to_cpu(fh->buffers.buffer[i].jpg. ++ frag_tab[2 * j + 1]) & 1) ++ break; /* was last fragment */ ++ } ++ fh->buffers.buffer[i].map = map; ++ if (size == 0) ++ break; ++ ++ } ++ } ++ ++mmap_unlock_and_return: ++ mutex_unlock(&zr->resource_lock); ++ ++ return res; ++} ++ ++static const struct v4l2_ioctl_ops zoran_ioctl_ops = { ++ .vidioc_querycap = zoran_querycap, ++ .vidioc_cropcap = zoran_cropcap, ++ .vidioc_s_crop = zoran_s_crop, ++ .vidioc_g_crop = zoran_g_crop, ++ .vidioc_enum_input = zoran_enum_input, ++ .vidioc_g_input = zoran_g_input, ++ .vidioc_s_input = zoran_s_input, ++ .vidioc_enum_output = zoran_enum_output, ++ .vidioc_g_output = zoran_g_output, ++ .vidioc_s_output = zoran_s_output, ++ .vidioc_g_fbuf = zoran_g_fbuf, ++ .vidioc_s_fbuf = zoran_s_fbuf, ++ .vidioc_g_std = zoran_g_std, ++ .vidioc_s_std = zoran_s_std, ++ .vidioc_g_jpegcomp = zoran_g_jpegcomp, ++ .vidioc_s_jpegcomp = zoran_s_jpegcomp, ++ .vidioc_overlay = zoran_overlay, ++ .vidioc_reqbufs = zoran_reqbufs, ++ .vidioc_querybuf = zoran_querybuf, ++ .vidioc_qbuf = zoran_qbuf, ++ .vidioc_dqbuf = zoran_dqbuf, ++ .vidioc_streamon = zoran_streamon, ++ .vidioc_streamoff = zoran_streamoff, ++ .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, ++ .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out, ++ .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, ++ .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out, ++ .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, ++ .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out, ++ .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, ++ .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out, ++ .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay, ++ .vidioc_queryctrl = zoran_queryctrl, ++ .vidioc_s_ctrl = zoran_s_ctrl, ++ .vidioc_g_ctrl = zoran_g_ctrl, ++}; ++ ++/* please use zr->resource_lock consistently and kill this wrapper */ ++static long zoran_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct zoran_fh *fh = file->private_data; ++ struct zoran *zr = fh->zr; ++ int ret; ++ ++ mutex_lock(&zr->other_lock); ++ ret = video_ioctl2(file, cmd, arg); ++ mutex_unlock(&zr->other_lock); ++ ++ return ret; ++} ++ ++static const struct v4l2_file_operations zoran_fops = { ++ .owner = THIS_MODULE, ++ .open = zoran_open, ++ .release = zoran_close, ++ .unlocked_ioctl = zoran_ioctl, ++ .read = zoran_read, ++ .write = zoran_write, ++ .mmap = zoran_mmap, ++ .poll = zoran_poll, ++}; ++ ++struct video_device zoran_template __devinitdata = { ++ .name = ZORAN_NAME, ++ .fops = &zoran_fops, ++ .ioctl_ops = &zoran_ioctl_ops, ++ .release = &zoran_vdev_release, ++ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, ++}; ++ +diff --git a/drivers/media/pci/zoran/zoran_procfs.c b/drivers/media/pci/zoran/zoran_procfs.c +new file mode 100644 +index 0000000..f1423b7 +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_procfs.c +@@ -0,0 +1,225 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles the procFS entries (/proc/ZORAN[%d]) ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "videocodec.h" ++#include "zoran.h" ++#include "zoran_procfs.h" ++#include "zoran_card.h" ++ ++#ifdef CONFIG_PROC_FS ++struct procfs_params_zr36067 { ++ char *name; ++ short reg; ++ u32 mask; ++ short bit; ++}; ++ ++static const struct procfs_params_zr36067 zr67[] = { ++ {"HSPol", 0x000, 1, 30}, ++ {"HStart", 0x000, 0x3ff, 10}, ++ {"HEnd", 0x000, 0x3ff, 0}, ++ ++ {"VSPol", 0x004, 1, 30}, ++ {"VStart", 0x004, 0x3ff, 10}, ++ {"VEnd", 0x004, 0x3ff, 0}, ++ ++ {"ExtFl", 0x008, 1, 26}, ++ {"TopField", 0x008, 1, 25}, ++ {"VCLKPol", 0x008, 1, 24}, ++ {"DupFld", 0x008, 1, 20}, ++ {"LittleEndian", 0x008, 1, 0}, ++ ++ {"HsyncStart", 0x10c, 0xffff, 16}, ++ {"LineTot", 0x10c, 0xffff, 0}, ++ ++ {"NAX", 0x110, 0xffff, 16}, ++ {"PAX", 0x110, 0xffff, 0}, ++ ++ {"NAY", 0x114, 0xffff, 16}, ++ {"PAY", 0x114, 0xffff, 0}, ++ ++ /* {"",,,}, */ ++ ++ {NULL, 0, 0, 0}, ++}; ++ ++static void ++setparam (struct zoran *zr, ++ char *name, ++ char *sval) ++{ ++ int i = 0, reg0, reg, val; ++ ++ while (zr67[i].name != NULL) { ++ if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) { ++ reg = reg0 = btread(zr67[i].reg); ++ reg &= ~(zr67[i].mask << zr67[i].bit); ++ if (!isdigit(sval[0])) ++ break; ++ val = simple_strtoul(sval, NULL, 0); ++ if ((val & ~zr67[i].mask)) ++ break; ++ reg |= (val & zr67[i].mask) << zr67[i].bit; ++ dprintk(4, ++ KERN_INFO ++ "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n", ++ ZR_DEVNAME(zr), zr67[i].reg, reg0, reg, ++ zr67[i].name, val); ++ btwrite(reg, zr67[i].reg); ++ break; ++ } ++ i++; ++ } ++} ++ ++static int zoran_show(struct seq_file *p, void *v) ++{ ++ struct zoran *zr = p->private; ++ int i; ++ ++ seq_printf(p, "ZR36067 registers:\n"); ++ for (i = 0; i < 0x130; i += 16) ++ seq_printf(p, "%03X %08X %08X %08X %08X \n", i, ++ btread(i), btread(i+4), btread(i+8), btread(i+12)); ++ return 0; ++} ++ ++static int zoran_open(struct inode *inode, struct file *file) ++{ ++ struct zoran *data = PDE(inode)->data; ++ return single_open(file, zoran_show, data); ++} ++ ++static ssize_t zoran_write(struct file *file, const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct zoran *zr = PDE(file->f_path.dentry->d_inode)->data; ++ char *string, *sp; ++ char *line, *ldelim, *varname, *svar, *tdelim; ++ ++ if (count > 32768) /* Stupidity filter */ ++ return -EINVAL; ++ ++ string = sp = vmalloc(count + 1); ++ if (!string) { ++ dprintk(1, ++ KERN_ERR ++ "%s: write_proc: can not allocate memory\n", ++ ZR_DEVNAME(zr)); ++ return -ENOMEM; ++ } ++ if (copy_from_user(string, buffer, count)) { ++ vfree (string); ++ return -EFAULT; ++ } ++ string[count] = 0; ++ dprintk(4, KERN_INFO "%s: write_proc: name=%s count=%zu zr=%p\n", ++ ZR_DEVNAME(zr), file->f_path.dentry->d_name.name, count, zr); ++ ldelim = " \t\n"; ++ tdelim = "="; ++ line = strpbrk(sp, ldelim); ++ while (line) { ++ *line = 0; ++ svar = strpbrk(sp, tdelim); ++ if (svar) { ++ *svar = 0; ++ varname = sp; ++ svar++; ++ setparam(zr, varname, svar); ++ } ++ sp = line + 1; ++ line = strpbrk(sp, ldelim); ++ } ++ vfree(string); ++ ++ return count; ++} ++ ++static const struct file_operations zoran_operations = { ++ .owner = THIS_MODULE, ++ .open = zoran_open, ++ .read = seq_read, ++ .write = zoran_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++#endif ++ ++int ++zoran_proc_init (struct zoran *zr) ++{ ++#ifdef CONFIG_PROC_FS ++ char name[8]; ++ ++ snprintf(name, 7, "zoran%d", zr->id); ++ zr->zoran_proc = proc_create_data(name, 0, NULL, &zoran_operations, zr); ++ if (zr->zoran_proc != NULL) { ++ dprintk(2, ++ KERN_INFO ++ "%s: procfs entry /proc/%s allocated. data=%p\n", ++ ZR_DEVNAME(zr), name, zr->zoran_proc->data); ++ } else { ++ dprintk(1, KERN_ERR "%s: Unable to initialise /proc/%s\n", ++ ZR_DEVNAME(zr), name); ++ return 1; ++ } ++#endif ++ return 0; ++} ++ ++void ++zoran_proc_cleanup (struct zoran *zr) ++{ ++#ifdef CONFIG_PROC_FS ++ char name[8]; ++ ++ snprintf(name, 7, "zoran%d", zr->id); ++ if (zr->zoran_proc) ++ remove_proc_entry(name, NULL); ++ zr->zoran_proc = NULL; ++#endif ++} +diff --git a/drivers/media/pci/zoran/zoran_procfs.h b/drivers/media/pci/zoran/zoran_procfs.h +new file mode 100644 +index 0000000..f2d5b1b +--- /dev/null ++++ b/drivers/media/pci/zoran/zoran_procfs.h +@@ -0,0 +1,36 @@ ++/* ++ * Zoran zr36057/zr36067 PCI controller driver, for the ++ * Pinnacle/Miro DC10/DC10+/DC30/DC30+, Iomega Buz, Linux ++ * Media Labs LML33/LML33R10. ++ * ++ * This part handles card-specific data and detection ++ * ++ * Copyright (C) 2000 Serguei Miridonov ++ * ++ * Currently maintained by: ++ * Ronald Bultje ++ * Laurent Pinchart ++ * Mailinglist ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __ZORAN_PROCFS_H__ ++#define __ZORAN_PROCFS_H__ ++ ++extern int zoran_proc_init(struct zoran *zr); ++extern void zoran_proc_cleanup(struct zoran *zr); ++ ++#endif /* __ZORAN_PROCFS_H__ */ +diff --git a/drivers/media/pci/zoran/zr36016.c b/drivers/media/pci/zoran/zr36016.c +new file mode 100644 +index 0000000..b87ddba +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36016.c +@@ -0,0 +1,524 @@ ++/* ++ * Zoran ZR36016 basic configuration functions ++ * ++ * Copyright (C) 2001 Wolfgang Scherr ++ * ++ * $Id: zr36016.c,v 1.1.2.14 2003/08/20 19:46:55 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * 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 ZR016_VERSION "v0.7" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* I/O commands, error codes */ ++#include ++ ++/* v4l API */ ++ ++/* headerfile of this module */ ++#include "zr36016.h" ++ ++/* codec io API */ ++#include "videocodec.h" ++ ++/* it doesn't make sense to have more than 20 or so, ++ just to prevent some unwanted loops */ ++#define MAX_CODECS 20 ++ ++/* amount of chips attached via this driver */ ++static int zr36016_codecs; ++ ++/* debugging is available via module parameter */ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-4)"); ++ ++#define dprintk(num, format, args...) \ ++ do { \ ++ if (debug >= num) \ ++ printk(format, ##args); \ ++ } while (0) ++ ++/* ========================================================================= ++ Local hardware I/O functions: ++ ++ read/write via codec layer (registers are located in the master device) ++ ========================================================================= */ ++ ++/* read and write functions */ ++static u8 ++zr36016_read (struct zr36016 *ptr, ++ u16 reg) ++{ ++ u8 value = 0; ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->readreg) ++ value = ++ (ptr->codec->master_data-> ++ readreg(ptr->codec, reg)) & 0xFF; ++ else ++ dprintk(1, ++ KERN_ERR "%s: invalid I/O setup, nothing read!\n", ++ ptr->name); ++ ++ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, ++ value); ++ ++ return value; ++} ++ ++static void ++zr36016_write (struct zr36016 *ptr, ++ u16 reg, ++ u8 value) ++{ ++ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, ++ reg); ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->writereg) { ++ ptr->codec->master_data->writereg(ptr->codec, reg, value); ++ } else ++ dprintk(1, ++ KERN_ERR ++ "%s: invalid I/O setup, nothing written!\n", ++ ptr->name); ++} ++ ++/* indirect read and write functions */ ++/* the 016 supports auto-addr-increment, but ++ * writing it all time cost not much and is safer... */ ++static u8 ++zr36016_readi (struct zr36016 *ptr, ++ u16 reg) ++{ ++ u8 value = 0; ++ ++ // just in case something is wrong... ++ if ((ptr->codec->master_data->writereg) && ++ (ptr->codec->master_data->readreg)) { ++ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR ++ value = (ptr->codec->master_data->readreg(ptr->codec, ZR016_IDATA)) & 0xFF; // DATA ++ } else ++ dprintk(1, ++ KERN_ERR ++ "%s: invalid I/O setup, nothing read (i)!\n", ++ ptr->name); ++ ++ dprintk(4, "%s: reading indirect from 0x%04x: %02x\n", ptr->name, ++ reg, value); ++ return value; ++} ++ ++static void ++zr36016_writei (struct zr36016 *ptr, ++ u16 reg, ++ u8 value) ++{ ++ dprintk(4, "%s: writing indirect 0x%02x to 0x%04x\n", ptr->name, ++ value, reg); ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->writereg) { ++ ptr->codec->master_data->writereg(ptr->codec, ZR016_IADDR, reg & 0x0F); // ADDR ++ ptr->codec->master_data->writereg(ptr->codec, ZR016_IDATA, value & 0x0FF); // DATA ++ } else ++ dprintk(1, ++ KERN_ERR ++ "%s: invalid I/O setup, nothing written (i)!\n", ++ ptr->name); ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ version read ++ ========================================================================= */ ++ ++/* version kept in datastructure */ ++static u8 ++zr36016_read_version (struct zr36016 *ptr) ++{ ++ ptr->version = zr36016_read(ptr, 0) >> 4; ++ return ptr->version; ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ basic test of "connectivity", writes/reads to/from PAX-Lo register ++ ========================================================================= */ ++ ++static int ++zr36016_basic_test (struct zr36016 *ptr) ++{ ++ if (debug) { ++ int i; ++ zr36016_writei(ptr, ZR016I_PAX_LO, 0x55); ++ dprintk(1, KERN_INFO "%s: registers: ", ptr->name); ++ for (i = 0; i <= 0x0b; i++) ++ dprintk(1, "%02x ", zr36016_readi(ptr, i)); ++ dprintk(1, "\n"); ++ } ++ // for testing just write 0, then the default value to a register and read ++ // it back in both cases ++ zr36016_writei(ptr, ZR016I_PAX_LO, 0x00); ++ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, can't connect to vfe processor!\n", ++ ptr->name); ++ return -ENXIO; ++ } ++ zr36016_writei(ptr, ZR016I_PAX_LO, 0x0d0); ++ if (zr36016_readi(ptr, ZR016I_PAX_LO) != 0x0d0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, can't connect to vfe processor!\n", ++ ptr->name); ++ return -ENXIO; ++ } ++ // we allow version numbers from 0-3, should be enough, though ++ zr36016_read_version(ptr); ++ if (ptr->version & 0x0c) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, suspicious version %d found...\n", ++ ptr->name, ptr->version); ++ return -ENXIO; ++ } ++ ++ return 0; /* looks good! */ ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ simple loop for pushing the init datasets - NO USE -- ++ ========================================================================= */ ++ ++#if 0 ++static int zr36016_pushit (struct zr36016 *ptr, ++ u16 startreg, ++ u16 len, ++ const char *data) ++{ ++ int i=0; ++ ++ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ++ ptr->name, startreg,len); ++ while (imode == CODEC_DO_COMPRESSION ? ++ ZR016_COMPRESSION : ZR016_EXPANSION)); ++ ++ // misc setup ++ zr36016_writei(ptr, ZR016I_SETUP1, ++ (ptr->xdec ? (ZR016_HRFL | ZR016_HORZ) : 0) | ++ (ptr->ydec ? ZR016_VERT : 0) | ZR016_CNTI); ++ zr36016_writei(ptr, ZR016I_SETUP2, ZR016_CCIR); ++ ++ // Window setup ++ // (no extra offset for now, norm defines offset, default width height) ++ zr36016_writei(ptr, ZR016I_PAX_HI, ptr->width >> 8); ++ zr36016_writei(ptr, ZR016I_PAX_LO, ptr->width & 0xFF); ++ zr36016_writei(ptr, ZR016I_PAY_HI, ptr->height >> 8); ++ zr36016_writei(ptr, ZR016I_PAY_LO, ptr->height & 0xFF); ++ zr36016_writei(ptr, ZR016I_NAX_HI, ptr->xoff >> 8); ++ zr36016_writei(ptr, ZR016I_NAX_LO, ptr->xoff & 0xFF); ++ zr36016_writei(ptr, ZR016I_NAY_HI, ptr->yoff >> 8); ++ zr36016_writei(ptr, ZR016I_NAY_LO, ptr->yoff & 0xFF); ++ ++ /* shall we continue now, please? */ ++ zr36016_write(ptr, ZR016_GOSTOP, 1); ++} ++ ++/* ========================================================================= ++ CODEC API FUNCTIONS ++ ++ this functions are accessed by the master via the API structure ++ ========================================================================= */ ++ ++/* set compression/expansion mode and launches codec - ++ this should be the last call from the master before starting processing */ ++static int ++zr36016_set_mode (struct videocodec *codec, ++ int mode) ++{ ++ struct zr36016 *ptr = (struct zr36016 *) codec->data; ++ ++ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); ++ ++ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) ++ return -EINVAL; ++ ++ ptr->mode = mode; ++ zr36016_init(ptr); ++ ++ return 0; ++} ++ ++/* set picture size */ ++static int ++zr36016_set_video (struct videocodec *codec, ++ struct tvnorm *norm, ++ struct vfe_settings *cap, ++ struct vfe_polarity *pol) ++{ ++ struct zr36016 *ptr = (struct zr36016 *) codec->data; ++ ++ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) call\n", ++ ptr->name, norm->HStart, norm->VStart, ++ cap->x, cap->y, cap->width, cap->height, ++ cap->decimation); ++ ++ /* if () return -EINVAL; ++ * trust the master driver that it knows what it does - so ++ * we allow invalid startx/y for now ... */ ++ ptr->width = cap->width; ++ ptr->height = cap->height; ++ /* (Ronald) This is ugly. zoran_device.c, line 387 ++ * already mentions what happens if HStart is even ++ * (blue faces, etc., cr/cb inversed). There's probably ++ * some good reason why HStart is 0 instead of 1, so I'm ++ * leaving it to this for now, but really... This can be ++ * done a lot simpler */ ++ ptr->xoff = (norm->HStart ? norm->HStart : 1) + cap->x; ++ /* Something to note here (I don't understand it), setting ++ * VStart too high will cause the codec to 'not work'. I ++ * really don't get it. values of 16 (VStart) already break ++ * it here. Just '0' seems to work. More testing needed! */ ++ ptr->yoff = norm->VStart + cap->y; ++ /* (Ronald) dzjeeh, can't this thing do hor_decimation = 4? */ ++ ptr->xdec = ((cap->decimation & 0xff) == 1) ? 0 : 1; ++ ptr->ydec = (((cap->decimation >> 8) & 0xff) == 1) ? 0 : 1; ++ ++ return 0; ++} ++ ++/* additional control functions */ ++static int ++zr36016_control (struct videocodec *codec, ++ int type, ++ int size, ++ void *data) ++{ ++ struct zr36016 *ptr = (struct zr36016 *) codec->data; ++ int *ival = (int *) data; ++ ++ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, ++ size); ++ ++ switch (type) { ++ case CODEC_G_STATUS: /* get last status - we don't know it ... */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = 0; ++ break; ++ ++ case CODEC_G_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = 0; ++ break; ++ ++ case CODEC_S_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ if (*ival != 0) ++ return -EINVAL; ++ /* not needed, do nothing */ ++ return 0; ++ ++ case CODEC_G_VFE: ++ case CODEC_S_VFE: ++ return 0; ++ ++ case CODEC_S_MMAP: ++ /* not available, give an error */ ++ return -ENXIO; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return size; ++} ++ ++/* ========================================================================= ++ Exit and unregister function: ++ ++ Deinitializes Zoran's JPEG processor ++ ========================================================================= */ ++ ++static int ++zr36016_unset (struct videocodec *codec) ++{ ++ struct zr36016 *ptr = codec->data; ++ ++ if (ptr) { ++ /* do wee need some codec deinit here, too ???? */ ++ ++ dprintk(1, "%s: finished codec #%d\n", ptr->name, ++ ptr->num); ++ kfree(ptr); ++ codec->data = NULL; ++ ++ zr36016_codecs--; ++ return 0; ++ } ++ ++ return -EFAULT; ++} ++ ++/* ========================================================================= ++ Setup and registry function: ++ ++ Initializes Zoran's JPEG processor ++ ++ Also sets pixel size, average code size, mode (compr./decompr.) ++ (the given size is determined by the processor with the video interface) ++ ========================================================================= */ ++ ++static int ++zr36016_setup (struct videocodec *codec) ++{ ++ struct zr36016 *ptr; ++ int res; ++ ++ dprintk(2, "zr36016: initializing VFE subsystem #%d.\n", ++ zr36016_codecs); ++ ++ if (zr36016_codecs == MAX_CODECS) { ++ dprintk(1, ++ KERN_ERR "zr36016: Can't attach more codecs!\n"); ++ return -ENOSPC; ++ } ++ //mem structure init ++ codec->data = ptr = kzalloc(sizeof(struct zr36016), GFP_KERNEL); ++ if (NULL == ptr) { ++ dprintk(1, KERN_ERR "zr36016: Can't get enough memory!\n"); ++ return -ENOMEM; ++ } ++ ++ snprintf(ptr->name, sizeof(ptr->name), "zr36016[%d]", ++ zr36016_codecs); ++ ptr->num = zr36016_codecs++; ++ ptr->codec = codec; ++ ++ //testing ++ res = zr36016_basic_test(ptr); ++ if (res < 0) { ++ zr36016_unset(codec); ++ return res; ++ } ++ //final setup ++ ptr->mode = CODEC_DO_COMPRESSION; ++ ptr->width = 768; ++ ptr->height = 288; ++ ptr->xdec = 1; ++ ptr->ydec = 0; ++ zr36016_init(ptr); ++ ++ dprintk(1, KERN_INFO "%s: codec v%d attached and running\n", ++ ptr->name, ptr->version); ++ ++ return 0; ++} ++ ++static const struct videocodec zr36016_codec = { ++ .owner = THIS_MODULE, ++ .name = "zr36016", ++ .magic = 0L, // magic not used ++ .flags = ++ CODEC_FLAG_HARDWARE | CODEC_FLAG_VFE | CODEC_FLAG_ENCODER | ++ CODEC_FLAG_DECODER, ++ .type = CODEC_TYPE_ZR36016, ++ .setup = zr36016_setup, // functionality ++ .unset = zr36016_unset, ++ .set_mode = zr36016_set_mode, ++ .set_video = zr36016_set_video, ++ .control = zr36016_control, ++ // others are not used ++}; ++ ++/* ========================================================================= ++ HOOK IN DRIVER AS KERNEL MODULE ++ ========================================================================= */ ++ ++static int __init ++zr36016_init_module (void) ++{ ++ //dprintk(1, "ZR36016 driver %s\n",ZR016_VERSION); ++ zr36016_codecs = 0; ++ return videocodec_register(&zr36016_codec); ++} ++ ++static void __exit ++zr36016_cleanup_module (void) ++{ ++ if (zr36016_codecs) { ++ dprintk(1, ++ "zr36016: something's wrong - %d codecs left somehow.\n", ++ zr36016_codecs); ++ } ++ videocodec_unregister(&zr36016_codec); ++} ++ ++module_init(zr36016_init_module); ++module_exit(zr36016_cleanup_module); ++ ++MODULE_AUTHOR("Wolfgang Scherr "); ++MODULE_DESCRIPTION("Driver module for ZR36016 video frontends " ++ ZR016_VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/zoran/zr36016.h b/drivers/media/pci/zoran/zr36016.h +new file mode 100644 +index 0000000..8c79229 +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36016.h +@@ -0,0 +1,111 @@ ++/* ++ * Zoran ZR36016 basic configuration functions - header file ++ * ++ * Copyright (C) 2001 Wolfgang Scherr ++ * ++ * $Id: zr36016.h,v 1.1.2.3 2003/01/14 21:18:07 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ------------------------------------------------------------------------ ++ */ ++ ++#ifndef ZR36016_H ++#define ZR36016_H ++ ++/* data stored for each zoran jpeg codec chip */ ++struct zr36016 { ++ char name[32]; ++ int num; ++ /* io datastructure */ ++ struct videocodec *codec; ++ // coder status ++ __u8 version; ++ // actual coder setup ++ int mode; ++ ++ __u16 xoff; ++ __u16 yoff; ++ __u16 width; ++ __u16 height; ++ __u16 xdec; ++ __u16 ydec; ++}; ++ ++/* direct register addresses */ ++#define ZR016_GOSTOP 0x00 ++#define ZR016_MODE 0x01 ++#define ZR016_IADDR 0x02 ++#define ZR016_IDATA 0x03 ++ ++/* indirect register addresses */ ++#define ZR016I_SETUP1 0x00 ++#define ZR016I_SETUP2 0x01 ++#define ZR016I_NAX_LO 0x02 ++#define ZR016I_NAX_HI 0x03 ++#define ZR016I_PAX_LO 0x04 ++#define ZR016I_PAX_HI 0x05 ++#define ZR016I_NAY_LO 0x06 ++#define ZR016I_NAY_HI 0x07 ++#define ZR016I_PAY_LO 0x08 ++#define ZR016I_PAY_HI 0x09 ++#define ZR016I_NOL_LO 0x0a ++#define ZR016I_NOL_HI 0x0b ++ ++/* possible values for mode register */ ++#define ZR016_RGB444_YUV444 0x00 ++#define ZR016_RGB444_YUV422 0x01 ++#define ZR016_RGB444_YUV411 0x02 ++#define ZR016_RGB444_Y400 0x03 ++#define ZR016_RGB444_RGB444 0x04 ++#define ZR016_YUV444_YUV444 0x08 ++#define ZR016_YUV444_YUV422 0x09 ++#define ZR016_YUV444_YUV411 0x0a ++#define ZR016_YUV444_Y400 0x0b ++#define ZR016_YUV444_RGB444 0x0c ++#define ZR016_YUV422_YUV422 0x11 ++#define ZR016_YUV422_YUV411 0x12 ++#define ZR016_YUV422_Y400 0x13 ++#define ZR016_YUV411_YUV411 0x16 ++#define ZR016_YUV411_Y400 0x17 ++#define ZR016_4444_4444 0x19 ++#define ZR016_100_100 0x1b ++ ++#define ZR016_RGB444 0x00 ++#define ZR016_YUV444 0x20 ++#define ZR016_YUV422 0x40 ++ ++#define ZR016_COMPRESSION 0x80 ++#define ZR016_EXPANSION 0x80 ++ ++/* possible values for setup 1 register */ ++#define ZR016_CKRT 0x80 ++#define ZR016_VERT 0x40 ++#define ZR016_HORZ 0x20 ++#define ZR016_HRFL 0x10 ++#define ZR016_DSFL 0x08 ++#define ZR016_SBFL 0x04 ++#define ZR016_RSTR 0x02 ++#define ZR016_CNTI 0x01 ++ ++/* possible values for setup 2 register */ ++#define ZR016_SYEN 0x40 ++#define ZR016_CCIR 0x04 ++#define ZR016_SIGN 0x02 ++#define ZR016_YMCS 0x01 ++ ++#endif /*fndef ZR36016_H */ +diff --git a/drivers/media/pci/zoran/zr36050.c b/drivers/media/pci/zoran/zr36050.c +new file mode 100644 +index 0000000..e198560 +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36050.c +@@ -0,0 +1,900 @@ ++/* ++ * Zoran ZR36050 basic configuration functions ++ * ++ * Copyright (C) 2001 Wolfgang Scherr ++ * ++ * $Id: zr36050.c,v 1.1.2.11 2003/08/03 14:54:53 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * 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 ZR050_VERSION "v0.7.1" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* I/O commands, error codes */ ++#include ++ ++/* headerfile of this module */ ++#include "zr36050.h" ++ ++/* codec io API */ ++#include "videocodec.h" ++ ++/* it doesn't make sense to have more than 20 or so, ++ just to prevent some unwanted loops */ ++#define MAX_CODECS 20 ++ ++/* amount of chips attached via this driver */ ++static int zr36050_codecs; ++ ++/* debugging is available via module parameter */ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-4)"); ++ ++#define dprintk(num, format, args...) \ ++ do { \ ++ if (debug >= num) \ ++ printk(format, ##args); \ ++ } while (0) ++ ++/* ========================================================================= ++ Local hardware I/O functions: ++ ++ read/write via codec layer (registers are located in the master device) ++ ========================================================================= */ ++ ++/* read and write functions */ ++static u8 ++zr36050_read (struct zr36050 *ptr, ++ u16 reg) ++{ ++ u8 value = 0; ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->readreg) ++ value = (ptr->codec->master_data->readreg(ptr->codec, ++ reg)) & 0xFF; ++ else ++ dprintk(1, ++ KERN_ERR "%s: invalid I/O setup, nothing read!\n", ++ ptr->name); ++ ++ dprintk(4, "%s: reading from 0x%04x: %02x\n", ptr->name, reg, ++ value); ++ ++ return value; ++} ++ ++static void ++zr36050_write (struct zr36050 *ptr, ++ u16 reg, ++ u8 value) ++{ ++ dprintk(4, "%s: writing 0x%02x to 0x%04x\n", ptr->name, value, ++ reg); ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->writereg) ++ ptr->codec->master_data->writereg(ptr->codec, reg, value); ++ else ++ dprintk(1, ++ KERN_ERR ++ "%s: invalid I/O setup, nothing written!\n", ++ ptr->name); ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ status read ++ ========================================================================= */ ++ ++/* status is kept in datastructure */ ++static u8 ++zr36050_read_status1 (struct zr36050 *ptr) ++{ ++ ptr->status1 = zr36050_read(ptr, ZR050_STATUS_1); ++ ++ zr36050_read(ptr, 0); ++ return ptr->status1; ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ scale factor read ++ ========================================================================= */ ++ ++/* scale factor is kept in datastructure */ ++static u16 ++zr36050_read_scalefactor (struct zr36050 *ptr) ++{ ++ ptr->scalefact = (zr36050_read(ptr, ZR050_SF_HI) << 8) | ++ (zr36050_read(ptr, ZR050_SF_LO) & 0xFF); ++ ++ /* leave 0 selected for an eventually GO from master */ ++ zr36050_read(ptr, 0); ++ return ptr->scalefact; ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ wait if codec is ready to proceed (end of processing) or time is over ++ ========================================================================= */ ++ ++static void ++zr36050_wait_end (struct zr36050 *ptr) ++{ ++ int i = 0; ++ ++ while (!(zr36050_read_status1(ptr) & 0x4)) { ++ udelay(1); ++ if (i++ > 200000) { // 200ms, there is for sure something wrong!!! ++ dprintk(1, ++ "%s: timeout at wait_end (last status: 0x%02x)\n", ++ ptr->name, ptr->status1); ++ break; ++ } ++ } ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ basic test of "connectivity", writes/reads to/from memory the SOF marker ++ ========================================================================= */ ++ ++static int ++zr36050_basic_test (struct zr36050 *ptr) ++{ ++ zr36050_write(ptr, ZR050_SOF_IDX, 0x00); ++ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0x00); ++ if ((zr36050_read(ptr, ZR050_SOF_IDX) | ++ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0x0000) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, can't connect to jpeg processor!\n", ++ ptr->name); ++ return -ENXIO; ++ } ++ zr36050_write(ptr, ZR050_SOF_IDX, 0xff); ++ zr36050_write(ptr, ZR050_SOF_IDX + 1, 0xc0); ++ if (((zr36050_read(ptr, ZR050_SOF_IDX) << 8) | ++ zr36050_read(ptr, ZR050_SOF_IDX + 1)) != 0xffc0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, can't connect to jpeg processor!\n", ++ ptr->name); ++ return -ENXIO; ++ } ++ ++ zr36050_wait_end(ptr); ++ if ((ptr->status1 & 0x4) == 0) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, jpeg processor failed (end flag)!\n", ++ ptr->name); ++ return -EBUSY; ++ } ++ ++ return 0; /* looks good! */ ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ simple loop for pushing the init datasets ++ ========================================================================= */ ++ ++static int ++zr36050_pushit (struct zr36050 *ptr, ++ u16 startreg, ++ u16 len, ++ const char *data) ++{ ++ int i = 0; ++ ++ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, ++ startreg, len); ++ while (i < len) { ++ zr36050_write(ptr, startreg++, data[i++]); ++ } ++ ++ return i; ++} ++ ++/* ========================================================================= ++ Basic datasets: ++ ++ jpeg baseline setup data (you find it on lots places in internet, or just ++ extract it from any regular .jpg image...) ++ ++ Could be variable, but until it's not needed it they are just fixed to save ++ memory. Otherwise expand zr36050 structure with arrays, push the values to ++ it and initialize from there, as e.g. the linux zr36057/60 driver does it. ++ ========================================================================= */ ++ ++static const char zr36050_dqt[0x86] = { ++ 0xff, 0xdb, //Marker: DQT ++ 0x00, 0x84, //Length: 2*65+2 ++ 0x00, //Pq,Tq first table ++ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, ++ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, ++ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, ++ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, ++ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, ++ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, ++ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, ++ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, ++ 0x01, //Pq,Tq second table ++ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, ++ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 ++}; ++ ++static const char zr36050_dht[0x1a4] = { ++ 0xff, 0xc4, //Marker: DHT ++ 0x01, 0xa2, //Length: 2*AC, 2*DC ++ 0x00, //DC first table ++ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, ++ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ++ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ++ 0x01, //DC second table ++ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ++ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ++ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ++ 0x10, //AC first table ++ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, ++ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, ++ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, ++ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, ++ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, ++ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, ++ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, ++ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, ++ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, ++ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, ++ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, ++ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, ++ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, ++ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, ++ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, ++ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, ++ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, ++ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, ++ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, ++ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, ++ 0xF8, 0xF9, 0xFA, ++ 0x11, //AC second table ++ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, ++ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, ++ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, ++ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, ++ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, ++ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, ++ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, ++ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, ++ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, ++ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, ++ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, ++ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, ++ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, ++ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, ++ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, ++ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, ++ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, ++ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, ++ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, ++ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, ++ 0xF9, 0xFA ++}; ++ ++/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ ++#define NO_OF_COMPONENTS 0x3 //Y,U,V ++#define BASELINE_PRECISION 0x8 //MCU size (?) ++static const char zr36050_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT ++static const char zr36050_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC ++static const char zr36050_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC ++ ++/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ ++static const char zr36050_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; ++static const char zr36050_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; ++ ++/* ========================================================================= ++ Local helper functions: ++ ++ calculation and setup of parameter-dependent JPEG baseline segments ++ (needed for compression only) ++ ========================================================================= */ ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* SOF (start of frame) segment depends on width, height and sampling ratio ++ of each color component */ ++ ++static int ++zr36050_set_sof (struct zr36050 *ptr) ++{ ++ char sof_data[34]; // max. size of register set ++ int i; ++ ++ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, ++ ptr->width, ptr->height, NO_OF_COMPONENTS); ++ sof_data[0] = 0xff; ++ sof_data[1] = 0xc0; ++ sof_data[2] = 0x00; ++ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; ++ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36050 ++ sof_data[5] = (ptr->height) >> 8; ++ sof_data[6] = (ptr->height) & 0xff; ++ sof_data[7] = (ptr->width) >> 8; ++ sof_data[8] = (ptr->width) & 0xff; ++ sof_data[9] = NO_OF_COMPONENTS; ++ for (i = 0; i < NO_OF_COMPONENTS; i++) { ++ sof_data[10 + (i * 3)] = i; // index identifier ++ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | (ptr->v_samp_ratio[i]); // sampling ratios ++ sof_data[12 + (i * 3)] = zr36050_tq[i]; // Q table selection ++ } ++ return zr36050_pushit(ptr, ZR050_SOF_IDX, ++ (3 * NO_OF_COMPONENTS) + 10, sof_data); ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* SOS (start of scan) segment depends on the used scan components ++ of each color component */ ++ ++static int ++zr36050_set_sos (struct zr36050 *ptr) ++{ ++ char sos_data[16]; // max. size of register set ++ int i; ++ ++ dprintk(3, "%s: write SOS\n", ptr->name); ++ sos_data[0] = 0xff; ++ sos_data[1] = 0xda; ++ sos_data[2] = 0x00; ++ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; ++ sos_data[4] = NO_OF_COMPONENTS; ++ for (i = 0; i < NO_OF_COMPONENTS; i++) { ++ sos_data[5 + (i * 2)] = i; // index ++ sos_data[6 + (i * 2)] = (zr36050_td[i] << 4) | zr36050_ta[i]; // AC/DC tbl.sel. ++ } ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3F; ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; ++ return zr36050_pushit(ptr, ZR050_SOS1_IDX, ++ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, ++ sos_data); ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* DRI (define restart interval) */ ++ ++static int ++zr36050_set_dri (struct zr36050 *ptr) ++{ ++ char dri_data[6]; // max. size of register set ++ ++ dprintk(3, "%s: write DRI\n", ptr->name); ++ dri_data[0] = 0xff; ++ dri_data[1] = 0xdd; ++ dri_data[2] = 0x00; ++ dri_data[3] = 0x04; ++ dri_data[4] = ptr->dri >> 8; ++ dri_data[5] = ptr->dri & 0xff; ++ return zr36050_pushit(ptr, ZR050_DRI_IDX, 6, dri_data); ++} ++ ++/* ========================================================================= ++ Setup function: ++ ++ Setup compression/decompression of Zoran's JPEG processor ++ ( see also zoran 36050 manual ) ++ ++ ... sorry for the spaghetti code ... ++ ========================================================================= */ ++static void ++zr36050_init (struct zr36050 *ptr) ++{ ++ int sum = 0; ++ long bitcnt, tmp; ++ ++ if (ptr->mode == CODEC_DO_COMPRESSION) { ++ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); ++ ++ /* 050 communicates with 057 in master mode */ ++ zr36050_write(ptr, ZR050_HARDWARE, ZR050_HW_MSTR); ++ ++ /* encoding table preload for compression */ ++ zr36050_write(ptr, ZR050_MODE, ++ ZR050_MO_COMP | ZR050_MO_TLM); ++ zr36050_write(ptr, ZR050_OPTIONS, 0); ++ ++ /* disable all IRQs */ ++ zr36050_write(ptr, ZR050_INT_REQ_0, 0); ++ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 ++ ++ /* volume control settings */ ++ /*zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol);*/ ++ zr36050_write(ptr, ZR050_SF_HI, ptr->scalefact >> 8); ++ zr36050_write(ptr, ZR050_SF_LO, ptr->scalefact & 0xff); ++ ++ zr36050_write(ptr, ZR050_AF_HI, 0xff); ++ zr36050_write(ptr, ZR050_AF_M, 0xff); ++ zr36050_write(ptr, ZR050_AF_LO, 0xff); ++ ++ /* setup the variable jpeg tables */ ++ sum += zr36050_set_sof(ptr); ++ sum += zr36050_set_sos(ptr); ++ sum += zr36050_set_dri(ptr); ++ ++ /* setup the fixed jpeg tables - maybe variable, though - ++ * (see table init section above) */ ++ dprintk(3, "%s: write DQT, DHT, APP\n", ptr->name); ++ sum += zr36050_pushit(ptr, ZR050_DQT_IDX, ++ sizeof(zr36050_dqt), zr36050_dqt); ++ sum += zr36050_pushit(ptr, ZR050_DHT_IDX, ++ sizeof(zr36050_dht), zr36050_dht); ++ zr36050_write(ptr, ZR050_APP_IDX, 0xff); ++ zr36050_write(ptr, ZR050_APP_IDX + 1, 0xe0 + ptr->app.appn); ++ zr36050_write(ptr, ZR050_APP_IDX + 2, 0x00); ++ zr36050_write(ptr, ZR050_APP_IDX + 3, ptr->app.len + 2); ++ sum += zr36050_pushit(ptr, ZR050_APP_IDX + 4, 60, ++ ptr->app.data) + 4; ++ zr36050_write(ptr, ZR050_COM_IDX, 0xff); ++ zr36050_write(ptr, ZR050_COM_IDX + 1, 0xfe); ++ zr36050_write(ptr, ZR050_COM_IDX + 2, 0x00); ++ zr36050_write(ptr, ZR050_COM_IDX + 3, ptr->com.len + 2); ++ sum += zr36050_pushit(ptr, ZR050_COM_IDX + 4, 60, ++ ptr->com.data) + 4; ++ ++ /* do the internal huffman table preload */ ++ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); ++ ++ zr36050_write(ptr, ZR050_GO, 1); // launch codec ++ zr36050_wait_end(ptr); ++ dprintk(2, "%s: Status after table preload: 0x%02x\n", ++ ptr->name, ptr->status1); ++ ++ if ((ptr->status1 & 0x4) == 0) { ++ dprintk(1, KERN_ERR "%s: init aborted!\n", ++ ptr->name); ++ return; // something is wrong, its timed out!!!! ++ } ++ ++ /* setup misc. data for compression (target code sizes) */ ++ ++ /* size of compressed code to reach without header data */ ++ sum = ptr->real_code_vol - sum; ++ bitcnt = sum << 3; /* need the size in bits */ ++ ++ tmp = bitcnt >> 16; ++ dprintk(3, ++ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", ++ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); ++ zr36050_write(ptr, ZR050_TCV_NET_HI, tmp >> 8); ++ zr36050_write(ptr, ZR050_TCV_NET_MH, tmp & 0xff); ++ tmp = bitcnt & 0xffff; ++ zr36050_write(ptr, ZR050_TCV_NET_ML, tmp >> 8); ++ zr36050_write(ptr, ZR050_TCV_NET_LO, tmp & 0xff); ++ ++ bitcnt -= bitcnt >> 7; // bits without stuffing ++ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob ++ ++ tmp = bitcnt >> 16; ++ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", ++ ptr->name, bitcnt, tmp); ++ zr36050_write(ptr, ZR050_TCV_DATA_HI, tmp >> 8); ++ zr36050_write(ptr, ZR050_TCV_DATA_MH, tmp & 0xff); ++ tmp = bitcnt & 0xffff; ++ zr36050_write(ptr, ZR050_TCV_DATA_ML, tmp >> 8); ++ zr36050_write(ptr, ZR050_TCV_DATA_LO, tmp & 0xff); ++ ++ /* compression setup with or without bitrate control */ ++ zr36050_write(ptr, ZR050_MODE, ++ ZR050_MO_COMP | ZR050_MO_PASS2 | ++ (ptr->bitrate_ctrl ? ZR050_MO_BRC : 0)); ++ ++ /* this headers seem to deliver "valid AVI" jpeg frames */ ++ zr36050_write(ptr, ZR050_MARKERS_EN, ++ ZR050_ME_DQT | ZR050_ME_DHT | ++ ((ptr->app.len > 0) ? ZR050_ME_APP : 0) | ++ ((ptr->com.len > 0) ? ZR050_ME_COM : 0)); ++ } else { ++ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); ++ ++ /* 050 communicates with 055 in master mode */ ++ zr36050_write(ptr, ZR050_HARDWARE, ++ ZR050_HW_MSTR | ZR050_HW_CFIS_2_CLK); ++ ++ /* encoding table preload */ ++ zr36050_write(ptr, ZR050_MODE, ZR050_MO_TLM); ++ ++ /* disable all IRQs */ ++ zr36050_write(ptr, ZR050_INT_REQ_0, 0); ++ zr36050_write(ptr, ZR050_INT_REQ_1, 3); // low 2 bits always 1 ++ ++ dprintk(3, "%s: write DHT\n", ptr->name); ++ zr36050_pushit(ptr, ZR050_DHT_IDX, sizeof(zr36050_dht), ++ zr36050_dht); ++ ++ /* do the internal huffman table preload */ ++ zr36050_write(ptr, ZR050_MARKERS_EN, ZR050_ME_DHTI); ++ ++ zr36050_write(ptr, ZR050_GO, 1); // launch codec ++ zr36050_wait_end(ptr); ++ dprintk(2, "%s: Status after table preload: 0x%02x\n", ++ ptr->name, ptr->status1); ++ ++ if ((ptr->status1 & 0x4) == 0) { ++ dprintk(1, KERN_ERR "%s: init aborted!\n", ++ ptr->name); ++ return; // something is wrong, its timed out!!!! ++ } ++ ++ /* setup misc. data for expansion */ ++ zr36050_write(ptr, ZR050_MODE, 0); ++ zr36050_write(ptr, ZR050_MARKERS_EN, 0); ++ } ++ ++ /* adr on selected, to allow GO from master */ ++ zr36050_read(ptr, 0); ++} ++ ++/* ========================================================================= ++ CODEC API FUNCTIONS ++ ++ this functions are accessed by the master via the API structure ++ ========================================================================= */ ++ ++/* set compression/expansion mode and launches codec - ++ this should be the last call from the master before starting processing */ ++static int ++zr36050_set_mode (struct videocodec *codec, ++ int mode) ++{ ++ struct zr36050 *ptr = (struct zr36050 *) codec->data; ++ ++ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); ++ ++ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) ++ return -EINVAL; ++ ++ ptr->mode = mode; ++ zr36050_init(ptr); ++ ++ return 0; ++} ++ ++/* set picture size (norm is ignored as the codec doesn't know about it) */ ++static int ++zr36050_set_video (struct videocodec *codec, ++ struct tvnorm *norm, ++ struct vfe_settings *cap, ++ struct vfe_polarity *pol) ++{ ++ struct zr36050 *ptr = (struct zr36050 *) codec->data; ++ int size; ++ ++ dprintk(2, "%s: set_video %d.%d, %d/%d-%dx%d (0x%x) q%d call\n", ++ ptr->name, norm->HStart, norm->VStart, ++ cap->x, cap->y, cap->width, cap->height, ++ cap->decimation, cap->quality); ++ /* if () return -EINVAL; ++ * trust the master driver that it knows what it does - so ++ * we allow invalid startx/y and norm for now ... */ ++ ptr->width = cap->width / (cap->decimation & 0xff); ++ ptr->height = cap->height / ((cap->decimation >> 8) & 0xff); ++ ++ /* (KM) JPEG quality */ ++ size = ptr->width * ptr->height; ++ size *= 16; /* size in bits */ ++ /* apply quality setting */ ++ size = size * cap->quality / 200; ++ ++ /* Minimum: 1kb */ ++ if (size < 8192) ++ size = 8192; ++ /* Maximum: 7/8 of code buffer */ ++ if (size > ptr->total_code_vol * 7) ++ size = ptr->total_code_vol * 7; ++ ++ ptr->real_code_vol = size >> 3; /* in bytes */ ++ ++ /* Set max_block_vol here (previously in zr36050_init, moved ++ * here for consistency with zr36060 code */ ++ zr36050_write(ptr, ZR050_MBCV, ptr->max_block_vol); ++ ++ return 0; ++} ++ ++/* additional control functions */ ++static int ++zr36050_control (struct videocodec *codec, ++ int type, ++ int size, ++ void *data) ++{ ++ struct zr36050 *ptr = (struct zr36050 *) codec->data; ++ int *ival = (int *) data; ++ ++ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, ++ size); ++ ++ switch (type) { ++ case CODEC_G_STATUS: /* get last status */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ zr36050_read_status1(ptr); ++ *ival = ptr->status1; ++ break; ++ ++ case CODEC_G_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = CODEC_MODE_BJPG; ++ break; ++ ++ case CODEC_S_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ if (*ival != CODEC_MODE_BJPG) ++ return -EINVAL; ++ /* not needed, do nothing */ ++ return 0; ++ ++ case CODEC_G_VFE: ++ case CODEC_S_VFE: ++ /* not needed, do nothing */ ++ return 0; ++ ++ case CODEC_S_MMAP: ++ /* not available, give an error */ ++ return -ENXIO; ++ ++ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = ptr->total_code_vol; ++ break; ++ ++ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ ptr->total_code_vol = *ival; ++ /* (Kieran Morrissey) ++ * code copied from zr36060.c to ensure proper bitrate */ ++ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; ++ break; ++ ++ case CODEC_G_JPEG_SCALE: /* get scaling factor */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = zr36050_read_scalefactor(ptr); ++ break; ++ ++ case CODEC_S_JPEG_SCALE: /* set scaling factor */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ ptr->scalefact = *ival; ++ break; ++ ++ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ ++ struct jpeg_app_marker *app = data; ++ ++ if (size != sizeof(struct jpeg_app_marker)) ++ return -EFAULT; ++ ++ *app = ptr->app; ++ break; ++ } ++ ++ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ ++ struct jpeg_app_marker *app = data; ++ ++ if (size != sizeof(struct jpeg_app_marker)) ++ return -EFAULT; ++ ++ ptr->app = *app; ++ break; ++ } ++ ++ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ ++ struct jpeg_com_marker *com = data; ++ ++ if (size != sizeof(struct jpeg_com_marker)) ++ return -EFAULT; ++ ++ *com = ptr->com; ++ break; ++ } ++ ++ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ ++ struct jpeg_com_marker *com = data; ++ ++ if (size != sizeof(struct jpeg_com_marker)) ++ return -EFAULT; ++ ++ ptr->com = *com; ++ break; ++ } ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return size; ++} ++ ++/* ========================================================================= ++ Exit and unregister function: ++ ++ Deinitializes Zoran's JPEG processor ++ ========================================================================= */ ++ ++static int ++zr36050_unset (struct videocodec *codec) ++{ ++ struct zr36050 *ptr = codec->data; ++ ++ if (ptr) { ++ /* do wee need some codec deinit here, too ???? */ ++ ++ dprintk(1, "%s: finished codec #%d\n", ptr->name, ++ ptr->num); ++ kfree(ptr); ++ codec->data = NULL; ++ ++ zr36050_codecs--; ++ return 0; ++ } ++ ++ return -EFAULT; ++} ++ ++/* ========================================================================= ++ Setup and registry function: ++ ++ Initializes Zoran's JPEG processor ++ ++ Also sets pixel size, average code size, mode (compr./decompr.) ++ (the given size is determined by the processor with the video interface) ++ ========================================================================= */ ++ ++static int ++zr36050_setup (struct videocodec *codec) ++{ ++ struct zr36050 *ptr; ++ int res; ++ ++ dprintk(2, "zr36050: initializing MJPEG subsystem #%d.\n", ++ zr36050_codecs); ++ ++ if (zr36050_codecs == MAX_CODECS) { ++ dprintk(1, ++ KERN_ERR "zr36050: Can't attach more codecs!\n"); ++ return -ENOSPC; ++ } ++ //mem structure init ++ codec->data = ptr = kzalloc(sizeof(struct zr36050), GFP_KERNEL); ++ if (NULL == ptr) { ++ dprintk(1, KERN_ERR "zr36050: Can't get enough memory!\n"); ++ return -ENOMEM; ++ } ++ ++ snprintf(ptr->name, sizeof(ptr->name), "zr36050[%d]", ++ zr36050_codecs); ++ ptr->num = zr36050_codecs++; ++ ptr->codec = codec; ++ ++ //testing ++ res = zr36050_basic_test(ptr); ++ if (res < 0) { ++ zr36050_unset(codec); ++ return res; ++ } ++ //final setup ++ memcpy(ptr->h_samp_ratio, zr36050_decimation_h, 8); ++ memcpy(ptr->v_samp_ratio, zr36050_decimation_v, 8); ++ ++ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag ++ * (what is the difference?) */ ++ ptr->mode = CODEC_DO_COMPRESSION; ++ ptr->width = 384; ++ ptr->height = 288; ++ ptr->total_code_vol = 16000; ++ ptr->max_block_vol = 240; ++ ptr->scalefact = 0x100; ++ ptr->dri = 1; ++ ++ /* no app/com marker by default */ ++ ptr->app.appn = 0; ++ ptr->app.len = 0; ++ ptr->com.len = 0; ++ ++ zr36050_init(ptr); ++ ++ dprintk(1, KERN_INFO "%s: codec attached and running\n", ++ ptr->name); ++ ++ return 0; ++} ++ ++static const struct videocodec zr36050_codec = { ++ .owner = THIS_MODULE, ++ .name = "zr36050", ++ .magic = 0L, // magic not used ++ .flags = ++ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | ++ CODEC_FLAG_DECODER, ++ .type = CODEC_TYPE_ZR36050, ++ .setup = zr36050_setup, // functionality ++ .unset = zr36050_unset, ++ .set_mode = zr36050_set_mode, ++ .set_video = zr36050_set_video, ++ .control = zr36050_control, ++ // others are not used ++}; ++ ++/* ========================================================================= ++ HOOK IN DRIVER AS KERNEL MODULE ++ ========================================================================= */ ++ ++static int __init ++zr36050_init_module (void) ++{ ++ //dprintk(1, "ZR36050 driver %s\n",ZR050_VERSION); ++ zr36050_codecs = 0; ++ return videocodec_register(&zr36050_codec); ++} ++ ++static void __exit ++zr36050_cleanup_module (void) ++{ ++ if (zr36050_codecs) { ++ dprintk(1, ++ "zr36050: something's wrong - %d codecs left somehow.\n", ++ zr36050_codecs); ++ } ++ videocodec_unregister(&zr36050_codec); ++} ++ ++module_init(zr36050_init_module); ++module_exit(zr36050_cleanup_module); ++ ++MODULE_AUTHOR("Wolfgang Scherr "); ++MODULE_DESCRIPTION("Driver module for ZR36050 jpeg processors " ++ ZR050_VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/zoran/zr36050.h b/drivers/media/pci/zoran/zr36050.h +new file mode 100644 +index 0000000..9f52f0c +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36050.h +@@ -0,0 +1,184 @@ ++/* ++ * Zoran ZR36050 basic configuration functions - header file ++ * ++ * Copyright (C) 2001 Wolfgang Scherr ++ * ++ * $Id: zr36050.h,v 1.1.2.2 2003/01/14 21:18:22 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ------------------------------------------------------------------------ ++ */ ++ ++#ifndef ZR36050_H ++#define ZR36050_H ++ ++#include "videocodec.h" ++ ++/* data stored for each zoran jpeg codec chip */ ++struct zr36050 { ++ char name[32]; ++ int num; ++ /* io datastructure */ ++ struct videocodec *codec; ++ // last coder status ++ __u8 status1; ++ // actual coder setup ++ int mode; ++ ++ __u16 width; ++ __u16 height; ++ ++ __u16 bitrate_ctrl; ++ ++ __u32 total_code_vol; ++ __u32 real_code_vol; ++ __u16 max_block_vol; ++ ++ __u8 h_samp_ratio[8]; ++ __u8 v_samp_ratio[8]; ++ __u16 scalefact; ++ __u16 dri; ++ ++ /* com/app marker */ ++ struct jpeg_com_marker com; ++ struct jpeg_app_marker app; ++}; ++ ++/* zr36050 register addresses */ ++#define ZR050_GO 0x000 ++#define ZR050_HARDWARE 0x002 ++#define ZR050_MODE 0x003 ++#define ZR050_OPTIONS 0x004 ++#define ZR050_MBCV 0x005 ++#define ZR050_MARKERS_EN 0x006 ++#define ZR050_INT_REQ_0 0x007 ++#define ZR050_INT_REQ_1 0x008 ++#define ZR050_TCV_NET_HI 0x009 ++#define ZR050_TCV_NET_MH 0x00a ++#define ZR050_TCV_NET_ML 0x00b ++#define ZR050_TCV_NET_LO 0x00c ++#define ZR050_TCV_DATA_HI 0x00d ++#define ZR050_TCV_DATA_MH 0x00e ++#define ZR050_TCV_DATA_ML 0x00f ++#define ZR050_TCV_DATA_LO 0x010 ++#define ZR050_SF_HI 0x011 ++#define ZR050_SF_LO 0x012 ++#define ZR050_AF_HI 0x013 ++#define ZR050_AF_M 0x014 ++#define ZR050_AF_LO 0x015 ++#define ZR050_ACV_HI 0x016 ++#define ZR050_ACV_MH 0x017 ++#define ZR050_ACV_ML 0x018 ++#define ZR050_ACV_LO 0x019 ++#define ZR050_ACT_HI 0x01a ++#define ZR050_ACT_MH 0x01b ++#define ZR050_ACT_ML 0x01c ++#define ZR050_ACT_LO 0x01d ++#define ZR050_ACV_TRUN_HI 0x01e ++#define ZR050_ACV_TRUN_MH 0x01f ++#define ZR050_ACV_TRUN_ML 0x020 ++#define ZR050_ACV_TRUN_LO 0x021 ++#define ZR050_STATUS_0 0x02e ++#define ZR050_STATUS_1 0x02f ++ ++#define ZR050_SOF_IDX 0x040 ++#define ZR050_SOS1_IDX 0x07a ++#define ZR050_SOS2_IDX 0x08a ++#define ZR050_SOS3_IDX 0x09a ++#define ZR050_SOS4_IDX 0x0aa ++#define ZR050_DRI_IDX 0x0c0 ++#define ZR050_DNL_IDX 0x0c6 ++#define ZR050_DQT_IDX 0x0cc ++#define ZR050_DHT_IDX 0x1d4 ++#define ZR050_APP_IDX 0x380 ++#define ZR050_COM_IDX 0x3c0 ++ ++/* zr36050 hardware register bits */ ++ ++#define ZR050_HW_BSWD 0x80 ++#define ZR050_HW_MSTR 0x40 ++#define ZR050_HW_DMA 0x20 ++#define ZR050_HW_CFIS_1_CLK 0x00 ++#define ZR050_HW_CFIS_2_CLK 0x04 ++#define ZR050_HW_CFIS_3_CLK 0x08 ++#define ZR050_HW_CFIS_4_CLK 0x0C ++#define ZR050_HW_CFIS_5_CLK 0x10 ++#define ZR050_HW_CFIS_6_CLK 0x14 ++#define ZR050_HW_CFIS_7_CLK 0x18 ++#define ZR050_HW_CFIS_8_CLK 0x1C ++#define ZR050_HW_BELE 0x01 ++ ++/* zr36050 mode register bits */ ++ ++#define ZR050_MO_COMP 0x80 ++#define ZR050_MO_COMP 0x80 ++#define ZR050_MO_ATP 0x40 ++#define ZR050_MO_PASS2 0x20 ++#define ZR050_MO_TLM 0x10 ++#define ZR050_MO_DCONLY 0x08 ++#define ZR050_MO_BRC 0x04 ++ ++#define ZR050_MO_ATP 0x40 ++#define ZR050_MO_PASS2 0x20 ++#define ZR050_MO_TLM 0x10 ++#define ZR050_MO_DCONLY 0x08 ++ ++/* zr36050 option register bits */ ++ ++#define ZR050_OP_NSCN_1 0x00 ++#define ZR050_OP_NSCN_2 0x20 ++#define ZR050_OP_NSCN_3 0x40 ++#define ZR050_OP_NSCN_4 0x60 ++#define ZR050_OP_NSCN_5 0x80 ++#define ZR050_OP_NSCN_6 0xA0 ++#define ZR050_OP_NSCN_7 0xC0 ++#define ZR050_OP_NSCN_8 0xE0 ++#define ZR050_OP_OVF 0x10 ++ ++ ++/* zr36050 markers-enable register bits */ ++ ++#define ZR050_ME_APP 0x80 ++#define ZR050_ME_COM 0x40 ++#define ZR050_ME_DRI 0x20 ++#define ZR050_ME_DQT 0x10 ++#define ZR050_ME_DHT 0x08 ++#define ZR050_ME_DNL 0x04 ++#define ZR050_ME_DQTI 0x02 ++#define ZR050_ME_DHTI 0x01 ++ ++/* zr36050 status0/1 register bit masks */ ++ ++#define ZR050_ST_RST_MASK 0x20 ++#define ZR050_ST_SOF_MASK 0x02 ++#define ZR050_ST_SOS_MASK 0x02 ++#define ZR050_ST_DATRDY_MASK 0x80 ++#define ZR050_ST_MRKDET_MASK 0x40 ++#define ZR050_ST_RFM_MASK 0x10 ++#define ZR050_ST_RFD_MASK 0x08 ++#define ZR050_ST_END_MASK 0x04 ++#define ZR050_ST_TCVOVF_MASK 0x02 ++#define ZR050_ST_DATOVF_MASK 0x01 ++ ++/* pixel component idx */ ++ ++#define ZR050_Y_COMPONENT 0 ++#define ZR050_U_COMPONENT 1 ++#define ZR050_V_COMPONENT 2 ++ ++#endif /*fndef ZR36050_H */ +diff --git a/drivers/media/pci/zoran/zr36057.h b/drivers/media/pci/zoran/zr36057.h +new file mode 100644 +index 0000000..54c9362 +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36057.h +@@ -0,0 +1,168 @@ ++/* ++ * zr36057.h - zr36057 register offsets ++ * ++ * Copyright (C) 1998 Dave Perks ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef _ZR36057_H_ ++#define _ZR36057_H_ ++ ++ ++/* Zoran ZR36057 registers */ ++ ++#define ZR36057_VFEHCR 0x000 /* Video Front End, Horizontal Configuration Register */ ++#define ZR36057_VFEHCR_HSPol (1<<30) ++#define ZR36057_VFEHCR_HStart 10 ++#define ZR36057_VFEHCR_HEnd 0 ++#define ZR36057_VFEHCR_Hmask 0x3ff ++ ++#define ZR36057_VFEVCR 0x004 /* Video Front End, Vertical Configuration Register */ ++#define ZR36057_VFEVCR_VSPol (1<<30) ++#define ZR36057_VFEVCR_VStart 10 ++#define ZR36057_VFEVCR_VEnd 0 ++#define ZR36057_VFEVCR_Vmask 0x3ff ++ ++#define ZR36057_VFESPFR 0x008 /* Video Front End, Scaler and Pixel Format Register */ ++#define ZR36057_VFESPFR_ExtFl (1<<26) ++#define ZR36057_VFESPFR_TopField (1<<25) ++#define ZR36057_VFESPFR_VCLKPol (1<<24) ++#define ZR36057_VFESPFR_HFilter 21 ++#define ZR36057_VFESPFR_HorDcm 14 ++#define ZR36057_VFESPFR_VerDcm 8 ++#define ZR36057_VFESPFR_DispMode 6 ++#define ZR36057_VFESPFR_YUV422 (0<<3) ++#define ZR36057_VFESPFR_RGB888 (1<<3) ++#define ZR36057_VFESPFR_RGB565 (2<<3) ++#define ZR36057_VFESPFR_RGB555 (3<<3) ++#define ZR36057_VFESPFR_ErrDif (1<<2) ++#define ZR36057_VFESPFR_Pack24 (1<<1) ++#define ZR36057_VFESPFR_LittleEndian (1<<0) ++ ++#define ZR36057_VDTR 0x00c /* Video Display "Top" Register */ ++ ++#define ZR36057_VDBR 0x010 /* Video Display "Bottom" Register */ ++ ++#define ZR36057_VSSFGR 0x014 /* Video Stride, Status, and Frame Grab Register */ ++#define ZR36057_VSSFGR_DispStride 16 ++#define ZR36057_VSSFGR_VidOvf (1<<8) ++#define ZR36057_VSSFGR_SnapShot (1<<1) ++#define ZR36057_VSSFGR_FrameGrab (1<<0) ++ ++#define ZR36057_VDCR 0x018 /* Video Display Configuration Register */ ++#define ZR36057_VDCR_VidEn (1<<31) ++#define ZR36057_VDCR_MinPix 24 ++#define ZR36057_VDCR_Triton (1<<24) ++#define ZR36057_VDCR_VidWinHt 12 ++#define ZR36057_VDCR_VidWinWid 0 ++ ++#define ZR36057_MMTR 0x01c /* Masking Map "Top" Register */ ++ ++#define ZR36057_MMBR 0x020 /* Masking Map "Bottom" Register */ ++ ++#define ZR36057_OCR 0x024 /* Overlay Control Register */ ++#define ZR36057_OCR_OvlEnable (1 << 15) ++#define ZR36057_OCR_MaskStride 0 ++ ++#define ZR36057_SPGPPCR 0x028 /* System, PCI, and General Purpose Pins Control Register */ ++#define ZR36057_SPGPPCR_SoftReset (1<<24) ++ ++#define ZR36057_GPPGCR1 0x02c /* General Purpose Pins and GuestBus Control Register (1) */ ++ ++#define ZR36057_MCSAR 0x030 /* MPEG Code Source Address Register */ ++ ++#define ZR36057_MCTCR 0x034 /* MPEG Code Transfer Control Register */ ++#define ZR36057_MCTCR_CodTime (1 << 30) ++#define ZR36057_MCTCR_CEmpty (1 << 29) ++#define ZR36057_MCTCR_CFlush (1 << 28) ++#define ZR36057_MCTCR_CodGuestID 20 ++#define ZR36057_MCTCR_CodGuestReg 16 ++ ++#define ZR36057_MCMPR 0x038 /* MPEG Code Memory Pointer Register */ ++ ++#define ZR36057_ISR 0x03c /* Interrupt Status Register */ ++#define ZR36057_ISR_GIRQ1 (1<<30) ++#define ZR36057_ISR_GIRQ0 (1<<29) ++#define ZR36057_ISR_CodRepIRQ (1<<28) ++#define ZR36057_ISR_JPEGRepIRQ (1<<27) ++ ++#define ZR36057_ICR 0x040 /* Interrupt Control Register */ ++#define ZR36057_ICR_GIRQ1 (1<<30) ++#define ZR36057_ICR_GIRQ0 (1<<29) ++#define ZR36057_ICR_CodRepIRQ (1<<28) ++#define ZR36057_ICR_JPEGRepIRQ (1<<27) ++#define ZR36057_ICR_IntPinEn (1<<24) ++ ++#define ZR36057_I2CBR 0x044 /* I2C Bus Register */ ++#define ZR36057_I2CBR_SDA (1<<1) ++#define ZR36057_I2CBR_SCL (1<<0) ++ ++#define ZR36057_JMC 0x100 /* JPEG Mode and Control */ ++#define ZR36057_JMC_JPG (1 << 31) ++#define ZR36057_JMC_JPGExpMode (0 << 29) ++#define ZR36057_JMC_JPGCmpMode (1 << 29) ++#define ZR36057_JMC_MJPGExpMode (2 << 29) ++#define ZR36057_JMC_MJPGCmpMode (3 << 29) ++#define ZR36057_JMC_RTBUSY_FB (1 << 6) ++#define ZR36057_JMC_Go_en (1 << 5) ++#define ZR36057_JMC_SyncMstr (1 << 4) ++#define ZR36057_JMC_Fld_per_buff (1 << 3) ++#define ZR36057_JMC_VFIFO_FB (1 << 2) ++#define ZR36057_JMC_CFIFO_FB (1 << 1) ++#define ZR36057_JMC_Stll_LitEndian (1 << 0) ++ ++#define ZR36057_JPC 0x104 /* JPEG Process Control */ ++#define ZR36057_JPC_P_Reset (1 << 7) ++#define ZR36057_JPC_CodTrnsEn (1 << 5) ++#define ZR36057_JPC_Active (1 << 0) ++ ++#define ZR36057_VSP 0x108 /* Vertical Sync Parameters */ ++#define ZR36057_VSP_VsyncSize 16 ++#define ZR36057_VSP_FrmTot 0 ++ ++#define ZR36057_HSP 0x10c /* Horizontal Sync Parameters */ ++#define ZR36057_HSP_HsyncStart 16 ++#define ZR36057_HSP_LineTot 0 ++ ++#define ZR36057_FHAP 0x110 /* Field Horizontal Active Portion */ ++#define ZR36057_FHAP_NAX 16 ++#define ZR36057_FHAP_PAX 0 ++ ++#define ZR36057_FVAP 0x114 /* Field Vertical Active Portion */ ++#define ZR36057_FVAP_NAY 16 ++#define ZR36057_FVAP_PAY 0 ++ ++#define ZR36057_FPP 0x118 /* Field Process Parameters */ ++#define ZR36057_FPP_Odd_Even (1 << 0) ++ ++#define ZR36057_JCBA 0x11c /* JPEG Code Base Address */ ++ ++#define ZR36057_JCFT 0x120 /* JPEG Code FIFO Threshold */ ++ ++#define ZR36057_JCGI 0x124 /* JPEG Codec Guest ID */ ++#define ZR36057_JCGI_JPEGuestID 4 ++#define ZR36057_JCGI_JPEGuestReg 0 ++ ++#define ZR36057_GCR2 0x12c /* GuestBus Control Register (2) */ ++ ++#define ZR36057_POR 0x200 /* Post Office Register */ ++#define ZR36057_POR_POPen (1<<25) ++#define ZR36057_POR_POTime (1<<24) ++#define ZR36057_POR_PODir (1<<23) ++ ++#define ZR36057_STR 0x300 /* "Still" Transfer Register */ ++ ++#endif +diff --git a/drivers/media/pci/zoran/zr36060.c b/drivers/media/pci/zoran/zr36060.c +new file mode 100644 +index 0000000..f08546f +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36060.c +@@ -0,0 +1,1010 @@ ++/* ++ * Zoran ZR36060 basic configuration functions ++ * ++ * Copyright (C) 2002 Laurent Pinchart ++ * ++ * $Id: zr36060.c,v 1.1.2.22 2003/05/06 09:35:36 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * 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 ZR060_VERSION "v0.7" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* I/O commands, error codes */ ++#include ++ ++/* headerfile of this module */ ++#include "zr36060.h" ++ ++/* codec io API */ ++#include "videocodec.h" ++ ++/* it doesn't make sense to have more than 20 or so, ++ just to prevent some unwanted loops */ ++#define MAX_CODECS 20 ++ ++/* amount of chips attached via this driver */ ++static int zr36060_codecs; ++ ++static bool low_bitrate; ++module_param(low_bitrate, bool, 0); ++MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); ++ ++/* debugging is available via module parameter */ ++static int debug; ++module_param(debug, int, 0); ++MODULE_PARM_DESC(debug, "Debug level (0-4)"); ++ ++#define dprintk(num, format, args...) \ ++ do { \ ++ if (debug >= num) \ ++ printk(format, ##args); \ ++ } while (0) ++ ++/* ========================================================================= ++ Local hardware I/O functions: ++ ++ read/write via codec layer (registers are located in the master device) ++ ========================================================================= */ ++ ++/* read and write functions */ ++static u8 ++zr36060_read (struct zr36060 *ptr, ++ u16 reg) ++{ ++ u8 value = 0; ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->readreg) ++ value = (ptr->codec->master_data->readreg(ptr->codec, ++ reg)) & 0xff; ++ else ++ dprintk(1, ++ KERN_ERR "%s: invalid I/O setup, nothing read!\n", ++ ptr->name); ++ ++ //dprintk(4, "%s: reading from 0x%04x: %02x\n",ptr->name,reg,value); ++ ++ return value; ++} ++ ++static void ++zr36060_write(struct zr36060 *ptr, ++ u16 reg, ++ u8 value) ++{ ++ //dprintk(4, "%s: writing 0x%02x to 0x%04x\n",ptr->name,value,reg); ++ dprintk(4, "0x%02x @0x%04x\n", value, reg); ++ ++ // just in case something is wrong... ++ if (ptr->codec->master_data->writereg) ++ ptr->codec->master_data->writereg(ptr->codec, reg, value); ++ else ++ dprintk(1, ++ KERN_ERR ++ "%s: invalid I/O setup, nothing written!\n", ++ ptr->name); ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ status read ++ ========================================================================= */ ++ ++/* status is kept in datastructure */ ++static u8 ++zr36060_read_status (struct zr36060 *ptr) ++{ ++ ptr->status = zr36060_read(ptr, ZR060_CFSR); ++ ++ zr36060_read(ptr, 0); ++ return ptr->status; ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ scale factor read ++ ========================================================================= */ ++ ++/* scale factor is kept in datastructure */ ++static u16 ++zr36060_read_scalefactor (struct zr36060 *ptr) ++{ ++ ptr->scalefact = (zr36060_read(ptr, ZR060_SF_HI) << 8) | ++ (zr36060_read(ptr, ZR060_SF_LO) & 0xFF); ++ ++ /* leave 0 selected for an eventually GO from master */ ++ zr36060_read(ptr, 0); ++ return ptr->scalefact; ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ wait if codec is ready to proceed (end of processing) or time is over ++ ========================================================================= */ ++ ++static void ++zr36060_wait_end (struct zr36060 *ptr) ++{ ++ int i = 0; ++ ++ while (zr36060_read_status(ptr) & ZR060_CFSR_Busy) { ++ udelay(1); ++ if (i++ > 200000) { // 200ms, there is for sure something wrong!!! ++ dprintk(1, ++ "%s: timeout at wait_end (last status: 0x%02x)\n", ++ ptr->name, ptr->status); ++ break; ++ } ++ } ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ basic test of "connectivity", writes/reads to/from memory the SOF marker ++ ========================================================================= */ ++ ++static int ++zr36060_basic_test (struct zr36060 *ptr) ++{ ++ if ((zr36060_read(ptr, ZR060_IDR_DEV) != 0x33) && ++ (zr36060_read(ptr, ZR060_IDR_REV) != 0x01)) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, can't connect to jpeg processor!\n", ++ ptr->name); ++ return -ENXIO; ++ } ++ ++ zr36060_wait_end(ptr); ++ if (ptr->status & ZR060_CFSR_Busy) { ++ dprintk(1, ++ KERN_ERR ++ "%s: attach failed, jpeg processor failed (end flag)!\n", ++ ptr->name); ++ return -EBUSY; ++ } ++ ++ return 0; /* looks good! */ ++} ++ ++/* ========================================================================= ++ Local helper function: ++ ++ simple loop for pushing the init datasets ++ ========================================================================= */ ++ ++static int ++zr36060_pushit (struct zr36060 *ptr, ++ u16 startreg, ++ u16 len, ++ const char *data) ++{ ++ int i = 0; ++ ++ dprintk(4, "%s: write data block to 0x%04x (len=%d)\n", ptr->name, ++ startreg, len); ++ while (i < len) { ++ zr36060_write(ptr, startreg++, data[i++]); ++ } ++ ++ return i; ++} ++ ++/* ========================================================================= ++ Basic datasets: ++ ++ jpeg baseline setup data (you find it on lots places in internet, or just ++ extract it from any regular .jpg image...) ++ ++ Could be variable, but until it's not needed it they are just fixed to save ++ memory. Otherwise expand zr36060 structure with arrays, push the values to ++ it and initialize from there, as e.g. the linux zr36057/60 driver does it. ++ ========================================================================= */ ++ ++static const char zr36060_dqt[0x86] = { ++ 0xff, 0xdb, //Marker: DQT ++ 0x00, 0x84, //Length: 2*65+2 ++ 0x00, //Pq,Tq first table ++ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, ++ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, ++ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, ++ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, ++ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, ++ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, ++ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, ++ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, ++ 0x01, //Pq,Tq second table ++ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, ++ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, ++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63 ++}; ++ ++static const char zr36060_dht[0x1a4] = { ++ 0xff, 0xc4, //Marker: DHT ++ 0x01, 0xa2, //Length: 2*AC, 2*DC ++ 0x00, //DC first table ++ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, ++ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ++ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ++ 0x01, //DC second table ++ 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ++ 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ++ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, ++ 0x10, //AC first table ++ 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, ++ 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, ++ 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, ++ 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, ++ 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, ++ 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, ++ 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, ++ 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, ++ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, ++ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, ++ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, ++ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, ++ 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, ++ 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, ++ 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, ++ 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, ++ 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, ++ 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, ++ 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, ++ 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, ++ 0xF8, 0xF9, 0xFA, ++ 0x11, //AC second table ++ 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, ++ 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, ++ 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, ++ 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, ++ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, ++ 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, ++ 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, ++ 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, ++ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, ++ 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, ++ 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, ++ 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, ++ 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, ++ 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, ++ 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, ++ 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, ++ 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, ++ 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, ++ 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, ++ 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, ++ 0xF9, 0xFA ++}; ++ ++/* jpeg baseline setup, this is just fixed in this driver (YUV pictures) */ ++#define NO_OF_COMPONENTS 0x3 //Y,U,V ++#define BASELINE_PRECISION 0x8 //MCU size (?) ++static const char zr36060_tq[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's QT ++static const char zr36060_td[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's DC ++static const char zr36060_ta[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; //table idx's AC ++ ++/* horizontal 422 decimation setup (maybe we support 411 or so later, too) */ ++static const char zr36060_decimation_h[8] = { 2, 1, 1, 0, 0, 0, 0, 0 }; ++static const char zr36060_decimation_v[8] = { 1, 1, 1, 0, 0, 0, 0, 0 }; ++ ++/* ========================================================================= ++ Local helper functions: ++ ++ calculation and setup of parameter-dependent JPEG baseline segments ++ (needed for compression only) ++ ========================================================================= */ ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* SOF (start of frame) segment depends on width, height and sampling ratio ++ of each color component */ ++ ++static int ++zr36060_set_sof (struct zr36060 *ptr) ++{ ++ char sof_data[34]; // max. size of register set ++ int i; ++ ++ dprintk(3, "%s: write SOF (%dx%d, %d components)\n", ptr->name, ++ ptr->width, ptr->height, NO_OF_COMPONENTS); ++ sof_data[0] = 0xff; ++ sof_data[1] = 0xc0; ++ sof_data[2] = 0x00; ++ sof_data[3] = (3 * NO_OF_COMPONENTS) + 8; ++ sof_data[4] = BASELINE_PRECISION; // only '8' possible with zr36060 ++ sof_data[5] = (ptr->height) >> 8; ++ sof_data[6] = (ptr->height) & 0xff; ++ sof_data[7] = (ptr->width) >> 8; ++ sof_data[8] = (ptr->width) & 0xff; ++ sof_data[9] = NO_OF_COMPONENTS; ++ for (i = 0; i < NO_OF_COMPONENTS; i++) { ++ sof_data[10 + (i * 3)] = i; // index identifier ++ sof_data[11 + (i * 3)] = (ptr->h_samp_ratio[i] << 4) | ++ (ptr->v_samp_ratio[i]); // sampling ratios ++ sof_data[12 + (i * 3)] = zr36060_tq[i]; // Q table selection ++ } ++ return zr36060_pushit(ptr, ZR060_SOF_IDX, ++ (3 * NO_OF_COMPONENTS) + 10, sof_data); ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* SOS (start of scan) segment depends on the used scan components ++ of each color component */ ++ ++static int ++zr36060_set_sos (struct zr36060 *ptr) ++{ ++ char sos_data[16]; // max. size of register set ++ int i; ++ ++ dprintk(3, "%s: write SOS\n", ptr->name); ++ sos_data[0] = 0xff; ++ sos_data[1] = 0xda; ++ sos_data[2] = 0x00; ++ sos_data[3] = 2 + 1 + (2 * NO_OF_COMPONENTS) + 3; ++ sos_data[4] = NO_OF_COMPONENTS; ++ for (i = 0; i < NO_OF_COMPONENTS; i++) { ++ sos_data[5 + (i * 2)] = i; // index ++ sos_data[6 + (i * 2)] = (zr36060_td[i] << 4) | ++ zr36060_ta[i]; // AC/DC tbl.sel. ++ } ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 2] = 00; // scan start ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 3] = 0x3f; ++ sos_data[2 + 1 + (2 * NO_OF_COMPONENTS) + 4] = 00; ++ return zr36060_pushit(ptr, ZR060_SOS_IDX, ++ 4 + 1 + (2 * NO_OF_COMPONENTS) + 3, ++ sos_data); ++} ++ ++/* ------------------------------------------------------------------------- */ ++ ++/* DRI (define restart interval) */ ++ ++static int ++zr36060_set_dri (struct zr36060 *ptr) ++{ ++ char dri_data[6]; // max. size of register set ++ ++ dprintk(3, "%s: write DRI\n", ptr->name); ++ dri_data[0] = 0xff; ++ dri_data[1] = 0xdd; ++ dri_data[2] = 0x00; ++ dri_data[3] = 0x04; ++ dri_data[4] = (ptr->dri) >> 8; ++ dri_data[5] = (ptr->dri) & 0xff; ++ return zr36060_pushit(ptr, ZR060_DRI_IDX, 6, dri_data); ++} ++ ++/* ========================================================================= ++ Setup function: ++ ++ Setup compression/decompression of Zoran's JPEG processor ++ ( see also zoran 36060 manual ) ++ ++ ... sorry for the spaghetti code ... ++ ========================================================================= */ ++static void ++zr36060_init (struct zr36060 *ptr) ++{ ++ int sum = 0; ++ long bitcnt, tmp; ++ ++ if (ptr->mode == CODEC_DO_COMPRESSION) { ++ dprintk(2, "%s: COMPRESSION SETUP\n", ptr->name); ++ ++ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); ++ ++ /* 060 communicates with 067 in master mode */ ++ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); ++ ++ /* Compression with or without variable scale factor */ ++ /*FIXME: What about ptr->bitrate_ctrl? */ ++ zr36060_write(ptr, ZR060_CMR, ++ ZR060_CMR_Comp | ZR060_CMR_Pass2 | ++ ZR060_CMR_BRB); ++ ++ /* Must be zero */ ++ zr36060_write(ptr, ZR060_MBZ, 0x00); ++ zr36060_write(ptr, ZR060_TCR_HI, 0x00); ++ zr36060_write(ptr, ZR060_TCR_LO, 0x00); ++ ++ /* Disable all IRQs - no DataErr means autoreset */ ++ zr36060_write(ptr, ZR060_IMR, 0); ++ ++ /* volume control settings */ ++ zr36060_write(ptr, ZR060_SF_HI, ptr->scalefact >> 8); ++ zr36060_write(ptr, ZR060_SF_LO, ptr->scalefact & 0xff); ++ ++ zr36060_write(ptr, ZR060_AF_HI, 0xff); ++ zr36060_write(ptr, ZR060_AF_M, 0xff); ++ zr36060_write(ptr, ZR060_AF_LO, 0xff); ++ ++ /* setup the variable jpeg tables */ ++ sum += zr36060_set_sof(ptr); ++ sum += zr36060_set_sos(ptr); ++ sum += zr36060_set_dri(ptr); ++ ++ /* setup the fixed jpeg tables - maybe variable, though - ++ * (see table init section above) */ ++ sum += ++ zr36060_pushit(ptr, ZR060_DQT_IDX, sizeof(zr36060_dqt), ++ zr36060_dqt); ++ sum += ++ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), ++ zr36060_dht); ++ zr36060_write(ptr, ZR060_APP_IDX, 0xff); ++ zr36060_write(ptr, ZR060_APP_IDX + 1, 0xe0 + ptr->app.appn); ++ zr36060_write(ptr, ZR060_APP_IDX + 2, 0x00); ++ zr36060_write(ptr, ZR060_APP_IDX + 3, ptr->app.len + 2); ++ sum += zr36060_pushit(ptr, ZR060_APP_IDX + 4, 60, ++ ptr->app.data) + 4; ++ zr36060_write(ptr, ZR060_COM_IDX, 0xff); ++ zr36060_write(ptr, ZR060_COM_IDX + 1, 0xfe); ++ zr36060_write(ptr, ZR060_COM_IDX + 2, 0x00); ++ zr36060_write(ptr, ZR060_COM_IDX + 3, ptr->com.len + 2); ++ sum += zr36060_pushit(ptr, ZR060_COM_IDX + 4, 60, ++ ptr->com.data) + 4; ++ ++ /* setup misc. data for compression (target code sizes) */ ++ ++ /* size of compressed code to reach without header data */ ++ sum = ptr->real_code_vol - sum; ++ bitcnt = sum << 3; /* need the size in bits */ ++ ++ tmp = bitcnt >> 16; ++ dprintk(3, ++ "%s: code: csize=%d, tot=%d, bit=%ld, highbits=%ld\n", ++ ptr->name, sum, ptr->real_code_vol, bitcnt, tmp); ++ zr36060_write(ptr, ZR060_TCV_NET_HI, tmp >> 8); ++ zr36060_write(ptr, ZR060_TCV_NET_MH, tmp & 0xff); ++ tmp = bitcnt & 0xffff; ++ zr36060_write(ptr, ZR060_TCV_NET_ML, tmp >> 8); ++ zr36060_write(ptr, ZR060_TCV_NET_LO, tmp & 0xff); ++ ++ bitcnt -= bitcnt >> 7; // bits without stuffing ++ bitcnt -= ((bitcnt * 5) >> 6); // bits without eob ++ ++ tmp = bitcnt >> 16; ++ dprintk(3, "%s: code: nettobit=%ld, highnettobits=%ld\n", ++ ptr->name, bitcnt, tmp); ++ zr36060_write(ptr, ZR060_TCV_DATA_HI, tmp >> 8); ++ zr36060_write(ptr, ZR060_TCV_DATA_MH, tmp & 0xff); ++ tmp = bitcnt & 0xffff; ++ zr36060_write(ptr, ZR060_TCV_DATA_ML, tmp >> 8); ++ zr36060_write(ptr, ZR060_TCV_DATA_LO, tmp & 0xff); ++ ++ /* JPEG markers to be included in the compressed stream */ ++ zr36060_write(ptr, ZR060_MER, ++ ZR060_MER_DQT | ZR060_MER_DHT | ++ ((ptr->com.len > 0) ? ZR060_MER_Com : 0) | ++ ((ptr->app.len > 0) ? ZR060_MER_App : 0)); ++ ++ /* Setup the Video Frontend */ ++ /* Limit pixel range to 16..235 as per CCIR-601 */ ++ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); ++ ++ } else { ++ dprintk(2, "%s: EXPANSION SETUP\n", ptr->name); ++ ++ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); ++ ++ /* 060 communicates with 067 in master mode */ ++ zr36060_write(ptr, ZR060_CIR, ZR060_CIR_CodeMstr); ++ ++ /* Decompression */ ++ zr36060_write(ptr, ZR060_CMR, 0); ++ ++ /* Must be zero */ ++ zr36060_write(ptr, ZR060_MBZ, 0x00); ++ zr36060_write(ptr, ZR060_TCR_HI, 0x00); ++ zr36060_write(ptr, ZR060_TCR_LO, 0x00); ++ ++ /* Disable all IRQs - no DataErr means autoreset */ ++ zr36060_write(ptr, ZR060_IMR, 0); ++ ++ /* setup misc. data for expansion */ ++ zr36060_write(ptr, ZR060_MER, 0); ++ ++ /* setup the fixed jpeg tables - maybe variable, though - ++ * (see table init section above) */ ++ zr36060_pushit(ptr, ZR060_DHT_IDX, sizeof(zr36060_dht), ++ zr36060_dht); ++ ++ /* Setup the Video Frontend */ ++ //zr36060_write(ptr, ZR060_VCR, ZR060_VCR_FIExt); ++ //this doesn't seem right and doesn't work... ++ zr36060_write(ptr, ZR060_VCR, ZR060_VCR_Range); ++ } ++ ++ /* Load the tables */ ++ zr36060_write(ptr, ZR060_LOAD, ++ ZR060_LOAD_SyncRst | ZR060_LOAD_Load); ++ zr36060_wait_end(ptr); ++ dprintk(2, "%s: Status after table preload: 0x%02x\n", ptr->name, ++ ptr->status); ++ ++ if (ptr->status & ZR060_CFSR_Busy) { ++ dprintk(1, KERN_ERR "%s: init aborted!\n", ptr->name); ++ return; // something is wrong, its timed out!!!! ++ } ++} ++ ++/* ========================================================================= ++ CODEC API FUNCTIONS ++ ++ this functions are accessed by the master via the API structure ++ ========================================================================= */ ++ ++/* set compression/expansion mode and launches codec - ++ this should be the last call from the master before starting processing */ ++static int ++zr36060_set_mode (struct videocodec *codec, ++ int mode) ++{ ++ struct zr36060 *ptr = (struct zr36060 *) codec->data; ++ ++ dprintk(2, "%s: set_mode %d call\n", ptr->name, mode); ++ ++ if ((mode != CODEC_DO_EXPANSION) && (mode != CODEC_DO_COMPRESSION)) ++ return -EINVAL; ++ ++ ptr->mode = mode; ++ zr36060_init(ptr); ++ ++ return 0; ++} ++ ++/* set picture size (norm is ignored as the codec doesn't know about it) */ ++static int ++zr36060_set_video (struct videocodec *codec, ++ struct tvnorm *norm, ++ struct vfe_settings *cap, ++ struct vfe_polarity *pol) ++{ ++ struct zr36060 *ptr = (struct zr36060 *) codec->data; ++ u32 reg; ++ int size; ++ ++ dprintk(2, "%s: set_video %d/%d-%dx%d (%%%d) call\n", ptr->name, ++ cap->x, cap->y, cap->width, cap->height, cap->decimation); ++ ++ /* if () return -EINVAL; ++ * trust the master driver that it knows what it does - so ++ * we allow invalid startx/y and norm for now ... */ ++ ptr->width = cap->width / (cap->decimation & 0xff); ++ ptr->height = cap->height / (cap->decimation >> 8); ++ ++ zr36060_write(ptr, ZR060_LOAD, ZR060_LOAD_SyncRst); ++ ++ /* Note that VSPol/HSPol bits in zr36060 have the opposite ++ * meaning of their zr360x7 counterparts with the same names ++ * N.b. for VSPol this is only true if FIVEdge = 0 (default, ++ * left unchanged here - in accordance with datasheet). ++ */ ++ reg = (!pol->vsync_pol ? ZR060_VPR_VSPol : 0) ++ | (!pol->hsync_pol ? ZR060_VPR_HSPol : 0) ++ | (pol->field_pol ? ZR060_VPR_FIPol : 0) ++ | (pol->blank_pol ? ZR060_VPR_BLPol : 0) ++ | (pol->subimg_pol ? ZR060_VPR_SImgPol : 0) ++ | (pol->poe_pol ? ZR060_VPR_PoePol : 0) ++ | (pol->pvalid_pol ? ZR060_VPR_PValPol : 0) ++ | (pol->vclk_pol ? ZR060_VPR_VCLKPol : 0); ++ zr36060_write(ptr, ZR060_VPR, reg); ++ ++ reg = 0; ++ switch (cap->decimation & 0xff) { ++ default: ++ case 1: ++ break; ++ ++ case 2: ++ reg |= ZR060_SR_HScale2; ++ break; ++ ++ case 4: ++ reg |= ZR060_SR_HScale4; ++ break; ++ } ++ ++ switch (cap->decimation >> 8) { ++ default: ++ case 1: ++ break; ++ ++ case 2: ++ reg |= ZR060_SR_VScale; ++ break; ++ } ++ zr36060_write(ptr, ZR060_SR, reg); ++ ++ zr36060_write(ptr, ZR060_BCR_Y, 0x00); ++ zr36060_write(ptr, ZR060_BCR_U, 0x80); ++ zr36060_write(ptr, ZR060_BCR_V, 0x80); ++ ++ /* sync generator */ ++ ++ reg = norm->Ht - 1; /* Vtotal */ ++ zr36060_write(ptr, ZR060_SGR_VTOTAL_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SGR_VTOTAL_LO, (reg >> 0) & 0xff); ++ ++ reg = norm->Wt - 1; /* Htotal */ ++ zr36060_write(ptr, ZR060_SGR_HTOTAL_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SGR_HTOTAL_LO, (reg >> 0) & 0xff); ++ ++ reg = 6 - 1; /* VsyncSize */ ++ zr36060_write(ptr, ZR060_SGR_VSYNC, reg); ++ ++ //reg = 30 - 1; /* HsyncSize */ ++///*CP*/ reg = (zr->params.norm == 1 ? 57 : 68); ++ reg = 68; ++ zr36060_write(ptr, ZR060_SGR_HSYNC, reg); ++ ++ reg = norm->VStart - 1; /* BVstart */ ++ zr36060_write(ptr, ZR060_SGR_BVSTART, reg); ++ ++ reg += norm->Ha / 2; /* BVend */ ++ zr36060_write(ptr, ZR060_SGR_BVEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SGR_BVEND_LO, (reg >> 0) & 0xff); ++ ++ reg = norm->HStart - 1; /* BHstart */ ++ zr36060_write(ptr, ZR060_SGR_BHSTART, reg); ++ ++ reg += norm->Wa; /* BHend */ ++ zr36060_write(ptr, ZR060_SGR_BHEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SGR_BHEND_LO, (reg >> 0) & 0xff); ++ ++ /* active area */ ++ reg = cap->y + norm->VStart; /* Vstart */ ++ zr36060_write(ptr, ZR060_AAR_VSTART_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_AAR_VSTART_LO, (reg >> 0) & 0xff); ++ ++ reg += cap->height; /* Vend */ ++ zr36060_write(ptr, ZR060_AAR_VEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_AAR_VEND_LO, (reg >> 0) & 0xff); ++ ++ reg = cap->x + norm->HStart; /* Hstart */ ++ zr36060_write(ptr, ZR060_AAR_HSTART_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_AAR_HSTART_LO, (reg >> 0) & 0xff); ++ ++ reg += cap->width; /* Hend */ ++ zr36060_write(ptr, ZR060_AAR_HEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_AAR_HEND_LO, (reg >> 0) & 0xff); ++ ++ /* subimage area */ ++ reg = norm->VStart - 4; /* SVstart */ ++ zr36060_write(ptr, ZR060_SWR_VSTART_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SWR_VSTART_LO, (reg >> 0) & 0xff); ++ ++ reg += norm->Ha / 2 + 8; /* SVend */ ++ zr36060_write(ptr, ZR060_SWR_VEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SWR_VEND_LO, (reg >> 0) & 0xff); ++ ++ reg = norm->HStart /*+ 64 */ - 4; /* SHstart */ ++ zr36060_write(ptr, ZR060_SWR_HSTART_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SWR_HSTART_LO, (reg >> 0) & 0xff); ++ ++ reg += norm->Wa + 8; /* SHend */ ++ zr36060_write(ptr, ZR060_SWR_HEND_HI, (reg >> 8) & 0xff); ++ zr36060_write(ptr, ZR060_SWR_HEND_LO, (reg >> 0) & 0xff); ++ ++ size = ptr->width * ptr->height; ++ /* Target compressed field size in bits: */ ++ size = size * 16; /* uncompressed size in bits */ ++ /* (Ronald) by default, quality = 100 is a compression ++ * ratio 1:2. Setting low_bitrate (insmod option) sets ++ * it to 1:4 (instead of 1:2, zr36060 max) as limit because the ++ * buz can't handle more at decimation=1... Use low_bitrate if ++ * you have a Buz, unless you know what you're doing */ ++ size = size * cap->quality / (low_bitrate ? 400 : 200); ++ /* Lower limit (arbitrary, 1 KB) */ ++ if (size < 8192) ++ size = 8192; ++ /* Upper limit: 7/8 of the code buffers */ ++ if (size > ptr->total_code_vol * 7) ++ size = ptr->total_code_vol * 7; ++ ++ ptr->real_code_vol = size >> 3; /* in bytes */ ++ ++ /* the MBCVR is the *maximum* block volume, according to the ++ * JPEG ISO specs, this shouldn't be used, since that allows ++ * for the best encoding quality. So set it to it's max value */ ++ reg = ptr->max_block_vol; ++ zr36060_write(ptr, ZR060_MBCVR, reg); ++ ++ return 0; ++} ++ ++/* additional control functions */ ++static int ++zr36060_control (struct videocodec *codec, ++ int type, ++ int size, ++ void *data) ++{ ++ struct zr36060 *ptr = (struct zr36060 *) codec->data; ++ int *ival = (int *) data; ++ ++ dprintk(2, "%s: control %d call with %d byte\n", ptr->name, type, ++ size); ++ ++ switch (type) { ++ case CODEC_G_STATUS: /* get last status */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ zr36060_read_status(ptr); ++ *ival = ptr->status; ++ break; ++ ++ case CODEC_G_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = CODEC_MODE_BJPG; ++ break; ++ ++ case CODEC_S_CODEC_MODE: ++ if (size != sizeof(int)) ++ return -EFAULT; ++ if (*ival != CODEC_MODE_BJPG) ++ return -EINVAL; ++ /* not needed, do nothing */ ++ return 0; ++ ++ case CODEC_G_VFE: ++ case CODEC_S_VFE: ++ /* not needed, do nothing */ ++ return 0; ++ ++ case CODEC_S_MMAP: ++ /* not available, give an error */ ++ return -ENXIO; ++ ++ case CODEC_G_JPEG_TDS_BYTE: /* get target volume in byte */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = ptr->total_code_vol; ++ break; ++ ++ case CODEC_S_JPEG_TDS_BYTE: /* get target volume in byte */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ ptr->total_code_vol = *ival; ++ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; ++ break; ++ ++ case CODEC_G_JPEG_SCALE: /* get scaling factor */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ *ival = zr36060_read_scalefactor(ptr); ++ break; ++ ++ case CODEC_S_JPEG_SCALE: /* set scaling factor */ ++ if (size != sizeof(int)) ++ return -EFAULT; ++ ptr->scalefact = *ival; ++ break; ++ ++ case CODEC_G_JPEG_APP_DATA: { /* get appn marker data */ ++ struct jpeg_app_marker *app = data; ++ ++ if (size != sizeof(struct jpeg_app_marker)) ++ return -EFAULT; ++ ++ *app = ptr->app; ++ break; ++ } ++ ++ case CODEC_S_JPEG_APP_DATA: { /* set appn marker data */ ++ struct jpeg_app_marker *app = data; ++ ++ if (size != sizeof(struct jpeg_app_marker)) ++ return -EFAULT; ++ ++ ptr->app = *app; ++ break; ++ } ++ ++ case CODEC_G_JPEG_COM_DATA: { /* get comment marker data */ ++ struct jpeg_com_marker *com = data; ++ ++ if (size != sizeof(struct jpeg_com_marker)) ++ return -EFAULT; ++ ++ *com = ptr->com; ++ break; ++ } ++ ++ case CODEC_S_JPEG_COM_DATA: { /* set comment marker data */ ++ struct jpeg_com_marker *com = data; ++ ++ if (size != sizeof(struct jpeg_com_marker)) ++ return -EFAULT; ++ ++ ptr->com = *com; ++ break; ++ } ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return size; ++} ++ ++/* ========================================================================= ++ Exit and unregister function: ++ ++ Deinitializes Zoran's JPEG processor ++ ========================================================================= */ ++ ++static int ++zr36060_unset (struct videocodec *codec) ++{ ++ struct zr36060 *ptr = codec->data; ++ ++ if (ptr) { ++ /* do wee need some codec deinit here, too ???? */ ++ ++ dprintk(1, "%s: finished codec #%d\n", ptr->name, ++ ptr->num); ++ kfree(ptr); ++ codec->data = NULL; ++ ++ zr36060_codecs--; ++ return 0; ++ } ++ ++ return -EFAULT; ++} ++ ++/* ========================================================================= ++ Setup and registry function: ++ ++ Initializes Zoran's JPEG processor ++ ++ Also sets pixel size, average code size, mode (compr./decompr.) ++ (the given size is determined by the processor with the video interface) ++ ========================================================================= */ ++ ++static int ++zr36060_setup (struct videocodec *codec) ++{ ++ struct zr36060 *ptr; ++ int res; ++ ++ dprintk(2, "zr36060: initializing MJPEG subsystem #%d.\n", ++ zr36060_codecs); ++ ++ if (zr36060_codecs == MAX_CODECS) { ++ dprintk(1, ++ KERN_ERR "zr36060: Can't attach more codecs!\n"); ++ return -ENOSPC; ++ } ++ //mem structure init ++ codec->data = ptr = kzalloc(sizeof(struct zr36060), GFP_KERNEL); ++ if (NULL == ptr) { ++ dprintk(1, KERN_ERR "zr36060: Can't get enough memory!\n"); ++ return -ENOMEM; ++ } ++ ++ snprintf(ptr->name, sizeof(ptr->name), "zr36060[%d]", ++ zr36060_codecs); ++ ptr->num = zr36060_codecs++; ++ ptr->codec = codec; ++ ++ //testing ++ res = zr36060_basic_test(ptr); ++ if (res < 0) { ++ zr36060_unset(codec); ++ return res; ++ } ++ //final setup ++ memcpy(ptr->h_samp_ratio, zr36060_decimation_h, 8); ++ memcpy(ptr->v_samp_ratio, zr36060_decimation_v, 8); ++ ++ ptr->bitrate_ctrl = 0; /* 0 or 1 - fixed file size flag ++ * (what is the difference?) */ ++ ptr->mode = CODEC_DO_COMPRESSION; ++ ptr->width = 384; ++ ptr->height = 288; ++ ptr->total_code_vol = 16000; /* CHECKME */ ++ ptr->real_code_vol = (ptr->total_code_vol * 6) >> 3; ++ ptr->max_block_vol = 240; /* CHECKME, was 120 is 240 */ ++ ptr->scalefact = 0x100; ++ ptr->dri = 1; /* CHECKME, was 8 is 1 */ ++ ++ /* by default, no COM or APP markers - app should set those */ ++ ptr->com.len = 0; ++ ptr->app.appn = 0; ++ ptr->app.len = 0; ++ ++ zr36060_init(ptr); ++ ++ dprintk(1, KERN_INFO "%s: codec attached and running\n", ++ ptr->name); ++ ++ return 0; ++} ++ ++static const struct videocodec zr36060_codec = { ++ .owner = THIS_MODULE, ++ .name = "zr36060", ++ .magic = 0L, // magic not used ++ .flags = ++ CODEC_FLAG_JPEG | CODEC_FLAG_HARDWARE | CODEC_FLAG_ENCODER | ++ CODEC_FLAG_DECODER | CODEC_FLAG_VFE, ++ .type = CODEC_TYPE_ZR36060, ++ .setup = zr36060_setup, // functionality ++ .unset = zr36060_unset, ++ .set_mode = zr36060_set_mode, ++ .set_video = zr36060_set_video, ++ .control = zr36060_control, ++ // others are not used ++}; ++ ++/* ========================================================================= ++ HOOK IN DRIVER AS KERNEL MODULE ++ ========================================================================= */ ++ ++static int __init ++zr36060_init_module (void) ++{ ++ //dprintk(1, "zr36060 driver %s\n",ZR060_VERSION); ++ zr36060_codecs = 0; ++ return videocodec_register(&zr36060_codec); ++} ++ ++static void __exit ++zr36060_cleanup_module (void) ++{ ++ if (zr36060_codecs) { ++ dprintk(1, ++ "zr36060: something's wrong - %d codecs left somehow.\n", ++ zr36060_codecs); ++ } ++ ++ /* however, we can't just stay alive */ ++ videocodec_unregister(&zr36060_codec); ++} ++ ++module_init(zr36060_init_module); ++module_exit(zr36060_cleanup_module); ++ ++MODULE_AUTHOR("Laurent Pinchart "); ++MODULE_DESCRIPTION("Driver module for ZR36060 jpeg processors " ++ ZR060_VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/pci/zoran/zr36060.h b/drivers/media/pci/zoran/zr36060.h +new file mode 100644 +index 0000000..914ffa4 +--- /dev/null ++++ b/drivers/media/pci/zoran/zr36060.h +@@ -0,0 +1,220 @@ ++/* ++ * Zoran ZR36060 basic configuration functions - header file ++ * ++ * Copyright (C) 2002 Laurent Pinchart ++ * ++ * $Id: zr36060.h,v 1.1.1.1.2.3 2003/01/14 21:18:47 rbultje Exp $ ++ * ++ * ------------------------------------------------------------------------ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * ------------------------------------------------------------------------ ++ */ ++ ++#ifndef ZR36060_H ++#define ZR36060_H ++ ++#include "videocodec.h" ++ ++/* data stored for each zoran jpeg codec chip */ ++struct zr36060 { ++ char name[32]; ++ int num; ++ /* io datastructure */ ++ struct videocodec *codec; ++ // last coder status ++ __u8 status; ++ // actual coder setup ++ int mode; ++ ++ __u16 width; ++ __u16 height; ++ ++ __u16 bitrate_ctrl; ++ ++ __u32 total_code_vol; ++ __u32 real_code_vol; ++ __u16 max_block_vol; ++ ++ __u8 h_samp_ratio[8]; ++ __u8 v_samp_ratio[8]; ++ __u16 scalefact; ++ __u16 dri; ++ ++ /* app/com marker data */ ++ struct jpeg_app_marker app; ++ struct jpeg_com_marker com; ++}; ++ ++/* ZR36060 register addresses */ ++#define ZR060_LOAD 0x000 ++#define ZR060_CFSR 0x001 ++#define ZR060_CIR 0x002 ++#define ZR060_CMR 0x003 ++#define ZR060_MBZ 0x004 ++#define ZR060_MBCVR 0x005 ++#define ZR060_MER 0x006 ++#define ZR060_IMR 0x007 ++#define ZR060_ISR 0x008 ++#define ZR060_TCV_NET_HI 0x009 ++#define ZR060_TCV_NET_MH 0x00a ++#define ZR060_TCV_NET_ML 0x00b ++#define ZR060_TCV_NET_LO 0x00c ++#define ZR060_TCV_DATA_HI 0x00d ++#define ZR060_TCV_DATA_MH 0x00e ++#define ZR060_TCV_DATA_ML 0x00f ++#define ZR060_TCV_DATA_LO 0x010 ++#define ZR060_SF_HI 0x011 ++#define ZR060_SF_LO 0x012 ++#define ZR060_AF_HI 0x013 ++#define ZR060_AF_M 0x014 ++#define ZR060_AF_LO 0x015 ++#define ZR060_ACV_HI 0x016 ++#define ZR060_ACV_MH 0x017 ++#define ZR060_ACV_ML 0x018 ++#define ZR060_ACV_LO 0x019 ++#define ZR060_ACT_HI 0x01a ++#define ZR060_ACT_MH 0x01b ++#define ZR060_ACT_ML 0x01c ++#define ZR060_ACT_LO 0x01d ++#define ZR060_ACV_TRUN_HI 0x01e ++#define ZR060_ACV_TRUN_MH 0x01f ++#define ZR060_ACV_TRUN_ML 0x020 ++#define ZR060_ACV_TRUN_LO 0x021 ++#define ZR060_IDR_DEV 0x022 ++#define ZR060_IDR_REV 0x023 ++#define ZR060_TCR_HI 0x024 ++#define ZR060_TCR_LO 0x025 ++#define ZR060_VCR 0x030 ++#define ZR060_VPR 0x031 ++#define ZR060_SR 0x032 ++#define ZR060_BCR_Y 0x033 ++#define ZR060_BCR_U 0x034 ++#define ZR060_BCR_V 0x035 ++#define ZR060_SGR_VTOTAL_HI 0x036 ++#define ZR060_SGR_VTOTAL_LO 0x037 ++#define ZR060_SGR_HTOTAL_HI 0x038 ++#define ZR060_SGR_HTOTAL_LO 0x039 ++#define ZR060_SGR_VSYNC 0x03a ++#define ZR060_SGR_HSYNC 0x03b ++#define ZR060_SGR_BVSTART 0x03c ++#define ZR060_SGR_BHSTART 0x03d ++#define ZR060_SGR_BVEND_HI 0x03e ++#define ZR060_SGR_BVEND_LO 0x03f ++#define ZR060_SGR_BHEND_HI 0x040 ++#define ZR060_SGR_BHEND_LO 0x041 ++#define ZR060_AAR_VSTART_HI 0x042 ++#define ZR060_AAR_VSTART_LO 0x043 ++#define ZR060_AAR_VEND_HI 0x044 ++#define ZR060_AAR_VEND_LO 0x045 ++#define ZR060_AAR_HSTART_HI 0x046 ++#define ZR060_AAR_HSTART_LO 0x047 ++#define ZR060_AAR_HEND_HI 0x048 ++#define ZR060_AAR_HEND_LO 0x049 ++#define ZR060_SWR_VSTART_HI 0x04a ++#define ZR060_SWR_VSTART_LO 0x04b ++#define ZR060_SWR_VEND_HI 0x04c ++#define ZR060_SWR_VEND_LO 0x04d ++#define ZR060_SWR_HSTART_HI 0x04e ++#define ZR060_SWR_HSTART_LO 0x04f ++#define ZR060_SWR_HEND_HI 0x050 ++#define ZR060_SWR_HEND_LO 0x051 ++ ++#define ZR060_SOF_IDX 0x060 ++#define ZR060_SOS_IDX 0x07a ++#define ZR060_DRI_IDX 0x0c0 ++#define ZR060_DQT_IDX 0x0cc ++#define ZR060_DHT_IDX 0x1d4 ++#define ZR060_APP_IDX 0x380 ++#define ZR060_COM_IDX 0x3c0 ++ ++/* ZR36060 LOAD register bits */ ++ ++#define ZR060_LOAD_Load (1 << 7) ++#define ZR060_LOAD_SyncRst (1 << 0) ++ ++/* ZR36060 Code FIFO Status register bits */ ++ ++#define ZR060_CFSR_Busy (1 << 7) ++#define ZR060_CFSR_CBusy (1 << 2) ++#define ZR060_CFSR_CFIFO (3 << 0) ++ ++/* ZR36060 Code Interface register */ ++ ++#define ZR060_CIR_Code16 (1 << 7) ++#define ZR060_CIR_Endian (1 << 6) ++#define ZR060_CIR_CFIS (1 << 2) ++#define ZR060_CIR_CodeMstr (1 << 0) ++ ++/* ZR36060 Codec Mode register */ ++ ++#define ZR060_CMR_Comp (1 << 7) ++#define ZR060_CMR_ATP (1 << 6) ++#define ZR060_CMR_Pass2 (1 << 5) ++#define ZR060_CMR_TLM (1 << 4) ++#define ZR060_CMR_BRB (1 << 2) ++#define ZR060_CMR_FSF (1 << 1) ++ ++/* ZR36060 Markers Enable register */ ++ ++#define ZR060_MER_App (1 << 7) ++#define ZR060_MER_Com (1 << 6) ++#define ZR060_MER_DRI (1 << 5) ++#define ZR060_MER_DQT (1 << 4) ++#define ZR060_MER_DHT (1 << 3) ++ ++/* ZR36060 Interrupt Mask register */ ++ ++#define ZR060_IMR_EOAV (1 << 3) ++#define ZR060_IMR_EOI (1 << 2) ++#define ZR060_IMR_End (1 << 1) ++#define ZR060_IMR_DataErr (1 << 0) ++ ++/* ZR36060 Interrupt Status register */ ++ ++#define ZR060_ISR_ProCnt (3 << 6) ++#define ZR060_ISR_EOAV (1 << 3) ++#define ZR060_ISR_EOI (1 << 2) ++#define ZR060_ISR_End (1 << 1) ++#define ZR060_ISR_DataErr (1 << 0) ++ ++/* ZR36060 Video Control register */ ++ ++#define ZR060_VCR_Video8 (1 << 7) ++#define ZR060_VCR_Range (1 << 6) ++#define ZR060_VCR_FIDet (1 << 3) ++#define ZR060_VCR_FIVedge (1 << 2) ++#define ZR060_VCR_FIExt (1 << 1) ++#define ZR060_VCR_SyncMstr (1 << 0) ++ ++/* ZR36060 Video Polarity register */ ++ ++#define ZR060_VPR_VCLKPol (1 << 7) ++#define ZR060_VPR_PValPol (1 << 6) ++#define ZR060_VPR_PoePol (1 << 5) ++#define ZR060_VPR_SImgPol (1 << 4) ++#define ZR060_VPR_BLPol (1 << 3) ++#define ZR060_VPR_FIPol (1 << 2) ++#define ZR060_VPR_HSPol (1 << 1) ++#define ZR060_VPR_VSPol (1 << 0) ++ ++/* ZR36060 Scaling register */ ++ ++#define ZR060_SR_VScale (1 << 2) ++#define ZR060_SR_HScale2 (1 << 0) ++#define ZR060_SR_HScale4 (2 << 0) ++ ++#endif /*fndef ZR36060_H */ +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +new file mode 100644 +index 0000000..2433e2b +--- /dev/null ++++ b/drivers/media/platform/Kconfig +@@ -0,0 +1,244 @@ ++# ++# Platform drivers ++# All drivers here are currently for webcam support ++ ++menuconfig V4L_PLATFORM_DRIVERS ++ bool "V4L platform devices" ++ depends on MEDIA_CAMERA_SUPPORT ++ default n ++ ---help--- ++ Say Y here to enable support for platform-specific V4L drivers. ++ ++if V4L_PLATFORM_DRIVERS ++ ++source "drivers/media/platform/marvell-ccic/Kconfig" ++ ++config VIDEO_VIA_CAMERA ++ tristate "VIAFB camera controller support" ++ depends on FB_VIA ++ select VIDEOBUF_DMA_SG ++ select VIDEO_OV7670 ++ help ++ Driver support for the integrated camera controller in VIA ++ Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems ++ with ov7670 sensors. ++ ++# ++# Platform multimedia device configuration ++# ++ ++source "drivers/media/platform/davinci/Kconfig" ++ ++source "drivers/media/platform/omap/Kconfig" ++ ++source "drivers/media/platform/blackfin/Kconfig" ++ ++config VIDEO_SH_VOU ++ tristate "SuperH VOU video output driver" ++ depends on MEDIA_CAMERA_SUPPORT ++ depends on VIDEO_DEV && ARCH_SHMOBILE ++ select VIDEOBUF_DMA_CONTIG ++ help ++ Support for the Video Output Unit (VOU) on SuperH SoCs. ++ ++config VIDEO_VIU ++ tristate "Freescale VIU Video Driver" ++ depends on VIDEO_V4L2 && PPC_MPC512x ++ select VIDEOBUF_DMA_CONTIG ++ default y ++ ---help--- ++ Support for Freescale VIU video driver. This device captures ++ video data, or overlays video on DIU frame buffer. ++ ++ Say Y here if you want to enable VIU device on MPC5121e Rev2+. ++ In doubt, say N. ++ ++config VIDEO_TIMBERDALE ++ tristate "Support for timberdale Video In/LogiWIN" ++ depends on VIDEO_V4L2 && I2C && DMADEVICES ++ select DMA_ENGINE ++ select TIMB_DMA ++ select VIDEO_ADV7180 ++ select VIDEOBUF_DMA_CONTIG ++ ---help--- ++ Add support for the Video In peripherial of the timberdale FPGA. ++ ++config VIDEO_VINO ++ tristate "SGI Vino Video For Linux" ++ depends on I2C && SGI_IP22 && VIDEO_V4L2 ++ select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Say Y here to build in support for the Vino video input system found ++ on SGI Indy machines. ++ ++config VIDEO_M32R_AR ++ tristate "AR devices" ++ depends on M32R && VIDEO_V4L2 ++ ---help--- ++ This is a video4linux driver for the Renesas AR (Artificial Retina) ++ camera module. ++ ++config VIDEO_M32R_AR_M64278 ++ tristate "AR device with color module M64278(VGA)" ++ depends on PLAT_M32700UT ++ select VIDEO_M32R_AR ++ ---help--- ++ This is a video4linux driver for the Renesas AR (Artificial ++ Retina) with M64278E-800 camera module. ++ This module supports VGA(640x480 pixels) resolutions. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called arv. ++ ++config VIDEO_OMAP2 ++ tristate "OMAP2 Camera Capture Interface driver" ++ depends on VIDEO_DEV && ARCH_OMAP2 && VIDEO_V4L2_INT_DEVICE ++ select VIDEOBUF_DMA_SG ++ ---help--- ++ This is a v4l2 driver for the TI OMAP2 camera capture interface ++ ++config VIDEO_OMAP3 ++ tristate "OMAP 3 Camera support (EXPERIMENTAL)" ++ depends on OMAP_IOVMM && VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3 && EXPERIMENTAL ++ ---help--- ++ Driver for an OMAP 3 camera controller. ++ ++config VIDEO_OMAP3_DEBUG ++ bool "OMAP 3 Camera debug messages" ++ depends on VIDEO_OMAP3 ++ ---help--- ++ Enable debug messages on OMAP 3 camera controller driver. ++ ++config VIDEO_S3C_CAMIF ++ tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" ++ depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ++ depends on (PLAT_S3C64XX || PLAT_S3C24XX) && PM_RUNTIME ++ select VIDEOBUF2_DMA_CONTIG ++ ---help--- ++ This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera ++ host interface (CAMIF). ++ ++ To compile this driver as a module, choose M here: the module ++ will be called s3c-camif. ++ ++source "drivers/media/platform/soc_camera/Kconfig" ++source "drivers/media/platform/s5p-fimc/Kconfig" ++source "drivers/media/platform/s5p-tv/Kconfig" ++ ++endif # V4L_PLATFORM_DRIVERS ++ ++menuconfig V4L_MEM2MEM_DRIVERS ++ bool "Memory-to-memory multimedia devices" ++ depends on VIDEO_V4L2 ++ depends on MEDIA_CAMERA_SUPPORT ++ default n ++ ---help--- ++ Say Y here to enable selecting drivers for V4L devices that ++ use system memory for both source and destination buffers, as opposed ++ to capture and output drivers, which use memory buffers for just ++ one of those. ++ ++if V4L_MEM2MEM_DRIVERS ++ ++config VIDEO_CODA ++ tristate "Chips&Media Coda multi-standard codec IP" ++ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_MXC ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ select IRAM_ALLOC if SOC_IMX53 ++ ---help--- ++ Coda is a range of video codec IPs that supports ++ H.264, MPEG-4, and other video formats. ++ ++config VIDEO_MEM2MEM_DEINTERLACE ++ tristate "Deinterlace support" ++ depends on VIDEO_DEV && VIDEO_V4L2 && DMA_ENGINE ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ help ++ Generic deinterlacing V4L2 driver. ++ ++config VIDEO_SAMSUNG_S5P_G2D ++ tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver" ++ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ default n ++ ---help--- ++ This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D ++ 2d graphics accelerator. ++ ++config VIDEO_SAMSUNG_S5P_JPEG ++ tristate "Samsung S5P/Exynos4 JPEG codec driver (EXPERIMENTAL)" ++ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P && EXPERIMENTAL ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ ---help--- ++ This is a v4l2 driver for Samsung S5P and EXYNOS4 JPEG codec ++ ++config VIDEO_SAMSUNG_S5P_MFC ++ tristate "Samsung S5P MFC Video Codec" ++ depends on VIDEO_DEV && VIDEO_V4L2 && PLAT_S5P ++ select VIDEOBUF2_DMA_CONTIG ++ default n ++ help ++ MFC 5.1 and 6.x driver for V4L2 ++ ++config VIDEO_MX2_EMMAPRP ++ tristate "MX2 eMMa-PrP support" ++ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27 ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ help ++ MX2X chips have a PrP that can be used to process buffers from ++ memory to memory. Operations include resizing and format ++ conversion. ++ ++config VIDEO_SAMSUNG_EXYNOS_GSC ++ tristate "Samsung Exynos G-Scaler driver" ++ depends on VIDEO_DEV && VIDEO_V4L2 && ARCH_EXYNOS5 ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ help ++ This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler. ++ ++config VIDEO_SH_VEU ++ tristate "SuperH VEU mem2mem video processing driver" ++ depends on VIDEO_DEV && VIDEO_V4L2 ++ select VIDEOBUF2_DMA_CONTIG ++ select V4L2_MEM2MEM_DEV ++ help ++ Support for the Video Engine Unit (VEU) on SuperH and ++ SH-Mobile SoCs. ++ ++endif # V4L_MEM2MEM_DRIVERS ++ ++menuconfig V4L_TEST_DRIVERS ++ bool "Media test drivers" ++ depends on MEDIA_CAMERA_SUPPORT ++ ++if V4L_TEST_DRIVERS ++config VIDEO_VIVI ++ tristate "Virtual Video Driver" ++ depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 ++ depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE ++ select FONT_8x16 ++ select VIDEOBUF2_VMALLOC ++ default n ++ ---help--- ++ Enables a virtual video driver. This device shows a color bar ++ and a timestamp, as a real device would generate by using V4L2 ++ api. ++ Say Y here if you want to test video apps or debug V4L devices. ++ In doubt, say N. ++ ++config VIDEO_MEM2MEM_TESTDEV ++ tristate "Virtual test device for mem2mem framework" ++ depends on VIDEO_DEV && VIDEO_V4L2 ++ select VIDEOBUF2_VMALLOC ++ select V4L2_MEM2MEM_DEV ++ default n ++ ---help--- ++ This is a virtual test device for the memory-to-memory driver ++ framework. ++endif #V4L_TEST_DRIVERS +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +new file mode 100644 +index 0000000..42089ba +--- /dev/null ++++ b/drivers/media/platform/Makefile +@@ -0,0 +1,53 @@ ++# ++# Makefile for the video capture/playback device drivers. ++# ++ ++omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o ++ ++obj-$(CONFIG_VIDEO_VINO) += indycam.o ++obj-$(CONFIG_VIDEO_VINO) += vino.o ++ ++obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o ++obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o ++ ++obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o ++obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ ++obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ ++ ++obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o ++obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ ++ ++obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o ++obj-$(CONFIG_VIDEO_VIVI) += vivi.o ++ ++obj-$(CONFIG_VIDEO_MEM2MEM_TESTDEV) += mem2mem_testdev.o ++ ++obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o ++obj-$(CONFIG_VIDEO_CODA) += coda.o ++ ++obj-$(CONFIG_VIDEO_SH_VEU) += sh_veu.o ++ ++obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o ++ ++obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/ ++obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/ ++obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/ ++obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/ ++obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/ ++ ++obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/ ++obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/ ++ ++obj-$(CONFIG_BLACKFIN) += blackfin/ ++ ++obj-$(CONFIG_ARCH_DAVINCI) += davinci/ ++ ++obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o ++ ++obj-$(CONFIG_SOC_CAMERA) += soc_camera/ ++ ++obj-y += davinci/ ++ ++obj-$(CONFIG_ARCH_OMAP) += omap/ ++ ++ccflags-y += -I$(srctree)/drivers/media/i2c +diff --git a/drivers/media/platform/arv.c b/drivers/media/platform/arv.c +new file mode 100644 +index 0000000..e346d32 +--- /dev/null ++++ b/drivers/media/platform/arv.c +@@ -0,0 +1,885 @@ ++/* ++ * Colour AR M64278(VGA) driver for Video4Linux ++ * ++ * Copyright (C) 2003 Takeo Takahashi ++ * ++ * 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. ++ * ++ * Some code is taken from AR driver sample program for M3T-M32700UT. ++ * ++ * AR driver sample (M32R SDK): ++ * Copyright (c) 2003 RENESAS TECHNOROGY CORPORATION ++ * AND RENESAS SOLUTIONS CORPORATION ++ * All Rights Reserved. ++ * ++ * 2003-09-01: Support w3cam by Takeo Takahashi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define DEBUG(n, args...) printk(KERN_INFO args) ++#define CHECK_LOST 1 ++#else ++#define DEBUG(n, args...) ++#define CHECK_LOST 0 ++#endif ++ ++/* ++ * USE_INT is always 0, interrupt mode is not available ++ * on linux due to lack of speed ++ */ ++#define USE_INT 0 /* Don't modify */ ++ ++#define VERSION "0.0.5" ++ ++#define ar_inl(addr) inl((unsigned long)(addr)) ++#define ar_outl(val, addr) outl((unsigned long)(val), (unsigned long)(addr)) ++ ++extern struct cpuinfo_m32r boot_cpu_data; ++ ++/* ++ * CCD pixel size ++ * Note that M32700UT does not support CIF mode, but QVGA is ++ * supported by M32700UT hardware using VGA mode of AR LSI. ++ * ++ * Supported: VGA (Normal mode, Interlace mode) ++ * QVGA (Always Interlace mode of VGA) ++ * ++ */ ++#define AR_WIDTH_VGA 640 ++#define AR_HEIGHT_VGA 480 ++#define AR_WIDTH_QVGA 320 ++#define AR_HEIGHT_QVGA 240 ++#define MIN_AR_WIDTH AR_WIDTH_QVGA ++#define MIN_AR_HEIGHT AR_HEIGHT_QVGA ++#define MAX_AR_WIDTH AR_WIDTH_VGA ++#define MAX_AR_HEIGHT AR_HEIGHT_VGA ++ ++/* bits & bytes per pixel */ ++#define AR_BITS_PER_PIXEL 16 ++#define AR_BYTES_PER_PIXEL (AR_BITS_PER_PIXEL / 8) ++ ++/* line buffer size */ ++#define AR_LINE_BYTES_VGA (AR_WIDTH_VGA * AR_BYTES_PER_PIXEL) ++#define AR_LINE_BYTES_QVGA (AR_WIDTH_QVGA * AR_BYTES_PER_PIXEL) ++#define MAX_AR_LINE_BYTES AR_LINE_BYTES_VGA ++ ++/* frame size & type */ ++#define AR_FRAME_BYTES_VGA \ ++ (AR_WIDTH_VGA * AR_HEIGHT_VGA * AR_BYTES_PER_PIXEL) ++#define AR_FRAME_BYTES_QVGA \ ++ (AR_WIDTH_QVGA * AR_HEIGHT_QVGA * AR_BYTES_PER_PIXEL) ++#define MAX_AR_FRAME_BYTES \ ++ (MAX_AR_WIDTH * MAX_AR_HEIGHT * AR_BYTES_PER_PIXEL) ++ ++#define AR_MAX_FRAME 15 ++ ++/* capture size */ ++#define AR_SIZE_VGA 0 ++#define AR_SIZE_QVGA 1 ++ ++/* capture mode */ ++#define AR_MODE_INTERLACE 0 ++#define AR_MODE_NORMAL 1 ++ ++struct ar { ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ unsigned int start_capture; /* duaring capture in INT. mode. */ ++#if USE_INT ++ unsigned char *line_buff; /* DMA line buffer */ ++#endif ++ unsigned char *frame[MAX_AR_HEIGHT]; /* frame data */ ++ short size; /* capture size */ ++ short mode; /* capture mode */ ++ int width, height; ++ int frame_bytes, line_bytes; ++ wait_queue_head_t wait; ++ struct mutex lock; ++}; ++ ++static struct ar ardev; ++ ++static int video_nr = -1; /* video device number (first free) */ ++static unsigned char yuv[MAX_AR_FRAME_BYTES]; ++ ++/* module parameters */ ++/* default frequency */ ++#define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */ ++static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */ ++static int vga; /* default mode(0:QVGA mode, other:VGA mode) */ ++static int vga_interlace; /* 0 is normal mode for, else interlace mode */ ++module_param(freq, int, 0); ++module_param(vga, int, 0); ++module_param(vga_interlace, int, 0); ++ ++static void wait_for_vsync(void) ++{ ++ while (ar_inl(ARVCR0) & ARVCR0_VDS) /* wait for VSYNC */ ++ cpu_relax(); ++ while (!(ar_inl(ARVCR0) & ARVCR0_VDS)) /* wait for VSYNC */ ++ cpu_relax(); ++} ++ ++static void wait_acknowledge(void) ++{ ++ int i; ++ ++ for (i = 0; i < 1000; i++) ++ cpu_relax(); ++ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_NOACK) ++ cpu_relax(); ++} ++ ++/******************************************************************* ++ * I2C functions ++ *******************************************************************/ ++static void iic(int n, unsigned long addr, unsigned long data1, unsigned long data2, ++ unsigned long data3) ++{ ++ int i; ++ ++ /* Slave Address */ ++ ar_outl(addr, PLDI2CDATA); ++ wait_for_vsync(); ++ ++ /* Start */ ++ ar_outl(1, PLDI2CCND); ++ wait_acknowledge(); ++ ++ /* Transfer data 1 */ ++ ar_outl(data1, PLDI2CDATA); ++ wait_for_vsync(); ++ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); ++ wait_acknowledge(); ++ ++ /* Transfer data 2 */ ++ ar_outl(data2, PLDI2CDATA); ++ wait_for_vsync(); ++ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); ++ wait_acknowledge(); ++ ++ if (n == 3) { ++ /* Transfer data 3 */ ++ ar_outl(data3, PLDI2CDATA); ++ wait_for_vsync(); ++ ar_outl(PLDI2CSTEN_STEN, PLDI2CSTEN); ++ wait_acknowledge(); ++ } ++ ++ /* Stop */ ++ for (i = 0; i < 100; i++) ++ cpu_relax(); ++ ar_outl(2, PLDI2CCND); ++ ar_outl(2, PLDI2CCND); ++ ++ while (ar_inl(PLDI2CSTS) & PLDI2CSTS_BB) ++ cpu_relax(); ++} ++ ++ ++static void init_iic(void) ++{ ++ DEBUG(1, "init_iic:\n"); ++ ++ /* ++ * ICU Setting (iic) ++ */ ++ /* I2C Setting */ ++ ar_outl(0x0, PLDI2CCR); /* I2CCR Disable */ ++ ar_outl(0x0300, PLDI2CMOD); /* I2CMOD ACK/8b-data/7b-addr/auto */ ++ ar_outl(0x1, PLDI2CACK); /* I2CACK ACK */ ++ ++ /* I2C CLK */ ++ /* 50MH-100k */ ++ if (freq == 75) ++ ar_outl(369, PLDI2CFREQ); /* BCLK = 75MHz */ ++ else if (freq == 50) ++ ar_outl(244, PLDI2CFREQ); /* BCLK = 50MHz */ ++ else ++ ar_outl(244, PLDI2CFREQ); /* default: BCLK = 50MHz */ ++ ar_outl(0x1, PLDI2CCR); /* I2CCR Enable */ ++} ++ ++/************************************************************************** ++ * ++ * Video4Linux Interface functions ++ * ++ **************************************************************************/ ++ ++static inline void disable_dma(void) ++{ ++ ar_outl(0x8000, M32R_DMAEN_PORTL); /* disable DMA0 */ ++} ++ ++static inline void enable_dma(void) ++{ ++ ar_outl(0x8080, M32R_DMAEN_PORTL); /* enable DMA0 */ ++} ++ ++static inline void clear_dma_status(void) ++{ ++ ar_outl(0x8000, M32R_DMAEDET_PORTL); /* clear status */ ++} ++ ++static void wait_for_vertical_sync(struct ar *ar, int exp_line) ++{ ++#if CHECK_LOST ++ int tmout = 10000; /* FIXME */ ++ int l; ++ ++ /* ++ * check HCOUNT because we cannot check vertical sync. ++ */ ++ for (; tmout >= 0; tmout--) { ++ l = ar_inl(ARVHCOUNT); ++ if (l == exp_line) ++ break; ++ } ++ if (tmout < 0) ++ v4l2_err(&ar->v4l2_dev, "lost %d -> %d\n", exp_line, l); ++#else ++ while (ar_inl(ARVHCOUNT) != exp_line) ++ cpu_relax(); ++#endif ++} ++ ++static ssize_t ar_read(struct file *file, char *buf, size_t count, loff_t *ppos) ++{ ++ struct ar *ar = video_drvdata(file); ++ long ret = ar->frame_bytes; /* return read bytes */ ++ unsigned long arvcr1 = 0; ++ unsigned long flags; ++ unsigned char *p; ++ int h, w; ++ unsigned char *py, *pu, *pv; ++#if !USE_INT ++ int l; ++#endif ++ ++ DEBUG(1, "ar_read()\n"); ++ ++ if (ar->size == AR_SIZE_QVGA) ++ arvcr1 |= ARVCR1_QVGA; ++ if (ar->mode == AR_MODE_NORMAL) ++ arvcr1 |= ARVCR1_NORMAL; ++ ++ mutex_lock(&ar->lock); ++ ++#if USE_INT ++ local_irq_save(flags); ++ disable_dma(); ++ ar_outl(0xa1871300, M32R_DMA0CR0_PORTL); ++ ar_outl(0x01000000, M32R_DMA0CR1_PORTL); ++ ++ /* set AR FIFO address as source(BSEL5) */ ++ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); ++ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); ++ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* destination addr. */ ++ ar_outl(ar->line_buff, M32R_DMA0RDA_PORTL); /* reload address */ ++ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); /* byte count (bytes) */ ++ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); /* reload count (bytes) */ ++ ++ /* ++ * Okay, kick AR LSI to invoke an interrupt ++ */ ++ ar->start_capture = 0; ++ ar_outl(arvcr1 | ARVCR1_HIEN, ARVCR1); ++ local_irq_restore(flags); ++ /* .... AR interrupts .... */ ++ interruptible_sleep_on(&ar->wait); ++ if (signal_pending(current)) { ++ printk(KERN_ERR "arv: interrupted while get frame data.\n"); ++ ret = -EINTR; ++ goto out_up; ++ } ++#else /* ! USE_INT */ ++ /* polling */ ++ ar_outl(arvcr1, ARVCR1); ++ disable_dma(); ++ ar_outl(0x8000, M32R_DMAEDET_PORTL); ++ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); ++ ar_outl(0x01000000, M32R_DMA0CR1_PORTL); ++ ar_outl(ARDATA32, M32R_DMA0CSA_PORTL); ++ ar_outl(ARDATA32, M32R_DMA0RSA_PORTL); ++ ar_outl(ar->line_bytes, M32R_DMA0CBCUT_PORTL); ++ ar_outl(ar->line_bytes, M32R_DMA0RBCUT_PORTL); ++ ++ local_irq_save(flags); ++ while (ar_inl(ARVHCOUNT) != 0) /* wait for 0 */ ++ cpu_relax(); ++ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { ++ for (h = 0; h < ar->height; h++) { ++ wait_for_vertical_sync(ar, h); ++ if (h < (AR_HEIGHT_VGA/2)) ++ l = h << 1; ++ else ++ l = (((h - (AR_HEIGHT_VGA/2)) << 1) + 1); ++ ar_outl(virt_to_phys(ar->frame[l]), M32R_DMA0CDA_PORTL); ++ enable_dma(); ++ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) ++ cpu_relax(); ++ disable_dma(); ++ clear_dma_status(); ++ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); ++ } ++ } else { ++ for (h = 0; h < ar->height; h++) { ++ wait_for_vertical_sync(ar, h); ++ ar_outl(virt_to_phys(ar->frame[h]), M32R_DMA0CDA_PORTL); ++ enable_dma(); ++ while (!(ar_inl(M32R_DMAEDET_PORTL) & 0x8000)) ++ cpu_relax(); ++ disable_dma(); ++ clear_dma_status(); ++ ar_outl(0xa0861300, M32R_DMA0CR0_PORTL); ++ } ++ } ++ local_irq_restore(flags); ++#endif /* ! USE_INT */ ++ ++ /* ++ * convert YUV422 to YUV422P ++ * +--------------------+ ++ * | Y0,Y1,... | ++ * | ..............Yn | ++ * +--------------------+ ++ * | U0,U1,........Un | ++ * +--------------------+ ++ * | V0,V1,........Vn | ++ * +--------------------+ ++ */ ++ py = yuv; ++ pu = py + (ar->frame_bytes / 2); ++ pv = pu + (ar->frame_bytes / 4); ++ for (h = 0; h < ar->height; h++) { ++ p = ar->frame[h]; ++ for (w = 0; w < ar->line_bytes; w += 4) { ++ *py++ = *p++; ++ *pu++ = *p++; ++ *py++ = *p++; ++ *pv++ = *p++; ++ } ++ } ++ if (copy_to_user(buf, yuv, ar->frame_bytes)) { ++ v4l2_err(&ar->v4l2_dev, "failed while copy_to_user yuv.\n"); ++ ret = -EFAULT; ++ goto out_up; ++ } ++ DEBUG(1, "ret = %d\n", ret); ++out_up: ++ mutex_unlock(&ar->lock); ++ return ret; ++} ++ ++static int ar_querycap(struct file *file, void *priv, ++ struct v4l2_capability *vcap) ++{ ++ struct ar *ar = video_drvdata(file); ++ ++ strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); ++ strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); ++ strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); ++ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; ++ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int ar_enum_input(struct file *file, void *fh, struct v4l2_input *vin) ++{ ++ if (vin->index > 0) ++ return -EINVAL; ++ strlcpy(vin->name, "Camera", sizeof(vin->name)); ++ vin->type = V4L2_INPUT_TYPE_CAMERA; ++ vin->audioset = 0; ++ vin->tuner = 0; ++ vin->std = V4L2_STD_ALL; ++ vin->status = 0; ++ return 0; ++} ++ ++static int ar_g_input(struct file *file, void *fh, unsigned int *inp) ++{ ++ *inp = 0; ++ return 0; ++} ++ ++static int ar_s_input(struct file *file, void *fh, unsigned int inp) ++{ ++ return inp ? -EINVAL : 0; ++} ++ ++static int ar_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ar *ar = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ pix->width = ar->width; ++ pix->height = ar->height; ++ pix->pixelformat = V4L2_PIX_FMT_YUV422P; ++ pix->field = (ar->mode == AR_MODE_NORMAL) ? V4L2_FIELD_NONE : V4L2_FIELD_INTERLACED; ++ pix->bytesperline = ar->width; ++ pix->sizeimage = 2 * ar->width * ar->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int ar_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ar *ar = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ ++ if (pix->height <= AR_HEIGHT_QVGA || pix->width <= AR_WIDTH_QVGA) { ++ pix->height = AR_HEIGHT_QVGA; ++ pix->width = AR_WIDTH_QVGA; ++ pix->field = V4L2_FIELD_INTERLACED; ++ } else { ++ pix->height = AR_HEIGHT_VGA; ++ pix->width = AR_WIDTH_VGA; ++ pix->field = vga_interlace ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; ++ } ++ pix->pixelformat = V4L2_PIX_FMT_YUV422P; ++ pix->bytesperline = ar->width; ++ pix->sizeimage = 2 * ar->width * ar->height; ++ /* Just a guess */ ++ pix->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ return 0; ++} ++ ++static int ar_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) ++{ ++ struct ar *ar = video_drvdata(file); ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ int ret = ar_try_fmt_vid_cap(file, fh, fmt); ++ ++ if (ret) ++ return ret; ++ mutex_lock(&ar->lock); ++ ar->width = pix->width; ++ ar->height = pix->height; ++ if (ar->width == AR_WIDTH_VGA) { ++ ar->size = AR_SIZE_VGA; ++ ar->frame_bytes = AR_FRAME_BYTES_VGA; ++ ar->line_bytes = AR_LINE_BYTES_VGA; ++ if (vga_interlace) ++ ar->mode = AR_MODE_INTERLACE; ++ else ++ ar->mode = AR_MODE_NORMAL; ++ } else { ++ ar->size = AR_SIZE_QVGA; ++ ar->frame_bytes = AR_FRAME_BYTES_QVGA; ++ ar->line_bytes = AR_LINE_BYTES_QVGA; ++ ar->mode = AR_MODE_INTERLACE; ++ } ++ /* Ok we figured out what to use from our wide choice */ ++ mutex_unlock(&ar->lock); ++ return 0; ++} ++ ++static int ar_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) ++{ ++ static struct v4l2_fmtdesc formats[] = { ++ { 0, 0, 0, ++ "YUV 4:2:2 Planar", V4L2_PIX_FMT_YUV422P, ++ { 0, 0, 0, 0 } ++ }, ++ }; ++ enum v4l2_buf_type type = fmt->type; ++ ++ if (fmt->index > 0) ++ return -EINVAL; ++ ++ *fmt = formats[fmt->index]; ++ fmt->type = type; ++ return 0; ++} ++ ++#if USE_INT ++/* ++ * Interrupt handler ++ */ ++static void ar_interrupt(int irq, void *dev) ++{ ++ struct ar *ar = dev; ++ unsigned int line_count; ++ unsigned int line_number; ++ unsigned int arvcr1; ++ ++ line_count = ar_inl(ARVHCOUNT); /* line number */ ++ if (ar->mode == AR_MODE_INTERLACE && ar->size == AR_SIZE_VGA) { ++ /* operations for interlace mode */ ++ if (line_count < (AR_HEIGHT_VGA / 2)) /* even line */ ++ line_number = (line_count << 1); ++ else /* odd line */ ++ line_number = ++ (((line_count - (AR_HEIGHT_VGA / 2)) << 1) + 1); ++ } else { ++ line_number = line_count; ++ } ++ ++ if (line_number == 0) { ++ /* ++ * It is an interrupt for line 0. ++ * we have to start capture. ++ */ ++ disable_dma(); ++#if 0 ++ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); /* needless? */ ++#endif ++ memcpy(ar->frame[0], ar->line_buff, ar->line_bytes); ++#if 0 ++ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); ++#endif ++ enable_dma(); ++ ar->start_capture = 1; /* during capture */ ++ return; ++ } ++ ++ if (ar->start_capture == 1 && line_number <= (ar->height - 1)) { ++ disable_dma(); ++ memcpy(ar->frame[line_number], ar->line_buff, ar->line_bytes); ++ ++ /* ++ * if captured all line of a frame, disable AR interrupt ++ * and wake a process up. ++ */ ++ if (line_number == (ar->height - 1)) { /* end of line */ ++ ++ ar->start_capture = 0; ++ ++ /* disable AR interrupt request */ ++ arvcr1 = ar_inl(ARVCR1); ++ arvcr1 &= ~ARVCR1_HIEN; /* clear int. flag */ ++ ar_outl(arvcr1, ARVCR1); /* disable */ ++ wake_up_interruptible(&ar->wait); ++ } else { ++#if 0 ++ ar_outl(ar->line_buff, M32R_DMA0CDA_PORTL); ++ ar_outl(0xa1861300, M32R_DMA0CR0_PORTL); ++#endif ++ enable_dma(); ++ } ++ } ++} ++#endif ++ ++/* ++ * ar_initialize() ++ * ar_initialize() is called by video_register_device() and ++ * initializes AR LSI and peripherals. ++ * ++ * -1 is returned in all failures. ++ * 0 is returned in success. ++ * ++ */ ++static int ar_initialize(struct ar *ar) ++{ ++ unsigned long cr = 0; ++ int i, found = 0; ++ ++ DEBUG(1, "ar_initialize:\n"); ++ ++ /* ++ * initialize AR LSI ++ */ ++ ar_outl(0, ARVCR0); /* assert reset of AR LSI */ ++ for (i = 0; i < 0x18; i++) /* wait for over 10 cycles @ 27MHz */ ++ cpu_relax(); ++ ar_outl(ARVCR0_RST, ARVCR0); /* negate reset of AR LSI (enable) */ ++ for (i = 0; i < 0x40d; i++) /* wait for over 420 cycles @ 27MHz */ ++ cpu_relax(); ++ ++ /* AR uses INT3 of CPU as interrupt pin. */ ++ ar_outl(ARINTSEL_INT3, ARINTSEL); ++ ++ if (ar->size == AR_SIZE_QVGA) ++ cr |= ARVCR1_QVGA; ++ if (ar->mode == AR_MODE_NORMAL) ++ cr |= ARVCR1_NORMAL; ++ ar_outl(cr, ARVCR1); ++ ++ /* ++ * Initialize IIC so that CPU can communicate with AR LSI, ++ * and send boot commands to AR LSI. ++ */ ++ init_iic(); ++ ++ for (i = 0; i < 0x100000; i++) { /* > 0xa1d10, 56ms */ ++ if ((ar_inl(ARVCR0) & ARVCR0_VDS)) { /* VSYNC */ ++ found = 1; ++ break; ++ } ++ } ++ ++ if (found == 0) ++ return -ENODEV; ++ ++ v4l2_info(&ar->v4l2_dev, "Initializing "); ++ ++ iic(2, 0x78, 0x11, 0x01, 0x00); /* start */ ++ iic(3, 0x78, 0x12, 0x00, 0x06); ++ iic(3, 0x78, 0x12, 0x12, 0x30); ++ iic(3, 0x78, 0x12, 0x15, 0x58); ++ iic(3, 0x78, 0x12, 0x17, 0x30); ++ printk(KERN_CONT "."); ++ iic(3, 0x78, 0x12, 0x1a, 0x97); ++ iic(3, 0x78, 0x12, 0x1b, 0xff); ++ iic(3, 0x78, 0x12, 0x1c, 0xff); ++ iic(3, 0x78, 0x12, 0x26, 0x10); ++ iic(3, 0x78, 0x12, 0x27, 0x00); ++ printk(KERN_CONT "."); ++ iic(2, 0x78, 0x34, 0x02, 0x00); ++ iic(2, 0x78, 0x7a, 0x10, 0x00); ++ iic(2, 0x78, 0x80, 0x39, 0x00); ++ iic(2, 0x78, 0x81, 0xe6, 0x00); ++ iic(2, 0x78, 0x8d, 0x00, 0x00); ++ printk(KERN_CONT "."); ++ iic(2, 0x78, 0x8e, 0x0c, 0x00); ++ iic(2, 0x78, 0x8f, 0x00, 0x00); ++#if 0 ++ iic(2, 0x78, 0x90, 0x00, 0x00); /* AWB on=1 off=0 */ ++#endif ++ iic(2, 0x78, 0x93, 0x01, 0x00); ++ iic(2, 0x78, 0x94, 0xcd, 0x00); ++ iic(2, 0x78, 0x95, 0x00, 0x00); ++ printk(KERN_CONT "."); ++ iic(2, 0x78, 0x96, 0xa0, 0x00); ++ iic(2, 0x78, 0x97, 0x00, 0x00); ++ iic(2, 0x78, 0x98, 0x60, 0x00); ++ iic(2, 0x78, 0x99, 0x01, 0x00); ++ iic(2, 0x78, 0x9a, 0x19, 0x00); ++ printk(KERN_CONT "."); ++ iic(2, 0x78, 0x9b, 0x02, 0x00); ++ iic(2, 0x78, 0x9c, 0xe8, 0x00); ++ iic(2, 0x78, 0x9d, 0x02, 0x00); ++ iic(2, 0x78, 0x9e, 0x2e, 0x00); ++ iic(2, 0x78, 0xb8, 0x78, 0x00); ++ iic(2, 0x78, 0xba, 0x05, 0x00); ++#if 0 ++ iic(2, 0x78, 0x83, 0x8c, 0x00); /* brightness */ ++#endif ++ printk(KERN_CONT "."); ++ ++ /* color correction */ ++ iic(3, 0x78, 0x49, 0x00, 0x95); /* a */ ++ iic(3, 0x78, 0x49, 0x01, 0x96); /* b */ ++ iic(3, 0x78, 0x49, 0x03, 0x85); /* c */ ++ iic(3, 0x78, 0x49, 0x04, 0x97); /* d */ ++ iic(3, 0x78, 0x49, 0x02, 0x7e); /* e(Lo) */ ++ iic(3, 0x78, 0x49, 0x05, 0xa4); /* f(Lo) */ ++ iic(3, 0x78, 0x49, 0x06, 0x04); /* e(Hi) */ ++ iic(3, 0x78, 0x49, 0x07, 0x04); /* e(Hi) */ ++ iic(2, 0x78, 0x48, 0x01, 0x00); /* on=1 off=0 */ ++ ++ printk(KERN_CONT "."); ++ iic(2, 0x78, 0x11, 0x00, 0x00); /* end */ ++ printk(KERN_CONT " done\n"); ++ return 0; ++} ++ ++ ++/**************************************************************************** ++ * ++ * Video4Linux Module functions ++ * ++ ****************************************************************************/ ++ ++static const struct v4l2_file_operations ar_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = v4l2_fh_release, ++ .read = ar_read, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++static const struct v4l2_ioctl_ops ar_ioctl_ops = { ++ .vidioc_querycap = ar_querycap, ++ .vidioc_g_input = ar_g_input, ++ .vidioc_s_input = ar_s_input, ++ .vidioc_enum_input = ar_enum_input, ++ .vidioc_enum_fmt_vid_cap = ar_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = ar_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = ar_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = ar_try_fmt_vid_cap, ++}; ++ ++#define ALIGN4(x) ((((int)(x)) & 0x3) == 0) ++ ++static int __init ar_init(void) ++{ ++ struct ar *ar; ++ struct v4l2_device *v4l2_dev; ++ int ret; ++ int i; ++ ++ ar = &ardev; ++ v4l2_dev = &ar->v4l2_dev; ++ strlcpy(v4l2_dev->name, "arv", sizeof(v4l2_dev->name)); ++ v4l2_info(v4l2_dev, "Colour AR VGA driver %s\n", VERSION); ++ ++ ret = v4l2_device_register(NULL, v4l2_dev); ++ if (ret < 0) { ++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); ++ return ret; ++ } ++ ret = -EIO; ++ ++#if USE_INT ++ /* allocate a DMA buffer for 1 line. */ ++ ar->line_buff = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL | GFP_DMA); ++ if (ar->line_buff == NULL || !ALIGN4(ar->line_buff)) { ++ v4l2_err(v4l2_dev, "buffer allocation failed for DMA.\n"); ++ ret = -ENOMEM; ++ goto out_end; ++ } ++#endif ++ /* allocate buffers for a frame */ ++ for (i = 0; i < MAX_AR_HEIGHT; i++) { ++ ar->frame[i] = kmalloc(MAX_AR_LINE_BYTES, GFP_KERNEL); ++ if (ar->frame[i] == NULL || !ALIGN4(ar->frame[i])) { ++ v4l2_err(v4l2_dev, "buffer allocation failed for frame.\n"); ++ ret = -ENOMEM; ++ goto out_line_buff; ++ } ++ } ++ ++ strlcpy(ar->vdev.name, "Colour AR VGA", sizeof(ar->vdev.name)); ++ ar->vdev.v4l2_dev = v4l2_dev; ++ ar->vdev.fops = &ar_fops; ++ ar->vdev.ioctl_ops = &ar_ioctl_ops; ++ ar->vdev.release = video_device_release_empty; ++ set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags); ++ video_set_drvdata(&ar->vdev, ar); ++ ++ if (vga) { ++ ar->width = AR_WIDTH_VGA; ++ ar->height = AR_HEIGHT_VGA; ++ ar->size = AR_SIZE_VGA; ++ ar->frame_bytes = AR_FRAME_BYTES_VGA; ++ ar->line_bytes = AR_LINE_BYTES_VGA; ++ if (vga_interlace) ++ ar->mode = AR_MODE_INTERLACE; ++ else ++ ar->mode = AR_MODE_NORMAL; ++ } else { ++ ar->width = AR_WIDTH_QVGA; ++ ar->height = AR_HEIGHT_QVGA; ++ ar->size = AR_SIZE_QVGA; ++ ar->frame_bytes = AR_FRAME_BYTES_QVGA; ++ ar->line_bytes = AR_LINE_BYTES_QVGA; ++ ar->mode = AR_MODE_INTERLACE; ++ } ++ mutex_init(&ar->lock); ++ init_waitqueue_head(&ar->wait); ++ ++#if USE_INT ++ if (request_irq(M32R_IRQ_INT3, ar_interrupt, 0, "arv", ar)) { ++ v4l2_err("request_irq(%d) failed.\n", M32R_IRQ_INT3); ++ ret = -EIO; ++ goto out_irq; ++ } ++#endif ++ ++ if (ar_initialize(ar) != 0) { ++ v4l2_err(v4l2_dev, "M64278 not found.\n"); ++ ret = -ENODEV; ++ goto out_dev; ++ } ++ ++ /* ++ * ok, we can initialize h/w according to parameters, ++ * so register video device as a frame grabber type. ++ * device is named "video[0-64]". ++ * video_register_device() initializes h/w using ar_initialize(). ++ */ ++ if (video_register_device(&ar->vdev, VFL_TYPE_GRABBER, video_nr) != 0) { ++ /* return -1, -ENFILE(full) or others */ ++ v4l2_err(v4l2_dev, "register video (Colour AR) failed.\n"); ++ ret = -ENODEV; ++ goto out_dev; ++ } ++ ++ v4l2_info(v4l2_dev, "%s: Found M64278 VGA (IRQ %d, Freq %dMHz).\n", ++ video_device_node_name(&ar->vdev), M32R_IRQ_INT3, freq); ++ ++ return 0; ++ ++out_dev: ++#if USE_INT ++ free_irq(M32R_IRQ_INT3, ar); ++ ++out_irq: ++#endif ++ for (i = 0; i < MAX_AR_HEIGHT; i++) ++ kfree(ar->frame[i]); ++ ++out_line_buff: ++#if USE_INT ++ kfree(ar->line_buff); ++ ++out_end: ++#endif ++ v4l2_device_unregister(&ar->v4l2_dev); ++ return ret; ++} ++ ++ ++static int __init ar_init_module(void) ++{ ++ freq = (boot_cpu_data.bus_clock / 1000000); ++ printk(KERN_INFO "arv: Bus clock %d\n", freq); ++ if (freq != 50 && freq != 75) ++ freq = DEFAULT_FREQ; ++ return ar_init(); ++} ++ ++static void __exit ar_cleanup_module(void) ++{ ++ struct ar *ar; ++ int i; ++ ++ ar = &ardev; ++ video_unregister_device(&ar->vdev); ++#if USE_INT ++ free_irq(M32R_IRQ_INT3, ar); ++#endif ++ for (i = 0; i < MAX_AR_HEIGHT; i++) ++ kfree(ar->frame[i]); ++#if USE_INT ++ kfree(ar->line_buff); ++#endif ++ v4l2_device_unregister(&ar->v4l2_dev); ++} ++ ++module_init(ar_init_module); ++module_exit(ar_cleanup_module); ++ ++MODULE_AUTHOR("Takeo Takahashi "); ++MODULE_DESCRIPTION("Colour AR M64278(VGA) for Video4Linux"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(VERSION); +diff --git a/drivers/media/platform/blackfin/Kconfig b/drivers/media/platform/blackfin/Kconfig +new file mode 100644 +index 0000000..cc23997 +--- /dev/null ++++ b/drivers/media/platform/blackfin/Kconfig +@@ -0,0 +1,15 @@ ++config VIDEO_BLACKFIN_CAPTURE ++ tristate "Blackfin Video Capture Driver" ++ depends on VIDEO_V4L2 && BLACKFIN && I2C ++ select VIDEOBUF2_DMA_CONTIG ++ help ++ V4L2 bridge driver for Blackfin video capture device. ++ Choose PPI or EPPI as its interface. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bfin_capture. ++ ++config VIDEO_BLACKFIN_PPI ++ tristate ++ depends on VIDEO_BLACKFIN_CAPTURE ++ default VIDEO_BLACKFIN_CAPTURE +diff --git a/drivers/media/platform/blackfin/Makefile b/drivers/media/platform/blackfin/Makefile +new file mode 100644 +index 0000000..30421bc +--- /dev/null ++++ b/drivers/media/platform/blackfin/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_capture.o ++obj-$(CONFIG_VIDEO_BLACKFIN_PPI) += ppi.o +diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c +new file mode 100644 +index 0000000..aa9f846 +--- /dev/null ++++ b/drivers/media/platform/blackfin/bfin_capture.c +@@ -0,0 +1,1175 @@ ++/* ++ * Analog Devices video capture driver ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#define CAPTURE_DRV_NAME "bfin_capture" ++#define BCAP_MIN_NUM_BUF 2 ++ ++struct bcap_format { ++ char *desc; ++ u32 pixelformat; ++ enum v4l2_mbus_pixelcode mbus_code; ++ int bpp; /* bits per pixel */ ++ int dlen; /* data length for ppi in bits */ ++}; ++ ++struct bcap_buffer { ++ struct vb2_buffer vb; ++ struct list_head list; ++}; ++ ++struct bcap_device { ++ /* capture device instance */ ++ struct v4l2_device v4l2_dev; ++ /* v4l2 control handler */ ++ struct v4l2_ctrl_handler ctrl_handler; ++ /* device node data */ ++ struct video_device *video_dev; ++ /* sub device instance */ ++ struct v4l2_subdev *sd; ++ /* capture config */ ++ struct bfin_capture_config *cfg; ++ /* ppi interface */ ++ struct ppi_if *ppi; ++ /* current input */ ++ unsigned int cur_input; ++ /* current selected standard */ ++ v4l2_std_id std; ++ /* current selected dv_timings */ ++ struct v4l2_dv_timings dv_timings; ++ /* used to store pixel format */ ++ struct v4l2_pix_format fmt; ++ /* bits per pixel*/ ++ int bpp; ++ /* data length for ppi in bits */ ++ int dlen; ++ /* used to store sensor supported format */ ++ struct bcap_format *sensor_formats; ++ /* number of sensor formats array */ ++ int num_sensor_formats; ++ /* pointing to current video buffer */ ++ struct bcap_buffer *cur_frm; ++ /* pointing to next video buffer */ ++ struct bcap_buffer *next_frm; ++ /* buffer queue used in videobuf2 */ ++ struct vb2_queue buffer_queue; ++ /* allocator-specific contexts for each plane */ ++ struct vb2_alloc_ctx *alloc_ctx; ++ /* queue of filled frames */ ++ struct list_head dma_queue; ++ /* used in videobuf2 callback */ ++ spinlock_t lock; ++ /* used to access capture device */ ++ struct mutex mutex; ++ /* used to wait ppi to complete one transfer */ ++ struct completion comp; ++ /* prepare to stop */ ++ bool stop; ++}; ++ ++struct bcap_fh { ++ struct v4l2_fh fh; ++ /* indicates whether this file handle is doing IO */ ++ bool io_allowed; ++}; ++ ++static const struct bcap_format bcap_formats[] = { ++ { ++ .desc = "YCbCr 4:2:2 Interleaved UYVY", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, ++ .bpp = 16, ++ .dlen = 8, ++ }, ++ { ++ .desc = "YCbCr 4:2:2 Interleaved YUYV", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .bpp = 16, ++ .dlen = 8, ++ }, ++ { ++ .desc = "YCbCr 4:2:2 Interleaved UYVY", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, ++ .bpp = 16, ++ .dlen = 16, ++ }, ++ { ++ .desc = "RGB 565", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .bpp = 16, ++ .dlen = 8, ++ }, ++ { ++ .desc = "RGB 444", ++ .pixelformat = V4L2_PIX_FMT_RGB444, ++ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, ++ .bpp = 16, ++ .dlen = 8, ++ }, ++ ++}; ++#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats) ++ ++static irqreturn_t bcap_isr(int irq, void *dev_id); ++ ++static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb) ++{ ++ return container_of(vb, struct bcap_buffer, vb); ++} ++ ++static int bcap_init_sensor_formats(struct bcap_device *bcap_dev) ++{ ++ enum v4l2_mbus_pixelcode code; ++ struct bcap_format *sf; ++ unsigned int num_formats = 0; ++ int i, j; ++ ++ while (!v4l2_subdev_call(bcap_dev->sd, video, ++ enum_mbus_fmt, num_formats, &code)) ++ num_formats++; ++ if (!num_formats) ++ return -ENXIO; ++ ++ sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL); ++ if (!sf) ++ return -ENOMEM; ++ ++ for (i = 0; i < num_formats; i++) { ++ v4l2_subdev_call(bcap_dev->sd, video, ++ enum_mbus_fmt, i, &code); ++ for (j = 0; j < BCAP_MAX_FMTS; j++) ++ if (code == bcap_formats[j].mbus_code) ++ break; ++ if (j == BCAP_MAX_FMTS) { ++ /* we don't allow this sensor working with our bridge */ ++ kfree(sf); ++ return -EINVAL; ++ } ++ sf[i] = bcap_formats[j]; ++ } ++ bcap_dev->sensor_formats = sf; ++ bcap_dev->num_sensor_formats = num_formats; ++ return 0; ++} ++ ++static void bcap_free_sensor_formats(struct bcap_device *bcap_dev) ++{ ++ bcap_dev->num_sensor_formats = 0; ++ kfree(bcap_dev->sensor_formats); ++ bcap_dev->sensor_formats = NULL; ++} ++ ++static int bcap_open(struct file *file) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct video_device *vfd = bcap_dev->video_dev; ++ struct bcap_fh *bcap_fh; ++ ++ if (!bcap_dev->sd) { ++ v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n"); ++ return -ENODEV; ++ } ++ ++ bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL); ++ if (!bcap_fh) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "unable to allocate memory for file handle object\n"); ++ return -ENOMEM; ++ } ++ ++ v4l2_fh_init(&bcap_fh->fh, vfd); ++ ++ /* store pointer to v4l2_fh in private_data member of file */ ++ file->private_data = &bcap_fh->fh; ++ v4l2_fh_add(&bcap_fh->fh); ++ bcap_fh->io_allowed = false; ++ return 0; ++} ++ ++static int bcap_release(struct file *file) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct v4l2_fh *fh = file->private_data; ++ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); ++ ++ /* if this instance is doing IO */ ++ if (bcap_fh->io_allowed) ++ vb2_queue_release(&bcap_dev->buffer_queue); ++ ++ file->private_data = NULL; ++ v4l2_fh_del(&bcap_fh->fh); ++ v4l2_fh_exit(&bcap_fh->fh); ++ kfree(bcap_fh); ++ return 0; ++} ++ ++static int bcap_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ int ret; ++ ++ if (mutex_lock_interruptible(&bcap_dev->mutex)) ++ return -ERESTARTSYS; ++ ret = vb2_mmap(&bcap_dev->buffer_queue, vma); ++ mutex_unlock(&bcap_dev->mutex); ++ return ret; ++} ++ ++#ifndef CONFIG_MMU ++static unsigned long bcap_get_unmapped_area(struct file *file, ++ unsigned long addr, ++ unsigned long len, ++ unsigned long pgoff, ++ unsigned long flags) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ return vb2_get_unmapped_area(&bcap_dev->buffer_queue, ++ addr, ++ len, ++ pgoff, ++ flags); ++} ++#endif ++ ++static unsigned int bcap_poll(struct file *file, poll_table *wait) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ unsigned int res; ++ ++ mutex_lock(&bcap_dev->mutex); ++ res = vb2_poll(&bcap_dev->buffer_queue, file, wait); ++ mutex_unlock(&bcap_dev->mutex); ++ return res; ++} ++ ++static int bcap_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); ++ ++ if (*nbuffers < BCAP_MIN_NUM_BUF) ++ *nbuffers = BCAP_MIN_NUM_BUF; ++ ++ *nplanes = 1; ++ sizes[0] = bcap_dev->fmt.sizeimage; ++ alloc_ctxs[0] = bcap_dev->alloc_ctx; ++ ++ return 0; ++} ++ ++static int bcap_buffer_init(struct vb2_buffer *vb) ++{ ++ struct bcap_buffer *buf = to_bcap_vb(vb); ++ ++ INIT_LIST_HEAD(&buf->list); ++ return 0; ++} ++ ++static int bcap_buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct bcap_buffer *buf = to_bcap_vb(vb); ++ unsigned long size; ++ ++ size = bcap_dev->fmt.sizeimage; ++ if (vb2_plane_size(vb, 0) < size) { ++ v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n", ++ vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ vb2_set_plane_payload(&buf->vb, 0, size); ++ ++ return 0; ++} ++ ++static void bcap_buffer_queue(struct vb2_buffer *vb) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct bcap_buffer *buf = to_bcap_vb(vb); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bcap_dev->lock, flags); ++ list_add_tail(&buf->list, &bcap_dev->dma_queue); ++ spin_unlock_irqrestore(&bcap_dev->lock, flags); ++} ++ ++static void bcap_buffer_cleanup(struct vb2_buffer *vb) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct bcap_buffer *buf = to_bcap_vb(vb); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&bcap_dev->lock, flags); ++ list_del_init(&buf->list); ++ spin_unlock_irqrestore(&bcap_dev->lock, flags); ++} ++ ++static void bcap_lock(struct vb2_queue *vq) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); ++ mutex_lock(&bcap_dev->mutex); ++} ++ ++static void bcap_unlock(struct vb2_queue *vq) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); ++ mutex_unlock(&bcap_dev->mutex); ++} ++ ++static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); ++ struct ppi_if *ppi = bcap_dev->ppi; ++ struct ppi_params params; ++ int ret; ++ ++ /* enable streamon on the sub device */ ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1); ++ if (ret && (ret != -ENOIOCTLCMD)) { ++ v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n"); ++ return ret; ++ } ++ ++ /* set ppi params */ ++ params.width = bcap_dev->fmt.width; ++ params.height = bcap_dev->fmt.height; ++ params.bpp = bcap_dev->bpp; ++ params.dlen = bcap_dev->dlen; ++ params.ppi_control = bcap_dev->cfg->ppi_control; ++ params.int_mask = bcap_dev->cfg->int_mask; ++ if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities ++ & V4L2_IN_CAP_CUSTOM_TIMINGS) { ++ struct v4l2_bt_timings *bt = &bcap_dev->dv_timings.bt; ++ ++ params.hdelay = bt->hsync + bt->hbackporch; ++ params.vdelay = bt->vsync + bt->vbackporch; ++ params.line = bt->hfrontporch + bt->hsync ++ + bt->hbackporch + bt->width; ++ params.frame = bt->vfrontporch + bt->vsync ++ + bt->vbackporch + bt->height; ++ if (bt->interlaced) ++ params.frame += bt->il_vfrontporch + bt->il_vsync ++ + bt->il_vbackporch; ++ } else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities ++ & V4L2_IN_CAP_STD) { ++ params.hdelay = 0; ++ params.vdelay = 0; ++ if (bcap_dev->std & V4L2_STD_525_60) { ++ params.line = 858; ++ params.frame = 525; ++ } else { ++ params.line = 864; ++ params.frame = 625; ++ } ++ } else { ++ params.hdelay = 0; ++ params.vdelay = 0; ++ params.line = params.width + bcap_dev->cfg->blank_pixels; ++ params.frame = params.height; ++ } ++ ret = ppi->ops->set_params(ppi, ¶ms); ++ if (ret < 0) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Error in setting ppi params\n"); ++ return ret; ++ } ++ ++ /* attach ppi DMA irq handler */ ++ ret = ppi->ops->attach_irq(ppi, bcap_isr); ++ if (ret < 0) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Error in attaching interrupt handler\n"); ++ return ret; ++ } ++ ++ INIT_COMPLETION(bcap_dev->comp); ++ bcap_dev->stop = false; ++ return 0; ++} ++ ++static int bcap_stop_streaming(struct vb2_queue *vq) ++{ ++ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq); ++ struct ppi_if *ppi = bcap_dev->ppi; ++ int ret; ++ ++ if (!vb2_is_streaming(vq)) ++ return 0; ++ ++ bcap_dev->stop = true; ++ wait_for_completion(&bcap_dev->comp); ++ ppi->ops->stop(ppi); ++ ppi->ops->detach_irq(ppi); ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0); ++ if (ret && (ret != -ENOIOCTLCMD)) ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "stream off failed in subdev\n"); ++ ++ /* release all active buffers */ ++ while (!list_empty(&bcap_dev->dma_queue)) { ++ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, ++ struct bcap_buffer, list); ++ list_del(&bcap_dev->next_frm->list); ++ vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR); ++ } ++ return 0; ++} ++ ++static struct vb2_ops bcap_video_qops = { ++ .queue_setup = bcap_queue_setup, ++ .buf_init = bcap_buffer_init, ++ .buf_prepare = bcap_buffer_prepare, ++ .buf_cleanup = bcap_buffer_cleanup, ++ .buf_queue = bcap_buffer_queue, ++ .wait_prepare = bcap_unlock, ++ .wait_finish = bcap_lock, ++ .start_streaming = bcap_start_streaming, ++ .stop_streaming = bcap_stop_streaming, ++}; ++ ++static int bcap_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *req_buf) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct vb2_queue *vq = &bcap_dev->buffer_queue; ++ struct v4l2_fh *fh = file->private_data; ++ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); ++ ++ if (vb2_is_busy(vq)) ++ return -EBUSY; ++ ++ bcap_fh->io_allowed = true; ++ ++ return vb2_reqbufs(vq, req_buf); ++} ++ ++static int bcap_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ return vb2_querybuf(&bcap_dev->buffer_queue, buf); ++} ++ ++static int bcap_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct v4l2_fh *fh = file->private_data; ++ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); ++ ++ if (!bcap_fh->io_allowed) ++ return -EBUSY; ++ ++ return vb2_qbuf(&bcap_dev->buffer_queue, buf); ++} ++ ++static int bcap_dqbuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct v4l2_fh *fh = file->private_data; ++ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh); ++ ++ if (!bcap_fh->io_allowed) ++ return -EBUSY; ++ ++ return vb2_dqbuf(&bcap_dev->buffer_queue, ++ buf, file->f_flags & O_NONBLOCK); ++} ++ ++static irqreturn_t bcap_isr(int irq, void *dev_id) ++{ ++ struct ppi_if *ppi = dev_id; ++ struct bcap_device *bcap_dev = ppi->priv; ++ struct vb2_buffer *vb = &bcap_dev->cur_frm->vb; ++ dma_addr_t addr; ++ ++ spin_lock(&bcap_dev->lock); ++ ++ if (bcap_dev->cur_frm != bcap_dev->next_frm) { ++ v4l2_get_timestamp(&vb->v4l2_buf.timestamp); ++ vb2_buffer_done(vb, VB2_BUF_STATE_DONE); ++ bcap_dev->cur_frm = bcap_dev->next_frm; ++ } ++ ++ ppi->ops->stop(ppi); ++ ++ if (bcap_dev->stop) { ++ complete(&bcap_dev->comp); ++ } else { ++ if (!list_empty(&bcap_dev->dma_queue)) { ++ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, ++ struct bcap_buffer, list); ++ list_del(&bcap_dev->next_frm->list); ++ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0); ++ ppi->ops->update_addr(ppi, (unsigned long)addr); ++ } ++ ppi->ops->start(ppi); ++ } ++ ++ spin_unlock(&bcap_dev->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcap_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct bcap_fh *fh = file->private_data; ++ struct ppi_if *ppi = bcap_dev->ppi; ++ dma_addr_t addr; ++ int ret; ++ ++ if (!fh->io_allowed) ++ return -EBUSY; ++ ++ /* call streamon to start streaming in videobuf */ ++ ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type); ++ if (ret) ++ return ret; ++ ++ /* if dma queue is empty, return error */ ++ if (list_empty(&bcap_dev->dma_queue)) { ++ v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ /* get the next frame from the dma queue */ ++ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next, ++ struct bcap_buffer, list); ++ bcap_dev->cur_frm = bcap_dev->next_frm; ++ /* remove buffer from the dma queue */ ++ list_del(&bcap_dev->cur_frm->list); ++ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0); ++ /* update DMA address */ ++ ppi->ops->update_addr(ppi, (unsigned long)addr); ++ /* enable ppi */ ++ ppi->ops->start(ppi); ++ ++ return 0; ++err: ++ vb2_streamoff(&bcap_dev->buffer_queue, buf_type); ++ return ret; ++} ++ ++static int bcap_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct bcap_fh *fh = file->private_data; ++ ++ if (!fh->io_allowed) ++ return -EBUSY; ++ ++ return vb2_streamoff(&bcap_dev->buffer_queue, buf_type); ++} ++ ++static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(bcap_dev->sd, video, querystd, std); ++} ++ ++static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ *std = bcap_dev->std; ++ return 0; ++} ++ ++static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ int ret; ++ ++ if (vb2_is_busy(&bcap_dev->buffer_queue)) ++ return -EBUSY; ++ ++ ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std); ++ if (ret < 0) ++ return ret; ++ ++ bcap_dev->std = *std; ++ return 0; ++} ++ ++static int bcap_g_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ int ret; ++ ++ ret = v4l2_subdev_call(bcap_dev->sd, video, ++ g_dv_timings, timings); ++ if (ret < 0) ++ return ret; ++ ++ bcap_dev->dv_timings = *timings; ++ return 0; ++} ++ ++static int bcap_s_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ int ret; ++ if (vb2_is_busy(&bcap_dev->buffer_queue)) ++ return -EBUSY; ++ ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_dv_timings, timings); ++ if (ret < 0) ++ return ret; ++ ++ bcap_dev->dv_timings = *timings; ++ return 0; ++} ++ ++static int bcap_enum_input(struct file *file, void *priv, ++ struct v4l2_input *input) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct bfin_capture_config *config = bcap_dev->cfg; ++ int ret; ++ u32 status; ++ ++ if (input->index >= config->num_inputs) ++ return -EINVAL; ++ ++ *input = config->inputs[input->index]; ++ /* get input status */ ++ ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status); ++ if (!ret) ++ input->status = status; ++ return 0; ++} ++ ++static int bcap_g_input(struct file *file, void *priv, unsigned int *index) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ *index = bcap_dev->cur_input; ++ return 0; ++} ++ ++static int bcap_s_input(struct file *file, void *priv, unsigned int index) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct bfin_capture_config *config = bcap_dev->cfg; ++ struct bcap_route *route; ++ int ret; ++ ++ if (vb2_is_busy(&bcap_dev->buffer_queue)) ++ return -EBUSY; ++ ++ if (index >= config->num_inputs) ++ return -EINVAL; ++ ++ route = &config->routes[index]; ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, ++ route->input, route->output, 0); ++ if ((ret < 0) && (ret != -ENOIOCTLCMD)) { ++ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); ++ return ret; ++ } ++ bcap_dev->cur_input = index; ++ /* if this route has specific config, update ppi control */ ++ if (route->ppi_control) ++ config->ppi_control = route->ppi_control; ++ return 0; ++} ++ ++static int bcap_try_format(struct bcap_device *bcap, ++ struct v4l2_pix_format *pixfmt, ++ struct bcap_format *bcap_fmt) ++{ ++ struct bcap_format *sf = bcap->sensor_formats; ++ struct bcap_format *fmt = NULL; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int ret, i; ++ ++ for (i = 0; i < bcap->num_sensor_formats; i++) { ++ fmt = &sf[i]; ++ if (pixfmt->pixelformat == fmt->pixelformat) ++ break; ++ } ++ if (i == bcap->num_sensor_formats) ++ fmt = &sf[0]; ++ ++ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code); ++ ret = v4l2_subdev_call(bcap->sd, video, ++ try_mbus_fmt, &mbus_fmt); ++ if (ret < 0) ++ return ret; ++ v4l2_fill_pix_format(pixfmt, &mbus_fmt); ++ if (bcap_fmt) { ++ for (i = 0; i < bcap->num_sensor_formats; i++) { ++ fmt = &sf[i]; ++ if (mbus_fmt.code == fmt->mbus_code) ++ break; ++ } ++ *bcap_fmt = *fmt; ++ } ++ pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8; ++ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; ++ return 0; ++} ++ ++static int bcap_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *fmt) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct bcap_format *sf = bcap_dev->sensor_formats; ++ ++ if (fmt->index >= bcap_dev->num_sensor_formats) ++ return -EINVAL; ++ ++ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ strlcpy(fmt->description, ++ sf[fmt->index].desc, ++ sizeof(fmt->description)); ++ fmt->pixelformat = sf[fmt->index].pixelformat; ++ return 0; ++} ++ ++static int bcap_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ return bcap_try_format(bcap_dev, pixfmt, NULL); ++} ++ ++static int bcap_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ fmt->fmt.pix = bcap_dev->fmt; ++ return 0; ++} ++ ++static int bcap_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ struct v4l2_mbus_framefmt mbus_fmt; ++ struct bcap_format bcap_fmt; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ int ret; ++ ++ if (vb2_is_busy(&bcap_dev->buffer_queue)) ++ return -EBUSY; ++ ++ /* see if format works */ ++ ret = bcap_try_format(bcap_dev, pixfmt, &bcap_fmt); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, bcap_fmt.mbus_code); ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt); ++ if (ret < 0) ++ return ret; ++ bcap_dev->fmt = *pixfmt; ++ bcap_dev->bpp = bcap_fmt.bpp; ++ bcap_dev->dlen = bcap_fmt.dlen; ++ return 0; ++} ++ ++static int bcap_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); ++ strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info)); ++ strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card)); ++ return 0; ++} ++ ++static int bcap_g_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a); ++} ++ ++static int bcap_s_parm(struct file *file, void *fh, ++ struct v4l2_streamparm *a) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a); ++} ++ ++static int bcap_g_chip_ident(struct file *file, void *priv, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && ++ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) ++ return -EINVAL; ++ ++ return v4l2_subdev_call(bcap_dev->sd, core, ++ g_chip_ident, chip); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int bcap_dbg_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(bcap_dev->sd, core, ++ g_register, reg); ++} ++ ++static int bcap_dbg_s_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ ++ return v4l2_subdev_call(bcap_dev->sd, core, ++ s_register, reg); ++} ++#endif ++ ++static int bcap_log_status(struct file *file, void *priv) ++{ ++ struct bcap_device *bcap_dev = video_drvdata(file); ++ /* status for sub devices */ ++ v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status); ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops bcap_ioctl_ops = { ++ .vidioc_querycap = bcap_querycap, ++ .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap, ++ .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap, ++ .vidioc_enum_input = bcap_enum_input, ++ .vidioc_g_input = bcap_g_input, ++ .vidioc_s_input = bcap_s_input, ++ .vidioc_querystd = bcap_querystd, ++ .vidioc_s_std = bcap_s_std, ++ .vidioc_g_std = bcap_g_std, ++ .vidioc_s_dv_timings = bcap_s_dv_timings, ++ .vidioc_g_dv_timings = bcap_g_dv_timings, ++ .vidioc_reqbufs = bcap_reqbufs, ++ .vidioc_querybuf = bcap_querybuf, ++ .vidioc_qbuf = bcap_qbuf, ++ .vidioc_dqbuf = bcap_dqbuf, ++ .vidioc_streamon = bcap_streamon, ++ .vidioc_streamoff = bcap_streamoff, ++ .vidioc_g_parm = bcap_g_parm, ++ .vidioc_s_parm = bcap_s_parm, ++ .vidioc_g_chip_ident = bcap_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = bcap_dbg_g_register, ++ .vidioc_s_register = bcap_dbg_s_register, ++#endif ++ .vidioc_log_status = bcap_log_status, ++}; ++ ++static struct v4l2_file_operations bcap_fops = { ++ .owner = THIS_MODULE, ++ .open = bcap_open, ++ .release = bcap_release, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = bcap_mmap, ++#ifndef CONFIG_MMU ++ .get_unmapped_area = bcap_get_unmapped_area, ++#endif ++ .poll = bcap_poll ++}; ++ ++static int __devinit bcap_probe(struct platform_device *pdev) ++{ ++ struct bcap_device *bcap_dev; ++ struct video_device *vfd; ++ struct i2c_adapter *i2c_adap; ++ struct bfin_capture_config *config; ++ struct vb2_queue *q; ++ struct bcap_route *route; ++ int ret; ++ ++ config = pdev->dev.platform_data; ++ if (!config) { ++ v4l2_err(pdev->dev.driver, "Unable to get board config\n"); ++ return -ENODEV; ++ } ++ ++ bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL); ++ if (!bcap_dev) { ++ v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n"); ++ return -ENOMEM; ++ } ++ ++ bcap_dev->cfg = config; ++ ++ bcap_dev->ppi = ppi_create_instance(config->ppi_info); ++ if (!bcap_dev->ppi) { ++ v4l2_err(pdev->dev.driver, "Unable to create ppi\n"); ++ ret = -ENODEV; ++ goto err_free_dev; ++ } ++ bcap_dev->ppi->priv = bcap_dev; ++ ++ bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); ++ if (IS_ERR(bcap_dev->alloc_ctx)) { ++ ret = PTR_ERR(bcap_dev->alloc_ctx); ++ goto err_free_ppi; ++ } ++ ++ vfd = video_device_alloc(); ++ if (!vfd) { ++ ret = -ENOMEM; ++ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); ++ goto err_cleanup_ctx; ++ } ++ ++ /* initialize field of video device */ ++ vfd->release = video_device_release; ++ vfd->fops = &bcap_fops; ++ vfd->ioctl_ops = &bcap_ioctl_ops; ++ vfd->tvnorms = 0; ++ vfd->v4l2_dev = &bcap_dev->v4l2_dev; ++ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); ++ strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name)); ++ bcap_dev->video_dev = vfd; ++ ++ ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev); ++ if (ret) { ++ v4l2_err(pdev->dev.driver, ++ "Unable to register v4l2 device\n"); ++ goto err_release_vdev; ++ } ++ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n"); ++ ++ bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler; ++ ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0); ++ if (ret) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to init control handler\n"); ++ goto err_unreg_v4l2; ++ } ++ ++ spin_lock_init(&bcap_dev->lock); ++ /* initialize queue */ ++ q = &bcap_dev->buffer_queue; ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP; ++ q->drv_priv = bcap_dev; ++ q->buf_struct_size = sizeof(struct bcap_buffer); ++ q->ops = &bcap_video_qops; ++ q->mem_ops = &vb2_dma_contig_memops; ++ ++ vb2_queue_init(q); ++ ++ mutex_init(&bcap_dev->mutex); ++ init_completion(&bcap_dev->comp); ++ ++ /* init video dma queues */ ++ INIT_LIST_HEAD(&bcap_dev->dma_queue); ++ ++ vfd->lock = &bcap_dev->mutex; ++ ++ /* register video device */ ++ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); ++ if (ret) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to register video device\n"); ++ goto err_free_handler; ++ } ++ video_set_drvdata(bcap_dev->video_dev, bcap_dev); ++ v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n", ++ video_device_node_name(vfd)); ++ ++ /* load up the subdevice */ ++ i2c_adap = i2c_get_adapter(config->i2c_adapter_id); ++ if (!i2c_adap) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to find i2c adapter\n"); ++ ret = -ENODEV; ++ goto err_unreg_vdev; ++ ++ } ++ bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev, ++ i2c_adap, ++ &config->board_info, ++ NULL); ++ if (bcap_dev->sd) { ++ int i; ++ if (!config->num_inputs) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to work without input\n"); ++ goto err_unreg_vdev; ++ } ++ ++ /* update tvnorms from the sub devices */ ++ for (i = 0; i < config->num_inputs; i++) ++ vfd->tvnorms |= config->inputs[i].std; ++ } else { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to register sub device\n"); ++ goto err_unreg_vdev; ++ } ++ ++ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n"); ++ ++ /* ++ * explicitly set input, otherwise some boards ++ * may not work at the state as we expected ++ */ ++ route = &config->routes[0]; ++ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing, ++ route->input, route->output, 0); ++ if ((ret < 0) && (ret != -ENOIOCTLCMD)) { ++ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n"); ++ goto err_unreg_vdev; ++ } ++ bcap_dev->cur_input = 0; ++ /* if this route has specific config, update ppi control */ ++ if (route->ppi_control) ++ config->ppi_control = route->ppi_control; ++ ++ /* now we can probe the default state */ ++ if (config->inputs[0].capabilities & V4L2_IN_CAP_STD) { ++ v4l2_std_id std; ++ ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std); ++ if (ret) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to get std\n"); ++ goto err_unreg_vdev; ++ } ++ bcap_dev->std = std; ++ } ++ if (config->inputs[0].capabilities & V4L2_IN_CAP_CUSTOM_TIMINGS) { ++ struct v4l2_dv_timings dv_timings; ++ ret = v4l2_subdev_call(bcap_dev->sd, video, ++ g_dv_timings, &dv_timings); ++ if (ret) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to get dv timings\n"); ++ goto err_unreg_vdev; ++ } ++ bcap_dev->dv_timings = dv_timings; ++ } ++ ret = bcap_init_sensor_formats(bcap_dev); ++ if (ret) { ++ v4l2_err(&bcap_dev->v4l2_dev, ++ "Unable to create sensor formats table\n"); ++ goto err_unreg_vdev; ++ } ++ return 0; ++err_unreg_vdev: ++ video_unregister_device(bcap_dev->video_dev); ++ bcap_dev->video_dev = NULL; ++err_free_handler: ++ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); ++err_unreg_v4l2: ++ v4l2_device_unregister(&bcap_dev->v4l2_dev); ++err_release_vdev: ++ if (bcap_dev->video_dev) ++ video_device_release(bcap_dev->video_dev); ++err_cleanup_ctx: ++ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); ++err_free_ppi: ++ ppi_delete_instance(bcap_dev->ppi); ++err_free_dev: ++ kfree(bcap_dev); ++ return ret; ++} ++ ++static int __devexit bcap_remove(struct platform_device *pdev) ++{ ++ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); ++ struct bcap_device *bcap_dev = container_of(v4l2_dev, ++ struct bcap_device, v4l2_dev); ++ ++ bcap_free_sensor_formats(bcap_dev); ++ video_unregister_device(bcap_dev->video_dev); ++ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler); ++ v4l2_device_unregister(v4l2_dev); ++ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx); ++ ppi_delete_instance(bcap_dev->ppi); ++ kfree(bcap_dev); ++ return 0; ++} ++ ++static struct platform_driver bcap_driver = { ++ .driver = { ++ .name = CAPTURE_DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcap_probe, ++ .remove = __devexit_p(bcap_remove), ++}; ++module_platform_driver(bcap_driver); ++ ++MODULE_DESCRIPTION("Analog Devices blackfin video capture driver"); ++MODULE_AUTHOR("Scott Jiang "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c +new file mode 100644 +index 0000000..1e24584 +--- /dev/null ++++ b/drivers/media/platform/blackfin/ppi.c +@@ -0,0 +1,322 @@ ++/* ++ * ppi.c Analog Devices Parallel Peripheral Interface driver ++ * ++ * Copyright (c) 2011 Analog Devices Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler); ++static void ppi_detach_irq(struct ppi_if *ppi); ++static int ppi_start(struct ppi_if *ppi); ++static int ppi_stop(struct ppi_if *ppi); ++static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params); ++static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr); ++ ++static const struct ppi_ops ppi_ops = { ++ .attach_irq = ppi_attach_irq, ++ .detach_irq = ppi_detach_irq, ++ .start = ppi_start, ++ .stop = ppi_stop, ++ .set_params = ppi_set_params, ++ .update_addr = ppi_update_addr, ++}; ++ ++static irqreturn_t ppi_irq_err(int irq, void *dev_id) ++{ ++ struct ppi_if *ppi = dev_id; ++ const struct ppi_info *info = ppi->info; ++ ++ switch (info->type) { ++ case PPI_TYPE_PPI: ++ { ++ struct bfin_ppi_regs *reg = info->base; ++ unsigned short status; ++ ++ /* register on bf561 is cleared when read ++ * others are W1C ++ */ ++ status = bfin_read16(®->status); ++ bfin_write16(®->status, 0xff00); ++ break; ++ } ++ case PPI_TYPE_EPPI: ++ { ++ struct bfin_eppi_regs *reg = info->base; ++ bfin_write16(®->status, 0xffff); ++ break; ++ } ++ case PPI_TYPE_EPPI3: ++ { ++ struct bfin_eppi3_regs *reg = info->base; ++ ++ bfin_write32(®->stat, 0xc0ff); ++ break; ++ } ++ default: ++ break; ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler) ++{ ++ const struct ppi_info *info = ppi->info; ++ int ret; ++ ++ ret = request_dma(info->dma_ch, "PPI_DMA"); ++ ++ if (ret) { ++ pr_err("Unable to allocate DMA channel for PPI\n"); ++ return ret; ++ } ++ set_dma_callback(info->dma_ch, handler, ppi); ++ ++ if (ppi->err_int) { ++ ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi); ++ if (ret) { ++ pr_err("Unable to allocate IRQ for PPI\n"); ++ free_dma(info->dma_ch); ++ } ++ } ++ return ret; ++} ++ ++static void ppi_detach_irq(struct ppi_if *ppi) ++{ ++ const struct ppi_info *info = ppi->info; ++ ++ if (ppi->err_int) ++ free_irq(info->irq_err, ppi); ++ free_dma(info->dma_ch); ++} ++ ++static int ppi_start(struct ppi_if *ppi) ++{ ++ const struct ppi_info *info = ppi->info; ++ ++ /* enable DMA */ ++ enable_dma(info->dma_ch); ++ ++ /* enable PPI */ ++ ppi->ppi_control |= PORT_EN; ++ switch (info->type) { ++ case PPI_TYPE_PPI: ++ { ++ struct bfin_ppi_regs *reg = info->base; ++ bfin_write16(®->control, ppi->ppi_control); ++ break; ++ } ++ case PPI_TYPE_EPPI: ++ { ++ struct bfin_eppi_regs *reg = info->base; ++ bfin_write32(®->control, ppi->ppi_control); ++ break; ++ } ++ case PPI_TYPE_EPPI3: ++ { ++ struct bfin_eppi3_regs *reg = info->base; ++ bfin_write32(®->ctl, ppi->ppi_control); ++ break; ++ } ++ default: ++ return -EINVAL; ++ } ++ ++ SSYNC(); ++ return 0; ++} ++ ++static int ppi_stop(struct ppi_if *ppi) ++{ ++ const struct ppi_info *info = ppi->info; ++ ++ /* disable PPI */ ++ ppi->ppi_control &= ~PORT_EN; ++ switch (info->type) { ++ case PPI_TYPE_PPI: ++ { ++ struct bfin_ppi_regs *reg = info->base; ++ bfin_write16(®->control, ppi->ppi_control); ++ break; ++ } ++ case PPI_TYPE_EPPI: ++ { ++ struct bfin_eppi_regs *reg = info->base; ++ bfin_write32(®->control, ppi->ppi_control); ++ break; ++ } ++ case PPI_TYPE_EPPI3: ++ { ++ struct bfin_eppi3_regs *reg = info->base; ++ bfin_write32(®->ctl, ppi->ppi_control); ++ break; ++ } ++ default: ++ return -EINVAL; ++ } ++ ++ /* disable DMA */ ++ clear_dma_irqstat(info->dma_ch); ++ disable_dma(info->dma_ch); ++ ++ SSYNC(); ++ return 0; ++} ++ ++static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) ++{ ++ const struct ppi_info *info = ppi->info; ++ int dma32 = 0; ++ int dma_config, bytes_per_line; ++ int hcount, hdelay, samples_per_line; ++ ++ bytes_per_line = params->width * params->bpp / 8; ++ /* convert parameters unit from pixels to samples */ ++ hcount = params->width * params->bpp / params->dlen; ++ hdelay = params->hdelay * params->bpp / params->dlen; ++ samples_per_line = params->line * params->bpp / params->dlen; ++ if (params->int_mask == 0xFFFFFFFF) ++ ppi->err_int = false; ++ else ++ ppi->err_int = true; ++ ++ dma_config = (DMA_FLOW_STOP | RESTART | DMA2D | DI_EN_Y); ++ ppi->ppi_control = params->ppi_control & ~PORT_EN; ++ if (!(ppi->ppi_control & PORT_DIR)) ++ dma_config |= WNR; ++ switch (info->type) { ++ case PPI_TYPE_PPI: ++ { ++ struct bfin_ppi_regs *reg = info->base; ++ ++ if (params->ppi_control & DMA32) ++ dma32 = 1; ++ ++ bfin_write16(®->control, ppi->ppi_control); ++ bfin_write16(®->count, samples_per_line - 1); ++ bfin_write16(®->frame, params->frame); ++ break; ++ } ++ case PPI_TYPE_EPPI: ++ { ++ struct bfin_eppi_regs *reg = info->base; ++ ++ if ((params->ppi_control & PACK_EN) ++ || (params->ppi_control & 0x38000) > DLEN_16) ++ dma32 = 1; ++ ++ bfin_write32(®->control, ppi->ppi_control); ++ bfin_write16(®->line, samples_per_line); ++ bfin_write16(®->frame, params->frame); ++ bfin_write16(®->hdelay, hdelay); ++ bfin_write16(®->vdelay, params->vdelay); ++ bfin_write16(®->hcount, hcount); ++ bfin_write16(®->vcount, params->height); ++ break; ++ } ++ case PPI_TYPE_EPPI3: ++ { ++ struct bfin_eppi3_regs *reg = info->base; ++ ++ if ((params->ppi_control & PACK_EN) ++ || (params->ppi_control & 0x70000) > DLEN_16) ++ dma32 = 1; ++ ++ bfin_write32(®->ctl, ppi->ppi_control); ++ bfin_write32(®->line, samples_per_line); ++ bfin_write32(®->frame, params->frame); ++ bfin_write32(®->hdly, hdelay); ++ bfin_write32(®->vdly, params->vdelay); ++ bfin_write32(®->hcnt, hcount); ++ bfin_write32(®->vcnt, params->height); ++ if (params->int_mask) ++ bfin_write32(®->imsk, params->int_mask & 0xFF); ++ break; ++ } ++ default: ++ return -EINVAL; ++ } ++ ++ if (dma32) { ++ dma_config |= WDSIZE_32 | PSIZE_32; ++ set_dma_x_count(info->dma_ch, bytes_per_line >> 2); ++ set_dma_x_modify(info->dma_ch, 4); ++ set_dma_y_modify(info->dma_ch, 4); ++ } else { ++ dma_config |= WDSIZE_16 | PSIZE_16; ++ set_dma_x_count(info->dma_ch, bytes_per_line >> 1); ++ set_dma_x_modify(info->dma_ch, 2); ++ set_dma_y_modify(info->dma_ch, 2); ++ } ++ set_dma_y_count(info->dma_ch, params->height); ++ set_dma_config(info->dma_ch, dma_config); ++ ++ SSYNC(); ++ return 0; ++} ++ ++static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr) ++{ ++ set_dma_start_addr(ppi->info->dma_ch, addr); ++} ++ ++struct ppi_if *ppi_create_instance(const struct ppi_info *info) ++{ ++ struct ppi_if *ppi; ++ ++ if (!info || !info->pin_req) ++ return NULL; ++ ++ if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) { ++ pr_err("request peripheral failed\n"); ++ return NULL; ++ } ++ ++ ppi = kzalloc(sizeof(*ppi), GFP_KERNEL); ++ if (!ppi) { ++ peripheral_free_list(info->pin_req); ++ pr_err("unable to allocate memory for ppi handle\n"); ++ return NULL; ++ } ++ ppi->ops = &ppi_ops; ++ ppi->info = info; ++ ++ pr_info("ppi probe success\n"); ++ return ppi; ++} ++EXPORT_SYMBOL(ppi_create_instance); ++ ++void ppi_delete_instance(struct ppi_if *ppi) ++{ ++ peripheral_free_list(ppi->info->pin_req); ++ kfree(ppi); ++} ++EXPORT_SYMBOL(ppi_delete_instance); ++ ++MODULE_DESCRIPTION("Analog Devices PPI driver"); ++MODULE_AUTHOR("Scott Jiang "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c +new file mode 100644 +index 0000000..2721f83 +--- /dev/null ++++ b/drivers/media/platform/coda.c +@@ -0,0 +1,2079 @@ ++/* ++ * Coda multi-standard codec IP ++ * ++ * Copyright (C) 2012 Vista Silicon S.L. ++ * Javier Martin, ++ * Xavier Duret ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "coda.h" ++ ++#define CODA_NAME "coda" ++ ++#define CODA_MAX_INSTANCES 4 ++ ++#define CODA_FMO_BUF_SIZE 32 ++#define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) ++#define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024) ++#define CODA_PARA_BUF_SIZE (10 * 1024) ++#define CODA_ISRAM_SIZE (2048 * 2) ++#define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */ ++ ++#define CODA_MAX_FRAMEBUFFERS 2 ++ ++#define MAX_W 720 ++#define MAX_H 576 ++#define CODA_MAX_FRAME_SIZE 0x90000 ++#define FMO_SLICE_SAVE_BUF_SIZE (32) ++#define CODA_DEFAULT_GAMMA 4096 ++ ++#define MIN_W 176 ++#define MIN_H 144 ++#define MAX_W 720 ++#define MAX_H 576 ++ ++#define S_ALIGN 1 /* multiple of 2 */ ++#define W_ALIGN 1 /* multiple of 2 */ ++#define H_ALIGN 1 /* multiple of 2 */ ++ ++#define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) ++ ++static int coda_debug; ++module_param(coda_debug, int, 0); ++MODULE_PARM_DESC(coda_debug, "Debug level (0-1)"); ++ ++enum { ++ V4L2_M2M_SRC = 0, ++ V4L2_M2M_DST = 1, ++}; ++ ++enum coda_fmt_type { ++ CODA_FMT_ENC, ++ CODA_FMT_RAW, ++}; ++ ++enum coda_inst_type { ++ CODA_INST_ENCODER, ++ CODA_INST_DECODER, ++}; ++ ++enum coda_product { ++ CODA_DX6 = 0xf001, ++ CODA_7541 = 0xf012, ++}; ++ ++struct coda_fmt { ++ char *name; ++ u32 fourcc; ++ enum coda_fmt_type type; ++}; ++ ++struct coda_devtype { ++ char *firmware; ++ enum coda_product product; ++ struct coda_fmt *formats; ++ unsigned int num_formats; ++ size_t workbuf_size; ++}; ++ ++/* Per-queue, driver-specific private data */ ++struct coda_q_data { ++ unsigned int width; ++ unsigned int height; ++ unsigned int sizeimage; ++ struct coda_fmt *fmt; ++}; ++ ++struct coda_aux_buf { ++ void *vaddr; ++ dma_addr_t paddr; ++ u32 size; ++}; ++ ++struct coda_dev { ++ struct v4l2_device v4l2_dev; ++ struct video_device vfd; ++ struct platform_device *plat_dev; ++ const struct coda_devtype *devtype; ++ ++ void __iomem *regs_base; ++ struct clk *clk_per; ++ struct clk *clk_ahb; ++ ++ struct coda_aux_buf codebuf; ++ struct coda_aux_buf workbuf; ++ long unsigned int iram_paddr; ++ ++ spinlock_t irqlock; ++ struct mutex dev_mutex; ++ struct v4l2_m2m_dev *m2m_dev; ++ struct vb2_alloc_ctx *alloc_ctx; ++ struct list_head instances; ++ unsigned long instance_mask; ++ struct delayed_work timeout; ++ struct completion done; ++}; ++ ++struct coda_params { ++ u8 rot_mode; ++ u8 h264_intra_qp; ++ u8 h264_inter_qp; ++ u8 mpeg4_intra_qp; ++ u8 mpeg4_inter_qp; ++ u8 gop_size; ++ int codec_mode; ++ enum v4l2_mpeg_video_multi_slice_mode slice_mode; ++ u32 framerate; ++ u16 bitrate; ++ u32 slice_max_bits; ++ u32 slice_max_mb; ++}; ++ ++struct coda_ctx { ++ struct coda_dev *dev; ++ struct list_head list; ++ int aborting; ++ int rawstreamon; ++ int compstreamon; ++ u32 isequence; ++ struct coda_q_data q_data[2]; ++ enum coda_inst_type inst_type; ++ enum v4l2_colorspace colorspace; ++ struct coda_params params; ++ struct v4l2_m2m_ctx *m2m_ctx; ++ struct v4l2_ctrl_handler ctrls; ++ struct v4l2_fh fh; ++ int gopcounter; ++ char vpu_header[3][64]; ++ int vpu_header_size[3]; ++ struct coda_aux_buf parabuf; ++ struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS]; ++ int num_internal_frames; ++ int idx; ++}; ++ ++static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 }; ++static const u8 coda_filler_size[8] = { 0, 7, 14, 13, 12, 11, 10, 9 }; ++ ++static inline void coda_write(struct coda_dev *dev, u32 data, u32 reg) ++{ ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, ++ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); ++ writel(data, dev->regs_base + reg); ++} ++ ++static inline unsigned int coda_read(struct coda_dev *dev, u32 reg) ++{ ++ u32 data; ++ data = readl(dev->regs_base + reg); ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, ++ "%s: data=0x%x, reg=0x%x\n", __func__, data, reg); ++ return data; ++} ++ ++static inline unsigned long coda_isbusy(struct coda_dev *dev) ++{ ++ return coda_read(dev, CODA_REG_BIT_BUSY); ++} ++ ++static inline int coda_is_initialized(struct coda_dev *dev) ++{ ++ return (coda_read(dev, CODA_REG_BIT_CUR_PC) != 0); ++} ++ ++static int coda_wait_timeout(struct coda_dev *dev) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(1000); ++ ++ while (coda_isbusy(dev)) { ++ if (time_after(jiffies, timeout)) ++ return -ETIMEDOUT; ++ } ++ return 0; ++} ++ ++static void coda_command_async(struct coda_ctx *ctx, int cmd) ++{ ++ struct coda_dev *dev = ctx->dev; ++ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); ++ ++ coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX); ++ coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD); ++ coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND); ++} ++ ++static int coda_command_sync(struct coda_ctx *ctx, int cmd) ++{ ++ struct coda_dev *dev = ctx->dev; ++ ++ coda_command_async(ctx, cmd); ++ return coda_wait_timeout(dev); ++} ++ ++static struct coda_q_data *get_q_data(struct coda_ctx *ctx, ++ enum v4l2_buf_type type) ++{ ++ switch (type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ return &(ctx->q_data[V4L2_M2M_SRC]); ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &(ctx->q_data[V4L2_M2M_DST]); ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++/* ++ * Add one array of supported formats for each version of Coda: ++ * i.MX27 -> codadx6 ++ * i.MX51 -> coda7 ++ * i.MX6 -> coda960 ++ */ ++static struct coda_fmt codadx6_formats[] = { ++ { ++ .name = "YUV 4:2:0 Planar", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .type = CODA_FMT_RAW, ++ }, ++ { ++ .name = "H264 Encoded Stream", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .type = CODA_FMT_ENC, ++ }, ++ { ++ .name = "MPEG4 Encoded Stream", ++ .fourcc = V4L2_PIX_FMT_MPEG4, ++ .type = CODA_FMT_ENC, ++ }, ++}; ++ ++static struct coda_fmt coda7_formats[] = { ++ { ++ .name = "YUV 4:2:0 Planar", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .type = CODA_FMT_RAW, ++ }, ++ { ++ .name = "H264 Encoded Stream", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .type = CODA_FMT_ENC, ++ }, ++ { ++ .name = "MPEG4 Encoded Stream", ++ .fourcc = V4L2_PIX_FMT_MPEG4, ++ .type = CODA_FMT_ENC, ++ }, ++}; ++ ++static struct coda_fmt *find_format(struct coda_dev *dev, struct v4l2_format *f) ++{ ++ struct coda_fmt *formats = dev->devtype->formats; ++ int num_formats = dev->devtype->num_formats; ++ unsigned int k; ++ ++ for (k = 0; k < num_formats; k++) { ++ if (formats[k].fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == num_formats) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++/* ++ * V4L2 ioctl() operations. ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, CODA_NAME, sizeof(cap->card)); ++ strlcpy(cap->bus_info, CODA_NAME, sizeof(cap->bus_info)); ++ /* ++ * This is only a mem-to-mem video device. The capture and output ++ * device capability flags are left only for backward compatibility ++ * and are scheduled for removal. ++ */ ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | ++ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, ++ enum coda_fmt_type type) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ struct coda_dev *dev = ctx->dev; ++ struct coda_fmt *formats = dev->devtype->formats; ++ struct coda_fmt *fmt; ++ int num_formats = dev->devtype->num_formats; ++ int i, num = 0; ++ ++ for (i = 0; i < num_formats; i++) { ++ if (formats[i].type == type) { ++ if (num == f->index) ++ break; ++ ++num; ++ } ++ } ++ ++ if (i < num_formats) { ++ fmt = &formats[i]; ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++ } ++ ++ /* Format not found */ ++ return -EINVAL; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(priv, f, CODA_FMT_ENC); ++} ++ ++static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(priv, f, CODA_FMT_RAW); ++} ++ ++static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) ++{ ++ struct vb2_queue *vq; ++ struct coda_q_data *q_data; ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = q_data->fmt->fourcc; ++ f->fmt.pix.width = q_data->width; ++ f->fmt.pix.height = q_data->height; ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ++ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2); ++ else /* encoded formats h.264/mpeg4 */ ++ f->fmt.pix.bytesperline = 0; ++ ++ f->fmt.pix.sizeimage = q_data->sizeimage; ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f) ++{ ++ enum v4l2_field field; ++ ++ field = f->fmt.pix.field; ++ if (field == V4L2_FIELD_ANY) ++ field = V4L2_FIELD_NONE; ++ else if (V4L2_FIELD_NONE != field) ++ return -EINVAL; ++ ++ /* V4L2 specification suggests the driver corrects the format struct ++ * if any of the dimensions is unsupported */ ++ f->fmt.pix.field = field; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, ++ W_ALIGN, &f->fmt.pix.height, ++ MIN_H, MAX_H, H_ALIGN, S_ALIGN); ++ f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2); ++ f->fmt.pix.sizeimage = f->fmt.pix.width * ++ f->fmt.pix.height * 3 / 2; ++ } else { /*encoded formats h.264/mpeg4 */ ++ f->fmt.pix.bytesperline = 0; ++ f->fmt.pix.sizeimage = CODA_MAX_FRAME_SIZE; ++ } ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct coda_fmt *fmt; ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ fmt = find_format(ctx->dev, f); ++ /* ++ * Since decoding support is not implemented yet do not allow ++ * CODA_FMT_RAW formats in the capture interface. ++ */ ++ if (!fmt || !(fmt->type == CODA_FMT_ENC)) ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; ++ ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ ret = vidioc_try_fmt(ctx->dev, f); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ struct coda_fmt *fmt; ++ int ret; ++ ++ fmt = find_format(ctx->dev, f); ++ /* ++ * Since decoding support is not implemented yet do not allow ++ * CODA_FMT formats in the capture interface. ++ */ ++ if (!fmt || !(fmt->type == CODA_FMT_RAW)) ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; ++ ++ if (!f->fmt.pix.colorspace) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; ++ ++ ret = vidioc_try_fmt(ctx->dev, f); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) ++{ ++ struct coda_q_data *q_data; ++ struct vb2_queue *vq; ++ int ret; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ if (!q_data) ++ return -EINVAL; ++ ++ if (vb2_is_busy(vq)) { ++ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ ret = vidioc_try_fmt(ctx->dev, f); ++ if (ret) ++ return ret; ++ ++ q_data->fmt = find_format(ctx->dev, f); ++ q_data->width = f->fmt.pix.width; ++ q_data->height = f->fmt.pix.height; ++ q_data->sizeimage = f->fmt.pix.sizeimage; ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "Setting format for type %d, wxh: %dx%d, fmt: %d\n", ++ f->type, q_data->width, q_data->height, q_data->fmt->fourcc); ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) ++ return ret; ++ ++ return vidioc_s_fmt(fh_to_ctx(priv), f); ++} ++ ++static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_out(file, priv, f); ++ if (ret) ++ return ret; ++ ++ ret = vidioc_s_fmt(ctx, f); ++ if (ret) ++ ctx->colorspace = f->fmt.pix.colorspace; ++ ++ return ret; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbufs) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(priv); ++ ++ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); ++} ++ ++static const struct v4l2_ioctl_ops coda_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = vidioc_g_fmt, ++ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, ++ ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++}; ++ ++/* ++ * Mem-to-mem operations. ++ */ ++static void coda_device_run(void *m2m_priv) ++{ ++ struct coda_ctx *ctx = m2m_priv; ++ struct coda_q_data *q_data_src, *q_data_dst; ++ struct vb2_buffer *src_buf, *dst_buf; ++ struct coda_dev *dev = ctx->dev; ++ int force_ipicture; ++ int quant_param = 0; ++ u32 picture_y, picture_cb, picture_cr; ++ u32 pic_stream_buffer_addr, pic_stream_buffer_size; ++ u32 dst_fourcc; ++ ++ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ++ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ dst_fourcc = q_data_dst->fmt->fourcc; ++ ++ src_buf->v4l2_buf.sequence = ctx->isequence; ++ dst_buf->v4l2_buf.sequence = ctx->isequence; ++ ctx->isequence++; ++ ++ /* ++ * Workaround coda firmware BUG that only marks the first ++ * frame as IDR. This is a problem for some decoders that can't ++ * recover when a frame is lost. ++ */ ++ if (src_buf->v4l2_buf.sequence % ctx->params.gop_size) { ++ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; ++ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; ++ } else { ++ src_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; ++ src_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; ++ } ++ ++ /* ++ * Copy headers at the beginning of the first frame for H.264 only. ++ * In MPEG4 they are already copied by the coda. ++ */ ++ if (src_buf->v4l2_buf.sequence == 0) { ++ pic_stream_buffer_addr = ++ vb2_dma_contig_plane_dma_addr(dst_buf, 0) + ++ ctx->vpu_header_size[0] + ++ ctx->vpu_header_size[1] + ++ ctx->vpu_header_size[2]; ++ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE - ++ ctx->vpu_header_size[0] - ++ ctx->vpu_header_size[1] - ++ ctx->vpu_header_size[2]; ++ memcpy(vb2_plane_vaddr(dst_buf, 0), ++ &ctx->vpu_header[0][0], ctx->vpu_header_size[0]); ++ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0], ++ &ctx->vpu_header[1][0], ctx->vpu_header_size[1]); ++ memcpy(vb2_plane_vaddr(dst_buf, 0) + ctx->vpu_header_size[0] + ++ ctx->vpu_header_size[1], &ctx->vpu_header[2][0], ++ ctx->vpu_header_size[2]); ++ } else { ++ pic_stream_buffer_addr = ++ vb2_dma_contig_plane_dma_addr(dst_buf, 0); ++ pic_stream_buffer_size = CODA_MAX_FRAME_SIZE; ++ } ++ ++ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { ++ force_ipicture = 1; ++ switch (dst_fourcc) { ++ case V4L2_PIX_FMT_H264: ++ quant_param = ctx->params.h264_intra_qp; ++ break; ++ case V4L2_PIX_FMT_MPEG4: ++ quant_param = ctx->params.mpeg4_intra_qp; ++ break; ++ default: ++ v4l2_warn(&ctx->dev->v4l2_dev, ++ "cannot set intra qp, fmt not supported\n"); ++ break; ++ } ++ } else { ++ force_ipicture = 0; ++ switch (dst_fourcc) { ++ case V4L2_PIX_FMT_H264: ++ quant_param = ctx->params.h264_inter_qp; ++ break; ++ case V4L2_PIX_FMT_MPEG4: ++ quant_param = ctx->params.mpeg4_inter_qp; ++ break; ++ default: ++ v4l2_warn(&ctx->dev->v4l2_dev, ++ "cannot set inter qp, fmt not supported\n"); ++ break; ++ } ++ } ++ ++ /* submit */ ++ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode, CODA_CMD_ENC_PIC_ROT_MODE); ++ coda_write(dev, quant_param, CODA_CMD_ENC_PIC_QS); ++ ++ ++ picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0); ++ picture_cb = picture_y + q_data_src->width * q_data_src->height; ++ picture_cr = picture_cb + q_data_src->width / 2 * ++ q_data_src->height / 2; ++ ++ coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); ++ coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); ++ coda_write(dev, picture_cr, CODA_CMD_ENC_PIC_SRC_ADDR_CR); ++ coda_write(dev, force_ipicture << 1 & 0x2, ++ CODA_CMD_ENC_PIC_OPTION); ++ ++ coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START); ++ coda_write(dev, pic_stream_buffer_size / 1024, ++ CODA_CMD_ENC_PIC_BB_SIZE); ++ ++ if (dev->devtype->product == CODA_7541) { ++ coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE | ++ CODA7_USE_ME_ENABLE | CODA7_USE_HOST_ME_ENABLE, ++ CODA7_REG_BIT_AXI_SRAM_USE); ++ } ++ ++ /* 1 second timeout in case CODA locks up */ ++ schedule_delayed_work(&dev->timeout, HZ); ++ ++ INIT_COMPLETION(dev->done); ++ coda_command_async(ctx, CODA_COMMAND_PIC_RUN); ++} ++ ++static int coda_job_ready(void *m2m_priv) ++{ ++ struct coda_ctx *ctx = m2m_priv; ++ ++ /* ++ * For both 'P' and 'key' frame cases 1 picture ++ * and 1 frame are needed. ++ */ ++ if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) || ++ !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) { ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "not ready: not enough video buffers.\n"); ++ return 0; ++ } ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "job ready\n"); ++ return 1; ++} ++ ++static void coda_job_abort(void *priv) ++{ ++ struct coda_ctx *ctx = priv; ++ struct coda_dev *dev = ctx->dev; ++ ++ ctx->aborting = 1; ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "Aborting task\n"); ++ ++ v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); ++} ++ ++static void coda_lock(void *m2m_priv) ++{ ++ struct coda_ctx *ctx = m2m_priv; ++ struct coda_dev *pcdev = ctx->dev; ++ mutex_lock(&pcdev->dev_mutex); ++} ++ ++static void coda_unlock(void *m2m_priv) ++{ ++ struct coda_ctx *ctx = m2m_priv; ++ struct coda_dev *pcdev = ctx->dev; ++ mutex_unlock(&pcdev->dev_mutex); ++} ++ ++static struct v4l2_m2m_ops coda_m2m_ops = { ++ .device_run = coda_device_run, ++ .job_ready = coda_job_ready, ++ .job_abort = coda_job_abort, ++ .lock = coda_lock, ++ .unlock = coda_unlock, ++}; ++ ++static void set_default_params(struct coda_ctx *ctx) ++{ ++ struct coda_dev *dev = ctx->dev; ++ ++ ctx->params.codec_mode = CODA_MODE_INVALID; ++ ctx->colorspace = V4L2_COLORSPACE_REC709; ++ ctx->params.framerate = 30; ++ ctx->aborting = 0; ++ ++ /* Default formats for output and input queues */ ++ ctx->q_data[V4L2_M2M_SRC].fmt = &dev->devtype->formats[0]; ++ ctx->q_data[V4L2_M2M_DST].fmt = &dev->devtype->formats[1]; ++ ctx->q_data[V4L2_M2M_SRC].width = MAX_W; ++ ctx->q_data[V4L2_M2M_SRC].height = MAX_H; ++ ctx->q_data[V4L2_M2M_SRC].sizeimage = (MAX_W * MAX_H * 3) / 2; ++ ctx->q_data[V4L2_M2M_DST].width = MAX_W; ++ ctx->q_data[V4L2_M2M_DST].height = MAX_H; ++ ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; ++} ++ ++/* ++ * Queue operations ++ */ ++static int coda_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(vq); ++ struct coda_q_data *q_data; ++ unsigned int size; ++ ++ q_data = get_q_data(ctx, vq->type); ++ size = q_data->sizeimage; ++ ++ *nplanes = 1; ++ sizes[0] = size; ++ ++ alloc_ctxs[0] = ctx->dev->alloc_ctx; ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "get %d buffer(s) of size %d each.\n", *nbuffers, size); ++ ++ return 0; ++} ++ ++static int coda_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ struct coda_q_data *q_data; ++ ++ q_data = get_q_data(ctx, vb->vb2_queue->type); ++ ++ if (vb2_plane_size(vb, 0) < q_data->sizeimage) { ++ v4l2_warn(&ctx->dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), ++ (long)q_data->sizeimage); ++ return -EINVAL; ++ } ++ ++ vb2_set_plane_payload(vb, 0, q_data->sizeimage); ++ ++ return 0; ++} ++ ++static void coda_buf_queue(struct vb2_buffer *vb) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); ++} ++ ++static void coda_wait_prepare(struct vb2_queue *q) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(q); ++ coda_unlock(ctx); ++} ++ ++static void coda_wait_finish(struct vb2_queue *q) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(q); ++ coda_lock(ctx); ++} ++ ++static void coda_free_framebuffers(struct coda_ctx *ctx) ++{ ++ int i; ++ ++ for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) { ++ if (ctx->internal_frames[i].vaddr) { ++ dma_free_coherent(&ctx->dev->plat_dev->dev, ++ ctx->internal_frames[i].size, ++ ctx->internal_frames[i].vaddr, ++ ctx->internal_frames[i].paddr); ++ ctx->internal_frames[i].vaddr = NULL; ++ } ++ } ++} ++ ++static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) ++{ ++ struct coda_dev *dev = ctx->dev; ++ ++ int height = q_data->height; ++ int width = q_data->width; ++ u32 *p; ++ int i; ++ ++ /* Allocate frame buffers */ ++ ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS; ++ for (i = 0; i < ctx->num_internal_frames; i++) { ++ ctx->internal_frames[i].size = q_data->sizeimage; ++ if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) ++ ctx->internal_frames[i].size += width / 2 * height / 2; ++ ctx->internal_frames[i].vaddr = dma_alloc_coherent( ++ &dev->plat_dev->dev, ctx->internal_frames[i].size, ++ &ctx->internal_frames[i].paddr, GFP_KERNEL); ++ if (!ctx->internal_frames[i].vaddr) { ++ coda_free_framebuffers(ctx); ++ return -ENOMEM; ++ } ++ } ++ ++ /* Register frame buffers in the parameter buffer */ ++ p = ctx->parabuf.vaddr; ++ ++ if (dev->devtype->product == CODA_DX6) { ++ for (i = 0; i < ctx->num_internal_frames; i++) { ++ p[i * 3] = ctx->internal_frames[i].paddr; /* Y */ ++ p[i * 3 + 1] = p[i * 3] + width * height; /* Cb */ ++ p[i * 3 + 2] = p[i * 3 + 1] + width / 2 * height / 2; /* Cr */ ++ } ++ } else { ++ for (i = 0; i < ctx->num_internal_frames; i += 2) { ++ p[i * 3 + 1] = ctx->internal_frames[i].paddr; /* Y */ ++ p[i * 3] = p[i * 3 + 1] + width * height; /* Cb */ ++ p[i * 3 + 3] = p[i * 3] + (width / 2) * (height / 2); /* Cr */ ++ ++ if (fourcc == V4L2_PIX_FMT_H264) ++ p[96 + i + 1] = p[i * 3 + 3] + (width / 2) * (height / 2); ++ ++ if (i + 1 < ctx->num_internal_frames) { ++ p[i * 3 + 2] = ctx->internal_frames[i+1].paddr; /* Y */ ++ p[i * 3 + 5] = p[i * 3 + 2] + width * height ; /* Cb */ ++ p[i * 3 + 4] = p[i * 3 + 5] + (width / 2) * (height / 2); /* Cr */ ++ ++ if (fourcc == V4L2_PIX_FMT_H264) ++ p[96 + i] = p[i * 3 + 4] + (width / 2) * (height / 2); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++static int coda_h264_padding(int size, char *p) ++{ ++ int nal_size; ++ int diff; ++ ++ diff = size - (size & ~0x7); ++ if (diff == 0) ++ return 0; ++ ++ nal_size = coda_filler_size[diff]; ++ memcpy(p, coda_filler_nal, nal_size); ++ ++ /* Add rbsp stop bit and trailing at the end */ ++ *(p + nal_size - 1) = 0x80; ++ ++ return nal_size; ++} ++ ++static int coda_start_streaming(struct vb2_queue *q, unsigned int count) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(q); ++ struct v4l2_device *v4l2_dev = &ctx->dev->v4l2_dev; ++ u32 bitstream_buf, bitstream_size; ++ struct coda_dev *dev = ctx->dev; ++ struct coda_q_data *q_data_src, *q_data_dst; ++ struct vb2_buffer *buf; ++ u32 dst_fourcc; ++ u32 value; ++ int ret; ++ ++ if (count < 1) ++ return -EINVAL; ++ ++ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ++ ctx->rawstreamon = 1; ++ else ++ ctx->compstreamon = 1; ++ ++ /* Don't start the coda unless both queues are on */ ++ if (!(ctx->rawstreamon & ctx->compstreamon)) ++ return 0; ++ ++ if (coda_isbusy(dev)) ++ if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) ++ return -EBUSY; ++ ++ ctx->gopcounter = ctx->params.gop_size - 1; ++ ++ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); ++ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ bitstream_size = q_data_dst->sizeimage; ++ dst_fourcc = q_data_dst->fmt->fourcc; ++ ++ /* Find out whether coda must encode or decode */ ++ if (q_data_src->fmt->type == CODA_FMT_RAW && ++ q_data_dst->fmt->type == CODA_FMT_ENC) { ++ ctx->inst_type = CODA_INST_ENCODER; ++ } else if (q_data_src->fmt->type == CODA_FMT_ENC && ++ q_data_dst->fmt->type == CODA_FMT_RAW) { ++ ctx->inst_type = CODA_INST_DECODER; ++ v4l2_err(v4l2_dev, "decoding not supported.\n"); ++ return -EINVAL; ++ } else { ++ v4l2_err(v4l2_dev, "couldn't tell instance type.\n"); ++ return -EINVAL; ++ } ++ ++ if (!coda_is_initialized(dev)) { ++ v4l2_err(v4l2_dev, "coda is not initialized.\n"); ++ return -EFAULT; ++ } ++ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); ++ coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx)); ++ coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx)); ++ switch (dev->devtype->product) { ++ case CODA_DX6: ++ coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN | ++ CODADX6_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); ++ break; ++ default: ++ coda_write(dev, CODA7_STREAM_BUF_DYNALLOC_EN | ++ CODA7_STREAM_BUF_PIC_RESET, CODA_REG_BIT_STREAM_CTRL); ++ } ++ ++ if (dev->devtype->product == CODA_DX6) { ++ /* Configure the coda */ ++ coda_write(dev, dev->iram_paddr, CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR); ++ } ++ ++ /* Could set rotation here if needed */ ++ switch (dev->devtype->product) { ++ case CODA_DX6: ++ value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; ++ break; ++ default: ++ value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; ++ } ++ value |= (q_data_src->height & CODA_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); ++ coda_write(dev, ctx->params.framerate, ++ CODA_CMD_ENC_SEQ_SRC_F_RATE); ++ ++ switch (dst_fourcc) { ++ case V4L2_PIX_FMT_MPEG4: ++ if (dev->devtype->product == CODA_DX6) ++ ctx->params.codec_mode = CODADX6_MODE_ENCODE_MP4; ++ else ++ ctx->params.codec_mode = CODA7_MODE_ENCODE_MP4; ++ ++ coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); ++ coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); ++ break; ++ case V4L2_PIX_FMT_H264: ++ if (dev->devtype->product == CODA_DX6) ++ ctx->params.codec_mode = CODADX6_MODE_ENCODE_H264; ++ else ++ ctx->params.codec_mode = CODA7_MODE_ENCODE_H264; ++ ++ coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); ++ coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA); ++ break; ++ default: ++ v4l2_err(v4l2_dev, ++ "dst format (0x%08x) invalid.\n", dst_fourcc); ++ return -EINVAL; ++ } ++ ++ switch (ctx->params.slice_mode) { ++ case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE: ++ value = 0; ++ break; ++ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB: ++ value = (ctx->params.slice_max_mb & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; ++ value |= (1 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET; ++ value |= 1 & CODA_SLICING_MODE_MASK; ++ break; ++ case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES: ++ value = (ctx->params.slice_max_bits & CODA_SLICING_SIZE_MASK) << CODA_SLICING_SIZE_OFFSET; ++ value |= (0 & CODA_SLICING_UNIT_MASK) << CODA_SLICING_UNIT_OFFSET; ++ value |= 1 & CODA_SLICING_MODE_MASK; ++ break; ++ } ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_SLICE_MODE); ++ value = ctx->params.gop_size & CODA_GOP_SIZE_MASK; ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_GOP_SIZE); ++ ++ if (ctx->params.bitrate) { ++ /* Rate control enabled */ ++ value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK) << CODA_RATECONTROL_BITRATE_OFFSET; ++ value |= 1 & CODA_RATECONTROL_ENABLE_MASK; ++ } else { ++ value = 0; ++ } ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA); ++ ++ coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE); ++ coda_write(dev, 0, CODA_CMD_ENC_SEQ_INTRA_REFRESH); ++ ++ coda_write(dev, bitstream_buf, CODA_CMD_ENC_SEQ_BB_START); ++ coda_write(dev, bitstream_size / 1024, CODA_CMD_ENC_SEQ_BB_SIZE); ++ ++ /* set default gamma */ ++ value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET; ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA); ++ ++ value = (CODA_DEFAULT_GAMMA > 0) << CODA_OPTION_GAMMA_OFFSET; ++ value |= (0 & CODA_OPTION_SLICEREPORT_MASK) << CODA_OPTION_SLICEREPORT_OFFSET; ++ coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); ++ ++ if (dst_fourcc == V4L2_PIX_FMT_H264) { ++ value = (FMO_SLICE_SAVE_BUF_SIZE << 7); ++ value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET; ++ value |= 0 & CODA_FMOPARAM_SLICENUM_MASK; ++ if (dev->devtype->product == CODA_DX6) { ++ coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO); ++ } else { ++ coda_write(dev, dev->iram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE); ++ coda_write(dev, 48 * 1024, CODA7_CMD_ENC_SEQ_SEARCH_SIZE); ++ } ++ } ++ ++ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) ++ return -EFAULT; ++ ++ ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); ++ if (ret < 0) ++ return ret; ++ ++ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); ++ coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE); ++ if (dev->devtype->product != CODA_DX6) { ++ coda_write(dev, round_up(q_data_src->width, 8), CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE); ++ coda_write(dev, dev->iram_paddr + 48 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR); ++ coda_write(dev, dev->iram_paddr + 53 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR); ++ coda_write(dev, dev->iram_paddr + 58 * 1024, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR); ++ coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); ++ coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); ++ } ++ if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ /* Save stream headers */ ++ buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ switch (dst_fourcc) { ++ case V4L2_PIX_FMT_H264: ++ /* ++ * Get SPS in the first frame and copy it to an ++ * intermediate buffer. ++ */ ++ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); ++ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); ++ coda_write(dev, CODA_HEADER_H264_SPS, CODA_CMD_ENC_HEADER_CODE); ++ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - ++ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); ++ memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0), ++ ctx->vpu_header_size[0]); ++ ++ /* ++ * Get PPS in the first frame and copy it to an ++ * intermediate buffer. ++ */ ++ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); ++ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); ++ coda_write(dev, CODA_HEADER_H264_PPS, CODA_CMD_ENC_HEADER_CODE); ++ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - ++ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); ++ memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ++ ctx->vpu_header_size[1]); ++ /* ++ * Length of H.264 headers is variable and thus it might not be ++ * aligned for the coda to append the encoded frame. In that is ++ * the case a filler NAL must be added to header 2. ++ */ ++ ctx->vpu_header_size[2] = coda_h264_padding( ++ (ctx->vpu_header_size[0] + ++ ctx->vpu_header_size[1]), ++ ctx->vpu_header[2]); ++ break; ++ case V4L2_PIX_FMT_MPEG4: ++ /* ++ * Get VOS in the first frame and copy it to an ++ * intermediate buffer ++ */ ++ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); ++ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); ++ coda_write(dev, CODA_HEADER_MP4V_VOS, CODA_CMD_ENC_HEADER_CODE); ++ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - ++ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); ++ memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0), ++ ctx->vpu_header_size[0]); ++ ++ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); ++ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); ++ coda_write(dev, CODA_HEADER_MP4V_VIS, CODA_CMD_ENC_HEADER_CODE); ++ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n"); ++ return -ETIMEDOUT; ++ } ++ ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - ++ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); ++ memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), ++ ctx->vpu_header_size[1]); ++ ++ coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); ++ coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); ++ coda_write(dev, CODA_HEADER_MP4V_VOL, CODA_CMD_ENC_HEADER_CODE); ++ if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { ++ v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n"); ++ return -ETIMEDOUT; ++ } ++ ctx->vpu_header_size[2] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - ++ coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); ++ memcpy(&ctx->vpu_header[2][0], vb2_plane_vaddr(buf, 0), ++ ctx->vpu_header_size[2]); ++ break; ++ default: ++ /* No more formats need to save headers at the moment */ ++ break; ++ } ++ ++ return 0; ++} ++ ++static int coda_stop_streaming(struct vb2_queue *q) ++{ ++ struct coda_ctx *ctx = vb2_get_drv_priv(q); ++ struct coda_dev *dev = ctx->dev; ++ ++ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "%s: output\n", __func__); ++ ctx->rawstreamon = 0; ++ } else { ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "%s: capture\n", __func__); ++ ctx->compstreamon = 0; ++ } ++ ++ /* Don't stop the coda unless both queues are off */ ++ if (ctx->rawstreamon || ctx->compstreamon) ++ return 0; ++ ++ if (coda_isbusy(dev)) { ++ if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) { ++ v4l2_warn(&dev->v4l2_dev, ++ "%s: timeout, sending SEQ_END anyway\n", __func__); ++ } ++ } ++ ++ cancel_delayed_work(&dev->timeout); ++ ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, ++ "%s: sent command 'SEQ_END' to coda\n", __func__); ++ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { ++ v4l2_err(&dev->v4l2_dev, ++ "CODA_COMMAND_SEQ_END failed\n"); ++ return -ETIMEDOUT; ++ } ++ ++ coda_free_framebuffers(ctx); ++ ++ return 0; ++} ++ ++static struct vb2_ops coda_qops = { ++ .queue_setup = coda_queue_setup, ++ .buf_prepare = coda_buf_prepare, ++ .buf_queue = coda_buf_queue, ++ .wait_prepare = coda_wait_prepare, ++ .wait_finish = coda_wait_finish, ++ .start_streaming = coda_start_streaming, ++ .stop_streaming = coda_stop_streaming, ++}; ++ ++static int coda_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct coda_ctx *ctx = ++ container_of(ctrl->handler, struct coda_ctx, ctrls); ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "s_ctrl: id = %d, val = %d\n", ctrl->id, ctrl->val); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ ctx->params.rot_mode |= CODA_MIR_HOR; ++ else ++ ctx->params.rot_mode &= ~CODA_MIR_HOR; ++ break; ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ ctx->params.rot_mode |= CODA_MIR_VER; ++ else ++ ctx->params.rot_mode &= ~CODA_MIR_VER; ++ break; ++ case V4L2_CID_MPEG_VIDEO_BITRATE: ++ ctx->params.bitrate = ctrl->val / 1000; ++ break; ++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE: ++ ctx->params.gop_size = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: ++ ctx->params.h264_intra_qp = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: ++ ctx->params.h264_inter_qp = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: ++ ctx->params.mpeg4_intra_qp = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: ++ ctx->params.mpeg4_inter_qp = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: ++ ctx->params.slice_mode = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: ++ ctx->params.slice_max_mb = ctrl->val; ++ break; ++ case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: ++ ctx->params.slice_max_bits = ctrl->val * 8; ++ break; ++ case V4L2_CID_MPEG_VIDEO_HEADER_MODE: ++ break; ++ default: ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "Invalid control, id=%d, val=%d\n", ++ ctrl->id, ctrl->val); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static struct v4l2_ctrl_ops coda_ctrl_ops = { ++ .s_ctrl = coda_s_ctrl, ++}; ++ ++static int coda_ctrls_setup(struct coda_ctx *ctx) ++{ ++ v4l2_ctrl_handler_init(&ctx->ctrls, 9); ++ ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_BITRATE, 0, 32767000, 1, 0); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 60, 1, 16); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 25); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 25); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP, 1, 31, 1, 2); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP, 1, 31, 1, 2); ++ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, ++ V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES, 0x0, ++ V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB, 1, 0x3fffffff, 1, 1); ++ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES, 1, 0x3fffffff, 1, 500); ++ v4l2_ctrl_new_std_menu(&ctx->ctrls, &coda_ctrl_ops, ++ V4L2_CID_MPEG_VIDEO_HEADER_MODE, ++ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, ++ (1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE), ++ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); ++ ++ if (ctx->ctrls.error) { ++ v4l2_err(&ctx->dev->v4l2_dev, "control initialization error (%d)", ++ ctx->ctrls.error); ++ return -EINVAL; ++ } ++ ++ return v4l2_ctrl_handler_setup(&ctx->ctrls); ++} ++ ++static int coda_queue_init(void *priv, struct vb2_queue *src_vq, ++ struct vb2_queue *dst_vq) ++{ ++ struct coda_ctx *ctx = priv; ++ int ret; ++ ++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ src_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ src_vq->drv_priv = ctx; ++ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ src_vq->ops = &coda_qops; ++ src_vq->mem_ops = &vb2_dma_contig_memops; ++ ++ ret = vb2_queue_init(src_vq); ++ if (ret) ++ return ret; ++ ++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ dst_vq->drv_priv = ctx; ++ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ dst_vq->ops = &coda_qops; ++ dst_vq->mem_ops = &vb2_dma_contig_memops; ++ ++ return vb2_queue_init(dst_vq); ++} ++ ++static int coda_next_free_instance(struct coda_dev *dev) ++{ ++ return ffz(dev->instance_mask); ++} ++ ++static int coda_open(struct file *file) ++{ ++ struct coda_dev *dev = video_drvdata(file); ++ struct coda_ctx *ctx = NULL; ++ int ret = 0; ++ int idx; ++ ++ idx = coda_next_free_instance(dev); ++ if (idx >= CODA_MAX_INSTANCES) ++ return -EBUSY; ++ set_bit(idx, &dev->instance_mask); ++ ++ ctx = kzalloc(sizeof *ctx, GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ v4l2_fh_init(&ctx->fh, video_devdata(file)); ++ file->private_data = &ctx->fh; ++ v4l2_fh_add(&ctx->fh); ++ ctx->dev = dev; ++ ctx->idx = idx; ++ ++ set_default_params(ctx); ++ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, ++ &coda_queue_init); ++ if (IS_ERR(ctx->m2m_ctx)) { ++ int ret = PTR_ERR(ctx->m2m_ctx); ++ ++ v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", ++ __func__, ret); ++ goto err; ++ } ++ ret = coda_ctrls_setup(ctx); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n"); ++ goto err; ++ } ++ ++ ctx->fh.ctrl_handler = &ctx->ctrls; ++ ++ ctx->parabuf.vaddr = dma_alloc_coherent(&dev->plat_dev->dev, ++ CODA_PARA_BUF_SIZE, &ctx->parabuf.paddr, GFP_KERNEL); ++ if (!ctx->parabuf.vaddr) { ++ v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ coda_lock(ctx); ++ list_add(&ctx->list, &dev->instances); ++ coda_unlock(ctx); ++ ++ clk_prepare_enable(dev->clk_per); ++ clk_prepare_enable(dev->clk_ahb); ++ ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n", ++ ctx->idx, ctx); ++ ++ return 0; ++ ++err: ++ v4l2_fh_del(&ctx->fh); ++ v4l2_fh_exit(&ctx->fh); ++ kfree(ctx); ++ return ret; ++} ++ ++static int coda_release(struct file *file) ++{ ++ struct coda_dev *dev = video_drvdata(file); ++ struct coda_ctx *ctx = fh_to_ctx(file->private_data); ++ ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", ++ ctx); ++ ++ coda_lock(ctx); ++ list_del(&ctx->list); ++ coda_unlock(ctx); ++ ++ dma_free_coherent(&dev->plat_dev->dev, CODA_PARA_BUF_SIZE, ++ ctx->parabuf.vaddr, ctx->parabuf.paddr); ++ v4l2_m2m_ctx_release(ctx->m2m_ctx); ++ v4l2_ctrl_handler_free(&ctx->ctrls); ++ clk_disable_unprepare(dev->clk_per); ++ clk_disable_unprepare(dev->clk_ahb); ++ v4l2_fh_del(&ctx->fh); ++ v4l2_fh_exit(&ctx->fh); ++ clear_bit(ctx->idx, &dev->instance_mask); ++ kfree(ctx); ++ ++ return 0; ++} ++ ++static unsigned int coda_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(file->private_data); ++ int ret; ++ ++ coda_lock(ctx); ++ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); ++ coda_unlock(ctx); ++ return ret; ++} ++ ++static int coda_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct coda_ctx *ctx = fh_to_ctx(file->private_data); ++ ++ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); ++} ++ ++static const struct v4l2_file_operations coda_fops = { ++ .owner = THIS_MODULE, ++ .open = coda_open, ++ .release = coda_release, ++ .poll = coda_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = coda_mmap, ++}; ++ ++static irqreturn_t coda_irq_handler(int irq, void *data) ++{ ++ struct vb2_buffer *src_buf, *dst_buf; ++ struct coda_dev *dev = data; ++ u32 wr_ptr, start_ptr; ++ struct coda_ctx *ctx; ++ ++ cancel_delayed_work(&dev->timeout); ++ ++ /* read status register to attend the IRQ */ ++ coda_read(dev, CODA_REG_BIT_INT_STATUS); ++ coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET, ++ CODA_REG_BIT_INT_CLEAR); ++ ++ ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); ++ if (ctx == NULL) { ++ v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (ctx->aborting) { ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "task has been aborted\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (coda_isbusy(ctx->dev)) { ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, ++ "coda is still busy!!!!\n"); ++ return IRQ_NONE; ++ } ++ ++ complete(&dev->done); ++ ++ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); ++ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); ++ ++ /* Get results from the coda */ ++ coda_read(dev, CODA_RET_ENC_PIC_TYPE); ++ start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START); ++ wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)); ++ /* Calculate bytesused field */ ++ if (dst_buf->v4l2_buf.sequence == 0) { ++ dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr) + ++ ctx->vpu_header_size[0] + ++ ctx->vpu_header_size[1] + ++ ctx->vpu_header_size[2]; ++ } else { ++ dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr); ++ } ++ ++ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n", ++ wr_ptr - start_ptr); ++ ++ coda_read(dev, CODA_RET_ENC_PIC_SLICE_NUM); ++ coda_read(dev, CODA_RET_ENC_PIC_FLAG); ++ ++ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) { ++ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME; ++ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME; ++ } else { ++ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME; ++ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME; ++ } ++ ++ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); ++ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); ++ ++ ctx->gopcounter--; ++ if (ctx->gopcounter < 0) ++ ctx->gopcounter = ctx->params.gop_size - 1; ++ ++ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, ++ "job finished: encoding frame (%d) (%s)\n", ++ dst_buf->v4l2_buf.sequence, ++ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? ++ "KEYFRAME" : "PFRAME"); ++ ++ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); ++ ++ return IRQ_HANDLED; ++} ++ ++static void coda_timeout(struct work_struct *work) ++{ ++ struct coda_ctx *ctx; ++ struct coda_dev *dev = container_of(to_delayed_work(work), ++ struct coda_dev, timeout); ++ ++ if (completion_done(&dev->done)) ++ return; ++ ++ complete(&dev->done); ++ ++ v4l2_err(&dev->v4l2_dev, "CODA PIC_RUN timeout, stopping all streams\n"); ++ ++ mutex_lock(&dev->dev_mutex); ++ list_for_each_entry(ctx, &dev->instances, list) { ++ v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ } ++ mutex_unlock(&dev->dev_mutex); ++} ++ ++static u32 coda_supported_firmwares[] = { ++ CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5), ++ CODA_FIRMWARE_VERNUM(CODA_7541, 13, 4, 29), ++}; ++ ++static bool coda_firmware_supported(u32 vernum) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(coda_supported_firmwares); i++) ++ if (vernum == coda_supported_firmwares[i]) ++ return true; ++ return false; ++} ++ ++static char *coda_product_name(int product) ++{ ++ static char buf[9]; ++ ++ switch (product) { ++ case CODA_DX6: ++ return "CodaDx6"; ++ case CODA_7541: ++ return "CODA7541"; ++ default: ++ snprintf(buf, sizeof(buf), "(0x%04x)", product); ++ return buf; ++ } ++} ++ ++static int coda_hw_init(struct coda_dev *dev) ++{ ++ u16 product, major, minor, release; ++ u32 data; ++ u16 *p; ++ int i; ++ ++ clk_prepare_enable(dev->clk_per); ++ clk_prepare_enable(dev->clk_ahb); ++ ++ /* ++ * Copy the first CODA_ISRAM_SIZE in the internal SRAM. ++ * The 16-bit chars in the code buffer are in memory access ++ * order, re-sort them to CODA order for register download. ++ * Data in this SRAM survives a reboot. ++ */ ++ p = (u16 *)dev->codebuf.vaddr; ++ if (dev->devtype->product == CODA_DX6) { ++ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { ++ data = CODA_DOWN_ADDRESS_SET(i) | ++ CODA_DOWN_DATA_SET(p[i ^ 1]); ++ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); ++ } ++ } else { ++ for (i = 0; i < (CODA_ISRAM_SIZE / 2); i++) { ++ data = CODA_DOWN_ADDRESS_SET(i) | ++ CODA_DOWN_DATA_SET(p[round_down(i, 4) + ++ 3 - (i % 4)]); ++ coda_write(dev, data, CODA_REG_BIT_CODE_DOWN); ++ } ++ } ++ ++ /* Tell the BIT where to find everything it needs */ ++ coda_write(dev, dev->workbuf.paddr, ++ CODA_REG_BIT_WORK_BUF_ADDR); ++ coda_write(dev, dev->codebuf.paddr, ++ CODA_REG_BIT_CODE_BUF_ADDR); ++ coda_write(dev, 0, CODA_REG_BIT_CODE_RUN); ++ ++ /* Set default values */ ++ switch (dev->devtype->product) { ++ case CODA_DX6: ++ coda_write(dev, CODADX6_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL); ++ break; ++ default: ++ coda_write(dev, CODA7_STREAM_BUF_PIC_FLUSH, CODA_REG_BIT_STREAM_CTRL); ++ } ++ coda_write(dev, 0, CODA_REG_BIT_FRAME_MEM_CTRL); ++ ++ if (dev->devtype->product != CODA_DX6) ++ coda_write(dev, 0, CODA7_REG_BIT_AXI_SRAM_USE); ++ ++ coda_write(dev, CODA_INT_INTERRUPT_ENABLE, ++ CODA_REG_BIT_INT_ENABLE); ++ ++ /* Reset VPU and start processor */ ++ data = coda_read(dev, CODA_REG_BIT_CODE_RESET); ++ data |= CODA_REG_RESET_ENABLE; ++ coda_write(dev, data, CODA_REG_BIT_CODE_RESET); ++ udelay(10); ++ data &= ~CODA_REG_RESET_ENABLE; ++ coda_write(dev, data, CODA_REG_BIT_CODE_RESET); ++ coda_write(dev, CODA_REG_RUN_ENABLE, CODA_REG_BIT_CODE_RUN); ++ ++ /* Load firmware */ ++ coda_write(dev, 0, CODA_CMD_FIRMWARE_VERNUM); ++ coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY); ++ coda_write(dev, 0, CODA_REG_BIT_RUN_INDEX); ++ coda_write(dev, 0, CODA_REG_BIT_RUN_COD_STD); ++ coda_write(dev, CODA_COMMAND_FIRMWARE_GET, CODA_REG_BIT_RUN_COMMAND); ++ if (coda_wait_timeout(dev)) { ++ clk_disable_unprepare(dev->clk_per); ++ clk_disable_unprepare(dev->clk_ahb); ++ v4l2_err(&dev->v4l2_dev, "firmware get command error\n"); ++ return -EIO; ++ } ++ ++ /* Check we are compatible with the loaded firmware */ ++ data = coda_read(dev, CODA_CMD_FIRMWARE_VERNUM); ++ product = CODA_FIRMWARE_PRODUCT(data); ++ major = CODA_FIRMWARE_MAJOR(data); ++ minor = CODA_FIRMWARE_MINOR(data); ++ release = CODA_FIRMWARE_RELEASE(data); ++ ++ clk_disable_unprepare(dev->clk_per); ++ clk_disable_unprepare(dev->clk_ahb); ++ ++ if (product != dev->devtype->product) { ++ v4l2_err(&dev->v4l2_dev, "Wrong firmware. Hw: %s, Fw: %s," ++ " Version: %u.%u.%u\n", ++ coda_product_name(dev->devtype->product), ++ coda_product_name(product), major, minor, release); ++ return -EINVAL; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, "Initialized %s.\n", ++ coda_product_name(product)); ++ ++ if (coda_firmware_supported(data)) { ++ v4l2_info(&dev->v4l2_dev, "Firmware version: %u.%u.%u\n", ++ major, minor, release); ++ } else { ++ v4l2_warn(&dev->v4l2_dev, "Unsupported firmware version: " ++ "%u.%u.%u\n", major, minor, release); ++ } ++ ++ return 0; ++} ++ ++static void coda_fw_callback(const struct firmware *fw, void *context) ++{ ++ struct coda_dev *dev = context; ++ struct platform_device *pdev = dev->plat_dev; ++ int ret; ++ ++ if (!fw) { ++ v4l2_err(&dev->v4l2_dev, "firmware request failed\n"); ++ return; ++ } ++ ++ /* allocate auxiliary per-device code buffer for the BIT processor */ ++ dev->codebuf.size = fw->size; ++ dev->codebuf.vaddr = dma_alloc_coherent(&pdev->dev, fw->size, ++ &dev->codebuf.paddr, ++ GFP_KERNEL); ++ if (!dev->codebuf.vaddr) { ++ dev_err(&pdev->dev, "failed to allocate code buffer\n"); ++ return; ++ } ++ ++ /* Copy the whole firmware image to the code buffer */ ++ memcpy(dev->codebuf.vaddr, fw->data, fw->size); ++ release_firmware(fw); ++ ++ ret = coda_hw_init(dev); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "HW initialization failed\n"); ++ return; ++ } ++ ++ dev->vfd.fops = &coda_fops, ++ dev->vfd.ioctl_ops = &coda_ioctl_ops; ++ dev->vfd.release = video_device_release_empty, ++ dev->vfd.lock = &dev->dev_mutex; ++ dev->vfd.v4l2_dev = &dev->v4l2_dev; ++ dev->vfd.vfl_dir = VFL_DIR_M2M; ++ snprintf(dev->vfd.name, sizeof(dev->vfd.name), "%s", CODA_NAME); ++ video_set_drvdata(&dev->vfd, dev); ++ ++ dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); ++ if (IS_ERR(dev->alloc_ctx)) { ++ v4l2_err(&dev->v4l2_dev, "Failed to alloc vb2 context\n"); ++ return; ++ } ++ ++ dev->m2m_dev = v4l2_m2m_init(&coda_m2m_ops); ++ if (IS_ERR(dev->m2m_dev)) { ++ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ++ goto rel_ctx; ++ } ++ ++ ret = video_register_device(&dev->vfd, VFL_TYPE_GRABBER, 0); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); ++ goto rel_m2m; ++ } ++ v4l2_info(&dev->v4l2_dev, "codec registered as /dev/video%d\n", ++ dev->vfd.num); ++ ++ return; ++ ++rel_m2m: ++ v4l2_m2m_release(dev->m2m_dev); ++rel_ctx: ++ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); ++} ++ ++static int coda_firmware_request(struct coda_dev *dev) ++{ ++ char *fw = dev->devtype->firmware; ++ ++ dev_dbg(&dev->plat_dev->dev, "requesting firmware '%s' for %s\n", fw, ++ coda_product_name(dev->devtype->product)); ++ ++ return request_firmware_nowait(THIS_MODULE, true, ++ fw, &dev->plat_dev->dev, GFP_KERNEL, dev, coda_fw_callback); ++} ++ ++enum coda_platform { ++ CODA_IMX27, ++ CODA_IMX53, ++}; ++ ++static const struct coda_devtype coda_devdata[] = { ++ [CODA_IMX27] = { ++ .firmware = "v4l-codadx6-imx27.bin", ++ .product = CODA_DX6, ++ .formats = codadx6_formats, ++ .num_formats = ARRAY_SIZE(codadx6_formats), ++ }, ++ [CODA_IMX53] = { ++ .firmware = "v4l-coda7541-imx53.bin", ++ .product = CODA_7541, ++ .formats = coda7_formats, ++ .num_formats = ARRAY_SIZE(coda7_formats), ++ }, ++}; ++ ++static struct platform_device_id coda_platform_ids[] = { ++ { .name = "coda-imx27", .driver_data = CODA_IMX27 }, ++ { .name = "coda-imx53", .driver_data = CODA_IMX53 }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(platform, coda_platform_ids); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id coda_dt_ids[] = { ++ { .compatible = "fsl,imx27-vpu", .data = &coda_platform_ids[CODA_IMX27] }, ++ { .compatible = "fsl,imx53-vpu", .data = &coda_devdata[CODA_IMX53] }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, coda_dt_ids); ++#endif ++ ++static int __devinit coda_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *of_id = ++ of_match_device(of_match_ptr(coda_dt_ids), &pdev->dev); ++ const struct platform_device_id *pdev_id; ++ struct coda_dev *dev; ++ struct resource *res; ++ int ret, irq; ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL); ++ if (!dev) { ++ dev_err(&pdev->dev, "Not enough memory for %s\n", ++ CODA_NAME); ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&dev->irqlock); ++ INIT_LIST_HEAD(&dev->instances); ++ INIT_DELAYED_WORK(&dev->timeout, coda_timeout); ++ init_completion(&dev->done); ++ complete(&dev->done); ++ ++ dev->plat_dev = pdev; ++ dev->clk_per = devm_clk_get(&pdev->dev, "per"); ++ if (IS_ERR(dev->clk_per)) { ++ dev_err(&pdev->dev, "Could not get per clock\n"); ++ return PTR_ERR(dev->clk_per); ++ } ++ ++ dev->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(dev->clk_ahb)) { ++ dev_err(&pdev->dev, "Could not get ahb clock\n"); ++ return PTR_ERR(dev->clk_ahb); ++ } ++ ++ /* Get memory for physical registers */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "failed to get memory region resource\n"); ++ return -ENOENT; ++ } ++ ++ if (devm_request_mem_region(&pdev->dev, res->start, ++ resource_size(res), CODA_NAME) == NULL) { ++ dev_err(&pdev->dev, "failed to request memory region\n"); ++ return -ENOENT; ++ } ++ dev->regs_base = devm_ioremap(&pdev->dev, res->start, ++ resource_size(res)); ++ if (!dev->regs_base) { ++ dev_err(&pdev->dev, "failed to ioremap address region\n"); ++ return -ENOENT; ++ } ++ ++ /* IRQ */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "failed to get irq resource\n"); ++ return -ENOENT; ++ } ++ ++ if (devm_request_irq(&pdev->dev, irq, coda_irq_handler, ++ 0, CODA_NAME, dev) < 0) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return -ENOENT; ++ } ++ ++ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); ++ if (ret) ++ return ret; ++ ++ mutex_init(&dev->dev_mutex); ++ ++ pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); ++ ++ if (of_id) { ++ dev->devtype = of_id->data; ++ } else if (pdev_id) { ++ dev->devtype = &coda_devdata[pdev_id->driver_data]; ++ } else { ++ v4l2_device_unregister(&dev->v4l2_dev); ++ return -EINVAL; ++ } ++ ++ /* allocate auxiliary per-device buffers for the BIT processor */ ++ switch (dev->devtype->product) { ++ case CODA_DX6: ++ dev->workbuf.size = CODADX6_WORK_BUF_SIZE; ++ break; ++ default: ++ dev->workbuf.size = CODA7_WORK_BUF_SIZE; ++ } ++ dev->workbuf.vaddr = dma_alloc_coherent(&pdev->dev, dev->workbuf.size, ++ &dev->workbuf.paddr, ++ GFP_KERNEL); ++ if (!dev->workbuf.vaddr) { ++ dev_err(&pdev->dev, "failed to allocate work buffer\n"); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ return -ENOMEM; ++ } ++ ++ if (dev->devtype->product == CODA_DX6) { ++ dev->iram_paddr = 0xffff4c00; ++ } else { ++ void __iomem *iram_vaddr; ++ ++ iram_vaddr = iram_alloc(CODA7_IRAM_SIZE, ++ &dev->iram_paddr); ++ if (!iram_vaddr) { ++ dev_err(&pdev->dev, "unable to alloc iram\n"); ++ return -ENOMEM; ++ } ++ } ++ ++ platform_set_drvdata(pdev, dev); ++ ++ return coda_firmware_request(dev); ++} ++ ++static int coda_remove(struct platform_device *pdev) ++{ ++ struct coda_dev *dev = platform_get_drvdata(pdev); ++ ++ video_unregister_device(&dev->vfd); ++ if (dev->m2m_dev) ++ v4l2_m2m_release(dev->m2m_dev); ++ if (dev->alloc_ctx) ++ vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ if (dev->iram_paddr) ++ iram_free(dev->iram_paddr, CODA7_IRAM_SIZE); ++ if (dev->codebuf.vaddr) ++ dma_free_coherent(&pdev->dev, dev->codebuf.size, ++ &dev->codebuf.vaddr, dev->codebuf.paddr); ++ if (dev->workbuf.vaddr) ++ dma_free_coherent(&pdev->dev, dev->workbuf.size, &dev->workbuf.vaddr, ++ dev->workbuf.paddr); ++ return 0; ++} ++ ++static struct platform_driver coda_driver = { ++ .probe = coda_probe, ++ .remove = __devexit_p(coda_remove), ++ .driver = { ++ .name = CODA_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(coda_dt_ids), ++ }, ++ .id_table = coda_platform_ids, ++}; ++ ++module_platform_driver(coda_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Javier Martin "); ++MODULE_DESCRIPTION("Coda multi-standard codec V4L2 driver"); +diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h +new file mode 100644 +index 0000000..f3f5e43 +--- /dev/null ++++ b/drivers/media/platform/coda.h +@@ -0,0 +1,238 @@ ++/* ++ * linux/drivers/media/platform/coda/coda_regs.h ++ * ++ * Copyright (C) 2012 Vista Silicon SL ++ * Javier Martin ++ * Xavier Duret ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef _REGS_CODA_H_ ++#define _REGS_CODA_H_ ++ ++/* HW registers */ ++#define CODA_REG_BIT_CODE_RUN 0x000 ++#define CODA_REG_RUN_ENABLE (1 << 0) ++#define CODA_REG_BIT_CODE_DOWN 0x004 ++#define CODA_DOWN_ADDRESS_SET(x) (((x) & 0xffff) << 16) ++#define CODA_DOWN_DATA_SET(x) ((x) & 0xffff) ++#define CODA_REG_BIT_HOST_IN_REQ 0x008 ++#define CODA_REG_BIT_INT_CLEAR 0x00c ++#define CODA_REG_BIT_INT_CLEAR_SET 0x1 ++#define CODA_REG_BIT_INT_STATUS 0x010 ++#define CODA_REG_BIT_CODE_RESET 0x014 ++#define CODA_REG_RESET_ENABLE (1 << 0) ++#define CODA_REG_BIT_CUR_PC 0x018 ++ ++/* Static SW registers */ ++#define CODA_REG_BIT_CODE_BUF_ADDR 0x100 ++#define CODA_REG_BIT_WORK_BUF_ADDR 0x104 ++#define CODA_REG_BIT_PARA_BUF_ADDR 0x108 ++#define CODA_REG_BIT_STREAM_CTRL 0x10c ++#define CODA7_STREAM_BUF_PIC_RESET (1 << 4) ++#define CODADX6_STREAM_BUF_PIC_RESET (1 << 3) ++#define CODA7_STREAM_BUF_PIC_FLUSH (1 << 3) ++#define CODADX6_STREAM_BUF_PIC_FLUSH (1 << 2) ++#define CODA7_STREAM_BUF_DYNALLOC_EN (1 << 5) ++#define CODADX6_STREAM_BUF_DYNALLOC_EN (1 << 4) ++#define CODA_STREAM_CHKDIS_OFFSET (1 << 1) ++#define CODA_STREAM_ENDIAN_SELECT (1 << 0) ++#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110 ++#define CODA_IMAGE_ENDIAN_SELECT (1 << 0) ++#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x)) ++#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x)) ++#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140 ++#define CODA7_REG_BIT_AXI_SRAM_USE 0x140 ++#define CODA7_USE_BIT_ENABLE (1 << 0) ++#define CODA7_USE_HOST_BIT_ENABLE (1 << 7) ++#define CODA7_USE_ME_ENABLE (1 << 4) ++#define CODA7_USE_HOST_ME_ENABLE (1 << 11) ++#define CODA_REG_BIT_BUSY 0x160 ++#define CODA_REG_BIT_BUSY_FLAG 1 ++#define CODA_REG_BIT_RUN_COMMAND 0x164 ++#define CODA_COMMAND_SEQ_INIT 1 ++#define CODA_COMMAND_SEQ_END 2 ++#define CODA_COMMAND_PIC_RUN 3 ++#define CODA_COMMAND_SET_FRAME_BUF 4 ++#define CODA_COMMAND_ENCODE_HEADER 5 ++#define CODA_COMMAND_ENC_PARA_SET 6 ++#define CODA_COMMAND_DEC_PARA_SET 7 ++#define CODA_COMMAND_DEC_BUF_FLUSH 8 ++#define CODA_COMMAND_RC_CHANGE_PARAMETER 9 ++#define CODA_COMMAND_FIRMWARE_GET 0xf ++#define CODA_REG_BIT_RUN_INDEX 0x168 ++#define CODA_INDEX_SET(x) ((x) & 0x3) ++#define CODA_REG_BIT_RUN_COD_STD 0x16c ++#define CODADX6_MODE_DECODE_MP4 0 ++#define CODADX6_MODE_ENCODE_MP4 1 ++#define CODADX6_MODE_DECODE_H264 2 ++#define CODADX6_MODE_ENCODE_H264 3 ++#define CODA7_MODE_DECODE_H264 0 ++#define CODA7_MODE_DECODE_VC1 1 ++#define CODA7_MODE_DECODE_MP2 2 ++#define CODA7_MODE_DECODE_MP4 3 ++#define CODA7_MODE_DECODE_DV3 3 ++#define CODA7_MODE_DECODE_RV 4 ++#define CODA7_MODE_DECODE_MJPG 5 ++#define CODA7_MODE_ENCODE_H264 8 ++#define CODA7_MODE_ENCODE_MP4 11 ++#define CODA7_MODE_ENCODE_MJPG 13 ++#define CODA_MODE_INVALID 0xffff ++#define CODA_REG_BIT_INT_ENABLE 0x170 ++#define CODA_INT_INTERRUPT_ENABLE (1 << 3) ++ ++/* ++ * Commands' mailbox: ++ * registers with offsets in the range 0x180-0x1d0 ++ * have different meaning depending on the command being ++ * issued. ++ */ ++ ++/* Encoder Sequence Initialization */ ++#define CODA_CMD_ENC_SEQ_BB_START 0x180 ++#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 ++#define CODA_CMD_ENC_SEQ_OPTION 0x188 ++#define CODA_OPTION_GAMMA_OFFSET 7 ++#define CODA_OPTION_GAMMA_MASK 0x01 ++#define CODA_OPTION_LIMITQP_OFFSET 6 ++#define CODA_OPTION_LIMITQP_MASK 0x01 ++#define CODA_OPTION_RCINTRAQP_OFFSET 5 ++#define CODA_OPTION_RCINTRAQP_MASK 0x01 ++#define CODA_OPTION_FMO_OFFSET 4 ++#define CODA_OPTION_FMO_MASK 0x01 ++#define CODA_OPTION_SLICEREPORT_OFFSET 1 ++#define CODA_OPTION_SLICEREPORT_MASK 0x01 ++#define CODA_CMD_ENC_SEQ_COD_STD 0x18c ++#define CODA_STD_MPEG4 0 ++#define CODA_STD_H263 1 ++#define CODA_STD_H264 2 ++#define CODA_STD_MJPG 3 ++#define CODA_CMD_ENC_SEQ_SRC_SIZE 0x190 ++#define CODA7_PICWIDTH_OFFSET 16 ++#define CODA7_PICWIDTH_MASK 0xffff ++#define CODADX6_PICWIDTH_OFFSET 10 ++#define CODADX6_PICWIDTH_MASK 0x3ff ++#define CODA_PICHEIGHT_OFFSET 0 ++#define CODA_PICHEIGHT_MASK 0x3ff ++#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 ++#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 ++#define CODA_MP4PARAM_VERID_OFFSET 6 ++#define CODA_MP4PARAM_VERID_MASK 0x01 ++#define CODA_MP4PARAM_INTRADCVLCTHR_OFFSET 2 ++#define CODA_MP4PARAM_INTRADCVLCTHR_MASK 0x07 ++#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_OFFSET 1 ++#define CODA_MP4PARAM_REVERSIBLEVLCENABLE_MASK 0x01 ++#define CODA_MP4PARAM_DATAPARTITIONENABLE_OFFSET 0 ++#define CODA_MP4PARAM_DATAPARTITIONENABLE_MASK 0x01 ++#define CODA_CMD_ENC_SEQ_263_PARA 0x19c ++#define CODA_263PARAM_ANNEXJENABLE_OFFSET 2 ++#define CODA_263PARAM_ANNEXJENABLE_MASK 0x01 ++#define CODA_263PARAM_ANNEXKENABLE_OFFSET 1 ++#define CODA_263PARAM_ANNEXKENABLE_MASK 0x01 ++#define CODA_263PARAM_ANNEXTENABLE_OFFSET 0 ++#define CODA_263PARAM_ANNEXTENABLE_MASK 0x01 ++#define CODA_CMD_ENC_SEQ_264_PARA 0x1a0 ++#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_OFFSET 12 ++#define CODA_264PARAM_DEBLKFILTEROFFSETBETA_MASK 0x0f ++#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_OFFSET 8 ++#define CODA_264PARAM_DEBLKFILTEROFFSETALPHA_MASK 0x0f ++#define CODA_264PARAM_DISABLEDEBLK_OFFSET 6 ++#define CODA_264PARAM_DISABLEDEBLK_MASK 0x01 ++#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_OFFSET 5 ++#define CODA_264PARAM_CONSTRAINEDINTRAPREDFLAG_MASK 0x01 ++#define CODA_264PARAM_CHROMAQPOFFSET_OFFSET 0 ++#define CODA_264PARAM_CHROMAQPOFFSET_MASK 0x1f ++#define CODA_CMD_ENC_SEQ_SLICE_MODE 0x1a4 ++#define CODA_SLICING_SIZE_OFFSET 2 ++#define CODA_SLICING_SIZE_MASK 0x3fffffff ++#define CODA_SLICING_UNIT_OFFSET 1 ++#define CODA_SLICING_UNIT_MASK 0x01 ++#define CODA_SLICING_MODE_OFFSET 0 ++#define CODA_SLICING_MODE_MASK 0x01 ++#define CODA_CMD_ENC_SEQ_GOP_SIZE 0x1a8 ++#define CODA_GOP_SIZE_OFFSET 0 ++#define CODA_GOP_SIZE_MASK 0x3f ++#define CODA_CMD_ENC_SEQ_RC_PARA 0x1ac ++#define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 ++#define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 ++#define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 ++#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7f ++#define CODA_RATECONTROL_BITRATE_OFFSET 1 ++#define CODA_RATECONTROL_BITRATE_MASK 0x7f ++#define CODA_RATECONTROL_ENABLE_OFFSET 0 ++#define CODA_RATECONTROL_ENABLE_MASK 0x01 ++#define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 ++#define CODA_CMD_ENC_SEQ_INTRA_REFRESH 0x1b4 ++#define CODADX6_CMD_ENC_SEQ_FMO 0x1b8 ++#define CODA_FMOPARAM_TYPE_OFFSET 4 ++#define CODA_FMOPARAM_TYPE_MASK 1 ++#define CODA_FMOPARAM_SLICENUM_OFFSET 0 ++#define CODA_FMOPARAM_SLICENUM_MASK 0x0f ++#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8 ++#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc ++#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8 ++#define CODA_QPMAX_OFFSET 0 ++#define CODA_QPMAX_MASK 0x3f ++#define CODA_CMD_ENC_SEQ_RC_GAMMA 0x1cc ++#define CODA_GAMMA_OFFSET 0 ++#define CODA_GAMMA_MASK 0xffff ++#define CODA_RET_ENC_SEQ_SUCCESS 0x1c0 ++ ++/* Encoder Picture Run */ ++#define CODA_CMD_ENC_PIC_SRC_ADDR_Y 0x180 ++#define CODA_CMD_ENC_PIC_SRC_ADDR_CB 0x184 ++#define CODA_CMD_ENC_PIC_SRC_ADDR_CR 0x188 ++#define CODA_CMD_ENC_PIC_QS 0x18c ++#define CODA_CMD_ENC_PIC_ROT_MODE 0x190 ++#define CODA_ROT_MIR_ENABLE (1 << 4) ++#define CODA_ROT_0 (0x0 << 0) ++#define CODA_ROT_90 (0x1 << 0) ++#define CODA_ROT_180 (0x2 << 0) ++#define CODA_ROT_270 (0x3 << 0) ++#define CODA_MIR_NONE (0x0 << 2) ++#define CODA_MIR_VER (0x1 << 2) ++#define CODA_MIR_HOR (0x2 << 2) ++#define CODA_MIR_VER_HOR (0x3 << 2) ++#define CODA_CMD_ENC_PIC_OPTION 0x194 ++#define CODA_CMD_ENC_PIC_BB_START 0x198 ++#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c ++#define CODA_RET_ENC_PIC_TYPE 0x1c4 ++#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc ++#define CODA_RET_ENC_PIC_FLAG 0x1d0 ++ ++/* Set Frame Buffer */ ++#define CODA_CMD_SET_FRAME_BUF_NUM 0x180 ++#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184 ++#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190 ++#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194 ++#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198 ++#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c ++#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0 ++#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8 ++ ++/* Encoder Header */ ++#define CODA_CMD_ENC_HEADER_CODE 0x180 ++#define CODA_GAMMA_OFFSET 0 ++#define CODA_HEADER_H264_SPS 0 ++#define CODA_HEADER_H264_PPS 1 ++#define CODA_HEADER_MP4V_VOL 0 ++#define CODA_HEADER_MP4V_VOS 1 ++#define CODA_HEADER_MP4V_VIS 2 ++#define CODA_CMD_ENC_HEADER_BB_START 0x184 ++#define CODA_CMD_ENC_HEADER_BB_SIZE 0x188 ++ ++/* Get Version */ ++#define CODA_CMD_FIRMWARE_VERNUM 0x1c0 ++#define CODA_FIRMWARE_PRODUCT(x) (((x) >> 16) & 0xffff) ++#define CODA_FIRMWARE_MAJOR(x) (((x) >> 12) & 0x0f) ++#define CODA_FIRMWARE_MINOR(x) (((x) >> 8) & 0x0f) ++#define CODA_FIRMWARE_RELEASE(x) ((x) & 0xff) ++#define CODA_FIRMWARE_VERNUM(product, major, minor, release) \ ++ ((product) << 16 | ((major) << 12) | \ ++ ((minor) << 8) | (release)) ++ ++#endif +diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig +new file mode 100644 +index 0000000..ccfde4e +--- /dev/null ++++ b/drivers/media/platform/davinci/Kconfig +@@ -0,0 +1,111 @@ ++config VIDEO_DAVINCI_VPIF_DISPLAY ++ tristate "DM646x/DA850/OMAPL138 EVM Video Display" ++ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) ++ select VIDEOBUF2_DMA_CONTIG ++ select VIDEO_DAVINCI_VPIF ++ select VIDEO_ADV7343 if MEDIA_SUBDRV_AUTOSELECT ++ select VIDEO_THS7303 if MEDIA_SUBDRV_AUTOSELECT ++ help ++ Enables Davinci VPIF module used for display devices. ++ This module is common for following DM6467/DA850/OMAPL138 ++ based display devices. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpif_display. ++ ++config VIDEO_DAVINCI_VPIF_CAPTURE ++ tristate "DM646x/DA850/OMAPL138 EVM Video Capture" ++ depends on VIDEO_DEV && (MACH_DAVINCI_DM6467_EVM || MACH_DAVINCI_DA850_EVM) ++ select VIDEOBUF2_DMA_CONTIG ++ select VIDEO_DAVINCI_VPIF ++ help ++ Enables Davinci VPIF module used for captur devices. ++ This module is common for following DM6467/DA850/OMAPL138 ++ based capture devices. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpif_capture. ++ ++config VIDEO_DAVINCI_VPIF ++ tristate "DaVinci VPIF Driver" ++ depends on VIDEO_DAVINCI_VPIF_DISPLAY || VIDEO_DAVINCI_VPIF_CAPTURE ++ help ++ Support for DaVinci VPIF Driver. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpif. ++ ++config VIDEO_VPSS_SYSTEM ++ tristate "VPSS System module driver" ++ depends on ARCH_DAVINCI ++ help ++ Support for vpss system module for video driver ++ ++config VIDEO_VPFE_CAPTURE ++ tristate "VPFE Video Capture Driver" ++ depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) ++ depends on I2C ++ select VIDEOBUF_DMA_CONTIG ++ help ++ Support for DMx/AMx VPFE based frame grabber. This is the ++ common V4L2 module for following DMx/AMx SoCs from Texas ++ Instruments:- DM6446, DM365, DM355 & AM3517/05. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpfe-capture. ++ ++config VIDEO_DM6446_CCDC ++ tristate "DM6446 CCDC HW module" ++ depends on VIDEO_VPFE_CAPTURE ++ select VIDEO_VPSS_SYSTEM ++ default y ++ help ++ Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces ++ with decoder modules such as TVP5146 over BT656 or ++ sensor module such as MT9T001 over a raw interface. This ++ module configures the interface and CCDC/ISIF to do ++ video frame capture from slave decoders. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpfe. ++ ++config VIDEO_DM355_CCDC ++ tristate "DM355 CCDC HW module" ++ depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE ++ select VIDEO_VPSS_SYSTEM ++ default y ++ help ++ Enables DM355 CCD hw module. DM355 CCDC hw interfaces ++ with decoder modules such as TVP5146 over BT656 or ++ sensor module such as MT9T001 over a raw interface. This ++ module configures the interface and CCDC/ISIF to do ++ video frame capture from a slave decoders ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpfe. ++ ++config VIDEO_ISIF ++ tristate "ISIF HW module" ++ depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE ++ select VIDEO_VPSS_SYSTEM ++ default y ++ help ++ Enables ISIF hw module. This is the hardware module for ++ configuring ISIF in VPFE to capture Raw Bayer RGB data from ++ a image sensor or YUV data from a YUV source. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpfe. ++ ++config VIDEO_DAVINCI_VPBE_DISPLAY ++ tristate "DM644X/DM365/DM355 VPBE HW module" ++ depends on ARCH_DAVINCI_DM644x || ARCH_DAVINCI_DM355 || ARCH_DAVINCI_DM365 ++ select VIDEO_VPSS_SYSTEM ++ select VIDEOBUF2_DMA_CONTIG ++ help ++ Enables Davinci VPBE module used for display devices. ++ This module is common for following DM644x/DM365/DM355 ++ based display devices. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called vpbe. +diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/davinci/Makefile +new file mode 100644 +index 0000000..f40f521 +--- /dev/null ++++ b/drivers/media/platform/davinci/Makefile +@@ -0,0 +1,20 @@ ++# ++# Makefile for the davinci video device drivers. ++# ++ ++# VPIF ++obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o ++ ++#VPIF Display driver ++obj-$(CONFIG_VIDEO_DAVINCI_VPIF_DISPLAY) += vpif_display.o ++#VPIF Capture driver ++obj-$(CONFIG_VIDEO_DAVINCI_VPIF_CAPTURE) += vpif_capture.o ++ ++# Capture: DM6446 and DM355 ++obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o ++obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o ++obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o ++obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o ++obj-$(CONFIG_VIDEO_ISIF) += isif.o ++obj-$(CONFIG_VIDEO_DAVINCI_VPBE_DISPLAY) += vpbe.o vpbe_osd.o \ ++ vpbe_venc.o vpbe_display.o +diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/davinci/ccdc_hw_device.h +new file mode 100644 +index 0000000..86b9b35 +--- /dev/null ++++ b/drivers/media/platform/davinci/ccdc_hw_device.h +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (C) 2008-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * ccdc device API ++ */ ++#ifndef _CCDC_HW_DEVICE_H ++#define _CCDC_HW_DEVICE_H ++ ++#ifdef __KERNEL__ ++#include ++#include ++#include ++#include ++ ++/* ++ * ccdc hw operations ++ */ ++struct ccdc_hw_ops { ++ /* Pointer to initialize function to initialize ccdc device */ ++ int (*open) (struct device *dev); ++ /* Pointer to deinitialize function */ ++ int (*close) (struct device *dev); ++ /* set ccdc base address */ ++ void (*set_ccdc_base)(void *base, int size); ++ /* Pointer to function to enable or disable ccdc */ ++ void (*enable) (int en); ++ /* reset sbl. only for 6446 */ ++ void (*reset) (void); ++ /* enable output to sdram */ ++ void (*enable_out_to_sdram) (int en); ++ /* Pointer to function to set hw parameters */ ++ int (*set_hw_if_params) (struct vpfe_hw_if_param *param); ++ /* get interface parameters */ ++ int (*get_hw_if_params) (struct vpfe_hw_if_param *param); ++ /* ++ * Pointer to function to set parameters. Used ++ * for implementing VPFE_S_CCDC_PARAMS ++ */ ++ int (*set_params) (void *params); ++ /* ++ * Pointer to function to get parameter. Used ++ * for implementing VPFE_G_CCDC_PARAMS ++ */ ++ int (*get_params) (void *params); ++ /* Pointer to function to configure ccdc */ ++ int (*configure) (void); ++ ++ /* Pointer to function to set buffer type */ ++ int (*set_buftype) (enum ccdc_buftype buf_type); ++ /* Pointer to function to get buffer type */ ++ enum ccdc_buftype (*get_buftype) (void); ++ /* Pointer to function to set frame format */ ++ int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); ++ /* Pointer to function to get frame format */ ++ enum ccdc_frmfmt (*get_frame_format) (void); ++ /* enumerate hw pix formats */ ++ int (*enum_pix)(u32 *hw_pix, int i); ++ /* Pointer to function to set buffer type */ ++ u32 (*get_pixel_format) (void); ++ /* Pointer to function to get pixel format. */ ++ int (*set_pixel_format) (u32 pixfmt); ++ /* Pointer to function to set image window */ ++ int (*set_image_window) (struct v4l2_rect *win); ++ /* Pointer to function to set image window */ ++ void (*get_image_window) (struct v4l2_rect *win); ++ /* Pointer to function to get line length */ ++ unsigned int (*get_line_length) (void); ++ ++ /* Query CCDC control IDs */ ++ int (*queryctrl)(struct v4l2_queryctrl *qctrl); ++ /* Set CCDC control */ ++ int (*set_control)(struct v4l2_control *ctrl); ++ /* Get CCDC control */ ++ int (*get_control)(struct v4l2_control *ctrl); ++ ++ /* Pointer to function to set frame buffer address */ ++ void (*setfbaddr) (unsigned long addr); ++ /* Pointer to function to get field id */ ++ int (*getfid) (void); ++}; ++ ++struct ccdc_hw_device { ++ /* ccdc device name */ ++ char name[32]; ++ /* module owner */ ++ struct module *owner; ++ /* hw ops */ ++ struct ccdc_hw_ops hw_ops; ++}; ++ ++/* Used by CCDC module to register & unregister with vpfe capture driver */ ++int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); ++void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); ++ ++#endif ++#endif +diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c +new file mode 100644 +index 0000000..030950d +--- /dev/null ++++ b/drivers/media/platform/davinci/dm355_ccdc.c +@@ -0,0 +1,1076 @@ ++/* ++ * Copyright (C) 2005-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * CCDC hardware module for DM355 ++ * ------------------------------ ++ * ++ * This module is for configuring DM355 CCD controller of VPFE to capture ++ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules ++ * such as Defect Pixel Correction, Color Space Conversion etc to ++ * pre-process the Bayer RGB data, before writing it to SDRAM. This ++ * module also allows application to configure individual ++ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. ++ * To do so, application include dm355_ccdc.h and vpfe_capture.h header ++ * files. The setparams() API is called by vpfe_capture driver ++ * to configure module parameters ++ * ++ * TODO: 1) Raw bayer parameter settings and bayer capture ++ * 2) Split module parameter structure to module specific ioctl structs ++ * 3) add support for lense shading correction ++ * 4) investigate if enum used for user space type definition ++ * to be replaced by #defines or integer ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dm355_ccdc_regs.h" ++#include "ccdc_hw_device.h" ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("CCDC Driver for DM355"); ++MODULE_AUTHOR("Texas Instruments"); ++ ++static struct ccdc_oper_config { ++ struct device *dev; ++ /* CCDC interface type */ ++ enum vpfe_hw_if_type if_type; ++ /* Raw Bayer configuration */ ++ struct ccdc_params_raw bayer; ++ /* YCbCr configuration */ ++ struct ccdc_params_ycbcr ycbcr; ++ /* Master clock */ ++ struct clk *mclk; ++ /* slave clock */ ++ struct clk *sclk; ++ /* ccdc base address */ ++ void __iomem *base_addr; ++} ccdc_cfg = { ++ /* Raw configurations */ ++ .bayer = { ++ .pix_fmt = CCDC_PIXFMT_RAW, ++ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, ++ .win = CCDC_WIN_VGA, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .gain = { ++ .r_ye = 256, ++ .gb_g = 256, ++ .gr_cy = 256, ++ .b_mg = 256 ++ }, ++ .config_params = { ++ .datasft = 2, ++ .mfilt1 = CCDC_NO_MEDIAN_FILTER1, ++ .mfilt2 = CCDC_NO_MEDIAN_FILTER2, ++ .alaw = { ++ .gama_wd = 2, ++ }, ++ .blk_clamp = { ++ .sample_pixel = 1, ++ .dc_sub = 25 ++ }, ++ .col_pat_field0 = { ++ .olop = CCDC_GREEN_BLUE, ++ .olep = CCDC_BLUE, ++ .elop = CCDC_RED, ++ .elep = CCDC_GREEN_RED ++ }, ++ .col_pat_field1 = { ++ .olop = CCDC_GREEN_BLUE, ++ .olep = CCDC_BLUE, ++ .elop = CCDC_RED, ++ .elep = CCDC_GREEN_RED ++ }, ++ }, ++ }, ++ /* YCbCr configuration */ ++ .ycbcr = { ++ .win = CCDC_WIN_PAL, ++ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, ++ .frm_fmt = CCDC_FRMFMT_INTERLACED, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .bt656_enable = 1, ++ .pix_order = CCDC_PIXORDER_CBYCRY, ++ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED ++ }, ++}; ++ ++ ++/* Raw Bayer formats */ ++static u32 ccdc_raw_bayer_pix_formats[] = ++ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; ++ ++/* Raw YUV formats */ ++static u32 ccdc_raw_yuv_pix_formats[] = ++ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; ++ ++/* register access routines */ ++static inline u32 regr(u32 offset) ++{ ++ return __raw_readl(ccdc_cfg.base_addr + offset); ++} ++ ++static inline void regw(u32 val, u32 offset) ++{ ++ __raw_writel(val, ccdc_cfg.base_addr + offset); ++} ++ ++static void ccdc_enable(int en) ++{ ++ unsigned int temp; ++ temp = regr(SYNCEN); ++ temp &= (~CCDC_SYNCEN_VDHDEN_MASK); ++ temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); ++ regw(temp, SYNCEN); ++} ++ ++static void ccdc_enable_output_to_sdram(int en) ++{ ++ unsigned int temp; ++ temp = regr(SYNCEN); ++ temp &= (~(CCDC_SYNCEN_WEN_MASK)); ++ temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); ++ regw(temp, SYNCEN); ++} ++ ++static void ccdc_config_gain_offset(void) ++{ ++ /* configure gain */ ++ regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); ++ regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); ++ regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); ++ regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); ++ /* configure offset */ ++ regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); ++} ++ ++/* ++ * ccdc_restore_defaults() ++ * This function restore power on defaults in the ccdc registers ++ */ ++static int ccdc_restore_defaults(void) ++{ ++ int i; ++ ++ dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); ++ /* set all registers to zero */ ++ for (i = 0; i <= CCDC_REG_LAST; i += 4) ++ regw(0, i); ++ ++ /* now override the values with power on defaults in registers */ ++ regw(MODESET_DEFAULT, MODESET); ++ /* no culling support */ ++ regw(CULH_DEFAULT, CULH); ++ regw(CULV_DEFAULT, CULV); ++ /* Set default Gain and Offset */ ++ ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; ++ ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; ++ ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; ++ ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; ++ ccdc_config_gain_offset(); ++ regw(OUTCLIP_DEFAULT, OUTCLIP); ++ regw(LSCCFG2_DEFAULT, LSCCFG2); ++ /* select ccdc input */ ++ if (vpss_select_ccdc_source(VPSS_CCDCIN)) { ++ dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); ++ return -EFAULT; ++ } ++ /* select ccdc clock */ ++ if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { ++ dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); ++ return -EFAULT; ++ } ++ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); ++ return 0; ++} ++ ++static int ccdc_open(struct device *device) ++{ ++ return ccdc_restore_defaults(); ++} ++ ++static int ccdc_close(struct device *device) ++{ ++ /* disable clock */ ++ vpss_enable_clock(VPSS_CCDC_CLOCK, 0); ++ /* do nothing for now */ ++ return 0; ++} ++/* ++ * ccdc_setwin() ++ * This function will configure the window size to ++ * be capture in CCDC reg. ++ */ ++static void ccdc_setwin(struct v4l2_rect *image_win, ++ enum ccdc_frmfmt frm_fmt, int ppc) ++{ ++ int horz_start, horz_nr_pixels; ++ int vert_start, vert_nr_lines; ++ int mid_img = 0; ++ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); ++ ++ /* ++ * ppc - per pixel count. indicates how many pixels per cell ++ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. ++ * raw capture this is 1 ++ */ ++ horz_start = image_win->left << (ppc - 1); ++ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; ++ ++ /* Writing the horizontal info into the registers */ ++ regw(horz_start, SPH); ++ regw(horz_nr_pixels, NPH); ++ vert_start = image_win->top; ++ ++ if (frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ vert_nr_lines = (image_win->height >> 1) - 1; ++ vert_start >>= 1; ++ /* Since first line doesn't have any data */ ++ vert_start += 1; ++ /* configure VDINT0 and VDINT1 */ ++ regw(vert_start, VDINT0); ++ } else { ++ /* Since first line doesn't have any data */ ++ vert_start += 1; ++ vert_nr_lines = image_win->height - 1; ++ /* configure VDINT0 and VDINT1 */ ++ mid_img = vert_start + (image_win->height / 2); ++ regw(vert_start, VDINT0); ++ regw(mid_img, VDINT1); ++ } ++ regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); ++ regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); ++ regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); ++ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); ++} ++ ++static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) ++{ ++ if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || ++ ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { ++ dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n"); ++ return -EINVAL; ++ } ++ ++ if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || ++ ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { ++ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n"); ++ return -EINVAL; ++ } ++ ++ if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || ++ ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { ++ dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n"); ++ return -EINVAL; ++ } ++ ++ if ((ccdcparam->med_filt_thres < 0) || ++ (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { ++ dev_dbg(ccdc_cfg.dev, ++ "Invalid value of median filter threshold\n"); ++ return -EINVAL; ++ } ++ ++ if (ccdcparam->data_sz < CCDC_DATA_16BITS || ++ ccdcparam->data_sz > CCDC_DATA_8BITS) { ++ dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n"); ++ return -EINVAL; ++ } ++ ++ if (ccdcparam->alaw.enable) { ++ if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || ++ ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { ++ dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (ccdcparam->blk_clamp.b_clamp_enable) { ++ if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || ++ ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { ++ dev_dbg(ccdc_cfg.dev, ++ "Invalid value of sample pixel\n"); ++ return -EINVAL; ++ } ++ if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || ++ ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { ++ dev_dbg(ccdc_cfg.dev, ++ "Invalid value of sample lines\n"); ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++ ++/* Parameter operations */ ++static int ccdc_set_params(void __user *params) ++{ ++ struct ccdc_config_params_raw ccdc_raw_params; ++ int x; ++ ++ /* only raw module parameters can be set through the IOCTL */ ++ if (ccdc_cfg.if_type != VPFE_RAW_BAYER) ++ return -EINVAL; ++ ++ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); ++ if (x) { ++ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc" ++ "params, %d\n", x); ++ return -EFAULT; ++ } ++ ++ if (!validate_ccdc_param(&ccdc_raw_params)) { ++ memcpy(&ccdc_cfg.bayer.config_params, ++ &ccdc_raw_params, ++ sizeof(ccdc_raw_params)); ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* This function will configure CCDC for YCbCr video capture */ ++static void ccdc_config_ycbcr(void) ++{ ++ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; ++ u32 temp; ++ ++ /* first set the CCDC power on defaults values in all registers */ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); ++ ccdc_restore_defaults(); ++ ++ /* configure pixel format & video frame format */ ++ temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << ++ CCDC_INPUT_MODE_SHIFT) | ++ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << ++ CCDC_FRM_FMT_SHIFT)); ++ ++ /* setup BT.656 sync mode */ ++ if (params->bt656_enable) { ++ regw(CCDC_REC656IF_BT656_EN, REC656IF); ++ /* ++ * configure the FID, VD, HD pin polarity fld,hd pol positive, ++ * vd negative, 8-bit pack mode ++ */ ++ temp |= CCDC_VD_POL_NEGATIVE; ++ } else { /* y/c external sync mode */ ++ temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << ++ CCDC_FID_POL_SHIFT) | ++ ((params->hd_pol & CCDC_HD_POL_MASK) << ++ CCDC_HD_POL_SHIFT) | ++ ((params->vd_pol & CCDC_VD_POL_MASK) << ++ CCDC_VD_POL_SHIFT)); ++ } ++ ++ /* pack the data to 8-bit */ ++ temp |= CCDC_DATA_PACK_ENABLE; ++ ++ regw(temp, MODESET); ++ ++ /* configure video window */ ++ ccdc_setwin(¶ms->win, params->frm_fmt, 2); ++ ++ /* configure the order of y cb cr in SD-RAM */ ++ temp = (params->pix_order << CCDC_Y8POS_SHIFT); ++ temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; ++ regw(temp, CCDCFG); ++ ++ /* ++ * configure the horizontal line offset. This is done by rounding up ++ * width to a multiple of 16 pixels and multiply by two to account for ++ * y:cb:cr 4:2:2 data ++ */ ++ regw(((params->win.width * 2 + 31) >> 5), HSIZE); ++ ++ /* configure the memory line offset */ ++ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { ++ /* two fields are interleaved in memory */ ++ regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); ++ } ++ ++ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); ++} ++ ++/* ++ * ccdc_config_black_clamp() ++ * configure parameters for Optical Black Clamp ++ */ ++static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) ++{ ++ u32 val; ++ ++ if (!bclamp->b_clamp_enable) { ++ /* configure DCSub */ ++ regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); ++ regw(0x0000, CLAMP); ++ return; ++ } ++ /* Enable the Black clamping, set sample lines and pixels */ ++ val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | ++ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << ++ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; ++ regw(val, CLAMP); ++ ++ /* If Black clamping is enable then make dcsub 0 */ ++ val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) ++ << CCDC_NUM_LINE_CALC_SHIFT; ++ regw(val, DCSUB); ++} ++ ++/* ++ * ccdc_config_black_compense() ++ * configure parameters for Black Compensation ++ */ ++static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) ++{ ++ u32 val; ++ ++ val = (bcomp->b & CCDC_BLK_COMP_MASK) | ++ ((bcomp->gb & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_GB_COMP_SHIFT); ++ regw(val, BLKCMP1); ++ ++ val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_GR_COMP_SHIFT) | ++ ((bcomp->r & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_R_COMP_SHIFT); ++ regw(val, BLKCMP0); ++} ++ ++/* ++ * ccdc_write_dfc_entry() ++ * write an entry in the dfc table. ++ */ ++int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) ++{ ++/* TODO This is to be re-visited and adjusted */ ++#define DFC_WRITE_WAIT_COUNT 1000 ++ u32 val, count = DFC_WRITE_WAIT_COUNT; ++ ++ regw(dfc->dft_corr_vert[index], DFCMEM0); ++ regw(dfc->dft_corr_horz[index], DFCMEM1); ++ regw(dfc->dft_corr_sub1[index], DFCMEM2); ++ regw(dfc->dft_corr_sub2[index], DFCMEM3); ++ regw(dfc->dft_corr_sub3[index], DFCMEM4); ++ /* set WR bit to write */ ++ val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; ++ regw(val, DFCMEMCTL); ++ ++ /* ++ * Assume, it is very short. If we get an error, we need to ++ * adjust this value ++ */ ++ while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) ++ count--; ++ /* ++ * TODO We expect the count to be non-zero to be successful. Adjust ++ * the count if write requires more time ++ */ ++ ++ if (count) { ++ dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * ccdc_config_vdfc() ++ * configure parameters for Vertical Defect Correction ++ */ ++static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) ++{ ++ u32 val; ++ int i; ++ ++ /* Configure General Defect Correction. The table used is from IPIPE */ ++ val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; ++ ++ /* Configure Vertical Defect Correction if needed */ ++ if (!dfc->ver_dft_en) { ++ /* Enable only General Defect Correction */ ++ regw(val, DFCCTL); ++ return 0; ++ } ++ ++ if (dfc->table_size > CCDC_DFT_TABLE_SIZE) ++ return -EINVAL; ++ ++ val |= CCDC_DFCCTL_VDFC_DISABLE; ++ val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << ++ CCDC_DFCCTL_VDFCSL_SHIFT; ++ val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << ++ CCDC_DFCCTL_VDFCUDA_SHIFT; ++ val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << ++ CCDC_DFCCTL_VDFLSFT_SHIFT; ++ regw(val , DFCCTL); ++ ++ /* clear address ptr to offset 0 */ ++ val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; ++ ++ /* write defect table entries */ ++ for (i = 0; i < dfc->table_size; i++) { ++ /* increment address for non zero index */ ++ if (i != 0) ++ val = CCDC_DFCMEMCTL_INC_ADDR; ++ regw(val, DFCMEMCTL); ++ if (ccdc_write_dfc_entry(i, dfc) < 0) ++ return -EFAULT; ++ } ++ ++ /* update saturation level and enable dfc */ ++ regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); ++ val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << ++ CCDC_DFCCTL_VDFCEN_SHIFT); ++ regw(val, DFCCTL); ++ return 0; ++} ++ ++/* ++ * ccdc_config_csc() ++ * configure parameters for color space conversion ++ * Each register CSCM0-7 has two values in S8Q5 format. ++ */ ++static void ccdc_config_csc(struct ccdc_csc *csc) ++{ ++ u32 val1, val2; ++ int i; ++ ++ if (!csc->enable) ++ return; ++ ++ /* Enable the CSC sub-module */ ++ regw(CCDC_CSC_ENABLE, CSCCTL); ++ ++ /* Converting the co-eff as per the format of the register */ ++ for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { ++ if ((i % 2) == 0) { ++ /* CSCM - LSB */ ++ val1 = (csc->coeff[i].integer & ++ CCDC_CSC_COEF_INTEG_MASK) ++ << CCDC_CSC_COEF_INTEG_SHIFT; ++ /* ++ * convert decimal part to binary. Use 2 decimal ++ * precision, user values range from .00 - 0.99 ++ */ ++ val1 |= (((csc->coeff[i].decimal & ++ CCDC_CSC_COEF_DECIMAL_MASK) * ++ CCDC_CSC_DEC_MAX) / 100); ++ } else { ++ ++ /* CSCM - MSB */ ++ val2 = (csc->coeff[i].integer & ++ CCDC_CSC_COEF_INTEG_MASK) ++ << CCDC_CSC_COEF_INTEG_SHIFT; ++ val2 |= (((csc->coeff[i].decimal & ++ CCDC_CSC_COEF_DECIMAL_MASK) * ++ CCDC_CSC_DEC_MAX) / 100); ++ val2 <<= CCDC_CSCM_MSB_SHIFT; ++ val2 |= val1; ++ regw(val2, (CSCM0 + ((i - 1) << 1))); ++ } ++ } ++} ++ ++/* ++ * ccdc_config_color_patterns() ++ * configure parameters for color patterns ++ */ ++static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, ++ struct ccdc_col_pat *pat1) ++{ ++ u32 val; ++ ++ val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | ++ (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | ++ (pat1->elop << 12) | (pat1->elep << 14)); ++ regw(val, COLPTN); ++} ++ ++/* This function will configure CCDC for Raw mode image capture */ ++static int ccdc_config_raw(void) ++{ ++ struct ccdc_params_raw *params = &ccdc_cfg.bayer; ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int val; ++ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); ++ ++ /* restore power on defaults to register */ ++ ccdc_restore_defaults(); ++ ++ /* CCDCFG register: ++ * set CCD Not to swap input since input is RAW data ++ * set FID detection function to Latch at V-Sync ++ * set WENLOG - ccdc valid area to AND ++ * set TRGSEL to WENBIT ++ * set EXTRG to DISABLE ++ * disable latching function on VSYNC - shadowed registers ++ */ ++ regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | ++ CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | ++ CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); ++ ++ /* ++ * Set VDHD direction to input, input type to raw input ++ * normal data polarity, do not use external WEN ++ */ ++ val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | ++ CCDC_EXWEN_DISABLE); ++ ++ /* ++ * Configure the vertical sync polarity (MODESET.VDPOL), horizontal ++ * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), ++ * frame format(progressive or interlace), & pixel format (Input mode) ++ */ ++ val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | ++ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | ++ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | ++ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | ++ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); ++ ++ /* set pack for alaw compression */ ++ if ((config_params->data_sz == CCDC_DATA_8BITS) || ++ config_params->alaw.enable) ++ val |= CCDC_DATA_PACK_ENABLE; ++ ++ /* Configure for LPF */ ++ if (config_params->lpf_enable) ++ val |= (config_params->lpf_enable & CCDC_LPF_MASK) << ++ CCDC_LPF_SHIFT; ++ ++ /* Configure the data shift */ ++ val |= (config_params->datasft & CCDC_DATASFT_MASK) << ++ CCDC_DATASFT_SHIFT; ++ regw(val , MODESET); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); ++ ++ /* Configure the Median Filter threshold */ ++ regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); ++ ++ /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ ++ val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | ++ CCDC_CFA_MOSAIC; ++ ++ /* Enable and configure aLaw register if needed */ ++ if (config_params->alaw.enable) { ++ val |= (CCDC_ALAW_ENABLE | ++ ((config_params->alaw.gama_wd & ++ CCDC_ALAW_GAMA_WD_MASK) << ++ CCDC_GAMMAWD_INPUT_SHIFT)); ++ } ++ ++ /* Configure Median filter1 & filter2 */ ++ val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | ++ (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); ++ ++ regw(val, GAMMAWD); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); ++ ++ /* configure video window */ ++ ccdc_setwin(¶ms->win, params->frm_fmt, 1); ++ ++ /* Optical Clamp Averaging */ ++ ccdc_config_black_clamp(&config_params->blk_clamp); ++ ++ /* Black level compensation */ ++ ccdc_config_black_compense(&config_params->blk_comp); ++ ++ /* Vertical Defect Correction if needed */ ++ if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) ++ return -EFAULT; ++ ++ /* color space conversion */ ++ ccdc_config_csc(&config_params->csc); ++ ++ /* color pattern */ ++ ccdc_config_color_patterns(&config_params->col_pat_field0, ++ &config_params->col_pat_field1); ++ ++ /* Configure the Gain & offset control */ ++ ccdc_config_gain_offset(); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); ++ ++ /* Configure DATAOFST register */ ++ val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << ++ CCDC_DATAOFST_H_SHIFT; ++ val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << ++ CCDC_DATAOFST_V_SHIFT; ++ regw(val, DATAOFST); ++ ++ /* configuring HSIZE register */ ++ val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << ++ CCDC_HSIZE_FLIP_SHIFT; ++ ++ /* If pack 8 is enable then 1 pixel will take 1 byte */ ++ if ((config_params->data_sz == CCDC_DATA_8BITS) || ++ config_params->alaw.enable) { ++ val |= (((params->win.width) + 31) >> 5) & ++ CCDC_HSIZE_VAL_MASK; ++ ++ /* adjust to multiple of 32 */ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", ++ (((params->win.width) + 31) >> 5) & ++ CCDC_HSIZE_VAL_MASK); ++ } else { ++ /* else one pixel will take 2 byte */ ++ val |= (((params->win.width * 2) + 31) >> 5) & ++ CCDC_HSIZE_VAL_MASK; ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", ++ (((params->win.width * 2) + 31) >> 5) & ++ CCDC_HSIZE_VAL_MASK); ++ } ++ regw(val, HSIZE); ++ ++ /* Configure SDOFST register */ ++ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ if (params->image_invert_enable) { ++ /* For interlace inverse mode */ ++ regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", ++ CCDC_SDOFST_INTERLACE_INVERSE); ++ } else { ++ /* For interlace non inverse mode */ ++ regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", ++ CCDC_SDOFST_INTERLACE_NORMAL); ++ } ++ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { ++ if (params->image_invert_enable) { ++ /* For progessive inverse mode */ ++ regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", ++ CCDC_SDOFST_PROGRESSIVE_INVERSE); ++ } else { ++ /* For progessive non inverse mode */ ++ regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", ++ CCDC_SDOFST_PROGRESSIVE_NORMAL); ++ } ++ } ++ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); ++ return 0; ++} ++ ++static int ccdc_configure(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ return ccdc_config_raw(); ++ else ++ ccdc_config_ycbcr(); ++ return 0; ++} ++ ++static int ccdc_set_buftype(enum ccdc_buftype buf_type) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.buf_type = buf_type; ++ else ++ ccdc_cfg.ycbcr.buf_type = buf_type; ++ return 0; ++} ++static enum ccdc_buftype ccdc_get_buftype(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ return ccdc_cfg.bayer.buf_type; ++ return ccdc_cfg.ycbcr.buf_type; ++} ++ ++static int ccdc_enum_pix(u32 *pix, int i) ++{ ++ int ret = -EINVAL; ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { ++ *pix = ccdc_raw_bayer_pix_formats[i]; ++ ret = 0; ++ } ++ } else { ++ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { ++ *pix = ccdc_raw_yuv_pix_formats[i]; ++ ret = 0; ++ } ++ } ++ return ret; ++} ++ ++static int ccdc_set_pixel_format(u32 pixfmt) ++{ ++ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; ++ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; ++ if (pixfmt == V4L2_PIX_FMT_SBGGR8) ++ alaw->enable = 1; ++ else if (pixfmt != V4L2_PIX_FMT_SBGGR16) ++ return -EINVAL; ++ } else { ++ if (pixfmt == V4L2_PIX_FMT_YUYV) ++ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; ++ else if (pixfmt == V4L2_PIX_FMT_UYVY) ++ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; ++ else ++ return -EINVAL; ++ } ++ return 0; ++} ++static u32 ccdc_get_pixel_format(void) ++{ ++ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; ++ u32 pixfmt; ++ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ if (alaw->enable) ++ pixfmt = V4L2_PIX_FMT_SBGGR8; ++ else ++ pixfmt = V4L2_PIX_FMT_SBGGR16; ++ else { ++ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) ++ pixfmt = V4L2_PIX_FMT_YUYV; ++ else ++ pixfmt = V4L2_PIX_FMT_UYVY; ++ } ++ return pixfmt; ++} ++static int ccdc_set_image_window(struct v4l2_rect *win) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.win = *win; ++ else ++ ccdc_cfg.ycbcr.win = *win; ++ return 0; ++} ++ ++static void ccdc_get_image_window(struct v4l2_rect *win) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ *win = ccdc_cfg.bayer.win; ++ else ++ *win = ccdc_cfg.ycbcr.win; ++} ++ ++static unsigned int ccdc_get_line_length(void) ++{ ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int len; ++ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ if ((config_params->alaw.enable) || ++ (config_params->data_sz == CCDC_DATA_8BITS)) ++ len = ccdc_cfg.bayer.win.width; ++ else ++ len = ccdc_cfg.bayer.win.width * 2; ++ } else ++ len = ccdc_cfg.ycbcr.win.width * 2; ++ return ALIGN(len, 32); ++} ++ ++static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.frm_fmt = frm_fmt; ++ else ++ ccdc_cfg.ycbcr.frm_fmt = frm_fmt; ++ return 0; ++} ++ ++static enum ccdc_frmfmt ccdc_get_frame_format(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ return ccdc_cfg.bayer.frm_fmt; ++ else ++ return ccdc_cfg.ycbcr.frm_fmt; ++} ++ ++static int ccdc_getfid(void) ++{ ++ return (regr(MODESET) >> 15) & 1; ++} ++ ++/* misc operations */ ++static inline void ccdc_setfbaddr(unsigned long addr) ++{ ++ regw((addr >> 21) & 0x007f, STADRH); ++ regw((addr >> 5) & 0x0ffff, STADRL); ++} ++ ++static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) ++{ ++ ccdc_cfg.if_type = params->if_type; ++ ++ switch (params->if_type) { ++ case VPFE_BT656: ++ case VPFE_YCBCR_SYNC_16: ++ case VPFE_YCBCR_SYNC_8: ++ ccdc_cfg.ycbcr.vd_pol = params->vdpol; ++ ccdc_cfg.ycbcr.hd_pol = params->hdpol; ++ break; ++ default: ++ /* TODO add support for raw bayer here */ ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static struct ccdc_hw_device ccdc_hw_dev = { ++ .name = "DM355 CCDC", ++ .owner = THIS_MODULE, ++ .hw_ops = { ++ .open = ccdc_open, ++ .close = ccdc_close, ++ .enable = ccdc_enable, ++ .enable_out_to_sdram = ccdc_enable_output_to_sdram, ++ .set_hw_if_params = ccdc_set_hw_if_params, ++ .set_params = ccdc_set_params, ++ .configure = ccdc_configure, ++ .set_buftype = ccdc_set_buftype, ++ .get_buftype = ccdc_get_buftype, ++ .enum_pix = ccdc_enum_pix, ++ .set_pixel_format = ccdc_set_pixel_format, ++ .get_pixel_format = ccdc_get_pixel_format, ++ .set_frame_format = ccdc_set_frame_format, ++ .get_frame_format = ccdc_get_frame_format, ++ .set_image_window = ccdc_set_image_window, ++ .get_image_window = ccdc_get_image_window, ++ .get_line_length = ccdc_get_line_length, ++ .setfbaddr = ccdc_setfbaddr, ++ .getfid = ccdc_getfid, ++ }, ++}; ++ ++static int __devinit dm355_ccdc_probe(struct platform_device *pdev) ++{ ++ void (*setup_pinmux)(void); ++ struct resource *res; ++ int status = 0; ++ ++ /* ++ * first try to register with vpfe. If not correct platform, then we ++ * don't have to iomap ++ */ ++ status = vpfe_register_ccdc_device(&ccdc_hw_dev); ++ if (status < 0) ++ return status; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ status = -ENODEV; ++ goto fail_nores; ++ } ++ ++ res = request_mem_region(res->start, resource_size(res), res->name); ++ if (!res) { ++ status = -EBUSY; ++ goto fail_nores; ++ } ++ ++ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res)); ++ if (!ccdc_cfg.base_addr) { ++ status = -ENOMEM; ++ goto fail_nomem; ++ } ++ ++ /* Get and enable Master clock */ ++ ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); ++ if (IS_ERR(ccdc_cfg.mclk)) { ++ status = PTR_ERR(ccdc_cfg.mclk); ++ goto fail_nomap; ++ } ++ if (clk_prepare_enable(ccdc_cfg.mclk)) { ++ status = -ENODEV; ++ goto fail_mclk; ++ } ++ ++ /* Get and enable Slave clock */ ++ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); ++ if (IS_ERR(ccdc_cfg.sclk)) { ++ status = PTR_ERR(ccdc_cfg.sclk); ++ goto fail_mclk; ++ } ++ if (clk_prepare_enable(ccdc_cfg.sclk)) { ++ status = -ENODEV; ++ goto fail_sclk; ++ } ++ ++ /* Platform data holds setup_pinmux function ptr */ ++ if (NULL == pdev->dev.platform_data) { ++ status = -ENODEV; ++ goto fail_sclk; ++ } ++ setup_pinmux = pdev->dev.platform_data; ++ /* ++ * setup Mux configuration for ccdc which may be different for ++ * different SoCs using this CCDC ++ */ ++ setup_pinmux(); ++ ccdc_cfg.dev = &pdev->dev; ++ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); ++ return 0; ++fail_sclk: ++ clk_disable_unprepare(ccdc_cfg.sclk); ++ clk_put(ccdc_cfg.sclk); ++fail_mclk: ++ clk_disable_unprepare(ccdc_cfg.mclk); ++ clk_put(ccdc_cfg.mclk); ++fail_nomap: ++ iounmap(ccdc_cfg.base_addr); ++fail_nomem: ++ release_mem_region(res->start, resource_size(res)); ++fail_nores: ++ vpfe_unregister_ccdc_device(&ccdc_hw_dev); ++ return status; ++} ++ ++static int dm355_ccdc_remove(struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ clk_disable_unprepare(ccdc_cfg.sclk); ++ clk_disable_unprepare(ccdc_cfg.mclk); ++ clk_put(ccdc_cfg.mclk); ++ clk_put(ccdc_cfg.sclk); ++ iounmap(ccdc_cfg.base_addr); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) ++ release_mem_region(res->start, resource_size(res)); ++ vpfe_unregister_ccdc_device(&ccdc_hw_dev); ++ return 0; ++} ++ ++static struct platform_driver dm355_ccdc_driver = { ++ .driver = { ++ .name = "dm355_ccdc", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __devexit_p(dm355_ccdc_remove), ++ .probe = dm355_ccdc_probe, ++}; ++ ++module_platform_driver(dm355_ccdc_driver); +diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/davinci/dm355_ccdc_regs.h +new file mode 100644 +index 0000000..d6d2ef0 +--- /dev/null ++++ b/drivers/media/platform/davinci/dm355_ccdc_regs.h +@@ -0,0 +1,310 @@ ++/* ++ * Copyright (C) 2005-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _DM355_CCDC_REGS_H ++#define _DM355_CCDC_REGS_H ++ ++/**************************************************************************\ ++* Register OFFSET Definitions ++\**************************************************************************/ ++#define SYNCEN 0x00 ++#define MODESET 0x04 ++#define HDWIDTH 0x08 ++#define VDWIDTH 0x0c ++#define PPLN 0x10 ++#define LPFR 0x14 ++#define SPH 0x18 ++#define NPH 0x1c ++#define SLV0 0x20 ++#define SLV1 0x24 ++#define NLV 0x28 ++#define CULH 0x2c ++#define CULV 0x30 ++#define HSIZE 0x34 ++#define SDOFST 0x38 ++#define STADRH 0x3c ++#define STADRL 0x40 ++#define CLAMP 0x44 ++#define DCSUB 0x48 ++#define COLPTN 0x4c ++#define BLKCMP0 0x50 ++#define BLKCMP1 0x54 ++#define MEDFILT 0x58 ++#define RYEGAIN 0x5c ++#define GRCYGAIN 0x60 ++#define GBGGAIN 0x64 ++#define BMGGAIN 0x68 ++#define OFFSET 0x6c ++#define OUTCLIP 0x70 ++#define VDINT0 0x74 ++#define VDINT1 0x78 ++#define RSV0 0x7c ++#define GAMMAWD 0x80 ++#define REC656IF 0x84 ++#define CCDCFG 0x88 ++#define FMTCFG 0x8c ++#define FMTPLEN 0x90 ++#define FMTSPH 0x94 ++#define FMTLNH 0x98 ++#define FMTSLV 0x9c ++#define FMTLNV 0xa0 ++#define FMTRLEN 0xa4 ++#define FMTHCNT 0xa8 ++#define FMT_ADDR_PTR_B 0xac ++#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) ++#define FMTPGM_VF0 0xcc ++#define FMTPGM_VF1 0xd0 ++#define FMTPGM_AP0 0xd4 ++#define FMTPGM_AP1 0xd8 ++#define FMTPGM_AP2 0xdc ++#define FMTPGM_AP3 0xe0 ++#define FMTPGM_AP4 0xe4 ++#define FMTPGM_AP5 0xe8 ++#define FMTPGM_AP6 0xec ++#define FMTPGM_AP7 0xf0 ++#define LSCCFG1 0xf4 ++#define LSCCFG2 0xf8 ++#define LSCH0 0xfc ++#define LSCV0 0x100 ++#define LSCKH 0x104 ++#define LSCKV 0x108 ++#define LSCMEMCTL 0x10c ++#define LSCMEMD 0x110 ++#define LSCMEMQ 0x114 ++#define DFCCTL 0x118 ++#define DFCVSAT 0x11c ++#define DFCMEMCTL 0x120 ++#define DFCMEM0 0x124 ++#define DFCMEM1 0x128 ++#define DFCMEM2 0x12c ++#define DFCMEM3 0x130 ++#define DFCMEM4 0x134 ++#define CSCCTL 0x138 ++#define CSCM0 0x13c ++#define CSCM1 0x140 ++#define CSCM2 0x144 ++#define CSCM3 0x148 ++#define CSCM4 0x14c ++#define CSCM5 0x150 ++#define CSCM6 0x154 ++#define CSCM7 0x158 ++#define DATAOFST 0x15c ++#define CCDC_REG_LAST DATAOFST ++/************************************************************** ++* Define for various register bit mask and shifts for CCDC ++* ++**************************************************************/ ++#define CCDC_RAW_IP_MODE 0 ++#define CCDC_VDHDOUT_INPUT 0 ++#define CCDC_YCINSWP_RAW (0 << 4) ++#define CCDC_EXWEN_DISABLE 0 ++#define CCDC_DATAPOL_NORMAL 0 ++#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 ++#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) ++#define CCDC_CCDCFG_WENLOG_AND 0 ++#define CCDC_CCDCFG_TRGSEL_WEN 0 ++#define CCDC_CCDCFG_EXTRG_DISABLE 0 ++#define CCDC_CFA_MOSAIC 0 ++#define CCDC_Y8POS_SHIFT 11 ++ ++#define CCDC_VDC_DFCVSAT_MASK 0x3fff ++#define CCDC_DATAOFST_MASK 0x0ff ++#define CCDC_DATAOFST_H_SHIFT 0 ++#define CCDC_DATAOFST_V_SHIFT 8 ++#define CCDC_GAMMAWD_CFA_MASK 1 ++#define CCDC_GAMMAWD_CFA_SHIFT 5 ++#define CCDC_GAMMAWD_INPUT_SHIFT 2 ++#define CCDC_FID_POL_MASK 1 ++#define CCDC_FID_POL_SHIFT 4 ++#define CCDC_HD_POL_MASK 1 ++#define CCDC_HD_POL_SHIFT 3 ++#define CCDC_VD_POL_MASK 1 ++#define CCDC_VD_POL_SHIFT 2 ++#define CCDC_VD_POL_NEGATIVE (1 << 2) ++#define CCDC_FRM_FMT_MASK 1 ++#define CCDC_FRM_FMT_SHIFT 7 ++#define CCDC_DATA_SZ_MASK 7 ++#define CCDC_DATA_SZ_SHIFT 8 ++#define CCDC_VDHDOUT_MASK 1 ++#define CCDC_VDHDOUT_SHIFT 0 ++#define CCDC_EXWEN_MASK 1 ++#define CCDC_EXWEN_SHIFT 5 ++#define CCDC_INPUT_MODE_MASK 3 ++#define CCDC_INPUT_MODE_SHIFT 12 ++#define CCDC_PIX_FMT_MASK 3 ++#define CCDC_PIX_FMT_SHIFT 12 ++#define CCDC_DATAPOL_MASK 1 ++#define CCDC_DATAPOL_SHIFT 6 ++#define CCDC_WEN_ENABLE (1 << 1) ++#define CCDC_VDHDEN_ENABLE (1 << 16) ++#define CCDC_LPF_ENABLE (1 << 14) ++#define CCDC_ALAW_ENABLE 1 ++#define CCDC_ALAW_GAMA_WD_MASK 7 ++#define CCDC_REC656IF_BT656_EN 3 ++ ++#define CCDC_FMTCFG_FMTMODE_MASK 3 ++#define CCDC_FMTCFG_FMTMODE_SHIFT 1 ++#define CCDC_FMTCFG_LNUM_MASK 3 ++#define CCDC_FMTCFG_LNUM_SHIFT 4 ++#define CCDC_FMTCFG_ADDRINC_MASK 7 ++#define CCDC_FMTCFG_ADDRINC_SHIFT 8 ++ ++#define CCDC_CCDCFG_FIDMD_SHIFT 6 ++#define CCDC_CCDCFG_WENLOG_SHIFT 8 ++#define CCDC_CCDCFG_TRGSEL_SHIFT 9 ++#define CCDC_CCDCFG_EXTRG_SHIFT 10 ++#define CCDC_CCDCFG_MSBINVI_SHIFT 13 ++ ++#define CCDC_HSIZE_FLIP_SHIFT 12 ++#define CCDC_HSIZE_FLIP_MASK 1 ++#define CCDC_HSIZE_VAL_MASK 0xFFF ++#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 ++#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D ++#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D ++#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 ++#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 ++#define CCDC_START_PX_HOR_MASK 0x7FFF ++#define CCDC_NUM_PX_HOR_MASK 0x7FFF ++#define CCDC_START_VER_ONE_MASK 0x7FFF ++#define CCDC_START_VER_TWO_MASK 0x7FFF ++#define CCDC_NUM_LINES_VER 0x7FFF ++ ++#define CCDC_BLK_CLAMP_ENABLE (1 << 15) ++#define CCDC_BLK_SGAIN_MASK 0x1F ++#define CCDC_BLK_ST_PXL_MASK 0x1FFF ++#define CCDC_BLK_SAMPLE_LN_MASK 3 ++#define CCDC_BLK_SAMPLE_LN_SHIFT 13 ++ ++#define CCDC_NUM_LINE_CALC_MASK 3 ++#define CCDC_NUM_LINE_CALC_SHIFT 14 ++ ++#define CCDC_BLK_DC_SUB_MASK 0x3FFF ++#define CCDC_BLK_COMP_MASK 0xFF ++#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 ++#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 ++#define CCDC_BLK_COMP_R_COMP_SHIFT 8 ++#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) ++#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) ++#define CCDC_FPC_ENABLE (1 << 15) ++#define CCDC_FPC_FPC_NUM_MASK 0x7FFF ++#define CCDC_DATA_PACK_ENABLE (1 << 11) ++#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF ++#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF ++#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 ++#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF ++#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF ++#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 ++#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF ++#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 ++#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF ++#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 ++#define CCDC_VP_OUT_HORZ_ST_MASK 0xF ++ ++#define CCDC_CSC_COEF_INTEG_MASK 7 ++#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f ++#define CCDC_CSC_COEF_INTEG_SHIFT 5 ++#define CCDC_CSCM_MSB_SHIFT 8 ++#define CCDC_CSC_ENABLE 1 ++#define CCDC_CSC_DEC_MAX 32 ++ ++#define CCDC_MFILT1_SHIFT 10 ++#define CCDC_MFILT2_SHIFT 8 ++#define CCDC_MED_FILT_THRESH 0x3FFF ++#define CCDC_LPF_MASK 1 ++#define CCDC_LPF_SHIFT 14 ++#define CCDC_OFFSET_MASK 0x3FF ++#define CCDC_DATASFT_MASK 7 ++#define CCDC_DATASFT_SHIFT 8 ++ ++#define CCDC_DF_ENABLE 1 ++ ++#define CCDC_FMTPLEN_P0_MASK 0xF ++#define CCDC_FMTPLEN_P1_MASK 0xF ++#define CCDC_FMTPLEN_P2_MASK 7 ++#define CCDC_FMTPLEN_P3_MASK 7 ++#define CCDC_FMTPLEN_P0_SHIFT 0 ++#define CCDC_FMTPLEN_P1_SHIFT 4 ++#define CCDC_FMTPLEN_P2_SHIFT 8 ++#define CCDC_FMTPLEN_P3_SHIFT 12 ++ ++#define CCDC_FMTSPH_MASK 0x1FFF ++#define CCDC_FMTLNH_MASK 0x1FFF ++#define CCDC_FMTSLV_MASK 0x1FFF ++#define CCDC_FMTLNV_MASK 0x7FFF ++#define CCDC_FMTRLEN_MASK 0x1FFF ++#define CCDC_FMTHCNT_MASK 0x1FFF ++ ++#define CCDC_ADP_INIT_MASK 0x1FFF ++#define CCDC_ADP_LINE_SHIFT 13 ++#define CCDC_ADP_LINE_MASK 3 ++#define CCDC_FMTPGN_APTR_MASK 7 ++ ++#define CCDC_DFCCTL_GDFCEN_MASK 1 ++#define CCDC_DFCCTL_VDFCEN_MASK 1 ++#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) ++#define CCDC_DFCCTL_VDFCEN_SHIFT 4 ++#define CCDC_DFCCTL_VDFCSL_MASK 3 ++#define CCDC_DFCCTL_VDFCSL_SHIFT 5 ++#define CCDC_DFCCTL_VDFCUDA_MASK 1 ++#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 ++#define CCDC_DFCCTL_VDFLSFT_MASK 3 ++#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 ++#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 ++#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 ++#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 ++#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 ++#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) ++ ++#define CCDC_LSCCFG_GFTSF_MASK 7 ++#define CCDC_LSCCFG_GFTSF_SHIFT 1 ++#define CCDC_LSCCFG_GFTINV_MASK 0xf ++#define CCDC_LSCCFG_GFTINV_SHIFT 4 ++#define CCDC_LSC_GFTABLE_SEL_MASK 3 ++#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 ++#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 ++#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 ++#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 ++#define CCDC_LSC_GFMODE_MASK 3 ++#define CCDC_LSC_GFMODE_SHIFT 4 ++#define CCDC_LSC_DISABLE 0 ++#define CCDC_LSC_ENABLE 1 ++#define CCDC_LSC_TABLE1_SLC 0 ++#define CCDC_LSC_TABLE2_SLC 1 ++#define CCDC_LSC_TABLE3_SLC 2 ++#define CCDC_LSC_MEMADDR_RESET (1 << 2) ++#define CCDC_LSC_MEMADDR_INCR (0 << 2) ++#define CCDC_LSC_FRAC_MASK_T1 0xFF ++#define CCDC_LSC_INT_MASK 3 ++#define CCDC_LSC_FRAC_MASK 0x3FFF ++#define CCDC_LSC_CENTRE_MASK 0x3FFF ++#define CCDC_LSC_COEF_MASK 0xff ++#define CCDC_LSC_COEFL_SHIFT 0 ++#define CCDC_LSC_COEFU_SHIFT 8 ++#define CCDC_GAIN_MASK 0x7FF ++#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) ++#define CCDC_SYNCEN_WEN_MASK (1 << 1) ++#define CCDC_SYNCEN_WEN_SHIFT 1 ++ ++/* Power on Defaults in hardware */ ++#define MODESET_DEFAULT 0x200 ++#define CULH_DEFAULT 0xFFFF ++#define CULV_DEFAULT 0xFF ++#define GAIN_DEFAULT 256 ++#define OUTCLIP_DEFAULT 0x3FFF ++#define LSCCFG2_DEFAULT 0xE ++ ++#endif +diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c +new file mode 100644 +index 0000000..0215ab6 +--- /dev/null ++++ b/drivers/media/platform/davinci/dm644x_ccdc.c +@@ -0,0 +1,1085 @@ ++/* ++ * Copyright (C) 2006-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * CCDC hardware module for DM6446 ++ * ------------------------------ ++ * ++ * This module is for configuring CCD controller of DM6446 VPFE to capture ++ * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules ++ * such as Defect Pixel Correction, Color Space Conversion etc to ++ * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This ++ * module also allows application to configure individual ++ * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. ++ * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header ++ * files. The setparams() API is called by vpfe_capture driver ++ * to configure module parameters. This file is named DM644x so that other ++ * variants such DM6443 may be supported using the same module. ++ * ++ * TODO: Test Raw bayer parameter settings and bayer capture ++ * Split module parameter structure to module specific ioctl structs ++ * investigate if enum used for user space type definition ++ * to be replaced by #defines or integer ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "dm644x_ccdc_regs.h" ++#include "ccdc_hw_device.h" ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("CCDC Driver for DM6446"); ++MODULE_AUTHOR("Texas Instruments"); ++ ++static struct ccdc_oper_config { ++ struct device *dev; ++ /* CCDC interface type */ ++ enum vpfe_hw_if_type if_type; ++ /* Raw Bayer configuration */ ++ struct ccdc_params_raw bayer; ++ /* YCbCr configuration */ ++ struct ccdc_params_ycbcr ycbcr; ++ /* Master clock */ ++ struct clk *mclk; ++ /* slave clock */ ++ struct clk *sclk; ++ /* ccdc base address */ ++ void __iomem *base_addr; ++} ccdc_cfg = { ++ /* Raw configurations */ ++ .bayer = { ++ .pix_fmt = CCDC_PIXFMT_RAW, ++ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, ++ .win = CCDC_WIN_VGA, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .config_params = { ++ .data_sz = CCDC_DATA_10BITS, ++ }, ++ }, ++ .ycbcr = { ++ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, ++ .frm_fmt = CCDC_FRMFMT_INTERLACED, ++ .win = CCDC_WIN_PAL, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .bt656_enable = 1, ++ .pix_order = CCDC_PIXORDER_CBYCRY, ++ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED ++ }, ++}; ++ ++#define CCDC_MAX_RAW_YUV_FORMATS 2 ++ ++/* Raw Bayer formats */ ++static u32 ccdc_raw_bayer_pix_formats[] = ++ {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; ++ ++/* Raw YUV formats */ ++static u32 ccdc_raw_yuv_pix_formats[] = ++ {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; ++ ++/* CCDC Save/Restore context */ ++static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)]; ++ ++/* register access routines */ ++static inline u32 regr(u32 offset) ++{ ++ return __raw_readl(ccdc_cfg.base_addr + offset); ++} ++ ++static inline void regw(u32 val, u32 offset) ++{ ++ __raw_writel(val, ccdc_cfg.base_addr + offset); ++} ++ ++static void ccdc_enable(int flag) ++{ ++ regw(flag, CCDC_PCR); ++} ++ ++static void ccdc_enable_vport(int flag) ++{ ++ if (flag) ++ /* enable video port */ ++ regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); ++ else ++ regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); ++} ++ ++/* ++ * ccdc_setwin() ++ * This function will configure the window size ++ * to be capture in CCDC reg ++ */ ++void ccdc_setwin(struct v4l2_rect *image_win, ++ enum ccdc_frmfmt frm_fmt, ++ int ppc) ++{ ++ int horz_start, horz_nr_pixels; ++ int vert_start, vert_nr_lines; ++ int val = 0, mid_img = 0; ++ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); ++ /* ++ * ppc - per pixel count. indicates how many pixels per cell ++ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. ++ * raw capture this is 1 ++ */ ++ horz_start = image_win->left << (ppc - 1); ++ horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; ++ regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, ++ CCDC_HORZ_INFO); ++ ++ vert_start = image_win->top; ++ ++ if (frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ vert_nr_lines = (image_win->height >> 1) - 1; ++ vert_start >>= 1; ++ /* Since first line doesn't have any data */ ++ vert_start += 1; ++ /* configure VDINT0 */ ++ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); ++ regw(val, CCDC_VDINT); ++ ++ } else { ++ /* Since first line doesn't have any data */ ++ vert_start += 1; ++ vert_nr_lines = image_win->height - 1; ++ /* ++ * configure VDINT0 and VDINT1. VDINT1 will be at half ++ * of image height ++ */ ++ mid_img = vert_start + (image_win->height / 2); ++ val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | ++ (mid_img & CCDC_VDINT_VDINT1_MASK); ++ regw(val, CCDC_VDINT); ++ ++ } ++ regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, ++ CCDC_VERT_START); ++ regw(vert_nr_lines, CCDC_VERT_LINES); ++ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); ++} ++ ++static void ccdc_readregs(void) ++{ ++ unsigned int val = 0; ++ ++ val = regr(CCDC_ALAW); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val); ++ val = regr(CCDC_CLAMP); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val); ++ val = regr(CCDC_DCSUB); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val); ++ val = regr(CCDC_BLKCMP); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val); ++ val = regr(CCDC_FPC_ADDR); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val); ++ val = regr(CCDC_FPC); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val); ++ val = regr(CCDC_FMTCFG); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val); ++ val = regr(CCDC_COLPTN); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val); ++ val = regr(CCDC_FMT_HORZ); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val); ++ val = regr(CCDC_FMT_VERT); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val); ++ val = regr(CCDC_HSIZE_OFF); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val); ++ val = regr(CCDC_SDOFST); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val); ++ val = regr(CCDC_VP_OUT); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val); ++ val = regr(CCDC_SYN_MODE); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val); ++ val = regr(CCDC_HORZ_INFO); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val); ++ val = regr(CCDC_VERT_START); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val); ++ val = regr(CCDC_VERT_LINES); ++ dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); ++} ++ ++static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) ++{ ++ if (ccdcparam->alaw.enable) { ++ if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || ++ (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || ++ (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { ++ dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) ++{ ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int *fpc_virtaddr = NULL; ++ unsigned int *fpc_physaddr = NULL; ++ ++ memcpy(config_params, raw_params, sizeof(*raw_params)); ++ /* ++ * allocate memory for fault pixel table and copy the user ++ * values to the table ++ */ ++ if (!config_params->fault_pxl.enable) ++ return 0; ++ ++ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; ++ fpc_virtaddr = (unsigned int *)phys_to_virt( ++ (unsigned long)fpc_physaddr); ++ /* ++ * Allocate memory for FPC table if current ++ * FPC table buffer is not big enough to ++ * accommodate FPC Number requested ++ */ ++ if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { ++ if (fpc_physaddr != NULL) { ++ free_pages((unsigned long)fpc_physaddr, ++ get_order ++ (config_params->fault_pxl.fp_num * ++ FP_NUM_BYTES)); ++ } ++ ++ /* Allocate memory for FPC table */ ++ fpc_virtaddr = ++ (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, ++ get_order(raw_params-> ++ fault_pxl.fp_num * ++ FP_NUM_BYTES)); ++ ++ if (fpc_virtaddr == NULL) { ++ dev_dbg(ccdc_cfg.dev, ++ "\nUnable to allocate memory for FPC"); ++ return -EFAULT; ++ } ++ fpc_physaddr = ++ (unsigned int *)virt_to_phys((void *)fpc_virtaddr); ++ } ++ ++ /* Copy number of fault pixels and FPC table */ ++ config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; ++ if (copy_from_user(fpc_virtaddr, ++ (void __user *)raw_params->fault_pxl.fpc_table_addr, ++ config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { ++ dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed"); ++ return -EFAULT; ++ } ++ config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; ++ return 0; ++} ++ ++static int ccdc_close(struct device *dev) ++{ ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; ++ ++ fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; ++ ++ if (fpc_physaddr != NULL) { ++ fpc_virtaddr = (unsigned int *) ++ phys_to_virt((unsigned long)fpc_physaddr); ++ free_pages((unsigned long)fpc_virtaddr, ++ get_order(config_params->fault_pxl.fp_num * ++ FP_NUM_BYTES)); ++ } ++ return 0; ++} ++ ++/* ++ * ccdc_restore_defaults() ++ * This function will write defaults to all CCDC registers ++ */ ++static void ccdc_restore_defaults(void) ++{ ++ int i; ++ ++ /* disable CCDC */ ++ ccdc_enable(0); ++ /* set all registers to default value */ ++ for (i = 4; i <= 0x94; i += 4) ++ regw(0, i); ++ regw(CCDC_NO_CULLING, CCDC_CULLING); ++ regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); ++} ++ ++static int ccdc_open(struct device *device) ++{ ++ ccdc_restore_defaults(); ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_enable_vport(1); ++ return 0; ++} ++ ++static void ccdc_sbl_reset(void) ++{ ++ vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); ++} ++ ++/* Parameter operations */ ++static int ccdc_set_params(void __user *params) ++{ ++ struct ccdc_config_params_raw ccdc_raw_params; ++ int x; ++ ++ if (ccdc_cfg.if_type != VPFE_RAW_BAYER) ++ return -EINVAL; ++ ++ x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); ++ if (x) { ++ dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying" ++ "ccdc params, %d\n", x); ++ return -EFAULT; ++ } ++ ++ if (!validate_ccdc_param(&ccdc_raw_params)) { ++ if (!ccdc_update_raw_params(&ccdc_raw_params)) ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * ccdc_config_ycbcr() ++ * This function will configure CCDC for YCbCr video capture ++ */ ++void ccdc_config_ycbcr(void) ++{ ++ struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; ++ u32 syn_mode; ++ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); ++ /* ++ * first restore the CCDC registers to default values ++ * This is important since we assume default values to be set in ++ * a lot of registers that we didn't touch ++ */ ++ ccdc_restore_defaults(); ++ ++ /* ++ * configure pixel format, frame format, configure video frame ++ * format, enable output to SDRAM, enable internal timing generator ++ * and 8bit pack mode ++ */ ++ syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << ++ CCDC_SYN_MODE_INPMOD_SHIFT) | ++ ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << ++ CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | ++ CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); ++ ++ /* setup BT.656 sync mode */ ++ if (params->bt656_enable) { ++ regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); ++ ++ /* ++ * configure the FID, VD, HD pin polarity, ++ * fld,hd pol positive, vd negative, 8-bit data ++ */ ++ syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE; ++ if (ccdc_cfg.if_type == VPFE_BT656_10BIT) ++ syn_mode |= CCDC_SYN_MODE_10BITS; ++ else ++ syn_mode |= CCDC_SYN_MODE_8BITS; ++ } else { ++ /* y/c external sync mode */ ++ syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << ++ CCDC_FID_POL_SHIFT) | ++ ((params->hd_pol & CCDC_HD_POL_MASK) << ++ CCDC_HD_POL_SHIFT) | ++ ((params->vd_pol & CCDC_VD_POL_MASK) << ++ CCDC_VD_POL_SHIFT)); ++ } ++ regw(syn_mode, CCDC_SYN_MODE); ++ ++ /* configure video window */ ++ ccdc_setwin(¶ms->win, params->frm_fmt, 2); ++ ++ /* ++ * configure the order of y cb cr in SDRAM, and disable latch ++ * internal register on vsync ++ */ ++ if (ccdc_cfg.if_type == VPFE_BT656_10BIT) ++ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | ++ CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT, ++ CCDC_CCDCFG); ++ else ++ regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | ++ CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); ++ ++ /* ++ * configure the horizontal line offset. This should be a ++ * on 32 byte boundary. So clear LSB 5 bits ++ */ ++ regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); ++ ++ /* configure the memory line offset */ ++ if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) ++ /* two fields are interleaved in memory */ ++ regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); ++ ++ ccdc_sbl_reset(); ++ dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); ++} ++ ++static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) ++{ ++ u32 val; ++ ++ if (!bclamp->enable) { ++ /* configure DCSub */ ++ val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; ++ regw(val, CCDC_DCSUB); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val); ++ regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n"); ++ return; ++ } ++ /* ++ * Configure gain, Start pixel, No of line to be avg, ++ * No of pixel/line to be avg, & Enable the Black clamping ++ */ ++ val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | ++ ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << ++ CCDC_BLK_ST_PXL_SHIFT) | ++ ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << ++ CCDC_BLK_SAMPLE_LINE_SHIFT) | ++ ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << ++ CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); ++ regw(val, CCDC_CLAMP); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val); ++ /* If Black clamping is enable then make dcsub 0 */ ++ regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n"); ++} ++ ++static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) ++{ ++ u32 val; ++ ++ val = ((bcomp->b & CCDC_BLK_COMP_MASK) | ++ ((bcomp->gb & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_GB_COMP_SHIFT) | ++ ((bcomp->gr & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_GR_COMP_SHIFT) | ++ ((bcomp->r & CCDC_BLK_COMP_MASK) << ++ CCDC_BLK_COMP_R_COMP_SHIFT)); ++ regw(val, CCDC_BLKCMP); ++} ++ ++static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) ++{ ++ u32 val; ++ ++ /* Initially disable FPC */ ++ val = CCDC_FPC_DISABLE; ++ regw(val, CCDC_FPC); ++ ++ if (!fpc->enable) ++ return; ++ ++ /* Configure Fault pixel if needed */ ++ regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n", ++ (fpc->fpc_table_addr)); ++ /* Write the FPC params with FPC disable */ ++ val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; ++ regw(val, CCDC_FPC); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); ++ /* read the FPC register */ ++ val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; ++ regw(val, CCDC_FPC); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); ++} ++ ++/* ++ * ccdc_config_raw() ++ * This function will configure CCDC for Raw capture mode ++ */ ++void ccdc_config_raw(void) ++{ ++ struct ccdc_params_raw *params = &ccdc_cfg.bayer; ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int syn_mode = 0; ++ unsigned int val; ++ ++ dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); ++ ++ /* Reset CCDC */ ++ ccdc_restore_defaults(); ++ ++ /* Disable latching function registers on VSYNC */ ++ regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); ++ ++ /* ++ * Configure the vertical sync polarity(SYN_MODE.VDPOL), ++ * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity ++ * (SYN_MODE.FLDPOL), frame format(progressive or interlace), ++ * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output ++ * SDRAM, enable internal timing generator ++ */ ++ syn_mode = ++ (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | ++ ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | ++ ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | ++ ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | ++ ((config_params->data_sz & CCDC_DATA_SZ_MASK) << ++ CCDC_DATA_SZ_SHIFT) | ++ ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | ++ CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); ++ ++ /* Enable and configure aLaw register if needed */ ++ if (config_params->alaw.enable) { ++ val = ((config_params->alaw.gama_wd & ++ CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); ++ regw(val, CCDC_ALAW); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); ++ } ++ ++ /* Configure video window */ ++ ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); ++ ++ /* Configure Black Clamp */ ++ ccdc_config_black_clamp(&config_params->blk_clamp); ++ ++ /* Configure Black level compensation */ ++ ccdc_config_black_compense(&config_params->blk_comp); ++ ++ /* Configure Fault Pixel Correction */ ++ ccdc_config_fpc(&config_params->fault_pxl); ++ ++ /* If data size is 8 bit then pack the data */ ++ if ((config_params->data_sz == CCDC_DATA_8BITS) || ++ config_params->alaw.enable) ++ syn_mode |= CCDC_DATA_PACK_ENABLE; ++ ++#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE ++ /* enable video port */ ++ val = CCDC_ENABLE_VIDEO_PORT; ++#else ++ /* disable video port */ ++ val = CCDC_DISABLE_VIDEO_PORT; ++#endif ++ ++ if (config_params->data_sz == CCDC_DATA_8BITS) ++ val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) ++ << CCDC_FMTCFG_VPIN_SHIFT; ++ else ++ val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) ++ << CCDC_FMTCFG_VPIN_SHIFT; ++ /* Write value in FMTCFG */ ++ regw(val, CCDC_FMTCFG); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val); ++ /* Configure the color pattern according to mt9t001 sensor */ ++ regw(CCDC_COLPTN_VAL, CCDC_COLPTN); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); ++ /* ++ * Configure Data formatter(Video port) pixel selection ++ * (FMT_HORZ, FMT_VERT) ++ */ ++ val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << ++ CCDC_FMT_HORZ_FMTSPH_SHIFT) | ++ (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); ++ regw(val, CCDC_FMT_HORZ); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val); ++ val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) ++ << CCDC_FMT_VERT_FMTSLV_SHIFT; ++ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) ++ val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; ++ else ++ val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; ++ ++ dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n", ++ params->win.height); ++ regw(val, CCDC_FMT_VERT); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val); ++ ++ dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)..."); ++ ++ /* ++ * Configure Horizontal offset register. If pack 8 is enabled then ++ * 1 pixel will take 1 byte ++ */ ++ if ((config_params->data_sz == CCDC_DATA_8BITS) || ++ config_params->alaw.enable) ++ regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & ++ CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); ++ else ++ /* else one pixel will take 2 byte */ ++ regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + ++ CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, ++ CCDC_HSIZE_OFF); ++ ++ /* Set value for SDOFST */ ++ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ if (params->image_invert_enable) { ++ /* For intelace inverse mode */ ++ regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n"); ++ } ++ ++ else { ++ /* For intelace non inverse mode */ ++ regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n"); ++ } ++ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { ++ regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n"); ++ } ++ ++ /* ++ * Configure video port pixel selection (VPOUT) ++ * Here -1 is to make the height value less than FMT_VERT.FMTLNV ++ */ ++ if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) ++ val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) ++ << CCDC_VP_OUT_VERT_NUM_SHIFT; ++ else ++ val = ++ ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - ++ 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << ++ CCDC_VP_OUT_VERT_NUM_SHIFT; ++ ++ val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) ++ << CCDC_VP_OUT_HORZ_NUM_SHIFT; ++ val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; ++ regw(val, CCDC_VP_OUT); ++ ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val); ++ regw(syn_mode, CCDC_SYN_MODE); ++ dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); ++ ++ ccdc_sbl_reset(); ++ dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); ++ ccdc_readregs(); ++} ++ ++static int ccdc_configure(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_config_raw(); ++ else ++ ccdc_config_ycbcr(); ++ return 0; ++} ++ ++static int ccdc_set_buftype(enum ccdc_buftype buf_type) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.buf_type = buf_type; ++ else ++ ccdc_cfg.ycbcr.buf_type = buf_type; ++ return 0; ++} ++ ++static enum ccdc_buftype ccdc_get_buftype(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ return ccdc_cfg.bayer.buf_type; ++ return ccdc_cfg.ycbcr.buf_type; ++} ++ ++static int ccdc_enum_pix(u32 *pix, int i) ++{ ++ int ret = -EINVAL; ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { ++ *pix = ccdc_raw_bayer_pix_formats[i]; ++ ret = 0; ++ } ++ } else { ++ if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { ++ *pix = ccdc_raw_yuv_pix_formats[i]; ++ ret = 0; ++ } ++ } ++ return ret; ++} ++ ++static int ccdc_set_pixel_format(u32 pixfmt) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; ++ if (pixfmt == V4L2_PIX_FMT_SBGGR8) ++ ccdc_cfg.bayer.config_params.alaw.enable = 1; ++ else if (pixfmt != V4L2_PIX_FMT_SBGGR16) ++ return -EINVAL; ++ } else { ++ if (pixfmt == V4L2_PIX_FMT_YUYV) ++ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; ++ else if (pixfmt == V4L2_PIX_FMT_UYVY) ++ ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; ++ else ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static u32 ccdc_get_pixel_format(void) ++{ ++ struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; ++ u32 pixfmt; ++ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ if (alaw->enable) ++ pixfmt = V4L2_PIX_FMT_SBGGR8; ++ else ++ pixfmt = V4L2_PIX_FMT_SBGGR16; ++ else { ++ if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) ++ pixfmt = V4L2_PIX_FMT_YUYV; ++ else ++ pixfmt = V4L2_PIX_FMT_UYVY; ++ } ++ return pixfmt; ++} ++ ++static int ccdc_set_image_window(struct v4l2_rect *win) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.win = *win; ++ else ++ ccdc_cfg.ycbcr.win = *win; ++ return 0; ++} ++ ++static void ccdc_get_image_window(struct v4l2_rect *win) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ *win = ccdc_cfg.bayer.win; ++ else ++ *win = ccdc_cfg.ycbcr.win; ++} ++ ++static unsigned int ccdc_get_line_length(void) ++{ ++ struct ccdc_config_params_raw *config_params = ++ &ccdc_cfg.bayer.config_params; ++ unsigned int len; ++ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { ++ if ((config_params->alaw.enable) || ++ (config_params->data_sz == CCDC_DATA_8BITS)) ++ len = ccdc_cfg.bayer.win.width; ++ else ++ len = ccdc_cfg.bayer.win.width * 2; ++ } else ++ len = ccdc_cfg.ycbcr.win.width * 2; ++ return ALIGN(len, 32); ++} ++ ++static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ ccdc_cfg.bayer.frm_fmt = frm_fmt; ++ else ++ ccdc_cfg.ycbcr.frm_fmt = frm_fmt; ++ return 0; ++} ++ ++static enum ccdc_frmfmt ccdc_get_frame_format(void) ++{ ++ if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ++ return ccdc_cfg.bayer.frm_fmt; ++ else ++ return ccdc_cfg.ycbcr.frm_fmt; ++} ++ ++static int ccdc_getfid(void) ++{ ++ return (regr(CCDC_SYN_MODE) >> 15) & 1; ++} ++ ++/* misc operations */ ++static inline void ccdc_setfbaddr(unsigned long addr) ++{ ++ regw(addr & 0xffffffe0, CCDC_SDR_ADDR); ++} ++ ++static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) ++{ ++ ccdc_cfg.if_type = params->if_type; ++ ++ switch (params->if_type) { ++ case VPFE_BT656: ++ case VPFE_YCBCR_SYNC_16: ++ case VPFE_YCBCR_SYNC_8: ++ case VPFE_BT656_10BIT: ++ ccdc_cfg.ycbcr.vd_pol = params->vdpol; ++ ccdc_cfg.ycbcr.hd_pol = params->hdpol; ++ break; ++ default: ++ /* TODO add support for raw bayer here */ ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static void ccdc_save_context(void) ++{ ++ ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR); ++ ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE); ++ ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID); ++ ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES); ++ ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO); ++ ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START); ++ ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES); ++ ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING); ++ ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF); ++ ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST); ++ ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR); ++ ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP); ++ ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB); ++ ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN); ++ ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP); ++ ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC); ++ ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR); ++ ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT); ++ ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW); ++ ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF); ++ ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG); ++ ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG); ++ ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ); ++ ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT); ++ ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0); ++ ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1); ++ ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2); ++ ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3); ++ ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4); ++ ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5); ++ ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6); ++ ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7); ++ ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0); ++ ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1); ++ ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0); ++ ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1); ++ ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT); ++} ++ ++static void ccdc_restore_context(void) ++{ ++ regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE); ++ regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID); ++ regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES); ++ regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO); ++ regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START); ++ regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES); ++ regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING); ++ regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF); ++ regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST); ++ regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR); ++ regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP); ++ regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB); ++ regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN); ++ regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP); ++ regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC); ++ regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR); ++ regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT); ++ regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW); ++ regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF); ++ regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG); ++ regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG); ++ regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ); ++ regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT); ++ regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0); ++ regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1); ++ regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2); ++ regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3); ++ regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4); ++ regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5); ++ regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6); ++ regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7); ++ regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0); ++ regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1); ++ regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0); ++ regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1); ++ regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT); ++ regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR); ++} ++static struct ccdc_hw_device ccdc_hw_dev = { ++ .name = "DM6446 CCDC", ++ .owner = THIS_MODULE, ++ .hw_ops = { ++ .open = ccdc_open, ++ .close = ccdc_close, ++ .reset = ccdc_sbl_reset, ++ .enable = ccdc_enable, ++ .set_hw_if_params = ccdc_set_hw_if_params, ++ .set_params = ccdc_set_params, ++ .configure = ccdc_configure, ++ .set_buftype = ccdc_set_buftype, ++ .get_buftype = ccdc_get_buftype, ++ .enum_pix = ccdc_enum_pix, ++ .set_pixel_format = ccdc_set_pixel_format, ++ .get_pixel_format = ccdc_get_pixel_format, ++ .set_frame_format = ccdc_set_frame_format, ++ .get_frame_format = ccdc_get_frame_format, ++ .set_image_window = ccdc_set_image_window, ++ .get_image_window = ccdc_get_image_window, ++ .get_line_length = ccdc_get_line_length, ++ .setfbaddr = ccdc_setfbaddr, ++ .getfid = ccdc_getfid, ++ }, ++}; ++ ++static int __devinit dm644x_ccdc_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int status = 0; ++ ++ /* ++ * first try to register with vpfe. If not correct platform, then we ++ * don't have to iomap ++ */ ++ status = vpfe_register_ccdc_device(&ccdc_hw_dev); ++ if (status < 0) ++ return status; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ status = -ENODEV; ++ goto fail_nores; ++ } ++ ++ res = request_mem_region(res->start, resource_size(res), res->name); ++ if (!res) { ++ status = -EBUSY; ++ goto fail_nores; ++ } ++ ++ ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res)); ++ if (!ccdc_cfg.base_addr) { ++ status = -ENOMEM; ++ goto fail_nomem; ++ } ++ ++ /* Get and enable Master clock */ ++ ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); ++ if (IS_ERR(ccdc_cfg.mclk)) { ++ status = PTR_ERR(ccdc_cfg.mclk); ++ goto fail_nomap; ++ } ++ if (clk_prepare_enable(ccdc_cfg.mclk)) { ++ status = -ENODEV; ++ goto fail_mclk; ++ } ++ ++ /* Get and enable Slave clock */ ++ ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); ++ if (IS_ERR(ccdc_cfg.sclk)) { ++ status = PTR_ERR(ccdc_cfg.sclk); ++ goto fail_mclk; ++ } ++ if (clk_prepare_enable(ccdc_cfg.sclk)) { ++ status = -ENODEV; ++ goto fail_sclk; ++ } ++ ccdc_cfg.dev = &pdev->dev; ++ printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); ++ return 0; ++fail_sclk: ++ clk_disable_unprepare(ccdc_cfg.sclk); ++ clk_put(ccdc_cfg.sclk); ++fail_mclk: ++ clk_disable_unprepare(ccdc_cfg.mclk); ++ clk_put(ccdc_cfg.mclk); ++fail_nomap: ++ iounmap(ccdc_cfg.base_addr); ++fail_nomem: ++ release_mem_region(res->start, resource_size(res)); ++fail_nores: ++ vpfe_unregister_ccdc_device(&ccdc_hw_dev); ++ return status; ++} ++ ++static int dm644x_ccdc_remove(struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ clk_disable_unprepare(ccdc_cfg.mclk); ++ clk_disable_unprepare(ccdc_cfg.sclk); ++ clk_put(ccdc_cfg.mclk); ++ clk_put(ccdc_cfg.sclk); ++ iounmap(ccdc_cfg.base_addr); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) ++ release_mem_region(res->start, resource_size(res)); ++ vpfe_unregister_ccdc_device(&ccdc_hw_dev); ++ return 0; ++} ++ ++static int dm644x_ccdc_suspend(struct device *dev) ++{ ++ /* Save CCDC context */ ++ ccdc_save_context(); ++ /* Disable CCDC */ ++ ccdc_enable(0); ++ /* Disable both master and slave clock */ ++ clk_disable_unprepare(ccdc_cfg.mclk); ++ clk_disable_unprepare(ccdc_cfg.sclk); ++ ++ return 0; ++} ++ ++static int dm644x_ccdc_resume(struct device *dev) ++{ ++ /* Enable both master and slave clock */ ++ clk_prepare_enable(ccdc_cfg.mclk); ++ clk_prepare_enable(ccdc_cfg.sclk); ++ /* Restore CCDC context */ ++ ccdc_restore_context(); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops dm644x_ccdc_pm_ops = { ++ .suspend = dm644x_ccdc_suspend, ++ .resume = dm644x_ccdc_resume, ++}; ++ ++static struct platform_driver dm644x_ccdc_driver = { ++ .driver = { ++ .name = "dm644x_ccdc", ++ .owner = THIS_MODULE, ++ .pm = &dm644x_ccdc_pm_ops, ++ }, ++ .remove = __devexit_p(dm644x_ccdc_remove), ++ .probe = dm644x_ccdc_probe, ++}; ++ ++module_platform_driver(dm644x_ccdc_driver); +diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/davinci/dm644x_ccdc_regs.h +new file mode 100644 +index 0000000..90370e4 +--- /dev/null ++++ b/drivers/media/platform/davinci/dm644x_ccdc_regs.h +@@ -0,0 +1,153 @@ ++/* ++ * Copyright (C) 2006-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _DM644X_CCDC_REGS_H ++#define _DM644X_CCDC_REGS_H ++ ++/**************************************************************************\ ++* Register OFFSET Definitions ++\**************************************************************************/ ++#define CCDC_PID 0x0 ++#define CCDC_PCR 0x4 ++#define CCDC_SYN_MODE 0x8 ++#define CCDC_HD_VD_WID 0xc ++#define CCDC_PIX_LINES 0x10 ++#define CCDC_HORZ_INFO 0x14 ++#define CCDC_VERT_START 0x18 ++#define CCDC_VERT_LINES 0x1c ++#define CCDC_CULLING 0x20 ++#define CCDC_HSIZE_OFF 0x24 ++#define CCDC_SDOFST 0x28 ++#define CCDC_SDR_ADDR 0x2c ++#define CCDC_CLAMP 0x30 ++#define CCDC_DCSUB 0x34 ++#define CCDC_COLPTN 0x38 ++#define CCDC_BLKCMP 0x3c ++#define CCDC_FPC 0x40 ++#define CCDC_FPC_ADDR 0x44 ++#define CCDC_VDINT 0x48 ++#define CCDC_ALAW 0x4c ++#define CCDC_REC656IF 0x50 ++#define CCDC_CCDCFG 0x54 ++#define CCDC_FMTCFG 0x58 ++#define CCDC_FMT_HORZ 0x5c ++#define CCDC_FMT_VERT 0x60 ++#define CCDC_FMT_ADDR0 0x64 ++#define CCDC_FMT_ADDR1 0x68 ++#define CCDC_FMT_ADDR2 0x6c ++#define CCDC_FMT_ADDR3 0x70 ++#define CCDC_FMT_ADDR4 0x74 ++#define CCDC_FMT_ADDR5 0x78 ++#define CCDC_FMT_ADDR6 0x7c ++#define CCDC_FMT_ADDR7 0x80 ++#define CCDC_PRGEVEN_0 0x84 ++#define CCDC_PRGEVEN_1 0x88 ++#define CCDC_PRGODD_0 0x8c ++#define CCDC_PRGODD_1 0x90 ++#define CCDC_VP_OUT 0x94 ++#define CCDC_REG_END 0x98 ++ ++/*************************************************************** ++* Define for various register bit mask and shifts for CCDC ++****************************************************************/ ++#define CCDC_FID_POL_MASK 1 ++#define CCDC_FID_POL_SHIFT 4 ++#define CCDC_HD_POL_MASK 1 ++#define CCDC_HD_POL_SHIFT 3 ++#define CCDC_VD_POL_MASK 1 ++#define CCDC_VD_POL_SHIFT 2 ++#define CCDC_HSIZE_OFF_MASK 0xffffffe0 ++#define CCDC_32BYTE_ALIGN_VAL 31 ++#define CCDC_FRM_FMT_MASK 0x1 ++#define CCDC_FRM_FMT_SHIFT 7 ++#define CCDC_DATA_SZ_MASK 7 ++#define CCDC_DATA_SZ_SHIFT 8 ++#define CCDC_PIX_FMT_MASK 3 ++#define CCDC_PIX_FMT_SHIFT 12 ++#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF ++#define CCDC_WEN_ENABLE (1 << 17) ++#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF ++#define CCDC_VDHDEN_ENABLE (1 << 16) ++#define CCDC_LPF_ENABLE (1 << 14) ++#define CCDC_ALAW_ENABLE (1 << 3) ++#define CCDC_ALAW_GAMA_WD_MASK 7 ++#define CCDC_BLK_CLAMP_ENABLE (1 << 31) ++#define CCDC_BLK_SGAIN_MASK 0x1F ++#define CCDC_BLK_ST_PXL_MASK 0x7FFF ++#define CCDC_BLK_ST_PXL_SHIFT 10 ++#define CCDC_BLK_SAMPLE_LN_MASK 7 ++#define CCDC_BLK_SAMPLE_LN_SHIFT 28 ++#define CCDC_BLK_SAMPLE_LINE_MASK 7 ++#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 ++#define CCDC_BLK_DC_SUB_MASK 0x03FFF ++#define CCDC_BLK_COMP_MASK 0xFF ++#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 ++#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 ++#define CCDC_BLK_COMP_R_COMP_SHIFT 24 ++#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) ++#define CCDC_FPC_ENABLE (1 << 15) ++#define CCDC_FPC_DISABLE 0 ++#define CCDC_FPC_FPC_NUM_MASK 0x7FFF ++#define CCDC_DATA_PACK_ENABLE (1 << 11) ++#define CCDC_FMTCFG_VPIN_MASK 7 ++#define CCDC_FMTCFG_VPIN_SHIFT 12 ++#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF ++#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF ++#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 ++#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF ++#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF ++#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 ++#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF ++#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 ++#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF ++#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 ++#define CCDC_VP_OUT_HORZ_ST_MASK 0xF ++#define CCDC_HORZ_INFO_SPH_SHIFT 16 ++#define CCDC_VERT_START_SLV0_SHIFT 16 ++#define CCDC_VDINT_VDINT0_SHIFT 16 ++#define CCDC_VDINT_VDINT1_MASK 0xFFFF ++#define CCDC_PPC_RAW 1 ++#define CCDC_DCSUB_DEFAULT_VAL 0 ++#define CCDC_CLAMP_DEFAULT_VAL 0 ++#define CCDC_ENABLE_VIDEO_PORT 0x8000 ++#define CCDC_DISABLE_VIDEO_PORT 0 ++#define CCDC_COLPTN_VAL 0xBB11BB11 ++#define CCDC_TWO_BYTES_PER_PIXEL 2 ++#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D ++#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 ++#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 ++#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 ++#define CCDC_INTERLACED_HEIGHT_SHIFT 1 ++#define CCDC_SYN_MODE_INPMOD_SHIFT 12 ++#define CCDC_SYN_MODE_INPMOD_MASK 3 ++#define CCDC_SYN_MODE_8BITS (7 << 8) ++#define CCDC_SYN_MODE_10BITS (6 << 8) ++#define CCDC_SYN_MODE_11BITS (5 << 8) ++#define CCDC_SYN_MODE_12BITS (4 << 8) ++#define CCDC_SYN_MODE_13BITS (3 << 8) ++#define CCDC_SYN_MODE_14BITS (2 << 8) ++#define CCDC_SYN_MODE_15BITS (1 << 8) ++#define CCDC_SYN_MODE_16BITS (0 << 8) ++#define CCDC_SYN_FLDMODE_MASK 1 ++#define CCDC_SYN_FLDMODE_SHIFT 7 ++#define CCDC_REC656IF_BT656_EN 3 ++#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) ++#define CCDC_CCDCFG_Y8POS_SHIFT 11 ++#define CCDC_CCDCFG_BW656_10BIT (1 << 5) ++#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 ++#define CCDC_NO_CULLING 0xffff00ff ++#endif +diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/davinci/isif.c +new file mode 100644 +index 0000000..2c26c3e +--- /dev/null ++++ b/drivers/media/platform/davinci/isif.c +@@ -0,0 +1,1165 @@ ++/* ++ * Copyright (C) 2008-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Image Sensor Interface (ISIF) driver ++ * ++ * This driver is for configuring the ISIF IP available on DM365 or any other ++ * TI SoCs. This is used for capturing yuv or bayer video or image data ++ * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 ++ * and DM6446, but with enhanced or additional ip blocks. The driver ++ * configures the ISIF upon commands from the vpfe bridge driver through ++ * ccdc_hw_device interface. ++ * ++ * TODO: 1) Raw bayer parameter settings and bayer capture ++ * 2) Add support for control ioctl ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include "isif_regs.h" ++#include "ccdc_hw_device.h" ++ ++/* Defaults for module configuration parameters */ ++static struct isif_config_params_raw isif_config_defaults = { ++ .linearize = { ++ .en = 0, ++ .corr_shft = ISIF_NO_SHIFT, ++ .scale_fact = {1, 0}, ++ }, ++ .df_csc = { ++ .df_or_csc = 0, ++ .csc = { ++ .en = 0, ++ }, ++ }, ++ .dfc = { ++ .en = 0, ++ }, ++ .bclamp = { ++ .en = 0, ++ }, ++ .gain_offset = { ++ .gain = { ++ .r_ye = {1, 0}, ++ .gr_cy = {1, 0}, ++ .gb_g = {1, 0}, ++ .b_mg = {1, 0}, ++ }, ++ }, ++ .culling = { ++ .hcpat_odd = 0xff, ++ .hcpat_even = 0xff, ++ .vcpat = 0xff, ++ }, ++ .compress = { ++ .alg = ISIF_ALAW, ++ }, ++}; ++ ++/* ISIF operation configuration */ ++static struct isif_oper_config { ++ struct device *dev; ++ enum vpfe_hw_if_type if_type; ++ struct isif_ycbcr_config ycbcr; ++ struct isif_params_raw bayer; ++ enum isif_data_pack data_pack; ++ /* Master clock */ ++ struct clk *mclk; ++ /* ISIF base address */ ++ void __iomem *base_addr; ++ /* ISIF Linear Table 0 */ ++ void __iomem *linear_tbl0_addr; ++ /* ISIF Linear Table 1 */ ++ void __iomem *linear_tbl1_addr; ++} isif_cfg = { ++ .ycbcr = { ++ .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, ++ .frm_fmt = CCDC_FRMFMT_INTERLACED, ++ .win = ISIF_WIN_NTSC, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .pix_order = CCDC_PIXORDER_CBYCRY, ++ .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, ++ }, ++ .bayer = { ++ .pix_fmt = CCDC_PIXFMT_RAW, ++ .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, ++ .win = ISIF_WIN_VGA, ++ .fid_pol = VPFE_PINPOL_POSITIVE, ++ .vd_pol = VPFE_PINPOL_POSITIVE, ++ .hd_pol = VPFE_PINPOL_POSITIVE, ++ .gain = { ++ .r_ye = {1, 0}, ++ .gr_cy = {1, 0}, ++ .gb_g = {1, 0}, ++ .b_mg = {1, 0}, ++ }, ++ .cfa_pat = ISIF_CFA_PAT_MOSAIC, ++ .data_msb = ISIF_BIT_MSB_11, ++ .config_params = { ++ .data_shift = ISIF_NO_SHIFT, ++ .col_pat_field0 = { ++ .olop = ISIF_GREEN_BLUE, ++ .olep = ISIF_BLUE, ++ .elop = ISIF_RED, ++ .elep = ISIF_GREEN_RED, ++ }, ++ .col_pat_field1 = { ++ .olop = ISIF_GREEN_BLUE, ++ .olep = ISIF_BLUE, ++ .elop = ISIF_RED, ++ .elep = ISIF_GREEN_RED, ++ }, ++ .test_pat_gen = 0, ++ }, ++ }, ++ .data_pack = ISIF_DATA_PACK8, ++}; ++ ++/* Raw Bayer formats */ ++static const u32 isif_raw_bayer_pix_formats[] = { ++ V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; ++ ++/* Raw YUV formats */ ++static const u32 isif_raw_yuv_pix_formats[] = { ++ V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; ++ ++/* register access routines */ ++static inline u32 regr(u32 offset) ++{ ++ return __raw_readl(isif_cfg.base_addr + offset); ++} ++ ++static inline void regw(u32 val, u32 offset) ++{ ++ __raw_writel(val, isif_cfg.base_addr + offset); ++} ++ ++/* reg_modify() - read, modify and write register */ ++static inline u32 reg_modify(u32 mask, u32 val, u32 offset) ++{ ++ u32 new_val = (regr(offset) & ~mask) | (val & mask); ++ ++ regw(new_val, offset); ++ return new_val; ++} ++ ++static inline void regw_lin_tbl(u32 val, u32 offset, int i) ++{ ++ if (!i) ++ __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); ++ else ++ __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); ++} ++ ++static void isif_disable_all_modules(void) ++{ ++ /* disable BC */ ++ regw(0, CLAMPCFG); ++ /* disable vdfc */ ++ regw(0, DFCCTL); ++ /* disable CSC */ ++ regw(0, CSCCTL); ++ /* disable linearization */ ++ regw(0, LINCFG0); ++ /* disable other modules here as they are supported */ ++} ++ ++static void isif_enable(int en) ++{ ++ if (!en) { ++ /* Before disable isif, disable all ISIF modules */ ++ isif_disable_all_modules(); ++ /* ++ * wait for next VD. Assume lowest scan rate is 12 Hz. So ++ * 100 msec delay is good enough ++ */ ++ msleep(100); ++ } ++ reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); ++} ++ ++static void isif_enable_output_to_sdram(int en) ++{ ++ reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); ++} ++ ++static void isif_config_culling(struct isif_cul *cul) ++{ ++ u32 val; ++ ++ /* Horizontal pattern */ ++ val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; ++ regw(val, CULH); ++ ++ /* vertical pattern */ ++ regw(cul->vcpat, CULV); ++ ++ /* LPF */ ++ reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, ++ cul->en_lpf << ISIF_LPF_SHIFT, MODESET); ++} ++ ++static void isif_config_gain_offset(void) ++{ ++ struct isif_gain_offsets_adj *gain_off_p = ++ &isif_cfg.bayer.config_params.gain_offset; ++ u32 val; ++ ++ val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | ++ (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | ++ (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | ++ (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | ++ (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | ++ (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); ++ ++ reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); ++ ++ val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | ++ gain_off_p->gain.r_ye.decimal; ++ regw(val, CRGAIN); ++ ++ val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | ++ gain_off_p->gain.gr_cy.decimal; ++ regw(val, CGRGAIN); ++ ++ val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | ++ gain_off_p->gain.gb_g.decimal; ++ regw(val, CGBGAIN); ++ ++ val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | ++ gain_off_p->gain.b_mg.decimal; ++ regw(val, CBGAIN); ++ ++ regw(gain_off_p->offset, COFSTA); ++} ++ ++static void isif_restore_defaults(void) ++{ ++ enum vpss_ccdc_source_sel source = VPSS_CCDCIN; ++ ++ dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); ++ isif_cfg.bayer.config_params = isif_config_defaults; ++ /* Enable clock to ISIF, IPIPEIF and BL */ ++ vpss_enable_clock(VPSS_CCDC_CLOCK, 1); ++ vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); ++ vpss_enable_clock(VPSS_BL_CLOCK, 1); ++ /* Set default offset and gain */ ++ isif_config_gain_offset(); ++ vpss_select_ccdc_source(source); ++ dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); ++} ++ ++static int isif_open(struct device *device) ++{ ++ isif_restore_defaults(); ++ return 0; ++} ++ ++/* This function will configure the window size to be capture in ISIF reg */ ++static void isif_setwin(struct v4l2_rect *image_win, ++ enum ccdc_frmfmt frm_fmt, int ppc) ++{ ++ int horz_start, horz_nr_pixels; ++ int vert_start, vert_nr_lines; ++ int mid_img = 0; ++ ++ dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); ++ /* ++ * ppc - per pixel count. indicates how many pixels per cell ++ * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. ++ * raw capture this is 1 ++ */ ++ horz_start = image_win->left << (ppc - 1); ++ horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; ++ ++ /* Writing the horizontal info into the registers */ ++ regw(horz_start & START_PX_HOR_MASK, SPH); ++ regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); ++ vert_start = image_win->top; ++ ++ if (frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ vert_nr_lines = (image_win->height >> 1) - 1; ++ vert_start >>= 1; ++ /* To account for VD since line 0 doesn't have any data */ ++ vert_start += 1; ++ } else { ++ /* To account for VD since line 0 doesn't have any data */ ++ vert_start += 1; ++ vert_nr_lines = image_win->height - 1; ++ /* configure VDINT0 and VDINT1 */ ++ mid_img = vert_start + (image_win->height / 2); ++ regw(mid_img, VDINT1); ++ } ++ ++ regw(0, VDINT0); ++ regw(vert_start & START_VER_ONE_MASK, SLV0); ++ regw(vert_start & START_VER_TWO_MASK, SLV1); ++ regw(vert_nr_lines & NUM_LINES_VER, LNV); ++} ++ ++static void isif_config_bclamp(struct isif_black_clamp *bc) ++{ ++ u32 val; ++ ++ /* ++ * DC Offset is always added to image data irrespective of bc enable ++ * status ++ */ ++ regw(bc->dc_offset, CLDCOFST); ++ ++ if (bc->en) { ++ val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; ++ ++ /* Enable BC and horizontal clamp caculation paramaters */ ++ val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); ++ ++ regw(val, CLAMPCFG); ++ ++ if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { ++ /* ++ * Window count for calculation ++ * Base window selection ++ * pixel limit ++ * Horizontal size of window ++ * vertical size of the window ++ * Horizontal start position of the window ++ * Vertical start position of the window ++ */ ++ val = bc->horz.win_count_calc | ++ ((!!bc->horz.base_win_sel_calc) << ++ ISIF_HORZ_BC_WIN_SEL_SHIFT) | ++ ((!!bc->horz.clamp_pix_limit) << ++ ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | ++ (bc->horz.win_h_sz_calc << ++ ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | ++ (bc->horz.win_v_sz_calc << ++ ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); ++ regw(val, CLHWIN0); ++ ++ regw(bc->horz.win_start_h_calc, CLHWIN1); ++ regw(bc->horz.win_start_v_calc, CLHWIN2); ++ } ++ ++ /* vertical clamp caculation paramaters */ ++ ++ /* Reset clamp value sel for previous line */ ++ val |= ++ (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | ++ (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); ++ regw(val, CLVWIN0); ++ ++ /* Optical Black horizontal start position */ ++ regw(bc->vert.ob_start_h, CLVWIN1); ++ /* Optical Black vertical start position */ ++ regw(bc->vert.ob_start_v, CLVWIN2); ++ /* Optical Black vertical size for calculation */ ++ regw(bc->vert.ob_v_sz_calc, CLVWIN3); ++ /* Vertical start position for BC subtraction */ ++ regw(bc->vert_start_sub, CLSV); ++ } ++} ++ ++static void isif_config_linearization(struct isif_linearize *linearize) ++{ ++ u32 val, i; ++ ++ if (!linearize->en) { ++ regw(0, LINCFG0); ++ return; ++ } ++ ++ /* shift value for correction & enable linearization (set lsb) */ ++ val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; ++ regw(val, LINCFG0); ++ ++ /* Scale factor */ ++ val = ((!!linearize->scale_fact.integer) << ++ ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | ++ linearize->scale_fact.decimal; ++ regw(val, LINCFG1); ++ ++ for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { ++ if (i % 2) ++ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); ++ else ++ regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); ++ } ++} ++ ++static int isif_config_dfc(struct isif_dfc *vdfc) ++{ ++ /* initialize retries to loop for max ~ 250 usec */ ++ u32 val, count, retries = loops_per_jiffy / (4000/HZ); ++ int i; ++ ++ if (!vdfc->en) ++ return 0; ++ ++ /* Correction mode */ ++ val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); ++ ++ /* Correct whole line or partial */ ++ if (vdfc->corr_whole_line) ++ val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; ++ ++ /* level shift value */ ++ val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; ++ ++ regw(val, DFCCTL); ++ ++ /* Defect saturation level */ ++ regw(vdfc->def_sat_level, VDFSATLV); ++ ++ regw(vdfc->table[0].pos_vert, DFCMEM0); ++ regw(vdfc->table[0].pos_horz, DFCMEM1); ++ if (vdfc->corr_mode == ISIF_VDFC_NORMAL || ++ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { ++ regw(vdfc->table[0].level_at_pos, DFCMEM2); ++ regw(vdfc->table[0].level_up_pixels, DFCMEM3); ++ regw(vdfc->table[0].level_low_pixels, DFCMEM4); ++ } ++ ++ /* set DFCMARST and set DFCMWR */ ++ val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; ++ regw(val, DFCMEMCTL); ++ ++ count = retries; ++ while (count && (regr(DFCMEMCTL) & 0x1)) ++ count--; ++ ++ if (!count) { ++ dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); ++ return -1; ++ } ++ ++ for (i = 1; i < vdfc->num_vdefects; i++) { ++ regw(vdfc->table[i].pos_vert, DFCMEM0); ++ regw(vdfc->table[i].pos_horz, DFCMEM1); ++ if (vdfc->corr_mode == ISIF_VDFC_NORMAL || ++ vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { ++ regw(vdfc->table[i].level_at_pos, DFCMEM2); ++ regw(vdfc->table[i].level_up_pixels, DFCMEM3); ++ regw(vdfc->table[i].level_low_pixels, DFCMEM4); ++ } ++ val = regr(DFCMEMCTL); ++ /* clear DFCMARST and set DFCMWR */ ++ val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); ++ val |= 1; ++ regw(val, DFCMEMCTL); ++ ++ count = retries; ++ while (count && (regr(DFCMEMCTL) & 0x1)) ++ count--; ++ ++ if (!count) { ++ dev_err(isif_cfg.dev, ++ "defect table write timeout !!!\n"); ++ return -1; ++ } ++ } ++ if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { ++ /* Extra cycle needed */ ++ regw(0, DFCMEM0); ++ regw(0x1FFF, DFCMEM1); ++ regw(1, DFCMEMCTL); ++ } ++ ++ /* enable VDFC */ ++ reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), ++ DFCCTL); ++ return 0; ++} ++ ++static void isif_config_csc(struct isif_df_csc *df_csc) ++{ ++ u32 val1 = 0, val2 = 0, i; ++ ++ if (!df_csc->csc.en) { ++ regw(0, CSCCTL); ++ return; ++ } ++ for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { ++ if ((i % 2) == 0) { ++ /* CSCM - LSB */ ++ val1 = (df_csc->csc.coeff[i].integer << ++ ISIF_CSC_COEF_INTEG_SHIFT) | ++ df_csc->csc.coeff[i].decimal; ++ } else { ++ ++ /* CSCM - MSB */ ++ val2 = (df_csc->csc.coeff[i].integer << ++ ISIF_CSC_COEF_INTEG_SHIFT) | ++ df_csc->csc.coeff[i].decimal; ++ val2 <<= ISIF_CSCM_MSB_SHIFT; ++ val2 |= val1; ++ regw(val2, (CSCM0 + ((i - 1) << 1))); ++ } ++ } ++ ++ /* program the active area */ ++ regw(df_csc->start_pix, FMTSPH); ++ /* ++ * one extra pixel as required for CSC. Actually number of ++ * pixel - 1 should be configured in this register. So we ++ * need to subtract 1 before writing to FMTSPH, but we will ++ * not do this since csc requires one extra pixel ++ */ ++ regw(df_csc->num_pixels, FMTLNH); ++ regw(df_csc->start_line, FMTSLV); ++ /* ++ * one extra line as required for CSC. See reason documented for ++ * num_pixels ++ */ ++ regw(df_csc->num_lines, FMTLNV); ++ ++ /* Enable CSC */ ++ regw(1, CSCCTL); ++} ++ ++static int isif_config_raw(void) ++{ ++ struct isif_params_raw *params = &isif_cfg.bayer; ++ struct isif_config_params_raw *module_params = ++ &isif_cfg.bayer.config_params; ++ struct vpss_pg_frame_size frame_size; ++ struct vpss_sync_pol sync; ++ u32 val; ++ ++ dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); ++ ++ /* ++ * Configure CCDCFG register:- ++ * Set CCD Not to swap input since input is RAW data ++ * Set FID detection function to Latch at V-Sync ++ * Set WENLOG - isif valid area ++ * Set TRGSEL ++ * Set EXTRG ++ * Packed to 8 or 16 bits ++ */ ++ ++ val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | ++ ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | ++ ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; ++ ++ dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); ++ regw(val, CCDCFG); ++ ++ /* ++ * Configure the vertical sync polarity(MODESET.VDPOL) ++ * Configure the horizontal sync polarity (MODESET.HDPOL) ++ * Configure frame id polarity (MODESET.FLDPOL) ++ * Configure data polarity ++ * Configure External WEN Selection ++ * Configure frame format(progressive or interlace) ++ * Configure pixel format (Input mode) ++ * Configure the data shift ++ */ ++ ++ val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | ++ (params->hd_pol << ISIF_HD_POL_SHIFT) | ++ (params->fid_pol << ISIF_FID_POL_SHIFT) | ++ (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | ++ (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | ++ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | ++ (params->pix_fmt << ISIF_INPUT_SHIFT) | ++ (params->config_params.data_shift << ISIF_DATASFT_SHIFT); ++ ++ regw(val, MODESET); ++ dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); ++ ++ /* ++ * Configure GAMMAWD register ++ * CFA pattern setting ++ */ ++ val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; ++ ++ /* Gamma msb */ ++ if (module_params->compress.alg == ISIF_ALAW) ++ val |= ISIF_ALAW_ENABLE; ++ ++ val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT); ++ regw(val, CGAMMAWD); ++ ++ /* Configure DPCM compression settings */ ++ if (module_params->compress.alg == ISIF_DPCM) { ++ val = BIT(ISIF_DPCM_EN_SHIFT) | ++ (module_params->compress.pred << ++ ISIF_DPCM_PREDICTOR_SHIFT); ++ } ++ ++ regw(val, MISC); ++ ++ /* Configure Gain & Offset */ ++ isif_config_gain_offset(); ++ ++ /* Configure Color pattern */ ++ val = (params->config_params.col_pat_field0.olop) | ++ (params->config_params.col_pat_field0.olep << 2) | ++ (params->config_params.col_pat_field0.elop << 4) | ++ (params->config_params.col_pat_field0.elep << 6) | ++ (params->config_params.col_pat_field1.olop << 8) | ++ (params->config_params.col_pat_field1.olep << 10) | ++ (params->config_params.col_pat_field1.elop << 12) | ++ (params->config_params.col_pat_field1.elep << 14); ++ regw(val, CCOLP); ++ dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); ++ ++ /* Configure HSIZE register */ ++ val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; ++ ++ /* calculate line offset in 32 bytes based on pack value */ ++ if (isif_cfg.data_pack == ISIF_PACK_8BIT) ++ val |= ((params->win.width + 31) >> 5); ++ else if (isif_cfg.data_pack == ISIF_PACK_12BIT) ++ val |= (((params->win.width + ++ (params->win.width >> 2)) + 31) >> 5); ++ else ++ val |= (((params->win.width * 2) + 31) >> 5); ++ regw(val, HSIZE); ++ ++ /* Configure SDOFST register */ ++ if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ if (params->image_invert_en) { ++ /* For interlace inverse mode */ ++ regw(0x4B6D, SDOFST); ++ dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); ++ } else { ++ /* For interlace non inverse mode */ ++ regw(0x0B6D, SDOFST); ++ dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); ++ } ++ } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { ++ if (params->image_invert_en) { ++ /* For progressive inverse mode */ ++ regw(0x4000, SDOFST); ++ dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); ++ } else { ++ /* For progressive non inverse mode */ ++ regw(0x0000, SDOFST); ++ dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); ++ } ++ } ++ ++ /* Configure video window */ ++ isif_setwin(¶ms->win, params->frm_fmt, 1); ++ ++ /* Configure Black Clamp */ ++ isif_config_bclamp(&module_params->bclamp); ++ ++ /* Configure Vertical Defection Pixel Correction */ ++ if (isif_config_dfc(&module_params->dfc) < 0) ++ return -EFAULT; ++ ++ if (!module_params->df_csc.df_or_csc) ++ /* Configure Color Space Conversion */ ++ isif_config_csc(&module_params->df_csc); ++ ++ isif_config_linearization(&module_params->linearize); ++ ++ /* Configure Culling */ ++ isif_config_culling(&module_params->culling); ++ ++ /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ ++ regw(module_params->horz_offset, DATAHOFST); ++ regw(module_params->vert_offset, DATAVOFST); ++ ++ /* Setup test pattern if enabled */ ++ if (params->config_params.test_pat_gen) { ++ /* Use the HD/VD pol settings from user */ ++ sync.ccdpg_hdpol = params->hd_pol; ++ sync.ccdpg_vdpol = params->vd_pol; ++ dm365_vpss_set_sync_pol(sync); ++ frame_size.hlpfr = isif_cfg.bayer.win.width; ++ frame_size.pplen = isif_cfg.bayer.win.height; ++ dm365_vpss_set_pg_frame_size(frame_size); ++ vpss_select_ccdc_source(VPSS_PGLPBK); ++ } ++ ++ dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); ++ return 0; ++} ++ ++static int isif_set_buftype(enum ccdc_buftype buf_type) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ isif_cfg.bayer.buf_type = buf_type; ++ else ++ isif_cfg.ycbcr.buf_type = buf_type; ++ ++ return 0; ++ ++} ++static enum ccdc_buftype isif_get_buftype(void) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ return isif_cfg.bayer.buf_type; ++ ++ return isif_cfg.ycbcr.buf_type; ++} ++ ++static int isif_enum_pix(u32 *pix, int i) ++{ ++ int ret = -EINVAL; ++ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) { ++ if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { ++ *pix = isif_raw_bayer_pix_formats[i]; ++ ret = 0; ++ } ++ } else { ++ if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { ++ *pix = isif_raw_yuv_pix_formats[i]; ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++static int isif_set_pixel_format(unsigned int pixfmt) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) { ++ if (pixfmt == V4L2_PIX_FMT_SBGGR8) { ++ if ((isif_cfg.bayer.config_params.compress.alg != ++ ISIF_ALAW) && ++ (isif_cfg.bayer.config_params.compress.alg != ++ ISIF_DPCM)) { ++ dev_dbg(isif_cfg.dev, ++ "Either configure A-Law or DPCM\n"); ++ return -EINVAL; ++ } ++ isif_cfg.data_pack = ISIF_PACK_8BIT; ++ } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { ++ isif_cfg.bayer.config_params.compress.alg = ++ ISIF_NO_COMPRESSION; ++ isif_cfg.data_pack = ISIF_PACK_16BIT; ++ } else ++ return -EINVAL; ++ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; ++ } else { ++ if (pixfmt == V4L2_PIX_FMT_YUYV) ++ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; ++ else if (pixfmt == V4L2_PIX_FMT_UYVY) ++ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; ++ else ++ return -EINVAL; ++ isif_cfg.data_pack = ISIF_PACK_8BIT; ++ } ++ return 0; ++} ++ ++static u32 isif_get_pixel_format(void) ++{ ++ u32 pixfmt; ++ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || ++ isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) ++ pixfmt = V4L2_PIX_FMT_SBGGR8; ++ else ++ pixfmt = V4L2_PIX_FMT_SBGGR16; ++ else { ++ if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) ++ pixfmt = V4L2_PIX_FMT_YUYV; ++ else ++ pixfmt = V4L2_PIX_FMT_UYVY; ++ } ++ return pixfmt; ++} ++ ++static int isif_set_image_window(struct v4l2_rect *win) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) { ++ isif_cfg.bayer.win.top = win->top; ++ isif_cfg.bayer.win.left = win->left; ++ isif_cfg.bayer.win.width = win->width; ++ isif_cfg.bayer.win.height = win->height; ++ } else { ++ isif_cfg.ycbcr.win.top = win->top; ++ isif_cfg.ycbcr.win.left = win->left; ++ isif_cfg.ycbcr.win.width = win->width; ++ isif_cfg.ycbcr.win.height = win->height; ++ } ++ return 0; ++} ++ ++static void isif_get_image_window(struct v4l2_rect *win) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ *win = isif_cfg.bayer.win; ++ else ++ *win = isif_cfg.ycbcr.win; ++} ++ ++static unsigned int isif_get_line_length(void) ++{ ++ unsigned int len; ++ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) { ++ if (isif_cfg.data_pack == ISIF_PACK_8BIT) ++ len = ((isif_cfg.bayer.win.width)); ++ else if (isif_cfg.data_pack == ISIF_PACK_12BIT) ++ len = (((isif_cfg.bayer.win.width * 2) + ++ (isif_cfg.bayer.win.width >> 2))); ++ else ++ len = (((isif_cfg.bayer.win.width * 2))); ++ } else ++ len = (((isif_cfg.ycbcr.win.width * 2))); ++ return ALIGN(len, 32); ++} ++ ++static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ isif_cfg.bayer.frm_fmt = frm_fmt; ++ else ++ isif_cfg.ycbcr.frm_fmt = frm_fmt; ++ return 0; ++} ++static enum ccdc_frmfmt isif_get_frame_format(void) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ return isif_cfg.bayer.frm_fmt; ++ return isif_cfg.ycbcr.frm_fmt; ++} ++ ++static int isif_getfid(void) ++{ ++ return (regr(MODESET) >> 15) & 0x1; ++} ++ ++/* misc operations */ ++static void isif_setfbaddr(unsigned long addr) ++{ ++ regw((addr >> 21) & 0x07ff, CADU); ++ regw((addr >> 5) & 0x0ffff, CADL); ++} ++ ++static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) ++{ ++ isif_cfg.if_type = params->if_type; ++ ++ switch (params->if_type) { ++ case VPFE_BT656: ++ case VPFE_BT656_10BIT: ++ case VPFE_YCBCR_SYNC_8: ++ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; ++ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; ++ break; ++ case VPFE_BT1120: ++ case VPFE_YCBCR_SYNC_16: ++ isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; ++ isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; ++ break; ++ case VPFE_RAW_BAYER: ++ isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; ++ break; ++ default: ++ dev_dbg(isif_cfg.dev, "Invalid interface type\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* This function will configure ISIF for YCbCr parameters. */ ++static int isif_config_ycbcr(void) ++{ ++ struct isif_ycbcr_config *params = &isif_cfg.ycbcr; ++ struct vpss_pg_frame_size frame_size; ++ u32 modeset = 0, ccdcfg = 0; ++ struct vpss_sync_pol sync; ++ ++ dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); ++ ++ /* configure pixel format or input mode */ ++ modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | ++ (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | ++ (params->fid_pol << ISIF_FID_POL_SHIFT) | ++ (params->hd_pol << ISIF_HD_POL_SHIFT) | ++ (params->vd_pol << ISIF_VD_POL_SHIFT); ++ ++ /* pack the data to 8-bit ISIFCFG */ ++ switch (isif_cfg.if_type) { ++ case VPFE_BT656: ++ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { ++ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); ++ return -EINVAL; ++ } ++ modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); ++ regw(3, REC656IF); ++ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; ++ break; ++ case VPFE_BT656_10BIT: ++ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { ++ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); ++ return -EINVAL; ++ } ++ /* setup BT.656, embedded sync */ ++ regw(3, REC656IF); ++ /* enable 10 bit mode in ccdcfg */ ++ ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | ++ ISIF_BW656_ENABLE; ++ break; ++ case VPFE_BT1120: ++ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { ++ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); ++ return -EINVAL; ++ } ++ regw(3, REC656IF); ++ break; ++ ++ case VPFE_YCBCR_SYNC_8: ++ ccdcfg |= ISIF_DATA_PACK8; ++ ccdcfg |= ISIF_YCINSWP_YCBCR; ++ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { ++ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); ++ return -EINVAL; ++ } ++ break; ++ case VPFE_YCBCR_SYNC_16: ++ if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { ++ dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); ++ return -EINVAL; ++ } ++ break; ++ default: ++ /* should never come here */ ++ dev_dbg(isif_cfg.dev, "Invalid interface type\n"); ++ return -EINVAL; ++ } ++ ++ regw(modeset, MODESET); ++ ++ /* Set up pix order */ ++ ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; ++ ++ regw(ccdcfg, CCDCFG); ++ ++ /* configure video window */ ++ if ((isif_cfg.if_type == VPFE_BT1120) || ++ (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) ++ isif_setwin(¶ms->win, params->frm_fmt, 1); ++ else ++ isif_setwin(¶ms->win, params->frm_fmt, 2); ++ ++ /* ++ * configure the horizontal line offset ++ * this is done by rounding up width to a multiple of 16 pixels ++ * and multiply by two to account for y:cb:cr 4:2:2 data ++ */ ++ regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); ++ ++ /* configure the memory line offset */ ++ if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && ++ (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) ++ /* two fields are interleaved in memory */ ++ regw(0x00000249, SDOFST); ++ ++ /* Setup test pattern if enabled */ ++ if (isif_cfg.bayer.config_params.test_pat_gen) { ++ sync.ccdpg_hdpol = params->hd_pol; ++ sync.ccdpg_vdpol = params->vd_pol; ++ dm365_vpss_set_sync_pol(sync); ++ dm365_vpss_set_pg_frame_size(frame_size); ++ } ++ return 0; ++} ++ ++static int isif_configure(void) ++{ ++ if (isif_cfg.if_type == VPFE_RAW_BAYER) ++ return isif_config_raw(); ++ return isif_config_ycbcr(); ++} ++ ++static int isif_close(struct device *device) ++{ ++ /* copy defaults to module params */ ++ isif_cfg.bayer.config_params = isif_config_defaults; ++ return 0; ++} ++ ++static struct ccdc_hw_device isif_hw_dev = { ++ .name = "ISIF", ++ .owner = THIS_MODULE, ++ .hw_ops = { ++ .open = isif_open, ++ .close = isif_close, ++ .enable = isif_enable, ++ .enable_out_to_sdram = isif_enable_output_to_sdram, ++ .set_hw_if_params = isif_set_hw_if_params, ++ .configure = isif_configure, ++ .set_buftype = isif_set_buftype, ++ .get_buftype = isif_get_buftype, ++ .enum_pix = isif_enum_pix, ++ .set_pixel_format = isif_set_pixel_format, ++ .get_pixel_format = isif_get_pixel_format, ++ .set_frame_format = isif_set_frame_format, ++ .get_frame_format = isif_get_frame_format, ++ .set_image_window = isif_set_image_window, ++ .get_image_window = isif_get_image_window, ++ .get_line_length = isif_get_line_length, ++ .setfbaddr = isif_setfbaddr, ++ .getfid = isif_getfid, ++ }, ++}; ++ ++static int __devinit isif_probe(struct platform_device *pdev) ++{ ++ void (*setup_pinmux)(void); ++ struct resource *res; ++ void *__iomem addr; ++ int status = 0, i; ++ ++ /* ++ * first try to register with vpfe. If not correct platform, then we ++ * don't have to iomap ++ */ ++ status = vpfe_register_ccdc_device(&isif_hw_dev); ++ if (status < 0) ++ return status; ++ ++ /* Get and enable Master clock */ ++ isif_cfg.mclk = clk_get(&pdev->dev, "master"); ++ if (IS_ERR(isif_cfg.mclk)) { ++ status = PTR_ERR(isif_cfg.mclk); ++ goto fail_mclk; ++ } ++ if (clk_prepare_enable(isif_cfg.mclk)) { ++ status = -ENODEV; ++ goto fail_mclk; ++ } ++ ++ /* Platform data holds setup_pinmux function ptr */ ++ if (NULL == pdev->dev.platform_data) { ++ status = -ENODEV; ++ goto fail_mclk; ++ } ++ setup_pinmux = pdev->dev.platform_data; ++ /* ++ * setup Mux configuration for ccdc which may be different for ++ * different SoCs using this CCDC ++ */ ++ setup_pinmux(); ++ ++ i = 0; ++ /* Get the ISIF base address, linearization table0 and table1 addr. */ ++ while (i < 3) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ if (!res) { ++ status = -ENODEV; ++ goto fail_nobase_res; ++ } ++ res = request_mem_region(res->start, resource_size(res), ++ res->name); ++ if (!res) { ++ status = -EBUSY; ++ goto fail_nobase_res; ++ } ++ addr = ioremap_nocache(res->start, resource_size(res)); ++ if (!addr) { ++ status = -ENOMEM; ++ goto fail_base_iomap; ++ } ++ switch (i) { ++ case 0: ++ /* ISIF base address */ ++ isif_cfg.base_addr = addr; ++ break; ++ case 1: ++ /* ISIF linear tbl0 address */ ++ isif_cfg.linear_tbl0_addr = addr; ++ break; ++ default: ++ /* ISIF linear tbl0 address */ ++ isif_cfg.linear_tbl1_addr = addr; ++ break; ++ } ++ i++; ++ } ++ isif_cfg.dev = &pdev->dev; ++ ++ printk(KERN_NOTICE "%s is registered with vpfe.\n", ++ isif_hw_dev.name); ++ return 0; ++fail_base_iomap: ++ release_mem_region(res->start, resource_size(res)); ++ i--; ++fail_nobase_res: ++ if (isif_cfg.base_addr) ++ iounmap(isif_cfg.base_addr); ++ if (isif_cfg.linear_tbl0_addr) ++ iounmap(isif_cfg.linear_tbl0_addr); ++ ++ while (i >= 0) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ release_mem_region(res->start, resource_size(res)); ++ i--; ++ } ++fail_mclk: ++ clk_disable_unprepare(isif_cfg.mclk); ++ clk_put(isif_cfg.mclk); ++ vpfe_unregister_ccdc_device(&isif_hw_dev); ++ return status; ++} ++ ++static int isif_remove(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int i = 0; ++ ++ iounmap(isif_cfg.base_addr); ++ iounmap(isif_cfg.linear_tbl0_addr); ++ iounmap(isif_cfg.linear_tbl1_addr); ++ while (i < 3) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ if (res) ++ release_mem_region(res->start, resource_size(res)); ++ i++; ++ } ++ vpfe_unregister_ccdc_device(&isif_hw_dev); ++ clk_disable_unprepare(isif_cfg.mclk); ++ clk_put(isif_cfg.mclk); ++ return 0; ++} ++ ++static struct platform_driver isif_driver = { ++ .driver = { ++ .name = "isif", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __devexit_p(isif_remove), ++ .probe = isif_probe, ++}; ++ ++module_platform_driver(isif_driver); ++ ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/davinci/isif_regs.h +new file mode 100644 +index 0000000..aa69a46 +--- /dev/null ++++ b/drivers/media/platform/davinci/isif_regs.h +@@ -0,0 +1,269 @@ ++/* ++ * Copyright (C) 2008-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _ISIF_REGS_H ++#define _ISIF_REGS_H ++ ++/* ISIF registers relative offsets */ ++#define SYNCEN 0x00 ++#define MODESET 0x04 ++#define HDW 0x08 ++#define VDW 0x0c ++#define PPLN 0x10 ++#define LPFR 0x14 ++#define SPH 0x18 ++#define LNH 0x1c ++#define SLV0 0x20 ++#define SLV1 0x24 ++#define LNV 0x28 ++#define CULH 0x2c ++#define CULV 0x30 ++#define HSIZE 0x34 ++#define SDOFST 0x38 ++#define CADU 0x3c ++#define CADL 0x40 ++#define LINCFG0 0x44 ++#define LINCFG1 0x48 ++#define CCOLP 0x4c ++#define CRGAIN 0x50 ++#define CGRGAIN 0x54 ++#define CGBGAIN 0x58 ++#define CBGAIN 0x5c ++#define COFSTA 0x60 ++#define FLSHCFG0 0x64 ++#define FLSHCFG1 0x68 ++#define FLSHCFG2 0x6c ++#define VDINT0 0x70 ++#define VDINT1 0x74 ++#define VDINT2 0x78 ++#define MISC 0x7c ++#define CGAMMAWD 0x80 ++#define REC656IF 0x84 ++#define CCDCFG 0x88 ++/***************************************************** ++* Defect Correction registers ++*****************************************************/ ++#define DFCCTL 0x8c ++#define VDFSATLV 0x90 ++#define DFCMEMCTL 0x94 ++#define DFCMEM0 0x98 ++#define DFCMEM1 0x9c ++#define DFCMEM2 0xa0 ++#define DFCMEM3 0xa4 ++#define DFCMEM4 0xa8 ++/**************************************************** ++* Black Clamp registers ++****************************************************/ ++#define CLAMPCFG 0xac ++#define CLDCOFST 0xb0 ++#define CLSV 0xb4 ++#define CLHWIN0 0xb8 ++#define CLHWIN1 0xbc ++#define CLHWIN2 0xc0 ++#define CLVRV 0xc4 ++#define CLVWIN0 0xc8 ++#define CLVWIN1 0xcc ++#define CLVWIN2 0xd0 ++#define CLVWIN3 0xd4 ++/**************************************************** ++* Lense Shading Correction ++****************************************************/ ++#define DATAHOFST 0xd8 ++#define DATAVOFST 0xdc ++#define LSCHVAL 0xe0 ++#define LSCVVAL 0xe4 ++#define TWODLSCCFG 0xe8 ++#define TWODLSCOFST 0xec ++#define TWODLSCINI 0xf0 ++#define TWODLSCGRBU 0xf4 ++#define TWODLSCGRBL 0xf8 ++#define TWODLSCGROF 0xfc ++#define TWODLSCORBU 0x100 ++#define TWODLSCORBL 0x104 ++#define TWODLSCOROF 0x108 ++#define TWODLSCIRQEN 0x10c ++#define TWODLSCIRQST 0x110 ++/**************************************************** ++* Data formatter ++****************************************************/ ++#define FMTCFG 0x114 ++#define FMTPLEN 0x118 ++#define FMTSPH 0x11c ++#define FMTLNH 0x120 ++#define FMTSLV 0x124 ++#define FMTLNV 0x128 ++#define FMTRLEN 0x12c ++#define FMTHCNT 0x130 ++#define FMTAPTR_BASE 0x134 ++/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ ++#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) ++#define FMTPGMVF0 0x174 ++#define FMTPGMVF1 0x178 ++#define FMTPGMAPU0 0x17c ++#define FMTPGMAPU1 0x180 ++#define FMTPGMAPS0 0x184 ++#define FMTPGMAPS1 0x188 ++#define FMTPGMAPS2 0x18c ++#define FMTPGMAPS3 0x190 ++#define FMTPGMAPS4 0x194 ++#define FMTPGMAPS5 0x198 ++#define FMTPGMAPS6 0x19c ++#define FMTPGMAPS7 0x1a0 ++/************************************************ ++* Color Space Converter ++************************************************/ ++#define CSCCTL 0x1a4 ++#define CSCM0 0x1a8 ++#define CSCM1 0x1ac ++#define CSCM2 0x1b0 ++#define CSCM3 0x1b4 ++#define CSCM4 0x1b8 ++#define CSCM5 0x1bc ++#define CSCM6 0x1c0 ++#define CSCM7 0x1c4 ++#define OBWIN0 0x1c8 ++#define OBWIN1 0x1cc ++#define OBWIN2 0x1d0 ++#define OBWIN3 0x1d4 ++#define OBVAL0 0x1d8 ++#define OBVAL1 0x1dc ++#define OBVAL2 0x1e0 ++#define OBVAL3 0x1e4 ++#define OBVAL4 0x1e8 ++#define OBVAL5 0x1ec ++#define OBVAL6 0x1f0 ++#define OBVAL7 0x1f4 ++#define CLKCTL 0x1f8 ++ ++/* Masks & Shifts below */ ++#define START_PX_HOR_MASK 0x7FFF ++#define NUM_PX_HOR_MASK 0x7FFF ++#define START_VER_ONE_MASK 0x7FFF ++#define START_VER_TWO_MASK 0x7FFF ++#define NUM_LINES_VER 0x7FFF ++ ++/* gain - offset masks */ ++#define GAIN_INTEGER_SHIFT 9 ++#define OFFSET_MASK 0xFFF ++#define GAIN_SDRAM_EN_SHIFT 12 ++#define GAIN_IPIPE_EN_SHIFT 13 ++#define GAIN_H3A_EN_SHIFT 14 ++#define OFST_SDRAM_EN_SHIFT 8 ++#define OFST_IPIPE_EN_SHIFT 9 ++#define OFST_H3A_EN_SHIFT 10 ++#define GAIN_OFFSET_EN_MASK 0x7700 ++ ++/* Culling */ ++#define CULL_PAT_EVEN_LINE_SHIFT 8 ++ ++/* CCDCFG register */ ++#define ISIF_YCINSWP_RAW (0x00 << 4) ++#define ISIF_YCINSWP_YCBCR (0x01 << 4) ++#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) ++#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) ++#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) ++#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) ++#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) ++#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) ++#define ISIF_DATA_PACK_MASK 3 ++#define ISIF_DATA_PACK16 0 ++#define ISIF_DATA_PACK12 1 ++#define ISIF_DATA_PACK8 2 ++#define ISIF_PIX_ORDER_SHIFT 11 ++#define ISIF_BW656_ENABLE (0x01 << 5) ++ ++/* MODESET registers */ ++#define ISIF_VDHDOUT_INPUT (0x00 << 0) ++#define ISIF_INPUT_SHIFT 12 ++#define ISIF_RAW_INPUT_MODE 0 ++#define ISIF_FID_POL_SHIFT 4 ++#define ISIF_HD_POL_SHIFT 3 ++#define ISIF_VD_POL_SHIFT 2 ++#define ISIF_DATAPOL_NORMAL 0 ++#define ISIF_DATAPOL_SHIFT 6 ++#define ISIF_EXWEN_DISABLE 0 ++#define ISIF_EXWEN_SHIFT 5 ++#define ISIF_FRM_FMT_SHIFT 7 ++#define ISIF_DATASFT_SHIFT 8 ++#define ISIF_LPF_SHIFT 14 ++#define ISIF_LPF_MASK 1 ++ ++/* GAMMAWD registers */ ++#define ISIF_ALAW_GAMA_WD_MASK 0xF ++#define ISIF_ALAW_GAMA_WD_SHIFT 1 ++#define ISIF_ALAW_ENABLE 1 ++#define ISIF_GAMMAWD_CFA_SHIFT 5 ++ ++/* HSIZE registers */ ++#define ISIF_HSIZE_FLIP_MASK 1 ++#define ISIF_HSIZE_FLIP_SHIFT 12 ++ ++/* MISC registers */ ++#define ISIF_DPCM_EN_SHIFT 12 ++#define ISIF_DPCM_PREDICTOR_SHIFT 13 ++ ++/* Black clamp related */ ++#define ISIF_BC_MODE_COLOR_SHIFT 4 ++#define ISIF_HORZ_BC_MODE_SHIFT 1 ++#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 ++#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 ++#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 ++#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 ++#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 ++#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 ++ ++/* VDFC registers */ ++#define ISIF_VDFC_EN_SHIFT 4 ++#define ISIF_VDFC_CORR_MOD_SHIFT 5 ++#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 ++#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 ++#define ISIF_VDFC_POS_MASK 0x1FFF ++#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 ++ ++/* CSC registers */ ++#define ISIF_CSC_COEF_INTEG_MASK 7 ++#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f ++#define ISIF_CSC_COEF_INTEG_SHIFT 5 ++#define ISIF_CSCM_MSB_SHIFT 8 ++#define ISIF_DF_CSC_SPH_MASK 0x1FFF ++#define ISIF_DF_CSC_LNH_MASK 0x1FFF ++#define ISIF_DF_CSC_SLV_MASK 0x1FFF ++#define ISIF_DF_CSC_LNV_MASK 0x1FFF ++#define ISIF_DF_NUMLINES 0x7FFF ++#define ISIF_DF_NUMPIX 0x1FFF ++ ++/* Offsets for LSC/DFC/Gain */ ++#define ISIF_DATA_H_OFFSET_MASK 0x1FFF ++#define ISIF_DATA_V_OFFSET_MASK 0x1FFF ++ ++/* Linearization */ ++#define ISIF_LIN_CORRSFT_SHIFT 4 ++#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 ++ ++ ++/* Pattern registers */ ++#define ISIF_PG_EN (1 << 3) ++#define ISIF_SEL_PG_SRC (3 << 4) ++#define ISIF_PG_VD_POL_SHIFT 0 ++#define ISIF_PG_HD_POL_SHIFT 1 ++ ++/*random other junk*/ ++#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) ++#define ISIF_SYNCEN_WEN_MASK (1 << 1) ++#define ISIF_SYNCEN_WEN_SHIFT 1 ++ ++#endif +diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/davinci/vpbe.c +new file mode 100644 +index 0000000..fe2b9ce +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe.c +@@ -0,0 +1,870 @@ ++/* ++ * Copyright (C) 2010 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define VPBE_DEFAULT_OUTPUT "Composite" ++#define VPBE_DEFAULT_MODE "ntsc" ++ ++static char *def_output = VPBE_DEFAULT_OUTPUT; ++static char *def_mode = VPBE_DEFAULT_MODE; ++static int debug; ++ ++module_param(def_output, charp, S_IRUGO); ++module_param(def_mode, charp, S_IRUGO); ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)"); ++MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc"); ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++ ++MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Texas Instruments"); ++ ++/** ++ * vpbe_current_encoder_info - Get config info for current encoder ++ * @vpbe_dev - vpbe device ptr ++ * ++ * Return ptr to current encoder config info ++ */ ++static struct encoder_config_info* ++vpbe_current_encoder_info(struct vpbe_device *vpbe_dev) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int index = vpbe_dev->current_sd_index; ++ ++ return ((index == 0) ? &cfg->venc : ++ &cfg->ext_encoders[index-1]); ++} ++ ++/** ++ * vpbe_find_encoder_sd_index - Given a name find encoder sd index ++ * ++ * @vpbe_config - ptr to vpbe cfg ++ * @output_index - index used by application ++ * ++ * Return sd index of the encoder ++ */ ++static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg, ++ int index) ++{ ++ char *encoder_name = cfg->outputs[index].subdev_name; ++ int i; ++ ++ /* Venc is always first */ ++ if (!strcmp(encoder_name, cfg->venc.module_name)) ++ return 0; ++ ++ for (i = 0; i < cfg->num_ext_encoders; i++) { ++ if (!strcmp(encoder_name, ++ cfg->ext_encoders[i].module_name)) ++ return i+1; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * vpbe_g_cropcap - Get crop capabilities of the display ++ * @vpbe_dev - vpbe device ptr ++ * @cropcap - cropcap is a ptr to struct v4l2_cropcap ++ * ++ * Update the crop capabilities in crop cap for current ++ * mode ++ */ ++static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev, ++ struct v4l2_cropcap *cropcap) ++{ ++ if (NULL == cropcap) ++ return -EINVAL; ++ cropcap->bounds.left = 0; ++ cropcap->bounds.top = 0; ++ cropcap->bounds.width = vpbe_dev->current_timings.xres; ++ cropcap->bounds.height = vpbe_dev->current_timings.yres; ++ cropcap->defrect = cropcap->bounds; ++ ++ return 0; ++} ++ ++/** ++ * vpbe_enum_outputs - enumerate outputs ++ * @vpbe_dev - vpbe device ptr ++ * @output - ptr to v4l2_output structure ++ * ++ * Enumerates the outputs available at the vpbe display ++ * returns the status, -EINVAL if end of output list ++ */ ++static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev, ++ struct v4l2_output *output) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int temp_index = output->index; ++ ++ if (temp_index >= cfg->num_outputs) ++ return -EINVAL; ++ ++ *output = cfg->outputs[temp_index].output; ++ output->index = temp_index; ++ ++ return 0; ++} ++ ++static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode, ++ int output_index) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ struct vpbe_enc_mode_info var; ++ int curr_output = output_index; ++ int i; ++ ++ if (NULL == mode) ++ return -EINVAL; ++ ++ for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) { ++ var = cfg->outputs[curr_output].modes[i]; ++ if (!strcmp(mode, var.name)) { ++ vpbe_dev->current_timings = var; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev, ++ struct vpbe_enc_mode_info *mode_info) ++{ ++ if (NULL == mode_info) ++ return -EINVAL; ++ ++ *mode_info = vpbe_dev->current_timings; ++ ++ return 0; ++} ++ ++/* Get std by std id */ ++static int vpbe_get_std_info(struct vpbe_device *vpbe_dev, ++ v4l2_std_id std_id) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ struct vpbe_enc_mode_info var; ++ int curr_output = vpbe_dev->current_out_index; ++ int i; ++ ++ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { ++ var = cfg->outputs[curr_output].modes[i]; ++ if ((var.timings_type & VPBE_ENC_STD) && ++ (var.std_id & std_id)) { ++ vpbe_dev->current_timings = var; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev, ++ char *std_name) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ struct vpbe_enc_mode_info var; ++ int curr_output = vpbe_dev->current_out_index; ++ int i; ++ ++ for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { ++ var = cfg->outputs[curr_output].modes[i]; ++ if (!strcmp(var.name, std_name)) { ++ vpbe_dev->current_timings = var; ++ return 0; ++ } ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * vpbe_set_output - Set output ++ * @vpbe_dev - vpbe device ptr ++ * @index - index of output ++ * ++ * Set vpbe output to the output specified by the index ++ */ ++static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index) ++{ ++ struct encoder_config_info *curr_enc_info = ++ vpbe_current_encoder_info(vpbe_dev); ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ struct venc_platform_data *venc_device = vpbe_dev->venc_device; ++ enum v4l2_mbus_pixelcode if_params; ++ int enc_out_index; ++ int sd_index; ++ int ret = 0; ++ ++ if (index >= cfg->num_outputs) ++ return -EINVAL; ++ ++ mutex_lock(&vpbe_dev->lock); ++ ++ sd_index = vpbe_dev->current_sd_index; ++ enc_out_index = cfg->outputs[index].output.index; ++ /* ++ * Currently we switch the encoder based on output selected ++ * by the application. If media controller is implemented later ++ * there is will be an API added to setup_link between venc ++ * and external encoder. So in that case below comparison always ++ * match and encoder will not be switched. But if application ++ * chose not to use media controller, then this provides current ++ * way of switching encoder at the venc output. ++ */ ++ if (strcmp(curr_enc_info->module_name, ++ cfg->outputs[index].subdev_name)) { ++ /* Need to switch the encoder at the output */ ++ sd_index = vpbe_find_encoder_sd_index(cfg, index); ++ if (sd_index < 0) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if_params = cfg->outputs[index].if_params; ++ venc_device->setup_if_config(if_params); ++ if (ret) ++ goto out; ++ } ++ ++ /* Set output at the encoder */ ++ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, ++ s_routing, 0, enc_out_index, 0); ++ if (ret) ++ goto out; ++ ++ /* ++ * It is assumed that venc or extenal encoder will set a default ++ * mode in the sub device. For external encoder or LCD pannel output, ++ * we also need to set up the lcd port for the required mode. So setup ++ * the lcd port for the default mode that is configured in the board ++ * arch/arm/mach-davinci/board-dm355-evm.setup file for the external ++ * encoder. ++ */ ++ ret = vpbe_get_mode_info(vpbe_dev, ++ cfg->outputs[index].default_mode, index); ++ if (!ret) { ++ struct osd_state *osd_device = vpbe_dev->osd_device; ++ ++ osd_device->ops.set_left_margin(osd_device, ++ vpbe_dev->current_timings.left_margin); ++ osd_device->ops.set_top_margin(osd_device, ++ vpbe_dev->current_timings.upper_margin); ++ vpbe_dev->current_sd_index = sd_index; ++ vpbe_dev->current_out_index = index; ++ } ++out: ++ mutex_unlock(&vpbe_dev->lock); ++ return ret; ++} ++ ++static int vpbe_set_default_output(struct vpbe_device *vpbe_dev) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int ret = 0; ++ int i; ++ ++ for (i = 0; i < cfg->num_outputs; i++) { ++ if (!strcmp(def_output, ++ cfg->outputs[i].output.name)) { ++ ret = vpbe_set_output(vpbe_dev, i); ++ if (!ret) ++ vpbe_dev->current_out_index = i; ++ return ret; ++ } ++ } ++ return ret; ++} ++ ++/** ++ * vpbe_get_output - Get output ++ * @vpbe_dev - vpbe device ptr ++ * ++ * return current vpbe output to the the index ++ */ ++static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev) ++{ ++ return vpbe_dev->current_out_index; ++} ++ ++/** ++ * vpbe_s_dv_timings - Set the given preset timings in the encoder ++ * ++ * Sets the timings if supported by the current encoder. Return the status. ++ * 0 - success & -EINVAL on error ++ */ ++static int vpbe_s_dv_timings(struct vpbe_device *vpbe_dev, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int out_index = vpbe_dev->current_out_index; ++ struct vpbe_output *output = &cfg->outputs[out_index]; ++ int sd_index = vpbe_dev->current_sd_index; ++ int ret, i; ++ ++ ++ if (!(cfg->outputs[out_index].output.capabilities & ++ V4L2_OUT_CAP_DV_TIMINGS)) ++ return -EINVAL; ++ ++ for (i = 0; i < output->num_modes; i++) { ++ if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS && ++ !memcmp(&output->modes[i].dv_timings, ++ dv_timings, sizeof(*dv_timings))) ++ break; ++ } ++ if (i >= output->num_modes) ++ return -EINVAL; ++ vpbe_dev->current_timings = output->modes[i]; ++ mutex_lock(&vpbe_dev->lock); ++ ++ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, ++ s_dv_timings, dv_timings); ++ if (!ret && (vpbe_dev->amp != NULL)) { ++ /* Call amplifier subdevice */ ++ ret = v4l2_subdev_call(vpbe_dev->amp, video, ++ s_dv_timings, dv_timings); ++ } ++ /* set the lcd controller output for the given mode */ ++ if (!ret) { ++ struct osd_state *osd_device = vpbe_dev->osd_device; ++ ++ osd_device->ops.set_left_margin(osd_device, ++ vpbe_dev->current_timings.left_margin); ++ osd_device->ops.set_top_margin(osd_device, ++ vpbe_dev->current_timings.upper_margin); ++ } ++ mutex_unlock(&vpbe_dev->lock); ++ ++ return ret; ++} ++ ++/** ++ * vpbe_g_dv_timings - Get the timings in the current encoder ++ * ++ * Get the timings in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int vpbe_g_dv_timings(struct vpbe_device *vpbe_dev, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ if (vpbe_dev->current_timings.timings_type & ++ VPBE_ENC_CUSTOM_TIMINGS) { ++ *dv_timings = vpbe_dev->current_timings.dv_timings; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * vpbe_enum_dv_timings - Enumerate the dv timings in the current encoder ++ * ++ * Get the timings in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int vpbe_enum_dv_timings(struct vpbe_device *vpbe_dev, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int out_index = vpbe_dev->current_out_index; ++ struct vpbe_output *output = &cfg->outputs[out_index]; ++ int j = 0; ++ int i; ++ ++ if (!(output->output.capabilities & V4L2_OUT_CAP_DV_TIMINGS)) ++ return -EINVAL; ++ ++ for (i = 0; i < output->num_modes; i++) { ++ if (output->modes[i].timings_type == VPBE_ENC_CUSTOM_TIMINGS) { ++ if (j == timings->index) ++ break; ++ j++; ++ } ++ } ++ ++ if (i == output->num_modes) ++ return -EINVAL; ++ timings->timings = output->modes[i].dv_timings; ++ return 0; ++} ++ ++/** ++ * vpbe_s_std - Set the given standard in the encoder ++ * ++ * Sets the standard if supported by the current encoder. Return the status. ++ * 0 - success & -EINVAL on error ++ */ ++static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) ++{ ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ int out_index = vpbe_dev->current_out_index; ++ int sd_index = vpbe_dev->current_sd_index; ++ int ret; ++ ++ if (!(cfg->outputs[out_index].output.capabilities & ++ V4L2_OUT_CAP_STD)) ++ return -EINVAL; ++ ++ ret = vpbe_get_std_info(vpbe_dev, *std_id); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&vpbe_dev->lock); ++ ++ ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, ++ s_std_output, *std_id); ++ /* set the lcd controller output for the given mode */ ++ if (!ret) { ++ struct osd_state *osd_device = vpbe_dev->osd_device; ++ ++ osd_device->ops.set_left_margin(osd_device, ++ vpbe_dev->current_timings.left_margin); ++ osd_device->ops.set_top_margin(osd_device, ++ vpbe_dev->current_timings.upper_margin); ++ } ++ mutex_unlock(&vpbe_dev->lock); ++ ++ return ret; ++} ++ ++/** ++ * vpbe_g_std - Get the standard in the current encoder ++ * ++ * Get the standard in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) ++{ ++ struct vpbe_enc_mode_info *cur_timings = &vpbe_dev->current_timings; ++ ++ if (cur_timings->timings_type & VPBE_ENC_STD) { ++ *std_id = cur_timings->std_id; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * vpbe_set_mode - Set mode in the current encoder using mode info ++ * ++ * Use the mode string to decide what timings to set in the encoder ++ * This is typically useful when fbset command is used to change the current ++ * timings by specifying a string to indicate the timings. ++ */ ++static int vpbe_set_mode(struct vpbe_device *vpbe_dev, ++ struct vpbe_enc_mode_info *mode_info) ++{ ++ struct vpbe_enc_mode_info *preset_mode = NULL; ++ struct vpbe_config *cfg = vpbe_dev->cfg; ++ struct v4l2_dv_timings dv_timings; ++ struct osd_state *osd_device; ++ int out_index = vpbe_dev->current_out_index; ++ int ret = 0; ++ int i; ++ ++ if ((NULL == mode_info) || (NULL == mode_info->name)) ++ return -EINVAL; ++ ++ for (i = 0; i < cfg->outputs[out_index].num_modes; i++) { ++ if (!strcmp(mode_info->name, ++ cfg->outputs[out_index].modes[i].name)) { ++ preset_mode = &cfg->outputs[out_index].modes[i]; ++ /* ++ * it may be one of the 3 timings type. Check and ++ * invoke right API ++ */ ++ if (preset_mode->timings_type & VPBE_ENC_STD) ++ return vpbe_s_std(vpbe_dev, ++ &preset_mode->std_id); ++ if (preset_mode->timings_type & ++ VPBE_ENC_CUSTOM_TIMINGS) { ++ dv_timings = ++ preset_mode->dv_timings; ++ return vpbe_s_dv_timings(vpbe_dev, &dv_timings); ++ } ++ } ++ } ++ ++ /* Only custom timing should reach here */ ++ if (preset_mode == NULL) ++ return -EINVAL; ++ ++ mutex_lock(&vpbe_dev->lock); ++ ++ osd_device = vpbe_dev->osd_device; ++ vpbe_dev->current_timings = *preset_mode; ++ osd_device->ops.set_left_margin(osd_device, ++ vpbe_dev->current_timings.left_margin); ++ osd_device->ops.set_top_margin(osd_device, ++ vpbe_dev->current_timings.upper_margin); ++ ++ mutex_unlock(&vpbe_dev->lock); ++ ++ return ret; ++} ++ ++static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev) ++{ ++ int ret; ++ ++ ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode); ++ if (ret) ++ return ret; ++ ++ /* set the default mode in the encoder */ ++ return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings); ++} ++ ++static int platform_device_get(struct device *dev, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct vpbe_device *vpbe_dev = data; ++ ++ if (strstr(pdev->name, "vpbe-osd") != NULL) ++ vpbe_dev->osd_device = platform_get_drvdata(pdev); ++ if (strstr(pdev->name, "vpbe-venc") != NULL) ++ vpbe_dev->venc_device = dev_get_platdata(&pdev->dev); ++ ++ return 0; ++} ++ ++/** ++ * vpbe_initialize() - Initialize the vpbe display controller ++ * @vpbe_dev - vpbe device ptr ++ * ++ * Master frame buffer device drivers calls this to initialize vpbe ++ * display controller. This will then registers v4l2 device and the sub ++ * devices and sets a current encoder sub device for display. v4l2 display ++ * device driver is the master and frame buffer display device driver is ++ * the slave. Frame buffer display driver checks the initialized during ++ * probe and exit if not initialized. Returns status. ++ */ ++static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) ++{ ++ struct encoder_config_info *enc_info; ++ struct amp_config_info *amp_info; ++ struct v4l2_subdev **enc_subdev; ++ struct osd_state *osd_device; ++ struct i2c_adapter *i2c_adap; ++ int num_encoders; ++ int ret = 0; ++ int err; ++ int i; ++ ++ /* ++ * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer ++ * from the platform device by iteration of platform drivers and ++ * matching with device name ++ */ ++ if (NULL == vpbe_dev || NULL == dev) { ++ printk(KERN_ERR "Null device pointers.\n"); ++ return -ENODEV; ++ } ++ ++ if (vpbe_dev->initialized) ++ return 0; ++ ++ mutex_lock(&vpbe_dev->lock); ++ ++ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { ++ /* We have dac clock available for platform */ ++ vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac"); ++ if (IS_ERR(vpbe_dev->dac_clk)) { ++ ret = PTR_ERR(vpbe_dev->dac_clk); ++ goto fail_mutex_unlock; ++ } ++ if (clk_prepare_enable(vpbe_dev->dac_clk)) { ++ ret = -ENODEV; ++ goto fail_mutex_unlock; ++ } ++ } ++ ++ /* first enable vpss clocks */ ++ vpss_enable_clock(VPSS_VPBE_CLOCK, 1); ++ ++ /* First register a v4l2 device */ ++ ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev); ++ if (ret) { ++ v4l2_err(dev->driver, ++ "Unable to register v4l2 device.\n"); ++ goto fail_clk_put; ++ } ++ v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n"); ++ ++ err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev, ++ platform_device_get); ++ if (err < 0) ++ return err; ++ ++ vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev, ++ vpbe_dev->cfg->venc.module_name); ++ /* register venc sub device */ ++ if (vpbe_dev->venc == NULL) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "vpbe unable to init venc sub device\n"); ++ ret = -ENODEV; ++ goto fail_dev_unregister; ++ } ++ /* initialize osd device */ ++ osd_device = vpbe_dev->osd_device; ++ ++ if (NULL != osd_device->ops.initialize) { ++ err = osd_device->ops.initialize(osd_device); ++ if (err) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "unable to initialize the OSD device"); ++ err = -ENOMEM; ++ goto fail_dev_unregister; ++ } ++ } ++ ++ /* ++ * Register any external encoders that are configured. At index 0 we ++ * store venc sd index. ++ */ ++ num_encoders = vpbe_dev->cfg->num_ext_encoders + 1; ++ vpbe_dev->encoders = kmalloc( ++ sizeof(struct v4l2_subdev *)*num_encoders, ++ GFP_KERNEL); ++ if (NULL == vpbe_dev->encoders) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "unable to allocate memory for encoders sub devices"); ++ ret = -ENOMEM; ++ goto fail_dev_unregister; ++ } ++ ++ i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id); ++ for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) { ++ if (i == 0) { ++ /* venc is at index 0 */ ++ enc_subdev = &vpbe_dev->encoders[i]; ++ *enc_subdev = vpbe_dev->venc; ++ continue; ++ } ++ enc_info = &vpbe_dev->cfg->ext_encoders[i]; ++ if (enc_info->is_i2c) { ++ enc_subdev = &vpbe_dev->encoders[i]; ++ *enc_subdev = v4l2_i2c_new_subdev_board( ++ &vpbe_dev->v4l2_dev, i2c_adap, ++ &enc_info->board_info, NULL); ++ if (*enc_subdev) ++ v4l2_info(&vpbe_dev->v4l2_dev, ++ "v4l2 sub device %s registered\n", ++ enc_info->module_name); ++ else { ++ v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s" ++ " failed to register", ++ enc_info->module_name); ++ ret = -ENODEV; ++ goto fail_kfree_encoders; ++ } ++ } else ++ v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders" ++ " currently not supported"); ++ } ++ /* Add amplifier subdevice for dm365 */ ++ if ((strcmp(vpbe_dev->cfg->module_name, "dm365-vpbe-display") == 0) && ++ vpbe_dev->cfg->amp != NULL) { ++ amp_info = vpbe_dev->cfg->amp; ++ if (amp_info->is_i2c) { ++ vpbe_dev->amp = v4l2_i2c_new_subdev_board( ++ &vpbe_dev->v4l2_dev, i2c_adap, ++ &_info->board_info, NULL); ++ if (!vpbe_dev->amp) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "amplifier %s failed to register", ++ amp_info->module_name); ++ ret = -ENODEV; ++ goto fail_kfree_encoders; ++ } ++ v4l2_info(&vpbe_dev->v4l2_dev, ++ "v4l2 sub device %s registered\n", ++ amp_info->module_name); ++ } else { ++ vpbe_dev->amp = NULL; ++ v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c amplifiers" ++ " currently not supported"); ++ } ++ } else { ++ vpbe_dev->amp = NULL; ++ } ++ ++ /* set the current encoder and output to that of venc by default */ ++ vpbe_dev->current_sd_index = 0; ++ vpbe_dev->current_out_index = 0; ++ ++ mutex_unlock(&vpbe_dev->lock); ++ ++ printk(KERN_NOTICE "Setting default output to %s\n", def_output); ++ ret = vpbe_set_default_output(vpbe_dev); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s", ++ def_output); ++ return ret; ++ } ++ ++ printk(KERN_NOTICE "Setting default mode to %s\n", def_mode); ++ ret = vpbe_set_default_mode(vpbe_dev); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s", ++ def_mode); ++ return ret; ++ } ++ vpbe_dev->initialized = 1; ++ /* TBD handling of bootargs for default output and mode */ ++ return 0; ++ ++fail_kfree_encoders: ++ kfree(vpbe_dev->encoders); ++fail_dev_unregister: ++ v4l2_device_unregister(&vpbe_dev->v4l2_dev); ++fail_clk_put: ++ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { ++ clk_disable_unprepare(vpbe_dev->dac_clk); ++ clk_put(vpbe_dev->dac_clk); ++ } ++fail_mutex_unlock: ++ mutex_unlock(&vpbe_dev->lock); ++ return ret; ++} ++ ++/** ++ * vpbe_deinitialize() - de-initialize the vpbe display controller ++ * @dev - Master and slave device ptr ++ * ++ * vpbe_master and slave frame buffer devices calls this to de-initialize ++ * the display controller. It is called when master and slave device ++ * driver modules are removed and no longer requires the display controller. ++ */ ++static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev) ++{ ++ v4l2_device_unregister(&vpbe_dev->v4l2_dev); ++ if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { ++ clk_disable_unprepare(vpbe_dev->dac_clk); ++ clk_put(vpbe_dev->dac_clk); ++ } ++ ++ kfree(vpbe_dev->amp); ++ kfree(vpbe_dev->encoders); ++ vpbe_dev->initialized = 0; ++ /* disable vpss clocks */ ++ vpss_enable_clock(VPSS_VPBE_CLOCK, 0); ++} ++ ++static struct vpbe_device_ops vpbe_dev_ops = { ++ .g_cropcap = vpbe_g_cropcap, ++ .enum_outputs = vpbe_enum_outputs, ++ .set_output = vpbe_set_output, ++ .get_output = vpbe_get_output, ++ .s_dv_timings = vpbe_s_dv_timings, ++ .g_dv_timings = vpbe_g_dv_timings, ++ .enum_dv_timings = vpbe_enum_dv_timings, ++ .s_std = vpbe_s_std, ++ .g_std = vpbe_g_std, ++ .initialize = vpbe_initialize, ++ .deinitialize = vpbe_deinitialize, ++ .get_mode_info = vpbe_get_current_mode_info, ++ .set_mode = vpbe_set_mode, ++}; ++ ++static __devinit int vpbe_probe(struct platform_device *pdev) ++{ ++ struct vpbe_device *vpbe_dev; ++ struct vpbe_config *cfg; ++ int ret = -EINVAL; ++ ++ if (pdev->dev.platform_data == NULL) { ++ v4l2_err(pdev->dev.driver, "No platform data\n"); ++ return -ENODEV; ++ } ++ cfg = pdev->dev.platform_data; ++ ++ if (!cfg->module_name[0] || ++ !cfg->osd.module_name[0] || ++ !cfg->venc.module_name[0]) { ++ v4l2_err(pdev->dev.driver, "vpbe display module names not" ++ " defined\n"); ++ return ret; ++ } ++ ++ vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL); ++ if (vpbe_dev == NULL) { ++ v4l2_err(pdev->dev.driver, "Unable to allocate memory" ++ " for vpbe_device\n"); ++ return -ENOMEM; ++ } ++ vpbe_dev->cfg = cfg; ++ vpbe_dev->ops = vpbe_dev_ops; ++ vpbe_dev->pdev = &pdev->dev; ++ ++ if (cfg->outputs->num_modes > 0) ++ vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0]; ++ else { ++ kfree(vpbe_dev); ++ return -ENODEV; ++ } ++ ++ /* set the driver data in platform device */ ++ platform_set_drvdata(pdev, vpbe_dev); ++ mutex_init(&vpbe_dev->lock); ++ ++ return 0; ++} ++ ++static int vpbe_remove(struct platform_device *device) ++{ ++ struct vpbe_device *vpbe_dev = platform_get_drvdata(device); ++ ++ kfree(vpbe_dev); ++ ++ return 0; ++} ++ ++static struct platform_driver vpbe_driver = { ++ .driver = { ++ .name = "vpbe_controller", ++ .owner = THIS_MODULE, ++ }, ++ .probe = vpbe_probe, ++ .remove = vpbe_remove, ++}; ++ ++module_platform_driver(vpbe_driver); +diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c +new file mode 100644 +index 0000000..d078738 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe_display.c +@@ -0,0 +1,1895 @@ ++/* ++ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "vpbe_venc_regs.h" ++ ++#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" ++ ++static int debug; ++ ++#define VPBE_DEFAULT_NUM_BUFS 3 ++ ++module_param(debug, int, 0644); ++ ++static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, ++ struct vpbe_layer *layer); ++ ++static int venc_is_second_field(struct vpbe_display *disp_dev) ++{ ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ int ret; ++ int val; ++ ++ ret = v4l2_subdev_call(vpbe_dev->venc, ++ core, ++ ioctl, ++ VENC_GET_FLD, ++ &val); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in getting Field ID 0\n"); ++ } ++ return val; ++} ++ ++static void vpbe_isr_even_field(struct vpbe_display *disp_obj, ++ struct vpbe_layer *layer) ++{ ++ struct timespec timevalue; ++ ++ if (layer->cur_frm == layer->next_frm) ++ return; ++ ktime_get_ts(&timevalue); ++ layer->cur_frm->vb.v4l2_buf.timestamp.tv_sec = ++ timevalue.tv_sec; ++ layer->cur_frm->vb.v4l2_buf.timestamp.tv_usec = ++ timevalue.tv_nsec / NSEC_PER_USEC; ++ vb2_buffer_done(&layer->cur_frm->vb, VB2_BUF_STATE_DONE); ++ /* Make cur_frm pointing to next_frm */ ++ layer->cur_frm = layer->next_frm; ++} ++ ++static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, ++ struct vpbe_layer *layer) ++{ ++ struct osd_state *osd_device = disp_obj->osd_device; ++ unsigned long addr; ++ ++ spin_lock(&disp_obj->dma_queue_lock); ++ if (list_empty(&layer->dma_queue) || ++ (layer->cur_frm != layer->next_frm)) { ++ spin_unlock(&disp_obj->dma_queue_lock); ++ return; ++ } ++ /* ++ * one field is displayed configure ++ * the next frame if it is available ++ * otherwise hold on current frame ++ * Get next from the buffer queue ++ */ ++ layer->next_frm = list_entry(layer->dma_queue.next, ++ struct vpbe_disp_buffer, list); ++ /* Remove that from the buffer queue */ ++ list_del(&layer->next_frm->list); ++ spin_unlock(&disp_obj->dma_queue_lock); ++ /* Mark state of the frame to active */ ++ layer->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ addr = vb2_dma_contig_plane_dma_addr(&layer->next_frm->vb, 0); ++ osd_device->ops.start_layer(osd_device, ++ layer->layer_info.id, ++ addr, ++ disp_obj->cbcr_ofst); ++} ++ ++/* interrupt service routine */ ++static irqreturn_t venc_isr(int irq, void *arg) ++{ ++ struct vpbe_display *disp_dev = (struct vpbe_display *)arg; ++ struct vpbe_layer *layer; ++ static unsigned last_event; ++ unsigned event = 0; ++ int fid; ++ int i; ++ ++ if ((NULL == arg) || (NULL == disp_dev->dev[0])) ++ return IRQ_HANDLED; ++ ++ if (venc_is_second_field(disp_dev)) ++ event |= VENC_SECOND_FIELD; ++ else ++ event |= VENC_FIRST_FIELD; ++ ++ if (event == (last_event & ~VENC_END_OF_FRAME)) { ++ /* ++ * If the display is non-interlaced, then we need to flag the ++ * end-of-frame event at every interrupt regardless of the ++ * value of the FIDST bit. We can conclude that the display is ++ * non-interlaced if the value of the FIDST bit is unchanged ++ * from the previous interrupt. ++ */ ++ event |= VENC_END_OF_FRAME; ++ } else if (event == VENC_SECOND_FIELD) { ++ /* end-of-frame for interlaced display */ ++ event |= VENC_END_OF_FRAME; ++ } ++ last_event = event; ++ ++ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { ++ layer = disp_dev->dev[i]; ++ /* If streaming is started in this layer */ ++ if (!layer->started) ++ continue; ++ ++ if (layer->layer_first_int) { ++ layer->layer_first_int = 0; ++ continue; ++ } ++ /* Check the field format */ ++ if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && ++ (event & VENC_END_OF_FRAME)) { ++ /* Progressive mode */ ++ ++ vpbe_isr_even_field(disp_dev, layer); ++ vpbe_isr_odd_field(disp_dev, layer); ++ } else { ++ /* Interlaced mode */ ++ ++ layer->field_id ^= 1; ++ if (event & VENC_FIRST_FIELD) ++ fid = 0; ++ else ++ fid = 1; ++ ++ /* ++ * If field id does not match with store ++ * field id ++ */ ++ if (fid != layer->field_id) { ++ /* Make them in sync */ ++ layer->field_id = fid; ++ continue; ++ } ++ /* ++ * device field id and local field id are ++ * in sync. If this is even field ++ */ ++ if (0 == fid) ++ vpbe_isr_even_field(disp_dev, layer); ++ else /* odd field */ ++ vpbe_isr_odd_field(disp_dev, layer); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * vpbe_buffer_prepare() ++ * This is the callback function called from vb2_qbuf() function ++ * the buffer is prepared and user space virtual address is converted into ++ * physical address ++ */ ++static int vpbe_buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_queue *q = vb->vb2_queue; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ unsigned long addr; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "vpbe_buffer_prepare\n"); ++ ++ if (vb->state != VB2_BUF_STATE_ACTIVE && ++ vb->state != VB2_BUF_STATE_PREPARED) { ++ vb2_set_plane_payload(vb, 0, layer->pix_fmt.sizeimage); ++ if (vb2_plane_vaddr(vb, 0) && ++ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) ++ return -EINVAL; ++ ++ addr = vb2_dma_contig_plane_dma_addr(vb, 0); ++ if (q->streaming) { ++ if (!IS_ALIGNED(addr, 8)) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "buffer_prepare:offset is \ ++ not aligned to 32 bytes\n"); ++ return -EINVAL; ++ } ++ } ++ } ++ return 0; ++} ++ ++/* ++ * vpbe_buffer_setup() ++ * This function allocates memory for the buffers ++ */ ++static int ++vpbe_buffer_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++ ++{ ++ /* Get the file handle object and layer object */ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vq); ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); ++ ++ /* Store number of buffers allocated in numbuffer member */ ++ if (*nbuffers < VPBE_DEFAULT_NUM_BUFS) ++ *nbuffers = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; ++ ++ *nplanes = 1; ++ sizes[0] = layer->pix_fmt.sizeimage; ++ alloc_ctxs[0] = layer->alloc_ctx; ++ ++ return 0; ++} ++ ++/* ++ * vpbe_buffer_queue() ++ * This function adds the buffer to DMA queue ++ */ ++static void vpbe_buffer_queue(struct vb2_buffer *vb) ++{ ++ /* Get the file handle object and layer object */ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vpbe_disp_buffer *buf = container_of(vb, ++ struct vpbe_disp_buffer, vb); ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_display *disp = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ unsigned long flags; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "vpbe_buffer_queue\n"); ++ ++ /* add the buffer to the DMA queue */ ++ spin_lock_irqsave(&disp->dma_queue_lock, flags); ++ list_add_tail(&buf->list, &layer->dma_queue); ++ spin_unlock_irqrestore(&disp->dma_queue_lock, flags); ++} ++ ++/* ++ * vpbe_buf_cleanup() ++ * This function is called from the vb2 layer to free memory allocated to ++ * the buffers ++ */ ++static void vpbe_buf_cleanup(struct vb2_buffer *vb) ++{ ++ /* Get the file handle object and layer object */ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct vpbe_disp_buffer *buf = container_of(vb, ++ struct vpbe_disp_buffer, vb); ++ unsigned long flags; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "vpbe_buf_cleanup\n"); ++ ++ spin_lock_irqsave(&layer->irqlock, flags); ++ if (vb->state == VB2_BUF_STATE_ACTIVE) ++ list_del_init(&buf->list); ++ spin_unlock_irqrestore(&layer->irqlock, flags); ++} ++ ++static void vpbe_wait_prepare(struct vb2_queue *vq) ++{ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vq); ++ struct vpbe_layer *layer = fh->layer; ++ ++ mutex_unlock(&layer->opslock); ++} ++ ++static void vpbe_wait_finish(struct vb2_queue *vq) ++{ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vq); ++ struct vpbe_layer *layer = fh->layer; ++ ++ mutex_lock(&layer->opslock); ++} ++ ++static int vpbe_buffer_init(struct vb2_buffer *vb) ++{ ++ struct vpbe_disp_buffer *buf = container_of(vb, ++ struct vpbe_disp_buffer, vb); ++ ++ INIT_LIST_HEAD(&buf->list); ++ return 0; ++} ++ ++static int vpbe_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vq); ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ /* If buffer queue is empty, return error */ ++ if (list_empty(&layer->dma_queue)) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); ++ return -EINVAL; ++ } ++ /* Get the next frame from the buffer queue */ ++ layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, ++ struct vpbe_disp_buffer, list); ++ /* Remove buffer from the buffer queue */ ++ list_del(&layer->cur_frm->list); ++ /* Mark state of the current frame to active */ ++ layer->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ /* Initialize field_id and started member */ ++ layer->field_id = 0; ++ ++ /* Set parameters in OSD and VENC */ ++ ret = vpbe_set_osd_display_params(fh->disp_dev, layer); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * if request format is yuv420 semiplanar, need to ++ * enable both video windows ++ */ ++ layer->started = 1; ++ layer->layer_first_int = 1; ++ ++ return ret; ++} ++ ++static int vpbe_stop_streaming(struct vb2_queue *vq) ++{ ++ struct vpbe_fh *fh = vb2_get_drv_priv(vq); ++ struct vpbe_layer *layer = fh->layer; ++ ++ if (!vb2_is_streaming(vq)) ++ return 0; ++ ++ /* release all active buffers */ ++ while (!list_empty(&layer->dma_queue)) { ++ layer->next_frm = list_entry(layer->dma_queue.next, ++ struct vpbe_disp_buffer, list); ++ list_del(&layer->next_frm->list); ++ vb2_buffer_done(&layer->next_frm->vb, VB2_BUF_STATE_ERROR); ++ } ++ ++ return 0; ++} ++ ++static struct vb2_ops video_qops = { ++ .queue_setup = vpbe_buffer_queue_setup, ++ .wait_prepare = vpbe_wait_prepare, ++ .wait_finish = vpbe_wait_finish, ++ .buf_init = vpbe_buffer_init, ++ .buf_prepare = vpbe_buffer_prepare, ++ .start_streaming = vpbe_start_streaming, ++ .stop_streaming = vpbe_stop_streaming, ++ .buf_cleanup = vpbe_buf_cleanup, ++ .buf_queue = vpbe_buffer_queue, ++}; ++ ++static ++struct vpbe_layer* ++_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, ++ struct vpbe_layer *layer) ++{ ++ enum vpbe_display_device_id thiswin, otherwin; ++ thiswin = layer->device_id; ++ ++ otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ? ++ VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0; ++ return disp_dev->dev[otherwin]; ++} ++ ++static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, ++ struct vpbe_layer *layer) ++{ ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ unsigned long addr; ++ int ret; ++ ++ addr = vb2_dma_contig_plane_dma_addr(&layer->cur_frm->vb, 0); ++ /* Set address in the display registers */ ++ osd_device->ops.start_layer(osd_device, ++ layer->layer_info.id, ++ addr, ++ disp_dev->cbcr_ofst); ++ ++ ret = osd_device->ops.enable_layer(osd_device, ++ layer->layer_info.id, 0); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in enabling osd window layer 0\n"); ++ return -1; ++ } ++ ++ /* Enable the window */ ++ layer->layer_info.enable = 1; ++ if (cfg->pixfmt == PIXFMT_NV12) { ++ struct vpbe_layer *otherlayer = ++ _vpbe_display_get_other_win_layer(disp_dev, layer); ++ ++ ret = osd_device->ops.enable_layer(osd_device, ++ otherlayer->layer_info.id, 1); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in enabling osd window layer 1\n"); ++ return -1; ++ } ++ otherlayer->layer_info.enable = 1; ++ } ++ return 0; ++} ++ ++static void ++vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, ++ struct vpbe_layer *layer, ++ int expected_xsize, int expected_ysize) ++{ ++ struct display_layer_info *layer_info = &layer->layer_info; ++ struct v4l2_pix_format *pixfmt = &layer->pix_fmt; ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ int calculated_xsize; ++ int h_exp = 0; ++ int v_exp = 0; ++ int h_scale; ++ int v_scale; ++ ++ v4l2_std_id standard_id = vpbe_dev->current_timings.std_id; ++ ++ /* ++ * Application initially set the image format. Current display ++ * size is obtained from the vpbe display controller. expected_xsize ++ * and expected_ysize are set through S_CROP ioctl. Based on this, ++ * driver will calculate the scale factors for vertical and ++ * horizontal direction so that the image is displayed scaled ++ * and expanded. Application uses expansion to display the image ++ * in a square pixel. Otherwise it is displayed using displays ++ * pixel aspect ratio.It is expected that application chooses ++ * the crop coordinates for cropped or scaled display. if crop ++ * size is less than the image size, it is displayed cropped or ++ * it is displayed scaled and/or expanded. ++ * ++ * to begin with, set the crop window same as expected. Later we ++ * will override with scaled window size ++ */ ++ ++ cfg->xsize = pixfmt->width; ++ cfg->ysize = pixfmt->height; ++ layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ ++ layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ ++ layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ ++ layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ ++ ++ if (pixfmt->width < expected_xsize) { ++ h_scale = vpbe_dev->current_timings.xres / pixfmt->width; ++ if (h_scale < 2) ++ h_scale = 1; ++ else if (h_scale >= 4) ++ h_scale = 4; ++ else ++ h_scale = 2; ++ cfg->xsize *= h_scale; ++ if (cfg->xsize < expected_xsize) { ++ if ((standard_id & V4L2_STD_525_60) || ++ (standard_id & V4L2_STD_625_50)) { ++ calculated_xsize = (cfg->xsize * ++ VPBE_DISPLAY_H_EXP_RATIO_N) / ++ VPBE_DISPLAY_H_EXP_RATIO_D; ++ if (calculated_xsize <= expected_xsize) { ++ h_exp = 1; ++ cfg->xsize = calculated_xsize; ++ } ++ } ++ } ++ if (h_scale == 2) ++ layer_info->h_zoom = ZOOM_X2; ++ else if (h_scale == 4) ++ layer_info->h_zoom = ZOOM_X4; ++ if (h_exp) ++ layer_info->h_exp = H_EXP_9_OVER_8; ++ } else { ++ /* no scaling, only cropping. Set display area to crop area */ ++ cfg->xsize = expected_xsize; ++ } ++ ++ if (pixfmt->height < expected_ysize) { ++ v_scale = expected_ysize / pixfmt->height; ++ if (v_scale < 2) ++ v_scale = 1; ++ else if (v_scale >= 4) ++ v_scale = 4; ++ else ++ v_scale = 2; ++ cfg->ysize *= v_scale; ++ if (cfg->ysize < expected_ysize) { ++ if ((standard_id & V4L2_STD_625_50)) { ++ calculated_xsize = (cfg->ysize * ++ VPBE_DISPLAY_V_EXP_RATIO_N) / ++ VPBE_DISPLAY_V_EXP_RATIO_D; ++ if (calculated_xsize <= expected_ysize) { ++ v_exp = 1; ++ cfg->ysize = calculated_xsize; ++ } ++ } ++ } ++ if (v_scale == 2) ++ layer_info->v_zoom = ZOOM_X2; ++ else if (v_scale == 4) ++ layer_info->v_zoom = ZOOM_X4; ++ if (v_exp) ++ layer_info->h_exp = V_EXP_6_OVER_5; ++ } else { ++ /* no scaling, only cropping. Set display area to crop area */ ++ cfg->ysize = expected_ysize; ++ } ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "crop display xsize = %d, ysize = %d\n", ++ cfg->xsize, cfg->ysize); ++} ++ ++static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, ++ struct vpbe_layer *layer, ++ int top, int left) ++{ ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ ++ cfg->xpos = min((unsigned int)left, ++ vpbe_dev->current_timings.xres - cfg->xsize); ++ cfg->ypos = min((unsigned int)top, ++ vpbe_dev->current_timings.yres - cfg->ysize); ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "new xpos = %d, ypos = %d\n", ++ cfg->xpos, cfg->ypos); ++} ++ ++static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, ++ struct v4l2_rect *c) ++{ ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ ++ if ((c->width == 0) || ++ ((c->width + c->left) > vpbe_dev->current_timings.xres)) ++ c->width = vpbe_dev->current_timings.xres - c->left; ++ ++ if ((c->height == 0) || ((c->height + c->top) > ++ vpbe_dev->current_timings.yres)) ++ c->height = vpbe_dev->current_timings.yres - c->top; ++ ++ /* window height must be even for interlaced display */ ++ if (vpbe_dev->current_timings.interlaced) ++ c->height &= (~0x01); ++ ++} ++ ++/** ++ * vpbe_try_format() ++ * If user application provides width and height, and have bytesperline set ++ * to zero, driver calculates bytesperline and sizeimage based on hardware ++ * limits. ++ */ ++static int vpbe_try_format(struct vpbe_display *disp_dev, ++ struct v4l2_pix_format *pixfmt, int check) ++{ ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ int min_height = 1; ++ int min_width = 32; ++ int max_height; ++ int max_width; ++ int bpp; ++ ++ if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && ++ (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) ++ /* choose default as V4L2_PIX_FMT_UYVY */ ++ pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; ++ ++ /* Check the field format */ ++ if ((pixfmt->field != V4L2_FIELD_INTERLACED) && ++ (pixfmt->field != V4L2_FIELD_NONE)) { ++ if (vpbe_dev->current_timings.interlaced) ++ pixfmt->field = V4L2_FIELD_INTERLACED; ++ else ++ pixfmt->field = V4L2_FIELD_NONE; ++ } ++ ++ if (pixfmt->field == V4L2_FIELD_INTERLACED) ++ min_height = 2; ++ ++ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) ++ bpp = 1; ++ else ++ bpp = 2; ++ ++ max_width = vpbe_dev->current_timings.xres; ++ max_height = vpbe_dev->current_timings.yres; ++ ++ min_width /= bpp; ++ ++ if (!pixfmt->width || (pixfmt->width < min_width) || ++ (pixfmt->width > max_width)) { ++ pixfmt->width = vpbe_dev->current_timings.xres; ++ } ++ ++ if (!pixfmt->height || (pixfmt->height < min_height) || ++ (pixfmt->height > max_height)) { ++ pixfmt->height = vpbe_dev->current_timings.yres; ++ } ++ ++ if (pixfmt->bytesperline < (pixfmt->width * bpp)) ++ pixfmt->bytesperline = pixfmt->width * bpp; ++ ++ /* Make the bytesperline 32 byte aligned */ ++ pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); ++ ++ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) ++ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + ++ (pixfmt->bytesperline * pixfmt->height >> 1); ++ else ++ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; ++ ++ return 0; ++} ++ ++static int vpbe_display_g_priority(struct file *file, void *priv, ++ enum v4l2_priority *p) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ ++ *p = v4l2_prio_max(&layer->prio); ++ ++ return 0; ++} ++ ++static int vpbe_display_s_priority(struct file *file, void *priv, ++ enum v4l2_priority p) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ int ret; ++ ++ ret = v4l2_prio_change(&layer->prio, &fh->prio, p); ++ ++ return ret; ++} ++ ++static int vpbe_display_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ cap->version = VPBE_DISPLAY_VERSION_CODE; ++ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ snprintf(cap->driver, sizeof(cap->driver), "%s", ++ dev_name(vpbe_dev->pdev)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ dev_name(vpbe_dev->pdev)); ++ strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); ++ ++ return 0; ++} ++ ++static int vpbe_display_s_crop(struct file *file, void *priv, ++ const struct v4l2_crop *crop) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_display *disp_dev = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ struct v4l2_rect rect = crop->c; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ ++ if (rect.top < 0) ++ rect.top = 0; ++ if (rect.left < 0) ++ rect.left = 0; ++ ++ vpbe_disp_check_window_params(disp_dev, &rect); ++ ++ osd_device->ops.get_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ ++ vpbe_disp_calculate_scale_factor(disp_dev, layer, ++ rect.width, ++ rect.height); ++ vpbe_disp_adj_position(disp_dev, layer, rect.top, ++ rect.left); ++ ret = osd_device->ops.set_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in set layer config:\n"); ++ return -EINVAL; ++ } ++ ++ /* apply zooming and h or v expansion */ ++ osd_device->ops.set_zoom(osd_device, ++ layer->layer_info.id, ++ layer->layer_info.h_zoom, ++ layer->layer_info.v_zoom); ++ ret = osd_device->ops.set_vid_expansion(osd_device, ++ layer->layer_info.h_exp, ++ layer->layer_info.v_exp); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in set vid expansion:\n"); ++ return -EINVAL; ++ } ++ ++ if ((layer->layer_info.h_zoom != ZOOM_X1) || ++ (layer->layer_info.v_zoom != ZOOM_X1) || ++ (layer->layer_info.h_exp != H_EXP_OFF) || ++ (layer->layer_info.v_exp != V_EXP_OFF)) ++ /* Enable expansion filter */ ++ osd_device->ops.set_interpolation_filter(osd_device, 1); ++ else ++ osd_device->ops.set_interpolation_filter(osd_device, 0); ++ ++ return 0; ++} ++ ++static int vpbe_display_g_crop(struct file *file, void *priv, ++ struct v4l2_crop *crop) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct osd_state *osd_device = fh->disp_dev->osd_device; ++ struct v4l2_rect *rect = &crop->c; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_G_CROP, layer id = %d\n", ++ layer->device_id); ++ ++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ osd_device->ops.get_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ rect->top = cfg->ypos; ++ rect->left = cfg->xpos; ++ rect->width = cfg->xsize; ++ rect->height = cfg->ysize; ++ ++ return 0; ++} ++ ++static int vpbe_display_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *cropcap) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); ++ ++ cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ cropcap->bounds.left = 0; ++ cropcap->bounds.top = 0; ++ cropcap->bounds.width = vpbe_dev->current_timings.xres; ++ cropcap->bounds.height = vpbe_dev->current_timings.yres; ++ cropcap->pixelaspect = vpbe_dev->current_timings.aspect; ++ cropcap->defrect = cropcap->bounds; ++ return 0; ++} ++ ++static int vpbe_display_g_fmt(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_G_FMT, layer id = %d\n", ++ layer->device_id); ++ ++ /* If buffer type is video output */ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); ++ return -EINVAL; ++ } ++ /* Fill in the information about format */ ++ fmt->fmt.pix = layer->pix_fmt; ++ ++ return 0; ++} ++ ++static int vpbe_display_enum_fmt(struct file *file, void *priv, ++ struct v4l2_fmtdesc *fmt) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ unsigned int index = 0; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_ENUM_FMT, layer id = %d\n", ++ layer->device_id); ++ if (fmt->index > 1) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); ++ return -EINVAL; ++ } ++ ++ /* Fill in the information about format */ ++ index = fmt->index; ++ memset(fmt, 0, sizeof(*fmt)); ++ fmt->index = index; ++ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ if (index == 0) { ++ strcpy(fmt->description, "YUV 4:2:2 - UYVY"); ++ fmt->pixelformat = V4L2_PIX_FMT_UYVY; ++ } else { ++ strcpy(fmt->description, "Y/CbCr 4:2:0"); ++ fmt->pixelformat = V4L2_PIX_FMT_NV12; ++ } ++ ++ return 0; ++} ++ ++static int vpbe_display_s_fmt(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_display *disp_dev = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_S_FMT, layer id = %d\n", ++ layer->device_id); ++ ++ /* If streaming is started, return error */ ++ if (layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); ++ return -EINVAL; ++ } ++ /* Check for valid pixel format */ ++ ret = vpbe_try_format(disp_dev, pixfmt, 1); ++ if (ret) ++ return ret; ++ ++ /* YUV420 is requested, check availability of the ++ other video window */ ++ ++ layer->pix_fmt = *pixfmt; ++ ++ /* Get osd layer config */ ++ osd_device->ops.get_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ /* Store the pixel format in the layer object */ ++ cfg->xsize = pixfmt->width; ++ cfg->ysize = pixfmt->height; ++ cfg->line_length = pixfmt->bytesperline; ++ cfg->ypos = 0; ++ cfg->xpos = 0; ++ cfg->interlaced = vpbe_dev->current_timings.interlaced; ++ ++ if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) ++ cfg->pixfmt = PIXFMT_YCbCrI; ++ ++ /* Change of the default pixel format for both video windows */ ++ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { ++ struct vpbe_layer *otherlayer; ++ cfg->pixfmt = PIXFMT_NV12; ++ otherlayer = _vpbe_display_get_other_win_layer(disp_dev, ++ layer); ++ otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; ++ } ++ ++ /* Set the layer config in the osd window */ ++ ret = osd_device->ops.set_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ if (ret < 0) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Error in S_FMT params:\n"); ++ return -EINVAL; ++ } ++ ++ /* Readback and fill the local copy of current pix format */ ++ osd_device->ops.get_layer_config(osd_device, ++ layer->layer_info.id, cfg); ++ ++ return 0; ++} ++ ++static int vpbe_display_try_fmt(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_display *disp_dev = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); ++ return -EINVAL; ++ } ++ ++ /* Check for valid field format */ ++ return vpbe_try_format(disp_dev, pixfmt, 0); ++ ++} ++ ++/** ++ * vpbe_display_s_std - Set the given standard in the encoder ++ * ++ * Sets the standard if supported by the current encoder. Return the status. ++ * 0 - success & -EINVAL on error ++ */ ++static int vpbe_display_s_std(struct file *file, void *priv, ++ v4l2_std_id *std_id) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); ++ ++ /* If streaming is started, return error */ ++ if (layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ if (NULL != vpbe_dev->ops.s_std) { ++ ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Failed to set standard for sub devices\n"); ++ return -EINVAL; ++ } ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_g_std - Get the standard in the current encoder ++ * ++ * Get the standard in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int vpbe_display_g_std(struct file *file, void *priv, ++ v4l2_std_id *std_id) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); ++ ++ /* Get the standard from the current encoder */ ++ if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { ++ *std_id = vpbe_dev->current_timings.std_id; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++/** ++ * vpbe_display_enum_output - enumerate outputs ++ * ++ * Enumerates the outputs available at the vpbe display ++ * returns the status, -EINVAL if end of output list ++ */ ++static int vpbe_display_enum_output(struct file *file, void *priv, ++ struct v4l2_output *output) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); ++ ++ /* Enumerate outputs */ ++ ++ if (NULL == vpbe_dev->ops.enum_outputs) ++ return -EINVAL; ++ ++ ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); ++ if (ret) { ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "Failed to enumerate outputs\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_s_output - Set output to ++ * the output specified by the index ++ */ ++static int vpbe_display_s_output(struct file *file, void *priv, ++ unsigned int i) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); ++ /* If streaming is started, return error */ ++ if (layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ if (NULL == vpbe_dev->ops.set_output) ++ return -EINVAL; ++ ++ ret = vpbe_dev->ops.set_output(vpbe_dev, i); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Failed to set output for sub devices\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_g_output - Get output from subdevice ++ * for a given by the index ++ */ ++static int vpbe_display_g_output(struct file *file, void *priv, ++ unsigned int *i) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); ++ /* Get the standard from the current encoder */ ++ *i = vpbe_dev->current_out_index; ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_enum_dv_timings - Enumerate the dv timings ++ * ++ * enum the timings in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int ++vpbe_display_enum_dv_timings(struct file *file, void *priv, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_TIMINGS\n"); ++ ++ /* Enumerate outputs */ ++ if (NULL == vpbe_dev->ops.enum_dv_timings) ++ return -EINVAL; ++ ++ ret = vpbe_dev->ops.enum_dv_timings(vpbe_dev, timings); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Failed to enumerate dv timings info\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_s_dv_timings - Set the dv timings ++ * ++ * Set the timings in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int ++vpbe_display_s_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_TIMINGS\n"); ++ ++ ++ /* If streaming is started, return error */ ++ if (layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ ++ /* Set the given standard in the encoder */ ++ if (!vpbe_dev->ops.s_dv_timings) ++ return -EINVAL; ++ ++ ret = vpbe_dev->ops.s_dv_timings(vpbe_dev, timings); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Failed to set the dv timings info\n"); ++ return -EINVAL; ++ } ++ /* set the current norm to zero to be consistent. If STD is used ++ * v4l2 layer will set the norm properly on successful s_std call ++ */ ++ layer->video_dev.current_norm = 0; ++ ++ return 0; ++} ++ ++/** ++ * vpbe_display_g_dv_timings - Set the dv timings ++ * ++ * Get the timings in the current encoder. Return the status. 0 - success ++ * -EINVAL on error ++ */ ++static int ++vpbe_display_g_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ struct vpbe_fh *fh = priv; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_TIMINGS\n"); ++ ++ /* Get the given standard in the encoder */ ++ ++ if (vpbe_dev->current_timings.timings_type & ++ VPBE_ENC_CUSTOM_TIMINGS) { ++ *dv_timings = vpbe_dev->current_timings.dv_timings; ++ } else { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vpbe_display_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct osd_state *osd_device = fh->disp_dev->osd_device; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_STREAMOFF,layer id = %d\n", ++ layer->device_id); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ /* If io is allowed for this file handle, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); ++ return -EACCES; ++ } ++ ++ /* If streaming is not started, return error */ ++ if (!layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer" ++ " id = %d\n", layer->device_id); ++ return -EINVAL; ++ } ++ ++ osd_device->ops.disable_layer(osd_device, ++ layer->layer_info.id); ++ layer->started = 0; ++ ret = vb2_streamoff(&layer->buffer_queue, buf_type); ++ ++ return ret; ++} ++ ++static int vpbe_display_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_display *disp_dev = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ int ret; ++ ++ osd_device->ops.disable_layer(osd_device, ++ layer->layer_info.id); ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n", ++ layer->device_id); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ /* If file handle is not allowed IO, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); ++ return -EACCES; ++ } ++ /* If Streaming is already started, return error */ ++ if (layer->started) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n"); ++ return -EBUSY; ++ } ++ ++ /* ++ * Call vb2_streamon to start streaming ++ * in videobuf ++ */ ++ ret = vb2_streamon(&layer->buffer_queue, buf_type); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "error in vb2_streamon\n"); ++ return ret; ++ } ++ return ret; ++} ++ ++static int vpbe_display_dqbuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_DQBUF, layer id = %d\n", ++ layer->device_id); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ /* If this file handle is not allowed to do IO, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); ++ return -EACCES; ++ } ++ if (file->f_flags & O_NONBLOCK) ++ /* Call videobuf_dqbuf for non blocking mode */ ++ ret = vb2_dqbuf(&layer->buffer_queue, buf, 1); ++ else ++ /* Call videobuf_dqbuf for blocking mode */ ++ ret = vb2_dqbuf(&layer->buffer_queue, buf, 0); ++ ++ return ret; ++} ++ ++static int vpbe_display_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_QBUF, layer id = %d\n", ++ layer->device_id); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ /* If this file handle is not allowed to do IO, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); ++ return -EACCES; ++ } ++ ++ return vb2_qbuf(&layer->buffer_queue, p); ++} ++ ++static int vpbe_display_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "VIDIOC_QUERYBUF, layer id = %d\n", ++ layer->device_id); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ /* Call vb2_querybuf to get information */ ++ return vb2_querybuf(&layer->buffer_queue, buf); ++} ++ ++static int vpbe_display_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *req_buf) ++{ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ struct vb2_queue *q; ++ int ret; ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ /* If io users of the layer is not zero, return error */ ++ if (0 != layer->io_usrs) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n"); ++ return -EBUSY; ++ } ++ /* Initialize videobuf queue as per the buffer type */ ++ layer->alloc_ctx = vb2_dma_contig_init_ctx(vpbe_dev->pdev); ++ if (IS_ERR(layer->alloc_ctx)) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "Failed to get the context\n"); ++ return PTR_ERR(layer->alloc_ctx); ++ } ++ q = &layer->buffer_queue; ++ memset(q, 0, sizeof(*q)); ++ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ q->io_modes = VB2_MMAP | VB2_USERPTR; ++ q->drv_priv = fh; ++ q->ops = &video_qops; ++ q->mem_ops = &vb2_dma_contig_memops; ++ q->buf_struct_size = sizeof(struct vpbe_disp_buffer); ++ ++ ret = vb2_queue_init(q); ++ if (ret) { ++ v4l2_err(&vpbe_dev->v4l2_dev, "vb2_queue_init() failed\n"); ++ vb2_dma_contig_cleanup_ctx(layer->alloc_ctx); ++ return ret; ++ } ++ /* Set io allowed member of file handle to TRUE */ ++ fh->io_allowed = 1; ++ /* Increment io usrs member of layer object to 1 */ ++ layer->io_usrs = 1; ++ /* Store type of memory requested in layer object */ ++ layer->memory = req_buf->memory; ++ /* Initialize buffer queue */ ++ INIT_LIST_HEAD(&layer->dma_queue); ++ /* Allocate buffers */ ++ return vb2_reqbufs(q, req_buf); ++} ++ ++/* ++ * vpbe_display_mmap() ++ * It is used to map kernel space buffers into user spaces ++ */ ++static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma) ++{ ++ /* Get the layer object and file handle object */ ++ struct vpbe_fh *fh = filep->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n"); ++ ++ if (mutex_lock_interruptible(&layer->opslock)) ++ return -ERESTARTSYS; ++ ret = vb2_mmap(&layer->buffer_queue, vma); ++ mutex_unlock(&layer->opslock); ++ return ret; ++} ++ ++/* vpbe_display_poll(): It is used for select/poll system call ++ */ ++static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) ++{ ++ struct vpbe_fh *fh = filep->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ unsigned int err = 0; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n"); ++ if (layer->started) { ++ mutex_lock(&layer->opslock); ++ err = vb2_poll(&layer->buffer_queue, filep, wait); ++ mutex_unlock(&layer->opslock); ++ } ++ return err; ++} ++ ++/* ++ * vpbe_display_open() ++ * It creates object of file handle structure and stores it in private_data ++ * member of filepointer ++ */ ++static int vpbe_display_open(struct file *file) ++{ ++ struct vpbe_fh *fh = NULL; ++ struct vpbe_layer *layer = video_drvdata(file); ++ struct vpbe_display *disp_dev = layer->disp_dev; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ int err; ++ ++ /* Allocate memory for the file handle object */ ++ fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); ++ if (fh == NULL) { ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "unable to allocate memory for file handle object\n"); ++ return -ENOMEM; ++ } ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "vpbe display open plane = %d\n", ++ layer->device_id); ++ ++ /* store pointer to fh in private_data member of filep */ ++ file->private_data = fh; ++ fh->layer = layer; ++ fh->disp_dev = disp_dev; ++ ++ if (!layer->usrs) { ++ if (mutex_lock_interruptible(&layer->opslock)) ++ return -ERESTARTSYS; ++ /* First claim the layer for this device */ ++ err = osd_device->ops.request_layer(osd_device, ++ layer->layer_info.id); ++ mutex_unlock(&layer->opslock); ++ if (err < 0) { ++ /* Couldn't get layer */ ++ v4l2_err(&vpbe_dev->v4l2_dev, ++ "Display Manager failed to allocate layer\n"); ++ kfree(fh); ++ return -EINVAL; ++ } ++ } ++ /* Increment layer usrs counter */ ++ layer->usrs++; ++ /* Set io_allowed member to false */ ++ fh->io_allowed = 0; ++ /* Initialize priority of this instance to default priority */ ++ fh->prio = V4L2_PRIORITY_UNSET; ++ v4l2_prio_open(&layer->prio, &fh->prio); ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, ++ "vpbe display device opened successfully\n"); ++ return 0; ++} ++ ++/* ++ * vpbe_display_release() ++ * This function deletes buffer queue, frees the buffers and the davinci ++ * display file * handle ++ */ ++static int vpbe_display_release(struct file *file) ++{ ++ /* Get the layer object and file handle object */ ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_layer *layer = fh->layer; ++ struct osd_layer_config *cfg = &layer->layer_info.config; ++ struct vpbe_display *disp_dev = fh->disp_dev; ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ struct osd_state *osd_device = disp_dev->osd_device; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); ++ ++ mutex_lock(&layer->opslock); ++ /* if this instance is doing IO */ ++ if (fh->io_allowed) { ++ /* Reset io_usrs member of layer object */ ++ layer->io_usrs = 0; ++ ++ osd_device->ops.disable_layer(osd_device, ++ layer->layer_info.id); ++ layer->started = 0; ++ /* Free buffers allocated */ ++ vb2_queue_release(&layer->buffer_queue); ++ vb2_dma_contig_cleanup_ctx(&layer->buffer_queue); ++ } ++ ++ /* Decrement layer usrs counter */ ++ layer->usrs--; ++ /* If this file handle has initialize encoder device, reset it */ ++ if (!layer->usrs) { ++ if (cfg->pixfmt == PIXFMT_NV12) { ++ struct vpbe_layer *otherlayer; ++ otherlayer = ++ _vpbe_display_get_other_win_layer(disp_dev, layer); ++ osd_device->ops.disable_layer(osd_device, ++ otherlayer->layer_info.id); ++ osd_device->ops.release_layer(osd_device, ++ otherlayer->layer_info.id); ++ } ++ osd_device->ops.disable_layer(osd_device, ++ layer->layer_info.id); ++ osd_device->ops.release_layer(osd_device, ++ layer->layer_info.id); ++ } ++ /* Close the priority */ ++ v4l2_prio_close(&layer->prio, fh->prio); ++ file->private_data = NULL; ++ mutex_unlock(&layer->opslock); ++ ++ /* Free memory allocated to file handle object */ ++ kfree(fh); ++ ++ disp_dev->cbcr_ofst = 0; ++ ++ return 0; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int vpbe_display_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct v4l2_dbg_match *match = ®->match; ++ struct vpbe_fh *fh = file->private_data; ++ struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; ++ ++ if (match->type >= 2) { ++ v4l2_subdev_call(vpbe_dev->venc, ++ core, ++ g_register, ++ reg); ++ } ++ ++ return 0; ++} ++ ++static int vpbe_display_s_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ return 0; ++} ++#endif ++ ++/* vpbe capture ioctl operations */ ++static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { ++ .vidioc_querycap = vpbe_display_querycap, ++ .vidioc_g_fmt_vid_out = vpbe_display_g_fmt, ++ .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt, ++ .vidioc_s_fmt_vid_out = vpbe_display_s_fmt, ++ .vidioc_try_fmt_vid_out = vpbe_display_try_fmt, ++ .vidioc_reqbufs = vpbe_display_reqbufs, ++ .vidioc_querybuf = vpbe_display_querybuf, ++ .vidioc_qbuf = vpbe_display_qbuf, ++ .vidioc_dqbuf = vpbe_display_dqbuf, ++ .vidioc_streamon = vpbe_display_streamon, ++ .vidioc_streamoff = vpbe_display_streamoff, ++ .vidioc_cropcap = vpbe_display_cropcap, ++ .vidioc_g_crop = vpbe_display_g_crop, ++ .vidioc_s_crop = vpbe_display_s_crop, ++ .vidioc_g_priority = vpbe_display_g_priority, ++ .vidioc_s_priority = vpbe_display_s_priority, ++ .vidioc_s_std = vpbe_display_s_std, ++ .vidioc_g_std = vpbe_display_g_std, ++ .vidioc_enum_output = vpbe_display_enum_output, ++ .vidioc_s_output = vpbe_display_s_output, ++ .vidioc_g_output = vpbe_display_g_output, ++ .vidioc_s_dv_timings = vpbe_display_s_dv_timings, ++ .vidioc_g_dv_timings = vpbe_display_g_dv_timings, ++ .vidioc_enum_dv_timings = vpbe_display_enum_dv_timings, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vpbe_display_g_register, ++ .vidioc_s_register = vpbe_display_s_register, ++#endif ++}; ++ ++static struct v4l2_file_operations vpbe_fops = { ++ .owner = THIS_MODULE, ++ .open = vpbe_display_open, ++ .release = vpbe_display_release, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vpbe_display_mmap, ++ .poll = vpbe_display_poll ++}; ++ ++static int vpbe_device_get(struct device *dev, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct vpbe_display *vpbe_disp = data; ++ ++ if (strcmp("vpbe_controller", pdev->name) == 0) ++ vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); ++ ++ if (strstr(pdev->name, "vpbe-osd") != NULL) ++ vpbe_disp->osd_device = platform_get_drvdata(pdev); ++ ++ return 0; ++} ++ ++static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, ++ struct platform_device *pdev) ++{ ++ struct vpbe_layer *vpbe_display_layer = NULL; ++ struct video_device *vbd = NULL; ++ ++ /* Allocate memory for four plane display objects */ ++ ++ disp_dev->dev[i] = ++ kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL); ++ ++ /* If memory allocation fails, return error */ ++ if (!disp_dev->dev[i]) { ++ printk(KERN_ERR "ran out of memory\n"); ++ return -ENOMEM; ++ } ++ spin_lock_init(&disp_dev->dev[i]->irqlock); ++ mutex_init(&disp_dev->dev[i]->opslock); ++ ++ /* Get the pointer to the layer object */ ++ vpbe_display_layer = disp_dev->dev[i]; ++ vbd = &vpbe_display_layer->video_dev; ++ /* Initialize field of video device */ ++ vbd->release = video_device_release_empty; ++ vbd->fops = &vpbe_fops; ++ vbd->ioctl_ops = &vpbe_ioctl_ops; ++ vbd->minor = -1; ++ vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; ++ vbd->lock = &vpbe_display_layer->opslock; ++ vbd->vfl_dir = VFL_DIR_TX; ++ ++ if (disp_dev->vpbe_dev->current_timings.timings_type & ++ VPBE_ENC_STD) { ++ vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); ++ vbd->current_norm = ++ disp_dev->vpbe_dev->current_timings.std_id; ++ } else ++ vbd->current_norm = 0; ++ ++ snprintf(vbd->name, sizeof(vbd->name), ++ "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", ++ (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, ++ (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, ++ (VPBE_DISPLAY_VERSION_CODE) & 0xff); ++ ++ vpbe_display_layer->device_id = i; ++ ++ vpbe_display_layer->layer_info.id = ++ ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); ++ ++ /* Initialize prio member of layer object */ ++ v4l2_prio_init(&vpbe_display_layer->prio); ++ ++ return 0; ++} ++ ++static __devinit int register_device(struct vpbe_layer *vpbe_display_layer, ++ struct vpbe_display *disp_dev, ++ struct platform_device *pdev) { ++ int err; ++ ++ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, ++ "Trying to register VPBE display device.\n"); ++ v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, ++ "layer=%x,layer->video_dev=%x\n", ++ (int)vpbe_display_layer, ++ (int)&vpbe_display_layer->video_dev); ++ ++ err = video_register_device(&vpbe_display_layer->video_dev, ++ VFL_TYPE_GRABBER, ++ -1); ++ if (err) ++ return -ENODEV; ++ ++ vpbe_display_layer->disp_dev = disp_dev; ++ /* set the driver data in platform device */ ++ platform_set_drvdata(pdev, disp_dev); ++ video_set_drvdata(&vpbe_display_layer->video_dev, ++ vpbe_display_layer); ++ ++ return 0; ++} ++ ++ ++ ++/* ++ * vpbe_display_probe() ++ * This function creates device entries by register itself to the V4L2 driver ++ * and initializes fields of each layer objects ++ */ ++static __devinit int vpbe_display_probe(struct platform_device *pdev) ++{ ++ struct vpbe_layer *vpbe_display_layer; ++ struct vpbe_display *disp_dev; ++ struct resource *res = NULL; ++ int k; ++ int i; ++ int err; ++ int irq; ++ ++ printk(KERN_DEBUG "vpbe_display_probe\n"); ++ /* Allocate memory for vpbe_display */ ++ disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); ++ if (!disp_dev) { ++ printk(KERN_ERR "ran out of memory\n"); ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&disp_dev->dma_queue_lock); ++ /* ++ * Scan all the platform devices to find the vpbe ++ * controller device and get the vpbe_dev object ++ */ ++ err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev, ++ vpbe_device_get); ++ if (err < 0) ++ return err; ++ /* Initialize the vpbe display controller */ ++ if (NULL != disp_dev->vpbe_dev->ops.initialize) { ++ err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, ++ disp_dev->vpbe_dev); ++ if (err) { ++ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, ++ "Error initing vpbe\n"); ++ err = -ENOMEM; ++ goto probe_out; ++ } ++ } ++ ++ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { ++ if (init_vpbe_layer(i, disp_dev, pdev)) { ++ err = -ENODEV; ++ goto probe_out; ++ } ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res) { ++ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, ++ "Unable to get VENC interrupt resource\n"); ++ err = -ENODEV; ++ goto probe_out; ++ } ++ ++ irq = res->start; ++ if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, ++ disp_dev)) { ++ v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, ++ "Unable to request interrupt\n"); ++ err = -ENODEV; ++ goto probe_out; ++ } ++ ++ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { ++ if (register_device(disp_dev->dev[i], disp_dev, pdev)) { ++ err = -ENODEV; ++ goto probe_out_irq; ++ } ++ } ++ ++ printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); ++ return 0; ++ ++probe_out_irq: ++ free_irq(res->start, disp_dev); ++probe_out: ++ for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { ++ /* Get the pointer to the layer object */ ++ vpbe_display_layer = disp_dev->dev[k]; ++ /* Unregister video device */ ++ if (vpbe_display_layer) { ++ video_unregister_device( ++ &vpbe_display_layer->video_dev); ++ kfree(disp_dev->dev[k]); ++ } ++ } ++ kfree(disp_dev); ++ return err; ++} ++ ++/* ++ * vpbe_display_remove() ++ * It un-register hardware layer from V4L2 driver ++ */ ++static int vpbe_display_remove(struct platform_device *pdev) ++{ ++ struct vpbe_layer *vpbe_display_layer; ++ struct vpbe_display *disp_dev = platform_get_drvdata(pdev); ++ struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; ++ struct resource *res; ++ int i; ++ ++ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); ++ ++ /* unregister irq */ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ free_irq(res->start, disp_dev); ++ ++ /* deinitialize the vpbe display controller */ ++ if (NULL != vpbe_dev->ops.deinitialize) ++ vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); ++ /* un-register device */ ++ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { ++ /* Get the pointer to the layer object */ ++ vpbe_display_layer = disp_dev->dev[i]; ++ /* Unregister video device */ ++ video_unregister_device(&vpbe_display_layer->video_dev); ++ ++ } ++ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { ++ kfree(disp_dev->dev[i]); ++ disp_dev->dev[i] = NULL; ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver vpbe_display_driver = { ++ .driver = { ++ .name = VPBE_DISPLAY_DRIVER, ++ .owner = THIS_MODULE, ++ .bus = &platform_bus_type, ++ }, ++ .probe = vpbe_display_probe, ++ .remove = __devexit_p(vpbe_display_remove), ++}; ++ ++module_platform_driver(vpbe_display_driver); ++ ++MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Texas Instruments"); +diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c +new file mode 100644 +index 0000000..12ad17c +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe_osd.c +@@ -0,0 +1,1619 @@ ++/* ++ * Copyright (C) 2007-2010 Texas Instruments Inc ++ * Copyright (C) 2007 MontaVista Software, Inc. ++ * ++ * Andy Lowe (alowe@mvista.com), MontaVista Software ++ * - Initial version ++ * Murali Karicheri (mkaricheri@gmail.com), Texas Instruments Ltd. ++ * - ported to sub device interface ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include "vpbe_osd_regs.h" ++ ++#define MODULE_NAME "davinci-vpbe-osd" ++ ++static struct platform_device_id vpbe_osd_devtype[] = { ++ { ++ .name = DM644X_VPBE_OSD_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_1, ++ }, { ++ .name = DM365_VPBE_OSD_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_2, ++ }, { ++ .name = DM355_VPBE_OSD_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_3, ++ }, ++}; ++ ++MODULE_DEVICE_TABLE(platform, vpbe_osd_devtype); ++ ++/* register access routines */ ++static inline u32 osd_read(struct osd_state *sd, u32 offset) ++{ ++ struct osd_state *osd = sd; ++ ++ return readl(osd->osd_base + offset); ++} ++ ++static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) ++{ ++ struct osd_state *osd = sd; ++ ++ writel(val, osd->osd_base + offset); ++ ++ return val; ++} ++ ++static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset) ++{ ++ struct osd_state *osd = sd; ++ ++ void __iomem *addr = osd->osd_base + offset; ++ u32 val = readl(addr) | mask; ++ ++ writel(val, addr); ++ ++ return val; ++} ++ ++static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset) ++{ ++ struct osd_state *osd = sd; ++ ++ void __iomem *addr = osd->osd_base + offset; ++ u32 val = readl(addr) & ~mask; ++ ++ writel(val, addr); ++ ++ return val; ++} ++ ++static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, ++ u32 offset) ++{ ++ struct osd_state *osd = sd; ++ ++ void __iomem *addr = osd->osd_base + offset; ++ u32 new_val = (readl(addr) & ~mask) | (val & mask); ++ ++ writel(new_val, addr); ++ ++ return new_val; ++} ++ ++/* define some macros for layer and pixfmt classification */ ++#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1)) ++#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1)) ++#define is_rgb_pixfmt(pixfmt) \ ++ (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888)) ++#define is_yc_pixfmt(pixfmt) \ ++ (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \ ++ ((pixfmt) == PIXFMT_NV12)) ++#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X ++#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5) ++ ++/** ++ * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446 ++ * @sd - ptr to struct osd_state ++ * @field_inversion - inversion flag ++ * @fb_base_phys - frame buffer address ++ * @lconfig - ptr to layer config ++ * ++ * This routine implements a workaround for the field signal inversion silicon ++ * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and ++ * lconfig parameters apply to the vid0 window. This routine should be called ++ * whenever the vid0 layer configuration or start address is modified, or when ++ * the OSD field inversion setting is modified. ++ * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or ++ * 0 otherwise ++ */ ++static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, ++ int field_inversion, ++ unsigned long fb_base_phys, ++ const struct osd_layer_config *lconfig) ++{ ++ struct osd_platform_data *pdata; ++ ++ pdata = (struct osd_platform_data *)sd->dev->platform_data; ++ if (pdata != NULL && pdata->field_inv_wa_enable) { ++ ++ if (!field_inversion || !lconfig->interlaced) { ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR); ++ osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0, ++ OSD_MISCCTL); ++ return 0; ++ } else { ++ unsigned miscctl = OSD_MISCCTL_PPRV; ++ ++ osd_write(sd, ++ (fb_base_phys & ~0x1F) - lconfig->line_length, ++ OSD_VIDWIN0ADR); ++ osd_write(sd, ++ (fb_base_phys & ~0x1F) + lconfig->line_length, ++ OSD_PPVWIN0ADR); ++ osd_modify(sd, ++ OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl, ++ OSD_MISCCTL); ++ ++ return 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void _osd_set_field_inversion(struct osd_state *sd, int enable) ++{ ++ unsigned fsinv = 0; ++ ++ if (enable) ++ fsinv = OSD_MODE_FSINV; ++ ++ osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE); ++} ++ ++static void _osd_set_blink_attribute(struct osd_state *sd, int enable, ++ enum osd_blink_interval blink) ++{ ++ u32 osdatrmd = 0; ++ ++ if (enable) { ++ osdatrmd |= OSD_OSDATRMD_BLNK; ++ osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT; ++ } ++ /* caller must ensure that OSD1 is configured in attribute mode */ ++ osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd, ++ OSD_OSDATRMD); ++} ++ ++static void _osd_set_rom_clut(struct osd_state *sd, ++ enum osd_rom_clut rom_clut) ++{ ++ if (rom_clut == ROM_CLUT0) ++ osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); ++ else ++ osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); ++} ++ ++static void _osd_set_palette_map(struct osd_state *sd, ++ enum osd_win_layer osdwin, ++ unsigned char pixel_value, ++ unsigned char clut_index, ++ enum osd_pix_format pixfmt) ++{ ++ static const int map_2bpp[] = { 0, 5, 10, 15 }; ++ static const int map_1bpp[] = { 0, 15 }; ++ int bmp_offset; ++ int bmp_shift; ++ int bmp_mask; ++ int bmp_reg; ++ ++ switch (pixfmt) { ++ case PIXFMT_1BPP: ++ bmp_reg = map_1bpp[pixel_value & 0x1]; ++ break; ++ case PIXFMT_2BPP: ++ bmp_reg = map_2bpp[pixel_value & 0x3]; ++ break; ++ case PIXFMT_4BPP: ++ bmp_reg = pixel_value & 0xf; ++ break; ++ default: ++ return; ++ } ++ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32); ++ break; ++ case OSDWIN_OSD1: ++ bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32); ++ break; ++ default: ++ return; ++ } ++ ++ if (bmp_reg & 1) { ++ bmp_shift = 8; ++ bmp_mask = 0xff << 8; ++ } else { ++ bmp_shift = 0; ++ bmp_mask = 0xff; ++ } ++ ++ osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset); ++} ++ ++static void _osd_set_rec601_attenuation(struct osd_state *sd, ++ enum osd_win_layer osdwin, int enable) ++{ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_modify(sd, OSD_OSDWIN0MD_ATN0E, ++ enable ? OSD_OSDWIN0MD_ATN0E : 0, ++ OSD_OSDWIN0MD); ++ if (sd->vpbe_type == VPBE_VERSION_1) ++ osd_modify(sd, OSD_OSDWIN0MD_ATN0E, ++ enable ? OSD_OSDWIN0MD_ATN0E : 0, ++ OSD_OSDWIN0MD); ++ else if ((sd->vpbe_type == VPBE_VERSION_3) || ++ (sd->vpbe_type == VPBE_VERSION_2)) ++ osd_modify(sd, OSD_EXTMODE_ATNOSD0EN, ++ enable ? OSD_EXTMODE_ATNOSD0EN : 0, ++ OSD_EXTMODE); ++ break; ++ case OSDWIN_OSD1: ++ osd_modify(sd, OSD_OSDWIN1MD_ATN1E, ++ enable ? OSD_OSDWIN1MD_ATN1E : 0, ++ OSD_OSDWIN1MD); ++ if (sd->vpbe_type == VPBE_VERSION_1) ++ osd_modify(sd, OSD_OSDWIN1MD_ATN1E, ++ enable ? OSD_OSDWIN1MD_ATN1E : 0, ++ OSD_OSDWIN1MD); ++ else if ((sd->vpbe_type == VPBE_VERSION_3) || ++ (sd->vpbe_type == VPBE_VERSION_2)) ++ osd_modify(sd, OSD_EXTMODE_ATNOSD1EN, ++ enable ? OSD_EXTMODE_ATNOSD1EN : 0, ++ OSD_EXTMODE); ++ break; ++ } ++} ++ ++static void _osd_set_blending_factor(struct osd_state *sd, ++ enum osd_win_layer osdwin, ++ enum osd_blending_factor blend) ++{ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_modify(sd, OSD_OSDWIN0MD_BLND0, ++ blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD); ++ break; ++ case OSDWIN_OSD1: ++ osd_modify(sd, OSD_OSDWIN1MD_BLND1, ++ blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD); ++ break; ++ } ++} ++ ++static void _osd_enable_rgb888_pixblend(struct osd_state *sd, ++ enum osd_win_layer osdwin) ++{ ++ ++ osd_modify(sd, OSD_MISCCTL_BLDSEL, 0, OSD_MISCCTL); ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_modify(sd, OSD_EXTMODE_OSD0BLDCHR, ++ OSD_EXTMODE_OSD0BLDCHR, OSD_EXTMODE); ++ break; ++ case OSDWIN_OSD1: ++ osd_modify(sd, OSD_EXTMODE_OSD1BLDCHR, ++ OSD_EXTMODE_OSD1BLDCHR, OSD_EXTMODE); ++ break; ++ } ++} ++ ++static void _osd_enable_color_key(struct osd_state *sd, ++ enum osd_win_layer osdwin, ++ unsigned colorkey, ++ enum osd_pix_format pixfmt) ++{ ++ switch (pixfmt) { ++ case PIXFMT_1BPP: ++ case PIXFMT_2BPP: ++ case PIXFMT_4BPP: ++ case PIXFMT_8BPP: ++ if (sd->vpbe_type == VPBE_VERSION_3) { ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_modify(sd, OSD_TRANSPBMPIDX_BMP0, ++ colorkey << ++ OSD_TRANSPBMPIDX_BMP0_SHIFT, ++ OSD_TRANSPBMPIDX); ++ break; ++ case OSDWIN_OSD1: ++ osd_modify(sd, OSD_TRANSPBMPIDX_BMP1, ++ colorkey << ++ OSD_TRANSPBMPIDX_BMP1_SHIFT, ++ OSD_TRANSPBMPIDX); ++ break; ++ } ++ } ++ break; ++ case PIXFMT_RGB565: ++ if (sd->vpbe_type == VPBE_VERSION_1) ++ osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS, ++ OSD_TRANSPVAL); ++ else if (sd->vpbe_type == VPBE_VERSION_3) ++ osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL, ++ OSD_TRANSPVALL); ++ break; ++ case PIXFMT_YCbCrI: ++ case PIXFMT_YCrCbI: ++ if (sd->vpbe_type == VPBE_VERSION_3) ++ osd_modify(sd, OSD_TRANSPVALU_Y, colorkey, ++ OSD_TRANSPVALU); ++ break; ++ case PIXFMT_RGB888: ++ if (sd->vpbe_type == VPBE_VERSION_3) { ++ osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL, ++ OSD_TRANSPVALL); ++ osd_modify(sd, OSD_TRANSPVALU_RGBU, colorkey >> 16, ++ OSD_TRANSPVALU); ++ } ++ break; ++ default: ++ break; ++ } ++ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); ++ break; ++ case OSDWIN_OSD1: ++ osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); ++ break; ++ } ++} ++ ++static void _osd_disable_color_key(struct osd_state *sd, ++ enum osd_win_layer osdwin) ++{ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); ++ break; ++ case OSDWIN_OSD1: ++ osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); ++ break; ++ } ++} ++ ++static void _osd_set_osd_clut(struct osd_state *sd, ++ enum osd_win_layer osdwin, ++ enum osd_clut clut) ++{ ++ u32 winmd = 0; ++ ++ switch (osdwin) { ++ case OSDWIN_OSD0: ++ if (clut == RAM_CLUT) ++ winmd |= OSD_OSDWIN0MD_CLUTS0; ++ osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD); ++ break; ++ case OSDWIN_OSD1: ++ if (clut == RAM_CLUT) ++ winmd |= OSD_OSDWIN1MD_CLUTS1; ++ osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD); ++ break; ++ } ++} ++ ++static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer, ++ enum osd_zoom_factor h_zoom, ++ enum osd_zoom_factor v_zoom) ++{ ++ u32 winmd = 0; ++ ++ switch (layer) { ++ case WIN_OSD0: ++ winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT); ++ winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT); ++ osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd, ++ OSD_OSDWIN0MD); ++ break; ++ case WIN_VID0: ++ winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT); ++ winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT); ++ osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd, ++ OSD_VIDWINMD); ++ break; ++ case WIN_OSD1: ++ winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT); ++ winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT); ++ osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd, ++ OSD_OSDWIN1MD); ++ break; ++ case WIN_VID1: ++ winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT); ++ winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT); ++ osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd, ++ OSD_VIDWINMD); ++ break; ++ } ++} ++ ++static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ switch (layer) { ++ case WIN_OSD0: ++ osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); ++ break; ++ case WIN_VID0: ++ osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); ++ break; ++ case WIN_OSD1: ++ /* disable attribute mode as well as disabling the window */ ++ osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, ++ OSD_OSDWIN1MD); ++ break; ++ case WIN_VID1: ++ osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); ++ break; ++ } ++} ++ ++static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ if (!win->is_enabled) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return; ++ } ++ win->is_enabled = 0; ++ ++ _osd_disable_layer(sd, layer); ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++} ++ ++static void _osd_enable_attribute_mode(struct osd_state *sd) ++{ ++ /* enable attribute mode for OSD1 */ ++ osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD); ++} ++ ++static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ switch (layer) { ++ case WIN_OSD0: ++ osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); ++ break; ++ case WIN_VID0: ++ osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); ++ break; ++ case WIN_OSD1: ++ /* enable OSD1 and disable attribute mode */ ++ osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, ++ OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD); ++ break; ++ case WIN_VID1: ++ osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); ++ break; ++ } ++} ++ ++static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer, ++ int otherwin) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ struct osd_layer_config *cfg = &win->lconfig; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ /* ++ * use otherwin flag to know this is the other vid window ++ * in YUV420 mode, if is, skip this check ++ */ ++ if (!otherwin && (!win->is_allocated || ++ !win->fb_base_phys || ++ !cfg->line_length || ++ !cfg->xsize || ++ !cfg->ysize)) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return -1; ++ } ++ ++ if (win->is_enabled) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return 0; ++ } ++ win->is_enabled = 1; ++ ++ if (cfg->pixfmt != PIXFMT_OSD_ATTR) ++ _osd_enable_layer(sd, layer); ++ else { ++ _osd_enable_attribute_mode(sd); ++ _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink); ++ } ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++ ++ return 0; ++} ++ ++#define OSD_SRC_ADDR_HIGH4 0x7800000 ++#define OSD_SRC_ADDR_HIGH7 0x7F0000 ++#define OSD_SRCADD_OFSET_SFT 23 ++#define OSD_SRCADD_ADD_SFT 16 ++#define OSD_WINADL_MASK 0xFFFF ++#define OSD_WINOFST_MASK 0x1000 ++#define VPBE_REG_BASE 0x80000000 ++ ++static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer, ++ unsigned long fb_base_phys, ++ unsigned long cbcr_ofst) ++{ ++ ++ if (sd->vpbe_type == VPBE_VERSION_1) { ++ switch (layer) { ++ case WIN_OSD0: ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR); ++ break; ++ case WIN_VID0: ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); ++ break; ++ case WIN_OSD1: ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR); ++ break; ++ case WIN_VID1: ++ osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR); ++ break; ++ } ++ } else if (sd->vpbe_type == VPBE_VERSION_3) { ++ unsigned long fb_offset_32 = ++ (fb_base_phys - VPBE_REG_BASE) >> 5; ++ ++ switch (layer) { ++ case WIN_OSD0: ++ osd_modify(sd, OSD_OSDWINADH_O0AH, ++ fb_offset_32 >> (OSD_SRCADD_ADD_SFT - ++ OSD_OSDWINADH_O0AH_SHIFT), ++ OSD_OSDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_OSDWIN0ADL_O0AL, ++ OSD_OSDWIN0ADL); ++ break; ++ case WIN_VID0: ++ osd_modify(sd, OSD_VIDWINADH_V0AH, ++ fb_offset_32 >> (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V0AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_VIDWIN0ADL_V0AL, ++ OSD_VIDWIN0ADL); ++ break; ++ case WIN_OSD1: ++ osd_modify(sd, OSD_OSDWINADH_O1AH, ++ fb_offset_32 >> (OSD_SRCADD_ADD_SFT - ++ OSD_OSDWINADH_O1AH_SHIFT), ++ OSD_OSDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_OSDWIN1ADL_O1AL, ++ OSD_OSDWIN1ADL); ++ break; ++ case WIN_VID1: ++ osd_modify(sd, OSD_VIDWINADH_V1AH, ++ fb_offset_32 >> (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V1AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_VIDWIN1ADL_V1AL, ++ OSD_VIDWIN1ADL); ++ break; ++ } ++ } else if (sd->vpbe_type == VPBE_VERSION_2) { ++ struct osd_window_state *win = &sd->win[layer]; ++ unsigned long fb_offset_32, cbcr_offset_32; ++ ++ fb_offset_32 = fb_base_phys - VPBE_REG_BASE; ++ if (cbcr_ofst) ++ cbcr_offset_32 = cbcr_ofst; ++ else ++ cbcr_offset_32 = win->lconfig.line_length * ++ win->lconfig.ysize; ++ cbcr_offset_32 += fb_offset_32; ++ fb_offset_32 = fb_offset_32 >> 5; ++ cbcr_offset_32 = cbcr_offset_32 >> 5; ++ /* ++ * DM365: start address is 27-bit long address b26 - b23 are ++ * in offset register b12 - b9, and * bit 26 has to be '1' ++ */ ++ if (win->lconfig.pixfmt == PIXFMT_NV12) { ++ switch (layer) { ++ case WIN_VID0: ++ case WIN_VID1: ++ /* Y is in VID0 */ ++ osd_modify(sd, OSD_VIDWIN0OFST_V0AH, ++ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | ++ OSD_WINOFST_MASK, OSD_VIDWIN0OFST); ++ osd_modify(sd, OSD_VIDWINADH_V0AH, ++ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V0AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK, ++ OSD_VIDWIN0ADL); ++ /* CbCr is in VID1 */ ++ osd_modify(sd, OSD_VIDWIN1OFST_V1AH, ++ ((cbcr_offset_32 & ++ OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | ++ OSD_WINOFST_MASK, OSD_VIDWIN1OFST); ++ osd_modify(sd, OSD_VIDWINADH_V1AH, ++ (cbcr_offset_32 & ++ OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V1AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, cbcr_offset_32 & OSD_WINADL_MASK, ++ OSD_VIDWIN1ADL); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ switch (layer) { ++ case WIN_OSD0: ++ osd_modify(sd, OSD_OSDWIN0OFST_O0AH, ++ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK, ++ OSD_OSDWIN0OFST); ++ osd_modify(sd, OSD_OSDWINADH_O0AH, ++ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_OSDWINADH_O0AH_SHIFT), OSD_OSDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK, ++ OSD_OSDWIN0ADL); ++ break; ++ case WIN_VID0: ++ if (win->lconfig.pixfmt != PIXFMT_NV12) { ++ osd_modify(sd, OSD_VIDWIN0OFST_V0AH, ++ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | ++ OSD_WINOFST_MASK, OSD_VIDWIN0OFST); ++ osd_modify(sd, OSD_VIDWINADH_V0AH, ++ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V0AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK, ++ OSD_VIDWIN0ADL); ++ } ++ break; ++ case WIN_OSD1: ++ osd_modify(sd, OSD_OSDWIN1OFST_O1AH, ++ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | OSD_WINOFST_MASK, ++ OSD_OSDWIN1OFST); ++ osd_modify(sd, OSD_OSDWINADH_O1AH, ++ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_OSDWINADH_O1AH_SHIFT), ++ OSD_OSDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK, ++ OSD_OSDWIN1ADL); ++ break; ++ case WIN_VID1: ++ if (win->lconfig.pixfmt != PIXFMT_NV12) { ++ osd_modify(sd, OSD_VIDWIN1OFST_V1AH, ++ ((fb_offset_32 & OSD_SRC_ADDR_HIGH4) >> ++ (OSD_SRCADD_OFSET_SFT - ++ OSD_WINOFST_AH_SHIFT)) | ++ OSD_WINOFST_MASK, OSD_VIDWIN1OFST); ++ osd_modify(sd, OSD_VIDWINADH_V1AH, ++ (fb_offset_32 & OSD_SRC_ADDR_HIGH7) >> ++ (OSD_SRCADD_ADD_SFT - ++ OSD_VIDWINADH_V1AH_SHIFT), ++ OSD_VIDWINADH); ++ osd_write(sd, fb_offset_32 & OSD_WINADL_MASK, ++ OSD_VIDWIN1ADL); ++ } ++ break; ++ } ++ } ++} ++ ++static void osd_start_layer(struct osd_state *sd, enum osd_layer layer, ++ unsigned long fb_base_phys, ++ unsigned long cbcr_ofst) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ struct osd_layer_config *cfg = &win->lconfig; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ win->fb_base_phys = fb_base_phys & ~0x1F; ++ _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst); ++ ++ if (layer == WIN_VID0) { ++ osd->pingpong = ++ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, ++ win->fb_base_phys, ++ cfg); ++ } ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++} ++ ++static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer, ++ struct osd_layer_config *lconfig) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ *lconfig = win->lconfig; ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++} ++ ++/** ++ * try_layer_config() - Try a specific configuration for the layer ++ * @sd - ptr to struct osd_state ++ * @layer - layer to configure ++ * @lconfig - layer configuration to try ++ * ++ * If the requested lconfig is completely rejected and the value of lconfig on ++ * exit is the current lconfig, then try_layer_config() returns 1. Otherwise, ++ * try_layer_config() returns 0. A return value of 0 does not necessarily mean ++ * that the value of lconfig on exit is identical to the value of lconfig on ++ * entry, but merely that it represents a change from the current lconfig. ++ */ ++static int try_layer_config(struct osd_state *sd, enum osd_layer layer, ++ struct osd_layer_config *lconfig) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ int bad_config = 0; ++ ++ /* verify that the pixel format is compatible with the layer */ ++ switch (lconfig->pixfmt) { ++ case PIXFMT_1BPP: ++ case PIXFMT_2BPP: ++ case PIXFMT_4BPP: ++ case PIXFMT_8BPP: ++ case PIXFMT_RGB565: ++ if (osd->vpbe_type == VPBE_VERSION_1) ++ bad_config = !is_vid_win(layer); ++ break; ++ case PIXFMT_YCbCrI: ++ case PIXFMT_YCrCbI: ++ bad_config = !is_vid_win(layer); ++ break; ++ case PIXFMT_RGB888: ++ if (osd->vpbe_type == VPBE_VERSION_1) ++ bad_config = !is_vid_win(layer); ++ else if ((osd->vpbe_type == VPBE_VERSION_3) || ++ (osd->vpbe_type == VPBE_VERSION_2)) ++ bad_config = !is_osd_win(layer); ++ break; ++ case PIXFMT_NV12: ++ if (osd->vpbe_type != VPBE_VERSION_2) ++ bad_config = 1; ++ else ++ bad_config = is_osd_win(layer); ++ break; ++ case PIXFMT_OSD_ATTR: ++ bad_config = (layer != WIN_OSD1); ++ break; ++ default: ++ bad_config = 1; ++ break; ++ } ++ if (bad_config) { ++ /* ++ * The requested pixel format is incompatible with the layer, ++ * so keep the current layer configuration. ++ */ ++ *lconfig = win->lconfig; ++ return bad_config; ++ } ++ ++ /* DM6446: */ ++ /* only one OSD window at a time can use RGB pixel formats */ ++ if ((osd->vpbe_type == VPBE_VERSION_1) && ++ is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { ++ enum osd_pix_format pixfmt; ++ if (layer == WIN_OSD0) ++ pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt; ++ else ++ pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt; ++ ++ if (is_rgb_pixfmt(pixfmt)) { ++ /* ++ * The other OSD window is already configured for an ++ * RGB, so keep the current layer configuration. ++ */ ++ *lconfig = win->lconfig; ++ return 1; ++ } ++ } ++ ++ /* DM6446: only one video window at a time can use RGB888 */ ++ if ((osd->vpbe_type == VPBE_VERSION_1) && is_vid_win(layer) && ++ lconfig->pixfmt == PIXFMT_RGB888) { ++ enum osd_pix_format pixfmt; ++ ++ if (layer == WIN_VID0) ++ pixfmt = osd->win[WIN_VID1].lconfig.pixfmt; ++ else ++ pixfmt = osd->win[WIN_VID0].lconfig.pixfmt; ++ ++ if (pixfmt == PIXFMT_RGB888) { ++ /* ++ * The other video window is already configured for ++ * RGB888, so keep the current layer configuration. ++ */ ++ *lconfig = win->lconfig; ++ return 1; ++ } ++ } ++ ++ /* window dimensions must be non-zero */ ++ if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) { ++ *lconfig = win->lconfig; ++ return 1; ++ } ++ ++ /* round line_length up to a multiple of 32 */ ++ lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32; ++ lconfig->line_length = ++ min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH); ++ lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE); ++ lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE); ++ lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE); ++ lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE); ++ lconfig->interlaced = (lconfig->interlaced != 0); ++ if (lconfig->interlaced) { ++ /* ysize and ypos must be even for interlaced displays */ ++ lconfig->ysize &= ~1; ++ lconfig->ypos &= ~1; ++ } ++ ++ return 0; ++} ++ ++static void _osd_disable_vid_rgb888(struct osd_state *sd) ++{ ++ /* ++ * The DM6446 supports RGB888 pixel format in a single video window. ++ * This routine disables RGB888 pixel format for both video windows. ++ * The caller must ensure that neither video window is currently ++ * configured for RGB888 pixel format. ++ */ ++ if (sd->vpbe_type == VPBE_VERSION_1) ++ osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL); ++} ++ ++static void _osd_enable_vid_rgb888(struct osd_state *sd, ++ enum osd_layer layer) ++{ ++ /* ++ * The DM6446 supports RGB888 pixel format in a single video window. ++ * This routine enables RGB888 pixel format for the specified video ++ * window. The caller must ensure that the other video window is not ++ * currently configured for RGB888 pixel format, as this routine will ++ * disable RGB888 pixel format for the other window. ++ */ ++ if (sd->vpbe_type == VPBE_VERSION_1) { ++ if (layer == WIN_VID0) ++ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, ++ OSD_MISCCTL_RGBEN, OSD_MISCCTL); ++ else if (layer == WIN_VID1) ++ osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, ++ OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, ++ OSD_MISCCTL); ++ } ++} ++ ++static void _osd_set_cbcr_order(struct osd_state *sd, ++ enum osd_pix_format pixfmt) ++{ ++ /* ++ * The caller must ensure that all windows using YC pixfmt use the same ++ * Cb/Cr order. ++ */ ++ if (pixfmt == PIXFMT_YCbCrI) ++ osd_clear(sd, OSD_MODE_CS, OSD_MODE); ++ else if (pixfmt == PIXFMT_YCrCbI) ++ osd_set(sd, OSD_MODE_CS, OSD_MODE); ++} ++ ++static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, ++ const struct osd_layer_config *lconfig) ++{ ++ u32 winmd = 0, winmd_mask = 0, bmw = 0; ++ ++ _osd_set_cbcr_order(sd, lconfig->pixfmt); ++ ++ switch (layer) { ++ case WIN_OSD0: ++ if (sd->vpbe_type == VPBE_VERSION_1) { ++ winmd_mask |= OSD_OSDWIN0MD_RGB0E; ++ if (lconfig->pixfmt == PIXFMT_RGB565) ++ winmd |= OSD_OSDWIN0MD_RGB0E; ++ } else if ((sd->vpbe_type == VPBE_VERSION_3) || ++ (sd->vpbe_type == VPBE_VERSION_2)) { ++ winmd_mask |= OSD_OSDWIN0MD_BMP0MD; ++ switch (lconfig->pixfmt) { ++ case PIXFMT_RGB565: ++ winmd |= (1 << ++ OSD_OSDWIN0MD_BMP0MD_SHIFT); ++ break; ++ case PIXFMT_RGB888: ++ winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT); ++ _osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0); ++ break; ++ case PIXFMT_YCbCrI: ++ case PIXFMT_YCrCbI: ++ winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0; ++ ++ switch (lconfig->pixfmt) { ++ case PIXFMT_1BPP: ++ bmw = 0; ++ break; ++ case PIXFMT_2BPP: ++ bmw = 1; ++ break; ++ case PIXFMT_4BPP: ++ bmw = 2; ++ break; ++ case PIXFMT_8BPP: ++ bmw = 3; ++ break; ++ default: ++ break; ++ } ++ winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT); ++ ++ if (lconfig->interlaced) ++ winmd |= OSD_OSDWIN0MD_OFF0; ++ ++ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD); ++ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST); ++ osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP); ++ osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL); ++ if (lconfig->interlaced) { ++ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP); ++ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL); ++ } else { ++ osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP); ++ osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL); ++ } ++ break; ++ case WIN_VID0: ++ winmd_mask |= OSD_VIDWINMD_VFF0; ++ if (lconfig->interlaced) ++ winmd |= OSD_VIDWINMD_VFF0; ++ ++ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); ++ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST); ++ osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP); ++ osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL); ++ /* ++ * For YUV420P format the register contents are ++ * duplicated in both VID registers ++ */ ++ if ((sd->vpbe_type == VPBE_VERSION_2) && ++ (lconfig->pixfmt == PIXFMT_NV12)) { ++ /* other window also */ ++ if (lconfig->interlaced) { ++ winmd_mask |= OSD_VIDWINMD_VFF1; ++ winmd |= OSD_VIDWINMD_VFF1; ++ osd_modify(sd, winmd_mask, winmd, ++ OSD_VIDWINMD); ++ } ++ ++ osd_modify(sd, OSD_MISCCTL_S420D, ++ OSD_MISCCTL_S420D, OSD_MISCCTL); ++ osd_write(sd, lconfig->line_length >> 5, ++ OSD_VIDWIN1OFST); ++ osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP); ++ osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL); ++ /* ++ * if NV21 pixfmt and line length not 32B ++ * aligned (e.g. NTSC), Need to set window ++ * X pixel size to be 32B aligned as well ++ */ ++ if (lconfig->xsize % 32) { ++ osd_write(sd, ++ ((lconfig->xsize + 31) & ~31), ++ OSD_VIDWIN1XL); ++ osd_write(sd, ++ ((lconfig->xsize + 31) & ~31), ++ OSD_VIDWIN0XL); ++ } ++ } else if ((sd->vpbe_type == VPBE_VERSION_2) && ++ (lconfig->pixfmt != PIXFMT_NV12)) { ++ osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D, ++ OSD_MISCCTL); ++ } ++ ++ if (lconfig->interlaced) { ++ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP); ++ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL); ++ if ((sd->vpbe_type == VPBE_VERSION_2) && ++ lconfig->pixfmt == PIXFMT_NV12) { ++ osd_write(sd, lconfig->ypos >> 1, ++ OSD_VIDWIN1YP); ++ osd_write(sd, lconfig->ysize >> 1, ++ OSD_VIDWIN1YL); ++ } ++ } else { ++ osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP); ++ osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL); ++ if ((sd->vpbe_type == VPBE_VERSION_2) && ++ lconfig->pixfmt == PIXFMT_NV12) { ++ osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP); ++ osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL); ++ } ++ } ++ break; ++ case WIN_OSD1: ++ /* ++ * The caller must ensure that OSD1 is disabled prior to ++ * switching from a normal mode to attribute mode or from ++ * attribute mode to a normal mode. ++ */ ++ if (lconfig->pixfmt == PIXFMT_OSD_ATTR) { ++ if (sd->vpbe_type == VPBE_VERSION_1) { ++ winmd_mask |= OSD_OSDWIN1MD_ATN1E | ++ OSD_OSDWIN1MD_RGB1E | OSD_OSDWIN1MD_CLUTS1 | ++ OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1; ++ } else { ++ winmd_mask |= OSD_OSDWIN1MD_BMP1MD | ++ OSD_OSDWIN1MD_CLUTS1 | OSD_OSDWIN1MD_BLND1 | ++ OSD_OSDWIN1MD_TE1; ++ } ++ } else { ++ if (sd->vpbe_type == VPBE_VERSION_1) { ++ winmd_mask |= OSD_OSDWIN1MD_RGB1E; ++ if (lconfig->pixfmt == PIXFMT_RGB565) ++ winmd |= OSD_OSDWIN1MD_RGB1E; ++ } else if ((sd->vpbe_type == VPBE_VERSION_3) ++ || (sd->vpbe_type == VPBE_VERSION_2)) { ++ winmd_mask |= OSD_OSDWIN1MD_BMP1MD; ++ switch (lconfig->pixfmt) { ++ case PIXFMT_RGB565: ++ winmd |= ++ (1 << OSD_OSDWIN1MD_BMP1MD_SHIFT); ++ break; ++ case PIXFMT_RGB888: ++ winmd |= ++ (2 << OSD_OSDWIN1MD_BMP1MD_SHIFT); ++ _osd_enable_rgb888_pixblend(sd, ++ OSDWIN_OSD1); ++ break; ++ case PIXFMT_YCbCrI: ++ case PIXFMT_YCrCbI: ++ winmd |= ++ (3 << OSD_OSDWIN1MD_BMP1MD_SHIFT); ++ break; ++ default: ++ break; ++ } ++ } ++ ++ winmd_mask |= OSD_OSDWIN1MD_BMW1; ++ switch (lconfig->pixfmt) { ++ case PIXFMT_1BPP: ++ bmw = 0; ++ break; ++ case PIXFMT_2BPP: ++ bmw = 1; ++ break; ++ case PIXFMT_4BPP: ++ bmw = 2; ++ break; ++ case PIXFMT_8BPP: ++ bmw = 3; ++ break; ++ default: ++ break; ++ } ++ winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT); ++ } ++ ++ winmd_mask |= OSD_OSDWIN1MD_OFF1; ++ if (lconfig->interlaced) ++ winmd |= OSD_OSDWIN1MD_OFF1; ++ ++ osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD); ++ osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST); ++ osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP); ++ osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL); ++ if (lconfig->interlaced) { ++ osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP); ++ osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL); ++ } else { ++ osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP); ++ osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL); ++ } ++ break; ++ case WIN_VID1: ++ winmd_mask |= OSD_VIDWINMD_VFF1; ++ if (lconfig->interlaced) ++ winmd |= OSD_VIDWINMD_VFF1; ++ ++ osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); ++ osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST); ++ osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP); ++ osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL); ++ /* ++ * For YUV420P format the register contents are ++ * duplicated in both VID registers ++ */ ++ if (sd->vpbe_type == VPBE_VERSION_2) { ++ if (lconfig->pixfmt == PIXFMT_NV12) { ++ /* other window also */ ++ if (lconfig->interlaced) { ++ winmd_mask |= OSD_VIDWINMD_VFF0; ++ winmd |= OSD_VIDWINMD_VFF0; ++ osd_modify(sd, winmd_mask, winmd, ++ OSD_VIDWINMD); ++ } ++ osd_modify(sd, OSD_MISCCTL_S420D, ++ OSD_MISCCTL_S420D, OSD_MISCCTL); ++ osd_write(sd, lconfig->line_length >> 5, ++ OSD_VIDWIN0OFST); ++ osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP); ++ osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL); ++ } else { ++ osd_modify(sd, OSD_MISCCTL_S420D, ++ ~OSD_MISCCTL_S420D, OSD_MISCCTL); ++ } ++ } ++ ++ if (lconfig->interlaced) { ++ osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP); ++ osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL); ++ if ((sd->vpbe_type == VPBE_VERSION_2) && ++ lconfig->pixfmt == PIXFMT_NV12) { ++ osd_write(sd, lconfig->ypos >> 1, ++ OSD_VIDWIN0YP); ++ osd_write(sd, lconfig->ysize >> 1, ++ OSD_VIDWIN0YL); ++ } ++ } else { ++ osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP); ++ osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL); ++ if ((sd->vpbe_type == VPBE_VERSION_2) && ++ lconfig->pixfmt == PIXFMT_NV12) { ++ osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP); ++ osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL); ++ } ++ } ++ break; ++ } ++} ++ ++static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, ++ struct osd_layer_config *lconfig) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ struct osd_layer_config *cfg = &win->lconfig; ++ unsigned long flags; ++ int reject_config; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ reject_config = try_layer_config(sd, layer, lconfig); ++ if (reject_config) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return reject_config; ++ } ++ ++ /* update the current Cb/Cr order */ ++ if (is_yc_pixfmt(lconfig->pixfmt)) ++ osd->yc_pixfmt = lconfig->pixfmt; ++ ++ /* ++ * If we are switching OSD1 from normal mode to attribute mode or from ++ * attribute mode to normal mode, then we must disable the window. ++ */ ++ if (layer == WIN_OSD1) { ++ if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) && ++ (cfg->pixfmt != PIXFMT_OSD_ATTR)) || ++ ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && ++ (cfg->pixfmt == PIXFMT_OSD_ATTR))) { ++ win->is_enabled = 0; ++ _osd_disable_layer(sd, layer); ++ } ++ } ++ ++ _osd_set_layer_config(sd, layer, lconfig); ++ ++ if (layer == WIN_OSD1) { ++ struct osd_osdwin_state *osdwin_state = ++ &osd->osdwin[OSDWIN_OSD1]; ++ ++ if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && ++ (cfg->pixfmt == PIXFMT_OSD_ATTR)) { ++ /* ++ * We just switched OSD1 from attribute mode to normal ++ * mode, so we must initialize the CLUT select, the ++ * blend factor, transparency colorkey enable, and ++ * attenuation enable (DM6446 only) bits in the ++ * OSDWIN1MD register. ++ */ ++ _osd_set_osd_clut(sd, OSDWIN_OSD1, ++ osdwin_state->clut); ++ _osd_set_blending_factor(sd, OSDWIN_OSD1, ++ osdwin_state->blend); ++ if (osdwin_state->colorkey_blending) { ++ _osd_enable_color_key(sd, OSDWIN_OSD1, ++ osdwin_state-> ++ colorkey, ++ lconfig->pixfmt); ++ } else ++ _osd_disable_color_key(sd, OSDWIN_OSD1); ++ _osd_set_rec601_attenuation(sd, OSDWIN_OSD1, ++ osdwin_state-> ++ rec601_attenuation); ++ } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) && ++ (cfg->pixfmt != PIXFMT_OSD_ATTR)) { ++ /* ++ * We just switched OSD1 from normal mode to attribute ++ * mode, so we must initialize the blink enable and ++ * blink interval bits in the OSDATRMD register. ++ */ ++ _osd_set_blink_attribute(sd, osd->is_blinking, ++ osd->blink); ++ } ++ } ++ ++ /* ++ * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format ++ * then configure a default palette map. ++ */ ++ if ((lconfig->pixfmt != cfg->pixfmt) && ++ ((lconfig->pixfmt == PIXFMT_1BPP) || ++ (lconfig->pixfmt == PIXFMT_2BPP) || ++ (lconfig->pixfmt == PIXFMT_4BPP))) { ++ enum osd_win_layer osdwin = ++ ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1); ++ struct osd_osdwin_state *osdwin_state = ++ &osd->osdwin[osdwin]; ++ unsigned char clut_index; ++ unsigned char clut_entries = 0; ++ ++ switch (lconfig->pixfmt) { ++ case PIXFMT_1BPP: ++ clut_entries = 2; ++ break; ++ case PIXFMT_2BPP: ++ clut_entries = 4; ++ break; ++ case PIXFMT_4BPP: ++ clut_entries = 16; ++ break; ++ default: ++ break; ++ } ++ /* ++ * The default palette map maps the pixel value to the clut ++ * index, i.e. pixel value 0 maps to clut entry 0, pixel value ++ * 1 maps to clut entry 1, etc. ++ */ ++ for (clut_index = 0; clut_index < 16; clut_index++) { ++ osdwin_state->palette_map[clut_index] = clut_index; ++ if (clut_index < clut_entries) { ++ _osd_set_palette_map(sd, osdwin, clut_index, ++ clut_index, ++ lconfig->pixfmt); ++ } ++ } ++ } ++ ++ *cfg = *lconfig; ++ /* DM6446: configure the RGB888 enable and window selection */ ++ if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888) ++ _osd_enable_vid_rgb888(sd, WIN_VID0); ++ else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888) ++ _osd_enable_vid_rgb888(sd, WIN_VID1); ++ else ++ _osd_disable_vid_rgb888(sd); ++ ++ if (layer == WIN_VID0) { ++ osd->pingpong = ++ _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, ++ win->fb_base_phys, ++ cfg); ++ } ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++ ++ return 0; ++} ++ ++static void osd_init_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ enum osd_win_layer osdwin; ++ struct osd_osdwin_state *osdwin_state; ++ struct osd_layer_config *cfg = &win->lconfig; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ win->is_enabled = 0; ++ _osd_disable_layer(sd, layer); ++ ++ win->h_zoom = ZOOM_X1; ++ win->v_zoom = ZOOM_X1; ++ _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom); ++ ++ win->fb_base_phys = 0; ++ _osd_start_layer(sd, layer, win->fb_base_phys, 0); ++ ++ cfg->line_length = 0; ++ cfg->xsize = 0; ++ cfg->ysize = 0; ++ cfg->xpos = 0; ++ cfg->ypos = 0; ++ cfg->interlaced = 0; ++ switch (layer) { ++ case WIN_OSD0: ++ case WIN_OSD1: ++ osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1; ++ osdwin_state = &osd->osdwin[osdwin]; ++ /* ++ * Other code relies on the fact that OSD windows default to a ++ * bitmap pixel format when they are deallocated, so don't ++ * change this default pixel format. ++ */ ++ cfg->pixfmt = PIXFMT_8BPP; ++ _osd_set_layer_config(sd, layer, cfg); ++ osdwin_state->clut = RAM_CLUT; ++ _osd_set_osd_clut(sd, osdwin, osdwin_state->clut); ++ osdwin_state->colorkey_blending = 0; ++ _osd_disable_color_key(sd, osdwin); ++ osdwin_state->blend = OSD_8_VID_0; ++ _osd_set_blending_factor(sd, osdwin, osdwin_state->blend); ++ osdwin_state->rec601_attenuation = 0; ++ _osd_set_rec601_attenuation(sd, osdwin, ++ osdwin_state-> ++ rec601_attenuation); ++ if (osdwin == OSDWIN_OSD1) { ++ osd->is_blinking = 0; ++ osd->blink = BLINK_X1; ++ } ++ break; ++ case WIN_VID0: ++ case WIN_VID1: ++ cfg->pixfmt = osd->yc_pixfmt; ++ _osd_set_layer_config(sd, layer, cfg); ++ break; ++ } ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++} ++ ++static void osd_release_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ if (!win->is_allocated) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return; ++ } ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++ osd_init_layer(sd, layer); ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ win->is_allocated = 0; ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++} ++ ++static int osd_request_layer(struct osd_state *sd, enum osd_layer layer) ++{ ++ struct osd_state *osd = sd; ++ struct osd_window_state *win = &osd->win[layer]; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&osd->lock, flags); ++ ++ if (win->is_allocated) { ++ spin_unlock_irqrestore(&osd->lock, flags); ++ return -1; ++ } ++ win->is_allocated = 1; ++ ++ spin_unlock_irqrestore(&osd->lock, flags); ++ ++ return 0; ++} ++ ++static void _osd_init(struct osd_state *sd) ++{ ++ osd_write(sd, 0, OSD_MODE); ++ osd_write(sd, 0, OSD_VIDWINMD); ++ osd_write(sd, 0, OSD_OSDWIN0MD); ++ osd_write(sd, 0, OSD_OSDWIN1MD); ++ osd_write(sd, 0, OSD_RECTCUR); ++ osd_write(sd, 0, OSD_MISCCTL); ++ if (sd->vpbe_type == VPBE_VERSION_3) { ++ osd_write(sd, 0, OSD_VBNDRY); ++ osd_write(sd, 0, OSD_EXTMODE); ++ osd_write(sd, OSD_MISCCTL_DMANG, OSD_MISCCTL); ++ } ++} ++ ++static void osd_set_left_margin(struct osd_state *sd, u32 val) ++{ ++ osd_write(sd, val, OSD_BASEPX); ++} ++ ++static void osd_set_top_margin(struct osd_state *sd, u32 val) ++{ ++ osd_write(sd, val, OSD_BASEPY); ++} ++ ++static int osd_initialize(struct osd_state *osd) ++{ ++ if (osd == NULL) ++ return -ENODEV; ++ _osd_init(osd); ++ ++ /* set default Cb/Cr order */ ++ osd->yc_pixfmt = PIXFMT_YCbCrI; ++ ++ if (osd->vpbe_type == VPBE_VERSION_3) { ++ /* ++ * ROM CLUT1 on the DM355 is similar (identical?) to ROM CLUT0 ++ * on the DM6446, so make ROM_CLUT1 the default on the DM355. ++ */ ++ osd->rom_clut = ROM_CLUT1; ++ } ++ ++ _osd_set_field_inversion(osd, osd->field_inversion); ++ _osd_set_rom_clut(osd, osd->rom_clut); ++ ++ osd_init_layer(osd, WIN_OSD0); ++ osd_init_layer(osd, WIN_VID0); ++ osd_init_layer(osd, WIN_OSD1); ++ osd_init_layer(osd, WIN_VID1); ++ ++ return 0; ++} ++ ++static const struct vpbe_osd_ops osd_ops = { ++ .initialize = osd_initialize, ++ .request_layer = osd_request_layer, ++ .release_layer = osd_release_layer, ++ .enable_layer = osd_enable_layer, ++ .disable_layer = osd_disable_layer, ++ .set_layer_config = osd_set_layer_config, ++ .get_layer_config = osd_get_layer_config, ++ .start_layer = osd_start_layer, ++ .set_left_margin = osd_set_left_margin, ++ .set_top_margin = osd_set_top_margin, ++}; ++ ++static int osd_probe(struct platform_device *pdev) ++{ ++ const struct platform_device_id *pdev_id; ++ struct osd_state *osd; ++ struct resource *res; ++ int ret = 0; ++ ++ osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL); ++ if (osd == NULL) ++ return -ENOMEM; ++ ++ pdev_id = platform_get_device_id(pdev); ++ if (!pdev_id) { ++ ret = -EINVAL; ++ goto free_mem; ++ } ++ ++ osd->dev = &pdev->dev; ++ osd->vpbe_type = pdev_id->driver_data; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(osd->dev, "Unable to get OSD register address map\n"); ++ ret = -ENODEV; ++ goto free_mem; ++ } ++ osd->osd_base_phys = res->start; ++ osd->osd_size = resource_size(res); ++ if (!request_mem_region(osd->osd_base_phys, osd->osd_size, ++ MODULE_NAME)) { ++ dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); ++ ret = -ENODEV; ++ goto free_mem; ++ } ++ osd->osd_base = ioremap_nocache(res->start, osd->osd_size); ++ if (!osd->osd_base) { ++ dev_err(osd->dev, "Unable to map the OSD region\n"); ++ ret = -ENODEV; ++ goto release_mem_region; ++ } ++ spin_lock_init(&osd->lock); ++ osd->ops = osd_ops; ++ platform_set_drvdata(pdev, osd); ++ dev_notice(osd->dev, "OSD sub device probe success\n"); ++ return ret; ++ ++release_mem_region: ++ release_mem_region(osd->osd_base_phys, osd->osd_size); ++free_mem: ++ kfree(osd); ++ return ret; ++} ++ ++static int osd_remove(struct platform_device *pdev) ++{ ++ struct osd_state *osd = platform_get_drvdata(pdev); ++ ++ iounmap((void *)osd->osd_base); ++ release_mem_region(osd->osd_base_phys, osd->osd_size); ++ kfree(osd); ++ return 0; ++} ++ ++static struct platform_driver osd_driver = { ++ .probe = osd_probe, ++ .remove = osd_remove, ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .id_table = vpbe_osd_devtype ++}; ++ ++module_platform_driver(osd_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("DaVinci OSD Manager Driver"); ++MODULE_AUTHOR("Texas Instruments"); +diff --git a/drivers/media/platform/davinci/vpbe_osd_regs.h b/drivers/media/platform/davinci/vpbe_osd_regs.h +new file mode 100644 +index 0000000..584520f +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe_osd_regs.h +@@ -0,0 +1,364 @@ ++/* ++ * Copyright (C) 2006-2010 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _VPBE_OSD_REGS_H ++#define _VPBE_OSD_REGS_H ++ ++/* VPBE Global Registers */ ++#define VPBE_PID 0x0 ++#define VPBE_PCR 0x4 ++ ++/* VPSS CLock Registers */ ++#define VPSSCLK_PID 0x00 ++#define VPSSCLK_CLKCTRL 0x04 ++ ++/* VPSS Buffer Logic Registers */ ++#define VPSSBL_PID 0x00 ++#define VPSSBL_PCR 0x04 ++#define VPSSBL_BCR 0x08 ++#define VPSSBL_INTSTAT 0x0C ++#define VPSSBL_INTSEL 0x10 ++#define VPSSBL_EVTSEL 0x14 ++#define VPSSBL_MEMCTRL 0x18 ++#define VPSSBL_CCDCMUX 0x1C ++ ++/* DM365 ISP5 system configuration */ ++#define ISP5_PID 0x0 ++#define ISP5_PCCR 0x4 ++#define ISP5_BCR 0x8 ++#define ISP5_INTSTAT 0xC ++#define ISP5_INTSEL1 0x10 ++#define ISP5_INTSEL2 0x14 ++#define ISP5_INTSEL3 0x18 ++#define ISP5_EVTSEL 0x1c ++#define ISP5_CCDCMUX 0x20 ++ ++/* VPBE On-Screen Display Subsystem Registers (OSD) */ ++#define OSD_MODE 0x00 ++#define OSD_VIDWINMD 0x04 ++#define OSD_OSDWIN0MD 0x08 ++#define OSD_OSDWIN1MD 0x0C ++#define OSD_OSDATRMD 0x0C ++#define OSD_RECTCUR 0x10 ++#define OSD_VIDWIN0OFST 0x18 ++#define OSD_VIDWIN1OFST 0x1C ++#define OSD_OSDWIN0OFST 0x20 ++#define OSD_OSDWIN1OFST 0x24 ++#define OSD_VIDWINADH 0x28 ++#define OSD_VIDWIN0ADL 0x2C ++#define OSD_VIDWIN0ADR 0x2C ++#define OSD_VIDWIN1ADL 0x30 ++#define OSD_VIDWIN1ADR 0x30 ++#define OSD_OSDWINADH 0x34 ++#define OSD_OSDWIN0ADL 0x38 ++#define OSD_OSDWIN0ADR 0x38 ++#define OSD_OSDWIN1ADL 0x3C ++#define OSD_OSDWIN1ADR 0x3C ++#define OSD_BASEPX 0x40 ++#define OSD_BASEPY 0x44 ++#define OSD_VIDWIN0XP 0x48 ++#define OSD_VIDWIN0YP 0x4C ++#define OSD_VIDWIN0XL 0x50 ++#define OSD_VIDWIN0YL 0x54 ++#define OSD_VIDWIN1XP 0x58 ++#define OSD_VIDWIN1YP 0x5C ++#define OSD_VIDWIN1XL 0x60 ++#define OSD_VIDWIN1YL 0x64 ++#define OSD_OSDWIN0XP 0x68 ++#define OSD_OSDWIN0YP 0x6C ++#define OSD_OSDWIN0XL 0x70 ++#define OSD_OSDWIN0YL 0x74 ++#define OSD_OSDWIN1XP 0x78 ++#define OSD_OSDWIN1YP 0x7C ++#define OSD_OSDWIN1XL 0x80 ++#define OSD_OSDWIN1YL 0x84 ++#define OSD_CURXP 0x88 ++#define OSD_CURYP 0x8C ++#define OSD_CURXL 0x90 ++#define OSD_CURYL 0x94 ++#define OSD_W0BMP01 0xA0 ++#define OSD_W0BMP23 0xA4 ++#define OSD_W0BMP45 0xA8 ++#define OSD_W0BMP67 0xAC ++#define OSD_W0BMP89 0xB0 ++#define OSD_W0BMPAB 0xB4 ++#define OSD_W0BMPCD 0xB8 ++#define OSD_W0BMPEF 0xBC ++#define OSD_W1BMP01 0xC0 ++#define OSD_W1BMP23 0xC4 ++#define OSD_W1BMP45 0xC8 ++#define OSD_W1BMP67 0xCC ++#define OSD_W1BMP89 0xD0 ++#define OSD_W1BMPAB 0xD4 ++#define OSD_W1BMPCD 0xD8 ++#define OSD_W1BMPEF 0xDC ++#define OSD_VBNDRY 0xE0 ++#define OSD_EXTMODE 0xE4 ++#define OSD_MISCCTL 0xE8 ++#define OSD_CLUTRAMYCB 0xEC ++#define OSD_CLUTRAMCR 0xF0 ++#define OSD_TRANSPVAL 0xF4 ++#define OSD_TRANSPVALL 0xF4 ++#define OSD_TRANSPVALU 0xF8 ++#define OSD_TRANSPBMPIDX 0xFC ++#define OSD_PPVWIN0ADR 0xFC ++ ++/* bit definitions */ ++#define VPBE_PCR_VENC_DIV (1 << 1) ++#define VPBE_PCR_CLK_OFF (1 << 0) ++ ++#define VPSSBL_INTSTAT_HSSIINT (1 << 14) ++#define VPSSBL_INTSTAT_CFALDINT (1 << 13) ++#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12) ++#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11) ++#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10) ++#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9) ++#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8) ++#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7) ++#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6) ++#define VPSSBL_INTSTAT_OSDINT (1 << 5) ++#define VPSSBL_INTSTAT_VENCINT (1 << 4) ++#define VPSSBL_INTSTAT_H3AINT (1 << 3) ++#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2) ++#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1) ++#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0) ++ ++/* DM365 ISP5 bit definitions */ ++#define ISP5_INTSTAT_VENCINT (1 << 21) ++#define ISP5_INTSTAT_OSDINT (1 << 20) ++ ++/* VMOD TVTYP options for HDMD=0 */ ++#define SDTV_NTSC 0 ++#define SDTV_PAL 1 ++/* VMOD TVTYP options for HDMD=1 */ ++#define HDTV_525P 0 ++#define HDTV_625P 1 ++#define HDTV_1080I 2 ++#define HDTV_720P 3 ++ ++#define OSD_MODE_CS (1 << 15) ++#define OSD_MODE_OVRSZ (1 << 14) ++#define OSD_MODE_OHRSZ (1 << 13) ++#define OSD_MODE_EF (1 << 12) ++#define OSD_MODE_VVRSZ (1 << 11) ++#define OSD_MODE_VHRSZ (1 << 10) ++#define OSD_MODE_FSINV (1 << 9) ++#define OSD_MODE_BCLUT (1 << 8) ++#define OSD_MODE_CABG_SHIFT 0 ++#define OSD_MODE_CABG (0xff << 0) ++ ++#define OSD_VIDWINMD_VFINV (1 << 15) ++#define OSD_VIDWINMD_V1EFC (1 << 14) ++#define OSD_VIDWINMD_VHZ1_SHIFT 12 ++#define OSD_VIDWINMD_VHZ1 (3 << 12) ++#define OSD_VIDWINMD_VVZ1_SHIFT 10 ++#define OSD_VIDWINMD_VVZ1 (3 << 10) ++#define OSD_VIDWINMD_VFF1 (1 << 9) ++#define OSD_VIDWINMD_ACT1 (1 << 8) ++#define OSD_VIDWINMD_V0EFC (1 << 6) ++#define OSD_VIDWINMD_VHZ0_SHIFT 4 ++#define OSD_VIDWINMD_VHZ0 (3 << 4) ++#define OSD_VIDWINMD_VVZ0_SHIFT 2 ++#define OSD_VIDWINMD_VVZ0 (3 << 2) ++#define OSD_VIDWINMD_VFF0 (1 << 1) ++#define OSD_VIDWINMD_ACT0 (1 << 0) ++ ++#define OSD_OSDWIN0MD_ATN0E (1 << 14) ++#define OSD_OSDWIN0MD_RGB0E (1 << 13) ++#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13 ++#define OSD_OSDWIN0MD_BMP0MD (3 << 13) ++#define OSD_OSDWIN0MD_CLUTS0 (1 << 12) ++#define OSD_OSDWIN0MD_OHZ0_SHIFT 10 ++#define OSD_OSDWIN0MD_OHZ0 (3 << 10) ++#define OSD_OSDWIN0MD_OVZ0_SHIFT 8 ++#define OSD_OSDWIN0MD_OVZ0 (3 << 8) ++#define OSD_OSDWIN0MD_BMW0_SHIFT 6 ++#define OSD_OSDWIN0MD_BMW0 (3 << 6) ++#define OSD_OSDWIN0MD_BLND0_SHIFT 3 ++#define OSD_OSDWIN0MD_BLND0 (7 << 3) ++#define OSD_OSDWIN0MD_TE0 (1 << 2) ++#define OSD_OSDWIN0MD_OFF0 (1 << 1) ++#define OSD_OSDWIN0MD_OACT0 (1 << 0) ++ ++#define OSD_OSDWIN1MD_OASW (1 << 15) ++#define OSD_OSDWIN1MD_ATN1E (1 << 14) ++#define OSD_OSDWIN1MD_RGB1E (1 << 13) ++#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13 ++#define OSD_OSDWIN1MD_BMP1MD (3 << 13) ++#define OSD_OSDWIN1MD_CLUTS1 (1 << 12) ++#define OSD_OSDWIN1MD_OHZ1_SHIFT 10 ++#define OSD_OSDWIN1MD_OHZ1 (3 << 10) ++#define OSD_OSDWIN1MD_OVZ1_SHIFT 8 ++#define OSD_OSDWIN1MD_OVZ1 (3 << 8) ++#define OSD_OSDWIN1MD_BMW1_SHIFT 6 ++#define OSD_OSDWIN1MD_BMW1 (3 << 6) ++#define OSD_OSDWIN1MD_BLND1_SHIFT 3 ++#define OSD_OSDWIN1MD_BLND1 (7 << 3) ++#define OSD_OSDWIN1MD_TE1 (1 << 2) ++#define OSD_OSDWIN1MD_OFF1 (1 << 1) ++#define OSD_OSDWIN1MD_OACT1 (1 << 0) ++ ++#define OSD_OSDATRMD_OASW (1 << 15) ++#define OSD_OSDATRMD_OHZA_SHIFT 10 ++#define OSD_OSDATRMD_OHZA (3 << 10) ++#define OSD_OSDATRMD_OVZA_SHIFT 8 ++#define OSD_OSDATRMD_OVZA (3 << 8) ++#define OSD_OSDATRMD_BLNKINT_SHIFT 6 ++#define OSD_OSDATRMD_BLNKINT (3 << 6) ++#define OSD_OSDATRMD_OFFA (1 << 1) ++#define OSD_OSDATRMD_BLNK (1 << 0) ++ ++#define OSD_RECTCUR_RCAD_SHIFT 8 ++#define OSD_RECTCUR_RCAD (0xff << 8) ++#define OSD_RECTCUR_CLUTSR (1 << 7) ++#define OSD_RECTCUR_RCHW_SHIFT 4 ++#define OSD_RECTCUR_RCHW (7 << 4) ++#define OSD_RECTCUR_RCVW_SHIFT 1 ++#define OSD_RECTCUR_RCVW (7 << 1) ++#define OSD_RECTCUR_RCACT (1 << 0) ++ ++#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0) ++ ++#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0) ++ ++#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0) ++ ++#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0) ++ ++#define OSD_WINOFST_AH_SHIFT 9 ++ ++#define OSD_VIDWIN0OFST_V0AH (0xf << 9) ++#define OSD_VIDWIN1OFST_V1AH (0xf << 9) ++#define OSD_OSDWIN0OFST_O0AH (0xf << 9) ++#define OSD_OSDWIN1OFST_O1AH (0xf << 9) ++ ++#define OSD_VIDWINADH_V1AH_SHIFT 8 ++#define OSD_VIDWINADH_V1AH (0x7f << 8) ++#define OSD_VIDWINADH_V0AH_SHIFT 0 ++#define OSD_VIDWINADH_V0AH (0x7f << 0) ++ ++#define OSD_VIDWIN0ADL_V0AL (0xffff << 0) ++ ++#define OSD_VIDWIN1ADL_V1AL (0xffff << 0) ++ ++#define OSD_OSDWINADH_O1AH_SHIFT 8 ++#define OSD_OSDWINADH_O1AH (0x7f << 8) ++#define OSD_OSDWINADH_O0AH_SHIFT 0 ++#define OSD_OSDWINADH_O0AH (0x7f << 0) ++ ++#define OSD_OSDWIN0ADL_O0AL (0xffff << 0) ++ ++#define OSD_OSDWIN1ADL_O1AL (0xffff << 0) ++ ++#define OSD_BASEPX_BPX (0x3ff << 0) ++ ++#define OSD_BASEPY_BPY (0x1ff << 0) ++ ++#define OSD_VIDWIN0XP_V0X (0x7ff << 0) ++ ++#define OSD_VIDWIN0YP_V0Y (0x7ff << 0) ++ ++#define OSD_VIDWIN0XL_V0W (0x7ff << 0) ++ ++#define OSD_VIDWIN0YL_V0H (0x7ff << 0) ++ ++#define OSD_VIDWIN1XP_V1X (0x7ff << 0) ++ ++#define OSD_VIDWIN1YP_V1Y (0x7ff << 0) ++ ++#define OSD_VIDWIN1XL_V1W (0x7ff << 0) ++ ++#define OSD_VIDWIN1YL_V1H (0x7ff << 0) ++ ++#define OSD_OSDWIN0XP_W0X (0x7ff << 0) ++ ++#define OSD_OSDWIN0YP_W0Y (0x7ff << 0) ++ ++#define OSD_OSDWIN0XL_W0W (0x7ff << 0) ++ ++#define OSD_OSDWIN0YL_W0H (0x7ff << 0) ++ ++#define OSD_OSDWIN1XP_W1X (0x7ff << 0) ++ ++#define OSD_OSDWIN1YP_W1Y (0x7ff << 0) ++ ++#define OSD_OSDWIN1XL_W1W (0x7ff << 0) ++ ++#define OSD_OSDWIN1YL_W1H (0x7ff << 0) ++ ++#define OSD_CURXP_RCSX (0x7ff << 0) ++ ++#define OSD_CURYP_RCSY (0x7ff << 0) ++ ++#define OSD_CURXL_RCSW (0x7ff << 0) ++ ++#define OSD_CURYL_RCSH (0x7ff << 0) ++ ++#define OSD_EXTMODE_EXPMDSEL (1 << 15) ++#define OSD_EXTMODE_SCRNHEXP_SHIFT 13 ++#define OSD_EXTMODE_SCRNHEXP (3 << 13) ++#define OSD_EXTMODE_SCRNVEXP (1 << 12) ++#define OSD_EXTMODE_OSD1BLDCHR (1 << 11) ++#define OSD_EXTMODE_OSD0BLDCHR (1 << 10) ++#define OSD_EXTMODE_ATNOSD1EN (1 << 9) ++#define OSD_EXTMODE_ATNOSD0EN (1 << 8) ++#define OSD_EXTMODE_OSDHRSZ15 (1 << 7) ++#define OSD_EXTMODE_VIDHRSZ15 (1 << 6) ++#define OSD_EXTMODE_ZMFILV1HEN (1 << 5) ++#define OSD_EXTMODE_ZMFILV1VEN (1 << 4) ++#define OSD_EXTMODE_ZMFILV0HEN (1 << 3) ++#define OSD_EXTMODE_ZMFILV0VEN (1 << 2) ++#define OSD_EXTMODE_EXPFILHEN (1 << 1) ++#define OSD_EXTMODE_EXPFILVEN (1 << 0) ++ ++#define OSD_MISCCTL_BLDSEL (1 << 15) ++#define OSD_MISCCTL_S420D (1 << 14) ++#define OSD_MISCCTL_BMAPT (1 << 13) ++#define OSD_MISCCTL_DM365M (1 << 12) ++#define OSD_MISCCTL_RGBEN (1 << 7) ++#define OSD_MISCCTL_RGBWIN (1 << 6) ++#define OSD_MISCCTL_DMANG (1 << 6) ++#define OSD_MISCCTL_TMON (1 << 5) ++#define OSD_MISCCTL_RSEL (1 << 4) ++#define OSD_MISCCTL_CPBSY (1 << 3) ++#define OSD_MISCCTL_PPSW (1 << 2) ++#define OSD_MISCCTL_PPRV (1 << 1) ++ ++#define OSD_CLUTRAMYCB_Y_SHIFT 8 ++#define OSD_CLUTRAMYCB_Y (0xff << 8) ++#define OSD_CLUTRAMYCB_CB_SHIFT 0 ++#define OSD_CLUTRAMYCB_CB (0xff << 0) ++ ++#define OSD_CLUTRAMCR_CR_SHIFT 8 ++#define OSD_CLUTRAMCR_CR (0xff << 8) ++#define OSD_CLUTRAMCR_CADDR_SHIFT 0 ++#define OSD_CLUTRAMCR_CADDR (0xff << 0) ++ ++#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0) ++ ++#define OSD_TRANSPVALL_RGBL (0xffff << 0) ++ ++#define OSD_TRANSPVALU_Y_SHIFT 8 ++#define OSD_TRANSPVALU_Y (0xff << 8) ++#define OSD_TRANSPVALU_RGBU_SHIFT 0 ++#define OSD_TRANSPVALU_RGBU (0xff << 0) ++ ++#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8 ++#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8) ++#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0 ++#define OSD_TRANSPBMPIDX_BMP0 0xff ++ ++#endif /* _DAVINCI_VPBE_H_ */ +diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c +new file mode 100644 +index 0000000..bdbebd5 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe_venc.c +@@ -0,0 +1,730 @@ ++/* ++ * Copyright (C) 2010 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "vpbe_venc_regs.h" ++ ++#define MODULE_NAME "davinci-vpbe-venc" ++ ++static struct platform_device_id vpbe_venc_devtype[] = { ++ { ++ .name = DM644X_VPBE_VENC_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_1, ++ }, { ++ .name = DM365_VPBE_VENC_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_2, ++ }, { ++ .name = DM355_VPBE_VENC_SUBDEV_NAME, ++ .driver_data = VPBE_VERSION_3, ++ }, ++}; ++ ++MODULE_DEVICE_TABLE(platform, vpbe_venc_devtype); ++ ++static int debug = 2; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Debug level 0-2"); ++ ++struct venc_state { ++ struct v4l2_subdev sd; ++ struct venc_callback *callback; ++ struct venc_platform_data *pdata; ++ struct device *pdev; ++ u32 output; ++ v4l2_std_id std; ++ spinlock_t lock; ++ void __iomem *venc_base; ++ void __iomem *vdaccfg_reg; ++ enum vpbe_version venc_type; ++}; ++ ++static inline struct venc_state *to_state(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct venc_state, sd); ++} ++ ++static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset) ++{ ++ struct venc_state *venc = to_state(sd); ++ ++ return readl(venc->venc_base + offset); ++} ++ ++static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val) ++{ ++ struct venc_state *venc = to_state(sd); ++ ++ writel(val, (venc->venc_base + offset)); ++ ++ return val; ++} ++ ++static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset, ++ u32 val, u32 mask) ++{ ++ u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask); ++ ++ venc_write(sd, offset, new_val); ++ ++ return new_val; ++} ++ ++static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val) ++{ ++ struct venc_state *venc = to_state(sd); ++ ++ writel(val, venc->vdaccfg_reg); ++ ++ val = readl(venc->vdaccfg_reg); ++ ++ return val; ++} ++ ++#define VDAC_COMPONENT 0x543 ++#define VDAC_S_VIDEO 0x210 ++/* This function sets the dac of the VPBE for various outputs ++ */ ++static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) ++{ ++ switch (out_index) { ++ case 0: ++ v4l2_dbg(debug, 1, sd, "Setting output to Composite\n"); ++ venc_write(sd, VENC_DACSEL, 0); ++ break; ++ case 1: ++ v4l2_dbg(debug, 1, sd, "Setting output to Component\n"); ++ venc_write(sd, VENC_DACSEL, VDAC_COMPONENT); ++ break; ++ case 2: ++ v4l2_dbg(debug, 1, sd, "Setting output to S-video\n"); ++ venc_write(sd, VENC_DACSEL, VDAC_S_VIDEO); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) ++{ ++ struct venc_state *venc = to_state(sd); ++ ++ v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); ++ ++ if (benable) { ++ venc_write(sd, VENC_VMOD, 0); ++ venc_write(sd, VENC_CVBS, 0); ++ venc_write(sd, VENC_LCDOUT, 0); ++ venc_write(sd, VENC_HSPLS, 0); ++ venc_write(sd, VENC_HSTART, 0); ++ venc_write(sd, VENC_HVALID, 0); ++ venc_write(sd, VENC_HINT, 0); ++ venc_write(sd, VENC_VSPLS, 0); ++ venc_write(sd, VENC_VSTART, 0); ++ venc_write(sd, VENC_VVALID, 0); ++ venc_write(sd, VENC_VINT, 0); ++ venc_write(sd, VENC_YCCCTL, 0); ++ venc_write(sd, VENC_DACSEL, 0); ++ ++ } else { ++ venc_write(sd, VENC_VMOD, 0); ++ /* disable VCLK output pin enable */ ++ venc_write(sd, VENC_VIDCTL, 0x141); ++ ++ /* Disable output sync pins */ ++ venc_write(sd, VENC_SYNCCTL, 0); ++ ++ /* Disable DCLOCK */ ++ venc_write(sd, VENC_DCLKCTL, 0); ++ venc_write(sd, VENC_DRGBX1, 0x0000057C); ++ ++ /* Disable LCD output control (accepting default polarity) */ ++ venc_write(sd, VENC_LCDOUT, 0); ++ if (venc->venc_type != VPBE_VERSION_3) ++ venc_write(sd, VENC_CMPNT, 0x100); ++ venc_write(sd, VENC_HSPLS, 0); ++ venc_write(sd, VENC_HINT, 0); ++ venc_write(sd, VENC_HSTART, 0); ++ venc_write(sd, VENC_HVALID, 0); ++ ++ venc_write(sd, VENC_VSPLS, 0); ++ venc_write(sd, VENC_VINT, 0); ++ venc_write(sd, VENC_VSTART, 0); ++ venc_write(sd, VENC_VVALID, 0); ++ ++ venc_write(sd, VENC_HSDLY, 0); ++ venc_write(sd, VENC_VSDLY, 0); ++ ++ venc_write(sd, VENC_YCCCTL, 0); ++ venc_write(sd, VENC_VSTARTA, 0); ++ ++ /* Set OSD clock and OSD Sync Adavance registers */ ++ venc_write(sd, VENC_OSDCLK0, 1); ++ venc_write(sd, VENC_OSDCLK1, 2); ++ } ++} ++ ++#define VDAC_CONFIG_SD_V3 0x0E21A6B6 ++#define VDAC_CONFIG_SD_V2 0x081141CF ++/* ++ * setting NTSC mode ++ */ ++static int venc_set_ntsc(struct v4l2_subdev *sd) ++{ ++ u32 val; ++ struct venc_state *venc = to_state(sd); ++ struct venc_platform_data *pdata = venc->pdata; ++ ++ v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n"); ++ ++ /* Setup clock at VPSS & VENC for SD */ ++ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); ++ if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ if (venc->venc_type == VPBE_VERSION_3) { ++ venc_write(sd, VENC_CLKCTL, 0x01); ++ venc_write(sd, VENC_VIDCTL, 0); ++ val = vdaccfg_write(sd, VDAC_CONFIG_SD_V3); ++ } else if (venc->venc_type == VPBE_VERSION_2) { ++ venc_write(sd, VENC_CLKCTL, 0x01); ++ venc_write(sd, VENC_VIDCTL, 0); ++ vdaccfg_write(sd, VDAC_CONFIG_SD_V2); ++ } else { ++ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ ++ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); ++ /* Set REC656 Mode */ ++ venc_write(sd, VENC_YCCCTL, 0x1); ++ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ); ++ venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS); ++ } ++ ++ venc_write(sd, VENC_VMOD, 0); ++ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); ++ venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ venc_write(sd, VENC_DACTST, 0x0); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ ++ return 0; ++} ++ ++/* ++ * setting PAL mode ++ */ ++static int venc_set_pal(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ ++ v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); ++ ++ /* Setup clock at VPSS & VENC for SD */ ++ vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); ++ if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ if (venc->venc_type == VPBE_VERSION_3) { ++ venc_write(sd, VENC_CLKCTL, 0x1); ++ venc_write(sd, VENC_VIDCTL, 0); ++ vdaccfg_write(sd, VDAC_CONFIG_SD_V3); ++ } else if (venc->venc_type == VPBE_VERSION_2) { ++ venc_write(sd, VENC_CLKCTL, 0x1); ++ venc_write(sd, VENC_VIDCTL, 0); ++ vdaccfg_write(sd, VDAC_CONFIG_SD_V2); ++ } else { ++ /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ ++ venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); ++ /* Set REC656 Mode */ ++ venc_write(sd, VENC_YCCCTL, 0x1); ++ } ++ ++ venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT, ++ VENC_SYNCCTL_OVD); ++ venc_write(sd, VENC_VMOD, 0); ++ venc_modify(sd, VENC_VMOD, ++ (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, ++ (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); ++ venc_modify(sd, VENC_VMOD, ++ (1 << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ venc_write(sd, VENC_DACTST, 0x0); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ ++ return 0; ++} ++ ++#define VDAC_CONFIG_HD_V2 0x081141EF ++/* ++ * venc_set_480p59_94 ++ * ++ * This function configures the video encoder to EDTV(525p) component setting. ++ */ ++static int venc_set_480p59_94(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ struct venc_platform_data *pdata = venc->pdata; ++ ++ v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); ++ if (venc->venc_type != VPBE_VERSION_1 && ++ venc->venc_type != VPBE_VERSION_2) ++ return -EINVAL; ++ ++ /* Setup clock at VPSS & VENC for SD */ ++ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ if (venc->venc_type == VPBE_VERSION_2) ++ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); ++ venc_write(sd, VENC_OSDCLK0, 0); ++ venc_write(sd, VENC_OSDCLK1, 1); ++ ++ if (venc->venc_type == VPBE_VERSION_1) { ++ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, ++ VENC_VDPRO_DAFRQ); ++ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, ++ VENC_VDPRO_DAUPS); ++ } ++ ++ venc_write(sd, VENC_VMOD, 0); ++ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); ++ venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << ++ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); ++ ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ ++ return 0; ++} ++ ++/* ++ * venc_set_625p ++ * ++ * This function configures the video encoder to HDTV(625p) component setting ++ */ ++static int venc_set_576p50(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ struct venc_platform_data *pdata = venc->pdata; ++ ++ v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); ++ ++ if (venc->venc_type != VPBE_VERSION_1 && ++ venc->venc_type != VPBE_VERSION_2) ++ return -EINVAL; ++ /* Setup clock at VPSS & VENC for SD */ ++ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 27000000) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ if (venc->venc_type == VPBE_VERSION_2) ++ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); ++ ++ venc_write(sd, VENC_OSDCLK0, 0); ++ venc_write(sd, VENC_OSDCLK1, 1); ++ ++ if (venc->venc_type == VPBE_VERSION_1) { ++ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, ++ VENC_VDPRO_DAFRQ); ++ venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, ++ VENC_VDPRO_DAUPS); ++ } ++ ++ venc_write(sd, VENC_VMOD, 0); ++ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); ++ venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << ++ VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ ++ return 0; ++} ++ ++/* ++ * venc_set_720p60_internal - Setup 720p60 in venc for dm365 only ++ */ ++static int venc_set_720p60_internal(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ struct venc_platform_data *pdata = venc->pdata; ++ ++ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ venc_write(sd, VENC_OSDCLK0, 0); ++ venc_write(sd, VENC_OSDCLK1, 1); ++ ++ venc_write(sd, VENC_VMOD, 0); ++ /* DM365 component HD mode */ ++ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); ++ venc_modify(sd, VENC_VMOD, (HDTV_720P << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ venc_write(sd, VENC_XHINTVL, 0); ++ return 0; ++} ++ ++/* ++ * venc_set_1080i30_internal - Setup 1080i30 in venc for dm365 only ++ */ ++static int venc_set_1080i30_internal(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ struct venc_platform_data *pdata = venc->pdata; ++ ++ if (pdata->setup_clock(VPBE_ENC_CUSTOM_TIMINGS, 74250000) < 0) ++ return -EINVAL; ++ ++ venc_enabledigitaloutput(sd, 0); ++ ++ venc_write(sd, VENC_OSDCLK0, 0); ++ venc_write(sd, VENC_OSDCLK1, 1); ++ ++ ++ venc_write(sd, VENC_VMOD, 0); ++ /* DM365 component HD mode */ ++ venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), ++ VENC_VMOD_VIE); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); ++ venc_modify(sd, VENC_VMOD, (HDTV_1080I << VENC_VMOD_TVTYP_SHIFT), ++ VENC_VMOD_TVTYP); ++ venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); ++ venc_write(sd, VENC_XHINTVL, 0); ++ return 0; ++} ++ ++static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) ++{ ++ v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); ++ ++ if (norm & V4L2_STD_525_60) ++ return venc_set_ntsc(sd); ++ else if (norm & V4L2_STD_625_50) ++ return venc_set_pal(sd); ++ ++ return -EINVAL; ++} ++ ++static int venc_s_dv_timings(struct v4l2_subdev *sd, ++ struct v4l2_dv_timings *dv_timings) ++{ ++ struct venc_state *venc = to_state(sd); ++ u32 height = dv_timings->bt.height; ++ int ret; ++ ++ v4l2_dbg(debug, 1, sd, "venc_s_dv_timings\n"); ++ ++ if (height == 576) ++ return venc_set_576p50(sd); ++ else if (height == 480) ++ return venc_set_480p59_94(sd); ++ else if ((height == 720) && ++ (venc->venc_type == VPBE_VERSION_2)) { ++ /* TBD setup internal 720p mode here */ ++ ret = venc_set_720p60_internal(sd); ++ /* for DM365 VPBE, there is DAC inside */ ++ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); ++ return ret; ++ } else if ((height == 1080) && ++ (venc->venc_type == VPBE_VERSION_2)) { ++ /* TBD setup internal 1080i mode here */ ++ ret = venc_set_1080i30_internal(sd); ++ /* for DM365 VPBE, there is DAC inside */ ++ vdaccfg_write(sd, VDAC_CONFIG_HD_V2); ++ return ret; ++ } ++ return -EINVAL; ++} ++ ++static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, ++ u32 config) ++{ ++ struct venc_state *venc = to_state(sd); ++ int ret; ++ ++ v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); ++ ++ ret = venc_set_dac(sd, output); ++ if (!ret) ++ venc->output = output; ++ ++ return ret; ++} ++ ++static long venc_ioctl(struct v4l2_subdev *sd, ++ unsigned int cmd, ++ void *arg) ++{ ++ u32 val; ++ ++ switch (cmd) { ++ case VENC_GET_FLD: ++ val = venc_read(sd, VENC_VSTAT); ++ *((int *)arg) = ((val & VENC_VSTAT_FIDST) == ++ VENC_VSTAT_FIDST); ++ break; ++ default: ++ v4l2_err(sd, "Wrong IOCTL cmd\n"); ++ break; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_subdev_core_ops venc_core_ops = { ++ .ioctl = venc_ioctl, ++}; ++ ++static const struct v4l2_subdev_video_ops venc_video_ops = { ++ .s_routing = venc_s_routing, ++ .s_std_output = venc_s_std_output, ++ .s_dv_timings = venc_s_dv_timings, ++}; ++ ++static const struct v4l2_subdev_ops venc_ops = { ++ .core = &venc_core_ops, ++ .video = &venc_video_ops, ++}; ++ ++static int venc_initialize(struct v4l2_subdev *sd) ++{ ++ struct venc_state *venc = to_state(sd); ++ int ret; ++ ++ /* Set default to output to composite and std to NTSC */ ++ venc->output = 0; ++ venc->std = V4L2_STD_525_60; ++ ++ ret = venc_s_routing(sd, 0, venc->output, 0); ++ if (ret < 0) { ++ v4l2_err(sd, "Error setting output during init\n"); ++ return -EINVAL; ++ } ++ ++ ret = venc_s_std_output(sd, venc->std); ++ if (ret < 0) { ++ v4l2_err(sd, "Error setting std during init\n"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int venc_device_get(struct device *dev, void *data) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct venc_state **venc = data; ++ ++ if (strstr(pdev->name, "vpbe-venc") != NULL) ++ *venc = platform_get_drvdata(pdev); ++ ++ return 0; ++} ++ ++struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, ++ const char *venc_name) ++{ ++ struct venc_state *venc; ++ int err; ++ ++ err = bus_for_each_dev(&platform_bus_type, NULL, &venc, ++ venc_device_get); ++ if (venc == NULL) ++ return NULL; ++ ++ v4l2_subdev_init(&venc->sd, &venc_ops); ++ ++ strcpy(venc->sd.name, venc_name); ++ if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) { ++ v4l2_err(v4l2_dev, ++ "vpbe unable to register venc sub device\n"); ++ return NULL; ++ } ++ if (venc_initialize(&venc->sd)) { ++ v4l2_err(v4l2_dev, ++ "vpbe venc initialization failed\n"); ++ return NULL; ++ } ++ ++ return &venc->sd; ++} ++EXPORT_SYMBOL(venc_sub_dev_init); ++ ++static int venc_probe(struct platform_device *pdev) ++{ ++ const struct platform_device_id *pdev_id; ++ struct venc_state *venc; ++ struct resource *res; ++ int ret; ++ ++ venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL); ++ if (venc == NULL) ++ return -ENOMEM; ++ ++ pdev_id = platform_get_device_id(pdev); ++ if (!pdev_id) { ++ ret = -EINVAL; ++ goto free_mem; ++ } ++ venc->venc_type = pdev_id->driver_data; ++ venc->pdev = &pdev->dev; ++ venc->pdata = pdev->dev.platform_data; ++ if (NULL == venc->pdata) { ++ dev_err(venc->pdev, "Unable to get platform data for" ++ " VENC sub device"); ++ ret = -ENOENT; ++ goto free_mem; ++ } ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(venc->pdev, ++ "Unable to get VENC register address map\n"); ++ ret = -ENODEV; ++ goto free_mem; ++ } ++ ++ if (!request_mem_region(res->start, resource_size(res), "venc")) { ++ dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n"); ++ ret = -ENODEV; ++ goto free_mem; ++ } ++ ++ venc->venc_base = ioremap_nocache(res->start, resource_size(res)); ++ if (!venc->venc_base) { ++ dev_err(venc->pdev, "Unable to map VENC IO space\n"); ++ ret = -ENODEV; ++ goto release_venc_mem_region; ++ } ++ ++ if (venc->venc_type != VPBE_VERSION_1) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!res) { ++ dev_err(venc->pdev, ++ "Unable to get VDAC_CONFIG address map\n"); ++ ret = -ENODEV; ++ goto unmap_venc_io; ++ } ++ ++ if (!request_mem_region(res->start, ++ resource_size(res), "venc")) { ++ dev_err(venc->pdev, ++ "Unable to reserve VDAC_CONFIG MMIO region\n"); ++ ret = -ENODEV; ++ goto unmap_venc_io; ++ } ++ ++ venc->vdaccfg_reg = ioremap_nocache(res->start, ++ resource_size(res)); ++ if (!venc->vdaccfg_reg) { ++ dev_err(venc->pdev, ++ "Unable to map VDAC_CONFIG IO space\n"); ++ ret = -ENODEV; ++ goto release_vdaccfg_mem_region; ++ } ++ } ++ spin_lock_init(&venc->lock); ++ platform_set_drvdata(pdev, venc); ++ dev_notice(venc->pdev, "VENC sub device probe success\n"); ++ return 0; ++ ++release_vdaccfg_mem_region: ++ release_mem_region(res->start, resource_size(res)); ++unmap_venc_io: ++ iounmap(venc->venc_base); ++release_venc_mem_region: ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++free_mem: ++ kfree(venc); ++ return ret; ++} ++ ++static int venc_remove(struct platform_device *pdev) ++{ ++ struct venc_state *venc = platform_get_drvdata(pdev); ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ iounmap((void *)venc->venc_base); ++ release_mem_region(res->start, resource_size(res)); ++ if (venc->venc_type != VPBE_VERSION_1) { ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ iounmap((void *)venc->vdaccfg_reg); ++ release_mem_region(res->start, resource_size(res)); ++ } ++ kfree(venc); ++ ++ return 0; ++} ++ ++static struct platform_driver venc_driver = { ++ .probe = venc_probe, ++ .remove = venc_remove, ++ .driver = { ++ .name = MODULE_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .id_table = vpbe_venc_devtype ++}; ++ ++module_platform_driver(venc_driver); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("VPBE VENC Driver"); ++MODULE_AUTHOR("Texas Instruments"); +diff --git a/drivers/media/platform/davinci/vpbe_venc_regs.h b/drivers/media/platform/davinci/vpbe_venc_regs.h +new file mode 100644 +index 0000000..947cb15 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpbe_venc_regs.h +@@ -0,0 +1,177 @@ ++/* ++ * Copyright (C) 2006-2010 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation version 2.. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _VPBE_VENC_REGS_H ++#define _VPBE_VENC_REGS_H ++ ++/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */ ++#define VENC_VMOD 0x00 ++#define VENC_VIDCTL 0x04 ++#define VENC_VDPRO 0x08 ++#define VENC_SYNCCTL 0x0C ++#define VENC_HSPLS 0x10 ++#define VENC_VSPLS 0x14 ++#define VENC_HINT 0x18 ++#define VENC_HSTART 0x1C ++#define VENC_HVALID 0x20 ++#define VENC_VINT 0x24 ++#define VENC_VSTART 0x28 ++#define VENC_VVALID 0x2C ++#define VENC_HSDLY 0x30 ++#define VENC_VSDLY 0x34 ++#define VENC_YCCCTL 0x38 ++#define VENC_RGBCTL 0x3C ++#define VENC_RGBCLP 0x40 ++#define VENC_LINECTL 0x44 ++#define VENC_CULLLINE 0x48 ++#define VENC_LCDOUT 0x4C ++#define VENC_BRTS 0x50 ++#define VENC_BRTW 0x54 ++#define VENC_ACCTL 0x58 ++#define VENC_PWMP 0x5C ++#define VENC_PWMW 0x60 ++#define VENC_DCLKCTL 0x64 ++#define VENC_DCLKPTN0 0x68 ++#define VENC_DCLKPTN1 0x6C ++#define VENC_DCLKPTN2 0x70 ++#define VENC_DCLKPTN3 0x74 ++#define VENC_DCLKPTN0A 0x78 ++#define VENC_DCLKPTN1A 0x7C ++#define VENC_DCLKPTN2A 0x80 ++#define VENC_DCLKPTN3A 0x84 ++#define VENC_DCLKHS 0x88 ++#define VENC_DCLKHSA 0x8C ++#define VENC_DCLKHR 0x90 ++#define VENC_DCLKVS 0x94 ++#define VENC_DCLKVR 0x98 ++#define VENC_CAPCTL 0x9C ++#define VENC_CAPDO 0xA0 ++#define VENC_CAPDE 0xA4 ++#define VENC_ATR0 0xA8 ++#define VENC_ATR1 0xAC ++#define VENC_ATR2 0xB0 ++#define VENC_VSTAT 0xB8 ++#define VENC_RAMADR 0xBC ++#define VENC_RAMPORT 0xC0 ++#define VENC_DACTST 0xC4 ++#define VENC_YCOLVL 0xC8 ++#define VENC_SCPROG 0xCC ++#define VENC_CVBS 0xDC ++#define VENC_CMPNT 0xE0 ++#define VENC_ETMG0 0xE4 ++#define VENC_ETMG1 0xE8 ++#define VENC_ETMG2 0xEC ++#define VENC_ETMG3 0xF0 ++#define VENC_DACSEL 0xF4 ++#define VENC_ARGBX0 0x100 ++#define VENC_ARGBX1 0x104 ++#define VENC_ARGBX2 0x108 ++#define VENC_ARGBX3 0x10C ++#define VENC_ARGBX4 0x110 ++#define VENC_DRGBX0 0x114 ++#define VENC_DRGBX1 0x118 ++#define VENC_DRGBX2 0x11C ++#define VENC_DRGBX3 0x120 ++#define VENC_DRGBX4 0x124 ++#define VENC_VSTARTA 0x128 ++#define VENC_OSDCLK0 0x12C ++#define VENC_OSDCLK1 0x130 ++#define VENC_HVLDCL0 0x134 ++#define VENC_HVLDCL1 0x138 ++#define VENC_OSDHADV 0x13C ++#define VENC_CLKCTL 0x140 ++#define VENC_GAMCTL 0x144 ++#define VENC_XHINTVL 0x174 ++ ++/* bit definitions */ ++#define VPBE_PCR_VENC_DIV (1 << 1) ++#define VPBE_PCR_CLK_OFF (1 << 0) ++ ++#define VENC_VMOD_VDMD_SHIFT 12 ++#define VENC_VMOD_VDMD_YCBCR16 0 ++#define VENC_VMOD_VDMD_YCBCR8 1 ++#define VENC_VMOD_VDMD_RGB666 2 ++#define VENC_VMOD_VDMD_RGB8 3 ++#define VENC_VMOD_VDMD_EPSON 4 ++#define VENC_VMOD_VDMD_CASIO 5 ++#define VENC_VMOD_VDMD_UDISPQVGA 6 ++#define VENC_VMOD_VDMD_STNLCD 7 ++#define VENC_VMOD_VIE_SHIFT 1 ++#define VENC_VMOD_VDMD (7 << 12) ++#define VENC_VMOD_ITLCL (1 << 11) ++#define VENC_VMOD_ITLC (1 << 10) ++#define VENC_VMOD_NSIT (1 << 9) ++#define VENC_VMOD_HDMD (1 << 8) ++#define VENC_VMOD_TVTYP_SHIFT 6 ++#define VENC_VMOD_TVTYP (3 << 6) ++#define VENC_VMOD_SLAVE (1 << 5) ++#define VENC_VMOD_VMD (1 << 4) ++#define VENC_VMOD_BLNK (1 << 3) ++#define VENC_VMOD_VIE (1 << 1) ++#define VENC_VMOD_VENC (1 << 0) ++ ++/* VMOD TVTYP options for HDMD=0 */ ++#define SDTV_NTSC 0 ++#define SDTV_PAL 1 ++/* VMOD TVTYP options for HDMD=1 */ ++#define HDTV_525P 0 ++#define HDTV_625P 1 ++#define HDTV_1080I 2 ++#define HDTV_720P 3 ++ ++#define VENC_VIDCTL_VCLKP (1 << 14) ++#define VENC_VIDCTL_VCLKE_SHIFT 13 ++#define VENC_VIDCTL_VCLKE (1 << 13) ++#define VENC_VIDCTL_VCLKZ_SHIFT 12 ++#define VENC_VIDCTL_VCLKZ (1 << 12) ++#define VENC_VIDCTL_SYDIR_SHIFT 8 ++#define VENC_VIDCTL_SYDIR (1 << 8) ++#define VENC_VIDCTL_DOMD_SHIFT 4 ++#define VENC_VIDCTL_DOMD (3 << 4) ++#define VENC_VIDCTL_YCDIR_SHIFT 0 ++#define VENC_VIDCTL_YCDIR (1 << 0) ++ ++#define VENC_VDPRO_ATYCC_SHIFT 5 ++#define VENC_VDPRO_ATYCC (1 << 5) ++#define VENC_VDPRO_ATCOM_SHIFT 4 ++#define VENC_VDPRO_ATCOM (1 << 4) ++#define VENC_VDPRO_DAFRQ (1 << 3) ++#define VENC_VDPRO_DAUPS (1 << 2) ++#define VENC_VDPRO_CUPS (1 << 1) ++#define VENC_VDPRO_YUPS (1 << 0) ++ ++#define VENC_SYNCCTL_VPL_SHIFT 3 ++#define VENC_SYNCCTL_VPL (1 << 3) ++#define VENC_SYNCCTL_HPL_SHIFT 2 ++#define VENC_SYNCCTL_HPL (1 << 2) ++#define VENC_SYNCCTL_SYEV_SHIFT 1 ++#define VENC_SYNCCTL_SYEV (1 << 1) ++#define VENC_SYNCCTL_SYEH_SHIFT 0 ++#define VENC_SYNCCTL_SYEH (1 << 0) ++#define VENC_SYNCCTL_OVD_SHIFT 14 ++#define VENC_SYNCCTL_OVD (1 << 14) ++ ++#define VENC_DCLKCTL_DCKEC_SHIFT 11 ++#define VENC_DCLKCTL_DCKEC (1 << 11) ++#define VENC_DCLKCTL_DCKPW_SHIFT 0 ++#define VENC_DCLKCTL_DCKPW (0x3f << 0) ++ ++#define VENC_VSTAT_FIDST (1 << 4) ++ ++#define VENC_CMPNT_MRGB_SHIFT 14 ++#define VENC_CMPNT_MRGB (1 << 14) ++ ++#endif /* _VPBE_VENC_REGS_H */ +diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c +new file mode 100644 +index 0000000..65f4264 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpfe_capture.c +@@ -0,0 +1,2078 @@ ++/* ++ * Copyright (C) 2008-2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Driver name : VPFE Capture driver ++ * VPFE Capture driver allows applications to capture and stream video ++ * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as ++ * TVP5146 or Raw Bayer RGB image data from an image sensor ++ * such as Microns' MT9T001, MT9T031 etc. ++ * ++ * These SoCs have, in common, a Video Processing Subsystem (VPSS) that ++ * consists of a Video Processing Front End (VPFE) for capturing ++ * video/raw image data and Video Processing Back End (VPBE) for displaying ++ * YUV data through an in-built analog encoder or Digital LCD port. This ++ * driver is for capture through VPFE. A typical EVM using these SoCs have ++ * following high level configuration. ++ * ++ * ++ * decoder(TVP5146/ YUV/ ++ * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) ++ * data input | | ++ * V | ++ * SDRAM | ++ * V ++ * Image Processor ++ * | ++ * V ++ * SDRAM ++ * The data flow happens from a decoder connected to the VPFE over a ++ * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface ++ * and to the input of VPFE through an optional MUX (if more inputs are ++ * to be interfaced on the EVM). The input data is first passed through ++ * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC ++ * does very little or no processing on YUV data and does pre-process Raw ++ * Bayer RGB data through modules such as Defect Pixel Correction (DFC) ++ * Color Space Conversion (CSC), data gain/offset etc. After this, data ++ * can be written to SDRAM or can be connected to the image processing ++ * block such as IPIPE (on DM355 only). ++ * ++ * Features supported ++ * - MMAP IO ++ * - Capture using TVP5146 over BT.656 ++ * - support for interfacing decoders using sub device model ++ * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV ++ * data capture to SDRAM. ++ * TODO list ++ * - Support multiple REQBUF after open ++ * - Support for de-allocating buffers through REQBUF ++ * - Support for Raw Bayer RGB capture ++ * - Support for chaining Image Processor ++ * - Support for static allocation of buffers ++ * - Support for USERPTR IO ++ * - Support for STREAMON before QBUF ++ * - Support for control ioctls ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ccdc_hw_device.h" ++ ++static int debug; ++static u32 numbuffers = 3; ++static u32 bufsize = (720 * 576 * 2); ++ ++module_param(numbuffers, uint, S_IRUGO); ++module_param(bufsize, uint, S_IRUGO); ++module_param(debug, int, 0644); ++ ++MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); ++MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++ ++MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Texas Instruments"); ++ ++/* standard information */ ++struct vpfe_standard { ++ v4l2_std_id std_id; ++ unsigned int width; ++ unsigned int height; ++ struct v4l2_fract pixelaspect; ++ /* 0 - progressive, 1 - interlaced */ ++ int frame_format; ++}; ++ ++/* ccdc configuration */ ++struct ccdc_config { ++ /* This make sure vpfe is probed and ready to go */ ++ int vpfe_probed; ++ /* name of ccdc device */ ++ char name[32]; ++}; ++ ++/* data structures */ ++static struct vpfe_config_params config_params = { ++ .min_numbuffers = 3, ++ .numbuffers = 3, ++ .min_bufsize = 720 * 480 * 2, ++ .device_bufsize = 720 * 576 * 2, ++}; ++ ++/* ccdc device registered */ ++static struct ccdc_hw_device *ccdc_dev; ++/* lock for accessing ccdc information */ ++static DEFINE_MUTEX(ccdc_lock); ++/* ccdc configuration */ ++static struct ccdc_config *ccdc_cfg; ++ ++const struct vpfe_standard vpfe_standards[] = { ++ {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, ++ {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, ++}; ++ ++/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ ++static const struct vpfe_pixel_format vpfe_pix_fmts[] = { ++ { ++ .fmtdesc = { ++ .index = 0, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "Bayer GrRBGb 8bit A-Law compr.", ++ .pixelformat = V4L2_PIX_FMT_SBGGR8, ++ }, ++ .bpp = 1, ++ }, ++ { ++ .fmtdesc = { ++ .index = 1, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "Bayer GrRBGb - 16bit", ++ .pixelformat = V4L2_PIX_FMT_SBGGR16, ++ }, ++ .bpp = 2, ++ }, ++ { ++ .fmtdesc = { ++ .index = 2, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "Bayer GrRBGb 8bit DPCM compr.", ++ .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, ++ }, ++ .bpp = 1, ++ }, ++ { ++ .fmtdesc = { ++ .index = 3, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "YCbCr 4:2:2 Interleaved UYVY", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ }, ++ .bpp = 2, ++ }, ++ { ++ .fmtdesc = { ++ .index = 4, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "YCbCr 4:2:2 Interleaved YUYV", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ }, ++ .bpp = 2, ++ }, ++ { ++ .fmtdesc = { ++ .index = 5, ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, ++ .description = "Y/CbCr 4:2:0 - Semi planar", ++ .pixelformat = V4L2_PIX_FMT_NV12, ++ }, ++ .bpp = 1, ++ }, ++}; ++ ++/* ++ * vpfe_lookup_pix_format() ++ * lookup an entry in the vpfe pix format table based on pix_format ++ */ ++static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { ++ if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat) ++ return &vpfe_pix_fmts[i]; ++ } ++ return NULL; ++} ++ ++/* ++ * vpfe_register_ccdc_device. CCDC module calls this to ++ * register with vpfe capture ++ */ ++int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) ++{ ++ int ret = 0; ++ printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); ++ ++ BUG_ON(!dev->hw_ops.open); ++ BUG_ON(!dev->hw_ops.enable); ++ BUG_ON(!dev->hw_ops.set_hw_if_params); ++ BUG_ON(!dev->hw_ops.configure); ++ BUG_ON(!dev->hw_ops.set_buftype); ++ BUG_ON(!dev->hw_ops.get_buftype); ++ BUG_ON(!dev->hw_ops.enum_pix); ++ BUG_ON(!dev->hw_ops.set_frame_format); ++ BUG_ON(!dev->hw_ops.get_frame_format); ++ BUG_ON(!dev->hw_ops.get_pixel_format); ++ BUG_ON(!dev->hw_ops.set_pixel_format); ++ BUG_ON(!dev->hw_ops.set_image_window); ++ BUG_ON(!dev->hw_ops.get_image_window); ++ BUG_ON(!dev->hw_ops.get_line_length); ++ BUG_ON(!dev->hw_ops.getfid); ++ ++ mutex_lock(&ccdc_lock); ++ if (NULL == ccdc_cfg) { ++ /* ++ * TODO. Will this ever happen? if so, we need to fix it. ++ * Proabably we need to add the request to a linked list and ++ * walk through it during vpfe probe ++ */ ++ printk(KERN_ERR "vpfe capture not initialized\n"); ++ ret = -EFAULT; ++ goto unlock; ++ } ++ ++ if (strcmp(dev->name, ccdc_cfg->name)) { ++ /* ignore this ccdc */ ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ if (ccdc_dev) { ++ printk(KERN_ERR "ccdc already registered\n"); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ ccdc_dev = dev; ++unlock: ++ mutex_unlock(&ccdc_lock); ++ return ret; ++} ++EXPORT_SYMBOL(vpfe_register_ccdc_device); ++ ++/* ++ * vpfe_unregister_ccdc_device. CCDC module calls this to ++ * unregister with vpfe capture ++ */ ++void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) ++{ ++ if (NULL == dev) { ++ printk(KERN_ERR "invalid ccdc device ptr\n"); ++ return; ++ } ++ ++ printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", ++ dev->name); ++ ++ if (strcmp(dev->name, ccdc_cfg->name)) { ++ /* ignore this ccdc */ ++ return; ++ } ++ ++ mutex_lock(&ccdc_lock); ++ ccdc_dev = NULL; ++ mutex_unlock(&ccdc_lock); ++ return; ++} ++EXPORT_SYMBOL(vpfe_unregister_ccdc_device); ++ ++/* ++ * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings ++ */ ++static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, ++ struct v4l2_format *f) ++{ ++ struct v4l2_rect image_win; ++ enum ccdc_buftype buf_type; ++ enum ccdc_frmfmt frm_fmt; ++ ++ memset(f, 0, sizeof(*f)); ++ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ ccdc_dev->hw_ops.get_image_window(&image_win); ++ f->fmt.pix.width = image_win.width; ++ f->fmt.pix.height = image_win.height; ++ f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); ++ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * ++ f->fmt.pix.height; ++ buf_type = ccdc_dev->hw_ops.get_buftype(); ++ f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); ++ frm_fmt = ccdc_dev->hw_ops.get_frame_format(); ++ if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { ++ if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) ++ f->fmt.pix.field = V4L2_FIELD_INTERLACED; ++ else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) ++ f->fmt.pix.field = V4L2_FIELD_SEQ_TB; ++ else { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); ++ return -EINVAL; ++ } ++ } else { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ++ * vpfe_config_ccdc_image_format() ++ * For a pix format, configure ccdc to setup the capture ++ */ ++static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) ++{ ++ enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; ++ int ret = 0; ++ ++ if (ccdc_dev->hw_ops.set_pixel_format( ++ vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "couldn't set pix format in ccdc\n"); ++ return -EINVAL; ++ } ++ /* configure the image window */ ++ ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); ++ ++ switch (vpfe_dev->fmt.fmt.pix.field) { ++ case V4L2_FIELD_INTERLACED: ++ /* do nothing, since it is default */ ++ ret = ccdc_dev->hw_ops.set_buftype( ++ CCDC_BUFTYPE_FLD_INTERLEAVED); ++ break; ++ case V4L2_FIELD_NONE: ++ frm_fmt = CCDC_FRMFMT_PROGRESSIVE; ++ /* buffer type only applicable for interlaced scan */ ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ ret = ccdc_dev->hw_ops.set_buftype( ++ CCDC_BUFTYPE_FLD_SEPARATED); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* set the frame format */ ++ if (!ret) ++ ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); ++ return ret; ++} ++/* ++ * vpfe_config_image_format() ++ * For a given standard, this functions sets up the default ++ * pix format & crop values in the vpfe device and ccdc. It first ++ * starts with defaults based values from the standard table. ++ * It then checks if sub device support g_mbus_fmt and then override the ++ * values based on that.Sets crop values to match with scan resolution ++ * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the ++ * values in ccdc ++ */ ++static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, ++ const v4l2_std_id *std_id) ++{ ++ struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ struct v4l2_pix_format *pix = &vpfe_dev->fmt.fmt.pix; ++ int i, ret = 0; ++ ++ for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { ++ if (vpfe_standards[i].std_id & *std_id) { ++ vpfe_dev->std_info.active_pixels = ++ vpfe_standards[i].width; ++ vpfe_dev->std_info.active_lines = ++ vpfe_standards[i].height; ++ vpfe_dev->std_info.frame_format = ++ vpfe_standards[i].frame_format; ++ vpfe_dev->std_index = i; ++ break; ++ } ++ } ++ ++ if (i == ARRAY_SIZE(vpfe_standards)) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); ++ return -EINVAL; ++ } ++ ++ vpfe_dev->crop.top = 0; ++ vpfe_dev->crop.left = 0; ++ vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; ++ vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; ++ pix->width = vpfe_dev->crop.width; ++ pix->height = vpfe_dev->crop.height; ++ ++ /* first field and frame format based on standard frame format */ ++ if (vpfe_dev->std_info.frame_format) { ++ pix->field = V4L2_FIELD_INTERLACED; ++ /* assume V4L2_PIX_FMT_UYVY as default */ ++ pix->pixelformat = V4L2_PIX_FMT_UYVY; ++ v4l2_fill_mbus_format(&mbus_fmt, pix, ++ V4L2_MBUS_FMT_YUYV10_2X10); ++ } else { ++ pix->field = V4L2_FIELD_NONE; ++ /* assume V4L2_PIX_FMT_SBGGR8 */ ++ pix->pixelformat = V4L2_PIX_FMT_SBGGR8; ++ v4l2_fill_mbus_format(&mbus_fmt, pix, ++ V4L2_MBUS_FMT_SBGGR8_1X8); ++ } ++ ++ /* if sub device supports g_mbus_fmt, override the defaults */ ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, ++ sdinfo->grp_id, video, g_mbus_fmt, &mbus_fmt); ++ ++ if (ret && ret != -ENOIOCTLCMD) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "error in getting g_mbus_fmt from sub device\n"); ++ return ret; ++ } ++ v4l2_fill_pix_format(pix, &mbus_fmt); ++ pix->bytesperline = pix->width * 2; ++ pix->sizeimage = pix->bytesperline * pix->height; ++ ++ /* Sets the values in CCDC */ ++ ret = vpfe_config_ccdc_image_format(vpfe_dev); ++ if (ret) ++ return ret; ++ ++ /* Update the values of sizeimage and bytesperline */ ++ if (!ret) { ++ pix->bytesperline = ccdc_dev->hw_ops.get_line_length(); ++ pix->sizeimage = pix->bytesperline * pix->height; ++ } ++ return ret; ++} ++ ++static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) ++{ ++ int ret = 0; ++ ++ /* set first input of current subdevice as the current input */ ++ vpfe_dev->current_input = 0; ++ ++ /* set default standard */ ++ vpfe_dev->std_index = 0; ++ ++ /* Configure the default format information */ ++ ret = vpfe_config_image_format(vpfe_dev, ++ &vpfe_standards[vpfe_dev->std_index].std_id); ++ if (ret) ++ return ret; ++ ++ /* now open the ccdc device to initialize it */ ++ mutex_lock(&ccdc_lock); ++ if (NULL == ccdc_dev) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); ++ ret = -ENODEV; ++ goto unlock; ++ } ++ ++ if (!try_module_get(ccdc_dev->owner)) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); ++ ret = -ENODEV; ++ goto unlock; ++ } ++ ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); ++ if (!ret) ++ vpfe_dev->initialized = 1; ++ ++ /* Clear all VPFE/CCDC interrupts */ ++ if (vpfe_dev->cfg->clr_intr) ++ vpfe_dev->cfg->clr_intr(-1); ++ ++unlock: ++ mutex_unlock(&ccdc_lock); ++ return ret; ++} ++ ++/* ++ * vpfe_open : It creates object of file handle structure and ++ * stores it in private_data member of filepointer ++ */ ++static int vpfe_open(struct file *file) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); ++ ++ if (!vpfe_dev->cfg->num_subdevs) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); ++ return -ENODEV; ++ } ++ ++ /* Allocate memory for the file handle object */ ++ fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL); ++ if (NULL == fh) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "unable to allocate memory for file handle object\n"); ++ return -ENOMEM; ++ } ++ /* store pointer to fh in private_data member of file */ ++ file->private_data = fh; ++ fh->vpfe_dev = vpfe_dev; ++ mutex_lock(&vpfe_dev->lock); ++ /* If decoder is not initialized. initialize it */ ++ if (!vpfe_dev->initialized) { ++ if (vpfe_initialize_device(vpfe_dev)) { ++ mutex_unlock(&vpfe_dev->lock); ++ return -ENODEV; ++ } ++ } ++ /* Increment device usrs counter */ ++ vpfe_dev->usrs++; ++ /* Set io_allowed member to false */ ++ fh->io_allowed = 0; ++ /* Initialize priority of this instance to default priority */ ++ fh->prio = V4L2_PRIORITY_UNSET; ++ v4l2_prio_open(&vpfe_dev->prio, &fh->prio); ++ mutex_unlock(&vpfe_dev->lock); ++ return 0; ++} ++ ++static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) ++{ ++ unsigned long addr; ++ ++ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, ++ struct videobuf_buffer, queue); ++ list_del(&vpfe_dev->next_frm->queue); ++ vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; ++ addr = videobuf_to_dma_contig(vpfe_dev->next_frm); ++ ++ ccdc_dev->hw_ops.setfbaddr(addr); ++} ++ ++static void vpfe_schedule_bottom_field(struct vpfe_device *vpfe_dev) ++{ ++ unsigned long addr; ++ ++ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); ++ addr += vpfe_dev->field_off; ++ ccdc_dev->hw_ops.setfbaddr(addr); ++} ++ ++static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) ++{ ++ v4l2_get_timestamp(&vpfe_dev->cur_frm->ts); ++ vpfe_dev->cur_frm->state = VIDEOBUF_DONE; ++ vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; ++ wake_up_interruptible(&vpfe_dev->cur_frm->done); ++ vpfe_dev->cur_frm = vpfe_dev->next_frm; ++} ++ ++/* ISR for VINT0*/ ++static irqreturn_t vpfe_isr(int irq, void *dev_id) ++{ ++ struct vpfe_device *vpfe_dev = dev_id; ++ enum v4l2_field field; ++ int fid; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); ++ field = vpfe_dev->fmt.fmt.pix.field; ++ ++ /* if streaming not started, don't do anything */ ++ if (!vpfe_dev->started) ++ goto clear_intr; ++ ++ /* only for 6446 this will be applicable */ ++ if (NULL != ccdc_dev->hw_ops.reset) ++ ccdc_dev->hw_ops.reset(); ++ ++ if (field == V4L2_FIELD_NONE) { ++ /* handle progressive frame capture */ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "frame format is progressive...\n"); ++ if (vpfe_dev->cur_frm != vpfe_dev->next_frm) ++ vpfe_process_buffer_complete(vpfe_dev); ++ goto clear_intr; ++ } ++ ++ /* interlaced or TB capture check which field we are in hardware */ ++ fid = ccdc_dev->hw_ops.getfid(); ++ ++ /* switch the software maintained field id */ ++ vpfe_dev->field_id ^= 1; ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", ++ fid, vpfe_dev->field_id); ++ if (fid == vpfe_dev->field_id) { ++ /* we are in-sync here,continue */ ++ if (fid == 0) { ++ /* ++ * One frame is just being captured. If the next frame ++ * is available, release the current frame and move on ++ */ ++ if (vpfe_dev->cur_frm != vpfe_dev->next_frm) ++ vpfe_process_buffer_complete(vpfe_dev); ++ /* ++ * based on whether the two fields are stored ++ * interleavely or separately in memory, reconfigure ++ * the CCDC memory address ++ */ ++ if (field == V4L2_FIELD_SEQ_TB) { ++ vpfe_schedule_bottom_field(vpfe_dev); ++ } ++ goto clear_intr; ++ } ++ /* ++ * if one field is just being captured configure ++ * the next frame get the next frame from the empty ++ * queue if no frame is available hold on to the ++ * current buffer ++ */ ++ spin_lock(&vpfe_dev->dma_queue_lock); ++ if (!list_empty(&vpfe_dev->dma_queue) && ++ vpfe_dev->cur_frm == vpfe_dev->next_frm) ++ vpfe_schedule_next_buffer(vpfe_dev); ++ spin_unlock(&vpfe_dev->dma_queue_lock); ++ } else if (fid == 0) { ++ /* ++ * out of sync. Recover from any hardware out-of-sync. ++ * May loose one frame ++ */ ++ vpfe_dev->field_id = fid; ++ } ++clear_intr: ++ if (vpfe_dev->cfg->clr_intr) ++ vpfe_dev->cfg->clr_intr(irq); ++ ++ return IRQ_HANDLED; ++} ++ ++/* vdint1_isr - isr handler for VINT1 interrupt */ ++static irqreturn_t vdint1_isr(int irq, void *dev_id) ++{ ++ struct vpfe_device *vpfe_dev = dev_id; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); ++ ++ /* if streaming not started, don't do anything */ ++ if (!vpfe_dev->started) { ++ if (vpfe_dev->cfg->clr_intr) ++ vpfe_dev->cfg->clr_intr(irq); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock(&vpfe_dev->dma_queue_lock); ++ if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && ++ !list_empty(&vpfe_dev->dma_queue) && ++ vpfe_dev->cur_frm == vpfe_dev->next_frm) ++ vpfe_schedule_next_buffer(vpfe_dev); ++ spin_unlock(&vpfe_dev->dma_queue_lock); ++ ++ if (vpfe_dev->cfg->clr_intr) ++ vpfe_dev->cfg->clr_intr(irq); ++ ++ return IRQ_HANDLED; ++} ++ ++static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) ++{ ++ enum ccdc_frmfmt frame_format; ++ ++ frame_format = ccdc_dev->hw_ops.get_frame_format(); ++ if (frame_format == CCDC_FRMFMT_PROGRESSIVE) ++ free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); ++} ++ ++static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) ++{ ++ enum ccdc_frmfmt frame_format; ++ ++ frame_format = ccdc_dev->hw_ops.get_frame_format(); ++ if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { ++ return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, ++ IRQF_DISABLED, "vpfe_capture1", ++ vpfe_dev); ++ } ++ return 0; ++} ++ ++/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ ++static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) ++{ ++ vpfe_dev->started = 0; ++ ccdc_dev->hw_ops.enable(0); ++ if (ccdc_dev->hw_ops.enable_out_to_sdram) ++ ccdc_dev->hw_ops.enable_out_to_sdram(0); ++} ++ ++/* ++ * vpfe_release : This function deletes buffer queue, frees the ++ * buffers and the vpfe file handle ++ */ ++static int vpfe_release(struct file *file) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh = file->private_data; ++ struct vpfe_subdev_info *sdinfo; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); ++ ++ /* Get the device lock */ ++ mutex_lock(&vpfe_dev->lock); ++ /* if this instance is doing IO */ ++ if (fh->io_allowed) { ++ if (vpfe_dev->started) { ++ sdinfo = vpfe_dev->current_subdev; ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, ++ sdinfo->grp_id, ++ video, s_stream, 0); ++ if (ret && (ret != -ENOIOCTLCMD)) ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "stream off failed in subdev\n"); ++ vpfe_stop_ccdc_capture(vpfe_dev); ++ vpfe_detach_irq(vpfe_dev); ++ videobuf_streamoff(&vpfe_dev->buffer_queue); ++ } ++ vpfe_dev->io_usrs = 0; ++ vpfe_dev->numbuffers = config_params.numbuffers; ++ } ++ ++ /* Decrement device usrs counter */ ++ vpfe_dev->usrs--; ++ /* Close the priority */ ++ v4l2_prio_close(&vpfe_dev->prio, fh->prio); ++ /* If this is the last file handle */ ++ if (!vpfe_dev->usrs) { ++ vpfe_dev->initialized = 0; ++ if (ccdc_dev->hw_ops.close) ++ ccdc_dev->hw_ops.close(vpfe_dev->pdev); ++ module_put(ccdc_dev->owner); ++ } ++ mutex_unlock(&vpfe_dev->lock); ++ file->private_data = NULL; ++ /* Free memory allocated to file handle object */ ++ kfree(fh); ++ return 0; ++} ++ ++/* ++ * vpfe_mmap : It is used to map kernel space buffers ++ * into user spaces ++ */ ++static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ /* Get the device object and file handle object */ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); ++ ++ return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); ++} ++ ++/* ++ * vpfe_poll: It is used for select/poll system call ++ */ ++static unsigned int vpfe_poll(struct file *file, poll_table *wait) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); ++ ++ if (vpfe_dev->started) ++ return videobuf_poll_stream(file, ++ &vpfe_dev->buffer_queue, wait); ++ return 0; ++} ++ ++/* vpfe capture driver file operations */ ++static const struct v4l2_file_operations vpfe_fops = { ++ .owner = THIS_MODULE, ++ .open = vpfe_open, ++ .release = vpfe_release, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vpfe_mmap, ++ .poll = vpfe_poll ++}; ++ ++/* ++ * vpfe_check_format() ++ * This function adjust the input pixel format as per hardware ++ * capabilities and update the same in pixfmt. ++ * Following algorithm used :- ++ * ++ * If given pixformat is not in the vpfe list of pix formats or not ++ * supported by the hardware, current value of pixformat in the device ++ * is used ++ * If given field is not supported, then current field is used. If field ++ * is different from current, then it is matched with that from sub device. ++ * Minimum height is 2 lines for interlaced or tb field and 1 line for ++ * progressive. Maximum height is clamped to active active lines of scan ++ * Minimum width is 32 bytes in memory and width is clamped to active ++ * pixels of scan. ++ * bytesperline is a multiple of 32. ++ */ ++static const struct vpfe_pixel_format * ++ vpfe_check_format(struct vpfe_device *vpfe_dev, ++ struct v4l2_pix_format *pixfmt) ++{ ++ u32 min_height = 1, min_width = 32, max_width, max_height; ++ const struct vpfe_pixel_format *vpfe_pix_fmt; ++ u32 pix; ++ int temp, found; ++ ++ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); ++ if (NULL == vpfe_pix_fmt) { ++ /* ++ * use current pixel format in the vpfe device. We ++ * will find this pix format in the table ++ */ ++ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; ++ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); ++ } ++ ++ /* check if hw supports it */ ++ temp = 0; ++ found = 0; ++ while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { ++ if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) { ++ found = 1; ++ break; ++ } ++ temp++; ++ } ++ ++ if (!found) { ++ /* use current pixel format */ ++ pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; ++ /* ++ * Since this is currently used in the vpfe device, we ++ * will find this pix format in the table ++ */ ++ vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); ++ } ++ ++ /* check what field format is supported */ ++ if (pixfmt->field == V4L2_FIELD_ANY) { ++ /* if field is any, use current value as default */ ++ pixfmt->field = vpfe_dev->fmt.fmt.pix.field; ++ } ++ ++ /* ++ * if field is not same as current field in the vpfe device ++ * try matching the field with the sub device field ++ */ ++ if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { ++ /* ++ * If field value is not in the supported fields, use current ++ * field used in the device as default ++ */ ++ switch (pixfmt->field) { ++ case V4L2_FIELD_INTERLACED: ++ case V4L2_FIELD_SEQ_TB: ++ /* if sub device is supporting progressive, use that */ ++ if (!vpfe_dev->std_info.frame_format) ++ pixfmt->field = V4L2_FIELD_NONE; ++ break; ++ case V4L2_FIELD_NONE: ++ if (vpfe_dev->std_info.frame_format) ++ pixfmt->field = V4L2_FIELD_INTERLACED; ++ break; ++ ++ default: ++ /* use current field as default */ ++ pixfmt->field = vpfe_dev->fmt.fmt.pix.field; ++ break; ++ } ++ } ++ ++ /* Now adjust image resolutions supported */ ++ if (pixfmt->field == V4L2_FIELD_INTERLACED || ++ pixfmt->field == V4L2_FIELD_SEQ_TB) ++ min_height = 2; ++ ++ max_width = vpfe_dev->std_info.active_pixels; ++ max_height = vpfe_dev->std_info.active_lines; ++ min_width /= vpfe_pix_fmt->bpp; ++ ++ v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", ++ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); ++ ++ pixfmt->width = clamp((pixfmt->width), min_width, max_width); ++ pixfmt->height = clamp((pixfmt->height), min_height, max_height); ++ ++ /* If interlaced, adjust height to be a multiple of 2 */ ++ if (pixfmt->field == V4L2_FIELD_INTERLACED) ++ pixfmt->height &= (~1); ++ /* ++ * recalculate bytesperline and sizeimage since width ++ * and height might have changed ++ */ ++ pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) ++ & ~31); ++ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) ++ pixfmt->sizeimage = ++ pixfmt->bytesperline * pixfmt->height + ++ ((pixfmt->bytesperline * pixfmt->height) >> 1); ++ else ++ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; ++ ++ v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height =" ++ " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", ++ pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, ++ pixfmt->bytesperline, pixfmt->sizeimage); ++ return vpfe_pix_fmt; ++} ++ ++static int vpfe_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); ++ ++ cap->version = VPFE_CAPTURE_VERSION_CODE; ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); ++ strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); ++ strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); ++ return 0; ++} ++ ++static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); ++ /* Fill in the information about format */ ++ *fmt = vpfe_dev->fmt; ++ return ret; ++} ++ ++static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *fmt) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ const struct vpfe_pixel_format *pix_fmt; ++ int temp_index; ++ u32 pix; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); ++ ++ if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) ++ return -EINVAL; ++ ++ /* Fill in the information about format */ ++ pix_fmt = vpfe_lookup_pix_format(pix); ++ if (NULL != pix_fmt) { ++ temp_index = fmt->index; ++ *fmt = pix_fmt->fmtdesc; ++ fmt->index = temp_index; ++ return 0; ++ } ++ return -EINVAL; ++} ++ ++static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ const struct vpfe_pixel_format *pix_fmts; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); ++ ++ /* If streaming is started, return error */ ++ if (vpfe_dev->started) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ ++ /* Check for valid frame format */ ++ pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); ++ ++ if (NULL == pix_fmts) ++ return -EINVAL; ++ ++ /* store the pixel format in the device object */ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ /* First detach any IRQ if currently attached */ ++ vpfe_detach_irq(vpfe_dev); ++ vpfe_dev->fmt = *fmt; ++ /* set image capture parameters in the ccdc */ ++ ret = vpfe_config_ccdc_image_format(vpfe_dev); ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ const struct vpfe_pixel_format *pix_fmts; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); ++ ++ pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); ++ if (NULL == pix_fmts) ++ return -EINVAL; ++ return 0; ++} ++ ++/* ++ * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a ++ * given app input index ++ */ ++static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, ++ int *subdev_index, ++ int *subdev_input_index, ++ int app_input_index) ++{ ++ struct vpfe_config *cfg = vpfe_dev->cfg; ++ struct vpfe_subdev_info *sdinfo; ++ int i, j = 0; ++ ++ for (i = 0; i < cfg->num_subdevs; i++) { ++ sdinfo = &cfg->sub_devs[i]; ++ if (app_input_index < (j + sdinfo->num_inputs)) { ++ *subdev_index = i; ++ *subdev_input_index = app_input_index - j; ++ return 0; ++ } ++ j += sdinfo->num_inputs; ++ } ++ return -EINVAL; ++} ++ ++/* ++ * vpfe_get_app_input - Get app input index for a given subdev input index ++ * driver stores the input index of the current sub device and translate it ++ * when application request the current input ++ */ ++static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, ++ int *app_input_index) ++{ ++ struct vpfe_config *cfg = vpfe_dev->cfg; ++ struct vpfe_subdev_info *sdinfo; ++ int i, j = 0; ++ ++ for (i = 0; i < cfg->num_subdevs; i++) { ++ sdinfo = &cfg->sub_devs[i]; ++ if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { ++ if (vpfe_dev->current_input >= sdinfo->num_inputs) ++ return -1; ++ *app_input_index = j + vpfe_dev->current_input; ++ return 0; ++ } ++ j += sdinfo->num_inputs; ++ } ++ return -EINVAL; ++} ++ ++static int vpfe_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ int subdev, index ; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); ++ ++ if (vpfe_get_subdev_input_index(vpfe_dev, ++ &subdev, ++ &index, ++ inp->index) < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "input information not found" ++ " for the subdev\n"); ++ return -EINVAL; ++ } ++ sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; ++ memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input)); ++ return 0; ++} ++ ++static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); ++ ++ return vpfe_get_app_input_index(vpfe_dev, index); ++} ++ ++ ++static int vpfe_s_input(struct file *file, void *priv, unsigned int index) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ int subdev_index, inp_index; ++ struct vpfe_route *route; ++ u32 input = 0, output = 0; ++ int ret = -EINVAL; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ /* ++ * If streaming is started return device busy ++ * error ++ */ ++ if (vpfe_dev->started) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); ++ ret = -EBUSY; ++ goto unlock_out; ++ } ++ ret = vpfe_get_subdev_input_index(vpfe_dev, ++ &subdev_index, ++ &inp_index, ++ index); ++ if (ret < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); ++ goto unlock_out; ++ } ++ ++ sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; ++ route = &sdinfo->routes[inp_index]; ++ if (route && sdinfo->can_route) { ++ input = route->input; ++ output = route->output; ++ } ++ ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ video, s_routing, input, output, 0); ++ ++ if (ret) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "vpfe_doioctl:error in setting input in decoder\n"); ++ ret = -EINVAL; ++ goto unlock_out; ++ } ++ vpfe_dev->current_subdev = sdinfo; ++ vpfe_dev->current_input = index; ++ vpfe_dev->std_index = 0; ++ ++ /* set the bus/interface parameter for the sub device in ccdc */ ++ ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); ++ if (ret) ++ goto unlock_out; ++ ++ /* set the default image parameters in the device */ ++ ret = vpfe_config_image_format(vpfe_dev, ++ &vpfe_standards[vpfe_dev->std_index].std_id); ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ sdinfo = vpfe_dev->current_subdev; ++ if (ret) ++ return ret; ++ /* Call querystd function of decoder device */ ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ video, querystd, std_id); ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); ++ ++ /* Call decoder driver function to set the standard */ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ sdinfo = vpfe_dev->current_subdev; ++ /* If streaming is started, return device busy error */ ++ if (vpfe_dev->started) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); ++ ret = -EBUSY; ++ goto unlock_out; ++ } ++ ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ core, s_std, *std_id); ++ if (ret < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); ++ goto unlock_out; ++ } ++ ret = vpfe_config_image_format(vpfe_dev, std_id); ++ ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); ++ ++ *std_id = vpfe_standards[vpfe_dev->std_index].std_id; ++ return 0; ++} ++/* ++ * Videobuf operations ++ */ ++static int vpfe_videobuf_setup(struct videobuf_queue *vq, ++ unsigned int *count, ++ unsigned int *size) ++{ ++ struct vpfe_fh *fh = vq->priv_data; ++ struct vpfe_device *vpfe_dev = fh->vpfe_dev; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); ++ *size = vpfe_dev->fmt.fmt.pix.sizeimage; ++ if (vpfe_dev->memory == V4L2_MEMORY_MMAP && ++ vpfe_dev->fmt.fmt.pix.sizeimage > config_params.device_bufsize) ++ *size = config_params.device_bufsize; ++ ++ if (*count < config_params.min_numbuffers) ++ *count = config_params.min_numbuffers; ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "count=%d, size=%d\n", *count, *size); ++ return 0; ++} ++ ++static int vpfe_videobuf_prepare(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct vpfe_fh *fh = vq->priv_data; ++ struct vpfe_device *vpfe_dev = fh->vpfe_dev; ++ unsigned long addr; ++ int ret; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); ++ ++ /* If buffer is not initialized, initialize it */ ++ if (VIDEOBUF_NEEDS_INIT == vb->state) { ++ vb->width = vpfe_dev->fmt.fmt.pix.width; ++ vb->height = vpfe_dev->fmt.fmt.pix.height; ++ vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; ++ vb->field = field; ++ ++ ret = videobuf_iolock(vq, vb, NULL); ++ if (ret < 0) ++ return ret; ++ ++ addr = videobuf_to_dma_contig(vb); ++ /* Make sure user addresses are aligned to 32 bytes */ ++ if (!ALIGN(addr, 32)) ++ return -EINVAL; ++ ++ vb->state = VIDEOBUF_PREPARED; ++ } ++ return 0; ++} ++ ++static void vpfe_videobuf_queue(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ /* Get the file handle object and device object */ ++ struct vpfe_fh *fh = vq->priv_data; ++ struct vpfe_device *vpfe_dev = fh->vpfe_dev; ++ unsigned long flags; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); ++ ++ /* add the buffer to the DMA queue */ ++ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); ++ list_add_tail(&vb->queue, &vpfe_dev->dma_queue); ++ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); ++ ++ /* Change state of the buffer */ ++ vb->state = VIDEOBUF_QUEUED; ++} ++ ++static void vpfe_videobuf_release(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ struct vpfe_fh *fh = vq->priv_data; ++ struct vpfe_device *vpfe_dev = fh->vpfe_dev; ++ unsigned long flags; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); ++ ++ /* ++ * We need to flush the buffer from the dma queue since ++ * they are de-allocated ++ */ ++ spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); ++ INIT_LIST_HEAD(&vpfe_dev->dma_queue); ++ spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); ++ videobuf_dma_contig_free(vq, vb); ++ vb->state = VIDEOBUF_NEEDS_INIT; ++} ++ ++static struct videobuf_queue_ops vpfe_videobuf_qops = { ++ .buf_setup = vpfe_videobuf_setup, ++ .buf_prepare = vpfe_videobuf_prepare, ++ .buf_queue = vpfe_videobuf_queue, ++ .buf_release = vpfe_videobuf_release, ++}; ++ ++/* ++ * vpfe_reqbufs. currently support REQBUF only once opening ++ * the device. ++ */ ++static int vpfe_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *req_buf) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh = file->private_data; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ if (vpfe_dev->io_usrs != 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); ++ ret = -EBUSY; ++ goto unlock_out; ++ } ++ ++ vpfe_dev->memory = req_buf->memory; ++ videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, ++ &vpfe_videobuf_qops, ++ vpfe_dev->pdev, ++ &vpfe_dev->irqlock, ++ req_buf->type, ++ vpfe_dev->fmt.fmt.pix.field, ++ sizeof(struct videobuf_buffer), ++ fh, NULL); ++ ++ fh->io_allowed = 1; ++ vpfe_dev->io_usrs = 1; ++ INIT_LIST_HEAD(&vpfe_dev->dma_queue); ++ ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ ++ if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); ++ return -EINVAL; ++ } ++ /* Call videobuf_querybuf to get information */ ++ return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); ++} ++ ++static int vpfe_qbuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh = file->private_data; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ ++ /* ++ * If this file handle is not allowed to do IO, ++ * return error ++ */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); ++ return -EACCES; ++ } ++ return videobuf_qbuf(&vpfe_dev->buffer_queue, p); ++} ++ ++static int vpfe_dqbuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ return videobuf_dqbuf(&vpfe_dev->buffer_queue, ++ buf, file->f_flags & O_NONBLOCK); ++} ++ ++static int vpfe_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *qctrl) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ ++ sdinfo = vpfe_dev->current_subdev; ++ ++ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ core, queryctrl, qctrl); ++ ++} ++ ++static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ ++ sdinfo = vpfe_dev->current_subdev; ++ ++ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ core, g_ctrl, ctrl); ++} ++ ++static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_subdev_info *sdinfo; ++ ++ sdinfo = vpfe_dev->current_subdev; ++ ++ return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ core, s_ctrl, ctrl); ++} ++ ++/* ++ * vpfe_calculate_offsets : This function calculates buffers offset ++ * for top and bottom field ++ */ ++static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) ++{ ++ struct v4l2_rect image_win; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); ++ ++ ccdc_dev->hw_ops.get_image_window(&image_win); ++ vpfe_dev->field_off = image_win.height * image_win.width; ++} ++ ++/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ ++static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) ++{ ++ ccdc_dev->hw_ops.enable(1); ++ if (ccdc_dev->hw_ops.enable_out_to_sdram) ++ ccdc_dev->hw_ops.enable_out_to_sdram(1); ++ vpfe_dev->started = 1; ++} ++ ++/* ++ * vpfe_streamon. Assume the DMA queue is not empty. ++ * application is expected to call QBUF before calling ++ * this ioctl. If not, driver returns error ++ */ ++static int vpfe_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh = file->private_data; ++ struct vpfe_subdev_info *sdinfo; ++ unsigned long addr; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ ++ /* If file handle is not allowed IO, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); ++ return -EACCES; ++ } ++ ++ sdinfo = vpfe_dev->current_subdev; ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ video, s_stream, 1); ++ ++ if (ret && (ret != -ENOIOCTLCMD)) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); ++ return -EINVAL; ++ } ++ ++ /* If buffer queue is empty, return error */ ++ if (list_empty(&vpfe_dev->buffer_queue.stream)) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); ++ return -EIO; ++ } ++ ++ /* Call videobuf_streamon to start streaming * in videobuf */ ++ ret = videobuf_streamon(&vpfe_dev->buffer_queue); ++ if (ret) ++ return ret; ++ ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ goto streamoff; ++ /* Get the next frame from the buffer queue */ ++ vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, ++ struct videobuf_buffer, queue); ++ vpfe_dev->cur_frm = vpfe_dev->next_frm; ++ /* Remove buffer from the buffer queue */ ++ list_del(&vpfe_dev->cur_frm->queue); ++ /* Mark state of the current frame to active */ ++ vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; ++ /* Initialize field_id and started member */ ++ vpfe_dev->field_id = 0; ++ addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); ++ ++ /* Calculate field offset */ ++ vpfe_calculate_offsets(vpfe_dev); ++ ++ if (vpfe_attach_irq(vpfe_dev) < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "Error in attaching interrupt handle\n"); ++ ret = -EFAULT; ++ goto unlock_out; ++ } ++ if (ccdc_dev->hw_ops.configure() < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "Error in configuring ccdc\n"); ++ ret = -EINVAL; ++ goto unlock_out; ++ } ++ ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); ++ vpfe_start_ccdc_capture(vpfe_dev); ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++streamoff: ++ ret = videobuf_streamoff(&vpfe_dev->buffer_queue); ++ return ret; ++} ++ ++static int vpfe_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type buf_type) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct vpfe_fh *fh = file->private_data; ++ struct vpfe_subdev_info *sdinfo; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); ++ return -EINVAL; ++ } ++ ++ /* If io is allowed for this file handle, return error */ ++ if (!fh->io_allowed) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); ++ return -EACCES; ++ } ++ ++ /* If streaming is not started, return error */ ++ if (!vpfe_dev->started) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); ++ return -EINVAL; ++ } ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ vpfe_stop_ccdc_capture(vpfe_dev); ++ vpfe_detach_irq(vpfe_dev); ++ ++ sdinfo = vpfe_dev->current_subdev; ++ ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, ++ video, s_stream, 0); ++ ++ if (ret && (ret != -ENOIOCTLCMD)) ++ v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); ++ ret = videobuf_streamoff(&vpfe_dev->buffer_queue); ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++static int vpfe_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *crop) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); ++ ++ if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) ++ return -EINVAL; ++ ++ memset(crop, 0, sizeof(struct v4l2_cropcap)); ++ crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ crop->bounds.width = crop->defrect.width = ++ vpfe_standards[vpfe_dev->std_index].width; ++ crop->bounds.height = crop->defrect.height = ++ vpfe_standards[vpfe_dev->std_index].height; ++ crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; ++ return 0; ++} ++ ++static int vpfe_g_crop(struct file *file, void *priv, ++ struct v4l2_crop *crop) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); ++ ++ crop->c = vpfe_dev->crop; ++ return 0; ++} ++ ++static int vpfe_s_crop(struct file *file, void *priv, ++ const struct v4l2_crop *crop) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ struct v4l2_rect rect = crop->c; ++ int ret = 0; ++ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); ++ ++ if (vpfe_dev->started) { ++ /* make sure streaming is not started */ ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "Cannot change crop when streaming is ON\n"); ++ return -EBUSY; ++ } ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ if (rect.top < 0 || rect.left < 0) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "doesn't support negative values for top & left\n"); ++ ret = -EINVAL; ++ goto unlock_out; ++ } ++ ++ /* adjust the width to 16 pixel boundary */ ++ rect.width = ((rect.width + 15) & ~0xf); ++ ++ /* make sure parameters are valid */ ++ if ((rect.left + rect.width > ++ vpfe_dev->std_info.active_pixels) || ++ (rect.top + rect.height > ++ vpfe_dev->std_info.active_lines)) { ++ v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); ++ ret = -EINVAL; ++ goto unlock_out; ++ } ++ ccdc_dev->hw_ops.set_image_window(&rect); ++ vpfe_dev->fmt.fmt.pix.width = rect.width; ++ vpfe_dev->fmt.fmt.pix.height = rect.height; ++ vpfe_dev->fmt.fmt.pix.bytesperline = ++ ccdc_dev->hw_ops.get_line_length(); ++ vpfe_dev->fmt.fmt.pix.sizeimage = ++ vpfe_dev->fmt.fmt.pix.bytesperline * ++ vpfe_dev->fmt.fmt.pix.height; ++ vpfe_dev->crop = rect; ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++ ++static long vpfe_param_handler(struct file *file, void *priv, ++ bool valid_prio, int cmd, void *param) ++{ ++ struct vpfe_device *vpfe_dev = video_drvdata(file); ++ int ret = 0; ++ ++ v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); ++ ++ if (vpfe_dev->started) { ++ /* only allowed if streaming is not started */ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "device already started\n"); ++ return -EBUSY; ++ } ++ ++ ret = mutex_lock_interruptible(&vpfe_dev->lock); ++ if (ret) ++ return ret; ++ ++ switch (cmd) { ++ case VPFE_CMD_S_CCDC_RAW_PARAMS: ++ v4l2_warn(&vpfe_dev->v4l2_dev, ++ "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); ++ if (ccdc_dev->hw_ops.set_params) { ++ ret = ccdc_dev->hw_ops.set_params(param); ++ if (ret) { ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "Error setting parameters in CCDC\n"); ++ goto unlock_out; ++ } ++ ret = vpfe_get_ccdc_image_format(vpfe_dev, ++ &vpfe_dev->fmt); ++ if (ret < 0) { ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "Invalid image format at CCDC\n"); ++ goto unlock_out; ++ } ++ } else { ++ ret = -EINVAL; ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n"); ++ } ++ break; ++ default: ++ ret = -ENOTTY; ++ } ++unlock_out: ++ mutex_unlock(&vpfe_dev->lock); ++ return ret; ++} ++ ++ ++/* vpfe capture ioctl operations */ ++static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { ++ .vidioc_querycap = vpfe_querycap, ++ .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, ++ .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, ++ .vidioc_enum_input = vpfe_enum_input, ++ .vidioc_g_input = vpfe_g_input, ++ .vidioc_s_input = vpfe_s_input, ++ .vidioc_querystd = vpfe_querystd, ++ .vidioc_s_std = vpfe_s_std, ++ .vidioc_g_std = vpfe_g_std, ++ .vidioc_queryctrl = vpfe_queryctrl, ++ .vidioc_g_ctrl = vpfe_g_ctrl, ++ .vidioc_s_ctrl = vpfe_s_ctrl, ++ .vidioc_reqbufs = vpfe_reqbufs, ++ .vidioc_querybuf = vpfe_querybuf, ++ .vidioc_qbuf = vpfe_qbuf, ++ .vidioc_dqbuf = vpfe_dqbuf, ++ .vidioc_streamon = vpfe_streamon, ++ .vidioc_streamoff = vpfe_streamoff, ++ .vidioc_cropcap = vpfe_cropcap, ++ .vidioc_g_crop = vpfe_g_crop, ++ .vidioc_s_crop = vpfe_s_crop, ++ .vidioc_default = vpfe_param_handler, ++}; ++ ++static struct vpfe_device *vpfe_initialize(void) ++{ ++ struct vpfe_device *vpfe_dev; ++ ++ /* Default number of buffers should be 3 */ ++ if ((numbuffers > 0) && ++ (numbuffers < config_params.min_numbuffers)) ++ numbuffers = config_params.min_numbuffers; ++ ++ /* ++ * Set buffer size to min buffers size if invalid buffer size is ++ * given ++ */ ++ if (bufsize < config_params.min_bufsize) ++ bufsize = config_params.min_bufsize; ++ ++ config_params.numbuffers = numbuffers; ++ ++ if (numbuffers) ++ config_params.device_bufsize = bufsize; ++ ++ /* Allocate memory for device objects */ ++ vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); ++ ++ return vpfe_dev; ++} ++ ++/* ++ * vpfe_probe : This function creates device entries by register ++ * itself to the V4L2 driver and initializes fields of each ++ * device objects ++ */ ++static __devinit int vpfe_probe(struct platform_device *pdev) ++{ ++ struct vpfe_subdev_info *sdinfo; ++ struct vpfe_config *vpfe_cfg; ++ struct resource *res1; ++ struct vpfe_device *vpfe_dev; ++ struct i2c_adapter *i2c_adap; ++ struct video_device *vfd; ++ int ret = -ENOMEM, i, j; ++ int num_subdevs = 0; ++ ++ /* Get the pointer to the device object */ ++ vpfe_dev = vpfe_initialize(); ++ ++ if (!vpfe_dev) { ++ v4l2_err(pdev->dev.driver, ++ "Failed to allocate memory for vpfe_dev\n"); ++ return ret; ++ } ++ ++ vpfe_dev->pdev = &pdev->dev; ++ ++ if (NULL == pdev->dev.platform_data) { ++ v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); ++ ret = -ENODEV; ++ goto probe_free_dev_mem; ++ } ++ ++ vpfe_cfg = pdev->dev.platform_data; ++ vpfe_dev->cfg = vpfe_cfg; ++ if (NULL == vpfe_cfg->ccdc || ++ NULL == vpfe_cfg->card_name || ++ NULL == vpfe_cfg->sub_devs) { ++ v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); ++ ret = -ENOENT; ++ goto probe_free_dev_mem; ++ } ++ ++ /* Allocate memory for ccdc configuration */ ++ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); ++ if (NULL == ccdc_cfg) { ++ v4l2_err(pdev->dev.driver, ++ "Memory allocation failed for ccdc_cfg\n"); ++ goto probe_free_lock; ++ } ++ ++ mutex_lock(&ccdc_lock); ++ ++ strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); ++ /* Get VINT0 irq resource */ ++ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res1) { ++ v4l2_err(pdev->dev.driver, ++ "Unable to get interrupt for VINT0\n"); ++ ret = -ENODEV; ++ goto probe_free_ccdc_cfg_mem; ++ } ++ vpfe_dev->ccdc_irq0 = res1->start; ++ ++ /* Get VINT1 irq resource */ ++ res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ if (!res1) { ++ v4l2_err(pdev->dev.driver, ++ "Unable to get interrupt for VINT1\n"); ++ ret = -ENODEV; ++ goto probe_free_ccdc_cfg_mem; ++ } ++ vpfe_dev->ccdc_irq1 = res1->start; ++ ++ ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, ++ "vpfe_capture0", vpfe_dev); ++ ++ if (0 != ret) { ++ v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); ++ goto probe_free_ccdc_cfg_mem; ++ } ++ ++ /* Allocate memory for video device */ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) { ++ ret = -ENOMEM; ++ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); ++ goto probe_out_release_irq; ++ } ++ ++ /* Initialize field of video device */ ++ vfd->release = video_device_release; ++ vfd->fops = &vpfe_fops; ++ vfd->ioctl_ops = &vpfe_ioctl_ops; ++ vfd->tvnorms = 0; ++ vfd->current_norm = V4L2_STD_PAL; ++ vfd->v4l2_dev = &vpfe_dev->v4l2_dev; ++ snprintf(vfd->name, sizeof(vfd->name), ++ "%s_V%d.%d.%d", ++ CAPTURE_DRV_NAME, ++ (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, ++ (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, ++ (VPFE_CAPTURE_VERSION_CODE) & 0xff); ++ /* Set video_dev to the video device */ ++ vpfe_dev->video_dev = vfd; ++ ++ ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); ++ if (ret) { ++ v4l2_err(pdev->dev.driver, ++ "Unable to register v4l2 device.\n"); ++ goto probe_out_video_release; ++ } ++ v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); ++ spin_lock_init(&vpfe_dev->irqlock); ++ spin_lock_init(&vpfe_dev->dma_queue_lock); ++ mutex_init(&vpfe_dev->lock); ++ ++ /* Initialize field of the device objects */ ++ vpfe_dev->numbuffers = config_params.numbuffers; ++ ++ /* Initialize prio member of device object */ ++ v4l2_prio_init(&vpfe_dev->prio); ++ /* register video device */ ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "trying to register vpfe device.\n"); ++ v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, ++ "video_dev=%x\n", (int)&vpfe_dev->video_dev); ++ vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ ret = video_register_device(vpfe_dev->video_dev, ++ VFL_TYPE_GRABBER, -1); ++ ++ if (ret) { ++ v4l2_err(pdev->dev.driver, ++ "Unable to register video device.\n"); ++ goto probe_out_v4l2_unregister; ++ } ++ ++ v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); ++ /* set the driver data in platform device */ ++ platform_set_drvdata(pdev, vpfe_dev); ++ /* set driver private data */ ++ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); ++ i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); ++ num_subdevs = vpfe_cfg->num_subdevs; ++ vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, ++ GFP_KERNEL); ++ if (NULL == vpfe_dev->sd) { ++ v4l2_err(&vpfe_dev->v4l2_dev, ++ "unable to allocate memory for subdevice pointers\n"); ++ ret = -ENOMEM; ++ goto probe_out_video_unregister; ++ } ++ ++ for (i = 0; i < num_subdevs; i++) { ++ struct v4l2_input *inps; ++ ++ sdinfo = &vpfe_cfg->sub_devs[i]; ++ ++ /* Load up the subdevice */ ++ vpfe_dev->sd[i] = ++ v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, ++ i2c_adap, ++ &sdinfo->board_info, ++ NULL); ++ if (vpfe_dev->sd[i]) { ++ v4l2_info(&vpfe_dev->v4l2_dev, ++ "v4l2 sub device %s registered\n", ++ sdinfo->name); ++ vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; ++ /* update tvnorms from the sub devices */ ++ for (j = 0; j < sdinfo->num_inputs; j++) { ++ inps = &sdinfo->inputs[j]; ++ vfd->tvnorms |= inps->std; ++ } ++ } else { ++ v4l2_info(&vpfe_dev->v4l2_dev, ++ "v4l2 sub device %s register fails\n", ++ sdinfo->name); ++ goto probe_sd_out; ++ } ++ } ++ ++ /* set first sub device as current one */ ++ vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; ++ ++ /* We have at least one sub device to work with */ ++ mutex_unlock(&ccdc_lock); ++ return 0; ++ ++probe_sd_out: ++ kfree(vpfe_dev->sd); ++probe_out_video_unregister: ++ video_unregister_device(vpfe_dev->video_dev); ++probe_out_v4l2_unregister: ++ v4l2_device_unregister(&vpfe_dev->v4l2_dev); ++probe_out_video_release: ++ if (!video_is_registered(vpfe_dev->video_dev)) ++ video_device_release(vpfe_dev->video_dev); ++probe_out_release_irq: ++ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); ++probe_free_ccdc_cfg_mem: ++ kfree(ccdc_cfg); ++probe_free_lock: ++ mutex_unlock(&ccdc_lock); ++probe_free_dev_mem: ++ kfree(vpfe_dev); ++ return ret; ++} ++ ++/* ++ * vpfe_remove : It un-register device from V4L2 driver ++ */ ++static int __devexit vpfe_remove(struct platform_device *pdev) ++{ ++ struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); ++ ++ v4l2_info(pdev->dev.driver, "vpfe_remove\n"); ++ ++ free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); ++ kfree(vpfe_dev->sd); ++ v4l2_device_unregister(&vpfe_dev->v4l2_dev); ++ video_unregister_device(vpfe_dev->video_dev); ++ kfree(vpfe_dev); ++ kfree(ccdc_cfg); ++ return 0; ++} ++ ++static int vpfe_suspend(struct device *dev) ++{ ++ return 0; ++} ++ ++static int vpfe_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static const struct dev_pm_ops vpfe_dev_pm_ops = { ++ .suspend = vpfe_suspend, ++ .resume = vpfe_resume, ++}; ++ ++static struct platform_driver vpfe_driver = { ++ .driver = { ++ .name = CAPTURE_DRV_NAME, ++ .owner = THIS_MODULE, ++ .pm = &vpfe_dev_pm_ops, ++ }, ++ .probe = vpfe_probe, ++ .remove = __devexit_p(vpfe_remove), ++}; ++ ++module_platform_driver(vpfe_driver); +diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c +new file mode 100644 +index 0000000..0d6cc8e +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif.c +@@ -0,0 +1,516 @@ ++/* ++ * vpif - Video Port Interface driver ++ * VPIF is a receiver and transmitter for video data. It has two channels(0, 1) ++ * that receiveing video byte stream and two channels(2, 3) for video output. ++ * The hardware supports SDTV, HDTV formats, raw data capture. ++ * Currently, the driver supports NTSC and PAL standards. ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "vpif.h" ++ ++MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver"); ++MODULE_LICENSE("GPL"); ++ ++#define VPIF_CH0_MAX_MODES (22) ++#define VPIF_CH1_MAX_MODES (02) ++#define VPIF_CH2_MAX_MODES (15) ++#define VPIF_CH3_MAX_MODES (02) ++ ++static resource_size_t res_len; ++static struct resource *res; ++spinlock_t vpif_lock; ++ ++void __iomem *vpif_base; ++struct clk *vpif_clk; ++ ++/** ++ * ch_params: video standard configuration parameters for vpif ++ * The table must include all presets from supported subdevices. ++ */ ++const struct vpif_channel_config_params ch_params[] = { ++ /* HDTV formats */ ++ { ++ .name = "480p59_94", ++ .width = 720, ++ .height = 480, ++ .frm_fmt = 1, ++ .ycmux_mode = 0, ++ .eav2sav = 138-8, ++ .sav2eav = 720, ++ .l1 = 1, ++ .l3 = 43, ++ .l5 = 523, ++ .vsize = 525, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_720X480P59_94, ++ }, ++ { ++ .name = "576p50", ++ .width = 720, ++ .height = 576, ++ .frm_fmt = 1, ++ .ycmux_mode = 0, ++ .eav2sav = 144-8, ++ .sav2eav = 720, ++ .l1 = 1, ++ .l3 = 45, ++ .l5 = 621, ++ .vsize = 625, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_720X576P50, ++ }, ++ { ++ .name = "720p50", ++ .width = 1280, ++ .height = 720, ++ .frm_fmt = 1, ++ .ycmux_mode = 0, ++ .eav2sav = 700-8, ++ .sav2eav = 1280, ++ .l1 = 1, ++ .l3 = 26, ++ .l5 = 746, ++ .vsize = 750, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_1280X720P50, ++ }, ++ { ++ .name = "720p60", ++ .width = 1280, ++ .height = 720, ++ .frm_fmt = 1, ++ .ycmux_mode = 0, ++ .eav2sav = 370 - 8, ++ .sav2eav = 1280, ++ .l1 = 1, ++ .l3 = 26, ++ .l5 = 746, ++ .vsize = 750, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_1280X720P60, ++ }, ++ { ++ .name = "1080I50", ++ .width = 1920, ++ .height = 1080, ++ .frm_fmt = 0, ++ .ycmux_mode = 0, ++ .eav2sav = 720 - 8, ++ .sav2eav = 1920, ++ .l1 = 1, ++ .l3 = 21, ++ .l5 = 561, ++ .l7 = 563, ++ .l9 = 584, ++ .l11 = 1124, ++ .vsize = 1125, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_1920X1080I50, ++ }, ++ { ++ .name = "1080I60", ++ .width = 1920, ++ .height = 1080, ++ .frm_fmt = 0, ++ .ycmux_mode = 0, ++ .eav2sav = 280 - 8, ++ .sav2eav = 1920, ++ .l1 = 1, ++ .l3 = 21, ++ .l5 = 561, ++ .l7 = 563, ++ .l9 = 584, ++ .l11 = 1124, ++ .vsize = 1125, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_1920X1080I60, ++ }, ++ { ++ .name = "1080p60", ++ .width = 1920, ++ .height = 1080, ++ .frm_fmt = 1, ++ .ycmux_mode = 0, ++ .eav2sav = 280 - 8, ++ .sav2eav = 1920, ++ .l1 = 1, ++ .l3 = 42, ++ .l5 = 1122, ++ .vsize = 1125, ++ .capture_format = 0, ++ .vbi_supported = 0, ++ .hd_sd = 1, ++ .dv_timings = V4L2_DV_BT_CEA_1920X1080P60, ++ }, ++ ++ /* SDTV formats */ ++ { ++ .name = "NTSC_M", ++ .width = 720, ++ .height = 480, ++ .frm_fmt = 0, ++ .ycmux_mode = 1, ++ .eav2sav = 268, ++ .sav2eav = 1440, ++ .l1 = 1, ++ .l3 = 23, ++ .l5 = 263, ++ .l7 = 266, ++ .l9 = 286, ++ .l11 = 525, ++ .vsize = 525, ++ .capture_format = 0, ++ .vbi_supported = 1, ++ .hd_sd = 0, ++ .stdid = V4L2_STD_525_60, ++ }, ++ { ++ .name = "PAL_BDGHIK", ++ .width = 720, ++ .height = 576, ++ .frm_fmt = 0, ++ .ycmux_mode = 1, ++ .eav2sav = 280, ++ .sav2eav = 1440, ++ .l1 = 1, ++ .l3 = 23, ++ .l5 = 311, ++ .l7 = 313, ++ .l9 = 336, ++ .l11 = 624, ++ .vsize = 625, ++ .capture_format = 0, ++ .vbi_supported = 1, ++ .hd_sd = 0, ++ .stdid = V4L2_STD_625_50, ++ }, ++}; ++ ++const unsigned int vpif_ch_params_count = ARRAY_SIZE(ch_params); ++ ++static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) ++{ ++ if (val) ++ vpif_set_bit(reg, bit); ++ else ++ vpif_clr_bit(reg, bit); ++} ++ ++/* This structure is used to keep track of VPIF size register's offsets */ ++struct vpif_registers { ++ u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl; ++ u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt; ++ u32 vanc1_size, width_mask, len_mask; ++ u8 max_modes; ++}; ++ ++static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = { ++ /* Channel0 */ ++ { ++ VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01, ++ VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL, ++ VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, ++ VPIF_CH0_MAX_MODES, ++ }, ++ /* Channel1 */ ++ { ++ VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01, ++ VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL, ++ VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, ++ VPIF_CH1_MAX_MODES, ++ }, ++ /* Channel2 */ ++ { ++ VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01, ++ VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL, ++ VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE, ++ VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF, ++ VPIF_CH2_MAX_MODES ++ }, ++ /* Channel3 */ ++ { ++ VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01, ++ VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL, ++ VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE, ++ VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF, ++ VPIF_CH3_MAX_MODES ++ }, ++}; ++ ++/* vpif_set_mode_info: ++ * This function is used to set horizontal and vertical config parameters ++ * As per the standard in the channel, configure the values of L1, L3, ++ * L5, L7 L9, L11 in VPIF Register , also write width and height ++ */ ++static void vpif_set_mode_info(const struct vpif_channel_config_params *config, ++ u8 channel_id, u8 config_channel_id) ++{ ++ u32 value; ++ ++ value = (config->eav2sav & vpifregs[config_channel_id].width_mask); ++ value <<= VPIF_CH_LEN_SHIFT; ++ value |= (config->sav2eav & vpifregs[config_channel_id].width_mask); ++ regw(value, vpifregs[channel_id].h_cfg); ++ ++ value = (config->l1 & vpifregs[config_channel_id].len_mask); ++ value <<= VPIF_CH_LEN_SHIFT; ++ value |= (config->l3 & vpifregs[config_channel_id].len_mask); ++ regw(value, vpifregs[channel_id].v_cfg_00); ++ ++ value = (config->l5 & vpifregs[config_channel_id].len_mask); ++ value <<= VPIF_CH_LEN_SHIFT; ++ value |= (config->l7 & vpifregs[config_channel_id].len_mask); ++ regw(value, vpifregs[channel_id].v_cfg_01); ++ ++ value = (config->l9 & vpifregs[config_channel_id].len_mask); ++ value <<= VPIF_CH_LEN_SHIFT; ++ value |= (config->l11 & vpifregs[config_channel_id].len_mask); ++ regw(value, vpifregs[channel_id].v_cfg_02); ++ ++ value = (config->vsize & vpifregs[config_channel_id].len_mask); ++ regw(value, vpifregs[channel_id].v_cfg); ++} ++ ++/* config_vpif_params ++ * Function to set the parameters of a channel ++ * Mainly modifies the channel ciontrol register ++ * It sets frame format, yc mux mode ++ */ ++static void config_vpif_params(struct vpif_params *vpifparams, ++ u8 channel_id, u8 found) ++{ ++ const struct vpif_channel_config_params *config = &vpifparams->std_info; ++ u32 value, ch_nip, reg; ++ u8 start, end; ++ int i; ++ ++ start = channel_id; ++ end = channel_id + found; ++ ++ for (i = start; i < end; i++) { ++ reg = vpifregs[i].ch_ctrl; ++ if (channel_id < 2) ++ ch_nip = VPIF_CAPTURE_CH_NIP; ++ else ++ ch_nip = VPIF_DISPLAY_CH_NIP; ++ ++ vpif_wr_bit(reg, ch_nip, config->frm_fmt); ++ vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode); ++ vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT, ++ vpifparams->video_params.storage_mode); ++ ++ /* Set raster scanning SDR Format */ ++ vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT); ++ vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format); ++ ++ if (channel_id > 1) /* Set the Pixel enable bit */ ++ vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT); ++ else if (config->capture_format) { ++ /* Set the polarity of various pins */ ++ vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT, ++ vpifparams->iface.fid_pol); ++ vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT, ++ vpifparams->iface.vd_pol); ++ vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT, ++ vpifparams->iface.hd_pol); ++ ++ value = regr(reg); ++ /* Set data width */ ++ value &= ~(0x3u << ++ VPIF_CH_DATA_WIDTH_BIT); ++ value |= ((vpifparams->params.data_sz) << ++ VPIF_CH_DATA_WIDTH_BIT); ++ regw(value, reg); ++ } ++ ++ /* Write the pitch in the driver */ ++ regw((vpifparams->video_params.hpitch), ++ vpifregs[i].line_offset); ++ } ++} ++ ++/* vpif_set_video_params ++ * This function is used to set video parameters in VPIF register ++ */ ++int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id) ++{ ++ const struct vpif_channel_config_params *config = &vpifparams->std_info; ++ int found = 1; ++ ++ vpif_set_mode_info(config, channel_id, channel_id); ++ if (!config->ycmux_mode) { ++ /* YC are on separate channels (HDTV formats) */ ++ vpif_set_mode_info(config, channel_id + 1, channel_id); ++ found = 2; ++ } ++ ++ config_vpif_params(vpifparams, channel_id, found); ++ ++ regw(0x80, VPIF_REQ_SIZE); ++ regw(0x01, VPIF_EMULATION_CTRL); ++ ++ return found; ++} ++EXPORT_SYMBOL(vpif_set_video_params); ++ ++void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, ++ u8 channel_id) ++{ ++ u32 value; ++ ++ value = 0x3F8 & (vbiparams->hstart0); ++ value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16); ++ regw(value, vpifregs[channel_id].vanc0_strt); ++ ++ value = 0x3F8 & (vbiparams->hstart1); ++ value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16); ++ regw(value, vpifregs[channel_id].vanc1_strt); ++ ++ value = 0x3F8 & (vbiparams->hsize0); ++ value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16); ++ regw(value, vpifregs[channel_id].vanc0_size); ++ ++ value = 0x3F8 & (vbiparams->hsize1); ++ value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16); ++ regw(value, vpifregs[channel_id].vanc1_size); ++ ++} ++EXPORT_SYMBOL(vpif_set_vbi_display_params); ++ ++int vpif_channel_getfid(u8 channel_id) ++{ ++ return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK) ++ >> VPIF_CH_FID_SHIFT; ++} ++EXPORT_SYMBOL(vpif_channel_getfid); ++ ++static int __devinit vpif_probe(struct platform_device *pdev) ++{ ++ int status = 0; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -ENOENT; ++ ++ res_len = resource_size(res); ++ ++ res = request_mem_region(res->start, res_len, res->name); ++ if (!res) ++ return -EBUSY; ++ ++ vpif_base = ioremap(res->start, res_len); ++ if (!vpif_base) { ++ status = -EBUSY; ++ goto fail; ++ } ++ ++ vpif_clk = clk_get(&pdev->dev, "vpif"); ++ if (IS_ERR(vpif_clk)) { ++ status = PTR_ERR(vpif_clk); ++ goto clk_fail; ++ } ++ clk_prepare_enable(vpif_clk); ++ ++ spin_lock_init(&vpif_lock); ++ dev_info(&pdev->dev, "vpif probe success\n"); ++ return 0; ++ ++clk_fail: ++ iounmap(vpif_base); ++fail: ++ release_mem_region(res->start, res_len); ++ return status; ++} ++ ++static int __devexit vpif_remove(struct platform_device *pdev) ++{ ++ if (vpif_clk) { ++ clk_disable_unprepare(vpif_clk); ++ clk_put(vpif_clk); ++ } ++ ++ iounmap(vpif_base); ++ release_mem_region(res->start, res_len); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int vpif_suspend(struct device *dev) ++{ ++ clk_disable_unprepare(vpif_clk); ++ return 0; ++} ++ ++static int vpif_resume(struct device *dev) ++{ ++ clk_prepare_enable(vpif_clk); ++ return 0; ++} ++ ++static const struct dev_pm_ops vpif_pm = { ++ .suspend = vpif_suspend, ++ .resume = vpif_resume, ++}; ++ ++#define vpif_pm_ops (&vpif_pm) ++#else ++#define vpif_pm_ops NULL ++#endif ++ ++static struct platform_driver vpif_driver = { ++ .driver = { ++ .name = "vpif", ++ .owner = THIS_MODULE, ++ .pm = vpif_pm_ops, ++ }, ++ .remove = __devexit_p(vpif_remove), ++ .probe = vpif_probe, ++}; ++ ++static void vpif_exit(void) ++{ ++ platform_driver_unregister(&vpif_driver); ++} ++ ++static int __init vpif_init(void) ++{ ++ return platform_driver_register(&vpif_driver); ++} ++subsys_initcall(vpif_init); ++module_exit(vpif_exit); ++ +diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/davinci/vpif.h +new file mode 100644 +index 0000000..a1ab6a0 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif.h +@@ -0,0 +1,688 @@ ++/* ++ * VPIF header file ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef VPIF_H ++#define VPIF_H ++ ++#include ++#include ++#include ++ ++/* Maximum channel allowed */ ++#define VPIF_NUM_CHANNELS (4) ++#define VPIF_CAPTURE_NUM_CHANNELS (2) ++#define VPIF_DISPLAY_NUM_CHANNELS (2) ++ ++/* Macros to read/write registers */ ++extern void __iomem *vpif_base; ++extern spinlock_t vpif_lock; ++ ++#define regr(reg) readl((reg) + vpif_base) ++#define regw(value, reg) writel(value, (reg + vpif_base)) ++ ++/* Register Address Offsets */ ++#define VPIF_PID (0x0000) ++#define VPIF_CH0_CTRL (0x0004) ++#define VPIF_CH1_CTRL (0x0008) ++#define VPIF_CH2_CTRL (0x000C) ++#define VPIF_CH3_CTRL (0x0010) ++ ++#define VPIF_INTEN (0x0020) ++#define VPIF_INTEN_SET (0x0024) ++#define VPIF_INTEN_CLR (0x0028) ++#define VPIF_STATUS (0x002C) ++#define VPIF_STATUS_CLR (0x0030) ++#define VPIF_EMULATION_CTRL (0x0034) ++#define VPIF_REQ_SIZE (0x0038) ++ ++#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040) ++#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044) ++#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048) ++#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c) ++#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050) ++#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054) ++#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058) ++#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c) ++#define VPIF_CH0_SP_CFG (0x0060) ++#define VPIF_CH0_IMG_ADD_OFST (0x0064) ++#define VPIF_CH0_HANC_ADD_OFST (0x0068) ++#define VPIF_CH0_H_CFG (0x006c) ++#define VPIF_CH0_V_CFG_00 (0x0070) ++#define VPIF_CH0_V_CFG_01 (0x0074) ++#define VPIF_CH0_V_CFG_02 (0x0078) ++#define VPIF_CH0_V_CFG_03 (0x007c) ++ ++#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080) ++#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084) ++#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088) ++#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c) ++#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090) ++#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094) ++#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098) ++#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c) ++#define VPIF_CH1_SP_CFG (0x00a0) ++#define VPIF_CH1_IMG_ADD_OFST (0x00a4) ++#define VPIF_CH1_HANC_ADD_OFST (0x00a8) ++#define VPIF_CH1_H_CFG (0x00ac) ++#define VPIF_CH1_V_CFG_00 (0x00b0) ++#define VPIF_CH1_V_CFG_01 (0x00b4) ++#define VPIF_CH1_V_CFG_02 (0x00b8) ++#define VPIF_CH1_V_CFG_03 (0x00bc) ++ ++#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0) ++#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4) ++#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8) ++#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc) ++#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0) ++#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4) ++#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8) ++#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc) ++#define VPIF_CH2_SP_CFG (0x00e0) ++#define VPIF_CH2_IMG_ADD_OFST (0x00e4) ++#define VPIF_CH2_HANC_ADD_OFST (0x00e8) ++#define VPIF_CH2_H_CFG (0x00ec) ++#define VPIF_CH2_V_CFG_00 (0x00f0) ++#define VPIF_CH2_V_CFG_01 (0x00f4) ++#define VPIF_CH2_V_CFG_02 (0x00f8) ++#define VPIF_CH2_V_CFG_03 (0x00fc) ++#define VPIF_CH2_HANC0_STRT (0x0100) ++#define VPIF_CH2_HANC0_SIZE (0x0104) ++#define VPIF_CH2_HANC1_STRT (0x0108) ++#define VPIF_CH2_HANC1_SIZE (0x010c) ++#define VPIF_CH2_VANC0_STRT (0x0110) ++#define VPIF_CH2_VANC0_SIZE (0x0114) ++#define VPIF_CH2_VANC1_STRT (0x0118) ++#define VPIF_CH2_VANC1_SIZE (0x011c) ++ ++#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140) ++#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144) ++#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148) ++#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c) ++#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150) ++#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154) ++#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158) ++#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c) ++#define VPIF_CH3_SP_CFG (0x0160) ++#define VPIF_CH3_IMG_ADD_OFST (0x0164) ++#define VPIF_CH3_HANC_ADD_OFST (0x0168) ++#define VPIF_CH3_H_CFG (0x016c) ++#define VPIF_CH3_V_CFG_00 (0x0170) ++#define VPIF_CH3_V_CFG_01 (0x0174) ++#define VPIF_CH3_V_CFG_02 (0x0178) ++#define VPIF_CH3_V_CFG_03 (0x017c) ++#define VPIF_CH3_HANC0_STRT (0x0180) ++#define VPIF_CH3_HANC0_SIZE (0x0184) ++#define VPIF_CH3_HANC1_STRT (0x0188) ++#define VPIF_CH3_HANC1_SIZE (0x018c) ++#define VPIF_CH3_VANC0_STRT (0x0190) ++#define VPIF_CH3_VANC0_SIZE (0x0194) ++#define VPIF_CH3_VANC1_STRT (0x0198) ++#define VPIF_CH3_VANC1_SIZE (0x019c) ++ ++#define VPIF_IODFT_CTRL (0x01c0) ++ ++/* Functions for bit Manipulation */ ++static inline void vpif_set_bit(u32 reg, u32 bit) ++{ ++ regw((regr(reg)) | (0x01 << bit), reg); ++} ++ ++static inline void vpif_clr_bit(u32 reg, u32 bit) ++{ ++ regw(((regr(reg)) & ~(0x01 << bit)), reg); ++} ++ ++/* Macro for Generating mask */ ++#ifdef GENERATE_MASK ++#undef GENERATE_MASK ++#endif ++ ++#define GENERATE_MASK(bits, pos) \ ++ ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos) ++ ++/* Bit positions in the channel control registers */ ++#define VPIF_CH_DATA_MODE_BIT (2) ++#define VPIF_CH_YC_MUX_BIT (3) ++#define VPIF_CH_SDR_FMT_BIT (4) ++#define VPIF_CH_HANC_EN_BIT (8) ++#define VPIF_CH_VANC_EN_BIT (9) ++ ++#define VPIF_CAPTURE_CH_NIP (10) ++#define VPIF_DISPLAY_CH_NIP (11) ++ ++#define VPIF_DISPLAY_PIX_EN_BIT (10) ++ ++#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12) ++ ++#define VPIF_CH_FID_POLARITY_BIT (15) ++#define VPIF_CH_V_VALID_POLARITY_BIT (14) ++#define VPIF_CH_H_VALID_POLARITY_BIT (13) ++#define VPIF_CH_DATA_WIDTH_BIT (28) ++ ++#define VPIF_CH_CLK_EDGE_CTRL_BIT (31) ++ ++/* Mask various length */ ++#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0) ++#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0) ++#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0) ++#define VPIF_CH_LEN_SHIFT (16) ++ ++/* VPIF masks for registers */ ++#define VPIF_REQ_SIZE_MASK (0x1ff) ++ ++/* bit posotion of interrupt vpif_ch_intr register */ ++#define VPIF_INTEN_FRAME_CH0 (0x00000001) ++#define VPIF_INTEN_FRAME_CH1 (0x00000002) ++#define VPIF_INTEN_FRAME_CH2 (0x00000004) ++#define VPIF_INTEN_FRAME_CH3 (0x00000008) ++ ++/* bit position of clock and channel enable in vpif_chn_ctrl register */ ++ ++#define VPIF_CH0_CLK_EN (0x00000002) ++#define VPIF_CH0_EN (0x00000001) ++#define VPIF_CH1_CLK_EN (0x00000002) ++#define VPIF_CH1_EN (0x00000001) ++#define VPIF_CH2_CLK_EN (0x00000002) ++#define VPIF_CH2_EN (0x00000001) ++#define VPIF_CH3_CLK_EN (0x00000002) ++#define VPIF_CH3_EN (0x00000001) ++#define VPIF_CH_CLK_EN (0x00000002) ++#define VPIF_CH_EN (0x00000001) ++ ++#define VPIF_INT_TOP (0x00) ++#define VPIF_INT_BOTTOM (0x01) ++#define VPIF_INT_BOTH (0x02) ++ ++#define VPIF_CH0_INT_CTRL_SHIFT (6) ++#define VPIF_CH1_INT_CTRL_SHIFT (6) ++#define VPIF_CH2_INT_CTRL_SHIFT (6) ++#define VPIF_CH3_INT_CTRL_SHIFT (6) ++#define VPIF_CH_INT_CTRL_SHIFT (6) ++ ++#define VPIF_CH2_CLIP_ANC_EN 14 ++#define VPIF_CH2_CLIP_ACTIVE_EN 13 ++ ++#define VPIF_CH3_CLIP_ANC_EN 14 ++#define VPIF_CH3_CLIP_ACTIVE_EN 13 ++ ++/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ ++#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\ ++ (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL)) ++ ++/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ ++#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\ ++ (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL)) ++ ++/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ ++#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\ ++ (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL)) ++ ++/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ ++#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\ ++ (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL)) ++ ++#define VPIF_CH_FID_MASK (0x20) ++#define VPIF_CH_FID_SHIFT (5) ++ ++#define VPIF_NTSC_VBI_START_FIELD0 (1) ++#define VPIF_NTSC_VBI_START_FIELD1 (263) ++#define VPIF_PAL_VBI_START_FIELD0 (624) ++#define VPIF_PAL_VBI_START_FIELD1 (311) ++ ++#define VPIF_NTSC_HBI_START_FIELD0 (1) ++#define VPIF_NTSC_HBI_START_FIELD1 (263) ++#define VPIF_PAL_HBI_START_FIELD0 (624) ++#define VPIF_PAL_HBI_START_FIELD1 (311) ++ ++#define VPIF_NTSC_VBI_COUNT_FIELD0 (20) ++#define VPIF_NTSC_VBI_COUNT_FIELD1 (19) ++#define VPIF_PAL_VBI_COUNT_FIELD0 (24) ++#define VPIF_PAL_VBI_COUNT_FIELD1 (25) ++ ++#define VPIF_NTSC_HBI_COUNT_FIELD0 (263) ++#define VPIF_NTSC_HBI_COUNT_FIELD1 (262) ++#define VPIF_PAL_HBI_COUNT_FIELD0 (312) ++#define VPIF_PAL_HBI_COUNT_FIELD1 (313) ++ ++#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720) ++#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720) ++#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268) ++#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280) ++ ++#define VPIF_CH_VANC_EN (0x20) ++#define VPIF_DMA_REQ_SIZE (0x080) ++#define VPIF_EMULATION_DISABLE (0x01) ++ ++extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS]; ++ ++/* inline function to enable/disable channel0 */ ++static inline void enable_channel0(int enable) ++{ ++ if (enable) ++ regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL); ++ else ++ regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL); ++} ++ ++/* inline function to enable/disable channel1 */ ++static inline void enable_channel1(int enable) ++{ ++ if (enable) ++ regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL); ++ else ++ regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL); ++} ++ ++/* inline function to enable interrupt for channel0 */ ++static inline void channel0_intr_enable(int enable) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpif_lock, flags); ++ ++ if (enable) { ++ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); ++ ++ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), ++ VPIF_INTEN_SET); ++ } else { ++ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), ++ VPIF_INTEN_SET); ++ } ++ spin_unlock_irqrestore(&vpif_lock, flags); ++} ++ ++/* inline function to enable interrupt for channel1 */ ++static inline void channel1_intr_enable(int enable) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpif_lock, flags); ++ ++ if (enable) { ++ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); ++ ++ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), ++ VPIF_INTEN_SET); ++ } else { ++ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), ++ VPIF_INTEN_SET); ++ } ++ spin_unlock_irqrestore(&vpif_lock, flags); ++} ++ ++/* inline function to set buffer addresses in case of Y/C non mux mode */ ++static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); ++} ++ ++/* inline function to set buffer addresses in VPIF registers for video data */ ++static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA); ++} ++ ++static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ ++ regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); ++} ++ ++static inline void ch0_set_vbi_addr(unsigned long top_vbi, ++ unsigned long btm_vbi, unsigned long a, unsigned long b) ++{ ++ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC); ++ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC); ++} ++ ++static inline void ch0_set_hbi_addr(unsigned long top_vbi, ++ unsigned long btm_vbi, unsigned long a, unsigned long b) ++{ ++ regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC); ++ regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC); ++} ++ ++static inline void ch1_set_vbi_addr(unsigned long top_vbi, ++ unsigned long btm_vbi, unsigned long a, unsigned long b) ++{ ++ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC); ++ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC); ++} ++ ++static inline void ch1_set_hbi_addr(unsigned long top_vbi, ++ unsigned long btm_vbi, unsigned long a, unsigned long b) ++{ ++ regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC); ++ regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC); ++} ++ ++/* Inline function to enable raw vbi in the given channel */ ++static inline void disable_raw_feature(u8 channel_id, u8 index) ++{ ++ u32 ctrl_reg; ++ if (0 == channel_id) ++ ctrl_reg = VPIF_CH0_CTRL; ++ else ++ ctrl_reg = VPIF_CH1_CTRL; ++ ++ if (1 == index) ++ vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); ++ else ++ vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); ++} ++ ++static inline void enable_raw_feature(u8 channel_id, u8 index) ++{ ++ u32 ctrl_reg; ++ if (0 == channel_id) ++ ctrl_reg = VPIF_CH0_CTRL; ++ else ++ ctrl_reg = VPIF_CH1_CTRL; ++ ++ if (1 == index) ++ vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); ++ else ++ vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); ++} ++ ++/* inline function to enable/disable channel2 */ ++static inline void enable_channel2(int enable) ++{ ++ if (enable) { ++ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); ++ regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL); ++ } else { ++ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); ++ regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL); ++ } ++} ++ ++/* inline function to enable/disable channel3 */ ++static inline void enable_channel3(int enable) ++{ ++ if (enable) { ++ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); ++ regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL); ++ } else { ++ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); ++ regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL); ++ } ++} ++ ++/* inline function to enable interrupt for channel2 */ ++static inline void channel2_intr_enable(int enable) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpif_lock, flags); ++ ++ if (enable) { ++ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); ++ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), ++ VPIF_INTEN_SET); ++ } else { ++ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), ++ VPIF_INTEN_SET); ++ } ++ spin_unlock_irqrestore(&vpif_lock, flags); ++} ++ ++/* inline function to enable interrupt for channel3 */ ++static inline void channel3_intr_enable(int enable) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpif_lock, flags); ++ ++ if (enable) { ++ regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); ++ ++ regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), ++ VPIF_INTEN_SET); ++ } else { ++ regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN); ++ regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), ++ VPIF_INTEN_SET); ++ } ++ spin_unlock_irqrestore(&vpif_lock, flags); ++} ++ ++/* inline function to enable raw vbi data for channel2 */ ++static inline void channel2_raw_enable(int enable, u8 index) ++{ ++ u32 mask; ++ ++ if (1 == index) ++ mask = VPIF_CH_VANC_EN_BIT; ++ else ++ mask = VPIF_CH_HANC_EN_BIT; ++ ++ if (enable) ++ vpif_set_bit(VPIF_CH2_CTRL, mask); ++ else ++ vpif_clr_bit(VPIF_CH2_CTRL, mask); ++} ++ ++/* inline function to enable raw vbi data for channel3*/ ++static inline void channel3_raw_enable(int enable, u8 index) ++{ ++ u32 mask; ++ ++ if (1 == index) ++ mask = VPIF_CH_VANC_EN_BIT; ++ else ++ mask = VPIF_CH_HANC_EN_BIT; ++ ++ if (enable) ++ vpif_set_bit(VPIF_CH3_CTRL, mask); ++ else ++ vpif_clr_bit(VPIF_CH3_CTRL, mask); ++} ++ ++/* function to enable clipping (for both active and blanking regions) on ch 2 */ ++static inline void channel2_clipping_enable(int enable) ++{ ++ if (enable) { ++ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN); ++ vpif_set_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN); ++ } else { ++ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ANC_EN); ++ vpif_clr_bit(VPIF_CH2_CTRL, VPIF_CH2_CLIP_ACTIVE_EN); ++ } ++} ++ ++/* function to enable clipping (for both active and blanking regions) on ch 3 */ ++static inline void channel3_clipping_enable(int enable) ++{ ++ if (enable) { ++ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN); ++ vpif_set_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN); ++ } else { ++ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ANC_EN); ++ vpif_clr_bit(VPIF_CH3_CTRL, VPIF_CH3_CLIP_ACTIVE_EN); ++ } ++} ++ ++/* inline function to set buffer addresses in case of Y/C non mux mode */ ++static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); ++} ++ ++/* inline function to set buffer addresses in VPIF registers for video data */ ++static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA); ++} ++ ++static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA); ++ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA); ++ regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); ++ regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); ++} ++ ++/* inline function to set buffer addresses in VPIF registers for vbi data */ ++static inline void ch2_set_vbi_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC); ++ regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC); ++} ++ ++static inline void ch3_set_vbi_addr(unsigned long top_strt_luma, ++ unsigned long btm_strt_luma, ++ unsigned long top_strt_chroma, ++ unsigned long btm_strt_chroma) ++{ ++ regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC); ++ regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC); ++} ++ ++static inline int vpif_intr_status(int channel) ++{ ++ int status = 0; ++ int mask; ++ ++ if (channel < 0 || channel > 3) ++ return 0; ++ ++ mask = 1 << channel; ++ status = regr(VPIF_STATUS) & mask; ++ regw(status, VPIF_STATUS_CLR); ++ ++ return status; ++} ++ ++#define VPIF_MAX_NAME (30) ++ ++/* This structure will store size parameters as per the mode selected by user */ ++struct vpif_channel_config_params { ++ char name[VPIF_MAX_NAME]; /* Name of the mode */ ++ u16 width; /* Indicates width of the image */ ++ u16 height; /* Indicates height of the image */ ++ u8 frm_fmt; /* Interlaced (0) or progressive (1) */ ++ u8 ycmux_mode; /* This mode requires one (0) or two (1) ++ channels */ ++ u16 eav2sav; /* length of eav 2 sav */ ++ u16 sav2eav; /* length of sav 2 eav */ ++ u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */ ++ u16 vsize; /* Vertical size of the image */ ++ u8 capture_format; /* Indicates whether capture format ++ * is in BT or in CCD/CMOS */ ++ u8 vbi_supported; /* Indicates whether this mode ++ * supports capturing vbi or not */ ++ u8 hd_sd; /* HDTV (1) or SDTV (0) format */ ++ v4l2_std_id stdid; /* SDTV format */ ++ struct v4l2_dv_timings dv_timings; /* HDTV format */ ++}; ++ ++extern const unsigned int vpif_ch_params_count; ++extern const struct vpif_channel_config_params ch_params[]; ++ ++struct vpif_video_params; ++struct vpif_params; ++struct vpif_vbi_params; ++ ++int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id); ++void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, ++ u8 channel_id); ++int vpif_channel_getfid(u8 channel_id); ++ ++enum data_size { ++ _8BITS = 0, ++ _10BITS, ++ _12BITS, ++}; ++ ++/* Structure for vpif parameters for raw vbi data */ ++struct vpif_vbi_params { ++ __u32 hstart0; /* Horizontal start of raw vbi data for first field */ ++ __u32 vstart0; /* Vertical start of raw vbi data for first field */ ++ __u32 hsize0; /* Horizontal size of raw vbi data for first field */ ++ __u32 vsize0; /* Vertical size of raw vbi data for first field */ ++ __u32 hstart1; /* Horizontal start of raw vbi data for second field */ ++ __u32 vstart1; /* Vertical start of raw vbi data for second field */ ++ __u32 hsize1; /* Horizontal size of raw vbi data for second field */ ++ __u32 vsize1; /* Vertical size of raw vbi data for second field */ ++}; ++ ++/* structure for vpif parameters */ ++struct vpif_video_params { ++ __u8 storage_mode; /* Indicates field or frame mode */ ++ unsigned long hpitch; ++ v4l2_std_id stdid; ++}; ++ ++struct vpif_params { ++ struct vpif_interface iface; ++ struct vpif_video_params video_params; ++ struct vpif_channel_config_params std_info; ++ union param { ++ struct vpif_vbi_params vbi_params; ++ enum data_size data_sz; ++ } params; ++}; ++ ++#endif /* End of #ifndef VPIF_H */ ++ +diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c +new file mode 100644 +index 0000000..5892d2b +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif_capture.c +@@ -0,0 +1,2380 @@ ++/* ++ * Copyright (C) 2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * TODO : add support for VBI & HBI data service ++ * add static buffer allocation ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vpif_capture.h" ++#include "vpif.h" ++ ++MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(VPIF_CAPTURE_VERSION); ++ ++#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) ++#define vpif_dbg(level, debug, fmt, arg...) \ ++ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) ++ ++static int debug = 1; ++static u32 ch0_numbuffers = 3; ++static u32 ch1_numbuffers = 3; ++static u32 ch0_bufsize = 1920 * 1080 * 2; ++static u32 ch1_bufsize = 720 * 576 * 2; ++ ++module_param(debug, int, 0644); ++module_param(ch0_numbuffers, uint, S_IRUGO); ++module_param(ch1_numbuffers, uint, S_IRUGO); ++module_param(ch0_bufsize, uint, S_IRUGO); ++module_param(ch1_bufsize, uint, S_IRUGO); ++ ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++MODULE_PARM_DESC(ch2_numbuffers, "Channel0 buffer count (default:3)"); ++MODULE_PARM_DESC(ch3_numbuffers, "Channel1 buffer count (default:3)"); ++MODULE_PARM_DESC(ch2_bufsize, "Channel0 buffer size (default:1920 x 1080 x 2)"); ++MODULE_PARM_DESC(ch3_bufsize, "Channel1 buffer size (default:720 x 576 x 2)"); ++ ++static struct vpif_config_params config_params = { ++ .min_numbuffers = 3, ++ .numbuffers[0] = 3, ++ .numbuffers[1] = 3, ++ .min_bufsize[0] = 720 * 480 * 2, ++ .min_bufsize[1] = 720 * 480 * 2, ++ .channel_bufsize[0] = 1920 * 1080 * 2, ++ .channel_bufsize[1] = 720 * 576 * 2, ++}; ++ ++/* global variables */ ++static struct vpif_device vpif_obj = { {NULL} }; ++static struct device *vpif_dev; ++static void vpif_calculate_offsets(struct channel_obj *ch); ++static void vpif_config_addr(struct channel_obj *ch, int muxmode); ++ ++/** ++ * buffer_prepare : callback function for buffer prepare ++ * @vb: ptr to vb2_buffer ++ * ++ * This is the callback function for buffer prepare when vb2_qbuf() ++ * function is called. The buffer is prepared and user space virtual address ++ * or user address is converted into physical address ++ */ ++static int vpif_buffer_prepare(struct vb2_buffer *vb) ++{ ++ /* Get the file handle object and channel object */ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_queue *q = vb->vb2_queue; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long addr; ++ ++ vpif_dbg(2, debug, "vpif_buffer_prepare\n"); ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ if (vb->state != VB2_BUF_STATE_ACTIVE && ++ vb->state != VB2_BUF_STATE_PREPARED) { ++ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); ++ if (vb2_plane_vaddr(vb, 0) && ++ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) ++ goto exit; ++ addr = vb2_dma_contig_plane_dma_addr(vb, 0); ++ ++ if (q->streaming) { ++ if (!IS_ALIGNED((addr + common->ytop_off), 8) || ++ !IS_ALIGNED((addr + common->ybtm_off), 8) || ++ !IS_ALIGNED((addr + common->ctop_off), 8) || ++ !IS_ALIGNED((addr + common->cbtm_off), 8)) ++ goto exit; ++ } ++ } ++ return 0; ++exit: ++ vpif_dbg(1, debug, "buffer_prepare:offset is not aligned to 8 bytes\n"); ++ return -EINVAL; ++} ++ ++/** ++ * vpif_buffer_queue_setup : Callback function for buffer setup. ++ * @vq: vb2_queue ptr ++ * @fmt: v4l2 format ++ * @nbuffers: ptr to number of buffers requested by application ++ * @nplanes:: contains number of distinct video planes needed to hold a frame ++ * @sizes[]: contains the size (in bytes) of each plane. ++ * @alloc_ctxs: ptr to allocation context ++ * ++ * This callback function is called when reqbuf() is called to adjust ++ * the buffer count and buffer size ++ */ ++static int vpif_buffer_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ /* Get the file handle object and channel object */ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long size; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ vpif_dbg(2, debug, "vpif_buffer_setup\n"); ++ ++ /* If memory type is not mmap, return */ ++ if (V4L2_MEMORY_MMAP == common->memory) { ++ /* Calculate the size of the buffer */ ++ size = config_params.channel_bufsize[ch->channel_id]; ++ /* ++ * Checking if the buffer size exceeds the available buffer ++ * ycmux_mode = 0 means 1 channel mode HD and ++ * ycmux_mode = 1 means 2 channels mode SD ++ */ ++ if (ch->vpifparams.std_info.ycmux_mode == 0) { ++ if (config_params.video_limit[ch->channel_id]) ++ while (size * *nbuffers > ++ (config_params.video_limit[0] ++ + config_params.video_limit[1])) ++ (*nbuffers)--; ++ } else { ++ if (config_params.video_limit[ch->channel_id]) ++ while (size * *nbuffers > ++ config_params.video_limit[ch->channel_id]) ++ (*nbuffers)--; ++ } ++ ++ } else { ++ size = common->fmt.fmt.pix.sizeimage; ++ } ++ ++ if (*nbuffers < config_params.min_numbuffers) ++ *nbuffers = config_params.min_numbuffers; ++ ++ *nplanes = 1; ++ sizes[0] = size; ++ alloc_ctxs[0] = common->alloc_ctx; ++ ++ return 0; ++} ++ ++/** ++ * vpif_buffer_queue : Callback function to add buffer to DMA queue ++ * @vb: ptr to vb2_buffer ++ */ ++static void vpif_buffer_queue(struct vb2_buffer *vb) ++{ ++ /* Get the file handle object and channel object */ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct channel_obj *ch = fh->channel; ++ struct vpif_cap_buffer *buf = container_of(vb, ++ struct vpif_cap_buffer, vb); ++ struct common_obj *common; ++ unsigned long flags; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ vpif_dbg(2, debug, "vpif_buffer_queue\n"); ++ ++ spin_lock_irqsave(&common->irqlock, flags); ++ /* add the buffer to the DMA queue */ ++ list_add_tail(&buf->list, &common->dma_queue); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++} ++ ++/** ++ * vpif_buf_cleanup : Callback function to free buffer ++ * @vb: ptr to vb2_buffer ++ * ++ * This function is called from the videobuf2 layer to free memory ++ * allocated to the buffers ++ */ ++static void vpif_buf_cleanup(struct vb2_buffer *vb) ++{ ++ /* Get the file handle object and channel object */ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vpif_cap_buffer *buf = container_of(vb, ++ struct vpif_cap_buffer, vb); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long flags; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ spin_lock_irqsave(&common->irqlock, flags); ++ if (vb->state == VB2_BUF_STATE_ACTIVE) ++ list_del_init(&buf->list); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ ++} ++ ++static void vpif_wait_prepare(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_unlock(&common->lock); ++} ++ ++static void vpif_wait_finish(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++} ++ ++static int vpif_buffer_init(struct vb2_buffer *vb) ++{ ++ struct vpif_cap_buffer *buf = container_of(vb, ++ struct vpif_cap_buffer, vb); ++ ++ INIT_LIST_HEAD(&buf->list); ++ ++ return 0; ++} ++ ++static u8 channel_first_int[VPIF_NUMBER_OF_OBJECTS][2] = ++ { {1, 1} }; ++ ++static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct vpif_capture_config *vpif_config_data = ++ vpif_dev->platform_data; ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_params *vpif = &ch->vpifparams; ++ unsigned long addr = 0; ++ unsigned long flags; ++ int ret; ++ ++ /* If buffer queue is empty, return error */ ++ spin_lock_irqsave(&common->irqlock, flags); ++ if (list_empty(&common->dma_queue)) { ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ vpif_dbg(1, debug, "buffer queue is empty\n"); ++ return -EIO; ++ } ++ ++ /* Get the next frame from the buffer queue */ ++ common->cur_frm = common->next_frm = list_entry(common->dma_queue.next, ++ struct vpif_cap_buffer, list); ++ /* Remove buffer from the buffer queue */ ++ list_del(&common->cur_frm->list); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ /* Mark state of the current frame to active */ ++ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ /* Initialize field_id and started member */ ++ ch->field_id = 0; ++ common->started = 1; ++ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); ++ ++ /* Calculate the offset for Y and C data in the buffer */ ++ vpif_calculate_offsets(ch); ++ ++ if ((vpif->std_info.frm_fmt && ++ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) && ++ (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) || ++ (!vpif->std_info.frm_fmt && ++ (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { ++ vpif_dbg(1, debug, "conflict in field format and std format\n"); ++ return -EINVAL; ++ } ++ ++ /* configure 1 or 2 channel mode */ ++ if (vpif_config_data->setup_input_channel_mode) { ++ ret = vpif_config_data-> ++ setup_input_channel_mode(vpif->std_info.ycmux_mode); ++ if (ret < 0) { ++ vpif_dbg(1, debug, "can't set vpif channel mode\n"); ++ return ret; ++ } ++ } ++ ++ /* Call vpif_set_params function to set the parameters and addresses */ ++ ret = vpif_set_video_params(vpif, ch->channel_id); ++ ++ if (ret < 0) { ++ vpif_dbg(1, debug, "can't set video params\n"); ++ return ret; ++ } ++ ++ common->started = ret; ++ vpif_config_addr(ch, ret); ++ ++ common->set_addr(addr + common->ytop_off, ++ addr + common->ybtm_off, ++ addr + common->ctop_off, ++ addr + common->cbtm_off); ++ ++ /** ++ * Set interrupt for both the fields in VPIF Register enable channel in ++ * VPIF register ++ */ ++ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; ++ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id)) { ++ channel0_intr_assert(); ++ channel0_intr_enable(1); ++ enable_channel0(1); ++ } ++ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || ++ (common->started == 2)) { ++ channel1_intr_assert(); ++ channel1_intr_enable(1); ++ enable_channel1(1); ++ } ++ ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static int vpif_stop_streaming(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long flags; ++ ++ if (!vb2_is_streaming(vq)) ++ return 0; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* release all active buffers */ ++ spin_lock_irqsave(&common->irqlock, flags); ++ while (!list_empty(&common->dma_queue)) { ++ common->next_frm = list_entry(common->dma_queue.next, ++ struct vpif_cap_buffer, list); ++ list_del(&common->next_frm->list); ++ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); ++ } ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ ++ return 0; ++} ++ ++static struct vb2_ops video_qops = { ++ .queue_setup = vpif_buffer_queue_setup, ++ .wait_prepare = vpif_wait_prepare, ++ .wait_finish = vpif_wait_finish, ++ .buf_init = vpif_buffer_init, ++ .buf_prepare = vpif_buffer_prepare, ++ .start_streaming = vpif_start_streaming, ++ .stop_streaming = vpif_stop_streaming, ++ .buf_cleanup = vpif_buf_cleanup, ++ .buf_queue = vpif_buffer_queue, ++}; ++ ++/** ++ * vpif_process_buffer_complete: process a completed buffer ++ * @common: ptr to common channel object ++ * ++ * This function time stamp the buffer and mark it as DONE. It also ++ * wake up any process waiting on the QUEUE and set the next buffer ++ * as current ++ */ ++static void vpif_process_buffer_complete(struct common_obj *common) ++{ ++ v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); ++ vb2_buffer_done(&common->cur_frm->vb, ++ VB2_BUF_STATE_DONE); ++ /* Make curFrm pointing to nextFrm */ ++ common->cur_frm = common->next_frm; ++} ++ ++/** ++ * vpif_schedule_next_buffer: set next buffer address for capture ++ * @common : ptr to common channel object ++ * ++ * This function will get next buffer from the dma queue and ++ * set the buffer address in the vpif register for capture. ++ * the buffer is marked active ++ */ ++static void vpif_schedule_next_buffer(struct common_obj *common) ++{ ++ unsigned long addr = 0; ++ ++ spin_lock(&common->irqlock); ++ common->next_frm = list_entry(common->dma_queue.next, ++ struct vpif_cap_buffer, list); ++ /* Remove that buffer from the buffer queue */ ++ list_del(&common->next_frm->list); ++ spin_unlock(&common->irqlock); ++ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); ++ ++ /* Set top and bottom field addresses in VPIF registers */ ++ common->set_addr(addr + common->ytop_off, ++ addr + common->ybtm_off, ++ addr + common->ctop_off, ++ addr + common->cbtm_off); ++} ++ ++/** ++ * vpif_channel_isr : ISR handler for vpif capture ++ * @irq: irq number ++ * @dev_id: dev_id ptr ++ * ++ * It changes status of the captured buffer, takes next buffer from the queue ++ * and sets its address in VPIF registers ++ */ ++static irqreturn_t vpif_channel_isr(int irq, void *dev_id) ++{ ++ struct vpif_device *dev = &vpif_obj; ++ struct common_obj *common; ++ struct channel_obj *ch; ++ enum v4l2_field field; ++ int channel_id = 0; ++ int fid = -1, i; ++ ++ channel_id = *(int *)(dev_id); ++ if (!vpif_intr_status(channel_id)) ++ return IRQ_NONE; ++ ++ ch = dev->dev[channel_id]; ++ ++ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; ++ ++ for (i = 0; i < VPIF_NUMBER_OF_OBJECTS; i++) { ++ common = &ch->common[i]; ++ /* skip If streaming is not started in this channel */ ++ if (0 == common->started) ++ continue; ++ ++ /* Check the field format */ ++ if (1 == ch->vpifparams.std_info.frm_fmt) { ++ /* Progressive mode */ ++ spin_lock(&common->irqlock); ++ if (list_empty(&common->dma_queue)) { ++ spin_unlock(&common->irqlock); ++ continue; ++ } ++ spin_unlock(&common->irqlock); ++ ++ if (!channel_first_int[i][channel_id]) ++ vpif_process_buffer_complete(common); ++ ++ channel_first_int[i][channel_id] = 0; ++ ++ vpif_schedule_next_buffer(common); ++ ++ ++ channel_first_int[i][channel_id] = 0; ++ } else { ++ /** ++ * Interlaced mode. If it is first interrupt, ignore ++ * it ++ */ ++ if (channel_first_int[i][channel_id]) { ++ channel_first_int[i][channel_id] = 0; ++ continue; ++ } ++ if (0 == i) { ++ ch->field_id ^= 1; ++ /* Get field id from VPIF registers */ ++ fid = vpif_channel_getfid(ch->channel_id); ++ if (fid != ch->field_id) { ++ /** ++ * If field id does not match stored ++ * field id, make them in sync ++ */ ++ if (0 == fid) ++ ch->field_id = fid; ++ return IRQ_HANDLED; ++ } ++ } ++ /* device field id and local field id are in sync */ ++ if (0 == fid) { ++ /* this is even field */ ++ if (common->cur_frm == common->next_frm) ++ continue; ++ ++ /* mark the current buffer as done */ ++ vpif_process_buffer_complete(common); ++ } else if (1 == fid) { ++ /* odd field */ ++ spin_lock(&common->irqlock); ++ if (list_empty(&common->dma_queue) || ++ (common->cur_frm != common->next_frm)) { ++ spin_unlock(&common->irqlock); ++ continue; ++ } ++ spin_unlock(&common->irqlock); ++ ++ vpif_schedule_next_buffer(common); ++ } ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++/** ++ * vpif_update_std_info() - update standard related info ++ * @ch: ptr to channel object ++ * ++ * For a given standard selected by application, update values ++ * in the device data structures ++ */ ++static int vpif_update_std_info(struct channel_obj *ch) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ const struct vpif_channel_config_params *config; ++ struct vpif_channel_config_params *std_info = &vpifparams->std_info; ++ struct video_obj *vid_ch = &ch->video; ++ int index; ++ ++ vpif_dbg(2, debug, "vpif_update_std_info\n"); ++ ++ for (index = 0; index < vpif_ch_params_count; index++) { ++ config = &ch_params[index]; ++ if (config->hd_sd == 0) { ++ vpif_dbg(2, debug, "SD format\n"); ++ if (config->stdid & vid_ch->stdid) { ++ memcpy(std_info, config, sizeof(*config)); ++ break; ++ } ++ } else { ++ vpif_dbg(2, debug, "HD format\n"); ++ if (!memcmp(&config->dv_timings, &vid_ch->dv_timings, ++ sizeof(vid_ch->dv_timings))) { ++ memcpy(std_info, config, sizeof(*config)); ++ break; ++ } ++ } ++ } ++ ++ /* standard not found */ ++ if (index == vpif_ch_params_count) ++ return -EINVAL; ++ ++ common->fmt.fmt.pix.width = std_info->width; ++ common->width = std_info->width; ++ common->fmt.fmt.pix.height = std_info->height; ++ common->height = std_info->height; ++ common->fmt.fmt.pix.bytesperline = std_info->width; ++ vpifparams->video_params.hpitch = std_info->width; ++ vpifparams->video_params.storage_mode = std_info->frm_fmt; ++ ++ return 0; ++} ++ ++/** ++ * vpif_calculate_offsets : This function calculates buffers offsets ++ * @ch : ptr to channel object ++ * ++ * This function calculates buffer offsets for Y and C in the top and ++ * bottom field ++ */ ++static void vpif_calculate_offsets(struct channel_obj *ch) ++{ ++ unsigned int hpitch, vpitch, sizeimage; ++ struct video_obj *vid_ch = &(ch->video); ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ enum v4l2_field field = common->fmt.fmt.pix.field; ++ ++ vpif_dbg(2, debug, "vpif_calculate_offsets\n"); ++ ++ if (V4L2_FIELD_ANY == field) { ++ if (vpifparams->std_info.frm_fmt) ++ vid_ch->buf_field = V4L2_FIELD_NONE; ++ else ++ vid_ch->buf_field = V4L2_FIELD_INTERLACED; ++ } else ++ vid_ch->buf_field = common->fmt.fmt.pix.field; ++ ++ sizeimage = common->fmt.fmt.pix.sizeimage; ++ ++ hpitch = common->fmt.fmt.pix.bytesperline; ++ vpitch = sizeimage / (hpitch * 2); ++ ++ if ((V4L2_FIELD_NONE == vid_ch->buf_field) || ++ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { ++ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ ++ common->ytop_off = 0; ++ common->ybtm_off = hpitch; ++ common->ctop_off = sizeimage / 2; ++ common->cbtm_off = sizeimage / 2 + hpitch; ++ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { ++ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ ++ common->ytop_off = 0; ++ common->ybtm_off = sizeimage / 4; ++ common->ctop_off = sizeimage / 2; ++ common->cbtm_off = common->ctop_off + sizeimage / 4; ++ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { ++ /* Calculate offsets for Y top, Y Bottom, C top and C Bottom */ ++ common->ybtm_off = 0; ++ common->ytop_off = sizeimage / 4; ++ common->cbtm_off = sizeimage / 2; ++ common->ctop_off = common->cbtm_off + sizeimage / 4; ++ } ++ if ((V4L2_FIELD_NONE == vid_ch->buf_field) || ++ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) ++ vpifparams->video_params.storage_mode = 1; ++ else ++ vpifparams->video_params.storage_mode = 0; ++ ++ if (1 == vpifparams->std_info.frm_fmt) ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline; ++ else { ++ if ((field == V4L2_FIELD_ANY) ++ || (field == V4L2_FIELD_INTERLACED)) ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline * 2; ++ else ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline; ++ } ++ ++ ch->vpifparams.video_params.stdid = vpifparams->std_info.stdid; ++} ++ ++/** ++ * vpif_config_format: configure default frame format in the device ++ * ch : ptr to channel object ++ */ ++static void vpif_config_format(struct channel_obj *ch) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ vpif_dbg(2, debug, "vpif_config_format\n"); ++ ++ common->fmt.fmt.pix.field = V4L2_FIELD_ANY; ++ if (config_params.numbuffers[ch->channel_id] == 0) ++ common->memory = V4L2_MEMORY_USERPTR; ++ else ++ common->memory = V4L2_MEMORY_MMAP; ++ ++ common->fmt.fmt.pix.sizeimage ++ = config_params.channel_bufsize[ch->channel_id]; ++ ++ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) ++ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; ++ else ++ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; ++ common->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++} ++ ++/** ++ * vpif_get_default_field() - Get default field type based on interface ++ * @vpif_params - ptr to vpif params ++ */ ++static inline enum v4l2_field vpif_get_default_field( ++ struct vpif_interface *iface) ++{ ++ return (iface->if_type == VPIF_IF_RAW_BAYER) ? V4L2_FIELD_NONE : ++ V4L2_FIELD_INTERLACED; ++} ++ ++/** ++ * vpif_check_format() - check given pixel format for compatibility ++ * @ch - channel ptr ++ * @pixfmt - Given pixel format ++ * @update - update the values as per hardware requirement ++ * ++ * Check the application pixel format for S_FMT and update the input ++ * values as per hardware limits for TRY_FMT. The default pixel and ++ * field format is selected based on interface type. ++ */ ++static int vpif_check_format(struct channel_obj *ch, ++ struct v4l2_pix_format *pixfmt, ++ int update) ++{ ++ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); ++ struct vpif_params *vpif_params = &ch->vpifparams; ++ enum v4l2_field field = pixfmt->field; ++ u32 sizeimage, hpitch, vpitch; ++ int ret = -EINVAL; ++ ++ vpif_dbg(2, debug, "vpif_check_format\n"); ++ /** ++ * first check for the pixel format. If if_type is Raw bayer, ++ * only V4L2_PIX_FMT_SBGGR8 format is supported. Otherwise only ++ * V4L2_PIX_FMT_YUV422P is supported ++ */ ++ if (vpif_params->iface.if_type == VPIF_IF_RAW_BAYER) { ++ if (pixfmt->pixelformat != V4L2_PIX_FMT_SBGGR8) { ++ if (!update) { ++ vpif_dbg(2, debug, "invalid pix format\n"); ++ goto exit; ++ } ++ pixfmt->pixelformat = V4L2_PIX_FMT_SBGGR8; ++ } ++ } else { ++ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) { ++ if (!update) { ++ vpif_dbg(2, debug, "invalid pixel format\n"); ++ goto exit; ++ } ++ pixfmt->pixelformat = V4L2_PIX_FMT_YUV422P; ++ } ++ } ++ ++ if (!(VPIF_VALID_FIELD(field))) { ++ if (!update) { ++ vpif_dbg(2, debug, "invalid field format\n"); ++ goto exit; ++ } ++ /** ++ * By default use FIELD_NONE for RAW Bayer capture ++ * and FIELD_INTERLACED for other interfaces ++ */ ++ field = vpif_get_default_field(&vpif_params->iface); ++ } else if (field == V4L2_FIELD_ANY) ++ /* unsupported field. Use default */ ++ field = vpif_get_default_field(&vpif_params->iface); ++ ++ /* validate the hpitch */ ++ hpitch = pixfmt->bytesperline; ++ if (hpitch < vpif_params->std_info.width) { ++ if (!update) { ++ vpif_dbg(2, debug, "invalid hpitch\n"); ++ goto exit; ++ } ++ hpitch = vpif_params->std_info.width; ++ } ++ ++ sizeimage = pixfmt->sizeimage; ++ ++ vpitch = sizeimage / (hpitch * 2); ++ ++ /* validate the vpitch */ ++ if (vpitch < vpif_params->std_info.height) { ++ if (!update) { ++ vpif_dbg(2, debug, "Invalid vpitch\n"); ++ goto exit; ++ } ++ vpitch = vpif_params->std_info.height; ++ } ++ ++ /* Check for 8 byte alignment */ ++ if (!ALIGN(hpitch, 8)) { ++ if (!update) { ++ vpif_dbg(2, debug, "invalid pitch alignment\n"); ++ goto exit; ++ } ++ /* adjust to next 8 byte boundary */ ++ hpitch = (((hpitch + 7) / 8) * 8); ++ } ++ /* if update is set, modify the bytesperline and sizeimage */ ++ if (update) { ++ pixfmt->bytesperline = hpitch; ++ pixfmt->sizeimage = hpitch * vpitch * 2; ++ } ++ /** ++ * Image width and height is always based on current standard width and ++ * height ++ */ ++ pixfmt->width = common->fmt.fmt.pix.width; ++ pixfmt->height = common->fmt.fmt.pix.height; ++ return 0; ++exit: ++ return ret; ++} ++ ++/** ++ * vpif_config_addr() - function to configure buffer address in vpif ++ * @ch - channel ptr ++ * @muxmode - channel mux mode ++ */ ++static void vpif_config_addr(struct channel_obj *ch, int muxmode) ++{ ++ struct common_obj *common; ++ ++ vpif_dbg(2, debug, "vpif_config_addr\n"); ++ ++ common = &(ch->common[VPIF_VIDEO_INDEX]); ++ ++ if (VPIF_CHANNEL1_VIDEO == ch->channel_id) ++ common->set_addr = ch1_set_videobuf_addr; ++ else if (2 == muxmode) ++ common->set_addr = ch0_set_videobuf_addr_yc_nmux; ++ else ++ common->set_addr = ch0_set_videobuf_addr; ++} ++ ++/** ++ * vpif_mmap : It is used to map kernel space buffers into user spaces ++ * @filep: file pointer ++ * @vma: ptr to vm_area_struct ++ */ ++static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) ++{ ++ /* Get the channel object and file handle object */ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); ++ int ret; ++ ++ vpif_dbg(2, debug, "vpif_mmap\n"); ++ ++ if (mutex_lock_interruptible(&common->lock)) ++ return -ERESTARTSYS; ++ ret = vb2_mmap(&common->buffer_queue, vma); ++ mutex_unlock(&common->lock); ++ return ret; ++} ++ ++/** ++ * vpif_poll: It is used for select/poll system call ++ * @filep: file pointer ++ * @wait: poll table to wait ++ */ ++static unsigned int vpif_poll(struct file *filep, poll_table * wait) ++{ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *channel = fh->channel; ++ struct common_obj *common = &(channel->common[VPIF_VIDEO_INDEX]); ++ unsigned int res = 0; ++ ++ vpif_dbg(2, debug, "vpif_poll\n"); ++ ++ if (common->started) { ++ mutex_lock(&common->lock); ++ res = vb2_poll(&common->buffer_queue, filep, wait); ++ mutex_unlock(&common->lock); ++ } ++ return res; ++} ++ ++/** ++ * vpif_open : vpif open handler ++ * @filep: file ptr ++ * ++ * It creates object of file handle structure and stores it in private_data ++ * member of filepointer ++ */ ++static int vpif_open(struct file *filep) ++{ ++ struct video_device *vdev = video_devdata(filep); ++ struct common_obj *common; ++ struct video_obj *vid_ch; ++ struct channel_obj *ch; ++ struct vpif_fh *fh; ++ ++ vpif_dbg(2, debug, "vpif_open\n"); ++ ++ ch = video_get_drvdata(vdev); ++ ++ vid_ch = &ch->video; ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* Allocate memory for the file handle object */ ++ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); ++ if (NULL == fh) { ++ vpif_err("unable to allocate memory for file handle object\n"); ++ return -ENOMEM; ++ } ++ ++ if (mutex_lock_interruptible(&common->lock)) { ++ kfree(fh); ++ return -ERESTARTSYS; ++ } ++ /* store pointer to fh in private_data member of filep */ ++ filep->private_data = fh; ++ fh->channel = ch; ++ fh->initialized = 0; ++ /* If decoder is not initialized. initialize it */ ++ if (!ch->initialized) { ++ fh->initialized = 1; ++ ch->initialized = 1; ++ memset(&(ch->vpifparams), 0, sizeof(struct vpif_params)); ++ } ++ /* Increment channel usrs counter */ ++ ch->usrs++; ++ /* Set io_allowed member to false */ ++ fh->io_allowed[VPIF_VIDEO_INDEX] = 0; ++ /* Initialize priority of this instance to default priority */ ++ fh->prio = V4L2_PRIORITY_UNSET; ++ v4l2_prio_open(&ch->prio, &fh->prio); ++ mutex_unlock(&common->lock); ++ return 0; ++} ++ ++/** ++ * vpif_release : function to clean up file close ++ * @filep: file pointer ++ * ++ * This function deletes buffer queue, frees the buffers and the vpif file ++ * handle ++ */ ++static int vpif_release(struct file *filep) ++{ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ ++ vpif_dbg(2, debug, "vpif_release\n"); ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ mutex_lock(&common->lock); ++ /* if this instance is doing IO */ ++ if (fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ /* Reset io_usrs member of channel object */ ++ common->io_usrs = 0; ++ /* Disable channel as per its device type and channel id */ ++ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { ++ enable_channel0(0); ++ channel0_intr_enable(0); ++ } ++ if ((VPIF_CHANNEL1_VIDEO == ch->channel_id) || ++ (2 == common->started)) { ++ enable_channel1(0); ++ channel1_intr_enable(0); ++ } ++ common->started = 0; ++ /* Free buffers allocated */ ++ vb2_queue_release(&common->buffer_queue); ++ vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ++ } ++ ++ /* Decrement channel usrs counter */ ++ ch->usrs--; ++ ++ /* Close the priority */ ++ v4l2_prio_close(&ch->prio, fh->prio); ++ ++ if (fh->initialized) ++ ch->initialized = 0; ++ ++ mutex_unlock(&common->lock); ++ filep->private_data = NULL; ++ kfree(fh); ++ return 0; ++} ++ ++/** ++ * vpif_reqbufs() - request buffer handler ++ * @file: file ptr ++ * @priv: file handle ++ * @reqbuf: request buffer structure ptr ++ */ ++static int vpif_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbuf) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ u8 index = 0; ++ struct vb2_queue *q; ++ int ret; ++ ++ vpif_dbg(2, debug, "vpif_reqbufs\n"); ++ ++ /** ++ * This file handle has not initialized the channel, ++ * It is not allowed to do settings ++ */ ++ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) ++ || (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_dbg(1, debug, "Channel Busy\n"); ++ return -EBUSY; ++ } ++ } ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != reqbuf->type || !vpif_dev) ++ return -EINVAL; ++ ++ index = VPIF_VIDEO_INDEX; ++ ++ common = &ch->common[index]; ++ ++ if (0 != common->io_usrs) ++ return -EBUSY; ++ ++ /* Initialize videobuf2 queue as per the buffer type */ ++ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); ++ if (IS_ERR(common->alloc_ctx)) { ++ vpif_err("Failed to get the context\n"); ++ return PTR_ERR(common->alloc_ctx); ++ } ++ q = &common->buffer_queue; ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_USERPTR; ++ q->drv_priv = fh; ++ q->ops = &video_qops; ++ q->mem_ops = &vb2_dma_contig_memops; ++ q->buf_struct_size = sizeof(struct vpif_cap_buffer); ++ ++ ret = vb2_queue_init(q); ++ if (ret) { ++ vpif_err("vpif_capture: vb2_queue_init() failed\n"); ++ vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ++ return ret; ++ } ++ /* Set io allowed member of file handle to TRUE */ ++ fh->io_allowed[index] = 1; ++ /* Increment io usrs member of channel object to 1 */ ++ common->io_usrs = 1; ++ /* Store type of memory requested in channel object */ ++ common->memory = reqbuf->memory; ++ INIT_LIST_HEAD(&common->dma_queue); ++ ++ /* Allocate buffers */ ++ return vb2_reqbufs(&common->buffer_queue, reqbuf); ++} ++ ++/** ++ * vpif_querybuf() - query buffer handler ++ * @file: file ptr ++ * @priv: file handle ++ * @buf: v4l2 buffer structure ptr ++ */ ++static int vpif_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ vpif_dbg(2, debug, "vpif_querybuf\n"); ++ ++ if (common->fmt.type != buf->type) ++ return -EINVAL; ++ ++ if (common->memory != V4L2_MEMORY_MMAP) { ++ vpif_dbg(1, debug, "Invalid memory\n"); ++ return -EINVAL; ++ } ++ ++ return vb2_querybuf(&common->buffer_queue, buf); ++} ++ ++/** ++ * vpif_qbuf() - query buffer handler ++ * @file: file ptr ++ * @priv: file handle ++ * @buf: v4l2 buffer structure ptr ++ */ ++static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct v4l2_buffer tbuf = *buf; ++ ++ vpif_dbg(2, debug, "vpif_qbuf\n"); ++ ++ if (common->fmt.type != tbuf.type) { ++ vpif_err("invalid buffer type\n"); ++ return -EINVAL; ++ } ++ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_err("fh io not allowed\n"); ++ return -EACCES; ++ } ++ ++ return vb2_qbuf(&common->buffer_queue, buf); ++} ++ ++/** ++ * vpif_dqbuf() - query buffer handler ++ * @file: file ptr ++ * @priv: file handle ++ * @buf: v4l2 buffer structure ptr ++ */ ++static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ vpif_dbg(2, debug, "vpif_dqbuf\n"); ++ ++ return vb2_dqbuf(&common->buffer_queue, buf, ++ (file->f_flags & O_NONBLOCK)); ++} ++ ++/** ++ * vpif_streamon() - streamon handler ++ * @file: file ptr ++ * @priv: file handle ++ * @buftype: v4l2 buffer type ++ */ ++static int vpif_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type buftype) ++{ ++ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; ++ struct vpif_params *vpif; ++ int ret = 0; ++ ++ vpif_dbg(2, debug, "vpif_streamon\n"); ++ ++ vpif = &ch->vpifparams; ++ ++ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ vpif_dbg(1, debug, "buffer type not supported\n"); ++ return -EINVAL; ++ } ++ ++ /* If file handle is not allowed IO, return error */ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_dbg(1, debug, "io not allowed\n"); ++ return -EACCES; ++ } ++ ++ /* If Streaming is already started, return error */ ++ if (common->started) { ++ vpif_dbg(1, debug, "channel->started\n"); ++ return -EBUSY; ++ } ++ ++ if ((ch->channel_id == VPIF_CHANNEL0_VIDEO && ++ oth_ch->common[VPIF_VIDEO_INDEX].started && ++ vpif->std_info.ycmux_mode == 0) || ++ ((ch->channel_id == VPIF_CHANNEL1_VIDEO) && ++ (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { ++ vpif_dbg(1, debug, "other channel is being used\n"); ++ return -EBUSY; ++ } ++ ++ ret = vpif_check_format(ch, &common->fmt.fmt.pix, 0); ++ if (ret) ++ return ret; ++ ++ /* Enable streamon on the sub device */ ++ ret = v4l2_subdev_call(ch->sd, video, s_stream, 1); ++ ++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { ++ vpif_dbg(1, debug, "stream on failed in subdev\n"); ++ return ret; ++ } ++ ++ /* Call vb2_streamon to start streaming in videobuf2 */ ++ ret = vb2_streamon(&common->buffer_queue, buftype); ++ if (ret) { ++ vpif_dbg(1, debug, "vb2_streamon\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++/** ++ * vpif_streamoff() - streamoff handler ++ * @file: file ptr ++ * @priv: file handle ++ * @buftype: v4l2 buffer type ++ */ ++static int vpif_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type buftype) ++{ ++ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ int ret; ++ ++ vpif_dbg(2, debug, "vpif_streamoff\n"); ++ ++ if (buftype != V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ vpif_dbg(1, debug, "buffer type not supported\n"); ++ return -EINVAL; ++ } ++ ++ /* If io is allowed for this file handle, return error */ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_dbg(1, debug, "io not allowed\n"); ++ return -EACCES; ++ } ++ ++ /* If streaming is not started, return error */ ++ if (!common->started) { ++ vpif_dbg(1, debug, "channel->started\n"); ++ return -EINVAL; ++ } ++ ++ /* disable channel */ ++ if (VPIF_CHANNEL0_VIDEO == ch->channel_id) { ++ enable_channel0(0); ++ channel0_intr_enable(0); ++ } else { ++ enable_channel1(0); ++ channel1_intr_enable(0); ++ } ++ ++ common->started = 0; ++ ++ ret = v4l2_subdev_call(ch->sd, video, s_stream, 0); ++ ++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) ++ vpif_dbg(1, debug, "stream off failed in subdev\n"); ++ ++ return vb2_streamoff(&common->buffer_queue, buftype); ++} ++ ++/** ++ * vpif_input_to_subdev() - Maps input to sub device ++ * @vpif_cfg - global config ptr ++ * @chan_cfg - channel config ptr ++ * @input_index - Given input index from application ++ * ++ * lookup the sub device information for a given input index. ++ * we report all the inputs to application. inputs table also ++ * has sub device name for the each input ++ */ ++static int vpif_input_to_subdev( ++ struct vpif_capture_config *vpif_cfg, ++ struct vpif_capture_chan_config *chan_cfg, ++ int input_index) ++{ ++ struct vpif_subdev_info *subdev_info; ++ const char *subdev_name; ++ int i; ++ ++ vpif_dbg(2, debug, "vpif_input_to_subdev\n"); ++ ++ subdev_name = chan_cfg->inputs[input_index].subdev_name; ++ if (subdev_name == NULL) ++ return -1; ++ ++ /* loop through the sub device list to get the sub device info */ ++ for (i = 0; i < vpif_cfg->subdev_count; i++) { ++ subdev_info = &vpif_cfg->subdev_info[i]; ++ if (!strcmp(subdev_info->name, subdev_name)) ++ return i; ++ } ++ return -1; ++} ++ ++/** ++ * vpif_set_input() - Select an input ++ * @vpif_cfg - global config ptr ++ * @ch - channel ++ * @_index - Given input index from application ++ * ++ * Select the given input. ++ */ ++static int vpif_set_input( ++ struct vpif_capture_config *vpif_cfg, ++ struct channel_obj *ch, ++ int index) ++{ ++ struct vpif_capture_chan_config *chan_cfg = ++ &vpif_cfg->chan_config[ch->channel_id]; ++ struct vpif_subdev_info *subdev_info = NULL; ++ struct v4l2_subdev *sd = NULL; ++ u32 input = 0, output = 0; ++ int sd_index; ++ int ret; ++ ++ sd_index = vpif_input_to_subdev(vpif_cfg, chan_cfg, index); ++ if (sd_index >= 0) { ++ sd = vpif_obj.sd[sd_index]; ++ subdev_info = &vpif_cfg->subdev_info[sd_index]; ++ } ++ ++ /* first setup input path from sub device to vpif */ ++ if (sd && vpif_cfg->setup_input_path) { ++ ret = vpif_cfg->setup_input_path(ch->channel_id, ++ subdev_info->name); ++ if (ret < 0) { ++ vpif_dbg(1, debug, "couldn't setup input path for the" \ ++ " sub device %s, for input index %d\n", ++ subdev_info->name, index); ++ return ret; ++ } ++ } ++ ++ if (sd) { ++ input = chan_cfg->inputs[index].input_route; ++ output = chan_cfg->inputs[index].output_route; ++ ret = v4l2_subdev_call(sd, video, s_routing, ++ input, output, 0); ++ if (ret < 0 && ret != -ENOIOCTLCMD) { ++ vpif_dbg(1, debug, "Failed to set input\n"); ++ return ret; ++ } ++ } ++ ch->input_idx = index; ++ ch->sd = sd; ++ /* copy interface parameters to vpif */ ++ ch->vpifparams.iface = chan_cfg->vpif_if; ++ ++ /* update tvnorms from the sub device input info */ ++ ch->video_dev->tvnorms = chan_cfg->inputs[index].input.std; ++ return 0; ++} ++ ++/** ++ * vpif_querystd() - querystd handler ++ * @file: file ptr ++ * @priv: file handle ++ * @std_id: ptr to std id ++ * ++ * This function is called to detect standard at the selected input ++ */ ++static int vpif_querystd(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ int ret = 0; ++ ++ vpif_dbg(2, debug, "vpif_querystd\n"); ++ ++ /* Call querystd function of decoder device */ ++ ret = v4l2_subdev_call(ch->sd, video, querystd, std_id); ++ ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ return -ENODATA; ++ if (ret) { ++ vpif_dbg(1, debug, "Failed to query standard for sub devices\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/** ++ * vpif_g_std() - get STD handler ++ * @file: file ptr ++ * @priv: file handle ++ * @std_id: ptr to std id ++ */ ++static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ vpif_dbg(2, debug, "vpif_g_std\n"); ++ ++ *std = ch->video.stdid; ++ return 0; ++} ++ ++/** ++ * vpif_s_std() - set STD handler ++ * @file: file ptr ++ * @priv: file handle ++ * @std_id: ptr to std id ++ */ ++static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ int ret = 0; ++ ++ vpif_dbg(2, debug, "vpif_s_std\n"); ++ ++ if (common->started) { ++ vpif_err("streaming in progress\n"); ++ return -EBUSY; ++ } ++ ++ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || ++ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_dbg(1, debug, "Channel Busy\n"); ++ return -EBUSY; ++ } ++ } ++ ++ ret = v4l2_prio_check(&ch->prio, fh->prio); ++ if (0 != ret) ++ return ret; ++ ++ fh->initialized = 1; ++ ++ /* Call encoder subdevice function to set the standard */ ++ ch->video.stdid = *std_id; ++ memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); ++ ++ /* Get the information about the standard */ ++ if (vpif_update_std_info(ch)) { ++ vpif_err("Error getting the standard info\n"); ++ return -EINVAL; ++ } ++ ++ /* Configure the default format information */ ++ vpif_config_format(ch); ++ ++ /* set standard in the sub device */ ++ ret = v4l2_subdev_call(ch->sd, core, s_std, *std_id); ++ if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) { ++ vpif_dbg(1, debug, "Failed to set standard for sub devices\n"); ++ return ret; ++ } ++ return 0; ++} ++ ++/** ++ * vpif_enum_input() - ENUMINPUT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @input: ptr to input structure ++ */ ++static int vpif_enum_input(struct file *file, void *priv, ++ struct v4l2_input *input) ++{ ++ ++ struct vpif_capture_config *config = vpif_dev->platform_data; ++ struct vpif_capture_chan_config *chan_cfg; ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ chan_cfg = &config->chan_config[ch->channel_id]; ++ ++ if (input->index >= chan_cfg->input_count) { ++ vpif_dbg(1, debug, "Invalid input index\n"); ++ return -EINVAL; ++ } ++ ++ memcpy(input, &chan_cfg->inputs[input->index].input, ++ sizeof(*input)); ++ return 0; ++} ++ ++/** ++ * vpif_g_input() - Get INPUT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @index: ptr to input index ++ */ ++static int vpif_g_input(struct file *file, void *priv, unsigned int *index) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ *index = ch->input_idx; ++ return 0; ++} ++ ++/** ++ * vpif_s_input() - Set INPUT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @index: input index ++ */ ++static int vpif_s_input(struct file *file, void *priv, unsigned int index) ++{ ++ struct vpif_capture_config *config = vpif_dev->platform_data; ++ struct vpif_capture_chan_config *chan_cfg; ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ int ret; ++ ++ chan_cfg = &config->chan_config[ch->channel_id]; ++ ++ if (index >= chan_cfg->input_count) ++ return -EINVAL; ++ ++ if (common->started) { ++ vpif_err("Streaming in progress\n"); ++ return -EBUSY; ++ } ++ ++ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || ++ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_dbg(1, debug, "Channel Busy\n"); ++ return -EBUSY; ++ } ++ } ++ ++ ret = v4l2_prio_check(&ch->prio, fh->prio); ++ if (0 != ret) ++ return ret; ++ ++ fh->initialized = 1; ++ return vpif_set_input(config, ch, index); ++} ++ ++/** ++ * vpif_enum_fmt_vid_cap() - ENUM_FMT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @index: input index ++ */ ++static int vpif_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ if (fmt->index != 0) { ++ vpif_dbg(1, debug, "Invalid format index\n"); ++ return -EINVAL; ++ } ++ ++ /* Fill in the information about format */ ++ if (ch->vpifparams.iface.if_type == VPIF_IF_RAW_BAYER) { ++ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ strcpy(fmt->description, "Raw Mode -Bayer Pattern GrRBGb"); ++ fmt->pixelformat = V4L2_PIX_FMT_SBGGR8; ++ } else { ++ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); ++ fmt->pixelformat = V4L2_PIX_FMT_YUV422P; ++ } ++ return 0; ++} ++ ++/** ++ * vpif_try_fmt_vid_cap() - TRY_FMT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @fmt: ptr to v4l2 format structure ++ */ ++static int vpif_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ ++ return vpif_check_format(ch, pixfmt, 1); ++} ++ ++ ++/** ++ * vpif_g_fmt_vid_cap() - Set INPUT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @fmt: ptr to v4l2 format structure ++ */ ++static int vpif_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* Check the validity of the buffer type */ ++ if (common->fmt.type != fmt->type) ++ return -EINVAL; ++ ++ /* Fill in the information about format */ ++ *fmt = common->fmt; ++ return 0; ++} ++ ++/** ++ * vpif_s_fmt_vid_cap() - Set FMT handler ++ * @file: file ptr ++ * @priv: file handle ++ * @fmt: ptr to v4l2 format structure ++ */ ++static int vpif_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct v4l2_pix_format *pixfmt; ++ int ret = 0; ++ ++ vpif_dbg(2, debug, "%s\n", __func__); ++ ++ /* If streaming is started, return error */ ++ if (common->started) { ++ vpif_dbg(1, debug, "Streaming is started\n"); ++ return -EBUSY; ++ } ++ ++ if ((VPIF_CHANNEL0_VIDEO == ch->channel_id) || ++ (VPIF_CHANNEL1_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_dbg(1, debug, "Channel Busy\n"); ++ return -EBUSY; ++ } ++ } ++ ++ ret = v4l2_prio_check(&ch->prio, fh->prio); ++ if (0 != ret) ++ return ret; ++ ++ fh->initialized = 1; ++ ++ pixfmt = &fmt->fmt.pix; ++ /* Check for valid field format */ ++ ret = vpif_check_format(ch, pixfmt, 0); ++ ++ if (ret) ++ return ret; ++ /* store the format in the channel object */ ++ common->fmt = *fmt; ++ return 0; ++} ++ ++/** ++ * vpif_querycap() - QUERYCAP handler ++ * @file: file ptr ++ * @priv: file handle ++ * @cap: ptr to v4l2_capability structure ++ */ ++static int vpif_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct vpif_capture_config *config = vpif_dev->platform_data; ++ ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ dev_name(vpif_dev)); ++ strlcpy(cap->card, config->card_name, sizeof(cap->card)); ++ ++ return 0; ++} ++ ++/** ++ * vpif_g_priority() - get priority handler ++ * @file: file ptr ++ * @priv: file handle ++ * @prio: ptr to v4l2_priority structure ++ */ ++static int vpif_g_priority(struct file *file, void *priv, ++ enum v4l2_priority *prio) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ *prio = v4l2_prio_max(&ch->prio); ++ ++ return 0; ++} ++ ++/** ++ * vpif_s_priority() - set priority handler ++ * @file: file ptr ++ * @priv: file handle ++ * @prio: ptr to v4l2_priority structure ++ */ ++static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_prio_change(&ch->prio, &fh->prio, p); ++} ++ ++/** ++ * vpif_cropcap() - cropcap handler ++ * @file: file ptr ++ * @priv: file handle ++ * @crop: ptr to v4l2_cropcap structure ++ */ ++static int vpif_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *crop) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != crop->type) ++ return -EINVAL; ++ ++ crop->bounds.left = 0; ++ crop->bounds.top = 0; ++ crop->bounds.height = common->height; ++ crop->bounds.width = common->width; ++ crop->defrect = crop->bounds; ++ return 0; ++} ++ ++/** ++ * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: input timings ++ */ ++static int ++vpif_enum_dv_timings(struct file *file, void *priv, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ int ret; ++ ++ ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ return -EINVAL; ++ return ret; ++} ++ ++/** ++ * vpif_query_dv_timings() - QUERY_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: input timings ++ */ ++static int ++vpif_query_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ int ret; ++ ++ ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings); ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ return -ENODATA; ++ return ret; ++} ++ ++/** ++ * vpif_s_dv_timings() - S_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: digital video timings ++ */ ++static int vpif_s_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ struct vpif_channel_config_params *std_info = &vpifparams->std_info; ++ struct video_obj *vid_ch = &ch->video; ++ struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; ++ int ret; ++ ++ if (timings->type != V4L2_DV_BT_656_1120) { ++ vpif_dbg(2, debug, "Timing type not defined\n"); ++ return -EINVAL; ++ } ++ ++ /* Configure subdevice timings, if any */ ++ ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings); ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ ret = 0; ++ if (ret < 0) { ++ vpif_dbg(2, debug, "Error setting custom DV timings\n"); ++ return ret; ++ } ++ ++ if (!(timings->bt.width && timings->bt.height && ++ (timings->bt.hbackporch || ++ timings->bt.hfrontporch || ++ timings->bt.hsync) && ++ timings->bt.vfrontporch && ++ (timings->bt.vbackporch || ++ timings->bt.vsync))) { ++ vpif_dbg(2, debug, "Timings for width, height, " ++ "horizontal back porch, horizontal sync, " ++ "horizontal front porch, vertical back porch, " ++ "vertical sync and vertical back porch " ++ "must be defined\n"); ++ return -EINVAL; ++ } ++ ++ vid_ch->dv_timings = *timings; ++ ++ /* Configure video port timings */ ++ ++ std_info->eav2sav = bt->hbackporch + bt->hfrontporch + ++ bt->hsync - 8; ++ std_info->sav2eav = bt->width; ++ ++ std_info->l1 = 1; ++ std_info->l3 = bt->vsync + bt->vbackporch + 1; ++ ++ if (bt->interlaced) { ++ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { ++ std_info->vsize = bt->height * 2 + ++ bt->vfrontporch + bt->vsync + bt->vbackporch + ++ bt->il_vfrontporch + bt->il_vsync + ++ bt->il_vbackporch; ++ std_info->l5 = std_info->vsize/2 - ++ (bt->vfrontporch - 1); ++ std_info->l7 = std_info->vsize/2 + 1; ++ std_info->l9 = std_info->l7 + bt->il_vsync + ++ bt->il_vbackporch + 1; ++ std_info->l11 = std_info->vsize - ++ (bt->il_vfrontporch - 1); ++ } else { ++ vpif_dbg(2, debug, "Required timing values for " ++ "interlaced BT format missing\n"); ++ return -EINVAL; ++ } ++ } else { ++ std_info->vsize = bt->height + bt->vfrontporch + ++ bt->vsync + bt->vbackporch; ++ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); ++ } ++ strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME); ++ std_info->width = bt->width; ++ std_info->height = bt->height; ++ std_info->frm_fmt = bt->interlaced ? 0 : 1; ++ std_info->ycmux_mode = 0; ++ std_info->capture_format = 0; ++ std_info->vbi_supported = 0; ++ std_info->hd_sd = 1; ++ std_info->stdid = 0; ++ ++ vid_ch->stdid = 0; ++ return 0; ++} ++ ++/** ++ * vpif_g_dv_timings() - G_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: digital video timings ++ */ ++static int vpif_g_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct video_obj *vid_ch = &ch->video; ++ ++ *timings = vid_ch->dv_timings; ++ ++ return 0; ++} ++ ++/* ++ * vpif_g_chip_ident() - Identify the chip ++ * @file: file ptr ++ * @priv: file handle ++ * @chip: chip identity ++ * ++ * Returns zero or -EINVAL if read operations fails. ++ */ ++static int vpif_g_chip_ident(struct file *file, void *priv, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && ++ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { ++ vpif_dbg(2, debug, "match_type is invalid.\n"); ++ return -EINVAL; ++ } ++ ++ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, ++ g_chip_ident, chip); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++/* ++ * vpif_dbg_g_register() - Read register ++ * @file: file ptr ++ * @priv: file handle ++ * @reg: register to be read ++ * ++ * Debugging only ++ * Returns zero or -EINVAL if read operations fails. ++ */ ++static int vpif_dbg_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg){ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_subdev_call(ch->sd, core, g_register, reg); ++} ++ ++/* ++ * vpif_dbg_s_register() - Write to register ++ * @file: file ptr ++ * @priv: file handle ++ * @reg: register to be modified ++ * ++ * Debugging only ++ * Returns zero or -EINVAL if write operations fails. ++ */ ++static int vpif_dbg_s_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg){ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_subdev_call(ch->sd, core, s_register, reg); ++} ++#endif ++ ++/* ++ * vpif_log_status() - Status information ++ * @file: file ptr ++ * @priv: file handle ++ * ++ * Returns zero. ++ */ ++static int vpif_log_status(struct file *filep, void *priv) ++{ ++ /* status for sub devices */ ++ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status); ++ ++ return 0; ++} ++ ++/* vpif capture ioctl operations */ ++static const struct v4l2_ioctl_ops vpif_ioctl_ops = { ++ .vidioc_querycap = vpif_querycap, ++ .vidioc_g_priority = vpif_g_priority, ++ .vidioc_s_priority = vpif_s_priority, ++ .vidioc_enum_fmt_vid_cap = vpif_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vpif_g_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vpif_s_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vpif_try_fmt_vid_cap, ++ .vidioc_enum_input = vpif_enum_input, ++ .vidioc_s_input = vpif_s_input, ++ .vidioc_g_input = vpif_g_input, ++ .vidioc_reqbufs = vpif_reqbufs, ++ .vidioc_querybuf = vpif_querybuf, ++ .vidioc_querystd = vpif_querystd, ++ .vidioc_s_std = vpif_s_std, ++ .vidioc_g_std = vpif_g_std, ++ .vidioc_qbuf = vpif_qbuf, ++ .vidioc_dqbuf = vpif_dqbuf, ++ .vidioc_streamon = vpif_streamon, ++ .vidioc_streamoff = vpif_streamoff, ++ .vidioc_cropcap = vpif_cropcap, ++ .vidioc_enum_dv_timings = vpif_enum_dv_timings, ++ .vidioc_query_dv_timings = vpif_query_dv_timings, ++ .vidioc_s_dv_timings = vpif_s_dv_timings, ++ .vidioc_g_dv_timings = vpif_g_dv_timings, ++ .vidioc_g_chip_ident = vpif_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vpif_dbg_g_register, ++ .vidioc_s_register = vpif_dbg_s_register, ++#endif ++ .vidioc_log_status = vpif_log_status, ++}; ++ ++/* vpif file operations */ ++static struct v4l2_file_operations vpif_fops = { ++ .owner = THIS_MODULE, ++ .open = vpif_open, ++ .release = vpif_release, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vpif_mmap, ++ .poll = vpif_poll ++}; ++ ++/* vpif video template */ ++static struct video_device vpif_video_template = { ++ .name = "vpif", ++ .fops = &vpif_fops, ++ .minor = -1, ++ .ioctl_ops = &vpif_ioctl_ops, ++}; ++ ++/** ++ * initialize_vpif() - Initialize vpif data structures ++ * ++ * Allocate memory for data structures and initialize them ++ */ ++static int initialize_vpif(void) ++{ ++ int err = 0, i, j; ++ int free_channel_objects_index; ++ ++ /* Default number of buffers should be 3 */ ++ if ((ch0_numbuffers > 0) && ++ (ch0_numbuffers < config_params.min_numbuffers)) ++ ch0_numbuffers = config_params.min_numbuffers; ++ if ((ch1_numbuffers > 0) && ++ (ch1_numbuffers < config_params.min_numbuffers)) ++ ch1_numbuffers = config_params.min_numbuffers; ++ ++ /* Set buffer size to min buffers size if it is invalid */ ++ if (ch0_bufsize < config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]) ++ ch0_bufsize = ++ config_params.min_bufsize[VPIF_CHANNEL0_VIDEO]; ++ if (ch1_bufsize < config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]) ++ ch1_bufsize = ++ config_params.min_bufsize[VPIF_CHANNEL1_VIDEO]; ++ ++ config_params.numbuffers[VPIF_CHANNEL0_VIDEO] = ch0_numbuffers; ++ config_params.numbuffers[VPIF_CHANNEL1_VIDEO] = ch1_numbuffers; ++ if (ch0_numbuffers) { ++ config_params.channel_bufsize[VPIF_CHANNEL0_VIDEO] ++ = ch0_bufsize; ++ } ++ if (ch1_numbuffers) { ++ config_params.channel_bufsize[VPIF_CHANNEL1_VIDEO] ++ = ch1_bufsize; ++ } ++ ++ /* Allocate memory for six channel objects */ ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ vpif_obj.dev[i] = ++ kzalloc(sizeof(*vpif_obj.dev[i]), GFP_KERNEL); ++ /* If memory allocation fails, return error */ ++ if (!vpif_obj.dev[i]) { ++ free_channel_objects_index = i; ++ err = -ENOMEM; ++ goto vpif_init_free_channel_objects; ++ } ++ } ++ return 0; ++ ++vpif_init_free_channel_objects: ++ for (j = 0; j < free_channel_objects_index; j++) ++ kfree(vpif_obj.dev[j]); ++ return err; ++} ++ ++/** ++ * vpif_probe : This function probes the vpif capture driver ++ * @pdev: platform device pointer ++ * ++ * This creates device entries by register itself to the V4L2 driver and ++ * initializes fields of each channel objects ++ */ ++static __init int vpif_probe(struct platform_device *pdev) ++{ ++ struct vpif_subdev_info *subdevdata; ++ struct vpif_capture_config *config; ++ int i, j, k, err; ++ int res_idx = 0; ++ struct i2c_adapter *i2c_adap; ++ struct channel_obj *ch; ++ struct common_obj *common; ++ struct video_device *vfd; ++ struct resource *res; ++ int subdev_count; ++ size_t size; ++ ++ vpif_dev = &pdev->dev; ++ ++ err = initialize_vpif(); ++ if (err) { ++ v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); ++ return err; ++ } ++ ++ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); ++ if (err) { ++ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); ++ return err; ++ } ++ ++ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { ++ for (i = res->start; i <= res->end; i++) { ++ if (request_irq(i, vpif_channel_isr, IRQF_SHARED, ++ "VPIF_Capture", (void *) ++ (&vpif_obj.dev[res_idx]->channel_id))) { ++ err = -EBUSY; ++ for (j = 0; j < i; j++) ++ free_irq(j, (void *) ++ (&vpif_obj.dev[res_idx]->channel_id)); ++ goto vpif_int_err; ++ } ++ } ++ res_idx++; ++ } ++ ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ /* Allocate memory for video device */ ++ vfd = video_device_alloc(); ++ if (NULL == vfd) { ++ for (j = 0; j < i; j++) { ++ ch = vpif_obj.dev[j]; ++ video_device_release(ch->video_dev); ++ } ++ err = -ENOMEM; ++ goto vpif_int_err; ++ } ++ ++ /* Initialize field of video device */ ++ *vfd = vpif_video_template; ++ vfd->v4l2_dev = &vpif_obj.v4l2_dev; ++ vfd->release = video_device_release; ++ snprintf(vfd->name, sizeof(vfd->name), ++ "VPIF_Capture_DRIVER_V%s", ++ VPIF_CAPTURE_VERSION); ++ /* Set video_dev to the video device */ ++ ch->video_dev = vfd; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) { ++ size = resource_size(res); ++ /* The resources are divided into two equal memory and when we ++ * have HD output we can add them together ++ */ ++ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { ++ ch = vpif_obj.dev[j]; ++ ch->channel_id = j; ++ /* only enabled if second resource exists */ ++ config_params.video_limit[ch->channel_id] = 0; ++ if (size) ++ config_params.video_limit[ch->channel_id] = ++ size/2; ++ } ++ } ++ ++ i2c_adap = i2c_get_adapter(1); ++ config = pdev->dev.platform_data; ++ ++ subdev_count = config->subdev_count; ++ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, ++ GFP_KERNEL); ++ if (vpif_obj.sd == NULL) { ++ vpif_err("unable to allocate memory for subdevice pointers\n"); ++ err = -ENOMEM; ++ goto vpif_sd_error; ++ } ++ ++ for (i = 0; i < subdev_count; i++) { ++ subdevdata = &config->subdev_info[i]; ++ vpif_obj.sd[i] = ++ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, ++ i2c_adap, ++ &subdevdata->board_info, ++ NULL); ++ ++ if (!vpif_obj.sd[i]) { ++ vpif_err("Error registering v4l2 subdevice\n"); ++ goto probe_subdev_out; ++ } ++ v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", ++ subdevdata->name); ++ } ++ ++ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) { ++ ch = vpif_obj.dev[j]; ++ ch->channel_id = j; ++ common = &(ch->common[VPIF_VIDEO_INDEX]); ++ spin_lock_init(&common->irqlock); ++ mutex_init(&common->lock); ++ ch->video_dev->lock = &common->lock; ++ /* Initialize prio member of channel object */ ++ v4l2_prio_init(&ch->prio); ++ video_set_drvdata(ch->video_dev, ch); ++ ++ /* select input 0 */ ++ err = vpif_set_input(config, ch, 0); ++ if (err) ++ goto probe_out; ++ ++ err = video_register_device(ch->video_dev, ++ VFL_TYPE_GRABBER, (j ? 1 : 0)); ++ if (err) ++ goto probe_out; ++ } ++ v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n"); ++ return 0; ++ ++probe_out: ++ for (k = 0; k < j; k++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[k]; ++ /* Unregister video device */ ++ video_unregister_device(ch->video_dev); ++ } ++probe_subdev_out: ++ /* free sub devices memory */ ++ kfree(vpif_obj.sd); ++ ++vpif_sd_error: ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ ch = vpif_obj.dev[i]; ++ /* Note: does nothing if ch->video_dev == NULL */ ++ video_device_release(ch->video_dev); ++ } ++vpif_int_err: ++ v4l2_device_unregister(&vpif_obj.v4l2_dev); ++ for (i = 0; i < res_idx; i++) { ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, i); ++ for (j = res->start; j <= res->end; j++) ++ free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id)); ++ } ++ return err; ++} ++ ++/** ++ * vpif_remove() - driver remove handler ++ * @device: ptr to platform device structure ++ * ++ * The vidoe device is unregistered ++ */ ++static int vpif_remove(struct platform_device *device) ++{ ++ int i; ++ struct channel_obj *ch; ++ ++ v4l2_device_unregister(&vpif_obj.v4l2_dev); ++ ++ /* un-register device */ ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ /* Unregister video device */ ++ video_unregister_device(ch->video_dev); ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++/** ++ * vpif_suspend: vpif device suspend ++ */ ++static int vpif_suspend(struct device *dev) ++{ ++ ++ struct common_obj *common; ++ struct channel_obj *ch; ++ int i; ++ ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++ if (ch->usrs && common->io_usrs) { ++ /* Disable channel */ ++ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { ++ enable_channel0(0); ++ channel0_intr_enable(0); ++ } ++ if (ch->channel_id == VPIF_CHANNEL1_VIDEO || ++ common->started == 2) { ++ enable_channel1(0); ++ channel1_intr_enable(0); ++ } ++ } ++ mutex_unlock(&common->lock); ++ } ++ ++ return 0; ++} ++ ++/* ++ * vpif_resume: vpif device suspend ++ */ ++static int vpif_resume(struct device *dev) ++{ ++ struct common_obj *common; ++ struct channel_obj *ch; ++ int i; ++ ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++ if (ch->usrs && common->io_usrs) { ++ /* Disable channel */ ++ if (ch->channel_id == VPIF_CHANNEL0_VIDEO) { ++ enable_channel0(1); ++ channel0_intr_enable(1); ++ } ++ if (ch->channel_id == VPIF_CHANNEL1_VIDEO || ++ common->started == 2) { ++ enable_channel1(1); ++ channel1_intr_enable(1); ++ } ++ } ++ mutex_unlock(&common->lock); ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops vpif_dev_pm_ops = { ++ .suspend = vpif_suspend, ++ .resume = vpif_resume, ++}; ++ ++#define vpif_pm_ops (&vpif_dev_pm_ops) ++#else ++#define vpif_pm_ops NULL ++#endif ++ ++static __refdata struct platform_driver vpif_driver = { ++ .driver = { ++ .name = "vpif_capture", ++ .owner = THIS_MODULE, ++ .pm = vpif_pm_ops, ++ }, ++ .probe = vpif_probe, ++ .remove = vpif_remove, ++}; ++ ++/** ++ * vpif_init: initialize the vpif driver ++ * ++ * This function registers device and driver to the kernel, requests irq ++ * handler and allocates memory ++ * for channel objects ++ */ ++static __init int vpif_init(void) ++{ ++ return platform_driver_register(&vpif_driver); ++} ++ ++/** ++ * vpif_cleanup : This function clean up the vpif capture resources ++ * ++ * This will un-registers device and driver to the kernel, frees ++ * requested irq handler and de-allocates memory allocated for channel ++ * objects. ++ */ ++static void vpif_cleanup(void) ++{ ++ struct platform_device *pdev; ++ struct resource *res; ++ int irq_num; ++ int i = 0; ++ ++ pdev = container_of(vpif_dev, struct platform_device, dev); ++ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { ++ for (irq_num = res->start; irq_num <= res->end; irq_num++) ++ free_irq(irq_num, ++ (void *)(&vpif_obj.dev[i]->channel_id)); ++ i++; ++ } ++ ++ platform_driver_unregister(&vpif_driver); ++ ++ kfree(vpif_obj.sd); ++ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) ++ kfree(vpif_obj.dev[i]); ++} ++ ++/* Function for module initialization and cleanup */ ++module_init(vpif_init); ++module_exit(vpif_cleanup); +diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h +new file mode 100644 +index 0000000..3d3c1e5 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif_capture.h +@@ -0,0 +1,162 @@ ++/* ++ * Copyright (C) 2009 Texas Instruments Inc ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VPIF_CAPTURE_H ++#define VPIF_CAPTURE_H ++ ++#ifdef __KERNEL__ ++ ++/* Header files */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "vpif.h" ++ ++/* Macros */ ++#define VPIF_CAPTURE_VERSION "0.0.2" ++ ++#define VPIF_VALID_FIELD(field) (((V4L2_FIELD_ANY == field) || \ ++ (V4L2_FIELD_NONE == field)) || \ ++ (((V4L2_FIELD_INTERLACED == field) || \ ++ (V4L2_FIELD_SEQ_TB == field)) || \ ++ (V4L2_FIELD_SEQ_BT == field))) ++ ++#define VPIF_CAPTURE_MAX_DEVICES 2 ++#define VPIF_VIDEO_INDEX 0 ++#define VPIF_NUMBER_OF_OBJECTS 1 ++ ++/* Enumerated data type to give id to each device per channel */ ++enum vpif_channel_id { ++ VPIF_CHANNEL0_VIDEO = 0, ++ VPIF_CHANNEL1_VIDEO, ++}; ++ ++struct video_obj { ++ enum v4l2_field buf_field; ++ /* Currently selected or default standard */ ++ v4l2_std_id stdid; ++ struct v4l2_dv_timings dv_timings; ++}; ++ ++struct vpif_cap_buffer { ++ struct vb2_buffer vb; ++ struct list_head list; ++}; ++ ++struct common_obj { ++ /* Pointer pointing to current v4l2_buffer */ ++ struct vpif_cap_buffer *cur_frm; ++ /* Pointer pointing to current v4l2_buffer */ ++ struct vpif_cap_buffer *next_frm; ++ /* ++ * This field keeps track of type of buffer exchange mechanism ++ * user has selected ++ */ ++ enum v4l2_memory memory; ++ /* Used to store pixel format */ ++ struct v4l2_format fmt; ++ /* Buffer queue used in video-buf */ ++ struct vb2_queue buffer_queue; ++ /* allocator-specific contexts for each plane */ ++ struct vb2_alloc_ctx *alloc_ctx; ++ /* Queue of filled frames */ ++ struct list_head dma_queue; ++ /* Used in video-buf */ ++ spinlock_t irqlock; ++ /* lock used to access this structure */ ++ struct mutex lock; ++ /* number of users performing IO */ ++ u32 io_usrs; ++ /* Indicates whether streaming started */ ++ u8 started; ++ /* Function pointer to set the addresses */ ++ void (*set_addr) (unsigned long, unsigned long, unsigned long, ++ unsigned long); ++ /* offset where Y top starts from the starting of the buffer */ ++ u32 ytop_off; ++ /* offset where Y bottom starts from the starting of the buffer */ ++ u32 ybtm_off; ++ /* offset where C top starts from the starting of the buffer */ ++ u32 ctop_off; ++ /* offset where C bottom starts from the starting of the buffer */ ++ u32 cbtm_off; ++ /* Indicates width of the image data */ ++ u32 width; ++ /* Indicates height of the image data */ ++ u32 height; ++}; ++ ++struct channel_obj { ++ /* Identifies video device for this channel */ ++ struct video_device *video_dev; ++ /* Used to keep track of state of the priority */ ++ struct v4l2_prio_state prio; ++ /* number of open instances of the channel */ ++ int usrs; ++ /* Indicates id of the field which is being displayed */ ++ u32 field_id; ++ /* flag to indicate whether decoder is initialized */ ++ u8 initialized; ++ /* Identifies channel */ ++ enum vpif_channel_id channel_id; ++ /* Current input */ ++ u32 input_idx; ++ /* subdev corresponding to the current input, may be NULL */ ++ struct v4l2_subdev *sd; ++ /* vpif configuration params */ ++ struct vpif_params vpifparams; ++ /* common object array */ ++ struct common_obj common[VPIF_NUMBER_OF_OBJECTS]; ++ /* video object */ ++ struct video_obj video; ++}; ++ ++/* File handle structure */ ++struct vpif_fh { ++ /* pointer to channel object for opened device */ ++ struct channel_obj *channel; ++ /* Indicates whether this file handle is doing IO */ ++ u8 io_allowed[VPIF_NUMBER_OF_OBJECTS]; ++ /* Used to keep track priority of this instance */ ++ enum v4l2_priority prio; ++ /* Used to indicate channel is initialize or not */ ++ u8 initialized; ++}; ++ ++struct vpif_device { ++ struct v4l2_device v4l2_dev; ++ struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS]; ++ struct v4l2_subdev **sd; ++}; ++ ++struct vpif_config_params { ++ u8 min_numbuffers; ++ u8 numbuffers[VPIF_CAPTURE_NUM_CHANNELS]; ++ s8 device_type; ++ u32 min_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; ++ u32 channel_bufsize[VPIF_CAPTURE_NUM_CHANNELS]; ++ u8 default_device[VPIF_CAPTURE_NUM_CHANNELS]; ++ u32 video_limit[VPIF_CAPTURE_NUM_CHANNELS]; ++ u8 max_device_type; ++}; ++ ++#endif /* End of __KERNEL__ */ ++#endif /* VPIF_CAPTURE_H */ +diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c +new file mode 100644 +index 0000000..dd249c9 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif_display.c +@@ -0,0 +1,2042 @@ ++/* ++ * vpif-display - VPIF display driver ++ * Display driver for TI DaVinci VPIF ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "vpif_display.h" ++#include "vpif.h" ++ ++MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(VPIF_DISPLAY_VERSION); ++ ++#define VPIF_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) ++ ++#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) ++#define vpif_dbg(level, debug, fmt, arg...) \ ++ v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) ++ ++static int debug = 1; ++static u32 ch2_numbuffers = 3; ++static u32 ch3_numbuffers = 3; ++static u32 ch2_bufsize = 1920 * 1080 * 2; ++static u32 ch3_bufsize = 720 * 576 * 2; ++ ++module_param(debug, int, 0644); ++module_param(ch2_numbuffers, uint, S_IRUGO); ++module_param(ch3_numbuffers, uint, S_IRUGO); ++module_param(ch2_bufsize, uint, S_IRUGO); ++module_param(ch3_bufsize, uint, S_IRUGO); ++ ++MODULE_PARM_DESC(debug, "Debug level 0-1"); ++MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); ++MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); ++MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); ++MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); ++ ++static struct vpif_config_params config_params = { ++ .min_numbuffers = 3, ++ .numbuffers[0] = 3, ++ .numbuffers[1] = 3, ++ .min_bufsize[0] = 720 * 480 * 2, ++ .min_bufsize[1] = 720 * 480 * 2, ++ .channel_bufsize[0] = 1920 * 1080 * 2, ++ .channel_bufsize[1] = 720 * 576 * 2, ++}; ++ ++static struct vpif_device vpif_obj = { {NULL} }; ++static struct device *vpif_dev; ++static void vpif_calculate_offsets(struct channel_obj *ch); ++static void vpif_config_addr(struct channel_obj *ch, int muxmode); ++ ++/* ++ * buffer_prepare: This is the callback function called from vb2_qbuf() ++ * function the buffer is prepared and user space virtual address is converted ++ * into physical address ++ */ ++static int vpif_buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_queue *q = vb->vb2_queue; ++ struct common_obj *common; ++ unsigned long addr; ++ ++ common = &fh->channel->common[VPIF_VIDEO_INDEX]; ++ if (vb->state != VB2_BUF_STATE_ACTIVE && ++ vb->state != VB2_BUF_STATE_PREPARED) { ++ vb2_set_plane_payload(vb, 0, common->fmt.fmt.pix.sizeimage); ++ if (vb2_plane_vaddr(vb, 0) && ++ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) ++ goto buf_align_exit; ++ ++ addr = vb2_dma_contig_plane_dma_addr(vb, 0); ++ if (q->streaming && ++ (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { ++ if (!ISALIGNED(addr + common->ytop_off) || ++ !ISALIGNED(addr + common->ybtm_off) || ++ !ISALIGNED(addr + common->ctop_off) || ++ !ISALIGNED(addr + common->cbtm_off)) ++ goto buf_align_exit; ++ } ++ } ++ return 0; ++ ++buf_align_exit: ++ vpif_err("buffer offset not aligned to 8 bytes\n"); ++ return -EINVAL; ++} ++ ++/* ++ * vpif_buffer_queue_setup: This function allocates memory for the buffers ++ */ ++static int vpif_buffer_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ unsigned long size; ++ ++ if (V4L2_MEMORY_MMAP == common->memory) { ++ size = config_params.channel_bufsize[ch->channel_id]; ++ /* ++ * Checking if the buffer size exceeds the available buffer ++ * ycmux_mode = 0 means 1 channel mode HD and ++ * ycmux_mode = 1 means 2 channels mode SD ++ */ ++ if (ch->vpifparams.std_info.ycmux_mode == 0) { ++ if (config_params.video_limit[ch->channel_id]) ++ while (size * *nbuffers > ++ (config_params.video_limit[0] ++ + config_params.video_limit[1])) ++ (*nbuffers)--; ++ } else { ++ if (config_params.video_limit[ch->channel_id]) ++ while (size * *nbuffers > ++ config_params.video_limit[ch->channel_id]) ++ (*nbuffers)--; ++ } ++ } else { ++ size = common->fmt.fmt.pix.sizeimage; ++ } ++ ++ if (*nbuffers < config_params.min_numbuffers) ++ *nbuffers = config_params.min_numbuffers; ++ ++ *nplanes = 1; ++ sizes[0] = size; ++ alloc_ctxs[0] = common->alloc_ctx; ++ return 0; ++} ++ ++/* ++ * vpif_buffer_queue: This function adds the buffer to DMA queue ++ */ ++static void vpif_buffer_queue(struct vb2_buffer *vb) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vpif_disp_buffer *buf = container_of(vb, ++ struct vpif_disp_buffer, vb); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long flags; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* add the buffer to the DMA queue */ ++ spin_lock_irqsave(&common->irqlock, flags); ++ list_add_tail(&buf->list, &common->dma_queue); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++} ++ ++/* ++ * vpif_buf_cleanup: This function is called from the videobuf2 layer to ++ * free memory allocated to the buffers ++ */ ++static void vpif_buf_cleanup(struct vb2_buffer *vb) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vb->vb2_queue); ++ struct vpif_disp_buffer *buf = container_of(vb, ++ struct vpif_disp_buffer, vb); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long flags; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ spin_lock_irqsave(&common->irqlock, flags); ++ if (vb->state == VB2_BUF_STATE_ACTIVE) ++ list_del_init(&buf->list); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++} ++ ++static void vpif_wait_prepare(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_unlock(&common->lock); ++} ++ ++static void vpif_wait_finish(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++} ++ ++static int vpif_buffer_init(struct vb2_buffer *vb) ++{ ++ struct vpif_disp_buffer *buf = container_of(vb, ++ struct vpif_disp_buffer, vb); ++ ++ INIT_LIST_HEAD(&buf->list); ++ ++ return 0; ++} ++ ++static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; ++ ++static int vpif_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct vpif_display_config *vpif_config_data = ++ vpif_dev->platform_data; ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_params *vpif = &ch->vpifparams; ++ unsigned long addr = 0; ++ unsigned long flags; ++ int ret; ++ ++ /* If buffer queue is empty, return error */ ++ spin_lock_irqsave(&common->irqlock, flags); ++ if (list_empty(&common->dma_queue)) { ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ vpif_err("buffer queue is empty\n"); ++ return -EIO; ++ } ++ ++ /* Get the next frame from the buffer queue */ ++ common->next_frm = common->cur_frm = ++ list_entry(common->dma_queue.next, ++ struct vpif_disp_buffer, list); ++ ++ list_del(&common->cur_frm->list); ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ /* Mark state of the current frame to active */ ++ common->cur_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ ++ /* Initialize field_id and started member */ ++ ch->field_id = 0; ++ common->started = 1; ++ addr = vb2_dma_contig_plane_dma_addr(&common->cur_frm->vb, 0); ++ /* Calculate the offset for Y and C data in the buffer */ ++ vpif_calculate_offsets(ch); ++ ++ if ((ch->vpifparams.std_info.frm_fmt && ++ ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) ++ && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) ++ || (!ch->vpifparams.std_info.frm_fmt ++ && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { ++ vpif_err("conflict in field format and std format\n"); ++ return -EINVAL; ++ } ++ ++ /* clock settings */ ++ if (vpif_config_data->set_clock) { ++ ret = vpif_config_data->set_clock(ch->vpifparams.std_info. ++ ycmux_mode, ch->vpifparams.std_info.hd_sd); ++ if (ret < 0) { ++ vpif_err("can't set clock\n"); ++ return ret; ++ } ++ } ++ ++ /* set the parameters and addresses */ ++ ret = vpif_set_video_params(vpif, ch->channel_id + 2); ++ if (ret < 0) ++ return ret; ++ ++ common->started = ret; ++ vpif_config_addr(ch, ret); ++ common->set_addr((addr + common->ytop_off), ++ (addr + common->ybtm_off), ++ (addr + common->ctop_off), ++ (addr + common->cbtm_off)); ++ ++ /* Set interrupt for both the fields in VPIF ++ Register enable channel in VPIF register */ ++ channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; ++ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { ++ channel2_intr_assert(); ++ channel2_intr_enable(1); ++ enable_channel2(1); ++ if (vpif_config_data->chan_config[VPIF_CHANNEL2_VIDEO].clip_en) ++ channel2_clipping_enable(1); ++ } ++ ++ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) ++ || (common->started == 2)) { ++ channel3_intr_assert(); ++ channel3_intr_enable(1); ++ enable_channel3(1); ++ if (vpif_config_data->chan_config[VPIF_CHANNEL3_VIDEO].clip_en) ++ channel3_clipping_enable(1); ++ } ++ ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static int vpif_stop_streaming(struct vb2_queue *vq) ++{ ++ struct vpif_fh *fh = vb2_get_drv_priv(vq); ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ unsigned long flags; ++ ++ if (!vb2_is_streaming(vq)) ++ return 0; ++ ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* release all active buffers */ ++ spin_lock_irqsave(&common->irqlock, flags); ++ while (!list_empty(&common->dma_queue)) { ++ common->next_frm = list_entry(common->dma_queue.next, ++ struct vpif_disp_buffer, list); ++ list_del(&common->next_frm->list); ++ vb2_buffer_done(&common->next_frm->vb, VB2_BUF_STATE_ERROR); ++ } ++ spin_unlock_irqrestore(&common->irqlock, flags); ++ ++ return 0; ++} ++ ++static struct vb2_ops video_qops = { ++ .queue_setup = vpif_buffer_queue_setup, ++ .wait_prepare = vpif_wait_prepare, ++ .wait_finish = vpif_wait_finish, ++ .buf_init = vpif_buffer_init, ++ .buf_prepare = vpif_buffer_prepare, ++ .start_streaming = vpif_start_streaming, ++ .stop_streaming = vpif_stop_streaming, ++ .buf_cleanup = vpif_buf_cleanup, ++ .buf_queue = vpif_buffer_queue, ++}; ++ ++static void process_progressive_mode(struct common_obj *common) ++{ ++ unsigned long addr = 0; ++ ++ spin_lock(&common->irqlock); ++ /* Get the next buffer from buffer queue */ ++ common->next_frm = list_entry(common->dma_queue.next, ++ struct vpif_disp_buffer, list); ++ /* Remove that buffer from the buffer queue */ ++ list_del(&common->next_frm->list); ++ spin_unlock(&common->irqlock); ++ /* Mark status of the buffer as active */ ++ common->next_frm->vb.state = VB2_BUF_STATE_ACTIVE; ++ ++ /* Set top and bottom field addrs in VPIF registers */ ++ addr = vb2_dma_contig_plane_dma_addr(&common->next_frm->vb, 0); ++ common->set_addr(addr + common->ytop_off, ++ addr + common->ybtm_off, ++ addr + common->ctop_off, ++ addr + common->cbtm_off); ++} ++ ++static void process_interlaced_mode(int fid, struct common_obj *common) ++{ ++ /* device field id and local field id are in sync */ ++ /* If this is even field */ ++ if (0 == fid) { ++ if (common->cur_frm == common->next_frm) ++ return; ++ ++ /* one frame is displayed If next frame is ++ * available, release cur_frm and move on */ ++ /* Copy frame display time */ ++ v4l2_get_timestamp(&common->cur_frm->vb.v4l2_buf.timestamp); ++ /* Change status of the cur_frm */ ++ vb2_buffer_done(&common->cur_frm->vb, ++ VB2_BUF_STATE_DONE); ++ /* Make cur_frm pointing to next_frm */ ++ common->cur_frm = common->next_frm; ++ ++ } else if (1 == fid) { /* odd field */ ++ spin_lock(&common->irqlock); ++ if (list_empty(&common->dma_queue) ++ || (common->cur_frm != common->next_frm)) { ++ spin_unlock(&common->irqlock); ++ return; ++ } ++ spin_unlock(&common->irqlock); ++ /* one field is displayed configure the next ++ * frame if it is available else hold on current ++ * frame */ ++ /* Get next from the buffer queue */ ++ process_progressive_mode(common); ++ } ++} ++ ++/* ++ * vpif_channel_isr: It changes status of the displayed buffer, takes next ++ * buffer from the queue and sets its address in VPIF registers ++ */ ++static irqreturn_t vpif_channel_isr(int irq, void *dev_id) ++{ ++ struct vpif_device *dev = &vpif_obj; ++ struct channel_obj *ch; ++ struct common_obj *common; ++ enum v4l2_field field; ++ int fid = -1, i; ++ int channel_id = 0; ++ ++ channel_id = *(int *)(dev_id); ++ if (!vpif_intr_status(channel_id + 2)) ++ return IRQ_NONE; ++ ++ ch = dev->dev[channel_id]; ++ field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; ++ for (i = 0; i < VPIF_NUMOBJECTS; i++) { ++ common = &ch->common[i]; ++ /* If streaming is started in this channel */ ++ if (0 == common->started) ++ continue; ++ ++ if (1 == ch->vpifparams.std_info.frm_fmt) { ++ spin_lock(&common->irqlock); ++ if (list_empty(&common->dma_queue)) { ++ spin_unlock(&common->irqlock); ++ continue; ++ } ++ spin_unlock(&common->irqlock); ++ ++ /* Progressive mode */ ++ if (!channel_first_int[i][channel_id]) { ++ /* Mark status of the cur_frm to ++ * done and unlock semaphore on it */ ++ v4l2_get_timestamp(&common->cur_frm->vb. ++ v4l2_buf.timestamp); ++ vb2_buffer_done(&common->cur_frm->vb, ++ VB2_BUF_STATE_DONE); ++ /* Make cur_frm pointing to next_frm */ ++ common->cur_frm = common->next_frm; ++ } ++ ++ channel_first_int[i][channel_id] = 0; ++ process_progressive_mode(common); ++ } else { ++ /* Interlaced mode */ ++ /* If it is first interrupt, ignore it */ ++ ++ if (channel_first_int[i][channel_id]) { ++ channel_first_int[i][channel_id] = 0; ++ continue; ++ } ++ ++ if (0 == i) { ++ ch->field_id ^= 1; ++ /* Get field id from VPIF registers */ ++ fid = vpif_channel_getfid(ch->channel_id + 2); ++ /* If fid does not match with stored field id */ ++ if (fid != ch->field_id) { ++ /* Make them in sync */ ++ if (0 == fid) ++ ch->field_id = fid; ++ ++ return IRQ_HANDLED; ++ } ++ } ++ process_interlaced_mode(fid, common); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int vpif_update_std_info(struct channel_obj *ch) ++{ ++ struct video_obj *vid_ch = &ch->video; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ struct vpif_channel_config_params *std_info = &vpifparams->std_info; ++ const struct vpif_channel_config_params *config; ++ ++ int i; ++ ++ for (i = 0; i < vpif_ch_params_count; i++) { ++ config = &ch_params[i]; ++ if (config->hd_sd == 0) { ++ vpif_dbg(2, debug, "SD format\n"); ++ if (config->stdid & vid_ch->stdid) { ++ memcpy(std_info, config, sizeof(*config)); ++ break; ++ } ++ } ++ } ++ ++ if (i == vpif_ch_params_count) { ++ vpif_dbg(1, debug, "Format not found\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int vpif_update_resolution(struct channel_obj *ch) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct video_obj *vid_ch = &ch->video; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ struct vpif_channel_config_params *std_info = &vpifparams->std_info; ++ ++ if (!vid_ch->stdid && !vid_ch->dv_timings.bt.height) ++ return -EINVAL; ++ ++ if (vid_ch->stdid) { ++ if (vpif_update_std_info(ch)) ++ return -EINVAL; ++ } ++ ++ common->fmt.fmt.pix.width = std_info->width; ++ common->fmt.fmt.pix.height = std_info->height; ++ vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", ++ common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); ++ ++ /* Set height and width paramateres */ ++ common->height = std_info->height; ++ common->width = std_info->width; ++ ++ return 0; ++} ++ ++/* ++ * vpif_calculate_offsets: This function calculates buffers offset for Y and C ++ * in the top and bottom field ++ */ ++static void vpif_calculate_offsets(struct channel_obj *ch) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ enum v4l2_field field = common->fmt.fmt.pix.field; ++ struct video_obj *vid_ch = &ch->video; ++ unsigned int hpitch, vpitch, sizeimage; ++ ++ if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { ++ if (ch->vpifparams.std_info.frm_fmt) ++ vid_ch->buf_field = V4L2_FIELD_NONE; ++ else ++ vid_ch->buf_field = V4L2_FIELD_INTERLACED; ++ } else { ++ vid_ch->buf_field = common->fmt.fmt.pix.field; ++ } ++ ++ sizeimage = common->fmt.fmt.pix.sizeimage; ++ ++ hpitch = common->fmt.fmt.pix.bytesperline; ++ vpitch = sizeimage / (hpitch * 2); ++ if ((V4L2_FIELD_NONE == vid_ch->buf_field) || ++ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { ++ common->ytop_off = 0; ++ common->ybtm_off = hpitch; ++ common->ctop_off = sizeimage / 2; ++ common->cbtm_off = sizeimage / 2 + hpitch; ++ } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { ++ common->ytop_off = 0; ++ common->ybtm_off = sizeimage / 4; ++ common->ctop_off = sizeimage / 2; ++ common->cbtm_off = common->ctop_off + sizeimage / 4; ++ } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { ++ common->ybtm_off = 0; ++ common->ytop_off = sizeimage / 4; ++ common->cbtm_off = sizeimage / 2; ++ common->ctop_off = common->cbtm_off + sizeimage / 4; ++ } ++ ++ if ((V4L2_FIELD_NONE == vid_ch->buf_field) || ++ (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { ++ vpifparams->video_params.storage_mode = 1; ++ } else { ++ vpifparams->video_params.storage_mode = 0; ++ } ++ ++ if (ch->vpifparams.std_info.frm_fmt == 1) { ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline; ++ } else { ++ if ((field == V4L2_FIELD_ANY) || ++ (field == V4L2_FIELD_INTERLACED)) ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline * 2; ++ else ++ vpifparams->video_params.hpitch = ++ common->fmt.fmt.pix.bytesperline; ++ } ++ ++ ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; ++} ++ ++static void vpif_config_format(struct channel_obj *ch) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ common->fmt.fmt.pix.field = V4L2_FIELD_ANY; ++ if (config_params.numbuffers[ch->channel_id] == 0) ++ common->memory = V4L2_MEMORY_USERPTR; ++ else ++ common->memory = V4L2_MEMORY_MMAP; ++ ++ common->fmt.fmt.pix.sizeimage = ++ config_params.channel_bufsize[ch->channel_id]; ++ common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; ++ common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++} ++ ++static int vpif_check_format(struct channel_obj *ch, ++ struct v4l2_pix_format *pixfmt) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ enum v4l2_field field = pixfmt->field; ++ u32 sizeimage, hpitch, vpitch; ++ ++ if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) ++ goto invalid_fmt_exit; ++ ++ if (!(VPIF_VALID_FIELD(field))) ++ goto invalid_fmt_exit; ++ ++ if (pixfmt->bytesperline <= 0) ++ goto invalid_pitch_exit; ++ ++ sizeimage = pixfmt->sizeimage; ++ ++ if (vpif_update_resolution(ch)) ++ return -EINVAL; ++ ++ hpitch = pixfmt->bytesperline; ++ vpitch = sizeimage / (hpitch * 2); ++ ++ /* Check for valid value of pitch */ ++ if ((hpitch < ch->vpifparams.std_info.width) || ++ (vpitch < ch->vpifparams.std_info.height)) ++ goto invalid_pitch_exit; ++ ++ /* Check for 8 byte alignment */ ++ if (!ISALIGNED(hpitch)) { ++ vpif_err("invalid pitch alignment\n"); ++ return -EINVAL; ++ } ++ pixfmt->width = common->fmt.fmt.pix.width; ++ pixfmt->height = common->fmt.fmt.pix.height; ++ ++ return 0; ++ ++invalid_fmt_exit: ++ vpif_err("invalid field format\n"); ++ return -EINVAL; ++ ++invalid_pitch_exit: ++ vpif_err("invalid pitch\n"); ++ return -EINVAL; ++} ++ ++static void vpif_config_addr(struct channel_obj *ch, int muxmode) ++{ ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { ++ common->set_addr = ch3_set_videobuf_addr; ++ } else { ++ if (2 == muxmode) ++ common->set_addr = ch2_set_videobuf_addr_yc_nmux; ++ else ++ common->set_addr = ch2_set_videobuf_addr; ++ } ++} ++ ++/* ++ * vpif_mmap: It is used to map kernel space buffers into user spaces ++ */ ++static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) ++{ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &(ch->common[VPIF_VIDEO_INDEX]); ++ int ret; ++ ++ vpif_dbg(2, debug, "vpif_mmap\n"); ++ ++ if (mutex_lock_interruptible(&common->lock)) ++ return -ERESTARTSYS; ++ ret = vb2_mmap(&common->buffer_queue, vma); ++ mutex_unlock(&common->lock); ++ return ret; ++} ++ ++/* ++ * vpif_poll: It is used for select/poll system call ++ */ ++static unsigned int vpif_poll(struct file *filep, poll_table *wait) ++{ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ unsigned int res = 0; ++ ++ if (common->started) { ++ mutex_lock(&common->lock); ++ res = vb2_poll(&common->buffer_queue, filep, wait); ++ mutex_unlock(&common->lock); ++ } ++ ++ return res; ++} ++ ++/* ++ * vpif_open: It creates object of file handle structure and stores it in ++ * private_data member of filepointer ++ */ ++static int vpif_open(struct file *filep) ++{ ++ struct video_device *vdev = video_devdata(filep); ++ struct channel_obj *ch = video_get_drvdata(vdev); ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_fh *fh; ++ ++ /* Allocate memory for the file handle object */ ++ fh = kzalloc(sizeof(struct vpif_fh), GFP_KERNEL); ++ if (fh == NULL) { ++ vpif_err("unable to allocate memory for file handle object\n"); ++ return -ENOMEM; ++ } ++ ++ if (mutex_lock_interruptible(&common->lock)) { ++ kfree(fh); ++ return -ERESTARTSYS; ++ } ++ /* store pointer to fh in private_data member of filep */ ++ filep->private_data = fh; ++ fh->channel = ch; ++ fh->initialized = 0; ++ if (!ch->initialized) { ++ fh->initialized = 1; ++ ch->initialized = 1; ++ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); ++ } ++ ++ /* Increment channel usrs counter */ ++ atomic_inc(&ch->usrs); ++ /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ ++ fh->io_allowed[VPIF_VIDEO_INDEX] = 0; ++ /* Initialize priority of this instance to default priority */ ++ fh->prio = V4L2_PRIORITY_UNSET; ++ v4l2_prio_open(&ch->prio, &fh->prio); ++ mutex_unlock(&common->lock); ++ ++ return 0; ++} ++ ++/* ++ * vpif_release: This function deletes buffer queue, frees the buffers and ++ * the vpif file handle ++ */ ++static int vpif_release(struct file *filep) ++{ ++ struct vpif_fh *fh = filep->private_data; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ mutex_lock(&common->lock); ++ /* if this instance is doing IO */ ++ if (fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ /* Reset io_usrs member of channel object */ ++ common->io_usrs = 0; ++ /* Disable channel */ ++ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { ++ enable_channel2(0); ++ channel2_intr_enable(0); ++ } ++ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || ++ (2 == common->started)) { ++ enable_channel3(0); ++ channel3_intr_enable(0); ++ } ++ common->started = 0; ++ ++ /* Free buffers allocated */ ++ vb2_queue_release(&common->buffer_queue); ++ vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ++ ++ common->numbuffers = ++ config_params.numbuffers[ch->channel_id]; ++ } ++ ++ /* Decrement channel usrs counter */ ++ atomic_dec(&ch->usrs); ++ /* If this file handle has initialize encoder device, reset it */ ++ if (fh->initialized) ++ ch->initialized = 0; ++ ++ /* Close the priority */ ++ v4l2_prio_close(&ch->prio, fh->prio); ++ filep->private_data = NULL; ++ fh->initialized = 0; ++ mutex_unlock(&common->lock); ++ kfree(fh); ++ ++ return 0; ++} ++ ++/* functions implementing ioctls */ ++/** ++ * vpif_querycap() - QUERYCAP handler ++ * @file: file ptr ++ * @priv: file handle ++ * @cap: ptr to v4l2_capability structure ++ */ ++static int vpif_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct vpif_display_config *config = vpif_dev->platform_data; ++ ++ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ snprintf(cap->driver, sizeof(cap->driver), "%s", dev_name(vpif_dev)); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", ++ dev_name(vpif_dev)); ++ strlcpy(cap->card, config->card_name, sizeof(cap->card)); ++ ++ return 0; ++} ++ ++static int vpif_enum_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index != 0) { ++ vpif_err("Invalid format index\n"); ++ return -EINVAL; ++ } ++ ++ /* Fill in the information about format */ ++ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); ++ fmt->pixelformat = V4L2_PIX_FMT_YUV422P; ++ ++ return 0; ++} ++ ++static int vpif_g_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ /* Check the validity of the buffer type */ ++ if (common->fmt.type != fmt->type) ++ return -EINVAL; ++ ++ if (vpif_update_resolution(ch)) ++ return -EINVAL; ++ *fmt = common->fmt; ++ return 0; ++} ++ ++static int vpif_s_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct v4l2_pix_format *pixfmt; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ int ret = 0; ++ ++ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) ++ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_dbg(1, debug, "Channel Busy\n"); ++ return -EBUSY; ++ } ++ ++ /* Check for the priority */ ++ ret = v4l2_prio_check(&ch->prio, fh->prio); ++ if (0 != ret) ++ return ret; ++ fh->initialized = 1; ++ } ++ ++ if (common->started) { ++ vpif_dbg(1, debug, "Streaming in progress\n"); ++ return -EBUSY; ++ } ++ ++ pixfmt = &fmt->fmt.pix; ++ /* Check for valid field format */ ++ ret = vpif_check_format(ch, pixfmt); ++ if (ret) ++ return ret; ++ ++ /* store the pix format in the channel object */ ++ common->fmt.fmt.pix = *pixfmt; ++ /* store the format in the channel object */ ++ common->fmt = *fmt; ++ return 0; ++} ++ ++static int vpif_try_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; ++ int ret = 0; ++ ++ ret = vpif_check_format(ch, pixfmt); ++ if (ret) { ++ *pixfmt = common->fmt.fmt.pix; ++ pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; ++ } ++ ++ return ret; ++} ++ ++static int vpif_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbuf) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common; ++ enum v4l2_field field; ++ struct vb2_queue *q; ++ u8 index = 0; ++ int ret; ++ ++ /* This file handle has not initialized the channel, ++ It is not allowed to do settings */ ++ if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) ++ || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { ++ if (!fh->initialized) { ++ vpif_err("Channel Busy\n"); ++ return -EBUSY; ++ } ++ } ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) ++ return -EINVAL; ++ ++ index = VPIF_VIDEO_INDEX; ++ ++ common = &ch->common[index]; ++ ++ if (common->fmt.type != reqbuf->type || !vpif_dev) ++ return -EINVAL; ++ if (0 != common->io_usrs) ++ return -EBUSY; ++ ++ if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) ++ field = V4L2_FIELD_INTERLACED; ++ else ++ field = common->fmt.fmt.pix.field; ++ } else { ++ field = V4L2_VBI_INTERLACED; ++ } ++ /* Initialize videobuf2 queue as per the buffer type */ ++ common->alloc_ctx = vb2_dma_contig_init_ctx(vpif_dev); ++ if (IS_ERR(common->alloc_ctx)) { ++ vpif_err("Failed to get the context\n"); ++ return PTR_ERR(common->alloc_ctx); ++ } ++ q = &common->buffer_queue; ++ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ q->io_modes = VB2_MMAP | VB2_USERPTR; ++ q->drv_priv = fh; ++ q->ops = &video_qops; ++ q->mem_ops = &vb2_dma_contig_memops; ++ q->buf_struct_size = sizeof(struct vpif_disp_buffer); ++ ++ ret = vb2_queue_init(q); ++ if (ret) { ++ vpif_err("vpif_display: vb2_queue_init() failed\n"); ++ vb2_dma_contig_cleanup_ctx(common->alloc_ctx); ++ return ret; ++ } ++ /* Set io allowed member of file handle to TRUE */ ++ fh->io_allowed[index] = 1; ++ /* Increment io usrs member of channel object to 1 */ ++ common->io_usrs = 1; ++ /* Store type of memory requested in channel object */ ++ common->memory = reqbuf->memory; ++ INIT_LIST_HEAD(&common->dma_queue); ++ /* Allocate buffers */ ++ return vb2_reqbufs(&common->buffer_queue, reqbuf); ++} ++ ++static int vpif_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *tbuf) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ if (common->fmt.type != tbuf->type) ++ return -EINVAL; ++ ++ return vb2_querybuf(&common->buffer_queue, tbuf); ++} ++ ++static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct vpif_fh *fh = NULL; ++ struct channel_obj *ch = NULL; ++ struct common_obj *common = NULL; ++ ++ if (!buf || !priv) ++ return -EINVAL; ++ ++ fh = priv; ++ ch = fh->channel; ++ if (!ch) ++ return -EINVAL; ++ ++ common = &(ch->common[VPIF_VIDEO_INDEX]); ++ if (common->fmt.type != buf->type) ++ return -EINVAL; ++ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_err("fh->io_allowed\n"); ++ return -EACCES; ++ } ++ ++ return vb2_qbuf(&common->buffer_queue, buf); ++} ++ ++static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ int ret = 0; ++ ++ if (!(*std_id & VPIF_V4L2_STD)) ++ return -EINVAL; ++ ++ if (common->started) { ++ vpif_err("streaming in progress\n"); ++ return -EBUSY; ++ } ++ ++ /* Call encoder subdevice function to set the standard */ ++ ch->video.stdid = *std_id; ++ memset(&ch->video.dv_timings, 0, sizeof(ch->video.dv_timings)); ++ /* Get the information about the standard */ ++ if (vpif_update_resolution(ch)) ++ return -EINVAL; ++ ++ if ((ch->vpifparams.std_info.width * ++ ch->vpifparams.std_info.height * 2) > ++ config_params.channel_bufsize[ch->channel_id]) { ++ vpif_err("invalid std for this size\n"); ++ return -EINVAL; ++ } ++ ++ common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; ++ /* Configure the default format information */ ++ vpif_config_format(ch); ++ ++ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, ++ s_std_output, *std_id); ++ if (ret < 0) { ++ vpif_err("Failed to set output standard\n"); ++ return ret; ++ } ++ ++ ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, ++ s_std, *std_id); ++ if (ret < 0) ++ vpif_err("Failed to set standard for sub devices\n"); ++ return ret; ++} ++ ++static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ *std = ch->video.stdid; ++ return 0; ++} ++ ++static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ return vb2_dqbuf(&common->buffer_queue, p, ++ (file->f_flags & O_NONBLOCK)); ++} ++ ++static int vpif_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type buftype) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; ++ int ret = 0; ++ ++ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ vpif_err("buffer type not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_err("fh->io_allowed\n"); ++ return -EACCES; ++ } ++ ++ /* If Streaming is already started, return error */ ++ if (common->started) { ++ vpif_err("channel->started\n"); ++ return -EBUSY; ++ } ++ ++ if ((ch->channel_id == VPIF_CHANNEL2_VIDEO ++ && oth_ch->common[VPIF_VIDEO_INDEX].started && ++ ch->vpifparams.std_info.ycmux_mode == 0) ++ || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) ++ && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { ++ vpif_err("other channel is using\n"); ++ return -EBUSY; ++ } ++ ++ ret = vpif_check_format(ch, &common->fmt.fmt.pix); ++ if (ret < 0) ++ return ret; ++ ++ /* Call vb2_streamon to start streaming in videobuf2 */ ++ ret = vb2_streamon(&common->buffer_queue, buftype); ++ if (ret < 0) { ++ vpif_err("vb2_streamon\n"); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++static int vpif_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type buftype) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ struct vpif_display_config *vpif_config_data = ++ vpif_dev->platform_data; ++ ++ if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ vpif_err("buffer type not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { ++ vpif_err("fh->io_allowed\n"); ++ return -EACCES; ++ } ++ ++ if (!common->started) { ++ vpif_err("channel->started\n"); ++ return -EINVAL; ++ } ++ ++ if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { ++ /* disable channel */ ++ if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { ++ if (vpif_config_data-> ++ chan_config[VPIF_CHANNEL2_VIDEO].clip_en) ++ channel2_clipping_enable(0); ++ enable_channel2(0); ++ channel2_intr_enable(0); ++ } ++ if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || ++ (2 == common->started)) { ++ if (vpif_config_data-> ++ chan_config[VPIF_CHANNEL3_VIDEO].clip_en) ++ channel3_clipping_enable(0); ++ enable_channel3(0); ++ channel3_intr_enable(0); ++ } ++ } ++ ++ common->started = 0; ++ return vb2_streamoff(&common->buffer_queue, buftype); ++} ++ ++static int vpif_cropcap(struct file *file, void *priv, ++ struct v4l2_cropcap *crop) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) ++ return -EINVAL; ++ ++ crop->bounds.left = crop->bounds.top = 0; ++ crop->defrect.left = crop->defrect.top = 0; ++ crop->defrect.height = crop->bounds.height = common->height; ++ crop->defrect.width = crop->bounds.width = common->width; ++ ++ return 0; ++} ++ ++static int vpif_enum_output(struct file *file, void *fh, ++ struct v4l2_output *output) ++{ ++ ++ struct vpif_display_config *config = vpif_dev->platform_data; ++ struct vpif_display_chan_config *chan_cfg; ++ struct vpif_fh *vpif_handler = fh; ++ struct channel_obj *ch = vpif_handler->channel; ++ ++ chan_cfg = &config->chan_config[ch->channel_id]; ++ if (output->index >= chan_cfg->output_count) { ++ vpif_dbg(1, debug, "Invalid output index\n"); ++ return -EINVAL; ++ } ++ ++ *output = chan_cfg->outputs[output->index].output; ++ return 0; ++} ++ ++/** ++ * vpif_output_to_subdev() - Maps output to sub device ++ * @vpif_cfg - global config ptr ++ * @chan_cfg - channel config ptr ++ * @index - Given output index from application ++ * ++ * lookup the sub device information for a given output index. ++ * we report all the output to application. output table also ++ * has sub device name for the each output ++ */ ++static int ++vpif_output_to_subdev(struct vpif_display_config *vpif_cfg, ++ struct vpif_display_chan_config *chan_cfg, int index) ++{ ++ struct vpif_subdev_info *subdev_info; ++ const char *subdev_name; ++ int i; ++ ++ vpif_dbg(2, debug, "vpif_output_to_subdev\n"); ++ ++ if (chan_cfg->outputs == NULL) ++ return -1; ++ ++ subdev_name = chan_cfg->outputs[index].subdev_name; ++ if (subdev_name == NULL) ++ return -1; ++ ++ /* loop through the sub device list to get the sub device info */ ++ for (i = 0; i < vpif_cfg->subdev_count; i++) { ++ subdev_info = &vpif_cfg->subdevinfo[i]; ++ if (!strcmp(subdev_info->name, subdev_name)) ++ return i; ++ } ++ return -1; ++} ++ ++/** ++ * vpif_set_output() - Select an output ++ * @vpif_cfg - global config ptr ++ * @ch - channel ++ * @index - Given output index from application ++ * ++ * Select the given output. ++ */ ++static int vpif_set_output(struct vpif_display_config *vpif_cfg, ++ struct channel_obj *ch, int index) ++{ ++ struct vpif_display_chan_config *chan_cfg = ++ &vpif_cfg->chan_config[ch->channel_id]; ++ struct vpif_subdev_info *subdev_info = NULL; ++ struct v4l2_subdev *sd = NULL; ++ u32 input = 0, output = 0; ++ int sd_index; ++ int ret; ++ ++ sd_index = vpif_output_to_subdev(vpif_cfg, chan_cfg, index); ++ if (sd_index >= 0) { ++ sd = vpif_obj.sd[sd_index]; ++ subdev_info = &vpif_cfg->subdevinfo[sd_index]; ++ } ++ ++ if (sd) { ++ input = chan_cfg->outputs[index].input_route; ++ output = chan_cfg->outputs[index].output_route; ++ ret = v4l2_subdev_call(sd, video, s_routing, input, output, 0); ++ if (ret < 0 && ret != -ENOIOCTLCMD) { ++ vpif_err("Failed to set output\n"); ++ return ret; ++ } ++ ++ } ++ ch->output_idx = index; ++ ch->sd = sd; ++ if (chan_cfg->outputs != NULL) ++ /* update tvnorms from the sub device output info */ ++ ch->video_dev->tvnorms = chan_cfg->outputs[index].output.std; ++ return 0; ++} ++ ++static int vpif_s_output(struct file *file, void *priv, unsigned int i) ++{ ++ struct vpif_display_config *config = vpif_dev->platform_data; ++ struct vpif_display_chan_config *chan_cfg; ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; ++ ++ chan_cfg = &config->chan_config[ch->channel_id]; ++ ++ if (i >= chan_cfg->output_count) ++ return -EINVAL; ++ ++ if (common->started) { ++ vpif_err("Streaming in progress\n"); ++ return -EBUSY; ++ } ++ ++ return vpif_set_output(config, ch, i); ++} ++ ++static int vpif_g_output(struct file *file, void *priv, unsigned int *i) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ *i = ch->output_idx; ++ ++ return 0; ++} ++ ++static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ *p = v4l2_prio_max(&ch->prio); ++ ++ return 0; ++} ++ ++static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_prio_change(&ch->prio, &fh->prio, p); ++} ++ ++/** ++ * vpif_enum_dv_timings() - ENUM_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: input timings ++ */ ++static int ++vpif_enum_dv_timings(struct file *file, void *priv, ++ struct v4l2_enum_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ int ret; ++ ++ ret = v4l2_subdev_call(ch->sd, video, enum_dv_timings, timings); ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ return -EINVAL; ++ return ret; ++} ++ ++/** ++ * vpif_s_dv_timings() - S_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: digital video timings ++ */ ++static int vpif_s_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct vpif_params *vpifparams = &ch->vpifparams; ++ struct vpif_channel_config_params *std_info = &vpifparams->std_info; ++ struct video_obj *vid_ch = &ch->video; ++ struct v4l2_bt_timings *bt = &vid_ch->dv_timings.bt; ++ int ret; ++ ++ if (timings->type != V4L2_DV_BT_656_1120) { ++ vpif_dbg(2, debug, "Timing type not defined\n"); ++ return -EINVAL; ++ } ++ ++ /* Configure subdevice timings, if any */ ++ ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings); ++ if (ret == -ENOIOCTLCMD || ret == -ENODEV) ++ ret = 0; ++ if (ret < 0) { ++ vpif_dbg(2, debug, "Error setting custom DV timings\n"); ++ return ret; ++ } ++ ++ if (!(timings->bt.width && timings->bt.height && ++ (timings->bt.hbackporch || ++ timings->bt.hfrontporch || ++ timings->bt.hsync) && ++ timings->bt.vfrontporch && ++ (timings->bt.vbackporch || ++ timings->bt.vsync))) { ++ vpif_dbg(2, debug, "Timings for width, height, " ++ "horizontal back porch, horizontal sync, " ++ "horizontal front porch, vertical back porch, " ++ "vertical sync and vertical back porch " ++ "must be defined\n"); ++ return -EINVAL; ++ } ++ ++ vid_ch->dv_timings = *timings; ++ ++ /* Configure video port timings */ ++ ++ std_info->eav2sav = bt->hbackporch + bt->hfrontporch + ++ bt->hsync - 8; ++ std_info->sav2eav = bt->width; ++ ++ std_info->l1 = 1; ++ std_info->l3 = bt->vsync + bt->vbackporch + 1; ++ ++ if (bt->interlaced) { ++ if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) { ++ std_info->vsize = bt->height * 2 + ++ bt->vfrontporch + bt->vsync + bt->vbackporch + ++ bt->il_vfrontporch + bt->il_vsync + ++ bt->il_vbackporch; ++ std_info->l5 = std_info->vsize/2 - ++ (bt->vfrontporch - 1); ++ std_info->l7 = std_info->vsize/2 + 1; ++ std_info->l9 = std_info->l7 + bt->il_vsync + ++ bt->il_vbackporch + 1; ++ std_info->l11 = std_info->vsize - ++ (bt->il_vfrontporch - 1); ++ } else { ++ vpif_dbg(2, debug, "Required timing values for " ++ "interlaced BT format missing\n"); ++ return -EINVAL; ++ } ++ } else { ++ std_info->vsize = bt->height + bt->vfrontporch + ++ bt->vsync + bt->vbackporch; ++ std_info->l5 = std_info->vsize - (bt->vfrontporch - 1); ++ } ++ strncpy(std_info->name, "Custom timings BT656/1120", ++ VPIF_MAX_NAME); ++ std_info->width = bt->width; ++ std_info->height = bt->height; ++ std_info->frm_fmt = bt->interlaced ? 0 : 1; ++ std_info->ycmux_mode = 0; ++ std_info->capture_format = 0; ++ std_info->vbi_supported = 0; ++ std_info->hd_sd = 1; ++ std_info->stdid = 0; ++ vid_ch->stdid = 0; ++ ++ return 0; ++} ++ ++/** ++ * vpif_g_dv_timings() - G_DV_TIMINGS handler ++ * @file: file ptr ++ * @priv: file handle ++ * @timings: digital video timings ++ */ ++static int vpif_g_dv_timings(struct file *file, void *priv, ++ struct v4l2_dv_timings *timings) ++{ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ struct video_obj *vid_ch = &ch->video; ++ ++ *timings = vid_ch->dv_timings; ++ ++ return 0; ++} ++ ++/* ++ * vpif_g_chip_ident() - Identify the chip ++ * @file: file ptr ++ * @priv: file handle ++ * @chip: chip identity ++ * ++ * Returns zero or -EINVAL if read operations fails. ++ */ ++static int vpif_g_chip_ident(struct file *file, void *priv, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && ++ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { ++ vpif_dbg(2, debug, "match_type is invalid.\n"); ++ return -EINVAL; ++ } ++ ++ return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, ++ g_chip_ident, chip); ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++/* ++ * vpif_dbg_g_register() - Read register ++ * @file: file ptr ++ * @priv: file handle ++ * @reg: register to be read ++ * ++ * Debugging only ++ * Returns zero or -EINVAL if read operations fails. ++ */ ++static int vpif_dbg_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg){ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_subdev_call(ch->sd, core, g_register, reg); ++} ++ ++/* ++ * vpif_dbg_s_register() - Write to register ++ * @file: file ptr ++ * @priv: file handle ++ * @reg: register to be modified ++ * ++ * Debugging only ++ * Returns zero or -EINVAL if write operations fails. ++ */ ++static int vpif_dbg_s_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg){ ++ struct vpif_fh *fh = priv; ++ struct channel_obj *ch = fh->channel; ++ ++ return v4l2_subdev_call(ch->sd, core, s_register, reg); ++} ++#endif ++ ++/* ++ * vpif_log_status() - Status information ++ * @file: file ptr ++ * @priv: file handle ++ * ++ * Returns zero. ++ */ ++static int vpif_log_status(struct file *filep, void *priv) ++{ ++ /* status for sub devices */ ++ v4l2_device_call_all(&vpif_obj.v4l2_dev, 0, core, log_status); ++ ++ return 0; ++} ++ ++/* vpif display ioctl operations */ ++static const struct v4l2_ioctl_ops vpif_ioctl_ops = { ++ .vidioc_querycap = vpif_querycap, ++ .vidioc_g_priority = vpif_g_priority, ++ .vidioc_s_priority = vpif_s_priority, ++ .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, ++ .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, ++ .vidioc_reqbufs = vpif_reqbufs, ++ .vidioc_querybuf = vpif_querybuf, ++ .vidioc_qbuf = vpif_qbuf, ++ .vidioc_dqbuf = vpif_dqbuf, ++ .vidioc_streamon = vpif_streamon, ++ .vidioc_streamoff = vpif_streamoff, ++ .vidioc_s_std = vpif_s_std, ++ .vidioc_g_std = vpif_g_std, ++ .vidioc_enum_output = vpif_enum_output, ++ .vidioc_s_output = vpif_s_output, ++ .vidioc_g_output = vpif_g_output, ++ .vidioc_cropcap = vpif_cropcap, ++ .vidioc_enum_dv_timings = vpif_enum_dv_timings, ++ .vidioc_s_dv_timings = vpif_s_dv_timings, ++ .vidioc_g_dv_timings = vpif_g_dv_timings, ++ .vidioc_g_chip_ident = vpif_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = vpif_dbg_g_register, ++ .vidioc_s_register = vpif_dbg_s_register, ++#endif ++ .vidioc_log_status = vpif_log_status, ++}; ++ ++static const struct v4l2_file_operations vpif_fops = { ++ .owner = THIS_MODULE, ++ .open = vpif_open, ++ .release = vpif_release, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = vpif_mmap, ++ .poll = vpif_poll ++}; ++ ++static struct video_device vpif_video_template = { ++ .name = "vpif", ++ .fops = &vpif_fops, ++ .ioctl_ops = &vpif_ioctl_ops, ++}; ++ ++/*Configure the channels, buffer sizei, request irq */ ++static int initialize_vpif(void) ++{ ++ int free_channel_objects_index; ++ int free_buffer_channel_index; ++ int free_buffer_index; ++ int err = 0, i, j; ++ ++ /* Default number of buffers should be 3 */ ++ if ((ch2_numbuffers > 0) && ++ (ch2_numbuffers < config_params.min_numbuffers)) ++ ch2_numbuffers = config_params.min_numbuffers; ++ if ((ch3_numbuffers > 0) && ++ (ch3_numbuffers < config_params.min_numbuffers)) ++ ch3_numbuffers = config_params.min_numbuffers; ++ ++ /* Set buffer size to min buffers size if invalid buffer size is ++ * given */ ++ if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) ++ ch2_bufsize = ++ config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; ++ if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) ++ ch3_bufsize = ++ config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; ++ ++ config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; ++ ++ if (ch2_numbuffers) { ++ config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = ++ ch2_bufsize; ++ } ++ config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; ++ ++ if (ch3_numbuffers) { ++ config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = ++ ch3_bufsize; ++ } ++ ++ /* Allocate memory for six channel objects */ ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ vpif_obj.dev[i] = ++ kzalloc(sizeof(struct channel_obj), GFP_KERNEL); ++ /* If memory allocation fails, return error */ ++ if (!vpif_obj.dev[i]) { ++ free_channel_objects_index = i; ++ err = -ENOMEM; ++ goto vpif_init_free_channel_objects; ++ } ++ } ++ ++ free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; ++ free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; ++ free_buffer_index = config_params.numbuffers[i - 1]; ++ ++ return 0; ++ ++vpif_init_free_channel_objects: ++ for (j = 0; j < free_channel_objects_index; j++) ++ kfree(vpif_obj.dev[j]); ++ return err; ++} ++ ++/* ++ * vpif_probe: This function creates device entries by register itself to the ++ * V4L2 driver and initializes fields of each channel objects ++ */ ++static __init int vpif_probe(struct platform_device *pdev) ++{ ++ struct vpif_subdev_info *subdevdata; ++ struct vpif_display_config *config; ++ int i, j = 0, k, err = 0; ++ int res_idx = 0; ++ struct i2c_adapter *i2c_adap; ++ struct common_obj *common; ++ struct channel_obj *ch; ++ struct video_device *vfd; ++ struct resource *res; ++ int subdev_count; ++ size_t size; ++ ++ vpif_dev = &pdev->dev; ++ err = initialize_vpif(); ++ ++ if (err) { ++ v4l2_err(vpif_dev->driver, "Error initializing vpif\n"); ++ return err; ++ } ++ ++ err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); ++ if (err) { ++ v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); ++ return err; ++ } ++ ++ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { ++ for (i = res->start; i <= res->end; i++) { ++ if (request_irq(i, vpif_channel_isr, IRQF_SHARED, ++ "VPIF_Display", (void *) ++ (&vpif_obj.dev[res_idx]->channel_id))) { ++ err = -EBUSY; ++ for (j = 0; j < i; j++) ++ free_irq(j, (void *) ++ (&vpif_obj.dev[res_idx]->channel_id)); ++ goto vpif_int_err; ++ } ++ } ++ res_idx++; ++ } ++ ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ ++ /* Allocate memory for video device */ ++ vfd = video_device_alloc(); ++ if (vfd == NULL) { ++ for (j = 0; j < i; j++) { ++ ch = vpif_obj.dev[j]; ++ video_device_release(ch->video_dev); ++ } ++ err = -ENOMEM; ++ goto vpif_int_err; ++ } ++ ++ /* Initialize field of video device */ ++ *vfd = vpif_video_template; ++ vfd->v4l2_dev = &vpif_obj.v4l2_dev; ++ vfd->release = video_device_release; ++ vfd->vfl_dir = VFL_DIR_TX; ++ snprintf(vfd->name, sizeof(vfd->name), ++ "VPIF_Display_DRIVER_V%s", ++ VPIF_DISPLAY_VERSION); ++ ++ /* Set video_dev to the video device */ ++ ch->video_dev = vfd; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res) { ++ size = resource_size(res); ++ /* The resources are divided into two equal memory and when ++ * we have HD output we can add them together ++ */ ++ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { ++ ch = vpif_obj.dev[j]; ++ ch->channel_id = j; ++ ++ /* only enabled if second resource exists */ ++ config_params.video_limit[ch->channel_id] = 0; ++ if (size) ++ config_params.video_limit[ch->channel_id] = ++ size/2; ++ } ++ } ++ ++ i2c_adap = i2c_get_adapter(1); ++ config = pdev->dev.platform_data; ++ subdev_count = config->subdev_count; ++ subdevdata = config->subdevinfo; ++ vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count, ++ GFP_KERNEL); ++ if (vpif_obj.sd == NULL) { ++ vpif_err("unable to allocate memory for subdevice pointers\n"); ++ err = -ENOMEM; ++ goto vpif_sd_error; ++ } ++ ++ for (i = 0; i < subdev_count; i++) { ++ vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev, ++ i2c_adap, ++ &subdevdata[i].board_info, ++ NULL); ++ if (!vpif_obj.sd[i]) { ++ vpif_err("Error registering v4l2 subdevice\n"); ++ goto probe_subdev_out; ++ } ++ ++ if (vpif_obj.sd[i]) ++ vpif_obj.sd[i]->grp_id = 1 << i; ++ } ++ ++ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { ++ ch = vpif_obj.dev[j]; ++ /* Initialize field of the channel objects */ ++ atomic_set(&ch->usrs, 0); ++ for (k = 0; k < VPIF_NUMOBJECTS; k++) { ++ ch->common[k].numbuffers = 0; ++ common = &ch->common[k]; ++ common->io_usrs = 0; ++ common->started = 0; ++ spin_lock_init(&common->irqlock); ++ mutex_init(&common->lock); ++ common->numbuffers = 0; ++ common->set_addr = NULL; ++ common->ytop_off = common->ybtm_off = 0; ++ common->ctop_off = common->cbtm_off = 0; ++ common->cur_frm = common->next_frm = NULL; ++ memset(&common->fmt, 0, sizeof(common->fmt)); ++ common->numbuffers = config_params.numbuffers[k]; ++ ++ } ++ ch->initialized = 0; ++ if (subdev_count) ++ ch->sd = vpif_obj.sd[0]; ++ ch->channel_id = j; ++ if (j < 2) ++ ch->common[VPIF_VIDEO_INDEX].numbuffers = ++ config_params.numbuffers[ch->channel_id]; ++ else ++ ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; ++ ++ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); ++ ++ /* Initialize prio member of channel object */ ++ v4l2_prio_init(&ch->prio); ++ ch->common[VPIF_VIDEO_INDEX].fmt.type = ++ V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ ch->video_dev->lock = &common->lock; ++ video_set_drvdata(ch->video_dev, ch); ++ ++ /* select output 0 */ ++ err = vpif_set_output(config, ch, 0); ++ if (err) ++ goto probe_out; ++ ++ /* register video device */ ++ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", ++ (int)ch, (int)&ch->video_dev); ++ ++ err = video_register_device(ch->video_dev, ++ VFL_TYPE_GRABBER, (j ? 3 : 2)); ++ if (err < 0) ++ goto probe_out; ++ } ++ ++ v4l2_info(&vpif_obj.v4l2_dev, ++ " VPIF display driver initialized\n"); ++ return 0; ++ ++probe_out: ++ for (k = 0; k < j; k++) { ++ ch = vpif_obj.dev[k]; ++ video_unregister_device(ch->video_dev); ++ video_device_release(ch->video_dev); ++ ch->video_dev = NULL; ++ } ++probe_subdev_out: ++ kfree(vpif_obj.sd); ++vpif_sd_error: ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ ch = vpif_obj.dev[i]; ++ /* Note: does nothing if ch->video_dev == NULL */ ++ video_device_release(ch->video_dev); ++ } ++vpif_int_err: ++ v4l2_device_unregister(&vpif_obj.v4l2_dev); ++ vpif_err("VPIF IRQ request failed\n"); ++ for (i = 0; i < res_idx; i++) { ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, i); ++ for (j = res->start; j <= res->end; j++) ++ free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id)); ++ } ++ ++ return err; ++} ++ ++/* ++ * vpif_remove: It un-register channels from V4L2 driver ++ */ ++static int vpif_remove(struct platform_device *device) ++{ ++ struct channel_obj *ch; ++ int i; ++ ++ v4l2_device_unregister(&vpif_obj.v4l2_dev); ++ ++ /* un-register device */ ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ /* Unregister video device */ ++ video_unregister_device(ch->video_dev); ++ ++ ch->video_dev = NULL; ++ } ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int vpif_suspend(struct device *dev) ++{ ++ struct common_obj *common; ++ struct channel_obj *ch; ++ int i; ++ ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++ if (atomic_read(&ch->usrs) && common->io_usrs) { ++ /* Disable channel */ ++ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { ++ enable_channel2(0); ++ channel2_intr_enable(0); ++ } ++ if (ch->channel_id == VPIF_CHANNEL3_VIDEO || ++ common->started == 2) { ++ enable_channel3(0); ++ channel3_intr_enable(0); ++ } ++ } ++ mutex_unlock(&common->lock); ++ } ++ ++ return 0; ++} ++ ++static int vpif_resume(struct device *dev) ++{ ++ ++ struct common_obj *common; ++ struct channel_obj *ch; ++ int i; ++ ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { ++ /* Get the pointer to the channel object */ ++ ch = vpif_obj.dev[i]; ++ common = &ch->common[VPIF_VIDEO_INDEX]; ++ mutex_lock(&common->lock); ++ if (atomic_read(&ch->usrs) && common->io_usrs) { ++ /* Enable channel */ ++ if (ch->channel_id == VPIF_CHANNEL2_VIDEO) { ++ enable_channel2(1); ++ channel2_intr_enable(1); ++ } ++ if (ch->channel_id == VPIF_CHANNEL3_VIDEO || ++ common->started == 2) { ++ enable_channel3(1); ++ channel3_intr_enable(1); ++ } ++ } ++ mutex_unlock(&common->lock); ++ } ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops vpif_pm = { ++ .suspend = vpif_suspend, ++ .resume = vpif_resume, ++}; ++ ++#define vpif_pm_ops (&vpif_pm) ++#else ++#define vpif_pm_ops NULL ++#endif ++ ++static __refdata struct platform_driver vpif_driver = { ++ .driver = { ++ .name = "vpif_display", ++ .owner = THIS_MODULE, ++ .pm = vpif_pm_ops, ++ }, ++ .probe = vpif_probe, ++ .remove = vpif_remove, ++}; ++ ++static __init int vpif_init(void) ++{ ++ return platform_driver_register(&vpif_driver); ++} ++ ++/* ++ * vpif_cleanup: This function un-registers device and driver to the kernel, ++ * frees requested irq handler and de-allocates memory allocated for channel ++ * objects. ++ */ ++static void vpif_cleanup(void) ++{ ++ struct platform_device *pdev; ++ struct resource *res; ++ int irq_num; ++ int i = 0; ++ ++ pdev = container_of(vpif_dev, struct platform_device, dev); ++ ++ while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { ++ for (irq_num = res->start; irq_num <= res->end; irq_num++) ++ free_irq(irq_num, ++ (void *)(&vpif_obj.dev[i]->channel_id)); ++ i++; ++ } ++ ++ platform_driver_unregister(&vpif_driver); ++ kfree(vpif_obj.sd); ++ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) ++ kfree(vpif_obj.dev[i]); ++} ++ ++module_init(vpif_init); ++module_exit(vpif_cleanup); +diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h +new file mode 100644 +index 0000000..a5a18f7 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpif_display.h +@@ -0,0 +1,165 @@ ++/* ++ * VPIF display header file ++ * ++ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. ++ * ++ * This program is distributed .as is. WITHOUT ANY WARRANTY of any ++ * kind, whether express or implied; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef DAVINCIHD_DISPLAY_H ++#define DAVINCIHD_DISPLAY_H ++ ++/* Header files */ ++#include ++#include ++#include ++#include ++#include ++ ++#include "vpif.h" ++ ++/* Macros */ ++#define VPIF_DISPLAY_VERSION "0.0.2" ++ ++#define VPIF_VALID_FIELD(field) \ ++ (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \ ++ (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \ ++ (V4L2_FIELD_SEQ_BT == field))) ++ ++#define VPIF_DISPLAY_MAX_DEVICES (2) ++#define VPIF_SLICED_BUF_SIZE (256) ++#define VPIF_SLICED_MAX_SERVICES (3) ++#define VPIF_VIDEO_INDEX (0) ++#define VPIF_VBI_INDEX (1) ++#define VPIF_HBI_INDEX (2) ++ ++/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/ ++#define VPIF_NUMOBJECTS (1) ++ ++/* Macros */ ++#define ISALIGNED(a) (0 == ((a) & 7)) ++ ++/* enumerated data types */ ++/* Enumerated data type to give id to each device per channel */ ++enum vpif_channel_id { ++ VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */ ++ VPIF_CHANNEL3_VIDEO, /* Channel3 Video */ ++}; ++ ++/* structures */ ++ ++struct video_obj { ++ enum v4l2_field buf_field; ++ u32 latest_only; /* indicate whether to return ++ * most recent displayed frame only */ ++ v4l2_std_id stdid; /* Currently selected or default ++ * standard */ ++ struct v4l2_dv_timings dv_timings; ++}; ++ ++struct vpif_disp_buffer { ++ struct vb2_buffer vb; ++ struct list_head list; ++}; ++ ++struct common_obj { ++ /* Buffer specific parameters */ ++ u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for ++ * storing frames */ ++ u32 numbuffers; /* number of buffers */ ++ struct vpif_disp_buffer *cur_frm; /* Pointer pointing to current ++ * vb2_buffer */ ++ struct vpif_disp_buffer *next_frm; /* Pointer pointing to next ++ * vb2_buffer */ ++ enum v4l2_memory memory; /* This field keeps track of ++ * type of buffer exchange ++ * method user has selected */ ++ struct v4l2_format fmt; /* Used to store the format */ ++ struct vb2_queue buffer_queue; /* Buffer queue used in ++ * video-buf */ ++ /* allocator-specific contexts for each plane */ ++ struct vb2_alloc_ctx *alloc_ctx; ++ ++ struct list_head dma_queue; /* Queue of filled frames */ ++ spinlock_t irqlock; /* Used in video-buf */ ++ ++ /* channel specific parameters */ ++ struct mutex lock; /* lock used to access this ++ * structure */ ++ u32 io_usrs; /* number of users performing ++ * IO */ ++ u8 started; /* Indicates whether streaming ++ * started */ ++ u32 ytop_off; /* offset of Y top from the ++ * starting of the buffer */ ++ u32 ybtm_off; /* offset of Y bottom from the ++ * starting of the buffer */ ++ u32 ctop_off; /* offset of C top from the ++ * starting of the buffer */ ++ u32 cbtm_off; /* offset of C bottom from the ++ * starting of the buffer */ ++ /* Function pointer to set the addresses */ ++ void (*set_addr) (unsigned long, unsigned long, ++ unsigned long, unsigned long); ++ u32 height; ++ u32 width; ++}; ++ ++struct channel_obj { ++ /* V4l2 specific parameters */ ++ struct video_device *video_dev; /* Identifies video device for ++ * this channel */ ++ struct v4l2_prio_state prio; /* Used to keep track of state of ++ * the priority */ ++ atomic_t usrs; /* number of open instances of ++ * the channel */ ++ u32 field_id; /* Indicates id of the field ++ * which is being displayed */ ++ u8 initialized; /* flag to indicate whether ++ * encoder is initialized */ ++ u32 output_idx; /* Current output index */ ++ struct v4l2_subdev *sd; /* Current output subdev(may be NULL) */ ++ ++ enum vpif_channel_id channel_id;/* Identifies channel */ ++ struct vpif_params vpifparams; ++ struct common_obj common[VPIF_NUMOBJECTS]; ++ struct video_obj video; ++}; ++ ++/* File handle structure */ ++struct vpif_fh { ++ struct channel_obj *channel; /* pointer to channel object for ++ * opened device */ ++ u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle ++ * is doing IO */ ++ enum v4l2_priority prio; /* Used to keep track priority of ++ * this instance */ ++ u8 initialized; /* Used to keep track of whether this ++ * file handle has initialized ++ * channel or not */ ++}; ++ ++/* vpif device structure */ ++struct vpif_device { ++ struct v4l2_device v4l2_dev; ++ struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; ++ struct v4l2_subdev **sd; ++ ++}; ++ ++struct vpif_config_params { ++ u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; ++ u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; ++ u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; ++ u32 video_limit[VPIF_DISPLAY_NUM_CHANNELS]; ++ u8 min_numbuffers; ++}; ++ ++#endif /* DAVINCIHD_DISPLAY_H */ +diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c +new file mode 100644 +index 0000000..d945f94 +--- /dev/null ++++ b/drivers/media/platform/davinci/vpss.c +@@ -0,0 +1,550 @@ ++/* ++ * Copyright (C) 2009 Texas Instruments. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * common vpss system module platform driver for all video drivers. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("VPSS Driver"); ++MODULE_AUTHOR("Texas Instruments"); ++ ++/* DM644x defines */ ++#define DM644X_SBL_PCR_VPSS (4) ++ ++#define DM355_VPSSBL_INTSEL 0x10 ++#define DM355_VPSSBL_EVTSEL 0x14 ++/* vpss BL register offsets */ ++#define DM355_VPSSBL_CCDCMUX 0x1c ++/* vpss CLK register offsets */ ++#define DM355_VPSSCLK_CLKCTRL 0x04 ++/* masks and shifts */ ++#define VPSS_HSSISEL_SHIFT 4 ++/* ++ * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4, ++ * IPIPE_INT1_SDR - vpss_int5 ++ */ ++#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10 ++/* VENCINT - vpss_int8 */ ++#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 ++ ++#define DM365_ISP5_PCCR 0x04 ++#define DM365_ISP5_PCCR_BL_CLK_ENABLE BIT(0) ++#define DM365_ISP5_PCCR_ISIF_CLK_ENABLE BIT(1) ++#define DM365_ISP5_PCCR_H3A_CLK_ENABLE BIT(2) ++#define DM365_ISP5_PCCR_RSZ_CLK_ENABLE BIT(3) ++#define DM365_ISP5_PCCR_IPIPE_CLK_ENABLE BIT(4) ++#define DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE BIT(5) ++#define DM365_ISP5_PCCR_RSV BIT(6) ++ ++#define DM365_ISP5_BCR 0x08 ++#define DM365_ISP5_BCR_ISIF_OUT_ENABLE BIT(1) ++ ++#define DM365_ISP5_INTSEL1 0x10 ++#define DM365_ISP5_INTSEL2 0x14 ++#define DM365_ISP5_INTSEL3 0x18 ++#define DM365_ISP5_CCDCMUX 0x20 ++#define DM365_ISP5_PG_FRAME_SIZE 0x28 ++#define DM365_VPBE_CLK_CTRL 0x00 ++ ++#define VPSS_CLK_CTRL 0x01c40044 ++#define VPSS_CLK_CTRL_VENCCLKEN BIT(3) ++#define VPSS_CLK_CTRL_DACCLKEN BIT(4) ++ ++/* ++ * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, ++ * AF - vpss_int3 ++ */ ++#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100 ++/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */ ++#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f ++/* VENC - vpss_int8 */ ++#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015 ++ ++/* masks and shifts for DM365*/ ++#define DM365_CCDC_PG_VD_POL_SHIFT 0 ++#define DM365_CCDC_PG_HD_POL_SHIFT 1 ++ ++#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4)) ++#define CCD_SRC_SEL_SHIFT 4 ++ ++/* Different SoC platforms supported by this driver */ ++enum vpss_platform_type { ++ DM644X, ++ DM355, ++ DM365, ++}; ++ ++/* ++ * vpss operations. Depends on platform. Not all functions are available ++ * on all platforms. The api, first check if a functio is available before ++ * invoking it. In the probe, the function ptrs are initialized based on ++ * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. ++ */ ++struct vpss_hw_ops { ++ /* enable clock */ ++ int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); ++ /* select input to ccdc */ ++ void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); ++ /* clear wbl overflow bit */ ++ int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); ++ /* set sync polarity */ ++ void (*set_sync_pol)(struct vpss_sync_pol); ++ /* set the PG_FRAME_SIZE register*/ ++ void (*set_pg_frame_size)(struct vpss_pg_frame_size); ++ /* check and clear interrupt if occured */ ++ int (*dma_complete_interrupt)(void); ++}; ++ ++/* vpss configuration */ ++struct vpss_oper_config { ++ __iomem void *vpss_regs_base0; ++ __iomem void *vpss_regs_base1; ++ resource_size_t *vpss_regs_base2; ++ enum vpss_platform_type platform; ++ spinlock_t vpss_lock; ++ struct vpss_hw_ops hw_ops; ++}; ++ ++static struct vpss_oper_config oper_cfg; ++ ++/* register access routines */ ++static inline u32 bl_regr(u32 offset) ++{ ++ return __raw_readl(oper_cfg.vpss_regs_base0 + offset); ++} ++ ++static inline void bl_regw(u32 val, u32 offset) ++{ ++ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); ++} ++ ++static inline u32 vpss_regr(u32 offset) ++{ ++ return __raw_readl(oper_cfg.vpss_regs_base1 + offset); ++} ++ ++static inline void vpss_regw(u32 val, u32 offset) ++{ ++ __raw_writel(val, oper_cfg.vpss_regs_base1 + offset); ++} ++ ++/* For DM365 only */ ++static inline u32 isp5_read(u32 offset) ++{ ++ return __raw_readl(oper_cfg.vpss_regs_base0 + offset); ++} ++ ++/* For DM365 only */ ++static inline void isp5_write(u32 val, u32 offset) ++{ ++ __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); ++} ++ ++static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) ++{ ++ u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK; ++ ++ /* if we are using pattern generator, enable it */ ++ if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG) ++ temp |= 0x08; ++ ++ temp |= (src_sel << CCD_SRC_SEL_SHIFT); ++ isp5_write(temp, DM365_ISP5_CCDCMUX); ++} ++ ++static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) ++{ ++ bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); ++} ++ ++int vpss_dma_complete_interrupt(void) ++{ ++ if (!oper_cfg.hw_ops.dma_complete_interrupt) ++ return 2; ++ return oper_cfg.hw_ops.dma_complete_interrupt(); ++} ++EXPORT_SYMBOL(vpss_dma_complete_interrupt); ++ ++int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) ++{ ++ if (!oper_cfg.hw_ops.select_ccdc_source) ++ return -EINVAL; ++ ++ oper_cfg.hw_ops.select_ccdc_source(src_sel); ++ return 0; ++} ++EXPORT_SYMBOL(vpss_select_ccdc_source); ++ ++static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) ++{ ++ u32 mask = 1, val; ++ ++ if (wbl_sel < VPSS_PCR_AEW_WBL_0 || ++ wbl_sel > VPSS_PCR_CCDC_WBL_O) ++ return -EINVAL; ++ ++ /* writing a 0 clear the overflow */ ++ mask = ~(mask << wbl_sel); ++ val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; ++ bl_regw(val, DM644X_SBL_PCR_VPSS); ++ return 0; ++} ++ ++void vpss_set_sync_pol(struct vpss_sync_pol sync) ++{ ++ if (!oper_cfg.hw_ops.set_sync_pol) ++ return; ++ ++ oper_cfg.hw_ops.set_sync_pol(sync); ++} ++EXPORT_SYMBOL(vpss_set_sync_pol); ++ ++int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) ++{ ++ if (!oper_cfg.hw_ops.clear_wbl_overflow) ++ return -EINVAL; ++ ++ return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); ++} ++EXPORT_SYMBOL(vpss_clear_wbl_overflow); ++ ++/* ++ * dm355_enable_clock - Enable VPSS Clock ++ * @clock_sel: CLock to be enabled/disabled ++ * @en: enable/disable flag ++ * ++ * This is called to enable or disable a vpss clock ++ */ ++static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) ++{ ++ unsigned long flags; ++ u32 utemp, mask = 0x1, shift = 0; ++ ++ switch (clock_sel) { ++ case VPSS_VPBE_CLOCK: ++ /* nothing since lsb */ ++ break; ++ case VPSS_VENC_CLOCK_SEL: ++ shift = 2; ++ break; ++ case VPSS_CFALD_CLOCK: ++ shift = 3; ++ break; ++ case VPSS_H3A_CLOCK: ++ shift = 4; ++ break; ++ case VPSS_IPIPE_CLOCK: ++ shift = 5; ++ break; ++ case VPSS_CCDC_CLOCK: ++ shift = 6; ++ break; ++ default: ++ printk(KERN_ERR "dm355_enable_clock:" ++ " Invalid selector: %d\n", clock_sel); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&oper_cfg.vpss_lock, flags); ++ utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); ++ if (!en) ++ utemp &= ~(mask << shift); ++ else ++ utemp |= (mask << shift); ++ ++ vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); ++ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); ++ return 0; ++} ++ ++static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en) ++{ ++ unsigned long flags; ++ u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR; ++ u32 (*read)(u32 offset) = isp5_read; ++ void(*write)(u32 val, u32 offset) = isp5_write; ++ ++ switch (clock_sel) { ++ case VPSS_BL_CLOCK: ++ break; ++ case VPSS_CCDC_CLOCK: ++ shift = 1; ++ break; ++ case VPSS_H3A_CLOCK: ++ shift = 2; ++ break; ++ case VPSS_RSZ_CLOCK: ++ shift = 3; ++ break; ++ case VPSS_IPIPE_CLOCK: ++ shift = 4; ++ break; ++ case VPSS_IPIPEIF_CLOCK: ++ shift = 5; ++ break; ++ case VPSS_PCLK_INTERNAL: ++ shift = 6; ++ break; ++ case VPSS_PSYNC_CLOCK_SEL: ++ shift = 7; ++ break; ++ case VPSS_VPBE_CLOCK: ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ case VPSS_VENC_CLOCK_SEL: ++ shift = 2; ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ case VPSS_LDC_CLOCK: ++ shift = 3; ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ case VPSS_FDIF_CLOCK: ++ shift = 4; ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ case VPSS_OSD_CLOCK_SEL: ++ shift = 6; ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ case VPSS_LDC_CLOCK_SEL: ++ shift = 7; ++ read = vpss_regr; ++ write = vpss_regw; ++ offset = DM365_VPBE_CLK_CTRL; ++ break; ++ default: ++ printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n", ++ clock_sel); ++ return -1; ++ } ++ ++ spin_lock_irqsave(&oper_cfg.vpss_lock, flags); ++ utemp = read(offset); ++ if (!en) { ++ mask = ~mask; ++ utemp &= (mask << shift); ++ } else ++ utemp |= (mask << shift); ++ ++ write(utemp, offset); ++ spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); ++ ++ return 0; ++} ++ ++int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) ++{ ++ if (!oper_cfg.hw_ops.enable_clock) ++ return -EINVAL; ++ ++ return oper_cfg.hw_ops.enable_clock(clock_sel, en); ++} ++EXPORT_SYMBOL(vpss_enable_clock); ++ ++void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) ++{ ++ int val = 0; ++ val = isp5_read(DM365_ISP5_CCDCMUX); ++ ++ val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT); ++ val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT); ++ ++ isp5_write(val, DM365_ISP5_CCDCMUX); ++} ++EXPORT_SYMBOL(dm365_vpss_set_sync_pol); ++ ++void vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) ++{ ++ if (!oper_cfg.hw_ops.set_pg_frame_size) ++ return; ++ ++ oper_cfg.hw_ops.set_pg_frame_size(frame_size); ++} ++EXPORT_SYMBOL(vpss_set_pg_frame_size); ++ ++void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) ++{ ++ int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; ++ ++ current_reg |= (frame_size.pplen - 1); ++ isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE); ++} ++EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); ++ ++static int __devinit vpss_probe(struct platform_device *pdev) ++{ ++ struct resource *r1, *r2; ++ char *platform_name; ++ int status; ++ ++ if (!pdev->dev.platform_data) { ++ dev_err(&pdev->dev, "no platform data\n"); ++ return -ENOENT; ++ } ++ ++ platform_name = pdev->dev.platform_data; ++ if (!strcmp(platform_name, "dm355_vpss")) ++ oper_cfg.platform = DM355; ++ else if (!strcmp(platform_name, "dm365_vpss")) ++ oper_cfg.platform = DM365; ++ else if (!strcmp(platform_name, "dm644x_vpss")) ++ oper_cfg.platform = DM644X; ++ else { ++ dev_err(&pdev->dev, "vpss driver not supported on" ++ " this platform\n"); ++ return -ENODEV; ++ } ++ ++ dev_info(&pdev->dev, "%s vpss probed\n", platform_name); ++ r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r1) ++ return -ENOENT; ++ ++ r1 = request_mem_region(r1->start, resource_size(r1), r1->name); ++ if (!r1) ++ return -EBUSY; ++ ++ oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1)); ++ if (!oper_cfg.vpss_regs_base0) { ++ status = -EBUSY; ++ goto fail1; ++ } ++ ++ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { ++ r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!r2) { ++ status = -ENOENT; ++ goto fail2; ++ } ++ r2 = request_mem_region(r2->start, resource_size(r2), r2->name); ++ if (!r2) { ++ status = -EBUSY; ++ goto fail2; ++ } ++ ++ oper_cfg.vpss_regs_base1 = ioremap(r2->start, ++ resource_size(r2)); ++ if (!oper_cfg.vpss_regs_base1) { ++ status = -EBUSY; ++ goto fail3; ++ } ++ } ++ ++ if (oper_cfg.platform == DM355) { ++ oper_cfg.hw_ops.enable_clock = dm355_enable_clock; ++ oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; ++ /* Setup vpss interrupts */ ++ bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL); ++ bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL); ++ } else if (oper_cfg.platform == DM365) { ++ oper_cfg.hw_ops.enable_clock = dm365_enable_clock; ++ oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; ++ /* Setup vpss interrupts */ ++ isp5_write((isp5_read(DM365_ISP5_PCCR) | ++ DM365_ISP5_PCCR_BL_CLK_ENABLE | ++ DM365_ISP5_PCCR_ISIF_CLK_ENABLE | ++ DM365_ISP5_PCCR_H3A_CLK_ENABLE | ++ DM365_ISP5_PCCR_RSZ_CLK_ENABLE | ++ DM365_ISP5_PCCR_IPIPE_CLK_ENABLE | ++ DM365_ISP5_PCCR_IPIPEIF_CLK_ENABLE | ++ DM365_ISP5_PCCR_RSV), DM365_ISP5_PCCR); ++ isp5_write((isp5_read(DM365_ISP5_BCR) | ++ DM365_ISP5_BCR_ISIF_OUT_ENABLE), DM365_ISP5_BCR); ++ isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); ++ isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); ++ isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); ++ } else ++ oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; ++ ++ spin_lock_init(&oper_cfg.vpss_lock); ++ dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); ++ return 0; ++ ++fail3: ++ release_mem_region(r2->start, resource_size(r2)); ++fail2: ++ iounmap(oper_cfg.vpss_regs_base0); ++fail1: ++ release_mem_region(r1->start, resource_size(r1)); ++ return status; ++} ++ ++static int __devexit vpss_remove(struct platform_device *pdev) ++{ ++ struct resource *res; ++ ++ iounmap(oper_cfg.vpss_regs_base0); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++ if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { ++ iounmap(oper_cfg.vpss_regs_base1); ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ release_mem_region(res->start, resource_size(res)); ++ } ++ return 0; ++} ++ ++static struct platform_driver vpss_driver = { ++ .driver = { ++ .name = "vpss", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __devexit_p(vpss_remove), ++ .probe = vpss_probe, ++}; ++ ++static void vpss_exit(void) ++{ ++ iounmap(oper_cfg.vpss_regs_base2); ++ release_mem_region(VPSS_CLK_CTRL, 4); ++ platform_driver_unregister(&vpss_driver); ++} ++ ++static int __init vpss_init(void) ++{ ++ if (!request_mem_region(VPSS_CLK_CTRL, 4, "vpss_clock_control")) ++ return -EBUSY; ++ ++ oper_cfg.vpss_regs_base2 = ioremap(VPSS_CLK_CTRL, 4); ++ writel(VPSS_CLK_CTRL_VENCCLKEN | ++ VPSS_CLK_CTRL_DACCLKEN, oper_cfg.vpss_regs_base2); ++ ++ return platform_driver_register(&vpss_driver); ++} ++subsys_initcall(vpss_init); ++module_exit(vpss_exit); +diff --git a/drivers/media/platform/exynos-gsc/Makefile b/drivers/media/platform/exynos-gsc/Makefile +new file mode 100644 +index 0000000..6d1411c +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/Makefile +@@ -0,0 +1,3 @@ ++exynos-gsc-objs := gsc-core.o gsc-m2m.o gsc-regs.o ++ ++obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc.o +diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c +new file mode 100644 +index 0000000..ae885c7 +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/gsc-core.c +@@ -0,0 +1,1256 @@ ++/* ++ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * Samsung EXYNOS5 SoC series G-Scaler driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation, either version 2 of the License, ++ * or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "gsc-core.h" ++ ++#define GSC_CLOCK_GATE_NAME "gscl" ++ ++static const struct gsc_fmt gsc_formats[] = { ++ { ++ .name = "RGB565", ++ .pixelformat = V4L2_PIX_FMT_RGB565X, ++ .depth = { 16 }, ++ .color = GSC_RGB, ++ .num_planes = 1, ++ .num_comp = 1, ++ }, { ++ .name = "XRGB-8-8-8-8, 32 bpp", ++ .pixelformat = V4L2_PIX_FMT_RGB32, ++ .depth = { 32 }, ++ .color = GSC_RGB, ++ .num_planes = 1, ++ .num_comp = 1, ++ }, { ++ .name = "YUV 4:2:2 packed, YCbYCr", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 1, ++ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, ++ }, { ++ .name = "YUV 4:2:2 packed, CbYCrY", ++ .pixelformat = V4L2_PIX_FMT_UYVY, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_C, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 1, ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, ++ }, { ++ .name = "YUV 4:2:2 packed, CrYCbY", ++ .pixelformat = V4L2_PIX_FMT_VYUY, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_C, ++ .corder = GSC_CRCB, ++ .num_planes = 1, ++ .num_comp = 1, ++ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, ++ }, { ++ .name = "YUV 4:2:2 packed, YCrYCb", ++ .pixelformat = V4L2_PIX_FMT_YVYU, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CRCB, ++ .num_planes = 1, ++ .num_comp = 1, ++ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, ++ }, { ++ .name = "YUV 4:4:4 planar, YCbYCr", ++ .pixelformat = V4L2_PIX_FMT_YUV32, ++ .depth = { 32 }, ++ .color = GSC_YUV444, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 1, ++ }, { ++ .name = "YUV 4:2:2 planar, Y/Cb/Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV422P, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 3, ++ }, { ++ .name = "YUV 4:2:2 planar, Y/CbCr", ++ .pixelformat = V4L2_PIX_FMT_NV16, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 2, ++ }, { ++ .name = "YUV 4:2:2 planar, Y/CrCb", ++ .pixelformat = V4L2_PIX_FMT_NV61, ++ .depth = { 16 }, ++ .color = GSC_YUV422, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CRCB, ++ .num_planes = 1, ++ .num_comp = 2, ++ }, { ++ .name = "YUV 4:2:0 planar, YCbCr", ++ .pixelformat = V4L2_PIX_FMT_YUV420, ++ .depth = { 12 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 3, ++ }, { ++ .name = "YUV 4:2:0 planar, YCrCb", ++ .pixelformat = V4L2_PIX_FMT_YVU420, ++ .depth = { 12 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CRCB, ++ .num_planes = 1, ++ .num_comp = 3, ++ ++ }, { ++ .name = "YUV 4:2:0 planar, Y/CbCr", ++ .pixelformat = V4L2_PIX_FMT_NV12, ++ .depth = { 12 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 1, ++ .num_comp = 2, ++ }, { ++ .name = "YUV 4:2:0 planar, Y/CrCb", ++ .pixelformat = V4L2_PIX_FMT_NV21, ++ .depth = { 12 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CRCB, ++ .num_planes = 1, ++ .num_comp = 2, ++ }, { ++ .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr", ++ .pixelformat = V4L2_PIX_FMT_NV12M, ++ .depth = { 8, 4 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 2, ++ .num_comp = 2, ++ }, { ++ .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr", ++ .pixelformat = V4L2_PIX_FMT_YUV420M, ++ .depth = { 8, 2, 2 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 3, ++ .num_comp = 3, ++ }, { ++ .name = "YUV 4:2:0 non-contig. 3p, Y/Cr/Cb", ++ .pixelformat = V4L2_PIX_FMT_YVU420M, ++ .depth = { 8, 2, 2 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CRCB, ++ .num_planes = 3, ++ .num_comp = 3, ++ }, { ++ .name = "YUV 4:2:0 n.c. 2p, Y/CbCr tiled", ++ .pixelformat = V4L2_PIX_FMT_NV12MT_16X16, ++ .depth = { 8, 4 }, ++ .color = GSC_YUV420, ++ .yorder = GSC_LSB_Y, ++ .corder = GSC_CBCR, ++ .num_planes = 2, ++ .num_comp = 2, ++ } ++}; ++ ++const struct gsc_fmt *get_format(int index) ++{ ++ if (index >= ARRAY_SIZE(gsc_formats)) ++ return NULL; ++ ++ return (struct gsc_fmt *)&gsc_formats[index]; ++} ++ ++const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index) ++{ ++ const struct gsc_fmt *fmt, *def_fmt = NULL; ++ unsigned int i; ++ ++ if (index >= ARRAY_SIZE(gsc_formats)) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(gsc_formats); ++i) { ++ fmt = get_format(i); ++ if (pixelformat && fmt->pixelformat == *pixelformat) ++ return fmt; ++ if (mbus_code && fmt->mbus_code == *mbus_code) ++ return fmt; ++ if (index == i) ++ def_fmt = fmt; ++ } ++ return def_fmt; ++ ++} ++ ++void gsc_set_frame_size(struct gsc_frame *frame, int width, int height) ++{ ++ frame->f_width = width; ++ frame->f_height = height; ++ frame->crop.width = width; ++ frame->crop.height = height; ++ frame->crop.left = 0; ++ frame->crop.top = 0; ++} ++ ++int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, ++ u32 *ratio) ++{ ++ if ((dst > src) || (dst >= src / var->poly_sc_down_max)) { ++ *ratio = 1; ++ return 0; ++ } ++ ++ if ((src / var->poly_sc_down_max / var->pre_sc_down_max) > dst) { ++ pr_err("Exceeded maximum downscaling ratio (1/16))"); ++ return -EINVAL; ++ } ++ ++ *ratio = (dst > (src / 8)) ? 2 : 4; ++ ++ return 0; ++} ++ ++void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh) ++{ ++ if (hratio == 4 && vratio == 4) ++ *sh = 4; ++ else if ((hratio == 4 && vratio == 2) || ++ (hratio == 2 && vratio == 4)) ++ *sh = 3; ++ else if ((hratio == 4 && vratio == 1) || ++ (hratio == 1 && vratio == 4) || ++ (hratio == 2 && vratio == 2)) ++ *sh = 2; ++ else if (hratio == 1 && vratio == 1) ++ *sh = 0; ++ else ++ *sh = 1; ++} ++ ++void gsc_check_src_scale_info(struct gsc_variant *var, ++ struct gsc_frame *s_frame, u32 *wratio, ++ u32 tx, u32 ty, u32 *hratio) ++{ ++ int remainder = 0, walign, halign; ++ ++ if (is_yuv420(s_frame->fmt->color)) { ++ walign = GSC_SC_ALIGN_4; ++ halign = GSC_SC_ALIGN_4; ++ } else if (is_yuv422(s_frame->fmt->color)) { ++ walign = GSC_SC_ALIGN_4; ++ halign = GSC_SC_ALIGN_2; ++ } else { ++ walign = GSC_SC_ALIGN_2; ++ halign = GSC_SC_ALIGN_2; ++ } ++ ++ remainder = s_frame->crop.width % (*wratio * walign); ++ if (remainder) { ++ s_frame->crop.width -= remainder; ++ gsc_cal_prescaler_ratio(var, s_frame->crop.width, tx, wratio); ++ pr_info("cropped src width size is recalculated from %d to %d", ++ s_frame->crop.width + remainder, s_frame->crop.width); ++ } ++ ++ remainder = s_frame->crop.height % (*hratio * halign); ++ if (remainder) { ++ s_frame->crop.height -= remainder; ++ gsc_cal_prescaler_ratio(var, s_frame->crop.height, ty, hratio); ++ pr_info("cropped src height size is recalculated from %d to %d", ++ s_frame->crop.height + remainder, s_frame->crop.height); ++ } ++} ++ ++int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f) ++{ ++ const struct gsc_fmt *fmt; ++ ++ fmt = find_fmt(NULL, NULL, f->index); ++ if (!fmt) ++ return -EINVAL; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->pixelformat; ++ ++ return 0; ++} ++ ++static u32 get_plane_info(struct gsc_frame *frm, u32 addr, u32 *index) ++{ ++ if (frm->addr.y == addr) { ++ *index = 0; ++ return frm->addr.y; ++ } else if (frm->addr.cb == addr) { ++ *index = 1; ++ return frm->addr.cb; ++ } else if (frm->addr.cr == addr) { ++ *index = 2; ++ return frm->addr.cr; ++ } else { ++ pr_err("Plane address is wrong"); ++ return -EINVAL; ++ } ++} ++ ++void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm) ++{ ++ u32 f_chk_addr, f_chk_len, s_chk_addr, s_chk_len; ++ f_chk_addr = f_chk_len = s_chk_addr = s_chk_len = 0; ++ ++ f_chk_addr = frm->addr.y; ++ f_chk_len = frm->payload[0]; ++ if (frm->fmt->num_planes == 2) { ++ s_chk_addr = frm->addr.cb; ++ s_chk_len = frm->payload[1]; ++ } else if (frm->fmt->num_planes == 3) { ++ u32 low_addr, low_plane, mid_addr, mid_plane; ++ u32 high_addr, high_plane; ++ u32 t_min, t_max; ++ ++ t_min = min3(frm->addr.y, frm->addr.cb, frm->addr.cr); ++ low_addr = get_plane_info(frm, t_min, &low_plane); ++ t_max = max3(frm->addr.y, frm->addr.cb, frm->addr.cr); ++ high_addr = get_plane_info(frm, t_max, &high_plane); ++ ++ mid_plane = 3 - (low_plane + high_plane); ++ if (mid_plane == 0) ++ mid_addr = frm->addr.y; ++ else if (mid_plane == 1) ++ mid_addr = frm->addr.cb; ++ else if (mid_plane == 2) ++ mid_addr = frm->addr.cr; ++ else ++ return; ++ ++ f_chk_addr = low_addr; ++ if (mid_addr + frm->payload[mid_plane] - low_addr > ++ high_addr + frm->payload[high_plane] - mid_addr) { ++ f_chk_len = frm->payload[low_plane]; ++ s_chk_addr = mid_addr; ++ s_chk_len = high_addr + ++ frm->payload[high_plane] - mid_addr; ++ } else { ++ f_chk_len = mid_addr + ++ frm->payload[mid_plane] - low_addr; ++ s_chk_addr = high_addr; ++ s_chk_len = frm->payload[high_plane]; ++ } ++ } ++ pr_debug("f_addr = 0x%08x, f_len = %d, s_addr = 0x%08x, s_len = %d\n", ++ f_chk_addr, f_chk_len, s_chk_addr, s_chk_len); ++} ++ ++int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) ++{ ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ struct gsc_variant *variant = gsc->variant; ++ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; ++ const struct gsc_fmt *fmt; ++ u32 max_w, max_h, mod_x, mod_y; ++ u32 min_w, min_h, tmp_w, tmp_h; ++ int i; ++ ++ pr_debug("user put w: %d, h: %d", pix_mp->width, pix_mp->height); ++ ++ fmt = find_fmt(&pix_mp->pixelformat, NULL, 0); ++ if (!fmt) { ++ pr_err("pixelformat format (0x%X) invalid\n", ++ pix_mp->pixelformat); ++ return -EINVAL; ++ } ++ ++ if (pix_mp->field == V4L2_FIELD_ANY) ++ pix_mp->field = V4L2_FIELD_NONE; ++ else if (pix_mp->field != V4L2_FIELD_NONE) { ++ pr_err("Not supported field order(%d)\n", pix_mp->field); ++ return -EINVAL; ++ } ++ ++ max_w = variant->pix_max->target_rot_dis_w; ++ max_h = variant->pix_max->target_rot_dis_h; ++ ++ mod_x = ffs(variant->pix_align->org_w) - 1; ++ if (is_yuv420(fmt->color)) ++ mod_y = ffs(variant->pix_align->org_h) - 1; ++ else ++ mod_y = ffs(variant->pix_align->org_h) - 2; ++ ++ if (V4L2_TYPE_IS_OUTPUT(f->type)) { ++ min_w = variant->pix_min->org_w; ++ min_h = variant->pix_min->org_h; ++ } else { ++ min_w = variant->pix_min->target_rot_dis_w; ++ min_h = variant->pix_min->target_rot_dis_h; ++ } ++ ++ pr_debug("mod_x: %d, mod_y: %d, max_w: %d, max_h = %d", ++ mod_x, mod_y, max_w, max_h); ++ ++ /* To check if image size is modified to adjust parameter against ++ hardware abilities */ ++ tmp_w = pix_mp->width; ++ tmp_h = pix_mp->height; ++ ++ v4l_bound_align_image(&pix_mp->width, min_w, max_w, mod_x, ++ &pix_mp->height, min_h, max_h, mod_y, 0); ++ if (tmp_w != pix_mp->width || tmp_h != pix_mp->height) ++ pr_info("Image size has been modified from %dx%d to %dx%d", ++ tmp_w, tmp_h, pix_mp->width, pix_mp->height); ++ ++ pix_mp->num_planes = fmt->num_planes; ++ ++ if (pix_mp->width >= 1280) /* HD */ ++ pix_mp->colorspace = V4L2_COLORSPACE_REC709; ++ else /* SD */ ++ pix_mp->colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ ++ for (i = 0; i < pix_mp->num_planes; ++i) { ++ int bpl = (pix_mp->width * fmt->depth[i]) >> 3; ++ pix_mp->plane_fmt[i].bytesperline = bpl; ++ pix_mp->plane_fmt[i].sizeimage = bpl * pix_mp->height; ++ ++ pr_debug("[%d]: bpl: %d, sizeimage: %d", ++ i, bpl, pix_mp->plane_fmt[i].sizeimage); ++ } ++ ++ return 0; ++} ++ ++int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f) ++{ ++ struct gsc_frame *frame; ++ struct v4l2_pix_format_mplane *pix_mp; ++ int i; ++ ++ frame = ctx_get_frame(ctx, f->type); ++ if (IS_ERR(frame)) ++ return PTR_ERR(frame); ++ ++ pix_mp = &f->fmt.pix_mp; ++ ++ pix_mp->width = frame->f_width; ++ pix_mp->height = frame->f_height; ++ pix_mp->field = V4L2_FIELD_NONE; ++ pix_mp->pixelformat = frame->fmt->pixelformat; ++ pix_mp->colorspace = V4L2_COLORSPACE_REC709; ++ pix_mp->num_planes = frame->fmt->num_planes; ++ ++ for (i = 0; i < pix_mp->num_planes; ++i) { ++ pix_mp->plane_fmt[i].bytesperline = (frame->f_width * ++ frame->fmt->depth[i]) / 8; ++ pix_mp->plane_fmt[i].sizeimage = ++ pix_mp->plane_fmt[i].bytesperline * frame->f_height; ++ } ++ ++ return 0; ++} ++ ++void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h) ++{ ++ if (tmp_w != *w || tmp_h != *h) { ++ pr_info("Cropped size has been modified from %dx%d to %dx%d", ++ *w, *h, tmp_w, tmp_h); ++ *w = tmp_w; ++ *h = tmp_h; ++ } ++} ++ ++int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr) ++{ ++ struct gsc_frame *frame; ++ ++ frame = ctx_get_frame(ctx, cr->type); ++ if (IS_ERR(frame)) ++ return PTR_ERR(frame); ++ ++ cr->c = frame->crop; ++ ++ return 0; ++} ++ ++int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr) ++{ ++ struct gsc_frame *f; ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ struct gsc_variant *variant = gsc->variant; ++ u32 mod_x = 0, mod_y = 0, tmp_w, tmp_h; ++ u32 min_w, min_h, max_w, max_h; ++ ++ if (cr->c.top < 0 || cr->c.left < 0) { ++ pr_err("doesn't support negative values for top & left\n"); ++ return -EINVAL; ++ } ++ pr_debug("user put w: %d, h: %d", cr->c.width, cr->c.height); ++ ++ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ++ f = &ctx->d_frame; ++ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ++ f = &ctx->s_frame; ++ else ++ return -EINVAL; ++ ++ max_w = f->f_width; ++ max_h = f->f_height; ++ tmp_w = cr->c.width; ++ tmp_h = cr->c.height; ++ ++ if (V4L2_TYPE_IS_OUTPUT(cr->type)) { ++ if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 1) || ++ is_rgb(f->fmt->color)) ++ min_w = 32; ++ else ++ min_w = 64; ++ if ((is_yuv422(f->fmt->color) && f->fmt->num_comp == 3) || ++ is_yuv420(f->fmt->color)) ++ min_h = 32; ++ else ++ min_h = 16; ++ } else { ++ if (is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) ++ mod_x = ffs(variant->pix_align->target_w) - 1; ++ if (is_yuv420(f->fmt->color)) ++ mod_y = ffs(variant->pix_align->target_h) - 1; ++ if (ctx->gsc_ctrls.rotate->val == 90 || ++ ctx->gsc_ctrls.rotate->val == 270) { ++ max_w = f->f_height; ++ max_h = f->f_width; ++ min_w = variant->pix_min->target_rot_en_w; ++ min_h = variant->pix_min->target_rot_en_h; ++ tmp_w = cr->c.height; ++ tmp_h = cr->c.width; ++ } else { ++ min_w = variant->pix_min->target_rot_dis_w; ++ min_h = variant->pix_min->target_rot_dis_h; ++ } ++ } ++ pr_debug("mod_x: %d, mod_y: %d, min_w: %d, min_h = %d", ++ mod_x, mod_y, min_w, min_h); ++ pr_debug("tmp_w : %d, tmp_h : %d", tmp_w, tmp_h); ++ ++ v4l_bound_align_image(&tmp_w, min_w, max_w, mod_x, ++ &tmp_h, min_h, max_h, mod_y, 0); ++ ++ if (!V4L2_TYPE_IS_OUTPUT(cr->type) && ++ (ctx->gsc_ctrls.rotate->val == 90 || ++ ctx->gsc_ctrls.rotate->val == 270)) ++ gsc_check_crop_change(tmp_h, tmp_w, ++ &cr->c.width, &cr->c.height); ++ else ++ gsc_check_crop_change(tmp_w, tmp_h, ++ &cr->c.width, &cr->c.height); ++ ++ ++ /* adjust left/top if cropping rectangle is out of bounds */ ++ /* Need to add code to algin left value with 2's multiple */ ++ if (cr->c.left + tmp_w > max_w) ++ cr->c.left = max_w - tmp_w; ++ if (cr->c.top + tmp_h > max_h) ++ cr->c.top = max_h - tmp_h; ++ ++ if ((is_yuv420(f->fmt->color) || is_yuv422(f->fmt->color)) && ++ cr->c.left & 1) ++ cr->c.left -= 1; ++ ++ pr_debug("Aligned l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", ++ cr->c.left, cr->c.top, cr->c.width, cr->c.height, max_w, max_h); ++ ++ return 0; ++} ++ ++int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw, ++ int dh, int rot, int out_path) ++{ ++ int tmp_w, tmp_h, sc_down_max; ++ ++ if (out_path == GSC_DMA) ++ sc_down_max = var->sc_down_max; ++ else ++ sc_down_max = var->local_sc_down; ++ ++ if (rot == 90 || rot == 270) { ++ tmp_w = dh; ++ tmp_h = dw; ++ } else { ++ tmp_w = dw; ++ tmp_h = dh; ++ } ++ ++ if ((sw / tmp_w) > sc_down_max || ++ (sh / tmp_h) > sc_down_max || ++ (tmp_w / sw) > var->sc_up_max || ++ (tmp_h / sh) > var->sc_up_max) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int gsc_set_scaler_info(struct gsc_ctx *ctx) ++{ ++ struct gsc_scaler *sc = &ctx->scaler; ++ struct gsc_frame *s_frame = &ctx->s_frame; ++ struct gsc_frame *d_frame = &ctx->d_frame; ++ struct gsc_variant *variant = ctx->gsc_dev->variant; ++ struct device *dev = &ctx->gsc_dev->pdev->dev; ++ int tx, ty; ++ int ret; ++ ++ ret = gsc_check_scaler_ratio(variant, s_frame->crop.width, ++ s_frame->crop.height, d_frame->crop.width, d_frame->crop.height, ++ ctx->gsc_ctrls.rotate->val, ctx->out_path); ++ if (ret) { ++ pr_err("out of scaler range"); ++ return ret; ++ } ++ ++ if (ctx->gsc_ctrls.rotate->val == 90 || ++ ctx->gsc_ctrls.rotate->val == 270) { ++ ty = d_frame->crop.width; ++ tx = d_frame->crop.height; ++ } else { ++ tx = d_frame->crop.width; ++ ty = d_frame->crop.height; ++ } ++ ++ if (tx <= 0 || ty <= 0) { ++ dev_err(dev, "Invalid target size: %dx%d", tx, ty); ++ return -EINVAL; ++ } ++ ++ ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.width, ++ tx, &sc->pre_hratio); ++ if (ret) { ++ pr_err("Horizontal scale ratio is out of range"); ++ return ret; ++ } ++ ++ ret = gsc_cal_prescaler_ratio(variant, s_frame->crop.height, ++ ty, &sc->pre_vratio); ++ if (ret) { ++ pr_err("Vertical scale ratio is out of range"); ++ return ret; ++ } ++ ++ gsc_check_src_scale_info(variant, s_frame, &sc->pre_hratio, ++ tx, ty, &sc->pre_vratio); ++ ++ gsc_get_prescaler_shfactor(sc->pre_hratio, sc->pre_vratio, ++ &sc->pre_shfactor); ++ ++ sc->main_hratio = (s_frame->crop.width << 16) / tx; ++ sc->main_vratio = (s_frame->crop.height << 16) / ty; ++ ++ pr_debug("scaler input/output size : sx = %d, sy = %d, tx = %d, ty = %d", ++ s_frame->crop.width, s_frame->crop.height, tx, ty); ++ pr_debug("scaler ratio info : pre_shfactor : %d, pre_h : %d", ++ sc->pre_shfactor, sc->pre_hratio); ++ pr_debug("pre_v :%d, main_h : %d, main_v : %d", ++ sc->pre_vratio, sc->main_hratio, sc->main_vratio); ++ ++ return 0; ++} ++ ++static int __gsc_s_ctrl(struct gsc_ctx *ctx, struct v4l2_ctrl *ctrl) ++{ ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ struct gsc_variant *variant = gsc->variant; ++ unsigned int flags = GSC_DST_FMT | GSC_SRC_FMT; ++ int ret = 0; ++ ++ if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) ++ return 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_HFLIP: ++ ctx->hflip = ctrl->val; ++ break; ++ ++ case V4L2_CID_VFLIP: ++ ctx->vflip = ctrl->val; ++ break; ++ ++ case V4L2_CID_ROTATE: ++ if ((ctx->state & flags) == flags) { ++ ret = gsc_check_scaler_ratio(variant, ++ ctx->s_frame.crop.width, ++ ctx->s_frame.crop.height, ++ ctx->d_frame.crop.width, ++ ctx->d_frame.crop.height, ++ ctx->gsc_ctrls.rotate->val, ++ ctx->out_path); ++ ++ if (ret) ++ return -EINVAL; ++ } ++ ++ ctx->rotation = ctrl->val; ++ break; ++ ++ case V4L2_CID_ALPHA_COMPONENT: ++ ctx->d_frame.alpha = ctrl->val; ++ break; ++ } ++ ++ ctx->state |= GSC_PARAMS; ++ return 0; ++} ++ ++static int gsc_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct gsc_ctx *ctx = ctrl_to_ctx(ctrl); ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&ctx->gsc_dev->slock, flags); ++ ret = __gsc_s_ctrl(ctx, ctrl); ++ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); ++ ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops gsc_ctrl_ops = { ++ .s_ctrl = gsc_s_ctrl, ++}; ++ ++int gsc_ctrls_create(struct gsc_ctx *ctx) ++{ ++ if (ctx->ctrls_rdy) { ++ pr_err("Control handler of this context was created already"); ++ return 0; ++ } ++ ++ v4l2_ctrl_handler_init(&ctx->ctrl_handler, GSC_MAX_CTRL_NUM); ++ ++ ctx->gsc_ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, ++ &gsc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0); ++ ctx->gsc_ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, ++ &gsc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); ++ ctx->gsc_ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, ++ &gsc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); ++ ctx->gsc_ctrls.global_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler, ++ &gsc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); ++ ++ ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; ++ ++ if (ctx->ctrl_handler.error) { ++ int err = ctx->ctrl_handler.error; ++ v4l2_ctrl_handler_free(&ctx->ctrl_handler); ++ pr_err("Failed to create G-Scaler control handlers"); ++ return err; ++ } ++ ++ return 0; ++} ++ ++void gsc_ctrls_delete(struct gsc_ctx *ctx) ++{ ++ if (ctx->ctrls_rdy) { ++ v4l2_ctrl_handler_free(&ctx->ctrl_handler); ++ ctx->ctrls_rdy = false; ++ } ++} ++ ++/* The color format (num_comp, num_planes) must be already configured. */ ++int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, ++ struct gsc_frame *frame, struct gsc_addr *addr) ++{ ++ int ret = 0; ++ u32 pix_size; ++ ++ if ((vb == NULL) || (frame == NULL)) ++ return -EINVAL; ++ ++ pix_size = frame->f_width * frame->f_height; ++ ++ pr_debug("num_planes= %d, num_comp= %d, pix_size= %d", ++ frame->fmt->num_planes, frame->fmt->num_comp, pix_size); ++ ++ addr->y = vb2_dma_contig_plane_dma_addr(vb, 0); ++ ++ if (frame->fmt->num_planes == 1) { ++ switch (frame->fmt->num_comp) { ++ case 1: ++ addr->cb = 0; ++ addr->cr = 0; ++ break; ++ case 2: ++ /* decompose Y into Y/Cb */ ++ addr->cb = (dma_addr_t)(addr->y + pix_size); ++ addr->cr = 0; ++ break; ++ case 3: ++ /* decompose Y into Y/Cb/Cr */ ++ addr->cb = (dma_addr_t)(addr->y + pix_size); ++ if (GSC_YUV420 == frame->fmt->color) ++ addr->cr = (dma_addr_t)(addr->cb ++ + (pix_size >> 2)); ++ else /* 422 */ ++ addr->cr = (dma_addr_t)(addr->cb ++ + (pix_size >> 1)); ++ break; ++ default: ++ pr_err("Invalid the number of color planes"); ++ return -EINVAL; ++ } ++ } else { ++ if (frame->fmt->num_planes >= 2) ++ addr->cb = vb2_dma_contig_plane_dma_addr(vb, 1); ++ ++ if (frame->fmt->num_planes == 3) ++ addr->cr = vb2_dma_contig_plane_dma_addr(vb, 2); ++ } ++ ++ if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) || ++ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) || ++ (frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) || ++ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) || ++ (frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) || ++ (frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M)) ++ swap(addr->cb, addr->cr); ++ ++ pr_debug("ADDR: y= 0x%X cb= 0x%X cr= 0x%X ret= %d", ++ addr->y, addr->cb, addr->cr, ret); ++ ++ return ret; ++} ++ ++static irqreturn_t gsc_irq_handler(int irq, void *priv) ++{ ++ struct gsc_dev *gsc = priv; ++ struct gsc_ctx *ctx; ++ int gsc_irq; ++ ++ gsc_irq = gsc_hw_get_irq_status(gsc); ++ gsc_hw_clear_irq(gsc, gsc_irq); ++ ++ if (gsc_irq == GSC_IRQ_OVERRUN) { ++ pr_err("Local path input over-run interrupt has occurred!\n"); ++ return IRQ_HANDLED; ++ } ++ ++ spin_lock(&gsc->slock); ++ ++ if (test_and_clear_bit(ST_M2M_PEND, &gsc->state)) { ++ ++ gsc_hw_enable_control(gsc, false); ++ ++ if (test_and_clear_bit(ST_M2M_SUSPENDING, &gsc->state)) { ++ set_bit(ST_M2M_SUSPENDED, &gsc->state); ++ wake_up(&gsc->irq_queue); ++ goto isr_unlock; ++ } ++ ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); ++ ++ if (!ctx || !ctx->m2m_ctx) ++ goto isr_unlock; ++ ++ spin_unlock(&gsc->slock); ++ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); ++ ++ /* wake_up job_abort, stop_streaming */ ++ if (ctx->state & GSC_CTX_STOP_REQ) { ++ ctx->state &= ~GSC_CTX_STOP_REQ; ++ wake_up(&gsc->irq_queue); ++ } ++ return IRQ_HANDLED; ++ } ++ ++isr_unlock: ++ spin_unlock(&gsc->slock); ++ return IRQ_HANDLED; ++} ++ ++static struct gsc_pix_max gsc_v_100_max = { ++ .org_scaler_bypass_w = 8192, ++ .org_scaler_bypass_h = 8192, ++ .org_scaler_input_w = 4800, ++ .org_scaler_input_h = 3344, ++ .real_rot_dis_w = 4800, ++ .real_rot_dis_h = 3344, ++ .real_rot_en_w = 2047, ++ .real_rot_en_h = 2047, ++ .target_rot_dis_w = 4800, ++ .target_rot_dis_h = 3344, ++ .target_rot_en_w = 2016, ++ .target_rot_en_h = 2016, ++}; ++ ++static struct gsc_pix_min gsc_v_100_min = { ++ .org_w = 64, ++ .org_h = 32, ++ .real_w = 64, ++ .real_h = 32, ++ .target_rot_dis_w = 64, ++ .target_rot_dis_h = 32, ++ .target_rot_en_w = 32, ++ .target_rot_en_h = 16, ++}; ++ ++static struct gsc_pix_align gsc_v_100_align = { ++ .org_h = 16, ++ .org_w = 16, /* yuv420 : 16, others : 8 */ ++ .offset_h = 2, /* yuv420/422 : 2, others : 1 */ ++ .real_w = 16, /* yuv420/422 : 4~16, others : 2~8 */ ++ .real_h = 16, /* yuv420 : 4~16, others : 1 */ ++ .target_w = 2, /* yuv420/422 : 2, others : 1 */ ++ .target_h = 2, /* yuv420 : 2, others : 1 */ ++}; ++ ++static struct gsc_variant gsc_v_100_variant = { ++ .pix_max = &gsc_v_100_max, ++ .pix_min = &gsc_v_100_min, ++ .pix_align = &gsc_v_100_align, ++ .in_buf_cnt = 32, ++ .out_buf_cnt = 32, ++ .sc_up_max = 8, ++ .sc_down_max = 16, ++ .poly_sc_down_max = 4, ++ .pre_sc_down_max = 4, ++ .local_sc_down = 2, ++}; ++ ++static struct gsc_driverdata gsc_v_100_drvdata = { ++ .variant = { ++ [0] = &gsc_v_100_variant, ++ [1] = &gsc_v_100_variant, ++ [2] = &gsc_v_100_variant, ++ [3] = &gsc_v_100_variant, ++ }, ++ .num_entities = 4, ++ .lclk_frequency = 266000000UL, ++}; ++ ++static struct platform_device_id gsc_driver_ids[] = { ++ { ++ .name = "exynos-gsc", ++ .driver_data = (unsigned long)&gsc_v_100_drvdata, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(platform, gsc_driver_ids); ++ ++static const struct of_device_id exynos_gsc_match[] = { ++ { ++ .compatible = "samsung,exynos5-gsc", ++ .data = &gsc_v_100_drvdata, ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, exynos_gsc_match); ++ ++static void *gsc_get_drv_data(struct platform_device *pdev) ++{ ++ struct gsc_driverdata *driver_data = NULL; ++ ++ if (pdev->dev.of_node) { ++ const struct of_device_id *match; ++ match = of_match_node(of_match_ptr(exynos_gsc_match), ++ pdev->dev.of_node); ++ if (match) ++ driver_data = (struct gsc_driverdata *)match->data; ++ } else { ++ driver_data = (struct gsc_driverdata *) ++ platform_get_device_id(pdev)->driver_data; ++ } ++ ++ return driver_data; ++} ++ ++static void gsc_clk_put(struct gsc_dev *gsc) ++{ ++ if (!IS_ERR(gsc->clock)) ++ clk_unprepare(gsc->clock); ++} ++ ++static int gsc_clk_get(struct gsc_dev *gsc) ++{ ++ int ret; ++ ++ dev_dbg(&gsc->pdev->dev, "gsc_clk_get Called\n"); ++ ++ gsc->clock = devm_clk_get(&gsc->pdev->dev, GSC_CLOCK_GATE_NAME); ++ if (IS_ERR(gsc->clock)) { ++ dev_err(&gsc->pdev->dev, "failed to get clock~~~: %s\n", ++ GSC_CLOCK_GATE_NAME); ++ return PTR_ERR(gsc->clock); ++ } ++ ++ ret = clk_prepare(gsc->clock); ++ if (ret < 0) { ++ dev_err(&gsc->pdev->dev, "clock prepare failed for clock: %s\n", ++ GSC_CLOCK_GATE_NAME); ++ gsc->clock = ERR_PTR(-EINVAL); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gsc_m2m_suspend(struct gsc_dev *gsc) ++{ ++ unsigned long flags; ++ int timeout; ++ ++ spin_lock_irqsave(&gsc->slock, flags); ++ if (!gsc_m2m_pending(gsc)) { ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ return 0; ++ } ++ clear_bit(ST_M2M_SUSPENDED, &gsc->state); ++ set_bit(ST_M2M_SUSPENDING, &gsc->state); ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ ++ timeout = wait_event_timeout(gsc->irq_queue, ++ test_bit(ST_M2M_SUSPENDED, &gsc->state), ++ GSC_SHUTDOWN_TIMEOUT); ++ ++ clear_bit(ST_M2M_SUSPENDING, &gsc->state); ++ return timeout == 0 ? -EAGAIN : 0; ++} ++ ++static int gsc_m2m_resume(struct gsc_dev *gsc) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gsc->slock, flags); ++ /* Clear for full H/W setup in first run after resume */ ++ gsc->m2m.ctx = NULL; ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ ++ if (test_and_clear_bit(ST_M2M_SUSPENDED, &gsc->state)) ++ gsc_m2m_job_finish(gsc->m2m.ctx, ++ VB2_BUF_STATE_ERROR); ++ return 0; ++} ++ ++static int gsc_probe(struct platform_device *pdev) ++{ ++ struct gsc_dev *gsc; ++ struct resource *res; ++ struct gsc_driverdata *drv_data = gsc_get_drv_data(pdev); ++ struct device *dev = &pdev->dev; ++ int ret = 0; ++ ++ gsc = devm_kzalloc(dev, sizeof(struct gsc_dev), GFP_KERNEL); ++ if (!gsc) ++ return -ENOMEM; ++ ++ if (dev->of_node) ++ gsc->id = of_alias_get_id(pdev->dev.of_node, "gsc"); ++ else ++ gsc->id = pdev->id; ++ ++ if (gsc->id < 0 || gsc->id >= drv_data->num_entities) { ++ dev_err(dev, "Invalid platform device id: %d\n", gsc->id); ++ return -EINVAL; ++ } ++ ++ gsc->variant = drv_data->variant[gsc->id]; ++ gsc->pdev = pdev; ++ gsc->pdata = dev->platform_data; ++ ++ init_waitqueue_head(&gsc->irq_queue); ++ spin_lock_init(&gsc->slock); ++ mutex_init(&gsc->lock); ++ gsc->clock = ERR_PTR(-EINVAL); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ gsc->regs = devm_request_and_ioremap(dev, res); ++ if (!gsc->regs) { ++ dev_err(dev, "failed to map registers\n"); ++ return -ENOENT; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res) { ++ dev_err(dev, "failed to get IRQ resource\n"); ++ return -ENXIO; ++ } ++ ++ ret = gsc_clk_get(gsc); ++ if (ret) ++ return ret; ++ ++ ret = devm_request_irq(dev, res->start, gsc_irq_handler, ++ 0, pdev->name, gsc); ++ if (ret) { ++ dev_err(dev, "failed to install irq (%d)\n", ret); ++ goto err_clk; ++ } ++ ++ ret = gsc_register_m2m_device(gsc); ++ if (ret) ++ goto err_clk; ++ ++ platform_set_drvdata(pdev, gsc); ++ pm_runtime_enable(dev); ++ ret = pm_runtime_get_sync(&pdev->dev); ++ if (ret < 0) ++ goto err_m2m; ++ ++ /* Initialize continious memory allocator */ ++ gsc->alloc_ctx = vb2_dma_contig_init_ctx(dev); ++ if (IS_ERR(gsc->alloc_ctx)) { ++ ret = PTR_ERR(gsc->alloc_ctx); ++ goto err_pm; ++ } ++ ++ dev_dbg(dev, "gsc-%d registered successfully\n", gsc->id); ++ ++ pm_runtime_put(dev); ++ return 0; ++err_pm: ++ pm_runtime_put(dev); ++err_m2m: ++ gsc_unregister_m2m_device(gsc); ++err_clk: ++ gsc_clk_put(gsc); ++ return ret; ++} ++ ++static int __devexit gsc_remove(struct platform_device *pdev) ++{ ++ struct gsc_dev *gsc = platform_get_drvdata(pdev); ++ ++ gsc_unregister_m2m_device(gsc); ++ ++ vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx); ++ pm_runtime_disable(&pdev->dev); ++ gsc_clk_put(gsc); ++ ++ dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name); ++ return 0; ++} ++ ++static int gsc_runtime_resume(struct device *dev) ++{ ++ struct gsc_dev *gsc = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); ++ ++ ret = clk_enable(gsc->clock); ++ if (ret) ++ return ret; ++ ++ gsc_hw_set_sw_reset(gsc); ++ gsc_wait_reset(gsc); ++ ++ return gsc_m2m_resume(gsc); ++} ++ ++static int gsc_runtime_suspend(struct device *dev) ++{ ++ struct gsc_dev *gsc = dev_get_drvdata(dev); ++ int ret = 0; ++ ++ ret = gsc_m2m_suspend(gsc); ++ if (!ret) ++ clk_disable(gsc->clock); ++ ++ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); ++ return ret; ++} ++ ++static int gsc_resume(struct device *dev) ++{ ++ struct gsc_dev *gsc = dev_get_drvdata(dev); ++ unsigned long flags; ++ ++ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); ++ ++ /* Do not resume if the device was idle before system suspend */ ++ spin_lock_irqsave(&gsc->slock, flags); ++ if (!test_and_clear_bit(ST_SUSPEND, &gsc->state) || ++ !gsc_m2m_active(gsc)) { ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ return 0; ++ } ++ gsc_hw_set_sw_reset(gsc); ++ gsc_wait_reset(gsc); ++ ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ ++ return gsc_m2m_resume(gsc); ++} ++ ++static int gsc_suspend(struct device *dev) ++{ ++ struct gsc_dev *gsc = dev_get_drvdata(dev); ++ ++ pr_debug("gsc%d: state: 0x%lx", gsc->id, gsc->state); ++ ++ if (test_and_set_bit(ST_SUSPEND, &gsc->state)) ++ return 0; ++ ++ return gsc_m2m_suspend(gsc); ++} ++ ++static const struct dev_pm_ops gsc_pm_ops = { ++ .suspend = gsc_suspend, ++ .resume = gsc_resume, ++ .runtime_suspend = gsc_runtime_suspend, ++ .runtime_resume = gsc_runtime_resume, ++}; ++ ++static struct platform_driver gsc_driver = { ++ .probe = gsc_probe, ++ .remove = __devexit_p(gsc_remove), ++ .id_table = gsc_driver_ids, ++ .driver = { ++ .name = GSC_MODULE_NAME, ++ .owner = THIS_MODULE, ++ .pm = &gsc_pm_ops, ++ .of_match_table = exynos_gsc_match, ++ } ++}; ++ ++module_platform_driver(gsc_driver); ++ ++MODULE_AUTHOR("Hyunwong Kim "); ++MODULE_DESCRIPTION("Samsung EXYNOS5 Soc series G-Scaler driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h +new file mode 100644 +index 0000000..cc19bba +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/gsc-core.h +@@ -0,0 +1,532 @@ ++/* ++ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * header file for Samsung EXYNOS5 SoC series G-Scaler driver ++ ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef GSC_CORE_H_ ++#define GSC_CORE_H_ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "gsc-regs.h" ++ ++#define CONFIG_VB2_GSC_DMA_CONTIG 1 ++#define GSC_MODULE_NAME "exynos-gsc" ++ ++#define GSC_SHUTDOWN_TIMEOUT ((100*HZ)/1000) ++#define GSC_MAX_DEVS 4 ++#define GSC_M2M_BUF_NUM 0 ++#define GSC_MAX_CTRL_NUM 10 ++#define GSC_SC_ALIGN_4 4 ++#define GSC_SC_ALIGN_2 2 ++#define DEFAULT_CSC_EQ 1 ++#define DEFAULT_CSC_RANGE 1 ++ ++#define GSC_PARAMS (1 << 0) ++#define GSC_SRC_FMT (1 << 1) ++#define GSC_DST_FMT (1 << 2) ++#define GSC_CTX_M2M (1 << 3) ++#define GSC_CTX_STOP_REQ (1 << 6) ++ ++enum gsc_dev_flags { ++ /* for global */ ++ ST_SUSPEND, ++ ++ /* for m2m node */ ++ ST_M2M_OPEN, ++ ST_M2M_RUN, ++ ST_M2M_PEND, ++ ST_M2M_SUSPENDED, ++ ST_M2M_SUSPENDING, ++}; ++ ++enum gsc_irq { ++ GSC_IRQ_DONE, ++ GSC_IRQ_OVERRUN ++}; ++ ++/** ++ * enum gsc_datapath - the path of data used for G-Scaler ++ * @GSC_CAMERA: from camera ++ * @GSC_DMA: from/to DMA ++ * @GSC_LOCAL: to local path ++ * @GSC_WRITEBACK: from FIMD ++ */ ++enum gsc_datapath { ++ GSC_CAMERA = 0x1, ++ GSC_DMA, ++ GSC_MIXER, ++ GSC_FIMD, ++ GSC_WRITEBACK, ++}; ++ ++enum gsc_color_fmt { ++ GSC_RGB = 0x1, ++ GSC_YUV420 = 0x2, ++ GSC_YUV422 = 0x4, ++ GSC_YUV444 = 0x8, ++}; ++ ++enum gsc_yuv_fmt { ++ GSC_LSB_Y = 0x10, ++ GSC_LSB_C, ++ GSC_CBCR = 0x20, ++ GSC_CRCB, ++}; ++ ++#define fh_to_ctx(__fh) container_of(__fh, struct gsc_ctx, fh) ++#define is_rgb(x) (!!((x) & 0x1)) ++#define is_yuv420(x) (!!((x) & 0x2)) ++#define is_yuv422(x) (!!((x) & 0x4)) ++ ++#define gsc_m2m_active(dev) test_bit(ST_M2M_RUN, &(dev)->state) ++#define gsc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state) ++#define gsc_m2m_opened(dev) test_bit(ST_M2M_OPEN, &(dev)->state) ++ ++#define ctrl_to_ctx(__ctrl) \ ++ container_of((__ctrl)->handler, struct gsc_ctx, ctrl_handler) ++/** ++ * struct gsc_fmt - the driver's internal color format data ++ * @mbus_code: Media Bus pixel code, -1 if not applicable ++ * @name: format description ++ * @pixelformat: the fourcc code for this format, 0 if not applicable ++ * @yorder: Y/C order ++ * @corder: Chrominance order control ++ * @num_planes: number of physically non-contiguous data planes ++ * @nr_comp: number of physically contiguous data planes ++ * @depth: per plane driver's private 'number of bits per pixel' ++ * @flags: flags indicating which operation mode format applies to ++ */ ++struct gsc_fmt { ++ enum v4l2_mbus_pixelcode mbus_code; ++ char *name; ++ u32 pixelformat; ++ u32 color; ++ u32 yorder; ++ u32 corder; ++ u16 num_planes; ++ u16 num_comp; ++ u8 depth[VIDEO_MAX_PLANES]; ++ u32 flags; ++}; ++ ++/** ++ * struct gsc_input_buf - the driver's video buffer ++ * @vb: videobuf2 buffer ++ * @list : linked list structure for buffer queue ++ * @idx : index of G-Scaler input buffer ++ */ ++struct gsc_input_buf { ++ struct vb2_buffer vb; ++ struct list_head list; ++ int idx; ++}; ++ ++/** ++ * struct gsc_addr - the G-Scaler physical address set ++ * @y: luminance plane address ++ * @cb: Cb plane address ++ * @cr: Cr plane address ++ */ ++struct gsc_addr { ++ dma_addr_t y; ++ dma_addr_t cb; ++ dma_addr_t cr; ++}; ++ ++/* struct gsc_ctrls - the G-Scaler control set ++ * @rotate: rotation degree ++ * @hflip: horizontal flip ++ * @vflip: vertical flip ++ * @global_alpha: the alpha value of current frame ++ */ ++struct gsc_ctrls { ++ struct v4l2_ctrl *rotate; ++ struct v4l2_ctrl *hflip; ++ struct v4l2_ctrl *vflip; ++ struct v4l2_ctrl *global_alpha; ++}; ++ ++/** ++ * struct gsc_scaler - the configuration data for G-Scaler inetrnal scaler ++ * @pre_shfactor: pre sclaer shift factor ++ * @pre_hratio: horizontal ratio of the prescaler ++ * @pre_vratio: vertical ratio of the prescaler ++ * @main_hratio: the main scaler's horizontal ratio ++ * @main_vratio: the main scaler's vertical ratio ++ */ ++struct gsc_scaler { ++ u32 pre_shfactor; ++ u32 pre_hratio; ++ u32 pre_vratio; ++ u32 main_hratio; ++ u32 main_vratio; ++}; ++ ++struct gsc_dev; ++ ++struct gsc_ctx; ++ ++/** ++ * struct gsc_frame - source/target frame properties ++ * @f_width: SRC : SRCIMG_WIDTH, DST : OUTPUTDMA_WHOLE_IMG_WIDTH ++ * @f_height: SRC : SRCIMG_HEIGHT, DST : OUTPUTDMA_WHOLE_IMG_HEIGHT ++ * @crop: cropped(source)/scaled(destination) size ++ * @payload: image size in bytes (w x h x bpp) ++ * @addr: image frame buffer physical addresses ++ * @fmt: G-Scaler color format pointer ++ * @colorspace: value indicating v4l2_colorspace ++ * @alpha: frame's alpha value ++ */ ++struct gsc_frame { ++ u32 f_width; ++ u32 f_height; ++ struct v4l2_rect crop; ++ unsigned long payload[VIDEO_MAX_PLANES]; ++ struct gsc_addr addr; ++ const struct gsc_fmt *fmt; ++ u32 colorspace; ++ u8 alpha; ++}; ++ ++/** ++ * struct gsc_m2m_device - v4l2 memory-to-memory device data ++ * @vfd: the video device node for v4l2 m2m mode ++ * @m2m_dev: v4l2 memory-to-memory device data ++ * @ctx: hardware context data ++ * @refcnt: the reference counter ++ */ ++struct gsc_m2m_device { ++ struct video_device *vfd; ++ struct v4l2_m2m_dev *m2m_dev; ++ struct gsc_ctx *ctx; ++ int refcnt; ++}; ++ ++/** ++ * struct gsc_pix_max - image pixel size limits in various IP configurations ++ * ++ * @org_scaler_bypass_w: max pixel width when the scaler is disabled ++ * @org_scaler_bypass_h: max pixel height when the scaler is disabled ++ * @org_scaler_input_w: max pixel width when the scaler is enabled ++ * @org_scaler_input_h: max pixel height when the scaler is enabled ++ * @real_rot_dis_w: max pixel src cropped height with the rotator is off ++ * @real_rot_dis_h: max pixel src croppped width with the rotator is off ++ * @real_rot_en_w: max pixel src cropped width with the rotator is on ++ * @real_rot_en_h: max pixel src cropped height with the rotator is on ++ * @target_rot_dis_w: max pixel dst scaled width with the rotator is off ++ * @target_rot_dis_h: max pixel dst scaled height with the rotator is off ++ * @target_rot_en_w: max pixel dst scaled width with the rotator is on ++ * @target_rot_en_h: max pixel dst scaled height with the rotator is on ++ */ ++struct gsc_pix_max { ++ u16 org_scaler_bypass_w; ++ u16 org_scaler_bypass_h; ++ u16 org_scaler_input_w; ++ u16 org_scaler_input_h; ++ u16 real_rot_dis_w; ++ u16 real_rot_dis_h; ++ u16 real_rot_en_w; ++ u16 real_rot_en_h; ++ u16 target_rot_dis_w; ++ u16 target_rot_dis_h; ++ u16 target_rot_en_w; ++ u16 target_rot_en_h; ++}; ++ ++/** ++ * struct gsc_pix_min - image pixel size limits in various IP configurations ++ * ++ * @org_w: minimum source pixel width ++ * @org_h: minimum source pixel height ++ * @real_w: minimum input crop pixel width ++ * @real_h: minimum input crop pixel height ++ * @target_rot_dis_w: minimum output scaled pixel height when rotator is off ++ * @target_rot_dis_h: minimum output scaled pixel height when rotator is off ++ * @target_rot_en_w: minimum output scaled pixel height when rotator is on ++ * @target_rot_en_h: minimum output scaled pixel height when rotator is on ++ */ ++struct gsc_pix_min { ++ u16 org_w; ++ u16 org_h; ++ u16 real_w; ++ u16 real_h; ++ u16 target_rot_dis_w; ++ u16 target_rot_dis_h; ++ u16 target_rot_en_w; ++ u16 target_rot_en_h; ++}; ++ ++struct gsc_pix_align { ++ u16 org_h; ++ u16 org_w; ++ u16 offset_h; ++ u16 real_w; ++ u16 real_h; ++ u16 target_w; ++ u16 target_h; ++}; ++ ++/** ++ * struct gsc_variant - G-Scaler variant information ++ */ ++struct gsc_variant { ++ struct gsc_pix_max *pix_max; ++ struct gsc_pix_min *pix_min; ++ struct gsc_pix_align *pix_align; ++ u16 in_buf_cnt; ++ u16 out_buf_cnt; ++ u16 sc_up_max; ++ u16 sc_down_max; ++ u16 poly_sc_down_max; ++ u16 pre_sc_down_max; ++ u16 local_sc_down; ++}; ++ ++/** ++ * struct gsc_driverdata - per device type driver data for init time. ++ * ++ * @variant: the variant information for this driver. ++ * @lclk_frequency: G-Scaler clock frequency ++ * @num_entities: the number of g-scalers ++ */ ++struct gsc_driverdata { ++ struct gsc_variant *variant[GSC_MAX_DEVS]; ++ unsigned long lclk_frequency; ++ int num_entities; ++}; ++ ++/** ++ * struct gsc_dev - abstraction for G-Scaler entity ++ * @slock: the spinlock protecting this data structure ++ * @lock: the mutex protecting this data structure ++ * @pdev: pointer to the G-Scaler platform device ++ * @variant: the IP variant information ++ * @id: G-Scaler device index (0..GSC_MAX_DEVS) ++ * @clock: clocks required for G-Scaler operation ++ * @regs: the mapped hardware registers ++ * @irq_queue: interrupt handler waitqueue ++ * @m2m: memory-to-memory V4L2 device information ++ * @state: flags used to synchronize m2m and capture mode operation ++ * @alloc_ctx: videobuf2 memory allocator context ++ * @vdev: video device for G-Scaler instance ++ */ ++struct gsc_dev { ++ spinlock_t slock; ++ struct mutex lock; ++ struct platform_device *pdev; ++ struct gsc_variant *variant; ++ u16 id; ++ struct clk *clock; ++ void __iomem *regs; ++ wait_queue_head_t irq_queue; ++ struct gsc_m2m_device m2m; ++ struct exynos_platform_gscaler *pdata; ++ unsigned long state; ++ struct vb2_alloc_ctx *alloc_ctx; ++ struct video_device vdev; ++}; ++ ++/** ++ * gsc_ctx - the device context data ++ * @s_frame: source frame properties ++ * @d_frame: destination frame properties ++ * @in_path: input mode (DMA or camera) ++ * @out_path: output mode (DMA or FIFO) ++ * @scaler: image scaler properties ++ * @flags: additional flags for image conversion ++ * @state: flags to keep track of user configuration ++ * @gsc_dev: the G-Scaler device this context applies to ++ * @m2m_ctx: memory-to-memory device context ++ * @fh: v4l2 file handle ++ * @ctrl_handler: v4l2 controls handler ++ * @gsc_ctrls G-Scaler control set ++ * @ctrls_rdy: true if the control handler is initialized ++ */ ++struct gsc_ctx { ++ struct gsc_frame s_frame; ++ struct gsc_frame d_frame; ++ enum gsc_datapath in_path; ++ enum gsc_datapath out_path; ++ struct gsc_scaler scaler; ++ u32 flags; ++ u32 state; ++ int rotation; ++ unsigned int hflip:1; ++ unsigned int vflip:1; ++ struct gsc_dev *gsc_dev; ++ struct v4l2_m2m_ctx *m2m_ctx; ++ struct v4l2_fh fh; ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct gsc_ctrls gsc_ctrls; ++ bool ctrls_rdy; ++}; ++ ++void gsc_set_prefbuf(struct gsc_dev *gsc, struct gsc_frame *frm); ++int gsc_register_m2m_device(struct gsc_dev *gsc); ++void gsc_unregister_m2m_device(struct gsc_dev *gsc); ++void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state); ++ ++u32 get_plane_size(struct gsc_frame *fr, unsigned int plane); ++const struct gsc_fmt *get_format(int index); ++const struct gsc_fmt *find_fmt(u32 *pixelformat, u32 *mbus_code, u32 index); ++int gsc_enum_fmt_mplane(struct v4l2_fmtdesc *f); ++int gsc_try_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); ++void gsc_set_frame_size(struct gsc_frame *frame, int width, int height); ++int gsc_g_fmt_mplane(struct gsc_ctx *ctx, struct v4l2_format *f); ++void gsc_check_crop_change(u32 tmp_w, u32 tmp_h, u32 *w, u32 *h); ++int gsc_g_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr); ++int gsc_try_crop(struct gsc_ctx *ctx, struct v4l2_crop *cr); ++int gsc_cal_prescaler_ratio(struct gsc_variant *var, u32 src, u32 dst, ++ u32 *ratio); ++void gsc_get_prescaler_shfactor(u32 hratio, u32 vratio, u32 *sh); ++void gsc_check_src_scale_info(struct gsc_variant *var, ++ struct gsc_frame *s_frame, ++ u32 *wratio, u32 tx, u32 ty, u32 *hratio); ++int gsc_check_scaler_ratio(struct gsc_variant *var, int sw, int sh, int dw, ++ int dh, int rot, int out_path); ++int gsc_set_scaler_info(struct gsc_ctx *ctx); ++int gsc_ctrls_create(struct gsc_ctx *ctx); ++void gsc_ctrls_delete(struct gsc_ctx *ctx); ++int gsc_prepare_addr(struct gsc_ctx *ctx, struct vb2_buffer *vb, ++ struct gsc_frame *frame, struct gsc_addr *addr); ++ ++static inline void gsc_ctx_state_lock_set(u32 state, struct gsc_ctx *ctx) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->gsc_dev->slock, flags); ++ ctx->state |= state; ++ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); ++} ++ ++static inline void gsc_ctx_state_lock_clear(u32 state, struct gsc_ctx *ctx) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ctx->gsc_dev->slock, flags); ++ ctx->state &= ~state; ++ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); ++} ++ ++static inline int is_tiled(const struct gsc_fmt *fmt) ++{ ++ return fmt->pixelformat == V4L2_PIX_FMT_NV12MT_16X16; ++} ++ ++static inline void gsc_hw_enable_control(struct gsc_dev *dev, bool on) ++{ ++ u32 cfg = readl(dev->regs + GSC_ENABLE); ++ ++ if (on) ++ cfg |= GSC_ENABLE_ON; ++ else ++ cfg &= ~GSC_ENABLE_ON; ++ ++ writel(cfg, dev->regs + GSC_ENABLE); ++} ++ ++static inline int gsc_hw_get_irq_status(struct gsc_dev *dev) ++{ ++ u32 cfg = readl(dev->regs + GSC_IRQ); ++ if (cfg & GSC_IRQ_STATUS_OR_IRQ) ++ return GSC_IRQ_OVERRUN; ++ else ++ return GSC_IRQ_DONE; ++ ++} ++ ++static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) ++{ ++ u32 cfg = readl(dev->regs + GSC_IRQ); ++ if (irq == GSC_IRQ_OVERRUN) ++ cfg |= GSC_IRQ_STATUS_OR_IRQ; ++ else if (irq == GSC_IRQ_DONE) ++ cfg |= GSC_IRQ_STATUS_FRM_DONE_IRQ; ++ writel(cfg, dev->regs + GSC_IRQ); ++} ++ ++static inline void gsc_lock(struct vb2_queue *vq) ++{ ++ struct gsc_ctx *ctx = vb2_get_drv_priv(vq); ++ mutex_lock(&ctx->gsc_dev->lock); ++} ++ ++static inline void gsc_unlock(struct vb2_queue *vq) ++{ ++ struct gsc_ctx *ctx = vb2_get_drv_priv(vq); ++ mutex_unlock(&ctx->gsc_dev->lock); ++} ++ ++static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) ++{ ++ unsigned long flags; ++ bool ret; ++ ++ spin_lock_irqsave(&ctx->gsc_dev->slock, flags); ++ ret = (ctx->state & mask) == mask; ++ spin_unlock_irqrestore(&ctx->gsc_dev->slock, flags); ++ return ret; ++} ++ ++static inline struct gsc_frame *ctx_get_frame(struct gsc_ctx *ctx, ++ enum v4l2_buf_type type) ++{ ++ struct gsc_frame *frame; ++ ++ if (V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE == type) { ++ frame = &ctx->s_frame; ++ } else if (V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE == type) { ++ frame = &ctx->d_frame; ++ } else { ++ pr_err("Wrong buffer/video queue type (%d)", type); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return frame; ++} ++ ++void gsc_hw_set_sw_reset(struct gsc_dev *dev); ++int gsc_wait_reset(struct gsc_dev *dev); ++ ++void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask); ++void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask); ++void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, bool enable); ++void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, bool enable); ++void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, ++ int index); ++void gsc_hw_set_output_addr(struct gsc_dev *dev, struct gsc_addr *addr, ++ int index); ++void gsc_hw_set_input_path(struct gsc_ctx *ctx); ++void gsc_hw_set_in_size(struct gsc_ctx *ctx); ++void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx); ++void gsc_hw_set_in_image_format(struct gsc_ctx *ctx); ++void gsc_hw_set_output_path(struct gsc_ctx *ctx); ++void gsc_hw_set_out_size(struct gsc_ctx *ctx); ++void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx); ++void gsc_hw_set_out_image_format(struct gsc_ctx *ctx); ++void gsc_hw_set_prescaler(struct gsc_ctx *ctx); ++void gsc_hw_set_mainscaler(struct gsc_ctx *ctx); ++void gsc_hw_set_rotation(struct gsc_ctx *ctx); ++void gsc_hw_set_global_alpha(struct gsc_ctx *ctx); ++void gsc_hw_set_sfr_update(struct gsc_ctx *ctx); ++ ++#endif /* GSC_CORE_H_ */ +diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c +new file mode 100644 +index 0000000..386c0a7 +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c +@@ -0,0 +1,784 @@ ++/* ++ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * Samsung EXYNOS5 SoC series G-Scaler driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation, either version 2 of the License, ++ * or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "gsc-core.h" ++ ++static int gsc_m2m_ctx_stop_req(struct gsc_ctx *ctx) ++{ ++ struct gsc_ctx *curr_ctx; ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ int ret; ++ ++ curr_ctx = v4l2_m2m_get_curr_priv(gsc->m2m.m2m_dev); ++ if (!gsc_m2m_pending(gsc) || (curr_ctx != ctx)) ++ return 0; ++ ++ gsc_ctx_state_lock_set(GSC_CTX_STOP_REQ, ctx); ++ ret = wait_event_timeout(gsc->irq_queue, ++ !gsc_ctx_state_is_set(GSC_CTX_STOP_REQ, ctx), ++ GSC_SHUTDOWN_TIMEOUT); ++ ++ return ret == 0 ? -ETIMEDOUT : ret; ++} ++ ++static int gsc_m2m_start_streaming(struct vb2_queue *q, unsigned int count) ++{ ++ struct gsc_ctx *ctx = q->drv_priv; ++ int ret; ++ ++ ret = pm_runtime_get_sync(&ctx->gsc_dev->pdev->dev); ++ return ret > 0 ? 0 : ret; ++} ++ ++static int gsc_m2m_stop_streaming(struct vb2_queue *q) ++{ ++ struct gsc_ctx *ctx = q->drv_priv; ++ int ret; ++ ++ ret = gsc_m2m_ctx_stop_req(ctx); ++ if (ret == -ETIMEDOUT) ++ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); ++ ++ pm_runtime_put(&ctx->gsc_dev->pdev->dev); ++ ++ return 0; ++} ++ ++void gsc_m2m_job_finish(struct gsc_ctx *ctx, int vb_state) ++{ ++ struct vb2_buffer *src_vb, *dst_vb; ++ ++ if (!ctx || !ctx->m2m_ctx) ++ return; ++ ++ src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); ++ dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); ++ ++ if (src_vb && dst_vb) { ++ v4l2_m2m_buf_done(src_vb, vb_state); ++ v4l2_m2m_buf_done(dst_vb, vb_state); ++ ++ v4l2_m2m_job_finish(ctx->gsc_dev->m2m.m2m_dev, ++ ctx->m2m_ctx); ++ } ++} ++ ++ ++static void gsc_m2m_job_abort(void *priv) ++{ ++ struct gsc_ctx *ctx = priv; ++ int ret; ++ ++ ret = gsc_m2m_ctx_stop_req(ctx); ++ if (ret == -ETIMEDOUT) ++ gsc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); ++} ++ ++static int gsc_get_bufs(struct gsc_ctx *ctx) ++{ ++ struct gsc_frame *s_frame, *d_frame; ++ struct vb2_buffer *src_vb, *dst_vb; ++ int ret; ++ ++ s_frame = &ctx->s_frame; ++ d_frame = &ctx->d_frame; ++ ++ src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ++ ret = gsc_prepare_addr(ctx, src_vb, s_frame, &s_frame->addr); ++ if (ret) ++ return ret; ++ ++ dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ ret = gsc_prepare_addr(ctx, dst_vb, d_frame, &d_frame->addr); ++ if (ret) ++ return ret; ++ ++ dst_vb->v4l2_buf.timestamp = src_vb->v4l2_buf.timestamp; ++ ++ return 0; ++} ++ ++static void gsc_m2m_device_run(void *priv) ++{ ++ struct gsc_ctx *ctx = priv; ++ struct gsc_dev *gsc; ++ unsigned long flags; ++ int ret; ++ bool is_set = false; ++ ++ if (WARN(!ctx, "null hardware context\n")) ++ return; ++ ++ gsc = ctx->gsc_dev; ++ spin_lock_irqsave(&gsc->slock, flags); ++ ++ set_bit(ST_M2M_PEND, &gsc->state); ++ ++ /* Reconfigure hardware if the context has changed. */ ++ if (gsc->m2m.ctx != ctx) { ++ pr_debug("gsc->m2m.ctx = 0x%p, current_ctx = 0x%p", ++ gsc->m2m.ctx, ctx); ++ ctx->state |= GSC_PARAMS; ++ gsc->m2m.ctx = ctx; ++ } ++ ++ is_set = (ctx->state & GSC_CTX_STOP_REQ) ? 1 : 0; ++ ctx->state &= ~GSC_CTX_STOP_REQ; ++ if (is_set) { ++ wake_up(&gsc->irq_queue); ++ goto put_device; ++ } ++ ++ ret = gsc_get_bufs(ctx); ++ if (ret) { ++ pr_err("Wrong address"); ++ goto put_device; ++ } ++ ++ gsc_set_prefbuf(gsc, &ctx->s_frame); ++ gsc_hw_set_input_addr(gsc, &ctx->s_frame.addr, GSC_M2M_BUF_NUM); ++ gsc_hw_set_output_addr(gsc, &ctx->d_frame.addr, GSC_M2M_BUF_NUM); ++ ++ if (ctx->state & GSC_PARAMS) { ++ gsc_hw_set_input_buf_masking(gsc, GSC_M2M_BUF_NUM, false); ++ gsc_hw_set_output_buf_masking(gsc, GSC_M2M_BUF_NUM, false); ++ gsc_hw_set_frm_done_irq_mask(gsc, false); ++ gsc_hw_set_gsc_irq_enable(gsc, true); ++ ++ if (gsc_set_scaler_info(ctx)) { ++ pr_err("Scaler setup error"); ++ goto put_device; ++ } ++ ++ gsc_hw_set_input_path(ctx); ++ gsc_hw_set_in_size(ctx); ++ gsc_hw_set_in_image_format(ctx); ++ ++ gsc_hw_set_output_path(ctx); ++ gsc_hw_set_out_size(ctx); ++ gsc_hw_set_out_image_format(ctx); ++ ++ gsc_hw_set_prescaler(ctx); ++ gsc_hw_set_mainscaler(ctx); ++ gsc_hw_set_rotation(ctx); ++ gsc_hw_set_global_alpha(ctx); ++ } ++ ++ /* update shadow registers */ ++ gsc_hw_set_sfr_update(ctx); ++ ++ ctx->state &= ~GSC_PARAMS; ++ gsc_hw_enable_control(gsc, true); ++ ++ spin_unlock_irqrestore(&gsc->slock, flags); ++ return; ++ ++put_device: ++ ctx->state &= ~GSC_PARAMS; ++ spin_unlock_irqrestore(&gsc->slock, flags); ++} ++ ++static int gsc_m2m_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *num_buffers, unsigned int *num_planes, ++ unsigned int sizes[], void *allocators[]) ++{ ++ struct gsc_ctx *ctx = vb2_get_drv_priv(vq); ++ struct gsc_frame *frame; ++ int i; ++ ++ frame = ctx_get_frame(ctx, vq->type); ++ if (IS_ERR(frame)) ++ return PTR_ERR(frame); ++ ++ if (!frame->fmt) ++ return -EINVAL; ++ ++ *num_planes = frame->fmt->num_planes; ++ for (i = 0; i < frame->fmt->num_planes; i++) { ++ sizes[i] = frame->payload[i]; ++ allocators[i] = ctx->gsc_dev->alloc_ctx; ++ } ++ return 0; ++} ++ ++static int gsc_m2m_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ struct gsc_frame *frame; ++ int i; ++ ++ frame = ctx_get_frame(ctx, vb->vb2_queue->type); ++ if (IS_ERR(frame)) ++ return PTR_ERR(frame); ++ ++ if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { ++ for (i = 0; i < frame->fmt->num_planes; i++) ++ vb2_set_plane_payload(vb, i, frame->payload[i]); ++ } ++ ++ return 0; ++} ++ ++static void gsc_m2m_buf_queue(struct vb2_buffer *vb) ++{ ++ struct gsc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ ++ pr_debug("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); ++ ++ if (ctx->m2m_ctx) ++ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); ++} ++ ++static struct vb2_ops gsc_m2m_qops = { ++ .queue_setup = gsc_m2m_queue_setup, ++ .buf_prepare = gsc_m2m_buf_prepare, ++ .buf_queue = gsc_m2m_buf_queue, ++ .wait_prepare = gsc_unlock, ++ .wait_finish = gsc_lock, ++ .stop_streaming = gsc_m2m_stop_streaming, ++ .start_streaming = gsc_m2m_start_streaming, ++}; ++ ++static int gsc_m2m_querycap(struct file *file, void *fh, ++ struct v4l2_capability *cap) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ ++ strlcpy(cap->driver, gsc->pdev->name, sizeof(cap->driver)); ++ strlcpy(cap->card, gsc->pdev->name, sizeof(cap->card)); ++ strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); ++ cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE | ++ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; ++ ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int gsc_m2m_enum_fmt_mplane(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return gsc_enum_fmt_mplane(f); ++} ++ ++static int gsc_m2m_g_fmt_mplane(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ ++ return gsc_g_fmt_mplane(ctx, f); ++} ++ ++static int gsc_m2m_try_fmt_mplane(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ ++ return gsc_try_fmt_mplane(ctx, f); ++} ++ ++static int gsc_m2m_s_fmt_mplane(struct file *file, void *fh, ++ struct v4l2_format *f) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ struct vb2_queue *vq; ++ struct gsc_frame *frame; ++ struct v4l2_pix_format_mplane *pix; ++ int i, ret = 0; ++ ++ ret = gsc_m2m_try_fmt_mplane(file, fh, f); ++ if (ret) ++ return ret; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ ++ if (vb2_is_streaming(vq)) { ++ pr_err("queue (%d) busy", f->type); ++ return -EBUSY; ++ } ++ ++ if (V4L2_TYPE_IS_OUTPUT(f->type)) ++ frame = &ctx->s_frame; ++ else ++ frame = &ctx->d_frame; ++ ++ pix = &f->fmt.pix_mp; ++ frame->fmt = find_fmt(&pix->pixelformat, NULL, 0); ++ frame->colorspace = pix->colorspace; ++ if (!frame->fmt) ++ return -EINVAL; ++ ++ for (i = 0; i < frame->fmt->num_planes; i++) ++ frame->payload[i] = pix->plane_fmt[i].sizeimage; ++ ++ gsc_set_frame_size(frame, pix->width, pix->height); ++ ++ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ++ gsc_ctx_state_lock_set(GSC_PARAMS | GSC_DST_FMT, ctx); ++ else ++ gsc_ctx_state_lock_set(GSC_PARAMS | GSC_SRC_FMT, ctx); ++ ++ pr_debug("f_w: %d, f_h: %d", frame->f_width, frame->f_height); ++ ++ return 0; ++} ++ ++static int gsc_m2m_reqbufs(struct file *file, void *fh, ++ struct v4l2_requestbuffers *reqbufs) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ struct gsc_frame *frame; ++ u32 max_cnt; ++ ++ max_cnt = (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? ++ gsc->variant->in_buf_cnt : gsc->variant->out_buf_cnt; ++ if (reqbufs->count > max_cnt) { ++ return -EINVAL; ++ } else if (reqbufs->count == 0) { ++ if (reqbufs->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ++ gsc_ctx_state_lock_clear(GSC_SRC_FMT, ctx); ++ else ++ gsc_ctx_state_lock_clear(GSC_DST_FMT, ctx); ++ } ++ ++ frame = ctx_get_frame(ctx, reqbufs->type); ++ ++ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); ++} ++ ++static int gsc_m2m_expbuf(struct file *file, void *fh, ++ struct v4l2_exportbuffer *eb) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); ++} ++ ++static int gsc_m2m_querybuf(struct file *file, void *fh, ++ struct v4l2_buffer *buf) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int gsc_m2m_qbuf(struct file *file, void *fh, ++ struct v4l2_buffer *buf) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int gsc_m2m_dqbuf(struct file *file, void *fh, ++ struct v4l2_buffer *buf) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int gsc_m2m_streamon(struct file *file, void *fh, ++ enum v4l2_buf_type type) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ ++ /* The source and target color format need to be set */ ++ if (V4L2_TYPE_IS_OUTPUT(type)) { ++ if (!gsc_ctx_state_is_set(GSC_SRC_FMT, ctx)) ++ return -EINVAL; ++ } else if (!gsc_ctx_state_is_set(GSC_DST_FMT, ctx)) { ++ return -EINVAL; ++ } ++ ++ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); ++} ++ ++static int gsc_m2m_streamoff(struct file *file, void *fh, ++ enum v4l2_buf_type type) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); ++} ++ ++/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ ++static int is_rectangle_enclosed(struct v4l2_rect *a, struct v4l2_rect *b) ++{ ++ if (a->left < b->left || a->top < b->top) ++ return 0; ++ ++ if (a->left + a->width > b->left + b->width) ++ return 0; ++ ++ if (a->top + a->height > b->top + b->height) ++ return 0; ++ ++ return 1; ++} ++ ++static int gsc_m2m_g_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct gsc_frame *frame; ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ ++ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && ++ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) ++ return -EINVAL; ++ ++ frame = ctx_get_frame(ctx, s->type); ++ if (IS_ERR(frame)) ++ return PTR_ERR(frame); ++ ++ switch (s->target) { ++ case V4L2_SEL_TGT_COMPOSE_DEFAULT: ++ case V4L2_SEL_TGT_COMPOSE_BOUNDS: ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ s->r.left = 0; ++ s->r.top = 0; ++ s->r.width = frame->f_width; ++ s->r.height = frame->f_height; ++ return 0; ++ ++ case V4L2_SEL_TGT_COMPOSE: ++ case V4L2_SEL_TGT_CROP: ++ s->r.left = frame->crop.left; ++ s->r.top = frame->crop.top; ++ s->r.width = frame->crop.width; ++ s->r.height = frame->crop.height; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int gsc_m2m_s_selection(struct file *file, void *fh, ++ struct v4l2_selection *s) ++{ ++ struct gsc_frame *frame; ++ struct gsc_ctx *ctx = fh_to_ctx(fh); ++ struct v4l2_crop cr; ++ struct gsc_variant *variant = ctx->gsc_dev->variant; ++ int ret; ++ ++ cr.type = s->type; ++ cr.c = s->r; ++ ++ if ((s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && ++ (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) ++ return -EINVAL; ++ ++ ret = gsc_try_crop(ctx, &cr); ++ if (ret) ++ return ret; ++ ++ if (s->flags & V4L2_SEL_FLAG_LE && ++ !is_rectangle_enclosed(&cr.c, &s->r)) ++ return -ERANGE; ++ ++ if (s->flags & V4L2_SEL_FLAG_GE && ++ !is_rectangle_enclosed(&s->r, &cr.c)) ++ return -ERANGE; ++ ++ s->r = cr.c; ++ ++ switch (s->target) { ++ case V4L2_SEL_TGT_COMPOSE_BOUNDS: ++ case V4L2_SEL_TGT_COMPOSE_DEFAULT: ++ case V4L2_SEL_TGT_COMPOSE: ++ frame = &ctx->s_frame; ++ break; ++ ++ case V4L2_SEL_TGT_CROP_BOUNDS: ++ case V4L2_SEL_TGT_CROP: ++ case V4L2_SEL_TGT_CROP_DEFAULT: ++ frame = &ctx->d_frame; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ /* Check to see if scaling ratio is within supported range */ ++ if (gsc_ctx_state_is_set(GSC_DST_FMT | GSC_SRC_FMT, ctx)) { ++ if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { ++ ret = gsc_check_scaler_ratio(variant, cr.c.width, ++ cr.c.height, ctx->d_frame.crop.width, ++ ctx->d_frame.crop.height, ++ ctx->gsc_ctrls.rotate->val, ctx->out_path); ++ } else { ++ ret = gsc_check_scaler_ratio(variant, ++ ctx->s_frame.crop.width, ++ ctx->s_frame.crop.height, cr.c.width, ++ cr.c.height, ctx->gsc_ctrls.rotate->val, ++ ctx->out_path); ++ } ++ ++ if (ret) { ++ pr_err("Out of scaler range"); ++ return -EINVAL; ++ } ++ } ++ ++ frame->crop = cr.c; ++ ++ gsc_ctx_state_lock_set(GSC_PARAMS, ctx); ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops gsc_m2m_ioctl_ops = { ++ .vidioc_querycap = gsc_m2m_querycap, ++ .vidioc_enum_fmt_vid_cap_mplane = gsc_m2m_enum_fmt_mplane, ++ .vidioc_enum_fmt_vid_out_mplane = gsc_m2m_enum_fmt_mplane, ++ .vidioc_g_fmt_vid_cap_mplane = gsc_m2m_g_fmt_mplane, ++ .vidioc_g_fmt_vid_out_mplane = gsc_m2m_g_fmt_mplane, ++ .vidioc_try_fmt_vid_cap_mplane = gsc_m2m_try_fmt_mplane, ++ .vidioc_try_fmt_vid_out_mplane = gsc_m2m_try_fmt_mplane, ++ .vidioc_s_fmt_vid_cap_mplane = gsc_m2m_s_fmt_mplane, ++ .vidioc_s_fmt_vid_out_mplane = gsc_m2m_s_fmt_mplane, ++ .vidioc_reqbufs = gsc_m2m_reqbufs, ++ .vidioc_expbuf = gsc_m2m_expbuf, ++ .vidioc_querybuf = gsc_m2m_querybuf, ++ .vidioc_qbuf = gsc_m2m_qbuf, ++ .vidioc_dqbuf = gsc_m2m_dqbuf, ++ .vidioc_streamon = gsc_m2m_streamon, ++ .vidioc_streamoff = gsc_m2m_streamoff, ++ .vidioc_g_selection = gsc_m2m_g_selection, ++ .vidioc_s_selection = gsc_m2m_s_selection ++}; ++ ++static int queue_init(void *priv, struct vb2_queue *src_vq, ++ struct vb2_queue *dst_vq) ++{ ++ struct gsc_ctx *ctx = priv; ++ int ret; ++ ++ memset(src_vq, 0, sizeof(*src_vq)); ++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; ++ src_vq->drv_priv = ctx; ++ src_vq->ops = &gsc_m2m_qops; ++ src_vq->mem_ops = &vb2_dma_contig_memops; ++ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ ++ ret = vb2_queue_init(src_vq); ++ if (ret) ++ return ret; ++ ++ memset(dst_vq, 0, sizeof(*dst_vq)); ++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ++ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; ++ dst_vq->drv_priv = ctx; ++ dst_vq->ops = &gsc_m2m_qops; ++ dst_vq->mem_ops = &vb2_dma_contig_memops; ++ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ ++ return vb2_queue_init(dst_vq); ++} ++ ++static int gsc_m2m_open(struct file *file) ++{ ++ struct gsc_dev *gsc = video_drvdata(file); ++ struct gsc_ctx *ctx = NULL; ++ int ret; ++ ++ pr_debug("pid: %d, state: 0x%lx", task_pid_nr(current), gsc->state); ++ ++ if (mutex_lock_interruptible(&gsc->lock)) ++ return -ERESTARTSYS; ++ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) { ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ v4l2_fh_init(&ctx->fh, gsc->m2m.vfd); ++ ret = gsc_ctrls_create(ctx); ++ if (ret) ++ goto error_fh; ++ ++ /* Use separate control handler per file handle */ ++ ctx->fh.ctrl_handler = &ctx->ctrl_handler; ++ file->private_data = &ctx->fh; ++ v4l2_fh_add(&ctx->fh); ++ ++ ctx->gsc_dev = gsc; ++ /* Default color format */ ++ ctx->s_frame.fmt = get_format(0); ++ ctx->d_frame.fmt = get_format(0); ++ /* Setup the device context for mem2mem mode. */ ++ ctx->state = GSC_CTX_M2M; ++ ctx->flags = 0; ++ ctx->in_path = GSC_DMA; ++ ctx->out_path = GSC_DMA; ++ ++ ctx->m2m_ctx = v4l2_m2m_ctx_init(gsc->m2m.m2m_dev, ctx, queue_init); ++ if (IS_ERR(ctx->m2m_ctx)) { ++ pr_err("Failed to initialize m2m context"); ++ ret = PTR_ERR(ctx->m2m_ctx); ++ goto error_ctrls; ++ } ++ ++ if (gsc->m2m.refcnt++ == 0) ++ set_bit(ST_M2M_OPEN, &gsc->state); ++ ++ pr_debug("gsc m2m driver is opened, ctx(0x%p)", ctx); ++ ++ mutex_unlock(&gsc->lock); ++ return 0; ++ ++error_ctrls: ++ gsc_ctrls_delete(ctx); ++error_fh: ++ v4l2_fh_del(&ctx->fh); ++ v4l2_fh_exit(&ctx->fh); ++ kfree(ctx); ++unlock: ++ mutex_unlock(&gsc->lock); ++ return ret; ++} ++ ++static int gsc_m2m_release(struct file *file) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(file->private_data); ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ ++ pr_debug("pid: %d, state: 0x%lx, refcnt= %d", ++ task_pid_nr(current), gsc->state, gsc->m2m.refcnt); ++ ++ mutex_lock(&gsc->lock); ++ ++ v4l2_m2m_ctx_release(ctx->m2m_ctx); ++ gsc_ctrls_delete(ctx); ++ v4l2_fh_del(&ctx->fh); ++ v4l2_fh_exit(&ctx->fh); ++ ++ if (--gsc->m2m.refcnt <= 0) ++ clear_bit(ST_M2M_OPEN, &gsc->state); ++ kfree(ctx); ++ ++ mutex_unlock(&gsc->lock); ++ return 0; ++} ++ ++static unsigned int gsc_m2m_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(file->private_data); ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ int ret; ++ ++ if (mutex_lock_interruptible(&gsc->lock)) ++ return -ERESTARTSYS; ++ ++ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); ++ mutex_unlock(&gsc->lock); ++ ++ return ret; ++} ++ ++static int gsc_m2m_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct gsc_ctx *ctx = fh_to_ctx(file->private_data); ++ struct gsc_dev *gsc = ctx->gsc_dev; ++ int ret; ++ ++ if (mutex_lock_interruptible(&gsc->lock)) ++ return -ERESTARTSYS; ++ ++ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); ++ mutex_unlock(&gsc->lock); ++ ++ return ret; ++} ++ ++static const struct v4l2_file_operations gsc_m2m_fops = { ++ .owner = THIS_MODULE, ++ .open = gsc_m2m_open, ++ .release = gsc_m2m_release, ++ .poll = gsc_m2m_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = gsc_m2m_mmap, ++}; ++ ++static struct v4l2_m2m_ops gsc_m2m_ops = { ++ .device_run = gsc_m2m_device_run, ++ .job_abort = gsc_m2m_job_abort, ++}; ++ ++int gsc_register_m2m_device(struct gsc_dev *gsc) ++{ ++ struct platform_device *pdev; ++ int ret; ++ ++ if (!gsc) ++ return -ENODEV; ++ ++ pdev = gsc->pdev; ++ ++ gsc->vdev.fops = &gsc_m2m_fops; ++ gsc->vdev.ioctl_ops = &gsc_m2m_ioctl_ops; ++ gsc->vdev.release = video_device_release_empty; ++ gsc->vdev.lock = &gsc->lock; ++ gsc->vdev.vfl_dir = VFL_DIR_M2M; ++ snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m", ++ GSC_MODULE_NAME, gsc->id); ++ ++ video_set_drvdata(&gsc->vdev, gsc); ++ ++ gsc->m2m.vfd = &gsc->vdev; ++ gsc->m2m.m2m_dev = v4l2_m2m_init(&gsc_m2m_ops); ++ if (IS_ERR(gsc->m2m.m2m_dev)) { ++ dev_err(&pdev->dev, "failed to initialize v4l2-m2m device\n"); ++ ret = PTR_ERR(gsc->m2m.m2m_dev); ++ goto err_m2m_r1; ++ } ++ ++ ret = video_register_device(&gsc->vdev, VFL_TYPE_GRABBER, -1); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "%s(): failed to register video device\n", __func__); ++ goto err_m2m_r2; ++ } ++ ++ pr_debug("gsc m2m driver registered as /dev/video%d", gsc->vdev.num); ++ return 0; ++ ++err_m2m_r2: ++ v4l2_m2m_release(gsc->m2m.m2m_dev); ++err_m2m_r1: ++ video_device_release(gsc->m2m.vfd); ++ ++ return ret; ++} ++ ++void gsc_unregister_m2m_device(struct gsc_dev *gsc) ++{ ++ if (gsc) ++ v4l2_m2m_release(gsc->m2m.m2m_dev); ++} +diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/exynos-gsc/gsc-regs.c +new file mode 100644 +index 0000000..6f5b5a4 +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/gsc-regs.c +@@ -0,0 +1,431 @@ ++/* ++ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * Samsung EXYNOS5 SoC series G-Scaler driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation, either version 2 of the License, ++ * or (at your option) any later version. ++ */ ++ ++#include ++#include ++#include ++ ++#include "gsc-core.h" ++ ++void gsc_hw_set_sw_reset(struct gsc_dev *dev) ++{ ++ writel(GSC_SW_RESET_SRESET, dev->regs + GSC_SW_RESET); ++} ++ ++int gsc_wait_reset(struct gsc_dev *dev) ++{ ++ unsigned long end = jiffies + msecs_to_jiffies(50); ++ u32 cfg; ++ ++ while (time_before(jiffies, end)) { ++ cfg = readl(dev->regs + GSC_SW_RESET); ++ if (!cfg) ++ return 0; ++ usleep_range(10, 20); ++ } ++ ++ return -EBUSY; ++} ++ ++void gsc_hw_set_frm_done_irq_mask(struct gsc_dev *dev, bool mask) ++{ ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_IRQ); ++ if (mask) ++ cfg |= GSC_IRQ_FRMDONE_MASK; ++ else ++ cfg &= ~GSC_IRQ_FRMDONE_MASK; ++ writel(cfg, dev->regs + GSC_IRQ); ++} ++ ++void gsc_hw_set_gsc_irq_enable(struct gsc_dev *dev, bool mask) ++{ ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_IRQ); ++ if (mask) ++ cfg |= GSC_IRQ_ENABLE; ++ else ++ cfg &= ~GSC_IRQ_ENABLE; ++ writel(cfg, dev->regs + GSC_IRQ); ++} ++ ++void gsc_hw_set_input_buf_masking(struct gsc_dev *dev, u32 shift, ++ bool enable) ++{ ++ u32 cfg = readl(dev->regs + GSC_IN_BASE_ADDR_Y_MASK); ++ u32 mask = 1 << shift; ++ ++ cfg &= ~mask; ++ cfg |= enable << shift; ++ ++ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_Y_MASK); ++ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CB_MASK); ++ writel(cfg, dev->regs + GSC_IN_BASE_ADDR_CR_MASK); ++} ++ ++void gsc_hw_set_output_buf_masking(struct gsc_dev *dev, u32 shift, ++ bool enable) ++{ ++ u32 cfg = readl(dev->regs + GSC_OUT_BASE_ADDR_Y_MASK); ++ u32 mask = 1 << shift; ++ ++ cfg &= ~mask; ++ cfg |= enable << shift; ++ ++ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_Y_MASK); ++ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CB_MASK); ++ writel(cfg, dev->regs + GSC_OUT_BASE_ADDR_CR_MASK); ++} ++ ++void gsc_hw_set_input_addr(struct gsc_dev *dev, struct gsc_addr *addr, ++ int index) ++{ ++ pr_debug("src_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", index, ++ addr->y, addr->cb, addr->cr); ++ writel(addr->y, dev->regs + GSC_IN_BASE_ADDR_Y(index)); ++ writel(addr->cb, dev->regs + GSC_IN_BASE_ADDR_CB(index)); ++ writel(addr->cr, dev->regs + GSC_IN_BASE_ADDR_CR(index)); ++ ++} ++ ++void gsc_hw_set_output_addr(struct gsc_dev *dev, ++ struct gsc_addr *addr, int index) ++{ ++ pr_debug("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", ++ index, addr->y, addr->cb, addr->cr); ++ writel(addr->y, dev->regs + GSC_OUT_BASE_ADDR_Y(index)); ++ writel(addr->cb, dev->regs + GSC_OUT_BASE_ADDR_CB(index)); ++ writel(addr->cr, dev->regs + GSC_OUT_BASE_ADDR_CR(index)); ++} ++ ++void gsc_hw_set_input_path(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ ++ u32 cfg = readl(dev->regs + GSC_IN_CON); ++ cfg &= ~(GSC_IN_PATH_MASK | GSC_IN_LOCAL_SEL_MASK); ++ ++ if (ctx->in_path == GSC_DMA) ++ cfg |= GSC_IN_PATH_MEMORY; ++ ++ writel(cfg, dev->regs + GSC_IN_CON); ++} ++ ++void gsc_hw_set_in_size(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->s_frame; ++ u32 cfg; ++ ++ /* Set input pixel offset */ ++ cfg = GSC_SRCIMG_OFFSET_X(frame->crop.left); ++ cfg |= GSC_SRCIMG_OFFSET_Y(frame->crop.top); ++ writel(cfg, dev->regs + GSC_SRCIMG_OFFSET); ++ ++ /* Set input original size */ ++ cfg = GSC_SRCIMG_WIDTH(frame->f_width); ++ cfg |= GSC_SRCIMG_HEIGHT(frame->f_height); ++ writel(cfg, dev->regs + GSC_SRCIMG_SIZE); ++ ++ /* Set input cropped size */ ++ cfg = GSC_CROPPED_WIDTH(frame->crop.width); ++ cfg |= GSC_CROPPED_HEIGHT(frame->crop.height); ++ writel(cfg, dev->regs + GSC_CROPPED_SIZE); ++} ++ ++void gsc_hw_set_in_image_rgb(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->s_frame; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_IN_CON); ++ if (frame->colorspace == V4L2_COLORSPACE_REC709) ++ cfg |= GSC_IN_RGB_HD_WIDE; ++ else ++ cfg |= GSC_IN_RGB_SD_WIDE; ++ ++ if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X) ++ cfg |= GSC_IN_RGB565; ++ else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32) ++ cfg |= GSC_IN_XRGB8888; ++ ++ writel(cfg, dev->regs + GSC_IN_CON); ++} ++ ++void gsc_hw_set_in_image_format(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->s_frame; ++ u32 i, depth = 0; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_IN_CON); ++ cfg &= ~(GSC_IN_RGB_TYPE_MASK | GSC_IN_YUV422_1P_ORDER_MASK | ++ GSC_IN_CHROMA_ORDER_MASK | GSC_IN_FORMAT_MASK | ++ GSC_IN_TILE_TYPE_MASK | GSC_IN_TILE_MODE); ++ writel(cfg, dev->regs + GSC_IN_CON); ++ ++ if (is_rgb(frame->fmt->color)) { ++ gsc_hw_set_in_image_rgb(ctx); ++ return; ++ } ++ for (i = 0; i < frame->fmt->num_planes; i++) ++ depth += frame->fmt->depth[i]; ++ ++ switch (frame->fmt->num_comp) { ++ case 1: ++ cfg |= GSC_IN_YUV422_1P; ++ if (frame->fmt->yorder == GSC_LSB_Y) ++ cfg |= GSC_IN_YUV422_1P_ORDER_LSB_Y; ++ else ++ cfg |= GSC_IN_YUV422_1P_OEDER_LSB_C; ++ if (frame->fmt->corder == GSC_CBCR) ++ cfg |= GSC_IN_CHROMA_ORDER_CBCR; ++ else ++ cfg |= GSC_IN_CHROMA_ORDER_CRCB; ++ break; ++ case 2: ++ if (depth == 12) ++ cfg |= GSC_IN_YUV420_2P; ++ else ++ cfg |= GSC_IN_YUV422_2P; ++ if (frame->fmt->corder == GSC_CBCR) ++ cfg |= GSC_IN_CHROMA_ORDER_CBCR; ++ else ++ cfg |= GSC_IN_CHROMA_ORDER_CRCB; ++ break; ++ case 3: ++ if (depth == 12) ++ cfg |= GSC_IN_YUV420_3P; ++ else ++ cfg |= GSC_IN_YUV422_3P; ++ break; ++ } ++ ++ if (is_tiled(frame->fmt)) ++ cfg |= GSC_IN_TILE_C_16x8 | GSC_IN_TILE_MODE; ++ ++ writel(cfg, dev->regs + GSC_IN_CON); ++} ++ ++void gsc_hw_set_output_path(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ ++ u32 cfg = readl(dev->regs + GSC_OUT_CON); ++ cfg &= ~GSC_OUT_PATH_MASK; ++ ++ if (ctx->out_path == GSC_DMA) ++ cfg |= GSC_OUT_PATH_MEMORY; ++ else ++ cfg |= GSC_OUT_PATH_LOCAL; ++ ++ writel(cfg, dev->regs + GSC_OUT_CON); ++} ++ ++void gsc_hw_set_out_size(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->d_frame; ++ u32 cfg; ++ ++ /* Set output original size */ ++ if (ctx->out_path == GSC_DMA) { ++ cfg = GSC_DSTIMG_OFFSET_X(frame->crop.left); ++ cfg |= GSC_DSTIMG_OFFSET_Y(frame->crop.top); ++ writel(cfg, dev->regs + GSC_DSTIMG_OFFSET); ++ ++ cfg = GSC_DSTIMG_WIDTH(frame->f_width); ++ cfg |= GSC_DSTIMG_HEIGHT(frame->f_height); ++ writel(cfg, dev->regs + GSC_DSTIMG_SIZE); ++ } ++ ++ /* Set output scaled size */ ++ if (ctx->gsc_ctrls.rotate->val == 90 || ++ ctx->gsc_ctrls.rotate->val == 270) { ++ cfg = GSC_SCALED_WIDTH(frame->crop.height); ++ cfg |= GSC_SCALED_HEIGHT(frame->crop.width); ++ } else { ++ cfg = GSC_SCALED_WIDTH(frame->crop.width); ++ cfg |= GSC_SCALED_HEIGHT(frame->crop.height); ++ } ++ writel(cfg, dev->regs + GSC_SCALED_SIZE); ++} ++ ++void gsc_hw_set_out_image_rgb(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->d_frame; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_OUT_CON); ++ if (frame->colorspace == V4L2_COLORSPACE_REC709) ++ cfg |= GSC_OUT_RGB_HD_WIDE; ++ else ++ cfg |= GSC_OUT_RGB_SD_WIDE; ++ ++ if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB565X) ++ cfg |= GSC_OUT_RGB565; ++ else if (frame->fmt->pixelformat == V4L2_PIX_FMT_RGB32) ++ cfg |= GSC_OUT_XRGB8888; ++ ++ writel(cfg, dev->regs + GSC_OUT_CON); ++} ++ ++void gsc_hw_set_out_image_format(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->d_frame; ++ u32 i, depth = 0; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_OUT_CON); ++ cfg &= ~(GSC_OUT_RGB_TYPE_MASK | GSC_OUT_YUV422_1P_ORDER_MASK | ++ GSC_OUT_CHROMA_ORDER_MASK | GSC_OUT_FORMAT_MASK | ++ GSC_OUT_TILE_TYPE_MASK | GSC_OUT_TILE_MODE); ++ writel(cfg, dev->regs + GSC_OUT_CON); ++ ++ if (is_rgb(frame->fmt->color)) { ++ gsc_hw_set_out_image_rgb(ctx); ++ return; ++ } ++ ++ if (ctx->out_path != GSC_DMA) { ++ cfg |= GSC_OUT_YUV444; ++ goto end_set; ++ } ++ ++ for (i = 0; i < frame->fmt->num_planes; i++) ++ depth += frame->fmt->depth[i]; ++ ++ switch (frame->fmt->num_comp) { ++ case 1: ++ cfg |= GSC_OUT_YUV422_1P; ++ if (frame->fmt->yorder == GSC_LSB_Y) ++ cfg |= GSC_OUT_YUV422_1P_ORDER_LSB_Y; ++ else ++ cfg |= GSC_OUT_YUV422_1P_OEDER_LSB_C; ++ if (frame->fmt->corder == GSC_CBCR) ++ cfg |= GSC_OUT_CHROMA_ORDER_CBCR; ++ else ++ cfg |= GSC_OUT_CHROMA_ORDER_CRCB; ++ break; ++ case 2: ++ if (depth == 12) ++ cfg |= GSC_OUT_YUV420_2P; ++ else ++ cfg |= GSC_OUT_YUV422_2P; ++ if (frame->fmt->corder == GSC_CBCR) ++ cfg |= GSC_OUT_CHROMA_ORDER_CBCR; ++ else ++ cfg |= GSC_OUT_CHROMA_ORDER_CRCB; ++ break; ++ case 3: ++ cfg |= GSC_OUT_YUV420_3P; ++ break; ++ } ++ ++ if (is_tiled(frame->fmt)) ++ cfg |= GSC_OUT_TILE_C_16x8 | GSC_OUT_TILE_MODE; ++ ++end_set: ++ writel(cfg, dev->regs + GSC_OUT_CON); ++} ++ ++void gsc_hw_set_prescaler(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_scaler *sc = &ctx->scaler; ++ u32 cfg; ++ ++ cfg = GSC_PRESC_SHFACTOR(sc->pre_shfactor); ++ cfg |= GSC_PRESC_H_RATIO(sc->pre_hratio); ++ cfg |= GSC_PRESC_V_RATIO(sc->pre_vratio); ++ writel(cfg, dev->regs + GSC_PRE_SCALE_RATIO); ++} ++ ++void gsc_hw_set_mainscaler(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_scaler *sc = &ctx->scaler; ++ u32 cfg; ++ ++ cfg = GSC_MAIN_H_RATIO_VALUE(sc->main_hratio); ++ writel(cfg, dev->regs + GSC_MAIN_H_RATIO); ++ ++ cfg = GSC_MAIN_V_RATIO_VALUE(sc->main_vratio); ++ writel(cfg, dev->regs + GSC_MAIN_V_RATIO); ++} ++ ++void gsc_hw_set_rotation(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_IN_CON); ++ cfg &= ~GSC_IN_ROT_MASK; ++ ++ switch (ctx->gsc_ctrls.rotate->val) { ++ case 270: ++ cfg |= GSC_IN_ROT_270; ++ break; ++ case 180: ++ cfg |= GSC_IN_ROT_180; ++ break; ++ case 90: ++ if (ctx->gsc_ctrls.hflip->val) ++ cfg |= GSC_IN_ROT_90_XFLIP; ++ else if (ctx->gsc_ctrls.vflip->val) ++ cfg |= GSC_IN_ROT_90_YFLIP; ++ else ++ cfg |= GSC_IN_ROT_90; ++ break; ++ case 0: ++ if (ctx->gsc_ctrls.hflip->val) ++ cfg |= GSC_IN_ROT_XFLIP; ++ else if (ctx->gsc_ctrls.vflip->val) ++ cfg |= GSC_IN_ROT_YFLIP; ++ } ++ ++ writel(cfg, dev->regs + GSC_IN_CON); ++} ++ ++void gsc_hw_set_global_alpha(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ struct gsc_frame *frame = &ctx->d_frame; ++ u32 cfg; ++ ++ if (!is_rgb(frame->fmt->color)) { ++ pr_debug("Not a RGB format"); ++ return; ++ } ++ ++ cfg = readl(dev->regs + GSC_OUT_CON); ++ cfg &= ~GSC_OUT_GLOBAL_ALPHA_MASK; ++ ++ cfg |= GSC_OUT_GLOBAL_ALPHA(ctx->gsc_ctrls.global_alpha->val); ++ writel(cfg, dev->regs + GSC_OUT_CON); ++} ++ ++void gsc_hw_set_sfr_update(struct gsc_ctx *ctx) ++{ ++ struct gsc_dev *dev = ctx->gsc_dev; ++ u32 cfg; ++ ++ cfg = readl(dev->regs + GSC_ENABLE); ++ cfg |= GSC_ENABLE_SFR_UPDATE; ++ writel(cfg, dev->regs + GSC_ENABLE); ++} +diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.h b/drivers/media/platform/exynos-gsc/gsc-regs.h +new file mode 100644 +index 0000000..4678f9a +--- /dev/null ++++ b/drivers/media/platform/exynos-gsc/gsc-regs.h +@@ -0,0 +1,172 @@ ++/* ++ * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. ++ * http://www.samsung.com ++ * ++ * Register definition file for Samsung G-Scaler driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef REGS_GSC_H_ ++#define REGS_GSC_H_ ++ ++/* G-Scaler enable */ ++#define GSC_ENABLE 0x00 ++#define GSC_ENABLE_OP_STATUS (1 << 2) ++#define GSC_ENABLE_SFR_UPDATE (1 << 1) ++#define GSC_ENABLE_ON (1 << 0) ++ ++/* G-Scaler S/W reset */ ++#define GSC_SW_RESET 0x04 ++#define GSC_SW_RESET_SRESET (1 << 0) ++ ++/* G-Scaler IRQ */ ++#define GSC_IRQ 0x08 ++#define GSC_IRQ_STATUS_OR_IRQ (1 << 17) ++#define GSC_IRQ_STATUS_FRM_DONE_IRQ (1 << 16) ++#define GSC_IRQ_FRMDONE_MASK (1 << 1) ++#define GSC_IRQ_ENABLE (1 << 0) ++ ++/* G-Scaler input control */ ++#define GSC_IN_CON 0x10 ++#define GSC_IN_ROT_MASK (7 << 16) ++#define GSC_IN_ROT_270 (7 << 16) ++#define GSC_IN_ROT_90_YFLIP (6 << 16) ++#define GSC_IN_ROT_90_XFLIP (5 << 16) ++#define GSC_IN_ROT_90 (4 << 16) ++#define GSC_IN_ROT_180 (3 << 16) ++#define GSC_IN_ROT_YFLIP (2 << 16) ++#define GSC_IN_ROT_XFLIP (1 << 16) ++#define GSC_IN_RGB_TYPE_MASK (3 << 14) ++#define GSC_IN_RGB_HD_NARROW (3 << 14) ++#define GSC_IN_RGB_HD_WIDE (2 << 14) ++#define GSC_IN_RGB_SD_NARROW (1 << 14) ++#define GSC_IN_RGB_SD_WIDE (0 << 14) ++#define GSC_IN_YUV422_1P_ORDER_MASK (1 << 13) ++#define GSC_IN_YUV422_1P_ORDER_LSB_Y (0 << 13) ++#define GSC_IN_YUV422_1P_OEDER_LSB_C (1 << 13) ++#define GSC_IN_CHROMA_ORDER_MASK (1 << 12) ++#define GSC_IN_CHROMA_ORDER_CBCR (0 << 12) ++#define GSC_IN_CHROMA_ORDER_CRCB (1 << 12) ++#define GSC_IN_FORMAT_MASK (7 << 8) ++#define GSC_IN_XRGB8888 (0 << 8) ++#define GSC_IN_RGB565 (1 << 8) ++#define GSC_IN_YUV420_2P (2 << 8) ++#define GSC_IN_YUV420_3P (3 << 8) ++#define GSC_IN_YUV422_1P (4 << 8) ++#define GSC_IN_YUV422_2P (5 << 8) ++#define GSC_IN_YUV422_3P (6 << 8) ++#define GSC_IN_TILE_TYPE_MASK (1 << 4) ++#define GSC_IN_TILE_C_16x8 (0 << 4) ++#define GSC_IN_TILE_MODE (1 << 3) ++#define GSC_IN_LOCAL_SEL_MASK (3 << 1) ++#define GSC_IN_PATH_MASK (1 << 0) ++#define GSC_IN_PATH_MEMORY (0 << 0) ++ ++/* G-Scaler source image size */ ++#define GSC_SRCIMG_SIZE 0x14 ++#define GSC_SRCIMG_HEIGHT(x) ((x) << 16) ++#define GSC_SRCIMG_WIDTH(x) ((x) << 0) ++ ++/* G-Scaler source image offset */ ++#define GSC_SRCIMG_OFFSET 0x18 ++#define GSC_SRCIMG_OFFSET_Y(x) ((x) << 16) ++#define GSC_SRCIMG_OFFSET_X(x) ((x) << 0) ++ ++/* G-Scaler cropped source image size */ ++#define GSC_CROPPED_SIZE 0x1c ++#define GSC_CROPPED_HEIGHT(x) ((x) << 16) ++#define GSC_CROPPED_WIDTH(x) ((x) << 0) ++ ++/* G-Scaler output control */ ++#define GSC_OUT_CON 0x20 ++#define GSC_OUT_GLOBAL_ALPHA_MASK (0xff << 24) ++#define GSC_OUT_GLOBAL_ALPHA(x) ((x) << 24) ++#define GSC_OUT_RGB_TYPE_MASK (3 << 10) ++#define GSC_OUT_RGB_HD_WIDE (3 << 10) ++#define GSC_OUT_RGB_HD_NARROW (2 << 10) ++#define GSC_OUT_RGB_SD_WIDE (1 << 10) ++#define GSC_OUT_RGB_SD_NARROW (0 << 10) ++#define GSC_OUT_YUV422_1P_ORDER_MASK (1 << 9) ++#define GSC_OUT_YUV422_1P_ORDER_LSB_Y (0 << 9) ++#define GSC_OUT_YUV422_1P_OEDER_LSB_C (1 << 9) ++#define GSC_OUT_CHROMA_ORDER_MASK (1 << 8) ++#define GSC_OUT_CHROMA_ORDER_CBCR (0 << 8) ++#define GSC_OUT_CHROMA_ORDER_CRCB (1 << 8) ++#define GSC_OUT_FORMAT_MASK (7 << 4) ++#define GSC_OUT_XRGB8888 (0 << 4) ++#define GSC_OUT_RGB565 (1 << 4) ++#define GSC_OUT_YUV420_2P (2 << 4) ++#define GSC_OUT_YUV420_3P (3 << 4) ++#define GSC_OUT_YUV422_1P (4 << 4) ++#define GSC_OUT_YUV422_2P (5 << 4) ++#define GSC_OUT_YUV444 (7 << 4) ++#define GSC_OUT_TILE_TYPE_MASK (1 << 2) ++#define GSC_OUT_TILE_C_16x8 (0 << 2) ++#define GSC_OUT_TILE_MODE (1 << 1) ++#define GSC_OUT_PATH_MASK (1 << 0) ++#define GSC_OUT_PATH_LOCAL (1 << 0) ++#define GSC_OUT_PATH_MEMORY (0 << 0) ++ ++/* G-Scaler scaled destination image size */ ++#define GSC_SCALED_SIZE 0x24 ++#define GSC_SCALED_HEIGHT(x) ((x) << 16) ++#define GSC_SCALED_WIDTH(x) ((x) << 0) ++ ++/* G-Scaler pre scale ratio */ ++#define GSC_PRE_SCALE_RATIO 0x28 ++#define GSC_PRESC_SHFACTOR(x) ((x) << 28) ++#define GSC_PRESC_V_RATIO(x) ((x) << 16) ++#define GSC_PRESC_H_RATIO(x) ((x) << 0) ++ ++/* G-Scaler main scale horizontal ratio */ ++#define GSC_MAIN_H_RATIO 0x2c ++#define GSC_MAIN_H_RATIO_VALUE(x) ((x) << 0) ++ ++/* G-Scaler main scale vertical ratio */ ++#define GSC_MAIN_V_RATIO 0x30 ++#define GSC_MAIN_V_RATIO_VALUE(x) ((x) << 0) ++ ++/* G-Scaler destination image size */ ++#define GSC_DSTIMG_SIZE 0x40 ++#define GSC_DSTIMG_HEIGHT(x) ((x) << 16) ++#define GSC_DSTIMG_WIDTH(x) ((x) << 0) ++ ++/* G-Scaler destination image offset */ ++#define GSC_DSTIMG_OFFSET 0x44 ++#define GSC_DSTIMG_OFFSET_Y(x) ((x) << 16) ++#define GSC_DSTIMG_OFFSET_X(x) ((x) << 0) ++ ++/* G-Scaler input y address mask */ ++#define GSC_IN_BASE_ADDR_Y_MASK 0x4c ++/* G-Scaler input y base address */ ++#define GSC_IN_BASE_ADDR_Y(n) (0x50 + (n) * 0x4) ++ ++/* G-Scaler input cb address mask */ ++#define GSC_IN_BASE_ADDR_CB_MASK 0x7c ++/* G-Scaler input cb base address */ ++#define GSC_IN_BASE_ADDR_CB(n) (0x80 + (n) * 0x4) ++ ++/* G-Scaler input cr address mask */ ++#define GSC_IN_BASE_ADDR_CR_MASK 0xac ++/* G-Scaler input cr base address */ ++#define GSC_IN_BASE_ADDR_CR(n) (0xb0 + (n) * 0x4) ++ ++/* G-Scaler output y address mask */ ++#define GSC_OUT_BASE_ADDR_Y_MASK 0x10c ++/* G-Scaler output y base address */ ++#define GSC_OUT_BASE_ADDR_Y(n) (0x110 + (n) * 0x4) ++ ++/* G-Scaler output cb address mask */ ++#define GSC_OUT_BASE_ADDR_CB_MASK 0x15c ++/* G-Scaler output cb base address */ ++#define GSC_OUT_BASE_ADDR_CB(n) (0x160 + (n) * 0x4) ++ ++/* G-Scaler output cr address mask */ ++#define GSC_OUT_BASE_ADDR_CR_MASK 0x1ac ++/* G-Scaler output cr base address */ ++#define GSC_OUT_BASE_ADDR_CR(n) (0x1b0 + (n) * 0x4) ++ ++#endif /* REGS_GSC_H_ */ +diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c +new file mode 100644 +index 0000000..d464509 +--- /dev/null ++++ b/drivers/media/platform/fsl-viu.c +@@ -0,0 +1,1688 @@ ++/* ++ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Freescale VIU video driver ++ * ++ * Authors: Hongjun Chen ++ * Porting to 2.6.35 by DENX Software Engineering, ++ * Anatolij Gustschin ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRV_NAME "fsl_viu" ++#define VIU_VERSION "0.5.1" ++ ++#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ ++ ++#define VIU_VID_MEM_LIMIT 4 /* Video memory limit, in Mb */ ++ ++/* I2C address of video decoder chip is 0x4A */ ++#define VIU_VIDEO_DECODER_ADDR 0x25 ++ ++/* supported controls */ ++static struct v4l2_queryctrl viu_qctrl[] = { ++ { ++ .id = V4L2_CID_BRIGHTNESS, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Brightness", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 1, ++ .default_value = 127, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_CONTRAST, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Contrast", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 0x1, ++ .default_value = 0x10, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_SATURATION, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Saturation", ++ .minimum = 0, ++ .maximum = 255, ++ .step = 0x1, ++ .default_value = 127, ++ .flags = 0, ++ }, { ++ .id = V4L2_CID_HUE, ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .name = "Hue", ++ .minimum = -128, ++ .maximum = 127, ++ .step = 0x1, ++ .default_value = 0, ++ .flags = 0, ++ } ++}; ++ ++static int qctl_regs[ARRAY_SIZE(viu_qctrl)]; ++ ++static int info_level; ++ ++#define dprintk(level, fmt, arg...) \ ++ do { \ ++ if (level <= info_level) \ ++ printk(KERN_DEBUG "viu: " fmt , ## arg); \ ++ } while (0) ++ ++/* ++ * Basic structures ++ */ ++struct viu_fmt { ++ char name[32]; ++ u32 fourcc; /* v4l2 format id */ ++ u32 pixelformat; ++ int depth; ++}; ++ ++static struct viu_fmt formats[] = { ++ { ++ .name = "RGB-16 (5/B-6/G-5/R)", ++ .fourcc = V4L2_PIX_FMT_RGB565, ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ .depth = 16, ++ }, { ++ .name = "RGB-32 (A-R-G-B)", ++ .fourcc = V4L2_PIX_FMT_RGB32, ++ .pixelformat = V4L2_PIX_FMT_RGB32, ++ .depth = 32, ++ } ++}; ++ ++struct viu_dev; ++struct viu_buf; ++ ++/* buffer for one video frame */ ++struct viu_buf { ++ /* common v4l buffer stuff -- must be first */ ++ struct videobuf_buffer vb; ++ struct viu_fmt *fmt; ++}; ++ ++struct viu_dmaqueue { ++ struct viu_dev *dev; ++ struct list_head active; ++ struct list_head queued; ++ struct timer_list timeout; ++}; ++ ++struct viu_status { ++ u32 field_irq; ++ u32 vsync_irq; ++ u32 hsync_irq; ++ u32 vstart_irq; ++ u32 dma_end_irq; ++ u32 error_irq; ++}; ++ ++struct viu_reg { ++ u32 status_cfg; ++ u32 luminance; ++ u32 chroma_r; ++ u32 chroma_g; ++ u32 chroma_b; ++ u32 field_base_addr; ++ u32 dma_inc; ++ u32 picture_count; ++ u32 req_alarm; ++ u32 alpha; ++} __attribute__ ((packed)); ++ ++struct viu_dev { ++ struct v4l2_device v4l2_dev; ++ struct mutex lock; ++ spinlock_t slock; ++ int users; ++ ++ struct device *dev; ++ /* various device info */ ++ struct video_device *vdev; ++ struct viu_dmaqueue vidq; ++ enum v4l2_field capfield; ++ int field; ++ int first; ++ int dma_done; ++ ++ /* Hardware register area */ ++ struct viu_reg *vr; ++ ++ /* Interrupt vector */ ++ int irq; ++ struct viu_status irqs; ++ ++ /* video overlay */ ++ struct v4l2_framebuffer ovbuf; ++ struct viu_fmt *ovfmt; ++ unsigned int ovenable; ++ enum v4l2_field ovfield; ++ ++ /* crop */ ++ struct v4l2_rect crop_current; ++ ++ /* clock pointer */ ++ struct clk *clk; ++ ++ /* decoder */ ++ struct v4l2_subdev *decoder; ++ ++ v4l2_std_id std; ++}; ++ ++struct viu_fh { ++ struct viu_dev *dev; ++ ++ /* video capture */ ++ struct videobuf_queue vb_vidq; ++ spinlock_t vbq_lock; /* spinlock for the videobuf queue */ ++ ++ /* video overlay */ ++ struct v4l2_window win; ++ struct v4l2_clip clips[1]; ++ ++ /* video capture */ ++ struct viu_fmt *fmt; ++ int width, height, sizeimage; ++ enum v4l2_buf_type type; ++}; ++ ++static struct viu_reg reg_val; ++ ++/* ++ * Macro definitions of VIU registers ++ */ ++ ++/* STATUS_CONFIG register */ ++enum status_config { ++ SOFT_RST = 1 << 0, ++ ++ ERR_MASK = 0x0f << 4, /* Error code mask */ ++ ERR_NO = 0x00, /* No error */ ++ ERR_DMA_V = 0x01 << 4, /* DMA in vertical active */ ++ ERR_DMA_VB = 0x02 << 4, /* DMA in vertical blanking */ ++ ERR_LINE_TOO_LONG = 0x04 << 4, /* Line too long */ ++ ERR_TOO_MANG_LINES = 0x05 << 4, /* Too many lines in field */ ++ ERR_LINE_TOO_SHORT = 0x06 << 4, /* Line too short */ ++ ERR_NOT_ENOUGH_LINE = 0x07 << 4, /* Not enough lines in field */ ++ ERR_FIFO_OVERFLOW = 0x08 << 4, /* FIFO overflow */ ++ ERR_FIFO_UNDERFLOW = 0x09 << 4, /* FIFO underflow */ ++ ERR_1bit_ECC = 0x0a << 4, /* One bit ECC error */ ++ ERR_MORE_ECC = 0x0b << 4, /* Two/more bits ECC error */ ++ ++ INT_FIELD_EN = 0x01 << 8, /* Enable field interrupt */ ++ INT_VSYNC_EN = 0x01 << 9, /* Enable vsync interrupt */ ++ INT_HSYNC_EN = 0x01 << 10, /* Enable hsync interrupt */ ++ INT_VSTART_EN = 0x01 << 11, /* Enable vstart interrupt */ ++ INT_DMA_END_EN = 0x01 << 12, /* Enable DMA end interrupt */ ++ INT_ERROR_EN = 0x01 << 13, /* Enable error interrupt */ ++ INT_ECC_EN = 0x01 << 14, /* Enable ECC interrupt */ ++ ++ INT_FIELD_STATUS = 0x01 << 16, /* field interrupt status */ ++ INT_VSYNC_STATUS = 0x01 << 17, /* vsync interrupt status */ ++ INT_HSYNC_STATUS = 0x01 << 18, /* hsync interrupt status */ ++ INT_VSTART_STATUS = 0x01 << 19, /* vstart interrupt status */ ++ INT_DMA_END_STATUS = 0x01 << 20, /* DMA end interrupt status */ ++ INT_ERROR_STATUS = 0x01 << 21, /* error interrupt status */ ++ ++ DMA_ACT = 0x01 << 27, /* Enable DMA transfer */ ++ FIELD_NO = 0x01 << 28, /* Field number */ ++ DITHER_ON = 0x01 << 29, /* Dithering is on */ ++ ROUND_ON = 0x01 << 30, /* Round is on */ ++ MODE_32BIT = 0x01 << 31, /* Data in RGBa888, ++ * 0 in RGB565 ++ */ ++}; ++ ++#define norm_maxw() 720 ++#define norm_maxh() 576 ++ ++#define INT_ALL_STATUS (INT_FIELD_STATUS | INT_VSYNC_STATUS | \ ++ INT_HSYNC_STATUS | INT_VSTART_STATUS | \ ++ INT_DMA_END_STATUS | INT_ERROR_STATUS) ++ ++#define NUM_FORMATS ARRAY_SIZE(formats) ++ ++static irqreturn_t viu_intr(int irq, void *dev_id); ++ ++struct viu_fmt *format_by_fourcc(int fourcc) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_FORMATS; i++) { ++ if (formats[i].pixelformat == fourcc) ++ return formats + i; ++ } ++ ++ dprintk(0, "unknown pixelformat:'%4.4s'\n", (char *)&fourcc); ++ return NULL; ++} ++ ++void viu_start_dma(struct viu_dev *dev) ++{ ++ struct viu_reg *vr = dev->vr; ++ ++ dev->field = 0; ++ ++ /* Enable DMA operation */ ++ out_be32(&vr->status_cfg, SOFT_RST); ++ out_be32(&vr->status_cfg, INT_FIELD_EN); ++} ++ ++void viu_stop_dma(struct viu_dev *dev) ++{ ++ struct viu_reg *vr = dev->vr; ++ int cnt = 100; ++ u32 status_cfg; ++ ++ out_be32(&vr->status_cfg, 0); ++ ++ /* Clear pending interrupts */ ++ status_cfg = in_be32(&vr->status_cfg); ++ if (status_cfg & 0x3f0000) ++ out_be32(&vr->status_cfg, status_cfg & 0x3f0000); ++ ++ if (status_cfg & DMA_ACT) { ++ do { ++ status_cfg = in_be32(&vr->status_cfg); ++ if (status_cfg & INT_DMA_END_STATUS) ++ break; ++ } while (cnt--); ++ ++ if (cnt < 0) { ++ /* timed out, issue soft reset */ ++ out_be32(&vr->status_cfg, SOFT_RST); ++ out_be32(&vr->status_cfg, 0); ++ } else { ++ /* clear DMA_END and other pending irqs */ ++ out_be32(&vr->status_cfg, status_cfg & 0x3f0000); ++ } ++ } ++ ++ dev->field = 0; ++} ++ ++static int restart_video_queue(struct viu_dmaqueue *vidq) ++{ ++ struct viu_buf *buf, *prev; ++ ++ dprintk(1, "%s vidq=0x%08lx\n", __func__, (unsigned long)vidq); ++ if (!list_empty(&vidq->active)) { ++ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); ++ dprintk(2, "restart_queue [%p/%d]: restart dma\n", ++ buf, buf->vb.i); ++ ++ viu_stop_dma(vidq->dev); ++ ++ /* cancel all outstanding capture requests */ ++ list_for_each_entry_safe(buf, prev, &vidq->active, vb.queue) { ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ } ++ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); ++ return 0; ++ } ++ ++ prev = NULL; ++ for (;;) { ++ if (list_empty(&vidq->queued)) ++ return 0; ++ buf = list_entry(vidq->queued.next, struct viu_buf, vb.queue); ++ if (prev == NULL) { ++ list_move_tail(&buf->vb.queue, &vidq->active); ++ ++ dprintk(1, "Restarting video dma\n"); ++ viu_stop_dma(vidq->dev); ++ viu_start_dma(vidq->dev); ++ ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2, "[%p/%d] restart_queue - first active\n", ++ buf, buf->vb.i); ++ ++ } else if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_move_tail(&buf->vb.queue, &vidq->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ dprintk(2, "[%p/%d] restart_queue - move to active\n", ++ buf, buf->vb.i); ++ } else { ++ return 0; ++ } ++ prev = buf; ++ } ++} ++ ++static void viu_vid_timeout(unsigned long data) ++{ ++ struct viu_dev *dev = (struct viu_dev *)data; ++ struct viu_buf *buf; ++ struct viu_dmaqueue *vidq = &dev->vidq; ++ ++ while (!list_empty(&vidq->active)) { ++ buf = list_entry(vidq->active.next, struct viu_buf, vb.queue); ++ list_del(&buf->vb.queue); ++ buf->vb.state = VIDEOBUF_ERROR; ++ wake_up(&buf->vb.done); ++ dprintk(1, "viu/0: [%p/%d] timeout\n", buf, buf->vb.i); ++ } ++ ++ restart_video_queue(vidq); ++} ++ ++/* ++ * Videobuf operations ++ */ ++static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, ++ unsigned int *size) ++{ ++ struct viu_fh *fh = vq->priv_data; ++ ++ *size = fh->width * fh->height * fh->fmt->depth >> 3; ++ if (*count == 0) ++ *count = 32; ++ ++ while (*size * *count > VIU_VID_MEM_LIMIT * 1024 * 1024) ++ (*count)--; ++ ++ dprintk(1, "%s, count=%d, size=%d\n", __func__, *count, *size); ++ return 0; ++} ++ ++static void free_buffer(struct videobuf_queue *vq, struct viu_buf *buf) ++{ ++ struct videobuf_buffer *vb = &buf->vb; ++ void *vaddr = NULL; ++ ++ BUG_ON(in_interrupt()); ++ ++ videobuf_waiton(vq, &buf->vb, 0, 0); ++ ++ if (vq->int_ops && vq->int_ops->vaddr) ++ vaddr = vq->int_ops->vaddr(vb); ++ ++ if (vaddr) ++ videobuf_dma_contig_free(vq, &buf->vb); ++ ++ buf->vb.state = VIDEOBUF_NEEDS_INIT; ++} ++ ++inline int buffer_activate(struct viu_dev *dev, struct viu_buf *buf) ++{ ++ struct viu_reg *vr = dev->vr; ++ int bpp; ++ ++ /* setup the DMA base address */ ++ reg_val.field_base_addr = videobuf_to_dma_contig(&buf->vb); ++ ++ dprintk(1, "buffer_activate [%p/%d]: dma addr 0x%lx\n", ++ buf, buf->vb.i, (unsigned long)reg_val.field_base_addr); ++ ++ /* interlace is on by default, set horizontal DMA increment */ ++ reg_val.status_cfg = 0; ++ bpp = buf->fmt->depth >> 3; ++ switch (bpp) { ++ case 2: ++ reg_val.status_cfg &= ~MODE_32BIT; ++ reg_val.dma_inc = buf->vb.width * 2; ++ break; ++ case 4: ++ reg_val.status_cfg |= MODE_32BIT; ++ reg_val.dma_inc = buf->vb.width * 4; ++ break; ++ default: ++ dprintk(0, "doesn't support color depth(%d)\n", ++ bpp * 8); ++ return -EINVAL; ++ } ++ ++ /* setup picture_count register */ ++ reg_val.picture_count = (buf->vb.height / 2) << 16 | ++ buf->vb.width; ++ ++ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; ++ ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ dev->capfield = buf->vb.field; ++ ++ /* reset dma increment if needed */ ++ if (!V4L2_FIELD_HAS_BOTH(buf->vb.field)) ++ reg_val.dma_inc = 0; ++ ++ out_be32(&vr->dma_inc, reg_val.dma_inc); ++ out_be32(&vr->picture_count, reg_val.picture_count); ++ out_be32(&vr->field_base_addr, reg_val.field_base_addr); ++ mod_timer(&dev->vidq.timeout, jiffies + BUFFER_TIMEOUT); ++ return 0; ++} ++ ++static int buffer_prepare(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb, ++ enum v4l2_field field) ++{ ++ struct viu_fh *fh = vq->priv_data; ++ struct viu_buf *buf = container_of(vb, struct viu_buf, vb); ++ int rc; ++ ++ BUG_ON(fh->fmt == NULL); ++ ++ if (fh->width < 48 || fh->width > norm_maxw() || ++ fh->height < 32 || fh->height > norm_maxh()) ++ return -EINVAL; ++ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; ++ if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) ++ return -EINVAL; ++ ++ if (buf->fmt != fh->fmt || ++ buf->vb.width != fh->width || ++ buf->vb.height != fh->height || ++ buf->vb.field != field) { ++ buf->fmt = fh->fmt; ++ buf->vb.width = fh->width; ++ buf->vb.height = fh->height; ++ buf->vb.field = field; ++ } ++ ++ if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { ++ rc = videobuf_iolock(vq, &buf->vb, NULL); ++ if (rc != 0) ++ goto fail; ++ ++ buf->vb.width = fh->width; ++ buf->vb.height = fh->height; ++ buf->vb.field = field; ++ buf->fmt = fh->fmt; ++ } ++ ++ buf->vb.state = VIDEOBUF_PREPARED; ++ return 0; ++ ++fail: ++ free_buffer(vq, buf); ++ return rc; ++} ++ ++static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) ++{ ++ struct viu_buf *buf = container_of(vb, struct viu_buf, vb); ++ struct viu_fh *fh = vq->priv_data; ++ struct viu_dev *dev = fh->dev; ++ struct viu_dmaqueue *vidq = &dev->vidq; ++ struct viu_buf *prev; ++ ++ if (!list_empty(&vidq->queued)) { ++ dprintk(1, "adding vb queue=0x%08lx\n", ++ (unsigned long)&buf->vb.queue); ++ dprintk(1, "vidq pointer 0x%p, queued 0x%p\n", ++ vidq, &vidq->queued); ++ dprintk(1, "dev %p, queued: self %p, next %p, head %p\n", ++ dev, &vidq->queued, vidq->queued.next, ++ vidq->queued.prev); ++ list_add_tail(&buf->vb.queue, &vidq->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - append to queued\n", ++ buf, buf->vb.i); ++ } else if (list_empty(&vidq->active)) { ++ dprintk(1, "adding vb active=0x%08lx\n", ++ (unsigned long)&buf->vb.queue); ++ list_add_tail(&buf->vb.queue, &vidq->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); ++ dprintk(2, "[%p/%d] buffer_queue - first active\n", ++ buf, buf->vb.i); ++ ++ buffer_activate(dev, buf); ++ } else { ++ dprintk(1, "adding vb queue2=0x%08lx\n", ++ (unsigned long)&buf->vb.queue); ++ prev = list_entry(vidq->active.prev, struct viu_buf, vb.queue); ++ if (prev->vb.width == buf->vb.width && ++ prev->vb.height == buf->vb.height && ++ prev->fmt == buf->fmt) { ++ list_add_tail(&buf->vb.queue, &vidq->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ dprintk(2, "[%p/%d] buffer_queue - append to active\n", ++ buf, buf->vb.i); ++ } else { ++ list_add_tail(&buf->vb.queue, &vidq->queued); ++ buf->vb.state = VIDEOBUF_QUEUED; ++ dprintk(2, "[%p/%d] buffer_queue - first queued\n", ++ buf, buf->vb.i); ++ } ++ } ++} ++ ++static void buffer_release(struct videobuf_queue *vq, ++ struct videobuf_buffer *vb) ++{ ++ struct viu_buf *buf = container_of(vb, struct viu_buf, vb); ++ struct viu_fh *fh = vq->priv_data; ++ struct viu_dev *dev = (struct viu_dev *)fh->dev; ++ ++ viu_stop_dma(dev); ++ free_buffer(vq, buf); ++} ++ ++static struct videobuf_queue_ops viu_video_qops = { ++ .buf_setup = buffer_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .buf_release = buffer_release, ++}; ++ ++/* ++ * IOCTL vidioc handling ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strcpy(cap->driver, "viu"); ++ strcpy(cap->card, "viu"); ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_STREAMING | ++ V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_READWRITE; ++ return 0; ++} ++ ++static int vidioc_enum_fmt(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ int index = f->index; ++ ++ if (f->index > NUM_FORMATS) ++ return -EINVAL; ++ ++ strlcpy(f->description, formats[index].name, sizeof(f->description)); ++ f->pixelformat = formats[index].fourcc; ++ return 0; ++} ++ ++static int vidioc_g_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct viu_fh *fh = priv; ++ ++ f->fmt.pix.width = fh->width; ++ f->fmt.pix.height = fh->height; ++ f->fmt.pix.field = fh->vb_vidq.field; ++ f->fmt.pix.pixelformat = fh->fmt->pixelformat; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fh->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = fh->sizeimage; ++ return 0; ++} ++ ++static int vidioc_try_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct viu_fmt *fmt; ++ enum v4l2_field field; ++ unsigned int maxw, maxh; ++ ++ fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ if (!fmt) { ++ dprintk(1, "Fourcc format (0x%08x) invalid.", ++ f->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ ++ field = f->fmt.pix.field; ++ ++ if (field == V4L2_FIELD_ANY) { ++ field = V4L2_FIELD_INTERLACED; ++ } else if (field != V4L2_FIELD_INTERLACED) { ++ dprintk(1, "Field type invalid.\n"); ++ return -EINVAL; ++ } ++ ++ maxw = norm_maxw(); ++ maxh = norm_maxh(); ++ ++ f->fmt.pix.field = field; ++ if (f->fmt.pix.height < 32) ++ f->fmt.pix.height = 32; ++ if (f->fmt.pix.height > maxh) ++ f->fmt.pix.height = maxh; ++ if (f->fmt.pix.width < 48) ++ f->fmt.pix.width = 48; ++ if (f->fmt.pix.width > maxw) ++ f->fmt.pix.width = maxw; ++ f->fmt.pix.width &= ~0x03; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * fmt->depth) >> 3; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct viu_fh *fh = priv; ++ int ret; ++ ++ ret = vidioc_try_fmt_cap(file, fh, f); ++ if (ret < 0) ++ return ret; ++ ++ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); ++ fh->width = f->fmt.pix.width; ++ fh->height = f->fmt.pix.height; ++ fh->sizeimage = f->fmt.pix.sizeimage; ++ fh->vb_vidq.field = f->fmt.pix.field; ++ fh->type = f->type; ++ dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name); ++ return 0; ++} ++ ++static int vidioc_g_fmt_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct viu_fh *fh = priv; ++ ++ f->fmt.win = fh->win; ++ return 0; ++} ++ ++static int verify_preview(struct viu_dev *dev, struct v4l2_window *win) ++{ ++ enum v4l2_field field; ++ int maxw, maxh; ++ ++ if (dev->ovbuf.base == NULL) ++ return -EINVAL; ++ if (dev->ovfmt == NULL) ++ return -EINVAL; ++ if (win->w.width < 48 || win->w.height < 32) ++ return -EINVAL; ++ ++ field = win->field; ++ maxw = dev->crop_current.width; ++ maxh = dev->crop_current.height; ++ ++ if (field == V4L2_FIELD_ANY) { ++ field = (win->w.height > maxh/2) ++ ? V4L2_FIELD_INTERLACED ++ : V4L2_FIELD_TOP; ++ } ++ switch (field) { ++ case V4L2_FIELD_TOP: ++ case V4L2_FIELD_BOTTOM: ++ maxh = maxh / 2; ++ break; ++ case V4L2_FIELD_INTERLACED: ++ break; ++ default: ++ 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; ++} ++ ++inline void viu_activate_overlay(struct viu_reg *viu_reg) ++{ ++ struct viu_reg *vr = viu_reg; ++ ++ out_be32(&vr->field_base_addr, reg_val.field_base_addr); ++ out_be32(&vr->dma_inc, reg_val.dma_inc); ++ out_be32(&vr->picture_count, reg_val.picture_count); ++} ++ ++static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh) ++{ ++ int bpp; ++ ++ dprintk(1, "%s %dx%d %s\n", __func__, ++ fh->win.w.width, fh->win.w.height, dev->ovfmt->name); ++ ++ reg_val.status_cfg = 0; ++ ++ /* setup window */ ++ reg_val.picture_count = (fh->win.w.height / 2) << 16 | ++ fh->win.w.width; ++ ++ /* setup color depth and dma increment */ ++ bpp = dev->ovfmt->depth / 8; ++ switch (bpp) { ++ case 2: ++ reg_val.status_cfg &= ~MODE_32BIT; ++ reg_val.dma_inc = fh->win.w.width * 2; ++ break; ++ case 4: ++ reg_val.status_cfg |= MODE_32BIT; ++ reg_val.dma_inc = fh->win.w.width * 4; ++ break; ++ default: ++ dprintk(0, "device doesn't support color depth(%d)\n", ++ bpp * 8); ++ return -EINVAL; ++ } ++ ++ dev->ovfield = fh->win.field; ++ if (!V4L2_FIELD_HAS_BOTH(dev->ovfield)) ++ reg_val.dma_inc = 0; ++ ++ reg_val.status_cfg |= DMA_ACT | INT_DMA_END_EN | INT_FIELD_EN; ++ ++ /* setup the base address of the overlay buffer */ ++ reg_val.field_base_addr = (u32)dev->ovbuf.base; ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct viu_fh *fh = priv; ++ struct viu_dev *dev = (struct viu_dev *)fh->dev; ++ unsigned long flags; ++ int err; ++ ++ err = verify_preview(dev, &f->fmt.win); ++ if (err) ++ return err; ++ ++ fh->win = f->fmt.win; ++ ++ spin_lock_irqsave(&dev->slock, flags); ++ viu_setup_preview(dev, fh); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ return 0; ++} ++ ++static int vidioc_try_fmt_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return 0; ++} ++ ++static int vidioc_overlay(struct file *file, void *priv, unsigned int on) ++{ ++ struct viu_fh *fh = priv; ++ struct viu_dev *dev = (struct viu_dev *)fh->dev; ++ unsigned long flags; ++ ++ if (on) { ++ spin_lock_irqsave(&dev->slock, flags); ++ viu_activate_overlay(dev->vr); ++ dev->ovenable = 1; ++ ++ /* start dma */ ++ viu_start_dma(dev); ++ spin_unlock_irqrestore(&dev->slock, flags); ++ } else { ++ viu_stop_dma(dev); ++ dev->ovenable = 0; ++ } ++ ++ return 0; ++} ++ ++int vidioc_g_fbuf(struct file *file, void *priv, struct v4l2_framebuffer *arg) ++{ ++ struct viu_fh *fh = priv; ++ struct viu_dev *dev = fh->dev; ++ struct v4l2_framebuffer *fb = arg; ++ ++ *fb = dev->ovbuf; ++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; ++ return 0; ++} ++ ++int vidioc_s_fbuf(struct file *file, void *priv, const struct v4l2_framebuffer *arg) ++{ ++ struct viu_fh *fh = priv; ++ struct viu_dev *dev = fh->dev; ++ const struct v4l2_framebuffer *fb = arg; ++ struct viu_fmt *fmt; ++ ++ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) ++ return -EPERM; ++ ++ /* check args */ ++ fmt = format_by_fourcc(fb->fmt.pixelformat); ++ if (fmt == NULL) ++ return -EINVAL; ++ ++ /* ok, accept it */ ++ dev->ovbuf = *fb; ++ dev->ovfmt = fmt; ++ if (dev->ovbuf.fmt.bytesperline == 0) { ++ dev->ovbuf.fmt.bytesperline = ++ dev->ovbuf.fmt.width * fmt->depth / 8; ++ } ++ return 0; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *p) ++{ ++ struct viu_fh *fh = priv; ++ ++ return videobuf_reqbufs(&fh->vb_vidq, p); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *p) ++{ ++ struct viu_fh *fh = priv; ++ ++ return videobuf_querybuf(&fh->vb_vidq, p); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct viu_fh *fh = priv; ++ ++ return videobuf_qbuf(&fh->vb_vidq, p); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) ++{ ++ struct viu_fh *fh = priv; ++ ++ return videobuf_dqbuf(&fh->vb_vidq, p, ++ file->f_flags & O_NONBLOCK); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct viu_fh *fh = priv; ++ struct viu_dev *dev = fh->dev; ++ ++ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (fh->type != i) ++ return -EINVAL; ++ ++ if (dev->ovenable) ++ dev->ovenable = 0; ++ ++ viu_start_dma(fh->dev); ++ ++ return videobuf_streamon(&fh->vb_vidq); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) ++{ ++ struct viu_fh *fh = priv; ++ ++ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ if (fh->type != i) ++ return -EINVAL; ++ ++ viu_stop_dma(fh->dev); ++ ++ return videobuf_streamoff(&fh->vb_vidq); ++} ++ ++#define decoder_call(viu, o, f, args...) \ ++ v4l2_subdev_call(viu->decoder, o, f, ##args) ++ ++static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct viu_fh *fh = priv; ++ ++ decoder_call(fh->dev, video, querystd, std_id); ++ return 0; ++} ++ ++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) ++{ ++ struct viu_fh *fh = priv; ++ ++ fh->dev->std = *id; ++ decoder_call(fh->dev, core, s_std, *id); ++ return 0; ++} ++ ++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std_id) ++{ ++ struct viu_fh *fh = priv; ++ ++ *std_id = fh->dev->std; ++ return 0; ++} ++ ++/* only one input in this driver */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ struct viu_fh *fh = priv; ++ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ inp->std = fh->dev->vdev->tvnorms; ++ strcpy(inp->name, "Camera"); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ struct viu_fh *fh = priv; ++ ++ if (i > 1) ++ return -EINVAL; ++ ++ decoder_call(fh->dev, video, s_routing, i, 0, 0); ++ return 0; ++} ++ ++/* Controls */ ++static int vidioc_queryctrl(struct file *file, void *priv, ++ struct v4l2_queryctrl *qc) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { ++ if (qc->id && qc->id == viu_qctrl[i].id) { ++ memcpy(qc, &(viu_qctrl[i]), sizeof(*qc)); ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++static int vidioc_g_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { ++ if (ctrl->id == viu_qctrl[i].id) { ++ ctrl->value = qctl_regs[i]; ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++static int vidioc_s_ctrl(struct file *file, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) { ++ if (ctrl->id == viu_qctrl[i].id) { ++ if (ctrl->value < viu_qctrl[i].minimum ++ || ctrl->value > viu_qctrl[i].maximum) ++ return -ERANGE; ++ qctl_regs[i] = ctrl->value; ++ return 0; ++ } ++ } ++ return -EINVAL; ++} ++ ++inline void viu_activate_next_buf(struct viu_dev *dev, ++ struct viu_dmaqueue *viuq) ++{ ++ struct viu_dmaqueue *vidq = viuq; ++ struct viu_buf *buf; ++ ++ /* launch another DMA operation for an active/queued buffer */ ++ if (!list_empty(&vidq->active)) { ++ buf = list_entry(vidq->active.next, struct viu_buf, ++ vb.queue); ++ dprintk(1, "start another queued buffer: 0x%p\n", buf); ++ buffer_activate(dev, buf); ++ } else if (!list_empty(&vidq->queued)) { ++ buf = list_entry(vidq->queued.next, struct viu_buf, ++ vb.queue); ++ list_del(&buf->vb.queue); ++ ++ dprintk(1, "start another queued buffer: 0x%p\n", buf); ++ list_add_tail(&buf->vb.queue, &vidq->active); ++ buf->vb.state = VIDEOBUF_ACTIVE; ++ buffer_activate(dev, buf); ++ } ++} ++ ++inline void viu_default_settings(struct viu_reg *viu_reg) ++{ ++ struct viu_reg *vr = viu_reg; ++ ++ out_be32(&vr->luminance, 0x9512A254); ++ out_be32(&vr->chroma_r, 0x03310000); ++ out_be32(&vr->chroma_g, 0x06600F38); ++ out_be32(&vr->chroma_b, 0x00000409); ++ out_be32(&vr->alpha, 0x000000ff); ++ out_be32(&vr->req_alarm, 0x00000090); ++ dprintk(1, "status reg: 0x%08x, field base: 0x%08x\n", ++ in_be32(&vr->status_cfg), in_be32(&vr->field_base_addr)); ++} ++ ++static void viu_overlay_intr(struct viu_dev *dev, u32 status) ++{ ++ struct viu_reg *vr = dev->vr; ++ ++ if (status & INT_DMA_END_STATUS) ++ dev->dma_done = 1; ++ ++ if (status & INT_FIELD_STATUS) { ++ if (dev->dma_done) { ++ u32 addr = reg_val.field_base_addr; ++ ++ dev->dma_done = 0; ++ if (status & FIELD_NO) ++ addr += reg_val.dma_inc; ++ ++ out_be32(&vr->field_base_addr, addr); ++ out_be32(&vr->dma_inc, reg_val.dma_inc); ++ out_be32(&vr->status_cfg, ++ (status & 0xffc0ffff) | ++ (status & INT_ALL_STATUS) | ++ reg_val.status_cfg); ++ } else if (status & INT_VSYNC_STATUS) { ++ out_be32(&vr->status_cfg, ++ (status & 0xffc0ffff) | ++ (status & INT_ALL_STATUS) | ++ reg_val.status_cfg); ++ } ++ } ++} ++ ++static void viu_capture_intr(struct viu_dev *dev, u32 status) ++{ ++ struct viu_dmaqueue *vidq = &dev->vidq; ++ struct viu_reg *vr = dev->vr; ++ struct viu_buf *buf; ++ int field_num; ++ int need_two; ++ int dma_done = 0; ++ ++ field_num = status & FIELD_NO; ++ need_two = V4L2_FIELD_HAS_BOTH(dev->capfield); ++ ++ if (status & INT_DMA_END_STATUS) { ++ dma_done = 1; ++ if (((field_num == 0) && (dev->field == 0)) || ++ (field_num && (dev->field == 1))) ++ dev->field++; ++ } ++ ++ if (status & INT_FIELD_STATUS) { ++ dprintk(1, "irq: field %d, done %d\n", ++ !!field_num, dma_done); ++ if (unlikely(dev->first)) { ++ if (field_num == 0) { ++ dev->first = 0; ++ dprintk(1, "activate first buf\n"); ++ viu_activate_next_buf(dev, vidq); ++ } else ++ dprintk(1, "wait field 0\n"); ++ return; ++ } ++ ++ /* setup buffer address for next dma operation */ ++ if (!list_empty(&vidq->active)) { ++ u32 addr = reg_val.field_base_addr; ++ ++ if (field_num && need_two) { ++ addr += reg_val.dma_inc; ++ dprintk(1, "field 1, 0x%lx, dev field %d\n", ++ (unsigned long)addr, dev->field); ++ } ++ out_be32(&vr->field_base_addr, addr); ++ out_be32(&vr->dma_inc, reg_val.dma_inc); ++ out_be32(&vr->status_cfg, ++ (status & 0xffc0ffff) | ++ (status & INT_ALL_STATUS) | ++ reg_val.status_cfg); ++ return; ++ } ++ } ++ ++ if (dma_done && field_num && (dev->field == 2)) { ++ dev->field = 0; ++ buf = list_entry(vidq->active.next, ++ struct viu_buf, vb.queue); ++ dprintk(1, "viu/0: [%p/%d] 0x%lx/0x%lx: dma complete\n", ++ buf, buf->vb.i, ++ (unsigned long)videobuf_to_dma_contig(&buf->vb), ++ (unsigned long)in_be32(&vr->field_base_addr)); ++ ++ if (waitqueue_active(&buf->vb.done)) { ++ list_del(&buf->vb.queue); ++ v4l2_get_timestamp(&buf->vb.ts); ++ buf->vb.state = VIDEOBUF_DONE; ++ buf->vb.field_count++; ++ wake_up(&buf->vb.done); ++ } ++ /* activate next dma buffer */ ++ viu_activate_next_buf(dev, vidq); ++ } ++} ++ ++static irqreturn_t viu_intr(int irq, void *dev_id) ++{ ++ struct viu_dev *dev = (struct viu_dev *)dev_id; ++ struct viu_reg *vr = dev->vr; ++ u32 status; ++ u32 error; ++ ++ status = in_be32(&vr->status_cfg); ++ ++ if (status & INT_ERROR_STATUS) { ++ dev->irqs.error_irq++; ++ error = status & ERR_MASK; ++ if (error) ++ dprintk(1, "Err: error(%d), times:%d!\n", ++ error >> 4, dev->irqs.error_irq); ++ /* Clear interrupt error bit and error flags */ ++ out_be32(&vr->status_cfg, ++ (status & 0xffc0ffff) | INT_ERROR_STATUS); ++ } ++ ++ if (status & INT_DMA_END_STATUS) { ++ dev->irqs.dma_end_irq++; ++ dev->dma_done = 1; ++ dprintk(2, "VIU DMA end interrupt times: %d\n", ++ dev->irqs.dma_end_irq); ++ } ++ ++ if (status & INT_HSYNC_STATUS) ++ dev->irqs.hsync_irq++; ++ ++ if (status & INT_FIELD_STATUS) { ++ dev->irqs.field_irq++; ++ dprintk(2, "VIU field interrupt times: %d\n", ++ dev->irqs.field_irq); ++ } ++ ++ if (status & INT_VSTART_STATUS) ++ dev->irqs.vstart_irq++; ++ ++ if (status & INT_VSYNC_STATUS) { ++ dev->irqs.vsync_irq++; ++ dprintk(2, "VIU vsync interrupt times: %d\n", ++ dev->irqs.vsync_irq); ++ } ++ ++ /* clear all pending irqs */ ++ status = in_be32(&vr->status_cfg); ++ out_be32(&vr->status_cfg, ++ (status & 0xffc0ffff) | (status & INT_ALL_STATUS)); ++ ++ if (dev->ovenable) { ++ viu_overlay_intr(dev, status); ++ return IRQ_HANDLED; ++ } ++ ++ /* Capture mode */ ++ viu_capture_intr(dev, status); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * File operations for the device ++ */ ++static int viu_open(struct file *file) ++{ ++ struct video_device *vdev = video_devdata(file); ++ struct viu_dev *dev = video_get_drvdata(vdev); ++ struct viu_fh *fh; ++ struct viu_reg *vr; ++ int minor = vdev->minor; ++ u32 status_cfg; ++ int i; ++ ++ dprintk(1, "viu: open (minor=%d)\n", minor); ++ ++ dev->users++; ++ if (dev->users > 1) { ++ dev->users--; ++ return -EBUSY; ++ } ++ ++ vr = dev->vr; ++ ++ dprintk(1, "open minor=%d type=%s users=%d\n", minor, ++ v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); ++ ++ if (mutex_lock_interruptible(&dev->lock)) { ++ dev->users--; ++ return -ERESTARTSYS; ++ } ++ ++ /* allocate and initialize per filehandle data */ ++ fh = kzalloc(sizeof(*fh), GFP_KERNEL); ++ if (!fh) { ++ dev->users--; ++ mutex_unlock(&dev->lock); ++ return -ENOMEM; ++ } ++ ++ file->private_data = fh; ++ fh->dev = dev; ++ ++ fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_RGB32); ++ fh->width = norm_maxw(); ++ fh->height = norm_maxh(); ++ dev->crop_current.width = fh->width; ++ dev->crop_current.height = fh->height; ++ ++ /* Put all controls at a sane state */ ++ for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) ++ qctl_regs[i] = viu_qctrl[i].default_value; ++ ++ dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n", ++ (unsigned long)fh, (unsigned long)dev, ++ (unsigned long)&dev->vidq); ++ dprintk(1, "Open: list_empty queued=%d\n", ++ list_empty(&dev->vidq.queued)); ++ dprintk(1, "Open: list_empty active=%d\n", ++ list_empty(&dev->vidq.active)); ++ ++ viu_default_settings(vr); ++ ++ status_cfg = in_be32(&vr->status_cfg); ++ out_be32(&vr->status_cfg, ++ status_cfg & ~(INT_VSYNC_EN | INT_HSYNC_EN | ++ INT_FIELD_EN | INT_VSTART_EN | ++ INT_DMA_END_EN | INT_ERROR_EN | INT_ECC_EN)); ++ ++ status_cfg = in_be32(&vr->status_cfg); ++ out_be32(&vr->status_cfg, status_cfg | INT_ALL_STATUS); ++ ++ spin_lock_init(&fh->vbq_lock); ++ videobuf_queue_dma_contig_init(&fh->vb_vidq, &viu_video_qops, ++ dev->dev, &fh->vbq_lock, ++ fh->type, V4L2_FIELD_INTERLACED, ++ sizeof(struct viu_buf), fh, ++ &fh->dev->lock); ++ mutex_unlock(&dev->lock); ++ return 0; ++} ++ ++static ssize_t viu_read(struct file *file, char __user *data, size_t count, ++ loff_t *ppos) ++{ ++ struct viu_fh *fh = file->private_data; ++ struct viu_dev *dev = fh->dev; ++ int ret = 0; ++ ++ dprintk(2, "%s\n", __func__); ++ if (dev->ovenable) ++ dev->ovenable = 0; ++ ++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { ++ if (mutex_lock_interruptible(&dev->lock)) ++ return -ERESTARTSYS; ++ viu_start_dma(dev); ++ ret = videobuf_read_stream(&fh->vb_vidq, data, count, ++ ppos, 0, file->f_flags & O_NONBLOCK); ++ mutex_unlock(&dev->lock); ++ return ret; ++ } ++ return 0; ++} ++ ++static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait) ++{ ++ struct viu_fh *fh = file->private_data; ++ struct videobuf_queue *q = &fh->vb_vidq; ++ struct viu_dev *dev = fh->dev; ++ unsigned int res; ++ ++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) ++ return POLLERR; ++ ++ mutex_lock(&dev->lock); ++ res = videobuf_poll_stream(file, q, wait); ++ mutex_unlock(&dev->lock); ++ return res; ++} ++ ++static int viu_release(struct file *file) ++{ ++ struct viu_fh *fh = file->private_data; ++ struct viu_dev *dev = fh->dev; ++ int minor = video_devdata(file)->minor; ++ ++ mutex_lock(&dev->lock); ++ viu_stop_dma(dev); ++ videobuf_stop(&fh->vb_vidq); ++ videobuf_mmap_free(&fh->vb_vidq); ++ mutex_unlock(&dev->lock); ++ ++ kfree(fh); ++ ++ dev->users--; ++ dprintk(1, "close (minor=%d, users=%d)\n", ++ minor, dev->users); ++ return 0; ++} ++ ++void viu_reset(struct viu_reg *reg) ++{ ++ out_be32(®->status_cfg, 0); ++ out_be32(®->luminance, 0x9512a254); ++ out_be32(®->chroma_r, 0x03310000); ++ out_be32(®->chroma_g, 0x06600f38); ++ out_be32(®->chroma_b, 0x00000409); ++ out_be32(®->field_base_addr, 0); ++ out_be32(®->dma_inc, 0); ++ out_be32(®->picture_count, 0x01e002d0); ++ out_be32(®->req_alarm, 0x00000090); ++ out_be32(®->alpha, 0x000000ff); ++} ++ ++static int viu_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct viu_fh *fh = file->private_data; ++ struct viu_dev *dev = fh->dev; ++ int ret; ++ ++ dprintk(1, "mmap called, vma=0x%08lx\n", (unsigned long)vma); ++ ++ if (mutex_lock_interruptible(&dev->lock)) ++ return -ERESTARTSYS; ++ ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); ++ mutex_unlock(&dev->lock); ++ ++ dprintk(1, "vma start=0x%08lx, size=%ld, ret=%d\n", ++ (unsigned long)vma->vm_start, ++ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, ++ ret); ++ ++ return ret; ++} ++ ++static struct v4l2_file_operations viu_fops = { ++ .owner = THIS_MODULE, ++ .open = viu_open, ++ .release = viu_release, ++ .read = viu_read, ++ .poll = viu_poll, ++ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ ++ .mmap = viu_mmap, ++}; ++ ++static const struct v4l2_ioctl_ops viu_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_cap, ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_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_querystd = vidioc_querystd, ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ .vidioc_queryctrl = vidioc_queryctrl, ++ .vidioc_g_ctrl = vidioc_g_ctrl, ++ .vidioc_s_ctrl = vidioc_s_ctrl, ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++}; ++ ++static struct video_device viu_template = { ++ .name = "FSL viu", ++ .fops = &viu_fops, ++ .minor = -1, ++ .ioctl_ops = &viu_ioctl_ops, ++ .release = video_device_release, ++ ++ .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, ++ .current_norm = V4L2_STD_NTSC_M, ++}; ++ ++static int __devinit viu_of_probe(struct platform_device *op) ++{ ++ struct viu_dev *viu_dev; ++ struct video_device *vdev; ++ struct resource r; ++ struct viu_reg __iomem *viu_regs; ++ struct i2c_adapter *ad; ++ int ret, viu_irq; ++ ++ ret = of_address_to_resource(op->dev.of_node, 0, &r); ++ if (ret) { ++ dev_err(&op->dev, "Can't parse device node resource\n"); ++ return -ENODEV; ++ } ++ ++ viu_irq = irq_of_parse_and_map(op->dev.of_node, 0); ++ if (viu_irq == NO_IRQ) { ++ dev_err(&op->dev, "Error while mapping the irq\n"); ++ return -EINVAL; ++ } ++ ++ /* request mem region */ ++ if (!devm_request_mem_region(&op->dev, r.start, ++ sizeof(struct viu_reg), DRV_NAME)) { ++ dev_err(&op->dev, "Error while requesting mem region\n"); ++ ret = -EBUSY; ++ goto err; ++ } ++ ++ /* remap registers */ ++ viu_regs = devm_ioremap(&op->dev, r.start, sizeof(struct viu_reg)); ++ if (!viu_regs) { ++ dev_err(&op->dev, "Can't map register set\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* Prepare our private structure */ ++ viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC); ++ if (!viu_dev) { ++ dev_err(&op->dev, "Can't allocate private structure\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ viu_dev->vr = viu_regs; ++ viu_dev->irq = viu_irq; ++ viu_dev->dev = &op->dev; ++ ++ /* init video dma queues */ ++ INIT_LIST_HEAD(&viu_dev->vidq.active); ++ INIT_LIST_HEAD(&viu_dev->vidq.queued); ++ ++ snprintf(viu_dev->v4l2_dev.name, ++ sizeof(viu_dev->v4l2_dev.name), "%s", "VIU"); ++ ret = v4l2_device_register(viu_dev->dev, &viu_dev->v4l2_dev); ++ if (ret < 0) { ++ dev_err(&op->dev, "v4l2_device_register() failed: %d\n", ret); ++ goto err; ++ } ++ ++ ad = i2c_get_adapter(0); ++ viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad, ++ "saa7113", VIU_VIDEO_DECODER_ADDR, NULL); ++ ++ viu_dev->vidq.timeout.function = viu_vid_timeout; ++ viu_dev->vidq.timeout.data = (unsigned long)viu_dev; ++ init_timer(&viu_dev->vidq.timeout); ++ viu_dev->first = 1; ++ ++ /* Allocate memory for video device */ ++ vdev = video_device_alloc(); ++ if (vdev == NULL) { ++ ret = -ENOMEM; ++ goto err_vdev; ++ } ++ ++ memcpy(vdev, &viu_template, sizeof(viu_template)); ++ ++ vdev->v4l2_dev = &viu_dev->v4l2_dev; ++ ++ viu_dev->vdev = vdev; ++ ++ /* initialize locks */ ++ mutex_init(&viu_dev->lock); ++ viu_dev->vdev->lock = &viu_dev->lock; ++ spin_lock_init(&viu_dev->slock); ++ ++ video_set_drvdata(viu_dev->vdev, viu_dev); ++ ++ mutex_lock(&viu_dev->lock); ++ ++ ret = video_register_device(viu_dev->vdev, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) { ++ video_device_release(viu_dev->vdev); ++ goto err_vdev; ++ } ++ ++ /* enable VIU clock */ ++ viu_dev->clk = clk_get(&op->dev, "viu_clk"); ++ if (IS_ERR(viu_dev->clk)) { ++ dev_err(&op->dev, "failed to find the clock module!\n"); ++ ret = -ENODEV; ++ goto err_clk; ++ } else { ++ clk_enable(viu_dev->clk); ++ } ++ ++ /* reset VIU module */ ++ viu_reset(viu_dev->vr); ++ ++ /* install interrupt handler */ ++ if (request_irq(viu_dev->irq, viu_intr, 0, "viu", (void *)viu_dev)) { ++ dev_err(&op->dev, "Request VIU IRQ failed.\n"); ++ ret = -ENODEV; ++ goto err_irq; ++ } ++ ++ mutex_unlock(&viu_dev->lock); ++ ++ dev_info(&op->dev, "Freescale VIU Video Capture Board\n"); ++ return ret; ++ ++err_irq: ++ clk_disable(viu_dev->clk); ++ clk_put(viu_dev->clk); ++err_clk: ++ video_unregister_device(viu_dev->vdev); ++err_vdev: ++ mutex_unlock(&viu_dev->lock); ++ i2c_put_adapter(ad); ++ v4l2_device_unregister(&viu_dev->v4l2_dev); ++err: ++ irq_dispose_mapping(viu_irq); ++ return ret; ++} ++ ++static int __devexit viu_of_remove(struct platform_device *op) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); ++ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); ++ struct v4l2_subdev *sdev = list_entry(v4l2_dev->subdevs.next, ++ struct v4l2_subdev, list); ++ struct i2c_client *client = v4l2_get_subdevdata(sdev); ++ ++ free_irq(dev->irq, (void *)dev); ++ irq_dispose_mapping(dev->irq); ++ ++ clk_disable(dev->clk); ++ clk_put(dev->clk); ++ ++ video_unregister_device(dev->vdev); ++ i2c_put_adapter(client->adapter); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int viu_suspend(struct platform_device *op, pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); ++ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); ++ ++ clk_disable(dev->clk); ++ return 0; ++} ++ ++static int viu_resume(struct platform_device *op) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&op->dev); ++ struct viu_dev *dev = container_of(v4l2_dev, struct viu_dev, v4l2_dev); ++ ++ clk_enable(dev->clk); ++ return 0; ++} ++#endif ++ ++/* ++ * Initialization and module stuff ++ */ ++static struct of_device_id mpc512x_viu_of_match[] = { ++ { ++ .compatible = "fsl,mpc5121-viu", ++ }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mpc512x_viu_of_match); ++ ++static struct platform_driver viu_of_platform_driver = { ++ .probe = viu_of_probe, ++ .remove = __devexit_p(viu_of_remove), ++#ifdef CONFIG_PM ++ .suspend = viu_suspend, ++ .resume = viu_resume, ++#endif ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = mpc512x_viu_of_match, ++ }, ++}; ++ ++module_platform_driver(viu_of_platform_driver); ++ ++MODULE_DESCRIPTION("Freescale Video-In(VIU)"); ++MODULE_AUTHOR("Hongjun Chen"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(VIU_VERSION); +diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c +new file mode 100644 +index 0000000..5482363 +--- /dev/null ++++ b/drivers/media/platform/indycam.c +@@ -0,0 +1,390 @@ ++/* ++ * indycam.c - Silicon Graphics IndyCam digital camera driver ++ * ++ * Copyright (C) 2003 Ladislav Michl ++ * Copyright (C) 2004,2005 Mikael Nousiainen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* IndyCam decodes stream of photons into digital image representation ;-) */ ++#include ++#include ++#include ++#include ++ ++#include "indycam.h" ++ ++#define INDYCAM_MODULE_VERSION "0.0.5" ++ ++MODULE_DESCRIPTION("SGI IndyCam driver"); ++MODULE_VERSION(INDYCAM_MODULE_VERSION); ++MODULE_AUTHOR("Mikael Nousiainen "); ++MODULE_LICENSE("GPL"); ++ ++ ++// #define INDYCAM_DEBUG ++ ++#ifdef INDYCAM_DEBUG ++#define dprintk(x...) printk("IndyCam: " x); ++#define indycam_regdump(client) indycam_regdump_debug(client) ++#else ++#define dprintk(x...) ++#define indycam_regdump(client) ++#endif ++ ++struct indycam { ++ struct v4l2_subdev sd; ++ u8 version; ++}; ++ ++static inline struct indycam *to_indycam(struct v4l2_subdev *sd) ++{ ++ return container_of(sd, struct indycam, sd); ++} ++ ++static const u8 initseq[] = { ++ INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ ++ INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */ ++ INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */ ++ 0x00, /* INDYCAM_BRIGHTNESS (read-only) */ ++ INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */ ++ INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */ ++ INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */ ++ INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */ ++}; ++ ++/* IndyCam register handling */ ++ ++static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int ret; ++ ++ if (reg == INDYCAM_REG_RESET) { ++ dprintk("indycam_read_reg(): " ++ "skipping write-only register %d\n", reg); ++ *value = 0; ++ return 0; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, reg); ++ ++ if (ret < 0) { ++ printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, " ++ "register = 0x%02x\n", reg); ++ return ret; ++ } ++ ++ *value = (u8)ret; ++ ++ return 0; ++} ++ ++static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ int err; ++ ++ if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) { ++ dprintk("indycam_write_reg(): " ++ "skipping read-only register %d\n", reg); ++ return 0; ++ } ++ ++ dprintk("Writing Reg %d = 0x%02x\n", reg, value); ++ err = i2c_smbus_write_byte_data(client, reg, value); ++ ++ if (err) { ++ printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, " ++ "register = 0x%02x, value = 0x%02x\n", reg, value); ++ } ++ return err; ++} ++ ++static int indycam_write_block(struct v4l2_subdev *sd, u8 reg, ++ u8 length, u8 *data) ++{ ++ int i, err; ++ ++ for (i = 0; i < length; i++) { ++ err = indycam_write_reg(sd, reg + i, data[i]); ++ if (err) ++ return err; ++ } ++ ++ return 0; ++} ++ ++/* Helper functions */ ++ ++#ifdef INDYCAM_DEBUG ++static void indycam_regdump_debug(struct v4l2_subdev *sd) ++{ ++ int i; ++ u8 val; ++ ++ for (i = 0; i < 9; i++) { ++ indycam_read_reg(sd, i, &val); ++ dprintk("Reg %d = 0x%02x\n", i, val); ++ } ++} ++#endif ++ ++static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct indycam *camera = to_indycam(sd); ++ u8 reg; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); ++ if (ret) ++ return -EIO; ++ if (ctrl->id == V4L2_CID_AUTOGAIN) ++ ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) ++ ? 1 : 0; ++ else ++ ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) ++ ? 1 : 0; ++ break; ++ case V4L2_CID_EXPOSURE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); ++ break; ++ case V4L2_CID_GAIN: ++ ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ break; ++ case V4L2_CID_RED_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ break; ++ case INDYCAM_CONTROL_RED_SATURATION: ++ ret = indycam_read_reg(sd, ++ INDYCAM_REG_RED_SATURATION, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ break; ++ case INDYCAM_CONTROL_BLUE_SATURATION: ++ ret = indycam_read_reg(sd, ++ INDYCAM_REG_BLUE_SATURATION, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ break; ++ case V4L2_CID_GAMMA: ++ if (camera->version == CAMERA_VERSION_MOOSE) { ++ ret = indycam_read_reg(sd, ++ INDYCAM_REG_GAMMA, ®); ++ if (ret) ++ return -EIO; ++ ctrl->value = (s32)reg; ++ } else { ++ ctrl->value = INDYCAM_GAMMA_DEFAULT; ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ++{ ++ struct indycam *camera = to_indycam(sd); ++ u8 reg; ++ int ret = 0; ++ ++ switch (ctrl->id) { ++ case V4L2_CID_AUTOGAIN: ++ case V4L2_CID_AUTO_WHITE_BALANCE: ++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); ++ if (ret) ++ break; ++ ++ if (ctrl->id == V4L2_CID_AUTOGAIN) { ++ if (ctrl->value) ++ reg |= INDYCAM_CONTROL_AGCENA; ++ else ++ reg &= ~INDYCAM_CONTROL_AGCENA; ++ } else { ++ if (ctrl->value) ++ reg |= INDYCAM_CONTROL_AWBCTL; ++ else ++ reg &= ~INDYCAM_CONTROL_AWBCTL; ++ } ++ ++ ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg); ++ break; ++ case V4L2_CID_EXPOSURE: ++ reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); ++ ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg); ++ break; ++ case V4L2_CID_GAIN: ++ ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value); ++ break; ++ case V4L2_CID_RED_BALANCE: ++ ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE, ++ ctrl->value); ++ break; ++ case V4L2_CID_BLUE_BALANCE: ++ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE, ++ ctrl->value); ++ break; ++ case INDYCAM_CONTROL_RED_SATURATION: ++ ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION, ++ ctrl->value); ++ break; ++ case INDYCAM_CONTROL_BLUE_SATURATION: ++ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION, ++ ctrl->value); ++ break; ++ case V4L2_CID_GAMMA: ++ if (camera->version == CAMERA_VERSION_MOOSE) { ++ ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA, ++ ctrl->value); ++ } ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/* I2C-interface */ ++ ++static int indycam_g_chip_ident(struct v4l2_subdev *sd, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct i2c_client *client = v4l2_get_subdevdata(sd); ++ struct indycam *camera = to_indycam(sd); ++ ++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM, ++ camera->version); ++} ++ ++/* ----------------------------------------------------------------------- */ ++ ++static const struct v4l2_subdev_core_ops indycam_core_ops = { ++ .g_chip_ident = indycam_g_chip_ident, ++ .g_ctrl = indycam_g_ctrl, ++ .s_ctrl = indycam_s_ctrl, ++}; ++ ++static const struct v4l2_subdev_ops indycam_ops = { ++ .core = &indycam_core_ops, ++}; ++ ++static int indycam_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ int err = 0; ++ struct indycam *camera; ++ struct v4l2_subdev *sd; ++ ++ v4l_info(client, "chip found @ 0x%x (%s)\n", ++ client->addr << 1, client->adapter->name); ++ ++ camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); ++ if (!camera) ++ return -ENOMEM; ++ ++ sd = &camera->sd; ++ v4l2_i2c_subdev_init(sd, client, &indycam_ops); ++ ++ camera->version = i2c_smbus_read_byte_data(client, ++ INDYCAM_REG_VERSION); ++ if (camera->version != CAMERA_VERSION_INDY && ++ camera->version != CAMERA_VERSION_MOOSE) { ++ kfree(camera); ++ return -ENODEV; ++ } ++ ++ printk(KERN_INFO "IndyCam v%d.%d detected\n", ++ INDYCAM_VERSION_MAJOR(camera->version), ++ INDYCAM_VERSION_MINOR(camera->version)); ++ ++ indycam_regdump(sd); ++ ++ // initialize ++ err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq); ++ if (err) { ++ printk(KERN_ERR "IndyCam initialization failed\n"); ++ kfree(camera); ++ return -EIO; ++ } ++ ++ indycam_regdump(sd); ++ ++ // white balance ++ err = indycam_write_reg(sd, INDYCAM_REG_CONTROL, ++ INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); ++ if (err) { ++ printk(KERN_ERR "IndyCam: White balancing camera failed\n"); ++ kfree(camera); ++ return -EIO; ++ } ++ ++ indycam_regdump(sd); ++ ++ printk(KERN_INFO "IndyCam initialized\n"); ++ ++ return 0; ++} ++ ++static int indycam_remove(struct i2c_client *client) ++{ ++ struct v4l2_subdev *sd = i2c_get_clientdata(client); ++ ++ v4l2_device_unregister_subdev(sd); ++ kfree(to_indycam(sd)); ++ return 0; ++} ++ ++static const struct i2c_device_id indycam_id[] = { ++ { "indycam", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, indycam_id); ++ ++static struct i2c_driver indycam_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "indycam", ++ }, ++ .probe = indycam_probe, ++ .remove = indycam_remove, ++ .id_table = indycam_id, ++}; ++ ++module_i2c_driver(indycam_driver); +diff --git a/drivers/media/platform/indycam.h b/drivers/media/platform/indycam.h +new file mode 100644 +index 0000000..881f21c +--- /dev/null ++++ b/drivers/media/platform/indycam.h +@@ -0,0 +1,93 @@ ++/* ++ * indycam.h - Silicon Graphics IndyCam digital camera driver ++ * ++ * Copyright (C) 2003 Ladislav Michl ++ * Copyright (C) 2004,2005 Mikael Nousiainen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef _INDYCAM_H_ ++#define _INDYCAM_H_ ++ ++/* I2C address for the Guinness Camera */ ++#define INDYCAM_ADDR 0x56 ++ ++/* Camera version */ ++#define CAMERA_VERSION_INDY 0x10 /* v1.0 */ ++#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */ ++#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4) ++#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f) ++ ++/* Register bus addresses */ ++#define INDYCAM_REG_CONTROL 0x00 ++#define INDYCAM_REG_SHUTTER 0x01 ++#define INDYCAM_REG_GAIN 0x02 ++#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */ ++#define INDYCAM_REG_RED_BALANCE 0x04 ++#define INDYCAM_REG_BLUE_BALANCE 0x05 ++#define INDYCAM_REG_RED_SATURATION 0x06 ++#define INDYCAM_REG_BLUE_SATURATION 0x07 ++#define INDYCAM_REG_GAMMA 0x08 ++#define INDYCAM_REG_VERSION 0x0e /* read-only */ ++#define INDYCAM_REG_RESET 0x0f /* write-only */ ++ ++#define INDYCAM_REG_LED 0x46 ++#define INDYCAM_REG_ORIENTATION 0x47 ++#define INDYCAM_REG_BUTTON 0x48 ++ ++/* Field definitions of registers */ ++#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */ ++#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */ ++ /* 2-3 are reserved */ ++#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */ ++ ++#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */ ++#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */ ++#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */ ++#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */ ++#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */ ++#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */ ++#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */ ++#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */ ++#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */ ++ ++#define INDYCAM_LED_ACTIVE 0x10 ++#define INDYCAM_LED_INACTIVE 0x30 ++#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40 ++#define INDYCAM_BUTTON_RELEASED 0x10 ++ ++/* Values for controls */ ++#define INDYCAM_SHUTTER_MIN 0x00 ++#define INDYCAM_SHUTTER_MAX 0xff ++#define INDYCAM_GAIN_MIN 0x00 ++#define INDYCAM_GAIN_MAX 0xff ++#define INDYCAM_RED_BALANCE_MIN 0x00 ++#define INDYCAM_RED_BALANCE_MAX 0xff ++#define INDYCAM_BLUE_BALANCE_MIN 0x00 ++#define INDYCAM_BLUE_BALANCE_MAX 0xff ++#define INDYCAM_RED_SATURATION_MIN 0x00 ++#define INDYCAM_RED_SATURATION_MAX 0xff ++#define INDYCAM_BLUE_SATURATION_MIN 0x00 ++#define INDYCAM_BLUE_SATURATION_MAX 0xff ++#define INDYCAM_GAMMA_MIN 0x00 ++#define INDYCAM_GAMMA_MAX 0xff ++ ++#define INDYCAM_AGC_DEFAULT 1 ++#define INDYCAM_AWB_DEFAULT 0 ++#define INDYCAM_SHUTTER_DEFAULT 0xff ++#define INDYCAM_GAIN_DEFAULT 0x80 ++#define INDYCAM_RED_BALANCE_DEFAULT 0x18 ++#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4 ++#define INDYCAM_RED_SATURATION_DEFAULT 0x80 ++#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0 ++#define INDYCAM_GAMMA_DEFAULT 0x80 ++ ++/* Driver interface definitions */ ++ ++#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0) ++#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1) ++ ++#endif +diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c +new file mode 100644 +index 0000000..6c4db9b +--- /dev/null ++++ b/drivers/media/platform/m2m-deinterlace.c +@@ -0,0 +1,1104 @@ ++/* ++ * V4L2 deinterlacing support. ++ * ++ * Copyright (c) 2012 Vista Silicon S.L. ++ * Javier Martin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MEM2MEM_TEST_MODULE_NAME "mem2mem-deinterlace" ++ ++MODULE_DESCRIPTION("mem2mem device which supports deinterlacing using dmaengine"); ++MODULE_AUTHOR("Javier Martin v4l2_dev, "%s: " fmt, __func__, ## arg) ++ ++struct deinterlace_fmt { ++ char *name; ++ u32 fourcc; ++ /* Types the format can be used for */ ++ u32 types; ++}; ++ ++static struct deinterlace_fmt formats[] = { ++ { ++ .name = "YUV 4:2:0 Planar", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, ++ }, ++ { ++ .name = "YUYV 4:2:2", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, ++ }, ++}; ++ ++#define NUM_FORMATS ARRAY_SIZE(formats) ++ ++/* Per-queue, driver-specific private data */ ++struct deinterlace_q_data { ++ unsigned int width; ++ unsigned int height; ++ unsigned int sizeimage; ++ struct deinterlace_fmt *fmt; ++ enum v4l2_field field; ++}; ++ ++enum { ++ V4L2_M2M_SRC = 0, ++ V4L2_M2M_DST = 1, ++}; ++ ++enum { ++ YUV420_DMA_Y_ODD, ++ YUV420_DMA_Y_EVEN, ++ YUV420_DMA_U_ODD, ++ YUV420_DMA_U_EVEN, ++ YUV420_DMA_V_ODD, ++ YUV420_DMA_V_EVEN, ++ YUV420_DMA_Y_ODD_DOUBLING, ++ YUV420_DMA_U_ODD_DOUBLING, ++ YUV420_DMA_V_ODD_DOUBLING, ++ YUYV_DMA_ODD, ++ YUYV_DMA_EVEN, ++ YUYV_DMA_EVEN_DOUBLING, ++}; ++ ++/* Source and destination queue data */ ++static struct deinterlace_q_data q_data[2]; ++ ++static struct deinterlace_q_data *get_q_data(enum v4l2_buf_type type) ++{ ++ switch (type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ return &q_data[V4L2_M2M_SRC]; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &q_data[V4L2_M2M_DST]; ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++static struct deinterlace_fmt *find_format(struct v4l2_format *f) ++{ ++ struct deinterlace_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < NUM_FORMATS; k++) { ++ fmt = &formats[k]; ++ if ((fmt->types & f->type) && ++ (fmt->fourcc == f->fmt.pix.pixelformat)) ++ break; ++ } ++ ++ if (k == NUM_FORMATS) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++struct deinterlace_dev { ++ struct v4l2_device v4l2_dev; ++ struct video_device *vfd; ++ ++ atomic_t busy; ++ struct mutex dev_mutex; ++ spinlock_t irqlock; ++ ++ struct dma_chan *dma_chan; ++ ++ struct v4l2_m2m_dev *m2m_dev; ++ struct vb2_alloc_ctx *alloc_ctx; ++}; ++ ++struct deinterlace_ctx { ++ struct deinterlace_dev *dev; ++ ++ /* Abort requested by m2m */ ++ int aborting; ++ enum v4l2_colorspace colorspace; ++ dma_cookie_t cookie; ++ struct v4l2_m2m_ctx *m2m_ctx; ++ struct dma_interleaved_template *xt; ++}; ++ ++/* ++ * mem2mem callbacks ++ */ ++static int deinterlace_job_ready(void *priv) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ struct deinterlace_dev *pcdev = ctx->dev; ++ ++ if ((v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) ++ && (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) > 0) ++ && (atomic_read(&ctx->dev->busy) == 0)) { ++ dprintk(pcdev, "Task ready\n"); ++ return 1; ++ } ++ ++ dprintk(pcdev, "Task not ready to run\n"); ++ ++ return 0; ++} ++ ++static void deinterlace_job_abort(void *priv) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ struct deinterlace_dev *pcdev = ctx->dev; ++ ++ ctx->aborting = 1; ++ ++ dprintk(pcdev, "Aborting task\n"); ++ ++ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); ++} ++ ++static void deinterlace_lock(void *priv) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ struct deinterlace_dev *pcdev = ctx->dev; ++ mutex_lock(&pcdev->dev_mutex); ++} ++ ++static void deinterlace_unlock(void *priv) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ struct deinterlace_dev *pcdev = ctx->dev; ++ mutex_unlock(&pcdev->dev_mutex); ++} ++ ++static void dma_callback(void *data) ++{ ++ struct deinterlace_ctx *curr_ctx = data; ++ struct deinterlace_dev *pcdev = curr_ctx->dev; ++ struct vb2_buffer *src_vb, *dst_vb; ++ ++ atomic_set(&pcdev->busy, 0); ++ ++ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); ++ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); ++ ++ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); ++ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); ++ ++ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); ++ ++ dprintk(pcdev, "dma transfers completed.\n"); ++} ++ ++static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op, ++ int do_callback) ++{ ++ struct deinterlace_q_data *s_q_data; ++ struct vb2_buffer *src_buf, *dst_buf; ++ struct deinterlace_dev *pcdev = ctx->dev; ++ struct dma_chan *chan = pcdev->dma_chan; ++ struct dma_device *dmadev = chan->device; ++ struct dma_async_tx_descriptor *tx; ++ unsigned int s_width, s_height; ++ unsigned int s_size; ++ dma_addr_t p_in, p_out; ++ enum dma_ctrl_flags flags; ++ ++ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ++ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ ++ s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ s_width = s_q_data->width; ++ s_height = s_q_data->height; ++ s_size = s_width * s_height; ++ ++ p_in = (dma_addr_t)vb2_dma_contig_plane_dma_addr(src_buf, 0); ++ p_out = (dma_addr_t)vb2_dma_contig_plane_dma_addr(dst_buf, 0); ++ if (!p_in || !p_out) { ++ v4l2_err(&pcdev->v4l2_dev, ++ "Acquiring kernel pointers to buffers failed\n"); ++ return; ++ } ++ ++ switch (op) { ++ case YUV420_DMA_Y_ODD: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width; ++ ctx->xt->sgl[0].icg = s_width; ++ ctx->xt->src_start = p_in; ++ ctx->xt->dst_start = p_out; ++ break; ++ case YUV420_DMA_Y_EVEN: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width; ++ ctx->xt->sgl[0].icg = s_width; ++ ctx->xt->src_start = p_in + s_size / 2; ++ ctx->xt->dst_start = p_out + s_width; ++ break; ++ case YUV420_DMA_U_ODD: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + s_size; ++ ctx->xt->dst_start = p_out + s_size; ++ break; ++ case YUV420_DMA_U_EVEN: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + (9 * s_size) / 8; ++ ctx->xt->dst_start = p_out + s_size + s_width / 2; ++ break; ++ case YUV420_DMA_V_ODD: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + (5 * s_size) / 4; ++ ctx->xt->dst_start = p_out + (5 * s_size) / 4; ++ break; ++ case YUV420_DMA_V_EVEN: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + (11 * s_size) / 8; ++ ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2; ++ break; ++ case YUV420_DMA_Y_ODD_DOUBLING: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width; ++ ctx->xt->sgl[0].icg = s_width; ++ ctx->xt->src_start = p_in; ++ ctx->xt->dst_start = p_out + s_width; ++ break; ++ case YUV420_DMA_U_ODD_DOUBLING: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + s_size; ++ ctx->xt->dst_start = p_out + s_size + s_width / 2; ++ break; ++ case YUV420_DMA_V_ODD_DOUBLING: ++ ctx->xt->numf = s_height / 4; ++ ctx->xt->sgl[0].size = s_width / 2; ++ ctx->xt->sgl[0].icg = s_width / 2; ++ ctx->xt->src_start = p_in + (5 * s_size) / 4; ++ ctx->xt->dst_start = p_out + (5 * s_size) / 4 + s_width / 2; ++ break; ++ case YUYV_DMA_ODD: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width * 2; ++ ctx->xt->sgl[0].icg = s_width * 2; ++ ctx->xt->src_start = p_in; ++ ctx->xt->dst_start = p_out; ++ break; ++ case YUYV_DMA_EVEN: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width * 2; ++ ctx->xt->sgl[0].icg = s_width * 2; ++ ctx->xt->src_start = p_in + s_size; ++ ctx->xt->dst_start = p_out + s_width * 2; ++ break; ++ case YUYV_DMA_EVEN_DOUBLING: ++ default: ++ ctx->xt->numf = s_height / 2; ++ ctx->xt->sgl[0].size = s_width * 2; ++ ctx->xt->sgl[0].icg = s_width * 2; ++ ctx->xt->src_start = p_in; ++ ctx->xt->dst_start = p_out + s_width * 2; ++ break; ++ } ++ ++ /* Common parameters for al transfers */ ++ ctx->xt->frame_size = 1; ++ ctx->xt->dir = DMA_MEM_TO_MEM; ++ ctx->xt->src_sgl = false; ++ ctx->xt->dst_sgl = true; ++ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | ++ DMA_COMPL_SKIP_DEST_UNMAP | DMA_COMPL_SKIP_SRC_UNMAP; ++ ++ tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags); ++ if (tx == NULL) { ++ v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep error\n"); ++ return; ++ } ++ ++ if (do_callback) { ++ tx->callback = dma_callback; ++ tx->callback_param = ctx; ++ } ++ ++ ctx->cookie = dmaengine_submit(tx); ++ if (dma_submit_error(ctx->cookie)) { ++ v4l2_warn(&pcdev->v4l2_dev, ++ "DMA submit error %d with src=0x%x dst=0x%x len=0x%x\n", ++ ctx->cookie, (unsigned)p_in, (unsigned)p_out, ++ s_size * 3/2); ++ return; ++ } ++ ++ dma_async_issue_pending(chan); ++} ++ ++static void deinterlace_device_run(void *priv) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ struct deinterlace_q_data *dst_q_data; ++ ++ atomic_set(&ctx->dev->busy, 1); ++ ++ dprintk(ctx->dev, "%s: DMA try issue.\n", __func__); ++ ++ dst_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ ++ /* ++ * 4 possible field conversions are possible at the moment: ++ * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_INTERLACED_TB: ++ * two separate fields in the same input buffer are interlaced ++ * in the output buffer using weaving. Top field comes first. ++ * V4L2_FIELD_SEQ_TB --> V4L2_FIELD_NONE: ++ * top field from the input buffer is copied to the output buffer ++ * using line doubling. Bottom field from the input buffer is discarded. ++ * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_INTERLACED_BT: ++ * two separate fields in the same input buffer are interlaced ++ * in the output buffer using weaving. Bottom field comes first. ++ * V4L2_FIELD_SEQ_BT --> V4L2_FIELD_NONE: ++ * bottom field from the input buffer is copied to the output buffer ++ * using line doubling. Top field from the input buffer is discarded. ++ */ ++ switch (dst_q_data->fmt->fourcc) { ++ case V4L2_PIX_FMT_YUV420: ++ switch (dst_q_data->field) { ++ case V4L2_FIELD_INTERLACED_TB: ++ case V4L2_FIELD_INTERLACED_BT: ++ dprintk(ctx->dev, "%s: yuv420 interlaced tb.\n", ++ __func__); ++ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_Y_EVEN, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_U_EVEN, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_V_EVEN, 1); ++ break; ++ case V4L2_FIELD_NONE: ++ default: ++ dprintk(ctx->dev, "%s: yuv420 interlaced line doubling.\n", ++ __func__); ++ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_Y_ODD_DOUBLING, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_U_ODD_DOUBLING, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD, 0); ++ deinterlace_issue_dma(ctx, YUV420_DMA_V_ODD_DOUBLING, 1); ++ break; ++ } ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ default: ++ switch (dst_q_data->field) { ++ case V4L2_FIELD_INTERLACED_TB: ++ case V4L2_FIELD_INTERLACED_BT: ++ dprintk(ctx->dev, "%s: yuyv interlaced_tb.\n", ++ __func__); ++ deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0); ++ deinterlace_issue_dma(ctx, YUYV_DMA_EVEN, 1); ++ break; ++ case V4L2_FIELD_NONE: ++ default: ++ dprintk(ctx->dev, "%s: yuyv interlaced line doubling.\n", ++ __func__); ++ deinterlace_issue_dma(ctx, YUYV_DMA_ODD, 0); ++ deinterlace_issue_dma(ctx, YUYV_DMA_EVEN_DOUBLING, 1); ++ break; ++ } ++ break; ++ } ++ ++ dprintk(ctx->dev, "%s: DMA issue done.\n", __func__); ++} ++ ++/* ++ * video ioctls ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strlcpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver)); ++ strlcpy(cap->card, MEM2MEM_NAME, sizeof(cap->card)); ++ strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->card)); ++ /* ++ * This is only a mem-to-mem video device. The capture and output ++ * device capability flags are left only for backward compatibility ++ * and are scheduled for removal. ++ */ ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | ++ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) ++{ ++ int i, num; ++ struct deinterlace_fmt *fmt; ++ ++ num = 0; ++ ++ for (i = 0; i < NUM_FORMATS; ++i) { ++ if (formats[i].types & type) { ++ /* index-th format of type type found ? */ ++ if (num == f->index) ++ break; ++ /* Correct type but haven't reached our index yet, ++ * just increment per-type index */ ++ ++num; ++ } ++ } ++ ++ if (i < NUM_FORMATS) { ++ /* Format found */ ++ fmt = &formats[i]; ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++ } ++ ++ /* Format not found */ ++ return -EINVAL; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_CAPTURE); ++} ++ ++static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_OUTPUT); ++} ++ ++static int vidioc_g_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f) ++{ ++ struct vb2_queue *vq; ++ struct deinterlace_q_data *q_data; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(f->type); ++ ++ f->fmt.pix.width = q_data->width; ++ f->fmt.pix.height = q_data->height; ++ f->fmt.pix.field = q_data->field; ++ f->fmt.pix.pixelformat = q_data->fmt->fourcc; ++ ++ switch (q_data->fmt->fourcc) { ++ case V4L2_PIX_FMT_YUV420: ++ f->fmt.pix.bytesperline = q_data->width * 3 / 2; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ default: ++ f->fmt.pix.bytesperline = q_data->width * 2; ++ } ++ ++ f->fmt.pix.sizeimage = q_data->sizeimage; ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(priv, f); ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(priv, f); ++} ++ ++static int vidioc_try_fmt(struct v4l2_format *f, struct deinterlace_fmt *fmt) ++{ ++ switch (f->fmt.pix.pixelformat) { ++ case V4L2_PIX_FMT_YUV420: ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ default: ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ } ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct deinterlace_fmt *fmt; ++ struct deinterlace_ctx *ctx = priv; ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; ++ ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ if (f->fmt.pix.field != V4L2_FIELD_INTERLACED_TB && ++ f->fmt.pix.field != V4L2_FIELD_INTERLACED_BT && ++ f->fmt.pix.field != V4L2_FIELD_NONE) ++ f->fmt.pix.field = V4L2_FIELD_INTERLACED_TB; ++ ++ return vidioc_try_fmt(f, fmt); ++} ++ ++static int vidioc_try_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct deinterlace_fmt *fmt; ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) ++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; ++ ++ if (!f->fmt.pix.colorspace) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; ++ ++ if (f->fmt.pix.field != V4L2_FIELD_SEQ_TB && ++ f->fmt.pix.field != V4L2_FIELD_SEQ_BT) ++ f->fmt.pix.field = V4L2_FIELD_SEQ_TB; ++ ++ return vidioc_try_fmt(f, fmt); ++} ++ ++static int vidioc_s_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f) ++{ ++ struct deinterlace_q_data *q_data; ++ struct vb2_queue *vq; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(f->type); ++ if (!q_data) ++ return -EINVAL; ++ ++ if (vb2_is_busy(vq)) { ++ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ q_data->fmt = find_format(f); ++ if (!q_data->fmt) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "Couldn't set format type %d, wxh: %dx%d. fmt: %d, field: %d\n", ++ f->type, f->fmt.pix.width, f->fmt.pix.height, ++ f->fmt.pix.pixelformat, f->fmt.pix.field); ++ return -EINVAL; ++ } ++ ++ q_data->width = f->fmt.pix.width; ++ q_data->height = f->fmt.pix.height; ++ q_data->field = f->fmt.pix.field; ++ ++ switch (f->fmt.pix.pixelformat) { ++ case V4L2_PIX_FMT_YUV420: ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2; ++ q_data->sizeimage = (q_data->width * q_data->height * 3) / 2; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ default: ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ q_data->sizeimage = q_data->width * q_data->height * 2; ++ } ++ ++ dprintk(ctx->dev, ++ "Setting format for type %d, wxh: %dx%d, fmt: %d, field: %d\n", ++ f->type, q_data->width, q_data->height, q_data->fmt->fourcc, ++ q_data->field); ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) ++ return ret; ++ return vidioc_s_fmt(priv, f); ++} ++ ++static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_out(file, priv, f); ++ if (ret) ++ return ret; ++ ++ ret = vidioc_s_fmt(priv, f); ++ if (!ret) ++ ctx->colorspace = f->fmt.pix.colorspace; ++ ++ return ret; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbufs) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ ++ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ ++ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ ++ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ ++ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct deinterlace_q_data *s_q_data, *d_q_data; ++ struct deinterlace_ctx *ctx = priv; ++ ++ s_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ d_q_data = get_q_data(V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ ++ /* Check that src and dst queues have the same pix format */ ++ if (s_q_data->fmt->fourcc != d_q_data->fmt->fourcc) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "src and dst formats don't match.\n"); ++ return -EINVAL; ++ } ++ ++ /* Check that input and output deinterlacing types are compatible */ ++ switch (s_q_data->field) { ++ case V4L2_FIELD_SEQ_BT: ++ if (d_q_data->field != V4L2_FIELD_NONE && ++ d_q_data->field != V4L2_FIELD_INTERLACED_BT) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "src and dst field conversion [(%d)->(%d)] not supported.\n", ++ s_q_data->field, d_q_data->field); ++ return -EINVAL; ++ } ++ break; ++ case V4L2_FIELD_SEQ_TB: ++ if (d_q_data->field != V4L2_FIELD_NONE && ++ d_q_data->field != V4L2_FIELD_INTERLACED_TB) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "src and dst field conversion [(%d)->(%d)] not supported.\n", ++ s_q_data->field, d_q_data->field); ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ ++ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); ++} ++ ++static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, ++ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, ++ ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++}; ++ ++ ++/* ++ * Queue operations ++ */ ++struct vb2_dc_conf { ++ struct device *dev; ++}; ++ ++static int deinterlace_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vq); ++ struct deinterlace_q_data *q_data; ++ unsigned int size, count = *nbuffers; ++ ++ q_data = get_q_data(vq->type); ++ ++ switch (q_data->fmt->fourcc) { ++ case V4L2_PIX_FMT_YUV420: ++ size = q_data->width * q_data->height * 3 / 2; ++ break; ++ case V4L2_PIX_FMT_YUYV: ++ default: ++ size = q_data->width * q_data->height * 2; ++ } ++ ++ *nplanes = 1; ++ *nbuffers = count; ++ sizes[0] = size; ++ ++ alloc_ctxs[0] = ctx->dev->alloc_ctx; ++ ++ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); ++ ++ return 0; ++} ++ ++static int deinterlace_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ struct deinterlace_q_data *q_data; ++ ++ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); ++ ++ q_data = get_q_data(vb->vb2_queue->type); ++ ++ if (vb2_plane_size(vb, 0) < q_data->sizeimage) { ++ dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage); ++ return -EINVAL; ++ } ++ ++ vb2_set_plane_payload(vb, 0, q_data->sizeimage); ++ ++ return 0; ++} ++ ++static void deinterlace_buf_queue(struct vb2_buffer *vb) ++{ ++ struct deinterlace_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); ++} ++ ++static struct vb2_ops deinterlace_qops = { ++ .queue_setup = deinterlace_queue_setup, ++ .buf_prepare = deinterlace_buf_prepare, ++ .buf_queue = deinterlace_buf_queue, ++}; ++ ++static int queue_init(void *priv, struct vb2_queue *src_vq, ++ struct vb2_queue *dst_vq) ++{ ++ struct deinterlace_ctx *ctx = priv; ++ int ret; ++ ++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ src_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ src_vq->drv_priv = ctx; ++ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ src_vq->ops = &deinterlace_qops; ++ src_vq->mem_ops = &vb2_dma_contig_memops; ++ q_data[V4L2_M2M_SRC].fmt = &formats[0]; ++ q_data[V4L2_M2M_SRC].width = 640; ++ q_data[V4L2_M2M_SRC].height = 480; ++ q_data[V4L2_M2M_SRC].sizeimage = (640 * 480 * 3) / 2; ++ q_data[V4L2_M2M_SRC].field = V4L2_FIELD_SEQ_TB; ++ ++ ret = vb2_queue_init(src_vq); ++ if (ret) ++ return ret; ++ ++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ dst_vq->drv_priv = ctx; ++ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ dst_vq->ops = &deinterlace_qops; ++ dst_vq->mem_ops = &vb2_dma_contig_memops; ++ q_data[V4L2_M2M_DST].fmt = &formats[0]; ++ q_data[V4L2_M2M_DST].width = 640; ++ q_data[V4L2_M2M_DST].height = 480; ++ q_data[V4L2_M2M_DST].sizeimage = (640 * 480 * 3) / 2; ++ q_data[V4L2_M2M_SRC].field = V4L2_FIELD_INTERLACED_TB; ++ ++ return vb2_queue_init(dst_vq); ++} ++ ++/* ++ * File operations ++ */ ++static int deinterlace_open(struct file *file) ++{ ++ struct deinterlace_dev *pcdev = video_drvdata(file); ++ struct deinterlace_ctx *ctx = NULL; ++ ++ ctx = kzalloc(sizeof *ctx, GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ file->private_data = ctx; ++ ctx->dev = pcdev; ++ ++ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); ++ if (IS_ERR(ctx->m2m_ctx)) { ++ int ret = PTR_ERR(ctx->m2m_ctx); ++ ++ kfree(ctx); ++ return ret; ++ } ++ ++ ctx->xt = kzalloc(sizeof(struct dma_async_tx_descriptor) + ++ sizeof(struct data_chunk), GFP_KERNEL); ++ if (!ctx->xt) { ++ kfree(ctx); ++ return -ENOMEM; ++ } ++ ++ ctx->colorspace = V4L2_COLORSPACE_REC709; ++ ++ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); ++ ++ return 0; ++} ++ ++static int deinterlace_release(struct file *file) ++{ ++ struct deinterlace_dev *pcdev = video_drvdata(file); ++ struct deinterlace_ctx *ctx = file->private_data; ++ ++ dprintk(pcdev, "Releasing instance %p\n", ctx); ++ ++ v4l2_m2m_ctx_release(ctx->m2m_ctx); ++ kfree(ctx->xt); ++ kfree(ctx); ++ ++ return 0; ++} ++ ++static unsigned int deinterlace_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct deinterlace_ctx *ctx = file->private_data; ++ int ret; ++ ++ deinterlace_lock(ctx); ++ ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); ++ deinterlace_unlock(ctx); ++ ++ return ret; ++} ++ ++static int deinterlace_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct deinterlace_ctx *ctx = file->private_data; ++ ++ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); ++} ++ ++static const struct v4l2_file_operations deinterlace_fops = { ++ .owner = THIS_MODULE, ++ .open = deinterlace_open, ++ .release = deinterlace_release, ++ .poll = deinterlace_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = deinterlace_mmap, ++}; ++ ++static struct video_device deinterlace_videodev = { ++ .name = MEM2MEM_NAME, ++ .fops = &deinterlace_fops, ++ .ioctl_ops = &deinterlace_ioctl_ops, ++ .minor = -1, ++ .release = video_device_release, ++ .vfl_dir = VFL_DIR_M2M, ++}; ++ ++static struct v4l2_m2m_ops m2m_ops = { ++ .device_run = deinterlace_device_run, ++ .job_ready = deinterlace_job_ready, ++ .job_abort = deinterlace_job_abort, ++ .lock = deinterlace_lock, ++ .unlock = deinterlace_unlock, ++}; ++ ++static int deinterlace_probe(struct platform_device *pdev) ++{ ++ struct deinterlace_dev *pcdev; ++ struct video_device *vfd; ++ dma_cap_mask_t mask; ++ int ret = 0; ++ ++ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL); ++ if (!pcdev) ++ return -ENOMEM; ++ ++ spin_lock_init(&pcdev->irqlock); ++ ++ dma_cap_zero(mask); ++ dma_cap_set(DMA_INTERLEAVE, mask); ++ pcdev->dma_chan = dma_request_channel(mask, NULL, pcdev); ++ if (!pcdev->dma_chan) ++ goto free_dev; ++ ++ if (!dma_has_cap(DMA_INTERLEAVE, pcdev->dma_chan->device->cap_mask)) { ++ v4l2_err(&pcdev->v4l2_dev, "DMA does not support INTERLEAVE\n"); ++ goto rel_dma; ++ } ++ ++ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); ++ if (ret) ++ goto rel_dma; ++ ++ atomic_set(&pcdev->busy, 0); ++ mutex_init(&pcdev->dev_mutex); ++ ++ vfd = video_device_alloc(); ++ if (!vfd) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n"); ++ ret = -ENOMEM; ++ goto unreg_dev; ++ } ++ ++ *vfd = deinterlace_videodev; ++ vfd->lock = &pcdev->dev_mutex; ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); ++ if (ret) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); ++ goto rel_vdev; ++ } ++ ++ video_set_drvdata(vfd, pcdev); ++ snprintf(vfd->name, sizeof(vfd->name), "%s", deinterlace_videodev.name); ++ pcdev->vfd = vfd; ++ v4l2_info(&pcdev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME ++ " Device registered as /dev/video%d\n", vfd->num); ++ ++ platform_set_drvdata(pdev, pcdev); ++ ++ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); ++ if (IS_ERR(pcdev->alloc_ctx)) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); ++ ret = PTR_ERR(pcdev->alloc_ctx); ++ goto err_ctx; ++ } ++ ++ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); ++ if (IS_ERR(pcdev->m2m_dev)) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); ++ ret = PTR_ERR(pcdev->m2m_dev); ++ goto err_m2m; ++ } ++ ++ return 0; ++ ++ v4l2_m2m_release(pcdev->m2m_dev); ++err_m2m: ++ video_unregister_device(pcdev->vfd); ++err_ctx: ++ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); ++rel_vdev: ++ video_device_release(vfd); ++unreg_dev: ++ v4l2_device_unregister(&pcdev->v4l2_dev); ++rel_dma: ++ dma_release_channel(pcdev->dma_chan); ++free_dev: ++ kfree(pcdev); ++ ++ return ret; ++} ++ ++static int deinterlace_remove(struct platform_device *pdev) ++{ ++ struct deinterlace_dev *pcdev = ++ (struct deinterlace_dev *)platform_get_drvdata(pdev); ++ ++ v4l2_info(&pcdev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME); ++ v4l2_m2m_release(pcdev->m2m_dev); ++ video_unregister_device(pcdev->vfd); ++ v4l2_device_unregister(&pcdev->v4l2_dev); ++ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); ++ dma_release_channel(pcdev->dma_chan); ++ kfree(pcdev); ++ ++ return 0; ++} ++ ++static struct platform_driver deinterlace_pdrv = { ++ .probe = deinterlace_probe, ++ .remove = deinterlace_remove, ++ .driver = { ++ .name = MEM2MEM_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(deinterlace_pdrv); ++ +diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig +new file mode 100644 +index 0000000..bf739e3 +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/Kconfig +@@ -0,0 +1,23 @@ ++config VIDEO_CAFE_CCIC ++ tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" ++ depends on PCI && I2C && VIDEO_V4L2 ++ select VIDEO_OV7670 ++ select VIDEOBUF2_VMALLOC ++ select VIDEOBUF2_DMA_CONTIG ++ ---help--- ++ This is a video4linux2 driver for the Marvell 88ALP01 integrated ++ CMOS camera controller. This is the controller found on first- ++ generation OLPC systems. ++ ++config VIDEO_MMP_CAMERA ++ tristate "Marvell Armada 610 integrated camera controller support" ++ depends on ARCH_MMP && I2C && VIDEO_V4L2 ++ select VIDEO_OV7670 ++ select I2C_GPIO ++ select VIDEOBUF2_DMA_SG ++ ---help--- ++ This is a Video4Linux2 driver for the integrated camera ++ controller found on Marvell Armada 610 application ++ processors (and likely beyond). This is the controller found ++ in OLPC XO 1.75 systems. ++ +diff --git a/drivers/media/platform/marvell-ccic/Makefile b/drivers/media/platform/marvell-ccic/Makefile +new file mode 100644 +index 0000000..05a792c +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o ++cafe_ccic-y := cafe-driver.o mcam-core.o ++ ++obj-$(CONFIG_VIDEO_MMP_CAMERA) += mmp_camera.o ++mmp_camera-y := mmp-driver.o mcam-core.o ++ +diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c +new file mode 100644 +index 0000000..d030f9b +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/cafe-driver.c +@@ -0,0 +1,654 @@ ++/* ++ * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" ++ * multifunction chip. Currently works with the Omnivision OV7670 ++ * sensor. ++ * ++ * The data sheet for this device can be found at: ++ * http://www.marvell.com/products/pc_connectivity/88alp01/ ++ * ++ * Copyright 2006-11 One Laptop Per Child Association, Inc. ++ * Copyright 2006-11 Jonathan Corbet ++ * ++ * Written by Jonathan Corbet, corbet@lwn.net. ++ * ++ * v4l2_device/v4l2_subdev conversion by: ++ * Copyright (C) 2009 Hans Verkuil ++ * ++ * This file may be distributed under the terms of the GNU General ++ * Public License, version 2. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mcam-core.h" ++ ++#define CAFE_VERSION 0x000002 ++ ++ ++/* ++ * Parameters. ++ */ ++MODULE_AUTHOR("Jonathan Corbet "); ++MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver"); ++MODULE_LICENSE("GPL"); ++MODULE_SUPPORTED_DEVICE("Video"); ++ ++ ++ ++ ++struct cafe_camera { ++ int registered; /* Fully initialized? */ ++ struct mcam_camera mcam; ++ struct pci_dev *pdev; ++ wait_queue_head_t smbus_wait; /* Waiting on i2c events */ ++}; ++ ++/* ++ * Most of the camera controller registers are defined in mcam-core.h, ++ * but the Cafe platform has some additional registers of its own; ++ * they are described here. ++ */ ++ ++/* ++ * "General purpose register" has a couple of GPIOs used for sensor ++ * power and reset on OLPC XO 1.0 systems. ++ */ ++#define REG_GPR 0xb4 ++#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */ ++#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */ ++#define GPR_C1 0x00000002 /* Control 1 value */ ++/* ++ * Control 0 is wired to reset on OLPC machines. For ov7x sensors, ++ * it is active low. ++ */ ++#define GPR_C0 0x00000001 /* Control 0 value */ ++ ++/* ++ * These registers control the SMBUS module for communicating ++ * with the sensor. ++ */ ++#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */ ++#define TWSIC0_EN 0x00000001 /* TWSI enable */ ++#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */ ++#define TWSIC0_SID 0x000003fc /* Slave ID */ ++/* ++ * Subtle trickery: the slave ID field starts with bit 2. But the ++ * Linux i2c stack wants to treat the bottommost bit as a separate ++ * read/write bit, which is why slave ID's are usually presented ++ * >>1. For consistency with that behavior, we shift over three ++ * bits instead of two. ++ */ ++#define TWSIC0_SID_SHIFT 3 ++#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */ ++#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */ ++#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */ ++ ++#define REG_TWSIC1 0xbc /* TWSI control 1 */ ++#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */ ++#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */ ++#define TWSIC1_ADDR_SHIFT 16 ++#define TWSIC1_READ 0x01000000 /* Set for read op */ ++#define TWSIC1_WSTAT 0x02000000 /* Write status */ ++#define TWSIC1_RVALID 0x04000000 /* Read data valid */ ++#define TWSIC1_ERROR 0x08000000 /* Something screwed up */ ++ ++/* ++ * Here's the weird global control registers ++ */ ++#define REG_GL_CSR 0x3004 /* Control/status register */ ++#define GCSR_SRS 0x00000001 /* SW Reset set */ ++#define GCSR_SRC 0x00000002 /* SW Reset clear */ ++#define GCSR_MRS 0x00000004 /* Master reset set */ ++#define GCSR_MRC 0x00000008 /* HW Reset clear */ ++#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */ ++#define REG_GL_IMASK 0x300c /* Interrupt mask register */ ++#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */ ++ ++#define REG_GL_FCR 0x3038 /* GPIO functional control register */ ++#define GFCR_GPIO_ON 0x08 /* Camera GPIO enabled */ ++#define REG_GL_GPIOR 0x315c /* GPIO register */ ++#define GGPIO_OUT 0x80000 /* GPIO output */ ++#define GGPIO_VAL 0x00008 /* Output pin value */ ++ ++#define REG_LEN (REG_GL_IMASK + 4) ++ ++ ++/* ++ * Debugging and related. ++ */ ++#define cam_err(cam, fmt, arg...) \ ++ dev_err(&(cam)->pdev->dev, fmt, ##arg); ++#define cam_warn(cam, fmt, arg...) \ ++ dev_warn(&(cam)->pdev->dev, fmt, ##arg); ++ ++/* -------------------------------------------------------------------- */ ++/* ++ * The I2C/SMBUS interface to the camera itself starts here. The ++ * controller handles SMBUS itself, presenting a relatively simple register ++ * interface; all we have to do is to tell it where to route the data. ++ */ ++#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */ ++ ++static inline struct cafe_camera *to_cam(struct v4l2_device *dev) ++{ ++ struct mcam_camera *m = container_of(dev, struct mcam_camera, v4l2_dev); ++ return container_of(m, struct cafe_camera, mcam); ++} ++ ++ ++static int cafe_smbus_write_done(struct mcam_camera *mcam) ++{ ++ unsigned long flags; ++ int c1; ++ ++ /* ++ * We must delay after the interrupt, or the controller gets confused ++ * and never does give us good status. Fortunately, we don't do this ++ * often. ++ */ ++ udelay(20); ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ c1 = mcam_reg_read(mcam, REG_TWSIC1); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT; ++} ++ ++static int cafe_smbus_write_data(struct cafe_camera *cam, ++ u16 addr, u8 command, u8 value) ++{ ++ unsigned int rval; ++ unsigned long flags; ++ struct mcam_camera *mcam = &cam->mcam; ++ ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); ++ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ ++ /* ++ * Marvell sez set clkdiv to all 1's for now. ++ */ ++ rval |= TWSIC0_CLKDIV; ++ mcam_reg_write(mcam, REG_TWSIC0, rval); ++ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */ ++ rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); ++ mcam_reg_write(mcam, REG_TWSIC1, rval); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ ++ /* Unfortunately, reading TWSIC1 too soon after sending a command ++ * causes the device to die. ++ * Use a busy-wait because we often send a large quantity of small ++ * commands at-once; using msleep() would cause a lot of context ++ * switches which take longer than 2ms, resulting in a noticeable ++ * boot-time and capture-start delays. ++ */ ++ mdelay(2); ++ ++ /* ++ * Another sad fact is that sometimes, commands silently complete but ++ * cafe_smbus_write_done() never becomes aware of this. ++ * This happens at random and appears to possible occur with any ++ * command. ++ * We don't understand why this is. We work around this issue ++ * with the timeout in the wait below, assuming that all commands ++ * complete within the timeout. ++ */ ++ wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(mcam), ++ CAFE_SMBUS_TIMEOUT); ++ ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ rval = mcam_reg_read(mcam, REG_TWSIC1); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ ++ if (rval & TWSIC1_WSTAT) { ++ cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr, ++ command, value); ++ return -EIO; ++ } ++ if (rval & TWSIC1_ERROR) { ++ cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr, ++ command, value); ++ return -EIO; ++ } ++ return 0; ++} ++ ++ ++ ++static int cafe_smbus_read_done(struct mcam_camera *mcam) ++{ ++ unsigned long flags; ++ int c1; ++ ++ /* ++ * We must delay after the interrupt, or the controller gets confused ++ * and never does give us good status. Fortunately, we don't do this ++ * often. ++ */ ++ udelay(20); ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ c1 = mcam_reg_read(mcam, REG_TWSIC1); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ return c1 & (TWSIC1_RVALID|TWSIC1_ERROR); ++} ++ ++ ++ ++static int cafe_smbus_read_data(struct cafe_camera *cam, ++ u16 addr, u8 command, u8 *value) ++{ ++ unsigned int rval; ++ unsigned long flags; ++ struct mcam_camera *mcam = &cam->mcam; ++ ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); ++ rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ ++ /* ++ * Marvel sez set clkdiv to all 1's for now. ++ */ ++ rval |= TWSIC0_CLKDIV; ++ mcam_reg_write(mcam, REG_TWSIC0, rval); ++ (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */ ++ rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); ++ mcam_reg_write(mcam, REG_TWSIC1, rval); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ ++ wait_event_timeout(cam->smbus_wait, ++ cafe_smbus_read_done(mcam), CAFE_SMBUS_TIMEOUT); ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ rval = mcam_reg_read(mcam, REG_TWSIC1); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ ++ if (rval & TWSIC1_ERROR) { ++ cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command); ++ return -EIO; ++ } ++ if (!(rval & TWSIC1_RVALID)) { ++ cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr, ++ command); ++ return -EIO; ++ } ++ *value = rval & 0xff; ++ return 0; ++} ++ ++/* ++ * Perform a transfer over SMBUS. This thing is called under ++ * the i2c bus lock, so we shouldn't race with ourselves... ++ */ ++static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, ++ unsigned short flags, char rw, u8 command, ++ int size, union i2c_smbus_data *data) ++{ ++ struct cafe_camera *cam = i2c_get_adapdata(adapter); ++ int ret = -EINVAL; ++ ++ /* ++ * This interface would appear to only do byte data ops. OK ++ * it can do word too, but the cam chip has no use for that. ++ */ ++ if (size != I2C_SMBUS_BYTE_DATA) { ++ cam_err(cam, "funky xfer size %d\n", size); ++ return -EINVAL; ++ } ++ ++ if (rw == I2C_SMBUS_WRITE) ++ ret = cafe_smbus_write_data(cam, addr, command, data->byte); ++ else if (rw == I2C_SMBUS_READ) ++ ret = cafe_smbus_read_data(cam, addr, command, &data->byte); ++ return ret; ++} ++ ++ ++static void cafe_smbus_enable_irq(struct cafe_camera *cam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cam->mcam.dev_lock, flags); ++ mcam_reg_set_bit(&cam->mcam, REG_IRQMASK, TWSIIRQS); ++ spin_unlock_irqrestore(&cam->mcam.dev_lock, flags); ++} ++ ++static u32 cafe_smbus_func(struct i2c_adapter *adapter) ++{ ++ return I2C_FUNC_SMBUS_READ_BYTE_DATA | ++ I2C_FUNC_SMBUS_WRITE_BYTE_DATA; ++} ++ ++static struct i2c_algorithm cafe_smbus_algo = { ++ .smbus_xfer = cafe_smbus_xfer, ++ .functionality = cafe_smbus_func ++}; ++ ++static int cafe_smbus_setup(struct cafe_camera *cam) ++{ ++ struct i2c_adapter *adap; ++ int ret; ++ ++ adap = kzalloc(sizeof(*adap), GFP_KERNEL); ++ if (adap == NULL) ++ return -ENOMEM; ++ cam->mcam.i2c_adapter = adap; ++ cafe_smbus_enable_irq(cam); ++ adap->owner = THIS_MODULE; ++ adap->algo = &cafe_smbus_algo; ++ strcpy(adap->name, "cafe_ccic"); ++ adap->dev.parent = &cam->pdev->dev; ++ i2c_set_adapdata(adap, cam); ++ ret = i2c_add_adapter(adap); ++ if (ret) ++ printk(KERN_ERR "Unable to register cafe i2c adapter\n"); ++ return ret; ++} ++ ++static void cafe_smbus_shutdown(struct cafe_camera *cam) ++{ ++ i2c_del_adapter(cam->mcam.i2c_adapter); ++ kfree(cam->mcam.i2c_adapter); ++} ++ ++ ++/* ++ * Controller-level stuff ++ */ ++ ++static void cafe_ctlr_init(struct mcam_camera *mcam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ /* ++ * Added magic to bring up the hardware on the B-Test board ++ */ ++ mcam_reg_write(mcam, 0x3038, 0x8); ++ mcam_reg_write(mcam, 0x315c, 0x80008); ++ /* ++ * Go through the dance needed to wake the device up. ++ * Note that these registers are global and shared ++ * with the NAND and SD devices. Interaction between the ++ * three still needs to be examined. ++ */ ++ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ ++ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); ++ mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); ++ /* ++ * Here we must wait a bit for the controller to come around. ++ */ ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++ msleep(5); ++ spin_lock_irqsave(&mcam->dev_lock, flags); ++ ++ mcam_reg_write(mcam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); ++ mcam_reg_set_bit(mcam, REG_GL_IMASK, GIMSK_CCIC_EN); ++ /* ++ * Mask all interrupts. ++ */ ++ mcam_reg_write(mcam, REG_IRQMASK, 0); ++ spin_unlock_irqrestore(&mcam->dev_lock, flags); ++} ++ ++ ++static void cafe_ctlr_power_up(struct mcam_camera *mcam) ++{ ++ /* ++ * Part one of the sensor dance: turn the global ++ * GPIO signal on. ++ */ ++ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON); ++ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL); ++ /* ++ * Put the sensor into operational mode (assumes OLPC-style ++ * wiring). Control 0 is reset - set to 1 to operate. ++ * Control 1 is power down, set to 0 to operate. ++ */ ++ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ ++ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); ++} ++ ++static void cafe_ctlr_power_down(struct mcam_camera *mcam) ++{ ++ mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1); ++ mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON); ++ mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT); ++} ++ ++ ++ ++/* ++ * The platform interrupt handler. ++ */ ++static irqreturn_t cafe_irq(int irq, void *data) ++{ ++ struct cafe_camera *cam = data; ++ struct mcam_camera *mcam = &cam->mcam; ++ unsigned int irqs, handled; ++ ++ spin_lock(&mcam->dev_lock); ++ irqs = mcam_reg_read(mcam, REG_IRQSTAT); ++ handled = cam->registered && mccic_irq(mcam, irqs); ++ if (irqs & TWSIIRQS) { ++ mcam_reg_write(mcam, REG_IRQSTAT, TWSIIRQS); ++ wake_up(&cam->smbus_wait); ++ handled = 1; ++ } ++ spin_unlock(&mcam->dev_lock); ++ return IRQ_RETVAL(handled); ++} ++ ++ ++/* -------------------------------------------------------------------------- */ ++/* ++ * PCI interface stuff. ++ */ ++ ++static int cafe_pci_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id) ++{ ++ int ret; ++ struct cafe_camera *cam; ++ struct mcam_camera *mcam; ++ ++ /* ++ * Start putting together one of our big camera structures. ++ */ ++ ret = -ENOMEM; ++ cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); ++ if (cam == NULL) ++ goto out; ++ cam->pdev = pdev; ++ mcam = &cam->mcam; ++ mcam->chip_id = V4L2_IDENT_CAFE; ++ spin_lock_init(&mcam->dev_lock); ++ init_waitqueue_head(&cam->smbus_wait); ++ mcam->plat_power_up = cafe_ctlr_power_up; ++ mcam->plat_power_down = cafe_ctlr_power_down; ++ mcam->dev = &pdev->dev; ++ /* ++ * Set the clock speed for the XO 1; I don't believe this ++ * driver has ever run anywhere else. ++ */ ++ mcam->clock_speed = 45; ++ mcam->use_smbus = 1; ++ /* ++ * Vmalloc mode for buffers is traditional with this driver. ++ * We *might* be able to run DMA_contig, especially on a system ++ * with CMA in it. ++ */ ++ mcam->buffer_mode = B_vmalloc; ++ /* ++ * Get set up on the PCI bus. ++ */ ++ ret = pci_enable_device(pdev); ++ if (ret) ++ goto out_free; ++ pci_set_master(pdev); ++ ++ ret = -EIO; ++ mcam->regs = pci_iomap(pdev, 0, 0); ++ if (!mcam->regs) { ++ printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); ++ goto out_disable; ++ } ++ ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); ++ if (ret) ++ goto out_iounmap; ++ ++ /* ++ * Initialize the controller and leave it powered up. It will ++ * stay that way until the sensor driver shows up. ++ */ ++ cafe_ctlr_init(mcam); ++ cafe_ctlr_power_up(mcam); ++ /* ++ * Set up I2C/SMBUS communications. We have to drop the mutex here ++ * because the sensor could attach in this call chain, leading to ++ * unsightly deadlocks. ++ */ ++ ret = cafe_smbus_setup(cam); ++ if (ret) ++ goto out_pdown; ++ ++ ret = mccic_register(mcam); ++ if (ret == 0) { ++ cam->registered = 1; ++ return 0; ++ } ++ ++ cafe_smbus_shutdown(cam); ++out_pdown: ++ cafe_ctlr_power_down(mcam); ++ free_irq(pdev->irq, cam); ++out_iounmap: ++ pci_iounmap(pdev, mcam->regs); ++out_disable: ++ pci_disable_device(pdev); ++out_free: ++ kfree(cam); ++out: ++ return ret; ++} ++ ++ ++/* ++ * Shut down an initialized device ++ */ ++static void cafe_shutdown(struct cafe_camera *cam) ++{ ++ mccic_shutdown(&cam->mcam); ++ cafe_smbus_shutdown(cam); ++ free_irq(cam->pdev->irq, cam); ++ pci_iounmap(cam->pdev, cam->mcam.regs); ++} ++ ++ ++static void cafe_pci_remove(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct cafe_camera *cam = to_cam(v4l2_dev); ++ ++ if (cam == NULL) { ++ printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); ++ return; ++ } ++ cafe_shutdown(cam); ++ kfree(cam); ++} ++ ++ ++#ifdef CONFIG_PM ++/* ++ * Basic power management. ++ */ ++static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct cafe_camera *cam = to_cam(v4l2_dev); ++ int ret; ++ ++ ret = pci_save_state(pdev); ++ if (ret) ++ return ret; ++ mccic_suspend(&cam->mcam); ++ pci_disable_device(pdev); ++ return 0; ++} ++ ++ ++static int cafe_pci_resume(struct pci_dev *pdev) ++{ ++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); ++ struct cafe_camera *cam = to_cam(v4l2_dev); ++ int ret = 0; ++ ++ pci_restore_state(pdev); ++ ret = pci_enable_device(pdev); ++ ++ if (ret) { ++ cam_warn(cam, "Unable to re-enable device on resume!\n"); ++ return ret; ++ } ++ cafe_ctlr_init(&cam->mcam); ++ return mccic_resume(&cam->mcam); ++} ++ ++#endif /* CONFIG_PM */ ++ ++static struct pci_device_id cafe_ids[] = { ++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, ++ PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, ++ { 0, } ++}; ++ ++MODULE_DEVICE_TABLE(pci, cafe_ids); ++ ++static struct pci_driver cafe_pci_driver = { ++ .name = "cafe1000-ccic", ++ .id_table = cafe_ids, ++ .probe = cafe_pci_probe, ++ .remove = cafe_pci_remove, ++#ifdef CONFIG_PM ++ .suspend = cafe_pci_suspend, ++ .resume = cafe_pci_resume, ++#endif ++}; ++ ++ ++ ++ ++static int __init cafe_init(void) ++{ ++ int ret; ++ ++ printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", ++ CAFE_VERSION); ++ ret = pci_register_driver(&cafe_pci_driver); ++ if (ret) { ++ printk(KERN_ERR "Unable to register cafe_ccic driver\n"); ++ goto out; ++ } ++ ret = 0; ++ ++out: ++ return ret; ++} ++ ++ ++static void __exit cafe_exit(void) ++{ ++ pci_unregister_driver(&cafe_pci_driver); ++} ++ ++module_init(cafe_init); ++module_exit(cafe_exit); +diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c +new file mode 100644 +index 0000000..7012913 +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/mcam-core.c +@@ -0,0 +1,1874 @@ ++/* ++ * The Marvell camera core. This device appears in a number of settings, ++ * so it needs platform-specific support outside of the core. ++ * ++ * Copyright 2011 Jonathan Corbet corbet@lwn.net ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mcam-core.h" ++ ++#ifdef MCAM_MODE_VMALLOC ++/* ++ * Internal DMA buffer management. Since the controller cannot do S/G I/O, ++ * we must have physically contiguous buffers to bring frames into. ++ * These parameters control how many buffers we use, whether we ++ * allocate them at load time (better chance of success, but nails down ++ * memory) or when somebody tries to use the camera (riskier), and, ++ * for load-time allocation, how big they should be. ++ * ++ * The controller can cycle through three buffers. We could use ++ * more by flipping pointers around, but it probably makes little ++ * sense. ++ */ ++ ++static bool alloc_bufs_at_read; ++module_param(alloc_bufs_at_read, bool, 0444); ++MODULE_PARM_DESC(alloc_bufs_at_read, ++ "Non-zero value causes DMA buffers to be allocated when the " ++ "video capture device is read, rather than at module load " ++ "time. This saves memory, but decreases the chances of " ++ "successfully getting those buffers. This parameter is " ++ "only used in the vmalloc buffer mode"); ++ ++static int n_dma_bufs = 3; ++module_param(n_dma_bufs, uint, 0644); ++MODULE_PARM_DESC(n_dma_bufs, ++ "The number of DMA buffers to allocate. Can be either two " ++ "(saves memory, makes timing tighter) or three."); ++ ++static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */ ++module_param(dma_buf_size, uint, 0444); ++MODULE_PARM_DESC(dma_buf_size, ++ "The size of the allocated DMA buffers. If actual operating " ++ "parameters require larger buffers, an attempt to reallocate " ++ "will be made."); ++#else /* MCAM_MODE_VMALLOC */ ++static const bool alloc_bufs_at_read = 0; ++static const int n_dma_bufs = 3; /* Used by S/G_PARM */ ++#endif /* MCAM_MODE_VMALLOC */ ++ ++static bool flip; ++module_param(flip, bool, 0444); ++MODULE_PARM_DESC(flip, ++ "If set, the sensor will be instructed to flip the image " ++ "vertically."); ++ ++static int buffer_mode = -1; ++module_param(buffer_mode, int, 0444); ++MODULE_PARM_DESC(buffer_mode, ++ "Set the buffer mode to be used; default is to go with what " ++ "the platform driver asks for. Set to 0 for vmalloc, 1 for " ++ "DMA contiguous."); ++ ++/* ++ * Status flags. Always manipulated with bit operations. ++ */ ++#define CF_BUF0_VALID 0 /* Buffers valid - first three */ ++#define CF_BUF1_VALID 1 ++#define CF_BUF2_VALID 2 ++#define CF_DMA_ACTIVE 3 /* A frame is incoming */ ++#define CF_CONFIG_NEEDED 4 /* Must configure hardware */ ++#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */ ++#define CF_SG_RESTART 6 /* SG restart needed */ ++ ++#define sensor_call(cam, o, f, args...) \ ++ v4l2_subdev_call(cam->sensor, o, f, ##args) ++ ++static struct mcam_format_struct { ++ __u8 *desc; ++ __u32 pixelformat; ++ int bpp; /* Bytes per pixel */ ++ enum v4l2_mbus_pixelcode mbus_code; ++} mcam_formats[] = { ++ { ++ .desc = "YUYV 4:2:2", ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, ++ .bpp = 2, ++ }, ++ { ++ .desc = "RGB 444", ++ .pixelformat = V4L2_PIX_FMT_RGB444, ++ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, ++ .bpp = 2, ++ }, ++ { ++ .desc = "RGB 565", ++ .pixelformat = V4L2_PIX_FMT_RGB565, ++ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, ++ .bpp = 2, ++ }, ++ { ++ .desc = "Raw RGB Bayer", ++ .pixelformat = V4L2_PIX_FMT_SBGGR8, ++ .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, ++ .bpp = 1 ++ }, ++}; ++#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats) ++ ++static struct mcam_format_struct *mcam_find_format(u32 pixelformat) ++{ ++ unsigned i; ++ ++ for (i = 0; i < N_MCAM_FMTS; i++) ++ if (mcam_formats[i].pixelformat == pixelformat) ++ return mcam_formats + i; ++ /* Not found? Then return the first format. */ ++ return mcam_formats; ++} ++ ++/* ++ * The default format we use until somebody says otherwise. ++ */ ++static const struct v4l2_pix_format mcam_def_pix_format = { ++ .width = VGA_WIDTH, ++ .height = VGA_HEIGHT, ++ .pixelformat = V4L2_PIX_FMT_YUYV, ++ .field = V4L2_FIELD_NONE, ++ .bytesperline = VGA_WIDTH*2, ++ .sizeimage = VGA_WIDTH*VGA_HEIGHT*2, ++}; ++ ++static const enum v4l2_mbus_pixelcode mcam_def_mbus_code = ++ V4L2_MBUS_FMT_YUYV8_2X8; ++ ++ ++/* ++ * The two-word DMA descriptor format used by the Armada 610 and like. There ++ * Is a three-word format as well (set C1_DESC_3WORD) where the third ++ * word is a pointer to the next descriptor, but we don't use it. Two-word ++ * descriptors have to be contiguous in memory. ++ */ ++struct mcam_dma_desc { ++ u32 dma_addr; ++ u32 segment_len; ++}; ++ ++/* ++ * Our buffer type for working with videobuf2. Note that the vb2 ++ * developers have decreed that struct vb2_buffer must be at the ++ * beginning of this structure. ++ */ ++struct mcam_vb_buffer { ++ struct vb2_buffer vb_buf; ++ struct list_head queue; ++ struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */ ++ dma_addr_t dma_desc_pa; /* Descriptor physical address */ ++ int dma_desc_nent; /* Number of mapped descriptors */ ++}; ++ ++static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb) ++{ ++ return container_of(vb, struct mcam_vb_buffer, vb_buf); ++} ++ ++/* ++ * Hand a completed buffer back to user space. ++ */ ++static void mcam_buffer_done(struct mcam_camera *cam, int frame, ++ struct vb2_buffer *vbuf) ++{ ++ vbuf->v4l2_buf.bytesused = cam->pix_format.sizeimage; ++ vbuf->v4l2_buf.sequence = cam->buf_seq[frame]; ++ vb2_set_plane_payload(vbuf, 0, cam->pix_format.sizeimage); ++ vb2_buffer_done(vbuf, VB2_BUF_STATE_DONE); ++} ++ ++ ++ ++/* ++ * Debugging and related. ++ */ ++#define cam_err(cam, fmt, arg...) \ ++ dev_err((cam)->dev, fmt, ##arg); ++#define cam_warn(cam, fmt, arg...) \ ++ dev_warn((cam)->dev, fmt, ##arg); ++#define cam_dbg(cam, fmt, arg...) \ ++ dev_dbg((cam)->dev, fmt, ##arg); ++ ++ ++/* ++ * Flag manipulation helpers ++ */ ++static void mcam_reset_buffers(struct mcam_camera *cam) ++{ ++ int i; ++ ++ cam->next_buf = -1; ++ for (i = 0; i < cam->nbufs; i++) ++ clear_bit(i, &cam->flags); ++} ++ ++static inline int mcam_needs_config(struct mcam_camera *cam) ++{ ++ return test_bit(CF_CONFIG_NEEDED, &cam->flags); ++} ++ ++static void mcam_set_config_needed(struct mcam_camera *cam, int needed) ++{ ++ if (needed) ++ set_bit(CF_CONFIG_NEEDED, &cam->flags); ++ else ++ clear_bit(CF_CONFIG_NEEDED, &cam->flags); ++} ++ ++/* ------------------------------------------------------------------- */ ++/* ++ * Make the controller start grabbing images. Everything must ++ * be set up before doing this. ++ */ ++static void mcam_ctlr_start(struct mcam_camera *cam) ++{ ++ /* set_bit performs a read, so no other barrier should be ++ needed here */ ++ mcam_reg_set_bit(cam, REG_CTRL0, C0_ENABLE); ++} ++ ++static void mcam_ctlr_stop(struct mcam_camera *cam) ++{ ++ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); ++} ++ ++/* ------------------------------------------------------------------- */ ++ ++#ifdef MCAM_MODE_VMALLOC ++/* ++ * Code specific to the vmalloc buffer mode. ++ */ ++ ++/* ++ * Allocate in-kernel DMA buffers for vmalloc mode. ++ */ ++static int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime) ++{ ++ int i; ++ ++ mcam_set_config_needed(cam, 1); ++ if (loadtime) ++ cam->dma_buf_size = dma_buf_size; ++ else ++ cam->dma_buf_size = cam->pix_format.sizeimage; ++ if (n_dma_bufs > 3) ++ n_dma_bufs = 3; ++ ++ cam->nbufs = 0; ++ for (i = 0; i < n_dma_bufs; i++) { ++ cam->dma_bufs[i] = dma_alloc_coherent(cam->dev, ++ cam->dma_buf_size, cam->dma_handles + i, ++ GFP_KERNEL); ++ if (cam->dma_bufs[i] == NULL) { ++ cam_warn(cam, "Failed to allocate DMA buffer\n"); ++ break; ++ } ++ (cam->nbufs)++; ++ } ++ ++ switch (cam->nbufs) { ++ case 1: ++ dma_free_coherent(cam->dev, cam->dma_buf_size, ++ cam->dma_bufs[0], cam->dma_handles[0]); ++ cam->nbufs = 0; ++ case 0: ++ cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); ++ return -ENOMEM; ++ ++ case 2: ++ if (n_dma_bufs > 2) ++ cam_warn(cam, "Will limp along with only 2 buffers\n"); ++ break; ++ } ++ return 0; ++} ++ ++static void mcam_free_dma_bufs(struct mcam_camera *cam) ++{ ++ int i; ++ ++ for (i = 0; i < cam->nbufs; i++) { ++ dma_free_coherent(cam->dev, cam->dma_buf_size, ++ cam->dma_bufs[i], cam->dma_handles[i]); ++ cam->dma_bufs[i] = NULL; ++ } ++ cam->nbufs = 0; ++} ++ ++ ++/* ++ * Set up DMA buffers when operating in vmalloc mode ++ */ ++static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) ++{ ++ /* ++ * Store the first two Y buffers (we aren't supporting ++ * planar formats for now, so no UV bufs). Then either ++ * set the third if it exists, or tell the controller ++ * to just use two. ++ */ ++ mcam_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]); ++ mcam_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]); ++ if (cam->nbufs > 2) { ++ mcam_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]); ++ mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); ++ } else ++ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); ++ if (cam->chip_id == V4L2_IDENT_CAFE) ++ mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */ ++} ++ ++/* ++ * Copy data out to user space in the vmalloc case ++ */ ++static void mcam_frame_tasklet(unsigned long data) ++{ ++ struct mcam_camera *cam = (struct mcam_camera *) data; ++ int i; ++ unsigned long flags; ++ struct mcam_vb_buffer *buf; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ for (i = 0; i < cam->nbufs; i++) { ++ int bufno = cam->next_buf; ++ ++ if (cam->state != S_STREAMING || bufno < 0) ++ break; /* I/O got stopped */ ++ if (++(cam->next_buf) >= cam->nbufs) ++ cam->next_buf = 0; ++ if (!test_bit(bufno, &cam->flags)) ++ continue; ++ if (list_empty(&cam->buffers)) { ++ cam->frame_state.singles++; ++ break; /* Leave it valid, hope for better later */ ++ } ++ cam->frame_state.delivered++; ++ clear_bit(bufno, &cam->flags); ++ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, ++ queue); ++ list_del_init(&buf->queue); ++ /* ++ * Drop the lock during the big copy. This *should* be safe... ++ */ ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ memcpy(vb2_plane_vaddr(&buf->vb_buf, 0), cam->dma_bufs[bufno], ++ cam->pix_format.sizeimage); ++ mcam_buffer_done(cam, bufno, &buf->vb_buf); ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ } ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++} ++ ++ ++/* ++ * Make sure our allocated buffers are up to the task. ++ */ ++static int mcam_check_dma_buffers(struct mcam_camera *cam) ++{ ++ if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage) ++ mcam_free_dma_bufs(cam); ++ if (cam->nbufs == 0) ++ return mcam_alloc_dma_bufs(cam, 0); ++ return 0; ++} ++ ++static void mcam_vmalloc_done(struct mcam_camera *cam, int frame) ++{ ++ tasklet_schedule(&cam->s_tasklet); ++} ++ ++#else /* MCAM_MODE_VMALLOC */ ++ ++static inline int mcam_alloc_dma_bufs(struct mcam_camera *cam, int loadtime) ++{ ++ return 0; ++} ++ ++static inline void mcam_free_dma_bufs(struct mcam_camera *cam) ++{ ++ return; ++} ++ ++static inline int mcam_check_dma_buffers(struct mcam_camera *cam) ++{ ++ return 0; ++} ++ ++ ++ ++#endif /* MCAM_MODE_VMALLOC */ ++ ++ ++#ifdef MCAM_MODE_DMA_CONTIG ++/* ---------------------------------------------------------------------- */ ++/* ++ * DMA-contiguous code. ++ */ ++/* ++ * Set up a contiguous buffer for the given frame. Here also is where ++ * the underrun strategy is set: if there is no buffer available, reuse ++ * the buffer from the other BAR and set the CF_SINGLE_BUFFER flag to ++ * keep the interrupt handler from giving that buffer back to user ++ * space. In this way, we always have a buffer to DMA to and don't ++ * have to try to play games stopping and restarting the controller. ++ */ ++static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame) ++{ ++ struct mcam_vb_buffer *buf; ++ /* ++ * If there are no available buffers, go into single mode ++ */ ++ if (list_empty(&cam->buffers)) { ++ buf = cam->vb_bufs[frame ^ 0x1]; ++ cam->vb_bufs[frame] = buf; ++ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, ++ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); ++ set_bit(CF_SINGLE_BUFFER, &cam->flags); ++ cam->frame_state.singles++; ++ return; ++ } ++ /* ++ * OK, we have a buffer we can use. ++ */ ++ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); ++ list_del_init(&buf->queue); ++ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, ++ vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0)); ++ cam->vb_bufs[frame] = buf; ++ clear_bit(CF_SINGLE_BUFFER, &cam->flags); ++} ++ ++/* ++ * Initial B_DMA_contig setup. ++ */ ++static void mcam_ctlr_dma_contig(struct mcam_camera *cam) ++{ ++ mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); ++ cam->nbufs = 2; ++ mcam_set_contig_buffer(cam, 0); ++ mcam_set_contig_buffer(cam, 1); ++} ++ ++/* ++ * Frame completion handling. ++ */ ++static void mcam_dma_contig_done(struct mcam_camera *cam, int frame) ++{ ++ struct mcam_vb_buffer *buf = cam->vb_bufs[frame]; ++ ++ if (!test_bit(CF_SINGLE_BUFFER, &cam->flags)) { ++ cam->frame_state.delivered++; ++ mcam_buffer_done(cam, frame, &buf->vb_buf); ++ } ++ mcam_set_contig_buffer(cam, frame); ++} ++ ++#endif /* MCAM_MODE_DMA_CONTIG */ ++ ++#ifdef MCAM_MODE_DMA_SG ++/* ---------------------------------------------------------------------- */ ++/* ++ * Scatter/gather-specific code. ++ */ ++ ++/* ++ * Set up the next buffer for S/G I/O; caller should be sure that ++ * the controller is stopped and a buffer is available. ++ */ ++static void mcam_sg_next_buffer(struct mcam_camera *cam) ++{ ++ struct mcam_vb_buffer *buf; ++ ++ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue); ++ list_del_init(&buf->queue); ++ /* ++ * Very Bad Not Good Things happen if you don't clear ++ * C1_DESC_ENA before making any descriptor changes. ++ */ ++ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA); ++ mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa); ++ mcam_reg_write(cam, REG_DESC_LEN_Y, ++ buf->dma_desc_nent*sizeof(struct mcam_dma_desc)); ++ mcam_reg_write(cam, REG_DESC_LEN_U, 0); ++ mcam_reg_write(cam, REG_DESC_LEN_V, 0); ++ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA); ++ cam->vb_bufs[0] = buf; ++} ++ ++/* ++ * Initial B_DMA_sg setup ++ */ ++static void mcam_ctlr_dma_sg(struct mcam_camera *cam) ++{ ++ /* ++ * The list-empty condition can hit us at resume time ++ * if the buffer list was empty when the system was suspended. ++ */ ++ if (list_empty(&cam->buffers)) { ++ set_bit(CF_SG_RESTART, &cam->flags); ++ return; ++ } ++ ++ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD); ++ mcam_sg_next_buffer(cam); ++ cam->nbufs = 3; ++} ++ ++ ++/* ++ * Frame completion with S/G is trickier. We can't muck with ++ * a descriptor chain on the fly, since the controller buffers it ++ * internally. So we have to actually stop and restart; Marvell ++ * says this is the way to do it. ++ * ++ * Of course, stopping is easier said than done; experience shows ++ * that the controller can start a frame *after* C0_ENABLE has been ++ * cleared. So when running in S/G mode, the controller is "stopped" ++ * on receipt of the start-of-frame interrupt. That means we can ++ * safely change the DMA descriptor array here and restart things ++ * (assuming there's another buffer waiting to go). ++ */ ++static void mcam_dma_sg_done(struct mcam_camera *cam, int frame) ++{ ++ struct mcam_vb_buffer *buf = cam->vb_bufs[0]; ++ ++ /* ++ * If we're no longer supposed to be streaming, don't do anything. ++ */ ++ if (cam->state != S_STREAMING) ++ return; ++ /* ++ * If we have another buffer available, put it in and ++ * restart the engine. ++ */ ++ if (!list_empty(&cam->buffers)) { ++ mcam_sg_next_buffer(cam); ++ mcam_ctlr_start(cam); ++ /* ++ * Otherwise set CF_SG_RESTART and the controller will ++ * be restarted once another buffer shows up. ++ */ ++ } else { ++ set_bit(CF_SG_RESTART, &cam->flags); ++ cam->frame_state.singles++; ++ cam->vb_bufs[0] = NULL; ++ } ++ /* ++ * Now we can give the completed frame back to user space. ++ */ ++ cam->frame_state.delivered++; ++ mcam_buffer_done(cam, frame, &buf->vb_buf); ++} ++ ++ ++/* ++ * Scatter/gather mode requires stopping the controller between ++ * frames so we can put in a new DMA descriptor array. If no new ++ * buffer exists at frame completion, the controller is left stopped; ++ * this function is charged with gettig things going again. ++ */ ++static void mcam_sg_restart(struct mcam_camera *cam) ++{ ++ mcam_ctlr_dma_sg(cam); ++ mcam_ctlr_start(cam); ++ clear_bit(CF_SG_RESTART, &cam->flags); ++} ++ ++#else /* MCAM_MODE_DMA_SG */ ++ ++static inline void mcam_sg_restart(struct mcam_camera *cam) ++{ ++ return; ++} ++ ++#endif /* MCAM_MODE_DMA_SG */ ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Buffer-mode-independent controller code. ++ */ ++ ++/* ++ * Image format setup ++ */ ++static void mcam_ctlr_image(struct mcam_camera *cam) ++{ ++ int imgsz; ++ struct v4l2_pix_format *fmt = &cam->pix_format; ++ ++ imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | ++ (fmt->bytesperline & IMGSZ_H_MASK); ++ mcam_reg_write(cam, REG_IMGSIZE, imgsz); ++ mcam_reg_write(cam, REG_IMGOFFSET, 0); ++ /* YPITCH just drops the last two bits */ ++ mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline, ++ IMGP_YP_MASK); ++ /* ++ * Tell the controller about the image format we are using. ++ */ ++ switch (cam->pix_format.pixelformat) { ++ case V4L2_PIX_FMT_YUYV: ++ mcam_reg_write_mask(cam, REG_CTRL0, ++ C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV, ++ C0_DF_MASK); ++ break; ++ ++ case V4L2_PIX_FMT_RGB444: ++ mcam_reg_write_mask(cam, REG_CTRL0, ++ C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB, ++ C0_DF_MASK); ++ /* Alpha value? */ ++ break; ++ ++ case V4L2_PIX_FMT_RGB565: ++ mcam_reg_write_mask(cam, REG_CTRL0, ++ C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR, ++ C0_DF_MASK); ++ break; ++ ++ default: ++ cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat); ++ break; ++ } ++ /* ++ * Make sure it knows we want to use hsync/vsync. ++ */ ++ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, ++ C0_SIFM_MASK); ++} ++ ++ ++/* ++ * Configure the controller for operation; caller holds the ++ * device mutex. ++ */ ++static int mcam_ctlr_configure(struct mcam_camera *cam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ clear_bit(CF_SG_RESTART, &cam->flags); ++ cam->dma_setup(cam); ++ mcam_ctlr_image(cam); ++ mcam_set_config_needed(cam, 0); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ return 0; ++} ++ ++static void mcam_ctlr_irq_enable(struct mcam_camera *cam) ++{ ++ /* ++ * Clear any pending interrupts, since we do not ++ * expect to have I/O active prior to enabling. ++ */ ++ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); ++ mcam_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS); ++} ++ ++static void mcam_ctlr_irq_disable(struct mcam_camera *cam) ++{ ++ mcam_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); ++} ++ ++ ++ ++static void mcam_ctlr_init(struct mcam_camera *cam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ /* ++ * Make sure it's not powered down. ++ */ ++ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); ++ /* ++ * Turn off the enable bit. It sure should be off anyway, ++ * but it's good to be sure. ++ */ ++ mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); ++ /* ++ * Clock the sensor appropriately. Controller clock should ++ * be 48MHz, sensor "typical" value is half that. ++ */ ++ mcam_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++} ++ ++ ++/* ++ * Stop the controller, and don't return until we're really sure that no ++ * further DMA is going on. ++ */ ++static void mcam_ctlr_stop_dma(struct mcam_camera *cam) ++{ ++ unsigned long flags; ++ ++ /* ++ * Theory: stop the camera controller (whether it is operating ++ * or not). Delay briefly just in case we race with the SOF ++ * interrupt, then wait until no DMA is active. ++ */ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ clear_bit(CF_SG_RESTART, &cam->flags); ++ mcam_ctlr_stop(cam); ++ cam->state = S_IDLE; ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ /* ++ * This is a brutally long sleep, but experience shows that ++ * it can take the controller a while to get the message that ++ * it needs to stop grabbing frames. In particular, we can ++ * sometimes (on mmp) get a frame at the end WITHOUT the ++ * start-of-frame indication. ++ */ ++ msleep(150); ++ if (test_bit(CF_DMA_ACTIVE, &cam->flags)) ++ cam_err(cam, "Timeout waiting for DMA to end\n"); ++ /* This would be bad news - what now? */ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ mcam_ctlr_irq_disable(cam); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++} ++ ++/* ++ * Power up and down. ++ */ ++static void mcam_ctlr_power_up(struct mcam_camera *cam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ cam->plat_power_up(cam); ++ mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ msleep(5); /* Just to be sure */ ++} ++ ++static void mcam_ctlr_power_down(struct mcam_camera *cam) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ /* ++ * School of hard knocks department: be sure we do any register ++ * twiddling on the controller *before* calling the platform ++ * power down routine. ++ */ ++ mcam_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); ++ cam->plat_power_down(cam); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++} ++ ++/* -------------------------------------------------------------------- */ ++/* ++ * Communications with the sensor. ++ */ ++ ++static int __mcam_cam_reset(struct mcam_camera *cam) ++{ ++ return sensor_call(cam, core, reset, 0); ++} ++ ++/* ++ * We have found the sensor on the i2c. Let's try to have a ++ * conversation. ++ */ ++static int mcam_cam_init(struct mcam_camera *cam) ++{ ++ struct v4l2_dbg_chip_ident chip; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ if (cam->state != S_NOTREADY) ++ cam_warn(cam, "Cam init with device in funky state %d", ++ cam->state); ++ ret = __mcam_cam_reset(cam); ++ if (ret) ++ goto out; ++ chip.ident = V4L2_IDENT_NONE; ++ chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; ++ chip.match.addr = cam->sensor_addr; ++ ret = sensor_call(cam, core, g_chip_ident, &chip); ++ if (ret) ++ goto out; ++ cam->sensor_type = chip.ident; ++ if (cam->sensor_type != V4L2_IDENT_OV7670) { ++ cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type); ++ ret = -EINVAL; ++ goto out; ++ } ++/* Get/set parameters? */ ++ ret = 0; ++ cam->state = S_IDLE; ++out: ++ mcam_ctlr_power_down(cam); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++/* ++ * Configure the sensor to match the parameters we have. Caller should ++ * hold s_mutex ++ */ ++static int mcam_cam_set_flip(struct mcam_camera *cam) ++{ ++ struct v4l2_control ctrl; ++ ++ memset(&ctrl, 0, sizeof(ctrl)); ++ ctrl.id = V4L2_CID_VFLIP; ++ ctrl.value = flip; ++ return sensor_call(cam, core, s_ctrl, &ctrl); ++} ++ ++ ++static int mcam_cam_configure(struct mcam_camera *cam) ++{ ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int ret; ++ ++ v4l2_fill_mbus_format(&mbus_fmt, &cam->pix_format, cam->mbus_code); ++ ret = sensor_call(cam, core, init, 0); ++ if (ret == 0) ++ ret = sensor_call(cam, video, s_mbus_fmt, &mbus_fmt); ++ /* ++ * OV7670 does weird things if flip is set *before* format... ++ */ ++ ret += mcam_cam_set_flip(cam); ++ return ret; ++} ++ ++/* ++ * Get everything ready, and start grabbing frames. ++ */ ++static int mcam_read_setup(struct mcam_camera *cam) ++{ ++ int ret; ++ unsigned long flags; ++ ++ /* ++ * Configuration. If we still don't have DMA buffers, ++ * make one last, desperate attempt. ++ */ ++ if (cam->buffer_mode == B_vmalloc && cam->nbufs == 0 && ++ mcam_alloc_dma_bufs(cam, 0)) ++ return -ENOMEM; ++ ++ if (mcam_needs_config(cam)) { ++ mcam_cam_configure(cam); ++ ret = mcam_ctlr_configure(cam); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Turn it loose. ++ */ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ clear_bit(CF_DMA_ACTIVE, &cam->flags); ++ mcam_reset_buffers(cam); ++ mcam_ctlr_irq_enable(cam); ++ cam->state = S_STREAMING; ++ if (!test_bit(CF_SG_RESTART, &cam->flags)) ++ mcam_ctlr_start(cam); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------- */ ++/* ++ * Videobuf2 interface code. ++ */ ++ ++static int mcam_vb_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, unsigned int *nbufs, ++ unsigned int *num_planes, unsigned int sizes[], ++ void *alloc_ctxs[]) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vq); ++ int minbufs = (cam->buffer_mode == B_DMA_contig) ? 3 : 2; ++ ++ sizes[0] = cam->pix_format.sizeimage; ++ *num_planes = 1; /* Someday we have to support planar formats... */ ++ if (*nbufs < minbufs) ++ *nbufs = minbufs; ++ if (cam->buffer_mode == B_DMA_contig) ++ alloc_ctxs[0] = cam->vb_alloc_ctx; ++ return 0; ++} ++ ++ ++static void mcam_vb_buf_queue(struct vb2_buffer *vb) ++{ ++ struct mcam_vb_buffer *mvb = vb_to_mvb(vb); ++ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); ++ unsigned long flags; ++ int start; ++ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers); ++ list_add(&mvb->queue, &cam->buffers); ++ if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags)) ++ mcam_sg_restart(cam); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ if (start) ++ mcam_read_setup(cam); ++} ++ ++ ++/* ++ * vb2 uses these to release the mutex when waiting in dqbuf. I'm ++ * not actually sure we need to do this (I'm not sure that vb2_dqbuf() needs ++ * to be called with the mutex held), but better safe than sorry. ++ */ ++static void mcam_vb_wait_prepare(struct vb2_queue *vq) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vq); ++ ++ mutex_unlock(&cam->s_mutex); ++} ++ ++static void mcam_vb_wait_finish(struct vb2_queue *vq) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vq); ++ ++ mutex_lock(&cam->s_mutex); ++} ++ ++/* ++ * These need to be called with the mutex held from vb2 ++ */ ++static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vq); ++ ++ if (cam->state != S_IDLE) { ++ INIT_LIST_HEAD(&cam->buffers); ++ return -EINVAL; ++ } ++ cam->sequence = 0; ++ /* ++ * Videobuf2 sneakily hoards all the buffers and won't ++ * give them to us until *after* streaming starts. But ++ * we can't actually start streaming until we have a ++ * destination. So go into a wait state and hope they ++ * give us buffers soon. ++ */ ++ if (cam->buffer_mode != B_vmalloc && list_empty(&cam->buffers)) { ++ cam->state = S_BUFWAIT; ++ return 0; ++ } ++ return mcam_read_setup(cam); ++} ++ ++static int mcam_vb_stop_streaming(struct vb2_queue *vq) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vq); ++ unsigned long flags; ++ ++ if (cam->state == S_BUFWAIT) { ++ /* They never gave us buffers */ ++ cam->state = S_IDLE; ++ return 0; ++ } ++ if (cam->state != S_STREAMING) ++ return -EINVAL; ++ mcam_ctlr_stop_dma(cam); ++ /* ++ * VB2 reclaims the buffers, so we need to forget ++ * about them. ++ */ ++ spin_lock_irqsave(&cam->dev_lock, flags); ++ INIT_LIST_HEAD(&cam->buffers); ++ spin_unlock_irqrestore(&cam->dev_lock, flags); ++ return 0; ++} ++ ++ ++static const struct vb2_ops mcam_vb2_ops = { ++ .queue_setup = mcam_vb_queue_setup, ++ .buf_queue = mcam_vb_buf_queue, ++ .start_streaming = mcam_vb_start_streaming, ++ .stop_streaming = mcam_vb_stop_streaming, ++ .wait_prepare = mcam_vb_wait_prepare, ++ .wait_finish = mcam_vb_wait_finish, ++}; ++ ++ ++#ifdef MCAM_MODE_DMA_SG ++/* ++ * Scatter/gather mode uses all of the above functions plus a ++ * few extras to deal with DMA mapping. ++ */ ++static int mcam_vb_sg_buf_init(struct vb2_buffer *vb) ++{ ++ struct mcam_vb_buffer *mvb = vb_to_mvb(vb); ++ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); ++ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1; ++ ++ mvb->dma_desc = dma_alloc_coherent(cam->dev, ++ ndesc * sizeof(struct mcam_dma_desc), ++ &mvb->dma_desc_pa, GFP_KERNEL); ++ if (mvb->dma_desc == NULL) { ++ cam_err(cam, "Unable to get DMA descriptor array\n"); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static int mcam_vb_sg_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct mcam_vb_buffer *mvb = vb_to_mvb(vb); ++ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0); ++ struct mcam_dma_desc *desc = mvb->dma_desc; ++ struct scatterlist *sg; ++ int i; ++ ++ mvb->dma_desc_nent = dma_map_sg(cam->dev, sgd->sglist, sgd->num_pages, ++ DMA_FROM_DEVICE); ++ if (mvb->dma_desc_nent <= 0) ++ return -EIO; /* Not sure what's right here */ ++ for_each_sg(sgd->sglist, sg, mvb->dma_desc_nent, i) { ++ desc->dma_addr = sg_dma_address(sg); ++ desc->segment_len = sg_dma_len(sg); ++ desc++; ++ } ++ return 0; ++} ++ ++static int mcam_vb_sg_buf_finish(struct vb2_buffer *vb) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); ++ struct vb2_dma_sg_desc *sgd = vb2_dma_sg_plane_desc(vb, 0); ++ ++ dma_unmap_sg(cam->dev, sgd->sglist, sgd->num_pages, DMA_FROM_DEVICE); ++ return 0; ++} ++ ++static void mcam_vb_sg_buf_cleanup(struct vb2_buffer *vb) ++{ ++ struct mcam_camera *cam = vb2_get_drv_priv(vb->vb2_queue); ++ struct mcam_vb_buffer *mvb = vb_to_mvb(vb); ++ int ndesc = cam->pix_format.sizeimage/PAGE_SIZE + 1; ++ ++ dma_free_coherent(cam->dev, ndesc * sizeof(struct mcam_dma_desc), ++ mvb->dma_desc, mvb->dma_desc_pa); ++} ++ ++ ++static const struct vb2_ops mcam_vb2_sg_ops = { ++ .queue_setup = mcam_vb_queue_setup, ++ .buf_init = mcam_vb_sg_buf_init, ++ .buf_prepare = mcam_vb_sg_buf_prepare, ++ .buf_queue = mcam_vb_buf_queue, ++ .buf_finish = mcam_vb_sg_buf_finish, ++ .buf_cleanup = mcam_vb_sg_buf_cleanup, ++ .start_streaming = mcam_vb_start_streaming, ++ .stop_streaming = mcam_vb_stop_streaming, ++ .wait_prepare = mcam_vb_wait_prepare, ++ .wait_finish = mcam_vb_wait_finish, ++}; ++ ++#endif /* MCAM_MODE_DMA_SG */ ++ ++static int mcam_setup_vb2(struct mcam_camera *cam) ++{ ++ struct vb2_queue *vq = &cam->vb_queue; ++ ++ memset(vq, 0, sizeof(*vq)); ++ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ vq->drv_priv = cam; ++ INIT_LIST_HEAD(&cam->buffers); ++ switch (cam->buffer_mode) { ++ case B_DMA_contig: ++#ifdef MCAM_MODE_DMA_CONTIG ++ vq->ops = &mcam_vb2_ops; ++ vq->mem_ops = &vb2_dma_contig_memops; ++ cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev); ++ vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ cam->dma_setup = mcam_ctlr_dma_contig; ++ cam->frame_complete = mcam_dma_contig_done; ++#endif ++ break; ++ case B_DMA_sg: ++#ifdef MCAM_MODE_DMA_SG ++ vq->ops = &mcam_vb2_sg_ops; ++ vq->mem_ops = &vb2_dma_sg_memops; ++ vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ cam->dma_setup = mcam_ctlr_dma_sg; ++ cam->frame_complete = mcam_dma_sg_done; ++#endif ++ break; ++ case B_vmalloc: ++#ifdef MCAM_MODE_VMALLOC ++ tasklet_init(&cam->s_tasklet, mcam_frame_tasklet, ++ (unsigned long) cam); ++ vq->ops = &mcam_vb2_ops; ++ vq->mem_ops = &vb2_vmalloc_memops; ++ vq->buf_struct_size = sizeof(struct mcam_vb_buffer); ++ vq->io_modes = VB2_MMAP; ++ cam->dma_setup = mcam_ctlr_dma_vmalloc; ++ cam->frame_complete = mcam_vmalloc_done; ++#endif ++ break; ++ } ++ return vb2_queue_init(vq); ++} ++ ++static void mcam_cleanup_vb2(struct mcam_camera *cam) ++{ ++ vb2_queue_release(&cam->vb_queue); ++#ifdef MCAM_MODE_DMA_CONTIG ++ if (cam->buffer_mode == B_DMA_contig) ++ vb2_dma_contig_cleanup_ctx(cam->vb_alloc_ctx); ++#endif ++} ++ ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * The long list of V4L2 ioctl() operations. ++ */ ++ ++static int mcam_vidioc_streamon(struct file *filp, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_streamon(&cam->vb_queue, type); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_streamoff(struct file *filp, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_streamoff(&cam->vb_queue, type); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_reqbufs(struct file *filp, void *priv, ++ struct v4l2_requestbuffers *req) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_reqbufs(&cam->vb_queue, req); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_querybuf(struct file *filp, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_querybuf(&cam->vb_queue, buf); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++static int mcam_vidioc_qbuf(struct file *filp, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_qbuf(&cam->vb_queue, buf); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++static int mcam_vidioc_dqbuf(struct file *filp, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_dqbuf(&cam->vb_queue, buf, filp->f_flags & O_NONBLOCK); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++ ++static int mcam_vidioc_queryctrl(struct file *filp, void *priv, ++ struct v4l2_queryctrl *qc) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, core, queryctrl, qc); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_g_ctrl(struct file *filp, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, core, g_ctrl, ctrl); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_s_ctrl(struct file *filp, void *priv, ++ struct v4l2_control *ctrl) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, core, s_ctrl, ctrl); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strcpy(cap->driver, "marvell_ccic"); ++ strcpy(cap->card, "marvell_ccic"); ++ cap->version = 1; ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | ++ V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; ++ return 0; ++} ++ ++ ++static int mcam_vidioc_enum_fmt_vid_cap(struct file *filp, ++ void *priv, struct v4l2_fmtdesc *fmt) ++{ ++ if (fmt->index >= N_MCAM_FMTS) ++ return -EINVAL; ++ strlcpy(fmt->description, mcam_formats[fmt->index].desc, ++ sizeof(fmt->description)); ++ fmt->pixelformat = mcam_formats[fmt->index].pixelformat; ++ return 0; ++} ++ ++static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct mcam_camera *cam = priv; ++ struct mcam_format_struct *f; ++ struct v4l2_pix_format *pix = &fmt->fmt.pix; ++ struct v4l2_mbus_framefmt mbus_fmt; ++ int ret; ++ ++ f = mcam_find_format(pix->pixelformat); ++ pix->pixelformat = f->pixelformat; ++ v4l2_fill_mbus_format(&mbus_fmt, pix, f->mbus_code); ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt); ++ mutex_unlock(&cam->s_mutex); ++ v4l2_fill_pix_format(pix, &mbus_fmt); ++ pix->bytesperline = pix->width * f->bpp; ++ pix->sizeimage = pix->height * pix->bytesperline; ++ return ret; ++} ++ ++static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, ++ struct v4l2_format *fmt) ++{ ++ struct mcam_camera *cam = priv; ++ struct mcam_format_struct *f; ++ int ret; ++ ++ /* ++ * Can't do anything if the device is not idle ++ * Also can't if there are streaming buffers in place. ++ */ ++ if (cam->state != S_IDLE || cam->vb_queue.num_buffers > 0) ++ return -EBUSY; ++ ++ f = mcam_find_format(fmt->fmt.pix.pixelformat); ++ ++ /* ++ * See if the formatting works in principle. ++ */ ++ ret = mcam_vidioc_try_fmt_vid_cap(filp, priv, fmt); ++ if (ret) ++ return ret; ++ /* ++ * Now we start to change things for real, so let's do it ++ * under lock. ++ */ ++ mutex_lock(&cam->s_mutex); ++ cam->pix_format = fmt->fmt.pix; ++ cam->mbus_code = f->mbus_code; ++ ++ /* ++ * Make sure we have appropriate DMA buffers. ++ */ ++ if (cam->buffer_mode == B_vmalloc) { ++ ret = mcam_check_dma_buffers(cam); ++ if (ret) ++ goto out; ++ } ++ mcam_set_config_needed(cam, 1); ++out: ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++/* ++ * Return our stored notion of how the camera is/should be configured. ++ * The V4l2 spec wants us to be smarter, and actually get this from ++ * the camera (and not mess with it at open time). Someday. ++ */ ++static int mcam_vidioc_g_fmt_vid_cap(struct file *filp, void *priv, ++ struct v4l2_format *f) ++{ ++ struct mcam_camera *cam = priv; ++ ++ f->fmt.pix = cam->pix_format; ++ return 0; ++} ++ ++/* ++ * We only have one input - the sensor - so minimize the nonsense here. ++ */ ++static int mcam_vidioc_enum_input(struct file *filp, void *priv, ++ struct v4l2_input *input) ++{ ++ if (input->index != 0) ++ return -EINVAL; ++ ++ input->type = V4L2_INPUT_TYPE_CAMERA; ++ input->std = V4L2_STD_ALL; /* Not sure what should go here */ ++ strcpy(input->name, "Camera"); ++ return 0; ++} ++ ++static int mcam_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int mcam_vidioc_s_input(struct file *filp, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ return 0; ++} ++ ++/* from vivi.c */ ++static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) ++{ ++ return 0; ++} ++ ++/* ++ * G/S_PARM. Most of this is done by the sensor, but we are ++ * the level which controls the number of read buffers. ++ */ ++static int mcam_vidioc_g_parm(struct file *filp, void *priv, ++ struct v4l2_streamparm *parms) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, video, g_parm, parms); ++ mutex_unlock(&cam->s_mutex); ++ parms->parm.capture.readbuffers = n_dma_bufs; ++ return ret; ++} ++ ++static int mcam_vidioc_s_parm(struct file *filp, void *priv, ++ struct v4l2_streamparm *parms) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, video, s_parm, parms); ++ mutex_unlock(&cam->s_mutex); ++ parms->parm.capture.readbuffers = n_dma_bufs; ++ return ret; ++} ++ ++static int mcam_vidioc_g_chip_ident(struct file *file, void *priv, ++ struct v4l2_dbg_chip_ident *chip) ++{ ++ struct mcam_camera *cam = priv; ++ ++ chip->ident = V4L2_IDENT_NONE; ++ chip->revision = 0; ++ if (v4l2_chip_match_host(&chip->match)) { ++ chip->ident = cam->chip_id; ++ return 0; ++ } ++ return sensor_call(cam, core, g_chip_ident, chip); ++} ++ ++static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, ++ struct v4l2_frmsizeenum *sizes) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, video, enum_framesizes, sizes); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++static int mcam_vidioc_enum_frameintervals(struct file *filp, void *priv, ++ struct v4l2_frmivalenum *interval) ++{ ++ struct mcam_camera *cam = priv; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = sensor_call(cam, video, enum_frameintervals, interval); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++static int mcam_vidioc_g_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct mcam_camera *cam = priv; ++ ++ if (v4l2_chip_match_host(®->match)) { ++ reg->val = mcam_reg_read(cam, reg->reg); ++ reg->size = 4; ++ return 0; ++ } ++ return sensor_call(cam, core, g_register, reg); ++} ++ ++static int mcam_vidioc_s_register(struct file *file, void *priv, ++ struct v4l2_dbg_register *reg) ++{ ++ struct mcam_camera *cam = priv; ++ ++ if (v4l2_chip_match_host(®->match)) { ++ mcam_reg_write(cam, reg->reg, reg->val); ++ return 0; ++ } ++ return sensor_call(cam, core, s_register, reg); ++} ++#endif ++ ++static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { ++ .vidioc_querycap = mcam_vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = mcam_vidioc_enum_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = mcam_vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = mcam_vidioc_s_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = mcam_vidioc_g_fmt_vid_cap, ++ .vidioc_enum_input = mcam_vidioc_enum_input, ++ .vidioc_g_input = mcam_vidioc_g_input, ++ .vidioc_s_input = mcam_vidioc_s_input, ++ .vidioc_s_std = mcam_vidioc_s_std, ++ .vidioc_reqbufs = mcam_vidioc_reqbufs, ++ .vidioc_querybuf = mcam_vidioc_querybuf, ++ .vidioc_qbuf = mcam_vidioc_qbuf, ++ .vidioc_dqbuf = mcam_vidioc_dqbuf, ++ .vidioc_streamon = mcam_vidioc_streamon, ++ .vidioc_streamoff = mcam_vidioc_streamoff, ++ .vidioc_queryctrl = mcam_vidioc_queryctrl, ++ .vidioc_g_ctrl = mcam_vidioc_g_ctrl, ++ .vidioc_s_ctrl = mcam_vidioc_s_ctrl, ++ .vidioc_g_parm = mcam_vidioc_g_parm, ++ .vidioc_s_parm = mcam_vidioc_s_parm, ++ .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes, ++ .vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals, ++ .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident, ++#ifdef CONFIG_VIDEO_ADV_DEBUG ++ .vidioc_g_register = mcam_vidioc_g_register, ++ .vidioc_s_register = mcam_vidioc_s_register, ++#endif ++}; ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Our various file operations. ++ */ ++static int mcam_v4l_open(struct file *filp) ++{ ++ struct mcam_camera *cam = video_drvdata(filp); ++ int ret = 0; ++ ++ filp->private_data = cam; ++ ++ cam->frame_state.frames = 0; ++ cam->frame_state.singles = 0; ++ cam->frame_state.delivered = 0; ++ mutex_lock(&cam->s_mutex); ++ if (cam->users == 0) { ++ ret = mcam_setup_vb2(cam); ++ if (ret) ++ goto out; ++ mcam_ctlr_power_up(cam); ++ __mcam_cam_reset(cam); ++ mcam_set_config_needed(cam, 1); ++ } ++ (cam->users)++; ++out: ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_v4l_release(struct file *filp) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ ++ cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", ++ cam->frame_state.frames, cam->frame_state.singles, ++ cam->frame_state.delivered); ++ mutex_lock(&cam->s_mutex); ++ (cam->users)--; ++ if (cam->users == 0) { ++ mcam_ctlr_stop_dma(cam); ++ mcam_cleanup_vb2(cam); ++ mcam_ctlr_power_down(cam); ++ if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read) ++ mcam_free_dma_bufs(cam); ++ } ++ mutex_unlock(&cam->s_mutex); ++ return 0; ++} ++ ++static ssize_t mcam_v4l_read(struct file *filp, ++ char __user *buffer, size_t len, loff_t *pos) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_read(&cam->vb_queue, buffer, len, pos, ++ filp->f_flags & O_NONBLOCK); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++ ++static unsigned int mcam_v4l_poll(struct file *filp, ++ struct poll_table_struct *pt) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_poll(&cam->vb_queue, filp, pt); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++static int mcam_v4l_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct mcam_camera *cam = filp->private_data; ++ int ret; ++ ++ mutex_lock(&cam->s_mutex); ++ ret = vb2_mmap(&cam->vb_queue, vma); ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++} ++ ++ ++ ++static const struct v4l2_file_operations mcam_v4l_fops = { ++ .owner = THIS_MODULE, ++ .open = mcam_v4l_open, ++ .release = mcam_v4l_release, ++ .read = mcam_v4l_read, ++ .poll = mcam_v4l_poll, ++ .mmap = mcam_v4l_mmap, ++ .unlocked_ioctl = video_ioctl2, ++}; ++ ++ ++/* ++ * This template device holds all of those v4l2 methods; we ++ * clone it for specific real devices. ++ */ ++static struct video_device mcam_v4l_template = { ++ .name = "mcam", ++ .tvnorms = V4L2_STD_NTSC_M, ++ .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ ++ ++ .fops = &mcam_v4l_fops, ++ .ioctl_ops = &mcam_v4l_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Interrupt handler stuff ++ */ ++static void mcam_frame_complete(struct mcam_camera *cam, int frame) ++{ ++ /* ++ * Basic frame housekeeping. ++ */ ++ set_bit(frame, &cam->flags); ++ clear_bit(CF_DMA_ACTIVE, &cam->flags); ++ cam->next_buf = frame; ++ cam->buf_seq[frame] = ++(cam->sequence); ++ cam->frame_state.frames++; ++ /* ++ * "This should never happen" ++ */ ++ if (cam->state != S_STREAMING) ++ return; ++ /* ++ * Process the frame and set up the next one. ++ */ ++ cam->frame_complete(cam, frame); ++} ++ ++ ++/* ++ * The interrupt handler; this needs to be called from the ++ * platform irq handler with the lock held. ++ */ ++int mccic_irq(struct mcam_camera *cam, unsigned int irqs) ++{ ++ unsigned int frame, handled = 0; ++ ++ mcam_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */ ++ /* ++ * Handle any frame completions. There really should ++ * not be more than one of these, or we have fallen ++ * far behind. ++ * ++ * When running in S/G mode, the frame number lacks any ++ * real meaning - there's only one descriptor array - but ++ * the controller still picks a different one to signal ++ * each time. ++ */ ++ for (frame = 0; frame < cam->nbufs; frame++) ++ if (irqs & (IRQ_EOF0 << frame)) { ++ mcam_frame_complete(cam, frame); ++ handled = 1; ++ if (cam->buffer_mode == B_DMA_sg) ++ break; ++ } ++ /* ++ * If a frame starts, note that we have DMA active. This ++ * code assumes that we won't get multiple frame interrupts ++ * at once; may want to rethink that. ++ */ ++ if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) { ++ set_bit(CF_DMA_ACTIVE, &cam->flags); ++ handled = 1; ++ if (cam->buffer_mode == B_DMA_sg) ++ mcam_ctlr_stop(cam); ++ } ++ return handled; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Registration and such. ++ */ ++static struct ov7670_config sensor_cfg = { ++ /* ++ * Exclude QCIF mode, because it only captures a tiny portion ++ * of the sensor FOV ++ */ ++ .min_width = 320, ++ .min_height = 240, ++}; ++ ++ ++int mccic_register(struct mcam_camera *cam) ++{ ++ struct i2c_board_info ov7670_info = { ++ .type = "ov7670", ++ .addr = 0x42 >> 1, ++ .platform_data = &sensor_cfg, ++ }; ++ int ret; ++ ++ /* ++ * Validate the requested buffer mode. ++ */ ++ if (buffer_mode >= 0) ++ cam->buffer_mode = buffer_mode; ++ if (cam->buffer_mode == B_DMA_sg && ++ cam->chip_id == V4L2_IDENT_CAFE) { ++ printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, " ++ "attempting vmalloc mode instead\n"); ++ cam->buffer_mode = B_vmalloc; ++ } ++ if (!mcam_buffer_mode_supported(cam->buffer_mode)) { ++ printk(KERN_ERR "marvell-cam: buffer mode %d unsupported\n", ++ cam->buffer_mode); ++ return -EINVAL; ++ } ++ /* ++ * Register with V4L ++ */ ++ ret = v4l2_device_register(cam->dev, &cam->v4l2_dev); ++ if (ret) ++ return ret; ++ ++ mutex_init(&cam->s_mutex); ++ cam->state = S_NOTREADY; ++ mcam_set_config_needed(cam, 1); ++ cam->pix_format = mcam_def_pix_format; ++ cam->mbus_code = mcam_def_mbus_code; ++ INIT_LIST_HEAD(&cam->buffers); ++ mcam_ctlr_init(cam); ++ ++ /* ++ * Try to find the sensor. ++ */ ++ sensor_cfg.clock_speed = cam->clock_speed; ++ sensor_cfg.use_smbus = cam->use_smbus; ++ cam->sensor_addr = ov7670_info.addr; ++ cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, ++ cam->i2c_adapter, &ov7670_info, NULL); ++ if (cam->sensor == NULL) { ++ ret = -ENODEV; ++ goto out_unregister; ++ } ++ ++ ret = mcam_cam_init(cam); ++ if (ret) ++ goto out_unregister; ++ /* ++ * Get the v4l2 setup done. ++ */ ++ mutex_lock(&cam->s_mutex); ++ cam->vdev = mcam_v4l_template; ++ cam->vdev.debug = 0; ++ cam->vdev.v4l2_dev = &cam->v4l2_dev; ++ ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); ++ if (ret) ++ goto out; ++ video_set_drvdata(&cam->vdev, cam); ++ ++ /* ++ * If so requested, try to get our DMA buffers now. ++ */ ++ if (cam->buffer_mode == B_vmalloc && !alloc_bufs_at_read) { ++ if (mcam_alloc_dma_bufs(cam, 1)) ++ cam_warn(cam, "Unable to alloc DMA buffers at load" ++ " will try again later."); ++ } ++ ++out: ++ mutex_unlock(&cam->s_mutex); ++ return ret; ++out_unregister: ++ v4l2_device_unregister(&cam->v4l2_dev); ++ return ret; ++} ++ ++ ++void mccic_shutdown(struct mcam_camera *cam) ++{ ++ /* ++ * If we have no users (and we really, really should have no ++ * users) the device will already be powered down. Trying to ++ * take it down again will wedge the machine, which is frowned ++ * upon. ++ */ ++ if (cam->users > 0) { ++ cam_warn(cam, "Removing a device with users!\n"); ++ mcam_ctlr_power_down(cam); ++ } ++ vb2_queue_release(&cam->vb_queue); ++ if (cam->buffer_mode == B_vmalloc) ++ mcam_free_dma_bufs(cam); ++ video_unregister_device(&cam->vdev); ++ v4l2_device_unregister(&cam->v4l2_dev); ++} ++ ++/* ++ * Power management ++ */ ++#ifdef CONFIG_PM ++ ++void mccic_suspend(struct mcam_camera *cam) ++{ ++ mutex_lock(&cam->s_mutex); ++ if (cam->users > 0) { ++ enum mcam_state cstate = cam->state; ++ ++ mcam_ctlr_stop_dma(cam); ++ mcam_ctlr_power_down(cam); ++ cam->state = cstate; ++ } ++ mutex_unlock(&cam->s_mutex); ++} ++ ++int mccic_resume(struct mcam_camera *cam) ++{ ++ int ret = 0; ++ ++ mutex_lock(&cam->s_mutex); ++ if (cam->users > 0) { ++ mcam_ctlr_power_up(cam); ++ __mcam_cam_reset(cam); ++ } else { ++ mcam_ctlr_power_down(cam); ++ } ++ mutex_unlock(&cam->s_mutex); ++ ++ set_bit(CF_CONFIG_NEEDED, &cam->flags); ++ if (cam->state == S_STREAMING) { ++ /* ++ * If there was a buffer in the DMA engine at suspend ++ * time, put it back on the queue or we'll forget about it. ++ */ ++ if (cam->buffer_mode == B_DMA_sg && cam->vb_bufs[0]) ++ list_add(&cam->vb_bufs[0]->queue, &cam->buffers); ++ ret = mcam_read_setup(cam); ++ } ++ return ret; ++} ++#endif /* CONFIG_PM */ +diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h +new file mode 100644 +index 0000000..5e802c6 +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/mcam-core.h +@@ -0,0 +1,331 @@ ++/* ++ * Marvell camera core structures. ++ * ++ * Copyright 2011 Jonathan Corbet corbet@lwn.net ++ */ ++#ifndef _MCAM_CORE_H ++#define _MCAM_CORE_H ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * Create our own symbols for the supported buffer modes, but, for now, ++ * base them entirely on which videobuf2 options have been selected. ++ */ ++#if defined(CONFIG_VIDEOBUF2_VMALLOC) || defined(CONFIG_VIDEOBUF2_VMALLOC_MODULE) ++#define MCAM_MODE_VMALLOC 1 ++#endif ++ ++#if defined(CONFIG_VIDEOBUF2_DMA_CONTIG) || defined(CONFIG_VIDEOBUF2_DMA_CONTIG_MODULE) ++#define MCAM_MODE_DMA_CONTIG 1 ++#endif ++ ++#if defined(CONFIG_VIDEOBUF2_DMA_SG) || defined(CONFIG_VIDEOBUF2_DMA_SG_MODULE) ++#define MCAM_MODE_DMA_SG 1 ++#endif ++ ++#if !defined(MCAM_MODE_VMALLOC) && !defined(MCAM_MODE_DMA_CONTIG) && \ ++ !defined(MCAM_MODE_DMA_SG) ++#error One of the videobuf buffer modes must be selected in the config ++#endif ++ ++ ++enum mcam_state { ++ S_NOTREADY, /* Not yet initialized */ ++ S_IDLE, /* Just hanging around */ ++ S_FLAKED, /* Some sort of problem */ ++ S_STREAMING, /* Streaming data */ ++ S_BUFWAIT /* streaming requested but no buffers yet */ ++}; ++#define MAX_DMA_BUFS 3 ++ ++/* ++ * Different platforms work best with different buffer modes, so we ++ * let the platform pick. ++ */ ++enum mcam_buffer_mode { ++ B_vmalloc = 0, ++ B_DMA_contig = 1, ++ B_DMA_sg = 2 ++}; ++ ++/* ++ * Is a given buffer mode supported by the current kernel configuration? ++ */ ++static inline int mcam_buffer_mode_supported(enum mcam_buffer_mode mode) ++{ ++ switch (mode) { ++#ifdef MCAM_MODE_VMALLOC ++ case B_vmalloc: ++#endif ++#ifdef MCAM_MODE_DMA_CONTIG ++ case B_DMA_contig: ++#endif ++#ifdef MCAM_MODE_DMA_SG ++ case B_DMA_sg: ++#endif ++ return 1; ++ default: ++ return 0; ++ } ++} ++ ++/* ++ * Basic frame states ++ */ ++struct mcam_frame_state { ++ unsigned int frames; ++ unsigned int singles; ++ unsigned int delivered; ++}; ++ ++/* ++ * A description of one of our devices. ++ * Locking: controlled by s_mutex. Certain fields, however, require ++ * the dev_lock spinlock; they are marked as such by comments. ++ * dev_lock is also required for access to device registers. ++ */ ++struct mcam_camera { ++ /* ++ * These fields should be set by the platform code prior to ++ * calling mcam_register(). ++ */ ++ struct i2c_adapter *i2c_adapter; ++ unsigned char __iomem *regs; ++ spinlock_t dev_lock; ++ struct device *dev; /* For messages, dma alloc */ ++ unsigned int chip_id; ++ short int clock_speed; /* Sensor clock speed, default 30 */ ++ short int use_smbus; /* SMBUS or straight I2c? */ ++ enum mcam_buffer_mode buffer_mode; ++ /* ++ * Callbacks from the core to the platform code. ++ */ ++ void (*plat_power_up) (struct mcam_camera *cam); ++ void (*plat_power_down) (struct mcam_camera *cam); ++ ++ /* ++ * Everything below here is private to the mcam core and ++ * should not be touched by the platform code. ++ */ ++ struct v4l2_device v4l2_dev; ++ enum mcam_state state; ++ unsigned long flags; /* Buffer status, mainly (dev_lock) */ ++ int users; /* How many open FDs */ ++ ++ struct mcam_frame_state frame_state; /* Frame state counter */ ++ /* ++ * Subsystem structures. ++ */ ++ struct video_device vdev; ++ struct v4l2_subdev *sensor; ++ unsigned short sensor_addr; ++ ++ /* Videobuf2 stuff */ ++ struct vb2_queue vb_queue; ++ struct list_head buffers; /* Available frames */ ++ ++ unsigned int nbufs; /* How many are alloc'd */ ++ int next_buf; /* Next to consume (dev_lock) */ ++ ++ /* DMA buffers - vmalloc mode */ ++#ifdef MCAM_MODE_VMALLOC ++ unsigned int dma_buf_size; /* allocated size */ ++ void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ ++ dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ ++ struct tasklet_struct s_tasklet; ++#endif ++ unsigned int sequence; /* Frame sequence number */ ++ unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */ ++ ++ /* DMA buffers - DMA modes */ ++ struct mcam_vb_buffer *vb_bufs[MAX_DMA_BUFS]; ++ struct vb2_alloc_ctx *vb_alloc_ctx; ++ ++ /* Mode-specific ops, set at open time */ ++ void (*dma_setup)(struct mcam_camera *cam); ++ void (*frame_complete)(struct mcam_camera *cam, int frame); ++ ++ /* Current operating parameters */ ++ u32 sensor_type; /* Currently ov7670 only */ ++ struct v4l2_pix_format pix_format; ++ enum v4l2_mbus_pixelcode mbus_code; ++ ++ /* Locks */ ++ struct mutex s_mutex; /* Access to this structure */ ++}; ++ ++ ++/* ++ * Register I/O functions. These are here because the platform code ++ * may legitimately need to mess with the register space. ++ */ ++/* ++ * Device register I/O ++ */ ++static inline void mcam_reg_write(struct mcam_camera *cam, unsigned int reg, ++ unsigned int val) ++{ ++ iowrite32(val, cam->regs + reg); ++} ++ ++static inline unsigned int mcam_reg_read(struct mcam_camera *cam, ++ unsigned int reg) ++{ ++ return ioread32(cam->regs + reg); ++} ++ ++ ++static inline void mcam_reg_write_mask(struct mcam_camera *cam, unsigned int reg, ++ unsigned int val, unsigned int mask) ++{ ++ unsigned int v = mcam_reg_read(cam, reg); ++ ++ v = (v & ~mask) | (val & mask); ++ mcam_reg_write(cam, reg, v); ++} ++ ++static inline void mcam_reg_clear_bit(struct mcam_camera *cam, ++ unsigned int reg, unsigned int val) ++{ ++ mcam_reg_write_mask(cam, reg, 0, val); ++} ++ ++static inline void mcam_reg_set_bit(struct mcam_camera *cam, ++ unsigned int reg, unsigned int val) ++{ ++ mcam_reg_write_mask(cam, reg, val, val); ++} ++ ++/* ++ * Functions for use by platform code. ++ */ ++int mccic_register(struct mcam_camera *cam); ++int mccic_irq(struct mcam_camera *cam, unsigned int irqs); ++void mccic_shutdown(struct mcam_camera *cam); ++#ifdef CONFIG_PM ++void mccic_suspend(struct mcam_camera *cam); ++int mccic_resume(struct mcam_camera *cam); ++#endif ++ ++/* ++ * Register definitions for the m88alp01 camera interface. Offsets in bytes ++ * as given in the spec. ++ */ ++#define REG_Y0BAR 0x00 ++#define REG_Y1BAR 0x04 ++#define REG_Y2BAR 0x08 ++/* ... */ ++ ++#define REG_IMGPITCH 0x24 /* Image pitch register */ ++#define IMGP_YP_SHFT 2 /* Y pitch params */ ++#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */ ++#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */ ++#define IMGP_UVP_MASK 0x3ffc0000 ++#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */ ++#define IRQ_EOF0 0x00000001 /* End of frame 0 */ ++#define IRQ_EOF1 0x00000002 /* End of frame 1 */ ++#define IRQ_EOF2 0x00000004 /* End of frame 2 */ ++#define IRQ_SOF0 0x00000008 /* Start of frame 0 */ ++#define IRQ_SOF1 0x00000010 /* Start of frame 1 */ ++#define IRQ_SOF2 0x00000020 /* Start of frame 2 */ ++#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */ ++#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */ ++#define IRQ_TWSIR 0x00020000 /* TWSI read */ ++#define IRQ_TWSIE 0x00040000 /* TWSI error */ ++#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE) ++#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2) ++#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW) ++#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */ ++#define REG_IRQSTAT 0x30 /* IRQ status / clear */ ++ ++#define REG_IMGSIZE 0x34 /* Image size */ ++#define IMGSZ_V_MASK 0x1fff0000 ++#define IMGSZ_V_SHIFT 16 ++#define IMGSZ_H_MASK 0x00003fff ++#define REG_IMGOFFSET 0x38 /* IMage offset */ ++ ++#define REG_CTRL0 0x3c /* Control 0 */ ++#define C0_ENABLE 0x00000001 /* Makes the whole thing go */ ++ ++/* Mask for all the format bits */ ++#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */ ++ ++/* RGB ordering */ ++#define C0_RGB4_RGBX 0x00000000 ++#define C0_RGB4_XRGB 0x00000004 ++#define C0_RGB4_BGRX 0x00000008 ++#define C0_RGB4_XBGR 0x0000000c ++#define C0_RGB5_RGGB 0x00000000 ++#define C0_RGB5_GRBG 0x00000004 ++#define C0_RGB5_GBRG 0x00000008 ++#define C0_RGB5_BGGR 0x0000000c ++ ++/* Spec has two fields for DIN and DOUT, but they must match, so ++ combine them here. */ ++#define C0_DF_YUV 0x00000000 /* Data is YUV */ ++#define C0_DF_RGB 0x000000a0 /* ... RGB */ ++#define C0_DF_BAYER 0x00000140 /* ... Bayer */ ++/* 8-8-8 must be missing from the below - ask */ ++#define C0_RGBF_565 0x00000000 ++#define C0_RGBF_444 0x00000800 ++#define C0_RGB_BGR 0x00001000 /* Blue comes first */ ++#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */ ++#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */ ++#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */ ++/* Think that 420 packed must be 111 - ask */ ++#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */ ++#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */ ++#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */ ++#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */ ++#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */ ++#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */ ++#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ ++#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ ++/* Bayer bits 18,19 if needed */ ++#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ ++#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ ++#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ ++#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ ++#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ ++#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ ++#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ ++ ++/* Bits below C1_444ALPHA are not present in Cafe */ ++#define REG_CTRL1 0x40 /* Control 1 */ ++#define C1_CLKGATE 0x00000001 /* Sensor clock gate */ ++#define C1_DESC_ENA 0x00000100 /* DMA descriptor enable */ ++#define C1_DESC_3WORD 0x00000200 /* Three-word descriptors used */ ++#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */ ++#define C1_ALPHA_SHFT 20 ++#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */ ++#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */ ++#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */ ++#define C1_DMAB_MASK 0x06000000 ++#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */ ++#define C1_PWRDWN 0x10000000 /* Power down */ ++ ++#define REG_CLKCTRL 0x88 /* Clock control */ ++#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */ ++ ++/* This appears to be a Cafe-only register */ ++#define REG_UBAR 0xc4 /* Upper base address register */ ++ ++/* Armada 610 DMA descriptor registers */ ++#define REG_DMA_DESC_Y 0x200 ++#define REG_DMA_DESC_U 0x204 ++#define REG_DMA_DESC_V 0x208 ++#define REG_DESC_LEN_Y 0x20c /* Lengths are in bytes */ ++#define REG_DESC_LEN_U 0x210 ++#define REG_DESC_LEN_V 0x214 ++ ++/* ++ * Useful stuff that probably belongs somewhere global. ++ */ ++#define VGA_WIDTH 640 ++#define VGA_HEIGHT 480 ++ ++#endif /* _MCAM_CORE_H */ +diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c +new file mode 100644 +index 0000000..c4c17fe +--- /dev/null ++++ b/drivers/media/platform/marvell-ccic/mmp-driver.c +@@ -0,0 +1,380 @@ ++/* ++ * Support for the camera device found on Marvell MMP processors; known ++ * to work with the Armada 610 as used in the OLPC 1.75 system. ++ * ++ * Copyright 2011 Jonathan Corbet ++ * ++ * This file may be distributed under the terms of the GNU General ++ * Public License, version 2. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mcam-core.h" ++ ++MODULE_ALIAS("platform:mmp-camera"); ++MODULE_AUTHOR("Jonathan Corbet "); ++MODULE_LICENSE("GPL"); ++ ++struct mmp_camera { ++ void *power_regs; ++ struct platform_device *pdev; ++ struct mcam_camera mcam; ++ struct list_head devlist; ++ int irq; ++}; ++ ++static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam) ++{ ++ return container_of(mcam, struct mmp_camera, mcam); ++} ++ ++/* ++ * A silly little infrastructure so we can keep track of our devices. ++ * Chances are that we will never have more than one of them, but ++ * the Armada 610 *does* have two controllers... ++ */ ++ ++static LIST_HEAD(mmpcam_devices); ++static struct mutex mmpcam_devices_lock; ++ ++static void mmpcam_add_device(struct mmp_camera *cam) ++{ ++ mutex_lock(&mmpcam_devices_lock); ++ list_add(&cam->devlist, &mmpcam_devices); ++ mutex_unlock(&mmpcam_devices_lock); ++} ++ ++static void mmpcam_remove_device(struct mmp_camera *cam) ++{ ++ mutex_lock(&mmpcam_devices_lock); ++ list_del(&cam->devlist); ++ mutex_unlock(&mmpcam_devices_lock); ++} ++ ++/* ++ * Platform dev remove passes us a platform_device, and there's ++ * no handy unused drvdata to stash a backpointer in. So just ++ * dig it out of our list. ++ */ ++static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev) ++{ ++ struct mmp_camera *cam; ++ ++ mutex_lock(&mmpcam_devices_lock); ++ list_for_each_entry(cam, &mmpcam_devices, devlist) { ++ if (cam->pdev == pdev) { ++ mutex_unlock(&mmpcam_devices_lock); ++ return cam; ++ } ++ } ++ mutex_unlock(&mmpcam_devices_lock); ++ return NULL; ++} ++ ++ ++ ++ ++/* ++ * Power-related registers; this almost certainly belongs ++ * somewhere else. ++ * ++ * ARMADA 610 register manual, sec 7.2.1, p1842. ++ */ ++#define CPU_SUBSYS_PMU_BASE 0xd4282800 ++#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */ ++#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */ ++ ++/* ++ * Power control. ++ */ ++static void mmpcam_power_up_ctlr(struct mmp_camera *cam) ++{ ++ iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR); ++ iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR); ++ mdelay(1); ++} ++ ++static void mmpcam_power_up(struct mcam_camera *mcam) ++{ ++ struct mmp_camera *cam = mcam_to_cam(mcam); ++ struct mmp_camera_platform_data *pdata; ++/* ++ * Turn on power and clocks to the controller. ++ */ ++ mmpcam_power_up_ctlr(cam); ++/* ++ * Provide power to the sensor. ++ */ ++ mcam_reg_write(mcam, REG_CLKCTRL, 0x60000002); ++ pdata = cam->pdev->dev.platform_data; ++ gpio_set_value(pdata->sensor_power_gpio, 1); ++ mdelay(5); ++ mcam_reg_clear_bit(mcam, REG_CTRL1, 0x10000000); ++ gpio_set_value(pdata->sensor_reset_gpio, 0); /* reset is active low */ ++ mdelay(5); ++ gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */ ++ mdelay(5); ++} ++ ++static void mmpcam_power_down(struct mcam_camera *mcam) ++{ ++ struct mmp_camera *cam = mcam_to_cam(mcam); ++ struct mmp_camera_platform_data *pdata; ++/* ++ * Turn off clocks and set reset lines ++ */ ++ iowrite32(0, cam->power_regs + REG_CCIC_DCGCR); ++ iowrite32(0, cam->power_regs + REG_CCIC_CRCR); ++/* ++ * Shut down the sensor. ++ */ ++ pdata = cam->pdev->dev.platform_data; ++ gpio_set_value(pdata->sensor_power_gpio, 0); ++ gpio_set_value(pdata->sensor_reset_gpio, 0); ++} ++ ++ ++static irqreturn_t mmpcam_irq(int irq, void *data) ++{ ++ struct mcam_camera *mcam = data; ++ unsigned int irqs, handled; ++ ++ spin_lock(&mcam->dev_lock); ++ irqs = mcam_reg_read(mcam, REG_IRQSTAT); ++ handled = mccic_irq(mcam, irqs); ++ spin_unlock(&mcam->dev_lock); ++ return IRQ_RETVAL(handled); ++} ++ ++ ++static int mmpcam_probe(struct platform_device *pdev) ++{ ++ struct mmp_camera *cam; ++ struct mcam_camera *mcam; ++ struct resource *res; ++ struct mmp_camera_platform_data *pdata; ++ int ret; ++ ++ cam = kzalloc(sizeof(*cam), GFP_KERNEL); ++ if (cam == NULL) ++ return -ENOMEM; ++ cam->pdev = pdev; ++ INIT_LIST_HEAD(&cam->devlist); ++ ++ mcam = &cam->mcam; ++ mcam->plat_power_up = mmpcam_power_up; ++ mcam->plat_power_down = mmpcam_power_down; ++ mcam->dev = &pdev->dev; ++ mcam->use_smbus = 0; ++ mcam->chip_id = V4L2_IDENT_ARMADA610; ++ mcam->buffer_mode = B_DMA_sg; ++ spin_lock_init(&mcam->dev_lock); ++ /* ++ * Get our I/O memory. ++ */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "no iomem resource!\n"); ++ ret = -ENODEV; ++ goto out_free; ++ } ++ mcam->regs = ioremap(res->start, resource_size(res)); ++ if (mcam->regs == NULL) { ++ dev_err(&pdev->dev, "MMIO ioremap fail\n"); ++ ret = -ENODEV; ++ goto out_free; ++ } ++ /* ++ * Power/clock memory is elsewhere; get it too. Perhaps this ++ * should really be managed outside of this driver? ++ */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "no power resource!\n"); ++ ret = -ENODEV; ++ goto out_unmap1; ++ } ++ cam->power_regs = ioremap(res->start, resource_size(res)); ++ if (cam->power_regs == NULL) { ++ dev_err(&pdev->dev, "power MMIO ioremap fail\n"); ++ ret = -ENODEV; ++ goto out_unmap1; ++ } ++ /* ++ * Find the i2c adapter. This assumes, of course, that the ++ * i2c bus is already up and functioning. ++ */ ++ pdata = pdev->dev.platform_data; ++ mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device); ++ if (mcam->i2c_adapter == NULL) { ++ ret = -ENODEV; ++ dev_err(&pdev->dev, "No i2c adapter\n"); ++ goto out_unmap2; ++ } ++ /* ++ * Sensor GPIO pins. ++ */ ++ ret = gpio_request(pdata->sensor_power_gpio, "cam-power"); ++ if (ret) { ++ dev_err(&pdev->dev, "Can't get sensor power gpio %d", ++ pdata->sensor_power_gpio); ++ goto out_unmap2; ++ } ++ gpio_direction_output(pdata->sensor_power_gpio, 0); ++ ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset"); ++ if (ret) { ++ dev_err(&pdev->dev, "Can't get sensor reset gpio %d", ++ pdata->sensor_reset_gpio); ++ goto out_gpio; ++ } ++ gpio_direction_output(pdata->sensor_reset_gpio, 0); ++ /* ++ * Power the device up and hand it off to the core. ++ */ ++ mmpcam_power_up(mcam); ++ ret = mccic_register(mcam); ++ if (ret) ++ goto out_gpio2; ++ /* ++ * Finally, set up our IRQ now that the core is ready to ++ * deal with it. ++ */ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (res == NULL) { ++ ret = -ENODEV; ++ goto out_unregister; ++ } ++ cam->irq = res->start; ++ ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED, ++ "mmp-camera", mcam); ++ if (ret == 0) { ++ mmpcam_add_device(cam); ++ return 0; ++ } ++ ++out_unregister: ++ mccic_shutdown(mcam); ++out_gpio2: ++ mmpcam_power_down(mcam); ++ gpio_free(pdata->sensor_reset_gpio); ++out_gpio: ++ gpio_free(pdata->sensor_power_gpio); ++out_unmap2: ++ iounmap(cam->power_regs); ++out_unmap1: ++ iounmap(mcam->regs); ++out_free: ++ kfree(cam); ++ return ret; ++} ++ ++ ++static int mmpcam_remove(struct mmp_camera *cam) ++{ ++ struct mcam_camera *mcam = &cam->mcam; ++ struct mmp_camera_platform_data *pdata; ++ ++ mmpcam_remove_device(cam); ++ free_irq(cam->irq, mcam); ++ mccic_shutdown(mcam); ++ mmpcam_power_down(mcam); ++ pdata = cam->pdev->dev.platform_data; ++ gpio_free(pdata->sensor_reset_gpio); ++ gpio_free(pdata->sensor_power_gpio); ++ iounmap(cam->power_regs); ++ iounmap(mcam->regs); ++ kfree(cam); ++ return 0; ++} ++ ++static int mmpcam_platform_remove(struct platform_device *pdev) ++{ ++ struct mmp_camera *cam = mmpcam_find_device(pdev); ++ ++ if (cam == NULL) ++ return -ENODEV; ++ return mmpcam_remove(cam); ++} ++ ++/* ++ * Suspend/resume support. ++ */ ++#ifdef CONFIG_PM ++ ++static int mmpcam_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct mmp_camera *cam = mmpcam_find_device(pdev); ++ ++ if (state.event != PM_EVENT_SUSPEND) ++ return 0; ++ mccic_suspend(&cam->mcam); ++ return 0; ++} ++ ++static int mmpcam_resume(struct platform_device *pdev) ++{ ++ struct mmp_camera *cam = mmpcam_find_device(pdev); ++ ++ /* ++ * Power up unconditionally just in case the core tries to ++ * touch a register even if nothing was active before; trust ++ * me, it's better this way. ++ */ ++ mmpcam_power_up_ctlr(cam); ++ return mccic_resume(&cam->mcam); ++} ++ ++#endif ++ ++ ++static struct platform_driver mmpcam_driver = { ++ .probe = mmpcam_probe, ++ .remove = mmpcam_platform_remove, ++#ifdef CONFIG_PM ++ .suspend = mmpcam_suspend, ++ .resume = mmpcam_resume, ++#endif ++ .driver = { ++ .name = "mmp-camera", ++ .owner = THIS_MODULE ++ } ++}; ++ ++ ++static int __init mmpcam_init_module(void) ++{ ++ mutex_init(&mmpcam_devices_lock); ++ return platform_driver_register(&mmpcam_driver); ++} ++ ++static void __exit mmpcam_exit_module(void) ++{ ++ platform_driver_unregister(&mmpcam_driver); ++ /* ++ * platform_driver_unregister() should have emptied the list ++ */ ++ if (!list_empty(&mmpcam_devices)) ++ printk(KERN_ERR "mmp_camera leaving devices behind\n"); ++} ++ ++module_init(mmpcam_init_module); ++module_exit(mmpcam_exit_module); +diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c +new file mode 100644 +index 0000000..7487d72 +--- /dev/null ++++ b/drivers/media/platform/mem2mem_testdev.c +@@ -0,0 +1,1126 @@ ++/* ++ * A virtual v4l2-mem2mem example device. ++ * ++ * This is a virtual device driver for testing mem-to-mem videobuf framework. ++ * It simulates a device that uses memory buffers for both source and ++ * destination, processes the data and issues an "irq" (simulated by a timer). ++ * The device is capable of multi-instance, multi-buffer-per-transaction ++ * operation (via the mem2mem framework). ++ * ++ * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. ++ * Pawel Osciak, ++ * Marek Szyprowski, ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev" ++ ++MODULE_DESCRIPTION("Virtual device for mem2mem framework testing"); ++MODULE_AUTHOR("Pawel Osciak, "); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION("0.1.1"); ++ ++#define MIN_W 32 ++#define MIN_H 32 ++#define MAX_W 640 ++#define MAX_H 480 ++#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */ ++ ++/* Flags that indicate a format can be used for capture/output */ ++#define MEM2MEM_CAPTURE (1 << 0) ++#define MEM2MEM_OUTPUT (1 << 1) ++ ++#define MEM2MEM_NAME "m2m-testdev" ++ ++/* Per queue */ ++#define MEM2MEM_DEF_NUM_BUFS VIDEO_MAX_FRAME ++/* In bytes, per queue */ ++#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024) ++ ++/* Default transaction time in msec */ ++#define MEM2MEM_DEF_TRANSTIME 1000 ++/* Default number of buffers per transaction */ ++#define MEM2MEM_DEF_TRANSLEN 1 ++#define MEM2MEM_COLOR_STEP (0xff >> 4) ++#define MEM2MEM_NUM_TILES 8 ++ ++/* Flags that indicate processing mode */ ++#define MEM2MEM_HFLIP (1 << 0) ++#define MEM2MEM_VFLIP (1 << 1) ++ ++#define dprintk(dev, fmt, arg...) \ ++ v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) ++ ++ ++static void m2mtest_dev_release(struct device *dev) ++{} ++ ++static struct platform_device m2mtest_pdev = { ++ .name = MEM2MEM_NAME, ++ .dev.release = m2mtest_dev_release, ++}; ++ ++struct m2mtest_fmt { ++ char *name; ++ u32 fourcc; ++ int depth; ++ /* Types the format can be used for */ ++ u32 types; ++}; ++ ++static struct m2mtest_fmt formats[] = { ++ { ++ .name = "RGB565 (BE)", ++ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ ++ .depth = 16, ++ /* Both capture and output format */ ++ .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .depth = 16, ++ /* Output-only format */ ++ .types = MEM2MEM_OUTPUT, ++ }, ++}; ++ ++#define NUM_FORMATS ARRAY_SIZE(formats) ++ ++/* Per-queue, driver-specific private data */ ++struct m2mtest_q_data { ++ unsigned int width; ++ unsigned int height; ++ unsigned int sizeimage; ++ struct m2mtest_fmt *fmt; ++}; ++ ++enum { ++ V4L2_M2M_SRC = 0, ++ V4L2_M2M_DST = 1, ++}; ++ ++#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000) ++#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001) ++ ++static struct m2mtest_fmt *find_format(struct v4l2_format *f) ++{ ++ struct m2mtest_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < NUM_FORMATS; k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == NUM_FORMATS) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++struct m2mtest_dev { ++ struct v4l2_device v4l2_dev; ++ struct video_device *vfd; ++ ++ atomic_t num_inst; ++ struct mutex dev_mutex; ++ spinlock_t irqlock; ++ ++ struct timer_list timer; ++ ++ struct v4l2_m2m_dev *m2m_dev; ++}; ++ ++struct m2mtest_ctx { ++ struct v4l2_fh fh; ++ struct m2mtest_dev *dev; ++ ++ struct v4l2_ctrl_handler hdl; ++ ++ /* Processed buffers in this transaction */ ++ u8 num_processed; ++ ++ /* Transaction length (i.e. how many buffers per transaction) */ ++ u32 translen; ++ /* Transaction time (i.e. simulated processing time) in milliseconds */ ++ u32 transtime; ++ ++ /* Abort requested by m2m */ ++ int aborting; ++ ++ /* Processing mode */ ++ int mode; ++ ++ enum v4l2_colorspace colorspace; ++ ++ struct v4l2_m2m_ctx *m2m_ctx; ++ ++ /* Source and destination queue data */ ++ struct m2mtest_q_data q_data[2]; ++}; ++ ++static inline struct m2mtest_ctx *file2ctx(struct file *file) ++{ ++ return container_of(file->private_data, struct m2mtest_ctx, fh); ++} ++ ++static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx, ++ enum v4l2_buf_type type) ++{ ++ switch (type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ return &ctx->q_data[V4L2_M2M_SRC]; ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &ctx->q_data[V4L2_M2M_DST]; ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++ ++static int device_process(struct m2mtest_ctx *ctx, ++ struct vb2_buffer *in_vb, ++ struct vb2_buffer *out_vb) ++{ ++ struct m2mtest_dev *dev = ctx->dev; ++ struct m2mtest_q_data *q_data; ++ u8 *p_in, *p_out; ++ int x, y, t, w; ++ int tile_w, bytes_left; ++ int width, height, bytesperline; ++ ++ q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ ++ width = q_data->width; ++ height = q_data->height; ++ bytesperline = (q_data->width * q_data->fmt->depth) >> 3; ++ ++ p_in = vb2_plane_vaddr(in_vb, 0); ++ p_out = vb2_plane_vaddr(out_vb, 0); ++ if (!p_in || !p_out) { ++ v4l2_err(&dev->v4l2_dev, ++ "Acquiring kernel pointers to buffers failed\n"); ++ return -EFAULT; ++ } ++ ++ if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) { ++ v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n"); ++ return -EINVAL; ++ } ++ ++ tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3)) ++ / MEM2MEM_NUM_TILES; ++ bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES; ++ w = 0; ++ ++ switch (ctx->mode) { ++ case MEM2MEM_HFLIP | MEM2MEM_VFLIP: ++ p_out += bytesperline * height - bytes_left; ++ for (y = 0; y < height; ++y) { ++ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { ++ if (w & 0x1) { ++ for (x = 0; x < tile_w; ++x) ++ *--p_out = *p_in++ + ++ MEM2MEM_COLOR_STEP; ++ } else { ++ for (x = 0; x < tile_w; ++x) ++ *--p_out = *p_in++ - ++ MEM2MEM_COLOR_STEP; ++ } ++ ++w; ++ } ++ p_in += bytes_left; ++ p_out -= bytes_left; ++ } ++ break; ++ ++ case MEM2MEM_HFLIP: ++ for (y = 0; y < height; ++y) { ++ p_out += MEM2MEM_NUM_TILES * tile_w; ++ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { ++ if (w & 0x01) { ++ for (x = 0; x < tile_w; ++x) ++ *--p_out = *p_in++ + ++ MEM2MEM_COLOR_STEP; ++ } else { ++ for (x = 0; x < tile_w; ++x) ++ *--p_out = *p_in++ - ++ MEM2MEM_COLOR_STEP; ++ } ++ ++w; ++ } ++ p_in += bytes_left; ++ p_out += bytesperline; ++ } ++ break; ++ ++ case MEM2MEM_VFLIP: ++ p_out += bytesperline * (height - 1); ++ for (y = 0; y < height; ++y) { ++ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { ++ if (w & 0x1) { ++ for (x = 0; x < tile_w; ++x) ++ *p_out++ = *p_in++ + ++ MEM2MEM_COLOR_STEP; ++ } else { ++ for (x = 0; x < tile_w; ++x) ++ *p_out++ = *p_in++ - ++ MEM2MEM_COLOR_STEP; ++ } ++ ++w; ++ } ++ p_in += bytes_left; ++ p_out += bytes_left - 2 * bytesperline; ++ } ++ break; ++ ++ default: ++ for (y = 0; y < height; ++y) { ++ for (t = 0; t < MEM2MEM_NUM_TILES; ++t) { ++ if (w & 0x1) { ++ for (x = 0; x < tile_w; ++x) ++ *p_out++ = *p_in++ + ++ MEM2MEM_COLOR_STEP; ++ } else { ++ for (x = 0; x < tile_w; ++x) ++ *p_out++ = *p_in++ - ++ MEM2MEM_COLOR_STEP; ++ } ++ ++w; ++ } ++ p_in += bytes_left; ++ p_out += bytes_left; ++ } ++ } ++ ++ return 0; ++} ++ ++static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout) ++{ ++ dprintk(dev, "Scheduling a simulated irq\n"); ++ mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout)); ++} ++ ++/* ++ * mem2mem callbacks ++ */ ++ ++/** ++ * job_ready() - check whether an instance is ready to be scheduled to run ++ */ ++static int job_ready(void *priv) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ ++ if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen ++ || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) { ++ dprintk(ctx->dev, "Not enough buffers available\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void job_abort(void *priv) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ ++ /* Will cancel the transaction in the next interrupt handler */ ++ ctx->aborting = 1; ++} ++ ++static void m2mtest_lock(void *priv) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ struct m2mtest_dev *dev = ctx->dev; ++ mutex_lock(&dev->dev_mutex); ++} ++ ++static void m2mtest_unlock(void *priv) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ struct m2mtest_dev *dev = ctx->dev; ++ mutex_unlock(&dev->dev_mutex); ++} ++ ++ ++/* device_run() - prepares and starts the device ++ * ++ * This simulates all the immediate preparations required before starting ++ * a device. This will be called by the framework when it decides to schedule ++ * a particular instance. ++ */ ++static void device_run(void *priv) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ struct m2mtest_dev *dev = ctx->dev; ++ struct vb2_buffer *src_buf, *dst_buf; ++ ++ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ++ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ ++ device_process(ctx, src_buf, dst_buf); ++ ++ /* Run a timer, which simulates a hardware irq */ ++ schedule_irq(dev, ctx->transtime); ++} ++ ++static void device_isr(unsigned long priv) ++{ ++ struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv; ++ struct m2mtest_ctx *curr_ctx; ++ struct vb2_buffer *src_vb, *dst_vb; ++ unsigned long flags; ++ ++ curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev); ++ ++ if (NULL == curr_ctx) { ++ pr_err("Instance released before the end of transaction\n"); ++ return; ++ } ++ ++ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); ++ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); ++ ++ curr_ctx->num_processed++; ++ ++ spin_lock_irqsave(&m2mtest_dev->irqlock, flags); ++ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); ++ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); ++ spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags); ++ ++ if (curr_ctx->num_processed == curr_ctx->translen ++ || curr_ctx->aborting) { ++ dprintk(curr_ctx->dev, "Finishing transaction\n"); ++ curr_ctx->num_processed = 0; ++ v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx); ++ } else { ++ device_run(curr_ctx); ++ } ++} ++ ++/* ++ * video ioctls ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); ++ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", MEM2MEM_NAME); ++ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ return 0; ++} ++ ++static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) ++{ ++ int i, num; ++ struct m2mtest_fmt *fmt; ++ ++ num = 0; ++ ++ for (i = 0; i < NUM_FORMATS; ++i) { ++ if (formats[i].types & type) { ++ /* index-th format of type type found ? */ ++ if (num == f->index) ++ break; ++ /* Correct type but haven't reached our index yet, ++ * just increment per-type index */ ++ ++num; ++ } ++ } ++ ++ if (i < NUM_FORMATS) { ++ /* Format found */ ++ fmt = &formats[i]; ++ strncpy(f->description, fmt->name, sizeof(f->description) - 1); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++ } ++ ++ /* Format not found */ ++ return -EINVAL; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_CAPTURE); ++} ++ ++static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_OUTPUT); ++} ++ ++static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) ++{ ++ struct vb2_queue *vq; ++ struct m2mtest_q_data *q_data; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ ++ f->fmt.pix.width = q_data->width; ++ f->fmt.pix.height = q_data->height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = q_data->fmt->fourcc; ++ f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = q_data->sizeimage; ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(file2ctx(file), f); ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(file2ctx(file), f); ++} ++ ++static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt) ++{ ++ enum v4l2_field field; ++ ++ field = f->fmt.pix.field; ++ ++ if (field == V4L2_FIELD_ANY) ++ field = V4L2_FIELD_NONE; ++ else if (V4L2_FIELD_NONE != field) ++ return -EINVAL; ++ ++ /* V4L2 specification suggests the driver corrects the format struct ++ * if any of the dimensions is unsupported */ ++ f->fmt.pix.field = field; ++ ++ if (f->fmt.pix.height < MIN_H) ++ f->fmt.pix.height = MIN_H; ++ else if (f->fmt.pix.height > MAX_H) ++ f->fmt.pix.height = MAX_H; ++ ++ if (f->fmt.pix.width < MIN_W) ++ f->fmt.pix.width = MIN_W; ++ else if (f->fmt.pix.width > MAX_W) ++ f->fmt.pix.width = MAX_W; ++ ++ f->fmt.pix.width &= ~DIM_ALIGN_MASK; ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct m2mtest_fmt *fmt; ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "Fourcc format (0x%08x) invalid.\n", ++ f->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ f->fmt.pix.colorspace = ctx->colorspace; ++ ++ return vidioc_try_fmt(f, fmt); ++} ++ ++static int vidioc_try_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct m2mtest_fmt *fmt; ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "Fourcc format (0x%08x) invalid.\n", ++ f->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ if (!f->fmt.pix.colorspace) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; ++ ++ return vidioc_try_fmt(f, fmt); ++} ++ ++static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f) ++{ ++ struct m2mtest_q_data *q_data; ++ struct vb2_queue *vq; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ if (!q_data) ++ return -EINVAL; ++ ++ if (vb2_is_busy(vq)) { ++ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ q_data->fmt = find_format(f); ++ q_data->width = f->fmt.pix.width; ++ q_data->height = f->fmt.pix.height; ++ q_data->sizeimage = q_data->width * q_data->height ++ * q_data->fmt->depth >> 3; ++ ++ dprintk(ctx->dev, ++ "Setting format for type %d, wxh: %dx%d, fmt: %d\n", ++ f->type, q_data->width, q_data->height, q_data->fmt->fourcc); ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) ++ return ret; ++ ++ return vidioc_s_fmt(file2ctx(file), f); ++} ++ ++static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_out(file, priv, f); ++ if (ret) ++ return ret; ++ ++ ret = vidioc_s_fmt(file2ctx(file), f); ++ if (!ret) ++ ctx->colorspace = f->fmt.pix.colorspace; ++ return ret; ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbufs) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); ++} ++ ++static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct m2mtest_ctx *ctx = ++ container_of(ctrl->handler, struct m2mtest_ctx, hdl); ++ ++ switch (ctrl->id) { ++ case V4L2_CID_HFLIP: ++ if (ctrl->val) ++ ctx->mode |= MEM2MEM_HFLIP; ++ else ++ ctx->mode &= ~MEM2MEM_HFLIP; ++ break; ++ ++ case V4L2_CID_VFLIP: ++ if (ctrl->val) ++ ctx->mode |= MEM2MEM_VFLIP; ++ else ++ ctx->mode &= ~MEM2MEM_VFLIP; ++ break; ++ ++ case V4L2_CID_TRANS_TIME_MSEC: ++ ctx->transtime = ctrl->val; ++ break; ++ ++ case V4L2_CID_TRANS_NUM_BUFS: ++ ctx->translen = ctrl->val; ++ break; ++ ++ default: ++ v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = { ++ .s_ctrl = m2mtest_s_ctrl, ++}; ++ ++ ++static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, ++ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, ++ ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++ ++/* ++ * Queue operations ++ */ ++ ++static int m2mtest_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq); ++ struct m2mtest_q_data *q_data; ++ unsigned int size, count = *nbuffers; ++ ++ q_data = get_q_data(ctx, vq->type); ++ ++ size = q_data->width * q_data->height * q_data->fmt->depth >> 3; ++ ++ while (size * count > MEM2MEM_VID_MEM_LIMIT) ++ (count)--; ++ ++ *nplanes = 1; ++ *nbuffers = count; ++ sizes[0] = size; ++ ++ /* ++ * videobuf2-vmalloc allocator is context-less so no need to set ++ * alloc_ctxs array. ++ */ ++ ++ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); ++ ++ return 0; ++} ++ ++static int m2mtest_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ struct m2mtest_q_data *q_data; ++ ++ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); ++ ++ q_data = get_q_data(ctx, vb->vb2_queue->type); ++ ++ if (vb2_plane_size(vb, 0) < q_data->sizeimage) { ++ dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage); ++ return -EINVAL; ++ } ++ ++ vb2_set_plane_payload(vb, 0, q_data->sizeimage); ++ ++ return 0; ++} ++ ++static void m2mtest_buf_queue(struct vb2_buffer *vb) ++{ ++ struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); ++} ++ ++static void m2mtest_wait_prepare(struct vb2_queue *q) ++{ ++ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); ++ m2mtest_unlock(ctx); ++} ++ ++static void m2mtest_wait_finish(struct vb2_queue *q) ++{ ++ struct m2mtest_ctx *ctx = vb2_get_drv_priv(q); ++ m2mtest_lock(ctx); ++} ++ ++static struct vb2_ops m2mtest_qops = { ++ .queue_setup = m2mtest_queue_setup, ++ .buf_prepare = m2mtest_buf_prepare, ++ .buf_queue = m2mtest_buf_queue, ++ .wait_prepare = m2mtest_wait_prepare, ++ .wait_finish = m2mtest_wait_finish, ++}; ++ ++static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) ++{ ++ struct m2mtest_ctx *ctx = priv; ++ int ret; ++ ++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ src_vq->io_modes = VB2_MMAP | VB2_DMABUF; ++ src_vq->drv_priv = ctx; ++ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ src_vq->ops = &m2mtest_qops; ++ src_vq->mem_ops = &vb2_vmalloc_memops; ++ ++ ret = vb2_queue_init(src_vq); ++ if (ret) ++ return ret; ++ ++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; ++ dst_vq->drv_priv = ctx; ++ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ dst_vq->ops = &m2mtest_qops; ++ dst_vq->mem_ops = &vb2_vmalloc_memops; ++ ++ return vb2_queue_init(dst_vq); ++} ++ ++static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = { ++ .ops = &m2mtest_ctrl_ops, ++ .id = V4L2_CID_TRANS_TIME_MSEC, ++ .name = "Transaction Time (msec)", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .def = 1001, ++ .min = 1, ++ .max = 10001, ++ .step = 100, ++}; ++ ++static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = { ++ .ops = &m2mtest_ctrl_ops, ++ .id = V4L2_CID_TRANS_NUM_BUFS, ++ .name = "Buffers Per Transaction", ++ .type = V4L2_CTRL_TYPE_INTEGER, ++ .def = 1, ++ .min = 1, ++ .max = MEM2MEM_DEF_NUM_BUFS, ++ .step = 1, ++}; ++ ++/* ++ * File operations ++ */ ++static int m2mtest_open(struct file *file) ++{ ++ struct m2mtest_dev *dev = video_drvdata(file); ++ struct m2mtest_ctx *ctx = NULL; ++ struct v4l2_ctrl_handler *hdl; ++ int rc = 0; ++ ++ if (mutex_lock_interruptible(&dev->dev_mutex)) ++ return -ERESTARTSYS; ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) { ++ rc = -ENOMEM; ++ goto open_unlock; ++ } ++ ++ v4l2_fh_init(&ctx->fh, video_devdata(file)); ++ file->private_data = &ctx->fh; ++ ctx->dev = dev; ++ hdl = &ctx->hdl; ++ v4l2_ctrl_handler_init(hdl, 4); ++ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); ++ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL); ++ v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL); ++ if (hdl->error) { ++ rc = hdl->error; ++ v4l2_ctrl_handler_free(hdl); ++ goto open_unlock; ++ } ++ ctx->fh.ctrl_handler = hdl; ++ v4l2_ctrl_handler_setup(hdl); ++ ++ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0]; ++ ctx->q_data[V4L2_M2M_SRC].width = 640; ++ ctx->q_data[V4L2_M2M_SRC].height = 480; ++ ctx->q_data[V4L2_M2M_SRC].sizeimage = ++ ctx->q_data[V4L2_M2M_SRC].width * ++ ctx->q_data[V4L2_M2M_SRC].height * ++ (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3); ++ ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC]; ++ ctx->colorspace = V4L2_COLORSPACE_REC709; ++ ++ ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); ++ ++ if (IS_ERR(ctx->m2m_ctx)) { ++ rc = PTR_ERR(ctx->m2m_ctx); ++ ++ v4l2_ctrl_handler_free(hdl); ++ kfree(ctx); ++ goto open_unlock; ++ } ++ ++ v4l2_fh_add(&ctx->fh); ++ atomic_inc(&dev->num_inst); ++ ++ dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); ++ ++open_unlock: ++ mutex_unlock(&dev->dev_mutex); ++ return rc; ++} ++ ++static int m2mtest_release(struct file *file) ++{ ++ struct m2mtest_dev *dev = video_drvdata(file); ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ dprintk(dev, "Releasing instance %p\n", ctx); ++ ++ v4l2_fh_del(&ctx->fh); ++ v4l2_fh_exit(&ctx->fh); ++ v4l2_ctrl_handler_free(&ctx->hdl); ++ mutex_lock(&dev->dev_mutex); ++ v4l2_m2m_ctx_release(ctx->m2m_ctx); ++ mutex_unlock(&dev->dev_mutex); ++ kfree(ctx); ++ ++ atomic_dec(&dev->num_inst); ++ ++ return 0; ++} ++ ++static unsigned int m2mtest_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ ++ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); ++} ++ ++static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct m2mtest_dev *dev = video_drvdata(file); ++ struct m2mtest_ctx *ctx = file2ctx(file); ++ int res; ++ ++ if (mutex_lock_interruptible(&dev->dev_mutex)) ++ return -ERESTARTSYS; ++ res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); ++ mutex_unlock(&dev->dev_mutex); ++ return res; ++} ++ ++static const struct v4l2_file_operations m2mtest_fops = { ++ .owner = THIS_MODULE, ++ .open = m2mtest_open, ++ .release = m2mtest_release, ++ .poll = m2mtest_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = m2mtest_mmap, ++}; ++ ++static struct video_device m2mtest_videodev = { ++ .name = MEM2MEM_NAME, ++ .vfl_dir = VFL_DIR_M2M, ++ .fops = &m2mtest_fops, ++ .ioctl_ops = &m2mtest_ioctl_ops, ++ .minor = -1, ++ .release = video_device_release, ++}; ++ ++static struct v4l2_m2m_ops m2m_ops = { ++ .device_run = device_run, ++ .job_ready = job_ready, ++ .job_abort = job_abort, ++ .lock = m2mtest_lock, ++ .unlock = m2mtest_unlock, ++}; ++ ++static int m2mtest_probe(struct platform_device *pdev) ++{ ++ struct m2mtest_dev *dev; ++ struct video_device *vfd; ++ int ret; ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ spin_lock_init(&dev->irqlock); ++ ++ ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); ++ if (ret) ++ return ret; ++ ++ atomic_set(&dev->num_inst, 0); ++ mutex_init(&dev->dev_mutex); ++ ++ vfd = video_device_alloc(); ++ if (!vfd) { ++ v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); ++ ret = -ENOMEM; ++ goto unreg_dev; ++ } ++ ++ *vfd = m2mtest_videodev; ++ vfd->lock = &dev->dev_mutex; ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); ++ goto rel_vdev; ++ } ++ ++ video_set_drvdata(vfd, dev); ++ snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name); ++ dev->vfd = vfd; ++ v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME ++ "Device registered as /dev/video%d\n", vfd->num); ++ ++ setup_timer(&dev->timer, device_isr, (long)dev); ++ platform_set_drvdata(pdev, dev); ++ ++ dev->m2m_dev = v4l2_m2m_init(&m2m_ops); ++ if (IS_ERR(dev->m2m_dev)) { ++ v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n"); ++ ret = PTR_ERR(dev->m2m_dev); ++ goto err_m2m; ++ } ++ ++ return 0; ++ ++err_m2m: ++ v4l2_m2m_release(dev->m2m_dev); ++ video_unregister_device(dev->vfd); ++rel_vdev: ++ video_device_release(vfd); ++unreg_dev: ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++ return ret; ++} ++ ++static int m2mtest_remove(struct platform_device *pdev) ++{ ++ struct m2mtest_dev *dev = ++ (struct m2mtest_dev *)platform_get_drvdata(pdev); ++ ++ v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME); ++ v4l2_m2m_release(dev->m2m_dev); ++ del_timer_sync(&dev->timer); ++ video_unregister_device(dev->vfd); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++ return 0; ++} ++ ++static struct platform_driver m2mtest_pdrv = { ++ .probe = m2mtest_probe, ++ .remove = m2mtest_remove, ++ .driver = { ++ .name = MEM2MEM_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static void __exit m2mtest_exit(void) ++{ ++ platform_driver_unregister(&m2mtest_pdrv); ++ platform_device_unregister(&m2mtest_pdev); ++} ++ ++static int __init m2mtest_init(void) ++{ ++ int ret; ++ ++ ret = platform_device_register(&m2mtest_pdev); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&m2mtest_pdrv); ++ if (ret) ++ platform_device_unregister(&m2mtest_pdev); ++ ++ return 0; ++} ++ ++module_init(m2mtest_init); ++module_exit(m2mtest_exit); ++ +diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c +new file mode 100644 +index 0000000..6b155d7 +--- /dev/null ++++ b/drivers/media/platform/mx2_emmaprp.c +@@ -0,0 +1,1016 @@ ++/* ++ * Support eMMa-PrP through mem2mem framework. ++ * ++ * eMMa-PrP is a piece of HW that allows fetching buffers ++ * from one memory location and do several operations on ++ * them such as scaling or format conversion giving, as a result ++ * a new processed buffer in another memory location. ++ * ++ * Based on mem2mem_testdev.c by Pawel Osciak. ++ * ++ * Copyright (c) 2011 Vista Silicon S.L. ++ * Javier Martin ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version ++ */ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp" ++ ++MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs"); ++MODULE_AUTHOR("Javier Martin v4l2_dev, "%s: " fmt, __func__, ## arg) ++ ++/* EMMA PrP */ ++#define PRP_CNTL 0x00 ++#define PRP_INTR_CNTL 0x04 ++#define PRP_INTRSTATUS 0x08 ++#define PRP_SOURCE_Y_PTR 0x0c ++#define PRP_SOURCE_CB_PTR 0x10 ++#define PRP_SOURCE_CR_PTR 0x14 ++#define PRP_DEST_RGB1_PTR 0x18 ++#define PRP_DEST_RGB2_PTR 0x1c ++#define PRP_DEST_Y_PTR 0x20 ++#define PRP_DEST_CB_PTR 0x24 ++#define PRP_DEST_CR_PTR 0x28 ++#define PRP_SRC_FRAME_SIZE 0x2c ++#define PRP_DEST_CH1_LINE_STRIDE 0x30 ++#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34 ++#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38 ++#define PRP_CH1_OUT_IMAGE_SIZE 0x3c ++#define PRP_CH2_OUT_IMAGE_SIZE 0x40 ++#define PRP_SRC_LINE_STRIDE 0x44 ++#define PRP_CSC_COEF_012 0x48 ++#define PRP_CSC_COEF_345 0x4c ++#define PRP_CSC_COEF_678 0x50 ++#define PRP_CH1_RZ_HORI_COEF1 0x54 ++#define PRP_CH1_RZ_HORI_COEF2 0x58 ++#define PRP_CH1_RZ_HORI_VALID 0x5c ++#define PRP_CH1_RZ_VERT_COEF1 0x60 ++#define PRP_CH1_RZ_VERT_COEF2 0x64 ++#define PRP_CH1_RZ_VERT_VALID 0x68 ++#define PRP_CH2_RZ_HORI_COEF1 0x6c ++#define PRP_CH2_RZ_HORI_COEF2 0x70 ++#define PRP_CH2_RZ_HORI_VALID 0x74 ++#define PRP_CH2_RZ_VERT_COEF1 0x78 ++#define PRP_CH2_RZ_VERT_COEF2 0x7c ++#define PRP_CH2_RZ_VERT_VALID 0x80 ++ ++#define PRP_CNTL_CH1EN (1 << 0) ++#define PRP_CNTL_CH2EN (1 << 1) ++#define PRP_CNTL_CSIEN (1 << 2) ++#define PRP_CNTL_DATA_IN_YUV420 (0 << 3) ++#define PRP_CNTL_DATA_IN_YUV422 (1 << 3) ++#define PRP_CNTL_DATA_IN_RGB16 (2 << 3) ++#define PRP_CNTL_DATA_IN_RGB32 (3 << 3) ++#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5) ++#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5) ++#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5) ++#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5) ++#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7) ++#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7) ++#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7) ++#define PRP_CNTL_CH1_LEN (1 << 9) ++#define PRP_CNTL_CH2_LEN (1 << 10) ++#define PRP_CNTL_SKIP_FRAME (1 << 11) ++#define PRP_CNTL_SWRST (1 << 12) ++#define PRP_CNTL_CLKEN (1 << 13) ++#define PRP_CNTL_WEN (1 << 14) ++#define PRP_CNTL_CH1BYP (1 << 15) ++#define PRP_CNTL_IN_TSKIP(x) ((x) << 16) ++#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19) ++#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22) ++#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25) ++#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27) ++#define PRP_CNTL_CH2B1EN (1 << 29) ++#define PRP_CNTL_CH2B2EN (1 << 30) ++#define PRP_CNTL_CH2FEN (1 << 31) ++ ++#define PRP_SIZE_HEIGHT(x) (x) ++#define PRP_SIZE_WIDTH(x) ((x) << 16) ++ ++/* IRQ Enable and status register */ ++#define PRP_INTR_RDERR (1 << 0) ++#define PRP_INTR_CH1WERR (1 << 1) ++#define PRP_INTR_CH2WERR (1 << 2) ++#define PRP_INTR_CH1FC (1 << 3) ++#define PRP_INTR_CH2FC (1 << 5) ++#define PRP_INTR_LBOVF (1 << 7) ++#define PRP_INTR_CH2OVF (1 << 8) ++ ++#define PRP_INTR_ST_RDERR (1 << 0) ++#define PRP_INTR_ST_CH1WERR (1 << 1) ++#define PRP_INTR_ST_CH2WERR (1 << 2) ++#define PRP_INTR_ST_CH2B2CI (1 << 3) ++#define PRP_INTR_ST_CH2B1CI (1 << 4) ++#define PRP_INTR_ST_CH1B2CI (1 << 5) ++#define PRP_INTR_ST_CH1B1CI (1 << 6) ++#define PRP_INTR_ST_LBOVF (1 << 7) ++#define PRP_INTR_ST_CH2OVF (1 << 8) ++ ++struct emmaprp_fmt { ++ char *name; ++ u32 fourcc; ++ /* Types the format can be used for */ ++ u32 types; ++}; ++ ++static struct emmaprp_fmt formats[] = { ++ { ++ .name = "YUV 4:2:0 Planar", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .types = MEM2MEM_CAPTURE, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .types = MEM2MEM_OUTPUT, ++ }, ++}; ++ ++/* Per-queue, driver-specific private data */ ++struct emmaprp_q_data { ++ unsigned int width; ++ unsigned int height; ++ unsigned int sizeimage; ++ struct emmaprp_fmt *fmt; ++}; ++ ++enum { ++ V4L2_M2M_SRC = 0, ++ V4L2_M2M_DST = 1, ++}; ++ ++#define NUM_FORMATS ARRAY_SIZE(formats) ++ ++static struct emmaprp_fmt *find_format(struct v4l2_format *f) ++{ ++ struct emmaprp_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < NUM_FORMATS; k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == NUM_FORMATS) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++struct emmaprp_dev { ++ struct v4l2_device v4l2_dev; ++ struct video_device *vfd; ++ ++ struct mutex dev_mutex; ++ spinlock_t irqlock; ++ ++ int irq_emma; ++ void __iomem *base_emma; ++ struct clk *clk_emma_ahb, *clk_emma_ipg; ++ struct resource *res_emma; ++ ++ struct v4l2_m2m_dev *m2m_dev; ++ struct vb2_alloc_ctx *alloc_ctx; ++}; ++ ++struct emmaprp_ctx { ++ struct emmaprp_dev *dev; ++ /* Abort requested by m2m */ ++ int aborting; ++ struct emmaprp_q_data q_data[2]; ++ struct v4l2_m2m_ctx *m2m_ctx; ++}; ++ ++static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx, ++ enum v4l2_buf_type type) ++{ ++ switch (type) { ++ case V4L2_BUF_TYPE_VIDEO_OUTPUT: ++ return &(ctx->q_data[V4L2_M2M_SRC]); ++ case V4L2_BUF_TYPE_VIDEO_CAPTURE: ++ return &(ctx->q_data[V4L2_M2M_DST]); ++ default: ++ BUG(); ++ } ++ return NULL; ++} ++ ++/* ++ * mem2mem callbacks ++ */ ++static void emmaprp_job_abort(void *priv) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ struct emmaprp_dev *pcdev = ctx->dev; ++ ++ ctx->aborting = 1; ++ ++ dprintk(pcdev, "Aborting task\n"); ++ ++ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx); ++} ++ ++static void emmaprp_lock(void *priv) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ struct emmaprp_dev *pcdev = ctx->dev; ++ mutex_lock(&pcdev->dev_mutex); ++} ++ ++static void emmaprp_unlock(void *priv) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ struct emmaprp_dev *pcdev = ctx->dev; ++ mutex_unlock(&pcdev->dev_mutex); ++} ++ ++static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev) ++{ ++ dprintk(pcdev, ++ "eMMa-PrP Registers:\n" ++ " SOURCE_Y_PTR = 0x%08X\n" ++ " SRC_FRAME_SIZE = 0x%08X\n" ++ " DEST_Y_PTR = 0x%08X\n" ++ " DEST_CR_PTR = 0x%08X\n" ++ " DEST_CB_PTR = 0x%08X\n" ++ " CH2_OUT_IMAGE_SIZE = 0x%08X\n" ++ " CNTL = 0x%08X\n", ++ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR), ++ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE), ++ readl(pcdev->base_emma + PRP_DEST_Y_PTR), ++ readl(pcdev->base_emma + PRP_DEST_CR_PTR), ++ readl(pcdev->base_emma + PRP_DEST_CB_PTR), ++ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE), ++ readl(pcdev->base_emma + PRP_CNTL)); ++} ++ ++static void emmaprp_device_run(void *priv) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ struct emmaprp_q_data *s_q_data, *d_q_data; ++ struct vb2_buffer *src_buf, *dst_buf; ++ struct emmaprp_dev *pcdev = ctx->dev; ++ unsigned int s_width, s_height; ++ unsigned int d_width, d_height; ++ unsigned int d_size; ++ dma_addr_t p_in, p_out; ++ u32 tmp; ++ ++ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); ++ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); ++ ++ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ s_width = s_q_data->width; ++ s_height = s_q_data->height; ++ ++ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ d_width = d_q_data->width; ++ d_height = d_q_data->height; ++ d_size = d_width * d_height; ++ ++ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0); ++ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0); ++ if (!p_in || !p_out) { ++ v4l2_err(&pcdev->v4l2_dev, ++ "Acquiring kernel pointers to buffers failed\n"); ++ return; ++ } ++ ++ /* Input frame parameters */ ++ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR); ++ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height), ++ pcdev->base_emma + PRP_SRC_FRAME_SIZE); ++ ++ /* Output frame parameters */ ++ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR); ++ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR); ++ writel(p_out + d_size + (d_size >> 2), ++ pcdev->base_emma + PRP_DEST_CR_PTR); ++ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height), ++ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE); ++ ++ /* IRQ configuration */ ++ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL); ++ writel(tmp | PRP_INTR_RDERR | ++ PRP_INTR_CH2WERR | ++ PRP_INTR_CH2FC, ++ pcdev->base_emma + PRP_INTR_CNTL); ++ ++ emmaprp_dump_regs(pcdev); ++ ++ /* Enable transfer */ ++ tmp = readl(pcdev->base_emma + PRP_CNTL); ++ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 | ++ PRP_CNTL_DATA_IN_YUV422 | ++ PRP_CNTL_CH2EN, ++ pcdev->base_emma + PRP_CNTL); ++} ++ ++static irqreturn_t emmaprp_irq(int irq_emma, void *data) ++{ ++ struct emmaprp_dev *pcdev = data; ++ struct emmaprp_ctx *curr_ctx; ++ struct vb2_buffer *src_vb, *dst_vb; ++ unsigned long flags; ++ u32 irqst; ++ ++ /* Check irq flags and clear irq */ ++ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS); ++ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS); ++ dprintk(pcdev, "irqst = 0x%08x\n", irqst); ++ ++ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev); ++ if (curr_ctx == NULL) { ++ pr_err("Instance released before the end of transaction\n"); ++ return IRQ_HANDLED; ++ } ++ ++ if (!curr_ctx->aborting) { ++ if ((irqst & PRP_INTR_ST_RDERR) || ++ (irqst & PRP_INTR_ST_CH2WERR)) { ++ pr_err("PrP bus error occurred, this transfer is probably corrupted\n"); ++ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL); ++ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */ ++ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx); ++ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx); ++ ++ spin_lock_irqsave(&pcdev->irqlock, flags); ++ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE); ++ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE); ++ spin_unlock_irqrestore(&pcdev->irqlock, flags); ++ } ++ } ++ ++ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * video ioctls ++ */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1); ++ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1); ++ /* ++ * This is only a mem-to-mem video device. The capture and output ++ * device capability flags are left only for backward compatibility ++ * and are scheduled for removal. ++ */ ++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | ++ V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; ++ return 0; ++} ++ ++static int enum_fmt(struct v4l2_fmtdesc *f, u32 type) ++{ ++ int i, num; ++ struct emmaprp_fmt *fmt; ++ ++ num = 0; ++ ++ for (i = 0; i < NUM_FORMATS; ++i) { ++ if (formats[i].types & type) { ++ /* index-th format of type type found ? */ ++ if (num == f->index) ++ break; ++ /* Correct type but haven't reached our index yet, ++ * just increment per-type index */ ++ ++num; ++ } ++ } ++ ++ if (i < NUM_FORMATS) { ++ /* Format found */ ++ fmt = &formats[i]; ++ strlcpy(f->description, fmt->name, sizeof(f->description) - 1); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++ } ++ ++ /* Format not found */ ++ return -EINVAL; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_CAPTURE); ++} ++ ++static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ return enum_fmt(f, MEM2MEM_OUTPUT); ++} ++ ++static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) ++{ ++ struct vb2_queue *vq; ++ struct emmaprp_q_data *q_data; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ ++ f->fmt.pix.width = q_data->width; ++ f->fmt.pix.height = q_data->height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = q_data->fmt->fourcc; ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) ++ f->fmt.pix.bytesperline = q_data->width * 3 / 2; ++ else /* YUYV */ ++ f->fmt.pix.bytesperline = q_data->width * 2; ++ f->fmt.pix.sizeimage = q_data->sizeimage; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(priv, f); ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ return vidioc_g_fmt(priv, f); ++} ++ ++static int vidioc_try_fmt(struct v4l2_format *f) ++{ ++ enum v4l2_field field; ++ ++ ++ if (!find_format(f)) ++ return -EINVAL; ++ ++ field = f->fmt.pix.field; ++ if (field == V4L2_FIELD_ANY) ++ field = V4L2_FIELD_NONE; ++ else if (V4L2_FIELD_NONE != field) ++ return -EINVAL; ++ ++ /* V4L2 specification suggests the driver corrects the format struct ++ * if any of the dimensions is unsupported */ ++ f->fmt.pix.field = field; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, ++ W_ALIGN_YUV420, &f->fmt.pix.height, ++ MIN_H, MAX_H, H_ALIGN, S_ALIGN); ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2; ++ } else { ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, ++ W_ALIGN_OTHERS, &f->fmt.pix.height, ++ MIN_H, MAX_H, H_ALIGN, S_ALIGN); ++ f->fmt.pix.bytesperline = f->fmt.pix.width * 2; ++ } ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct emmaprp_fmt *fmt; ++ struct emmaprp_ctx *ctx = priv; ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "Fourcc format (0x%08x) invalid.\n", ++ f->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ ++ return vidioc_try_fmt(f); ++} ++ ++static int vidioc_try_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct emmaprp_fmt *fmt; ++ struct emmaprp_ctx *ctx = priv; ++ ++ fmt = find_format(f); ++ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) { ++ v4l2_err(&ctx->dev->v4l2_dev, ++ "Fourcc format (0x%08x) invalid.\n", ++ f->fmt.pix.pixelformat); ++ return -EINVAL; ++ } ++ ++ return vidioc_try_fmt(f); ++} ++ ++static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f) ++{ ++ struct emmaprp_q_data *q_data; ++ struct vb2_queue *vq; ++ int ret; ++ ++ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); ++ if (!vq) ++ return -EINVAL; ++ ++ q_data = get_q_data(ctx, f->type); ++ if (!q_data) ++ return -EINVAL; ++ ++ if (vb2_is_busy(vq)) { ++ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ ret = vidioc_try_fmt(f); ++ if (ret) ++ return ret; ++ ++ q_data->fmt = find_format(f); ++ q_data->width = f->fmt.pix.width; ++ q_data->height = f->fmt.pix.height; ++ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) ++ q_data->sizeimage = q_data->width * q_data->height * 3 / 2; ++ else /* YUYV */ ++ q_data->sizeimage = q_data->width * q_data->height * 2; ++ ++ dprintk(ctx->dev, ++ "Setting format for type %d, wxh: %dx%d, fmt: %d\n", ++ f->type, q_data->width, q_data->height, q_data->fmt->fourcc); ++ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) ++ return ret; ++ ++ return vidioc_s_fmt(priv, f); ++} ++ ++static int vidioc_s_fmt_vid_out(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ ++ ret = vidioc_try_fmt_vid_out(file, priv, f); ++ if (ret) ++ return ret; ++ ++ return vidioc_s_fmt(priv, f); ++} ++ ++static int vidioc_reqbufs(struct file *file, void *priv, ++ struct v4l2_requestbuffers *reqbufs) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); ++} ++ ++static int vidioc_querybuf(struct file *file, void *priv, ++ struct v4l2_buffer *buf) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); ++} ++ ++static int vidioc_streamon(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); ++} ++ ++static int vidioc_streamoff(struct file *file, void *priv, ++ enum v4l2_buf_type type) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ ++ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); ++} ++ ++static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = { ++ .vidioc_querycap = vidioc_querycap, ++ ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, ++ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out, ++ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out, ++ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out, ++ ++ .vidioc_reqbufs = vidioc_reqbufs, ++ .vidioc_querybuf = vidioc_querybuf, ++ ++ .vidioc_qbuf = vidioc_qbuf, ++ .vidioc_dqbuf = vidioc_dqbuf, ++ ++ .vidioc_streamon = vidioc_streamon, ++ .vidioc_streamoff = vidioc_streamoff, ++}; ++ ++ ++/* ++ * Queue operations ++ */ ++static int emmaprp_queue_setup(struct vb2_queue *vq, ++ const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq); ++ struct emmaprp_q_data *q_data; ++ unsigned int size, count = *nbuffers; ++ ++ q_data = get_q_data(ctx, vq->type); ++ ++ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420) ++ size = q_data->width * q_data->height * 3 / 2; ++ else ++ size = q_data->width * q_data->height * 2; ++ ++ while (size * count > MEM2MEM_VID_MEM_LIMIT) ++ (count)--; ++ ++ *nplanes = 1; ++ *nbuffers = count; ++ sizes[0] = size; ++ ++ alloc_ctxs[0] = ctx->dev->alloc_ctx; ++ ++ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size); ++ ++ return 0; ++} ++ ++static int emmaprp_buf_prepare(struct vb2_buffer *vb) ++{ ++ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ struct emmaprp_q_data *q_data; ++ ++ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type); ++ ++ q_data = get_q_data(ctx, vb->vb2_queue->type); ++ ++ if (vb2_plane_size(vb, 0) < q_data->sizeimage) { ++ dprintk(ctx->dev, "%s data will not fit into plane" ++ "(%lu < %lu)\n", __func__, ++ vb2_plane_size(vb, 0), ++ (long)q_data->sizeimage); ++ return -EINVAL; ++ } ++ ++ vb2_set_plane_payload(vb, 0, q_data->sizeimage); ++ ++ return 0; ++} ++ ++static void emmaprp_buf_queue(struct vb2_buffer *vb) ++{ ++ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); ++ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); ++} ++ ++static struct vb2_ops emmaprp_qops = { ++ .queue_setup = emmaprp_queue_setup, ++ .buf_prepare = emmaprp_buf_prepare, ++ .buf_queue = emmaprp_buf_queue, ++}; ++ ++static int queue_init(void *priv, struct vb2_queue *src_vq, ++ struct vb2_queue *dst_vq) ++{ ++ struct emmaprp_ctx *ctx = priv; ++ int ret; ++ ++ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ src_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ src_vq->drv_priv = ctx; ++ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ src_vq->ops = &emmaprp_qops; ++ src_vq->mem_ops = &vb2_dma_contig_memops; ++ ++ ret = vb2_queue_init(src_vq); ++ if (ret) ++ return ret; ++ ++ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; ++ dst_vq->drv_priv = ctx; ++ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); ++ dst_vq->ops = &emmaprp_qops; ++ dst_vq->mem_ops = &vb2_dma_contig_memops; ++ ++ return vb2_queue_init(dst_vq); ++} ++ ++/* ++ * File operations ++ */ ++static int emmaprp_open(struct file *file) ++{ ++ struct emmaprp_dev *pcdev = video_drvdata(file); ++ struct emmaprp_ctx *ctx; ++ ++ ctx = kzalloc(sizeof *ctx, GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ++ file->private_data = ctx; ++ ctx->dev = pcdev; ++ ++ if (mutex_lock_interruptible(&pcdev->dev_mutex)) { ++ kfree(ctx); ++ return -ERESTARTSYS; ++ } ++ ++ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init); ++ ++ if (IS_ERR(ctx->m2m_ctx)) { ++ int ret = PTR_ERR(ctx->m2m_ctx); ++ ++ mutex_unlock(&pcdev->dev_mutex); ++ kfree(ctx); ++ return ret; ++ } ++ ++ clk_prepare_enable(pcdev->clk_emma_ipg); ++ clk_prepare_enable(pcdev->clk_emma_ahb); ++ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1]; ++ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0]; ++ mutex_unlock(&pcdev->dev_mutex); ++ ++ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx); ++ ++ return 0; ++} ++ ++static int emmaprp_release(struct file *file) ++{ ++ struct emmaprp_dev *pcdev = video_drvdata(file); ++ struct emmaprp_ctx *ctx = file->private_data; ++ ++ dprintk(pcdev, "Releasing instance %p\n", ctx); ++ ++ mutex_lock(&pcdev->dev_mutex); ++ clk_disable_unprepare(pcdev->clk_emma_ahb); ++ clk_disable_unprepare(pcdev->clk_emma_ipg); ++ v4l2_m2m_ctx_release(ctx->m2m_ctx); ++ mutex_unlock(&pcdev->dev_mutex); ++ kfree(ctx); ++ ++ return 0; ++} ++ ++static unsigned int emmaprp_poll(struct file *file, ++ struct poll_table_struct *wait) ++{ ++ struct emmaprp_dev *pcdev = video_drvdata(file); ++ struct emmaprp_ctx *ctx = file->private_data; ++ unsigned int res; ++ ++ mutex_lock(&pcdev->dev_mutex); ++ res = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); ++ mutex_unlock(&pcdev->dev_mutex); ++ return res; ++} ++ ++static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ struct emmaprp_dev *pcdev = video_drvdata(file); ++ struct emmaprp_ctx *ctx = file->private_data; ++ int ret; ++ ++ if (mutex_lock_interruptible(&pcdev->dev_mutex)) ++ return -ERESTARTSYS; ++ ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); ++ mutex_unlock(&pcdev->dev_mutex); ++ return ret; ++} ++ ++static const struct v4l2_file_operations emmaprp_fops = { ++ .owner = THIS_MODULE, ++ .open = emmaprp_open, ++ .release = emmaprp_release, ++ .poll = emmaprp_poll, ++ .unlocked_ioctl = video_ioctl2, ++ .mmap = emmaprp_mmap, ++}; ++ ++static struct video_device emmaprp_videodev = { ++ .name = MEM2MEM_NAME, ++ .fops = &emmaprp_fops, ++ .ioctl_ops = &emmaprp_ioctl_ops, ++ .minor = -1, ++ .release = video_device_release, ++ .vfl_dir = VFL_DIR_M2M, ++}; ++ ++static struct v4l2_m2m_ops m2m_ops = { ++ .device_run = emmaprp_device_run, ++ .job_abort = emmaprp_job_abort, ++ .lock = emmaprp_lock, ++ .unlock = emmaprp_unlock, ++}; ++ ++static int emmaprp_probe(struct platform_device *pdev) ++{ ++ struct emmaprp_dev *pcdev; ++ struct video_device *vfd; ++ struct resource *res_emma; ++ int irq_emma; ++ int ret; ++ ++ pcdev = devm_kzalloc(&pdev->dev, sizeof(*pcdev), GFP_KERNEL); ++ if (!pcdev) ++ return -ENOMEM; ++ ++ spin_lock_init(&pcdev->irqlock); ++ ++ pcdev->clk_emma_ipg = devm_clk_get(&pdev->dev, "ipg"); ++ if (IS_ERR(pcdev->clk_emma_ipg)) { ++ return PTR_ERR(pcdev->clk_emma_ipg); ++ } ++ ++ pcdev->clk_emma_ahb = devm_clk_get(&pdev->dev, "ahb"); ++ if (IS_ERR(pcdev->clk_emma_ahb)) ++ return PTR_ERR(pcdev->clk_emma_ahb); ++ ++ irq_emma = platform_get_irq(pdev, 0); ++ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (irq_emma < 0 || res_emma == NULL) { ++ dev_err(&pdev->dev, "Missing platform resources data\n"); ++ return -ENODEV; ++ } ++ ++ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev); ++ if (ret) ++ return ret; ++ ++ mutex_init(&pcdev->dev_mutex); ++ ++ vfd = video_device_alloc(); ++ if (!vfd) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n"); ++ ret = -ENOMEM; ++ goto unreg_dev; ++ } ++ ++ *vfd = emmaprp_videodev; ++ vfd->lock = &pcdev->dev_mutex; ++ ++ video_set_drvdata(vfd, pcdev); ++ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); ++ pcdev->vfd = vfd; ++ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME ++ " Device registered as /dev/video%d\n", vfd->num); ++ ++ platform_set_drvdata(pdev, pcdev); ++ ++ pcdev->base_emma = devm_request_and_ioremap(&pdev->dev, res_emma); ++ if (!pcdev->base_emma) { ++ ret = -ENXIO; ++ goto rel_vdev; ++ } ++ ++ pcdev->irq_emma = irq_emma; ++ pcdev->res_emma = res_emma; ++ ++ if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq, ++ 0, MEM2MEM_NAME, pcdev) < 0) { ++ ret = -ENODEV; ++ goto rel_vdev; ++ } ++ ++ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); ++ if (IS_ERR(pcdev->alloc_ctx)) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n"); ++ ret = PTR_ERR(pcdev->alloc_ctx); ++ goto rel_vdev; ++ } ++ ++ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops); ++ if (IS_ERR(pcdev->m2m_dev)) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n"); ++ ret = PTR_ERR(pcdev->m2m_dev); ++ goto rel_ctx; ++ } ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); ++ if (ret) { ++ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n"); ++ goto rel_m2m; ++ } ++ ++ return 0; ++ ++ ++rel_m2m: ++ v4l2_m2m_release(pcdev->m2m_dev); ++rel_ctx: ++ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); ++rel_vdev: ++ video_device_release(vfd); ++unreg_dev: ++ v4l2_device_unregister(&pcdev->v4l2_dev); ++ ++ return ret; ++} ++ ++static int emmaprp_remove(struct platform_device *pdev) ++{ ++ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev); ++ ++ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME); ++ ++ video_unregister_device(pcdev->vfd); ++ v4l2_m2m_release(pcdev->m2m_dev); ++ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); ++ v4l2_device_unregister(&pcdev->v4l2_dev); ++ ++ return 0; ++} ++ ++static struct platform_driver emmaprp_pdrv = { ++ .probe = emmaprp_probe, ++ .remove = emmaprp_remove, ++ .driver = { ++ .name = MEM2MEM_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(emmaprp_pdrv); +diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig +new file mode 100644 +index 0000000..37ad446 +--- /dev/null ++++ b/drivers/media/platform/omap/Kconfig +@@ -0,0 +1,14 @@ ++config VIDEO_OMAP2_VOUT_VRFB ++ bool ++ ++config VIDEO_OMAP2_VOUT ++ tristate "OMAP2/OMAP3 V4L2-Display driver" ++ depends on ARCH_OMAP2 || ARCH_OMAP3 ++ select VIDEOBUF_GEN ++ select VIDEOBUF_DMA_CONTIG ++ select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS ++ select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3 ++ select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB ++ default n ++ ---help--- ++ V4L2 Display driver support for OMAP2/3 based boards. +diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/omap/Makefile +new file mode 100644 +index 0000000..d80df41 +--- /dev/null ++++ b/drivers/media/platform/omap/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the omap video device drivers. ++# ++ ++# OMAP2/3 Display driver ++omap-vout-y += omap_vout.o omap_voutlib.o ++omap-vout-$(CONFIG_VIDEO_OMAP2_VOUT_VRFB) += omap_vout_vrfb.o ++obj-$(CONFIG_VIDEO_OMAP2_VOUT) += omap-vout.o +diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c +new file mode 100644 +index 0000000..96c4a17 +--- /dev/null ++++ b/drivers/media/platform/omap/omap_vout.c +@@ -0,0 +1,2285 @@ ++/* ++ * omap_vout.c ++ * ++ * Copyright (C) 2005-2010 Texas Instruments. ++ * ++ * This file is licensed under the terms of the GNU General Public License ++ * version 2. This program is licensed "as is" without any warranty of any ++ * kind, whether express or implied. ++ * ++ * Leveraged code from the OMAP2 camera driver ++ * Video-for-Linux (Version 2) camera capture driver for ++ * the OMAP24xx camera controller. ++ * ++ * Author: Andy Lowe (source@mvista.com) ++ * ++ * Copyright (C) 2004 MontaVista Software, Inc. ++ * Copyright (C) 2010 Texas Instruments. ++ * ++ * History: ++ * 20-APR-2006 Khasim Modified VRFB based Rotation, ++ * The image data is always read from 0 degree ++ * view and written ++ * to the virtual space of desired rotation angle ++ * 4-DEC-2006 Jian Changed to support better memory management ++ * ++ * 17-Nov-2008 Hardik Changed driver to use video_ioctl2 ++ * ++ * 23-Feb-2010 Vaibhav H Modified to use new DSS2 interface ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include